summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore12
-rw-r--r--mysql-test/valgrind.supp13
-rw-r--r--storage/mroonga/AUTHORS7
-rw-r--r--storage/mroonga/CMakeLists.txt380
-rw-r--r--storage/mroonga/ChangeLog3
-rw-r--r--storage/mroonga/Makefile.am157
-rw-r--r--storage/mroonga/NEWS1
-rw-r--r--storage/mroonga/README1
-rwxr-xr-xstorage/mroonga/autogen.sh116
-rw-r--r--storage/mroonga/build/Makefile.am2
-rw-r--r--storage/mroonga/build/cmake_modules/Makefile.am2
-rw-r--r--storage/mroonga/build/cmake_modules/ReadFileList.cmake27
-rw-r--r--storage/mroonga/build/makefiles/LC_MESSAGES.am5
-rw-r--r--storage/mroonga/build/makefiles/gettext.am73
-rw-r--r--storage/mroonga/build/makefiles/locale.am12
-rw-r--r--storage/mroonga/build/makefiles/sphinx-build.am19
-rw-r--r--storage/mroonga/build/makefiles/sphinx.am179
-rw-r--r--storage/mroonga/config.sh.in20
-rw-r--r--storage/mroonga/configure.ac492
-rw-r--r--storage/mroonga/data/Makefile.am4
-rw-r--r--storage/mroonga/data/install.sql.in19
-rw-r--r--storage/mroonga/data/uninstall.sql8
-rw-r--r--storage/mroonga/gpg_uid1
-rw-r--r--storage/mroonga/ha_mroonga.cpp15536
-rw-r--r--storage/mroonga/ha_mroonga.def15
-rw-r--r--storage/mroonga/ha_mroonga.hpp1190
-rw-r--r--storage/mroonga/lib/Makefile.am23
-rw-r--r--storage/mroonga/lib/libmrn_need_mysql_sources.am25
-rw-r--r--storage/mroonga/lib/libmrn_no_mysql_sources.am8
-rw-r--r--storage/mroonga/lib/libmysqlservices_compat_sources.am2
-rw-r--r--storage/mroonga/lib/mrn_auto_increment_value_lock.cpp42
-rw-r--r--storage/mroonga/lib/mrn_auto_increment_value_lock.hpp36
-rw-r--r--storage/mroonga/lib/mrn_condition_converter.cpp608
-rw-r--r--storage/mroonga/lib/mrn_condition_converter.hpp82
-rw-r--r--storage/mroonga/lib/mrn_debug_column_access.cpp36
-rw-r--r--storage/mroonga/lib/mrn_debug_column_access.hpp38
-rw-r--r--storage/mroonga/lib/mrn_encoding.cpp222
-rw-r--r--storage/mroonga/lib/mrn_encoding.hpp35
-rw-r--r--storage/mroonga/lib/mrn_external_lock.cpp43
-rw-r--r--storage/mroonga/lib/mrn_external_lock.hpp38
-rw-r--r--storage/mroonga/lib/mrn_field_normalizer.cpp142
-rw-r--r--storage/mroonga/lib/mrn_field_normalizer.hpp47
-rw-r--r--storage/mroonga/lib/mrn_index_column_name.cpp96
-rw-r--r--storage/mroonga/lib/mrn_index_column_name.hpp43
-rw-r--r--storage/mroonga/lib/mrn_index_table_name.cpp89
-rw-r--r--storage/mroonga/lib/mrn_index_table_name.hpp43
-rw-r--r--storage/mroonga/lib/mrn_lock.cpp31
-rw-r--r--storage/mroonga/lib/mrn_lock.hpp36
-rw-r--r--storage/mroonga/lib/mrn_match_escalation_threshold_scope.cpp33
-rw-r--r--storage/mroonga/lib/mrn_match_escalation_threshold_scope.hpp35
-rw-r--r--storage/mroonga/lib/mrn_multiple_column_key_codec.cpp548
-rw-r--r--storage/mroonga/lib/mrn_multiple_column_key_codec.hpp70
-rw-r--r--storage/mroonga/lib/mrn_mysqlservices.cpp32
-rw-r--r--storage/mroonga/lib/mrn_parameters_parser.cpp176
-rw-r--r--storage/mroonga/lib/mrn_parameters_parser.hpp59
-rw-r--r--storage/mroonga/lib/mrn_path_mapper.cpp196
-rw-r--r--storage/mroonga/lib/mrn_path_mapper.hpp51
-rw-r--r--storage/mroonga/lib/mrn_smart_grn_obj.cpp53
-rw-r--r--storage/mroonga/lib/mrn_smart_grn_obj.hpp39
-rw-r--r--storage/mroonga/lib/mrn_time_converter.cpp260
-rw-r--r--storage/mroonga/lib/mrn_time_converter.hpp47
-rw-r--r--storage/mroonga/lib/mrn_windows.hpp31
-rw-r--r--storage/mroonga/mrn_constants.hpp47
-rw-r--r--storage/mroonga/mrn_err.h32
-rw-r--r--storage/mroonga/mrn_macro.hpp30
-rw-r--r--storage/mroonga/mrn_mysql.h83
-rw-r--r--storage/mroonga/mrn_mysql_compat.h143
-rw-r--r--storage/mroonga/mrn_sys.cpp88
-rw-r--r--storage/mroonga/mrn_sys.hpp36
-rw-r--r--storage/mroonga/mrn_table.cpp1129
-rw-r--r--storage/mroonga/mrn_table.hpp172
-rw-r--r--storage/mroonga/mrn_version.h.in40
-rw-r--r--storage/mroonga/mysql-test/Makefile.am8
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/check_freebsd.inc19
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/check_ha_mroonga_so.inc26
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/check_mariadb.inc19
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/check_osx.inc19
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/check_version.inc28
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/check_windows.inc20
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_32bit.inc28
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_64bit.inc25
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_fractional_seconds.inc32
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_freebsd.inc21
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_fulltext_index_comment.inc25
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_mroonga.inc44
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_mroonga_deinit.inc36
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_mroonga_helper.inc17
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_mysql.inc21
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_100.inc21
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_100_or_later.inc21
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_55.inc21
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_56.inc21
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_56_or_later.inc21
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/load_mroonga_functions.inc24
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/skip_freebsd.inc21
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/skip_mariadb_55.inc24
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/skip_osx.inc21
-rw-r--r--storage/mroonga/mysql-test/mroonga/include/mroonga/unload_mroonga_functions.inc22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_after.result26
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_first.result26
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_multiple.result44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_plain.result37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_with_flags.result10
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_with_type.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_key_multiple_column_with_data.result31
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_comment_not_for_mroonga.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_have_index.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_after.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_first.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_multiple.result32
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_no_order.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_engine.result49
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_create_fulltext.result27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_fulltext_ujis.result23
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_fulltext_utf8.result23
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_multiple_column.result27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_normal.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_primary.result23
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_truncate.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_updating.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_drop_column_multiple.result36
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_drop_column_one.result37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_drop_key_multiple_column_with_data.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_fulltext.result24
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_fulltext_ujis.result24
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_fulltext_utf8.result24
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_multiple_column.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_normal.result26
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_primary.result24
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_engine_decimal.result38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_add_no_primary_key.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_add_normal.result22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_add_table.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_drop_table.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_modify_column_after.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_modify_column_first.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_modify_column_no_order.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_recreate_anonymous_index_at_once.result47
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_rename_table.result45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/alter_table_spatial.result132
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/auto_increment_TODO_SPLIT_ME.result53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/auto_increment_table_param.result70
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/auto_increment_text.result15
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/binlog_TODO_SPLIT_ME.result34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/collation_utf8_general_ci_french.result11
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/collation_utf8_unicode_ci_french.result11
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/collation_utf8_unicode_ci_japanese.result11
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_comment_index_not_for_mroonga.result12
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_comment_normal_not_for_mroonga.result13
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_date_with_index.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_date_without_index.result27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_date_zero_date.result27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_2038.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_before_unix_epoch.result22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_max.result22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_out_of_range.result23
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_2038.result23
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_before_unix_epoch.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_max.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_version_55_out_of_range.result23
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_version_56_or_later_out_of_range.result23
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_fractional_seconds_with_index.result34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_fractional_seconds_without_index.result32
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_freebsd_before_unix_epoch.result22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_null.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_with_index.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_without_index.result31
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_zero_date.result27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_fractional_seconds_with_index.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_fractional_seconds_without_index.result27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_with_index.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_without_index.result27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_enum_less_with_index.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_enum_many_with_index.result287
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_groonga__id__id.result13
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_groonga__id_invalid_id.result8
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_index_fulltext_other_table.result42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_index_int_other_table.result45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_scalar_reference.result24
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_scalar_with_not_for_mroonga_comment.result27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_vector_order_by_with_function.result23
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_vector_reference.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_int_with_index_zero_value.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_set_16_with_index.result40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_set_24_with_index.result48
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_set_32_with_index.result56
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_set_64_with_index.result88
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_set_8_with_index.result32
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_signed_bigint_with_index.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_signed_int_with_index.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_signed_mediumint_with_index.result26
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_signed_smallint_with_index.result24
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_signed_tinyint_with_index.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_time_fractional_seconds_with_index.result40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_time_with_index.result40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_timestamp_fractional_seconds_with_index.result42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_timestamp_with_index.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_tinyint_without_index.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_bigint_with_index.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_bigint_without_index.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_int_with_index.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_mediumint_with_index.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_smallint_with_index.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_tinyint_with_index.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_year_with_index.result37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/column_year_without_index.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/create_database_name_slash.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/create_table_TODO_SPLIT_ME.result172
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/create_table_comment_normal.result11
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/create_table_default_tokenizer.result11
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/create_table_normalizer_fulltext_index.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/create_table_normalizer_no_utf8_charset_with_utf8_normalizer.result12
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/create_table_reference_type.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/create_table_vector.result11
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/delete_fulltext_column.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/delete_index_btree_many_records.result27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/delete_index_hash_id_no_unique.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/delete_index_hash_id_unique.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/delete_normal_column.result34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/delete_unsigned_bigint.result12
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/drop_database_TODO_SPLIT_ME.result14
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/drop_table_TODO_SPLIT_ME.result5
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/flush_logs.result1
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/foreign_key_create.result147
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_empty_query.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_escape.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_leading_not.result24
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_minus_with_or.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_or_no_operator.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_or_with_minus.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_or_with_plus.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.result14
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.result14
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_plus_with_or.result15
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_full_spec.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_no_weight.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_omit_section.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_ten_or_more_sections.result39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_three_or_more_sections.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_error.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_error_and_log.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_ignore.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_ignore_and_log.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_ascii.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_cp932.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_eucjpms.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_japanese.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_utf8mb4.result26
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_empty_query.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_found_rows.result42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_groonga_varchar_vector.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_index_recreate.result41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_insert_select.result66
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_insert_values.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_delete.result34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_insert.result40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_recreate.result42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_update.result37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_index.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_no_primary_key.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_not_match_against.result68
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_or.result22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_different_against.result23
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_different_match.result24
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_no_where.result22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_same_match_against.result22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_parser_comment.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_parser_default.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_parser_off.result42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_two_inner_join.result41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_version_100_no_such_key.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_version_55_no_such_key.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/fulltext_version_56_no_such_key.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_command_select.result13
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_escape_error_query_is_missing.result3
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_escape_error_query_is_not_string.result3
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_escape_error_target_characters_is_not_string.result3
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_escape_success_all.result4
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_escape_success_custom.result4
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_escape_success_nested.result13
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_last_insert_grn_id.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_last_insert_id_reference.result15
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_last_insert_id_set.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_ascii.result120
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_cp932.result91
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_eucjpms.result91
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_invalid_nonexistent_charset.result4
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_invalid_unsupported_charset.result4
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_japanese.result95
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/geometry_bulk_insert_null.result13
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/geometry_contains.result170
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_btree_equal_datetime.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_btree_equal_time.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_btree_equal_timestamp.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_btree_normal_column_insert.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_hash_id_normal.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_hash_id_primary.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_hash_id_unique.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_hash_normal_column_insert.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_delete.result32
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_smallint.result65
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_unsigned_bigint.result65
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_unsigned_int.result65
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_unsigned_smallint.result65
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_varchar.result65
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_primary_delete.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_primary_select_int.result37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_primary_update.result32
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_recreate.result39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_replace.result39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_double.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_float.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_int.result37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_string.result40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_varchar.result39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_32bit_equal.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_64bit_equal.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_index_read.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_32bit_asc.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_32bit_desc.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_64bit_asc.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_64bit_desc.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_reinsert.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_index_read.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_order_asc.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_order_desc.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_reinsert.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_decimal.result36
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_index_read.result22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_order_asc.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_order_desc.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_reinsert.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_index_read.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_order_asc.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_order_desc.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_reinsert.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_varchar.resultbin0 -> 1027 bytes
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_32bit_equal.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_64bit_equal.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_index_read.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_32bit_asc.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_32bit_desc.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_64bit_asc.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_64bit_desc.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_reinsert.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_update_int.result30
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_update_string.result34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_char_exact_length.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_char_null_character.resultbin0 -> 387 bytes
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_char_short.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_date.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_datetime_with_fractional_seconds.result30
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_datetime_without_fractional_seconds.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_decimal_with_fractional_seconds.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_decimal_without_fractional_seconds.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_time_with_fractional_seconds.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_time_without_fractional_seconds.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_timestamp_with_fractional_seconds.result30
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_timestamp_without_fractional_seconds.result26
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_varchar_null_character.resultbin0 -> 390 bytes
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_primary_year.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_asc.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_desc.result15
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_or_equal_asc.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_or_equal_desc.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_asc.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_desc.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_or_equal_asc.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_or_equal_desc.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_bigint.result38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_bigint_unsigned.result31
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_double.result38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_float.result38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_int.result38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_int_unsigned.result31
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_mediumint.result38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_mediumint_unsigned.result31
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_smallint.result38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_smallint_unsigned.result31
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_tinyint.result38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_tinyint_unsigned.result31
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_varchar.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_varchar_collation.result31
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_normal_int.result55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_normal_varchar.result55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_primary_int.result55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_read_primary_varchar.result55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_unique_delete_by_primary_key.result13
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_unique_insert_after_error.result15
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_unique_varchar.result15
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_update_multiple_column.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/index_update_single_column.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/information_schema_plugins.result4
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/information_schema_plugins.result.in4
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/information_schema_tables_auto_increment_none.result10
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/information_schema_tables_auto_increment_use.result10
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/information_schema_tables_data_length.result26
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/insert_TODO_SPLIT_ME.result90
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/insert_on_duplicate_key_update_no_primary_key_and_unique_key_twice.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/insert_on_duplicate_key_update_primary_key.result26
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/insert_on_duplicate_key_update_unique_key.result30
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/like_unicode_ci.result13
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/lock_tables_read.result7
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_not_optimized_disabled.result47
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_not_optimized_no_limit.result47
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_between.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_between_over.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_equal.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_greater_than.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_greater_than_or_equal.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_less_than.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_less_than_or_equal.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_have_primary_key.result43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_between.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_between_over.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_equal.result45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_greater_than.result45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_greater_than_or_equal.result45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_less_than.result45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_less_than_or_equal.result45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_no_primary_key.result42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_no_where_clause.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_asc.result43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_desc.result43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_id.result46
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_match_against.result44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_select_match_against.result45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_between.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_between_over.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_equal.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_greater_than.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_greater_than_or_equal.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_less_than.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_less_than_or_equal.result35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_varchar_equal_with_index.result46
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_varchar_equal_without_index.result44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_between.result30
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_between_over.result30
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_equal.result32
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_greater_than.result30
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_greater_than_or_equal.result30
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_less_than.result30
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_less_than_or_equal.result30
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_TODO_SPLIT_ME.result106
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_after_insert_multithread.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_after_insert_single_thread.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_disabled.result32
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_index_view.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/replace_geometry.result23
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/replace_select_varchar.result39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/replace_text.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/replace_varchar.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/replace_vector.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/select_all.result198
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/select_empty_key_where_equal.result12
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/select_empty_key_where_not_equal.result13
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/select_group_by_with_index.result15
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/select_group_by_without_index.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/select_pkey.result27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/select_secondary_key.result55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/show_create_table_TODO_SPLIT_ME.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/sub_query_fulltext.result44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/temporary_table.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/truncate.result55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/update_fulltext.result22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/update_id_hash_index.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/update_id_unique_hash_index.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/update_int.result42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/update_last_insert_grn_id.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/update_virtual_column.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_database_path_prefix.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_default_parser_new_value.result6
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_default_parser_same_value.result4
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_dry_write_delete.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_dry_write_insert.result30
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_dry_write_update.result26
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_disable.result4
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_invalid.result6
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_no_retry.result4
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_valid.result4
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_log_file_new_value.result6
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_log_file_nonexistent_path.result4
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_log_file_same_value.result4
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_log_level_TODO_SPLIT_ME.result49
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_match_escalation_threshold_global.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_match_escalation_threshold_session.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_vector_column_delimiter.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_version.result3
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/r/variable_version.result.in3
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/suite.opt1
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/suite.pm23
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_after.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_first.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_multiple.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_plain.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_with_flags.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_with_type.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_key_multiple_column_with_data.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_comment_not_for_mroonga.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_have_index.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_after.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_first.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_multiple.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_no_order.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_engine.test54
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_create_fulltext.test55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_fulltext_ujis.test49
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_fulltext_utf8.test49
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_multiple_column.test51
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_normal.test49
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_primary.test47
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_truncate.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_updating.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_drop_column_multiple.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_drop_column_one.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_drop_key_multiple_column_with_data.test46
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_fulltext.test51
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_fulltext_ujis.test51
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_fulltext_utf8.test51
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_multiple_column.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_normal.test51
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_primary.test49
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_engine_decimal.test46
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_add_no_primary_key.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_add_normal.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_add_table.test47
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_drop_table.test48
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_modify_column_after.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_modify_column_first.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_modify_column_no_order.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_recreate_anonymous_index_at_once.test51
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_rename_table.test49
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/alter_table_spatial.test150
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/auto_increment_TODO_SPLIT_ME.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/auto_increment_table_param.test49
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/auto_increment_text.test33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/binlog_TODO_SPLIT_ME.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/collation_utf8_general_ci_french.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/collation_utf8_unicode_ci_french.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/collation_utf8_unicode_ci_japanese.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_comment_index_not_for_mroonga.test34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_comment_normal_not_for_mroonga.test34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_date_with_index.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_date_without_index.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_date_zero_date.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_2038.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_before_unix_epoch.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_max.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_out_of_range.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_2038.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_before_unix_epoch.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_max.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_version_55_out_of_range.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_version_56_or_later_out_of_range.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_fractional_seconds_with_index.test47
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_fractional_seconds_without_index.test46
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_freebsd_before_unix_epoch.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_null.test37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_with_index.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_without_index.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_zero_date.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_fractional_seconds_with_index.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_fractional_seconds_without_index.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_with_index.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_without_index.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_enum_less_with_index.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_enum_many_with_index.test298
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_groonga__id__id.test34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_groonga__id_invalid_id.test34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_index_fulltext_other_table.test54
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_index_int_other_table.test55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_scalar_reference.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_scalar_with_not_for_mroonga_comment.test48
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_vector_order_by_with_function.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_vector_reference.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_int_with_index_zero_value.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_set_16_with_index.test56
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_set_24_with_index.test65
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_set_32_with_index.test74
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_set_64_with_index.test110
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_set_8_with_index.test47
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_signed_bigint_with_index.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_signed_int_with_index.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_signed_mediumint_with_index.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_signed_smallint_with_index.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_signed_tinyint_with_index.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_time_fractional_seconds_with_index.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_time_with_index.test49
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_timestamp_fractional_seconds_with_index.test55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_timestamp_with_index.test46
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_tinyint_without_index.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_bigint_with_index.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_bigint_without_index.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_int_with_index.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_mediumint_with_index.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_smallint_with_index.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_tinyint_with_index.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_year_with_index.test47
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/column_year_without_index.test46
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/create_database_name_slash.test60
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/create_table_TODO_SPLIT_ME.test147
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/create_table_comment_normal.test34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/create_table_default_tokenizer.test36
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/create_table_normalizer_fulltext_index.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/create_table_normalizer_no_utf8_charset_with_utf8_normalizer.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/create_table_reference_type.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/create_table_vector.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/delete_fulltext_column.test36
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/delete_index_btree_many_records.test46
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/delete_index_hash_id_no_unique.test33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/delete_index_hash_id_unique.test33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/delete_normal_column.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/delete_unsigned_bigint.test37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/drop_database_TODO_SPLIT_ME.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/drop_table_TODO_SPLIT_ME.test29
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/flush_logs.test21
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/foreign_key_create.test118
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_empty_query.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_escape.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_leading_not.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_minus_with_or.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_or_no_operator.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_or_with_minus.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_or_with_plus.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_plus_with_or.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_full_spec.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_no_weight.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_omit_section.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_ten_or_more_sections.test63
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_three_or_more_sections.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_error.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_error_and_log.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_ignore.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_ignore_and_log.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_ascii.test36
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_cp932.test34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_eucjpms.test34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_japanese.test33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_utf8mb4.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_empty_query.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_found_rows.test49
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_groonga_varchar_vector.test47
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_index_recreate.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_insert_select.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_insert_values.test33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_delete.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_insert.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_recreate.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_update.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_index.test47
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_no_primary_key.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_not_match_against.test46
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_or.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_different_against.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_different_match.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_no_where.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_same_match_against.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_parser_comment.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_parser_default.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_parser_off.test48
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_two_inner_join.test69
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_version_100_no_such_key.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_version_55_no_such_key.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/fulltext_version_56_no_such_key.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_command_select.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_escape_error_query_is_missing.test27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_escape_error_query_is_not_string.test27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_escape_error_target_characters_is_not_string.test27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_escape_success_all.test26
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_escape_success_custom.test26
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_escape_success_nested.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_last_insert_grn_id.test48
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_last_insert_id_reference.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_last_insert_id_set.test37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_ascii.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_cp932.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_eucjpms.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_invalid_nonexistent_charset.test28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_invalid_unsupported_charset.test28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_japanese.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/geometry_bulk_insert_null.test34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/geometry_contains.test148
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_btree_equal_datetime.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_btree_equal_time.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_btree_equal_timestamp.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_btree_normal_column_insert.test33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_hash_id_normal.test33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_hash_id_primary.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_hash_id_unique.test33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_hash_normal_column_insert.test33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_delete.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_smallint.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_unsigned_bigint.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_unsigned_int.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_unsigned_smallint.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_varchar.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_primary_delete.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_primary_select_int.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_primary_update.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_recreate.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_replace.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_double.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_float.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_int.test46
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_string.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_varchar.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_32bit_equal.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_64bit_equal.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_index_read.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_32bit_asc.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_32bit_desc.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_64bit_asc.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_64bit_desc.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_reinsert.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_index_read.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_order_asc.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_order_desc.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_reinsert.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_decimal.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_index_read.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_order_asc.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_order_desc.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_reinsert.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_index_read.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_order_asc.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_order_desc.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_reinsert.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_varchar.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_32bit_equal.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_64bit_equal.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_index_read.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_32bit_asc.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_32bit_desc.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_64bit_asc.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_64bit_desc.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_reinsert.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_update_int.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_update_string.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_char_exact_length.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_char_null_character.test37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_char_short.test37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_date.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_datetime_with_fractional_seconds.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_datetime_without_fractional_seconds.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_decimal_with_fractional_seconds.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_decimal_without_fractional_seconds.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_time_with_fractional_seconds.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_time_without_fractional_seconds.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_timestamp_with_fractional_seconds.test46
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_timestamp_without_fractional_seconds.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_varchar_null_character.test37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_primary_year.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_asc.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_desc.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_or_equal_asc.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_or_equal_desc.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_asc.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_desc.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_or_equal_asc.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_or_equal_desc.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_bigint.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_bigint_unsigned.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_double.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_float.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_int.test46
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_int_unsigned.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_mediumint.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_mediumint_unsigned.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_smallint.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_smallint_unsigned.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_tinyint.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_tinyint_unsigned.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_varchar.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_varchar_collation.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_normal_int.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_normal_varchar.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_primary_int.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_read_primary_varchar.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_unique_delete_by_primary_key.test37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_unique_insert_after_error.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_unique_varchar.test36
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_update_multiple_column.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/index_update_single_column.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/information_schema_plugins.test22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/information_schema_tables_auto_increment_none.test34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/information_schema_tables_auto_increment_use.test34
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/information_schema_tables_data_length.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/insert_TODO_SPLIT_ME.test105
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/insert_on_duplicate_key_update_no_primary_key_and_unique_key_twice.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/insert_on_duplicate_key_update_primary_key.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/insert_on_duplicate_key_update_unique_key.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/like_unicode_ci.test37
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/lock_tables_read.test32
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_not_optimized_disabled.test60
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_not_optimized_no_limit.test55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_between.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_between_over.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_equal.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_greater_than.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_greater_than_or_equal.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_less_than.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_less_than_or_equal.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_have_primary_key.test51
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_between.test48
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_between_over.test48
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_equal.test56
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_greater_than.test56
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_greater_than_or_equal.test56
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_less_than.test56
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_less_than_or_equal.test56
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_no_primary_key.test51
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_no_where_clause.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_asc.test55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_desc.test55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_id.test57
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_match_against.test56
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_select_match_against.test57
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_between.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_between_over.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_equal.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_greater_than.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_greater_than_or_equal.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_less_than.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_less_than_or_equal.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_varchar_equal_with_index.test57
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_varchar_equal_without_index.test56
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_between.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_between_over.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_equal.test55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_greater_than.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_greater_than_or_equal.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_less_than.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_less_than_or_equal.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_TODO_SPLIT_ME.test61
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_after_insert_multithread.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_after_insert_single_thread.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_disabled.test51
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_index_view.test56
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/replace_geometry.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/replace_select_varchar.test64
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/replace_text.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/replace_varchar.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/replace_vector.test46
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/select_all.test100
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/select_empty_key_where_equal.test36
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/select_empty_key_where_not_equal.test36
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/select_group_by_with_index.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/select_group_by_without_index.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/select_pkey.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/select_secondary_key.test51
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/show_create_table_TODO_SPLIT_ME.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/sub_query_fulltext.test55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/temporary_table.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/truncate.test56
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/update_fulltext.test36
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/update_id_hash_index.test33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/update_id_unique_hash_index.test33
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/update_int.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/update_last_insert_grn_id.test48
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/update_virtual_column.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_database_path_prefix.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_default_parser_new_value.test25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_default_parser_same_value.test22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_dry_write_delete.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_dry_write_insert.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_dry_write_update.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_disable.test28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_invalid.test28
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_no_retry.test27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_valid.test27
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_log_file_new_value.test25
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_log_file_nonexistent_path.test22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_log_file_same_value.test22
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_log_level_TODO_SPLIT_ME.test61
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_match_escalation_threshold_global.test55
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_match_escalation_threshold_session.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_vector_column_delimiter.test54
-rw-r--r--storage/mroonga/mysql-test/mroonga/storage/t/variable_version.test22
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_add_column.result37
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_change_column_comment.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_change_engine.result49
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_comment_change_engine.result40
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_create_fulltext.result27
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_fulltext.result23
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_multiple_column.result27
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_normal.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_primary.result23
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_updating.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_drop_column.result37
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_fulltext.result24
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_lock_tables.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_multiple_column.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_normal.result26
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_primary.result24
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_fulltext.result52
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_rename_table.result45
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_spatial.result131
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/auto_increment_text.result15
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/binlog_TODO_SPLIT_ME.result34
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/column_comment_index_not_for_mroonga.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/column_normal_comment.result13
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/count_star_with_index.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/create_table_TODO_SPLIT_ME.result109
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/create_table_comment_combined.result12
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/create_table_normalizer_fulltext_index.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/delete_TODO_SPLIT_ME.result55
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/delete_all.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_leading_not.result24
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_multiple_match_against.result32
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_minus_with_or.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_or_no_operator.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_or_with_minus.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_or_with_plus.result19
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.result15
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.result15
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_plus_with_or.result16
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_weight_full_spec.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_weight_no_weight.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_weight_omit_section.result20
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_ascii.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_cp932.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_eucjpms.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_japanese.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_index_recreate.result41
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_insert_select.result66
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_insert_values.result25
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_many_records.result4389
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_delete.result34
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_insert.result40
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_recreate.result42
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_update.result37
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_index.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_myisam.result202
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_not_match_against.result68
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_order_TODO_SPLIT_ME.result50
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_order_transaction.result50
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_parser_comment.result29
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/function_last_insert_grn_id.result17
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/function_last_insert_id_reference.result15
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/function_last_insert_id_set.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/geometry_contains.result169
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/geometry_delete.result31
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/geometry_update.result36
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/index_force_index_not_used.result8
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/insert_TODO_SPLIT_ME.result78
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/insert_bulk.result30
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/insert_on_duplicate_key_update_multiple_column_primary_key_myisam.result36
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/insert_on_duplicate_key_update_multiple_column_unique_index_myisam.result39
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/multi_range_read_disk_sweep.result18
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/optimization_order_limit_TODO_SPLIT_ME.result67
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/optimization_order_limit_no_where_clause.result24
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/repair_table_no_files.result42
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/repair_table_no_index_file.result42
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/temporary_table.result21
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/transaction_query_cache.result28
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/transaction_rollback_delete_delete.result50
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/transaction_rollback_delete_update.result47
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/truncate.result55
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/update_fulltext.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/update_int.result36
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/variable_dry_write_delete.result53
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/variable_dry_write_insert.result42
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/variable_dry_write_update.result39
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/variable_match_escalation_threshold_global.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/variable_match_escalation_threshold_session.result33
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/version_55_performance_schema.result38
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/r/version_56_or_later_performance_schema.result73
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/suite.opt1
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/suite.pm23
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_add_column.test46
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_change_column_comment.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_change_engine.test55
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_comment_change_engine.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_create_fulltext.test56
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_fulltext.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_multiple_column.test52
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_normal.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_primary.test48
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_updating.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_drop_column.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_fulltext.test52
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_lock_tables.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_multiple_column.test54
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_normal.test52
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_primary.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_fulltext.test57
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_rename_table.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_spatial.test150
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/auto_increment_text.test34
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/binlog_TODO_SPLIT_ME.test55
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/column_comment_index_not_for_mroonga.test37
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/column_normal_comment.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/count_star_with_index.test54
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/create_table_TODO_SPLIT_ME.test99
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/create_table_comment_combined.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/create_table_normalizer_fulltext_index.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/delete_TODO_SPLIT_ME.test59
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/delete_all.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_leading_not.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_multiple_match_against.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_minus_with_or.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_or_no_operator.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_or_with_minus.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_or_with_plus.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_plus_with_or.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_weight_full_spec.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_weight_no_weight.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_weight_omit_section.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_ascii.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_cp932.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_eucjpms.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_japanese.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_index_recreate.test51
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_insert_select.test47
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_insert_values.test35
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_many_records.test4136
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_delete.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_insert.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_recreate.test51
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_update.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_index.test48
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_myisam.test102
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_not_match_against.test47
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_order_TODO_SPLIT_ME.test51
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_order_transaction.test64
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_parser_comment.test39
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/function_last_insert_grn_id.test52
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/function_last_insert_id_reference.test36
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/function_last_insert_id_set.test38
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/geometry_contains.test145
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/geometry_delete.test48
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/geometry_update.test50
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/index_force_index_not_used.test32
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/insert_TODO_SPLIT_ME.test91
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/insert_bulk.test44
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/insert_on_duplicate_key_update_multiple_column_primary_key_myisam.test49
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/insert_on_duplicate_key_update_multiple_column_unique_index_myisam.test49
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/multi_range_read_disk_sweep.test45
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/optimization_order_limit_TODO_SPLIT_ME.test68
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/optimization_order_limit_no_where_clause.test46
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/repair_table_no_files.test69
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/repair_table_no_index_file.test63
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/temporary_table.test40
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/transaction_query_cache.test53
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/transaction_rollback_delete_delete.test59
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/transaction_rollback_delete_update.test56
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/truncate.test57
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/update_fulltext.test43
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/update_int.test41
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/variable_dry_write_delete.test55
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/variable_dry_write_insert.test47
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/variable_dry_write_update.test48
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/variable_match_escalation_threshold_global.test56
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/variable_match_escalation_threshold_session.test54
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/version_55_performance_schema.test42
-rw-r--r--storage/mroonga/mysql-test/mroonga/wrapper/t/version_56_or_later_performance_schema.test42
-rw-r--r--storage/mroonga/packages/Makefile.am7
-rw-r--r--storage/mroonga/packages/apt/Makefile.am55
-rwxr-xr-xstorage/mroonga/packages/apt/build-deb.sh121
-rwxr-xr-xstorage/mroonga/packages/apt/build-in-chroot.sh134
-rw-r--r--storage/mroonga/packages/apt/mroonga-depended-packages11
-rwxr-xr-xstorage/mroonga/packages/apt/sign-packages.sh42
-rwxr-xr-xstorage/mroonga/packages/apt/sign-repository.sh46
-rwxr-xr-xstorage/mroonga/packages/apt/update-repository.sh130
-rwxr-xr-xstorage/mroonga/packages/check-utility.sh665
-rw-r--r--storage/mroonga/packages/debian/apparmor/mysql-server-mroonga5
-rw-r--r--storage/mroonga/packages/debian/changelog355
-rw-r--r--storage/mroonga/packages/debian/compat1
-rw-r--r--storage/mroonga/packages/debian/control.in51
-rw-r--r--storage/mroonga/packages/debian/copyright27
-rw-r--r--storage/mroonga/packages/debian/mysql-server-mroonga-doc.install1
-rw-r--r--storage/mroonga/packages/debian/mysql-server-mroonga.install3
-rwxr-xr-xstorage/mroonga/packages/debian/mysql-server-mroonga.postinst72
-rwxr-xr-xstorage/mroonga/packages/debian/mysql-server-mroonga.postrm38
-rwxr-xr-xstorage/mroonga/packages/debian/mysql-server-mroonga.prerm10
-rwxr-xr-xstorage/mroonga/packages/debian/rules35
-rw-r--r--storage/mroonga/packages/rpm/Makefile.am3
-rw-r--r--storage/mroonga/packages/rpm/centos/Makefile.am9
-rw-r--r--storage/mroonga/packages/rpm/centos/mariadb-mroonga.spec.in375
-rw-r--r--storage/mroonga/packages/rpm/centos/mysql55-mroonga.spec.in200
-rw-r--r--storage/mroonga/packages/rpm/centos/mysql56-community-mroonga.spec.in198
-rw-r--r--storage/mroonga/packages/rpm/fedora/Makefile.am2
-rw-r--r--storage/mroonga/packages/rpm/fedora/mariadb-mroonga.spec.in189
-rw-r--r--storage/mroonga/packages/rpm/fedora/mysql-mroonga.spec.in324
-rw-r--r--storage/mroonga/packages/source/Makefile.am122
-rw-r--r--storage/mroonga/packages/source/patches/mariadb-10.0.3-windows-build.diff9
-rw-r--r--storage/mroonga/packages/ubuntu/Makefile.am24
-rwxr-xr-xstorage/mroonga/packages/ubuntu/upload.rb168
-rw-r--r--storage/mroonga/packages/windows/Makefile.am12
-rw-r--r--storage/mroonga/packages/windows/README.md20
-rw-r--r--storage/mroonga/packages/windows/build-vc2010-msi-32.bat8
-rw-r--r--storage/mroonga/packages/windows/build-vc2010-msi-64.bat8
-rw-r--r--storage/mroonga/packages/windows/build-vc2010-zip-32.bat8
-rw-r--r--storage/mroonga/packages/windows/build-vc2010-zip-64.bat8
-rw-r--r--storage/mroonga/packages/windows/build-vc2010.bat4
-rw-r--r--storage/mroonga/packages/windows/build-vc2013-msi-32.bat8
-rw-r--r--storage/mroonga/packages/windows/build-vc2013-msi-64.bat8
-rw-r--r--storage/mroonga/packages/windows/build-vc2013-zip-32.bat8
-rw-r--r--storage/mroonga/packages/windows/build-vc2013-zip-64.bat8
-rw-r--r--storage/mroonga/packages/windows/build-vc2013.bat4
-rw-r--r--storage/mroonga/packages/yum/Makefile.am63
-rw-r--r--storage/mroonga/packages/yum/Vagrantfile50
-rwxr-xr-xstorage/mroonga/packages/yum/build-in-vm.sh60
-rwxr-xr-xstorage/mroonga/packages/yum/build-rpm.sh108
-rw-r--r--storage/mroonga/packages/yum/env.sh.in27
-rwxr-xr-xstorage/mroonga/packages/yum/sign-rpm.sh44
-rwxr-xr-xstorage/mroonga/packages/yum/update-repository.sh29
-rw-r--r--storage/mroonga/plug.in6
-rw-r--r--storage/mroonga/plugin_version1
-rw-r--r--storage/mroonga/required_groonga_normalizer_mysql_version1
-rw-r--r--storage/mroonga/required_groonga_version1
-rw-r--r--storage/mroonga/sources.am13
-rw-r--r--storage/mroonga/test/Makefile.am14
-rwxr-xr-xstorage/mroonga/test/run-sql-test.sh232
-rwxr-xr-xstorage/mroonga/test/run-unit-test.sh49
-rw-r--r--storage/mroonga/test/unit/Makefile.am34
-rw-r--r--storage/mroonga/test/unit/test_mrn_path_mapper.cpp116
-rw-r--r--storage/mroonga/test/unit/test_mrn_sys.cpp95
-rw-r--r--storage/mroonga/tools/Makefile.am6
-rwxr-xr-xstorage/mroonga/tools/prepare-sphinx-html.rb183
-rwxr-xr-xstorage/mroonga/tools/travis/before_script.sh32
-rwxr-xr-xstorage/mroonga/tools/travis/install.sh103
-rwxr-xr-xstorage/mroonga/tools/travis/script.sh114
-rwxr-xr-xstorage/mroonga/tools/upload-to-github.rb42
-rw-r--r--storage/mroonga/udf/Makefile.am10
-rw-r--r--storage/mroonga/udf/mrn_udf_command.cpp165
-rw-r--r--storage/mroonga/udf/mrn_udf_escape.cpp154
-rw-r--r--storage/mroonga/udf/mrn_udf_last_insert_grn_id.cpp54
-rw-r--r--storage/mroonga/udf/mrn_udf_snippet.cpp302
-rw-r--r--storage/mroonga/udf/sources.am5
-rw-r--r--storage/mroonga/vendor/groonga/CMakeLists.txt505
-rw-r--r--storage/mroonga/vendor/groonga/Makefile.am135
-rw-r--r--storage/mroonga/vendor/groonga/README.md49
-rwxr-xr-xstorage/mroonga/vendor/groonga/autogen.sh21
-rw-r--r--storage/mroonga/vendor/groonga/base_version1
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/Makefile.am89
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/bench-ctx-create.c192
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/bench-geo-distance.c499
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/bench-geo-select.c269
-rwxr-xr-xstorage/mroonga/vendor/groonga/benchmark/bench-geo-select.sh38
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/bench-query-optimizer-ddl.grn16
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/bench-query-optimizer.c208
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/bench-table-factory.c270
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/fixtures/Makefile.am2
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/13_2010.CSV.xzbin0 -> 2355640 bytes
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/Makefile.am4
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/README.txt6
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/format_2010.html111
-rwxr-xr-xstorage/mroonga/vendor/groonga/benchmark/geo-distance-summary.rb140
-rwxr-xr-xstorage/mroonga/vendor/groonga/benchmark/geo-select-generate-grn.rb53
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/lib/Makefile.am21
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/lib/bench-reporter.c315
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/lib/bench-reporter.h64
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/lib/bench-utils.c83
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/lib/bench-utils.h34
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/lib/benchmark.c35
-rw-r--r--storage/mroonga/vendor/groonga/benchmark/lib/benchmark.h32
-rw-r--r--storage/mroonga/vendor/groonga/bindings/php/config.m466
-rw-r--r--storage/mroonga/vendor/groonga/bindings/php/config.w326
-rw-r--r--storage/mroonga/vendor/groonga/bindings/php/groonga.c218
-rw-r--r--storage/mroonga/vendor/groonga/bindings/php/php_groonga.h125
-rw-r--r--storage/mroonga/vendor/groonga/bindings/php/tests/001.phpt29
-rw-r--r--storage/mroonga/vendor/groonga/bindings/python/ql/groongaql.c364
-rwxr-xr-xstorage/mroonga/vendor/groonga/bindings/python/ql/setup.py22
-rw-r--r--storage/mroonga/vendor/groonga/build/Makefile.am2
-rw-r--r--storage/mroonga/vendor/groonga/build/ac_macros/check_functions.m413
-rw-r--r--storage/mroonga/vendor/groonga/build/ac_macros/check_headers.m424
-rw-r--r--storage/mroonga/vendor/groonga/build/cmake_modules/Makefile.am2
-rw-r--r--storage/mroonga/vendor/groonga/build/cmake_modules/ReadFileList.cmake27
-rw-r--r--storage/mroonga/vendor/groonga/build/makefiles/LC_MESSAGES.am5
-rw-r--r--storage/mroonga/vendor/groonga/build/makefiles/gettext.am73
-rw-r--r--storage/mroonga/vendor/groonga/build/makefiles/locale.am12
-rw-r--r--storage/mroonga/vendor/groonga/build/makefiles/sphinx-build.am19
-rw-r--r--storage/mroonga/vendor/groonga/build/makefiles/sphinx.am179
-rw-r--r--storage/mroonga/vendor/groonga/config.h.cmake153
-rw-r--r--storage/mroonga/vendor/groonga/config.sh.in6
-rw-r--r--storage/mroonga/vendor/groonga/configure.ac1514
-rw-r--r--storage/mroonga/vendor/groonga/data/CMakeLists.txt20
-rw-r--r--storage/mroonga/vendor/groonga/data/Makefile.am20
-rw-r--r--storage/mroonga/vendor/groonga/data/account/twitter.gpg.hayashibin0 -> 657 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/account/twitter.gpg.koubin0 -> 657 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/account/twitter.gpg.yukibin0 -> 662 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/groonga-httpd.conf.in57
-rw-r--r--storage/mroonga/vendor/groonga/data/groonga.conf11
-rw-r--r--storage/mroonga/vendor/groonga/data/html/CMakeLists.txt18
-rw-r--r--storage/mroonga/vendor/groonga/data/html/Makefile.am9
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/groonga-admin.css120
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_flat_0_aaaaaa_40x100.pngbin0 -> 180 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_flat_55_fbec88_40x100.pngbin0 -> 182 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_glass_75_d0e5f5_1x400.pngbin0 -> 124 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_glass_85_dfeffc_1x400.pngbin0 -> 123 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_glass_95_fef1ec_1x400.pngbin0 -> 119 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.pngbin0 -> 4033 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.pngbin0 -> 104 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.pngbin0 -> 88 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_217bc0_256x240.pngbin0 -> 4369 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_2e83ff_256x240.pngbin0 -> 4369 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_469bdd_256x240.pngbin0 -> 4369 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_6da8d5_256x240.pngbin0 -> 5355 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_cd0a0a_256x240.pngbin0 -> 4369 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_d8e7f3_256x240.pngbin0 -> 4369 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_f9bd01_256x240.pngbin0 -> 5355 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/css/redmond/jquery-ui-1.8.18.custom.css565
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/favicon.icobin0 -> 1406 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/favicon.pngbin0 -> 682 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/favicon.svg121
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/images/groonga.pngbin0 -> 6064 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/images/groonga.svg121
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/images/loading.gifbin0 -> 673 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/index.html297
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/index.ja.html301
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/js/groonga-admin.ja.js1372
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/js/groonga-admin.js1372
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/js/jquery-1.7.2.min.js4
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/js/jquery-ui-1.8.18.custom.min.js356
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/js/jquery.flot-0.7.min.js6
-rw-r--r--storage/mroonga/vendor/groonga/data/html/admin/js/jquery.flot.license.txt22
-rw-r--r--storage/mroonga/vendor/groonga/data/html/files.am34
-rwxr-xr-xstorage/mroonga/vendor/groonga/data/html/update-files.sh17
-rw-r--r--storage/mroonga/vendor/groonga/data/images/Makefile.am2
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/Makefile.am22
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/files.am53
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-foreground-white.pngbin0 -> 2266 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-foreground-white.svg118
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-full-size.pngbin0 -> 6841 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-full-size.svg97
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-icon.pngbin0 -> 2281 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-icon.svg93
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-logo-foreground-white.pngbin0 -> 7393 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-logo-foreground-white.svg108
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-logo.pngbin0 -> 7311 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-logo.svg118
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar-foreground-white.pngbin0 -> 4752 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar-foreground-white.svg170
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar.pngbin0 -> 4680 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar.svg186
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-foreground-white.pngbin0 -> 3245 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-foreground-white.svg183
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-large.pngbin0 -> 5900 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-large.svg179
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner.pngbin0 -> 3038 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner.svg201
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-foreground-white.pngbin0 -> 2119 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-foreground-white.svg106
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-full-size.pngbin0 -> 6682 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-full-size.svg97
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon.pngbin0 -> 2090 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon.svg93
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo-foreground-white.pngbin0 -> 2692 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo-foreground-white.svg122
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo.ai243
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo.pngbin0 -> 6494 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo.svg106
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-foreground-white.pngbin0 -> 2357 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-foreground-white.svg106
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-full-size.pngbin0 -> 6741 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-full-size.svg107
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon.pngbin0 -> 2350 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon.svg93
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo-foreground-white.pngbin0 -> 2974 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo-foreground-white.svg122
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo.pngbin0 -> 6732 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo.svg117
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-foreground-white.pngbin0 -> 2306 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-foreground-white.svg106
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-full-size.pngbin0 -> 6162 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-full-size.svg107
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon.pngbin0 -> 2306 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon.svg93
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo-foreground-white.pngbin0 -> 3008 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo-foreground-white.svg122
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo.pngbin0 -> 6291 bytes
-rw-r--r--storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo.svg117
-rwxr-xr-xstorage/mroonga/vendor/groonga/data/images/logo/update-files.sh17
-rw-r--r--storage/mroonga/vendor/groonga/data/munin/Makefile.am12
-rwxr-xr-xstorage/mroonga/vendor/groonga/data/munin/groonga_cpu_load_100
-rwxr-xr-xstorage/mroonga/vendor/groonga/data/munin/groonga_cpu_time_122
-rwxr-xr-xstorage/mroonga/vendor/groonga/data/munin/groonga_disk_246
-rwxr-xr-xstorage/mroonga/vendor/groonga/data/munin/groonga_memory_104
-rwxr-xr-xstorage/mroonga/vendor/groonga/data/munin/groonga_n_records_256
-rwxr-xr-xstorage/mroonga/vendor/groonga/data/munin/groonga_query_performance_198
-rwxr-xr-xstorage/mroonga/vendor/groonga/data/munin/groonga_status_176
-rwxr-xr-xstorage/mroonga/vendor/groonga/data/munin/groonga_throughput_176
-rw-r--r--storage/mroonga/vendor/groonga/data/scripts/Makefile.am1
-rwxr-xr-xstorage/mroonga/vendor/groonga/data/scripts/groonga-httpd-restart.in106
-rw-r--r--storage/mroonga/vendor/groonga/data/synonyms.tsv5
-rw-r--r--storage/mroonga/vendor/groonga/data/systemd/Makefile.am1
-rw-r--r--storage/mroonga/vendor/groonga/data/systemd/fedora/Makefile.am13
-rw-r--r--storage/mroonga/vendor/groonga/data/systemd/fedora/groonga-httpd.service14
-rw-r--r--storage/mroonga/vendor/groonga/data/systemd/fedora/groonga-server-gqtp.service13
-rw-r--r--storage/mroonga/vendor/groonga/data/systemd/fedora/groonga-server-http.service13
-rw-r--r--storage/mroonga/vendor/groonga/data/systemd/fedora/sysconfig/Makefile.am8
-rw-r--r--storage/mroonga/vendor/groonga/data/systemd/fedora/sysconfig/groonga-server-gqtp9
-rw-r--r--storage/mroonga/vendor/groonga/data/systemd/fedora/sysconfig/groonga-server-http9
-rwxr-xr-xstorage/mroonga/vendor/groonga/data/travis/setup.sh55
-rw-r--r--storage/mroonga/vendor/groonga/examples/Makefile.am1
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/Makefile.am34
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/edict/Makefile.am4
-rwxr-xr-xstorage/mroonga/vendor/groonga/examples/dictionary/edict/edict-import.sh21
-rwxr-xr-xstorage/mroonga/vendor/groonga/examples/dictionary/edict/edict2grn.rb56
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/eijiro/Makefile.am4
-rwxr-xr-xstorage/mroonga/vendor/groonga/examples/dictionary/eijiro/eijiro-import.sh12
-rwxr-xr-xstorage/mroonga/vendor/groonga/examples/dictionary/eijiro/eijiro2grn.rb61
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/gene95/Makefile.am4
-rwxr-xr-xstorage/mroonga/vendor/groonga/examples/dictionary/gene95/gene-import.sh26
-rwxr-xr-xstorage/mroonga/vendor/groonga/examples/dictionary/gene95/gene2grn.rb46
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/dictionary.css3
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.pngbin0 -> 180 bytes
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.pngbin0 -> 178 bytes
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.pngbin0 -> 120 bytes
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.pngbin0 -> 105 bytes
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_75_dadada_1x400.pngbin0 -> 111 bytes
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.pngbin0 -> 110 bytes
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.pngbin0 -> 119 bytes
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.pngbin0 -> 101 bytes
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_222222_256x240.pngbin0 -> 4369 bytes
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_2e83ff_256x240.pngbin0 -> 4369 bytes
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_454545_256x240.pngbin0 -> 4369 bytes
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_888888_256x240.pngbin0 -> 4369 bytes
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_cd0a0a_256x240.pngbin0 -> 4369 bytes
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/jquery-ui-1.8.12.custom.css578
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/index.html28
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/js/dictionary.js82
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/js/jquery-1.7.2.min.js4
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/html/js/jquery-ui-1.8.18.custom.min.js356
-rwxr-xr-xstorage/mroonga/vendor/groonga/examples/dictionary/init-db.sh10
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/jmdict/Makefile.am3
-rwxr-xr-xstorage/mroonga/vendor/groonga/examples/dictionary/jmdict/jmdict.rb42
-rw-r--r--storage/mroonga/vendor/groonga/examples/dictionary/readme.txt71
-rw-r--r--storage/mroonga/vendor/groonga/gpg_uid1
-rw-r--r--storage/mroonga/vendor/groonga/groonga-httpd-conf.sh.in26
-rw-r--r--storage/mroonga/vendor/groonga/groonga.pc.in20
-rw-r--r--storage/mroonga/vendor/groonga/include/CMakeLists.txt20
-rw-r--r--storage/mroonga/vendor/groonga/include/Makefile.am6
-rw-r--r--storage/mroonga/vendor/groonga/include/groonga.h2101
-rw-r--r--storage/mroonga/vendor/groonga/include/groonga/Makefile.am6
-rw-r--r--storage/mroonga/vendor/groonga/include/groonga/nfkc.h32
-rw-r--r--storage/mroonga/vendor/groonga/include/groonga/normalizer.h55
-rw-r--r--storage/mroonga/vendor/groonga/include/groonga/plugin.h152
-rw-r--r--storage/mroonga/vendor/groonga/include/groonga/tokenizer.h225
-rw-r--r--storage/mroonga/vendor/groonga/lib/CMakeLists.txt90
-rw-r--r--storage/mroonga/vendor/groonga/lib/Makefile.am46
-rw-r--r--storage/mroonga/vendor/groonga/lib/com.c1161
-rw-r--r--storage/mroonga/vendor/groonga/lib/com.h260
-rw-r--r--storage/mroonga/vendor/groonga/lib/ctx.c2992
-rw-r--r--storage/mroonga/vendor/groonga/lib/ctx.h506
-rw-r--r--storage/mroonga/vendor/groonga/lib/ctx_impl.h188
-rw-r--r--storage/mroonga/vendor/groonga/lib/ctx_impl_mrb.c104
-rw-r--r--storage/mroonga/vendor/groonga/lib/ctx_impl_mrb.h36
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat.cpp1099
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat.h99
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/Makefile.am12
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/array.hpp100
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/base.hpp69
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/block.hpp96
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/check.hpp151
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/cursor-factory.cpp94
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/cursor-factory.hpp46
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/cursor.hpp48
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/dat.hpp255
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/entry.hpp61
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/file-impl.cpp244
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/file-impl.hpp73
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/file.cpp67
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/file.hpp60
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/header.hpp181
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/id-cursor.cpp184
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/id-cursor.hpp85
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/key-cursor.cpp349
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/key-cursor.hpp90
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/key.hpp112
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/node.hpp129
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/predictive-cursor.cpp206
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/predictive-cursor.hpp86
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/prefix-cursor.cpp175
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/prefix-cursor.hpp80
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/sources.am29
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/string.hpp175
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/trie.cpp1213
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/trie.hpp285
-rw-r--r--storage/mroonga/vendor/groonga/lib/dat/vector.hpp193
-rw-r--r--storage/mroonga/vendor/groonga/lib/db.c10615
-rw-r--r--storage/mroonga/vendor/groonga/lib/db.h469
-rw-r--r--storage/mroonga/vendor/groonga/lib/ecmascript.c2242
-rw-r--r--storage/mroonga/vendor/groonga/lib/ecmascript.h72
-rw-r--r--storage/mroonga/vendor/groonga/lib/ecmascript.lemon473
-rw-r--r--storage/mroonga/vendor/groonga/lib/error.c53
-rw-r--r--storage/mroonga/vendor/groonga/lib/error.h35
-rw-r--r--storage/mroonga/vendor/groonga/lib/expr.c7085
-rw-r--r--storage/mroonga/vendor/groonga/lib/expr.h70
-rw-r--r--storage/mroonga/vendor/groonga/lib/geo.c2682
-rw-r--r--storage/mroonga/vendor/groonga/lib/geo.h207
-rw-r--r--storage/mroonga/vendor/groonga/lib/groonga_in.h746
-rw-r--r--storage/mroonga/vendor/groonga/lib/hash.c3342
-rw-r--r--storage/mroonga/vendor/groonga/lib/hash.h346
-rw-r--r--storage/mroonga/vendor/groonga/lib/icudump.c294
-rw-r--r--storage/mroonga/vendor/groonga/lib/ii.c7396
-rw-r--r--storage/mroonga/vendor/groonga/lib/ii.h196
-rw-r--r--storage/mroonga/vendor/groonga/lib/io.c1801
-rw-r--r--storage/mroonga/vendor/groonga/lib/io.h494
-rw-r--r--storage/mroonga/vendor/groonga/lib/libgroonga.c8
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb.c226
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb.h43
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/Makefile.am20
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_accessor.c71
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_accessor.h35
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_bulk.c149
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_bulk.h35
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_column.c39
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_column.h34
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_converter.c93
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_converter.h37
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_ctx.c224
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_ctx.h34
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_expr.c460
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_expr.h36
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_fixed_size_column.c59
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_fixed_size_column.h34
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_id.c74
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_id.h34
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_index_column.c59
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_index_column.h34
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_logger.c76
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_logger.h34
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_obj.c114
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_obj.h34
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_operator.c190
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_operator.h34
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_procedure.c58
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_procedure.h34
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_variable_size_column.c60
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_variable_size_column.h34
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_void.c59
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/mrb_void.h34
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/Makefile.am9
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/backtrace_entry.rb20
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/context.rb31
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/context/error_level.rb30
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/context/rc.rb99
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/eval_context.rb13
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/expression.rb13
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/index_info.rb10
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/logger.rb27
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/level.rb32
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/object.rb7
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info.rb27
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_builder.rb243
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_data.rb171
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/sources.am13
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/scripts/test/empty.rb1
-rw-r--r--storage/mroonga/vendor/groonga/lib/mrb/sources.am31
-rw-r--r--storage/mroonga/vendor/groonga/lib/nfkc-custom-rules.txt1
-rw-r--r--storage/mroonga/vendor/groonga/lib/nfkc.c80249
-rwxr-xr-xstorage/mroonga/vendor/groonga/lib/nfkc.rb418
-rw-r--r--storage/mroonga/vendor/groonga/lib/normalizer.c1195
-rw-r--r--storage/mroonga/vendor/groonga/lib/normalizer_in.h52
-rw-r--r--storage/mroonga/vendor/groonga/lib/output.c1926
-rw-r--r--storage/mroonga/vendor/groonga/lib/output.h89
-rw-r--r--storage/mroonga/vendor/groonga/lib/pat.c2913
-rw-r--r--storage/mroonga/vendor/groonga/lib/pat.h113
-rw-r--r--storage/mroonga/vendor/groonga/lib/plugin.c753
-rw-r--r--storage/mroonga/vendor/groonga/lib/plugin_in.h68
-rw-r--r--storage/mroonga/vendor/groonga/lib/proc.c5247
-rw-r--r--storage/mroonga/vendor/groonga/lib/proc.h35
-rw-r--r--storage/mroonga/vendor/groonga/lib/snip.c838
-rw-r--r--storage/mroonga/vendor/groonga/lib/snip.h132
-rw-r--r--storage/mroonga/vendor/groonga/lib/sources.am51
-rw-r--r--storage/mroonga/vendor/groonga/lib/store.c1779
-rw-r--r--storage/mroonga/vendor/groonga/lib/store.h160
-rw-r--r--storage/mroonga/vendor/groonga/lib/str.c3181
-rw-r--r--storage/mroonga/vendor/groonga/lib/str.h129
-rw-r--r--storage/mroonga/vendor/groonga/lib/string.c406
-rw-r--r--storage/mroonga/vendor/groonga/lib/string_in.h65
-rw-r--r--storage/mroonga/vendor/groonga/lib/token.c778
-rw-r--r--storage/mroonga/vendor/groonga/lib/token.h91
-rw-r--r--storage/mroonga/vendor/groonga/lib/tokenizer.c320
-rw-r--r--storage/mroonga/vendor/groonga/lib/util.c1148
-rw-r--r--storage/mroonga/vendor/groonga/lib/util.h48
-rw-r--r--storage/mroonga/vendor/groonga/nginx_version1
-rw-r--r--storage/mroonga/vendor/groonga/packages/Makefile.am7
-rw-r--r--storage/mroonga/vendor/groonga/packages/apt/Makefile.am79
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/apt/build-deb.sh115
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/apt/build-in-chroot.sh134
-rw-r--r--storage/mroonga/vendor/groonga/packages/apt/debian/changelog11
-rw-r--r--storage/mroonga/vendor/groonga/packages/apt/debian/compat1
-rw-r--r--storage/mroonga/vendor/groonga/packages/apt/debian/control18
-rw-r--r--storage/mroonga/vendor/groonga/packages/apt/debian/copyright34
-rw-r--r--storage/mroonga/vendor/groonga/packages/apt/debian/groonga-keyring.install1
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/apt/debian/groonga-keyring.postinst22
-rw-r--r--storage/mroonga/vendor/groonga/packages/apt/debian/groonga-keyring.postrm.in26
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/apt/debian/rules17
-rw-r--r--storage/mroonga/vendor/groonga/packages/apt/debian/source/format1
-rw-r--r--storage/mroonga/vendor/groonga/packages/apt/groonga-depended-packages7
-rw-r--r--storage/mroonga/vendor/groonga/packages/apt/groonga-keyring-depended-packages1
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/apt/sign-packages.sh42
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/apt/sign-repository.sh46
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/apt/update-repository.sh130
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/check-utility.sh538
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/changelog414
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/compat1
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/control210
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/copyright109
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-bin.install6
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-doc.install1
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-doc.links4
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-examples.install1
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.default15
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.dirs3
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/debian/groonga-httpd.init193
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.install3
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.logrotate17
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/debian/groonga-httpd.postinst26
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/debian/groonga-httpd.postrm12
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.conf24
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.install2
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.links8
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.postinst27
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.postrm9
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-plugin-suggest.install2
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/debian/groonga-server-common.postinst42
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/debian/groonga-server-common.postrm13
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.default15
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.dirs2
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.init235
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.logrotate17
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.default16
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.dirs2
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/debian/groonga-server-http.init250
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.logrotate17
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/groonga-tokenizer-mecab.install1
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/libgroonga-dev.install3
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/libgroonga0.install4
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery-1.7.2.js9404
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery-ui-1.8.18.custom.js11802
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery.flot-0.7.js2599
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery.js9472
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/missing-sources/underscore.js999
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/patches/series0
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/debian/rules31
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/source/format1
-rw-r--r--storage/mroonga/vendor/groonga/packages/debian/watch3
-rw-r--r--storage/mroonga/vendor/groonga/packages/release-key-secret.asc.gpg.hayashibin0 -> 1458 bytes
-rw-r--r--storage/mroonga/vendor/groonga/packages/release-key-secret.asc.gpg.koubin0 -> 1457 bytes
-rw-r--r--storage/mroonga/vendor/groonga/packages/release-key-secret.asc.gpg.yoshiharabin0 -> 1457 bytes
-rw-r--r--storage/mroonga/vendor/groonga/packages/rpm/Makefile.am1
-rw-r--r--storage/mroonga/vendor/groonga/packages/rpm/centos/Makefile.am2
-rw-r--r--storage/mroonga/vendor/groonga/packages/rpm/centos/groonga.spec.in573
-rw-r--r--storage/mroonga/vendor/groonga/packages/rpm/fedora/Makefile.am3
-rw-r--r--storage/mroonga/vendor/groonga/packages/rpm/fedora/groonga.spec.in641
-rw-r--r--storage/mroonga/vendor/groonga/packages/source/Makefile.am25
-rw-r--r--storage/mroonga/vendor/groonga/packages/ubuntu/Makefile.am24
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/ubuntu/upload.rb135
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/Makefile.am130
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/Rakefile248
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/create-setup.bat2
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/language-files/Makefile.am3
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/language-files/english.nsi2
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/language-files/japanese.nsi2
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/patches/Makefile.am3
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.98-add-missing-dll-export.diff11
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.98-mingw-w64.diff13
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.98-not-use-locale-on-mingw.diff15
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-cflags.diff12
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-externc.diff12
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-ltmain-wmain.diff11
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-yieldprocessor-macro.diff11
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/patches/mecab-mingw-unsigned-long-long-int.diff24
-rw-r--r--storage/mroonga/vendor/groonga/packages/windows/setup-x64.nsi.in112
-rw-r--r--storage/mroonga/vendor/groonga/packages/yum/Makefile.am82
-rw-r--r--storage/mroonga/vendor/groonga/packages/yum/Vagrantfile41
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/yum/build-release-rpm.sh99
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/yum/build-rpm.sh124
-rw-r--r--storage/mroonga/vendor/groonga/packages/yum/env.sh.in19
-rw-r--r--storage/mroonga/vendor/groonga/packages/yum/patches/mecab-ipadic-provides-mecab-dic.diff10
-rw-r--r--storage/mroonga/vendor/groonga/packages/yum/patches/mecab-jumandic-provides-mecab-dic.diff10
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/yum/sign-rpm.sh44
-rwxr-xr-xstorage/mroonga/vendor/groonga/packages/yum/update-repository.sh33
-rw-r--r--storage/mroonga/vendor/groonga/plugins/CMakeLists.txt20
-rw-r--r--storage/mroonga/vendor/groonga/plugins/Makefile.am9
-rw-r--r--storage/mroonga/vendor/groonga/plugins/query_expanders/CMakeLists.txt32
-rw-r--r--storage/mroonga/vendor/groonga/plugins/query_expanders/Makefile.am20
-rw-r--r--storage/mroonga/vendor/groonga/plugins/query_expanders/tsv.c299
-rw-r--r--storage/mroonga/vendor/groonga/plugins/query_expanders/tsv_sources.am2
-rw-r--r--storage/mroonga/vendor/groonga/plugins/ruby/CMakeLists.txt44
-rw-r--r--storage/mroonga/vendor/groonga/plugins/ruby/Makefile.am29
-rw-r--r--storage/mroonga/vendor/groonga/plugins/ruby/eval.c64
-rw-r--r--storage/mroonga/vendor/groonga/plugins/ruby/eval_sources.am3
-rw-r--r--storage/mroonga/vendor/groonga/plugins/ruby/load.c63
-rw-r--r--storage/mroonga/vendor/groonga/plugins/ruby/load_sources.am3
-rw-r--r--storage/mroonga/vendor/groonga/plugins/ruby/ruby_plugin.h76
-rw-r--r--storage/mroonga/vendor/groonga/plugins/suggest/CMakeLists.txt29
-rw-r--r--storage/mroonga/vendor/groonga/plugins/suggest/Makefile.am24
-rw-r--r--storage/mroonga/vendor/groonga/plugins/suggest/sources.am2
-rw-r--r--storage/mroonga/vendor/groonga/plugins/suggest/suggest.c1022
-rw-r--r--storage/mroonga/vendor/groonga/plugins/table/CMakeLists.txt29
-rw-r--r--storage/mroonga/vendor/groonga/plugins/table/Makefile.am24
-rw-r--r--storage/mroonga/vendor/groonga/plugins/table/sources.am2
-rw-r--r--storage/mroonga/vendor/groonga/plugins/table/table.c747
-rw-r--r--storage/mroonga/vendor/groonga/plugins/tokenizers/CMakeLists.txt53
-rw-r--r--storage/mroonga/vendor/groonga/plugins/tokenizers/Makefile.am33
-rw-r--r--storage/mroonga/vendor/groonga/plugins/tokenizers/kytea.cpp354
-rw-r--r--storage/mroonga/vendor/groonga/plugins/tokenizers/kytea_sources.am2
-rw-r--r--storage/mroonga/vendor/groonga/plugins/tokenizers/mecab.c338
-rw-r--r--storage/mroonga/vendor/groonga/plugins/tokenizers/mecab_sources.am2
-rw-r--r--storage/mroonga/vendor/groonga/src/.gitignore2
-rw-r--r--storage/mroonga/vendor/groonga/src/CMakeLists.txt54
-rw-r--r--storage/mroonga/vendor/groonga/src/Makefile.am48
-rw-r--r--storage/mroonga/vendor/groonga/src/grnslap.c373
-rw-r--r--storage/mroonga/vendor/groonga/src/grnslap_sources.am2
-rw-r--r--storage/mroonga/vendor/groonga/src/groonga.c2819
-rw-r--r--storage/mroonga/vendor/groonga/src/groonga_benchmark.c3172
-rw-r--r--storage/mroonga/vendor/groonga/src/groonga_benchmark_sources.am2
-rw-r--r--storage/mroonga/vendor/groonga/src/groonga_sources.am2
-rw-r--r--storage/mroonga/vendor/groonga/src/httpd/Makefile.am32
-rw-r--r--storage/mroonga/vendor/groonga/src/httpd/nginx-module/config52
-rw-r--r--storage/mroonga/vendor/groonga/src/httpd/nginx-module/ngx_http_groonga_module.c1440
-rw-r--r--storage/mroonga/vendor/groonga/src/suggest/CMakeLists.txt90
-rw-r--r--storage/mroonga/vendor/groonga/src/suggest/Makefile.am77
-rw-r--r--storage/mroonga/vendor/groonga/src/suggest/create_dataset_sources.am2
-rw-r--r--storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_create_dataset.c220
-rw-r--r--storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_ddl.txt62
-rw-r--r--storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_httpd.c842
-rw-r--r--storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_learner.c839
-rw-r--r--storage/mroonga/vendor/groonga/src/suggest/httpd_sources.am3
-rw-r--r--storage/mroonga/vendor/groonga/src/suggest/learner_sources.am3
-rw-r--r--storage/mroonga/vendor/groonga/src/suggest/util.c215
-rw-r--r--storage/mroonga/vendor/groonga/src/suggest/util.h40
-rw-r--r--storage/mroonga/vendor/groonga/src/suggest/util_sources.am3
-rw-r--r--storage/mroonga/vendor/groonga/src/suggest/zmq_compatible.h33
-rw-r--r--storage/mroonga/vendor/groonga/tools/Makefile.am6
-rwxr-xr-xstorage/mroonga/vendor/groonga/tools/groonga-memory-leak-checker.rb93
-rwxr-xr-xstorage/mroonga/vendor/groonga/tools/groonga-suggest-httpd-client.rb181
-rwxr-xr-xstorage/mroonga/vendor/groonga/tools/prepare-sphinx-html.rb183
-rwxr-xr-xstorage/mroonga/vendor/groonga/tools/travis-before-script.sh33
-rwxr-xr-xstorage/mroonga/vendor/groonga/tools/travis-script.sh15
-rw-r--r--storage/mroonga/vendor/groonga/vendor/CMakeLists.txt16
-rw-r--r--storage/mroonga/vendor/groonga/vendor/Makefile.am17
-rw-r--r--storage/mroonga/vendor/groonga/vendor/mruby/CMakeLists.txt35
-rw-r--r--storage/mroonga/vendor/groonga/vendor/mruby/Makefile.am60
-rw-r--r--storage/mroonga/vendor/groonga/vendor/mruby/build_config.rb33
-rw-r--r--storage/mroonga/vendor/groonga/vendor/mruby/sources.am56
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/mruby/update.rb88
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/CHANGES6766
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/CHANGES.ru6875
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/LICENSE26
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/README3
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/acc15
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/bcc72
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/ccc46
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/clang104
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/conf218
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/gcc186
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/icc121
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/msvc136
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/name89
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/owc104
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/sunc158
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/define12
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/endianness45
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/feature123
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/have12
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/have_headers12
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/headers13
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/include61
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/init51
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/conf83
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/geoip/conf94
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/google-perftools/conf61
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libatomic/conf43
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libatomic/make16
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libgd/conf83
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libxslt/conf156
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/make32
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/conf103
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/make96
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/makefile.bcc22
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/makefile.msvc22
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/makefile.owc11
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/conf78
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/make67
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/makefile.bcc18
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/makefile.msvc14
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/conf203
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/make64
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/makefile.bcc27
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/makefile.msvc23
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/makefile.owc25
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/perl/conf78
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/perl/make41
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/conf79
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/make96
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/makefile.bcc22
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/makefile.msvc22
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/makefile.owc11
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/test40
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/conf79
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/make135
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/makefile.bcc17
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/makefile.msvc17
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/makefile.owc14
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/make417
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/modules534
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/nohave12
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/options527
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/conf107
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/darwin116
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/freebsd144
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/linux185
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/solaris61
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/win3240
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/sources557
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/stubs8
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/summary116
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/sizeof84
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/typedef77
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/uintptr_t45
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/value12
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/unix827
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/fastcgi.conf25
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/fastcgi_params24
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/koi-utf109
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/koi-win103
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/mime.types89
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/nginx.conf117
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/scgi_params16
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/uwsgi_params16
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/win-utf126
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/README21
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/geo2nginx.pl58
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/unicode2nginx/koi-utf131
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/unicode2nginx/unicode-to-nginx.pl45
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/unicode2nginx/win-utf130
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/vim/ftdetect/nginx.vim4
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/vim/indent/nginx.vim11
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/vim/syntax/nginx.vim703
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/html/50x.html21
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/html/index.html25
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/man/nginx.8202
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_aio_module.c171
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_devpoll_module.c575
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_epoll_module.c845
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_eventport_module.c633
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_kqueue_module.c785
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_poll_module.c443
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_rtsig_module.c747
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_select_module.c435
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_win32_select_module.c400
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event.c1339
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event.h569
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_accept.c515
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_busy_lock.c286
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_busy_lock.h65
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_connect.c258
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_connect.h76
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_mutex.c70
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_openssl.c3344
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_openssl.h215
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_openssl_stapling.c1762
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_pipe.c1006
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_pipe.h94
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_posted.c173
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_posted.h75
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_timer.c158
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_timer.h102
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_access_module.c469
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_addition_filter_module.c251
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_auth_basic_module.c467
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_auth_request_module.c444
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_autoindex_module.c705
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_browser_module.c715
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_charset_filter_module.c1685
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_chunked_filter_module.c243
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_dav_module.c1156
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_degradation_module.c243
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_empty_gif_module.c140
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_fastcgi_module.c3279
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_flv_module.c266
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_geo_module.c1644
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_geoip_module.c925
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_gunzip_filter_module.c681
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_gzip_filter_module.c1229
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_gzip_static_module.c331
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_headers_filter_module.c635
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_image_filter_module.c1520
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_index_module.c540
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_limit_conn_module.c767
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_limit_req_module.c989
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_log_module.c1792
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_map_module.c567
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_memcached_module.c700
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_mp4_module.c3500
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_not_modified_filter_module.c266
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_proxy_module.c3920
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_random_index_module.c317
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_range_filter_module.c890
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_realip_module.c442
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_referer_module.c671
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_rewrite_module.c1025
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_scgi_module.c1810
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_secure_link_module.c368
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_split_clients_module.c246
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssi_filter_module.c2930
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssi_filter_module.h114
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssl_module.c962
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssl_module.h65
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_static_module.c290
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_stub_status_module.c236
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_sub_filter_module.c756
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_hash_module.c631
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_ip_hash_module.c279
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_keepalive_module.c536
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_least_conn_module.c408
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_userid_filter_module.c842
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_uwsgi_module.c2121
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_xslt_filter_module.c1147
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/Makefile.PL33
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/nginx.pm138
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/nginx.xs1038
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/ngx_http_perl_module.c1076
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/ngx_http_perl_module.h67
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/typemap3
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http.c2118
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http.h186
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_busy_lock.c307
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_busy_lock.h54
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_cache.h169
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_config.h75
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_copy_filter_module.c303
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_core_module.c5283
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_core_module.h589
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_file_cache.c2015
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_header_filter_module.c633
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_parse.c2359
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_parse_time.c277
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_postpone_filter_module.c176
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_request.c3650
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_request.h598
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_request_body.c1099
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_script.c1754
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_script.h257
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy.c3647
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy.h261
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy_filter_module.c1213
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy_module.c408
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy_module.h41
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_special_response.c792
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream.c5553
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream.h396
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream_round_robin.c697
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream_round_robin.h90
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_variables.c2613
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_variables.h112
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_write_filter_module.c318
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail.c568
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail.h421
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_auth_http_module.c1461
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_core_module.c653
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_handler.c788
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_imap_handler.c457
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_imap_module.c253
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_imap_module.h39
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_parse.c918
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_pop3_handler.c500
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_pop3_module.c264
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_pop3_module.h38
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_proxy_module.c1136
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_smtp_handler.c857
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_smtp_module.c307
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_smtp_module.h45
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_ssl_module.c568
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_ssl_module.h57
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/misc/ngx_cpp_test_module.cpp29
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/misc/ngx_google_perftools_module.c126
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_read.c109
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_read_chain.c78
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_write.c109
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_write_chain.c100
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_alloc.c90
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_alloc.h45
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_atomic.h313
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_channel.c260
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_channel.h34
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_daemon.c70
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin.h23
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin_config.h95
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin_init.c196
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin_sendfile_chain.c370
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_errno.c87
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_errno.h78
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_file_aio_read.c208
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_files.c564
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_files.h386
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd.h25
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_config.h127
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_init.c260
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_rfork_thread.c756
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_rfork_thread.h122
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_sendfile_chain.c440
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_amd64.h82
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_ppc.h155
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_sparc64.h82
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_x86.h127
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux.h18
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_aio_read.c137
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_config.h127
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_init.c91
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_sendfile_chain.c378
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_os.h83
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_posix_config.h158
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_posix_init.c128
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process.c630
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process.h88
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process_cycle.c1413
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process_cycle.h61
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_pthread_thread.c278
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_readv_chain.c271
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_recv.c183
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_send.c73
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setaffinity.c69
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setaffinity.h23
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setproctitle.c135
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setproctitle.h52
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_shmem.c126
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_shmem.h29
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_socket.c116
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_socket.h64
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris.h16
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris_config.h110
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris_init.c75
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris_sendfilev_chain.c260
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_amd64.il43
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_atomic_sparc64.h61
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_sparc64.il36
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_x86.il44
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_thread.h128
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_time.c104
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_time.h66
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_udp_recv.c115
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_user.c108
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_user.h24
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_writev_chain.c185
-rw-r--r--storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/rfork_thread.S73
-rw-r--r--storage/mroonga/vendor/groonga/vendor/onigmo/Makefile.am22
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/CMakeLists.txt27
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/AUTHORS1
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/CMakeLists.txt65
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/ChangeLog0
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/INSTALL1
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/Makefile.am84
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/NEWS1
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/README1
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/README.md201
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/autogen.sh20
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/build/Makefile.am18
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/build/cmake_modules/Makefile.am18
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/build/cmake_modules/ReadFileList.cmake27
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/configure.ac179
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/data/travis/setup.sh34
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/doc/Makefile.am18
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/doc/text/Makefile.am19
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/doc/text/lgpl-2.0.txt481
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/gpg_uid1
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/groonga-normalizer-mysql.pc.in5
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/CMakeLists.txt27
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/Makefile.am69
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql.c666
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_general_ci_table.h565
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_sources.am5
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_table.h1685
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_unicode_ci_table.h1685
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/Makefile.am6
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/Makefile.am60
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/build-deb.sh86
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/build-in-chroot.sh134
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/groonga-normalizer-mysql-depended-packages4
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/sign-packages.sh42
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/sign-repository.sh46
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/update-repository.sh130
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/changelog41
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/compat1
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/control25
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/copyright29
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/groonga-normalizer-mysql.install1
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/patches/series0
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/rules15
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/source/format1
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/Makefile.am1
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/centos/Makefile.am1
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/centos/groonga-normalizer-mysql.spec.in78
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/fedora/Makefile.am2
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/fedora/groonga-normalizer-mysql.spec.in77
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/source/Makefile.am25
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/ubuntu/Makefile.am28
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/Makefile.am72
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/Vagrantfile38
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/build-rpm.sh67
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/env.sh.in11
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/sign-rpm.sh37
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/update-repository.sh33
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/required_groonga_version1
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/dump_difference_uca.rb50
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/dump_difference_utf8.rb50
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/generate_uca_table.rb286
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/generate_utf8_table.rb146
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/parser.rb161
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/travis/before_script.sh23
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/travis/install.sh27
-rw-r--r--storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/version1
-rwxr-xr-xstorage/mroonga/vendor/groonga/vendor/update_nginx.sh33
-rwxr-xr-xstorage/mroonga/vendor/groonga/version-gen.sh35
-rw-r--r--storage/mroonga/version1
-rw-r--r--storage/mroonga/version_in_hex1
-rw-r--r--storage/mroonga/version_major1
-rw-r--r--storage/mroonga/version_micro1
-rw-r--r--storage/mroonga/version_minor1
2027 files changed, 460307 insertions, 0 deletions
diff --git a/.bzrignore b/.bzrignore
index 682d8e983d5..a98f46a84b2 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -1455,6 +1455,18 @@ storage/tokudb/ft-index/utils/tokudb_gen
storage/tokudb/ft-index/utils/tokudb_load
storage/connect/connect.cnf
storage/cassandra/cassandra.cnf
+storage/mroonga/config.sh
+storage/mroonga/mrn_version.h
+storage/mroonga/data/install.sql
+storage/mroonga/vendor/groonga/config.sh
+storage/mroonga/vendor/groonga/groonga.pc
+storage/mroonga/vendor/groonga/version.sh
+storage/mroonga/vendor/groonga/src/grnslap
+storage/mroonga/vendor/groonga/src/groonga
+storage/mroonga/vendor/groonga/src/groonga-benchmark
+storage/mroonga/vendor/groonga/src/suggest/groonga-suggest-create-dataset
+storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/groonga-normalizer-mysql.pc
+libmysql/libmysql.version
libmysql/libmysql_versions.ld
scripts/mysql_config.pl
pcre/pcre_chartables.c
diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp
index ca190104d79..5c54c4ff30c 100644
--- a/mysql-test/valgrind.supp
+++ b/mysql-test/valgrind.supp
@@ -1269,3 +1269,16 @@
fun:__tls_get_addr
}
+{
+ Mroonga: dlopen leaves some "still reachable"
+ Memcheck:Leak
+ fun:malloc
+ ...
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+}
diff --git a/storage/mroonga/AUTHORS b/storage/mroonga/AUTHORS
new file mode 100644
index 00000000000..c29bd9cc5fa
--- /dev/null
+++ b/storage/mroonga/AUTHORS
@@ -0,0 +1,7 @@
+Active developers:
+* Kentoku SHIBA
+* Kouhei Sutou
+
+Inactive developers:
+* Tetsuro IKEDA: The original author: Active
+* Yoshinori Matsunobu: The original author of information schema
diff --git a/storage/mroonga/CMakeLists.txt b/storage/mroonga/CMakeLists.txt
new file mode 100644
index 00000000000..5c420d9672b
--- /dev/null
+++ b/storage/mroonga/CMakeLists.txt
@@ -0,0 +1,380 @@
+# -*- indent-tabs-mode: nil -*-
+#
+# Copyright(C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+cmake_minimum_required(VERSION 2.6)
+project(mroonga)
+
+if(MSVC)
+ if(MSVC_VERSION LESS 1600)
+ message(STATUS "Mroonga supports only MSVC 2010 or later")
+ return()
+ endif()
+endif()
+
+if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
+ set(MRN_BUNDLED FALSE)
+else()
+ set(MRN_BUNDLED TRUE)
+endif()
+
+set(MRN_BUNDLED_GROONGA_RELATIVE_DIR "vendor/groonga")
+set(MRN_BUNDLED_GROONGA_DIR
+ "${CMAKE_CURRENT_SOURCE_DIR}/${MRN_BUNDLED_GROONGA_RELATIVE_DIR}")
+if(EXISTS "${MRN_BUNDLED_GROONGA_DIR}")
+ set(MRN_GROONGA_BUNDLED TRUE)
+else()
+ set(MRN_GROONGA_BUNDLED FALSE)
+ if(MSVC)
+ message(STATUS "Bundled Mroonga does not support MSVC yet")
+ return()
+ endif()
+endif()
+
+set(MRN_PLUGIN_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX})
+
+if(MRN_BUNDLED)
+ set(MRN_SOURCE_DIR ${CMAKE_SOURCE_DIR}/storage/mroonga)
+else()
+ set(MRN_SOURCE_DIR ${CMAKE_SOURCE_DIR})
+endif()
+
+file(READ ${MRN_SOURCE_DIR}/version MRN_VERSION)
+file(READ ${MRN_SOURCE_DIR}/version_major MRN_VERSION_MAJOR)
+file(READ ${MRN_SOURCE_DIR}/version_minor MRN_VERSION_MINOR)
+file(READ ${MRN_SOURCE_DIR}/version_micro MRN_VERSION_MICRO)
+file(READ ${MRN_SOURCE_DIR}/version_in_hex MRN_VERSION_IN_HEX)
+file(READ ${MRN_SOURCE_DIR}/plugin_version MRN_PLUGIN_VERSION)
+
+if(MRN_GROONGA_BUNDLED)
+ add_subdirectory("${MRN_BUNDLED_GROONGA_RELATIVE_DIR}")
+else()
+ file(READ ${MRN_SOURCE_DIR}/required_groonga_version REQUIRED_GROONGA_VERSION)
+ string(STRIP "${REQUIRED_GROONGA_VERSION}" REQUIRED_GROONGA_VERSION)
+
+ file(READ
+ ${MRN_SOURCE_DIR}/required_groonga_normalizer_mysql_version
+ REQUIRED_GROONGA_NORMALIZER_MYSQL_VERSION)
+ string(STRIP
+ "${REQUIRED_GROONGA_NORMALIZER_MYSQL_VERSION}"
+ REQUIRED_GROONGA_NORMALIZER_MYSQL_VERSION)
+endif()
+
+set(MRN_PACKAGE_STRING "${PROJECT_NAME} ${MRN_VERSION}")
+
+include(CheckCCompilerFlag)
+include(CheckCXXCompilerFlag)
+include(${MRN_SOURCE_DIR}/build/cmake_modules/ReadFileList.cmake)
+
+set(MRN_C_COMPILE_FLAGS "")
+set(MRN_CXX_COMPILE_FLAGS "")
+
+macro(mrn_check_cflag flag)
+ set(checking_message "checking for C flag '${flag}'")
+ check_c_compiler_flag(${flag} is_available)
+ if(${is_available})
+ message(STATUS "${checking_message} - available")
+ set(MRN_C_COMPILE_FLAGS "${MRN_C_COMPILE_FLAGS} ${flag}")
+ else()
+ message(STATUS "${checking_message} - not available")
+ endif()
+endmacro()
+
+macro(mrn_check_cxxflag flag)
+ set(checking_message "checking for CXX flag '${flag}'")
+ check_cxx_compiler_flag(${flag} is_available)
+ if(${is_available})
+ message(STATUS "${checking_message} - available")
+ set(MRN_CXX_COMPILE_FLAGS "${MRN_CXX_COMPILE_FLAGS} ${flag}")
+ else()
+ message(STATUS "${checking_message} - not available")
+ endif()
+endmacro()
+
+macro(mrn_build_flag flag)
+ mrn_check_cflag(${flag})
+ mrn_check_cxxflag(${flag})
+endmacro()
+
+if(MRN_BUNDLED)
+ set(MRN_RELATIVE_DIR_PREFIX "${MRN_SOURCE_DIR}/")
+else()
+ set(MRN_RELATIVE_DIR_PREFIX "")
+endif()
+
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/sources.am MROONGA_SOURCES)
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/lib/libmrn_no_mysql_sources.am
+ LIBMRN_NO_MYSQL_SOURCES)
+string(REGEX REPLACE "([^;]+)" "${MRN_RELATIVE_DIR_PREFIX}lib/\\1"
+ LIBMRN_NO_MYSQL_SOURCES "${LIBMRN_NO_MYSQL_SOURCES}")
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/lib/libmrn_need_mysql_sources.am
+ LIBMRN_NEED_MYSQL_SOURCES)
+string(REGEX REPLACE "([^;]+)" "${MRN_RELATIVE_DIR_PREFIX}lib/\\1"
+ LIBMRN_NEED_MYSQL_SOURCES "${LIBMRN_NEED_MYSQL_SOURCES}")
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/udf/sources.am MRN_UDF_SOURCES)
+string(REGEX REPLACE "([^;]+)" "${MRN_RELATIVE_DIR_PREFIX}udf/\\1"
+ MRN_UDF_SOURCES "${MRN_UDF_SOURCES}")
+
+if(MRN_BUNDLED)
+ set(MYSQL_SOURCE_DIR ${CMAKE_SOURCE_DIR})
+ set(MYSQL_BUILD_DIR ${MYSQL_SOURCE_DIR})
+ set(MYSQL_CONFIG ${CMAKE_SOURCE_DIR}/scripts/mysql_config)
+else()
+ set(MYSQL_SOURCE_DIR "/PATH/TO/MYSQL/SOURCE/DIRECTORY/"
+ CACHE PATH "MySQL source directory")
+ if(NOT EXISTS ${MYSQL_SOURCE_DIR})
+ message(FATAL_ERROR
+ "MySQL source directory (MYSQL_SOURCE_DIR) doesn't exist: <${MYSQL_SOURCE_DIR}>")
+ endif()
+ set(MYSQL_BUILD_DIR ${MYSQL_SOURCE_DIR} CACHE PATH "MySQL build directory")
+ set(MYSQL_CONFIG "mysql_config" CACHE PATH "mysql-config command path")
+endif()
+find_path(MYSQL_CONFIG "${MYSQL_CONFIG}")
+
+if(EXISTS "${MYSQL_SOURCE_DIR}/pcre")
+ set(MYSQL_REGEX_INCLUDE_DIR "${MYSQL_SOURCE_DIR}/pcre")
+else()
+ set(MYSQL_REGEX_INCLUDE_DIR "${MYSQL_SOURCE_DIR}/regex")
+endif()
+
+set(MYSQL_INCLUDE_DIRS
+ "${MYSQL_BUILD_DIR}/include"
+ "${MYSQL_SOURCE_DIR}/sql"
+ "${MYSQL_SOURCE_DIR}/include"
+ "${MYSQL_REGEX_INCLUDE_DIR}"
+ "${MYSQL_SOURCE_DIR}"
+ CACHE INTERNAL "MySQL include directories")
+
+if(MRN_BUNDLED)
+ set(MYSQL_PLUGIN_DIR "${INSTALL_PLUGINDIR}")
+ set(MYSQL_SERVICES_LIB_DIR "${MYSQL_BUILD_DIR}/libservices")
+ set(MYSQL_CFLAGS "${CMAKE_C_FLAGS}")
+ set(MYSQL_VERSION "${MYSQL_BASE_VERSION}")
+else()
+ macro(SET_MYSQL_CONFIG_VALUE OPTION VARIABLE)
+ if(NOT ${VARIABLE})
+ execute_process(COMMAND "${MYSQL_CONFIG}" ${OPTION}
+ OUTPUT_VARIABLE MYSQL_CONFIG_OUTPUT)
+ string(STRIP ${MYSQL_CONFIG_OUTPUT} ${VARIABLE})
+ endif()
+ endmacro()
+
+ set_mysql_config_value("--plugindir" MYSQL_PLUGIN_DIR)
+ set_mysql_config_value("--variable=pkglibdir" MYSQL_PKG_LIB_DIR)
+ set(MYSQL_BUILD_LIBSERVICES_DIR "${MYSQL_BUILD_DIR}/libservices")
+ if(EXISTS "${MYSQL_BUILD_LIBSERVICES_DIR}/libmysqlservices.a")
+ set(MYSQL_SERVICES_LIB_DIR "${MYSQL_BUILD_LIBSERVICES_DIR}")
+ else()
+ set(MYSQL_SERVICES_LIB_DIR "${MYSQL_PKG_LIB_DIR}")
+ endif()
+ set_mysql_config_value("--cflags" MYSQL_CFLAGS)
+ set_mysql_config_value("--version" MYSQL_VERSION)
+endif()
+
+if(${MYSQL_VERSION} VERSION_GREATER "10.0.0" AND
+ ${MYSQL_VERSION} VERSION_LESS "10.0.9")
+ message(FATAL_ERROR
+ "Mroonga doesn't support MariaDB 10.0.0-10.0.8: <${MYSQL_VERSION}>")
+ return()
+endif()
+
+if(MRN_GROONGA_BUNDLED)
+ set(GROONGA_INCLUDE_DIRS "${MRN_BUNDLED_GROONGA_DIR}/include")
+ set(GROONGA_LIBRARY_DIRS "${MRN_BUNDLED_GROONGA_DIR}/lib")
+ set(GROONGA_LIBRARIES "libgroonga")
+ if(EXISTS "${MRN_BUNDLED_GROONGA_DIR}/vendor/plugins/groonga-normalizer-mysql")
+ set(GROONGA_NORMALIZER_MYSQL_FOUND TRUE)
+ else()
+ set(GROONGA_NORMALIZER_MYSQL_FOUND FALSE)
+ endif()
+else()
+ include(FindPkgConfig)
+ pkg_check_modules(GROONGA REQUIRED "groonga >= ${REQUIRED_GROONGA_VERSION}")
+ pkg_check_modules(GROONGA_NORMALIZER_MYSQL
+ "groonga-normalizer-mysql >= ${REQUIRED_GROONGA_NORMALIZER_MYSQL_VERSION}")
+endif()
+
+if(GROONGA_NORMALIZER_MYSQL_FOUND AND MRN_GROONGA_BUNDLED)
+ read_file_list(${MRN_BUNDLED_GROONGA_DIR}/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_sources.am MRN_GRN_NORMALIZER_MYSQL_SOURCES)
+ string(REGEX REPLACE "([^;]+)" "${MRN_BUNDLED_GROONGA_DIR}/vendor/plugins/groonga-normalizer-mysql/normalizers/\\1"
+ MRN_GRN_NORMALIZER_MYSQL_SOURCES "${MRN_GRN_NORMALIZER_MYSQL_SOURCES}")
+endif()
+
+include_directories(
+ "${PROJECT_BINARY_DIR}"
+ "${PROJECT_SOURCE_DIR}"
+ "${PROJECT_SOURCE_DIR}/lib"
+ ${MYSQL_INCLUDE_DIRS}
+ ${GROONGA_INCLUDE_DIRS})
+
+if(WIN32)
+ set(MYSQL_LIBRARY_DIRS
+ "${MYSQL_BUILD_DIR}/lib"
+ "${MYSQL_BUILD_DIR}/libmysqld")
+else()
+ set(MYSQL_LIBRARY_DIRS
+ "${MYSQL_SERVICES_LIB_DIR}")
+endif()
+link_directories(
+ ${GROONGA_LIBRARY_DIRS}
+ ${MYSQL_LIBRARY_DIRS})
+
+if(MRN_BUNDLED)
+ if(GROONGA_NORMALIZER_MYSQL_FOUND AND MRN_GROONGA_BUNDLED)
+ mysql_add_plugin(ha_mroonga
+ "${MROONGA_SOURCES};${MRN_UDF_SOURCES};${MRN_GRN_NORMALIZER_MYSQL_SOURCES};${LIBMRN_NEED_MYSQL_SOURCES};${LIBMRN_NO_MYSQL_SOURCES}"
+ STORAGE_ENGINE MODULE_ONLY
+ LINK_LIBRARIES ${GROONGA_LIBRARIES}
+ MODULE_OUTPUT_NAME "ha_mroonga")
+ else()
+ mysql_add_plugin(ha_mroonga
+ "${MROONGA_SOURCES};${MRN_UDF_SOURCES};${LIBMRN_NEED_MYSQL_SOURCES};${LIBMRN_NO_MYSQL_SOURCES}"
+ STORAGE_ENGINE MODULE_ONLY
+ LINK_LIBRARIES ${GROONGA_LIBRARIES}
+ MODULE_OUTPUT_NAME "ha_mroonga")
+ endif()
+ set(EXPANDED_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBDIR}")
+ list(FIND
+ CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES
+ "${EXPANDED_INSTALL_LIBDIR}"
+ EXPANDED_INSTALL_LIBDIR_INDEX_IN_IMPLICIT_LINK_DIRECTORIES)
+ if(EXPANDED_INSTALL_LIBDIR_INDEX_IN_IMPLICIT_LINK_DIRECTORIES EQUAL -1)
+ set_property(TARGET ha_mroonga APPEND PROPERTY
+ INSTALL_RPATH "${EXPANDED_INSTALL_LIBDIR}")
+ endif()
+else()
+ add_library(ha_mroonga MODULE
+ ${MROONGA_SOURCES}
+ ${MRN_UDF_SOURCES}
+ ${LIBMRN_NO_MYSQL_SOURCES}
+ ${LIBMRN_NEED_MYSQL_SOURCES})
+
+ set(MYSQL_LIBRARIES "mysqlservices")
+ target_link_libraries(ha_mroonga ${GROONGA_LIBRARIES} ${MYSQL_LIBRARIES})
+
+ option(WITH_DEBUG "Enable debug options" OFF)
+ if(WITH_DEBUG)
+ set_property(TARGET ha_mroonga APPEND PROPERTY
+ COMPILE_DEFINITIONS "SAFE_MUTEX")
+ if(CMAKE_COMPILER_IS_GNUCXX)
+ set(MRN_C_COMPILE_FLAGS "${MRN_C_COMPILE_FLAGS} -g3 -O0")
+ set(MRN_CXX_COMPILE_FLAGS "${MRN_CXX_COMPILE_FLAGS} -g3 -O0")
+ endif()
+ else()
+ set_property(TARGET ha_mroonga APPEND PROPERTY
+ COMPILE_DEFINITIONS "DBUG_OFF")
+ endif()
+
+ option(WITH_DEBUG_FULL "Enable full debug options" OFF)
+ if(WITH_DEBUG_FULL)
+ set_property(TARGET ha_mroonga APPEND PROPERTY
+ COMPILE_DEFINITIONS "SAFE_MUTEX" "SAFEMALLOC")
+ endif()
+
+ option(DISABLE_FAST_MUTEXES "Force disabling fast mutex" OFF)
+ if(DISABLE_FAST_MUTEXES)
+ set_property(TARGET ha_mroonga APPEND PROPERTY
+ COMPILE_DEFINITIONS "FORCE_FAST_MUTEX_DISABLED=1")
+ endif()
+
+ option(WITH_FAST_MUTEXES "Enable fast mutex" OFF)
+ if(WITH_FAST_MUTEXES)
+ set_property(TARGET ha_mroonga APPEND PROPERTY
+ COMPILE_DEFINITIONS "MY_PTHREAD_FASTMUTEX")
+ endif()
+
+ if(CMAKE_COMPILER_IS_GNUCXX)
+ mrn_build_flag("-Wall")
+ mrn_build_flag("-Wextra")
+ mrn_build_flag("-Wno-unused-parameter")
+ mrn_build_flag("-Wno-strict-aliasing")
+ mrn_build_flag("-Wno-deprecated")
+ mrn_check_cxxflag("-fno-implicit-templates")
+ mrn_check_cxxflag("-fno-exceptions")
+ mrn_check_cxxflag("-fno-rtti")
+ mrn_check_cxxflag("-felide-constructors")
+ endif()
+ set_source_files_properties(${MROONGA_SOURCES} PROPERTIES
+ COMPILE_FLAGS "${MYSQL_CFLAGS} ${MRN_CXX_COMPILE_FLAGS}")
+ set_source_files_properties(${LIBMRN_NEED_MYSQL_SOURCES} PROPERTIES
+ COMPILE_FLAGS "${MYSQL_CFLAGS} ${MRN_CXX_COMPILE_FLAGS}")
+ set_source_files_properties(${MRN_UDF_SOURCES} PROPERTIES
+ COMPILE_FLAGS "${MRN_C_COMPILE_FLAGS}")
+ set_source_files_properties(${LIBMRN_NO_MYSQL_SOURCES} PROPERTIES
+ COMPILE_FLAGS "${MRN_C_COMPILE_FLAGS}")
+ set_property(TARGET ha_mroonga APPEND PROPERTY
+ COMPILE_DEFINITIONS "MYSQL_DYNAMIC_PLUGIN")
+ set_target_properties(ha_mroonga PROPERTIES
+ PREFIX ""
+ OUTPUT_NAME "ha_mroonga")
+
+ install(TARGETS ha_mroonga DESTINATION "${MYSQL_PLUGIN_DIR}")
+endif()
+
+if(GROONGA_NORMALIZER_MYSQL_FOUND)
+ set(WITH_GROONGA_NORMALIZER_MYSQL 1)
+ set_property(TARGET ha_mroonga APPEND PROPERTY
+ COMPILE_DEFINITIONS "WITH_GROONGA_NORMALIZER_MYSQL=1")
+ if(MRN_GROONGA_BUNDLED)
+ set_property(TARGET ha_mroonga APPEND PROPERTY
+ COMPILE_DEFINITIONS "GROONGA_NORMALIZER_MYSQL_PLUGIN_IS_BUNDLED_STATIC=1")
+ else()
+ set(GROONGA_NORMALIZER_MYSQL_PLUGIN_NAME \"normalizers/mysql\")
+ set_property(TARGET ha_mroonga APPEND PROPERTY
+ COMPILE_DEFINITIONS "GROONGA_NORMALIZER_MYSQL_PLUGIN_NAME=\"normalizers/mysql\"")
+ endif()
+endif()
+
+set(DEFAULT_PARSER "TokenBigram" CACHE STRING "The default fulltext parser")
+set_property(TARGET ha_mroonga APPEND PROPERTY
+ COMPILE_DEFINITIONS "MRN_PARSER_DEFAULT=\"${DEFAULT_PARSER}\"")
+
+configure_file(
+ "${PROJECT_SOURCE_DIR}/mrn_version.h.in"
+ "${PROJECT_BINARY_DIR}/mrn_version.h")
+
+configure_file(
+ "${PROJECT_SOURCE_DIR}/config.sh.in"
+ "${PROJECT_BINARY_DIR}/config.sh")
+
+set(MRN_TEST_SUITE_DIR "${CMAKE_SOURCE_DIR}/mysql-test/suite/mroonga")
+if(NOT EXISTS "${MRN_TEST_SUITE_DIR}")
+ set(MRN_TEST_SUITE_DIR "${PROJECT_SOURCE_DIR}/mysql-test/mroonga")
+endif()
+configure_file(
+ "${MRN_TEST_SUITE_DIR}/storage/r/information_schema_plugins.result.in"
+ "${MRN_TEST_SUITE_DIR}/storage/r/information_schema_plugins.result"
+ NEWLINE_STYLE LF)
+configure_file(
+ "${MRN_TEST_SUITE_DIR}/storage/r/variable_version.result.in"
+ "${MRN_TEST_SUITE_DIR}/storage/r/variable_version.result"
+ NEWLINE_STYLE LF)
+
+configure_file(
+ "${PROJECT_SOURCE_DIR}/data/install.sql.in"
+ "${PROJECT_BINARY_DIR}/data/install.sql")
+
+if(MRN_BUNDLED)
+ set(MRN_DATA_DIR "${INSTALL_MYSQLSHAREDIR}/${PROJECT_NAME}")
+else()
+ set(MRN_DATA_DIR "share/${PROJECT_NAME}")
+endif()
+install(FILES
+ "${PROJECT_BINARY_DIR}/data/install.sql"
+ "${PROJECT_SOURCE_DIR}/data/uninstall.sql"
+ DESTINATION "${MRN_DATA_DIR}/")
diff --git a/storage/mroonga/ChangeLog b/storage/mroonga/ChangeLog
new file mode 100644
index 00000000000..1a63e191506
--- /dev/null
+++ b/storage/mroonga/ChangeLog
@@ -0,0 +1,3 @@
+2009-01-27 Tetsuro IKEDA <ikdttr at gmail.com>
+
+ * initial import for development
diff --git a/storage/mroonga/Makefile.am b/storage/mroonga/Makefile.am
new file mode 100644
index 00000000000..250aefdd87c
--- /dev/null
+++ b/storage/mroonga/Makefile.am
@@ -0,0 +1,157 @@
+AUTOMAKE_OPTIONS = 1.9.7
+
+LOCALES = ja
+
+AM_CPPFLAGS = $(MYSQL_INCLUDES) $(GROONGA_CFLAGS) -I$(top_srcdir)/lib
+ACLOCAL_AMFLAGS = $$ACLOCAL_ARGS
+
+include sources.am
+
+libraries = \
+ $(top_builddir)/udf/libmrn_udf.la \
+ $(top_builddir)/lib/libmrn_no_mysql.la \
+ $(top_builddir)/lib/libmrn_need_mysql.la
+if WITH_LIBMYSQLSERVICES_COMPAT
+libraries += $(top_builddir)/lib/libmysqlservices.la
+endif
+
+dynamic_plugin_ldflags = -module $(GROONGA_LIBS) $(MYSQL_LIBS)
+dynamic_plugin_cxxflags = $(AM_CXXFLAGS) $(MYSQL_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN
+dynamic_plugin_cflags = $(AM_CFLAGS) $(MYSQL_CFLAGS) -DMYSQL_DYNAMIC_PLUGIN
+
+plugin_LTLIBRARIES = ha_mroonga.la
+ha_mroonga_la_LDFLAGS = $(dynamic_plugin_ldflags)
+ha_mroonga_la_CXXFLAGS = $(dynamic_plugin_cxxflags)
+ha_mroonga_la_CFLAGS = $(dynamic_plugin_cflags)
+ha_mroonga_la_SOURCES = $(sources)
+ha_mroonga_la_LIBADD = $(libraries)
+
+SUBDIRS = \
+ build \
+ lib \
+ udf \
+ test \
+ mysql-test \
+ doc \
+ tools \
+ packages \
+ data
+
+EXTRA_DIST = \
+ AUTHORS \
+ gpg_uid \
+ plug.in \
+ CMakeLists.txt
+
+installcheck-local: install
+ test/run-sql-test.sh
+
+tag:
+ cd $(top_srcdir) && \
+ git tag v$(VERSION) -a -m 'Mroonga $(VERSION)!!!'
+
+update-latest-release: misc
+ @if test -z "$(OLD_RELEASE)"; then \
+ echo "\$$(OLD_RELEASE) is missing"; \
+ exit 1; \
+ fi
+ @if test -z "$(OLD_RELEASE_DATE)"; then \
+ echo "\$$(OLD_RELEASE_DATE) is missing"; \
+ exit 1; \
+ fi
+ @if test -z "$(NEW_RELEASE_DATE)"; then \
+ echo "\$$(NEW_RELEASE_DATE) is missing"; \
+ exit 1; \
+ fi
+ cd $(top_srcdir) && \
+ misc/update-latest-release.rb \
+ $(PACKAGE) $(OLD_RELEASE) $(OLD_RELEASE_DATE) \
+ $(VERSION) $(NEW_RELEASE_DATE) \
+ packages/rpm/fedora/mysql-mroonga.spec.in \
+ packages/rpm/fedora/mariadb-mroonga.spec.in \
+ packages/rpm/centos/mariadb-mroonga.spec.in \
+ packages/rpm/centos/mysql55-mroonga.spec.in \
+ packages/rpm/centos/mysql56-community-mroonga.spec.in \
+ packages/debian/changelog \
+ doc/source/install/*.rst \
+ doc/locale/*/LC_MESSAGES/install.po \
+ $(MROONGA_GITHUB_COM_PATH)/index.html \
+ $(MROONGA_GITHUB_COM_PATH)/ja/index.html
+
+update-po:
+ @for lang in $(LOCALES); do \
+ (cd $(top_srcdir)/doc/locale/$$lang/LC_MESSAGES && make update) \
+ done
+
+update-document:
+ @if test -z "$(MROONGA_GITHUB_COM_PATH)"; then \
+ echo "\$$(MROONGA_GITHUB_COM_PATH) is missing"; \
+ echo "add --with-mroonga-github-com-path in configure"; \
+ exit 1; \
+ fi
+ rm -rf tmp-doc
+ mkdir tmp-doc
+ (cd doc && $(MAKE) clean-html)
+ (cd doc && $(MAKE) install docdir=$(abs_srcdir)/tmp-doc/install)
+ ruby $(srcdir)/tools/prepare-sphinx-html.rb tmp-doc/install tmp-doc/dist
+ rm -rf $(MROONGA_GITHUB_COM_PATH)/docs
+ mv tmp-doc/dist/en $(MROONGA_GITHUB_COM_PATH)/docs
+ for locale in `cd tmp-doc/dist; echo *`; do \
+ dest_base_dir=$(MROONGA_GITHUB_COM_PATH)/$${locale}; \
+ mkdir -p $${dest_base_dir}; \
+ dest_dir=$${dest_base_dir}/docs; \
+ rm -rf $${dest_dir}; \
+ mv tmp-doc/dist/$${locale} $${dest_dir}; \
+ done
+
+update-files:
+ cd $(srcdir)/doc && $(MAKE) update-files
+
+update-version:
+ @if test -z "$(NEW_VERSION_MAJOR)"; then \
+ echo "\$$(NEW_VERSION_MAJOR) is missing"; \
+ exit 1; \
+ fi
+ @if test -z "$(NEW_VERSION_MINOR)"; then \
+ echo "\$$(NEW_VERSION_MINOR) is missing"; \
+ exit 1; \
+ fi
+ @if test -z "$(NEW_VERSION_MICRO)"; then \
+ echo "\$$(NEW_VERSION_MICRO) is missing"; \
+ exit 1; \
+ fi
+ @echo -n $(NEW_VERSION_MAJOR) > $(srcdir)/version_major
+ @echo -n $(NEW_VERSION_MINOR) > $(srcdir)/version_minor
+ @echo -n $(NEW_VERSION_MICRO) > $(srcdir)/version_micro
+ @echo -n $(NEW_VERSION_MAJOR).$(NEW_VERSION_MINOR)$(NEW_VERSION_MICRO) \
+ > $(srcdir)/version
+ @if test $(NEW_VERSION_MINOR) -eq 0 ; then \
+ printf "0x%02x%02x" \
+ $(NEW_VERSION_MAJOR) $(NEW_VERSION_MICRO) \
+ > $(srcdir)/version_in_hex; \
+ printf "%d.%d" \
+ $(NEW_VERSION_MAJOR) $(NEW_VERSION_MICRO) \
+ > $(srcdir)/plugin_version; \
+ else \
+ printf "0x%02x%02x" \
+ $(NEW_VERSION_MAJOR) $(NEW_VERSION_MINOR)$(NEW_VERSION_MICRO) \
+ > $(srcdir)/version_in_hex; \
+ printf "%d.%d" \
+ $(NEW_VERSION_MAJOR) $(NEW_VERSION_MINOR)$(NEW_VERSION_MICRO) \
+ > $(srcdir)/plugin_version; \
+ fi
+
+upload-to-github:
+ ruby $(srcdir)/tools/upload-to-github.rb \
+ $$USER $(PACKAGE)-$(VERSION).tar.gz
+
+echo-cutter:
+ echo $(CUTTER)
+
+misc:
+ @if test -z "$(CUTTER_SOURCE_PATH)"; then \
+ echo "\$$(CUTTER_SOURCE_PATH) is missing"; \
+ exit 1; \
+ fi
+ ln -s "$(CUTTER_SOURCE_PATH)/misc" misc
+
diff --git a/storage/mroonga/NEWS b/storage/mroonga/NEWS
new file mode 100644
index 00000000000..9dfa8b8dcc5
--- /dev/null
+++ b/storage/mroonga/NEWS
@@ -0,0 +1 @@
+See doc/source/news.txt or http://mroonga.github.com/docs/news.html.
diff --git a/storage/mroonga/README b/storage/mroonga/README
new file mode 100644
index 00000000000..7c431018d86
--- /dev/null
+++ b/storage/mroonga/README
@@ -0,0 +1 @@
+See doc/locale/en/html/index.html or doc/locale/ja/html/index.html
diff --git a/storage/mroonga/autogen.sh b/storage/mroonga/autogen.sh
new file mode 100755
index 00000000000..7a1d38635d4
--- /dev/null
+++ b/storage/mroonga/autogen.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+
+warn() {
+ echo " WARNING: $@" 1>&2
+}
+
+# init
+
+LIBTOOLIZE=libtoolize
+ACLOCAL=aclocal
+AUTOCONF=autoconf
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+
+case `uname -s` in
+Darwin)
+ LIBTOOLIZE=glibtoolize
+ ;;
+FreeBSD)
+ ACLOCAL_ARGS="$ACLOCAL_ARGS -I /usr/local/share/aclocal/"
+ ;;
+esac
+
+
+# libtoolize
+echo "Searching libtoolize..."
+if [ `which $LIBTOOLIZE` ] ; then
+ echo " FOUND: libtoolize -> $LIBTOOLIZE"
+else
+ warn "Cannot Found libtoolize... input libtool command"
+ read LIBTOOLIZE
+ LIBTOOLIZE=`which $LIBTOOLIZE`
+ if [ `which $LIBTOOLIZE` ] ; then
+ echo " SET: libtoolize -> $LIBTOOLIZE"
+ else
+ warn "$LIBTOOLIZE: Command not found."
+ exit 1;
+ fi
+fi
+
+# aclocal
+echo "Searching aclocal..."
+if [ `which $ACLOCAL` ] ; then
+ echo " FOUND: aclocal -> $ACLOCAL"
+else
+ warn "Cannot Found aclocal... input aclocal command"
+ read ACLOCAL
+ ACLOCAL=`which $ACLOCAL`
+ if [ `which $ACLOCAL` ] ; then
+ echo " SET: aclocal -> $ACLOCAL"
+ else
+ warn "$ACLOCAL: Command not found."
+ exit 1;
+ fi
+fi
+
+# automake
+echo "Searching automake..."
+if [ `which $AUTOMAKE` ] ; then
+ echo " FOUND: automake -> $AUTOMAKE"
+else
+ warn "Cannot Found automake... input automake command"
+ read AUTOMAKE
+ ACLOCAL=`which $AUTOMAKE`
+ if [ `which $AUTOMAKE` ] ; then
+ echo " SET: automake -> $AUTOMAKE"
+ else
+ warn "$AUTOMAKE: Command not found."
+ exit 1;
+ fi
+fi
+
+# autoheader
+echo "Searching autoheader..."
+if [ `which $AUTOHEADER` ] ; then
+ echo " FOUND: autoheader -> $AUTOHEADER"
+else
+ warn "Cannot Found autoheader... input autoheader command"
+ read AUTOHEADER
+ ACLOCAL=`which $AUTOHEADER`
+ if [ `which $AUTOHEADER` ] ; then
+ echo " SET: autoheader -> $AUTOHEADER"
+ else
+ warn "$AUTOHEADER: Command not found."
+ exit 1;
+ fi
+fi
+
+# autoconf
+echo "Searching autoconf..."
+if [ `which $AUTOCONF` ] ; then
+ echo " FOUND: autoconf -> $AUTOCONF"
+else
+ warn "Cannot Found autoconf... input autoconf command"
+ read AUTOCONF
+ ACLOCAL=`which $AUTOCONF`
+ if [ `which $AUTOCONF` ] ; then
+ echo " SET: autoconf -> $AUTOCONF"
+ else
+ warn "$AUTOCONF: Command not found."
+ exit 1;
+ fi
+fi
+
+set -e
+
+echo "Running libtoolize ..."
+$LIBTOOLIZE --force --copy
+echo "Running aclocal ..."
+$ACLOCAL ${ACLOCAL_ARGS}
+echo "Running autoheader..."
+$AUTOHEADER
+echo "Running automake ..."
+$AUTOMAKE --add-missing --copy
+echo "Running autoconf ..."
+$AUTOCONF
diff --git a/storage/mroonga/build/Makefile.am b/storage/mroonga/build/Makefile.am
new file mode 100644
index 00000000000..506a11dc3b0
--- /dev/null
+++ b/storage/mroonga/build/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = \
+ cmake_modules
diff --git a/storage/mroonga/build/cmake_modules/Makefile.am b/storage/mroonga/build/cmake_modules/Makefile.am
new file mode 100644
index 00000000000..83fb0f0c1b4
--- /dev/null
+++ b/storage/mroonga/build/cmake_modules/Makefile.am
@@ -0,0 +1,2 @@
+EXTRA_DIST = \
+ ReadFileList.cmake
diff --git a/storage/mroonga/build/cmake_modules/ReadFileList.cmake b/storage/mroonga/build/cmake_modules/ReadFileList.cmake
new file mode 100644
index 00000000000..018587991d8
--- /dev/null
+++ b/storage/mroonga/build/cmake_modules/ReadFileList.cmake
@@ -0,0 +1,27 @@
+# Copyright(C) 2012 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+macro(read_file_list file_name output_variable)
+ file(READ ${file_name} ${output_variable})
+ # Remove variable declaration at the first line:
+ # "libgroonga_la_SOURCES = \" -> ""
+ string(REGEX REPLACE "^.*=[ \t]*\\\\" ""
+ ${output_variable} "${${output_variable}}")
+ # Remove white spaces: " com.c \\\n com.h \\\n" -> "com.c\\com.h"
+ string(REGEX REPLACE "[ \t\n]" "" ${output_variable} "${${output_variable}}")
+ # Convert string to list: "com.c\\com.h" -> "com.c;com.h"
+ # NOTE: List in CMake is ";" separated string.
+ string(REGEX REPLACE "\\\\" ";" ${output_variable} "${${output_variable}}")
+endmacro()
diff --git a/storage/mroonga/build/makefiles/LC_MESSAGES.am b/storage/mroonga/build/makefiles/LC_MESSAGES.am
new file mode 100644
index 00000000000..acfc3da238e
--- /dev/null
+++ b/storage/mroonga/build/makefiles/LC_MESSAGES.am
@@ -0,0 +1,5 @@
+BUILT_SOURCES =
+EXTRA_DIST =
+SUFFIXES =
+
+include $(top_srcdir)/build/makefiles/gettext.am
diff --git a/storage/mroonga/build/makefiles/gettext.am b/storage/mroonga/build/makefiles/gettext.am
new file mode 100644
index 00000000000..9706b485dd1
--- /dev/null
+++ b/storage/mroonga/build/makefiles/gettext.am
@@ -0,0 +1,73 @@
+include $(top_srcdir)/doc/files.am
+include $(top_srcdir)/build/makefiles/sphinx-build.am
+
+EXTRA_DIST += \
+ $(po_files)
+
+if DOCUMENT_AVAILABLE
+EXTRA_DIST += \
+ $(mo_files)
+endif
+
+if DOCUMENT_BUILDABLE
+BUILT_SOURCES += \
+ pot-build-stamp \
+ edit-po-build-stamp \
+ $(mo_files)
+endif
+
+SUFFIXES += .pot .po .mo .edit
+
+.PHONY: gettext update build
+
+.pot.edit:
+ if test -f $*.po; then \
+ msgmerge \
+ --quiet \
+ --sort-by-file \
+ --output-file=$@.tmp \
+ $*.po \
+ $<; \
+ else \
+ msginit \
+ --input=$< \
+ --output-file=$@.tmp \
+ --locale=$(LOCALE) \
+ --no-translator; \
+ fi
+ (echo "# -*- po -*-"; \
+ GREP_OPTIONS= grep -v '^# -\*- po -\*-' $@.tmp | \
+ GREP_OPTIONS= grep -v '^"POT-Creation-Date:') > $@
+ rm $@.tmp
+
+.edit.po:
+ msgcat --no-location --output $@ $<
+
+.po.mo:
+ msgfmt -o $@ $<
+
+if DOCUMENT_BUILDABLE
+update: pot-build-stamp edit-po-build-stamp
+build: update $(mo_files)
+else
+update:
+build:
+endif
+
+html: build
+man: build
+pdf: build
+
+gettext:
+ rm *.pot || true
+ $(SPHINX_BUILD_COMMAND) -d doctrees -b gettext $(ALLSPHINXOPTS) .
+ xgettext --language Python --output conf.pot \
+ $(top_srcdir)/doc/source/conf.py
+
+pot-build-stamp: $(absolute_source_files)
+ $(MAKE) gettext
+ @touch $@
+
+edit-po-build-stamp: $(absolute_source_files)
+ $(MAKE) $(edit_po_files)
+ @touch $@
diff --git a/storage/mroonga/build/makefiles/locale.am b/storage/mroonga/build/makefiles/locale.am
new file mode 100644
index 00000000000..414c19a7e34
--- /dev/null
+++ b/storage/mroonga/build/makefiles/locale.am
@@ -0,0 +1,12 @@
+SUBDIRS = LC_MESSAGES
+
+BUILT_SOURCES =
+EXTRA_DIST =
+
+include $(top_srcdir)/build/makefiles/sphinx.am
+
+init:
+ cd LC_MESSAGES && $(MAKE) $@
+
+update-po:
+ cd LC_MESSAGES && $(MAKE) update
diff --git a/storage/mroonga/build/makefiles/sphinx-build.am b/storage/mroonga/build/makefiles/sphinx-build.am
new file mode 100644
index 00000000000..e237377ba80
--- /dev/null
+++ b/storage/mroonga/build/makefiles/sphinx-build.am
@@ -0,0 +1,19 @@
+# You can set these variables from the command line.
+DOCTREES_BASE = doctrees
+
+SPHINXOPTS =
+PAPER =
+
+# Internal variables.
+SOURCE_DIR = $(abs_top_srcdir)/doc/source
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = $(PAPEROPT_$(PAPER)) -E $(SPHINXOPTS) $(SOURCE_DIR)
+
+SPHINX_DIR = $(abs_top_builddir)/doc/sphinx
+SPHINX_BUILD_COMMAND = \
+ DOCUMENT_VERSION="$(DOCUMENT_VERSION)" \
+ DOCUMENT_VERSION_FULL="$(DOCUMENT_VERSION_FULL)" \
+ LOCALE="$(LOCALE)" \
+ PYTHONPATH="$(SPHINX_DIR):$$PYTHONPATH" \
+ $(SPHINX_BUILD)
diff --git a/storage/mroonga/build/makefiles/sphinx.am b/storage/mroonga/build/makefiles/sphinx.am
new file mode 100644
index 00000000000..f84fb23b739
--- /dev/null
+++ b/storage/mroonga/build/makefiles/sphinx.am
@@ -0,0 +1,179 @@
+include $(top_srcdir)/doc/files.am
+include $(top_srcdir)/build/makefiles/sphinx-build.am
+
+$(html_files): html-build-stamp
+$(html_files_relative_from_locale_dir): html-build-stamp
+$(man_files): man-build-stamp
+
+am__nobase_dist_doc_locale_DATA_DIST =
+if DOCUMENT_AVAILABLE
+doc_localedir = $(docdir)/$(LOCALE)
+nobase_dist_doc_locale_DATA = \
+ $(html_files_relative_from_locale_dir)
+am__nobase_dist_doc_locale_DATA_DIST += \
+ $(nobase_dist_doc_locale_DATA)
+endif
+
+document_source_files = \
+ $(absolute_source_files) \
+ $(absolute_theme_files) \
+ $(po_files_relative_from_locale_dir) \
+ $(mo_files_relative_from_locale_dir)
+
+required_build_stamps = \
+ html-build-stamp \
+ man-build-stamp \
+ mo-build-stamp
+
+if DOCUMENT_BUILDABLE
+EXTRA_DIST += $(required_build_stamps)
+endif
+
+man_files = \
+ man/$(PACKAGE_NAME).1
+
+generated_files = \
+ $(DOCTREES_BASE) \
+ man \
+ man-build-stamp \
+ html \
+ html-build-stamp \
+ pdf \
+ pdf-build-stamp \
+ dirhtml \
+ dirhtml-build-stamp \
+ pickle \
+ pikcle-build-stamp \
+ json \
+ json-build-stamp \
+ htmlhelp \
+ htmlhelp-build-stamp \
+ qthelp \
+ qthelp-build-stamp \
+ latex \
+ latex-build-stamp \
+ changes \
+ changes-build-stamp \
+ linkcheck \
+ linkcheck-build-stamp \
+ doctest
+
+$(mo_files_relative_from_locale_dir): mo-build-stamp
+
+mo-build-stamp: $(po_files_relative_from_locale_dir)
+ cd LC_MESSAGES && $(MAKE) build
+ @touch $@
+
+if DOCUMENT_BUILDABLE
+clean-local: $(clean_targets) clean-doctrees
+
+clean-doctrees:
+ rm -rf $(DOCTREES_BASE)
+
+maintainer-clean-local:
+ rm -rf -- $(generated_files)
+endif
+
+.PHONY: help
+.PHONY: man clean-man
+.PHONY: html clean-html
+.PHONY: pdf
+.PHONY: dirhtml
+.PHONY: pickle
+.PHONY: json
+.PHONY: htmlhelp
+.PHONY: qthelp
+.PHONY: latex
+.PHONY: changes
+.PHONY: linkcheck
+.PHONY: doctest
+
+if DOCUMENT_BUILDABLE
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " man to make man files"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " rdoc to make RDoc files"
+ @echo " textile to make Textile files"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+man: man-build-stamp
+html: html-build-stamp
+dirhtml: dirhtml-build-stamp
+pickle: pickle-build-stamp
+json: json-build-stamp
+htmlhelp: htmlhelp-build-stamp
+qthelp: qthelp-build-stamp
+latex: latex-build-stamp
+rdoc: rdoc-build-stamp
+textile: textile-build-stamp
+changes: changes-build-stamp
+linkcheck: linkcheck-build-stamp
+doctest: doctest-build-stamp
+
+clean_targets = \
+ clean-man \
+ clean-html \
+ clean-dirhtml \
+ clean-pickle \
+ clean-json \
+ clean-htmlhelp \
+ clean-qthelp \
+ clean-latex \
+ clean-rdoc \
+ clean-textile \
+ clean-changes \
+ clean-linkcheck \
+ clean-doctest
+
+$(clean_targets):
+ target=`echo $@ | sed -e 's/^clean-//'`; \
+ rm -rf $${target}-build-stamp $${target}
+
+build_stamps = \
+ man-build-stamp \
+ html-build-stamp \
+ dirhtml-build-stamp \
+ pickle-build-stamp \
+ json-build-stamp \
+ htmlhelp-build-stamp \
+ qthelp-build-stamp \
+ latex-build-stamp \
+ rdoc-build-stamp \
+ textile-build-stamp \
+ changes-build-stamp \
+ linkcheck-build-stamp \
+ doctest-build-stamp
+
+$(build_stamps): $(document_source_files)
+ target=`echo $@ | sed -e 's/-build-stamp$$//'`; \
+ $(SPHINX_BUILD_COMMAND) \
+ -Dlanguage=$(LOCALE) \
+ -d $(DOCTREES_BASE)/$${target} \
+ -b $${target} \
+ $(ALLSPHINXOPTS) \
+ $${target}
+ @touch $@
+
+qthelp: qthelp-message
+qthelp-message: qthelp-build-stamp
+ @echo "Build finished; now you can run 'qcollectiongenerator' with the" \
+ ".qhcp project file in qthelp/*, like this:"
+ @echo "# qcollectiongenerator qthelp/groonga.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile qthelp/groonga.qhc"
+
+latex: latex-message
+latex-message: latex-build-stamp
+ @echo "Build finished; the LaTeX files are in latex/*."
+ @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+ "run these through (pdf)latex."
+endif
diff --git a/storage/mroonga/config.sh.in b/storage/mroonga/config.sh.in
new file mode 100644
index 00000000000..2b584c6916d
--- /dev/null
+++ b/storage/mroonga/config.sh.in
@@ -0,0 +1,20 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+MYSQL_SOURCE_DIR="@MYSQL_SOURCE_DIR@"
+MYSQL_BUILD_DIR="@MYSQL_BUILD_DIR@"
+MYSQL_VERSION="@MYSQL_VERSION@"
+MRN_BUNDLED="@MRN_BUNDLED@"
diff --git a/storage/mroonga/configure.ac b/storage/mroonga/configure.ac
new file mode 100644
index 00000000000..dc39935d8ee
--- /dev/null
+++ b/storage/mroonga/configure.ac
@@ -0,0 +1,492 @@
+AC_PREREQ(2.59)
+
+m4_define([mrn_version_major], m4_include(version_major))
+m4_define([mrn_version_minor], m4_include(version_minor))
+m4_define([mrn_version_micro], m4_include(version_micro))
+m4_define([mrn_version], m4_include(version))
+m4_define([mrn_version_in_hex], m4_include(version_in_hex))
+m4_define([mrn_plugin_version], m4_include(plugin_version))
+
+AC_INIT([mroonga], [mrn_version], [groonga-talk@lists.sourceforge.net])
+AC_CONFIG_HEADERS([config.h])
+
+AM_INIT_AUTOMAKE([tar-pax foreign subdir-objects])
+
+MRN_VERSION=mrn_version
+MRN_VERSION_MAJOR=mrn_version_major
+MRN_VERSION_MINOR=mrn_version_minor
+MRN_VERSION_MICRO=mrn_version_micro
+MRN_VERSION_IN_HEX=mrn_version_in_hex
+MRN_PLUGIN_VERSION=mrn_plugin_version
+AC_SUBST([MRN_VERSION])
+AC_SUBST([MRN_VERSION_MAJOR])
+AC_SUBST([MRN_VERSION_MINOR])
+AC_SUBST([MRN_VERSION_MICRO])
+AC_SUBST([MRN_VERSION_IN_HEX])
+AC_SUBST([MRN_PLUGIN_VERSION])
+
+MRN_PACKAGE_STRING="$PACKAGE_STRING"
+AC_SUBST([MRN_PACKAGE_STRING])
+
+MRN_BUNDLED=FALSE
+AC_SUBST([MRN_BUNDLED])
+
+AC_C_BIGENDIAN
+AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_CPP
+AC_PROG_LIBTOOL
+m4_ifdef([LT_OUTPUT], [LT_OUTPUT])
+
+AC_DEFUN([CHECK_CFLAG], [
+ AC_MSG_CHECKING([if gcc supports $1])
+ old_CFLAGS=$CFLAGS
+ flag=`echo '$1' | sed -e 's,^-Wno-,-W,'`
+ CFLAGS="$CFLAGS $flag -Werror"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])],
+ [check_cflag=yes],
+ [check_cflag=no])
+ CFLAGS="$old_CFLAGS"
+ if test "x$check_cflag" = "xyes"; then
+ CFLAGS="$CFLAGS $1"
+ fi
+ AC_MSG_RESULT([$check_cflag])
+])
+
+AC_DEFUN([CHECK_CXXFLAG], [
+ AC_MSG_CHECKING([if g++ supports $1])
+ old_CXXFLAGS=$CXXFLAGS
+ flag=`echo '$1' | sed -e 's,^-Wno-,-W,'`
+ CXXFLAGS="$CXXFLAGS $flag -Werror"
+ AC_LANG_PUSH([C++])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])],
+ [check_cxxflag=yes],
+ [check_cxxflag=no])
+ AC_LANG_POP([C++])
+ CXXFLAGS="$old_CXXFLAGS"
+ if test "x$check_cxxflag" = "xyes"; then
+ CXXFLAGS="$CXXFLAGS $1"
+ fi
+ AC_MSG_RESULT([$check_cxxflag])
+])
+
+AC_DEFUN([CHECK_BUILD_FLAG], [
+ CHECK_CFLAG([$1])
+ CHECK_CXXFLAG([$1])
+])
+
+if test "$GCC" = "yes"; then
+ CHECK_BUILD_FLAG([-Wall])
+ CHECK_BUILD_FLAG([-Wextra])
+ CHECK_BUILD_FLAG([-Wno-unused-parameter])
+ CHECK_BUILD_FLAG([-Wno-strict-aliasing])
+ # REMOVEME: workaround for MySQL/MariaDB 5.5.22 :<
+ # They use deprecated MYSQL::generate_name style in class definition.
+ CHECK_BUILD_FLAG([-Wno-deprecated])
+fi
+
+AC_MSG_CHECKING(for the suffix of plugin shared libraries)
+shrext_cmds=$(./libtool --config | grep '^shrext_cmds=')
+eval $shrext_cmds
+module=yes eval MRN_PLUGIN_SUFFIX="$shrext_cmds"
+AC_MSG_RESULT($MRN_PLUGIN_SUFFIX)
+if test -z "$MRN_PLUGIN_SUFFIX"; then
+ AC_MSG_ERROR([can't detect plugin suffix])
+fi
+AC_SUBST(MRN_PLUGIN_SUFFIX)
+
+AC_ARG_WITH(libmysqlservices-compat,
+ [AS_HELP_STRING([--with-libmysqlservices-compat],
+ [Use libmysqlservices compatible library for missing libmysqlservices.a])
+ ],
+ [with_libmysqlservices_compat=$withval],
+ [with_libmysqlservices_compat=no])
+AM_CONDITIONAL([WITH_LIBMYSQLSERVICES_COMPAT], [test "${with_libmysqlservices_compat}" != "no"])
+
+AC_DEFUN([CONFIG_OPTION_MYSQL],[
+ AC_MSG_CHECKING([mysql source])
+
+ ac_mysql_source_dir=
+ AC_ARG_WITH([mysql-source],
+ [AS_HELP_STRING([--with-mysql-source=PATH], [MySQL source directory PATH])],
+ [
+ ac_mysql_source_dir="$withval"
+ if test -f "$ac_mysql_source_dir/sql/handler.h"; then
+ case "$ac_mysql_source_dir" in
+ /*)
+ :
+ ;;
+ *)
+ ac_mysql_source_dir="$ac_pwd/$ac_mysql_source_dir"
+ ;;
+ esac
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_ERROR([invalid MySQL source directory])
+ fi
+ ],
+ [AC_MSG_ERROR([--with-mysql-source=PATH is required])]
+ )
+ MYSQL_SOURCE_DIR="$ac_mysql_source_dir"
+ AC_SUBST(MYSQL_SOURCE_DIR)
+
+ ac_mysql_build_dir=
+ AC_ARG_WITH([mysql-build],
+ [AS_HELP_STRING([--with-mysql-build=PATH], [MySQL build directory PATH])],
+ [ac_mysql_build_dir="$withval"],
+ [ac_mysql_build_dir="$ac_mysql_source_dir"]
+ )
+ case "$ac_mysql_build_dir" in
+ /*)
+ :
+ ;;
+ *)
+ ac_mysql_build_dir="$ac_pwd/$ac_mysql_build_dir"
+ ;;
+ esac
+ MYSQL_BUILD_DIR="$ac_mysql_build_dir"
+ AC_SUBST(MYSQL_BUILD_DIR)
+
+ AC_MSG_CHECKING([mysql_config])
+ AC_ARG_WITH([mysql-config],
+ [AS_HELP_STRING([--with-mysql-config=PATH],
+ [mysql_config PATH])],
+ [ac_mysql_config="$withval"],
+ [ac_mysql_config=])
+ if test -z "$ac_mysql_config"; then
+ AC_PATH_PROG(ac_mysql_config, mysql_config, mysql-config-not-found)
+ fi
+ if test "$ac_mysql_config" = "mysql-config-not-found"; then
+ AC_MSG_ERROR([can't detect mysql_config. Please specify mysql_config path by --with-mysql-config=PATH.])
+ fi
+ AC_MSG_RESULT([$ac_mysql_config])
+
+ plugindir="$($ac_mysql_config --plugindir)"
+ if test $? -ne 0; then
+ AC_MSG_ERROR([failed to run "$ac_mysql_config": $plugindir])
+ fi
+ AC_SUBST(plugindir)
+
+ MYSQL_CFLAGS="$MYSQL_CFLAGS $($ac_mysql_config --cflags)"
+ AC_SUBST(MYSQL_CFLAGS)
+
+ MYSQL_INCLUDES=""
+ MYSQL_INCLUDES="$MYSQL_INCLUDES -I$ac_mysql_build_dir/include"
+ MYSQL_INCLUDES="$MYSQL_INCLUDES -I$ac_mysql_source_dir/sql"
+ MYSQL_INCLUDES="$MYSQL_INCLUDES -I$ac_mysql_source_dir/include"
+ if test -d "$ac_mysql_source_dir/pcre"; then
+ mysql_regex_include_dir="$ac_mysql_source_dir/pcre"
+ else
+ mysql_regex_include_dir="$ac_mysql_source_dir/regex"
+ fi
+ MYSQL_INCLUDES="$MYSQL_INCLUDES -I$mysql_regex_include_dir"
+ MYSQL_INCLUDES="$MYSQL_INCLUDES -I$ac_mysql_source_dir"
+ MYSQL_INCLUDES="$MYSQL_INCLUDES $($ac_mysql_config --include)"
+ AC_SUBST(MYSQL_INCLUDES)
+
+ MYSQL_VERSION="$($ac_mysql_config --version)"
+ AC_SUBST(MYSQL_VERSION)
+
+ if test "${with_libmysqlservices_compat}" = "no"; then
+ MYSQL_MAJOR_MINOR_VERSION=["$(echo $MYSQL_VERSION | sed -e 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*[a-z]*\)$/\1.\2/')"]
+ case "$MYSQL_MAJOR_MINOR_VERSION" in
+ 5.1)
+ MYSQL_LIBS=""
+ ;;
+ *)
+ AC_MSG_CHECKING([for libmysqlservices.a directory])
+ pkglibdir="$($ac_mysql_config --variable=pkglibdir)"
+ mysql_build_libservices_dir="${MYSQL_BUILD_DIR}/libservices"
+ if test -f "${mysql_build_libservices_dir}/libmysqlservices.a"; then
+ mysql_services_lib_dir="${mysql_build_libservices_dir}"
+ else
+ if test -f "${pkglibdir}/libmysqlservices.a"; then
+ mysql_services_lib_dir="${pkglibdir}"
+ elif test -f "${pkglibdir}/mysql/libmysqlservices.a"; then
+ mysql_services_lib_dir="${pkglibdir}/mysql"
+ else
+ AC_MSG_ERROR([libmysqlservices.a is not found in <${pkglibdir}/> and <${pkglibdir}/mysql/>])
+ fi
+ fi
+ AC_MSG_RESULT([$mysql_services_lib_dir])
+ MYSQL_LIBS="$MYSQL_LIBS -L\"$mysql_services_lib_dir\" -lmysqlservices"
+ ;;
+ esac
+ AC_SUBST(MYSQL_LIBS)
+ fi
+])
+
+m4_define([mrn_required_groonga_version], m4_include(required_groonga_version))
+REQUIRED_GROONGA_VERSION=mrn_required_groonga_version
+AC_SUBST(REQUIRED_GROONGA_VERSION)
+AC_DEFUN([CONFIG_OPTION_GROONGA],[
+ PKG_CHECK_MODULES(GROONGA, groonga >= ${REQUIRED_GROONGA_VERSION})
+ _PKG_CONFIG(GROONGA_VERSION, variable=groonga_version, groonga)
+ GROONGA_VERSION=$pkg_cv_GROONGA_VERSION
+ AC_SUBST(GROONGA_VERSION)
+])
+
+m4_define([mrn_required_groonga_normalizer_mysql_version],
+ m4_include(required_groonga_normalizer_mysql_version))
+REQUIRED_GROONGA_NORMALIZER_MYSQL_VERSION=mrn_required_groonga_normalizer_mysql_version
+AC_DEFUN([CONFIG_OPTION_GROONGA_NORMALIZER_MYSQL], [
+ AC_MSG_CHECKING([for groonga-normalizer-mysql])
+ PKG_CHECK_EXISTS([groonga-normalizer-mysql >= ${REQUIRED_GROONGA_NORMALIZER_MYSQL_VERSION}],
+ [WITH_GROONGA_NORMALIZER_MYSQL=yes],
+ [WITH_GROONGA_NORMALIZER_MYSQL=no])
+ AC_MSG_RESULT($WITH_GROONGA_NORMALIZER_MYSQL)
+ if test "$WITH_GROONGA_NORMALIZER_MYSQL" = "yes"; then
+ AC_DEFINE([WITH_GROONGA_NORMALIZER_MYSQL],
+ [1],
+ [Use MySQL normalizer plugin for groonga])
+ _PKG_CONFIG(plugin_name, variable=plugin_name, groonga-normalizer-mysql)
+ GROONGA_NORMALIZER_MYSQL_PLUGIN_NAME=$pkg_cv_plugin_name
+ AC_DEFINE_UNQUOTED([GROONGA_NORMALIZER_MYSQL_PLUGIN_NAME],
+ "${GROONGA_NORMALIZER_MYSQL_PLUGIN_NAME}",
+ [Name of MySQL normalizer plugin for groonga])
+ fi
+])
+
+AC_ARG_WITH(debug,
+ [dnl
+AS_HELP_STRING([--with-debug],
+ [Add debug code])
+AS_HELP_STRING([--with-debug=full],
+ [Add debug code (adds memory checker, very slow)])dnl
+ ],
+ [with_debug=$withval],
+ [with_debug=no])
+if test "$with_debug" = "yes"
+then
+ # Medium debug.
+ AC_DEFINE([DBUG_ON], [1], [Use libdbug])
+ CFLAGS="$DEBUG_CFLAGS $DEBUG_OPTIMIZE_CC -DSAFE_MUTEX $CFLAGS -O0 -g3"
+ CXXFLAGS="$DEBUG_CXXFLAGS $DEBUG_OPTIMIZE_CXX -DSAFE_MUTEX $CXXFLAGS -O0 -g3"
+elif test "$with_debug" = "full"
+then
+ # Full debug. Very slow in some cases
+ AC_DEFINE([DBUG_ON], [1], [Use libdbug])
+ CFLAGS="$DEBUG_CFLAGS -DSAFE_MUTEX -DSAFEMALLOC $CFLAGS -O0 -g3"
+ CXXFLAGS="$DEBUG_CXXFLAGS -DSAFE_MUTEX -DSAFEMALLOC $CXXFLAGS -O0 -g3"
+else
+ # Optimized version. No debug
+ AC_DEFINE([DBUG_OFF], [1], [Don't use libdbug])
+ CFLAGS="$OPTIMIZE_CFLAGS $CFLAGS"
+ CXXFLAGS="$OPTIMIZE_CXXFLAGS $CXXFLAGS"
+fi
+
+AC_ARG_WITH(valgrind,
+ [AS_HELP_STRING([--with-valgrind], [Use valgrind. [default=no]])],
+ [with_valgrind="$withval"],
+ [with_valgrind="no"])
+if test "$with_valgrind" != "no"; then
+ CFLAGS="-DHAVE_valgrind $CFLAGS"
+ CXXFLAGS="-DHAVE_valgrind $CXXFLAGS"
+fi
+
+CONFIG_OPTION_MYSQL
+CONFIG_OPTION_GROONGA
+CONFIG_OPTION_GROONGA_NORMALIZER_MYSQL
+
+AC_ARG_WITH(default_parser,
+ [AS_HELP_STRING([--with-default-parser=PARSER],
+ [specify the default fulltext parser like
+ --with-default-parser=TokenMecab.
+ (default: TokenBigram)])],
+ [default_parser=$withval],
+ [default_parser=no])
+if test x"$default_parser" != x"no"; then
+ AC_DEFINE_UNQUOTED(MRN_PARSER_DEFAULT,
+ "$default_parser",
+ "specified default fulltext parser")
+ MRN_DEFAULT_PARSER=$default_parser
+else
+ MRN_DEFAULT_PARSER=TokenBigram
+fi
+AC_SUBST(MRN_DEFAULT_PARSER)
+
+AC_ARG_ENABLE(fast_mutexes,
+ [AS_HELP_STRING([--disable-fast-mutexes],
+ [Force disable fast mutex.
+ [default: use mysql_config output]])],
+ [enable_fast_mutexes=$enableval],
+ [enable_fast_mutexes=auto])
+if test "$enable_fast_mutexes" = "no"; then
+ AC_DEFINE(FORCE_FAST_MUTEX_DISABLED, [1],
+ [Define to 1 if force fast mutexes disabled])
+elif test "$enable_fast_mutexes" = "yes"; then
+ AC_DEFINE(MY_PTHREAD_FASTMUTEX, [1],
+ [Define to 1 if fast mutexes enabled])
+fi
+
+AC_ARG_ENABLE(dtrace,
+ [AS_HELP_STRING([--enable-dtrace],
+ [Enable DTrace. [default: no]])],
+ [enable_dtrace=$enableval],
+ [enable_dtrace=no])
+if test "$enable_dtrace" = "no"; then
+ AC_DEFINE(DISABLE_DTRACE, [1], [Define to 1 if DTrace is disabled])
+fi
+
+# check Cutter with C++ support if available
+REQUIRED_MINIMUM_CUTTER_VERSION=1.1.3
+m4_ifdef([AC_CHECK_CPPCUTTER], [
+AC_CHECK_CPPCUTTER(>= $REQUIRED_MINIMUM_CUTTER_VERSION)
+],
+[ac_cv_use_cutter="no"])
+AM_CONDITIONAL([WITH_CUTTER], [test "$ac_cv_use_cutter" != "no"])
+
+# For mroonga.github.com
+AC_ARG_WITH(mroonga-github-com-path,
+ [AS_HELP_STRING([--with-mroonga-github-com-path=PATH],
+ [specify mroonga.github.com path to update mroonga.github.com.])],
+ [MROONGA_GITHUB_COM_PATH="$withval"],
+ [MROONGA_GITHUB_COM_PATH=""])
+AC_SUBST(MROONGA_GITHUB_COM_PATH)
+
+# For package
+AC_ARG_WITH(rsync-path,
+ [AS_HELP_STRING([--with-rsync-path=PATH],
+ [specify rsync path to upload mroonga packages.])],
+ [RSYNC_PATH="$withval"],
+ [RSYNC_PATH="packages@packages.groonga.org:public"])
+AC_SUBST(RSYNC_PATH)
+
+AC_ARG_WITH(launchpad-uploader-pgp-key,
+ [AS_HELP_STRING([--with-launchpad-uploader-pgp-key=KEY],
+ [specify PGP key UID to upload Groonga packages to Launchpad.])],
+ [LAUNCHPAD_UPLOADER_PGP_KEY="$withval"],
+ [LAUNCHPAD_UPLOADER_PGP_KEY=""])
+AC_SUBST(LAUNCHPAD_UPLOADER_PGP_KEY)
+
+GPG_UID=m4_include(gpg_uid)
+AC_SUBST(GPG_UID)
+
+# For update-version
+AC_ARG_WITH(cutter-source-path,
+ [AS_HELP_STRING([--with-cutter-source-path=PATH],
+ [specify Cutter source path for mroonga's release manager.])],
+ [CUTTER_SOURCE_PATH="$withval"])
+case "$CUTTER_SOURCE_PATH" in
+ ""|/*)
+ : # do nothing
+ ;;
+ *)
+ CUTTER_SOURCE_PATH="\$(top_builddir)/${CUTTER_SOURCE_PATH}"
+ ;;
+esac
+AC_SUBST(CUTTER_SOURCE_PATH)
+
+
+# Document
+AC_MSG_CHECKING([whether enable document])
+AC_ARG_ENABLE(document,
+ [AS_HELP_STRING([--enable-document],
+ [enable document generation by Sphinx. [default=auto]])],
+ [enable_document="$enableval"],
+ [enable_document="auto"])
+AC_MSG_RESULT($enable_document)
+
+document_available=no
+document_buildable=no
+have_built_document=no
+if test x"$enable_document" != x"no"; then
+ if test -f "$srcdir/doc/build-stamp"; then
+ document_available=yes
+ have_built_document=yes
+ fi
+
+ if test x"$enable_document" = x"yes"; then
+ AC_PATH_PROG(SPHINX_BUILD, sphinx-build, [])
+ if test -n "$SPHINX_BUILD"; then
+ sphinx_build_version=`"$SPHINX_BUILD" --version`
+ if ! echo "$sphinx_build_version" | grep -q ' 1\.[[23]]'; then
+ AC_MSG_ERROR([
+sphinx-build is old: $sphinx_build_version
+Sphinx 1.2 or later is required.])
+ fi
+ document_available=yes
+ document_buildable=yes
+ else
+ AC_MSG_ERROR([
+No sphinx-build found.
+Install it and try again.
+
+How to install sphinx-build:
+
+For Debian GNU/Linux based system like Ubuntu:
+ % sudo apt-get install -y python-pip
+ % sudo pip install sphinx
+
+For Red Hat based system like CentOS:
+ % sudo yum install -y python-pip
+ % sudo pip install sphinx])
+ fi
+ AC_SUBST(SPHINX_BUILD)
+ fi
+fi
+
+AM_CONDITIONAL([DOCUMENT_AVAILABLE],
+ [test "${document_available}" = "yes"])
+AC_MSG_CHECKING([whether document available])
+AC_MSG_RESULT($document_available)
+
+AM_CONDITIONAL([DOCUMENT_BUILDABLE],
+ [test "${document_buildable}" = "yes"])
+AC_MSG_CHECKING([whether document buildable])
+AC_MSG_RESULT($document_buildable)
+
+AM_CONDITIONAL([HAVE_BUILT_DOCUMENT],
+ [test "${have_built_document}" = "yes"])
+AC_MSG_CHECKING([whether having built document])
+AC_MSG_RESULT($have_built_document)
+
+DOCUMENT_VERSION=mrn_version
+DOCUMENT_VERSION_FULL="$DOCUMENT_VERSION"
+AC_SUBST(DOCUMENT_VERSION)
+AC_SUBST(DOCUMENT_VERSION_FULL)
+
+CFLAGS="$CFLAGS"
+CXXFLAGS="$CXXFLAGS -fno-implicit-templates -fno-exceptions -fno-rtti -felide-constructors"
+
+AC_CONFIG_FILES([
+ Makefile
+ build/Makefile
+ build/cmake_modules/Makefile
+ lib/Makefile
+ udf/Makefile
+ test/Makefile
+ test/unit/Makefile
+ mysql-test/Makefile
+ packages/Makefile
+ packages/rpm/Makefile
+ packages/rpm/centos/Makefile
+ packages/rpm/fedora/Makefile
+ packages/yum/Makefile
+ packages/apt/Makefile
+ packages/source/Makefile
+ packages/ubuntu/Makefile
+ packages/windows/Makefile
+ tools/Makefile
+ doc/Makefile
+ doc/locale/Makefile
+ doc/locale/en/Makefile
+ doc/locale/en/LC_MESSAGES/Makefile
+ doc/locale/ja/Makefile
+ doc/locale/ja/LC_MESSAGES/Makefile
+ data/Makefile
+])
+AC_OUTPUT([
+ config.sh
+ mrn_version.h
+ mysql-test/mroonga/storage/information_schema/r/plugins.result
+ mysql-test/mroonga/storage/variable/r/version.result
+ packages/debian/control
+ packages/rpm/centos/mysql55-mroonga.spec
+ packages/rpm/centos/mysql56-community-mroonga.spec
+ packages/rpm/centos/mariadb-mroonga.spec
+ packages/rpm/fedora/mysql-mroonga.spec
+ packages/rpm/fedora/mariadb-mroonga.spec
+ packages/yum/env.sh
+ data/install.sql
+])
diff --git a/storage/mroonga/data/Makefile.am b/storage/mroonga/data/Makefile.am
new file mode 100644
index 00000000000..c088c78c30f
--- /dev/null
+++ b/storage/mroonga/data/Makefile.am
@@ -0,0 +1,4 @@
+sqldir = $(pkgdatadir)
+dist_sql_DATA = \
+ install.sql \
+ uninstall.sql
diff --git a/storage/mroonga/data/install.sql.in b/storage/mroonga/data/install.sql.in
new file mode 100644
index 00000000000..b0c930c8144
--- /dev/null
+++ b/storage/mroonga/data/install.sql.in
@@ -0,0 +1,19 @@
+DELETE IGNORE FROM mysql.plugin WHERE dl = 'ha_mroonga@MRN_PLUGIN_SUFFIX@';
+
+INSTALL PLUGIN Mroonga SONAME 'ha_mroonga@MRN_PLUGIN_SUFFIX@';
+
+DROP FUNCTION IF EXISTS last_insert_grn_id;
+CREATE FUNCTION last_insert_grn_id RETURNS INTEGER
+ SONAME 'ha_mroonga@MRN_PLUGIN_SUFFIX@';
+
+DROP FUNCTION IF EXISTS mroonga_snippet;
+CREATE FUNCTION mroonga_snippet RETURNS STRING
+ SONAME 'ha_mroonga@MRN_PLUGIN_SUFFIX@';
+
+DROP FUNCTION IF EXISTS mroonga_command;
+CREATE FUNCTION mroonga_command RETURNS STRING
+ SONAME 'ha_mroonga@MRN_PLUGIN_SUFFIX@';
+
+DROP FUNCTION IF EXISTS mroonga_escape;
+CREATE FUNCTION mroonga_escape RETURNS STRING
+ SONAME 'ha_mroonga@MRN_PLUGIN_SUFFIX@';
diff --git a/storage/mroonga/data/uninstall.sql b/storage/mroonga/data/uninstall.sql
new file mode 100644
index 00000000000..b79e6c03d18
--- /dev/null
+++ b/storage/mroonga/data/uninstall.sql
@@ -0,0 +1,8 @@
+DROP FUNCTION IF EXISTS last_insert_grn_id;
+DROP FUNCTION IF EXISTS mroonga_snippet;
+DROP FUNCTION IF EXISTS mroonga_command;
+DROP FUNCTION IF EXISTS mroonga_escape;
+
+UNINSTALL PLUGIN Mroonga;
+
+FLUSH TABLES;
diff --git a/storage/mroonga/gpg_uid b/storage/mroonga/gpg_uid
new file mode 100644
index 00000000000..7c1a800ba92
--- /dev/null
+++ b/storage/mroonga/gpg_uid
@@ -0,0 +1 @@
+45499429
diff --git a/storage/mroonga/ha_mroonga.cpp b/storage/mroonga/ha_mroonga.cpp
new file mode 100644
index 00000000000..a4a2f9417c4
--- /dev/null
+++ b/storage/mroonga/ha_mroonga.cpp
@@ -0,0 +1,15536 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ Copyright(C) 2010 Tetsuro IKEDA
+ Copyright(C) 2010-2013 Kentoku SHIBA
+ Copyright(C) 2011-2014 Kouhei Sutou <kou@clear-code.com>
+ Copyright(C) 2013 Kenji Maruyama <mmmaru777@gmail.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "mrn_mysql.h"
+
+#ifdef USE_PRAGMA_IMPLEMENTATION
+#pragma implementation
+#endif
+
+#if MYSQL_VERSION_ID >= 50500
+# include <sql_plugin.h>
+# include <sql_show.h>
+# include <key.h>
+# include <tztime.h>
+# include <sql_base.h>
+#endif
+
+#include <sql_select.h>
+
+#ifdef MRN_HAVE_SQL_OPTIMIZER_H
+# include <sql_optimizer.h>
+#endif
+
+#include <ft_global.h>
+#include <spatial.h>
+#include <mysql.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef WIN32
+# include <math.h>
+# include <direct.h>
+# define MRN_MKDIR(pathname, mode) _mkdir((pathname))
+# define MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(type, variable_name, variable_size) \
+ type *variable_name = (type *)_malloca(sizeof(type) * (variable_size))
+# define MRN_FREE_VARIABLE_LENGTH_ARRAYS(variable_name) _freea(variable_name)
+# define MRN_TABLE_SHARE_LOCK_SHARE_PROC "?key_TABLE_SHARE_LOCK_share@@3IA"
+# define MRN_TABLE_SHARE_LOCK_HA_DATA_PROC "?key_TABLE_SHARE_LOCK_ha_data@@3IA"
+# ifdef _WIN64
+# define MRN_BINLOG_FILTER_PROC "?binlog_filter@@3PEAVRpl_filter@@EA"
+# define MRN_MY_TZ_UTC_PROC "?my_tz_UTC@@3PEAVTime_zone@@EA"
+# else
+# define MRN_BINLOG_FILTER_PROC "?binlog_filter@@3PAVRpl_filter@@A"
+# define MRN_MY_TZ_UTC_PROC "?my_tz_UTC@@3PAVTime_zone@@A"
+# endif
+#else
+# include <dirent.h>
+# include <unistd.h>
+# define MRN_MKDIR(pathname, mode) mkdir((pathname), (mode))
+# define MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(type, variable_name, variable_size) \
+ type variable_name[variable_size]
+# define MRN_FREE_VARIABLE_LENGTH_ARRAYS(variable_name)
+#endif
+
+#include "mrn_err.h"
+#include "mrn_table.hpp"
+#include "ha_mroonga.hpp"
+#include <mrn_path_mapper.hpp>
+#include <mrn_index_table_name.hpp>
+#include <mrn_index_column_name.hpp>
+#include <mrn_debug_column_access.hpp>
+#include <mrn_auto_increment_value_lock.hpp>
+#include <mrn_external_lock.hpp>
+#include <mrn_match_escalation_threshold_scope.hpp>
+#include <mrn_multiple_column_key_codec.hpp>
+#include <mrn_field_normalizer.hpp>
+#include <mrn_encoding.hpp>
+#include <mrn_parameters_parser.hpp>
+#include <mrn_lock.hpp>
+#include <mrn_condition_converter.hpp>
+#include <mrn_time_converter.hpp>
+#include <mrn_smart_grn_obj.hpp>
+
+#ifdef MRN_SUPPORT_FOREIGN_KEYS
+# include <sql_table.h>
+#endif
+
+// for debug
+#define MRN_CLASS_NAME "ha_mroonga"
+
+#define MRN_SHORT_TEXT_SIZE (1 << 12) // 4Kbytes
+#define MRN_TEXT_SIZE (1 << 16) // 64Kbytes
+#define MRN_LONG_TEXT_SIZE (1 << 31) // 2Gbytes
+
+#ifdef MRN_HAVE_TDC_LOCK_TABLE_SHARE
+# define mrn_open_mutex(share) &((share)->tdc.LOCK_table_share)
+# define mrn_open_mutex_lock(share) do { \
+ TABLE_SHARE *share_ = share; \
+ if (share_) { \
+ mysql_mutex_lock(mrn_open_mutex(share_)); \
+ } \
+} while (0)
+# define mrn_open_mutex_unlock(share) do { \
+ TABLE_SHARE *share_ = share; \
+ if (share_) { \
+ mysql_mutex_unlock(mrn_open_mutex(share_)); \
+ } \
+} while (0)
+#else
+# if MYSQL_VERSION_ID >= 50500
+# ifdef DBUG_OFF
+# ifndef _WIN32
+extern mysql_mutex_t LOCK_open;
+# endif
+# endif
+static mysql_mutex_t *mrn_LOCK_open;
+# define mrn_open_mutex_lock(share) mysql_mutex_lock(mrn_LOCK_open)
+# define mrn_open_mutex_unlock(share) mysql_mutex_unlock(mrn_LOCK_open)
+# else
+# ifndef _WIN32
+extern pthread_mutex_t LOCK_open;
+# endif
+static pthread_mutex_t *mrn_LOCK_open;
+# define mrn_open_mutex_lock(share)
+# define mrn_open_mutex_unlock(share)
+# endif
+#endif
+
+#if MYSQL_VERSION_ID >= 50600
+# define MRN_NEED_M_LOCK_TYPE_CHECK_FOR_WRAPPER_EXTERNAL_LOCK
+#endif
+
+#if MYSQL_VERSION_ID >= 50603 && !defined(MRN_MARIADB_P)
+# define MRN_ORDER_IS_ASC(order) ((order)->direction == ORDER::ORDER_ASC)
+#else
+# define MRN_ORDER_IS_ASC(order) ((order)->asc)
+#endif
+
+#define MRN_STRINGIFY(macro_or_string) MRN_STRINGIFY_ARG(macro_or_string)
+#define MRN_STRINGIFY_ARG(contents) #contents
+
+#define MRN_PLUGIN_NAME mroonga
+#define MRN_PLUGIN_NAME_STRING "Mroonga"
+#define MRN_STATUS_VARIABLE_NAME_PREFIX_STRING "Mroonga"
+
+#ifdef MRN_MARIADB_P
+# define st_mysql_plugin st_maria_plugin
+# define mrn_declare_plugin(NAME) maria_declare_plugin(NAME)
+# define mrn_declare_plugin_end maria_declare_plugin_end
+# define MRN_PLUGIN_LAST_VALUES MRN_VERSION, MariaDB_PLUGIN_MATURITY_STABLE
+# define MRN_ABORT_ON_WARNING(thd) thd_kill_level(thd)
+#else
+# define mrn_declare_plugin(NAME) mysql_declare_plugin(NAME)
+# define mrn_declare_plugin_end mysql_declare_plugin_end
+# ifdef MRN_PLUGIN_HAVE_FLAGS
+# define MRN_PLUGIN_LAST_VALUES NULL, 0
+# else
+# define MRN_PLUGIN_LAST_VALUES NULL
+# endif
+# define MRN_ABORT_ON_WARNING(thd) thd->abort_on_warning
+#endif
+
+#ifdef WIN32
+# ifdef MRN_TABLE_SHARE_HAVE_LOCK_SHARE
+ PSI_mutex_key *mrn_table_share_lock_share;
+# endif
+# ifdef MRN_TABLE_SHARE_HAVE_LOCK_HA_DATA
+ PSI_mutex_key *mrn_table_share_lock_ha_data;
+# endif
+#endif
+
+#if MYSQL_VERSION_ID >= 100007 && defined(MRN_MARIADB_P)
+# define MRN_THD_GET_AUTOINC(thd, off, inc) thd_get_autoinc(thd, off, inc)
+# define MRN_GET_ERR_MSG(code) my_get_err_msg(code)
+#else
+# define MRN_THD_GET_AUTOINC(thd, off, inc) \
+ { \
+ *(off) = thd->variables.auto_increment_offset; \
+ *(inc) = thd->variables.auto_increment_increment; \
+ }
+# define MRN_GET_ERR_MSG(code) ER(code)
+#endif
+
+Rpl_filter *mrn_binlog_filter;
+Time_zone *mrn_my_tz_UTC;
+#ifdef MRN_HAVE_TABLE_DEF_CACHE
+HASH *mrn_table_def_cache;
+#endif
+
+static const char *INDEX_COLUMN_NAME = "index";
+static const char *MRN_PLUGIN_AUTHOR = "The Mroonga project";
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* groonga's internal functions */
+const char *grn_obj_get_value_(grn_ctx *ctx, grn_obj *obj, grn_id id, uint32 *size);
+int grn_atoi(const char *nptr, const char *end, const char **rest);
+uint grn_atoui(const char *nptr, const char *end, const char **rest);
+
+/* global variables */
+static pthread_mutex_t mrn_db_mutex;
+static pthread_mutex_t mrn_log_mutex;
+handlerton *mrn_hton_ptr;
+HASH mrn_open_tables;
+pthread_mutex_t mrn_open_tables_mutex;
+HASH mrn_long_term_share;
+pthread_mutex_t mrn_long_term_share_mutex;
+
+/* internal variables */
+static grn_ctx mrn_ctx;
+static grn_obj *mrn_db;
+static grn_hash *mrn_hash;
+
+#ifdef WIN32
+static inline double round(double x)
+{
+ return (floor(x + 0.5));
+}
+#endif
+
+static void mrn_init_encoding_map()
+{
+ mrn::encoding::init();
+}
+
+static int mrn_change_encoding(grn_ctx *ctx, const CHARSET_INFO *charset)
+{
+ int error = 0;
+ if (!mrn::encoding::set(ctx, charset)) {
+ const char *name = "<null>";
+ const char *csname = "<null>";
+ if (charset) {
+ name = charset->name;
+ csname = charset->csname;
+ }
+ error = ER_MRN_CHARSET_NOT_SUPPORT_NUM;
+ my_printf_error(error,
+ ER_MRN_CHARSET_NOT_SUPPORT_STR,
+ MYF(0), name, csname);
+ }
+ return error;
+}
+
+#if !defined(DBUG_OFF) && !defined(_lint)
+static const char *mrn_inspect_thr_lock_type(enum thr_lock_type lock_type)
+{
+ const char *inspected = "<unknown>";
+ switch (lock_type) {
+ case TL_IGNORE:
+ inspected = "TL_IGNORE";
+ break;
+ case TL_UNLOCK:
+ inspected = "TL_UNLOCK";
+ break;
+ case TL_READ_DEFAULT:
+ inspected = "TL_READ_DEFAULT";
+ break;
+ case TL_READ:
+ inspected = "TL_READ";
+ break;
+ case TL_READ_WITH_SHARED_LOCKS:
+ inspected = "TL_READ_WITH_SHARED_LOCKS";
+ break;
+ case TL_READ_HIGH_PRIORITY:
+ inspected = "TL_READ_HIGH_PRIORITY";
+ break;
+ case TL_READ_NO_INSERT:
+ inspected = "TL_READ_NO_INSERT";
+ break;
+ case TL_WRITE_ALLOW_WRITE:
+ inspected = "TL_WRITE_ALLOW_WRITE";
+ break;
+#ifdef MRN_HAVE_TL_WRITE_ALLOW_READ
+ case TL_WRITE_ALLOW_READ:
+ inspected = "TL_WRITE_ALLOW_READ";
+ break;
+#endif
+ case TL_WRITE_CONCURRENT_INSERT:
+ inspected = "TL_WRITE_CONCURRENT_INSERT";
+ break;
+ case TL_WRITE_DELAYED:
+ inspected = "TL_WRITE_DELAYED";
+ break;
+ case TL_WRITE_DEFAULT:
+ inspected = "TL_WRITE_DEFAULT";
+ break;
+ case TL_WRITE_LOW_PRIORITY:
+ inspected = "TL_WRITE_LOW_PRIORITY";
+ break;
+ case TL_WRITE:
+ inspected = "TL_WRITE";
+ break;
+ case TL_WRITE_ONLY:
+ inspected = "TL_WRITE_ONLY";
+ break;
+ }
+ return inspected;
+}
+
+static const char *mrn_inspect_extra_function(enum ha_extra_function operation)
+{
+ const char *inspected = "<unknown>";
+ switch (operation) {
+ case HA_EXTRA_NORMAL:
+ inspected = "HA_EXTRA_NORMAL";
+ break;
+ case HA_EXTRA_QUICK:
+ inspected = "HA_EXTRA_QUICK";
+ break;
+ case HA_EXTRA_NOT_USED:
+ inspected = "HA_EXTRA_NOT_USED";
+ break;
+ case HA_EXTRA_CACHE:
+ inspected = "HA_EXTRA_CACHE";
+ break;
+ case HA_EXTRA_NO_CACHE:
+ inspected = "HA_EXTRA_NO_CACHE";
+ break;
+ case HA_EXTRA_NO_READCHECK:
+ inspected = "HA_EXTRA_NO_READCHECK";
+ break;
+ case HA_EXTRA_READCHECK:
+ inspected = "HA_EXTRA_READCHECK";
+ break;
+ case HA_EXTRA_KEYREAD:
+ inspected = "HA_EXTRA_KEYREAD";
+ break;
+ case HA_EXTRA_NO_KEYREAD:
+ inspected = "HA_EXTRA_NO_KEYREAD";
+ break;
+ case HA_EXTRA_NO_USER_CHANGE:
+ inspected = "HA_EXTRA_NO_USER_CHANGE";
+ break;
+ case HA_EXTRA_KEY_CACHE:
+ inspected = "HA_EXTRA_KEY_CACHE";
+ break;
+ case HA_EXTRA_NO_KEY_CACHE:
+ inspected = "HA_EXTRA_NO_KEY_CACHE";
+ break;
+ case HA_EXTRA_WAIT_LOCK:
+ inspected = "HA_EXTRA_WAIT_LOCK";
+ break;
+ case HA_EXTRA_NO_WAIT_LOCK:
+ inspected = "HA_EXTRA_NO_WAIT_LOCK";
+ break;
+ case HA_EXTRA_WRITE_CACHE:
+ inspected = "HA_EXTRA_WRITE_CACHE";
+ break;
+ case HA_EXTRA_FLUSH_CACHE:
+ inspected = "HA_EXTRA_FLUSH_CACHE";
+ break;
+ case HA_EXTRA_NO_KEYS:
+ inspected = "HA_EXTRA_NO_KEYS";
+ break;
+ case HA_EXTRA_KEYREAD_CHANGE_POS:
+ inspected = "HA_EXTRA_KEYREAD_CHANGE_POS";
+ break;
+ case HA_EXTRA_REMEMBER_POS:
+ inspected = "HA_EXTRA_REMEMBER_POS";
+ break;
+ case HA_EXTRA_RESTORE_POS:
+ inspected = "HA_EXTRA_RESTORE_POS";
+ break;
+ case HA_EXTRA_REINIT_CACHE:
+ inspected = "HA_EXTRA_REINIT_CACHE";
+ break;
+ case HA_EXTRA_FORCE_REOPEN:
+ inspected = "HA_EXTRA_FORCE_REOPEN";
+ break;
+ case HA_EXTRA_FLUSH:
+ inspected = "HA_EXTRA_FLUSH";
+ break;
+ case HA_EXTRA_NO_ROWS:
+ inspected = "HA_EXTRA_NO_ROWS";
+ break;
+ case HA_EXTRA_RESET_STATE:
+ inspected = "HA_EXTRA_RESET_STATE";
+ break;
+ case HA_EXTRA_IGNORE_DUP_KEY:
+ inspected = "HA_EXTRA_IGNORE_DUP_KEY";
+ break;
+ case HA_EXTRA_NO_IGNORE_DUP_KEY:
+ inspected = "HA_EXTRA_NO_IGNORE_DUP_KEY";
+ break;
+ case HA_EXTRA_PREPARE_FOR_DROP:
+ inspected = "HA_EXTRA_PREPARE_FOR_DROP";
+ break;
+ case HA_EXTRA_PREPARE_FOR_UPDATE:
+ inspected = "HA_EXTRA_PREPARE_FOR_UPDATE";
+ break;
+ case HA_EXTRA_PRELOAD_BUFFER_SIZE:
+ inspected = "HA_EXTRA_PRELOAD_BUFFER_SIZE";
+ break;
+ case HA_EXTRA_CHANGE_KEY_TO_UNIQUE:
+ inspected = "HA_EXTRA_CHANGE_KEY_TO_UNIQUE";
+ break;
+ case HA_EXTRA_CHANGE_KEY_TO_DUP:
+ inspected = "HA_EXTRA_CHANGE_KEY_TO_DUP";
+ break;
+ case HA_EXTRA_KEYREAD_PRESERVE_FIELDS:
+ inspected = "HA_EXTRA_KEYREAD_PRESERVE_FIELDS";
+ break;
+ case HA_EXTRA_MMAP:
+ inspected = "HA_EXTRA_MMAP";
+ break;
+ case HA_EXTRA_IGNORE_NO_KEY:
+ inspected = "HA_EXTRA_IGNORE_NO_KEY";
+ break;
+ case HA_EXTRA_NO_IGNORE_NO_KEY:
+ inspected = "HA_EXTRA_NO_IGNORE_NO_KEY";
+ break;
+ case HA_EXTRA_MARK_AS_LOG_TABLE:
+ inspected = "HA_EXTRA_MARK_AS_LOG_TABLE";
+ break;
+ case HA_EXTRA_WRITE_CAN_REPLACE:
+ inspected = "HA_EXTRA_WRITE_CAN_REPLACE";
+ break;
+ case HA_EXTRA_WRITE_CANNOT_REPLACE:
+ inspected = "HA_EXTRA_WRITE_CANNOT_REPLACE";
+ break;
+ case HA_EXTRA_DELETE_CANNOT_BATCH:
+ inspected = "HA_EXTRA_DELETE_CANNOT_BATCH";
+ break;
+ case HA_EXTRA_UPDATE_CANNOT_BATCH:
+ inspected = "HA_EXTRA_UPDATE_CANNOT_BATCH";
+ break;
+ case HA_EXTRA_INSERT_WITH_UPDATE:
+ inspected = "HA_EXTRA_INSERT_WITH_UPDATE";
+ break;
+ case HA_EXTRA_PREPARE_FOR_RENAME:
+ inspected = "HA_EXTRA_PREPARE_FOR_RENAME";
+ break;
+#ifdef MRN_HAVE_HA_EXTRA_ADD_CHILDREN_LIST
+ case HA_EXTRA_ADD_CHILDREN_LIST:
+ inspected = "HA_EXTRA_ADD_CHILDREN_LIST";
+ break;
+#endif
+ case HA_EXTRA_ATTACH_CHILDREN:
+ inspected = "HA_EXTRA_ATTACH_CHILDREN";
+ break;
+#ifdef MRN_HAVE_HA_EXTRA_IS_ATTACHED_CHILDREN
+ case HA_EXTRA_IS_ATTACHED_CHILDREN:
+ inspected = "HA_EXTRA_IS_ATTACHED_CHILDREN";
+ break;
+#endif
+ case HA_EXTRA_DETACH_CHILDREN:
+ inspected = "HA_EXTRA_DETACH_CHILDREN";
+ break;
+#ifdef MRN_HAVE_HA_EXTRA_EXPORT
+ case HA_EXTRA_EXPORT:
+ inspected = "HA_EXTRA_EXPORT";
+ break;
+#endif
+#ifdef MRN_HAVE_HA_EXTRA_SECONDARY_SORT_ROWID
+ case HA_EXTRA_SECONDARY_SORT_ROWID:
+ inspected = "HA_EXTRA_SECONDARY_SORT_ROWID";
+ break;
+#endif
+#ifdef MRN_HAVE_HA_EXTRA_DETACH_CHILD
+ case HA_EXTRA_DETACH_CHILD:
+ inspected = "HA_EXTRA_DETACH_CHILD";
+ break;
+#endif
+#ifdef MRN_HAVE_HA_EXTRA_PREPARE_FOR_FORCED_CLOSE
+ case HA_EXTRA_PREPARE_FOR_FORCED_CLOSE:
+ inspected = "HA_EXTRA_PREPARE_FOR_FORCED_CLOSE";
+ break;
+#endif
+ }
+ return inspected;
+}
+#endif
+
+static uchar *mrn_open_tables_get_key(const uchar *record,
+ size_t *length,
+ my_bool not_used __attribute__ ((unused)))
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ MRN_SHARE *share = reinterpret_cast<MRN_SHARE *>(const_cast<uchar *>(record));
+ *length = share->table_name_length;
+ DBUG_RETURN(reinterpret_cast<uchar *>(share->table_name));
+}
+
+static uchar *mrn_long_term_share_get_key(const uchar *record,
+ size_t *length,
+ my_bool not_used __attribute__ ((unused)))
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ MRN_LONG_TERM_SHARE *long_term_share =
+ reinterpret_cast<MRN_LONG_TERM_SHARE *>(const_cast<uchar *>(record));
+ *length = long_term_share->table_name_length;
+ DBUG_RETURN(reinterpret_cast<uchar *>(long_term_share->table_name));
+}
+
+/* status */
+static long mrn_count_skip = 0;
+static long mrn_fast_order_limit = 0;
+
+/* logging */
+static char *mrn_log_file_path = NULL;
+static FILE *mrn_log_file = NULL;
+static bool mrn_log_file_opened = false;
+static grn_log_level mrn_log_level_default = GRN_LOG_DEFAULT_LEVEL;
+static ulong mrn_log_level = mrn_log_level_default;
+
+char *mrn_default_parser = NULL;
+char *mrn_default_wrapper_engine = NULL;
+static int mrn_lock_timeout = grn_get_lock_timeout();
+static char *mrn_libgroonga_version = const_cast<char *>(grn_get_version());
+static char *mrn_version = const_cast<char *>(MRN_VERSION);
+static char *mrn_vector_column_delimiter = NULL;
+
+typedef enum {
+ MRN_ACTION_ON_ERROR_ERROR,
+ MRN_ACTION_ON_ERROR_ERROR_AND_LOG,
+ MRN_ACTION_ON_ERROR_IGNORE,
+ MRN_ACTION_ON_ERROR_IGNORE_AND_LOG,
+} mrn_action_on_error;
+
+static const char *mrn_action_on_error_names[] = {
+ "ERROR",
+ "ERROR_AND_LOG",
+ "IGNORE",
+ "IGNORE_AND_LOG",
+ NullS,
+};
+
+static mrn_action_on_error mrn_action_on_fulltext_query_error_default =
+ MRN_ACTION_ON_ERROR_ERROR_AND_LOG;
+
+static void mrn_logger_log(grn_ctx *ctx, grn_log_level level,
+ const char *timestamp, const char *title,
+ const char *message, const char *location,
+ void *user_data)
+{
+ const char level_marks[] = " EACewnid-";
+ if (mrn_log_file_opened) {
+ mrn::Lock lock(&mrn_log_mutex);
+ fprintf(mrn_log_file,
+ "%s|%c|%08x|%s\n",
+ timestamp,
+ level_marks[level],
+ static_cast<uint>((ulong)(pthread_self())),
+ message);
+ fflush(mrn_log_file);
+ }
+}
+
+static grn_logger mrn_logger = {
+ mrn_log_level_default,
+ GRN_LOG_TIME|GRN_LOG_MESSAGE,
+ NULL,
+ mrn_logger_log,
+ NULL,
+ NULL
+};
+
+/* global hashes and mutexes */
+HASH mrn_allocated_thds;
+pthread_mutex_t mrn_allocated_thds_mutex;
+static uchar *mrn_allocated_thds_get_key(const uchar *record,
+ size_t *length,
+ my_bool not_used __attribute__ ((unused)))
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ *length = sizeof(THD *);
+ DBUG_RETURN(const_cast<uchar *>(record));
+}
+
+/* system functions */
+
+static struct st_mysql_storage_engine storage_engine_structure =
+{ MYSQL_HANDLERTON_INTERFACE_VERSION };
+
+static struct st_mysql_show_var mrn_status_variables[] =
+{
+ {MRN_STATUS_VARIABLE_NAME_PREFIX_STRING "_count_skip",
+ (char *)&mrn_count_skip, SHOW_LONG},
+ {MRN_STATUS_VARIABLE_NAME_PREFIX_STRING "_fast_order_limit",
+ (char *)&mrn_fast_order_limit, SHOW_LONG},
+ {NullS, NullS, SHOW_LONG}
+};
+
+static const char *mrn_log_level_type_names[] = { "NONE", "EMERG", "ALERT",
+ "CRIT", "ERROR", "WARNING",
+ "NOTICE", "INFO", "DEBUG",
+ "DUMP", NullS };
+static TYPELIB mrn_log_level_typelib =
+{
+ array_elements(mrn_log_level_type_names)-1,
+ "mrn_log_level_typelib",
+ mrn_log_level_type_names,
+ NULL
+};
+
+static void mrn_log_level_update(THD *thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ ulong new_value = *static_cast<const ulong *>(save);
+ ulong old_value = mrn_log_level;
+ mrn_log_level = new_value;
+ mrn_logger.max_level = static_cast<grn_log_level>(mrn_log_level);
+ grn_logger_set(&mrn_ctx, &mrn_logger);
+ grn_ctx *ctx = grn_ctx_open(0);
+ mrn_change_encoding(ctx, system_charset_info);
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "log level changed from '%s' to '%s'",
+ mrn_log_level_type_names[old_value],
+ mrn_log_level_type_names[new_value]);
+ grn_ctx_fin(ctx);
+ DBUG_VOID_RETURN;
+}
+
+static MYSQL_SYSVAR_ENUM(log_level, mrn_log_level,
+ PLUGIN_VAR_RQCMDARG,
+ "logging level",
+ NULL,
+ mrn_log_level_update,
+ static_cast<ulong>(mrn_log_level),
+ &mrn_log_level_typelib);
+
+static void mrn_log_file_update(THD *thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ const char *new_value = *((const char **)save);
+ char **old_value_ptr = (char **)var_ptr;
+
+ grn_ctx ctx;
+ grn_ctx_init(&ctx, 0);
+ mrn_change_encoding(&ctx, system_charset_info);
+
+ const char *new_log_file_name;
+ new_log_file_name = *old_value_ptr;
+
+ if (strcmp(*old_value_ptr, new_value) == 0) {
+ GRN_LOG(&ctx, GRN_LOG_NOTICE,
+ "log file isn't changed "
+ "because the requested path isn't different: <%s>",
+ new_value);
+ } else {
+ GRN_LOG(&ctx, GRN_LOG_NOTICE,
+ "log file is changed: <%s> -> <%s>",
+ *old_value_ptr, new_value);
+
+ int log_file_open_errno = 0;
+ {
+ mrn::Lock lock(&mrn_log_mutex);
+ FILE *new_log_file;
+ new_log_file = fopen(new_value, "a");
+ if (new_log_file) {
+ if (mrn_log_file_opened) {
+ fclose(mrn_log_file);
+ }
+ mrn_log_file = new_log_file;
+ mrn_log_file_opened = true;
+ } else {
+ log_file_open_errno = errno;
+ }
+ }
+
+ if (log_file_open_errno == 0) {
+ GRN_LOG(&ctx, GRN_LOG_NOTICE,
+ "log file is changed: <%s> -> <%s>",
+ *old_value_ptr, new_value);
+ new_log_file_name = new_value;
+ } else {
+ if (mrn_log_file) {
+ GRN_LOG(&ctx, GRN_LOG_ERROR,
+ "log file isn't changed "
+ "because the requested path can't be opened: <%s>: <%s>",
+ new_value, strerror(log_file_open_errno));
+ } else {
+ GRN_LOG(&ctx, GRN_LOG_ERROR,
+ "log file can't be opened: <%s>: <%s>",
+ new_value, strerror(log_file_open_errno));
+ }
+ }
+ }
+
+#ifdef MRN_NEED_FREE_STRING_MEMALLOC_PLUGIN_VAR
+ char *old_log_file_name = *old_value_ptr;
+ *old_value_ptr = my_strdup(new_log_file_name, MYF(MY_WME));
+ my_free(old_log_file_name, MYF(0));
+#else
+ *old_value_ptr = my_strdup(new_log_file_name, MYF(MY_WME));
+#endif
+
+ grn_ctx_fin(&ctx);
+
+ DBUG_VOID_RETURN;
+}
+
+static MYSQL_SYSVAR_STR(log_file, mrn_log_file_path,
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
+ "log file for " MRN_PLUGIN_NAME_STRING,
+ NULL,
+ mrn_log_file_update,
+ MRN_LOG_FILE_PATH);
+
+static void mrn_default_parser_update(THD *thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ const char *new_value = *((const char **)save);
+ char **old_value_ptr = (char **)var_ptr;
+ grn_ctx ctx;
+
+ grn_ctx_init(&ctx, 0);
+ mrn_change_encoding(&ctx, system_charset_info);
+ if (strcmp(*old_value_ptr, new_value) == 0) {
+ GRN_LOG(&ctx, GRN_LOG_NOTICE,
+ "default parser isn't changed "
+ "because the requested default parser isn't different: <%s>",
+ new_value);
+ } else {
+ GRN_LOG(&ctx, GRN_LOG_NOTICE,
+ "default fulltext parser is changed: <%s> -> <%s>",
+ *old_value_ptr, new_value);
+ }
+
+#ifdef MRN_NEED_FREE_STRING_MEMALLOC_PLUGIN_VAR
+ my_free(*old_value_ptr, MYF(0));
+ *old_value_ptr = my_strdup(new_value, MYF(MY_WME));
+#else
+ *old_value_ptr = (char *)new_value;
+#endif
+
+ grn_ctx_fin(&ctx);
+
+ DBUG_VOID_RETURN;
+}
+
+static MYSQL_SYSVAR_STR(default_parser, mrn_default_parser,
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
+ "default fulltext parser",
+ NULL,
+ mrn_default_parser_update,
+ MRN_PARSER_DEFAULT);
+
+static MYSQL_THDVAR_BOOL(
+ dry_write, /* name */
+ PLUGIN_VAR_OPCMDARG, /* options */
+ "If dry_write is true, any write operations are ignored.", /* comment */
+ NULL, /* check */
+ NULL, /* update */
+ false /* default */
+);
+
+static MYSQL_THDVAR_BOOL(
+ enable_optimization, /* name */
+ PLUGIN_VAR_OPCMDARG, /* options */
+ "If enable_optimization is true, some optimizations will be applied.", /* comment */
+ NULL, /* check */
+ NULL, /* update */
+ true /* default */
+);
+
+static MYSQL_THDVAR_LONGLONG(match_escalation_threshold,
+ PLUGIN_VAR_RQCMDARG,
+ "The threshold to determin whether search method is escalated",
+ NULL,
+ NULL,
+ grn_get_default_match_escalation_threshold(),
+ -1,
+ LONGLONG_MAX,
+ 0);
+
+static void mrn_vector_column_delimiter_update(THD *thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ const char *new_value = *((const char **)save);
+ char **old_value_ptr = (char **)var_ptr;
+
+#ifdef MRN_NEED_FREE_STRING_MEMALLOC_PLUGIN_VAR
+ my_free(*old_value_ptr, MYF(0));
+ *old_value_ptr = my_strdup(new_value, MYF(MY_WME));
+#else
+ *old_value_ptr = (char *)new_value;
+#endif
+
+ DBUG_VOID_RETURN;
+}
+
+static MYSQL_SYSVAR_STR(vector_column_delimiter, mrn_vector_column_delimiter,
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
+ "The vector column delimiter",
+ NULL,
+ &mrn_vector_column_delimiter_update,
+ " ");
+
+static void mrn_database_path_prefix_update(THD *thd,
+ struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ const char *new_value = *((const char **)save);
+ char **old_value_ptr = (char **)var_ptr;
+#ifdef MRN_NEED_FREE_STRING_MEMALLOC_PLUGIN_VAR
+ if (*old_value_ptr)
+ my_free(*old_value_ptr, MYF(0));
+ if (new_value)
+ *old_value_ptr = my_strdup(new_value, MYF(MY_WME));
+ else
+ *old_value_ptr = NULL;
+#else
+ *old_value_ptr = (char *)new_value;
+#endif
+ DBUG_VOID_RETURN;
+}
+
+static MYSQL_SYSVAR_STR(database_path_prefix,
+ mrn::PathMapper::default_path_prefix,
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_MEMALLOC,
+ "The database path prefix",
+ NULL,
+ &mrn_database_path_prefix_update,
+ NULL);
+
+static MYSQL_SYSVAR_STR(default_wrapper_engine, mrn_default_wrapper_engine,
+ PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
+ "The default engine for wrapper mode",
+ NULL,
+ NULL,
+ NULL);
+
+static TYPELIB mrn_action_on_error_typelib =
+{
+ array_elements(mrn_action_on_error_names) - 1,
+ "mrn_action_on_error_typelib",
+ mrn_action_on_error_names,
+ NULL
+};
+
+static MYSQL_THDVAR_ENUM(action_on_fulltext_query_error,
+ PLUGIN_VAR_RQCMDARG,
+ "action on fulltext query error",
+ NULL,
+ NULL,
+ mrn_action_on_fulltext_query_error_default,
+ &mrn_action_on_error_typelib);
+
+static void mrn_lock_timeout_update(THD *thd, struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ const int new_value = *static_cast<const int *>(save);
+ int *old_value_ptr = static_cast<int *>(var_ptr);
+
+ *old_value_ptr = new_value;
+ grn_set_lock_timeout(new_value);
+
+ DBUG_VOID_RETURN;
+}
+
+static MYSQL_SYSVAR_INT(lock_timeout,
+ mrn_lock_timeout,
+ PLUGIN_VAR_RQCMDARG,
+ "lock timeout used in Groonga",
+ NULL,
+ mrn_lock_timeout_update,
+ grn_get_lock_timeout(),
+ -1,
+ INT_MAX,
+ 1);
+
+static MYSQL_SYSVAR_STR(libgroonga_version, mrn_libgroonga_version,
+ PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_READONLY,
+ "The version of libgroonga",
+ NULL,
+ NULL,
+ grn_get_version());
+
+static MYSQL_SYSVAR_STR(version, mrn_version,
+ PLUGIN_VAR_NOCMDOPT | PLUGIN_VAR_READONLY,
+ "The version of mroonga",
+ NULL,
+ NULL,
+ MRN_VERSION);
+
+static struct st_mysql_sys_var *mrn_system_variables[] =
+{
+ MYSQL_SYSVAR(log_level),
+ MYSQL_SYSVAR(log_file),
+ MYSQL_SYSVAR(default_parser),
+ MYSQL_SYSVAR(dry_write),
+ MYSQL_SYSVAR(enable_optimization),
+ MYSQL_SYSVAR(match_escalation_threshold),
+ MYSQL_SYSVAR(database_path_prefix),
+ MYSQL_SYSVAR(default_wrapper_engine),
+ MYSQL_SYSVAR(action_on_fulltext_query_error),
+ MYSQL_SYSVAR(lock_timeout),
+ MYSQL_SYSVAR(libgroonga_version),
+ MYSQL_SYSVAR(version),
+ MYSQL_SYSVAR(vector_column_delimiter),
+ NULL
+};
+
+/* mroonga information schema */
+static struct st_mysql_information_schema i_s_info =
+{
+ MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION
+};
+
+static ST_FIELD_INFO i_s_mrn_stats_fields_info[] =
+{
+ {
+ "VERSION",
+ 40,
+ MYSQL_TYPE_STRING,
+ 0,
+ 0,
+ "",
+ SKIP_OPEN_TABLE
+ },
+ {
+ "rows_written",
+ MY_INT32_NUM_DECIMAL_DIGITS,
+ MYSQL_TYPE_LONG,
+ 0,
+ 0,
+ "Rows written to groonga",
+ SKIP_OPEN_TABLE
+ },
+ {
+ "rows_read",
+ MY_INT32_NUM_DECIMAL_DIGITS,
+ MYSQL_TYPE_LONG,
+ 0,
+ 0,
+ "Rows read from groonga",
+ SKIP_OPEN_TABLE
+ }
+};
+
+static int i_s_mrn_stats_deinit(void* p)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ DBUG_RETURN(0);
+}
+
+static int i_s_mrn_stats_fill(
+ THD* thd, TABLE_LIST* tables, Item* cond)
+{
+ TABLE* table = (TABLE *) tables->table;
+ int status = 0;
+ MRN_DBUG_ENTER_FUNCTION();
+ table->field[0]->store(grn_get_version(), strlen(grn_get_version()),
+ system_charset_info);
+ table->field[0]->set_notnull();
+ table->field[1]->store(1); /* TODO */
+ table->field[2]->store(2); /* TODO */
+ if (schema_table_store_record(thd, table)) {
+ status = 1;
+ }
+ DBUG_RETURN(status);
+}
+
+static int i_s_mrn_stats_init(void* p)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ ST_SCHEMA_TABLE* schema = (ST_SCHEMA_TABLE*) p;
+ schema->fields_info = i_s_mrn_stats_fields_info;
+ schema->fill_table = i_s_mrn_stats_fill;
+ DBUG_RETURN(0);
+}
+
+struct st_mysql_plugin i_s_mrn_stats =
+{
+ MYSQL_INFORMATION_SCHEMA_PLUGIN,
+ &i_s_info,
+ MRN_STATUS_VARIABLE_NAME_PREFIX_STRING "_stats",
+ MRN_PLUGIN_AUTHOR,
+ "Statistics for " MRN_PLUGIN_NAME_STRING,
+ PLUGIN_LICENSE_GPL,
+ i_s_mrn_stats_init,
+ i_s_mrn_stats_deinit,
+ MRN_VERSION_IN_HEX,
+ NULL,
+ NULL,
+ MRN_PLUGIN_LAST_VALUES
+};
+/* End of mroonga information schema implementations */
+
+static handler *mrn_handler_create(handlerton *hton, TABLE_SHARE *share, MEM_ROOT *root)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ handler *new_handler = new (root) ha_mroonga(hton, share);
+ DBUG_RETURN(new_handler);
+}
+
+static void mrn_drop_db(const char *path)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ mrn::PathMapper mapper(path);
+ mrn::Lock lock(&mrn_db_mutex);
+ grn_obj *db = NULL;
+ if (!mrn_hash_get(&mrn_ctx, mrn_hash, mapper.db_name(), &db)) {
+ struct stat dummy;
+ if (stat(mapper.db_path(), &dummy) == 0) {
+ db = grn_db_open(&mrn_ctx, mapper.db_path());
+ }
+ }
+ if (db) {
+ if (grn_obj_remove(&mrn_ctx, db)) {
+ GRN_LOG(&mrn_ctx, GRN_LOG_ERROR,
+ "cannot drop database (%s)", mapper.db_path());
+ }
+ }
+ mrn_hash_remove(&mrn_ctx, mrn_hash, mapper.db_name());
+ DBUG_VOID_RETURN;
+}
+
+static void mrn_drop_database(handlerton *hton, char *path)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ mrn_drop_db(path);
+ DBUG_VOID_RETURN;
+}
+
+static int mrn_close_connection(handlerton *hton, THD *thd)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ void *p = *thd_ha_data(thd, mrn_hton_ptr);
+ if (p) {
+ mrn_clear_alter_share(thd);
+ free(p);
+ *thd_ha_data(thd, mrn_hton_ptr) = (void *) NULL;
+ {
+ mrn::Lock lock(&mrn_allocated_thds_mutex);
+ my_hash_delete(&mrn_allocated_thds, (uchar*) thd);
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+static bool mrn_flush_logs(handlerton *hton)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ bool result = 0;
+ if (mrn_log_file_opened) {
+ mrn::Lock lock(&mrn_log_mutex);
+ fclose(mrn_log_file);
+ mrn_log_file = fopen(mrn_log_file_path, "a");
+ }
+ DBUG_RETURN(result);
+}
+
+static grn_builtin_type mrn_grn_type_from_field(grn_ctx *ctx, Field *field,
+ bool for_index_key)
+{
+ grn_builtin_type type = GRN_DB_VOID;
+ enum_field_types mysql_field_type = field->real_type();
+ switch (mysql_field_type) {
+ case MYSQL_TYPE_DECIMAL: // DECIMAL; <= 65bytes
+ type = GRN_DB_SHORT_TEXT; // 4Kbytes
+ break;
+ case MYSQL_TYPE_TINY: // TINYINT; 1byte
+ if (static_cast<Field_num *>(field)->unsigned_flag) {
+ type = GRN_DB_UINT8; // 1byte
+ } else {
+ type = GRN_DB_INT8; // 1byte
+ }
+ break;
+ case MYSQL_TYPE_SHORT: // SMALLINT; 2bytes
+ if (static_cast<Field_num *>(field)->unsigned_flag) {
+ type = GRN_DB_UINT16; // 2bytes
+ } else {
+ type = GRN_DB_INT16; // 2bytes
+ }
+ break;
+ case MYSQL_TYPE_LONG: // INT; 4bytes
+ if (static_cast<Field_num *>(field)->unsigned_flag) {
+ type = GRN_DB_UINT32; // 4bytes
+ } else {
+ type = GRN_DB_INT32; // 4bytes
+ }
+ break;
+ case MYSQL_TYPE_FLOAT: // FLOAT; 4 or 8bytes
+ case MYSQL_TYPE_DOUBLE: // DOUBLE; 8bytes
+ type = GRN_DB_FLOAT; // 8bytes
+ break;
+ case MYSQL_TYPE_NULL: // NULL; 1byte
+ type = GRN_DB_INT8; // 1byte
+ break;
+ case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP; 4bytes
+ type = GRN_DB_TIME; // 8bytes
+ break;
+ case MYSQL_TYPE_LONGLONG: // BIGINT; 8bytes
+ if (static_cast<Field_num *>(field)->unsigned_flag) {
+ type = GRN_DB_UINT64; // 8bytes
+ } else {
+ type = GRN_DB_INT64; // 8bytes
+ }
+ break;
+ case MYSQL_TYPE_INT24: // MEDIUMINT; 3bytes
+ if (static_cast<Field_num *>(field)->unsigned_flag) {
+ type = GRN_DB_UINT32; // 4bytes
+ } else {
+ type = GRN_DB_INT32; // 4bytes
+ }
+ break;
+ case MYSQL_TYPE_DATE: // DATE; 4bytes
+ case MYSQL_TYPE_TIME: // TIME; 3bytes
+ case MYSQL_TYPE_DATETIME: // DATETIME; 8bytes
+ case MYSQL_TYPE_YEAR: // YEAR; 1byte
+ case MYSQL_TYPE_NEWDATE: // DATE; 3bytes
+ type = GRN_DB_TIME; // 8bytes
+ break;
+ case MYSQL_TYPE_VARCHAR: // VARCHAR; <= 64KB * 4 + 2bytes
+ if (for_index_key) {
+ type = GRN_DB_SHORT_TEXT; // 4Kbytes
+ } else {
+ if (field->field_length <= MRN_SHORT_TEXT_SIZE) {
+ type = GRN_DB_SHORT_TEXT; // 4Kbytes
+ } else if (field->field_length <= MRN_TEXT_SIZE) {
+ type = GRN_DB_TEXT; // 64Kbytes
+ } else {
+ type = GRN_DB_LONG_TEXT; // 2Gbytes
+ }
+ }
+ break;
+ case MYSQL_TYPE_BIT: // BIT; <= 8bytes
+ type = GRN_DB_INT64; // 8bytes
+ break;
+#ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2
+ case MYSQL_TYPE_TIMESTAMP2: // TIMESTAMP; 4bytes
+ type = GRN_DB_TIME; // 8bytes
+ break;
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
+ case MYSQL_TYPE_DATETIME2: // DATETIME; 8bytes
+ type = GRN_DB_TIME; // 8bytes
+ break;
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
+ case MYSQL_TYPE_TIME2: // TIME(FSP); 3 + (FSP + 1) / 2 bytes
+ // 0 <= FSP <= 6; 3-6bytes
+ type = GRN_DB_TIME; // 8bytes
+ break;
+#endif
+ case MYSQL_TYPE_NEWDECIMAL: // DECIMAL; <= 9bytes
+ type = GRN_DB_SHORT_TEXT; // 4Kbytes
+ break;
+ case MYSQL_TYPE_ENUM: // ENUM; <= 2bytes
+ if (field->pack_length() == 1) {
+ type = GRN_DB_UINT8; // 1bytes
+ } else {
+ type = GRN_DB_UINT16; // 2bytes
+ }
+ break;
+ case MYSQL_TYPE_SET: // SET; <= 8bytes
+ switch (field->pack_length()) {
+ case 1:
+ type = GRN_DB_UINT8; // 1byte
+ break;
+ case 2:
+ type = GRN_DB_UINT16; // 2bytes
+ break;
+ case 3:
+ case 4:
+ type = GRN_DB_UINT32; // 3bytes
+ break;
+ case 8:
+ default:
+ type = GRN_DB_UINT64; // 8bytes
+ break;
+ }
+ break;
+ case MYSQL_TYPE_TINY_BLOB: // TINYBLOB; <= 256bytes + 1byte
+ type = GRN_DB_SHORT_TEXT; // 4Kbytes
+ break;
+ case MYSQL_TYPE_MEDIUM_BLOB: // MEDIUMBLOB; <= 16Mbytes + 3bytes
+ if (for_index_key) {
+ type = GRN_DB_SHORT_TEXT; // 4Kbytes
+ } else {
+ type = GRN_DB_LONG_TEXT; // 2Gbytes
+ }
+ break;
+ case MYSQL_TYPE_LONG_BLOB: // LONGBLOB; <= 4Gbytes + 4bytes
+ if (for_index_key) {
+ type = GRN_DB_SHORT_TEXT; // 4Kbytes
+ } else {
+ type = GRN_DB_LONG_TEXT; // 2Gbytes
+ }
+ break;
+ case MYSQL_TYPE_BLOB: // BLOB; <= 64Kbytes + 2bytes
+ if (for_index_key) {
+ type = GRN_DB_SHORT_TEXT; // 4Kbytes
+ } else {
+ type = GRN_DB_LONG_TEXT; // 2Gbytes
+ }
+ break;
+ case MYSQL_TYPE_VAR_STRING: // VARCHAR; <= 255byte * 4 + 1bytes
+ if (for_index_key) {
+ type = GRN_DB_SHORT_TEXT; // 4Kbytes
+ } else {
+ if (field->field_length <= MRN_SHORT_TEXT_SIZE) {
+ type = GRN_DB_SHORT_TEXT; // 4Kbytes
+ } else if (field->field_length <= MRN_TEXT_SIZE) {
+ type = GRN_DB_TEXT; // 64Kbytes
+ } else {
+ type = GRN_DB_LONG_TEXT; // 2Gbytes
+ }
+ }
+ break;
+ case MYSQL_TYPE_STRING: // CHAR; < 1Kbytes =~ (255 * 4)bytes
+ // 4 is the maximum size of a character
+ type = GRN_DB_SHORT_TEXT; // 4Kbytes
+ break;
+ case MYSQL_TYPE_GEOMETRY: // case-by-case
+ type = GRN_DB_WGS84_GEO_POINT; // 8bytes
+ break;
+ }
+ return type;
+}
+
+#ifdef HAVE_SPATIAL
+static int mrn_set_geometry(grn_ctx *ctx, grn_obj *buf,
+ const char *wkb, uint wkb_size)
+{
+ int error = 0;
+ Geometry_buffer buffer;
+ Geometry *geometry;
+
+ geometry = Geometry::construct(&buffer, wkb, wkb_size);
+ if (!geometry) {
+ return ER_CANT_CREATE_GEOMETRY_OBJECT;
+ }
+ switch (geometry->get_class_info()->m_type_id) {
+ case Geometry::wkb_point:
+ {
+ Gis_point *point = (Gis_point *)geometry;
+ double latitude = 0.0, longitude = 0.0;
+#ifdef MRN_HAVE_POINT_XY
+ point_xy xy(0.0, 0.0);
+ point->get_xy(&xy);
+ longitude = xy.x;
+ latitude = xy.y;
+#else
+ point->get_xy(&longitude, &latitude);
+#endif
+ grn_obj_reinit(ctx, buf, GRN_DB_WGS84_GEO_POINT, 0);
+ GRN_GEO_POINT_SET(ctx, buf,
+ GRN_GEO_DEGREE2MSEC(latitude),
+ GRN_GEO_DEGREE2MSEC(longitude));
+ break;
+ }
+ default:
+ my_printf_error(ER_MRN_GEOMETRY_NOT_SUPPORT_NUM,
+ ER_MRN_GEOMETRY_NOT_SUPPORT_STR, MYF(0));
+ error = ER_MRN_GEOMETRY_NOT_SUPPORT_NUM;
+ break;
+ }
+ delete geometry;
+
+ return error;
+}
+#endif
+
+static uint mrn_alter_table_flags(uint flags) {
+ uint alter_flags = 0;
+#ifdef HA_INPLACE_ADD_INDEX_NO_READ_WRITE
+ bool is_inplace_index_change;
+# ifdef MRN_HAVE_ALTER_INFO
+ is_inplace_index_change = (((flags & Alter_info::ALTER_ADD_INDEX) &&
+ (flags & Alter_info::ALTER_DROP_INDEX)) ||
+ (flags & Alter_info::ALTER_CHANGE_COLUMN));
+# else
+ is_inplace_index_change = (((flags & ALTER_ADD_INDEX) &&
+ (flags & ALTER_DROP_INDEX)) ||
+ (flags & ALTER_CHANGE_COLUMN));
+# endif
+ if (!is_inplace_index_change) {
+ alter_flags |=
+ HA_INPLACE_ADD_INDEX_NO_READ_WRITE |
+ HA_INPLACE_DROP_INDEX_NO_READ_WRITE |
+ HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE |
+ HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE |
+ HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE |
+ HA_INPLACE_DROP_PK_INDEX_NO_READ_WRITE |
+ HA_INPLACE_ADD_INDEX_NO_WRITE |
+ HA_INPLACE_DROP_INDEX_NO_WRITE |
+ HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE |
+ HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE |
+ HA_INPLACE_ADD_PK_INDEX_NO_WRITE |
+ HA_INPLACE_DROP_PK_INDEX_NO_WRITE;
+ }
+#endif
+ return alter_flags;
+}
+
+static int mrn_init(void *p)
+{
+ // init handlerton
+ grn_ctx *ctx = NULL;
+ handlerton *hton;
+ hton = (handlerton *)p;
+ hton->state = SHOW_OPTION_YES;
+ hton->create = mrn_handler_create;
+ hton->flags = HTON_NO_PARTITION;
+ hton->drop_database = mrn_drop_database;
+ hton->close_connection = mrn_close_connection;
+ hton->flush_logs = mrn_flush_logs;
+ hton->alter_table_flags = mrn_alter_table_flags;
+ mrn_hton_ptr = hton;
+
+#ifdef _WIN32
+ HMODULE current_module = GetModuleHandle(NULL);
+ mrn_binlog_filter =
+ *((Rpl_filter **)GetProcAddress(current_module, MRN_BINLOG_FILTER_PROC));
+ mrn_my_tz_UTC =
+ *((Time_zone **)GetProcAddress(current_module, MRN_MY_TZ_UTC_PROC));
+# ifdef MRN_HAVE_TABLE_DEF_CACHE
+ mrn_table_def_cache = (HASH *)GetProcAddress(current_module,
+ "?table_def_cache@@3Ust_hash@@A");
+# endif
+# ifndef MRN_HAVE_TDC_LOCK_TABLE_SHARE
+ mrn_LOCK_open =
+# if MYSQL_VERSION_ID >= 50500
+ (mysql_mutex_t *)GetProcAddress(current_module,
+ "?LOCK_open@@3Ust_mysql_mutex@@A");
+# else
+ (pthread_mutex_t *)GetProcAddress(current_module,
+ "?LOCK_open@@3U_RTL_CRITICAL_SECTION@@A");
+# endif
+# endif
+# ifdef MRN_TABLE_SHARE_HAVE_LOCK_SHARE
+ mrn_table_share_lock_share =
+ (PSI_mutex_key *)GetProcAddress(current_module, MRN_TABLE_SHARE_LOCK_SHARE_PROC);
+# endif
+# ifdef MRN_TABLE_SHARE_HAVE_LOCK_HA_DATA
+ mrn_table_share_lock_ha_data =
+ (PSI_mutex_key *)GetProcAddress(current_module, MRN_TABLE_SHARE_LOCK_HA_DATA_PROC);
+# endif
+#else
+ mrn_binlog_filter = binlog_filter;
+ mrn_my_tz_UTC = my_tz_UTC;
+# ifdef MRN_HAVE_TABLE_DEF_CACHE
+ mrn_table_def_cache = &table_def_cache;
+# endif
+# ifndef MRN_HAVE_TDC_LOCK_TABLE_SHARE
+ mrn_LOCK_open = &LOCK_open;
+# endif
+#endif
+
+ // init groonga
+ if (grn_init() != GRN_SUCCESS) {
+ goto err_grn_init;
+ }
+
+ grn_set_lock_timeout(mrn_lock_timeout);
+
+ mrn_init_encoding_map();
+
+ grn_ctx_init(&mrn_ctx, 0);
+ ctx = &mrn_ctx;
+ if (mrn_change_encoding(ctx, system_charset_info))
+ goto err_mrn_change_encoding;
+
+ if (pthread_mutex_init(&mrn_log_mutex, NULL) != 0) {
+ goto err_log_mutex_init;
+ }
+
+ mrn_logger.max_level = static_cast<grn_log_level>(mrn_log_level);
+ grn_logger_set(ctx, &mrn_logger);
+ if (!(mrn_log_file = fopen(mrn_log_file_path, "a"))) {
+ goto err_log_file_open;
+ }
+ mrn_log_file_opened = true;
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "%s started.", MRN_PACKAGE_STRING);
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "log level is '%s'",
+ mrn_log_level_type_names[mrn_log_level]);
+
+ // init meta-info database
+ if (!(mrn_db = grn_db_create(ctx, NULL, NULL))) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "cannot create system database, exiting");
+ goto err_db_create;
+ }
+ grn_ctx_use(ctx, mrn_db);
+
+ // init hash
+ if (!(mrn_hash = grn_hash_create(ctx, NULL,
+ MRN_MAX_KEY_SIZE, sizeof(grn_obj *),
+ GRN_OBJ_KEY_VAR_SIZE))) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "cannot init hash, exiting");
+ goto err_hash_create;
+ }
+
+ // init lock
+ if ((pthread_mutex_init(&mrn_db_mutex, NULL) != 0)) {
+ goto err_db_mutex_init;
+ }
+ if ((pthread_mutex_init(&mrn_allocated_thds_mutex, NULL) != 0)) {
+ goto err_allocated_thds_mutex_init;
+ }
+ if (my_hash_init(&mrn_allocated_thds, system_charset_info, 32, 0, 0,
+ mrn_allocated_thds_get_key, 0, 0)) {
+ goto error_allocated_thds_hash_init;
+ }
+ if ((pthread_mutex_init(&mrn_open_tables_mutex, NULL) != 0)) {
+ goto err_allocated_open_tables_mutex_init;
+ }
+ if (my_hash_init(&mrn_open_tables, system_charset_info, 32, 0, 0,
+ mrn_open_tables_get_key, 0, 0)) {
+ goto error_allocated_open_tables_hash_init;
+ }
+ if ((pthread_mutex_init(&mrn_long_term_share_mutex, NULL) != 0)) {
+ goto error_allocated_long_term_share_mutex_init;
+ }
+ if (my_hash_init(&mrn_long_term_share, system_charset_info, 32, 0, 0,
+ mrn_long_term_share_get_key, 0, 0)) {
+ goto error_allocated_long_term_share_hash_init;
+ }
+
+#ifdef MRN_USE_MYSQL_DATA_HOME
+ mrn::PathMapper::default_mysql_data_home_path = mysql_data_home;
+#endif
+
+ return 0;
+
+error_allocated_long_term_share_hash_init:
+ pthread_mutex_destroy(&mrn_long_term_share_mutex);
+error_allocated_long_term_share_mutex_init:
+ my_hash_free(&mrn_open_tables);
+error_allocated_open_tables_hash_init:
+ pthread_mutex_destroy(&mrn_open_tables_mutex);
+err_allocated_open_tables_mutex_init:
+ my_hash_free(&mrn_allocated_thds);
+error_allocated_thds_hash_init:
+ pthread_mutex_destroy(&mrn_allocated_thds_mutex);
+err_allocated_thds_mutex_init:
+ pthread_mutex_destroy(&mrn_db_mutex);
+err_db_mutex_init:
+ grn_hash_close(ctx, mrn_hash);
+err_hash_create:
+ grn_obj_unlink(ctx, mrn_db);
+err_db_create:
+ if (mrn_log_file_opened) {
+ fclose(mrn_log_file);
+ mrn_log_file_opened = false;
+ }
+err_log_file_open:
+ pthread_mutex_destroy(&mrn_log_mutex);
+err_log_mutex_init:
+err_mrn_change_encoding:
+ grn_ctx_fin(ctx);
+ grn_fin();
+err_grn_init:
+ return -1;
+}
+
+static int mrn_deinit(void *p)
+{
+ THD *thd = current_thd, *tmp_thd;
+ grn_ctx *ctx = &mrn_ctx;
+ void *value;
+ MRN_LONG_TERM_SHARE *long_term_share;
+
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "%s deinit", MRN_PACKAGE_STRING);
+
+ if (thd && thd_sql_command(thd) == SQLCOM_UNINSTALL_PLUGIN) {
+ mrn::Lock lock(&mrn_allocated_thds_mutex);
+ while ((tmp_thd = (THD *) my_hash_element(&mrn_allocated_thds, 0)))
+ {
+ mrn_clear_alter_share(tmp_thd);
+ void *slot_ptr = mrn_get_slot_data(tmp_thd, false);
+ if (slot_ptr) free(slot_ptr);
+ *thd_ha_data(tmp_thd, mrn_hton_ptr) = (void *) NULL;
+ my_hash_delete(&mrn_allocated_thds, (uchar *) tmp_thd);
+ }
+ }
+
+ {
+ mrn::Lock lock(&mrn_open_tables_mutex);
+ while ((long_term_share = (MRN_LONG_TERM_SHARE *)
+ my_hash_element(&mrn_long_term_share, 0)))
+ {
+ mrn_free_long_term_share(long_term_share);
+ }
+ }
+
+ my_hash_free(&mrn_long_term_share);
+ pthread_mutex_destroy(&mrn_long_term_share_mutex);
+ my_hash_free(&mrn_open_tables);
+ pthread_mutex_destroy(&mrn_open_tables_mutex);
+ my_hash_free(&mrn_allocated_thds);
+ pthread_mutex_destroy(&mrn_allocated_thds_mutex);
+ pthread_mutex_destroy(&mrn_db_mutex);
+ GRN_HASH_EACH(ctx, mrn_hash, id, NULL, 0, &value, {
+ grn_obj *db;
+ memcpy(&db, value, sizeof(grn_obj *));
+ grn_obj_unlink(ctx, db);
+ });
+ grn_hash_close(ctx, mrn_hash);
+ grn_obj_unlink(ctx, mrn_db);
+
+ grn_ctx_fin(ctx);
+ grn_fin();
+
+ if (mrn_log_file_opened) {
+ fclose(mrn_log_file);
+ mrn_log_file_opened = false;
+ }
+ pthread_mutex_destroy(&mrn_log_mutex);
+
+ return 0;
+}
+
+mrn_declare_plugin(MRN_PLUGIN_NAME)
+{
+ MYSQL_STORAGE_ENGINE_PLUGIN,
+ &storage_engine_structure,
+ MRN_PLUGIN_NAME_STRING,
+ MRN_PLUGIN_AUTHOR,
+ "CJK-ready fulltext search, column store",
+ PLUGIN_LICENSE_GPL,
+ mrn_init,
+ mrn_deinit,
+ MRN_VERSION_IN_HEX,
+ mrn_status_variables,
+ mrn_system_variables,
+ MRN_PLUGIN_LAST_VALUES
+},
+i_s_mrn_stats
+mrn_declare_plugin_end;
+
+static void mrn_generic_ft_clear(FT_INFO *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ st_mrn_ft_info *info = (st_mrn_ft_info *)handler;
+ if (!info->ctx) {
+ DBUG_VOID_RETURN;
+ }
+
+ if (info->cursor) {
+ grn_obj_unlink(info->ctx, info->cursor);
+ }
+ if (info->id_accessor) {
+ grn_obj_unlink(info->ctx, info->id_accessor);
+ }
+ if (info->key_accessor) {
+ grn_obj_unlink(info->ctx, info->key_accessor);
+ }
+ grn_obj_unlink(info->ctx, info->result);
+ grn_obj_unlink(info->ctx, info->score_column);
+ grn_obj_unlink(info->ctx, &(info->key));
+ grn_obj_unlink(info->ctx, &(info->score));
+
+ info->ctx = NULL;
+
+ DBUG_VOID_RETURN;
+}
+
+static void mrn_generic_ft_close_search(FT_INFO *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ st_mrn_ft_info *info = (st_mrn_ft_info *)handler;
+ mrn_generic_ft_clear(handler);
+ delete info;
+ DBUG_VOID_RETURN;
+}
+
+static int mrn_wrapper_ft_read_next(FT_INFO *handler, char *record)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+}
+
+static float mrn_wrapper_ft_find_relevance(FT_INFO *handler, uchar *record,
+ uint length)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ st_mrn_ft_info *info = (st_mrn_ft_info *)handler;
+ float score = 0.0;
+ grn_id record_id;
+
+ mrn_change_encoding(info->ctx, NULL);
+ key_copy((uchar *)(GRN_TEXT_VALUE(&(info->key))), record,
+ info->primary_key_info, info->primary_key_info->key_length);
+ record_id = grn_table_get(info->ctx,
+ info->table,
+ GRN_TEXT_VALUE(&(info->key)),
+ GRN_TEXT_LEN(&(info->key)));
+
+ if (record_id != GRN_ID_NIL) {
+ grn_id result_record_id;
+ result_record_id = grn_table_get(info->ctx, info->result,
+ &record_id, sizeof(grn_id));
+ if (result_record_id != GRN_ID_NIL) {
+ GRN_BULK_REWIND(&(info->score));
+ grn_obj_get_value(info->ctx, info->score_column,
+ result_record_id, &(info->score));
+ score = (float)GRN_INT32_VALUE(&(info->score));
+ }
+ }
+
+ DBUG_PRINT("info",
+ ("mroonga: record_id=%d score=%g", record_id, score));
+
+ DBUG_RETURN(score);
+}
+
+static void mrn_wrapper_ft_close_search(FT_INFO *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ mrn_generic_ft_close_search(handler);
+ DBUG_VOID_RETURN;
+}
+
+static float mrn_wrapper_ft_get_relevance(FT_INFO *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ st_mrn_ft_info *info = (st_mrn_ft_info *)handler;
+ float score = 0.0;
+ grn_id record_id;
+ ha_mroonga *mroonga = info->mroonga;
+ mrn_change_encoding(info->ctx, NULL);
+ record_id = grn_table_get(info->ctx,
+ info->table,
+ GRN_TEXT_VALUE(&(mroonga->key_buffer)),
+ GRN_TEXT_LEN(&(mroonga->key_buffer)));
+
+ if (record_id != GRN_ID_NIL) {
+ grn_id result_record_id;
+ result_record_id = grn_table_get(info->ctx, info->result,
+ &record_id, sizeof(grn_id));
+ if (result_record_id != GRN_ID_NIL) {
+ GRN_BULK_REWIND(&(info->score));
+ grn_obj_get_value(info->ctx, info->score_column,
+ result_record_id, &(info->score));
+ score = (float)GRN_INT32_VALUE(&(info->score));
+ }
+ }
+
+ DBUG_PRINT("info",
+ ("mroonga: record_id=%d score=%g", record_id, score));
+
+ DBUG_RETURN(score);
+}
+
+static void mrn_wrapper_ft_reinit_search(FT_INFO *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ DBUG_VOID_RETURN;
+}
+
+static _ft_vft mrn_wrapper_ft_vft = {
+ mrn_wrapper_ft_read_next,
+ mrn_wrapper_ft_find_relevance,
+ mrn_wrapper_ft_close_search,
+ mrn_wrapper_ft_get_relevance,
+ mrn_wrapper_ft_reinit_search
+};
+
+static int mrn_storage_ft_read_next(FT_INFO *handler, char *record)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+}
+
+static float mrn_storage_ft_find_relevance(FT_INFO *handler, uchar *record,
+ uint length)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ st_mrn_ft_info *info = (st_mrn_ft_info *)handler;
+ ha_mroonga *mroonga = info->mroonga;
+ mrn_change_encoding(info->ctx, NULL);
+
+ float score = 0.0;
+ if (mroonga->record_id != GRN_ID_NIL) {
+ grn_id result_record_id;
+ result_record_id = grn_table_get(info->ctx, info->result,
+ &(mroonga->record_id), sizeof(grn_id));
+ if (result_record_id != GRN_ID_NIL) {
+ GRN_BULK_REWIND(&(info->score));
+ grn_obj_get_value(info->ctx, info->score_column,
+ result_record_id, &(info->score));
+ score = (float)GRN_INT32_VALUE(&(info->score));
+ }
+ }
+ DBUG_PRINT("info", ("mroonga: record_id=%d score=%g",
+ mroonga->record_id, score));
+
+ DBUG_RETURN(score);
+}
+
+static void mrn_storage_ft_close_search(FT_INFO *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ mrn_generic_ft_close_search(handler);
+ DBUG_VOID_RETURN;
+}
+
+static float mrn_storage_ft_get_relevance(FT_INFO *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ st_mrn_ft_info *info = (st_mrn_ft_info *)handler;
+ ha_mroonga *mroonga = info->mroonga;
+ mrn_change_encoding(info->ctx, NULL);
+
+ float score = 0.0;
+ if (mroonga->record_id != GRN_ID_NIL) {
+ grn_id result_record_id;
+ result_record_id = grn_table_get(info->ctx, info->result,
+ &(mroonga->record_id), sizeof(grn_id));
+ if (result_record_id != GRN_ID_NIL) {
+ GRN_BULK_REWIND(&(info->score));
+ grn_obj_get_value(info->ctx, info->score_column,
+ result_record_id, &(info->score));
+ score = (float)GRN_INT32_VALUE(&(info->score));
+ }
+ }
+ DBUG_PRINT("info",
+ ("mroonga: record_id=%d score=%g", mroonga->record_id, score));
+
+ DBUG_RETURN(score);
+}
+
+static void mrn_storage_ft_reinit_search(FT_INFO *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ DBUG_VOID_RETURN;
+}
+
+static _ft_vft mrn_storage_ft_vft = {
+ mrn_storage_ft_read_next,
+ mrn_storage_ft_find_relevance,
+ mrn_storage_ft_close_search,
+ mrn_storage_ft_get_relevance,
+ mrn_storage_ft_reinit_search
+};
+
+static int mrn_no_such_key_ft_read_next(FT_INFO *handler, char *record)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+}
+
+static float mrn_no_such_key_ft_find_relevance(FT_INFO *handler, uchar *record,
+ uint length)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ DBUG_RETURN(0.0);
+}
+
+static void mrn_no_such_key_ft_close_search(FT_INFO *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ st_mrn_ft_info *info = (st_mrn_ft_info *)handler;
+ delete info;
+ DBUG_VOID_RETURN;
+}
+
+static float mrn_no_such_key_ft_get_relevance(FT_INFO *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ DBUG_RETURN(0.0);
+}
+
+static void mrn_no_such_key_ft_reinit_search(FT_INFO *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ DBUG_VOID_RETURN;
+}
+
+static _ft_vft mrn_no_such_key_ft_vft = {
+ mrn_no_such_key_ft_read_next,
+ mrn_no_such_key_ft_find_relevance,
+ mrn_no_such_key_ft_close_search,
+ mrn_no_such_key_ft_get_relevance,
+ mrn_no_such_key_ft_reinit_search
+};
+
+#ifdef HA_CAN_FULLTEXT_EXT
+static uint mrn_generic_ft_get_version()
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ // This value is not used in MySQL 5.6.7-rc. So it is
+ // meaningless. It may be used in the future...
+ uint version = 1;
+ DBUG_RETURN(version);
+}
+
+static ulonglong mrn_generic_ft_ext_get_flags()
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ // TODO: Should we support FTS_ORDERED_RESULT?
+ // TODO: Shuold we support FTS_DOCID_IN_RESULT?
+ ulonglong flags = 0;
+ DBUG_RETURN(flags);
+}
+
+// This function is used if we enable FTS_DOCID_IN_RESULT flag and the
+// table has "FTS_DOC_ID" (defined as FTS_DOC_ID_COL_NAME macro)
+// special name column. Should we support "FTS_DOC_ID" special name
+// column?
+// See also sql/sql_optimizer.cc:JOIN::optimize_fts_query().
+static ulonglong mrn_generic_ft_ext_get_docid(FT_INFO_EXT *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ ulonglong id = GRN_ID_NIL;
+ DBUG_RETURN(id);
+}
+
+static ulonglong mrn_generic_ft_ext_count_matches(FT_INFO_EXT *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ st_mrn_ft_info *info = reinterpret_cast<st_mrn_ft_info *>(handler);
+ ulonglong n_records = grn_table_size(info->ctx, info->result);
+ DBUG_RETURN(n_records);
+}
+
+static uint mrn_wrapper_ft_ext_get_version()
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ uint version = mrn_generic_ft_get_version();
+ DBUG_RETURN(version);
+}
+
+static ulonglong mrn_wrapper_ft_ext_get_flags()
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ ulonglong flags = mrn_generic_ft_ext_get_flags();
+ DBUG_RETURN(flags);
+}
+
+static ulonglong mrn_wrapper_ft_ext_get_docid(FT_INFO_EXT *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ ulonglong id = mrn_generic_ft_ext_get_docid(handler);
+ DBUG_RETURN(id);
+}
+
+static ulonglong mrn_wrapper_ft_ext_count_matches(FT_INFO_EXT *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ ulonglong n_records = mrn_generic_ft_ext_count_matches(handler);
+ DBUG_RETURN(n_records);
+}
+
+static _ft_vft_ext mrn_wrapper_ft_vft_ext = {
+ mrn_wrapper_ft_ext_get_version,
+ mrn_wrapper_ft_ext_get_flags,
+ mrn_wrapper_ft_ext_get_docid,
+ mrn_wrapper_ft_ext_count_matches
+};
+
+static uint mrn_storage_ft_ext_get_version()
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ uint version = mrn_generic_ft_get_version();
+ DBUG_RETURN(version);
+}
+
+static ulonglong mrn_storage_ft_ext_get_flags()
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ ulonglong flags = mrn_generic_ft_ext_get_flags();
+ DBUG_RETURN(flags);
+}
+
+static ulonglong mrn_storage_ft_ext_get_docid(FT_INFO_EXT *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ ulonglong id = mrn_generic_ft_ext_get_docid(handler);
+ DBUG_RETURN(id);
+}
+
+static ulonglong mrn_storage_ft_ext_count_matches(FT_INFO_EXT *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ ulonglong n_records = mrn_generic_ft_ext_count_matches(handler);
+ DBUG_RETURN(n_records);
+}
+
+static _ft_vft_ext mrn_storage_ft_vft_ext = {
+ mrn_storage_ft_ext_get_version,
+ mrn_storage_ft_ext_get_flags,
+ mrn_storage_ft_ext_get_docid,
+ mrn_storage_ft_ext_count_matches
+};
+
+static uint mrn_no_such_key_ft_ext_get_version()
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ uint version = mrn_generic_ft_get_version();
+ DBUG_RETURN(version);
+}
+
+static ulonglong mrn_no_such_key_ft_ext_get_flags()
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ ulonglong flags = mrn_generic_ft_ext_get_flags();
+ DBUG_RETURN(flags);
+}
+
+static ulonglong mrn_no_such_key_ft_ext_get_docid(FT_INFO_EXT *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ ulonglong id = GRN_ID_NIL;
+ DBUG_RETURN(id);
+}
+
+static ulonglong mrn_no_such_key_ft_ext_count_matches(FT_INFO_EXT *handler)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ ulonglong n_records = 0;
+ DBUG_RETURN(n_records);
+}
+
+static _ft_vft_ext mrn_no_such_key_ft_vft_ext = {
+ mrn_no_such_key_ft_ext_get_version,
+ mrn_no_such_key_ft_ext_get_flags,
+ mrn_no_such_key_ft_ext_get_docid,
+ mrn_no_such_key_ft_ext_count_matches
+};
+#endif
+
+/* handler implementation */
+ha_mroonga::ha_mroonga(handlerton *hton, TABLE_SHARE *share_arg)
+ :handler(hton, share_arg),
+ wrap_handler(NULL),
+ is_clone(false),
+ parent_for_clone(NULL),
+ mem_root_for_clone(NULL),
+ record_id(GRN_ID_NIL),
+ key_id(NULL),
+ del_key_id(NULL),
+
+ wrap_ft_init_count(0),
+ share(NULL),
+ wrap_key_info(NULL),
+ base_key_info(NULL),
+
+ analyzed_for_create(false),
+ wrap_handler_for_create(NULL),
+#ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX
+ hnd_add_index(NULL),
+#endif
+#ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER
+ alter_key_info_buffer(NULL),
+#else
+ wrap_alter_key_info(NULL),
+#endif
+ mrn_lock_type(F_UNLCK),
+
+ ctx_entity_(),
+ ctx(&ctx_entity_),
+ grn_table(NULL),
+ grn_columns(NULL),
+ grn_column_ranges(NULL),
+ grn_index_tables(NULL),
+ grn_index_columns(NULL),
+ grn_table_is_referenced(false),
+
+ grn_source_column_geo(NULL),
+ cursor_geo(NULL),
+ cursor(NULL),
+ index_table_cursor(NULL),
+ empty_value_records(NULL),
+ empty_value_records_cursor(NULL),
+
+ sorted_result(NULL),
+ matched_record_keys(NULL),
+ blob_buffers(NULL),
+
+ dup_key(0),
+
+ count_skip(false),
+ fast_order_limit(false),
+ fast_order_limit_with_index(false),
+
+ ignoring_duplicated_key(false),
+ inserting_with_update(false),
+ fulltext_searching(false),
+ ignoring_no_key_columns(false),
+ replacing_(false),
+ written_by_row_based_binlog(0),
+ current_ft_item(NULL)
+{
+ MRN_DBUG_ENTER_METHOD();
+ grn_ctx_init(ctx, 0);
+ mrn_change_encoding(ctx, system_charset_info);
+ grn_ctx_use(ctx, mrn_db);
+ GRN_WGS84_GEO_POINT_INIT(&top_left_point, 0);
+ GRN_WGS84_GEO_POINT_INIT(&bottom_right_point, 0);
+ GRN_WGS84_GEO_POINT_INIT(&source_point, 0);
+ GRN_TEXT_INIT(&key_buffer, 0);
+ GRN_TEXT_INIT(&encoded_key_buffer, 0);
+ GRN_VOID_INIT(&old_value_buffer);
+ GRN_VOID_INIT(&new_value_buffer);
+ DBUG_VOID_RETURN;
+}
+
+ha_mroonga::~ha_mroonga()
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (analyzed_for_create) {
+ if (wrap_handler_for_create) {
+ delete wrap_handler_for_create;
+ }
+ if (share_for_create.wrapper_mode) {
+ plugin_unlock(NULL, share_for_create.plugin);
+ }
+ mrn_free_share_alloc(&share_for_create);
+ free_root(&mem_root_for_create, MYF(0));
+ }
+ if (blob_buffers)
+ {
+ delete [] blob_buffers;
+ }
+ grn_obj_unlink(ctx, &top_left_point);
+ grn_obj_unlink(ctx, &bottom_right_point);
+ grn_obj_unlink(ctx, &source_point);
+ grn_obj_unlink(ctx, &key_buffer);
+ grn_obj_unlink(ctx, &encoded_key_buffer);
+ grn_obj_unlink(ctx, &old_value_buffer);
+ grn_obj_unlink(ctx, &new_value_buffer);
+ grn_ctx_fin(ctx);
+ DBUG_VOID_RETURN;
+}
+
+const char *ha_mroonga::table_type() const
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(MRN_PLUGIN_NAME_STRING);
+}
+
+const char *ha_mroonga::index_type(uint key_nr)
+{
+ MRN_DBUG_ENTER_METHOD();
+ KEY key_info = table->s->key_info[key_nr];
+ if (key_info.algorithm == HA_KEY_ALG_FULLTEXT) {
+ DBUG_RETURN("FULLTEXT");
+ } else if (key_info.algorithm == HA_KEY_ALG_HASH) {
+ DBUG_RETURN("HASH");
+ } else {
+ DBUG_RETURN("BTREE");
+ }
+}
+
+static const char *ha_mroonga_exts[] = {
+ NullS
+};
+const char **ha_mroonga::bas_ext() const
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(ha_mroonga_exts);
+}
+
+uint ha_mroonga::wrapper_max_supported_record_length() const
+{
+ uint res;
+ MRN_DBUG_ENTER_METHOD();
+ if (analyzed_for_create && share_for_create.wrapper_mode) {
+ res = wrap_handler_for_create->max_supported_record_length();
+ } else {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->max_supported_record_length();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ DBUG_RETURN(res);
+}
+
+uint ha_mroonga::storage_max_supported_record_length() const
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(HA_MAX_REC_LENGTH);
+}
+
+uint ha_mroonga::max_supported_record_length() const
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ uint res;
+ if (!share && !analyzed_for_create &&
+ (
+ thd_sql_command(ha_thd()) == SQLCOM_CREATE_TABLE ||
+ thd_sql_command(ha_thd()) == SQLCOM_CREATE_INDEX ||
+ thd_sql_command(ha_thd()) == SQLCOM_ALTER_TABLE
+ )
+ ) {
+ create_share_for_create();
+ }
+ if (analyzed_for_create && share_for_create.wrapper_mode) {
+ res = wrapper_max_supported_record_length();
+ } else if (wrap_handler && share && share->wrapper_mode) {
+ res = wrapper_max_supported_record_length();
+ } else {
+ res = storage_max_supported_record_length();
+ }
+
+ DBUG_RETURN(res);
+}
+
+uint ha_mroonga::wrapper_max_supported_keys() const
+{
+ uint res;
+ MRN_DBUG_ENTER_METHOD();
+ if (analyzed_for_create && share_for_create.wrapper_mode) {
+ res = wrap_handler_for_create->max_supported_keys();
+ } else {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->max_supported_keys();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ DBUG_RETURN(res);
+}
+
+uint ha_mroonga::storage_max_supported_keys() const
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(HA_MAX_REC_LENGTH);
+}
+
+uint ha_mroonga::max_supported_keys() const
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ uint res;
+ if (!share && !analyzed_for_create &&
+ (
+ thd_sql_command(ha_thd()) == SQLCOM_CREATE_TABLE ||
+ thd_sql_command(ha_thd()) == SQLCOM_CREATE_INDEX ||
+ thd_sql_command(ha_thd()) == SQLCOM_ALTER_TABLE
+ )
+ ) {
+ create_share_for_create();
+ }
+ if (analyzed_for_create && share_for_create.wrapper_mode) {
+ res = wrapper_max_supported_keys();
+ } else if (wrap_handler && share && share->wrapper_mode) {
+ res = wrapper_max_supported_keys();
+ } else {
+ res = storage_max_supported_keys();
+ }
+
+ DBUG_RETURN(res);
+}
+
+uint ha_mroonga::wrapper_max_supported_key_length() const
+{
+ uint res;
+ MRN_DBUG_ENTER_METHOD();
+ if (analyzed_for_create && share_for_create.wrapper_mode) {
+ res = wrap_handler_for_create->max_supported_key_length();
+ } else {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->max_supported_key_length();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ DBUG_RETURN(res);
+}
+
+uint ha_mroonga::storage_max_supported_key_length() const
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(HA_MAX_REC_LENGTH);
+}
+
+uint ha_mroonga::max_supported_key_length() const
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ uint res;
+ if (!share && !analyzed_for_create &&
+ (
+ thd_sql_command(ha_thd()) == SQLCOM_CREATE_TABLE ||
+ thd_sql_command(ha_thd()) == SQLCOM_CREATE_INDEX ||
+ thd_sql_command(ha_thd()) == SQLCOM_ALTER_TABLE
+ )
+ ) {
+ create_share_for_create();
+ }
+ if (analyzed_for_create && share_for_create.wrapper_mode) {
+ res = wrapper_max_supported_key_length();
+ } else if (wrap_handler && share && share->wrapper_mode) {
+ res = wrapper_max_supported_key_length();
+ } else {
+ res = storage_max_supported_key_length();
+ }
+
+ DBUG_RETURN(res);
+}
+
+uint ha_mroonga::wrapper_max_supported_key_part_length() const
+{
+ uint res;
+ MRN_DBUG_ENTER_METHOD();
+ if (analyzed_for_create && share_for_create.wrapper_mode) {
+ res = wrap_handler_for_create->max_supported_key_part_length();
+ } else {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->max_supported_key_part_length();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ DBUG_RETURN(res);
+}
+
+uint ha_mroonga::storage_max_supported_key_part_length() const
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(HA_MAX_REC_LENGTH);
+}
+
+uint ha_mroonga::max_supported_key_part_length() const
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ uint res;
+ if (!share && !analyzed_for_create &&
+ (
+ thd_sql_command(ha_thd()) == SQLCOM_CREATE_TABLE ||
+ thd_sql_command(ha_thd()) == SQLCOM_CREATE_INDEX ||
+ thd_sql_command(ha_thd()) == SQLCOM_ALTER_TABLE
+ )
+ ) {
+ create_share_for_create();
+ }
+ if (analyzed_for_create && share_for_create.wrapper_mode) {
+ res = wrapper_max_supported_key_part_length();
+ } else if (wrap_handler && share && share->wrapper_mode) {
+ res = wrapper_max_supported_key_part_length();
+ } else {
+ res = storage_max_supported_key_part_length();
+ }
+
+ DBUG_RETURN(res);
+}
+
+ulonglong ha_mroonga::wrapper_table_flags() const
+{
+ ulonglong table_flags;
+ MRN_DBUG_ENTER_METHOD();
+ if (analyzed_for_create && share_for_create.wrapper_mode) {
+ table_flags = wrap_handler_for_create->ha_table_flags();
+ } else {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ table_flags = wrap_handler->ha_table_flags();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ table_flags |= HA_CAN_FULLTEXT | HA_PRIMARY_KEY_REQUIRED_FOR_DELETE |
+ HA_CAN_RTREEKEYS | HA_REC_NOT_IN_SEQ;
+#ifdef HA_CAN_REPAIR
+ table_flags |= HA_CAN_REPAIR;
+#endif
+#ifdef HA_CAN_FULLTEXT_EXT
+ table_flags |= HA_CAN_FULLTEXT_EXT;
+#endif
+ DBUG_RETURN(table_flags);
+}
+
+ulonglong ha_mroonga::storage_table_flags() const
+{
+ MRN_DBUG_ENTER_METHOD();
+ ulonglong flags =
+ HA_NO_TRANSACTIONS |
+ HA_PARTIAL_COLUMN_READ |
+ HA_REC_NOT_IN_SEQ |
+ HA_NULL_IN_KEY |
+ HA_CAN_INDEX_BLOBS |
+ HA_STATS_RECORDS_IS_EXACT |
+ HA_CAN_FULLTEXT |
+ HA_CAN_INSERT_DELAYED |
+ HA_BINLOG_FLAGS |
+ HA_CAN_BIT_FIELD |
+ HA_DUPLICATE_POS |
+ HA_CAN_GEOMETRY |
+ HA_CAN_RTREEKEYS;
+ //HA_HAS_RECORDS;
+#ifdef HA_MUST_USE_TABLE_CONDITION_PUSHDOWN
+ flags |= HA_MUST_USE_TABLE_CONDITION_PUSHDOWN;
+#endif
+#ifdef HA_CAN_REPAIR
+ flags |= HA_CAN_REPAIR;
+#endif
+#ifdef HA_CAN_FULLTEXT_EXT
+ flags |= HA_CAN_FULLTEXT_EXT;
+#endif
+ DBUG_RETURN(flags);
+}
+
+ulonglong ha_mroonga::table_flags() const
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ ulonglong flags;
+ if (!share && !analyzed_for_create &&
+ (
+ thd_sql_command(ha_thd()) == SQLCOM_CREATE_TABLE ||
+ thd_sql_command(ha_thd()) == SQLCOM_CREATE_INDEX ||
+ thd_sql_command(ha_thd()) == SQLCOM_ALTER_TABLE
+ )
+ ) {
+ create_share_for_create();
+ }
+ if (analyzed_for_create && share_for_create.wrapper_mode) {
+ flags = wrapper_table_flags();
+ } else if (wrap_handler && share && share->wrapper_mode) {
+ flags = wrapper_table_flags();
+ } else {
+ flags = storage_table_flags();
+ }
+
+ DBUG_RETURN(flags);
+}
+
+ulong ha_mroonga::wrapper_index_flags(uint idx, uint part, bool all_parts) const
+{
+ ulong index_flags;
+ KEY key = table_share->key_info[idx];
+ MRN_DBUG_ENTER_METHOD();
+ if (key.algorithm == HA_KEY_ALG_BTREE || key.algorithm == HA_KEY_ALG_UNDEF) {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ index_flags = wrap_handler->index_flags(idx, part, all_parts);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ } else {
+ index_flags = HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR;
+ }
+ DBUG_RETURN(index_flags);
+}
+
+ulong ha_mroonga::storage_index_flags(uint idx, uint part, bool all_parts) const
+{
+ MRN_DBUG_ENTER_METHOD();
+ ulong flags;
+ KEY key = table_share->key_info[idx];
+ if (key.algorithm == HA_KEY_ALG_BTREE || key.algorithm == HA_KEY_ALG_UNDEF) {
+ flags = HA_READ_NEXT | HA_READ_PREV | HA_READ_RANGE;
+ bool need_normalize_p = false;
+ Field *field = &key.key_part[part].field[0];
+ if (field && should_normalize(field)) {
+ need_normalize_p = true;
+ }
+ if (!need_normalize_p) {
+ flags |= HA_KEYREAD_ONLY;
+ }
+ if (KEY_N_KEY_PARTS(&key) > 1 || !need_normalize_p) {
+ flags |= HA_READ_ORDER;
+ }
+ } else {
+ flags = HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR;
+ }
+ DBUG_RETURN(flags);
+}
+
+ulong ha_mroonga::index_flags(uint idx, uint part, bool all_parts) const
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ KEY key = table_share->key_info[idx];
+ if (key.algorithm == HA_KEY_ALG_FULLTEXT) {
+ DBUG_RETURN(HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR);
+ }
+ if (mrn_is_geo_key(&key)) {
+ DBUG_RETURN(HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR);
+ }
+
+ int error = 0;
+ if (wrap_handler && share && share->wrapper_mode)
+ {
+ error = wrapper_index_flags(idx, part, all_parts);
+ } else {
+ error = storage_index_flags(idx, part, all_parts);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::create_share_for_create() const
+{
+ int error;
+ THD *thd = ha_thd();
+ LEX *lex = thd->lex;
+ HA_CREATE_INFO *create_info = &lex->create_info;
+ TABLE_LIST *table_list = lex->select_lex.table_list.first;
+ MRN_DBUG_ENTER_METHOD();
+ wrap_handler_for_create = NULL;
+ memset(&table_for_create, 0, sizeof(TABLE));
+ memset(&share_for_create, 0, sizeof(MRN_SHARE));
+ memset(&table_share_for_create, 0, sizeof(TABLE_SHARE));
+ if (table_share) {
+ table_share_for_create.comment = table_share->comment;
+ table_share_for_create.connect_string = table_share->connect_string;
+ } else {
+#ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER
+ if (thd_sql_command(ha_thd()) != SQLCOM_CREATE_INDEX) {
+#endif
+ table_share_for_create.comment = create_info->comment;
+ table_share_for_create.connect_string = create_info->connect_string;
+#ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER
+ }
+#endif
+ if (thd_sql_command(ha_thd()) == SQLCOM_ALTER_TABLE ||
+ thd_sql_command(ha_thd()) == SQLCOM_CREATE_INDEX) {
+ st_mrn_slot_data *slot_data = mrn_get_slot_data(thd, false);
+ if (slot_data && slot_data->alter_create_info) {
+ create_info = slot_data->alter_create_info;
+ if (slot_data->alter_connect_string) {
+ table_share_for_create.connect_string.str =
+ slot_data->alter_connect_string;
+ table_share_for_create.connect_string.length =
+ strlen(slot_data->alter_connect_string);
+ } else {
+ table_share_for_create.connect_string.str = NULL;
+ table_share_for_create.connect_string.length = 0;
+ }
+ if (slot_data->alter_comment) {
+ table_share_for_create.comment.str =
+ slot_data->alter_comment;
+ table_share_for_create.comment.length =
+ strlen(slot_data->alter_comment);
+ } else {
+ table_share_for_create.comment.str = NULL;
+ table_share_for_create.comment.length = 0;
+ }
+ }
+ }
+ }
+ init_alloc_root(&mem_root_for_create, 1024, 0, MYF(0));
+ analyzed_for_create = true;
+ if (table_list) {
+ share_for_create.table_name = table_list->table_name;
+ share_for_create.table_name_length = table_list->table_name_length;
+ }
+ share_for_create.table_share = &table_share_for_create;
+ table_for_create.s = &table_share_for_create;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ table_for_create.part_info = NULL;
+#endif
+ if ((error = mrn_parse_table_param(&share_for_create, &table_for_create)))
+ goto error;
+
+ if (share_for_create.wrapper_mode)
+ {
+ wrap_handler_for_create =
+ share_for_create.hton->create(share_for_create.hton, NULL,
+ &mem_root_for_create);
+ if (!wrap_handler_for_create) {
+ error = HA_ERR_OUT_OF_MEM;
+ goto error;
+ }
+ wrap_handler_for_create->init();
+ }
+ DBUG_RETURN(0);
+
+error:
+ if (share_for_create.wrapper_mode) {
+ plugin_unlock(NULL, share_for_create.plugin);
+ }
+ mrn_free_share_alloc(&share_for_create);
+ free_root(&mem_root_for_create, MYF(0));
+ analyzed_for_create = false;
+ thd->clear_error();
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_create(const char *name, TABLE *table,
+ HA_CREATE_INFO *info, MRN_SHARE *tmp_share)
+{
+ int error = 0;
+ handler *hnd;
+ MRN_DBUG_ENTER_METHOD();
+
+ if (table_share->primary_key == MAX_KEY)
+ {
+ my_message(ER_REQUIRES_PRIMARY_KEY,
+ MRN_GET_ERR_MSG(ER_REQUIRES_PRIMARY_KEY), MYF(0));
+ DBUG_RETURN(ER_REQUIRES_PRIMARY_KEY);
+ }
+
+ mrn::PathMapper mapper(name);
+ error = wrapper_create_index(name, table, info, tmp_share,
+ mapper.table_name());
+ if (error)
+ DBUG_RETURN(error);
+
+ wrap_key_info = mrn_create_key_info_for_table(tmp_share, table, &error);
+ if (error)
+ DBUG_RETURN(error);
+ base_key_info = table->key_info;
+
+ share = tmp_share;
+ MRN_SET_WRAP_SHARE_KEY(tmp_share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (!(hnd =
+ tmp_share->hton->create(tmp_share->hton, table->s,
+ current_thd->mem_root)))
+ {
+ MRN_SET_BASE_SHARE_KEY(tmp_share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ share = NULL;
+ if (wrap_key_info)
+ {
+ my_free(wrap_key_info, MYF(0));
+ wrap_key_info = NULL;
+ }
+ base_key_info = NULL;
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+ hnd->init();
+ error = hnd->ha_create(name, table, info);
+ MRN_SET_BASE_SHARE_KEY(tmp_share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ share = NULL;
+ delete hnd;
+
+ if (error) {
+ wrapper_delete_index(name, tmp_share, mapper.table_name());
+ }
+
+ if (wrap_key_info)
+ {
+ my_free(wrap_key_info, MYF(0));
+ wrap_key_info = NULL;
+ }
+ base_key_info = NULL;
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_create_index_fulltext_validate(KEY *key_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+ uint i;
+ for (i = 0; i < KEY_N_KEY_PARTS(key_info); i++) {
+ Field *field = key_info->key_part[i].field;
+
+ grn_builtin_type gtype = mrn_grn_type_from_field(ctx, field, true);
+ if (gtype != GRN_DB_SHORT_TEXT)
+ {
+ error = ER_CANT_CREATE_TABLE;
+ GRN_LOG(ctx, GRN_LOG_ERROR,
+ "key type must be text: <%d> "
+ "(TODO: We should show type name not type ID.)",
+ field->type());
+ my_message(ER_CANT_CREATE_TABLE,
+ "key type must be text. (TODO: We should show type name.)",
+ MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_create_index_fulltext(const char *grn_table_name,
+ int i,
+ KEY *key_info,
+ grn_obj **index_tables,
+ grn_obj **index_columns,
+ MRN_SHARE *tmp_share)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+
+ error = wrapper_create_index_fulltext_validate(key_info);
+ if (error) {
+ DBUG_RETURN(error);
+ }
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ grn_obj_flags index_table_flags =
+ GRN_OBJ_TABLE_PAT_KEY |
+ GRN_OBJ_PERSISTENT;
+ grn_obj *index_table;
+
+ grn_obj_flags index_column_flags =
+ GRN_OBJ_COLUMN_INDEX | GRN_OBJ_WITH_POSITION | GRN_OBJ_PERSISTENT;
+ if (KEY_N_KEY_PARTS(key_info) > 1) {
+ index_column_flags |= GRN_OBJ_WITH_SECTION;
+ }
+
+ mrn::SmartGrnObj lexicon_key_type(ctx, GRN_DB_SHORT_TEXT);
+ error = mrn_change_encoding(ctx, key_info->key_part->field->charset());
+ if (error) {
+ DBUG_RETURN(error);
+ }
+ mrn::IndexTableName index_table_name(grn_table_name, key_info->name);
+ index_table = grn_table_create(ctx,
+ index_table_name.c_str(),
+ index_table_name.length(),
+ NULL,
+ index_table_flags,
+ lexicon_key_type.get(),
+ 0);
+ if (ctx->rc) {
+ error = ER_CANT_CREATE_TABLE;
+ my_message(ER_CANT_CREATE_TABLE, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ mrn_change_encoding(ctx, system_charset_info);
+ index_tables[i] = index_table;
+
+ grn_obj *tokenizer = find_tokenizer(tmp_share->key_parser[i],
+ tmp_share->key_parser_length[i]);
+ if (tokenizer) {
+ grn_info_type info_type = GRN_INFO_DEFAULT_TOKENIZER;
+ grn_obj_set_info(ctx, index_table, info_type, tokenizer);
+ grn_obj_unlink(ctx, tokenizer);
+ }
+
+ if (should_normalize(&key_info->key_part->field[0])) {
+ grn_info_type info_type = GRN_INFO_NORMALIZER;
+ grn_obj *normalizer = find_normalizer(key_info);
+ if (normalizer) {
+ grn_obj_set_info(ctx, index_table, info_type, normalizer);
+ grn_obj_unlink(ctx, normalizer);
+ }
+ }
+
+ grn_obj *index_column = grn_column_create(ctx, index_table,
+ INDEX_COLUMN_NAME,
+ strlen(INDEX_COLUMN_NAME),
+ NULL,
+ index_column_flags,
+ grn_table);
+ if (ctx->rc) {
+ error = ER_CANT_CREATE_TABLE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ if (index_columns) {
+ index_columns[i] = index_column;
+ } else {
+ grn_obj_unlink(ctx, index_column);
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_create_index_geo(const char *grn_table_name,
+ int i,
+ KEY *key_info,
+ grn_obj **index_tables,
+ grn_obj **index_columns,
+ MRN_SHARE *tmp_share)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error;
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ mrn::IndexTableName index_table_name(grn_table_name, key_info->name);
+
+ grn_obj_flags index_table_flags =
+ GRN_OBJ_TABLE_PAT_KEY |
+ GRN_OBJ_PERSISTENT;
+ grn_obj *index_table;
+
+ grn_obj_flags index_column_flags =
+ GRN_OBJ_COLUMN_INDEX | GRN_OBJ_PERSISTENT;
+
+ grn_obj *lexicon_key_type = grn_ctx_at(ctx, GRN_DB_WGS84_GEO_POINT);
+ index_table = grn_table_create(ctx,
+ index_table_name.c_str(),
+ index_table_name.length(),
+ NULL,
+ index_table_flags, lexicon_key_type, 0);
+ if (ctx->rc) {
+ error = ER_CANT_CREATE_TABLE;
+ my_message(ER_CANT_CREATE_TABLE, ctx->errbuf, MYF(0));
+ grn_obj_unlink(ctx, lexicon_key_type);
+ DBUG_RETURN(error);
+ }
+ grn_obj_unlink(ctx, lexicon_key_type);
+ index_tables[i] = index_table;
+
+ grn_obj *index_column = grn_column_create(ctx, index_table,
+ INDEX_COLUMN_NAME,
+ strlen(INDEX_COLUMN_NAME),
+ NULL,
+ index_column_flags,
+ grn_table);
+ if (ctx->rc) {
+ error = ER_CANT_CREATE_TABLE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ if (index_columns) {
+ index_columns[i] = index_column;
+ } else {
+ grn_obj_unlink(ctx, index_column);
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_create_index(const char *name, TABLE *table,
+ HA_CREATE_INFO *info,
+ MRN_SHARE *tmp_share,
+ const char *grn_table_name)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+ error = ensure_database_create(name);
+ if (error)
+ DBUG_RETURN(error);
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ grn_obj *grn_index_table;
+ char *grn_table_path = NULL; // we don't specify path
+ grn_obj *pkey_type = grn_ctx_at(ctx, GRN_DB_SHORT_TEXT);
+ grn_obj *pkey_value_type = NULL; // we don't use this
+ grn_obj_flags grn_table_flags = GRN_OBJ_PERSISTENT | GRN_OBJ_TABLE_HASH_KEY;
+
+ grn_index_table = grn_table_create(ctx, grn_table_name, strlen(grn_table_name),
+ grn_table_path, grn_table_flags,
+ pkey_type, pkey_value_type);
+ if (ctx->rc) {
+ error = ER_CANT_CREATE_TABLE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ if (grn_table) {
+ grn_obj_unlink(ctx, grn_table);
+ }
+ grn_table = grn_index_table;
+
+ uint i;
+ uint n_keys = table->s->keys;
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, n_keys);
+ if (!tmp_share->disable_keys) {
+ for (i = 0; i < n_keys; i++) {
+ index_tables[i] = NULL;
+
+ KEY key_info = table->s->key_info[i];
+ if (key_info.algorithm == HA_KEY_ALG_FULLTEXT) {
+ error = wrapper_create_index_fulltext(grn_table_name,
+ i, &key_info,
+ index_tables, NULL, tmp_share);
+ } else if (mrn_is_geo_key(&key_info)) {
+ error = wrapper_create_index_geo(grn_table_name,
+ i, &key_info,
+ index_tables, NULL, tmp_share);
+ }
+ }
+ }
+
+ if (error) {
+ for (uint j = 0; j < i; j++) {
+ if (index_tables[j]) {
+ grn_obj_remove(ctx, index_tables[j]);
+ }
+ }
+ grn_obj_remove(ctx, grn_table);
+ grn_table = NULL;
+ }
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_create(const char *name, TABLE *table,
+ HA_CREATE_INFO *info, MRN_SHARE *tmp_share)
+{
+ int error;
+ MRN_LONG_TERM_SHARE *long_term_share = tmp_share->long_term_share;
+ MRN_DBUG_ENTER_METHOD();
+
+ if (info->auto_increment_value) {
+ mrn::Lock lock(&long_term_share->auto_inc_mutex);
+ long_term_share->auto_inc_value = info->auto_increment_value;
+ DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu",
+ long_term_share->auto_inc_value));
+ long_term_share->auto_inc_inited = true;
+ }
+
+ error = storage_create_validate_pseudo_column(table);
+ if (error)
+ DBUG_RETURN(error);
+
+ error = storage_create_validate_index(table);
+ if (error)
+ DBUG_RETURN(error);
+
+ error = ensure_database_create(name);
+ if (error)
+ DBUG_RETURN(error);
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ grn_obj_flags table_flags = GRN_OBJ_PERSISTENT;
+
+ /* primary key must be handled before creating table */
+ grn_obj *pkey_type;
+ uint pkey_nr = table->s->primary_key;
+ if (pkey_nr != MAX_INDEXES) {
+ KEY key_info = table->s->key_info[pkey_nr];
+ bool is_id;
+
+ int key_parts = KEY_N_KEY_PARTS(&key_info);
+ if (key_parts == 1) {
+ Field *pkey_field = key_info.key_part[0].field;
+ const char *column_name = pkey_field->field_name;
+ is_id = (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0);
+
+ grn_builtin_type gtype = mrn_grn_type_from_field(ctx, pkey_field, false);
+ pkey_type = grn_ctx_at(ctx, gtype);
+ } else {
+ is_id = false;
+ pkey_type = grn_ctx_at(ctx, GRN_DB_SHORT_TEXT);
+ }
+
+ // default algorithm is BTREE ==> PAT
+ if (!is_id && key_info.algorithm == HA_KEY_ALG_HASH) {
+ table_flags |= GRN_OBJ_TABLE_HASH_KEY;
+ } else if (!is_id) {
+ table_flags |= GRN_OBJ_TABLE_PAT_KEY;
+ } else {
+ // for _id
+ table_flags |= GRN_OBJ_TABLE_NO_KEY;
+ pkey_type = NULL;
+ }
+
+ } else {
+ // primary key doesn't exists
+ table_flags |= GRN_OBJ_TABLE_NO_KEY;
+ pkey_type = NULL;
+ }
+
+ /* create table */
+ grn_obj *table_obj;
+ mrn::PathMapper mapper(name);
+
+ char *table_path = NULL; // we don't specify path
+ grn_obj *pkey_value_type = NULL; // we don't use this
+
+ table_obj = grn_table_create(ctx,
+ mapper.table_name(), strlen(mapper.table_name()),
+ table_path,
+ table_flags, pkey_type, pkey_value_type);
+ if (ctx->rc) {
+ error = ER_CANT_CREATE_TABLE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+
+ if (table_flags == (GRN_OBJ_PERSISTENT | GRN_OBJ_TABLE_PAT_KEY)) {
+ KEY key_info = table->s->key_info[pkey_nr];
+ int key_parts = KEY_N_KEY_PARTS(&key_info);
+ if (key_parts == 1) {
+ Field *field = &(key_info.key_part->field[0]);
+ if (should_normalize(field)) {
+ grn_obj *normalizer = find_normalizer(&key_info);
+ if (normalizer) {
+ grn_info_type info_type = GRN_INFO_NORMALIZER;
+ grn_obj_set_info(ctx, table_obj, info_type, normalizer);
+ grn_obj_unlink(ctx, normalizer);
+ }
+ }
+ if (tmp_share->default_tokenizer) {
+ grn_obj *default_tokenizer =
+ grn_ctx_get(ctx,
+ tmp_share->default_tokenizer,
+ tmp_share->default_tokenizer_length);
+ if (default_tokenizer) {
+ grn_info_type info_type = GRN_INFO_DEFAULT_TOKENIZER;
+ grn_obj_set_info(ctx, table_obj, info_type, default_tokenizer);
+ grn_obj_unlink(ctx, default_tokenizer);
+ }
+ }
+ }
+ }
+
+ /* create columns */
+ uint n_columns = table->s->fields;
+ for (uint i = 0; i < n_columns; i++) {
+ grn_obj *col_type;
+ Field *field = table->s->field[i];
+ const char *column_name = field->field_name;
+ int column_name_size = strlen(column_name);
+
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ continue;
+ }
+
+#ifdef MRN_SUPPORT_FOREIGN_KEYS
+ if (storage_create_foreign_key(table, mapper.table_name(), field, table_obj,
+ error)) {
+ continue;
+ }
+ if (error) {
+ grn_obj_remove(ctx, table_obj);
+ DBUG_RETURN(error);
+ }
+#endif
+
+ grn_obj_flags col_flags = GRN_OBJ_PERSISTENT;
+ if (tmp_share->col_flags[i]) {
+ // TODO: parse flags
+ if (strcmp(tmp_share->col_flags[i], "COLUMN_VECTOR") == 0) {
+ col_flags |= GRN_OBJ_COLUMN_VECTOR;
+ } else {
+ col_flags |= GRN_OBJ_COLUMN_SCALAR;
+ }
+ } else {
+ col_flags |= GRN_OBJ_COLUMN_SCALAR;
+ }
+ grn_builtin_type gtype = mrn_grn_type_from_field(ctx, field, false);
+ if (tmp_share->col_type[i]) {
+ col_type = grn_ctx_get(ctx, tmp_share->col_type[i], -1);
+ } else {
+ col_type = grn_ctx_at(ctx, gtype);
+ }
+ char *col_path = NULL; // we don't specify path
+
+ grn_column_create(ctx, table_obj, column_name, column_name_size,
+ col_path, col_flags, col_type);
+ if (ctx->rc) {
+ grn_obj_remove(ctx, table_obj);
+ error = ER_CANT_CREATE_TABLE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+
+ error = storage_create_indexes(table, mapper.table_name(), table_obj,
+ tmp_share);
+ if (error) {
+ grn_obj_remove(ctx, table_obj);
+ table_obj = NULL;
+ }
+
+ if (table_obj) {
+ grn_obj_unlink(ctx, table_obj);
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_create_validate_pseudo_column(TABLE *table)
+{
+ int error = 0;
+ uint i, n_columns;
+
+ MRN_DBUG_ENTER_METHOD();
+ n_columns = table->s->fields;
+ for (i = 0; i < n_columns; i++) {
+ Field *field = table->s->field[i];
+ const char *column_name = field->field_name;
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ switch (field->type()) {
+ case MYSQL_TYPE_TINY :
+ case MYSQL_TYPE_SHORT :
+ case MYSQL_TYPE_INT24 :
+ case MYSQL_TYPE_LONG :
+ case MYSQL_TYPE_LONGLONG :
+ break;
+ default:
+ GRN_LOG(ctx, GRN_LOG_ERROR, "_id must be numeric data type");
+ error = ER_CANT_CREATE_TABLE;
+ my_message(error, "_id must be numeric data type", MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+ }
+
+ DBUG_RETURN(error);
+}
+
+#ifdef MRN_SUPPORT_FOREIGN_KEYS
+bool ha_mroonga::storage_create_foreign_key(TABLE *table,
+ const char *grn_table_name,
+ Field *field,
+ grn_obj *table_obj, int &error)
+{
+ MRN_DBUG_ENTER_METHOD();
+ LEX *lex = ha_thd()->lex;
+ Alter_info *alter_info = &lex->alter_info;
+ List_iterator<Key> key_iterator(alter_info->key_list);
+ Key *key;
+ char ref_db_buff[NAME_LEN + 1], ref_table_buff[NAME_LEN + 1];
+ while ((key = key_iterator++))
+ {
+ if (key->type != Key::FOREIGN_KEY)
+ {
+ continue;
+ }
+ if (key->columns.elements > 1)
+ {
+ error = ER_CANT_CREATE_TABLE;
+ my_message(error, "mroonga can't use FOREIGN_KEY with multiple columns",
+ MYF(0));
+ DBUG_RETURN(false);
+ }
+ List_iterator<Key_part_spec> key_part_col_iterator(key->columns);
+ Key_part_spec *key_part_col = key_part_col_iterator++;
+ LEX_STRING field_name = key_part_col->field_name;
+ DBUG_PRINT("info", ("mroonga: field_name=%s", field_name.str));
+ DBUG_PRINT("info", ("mroonga: field->field_name=%s", field->field_name));
+ if (strcmp(field->field_name, field_name.str))
+ {
+ continue;
+ }
+ Foreign_key *fk = (Foreign_key *) key;
+ List_iterator<Key_part_spec> key_part_ref_col_iterator(fk->ref_columns);
+ Key_part_spec *key_part_ref_col = key_part_ref_col_iterator++;
+ LEX_STRING ref_field_name = key_part_ref_col->field_name;
+ DBUG_PRINT("info", ("mroonga: ref_field_name=%s", ref_field_name.str));
+ LEX_STRING ref_db_name = fk->ref_db;
+ DBUG_PRINT("info", ("mroonga: ref_db_name=%s", ref_db_name.str));
+ if (ref_db_name.str && lower_case_table_names) {
+ strmake(ref_db_buff, ref_db_name.str, sizeof(ref_db_buff) - 1);
+ my_casedn_str(system_charset_info, ref_db_buff);
+ ref_db_name.str = ref_db_buff;
+ DBUG_PRINT("info", ("mroonga: casedn ref_db_name=%s", ref_db_name.str));
+ }
+ LEX_STRING ref_table_name = fk->ref_table;
+ DBUG_PRINT("info", ("mroonga: ref_table_name=%s", ref_table_name.str));
+ if (ref_table_name.str && lower_case_table_names) {
+ strmake(ref_table_buff, ref_table_name.str, sizeof(ref_table_buff) - 1);
+ my_casedn_str(system_charset_info, ref_table_buff);
+ ref_table_name.str = ref_table_buff;
+ DBUG_PRINT("info", ("mroonga: casedn ref_table_name=%s", ref_table_name.str));
+ }
+ if (ref_db_name.str && strcmp(table->s->db.str, ref_db_name.str))
+ {
+ error = ER_CANT_CREATE_TABLE;
+ my_message(error,
+ "mroonga can't use FOREIGN_KEY during different database tables",
+ MYF(0));
+ DBUG_RETURN(false);
+ }
+
+ grn_obj *column, *column_ref = NULL, *grn_table_ref = NULL;
+ char ref_path[FN_REFLEN + 1];
+ TABLE_LIST table_list;
+ TABLE_SHARE *tmp_ref_table_share;
+ build_table_filename(ref_path, sizeof(ref_path) - 1,
+ table->s->db.str, ref_table_name.str, "", 0);
+
+ DBUG_PRINT("info", ("mroonga: ref_path=%s", ref_path));
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(false);
+ mrn::PathMapper mapper(ref_path);
+ grn_table_ref = grn_ctx_get(ctx, mapper.table_name(),
+ strlen(mapper.table_name()));
+ if (!grn_table_ref) {
+ error = ER_CANT_CREATE_TABLE;
+ char err_msg[MRN_BUFFER_SIZE];
+ sprintf(err_msg, "refference table [%s.%s] is not mroonga table",
+ table->s->db.str, ref_table_name.str);
+ my_message(error, err_msg, MYF(0));
+ DBUG_RETURN(false);
+ }
+
+#ifdef MRN_TABLE_LIST_INIT_REQUIRE_ALIAS
+ table_list.init_one_table(mapper.db_name(),
+ strlen(mapper.db_name()),
+ mapper.mysql_table_name(),
+ strlen(mapper.mysql_table_name()),
+ mapper.mysql_table_name(), TL_WRITE);
+#else
+ table_list.init_one_table(mapper.db_name(),
+ mapper.mysql_table_name(),
+ TL_WRITE);
+#endif
+ mrn_open_mutex_lock(table->s);
+ tmp_ref_table_share =
+ mrn_create_tmp_table_share(&table_list, ref_path, &error);
+ mrn_open_mutex_unlock(table->s);
+ if (!tmp_ref_table_share) {
+ grn_obj_unlink(ctx, grn_table_ref);
+ error = ER_CANT_CREATE_TABLE;
+ char err_msg[MRN_BUFFER_SIZE];
+ sprintf(err_msg, "refference table [%s.%s] is not found",
+ table->s->db.str, ref_table_name.str);
+ my_message(error, err_msg, MYF(0));
+ DBUG_RETURN(false);
+ }
+ uint ref_pkey_nr = tmp_ref_table_share->primary_key;
+ if (ref_pkey_nr == MAX_KEY) {
+ mrn_open_mutex_lock(table->s);
+ mrn_free_tmp_table_share(tmp_ref_table_share);
+ mrn_open_mutex_unlock(table->s);
+ grn_obj_unlink(ctx, grn_table_ref);
+ error = ER_CANT_CREATE_TABLE;
+ char err_msg[MRN_BUFFER_SIZE];
+ sprintf(err_msg, "refference table [%s.%s] has no primary key",
+ table->s->db.str, ref_table_name.str);
+ my_message(error, err_msg, MYF(0));
+ DBUG_RETURN(false);
+ }
+ KEY *ref_key_info = &tmp_ref_table_share->key_info[ref_pkey_nr];
+ uint ref_key_parts = KEY_N_KEY_PARTS(ref_key_info);
+ if (ref_key_parts > 1) {
+ mrn_open_mutex_lock(table->s);
+ mrn_free_tmp_table_share(tmp_ref_table_share);
+ mrn_open_mutex_unlock(table->s);
+ grn_obj_unlink(ctx, grn_table_ref);
+ error = ER_CANT_CREATE_TABLE;
+ char err_msg[MRN_BUFFER_SIZE];
+ sprintf(err_msg,
+ "refference table [%s.%s] primary key is multiple column",
+ table->s->db.str, ref_table_name.str);
+ my_message(error, err_msg, MYF(0));
+ DBUG_RETURN(false);
+ }
+ Field *ref_field = &ref_key_info->key_part->field[0];
+ if (strcmp(ref_field->field_name, ref_field_name.str)) {
+ mrn_open_mutex_lock(table->s);
+ mrn_free_tmp_table_share(tmp_ref_table_share);
+ mrn_open_mutex_unlock(table->s);
+ grn_obj_unlink(ctx, grn_table_ref);
+ error = ER_CANT_CREATE_TABLE;
+ char err_msg[MRN_BUFFER_SIZE];
+ sprintf(err_msg,
+ "refference column [%s.%s.%s] is not used for primary key",
+ table->s->db.str, ref_table_name.str, ref_field_name.str);
+ my_message(error, err_msg, MYF(0));
+ DBUG_RETURN(false);
+ }
+ mrn_open_mutex_lock(table->s);
+ mrn_free_tmp_table_share(tmp_ref_table_share);
+ mrn_open_mutex_unlock(table->s);
+ grn_obj_flags col_flags = GRN_OBJ_PERSISTENT;
+ column = grn_column_create(ctx, table_obj, field->field_name,
+ strlen(field->field_name),
+ NULL, col_flags, grn_table_ref);
+ if (ctx->rc) {
+ grn_obj_unlink(ctx, grn_table_ref);
+ error = ER_CANT_CREATE_TABLE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(false);
+ }
+
+ mrn::IndexColumnName index_column_name(grn_table_name, field->field_name);
+ grn_obj_flags ref_col_flags = GRN_OBJ_COLUMN_INDEX | GRN_OBJ_PERSISTENT;
+ column_ref = grn_column_create(ctx, grn_table_ref,
+ index_column_name.c_str(),
+ index_column_name.length(),
+ NULL, ref_col_flags, table_obj);
+ if (ctx->rc) {
+ grn_obj_unlink(ctx, column);
+ grn_obj_unlink(ctx, grn_table_ref);
+ error = ER_CANT_CREATE_TABLE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(false);
+ }
+
+ grn_obj source_ids;
+ grn_id source_id = grn_obj_id(ctx, column);
+ GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR);
+ GRN_UINT32_PUT(ctx, &source_ids, source_id);
+ if (error) {
+ grn_obj_unlink(ctx, &source_ids);
+ grn_obj_unlink(ctx, column_ref);
+ grn_obj_unlink(ctx, column);
+ grn_obj_unlink(ctx, grn_table_ref);
+ DBUG_RETURN(false);
+ }
+ grn_obj_set_info(ctx, column_ref, GRN_INFO_SOURCE, &source_ids);
+ grn_obj_unlink(ctx, &source_ids);
+ grn_obj_unlink(ctx, column_ref);
+ grn_obj_unlink(ctx, column);
+ grn_obj_unlink(ctx, grn_table_ref);
+ error = 0;
+ DBUG_RETURN(true);
+ }
+ error = 0;
+ DBUG_RETURN(false);
+}
+#endif
+
+int ha_mroonga::storage_create_validate_index(TABLE *table)
+{
+ int error = 0;
+ uint i;
+
+ MRN_DBUG_ENTER_METHOD();
+ /* checking if index is used for virtual columns */
+ uint n_keys = table->s->keys;
+ for (i = 0; i < n_keys; i++) {
+ KEY key_info = table->s->key_info[i];
+ // must be single column key
+ int key_parts = KEY_N_KEY_PARTS(&key_info);
+ if (key_parts != 1) {
+ continue;
+ }
+ Field *field = key_info.key_part[0].field;
+ const char *column_name = field->field_name;
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ if (key_info.algorithm == HA_KEY_ALG_HASH) {
+ continue; // hash index is ok
+ }
+ GRN_LOG(ctx, GRN_LOG_ERROR, "only hash index can be defined for _id");
+ error = ER_CANT_CREATE_TABLE;
+ my_message(error, "only hash index can be defined for _id", MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_create_index_table(TABLE *table,
+ const char *grn_table_name,
+ grn_obj *grn_table,
+ MRN_SHARE *tmp_share,
+ KEY *key_info,
+ grn_obj **index_tables,
+ uint i)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ grn_obj *index_type;
+ grn_obj *index_table;
+ grn_obj_flags index_table_flags = GRN_OBJ_PERSISTENT;
+ bool is_multiple_column_index = KEY_N_KEY_PARTS(key_info) > 1;
+
+ if (tmp_share->index_table && tmp_share->index_table[i]) {
+ index_table = grn_ctx_get(ctx,
+ tmp_share->index_table[i],
+ tmp_share->index_table_length[i]);
+ // TODO: add error check
+ index_tables[i] = index_table;
+ DBUG_RETURN(error);
+ }
+
+ if (is_multiple_column_index) {
+ index_type = grn_ctx_at(ctx, GRN_DB_SHORT_TEXT);
+ } else {
+ Field *field = key_info->key_part[0].field;
+ grn_builtin_type groonga_type = mrn_grn_type_from_field(ctx, field, true);
+ index_type = grn_ctx_at(ctx, groonga_type);
+ }
+ // TODO: Add NULL check for index_type
+
+ int key_alg = key_info->algorithm;
+ if (key_info->flags & HA_FULLTEXT) {
+ index_table_flags |= GRN_OBJ_TABLE_PAT_KEY;
+ error = mrn_change_encoding(ctx, key_info->key_part->field->charset());
+ if (error) {
+ grn_obj_remove(ctx, grn_table);
+ DBUG_RETURN(error);
+ }
+ } else if (key_alg == HA_KEY_ALG_HASH) {
+ index_table_flags |= GRN_OBJ_TABLE_HASH_KEY;
+ } else {
+ index_table_flags |= GRN_OBJ_TABLE_PAT_KEY;
+ }
+
+ {
+ mrn::IndexTableName index_table_name(grn_table_name, key_info->name);
+ index_table = grn_table_create(ctx,
+ index_table_name.c_str(),
+ index_table_name.length(),
+ NULL,
+ index_table_flags,
+ index_type,
+ NULL);
+ }
+ if (ctx->rc) {
+ grn_obj_unlink(ctx, index_type);
+ grn_obj_remove(ctx, grn_table);
+ error = ER_CANT_CREATE_TABLE;
+ my_message(ER_CANT_CREATE_TABLE, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+
+ if (key_info->flags & HA_FULLTEXT) {
+ grn_obj *tokenizer = find_tokenizer(tmp_share->key_parser[i],
+ tmp_share->key_parser_length[i]);
+ if (tokenizer) {
+ grn_info_type info_type = GRN_INFO_DEFAULT_TOKENIZER;
+ grn_obj_set_info(ctx, index_table, info_type, tokenizer);
+ grn_obj_unlink(ctx, tokenizer);
+ }
+ }
+
+ {
+ grn_obj *normalizer = NULL;
+ Field *field = &(key_info->key_part->field[0]);
+ if (key_info->flags & HA_FULLTEXT) {
+ if (should_normalize(field)) {
+ normalizer = find_normalizer(key_info);
+ }
+ } else if (key_alg != HA_KEY_ALG_HASH) {
+ if (!is_multiple_column_index && should_normalize(field)) {
+ normalizer = find_normalizer(key_info);
+ }
+ }
+ if (normalizer) {
+ grn_info_type info_type = GRN_INFO_NORMALIZER;
+ grn_obj_set_info(ctx, index_table, info_type, normalizer);
+ grn_obj_unlink(ctx, normalizer);
+ }
+ }
+
+ index_tables[i] = index_table;
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_create_index(TABLE *table, const char *grn_table_name,
+ grn_obj *grn_table, MRN_SHARE *tmp_share,
+ KEY *key_info, grn_obj **index_tables,
+ grn_obj **index_columns, uint i)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ grn_obj *index_table, *index_column;
+ const char *column_name = NULL;
+ int column_name_size = 0;
+
+ bool is_multiple_column_index = KEY_N_KEY_PARTS(key_info) > 1;
+ if (!is_multiple_column_index) {
+ Field *field = key_info->key_part[0].field;
+ column_name = field->field_name;
+ column_name_size = strlen(column_name);
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ // skipping _id virtual column
+ DBUG_RETURN(0);
+ }
+ }
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ error = storage_create_index_table(table, grn_table_name,
+ grn_table, tmp_share,
+ key_info, index_tables, i);
+ if (error)
+ DBUG_RETURN(error);
+
+ grn_obj_flags index_column_flags =
+ GRN_OBJ_COLUMN_INDEX | GRN_OBJ_WITH_POSITION | GRN_OBJ_PERSISTENT;
+ if (is_multiple_column_index) {
+ index_column_flags |= GRN_OBJ_WITH_SECTION;
+ }
+
+ index_table = index_tables[i];
+ const char *index_column_name;
+ if (tmp_share->index_table && tmp_share->index_table[i]) {
+ index_column_name = key_info->name;
+ } else {
+ index_column_name = INDEX_COLUMN_NAME;
+ }
+ index_column = grn_column_create(ctx,
+ index_table,
+ index_column_name,
+ strlen(index_column_name),
+ NULL,
+ index_column_flags,
+ grn_table);
+
+ if (ctx->rc) {
+ grn_obj_remove(ctx, index_table);
+ error = ER_CANT_CREATE_TABLE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+
+ mrn_change_encoding(ctx, system_charset_info);
+ if (is_multiple_column_index) {
+ if (key_info->flags & HA_FULLTEXT) {
+ grn_obj source_ids;
+ GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR);
+
+ int j, n_key_parts = KEY_N_KEY_PARTS(key_info);
+ for (j = 0; j < n_key_parts; j++) {
+ Field *field = key_info->key_part[j].field;
+ const char *column_name = field->field_name;
+ int column_name_size = strlen(column_name);
+ grn_obj *source_column = grn_obj_column(ctx, grn_table,
+ column_name, column_name_size);
+ grn_id source_id = grn_obj_id(ctx, source_column);
+ GRN_UINT32_PUT(ctx, &source_ids, source_id);
+ grn_obj_unlink(ctx, source_column);
+ }
+ mrn_change_encoding(ctx, key_info->key_part->field->charset());
+ grn_obj_set_info(ctx, index_column, GRN_INFO_SOURCE, &source_ids);
+ grn_obj_unlink(ctx, &source_ids);
+ }
+ } else {
+ grn_obj *column;
+ column = grn_obj_column(ctx, grn_table, column_name, column_name_size);
+ if (column) {
+ grn_obj source_ids;
+ grn_id source_id = grn_obj_id(ctx, column);
+ GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR);
+ GRN_UINT32_PUT(ctx, &source_ids, source_id);
+ mrn_change_encoding(ctx, key_info->key_part->field->charset());
+ grn_obj_set_info(ctx, index_column, GRN_INFO_SOURCE, &source_ids);
+ grn_obj_unlink(ctx, &source_ids);
+ grn_obj_unlink(ctx, column);
+ }
+ }
+ mrn_change_encoding(ctx, system_charset_info);
+
+ if (index_columns) {
+ index_columns[i] = index_column;
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_create_indexes(TABLE *table, const char *grn_table_name,
+ grn_obj *grn_table, MRN_SHARE *tmp_share)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+
+ uint n_keys = table->s->keys;
+ uint i;
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, n_keys);
+ for (i = 0; i < n_keys; i++) {
+ index_tables[i] = NULL;
+ if (i == table->s->primary_key) {
+ continue; // pkey is already handled
+ }
+ KEY *key_info = &table->s->key_info[i];
+ if (tmp_share->disable_keys && !(key_info->flags & HA_NOSAME)) {
+ continue; // key is disabled
+ }
+ if ((error = storage_create_index(table, grn_table_name, grn_table,
+ tmp_share, key_info,
+ index_tables, NULL, i))) {
+ break;
+ }
+ }
+ if (error) {
+ while (true) {
+ if (index_tables[i]) {
+ grn_obj_remove(ctx, index_tables[i]);
+ }
+ if (!i)
+ break;
+ i--;
+ }
+ }
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::close_databases()
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+ mrn::Lock lock(&mrn_db_mutex);
+
+ grn_hash_cursor *hash_cursor;
+ hash_cursor = grn_hash_cursor_open(&mrn_ctx, mrn_hash,
+ NULL, 0, NULL, 0,
+ 0, -1, 0);
+ if (mrn_ctx.rc) {
+ my_message(ER_ERROR_ON_READ, mrn_ctx.errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_READ);
+ }
+
+ while (grn_hash_cursor_next(&mrn_ctx, hash_cursor) != GRN_ID_NIL) {
+ if (mrn_ctx.rc) {
+ error = ER_ERROR_ON_READ;
+ my_message(error, mrn_ctx.errbuf, MYF(0));
+ break;
+ }
+ void *value;
+ grn_obj *db;
+ grn_hash_cursor_get_value(&mrn_ctx, hash_cursor, &value);
+ memcpy(&db, value, sizeof(grn_obj *));
+ grn_rc rc = grn_hash_cursor_delete(&mrn_ctx, hash_cursor, NULL);
+ if (rc)
+ {
+ error = ER_ERROR_ON_READ;
+ my_message(error, mrn_ctx.errbuf, MYF(0));
+ break;
+ }
+ grn_obj_close(&mrn_ctx, db);
+ }
+ grn_hash_cursor_close(&mrn_ctx, hash_cursor);
+
+ DBUG_RETURN(error);
+}
+
+void ha_mroonga::ensure_database_directory()
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ const char *path_prefix = mrn::PathMapper::default_path_prefix;
+ if (!path_prefix)
+ DBUG_VOID_RETURN;
+
+ const char *last_path_separator;
+ last_path_separator = strrchr(path_prefix, FN_LIBCHAR);
+ if (!last_path_separator)
+ last_path_separator = strrchr(path_prefix, FN_LIBCHAR2);
+ if (!last_path_separator)
+ DBUG_VOID_RETURN;
+ if (path_prefix == last_path_separator)
+ DBUG_VOID_RETURN;
+
+ char database_directory[MRN_MAX_PATH_SIZE];
+ size_t database_directory_length = last_path_separator - path_prefix;
+ strncpy(database_directory, path_prefix, database_directory_length);
+ database_directory[database_directory_length] = '\0';
+ mkdir_p(database_directory);
+
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::ensure_normalizers_register()
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+#ifdef WITH_GROONGA_NORMALIZER_MYSQL
+ {
+ grn_obj *mysql_normalizer;
+ mysql_normalizer = grn_ctx_get(ctx, "NormalizerMySQLGeneralCI", -1);
+ if (mysql_normalizer) {
+ grn_obj_unlink(ctx, mysql_normalizer);
+ } else {
+#ifdef GROONGA_NORMALIZER_MYSQL_PLUGIN_IS_BUNDLED_STATIC
+ char ref_path[FN_REFLEN + 1], *tmp;
+ tmp = strmov(ref_path, opt_plugin_dir);
+ tmp = strmov(tmp, "/ha_mroonga");
+ strcpy(tmp, SO_EXT);
+ grn_plugin_register_by_path(ctx, ref_path);
+#else
+ grn_plugin_register(ctx, GROONGA_NORMALIZER_MYSQL_PLUGIN_NAME);
+#endif
+ }
+ }
+#endif
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::ensure_database_create(const char *name)
+{
+ int error;
+
+ MRN_DBUG_ENTER_METHOD();
+ /* before creating table, we must check if database is alreadly opened, created */
+ grn_obj *db;
+ struct stat db_stat;
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ mrn::PathMapper mapper(name);
+ {
+ mrn::Lock lock(&mrn_db_mutex);
+ if (!mrn_hash_get(&mrn_ctx, mrn_hash, mapper.db_name(), &db)) {
+ if (stat(mapper.db_path(), &db_stat)) {
+ // creating new database
+ GRN_LOG(ctx, GRN_LOG_INFO,
+ "database not found. creating...(%s)", mapper.db_path());
+ if (name[0] == FN_CURLIB &&
+ (name[1] == FN_LIBCHAR || name[1] == FN_LIBCHAR2)) {
+ ensure_database_directory();
+ }
+ db = grn_db_create(&mrn_ctx, mapper.db_path(), NULL);
+ if (mrn_ctx.rc) {
+ error = ER_CANT_CREATE_TABLE;
+ my_message(error, mrn_ctx.errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ } else {
+ // opening existing database
+ db = grn_db_open(&mrn_ctx, mapper.db_path());
+ if (mrn_ctx.rc) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, mrn_ctx.errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+ mrn_hash_put(&mrn_ctx, mrn_hash, mapper.db_name(), db);
+ }
+ }
+ grn_ctx_use(ctx, db);
+ error = ensure_normalizers_register();
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::ensure_database_open(const char *name)
+{
+ int error;
+
+ MRN_DBUG_ENTER_METHOD();
+ grn_obj *db;
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ mrn::PathMapper mapper(name);
+ {
+ mrn::Lock lock(&mrn_db_mutex);
+ if (!mrn_hash_get(&mrn_ctx, mrn_hash, mapper.db_name(), &db)) {
+ db = grn_db_open(&mrn_ctx, mapper.db_path());
+ if (mrn_ctx.rc) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, mrn_ctx.errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ mrn_hash_put(&mrn_ctx, mrn_hash, mapper.db_name(), db);
+ }
+ }
+ grn_ctx_use(ctx, db);
+ error = ensure_normalizers_register();
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::ensure_database_remove(const char *name)
+{
+ int error;
+
+ MRN_DBUG_ENTER_METHOD();
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ mrn::PathMapper mapper(name);
+ {
+ mrn::Lock lock(&mrn_db_mutex);
+ grn_obj *db;
+ if (mrn_hash_get(&mrn_ctx, mrn_hash, mapper.db_name(), &db)) {
+ mrn_hash_remove(&mrn_ctx, mrn_hash, mapper.db_name());
+ grn_obj_close(&mrn_ctx, db);
+ }
+ }
+ remove_related_files(mapper.db_path());
+
+ DBUG_RETURN(error);
+}
+
+
+int ha_mroonga::create(const char *name, TABLE *table, HA_CREATE_INFO *info)
+{
+ int error = 0;
+ MRN_SHARE *tmp_share;
+ MRN_DBUG_ENTER_METHOD();
+ /* checking data type of virtual columns */
+
+ if (!(tmp_share = mrn_get_share(name, table, &error)))
+ DBUG_RETURN(error);
+
+ st_mrn_slot_data *slot_data = mrn_get_slot_data(ha_thd(), false);
+ if (slot_data && slot_data->disable_keys_create_info == info) {
+ tmp_share->disable_keys = true;
+ }
+
+ if (tmp_share->wrapper_mode)
+ {
+ error = wrapper_create(name, table, info, tmp_share);
+ } else {
+ error = storage_create(name, table, info, tmp_share);
+ }
+
+ if (error) {
+ mrn_free_long_term_share(tmp_share->long_term_share);
+ tmp_share->long_term_share = NULL;
+ }
+ mrn_free_share(tmp_share);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_open(const char *name, int mode, uint test_if_locked)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+
+ if (thd_sql_command(ha_thd()) == SQLCOM_REPAIR) {
+ error = ensure_database_remove(name);
+ if (error)
+ DBUG_RETURN(error);
+ error = ensure_database_create(name);
+ if (error)
+ DBUG_RETURN(error);
+ grn_table = NULL;
+ grn_index_tables = NULL;
+ grn_index_columns = NULL;
+ } else {
+ error = ensure_database_open(name);
+ if (error)
+ DBUG_RETURN(error);
+
+ error = open_table(name);
+ if (error)
+ DBUG_RETURN(error);
+
+ error = wrapper_open_indexes(name);
+ if (error) {
+ grn_obj_unlink(ctx, grn_table);
+ grn_table = NULL;
+ DBUG_RETURN(error);
+ }
+ }
+
+ init_alloc_root(&mem_root, 1024, 0, MYF(0));
+ wrap_key_info = mrn_create_key_info_for_table(share, table, &error);
+ if (error)
+ DBUG_RETURN(error);
+ base_key_info = table->key_info;
+
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (!is_clone)
+ {
+ if (!(wrap_handler =
+ share->hton->create(share->hton, table->s, &mem_root)))
+ {
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ if (wrap_key_info)
+ {
+ my_free(wrap_key_info, MYF(0));
+ wrap_key_info = NULL;
+ }
+ base_key_info = NULL;
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+ wrap_handler->init();
+#ifdef MRN_HANDLER_HAVE_SET_HA_SHARE_REF
+ wrap_handler->set_ha_share_ref(&table->s->ha_share);
+#endif
+ error = wrap_handler->ha_open(table, name, mode, test_if_locked);
+ } else {
+#ifdef MRN_HANDLER_CLONE_NEED_NAME
+ if (!(wrap_handler = parent_for_clone->wrap_handler->clone(name,
+ mem_root_for_clone)))
+#else
+ if (!(wrap_handler = parent_for_clone->wrap_handler->clone(
+ mem_root_for_clone)))
+#endif
+ {
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ if (wrap_key_info)
+ {
+ my_free(wrap_key_info, MYF(0));
+ wrap_key_info = NULL;
+ }
+ base_key_info = NULL;
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+ }
+ ref_length = wrap_handler->ref_length;
+ key_used_on_scan = wrap_handler->key_used_on_scan;
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ init();
+ wrapper_overwrite_index_bits();
+ wrapper_set_keys_in_use();
+
+ pk_keypart_map = make_prev_keypart_map(
+ KEY_N_KEY_PARTS(&(table->key_info[table_share->primary_key])));
+
+ if (error)
+ {
+ grn_obj_unlink(ctx, grn_table);
+ grn_table = NULL;
+ // TODO: free indexes.
+
+ delete wrap_handler;
+ wrap_handler = NULL;
+ if (wrap_key_info)
+ {
+ my_free(wrap_key_info, MYF(0));
+ wrap_key_info = NULL;
+ }
+ base_key_info = NULL;
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_open_indexes(const char *name)
+{
+ int error;
+
+ MRN_DBUG_ENTER_METHOD();
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ uint n_keys = table->s->keys;
+ uint n_primary_keys = table->s->primary_key;
+ if (n_keys > 0) {
+ // TODO: reduce allocate memories. We only need just
+ // for HA_KEY_ALG_FULLTEXT keys.
+ grn_index_tables = (grn_obj **)malloc(sizeof(grn_obj *) * n_keys);
+ grn_index_columns = (grn_obj **)malloc(sizeof(grn_obj *) * n_keys);
+ } else {
+ grn_index_tables = grn_index_columns = NULL;
+ }
+
+ mrn::PathMapper mapper(name);
+ uint i = 0;
+ for (i = 0; i < n_keys; i++) {
+ KEY key_info = table->s->key_info[i];
+
+ grn_index_tables[i] = NULL;
+ grn_index_columns[i] = NULL;
+
+ if (!(wrapper_is_target_index(&key_info))) {
+ continue;
+ }
+
+ if (i == n_primary_keys) {
+ continue;
+ }
+
+ mrn::IndexTableName index_table_name(mapper.table_name(), key_info.name);
+ grn_index_tables[i] = grn_ctx_get(ctx,
+ index_table_name.c_str(),
+ index_table_name.length());
+ if (ctx->rc) {
+ DBUG_PRINT("info",
+ ("mroonga: sql_command=%u", thd_sql_command(ha_thd())));
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ goto error;
+ }
+
+ grn_index_columns[i] = grn_obj_column(ctx, grn_index_tables[i],
+ INDEX_COLUMN_NAME,
+ strlen(INDEX_COLUMN_NAME));
+ if (!grn_index_columns[i]) {
+ /* just for backward compatibility before 1.0. */
+ Field *field = key_info.key_part[0].field;
+ grn_index_columns[i] = grn_obj_column(ctx, grn_index_tables[i],
+ field->field_name,
+ strlen(field->field_name));
+ }
+
+ if (ctx->rc) {
+ DBUG_PRINT("info",
+ ("mroonga: sql_command=%u", thd_sql_command(ha_thd())));
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ grn_obj_unlink(ctx, grn_index_tables[i]);
+ goto error;
+ }
+ }
+
+ grn_bulk_space(ctx, &key_buffer, table->key_info->key_length);
+
+error:
+ if (error) {
+ while (i-- > 0) {
+ grn_obj *index_column = grn_index_columns[i];
+ if (index_column) {
+ grn_obj_unlink(ctx, index_column);
+ }
+ grn_obj *index_table = grn_index_tables[i];
+ if (index_table) {
+ grn_obj_unlink(ctx, index_table);
+ }
+ }
+ free(grn_index_columns);
+ free(grn_index_tables);
+ grn_index_columns = NULL;
+ grn_index_tables = NULL;
+ }
+
+ DBUG_RETURN(error);
+}
+
+void ha_mroonga::wrapper_overwrite_index_bits()
+{
+ uint i, j;
+ longlong table_option = table_flags();
+ MRN_DBUG_ENTER_METHOD();
+ table_share->keys_for_keyread.clear_all();
+ for (i = 0; i < table_share->fields; i++)
+ {
+ Field *field = table_share->field[i];
+ field->part_of_key.clear_all();
+ field->part_of_key_not_clustered.clear_all();
+ field->part_of_sortkey.clear_all();
+ }
+ for (i = 0; i < table_share->keys; i++) {
+ KEY *key_info = &table->s->key_info[i];
+ KEY_PART_INFO *key_part = key_info->key_part;
+ for (j = 0 ; j < KEY_N_KEY_PARTS(key_info); key_part++, j++)
+ {
+ Field *field = key_part->field;
+ if (field->key_length() == key_part->length &&
+ !(field->flags & BLOB_FLAG))
+ {
+ if (index_flags(i, j, 0) & HA_KEYREAD_ONLY)
+ {
+ table_share->keys_for_keyread.set_bit(i);
+ field->part_of_key.set_bit(i);
+ field->part_of_key_not_clustered.set_bit(i);
+ }
+ if (index_flags(i, j, 1) & HA_READ_ORDER)
+ field->part_of_sortkey.set_bit(i);
+ }
+ if (i == table_share->primary_key &&
+ (table_option & HA_PRIMARY_KEY_IN_READ_INDEX))
+ {
+ if (field->key_length() == key_part->length &&
+ !(field->flags & BLOB_FLAG))
+ field->part_of_key = table_share->keys_in_use;
+ if (field->part_of_sortkey.is_set(i))
+ field->part_of_sortkey = table_share->keys_in_use;
+ }
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::storage_open(const char *name, int mode, uint test_if_locked)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+
+ error = ensure_database_open(name);
+ if (error)
+ DBUG_RETURN(error);
+
+ error = open_table(name);
+ if (error)
+ DBUG_RETURN(error);
+
+ error = storage_open_columns();
+ if (error) {
+ grn_obj_unlink(ctx, grn_table);
+ grn_table = NULL;
+ DBUG_RETURN(error);
+ }
+
+ error = storage_open_indexes(name);
+ if (error) {
+ // TODO: free grn_columns and set NULL;
+ grn_obj_unlink(ctx, grn_table);
+ grn_table = NULL;
+ DBUG_RETURN(error);
+ }
+
+ storage_set_keys_in_use();
+
+ ref_length = sizeof(grn_id);
+ DBUG_RETURN(0);
+}
+
+void ha_mroonga::update_grn_table_is_referenced()
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ grn_table_is_referenced = false;
+
+ grn_table_cursor *cursor;
+ int flags = GRN_CURSOR_BY_ID | GRN_CURSOR_ASCENDING;;
+ cursor = grn_table_cursor_open(ctx, grn_ctx_db(ctx),
+ NULL, 0,
+ NULL, 0,
+ 0, -1, flags);
+ if (cursor) {
+ grn_id id;
+ grn_id grn_table_id;
+
+ grn_table_id = grn_obj_id(ctx, grn_table);
+ while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) {
+ grn_obj *object;
+ grn_id range = GRN_ID_NIL;
+
+ object = grn_ctx_at(ctx, id);
+ if (!object) {
+ ctx->rc = GRN_SUCCESS;
+ continue;
+ }
+
+ switch (object->header.type) {
+ case GRN_COLUMN_FIX_SIZE:
+ case GRN_COLUMN_VAR_SIZE:
+ range = grn_obj_get_range(ctx, object);
+ break;
+ default:
+ break;
+ }
+ grn_obj_unlink(ctx, object);
+
+ if (range == grn_table_id) {
+ grn_table_is_referenced = true;
+ break;
+ }
+ }
+
+ grn_table_cursor_close(ctx, cursor);
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::open_table(const char *name)
+{
+ int error;
+ MRN_DBUG_ENTER_METHOD();
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ mrn::PathMapper mapper(name);
+ grn_table = grn_ctx_get(ctx, mapper.table_name(), strlen(mapper.table_name()));
+ if (ctx->rc) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ if (!grn_table) {
+ error = ER_CANT_OPEN_FILE;
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "mroonga: failed to open table: <%s>",
+ mapper.table_name());
+ my_message(error, error_message, MYF(0));
+ DBUG_RETURN(error);
+ }
+
+ update_grn_table_is_referenced();
+
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::storage_open_columns(void)
+{
+ int error;
+ MRN_DBUG_ENTER_METHOD();
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ int n_columns = table->s->fields;
+ grn_columns = (grn_obj **)malloc(sizeof(grn_obj *) * n_columns);
+ grn_column_ranges = (grn_obj **)malloc(sizeof(grn_obj *) * n_columns);
+ if (table_share->blob_fields)
+ {
+ if (blob_buffers)
+ {
+ delete [] blob_buffers;
+ }
+ if (!(blob_buffers = new String[n_columns]))
+ {
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+ }
+
+ int i;
+ for (i = 0; i < n_columns; i++) {
+ Field *field = table->field[i];
+ const char *column_name = field->field_name;
+ int column_name_size = strlen(column_name);
+ if (table_share->blob_fields)
+ {
+ blob_buffers[i].set_charset(field->charset());
+ }
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ grn_columns[i] = NULL;
+ grn_column_ranges[i] = NULL;
+ continue;
+ }
+
+ grn_columns[i] = grn_obj_column(ctx, grn_table,
+ column_name, column_name_size);
+ grn_id range_id = grn_obj_get_range(ctx, grn_columns[i]);
+ grn_column_ranges[i] = grn_ctx_at(ctx, range_id);
+ if (ctx->rc) {
+ // TODO: free grn_columns and set NULL;
+ int error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::storage_open_indexes(const char *name)
+{
+ int error;
+
+ MRN_DBUG_ENTER_METHOD();
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ uint n_keys = table->s->keys;
+ uint pkey_nr = table->s->primary_key;
+ if (n_keys > 0) {
+ grn_index_tables = (grn_obj **)malloc(sizeof(grn_obj *) * n_keys);
+ grn_index_columns = (grn_obj **)malloc(sizeof(grn_obj *) * n_keys);
+ key_id = (grn_id *)malloc(sizeof(grn_id) * n_keys);
+ del_key_id = (grn_id *)malloc(sizeof(grn_id) * n_keys);
+ } else {
+ grn_index_tables = grn_index_columns = NULL;
+ key_id = NULL;
+ del_key_id = NULL;
+ }
+
+ mrn::PathMapper mapper(name);
+ uint i, j;
+ for (i = 0; i < n_keys; i++) {
+ if (i == pkey_nr) {
+ grn_index_tables[i] = grn_index_columns[i] = NULL;
+ continue;
+ }
+
+ KEY key_info = table->s->key_info[i];
+ if (KEY_N_KEY_PARTS(&key_info) > 1) {
+ KEY_PART_INFO *key_part = key_info.key_part;
+ for (j = 0; j < KEY_N_KEY_PARTS(&key_info); j++) {
+ bitmap_set_bit(&multiple_column_key_bitmap,
+ key_part[j].field->field_index);
+ }
+ }
+
+ MRN_SHARE *tmp_share;
+ tmp_share = mrn_get_share(name, table, &error);
+ if (tmp_share->index_table[i]) {
+ grn_index_tables[i] = grn_ctx_get(ctx,
+ tmp_share->index_table[i],
+ tmp_share->index_table_length[i]);
+ if (ctx->rc == GRN_SUCCESS) {
+ grn_index_columns[i] = grn_obj_column(ctx,
+ grn_index_tables[i],
+ key_info.name,
+ strlen(key_info.name));
+ }
+ } else {
+ mrn::IndexTableName index_table_name(mapper.table_name(), key_info.name);
+ grn_index_tables[i] = grn_ctx_get(ctx,
+ index_table_name.c_str(),
+ index_table_name.length());
+ if (ctx->rc == GRN_SUCCESS) {
+ grn_index_columns[i] = grn_obj_column(ctx,
+ grn_index_tables[i],
+ INDEX_COLUMN_NAME,
+ strlen(INDEX_COLUMN_NAME));
+ if (!grn_index_columns[i]) {
+ /* just for backward compatibility before 1.0. */
+ Field *field = key_info.key_part[0].field;
+ grn_index_columns[i] = grn_obj_column(ctx, grn_index_tables[i],
+ field->field_name,
+ strlen(field->field_name));
+ }
+ }
+ }
+ mrn_free_share(tmp_share);
+ if (ctx->rc) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ goto error;
+ }
+
+ if (ctx->rc) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ goto error;
+ }
+ }
+
+error:
+ if (error) {
+ if (i) {
+ while (true) {
+ grn_obj *index_column = grn_index_columns[i];
+ if (index_column) {
+ grn_obj_unlink(ctx, index_column);
+ }
+ grn_obj *index_table = grn_index_tables[i];
+ if (index_table) {
+ grn_obj_unlink(ctx, index_table);
+ }
+ if (!i)
+ break;
+ i--;
+ }
+ }
+ free(key_id);
+ free(del_key_id);
+ free(grn_index_columns);
+ free(grn_index_tables);
+ key_id = NULL;
+ del_key_id = NULL;
+ grn_index_columns = NULL;
+ grn_index_tables = NULL;
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::open(const char *name, int mode, uint test_if_locked)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+
+ if (!(share = mrn_get_share(name, table, &error)))
+ DBUG_RETURN(error);
+ thr_lock_data_init(&share->lock,&thr_lock_data,NULL);
+
+ if (bitmap_init(&multiple_column_key_bitmap, NULL, table->s->fields, false))
+ {
+ mrn_free_share(share);
+ share = NULL;
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+
+ if (share->wrapper_mode)
+ {
+ error = wrapper_open(name, mode, test_if_locked);
+ } else {
+ error = storage_open(name, mode, test_if_locked);
+ }
+
+ if (error)
+ {
+ bitmap_free(&multiple_column_key_bitmap);
+ mrn_free_share(share);
+ share = NULL;
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_close()
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+#ifdef MRN_HANDLER_HAVE_HA_CLOSE
+ error = wrap_handler->ha_close();
+#else
+ error = wrap_handler->close();
+#endif
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ delete wrap_handler;
+ wrap_handler = NULL;
+ if (wrap_key_info)
+ {
+ my_free(wrap_key_info, MYF(0));
+ wrap_key_info = NULL;
+ }
+ base_key_info = NULL;
+ free_root(&mem_root, MYF(0));
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_close()
+{
+ MRN_DBUG_ENTER_METHOD();
+ grn_obj_unlink(ctx, grn_table);
+ // TODO: unlink elements
+ free(grn_columns);
+ // TODO: unlink elements
+ free(grn_column_ranges);
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::close()
+{
+ int error = 0;
+ THD *thd = ha_thd();
+ MRN_DBUG_ENTER_METHOD();
+
+ clear_indexes();
+
+ if (share->wrapper_mode)
+ {
+ error = wrapper_close();
+ } else {
+ error = storage_close();
+ }
+
+ if (is_temporary_table_name(share->table_name)) {
+ TABLE_LIST table_list;
+ TABLE_SHARE *tmp_table_share;
+ int tmp_error;
+ /* no need to decode */
+ mrn::PathMapper mapper(share->table_name);
+#ifdef MRN_TABLE_LIST_INIT_REQUIRE_ALIAS
+ table_list.init_one_table(mapper.db_name(), strlen(mapper.db_name()),
+ mapper.mysql_table_name(),
+ strlen(mapper.mysql_table_name()),
+ mapper.mysql_table_name(),
+ TL_WRITE);
+#else
+ table_list.init_one_table(mapper.db_name(), mapper.mysql_table_name(),
+ TL_WRITE);
+#endif
+ mrn_open_mutex_lock(NULL);
+ tmp_table_share =
+ mrn_create_tmp_table_share(&table_list, share->table_name, &tmp_error);
+ mrn_open_mutex_unlock(NULL);
+ if (!tmp_table_share) {
+ error = tmp_error;
+ } else if ((tmp_error = alter_share_add(share->table_name,
+ tmp_table_share))) {
+ error = tmp_error;
+ mrn_open_mutex_lock(NULL);
+ mrn_free_tmp_table_share(tmp_table_share);
+ mrn_open_mutex_unlock(NULL);
+ }
+ }
+ bitmap_free(&multiple_column_key_bitmap);
+ mrn_free_share(share);
+ share = NULL;
+ is_clone = false;
+ if (
+ thd &&
+ thd_sql_command(thd) == SQLCOM_FLUSH
+ ) {
+ /* flush tables */
+ mrn::Lock lock(&mrn_open_tables_mutex);
+ if (!mrn_open_tables.records)
+ {
+ int tmp_error = close_databases();
+ if (tmp_error)
+ error = tmp_error;
+ }
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_delete_table(const char *name, MRN_SHARE *tmp_share,
+ const char *table_name)
+{
+ int error = 0;
+ handler *hnd;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(tmp_share, tmp_share->table_share);
+ if (!(hnd =
+ tmp_share->hton->create(tmp_share->hton, tmp_share->table_share,
+ current_thd->mem_root)))
+ {
+ MRN_SET_BASE_SHARE_KEY(tmp_share, tmp_share->table_share);
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+ hnd->init();
+ MRN_SET_BASE_SHARE_KEY(tmp_share, tmp_share->table_share);
+
+ if ((error = hnd->ha_delete_table(name)))
+ {
+ delete hnd;
+ DBUG_RETURN(error);
+ }
+
+ error = wrapper_delete_index(name, tmp_share, table_name);
+
+ delete hnd;
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_delete_index(const char *name, MRN_SHARE *tmp_share,
+ const char *table_name)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+
+ error = ensure_database_open(name);
+ if (error)
+ DBUG_RETURN(error);
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ TABLE_SHARE *tmp_table_share = tmp_share->table_share;
+
+ uint i;
+ for (i = 0; i < tmp_table_share->keys; i++) {
+ error = drop_index(tmp_share, i);
+ if (error) {
+ DBUG_RETURN(error);
+ }
+ }
+
+ grn_obj *table = grn_ctx_get(ctx, table_name, strlen(table_name));
+ if (!ctx->rc) {
+ grn_obj_remove(ctx, table);
+ }
+ if (ctx->rc) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_delete_table(const char *name, MRN_SHARE *tmp_share,
+ const char *table_name)
+{
+ int error = 0;
+ TABLE_SHARE *tmp_table_share = tmp_share->table_share;
+ MRN_DBUG_ENTER_METHOD();
+
+ error = ensure_database_open(name);
+ if (error)
+ DBUG_RETURN(error);
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ uint i;
+ for (i = 0; i < tmp_table_share->keys; i++) {
+ error = drop_index(tmp_share, i);
+ if (error) {
+ DBUG_RETURN(error);
+ }
+ }
+
+ grn_obj *table_obj = grn_ctx_get(ctx, table_name, strlen(table_name));
+ if (!ctx->rc) {
+ grn_obj_remove(ctx, table_obj);
+ }
+ if (ctx->rc) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::delete_table(const char *name)
+{
+ int error = 0;
+ THD *thd = ha_thd();
+ TABLE_LIST table_list;
+ TABLE_SHARE *tmp_table_share = NULL;
+ TABLE tmp_table;
+ MRN_SHARE *tmp_share;
+ st_mrn_alter_share *alter_share, *tmp_alter_share;
+ MRN_DBUG_ENTER_METHOD();
+ mrn::PathMapper mapper(name);
+ st_mrn_slot_data *slot_data = mrn_get_slot_data(thd, false);
+ if (slot_data && slot_data->first_alter_share)
+ {
+ tmp_alter_share = NULL;
+ alter_share = slot_data->first_alter_share;
+ while (alter_share)
+ {
+ if (!strcmp(alter_share->path, name))
+ {
+ /* found */
+ tmp_table_share = alter_share->alter_share;
+ if (tmp_alter_share)
+ tmp_alter_share->next = alter_share->next;
+ else
+ slot_data->first_alter_share = alter_share->next;
+ free(alter_share);
+ break;
+ }
+ tmp_alter_share = alter_share;
+ alter_share = alter_share->next;
+ }
+ }
+ if (!tmp_table_share)
+ {
+ mrn::PathMapper mapper(name);
+#ifdef MRN_TABLE_LIST_INIT_REQUIRE_ALIAS
+ table_list.init_one_table(mapper.db_name(), strlen(mapper.db_name()),
+ mapper.mysql_table_name(),
+ strlen(mapper.mysql_table_name()),
+ mapper.mysql_table_name(),
+ TL_WRITE);
+#else
+ table_list.init_one_table(mapper.db_name(), mapper.mysql_table_name(),
+ TL_WRITE);
+#endif
+ mrn_open_mutex_lock(NULL);
+ tmp_table_share = mrn_create_tmp_table_share(&table_list, name, &error);
+ mrn_open_mutex_unlock(NULL);
+ if (!tmp_table_share) {
+ DBUG_RETURN(error);
+ }
+ }
+ tmp_table.s = tmp_table_share;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ tmp_table.part_info = NULL;
+#endif
+ if (!(tmp_share = mrn_get_share(name, &tmp_table, &error)))
+ {
+ mrn_open_mutex_lock(NULL);
+ mrn_free_tmp_table_share(tmp_table_share);
+ mrn_open_mutex_unlock(NULL);
+ DBUG_RETURN(error);
+ }
+
+ if (tmp_share->wrapper_mode)
+ {
+ error = wrapper_delete_table(name, tmp_share, mapper.table_name());
+ } else {
+ error = storage_delete_table(name, tmp_share, mapper.table_name());
+ }
+
+ if (!error) {
+ mrn_free_long_term_share(tmp_share->long_term_share);
+ tmp_share->long_term_share = NULL;
+ }
+ mrn_free_share(tmp_share);
+ mrn_open_mutex_lock(NULL);
+ mrn_free_tmp_table_share(tmp_table_share);
+ mrn_open_mutex_unlock(NULL);
+ if (is_temporary_table_name(name)) {
+ mrn_drop_db(name);
+ }
+ DBUG_RETURN(error);
+}
+
+void ha_mroonga::wrapper_set_keys_in_use()
+{
+ uint i, j;
+ MRN_DBUG_ENTER_METHOD();
+ mrn::AutoIncrementValueLock lock_(table_share);
+ table_share->keys_in_use.set_prefix(table_share->keys);
+ share->disable_keys = false;
+ for (i = 0; i < table_share->keys; i++) {
+ j = share->wrap_key_nr[i];
+ if (j < MAX_KEY) {
+ if (!share->wrap_table_share->keys_in_use.is_set(j)) {
+ /* copy bitmap */
+ table_share->keys_in_use.clear_bit(i);
+ share->disable_keys = true;
+ }
+ } else {
+ if (!grn_index_tables || !grn_index_tables[i]) {
+ /* disabled */
+ table_share->keys_in_use.clear_bit(i);
+ share->disable_keys = true;
+ }
+ }
+ }
+ table_share->keys_for_keyread.set_prefix(table_share->keys);
+ table_share->keys_for_keyread.intersect(table_share->keys_in_use);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_set_keys_in_use()
+{
+ uint i;
+ MRN_DBUG_ENTER_METHOD();
+ mrn::AutoIncrementValueLock lock_(table_share);
+ table_share->keys_in_use.set_prefix(table_share->keys);
+ share->disable_keys = false;
+ for (i = 0; i < table_share->keys; i++) {
+ if (i == table_share->primary_key) {
+ continue;
+ }
+ if (!grn_index_tables[i]) {
+ /* disabled */
+ table_share->keys_in_use.clear_bit(i);
+ DBUG_PRINT("info", ("mroonga: key %u disabled", i));
+ share->disable_keys = true;
+ }
+ }
+ table_share->keys_for_keyread.set_prefix(table_share->keys);
+ table_share->keys_for_keyread.intersect(table_share->keys_in_use);
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::wrapper_info(uint flag)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->info(flag);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ if (flag & HA_STATUS_ERRKEY) {
+ errkey = wrap_handler->errkey;
+ memcpy(dup_ref, wrap_handler->dup_ref, wrap_handler->ref_length);
+ }
+ if (flag & HA_STATUS_TIME) {
+ stats.update_time = wrap_handler->stats.update_time;
+ }
+ if (flag & HA_STATUS_CONST) {
+ stats.max_data_file_length = wrap_handler->stats.max_data_file_length;
+ stats.create_time = wrap_handler->stats.create_time;
+ stats.block_size = wrap_handler->stats.block_size;
+ wrapper_set_keys_in_use();
+ }
+ if (flag & HA_STATUS_VARIABLE) {
+ stats.data_file_length = wrap_handler->stats.data_file_length;
+ stats.index_file_length = wrap_handler->stats.index_file_length;
+ stats.records = wrap_handler->stats.records;
+ stats.mean_rec_length = wrap_handler->stats.mean_rec_length;
+ stats.check_time = wrap_handler->stats.check_time;
+ }
+ if (flag & HA_STATUS_AUTO) {
+ stats.auto_increment_value = wrap_handler->stats.auto_increment_value;
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_info(uint flag)
+{
+ MRN_DBUG_ENTER_METHOD();
+ mrn_change_encoding(ctx, NULL);
+
+ if (flag & (HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK)) {
+ errkey = dup_key;
+ }
+
+ if ((flag & HA_STATUS_AUTO) && table->found_next_number_field) {
+ THD *thd = ha_thd();
+ ulonglong nb_reserved_values;
+ bool next_number_field_is_null = !table->next_number_field;
+ mrn::ExternalLock mrn_external_lock(ha_thd(), this,
+ mrn_lock_type == F_UNLCK ?
+ F_RDLCK : F_UNLCK);
+ if (mrn_external_lock.error()) {
+ DBUG_RETURN(mrn_external_lock.error());
+ }
+ if (next_number_field_is_null) {
+ table->next_number_field = table->found_next_number_field;
+ }
+ MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share;
+ {
+ mrn::Lock lock(&long_term_share->auto_inc_mutex);
+ unsigned long auto_increment_offset, auto_increment_increment;
+ MRN_THD_GET_AUTOINC(thd, &auto_increment_offset,
+ &auto_increment_increment);
+ storage_get_auto_increment(auto_increment_offset,
+ auto_increment_increment, 1,
+ &stats.auto_increment_value,
+ &nb_reserved_values);
+ }
+ if (next_number_field_is_null) {
+ table->next_number_field = NULL;
+ }
+ }
+
+ if (flag & HA_STATUS_CONST) {
+ storage_set_keys_in_use();
+ }
+
+ if (flag & HA_STATUS_VARIABLE) {
+ storage_info_variable();
+ }
+
+ DBUG_RETURN(0);
+}
+
+void ha_mroonga::storage_info_variable()
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ storage_info_variable_records();
+ storage_info_variable_data_file_length();
+
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_info_variable_records()
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ stats.records = grn_table_size(ctx, grn_table);
+
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_info_variable_data_file_length()
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ stats.data_file_length = 0;
+ stats.data_file_length += file_size(grn_obj_path(ctx, grn_table));
+ grn_hash *columns = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY);
+ grn_table_columns(ctx, grn_table, NULL, 0, (grn_obj *)columns);
+ /* grn_id id __attribute__((unused)); */
+ grn_id *column_id;
+ GRN_HASH_EACH(ctx, columns, id, &column_id, NULL, NULL, {
+ grn_obj *column = grn_ctx_at(ctx, *column_id);
+ stats.data_file_length += file_size(grn_obj_path(ctx, column));
+ grn_obj_unlink(ctx, column);
+ });
+ grn_hash_close(ctx, columns);
+
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::info(uint flag)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_info(flag);
+ } else {
+ error = storage_info(flag);
+ }
+ DBUG_RETURN(error);
+}
+
+uint ha_mroonga::wrapper_lock_count() const
+{
+ uint lock_count;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ lock_count = wrap_handler->lock_count();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(lock_count);
+}
+
+uint ha_mroonga::storage_lock_count() const
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(1);
+}
+
+uint ha_mroonga::lock_count() const
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_lock_count();
+ } else {
+ error = storage_lock_count();
+ }
+ DBUG_RETURN(error);
+}
+
+THR_LOCK_DATA **ha_mroonga::wrapper_store_lock(THD *thd, THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ to = wrap_handler->store_lock(thd, to, lock_type);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(to);
+}
+
+THR_LOCK_DATA **ha_mroonga::storage_store_lock(THD *thd, THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (lock_type != TL_IGNORE && thr_lock_data.type == TL_UNLOCK) {
+ if (!thd_in_lock_tables(thd)) {
+ if (lock_type == TL_READ_NO_INSERT) {
+ lock_type = TL_READ;
+ } else if (lock_type >= TL_WRITE_CONCURRENT_INSERT &&
+ lock_type <= TL_WRITE && !thd_tablespace_op(thd)) {
+ lock_type = TL_WRITE_ALLOW_WRITE;
+ }
+ }
+
+ thr_lock_data.type = lock_type;
+ }
+ *to++ = &thr_lock_data;
+ DBUG_RETURN(to);
+}
+
+THR_LOCK_DATA **ha_mroonga::store_lock(THD *thd, THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_PRINT("info", ("mroonga: lock_type=%s",
+ mrn_inspect_thr_lock_type(lock_type)));
+ if (share->wrapper_mode)
+ to = wrapper_store_lock(thd, to, lock_type);
+ else
+ to = storage_store_lock(thd, to, lock_type);
+ DBUG_RETURN(to);
+}
+
+int ha_mroonga::wrapper_external_lock(THD *thd, int lock_type)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->ha_external_lock(thd, lock_type);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_external_lock(THD *thd, int lock_type)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::external_lock(THD *thd, int lock_type)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ mrn_lock_type = lock_type;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_external_lock(thd, lock_type);
+ } else {
+ error = storage_external_lock(thd, lock_type);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_rnd_init(bool scan)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->ha_rnd_init(scan);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_rnd_init(bool scan)
+{
+ MRN_DBUG_ENTER_METHOD();
+ mrn_change_encoding(ctx, NULL);
+ cursor = grn_table_cursor_open(ctx, grn_table, NULL, 0, NULL, 0, 0, -1, 0);
+ if (ctx->rc) {
+ my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_READ);
+ }
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::rnd_init(bool scan)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_rnd_init(scan);
+ } else {
+ error = storage_rnd_init(scan);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_rnd_end()
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->ha_rnd_end();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_rnd_end()
+{
+ MRN_DBUG_ENTER_METHOD();
+ clear_cursor();
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::rnd_end()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_rnd_end();
+ } else {
+ error = storage_rnd_end();
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_rnd_next(uchar *buf)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+#ifdef MRN_HANDLER_HAVE_HA_RND_NEXT
+ error = wrap_handler->ha_rnd_next(buf);
+#else
+ error = wrap_handler->rnd_next(buf);
+#endif
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_rnd_next(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = storage_get_next_record(buf);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::rnd_next(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_rnd_next(buf);
+ } else {
+ error = storage_rnd_next(buf);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_rnd_pos(uchar *buf, uchar *pos)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+#ifdef MRN_HANDLER_HAVE_HA_RND_POS
+ error = wrap_handler->ha_rnd_pos(buf, pos);
+#else
+ error = wrap_handler->rnd_pos(buf, pos);
+#endif
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_rnd_pos(uchar *buf, uchar *pos)
+{
+ MRN_DBUG_ENTER_METHOD();
+ record_id = *((grn_id*) pos);
+ storage_store_fields(buf, record_id);
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::rnd_pos(uchar *buf, uchar *pos)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_rnd_pos(buf, pos);
+ } else {
+ error = storage_rnd_pos(buf, pos);
+ }
+ DBUG_RETURN(error);
+}
+
+void ha_mroonga::wrapper_position(const uchar *record)
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->ref = ref;
+ wrap_handler->position(record);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_position(const uchar *record)
+{
+ MRN_DBUG_ENTER_METHOD();
+ memcpy(ref, &record_id, sizeof(grn_id));
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::position(const uchar *record)
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ wrapper_position(record);
+ else
+ storage_position(record);
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::generic_extra(enum ha_extra_function operation)
+{
+ MRN_DBUG_ENTER_METHOD();
+ switch (operation) {
+ case HA_EXTRA_IGNORE_DUP_KEY:
+ ignoring_duplicated_key = true;
+ break;
+ case HA_EXTRA_NO_IGNORE_DUP_KEY:
+ ignoring_duplicated_key = false;
+ break;
+ case HA_EXTRA_WRITE_CAN_REPLACE:
+ replacing_ = true;
+ break;
+ case HA_EXTRA_WRITE_CANNOT_REPLACE:
+ replacing_ = false;
+ break;
+ case HA_EXTRA_INSERT_WITH_UPDATE:
+ inserting_with_update = true;
+ break;
+ case HA_EXTRA_KEYREAD:
+ ignoring_no_key_columns = true;
+ break;
+ case HA_EXTRA_NO_KEYREAD:
+ ignoring_no_key_columns = false;
+ break;
+ default:
+ break;
+ }
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::wrapper_extra(enum ha_extra_function operation)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->extra(operation);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_extra(enum ha_extra_function operation)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::extra(enum ha_extra_function operation)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_PRINT("info",
+ ("mroonga: this=%p; extra-operation=%s",
+ this, mrn_inspect_extra_function(operation)));
+ if (share->wrapper_mode) {
+ if ((error = wrapper_extra(operation)))
+ DBUG_RETURN(error);
+ } else {
+ if ((error = storage_extra(operation)))
+ DBUG_RETURN(error);
+ }
+ error = generic_extra(operation);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_extra_opt(enum ha_extra_function operation,
+ ulong cache_size)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->extra_opt(operation, cache_size);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_extra_opt(enum ha_extra_function operation,
+ ulong cache_size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::extra_opt(enum ha_extra_function operation, ulong cache_size)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ {
+ if ((error = wrapper_extra_opt(operation, cache_size)))
+ DBUG_RETURN(error);
+ } else {
+ if ((error = storage_extra_opt(operation, cache_size)))
+ DBUG_RETURN(error);
+ }
+ error = generic_extra(operation);
+ DBUG_RETURN(error);
+}
+
+bool ha_mroonga::wrapper_is_target_index(KEY *key_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool target_index =
+ (key_info->algorithm == HA_KEY_ALG_FULLTEXT) || mrn_is_geo_key(key_info);
+ DBUG_PRINT("info", ("mroonga: %s", target_index ? "true" : "false"));
+ DBUG_RETURN(target_index);
+}
+
+bool ha_mroonga::wrapper_have_target_index()
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ bool have_target_index = false;
+
+ uint i;
+ uint n_keys = table->s->keys;
+ for (i = 0; i < n_keys; i++) {
+ KEY key_info = table->key_info[i];
+
+ if (wrapper_is_target_index(&key_info)) {
+ have_target_index = true;
+ break;
+ }
+ }
+
+ DBUG_PRINT("info", ("mroonga: %s", have_target_index ? "true" : "false"));
+ DBUG_RETURN(have_target_index);
+}
+
+int ha_mroonga::wrapper_write_row(uchar *buf)
+{
+ int error = 0;
+ THD *thd = ha_thd();
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ tmp_disable_binlog(thd);
+ error = wrap_handler->ha_write_row(buf);
+ insert_id_for_cur_row = wrap_handler->insert_id_for_cur_row;
+ reenable_binlog(thd);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+
+ if (!error && wrapper_have_target_index()) {
+ error = wrapper_write_row_index(buf);
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_write_row_index(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+
+ if (is_dry_write()) {
+ DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__));
+ DBUG_RETURN(error);
+ }
+
+ mrn_change_encoding(ctx, NULL);
+ GRN_BULK_REWIND(&key_buffer);
+ grn_bulk_space(ctx, &key_buffer, table->key_info->key_length);
+ key_copy((uchar *)(GRN_TEXT_VALUE(&key_buffer)),
+ buf,
+ &(table->key_info[table_share->primary_key]),
+ table->key_info[table_share->primary_key].key_length);
+
+ int added;
+ grn_id record_id;
+ record_id = grn_table_add(ctx, grn_table,
+ GRN_TEXT_VALUE(&key_buffer),
+ GRN_TEXT_LEN(&key_buffer),
+ &added);
+ if (record_id == GRN_ID_NIL) {
+ DBUG_PRINT("info", ("mroonga: failed to add a new record into groonga"));
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "failed to add a new record into groonga: key=<%.*s>",
+ (int)GRN_TEXT_LEN(&key_buffer),
+ GRN_TEXT_VALUE(&key_buffer));
+ error = ER_ERROR_ON_WRITE;
+ push_warning(ha_thd(), Sql_condition::WARN_LEVEL_WARN, error,
+ error_message);
+ DBUG_RETURN(0);
+ }
+
+ mrn::DebugColumnAccess debug_column_access(table, table->read_set);
+ uint i;
+ uint n_keys = table->s->keys;
+ for (i = 0; i < n_keys; i++) {
+ KEY key_info = table->key_info[i];
+
+ if (!(wrapper_is_target_index(&key_info))) {
+ continue;
+ }
+
+ grn_obj *index_column = grn_index_columns[i];
+ if (!index_column) {
+ continue;
+ }
+
+ uint j;
+ for (j = 0; j < KEY_N_KEY_PARTS(&key_info); j++) {
+ Field *field = key_info.key_part[j].field;
+
+ if (field->is_null())
+ continue;
+
+ error = mrn_change_encoding(ctx, field->charset());
+ if (error)
+ goto err;
+ error = generic_store_bulk(field, &new_value_buffer);
+ if (error) {
+ my_message(error,
+ "mroonga: wrapper: "
+ "failed to get new value for updating index.",
+ MYF(0));
+ goto err;
+ }
+
+ grn_rc rc;
+ rc = grn_column_index_update(ctx, index_column, record_id, j + 1,
+ NULL, &new_value_buffer);
+ if (rc) {
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, ctx->errbuf, MYF(0));
+ goto err;
+ }
+ }
+ }
+err:
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_write_row(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+
+ if (is_dry_write()) {
+ DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__));
+ DBUG_RETURN(error);
+ }
+
+ THD *thd = ha_thd();
+ int i;
+ uint j;
+ int n_columns = table->s->fields;
+
+ if (table->next_number_field && buf == table->record[0])
+ {
+ if ((error = update_auto_increment()))
+ DBUG_RETURN(error);
+ }
+
+ mrn::DebugColumnAccess debug_column_access(table, table->read_set);
+ for (i = 0; i < n_columns; i++) {
+ Field *field = table->field[i];
+ const char *column_name = field->field_name;
+
+ if (field->is_null()) continue;
+
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, MRN_GET_ERR_MSG(WARN_DATA_TRUNCATED),
+ MRN_COLUMN_NAME_ID,
+ MRN_GET_CURRENT_ROW_FOR_WARNING(thd));
+ if (MRN_ABORT_ON_WARNING(thd)) {
+ DBUG_RETURN(ER_DATA_TOO_LONG);
+ }
+ }
+ }
+
+ char *pkey;
+ int pkey_size;
+ uint pkey_nr;
+ pkey_nr = table->s->primary_key;
+ GRN_BULK_REWIND(&key_buffer);
+ if (pkey_nr == MAX_INDEXES) {
+ pkey = NULL;
+ pkey_size = 0;
+ } else {
+ KEY key_info = table->key_info[pkey_nr];
+ if (KEY_N_KEY_PARTS(&key_info) == 1) {
+ Field *pkey_field = key_info.key_part[0].field;
+ error = mrn_change_encoding(ctx, pkey_field->charset());
+ if (error) {
+ DBUG_RETURN(error);
+ }
+ generic_store_bulk(pkey_field, &key_buffer);
+ pkey = GRN_TEXT_VALUE(&key_buffer);
+ pkey_size = GRN_TEXT_LEN(&key_buffer);
+ } else {
+ mrn_change_encoding(ctx, NULL);
+ uchar key[MRN_MAX_KEY_SIZE];
+ key_copy(key, buf, &key_info, key_info.key_length);
+ grn_bulk_space(ctx, &key_buffer, key_info.key_length);
+ pkey = GRN_TEXT_VALUE(&key_buffer);
+ storage_encode_multiple_column_key(&key_info,
+ key, key_info.key_length,
+ (uchar *)pkey, (uint *)&pkey_size);
+ }
+ }
+
+ int added;
+ record_id = grn_table_add(ctx, grn_table, pkey, pkey_size, &added);
+ if (ctx->rc) {
+ my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
+ }
+ if (!added) {
+ // duplicated error
+ error = HA_ERR_FOUND_DUPP_KEY;
+ memcpy(dup_ref, &record_id, sizeof(grn_id));
+ dup_key = pkey_nr;
+ if (!ignoring_duplicated_key) {
+ GRN_LOG(ctx, GRN_LOG_ERROR,
+ "duplicated id on insert: update primary key: <%.*s>",
+ pkey_size, pkey);
+ }
+ DBUG_RETURN(error);
+ }
+
+ if ((error = storage_write_row_unique_indexes(buf)))
+ {
+ goto err;
+ }
+
+ grn_obj colbuf;
+ GRN_VOID_INIT(&colbuf);
+ for (i = 0; i < n_columns; i++) {
+ Field *field = table->field[i];
+ const char *column_name = field->field_name;
+
+ if (field->is_null())
+ continue;
+
+#ifdef HAVE_SPATIAL
+ bool is_null_geometry_value =
+ field->real_type() == MYSQL_TYPE_GEOMETRY &&
+ static_cast<Field_geom *>(field)->get_length() == 0;
+ if (is_null_geometry_value) {
+ continue;
+ }
+#endif
+
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ continue;
+ }
+
+ error = mrn_change_encoding(ctx, field->charset());
+ if (error) {
+ grn_obj_unlink(ctx, &colbuf);
+ goto err;
+ }
+ error = generic_store_bulk(field, &colbuf);
+ if (error) {
+ grn_obj_unlink(ctx, &colbuf);
+ goto err;
+ }
+ if (added && is_grn_zero_column_value(grn_columns[i], &colbuf)) {
+ // WORKAROUND: groonga can't index newly added '0' value for
+ // fix size column. So we add non-'0' value first then add
+ // real '0' value again. It will be removed when groonga
+ // supports 'null' value.
+ char *bytes = GRN_BULK_HEAD(&colbuf);
+ bytes[0] = '\1';
+ grn_obj_set_value(ctx, grn_columns[i], record_id, &colbuf, GRN_OBJ_SET);
+ bytes[0] = '\0';
+ }
+ grn_obj_set_value(ctx, grn_columns[i], record_id, &colbuf, GRN_OBJ_SET);
+ if (ctx->rc) {
+ grn_obj_unlink(ctx, &colbuf);
+ my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0));
+ error = ER_ERROR_ON_WRITE;
+ goto err;
+ }
+ }
+ grn_obj_unlink(ctx, &colbuf);
+
+ error = storage_write_row_multiple_column_indexes(buf, record_id);
+ if (error) {
+ goto err;
+ }
+
+ // for UDF last_insert_grn_id()
+ st_mrn_slot_data *slot_data;
+ slot_data = mrn_get_slot_data(thd, true);
+ if (slot_data == NULL) {
+ error = HA_ERR_OUT_OF_MEM;
+ goto err;
+ }
+ slot_data->last_insert_record_id = record_id;
+
+ grn_db_touch(ctx, grn_ctx_db(ctx));
+
+ if (table->found_next_number_field &&
+ !table->s->next_number_keypart) {
+ Field_num *field = (Field_num *) table->found_next_number_field;
+ if (field->unsigned_flag || field->val_int() > 0) {
+ MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share;
+ ulonglong nr = (ulonglong) field->val_int();
+ if (!long_term_share->auto_inc_inited) {
+ storage_info(HA_STATUS_AUTO);
+ }
+ {
+ mrn::Lock lock(&long_term_share->auto_inc_mutex);
+ if (long_term_share->auto_inc_value <= nr) {
+ long_term_share->auto_inc_value = nr + 1;
+ DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu",
+ long_term_share->auto_inc_value));
+ }
+ }
+ }
+ }
+ DBUG_RETURN(0);
+
+err:
+ for (j = 0; j < table->s->keys; j++) {
+ if (j == pkey_nr) {
+ continue;
+ }
+ KEY *key_info = &table->key_info[j];
+ if (key_info->flags & HA_NOSAME) {
+ grn_table_delete_by_id(ctx, grn_index_tables[j], key_id[j]);
+ }
+ }
+ grn_table_delete_by_id(ctx, grn_table, record_id);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_write_row_multiple_column_index(uchar *buf,
+ grn_id record_id,
+ KEY *key_info,
+ grn_obj *index_column)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+
+ mrn_change_encoding(ctx, NULL);
+ GRN_BULK_REWIND(&key_buffer);
+ grn_bulk_space(ctx, &key_buffer, key_info->key_length);
+ key_copy((uchar *)(GRN_TEXT_VALUE(&key_buffer)),
+ buf,
+ key_info,
+ key_info->key_length);
+ GRN_BULK_REWIND(&encoded_key_buffer);
+ grn_bulk_space(ctx, &encoded_key_buffer, key_info->key_length);
+ uint encoded_key_length;
+ storage_encode_multiple_column_key(key_info,
+ (uchar *)(GRN_TEXT_VALUE(&key_buffer)),
+ key_info->key_length,
+ (uchar *)(GRN_TEXT_VALUE(&encoded_key_buffer)),
+ &encoded_key_length);
+ DBUG_PRINT("info", ("mroonga: key_length=%u", key_info->key_length));
+ DBUG_PRINT("info", ("mroonga: encoded_key_length=%u", encoded_key_length));
+ DBUG_ASSERT(key_info->key_length >= encoded_key_length);
+
+ grn_rc rc;
+ rc = grn_column_index_update(ctx, index_column, record_id, 1, NULL,
+ &encoded_key_buffer);
+ if (rc) {
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, ctx->errbuf, MYF(0));
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_write_row_multiple_column_indexes(uchar *buf,
+ grn_id record_id)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+
+ mrn::DebugColumnAccess debug_column_access(table, table->read_set);
+ uint i;
+ uint n_keys = table->s->keys;
+ for (i = 0; i < n_keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+
+ KEY key_info = table->key_info[i];
+
+ if (KEY_N_KEY_PARTS(&key_info) == 1 || (key_info.flags & HA_FULLTEXT)) {
+ continue;
+ }
+
+ grn_obj *index_column = grn_index_columns[i];
+ if (!index_column) {
+ continue;
+ }
+
+ if ((error = storage_write_row_multiple_column_index(buf,
+ record_id,
+ &key_info,
+ index_column)))
+ {
+ goto err;
+ }
+ }
+
+err:
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_write_row_unique_index(uchar *buf,
+ KEY *key_info,
+ grn_obj *index_table,
+ grn_id *key_id)
+{
+ char *ukey = NULL;
+ int error, ukey_size = 0;
+ MRN_DBUG_ENTER_METHOD();
+ GRN_BULK_REWIND(&key_buffer);
+ if (KEY_N_KEY_PARTS(key_info) == 1) {
+ Field *ukey_field = key_info->key_part[0].field;
+ error = mrn_change_encoding(ctx, ukey_field->charset());
+ if (error) {
+ DBUG_RETURN(error);
+ }
+ generic_store_bulk(ukey_field, &key_buffer);
+ ukey = GRN_TEXT_VALUE(&key_buffer);
+ ukey_size = GRN_TEXT_LEN(&key_buffer);
+ } else {
+ mrn_change_encoding(ctx, NULL);
+ uchar key[MRN_MAX_KEY_SIZE];
+ key_copy(key, buf, key_info, key_info->key_length);
+ grn_bulk_space(ctx, &key_buffer, key_info->key_length);
+ ukey = GRN_TEXT_VALUE(&key_buffer);
+ storage_encode_multiple_column_key(key_info,
+ key, key_info->key_length,
+ (uchar *)(ukey), (uint *)&ukey_size);
+ }
+
+ int added;
+ *key_id = grn_table_add(ctx, index_table, ukey, ukey_size, &added);
+ if (ctx->rc) {
+ my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
+ }
+ if (!added) {
+ // duplicated error
+ error = HA_ERR_FOUND_DUPP_KEY;
+ memcpy(dup_ref, key_id, sizeof(grn_id));
+ if (!ignoring_duplicated_key) {
+ GRN_LOG(ctx, GRN_LOG_ERROR,
+ "duplicated id on insert: update unique index: <%.*s>",
+ ukey_size, ukey);
+ }
+ DBUG_RETURN(error);
+ }
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::storage_write_row_unique_indexes(uchar *buf)
+{
+ int error = 0;
+ uint i;
+ uint n_keys = table->s->keys;
+ MRN_DBUG_ENTER_METHOD();
+
+ for (i = 0; i < n_keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+
+ KEY *key_info = &table->key_info[i];
+
+ if (!(key_info->flags & HA_NOSAME)) {
+ continue;
+ }
+
+ grn_obj *index_table = grn_index_tables[i];
+ if (!index_table) {
+ continue;
+ }
+
+ if ((error = storage_write_row_unique_index(buf, key_info,
+ index_table, &key_id[i])))
+ {
+ if (error == HA_ERR_FOUND_DUPP_KEY)
+ {
+ dup_key = i;
+ }
+ goto err;
+ }
+ }
+ DBUG_RETURN(0);
+
+err:
+ if (i) {
+ mrn_change_encoding(ctx, NULL);
+ do {
+ i--;
+ KEY *key_info = &table->key_info[i];
+ if (key_info->flags & HA_NOSAME) {
+ grn_table_delete_by_id(ctx, grn_index_tables[i], key_id[i]);
+ }
+ } while (i);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::write_row(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_write_row(buf);
+ } else {
+ error = storage_write_row(buf);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_get_record_id(uchar *data, grn_id *record_id,
+ const char *context)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+
+ grn_obj key;
+ GRN_TEXT_INIT(&key, 0);
+
+ mrn_change_encoding(ctx, NULL);
+ grn_bulk_space(ctx, &key, table->key_info->key_length);
+ key_copy((uchar *)(GRN_TEXT_VALUE(&key)),
+ data,
+ &(table->key_info[table_share->primary_key]),
+ table->key_info[table_share->primary_key].key_length);
+
+ *record_id = grn_table_get(ctx, grn_table,
+ GRN_TEXT_VALUE(&key), GRN_TEXT_LEN(&key));
+ if (*record_id == GRN_ID_NIL) {
+ DBUG_PRINT("info", ("mroonga: %s", context));
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "%s: key=<%.*s>",
+ context, (int)GRN_TEXT_LEN(&key), GRN_TEXT_VALUE(&key));
+ error = ER_ERROR_ON_WRITE;
+ push_warning(ha_thd(), Sql_condition::WARN_LEVEL_WARN, error,
+ error_message);
+ }
+ grn_obj_unlink(ctx, &key);
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_update_row(const uchar *old_data, uchar *new_data)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+ THD *thd = ha_thd();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ tmp_disable_binlog(thd);
+ error = wrap_handler->ha_update_row(old_data, new_data);
+ reenable_binlog(thd);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+
+ if (!error && wrapper_have_target_index()) {
+ error = wrapper_update_row_index(old_data, new_data);
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_update_row_index(const uchar *old_data, uchar *new_data)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+
+ if (is_dry_write()) {
+ DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__));
+ DBUG_RETURN(error);
+ }
+
+ mrn_change_encoding(ctx, NULL);
+ KEY key_info = table->key_info[table_share->primary_key];
+ GRN_BULK_REWIND(&key_buffer);
+ key_copy((uchar *)(GRN_TEXT_VALUE(&key_buffer)),
+ new_data,
+ &key_info, key_info.key_length);
+ int added;
+ grn_id new_record_id;
+ new_record_id = grn_table_add(ctx, grn_table,
+ GRN_TEXT_VALUE(&key_buffer),
+ table->key_info->key_length,
+ &added);
+ if (new_record_id == GRN_ID_NIL) {
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "failed to get new record ID for updating from groonga: key=<%.*s>",
+ (int)GRN_TEXT_LEN(&key_buffer), GRN_TEXT_VALUE(&key_buffer));
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, error_message, MYF(0));
+ DBUG_RETURN(error);
+ }
+
+ grn_id old_record_id;
+ my_ptrdiff_t ptr_diff = PTR_BYTE_DIFF(old_data, table->record[0]);
+ for (uint j = 0; j < KEY_N_KEY_PARTS(&key_info); j++) {
+ Field *field = key_info.key_part[j].field;
+ field->move_field_offset(ptr_diff);
+ }
+ error = wrapper_get_record_id((uchar *)old_data, &old_record_id,
+ "failed to get old record ID "
+ "for updating from groonga");
+ for (uint j = 0; j < KEY_N_KEY_PARTS(&key_info); j++) {
+ Field *field = key_info.key_part[j].field;
+ field->move_field_offset(-ptr_diff);
+ }
+ if (error) {
+ DBUG_RETURN(0);
+ }
+
+ mrn::DebugColumnAccess debug_column_access(table, table->read_set);
+ uint i;
+ uint n_keys = table->s->keys;
+ for (i = 0; i < n_keys; i++) {
+ KEY key_info = table->key_info[i];
+
+ if (!(wrapper_is_target_index(&key_info))) {
+ continue;
+ }
+
+ grn_obj *index_column = grn_index_columns[i];
+ if (!index_column) {
+ /* disable keys */
+ continue;
+ }
+
+ uint j;
+ for (j = 0; j < KEY_N_KEY_PARTS(&key_info); j++) {
+ Field *field = key_info.key_part[j].field;
+
+ generic_store_bulk(field, &new_value_buffer);
+
+ field->move_field_offset(ptr_diff);
+ generic_store_bulk(field, &old_value_buffer);
+ field->move_field_offset(-ptr_diff);
+
+ grn_rc rc;
+ if (old_record_id == new_record_id) {
+ if (added) {
+ rc = grn_column_index_update(ctx, index_column, old_record_id, j + 1,
+ &old_value_buffer, NULL);
+ if (!rc) {
+ rc = grn_column_index_update(ctx, index_column, new_record_id, j + 1,
+ NULL, &new_value_buffer);
+ }
+ } else {
+ rc = grn_column_index_update(ctx, index_column, old_record_id, j + 1,
+ &old_value_buffer, &new_value_buffer);
+ }
+ } else {
+ rc = grn_column_index_update(ctx, index_column, old_record_id, j + 1,
+ &old_value_buffer, NULL);
+ if (!rc) {
+ rc = grn_column_index_update(ctx, index_column, new_record_id, j + 1,
+ NULL, &new_value_buffer);
+ }
+ if (!rc) {
+ rc = grn_table_delete_by_id(ctx, grn_table, old_record_id);
+ }
+ }
+ if (rc) {
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, ctx->errbuf, MYF(0));
+ goto err;
+ }
+ }
+ }
+err:
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_update_row(const uchar *old_data, uchar *new_data)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+
+ if (is_dry_write()) {
+ DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__));
+ DBUG_RETURN(error);
+ }
+
+ grn_obj colbuf;
+ int i;
+ uint j;
+ int n_columns = table->s->fields;
+ THD *thd = ha_thd();
+
+ for (i = 0; i < n_columns; i++) {
+ Field *field = table->field[i];
+ const char *column_name = field->field_name;
+
+ if (bitmap_is_set(table->write_set, field->field_index)) {
+ if (field->is_null()) continue;
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, MRN_GET_ERR_MSG(WARN_DATA_TRUNCATED),
+ MRN_COLUMN_NAME_ID,
+ MRN_GET_CURRENT_ROW_FOR_WARNING(thd));
+ if (MRN_ABORT_ON_WARNING(thd)) {
+ DBUG_RETURN(ER_DATA_TOO_LONG);
+ }
+ }
+ }
+ }
+
+ KEY *pkey_info = NULL;
+ storage_store_fields_for_prep_update(old_data, new_data, record_id);
+ {
+ mrn::DebugColumnAccess debug_column_access(table, table->read_set);
+ if ((error = storage_prepare_delete_row_unique_indexes(old_data,
+ record_id))) {
+ DBUG_RETURN(error);
+ }
+ if ((error = storage_update_row_unique_indexes(new_data)))
+ {
+ DBUG_RETURN(error);
+ }
+ }
+
+ if (table->s->primary_key != MAX_INDEXES) {
+ pkey_info = &(table->key_info[table->s->primary_key]);
+ }
+ GRN_VOID_INIT(&colbuf);
+ for (i = 0; i < n_columns; i++) {
+ Field *field = table->field[i];
+ const char *column_name = field->field_name;
+ if (bitmap_is_set(table->write_set, field->field_index)) {
+ mrn::DebugColumnAccess debug_column_access(table, table->read_set);
+ DBUG_PRINT("info", ("mroonga: update column %d(%d)",i,field->field_index));
+
+ if (field->is_null()) continue;
+
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ continue;
+ }
+
+ error = mrn_change_encoding(ctx, field->charset());
+ if (error)
+ goto err;
+
+ bool on_duplicate_key_update =
+ (inserting_with_update && ignoring_duplicated_key);
+ if (!on_duplicate_key_update && pkey_info) {
+ bool have_pkey = false;
+ for (j = 0; j < KEY_N_KEY_PARTS(pkey_info); j++) {
+ Field *pkey_field = pkey_info->key_part[j].field;
+ if (strcmp(pkey_field->field_name, column_name) == 0) {
+ if (!replacing_) {
+ char message[MRN_BUFFER_SIZE];
+ snprintf(message, MRN_BUFFER_SIZE,
+ "data truncated for primary key column: <%s>",
+ column_name);
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, message);
+ }
+ have_pkey = true;
+ }
+ }
+ if (have_pkey) {
+ continue;
+ }
+ }
+
+ generic_store_bulk(field, &colbuf);
+ grn_obj_set_value(ctx, grn_columns[i], record_id, &colbuf, GRN_OBJ_SET);
+ if (ctx->rc) {
+ grn_obj_unlink(ctx, &colbuf);
+ my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0));
+ error = ER_ERROR_ON_WRITE;
+ goto err;
+ }
+ }
+ }
+ grn_obj_unlink(ctx, &colbuf);
+
+ if ((error = storage_update_row_index(old_data, new_data)))
+ {
+ goto err;
+ }
+
+ if ((error = storage_delete_row_unique_indexes()))
+ {
+ DBUG_RETURN(error);
+ }
+
+ grn_db_touch(ctx, grn_ctx_db(ctx));
+
+ if (table->found_next_number_field &&
+ !table->s->next_number_keypart &&
+ new_data == table->record[0]) {
+ mrn::DebugColumnAccess debug_column_access(table, table->read_set);
+ Field_num *field = (Field_num *) table->found_next_number_field;
+ if (field->unsigned_flag || field->val_int() > 0) {
+ MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share;
+ ulonglong nr = (ulonglong) field->val_int();
+ if (!long_term_share->auto_inc_inited) {
+ storage_info(HA_STATUS_AUTO);
+ }
+ {
+ mrn::Lock lock(&long_term_share->auto_inc_mutex);
+ if (long_term_share->auto_inc_value <= nr) {
+ long_term_share->auto_inc_value = nr + 1;
+ DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu",
+ long_term_share->auto_inc_value));
+ }
+ }
+ }
+ }
+ DBUG_RETURN(0);
+
+err:
+ for (j = 0; j < table->s->keys; j++) {
+ if (j == table->s->primary_key) {
+ continue;
+ }
+ KEY *key_info = &table->key_info[j];
+ if ((key_info->flags & HA_NOSAME) && key_id[j] != GRN_ID_NIL) {
+ grn_table_delete_by_id(ctx, grn_index_tables[j], key_id[j]);
+ }
+ }
+
+ if (!error && thd_sql_command(ha_thd()) == SQLCOM_TRUNCATE) {
+ MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share;
+ mrn::Lock lock(&long_term_share->auto_inc_mutex);
+ long_term_share->auto_inc_value = 0;
+ DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu",
+ long_term_share->auto_inc_value));
+ long_term_share->auto_inc_inited = false;
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_update_row_index(const uchar *old_data, uchar *new_data)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+
+ grn_obj old_key, old_encoded_key, new_key, new_encoded_key;
+ GRN_TEXT_INIT(&old_key, 0);
+ GRN_TEXT_INIT(&old_encoded_key, 0);
+ GRN_TEXT_INIT(&new_key, 0);
+ GRN_TEXT_INIT(&new_encoded_key, 0);
+
+ my_ptrdiff_t ptr_diff = PTR_BYTE_DIFF(old_data, table->record[0]);
+
+ mrn::DebugColumnAccess debug_column_access(table, table->read_set);
+ uint i;
+ uint n_keys = table->s->keys;
+ mrn_change_encoding(ctx, NULL);
+ for (i = 0; i < n_keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+
+ KEY key_info = table->key_info[i];
+
+ if (KEY_N_KEY_PARTS(&key_info) == 1 || (key_info.flags & HA_FULLTEXT)) {
+ continue;
+ }
+
+ grn_obj *index_column = grn_index_columns[i];
+ if (!index_column) {
+ /* disable keys */
+ continue;
+ }
+
+ GRN_BULK_REWIND(&old_key);
+ grn_bulk_space(ctx, &old_key, key_info.key_length);
+ for (uint j = 0; j < KEY_N_KEY_PARTS(&key_info); j++) {
+ Field *field = key_info.key_part[j].field;
+ field->move_field_offset(ptr_diff);
+ }
+ key_copy((uchar *)(GRN_TEXT_VALUE(&old_key)),
+ (uchar *)old_data,
+ &key_info,
+ key_info.key_length);
+ for (uint j = 0; j < KEY_N_KEY_PARTS(&key_info); j++) {
+ Field *field = key_info.key_part[j].field;
+ field->move_field_offset(-ptr_diff);
+ }
+ GRN_BULK_REWIND(&old_encoded_key);
+ grn_bulk_space(ctx, &old_encoded_key, key_info.key_length);
+ uint old_encoded_key_length;
+ storage_encode_multiple_column_key(&key_info,
+ (uchar *)(GRN_TEXT_VALUE(&old_key)),
+ key_info.key_length,
+ (uchar *)(GRN_TEXT_VALUE(&old_encoded_key)),
+ &old_encoded_key_length);
+
+ GRN_BULK_REWIND(&new_key);
+ grn_bulk_space(ctx, &new_key, key_info.key_length);
+ key_copy((uchar *)(GRN_TEXT_VALUE(&new_key)),
+ (uchar *)new_data,
+ &key_info,
+ key_info.key_length);
+ GRN_BULK_REWIND(&new_encoded_key);
+ grn_bulk_space(ctx, &new_encoded_key, key_info.key_length);
+ uint new_encoded_key_length;
+ storage_encode_multiple_column_key(&key_info,
+ (uchar *)(GRN_TEXT_VALUE(&new_key)),
+ key_info.key_length,
+ (uchar *)(GRN_TEXT_VALUE(&new_encoded_key)),
+ &new_encoded_key_length);
+
+ grn_rc rc;
+ rc = grn_column_index_update(ctx, index_column, record_id, 1,
+ &old_encoded_key, &new_encoded_key);
+ if (rc) {
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, ctx->errbuf, MYF(0));
+ goto err;
+ }
+ }
+err:
+ grn_obj_unlink(ctx, &old_key);
+ grn_obj_unlink(ctx, &old_encoded_key);
+ grn_obj_unlink(ctx, &new_key);
+ grn_obj_unlink(ctx, &new_encoded_key);
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_update_row_unique_indexes(uchar *new_data)
+{
+ int error;
+ uint i;
+ uint n_keys = table->s->keys;
+ MRN_DBUG_ENTER_METHOD();
+
+ for (i = 0; i < n_keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+
+ KEY *key_info = &table->key_info[i];
+ if (!(key_info->flags & HA_NOSAME)) {
+ continue;
+ }
+
+ grn_obj *index_table = grn_index_tables[i];
+ if (!index_table) {
+ key_id[i] = GRN_ID_NIL;
+ del_key_id[i] = GRN_ID_NIL;
+ continue;
+ }
+
+ if (
+ KEY_N_KEY_PARTS(key_info) == 1 &&
+ !bitmap_is_set(table->write_set,
+ key_info->key_part[0].field->field_index)
+ ) {
+ /* no change */
+ key_id[i] = GRN_ID_NIL;
+ del_key_id[i] = GRN_ID_NIL;
+ continue;
+ }
+
+ if ((error = storage_write_row_unique_index(new_data, key_info,
+ index_table, &key_id[i])))
+ {
+ if (error == HA_ERR_FOUND_DUPP_KEY)
+ {
+ if (key_id[i] == del_key_id[i]) {
+ /* no change */
+ key_id[i] = GRN_ID_NIL;
+ del_key_id[i] = GRN_ID_NIL;
+ continue;
+ }
+ dup_key = i;
+ DBUG_PRINT("info", ("mroonga: different key ID: %d record ID: %d,%d",
+ i, key_id[i], del_key_id[i]));
+ }
+ goto err;
+ }
+ }
+ DBUG_RETURN(0);
+
+err:
+ if (i) {
+ mrn_change_encoding(ctx, NULL);
+ do {
+ i--;
+ KEY *key_info = &table->key_info[i];
+ if ((key_info->flags & HA_NOSAME) && key_id[i] != GRN_ID_NIL) {
+ grn_table_delete_by_id(ctx, grn_index_tables[i], key_id[i]);
+ }
+ } while (i);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::update_row(const uchar *old_data, uchar *new_data)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_update_row(old_data, new_data);
+ } else {
+ error = storage_update_row(old_data, new_data);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_delete_row(const uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+ THD *thd= ha_thd();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ tmp_disable_binlog(thd);
+ error = wrap_handler->ha_delete_row(buf);
+ reenable_binlog(thd);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+
+ if (!error && wrapper_have_target_index()) {
+ error = wrapper_delete_row_index(buf);
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_delete_row_index(const uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+
+ if (is_dry_write()) {
+ DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__));
+ DBUG_RETURN(error);
+ }
+
+ mrn_change_encoding(ctx, NULL);
+ grn_id record_id;
+ error = wrapper_get_record_id((uchar *)buf, &record_id,
+ "failed to get record ID "
+ "for deleting from groonga");
+ if (error) {
+ DBUG_RETURN(0);
+ }
+
+ mrn::DebugColumnAccess debug_column_access(table, table->read_set);
+ uint i;
+ uint n_keys = table->s->keys;
+ for (i = 0; i < n_keys; i++) {
+ KEY key_info = table->key_info[i];
+
+ if (!(wrapper_is_target_index(&key_info))) {
+ continue;
+ }
+
+ grn_obj *index_column = grn_index_columns[i];
+ if (!index_column) {
+ /* disable keys */
+ continue;
+ }
+
+ uint j;
+ for (j = 0; j < KEY_N_KEY_PARTS(&key_info); j++) {
+ Field *field = key_info.key_part[j].field;
+
+ if (field->is_null())
+ continue;
+
+ generic_store_bulk(field, &old_value_buffer);
+ grn_rc rc;
+ rc = grn_column_index_update(ctx, index_column, record_id, j + 1,
+ &old_value_buffer, NULL);
+ if (rc) {
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, ctx->errbuf, MYF(0));
+ goto err;
+ }
+ }
+ }
+err:
+ grn_table_delete_by_id(ctx, grn_table, record_id);
+ if (ctx->rc) {
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, ctx->errbuf, MYF(0));
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_delete_row(const uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error;
+
+ if (is_dry_write()) {
+ DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__));
+ DBUG_RETURN(0);
+ }
+
+ storage_store_fields_for_prep_update(buf, NULL, record_id);
+ if ((error = storage_prepare_delete_row_unique_indexes(buf, record_id))) {
+ DBUG_RETURN(error);
+ }
+ mrn_change_encoding(ctx, NULL);
+ grn_table_delete_by_id(ctx, grn_table, record_id);
+ if (ctx->rc) {
+ my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
+ }
+ if (
+ (error = storage_delete_row_index(buf)) ||
+ (error = storage_delete_row_unique_indexes())
+ ) {
+ DBUG_RETURN(error);
+ }
+
+ grn_db_touch(ctx, grn_ctx_db(ctx));
+
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::storage_delete_row_index(const uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+
+ grn_obj key, encoded_key;
+ GRN_TEXT_INIT(&key, 0);
+ GRN_TEXT_INIT(&encoded_key, 0);
+
+ mrn::DebugColumnAccess debug_column_access(table, table->read_set);
+ uint i;
+ uint n_keys = table->s->keys;
+ mrn_change_encoding(ctx, NULL);
+ for (i = 0; i < n_keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+
+ KEY key_info = table->key_info[i];
+
+ if (KEY_N_KEY_PARTS(&key_info) == 1 || (key_info.flags & HA_FULLTEXT)) {
+ continue;
+ }
+
+ grn_obj *index_column = grn_index_columns[i];
+ if (!index_column) {
+ /* disable keys */
+ continue;
+ }
+
+ GRN_BULK_REWIND(&key);
+ grn_bulk_space(ctx, &key, key_info.key_length);
+ key_copy((uchar *)(GRN_TEXT_VALUE(&key)),
+ (uchar *)buf,
+ &key_info,
+ key_info.key_length);
+ GRN_BULK_REWIND(&encoded_key);
+ grn_bulk_space(ctx, &encoded_key, key_info.key_length);
+ uint encoded_key_length;
+ storage_encode_multiple_column_key(&key_info,
+ (uchar *)(GRN_TEXT_VALUE(&key)),
+ key_info.key_length,
+ (uchar *)(GRN_TEXT_VALUE(&encoded_key)),
+ &encoded_key_length);
+
+ grn_rc rc;
+ rc = grn_column_index_update(ctx, index_column, record_id, 1,
+ &encoded_key, NULL);
+ if (rc) {
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, ctx->errbuf, MYF(0));
+ goto err;
+ }
+ }
+err:
+ grn_obj_unlink(ctx, &encoded_key);
+ grn_obj_unlink(ctx, &key);
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_delete_row_unique_index(grn_obj *index_table,
+ grn_id del_key_id)
+{
+ MRN_DBUG_ENTER_METHOD();
+ grn_rc rc = grn_table_delete_by_id(ctx, index_table, del_key_id);
+ if (rc) {
+ my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
+ }
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::storage_delete_row_unique_indexes()
+{
+ int error = 0, tmp_error;
+ uint i;
+ uint n_keys = table->s->keys;
+ MRN_DBUG_ENTER_METHOD();
+
+ for (i = 0; i < n_keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+
+ KEY *key_info = &table->key_info[i];
+ if ((!(key_info->flags & HA_NOSAME)) || del_key_id[i] == GRN_ID_NIL) {
+ continue;
+ }
+
+ grn_obj *index_table = grn_index_tables[i];
+ if ((tmp_error = storage_delete_row_unique_index(index_table,
+ del_key_id[i])))
+ {
+ error = tmp_error;
+ }
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_prepare_delete_row_unique_index(const uchar *buf,
+ grn_id record_id,
+ KEY *key_info,
+ grn_obj *index_table,
+ grn_obj *index_column,
+ grn_id *del_key_id)
+{
+ const void *ukey = NULL;
+ uint32 ukey_size = 0;
+ MRN_DBUG_ENTER_METHOD();
+ if (KEY_N_KEY_PARTS(key_info) == 1) {
+ ukey = grn_obj_get_value_(ctx, index_column, record_id, &ukey_size);
+ } else {
+ mrn_change_encoding(ctx, NULL);
+ uchar key[MRN_MAX_KEY_SIZE];
+ key_copy(key, (uchar *) buf, key_info, key_info->key_length);
+ grn_bulk_space(ctx, &key_buffer, key_info->key_length);
+ ukey = GRN_TEXT_VALUE(&key_buffer);
+ storage_encode_multiple_column_key(key_info,
+ key, key_info->key_length,
+ (uchar *)ukey, (uint *)&ukey_size);
+ }
+ *del_key_id = grn_table_get(ctx, index_table, ukey, ukey_size);
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::storage_prepare_delete_row_unique_indexes(const uchar *buf,
+ grn_id record_id)
+{
+ int error = 0, tmp_error;
+ uint i;
+ uint n_keys = table->s->keys;
+ MRN_DBUG_ENTER_METHOD();
+
+ for (i = 0; i < n_keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+
+ KEY *key_info = &table->key_info[i];
+ if (!(key_info->flags & HA_NOSAME)) {
+ continue;
+ }
+
+ grn_obj *index_table = grn_index_tables[i];
+ if (!index_table) {
+ del_key_id[i] = GRN_ID_NIL;
+ continue;
+ }
+
+ grn_obj *index_column;
+ if (KEY_N_KEY_PARTS(key_info) == 1) {
+ Field *field = key_info->key_part[0].field;
+ mrn_change_encoding(ctx, field->charset());
+ index_column = grn_columns[field->field_index];
+ } else {
+ mrn_change_encoding(ctx, NULL);
+ index_column = grn_index_columns[i];
+ }
+ if ((tmp_error = storage_prepare_delete_row_unique_index(buf, record_id,
+ key_info,
+ index_table,
+ index_column,
+ &del_key_id[i])))
+ {
+ error = tmp_error;
+ }
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::delete_row(const uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_delete_row(buf);
+ } else {
+ error = storage_delete_row(buf);
+ }
+ DBUG_RETURN(error);
+}
+
+uint ha_mroonga::wrapper_max_supported_key_parts()
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(MAX_REF_PARTS);
+}
+
+uint ha_mroonga::storage_max_supported_key_parts()
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(1);
+}
+
+uint ha_mroonga::max_supported_key_parts()
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint parts;
+ if (share->wrapper_mode)
+ {
+ parts = wrapper_max_supported_key_parts();
+ } else {
+ parts = storage_max_supported_key_parts();
+ }
+ DBUG_RETURN(parts);
+}
+
+ha_rows ha_mroonga::wrapper_records_in_range(uint key_nr, key_range *range_min,
+ key_range *range_max)
+{
+ ha_rows row_count;
+ MRN_DBUG_ENTER_METHOD();
+ KEY key_info = table->s->key_info[key_nr];
+ if (mrn_is_geo_key(&key_info)) {
+ row_count = generic_records_in_range_geo(key_nr, range_min, range_max);
+ } else {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ row_count = wrap_handler->records_in_range(key_nr, range_min, range_max);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ DBUG_RETURN(row_count);
+}
+
+ha_rows ha_mroonga::storage_records_in_range(uint key_nr, key_range *range_min,
+ key_range *range_max)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int flags = 0;
+ uint size_min = 0, size_max = 0;
+ ha_rows row_count = 0;
+ uchar *key_min = NULL, *key_max = NULL;
+ uchar key_min_entity[MRN_MAX_KEY_SIZE];
+ uchar key_max_entity[MRN_MAX_KEY_SIZE];
+ KEY key_info = table->s->key_info[key_nr];
+ bool is_multiple_column_index = KEY_N_KEY_PARTS(&key_info) > 1;
+
+ if (is_multiple_column_index) {
+ mrn_change_encoding(ctx, NULL);
+ if (range_min && range_max &&
+ range_min->length == range_max->length &&
+ memcmp(range_min->key, range_max->key, range_min->length) == 0) {
+ flags |= GRN_CURSOR_PREFIX;
+ key_min = key_min_entity;
+ storage_encode_multiple_column_key(&key_info,
+ range_min->key, range_min->length,
+ key_min, &size_min);
+ } else {
+ key_min = key_min_entity;
+ key_max = key_max_entity;
+ storage_encode_multiple_column_key_range(&key_info,
+ range_min, range_max,
+ key_min, &size_min,
+ key_max, &size_max);
+ }
+ } else if (mrn_is_geo_key(&key_info)) {
+ mrn_change_encoding(ctx, key_info.key_part->field->charset());
+ row_count = generic_records_in_range_geo(key_nr, range_min, range_max);
+ DBUG_RETURN(row_count);
+ } else {
+ KEY_PART_INFO key_part = key_info.key_part[0];
+ Field *field = key_part.field;
+ const char *column_name = field->field_name;
+ mrn_change_encoding(ctx, field->charset());
+
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ DBUG_RETURN((ha_rows)1);
+ }
+
+ if (range_min) {
+ key_min = key_min_entity;
+ storage_encode_key(field, range_min->key, key_min, &size_min);
+ if (size_min == 0) {
+ DBUG_RETURN(HA_POS_ERROR);
+ }
+ }
+ if (range_max) {
+ key_max = key_max_entity;
+ storage_encode_key(field, range_max->key, key_max, &size_max);
+ if (size_max == 0) {
+ DBUG_RETURN(HA_POS_ERROR);
+ }
+ }
+ }
+
+ if (range_min) {
+ DBUG_PRINT("info", ("mroonga: range_min->flag=%u", range_min->flag));
+ if (range_min->flag == HA_READ_AFTER_KEY) {
+ flags |= GRN_CURSOR_GT;
+ }
+ }
+ if (range_max) {
+ DBUG_PRINT("info", ("mroonga: range_min->flag=%u", range_max->flag));
+ if (range_max->flag == HA_READ_BEFORE_KEY) {
+ flags |= GRN_CURSOR_LT;
+ }
+ }
+
+ uint pkey_nr = table->s->primary_key;
+ if (key_nr == pkey_nr) {
+ DBUG_PRINT("info", ("mroonga: use primary key"));
+ grn_table_cursor *cursor;
+ cursor = grn_table_cursor_open(ctx, grn_table,
+ key_min, size_min,
+ key_max, size_max,
+ 0, -1, flags);
+ while (grn_table_cursor_next(ctx, cursor) != GRN_ID_NIL) {
+ row_count++;
+ }
+ grn_table_cursor_close(ctx, cursor);
+ } else {
+ if (is_multiple_column_index) {
+ DBUG_PRINT("info", ("mroonga: use multiple column key%u", key_nr));
+ } else {
+ DBUG_PRINT("info", ("mroonga: use key%u", key_nr));
+ }
+
+ grn_table_cursor *cursor;
+ grn_table_cursor *index_cursor;
+ cursor = grn_table_cursor_open(ctx, grn_index_tables[key_nr],
+ key_min, size_min,
+ key_max, size_max,
+ 0, -1, flags);
+ index_cursor = grn_index_cursor_open(ctx, cursor,
+ grn_index_columns[key_nr],
+ 0, GRN_ID_MAX, 0);
+ while (grn_table_cursor_next(ctx, index_cursor) != GRN_ID_NIL) {
+ row_count++;
+ }
+ grn_obj_unlink(ctx, index_cursor);
+ grn_table_cursor_close(ctx, cursor);
+ }
+ DBUG_RETURN(row_count);
+}
+
+ha_rows ha_mroonga::generic_records_in_range_geo(uint key_nr,
+ key_range *range_min,
+ key_range *range_max)
+{
+ MRN_DBUG_ENTER_METHOD();
+ ha_rows row_count;
+ int error;
+
+ if (!range_min) {
+ DBUG_PRINT("info",
+ ("mroonga: range min is missing for geometry range search"));
+ DBUG_RETURN(HA_POS_ERROR);
+ }
+ if (range_max) {
+ DBUG_PRINT("info",
+ ("mroonga: range max is specified for geometry range search"));
+ DBUG_RETURN(HA_POS_ERROR);
+ }
+ error = mrn_change_encoding(ctx,
+ table->key_info[key_nr].key_part->field->charset());
+ if (error)
+ DBUG_RETURN(error);
+ if (!(range_min->flag & HA_READ_MBR_CONTAIN)) {
+ push_warning_unsupported_spatial_index_search(range_min->flag);
+ row_count = grn_table_size(ctx, grn_table);
+ DBUG_RETURN(row_count);
+ }
+
+ geo_store_rectangle(range_min->key);
+ row_count = grn_geo_estimate_in_rectangle(ctx,
+ grn_index_columns[key_nr],
+ &top_left_point,
+ &bottom_right_point);
+ DBUG_RETURN(row_count);
+}
+
+ha_rows ha_mroonga::records_in_range(uint key_nr, key_range *range_min, key_range *range_max)
+{
+ MRN_DBUG_ENTER_METHOD();
+ ha_rows row_count = 0;
+ if (share->wrapper_mode)
+ {
+ row_count = wrapper_records_in_range(key_nr, range_min, range_max);
+ } else {
+ row_count = storage_records_in_range(key_nr, range_min, range_max);
+ }
+ DBUG_PRINT("info", ("mroonga: row_count=%" MRN_HA_ROWS_FORMAT, row_count));
+ DBUG_RETURN(row_count);
+}
+
+int ha_mroonga::wrapper_index_init(uint idx, bool sorted)
+{
+ int error = 0;
+ KEY key_info = table->s->key_info[idx];
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (!mrn_is_geo_key(&key_info) && key_info.algorithm != HA_KEY_ALG_FULLTEXT)
+ {
+ error = wrap_handler->ha_index_init(share->wrap_key_nr[idx], sorted);
+ } else {
+ error = wrap_handler->ha_index_init(share->wrap_primary_key, sorted);
+ }
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_index_init(uint idx, bool sorted)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::index_init(uint idx, bool sorted)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_PRINT("info", ("mroonga: idx=%u", idx));
+ active_index = idx;
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_index_init(idx, sorted);
+ } else {
+ error = storage_index_init(idx, sorted);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_index_end()
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->ha_index_or_rnd_end();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_index_end()
+{
+ MRN_DBUG_ENTER_METHOD();
+ clear_cursor();
+ clear_cursor_geo();
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::index_end()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_index_end();
+ } else {
+ error = storage_index_end();
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_index_read_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ KEY key_info = table->key_info[active_index];
+ if (mrn_is_geo_key(&key_info)) {
+ clear_cursor_geo();
+ error = generic_geo_open_cursor(key, find_flag);
+ if (!error) {
+ error = wrapper_get_next_geo_record(buf);
+ }
+ DBUG_RETURN(error);
+ } else {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+#ifdef MRN_HANDLER_HAVE_HA_INDEX_READ_MAP
+ error = wrap_handler->ha_index_read_map(buf, key, keypart_map, find_flag);
+#else
+ error = wrap_handler->index_read_map(buf, key, keypart_map, find_flag);
+#endif
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_index_read_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ MRN_DBUG_ENTER_METHOD();
+ check_count_skip(keypart_map, 0, false);
+
+ int error = 0;
+
+ uint key_nr = active_index;
+ KEY key_info = table->key_info[key_nr];
+ int flags = 0;
+ uint size_min = 0, size_max = 0;
+ uchar *key_min = NULL, *key_max = NULL;
+ uchar key_min_entity[MRN_MAX_KEY_SIZE];
+ uchar key_max_entity[MRN_MAX_KEY_SIZE];
+
+ clear_cursor();
+ clear_cursor_geo();
+ clear_empty_value_records();
+
+ bool is_multiple_column_index = KEY_N_KEY_PARTS(&key_info) > 1;
+ if (is_multiple_column_index) {
+ mrn_change_encoding(ctx, NULL);
+ flags |= GRN_CURSOR_PREFIX;
+ uint key_length = calculate_key_len(table, active_index, key, keypart_map);
+ key_min = key_min_entity;
+ storage_encode_multiple_column_key(&key_info,
+ key, key_length,
+ key_min, &size_min);
+ } else if (mrn_is_geo_key(&key_info)) {
+ error = mrn_change_encoding(ctx, key_info.key_part->field->charset());
+ if (error)
+ DBUG_RETURN(error);
+ error = generic_geo_open_cursor(key, find_flag);
+ if (!error) {
+ error = storage_get_next_record(buf);
+ }
+ DBUG_RETURN(error);
+ } else {
+ KEY_PART_INFO key_part = key_info.key_part[0];
+ Field *field = key_part.field;
+ error = mrn_change_encoding(ctx, field->charset());
+ if (error)
+ DBUG_RETURN(error);
+
+ if (find_flag == HA_READ_KEY_EXACT) {
+ const char *column_name = field->field_name;
+
+ key_min = key_min_entity;
+ key_max = key_min_entity;
+ storage_encode_key(field, key, key_min, &size_min);
+ size_max = size_min;
+ // for _id
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ grn_id found_record_id = *((grn_id *)key_min);
+ if (grn_table_at(ctx, grn_table, found_record_id) != GRN_ID_NIL) { // found
+ storage_store_fields(buf, found_record_id);
+ table->status = 0;
+ record_id = found_record_id;
+ DBUG_RETURN(0);
+ } else {
+ table->status = STATUS_NOT_FOUND;
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ }
+ } else if (
+ find_flag == HA_READ_BEFORE_KEY ||
+ find_flag == HA_READ_PREFIX_LAST_OR_PREV
+ ) {
+ key_max = key_max_entity;
+ storage_encode_key(field, key, key_max_entity, &size_max);
+ } else {
+ key_min = key_min_entity;
+ storage_encode_key(field, key, key_min_entity, &size_min);
+ }
+ }
+
+ switch (find_flag) {
+ case HA_READ_BEFORE_KEY:
+ flags |= GRN_CURSOR_LT | GRN_CURSOR_DESCENDING;
+ break;
+ case HA_READ_PREFIX_LAST_OR_PREV:
+ flags |= GRN_CURSOR_LE | GRN_CURSOR_DESCENDING;
+ break;
+ case HA_READ_AFTER_KEY:
+ flags |= GRN_CURSOR_GT | GRN_CURSOR_ASCENDING;
+ break;
+ case HA_READ_KEY_OR_NEXT:
+ flags |= GRN_CURSOR_GE | GRN_CURSOR_ASCENDING;
+ break;
+ default:
+ break;
+ }
+
+ uint pkey_nr = table->s->primary_key;
+ if (key_nr == pkey_nr) {
+ DBUG_PRINT("info", ("mroonga: use primary key"));
+ cursor = grn_table_cursor_open(ctx, grn_table,
+ key_min, size_min,
+ key_max, size_max,
+ 0, -1, flags);
+ } else {
+ bool is_empty_value_records_search = false;
+ if (is_multiple_column_index) {
+ DBUG_PRINT("info", ("mroonga: use multiple column key%u", key_nr));
+ } else if (flags == 0 && size_min == 0 && size_max == 0) {
+ is_empty_value_records_search = true;
+ DBUG_PRINT("info",
+ ("mroonga: use table scan for searching empty value records"));
+ } else {
+ DBUG_PRINT("info", ("mroonga: use key%u", key_nr));
+ }
+ if (is_empty_value_records_search) {
+ grn_obj *expression, *expression_variable;
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, grn_table,
+ expression, expression_variable);
+ grn_obj *target_column =
+ grn_columns[key_info.key_part->field->field_index];
+ grn_expr_append_const(ctx, expression, target_column, GRN_OP_GET_VALUE, 1);
+ grn_obj empty_value;
+ GRN_TEXT_INIT(&empty_value, 0);
+ grn_expr_append_obj(ctx, expression, &empty_value, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, expression, GRN_OP_EQUAL, 2);
+
+ empty_value_records =
+ grn_table_create(ctx, NULL, 0, NULL,
+ GRN_OBJ_TABLE_HASH_KEY | GRN_OBJ_WITH_SUBREC,
+ grn_table, 0);
+ grn_table_select(ctx, grn_table, expression, empty_value_records,
+ GRN_OP_OR);
+ grn_obj_unlink(ctx, expression);
+ grn_obj_unlink(ctx, &empty_value);
+
+ empty_value_records_cursor =
+ grn_table_cursor_open(ctx, empty_value_records,
+ NULL, 0, NULL, 0,
+ 0, -1, flags);
+ } else {
+ index_table_cursor = grn_table_cursor_open(ctx, grn_index_tables[key_nr],
+ key_min, size_min,
+ key_max, size_max,
+ 0, -1, flags);
+ cursor = grn_index_cursor_open(ctx, index_table_cursor,
+ grn_index_columns[key_nr],
+ 0, GRN_ID_MAX, 0);
+ }
+ }
+ if (ctx->rc) {
+ my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_READ);
+ }
+ error = storage_get_next_record(buf);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::index_read_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_index_read_map(buf, key, keypart_map, find_flag);
+ } else {
+ error = storage_index_read_map(buf, key, keypart_map, find_flag);
+ }
+ DBUG_PRINT("info", ("mroonga: error=%d", error));
+ DBUG_RETURN(error);
+}
+
+#ifdef MRN_HANDLER_HAVE_INDEX_READ_LAST_MAP
+int ha_mroonga::wrapper_index_read_last_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+# ifdef MRN_HANDLER_HAVE_HA_INDEX_READ_LAST_MAP
+ error = wrap_handler->ha_index_read_last_map(buf, key, keypart_map);
+# else
+ error = wrap_handler->index_read_last_map(buf, key, keypart_map);
+# endif
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_index_read_last_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map)
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint key_nr = active_index;
+ KEY key_info = table->key_info[key_nr];
+
+ int flags = GRN_CURSOR_DESCENDING, error;
+ uint size_min = 0, size_max = 0;
+ uchar *key_min = NULL, *key_max = NULL;
+ uchar key_min_entity[MRN_MAX_KEY_SIZE];
+
+ clear_cursor();
+
+ bool is_multiple_column_index = KEY_N_KEY_PARTS(&key_info) > 1;
+ if (is_multiple_column_index) {
+ mrn_change_encoding(ctx, NULL);
+ flags |= GRN_CURSOR_PREFIX;
+ uint key_length = calculate_key_len(table, active_index, key, keypart_map);
+ key_min = key_min_entity;
+ storage_encode_multiple_column_key(&key_info,
+ key, key_length,
+ key_min, &size_min);
+ } else {
+ KEY_PART_INFO key_part = key_info.key_part[0];
+ Field *field = key_part.field;
+ error = mrn_change_encoding(ctx, field->charset());
+ if (error)
+ DBUG_RETURN(error);
+
+ key_min = key_min_entity;
+ key_max = key_min_entity;
+ storage_encode_key(field, key, key_min, &size_min);
+ size_max = size_min;
+ }
+
+ uint pkey_nr = table->s->primary_key;
+ if (key_nr == pkey_nr) {
+ DBUG_PRINT("info", ("mroonga: use primary key"));
+ cursor = grn_table_cursor_open(ctx, grn_table,
+ key_min, size_min, key_max, size_max,
+ 0, -1, flags);
+ } else {
+ if (is_multiple_column_index) {
+ DBUG_PRINT("info", ("mroonga: use multiple column key%u", key_nr));
+ } else {
+ DBUG_PRINT("info", ("mroonga: use key%u", key_nr));
+ }
+ index_table_cursor = grn_table_cursor_open(ctx, grn_index_tables[key_nr],
+ key_min, size_min,
+ key_max, size_max,
+ 0, -1, flags);
+ cursor = grn_index_cursor_open(ctx, index_table_cursor,
+ grn_index_columns[key_nr],
+ 0, GRN_ID_MAX, 0);
+ }
+ if (ctx->rc) {
+ my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_READ);
+ }
+ error = storage_get_next_record(buf);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::index_read_last_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_index_read_last_map(buf, key, keypart_map);
+ } else {
+ error = storage_index_read_last_map(buf, key, keypart_map);
+ }
+ DBUG_RETURN(error);
+}
+#endif
+
+int ha_mroonga::wrapper_index_next(uchar *buf)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ KEY key_info = table->key_info[active_index];
+ if (mrn_is_geo_key(&key_info)) {
+ error = wrapper_get_next_geo_record(buf);
+ } else {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+#ifdef MRN_HANDLER_HAVE_HA_INDEX_NEXT
+ error = wrap_handler->ha_index_next(buf);
+#else
+ error = wrap_handler->index_next(buf);
+#endif
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_index_next(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = storage_get_next_record(buf);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::index_next(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_index_next(buf);
+ } else {
+ error = storage_index_next(buf);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_index_prev(uchar *buf)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ KEY key_info = table->key_info[active_index];
+ if (mrn_is_geo_key(&key_info)) {
+ error = wrapper_get_next_geo_record(buf);
+ } else {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+#ifdef MRN_HANDLER_HAVE_HA_INDEX_NEXT
+ error = wrap_handler->ha_index_prev(buf);
+#else
+ error = wrap_handler->index_prev(buf);
+#endif
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_index_prev(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = storage_get_next_record(buf);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::index_prev(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_index_prev(buf);
+ } else {
+ error = storage_index_prev(buf);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_index_first(uchar *buf)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+#ifdef MRN_HANDLER_HAVE_HA_INDEX_FIRST
+ error = wrap_handler->ha_index_first(buf);
+#else
+ error = wrap_handler->index_first(buf);
+#endif
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_index_first(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ clear_cursor();
+ int flags = GRN_CURSOR_ASCENDING;
+ uint pkey_nr = table->s->primary_key;
+ mrn_change_encoding(ctx, NULL);
+ if (active_index == pkey_nr) {
+ DBUG_PRINT("info", ("mroonga: use primary key"));
+ cursor = grn_table_cursor_open(ctx, grn_table, NULL, 0, NULL, 0,
+ 0, -1, flags);
+ } else {
+ if (KEY_N_KEY_PARTS(&(table->key_info[active_index])) > 1) {
+ DBUG_PRINT("info", ("mroonga: use multiple column key%u", active_index));
+ } else {
+ DBUG_PRINT("info", ("mroonga: use key%u", active_index));
+ }
+ index_table_cursor = grn_table_cursor_open(ctx,
+ grn_index_tables[active_index],
+ NULL, 0,
+ NULL, 0,
+ 0, -1, flags);
+ cursor = grn_index_cursor_open(ctx, index_table_cursor,
+ grn_index_columns[active_index],
+ 0, GRN_ID_MAX, 0);
+ }
+ if (ctx->rc) {
+ my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_READ);
+ }
+ int error = storage_get_next_record(buf);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::index_first(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_index_first(buf);
+ } else {
+ error = storage_index_first(buf);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_index_last(uchar *buf)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+#ifdef MRN_HANDLER_HAVE_HA_INDEX_LAST
+ error = wrap_handler->ha_index_last(buf);
+#else
+ error = wrap_handler->index_last(buf);
+#endif
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_index_last(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ clear_cursor();
+ int flags = GRN_CURSOR_DESCENDING;
+ uint pkey_nr = table->s->primary_key;
+ mrn_change_encoding(ctx, NULL);
+ if (active_index == pkey_nr) {
+ DBUG_PRINT("info", ("mroonga: use primary key"));
+ cursor = grn_table_cursor_open(ctx, grn_table, NULL, 0, NULL, 0,
+ 0, -1, flags);
+ } else {
+ if (KEY_N_KEY_PARTS(&(table->key_info[active_index])) > 1) {
+ DBUG_PRINT("info", ("mroonga: use multiple column key%u", active_index));
+ } else {
+ DBUG_PRINT("info", ("mroonga: use key%u", active_index));
+ }
+ index_table_cursor = grn_table_cursor_open(ctx,
+ grn_index_tables[active_index],
+ NULL, 0,
+ NULL, 0,
+ 0, -1, flags);
+ cursor = grn_index_cursor_open(ctx, index_table_cursor,
+ grn_index_columns[active_index],
+ 0, GRN_ID_MAX, 0);
+ }
+ if (ctx->rc) {
+ my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_READ);
+ }
+ int error = storage_get_next_record(buf);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::index_last(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_index_last(buf);
+ } else {
+ error = storage_index_last(buf);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_index_next_same(uchar *buf, const uchar *key,
+ uint keylen)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ KEY key_info = table->s->key_info[active_index];
+ if (mrn_is_geo_key(&key_info)) {
+ error = wrapper_get_next_geo_record(buf);
+ } else {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+#ifdef MRN_HANDLER_HAVE_HA_INDEX_NEXT_SAME
+ error = wrap_handler->ha_index_next_same(buf, key, keylen);
+#else
+ error = wrap_handler->index_next_same(buf, key, keylen);
+#endif
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_index_next_same(uchar *buf, const uchar *key,
+ uint keylen)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = storage_get_next_record(count_skip ? NULL : buf);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::index_next_same(uchar *buf, const uchar *key, uint keylen)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_index_next_same(buf, key, keylen);
+ } else {
+ error = storage_index_next_same(buf, key, keylen);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_read_range_first(const key_range *start_key,
+ const key_range *end_key,
+ bool eq_range, bool sorted)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ KEY key_info = table->key_info[active_index];
+ if (mrn_is_geo_key(&key_info)) {
+ clear_cursor_geo();
+ error = generic_geo_open_cursor(start_key->key, start_key->flag);
+ if (!error) {
+ error = wrapper_get_next_geo_record(table->record[0]);
+ }
+ DBUG_RETURN(error);
+ }
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+ error = wrap_handler->read_range_first(start_key, end_key, eq_range,
+ sorted);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_read_range_first(const key_range *start_key,
+ const key_range *end_key,
+ bool eq_range, bool sorted)
+{
+ MRN_DBUG_ENTER_METHOD();
+ check_count_skip(start_key ? start_key->keypart_map : 0,
+ end_key ? end_key->keypart_map : 0, false);
+ int flags = 0, error;
+ uint size_min = 0, size_max = 0;
+ uchar *key_min = NULL, *key_max = NULL;
+ uchar key_min_entity[MRN_MAX_KEY_SIZE];
+ uchar key_max_entity[MRN_MAX_KEY_SIZE];
+ KEY key_info = table->s->key_info[active_index];
+
+ clear_cursor();
+
+ bool is_multiple_column_index = KEY_N_KEY_PARTS(&key_info) > 1;
+ if (is_multiple_column_index) {
+ mrn_change_encoding(ctx, NULL);
+ if (start_key && end_key &&
+ start_key->length == end_key->length &&
+ memcmp(start_key->key, end_key->key, start_key->length) == 0) {
+ flags |= GRN_CURSOR_PREFIX;
+ key_min = key_min_entity;
+ storage_encode_multiple_column_key(&key_info,
+ start_key->key, start_key->length,
+ key_min, &size_min);
+ } else {
+ key_min = key_min_entity;
+ key_max = key_max_entity;
+ storage_encode_multiple_column_key_range(&key_info,
+ start_key, end_key,
+ key_min, &size_min,
+ key_max, &size_max);
+ if (size_min == 0) {
+ key_min = NULL;
+ }
+ if (size_max == 0) {
+ key_max = NULL;
+ }
+ }
+ } else {
+ KEY_PART_INFO key_part = key_info.key_part[0];
+ Field *field = key_part.field;
+ const char *column_name = field->field_name;
+ error = mrn_change_encoding(ctx, field->charset());
+ if (error)
+ DBUG_RETURN(error);
+ if (start_key) {
+ key_min = key_min_entity;
+ storage_encode_key(field, start_key->key, key_min_entity,
+ &size_min);
+ if (start_key->flag == HA_READ_KEY_EXACT) {
+ // for _id
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ grn_id found_record_id = *((grn_id *)key_min);
+ if (grn_table_at(ctx, grn_table, found_record_id) != GRN_ID_NIL) { // found
+ storage_store_fields(table->record[0], found_record_id);
+ table->status = 0;
+ cursor = NULL;
+ record_id = found_record_id;
+ DBUG_RETURN(0);
+ } else {
+ table->status = STATUS_NOT_FOUND;
+ cursor = NULL;
+ record_id = GRN_ID_NIL;
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ }
+ }
+ }
+ if (end_key) {
+ key_max = key_max_entity;
+ storage_encode_key(field, end_key->key, key_max, &size_max);
+ }
+ }
+
+ if (start_key) {
+ switch (start_key->flag) {
+ case HA_READ_AFTER_KEY:
+ flags |= GRN_CURSOR_GT | GRN_CURSOR_ASCENDING;
+ break;
+ case HA_READ_KEY_OR_NEXT:
+ flags |= GRN_CURSOR_GE | GRN_CURSOR_ASCENDING;
+ break;
+ default:
+ break;
+ }
+ }
+ if (end_key) {
+ switch (end_key->flag) {
+ case HA_READ_BEFORE_KEY:
+ flags |= GRN_CURSOR_LT | GRN_CURSOR_ASCENDING;
+ break;
+ case HA_READ_AFTER_KEY:
+ flags |= GRN_CURSOR_GE | GRN_CURSOR_ASCENDING;
+ break;
+ default:
+ break;
+ }
+ }
+
+ uint pkey_nr = table->s->primary_key;
+ if (active_index == pkey_nr) {
+ DBUG_PRINT("info", ("mroonga: use primary key"));
+ cursor = grn_table_cursor_open(ctx, grn_table,
+ key_min, size_min, key_max, size_max,
+ 0, -1, flags);
+ } else {
+ if (is_multiple_column_index) {
+ DBUG_PRINT("info", ("mroonga: use multiple column key%u", active_index));
+ } else {
+ DBUG_PRINT("info", ("mroonga: use key%u", active_index));
+ }
+ index_table_cursor = grn_table_cursor_open(ctx,
+ grn_index_tables[active_index],
+ key_min, size_min,
+ key_max, size_max,
+ 0, -1, flags);
+ cursor = grn_index_cursor_open(ctx, index_table_cursor,
+ grn_index_columns[active_index],
+ 0, GRN_ID_MAX, 0);
+ }
+ if (ctx->rc) {
+ my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_READ);
+ }
+ error = storage_get_next_record(table->record[0]);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::read_range_first(const key_range *start_key,
+ const key_range *end_key,
+ bool eq_range, bool sorted)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_read_range_first(start_key, end_key, eq_range,
+ sorted);
+ } else {
+ error = storage_read_range_first(start_key, end_key, eq_range, sorted);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_read_range_next()
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ KEY key_info = table->key_info[active_index];
+ if (mrn_is_geo_key(&key_info)) {
+ error = wrapper_get_next_geo_record(table->record[0]);
+ DBUG_RETURN(error);
+ }
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+ error = wrap_handler->read_range_next();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_read_range_next()
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ if (cursor == NULL) {
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ int error = storage_get_next_record(count_skip ? NULL : table->record[0]);
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::read_range_next()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_read_range_next();
+ } else {
+ error = storage_read_range_next();
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_ft_init()
+{
+ MRN_DBUG_ENTER_METHOD();
+ struct st_mrn_ft_info *mrn_ft_info =
+ reinterpret_cast<struct st_mrn_ft_info *>(ft_handler);
+ GRN_CTX_SET_ENCODING(ctx, mrn_ft_info->encoding);
+
+ int error = 0;
+ if (sorted_result) {
+ mrn_ft_info->cursor = grn_table_cursor_open(ctx, sorted_result,
+ NULL, 0, NULL, 0,
+ 0, -1, 0);
+ } else {
+ mrn_ft_info->cursor = grn_table_cursor_open(ctx, mrn_ft_info->result,
+ NULL, 0, NULL, 0,
+ 0, -1, 0);
+ }
+ if (ctx->rc) {
+ error = ER_ERROR_ON_READ;
+ my_message(error, ctx->errbuf, MYF(0));
+ } else {
+ if (sorted_result) {
+ if (grn_table->header.type == GRN_TABLE_NO_KEY) {
+ mrn_ft_info->id_accessor = grn_obj_column(ctx, sorted_result,
+ MRN_COLUMN_NAME_ID,
+ strlen(MRN_COLUMN_NAME_ID));
+ } else {
+ mrn_ft_info->key_accessor = grn_obj_column(ctx, sorted_result,
+ MRN_COLUMN_NAME_KEY,
+ strlen(MRN_COLUMN_NAME_KEY));
+ }
+ } else {
+ mrn_ft_info->key_accessor = grn_obj_column(ctx, mrn_ft_info->result,
+ MRN_COLUMN_NAME_KEY,
+ strlen(MRN_COLUMN_NAME_KEY));
+ }
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_ft_init()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = generic_ft_init();
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_ft_init()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = generic_ft_init();
+ record_id = GRN_ID_NIL;
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::ft_init()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_ft_init();
+ } else {
+ error = storage_ft_init();
+ }
+ DBUG_RETURN(error);
+}
+
+void ha_mroonga::generic_ft_init_ext_add_conditions_fast_order_limit(
+ struct st_mrn_ft_info *info, grn_obj *expression)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ Item *where = table->pos_in_table_list->select_lex->where;
+
+ bool is_storage_mode = !(share->wrapper_mode);
+ mrn::ConditionConverter converter(info->ctx, grn_table, is_storage_mode);
+ converter.convert(where, expression);
+
+ DBUG_VOID_RETURN;
+}
+
+bool ha_mroonga::generic_ft_init_ext_parse_pragma_d(struct st_mrn_ft_info *info,
+ const char *keyword,
+ uint keyword_length,
+ grn_operator *default_operator,
+ uint *consumed_keyword_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ grn_bool succeeded = true;
+ if (keyword_length >= 1 && keyword[0] == '+') {
+ *default_operator = GRN_OP_AND;
+ *consumed_keyword_length = 1;
+ } else if (keyword_length >= 1 && keyword[0] == '-') {
+ *default_operator = GRN_OP_AND_NOT;
+ *consumed_keyword_length = 1;
+ } else if (keyword_length >= 2 && memcmp(keyword, "OR", 2) == 0) {
+ *default_operator = GRN_OP_OR;
+ *consumed_keyword_length = 2;
+ } else {
+ succeeded = false;
+ }
+
+ DBUG_RETURN(succeeded);
+}
+
+void ha_mroonga::generic_ft_init_ext_parse_pragma_w_append_section(
+ struct st_mrn_ft_info *info,
+ grn_obj *index_column,
+ grn_obj *match_columns,
+ uint section,
+ grn_obj *section_value_buffer,
+ int weight,
+ uint n_weights)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ grn_expr_append_obj(info->ctx, match_columns, index_column, GRN_OP_PUSH, 1);
+ GRN_UINT32_SET(info->ctx, section_value_buffer, section);
+ grn_expr_append_const(info->ctx, match_columns, section_value_buffer,
+ GRN_OP_PUSH, 1);
+ grn_expr_append_op(info->ctx, match_columns, GRN_OP_GET_MEMBER, 2);
+
+ if (weight != 1) {
+ grn_expr_append_const_int(info->ctx, match_columns, weight,
+ GRN_OP_PUSH, 1);
+ grn_expr_append_op(info->ctx, match_columns, GRN_OP_STAR, 2);
+ }
+
+ if (n_weights >= 2) {
+ grn_expr_append_op(info->ctx, match_columns, GRN_OP_OR, 2);
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+bool ha_mroonga::generic_ft_init_ext_parse_pragma_w(struct st_mrn_ft_info *info,
+ const char *keyword,
+ uint keyword_length,
+ grn_obj *index_column,
+ grn_obj *match_columns,
+ uint *consumed_keyword_length,
+ grn_obj *tmp_objects)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ *consumed_keyword_length = 0;
+
+ uint n_sections = KEY_N_KEY_PARTS(info->key_info);
+
+ grn_obj section_value_buffer;
+ GRN_UINT32_INIT(&section_value_buffer, 0);
+
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(bool, specified_sections, n_sections);
+ for (uint i = 0; i < n_sections; ++i) {
+ specified_sections[i] = false;
+ }
+
+ uint n_weights = 0;
+ while (keyword_length >= 1) {
+ if (n_weights >= 1) {
+ if (keyword[0] != ',') {
+ break;
+ }
+ uint n_used_keyword_length = 1;
+ *consumed_keyword_length += n_used_keyword_length;
+ keyword_length -= n_used_keyword_length;
+ keyword += n_used_keyword_length;
+ if (keyword_length == 0) {
+ break;
+ }
+ }
+
+ uint section = 0;
+ if ('1' <= keyword[0] && keyword[0] <= '9') {
+ const char *section_start = keyword;
+ const char *keyword_end = keyword + keyword_length;
+ const char *keyword_rest;
+ section = grn_atoui(section_start, keyword_end, &keyword_rest);
+ if (section_start == keyword_rest) {
+ break;
+ }
+ if (!(0 < section && section <= n_sections)) {
+ break;
+ }
+ section -= 1;
+ specified_sections[section] = true;
+ uint n_used_keyword_length = keyword_rest - keyword;
+ *consumed_keyword_length += n_used_keyword_length;
+ keyword_length -= n_used_keyword_length;
+ keyword += n_used_keyword_length;
+ } else {
+ break;
+ }
+
+ int weight = 1;
+ if (keyword_length >= 2 && keyword[0] == ':') {
+ const char *weight_start = keyword + 1;
+ const char *keyword_end = keyword + keyword_length;
+ const char *keyword_rest;
+ weight = grn_atoi(weight_start, keyword_end, &keyword_rest);
+ if (weight_start == keyword_rest) {
+ break;
+ }
+ uint n_used_keyword_length = keyword_rest - keyword;
+ *consumed_keyword_length += n_used_keyword_length;
+ keyword_length -= n_used_keyword_length;
+ keyword += n_used_keyword_length;
+ }
+
+ n_weights++;
+
+ generic_ft_init_ext_parse_pragma_w_append_section(info,
+ index_column,
+ match_columns,
+ section,
+ &section_value_buffer,
+ weight,
+ n_weights);
+ }
+
+ for (uint section = 0; section < n_sections; ++section) {
+ if (specified_sections[section]) {
+ continue;
+ }
+
+ ++n_weights;
+
+ int default_weight = 1;
+ generic_ft_init_ext_parse_pragma_w_append_section(info,
+ index_column,
+ match_columns,
+ section,
+ &section_value_buffer,
+ default_weight,
+ n_weights);
+ }
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(specified_sections);
+
+ GRN_OBJ_FIN(info->ctx, &section_value_buffer);
+
+ DBUG_RETURN(n_weights > 0);
+}
+
+grn_rc ha_mroonga::generic_ft_init_ext_prepare_expression_in_boolean_mode(
+ struct st_mrn_ft_info *info,
+ String *key,
+ grn_obj *index_column,
+ grn_obj *match_columns,
+ grn_obj *expression,
+ grn_obj *tmp_objects)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ grn_rc rc = GRN_SUCCESS;
+
+ const char *keyword, *keyword_original;
+ uint keyword_length, keyword_length_original;
+ grn_operator default_operator = GRN_OP_OR;
+ grn_bool weight_specified = false;
+ keyword = keyword_original = key->ptr();
+ keyword_length = keyword_length_original = key->length();
+ // WORKAROUND: support only "D" and "W" pragmas.
+ if (keyword_length >= 2 && keyword[0] == '*') {
+ bool parsed = false;
+ bool done = false;
+ keyword++;
+ keyword_length++;
+ while (!done) {
+ uint consumed_keyword_length = 0;
+ switch (keyword[0]) {
+ case 'D':
+ if (generic_ft_init_ext_parse_pragma_d(info,
+ keyword + 1,
+ keyword_length - 1,
+ &default_operator,
+ &consumed_keyword_length)) {
+ parsed = true;
+ consumed_keyword_length += 1;
+ keyword += consumed_keyword_length;
+ keyword_length -= consumed_keyword_length;
+ } else {
+ done = true;
+ }
+ break;
+ case 'W':
+ if (generic_ft_init_ext_parse_pragma_w(info,
+ keyword + 1,
+ keyword_length - 1,
+ index_column,
+ match_columns,
+ &consumed_keyword_length,
+ tmp_objects)) {
+ parsed = true;
+ weight_specified = true;
+ consumed_keyword_length += 1;
+ keyword += consumed_keyword_length;
+ keyword_length -= consumed_keyword_length;
+ } else {
+ done = true;
+ }
+ break;
+ default:
+ done = true;
+ break;
+ }
+ }
+ if (!parsed) {
+ keyword = keyword_original;
+ keyword_length = keyword_length_original;
+ }
+ }
+ // WORKAROUND: ignore the first '+' to support "+apple macintosh" pattern.
+ while (keyword_length > 0 && keyword[0] == ' ') {
+ keyword++;
+ keyword_length--;
+ }
+ if (keyword_length > 0 && keyword[0] == '+') {
+ keyword++;
+ keyword_length--;
+ }
+ if (!weight_specified) {
+ grn_expr_append_obj(info->ctx, match_columns, index_column, GRN_OP_PUSH, 1);
+ }
+ grn_expr_flags expression_flags =
+ GRN_EXPR_SYNTAX_QUERY | GRN_EXPR_ALLOW_LEADING_NOT;
+ rc = grn_expr_parse(info->ctx, expression,
+ keyword, keyword_length,
+ match_columns, GRN_OP_MATCH, default_operator,
+ expression_flags);
+ if (rc) {
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "failed to parse fulltext search keyword: <%.*s>: <%s>",
+ keyword_length_original, keyword_original,
+ info->ctx->errbuf);
+ ulong action = THDVAR(ha_thd(), action_on_fulltext_query_error);
+ switch (static_cast<mrn_action_on_error>(action)) {
+ case MRN_ACTION_ON_ERROR_ERROR:
+ my_message(ER_PARSE_ERROR, error_message, MYF(0));
+ break;
+ case MRN_ACTION_ON_ERROR_ERROR_AND_LOG:
+ my_message(ER_PARSE_ERROR, error_message, MYF(0));
+ GRN_LOG(info->ctx, GRN_LOG_ERROR, "%s", error_message);
+ break;
+ case MRN_ACTION_ON_ERROR_IGNORE:
+ break;
+ case MRN_ACTION_ON_ERROR_IGNORE_AND_LOG:
+ GRN_LOG(info->ctx, GRN_LOG_ERROR, "%s", error_message);
+ break;
+ }
+ }
+
+ DBUG_RETURN(rc);
+}
+
+grn_rc ha_mroonga::generic_ft_init_ext_prepare_expression_in_normal_mode(
+ struct st_mrn_ft_info *info,
+ String *key,
+ grn_obj *index_column,
+ grn_obj *match_columns,
+ grn_obj *expression,
+ grn_obj *tmp_objects)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ grn_rc rc = GRN_SUCCESS;
+
+ grn_obj query;
+ GRN_TEXT_INIT(&query, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET(info->ctx, &query, key->ptr(), key->length());
+ grn_expr_append_obj(info->ctx, match_columns, index_column, GRN_OP_PUSH, 1);
+ grn_expr_append_obj(info->ctx, expression, match_columns, GRN_OP_PUSH, 1);
+ grn_expr_append_const(info->ctx, expression, &query, GRN_OP_PUSH, 1);
+ grn_expr_append_op(info->ctx, expression, GRN_OP_SIMILAR, 2);
+ grn_obj_unlink(info->ctx, &query);
+
+ DBUG_RETURN(rc);
+}
+
+struct st_mrn_ft_info *ha_mroonga::generic_ft_init_ext_select(uint flags,
+ uint key_nr,
+ String *key)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ struct st_mrn_ft_info *info = new st_mrn_ft_info();
+ info->mroonga = this;
+ info->ctx = ctx;
+ mrn_change_encoding(info->ctx,
+ table->key_info[key_nr].key_part->field->charset());
+ info->encoding = GRN_CTX_GET_ENCODING(info->ctx);
+ info->table = grn_table;
+ info->result = grn_table_create(info->ctx, NULL, 0, NULL,
+ GRN_OBJ_TABLE_HASH_KEY | GRN_OBJ_WITH_SUBREC,
+ grn_table, 0);
+ info->score_column = grn_obj_column(info->ctx, info->result,
+ MRN_COLUMN_NAME_SCORE,
+ strlen(MRN_COLUMN_NAME_SCORE));
+ GRN_TEXT_INIT(&(info->key), 0);
+ grn_bulk_space(info->ctx, &(info->key), table->key_info->key_length);
+ GRN_INT32_INIT(&(info->score), 0);
+ info->active_index = key_nr;
+ info->key_info = &(table->key_info[key_nr]);
+ info->primary_key_info = &(table->key_info[table_share->primary_key]);
+ info->cursor = NULL;
+ info->id_accessor = NULL;
+ info->key_accessor = NULL;
+
+ if (key->length() == 0) {
+ DBUG_RETURN(info);
+ }
+
+ grn_obj *index_column = grn_index_columns[key_nr];
+ grn_obj *match_columns, *match_columns_variable;
+ GRN_EXPR_CREATE_FOR_QUERY(info->ctx, info->table, match_columns,
+ match_columns_variable);
+
+ grn_obj *expression, *expression_variable;
+ GRN_EXPR_CREATE_FOR_QUERY(info->ctx, info->table,
+ expression, expression_variable);
+ grn_obj tmp_objects;
+ GRN_PTR_INIT(&tmp_objects, GRN_OBJ_VECTOR, GRN_ID_NIL);
+
+ grn_rc rc = GRN_SUCCESS;
+ if (flags & FT_BOOL) {
+ rc = generic_ft_init_ext_prepare_expression_in_boolean_mode(info,
+ key,
+ index_column,
+ match_columns,
+ expression,
+ &tmp_objects);
+ } else {
+ rc = generic_ft_init_ext_prepare_expression_in_normal_mode(info,
+ key,
+ index_column,
+ match_columns,
+ expression,
+ &tmp_objects);
+ }
+
+ if (rc == GRN_SUCCESS) {
+ if (fast_order_limit) {
+ generic_ft_init_ext_add_conditions_fast_order_limit(info, expression);
+ }
+ longlong escalation_threshold = THDVAR(ha_thd(), match_escalation_threshold);
+ mrn::MatchEscalationThresholdScope scope(info->ctx, escalation_threshold);
+ grn_table_select(info->ctx, info->table, expression,
+ info->result, GRN_OP_OR);
+ }
+
+ grn_obj_unlink(info->ctx, expression);
+ grn_obj_unlink(info->ctx, match_columns);
+
+ uint n_tmp_objects = GRN_BULK_VSIZE(&tmp_objects) / sizeof(grn_obj *);
+ for (uint i = 0; i < n_tmp_objects; ++i) {
+ grn_obj_unlink(info->ctx, GRN_PTR_VALUE_AT(&tmp_objects, i));
+ }
+ grn_obj_unlink(info->ctx, &tmp_objects);
+
+ DBUG_RETURN(info);
+}
+
+FT_INFO *ha_mroonga::generic_ft_init_ext(uint flags, uint key_nr, String *key)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ check_count_skip(0, 0, true);
+
+ mrn_change_encoding(ctx, system_charset_info);
+ grn_operator operation = GRN_OP_AND;
+ if (!matched_record_keys) {
+ matched_record_keys = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_OBJ_TABLE_HASH_KEY | GRN_OBJ_WITH_SUBREC,
+ grn_table, 0);
+ operation = GRN_OP_OR;
+ }
+
+ grn_table_sort_key *sort_keys = NULL;
+ int n_sort_keys = 0;
+ longlong limit = -1;
+ check_fast_order_limit(&sort_keys, &n_sort_keys, &limit);
+
+ struct st_mrn_ft_info *info =
+ generic_ft_init_ext_select(flags, key_nr, key);
+
+ grn_rc rc;
+ rc = grn_table_setoperation(ctx, matched_record_keys, info->result,
+ matched_record_keys, operation);
+ if (rc) {
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "failed to merge matched record keys: <%s>",
+ ctx->errbuf);
+ my_message(ER_ERROR_ON_READ, error_message, MYF(0));
+ GRN_LOG(ctx, GRN_LOG_ERROR, "%s", error_message);
+ }
+ if (fast_order_limit) {
+ sorted_result = grn_table_create(ctx, NULL,
+ 0, NULL,
+ GRN_OBJ_TABLE_NO_KEY, NULL,
+ matched_record_keys);
+ grn_table_sort(ctx, matched_record_keys, 0, static_cast<int>(limit),
+ sorted_result, sort_keys, n_sort_keys);
+ }
+ if (sort_keys) {
+ for (int i = 0; i < n_sort_keys; i++) {
+ grn_obj_unlink(info->ctx, sort_keys[i].key);
+ }
+ free(sort_keys);
+ }
+
+ DBUG_RETURN((FT_INFO *)info);
+}
+
+FT_INFO *ha_mroonga::wrapper_ft_init_ext(uint flags, uint key_nr, String *key)
+{
+ MRN_DBUG_ENTER_METHOD();
+ FT_INFO *info = generic_ft_init_ext(flags, key_nr, key);
+ struct st_mrn_ft_info *mrn_ft_info = (struct st_mrn_ft_info *)info;
+ mrn_ft_info->please = &mrn_wrapper_ft_vft;
+#ifdef HA_CAN_FULLTEXT_EXT
+ mrn_ft_info->could_you = &mrn_wrapper_ft_vft_ext;
+#endif
+ ++wrap_ft_init_count;
+ DBUG_RETURN(info);
+}
+
+FT_INFO *ha_mroonga::storage_ft_init_ext(uint flags, uint key_nr, String *key)
+{
+ MRN_DBUG_ENTER_METHOD();
+ FT_INFO *info = generic_ft_init_ext(flags, key_nr, key);
+ struct st_mrn_ft_info *mrn_ft_info = (struct st_mrn_ft_info *)info;
+ mrn_ft_info->please = &mrn_storage_ft_vft;
+#ifdef HA_CAN_FULLTEXT_EXT
+ mrn_ft_info->could_you = &mrn_storage_ft_vft_ext;
+#endif
+ DBUG_RETURN(info);
+}
+
+FT_INFO *ha_mroonga::ft_init_ext(uint flags, uint key_nr, String *key)
+{
+ MRN_DBUG_ENTER_METHOD();
+ fulltext_searching = true;
+ FT_INFO *info;
+ if (key_nr == NO_SUCH_KEY) {
+ struct st_mrn_ft_info *mrn_ft_info = new st_mrn_ft_info();
+ mrn_ft_info->please = &mrn_no_such_key_ft_vft;
+#ifdef HA_CAN_FULLTEXT_EXT
+ mrn_ft_info->could_you = &mrn_no_such_key_ft_vft_ext;
+#endif
+ info = (FT_INFO *)mrn_ft_info;
+ } else {
+ if (share->wrapper_mode)
+ {
+ info = wrapper_ft_init_ext(flags, key_nr, key);
+ } else {
+ info = storage_ft_init_ext(flags, key_nr, key);
+ }
+ }
+ DBUG_RETURN(info);
+}
+
+int ha_mroonga::wrapper_ft_read(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (wrap_ft_init_count)
+ set_pk_bitmap();
+
+ struct st_mrn_ft_info *mrn_ft_info =
+ reinterpret_cast<struct st_mrn_ft_info *>(ft_handler);
+ GRN_CTX_SET_ENCODING(ctx, mrn_ft_info->encoding);
+
+ int error = 0;
+ do {
+ grn_id found_record_id;
+ found_record_id = grn_table_cursor_next(ctx, mrn_ft_info->cursor);
+ if (found_record_id == GRN_ID_NIL) {
+ error = HA_ERR_END_OF_FILE;
+ break;
+ }
+
+ GRN_BULK_REWIND(&key_buffer);
+ if (mrn_ft_info->key_accessor) {
+ grn_obj_get_value(ctx, mrn_ft_info->key_accessor,
+ found_record_id, &key_buffer);
+ } else {
+ void *key;
+ int key_length;
+ key_length = grn_table_cursor_get_key(ctx, mrn_ft_info->cursor, &key);
+ GRN_TEXT_SET(ctx, &key_buffer, key, key_length);
+ }
+ error = wrapper_get_record(buf, (const uchar *)GRN_TEXT_VALUE(&key_buffer));
+ } while (error == HA_ERR_END_OF_FILE || error == HA_ERR_KEY_NOT_FOUND);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_ft_read(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ struct st_mrn_ft_info *mrn_ft_info =
+ reinterpret_cast<struct st_mrn_ft_info *>(ft_handler);
+ GRN_CTX_SET_ENCODING(ctx, mrn_ft_info->encoding);
+
+ grn_id found_record_id;
+ found_record_id = grn_table_cursor_next(ctx, mrn_ft_info->cursor);
+ if (ctx->rc) {
+ my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_READ);
+ }
+
+ if (found_record_id == GRN_ID_NIL) {
+ table->status = STATUS_NOT_FOUND;
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ table->status = 0;
+
+ if (count_skip && record_id != GRN_ID_NIL) {
+ DBUG_RETURN(0);
+ }
+
+ GRN_BULK_REWIND(&key_buffer);
+ if (mrn_ft_info->id_accessor) {
+ grn_obj id_buffer;
+ GRN_RECORD_INIT(&id_buffer, 0, grn_obj_id(ctx, grn_table));
+ grn_obj_get_value(ctx, mrn_ft_info->id_accessor,
+ found_record_id, &id_buffer);
+ record_id = GRN_RECORD_VALUE(&id_buffer);
+ } else if (mrn_ft_info->key_accessor) {
+ grn_obj_get_value(ctx, mrn_ft_info->key_accessor,
+ found_record_id, &key_buffer);
+ record_id = grn_table_get(ctx, grn_table,
+ GRN_TEXT_VALUE(&key_buffer),
+ GRN_TEXT_LEN(&key_buffer));
+ } else {
+ void *key;
+ grn_table_cursor_get_key(ctx, mrn_ft_info->cursor, &key);
+ if (ctx->rc) {
+ record_id = GRN_ID_NIL;
+ my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_READ);
+ } else {
+ record_id = *((grn_id *)key);
+ }
+ }
+ storage_store_fields(buf, record_id);
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::ft_read(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_ft_read(buf);
+ } else {
+ error = storage_ft_read(buf);
+ }
+ DBUG_RETURN(error);
+}
+
+const Item *ha_mroonga::wrapper_cond_push(const Item *cond)
+{
+ const Item *reminder_cond;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ reminder_cond = wrap_handler->cond_push(cond);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(reminder_cond);
+}
+
+const Item *ha_mroonga::storage_cond_push(const Item *cond)
+{
+ MRN_DBUG_ENTER_METHOD();
+ const Item *reminder_cond = cond;
+ if (!pushed_cond) {
+ mrn::ConditionConverter converter(ctx, grn_table, true);
+ if (converter.find_match_against(cond) && converter.is_convertable(cond)) {
+ reminder_cond = NULL;
+ }
+ }
+ DBUG_RETURN(reminder_cond);
+}
+
+const Item *ha_mroonga::cond_push(const Item *cond)
+{
+ MRN_DBUG_ENTER_METHOD();
+ const Item *reminder_cond;
+ if (share->wrapper_mode)
+ {
+ reminder_cond = wrapper_cond_push(cond);
+ } else {
+ reminder_cond = storage_cond_push(cond);
+ }
+ DBUG_RETURN(reminder_cond);
+}
+
+void ha_mroonga::wrapper_cond_pop()
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->cond_pop();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_cond_pop()
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::cond_pop()
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ wrapper_cond_pop();
+ else
+ storage_cond_pop();
+ DBUG_VOID_RETURN;
+}
+
+bool ha_mroonga::wrapper_get_error_message(int error, String *buf)
+{
+ bool temporary_error;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ temporary_error = wrap_handler->get_error_message(error, buf);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(temporary_error);
+}
+
+bool ha_mroonga::storage_get_error_message(int error, String *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool temporary_error = false;
+ // latest error message
+ buf->copy(ctx->errbuf, (uint) strlen(ctx->errbuf), system_charset_info);
+ DBUG_RETURN(temporary_error);
+}
+
+bool ha_mroonga::get_error_message(int error, String *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool temporary_error;
+ if (share && share->wrapper_mode)
+ {
+ temporary_error = wrapper_get_error_message(error, buf);
+ } else {
+ temporary_error = storage_get_error_message(error, buf);
+ }
+ DBUG_RETURN(temporary_error);
+}
+
+void ha_mroonga::mkdir_p(const char *directory)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int i = 0;
+ char sub_directory[MRN_MAX_PATH_SIZE];
+ sub_directory[0] = '\0';
+ while (true) {
+ if (directory[i] == FN_LIBCHAR ||
+ directory[i] == FN_LIBCHAR2 ||
+ directory[i] == '\0') {
+ sub_directory[i] = '\0';
+ struct stat directory_status;
+ if (stat(sub_directory, &directory_status) != 0) {
+ DBUG_PRINT("info", ("mroonga: creating directory: <%s>", sub_directory));
+ GRN_LOG(ctx, GRN_LOG_INFO, "creating directory: <%s>", sub_directory);
+ if (MRN_MKDIR(sub_directory, S_IRWXU) == 0) {
+ DBUG_PRINT("info",
+ ("mroonga: created directory: <%s>", sub_directory));
+ GRN_LOG(ctx, GRN_LOG_INFO, "created directory: <%s>", sub_directory);
+ } else {
+ DBUG_PRINT("error",
+ ("mroonga: failed to create directory: <%s>: <%s>",
+ sub_directory, strerror(errno)));
+ GRN_LOG(ctx, GRN_LOG_ERROR,
+ "failed to create directory: <%s>: <%s>",
+ sub_directory, strerror(errno));
+ DBUG_VOID_RETURN;
+ }
+ }
+ }
+
+ if (directory[i] == '\0') {
+ break;
+ }
+
+ sub_directory[i] = directory[i];
+ ++i;
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+ulonglong ha_mroonga::file_size(const char *path)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ struct stat file_status;
+ if (stat(path, &file_status) == 0) {
+ DBUG_RETURN(file_status.st_size);
+ } else {
+ DBUG_RETURN(0);
+ }
+}
+
+void ha_mroonga::push_warning_unsupported_spatial_index_search(enum ha_rkey_function flag)
+{
+ char search_name[MRN_BUFFER_SIZE];
+ if (flag == HA_READ_MBR_INTERSECT) {
+ strcpy(search_name, "intersect");
+ } else if (flag == HA_READ_MBR_WITHIN) {
+ strcpy(search_name, "within");
+ } else if (flag & HA_READ_MBR_DISJOINT) {
+ strcpy(search_name, "disjoint");
+ } else if (flag & HA_READ_MBR_EQUAL) {
+ strcpy(search_name, "equal");
+ } else {
+ sprintf(search_name, "unknown: %d", flag);
+ }
+ push_warning_printf(ha_thd(),
+ Sql_condition::WARN_LEVEL_WARN,
+ ER_UNSUPPORTED_EXTENSION,
+ "spatial index search "
+ "except MBRContains aren't supported: <%s>",
+ search_name);
+}
+
+void ha_mroonga::clear_cursor()
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (cursor) {
+ grn_obj_unlink(ctx, cursor);
+ cursor = NULL;
+ }
+ if (index_table_cursor) {
+ grn_table_cursor_close(ctx, index_table_cursor);
+ index_table_cursor = NULL;
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::clear_cursor_geo()
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (cursor_geo) {
+ grn_obj_unlink(ctx, cursor_geo);
+ cursor_geo = NULL;
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::clear_empty_value_records()
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (empty_value_records_cursor) {
+ grn_table_cursor_close(ctx, empty_value_records_cursor);
+ empty_value_records_cursor = NULL;
+ }
+ if (empty_value_records) {
+ grn_obj_unlink(ctx, empty_value_records);
+ empty_value_records = NULL;
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::clear_search_result()
+{
+ MRN_DBUG_ENTER_METHOD();
+ clear_cursor();
+ if (sorted_result) {
+ grn_obj_unlink(ctx, sorted_result);
+ sorted_result = NULL;
+ }
+ if (matched_record_keys) {
+ grn_obj_unlink(ctx, matched_record_keys);
+ matched_record_keys = NULL;
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::clear_search_result_geo()
+{
+ MRN_DBUG_ENTER_METHOD();
+ clear_cursor_geo();
+ if (grn_source_column_geo) {
+ grn_obj_unlink(ctx, grn_source_column_geo);
+ grn_source_column_geo = NULL;
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::clear_indexes()
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint n_keys = table->s->keys;
+ uint pkey_nr = table->s->primary_key;
+
+ for (uint i = 0; i < n_keys; i++) {
+ if (i != pkey_nr) {
+ if (grn_index_tables) {
+ grn_obj_unlink(ctx, grn_index_tables[i]);
+ }
+ if (grn_index_columns) {
+ grn_obj_unlink(ctx, grn_index_columns[i]);
+ }
+ }
+ }
+
+ if (grn_index_tables) {
+ free(grn_index_tables);
+ grn_index_tables = NULL;
+ }
+
+ if (grn_index_columns) {
+ free(grn_index_columns);
+ grn_index_columns = NULL;
+ }
+
+ if (key_id) {
+ free(key_id);
+ key_id = NULL;
+ }
+
+ if (del_key_id) {
+ free(del_key_id);
+ del_key_id = NULL;
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::alter_share_add(const char *path, TABLE_SHARE *table_share)
+{
+ MRN_DBUG_ENTER_METHOD();
+ st_mrn_slot_data *slot_data = mrn_get_slot_data(ha_thd(), true);
+ if (!slot_data)
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ st_mrn_alter_share *alter_share =
+ (st_mrn_alter_share *)malloc(sizeof(st_mrn_alter_share));
+ if (!alter_share)
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ alter_share->next = NULL;
+ strcpy(alter_share->path, path);
+ alter_share->alter_share = table_share;
+ if (slot_data->first_alter_share)
+ {
+ st_mrn_alter_share *tmp_alter_share = slot_data->first_alter_share;
+ while (tmp_alter_share->next)
+ tmp_alter_share = tmp_alter_share->next;
+ tmp_alter_share->next = alter_share;
+ } else {
+ slot_data->first_alter_share = alter_share;
+ }
+ DBUG_RETURN(0);
+}
+
+void ha_mroonga::remove_related_files(const char *base_path)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ const char *base_directory_name = ".";
+ size_t base_path_length = strlen(base_path);
+#ifdef WIN32
+ WIN32_FIND_DATA data;
+ HANDLE finder = FindFirstFile(base_directory_name, &data);
+ if (finder != INVALID_HANDLE_VALUE) {
+ do {
+ if (!(data.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)) {
+ continue;
+ }
+ if (strncmp(data.cFileName, base_path, base_path_length) == 0) {
+ unlink(data.cFileName);
+ }
+ } while (FindNextFile(finder, &data) != 0);
+ FindClose(finder);
+ }
+#else
+ DIR *dir = opendir(base_directory_name);
+ if (dir) {
+ while (struct dirent *entry = readdir(dir)) {
+ struct stat file_status;
+ if (stat(entry->d_name, &file_status) != 0) {
+ continue;
+ }
+ if (!((file_status.st_mode & S_IFMT) && S_IFREG)) {
+ continue;
+ }
+ if (strncmp(entry->d_name, base_path, base_path_length) == 0) {
+ unlink(entry->d_name);
+ }
+ }
+ closedir(dir);
+ }
+#endif
+
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::remove_grn_obj_force(const char *name)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ grn_obj *obj = grn_ctx_get(ctx, name, strlen(name));
+ if (obj) {
+ grn_obj_remove(ctx, obj);
+ } else {
+ grn_obj *db = grn_ctx_db(ctx);
+ grn_id id = grn_table_get(ctx, db, name, strlen(name));
+ if (id) {
+ char path[MRN_MAX_PATH_SIZE];
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ if (grn_obj_path_by_id(ctx, db, id, path) == GRN_SUCCESS) {
+ remove_related_files(path);
+ }
+ }
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::drop_index(MRN_SHARE *target_share, uint key_index)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ grn_rc rc = GRN_SUCCESS;
+ char target_name[GRN_TABLE_MAX_KEY_SIZE];
+ int target_name_length;
+
+ KEY *key_info = target_share->table_share->key_info;
+ if (!target_share->wrapper_mode && target_share->index_table[key_index]) {
+ const char *table_name = target_share->index_table[key_index];
+ snprintf(target_name, GRN_TABLE_MAX_KEY_SIZE,
+ "%s.%s", table_name, key_info[key_index].name);
+ grn_obj *index_column = grn_ctx_get(ctx, target_name, strlen(target_name));
+ if (index_column) {
+ rc = grn_obj_remove(ctx, index_column);
+ }
+ } else {
+ mrn::PathMapper mapper(target_share->table_name);
+ mrn::IndexTableName index_table_name(mapper.table_name(),
+ key_info[key_index].name);
+ grn_obj *index_table = grn_ctx_get(ctx,
+ index_table_name.c_str(),
+ index_table_name.length());
+ if (index_table) {
+ target_name_length = grn_obj_name(ctx, index_table,
+ target_name, GRN_TABLE_MAX_KEY_SIZE);
+ rc = grn_obj_remove(ctx, index_table);
+ }
+ }
+
+ if (rc != GRN_SUCCESS) {
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "failed to drop index: <%.*s>: <%s>",
+ target_name_length, target_name,
+ ctx->errbuf);
+ my_message(ER_ERROR_ON_WRITE, error_message, MYF(0));
+ GRN_LOG(ctx, GRN_LOG_ERROR, "%s", error_message);
+ }
+
+ DBUG_RETURN(error);
+}
+
+grn_obj *ha_mroonga::find_tokenizer(const char *name, int name_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ if (strncasecmp("off", name, name_length) == 0) {
+ DBUG_RETURN(NULL);
+ }
+
+ grn_obj *tokenizer;
+ mrn_change_encoding(ctx, system_charset_info);
+ tokenizer = grn_ctx_get(ctx, name, name_length);
+ if (!tokenizer) {
+ char message[MRN_BUFFER_SIZE];
+ sprintf(message,
+ "specified fulltext parser <%.*s> doesn't exist. "
+ "default fulltext parser <%s> is used instead.",
+ name_length, name,
+ MRN_PARSER_DEFAULT);
+ push_warning(ha_thd(),
+ Sql_condition::WARN_LEVEL_WARN, ER_UNSUPPORTED_EXTENSION,
+ message);
+ tokenizer = grn_ctx_get(ctx,
+ MRN_PARSER_DEFAULT,
+ strlen(MRN_PARSER_DEFAULT));
+ }
+ if (!tokenizer) {
+ push_warning(ha_thd(),
+ Sql_condition::WARN_LEVEL_WARN, ER_UNSUPPORTED_EXTENSION,
+ "couldn't find fulltext parser. "
+ "Bigram fulltext parser is used instead.");
+ tokenizer = grn_ctx_at(ctx, GRN_DB_BIGRAM);
+ }
+ DBUG_RETURN(tokenizer);
+}
+
+grn_obj *ha_mroonga::find_normalizer(KEY *key_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ grn_obj *normalizer = NULL;
+#if MYSQL_VERSION_ID >= 50500
+ if (key_info->comment.length > 0) {
+ mrn::ParametersParser parser(key_info->comment.str,
+ key_info->comment.length);
+ parser.parse();
+ const char *normalizer_name = parser["normalizer"];
+ if (normalizer_name) {
+ normalizer = grn_ctx_get(ctx, normalizer_name, -1);
+ }
+ }
+#endif
+ if (!normalizer) {
+ Field *field = key_info->key_part[0].field;
+ mrn::FieldNormalizer field_normalizer(ctx, ha_thd(), field);
+ normalizer = field_normalizer.find_grn_normalizer();
+ }
+ DBUG_RETURN(normalizer);
+}
+
+int ha_mroonga::wrapper_get_record(uchar *buf, const uchar *key)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (wrap_handler->inited == NONE) {
+#ifdef MRN_HANDLER_HAVE_HA_INDEX_READ_IDX_MAP
+ error = wrap_handler->ha_index_read_idx_map(buf,
+ share->wrap_primary_key,
+ key,
+ pk_keypart_map,
+ HA_READ_KEY_EXACT);
+#else
+ error = wrap_handler->index_read_idx_map(buf,
+ share->wrap_primary_key,
+ key,
+ pk_keypart_map,
+ HA_READ_KEY_EXACT);
+#endif
+ } else {
+#ifdef MRN_HANDLER_HAVE_HA_INDEX_READ_MAP
+ error = wrap_handler->ha_index_read_map(buf,
+ key,
+ pk_keypart_map,
+ HA_READ_KEY_EXACT);
+#else
+ error = wrap_handler->index_read_map(buf,
+ key,
+ pk_keypart_map,
+ HA_READ_KEY_EXACT);
+#endif
+ }
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_get_next_geo_record(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ mrn_change_encoding(ctx, NULL);
+ do {
+ GRN_BULK_REWIND(&key_buffer);
+ grn_id found_record_id;
+ grn_posting *posting;
+ posting = grn_geo_cursor_next(ctx, cursor_geo);
+ if (!posting) {
+ error = HA_ERR_END_OF_FILE;
+ clear_cursor_geo();
+ break;
+ }
+ found_record_id = posting->rid;
+ grn_table_get_key(ctx, grn_table, found_record_id,
+ GRN_TEXT_VALUE(&key_buffer),
+ table->key_info->key_length);
+ error = wrapper_get_record(buf, (const uchar *)GRN_TEXT_VALUE(&key_buffer));
+ } while (error == HA_ERR_END_OF_FILE || error == HA_ERR_KEY_NOT_FOUND);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_get_next_record(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (cursor_geo) {
+ grn_posting *posting;
+ posting = grn_geo_cursor_next(ctx, cursor_geo);
+ if (posting) {
+ record_id = posting->rid;
+ } else {
+ record_id = GRN_ID_NIL;
+ }
+ } else if (cursor) {
+ record_id = grn_table_cursor_next(ctx, cursor);
+ } else if (empty_value_records_cursor) {
+ grn_id empty_value_record_id;
+ empty_value_record_id =
+ grn_table_cursor_next(ctx, empty_value_records_cursor);
+ if (empty_value_record_id == GRN_ID_NIL) {
+ record_id = GRN_ID_NIL;
+ } else {
+ grn_table_get_key(ctx, empty_value_records, empty_value_record_id,
+ &record_id, sizeof(grn_id));
+ }
+ } else {
+ record_id = GRN_ID_NIL;
+ }
+ if (ctx->rc) {
+ int error = ER_ERROR_ON_READ;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ if (record_id == GRN_ID_NIL) {
+ DBUG_PRINT("info", ("mroonga: storage_get_next_record: end-of-file"));
+ table->status = STATUS_NOT_FOUND;
+ DBUG_RETURN(HA_ERR_END_OF_FILE);
+ }
+ if (buf) {
+ if (ignoring_no_key_columns)
+ storage_store_fields_by_index(buf);
+ else
+ storage_store_fields(buf, record_id);
+ if (cursor_geo && grn_source_column_geo) {
+ int latitude, longitude;
+ GRN_GEO_POINT_VALUE(&source_point, latitude, longitude);
+ double latitude_in_degree = GRN_GEO_MSEC2DEGREE(latitude);
+ double longitude_in_degree = GRN_GEO_MSEC2DEGREE(longitude);
+ if (!((bottom_right_latitude_in_degree <= latitude_in_degree &&
+ latitude_in_degree <= top_left_latitude_in_degree) &&
+ (top_left_longitude_in_degree <= longitude_in_degree &&
+ longitude_in_degree <= bottom_right_longitude_in_degree))) {
+ DBUG_PRINT("info",
+ ("mroonga: remove not contained geo point: "
+ "<%g,%g>(<%d,%d>); key: <%g,%g>(<%d,%d>), <%g,%g>(<%d,%d>)",
+ latitude_in_degree, longitude_in_degree,
+ latitude, longitude,
+ top_left_latitude_in_degree, top_left_longitude_in_degree,
+ GRN_GEO_DEGREE2MSEC(top_left_latitude_in_degree),
+ GRN_GEO_DEGREE2MSEC(top_left_longitude_in_degree),
+ bottom_right_latitude_in_degree,
+ bottom_right_longitude_in_degree,
+ GRN_GEO_DEGREE2MSEC(bottom_right_latitude_in_degree),
+ GRN_GEO_DEGREE2MSEC(bottom_right_longitude_in_degree)));
+ int error = storage_get_next_record(buf);
+ DBUG_RETURN(error);
+ }
+ }
+ }
+ table->status = 0;
+ DBUG_RETURN(0);
+}
+
+void ha_mroonga::geo_store_rectangle(const uchar *rectangle)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ double locations[4];
+ for (int i = 0; i < 4; i++) {
+ uchar reversed_value[8];
+ for (int j = 0; j < 8; j++) {
+ reversed_value[j] = (rectangle + (8 * i))[7 - j];
+ }
+ mi_float8get(locations[i], reversed_value);
+ }
+ top_left_longitude_in_degree = locations[0];
+ bottom_right_longitude_in_degree = locations[1];
+ bottom_right_latitude_in_degree = locations[2];
+ top_left_latitude_in_degree = locations[3];
+ int top_left_latitude = GRN_GEO_DEGREE2MSEC(top_left_latitude_in_degree);
+ int top_left_longitude = GRN_GEO_DEGREE2MSEC(top_left_longitude_in_degree);
+ int bottom_right_latitude = GRN_GEO_DEGREE2MSEC(bottom_right_latitude_in_degree);
+ int bottom_right_longitude = GRN_GEO_DEGREE2MSEC(bottom_right_longitude_in_degree);
+ GRN_GEO_POINT_SET(ctx, &top_left_point,
+ top_left_latitude, top_left_longitude);
+ GRN_GEO_POINT_SET(ctx, &bottom_right_point,
+ bottom_right_latitude, bottom_right_longitude);
+
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::generic_geo_open_cursor(const uchar *key,
+ enum ha_rkey_function find_flag)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ int flags = 0;
+ if (find_flag & HA_READ_MBR_CONTAIN) {
+ grn_obj *index = grn_index_columns[active_index];
+ geo_store_rectangle(key);
+ cursor_geo = grn_geo_cursor_open_in_rectangle(ctx,
+ index,
+ &top_left_point,
+ &bottom_right_point,
+ 0, -1);
+ if (cursor_geo) {
+ if (grn_source_column_geo) {
+ grn_obj_unlink(ctx, grn_source_column_geo);
+ }
+ grn_obj sources;
+ GRN_OBJ_INIT(&sources, GRN_BULK, 0, GRN_ID_NIL);
+ grn_obj_get_info(ctx, index, GRN_INFO_SOURCE, &sources);
+ grn_source_column_geo = grn_ctx_at(ctx, GRN_RECORD_VALUE(&sources));
+ grn_obj_unlink(ctx, &sources);
+ }
+ } else {
+ push_warning_unsupported_spatial_index_search(find_flag);
+ cursor = grn_table_cursor_open(ctx, grn_table, NULL, 0, NULL, 0,
+ 0, -1, flags);
+ }
+ if (ctx->rc) {
+ error = ER_ERROR_ON_READ;
+ my_message(error, ctx->errbuf, MYF(0));
+ }
+ DBUG_RETURN(error);
+}
+
+bool ha_mroonga::is_dry_write()
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool dry_write_p = THDVAR(ha_thd(), dry_write);
+ DBUG_RETURN(dry_write_p);
+}
+
+bool ha_mroonga::is_enable_optimization()
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool enable_optimization_p = THDVAR(ha_thd(), enable_optimization);
+ DBUG_RETURN(enable_optimization_p);
+}
+
+bool ha_mroonga::should_normalize(Field *field) const
+{
+ MRN_DBUG_ENTER_METHOD();
+ mrn::FieldNormalizer field_normalizer(ctx, ha_thd(), field);
+ bool need_normalize_p = field_normalizer.should_normalize();
+ DBUG_RETURN(need_normalize_p);
+}
+
+bool ha_mroonga::is_temporary_table_name(const char *name) const
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_PRINT("info", ("mroonga: table name = %s", name));
+#ifdef MRN_USE_MYSQL_DATA_HOME
+ bool temporary_table_name_p = false;
+ if (name[0] != '.') {
+ int len = strlen(name);
+ int mysql_data_home_len = strlen(mysql_data_home);
+ if (len < mysql_data_home_len ||
+ strncmp(name, mysql_data_home, mysql_data_home_len) ||
+ !strchr(&name[mysql_data_home_len], FN_LIBCHAR)) {
+ temporary_table_name_p = true;
+ }
+ }
+#else
+ bool temporary_table_name_p = (name[0] != '.');
+#endif
+ DBUG_RETURN(temporary_table_name_p);
+}
+
+void ha_mroonga::check_count_skip(key_part_map start_key_part_map,
+ key_part_map end_key_part_map, bool fulltext)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ if (!is_enable_optimization()) {
+ DBUG_PRINT("info", ("mroonga: count skip: optimization is disabled"));
+ count_skip = false;
+ DBUG_VOID_RETURN;
+ }
+
+ st_select_lex *select_lex = table->pos_in_table_list->select_lex;
+
+ if (
+ thd_sql_command(ha_thd()) == SQLCOM_SELECT &&
+ !select_lex->non_agg_fields.elements &&
+ !select_lex->group_list.elements &&
+ !select_lex->having &&
+ select_lex->table_list.elements == 1
+ ) {
+ Item *info = (Item *) select_lex->item_list.first_node()->info;
+ if (
+ info->type() != Item::SUM_FUNC_ITEM ||
+ ((Item_sum *) info)->sum_func() != Item_sum::COUNT_FUNC ||
+ ((Item_sum *) info)->nest_level ||
+ ((Item_sum *) info)->aggr_level ||
+ ((Item_sum *) info)->max_arg_level != -1 ||
+ ((Item_sum *) info)->max_sum_func_level != -1
+ ) {
+ DBUG_PRINT("info", ("mroonga: count skip: sum func is not match"));
+ count_skip = false;
+ DBUG_VOID_RETURN;
+ }
+
+ uint i = 0;
+ Item *where;
+ if (fulltext) {
+ DBUG_PRINT("info", ("mroonga: count skip: fulltext"));
+ where = select_lex->where;
+ if (!where ||
+ where->type() != Item::FUNC_ITEM ||
+ ((Item_func *)where)->functype() != Item_func::FT_FUNC) {
+ DBUG_PRINT("info", ("mroonga: count skip: ft func is not match"));
+ count_skip = false;
+ DBUG_VOID_RETURN;
+ }
+ where = where->next;
+ if (!where ||
+ where->type() != Item::STRING_ITEM) {
+ DBUG_PRINT("info", ("mroonga: count skip: string item is not match"));
+ count_skip = false;
+ DBUG_VOID_RETURN;
+ }
+ for (where = where->next; where; where = where->next) {
+ if (where->type() != Item::FIELD_ITEM)
+ break;
+ DBUG_PRINT("info", ("mroonga: count skip: FIELD_ITEM=%p", where));
+ }
+ if (where != info) {
+ DBUG_PRINT("info", ("mroonga: count skip: where clause is not match"));
+ count_skip = false;
+ DBUG_VOID_RETURN;
+ }
+ if (share->wrapper_mode &&
+ !(wrap_handler->ha_table_flags() & HA_NO_TRANSACTIONS)) {
+ DBUG_PRINT("info", ("mroonga: count skip: transactional wrapper mode"));
+ count_skip = false;
+ DBUG_VOID_RETURN;
+ }
+ DBUG_PRINT("info", ("mroonga: count skip: skip enabled"));
+ count_skip = true;
+ mrn_count_skip++;
+ DBUG_VOID_RETURN;
+ } else if (share->wrapper_mode) {
+ DBUG_PRINT("info", ("mroonga: count skip: wrapper mode"));
+ count_skip = false;
+ DBUG_VOID_RETURN;
+ } else {
+ DBUG_PRINT("info", ("mroonga: count skip: without fulltext"));
+ uint key_nr = active_index;
+ KEY key_info = table->key_info[key_nr];
+ KEY_PART_INFO *key_part = key_info.key_part;
+ for (where = select_lex->where; where; where = where->next) {
+ if (where->type() == Item::FIELD_ITEM)
+ {
+ Field *field = ((Item_field *)where)->field;
+ if (!field)
+ break;
+ if (field->table != table)
+ break;
+ uint j;
+ for (j = 0; j < KEY_N_KEY_PARTS(&key_info); j++) {
+ if (key_part[j].field == field)
+ {
+ if (!(start_key_part_map >> j) && !(end_key_part_map >> j))
+ j = KEY_N_KEY_PARTS(&key_info);
+ else
+ i++;
+ break;
+ }
+ }
+ if (j >= KEY_N_KEY_PARTS(&key_info))
+ break;
+ }
+ if (i >= select_lex->select_n_where_fields)
+ {
+ DBUG_PRINT("info", ("mroonga: count skip: skip enabled"));
+ count_skip = true;
+ mrn_count_skip++;
+ DBUG_VOID_RETURN;
+ }
+ }
+ DBUG_PRINT("info", ("mroonga: count skip: skip disabled"));
+ }
+ }
+ DBUG_PRINT("info", ("mroonga: count skip: select type is not match"));
+ count_skip = false;
+ DBUG_VOID_RETURN;
+}
+
+bool ha_mroonga::is_grn_zero_column_value(grn_obj *column, grn_obj *value)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ if (column->header.type != GRN_COLUMN_FIX_SIZE) {
+ DBUG_RETURN(false);
+ }
+
+ char *bytes = GRN_BULK_HEAD(value);
+ unsigned int size = GRN_BULK_VSIZE(value);
+ for (unsigned int i = 0; i < size; ++i) {
+ if (bytes[i] != '\0') {
+ DBUG_RETURN(false);
+ }
+ }
+
+ DBUG_RETURN(true);
+}
+
+void ha_mroonga::check_fast_order_limit(grn_table_sort_key **sort_keys,
+ int *n_sort_keys,
+ longlong *limit)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ if (!is_enable_optimization()) {
+ DBUG_PRINT("info", ("mroonga: fast order limit: optimization is disabled"));
+ fast_order_limit = false;
+ DBUG_VOID_RETURN;
+ }
+
+ TABLE_LIST *table_list = table->pos_in_table_list;
+ st_select_lex *select_lex = table_list->select_lex;
+ SELECT_LEX_UNIT *unit = table_list->derived;
+ st_select_lex *first_select_lex;
+ if (unit)
+ {
+ first_select_lex = unit->first_select();
+ } else {
+ first_select_lex = select_lex;
+ }
+ DBUG_PRINT("info",
+ ("mroonga: first_select_lex->options=%llu",
+ first_select_lex ? first_select_lex->options : 0));
+
+ if (
+ thd_sql_command(ha_thd()) == SQLCOM_SELECT &&
+ !select_lex->with_sum_func &&
+ !select_lex->group_list.elements &&
+ !select_lex->having &&
+ select_lex->table_list.elements == 1 &&
+ select_lex->order_list.elements &&
+ select_lex->explicit_limit &&
+ select_lex->select_limit &&
+ select_lex->select_limit->val_int() > 0
+ ) {
+ if (select_lex->offset_limit) {
+ *limit = select_lex->offset_limit->val_int();
+ } else {
+ *limit = 0;
+ }
+ *limit += select_lex->select_limit->val_int();
+ if (*limit > (longlong)INT_MAX) {
+ DBUG_PRINT("info",
+ ("mroonga: fast_order_limit = false: "
+ "too long limit: %lld <= %d is required",
+ *limit, INT_MAX));
+ fast_order_limit = false;
+ DBUG_VOID_RETURN;
+ }
+ if (first_select_lex && (first_select_lex->options & OPTION_FOUND_ROWS)) {
+ DBUG_PRINT("info",
+ ("mroonga: fast_order_limit = false: "
+ "SQL_CALC_FOUND_ROWS is specified"));
+ fast_order_limit = false;
+ DBUG_VOID_RETURN;
+ }
+ Item *where = select_lex->where;
+ const Item_func *match_against = NULL;
+ if (where) {
+ bool is_storage_mode = !(share->wrapper_mode);
+ mrn::ConditionConverter converter(ctx, grn_table, is_storage_mode);
+ if (!converter.is_convertable(where)) {
+ DBUG_PRINT("info",
+ ("mroonga: fast_order_limit = false: "
+ "not groonga layer condition search"));
+ fast_order_limit = false;
+ DBUG_VOID_RETURN;
+ }
+ match_against = converter.find_match_against(where);
+ if (!match_against) {
+ DBUG_PRINT("info",
+ ("mroonga: fast_order_limit = false: "
+ "groonga layer condition but not fulltext search"));
+ fast_order_limit = false;
+ DBUG_VOID_RETURN;
+ }
+ }
+ *n_sort_keys = select_lex->order_list.elements;
+ *sort_keys = (grn_table_sort_key *)malloc(sizeof(grn_table_sort_key) *
+ *n_sort_keys);
+ ORDER *order;
+ int i;
+ mrn_change_encoding(ctx, system_charset_info);
+ for (order = (ORDER *) select_lex->order_list.first, i = 0; order;
+ order = order->next, i++) {
+ Item *item = *order->item;
+ if (grn_columns && item->type() == Item::FIELD_ITEM)
+ {
+ Field *field = ((Item_field *) (*order->item))->field;
+ const char *column_name = field->field_name;
+ int column_name_size = strlen(column_name);
+
+ if (should_normalize(field))
+ {
+ DBUG_PRINT("info", ("mroonga: fast_order_limit = false: "
+ "sort by collated value isn't supported yet."));
+ fast_order_limit = false;
+ free(*sort_keys);
+ *sort_keys = NULL;
+ *n_sort_keys = 0;
+ DBUG_VOID_RETURN;
+ }
+
+ (*sort_keys)[i].key = grn_obj_column(ctx, matched_record_keys,
+ column_name, column_name_size);
+ } else if (!match_against || match_against->eq(item, true)) {
+ (*sort_keys)[i].key = grn_obj_column(ctx, matched_record_keys,
+ MRN_COLUMN_NAME_SCORE,
+ strlen(MRN_COLUMN_NAME_SCORE));
+ } else {
+ DBUG_PRINT("info", ("mroonga: fast_order_limit = false: "
+ "sort by computed value isn't supported."));
+ fast_order_limit = false;
+ free(*sort_keys);
+ *sort_keys = NULL;
+ *n_sort_keys = 0;
+ DBUG_VOID_RETURN;
+ }
+ (*sort_keys)[i].offset = 0;
+ if (MRN_ORDER_IS_ASC(order))
+ {
+ (*sort_keys)[i].flags = GRN_TABLE_SORT_ASC;
+ } else {
+ (*sort_keys)[i].flags = GRN_TABLE_SORT_DESC;
+ }
+ }
+ DBUG_PRINT("info", ("mroonga: fast_order_limit = true"));
+ fast_order_limit = true;
+ mrn_fast_order_limit++;
+ DBUG_VOID_RETURN;
+ }
+ DBUG_PRINT("info", ("mroonga: fast_order_limit = false"));
+ fast_order_limit = false;
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::generic_store_bulk_fixed_size_string(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ grn_obj_reinit(ctx, buf, GRN_DB_SHORT_TEXT, 0);
+ GRN_TEXT_SET(ctx, buf, field->ptr, field->field_length);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_store_bulk_variable_size_string(Field *field,
+ grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ String value;
+ field->val_str(NULL, &value);
+ grn_obj_reinit(ctx, buf, GRN_DB_SHORT_TEXT, 0);
+ DBUG_PRINT("info", ("mroonga: length=%u", value.length()));
+ DBUG_PRINT("info", ("mroonga: value=%s", value.c_ptr_safe()));
+ GRN_TEXT_SET(ctx, buf, value.ptr(), value.length());
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_store_bulk_integer(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ long long value = field->val_int();
+ DBUG_PRINT("info", ("mroonga: value=%lld", value));
+ uint32 size = field->pack_length();
+ DBUG_PRINT("info", ("mroonga: size=%u", size));
+ Field_num *field_num = static_cast<Field_num *>(field);
+ bool is_unsigned = field_num->unsigned_flag;
+ DBUG_PRINT("info", ("mroonga: is_unsigned=%s", is_unsigned ? "true" : "false"));
+ switch (size) {
+ case 1:
+ if (is_unsigned) {
+ grn_obj_reinit(ctx, buf, GRN_DB_UINT8, 0);
+ GRN_UINT8_SET(ctx, buf, value);
+ } else {
+ grn_obj_reinit(ctx, buf, GRN_DB_INT8, 0);
+ GRN_INT8_SET(ctx, buf, value);
+ }
+ break;
+ case 2:
+ if (is_unsigned) {
+ grn_obj_reinit(ctx, buf, GRN_DB_UINT16, 0);
+ GRN_UINT16_SET(ctx, buf, value);
+ } else {
+ grn_obj_reinit(ctx, buf, GRN_DB_INT16, 0);
+ GRN_INT16_SET(ctx, buf, value);
+ }
+ break;
+ case 3:
+ case 4:
+ if (is_unsigned) {
+ grn_obj_reinit(ctx, buf, GRN_DB_UINT32, 0);
+ GRN_UINT32_SET(ctx, buf, value);
+ } else {
+ grn_obj_reinit(ctx, buf, GRN_DB_INT32, 0);
+ GRN_INT32_SET(ctx, buf, value);
+ }
+ break;
+ case 8:
+ if (is_unsigned) {
+ grn_obj_reinit(ctx, buf, GRN_DB_UINT64, 0);
+ GRN_UINT64_SET(ctx, buf, value);
+ } else {
+ grn_obj_reinit(ctx, buf, GRN_DB_INT64, 0);
+ GRN_INT64_SET(ctx, buf, value);
+ }
+ break;
+ default:
+ // Why!?
+ error = HA_ERR_UNSUPPORTED;
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "unknown integer value size: <%u>: "
+ "available sizes: [1, 2, 3, 4, 8]",
+ size);
+ push_warning(ha_thd(), Sql_condition::WARN_LEVEL_WARN,
+ error, error_message);
+ break;
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_store_bulk_unsigned_integer(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ long long signed_value = field->val_int();
+ unsigned long long unsigned_value = *((unsigned long long *)(&signed_value));
+ uint32 size = field->pack_length();
+ switch (size) {
+ case 1:
+ grn_obj_reinit(ctx, buf, GRN_DB_UINT8, 0);
+ GRN_UINT8_SET(ctx, buf, unsigned_value);
+ break;
+ case 2:
+ grn_obj_reinit(ctx, buf, GRN_DB_UINT16, 0);
+ GRN_UINT16_SET(ctx, buf, unsigned_value);
+ break;
+ case 3:
+ case 4:
+ grn_obj_reinit(ctx, buf, GRN_DB_UINT32, 0);
+ GRN_UINT32_SET(ctx, buf, unsigned_value);
+ break;
+ case 8:
+ grn_obj_reinit(ctx, buf, GRN_DB_UINT64, 0);
+ GRN_UINT64_SET(ctx, buf, unsigned_value);
+ break;
+ default:
+ // Why!?
+ error = HA_ERR_UNSUPPORTED;
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "unknown unsigned integer value size: <%u>: "
+ "available sizes: [1, 2, 3, 4, 8]",
+ size);
+ push_warning(ha_thd(), Sql_condition::WARN_LEVEL_WARN,
+ error, error_message);
+ break;
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_store_bulk_float(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ double value = field->val_real();
+ uint32 size = field->pack_length();
+ switch (size) {
+ case 4:
+ case 8:
+ grn_obj_reinit(ctx, buf, GRN_DB_FLOAT, 0);
+ GRN_FLOAT_SET(ctx, buf, value);
+ break;
+ default:
+ // Why!?
+ error = HA_ERR_UNSUPPORTED;
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "unknown float value size: <%u>: "
+ "available sizes: [4, 8]",
+ size);
+ push_warning(ha_thd(), Sql_condition::WARN_LEVEL_WARN,
+ error, error_message);
+ break;
+ }
+ DBUG_RETURN(error);
+}
+
+long long int ha_mroonga::get_grn_time_from_timestamp_field(Field_timestamp *field)
+{
+ MRN_DBUG_ENTER_METHOD();
+ long long int grn_time = 0;
+#ifdef MRN_TIMESTAMP_USE_TIMEVAL
+ int warnings = 0;
+ struct timeval time_value;
+ if (field->get_timestamp(&time_value, &warnings)) {
+ // XXX: Should we report warnings or MySQL does?
+ } else {
+ DBUG_PRINT("info", ("mroonga: timeval tv_sec=%ld", time_value.tv_sec));
+ grn_time = GRN_TIME_PACK(time_value.tv_sec, time_value.tv_usec);
+ }
+#elif defined(MRN_TIMESTAMP_USE_MY_TIME_T)
+ unsigned long int micro_seconds;
+ my_time_t seconds = field->get_timestamp(&micro_seconds);
+ DBUG_PRINT("info", ("mroonga: my_time_t seconds=%ld", seconds));
+ grn_time = GRN_TIME_PACK(seconds, micro_seconds);
+#else
+ my_bool is_null_value;
+ long seconds = field->get_timestamp(&is_null_value);
+ DBUG_PRINT("info", ("mroonga: long seconds=%ld", seconds));
+ grn_time = GRN_TIME_PACK(seconds, 0);
+#endif
+ DBUG_RETURN(grn_time);
+}
+
+int ha_mroonga::generic_store_bulk_timestamp(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ Field_timestamp *timestamp_field = (Field_timestamp *)field;
+ long long int time = get_grn_time_from_timestamp_field(timestamp_field);
+ grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0);
+ GRN_TIME_SET(ctx, buf, time);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_store_bulk_date(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ bool truncated = false;
+ long long int date_value = field->val_int();
+ struct tm date;
+ memset(&date, 0, sizeof(struct tm));
+ date.tm_year = date_value / 10000 % 10000 - mrn::TimeConverter::TM_YEAR_BASE;
+ date.tm_mon = date_value / 100 % 100 - 1;
+ date.tm_mday = date_value % 100;
+ int usec = 0;
+ mrn::TimeConverter time_converter;
+ long long int time = time_converter.tm_to_grn_time(&date, usec, &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0);
+ GRN_TIME_SET(ctx, buf, time);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_store_bulk_time(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ bool truncated = false;
+ Field_time *time_field = (Field_time *)field;
+ MYSQL_TIME mysql_time;
+ time_field->get_time(&mysql_time);
+ mrn::TimeConverter time_converter;
+ long long int time = time_converter.mysql_time_to_grn_time(&mysql_time,
+ &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0);
+ GRN_TIME_SET(ctx, buf, time);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_store_bulk_datetime(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ bool truncated = false;
+ Field_datetime *datetime_field = (Field_datetime *)field;
+ MYSQL_TIME mysql_time;
+ datetime_field->get_time(&mysql_time);
+ mrn::TimeConverter time_converter;
+ long long int time = time_converter.mysql_time_to_grn_time(&mysql_time,
+ &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0);
+ GRN_TIME_SET(ctx, buf, time);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_store_bulk_year(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ bool truncated = false;
+
+ int year;
+ if (field->field_length == 2) {
+ year = static_cast<int>(field->val_int() + 2000);
+ } else {
+ year = static_cast<int>(field->val_int());
+ }
+
+ DBUG_PRINT("info", ("mroonga: year=%d", year));
+ struct tm date;
+ memset(&date, 0, sizeof(struct tm));
+ date.tm_year = year - mrn::TimeConverter::TM_YEAR_BASE;
+ date.tm_mon = 0;
+ date.tm_mday = 1;
+
+ int usec = 0;
+ mrn::TimeConverter time_converter;
+ long long int time = time_converter.tm_to_grn_time(&date, usec, &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0);
+ GRN_TIME_SET(ctx, buf, time);
+ DBUG_RETURN(error);
+}
+
+#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
+int ha_mroonga::generic_store_bulk_datetime2(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ bool truncated = false;
+ Field_datetimef *datetimef_field = (Field_datetimef *)field;
+ MYSQL_TIME mysql_time;
+ datetimef_field->get_time(&mysql_time);
+ mrn::TimeConverter time_converter;
+ long long int time = time_converter.mysql_time_to_grn_time(&mysql_time,
+ &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0);
+ GRN_TIME_SET(ctx, buf, time);
+ DBUG_RETURN(error);
+}
+#endif
+
+#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
+int ha_mroonga::generic_store_bulk_time2(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ bool truncated = false;
+ MYSQL_TIME mysql_time;
+ field->get_time(&mysql_time);
+ mrn::TimeConverter time_converter;
+ long long int time = time_converter.mysql_time_to_grn_time(&mysql_time,
+ &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0);
+ GRN_TIME_SET(ctx, buf, time);
+ DBUG_RETURN(error);
+}
+#endif
+
+int ha_mroonga::generic_store_bulk_new_date(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ bool truncated = false;
+ Field_newdate *newdate_field = (Field_newdate *)field;
+ MYSQL_TIME mysql_date;
+ newdate_field->get_time(&mysql_date);
+ mrn::TimeConverter time_converter;
+ long long int time = time_converter.mysql_time_to_grn_time(&mysql_date,
+ &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ grn_obj_reinit(ctx, buf, GRN_DB_TIME, 0);
+ GRN_TIME_SET(ctx, buf, time);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_store_bulk_new_decimal(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ String value;
+ Field_new_decimal *new_decimal_field = (Field_new_decimal *)field;
+ new_decimal_field->val_str(&value, NULL);
+ grn_obj_reinit(ctx, buf, GRN_DB_SHORT_TEXT, 0);
+ GRN_TEXT_SET(ctx, buf, value.ptr(), value.length());
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_store_bulk_blob(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ String buffer;
+ Field_blob *blob = (Field_blob *)field;
+ const char *value = blob->val_str(0, &buffer)->ptr();
+ grn_obj_reinit(ctx, buf, GRN_DB_TEXT, 0);
+ GRN_TEXT_SET(ctx, buf, value, blob->get_length());
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_store_bulk_geometry(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+#ifdef HAVE_SPATIAL
+ String buffer;
+ Field_geom *geometry = (Field_geom *)field;
+ const char *wkb = geometry->val_str(0, &buffer)->ptr();
+ int len = geometry->get_length();
+ error = mrn_set_geometry(ctx, buf, wkb, len);
+#endif
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_store_bulk(Field *field, grn_obj *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error;
+ error = mrn_change_encoding(ctx, field->charset());
+ if (error)
+ return error;
+ switch (field->real_type()) {
+ case MYSQL_TYPE_DECIMAL:
+ error = generic_store_bulk_variable_size_string(field, buf);
+ break;
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ error = generic_store_bulk_integer(field, buf);
+ break;
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ error = generic_store_bulk_float(field, buf);
+ break;
+ case MYSQL_TYPE_NULL:
+ error = generic_store_bulk_unsigned_integer(field, buf);
+ break;
+ case MYSQL_TYPE_TIMESTAMP:
+ error = generic_store_bulk_timestamp(field, buf);
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ error = generic_store_bulk_integer(field, buf);
+ break;
+ case MYSQL_TYPE_DATE:
+ error = generic_store_bulk_date(field, buf);
+ break;
+ case MYSQL_TYPE_TIME:
+ error = generic_store_bulk_time(field, buf);
+ break;
+ case MYSQL_TYPE_DATETIME:
+ error = generic_store_bulk_datetime(field, buf);
+ break;
+ case MYSQL_TYPE_YEAR:
+ error = generic_store_bulk_year(field, buf);
+ break;
+ case MYSQL_TYPE_NEWDATE:
+ error = generic_store_bulk_new_date(field, buf);
+ break;
+ case MYSQL_TYPE_VARCHAR:
+ error = generic_store_bulk_variable_size_string(field, buf);
+ break;
+ case MYSQL_TYPE_BIT:
+ error = generic_store_bulk_unsigned_integer(field, buf);
+ break;
+#ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2
+ case MYSQL_TYPE_TIMESTAMP2:
+ error = generic_store_bulk_timestamp(field, buf);
+ break;
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
+ case MYSQL_TYPE_DATETIME2:
+ error = generic_store_bulk_datetime2(field, buf);
+ break;
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
+ case MYSQL_TYPE_TIME2:
+ error = generic_store_bulk_time2(field, buf);
+ break;
+#endif
+ case MYSQL_TYPE_NEWDECIMAL:
+ error = generic_store_bulk_new_decimal(field, buf);
+ break;
+ case MYSQL_TYPE_ENUM:
+ error = generic_store_bulk_unsigned_integer(field, buf);
+ break;
+ case MYSQL_TYPE_SET:
+ error = generic_store_bulk_unsigned_integer(field, buf);
+ break;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ error = generic_store_bulk_blob(field, buf);
+ break;
+ case MYSQL_TYPE_VAR_STRING:
+ error = generic_store_bulk_variable_size_string(field, buf);
+ break;
+ case MYSQL_TYPE_STRING:
+ error = generic_store_bulk_fixed_size_string(field, buf);
+ break;
+ case MYSQL_TYPE_GEOMETRY:
+ error = generic_store_bulk_geometry(field, buf);
+ break;
+ default:
+ error = HA_ERR_UNSUPPORTED;
+ break;
+ }
+ DBUG_RETURN(error);
+}
+
+void ha_mroonga::storage_store_field_string(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ field->store(value, value_length, field->charset());
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_store_field_integer(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ Field_num *field_num = static_cast<Field_num *>(field);
+ bool is_unsigned = field_num->unsigned_flag;
+ switch (value_length) {
+ case 1:
+ {
+ if (is_unsigned) {
+ unsigned char field_value;
+ field_value = *((unsigned char *)value);
+ field->store(field_value, is_unsigned);
+ } else {
+ char field_value;
+ field_value = *((char *)value);
+ field->store(field_value, is_unsigned);
+ }
+ break;
+ }
+ case 2:
+ {
+ if (is_unsigned) {
+ unsigned short field_value;
+ field_value = *((unsigned short *)value);
+ field->store(field_value, is_unsigned);
+ } else {
+ short field_value;
+ field_value = *((short *)value);
+ field->store(field_value, is_unsigned);
+ }
+ break;
+ }
+ case 4:
+ {
+ if (is_unsigned) {
+ unsigned int field_value;
+ field_value = *((unsigned int *)value);
+ field->store(field_value, is_unsigned);
+ } else {
+ int field_value;
+ field_value = *((int *)value);
+ field->store(field_value, is_unsigned);
+ }
+ break;
+ }
+ case 8:
+ {
+ if (is_unsigned) {
+ unsigned long long int field_value;
+ field_value = *((unsigned long long int *)value);
+ DBUG_PRINT("info", ("mroonga: field_value=%llu", field_value));
+ field->store(field_value, is_unsigned);
+ } else {
+ long long int field_value;
+ field_value = *((long long int *)value);
+ field->store(field_value, is_unsigned);
+ }
+ break;
+ }
+ default:
+ {
+ // Why!?
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "unknown integer value size: <%d>: "
+ "available sizes: [1, 2, 4, 8]",
+ value_length);
+ push_warning(ha_thd(), Sql_condition::WARN_LEVEL_WARN,
+ HA_ERR_UNSUPPORTED, error_message);
+ storage_store_field_string(field, value, value_length);
+ break;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_store_field_unsigned_integer(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ switch (value_length) {
+ case 1:
+ {
+ unsigned char field_value;
+ field_value = *((unsigned char *)value);
+ field->store(field_value, true);
+ break;
+ }
+ case 2:
+ {
+ unsigned short field_value;
+ field_value = *((unsigned short *)value);
+ field->store(field_value, true);
+ break;
+ }
+ case 4:
+ {
+ unsigned int field_value;
+ field_value = *((unsigned int *)value);
+ field->store(field_value, true);
+ break;
+ }
+ case 8:
+ {
+ unsigned long long int field_value;
+ field_value = *((unsigned long long int *)value);
+ DBUG_PRINT("info", ("mroonga: field_value=%llu", field_value));
+ field->store(field_value, true);
+ break;
+ }
+ default:
+ {
+ // Why!?
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "unknown integer value size: <%d>: "
+ "available sizes: [1, 2, 4, 8]",
+ value_length);
+ push_warning(ha_thd(), Sql_condition::WARN_LEVEL_WARN,
+ HA_ERR_UNSUPPORTED, error_message);
+ storage_store_field_string(field, value, value_length);
+ break;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_store_field_float(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ double field_value;
+ field_value = *((double *)value);
+ field->store(field_value);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_store_field_timestamp(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ long long int time = *((long long int *)value);
+ Field_timestamp *timestamp_field = (Field_timestamp *)field;
+#ifdef MRN_TIMESTAMP_USE_TIMEVAL
+ struct timeval time_value;
+ GRN_TIME_UNPACK(time, time_value.tv_sec, time_value.tv_usec);
+ timestamp_field->store_timestamp(&time_value);
+#elif defined(MRN_TIMESTAMP_USE_MY_TIME_T)
+ long long int sec, usec;
+ GRN_TIME_UNPACK(time, sec, usec);
+ timestamp_field->store_TIME(static_cast<int32>(sec),
+ static_cast<int32>(usec));
+#else
+ int32 sec, usec __attribute__((unused));
+ GRN_TIME_UNPACK(time, sec, usec);
+ timestamp_field->store_timestamp(sec);
+#endif
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_store_field_date(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ long long int time = *((long long int *)value);
+ long long int sec, usec __attribute__((unused));
+ GRN_TIME_UNPACK(time, sec, usec);
+ struct tm date;
+ time_t sec_t = static_cast<int32>(sec);
+ gmtime_r(&sec_t, &date);
+ long long int date_in_mysql =
+ (date.tm_year + mrn::TimeConverter::TM_YEAR_BASE) * 10000 +
+ (date.tm_mon + 1) * 100 +
+ date.tm_mday;
+ field->store(date_in_mysql, false);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_store_field_time(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ long long int time = *((long long int *)value);
+ MYSQL_TIME mysql_time;
+ memset(&mysql_time, 0, sizeof(MYSQL_TIME));
+ mysql_time.time_type = MYSQL_TIMESTAMP_TIME;
+ mrn::TimeConverter time_converter;
+ time_converter.grn_time_to_mysql_time(time, &mysql_time);
+#ifdef MRN_FIELD_STORE_TIME_NEED_TYPE
+ Field_time *time_field = (Field_time *)field;
+ time_field->store_time(&mysql_time, mysql_time.time_type);
+#else
+ field->store_time(&mysql_time);
+#endif
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_store_field_datetime(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ long long int time = *((long long int *)value);
+ MYSQL_TIME mysql_datetime;
+ memset(&mysql_datetime, 0, sizeof(MYSQL_TIME));
+ mysql_datetime.time_type = MYSQL_TIMESTAMP_DATETIME;
+ mrn::TimeConverter time_converter;
+ time_converter.grn_time_to_mysql_time(time, &mysql_datetime);
+#ifdef MRN_FIELD_STORE_TIME_NEED_TYPE
+ Field_datetime *datetime_field = (Field_datetime *)field;
+ datetime_field->store_time(&mysql_datetime, mysql_datetime.time_type);
+#else
+ field->store_time(&mysql_datetime);
+#endif
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_store_field_year(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ long long int time = *((long long int *)value);
+ MYSQL_TIME mysql_time;
+ memset(&mysql_time, 0, sizeof(MYSQL_TIME));
+ mysql_time.time_type = MYSQL_TIMESTAMP_DATE;
+ mrn::TimeConverter time_converter;
+ time_converter.grn_time_to_mysql_time(time, &mysql_time);
+ DBUG_PRINT("info", ("mroonga: stored %d", mysql_time.year));
+ field->store(mysql_time.year, false);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_store_field_new_date(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ long long int time = *((long long int *)value);
+ MYSQL_TIME mysql_date;
+ memset(&mysql_date, 0, sizeof(MYSQL_TIME));
+ mysql_date.time_type = MYSQL_TIMESTAMP_DATE;
+ mrn::TimeConverter time_converter;
+ time_converter.grn_time_to_mysql_time(time, &mysql_date);
+#ifdef MRN_FIELD_STORE_TIME_NEED_TYPE
+ Field_newdate *newdate_field = (Field_newdate *)field;
+ newdate_field->store_time(&mysql_date, MYSQL_TIMESTAMP_DATE);
+#else
+ field->store_time(&mysql_date);
+#endif
+ DBUG_VOID_RETURN;
+}
+
+#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
+void ha_mroonga::storage_store_field_datetime2(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ long long int time = *((long long int *)value);
+ MYSQL_TIME mysql_datetime;
+ memset(&mysql_datetime, 0, sizeof(MYSQL_TIME));
+ mysql_datetime.time_type = MYSQL_TIMESTAMP_DATETIME;
+ mrn::TimeConverter time_converter;
+ time_converter.grn_time_to_mysql_time(time, &mysql_datetime);
+ field->store_time(&mysql_datetime);
+ DBUG_VOID_RETURN;
+}
+#endif
+
+#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
+void ha_mroonga::storage_store_field_time2(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ long long int time = *((long long int *)value);
+
+ MYSQL_TIME mysql_time;
+ memset(&mysql_time, 0, sizeof(MYSQL_TIME));
+ mysql_time.time_type = MYSQL_TIMESTAMP_TIME;
+ mrn::TimeConverter time_converter;
+ time_converter.grn_time_to_mysql_time(time, &mysql_time);
+ field->store_time(&mysql_time);
+ DBUG_VOID_RETURN;
+}
+#endif
+
+void ha_mroonga::storage_store_field_blob(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ Field_blob *blob = (Field_blob *)field;
+ String *blob_buffer = &blob_buffers[field->field_index];
+ blob_buffer->length(0);
+ blob_buffer->reserve(value_length);
+ blob_buffer->q_append(value, value_length);
+ blob->set_ptr((uint32) value_length, (uchar *) blob_buffer->ptr());
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_store_field_geometry(Field *field,
+ const char *value,
+ uint value_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+#ifdef HAVE_SPATIAL
+ uchar wkb[SRID_SIZE + WKB_HEADER_SIZE + POINT_DATA_SIZE];
+ grn_geo_point *field_value = (grn_geo_point *)value;
+ int latitude, longitude;
+ latitude = field_value->latitude;
+ longitude = field_value->longitude;
+ if (grn_source_column_geo) {
+ GRN_GEO_POINT_SET(ctx, &source_point, latitude, longitude);
+ }
+ memset(wkb, 0, SRID_SIZE);
+ memset(wkb + SRID_SIZE, Geometry::wkb_ndr, 1); // wkb_ndr is meaningless.
+ int4store(wkb + SRID_SIZE + 1, Geometry::wkb_point);
+ double latitude_in_degree, longitude_in_degree;
+ latitude_in_degree = GRN_GEO_MSEC2DEGREE(latitude);
+ longitude_in_degree = GRN_GEO_MSEC2DEGREE(longitude);
+ float8store(wkb + SRID_SIZE + WKB_HEADER_SIZE,
+ longitude_in_degree);
+ float8store(wkb + SRID_SIZE + WKB_HEADER_SIZE + SIZEOF_STORED_DOUBLE,
+ latitude_in_degree);
+ String *geometry_buffer = &blob_buffers[field->field_index];
+ geometry_buffer->length(0);
+ uint wkb_length = sizeof(wkb) / sizeof(*wkb);
+ Field_geom *geometry = (Field_geom *)field;
+ geometry_buffer->reserve(wkb_length);
+ geometry_buffer->q_append((const char *) wkb, wkb_length);
+ geometry->set_ptr((uint32) wkb_length, (uchar *) geometry_buffer->ptr());
+#endif
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_store_field(Field *field,
+ const char *value, uint value_length)
+{
+ field->set_notnull();
+ switch (field->real_type()) {
+ case MYSQL_TYPE_DECIMAL:
+ storage_store_field_string(field, value, value_length);
+ break;
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ storage_store_field_integer(field, value, value_length);
+ break;
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ storage_store_field_float(field, value, value_length);
+ break;
+ case MYSQL_TYPE_NULL:
+ storage_store_field_unsigned_integer(field, value, value_length);
+ break;
+ case MYSQL_TYPE_TIMESTAMP:
+ storage_store_field_timestamp(field, value, value_length);
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ storage_store_field_integer(field, value, value_length);
+ break;
+ case MYSQL_TYPE_DATE:
+ storage_store_field_date(field, value, value_length);
+ break;
+ case MYSQL_TYPE_TIME:
+ storage_store_field_time(field, value, value_length);
+ break;
+ case MYSQL_TYPE_DATETIME:
+ storage_store_field_datetime(field, value, value_length);
+ break;
+ case MYSQL_TYPE_YEAR:
+ storage_store_field_year(field, value, value_length);
+ break;
+ case MYSQL_TYPE_NEWDATE:
+ storage_store_field_new_date(field, value, value_length);
+ break;
+ case MYSQL_TYPE_VARCHAR:
+ storage_store_field_string(field, value, value_length);
+ break;
+ case MYSQL_TYPE_BIT:
+ storage_store_field_unsigned_integer(field, value, value_length);
+ break;
+#ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2
+ case MYSQL_TYPE_TIMESTAMP2:
+ storage_store_field_timestamp(field, value, value_length);
+ break;
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
+ case MYSQL_TYPE_DATETIME2:
+ storage_store_field_datetime2(field, value, value_length);
+ break;
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
+ case MYSQL_TYPE_TIME2:
+ storage_store_field_time2(field, value, value_length);
+ break;
+#endif
+ case MYSQL_TYPE_NEWDECIMAL:
+ storage_store_field_string(field, value, value_length);
+ break;
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ storage_store_field_unsigned_integer(field, value, value_length);
+ break;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ storage_store_field_blob(field, value, value_length);
+ break;
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ storage_store_field_string(field, value, value_length);
+ break;
+ case MYSQL_TYPE_GEOMETRY:
+ storage_store_field_geometry(field, value, value_length);
+ break;
+ }
+}
+
+void ha_mroonga::storage_store_fields(uchar *buf, grn_id record_id)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_PRINT("info", ("mroonga: stored record ID: %d", record_id));
+
+ my_ptrdiff_t ptr_diff = PTR_BYTE_DIFF(buf, table->record[0]);
+
+ Field *primary_key_field = NULL;
+ if (grn_table_is_referenced && table->s->primary_key != MAX_INDEXES) {
+ KEY *key_info = &(table->s->key_info[table->s->primary_key]);
+ primary_key_field = key_info->key_part[0].field;
+ }
+
+ int i;
+ int n_columns = table->s->fields;
+ for (i = 0; i < n_columns; i++) {
+ Field *field = table->field[i];
+
+ if (bitmap_is_set(table->read_set, field->field_index) ||
+ bitmap_is_set(table->write_set, field->field_index)) {
+ const char *column_name = field->field_name;
+
+ if (ignoring_no_key_columns) {
+ KEY key_info = table->s->key_info[active_index];
+ if (strcmp(key_info.key_part[0].field->field_name, column_name)) {
+ continue;
+ }
+ }
+
+ mrn::DebugColumnAccess debug_column_access(table, table->write_set);
+ DBUG_PRINT("info", ("mroonga: store column %d(%d)",i,field->field_index));
+ field->move_field_offset(ptr_diff);
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ // for _id column
+ field->set_notnull();
+ field->store((int)record_id);
+ } else if (primary_key_field &&
+ strcmp(primary_key_field->field_name, column_name) == 0) {
+ // for primary key column
+ char key[GRN_TABLE_MAX_KEY_SIZE];
+ int key_length;
+ key_length = grn_table_get_key(ctx, grn_table, record_id,
+ &key, GRN_TABLE_MAX_KEY_SIZE);
+ storage_store_field(field, key, key_length);
+ } else {
+ // actual column
+ const char *value;
+ uint32 value_length;
+ value = grn_obj_get_value_(ctx, grn_columns[i], record_id,
+ &value_length);
+ DBUG_PRINT("info", ("mroonga: value_length=%u",value_length));
+ grn_obj *range = grn_column_ranges[i];
+ // TODO: create mrn::is_grn_table() and use it.
+ if (GRN_TABLE_HASH_KEY <= range->header.type &&
+ range->header.type <= GRN_DB) {
+ // TODO: extract as a method
+ if (((grn_columns[i]->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) ==
+ GRN_OBJ_COLUMN_VECTOR)) {
+ // TODO: Check whether reference type or not
+ grn_obj unvectored_value;
+ GRN_TEXT_INIT(&unvectored_value, 0);
+ grn_id *ids = (grn_id *)value;
+ for (int i = 0; i * sizeof(grn_id) < value_length; i++) {
+ grn_id id = ids[i];
+ if (i > 0) {
+ GRN_TEXT_PUTS(ctx, &unvectored_value, mrn_vector_column_delimiter);
+ }
+ char key[GRN_TABLE_MAX_KEY_SIZE];
+ int key_length;
+ key_length = grn_table_get_key(ctx, range, id,
+ &key, GRN_TABLE_MAX_KEY_SIZE);
+ GRN_TEXT_PUT(ctx, &unvectored_value, key, key_length);
+ }
+ storage_store_field(field,
+ GRN_TEXT_VALUE(&unvectored_value),
+ GRN_TEXT_LEN(&unvectored_value));
+ GRN_OBJ_FIN(ctx, &unvectored_value);
+ } else {
+ grn_id id = *((grn_id *)value);
+ char key[GRN_TABLE_MAX_KEY_SIZE];
+ int key_length;
+ key_length = grn_table_get_key(ctx, range, id,
+ &key, GRN_TABLE_MAX_KEY_SIZE);
+ storage_store_field(field, key, key_length);
+ }
+ } else {
+ storage_store_field(field, value, value_length);
+ }
+ }
+ field->move_field_offset(-ptr_diff);
+ }
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_store_fields_for_prep_update(const uchar *old_data,
+ uchar *new_data,
+ grn_id record_id)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_PRINT("info", ("mroonga: stored record ID: %d", record_id));
+ my_ptrdiff_t ptr_diff_old = PTR_BYTE_DIFF(old_data, table->record[0]);
+ my_ptrdiff_t ptr_diff_new = 0;
+#ifdef MRN_RBR_UPDATE_NEED_ALL_COLUMNS
+ if (!written_by_row_based_binlog) {
+ if (check_written_by_row_based_binlog()) {
+ written_by_row_based_binlog = 2;
+ } else {
+ written_by_row_based_binlog = 1;
+ }
+ }
+ bool need_all_columns =
+ (new_data && written_by_row_based_binlog == 2);
+#endif
+ if (new_data) {
+ ptr_diff_new = PTR_BYTE_DIFF(new_data, table->record[0]);
+ }
+ int i;
+ int n_columns = table->s->fields;
+ for (i = 0; i < n_columns; i++) {
+ Field *field = table->field[i];
+
+ if (
+ !bitmap_is_set(table->read_set, field->field_index) &&
+ !bitmap_is_set(table->write_set, field->field_index) &&
+#ifdef MRN_RBR_UPDATE_NEED_ALL_COLUMNS
+ (
+ need_all_columns ||
+#endif
+ bitmap_is_set(&multiple_column_key_bitmap, field->field_index)
+#ifdef MRN_RBR_UPDATE_NEED_ALL_COLUMNS
+ )
+#endif
+ ) {
+ mrn::DebugColumnAccess debug_column_access(table, table->write_set);
+ DBUG_PRINT("info", ("mroonga: store column %d(%d)",i,field->field_index));
+ const char *value;
+ uint32 value_length;
+ value = grn_obj_get_value_(ctx, grn_columns[i], record_id,
+ &value_length);
+ // old column
+ field->move_field_offset(ptr_diff_old);
+ storage_store_field(field, value, value_length);
+ field->move_field_offset(-ptr_diff_old);
+ if (new_data) {
+ // new column
+ field->move_field_offset(ptr_diff_new);
+ storage_store_field(field, value, value_length);
+ field->move_field_offset(-ptr_diff_new);
+ }
+ }
+ }
+
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_store_fields_by_index(uchar *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint key_length;
+ void *key;
+ KEY *key_info = &table->key_info[active_index];
+ if (table->s->primary_key == active_index)
+ key_length = grn_table_cursor_get_key(ctx, cursor, &key);
+ else
+ key_length = grn_table_cursor_get_key(ctx, index_table_cursor, &key);
+
+ if (KEY_N_KEY_PARTS(key_info) == 1) {
+ my_ptrdiff_t ptr_diff = PTR_BYTE_DIFF(buf, table->record[0]);
+ Field *field = key_info->key_part->field;
+ mrn::DebugColumnAccess debug_column_access(table, table->write_set);
+ field->move_field_offset(ptr_diff);
+ storage_store_field(field, (const char *)key, key_length);
+ field->move_field_offset(-ptr_diff);
+ } else {
+ uchar enc_buf[MAX_KEY_LENGTH];
+ uint enc_len;
+ mrn::MultipleColumnKeyCodec codec(ctx, ha_thd(), key_info);
+ codec.decode(static_cast<uchar *>(key), key_length, enc_buf, &enc_len);
+ key_restore(buf, enc_buf, key_info, enc_len);
+ }
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::storage_encode_key_normalize_min_sort_chars(Field *field,
+ uchar *buf,
+ uint size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+
+ if (size == 0) {
+ DBUG_RETURN(0);
+ }
+ if (!field->has_charset()) {
+ DBUG_RETURN(0);
+ }
+
+ uint16 raw_min_sort_char =
+ static_cast<uint16>(field->sort_charset()->min_sort_char);
+ if (raw_min_sort_char <= UINT_MAX8) {
+ uchar min_sort_char = static_cast<uchar>(raw_min_sort_char);
+ for (uint i = size - 1; i > 0; --i) {
+ if (buf[i] != min_sort_char) {
+ break;
+ }
+ buf[i] = '\0';
+ }
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_encode_key_fixed_size_string(Field *field,
+ const uchar *key,
+ uchar *buf, uint *size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ memcpy(buf, key, field->field_length);
+ *size = field->field_length;
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_encode_key_variable_size_string(Field *field,
+ const uchar *key,
+ uchar *buf, uint *size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ *size = uint2korr(key);
+ memcpy(buf, key + HA_KEY_BLOB_LENGTH, *size);
+ storage_encode_key_normalize_min_sort_chars(field, buf, *size);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_encode_key_timestamp(Field *field, const uchar *key,
+ uchar *buf, uint *size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ bool truncated = false;
+ long long int time;
+ MYSQL_TIME mysql_time;
+#ifdef MRN_MARIADB_P
+ if (field->decimals() == 0) {
+ my_time_t my_time = sint4korr(key);
+ mrn_my_tz_UTC->gmt_sec_to_TIME(&mysql_time, my_time);
+ mysql_time.second_part = 0;
+ } else {
+ Field_timestamp_hires *timestamp_hires_field =
+ (Field_timestamp_hires *)field;
+ uint fuzzy_date = 0;
+ uchar *ptr_backup = field->ptr;
+ uchar *null_ptr_backup = field->null_ptr;
+ TABLE *table_backup = field->table;
+ field->ptr = (uchar *)key;
+ field->null_ptr = (uchar *)(key - 1);
+ field->table = table;
+ timestamp_hires_field->get_date(&mysql_time, fuzzy_date);
+ field->ptr = ptr_backup;
+ field->null_ptr = null_ptr_backup;
+ field->table = table_backup;
+ }
+#else
+ my_time_t my_time = uint4korr(key);
+ mrn_my_tz_UTC->gmt_sec_to_TIME(&mysql_time, my_time);
+#endif
+ mrn::TimeConverter time_converter;
+ time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ memcpy(buf, &time, 8);
+ *size = 8;
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_encode_key_time(Field *field, const uchar *key,
+ uchar *buf, uint *size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ long long int time;
+#ifdef MRN_MARIADB_P
+ MYSQL_TIME mysql_time;
+ bool truncated = false;
+ if (field->decimals() == 0) {
+ long long int packed_time = sint3korr(key);
+ mysql_time.neg = false;
+ if (packed_time < 0) {
+ mysql_time.neg = true;
+ packed_time = -packed_time;
+ }
+ mysql_time.year = 0;
+ mysql_time.month = 0;
+ mysql_time.day = 0;
+ mysql_time.hour = (int)(packed_time / 10000);
+ long long int minute_part = packed_time - mysql_time.hour * 10000;
+ mysql_time.minute = (int)(minute_part / 100);
+ mysql_time.second = (int)(minute_part % 100);
+ mysql_time.second_part = 0;
+ mysql_time.time_type = MYSQL_TIMESTAMP_TIME;
+ } else {
+ Field_time_hires *time_hires_field = (Field_time_hires *)field;
+ uint fuzzy_date = 0;
+ uchar *ptr_backup = field->ptr;
+ uchar *null_ptr_backup = field->null_ptr;
+ field->ptr = (uchar *)key;
+ field->null_ptr = (uchar *)(key - 1);
+ time_hires_field->get_date(&mysql_time, fuzzy_date);
+ field->ptr = ptr_backup;
+ field->null_ptr = null_ptr_backup;
+ }
+ mrn::TimeConverter time_converter;
+ time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+#else
+ int mysql_time = (int)sint3korr(key);
+ int sec =
+ mysql_time / 10000 * 60 * 60 +
+ mysql_time / 100 % 100 * 60 +
+ mysql_time % 60;
+ int usec = 0;
+ time = GRN_TIME_PACK(sec, usec);
+#endif
+ memcpy(buf, &time, 8);
+ *size = 8;
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_encode_key_year(Field *field, const uchar *key,
+ uchar *buf, uint *size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ bool truncated = false;
+ int year = (int)key[0];
+
+ struct tm datetime;
+ memset(&datetime, 0, sizeof(struct tm));
+ datetime.tm_year = year;
+ datetime.tm_mon = 0;
+ datetime.tm_mday = 1;
+ int usec = 0;
+ mrn::TimeConverter time_converter;
+ long long int time = time_converter.tm_to_grn_time(&datetime, usec,
+ &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ memcpy(buf, &time, 8);
+ *size = 8;
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_encode_key_datetime(Field *field, const uchar *key,
+ uchar *buf, uint *size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ bool truncated = false;
+ long long int time;
+#ifdef MRN_MARIADB_P
+ if (field->decimals() > 0) {
+ Field_datetime_hires *datetime_hires_field = (Field_datetime_hires *)field;
+ MYSQL_TIME mysql_time;
+ uint fuzzy_date = 0;
+ uchar *ptr_backup = field->ptr;
+ uchar *null_ptr_backup = field->null_ptr;
+ field->ptr = (uchar *)key;
+ field->null_ptr = (uchar *)(key - 1);
+ datetime_hires_field->get_date(&mysql_time, fuzzy_date);
+ field->ptr = ptr_backup;
+ field->null_ptr = null_ptr_backup;
+ mrn::TimeConverter time_converter;
+ time = time_converter.mysql_time_to_grn_time(&mysql_time, &truncated);
+ } else
+#endif
+ {
+ long long int encoded_datetime = sint8korr(key);
+ uint32 part1 = (uint32)(encoded_datetime / 1000000LL);
+ uint32 part2 = (uint32)(encoded_datetime -
+ (unsigned long long int)part1 * 1000000LL);
+ struct tm date;
+ memset(&date, 0, sizeof(struct tm));
+ date.tm_year = part1 / 10000 - mrn::TimeConverter::TM_YEAR_BASE;
+ date.tm_mon = part1 / 100 % 100 - 1;
+ date.tm_mday = part1 % 100;
+ date.tm_hour = part2 / 10000;
+ date.tm_min = part2 / 100 % 100;
+ date.tm_sec = part2 % 100;
+ int usec = 0;
+ mrn::TimeConverter time_converter;
+ time = time_converter.tm_to_grn_time(&date, usec, &truncated);
+ }
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ memcpy(buf, &time, 8);
+ *size = 8;
+ DBUG_RETURN(error);
+}
+
+#ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2
+int ha_mroonga::storage_encode_key_timestamp2(Field *field, const uchar *key,
+ uchar *buf, uint *size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ bool truncated = false;
+
+ Field_timestampf *timestamp2_field = (Field_timestampf *)field;
+ struct timeval tm;
+ my_timestamp_from_binary(&tm, key, timestamp2_field->decimals());
+ MYSQL_TIME mysql_time;
+ mrn_my_tz_UTC->gmt_sec_to_TIME(&mysql_time, (my_time_t)tm.tv_sec);
+ mysql_time.second_part = tm.tv_usec;
+ mrn::TimeConverter time_converter;
+ long long int grn_time = time_converter.mysql_time_to_grn_time(&mysql_time,
+ &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ memcpy(buf, &grn_time, 8);
+ *size = 8;
+
+ DBUG_RETURN(error);
+}
+#endif
+
+#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
+int ha_mroonga::storage_encode_key_datetime2(Field *field, const uchar *key,
+ uchar *buf, uint *size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ bool truncated = false;
+
+ Field_datetimef *datetime2_field = (Field_datetimef *)field;
+ longlong packed_time =
+ my_datetime_packed_from_binary(key, datetime2_field->decimals());
+ MYSQL_TIME mysql_time;
+ TIME_from_longlong_datetime_packed(&mysql_time, packed_time);
+ mrn::TimeConverter time_converter;
+ long long int grn_time = time_converter.mysql_time_to_grn_time(&mysql_time,
+ &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ memcpy(buf, &grn_time, 8);
+ *size = 8;
+
+ DBUG_RETURN(error);
+}
+#endif
+
+#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
+int ha_mroonga::storage_encode_key_time2(Field *field, const uchar *key,
+ uchar *buf, uint *size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ bool truncated = false;
+
+ Field_timef *time2_field = (Field_timef *)field;
+ longlong packed_time =
+ my_time_packed_from_binary(key, time2_field->decimals());
+ MYSQL_TIME mysql_time;
+ TIME_from_longlong_time_packed(&mysql_time, packed_time);
+ mrn::TimeConverter time_converter;
+ long long int grn_time = time_converter.mysql_time_to_grn_time(&mysql_time,
+ &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ memcpy(buf, &grn_time, 8);
+ *size = 8;
+
+ DBUG_RETURN(error);
+}
+#endif
+
+int ha_mroonga::storage_encode_key_enum(Field *field, const uchar *key,
+ uchar *buf, uint *size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (field->pack_length() == 1) {
+ uchar value;
+ value = key[0];
+ *size = 1;
+ memcpy(buf, &value, *size);
+ } else {
+ uint16 value;
+ shortget(value, key);
+ *size = 2;
+ memcpy(buf, &value, *size);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_encode_key_set(Field *field, const uchar *key,
+ uchar *buf, uint *size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ Field_set unpacker((uchar *)key, field->field_length, (uchar *)(key - 1),
+ field->null_bit, field->unireg_check, field->field_name,
+ field->pack_length(),
+ static_cast<Field_set*>(field)->typelib,
+ static_cast<Field_set*>(field)->charset());
+ switch (field->pack_length()) {
+ case 1:
+ {
+ int8 signed_value = (int8)(unpacker.val_int());
+ uint8 unsigned_value = *((uint8 *)&signed_value);
+ *size = 1;
+ memcpy(buf, &unsigned_value, *size);
+ }
+ break;
+ case 2:
+ {
+ int16 signed_value = (int16)(unpacker.val_int());
+ uint16 unsigned_value = *((uint16 *)&signed_value);
+ *size = 2;
+ memcpy(buf, &unsigned_value, *size);
+ }
+ break;
+ case 3:
+ case 4:
+ {
+ int32 signed_value = (int32)(unpacker.val_int());
+ uint32 unsigned_value = *((uint32 *)&signed_value);
+ *size = 4;
+ memcpy(buf, &unsigned_value, *size);
+ }
+ break;
+ case 8:
+ default:
+ {
+ int64 signed_value = (int64)(unpacker.val_int());
+ uint64 unsigned_value = *((uint64 *)&signed_value);
+ *size = 8;
+ memcpy(buf, &unsigned_value, *size);
+ }
+ break;
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_encode_key(Field *field, const uchar *key,
+ uchar *buf, uint *size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error;
+ bool truncated = false;
+ const uchar *ptr = key;
+
+ error = mrn_change_encoding(ctx, field->charset());
+ if (error)
+ DBUG_RETURN(error);
+
+ if (field->null_bit) {
+ ptr += 1;
+ }
+
+ switch (field->real_type()) {
+ case MYSQL_TYPE_BIT:
+ case MYSQL_TYPE_TINY:
+ {
+ memcpy(buf, ptr, 1);
+ *size = 1;
+ break;
+ }
+ case MYSQL_TYPE_SHORT:
+ {
+ memcpy(buf, ptr, 2);
+ *size = 2;
+ break;
+ }
+ case MYSQL_TYPE_INT24:
+ {
+ memcpy(buf, ptr, 3);
+ buf[3] = 0;
+ *size = 4;
+ break;
+ }
+ case MYSQL_TYPE_LONG:
+ {
+ memcpy(buf, ptr, 4);
+ *size = 4;
+ break;
+ }
+ case MYSQL_TYPE_TIMESTAMP:
+ error = storage_encode_key_timestamp(field, ptr, buf, size);
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ {
+ memcpy(buf, ptr, 8);
+ *size = 8;
+ break;
+ }
+ case MYSQL_TYPE_FLOAT:
+ {
+ float float_value;
+ double double_value;
+ float4get(float_value, ptr);
+ double_value = float_value;
+ memcpy(buf, &double_value, 8);
+ *size = 8;
+ break;
+ }
+ case MYSQL_TYPE_DOUBLE:
+ {
+ double val;
+ float8get(val, ptr);
+ memcpy(buf, &val, 8);
+ *size = 8;
+ break;
+ }
+ case MYSQL_TYPE_TIME:
+ error = storage_encode_key_time(field, ptr, buf, size);
+ break;
+ case MYSQL_TYPE_YEAR:
+ error = storage_encode_key_year(field, ptr, buf, size);
+ break;
+ case MYSQL_TYPE_DATETIME:
+ error = storage_encode_key_datetime(field, ptr, buf, size);
+ break;
+ case MYSQL_TYPE_NEWDATE:
+ {
+ uint32 encoded_date = uint3korr(ptr);
+ struct tm date;
+ memset(&date, 0, sizeof(struct tm));
+ date.tm_year = encoded_date / (16 * 32) - mrn::TimeConverter::TM_YEAR_BASE;
+ date.tm_mon = encoded_date / 32 % 16 - 1;
+ date.tm_mday = encoded_date % 32;
+ int usec = 0;
+ mrn::TimeConverter time_converter;
+ long long int time = time_converter.tm_to_grn_time(&date, usec,
+ &truncated);
+ if (truncated) {
+ field->set_warning(Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED, 1);
+ }
+ memcpy(buf, &time, 8);
+ *size = 8;
+ break;
+ }
+#ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2
+ case MYSQL_TYPE_TIMESTAMP2:
+ error = storage_encode_key_timestamp2(field, ptr, buf, size);
+ break;
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
+ case MYSQL_TYPE_DATETIME2:
+ error = storage_encode_key_datetime2(field, ptr, buf, size);
+ break;
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
+ case MYSQL_TYPE_TIME2:
+ error = storage_encode_key_time2(field, ptr, buf, size);
+ break;
+#endif
+ case MYSQL_TYPE_STRING:
+ error = storage_encode_key_fixed_size_string(field, ptr, buf, size);
+ break;
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_BLOB:
+ error = storage_encode_key_variable_size_string(field, ptr, buf, size);
+ break;
+ case MYSQL_TYPE_ENUM:
+ error = storage_encode_key_enum(field, ptr, buf, size);
+ break;
+ case MYSQL_TYPE_SET:
+ error = storage_encode_key_set(field, ptr, buf, size);
+ break;
+ default:
+ error = HA_ERR_UNSUPPORTED;
+ break;
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_encode_multiple_column_key(KEY *key_info,
+ const uchar *key,
+ uint key_length,
+ uchar *buffer,
+ uint *encoded_length)
+{
+ MRN_DBUG_ENTER_METHOD();
+ mrn::MultipleColumnKeyCodec codec(ctx, ha_thd(), key_info);
+ int error = codec.encode(key, key_length, buffer, encoded_length);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_encode_multiple_column_key_range(KEY *key_info,
+ const key_range *start,
+ const key_range *end,
+ uchar *min_buffer,
+ uint *min_encoded_size,
+ uchar *max_buffer,
+ uint *max_encoded_size)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ mrn::MultipleColumnKeyCodec codec(ctx, ha_thd(), key_info);
+ uint encoded_key_size = codec.size();
+ if (start) {
+ memset(min_buffer, 0, encoded_key_size);
+ error = codec.encode(start->key, start->length,
+ min_buffer, min_encoded_size);
+ // TODO: handle error?
+ *min_encoded_size = encoded_key_size;
+ }
+ if (end) {
+ memset(max_buffer, 0xff, encoded_key_size);
+ error = codec.encode(end->key, end->length,
+ max_buffer, max_encoded_size);
+ // TODO: handle error?
+ *max_encoded_size = encoded_key_size;
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_reset()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (thd_sql_command(ha_thd()) == SQLCOM_SELECT) {
+ st_select_lex *select_lex = table->pos_in_table_list->select_lex;
+ List_iterator<Item_func_match> iterator(*(select_lex->ftfunc_list));
+ Item_func_match *item;
+ while ((item = iterator++)) {
+ if (item->ft_handler) {
+ mrn_generic_ft_clear(item->ft_handler);
+ }
+ }
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_reset()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->ha_reset();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+#ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER
+ if (alter_key_info_buffer) {
+ my_free(alter_key_info_buffer, MYF(0));
+ alter_key_info_buffer = NULL;
+ }
+#else
+ if (wrap_alter_key_info) {
+ my_free(wrap_alter_key_info, MYF(0));
+ wrap_alter_key_info = NULL;
+ }
+#endif
+ wrap_ft_init_count = 0;
+ int generic_error = generic_reset();
+ if (error == 0) {
+ error = generic_error;
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_reset()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error;
+ error = generic_reset();
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::reset()
+{
+ int error = 0;
+ THD *thd = ha_thd();
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_PRINT("info", ("mroonga: this=%p", this));
+ clear_empty_value_records();
+ clear_search_result();
+ clear_search_result_geo();
+ if (share->wrapper_mode)
+ error = wrapper_reset();
+ else
+ error = storage_reset();
+ ignoring_no_key_columns = false;
+ inserting_with_update = false;
+ ignoring_duplicated_key = false;
+ fulltext_searching = false;
+ replacing_ = false;
+ written_by_row_based_binlog = 0;
+ mrn_lock_type = F_UNLCK;
+ mrn_clear_alter_share(thd);
+ current_ft_item = NULL;
+ DBUG_RETURN(error);
+}
+
+#ifdef MRN_HANDLER_CLONE_NEED_NAME
+handler *ha_mroonga::wrapper_clone(const char *name, MEM_ROOT *mem_root)
+{
+ handler *cloned_handler;
+ MRN_DBUG_ENTER_METHOD();
+ if (!(cloned_handler = get_new_handler(table->s, mem_root,
+ table->s->db_type())))
+ DBUG_RETURN(NULL);
+ ((ha_mroonga *) cloned_handler)->is_clone = true;
+ ((ha_mroonga *) cloned_handler)->parent_for_clone = this;
+ ((ha_mroonga *) cloned_handler)->mem_root_for_clone = mem_root;
+ if (cloned_handler->ha_open(table, table->s->normalized_path.str,
+ table->db_stat, HA_OPEN_IGNORE_IF_LOCKED))
+ {
+ delete cloned_handler;
+ DBUG_RETURN(NULL);
+ }
+ DBUG_RETURN(cloned_handler);
+}
+
+handler *ha_mroonga::storage_clone(const char *name, MEM_ROOT *mem_root)
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler *cloned_handler;
+ cloned_handler = handler::clone(name, mem_root);
+ DBUG_RETURN(cloned_handler);
+}
+
+handler *ha_mroonga::clone(const char *name, MEM_ROOT *mem_root)
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler *cloned_handler;
+ if (share->wrapper_mode)
+ {
+ cloned_handler = wrapper_clone(name, mem_root);
+ } else {
+ cloned_handler = storage_clone(name, mem_root);
+ }
+ DBUG_RETURN(cloned_handler);
+}
+#else
+handler *ha_mroonga::wrapper_clone(MEM_ROOT *mem_root)
+{
+ handler *cloned_handler;
+ MRN_DBUG_ENTER_METHOD();
+ if (!(cloned_handler = get_new_handler(table->s, mem_root,
+ table->s->db_type())))
+ DBUG_RETURN(NULL);
+ ((ha_mroonga *) cloned_handler)->is_clone = true;
+ ((ha_mroonga *) cloned_handler)->parent_for_clone = this;
+ ((ha_mroonga *) cloned_handler)->mem_root_for_clone = mem_root;
+ if (cloned_handler->ha_open(table, table->s->normalized_path.str,
+ table->db_stat, HA_OPEN_IGNORE_IF_LOCKED))
+ {
+ delete cloned_handler;
+ DBUG_RETURN(NULL);
+ }
+ DBUG_RETURN(cloned_handler);
+}
+
+handler *ha_mroonga::storage_clone(MEM_ROOT *mem_root)
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler *cloned_handler;
+ cloned_handler = handler::clone(mem_root);
+ DBUG_RETURN(cloned_handler);
+}
+
+handler *ha_mroonga::clone(MEM_ROOT *mem_root)
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler *cloned_handler;
+ if (share->wrapper_mode)
+ {
+ cloned_handler = wrapper_clone(mem_root);
+ } else {
+ cloned_handler = storage_clone(mem_root);
+ }
+ DBUG_RETURN(cloned_handler);
+}
+#endif
+
+uint8 ha_mroonga::wrapper_table_cache_type()
+{
+ uint8 res;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->table_cache_type();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+uint8 ha_mroonga::storage_table_cache_type()
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint8 type = handler::table_cache_type();
+ DBUG_RETURN(type);
+}
+
+uint8 ha_mroonga::table_cache_type()
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint8 type;
+ if (share->wrapper_mode)
+ {
+ type = wrapper_table_cache_type();
+ } else {
+ type = storage_table_cache_type();
+ }
+ DBUG_RETURN(type);
+}
+
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ
+ha_rows ha_mroonga::wrapper_multi_range_read_info_const(uint keyno,
+ RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges,
+ uint *bufsz,
+ uint *flags,
+ Cost_estimate *cost)
+{
+ MRN_DBUG_ENTER_METHOD();
+ ha_rows rows;
+ KEY key_info = table->key_info[keyno];
+ if (mrn_is_geo_key(&key_info)) {
+ rows = handler::multi_range_read_info_const(keyno, seq, seq_init_param,
+ n_ranges, bufsz, flags, cost);
+ DBUG_RETURN(rows);
+ }
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+ rows = wrap_handler->multi_range_read_info_const(keyno, seq, seq_init_param,
+ n_ranges, bufsz, flags,
+ cost);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(rows);
+}
+
+ha_rows ha_mroonga::storage_multi_range_read_info_const(uint keyno,
+ RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges,
+ uint *bufsz,
+ uint *flags,
+ Cost_estimate *cost)
+{
+ MRN_DBUG_ENTER_METHOD();
+ ha_rows rows = handler::multi_range_read_info_const(keyno, seq,
+ seq_init_param,
+ n_ranges, bufsz, flags,
+ cost);
+ DBUG_RETURN(rows);
+}
+
+ha_rows ha_mroonga::multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *flags,
+ Cost_estimate *cost)
+{
+ MRN_DBUG_ENTER_METHOD();
+ ha_rows rows;
+ if (share->wrapper_mode)
+ {
+ rows = wrapper_multi_range_read_info_const(keyno, seq, seq_init_param,
+ n_ranges, bufsz,
+ flags, cost);
+ } else {
+ rows = storage_multi_range_read_info_const(keyno, seq, seq_init_param,
+ n_ranges, bufsz,
+ flags, cost);
+ }
+ DBUG_RETURN(rows);
+}
+
+ha_rows ha_mroonga::wrapper_multi_range_read_info(uint keyno, uint n_ranges,
+ uint keys,
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS
+ uint key_parts,
+#endif
+ uint *bufsz,
+ uint *flags,
+ Cost_estimate *cost)
+{
+ MRN_DBUG_ENTER_METHOD();
+ ha_rows rows;
+ KEY key_info = table->key_info[keyno];
+ if (mrn_is_geo_key(&key_info)) {
+ rows = handler::multi_range_read_info(keyno, n_ranges, keys,
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS
+ key_parts,
+#endif
+ bufsz, flags, cost);
+ DBUG_RETURN(rows);
+ }
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+ rows = wrap_handler->multi_range_read_info(keyno, n_ranges, keys,
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS
+ key_parts,
+#endif
+ bufsz, flags, cost);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(rows);
+}
+
+ha_rows ha_mroonga::storage_multi_range_read_info(uint keyno, uint n_ranges,
+ uint keys,
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS
+ uint key_parts,
+#endif
+ uint *bufsz,
+ uint *flags,
+ Cost_estimate *cost)
+{
+ MRN_DBUG_ENTER_METHOD();
+ ha_rows rows = handler::multi_range_read_info(keyno, n_ranges, keys,
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS
+ key_parts,
+#endif
+ bufsz, flags, cost);
+ DBUG_RETURN(rows);
+}
+
+ha_rows ha_mroonga::multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS
+ uint key_parts,
+#endif
+ uint *bufsz, uint *flags,
+ Cost_estimate *cost)
+{
+ MRN_DBUG_ENTER_METHOD();
+ ha_rows rows;
+ if (share->wrapper_mode)
+ {
+ rows = wrapper_multi_range_read_info(keyno, n_ranges, keys,
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS
+ key_parts,
+#endif
+ bufsz, flags, cost);
+ } else {
+ rows = storage_multi_range_read_info(keyno, n_ranges, keys,
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS
+ key_parts,
+#endif
+ bufsz, flags, cost);
+ }
+ DBUG_RETURN(rows);
+}
+
+int ha_mroonga::wrapper_multi_range_read_init(RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint mode,
+ HANDLER_BUFFER *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ KEY key_info = table->key_info[active_index];
+ if (mrn_is_geo_key(&key_info)) {
+ error = handler::multi_range_read_init(seq, seq_init_param,
+ n_ranges, mode, buf);
+ DBUG_RETURN(error);
+ }
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+ error = wrap_handler->multi_range_read_init(seq, seq_init_param,
+ n_ranges, mode, buf);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_multi_range_read_init(RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint mode,
+ HANDLER_BUFFER *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = handler::multi_range_read_init(seq, seq_init_param,
+ n_ranges, mode, buf);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mode,
+ HANDLER_BUFFER *buf)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_multi_range_read_init(seq, seq_init_param,
+ n_ranges, mode, buf);
+ } else {
+ error = storage_multi_range_read_init(seq, seq_init_param,
+ n_ranges, mode, buf);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_multi_range_read_next(range_id_t *range_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ KEY key_info = table->key_info[active_index];
+ if (mrn_is_geo_key(&key_info)) {
+ error = handler::multi_range_read_next(range_info);
+ DBUG_RETURN(error);
+ }
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+ error = wrap_handler->multi_range_read_next(range_info);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_multi_range_read_next(range_id_t *range_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = handler::multi_range_read_next(range_info);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::multi_range_read_next(range_id_t *range_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_multi_range_read_next(range_info);
+ } else {
+ error = storage_multi_range_read_next(range_info);
+ }
+ DBUG_RETURN(error);
+}
+#else // MRN_HANDLER_HAVE_MULTI_RANGE_READ
+int ha_mroonga::wrapper_read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
+ KEY_MULTI_RANGE *ranges,
+ uint range_count,
+ bool sorted,
+ HANDLER_BUFFER *buffer)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ KEY key_info = table->key_info[active_index];
+ if (mrn_is_geo_key(&key_info)) {
+ error = handler::read_multi_range_first(found_range_p, ranges,
+ range_count, sorted, buffer);
+ DBUG_RETURN(error);
+ }
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+ error = wrap_handler->read_multi_range_first(found_range_p, ranges,
+ range_count, sorted, buffer);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
+ KEY_MULTI_RANGE *ranges,
+ uint range_count,
+ bool sorted,
+ HANDLER_BUFFER *buffer)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = handler::read_multi_range_first(found_range_p, ranges,
+ range_count, sorted, buffer);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
+ KEY_MULTI_RANGE *ranges,
+ uint range_count,
+ bool sorted,
+ HANDLER_BUFFER *buffer)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_read_multi_range_first(found_range_p, ranges,
+ range_count, sorted, buffer);
+ } else {
+ error = storage_read_multi_range_first(found_range_p, ranges,
+ range_count, sorted, buffer);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_read_multi_range_next(KEY_MULTI_RANGE **found_range_p)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ KEY key_info = table->key_info[active_index];
+ if (mrn_is_geo_key(&key_info)) {
+ error = handler::read_multi_range_next(found_range_p);
+ DBUG_RETURN(error);
+ }
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ if (fulltext_searching)
+ set_pk_bitmap();
+ error = wrap_handler->read_multi_range_next(found_range_p);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_read_multi_range_next(KEY_MULTI_RANGE **found_range_p)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = handler::read_multi_range_next(found_range_p);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::read_multi_range_next(KEY_MULTI_RANGE **found_range_p)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_read_multi_range_next(found_range_p);
+ } else {
+ error = storage_read_multi_range_next(found_range_p);
+ }
+ DBUG_RETURN(error);
+}
+#endif // MRN_HANDLER_HAVE_MULTI_RANGE_READ
+
+#ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS
+void ha_mroonga::wrapper_start_bulk_insert(ha_rows rows, uint flags)
+#else
+void ha_mroonga::wrapper_start_bulk_insert(ha_rows rows)
+#endif
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+#ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS
+ wrap_handler->ha_start_bulk_insert(rows, flags);
+#else
+ wrap_handler->ha_start_bulk_insert(rows);
+#endif
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+#ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS
+void ha_mroonga::storage_start_bulk_insert(ha_rows rows, uint flags)
+#else
+void ha_mroonga::storage_start_bulk_insert(ha_rows rows)
+#endif
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_VOID_RETURN;
+}
+
+#ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS
+void ha_mroonga::start_bulk_insert(ha_rows rows, uint flags)
+#else
+void ha_mroonga::start_bulk_insert(ha_rows rows)
+#endif
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode) {
+#ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS
+ wrapper_start_bulk_insert(rows, flags);
+#else
+ wrapper_start_bulk_insert(rows);
+#endif
+ } else {
+#ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS
+ storage_start_bulk_insert(rows, flags);
+#else
+ storage_start_bulk_insert(rows);
+#endif
+ }
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::wrapper_end_bulk_insert()
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->ha_end_bulk_insert();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_end_bulk_insert()
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::end_bulk_insert()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_end_bulk_insert();
+ } else {
+ error = storage_end_bulk_insert();
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::generic_delete_all_rows(grn_obj *target_grn_table,
+ const char *function_name)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ if (is_dry_write()) {
+ DBUG_PRINT("info",
+ ("mroonga: dry write: %s::%s", MRN_CLASS_NAME, function_name));
+ DBUG_RETURN(error);
+ }
+
+ grn_table_cursor *cursor;
+ cursor = grn_table_cursor_open(ctx, target_grn_table,
+ NULL, 0,
+ NULL, 0,
+ 0, -1,
+ 0);
+ if (cursor) {
+ while (grn_table_cursor_next(ctx, cursor) != GRN_ID_NIL) {
+ grn_table_cursor_delete(ctx, cursor);
+ }
+ grn_table_cursor_close(ctx, cursor);
+ } else {
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, ctx->errbuf, MYF(0));
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_delete_all_rows()
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->ha_delete_all_rows();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+
+ if (error) {
+ DBUG_RETURN(error);
+ }
+
+ if (!wrapper_have_target_index()) {
+ DBUG_RETURN(error);
+ }
+
+ uint i;
+ uint n_keys = table->s->keys;
+ for (i = 0; i < n_keys; i++) {
+ KEY key_info = table->key_info[i];
+
+ if (!(wrapper_is_target_index(&key_info))) {
+ continue;
+ }
+
+ if (!grn_index_tables[i]) {
+ /* disable keys */
+ continue;
+ }
+
+ error = generic_delete_all_rows(grn_index_tables[i], __FUNCTION__);
+ if (error) {
+ break;
+ }
+ }
+
+ int grn_table_error;
+ grn_table_error = generic_delete_all_rows(grn_table, __FUNCTION__);
+ if (!error) {
+ error = grn_table_error;
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_delete_all_rows()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = generic_delete_all_rows(grn_table, __FUNCTION__);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::delete_all_rows()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_delete_all_rows();
+ } else {
+ error = storage_delete_all_rows();
+ }
+ DBUG_RETURN(error);
+}
+
+#ifdef MRN_HANDLER_HAVE_TRUNCATE
+int ha_mroonga::wrapper_truncate()
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->ha_truncate();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+
+ if (!error && wrapper_have_target_index()) {
+ error = wrapper_truncate_index();
+ }
+
+ DBUG_RETURN(error);
+}
+#endif
+
+int ha_mroonga::wrapper_truncate_index()
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ if (is_dry_write()) {
+ DBUG_PRINT("info",
+ ("mroonga: dry write: %s::%s", MRN_CLASS_NAME, __FUNCTION__));
+ DBUG_RETURN(error);
+ }
+
+ grn_rc rc;
+ uint i;
+ uint n_keys = table->s->keys;
+ for (i = 0; i < n_keys; i++) {
+ KEY key_info = table->key_info[i];
+
+ if (!(wrapper_is_target_index(&key_info))) {
+ continue;
+ }
+
+ if (!grn_index_tables[i]) {
+ /* disable keys */
+ continue;
+ }
+
+ rc = grn_table_truncate(ctx, grn_index_tables[i]);
+ if (rc) {
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, ctx->errbuf, MYF(0));
+ goto err;
+ }
+ }
+err:
+ rc = grn_table_truncate(ctx, grn_table);
+ if (rc) {
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, ctx->errbuf, MYF(0));
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_truncate()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+
+ if (is_dry_write()) {
+ DBUG_PRINT("info", ("mroonga: dry write: ha_mroonga::%s", __FUNCTION__));
+ DBUG_RETURN(error);
+ }
+
+ grn_rc rc;
+ rc = grn_table_truncate(ctx, grn_table);
+ if (rc) {
+ my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0));
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
+ }
+ error = storage_truncate_index();
+
+ if (!error && thd_sql_command(ha_thd()) == SQLCOM_TRUNCATE) {
+ MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share;
+ mrn::Lock lock(&long_term_share->auto_inc_mutex);
+ long_term_share->auto_inc_value = 0;
+ DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu",
+ long_term_share->auto_inc_value));
+ long_term_share->auto_inc_inited = false;
+ }
+
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_truncate_index()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ grn_rc rc;
+ uint i;
+ uint n_keys = table->s->keys;
+ for (i = 0; i < n_keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+
+ KEY key_info = table->key_info[i];
+
+ if (
+ !(key_info.flags & HA_NOSAME) &&
+ (KEY_N_KEY_PARTS(&key_info) == 1 || (key_info.flags & HA_FULLTEXT))
+ ) {
+ continue;
+ }
+
+ if (!grn_index_tables[i]) {
+ /* disable keys */
+ continue;
+ }
+
+ rc = grn_table_truncate(ctx, grn_index_tables[i]);
+ if (rc) {
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, ctx->errbuf, MYF(0));
+ goto err;
+ }
+ }
+err:
+ DBUG_RETURN(error);
+}
+
+#ifdef MRN_HANDLER_HAVE_TRUNCATE
+int ha_mroonga::truncate()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_truncate();
+ } else {
+ error = storage_truncate();
+ }
+ DBUG_RETURN(error);
+}
+#endif
+
+double ha_mroonga::wrapper_scan_time()
+{
+ double res;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->scan_time();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+double ha_mroonga::storage_scan_time()
+{
+ MRN_DBUG_ENTER_METHOD();
+ double time = handler::scan_time();
+ DBUG_RETURN(time);
+}
+
+double ha_mroonga::scan_time()
+{
+ MRN_DBUG_ENTER_METHOD();
+ double time;
+ if (share->wrapper_mode)
+ {
+ time = wrapper_scan_time();
+ } else {
+ time = storage_scan_time();
+ }
+ DBUG_RETURN(time);
+}
+
+double ha_mroonga::wrapper_read_time(uint index, uint ranges, ha_rows rows)
+{
+ double res;
+ MRN_DBUG_ENTER_METHOD();
+ if (index < MAX_KEY) {
+ KEY key_info = table->key_info[index];
+ if (mrn_is_geo_key(&key_info)) {
+ res = handler::read_time(index, ranges, rows);
+ DBUG_RETURN(res);
+ }
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->read_time(share->wrap_key_nr[index], ranges, rows);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ } else {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->read_time(index, ranges, rows);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ DBUG_RETURN(res);
+}
+
+double ha_mroonga::storage_read_time(uint index, uint ranges, ha_rows rows)
+{
+ MRN_DBUG_ENTER_METHOD();
+ double time = handler::read_time(index, ranges, rows);
+ DBUG_RETURN(time);
+}
+
+double ha_mroonga::read_time(uint index, uint ranges, ha_rows rows)
+{
+ MRN_DBUG_ENTER_METHOD();
+ double time;
+ if (share->wrapper_mode)
+ {
+ time = wrapper_read_time(index, ranges, rows);
+ } else {
+ time = storage_read_time(index, ranges, rows);
+ }
+ DBUG_RETURN(time);
+}
+
+const key_map *ha_mroonga::wrapper_keys_to_use_for_scanning()
+{
+ const key_map *res;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->keys_to_use_for_scanning();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+const key_map *ha_mroonga::storage_keys_to_use_for_scanning()
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(&key_map_full);
+}
+
+const key_map *ha_mroonga::keys_to_use_for_scanning()
+{
+ MRN_DBUG_ENTER_METHOD();
+ const key_map *key_map;
+ if (share->wrapper_mode)
+ {
+ key_map = wrapper_keys_to_use_for_scanning();
+ } else {
+ key_map = storage_keys_to_use_for_scanning();
+ }
+ DBUG_RETURN(key_map);
+}
+
+ha_rows ha_mroonga::wrapper_estimate_rows_upper_bound()
+{
+ ha_rows res;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->estimate_rows_upper_bound();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+ha_rows ha_mroonga::storage_estimate_rows_upper_bound()
+{
+ MRN_DBUG_ENTER_METHOD();
+ ha_rows rows = handler::estimate_rows_upper_bound();
+ DBUG_RETURN(rows);
+}
+
+ha_rows ha_mroonga::estimate_rows_upper_bound()
+{
+ MRN_DBUG_ENTER_METHOD();
+ ha_rows rows;
+ if (share->wrapper_mode)
+ {
+ rows = wrapper_estimate_rows_upper_bound();
+ } else {
+ rows = storage_estimate_rows_upper_bound();
+ }
+ DBUG_RETURN(rows);
+}
+
+void ha_mroonga::wrapper_update_create_info(HA_CREATE_INFO* create_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->update_create_info(create_info);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_update_create_info(HA_CREATE_INFO* create_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler::update_create_info(create_info);
+ if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) {
+ MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share;
+ if (!long_term_share->auto_inc_inited) {
+ storage_info(HA_STATUS_AUTO);
+ }
+ create_info->auto_increment_value = long_term_share->auto_inc_value;
+ DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu",
+ long_term_share->auto_inc_value));
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::update_create_info(HA_CREATE_INFO* create_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (!create_info->connect_string.str)
+ {
+ create_info->connect_string.str = table->s->connect_string.str;
+ create_info->connect_string.length = table->s->connect_string.length;
+ }
+ if (share->wrapper_mode)
+ wrapper_update_create_info(create_info);
+ else
+ storage_update_create_info(create_info);
+ st_mrn_slot_data *slot_data = mrn_get_slot_data(ha_thd(), true);
+ if (slot_data) {
+ slot_data->alter_create_info = create_info;
+ if (slot_data->alter_connect_string) {
+ my_free(slot_data->alter_connect_string, MYF(0));
+ slot_data->alter_connect_string = NULL;
+ }
+ if (create_info->connect_string.str) {
+ slot_data->alter_connect_string =
+ my_strndup(create_info->connect_string.str,
+ create_info->connect_string.length,
+ MYF(MY_WME));
+ }
+ if (slot_data->alter_comment) {
+ my_free(slot_data->alter_comment, MYF(0));
+ slot_data->alter_comment = NULL;
+ }
+ if (create_info->comment.str) {
+ slot_data->alter_comment =
+ my_strndup(create_info->comment.str,
+ create_info->comment.length,
+ MYF(MY_WME));
+ }
+ if (share && share->disable_keys) {
+ slot_data->disable_keys_create_info = create_info;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::wrapper_rename_table(const char *from, const char *to,
+ MRN_SHARE *tmp_share,
+ const char *from_table_name,
+ const char *to_table_name)
+{
+ int error = 0;
+ handler *hnd;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(tmp_share, tmp_share->table_share);
+ if (!(hnd =
+ tmp_share->hton->create(tmp_share->hton, tmp_share->table_share,
+ current_thd->mem_root)))
+ {
+ MRN_SET_BASE_SHARE_KEY(tmp_share, tmp_share->table_share);
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+ hnd->init();
+ MRN_SET_BASE_SHARE_KEY(tmp_share, tmp_share->table_share);
+
+ if ((error = hnd->ha_rename_table(from, to)))
+ {
+ delete hnd;
+ DBUG_RETURN(error);
+ }
+
+ error = wrapper_rename_index(from, to, tmp_share,
+ from_table_name, to_table_name);
+
+ delete hnd;
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_rename_index(const char *from, const char *to,
+ MRN_SHARE *tmp_share,
+ const char *from_table_name,
+ const char *to_table_name)
+{
+ int error;
+ grn_rc rc;
+ MRN_DBUG_ENTER_METHOD();
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ error = ensure_database_open(from);
+ if (error)
+ DBUG_RETURN(error);
+
+ TABLE_SHARE *tmp_table_share = tmp_share->table_share;
+
+ uint i;
+ for (i = 0; i < tmp_table_share->keys; i++) {
+ const char *mysql_index_name = tmp_table_share->key_info[i].name;
+ mrn::IndexTableName from_index_table_name(from_table_name, mysql_index_name);
+ mrn::IndexTableName to_index_table_name(to_table_name, mysql_index_name);
+ grn_obj *index_table;
+ index_table = grn_ctx_get(ctx,
+ from_index_table_name.c_str(),
+ from_index_table_name.length());
+ if (index_table) {
+ rc = grn_table_rename(ctx, index_table,
+ to_index_table_name.c_str(),
+ to_index_table_name.length());
+ if (rc != GRN_SUCCESS) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+ }
+
+ grn_obj *table = grn_ctx_get(ctx, from_table_name, strlen(from_table_name));
+ if (ctx->rc != GRN_SUCCESS) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ rc = grn_table_rename(ctx, table, to_table_name,
+ strlen(to_table_name));
+ if (rc != GRN_SUCCESS) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::storage_rename_table(const char *from, const char *to,
+ MRN_SHARE *tmp_share,
+ const char *from_table_name,
+ const char *to_table_name)
+{
+ int error;
+ grn_rc rc;
+ TABLE_SHARE *tmp_table_share = tmp_share->table_share;
+ MRN_LONG_TERM_SHARE *from_long_term_share = tmp_share->long_term_share,
+ *to_long_term_share;
+ MRN_DBUG_ENTER_METHOD();
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ error = ensure_database_open(from);
+ if (error)
+ DBUG_RETURN(error);
+
+ if (!(to_long_term_share = mrn_get_long_term_share(to, strlen(to), &error)))
+ DBUG_RETURN(error);
+ to_long_term_share->auto_inc_value = from_long_term_share->auto_inc_value;
+ DBUG_PRINT("info", ("mroonga: to_auto_inc_value=%llu",
+ to_long_term_share->auto_inc_value));
+ to_long_term_share->auto_inc_inited = from_long_term_share->auto_inc_inited;
+
+ uint i;
+ for (i = 0; i < tmp_table_share->keys; i++) {
+ const char *mysql_index_name = tmp_table_share->key_info[i].name;
+ mrn::IndexTableName from_index_table_name(from_table_name,
+ mysql_index_name);
+ mrn::IndexTableName to_index_table_name(to_table_name,
+ mysql_index_name);
+ grn_obj *index_table;
+ index_table = grn_ctx_get(ctx,
+ from_index_table_name.c_str(),
+ from_index_table_name.length());
+ if (index_table) {
+ rc = grn_table_rename(ctx, index_table,
+ to_index_table_name.c_str(),
+ to_index_table_name.length());
+ if (rc != GRN_SUCCESS) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ goto error_end;
+ }
+ }
+ }
+#ifdef MRN_SUPPORT_FOREIGN_KEYS
+ error = storage_rename_foreign_key(tmp_share, from_table_name, to_table_name);
+ if (error) {
+ goto error_end;
+ }
+#endif
+ {
+ grn_obj *table_obj = grn_ctx_get(ctx, from_table_name, strlen(from_table_name));
+ if (ctx->rc != GRN_SUCCESS) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ goto error_end;
+ }
+ rc = grn_table_rename(ctx, table_obj, to_table_name,
+ strlen(to_table_name));
+ if (rc != GRN_SUCCESS) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ goto error_end;
+ }
+ }
+ DBUG_RETURN(0);
+
+error_end:
+ mrn_free_long_term_share(to_long_term_share);
+ DBUG_RETURN(error);
+}
+
+#ifdef MRN_SUPPORT_FOREIGN_KEYS
+int ha_mroonga::storage_rename_foreign_key(MRN_SHARE *tmp_share,
+ const char *from_table_name,
+ const char *to_table_name)
+{
+ int error;
+ uint i;
+ grn_obj *column, *ref_column;
+ grn_rc rc;
+ TABLE_SHARE *tmp_table_share = tmp_share->table_share;
+ uint n_columns = tmp_table_share->fields;
+ MRN_DBUG_ENTER_METHOD();
+ for (i = 0; i < n_columns; ++i) {
+ Field *field = tmp_table_share->field[i];
+ const char *column_name = field->field_name;
+ uint column_name_size = strlen(column_name);
+
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ continue;
+ }
+
+ column = grn_obj_column(ctx, grn_table,
+ column_name, column_name_size);
+ if (!column) {
+ continue;
+ }
+ grn_id ref_table_id = grn_obj_get_range(ctx, column);
+ grn_obj *ref_table = grn_ctx_at(ctx, ref_table_id);
+ if (ref_table->header.type != GRN_TABLE_NO_KEY &&
+ ref_table->header.type != GRN_TABLE_HASH_KEY &&
+ ref_table->header.type != GRN_TABLE_PAT_KEY &&
+ ref_table->header.type != GRN_TABLE_DAT_KEY) {
+ continue;
+ }
+ mrn::IndexColumnName from_index_column_name(from_table_name, column_name);
+ ref_column = grn_obj_column(ctx, ref_table,
+ from_index_column_name.c_str(),
+ from_index_column_name.length());
+ if (!ref_column) {
+ continue;
+ }
+ mrn::IndexColumnName to_index_column_name(to_table_name, column_name);
+ rc = grn_column_rename(ctx, ref_column,
+ to_index_column_name.c_str(),
+ to_index_column_name.length());
+ if (rc != GRN_SUCCESS) {
+ error = ER_CANT_OPEN_FILE;
+ my_message(error, ctx->errbuf, MYF(0));
+ DBUG_RETURN(error);
+ }
+ }
+ DBUG_RETURN(0);
+}
+#endif
+
+int ha_mroonga::rename_table(const char *from, const char *to)
+{
+ int error = 0;
+ TABLE_LIST table_list;
+ TABLE_SHARE *tmp_table_share;
+ TABLE tmp_table;
+ MRN_SHARE *tmp_share;
+ MRN_DBUG_ENTER_METHOD();
+ mrn::PathMapper to_mapper(to);
+ mrn::PathMapper from_mapper(from);
+ if (strcmp(from_mapper.db_name(), to_mapper.db_name()))
+ DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+
+#ifdef MRN_TABLE_LIST_INIT_REQUIRE_ALIAS
+ table_list.init_one_table(from_mapper.db_name(),
+ strlen(from_mapper.db_name()),
+ from_mapper.mysql_table_name(),
+ strlen(from_mapper.mysql_table_name()),
+ from_mapper.mysql_table_name(), TL_WRITE);
+#else
+ table_list.init_one_table(from_mapper.db_name(),
+ from_mapper.mysql_table_name(),
+ TL_WRITE);
+#endif
+ mrn_open_mutex_lock(NULL);
+ tmp_table_share = mrn_create_tmp_table_share(&table_list, from, &error);
+ mrn_open_mutex_unlock(NULL);
+ if (!tmp_table_share) {
+ DBUG_RETURN(error);
+ }
+ tmp_table.s = tmp_table_share;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ tmp_table.part_info = NULL;
+#endif
+ if (!(tmp_share = mrn_get_share(from, &tmp_table, &error)))
+ {
+ mrn_open_mutex_lock(NULL);
+ mrn_free_tmp_table_share(tmp_table_share);
+ mrn_open_mutex_unlock(NULL);
+ DBUG_RETURN(error);
+ }
+
+ if (tmp_share->wrapper_mode)
+ {
+ error = wrapper_rename_table(from, to, tmp_share,
+ from_mapper.table_name(),
+ to_mapper.table_name());
+ } else {
+ error = storage_rename_table(from, to, tmp_share,
+ from_mapper.table_name(),
+ to_mapper.table_name());
+ }
+
+ if (!error) {
+ mrn_free_long_term_share(tmp_share->long_term_share);
+ tmp_share->long_term_share = NULL;
+ }
+ mrn_free_share(tmp_share);
+ if (!error && to_mapper.table_name()[0] == '#') {
+ if ((error = alter_share_add(to, tmp_table_share)))
+ DBUG_RETURN(error);
+ } else if (error && from_mapper.table_name()[0] == '#') {
+ alter_share_add(from, tmp_table_share);
+ } else {
+ mrn_open_mutex_lock(NULL);
+ mrn_free_tmp_table_share(tmp_table_share);
+ mrn_open_mutex_unlock(NULL);
+ }
+ DBUG_RETURN(error);
+}
+
+bool ha_mroonga::wrapper_is_crashed() const
+{
+ bool res;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->is_crashed();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+bool ha_mroonga::storage_is_crashed() const
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool crashed = handler::is_crashed();
+ DBUG_RETURN(crashed);
+}
+
+bool ha_mroonga::is_crashed() const
+{
+ MRN_DBUG_ENTER_METHOD();
+ int crashed;
+ if (share->wrapper_mode)
+ {
+ crashed = wrapper_is_crashed();
+ } else {
+ crashed = storage_is_crashed();
+ }
+ DBUG_RETURN(crashed);
+}
+
+bool ha_mroonga::wrapper_auto_repair(int error) const
+{
+ bool crashed;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+#ifdef MRN_HANDLER_AUTO_REPAIR_HAVE_ERROR
+ crashed = wrap_handler->auto_repair(error);
+#else
+ crashed = wrap_handler->auto_repair();
+#endif
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(crashed);
+}
+
+bool ha_mroonga::storage_auto_repair(int error) const
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool crashed;
+#ifdef MRN_HANDLER_AUTO_REPAIR_HAVE_ERROR
+ crashed = handler::auto_repair(error);
+#else
+ crashed = handler::auto_repair();
+#endif
+ DBUG_RETURN(crashed);
+}
+
+bool ha_mroonga::auto_repair(int error) const
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool crashed;
+ // TODO: We should consider about creating share for error =
+ // ER_CANT_OPEN_FILE. The following code just ignores the error.
+ if (share && share->wrapper_mode)
+ {
+ crashed = wrapper_auto_repair(error);
+ } else {
+ crashed = storage_auto_repair(error);
+ }
+ DBUG_RETURN(crashed);
+}
+
+bool ha_mroonga::auto_repair() const
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool crashed = auto_repair(HA_ERR_CRASHED_ON_USAGE);
+ DBUG_RETURN(crashed);
+}
+
+int ha_mroonga::wrapper_disable_indexes(uint mode)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->ha_disable_indexes(mode);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ if (error == HA_ERR_WRONG_COMMAND) {
+ error = 0;
+ }
+ if (!error) {
+ if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE || mode == HA_KEY_SWITCH_ALL) {
+ uint i;
+ for (i = 0; i < table_share->keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+ if (share->wrap_key_nr[i] < MAX_KEY) {
+ continue;
+ }
+ if (!grn_index_tables[i]) {
+ DBUG_PRINT("info", ("mroonga: keys are disabled already %u", i));
+ DBUG_RETURN(0);
+ }
+ }
+ KEY *key_info = table_share->key_info;
+ mrn::PathMapper mapper(share->table_name);
+ for (i = 0; i < table_share->keys; i++) {
+ if (!(key_info[i].flags & HA_FULLTEXT) &&
+ !mrn_is_geo_key(&key_info[i])) {
+ continue;
+ }
+
+ mrn::IndexTableName index_table_name(mapper.table_name(),
+ key_info[i].name);
+ grn_obj *index_table = grn_ctx_get(ctx,
+ index_table_name.c_str(),
+ index_table_name.length());
+ if (index_table) {
+ grn_obj_remove(ctx, index_table);
+ }
+ grn_index_tables[i] = NULL;
+ grn_index_columns[i] = NULL;
+ }
+ } else {
+ error = HA_ERR_WRONG_COMMAND;
+ }
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_disable_indexes(uint mode)
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE || mode == HA_KEY_SWITCH_ALL) {
+ uint i;
+ for (i = 0; i < table_share->keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+ if (!grn_index_tables[i]) {
+ DBUG_PRINT("info", ("mroonga: keys are disabled already %u", i));
+ DBUG_RETURN(0);
+ }
+ }
+ KEY *key_info = table_share->key_info;
+ mrn::PathMapper mapper(share->table_name);
+ for (i = 0; i < table_share->keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+ if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE &&
+ (key_info[i].flags & HA_NOSAME)) {
+ continue;
+ }
+
+ mrn::IndexTableName index_table_name(mapper.table_name(),
+ key_info[i].name);
+ grn_obj *index_table = grn_ctx_get(ctx,
+ index_table_name.c_str(),
+ index_table_name.length());
+ if (index_table) {
+ grn_obj_remove(ctx, index_table);
+ }
+ grn_index_tables[i] = NULL;
+ grn_index_columns[i] = NULL;
+ }
+ } else {
+ DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+ }
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::disable_indexes(uint mode)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ {
+ error = wrapper_disable_indexes(mode);
+ } else {
+ error = storage_disable_indexes(mode);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_enable_indexes(uint mode)
+{
+ int error = 0, tmp_error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE || mode == HA_KEY_SWITCH_ALL) {
+ uint i, j;
+ for (i = 0; i < table_share->keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+ if (share->wrap_key_nr[i] < MAX_KEY) {
+ continue;
+ }
+ if (!grn_index_tables[i]) {
+ break;
+ }
+ }
+ if (i == table_share->keys) {
+ DBUG_PRINT("info", ("mroonga: keys are enabled already"));
+ DBUG_RETURN(0);
+ }
+ KEY *p_key_info = &table->key_info[table_share->primary_key];
+ KEY *key_info = table_share->key_info;
+ uint n_keys = table_share->keys;
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, n_keys);
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_columns, n_keys);
+ bitmap_clear_all(table->read_set);
+ mrn_set_bitmap_by_key(table->read_set, p_key_info);
+ mrn::PathMapper mapper(share->table_name);
+ for (i = 0, j = 0; i < n_keys; i++) {
+ if (!(key_info[i].flags & HA_FULLTEXT) &&
+ !mrn_is_geo_key(&key_info[i])) {
+ j++;
+ continue;
+ }
+
+ if ((error = mrn_add_index_param(share, &key_info[i], i)))
+ {
+ break;
+ }
+ index_tables[i] = NULL;
+ index_columns[i] = NULL;
+ if (!grn_index_tables[i]) {
+ if (
+ (key_info[i].flags & HA_FULLTEXT) &&
+ (error = wrapper_create_index_fulltext(mapper.table_name(),
+ i, &key_info[i],
+ index_tables, index_columns,
+ share))
+ ) {
+ break;
+ } else if (
+ mrn_is_geo_key(&key_info[i]) &&
+ (error = wrapper_create_index_geo(mapper.table_name(),
+ i, &key_info[i],
+ index_tables, index_columns,
+ share))
+ ) {
+ break;
+ }
+ grn_index_columns[i] = index_columns[i];
+ }
+ mrn_set_bitmap_by_key(table->read_set, &key_info[i]);
+ }
+ if (!error && i > j)
+ {
+ error = wrapper_fill_indexes(ha_thd(), table->key_info, index_columns,
+ n_keys);
+ }
+ bitmap_set_all(table->read_set);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns);
+ } else {
+ tmp_error = HA_ERR_WRONG_COMMAND;
+ }
+
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->ha_enable_indexes(mode);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ if (error == HA_ERR_WRONG_COMMAND) {
+ error = tmp_error;
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_enable_indexes(uint mode)
+{
+ int error = 0;
+ uint n_keys = table_share->keys;
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, n_keys);
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_columns, n_keys);
+ bool have_multiple_column_index = false;
+ bool skip_unique_key = (mode == HA_KEY_SWITCH_NONUNIQ_SAVE);
+ MRN_DBUG_ENTER_METHOD();
+ if (mode == HA_KEY_SWITCH_NONUNIQ_SAVE || mode == HA_KEY_SWITCH_ALL) {
+ uint i;
+ for (i = 0; i < table_share->keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+ if (!grn_index_tables[i]) {
+ break;
+ }
+ }
+ if (i == table_share->keys) {
+ DBUG_PRINT("info", ("mroonga: keys are enabled already"));
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns);
+ DBUG_RETURN(0);
+ }
+ KEY *key_info = table->key_info;
+ bitmap_clear_all(table->read_set);
+ mrn::PathMapper mapper(share->table_name);
+ for (i = 0; i < n_keys; i++) {
+ if (i == table->s->primary_key) {
+ continue;
+ }
+ if (skip_unique_key && (key_info[i].flags & HA_NOSAME)) {
+ continue;
+ }
+
+ if ((error = mrn_add_index_param(share, &key_info[i], i)))
+ {
+ break;
+ }
+ index_tables[i] = NULL;
+ if (!grn_index_tables[i]) {
+ if ((error = storage_create_index(table, mapper.table_name(), grn_table,
+ share, &key_info[i], index_tables,
+ index_columns, i)))
+ {
+ break;
+ }
+ if (
+ KEY_N_KEY_PARTS(&(key_info[i])) != 1 &&
+ !(key_info[i].flags & HA_FULLTEXT)
+ ) {
+ mrn_set_bitmap_by_key(table->read_set, &key_info[i]);
+ have_multiple_column_index = true;
+ }
+ } else {
+ index_columns[i] = NULL;
+ }
+ }
+ if (!error && have_multiple_column_index)
+ {
+ error = storage_add_index_multiple_columns(key_info, n_keys,
+ index_tables,
+ index_columns,
+ skip_unique_key);
+ }
+ bitmap_set_all(table->read_set);
+ } else {
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns);
+ DBUG_RETURN(HA_ERR_WRONG_COMMAND);
+ }
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::enable_indexes(uint mode)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ share->disable_keys = false;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_enable_indexes(mode);
+ } else {
+ error = storage_enable_indexes(mode);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_check(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->ha_check(thd, check_opt);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_check(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
+}
+
+int ha_mroonga::check(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_check(thd, check_opt);
+ } else {
+ error = storage_check(thd, check_opt);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_fill_indexes(THD *thd, KEY *key_info,
+ grn_obj **index_columns, uint n_keys)
+{
+ int error = 0;
+ KEY *p_key_info = &table->key_info[table_share->primary_key];
+ KEY *tmp_key_info;
+#ifdef MRN_NEED_M_LOCK_TYPE_CHECK_FOR_WRAPPER_EXTERNAL_LOCK
+ int wrapper_lock_type_backup = wrap_handler->get_lock_type();
+#endif
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_PRINT("info", ("mroonga: n_keys=%u", n_keys));
+
+ grn_bool need_lock = true;
+ if (mrn_lock_type != F_UNLCK) {
+ need_lock = false;
+ }
+#ifdef MRN_NEED_M_LOCK_TYPE_CHECK_FOR_WRAPPER_EXTERNAL_LOCK
+ if (wrapper_lock_type_backup != F_UNLCK) {
+ need_lock = false;
+ }
+#endif
+ if (need_lock) {
+ error = wrapper_external_lock(thd, F_WRLCK);
+ }
+ if (!error) {
+ if (
+ !(error = wrapper_start_stmt(thd, thr_lock_data.type)) &&
+ !(error = wrapper_rnd_init(true))
+ ) {
+ grn_obj key;
+ GRN_TEXT_INIT(&key, 0);
+ grn_bulk_space(ctx, &key, p_key_info->key_length);
+ while (!(error = wrapper_rnd_next(table->record[0])))
+ {
+ key_copy((uchar *)(GRN_TEXT_VALUE(&key)), table->record[0],
+ p_key_info, p_key_info->key_length);
+ int added;
+ grn_id record_id;
+ mrn_change_encoding(ctx, NULL);
+ record_id = grn_table_add(ctx, grn_table,
+ GRN_TEXT_VALUE(&key), p_key_info->key_length,
+ &added);
+ if (record_id == GRN_ID_NIL)
+ {
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "failed to add a new record into groonga: key=<%.*s>",
+ (int) p_key_info->key_length, GRN_TEXT_VALUE(&key));
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, error_message, MYF(0));
+ }
+ if (error)
+ break;
+
+ uint k;
+ for (k = 0; k < n_keys; k++) {
+ tmp_key_info = &key_info[k];
+ if (!(tmp_key_info->flags & HA_FULLTEXT) &&
+ !mrn_is_geo_key(tmp_key_info)) {
+ continue;
+ }
+ if (!index_columns[k]) {
+ continue;
+ }
+ DBUG_PRINT("info", ("mroonga: key_num=%u", k));
+
+ uint l;
+ for (l = 0; l < KEY_N_KEY_PARTS(tmp_key_info); l++) {
+ Field *field = tmp_key_info->key_part[l].field;
+
+ if (field->is_null())
+ continue;
+ error = mrn_change_encoding(ctx, field->charset());
+ if (error)
+ break;
+
+ error = generic_store_bulk(field, &new_value_buffer);
+ if (error) {
+ my_message(error,
+ "mroonga: wrapper: "
+ "failed to get new value for updating index.",
+ MYF(0));
+ break;
+ }
+
+ grn_obj *index_column = index_columns[k];
+ grn_rc rc;
+ rc = grn_column_index_update(ctx, index_column, record_id, l + 1,
+ NULL, &new_value_buffer);
+ grn_obj_unlink(ctx, index_column);
+ if (rc) {
+ error = ER_ERROR_ON_WRITE;
+ my_message(error, ctx->errbuf, MYF(0));
+ break;
+ }
+ }
+ if (error)
+ break;
+ }
+ if (error)
+ break;
+ }
+ grn_obj_unlink(ctx, &key);
+ if (error != HA_ERR_END_OF_FILE)
+ wrapper_rnd_end();
+ else
+ error = wrapper_rnd_end();
+ }
+ if (need_lock) {
+ wrapper_external_lock(thd, F_UNLCK);
+ }
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_recreate_indexes(THD *thd)
+{
+ int error;
+ uint i, n_keys = table_share->keys;
+ KEY *p_key_info = &table->key_info[table_share->primary_key];
+ KEY *key_info = table->key_info;
+ MRN_DBUG_ENTER_METHOD();
+ mrn::PathMapper mapper(table_share->normalized_path.str);
+ bitmap_clear_all(table->read_set);
+ clear_indexes();
+ remove_grn_obj_force(mapper.table_name());
+ grn_table = NULL;
+ mrn_set_bitmap_by_key(table->read_set, p_key_info);
+ for (i = 0; i < n_keys; i++) {
+ if (!(key_info[i].flags & HA_FULLTEXT) && !mrn_is_geo_key(&key_info[i])) {
+ continue;
+ }
+ mrn::IndexTableName index_table_name(mapper.table_name(),
+ table_share->key_info[i].name);
+ char index_column_full_name[MRN_MAX_PATH_SIZE];
+ snprintf(index_column_full_name, MRN_MAX_PATH_SIZE,
+ "%s.%s", index_table_name.c_str(), INDEX_COLUMN_NAME);
+ remove_grn_obj_force(index_column_full_name);
+ remove_grn_obj_force(index_table_name.c_str());
+ mrn_set_bitmap_by_key(table->read_set, &key_info[i]);
+ }
+ error = wrapper_create_index(table_share->normalized_path.str, table,
+ NULL, share, mapper.table_name());
+ if (error)
+ DBUG_RETURN(error);
+ error = wrapper_open_indexes(table_share->normalized_path.str);
+ if (error)
+ DBUG_RETURN(error);
+ error = wrapper_fill_indexes(thd, key_info, grn_index_columns, n_keys);
+ bitmap_set_all(table->read_set);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_repair(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ int error;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->ha_repair(thd, check_opt);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ if (error && error != HA_ADMIN_NOT_IMPLEMENTED)
+ DBUG_RETURN(error);
+ error = wrapper_recreate_indexes(thd);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_repair(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
+}
+
+int ha_mroonga::repair(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ share->disable_keys = false;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_repair(thd, check_opt);
+ } else {
+ error = storage_repair(thd, check_opt);
+ }
+ DBUG_RETURN(error);
+}
+
+bool ha_mroonga::wrapper_check_and_repair(THD *thd)
+{
+ // XXX: success is valid variable name?
+ bool success;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ success = wrap_handler->ha_check_and_repair(thd);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(success);
+}
+
+bool ha_mroonga::storage_check_and_repair(THD *thd)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(true);
+}
+
+bool ha_mroonga::check_and_repair(THD *thd)
+{
+ MRN_DBUG_ENTER_METHOD();
+ // XXX: success is valid variable name?
+ bool success;
+ if (share->wrapper_mode)
+ {
+ success = wrapper_check_and_repair(thd);
+ } else {
+ success = storage_check_and_repair(thd);
+ }
+ DBUG_RETURN(success);
+}
+
+int ha_mroonga::wrapper_analyze(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->ha_analyze(thd, check_opt);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_analyze(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
+}
+
+int ha_mroonga::analyze(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_analyze(thd, check_opt);
+ } else {
+ error = storage_analyze(thd, check_opt);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_optimize(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(HA_ADMIN_TRY_ALTER);
+}
+
+int ha_mroonga::storage_optimize(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
+}
+
+int ha_mroonga::optimize(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_optimize(thd, check_opt);
+ } else {
+ error = storage_optimize(thd, check_opt);
+ }
+ DBUG_RETURN(error);
+}
+
+bool ha_mroonga::wrapper_is_fatal_error(int error_num, uint flags)
+{
+ bool res;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->is_fatal_error(error_num, flags);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+bool ha_mroonga::storage_is_fatal_error(int error_num, uint flags)
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool is_fatal_error = handler::is_fatal_error(error_num, flags);
+ DBUG_RETURN(is_fatal_error);
+}
+
+bool ha_mroonga::is_fatal_error(int error_num, uint flags)
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool is_fatal_error;
+ if (share->wrapper_mode)
+ {
+ is_fatal_error = wrapper_is_fatal_error(error_num, flags);
+ } else {
+ is_fatal_error = storage_is_fatal_error(error_num, flags);
+ }
+ DBUG_RETURN(is_fatal_error);
+}
+
+bool ha_mroonga::wrapper_check_if_incompatible_data(
+ HA_CREATE_INFO *create_info, uint table_changes)
+{
+ bool res;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->check_if_incompatible_data(create_info, table_changes);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+bool ha_mroonga::storage_check_if_incompatible_data(
+ HA_CREATE_INFO *create_info, uint table_changes)
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint n = table_share->fields;
+ for (uint i = 0; i < n; i++) {
+ Field *field = table->field[i];
+ if (field->flags & FIELD_IS_RENAMED) {
+ DBUG_RETURN(COMPATIBLE_DATA_NO);
+ }
+ }
+ DBUG_RETURN(COMPATIBLE_DATA_YES);
+}
+
+bool ha_mroonga::check_if_incompatible_data(
+ HA_CREATE_INFO *create_info, uint table_changes)
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool res;
+ if (
+ create_info->comment.str != table_share->comment.str ||
+ create_info->connect_string.str != table_share->connect_string.str
+ ) {
+ DBUG_RETURN(COMPATIBLE_DATA_NO);
+ }
+ if (share->wrapper_mode)
+ {
+ res = wrapper_check_if_incompatible_data(create_info, table_changes);
+ } else {
+ res = storage_check_if_incompatible_data(create_info, table_changes);
+ }
+ DBUG_RETURN(res);
+}
+
+int ha_mroonga::storage_add_index_multiple_columns(KEY *key_info,
+ uint num_of_keys,
+ grn_obj **index_tables,
+ grn_obj **index_columns,
+ bool skip_unique_key)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ int error = 0;
+
+ if (!(error = storage_rnd_init(true)))
+ {
+ while (!(error = storage_rnd_next(table->record[0])))
+ {
+ for (uint i = 0; i < num_of_keys; i++) {
+ KEY *current_key_info = key_info + i;
+ if (
+ KEY_N_KEY_PARTS(current_key_info) == 1 ||
+ (current_key_info->flags & HA_FULLTEXT)
+ ) {
+ continue;
+ }
+ if (skip_unique_key && (key_info[i].flags & HA_NOSAME)) {
+ continue;
+ }
+ if (!index_columns[i]) {
+ continue;
+ }
+
+ /* fix key_info.key_length */
+ for (uint j = 0; j < KEY_N_KEY_PARTS(current_key_info); j++) {
+ if (
+ !current_key_info->key_part[j].null_bit &&
+ current_key_info->key_part[j].field->null_bit
+ ) {
+ current_key_info->key_length++;
+ current_key_info->key_part[j].null_bit =
+ current_key_info->key_part[j].field->null_bit;
+ }
+ }
+ if (key_info[i].flags & HA_NOSAME) {
+ grn_id key_id;
+ if ((error = storage_write_row_unique_index(table->record[0],
+ current_key_info,
+ index_tables[i],
+ &key_id)))
+ {
+ if (error == HA_ERR_FOUND_DUPP_KEY)
+ {
+ error = HA_ERR_FOUND_DUPP_UNIQUE;
+ }
+ break;
+ }
+ }
+ if ((error = storage_write_row_multiple_column_index(table->record[0],
+ record_id,
+ current_key_info,
+ index_columns[i])))
+ {
+ break;
+ }
+ }
+ if (error)
+ break;
+ }
+ if (error != HA_ERR_END_OF_FILE) {
+ storage_rnd_end();
+ } else {
+ error = storage_rnd_end();
+ }
+ }
+
+ DBUG_RETURN(error);
+}
+
+#ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER
+bool ha_mroonga::wrapper_is_comment_changed(TABLE *table1, TABLE *table2)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ if (table1->s->comment.length != table2->s->comment.length) {
+ DBUG_RETURN(true);
+ }
+
+ if (strncmp(table1->s->comment.str,
+ table2->s->comment.str,
+ table1->s->comment.length) == 0) {
+ DBUG_RETURN(false);
+ } else {
+ DBUG_RETURN(true);
+ }
+}
+
+enum_alter_inplace_result ha_mroonga::wrapper_check_if_supported_inplace_alter(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint n_keys;
+ uint i;
+ enum_alter_inplace_result result_mroonga = HA_ALTER_INPLACE_NO_LOCK;
+ DBUG_PRINT("info", ("mroonga: handler_flags=%lu", ha_alter_info->handler_flags));
+
+ if (wrapper_is_comment_changed(table, altered_table)) {
+ DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+ }
+ if (
+ (ha_alter_info->handler_flags & Alter_inplace_info::ADD_INDEX) &&
+ (ha_alter_info->handler_flags &
+ (
+ Alter_inplace_info::ADD_COLUMN |
+ Alter_inplace_info::DROP_COLUMN |
+ Alter_inplace_info::ALTER_COLUMN_TYPE |
+ Alter_inplace_info::ALTER_COLUMN_ORDER |
+ Alter_inplace_info::ALTER_COLUMN_NULLABLE |
+ Alter_inplace_info::ALTER_COLUMN_NOT_NULLABLE |
+ Alter_inplace_info::ALTER_COLUMN_STORAGE_TYPE |
+ Alter_inplace_info::ALTER_COLUMN_COLUMN_FORMAT
+ )
+ )
+ ) {
+ DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+ }
+ if (ha_alter_info->handler_flags & Alter_inplace_info::ALTER_RENAME)
+ {
+ DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+ }
+
+ DBUG_ASSERT(ha_alter_info->key_count == altered_table->s->keys);
+ alter_key_count = 0;
+ alter_index_drop_count = 0;
+ alter_index_add_count = 0;
+ alter_handler_flags = ha_alter_info->handler_flags;
+ if (!(alter_key_info_buffer = (KEY *)
+ my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
+ &alter_key_info_buffer, sizeof(KEY) * ha_alter_info->key_count,
+ &alter_index_drop_buffer, sizeof(KEY) * ha_alter_info->index_drop_count,
+ &alter_index_add_buffer, sizeof(uint) * ha_alter_info->index_add_count,
+ &wrap_altered_table, sizeof(TABLE),
+ &wrap_altered_table_key_info, sizeof(KEY) * altered_table->s->keys,
+ &wrap_altered_table_share, sizeof(TABLE_SHARE),
+ &wrap_altered_table_share_key_info, sizeof(KEY) * altered_table->s->keys,
+ NullS))
+ ) {
+ DBUG_RETURN(HA_ALTER_ERROR);
+ }
+ memcpy(wrap_altered_table, altered_table, sizeof(TABLE));
+ memcpy(wrap_altered_table_share, altered_table->s, sizeof(TABLE_SHARE));
+
+ n_keys = ha_alter_info->index_drop_count;
+ for (i = 0; i < n_keys; ++i) {
+ const KEY *key = ha_alter_info->index_drop_buffer[i];
+ if (key->flags & HA_FULLTEXT || mrn_is_geo_key(key)) {
+ result_mroonga = HA_ALTER_INPLACE_EXCLUSIVE_LOCK;
+ } else {
+ memcpy(&alter_index_drop_buffer[alter_index_drop_count],
+ ha_alter_info->index_drop_buffer[i], sizeof(KEY));
+ ++alter_index_drop_count;
+ }
+ }
+ if (!alter_index_drop_count) {
+ alter_handler_flags &= ~Alter_inplace_info::DROP_INDEX;
+ }
+ n_keys = ha_alter_info->index_add_count;
+ for (i = 0; i < n_keys; ++i) {
+ const KEY *key =
+ &altered_table->key_info[ha_alter_info->index_add_buffer[i]];
+ if (key->flags & HA_FULLTEXT || mrn_is_geo_key(key)) {
+ result_mroonga = HA_ALTER_INPLACE_EXCLUSIVE_LOCK;
+ } else {
+ alter_index_add_buffer[alter_index_add_count] =
+ ha_alter_info->index_add_buffer[i];
+ ++alter_index_add_count;
+ }
+ }
+ if (!alter_index_add_count) {
+ alter_handler_flags &= ~Alter_inplace_info::ADD_INDEX;
+ }
+ uint add_index_pos = 0;
+ n_keys = ha_alter_info->key_count;
+ for (i = 0; i < n_keys; ++i) {
+ const KEY *key = &altered_table->key_info[i];
+ if (!(key->flags & HA_FULLTEXT || mrn_is_geo_key(key))) {
+ memcpy(&alter_key_info_buffer[alter_key_count],
+ &ha_alter_info->key_info_buffer[i], sizeof(KEY));
+ memcpy(&wrap_altered_table_key_info[alter_key_count],
+ &altered_table->key_info[i], sizeof(KEY));
+ memcpy(&wrap_altered_table_share_key_info[alter_key_count],
+ &altered_table->s->key_info[i], sizeof(KEY));
+ if (add_index_pos < alter_index_add_count &&
+ alter_index_add_buffer[add_index_pos] == i) {
+ alter_index_add_buffer[add_index_pos] = alter_key_count;
+ ++add_index_pos;
+ }
+ ++alter_key_count;
+ }
+ }
+ wrap_altered_table->key_info = wrap_altered_table_key_info;
+ wrap_altered_table_share->key_info = wrap_altered_table_share_key_info;
+ wrap_altered_table_share->keys = alter_key_count;
+ wrap_altered_table->s = wrap_altered_table_share;
+
+ if (!alter_handler_flags) {
+ DBUG_RETURN(result_mroonga);
+ }
+ enum_alter_inplace_result result;
+ MRN_SET_WRAP_ALTER_KEY(this, ha_alter_info);
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ result = wrap_handler->check_if_supported_inplace_alter(wrap_altered_table,
+ ha_alter_info);
+ MRN_SET_BASE_ALTER_KEY(this, ha_alter_info);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ if (result_mroonga > result)
+ DBUG_RETURN(result);
+ DBUG_RETURN(result_mroonga);
+}
+
+enum_alter_inplace_result ha_mroonga::storage_check_if_supported_inplace_alter(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ Alter_inplace_info::HA_ALTER_FLAGS supported_flags =
+ Alter_inplace_info::ADD_INDEX |
+ Alter_inplace_info::DROP_INDEX |
+ Alter_inplace_info::ADD_UNIQUE_INDEX |
+ Alter_inplace_info::DROP_UNIQUE_INDEX |
+ Alter_inplace_info::ADD_PK_INDEX |
+ Alter_inplace_info::DROP_PK_INDEX |
+ Alter_inplace_info::ADD_COLUMN |
+ Alter_inplace_info::DROP_COLUMN |
+ Alter_inplace_info::ALTER_COLUMN_NAME;
+ if (ha_alter_info->handler_flags & supported_flags) {
+ DBUG_RETURN(HA_ALTER_INPLACE_EXCLUSIVE_LOCK);
+ } else {
+ DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+ }
+}
+
+enum_alter_inplace_result ha_mroonga::check_if_supported_inplace_alter(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ enum_alter_inplace_result result;
+ if (share->wrapper_mode) {
+ result = wrapper_check_if_supported_inplace_alter(altered_table,
+ ha_alter_info);
+ } else {
+ result = storage_check_if_supported_inplace_alter(altered_table,
+ ha_alter_info);
+ }
+ DBUG_RETURN(result);
+}
+
+bool ha_mroonga::wrapper_prepare_inplace_alter_table(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ bool result;
+ MRN_DBUG_ENTER_METHOD();
+ if (!alter_handler_flags) {
+ DBUG_RETURN(false);
+ }
+ MRN_SET_WRAP_ALTER_KEY(this, ha_alter_info);
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ result = wrap_handler->ha_prepare_inplace_alter_table(wrap_altered_table,
+ ha_alter_info);
+ MRN_SET_BASE_ALTER_KEY(this, ha_alter_info);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(result);
+}
+
+bool ha_mroonga::storage_prepare_inplace_alter_table(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(false);
+}
+
+bool ha_mroonga::prepare_inplace_alter_table(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool result;
+ if (share->wrapper_mode) {
+ result = wrapper_prepare_inplace_alter_table(altered_table, ha_alter_info);
+ } else {
+ result = storage_prepare_inplace_alter_table(altered_table, ha_alter_info);
+ }
+ DBUG_RETURN(result);
+}
+
+bool ha_mroonga::wrapper_inplace_alter_table(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ int error;
+ bool result = false;
+ uint n_keys;
+ uint i, j = 0;
+ KEY *key_info = table_share->key_info;
+ MRN_DBUG_ENTER_METHOD();
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(true);
+
+ DBUG_PRINT("info", ("mroonga: table_name=%s", share->table_name));
+ mrn::PathMapper mapper(share->table_name);
+ n_keys = ha_alter_info->index_drop_count;
+ for (i = 0; i < n_keys; ++i) {
+ const KEY *key = ha_alter_info->index_drop_buffer[i];
+ if (!(key->flags & HA_FULLTEXT || mrn_is_geo_key(key))) {
+ continue;
+ }
+ while (strcmp(key_info[j].name, key->name)) {
+ ++j;
+ }
+ DBUG_PRINT("info", ("mroonga: key_name=%s", key->name));
+ error = drop_index(share, j);
+ if (error)
+ DBUG_RETURN(true);
+ grn_index_tables[j] = NULL;
+ grn_index_columns[j] = NULL;
+ }
+
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables,
+ ha_alter_info->key_count);
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_columns,
+ ha_alter_info->key_count);
+ MRN_SHARE *tmp_share;
+ TABLE_SHARE tmp_table_share;
+ char **key_parser;
+ uint *key_parser_length;
+ KEY *p_key_info = &table->key_info[table_share->primary_key];
+ bool need_fill_index = false;
+ memset(index_tables, 0, sizeof(grn_obj *) * ha_alter_info->key_count);
+ memset(index_columns, 0, sizeof(grn_obj *) * ha_alter_info->key_count);
+ tmp_table_share.keys = ha_alter_info->key_count;
+ tmp_table_share.fields = 0;
+ if (!(tmp_share = (MRN_SHARE *)
+ my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
+ &tmp_share, sizeof(*tmp_share),
+ &key_parser, sizeof(char *) * (tmp_table_share.keys),
+ &key_parser_length, sizeof(uint) * (tmp_table_share.keys),
+ NullS))
+ ) {
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns);
+ DBUG_RETURN(true);
+ }
+ tmp_share->engine = NULL;
+ tmp_share->table_share = &tmp_table_share;
+ tmp_share->index_table = NULL;
+ tmp_share->index_table_length = NULL;
+ tmp_share->key_parser = key_parser;
+ tmp_share->key_parser_length = key_parser_length;
+ bitmap_clear_all(table->read_set);
+ mrn_set_bitmap_by_key(table->read_set, p_key_info);
+ n_keys = ha_alter_info->index_add_count;
+ for (i = 0; i < n_keys; ++i) {
+ uint key_pos = ha_alter_info->index_add_buffer[i];
+ KEY *key = &altered_table->key_info[key_pos];
+ if (!(key->flags & HA_FULLTEXT || mrn_is_geo_key(key))) {
+ continue;
+ }
+ if (share->disable_keys) {
+ continue;
+ }
+ if ((error = mrn_add_index_param(tmp_share, key, key_pos)))
+ {
+ break;
+ }
+ DBUG_PRINT("info", ("mroonga: add key pos=%u", key_pos));
+ if (
+ (key->flags & HA_FULLTEXT) &&
+ (error = wrapper_create_index_fulltext(mapper.table_name(),
+ key_pos,
+ key, index_tables, NULL,
+ tmp_share))
+ ) {
+ break;
+ } else if (
+ mrn_is_geo_key(key) &&
+ (error = wrapper_create_index_geo(mapper.table_name(),
+ key_pos, key,
+ index_tables, NULL, tmp_share))
+ ) {
+ break;
+ }
+ mrn_set_bitmap_by_key(table->read_set, key);
+ index_columns[key_pos] = grn_obj_column(ctx,
+ index_tables[key_pos],
+ INDEX_COLUMN_NAME,
+ strlen(INDEX_COLUMN_NAME));
+ need_fill_index = true;
+ }
+ if (!error && need_fill_index) {
+ my_ptrdiff_t ptr_diff = PTR_BYTE_DIFF(table->record[0], altered_table->record[0]);
+ uint n_columns = altered_table->s->fields;
+ for (i = 0; i < n_columns; ++i) {
+ Field *field = altered_table->field[i];
+ field->move_field_offset(ptr_diff);
+ }
+ error = wrapper_fill_indexes(ha_thd(), altered_table->key_info,
+ index_columns, ha_alter_info->key_count);
+ for (i = 0; i < n_columns; ++i) {
+ Field *field = altered_table->field[i];
+ field->move_field_offset(-ptr_diff);
+ }
+ }
+ bitmap_set_all(table->read_set);
+
+ if (!error && alter_handler_flags) {
+ MRN_SET_WRAP_ALTER_KEY(this, ha_alter_info);
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ result = wrap_handler->ha_inplace_alter_table(wrap_altered_table,
+ ha_alter_info);
+ MRN_SET_BASE_ALTER_KEY(this, ha_alter_info);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+
+ if (result || error)
+ {
+ n_keys = ha_alter_info->index_add_count;
+ for (i = 0; i < n_keys; ++i) {
+ uint key_pos = ha_alter_info->index_add_buffer[i];
+ KEY *key =
+ &altered_table->key_info[key_pos];
+ if (!(key->flags & HA_FULLTEXT || mrn_is_geo_key(key))) {
+ continue;
+ }
+ if (share->disable_keys) {
+ continue;
+ }
+ if (index_tables[key_pos])
+ {
+ grn_obj_remove(ctx, index_tables[key_pos]);
+ }
+ }
+ result = true;
+ }
+ mrn_free_share_alloc(tmp_share);
+ my_free(tmp_share, MYF(0));
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns);
+ DBUG_RETURN(result);
+}
+
+bool ha_mroonga::storage_inplace_alter_table_index(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ bool have_error = false;
+ int error = 0;
+ uint n_keys;
+ uint i, j = 0;
+ KEY *key_info = table_share->key_info;
+ mrn::PathMapper mapper(share->table_name);
+ n_keys = ha_alter_info->index_drop_count;
+ for (i = 0; i < n_keys; ++i) {
+ KEY *key = ha_alter_info->index_drop_buffer[i];
+ while (strcmp(key_info[j].name, key->name)) {
+ ++j;
+ }
+ error = drop_index(share, j);
+ if (error)
+ DBUG_RETURN(true);
+ grn_index_tables[j] = NULL;
+ grn_index_columns[j] = NULL;
+ }
+
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables,
+ ha_alter_info->key_count);
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_columns,
+ ha_alter_info->key_count);
+ MRN_SHARE *tmp_share;
+ TABLE_SHARE tmp_table_share;
+ char **index_table, **key_parser, **col_flags, **col_type;
+ uint *index_table_length, *key_parser_length, *col_flags_length, *col_type_length;
+ bool have_multiple_column_index = false;
+ memset(index_tables, 0, sizeof(grn_obj *) * ha_alter_info->key_count);
+ memset(index_columns, 0, sizeof(grn_obj *) * ha_alter_info->key_count);
+ tmp_table_share.keys = ha_alter_info->key_count;
+ tmp_table_share.fields = 0;
+ if (!(tmp_share = (MRN_SHARE *)
+ my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
+ &tmp_share, sizeof(*tmp_share),
+ &index_table, sizeof(char *) * tmp_table_share.keys,
+ &index_table_length, sizeof(uint) * tmp_table_share.keys,
+ &key_parser, sizeof(char *) * tmp_table_share.keys,
+ &key_parser_length, sizeof(uint) * tmp_table_share.keys,
+ &col_flags, sizeof(char *) * tmp_table_share.fields,
+ &col_flags_length, sizeof(uint) * tmp_table_share.fields,
+ &col_type, sizeof(char *) * tmp_table_share.fields,
+ &col_type_length, sizeof(uint) * tmp_table_share.fields,
+ NullS))
+ ) {
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns);
+ DBUG_RETURN(true);
+ }
+ tmp_share->engine = NULL;
+ tmp_share->table_share = &tmp_table_share;
+ tmp_share->index_table = index_table;
+ tmp_share->index_table_length = index_table_length;
+ tmp_share->key_parser = key_parser;
+ tmp_share->key_parser_length = key_parser_length;
+ tmp_share->col_flags = col_flags;
+ tmp_share->col_flags_length = col_flags_length;
+ tmp_share->col_type = col_type;
+ tmp_share->col_type_length = col_type_length;
+ bitmap_clear_all(table->read_set);
+ if (table_share->primary_key != MAX_KEY) {
+ KEY *p_key_info = &table->key_info[table_share->primary_key];
+ mrn_set_bitmap_by_key(table->read_set, p_key_info);
+ }
+ n_keys = ha_alter_info->index_add_count;
+ for (i = 0; i < n_keys; ++i) {
+ uint key_pos = ha_alter_info->index_add_buffer[i];
+ KEY *key =
+ &altered_table->key_info[key_pos];
+ if (share->disable_keys && !(key->flags & HA_NOSAME)) {
+ continue; // key is disabled
+ }
+ if ((error = mrn_add_index_param(tmp_share, key, key_pos)))
+ {
+ break;
+ }
+ DBUG_PRINT("info", ("mroonga: add key pos=%u", key_pos));
+ if ((error = storage_create_index(table, mapper.table_name(), grn_table,
+ tmp_share, key, index_tables,
+ index_columns, key_pos)))
+ {
+ break;
+ }
+ if (
+ KEY_N_KEY_PARTS(key) == 1 &&
+ (key->flags & HA_NOSAME) &&
+ grn_table_size(ctx, grn_table) !=
+ grn_table_size(ctx, index_tables[key_pos])
+ ) {
+ error = HA_ERR_FOUND_DUPP_UNIQUE;
+ ++i;
+ break;
+ }
+ if (
+ KEY_N_KEY_PARTS(key) != 1 &&
+ !(key->flags & HA_FULLTEXT)
+ ) {
+ mrn_set_bitmap_by_key(table->read_set, key);
+ have_multiple_column_index = true;
+ }
+ }
+ if (!error && have_multiple_column_index) {
+ my_ptrdiff_t ptr_diff = PTR_BYTE_DIFF(table->record[0], altered_table->record[0]);
+ uint n_columns = altered_table->s->fields;
+ for (i = 0; i < n_columns; ++i) {
+ Field *field = altered_table->field[i];
+ field->move_field_offset(ptr_diff);
+ }
+ error = storage_add_index_multiple_columns(altered_table->key_info,
+ ha_alter_info->key_count,
+ index_tables,
+ index_columns, false);
+ for (i = 0; i < n_columns; ++i) {
+ Field *field = altered_table->field[i];
+ field->move_field_offset(-ptr_diff);
+ }
+ }
+ bitmap_set_all(table->read_set);
+
+ if (error)
+ {
+ n_keys = ha_alter_info->index_add_count;
+ for (i = 0; i < n_keys; ++i) {
+ uint key_pos = ha_alter_info->index_add_buffer[i];
+ KEY *key =
+ &altered_table->key_info[key_pos];
+ if (share->disable_keys && !(key->flags & HA_NOSAME)) {
+ continue;
+ }
+ if (index_tables[key_pos])
+ {
+ grn_obj_remove(ctx, index_columns[key_pos]);
+ grn_obj_remove(ctx, index_tables[key_pos]);
+ }
+ }
+ have_error = true;
+ }
+ mrn_free_share_alloc(tmp_share);
+ my_free(tmp_share, MYF(0));
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns);
+
+ DBUG_RETURN(have_error);
+}
+
+bool ha_mroonga::storage_inplace_alter_table_add_column(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ bool have_error = false;
+
+ MRN_SHARE *tmp_share;
+ TABLE_SHARE tmp_table_share;
+ char **index_table, **key_parser, **col_flags, **col_type;
+ uint *index_table_length, *key_parser_length, *col_flags_length, *col_type_length;
+ tmp_table_share.keys = 0;
+ tmp_table_share.fields = altered_table->s->fields;
+ tmp_share = (MRN_SHARE *)my_multi_malloc(
+ MYF(MY_WME | MY_ZEROFILL),
+ &tmp_share, sizeof(*tmp_share),
+ &index_table, sizeof(char *) * tmp_table_share.keys,
+ &index_table_length, sizeof(uint) * tmp_table_share.keys,
+ &key_parser, sizeof(char *) * tmp_table_share.keys,
+ &key_parser_length, sizeof(uint) * tmp_table_share.keys,
+ &col_flags, sizeof(char *) * tmp_table_share.fields,
+ &col_flags_length, sizeof(uint) * tmp_table_share.fields,
+ &col_type, sizeof(char *) * tmp_table_share.fields,
+ &col_type_length, sizeof(uint) * tmp_table_share.fields,
+ NullS);
+ if (!tmp_share) {
+ have_error = true;
+ DBUG_RETURN(have_error);
+ }
+ tmp_share->engine = NULL;
+ tmp_share->table_share = &tmp_table_share;
+ tmp_share->index_table = index_table;
+ tmp_share->index_table_length = index_table_length;
+ tmp_share->key_parser = key_parser;
+ tmp_share->key_parser_length = key_parser_length;
+ tmp_share->col_flags = col_flags;
+ tmp_share->col_flags_length = col_flags_length;
+ tmp_share->col_type = col_type;
+ tmp_share->col_type_length = col_type_length;
+
+ mrn::PathMapper mapper(share->table_name);
+ grn_obj *table_obj;
+ table_obj = grn_ctx_get(ctx, mapper.table_name(), strlen(mapper.table_name()));
+
+ Alter_info *alter_info = ha_alter_info->alter_info;
+ List_iterator_fast<Create_field> create_fields(alter_info->create_list);
+ for (uint i = 0; Create_field *create_field = create_fields++; i++) {
+ if (create_field->field) {
+ continue;
+ }
+
+ grn_obj *col_type;
+ Field *field = altered_table->s->field[i];
+ const char *column_name = field->field_name;
+ int column_name_size = strlen(column_name);
+
+ int error = mrn_add_column_param(tmp_share, field, i);
+ if (error) {
+ have_error = true;
+ break;
+ }
+
+ grn_obj_flags col_flags = GRN_OBJ_PERSISTENT;
+ if (tmp_share->col_flags[i]) {
+ // TODO: parse flags
+ if (strcmp(tmp_share->col_flags[i], "COLUMN_VECTOR") == 0) {
+ col_flags |= GRN_OBJ_COLUMN_VECTOR;
+ } else {
+ col_flags |= GRN_OBJ_COLUMN_SCALAR;
+ }
+ } else {
+ col_flags |= GRN_OBJ_COLUMN_SCALAR;
+ }
+
+ grn_builtin_type gtype = mrn_grn_type_from_field(ctx, field, false);
+ if (tmp_share->col_type[i]) {
+ col_type = grn_ctx_get(ctx, tmp_share->col_type[i], -1);
+ } else {
+ col_type = grn_ctx_at(ctx, gtype);
+ }
+ char *col_path = NULL; // we don't specify path
+
+ grn_obj *column_obj =
+ grn_column_create(ctx, table_obj, column_name, column_name_size,
+ col_path, col_flags, col_type);
+ if (ctx->rc) {
+ error = ER_WRONG_COLUMN_NAME;
+ my_message(error, ctx->errbuf, MYF(0));
+ have_error = true;
+ }
+ if (column_obj) {
+ grn_obj_unlink(ctx, column_obj);
+ }
+
+ if (have_error) {
+ break;
+ }
+ }
+
+ grn_obj_unlink(ctx, table_obj);
+
+ mrn_free_share_alloc(tmp_share);
+ my_free(tmp_share, MYF(0));
+
+ DBUG_RETURN(have_error);
+}
+
+bool ha_mroonga::storage_inplace_alter_table_drop_column(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ bool have_error = false;
+
+ mrn::PathMapper mapper(share->table_name);
+ grn_obj *table_obj;
+ table_obj = grn_ctx_get(ctx, mapper.table_name(), strlen(mapper.table_name()));
+
+ Alter_info *alter_info = ha_alter_info->alter_info;
+
+ uint n_fields = table->s->fields;
+ for (uint i = 0; i < n_fields; i++) {
+ Field *field = table->field[i];
+
+ bool dropped = true;
+ List_iterator_fast<Create_field> create_fields(alter_info->create_list);
+ while (Create_field *create_field = create_fields++) {
+ if (create_field->field == field) {
+ dropped = false;
+ break;
+ }
+ }
+ if (!dropped) {
+ continue;
+ }
+
+ const char *column_name = field->field_name;
+ int column_name_size = strlen(column_name);
+
+ grn_obj *column_obj;
+ column_obj = grn_obj_column(ctx, table_obj, column_name, column_name_size);
+ if (column_obj) {
+ grn_obj_remove(ctx, column_obj);
+ }
+ if (ctx->rc) {
+ int error = ER_WRONG_COLUMN_NAME;
+ my_message(error, ctx->errbuf, MYF(0));
+ have_error = true;
+ break;
+ }
+ }
+ grn_obj_unlink(ctx, table_obj);
+
+ DBUG_RETURN(have_error);
+}
+
+bool ha_mroonga::storage_inplace_alter_table_rename_column(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ bool have_error = false;
+
+ mrn::PathMapper mapper(share->table_name);
+ grn_obj *table_obj;
+ table_obj = grn_ctx_get(ctx, mapper.table_name(), strlen(mapper.table_name()));
+
+ Alter_info *alter_info = ha_alter_info->alter_info;
+ uint n_fields = table->s->fields;
+ for (uint i = 0; i < n_fields; i++) {
+ Field *field = table->field[i];
+
+ if (!(field->flags & FIELD_IS_RENAMED)) {
+ continue;
+ }
+
+ const char *new_name = NULL;
+ List_iterator_fast<Create_field> create_fields(alter_info->create_list);
+ while (Create_field *create_field = create_fields++) {
+ if (create_field->field == field) {
+ new_name = create_field->field_name;
+ break;
+ }
+ }
+
+ if (!new_name) {
+ continue;
+ }
+
+ const char *old_name = field->field_name;
+ grn_obj *column_obj;
+ column_obj = grn_obj_column(ctx, table_obj, old_name, strlen(old_name));
+ if (column_obj) {
+ grn_column_rename(ctx, column_obj, new_name, strlen(new_name));
+ if (ctx->rc) {
+ int error = ER_WRONG_COLUMN_NAME;
+ my_message(error, ctx->errbuf, MYF(0));
+ have_error = true;
+ }
+ grn_obj_unlink(ctx, column_obj);
+ }
+
+ if (have_error) {
+ break;
+ }
+ }
+ grn_obj_unlink(ctx, table_obj);
+
+ DBUG_RETURN(have_error);
+}
+
+bool ha_mroonga::storage_inplace_alter_table(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+
+ bool have_error = false;
+
+ int error = mrn_change_encoding(ctx, system_charset_info);
+ if (error) {
+ have_error = true;
+ }
+
+ Alter_inplace_info::HA_ALTER_FLAGS index_related_flags =
+ Alter_inplace_info::ADD_INDEX |
+ Alter_inplace_info::DROP_INDEX |
+ Alter_inplace_info::ADD_UNIQUE_INDEX |
+ Alter_inplace_info::DROP_UNIQUE_INDEX |
+ Alter_inplace_info::ADD_PK_INDEX |
+ Alter_inplace_info::DROP_PK_INDEX;
+ if (!have_error &&
+ (ha_alter_info->handler_flags & index_related_flags)) {
+ have_error = storage_inplace_alter_table_index(altered_table, ha_alter_info);
+ }
+
+ Alter_inplace_info::HA_ALTER_FLAGS add_column_related_flags =
+ Alter_inplace_info::ADD_COLUMN;
+ if (!have_error &&
+ (ha_alter_info->handler_flags & add_column_related_flags)) {
+ have_error = storage_inplace_alter_table_add_column(altered_table, ha_alter_info);
+ }
+
+ Alter_inplace_info::HA_ALTER_FLAGS drop_column_related_flags =
+ Alter_inplace_info::DROP_COLUMN;
+ if (!have_error &&
+ (ha_alter_info->handler_flags & drop_column_related_flags)) {
+ have_error = storage_inplace_alter_table_drop_column(altered_table, ha_alter_info);
+ }
+
+ Alter_inplace_info::HA_ALTER_FLAGS rename_column_related_flags =
+ Alter_inplace_info::ALTER_COLUMN_NAME;
+ if (!have_error &&
+ (ha_alter_info->handler_flags & rename_column_related_flags)) {
+ have_error = storage_inplace_alter_table_rename_column(altered_table, ha_alter_info);
+ }
+
+ DBUG_RETURN(have_error);
+}
+
+bool ha_mroonga::inplace_alter_table(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool result;
+ if (share->wrapper_mode) {
+ result = wrapper_inplace_alter_table(altered_table, ha_alter_info);
+ } else {
+ result = storage_inplace_alter_table(altered_table, ha_alter_info);
+ }
+ DBUG_RETURN(result);
+}
+
+bool ha_mroonga::wrapper_commit_inplace_alter_table(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit)
+{
+ bool result;
+ MRN_DBUG_ENTER_METHOD();
+ if (!alter_handler_flags) {
+ my_free(alter_key_info_buffer, MYF(0));
+ alter_key_info_buffer = NULL;
+ DBUG_RETURN(false);
+ }
+ MRN_SET_WRAP_ALTER_KEY(this, ha_alter_info);
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ result = wrap_handler->ha_commit_inplace_alter_table(wrap_altered_table,
+ ha_alter_info,
+ commit);
+ MRN_SET_BASE_ALTER_KEY(this, ha_alter_info);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ my_free(alter_key_info_buffer, MYF(0));
+ alter_key_info_buffer = NULL;
+ DBUG_RETURN(result);
+}
+
+bool ha_mroonga::storage_commit_inplace_alter_table(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(false);
+}
+
+bool ha_mroonga::commit_inplace_alter_table(
+ TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit)
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool result;
+ if (share->wrapper_mode) {
+ result = wrapper_commit_inplace_alter_table(altered_table, ha_alter_info,
+ commit);
+ } else {
+ result = storage_commit_inplace_alter_table(altered_table, ha_alter_info,
+ commit);
+ }
+ DBUG_RETURN(result);
+}
+
+void ha_mroonga::wrapper_notify_table_changed()
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->ha_notify_table_changed();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_notify_table_changed()
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::notify_table_changed()
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode) {
+ wrapper_notify_table_changed();
+ } else {
+ storage_notify_table_changed();
+ }
+ DBUG_VOID_RETURN;
+}
+#else
+uint ha_mroonga::wrapper_alter_table_flags(uint flags)
+{
+ uint res;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->alter_table_flags(flags);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+uint ha_mroonga::storage_alter_table_flags(uint flags)
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint res = handler::alter_table_flags(flags);
+ DBUG_RETURN(res);
+}
+
+uint ha_mroonga::alter_table_flags(uint flags)
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint res;
+ if (share->wrapper_mode)
+ {
+ res = wrapper_alter_table_flags(flags);
+ } else {
+ res = storage_alter_table_flags(flags);
+ }
+ DBUG_RETURN(res);
+}
+
+#ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX
+int ha_mroonga::wrapper_add_index(TABLE *table_arg, KEY *key_info,
+ uint num_of_keys, handler_add_index **add)
+#else
+int ha_mroonga::wrapper_add_index(TABLE *table_arg, KEY *key_info,
+ uint num_of_keys)
+#endif
+{
+ int error = 0;
+ uint i, j, k;
+ uint n_keys = table->s->keys;
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, num_of_keys + n_keys);
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_columns, num_of_keys + n_keys);
+ THD *thd = ha_thd();
+ MRN_SHARE *tmp_share;
+ TABLE_SHARE tmp_table_share;
+ char **key_parser;
+ uint *key_parser_length;
+ MRN_DBUG_ENTER_METHOD();
+ if (!(wrap_alter_key_info = (KEY *) my_malloc(sizeof(KEY) * num_of_keys,
+ MYF(MY_WME)))) {
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns);
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+ KEY *p_key_info = &table->key_info[table_share->primary_key], *tmp_key_info;
+ tmp_table_share.keys = n_keys + num_of_keys;
+ tmp_table_share.fields = 0;
+ if (!(tmp_share = (MRN_SHARE *)
+ my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
+ &tmp_share, sizeof(*tmp_share),
+ &key_parser, sizeof(char *) * (n_keys + num_of_keys),
+ &key_parser_length, sizeof(uint) * (n_keys + num_of_keys),
+ NullS))
+ ) {
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns);
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+ tmp_share->engine = NULL;
+ tmp_share->table_share = &tmp_table_share;
+ tmp_share->index_table = NULL;
+ tmp_share->index_table_length = NULL;
+ tmp_share->key_parser = key_parser;
+ tmp_share->key_parser_length = key_parser_length;
+ tmp_share->col_flags = NULL;
+ tmp_share->col_type = NULL;
+#ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX
+ hnd_add_index = NULL;
+#endif
+ bitmap_clear_all(table->read_set);
+ mrn_set_bitmap_by_key(table->read_set, p_key_info);
+ mrn::PathMapper mapper(share->table_name);
+ for (i = 0, j = 0; i < num_of_keys; i++) {
+ if (!(key_info[i].flags & HA_FULLTEXT) && !mrn_is_geo_key(&key_info[i])) {
+ wrap_alter_key_info[j] = key_info[i];
+ j++;
+ continue;
+ }
+ if (share->disable_keys) {
+ continue;
+ }
+ if ((error = mrn_add_index_param(tmp_share, &key_info[i], i + n_keys)))
+ {
+ break;
+ }
+ index_tables[i + n_keys] = NULL;
+ if (
+ (key_info[i].flags & HA_FULLTEXT) &&
+ (error = wrapper_create_index_fulltext(mapper.table_name(),
+ i + n_keys,
+ &key_info[i], index_tables, NULL,
+ tmp_share))
+ ) {
+ break;
+ } else if (
+ mrn_is_geo_key(&key_info[i]) &&
+ (error = wrapper_create_index_geo(mapper.table_name(),
+ i + n_keys, &key_info[i],
+ index_tables, NULL, tmp_share))
+ ) {
+ break;
+ }
+ mrn_set_bitmap_by_key(table->read_set, &key_info[i]);
+ }
+ if (!error && i > j && !share->disable_keys) {
+ for (k = 0; k < num_of_keys; k++) {
+ tmp_key_info = &key_info[k];
+ if (!(tmp_key_info->flags & HA_FULLTEXT) &&
+ !mrn_is_geo_key(tmp_key_info)) {
+ continue;
+ }
+ index_columns[k + n_keys] = grn_obj_column(ctx,
+ index_tables[k + n_keys],
+ INDEX_COLUMN_NAME,
+ strlen(INDEX_COLUMN_NAME));
+ }
+ error = wrapper_fill_indexes(thd, key_info, &index_columns[n_keys],
+ num_of_keys);
+ }
+ bitmap_set_all(table->read_set);
+
+ if (!error && j)
+ {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+#ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX
+ error = wrap_handler->add_index(table_arg, wrap_alter_key_info, j,
+ &hnd_add_index);
+#else
+ error = wrap_handler->add_index(table_arg, wrap_alter_key_info, j);
+#endif
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ if (error)
+ {
+ for (k = 0; k < i; k++) {
+ if (!(key_info[k].flags & HA_FULLTEXT) && !mrn_is_geo_key(&key_info[k]))
+ {
+ continue;
+ }
+ if (index_tables[k + n_keys])
+ {
+ grn_obj_remove(ctx, index_tables[k + n_keys]);
+ }
+ }
+ }
+#ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX
+ else {
+ *add = new handler_add_index(table_arg, key_info, num_of_keys);
+ }
+#endif
+ mrn_free_share_alloc(tmp_share);
+ my_free(tmp_share, MYF(0));
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns);
+ DBUG_RETURN(error);
+}
+
+#ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX
+int ha_mroonga::storage_add_index(TABLE *table_arg, KEY *key_info,
+ uint num_of_keys, handler_add_index **add)
+#else
+int ha_mroonga::storage_add_index(TABLE *table_arg, KEY *key_info,
+ uint num_of_keys)
+#endif
+{
+ int error = 0;
+ uint i;
+ uint n_keys = table->s->keys;
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_tables, num_of_keys + n_keys);
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(grn_obj *, index_columns, num_of_keys + n_keys);
+ MRN_SHARE *tmp_share;
+ TABLE_SHARE tmp_table_share;
+ char **index_table, **key_parser, **col_flags, **col_type;
+ uint *index_table_length, *key_parser_length, *col_flags_length, *col_type_length;
+ bool have_multiple_column_index = false;
+
+ MRN_DBUG_ENTER_METHOD();
+ tmp_table_share.keys = n_keys + num_of_keys;
+ tmp_table_share.fields = 0;
+ if (!(tmp_share = (MRN_SHARE *)
+ my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
+ &tmp_share, sizeof(*tmp_share),
+ &index_table, sizeof(char*) * tmp_table_share.keys,
+ &index_table_length, sizeof(uint) * tmp_table_share.keys,
+ &key_parser, sizeof(char *) * tmp_table_share.keys,
+ &key_parser_length, sizeof(uint) * tmp_table_share.keys,
+ &col_flags, sizeof(char *) * tmp_table_share.fields,
+ &col_flags_length, sizeof(uint) * tmp_table_share.fields,
+ &col_type, sizeof(char *) * tmp_table_share.fields,
+ &col_type_length, sizeof(uint) * tmp_table_share.fields,
+ NullS))
+ ) {
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns);
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+ tmp_share->engine = NULL;
+ tmp_share->table_share = &tmp_table_share;
+ tmp_share->index_table = index_table;
+ tmp_share->index_table_length = index_table_length;
+ tmp_share->key_parser = key_parser;
+ tmp_share->key_parser_length = key_parser_length;
+ tmp_share->col_flags = col_flags;
+ tmp_share->col_flags_length = col_flags_length;
+ tmp_share->col_type = col_type;
+ tmp_share->col_type_length = col_type_length;
+ bitmap_clear_all(table->read_set);
+ mrn::PathMapper mapper(share->table_name);
+ for (i = 0; i < num_of_keys; i++) {
+ if (share->disable_keys && !(key_info[i].flags & HA_NOSAME)) {
+ continue; // key is disabled
+ }
+ index_tables[i + n_keys] = NULL;
+ index_columns[i + n_keys] = NULL;
+ if ((error = mrn_add_index_param(tmp_share, &key_info[i], i + n_keys)))
+ {
+ break;
+ }
+ if ((error = storage_create_index(table, mapper.table_name(), grn_table,
+ tmp_share, &key_info[i], index_tables,
+ index_columns, i + n_keys)))
+ {
+ break;
+ }
+ if (
+ KEY_N_KEY_PARTS(&(key_info[i])) == 1 &&
+ (key_info[i].flags & HA_NOSAME) &&
+ grn_table_size(ctx, grn_table) !=
+ grn_table_size(ctx, index_tables[i + n_keys])
+ ) {
+ error = HA_ERR_FOUND_DUPP_UNIQUE;
+ i++;
+ break;
+ }
+ if (
+ KEY_N_KEY_PARTS(&(key_info[i])) != 1 &&
+ !(key_info[i].flags & HA_FULLTEXT)
+ ) {
+ mrn_set_bitmap_by_key(table->read_set, &key_info[i]);
+ have_multiple_column_index = true;
+ }
+ }
+ if (!error && have_multiple_column_index)
+ {
+ error = storage_add_index_multiple_columns(key_info, num_of_keys,
+ index_tables + n_keys,
+ index_columns + n_keys, false);
+ }
+ bitmap_set_all(table->read_set);
+ if (error)
+ {
+ for (uint j = 0; j < i; j++) {
+ if (index_tables[j + n_keys])
+ {
+ grn_obj_remove(ctx, index_columns[j + n_keys]);
+ grn_obj_remove(ctx, index_tables[j + n_keys]);
+ }
+ }
+ }
+#ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX
+ else {
+ *add = new handler_add_index(table_arg, key_info, num_of_keys);
+ }
+#endif
+ mrn_free_share_alloc(tmp_share);
+ my_free(tmp_share, MYF(0));
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_tables);
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(index_columns);
+ DBUG_RETURN(error);
+}
+#ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX
+int ha_mroonga::add_index(TABLE *table_arg, KEY *key_info,
+ uint num_of_keys, handler_add_index **add)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_add_index(table_arg, key_info, num_of_keys, add);
+ } else {
+ error = storage_add_index(table_arg, key_info, num_of_keys, add);
+ }
+ DBUG_RETURN(error);
+}
+#else
+int ha_mroonga::add_index(TABLE *table_arg, KEY *key_info,
+ uint num_of_keys)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_add_index(table_arg, key_info, num_of_keys);
+ } else {
+ error = storage_add_index(table_arg, key_info, num_of_keys);
+ }
+ DBUG_RETURN(error);
+}
+#endif
+
+#ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX
+int ha_mroonga::wrapper_final_add_index(handler_add_index *add, bool commit)
+{
+ int error = 0;
+ MRN_DBUG_ENTER_METHOD();
+ if (hnd_add_index)
+ {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ error = wrap_handler->final_add_index(hnd_add_index, commit);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ if (add)
+ {
+ delete add;
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_final_add_index(handler_add_index *add, bool commit)
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (add)
+ {
+ delete add;
+ }
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::final_add_index(handler_add_index *add, bool commit)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error;
+ if (share->wrapper_mode)
+ {
+ error = wrapper_final_add_index(add, commit);
+ } else {
+ error = storage_final_add_index(add, commit);
+ }
+ DBUG_RETURN(error);
+}
+#endif
+
+int ha_mroonga::wrapper_prepare_drop_index(TABLE *table_arg, uint *key_num,
+ uint num_of_keys)
+{
+ int res = 0;
+ uint i, j;
+ KEY *key_info = table_share->key_info;
+ MRN_DBUG_ENTER_METHOD();
+ res = mrn_change_encoding(ctx, system_charset_info);
+ if (res)
+ DBUG_RETURN(res);
+
+ MRN_ALLOCATE_VARIABLE_LENGTH_ARRAYS(uint, wrap_key_num, num_of_keys);
+ for (i = 0, j = 0; i < num_of_keys; i++) {
+ uint key_index = key_num[i];
+ if (!(key_info[key_index].flags & HA_FULLTEXT) &&
+ !mrn_is_geo_key(&key_info[key_index])) {
+ wrap_key_num[j] = share->wrap_key_nr[key_index];
+ j++;
+ continue;
+ }
+
+ res = drop_index(share, key_index);
+ if (res)
+ DBUG_RETURN(res);
+ grn_index_tables[key_index] = NULL;
+ grn_index_columns[key_index] = NULL;
+ }
+ if (j)
+ {
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->prepare_drop_index(table_arg, wrap_key_num, j);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ }
+ MRN_FREE_VARIABLE_LENGTH_ARRAYS(wrap_key_num);
+ DBUG_RETURN(res);
+}
+
+int ha_mroonga::storage_prepare_drop_index(TABLE *table_arg, uint *key_num,
+ uint num_of_keys)
+{
+ int error;
+ uint i;
+ MRN_DBUG_ENTER_METHOD();
+ error = mrn_change_encoding(ctx, system_charset_info);
+ if (error)
+ DBUG_RETURN(error);
+
+ for (i = 0; i < num_of_keys; i++) {
+ uint key_index = key_num[i];
+ error = drop_index(share, key_index);
+ if (error)
+ break;
+ grn_index_tables[key_index] = NULL;
+ grn_index_columns[key_index] = NULL;
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::prepare_drop_index(TABLE *table_arg, uint *key_num,
+ uint num_of_keys)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int res;
+ if (share->wrapper_mode)
+ {
+ res = wrapper_prepare_drop_index(table_arg, key_num, num_of_keys);
+ } else {
+ res = storage_prepare_drop_index(table_arg, key_num, num_of_keys);
+ }
+ DBUG_RETURN(res);
+}
+
+int ha_mroonga::wrapper_final_drop_index(TABLE *table_arg)
+{
+ uint res;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->final_drop_index(table_arg);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+int ha_mroonga::storage_final_drop_index(TABLE *table_arg)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::final_drop_index(TABLE *table_arg)
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint res;
+ if (share->wrapper_mode)
+ {
+ res = wrapper_final_drop_index(table_arg);
+ } else {
+ res = storage_final_drop_index(table_arg);
+ }
+ DBUG_RETURN(res);
+}
+#endif
+
+int ha_mroonga::wrapper_update_auto_increment()
+{
+ int res;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->update_auto_increment();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+int ha_mroonga::storage_update_auto_increment()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int res = handler::update_auto_increment();
+ DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu",
+ table->next_number_field->val_int()));
+ DBUG_RETURN(res);
+}
+
+int ha_mroonga::update_auto_increment()
+{
+ MRN_DBUG_ENTER_METHOD();
+ int res;
+ if (share->wrapper_mode)
+ {
+ res = wrapper_update_auto_increment();
+ } else {
+ res = storage_update_auto_increment();
+ }
+ DBUG_RETURN(res);
+}
+
+void ha_mroonga::wrapper_set_next_insert_id(ulonglong id)
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->set_next_insert_id(id);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_set_next_insert_id(ulonglong id)
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler::set_next_insert_id(id);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::set_next_insert_id(ulonglong id)
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ {
+ wrapper_set_next_insert_id(id);
+ } else {
+ storage_set_next_insert_id(id);
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::wrapper_get_auto_increment(ulonglong offset,
+ ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values)
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->get_auto_increment(offset, increment, nb_desired_values,
+ first_value, nb_reserved_values);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_get_auto_increment(ulonglong offset,
+ ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values)
+{
+ MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share;
+ MRN_DBUG_ENTER_METHOD();
+ if (table->found_next_number_field &&
+ !table->s->next_number_keypart) {
+ if (long_term_share->auto_inc_inited) {
+ *first_value = long_term_share->auto_inc_value;
+ DBUG_PRINT("info", ("mroonga: *first_value(auto_inc_value)=%llu",
+ *first_value));
+ *nb_reserved_values = ULONGLONG_MAX;
+ } else {
+ handler::get_auto_increment(offset, increment, nb_desired_values,
+ first_value, nb_reserved_values);
+ long_term_share->auto_inc_value = *first_value;
+ DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu",
+ long_term_share->auto_inc_value));
+ long_term_share->auto_inc_inited = true;
+ }
+ } else {
+ handler::get_auto_increment(offset, increment, nb_desired_values,
+ first_value, nb_reserved_values);
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values)
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ {
+ wrapper_get_auto_increment(offset, increment, nb_desired_values,
+ first_value, nb_reserved_values);
+ } else {
+ MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share;
+ mrn::Lock lock(&long_term_share->auto_inc_mutex);
+ storage_get_auto_increment(offset, increment, nb_desired_values,
+ first_value, nb_reserved_values);
+ long_term_share->auto_inc_value += nb_desired_values * increment;
+ DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu",
+ long_term_share->auto_inc_value));
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::wrapper_restore_auto_increment(ulonglong prev_insert_id)
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->restore_auto_increment(prev_insert_id);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_restore_auto_increment(ulonglong prev_insert_id)
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler::restore_auto_increment(prev_insert_id);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::restore_auto_increment(ulonglong prev_insert_id)
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ {
+ wrapper_restore_auto_increment(prev_insert_id);
+ } else {
+ storage_restore_auto_increment(prev_insert_id);
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::wrapper_release_auto_increment()
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->ha_release_auto_increment();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_release_auto_increment()
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::release_auto_increment()
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ {
+ wrapper_release_auto_increment();
+ } else {
+ storage_release_auto_increment();
+ }
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::wrapper_check_for_upgrade(HA_CHECK_OPT *check_opt)
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ int error = wrap_handler->ha_check_for_upgrade(check_opt);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::storage_check_for_upgrade(HA_CHECK_OPT *check_opt)
+{
+ MRN_DBUG_ENTER_METHOD();
+ for (uint i = 0; i < table->s->fields; ++i) {
+ grn_obj *column = grn_columns[i];
+ if (!column) {
+ continue;
+ }
+ Field *field = table->field[i];
+ grn_id column_range = grn_obj_get_range(ctx, column);
+ switch (field->real_type()) {
+ case MYSQL_TYPE_ENUM:
+ if (column_range != GRN_DB_UINT16) {
+ DBUG_RETURN(HA_ADMIN_NEEDS_ALTER);
+ }
+ break;
+ case MYSQL_TYPE_SET:
+ if (column_range != GRN_DB_UINT64) {
+ DBUG_RETURN(HA_ADMIN_NEEDS_ALTER);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ DBUG_RETURN(HA_ADMIN_OK);
+}
+
+int ha_mroonga::check_for_upgrade(HA_CHECK_OPT *check_opt)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int error;
+ if (share->wrapper_mode) {
+ error = wrapper_check_for_upgrade(check_opt);
+ } else {
+ error = storage_check_for_upgrade(check_opt);
+ }
+ DBUG_RETURN(error);
+}
+
+int ha_mroonga::wrapper_reset_auto_increment(ulonglong value)
+{
+ int res;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->ha_reset_auto_increment(value);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+int ha_mroonga::storage_reset_auto_increment(ulonglong value)
+{
+ MRN_LONG_TERM_SHARE *long_term_share = share->long_term_share;
+ MRN_DBUG_ENTER_METHOD();
+ mrn::Lock lock(&long_term_share->auto_inc_mutex);
+ long_term_share->auto_inc_value = value;
+ DBUG_PRINT("info", ("mroonga: auto_inc_value=%llu",
+ long_term_share->auto_inc_value));
+ long_term_share->auto_inc_inited = true;
+ DBUG_RETURN(0);
+}
+
+int ha_mroonga::reset_auto_increment(ulonglong value)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int res;
+ if (share->wrapper_mode)
+ {
+ res = wrapper_reset_auto_increment(value);
+ } else {
+ res = storage_reset_auto_increment(value);
+ }
+ DBUG_RETURN(res);
+}
+
+void ha_mroonga::set_pk_bitmap()
+{
+ KEY key_info = table->key_info[table_share->primary_key];
+ uint j;
+ MRN_DBUG_ENTER_METHOD();
+ for (j = 0; j < KEY_N_KEY_PARTS(&key_info); j++) {
+ Field *field = key_info.key_part[j].field;
+ bitmap_set_bit(table->read_set, field->field_index);
+ }
+ DBUG_VOID_RETURN;
+}
+
+bool ha_mroonga::wrapper_was_semi_consistent_read()
+{
+ bool res;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->was_semi_consistent_read();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+bool ha_mroonga::storage_was_semi_consistent_read()
+{
+ bool res;
+ MRN_DBUG_ENTER_METHOD();
+ res = handler::was_semi_consistent_read();
+ DBUG_RETURN(res);
+}
+
+bool ha_mroonga::was_semi_consistent_read()
+{
+ bool res;
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ {
+ res = wrapper_was_semi_consistent_read();
+ } else {
+ res = storage_was_semi_consistent_read();
+ }
+ DBUG_RETURN(res);
+}
+
+void ha_mroonga::wrapper_try_semi_consistent_read(bool yes)
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->try_semi_consistent_read(yes);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_try_semi_consistent_read(bool yes)
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler::try_semi_consistent_read(yes);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::try_semi_consistent_read(bool yes)
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ {
+ wrapper_try_semi_consistent_read(yes);
+ } else {
+ storage_try_semi_consistent_read(yes);
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::wrapper_unlock_row()
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->unlock_row();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_unlock_row()
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler::unlock_row();
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::unlock_row()
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ {
+ wrapper_unlock_row();
+ } else {
+ storage_unlock_row();
+ }
+ DBUG_VOID_RETURN;
+}
+
+int ha_mroonga::wrapper_start_stmt(THD *thd, thr_lock_type lock_type)
+{
+ int res;
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->start_stmt(thd, lock_type);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+int ha_mroonga::storage_start_stmt(THD *thd, thr_lock_type lock_type)
+{
+ int res;
+ MRN_DBUG_ENTER_METHOD();
+ res = handler::start_stmt(thd, lock_type);
+ DBUG_RETURN(res);
+}
+
+int ha_mroonga::start_stmt(THD *thd, thr_lock_type lock_type)
+{
+ int res;
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ {
+ res = wrapper_start_stmt(thd, lock_type);
+ } else {
+ res = storage_start_stmt(thd, lock_type);
+ }
+ DBUG_RETURN(res);
+}
+
+void ha_mroonga::wrapper_change_table_ptr(TABLE *table_arg,
+ TABLE_SHARE *share_arg)
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->change_table_ptr(table_arg, share->wrap_table_share);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_change_table_ptr(TABLE *table_arg,
+ TABLE_SHARE *share_arg)
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::change_table_ptr(TABLE *table_arg, TABLE_SHARE *share_arg)
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler::change_table_ptr(table_arg, share_arg);
+ if (share && share->wrapper_mode)
+ {
+ wrapper_change_table_ptr(table_arg, share_arg);
+ } else {
+ storage_change_table_ptr(table_arg, share_arg);
+ }
+ DBUG_VOID_RETURN;
+}
+
+bool ha_mroonga::wrapper_primary_key_is_clustered()
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool is_clustered;
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ is_clustered = wrap_handler->primary_key_is_clustered();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(is_clustered);
+}
+
+bool ha_mroonga::storage_primary_key_is_clustered()
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool is_clustered = handler::primary_key_is_clustered();
+ DBUG_RETURN(is_clustered);
+}
+
+bool ha_mroonga::primary_key_is_clustered()
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool is_clustered;
+ if (share && share->wrapper_mode)
+ {
+ is_clustered = wrapper_primary_key_is_clustered();
+ } else {
+ is_clustered = storage_primary_key_is_clustered();
+ }
+ DBUG_RETURN(is_clustered);
+}
+
+bool ha_mroonga::wrapper_is_fk_defined_on_table_or_index(uint index)
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool res;
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->is_fk_defined_on_table_or_index(index);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+bool ha_mroonga::storage_is_fk_defined_on_table_or_index(uint index)
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool res = handler::is_fk_defined_on_table_or_index(index);
+ DBUG_RETURN(res);
+}
+
+bool ha_mroonga::is_fk_defined_on_table_or_index(uint index)
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool res;
+ if (share->wrapper_mode)
+ {
+ res = wrapper_is_fk_defined_on_table_or_index(index);
+ } else {
+ res = storage_is_fk_defined_on_table_or_index(index);
+ }
+ DBUG_RETURN(res);
+}
+
+char *ha_mroonga::wrapper_get_foreign_key_create_info()
+{
+ MRN_DBUG_ENTER_METHOD();
+ char *res;
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->get_foreign_key_create_info();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+#ifdef MRN_SUPPORT_FOREIGN_KEYS
+char *ha_mroonga::storage_get_foreign_key_create_info()
+{
+ int error;
+ uint i;
+ grn_obj *column;
+ uint n_columns = table_share->fields;
+ char create_info_buff[2048], *create_info;
+ String create_info_str(create_info_buff, sizeof(create_info_buff),
+ system_charset_info);
+ MRN_DBUG_ENTER_METHOD();
+ create_info_str.length(0);
+ for (i = 0; i < n_columns; ++i) {
+ Field *field = table_share->field[i];
+ const char *column_name = field->field_name;
+ uint column_name_size = strlen(column_name);
+
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ continue;
+ }
+
+ column = grn_obj_column(ctx, grn_table,
+ column_name, column_name_size);
+ if (!column) {
+ continue;
+ }
+ grn_id ref_table_id = grn_obj_get_range(ctx, column);
+ grn_obj *ref_table = grn_ctx_at(ctx, ref_table_id);
+ if (ref_table->header.type != GRN_TABLE_NO_KEY &&
+ ref_table->header.type != GRN_TABLE_HASH_KEY &&
+ ref_table->header.type != GRN_TABLE_PAT_KEY &&
+ ref_table->header.type != GRN_TABLE_DAT_KEY) {
+ continue;
+ }
+ char ref_table_buff[NAME_LEN + 1];
+ int ref_table_name_length = grn_obj_name(ctx, ref_table, ref_table_buff,
+ NAME_LEN);
+ ref_table_buff[ref_table_name_length] = '\0';
+
+ if (create_info_str.reserve(15)) {
+ DBUG_RETURN(NULL);
+ }
+ create_info_str.q_append(",\n CONSTRAINT ", 15);
+ append_identifier(ha_thd(), &create_info_str, column_name,
+ column_name_size);
+ if (create_info_str.reserve(14)) {
+ DBUG_RETURN(NULL);
+ }
+ create_info_str.q_append(" FOREIGN KEY (", 14);
+ append_identifier(ha_thd(), &create_info_str, column_name,
+ column_name_size);
+ if (create_info_str.reserve(13)) {
+ DBUG_RETURN(NULL);
+ }
+ create_info_str.q_append(") REFERENCES ", 13);
+ append_identifier(ha_thd(), &create_info_str, table_share->db.str,
+ table_share->db.length);
+ if (create_info_str.reserve(1)) {
+ DBUG_RETURN(NULL);
+ }
+ create_info_str.q_append(".", 1);
+ append_identifier(ha_thd(), &create_info_str, ref_table_buff,
+ ref_table_name_length);
+ if (create_info_str.reserve(2)) {
+ DBUG_RETURN(NULL);
+ }
+ create_info_str.q_append(" (", 2);
+
+ char ref_path[FN_REFLEN + 1];
+ TABLE_LIST table_list;
+ TABLE_SHARE *tmp_ref_table_share;
+ build_table_filename(ref_path, sizeof(ref_path) - 1,
+ table_share->db.str, ref_table_buff, "", 0);
+ DBUG_PRINT("info", ("mroonga: ref_path=%s", ref_path));
+#ifdef MRN_TABLE_LIST_INIT_REQUIRE_ALIAS
+ table_list.init_one_table(table_share->db.str,
+ table_share->db.length,
+ ref_table_buff,
+ ref_table_name_length,
+ ref_table_buff, TL_WRITE);
+#else
+ table_list.init_one_table(table_share->db.str,
+ ref_table_buff,
+ TL_WRITE);
+#endif
+ mrn_open_mutex_lock(table_share);
+ tmp_ref_table_share =
+ mrn_create_tmp_table_share(&table_list, ref_path, &error);
+ mrn_open_mutex_unlock(table_share);
+ if (!tmp_ref_table_share) {
+ DBUG_RETURN(NULL);
+ }
+ uint ref_pkey_nr = tmp_ref_table_share->primary_key;
+ KEY *ref_key_info = &tmp_ref_table_share->key_info[ref_pkey_nr];
+ Field *ref_field = &ref_key_info->key_part->field[0];
+ append_identifier(ha_thd(), &create_info_str, ref_field->field_name,
+ strlen(ref_field->field_name));
+ mrn_open_mutex_lock(table_share);
+ mrn_free_tmp_table_share(tmp_ref_table_share);
+ mrn_open_mutex_unlock(table_share);
+ if (create_info_str.reserve(39)) {
+ DBUG_RETURN(NULL);
+ }
+ create_info_str.q_append(") ON DELETE RESTRICT ON UPDATE RESTRICT", 39);
+ }
+ if (!(create_info = (char *) my_malloc(create_info_str.length() + 1,
+ MYF(MY_WME)))) {
+ DBUG_RETURN(NULL);
+ }
+ memcpy(create_info, create_info_str.ptr(), create_info_str.length());
+ create_info[create_info_str.length()] = '\0';
+ DBUG_RETURN(create_info);
+}
+#else
+char *ha_mroonga::storage_get_foreign_key_create_info()
+{
+ MRN_DBUG_ENTER_METHOD();
+ char *res = handler::get_foreign_key_create_info();
+ DBUG_RETURN(res);
+}
+#endif
+
+char *ha_mroonga::get_foreign_key_create_info()
+{
+ MRN_DBUG_ENTER_METHOD();
+ char *res;
+ if (share->wrapper_mode)
+ {
+ res = wrapper_get_foreign_key_create_info();
+ } else {
+ res = storage_get_foreign_key_create_info();
+ }
+ DBUG_RETURN(res);
+}
+
+#ifdef MRN_HANDLER_HAVE_GET_TABLESPACE_NAME
+char *ha_mroonga::wrapper_get_tablespace_name(THD *thd, char *name,
+ uint name_len)
+{
+ MRN_DBUG_ENTER_METHOD();
+ char *res;
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->get_tablespace_name(thd, name, name_len);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+char *ha_mroonga::storage_get_tablespace_name(THD *thd, char *name,
+ uint name_len)
+{
+ MRN_DBUG_ENTER_METHOD();
+ char *res = handler::get_tablespace_name(thd, name, name_len);
+ DBUG_RETURN(res);
+}
+
+char *ha_mroonga::get_tablespace_name(THD *thd, char *name, uint name_len)
+{
+ MRN_DBUG_ENTER_METHOD();
+ char *res;
+ if (share->wrapper_mode)
+ {
+ res = wrapper_get_tablespace_name(thd, name, name_len);
+ } else {
+ res = storage_get_tablespace_name(thd, name, name_len);
+ }
+ DBUG_RETURN(res);
+}
+#endif
+
+bool ha_mroonga::wrapper_can_switch_engines()
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool res;
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->can_switch_engines();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+bool ha_mroonga::storage_can_switch_engines()
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool res = handler::can_switch_engines();
+ DBUG_RETURN(res);
+}
+
+bool ha_mroonga::can_switch_engines()
+{
+ MRN_DBUG_ENTER_METHOD();
+ bool res;
+ if (share->wrapper_mode)
+ {
+ res = wrapper_can_switch_engines();
+ } else {
+ res = storage_can_switch_engines();
+ }
+ DBUG_RETURN(res);
+}
+
+int ha_mroonga::wrapper_get_foreign_key_list(THD *thd,
+ List<FOREIGN_KEY_INFO> *f_key_list)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int res;
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->get_foreign_key_list(thd, f_key_list);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+#ifdef MRN_SUPPORT_FOREIGN_KEYS
+int ha_mroonga::storage_get_foreign_key_list(THD *thd,
+ List<FOREIGN_KEY_INFO> *f_key_list)
+{
+ int error;
+ uint i;
+ grn_obj *column;
+ uint n_columns = table_share->fields;
+ MRN_DBUG_ENTER_METHOD();
+ for (i = 0; i < n_columns; ++i) {
+ Field *field = table_share->field[i];
+ const char *column_name = field->field_name;
+ uint column_name_size = strlen(column_name);
+
+ if (strcmp(MRN_COLUMN_NAME_ID, column_name) == 0) {
+ continue;
+ }
+
+ column = grn_obj_column(ctx, grn_table,
+ column_name, column_name_size);
+ if (!column) {
+ continue;
+ }
+ grn_id ref_table_id = grn_obj_get_range(ctx, column);
+ grn_obj *ref_table = grn_ctx_at(ctx, ref_table_id);
+ if (ref_table->header.type != GRN_TABLE_NO_KEY &&
+ ref_table->header.type != GRN_TABLE_HASH_KEY &&
+ ref_table->header.type != GRN_TABLE_PAT_KEY &&
+ ref_table->header.type != GRN_TABLE_DAT_KEY) {
+ continue;
+ }
+ FOREIGN_KEY_INFO f_key_info;
+ f_key_info.foreign_id = thd_make_lex_string(thd, NULL, column_name,
+ column_name_size, TRUE);
+ f_key_info.foreign_db = thd_make_lex_string(thd, NULL,
+ table_share->db.str,
+ table_share->db.length,
+ TRUE);
+ f_key_info.foreign_table = thd_make_lex_string(thd, NULL,
+ table_share->table_name.str,
+ table_share->table_name.length,
+ TRUE);
+ f_key_info.referenced_db = f_key_info.foreign_db;
+
+ char ref_table_buff[NAME_LEN + 1];
+ int ref_table_name_length = grn_obj_name(ctx, ref_table, ref_table_buff,
+ NAME_LEN);
+ ref_table_buff[ref_table_name_length] = '\0';
+ DBUG_PRINT("info", ("mroonga: ref_table_buff=%s", ref_table_buff));
+ DBUG_PRINT("info", ("mroonga: ref_table_name_length=%d", ref_table_name_length));
+ f_key_info.referenced_table = thd_make_lex_string(thd, NULL,
+ ref_table_buff,
+ ref_table_name_length,
+ TRUE);
+ f_key_info.update_method = thd_make_lex_string(thd, NULL, "RESTRICT",
+ 8, TRUE);
+ f_key_info.delete_method = thd_make_lex_string(thd, NULL, "RESTRICT",
+ 8, TRUE);
+ f_key_info.referenced_key_name = thd_make_lex_string(thd, NULL, "PRIMARY",
+ 7, TRUE);
+ LEX_STRING *field_name = thd_make_lex_string(thd, NULL, column_name,
+ column_name_size, TRUE);
+ f_key_info.foreign_fields.push_back(field_name);
+
+ char ref_path[FN_REFLEN + 1];
+ TABLE_LIST table_list;
+ TABLE_SHARE *tmp_ref_table_share;
+ build_table_filename(ref_path, sizeof(ref_path) - 1,
+ table_share->db.str, ref_table_buff, "", 0);
+ DBUG_PRINT("info", ("mroonga: ref_path=%s", ref_path));
+#ifdef MRN_TABLE_LIST_INIT_REQUIRE_ALIAS
+ table_list.init_one_table(table_share->db.str,
+ table_share->db.length,
+ ref_table_buff,
+ ref_table_name_length,
+ ref_table_buff, TL_WRITE);
+#else
+ table_list.init_one_table(table_share->db.str,
+ ref_table_buff,
+ TL_WRITE);
+#endif
+ mrn_open_mutex_lock(table_share);
+ tmp_ref_table_share =
+ mrn_create_tmp_table_share(&table_list, ref_path, &error);
+ mrn_open_mutex_unlock(table_share);
+ if (!tmp_ref_table_share) {
+ DBUG_RETURN(error);
+ }
+ uint ref_pkey_nr = tmp_ref_table_share->primary_key;
+ KEY *ref_key_info = &tmp_ref_table_share->key_info[ref_pkey_nr];
+ Field *ref_field = &ref_key_info->key_part->field[0];
+ LEX_STRING *ref_col_name = thd_make_lex_string(thd, NULL,
+ ref_field->field_name,
+ strlen(ref_field->field_name),
+ TRUE);
+ f_key_info.referenced_fields.push_back(ref_col_name);
+ mrn_open_mutex_lock(table_share);
+ mrn_free_tmp_table_share(tmp_ref_table_share);
+ mrn_open_mutex_unlock(table_share);
+ FOREIGN_KEY_INFO *p_f_key_info =
+ (FOREIGN_KEY_INFO *) thd_memdup(thd, &f_key_info,
+ sizeof(FOREIGN_KEY_INFO));
+ if (!p_f_key_info) {
+ DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+ }
+ f_key_list->push_back(p_f_key_info);
+ }
+ DBUG_RETURN(0);
+}
+#else
+int ha_mroonga::storage_get_foreign_key_list(THD *thd,
+ List<FOREIGN_KEY_INFO> *f_key_list)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int res = handler::get_foreign_key_list(thd, f_key_list);
+ DBUG_RETURN(res);
+}
+#endif
+
+int ha_mroonga::get_foreign_key_list(THD *thd,
+ List<FOREIGN_KEY_INFO> *f_key_list)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int res;
+ if (share->wrapper_mode)
+ {
+ res = wrapper_get_foreign_key_list(thd, f_key_list);
+ } else {
+ res = storage_get_foreign_key_list(thd, f_key_list);
+ }
+ DBUG_RETURN(res);
+}
+
+#ifdef MRN_HANDLER_HAVE_GET_PARENT_FOREIGN_KEY_LIST
+int ha_mroonga::wrapper_get_parent_foreign_key_list(THD *thd,
+ List<FOREIGN_KEY_INFO> *f_key_list)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int res;
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->get_parent_foreign_key_list(thd, f_key_list);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+int ha_mroonga::storage_get_parent_foreign_key_list(THD *thd,
+ List<FOREIGN_KEY_INFO> *f_key_list)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int res = handler::get_parent_foreign_key_list(thd, f_key_list);
+ DBUG_RETURN(res);
+}
+
+int ha_mroonga::get_parent_foreign_key_list(THD *thd,
+ List<FOREIGN_KEY_INFO> *f_key_list)
+{
+ MRN_DBUG_ENTER_METHOD();
+ int res;
+ if (share->wrapper_mode)
+ {
+ res = wrapper_get_parent_foreign_key_list(thd, f_key_list);
+ } else {
+ res = storage_get_parent_foreign_key_list(thd, f_key_list);
+ }
+ DBUG_RETURN(res);
+}
+#endif
+
+uint ha_mroonga::wrapper_referenced_by_foreign_key()
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint res;
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->referenced_by_foreign_key();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+uint ha_mroonga::storage_referenced_by_foreign_key()
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint res = handler::referenced_by_foreign_key();
+ DBUG_RETURN(res);
+}
+
+uint ha_mroonga::referenced_by_foreign_key()
+{
+ MRN_DBUG_ENTER_METHOD();
+ uint res;
+ if (share->wrapper_mode)
+ {
+ res = wrapper_referenced_by_foreign_key();
+ } else {
+ res = storage_referenced_by_foreign_key();
+ }
+ DBUG_RETURN(res);
+}
+
+void ha_mroonga::wrapper_init_table_handle_for_HANDLER()
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->init_table_handle_for_HANDLER();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_init_table_handle_for_HANDLER()
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler::init_table_handle_for_HANDLER();
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::init_table_handle_for_HANDLER()
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ {
+ wrapper_init_table_handle_for_HANDLER();
+ } else {
+ storage_init_table_handle_for_HANDLER();
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::wrapper_free_foreign_key_create_info(char* str)
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->free_foreign_key_create_info(str);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+#ifdef MRN_SUPPORT_FOREIGN_KEYS
+void ha_mroonga::storage_free_foreign_key_create_info(char* str)
+{
+ MRN_DBUG_ENTER_METHOD();
+ my_free(str, MYF(0));
+ DBUG_VOID_RETURN;
+}
+#else
+void ha_mroonga::storage_free_foreign_key_create_info(char* str)
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler::free_foreign_key_create_info(str);
+ DBUG_VOID_RETURN;
+}
+#endif
+
+void ha_mroonga::free_foreign_key_create_info(char* str)
+{
+ MRN_DBUG_ENTER_METHOD();
+ if (share->wrapper_mode)
+ {
+ wrapper_free_foreign_key_create_info(str);
+ } else {
+ storage_free_foreign_key_create_info(str);
+ }
+ DBUG_VOID_RETURN;
+}
+
+bool ha_mroonga::check_written_by_row_based_binlog()
+{
+ MRN_DBUG_ENTER_METHOD();
+ THD *thd = ha_thd();
+
+ int current_stmt_binlog_row;
+#ifdef MRN_ROW_BASED_CHECK_IS_METHOD
+ current_stmt_binlog_row = thd->is_current_stmt_binlog_format_row();
+#else
+ current_stmt_binlog_row = thd->current_stmt_binlog_row_based;
+#endif
+ if (!current_stmt_binlog_row) {
+ DBUG_RETURN(false);
+ }
+
+ if (table->s->tmp_table != NO_TMP_TABLE) {
+ DBUG_RETURN(false);
+ }
+
+ if (!mrn_binlog_filter->db_ok(table->s->db.str)) {
+ DBUG_RETURN(false);
+ }
+
+ if (!thd_test_options(thd, OPTION_BIN_LOG)) {
+ DBUG_RETURN(false);
+ }
+
+ if (!mysql_bin_log.is_open()) {
+ DBUG_RETURN(false);
+ }
+
+ DBUG_RETURN(true);
+}
+
+#ifdef MRN_HAVE_HA_REBIND_PSI
+void ha_mroonga::wrapper_unbind_psi()
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->unbind_psi();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_unbind_psi()
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::unbind_psi()
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler::unbind_psi();
+ if (share->wrapper_mode)
+ {
+ wrapper_unbind_psi();
+ } else {
+ storage_unbind_psi();
+ }
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::wrapper_rebind_psi()
+{
+ MRN_DBUG_ENTER_METHOD();
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ wrap_handler->rebind_psi();
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::storage_rebind_psi()
+{
+ MRN_DBUG_ENTER_METHOD();
+ DBUG_VOID_RETURN;
+}
+
+void ha_mroonga::rebind_psi()
+{
+ MRN_DBUG_ENTER_METHOD();
+ handler::rebind_psi();
+ if (share->wrapper_mode)
+ {
+ wrapper_rebind_psi();
+ } else {
+ storage_rebind_psi();
+ }
+ DBUG_VOID_RETURN;
+}
+#endif
+
+my_bool ha_mroonga::wrapper_register_query_cache_table(THD *thd,
+ char *table_key,
+ uint key_length,
+ qc_engine_callback
+ *engine_callback,
+ ulonglong *engine_data)
+{
+ MRN_DBUG_ENTER_METHOD();
+ my_bool res;
+ MRN_SET_WRAP_SHARE_KEY(share, table->s);
+ MRN_SET_WRAP_TABLE_KEY(this, table);
+ res = wrap_handler->register_query_cache_table(thd,
+ table_key,
+ key_length,
+ engine_callback,
+ engine_data);
+ MRN_SET_BASE_SHARE_KEY(share, table->s);
+ MRN_SET_BASE_TABLE_KEY(this, table);
+ DBUG_RETURN(res);
+}
+
+my_bool ha_mroonga::storage_register_query_cache_table(THD *thd,
+ char *table_key,
+ uint key_length,
+ qc_engine_callback
+ *engine_callback,
+ ulonglong *engine_data)
+{
+ MRN_DBUG_ENTER_METHOD();
+ my_bool res = handler::register_query_cache_table(thd,
+ table_key,
+ key_length,
+ engine_callback,
+ engine_data);
+ DBUG_RETURN(res);
+}
+
+my_bool ha_mroonga::register_query_cache_table(THD *thd,
+ char *table_key,
+ uint key_length,
+ qc_engine_callback
+ *engine_callback,
+ ulonglong *engine_data)
+{
+ MRN_DBUG_ENTER_METHOD();
+ my_bool res;
+ if (share->wrapper_mode)
+ {
+ res = wrapper_register_query_cache_table(thd,
+ table_key,
+ key_length,
+ engine_callback,
+ engine_data);
+ } else {
+ res = storage_register_query_cache_table(thd,
+ table_key,
+ key_length,
+ engine_callback,
+ engine_data);
+ }
+ DBUG_RETURN(res);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/storage/mroonga/ha_mroonga.def b/storage/mroonga/ha_mroonga.def
new file mode 100644
index 00000000000..5770cde72e7
--- /dev/null
+++ b/storage/mroonga/ha_mroonga.def
@@ -0,0 +1,15 @@
+LIBRARY ha_mroonga
+VERSION 1.0
+EXPORTS
+ last_insert_grn_id
+ last_insert_grn_id_init
+ last_insert_grn_id_deinit
+ mroonga_snippet
+ mroonga_snippet_init
+ mroonga_snippet_deinit
+ mroonga_command
+ mroonga_command_init
+ mroonga_command_deinit
+ mroonga_escape
+ mroonga_escape_init
+ mroonga_escape_deinit
diff --git a/storage/mroonga/ha_mroonga.hpp b/storage/mroonga/ha_mroonga.hpp
new file mode 100644
index 00000000000..0aec57c87ae
--- /dev/null
+++ b/storage/mroonga/ha_mroonga.hpp
@@ -0,0 +1,1190 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ Copyright(C) 2010 Tetsuro IKEDA
+ Copyright(C) 2010-2013 Kentoku SHIBA
+ Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef HA_MROONGA_HPP_
+#define HA_MROONGA_HPP_
+
+#ifdef USE_PRAGMA_INTERFACE
+#pragma interface
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <groonga.h>
+#include "mrn_sys.hpp"
+#include "mrn_mysql_compat.h"
+
+#if (MYSQL_VERSION_ID >= 50603) || \
+ (MYSQL_VERSION_ID >= 50513 && MYSQL_VERSION_ID < 50600) || \
+ (MYSQL_VERSION_ID >= 50158 && MYSQL_VERSION_ID < 50500)
+# define MRN_HANDLER_CLONE_NEED_NAME 1
+#endif
+
+#if (MYSQL_VERSION_ID >= 50514 && MYSQL_VERSION_ID < 50600)
+# define MRN_HANDLER_HAVE_FINAL_ADD_INDEX 1
+#endif
+
+#if (MYSQL_VERSION_ID >= 50603) || \
+ (defined(MRN_MARIADB_P) && MYSQL_VERSION_ID >= 50209)
+# define MRN_HANDLER_HAVE_HA_RND_NEXT 1
+# define MRN_HANDLER_HAVE_HA_RND_POS 1
+# define MRN_HANDLER_HAVE_HA_INDEX_READ_MAP 1
+# define MRN_HANDLER_HAVE_HA_INDEX_READ_IDX_MAP 1
+# define MRN_HANDLER_HAVE_HA_INDEX_NEXT 1
+# define MRN_HANDLER_HAVE_HA_INDEX_PREV 1
+# define MRN_HANDLER_HAVE_HA_INDEX_FIRST 1
+# define MRN_HANDLER_HAVE_HA_INDEX_LAST 1
+# define MRN_HANDLER_HAVE_HA_INDEX_NEXT_SAME 1
+#endif
+
+#if (MYSQL_VERSION_ID >= 50604) || \
+ (defined(MRN_MARIADB_P) && MYSQL_VERSION_ID >= 50302)
+# define MRN_HANDLER_HAVE_HA_CLOSE 1
+# define MRN_HANDLER_HAVE_MULTI_RANGE_READ 1
+#endif
+
+#if (MYSQL_VERSION_ID >= 50607)
+# define MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER 1
+# define MRN_HANDLER_HAVE_HA_PREPARE_INPLACE_ALTER_TABLE 1
+# define MRN_HANDLER_HAVE_HA_INPLACE_ALTER_TABLE 1
+# define MRN_HANDLER_HAVE_HA_COMMIT_INPLACE_ALTER_TABLE 1
+# define MRN_SUPPORT_FOREIGN_KEYS 1
+#endif
+
+#ifndef MRN_MARIADB_P
+# define MRN_HANDLER_HAVE_INDEX_READ_LAST_MAP
+# if MYSQL_VERSION_ID >= 50611
+# define MRN_HANDLER_HAVE_HA_INDEX_READ_LAST_MAP
+# endif
+#endif
+
+#if (defined(MRN_MARIADB_P) && MYSQL_VERSION_ID >= 50302)
+# define MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS
+#endif
+
+#if MYSQL_VERSION_ID >= 50500
+# define MRN_HANDLER_HAVE_TRUNCATE
+# define MRN_HANDLER_HAVE_GET_PARENT_FOREIGN_KEY_LIST
+#endif
+
+#if MYSQL_VERSION_ID < 50600
+# define MRN_HANDLER_HAVE_GET_TABLESPACE_NAME
+#endif
+
+#if MYSQL_VERSION_ID >= 50607
+# define MRN_HANDLER_HAVE_SET_HA_SHARE_REF
+#endif
+
+#if MYSQL_VERSION_ID >= 50500
+# define MRN_TABLE_LIST_INIT_REQUIRE_ALIAS
+#endif
+
+#ifdef BIG_TABLES
+# define MRN_HA_ROWS_FORMAT "llu"
+#else
+# define MRN_HA_ROWS_FORMAT "lu"
+#endif
+
+#if (MYSQL_VERSION_ID < 50519) || \
+ defined(MRN_MARIADB_P) || \
+ (50600 <= MYSQL_VERSION_ID && MYSQL_VERSION_ID < 50604)
+# define MRN_NEED_FREE_STRING_MEMALLOC_PLUGIN_VAR
+#endif
+
+#if MYSQL_VERSION_ID >= 50500
+# define MRN_HAVE_HA_EXTRA_ADD_CHILDREN_LIST
+# define MRN_HAVE_HA_EXTRA_IS_ATTACHED_CHILDREN
+#endif
+
+#ifdef MRN_MARIADB_P
+# define MRN_HAVE_HA_EXTRA_DETACH_CHILD
+# define MRN_HAVE_HA_EXTRA_PREPARE_FOR_FORCED_CLOSE
+#endif
+
+#if MYSQL_VERSION_ID >= 50607 && \
+ (!defined(MRN_MARIADB_P) || MYSQL_VERSION_ID < 100008)
+# define MRN_HAVE_HA_EXTRA_EXPORT
+#endif
+
+#if MYSQL_VERSION_ID >= 50617 && !defined(MRN_MARIADB_P)
+# define MRN_HAVE_HA_EXTRA_SECONDARY_SORT_ROWID
+#endif
+
+#if MYSQL_VERSION_ID >= 50604 && !defined(MRN_MARIADB_P)
+# define MRN_TIMESTAMP_USE_TIMEVAL
+#elif defined(MRN_MARIADB_P)
+# define MRN_TIMESTAMP_USE_MY_TIME_T
+#else
+# define MRN_TIMESTAMP_USE_LONG
+#endif
+
+#if MYSQL_VERSION_ID < 50600 && !defined(MRN_MARIADB_P)
+# define MRN_FIELD_STORE_TIME_NEED_TYPE
+#endif
+
+#if MYSQL_VERSION_ID < 50500
+# define MRN_HAVE_TL_WRITE_ALLOW_READ
+#endif
+
+#if (defined(MRN_MARIADB_P) && \
+ ((MYSQL_VERSION_ID >= 50306 && MYSQL_VERSION_ID < 50500) || \
+ MYSQL_VERSION_ID >= 50523))
+# define MRN_HANDLER_AUTO_REPAIR_HAVE_ERROR
+#endif
+
+#if MYSQL_VERSION_ID >= 50604
+# define MRN_JOIN_TAB_HAVE_CONDITION
+#endif
+
+#if MYSQL_VERSION_ID < 50600
+# define MRN_RBR_UPDATE_NEED_ALL_COLUMNS
+#endif
+
+#if MYSQL_VERSION_ID >= 50500
+# define MRN_ROW_BASED_CHECK_IS_METHOD
+#endif
+
+#if MYSQL_VERSION_ID >= 50600
+# define MRN_HAVE_HA_REBIND_PSI
+#endif
+
+#if MYSQL_VERSION_ID >= 50612 && !defined(MRN_MARIADB_P)
+# define MRN_HAVE_POINT_XY
+#endif
+
+#if (defined(MRN_MARIADB_P) && MYSQL_VERSION_ID >= 100000)
+# define MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS
+#endif
+
+#if (defined(MRN_MARIADB_P) && MYSQL_VERSION_ID >= 100010)
+# define MRN_HAVE_TDC_LOCK_TABLE_SHARE
+#endif
+
+class ha_mroonga;
+
+/* structs */
+struct st_mrn_ft_info
+{
+ struct _ft_vft *please;
+#ifdef HA_CAN_FULLTEXT_EXT
+ struct _ft_vft_ext *could_you;
+#endif
+ grn_ctx *ctx;
+ grn_encoding encoding;
+ grn_obj *table;
+ grn_obj *result;
+ grn_obj *score_column;
+ grn_obj key;
+ grn_obj score;
+ uint active_index;
+ KEY *key_info;
+ KEY *primary_key_info;
+ grn_obj *cursor;
+ grn_obj *id_accessor;
+ grn_obj *key_accessor;
+ ha_mroonga *mroonga;
+};
+
+/* handler class */
+class ha_mroonga: public handler
+{
+public:
+ handler *wrap_handler;
+ bool is_clone;
+ ha_mroonga *parent_for_clone;
+ MEM_ROOT *mem_root_for_clone;
+ grn_obj key_buffer;
+ grn_id record_id;
+ grn_id *key_id;
+ grn_id *del_key_id;
+ MY_BITMAP multiple_column_key_bitmap;
+
+private:
+ THR_LOCK_DATA thr_lock_data;
+
+ // for wrapper mode (TODO: need to be confirmed)
+ uint wrap_ft_init_count;
+ MRN_SHARE *share;
+ KEY *wrap_key_info;
+ KEY *base_key_info;
+ key_part_map pk_keypart_map;
+ MEM_ROOT mem_root;
+ /// for create table and alter table
+ mutable bool analyzed_for_create;
+ mutable TABLE table_for_create;
+ mutable MRN_SHARE share_for_create;
+ mutable TABLE_SHARE table_share_for_create;
+ mutable MEM_ROOT mem_root_for_create;
+ mutable handler *wrap_handler_for_create;
+#ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX
+ handler_add_index *hnd_add_index;
+#endif
+#ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER
+ Alter_inplace_info::HA_ALTER_FLAGS alter_handler_flags;
+ KEY *alter_key_info_buffer;
+ uint alter_key_count;
+ uint alter_index_drop_count;
+ KEY *alter_index_drop_buffer;
+ uint alter_index_add_count;
+ uint *alter_index_add_buffer;
+ TABLE *wrap_altered_table;
+ KEY *wrap_altered_table_key_info;
+ TABLE_SHARE *wrap_altered_table_share;
+ KEY *wrap_altered_table_share_key_info;
+#else
+ KEY *wrap_alter_key_info;
+#endif
+ int mrn_lock_type;
+
+ // for groonga objects
+ grn_ctx ctx_entity_;
+ grn_ctx *ctx;
+ grn_obj *grn_table;
+ grn_obj **grn_columns;
+ grn_obj **grn_column_ranges;
+ grn_obj **grn_index_tables;
+ grn_obj **grn_index_columns;
+ bool grn_table_is_referenced;
+
+ // buffers
+ grn_obj encoded_key_buffer;
+ grn_obj old_value_buffer;
+ grn_obj new_value_buffer;
+ grn_obj top_left_point;
+ grn_obj bottom_right_point;
+ grn_obj source_point;
+ double top_left_longitude_in_degree;
+ double bottom_right_longitude_in_degree;
+ double bottom_right_latitude_in_degree;
+ double top_left_latitude_in_degree;
+
+ // for search
+ grn_obj *grn_source_column_geo;
+ grn_obj *cursor_geo;
+ grn_table_cursor *cursor;
+ grn_table_cursor *index_table_cursor;
+ grn_obj *empty_value_records;
+ grn_table_cursor *empty_value_records_cursor;
+ grn_obj *sorted_result;
+ grn_obj *matched_record_keys;
+ String *blob_buffers;
+
+ // for error report
+ uint dup_key;
+
+ // for optimization
+ bool count_skip;
+ bool fast_order_limit;
+ bool fast_order_limit_with_index;
+
+ // for context
+ bool ignoring_duplicated_key;
+ bool inserting_with_update;
+ bool fulltext_searching;
+ bool ignoring_no_key_columns;
+ bool replacing_;
+ uint written_by_row_based_binlog;
+
+ // for ft in where clause test
+ Item_func_match *current_ft_item;
+
+public:
+ ha_mroonga(handlerton *hton, TABLE_SHARE *share_arg);
+ ~ha_mroonga();
+ const char *table_type() const; // required
+ const char *index_type(uint inx);
+ const char **bas_ext() const; // required
+
+ ulonglong table_flags() const; // required
+ ulong index_flags(uint idx, uint part, bool all_parts) const; // required
+
+ int create(const char *name, TABLE *form, HA_CREATE_INFO *info); // required
+ int open(const char *name, int mode, uint test_if_locked); // required
+#ifndef MRN_HANDLER_HAVE_HA_CLOSE
+ int close(); // required
+#endif
+ int info(uint flag); // required
+
+ uint lock_count() const;
+ THR_LOCK_DATA **store_lock(THD *thd, // required
+ THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type);
+ int external_lock(THD *thd, int lock_type);
+
+ int rnd_init(bool scan); // required
+ int rnd_end();
+#ifndef MRN_HANDLER_HAVE_HA_RND_NEXT
+ int rnd_next(uchar *buf); // required
+#endif
+#ifndef MRN_HANDLER_HAVE_HA_RND_POS
+ int rnd_pos(uchar *buf, uchar *pos); // required
+#endif
+ void position(const uchar *record); // required
+ int extra(enum ha_extra_function operation);
+ int extra_opt(enum ha_extra_function operation, ulong cache_size);
+
+ int delete_table(const char *name);
+ int write_row(uchar *buf);
+ int update_row(const uchar *old_data, uchar *new_data);
+ int delete_row(const uchar *buf);
+
+ uint max_supported_record_length() const;
+ uint max_supported_keys() const;
+ uint max_supported_key_parts();
+ uint max_supported_key_length() const;
+ uint max_supported_key_part_length() const;
+
+ ha_rows records_in_range(uint inx, key_range *min_key, key_range *max_key);
+ int index_init(uint idx, bool sorted);
+ int index_end();
+#ifndef MRN_HANDLER_HAVE_HA_INDEX_READ_MAP
+ int index_read_map(uchar * buf, const uchar * key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+#endif
+#ifdef MRN_HANDLER_HAVE_INDEX_READ_LAST_MAP
+ int index_read_last_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map);
+#endif
+#ifndef MRN_HANDLER_HAVE_HA_INDEX_NEXT
+ int index_next(uchar *buf);
+#endif
+#ifndef MRN_HANDLER_HAVE_HA_INDEX_PREV
+ int index_prev(uchar *buf);
+#endif
+#ifndef MRN_HANDLER_HAVE_HA_INDEX_FIRST
+ int index_first(uchar *buf);
+#endif
+#ifndef MRN_HANDLER_HAVE_HA_INDEX_LAST
+ int index_last(uchar *buf);
+#endif
+ int index_next_same(uchar *buf, const uchar *key, uint keylen);
+
+ int read_range_first(const key_range *start_key,
+ const key_range *end_key,
+ bool eq_range, bool sorted);
+ int read_range_next();
+
+ int ft_init();
+ FT_INFO *ft_init_ext(uint flags, uint inx, String *key);
+ int ft_read(uchar *buf);
+
+ const Item *cond_push(const Item *cond);
+ void cond_pop();
+
+ bool get_error_message(int error, String *buf);
+
+ int reset();
+
+#ifdef MRN_HANDLER_CLONE_NEED_NAME
+ handler *clone(const char *name, MEM_ROOT *mem_root);
+#else
+ handler *clone(MEM_ROOT *mem_root);
+#endif
+ uint8 table_cache_type();
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ
+ ha_rows multi_range_read_info_const(uint keyno, RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges, uint *bufsz,
+ uint *flags, Cost_estimate *cost);
+ ha_rows multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS
+ uint key_parts,
+#endif
+ uint *bufsz, uint *flags, Cost_estimate *cost);
+ int multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mode,
+ HANDLER_BUFFER *buf);
+ int multi_range_read_next(range_id_t *range_info);
+#else // MRN_HANDLER_HAVE_MULTI_RANGE_READ
+ int read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
+ KEY_MULTI_RANGE *ranges,
+ uint range_count,
+ bool sorted,
+ HANDLER_BUFFER *buffer);
+ int read_multi_range_next(KEY_MULTI_RANGE **found_range_p);
+#endif // MRN_HANDLER_HAVE_MULTI_RANGE_READ
+#ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS
+ void start_bulk_insert(ha_rows rows, uint flags);
+#else
+ void start_bulk_insert(ha_rows rows);
+#endif
+ int end_bulk_insert();
+ int delete_all_rows();
+#ifdef MRN_HANDLER_HAVE_TRUNCATE
+ int truncate();
+#endif // MRN_HANDLER_HAVE_TRUNCATE
+ double scan_time();
+ double read_time(uint index, uint ranges, ha_rows rows);
+ const key_map *keys_to_use_for_scanning();
+ ha_rows estimate_rows_upper_bound();
+ void update_create_info(HA_CREATE_INFO* create_info);
+ int rename_table(const char *from, const char *to);
+ bool is_crashed() const;
+ bool auto_repair(int error) const;
+ bool auto_repair() const;
+ int disable_indexes(uint mode);
+ int enable_indexes(uint mode);
+ int check(THD* thd, HA_CHECK_OPT* check_opt);
+ int repair(THD* thd, HA_CHECK_OPT* check_opt);
+ bool check_and_repair(THD *thd);
+ int analyze(THD* thd, HA_CHECK_OPT* check_opt);
+ int optimize(THD* thd, HA_CHECK_OPT* check_opt);
+ bool is_fatal_error(int error_num, uint flags);
+ bool check_if_incompatible_data(HA_CREATE_INFO *create_info,
+ uint table_changes);
+#ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER
+ enum_alter_inplace_result
+ check_if_supported_inplace_alter(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+#else
+ uint alter_table_flags(uint flags);
+# ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX
+ int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys,
+ handler_add_index **add);
+ int final_add_index(handler_add_index *add, bool commit);
+# else
+ int add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys);
+# endif
+ int prepare_drop_index(TABLE *table_arg, uint *key_num, uint num_of_keys);
+ int final_drop_index(TABLE *table_arg);
+#endif
+ int update_auto_increment();
+ void set_next_insert_id(ulonglong id);
+ void get_auto_increment(ulonglong offset, ulonglong increment, ulonglong nb_desired_values,
+ ulonglong *first_value, ulonglong *nb_reserved_values);
+ void restore_auto_increment(ulonglong prev_insert_id);
+ void release_auto_increment();
+ int check_for_upgrade(HA_CHECK_OPT *check_opt);
+ int reset_auto_increment(ulonglong value);
+ bool was_semi_consistent_read();
+ void try_semi_consistent_read(bool yes);
+ void unlock_row();
+ int start_stmt(THD *thd, thr_lock_type lock_type);
+
+protected:
+#ifdef MRN_HANDLER_HAVE_HA_RND_NEXT
+ int rnd_next(uchar *buf);
+#endif
+#ifdef MRN_HANDLER_HAVE_HA_RND_POS
+ int rnd_pos(uchar *buf, uchar *pos);
+#endif
+#ifdef MRN_HANDLER_HAVE_HA_INDEX_READ_MAP
+ int index_read_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+#endif
+#ifdef MRN_HANDLER_HAVE_HA_INDEX_NEXT
+ int index_next(uchar *buf);
+#endif
+#ifdef MRN_HANDLER_HAVE_HA_INDEX_PREV
+ int index_prev(uchar *buf);
+#endif
+#ifdef MRN_HANDLER_HAVE_HA_INDEX_FIRST
+ int index_first(uchar *buf);
+#endif
+#ifdef MRN_HANDLER_HAVE_HA_INDEX_LAST
+ int index_last(uchar *buf);
+#endif
+ void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share_arg);
+ bool primary_key_is_clustered();
+ bool is_fk_defined_on_table_or_index(uint index);
+ char *get_foreign_key_create_info();
+#ifdef MRN_HANDLER_HAVE_GET_TABLESPACE_NAME
+ char *get_tablespace_name(THD *thd, char *name, uint name_len);
+#endif
+ bool can_switch_engines();
+ int get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list);
+#ifdef MRN_HANDLER_HAVE_GET_PARENT_FOREIGN_KEY_LIST
+ int get_parent_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list);
+#endif
+ uint referenced_by_foreign_key();
+ void init_table_handle_for_HANDLER();
+ void free_foreign_key_create_info(char* str);
+#ifdef MRN_HAVE_HA_REBIND_PSI
+ void unbind_psi();
+ void rebind_psi();
+#endif
+ my_bool register_query_cache_table(THD *thd,
+ char *table_key,
+ uint key_length,
+ qc_engine_callback *engine_callback,
+ ulonglong *engine_data);
+#ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER
+ bool prepare_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ bool inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ bool commit_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit);
+ void notify_table_changed();
+#endif
+
+private:
+ void mkdir_p(const char *directory);
+ ulonglong file_size(const char *path);
+
+ void push_warning_unsupported_spatial_index_search(enum ha_rkey_function flag);
+ void clear_cursor();
+ void clear_cursor_geo();
+ void clear_empty_value_records();
+ void clear_search_result();
+ void clear_search_result_geo();
+ void clear_indexes();
+ int alter_share_add(const char *path, TABLE_SHARE *table_share);
+ void remove_related_files(const char *base_path);
+ void remove_grn_obj_force(const char *name);
+ int drop_index(MRN_SHARE *target_share, uint key_index);
+ grn_obj *find_tokenizer(const char *name, int name_length);
+ grn_obj *find_normalizer(KEY *key_info);
+ int wrapper_get_record(uchar *buf, const uchar *key);
+ int wrapper_get_next_geo_record(uchar *buf);
+ int storage_get_next_record(uchar *buf);
+ void geo_store_rectangle(const uchar *rectangle);
+ int generic_geo_open_cursor(const uchar *key, enum ha_rkey_function find_flag);
+
+#ifdef MRN_HANDLER_HAVE_HA_CLOSE
+ int close();
+#endif
+ bool is_dry_write();
+ bool is_enable_optimization();
+ bool should_normalize(Field *field) const;
+ bool is_temporary_table_name(const char *name) const;
+ void check_count_skip(key_part_map start_key_part_map,
+ key_part_map end_key_part_map, bool fulltext);
+ bool is_grn_zero_column_value(grn_obj *column, grn_obj *value);
+ void check_fast_order_limit(grn_table_sort_key **sort_keys, int *n_sort_keys,
+ longlong *limit);
+
+ long long int get_grn_time_from_timestamp_field(Field_timestamp *field);
+
+ int generic_store_bulk_fixed_size_string(Field *field, grn_obj *buf);
+ int generic_store_bulk_variable_size_string(Field *field, grn_obj *buf);
+ int generic_store_bulk_integer(Field *field, grn_obj *buf);
+ int generic_store_bulk_unsigned_integer(Field *field, grn_obj *buf);
+ int generic_store_bulk_float(Field *field, grn_obj *buf);
+ int generic_store_bulk_timestamp(Field *field, grn_obj *buf);
+ int generic_store_bulk_date(Field *field, grn_obj *buf);
+ int generic_store_bulk_time(Field *field, grn_obj *buf);
+ int generic_store_bulk_datetime(Field *field, grn_obj *buf);
+ int generic_store_bulk_year(Field *field, grn_obj *buf);
+ int generic_store_bulk_new_date(Field *field, grn_obj *buf);
+#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
+ int generic_store_bulk_datetime2(Field *field, grn_obj *buf);
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
+ int generic_store_bulk_time2(Field *field, grn_obj *buf);
+#endif
+ int generic_store_bulk_new_decimal(Field *field, grn_obj *buf);
+ int generic_store_bulk_blob(Field *field, grn_obj *buf);
+ int generic_store_bulk_geometry(Field *field, grn_obj *buf);
+ int generic_store_bulk(Field *field, grn_obj *buf);
+
+ void storage_store_field_string(Field *field,
+ const char *value, uint value_length);
+ void storage_store_field_integer(Field *field,
+ const char *value, uint value_length);
+ void storage_store_field_unsigned_integer(Field *field,
+ const char *value,
+ uint value_length);
+ void storage_store_field_float(Field *field,
+ const char *value, uint value_length);
+ void storage_store_field_timestamp(Field *field,
+ const char *value, uint value_length);
+ void storage_store_field_date(Field *field,
+ const char *value, uint value_length);
+ void storage_store_field_time(Field *field,
+ const char *value, uint value_length);
+ void storage_store_field_datetime(Field *field,
+ const char *value, uint value_length);
+ void storage_store_field_year(Field *field,
+ const char *value, uint value_length);
+ void storage_store_field_new_date(Field *field,
+ const char *value, uint value_length);
+#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
+ void storage_store_field_datetime2(Field *field,
+ const char *value, uint value_length);
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
+ void storage_store_field_time2(Field *field,
+ const char *value, uint value_length);
+#endif
+ void storage_store_field_blob(Field *field,
+ const char *value, uint value_length);
+ void storage_store_field_geometry(Field *field,
+ const char *value, uint value_length);
+ void storage_store_field(Field *field, const char *value, uint value_length);
+ void storage_store_fields(uchar *buf, grn_id record_id);
+ void storage_store_fields_for_prep_update(const uchar *old_data,
+ uchar *new_data,
+ grn_id record_id);
+ void storage_store_fields_by_index(uchar *buf);
+
+ int storage_encode_key_normalize_min_sort_chars(Field *field,
+ uchar *buf,
+ uint size);
+ int storage_encode_key_fixed_size_string(Field *field, const uchar *key,
+ uchar *buf, uint *size);
+ int storage_encode_key_variable_size_string(Field *field, const uchar *key,
+ uchar *buf, uint *size);
+ int storage_encode_key_timestamp(Field *field, const uchar *key,
+ uchar *buf, uint *size);
+ int storage_encode_key_time(Field *field, const uchar *key,
+ uchar *buf, uint *size);
+ int storage_encode_key_year(Field *field, const uchar *key,
+ uchar *buf, uint *size);
+ int storage_encode_key_datetime(Field *field, const uchar *key,
+ uchar *buf, uint *size);
+#ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2
+ int storage_encode_key_timestamp2(Field *field, const uchar *key,
+ uchar *buf, uint *size);
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
+ int storage_encode_key_datetime2(Field *field, const uchar *key,
+ uchar *buf, uint *size);
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
+ int storage_encode_key_time2(Field *field, const uchar *key,
+ uchar *buf, uint *size);
+#endif
+ int storage_encode_key_enum(Field *field, const uchar *key,
+ uchar *buf, uint *size);
+ int storage_encode_key_set(Field *field, const uchar *key,
+ uchar *buf, uint *size);
+ int storage_encode_key(Field *field, const uchar *key, uchar *buf, uint *size);
+ int storage_encode_multiple_column_key(KEY *key_info,
+ const uchar *key, uint key_length,
+ uchar *buffer, uint *encoded_length);
+ int storage_encode_multiple_column_key_range(KEY *key_info,
+ const key_range *start,
+ const key_range *end,
+ uchar *min_buffer,
+ uint *min_encoded_size,
+ uchar *max_buffer,
+ uint *max_encoded_size);
+
+ void set_pk_bitmap();
+ int create_share_for_create() const;
+ int wrapper_create(const char *name, TABLE *table,
+ HA_CREATE_INFO *info, MRN_SHARE *tmp_share);
+ int storage_create(const char *name, TABLE *table,
+ HA_CREATE_INFO *info, MRN_SHARE *tmp_share);
+ int wrapper_create_index_fulltext_validate(KEY *key_info);
+ int wrapper_create_index_fulltext(const char *grn_table_name,
+ int i,
+ KEY *key_info,
+ grn_obj **index_tables,
+ grn_obj **index_columns,
+ MRN_SHARE *tmp_share);
+ int wrapper_create_index_geo(const char *grn_table_name,
+ int i,
+ KEY *key_info,
+ grn_obj **index_tables,
+ grn_obj **index_columns,
+ MRN_SHARE *tmp_share);
+ int wrapper_create_index(const char *name, TABLE *table,
+ HA_CREATE_INFO *info, MRN_SHARE *tmp_share,
+ const char *grn_table_name);
+ int storage_create_validate_pseudo_column(TABLE *table);
+#ifdef MRN_SUPPORT_FOREIGN_KEYS
+ bool storage_create_foreign_key(TABLE *table, const char *grn_table_name,
+ Field *field, grn_obj *table_obj, int &error);
+#endif
+ int storage_create_validate_index(TABLE *table);
+ int storage_create_index_table(TABLE *table, const char *grn_table_name,
+ grn_obj *grn_table, MRN_SHARE *tmp_share,
+ KEY *key_info, grn_obj **index_tables,
+ uint i);
+ int storage_create_index(TABLE *table, const char *grn_table_name,
+ grn_obj *grn_table, MRN_SHARE *tmp_share,
+ KEY *key_info, grn_obj **index_tables,
+ grn_obj **index_columns, uint i);
+ int storage_create_indexes(TABLE *table, const char *grn_table_name,
+ grn_obj *grn_table, MRN_SHARE *tmp_share);
+ int close_databases();
+ void ensure_database_directory();
+ int ensure_normalizers_register();
+ int ensure_database_create(const char *name);
+ int ensure_database_open(const char *name);
+ int ensure_database_remove(const char *name);
+ int wrapper_delete_table(const char *name, MRN_SHARE *tmp_share,
+ const char *table_name);
+ int wrapper_delete_index(const char *name, MRN_SHARE *tmp_share,
+ const char *table_name);
+ int storage_delete_table(const char *name, MRN_SHARE *tmp_share,
+ const char *table_name);
+ int wrapper_open(const char *name, int mode, uint test_if_locked);
+ int wrapper_open_indexes(const char *name);
+ int storage_open(const char *name, int mode, uint test_if_locked);
+ void update_grn_table_is_referenced();
+ int open_table(const char *name);
+ int storage_open_columns(void);
+ int storage_open_indexes(const char *name);
+ void wrapper_overwrite_index_bits();
+ int wrapper_close();
+ int storage_close();
+ int generic_extra(enum ha_extra_function operation);
+ int wrapper_extra(enum ha_extra_function operation);
+ int storage_extra(enum ha_extra_function operation);
+ int wrapper_extra_opt(enum ha_extra_function operation, ulong cache_size);
+ int storage_extra_opt(enum ha_extra_function operation, ulong cache_size);
+ int generic_reset();
+ int wrapper_reset();
+ int storage_reset();
+ uint wrapper_lock_count() const;
+ uint storage_lock_count() const;
+ THR_LOCK_DATA **wrapper_store_lock(THD *thd, THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type);
+ THR_LOCK_DATA **storage_store_lock(THD *thd, THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type);
+ int wrapper_external_lock(THD *thd, int lock_type);
+ int storage_external_lock(THD *thd, int lock_type);
+#ifdef MRN_HANDLER_START_BULK_INSERT_HAS_FLAGS
+ void wrapper_start_bulk_insert(ha_rows rows, uint flags);
+ void storage_start_bulk_insert(ha_rows rows, uint flags);
+#else
+ void wrapper_start_bulk_insert(ha_rows rows);
+ void storage_start_bulk_insert(ha_rows rows);
+#endif
+ int wrapper_end_bulk_insert();
+ int storage_end_bulk_insert();
+ bool wrapper_is_target_index(KEY *key_info);
+ bool wrapper_have_target_index();
+ int wrapper_write_row(uchar *buf);
+ int wrapper_write_row_index(uchar *buf);
+ int storage_write_row(uchar *buf);
+ int storage_write_row_multiple_column_index(uchar *buf,
+ grn_id record_id,
+ KEY *key_info,
+ grn_obj *index_column);
+ int storage_write_row_multiple_column_indexes(uchar *buf, grn_id record_id);
+ int storage_write_row_unique_index(uchar *buf,
+ KEY *key_info, grn_obj *index_table,
+ grn_id *key_id);
+ int storage_write_row_unique_indexes(uchar *buf);
+ int wrapper_get_record_id(uchar *data, grn_id *record_id, const char *context);
+ int wrapper_update_row(const uchar *old_data, uchar *new_data);
+ int wrapper_update_row_index(const uchar *old_data, uchar *new_data);
+ int storage_update_row(const uchar *old_data, uchar *new_data);
+ int storage_update_row_index(const uchar *old_data, uchar *new_data);
+ int storage_update_row_unique_indexes(uchar *new_data);
+ int wrapper_delete_row(const uchar *buf);
+ int wrapper_delete_row_index(const uchar *buf);
+ int storage_delete_row(const uchar *buf);
+ int storage_delete_row_index(const uchar *buf);
+ int storage_delete_row_unique_index(grn_obj *index_table, grn_id del_key_id);
+ int storage_delete_row_unique_indexes();
+ int storage_prepare_delete_row_unique_index(const uchar *buf,
+ grn_id record_id,
+ KEY *key_info,
+ grn_obj *index_table,
+ grn_obj *index_column,
+ grn_id *del_key_id);
+ int storage_prepare_delete_row_unique_indexes(const uchar *buf,
+ grn_id record_id);
+ uint wrapper_max_supported_record_length() const;
+ uint storage_max_supported_record_length() const;
+ uint wrapper_max_supported_keys() const;
+ uint storage_max_supported_keys() const;
+ uint wrapper_max_supported_key_parts();
+ uint storage_max_supported_key_parts();
+ uint wrapper_max_supported_key_length() const;
+ uint storage_max_supported_key_length() const;
+ uint wrapper_max_supported_key_part_length() const;
+ uint storage_max_supported_key_part_length() const;
+ ulonglong wrapper_table_flags() const;
+ ulonglong storage_table_flags() const;
+ ulong wrapper_index_flags(uint idx, uint part, bool all_parts) const;
+ ulong storage_index_flags(uint idx, uint part, bool all_parts) const;
+ int wrapper_info(uint flag);
+ int storage_info(uint flag);
+ void storage_info_variable();
+ void storage_info_variable_records();
+ void storage_info_variable_data_file_length();
+ int wrapper_rnd_init(bool scan);
+ int storage_rnd_init(bool scan);
+ int wrapper_rnd_end();
+ int storage_rnd_end();
+ int wrapper_rnd_next(uchar *buf);
+ int storage_rnd_next(uchar *buf);
+ int wrapper_rnd_pos(uchar *buf, uchar *pos);
+ int storage_rnd_pos(uchar *buf, uchar *pos);
+ void wrapper_position(const uchar *record);
+ void storage_position(const uchar *record);
+ ha_rows wrapper_records_in_range(uint key_nr, key_range *range_min,
+ key_range *range_max);
+ ha_rows storage_records_in_range(uint key_nr, key_range *range_min,
+ key_range *range_max);
+ ha_rows generic_records_in_range_geo(uint key_nr, key_range *range_min,
+ key_range *range_max);
+ int wrapper_index_init(uint idx, bool sorted);
+ int storage_index_init(uint idx, bool sorted);
+ int wrapper_index_end();
+ int storage_index_end();
+ int wrapper_index_read_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+ int storage_index_read_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag);
+#ifdef MRN_HANDLER_HAVE_INDEX_READ_LAST_MAP
+ int wrapper_index_read_last_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map);
+ int storage_index_read_last_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map);
+#endif
+ int wrapper_index_next(uchar *buf);
+ int storage_index_next(uchar *buf);
+ int wrapper_index_prev(uchar *buf);
+ int storage_index_prev(uchar *buf);
+ int wrapper_index_first(uchar *buf);
+ int storage_index_first(uchar *buf);
+ int wrapper_index_last(uchar *buf);
+ int storage_index_last(uchar *buf);
+ int wrapper_index_next_same(uchar *buf, const uchar *key, uint keylen);
+ int storage_index_next_same(uchar *buf, const uchar *key, uint keylen);
+ int wrapper_read_range_first(const key_range *start_key,
+ const key_range *end_key,
+ bool eq_range, bool sorted);
+ int storage_read_range_first(const key_range *start_key,
+ const key_range *end_key,
+ bool eq_range, bool sorted);
+ int wrapper_read_range_next();
+ int storage_read_range_next();
+ int generic_ft_init();
+ int wrapper_ft_init();
+ int storage_ft_init();
+ FT_INFO *wrapper_ft_init_ext(uint flags, uint key_nr, String *key);
+ FT_INFO *storage_ft_init_ext(uint flags, uint key_nr, String *key);
+ void generic_ft_init_ext_add_conditions_fast_order_limit(
+ struct st_mrn_ft_info *info, grn_obj *expression);
+ bool generic_ft_init_ext_parse_pragma_d(struct st_mrn_ft_info *info,
+ const char *keyword,
+ uint keyword_length,
+ grn_operator *default_operator,
+ uint *consumed_keyword_length);
+ void generic_ft_init_ext_parse_pragma_w_append_section(
+ struct st_mrn_ft_info *info,
+ grn_obj *index_column,
+ grn_obj *match_columns,
+ uint section,
+ grn_obj *section_value_buffer,
+ int weight,
+ uint n_weights);
+ bool generic_ft_init_ext_parse_pragma_w(struct st_mrn_ft_info *info,
+ const char *keyword,
+ uint keyword_length,
+ grn_obj *index_column,
+ grn_obj *match_columns,
+ uint *consumed_keyword_length,
+ grn_obj *tmp_objects);
+ grn_rc generic_ft_init_ext_prepare_expression_in_boolean_mode(
+ struct st_mrn_ft_info *info,
+ String *key,
+ grn_obj *index_column,
+ grn_obj *match_columns,
+ grn_obj *expression,
+ grn_obj *tmp_objects);
+ grn_rc generic_ft_init_ext_prepare_expression_in_normal_mode(
+ struct st_mrn_ft_info *info,
+ String *key,
+ grn_obj *index_column,
+ grn_obj *match_columns,
+ grn_obj *expression,
+ grn_obj *tmp_objects);
+ struct st_mrn_ft_info *generic_ft_init_ext_select(uint flags,
+ uint key_nr,
+ String *key);
+ FT_INFO *generic_ft_init_ext(uint flags, uint key_nr, String *key);
+ int wrapper_ft_read(uchar *buf);
+ int storage_ft_read(uchar *buf);
+ const Item *wrapper_cond_push(const Item *cond);
+ const Item *storage_cond_push(const Item *cond);
+ void wrapper_cond_pop();
+ void storage_cond_pop();
+ bool wrapper_get_error_message(int error, String *buf);
+ bool storage_get_error_message(int error, String *buf);
+#ifdef MRN_HANDLER_CLONE_NEED_NAME
+ handler *wrapper_clone(const char *name, MEM_ROOT *mem_root);
+ handler *storage_clone(const char *name, MEM_ROOT *mem_root);
+#else
+ handler *wrapper_clone(MEM_ROOT *mem_root);
+ handler *storage_clone(MEM_ROOT *mem_root);
+#endif
+ uint8 wrapper_table_cache_type();
+ uint8 storage_table_cache_type();
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ
+ ha_rows wrapper_multi_range_read_info_const(uint keyno,
+ RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges,
+ uint *bufsz,
+ uint *flags,
+ Cost_estimate *cost);
+ ha_rows storage_multi_range_read_info_const(uint keyno,
+ RANGE_SEQ_IF *seq,
+ void *seq_init_param,
+ uint n_ranges,
+ uint *bufsz,
+ uint *flags,
+ Cost_estimate *cost);
+ ha_rows wrapper_multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS
+ uint key_parts,
+#endif
+ uint *bufsz, uint *flags,
+ Cost_estimate *cost);
+ ha_rows storage_multi_range_read_info(uint keyno, uint n_ranges, uint keys,
+#ifdef MRN_HANDLER_HAVE_MULTI_RANGE_READ_INFO_KEY_PARTS
+ uint key_parts,
+#endif
+ uint *bufsz, uint *flags,
+ Cost_estimate *cost);
+ int wrapper_multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mode,
+ HANDLER_BUFFER *buf);
+ int storage_multi_range_read_init(RANGE_SEQ_IF *seq, void *seq_init_param,
+ uint n_ranges, uint mode,
+ HANDLER_BUFFER *buf);
+ int wrapper_multi_range_read_next(range_id_t *range_info);
+ int storage_multi_range_read_next(range_id_t *range_info);
+#else // MRN_HANDLER_HAVE_MULTI_RANGE_READ
+ int wrapper_read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
+ KEY_MULTI_RANGE *ranges,
+ uint range_count,
+ bool sorted,
+ HANDLER_BUFFER *buffer);
+ int storage_read_multi_range_first(KEY_MULTI_RANGE **found_range_p,
+ KEY_MULTI_RANGE *ranges,
+ uint range_count,
+ bool sorted,
+ HANDLER_BUFFER *buffer);
+ int wrapper_read_multi_range_next(KEY_MULTI_RANGE **found_range_p);
+ int storage_read_multi_range_next(KEY_MULTI_RANGE **found_range_p);
+#endif // MRN_HANDLER_HAVE_MULTI_RANGE_READ
+ int generic_delete_all_rows(grn_obj *target_grn_table,
+ const char *function_name);
+ int wrapper_delete_all_rows();
+ int storage_delete_all_rows();
+#ifdef MRN_HANDLER_HAVE_TRUNCATE
+ int wrapper_truncate();
+#endif // MRN_HANDLER_HAVE_TRUNCATE
+ int wrapper_truncate_index();
+ int storage_truncate();
+ int storage_truncate_index();
+ double wrapper_scan_time();
+ double storage_scan_time();
+ double wrapper_read_time(uint index, uint ranges, ha_rows rows);
+ double storage_read_time(uint index, uint ranges, ha_rows rows);
+ const key_map *wrapper_keys_to_use_for_scanning();
+ const key_map *storage_keys_to_use_for_scanning();
+ ha_rows wrapper_estimate_rows_upper_bound();
+ ha_rows storage_estimate_rows_upper_bound();
+ void wrapper_update_create_info(HA_CREATE_INFO* create_info);
+ void storage_update_create_info(HA_CREATE_INFO* create_info);
+ int wrapper_rename_table(const char *from, const char *to,
+ MRN_SHARE *tmp_share,
+ const char *from_table_name,
+ const char *to_table_name);
+ int wrapper_rename_index(const char *from, const char *to,
+ MRN_SHARE *tmp_share,
+ const char *from_table_name,
+ const char *to_table_name);
+ int storage_rename_table(const char *from, const char *to,
+ MRN_SHARE *tmp_share,
+ const char *from_table_name,
+ const char *to_table_name);
+#ifdef MRN_SUPPORT_FOREIGN_KEYS
+ int storage_rename_foreign_key(MRN_SHARE *tmp_share,
+ const char *from_table_name,
+ const char *to_table_name);
+#endif
+ bool wrapper_is_crashed() const;
+ bool storage_is_crashed() const;
+ bool wrapper_auto_repair(int error) const;
+ bool storage_auto_repair(int error) const;
+ int wrapper_disable_indexes(uint mode);
+ int storage_disable_indexes(uint mode);
+ int wrapper_enable_indexes(uint mode);
+ int storage_enable_indexes(uint mode);
+ int wrapper_check(THD* thd, HA_CHECK_OPT* check_opt);
+ int storage_check(THD* thd, HA_CHECK_OPT* check_opt);
+ int wrapper_fill_indexes(THD *thd, KEY *key_info,
+ grn_obj **index_columns, uint n_keys);
+ int wrapper_recreate_indexes(THD *thd);
+ int wrapper_repair(THD* thd, HA_CHECK_OPT* check_opt);
+ int storage_repair(THD* thd, HA_CHECK_OPT* check_opt);
+ bool wrapper_check_and_repair(THD *thd);
+ bool storage_check_and_repair(THD *thd);
+ int wrapper_analyze(THD* thd, HA_CHECK_OPT* check_opt);
+ int storage_analyze(THD* thd, HA_CHECK_OPT* check_opt);
+ int wrapper_optimize(THD* thd, HA_CHECK_OPT* check_opt);
+ int storage_optimize(THD* thd, HA_CHECK_OPT* check_opt);
+ bool wrapper_is_fatal_error(int error_num, uint flags);
+ bool storage_is_fatal_error(int error_num, uint flags);
+ bool wrapper_is_comment_changed(TABLE *table1, TABLE *table2);
+ bool wrapper_check_if_incompatible_data(HA_CREATE_INFO *create_info,
+ uint table_changes);
+ bool storage_check_if_incompatible_data(HA_CREATE_INFO *create_info,
+ uint table_changes);
+ int storage_add_index_multiple_columns(KEY *key_info, uint num_of_keys,
+ grn_obj **index_tables,
+ grn_obj **index_columns,
+ bool skip_unique_key);
+#ifdef MRN_HANDLER_HAVE_CHECK_IF_SUPPORTED_INPLACE_ALTER
+ enum_alter_inplace_result
+ wrapper_check_if_supported_inplace_alter(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ enum_alter_inplace_result
+ storage_check_if_supported_inplace_alter(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ bool wrapper_prepare_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ bool storage_prepare_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ bool wrapper_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ bool storage_inplace_alter_table_index(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ bool storage_inplace_alter_table_add_column(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ bool storage_inplace_alter_table_drop_column(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ bool storage_inplace_alter_table_rename_column(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ bool storage_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info);
+ bool wrapper_commit_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit);
+ bool storage_commit_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit);
+ void wrapper_notify_table_changed();
+ void storage_notify_table_changed();
+#else
+ uint wrapper_alter_table_flags(uint flags);
+ uint storage_alter_table_flags(uint flags);
+# ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX
+ int wrapper_add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys,
+ handler_add_index **add);
+ int storage_add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys,
+ handler_add_index **add);
+# else
+ int wrapper_add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys);
+ int storage_add_index(TABLE *table_arg, KEY *key_info, uint num_of_keys);
+# endif
+# ifdef MRN_HANDLER_HAVE_FINAL_ADD_INDEX
+ int wrapper_final_add_index(handler_add_index *add, bool commit);
+ int storage_final_add_index(handler_add_index *add, bool commit);
+# endif
+ int wrapper_prepare_drop_index(TABLE *table_arg, uint *key_num,
+ uint num_of_keys);
+ int storage_prepare_drop_index(TABLE *table_arg, uint *key_num,
+ uint num_of_keys);
+ int wrapper_final_drop_index(TABLE *table_arg);
+ int storage_final_drop_index(TABLE *table_arg);
+#endif
+ int wrapper_update_auto_increment();
+ int storage_update_auto_increment();
+ void wrapper_set_next_insert_id(ulonglong id);
+ void storage_set_next_insert_id(ulonglong id);
+ void wrapper_get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values);
+ void storage_get_auto_increment(ulonglong offset, ulonglong increment,
+ ulonglong nb_desired_values,
+ ulonglong *first_value,
+ ulonglong *nb_reserved_values);
+ void wrapper_restore_auto_increment(ulonglong prev_insert_id);
+ void storage_restore_auto_increment(ulonglong prev_insert_id);
+ void wrapper_release_auto_increment();
+ void storage_release_auto_increment();
+ int wrapper_check_for_upgrade(HA_CHECK_OPT *check_opt);
+ int storage_check_for_upgrade(HA_CHECK_OPT *check_opt);
+ int wrapper_reset_auto_increment(ulonglong value);
+ int storage_reset_auto_increment(ulonglong value);
+ bool wrapper_was_semi_consistent_read();
+ bool storage_was_semi_consistent_read();
+ void wrapper_try_semi_consistent_read(bool yes);
+ void storage_try_semi_consistent_read(bool yes);
+ void wrapper_unlock_row();
+ void storage_unlock_row();
+ int wrapper_start_stmt(THD *thd, thr_lock_type lock_type);
+ int storage_start_stmt(THD *thd, thr_lock_type lock_type);
+ void wrapper_change_table_ptr(TABLE *table_arg, TABLE_SHARE *share_arg);
+ void storage_change_table_ptr(TABLE *table_arg, TABLE_SHARE *share_arg);
+ bool wrapper_primary_key_is_clustered();
+ bool storage_primary_key_is_clustered();
+ bool wrapper_is_fk_defined_on_table_or_index(uint index);
+ bool storage_is_fk_defined_on_table_or_index(uint index);
+ char *wrapper_get_foreign_key_create_info();
+ char *storage_get_foreign_key_create_info();
+#ifdef MRN_HANDLER_HAVE_GET_TABLESPACE_NAME
+ char *wrapper_get_tablespace_name(THD *thd, char *name, uint name_len);
+ char *storage_get_tablespace_name(THD *thd, char *name, uint name_len);
+#endif
+ bool wrapper_can_switch_engines();
+ bool storage_can_switch_engines();
+ int wrapper_get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list);
+ int storage_get_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list);
+#ifdef MRN_HANDLER_HAVE_GET_PARENT_FOREIGN_KEY_LIST
+ int wrapper_get_parent_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list);
+ int storage_get_parent_foreign_key_list(THD *thd, List<FOREIGN_KEY_INFO> *f_key_list);
+#endif
+ uint wrapper_referenced_by_foreign_key();
+ uint storage_referenced_by_foreign_key();
+ void wrapper_init_table_handle_for_HANDLER();
+ void storage_init_table_handle_for_HANDLER();
+ void wrapper_free_foreign_key_create_info(char* str);
+ void storage_free_foreign_key_create_info(char* str);
+ void wrapper_set_keys_in_use();
+ void storage_set_keys_in_use();
+ bool check_written_by_row_based_binlog();
+#ifdef MRN_HAVE_HA_REBIND_PSI
+ void wrapper_unbind_psi();
+ void storage_unbind_psi();
+ void wrapper_rebind_psi();
+ void storage_rebind_psi();
+#endif
+ my_bool wrapper_register_query_cache_table(THD *thd,
+ char *table_key,
+ uint key_length,
+ qc_engine_callback
+ *engine_callback,
+ ulonglong *engine_data);
+ my_bool storage_register_query_cache_table(THD *thd,
+ char *table_key,
+ uint key_length,
+ qc_engine_callback
+ *engine_callback,
+ ulonglong *engine_data);
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HA_MROONGA_HPP_ */
diff --git a/storage/mroonga/lib/Makefile.am b/storage/mroonga/lib/Makefile.am
new file mode 100644
index 00000000000..300131db70a
--- /dev/null
+++ b/storage/mroonga/lib/Makefile.am
@@ -0,0 +1,23 @@
+AM_CPPFLAGS = \
+ -I$(top_builddir) \
+ -I$(top_srcdir) \
+ $(MYSQL_INCLUDES) \
+ $(GROONGA_CFLAGS) \
+ $(MYSQL_VERSION_CFLAGS)
+
+libmrn_need_mysql_la_CXXFLAGS = $(AM_CXXFLAGS) $(MYSQL_CFLAGS)
+
+if WITH_LIBMYSQLSERVICES_COMPAT
+LIBMYSQLSERVICES_COMPAT = libmysqlservices.la
+endif
+
+noinst_LTLIBRARIES = \
+ $(LIBMYSQLSERVICES_COMPAT) \
+ libmrn_no_mysql.la \
+ libmrn_need_mysql.la
+
+include libmrn_no_mysql_sources.am
+include libmrn_need_mysql_sources.am
+if WITH_LIBMYSQLSERVICES_COMPAT
+include libmysqlservices_compat_sources.am
+endif
diff --git a/storage/mroonga/lib/libmrn_need_mysql_sources.am b/storage/mroonga/lib/libmrn_need_mysql_sources.am
new file mode 100644
index 00000000000..9dca2b6fb04
--- /dev/null
+++ b/storage/mroonga/lib/libmrn_need_mysql_sources.am
@@ -0,0 +1,25 @@
+libmrn_need_mysql_la_SOURCES = \
+ mrn_index_table_name.cpp \
+ mrn_index_table_name.hpp \
+ mrn_index_column_name.cpp \
+ mrn_index_column_name.hpp \
+ mrn_debug_column_access.cpp \
+ mrn_debug_column_access.hpp \
+ mrn_auto_increment_value_lock.cpp \
+ mrn_auto_increment_value_lock.hpp \
+ mrn_external_lock.cpp \
+ mrn_external_lock.hpp \
+ mrn_multiple_column_key_codec.cpp \
+ mrn_multiple_column_key_codec.hpp \
+ mrn_field_normalizer.cpp \
+ mrn_field_normalizer.hpp \
+ mrn_encoding.cpp \
+ mrn_encoding.hpp \
+ mrn_parameters_parser.cpp \
+ mrn_parameters_parser.hpp \
+ mrn_lock.cpp \
+ mrn_lock.hpp \
+ mrn_condition_converter.cpp \
+ mrn_condition_converter.hpp \
+ mrn_time_converter.cpp \
+ mrn_time_converter.hpp
diff --git a/storage/mroonga/lib/libmrn_no_mysql_sources.am b/storage/mroonga/lib/libmrn_no_mysql_sources.am
new file mode 100644
index 00000000000..89cfb277595
--- /dev/null
+++ b/storage/mroonga/lib/libmrn_no_mysql_sources.am
@@ -0,0 +1,8 @@
+libmrn_no_mysql_la_SOURCES = \
+ mrn_match_escalation_threshold_scope.cpp \
+ mrn_match_escalation_threshold_scope.hpp \
+ mrn_path_mapper.cpp \
+ mrn_path_mapper.hpp \
+ mrn_windows.hpp \
+ mrn_smart_grn_obj.cpp \
+ mrn_smart_grn_obj.hpp
diff --git a/storage/mroonga/lib/libmysqlservices_compat_sources.am b/storage/mroonga/lib/libmysqlservices_compat_sources.am
new file mode 100644
index 00000000000..bb0712a4add
--- /dev/null
+++ b/storage/mroonga/lib/libmysqlservices_compat_sources.am
@@ -0,0 +1,2 @@
+libmysqlservices_la_SOURCES = \
+ mrn_mysqlservices.cpp
diff --git a/storage/mroonga/lib/mrn_auto_increment_value_lock.cpp b/storage/mroonga/lib/mrn_auto_increment_value_lock.cpp
new file mode 100644
index 00000000000..3bac5e31c6c
--- /dev/null
+++ b/storage/mroonga/lib/mrn_auto_increment_value_lock.cpp
@@ -0,0 +1,42 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "mrn_auto_increment_value_lock.hpp"
+
+#if MYSQL_VERSION_ID >= 50500
+# define AUTO_INCREMENT_VALUE_MUTEX(table_share) (&(table_share->LOCK_ha_data))
+#else
+# define AUTO_INCREMENT_VALUE_MUTEX(table_share) (&(table_share->mutex))
+#endif
+
+namespace mrn {
+ AutoIncrementValueLock::AutoIncrementValueLock(TABLE_SHARE *table_share)
+ : table_share_(table_share),
+ need_lock_(table_share_->tmp_table == NO_TMP_TABLE) {
+ if (need_lock_) {
+ mysql_mutex_lock(AUTO_INCREMENT_VALUE_MUTEX(table_share_));
+ }
+ }
+
+ AutoIncrementValueLock::~AutoIncrementValueLock() {
+ if (need_lock_) {
+ mysql_mutex_unlock(AUTO_INCREMENT_VALUE_MUTEX(table_share_));
+ }
+ }
+}
diff --git a/storage/mroonga/lib/mrn_auto_increment_value_lock.hpp b/storage/mroonga/lib/mrn_auto_increment_value_lock.hpp
new file mode 100644
index 00000000000..8aabe6a8a7f
--- /dev/null
+++ b/storage/mroonga/lib/mrn_auto_increment_value_lock.hpp
@@ -0,0 +1,36 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_AUTO_INCREMENT_VALUE_LOCK_HPP_
+#define MRN_AUTO_INCREMENT_VALUE_LOCK_HPP_
+
+#include <mrn_mysql.h>
+#include <mrn_mysql_compat.h>
+
+namespace mrn {
+ class AutoIncrementValueLock {
+ TABLE_SHARE *table_share_;
+ bool need_lock_;
+ public:
+ AutoIncrementValueLock(TABLE_SHARE *table_share);
+ ~AutoIncrementValueLock();
+ };
+}
+
+#endif // MRN_AUTO_INCREMENT_VALUE_LOCK_HPP_
diff --git a/storage/mroonga/lib/mrn_condition_converter.cpp b/storage/mroonga/lib/mrn_condition_converter.cpp
new file mode 100644
index 00000000000..1bfae1d4f8a
--- /dev/null
+++ b/storage/mroonga/lib/mrn_condition_converter.cpp
@@ -0,0 +1,608 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013-2014 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "mrn_condition_converter.hpp"
+#include "mrn_time_converter.hpp"
+#include "mrn_smart_grn_obj.hpp"
+
+// for debug
+#define MRN_CLASS_NAME "mrn::ConditionConverter"
+
+#ifdef MRN_ITEM_HAVE_ITEM_NAME
+# define MRN_ITEM_FIELD_GET_NAME(item) ((item)->item_name.ptr())
+# define MRN_ITEM_FIELD_GET_NAME_LENGTH(item) ((item)->item_name.length())
+#else
+# define MRN_ITEM_FIELD_GET_NAME(item) ((item)->name)
+# define MRN_ITEM_FIELD_GET_NAME_LENGTH(item) (strlen((item)->name))
+#endif
+
+namespace mrn {
+ ConditionConverter::ConditionConverter(grn_ctx *ctx, grn_obj *table,
+ bool is_storage_mode)
+ : ctx_(ctx),
+ table_(table),
+ is_storage_mode_(is_storage_mode) {
+ GRN_TEXT_INIT(&column_name_, 0);
+ GRN_VOID_INIT(&value_);
+ }
+
+ ConditionConverter::~ConditionConverter() {
+ grn_obj_unlink(ctx_, &column_name_);
+ grn_obj_unlink(ctx_, &value_);
+ }
+
+ bool ConditionConverter::is_convertable(const Item *item) {
+ MRN_DBUG_ENTER_METHOD();
+
+ if (!item) {
+ DBUG_RETURN(false);
+ }
+
+ switch (item->type()) {
+ case Item::COND_ITEM:
+ {
+ const Item_cond *cond_item = reinterpret_cast<const Item_cond *>(item);
+ bool convertable = is_convertable(cond_item);
+ DBUG_RETURN(convertable);
+ }
+ break;
+ case Item::FUNC_ITEM:
+ {
+ const Item_func *func_item = reinterpret_cast<const Item_func *>(item);
+ bool convertable = is_convertable(func_item);
+ DBUG_RETURN(convertable);
+ }
+ break;
+ default:
+ DBUG_RETURN(false);
+ break;
+ }
+
+ DBUG_RETURN(false);
+ }
+
+ bool ConditionConverter::is_convertable(const Item_cond *cond_item) {
+ MRN_DBUG_ENTER_METHOD();
+
+ if (!is_storage_mode_) {
+ DBUG_RETURN(false);
+ }
+
+ if (cond_item->functype() != Item_func::COND_AND_FUNC) {
+ DBUG_RETURN(false);
+ }
+
+ List<Item> *argument_list =
+ const_cast<Item_cond *>(cond_item)->argument_list();
+ List_iterator<Item> iterator(*argument_list);
+ const Item *sub_item;
+ while ((sub_item = iterator++)) {
+ if (!is_convertable(sub_item)) {
+ DBUG_RETURN(false);
+ }
+ }
+
+ DBUG_RETURN(true);
+ }
+
+ bool ConditionConverter::is_convertable(const Item_func *func_item) {
+ MRN_DBUG_ENTER_METHOD();
+
+ switch (func_item->functype()) {
+ case Item_func::EQ_FUNC:
+ case Item_func::LT_FUNC:
+ case Item_func::LE_FUNC:
+ case Item_func::GE_FUNC:
+ case Item_func::GT_FUNC:
+ if (!is_storage_mode_) {
+ DBUG_RETURN(false);
+ }
+ {
+ Item **arguments = func_item->arguments();
+ Item *left_item = arguments[0];
+ Item *right_item = arguments[1];
+ if (left_item->type() != Item::FIELD_ITEM) {
+ DBUG_RETURN(false);
+ }
+ if (!right_item->basic_const_item()) {
+ DBUG_RETURN(false);
+ }
+
+ bool convertable =
+ is_convertable_binary_operation(static_cast<Item_field *>(left_item),
+ right_item,
+ func_item->functype());
+ DBUG_RETURN(convertable);
+ }
+ break;
+ case Item_func::FT_FUNC:
+ DBUG_RETURN(true);
+ break;
+ case Item_func::BETWEEN:
+ if (!is_storage_mode_) {
+ DBUG_RETURN(false);
+ }
+ {
+ Item **arguments = func_item->arguments();
+ Item *target_item = arguments[0];
+ Item *min_item = arguments[1];
+ Item *max_item = arguments[2];
+ if (target_item->type() != Item::FIELD_ITEM) {
+ DBUG_RETURN(false);
+ }
+ if (!min_item->basic_const_item()) {
+ DBUG_RETURN(false);
+ }
+ if (!max_item->basic_const_item()) {
+ DBUG_RETURN(false);
+ }
+
+ bool convertable =
+ is_convertable_between(static_cast<Item_field *>(target_item),
+ min_item,
+ max_item);
+ DBUG_RETURN(convertable);
+ }
+ default:
+ DBUG_RETURN(false);
+ break;
+ }
+
+ DBUG_RETURN(true);
+ }
+
+ bool ConditionConverter::is_convertable_binary_operation(
+ const Item_field *field_item,
+ Item *value_item,
+ Item_func::Functype func_type) {
+ MRN_DBUG_ENTER_METHOD();
+
+ bool convertable = false;
+
+ enum_field_types field_type = field_item->field_type();
+ NormalizedType normalized_type = normalize_field_type(field_type);
+ switch (normalized_type) {
+ case STRING_TYPE:
+ if (value_item->type() == Item::STRING_ITEM &&
+ func_type == Item_func::EQ_FUNC) {
+ convertable = have_index(field_item, GRN_OP_EQUAL);
+ }
+ break;
+ case INT_TYPE:
+ convertable = value_item->type() == Item::INT_ITEM;
+ break;
+ case TIME_TYPE:
+ if (is_valid_time_value(field_item, value_item)) {
+ convertable = have_index(field_item, func_type);
+ }
+ break;
+ case UNSUPPORTED_TYPE:
+ break;
+ }
+
+ DBUG_RETURN(convertable);
+ }
+
+ bool ConditionConverter::is_convertable_between(const Item_field *field_item,
+ Item *min_item,
+ Item *max_item) {
+ MRN_DBUG_ENTER_METHOD();
+
+ bool convertable = false;
+
+ enum_field_types field_type = field_item->field_type();
+ NormalizedType normalized_type = normalize_field_type(field_type);
+ switch (normalized_type) {
+ case STRING_TYPE:
+ if (min_item->type() == Item::STRING_ITEM &&
+ max_item->type() == Item::STRING_ITEM) {
+ convertable = have_index(field_item, GRN_OP_LESS);
+ }
+ break;
+ case INT_TYPE:
+ if (min_item->type() == Item::INT_ITEM &&
+ max_item->type() == Item::INT_ITEM) {
+ convertable = have_index(field_item, GRN_OP_LESS);
+ }
+ break;
+ case TIME_TYPE:
+ if (is_valid_time_value(field_item, min_item) &&
+ is_valid_time_value(field_item, max_item)) {
+ convertable = have_index(field_item, GRN_OP_LESS);
+ }
+ break;
+ case UNSUPPORTED_TYPE:
+ break;
+ }
+
+ DBUG_RETURN(convertable);
+ }
+
+ bool ConditionConverter::is_valid_time_value(const Item_field *field_item,
+ Item *value_item) {
+ MRN_DBUG_ENTER_METHOD();
+
+ MYSQL_TIME mysql_time;
+ bool error = get_time_value(field_item, value_item, &mysql_time);
+
+ DBUG_RETURN(!error);
+ }
+
+ bool ConditionConverter::get_time_value(const Item_field *field_item,
+ Item *value_item,
+ MYSQL_TIME *mysql_time) {
+ MRN_DBUG_ENTER_METHOD();
+
+ bool error;
+ Item *real_value_item = value_item->real_item();
+ switch (field_item->field_type()) {
+ case MYSQL_TYPE_TIME:
+ error = real_value_item->get_time(mysql_time);
+ break;
+ case MYSQL_TYPE_YEAR:
+ mysql_time->year = static_cast<int>(value_item->val_int());
+ mysql_time->month = 1;
+ mysql_time->day = 1;
+ mysql_time->hour = 0;
+ mysql_time->hour = 0;
+ mysql_time->minute = 0;
+ mysql_time->second_part = 0;
+ mysql_time->neg = false;
+ mysql_time->time_type = MYSQL_TIMESTAMP_DATE;
+ error = false;
+ break;
+ default:
+ error = real_value_item->get_date(mysql_time, TIME_FUZZY_DATE);
+ break;
+ }
+
+ DBUG_RETURN(error);
+ }
+
+ ConditionConverter::NormalizedType
+ ConditionConverter::normalize_field_type(enum_field_types field_type) {
+ MRN_DBUG_ENTER_METHOD();
+
+ NormalizedType type = UNSUPPORTED_TYPE;
+
+ switch (field_type) {
+ case MYSQL_TYPE_DECIMAL:
+ type = STRING_TYPE;
+ break;
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ type = INT_TYPE;
+ break;
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ type = UNSUPPORTED_TYPE;
+ break;
+ case MYSQL_TYPE_NULL:
+ type = UNSUPPORTED_TYPE;
+ break;
+ case MYSQL_TYPE_TIMESTAMP:
+ type = TIME_TYPE;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ type = INT_TYPE;
+ break;
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_NEWDATE:
+ type = TIME_TYPE;
+ break;
+ case MYSQL_TYPE_VARCHAR:
+ type = STRING_TYPE;
+ break;
+ case MYSQL_TYPE_BIT:
+ type = INT_TYPE;
+ break;
+#ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2
+ case MYSQL_TYPE_TIMESTAMP2:
+ type = TIME_TYPE;
+ break;
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
+ case MYSQL_TYPE_DATETIME2:
+ type = TIME_TYPE;
+ break;
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
+ case MYSQL_TYPE_TIME2:
+ type = TIME_TYPE;
+ break;
+#endif
+ case MYSQL_TYPE_NEWDECIMAL:
+ type = STRING_TYPE;
+ break;
+ case MYSQL_TYPE_ENUM:
+ type = INT_TYPE;
+ break;
+ case MYSQL_TYPE_SET:
+ type = INT_TYPE;
+ break;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ type = STRING_TYPE;
+ break;
+ case MYSQL_TYPE_GEOMETRY:
+ type = UNSUPPORTED_TYPE;
+ break;
+ }
+
+ DBUG_RETURN(type);
+ }
+
+ bool ConditionConverter::have_index(const Item_field *field_item,
+ grn_operator _operator) {
+ MRN_DBUG_ENTER_METHOD();
+
+ grn_obj *column;
+ column = grn_obj_column(ctx_, table_,
+ MRN_ITEM_FIELD_GET_NAME(field_item),
+ MRN_ITEM_FIELD_GET_NAME_LENGTH(field_item));
+ if (!column) {
+ DBUG_RETURN(false);
+ }
+ mrn::SmartGrnObj smart_column(ctx_, column);
+
+ int n_indexes = grn_column_index(ctx_, column, _operator, NULL, 0, NULL);
+ bool convertable = (n_indexes > 0);
+
+ DBUG_RETURN(convertable);
+ }
+
+ bool ConditionConverter::have_index(const Item_field *field_item,
+ Item_func::Functype func_type) {
+ MRN_DBUG_ENTER_METHOD();
+
+ bool have = false;
+ switch (func_type) {
+ case Item_func::EQ_FUNC:
+ have = have_index(field_item, GRN_OP_EQUAL);
+ break;
+ case Item_func::LT_FUNC:
+ have = have_index(field_item, GRN_OP_LESS);
+ break;
+ case Item_func::LE_FUNC:
+ have = have_index(field_item, GRN_OP_LESS_EQUAL);
+ break;
+ case Item_func::GE_FUNC:
+ have = have_index(field_item, GRN_OP_GREATER_EQUAL);
+ break;
+ case Item_func::GT_FUNC:
+ have = have_index(field_item, GRN_OP_GREATER);
+ break;
+ default:
+ break;
+ }
+
+ DBUG_RETURN(have);
+ }
+
+ const Item_func *ConditionConverter::find_match_against(const Item *item) {
+ MRN_DBUG_ENTER_METHOD();
+
+ if (!item) {
+ DBUG_RETURN(NULL);
+ }
+
+ switch (item->type()) {
+ case Item::COND_ITEM:
+ if (is_storage_mode_) {
+ Item_cond *cond_item = (Item_cond *)item;
+ if (cond_item->functype() == Item_func::COND_AND_FUNC) {
+ List_iterator<Item> iterator(*((cond_item)->argument_list()));
+ const Item *sub_item;
+ while ((sub_item = iterator++)) {
+ const Item_func *match_against = find_match_against(sub_item);
+ if (match_against) {
+ DBUG_RETURN(match_against);
+ }
+ }
+ }
+ }
+ break;
+ case Item::FUNC_ITEM:
+ {
+ const Item_func *func_item = (const Item_func *)item;
+ switch (func_item->functype()) {
+ case Item_func::FT_FUNC:
+ DBUG_RETURN(func_item);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ DBUG_RETURN(NULL);
+ }
+
+ void ConditionConverter::convert(const Item *where, grn_obj *expression) {
+ MRN_DBUG_ENTER_METHOD();
+
+ if (!where || where->type() != Item::COND_ITEM) {
+ DBUG_VOID_RETURN;
+ }
+
+ Item_cond *cond_item = (Item_cond *)where;
+ List_iterator<Item> iterator(*((cond_item)->argument_list()));
+ const Item *sub_item;
+ while ((sub_item = iterator++)) {
+ switch (sub_item->type()) {
+ case Item::FUNC_ITEM:
+ {
+ const Item_func *func_item = (const Item_func *)sub_item;
+ switch (func_item->functype()) {
+ case Item_func::EQ_FUNC:
+ convert_binary_operation(func_item, expression, GRN_OP_EQUAL);
+ break;
+ case Item_func::LT_FUNC:
+ convert_binary_operation(func_item, expression, GRN_OP_LESS);
+ break;
+ case Item_func::LE_FUNC:
+ convert_binary_operation(func_item, expression, GRN_OP_LESS_EQUAL);
+ break;
+ case Item_func::GE_FUNC:
+ convert_binary_operation(func_item, expression,
+ GRN_OP_GREATER_EQUAL);
+ break;
+ case Item_func::GT_FUNC:
+ convert_binary_operation(func_item, expression, GRN_OP_GREATER);
+ break;
+ case Item_func::BETWEEN:
+ convert_between(func_item, expression);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ DBUG_VOID_RETURN;
+ }
+
+ void ConditionConverter::convert_binary_operation(const Item_func *func_item,
+ grn_obj *expression,
+ grn_operator _operator) {
+ Item **arguments = func_item->arguments();
+ Item *left_item = arguments[0];
+ Item *right_item = arguments[1];
+ if (left_item->type() == Item::FIELD_ITEM) {
+ const Item_field *field_item = static_cast<const Item_field *>(left_item);
+ append_field_value(field_item, expression);
+ append_const_item(field_item, right_item, expression);
+ grn_expr_append_op(ctx_, expression, _operator, 2);
+ grn_expr_append_op(ctx_, expression, GRN_OP_AND, 2);
+ }
+ }
+
+ void ConditionConverter::convert_between(const Item_func *func_item,
+ grn_obj *expression) {
+ MRN_DBUG_ENTER_METHOD();
+
+ Item **arguments = func_item->arguments();
+ Item *target_item = arguments[0];
+ Item *min_item = arguments[1];
+ Item *max_item = arguments[2];
+
+ grn_obj *between_func = grn_ctx_get(ctx_, "between", strlen("between"));
+ grn_expr_append_obj(ctx_, expression, between_func, GRN_OP_PUSH, 1);
+
+ const Item_field *field_item = static_cast<const Item_field *>(target_item);
+ append_field_value(field_item, expression);
+
+ grn_obj include;
+ mrn::SmartGrnObj smart_include(ctx_, &include);
+ GRN_TEXT_INIT(&include, 0);
+ GRN_TEXT_PUTS(ctx_, &include, "include");
+ append_const_item(field_item, min_item, expression);
+ grn_expr_append_const(ctx_, expression, &include, GRN_OP_PUSH, 1);
+ append_const_item(field_item, max_item, expression);
+ grn_expr_append_const(ctx_, expression, &include, GRN_OP_PUSH, 1);
+
+ grn_expr_append_op(ctx_, expression, GRN_OP_CALL, 5);
+
+ grn_expr_append_op(ctx_, expression, GRN_OP_AND, 2);
+
+ DBUG_VOID_RETURN;
+ }
+
+ void ConditionConverter::append_field_value(const Item_field *field_item,
+ grn_obj *expression) {
+ MRN_DBUG_ENTER_METHOD();
+
+ GRN_BULK_REWIND(&column_name_);
+ GRN_TEXT_PUT(ctx_, &column_name_,
+ MRN_ITEM_FIELD_GET_NAME(field_item),
+ MRN_ITEM_FIELD_GET_NAME_LENGTH(field_item));
+ grn_expr_append_const(ctx_, expression, &column_name_,
+ GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx_, expression, GRN_OP_GET_VALUE, 1);
+
+ DBUG_VOID_RETURN;
+ }
+
+ void ConditionConverter::append_const_item(const Item_field *field_item,
+ Item *const_item,
+ grn_obj *expression) {
+ MRN_DBUG_ENTER_METHOD();
+
+ enum_field_types field_type = field_item->field_type();
+ NormalizedType normalized_type = normalize_field_type(field_type);
+
+ switch (normalized_type) {
+ case STRING_TYPE:
+ grn_obj_reinit(ctx_, &value_, GRN_DB_TEXT, 0);
+ {
+ String *string;
+ string = const_item->val_str(NULL);
+ GRN_TEXT_SET(ctx_, &value_, string->ptr(), string->length());
+ }
+ break;
+ case INT_TYPE:
+ grn_obj_reinit(ctx_, &value_, GRN_DB_INT64, 0);
+ GRN_INT64_SET(ctx_, &value_, const_item->val_int());
+ break;
+ case TIME_TYPE:
+ grn_obj_reinit(ctx_, &value_, GRN_DB_TIME, 0);
+ {
+ MYSQL_TIME mysql_time;
+ get_time_value(field_item, const_item, &mysql_time);
+ bool truncated = false;
+ TimeConverter time_converter;
+ long long int time =
+ time_converter.mysql_time_to_grn_time(&mysql_time, &truncated);
+ GRN_TIME_SET(ctx_, &value_, time);
+ }
+ break;
+ case UNSUPPORTED_TYPE:
+ // Should not be occurred.
+ DBUG_PRINT("error",
+ ("mroonga: append_const_item: unsupported type: <%d> "
+ "This case should not be occurred.",
+ field_type));
+ grn_obj_reinit(ctx_, &value_, GRN_DB_VOID, 0);
+ break;
+ }
+ grn_expr_append_const(ctx_, expression, &value_, GRN_OP_PUSH, 1);
+
+ DBUG_VOID_RETURN;
+ }
+}
diff --git a/storage/mroonga/lib/mrn_condition_converter.hpp b/storage/mroonga/lib/mrn_condition_converter.hpp
new file mode 100644
index 00000000000..3cf97c62bbe
--- /dev/null
+++ b/storage/mroonga/lib/mrn_condition_converter.hpp
@@ -0,0 +1,82 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_CONDITION_CONVERTER_HPP_
+#define MRN_CONDITION_CONVERTER_HPP_
+
+#include <groonga.h>
+#include <mrn_mysql_compat.h>
+
+namespace mrn {
+ class ConditionConverter {
+ public:
+ ConditionConverter(grn_ctx *ctx, grn_obj *table, bool is_storage_mode);
+ ~ConditionConverter();
+
+ bool is_convertable(const Item *item);
+ const Item_func *find_match_against(const Item *item);
+ // caller must check "where" can be convertable by
+ // is_convertable(). This method doesn't validate "where".
+ void convert(const Item *where, grn_obj *expression);
+
+ private:
+ enum NormalizedType {
+ STRING_TYPE,
+ INT_TYPE,
+ TIME_TYPE,
+ UNSUPPORTED_TYPE,
+ };
+
+ grn_ctx *ctx_;
+ grn_obj *table_;
+ bool is_storage_mode_;
+ grn_obj column_name_;
+ grn_obj value_;
+
+ bool is_convertable(const Item_cond *cond_item);
+ bool is_convertable(const Item_func *func_item);
+ bool is_convertable_binary_operation(const Item_field *field_item,
+ Item *value_item,
+ Item_func::Functype func_type);
+ bool is_convertable_between(const Item_field *field_item,
+ Item *min_item,
+ Item *max_item);
+ bool is_valid_time_value(const Item_field *field_item,
+ Item *value_item);
+ bool get_time_value(const Item_field *field_item,
+ Item *value_item,
+ MYSQL_TIME *mysql_time);
+ bool have_index(const Item_field *field_item, grn_operator _operator);
+ bool have_index(const Item_field *field_item, Item_func::Functype func_type);
+
+ NormalizedType normalize_field_type(enum_field_types field_type);
+
+ void convert_binary_operation(const Item_func *func_item,
+ grn_obj *expression,
+ grn_operator _operator);
+ void convert_between(const Item_func *func_item, grn_obj *expression);
+ void append_field_value(const Item_field *field_item,
+ grn_obj *expression);
+ void append_const_item(const Item_field *field_item,
+ Item *const_item,
+ grn_obj *expression);
+ };
+}
+
+#endif /* MRN_CONDITION_CONVERTER_HPP_ */
diff --git a/storage/mroonga/lib/mrn_debug_column_access.cpp b/storage/mroonga/lib/mrn_debug_column_access.cpp
new file mode 100644
index 00000000000..ed7cacae90f
--- /dev/null
+++ b/storage/mroonga/lib/mrn_debug_column_access.cpp
@@ -0,0 +1,36 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "mrn_debug_column_access.hpp"
+
+namespace mrn {
+ DebugColumnAccess::DebugColumnAccess(TABLE *table, MY_BITMAP *bitmap)
+ : table_(table),
+ bitmap_(bitmap) {
+#ifndef DBUG_OFF
+ map_ = dbug_tmp_use_all_columns(table_, bitmap_);
+#endif
+ }
+
+ DebugColumnAccess::~DebugColumnAccess() {
+#ifndef DBUG_OFF
+ dbug_tmp_restore_column_map(bitmap_, map_);
+#endif
+ }
+}
diff --git a/storage/mroonga/lib/mrn_debug_column_access.hpp b/storage/mroonga/lib/mrn_debug_column_access.hpp
new file mode 100644
index 00000000000..1548b4d8459
--- /dev/null
+++ b/storage/mroonga/lib/mrn_debug_column_access.hpp
@@ -0,0 +1,38 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_DEBUG_COLUMN_ACCESS_HPP_
+#define MRN_DEBUG_COLUMN_ACCESS_HPP_
+
+#include <mrn_mysql.h>
+
+namespace mrn {
+ class DebugColumnAccess {
+ TABLE *table_;
+ MY_BITMAP *bitmap_;
+#ifndef DBUG_OFF
+ my_bitmap_map *map_;
+#endif
+ public:
+ DebugColumnAccess(TABLE *table, MY_BITMAP *bitmap);
+ ~DebugColumnAccess();
+ };
+}
+
+#endif // MRN_DEBUG_COLUMN_ACCESS_HPP_
diff --git a/storage/mroonga/lib/mrn_encoding.cpp b/storage/mroonga/lib/mrn_encoding.cpp
new file mode 100644
index 00000000000..a69a849c053
--- /dev/null
+++ b/storage/mroonga/lib/mrn_encoding.cpp
@@ -0,0 +1,222 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+ Copyright(C) 2011-2013 Kentoku SHIBA
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <mrn_err.h>
+#include "mrn_encoding.hpp"
+
+namespace mrn {
+ namespace encoding {
+ CHARSET_INFO *mrn_charset_utf8 = NULL;
+ CHARSET_INFO *mrn_charset_utf8mb4 = NULL;
+ CHARSET_INFO *mrn_charset_binary = NULL;
+ CHARSET_INFO *mrn_charset_ascii = NULL;
+ CHARSET_INFO *mrn_charset_latin1_1 = NULL;
+ CHARSET_INFO *mrn_charset_latin1_2 = NULL;
+ CHARSET_INFO *mrn_charset_cp932 = NULL;
+ CHARSET_INFO *mrn_charset_sjis = NULL;
+ CHARSET_INFO *mrn_charset_eucjpms = NULL;
+ CHARSET_INFO *mrn_charset_ujis = NULL;
+ CHARSET_INFO *mrn_charset_koi8r = NULL;
+
+ void init(void) {
+ CHARSET_INFO **cs;
+ MRN_DBUG_ENTER_FUNCTION();
+ for (cs = all_charsets; cs < all_charsets + MY_ALL_CHARSETS_SIZE; cs++)
+ {
+ if (!cs[0])
+ continue;
+ if (!strcmp(cs[0]->csname, "utf8"))
+ {
+ DBUG_PRINT("info", ("mroonga: %s is %s [%p]",
+ cs[0]->name, cs[0]->csname, cs[0]->cset));
+ if (!mrn_charset_utf8)
+ mrn_charset_utf8 = cs[0];
+ else if (mrn_charset_utf8->cset != cs[0]->cset)
+ DBUG_ASSERT(0);
+ continue;
+ }
+ if (!strcmp(cs[0]->csname, "utf8mb4"))
+ {
+ DBUG_PRINT("info", ("mroonga: %s is %s [%p]",
+ cs[0]->name, cs[0]->csname, cs[0]->cset));
+ if (!mrn_charset_utf8mb4)
+ mrn_charset_utf8mb4 = cs[0];
+ else if (mrn_charset_utf8mb4->cset != cs[0]->cset)
+ DBUG_ASSERT(0);
+ continue;
+ }
+ if (!strcmp(cs[0]->csname, "binary"))
+ {
+ DBUG_PRINT("info", ("mroonga: %s is %s [%p]",
+ cs[0]->name, cs[0]->csname, cs[0]->cset));
+ if (!mrn_charset_binary)
+ mrn_charset_binary = cs[0];
+ else if (mrn_charset_binary->cset != cs[0]->cset)
+ DBUG_ASSERT(0);
+ continue;
+ }
+ if (!strcmp(cs[0]->csname, "ascii"))
+ {
+ DBUG_PRINT("info", ("mroonga: %s is %s [%p]",
+ cs[0]->name, cs[0]->csname, cs[0]->cset));
+ if (!mrn_charset_ascii)
+ mrn_charset_ascii = cs[0];
+ else if (mrn_charset_ascii->cset != cs[0]->cset)
+ DBUG_ASSERT(0);
+ continue;
+ }
+ if (!strcmp(cs[0]->csname, "latin1"))
+ {
+ DBUG_PRINT("info", ("mroonga: %s is %s [%p]",
+ cs[0]->name, cs[0]->csname, cs[0]->cset));
+ if (!mrn_charset_latin1_1)
+ mrn_charset_latin1_1 = cs[0];
+ else if (mrn_charset_latin1_1->cset != cs[0]->cset)
+ {
+ if (!mrn_charset_latin1_2)
+ mrn_charset_latin1_2 = cs[0];
+ else if (mrn_charset_latin1_2->cset != cs[0]->cset)
+ DBUG_ASSERT(0);
+ }
+ continue;
+ }
+ if (!strcmp(cs[0]->csname, "cp932"))
+ {
+ DBUG_PRINT("info", ("mroonga: %s is %s [%p]",
+ cs[0]->name, cs[0]->csname, cs[0]->cset));
+ if (!mrn_charset_cp932)
+ mrn_charset_cp932 = cs[0];
+ else if (mrn_charset_cp932->cset != cs[0]->cset)
+ DBUG_ASSERT(0);
+ continue;
+ }
+ if (!strcmp(cs[0]->csname, "sjis"))
+ {
+ DBUG_PRINT("info", ("mroonga: %s is %s [%p]",
+ cs[0]->name, cs[0]->csname, cs[0]->cset));
+ if (!mrn_charset_sjis)
+ mrn_charset_sjis = cs[0];
+ else if (mrn_charset_sjis->cset != cs[0]->cset)
+ DBUG_ASSERT(0);
+ continue;
+ }
+ if (!strcmp(cs[0]->csname, "eucjpms"))
+ {
+ DBUG_PRINT("info", ("mroonga: %s is %s [%p]",
+ cs[0]->name, cs[0]->csname, cs[0]->cset));
+ if (!mrn_charset_eucjpms)
+ mrn_charset_eucjpms = cs[0];
+ else if (mrn_charset_eucjpms->cset != cs[0]->cset)
+ DBUG_ASSERT(0);
+ continue;
+ }
+ if (!strcmp(cs[0]->csname, "ujis"))
+ {
+ DBUG_PRINT("info", ("mroonga: %s is %s [%p]",
+ cs[0]->name, cs[0]->csname, cs[0]->cset));
+ if (!mrn_charset_ujis)
+ mrn_charset_ujis = cs[0];
+ else if (mrn_charset_ujis->cset != cs[0]->cset)
+ DBUG_ASSERT(0);
+ continue;
+ }
+ if (!strcmp(cs[0]->csname, "koi8r"))
+ {
+ DBUG_PRINT("info", ("mroonga: %s is %s [%p]",
+ cs[0]->name, cs[0]->csname, cs[0]->cset));
+ if (!mrn_charset_koi8r)
+ mrn_charset_koi8r = cs[0];
+ else if (mrn_charset_koi8r->cset != cs[0]->cset)
+ DBUG_ASSERT(0);
+ continue;
+ }
+ DBUG_PRINT("info", ("mroonga: %s[%s][%p] is not supported",
+ cs[0]->name, cs[0]->csname, cs[0]->cset));
+ }
+ DBUG_VOID_RETURN;
+ }
+
+ bool set(grn_ctx *ctx, const CHARSET_INFO *charset) {
+ MRN_DBUG_ENTER_FUNCTION();
+ if (!charset)
+ {
+ GRN_CTX_SET_ENCODING(ctx, GRN_ENC_NONE);
+ DBUG_RETURN(true);
+ }
+ if (charset->cset == mrn_charset_utf8->cset)
+ {
+ GRN_CTX_SET_ENCODING(ctx, GRN_ENC_UTF8);
+ DBUG_RETURN(true);
+ }
+ if (mrn_charset_utf8mb4 && charset->cset == mrn_charset_utf8mb4->cset)
+ {
+ GRN_CTX_SET_ENCODING(ctx, GRN_ENC_UTF8);
+ DBUG_RETURN(true);
+ }
+ if (charset->cset == mrn_charset_cp932->cset)
+ {
+ GRN_CTX_SET_ENCODING(ctx, GRN_ENC_SJIS);
+ DBUG_RETURN(true);
+ }
+ if (charset->cset == mrn_charset_eucjpms->cset)
+ {
+ GRN_CTX_SET_ENCODING(ctx, GRN_ENC_EUC_JP);
+ DBUG_RETURN(true);
+ }
+ if (charset->cset == mrn_charset_latin1_1->cset)
+ {
+ GRN_CTX_SET_ENCODING(ctx, GRN_ENC_LATIN1);
+ DBUG_RETURN(true);
+ }
+ if (charset->cset == mrn_charset_latin1_2->cset)
+ {
+ GRN_CTX_SET_ENCODING(ctx, GRN_ENC_LATIN1);
+ DBUG_RETURN(true);
+ }
+ if (charset->cset == mrn_charset_koi8r->cset)
+ {
+ GRN_CTX_SET_ENCODING(ctx, GRN_ENC_KOI8R);
+ DBUG_RETURN(true);
+ }
+ if (charset->cset == mrn_charset_binary->cset)
+ {
+ GRN_CTX_SET_ENCODING(ctx, GRN_ENC_NONE);
+ DBUG_RETURN(true);
+ }
+ if (charset->cset == mrn_charset_ascii->cset)
+ {
+ GRN_CTX_SET_ENCODING(ctx, GRN_ENC_UTF8);
+ DBUG_RETURN(true);
+ }
+ if (charset->cset == mrn_charset_sjis->cset)
+ {
+ GRN_CTX_SET_ENCODING(ctx, GRN_ENC_SJIS);
+ DBUG_RETURN(true);
+ }
+ if (charset->cset == mrn_charset_ujis->cset)
+ {
+ GRN_CTX_SET_ENCODING(ctx, GRN_ENC_EUC_JP);
+ DBUG_RETURN(true);
+ }
+ GRN_CTX_SET_ENCODING(ctx, GRN_ENC_NONE);
+ DBUG_RETURN(false);
+ }
+ }
+}
diff --git a/storage/mroonga/lib/mrn_encoding.hpp b/storage/mroonga/lib/mrn_encoding.hpp
new file mode 100644
index 00000000000..b4f21e1a274
--- /dev/null
+++ b/storage/mroonga/lib/mrn_encoding.hpp
@@ -0,0 +1,35 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_ENCODING_HPP_
+#define MRN_ENCODING_HPP_
+
+#include <groonga.h>
+
+#include <mrn_mysql.h>
+#include <mrn_mysql_compat.h>
+
+namespace mrn {
+ namespace encoding {
+ void init(void);
+ bool set(grn_ctx *ctx, const CHARSET_INFO *charset);
+ }
+}
+
+#endif // MRN_ENCODING_HPP_
diff --git a/storage/mroonga/lib/mrn_external_lock.cpp b/storage/mroonga/lib/mrn_external_lock.cpp
new file mode 100644
index 00000000000..b266b6594ca
--- /dev/null
+++ b/storage/mroonga/lib/mrn_external_lock.cpp
@@ -0,0 +1,43 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Kentoku SHIBA
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "mrn_external_lock.hpp"
+
+namespace mrn {
+ ExternalLock::ExternalLock(THD *thd, handler *handler, int lock_type)
+ : thd_(thd),
+ handler_(handler),
+ lock_type_(lock_type) {
+ if (lock_type_ != F_UNLCK) {
+ error_ = handler_->ha_external_lock(thd_, lock_type);
+ } else {
+ error_ = 0;
+ }
+ }
+
+ ExternalLock::~ExternalLock() {
+ if (lock_type_ != F_UNLCK) {
+ handler_->ha_external_lock(thd_, F_UNLCK);
+ }
+ }
+
+ int ExternalLock::error() {
+ return error_;
+ }
+}
diff --git a/storage/mroonga/lib/mrn_external_lock.hpp b/storage/mroonga/lib/mrn_external_lock.hpp
new file mode 100644
index 00000000000..f78b436f6e8
--- /dev/null
+++ b/storage/mroonga/lib/mrn_external_lock.hpp
@@ -0,0 +1,38 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Kentoku SHIBA
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_EXTERNAL_LOCK_HPP_
+#define MRN_EXTERNAL_LOCK_HPP_
+
+#include <mrn_mysql.h>
+
+namespace mrn {
+ class ExternalLock {
+ THD *thd_;
+ handler *handler_;
+ int lock_type_;
+ int error_;
+ public:
+ ExternalLock(THD *thd, handler *handler, int lock_type);
+ ~ExternalLock();
+ int error();
+ };
+}
+
+#endif // MRN_EXTERNAL_LOCK_HPP_
diff --git a/storage/mroonga/lib/mrn_field_normalizer.cpp b/storage/mroonga/lib/mrn_field_normalizer.cpp
new file mode 100644
index 00000000000..bb91b3ab98d
--- /dev/null
+++ b/storage/mroonga/lib/mrn_field_normalizer.cpp
@@ -0,0 +1,142 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "mrn_field_normalizer.hpp"
+#include "mrn_encoding.hpp"
+
+// for debug
+#define MRN_CLASS_NAME "mrn::FieldNormalizer"
+
+namespace mrn {
+ FieldNormalizer::FieldNormalizer(grn_ctx *ctx, THD *thread, Field *field)
+ : ctx_(ctx),
+ thread_(thread),
+ field_(field) {
+ }
+
+ FieldNormalizer::~FieldNormalizer() {
+ }
+
+ bool FieldNormalizer::should_normalize() {
+ MRN_DBUG_ENTER_METHOD();
+
+ DBUG_PRINT("info",
+ ("mroonga: result_type = %u", field_->result_type()));
+ DBUG_PRINT("info",
+ ("mroonga: charset->name = %s", field_->charset()->name));
+ DBUG_PRINT("info",
+ ("mroonga: charset->csname = %s", field_->charset()->csname));
+ DBUG_PRINT("info",
+ ("mroonga: charset->state = %u", field_->charset()->state));
+ bool need_normalize_p;
+ if (field_->charset()->state & (MY_CS_BINSORT | MY_CS_CSSORT)) {
+ need_normalize_p = false;
+ DBUG_PRINT("info",
+ ("mroonga: should_normalize: false: sort is required"));
+ } else {
+ if (is_text_type()) {
+ need_normalize_p = true;
+ DBUG_PRINT("info", ("mroonga: should_normalize: true: text type"));
+ } else {
+ need_normalize_p = false;
+ DBUG_PRINT("info", ("mroonga: should_normalize: false: no text type"));
+ }
+ }
+
+ DBUG_RETURN(need_normalize_p);
+ }
+
+ bool FieldNormalizer::is_text_type() {
+ MRN_DBUG_ENTER_METHOD();
+ bool text_type_p;
+ switch (field_->type()) {
+ case MYSQL_TYPE_VARCHAR:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_VAR_STRING:
+ text_type_p = true;
+ break;
+ case MYSQL_TYPE_STRING:
+ switch (field_->real_type()) {
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ text_type_p = false;
+ break;
+ default:
+ text_type_p = true;
+ break;
+ }
+ break;
+ default:
+ text_type_p = false;
+ break;
+ }
+ DBUG_RETURN(text_type_p);
+ }
+
+ grn_obj *FieldNormalizer::normalize(const char *string,
+ unsigned int string_length) {
+ MRN_DBUG_ENTER_METHOD();
+ grn_obj *normalizer = find_grn_normalizer();
+ int flags = 0;
+ grn_encoding original_encoding = GRN_CTX_GET_ENCODING(ctx_);
+ encoding::set(ctx_, field_->charset());
+ grn_obj *grn_string = grn_string_open(ctx_, string, string_length,
+ normalizer, flags);
+ GRN_CTX_SET_ENCODING(ctx_, original_encoding);
+ DBUG_RETURN(grn_string);
+ }
+
+ grn_obj *FieldNormalizer::find_grn_normalizer() {
+ MRN_DBUG_ENTER_METHOD();
+
+ const CHARSET_INFO *charset_info = field_->charset();
+ const char *normalizer_name = NULL;
+ const char *default_normalizer_name = "NormalizerAuto";
+ if ((strcmp(charset_info->name, "utf8_general_ci") == 0) ||
+ (strcmp(charset_info->name, "utf8mb4_general_ci") == 0)) {
+ normalizer_name = "NormalizerMySQLGeneralCI";
+ } else if ((strcmp(charset_info->name, "utf8_unicode_ci") == 0) ||
+ (strcmp(charset_info->name, "utf8mb4_unicode_ci") == 0)) {
+ normalizer_name = "NormalizerMySQLUnicodeCI";
+ }
+
+ grn_obj *normalizer = NULL;
+ if (normalizer_name) {
+ normalizer = grn_ctx_get(ctx_, normalizer_name, -1);
+ if (!normalizer) {
+ char error_message[MRN_MESSAGE_BUFFER_SIZE];
+ snprintf(error_message, MRN_MESSAGE_BUFFER_SIZE,
+ "%s normalizer isn't found for %s. "
+ "Install groonga-normalizer-mysql normalizer. "
+ "%s is used as fallback.",
+ normalizer_name,
+ charset_info->name,
+ default_normalizer_name);
+ push_warning(thread_, Sql_condition::WARN_LEVEL_WARN,
+ HA_ERR_UNSUPPORTED, error_message);
+ }
+ }
+
+ if (!normalizer) {
+ normalizer = grn_ctx_get(ctx_, default_normalizer_name, -1);
+ }
+
+ DBUG_RETURN(normalizer);
+ }
+}
diff --git a/storage/mroonga/lib/mrn_field_normalizer.hpp b/storage/mroonga/lib/mrn_field_normalizer.hpp
new file mode 100644
index 00000000000..5fd8974ce5b
--- /dev/null
+++ b/storage/mroonga/lib/mrn_field_normalizer.hpp
@@ -0,0 +1,47 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_FIELD_NORMALIZER_HPP_
+#define MRN_FIELD_NORMALIZER_HPP_
+
+#include <groonga.h>
+
+#include <mrn_mysql.h>
+#include <mrn_mysql_compat.h>
+
+namespace mrn {
+ class FieldNormalizer {
+ public:
+ FieldNormalizer(grn_ctx *ctx, THD *thread, Field *field);
+ ~FieldNormalizer();
+
+ bool should_normalize();
+ grn_obj *normalize(const char *string, unsigned int string_length);
+ grn_obj *find_grn_normalizer();
+
+ private:
+ grn_ctx *ctx_;
+ THD *thread_;
+ Field *field_;
+
+ bool is_text_type();
+ };
+}
+
+#endif // MRN_FIELD_NORMALIZER_HPP_
diff --git a/storage/mroonga/lib/mrn_index_column_name.cpp b/storage/mroonga/lib/mrn_index_column_name.cpp
new file mode 100644
index 00000000000..14e83ec8e34
--- /dev/null
+++ b/storage/mroonga/lib/mrn_index_column_name.cpp
@@ -0,0 +1,96 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2011-2013 Kentoku SHIBA
+ Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <mrn_mysql.h>
+
+#include "mrn_index_column_name.hpp"
+
+#define MRN_MIN_INDEX_COLUMN_NAME_LENGTH 65
+
+// for debug
+#define MRN_CLASS_NAME "mrn::IndexColumnName"
+
+namespace mrn {
+ IndexColumnName::IndexColumnName(const char *table_name,
+ const char *mysql_column_name)
+ : table_name_(table_name),
+ mysql_column_name_(mysql_column_name) {
+ uchar encoded_mysql_column_name_multibyte[MRN_MAX_KEY_SIZE];
+ const uchar *mysql_column_name_multibyte =
+ reinterpret_cast<const uchar *>(mysql_column_name_);
+ encode(encoded_mysql_column_name_multibyte,
+ encoded_mysql_column_name_multibyte + MRN_MAX_KEY_SIZE,
+ mysql_column_name_multibyte,
+ mysql_column_name_multibyte + strlen(mysql_column_name_));
+ snprintf(name_, MRN_MAX_KEY_SIZE,
+ "%s-%s", table_name_, encoded_mysql_column_name_multibyte);
+ length_ = strlen(name_);
+ if (length_ < MRN_MIN_INDEX_COLUMN_NAME_LENGTH) {
+ memset(name_ + length_, '-', MRN_MIN_INDEX_COLUMN_NAME_LENGTH - length_);
+ length_ = MRN_MIN_INDEX_COLUMN_NAME_LENGTH;
+ name_[length_] = '\0';
+ }
+ }
+
+ const char *IndexColumnName::c_str() {
+ return name_;
+ }
+
+ size_t IndexColumnName::length() {
+ return length_;
+ }
+
+ uint IndexColumnName::encode(uchar *encoded_start,
+ uchar *encoded_end,
+ const uchar *mysql_string_start,
+ const uchar *mysql_string_end) {
+ MRN_DBUG_ENTER_METHOD();
+ my_charset_conv_mb_wc mb_wc = system_charset_info->cset->mb_wc;
+ my_charset_conv_wc_mb wc_mb = my_charset_filename.cset->wc_mb;
+ DBUG_PRINT("info", ("mroonga: in=%s", mysql_string_start));
+ encoded_end--;
+ uchar *encoded = encoded_start;
+ const uchar *mysql_string = mysql_string_start;
+ while (mysql_string < mysql_string_end && encoded < encoded_end) {
+ my_wc_t wc;
+ int mb_wc_converted_length;
+ int wc_mb_converted_length;
+ mb_wc_converted_length =
+ (*mb_wc)(NULL, &wc, mysql_string, mysql_string_end);
+ if (mb_wc_converted_length > 0) {
+ wc_mb_converted_length = (*wc_mb)(NULL, wc, encoded, encoded_end);
+ if (wc_mb_converted_length <= 0) {
+ break;
+ }
+ } else if (mb_wc_converted_length == MY_CS_ILSEQ) {
+ *encoded = *mysql_string;
+ mb_wc_converted_length = 1;
+ wc_mb_converted_length = 1;
+ } else {
+ break;
+ }
+ mysql_string += mb_wc_converted_length;
+ encoded += wc_mb_converted_length;
+ }
+ *encoded = '\0';
+ DBUG_PRINT("info", ("mroonga: out=%s", encoded_start));
+ DBUG_RETURN(encoded - encoded_start);
+ }
+}
diff --git a/storage/mroonga/lib/mrn_index_column_name.hpp b/storage/mroonga/lib/mrn_index_column_name.hpp
new file mode 100644
index 00000000000..5cd24623abd
--- /dev/null
+++ b/storage/mroonga/lib/mrn_index_column_name.hpp
@@ -0,0 +1,43 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2011-2013 Kentoku SHIBA
+ Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_INDEX_COLUMN_NAME_HPP_
+#define MRN_INDEX_COLUMN_NAME_HPP_
+
+#include <mrn_constants.hpp>
+
+namespace mrn {
+ class IndexColumnName {
+ public:
+ IndexColumnName(const char *table_name, const char *mysql_column_name);
+ const char *c_str();
+ size_t length();
+ private:
+ const char *table_name_;
+ const char *mysql_column_name_;
+ char name_[MRN_MAX_KEY_SIZE];
+ size_t length_;
+
+ uint encode(uchar *encoded_start, uchar *encoded_end,
+ const uchar *mysql_string_start, const uchar *mysql_string_end);
+ };
+}
+
+#endif /* MRN_INDEX_COLUMN_NAME_HPP_ */
diff --git a/storage/mroonga/lib/mrn_index_table_name.cpp b/storage/mroonga/lib/mrn_index_table_name.cpp
new file mode 100644
index 00000000000..93f4ff8f8fd
--- /dev/null
+++ b/storage/mroonga/lib/mrn_index_table_name.cpp
@@ -0,0 +1,89 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2011 Kentoku SHIBA
+ Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <mrn_mysql.h>
+
+#include "mrn_index_table_name.hpp"
+
+// for debug
+#define MRN_CLASS_NAME "mrn::IndexTableName"
+
+namespace mrn {
+ IndexTableName::IndexTableName(const char *table_name,
+ const char *mysql_index_name)
+ : table_name_(table_name),
+ mysql_index_name_(mysql_index_name) {
+ uchar encoded_mysql_index_name_multibyte[MRN_MAX_KEY_SIZE];
+ const uchar *mysql_index_name_multibyte =
+ reinterpret_cast<const uchar *>(mysql_index_name_);
+ encode(encoded_mysql_index_name_multibyte,
+ encoded_mysql_index_name_multibyte + MRN_MAX_KEY_SIZE,
+ mysql_index_name_multibyte,
+ mysql_index_name_multibyte + strlen(mysql_index_name_));
+ snprintf(name_, MRN_MAX_KEY_SIZE,
+ "%s-%s", table_name_, encoded_mysql_index_name_multibyte);
+ length_ = strlen(name_);
+ }
+
+ const char *IndexTableName::c_str() {
+ return name_;
+ }
+
+ size_t IndexTableName::length() {
+ return length_;
+ }
+
+ uint IndexTableName::encode(uchar *encoded_start,
+ uchar *encoded_end,
+ const uchar *mysql_string_start,
+ const uchar *mysql_string_end) {
+ MRN_DBUG_ENTER_METHOD();
+ my_charset_conv_mb_wc mb_wc = system_charset_info->cset->mb_wc;
+ my_charset_conv_wc_mb wc_mb = my_charset_filename.cset->wc_mb;
+ DBUG_PRINT("info", ("mroonga: in=%s", mysql_string_start));
+ encoded_end--;
+ uchar *encoded = encoded_start;
+ const uchar *mysql_string = mysql_string_start;
+ while (mysql_string < mysql_string_end && encoded < encoded_end) {
+ my_wc_t wc;
+ int mb_wc_converted_length;
+ int wc_mb_converted_length;
+ mb_wc_converted_length =
+ (*mb_wc)(NULL, &wc, mysql_string, mysql_string_end);
+ if (mb_wc_converted_length > 0) {
+ wc_mb_converted_length = (*wc_mb)(NULL, wc, encoded, encoded_end);
+ if (wc_mb_converted_length <= 0) {
+ break;
+ }
+ } else if (mb_wc_converted_length == MY_CS_ILSEQ) {
+ *encoded = *mysql_string;
+ mb_wc_converted_length = 1;
+ wc_mb_converted_length = 1;
+ } else {
+ break;
+ }
+ mysql_string += mb_wc_converted_length;
+ encoded += wc_mb_converted_length;
+ }
+ *encoded = '\0';
+ DBUG_PRINT("info", ("mroonga: out=%s", encoded_start));
+ DBUG_RETURN(encoded - encoded_start);
+ }
+}
diff --git a/storage/mroonga/lib/mrn_index_table_name.hpp b/storage/mroonga/lib/mrn_index_table_name.hpp
new file mode 100644
index 00000000000..4ac4bfe087b
--- /dev/null
+++ b/storage/mroonga/lib/mrn_index_table_name.hpp
@@ -0,0 +1,43 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2011 Kentoku SHIBA
+ Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_INDEX_TABLE_NAME_HPP_
+#define MRN_INDEX_TABLE_NAME_HPP_
+
+#include <mrn_constants.hpp>
+
+namespace mrn {
+ class IndexTableName {
+ public:
+ IndexTableName(const char *table_name, const char *mysql_index_name);
+ const char *c_str();
+ size_t length();
+ private:
+ const char *table_name_;
+ const char *mysql_index_name_;
+ char name_[MRN_MAX_KEY_SIZE];
+ size_t length_;
+
+ uint encode(uchar *encoded_start, uchar *encoded_end,
+ const uchar *mysql_string_start, const uchar *mysql_string_end);
+ };
+}
+
+#endif /* MRN_INDEX_TABLE_NAME_HPP_ */
diff --git a/storage/mroonga/lib/mrn_lock.cpp b/storage/mroonga/lib/mrn_lock.cpp
new file mode 100644
index 00000000000..94f8a4774af
--- /dev/null
+++ b/storage/mroonga/lib/mrn_lock.cpp
@@ -0,0 +1,31 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "mrn_lock.hpp"
+
+namespace mrn {
+ Lock::Lock(pthread_mutex_t *mutex)
+ : mutex_(mutex) {
+ pthread_mutex_lock(mutex_);
+ }
+
+ Lock::~Lock() {
+ pthread_mutex_unlock(mutex_);
+ }
+}
diff --git a/storage/mroonga/lib/mrn_lock.hpp b/storage/mroonga/lib/mrn_lock.hpp
new file mode 100644
index 00000000000..31dd7b3e53b
--- /dev/null
+++ b/storage/mroonga/lib/mrn_lock.hpp
@@ -0,0 +1,36 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_LOCK_HPP_
+#define MRN_LOCK_HPP_
+
+#include <mrn_mysql.h>
+#include <mrn_mysql_compat.h>
+
+namespace mrn {
+ class Lock {
+ public:
+ Lock(pthread_mutex_t *mutex);
+ ~Lock();
+ private:
+ pthread_mutex_t *mutex_;
+ };
+}
+
+#endif /* MRN_LOCK_HPP_ */
diff --git a/storage/mroonga/lib/mrn_match_escalation_threshold_scope.cpp b/storage/mroonga/lib/mrn_match_escalation_threshold_scope.cpp
new file mode 100644
index 00000000000..c944b4a4bc0
--- /dev/null
+++ b/storage/mroonga/lib/mrn_match_escalation_threshold_scope.cpp
@@ -0,0 +1,33 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "mrn_match_escalation_threshold_scope.hpp"
+
+namespace mrn {
+ MatchEscalationThresholdScope::MatchEscalationThresholdScope(
+ grn_ctx *ctx, long long int threshold)
+ : ctx_(ctx),
+ original_threshold_(grn_ctx_get_match_escalation_threshold(ctx_)) {
+ grn_ctx_set_match_escalation_threshold(ctx_, threshold);
+ }
+
+ MatchEscalationThresholdScope::~MatchEscalationThresholdScope() {
+ grn_ctx_set_match_escalation_threshold(ctx_, original_threshold_);
+ }
+}
diff --git a/storage/mroonga/lib/mrn_match_escalation_threshold_scope.hpp b/storage/mroonga/lib/mrn_match_escalation_threshold_scope.hpp
new file mode 100644
index 00000000000..352e6589f0d
--- /dev/null
+++ b/storage/mroonga/lib/mrn_match_escalation_threshold_scope.hpp
@@ -0,0 +1,35 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_MATCH_ESCALATION_THRESHOLD_SCOPE_HPP_
+#define MRN_MATCH_ESCALATION_THRESHOLD_SCOPE_HPP_
+
+#include <groonga.h>
+
+namespace mrn {
+ class MatchEscalationThresholdScope {
+ grn_ctx *ctx_;
+ long long int original_threshold_;
+ public:
+ MatchEscalationThresholdScope(grn_ctx *ctx, long long int threshold);
+ ~MatchEscalationThresholdScope();
+ };
+}
+
+#endif // MRN_MATCH_ESCALATION_THRESHOLD_SCOPE_HPP_
diff --git a/storage/mroonga/lib/mrn_multiple_column_key_codec.cpp b/storage/mroonga/lib/mrn_multiple_column_key_codec.cpp
new file mode 100644
index 00000000000..5ce736a49b8
--- /dev/null
+++ b/storage/mroonga/lib/mrn_multiple_column_key_codec.cpp
@@ -0,0 +1,548 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
+ Copyright(C) 2013 Kentoku SHIBA
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <mrn_mysql.h>
+
+#include "mrn_multiple_column_key_codec.hpp"
+#include "mrn_field_normalizer.hpp"
+#include "mrn_smart_grn_obj.hpp"
+
+// for debug
+#define MRN_CLASS_NAME "mrn::MultipleColumnKeyCodec"
+
+#ifdef WORDS_BIGENDIAN
+#define mrn_byte_order_host_to_network(buf, key, size) \
+{ \
+ uint32 size_ = (uint32)(size); \
+ uint8 *buf_ = (uint8 *)(buf); \
+ uint8 *key_ = (uint8 *)(key); \
+ while (size_--) { *buf_++ = *key_++; } \
+}
+#else /* WORDS_BIGENDIAN */
+#define mrn_byte_order_host_to_network(buf, key, size) \
+{ \
+ uint32 size_ = (uint32)(size); \
+ uint8 *buf_ = (uint8 *)(buf); \
+ uint8 *key_ = (uint8 *)(key) + size_; \
+ while (size_--) { *buf_++ = *(--key_); } \
+}
+#endif /* WORDS_BIGENDIAN */
+
+namespace mrn {
+ MultipleColumnKeyCodec::MultipleColumnKeyCodec(grn_ctx *ctx,
+ THD *thread,
+ KEY *key_info)
+ : ctx_(ctx),
+ thread_(thread),
+ key_info_(key_info) {
+ }
+
+ MultipleColumnKeyCodec::~MultipleColumnKeyCodec() {
+ }
+
+ int MultipleColumnKeyCodec::encode(const uchar *mysql_key,
+ uint mysql_key_length,
+ uchar *grn_key,
+ uint *grn_key_length) {
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ const uchar *current_mysql_key = mysql_key;
+ const uchar *mysql_key_end = mysql_key + mysql_key_length;
+ uchar *current_grn_key = grn_key;
+
+ int n_key_parts = KEY_N_KEY_PARTS(key_info_);
+ DBUG_PRINT("info", ("mroonga: n_key_parts=%d", n_key_parts));
+ *grn_key_length = 0;
+ for (int i = 0; i < n_key_parts && current_mysql_key < mysql_key_end; i++) {
+ KEY_PART_INFO *key_part = &(key_info_->key_part[i]);
+ Field *field = key_part->field;
+ DBUG_PRINT("info", ("mroonga: key_part->length=%u", key_part->length));
+
+ if (field->null_bit) {
+ DBUG_PRINT("info", ("mroonga: field has null bit"));
+ *current_grn_key = 0;
+ current_mysql_key += 1;
+ current_grn_key += 1;
+ (*grn_key_length)++;
+ }
+
+ DataType data_type = TYPE_UNKNOWN;
+ uint data_size = 0;
+ get_key_info(key_part, &data_type, &data_size);
+
+ switch (data_type) {
+ case TYPE_UNKNOWN:
+ // TODO: This will not be happen. This is just for
+ // suppressing warnings by gcc -O2. :<
+ error = HA_ERR_UNSUPPORTED;
+ break;
+ case TYPE_LONG_LONG_NUMBER:
+ {
+ long long int long_long_value = 0;
+ switch (data_size) {
+ case 3:
+ long_long_value = (long long int)sint3korr(current_mysql_key);
+ break;
+ case 8:
+ long_long_value = (long long int)sint8korr(current_mysql_key);
+ break;
+ }
+ mrn_byte_order_host_to_network(current_grn_key, &long_long_value,
+ data_size);
+ *((uint8 *)(current_grn_key)) ^= 0x80;
+ }
+ break;
+ case TYPE_NUMBER:
+ mrn_byte_order_host_to_network(current_grn_key, current_mysql_key, data_size);
+ {
+ Field_num *number_field = (Field_num *)field;
+ if (!number_field->unsigned_flag) {
+ *((uint8 *)(current_grn_key)) ^= 0x80;
+ }
+ }
+ break;
+ case TYPE_FLOAT:
+ {
+ float value;
+ float4get(value, current_mysql_key);
+ encode_float(value, data_size, current_grn_key);
+ }
+ break;
+ case TYPE_DOUBLE:
+ {
+ double value;
+ float8get(value, current_mysql_key);
+ encode_double(value, data_size, current_grn_key);
+ }
+ break;
+ case TYPE_BYTE_SEQUENCE:
+ memcpy(current_grn_key, current_mysql_key, data_size);
+ break;
+ case TYPE_BYTE_REVERSE:
+ encode_reverse(current_mysql_key, data_size, current_grn_key);
+ break;
+ case TYPE_BYTE_BLOB:
+ encode_blob(field, current_mysql_key, current_grn_key, &data_size);
+ break;
+ }
+
+ if (error) {
+ break;
+ }
+
+ current_mysql_key += data_size;
+ current_grn_key += data_size;
+ *grn_key_length += data_size;
+ }
+
+ DBUG_RETURN(error);
+ }
+
+ int MultipleColumnKeyCodec::decode(const uchar *grn_key,
+ uint grn_key_length,
+ uchar *mysql_key,
+ uint *mysql_key_length) {
+ MRN_DBUG_ENTER_METHOD();
+ int error = 0;
+ const uchar *current_grn_key = grn_key;
+ const uchar *grn_key_end = grn_key + grn_key_length;
+ uchar *current_mysql_key = mysql_key;
+
+ int n_key_parts = KEY_N_KEY_PARTS(key_info_);
+ DBUG_PRINT("info", ("mroonga: n_key_parts=%d", n_key_parts));
+ *mysql_key_length = 0;
+ for (int i = 0; i < n_key_parts && current_grn_key < grn_key_end; i++) {
+ KEY_PART_INFO *key_part = &(key_info_->key_part[i]);
+ Field *field = key_part->field;
+ DBUG_PRINT("info", ("mroonga: key_part->length=%u", key_part->length));
+
+ if (field->null_bit) {
+ DBUG_PRINT("info", ("mroonga: field has null bit"));
+ *current_mysql_key = 0;
+ current_grn_key += 1;
+ current_mysql_key += 1;
+ (*mysql_key_length)++;
+ }
+
+ DataType data_type = TYPE_UNKNOWN;
+ uint data_size = 0;
+ get_key_info(key_part, &data_type, &data_size);
+
+ switch (data_type) {
+ case TYPE_UNKNOWN:
+ // TODO: This will not be happen. This is just for
+ // suppressing warnings by gcc -O2. :<
+ error = HA_ERR_UNSUPPORTED;
+ break;
+ case TYPE_LONG_LONG_NUMBER:
+ {
+ long long int long_long_value = 0;
+ switch (data_size) {
+ case 3:
+ long_long_value = (long long int)sint3korr(current_grn_key);
+ break;
+ case 8:
+ long_long_value = (long long int)sint8korr(current_grn_key);
+ break;
+ }
+ *((uint8 *)(&long_long_value)) ^= 0x80;
+ mrn_byte_order_host_to_network(current_mysql_key, &long_long_value,
+ data_size);
+ }
+ break;
+ case TYPE_NUMBER:
+ {
+ uchar buffer[8];
+ memcpy(buffer, current_grn_key, data_size);
+ Field_num *number_field = (Field_num *)field;
+ if (!number_field->unsigned_flag) {
+ buffer[0] ^= 0x80;
+ }
+ mrn_byte_order_host_to_network(current_mysql_key, buffer,
+ data_size);
+ }
+ break;
+ case TYPE_FLOAT:
+ decode_float(current_grn_key, current_mysql_key, data_size);
+ break;
+ case TYPE_DOUBLE:
+ decode_double(current_grn_key, current_mysql_key, data_size);
+ break;
+ case TYPE_BYTE_SEQUENCE:
+ memcpy(current_mysql_key, current_grn_key, data_size);
+ break;
+ case TYPE_BYTE_REVERSE:
+ decode_reverse(current_grn_key, current_mysql_key, data_size);
+ break;
+ case TYPE_BYTE_BLOB:
+ memcpy(current_mysql_key,
+ current_grn_key + data_size,
+ HA_KEY_BLOB_LENGTH);
+ memcpy(current_mysql_key + HA_KEY_BLOB_LENGTH,
+ current_grn_key,
+ data_size);
+ data_size += HA_KEY_BLOB_LENGTH;
+ break;
+ }
+
+ if (error) {
+ break;
+ }
+
+ current_grn_key += data_size;
+ current_mysql_key += data_size;
+ *mysql_key_length += data_size;
+ }
+
+ DBUG_RETURN(error);
+ }
+
+ uint MultipleColumnKeyCodec::size() {
+ MRN_DBUG_ENTER_METHOD();
+
+ int n_key_parts = KEY_N_KEY_PARTS(key_info_);
+ DBUG_PRINT("info", ("mroonga: n_key_parts=%d", n_key_parts));
+
+ uint total_size = 0;
+ for (int i = 0; i < n_key_parts; ++i) {
+ KEY_PART_INFO *key_part = &(key_info_->key_part[i]);
+ Field *field = key_part->field;
+ DBUG_PRINT("info", ("mroonga: key_part->length=%u", key_part->length));
+
+ if (field->null_bit) {
+ DBUG_PRINT("info", ("mroonga: field has null bit"));
+ ++total_size;
+ }
+
+ DataType data_type = TYPE_UNKNOWN;
+ uint data_size = 0;
+ get_key_info(key_part, &data_type, &data_size);
+ total_size += data_size;
+ if (data_type == TYPE_BYTE_BLOB) {
+ total_size += HA_KEY_BLOB_LENGTH;
+ }
+ }
+
+ DBUG_RETURN(total_size);
+ }
+
+ void MultipleColumnKeyCodec::get_key_info(KEY_PART_INFO *key_part,
+ DataType *data_type,
+ uint *data_size) {
+ MRN_DBUG_ENTER_METHOD();
+
+ *data_type = TYPE_UNKNOWN;
+ *data_size = 0;
+
+ Field *field = key_part->field;
+ switch (field->real_type()) {
+ case MYSQL_TYPE_DECIMAL:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_DECIMAL"));
+ *data_type = TYPE_BYTE_SEQUENCE;
+ *data_size = key_part->length;
+ break;
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_YEAR:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_TINY"));
+ *data_type = TYPE_NUMBER;
+ *data_size = 1;
+ break;
+ case MYSQL_TYPE_SHORT:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_SHORT"));
+ *data_type = TYPE_NUMBER;
+ *data_size = 2;
+ break;
+ case MYSQL_TYPE_LONG:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_LONG"));
+ *data_type = TYPE_NUMBER;
+ *data_size = 4;
+ break;
+ case MYSQL_TYPE_FLOAT:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_FLOAT"));
+ *data_type = TYPE_FLOAT;
+ *data_size = 4;
+ break;
+ case MYSQL_TYPE_DOUBLE:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_DOUBLE"));
+ *data_type = TYPE_DOUBLE;
+ *data_size = 8;
+ break;
+ case MYSQL_TYPE_NULL:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_NULL"));
+ *data_type = TYPE_NUMBER;
+ *data_size = 1;
+ break;
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_NEWDATE:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_DATETIME"));
+ *data_type = TYPE_BYTE_REVERSE;
+ *data_size = key_part->length;
+ break;
+ case MYSQL_TYPE_LONGLONG:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_LONGLONG"));
+ *data_type = TYPE_NUMBER;
+ *data_size = 8;
+ break;
+ case MYSQL_TYPE_INT24:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_INT24"));
+ *data_type = TYPE_NUMBER;
+ *data_size = 3;
+ break;
+ case MYSQL_TYPE_TIME:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_TIME"));
+ *data_type = TYPE_LONG_LONG_NUMBER;
+ *data_size = 3;
+ break;
+ case MYSQL_TYPE_VARCHAR:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_VARCHAR"));
+ *data_type = TYPE_BYTE_BLOB;
+ *data_size = key_part->length;
+ break;
+ case MYSQL_TYPE_BIT:
+ // TODO
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_BIT"));
+ *data_type = TYPE_NUMBER;
+ *data_size = 1;
+ break;
+#ifdef MRN_HAVE_MYSQL_TYPE_TIMESTAMP2
+ case MYSQL_TYPE_TIMESTAMP2:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_TIMESTAMP2"));
+ *data_type = TYPE_BYTE_SEQUENCE;
+ *data_size = key_part->length;
+ break;
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_DATETIME2
+ case MYSQL_TYPE_DATETIME2:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_DATETIME2"));
+ *data_type = TYPE_BYTE_SEQUENCE;
+ *data_size = key_part->length;
+ break;
+#endif
+#ifdef MRN_HAVE_MYSQL_TYPE_TIME2
+ case MYSQL_TYPE_TIME2:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_TIME2"));
+ *data_type = TYPE_BYTE_SEQUENCE;
+ *data_size = key_part->length;
+ break;
+#endif
+ case MYSQL_TYPE_NEWDECIMAL:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_NEWDECIMAL"));
+ *data_type = TYPE_BYTE_SEQUENCE;
+ *data_size = key_part->length;
+ break;
+ case MYSQL_TYPE_ENUM:
+ // TODO
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_ENUM"));
+ *data_type = TYPE_NUMBER;
+ *data_size = 1;
+ break;
+ case MYSQL_TYPE_SET:
+ // TODO
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_SET"));
+ *data_type = TYPE_NUMBER;
+ *data_size = 1;
+ break;
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ // TODO
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_BLOB"));
+ *data_type = TYPE_BYTE_BLOB;
+ *data_size = key_part->length;
+ break;
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ // TODO
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_STRING"));
+ *data_type = TYPE_BYTE_SEQUENCE;
+ *data_size = key_part->length;
+ break;
+ case MYSQL_TYPE_GEOMETRY:
+ // TODO
+ DBUG_PRINT("info", ("mroonga: MYSQL_TYPE_GEOMETRY"));
+ *data_type = TYPE_BYTE_SEQUENCE;
+ *data_size = key_part->length;
+ break;
+ }
+ DBUG_VOID_RETURN;
+ }
+
+ void MultipleColumnKeyCodec::encode_float(volatile float value, uint data_size,
+ uchar *grn_key) {
+ MRN_DBUG_ENTER_METHOD();
+ int n_bits = (data_size * 8 - 1);
+ volatile int *int_value_pointer = (int *)(&value);
+ int int_value = *int_value_pointer;
+ int_value ^= ((int_value >> n_bits) | (1 << n_bits));
+ mrn_byte_order_host_to_network(grn_key, &int_value, data_size);
+ DBUG_VOID_RETURN;
+ }
+
+ void MultipleColumnKeyCodec::decode_float(const uchar *grn_key,
+ uchar *mysql_key,
+ uint data_size) {
+ MRN_DBUG_ENTER_METHOD();
+ int int_value;
+ mrn_byte_order_host_to_network(&int_value, grn_key, data_size);
+ int max_bit = (data_size * 8 - 1);
+ *((int *)mysql_key) =
+ int_value ^ (((int_value ^ (1 << max_bit)) >> max_bit) |
+ (1 << max_bit));
+ DBUG_VOID_RETURN;
+ }
+
+ void MultipleColumnKeyCodec::encode_double(volatile double value, uint data_size,
+ uchar *grn_key) {
+ MRN_DBUG_ENTER_METHOD();
+ int n_bits = (data_size * 8 - 1);
+ volatile long long int *long_long_value_pointer = (long long int *)(&value);
+ volatile long long int long_long_value = *long_long_value_pointer;
+ long_long_value ^= ((long_long_value >> n_bits) | (1LL << n_bits));
+ mrn_byte_order_host_to_network(grn_key, &long_long_value, data_size);
+ DBUG_VOID_RETURN;
+ }
+
+ void MultipleColumnKeyCodec::decode_double(const uchar *grn_key,
+ uchar *mysql_key,
+ uint data_size) {
+ MRN_DBUG_ENTER_METHOD();
+ long long int long_long_value;
+ mrn_byte_order_host_to_network(&long_long_value, grn_key, data_size);
+ int max_bit = (data_size * 8 - 1);
+ *((long long int *)mysql_key) =
+ long_long_value ^ (((long_long_value ^ (1LL << max_bit)) >> max_bit) |
+ (1LL << max_bit));
+ DBUG_VOID_RETURN;
+ }
+
+ void MultipleColumnKeyCodec::encode_reverse(const uchar *mysql_key, uint data_size,
+ uchar *grn_key) {
+ MRN_DBUG_ENTER_METHOD();
+ for (uint i = 0; i < data_size; i++) {
+ grn_key[i] = mysql_key[data_size - i - 1];
+ }
+ DBUG_VOID_RETURN;
+ }
+
+ void MultipleColumnKeyCodec::decode_reverse(const uchar *grn_key,
+ uchar *mysql_key,
+ uint data_size) {
+ MRN_DBUG_ENTER_METHOD();
+ for (uint i = 0; i < data_size; i++) {
+ mysql_key[i] = grn_key[data_size - i - 1];
+ }
+ DBUG_VOID_RETURN;
+ }
+
+ void MultipleColumnKeyCodec::encode_blob(Field *field,
+ const uchar *mysql_key,
+ uchar *grn_key,
+ uint *data_size) {
+ FieldNormalizer normalizer(ctx_, thread_, field);
+ if (normalizer.should_normalize()) {
+#if HA_KEY_BLOB_LENGTH != 2
+# error "TODO: support HA_KEY_BLOB_LENGTH != 2 case if it is needed"
+#endif
+ const char *blob_data =
+ reinterpret_cast<const char *>(mysql_key + HA_KEY_BLOB_LENGTH);
+ uint16 blob_data_length = *((uint16 *)(mysql_key));
+ grn_obj *grn_string = normalizer.normalize(blob_data,
+ blob_data_length);
+ mrn::SmartGrnObj smart_grn_string(ctx_, grn_string);
+ const char *normalized;
+ unsigned int normalized_length = 0;
+ grn_string_get_normalized(ctx_, grn_string,
+ &normalized, &normalized_length, NULL);
+ uint16 new_blob_data_length;
+ if (normalized_length <= UINT_MAX16) {
+ memcpy(grn_key, normalized, normalized_length);
+ if (normalized_length < *data_size) {
+ memset(grn_key + normalized_length,
+ '\0', *data_size - normalized_length);
+ }
+ new_blob_data_length = normalized_length;
+ } else {
+ push_warning_printf(thread_,
+ Sql_condition::WARN_LEVEL_WARN,
+ WARN_DATA_TRUNCATED,
+ "normalized data truncated "
+ "for multiple column index: "
+ "normalized-data-size: <%u> "
+ "max-data-size: <%u> "
+ "column-name: <%s> "
+ "data: <%.*s>",
+ normalized_length,
+ UINT_MAX16,
+ field->field_name,
+ blob_data_length, blob_data);
+ memcpy(grn_key, normalized, blob_data_length);
+ new_blob_data_length = blob_data_length;
+ }
+ memcpy(grn_key + *data_size, &new_blob_data_length, HA_KEY_BLOB_LENGTH);
+ } else {
+ memcpy(grn_key + *data_size, mysql_key, HA_KEY_BLOB_LENGTH);
+ memcpy(grn_key, mysql_key + HA_KEY_BLOB_LENGTH, *data_size);
+ }
+ *data_size += HA_KEY_BLOB_LENGTH;
+ }
+}
diff --git a/storage/mroonga/lib/mrn_multiple_column_key_codec.hpp b/storage/mroonga/lib/mrn_multiple_column_key_codec.hpp
new file mode 100644
index 00000000000..fc6ae285357
--- /dev/null
+++ b/storage/mroonga/lib/mrn_multiple_column_key_codec.hpp
@@ -0,0 +1,70 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_MULTIPLE_COLUMN_KEY_CODEC_HPP_
+#define MRN_MULTIPLE_COLUMN_KEY_CODEC_HPP_
+
+#include <groonga.h>
+
+#include <mrn_mysql.h>
+#include <mrn_mysql_compat.h>
+
+namespace mrn {
+ class MultipleColumnKeyCodec {
+ public:
+ MultipleColumnKeyCodec(grn_ctx *ctx, THD *thread, KEY *key_info);
+ ~MultipleColumnKeyCodec();
+
+ int encode(const uchar *mysql_key, uint mysql_key_length,
+ uchar *grn_key, uint *grn_key_length);
+ int decode(const uchar *grn_key, uint grn_key_length,
+ uchar *mysql_key, uint *mysql_key_length);
+ uint size();
+
+ private:
+ enum DataType {
+ TYPE_UNKNOWN,
+ TYPE_LONG_LONG_NUMBER,
+ TYPE_NUMBER,
+ TYPE_FLOAT,
+ TYPE_DOUBLE,
+ TYPE_BYTE_SEQUENCE,
+ TYPE_BYTE_REVERSE,
+ TYPE_BYTE_BLOB
+ };
+
+ grn_ctx *ctx_;
+ THD *thread_;
+ KEY *key_info_;
+
+ void get_key_info(KEY_PART_INFO *key_part,
+ DataType *data_type, uint *data_size);
+
+ void encode_float(volatile float value, uint data_size, uchar *grn_key);
+ void decode_float(const uchar *grn_key, uchar *mysql_key, uint data_size);
+ void encode_double(volatile double value, uint data_size, uchar *grn_key);
+ void decode_double(const uchar *grn_key, uchar *mysql_key, uint data_size);
+ void encode_reverse(const uchar *mysql_key, uint data_size, uchar *grn_key);
+ void decode_reverse(const uchar *grn_key, uchar *mysql_key, uint data_size);
+ void encode_blob(Field *field,
+ const uchar *mysql_key, uchar *grn_key, uint *data_size);
+ };
+}
+
+#endif // MRN_MULTIPLE_COLUMN_KEY_CODEC_HPP_
diff --git a/storage/mroonga/lib/mrn_mysqlservices.cpp b/storage/mroonga/lib/mrn_mysqlservices.cpp
new file mode 100644
index 00000000000..9c7af9acfe0
--- /dev/null
+++ b/storage/mroonga/lib/mrn_mysqlservices.cpp
@@ -0,0 +1,32 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <mrn_mysql.h>
+#include <mrn_mysql_compat.h>
+
+/*
+void *thd_alloc(MYSQL_THD thd, unsigned int size)
+{
+ return thd->alloc(size);
+}
+*/
diff --git a/storage/mroonga/lib/mrn_parameters_parser.cpp b/storage/mroonga/lib/mrn_parameters_parser.cpp
new file mode 100644
index 00000000000..9a05097e548
--- /dev/null
+++ b/storage/mroonga/lib/mrn_parameters_parser.cpp
@@ -0,0 +1,176 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2011-2013 Kentoku SHIBA
+ Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "mrn_parameters_parser.hpp"
+
+#include <mrn_mysql_compat.h>
+
+namespace mrn {
+ class Parameter {
+ public:
+ char *key_;
+ char *value_;
+
+ Parameter(const char *key, unsigned int key_length,
+ const char *value, unsigned int value_length)
+ : key_(my_strndup(key, key_length, MYF(0))),
+ value_(my_strndup(value, value_length, MYF(0))) {
+ };
+ ~Parameter() {
+ if (key_) {
+ my_free(key_, MYF(0));
+ }
+ if (value_) {
+ my_free(value_, MYF(0));
+ }
+ };
+ };
+
+ ParametersParser::ParametersParser(const char *input,
+ unsigned int input_length)
+ : input_(input),
+ input_length_(input_length),
+ parameters_(NULL) {
+ }
+
+ ParametersParser::~ParametersParser() {
+ for (LIST *next = parameters_; next; next = next->next) {
+ Parameter *parameter = static_cast<Parameter *>(next->data);
+ delete parameter;
+ }
+ list_free(parameters_, false);
+ }
+
+ void ParametersParser::parse() {
+ const char *current = input_;
+ const char *end = input_ + input_length_;
+ for (; current < end; ++current) {
+ if (is_white_space(current[0])) {
+ continue;
+ }
+
+ const char *key = current;
+ unsigned int key_length = 0;
+ while (current < end &&
+ !is_white_space(current[0]) &&
+ current[0] != '\'' && current[0] != '"' && current[0] != ',') {
+ ++current;
+ ++key_length;
+ }
+ if (current == end) {
+ break;
+ }
+
+ while (current < end && is_white_space(current[0])) {
+ ++current;
+ }
+ if (current == end) {
+ break;
+ }
+ current = parse_value(current, end, key, key_length);
+ if (!current) {
+ break;
+ }
+
+ while (current < end && is_white_space(current[0])) {
+ ++current;
+ }
+ if (current == end) {
+ break;
+ }
+ if (current[0] != ',') {
+ // TODO: report error
+ break;
+ }
+ }
+ }
+
+ const char *ParametersParser::parse_value(const char *current,
+ const char *end,
+ const char *key,
+ unsigned int key_length) {
+ char quote = current[0];
+ if (quote != '\'' && quote != '"') {
+ // TODO: report error
+ return NULL;
+ }
+ ++current;
+
+ bool found = false;
+ static const unsigned int max_value_length = 4096;
+ char value[max_value_length];
+ unsigned int value_length = 0;
+ for (; current < end && value_length < max_value_length; ++current) {
+ if (current[0] == quote) {
+ Parameter *parameter = new Parameter(key, key_length,
+ value, value_length);
+ list_push(parameters_, parameter);
+ found = true;
+ ++current;
+ break;
+ }
+
+ switch (current[0]) {
+ case '\\':
+ if (current + 1 == end) {
+ break;
+ }
+ switch (current[1]) {
+ case 'b':
+ value[value_length] = '\b';
+ break;
+ case 'n':
+ value[value_length] = '\n';
+ break;
+ case 'r':
+ value[value_length] = '\r';
+ break;
+ case 't':
+ value[value_length] = '\t';
+ break;
+ default:
+ value[value_length] = current[1];
+ break;
+ }
+ break;
+ default:
+ value[value_length] = current[0];
+ break;
+ }
+ ++value_length;
+ }
+
+ if (!found) {
+ // TODO: report error
+ }
+
+ return current;
+ }
+
+ const char *ParametersParser::operator[](const char *key) {
+ for (LIST *next = parameters_; next; next = next->next) {
+ Parameter *parameter = static_cast<Parameter *>(next->data);
+ if (strcasecmp(parameter->key_, key) == 0) {
+ return parameter->value_;
+ }
+ }
+ return NULL;
+ }
+}
diff --git a/storage/mroonga/lib/mrn_parameters_parser.hpp b/storage/mroonga/lib/mrn_parameters_parser.hpp
new file mode 100644
index 00000000000..a15371ca72e
--- /dev/null
+++ b/storage/mroonga/lib/mrn_parameters_parser.hpp
@@ -0,0 +1,59 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2011-2013 Kentoku SHIBA
+ Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_PARAMETERS_PARSER_HPP_
+#define MRN_PARAMETERS_PARSER_HPP_
+
+#include <mrn_mysql.h>
+#include <my_list.h>
+
+namespace mrn {
+ class ParametersParser {
+ public:
+ ParametersParser(const char *input, unsigned int input_length);
+ ~ParametersParser();
+ void parse();
+ const char *operator[](const char *key);
+
+ private:
+ const char *input_;
+ unsigned int input_length_;
+
+ LIST *parameters_;
+
+ bool is_white_space(char character) {
+ switch (character) {
+ case ' ':
+ case '\r':
+ case '\n':
+ case '\t':
+ return true;
+ break;
+ default:
+ return false;
+ break;
+ }
+ };
+ const char *parse_value(const char *current, const char *end,
+ const char *key, unsigned int key_length);
+ };
+}
+
+#endif /* MRN_PARAMETERS_PARSER_HPP_ */
diff --git a/storage/mroonga/lib/mrn_path_mapper.cpp b/storage/mroonga/lib/mrn_path_mapper.cpp
new file mode 100644
index 00000000000..8856e6f6082
--- /dev/null
+++ b/storage/mroonga/lib/mrn_path_mapper.cpp
@@ -0,0 +1,196 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2010 Tetsuro IKEDA
+ Copyright(C) 2011-2013 Kentoku SHIBA
+ Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "mrn_path_mapper.hpp"
+
+#include <string.h>
+
+#include <mrn_mysql.h>
+
+namespace mrn {
+ char *PathMapper::default_path_prefix = NULL;
+ char *PathMapper::default_mysql_data_home_path = NULL;
+
+ PathMapper::PathMapper(const char *mysql_path,
+ const char *path_prefix,
+ const char *mysql_data_home_path)
+ : mysql_path_(mysql_path),
+ path_prefix_(path_prefix),
+ mysql_data_home_path_(mysql_data_home_path) {
+ db_path_[0] = '\0';
+ db_name_[0] = '\0';
+ table_name_[0] = '\0';
+ mysql_table_name_[0] = '\0';
+ }
+
+ /**
+ * "./${db}/${table}" ==> "${db}.mrn"
+ * "./${db}/" ==> "${db}.mrn"
+ * "/tmp/mysql-test/var/tmp/mysqld.1/#sql27c5_1_0" ==>
+ * "/tmp/mysql-test/var/tmp/mysqld.1/#sql27c5_1_0.mrn"
+ */
+ const char *PathMapper::db_path() {
+ if (db_path_[0] != '\0') {
+ return db_path_;
+ }
+
+ if (mysql_path_[0] == FN_CURLIB && mysql_path_[1] == FN_LIBCHAR) {
+ if (path_prefix_) {
+ strcpy(db_path_, path_prefix_);
+ }
+
+ int i = 2, j = strlen(db_path_), len;
+ len = strlen(mysql_path_);
+ while (mysql_path_[i] != FN_LIBCHAR && i < len) {
+ db_path_[j++] = mysql_path_[i++];
+ }
+ db_path_[j] = '\0';
+ } else if (mysql_data_home_path_) {
+ int len = strlen(mysql_path_);
+ int mysql_data_home_len = strlen(mysql_data_home_path_);
+ if (len > mysql_data_home_len &&
+ !strncmp(mysql_path_, mysql_data_home_path_, mysql_data_home_len)) {
+ int i = mysql_data_home_len, j;
+ if (path_prefix_ && path_prefix_[0] == FN_LIBCHAR) {
+ strcpy(db_path_, path_prefix_);
+ j = strlen(db_path_);
+ } else {
+ memcpy(db_path_, mysql_data_home_path_, mysql_data_home_len);
+ if (path_prefix_) {
+ if (path_prefix_[0] == FN_CURLIB &&
+ path_prefix_[1] == FN_LIBCHAR) {
+ strcpy(&db_path_[mysql_data_home_len], &path_prefix_[2]);
+ } else {
+ strcpy(&db_path_[mysql_data_home_len], path_prefix_);
+ }
+ j = strlen(db_path_);
+ } else {
+ j = mysql_data_home_len;
+ }
+ }
+
+ while (mysql_path_[i] != FN_LIBCHAR && i < len) {
+ db_path_[j++] = mysql_path_[i++];
+ }
+ if (i == len) {
+ memcpy(db_path_, mysql_path_, len);
+ } else {
+ db_path_[j] = '\0';
+ }
+ } else {
+ strcpy(db_path_, mysql_path_);
+ }
+ } else {
+ strcpy(db_path_, mysql_path_);
+ }
+ strcat(db_path_, MRN_DB_FILE_SUFFIX);
+ return db_path_;
+ }
+
+ /**
+ * "./${db}/${table}" ==> "${db}"
+ * "./${db}/" ==> "${db}"
+ * "/tmp/mysql-test/var/tmp/mysqld.1/#sql27c5_1_0" ==>
+ * "/tmp/mysql-test/var/tmp/mysqld.1/#sql27c5_1_0"
+ */
+ const char *PathMapper::db_name() {
+ if (db_name_[0] != '\0') {
+ return db_name_;
+ }
+
+ if (mysql_path_[0] == FN_CURLIB && mysql_path_[1] == FN_LIBCHAR) {
+ int i = 2, j = 0, len;
+ len = strlen(mysql_path_);
+ while (mysql_path_[i] != FN_LIBCHAR && i < len) {
+ db_name_[j++] = mysql_path_[i++];
+ }
+ db_name_[j] = '\0';
+ } else if (mysql_data_home_path_) {
+ int len = strlen(mysql_path_);
+ int mysql_data_home_len = strlen(mysql_data_home_path_);
+ if (len > mysql_data_home_len &&
+ !strncmp(mysql_path_, mysql_data_home_path_, mysql_data_home_len)) {
+ int i = mysql_data_home_len, j = 0;
+ while (mysql_path_[i] != FN_LIBCHAR && i < len) {
+ db_name_[j++] = mysql_path_[i++];
+ }
+ if (i == len) {
+ memcpy(db_name_, mysql_path_, len);
+ } else {
+ db_name_[j] = '\0';
+ }
+ } else {
+ strcpy(db_name_, mysql_path_);
+ }
+ } else {
+ strcpy(db_name_, mysql_path_);
+ }
+ return db_name_;
+ }
+
+ /**
+ * "./${db}/${table}" ==> "${table}" (with encoding first '_')
+ */
+ const char *PathMapper::table_name() {
+ if (table_name_[0] != '\0') {
+ return table_name_;
+ }
+
+ int len = strlen(mysql_path_);
+ int i = len, j = 0;
+ for (; mysql_path_[--i] != FN_LIBCHAR ;) {}
+ if (mysql_path_[i + 1] == '_') {
+ table_name_[j++] = '@';
+ table_name_[j++] = '0';
+ table_name_[j++] = '0';
+ table_name_[j++] = '5';
+ table_name_[j++] = 'f';
+ i++;
+ }
+ for (; i < len ;) {
+ table_name_[j++] = mysql_path_[++i];
+ }
+ table_name_[j] = '\0';
+ return table_name_;
+ }
+
+ /**
+ * "./${db}/${table}" ==> "${table}" (without encoding first '_')
+ */
+ const char *PathMapper::mysql_table_name() {
+ if (mysql_table_name_[0] != '\0') {
+ return mysql_table_name_;
+ }
+
+ int len = strlen(mysql_path_);
+ int i = len, j = 0;
+ for (; mysql_path_[--i] != FN_LIBCHAR ;) {}
+ for (; i < len ;) {
+ mysql_table_name_[j++] = mysql_path_[++i];
+ }
+ mysql_table_name_[j] = '\0';
+ return mysql_table_name_;
+ }
+}
diff --git a/storage/mroonga/lib/mrn_path_mapper.hpp b/storage/mroonga/lib/mrn_path_mapper.hpp
new file mode 100644
index 00000000000..f70cd7b5587
--- /dev/null
+++ b/storage/mroonga/lib/mrn_path_mapper.hpp
@@ -0,0 +1,51 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2010 Tetsuro IKEDA
+ Copyright(C) 2010-2013 Kentoku SHIBA
+ Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_PATH_MAPPER_HPP_
+#define MRN_PATH_MAPPER_HPP_
+
+#include <mrn_constants.hpp>
+
+namespace mrn {
+ class PathMapper {
+ public:
+ static char *default_path_prefix;
+ static char *default_mysql_data_home_path;
+
+ PathMapper(const char *mysql_path,
+ const char *path_prefix=default_path_prefix,
+ const char *mysql_data_home_path=default_mysql_data_home_path);
+ const char *db_path();
+ const char *db_name();
+ const char *table_name();
+ const char *mysql_table_name();
+ private:
+ const char *mysql_path_;
+ const char *path_prefix_;
+ const char *mysql_data_home_path_;
+ char db_path_[MRN_MAX_PATH_SIZE];
+ char db_name_[MRN_MAX_PATH_SIZE];
+ char table_name_[MRN_MAX_PATH_SIZE];
+ char mysql_table_name_[MRN_MAX_PATH_SIZE];
+ };
+}
+
+#endif /* MRN_PATH_MAPPER_HPP_ */
diff --git a/storage/mroonga/lib/mrn_smart_grn_obj.cpp b/storage/mroonga/lib/mrn_smart_grn_obj.cpp
new file mode 100644
index 00000000000..9dbae6528f9
--- /dev/null
+++ b/storage/mroonga/lib/mrn_smart_grn_obj.cpp
@@ -0,0 +1,53 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <string.h>
+
+#include "mrn_smart_grn_obj.hpp"
+
+namespace mrn {
+ SmartGrnObj::SmartGrnObj(grn_ctx *ctx, grn_obj *obj)
+ : ctx_(ctx),
+ obj_(obj) {
+ }
+
+ SmartGrnObj::SmartGrnObj(grn_ctx *ctx, const char *name, int name_size)
+ : ctx_(ctx),
+ obj_(NULL) {
+ if (name_size < 0) {
+ name_size = strlen(name);
+ }
+ obj_ = grn_ctx_get(ctx_, name, name_size);
+ }
+
+ SmartGrnObj::SmartGrnObj(grn_ctx *ctx, grn_id id)
+ : ctx_(ctx),
+ obj_(grn_ctx_at(ctx_, id)) {
+ }
+
+ SmartGrnObj::~SmartGrnObj() {
+ if (obj_) {
+ grn_obj_unlink(ctx_, obj_);
+ }
+ }
+
+ grn_obj *SmartGrnObj::get() {
+ return obj_;
+ }
+}
diff --git a/storage/mroonga/lib/mrn_smart_grn_obj.hpp b/storage/mroonga/lib/mrn_smart_grn_obj.hpp
new file mode 100644
index 00000000000..c9c86f3e46e
--- /dev/null
+++ b/storage/mroonga/lib/mrn_smart_grn_obj.hpp
@@ -0,0 +1,39 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_SMART_GRN_OBJ_HPP_
+#define MRN_SMART_GRN_OBJ_HPP_
+
+#include <groonga.h>
+
+namespace mrn {
+ class SmartGrnObj {
+ grn_ctx *ctx_;
+ grn_obj *obj_;
+ public:
+ SmartGrnObj(grn_ctx *ctx, grn_obj *obj);
+ SmartGrnObj(grn_ctx *ctx, const char *name, int name_size=-1);
+ SmartGrnObj(grn_ctx *ctx, grn_id id);
+ ~SmartGrnObj();
+
+ grn_obj *get();
+ };
+}
+
+#endif // MRN_SMART_GRN_OBJ_HPP_
diff --git a/storage/mroonga/lib/mrn_time_converter.cpp b/storage/mroonga/lib/mrn_time_converter.cpp
new file mode 100644
index 00000000000..63b2e53766d
--- /dev/null
+++ b/storage/mroonga/lib/mrn_time_converter.cpp
@@ -0,0 +1,260 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2010-2013 Kentoku SHIBA
+ Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "mrn_time_converter.hpp"
+
+#ifdef min
+# undef min
+#endif
+#ifdef max
+# undef max
+#endif
+
+#include <limits>
+
+// for debug
+#define MRN_CLASS_NAME "mrn::TimeConverter"
+
+namespace mrn {
+ TimeConverter::TimeConverter() {
+ }
+
+ TimeConverter::~TimeConverter() {
+ }
+
+ time_t TimeConverter::tm_to_time_gm(struct tm *time, bool *truncated) {
+ MRN_DBUG_ENTER_METHOD();
+ *truncated = true;
+ struct tm gmdate;
+ time->tm_yday = -1;
+ time->tm_isdst = -1;
+ time_t sec_t = mktime(time);
+ if (time->tm_yday == -1) {
+ DBUG_RETURN(-1);
+ }
+ if (!gmtime_r(&sec_t, &gmdate)) {
+ DBUG_RETURN(-1);
+ }
+ int32 mrn_utc_diff_in_seconds =
+ (
+ time->tm_mday > 25 && gmdate.tm_mday == 1 ? -1 :
+ time->tm_mday == 1 && gmdate.tm_mday > 25 ? 1 :
+ time->tm_mday - gmdate.tm_mday
+ ) * 24 * 60 * 60 +
+ (time->tm_hour - gmdate.tm_hour) * 60 * 60 +
+ (time->tm_min - gmdate.tm_min) * 60 +
+ (time->tm_sec - gmdate.tm_sec);
+ DBUG_PRINT("info", ("mroonga: time->tm_year=%d", time->tm_year));
+ DBUG_PRINT("info", ("mroonga: time->tm_mon=%d", time->tm_mon));
+ DBUG_PRINT("info", ("mroonga: time->tm_mday=%d", time->tm_mday));
+ DBUG_PRINT("info", ("mroonga: time->tm_hour=%d", time->tm_hour));
+ DBUG_PRINT("info", ("mroonga: time->tm_min=%d", time->tm_min));
+ DBUG_PRINT("info", ("mroonga: time->tm_sec=%d", time->tm_sec));
+ DBUG_PRINT("info", ("mroonga: mrn_utc_diff_in_seconds=%d",
+ mrn_utc_diff_in_seconds));
+ if (mrn_utc_diff_in_seconds > 0) {
+ if (sec_t > std::numeric_limits<time_t>::max() - mrn_utc_diff_in_seconds) {
+ DBUG_RETURN(-1);
+ }
+ } else {
+ if (sec_t < std::numeric_limits<time_t>::min() - mrn_utc_diff_in_seconds) {
+ DBUG_RETURN(-1);
+ }
+ }
+ *truncated = false;
+ DBUG_RETURN(sec_t + mrn_utc_diff_in_seconds);
+ }
+
+ long long int TimeConverter::tm_to_grn_time(struct tm *time, int usec,
+ bool *truncated) {
+ MRN_DBUG_ENTER_METHOD();
+
+ long long int sec = tm_to_time_gm(time, truncated);
+
+ DBUG_PRINT("info", ("mroonga: sec=%lld", sec));
+ DBUG_PRINT("info", ("mroonga: usec=%d", usec));
+
+ long long int grn_time = *truncated ? 0 : GRN_TIME_PACK(sec, usec);
+
+ DBUG_RETURN(grn_time);
+ }
+
+ long long int TimeConverter::mysql_time_to_grn_time(MYSQL_TIME *mysql_time,
+ bool *truncated) {
+ MRN_DBUG_ENTER_METHOD();
+
+ int usec = mysql_time->second_part;
+ long long int grn_time = 0;
+
+ *truncated = false;
+ switch (mysql_time->time_type) {
+ case MYSQL_TIMESTAMP_DATE:
+ {
+ DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_DATE"));
+ struct tm date;
+ memset(&date, 0, sizeof(struct tm));
+ date.tm_year = mysql_time->year - TM_YEAR_BASE;
+ if (mysql_time->month > 0) {
+ date.tm_mon = mysql_time->month - 1;
+ } else {
+ date.tm_mon = 0;
+ *truncated = true;
+ }
+ if (mysql_time->day > 0) {
+ date.tm_mday = mysql_time->day;
+ } else {
+ date.tm_mday = 1;
+ *truncated = true;
+ }
+ DBUG_PRINT("info", ("mroonga: tm_year=%d", date.tm_year));
+ DBUG_PRINT("info", ("mroonga: tm_mon=%d", date.tm_mon));
+ DBUG_PRINT("info", ("mroonga: tm_mday=%d", date.tm_mday));
+ bool tm_truncated = false;
+ grn_time = tm_to_grn_time(&date, usec, &tm_truncated);
+ if (tm_truncated) {
+ *truncated = true;
+ }
+ }
+ break;
+ case MYSQL_TIMESTAMP_DATETIME:
+ {
+ DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_DATETIME"));
+ struct tm datetime;
+ memset(&datetime, 0, sizeof(struct tm));
+ datetime.tm_year = mysql_time->year - TM_YEAR_BASE;
+ if (mysql_time->month > 0) {
+ datetime.tm_mon = mysql_time->month - 1;
+ } else {
+ datetime.tm_mon = 0;
+ *truncated = true;
+ }
+ if (mysql_time->day > 0) {
+ datetime.tm_mday = mysql_time->day;
+ } else {
+ datetime.tm_mday = 1;
+ *truncated = true;
+ }
+ datetime.tm_hour = mysql_time->hour;
+ datetime.tm_min = mysql_time->minute;
+ datetime.tm_sec = mysql_time->second;
+ DBUG_PRINT("info", ("mroonga: tm_year=%d", datetime.tm_year));
+ DBUG_PRINT("info", ("mroonga: tm_mon=%d", datetime.tm_mon));
+ DBUG_PRINT("info", ("mroonga: tm_mday=%d", datetime.tm_mday));
+ DBUG_PRINT("info", ("mroonga: tm_hour=%d", datetime.tm_hour));
+ DBUG_PRINT("info", ("mroonga: tm_min=%d", datetime.tm_min));
+ DBUG_PRINT("info", ("mroonga: tm_sec=%d", datetime.tm_sec));
+ bool tm_truncated = false;
+ grn_time = tm_to_grn_time(&datetime, usec, &tm_truncated);
+ if (tm_truncated) {
+ *truncated = true;
+ }
+ }
+ break;
+ case MYSQL_TIMESTAMP_TIME:
+ {
+ DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_TIME"));
+ int sec =
+ mysql_time->hour * 60 * 60 +
+ mysql_time->minute * 60 +
+ mysql_time->second;
+ DBUG_PRINT("info", ("mroonga: sec=%d", sec));
+ grn_time = GRN_TIME_PACK(sec, usec);
+ if (mysql_time->neg) {
+ grn_time = -grn_time;
+ }
+ }
+ break;
+ default:
+ DBUG_PRINT("info", ("mroonga: default"));
+ grn_time = 0;
+ break;
+ }
+
+ DBUG_RETURN(grn_time);
+ }
+
+ void TimeConverter::grn_time_to_mysql_time(long long int grn_time,
+ MYSQL_TIME *mysql_time) {
+ MRN_DBUG_ENTER_METHOD();
+ long long int sec;
+ int usec;
+ GRN_TIME_UNPACK(grn_time, sec, usec);
+ DBUG_PRINT("info", ("mroonga: sec=%lld", sec));
+ DBUG_PRINT("info", ("mroonga: usec=%d", usec));
+ switch (mysql_time->time_type) {
+ case MYSQL_TIMESTAMP_DATE:
+ {
+ DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_DATE"));
+ struct tm date;
+ time_t sec_t = sec;
+ // TODO: Add error check
+ gmtime_r(&sec_t, &date);
+ DBUG_PRINT("info", ("mroonga: tm_year=%d", date.tm_year));
+ mysql_time->year = date.tm_year + TM_YEAR_BASE;
+ DBUG_PRINT("info", ("mroonga: tm_mon=%d", date.tm_mon));
+ mysql_time->month = date.tm_mon + 1;
+ DBUG_PRINT("info", ("mroonga: tm_mday=%d", date.tm_mday));
+ mysql_time->day = date.tm_mday;
+ }
+ break;
+ case MYSQL_TIMESTAMP_DATETIME:
+ {
+ DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_DATETIME"));
+ struct tm date;
+ time_t sec_t = sec;
+ // TODO: Add error check
+ gmtime_r(&sec_t, &date);
+ DBUG_PRINT("info", ("mroonga: tm_year=%d", date.tm_year));
+ mysql_time->year = date.tm_year + TM_YEAR_BASE;
+ DBUG_PRINT("info", ("mroonga: tm_mon=%d", date.tm_mon));
+ mysql_time->month = date.tm_mon + 1;
+ DBUG_PRINT("info", ("mroonga: tm_mday=%d", date.tm_mday));
+ mysql_time->day = date.tm_mday;
+ DBUG_PRINT("info", ("mroonga: tm_hour=%d", date.tm_hour));
+ mysql_time->hour = date.tm_hour;
+ DBUG_PRINT("info", ("mroonga: tm_min=%d", date.tm_min));
+ mysql_time->minute = date.tm_min;
+ DBUG_PRINT("info", ("mroonga: tm_sec=%d", date.tm_sec));
+ mysql_time->second = date.tm_sec;
+ mysql_time->second_part = usec;
+ }
+ break;
+ case MYSQL_TIMESTAMP_TIME:
+ DBUG_PRINT("info", ("mroonga: MYSQL_TIMESTAMP_TIME"));
+ if (sec < 0) {
+ mysql_time->neg = true;
+ sec = -sec;
+ }
+ mysql_time->hour = static_cast<unsigned int>(sec / 60 / 60);
+ mysql_time->minute = sec / 60 % 60;
+ mysql_time->second = sec % 60;
+ mysql_time->second_part = usec;
+ break;
+ default:
+ DBUG_PRINT("info", ("mroonga: default"));
+ break;
+ }
+ DBUG_VOID_RETURN;
+ }
+}
diff --git a/storage/mroonga/lib/mrn_time_converter.hpp b/storage/mroonga/lib/mrn_time_converter.hpp
new file mode 100644
index 00000000000..c5c69a0a8ad
--- /dev/null
+++ b/storage/mroonga/lib/mrn_time_converter.hpp
@@ -0,0 +1,47 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2010-2013 Kentoku SHIBA
+ Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_TIME_CONVERTER_HPP_
+#define MRN_TIME_CONVERTER_HPP_
+
+#include <groonga.h>
+#include <mrn_mysql_compat.h>
+
+namespace mrn {
+ class TimeConverter {
+ public:
+ static const long long int TM_YEAR_BASE = 1900;
+
+ TimeConverter();
+ ~TimeConverter();
+
+ long long int mysql_time_to_grn_time(MYSQL_TIME *mysql_time,
+ bool *truncated);
+
+ long long int tm_to_grn_time(struct tm *time, int usec, bool *truncated);
+
+ void grn_time_to_mysql_time(long long int grn_time, MYSQL_TIME *mysql_time);
+
+ private:
+ time_t tm_to_time_gm(struct tm *time, bool *truncated);
+ };
+}
+
+#endif /* MRN_TIME_CONVERTER_HPP_ */
diff --git a/storage/mroonga/lib/mrn_windows.hpp b/storage/mroonga/lib/mrn_windows.hpp
new file mode 100644
index 00000000000..f6ae4490254
--- /dev/null
+++ b/storage/mroonga/lib/mrn_windows.hpp
@@ -0,0 +1,31 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ Copyright(C) 2010 Tetsuro IKEDA
+ Copyright(C) 2010-2013 Kentoku SHIBA
+ Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_WINDOWS_HPP_
+#define MRN_WINDOWS_HPP_
+
+#if defined(_WIN32) || defined(_WIN64)
+# define MRN_API __declspec(dllexport)
+#else
+# define MRN_API
+#endif
+
+#endif /* MRN_WINDOWS_HPP_ */
diff --git a/storage/mroonga/mrn_constants.hpp b/storage/mroonga/mrn_constants.hpp
new file mode 100644
index 00000000000..494e08721bd
--- /dev/null
+++ b/storage/mroonga/mrn_constants.hpp
@@ -0,0 +1,47 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2010 Tetsuro IKEDA
+ Copyright(C) 2011 Kentoku SHIBA
+ Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_CONSTANTS_HPP_
+#define MRN_CONSTANTS_HPP_
+
+#include <groonga.h>
+
+#define MRN_BUFFER_SIZE 1024
+#define MRN_MAX_KEY_SIZE GRN_TABLE_MAX_KEY_SIZE
+#if defined(MAX_PATH)
+# define MRN_MAX_PATH_SIZE (MAX_PATH + 1)
+#elif defined(PATH_MAX)
+# define MRN_MAX_PATH_SIZE (PATH_MAX)
+#elif defined(MAXPATHLEN)
+# define MRN_MAX_PATH_SIZE (MAXPATHLEN)
+#else
+# define MRN_MAX_PATH_SIZE (256)
+#endif
+#define MRN_DB_FILE_SUFFIX ".mrn"
+#define MRN_LOG_FILE_PATH "groonga.log"
+#define MRN_COLUMN_NAME_ID "_id"
+#define MRN_COLUMN_NAME_KEY "_key"
+#define MRN_COLUMN_NAME_SCORE "_score"
+#ifndef MRN_PARSER_DEFAULT
+# define MRN_PARSER_DEFAULT "TokenBigram"
+#endif
+
+#endif /* MRN_CONSTANTS_HPP_ */
diff --git a/storage/mroonga/mrn_err.h b/storage/mroonga/mrn_err.h
new file mode 100644
index 00000000000..c2ca885407a
--- /dev/null
+++ b/storage/mroonga/mrn_err.h
@@ -0,0 +1,32 @@
+/* Copyright(C) 2011 Kentoku SHIBA
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_ERR_H_
+#define MRN_ERR_H_
+
+#define ER_MRN_INVALID_TABLE_PARAM_NUM 16501
+#define ER_MRN_INVALID_TABLE_PARAM_STR "The table parameter '%-.64s' is invalid"
+#define ER_MRN_CHARSET_NOT_SUPPORT_NUM 16502
+#define ER_MRN_CHARSET_NOT_SUPPORT_STR "The character set '%s[%s]' is not supported by groonga"
+#define ER_MRN_GEOMETRY_NOT_SUPPORT_NUM 16503
+#define ER_MRN_GEOMETRY_NOT_SUPPORT_STR "This geometry type is not supported. Groonga is supported point only"
+#define ER_MRN_ERROR_FROM_GROONGA_NUM 16504
+#define ER_MRN_ERROR_FROM_GROONGA_STR "Error from Groonga [%s]"
+#define ER_MRN_INVALID_NULL_VALUE_NUM 16505
+#define ER_MRN_INVALID_NULL_VALUE_STR "NULL value can't be used for %s"
+
+#endif /* MRN_ERR_H_ */
diff --git a/storage/mroonga/mrn_macro.hpp b/storage/mroonga/mrn_macro.hpp
new file mode 100644
index 00000000000..b20fdb4c140
--- /dev/null
+++ b/storage/mroonga/mrn_macro.hpp
@@ -0,0 +1,30 @@
+/*
+ Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_MACRO_HPP_
+#define MRN_MACRO_HPP_
+
+#ifdef __cplusplus
+# define MRN_BEGIN_DECLS extern "C" {
+# define MRN_END_DECLS }
+#else
+# define MRN_BEGIN_DECLS
+# define MRN_END_DECLS
+#endif
+
+#endif /* MRN_MACRO_HPP_ */
diff --git a/storage/mroonga/mrn_mysql.h b/storage/mroonga/mrn_mysql.h
new file mode 100644
index 00000000000..cf2a54621bb
--- /dev/null
+++ b/storage/mroonga/mrn_mysql.h
@@ -0,0 +1,83 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_MYSQL_H_
+#define MRN_MYSQL_H_
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+/* We need to undefine them because my_config.h defines them. :< */
+# undef VERSION
+# undef PACKAGE
+# undef PACKAGE_BUGREPORT
+# undef PACKAGE_NAME
+# undef PACKAGE_STRING
+# undef PACKAGE_TARNAME
+# undef PACKAGE_VERSION
+#endif
+
+#include <mrn_version.h>
+
+#ifdef FORCE_FAST_MUTEX_DISABLED
+# ifdef MY_PTHREAD_FASTMUTEX
+# undef MY_PTHREAD_FASTMUTEX
+# endif
+#endif
+
+#define MYSQL_SERVER 1
+#include <mysql_version.h>
+
+#if MYSQL_VERSION_ID < 50500
+# include <mysql_priv.h>
+# include <mysql/plugin.h>
+#else
+# include <sql_priv.h>
+# include <sql_class.h>
+# include <probes_mysql.h>
+# include <sql_partition.h>
+#endif
+#include <rpl_filter.h>
+
+#ifdef MARIADB_BASE_VERSION
+# define MRN_MARIADB_P 1
+#endif
+
+#if MYSQL_VERSION_ID >= 50607
+# if !defined(MRN_MARIADB_P)
+# define MRN_HAVE_SQL_OPTIMIZER_H
+# endif
+#endif
+
+#define MRN_MESSAGE_BUFFER_SIZE 1024
+
+#define MRN_DBUG_ENTER_FUNCTION() DBUG_ENTER(__FUNCTION__)
+
+#if !defined(DBUG_OFF) && !defined(_lint)
+# define MRN_DBUG_ENTER_METHOD() \
+ char method_name[MRN_MESSAGE_BUFFER_SIZE]; \
+ method_name[0] = '\0'; \
+ strcat(method_name, MRN_CLASS_NAME); \
+ strcat(method_name, "::"); \
+ strcat(method_name, __FUNCTION__); \
+ DBUG_ENTER(method_name)
+#else
+# define MRN_DBUG_ENTER_METHOD() MRN_DBUG_ENTER_FUNCTION()
+#endif
+
+#endif /* MRN_MYSQL_H_ */
diff --git a/storage/mroonga/mrn_mysql_compat.h b/storage/mroonga/mrn_mysql_compat.h
new file mode 100644
index 00000000000..7312dd70827
--- /dev/null
+++ b/storage/mroonga/mrn_mysql_compat.h
@@ -0,0 +1,143 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_MYSQL_COMPAT_H_
+#define MRN_MYSQL_COMPAT_H_
+
+#include "mrn_mysql.h"
+
+#if MYSQL_VERSION_ID >= 50500
+# define my_free(PTR, FLAG) my_free(PTR)
+#endif
+
+#if MYSQL_VERSION_ID < 50500
+# define mysql_mutex_lock(mutex) pthread_mutex_lock(mutex)
+# define mysql_mutex_unlock(mutex) pthread_mutex_unlock(mutex)
+#endif
+
+#if MYSQL_VERSION_ID >= 50604
+# define MRN_HAVE_MYSQL_TYPE_TIMESTAMP2
+# define MRN_HAVE_MYSQL_TYPE_DATETIME2
+# define MRN_HAVE_MYSQL_TYPE_TIME2
+#endif
+
+#if MYSQL_VERSION_ID < 50603
+ typedef MYSQL_ERROR Sql_condition;
+#endif
+
+#if defined(MRN_MARIADB_P)
+# if MYSQL_VERSION_ID >= 50302 && MYSQL_VERSION_ID < 100000
+ typedef COST_VECT Cost_estimate;
+# endif
+#endif
+
+#if MYSQL_VERSION_ID >= 50516
+# define MRN_PLUGIN_HAVE_FLAGS 1
+#endif
+
+// for MySQL < 5.5
+#ifndef MY_ALL_CHARSETS_SIZE
+# define MY_ALL_CHARSETS_SIZE 256
+#endif
+
+#ifndef MRN_MARIADB_P
+ typedef char *range_id_t;
+#endif
+
+#if MYSQL_VERSION_ID >= 50609
+# define MRN_KEY_HAS_USER_DEFINED_KEYPARTS
+#endif
+
+#ifdef MRN_KEY_HAS_USER_DEFINED_KEYPARTS
+# define KEY_N_KEY_PARTS(key) (key)->user_defined_key_parts
+#else
+# define KEY_N_KEY_PARTS(key) (key)->key_parts
+#endif
+
+#if MYSQL_VERSION_ID < 100000 || !defined(MRN_MARIADB_P)
+# define init_alloc_root(PTR, SZ1, SZ2, FLAG) init_alloc_root(PTR, SZ1, SZ2)
+#endif
+
+#if MYSQL_VERSION_ID < 100002 || !defined(MRN_MARIADB_P)
+# define GTS_TABLE 0
+#endif
+
+/* For MySQL 5.1. MySQL 5.1 doesn't have FN_LIBCHAR2. */
+#ifndef FN_LIBCHAR2
+# define FN_LIBCHAR2 FN_LIBCHAR
+#endif
+
+#if MYSQL_VERSION_ID >= 50607
+# if MYSQL_VERSION_ID >= 100007 && defined(MRN_MARIADB_P)
+# define MRN_GET_ERROR_MESSAGE thd_get_error_message(current_thd)
+# define MRN_GET_CURRENT_ROW_FOR_WARNING(thd) thd_get_error_row(thd)
+# else
+# define MRN_GET_ERROR_MESSAGE current_thd->get_stmt_da()->message()
+# define MRN_GET_CURRENT_ROW_FOR_WARNING(thd) thd->get_stmt_da()->current_row_for_warning()
+# endif
+#else
+# if MYSQL_VERSION_ID >= 50500
+# define MRN_GET_ERROR_MESSAGE current_thd->stmt_da->message()
+# define MRN_GET_CURRENT_ROW_FOR_WARNING(thd) thd->warning_info->current_row_for_warning()
+# else
+# define MRN_GET_ERROR_MESSAGE current_thd->main_da.message()
+# define MRN_GET_CURRENT_ROW_FOR_WARNING(thd) thd->row_count
+# endif
+#endif
+
+#if MYSQL_VERSION_ID >= 50607 && !defined(MRN_MARIADB_P)
+# define MRN_ITEM_HAVE_ITEM_NAME
+#endif
+
+#if MYSQL_VERSION_ID >= 50500 && MYSQL_VERSION_ID < 50700
+# define MRN_HAVE_TABLE_DEF_CACHE
+#endif
+
+#if defined(MRN_MARIADB_P) && MYSQL_VERSION_ID >= 100009
+# define MRN_HAVE_TDC_ACQUIRE_SHARE
+#endif
+
+#if MYSQL_VERSION_ID >= 50613
+# define MRN_HAVE_ALTER_INFO
+#endif
+
+#if MYSQL_VERSION_ID >= 50603
+# define MRN_HAVE_GET_TABLE_DEF_KEY
+#endif
+
+#if defined(MRN_MARIADB_P) && MYSQL_VERSION_ID >= 100004
+# define MRN_TABLE_SHARE_HAVE_LOCK_SHARE
+#endif
+
+#if MYSQL_VERSION_ID >= 50404
+# define MRN_TABLE_SHARE_HAVE_LOCK_HA_DATA
+#endif
+
+#ifndef TIME_FUZZY_DATE
+/* For MariaDB 10. */
+# ifdef TIME_FUZZY_DATES
+# define TIME_FUZZY_DATE TIME_FUZZY_DATES
+# endif
+#endif
+
+#if MYSQL_VERSION_ID >= 100007 && defined(MRN_MARIADB_P)
+# define MRN_USE_MYSQL_DATA_HOME
+#endif
+
+#endif /* MRN_MYSQL_COMPAT_H_ */
diff --git a/storage/mroonga/mrn_sys.cpp b/storage/mroonga/mrn_sys.cpp
new file mode 100644
index 00000000000..86cce1cc415
--- /dev/null
+++ b/storage/mroonga/mrn_sys.cpp
@@ -0,0 +1,88 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2010 Tetsuro IKEDA
+ Copyright(C) 2011-2012 Kentoku SHIBA
+ Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "mrn_sys.hpp"
+
+bool mrn_hash_put(grn_ctx *ctx, grn_hash *hash, const char *key, grn_obj *value)
+{
+ int added;
+ bool succeed;
+ void *buf;
+ grn_hash_add(ctx, hash, (const char *)key, strlen(key), &buf, &added);
+ // duplicate check
+ if (added == 0) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "hash put duplicated (key=%s)", key);
+ succeed = false;
+ } else {
+ // store address of value
+ memcpy(buf, &value, sizeof(grn_obj *));
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "hash put (key=%s)", key);
+ succeed = true;
+ }
+ return succeed;
+}
+
+bool mrn_hash_get(grn_ctx *ctx, grn_hash *hash, const char *key, grn_obj **value)
+{
+ bool found;
+ grn_id id;
+ void *buf;
+ id = grn_hash_get(ctx, hash, (const char *)key, strlen(key), &buf);
+ // key not found
+ if (id == GRN_ID_NIL) {
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "hash get not found (key=%s)", key);
+ found = false;
+ } else {
+ // restore address of value
+ memcpy(value, buf, sizeof(grn_obj *));
+ found = true;
+ }
+ return found;
+}
+
+bool mrn_hash_remove(grn_ctx *ctx, grn_hash *hash, const char *key)
+{
+ bool succeed;
+ grn_rc rc;
+ grn_id id;
+ id = grn_hash_get(ctx, hash, (const char*) key, strlen(key), NULL);
+ if (id == GRN_ID_NIL) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "hash remove not found (key=%s)", key);
+ succeed = false;
+ } else {
+ rc = grn_hash_delete_by_id(ctx, hash, id, NULL);
+ if (rc != GRN_SUCCESS) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "hash remove error (key=%s)", key);
+ succeed = false;
+ } else {
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "hash remove (key=%s)", key);
+ succeed = true;
+ }
+ }
+ return succeed;
+}
diff --git a/storage/mroonga/mrn_sys.hpp b/storage/mroonga/mrn_sys.hpp
new file mode 100644
index 00000000000..904421682ff
--- /dev/null
+++ b/storage/mroonga/mrn_sys.hpp
@@ -0,0 +1,36 @@
+/*
+ Copyright(C) 2010 Tetsuro IKEDA
+ Copyright(C) 2011 Kentoku SHIBA
+ Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_SYS_HPP_
+#define MRN_SYS_HPP_
+
+#include <groonga.h>
+#include "mrn_macro.hpp"
+
+MRN_BEGIN_DECLS
+
+/* functions */
+bool mrn_hash_put(grn_ctx *ctx, grn_hash *hash, const char *key, grn_obj *value);
+bool mrn_hash_get(grn_ctx *ctx, grn_hash *hash, const char *key, grn_obj **value);
+bool mrn_hash_remove(grn_ctx *ctx, grn_hash *hash, const char *key);
+
+MRN_END_DECLS
+
+#endif /* MRN_SYS_HPP_ */
diff --git a/storage/mroonga/mrn_table.cpp b/storage/mroonga/mrn_table.cpp
new file mode 100644
index 00000000000..79e1c8388fb
--- /dev/null
+++ b/storage/mroonga/mrn_table.cpp
@@ -0,0 +1,1129 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2011-2013 Kentoku SHIBA
+ Copyright(C) 2011-2014 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <string>
+
+#include "mrn_mysql.h"
+
+#if MYSQL_VERSION_ID >= 50500
+# include <sql_servers.h>
+# include <sql_base.h>
+#endif
+#include "mrn_err.h"
+#include "mrn_sys.hpp"
+#include "mrn_table.hpp"
+#include "mrn_mysql_compat.h"
+#include <mrn_lock.hpp>
+
+#if MYSQL_VERSION_ID >= 50603 && !defined(MRN_MARIADB_P)
+# define MRN_HA_RESOLVE_BY_NAME(name) ha_resolve_by_name(NULL, (name), TRUE)
+#else
+# define MRN_HA_RESOLVE_BY_NAME(name) ha_resolve_by_name(NULL, (name))
+#endif
+
+#define LEX_STRING_IS_EMPTY(string) \
+ ((string).length == 0 || !(string).str || (string).str[0] == '\0')
+
+#define MRN_DEFAULT_STR "DEFAULT"
+#define MRN_DEFAULT_LEN (sizeof(MRN_DEFAULT_STR) - 1)
+#define MRN_GROONGA_STR "GROONGA"
+#define MRN_GROONGA_LEN (sizeof(MRN_GROONGA_STR) - 1)
+
+#ifdef MRN_HAVE_TABLE_DEF_CACHE
+extern HASH *mrn_table_def_cache;
+#endif
+
+#ifdef WIN32
+# ifdef MRN_TABLE_SHARE_HAVE_LOCK_SHARE
+extern PSI_mutex_key *mrn_table_share_lock_share;
+# endif
+# ifdef MRN_TABLE_SHARE_HAVE_LOCK_HA_DATA
+extern PSI_mutex_key *mrn_table_share_lock_ha_data;
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern HASH mrn_open_tables;
+extern pthread_mutex_t mrn_open_tables_mutex;
+extern HASH mrn_long_term_share;
+extern pthread_mutex_t mrn_long_term_share_mutex;
+extern char *mrn_default_parser;
+extern char *mrn_default_wrapper_engine;
+extern handlerton *mrn_hton_ptr;
+extern HASH mrn_allocated_thds;
+extern pthread_mutex_t mrn_allocated_thds_mutex;
+
+static char *mrn_get_string_between_quote(const char *ptr)
+{
+ const char *start_ptr, *end_ptr, *tmp_ptr, *esc_ptr;
+ bool find_flg = FALSE, esc_flg = FALSE;
+ MRN_DBUG_ENTER_FUNCTION();
+
+ start_ptr = strchr(ptr, '\'');
+ end_ptr = strchr(ptr, '"');
+ if (start_ptr && (!end_ptr || start_ptr < end_ptr))
+ {
+ tmp_ptr = ++start_ptr;
+ while (!find_flg)
+ {
+ if (!(end_ptr = strchr(tmp_ptr, '\'')))
+ DBUG_RETURN(NULL);
+ esc_ptr = tmp_ptr;
+ while (!find_flg)
+ {
+ esc_ptr = strchr(esc_ptr, '\\');
+ if (!esc_ptr || esc_ptr > end_ptr)
+ find_flg = TRUE;
+ else if (esc_ptr == end_ptr - 1)
+ {
+ esc_flg = TRUE;
+ tmp_ptr = end_ptr + 1;
+ break;
+ } else {
+ esc_flg = TRUE;
+ esc_ptr += 2;
+ }
+ }
+ }
+ } else if (end_ptr)
+ {
+ start_ptr = end_ptr;
+ tmp_ptr = ++start_ptr;
+ while (!find_flg)
+ {
+ if (!(end_ptr = strchr(tmp_ptr, '"')))
+ DBUG_RETURN(NULL);
+ esc_ptr = tmp_ptr;
+ while (!find_flg)
+ {
+ esc_ptr = strchr(esc_ptr, '\\');
+ if (!esc_ptr || esc_ptr > end_ptr)
+ find_flg = TRUE;
+ else if (esc_ptr == end_ptr - 1)
+ {
+ esc_flg = TRUE;
+ tmp_ptr = end_ptr + 1;
+ break;
+ } else {
+ esc_flg = TRUE;
+ esc_ptr += 2;
+ }
+ }
+ }
+ } else
+ DBUG_RETURN(NULL);
+
+ size_t length = end_ptr - start_ptr;
+ char *extracted_string = (char *)my_malloc(length + 1, MYF(MY_WME));
+ if (esc_flg) {
+ size_t extracted_index = 0;
+ const char *current_ptr = start_ptr;
+ while (current_ptr < end_ptr) {
+ if (*current_ptr != '\\') {
+ extracted_string[extracted_index] = *current_ptr;
+ ++extracted_index;
+ current_ptr++;
+ continue;
+ }
+
+ if (current_ptr + 1 == end_ptr) {
+ break;
+ }
+
+ switch (*(current_ptr + 1))
+ {
+ case 'b':
+ extracted_string[extracted_index] = '\b';
+ break;
+ case 'n':
+ extracted_string[extracted_index] = '\n';
+ break;
+ case 'r':
+ extracted_string[extracted_index] = '\r';
+ break;
+ case 't':
+ extracted_string[extracted_index] = '\t';
+ break;
+ default:
+ extracted_string[extracted_index] = *(current_ptr + 1);
+ break;
+ }
+ ++extracted_index;
+ }
+ } else {
+ memcpy(extracted_string, start_ptr, length);
+ extracted_string[length] = '\0';
+ }
+
+ DBUG_RETURN(extracted_string);
+}
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+void mrn_get_partition_info(const char *table_name, uint table_name_length,
+ const TABLE *table, partition_element **part_elem,
+ partition_element **sub_elem)
+{
+ char tmp_name[FN_LEN];
+ partition_info *part_info = table->part_info;
+ partition_element *tmp_part_elem = NULL, *tmp_sub_elem = NULL;
+ bool tmp_flg = FALSE, tmp_find_flg = FALSE;
+ MRN_DBUG_ENTER_FUNCTION();
+ *part_elem = NULL;
+ *sub_elem = NULL;
+ if (!part_info)
+ DBUG_VOID_RETURN;
+
+ if (table_name && !memcmp(table_name + table_name_length - 5, "#TMP#", 5))
+ tmp_flg = TRUE;
+
+ DBUG_PRINT("info", ("mroonga table_name=%s", table_name));
+ List_iterator<partition_element> part_it(part_info->partitions);
+ while ((*part_elem = part_it++))
+ {
+ if ((*part_elem)->subpartitions.elements)
+ {
+ List_iterator<partition_element> sub_it((*part_elem)->subpartitions);
+ while ((*sub_elem = sub_it++))
+ {
+ create_subpartition_name(tmp_name, table->s->path.str,
+ (*part_elem)->partition_name, (*sub_elem)->partition_name,
+ NORMAL_PART_NAME);
+ DBUG_PRINT("info", ("mroonga tmp_name=%s", tmp_name));
+ if (table_name && !memcmp(table_name, tmp_name, table_name_length + 1))
+ DBUG_VOID_RETURN;
+ if (
+ tmp_flg &&
+ table_name &&
+ *(tmp_name + table_name_length - 5) == '\0' &&
+ !memcmp(table_name, tmp_name, table_name_length - 5)
+ ) {
+ tmp_part_elem = *part_elem;
+ tmp_sub_elem = *sub_elem;
+ tmp_flg = FALSE;
+ tmp_find_flg = TRUE;
+ }
+ }
+ } else {
+ create_partition_name(tmp_name, table->s->path.str,
+ (*part_elem)->partition_name, NORMAL_PART_NAME, TRUE);
+ DBUG_PRINT("info", ("mroonga tmp_name=%s", tmp_name));
+ if (table_name && !memcmp(table_name, tmp_name, table_name_length + 1))
+ DBUG_VOID_RETURN;
+ if (
+ tmp_flg &&
+ table_name &&
+ *(tmp_name + table_name_length - 5) == '\0' &&
+ !memcmp(table_name, tmp_name, table_name_length - 5)
+ ) {
+ tmp_part_elem = *part_elem;
+ tmp_flg = FALSE;
+ tmp_find_flg = TRUE;
+ }
+ }
+ }
+ if (tmp_find_flg)
+ {
+ *part_elem = tmp_part_elem;
+ *sub_elem = tmp_sub_elem;
+ DBUG_PRINT("info", ("mroonga tmp find"));
+ DBUG_VOID_RETURN;
+ }
+ *part_elem = NULL;
+ *sub_elem = NULL;
+ DBUG_PRINT("info", ("mroonga no hit"));
+ DBUG_VOID_RETURN;
+}
+#endif
+
+#define MRN_PARAM_STR_LEN(name) name ## _length
+#define MRN_PARAM_STR(title_name, param_name) \
+ if (!strncasecmp(tmp_ptr, title_name, title_length)) \
+ { \
+ DBUG_PRINT("info", ("mroonga "title_name" start")); \
+ if (!share->param_name) \
+ { \
+ if ((share->param_name = mrn_get_string_between_quote( \
+ start_ptr))) \
+ share->MRN_PARAM_STR_LEN(param_name) = strlen(share->param_name); \
+ else { \
+ error = ER_MRN_INVALID_TABLE_PARAM_NUM; \
+ my_printf_error(error, ER_MRN_INVALID_TABLE_PARAM_STR, \
+ MYF(0), tmp_ptr); \
+ goto error; \
+ } \
+ DBUG_PRINT("info", ("mroonga "title_name"=%s", share->param_name)); \
+ } \
+ break; \
+ }
+
+#define MRN_PARAM_STR_LIST(title_name, param_name, param_pos) \
+ if (!strncasecmp(tmp_ptr, title_name, title_length)) \
+ { \
+ DBUG_PRINT("info", ("mroonga "title_name" start")); \
+ if (share->param_name && !share->param_name[param_pos]) \
+ { \
+ if ((share->param_name[param_pos] = mrn_get_string_between_quote( \
+ start_ptr))) \
+ share->MRN_PARAM_STR_LEN(param_name)[param_pos] = \
+ strlen(share->param_name[param_pos]); \
+ else { \
+ error = ER_MRN_INVALID_TABLE_PARAM_NUM; \
+ my_printf_error(error, ER_MRN_INVALID_TABLE_PARAM_STR, \
+ MYF(0), tmp_ptr); \
+ goto error; \
+ } \
+ DBUG_PRINT("info", ("mroonga "title_name"[%d]=%s", param_pos, \
+ share->param_name[param_pos])); \
+ } \
+ break; \
+ }
+
+int mrn_parse_table_param(MRN_SHARE *share, TABLE *table)
+{
+ int i, error;
+ int title_length;
+ const char *sprit_ptr[2];
+ const char *tmp_ptr, *start_ptr;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ partition_element *part_elem;
+ partition_element *sub_elem;
+#endif
+ MRN_DBUG_ENTER_FUNCTION();
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ mrn_get_partition_info(share->table_name, share->table_name_length, table,
+ &part_elem, &sub_elem);
+#endif
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ for (i = 4; i > 0; i--)
+#else
+ for (i = 2; i > 0; i--)
+#endif
+ {
+ const char *params_string_value;
+ uint params_string_length;
+ switch (i)
+ {
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ case 4:
+ if (!sub_elem || !sub_elem->part_comment)
+ continue;
+ DBUG_PRINT("info", ("mroonga create sub comment string"));
+ params_string_value = sub_elem->part_comment;
+ params_string_length = strlen(params_string_value);
+ DBUG_PRINT("info",
+ ("mroonga sub comment string=%s", params_string_value));
+ break;
+ case 3:
+ if (!part_elem || !part_elem->part_comment)
+ continue;
+ DBUG_PRINT("info", ("mroonga create part comment string"));
+ params_string_value = part_elem->part_comment;
+ params_string_length = strlen(params_string_value);
+ DBUG_PRINT("info",
+ ("mroonga part comment string=%s", params_string_value));
+ break;
+#endif
+ case 2:
+ if (LEX_STRING_IS_EMPTY(table->s->comment))
+ continue;
+ DBUG_PRINT("info", ("mroonga create comment string"));
+ params_string_value = table->s->comment.str;
+ params_string_length = table->s->comment.length;
+ DBUG_PRINT("info",
+ ("mroonga comment string=%.*s",
+ params_string_length, params_string_value));
+ break;
+ default:
+ if (LEX_STRING_IS_EMPTY(table->s->connect_string))
+ continue;
+ DBUG_PRINT("info", ("mroonga create connect_string string"));
+ params_string_value = table->s->connect_string.str;
+ params_string_length = table->s->connect_string.length;
+ DBUG_PRINT("info",
+ ("mroonga connect_string=%.*s",
+ params_string_length, params_string_value));
+ break;
+ }
+
+ if (!params_string_value) {
+ continue;
+ }
+
+ {
+ std::string params_string(params_string_value, params_string_length);
+ sprit_ptr[0] = params_string.c_str();
+ while (sprit_ptr[0])
+ {
+ if ((sprit_ptr[1] = strchr(sprit_ptr[0], ',')))
+ {
+ sprit_ptr[1]++;
+ }
+ tmp_ptr = sprit_ptr[0];
+ sprit_ptr[0] = sprit_ptr[1];
+ while (*tmp_ptr == ' ' || *tmp_ptr == '\r' ||
+ *tmp_ptr == '\n' || *tmp_ptr == '\t')
+ tmp_ptr++;
+
+ if (*tmp_ptr == '\0')
+ continue;
+
+ DBUG_PRINT("info", ("mroonga title_str=%s", tmp_ptr));
+ title_length = 0;
+ start_ptr = tmp_ptr;
+ while (*start_ptr != ' ' && *start_ptr != '\'' &&
+ *start_ptr != '"' && *start_ptr != '\0' &&
+ *start_ptr != '\r' && *start_ptr != '\n' &&
+ *start_ptr != '\t' && *start_ptr != ',')
+ {
+ title_length++;
+ start_ptr++;
+ }
+ DBUG_PRINT("info", ("mroonga title_length=%u", title_length));
+
+ switch (title_length)
+ {
+ case 6:
+ MRN_PARAM_STR("engine", engine);
+ break;
+ case 17:
+ MRN_PARAM_STR("default_tokenizer", default_tokenizer);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if (!share->engine && mrn_default_wrapper_engine)
+ {
+ share->engine_length = strlen(mrn_default_wrapper_engine);
+ if (
+ !(share->engine = my_strndup(mrn_default_wrapper_engine,
+ share->engine_length,
+ MYF(MY_WME)))
+ ) {
+ error = HA_ERR_OUT_OF_MEM;
+ goto error;
+ }
+ }
+
+ if (share->engine)
+ {
+ LEX_STRING engine_name;
+ if (
+ (
+ share->engine_length == MRN_DEFAULT_LEN &&
+ !strncasecmp(share->engine, MRN_DEFAULT_STR, MRN_DEFAULT_LEN)
+ ) ||
+ (
+ share->engine_length == MRN_GROONGA_LEN &&
+ !strncasecmp(share->engine, MRN_GROONGA_STR, MRN_GROONGA_LEN)
+ )
+ ) {
+ my_free(share->engine, MYF(0));
+ share->engine = NULL;
+ share->engine_length = 0;
+ } else {
+ engine_name.str = share->engine;
+ engine_name.length = share->engine_length;
+ if (!(share->plugin = MRN_HA_RESOLVE_BY_NAME(&engine_name)))
+ {
+ my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), share->engine);
+ error = ER_UNKNOWN_STORAGE_ENGINE;
+ goto error;
+ }
+ share->hton = plugin_data(share->plugin, handlerton *);
+ share->wrapper_mode = TRUE;
+ }
+ }
+
+ DBUG_RETURN(0);
+
+error:
+ DBUG_RETURN(error);
+}
+
+bool mrn_is_geo_key(const KEY *key_info)
+{
+ return key_info->algorithm == HA_KEY_ALG_UNDEF &&
+ KEY_N_KEY_PARTS(key_info) == 1 &&
+ key_info->key_part[0].field->type() == MYSQL_TYPE_GEOMETRY;
+}
+
+int mrn_add_index_param(MRN_SHARE *share, KEY *key_info, int i)
+{
+ int error;
+ char *param_string = NULL;
+#if MYSQL_VERSION_ID >= 50500
+ int title_length;
+ char *sprit_ptr[2];
+ char *tmp_ptr, *start_ptr;
+#endif
+ MRN_DBUG_ENTER_FUNCTION();
+
+#if MYSQL_VERSION_ID >= 50500
+ if (key_info->comment.length == 0)
+ {
+ if (share->key_parser[i]) {
+ my_free(share->key_parser[i], MYF(0));
+ }
+ if (
+ !(share->key_parser[i] = my_strdup(mrn_default_parser, MYF(MY_WME)))
+ ) {
+ error = HA_ERR_OUT_OF_MEM;
+ goto error;
+ }
+ share->key_parser_length[i] = strlen(share->key_parser[i]);
+ DBUG_RETURN(0);
+ }
+ DBUG_PRINT("info", ("mroonga create comment string"));
+ if (
+ !(param_string = my_strndup(key_info->comment.str,
+ key_info->comment.length,
+ MYF(MY_WME)))
+ ) {
+ error = HA_ERR_OUT_OF_MEM;
+ goto error_alloc_param_string;
+ }
+ DBUG_PRINT("info", ("mroonga comment string=%s", param_string));
+
+ sprit_ptr[0] = param_string;
+ while (sprit_ptr[0])
+ {
+ if ((sprit_ptr[1] = strchr(sprit_ptr[0], ',')))
+ {
+ *sprit_ptr[1] = '\0';
+ sprit_ptr[1]++;
+ }
+ tmp_ptr = sprit_ptr[0];
+ sprit_ptr[0] = sprit_ptr[1];
+ while (*tmp_ptr == ' ' || *tmp_ptr == '\r' ||
+ *tmp_ptr == '\n' || *tmp_ptr == '\t')
+ tmp_ptr++;
+
+ if (*tmp_ptr == '\0')
+ continue;
+
+ title_length = 0;
+ start_ptr = tmp_ptr;
+ while (*start_ptr != ' ' && *start_ptr != '\'' &&
+ *start_ptr != '"' && *start_ptr != '\0' &&
+ *start_ptr != '\r' && *start_ptr != '\n' &&
+ *start_ptr != '\t')
+ {
+ title_length++;
+ start_ptr++;
+ }
+
+ switch (title_length)
+ {
+ case 5:
+ MRN_PARAM_STR_LIST("table", index_table, i);
+ break;
+ case 6:
+ MRN_PARAM_STR_LIST("parser", key_parser, i);
+ break;
+ default:
+ break;
+ }
+ }
+#endif
+ if (!share->key_parser[i]) {
+ if (
+ !(share->key_parser[i] = my_strdup(mrn_default_parser, MYF(MY_WME)))
+ ) {
+ error = HA_ERR_OUT_OF_MEM;
+ goto error;
+ }
+ share->key_parser_length[i] = strlen(share->key_parser[i]);
+ }
+
+ if (param_string)
+ my_free(param_string, MYF(0));
+ DBUG_RETURN(0);
+
+error:
+ if (param_string)
+ my_free(param_string, MYF(0));
+#if MYSQL_VERSION_ID >= 50500
+error_alloc_param_string:
+#endif
+ DBUG_RETURN(error);
+}
+
+int mrn_parse_index_param(MRN_SHARE *share, TABLE *table)
+{
+ int error;
+ MRN_DBUG_ENTER_FUNCTION();
+ for (uint i = 0; i < table->s->keys; i++)
+ {
+ KEY *key_info = &table->s->key_info[i];
+ bool is_wrapper_mode = share->engine != NULL;
+
+ if (is_wrapper_mode) {
+ if (!(key_info->flags & HA_FULLTEXT) && !mrn_is_geo_key(key_info)) {
+ continue;
+ }
+ }
+
+ if ((error = mrn_add_index_param(share, key_info, i)))
+ goto error;
+ }
+ DBUG_RETURN(0);
+
+error:
+ DBUG_RETURN(error);
+}
+
+int mrn_add_column_param(MRN_SHARE *share, Field *field, int i)
+{
+ int error;
+ char *param_string = NULL;
+ int title_length;
+ char *sprit_ptr[2];
+ char *tmp_ptr, *start_ptr;
+
+ MRN_DBUG_ENTER_FUNCTION();
+
+ if (share->wrapper_mode) {
+ DBUG_RETURN(0);
+ }
+
+ DBUG_PRINT("info", ("mroonga create comment string"));
+ if (
+ !(param_string = my_strndup(field->comment.str,
+ field->comment.length,
+ MYF(MY_WME)))
+ ) {
+ error = HA_ERR_OUT_OF_MEM;
+ goto error_alloc_param_string;
+ }
+ DBUG_PRINT("info", ("mroonga comment string=%s", param_string));
+
+ sprit_ptr[0] = param_string;
+ while (sprit_ptr[0])
+ {
+ if ((sprit_ptr[1] = strchr(sprit_ptr[0], ',')))
+ {
+ *sprit_ptr[1] = '\0';
+ sprit_ptr[1]++;
+ }
+ tmp_ptr = sprit_ptr[0];
+ sprit_ptr[0] = sprit_ptr[1];
+ while (*tmp_ptr == ' ' || *tmp_ptr == '\r' ||
+ *tmp_ptr == '\n' || *tmp_ptr == '\t')
+ tmp_ptr++;
+
+ if (*tmp_ptr == '\0')
+ continue;
+
+ title_length = 0;
+ start_ptr = tmp_ptr;
+ while (*start_ptr != ' ' && *start_ptr != '\'' &&
+ *start_ptr != '"' && *start_ptr != '\0' &&
+ *start_ptr != '\r' && *start_ptr != '\n' &&
+ *start_ptr != '\t')
+ {
+ title_length++;
+ start_ptr++;
+ }
+
+ switch (title_length)
+ {
+ case 4:
+ MRN_PARAM_STR_LIST("type", col_type, i);
+ break;
+ case 5:
+ MRN_PARAM_STR_LIST("flags", col_flags, i);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (param_string)
+ my_free(param_string, MYF(0));
+ DBUG_RETURN(0);
+
+error:
+ if (param_string)
+ my_free(param_string, MYF(0));
+error_alloc_param_string:
+ DBUG_RETURN(error);
+}
+
+int mrn_parse_column_param(MRN_SHARE *share, TABLE *table)
+{
+ int error;
+ MRN_DBUG_ENTER_FUNCTION();
+ for (uint i = 0; i < table->s->fields; i++)
+ {
+ Field *field = table->s->field[i];
+
+ if (LEX_STRING_IS_EMPTY(field->comment)) {
+ continue;
+ }
+
+ if ((error = mrn_add_column_param(share, field, i)))
+ goto error;
+ }
+ DBUG_RETURN(0);
+
+error:
+ DBUG_RETURN(error);
+}
+
+int mrn_free_share_alloc(
+ MRN_SHARE *share
+) {
+ uint i;
+ MRN_DBUG_ENTER_FUNCTION();
+ if (share->engine)
+ my_free(share->engine, MYF(0));
+ if (share->default_tokenizer)
+ my_free(share->default_tokenizer, MYF(0));
+ for (i = 0; i < share->table_share->keys; i++)
+ {
+ if (share->index_table && share->index_table[i])
+ my_free(share->index_table[i], MYF(0));
+ if (share->key_parser[i])
+ my_free(share->key_parser[i], MYF(0));
+ }
+ for (i = 0; i < share->table_share->fields; i++)
+ {
+ if (share->col_flags && share->col_flags[i])
+ my_free(share->col_flags[i], MYF(0));
+ if (share->col_type && share->col_type[i])
+ my_free(share->col_type[i], MYF(0));
+ }
+ DBUG_RETURN(0);
+}
+
+void mrn_free_long_term_share(MRN_LONG_TERM_SHARE *long_term_share)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ {
+ mrn::Lock lock(&mrn_long_term_share_mutex);
+ my_hash_delete(&mrn_long_term_share, (uchar*) long_term_share);
+ }
+ pthread_mutex_destroy(&long_term_share->auto_inc_mutex);
+ my_free(long_term_share, MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+MRN_LONG_TERM_SHARE *mrn_get_long_term_share(const char *table_name,
+ uint table_name_length,
+ int *error)
+{
+ MRN_LONG_TERM_SHARE *long_term_share;
+ char *tmp_name;
+ MRN_DBUG_ENTER_FUNCTION();
+ DBUG_PRINT("info", ("mroonga: table_name=%s", table_name));
+ mrn::Lock lock(&mrn_long_term_share_mutex);
+ if (!(long_term_share = (MRN_LONG_TERM_SHARE*)
+ my_hash_search(&mrn_long_term_share, (uchar*) table_name,
+ table_name_length)))
+ {
+ if (!(long_term_share = (MRN_LONG_TERM_SHARE *)
+ my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
+ &long_term_share, sizeof(*long_term_share),
+ &tmp_name, table_name_length + 1,
+ NullS))
+ ) {
+ *error = HA_ERR_OUT_OF_MEM;
+ goto error_alloc_long_term_share;
+ }
+ long_term_share->table_name = tmp_name;
+ long_term_share->table_name_length = table_name_length;
+ memcpy(long_term_share->table_name, table_name, table_name_length);
+ if (pthread_mutex_init(&long_term_share->auto_inc_mutex,
+ MY_MUTEX_INIT_FAST))
+ {
+ *error = HA_ERR_OUT_OF_MEM;
+ goto error_init_auto_inc_mutex;
+ }
+ if (my_hash_insert(&mrn_long_term_share, (uchar*) long_term_share))
+ {
+ *error = HA_ERR_OUT_OF_MEM;
+ goto error_hash_insert;
+ }
+ }
+ DBUG_RETURN(long_term_share);
+
+error_hash_insert:
+ pthread_mutex_destroy(&long_term_share->auto_inc_mutex);
+error_init_auto_inc_mutex:
+ my_free(long_term_share, MYF(0));
+error_alloc_long_term_share:
+ DBUG_RETURN(NULL);
+}
+
+MRN_SHARE *mrn_get_share(const char *table_name, TABLE *table, int *error)
+{
+ MRN_SHARE *share;
+ char *tmp_name, **index_table, **key_parser, **col_flags, **col_type;
+ uint length, *wrap_key_nr, *index_table_length;
+ uint *key_parser_length, *col_flags_length, *col_type_length, i, j;
+ KEY *wrap_key_info;
+ TABLE_SHARE *wrap_table_share;
+ MRN_DBUG_ENTER_FUNCTION();
+ length = (uint) strlen(table_name);
+ mrn::Lock lock(&mrn_open_tables_mutex);
+ if (!(share = (MRN_SHARE*) my_hash_search(&mrn_open_tables,
+ (uchar*) table_name, length)))
+ {
+ if (!(share = (MRN_SHARE *)
+ my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
+ &share, sizeof(*share),
+ &tmp_name, length + 1,
+ &index_table, sizeof(char *) * table->s->keys,
+ &index_table_length, sizeof(uint) * table->s->keys,
+ &key_parser, sizeof(char *) * table->s->keys,
+ &key_parser_length, sizeof(uint) * table->s->keys,
+ &col_flags, sizeof(char *) * table->s->fields,
+ &col_flags_length, sizeof(uint) * table->s->fields,
+ &col_type, sizeof(char *) * table->s->fields,
+ &col_type_length, sizeof(uint) * table->s->fields,
+ &wrap_key_nr, sizeof(*wrap_key_nr) * table->s->keys,
+ &wrap_key_info, sizeof(*wrap_key_info) * table->s->keys,
+ &wrap_table_share, sizeof(*wrap_table_share),
+ NullS))
+ ) {
+ *error = HA_ERR_OUT_OF_MEM;
+ goto error_alloc_share;
+ }
+ share->use_count = 0;
+ share->table_name_length = length;
+ share->table_name = tmp_name;
+ share->index_table = index_table;
+ share->index_table_length = index_table_length;
+ share->key_parser = key_parser;
+ share->key_parser_length = key_parser_length;
+ share->col_flags = col_flags;
+ share->col_flags_length = col_flags_length;
+ share->col_type = col_type;
+ share->col_type_length = col_type_length;
+ strmov(share->table_name, table_name);
+ share->table_share = table->s;
+
+ if (
+ (*error = mrn_parse_table_param(share, table)) ||
+ (*error = mrn_parse_column_param(share, table)) ||
+ (*error = mrn_parse_index_param(share, table))
+ )
+ goto error_parse_table_param;
+
+ if (share->wrapper_mode)
+ {
+ j = 0;
+ for (i = 0; i < table->s->keys; i++)
+ {
+ if (table->s->key_info[i].algorithm != HA_KEY_ALG_FULLTEXT &&
+ !mrn_is_geo_key(&table->s->key_info[i]))
+ {
+ wrap_key_nr[i] = j;
+ memcpy(&wrap_key_info[j], &table->s->key_info[i],
+ sizeof(*wrap_key_info));
+ j++;
+ } else {
+ wrap_key_nr[i] = MAX_KEY;
+ }
+ }
+ share->wrap_keys = j;
+ share->base_keys = table->s->keys;
+ share->base_key_info = table->s->key_info;
+ share->base_primary_key = table->s->primary_key;
+ if (i)
+ {
+ share->wrap_key_nr = wrap_key_nr;
+ share->wrap_key_info = wrap_key_info;
+ if (table->s->primary_key == MAX_KEY)
+ share->wrap_primary_key = MAX_KEY;
+ else
+ share->wrap_primary_key = wrap_key_nr[table->s->primary_key];
+ } else {
+ share->wrap_key_nr = NULL;
+ share->wrap_key_info = NULL;
+ share->wrap_primary_key = MAX_KEY;
+ }
+ memcpy(wrap_table_share, table->s, sizeof(*wrap_table_share));
+ wrap_table_share->keys = share->wrap_keys;
+ wrap_table_share->key_info = share->wrap_key_info;
+ wrap_table_share->primary_key = share->wrap_primary_key;
+ wrap_table_share->keys_in_use.init(share->wrap_keys);
+ wrap_table_share->keys_for_keyread.init(share->wrap_keys);
+#ifdef MRN_TABLE_SHARE_HAVE_LOCK_SHARE
+# ifdef WIN32
+ mysql_mutex_init(*mrn_table_share_lock_share,
+ &(wrap_table_share->LOCK_share), MY_MUTEX_INIT_SLOW);
+# else
+ mysql_mutex_init(key_TABLE_SHARE_LOCK_share,
+ &(wrap_table_share->LOCK_share), MY_MUTEX_INIT_SLOW);
+# endif
+#endif
+#ifdef MRN_TABLE_SHARE_HAVE_LOCK_HA_DATA
+# ifdef WIN32
+ mysql_mutex_init(*mrn_table_share_lock_ha_data,
+ &(wrap_table_share->LOCK_ha_data), MY_MUTEX_INIT_FAST);
+# else
+ mysql_mutex_init(key_TABLE_SHARE_LOCK_ha_data,
+ &(wrap_table_share->LOCK_ha_data), MY_MUTEX_INIT_FAST);
+# endif
+#endif
+ share->wrap_table_share = wrap_table_share;
+ }
+
+ if (pthread_mutex_init(&share->mutex, MY_MUTEX_INIT_FAST))
+ {
+ *error = HA_ERR_OUT_OF_MEM;
+ goto error_init_mutex;
+ }
+ thr_lock_init(&share->lock);
+ if (!(share->long_term_share = mrn_get_long_term_share(table_name, length,
+ error)))
+ {
+ goto error_get_long_term_share;
+ }
+ if (my_hash_insert(&mrn_open_tables, (uchar*) share))
+ {
+ *error = HA_ERR_OUT_OF_MEM;
+ goto error_hash_insert;
+ }
+ }
+ share->use_count++;
+ DBUG_RETURN(share);
+
+error_hash_insert:
+error_get_long_term_share:
+ pthread_mutex_destroy(&share->mutex);
+error_init_mutex:
+error_parse_table_param:
+ mrn_free_share_alloc(share);
+ my_free(share, MYF(0));
+error_alloc_share:
+ DBUG_RETURN(NULL);
+}
+
+int mrn_free_share(MRN_SHARE *share)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ mrn::Lock lock(&mrn_open_tables_mutex);
+ if (!--share->use_count)
+ {
+ my_hash_delete(&mrn_open_tables, (uchar*) share);
+ if (share->wrapper_mode)
+ plugin_unlock(NULL, share->plugin);
+ mrn_free_share_alloc(share);
+ thr_lock_delete(&share->lock);
+ pthread_mutex_destroy(&share->mutex);
+ if (share->wrapper_mode) {
+#ifdef MRN_TABLE_SHARE_HAVE_LOCK_SHARE
+ mysql_mutex_destroy(&(share->wrap_table_share->LOCK_share));
+#endif
+#ifdef MRN_TABLE_SHARE_HAVE_LOCK_HA_DATA
+ mysql_mutex_destroy(&(share->wrap_table_share->LOCK_ha_data));
+#endif
+ }
+ my_free(share, MYF(0));
+ }
+ DBUG_RETURN(0);
+}
+
+TABLE_SHARE *mrn_get_table_share(TABLE_LIST *table_list, int *error)
+{
+ uint key_length;
+ TABLE_SHARE *share;
+ THD *thd = current_thd;
+ MRN_DBUG_ENTER_FUNCTION();
+#ifdef MRN_HAVE_GET_TABLE_DEF_KEY
+ const char *key;
+ key_length = get_table_def_key(table_list, &key);
+#else
+ char key[MAX_DBKEY_LENGTH];
+ key_length = create_table_def_key(thd, key, table_list, FALSE);
+#endif
+#ifdef MRN_HAVE_TABLE_DEF_CACHE
+ my_hash_value_type hash_value;
+ hash_value = my_calc_hash(mrn_table_def_cache, (uchar*) key, key_length);
+ share = get_table_share(thd, table_list, key, key_length, 0, error,
+ hash_value);
+#elif defined(MRN_HAVE_TDC_ACQUIRE_SHARE)
+ share = tdc_acquire_share(thd, table_list->db, table_list->table_name, key,
+ key_length,
+ table_list->mdl_request.key.tc_hash_value(),
+ GTS_TABLE, NULL);
+#else
+ share = get_table_share(thd, table_list, key, key_length, 0, error);
+#endif
+ DBUG_RETURN(share);
+}
+
+TABLE_SHARE *mrn_create_tmp_table_share(TABLE_LIST *table_list, const char *path,
+ int *error)
+{
+ uint key_length;
+ TABLE_SHARE *share;
+ THD *thd = current_thd;
+
+ MRN_DBUG_ENTER_FUNCTION();
+#ifdef MRN_HAVE_GET_TABLE_DEF_KEY
+ const char *key;
+ key_length = get_table_def_key(table_list, &key);
+#else
+ char key[MAX_DBKEY_LENGTH];
+ key_length = create_table_def_key(thd, key, table_list, FALSE);
+#endif
+#if MYSQL_VERSION_ID >= 100002 && defined(MRN_MARIADB_P)
+ share = alloc_table_share(table_list->db, table_list->table_name, key,
+ key_length);
+#else
+ share = alloc_table_share(table_list, key, key_length);
+#endif
+ if (!share)
+ {
+ *error = ER_CANT_OPEN_FILE;
+ DBUG_RETURN(NULL);
+ }
+ share->tmp_table = INTERNAL_TMP_TABLE; // TODO: is this right?
+ share->path.str = (char *) path;
+ share->path.length = strlen(path);
+ share->normalized_path.str = share->path.str;
+ share->normalized_path.length = share->path.length;
+ if (open_table_def(thd, share, GTS_TABLE))
+ {
+ *error = ER_CANT_OPEN_FILE;
+ DBUG_RETURN(NULL);
+ }
+ DBUG_RETURN(share);
+}
+
+void mrn_free_tmp_table_share(TABLE_SHARE *tmp_table_share)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ free_table_share(tmp_table_share);
+ DBUG_VOID_RETURN;
+}
+
+KEY *mrn_create_key_info_for_table(MRN_SHARE *share, TABLE *table, int *error)
+{
+ uint *wrap_key_nr = share->wrap_key_nr, i, j;
+ KEY *wrap_key_info;
+ MRN_DBUG_ENTER_FUNCTION();
+ if (share->wrap_keys)
+ {
+ if (!(wrap_key_info = (KEY *)
+ my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
+ &wrap_key_info, sizeof(*wrap_key_info) * share->wrap_keys,
+ NullS))
+ ) {
+ *error = HA_ERR_OUT_OF_MEM;
+ DBUG_RETURN(NULL);
+ }
+ for (i = 0; i < table->s->keys; i++)
+ {
+ j = wrap_key_nr[i];
+ if (j < MAX_KEY)
+ {
+ memcpy(&wrap_key_info[j], &table->key_info[i],
+ sizeof(*wrap_key_info));
+ }
+ }
+ } else
+ wrap_key_info = NULL;
+ *error = 0;
+ DBUG_RETURN(wrap_key_info);
+}
+
+void mrn_set_bitmap_by_key(MY_BITMAP *map, KEY *key_info)
+{
+ uint i;
+ MRN_DBUG_ENTER_FUNCTION();
+ for (i = 0; i < KEY_N_KEY_PARTS(key_info); i++)
+ {
+ Field *field = key_info->key_part[i].field;
+ bitmap_set_bit(map, field->field_index);
+ }
+ DBUG_VOID_RETURN;
+}
+
+st_mrn_slot_data *mrn_get_slot_data(THD *thd, bool can_create)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ st_mrn_slot_data *slot_data =
+ (st_mrn_slot_data*) *thd_ha_data(thd, mrn_hton_ptr);
+ if (slot_data == NULL) {
+ slot_data = (st_mrn_slot_data*) malloc(sizeof(st_mrn_slot_data));
+ slot_data->last_insert_record_id = GRN_ID_NIL;
+ slot_data->first_alter_share = NULL;
+ slot_data->alter_create_info = NULL;
+ slot_data->disable_keys_create_info = NULL;
+ slot_data->alter_connect_string = NULL;
+ slot_data->alter_comment = NULL;
+ *thd_ha_data(thd, mrn_hton_ptr) = (void *) slot_data;
+ {
+ mrn::Lock lock(&mrn_allocated_thds_mutex);
+ if (my_hash_insert(&mrn_allocated_thds, (uchar*) thd))
+ {
+ free(slot_data);
+ DBUG_RETURN(NULL);
+ }
+ }
+ }
+ DBUG_RETURN(slot_data);
+}
+
+void mrn_clear_alter_share(THD *thd)
+{
+ MRN_DBUG_ENTER_FUNCTION();
+ st_mrn_slot_data *slot_data = mrn_get_slot_data(thd, FALSE);
+ if (slot_data) {
+ if (slot_data->first_alter_share) {
+ st_mrn_alter_share *tmp_alter_share;
+ st_mrn_alter_share *alter_share = slot_data->first_alter_share;
+ while (alter_share)
+ {
+ tmp_alter_share = alter_share->next;
+ mrn_free_tmp_table_share(alter_share->alter_share);
+ free(alter_share);
+ alter_share = tmp_alter_share;
+ }
+ slot_data->first_alter_share = NULL;
+ }
+ slot_data->alter_create_info = NULL;
+ slot_data->disable_keys_create_info = NULL;
+ if (slot_data->alter_connect_string) {
+ my_free(slot_data->alter_connect_string, MYF(0));
+ slot_data->alter_connect_string = NULL;
+ }
+ if (slot_data->alter_comment) {
+ my_free(slot_data->alter_comment, MYF(0));
+ slot_data->alter_comment = NULL;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/storage/mroonga/mrn_table.hpp b/storage/mroonga/mrn_table.hpp
new file mode 100644
index 00000000000..25fffa914a0
--- /dev/null
+++ b/storage/mroonga/mrn_table.hpp
@@ -0,0 +1,172 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2011-2013 Kentoku SHIBA
+ Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_TABLE_HPP_
+#define MRN_TABLE_HPP_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <groonga.h>
+
+typedef struct st_mroonga_long_term_share
+{
+ char *table_name;
+ uint table_name_length;
+
+ // for auto_increment (storage mode only)
+ pthread_mutex_t auto_inc_mutex;
+ bool auto_inc_inited;
+ ulonglong auto_inc_value;
+} MRN_LONG_TERM_SHARE;
+
+typedef struct st_mroonga_share
+{
+ char *table_name;
+ uint table_name_length;
+ uint use_count;
+ pthread_mutex_t mutex;
+ THR_LOCK lock;
+ TABLE_SHARE *table_share;
+ TABLE_SHARE *wrap_table_share;
+ MRN_LONG_TERM_SHARE *long_term_share;
+
+ char *engine;
+ int engine_length;
+ char *default_tokenizer;
+ int default_tokenizer_length;
+ plugin_ref plugin;
+ handlerton *hton;
+ char **index_table;
+ char **key_parser;
+ char **col_flags;
+ char **col_type;
+ uint *index_table_length;
+ uint *key_parser_length;
+ uint *col_flags_length;
+ uint *col_type_length;
+ uint *wrap_key_nr;
+ uint wrap_keys;
+ uint base_keys;
+ KEY *wrap_key_info;
+ KEY *base_key_info;
+ uint wrap_primary_key;
+ uint base_primary_key;
+ bool wrapper_mode;
+ bool disable_keys;
+} MRN_SHARE;
+
+struct st_mrn_alter_share
+{
+ char path[FN_REFLEN + 1];
+ TABLE_SHARE *alter_share;
+ st_mrn_alter_share *next;
+};
+
+struct st_mrn_slot_data
+{
+ grn_id last_insert_record_id;
+ st_mrn_alter_share *first_alter_share;
+ HA_CREATE_INFO *alter_create_info;
+ HA_CREATE_INFO *disable_keys_create_info;
+ char *alter_connect_string;
+ char *alter_comment;
+};
+
+#define MRN_SET_WRAP_ALTER_KEY(file, ha_alter_info) \
+ Alter_inplace_info::HA_ALTER_FLAGS base_handler_flags = ha_alter_info->handler_flags; \
+ KEY *base_key_info_buffer = ha_alter_info->key_info_buffer; \
+ uint base_key_count = ha_alter_info->key_count; \
+ uint base_index_drop_count = ha_alter_info->index_drop_count; \
+ KEY **base_index_drop_buffer = ha_alter_info->index_drop_buffer; \
+ uint base_index_add_count = ha_alter_info->index_add_count; \
+ uint *base_index_add_buffer = ha_alter_info->index_add_buffer; \
+ ha_alter_info->handler_flags = file->alter_handler_flags; \
+ ha_alter_info->key_info_buffer = file->alter_key_info_buffer; \
+ ha_alter_info->key_count = file->alter_key_count; \
+ ha_alter_info->index_drop_count = file->alter_index_drop_count; \
+ ha_alter_info->index_drop_buffer = &file->alter_index_drop_buffer; \
+ ha_alter_info->index_add_count = file->alter_index_add_count; \
+ ha_alter_info->index_add_buffer = file->alter_index_add_buffer;
+
+#define MRN_SET_BASE_ALTER_KEY(share, table_share) \
+ ha_alter_info->handler_flags = base_handler_flags; \
+ ha_alter_info->key_info_buffer = base_key_info_buffer; \
+ ha_alter_info->key_count = base_key_count; \
+ ha_alter_info->index_drop_count = base_index_drop_count; \
+ ha_alter_info->index_drop_buffer = base_index_drop_buffer; \
+ ha_alter_info->index_add_count = base_index_add_count; \
+ ha_alter_info->index_add_buffer = base_index_add_buffer;
+
+#define MRN_SET_WRAP_SHARE_KEY(share, table_share)
+/*
+ table_share->keys = share->wrap_keys; \
+ table_share->key_info = share->wrap_key_info; \
+ table_share->primary_key = share->wrap_primary_key;
+*/
+
+#define MRN_SET_BASE_SHARE_KEY(share, table_share)
+/*
+ table_share->keys = share->base_keys; \
+ table_share->key_info = share->base_key_info; \
+ table_share->primary_key = share->base_primary_key;
+*/
+
+#define MRN_SET_WRAP_TABLE_KEY(file, table) \
+ table->key_info = file->wrap_key_info; \
+ table->s = share->wrap_table_share;
+
+#define MRN_SET_BASE_TABLE_KEY(file, table) \
+ table->key_info = file->base_key_info; \
+ table->s = share->table_share;
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+void mrn_get_partition_info(const char *table_name, uint table_name_length,
+ const TABLE *table, partition_element **part_elem,
+ partition_element **sub_elem);
+#endif
+int mrn_parse_table_param(MRN_SHARE *share, TABLE *table);
+bool mrn_is_geo_key(const KEY *key_info);
+int mrn_add_index_param(MRN_SHARE *share, KEY *key_info, int i);
+int mrn_parse_index_param(MRN_SHARE *share, TABLE *table);
+int mrn_add_column_param(MRN_SHARE *share, Field *field, int i);
+int mrn_parse_column_param(MRN_SHARE *share, TABLE *table);
+MRN_SHARE *mrn_get_share(const char *table_name, TABLE *table, int *error);
+int mrn_free_share_alloc(MRN_SHARE *share);
+int mrn_free_share(MRN_SHARE *share);
+MRN_LONG_TERM_SHARE *mrn_get_long_term_share(const char *table_name,
+ uint table_name_length,
+ int *error);
+void mrn_free_long_term_share(MRN_LONG_TERM_SHARE *long_term_share);
+TABLE_SHARE *mrn_get_table_share(TABLE_LIST *table_list, int *error);
+TABLE_SHARE *mrn_create_tmp_table_share(TABLE_LIST *table_list, const char *path,
+ int *error);
+void mrn_free_tmp_table_share(TABLE_SHARE *table_share);
+KEY *mrn_create_key_info_for_table(MRN_SHARE *share, TABLE *table, int *error);
+void mrn_set_bitmap_by_key(MY_BITMAP *map, KEY *key_info);
+st_mrn_slot_data *mrn_get_slot_data(THD *thd, bool can_create);
+void mrn_clear_alter_share(THD *thd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MRN_TABLE_HPP_ */
diff --git a/storage/mroonga/mrn_version.h.in b/storage/mroonga/mrn_version.h.in
new file mode 100644
index 00000000000..dfa0e2dffd5
--- /dev/null
+++ b/storage/mroonga/mrn_version.h.in
@@ -0,0 +1,40 @@
+/*
+ Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MRN_VERSION_H_
+#define MRN_VERSION_H_
+
+/* Define mroonga version in string */
+#define MRN_VERSION "@MRN_VERSION@"
+
+/* Define mroonga version in hex */
+#define MRN_VERSION_IN_HEX @MRN_VERSION_IN_HEX@
+
+/* Define mroonga major version */
+#define MRN_VERSION_MAJOR @MRN_VERSION_MAJOR@
+
+/* Define mroonga minor version */
+#define MRN_VERSION_MINOR @MRN_VERSION_MINOR@
+
+/* Define mroonga micro version */
+#define MRN_VERSION_MICRO @MRN_VERSION_MICRO@
+
+/* Define to the full name and version of this package. */
+#define MRN_PACKAGE_STRING "@MRN_PACKAGE_STRING@"
+
+#endif /* MRN_VERSION_H_ */
diff --git a/storage/mroonga/mysql-test/Makefile.am b/storage/mroonga/mysql-test/Makefile.am
new file mode 100644
index 00000000000..9ea677b579a
--- /dev/null
+++ b/storage/mroonga/mysql-test/Makefile.am
@@ -0,0 +1,8 @@
+dist-hook:
+ if [ -n "`find mroonga -name '*.reject'`" ]; then \
+ echo "reject files exist"; \
+ exit 1; \
+ fi
+
+EXTRA_DIST = \
+ mroonga
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/check_freebsd.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/check_freebsd.inc
new file mode 100644
index 00000000000..89700a87cff
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/check_freebsd.inc
@@ -0,0 +1,19 @@
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--disable_query_log
+let $VERSION_COMPILE_OS_FREEBSD=`SELECT IF(@@version_compile_os like 'FREEBSD%', 1, 0);`;
+--enable_query_log
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/check_ha_mroonga_so.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/check_ha_mroonga_so.inc
new file mode 100644
index 00000000000..94cf42dd5ab
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/check_ha_mroonga_so.inc
@@ -0,0 +1,26 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_windows.inc
+
+--disable_query_log
+if ($VERSION_COMPILE_OS_WIN) {
+ let ha_mroonga_so='ha_mroonga.dll';
+}
+if (!$VERSION_COMPILE_OS_WIN) {
+ let ha_mroonga_so='ha_mroonga.so';
+}
+--enable_query_log
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/check_mariadb.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/check_mariadb.inc
new file mode 100644
index 00000000000..13b2f3439e1
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/check_mariadb.inc
@@ -0,0 +1,19 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--disable_query_log
+let $mariadb = `SELECT LOCATE('MariaDB', @@global.version) > 0`;
+--enable_query_log
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/check_osx.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/check_osx.inc
new file mode 100644
index 00000000000..8b8387a74fb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/check_osx.inc
@@ -0,0 +1,19 @@
+# Copyright(C) 2014 Toshihisa Tashiro
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--disable_query_log
+let $VERSION_COMPILE_OS_OSX=`SELECT IF(@@version_compile_os like 'osx%', 1, 0);`;
+--enable_query_log
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/check_version.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/check_version.inc
new file mode 100644
index 00000000000..b59a981d822
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/check_version.inc
@@ -0,0 +1,28 @@
+# Copyright(C) 2012-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--disable_query_log
+let $version_major_minor =
+ `SELECT CAST(SUBSTRING_INDEX(@@global.version, '.', 2) AS DECIMAL(4, 2))`;
+
+let $version_55 = `SELECT $version_major_minor = 5.5`;
+let $version_56 = `SELECT $version_major_minor = 5.6`;
+let $version_100 = `SELECT $version_major_minor = 10.0`;
+
+let $version_55_or_later = `SELECT $version_major_minor >= 5.5`;
+let $version_56_or_later = `SELECT $version_major_minor >= 5.6`;
+let $version_100_or_later = `SELECT $version_major_minor >= 10.0`;
+--enable_query_log
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/check_windows.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/check_windows.inc
new file mode 100644
index 00000000000..21e61000a06
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/check_windows.inc
@@ -0,0 +1,20 @@
+# Copyright(C) 2013 Kentoku SHIBA
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--disable_query_log
+let $VERSION_COMPILE_OS_WIN=`SELECT IF(@@version_compile_os like 'Win%', 1, 0)`;
+--enable_query_log
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_32bit.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_32bit.inc
new file mode 100644
index 00000000000..41ab6bf4899
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_32bit.inc
@@ -0,0 +1,28 @@
+# Copyright(C) 2013 Kentoku SHIBA
+# Copyright(C) 2014 Toshihisa Tashiro
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/skip_osx.inc
+
+disable_query_log;
+disable_warnings;
+let $VERSION_COMPILE_64BIT=
+ `SELECT IF(@@version_compile_machine like '%64%', 1, 0)`;
+enable_warnings;
+enable_query_log;
+if ($VERSION_COMPILE_64BIT) {
+ skip Need a 32 bit machine/binary;
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_64bit.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_64bit.inc
new file mode 100644
index 00000000000..3774de2f479
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_64bit.inc
@@ -0,0 +1,25 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+disable_query_log;
+disable_warnings;
+let $VERSION_COMPILE_64BIT=
+ `SELECT IF(@@version_compile_machine like '%64%', 1, 0)`;
+enable_warnings;
+enable_query_log;
+if (!$VERSION_COMPILE_64BIT) {
+ skip Need a 64 binary;
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_fractional_seconds.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_fractional_seconds.inc
new file mode 100644
index 00000000000..c4764b83c8a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_fractional_seconds.inc
@@ -0,0 +1,32 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_mariadb.inc
+--source ../../include/mroonga/check_version.inc
+
+if ($mariadb) {
+ let $fractional_seconds = 1;
+}
+
+if (!$mariadb) {
+ if ($version_56) {
+ let $fractional_seconds = `SELECT @@global.version >= '5.6'`;
+ }
+}
+
+if (!$fractional_seconds) {
+ skip fractional seconds in time values are available in MySQL version 5.6 or later or MariaDB;
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_freebsd.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_freebsd.inc
new file mode 100644
index 00000000000..dfe198ca9ba
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_freebsd.inc
@@ -0,0 +1,21 @@
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_freebsd.inc
+
+if (!$VERSION_COMPILE_OS_FREEBSD) {
+ skip Need OS FreeBSD;
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_fulltext_index_comment.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_fulltext_index_comment.inc
new file mode 100644
index 00000000000..e8c79936cc6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_fulltext_index_comment.inc
@@ -0,0 +1,25 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_version.inc
+
+if ($version_55_or_later) {
+ let $fulltext_index_comment = 1;
+}
+
+if (!$fulltext_index_comment) {
+ skip Fulltext index comment is available in version 5.5 or later;
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_mroonga.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_mroonga.inc
new file mode 100644
index 00000000000..2ebec2df8c4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_mroonga.inc
@@ -0,0 +1,44 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2013-2014 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_ha_mroonga_so.inc
+
+disable_query_log;
+
+let have_mroonga_storage_engine=`SELECT 1 FROM information_schema.plugins WHERE plugin_name = "mroonga"`;
+if (!$have_mroonga_storage_engine) {
+ eval INSTALL PLUGIN mroonga SONAME $ha_mroonga_so;
+ eval INSTALL PLUGIN mroonga_stats SONAME $ha_mroonga_so;
+}
+
+let have_default_storage_engine_variable=`SELECT 1 FROM information_schema.global_variables WHERE variable_name = "default_storage_engine"`;
+if ($have_default_storage_engine_variable) {
+ let original_default_storage_engine=`SELECT variable_value FROM information_schema.global_variables WHERE variable_name = "default_storage_engine"`;
+ set default_storage_engine=Mroonga;
+}
+if (!$have_default_storage_engine_variable) {
+ let original_storage_engine=`SELECT variable_value FROM information_schema.global_variables WHERE variable_name = "storage_engine"`;
+ set storage_engine=Mroonga;
+}
+
+let have_default_tmp_storage_engine_variable=`SELECT 1 FROM information_schema.global_variables WHERE variable_name = "default_tmp_storage_engine"`;
+if ($have_default_tmp_storage_engine_variable) {
+ let original_default_tmp_storage_engine=`SELECT variable_value FROM information_schema.global_variables WHERE variable_name = "default_tmp_storage_engine"`;
+ set default_tmp_storage_engine=Mroonga;
+}
+
+enable_query_log;
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_mroonga_deinit.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_mroonga_deinit.inc
new file mode 100644
index 00000000000..0b6b7081d00
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_mroonga_deinit.inc
@@ -0,0 +1,36 @@
+# Copyright(C) 2010-2014 Kentoku SHIBA
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+disable_query_log;
+
+if ($have_default_storage_engine_variable) {
+ eval set default_storage_engine=$original_default_storage_engine;
+}
+if (!$have_default_storage_engine_variable) {
+ eval set storage_engine=$original_storage_engine;
+}
+
+if ($have_default_tmp_storage_engine_variable) {
+ eval set default_tmp_storage_engine=$original_default_tmp_storage_engine;
+}
+
+if (!$have_mroonga_storage_engine) {
+ UNINSTALL PLUGIN mroonga_stats;
+ UNINSTALL PLUGIN mroonga;
+}
+
+enable_query_log;
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_mroonga_helper.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_mroonga_helper.inc
new file mode 100644
index 00000000000..1a7ec750288
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_mroonga_helper.inc
@@ -0,0 +1,17 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+let $MYSQLD_DATADIR= `select @@datadir`;
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_mysql.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_mysql.inc
new file mode 100644
index 00000000000..d054f0a9afd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_mysql.inc
@@ -0,0 +1,21 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_mariadb.inc
+
+if ($mariadb) {
+ skip This test is for MySQL;
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_100.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_100.inc
new file mode 100644
index 00000000000..5643d8c8c2d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_100.inc
@@ -0,0 +1,21 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_version.inc
+
+if (!$version_100) {
+ skip This test is for MariaDB version 10.0.x;
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_100_or_later.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_100_or_later.inc
new file mode 100644
index 00000000000..9c9faa00ea2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_100_or_later.inc
@@ -0,0 +1,21 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_version.inc
+
+if (!$version_100_or_later) {
+ skip This test is for MariaDB version 10.0.x or later;
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_55.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_55.inc
new file mode 100644
index 00000000000..b34f7876ed8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_55.inc
@@ -0,0 +1,21 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_version.inc
+
+if (!$version_55) {
+ skip This test is for MySQL version 5.5.x;
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_56.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_56.inc
new file mode 100644
index 00000000000..4ecff3e4466
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_56.inc
@@ -0,0 +1,21 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_version.inc
+
+if (!$version_56) {
+ skip This test is for MySQL version 5.6.x;
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_56_or_later.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_56_or_later.inc
new file mode 100644
index 00000000000..ef166fcf590
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/have_version_56_or_later.inc
@@ -0,0 +1,21 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_version.inc
+
+if (!$version_56_or_later) {
+ skip This test is for MySQL version 5.6.x or later;
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/load_mroonga_functions.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/load_mroonga_functions.inc
new file mode 100644
index 00000000000..6df4e88ffc8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/load_mroonga_functions.inc
@@ -0,0 +1,24 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_ha_mroonga_so.inc
+
+--disable_query_log
+eval CREATE FUNCTION last_insert_grn_id RETURNS INTEGER SONAME $ha_mroonga_so;
+eval CREATE FUNCTION mroonga_snippet RETURNS STRING SONAME $ha_mroonga_so;
+eval CREATE FUNCTION mroonga_command RETURNS STRING SONAME $ha_mroonga_so;
+eval CREATE FUNCTION mroonga_escape RETURNS STRING SONAME $ha_mroonga_so;
+--enable_query_log
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/skip_freebsd.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/skip_freebsd.inc
new file mode 100644
index 00000000000..0c48adfee92
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/skip_freebsd.inc
@@ -0,0 +1,21 @@
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_freebsd.inc
+
+if ($VERSION_COMPILE_OS_FREEBSD) {
+ skip This test is not for FreeBSD;
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/skip_mariadb_55.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/skip_mariadb_55.inc
new file mode 100644
index 00000000000..8b46d606eec
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/skip_mariadb_55.inc
@@ -0,0 +1,24 @@
+# Copyright(C) 2012-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_version.inc
+--source ../../include/mroonga/check_mariadb.inc
+
+if ($version_55) {
+ if ($mariadb) {
+ skip This test is not for MariaDB 5.5.x;
+ }
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/skip_osx.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/skip_osx.inc
new file mode 100644
index 00000000000..a8f4409f7e7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/skip_osx.inc
@@ -0,0 +1,21 @@
+# Copyright(C) 2014 Toshihisa Tashiro
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/check_osx.inc
+
+if ($VERSION_COMPILE_OS_OSX) {
+ skip This test is not for OSX;
+}
diff --git a/storage/mroonga/mysql-test/mroonga/include/mroonga/unload_mroonga_functions.inc b/storage/mroonga/mysql-test/mroonga/include/mroonga/unload_mroonga_functions.inc
new file mode 100644
index 00000000000..881aa47c629
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/include/mroonga/unload_mroonga_functions.inc
@@ -0,0 +1,22 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--disable_query_log
+DROP FUNCTION last_insert_grn_id;
+DROP FUNCTION mroonga_snippet;
+DROP FUNCTION mroonga_command;
+DROP FUNCTION mroonga_escape;
+--enable_query_log
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_after.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_after.result
new file mode 100644
index 00000000000..52a72155af3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_after.result
@@ -0,0 +1,26 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+ALTER TABLE diaries ADD title TEXT AFTER id;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+id title body
+1 groonga (1) starting groonga.
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_first.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_first.result
new file mode 100644
index 00000000000..81feeefc589
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_first.result
@@ -0,0 +1,26 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+ALTER TABLE diaries ADD title TEXT FIRST;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `title` text,
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+title id body
+groonga (1) 1 starting groonga.
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_multiple.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_multiple.result
new file mode 100644
index 00000000000..bb157539ac9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_multiple.result
@@ -0,0 +1,44 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title) VALUES ("survey");
+SELECT * FROM diaries;
+id title
+1 survey
+ALTER TABLE diaries
+ADD COLUMN body TEXT FIRST,
+ADD COLUMN published BOOLEAN AFTER id,
+ADD COLUMN created_at DATETIME;
+UPDATE diaries SET body = "will start groonga!";
+SELECT * FROM diaries;
+body id published title created_at
+will start groonga! 1 0 survey 1970-01-01 00:00:00
+INSERT INTO diaries (title, body, published, created_at)
+VALUES ("groonga (1)", "starting groonga...", TRUE, "2014-2-9 02:09:00");
+INSERT INTO diaries (title, body, published, created_at)
+VALUES ("groonga (2)", "started groonga.", FALSE, "2014-2-9 12:19:00");
+SELECT * FROM diaries;
+body id published title created_at
+will start groonga! 1 0 survey 1970-01-01 00:00:00
+starting groonga... 2 1 groonga (1) 2014-02-09 02:09:00
+started groonga. 3 0 groonga (2) 2014-02-09 12:19:00
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `body` text,
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `published` tinyint(1) DEFAULT NULL,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_plain.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_plain.result
new file mode 100644
index 00000000000..df5a15568d8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_plain.result
@@ -0,0 +1,37 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title) VALUES ("survey");
+SELECT * FROM diaries;
+id title
+1 survey
+ALTER TABLE diaries ADD COLUMN body TEXT;
+UPDATE diaries SET body = "will start groonga!";
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) values ("groonga (2)", "started groonga.");
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_with_flags.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_with_flags.result
new file mode 100644
index 00000000000..1bc15c6efe2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_with_flags.result
@@ -0,0 +1,10 @@
+CREATE TABLE tags (
+id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
+) DEFAULT CHARSET=utf8;
+ALTER TABLE tags ADD COLUMN name VARCHAR(64) COMMENT 'flags "COLUMN_VECTOR"';
+SELECT mroonga_command("dump");
+mroonga_command("dump")
+table_create tags TABLE_PAT_KEY UInt32
+column_create tags id COLUMN_SCALAR UInt32
+column_create tags name COLUMN_VECTOR ShortText
+DROP TABLE tags;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_with_type.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_with_type.result
new file mode 100644
index 00000000000..6ff92ec8e99
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_column_with_type.result
@@ -0,0 +1,16 @@
+CREATE TABLE tags (
+id INT UNSIGNED PRIMARY KEY
+) DEFAULT CHARSET=utf8;
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY
+) DEFAULT CHARSET=utf8;
+ALTER TABLE bugs ADD COLUMN name VARCHAR(64) COMMENT 'type "tags"';
+SELECT mroonga_command("dump");
+mroonga_command("dump")
+table_create tags TABLE_PAT_KEY UInt32
+column_create tags id COLUMN_SCALAR UInt32
+table_create bugs TABLE_PAT_KEY UInt32
+column_create bugs id COLUMN_SCALAR UInt32
+column_create bugs name COLUMN_SCALAR tags
+DROP TABLE bugs;
+DROP TABLE tags;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_key_multiple_column_with_data.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_key_multiple_column_with_data.result
new file mode 100644
index 00000000000..73fb6a7abe3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_add_key_multiple_column_with_data.result
@@ -0,0 +1,31 @@
+DROP TABLE IF EXISTS scores;
+SET NAMES UTF8;
+CREATE TABLE scores (
+id BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL,
+name CHAR(30) NOT NULL,
+score INT NOT NULL
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE scores;
+Table Create Table
+scores CREATE TABLE `scores` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `name` char(30) NOT NULL,
+ `score` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 29);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", -12);
+INSERT INTO scores (name, score) VALUES("Jiro Yamada", 27);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 10);
+SELECT * FROM scores
+WHERE name = "Taro Yamada" AND (score >= -12 AND score < 29);
+id name score
+2 Taro Yamada -12
+4 Taro Yamada 10
+ALTER TABLE scores ADD KEY property (name, score);
+SELECT * FROM scores
+WHERE name = "Taro Yamada" AND (score >= -12 AND score < 29);
+id name score
+2 Taro Yamada -12
+4 Taro Yamada 10
+DROP TABLE scores;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_comment_not_for_mroonga.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_comment_not_for_mroonga.result
new file mode 100644
index 00000000000..a993756ad62
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_comment_not_for_mroonga.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS bugs;
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tag VARCHAR(64)
+) DEFAULT CHARSET=utf8;
+ALTER TABLE bugs
+CHANGE COLUMN
+tag
+tag VARCHAR(64) COMMENT 'It must consist of only alphabet and number.';
+SHOW CREATE TABLE bugs;
+Table Create Table
+bugs CREATE TABLE `bugs` (
+ `id` int(10) unsigned NOT NULL,
+ `tag` varchar(64) DEFAULT NULL COMMENT 'It must consist of only alphabet and number.',
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+DROP TABLE bugs;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_have_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_have_index.result
new file mode 100644
index 00000000000..c51c10b6da3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_have_index.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS bugs;
+CREATE TABLE bugs (
+id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+title VARCHAR(32),
+FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+ALTER TABLE bugs CHANGE COLUMN title title VARCHAR(64);
+SHOW CREATE TABLE bugs;
+Table Create Table
+bugs CREATE TABLE `bugs` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `title` varchar(64) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+DROP TABLE bugs;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_after.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_after.result
new file mode 100644
index 00000000000..19d5d017fb2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_after.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+ALTER TABLE diaries CHANGE body description TEXT AFTER id;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `description` text,
+ `title` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, description) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+id description title
+1 starting groonga. groonga (1)
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_first.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_first.result
new file mode 100644
index 00000000000..cf2bcc0fc2c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_first.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+ALTER TABLE diaries CHANGE body description TEXT FIRST;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `description` text,
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, description) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+description id title
+starting groonga. 1 groonga (1)
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_multiple.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_multiple.result
new file mode 100644
index 00000000000..bc5b0132e43
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_multiple.result
@@ -0,0 +1,32 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+ALTER TABLE diaries
+CHANGE body description TEXT FIRST,
+CHANGE title subject TEXT AFTER internal_id,
+CHANGE id internal_id INT;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `description` text,
+ `internal_id` int(11) NOT NULL DEFAULT '0',
+ `subject` text,
+ PRIMARY KEY (`internal_id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (subject, description)
+VALUES ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+description internal_id subject
+starting groonga. 0 groonga (1)
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_no_order.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_no_order.result
new file mode 100644
index 00000000000..6d60200e5ce
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_column_rename_no_order.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+ALTER TABLE diaries CHANGE body description TEXT;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `description` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, description) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+id title description
+1 groonga (1) starting groonga.
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_engine.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_engine.result
new file mode 100644
index 00000000000..3a7413e389b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_change_engine.result
@@ -0,0 +1,49 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT,
+FULLTEXT INDEX title_index (title),
+FULLTEXT INDEX body_index (body)
+) ENGINE MyISAM DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE) AND
+MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+id title body
+1 survey will start groonga!
+ALTER TABLE diaries ENGINE = mroonga;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE) AND
+MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+id title body
+1 survey will start groonga!
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("groonga" IN BOOLEAN MODE) AND
+MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+id title body
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_create_fulltext.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_create_fulltext.result
new file mode 100644
index 00000000000..6127df61cb4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_create_fulltext.result
@@ -0,0 +1,27 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+SELECT *
+FROM diaries
+WHERE MATCH (title) AGAINST ("富士山");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+CREATE FULLTEXT INDEX title_index on diaries (title);
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+id title
+3 富士山
+ALTER TABLE diaries DISABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_fulltext_ujis.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_fulltext_ujis.result
new file mode 100644
index 00000000000..ff7bc5e7cea
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_fulltext_ujis.result
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES ujis;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+FULLTEXT KEY title_index (title)
+) DEFAULT CHARSET=ujis;
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "ŷ");
+INSERT INTO diaries VALUES (3, "ٻλ");
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("ٻλ");
+id title
+3 ٻλ
+ALTER TABLE diaries DISABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("ٻλ");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_fulltext_utf8.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_fulltext_utf8.result
new file mode 100644
index 00000000000..cbaa8d62c98
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_fulltext_utf8.result
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+FULLTEXT KEY title_index (title)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+id title
+3 富士山
+ALTER TABLE diaries DISABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_multiple_column.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_multiple_column.result
new file mode 100644
index 00000000000..9d6cdcd9ba8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_multiple_column.result
@@ -0,0 +1,27 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+created_at datetime,
+KEY title_and_created_at_index (title, created_at)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+SELECT *
+FROM diaries
+FORCE INDEX (title_and_created_at_index)
+WHERE title = "天気" AND
+created_at = "2012-04-30 23:00:00";
+id title created_at
+2 天気 2012-04-30 23:00:00
+ALTER TABLE diaries DISABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (title_and_created_at_index)
+WHERE title = "天気" AND
+created_at = "2012-04-30 23:00:00";
+id title created_at
+2 天気 2012-04-30 23:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_normal.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_normal.result
new file mode 100644
index 00000000000..09399c12a3e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_normal.result
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+created_at datetime,
+KEY created_at_index (created_at)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+SELECT *
+FROM diaries
+FORCE INDEX (created_at_index)
+WHERE created_at = "2012-04-30 20:00:00";
+id title created_at
+1 Hello 2012-04-30 20:00:00
+ALTER TABLE diaries DISABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (created_at_index)
+WHERE created_at = "2012-04-30 20:00:00";
+id title created_at
+1 Hello 2012-04-30 20:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_primary.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_primary.result
new file mode 100644
index 00000000000..f94c98ff9b2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_primary.result
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+SELECT *
+FROM diaries
+FORCE INDEX (PRIMARY)
+WHERE id = 2;
+id title
+2 天気
+ALTER TABLE diaries DISABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (PRIMARY)
+WHERE id = 2;
+id title
+2 天気
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_truncate.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_truncate.result
new file mode 100644
index 00000000000..7b71832b84f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_truncate.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS users;
+SET NAMES utf8;
+CREATE TABLE users (
+first_name VARCHAR(32) NOT NULL,
+last_name VARCHAR(32) NOT NULL,
+KEY (first_name, last_name)
+);
+INSERT INTO users VALUES("Taro", "Yamada");
+INSERT INTO users VALUES("Hanako", "Tanaka");
+INSERT INTO users VALUES("Joe", "Honda");
+SELECT * FROM users;
+first_name last_name
+Taro Yamada
+Hanako Tanaka
+Joe Honda
+ALTER TABLE users DISABLE KEYS;
+TRUNCATE users;
+SELECT * FROM users;
+first_name last_name
+DROP TABLE users;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_updating.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_updating.result
new file mode 100644
index 00000000000..8b6f94c0cb5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_disable_keys_updating.result
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (
+c1 int NOT NULL,
+c2 text NOT NULL,
+c3 int NOT NULL,
+c4 int NOT NULL,
+PRIMARY KEY(c1),
+KEY idx1(c3,c4),
+FULLTEXT KEY ft1(c2)
+);
+INSERT INTO t1 VALUES(1, 'test1', 1, 1);
+INSERT INTO t1 VALUES(2, 'test2', 2, 2);
+INSERT INTO t1 VALUES(3, 'test3', 1, 3);
+ALTER TABLE t1 DISABLE KEYS;
+DELETE FROM t1 WHERE c1 = 2;
+UPDATE t1 SET c4 = 4 WHERE c1 = 1;
+INSERT INTO t1 VALUES(4, 'test4', 2, 4);
+DROP TABLE t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_drop_column_multiple.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_drop_column_multiple.result
new file mode 100644
index 00000000000..6475de51b21
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_drop_column_multiple.result
@@ -0,0 +1,36 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+ALTER TABLE diaries
+DROP COLUMN title,
+DROP COLUMN body;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
+SELECT * FROM diaries;
+id
+1
+INSERT INTO diaries () VALUES ();
+SELECT * FROM diaries;
+id
+1
+2
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_drop_column_one.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_drop_column_one.result
new file mode 100644
index 00000000000..cbc94cebccf
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_drop_column_one.result
@@ -0,0 +1,37 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+ALTER TABLE diaries DROP COLUMN body;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
+SELECT * FROM diaries;
+id title
+1 survey
+INSERT INTO diaries (title) values ("groonga (1)");
+INSERT INTO diaries (title) values ("groonga (2)");
+SELECT * FROM diaries;
+id title
+1 survey
+2 groonga (1)
+3 groonga (2)
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_drop_key_multiple_column_with_data.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_drop_key_multiple_column_with_data.result
new file mode 100644
index 00000000000..b0aa59a20b5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_drop_key_multiple_column_with_data.result
@@ -0,0 +1,33 @@
+DROP TABLE IF EXISTS scores;
+SET NAMES UTF8;
+CREATE TABLE scores (
+id BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL,
+name CHAR(30) NOT NULL,
+score INT NOT NULL,
+KEY property (name, score)
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE scores;
+Table Create Table
+scores CREATE TABLE `scores` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `name` char(30) NOT NULL,
+ `score` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `property` (`name`,`score`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 29);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", -12);
+INSERT INTO scores (name, score) VALUES("Jiro Yamada", 27);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 10);
+SELECT * FROM scores
+WHERE name = "Taro Yamada" AND (score >= -12 AND score < 29);
+id name score
+2 Taro Yamada -12
+4 Taro Yamada 10
+ALTER TABLE scores DROP KEY property;
+SELECT * FROM scores
+WHERE name = "Taro Yamada" AND (score >= -12 AND score < 29);
+id name score
+2 Taro Yamada -12
+4 Taro Yamada 10
+DROP TABLE scores;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_fulltext.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_fulltext.result
new file mode 100644
index 00000000000..e5c8a34901f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_fulltext.result
@@ -0,0 +1,24 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+FULLTEXT KEY title_index (title)
+) DEFAULT CHARSET=utf8;
+ALTER TABLE diaries DISABLE KEYS;
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+ALTER TABLE diaries ENABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+id title
+3 富士山
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_fulltext_ujis.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_fulltext_ujis.result
new file mode 100644
index 00000000000..3853cc849fb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_fulltext_ujis.result
@@ -0,0 +1,24 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES ujis;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+FULLTEXT KEY title_index (title)
+) DEFAULT CHARSET=ujis;
+ALTER TABLE diaries DISABLE KEYS;
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "ŷ");
+INSERT INTO diaries VALUES (3, "ٻλ");
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("ٻλ");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+ALTER TABLE diaries ENABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("ٻλ");
+id title
+3 ٻλ
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_fulltext_utf8.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_fulltext_utf8.result
new file mode 100644
index 00000000000..e5c8a34901f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_fulltext_utf8.result
@@ -0,0 +1,24 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+FULLTEXT KEY title_index (title)
+) DEFAULT CHARSET=utf8;
+ALTER TABLE diaries DISABLE KEYS;
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+ALTER TABLE diaries ENABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+id title
+3 富士山
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_multiple_column.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_multiple_column.result
new file mode 100644
index 00000000000..e252061d109
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_multiple_column.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+created_at datetime,
+KEY title_and_created_at_index (title, created_at)
+) DEFAULT CHARSET=utf8;
+ALTER TABLE diaries DISABLE KEYS;
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+SELECT *
+FROM diaries
+FORCE INDEX (title_and_created_at_index)
+WHERE title = "天気" AND
+created_at = "2012-04-30 23:00:00";
+id title created_at
+2 天気 2012-04-30 23:00:00
+ALTER TABLE diaries ENABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (title_and_created_at_index)
+WHERE title = "天気" AND
+created_at = "2012-04-30 23:00:00";
+id title created_at
+2 天気 2012-04-30 23:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_normal.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_normal.result
new file mode 100644
index 00000000000..0e56e78d0c6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_normal.result
@@ -0,0 +1,26 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+created_at datetime,
+KEY created_at_index (created_at)
+) DEFAULT CHARSET=utf8;
+ALTER TABLE diaries DISABLE KEYS;
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+SELECT *
+FROM diaries
+FORCE INDEX (created_at_index)
+WHERE created_at = "2012-04-30 20:00:00";
+id title created_at
+1 Hello 2012-04-30 20:00:00
+ALTER TABLE diaries ENABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (created_at_index)
+WHERE created_at = "2012-04-30 20:00:00";
+id title created_at
+1 Hello 2012-04-30 20:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_primary.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_primary.result
new file mode 100644
index 00000000000..722e62f966b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_enable_keys_primary.result
@@ -0,0 +1,24 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255)
+) DEFAULT CHARSET=utf8;
+ALTER TABLE diaries DISABLE KEYS;
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+SELECT *
+FROM diaries
+FORCE INDEX (PRIMARY)
+WHERE id = 2;
+id title
+2 天気
+ALTER TABLE diaries ENABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (PRIMARY)
+WHERE id = 2;
+id title
+2 天気
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_engine_decimal.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_engine_decimal.result
new file mode 100644
index 00000000000..6bbedd41e95
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_engine_decimal.result
@@ -0,0 +1,38 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+temperature DECIMAL(6, 3)
+) ENGINE InnoDB DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `temperature` decimal(6,3) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, temperature) VALUES ("clear day", 21.281);
+SELECT * FROM diaries;
+id title temperature
+1 clear day 21.281
+ALTER TABLE diaries ENGINE = mroonga;
+SELECT * FROM diaries;
+id title temperature
+1 clear day 21.281
+INSERT INTO diaries (title, temperature) VALUES ("rainy day", 14.213);
+INSERT INTO diaries (title, temperature) VALUES ("cloudy day", 17.821);
+SELECT * FROM diaries;
+id title temperature
+1 clear day 21.281
+2 rainy day 14.213
+3 cloudy day 17.821
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `temperature` decimal(6,3) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_add_no_primary_key.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_add_no_primary_key.result
new file mode 100644
index 00000000000..bf7593f78eb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_add_no_primary_key.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS memos;
+CREATE TABLE memos (
+content varchar(32)
+) DEFAULT CHARSET="utf8";
+INSERT INTO memos (content) values ("Starting Groonga...");
+INSERT INTO memos (content) values ("Started Groonga.");
+INSERT INTO memos (content) values ("Starting Mroonga...");
+ALTER TABLE memos ADD FULLTEXT INDEX content_index (content);
+SHOW CREATE TABLE memos;
+Table Create Table
+memos CREATE TABLE `memos` (
+ `content` varchar(32) DEFAULT NULL,
+ FULLTEXT KEY `content_index` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+SELECT * FROM memos WHERE MATCH(content) AGAINST("groonga");
+content
+Starting Groonga...
+Started Groonga.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_add_normal.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_add_normal.result
new file mode 100644
index 00000000000..d98dc431a0b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_add_normal.result
@@ -0,0 +1,22 @@
+DROP TABLE IF EXISTS memos;
+CREATE TABLE memos (
+id INT PRIMARY KEY AUTO_INCREMENT,
+content TEXT
+) DEFAULT CHARSET="utf8";
+INSERT INTO memos (content) values ("Starting Groonga...");
+INSERT INTO memos (content) values ("Started Groonga.");
+INSERT INTO memos (content) values ("Starting Mroonga...");
+ALTER TABLE memos ADD FULLTEXT INDEX content_index (content);
+SHOW CREATE TABLE memos;
+Table Create Table
+memos CREATE TABLE `memos` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `content_index` (`content`)
+) ENGINE=Mroonga AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
+SELECT * FROM memos WHERE MATCH(content) AGAINST("groonga");
+id content
+1 Starting Groonga...
+2 Started Groonga.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_add_table.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_add_table.result
new file mode 100644
index 00000000000..705d2f70126
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_add_table.result
@@ -0,0 +1,21 @@
+DROP TABLE IF EXISTS tags;
+DROP TABLE IF EXISTS bugs;
+CREATE TABLE tags (
+name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8 COMMENT='default_tokenizer "TokenDelimit"';
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tags VARCHAR(40) COMMENT 'type "tags"'
+) DEFAULT CHARSET=utf8;
+INSERT INTO tags (name) VALUES ("Groonga");
+INSERT INTO bugs (id, tags) VALUES (1, "Groonga Mroonga");
+SELECT * FROM bugs;
+id tags
+1 GROONGA MROONGA
+ALTER TABLE bugs ADD FULLTEXT INDEX bugs_tags_index (tags) COMMENT 'table "tags"';
+SELECT * FROM bugs
+WHERE MATCH(tags) AGAINST("Groonga");
+id tags
+1 GROONGA MROONGA
+DROP TABLE bugs;
+DROP TABLE tags;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_drop_table.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_drop_table.result
new file mode 100644
index 00000000000..2d5e3d55ca7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_fulltext_drop_table.result
@@ -0,0 +1,21 @@
+DROP TABLE IF EXISTS tags;
+DROP TABLE IF EXISTS bugs;
+CREATE TABLE tags (
+name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8 COMMENT='default_tokenizer "TokenDelimit"';
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tags VARCHAR(40) COMMENT 'type "tags"',
+FULLTEXT INDEX bugs_tags_index (tags) COMMENT 'table "tags"'
+) DEFAULT CHARSET=utf8;
+INSERT INTO tags (name) VALUES ("Groonga");
+INSERT INTO bugs (id, tags) VALUES (1, "Groonga Mroonga");
+ALTER TABLE bugs DROP INDEX bugs_tags_index;
+ALTER TABLE bugs
+ADD FULLTEXT INDEX bugs_tags_index (tags) COMMENT 'table "tags"';
+SELECT * FROM bugs
+WHERE MATCH(tags) AGAINST("Groonga");
+id tags
+1 GROONGA MROONGA
+DROP TABLE bugs;
+DROP TABLE tags;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_modify_column_after.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_modify_column_after.result
new file mode 100644
index 00000000000..d831d02d76a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_modify_column_after.result
@@ -0,0 +1,33 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+id title body
+1 groonga (1) starting groonga.
+ALTER TABLE diaries MODIFY body TEXT AFTER id;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `body` text,
+ `title` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) values ("groonga (2)", "started groonga.");
+SELECT * FROM diaries;
+id body title
+1 starting groonga. groonga (1)
+2 started groonga. groonga (2)
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_modify_column_first.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_modify_column_first.result
new file mode 100644
index 00000000000..bd936507211
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_modify_column_first.result
@@ -0,0 +1,33 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+id title body
+1 groonga (1) starting groonga.
+ALTER TABLE diaries MODIFY body TEXT FIRST;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `body` text,
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) values ("groonga (2)", "started groonga.");
+SELECT * FROM diaries;
+body id title
+starting groonga. 1 groonga (1)
+started groonga. 2 groonga (2)
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_modify_column_no_order.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_modify_column_no_order.result
new file mode 100644
index 00000000000..d8d963d87b4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_modify_column_no_order.result
@@ -0,0 +1,33 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+id title body
+1 groonga (1) starting groonga.
+ALTER TABLE diaries MODIFY title VARCHAR(100);
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` varchar(100) DEFAULT NULL,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) values ("groonga (2)", "started groonga.");
+SELECT * FROM diaries;
+id title body
+1 groonga (1) starting groonga.
+2 groonga (2) started groonga.
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_recreate_anonymous_index_at_once.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_recreate_anonymous_index_at_once.result
new file mode 100644
index 00000000000..64f524de06c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_recreate_anonymous_index_at_once.result
@@ -0,0 +1,47 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT,
+FULLTEXT INDEX (body)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `body` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("survey", "will start mroonga!");
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+2 survey will start mroonga!
+SELECT * FROM diaries
+WHERE MATCH (body) AGAINST ("+groonga" IN BOOLEAN MODE);
+id title body
+1 survey will start groonga!
+ALTER TABLE diaries
+DROP INDEX body,
+ADD FULLTEXT INDEX (body);
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+2 survey will start mroonga!
+SELECT * FROM diaries
+WHERE MATCH (body) AGAINST ("+groonga" IN BOOLEAN MODE);
+id title body
+1 survey will start groonga!
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `body` (`body`)
+) ENGINE=Mroonga AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_rename_table.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_rename_table.result
new file mode 100644
index 00000000000..eac322ceb26
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_rename_table.result
@@ -0,0 +1,45 @@
+DROP TABLE IF EXISTS diaries, memos;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT,
+FULLTEXT INDEX title_index (title),
+FULLTEXT INDEX body_index (body)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("groonga") AND
+MATCH(body) AGAINST("starting");
+id title body
+ALTER TABLE diaries RENAME memos;
+SELECT * FROM memos;
+id title body
+1 survey will start groonga!
+SELECT * FROM memos
+WHERE MATCH(title) AGAINST("groonga") AND
+MATCH(body) AGAINST("starting");
+id title body
+SHOW CREATE TABLE memos;
+Table Create Table
+memos CREATE TABLE `memos` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_spatial.result b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_spatial.result
new file mode 100644
index 00000000000..d8ed80a157d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/alter_table_spatial.result
@@ -0,0 +1,132 @@
+DROP TABLE IF EXISTS shops;
+CREATE TABLE shops (
+id INT PRIMARY KEY AUTO_INCREMENT,
+name TEXT,
+location GEOMETRY NOT NULL
+);
+INSERT INTO shops (name, location)
+VALUES ('nezu-no-taiyaki',
+GeomFromText('POINT(139.762573 35.720253)'));
+INSERT INTO shops (name, location)
+VALUES ('taiyaki-kataoka',
+GeomFromText('POINT(139.715591 35.712521)'));
+INSERT INTO shops (name, location)
+VALUES ('soba-taiyaki-ku',
+GeomFromText('POINT(139.659088 35.683712)'));
+INSERT INTO shops (name, location)
+VALUES ('kuruma',
+GeomFromText('POINT(139.706207 35.721516)'));
+INSERT INTO shops (name, location)
+VALUES ('hirose-ya',
+GeomFromText('POINT(139.685608 35.714844)'));
+INSERT INTO shops (name, location)
+VALUES ('sazare',
+GeomFromText('POINT(139.685043 35.714653)'));
+INSERT INTO shops (name, location)
+VALUES ('omede-taiyaki',
+GeomFromText('POINT(139.817154 35.700516)'));
+INSERT INTO shops (name, location)
+VALUES ('onaga-ya',
+GeomFromText('POINT(139.81105 35.698254)'));
+INSERT INTO shops (name, location)
+VALUES ('shiro-ya',
+GeomFromText('POINT(139.638611 35.705517)'));
+INSERT INTO shops (name, location)
+VALUES ('fuji-ya',
+GeomFromText('POINT(139.637115 35.703938)'));
+INSERT INTO shops (name, location)
+VALUES ('miyoshi',
+GeomFromText('POINT(139.537323 35.644539)'));
+INSERT INTO shops (name, location)
+VALUES ('juju-ya',
+GeomFromText('POINT(139.695755 35.628922)'));
+INSERT INTO shops (name, location)
+VALUES ('tatsumi-ya',
+GeomFromText('POINT(139.638657 35.665501)'));
+INSERT INTO shops (name, location)
+VALUES ('tetsuji',
+GeomFromText('POINT(139.76857 35.680912)'));
+INSERT INTO shops (name, location)
+VALUES ('gazuma-ya',
+GeomFromText('POINT(139.647598 35.700817)'));
+INSERT INTO shops (name, location)
+VALUES ('honma-mon',
+GeomFromText('POINT(139.652573 35.722736)'));
+INSERT INTO shops (name, location)
+VALUES ('naniwa-ya',
+GeomFromText('POINT(139.796234 35.730061)'));
+INSERT INTO shops (name, location)
+VALUES ('kuro-dai',
+GeomFromText('POINT(139.704834 35.650345)'));
+INSERT INTO shops (name, location)
+VALUES ('daruma',
+GeomFromText('POINT(139.770599 35.681461)'));
+INSERT INTO shops (name, location)
+VALUES ('yanagi-ya',
+GeomFromText('POINT(139.783981 35.685341)'));
+INSERT INTO shops (name, location)
+VALUES ('sharaku',
+GeomFromText('POINT(139.794846 35.716969)'));
+INSERT INTO shops (name, location)
+VALUES ('takane',
+GeomFromText('POINT(139.560913 35.698601)'));
+INSERT INTO shops (name, location)
+VALUES ('chiyoda',
+GeomFromText('POINT(139.652817 35.642601)'));
+INSERT INTO shops (name, location)
+VALUES ('da-ka-po',
+GeomFromText('POINT(139.727356 35.627346)'));
+INSERT INTO shops (name, location)
+VALUES ('matsushima-ya',
+GeomFromText('POINT(139.737381 35.640556)'));
+INSERT INTO shops (name, location)
+VALUES ('kazuya',
+GeomFromText('POINT(139.760895 35.673508)'));
+INSERT INTO shops (name, location)
+VALUES ('furuya-kogane-an',
+GeomFromText('POINT(139.676071 35.680603)'));
+INSERT INTO shops (name, location)
+VALUES ('hachi-no-ie',
+GeomFromText('POINT(139.668106 35.608021)'));
+INSERT INTO shops (name, location)
+VALUES ('azuki-chan',
+GeomFromText('POINT(139.673203 35.64151)'));
+INSERT INTO shops (name, location)
+VALUES ('kuriko-an',
+GeomFromText('POINT(139.796829 35.712013)'));
+INSERT INTO shops (name, location)
+VALUES ('yume-no-aru-machi-no-taiyaki-ya-san',
+GeomFromText('POINT(139.712524 35.616199)'));
+INSERT INTO shops (name, location)
+VALUES ('naze-ya',
+GeomFromText('POINT(139.665833 35.609039)'));
+INSERT INTO shops (name, location)
+VALUES ('sanoki-ya',
+GeomFromText('POINT(139.770721 35.66592)'));
+INSERT INTO shops (name, location)
+VALUES ('shigeta',
+GeomFromText('POINT(139.780273 35.672626)'));
+INSERT INTO shops (name, location)
+VALUES ('nishimi-ya',
+GeomFromText('POINT(139.774628 35.671825)'));
+INSERT INTO shops (name, location)
+VALUES ('hiiragi',
+GeomFromText('POINT(139.711517 35.647701)'));
+ALTER TABLE shops ADD SPATIAL KEY location_index (location);
+SELECT id, name, AsText(location) AS location_text FROM shops
+WHERE MBRContains(GeomFromText('LineString(139.7727 35.6684, 139.7038 35.7121)'), location)
+ORDER BY id;
+id name location_text
+14 tetsuji POINT(139.76857 35.680911944444446)
+19 daruma POINT(139.7705988888889 35.68146111111111)
+26 kazuya POINT(139.760895 35.67350805555556)
+SHOW CREATE TABLE shops;
+Table Create Table
+shops CREATE TABLE `shops` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` text,
+ `location` geometry NOT NULL,
+ PRIMARY KEY (`id`),
+ SPATIAL KEY `location_index` (`location`)
+) ENGINE=Mroonga AUTO_INCREMENT=37 DEFAULT CHARSET=latin1
+DROP TABLE shops;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/auto_increment_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/storage/r/auto_increment_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..6a5729af023
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/auto_increment_TODO_SPLIT_ME.result
@@ -0,0 +1,53 @@
+drop table if exists t1;
+create table t1 (c1 int auto_increment, primary key(c1));
+insert into t1 values(null);
+select c1 from t1 order by c1 desc limit 1;
+c1
+1
+insert into t1 values(null);
+select c1 from t1 order by c1 desc limit 1;
+c1
+2
+insert into t1 values(10);
+select c1 from t1 order by c1 desc limit 1;
+c1
+10
+insert into t1 values(null);
+select c1 from t1 order by c1 desc limit 1;
+c1
+11
+insert into t1 values(6);
+select c1 from t1 order by c1 desc limit 1;
+c1
+11
+insert into t1 values(null);
+select c1 from t1 order by c1 desc limit 1;
+c1
+12
+drop table t1;
+create table t1 (c1 int, c2 int auto_increment, primary key(c1), key idx1(c2));
+insert into t1 values(1, null);
+select * from t1 order by c2 desc limit 1;
+c1 c2
+1 1
+insert into t1 values(2, null);
+select * from t1 order by c2 desc limit 1;
+c1 c2
+2 2
+insert into t1 values(3, 10);
+select * from t1 order by c2 desc limit 1;
+c1 c2
+3 10
+insert into t1 values(4, null);
+select * from t1 order by c2 desc limit 1;
+c1 c2
+4 11
+insert into t1 values(5, 6);
+select * from t1 order by c2 desc limit 1;
+c1 c2
+4 11
+insert into t1 values(6, null);
+select * from t1 order by c2 desc limit 1;
+c1 c2
+6 12
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/auto_increment_table_param.result b/storage/mroonga/mysql-test/mroonga/storage/r/auto_increment_table_param.result
new file mode 100644
index 00000000000..f89b74e571a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/auto_increment_table_param.result
@@ -0,0 +1,70 @@
+drop table if exists t1;
+create table t1 (c1 int auto_increment, primary key(c1)) auto_increment=34129;
+insert into t1 values(null);
+select c1 from t1 order by c1 desc;
+c1
+34129
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `c1` int(11) NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY (`c1`)
+) ENGINE=Mroonga AUTO_INCREMENT=34130 DEFAULT CHARSET=latin1
+insert into t1 values(null);
+select c1 from t1 order by c1 desc;
+c1
+34130
+34129
+insert into t1 values(10);
+select c1 from t1 order by c1 desc;
+c1
+34130
+34129
+10
+insert into t1 values(null);
+select c1 from t1 order by c1 desc;
+c1
+34131
+34130
+34129
+10
+insert into t1 values(6);
+select c1 from t1 order by c1 desc;
+c1
+34131
+34130
+34129
+10
+6
+insert into t1 values(null);
+select c1 from t1 order by c1 desc;
+c1
+34132
+34131
+34130
+34129
+10
+6
+truncate table t1;
+insert into t1 values(null);
+select c1 from t1 order by c1 desc;
+c1
+1
+delete from t1;
+insert into t1 values(null);
+select c1 from t1 order by c1 desc;
+c1
+2
+rename table t1 to t2;
+insert into t2 values(null);
+select c1 from t2 order by c1 desc;
+c1
+3
+2
+show create table t2;
+Table Create Table
+t2 CREATE TABLE `t2` (
+ `c1` int(11) NOT NULL AUTO_INCREMENT,
+ PRIMARY KEY (`c1`)
+) ENGINE=Mroonga AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
+drop table t2;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/auto_increment_text.result b/storage/mroonga/mysql-test/mroonga/storage/r/auto_increment_text.result
new file mode 100644
index 00000000000..fe5e6409abe
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/auto_increment_text.result
@@ -0,0 +1,15 @@
+drop table if exists diaries;
+create table diaries (
+id int primary key auto_increment,
+body text
+);
+insert into diaries (body) values ("started groonga (long text)");
+select * from diaries;
+id body
+1 started groonga (long text)
+insert into diaries (body) values ("sleeping... (short text)");
+select * from diaries;
+id body
+1 started groonga (long text)
+2 sleeping... (short text)
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/binlog_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/storage/r/binlog_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..340509f6e53
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/binlog_TODO_SPLIT_ME.result
@@ -0,0 +1,34 @@
+drop table if exists t1;
+show variables like 'log_bin';
+Variable_name Value
+log_bin ON
+set binlog_format="STATEMENT";
+create table t1 (c1 int primary key, c2 int) engine = mroonga;
+insert into t1 values(1,100);
+insert into t1 values(2,100);
+commit;
+select * from t1;
+c1 c2
+1 100
+2 100
+drop table t1;
+set binlog_format="ROW";
+create table t1 (c1 int primary key, c2 int) engine = mroonga;
+insert into t1 values(1,100);
+insert into t1 values(2,100);
+commit;
+select * from t1;
+c1 c2
+1 100
+2 100
+drop table t1;
+set binlog_format="MIXED";
+create table t1 (c1 int primary key, c2 int) engine = mroonga;
+insert into t1 values(1,100);
+insert into t1 values(2,100);
+commit;
+select * from t1;
+c1 c2
+1 100
+2 100
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/collation_utf8_general_ci_french.result b/storage/mroonga/mysql-test/mroonga/storage/r/collation_utf8_general_ci_french.result
new file mode 100644
index 00000000000..880092f46fb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/collation_utf8_general_ci_french.result
@@ -0,0 +1,11 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+content varchar(256) COLLATE utf8_general_ci,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES ("Je suis un garçon.");
+SELECT * FROM diaries WHERE MATCH (content) AGAINST ("garcon");
+content
+Je suis un garçon.
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/collation_utf8_unicode_ci_french.result b/storage/mroonga/mysql-test/mroonga/storage/r/collation_utf8_unicode_ci_french.result
new file mode 100644
index 00000000000..3f24de87035
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/collation_utf8_unicode_ci_french.result
@@ -0,0 +1,11 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+content varchar(256) COLLATE utf8_unicode_ci,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES ("Je suis un garçon.");
+SELECT * FROM diaries WHERE MATCH (content) AGAINST ("garcon");
+content
+Je suis un garçon.
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/collation_utf8_unicode_ci_japanese.result b/storage/mroonga/mysql-test/mroonga/storage/r/collation_utf8_unicode_ci_japanese.result
new file mode 100644
index 00000000000..94ef2608b81
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/collation_utf8_unicode_ci_japanese.result
@@ -0,0 +1,11 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+content varchar(256) COLLATE utf8_unicode_ci,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES ("ひらがなとカタカナを覚えました。");
+SELECT * FROM diaries WHERE MATCH (content) AGAINST ("かたかな");
+content
+ひらがなとカタカナを覚えました。
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_comment_index_not_for_mroonga.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_comment_index_not_for_mroonga.result
new file mode 100644
index 00000000000..94b3a603389
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_comment_index_not_for_mroonga.result
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS bugs;
+CREATE TABLE bugs (
+id INT UNSIGNED,
+INDEX (id) COMMENT 'ID search is required.'
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE bugs;
+Table Create Table
+bugs CREATE TABLE `bugs` (
+ `id` int(10) unsigned DEFAULT NULL,
+ KEY `id` (`id`) COMMENT 'ID search is required.'
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+DROP TABLE bugs;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_comment_normal_not_for_mroonga.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_comment_normal_not_for_mroonga.result
new file mode 100644
index 00000000000..162b515d898
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_comment_normal_not_for_mroonga.result
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS bugs;
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tag VARCHAR(64) COMMENT 'It must consist of only alphabet and number.'
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE bugs;
+Table Create Table
+bugs CREATE TABLE `bugs` (
+ `id` int(10) unsigned NOT NULL,
+ `tag` varchar(64) DEFAULT NULL COMMENT 'It must consist of only alphabet and number.',
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+DROP TABLE bugs;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_date_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_date_with_index.result
new file mode 100644
index 00000000000..e24a56be3a2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_date_with_index.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATE,
+KEY (created_at)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` date DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `created_at` (`created_at`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at) VALUES ("clear day", "2012-01-29");
+INSERT INTO diaries (title, created_at) VALUES ("rainy day", "2012-01-30");
+INSERT INTO diaries (title, created_at) VALUES ("cloudy day", "2012-01-31");
+SELECT * FROM diaries;
+id title created_at
+1 clear day 2012-01-29
+2 rainy day 2012-01-30
+3 cloudy day 2012-01-31
+SELECT * FROM diaries WHERE created_at BETWEEN "2012-01-29" AND "2012-01-30";
+id title created_at
+1 clear day 2012-01-29
+2 rainy day 2012-01-30
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_date_without_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_date_without_index.result
new file mode 100644
index 00000000000..018ee8eb5b8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_date_without_index.result
@@ -0,0 +1,27 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATE
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` date DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at) VALUES ("clear day", "2012-01-29");
+INSERT INTO diaries (title, created_at) VALUES ("rainy day", "2012-01-30");
+INSERT INTO diaries (title, created_at) VALUES ("cloudy day", "2012-01-31");
+SELECT * FROM diaries;
+id title created_at
+1 clear day 2012-01-29
+2 rainy day 2012-01-30
+3 cloudy day 2012-01-31
+SELECT * FROM diaries WHERE created_at BETWEEN "2012-01-29" AND "2012-01-30";
+id title created_at
+1 clear day 2012-01-29
+2 rainy day 2012-01-30
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_date_zero_date.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_date_zero_date.result
new file mode 100644
index 00000000000..b2364e1158e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_date_zero_date.result
@@ -0,0 +1,27 @@
+DROP TABLE IF EXISTS timestamps;
+CREATE TABLE timestamps (
+id INT PRIMARY KEY AUTO_INCREMENT,
+create_dt DATE
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE timestamps;
+Table Create Table
+timestamps CREATE TABLE `timestamps` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `create_dt` date DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO timestamps (create_dt) VALUES ("2012-00-01");
+Warnings:
+Warning 1265 Data truncated for column 'create_dt' at row 1
+INSERT INTO timestamps (create_dt) VALUES ("2012-01-00");
+Warnings:
+Warning 1265 Data truncated for column 'create_dt' at row 1
+SELECT * FROM timestamps;
+id create_dt
+1 2012-01-01
+2 2012-01-01
+SELECT * FROM timestamps WHERE create_dt = "2012-01-01";
+id create_dt
+1 2012-01-01
+2 2012-01-01
+DROP TABLE timestamps;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_2038.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_2038.result
new file mode 100644
index 00000000000..453f641968b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_2038.result
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ('2038-01-18 03:14:07', '2038-01-18 03:14:07');
+INSERT INTO diaries (title, created_at)
+VALUES ('2038-01-20 03:14:08', '2038-01-20 03:14:08');
+Warnings:
+Warning 1265 Data truncated for column 'created_at' at row 1
+SELECT * FROM diaries;
+id title created_at
+1 2038-01-18 03:14:07 2038-01-18 03:14:07
+2 2038-01-20 03:14:08 1970-01-01 00:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_before_unix_epoch.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_before_unix_epoch.result
new file mode 100644
index 00000000000..10824d7c28d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_before_unix_epoch.result
@@ -0,0 +1,22 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ('1000-01-01 00:00:00', '1000-01-01 00:00:00');
+Warnings:
+Warning 1265 Data truncated for column 'created_at' at row 1
+SELECT * FROM diaries;
+id title created_at
+1 1000-01-01 00:00:00 1970-01-01 00:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_max.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_max.result
new file mode 100644
index 00000000000..f3a7b27f342
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_max.result
@@ -0,0 +1,22 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ('9999-12-31 23:59:59', '9999-12-31 23:59:59');
+Warnings:
+Warning 1265 Data truncated for column 'created_at' at row 1
+SELECT * FROM diaries;
+id title created_at
+1 9999-12-31 23:59:59 1970-01-01 00:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_out_of_range.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_out_of_range.result
new file mode 100644
index 00000000000..d7acad79bab
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_32bit_out_of_range.result
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ('2012', '2012');
+Warnings:
+Warning 1265 Data truncated for column 'created_at' at row 1
+Warning 1265 Data truncated for column 'created_at' at row 1
+SELECT * FROM diaries;
+id title created_at
+1 2012 1970-01-01 00:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_2038.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_2038.result
new file mode 100644
index 00000000000..56e38902f28
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_2038.result
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ('2038-01-19 03:14:07', '2038-01-19 03:14:07');
+INSERT INTO diaries (title, created_at)
+VALUES ('2038-01-19 03:14:08', '2038-01-19 03:14:08');
+SELECT * FROM diaries;
+id title created_at
+1 2038-01-19 03:14:07 2038-01-19 03:14:07
+2 2038-01-19 03:14:08 2038-01-19 03:14:08
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_before_unix_epoch.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_before_unix_epoch.result
new file mode 100644
index 00000000000..c3ca2628bca
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_before_unix_epoch.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ('1000-01-01 00:00:00', '1000-01-01 00:00:00');
+SELECT * FROM diaries;
+id title created_at
+1 1000-01-01 00:00:00 1000-01-01 00:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_max.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_max.result
new file mode 100644
index 00000000000..0373d17530e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_max.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ('9999-12-31 23:59:59', '9999-12-31 23:59:59');
+SELECT * FROM diaries;
+id title created_at
+1 9999-12-31 23:59:59 9999-12-31 23:59:59
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_version_55_out_of_range.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_version_55_out_of_range.result
new file mode 100644
index 00000000000..733217fda85
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_version_55_out_of_range.result
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ('2012', '2012');
+Warnings:
+Warning 1264 Out of range value for column 'created_at' at row 1
+Warning 1265 Data truncated for column 'created_at' at row 1
+SELECT * FROM diaries;
+id title created_at
+1 2012 0000-01-01 00:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_version_56_or_later_out_of_range.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_version_56_or_later_out_of_range.result
new file mode 100644
index 00000000000..962212c86f8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_64bit_version_56_or_later_out_of_range.result
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ('2012', '2012');
+Warnings:
+Warning 1265 Data truncated for column 'created_at' at row 1
+Warning 1265 Data truncated for column 'created_at' at row 1
+SELECT * FROM diaries;
+id title created_at
+1 2012 0000-01-01 00:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_fractional_seconds_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_fractional_seconds_with_index.result
new file mode 100644
index 00000000000..a31042f768d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_fractional_seconds_with_index.result
@@ -0,0 +1,34 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME(6),
+KEY (created_at)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime(6) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `created_at` (`created_at`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ("clear day", "2012-01-29 21:51:01.111111");
+INSERT INTO diaries (title, created_at)
+VALUES ("rainy day", "2012-01-30 01:23:45.333");
+INSERT INTO diaries (title, created_at)
+VALUES ("cloudy day", "2012-01-31 08:32:10.5555");
+SELECT * FROM diaries;
+id title created_at
+1 clear day 2012-01-29 21:51:01.111111
+2 rainy day 2012-01-30 01:23:45.333000
+3 cloudy day 2012-01-31 08:32:10.555500
+SELECT * FROM diaries
+WHERE created_at BETWEEN "2012-01-29 00:00:00.123456" AND
+"2012-01-31 00:00:00.999999";
+id title created_at
+1 clear day 2012-01-29 21:51:01.111111
+2 rainy day 2012-01-30 01:23:45.333000
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_fractional_seconds_without_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_fractional_seconds_without_index.result
new file mode 100644
index 00000000000..0b1bf0f0eb4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_fractional_seconds_without_index.result
@@ -0,0 +1,32 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME(6)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime(6) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ("clear day", "2012-01-29 21:51:01.111111");
+INSERT INTO diaries (title, created_at)
+VALUES ("rainy day", "2012-01-30 01:23:45.333");
+INSERT INTO diaries (title, created_at)
+VALUES ("cloudy day", "2012-01-31 08:32:10.5555");
+SELECT * FROM diaries;
+id title created_at
+1 clear day 2012-01-29 21:51:01.111111
+2 rainy day 2012-01-30 01:23:45.333000
+3 cloudy day 2012-01-31 08:32:10.555500
+SELECT * FROM diaries
+WHERE created_at BETWEEN "2012-01-29 00:00:00.123456" AND
+"2012-01-31 00:00:00.999999";
+id title created_at
+1 clear day 2012-01-29 21:51:01.111111
+2 rainy day 2012-01-30 01:23:45.333000
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_freebsd_before_unix_epoch.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_freebsd_before_unix_epoch.result
new file mode 100644
index 00000000000..10824d7c28d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_freebsd_before_unix_epoch.result
@@ -0,0 +1,22 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ('1000-01-01 00:00:00', '1000-01-01 00:00:00');
+Warnings:
+Warning 1265 Data truncated for column 'created_at' at row 1
+SELECT * FROM diaries;
+id title created_at
+1 1000-01-01 00:00:00 1970-01-01 00:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_null.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_null.result
new file mode 100644
index 00000000000..82f01b9f237
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_null.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ('NULL', NULL);
+SELECT * FROM diaries;
+id title created_at
+1 NULL 1970-01-01 00:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_with_index.result
new file mode 100644
index 00000000000..b205031dec1
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_with_index.result
@@ -0,0 +1,33 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME,
+KEY (created_at)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `created_at` (`created_at`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ("clear day", "2012-01-29 21:51:01");
+INSERT INTO diaries (title, created_at)
+VALUES ("rainy day", "2012-01-30 01:23:45");
+INSERT INTO diaries (title, created_at)
+VALUES ("cloudy day", "2012-01-31 08:32:10");
+SELECT * FROM diaries;
+id title created_at
+1 clear day 2012-01-29 21:51:01
+2 rainy day 2012-01-30 01:23:45
+3 cloudy day 2012-01-31 08:32:10
+SELECT * FROM diaries
+WHERE created_at BETWEEN "2012-01-29 00:00:00" AND "2012-01-31 00:00:00";
+id title created_at
+1 clear day 2012-01-29 21:51:01
+2 rainy day 2012-01-30 01:23:45
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_without_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_without_index.result
new file mode 100644
index 00000000000..6592df35fcb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_without_index.result
@@ -0,0 +1,31 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at)
+VALUES ("clear day", "2012-01-29 21:51:01");
+INSERT INTO diaries (title, created_at)
+VALUES ("rainy day", "2012-01-30 01:23:45");
+INSERT INTO diaries (title, created_at)
+VALUES ("cloudy day", "2012-01-31 08:32:10");
+SELECT * FROM diaries;
+id title created_at
+1 clear day 2012-01-29 21:51:01
+2 rainy day 2012-01-30 01:23:45
+3 cloudy day 2012-01-31 08:32:10
+SELECT * FROM diaries
+WHERE created_at BETWEEN "2012-01-29 00:00:00" AND "2012-01-31 00:00:00";
+id title created_at
+1 clear day 2012-01-29 21:51:01
+2 rainy day 2012-01-30 01:23:45
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_zero_date.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_zero_date.result
new file mode 100644
index 00000000000..c22a63f9744
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_datetime_zero_date.result
@@ -0,0 +1,27 @@
+DROP TABLE IF EXISTS timestamps;
+CREATE TABLE timestamps (
+id INT PRIMARY KEY AUTO_INCREMENT,
+create_dt DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE timestamps;
+Table Create Table
+timestamps CREATE TABLE `timestamps` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `create_dt` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO timestamps (create_dt) VALUES ("2012-00-01 00:00:00");
+Warnings:
+Warning 1265 Data truncated for column 'create_dt' at row 1
+INSERT INTO timestamps (create_dt) VALUES ("2012-01-00 00:00:00");
+Warnings:
+Warning 1265 Data truncated for column 'create_dt' at row 1
+SELECT * FROM timestamps;
+id create_dt
+1 2012-01-01 00:00:00
+2 2012-01-01 00:00:00
+SELECT * FROM timestamps WHERE create_dt = "2012-01-01 00:00:00";
+id create_dt
+1 2012-01-01 00:00:00
+2 2012-01-01 00:00:00
+DROP TABLE timestamps;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_fractional_seconds_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_fractional_seconds_with_index.result
new file mode 100644
index 00000000000..4a21d62dd14
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_fractional_seconds_with_index.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+temperature DECIMAL(6, 3),
+KEY (temperature)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `temperature` decimal(6,3) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `temperature` (`temperature`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, temperature) VALUES ("clear day", 21.281);
+INSERT INTO diaries (title, temperature) VALUES ("rainy day", 14.213);
+INSERT INTO diaries (title, temperature) VALUES ("cloudy day", 17.821);
+SELECT * FROM diaries;
+id title temperature
+1 clear day 21.281
+2 rainy day 14.213
+3 cloudy day 17.821
+SELECT * FROM diaries WHERE temperature BETWEEN "14.213" AND "17.821";
+id title temperature
+2 rainy day 14.213
+3 cloudy day 17.821
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_fractional_seconds_without_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_fractional_seconds_without_index.result
new file mode 100644
index 00000000000..cd939fa5483
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_fractional_seconds_without_index.result
@@ -0,0 +1,27 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+temperature DECIMAL(6, 3)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `temperature` decimal(6,3) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, temperature) VALUES ("clear day", 21.281);
+INSERT INTO diaries (title, temperature) VALUES ("rainy day", 14.213);
+INSERT INTO diaries (title, temperature) VALUES ("cloudy day", 17.821);
+SELECT * FROM diaries;
+id title temperature
+1 clear day 21.281
+2 rainy day 14.213
+3 cloudy day 17.821
+SELECT * FROM diaries WHERE temperature BETWEEN "14.213" AND "17.821";
+id title temperature
+2 rainy day 14.213
+3 cloudy day 17.821
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_with_index.result
new file mode 100644
index 00000000000..56a6a360dc9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_with_index.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+temperature DECIMAL,
+KEY (temperature)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `temperature` decimal(10,0) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `temperature` (`temperature`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, temperature) VALUES ("clear day", 21);
+INSERT INTO diaries (title, temperature) VALUES ("rainy day", 14);
+INSERT INTO diaries (title, temperature) VALUES ("cloudy day", 17);
+SELECT * FROM diaries;
+id title temperature
+1 clear day 21
+2 rainy day 14
+3 cloudy day 17
+SELECT * FROM diaries WHERE temperature BETWEEN "14" AND "17";
+id title temperature
+2 rainy day 14
+3 cloudy day 17
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_without_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_without_index.result
new file mode 100644
index 00000000000..06162f76ddd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_decimal_without_index.result
@@ -0,0 +1,27 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+temperature DECIMAL
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `temperature` decimal(10,0) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, temperature) VALUES ("clear day", 21);
+INSERT INTO diaries (title, temperature) VALUES ("rainy day", 14);
+INSERT INTO diaries (title, temperature) VALUES ("cloudy day", 17);
+SELECT * FROM diaries;
+id title temperature
+1 clear day 21
+2 rainy day 14
+3 cloudy day 17
+SELECT * FROM diaries WHERE temperature BETWEEN "14" AND "17";
+id title temperature
+2 rainy day 14
+3 cloudy day 17
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_enum_less_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_enum_less_with_index.result
new file mode 100644
index 00000000000..28c80dcc6f8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_enum_less_with_index.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+size ENUM("small", "medium", "large"),
+INDEX (size)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+Table Create Table
+items CREATE TABLE `items` (
+ `name` varchar(255) DEFAULT NULL,
+ `size` enum('small','medium','large') DEFAULT NULL,
+ KEY `size` (`size`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO items VALUES ("t-shart for child", "small");
+INSERT INTO items VALUES ("leadies' coat", "medium");
+INSERT INTO items VALUES ("parka", "large");
+INSERT INTO items VALUES ("hat", "medium");
+SELECT * FROM items;
+name size
+t-shart for child small
+leadies' coat medium
+parka large
+hat medium
+SELECT * FROM items WHERE size = "medium";
+name size
+leadies' coat medium
+hat medium
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_enum_many_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_enum_many_with_index.result
new file mode 100644
index 00000000000..731a9690702
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_enum_many_with_index.result
@@ -0,0 +1,287 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+size ENUM("size1",
+"size2",
+"size3",
+"size4",
+"size5",
+"size6",
+"size7",
+"size8",
+"size9",
+"size10",
+"size11",
+"size12",
+"size13",
+"size14",
+"size15",
+"size16",
+"size17",
+"size18",
+"size19",
+"size20",
+"size21",
+"size22",
+"size23",
+"size24",
+"size25",
+"size26",
+"size27",
+"size28",
+"size29",
+"size30",
+"size31",
+"size32",
+"size33",
+"size34",
+"size35",
+"size36",
+"size37",
+"size38",
+"size39",
+"size40",
+"size41",
+"size42",
+"size43",
+"size44",
+"size45",
+"size46",
+"size47",
+"size48",
+"size49",
+"size50",
+"size51",
+"size52",
+"size53",
+"size54",
+"size55",
+"size56",
+"size57",
+"size58",
+"size59",
+"size60",
+"size61",
+"size62",
+"size63",
+"size64",
+"size65",
+"size66",
+"size67",
+"size68",
+"size69",
+"size70",
+"size71",
+"size72",
+"size73",
+"size74",
+"size75",
+"size76",
+"size77",
+"size78",
+"size79",
+"size80",
+"size81",
+"size82",
+"size83",
+"size84",
+"size85",
+"size86",
+"size87",
+"size88",
+"size89",
+"size90",
+"size91",
+"size92",
+"size93",
+"size94",
+"size95",
+"size96",
+"size97",
+"size98",
+"size99",
+"size100",
+"size101",
+"size102",
+"size103",
+"size104",
+"size105",
+"size106",
+"size107",
+"size108",
+"size109",
+"size110",
+"size111",
+"size112",
+"size113",
+"size114",
+"size115",
+"size116",
+"size117",
+"size118",
+"size119",
+"size120",
+"size121",
+"size122",
+"size123",
+"size124",
+"size125",
+"size126",
+"size127",
+"size128",
+"size129",
+"size130",
+"size131",
+"size132",
+"size133",
+"size134",
+"size135",
+"size136",
+"size137",
+"size138",
+"size139",
+"size140",
+"size141",
+"size142",
+"size143",
+"size144",
+"size145",
+"size146",
+"size147",
+"size148",
+"size149",
+"size150",
+"size151",
+"size152",
+"size153",
+"size154",
+"size155",
+"size156",
+"size157",
+"size158",
+"size159",
+"size160",
+"size161",
+"size162",
+"size163",
+"size164",
+"size165",
+"size166",
+"size167",
+"size168",
+"size169",
+"size170",
+"size171",
+"size172",
+"size173",
+"size174",
+"size175",
+"size176",
+"size177",
+"size178",
+"size179",
+"size180",
+"size181",
+"size182",
+"size183",
+"size184",
+"size185",
+"size186",
+"size187",
+"size188",
+"size189",
+"size190",
+"size191",
+"size192",
+"size193",
+"size194",
+"size195",
+"size196",
+"size197",
+"size198",
+"size199",
+"size200",
+"size201",
+"size202",
+"size203",
+"size204",
+"size205",
+"size206",
+"size207",
+"size208",
+"size209",
+"size210",
+"size211",
+"size212",
+"size213",
+"size214",
+"size215",
+"size216",
+"size217",
+"size218",
+"size219",
+"size220",
+"size221",
+"size222",
+"size223",
+"size224",
+"size225",
+"size226",
+"size227",
+"size228",
+"size229",
+"size230",
+"size231",
+"size232",
+"size233",
+"size234",
+"size235",
+"size236",
+"size237",
+"size238",
+"size239",
+"size240",
+"size241",
+"size242",
+"size243",
+"size244",
+"size245",
+"size246",
+"size247",
+"size248",
+"size249",
+"size250",
+"size251",
+"size252",
+"size253",
+"size254",
+"size255",
+"size256"),
+INDEX (size)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+Table Create Table
+items CREATE TABLE `items` (
+ `name` varchar(255) DEFAULT NULL,
+ `size` enum('size1','size2','size3','size4','size5','size6','size7','size8','size9','size10','size11','size12','size13','size14','size15','size16','size17','size18','size19','size20','size21','size22','size23','size24','size25','size26','size27','size28','size29','size30','size31','size32','size33','size34','size35','size36','size37','size38','size39','size40','size41','size42','size43','size44','size45','size46','size47','size48','size49','size50','size51','size52','size53','size54','size55','size56','size57','size58','size59','size60','size61','size62','size63','size64','size65','size66','size67','size68','size69','size70','size71','size72','size73','size74','size75','size76','size77','size78','size79','size80','size81','size82','size83','size84','size85','size86','size87','size88','size89','size90','size91','size92','size93','size94','size95','size96','size97','size98','size99','size100','size101','size102','size103','size104','size105','size106','size107','size108','size109','size110','size111','size112','size113','size114','size115','size116','size117','size118','size119','size120','size121','size122','size123','size124','size125','size126','size127','size128','size129','size130','size131','size132','size133','size134','size135','size136','size137','size138','size139','size140','size141','size142','size143','size144','size145','size146','size147','size148','size149','size150','size151','size152','size153','size154','size155','size156','size157','size158','size159','size160','size161','size162','size163','size164','size165','size166','size167','size168','size169','size170','size171','size172','size173','size174','size175','size176','size177','size178','size179','size180','size181','size182','size183','size184','size185','size186','size187','size188','size189','size190','size191','size192','size193','size194','size195','size196','size197','size198','size199','size200','size201','size202','size203','size204','size205','size206','size207','size208','size209','size210','size211','size212','size213','size214','size215','size216','size217','size218','size219','size220','size221','size222','size223','size224','size225','size226','size227','size228','size229','size230','size231','size232','size233','size234','size235','size236','size237','size238','size239','size240','size241','size242','size243','size244','size245','size246','size247','size248','size249','size250','size251','size252','size253','size254','size255','size256') DEFAULT NULL,
+ KEY `size` (`size`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO items VALUES ("t-shart for child", "size1");
+INSERT INTO items VALUES ("leadies' coat", "size1");
+INSERT INTO items VALUES ("parka", "size256");
+INSERT INTO items VALUES ("hat", "size256");
+SELECT * FROM items;
+name size
+t-shart for child size1
+leadies' coat size1
+parka size256
+hat size256
+SELECT * FROM items WHERE size = "size1";
+name size
+t-shart for child size1
+leadies' coat size1
+SELECT * FROM items WHERE size = "size256";
+name size
+parka size256
+hat size256
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga__id__id.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga__id__id.result
new file mode 100644
index 00000000000..33f31ed38df
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga__id__id.result
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS contents;
+CREATE TABLE contents (
+_id INT,
+content TEXT NOT NULL,
+FULLTEXT INDEX(content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO contents (content) VALUES ('first');
+INSERT INTO contents (content) VALUES ('second');
+SELECT _id, content FROM contents;
+_id content
+1 first
+2 second
+DROP TABLE contents;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga__id_invalid_id.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga__id_invalid_id.result
new file mode 100644
index 00000000000..903e3a85cab
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga__id_invalid_id.result
@@ -0,0 +1,8 @@
+DROP TABLE IF EXISTS contents;
+CREATE TABLE contents (
+_i INT,
+content TEXT NOT NULL,
+FULLTEXT INDEX(content)
+) DEFAULT CHARSET=utf8;
+ERROR HY000: [column][create] name can't start with '_' and contains only 0-9, A-Z, a-z, #, @, - or _: <_i>
+DROP TABLE IF EXISTS contents;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_index_fulltext_other_table.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_index_fulltext_other_table.result
new file mode 100644
index 00000000000..87c14a98f15
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_index_fulltext_other_table.result
@@ -0,0 +1,42 @@
+DROP DATABASE IF EXISTS mroonga;
+CREATE DATABASE mroonga;
+USE mroonga;
+CREATE TABLE tags (
+name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+COLLATE=utf8_bin
+COMMENT='default_tokenizer "TokenDelimit"';
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tags TEXT COMMENT 'flags "COLUMN_VECTOR", type "tags"',
+FULLTEXT INDEX bugs_tags_index (tags) COMMENT 'table "tags"'
+) DEFAULT CHARSET=utf8;
+INSERT INTO bugs (id, tags) VALUES (1, "Linux MySQL groonga");
+SELECT mroonga_command("dump");
+mroonga_command("dump")
+table_create tags TABLE_PAT_KEY ShortText --default_tokenizer TokenDelimit
+column_create tags name COLUMN_SCALAR ShortText
+table_create bugs TABLE_PAT_KEY UInt32
+column_create bugs id COLUMN_SCALAR UInt32
+column_create bugs tags COLUMN_VECTOR tags
+column_create tags bugs_tags_index COLUMN_INDEX|WITH_POSITION bugs tags
+load --table tags
+[
+["_key","name"],
+["Linux",""],
+["MySQL",""],
+["groonga",""]
+]
+load --table bugs
+[
+["_key","id","tags"],
+[1,1,["Linux","MySQL","groonga"]]
+]
+SELECT *, MATCH (tags) AGAINST ("MySQL" IN BOOLEAN MODE) AS score
+FROM bugs
+WHERE MATCH (tags) AGAINST ("MySQL" IN BOOLEAN MODE);
+id tags score
+1 Linux MySQL groonga 1
+DROP TABLE bugs;
+DROP TABLE tags;
+DROP DATABASE mroonga;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_index_int_other_table.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_index_int_other_table.result
new file mode 100644
index 00000000000..e46ed71d0fe
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_index_int_other_table.result
@@ -0,0 +1,45 @@
+DROP DATABASE IF EXISTS mroonga;
+CREATE DATABASE mroonga;
+USE mroonga;
+CREATE TABLE priorities (
+id INT PRIMARY KEY
+) DEFAULT CHARSET=utf8
+COLLATE=utf8_bin;
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+priority INT COMMENT 'type "priorities"',
+INDEX bugs_priority_index (priority) COMMENT 'table "priorities"'
+) DEFAULT CHARSET=utf8;
+INSERT INTO bugs (id, priority) VALUES (1, 10);
+INSERT INTO bugs (id, priority) VALUES (2, 3);
+INSERT INTO bugs (id, priority) VALUES (3, -2);
+SELECT mroonga_command("dump");
+mroonga_command("dump")
+table_create priorities TABLE_PAT_KEY Int32
+column_create priorities id COLUMN_SCALAR Int32
+table_create bugs TABLE_PAT_KEY UInt32
+column_create bugs id COLUMN_SCALAR UInt32
+column_create bugs priority COLUMN_SCALAR priorities
+column_create priorities bugs_priority_index COLUMN_INDEX|WITH_POSITION bugs priority
+load --table priorities
+[
+["_key","id"],
+[-2,0],
+[3,0],
+[10,0]
+]
+load --table bugs
+[
+["_key","id","priority"],
+[1,1,10],
+[2,2,3],
+[3,3,-2]
+]
+SELECT *
+FROM bugs
+WHERE priority = 3;
+id priority
+2 3
+DROP TABLE bugs;
+DROP TABLE priorities;
+DROP DATABASE mroonga;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_scalar_reference.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_scalar_reference.result
new file mode 100644
index 00000000000..8acf8aab220
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_scalar_reference.result
@@ -0,0 +1,24 @@
+DROP TABLE IF EXISTS tags, bugs;
+CREATE TABLE tags (
+name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+COLLATE=utf8_bin;
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tag TEXT COMMENT 'type "tags"'
+) DEFAULT CHARSET=utf8;
+INSERT INTO bugs (id, tag) VALUES (1, "Linux");
+INSERT INTO bugs (id, tag) VALUES (2, "MySQL");
+INSERT INTO bugs (id, tag) VALUES (3, "groonga");
+SELECT * FROM bugs;
+id tag
+1 Linux
+2 MySQL
+3 groonga
+SELECT * FROM tags;
+name
+Linux
+MySQL
+groonga
+DROP TABLE bugs;
+DROP TABLE tags;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_scalar_with_not_for_mroonga_comment.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_scalar_with_not_for_mroonga_comment.result
new file mode 100644
index 00000000000..cd910206a3d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_scalar_with_not_for_mroonga_comment.result
@@ -0,0 +1,27 @@
+DROP TABLE IF EXISTS tags, bugs;
+CREATE TABLE tags (
+name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+COLLATE=utf8_bin;
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tag TEXT COMMENT 'It references to tags.name, type "tags"'
+) DEFAULT CHARSET=utf8;
+SHOW FULL COLUMNS FROM bugs LIKE 'tag';
+Field Type Collation Null Key Default Extra Privileges Comment
+tag text utf8_general_ci YES NULL select,insert,update,references It references to tags.name, type "tags"
+INSERT INTO bugs (id, tag) VALUES (1, "Linux");
+INSERT INTO bugs (id, tag) VALUES (2, "MySQL");
+INSERT INTO bugs (id, tag) VALUES (3, "groonga");
+SELECT * FROM bugs;
+id tag
+1 Linux
+2 MySQL
+3 groonga
+SELECT * FROM tags;
+name
+Linux
+MySQL
+groonga
+DROP TABLE bugs;
+DROP TABLE tags;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_vector_order_by_with_function.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_vector_order_by_with_function.result
new file mode 100644
index 00000000000..468115e8a20
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_vector_order_by_with_function.result
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS tags, bugs;
+CREATE TABLE tags (
+name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+COLLATE=utf8_bin
+COMMENT='default_tokenizer "TokenDelimit"';
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tags TEXT COMMENT 'flags "COLUMN_VECTOR", type "tags"'
+) DEFAULT CHARSET=utf8;
+INSERT INTO bugs (id, tags) VALUES (1, "Linux MySQL Groonga");
+INSERT INTO bugs (id, tags) VALUES (2, "MySQL Mroonga");
+INSERT INTO bugs (id, tags) VALUES (3, "Ruby Rroonga");
+SELECT * FROM tags ORDER BY SUBSTRING(name, 1, 1) ASC;
+name
+Groonga
+Linux
+Mroonga
+MySQL
+Rroonga
+Ruby
+DROP TABLE bugs;
+DROP TABLE tags;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_vector_reference.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_vector_reference.result
new file mode 100644
index 00000000000..515dad1da2e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_groonga_vector_reference.result
@@ -0,0 +1,21 @@
+DROP TABLE IF EXISTS tags, bugs;
+CREATE TABLE tags (
+name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+COLLATE=utf8_bin
+COMMENT='default_tokenizer "TokenDelimit"';
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tags TEXT COMMENT 'flags "COLUMN_VECTOR", type "tags"'
+) DEFAULT CHARSET=utf8;
+INSERT INTO bugs (id, tags) VALUES (1, "Linux MySQL groonga");
+SELECT * FROM bugs;
+id tags
+1 Linux MySQL groonga
+SELECT * FROM tags;
+name
+Linux
+MySQL
+groonga
+DROP TABLE bugs;
+DROP TABLE tags;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_int_with_index_zero_value.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_int_with_index_zero_value.result
new file mode 100644
index 00000000000..1afd7da9839
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_int_with_index_zero_value.result
@@ -0,0 +1,21 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+price INT KEY
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+INSERT INTO items VALUES ("hamburger", 200);
+INSERT INTO items VALUES ("smile", 0);
+INSERT INTO items VALUES ("coke", 100);
+SELECT * FROM items;
+name price
+smile 0
+coke 100
+hamburger 200
+SELECT * FROM items WHERE price = 0;
+name price
+smile 0
+SELECT * FROM items WHERE price <= 100;
+name price
+smile 0
+coke 100
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_set_16_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_set_16_with_index.result
new file mode 100644
index 00000000000..ee818a62e75
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_set_16_with_index.result
@@ -0,0 +1,40 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+colors SET("black",
+"dim gray",
+"dark gray",
+"gray",
+"light gray",
+"gainsboro",
+"white smoke",
+"white",
+"red",
+"orange red",
+"dark orange",
+"orange",
+"gold",
+"yellow",
+"chartreuse",
+"lawn green"),
+INDEX (colors)
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+Table Create Table
+items CREATE TABLE `items` (
+ `name` varchar(255) DEFAULT NULL,
+ `colors` set('black','dim gray','dark gray','gray','light gray','gainsboro','white smoke','white','red','orange red','dark orange','orange','gold','yellow','chartreuse','lawn green') DEFAULT NULL,
+ KEY `colors` (`colors`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO items VALUES ("t-shart", "black,gray");
+INSERT INTO items VALUES ("hat", "white,dark gray");
+INSERT INTO items VALUES ("parka", "chartreuse,orange");
+SELECT * FROM items;
+name colors
+t-shart black,gray
+hat dark gray,white
+parka orange,chartreuse
+SELECT * FROM items WHERE colors = "dark gray,white";
+name colors
+hat dark gray,white
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_set_24_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_set_24_with_index.result
new file mode 100644
index 00000000000..10d4fd8bd1c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_set_24_with_index.result
@@ -0,0 +1,48 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+colors SET("black",
+"dim gray",
+"dark gray",
+"gray",
+"light gray",
+"gainsboro",
+"white smoke",
+"white",
+"red",
+"orange red",
+"dark orange",
+"orange",
+"gold",
+"yellow",
+"chartreuse",
+"lawn green",
+"green",
+"spring green",
+"medium spring green",
+"cyan",
+"deep sky blue",
+"blue",
+"medium blue",
+"dark violet"),
+INDEX (colors)
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+Table Create Table
+items CREATE TABLE `items` (
+ `name` varchar(255) DEFAULT NULL,
+ `colors` set('black','dim gray','dark gray','gray','light gray','gainsboro','white smoke','white','red','orange red','dark orange','orange','gold','yellow','chartreuse','lawn green','green','spring green','medium spring green','cyan','deep sky blue','blue','medium blue','dark violet') DEFAULT NULL,
+ KEY `colors` (`colors`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO items VALUES ("t-shart", "black,white");
+INSERT INTO items VALUES ("hat", "white,lawn green");
+INSERT INTO items VALUES ("parka", "gray,medium blue");
+SELECT * FROM items;
+name colors
+t-shart black,white
+hat white,lawn green
+parka gray,medium blue
+SELECT * FROM items WHERE colors = "white,lawn green";
+name colors
+hat white,lawn green
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_set_32_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_set_32_with_index.result
new file mode 100644
index 00000000000..0432970aa9e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_set_32_with_index.result
@@ -0,0 +1,56 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+colors SET("black",
+"dim gray",
+"dark gray",
+"gray",
+"light gray",
+"gainsboro",
+"white smoke",
+"white",
+"red",
+"orange red",
+"dark orange",
+"orange",
+"gold",
+"yellow",
+"chartreuse",
+"lawn green",
+"green",
+"spring green",
+"medium spring green",
+"cyan",
+"deep sky blue",
+"blue",
+"medium blue",
+"dark violet",
+"dark magenta",
+"magenta",
+"dark red",
+"brown",
+"firebrick",
+"indian red",
+"light coral",
+"salmon"),
+INDEX (colors)
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+Table Create Table
+items CREATE TABLE `items` (
+ `name` varchar(255) DEFAULT NULL,
+ `colors` set('black','dim gray','dark gray','gray','light gray','gainsboro','white smoke','white','red','orange red','dark orange','orange','gold','yellow','chartreuse','lawn green','green','spring green','medium spring green','cyan','deep sky blue','blue','medium blue','dark violet','dark magenta','magenta','dark red','brown','firebrick','indian red','light coral','salmon') DEFAULT NULL,
+ KEY `colors` (`colors`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO items VALUES ("t-shart", "black,white");
+INSERT INTO items VALUES ("hat", "white,dark violet");
+INSERT INTO items VALUES ("parka", "green,brown,red");
+SELECT * FROM items;
+name colors
+t-shart black,white
+hat white,dark violet
+parka red,green,brown
+SELECT * FROM items WHERE colors = "white,dark violet";
+name colors
+hat white,dark violet
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_set_64_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_set_64_with_index.result
new file mode 100644
index 00000000000..5b6d4511adf
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_set_64_with_index.result
@@ -0,0 +1,88 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+colors SET("black",
+"dim gray",
+"dark gray",
+"gray",
+"light gray",
+"gainsboro",
+"white smoke",
+"white",
+"red",
+"orange red",
+"dark orange",
+"orange",
+"gold",
+"yellow",
+"chartreuse",
+"lawn green",
+"green",
+"spring green",
+"medium spring green",
+"cyan",
+"deep sky blue",
+"blue",
+"medium blue",
+"dark violet",
+"dark magenta",
+"magenta",
+"dark red",
+"brown",
+"firebrick",
+"indian red",
+"light coral",
+"salmon",
+"light salmon",
+"tomato",
+"coral",
+"dark salmon",
+"rosy brown",
+"sienna",
+"saddle brown",
+"chocolate",
+"peru",
+"sandy brown",
+"burlywood",
+"tan",
+"navajo white",
+"wheat",
+"dark goldenrod",
+"goldenrod",
+"light goldenrod",
+"pale goldenrod",
+"cornsilk",
+"dark khaki",
+"khaki",
+"lemon chiffon",
+"dark olive green",
+"olive drab",
+"yellow green",
+"green yellow",
+"light green",
+"forest green",
+"dark green",
+"lime green",
+"pale green",
+"dark sea green"),
+INDEX (colors)
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+Table Create Table
+items CREATE TABLE `items` (
+ `name` varchar(255) DEFAULT NULL,
+ `colors` set('black','dim gray','dark gray','gray','light gray','gainsboro','white smoke','white','red','orange red','dark orange','orange','gold','yellow','chartreuse','lawn green','green','spring green','medium spring green','cyan','deep sky blue','blue','medium blue','dark violet','dark magenta','magenta','dark red','brown','firebrick','indian red','light coral','salmon','light salmon','tomato','coral','dark salmon','rosy brown','sienna','saddle brown','chocolate','peru','sandy brown','burlywood','tan','navajo white','wheat','dark goldenrod','goldenrod','light goldenrod','pale goldenrod','cornsilk','dark khaki','khaki','lemon chiffon','dark olive green','olive drab','yellow green','green yellow','light green','forest green','dark green','lime green','pale green','dark sea green') DEFAULT NULL,
+ KEY `colors` (`colors`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO items VALUES ("t-shart", "black,white,lawn green,dark violet");
+INSERT INTO items VALUES ("hat", "white,dark violet,yellow green");
+INSERT INTO items VALUES ("parka", "green,brown,red,lime green");
+SELECT * FROM items;
+name colors
+t-shart black,white,lawn green,dark violet
+hat white,dark violet,yellow green
+parka red,green,brown,lime green
+SELECT * FROM items WHERE colors = "white,dark violet,yellow green";
+name colors
+hat white,dark violet,yellow green
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_set_8_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_set_8_with_index.result
new file mode 100644
index 00000000000..3e6bf4e1939
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_set_8_with_index.result
@@ -0,0 +1,32 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+colors SET("black",
+"dim gray",
+"dark gray",
+"gray",
+"light gray",
+"gainsboro",
+"white smoke",
+"white"),
+INDEX (colors)
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+Table Create Table
+items CREATE TABLE `items` (
+ `name` varchar(255) DEFAULT NULL,
+ `colors` set('black','dim gray','dark gray','gray','light gray','gainsboro','white smoke','white') DEFAULT NULL,
+ KEY `colors` (`colors`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO items VALUES ("t-shart", "black,gray");
+INSERT INTO items VALUES ("hat", "dim gray,dark gray");
+INSERT INTO items VALUES ("parka", "white smoke,light gray");
+SELECT * FROM items;
+name colors
+t-shart black,gray
+hat dim gray,dark gray
+parka light gray,white smoke
+SELECT * FROM items WHERE colors = "dim gray,dark gray";
+name colors
+hat dim gray,dark gray
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_bigint_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_bigint_with_index.result
new file mode 100644
index 00000000000..262f77556c6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_bigint_with_index.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+price BIGINT KEY
+) DEFAULT CHARSET=utf8;
+INSERT INTO items VALUES ("house", 9223372036854775807);
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", -9223372036854775808);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("super car", 2147483648);
+SELECT * FROM items;
+name price
+discount -9223372036854775808
+coke 100
+note PC 32767
+super car 2147483648
+house 9223372036854775807
+SELECT * FROM items WHERE price <= 2147483648;
+name price
+discount -9223372036854775808
+coke 100
+note PC 32767
+super car 2147483648
+SELECT * FROM items WHERE price > 2147483647;
+name price
+super car 2147483648
+house 9223372036854775807
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_int_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_int_with_index.result
new file mode 100644
index 00000000000..867b887182f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_int_with_index.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+price INT KEY
+) DEFAULT CHARSET=utf8;
+INSERT INTO items VALUES ("car", 2147483647);
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", -2147483647);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("bike", 16777216);
+SELECT * FROM items;
+name price
+discount -2147483647
+coke 100
+note PC 32767
+bike 16777216
+car 2147483647
+SELECT * FROM items WHERE price <= 16777216;
+name price
+discount -2147483647
+coke 100
+note PC 32767
+bike 16777216
+SELECT * FROM items WHERE price > 16777215;
+name price
+bike 16777216
+car 2147483647
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_mediumint_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_mediumint_with_index.result
new file mode 100644
index 00000000000..c8b4f895a9c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_mediumint_with_index.result
@@ -0,0 +1,26 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+price MEDIUMINT KEY
+) DEFAULT CHARSET=utf8;
+INSERT INTO items VALUES ("car", 8388607);
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", -8388608);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("bike", 32768);
+SELECT * FROM items;
+name price
+discount -8388608
+coke 100
+note PC 32767
+bike 32768
+car 8388607
+SELECT * FROM items WHERE price <= 127;
+name price
+discount -8388608
+coke 100
+SELECT * FROM items WHERE price >= 32768;
+name price
+bike 32768
+car 8388607
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_smallint_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_smallint_with_index.result
new file mode 100644
index 00000000000..8a51ee5fd8f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_smallint_with_index.result
@@ -0,0 +1,24 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+price SMALLINT KEY
+) DEFAULT CHARSET=utf8;
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", -32768);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("tablet PC", 20000);
+SELECT * FROM items;
+name price
+discount -32768
+coke 100
+tablet PC 20000
+note PC 32767
+SELECT * FROM items WHERE price <= 127;
+name price
+discount -32768
+coke 100
+SELECT * FROM items WHERE price >= 128;
+name price
+tablet PC 20000
+note PC 32767
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_tinyint_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_tinyint_with_index.result
new file mode 100644
index 00000000000..d14efcca9a0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_signed_tinyint_with_index.result
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+price TINYINT KEY
+) DEFAULT CHARSET=utf8;
+INSERT INTO items VALUES ("hamburger", 120);
+INSERT INTO items VALUES ("discount", -10);
+INSERT INTO items VALUES ("coke", 100);
+SELECT * FROM items;
+name price
+discount -10
+coke 100
+hamburger 120
+SELECT * FROM items WHERE price <= 100;
+name price
+discount -10
+coke 100
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_time_fractional_seconds_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_time_fractional_seconds_with_index.result
new file mode 100644
index 00000000000..9c2a8e876e3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_time_fractional_seconds_with_index.result
@@ -0,0 +1,40 @@
+DROP TABLE IF EXISTS running_records;
+CREATE TABLE running_records (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+average TIME(6),
+max TIME(6),
+KEY (average)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE running_records;
+Table Create Table
+running_records CREATE TABLE `running_records` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `average` time(6) DEFAULT NULL,
+ `max` time(6) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `average` (`average`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO running_records (title, average, max)
+VALUES ("normal condition", "01:00:00.000001", "01:05:00.000001");
+INSERT INTO running_records (title, average, max)
+VALUES ("bad condition", "12:23:34.123456", "838:59:58.999999");
+INSERT INTO running_records (title, average, max)
+VALUES ("record failure", "-838:59:59.000000", "-838:59:59.000000");
+SELECT * FROM running_records;
+id title average max
+1 normal condition 01:00:00.000001 01:05:00.000001
+2 bad condition 12:23:34.123456 838:59:58.999999
+3 record failure -838:59:59.000000 -838:59:59.000000
+SELECT * FROM running_records
+WHERE average BETWEEN "00:59:59.999999" AND "100:10:10.101010";
+id title average max
+1 normal condition 01:00:00.000001 01:05:00.000001
+2 bad condition 12:23:34.123456 838:59:58.999999
+SELECT * FROM running_records
+WHERE average BETWEEN "-838:59:59.000000" AND "01:00:00.000001";
+id title average max
+3 record failure -838:59:59.000000 -838:59:59.000000
+1 normal condition 01:00:00.000001 01:05:00.000001
+DROP TABLE running_records;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_time_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_time_with_index.result
new file mode 100644
index 00000000000..da75fd97424
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_time_with_index.result
@@ -0,0 +1,40 @@
+DROP TABLE IF EXISTS running_records;
+CREATE TABLE running_records (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+average TIME,
+max TIME,
+KEY (average)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE running_records;
+Table Create Table
+running_records CREATE TABLE `running_records` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `average` time DEFAULT NULL,
+ `max` time DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `average` (`average`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO running_records (title, average, max)
+VALUES ("normal condition", "01:00:00", "01:05:00");
+INSERT INTO running_records (title, average, max)
+VALUES ("bad condition", "12:23:34", "838:59:59");
+INSERT INTO running_records (title, average, max)
+VALUES ("record failure", "-838:59:59", "-838:59:59");
+SELECT * FROM running_records;
+id title average max
+1 normal condition 01:00:00 01:05:00
+2 bad condition 12:23:34 838:59:59
+3 record failure -838:59:59 -838:59:59
+SELECT * FROM running_records
+WHERE average BETWEEN "00:59:59" AND "100:10:10";
+id title average max
+1 normal condition 01:00:00 01:05:00
+2 bad condition 12:23:34 838:59:59
+SELECT * FROM running_records
+WHERE average BETWEEN "-838:59:59" AND "01:00:00";
+id title average max
+3 record failure -838:59:59 -838:59:59
+1 normal condition 01:00:00 01:05:00
+DROP TABLE running_records;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_timestamp_fractional_seconds_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_timestamp_fractional_seconds_with_index.result
new file mode 100644
index 00000000000..da245535f2c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_timestamp_fractional_seconds_with_index.result
@@ -0,0 +1,42 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at TIMESTAMP(6),
+updated_at TIMESTAMP(6),
+KEY (updated_at)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
+ `updated_at` timestamp(6) NOT NULL DEFAULT '0000-00-00 00:00:00.000000',
+ PRIMARY KEY (`id`),
+ KEY `updated_at` (`updated_at`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at, updated_at)
+VALUES ("clear day",
+"2012-01-29 21:51:01.111111",
+"2012-01-29 21:51:02.222222");
+INSERT INTO diaries (title, created_at, updated_at)
+VALUES ("rainy day",
+"2012-01-30 01:23:45.333",
+"2012-01-30 01:23:46.444");
+INSERT INTO diaries (title, created_at, updated_at)
+VALUES ("cloudy day",
+"2012-01-31 08:32:10.5555",
+"2012-01-31 08:32:11.6666");
+SELECT * FROM diaries;
+id title created_at updated_at
+1 clear day 2012-01-29 21:51:01.111111 2012-01-29 21:51:02.222222
+2 rainy day 2012-01-30 01:23:45.333000 2012-01-30 01:23:46.444000
+3 cloudy day 2012-01-31 08:32:10.555500 2012-01-31 08:32:11.666600
+SELECT * FROM diaries
+WHERE updated_at BETWEEN "2012-01-29 00:00:00.123456" AND
+"2012-01-31 00:00:00.999999";
+id title created_at updated_at
+1 clear day 2012-01-29 21:51:01.111111 2012-01-29 21:51:02.222222
+2 rainy day 2012-01-30 01:23:45.333000 2012-01-30 01:23:46.444000
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_timestamp_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_timestamp_with_index.result
new file mode 100644
index 00000000000..b310de48111
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_timestamp_with_index.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+created_at TIMESTAMP,
+updated_at TIMESTAMP,
+KEY (updated_at)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
+ PRIMARY KEY (`id`),
+ KEY `updated_at` (`updated_at`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, created_at, updated_at)
+VALUES ("clear day", "2012-01-29 21:51:01", "2012-01-29 21:51:02");
+INSERT INTO diaries (title, created_at, updated_at)
+VALUES ("rainy day", "2012-01-30 01:23:45", "2012-01-30 01:23:46");
+INSERT INTO diaries (title, created_at, updated_at)
+VALUES ("cloudy day", "2012-01-31 08:32:10", "2012-01-31 08:32:11");
+SELECT * FROM diaries;
+id title created_at updated_at
+1 clear day 2012-01-29 21:51:01 2012-01-29 21:51:02
+2 rainy day 2012-01-30 01:23:45 2012-01-30 01:23:46
+3 cloudy day 2012-01-31 08:32:10 2012-01-31 08:32:11
+SELECT * FROM diaries
+WHERE updated_at BETWEEN "2012-01-29 00:00:00" AND "2012-01-31 00:00:00";
+id title created_at updated_at
+1 clear day 2012-01-29 21:51:01 2012-01-29 21:51:02
+2 rainy day 2012-01-30 01:23:45 2012-01-30 01:23:46
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_tinyint_without_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_tinyint_without_index.result
new file mode 100644
index 00000000000..8dde1bdde6d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_tinyint_without_index.result
@@ -0,0 +1,18 @@
+drop table if exists books;
+create table books(title varchar(255), published tinyint);
+insert into books values ("MySQL", 1);
+insert into books values ("groonga", 1);
+insert into books values ("mroonga", 0);
+select count(*) from books where published = 0;
+count(*)
+1
+select count(*) from books where published = 1;
+count(*)
+2
+select count(*) from books where published != 2;
+count(*)
+3
+select count(*) from books where published != 1;
+count(*)
+1
+drop table books;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_bigint_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_bigint_with_index.result
new file mode 100644
index 00000000000..d16004e3b82
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_bigint_with_index.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+price BIGINT UNSIGNED KEY
+) DEFAULT CHARSET=utf8;
+INSERT INTO items VALUES ("house", 18446744073709551615);
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", 0);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("super car", 9223372036854775808);
+SELECT * FROM items;
+name price
+discount 0
+coke 100
+note PC 32767
+super car 9223372036854775808
+house 18446744073709551615
+SELECT * FROM items WHERE price <= 9223372036854775808;
+name price
+discount 0
+coke 100
+note PC 32767
+super car 9223372036854775808
+SELECT * FROM items WHERE price > 9223372036854775807;
+name price
+super car 9223372036854775808
+house 18446744073709551615
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_bigint_without_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_bigint_without_index.result
new file mode 100644
index 00000000000..277580c7710
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_bigint_without_index.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id BIGINT UNSIGNED
+) DEFAULT CHARSET=utf8;
+INSERT INTO ids VALUES (317173755057152000);
+INSERT INTO ids VALUES (317173755057152002);
+SELECT * FROM ids;
+id
+317173755057152000
+317173755057152002
+SELECT * FROM ids WHERE id = 317173755057152000;
+id
+317173755057152000
+SELECT * FROM ids WHERE id = 317173755057152002;
+id
+317173755057152002
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_int_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_int_with_index.result
new file mode 100644
index 00000000000..d14ef87702d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_int_with_index.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+price INT UNSIGNED KEY
+) DEFAULT CHARSET=utf8;
+INSERT INTO items VALUES ("car", 4294967295);
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", 0);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("bike", 2147483648);
+SELECT * FROM items;
+name price
+discount 0
+coke 100
+note PC 32767
+bike 2147483648
+car 4294967295
+SELECT * FROM items WHERE price <= 2147483648;
+name price
+discount 0
+coke 100
+note PC 32767
+bike 2147483648
+SELECT * FROM items WHERE price > 2147483647;
+name price
+bike 2147483648
+car 4294967295
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_mediumint_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_mediumint_with_index.result
new file mode 100644
index 00000000000..2825aadae48
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_mediumint_with_index.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+price MEDIUMINT UNSIGNED KEY
+) DEFAULT CHARSET=utf8;
+INSERT INTO items VALUES ("car", 16777215);
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", 0);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("bike", 8388607);
+SELECT * FROM items;
+name price
+discount 0
+coke 100
+note PC 32767
+bike 8388607
+car 16777215
+SELECT * FROM items WHERE price <= 8388608;
+name price
+discount 0
+coke 100
+note PC 32767
+bike 8388607
+SELECT * FROM items WHERE price >= 8388607;
+name price
+bike 8388607
+car 16777215
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_smallint_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_smallint_with_index.result
new file mode 100644
index 00000000000..8586d091386
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_smallint_with_index.result
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+price SMALLINT UNSIGNED KEY
+) DEFAULT CHARSET=utf8;
+INSERT INTO items VALUES ("note PC", 65535);
+INSERT INTO items VALUES ("discount", 0);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("tablet PC", 32767);
+SELECT * FROM items;
+name price
+discount 0
+coke 100
+tablet PC 32767
+note PC 65535
+SELECT * FROM items WHERE price <= 32768;
+name price
+discount 0
+coke 100
+tablet PC 32767
+SELECT * FROM items WHERE price >= 32767;
+name price
+tablet PC 32767
+note PC 65535
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_tinyint_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_tinyint_with_index.result
new file mode 100644
index 00000000000..90adb048361
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_unsigned_tinyint_with_index.result
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS items;
+CREATE TABLE items (
+name VARCHAR(255),
+price TINYINT UNSIGNED KEY
+) DEFAULT CHARSET=utf8;
+INSERT INTO items VALUES ("hamburger", 255);
+INSERT INTO items VALUES ("discount", 0);
+INSERT INTO items VALUES ("coke", 100);
+SELECT * FROM items;
+name price
+discount 0
+coke 100
+hamburger 255
+SELECT * FROM items WHERE price <= 100;
+name price
+discount 0
+coke 100
+DROP TABLE items;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_year_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_year_with_index.result
new file mode 100644
index 00000000000..3ee91c1a408
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_year_with_index.result
@@ -0,0 +1,37 @@
+DROP TABLE IF EXISTS aniversary_memos;
+CREATE TABLE aniversary_memos (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+party_year YEAR,
+KEY (party_year)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE aniversary_memos;
+Table Create Table
+aniversary_memos CREATE TABLE `aniversary_memos` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `party_year` year(4) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `party_year` (`party_year`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO aniversary_memos (title, party_year)
+VALUES ("We need a big cake!", "11");
+INSERT INTO aniversary_memos (title, party_year)
+VALUES ("Invitations are sent.", "2012");
+INSERT INTO aniversary_memos (title, party_year)
+VALUES ("Tommorow is the anniversary party day!", "2012");
+INSERT INTO aniversary_memos (title, party_year)
+VALUES ("Wow! Today is the anniversary party day!", "13");
+SELECT * FROM aniversary_memos;
+id title party_year
+1 We need a big cake! 2011
+2 Invitations are sent. 2012
+3 Tommorow is the anniversary party day! 2012
+4 Wow! Today is the anniversary party day! 2013
+SELECT * FROM aniversary_memos
+WHERE party_year BETWEEN "12" AND "2013";
+id title party_year
+2 Invitations are sent. 2012
+3 Tommorow is the anniversary party day! 2012
+4 Wow! Today is the anniversary party day! 2013
+DROP TABLE aniversary_memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/column_year_without_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/column_year_without_index.result
new file mode 100644
index 00000000000..254a0c0718a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/column_year_without_index.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS aniversary_memos;
+CREATE TABLE aniversary_memos (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+party_year YEAR
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE aniversary_memos;
+Table Create Table
+aniversary_memos CREATE TABLE `aniversary_memos` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `party_year` year(4) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO aniversary_memos (title, party_year)
+VALUES ("We need a big cake!", "11");
+INSERT INTO aniversary_memos (title, party_year)
+VALUES ("Invitations are sent.", "2012");
+INSERT INTO aniversary_memos (title, party_year)
+VALUES ("Tommorow is the anniversary party day!", "2012");
+INSERT INTO aniversary_memos (title, party_year)
+VALUES ("Wow! Today is the anniversary party day!", "13");
+SELECT * FROM aniversary_memos;
+id title party_year
+1 We need a big cake! 2011
+2 Invitations are sent. 2012
+3 Tommorow is the anniversary party day! 2012
+4 Wow! Today is the anniversary party day! 2013
+SELECT * FROM aniversary_memos
+WHERE party_year BETWEEN "12" AND "2013";
+id title party_year
+2 Invitations are sent. 2012
+3 Tommorow is the anniversary party day! 2012
+4 Wow! Today is the anniversary party day! 2013
+DROP TABLE aniversary_memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/create_database_name_slash.result b/storage/mroonga/mysql-test/mroonga/storage/r/create_database_name_slash.result
new file mode 100644
index 00000000000..56c868b2aa9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/create_database_name_slash.result
@@ -0,0 +1,33 @@
+DROP DATABASE IF EXISTS `master/production`;
+DROP DATABASE IF EXISTS `master/development`;
+CREATE DATABASE `master/production`;
+USE `master/production`;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT
+) DEFAULT CHARSET=UTF8;
+INSERT INTO diaries (title) VALUES ("clear day (production)");
+INSERT INTO diaries (title) VALUES ("rainy day (production)");
+INSERT INTO diaries (title) VALUES ("cloudy day (production)");
+SELECT * FROM diaries;
+id title
+1 clear day (production)
+2 rainy day (production)
+3 cloudy day (production)
+CREATE DATABASE `master/development`;
+USE `master/development`;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT
+) DEFAULT CHARSET=UTF8;
+INSERT INTO diaries (title) VALUES ("clear day (development)");
+INSERT INTO diaries (title) VALUES ("rainy day (development)");
+INSERT INTO diaries (title) VALUES ("cloudy day (development)");
+SELECT * FROM diaries;
+id title
+1 clear day (development)
+2 rainy day (development)
+3 cloudy day (development)
+USE test;
+DROP DATABASE `master/production`;
+DROP DATABASE `master/development`;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/create_table_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..d2a00b777ec
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_TODO_SPLIT_ME.result
@@ -0,0 +1,172 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int);
+create table t2 (c1 int);
+create table t3 (c1 int);
+drop table t1,t2,t3;
+create table t1 (c1 int, c2 int, c3 int);
+create table t2 (c1 int primary key, c2 int, c3 int);
+drop table t1,t2;
+create table t1 (c1 bit);
+desc t1;
+Field Type Null Key Default Extra
+c1 bit(1) YES NULL
+drop table t1;
+create table t1 (c1 tinyint);
+desc t1;
+Field Type Null Key Default Extra
+c1 tinyint(4) YES NULL
+drop table t1;
+create table t1 (c1 smallint);
+desc t1;
+Field Type Null Key Default Extra
+c1 smallint(6) YES NULL
+drop table t1;
+create table t1 (c1 mediumint);
+desc t1;
+Field Type Null Key Default Extra
+c1 mediumint(9) YES NULL
+drop table t1;
+create table t1 (c1 int);
+desc t1;
+Field Type Null Key Default Extra
+c1 int(11) YES NULL
+drop table t1;
+create table t1 (c1 bigint);
+desc t1;
+Field Type Null Key Default Extra
+c1 bigint(20) YES NULL
+drop table t1;
+create table t1 (c1 double);
+desc t1;
+Field Type Null Key Default Extra
+c1 double YES NULL
+drop table t1;
+create table t1 (c1 float);
+desc t1;
+Field Type Null Key Default Extra
+c1 float YES NULL
+drop table t1;
+create table t1 (c1 decimal);
+desc t1;
+Field Type Null Key Default Extra
+c1 decimal(10,0) YES NULL
+drop table t1;
+create table t1 (c1 date);
+desc t1;
+Field Type Null Key Default Extra
+c1 date YES NULL
+drop table t1;
+create table t1 (c1 time);
+desc t1;
+Field Type Null Key Default Extra
+c1 time YES NULL
+drop table t1;
+create table t1 (c1 timestamp);
+desc t1;
+Field Type Null Key Default Extra
+c1 timestamp NO CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP
+drop table t1;
+create table t1 (c1 datetime);
+desc t1;
+Field Type Null Key Default Extra
+c1 datetime YES NULL
+drop table t1;
+create table t1 (c1 year);
+desc t1;
+Field Type Null Key Default Extra
+c1 year(4) YES NULL
+drop table t1;
+create table t1 (c1 char(10));
+desc t1;
+Field Type Null Key Default Extra
+c1 char(10) YES NULL
+drop table t1;
+create table t1 (c1 varchar(10));
+desc t1;
+Field Type Null Key Default Extra
+c1 varchar(10) YES NULL
+drop table t1;
+create table t1 (c1 binary(10));
+desc t1;
+Field Type Null Key Default Extra
+c1 binary(10) YES NULL
+drop table t1;
+create table t1 (c1 varbinary(10));
+desc t1;
+Field Type Null Key Default Extra
+c1 varbinary(10) YES NULL
+drop table t1;
+create table t1 (c1 tinyblob);
+desc t1;
+Field Type Null Key Default Extra
+c1 tinyblob YES NULL
+drop table t1;
+create table t1 (c1 blob);
+desc t1;
+Field Type Null Key Default Extra
+c1 blob YES NULL
+drop table t1;
+create table t1 (c1 mediumblob);
+desc t1;
+Field Type Null Key Default Extra
+c1 mediumblob YES NULL
+drop table t1;
+create table t1 (c1 longblob);
+desc t1;
+Field Type Null Key Default Extra
+c1 longblob YES NULL
+drop table t1;
+create table t1 (c1 tinytext);
+desc t1;
+Field Type Null Key Default Extra
+c1 tinytext YES NULL
+drop table t1;
+create table t1 (c1 text);
+desc t1;
+Field Type Null Key Default Extra
+c1 text YES NULL
+drop table t1;
+create table t1 (c1 mediumtext);
+desc t1;
+Field Type Null Key Default Extra
+c1 mediumtext YES NULL
+drop table t1;
+create table t1 (c1 longtext);
+desc t1;
+Field Type Null Key Default Extra
+c1 longtext YES NULL
+drop table t1;
+create table t1 (c1 enum("yes","no"));
+desc t1;
+Field Type Null Key Default Extra
+c1 enum('yes','no') YES NULL
+drop table t1;
+create table t1 (c1 set("A","B","AB","O"));
+desc t1;
+Field Type Null Key Default Extra
+c1 set('A','B','AB','O') YES NULL
+drop table t1;
+create table t1 (c1 int, `_id` int) engine = mroonga;
+desc t1;
+Field Type Null Key Default Extra
+c1 int(11) YES NULL
+_id int(11) YES NULL
+drop table t1;
+create table t1 (c1 int, `_score` float) engine = mroonga;
+ERROR HY000: [column][create] name can't start with '_' and contains only 0-9, A-Z, a-z, #, @, - or _: <_score>
+create table t1 (c1 int, `_id` text) engine = mroonga;
+ERROR HY000: _id must be numeric data type
+create table t1 (c1 int, `_id` int, index(`_id`)) engine = mroonga;
+ERROR HY000: only hash index can be defined for _id
+create table t1 (_id int, c1 int, primary key (_id));
+ERROR HY000: only hash index can be defined for _id
+create table t1 (_id int, c1 int, primary key (_id) using hash);
+drop table t1;
+create table t1 (_id int, c1 int, unique key (_id));
+ERROR HY000: only hash index can be defined for _id
+create table t1 (_id int, c1 int, unique key (_id) using hash);
+drop table t1;
+create table t1 (_id int, c1 int, key (_id));
+ERROR HY000: only hash index can be defined for _id
+create table t1 (_id int, c1 int, key (_id) using hash);
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/create_table_comment_normal.result b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_comment_normal.result
new file mode 100644
index 00000000000..edda98cd034
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_comment_normal.result
@@ -0,0 +1,11 @@
+DROP TABLE IF EXISTS bugs;
+CREATE TABLE bugs (
+id INT UNSIGNED
+) DEFAULT CHARSET=utf8
+COMMENT='Free style normal comment';
+SHOW CREATE TABLE bugs;
+Table Create Table
+bugs CREATE TABLE `bugs` (
+ `id` int(10) unsigned DEFAULT NULL
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='Free style normal comment'
+DROP TABLE bugs;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/create_table_default_tokenizer.result b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_default_tokenizer.result
new file mode 100644
index 00000000000..1a9888d71da
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_default_tokenizer.result
@@ -0,0 +1,11 @@
+DROP TABLE IF EXISTS tags;
+CREATE TABLE tags (
+name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+COLLATE=utf8_bin
+COMMENT='default_tokenizer "TokenDelimit"';
+SELECT mroonga_command("dump");
+mroonga_command("dump")
+table_create tags TABLE_PAT_KEY ShortText --default_tokenizer TokenDelimit
+column_create tags name COLUMN_SCALAR ShortText
+DROP TABLE tags;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/create_table_normalizer_fulltext_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_normalizer_fulltext_index.result
new file mode 100644
index 00000000000..e2d405a1e35
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_normalizer_fulltext_index.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+day DATE PRIMARY KEY,
+content VARCHAR(64) NOT NULL,
+FULLTEXT INDEX (content) COMMENT 'normalizer "NormalizerAuto"'
+) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+INSERT INTO diaries VALUES ("2013-04-23", "ブラックコーヒーを飲んだ。");
+SELECT * FROM diaries
+WHERE MATCH (content) AGAINST ("+ふらつく" IN BOOLEAN MODE);
+day content
+SELECT * FROM diaries
+WHERE MATCH (content) AGAINST ("+ブラック" IN BOOLEAN MODE);
+day content
+2013-04-23 ブラックコーヒーを飲んだ。
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/create_table_normalizer_no_utf8_charset_with_utf8_normalizer.result b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_normalizer_no_utf8_charset_with_utf8_normalizer.result
new file mode 100644
index 00000000000..9d12e2d0e39
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_normalizer_no_utf8_charset_with_utf8_normalizer.result
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES latin1;
+CREATE TABLE diaries (
+day DATE PRIMARY KEY,
+content VARCHAR(64) NOT NULL,
+FULLTEXT INDEX (content) COMMENT 'normalizer "NormalizerMySQLGeneralCI"'
+) DEFAULT CHARSET=latin1;
+INSERT INTO diaries VALUES ("2013-04-23", "I drunk a black cookie.");
+ERROR HY000: [tokenizer] failed to open normalized string
+SELECT * FROM diaries;
+day content
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/create_table_reference_type.result b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_reference_type.result
new file mode 100644
index 00000000000..68a6bc8012f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_reference_type.result
@@ -0,0 +1,16 @@
+CREATE TABLE tags (
+name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tag VARCHAR(64) COMMENT 'type "tags"'
+) DEFAULT CHARSET=utf8;
+SELECT mroonga_command("dump");
+mroonga_command("dump")
+table_create tags TABLE_PAT_KEY ShortText
+column_create tags name COLUMN_SCALAR ShortText
+table_create bugs TABLE_PAT_KEY UInt32
+column_create bugs id COLUMN_SCALAR UInt32
+column_create bugs tag COLUMN_SCALAR tags
+DROP TABLE bugs;
+DROP TABLE tags;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/create_table_vector.result b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_vector.result
new file mode 100644
index 00000000000..cb6248ec3d5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/create_table_vector.result
@@ -0,0 +1,11 @@
+DROP TABLE IF EXISTS bugs;
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tags TEXT COMMENT 'flags "COLUMN_VECTOR"'
+) DEFAULT CHARSET=utf8;
+SELECT mroonga_command("dump");
+mroonga_command("dump")
+table_create bugs TABLE_PAT_KEY UInt32
+column_create bugs id COLUMN_SCALAR UInt32
+column_create bugs tags COLUMN_VECTOR LongText
+DROP TABLE bugs;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/delete_fulltext_column.result b/storage/mroonga/mysql-test/mroonga/storage/r/delete_fulltext_column.result
new file mode 100644
index 00000000000..5fb52294758
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/delete_fulltext_column.result
@@ -0,0 +1,21 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 text, fulltext index (c2));
+insert into t1 values(10, "aa ii uu ee");
+insert into t1 values(20, "ka ki ku ke");
+insert into t1 values(30, "sa si su se");
+select * from t1;
+c1 c2
+10 aa ii uu ee
+20 ka ki ku ke
+30 sa si su se
+select * from t1 where match(c2) against("ki");
+c1 c2
+20 ka ki ku ke
+delete from t1 where c1=20;
+select * from t1;
+c1 c2
+10 aa ii uu ee
+30 sa si su se
+select * from t1 where match(c2) against("ki");
+c1 c2
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/delete_index_btree_many_records.result b/storage/mroonga/mysql-test/mroonga/storage/r/delete_index_btree_many_records.result
new file mode 100644
index 00000000000..f318e1255fe
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/delete_index_btree_many_records.result
@@ -0,0 +1,27 @@
+DROP TABLE IF EXISTS ids;
+SET NAMES UTF8;
+CREATE TABLE ids (
+id INT,
+KEY (id)
+) DEFAULT CHARSET UTF8;
+INSERT INTO ids VALUES(1);
+INSERT INTO ids VALUES(2);
+INSERT INTO ids VALUES(3);
+SELECT * FROM ids ORDER BY id;
+id
+1
+2
+3
+DELETE FROM ids WHERE id = 1;
+SELECT * FROM ids ORDER BY id;
+id
+2
+3
+DELETE FROM ids WHERE id = 2;
+SELECT * FROM ids ORDER BY id;
+id
+3
+DELETE FROM ids WHERE id = 3;
+SELECT * FROM ids ORDER BY id;
+id
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/delete_index_hash_id_no_unique.result b/storage/mroonga/mysql-test/mroonga/storage/r/delete_index_hash_id_no_unique.result
new file mode 100644
index 00000000000..76a4ed8835b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/delete_index_hash_id_no_unique.result
@@ -0,0 +1,19 @@
+drop table if exists t1, t2, t3;
+create table t1 (_id int, c1 int, key (_id) using hash);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+select * from t1;
+_id c1
+1 100
+2 100
+3 100
+4 100
+delete from t1 where _id = 2;
+select * from t1;
+_id c1
+1 100
+3 100
+4 100
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/delete_index_hash_id_unique.result b/storage/mroonga/mysql-test/mroonga/storage/r/delete_index_hash_id_unique.result
new file mode 100644
index 00000000000..6307c55fcc0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/delete_index_hash_id_unique.result
@@ -0,0 +1,19 @@
+drop table if exists t1, t2, t3;
+create table t1 (_id int, c1 int, unique key (_id) using hash);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+select * from t1;
+_id c1
+1 100
+2 100
+3 100
+4 100
+delete from t1 where _id = 2;
+select * from t1;
+_id c1
+1 100
+3 100
+4 100
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/delete_normal_column.result b/storage/mroonga/mysql-test/mroonga/storage/r/delete_normal_column.result
new file mode 100644
index 00000000000..7503c0b801e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/delete_normal_column.result
@@ -0,0 +1,34 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int, c2 int);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `c1` int(11) DEFAULT NULL,
+ `c2` int(11) DEFAULT NULL
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+insert into t1 values (1, 100);
+insert into t1 values (2, 101);
+insert into t1 values (3, 102);
+insert into t1 values (4, 102);
+select * from t1;
+c1 c2
+1 100
+2 101
+3 102
+4 102
+delete from t1 where c1=3;
+select * from t1;
+c1 c2
+1 100
+2 101
+4 102
+flush tables;
+delete from t1 where c1=2;
+select * from t1;
+c1 c2
+1 100
+4 102
+delete from t1;
+select * from t1;
+c1 c2
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/delete_unsigned_bigint.result b/storage/mroonga/mysql-test/mroonga/storage/r/delete_unsigned_bigint.result
new file mode 100644
index 00000000000..23cc23eeef6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/delete_unsigned_bigint.result
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS numbers;
+CREATE TABLE numbers (
+data BIGINT UNSIGNED
+);
+INSERT INTO numbers VALUES(18446744073709551615);
+SELECT * FROM numbers ORDER BY data;
+data
+18446744073709551615
+DELETE FROM numbers WHERE data = 18446744073709551615;
+SELECT * FROM numbers ORDER BY data;
+data
+DROP TABLE numbers;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/drop_database_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/storage/r/drop_database_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..4bd97162a6b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/drop_database_TODO_SPLIT_ME.result
@@ -0,0 +1,14 @@
+drop database if exists groonga;
+create database groonga;
+drop database groonga;
+create database groonga;
+use groonga;
+create table t1 (c1 int primary key, c2 varchar(255)) default charset utf8;
+create table t2 (c1 int primary key, c2 varchar(255)) default charset utf8;
+drop database groonga;
+create database groonga;
+use groonga;
+create table t1 (c1 int primary key, c2 varchar(255)) default charset utf8;
+create table t2 (c1 int primary key, c2 varchar(255)) default charset utf8;
+drop table t1, t2;
+drop database groonga;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/drop_table_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/storage/r/drop_table_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..99896765e28
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/drop_table_TODO_SPLIT_ME.result
@@ -0,0 +1,5 @@
+drop table if exists alphabet, `with-hyphen`;
+create table alphabet (c1 int primary key, c2 int, c3 int);
+drop table alphabet;
+create table `with-hyphen` (c1 int primary key, c2 int, c3 int);
+drop table `with-hyphen`;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/flush_logs.result b/storage/mroonga/mysql-test/mroonga/storage/r/flush_logs.result
new file mode 100644
index 00000000000..449f6437501
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/flush_logs.result
@@ -0,0 +1 @@
+flush logs;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/foreign_key_create.result b/storage/mroonga/mysql-test/mroonga/storage/r/foreign_key_create.result
new file mode 100644
index 00000000000..fc550d97f87
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/foreign_key_create.result
@@ -0,0 +1,147 @@
+drop table if exists articles2;
+drop table if exists articles;
+drop table if exists comments2;
+drop table if exists comments;
+create table comments(
+comment int unsigned,
+content text not null,
+primary key(comment)
+);
+create table articles(
+content text not null,
+comment int unsigned,
+FOREIGN KEY (comment) REFERENCES comments (comment)
+);
+insert into comments (comment, content) values
+(1, 'aaa bbb'),(2, 'ccc ddd'),(3, 'eee fff');
+insert into articles (content, comment) values
+('111aaa', 1),('222bbb', 2),('222ccc', 2);
+select comment, content from comments;
+comment content
+1 aaa bbb
+2 ccc ddd
+3 eee fff
+select content, comment from articles;
+content comment
+111aaa 1
+222bbb 2
+222ccc 2
+show create table comments;
+Table Create Table
+comments CREATE TABLE `comments` (
+ `comment` int(10) unsigned NOT NULL DEFAULT '0',
+ `content` text NOT NULL,
+ PRIMARY KEY (`comment`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+show create table articles;
+Table Create Table
+articles CREATE TABLE `articles` (
+ `content` text NOT NULL,
+ `comment` int(10) unsigned DEFAULT NULL,
+ KEY `comment` (`comment`),
+ CONSTRAINT `comment` FOREIGN KEY (`comment`) REFERENCES `test`.`comments` (`comment`) ON DELETE RESTRICT ON UPDATE RESTRICT
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+select * from information_schema.referential_constraints;
+CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME UNIQUE_CONSTRAINT_CATALOG UNIQUE_CONSTRAINT_SCHEMA UNIQUE_CONSTRAINT_NAME MATCH_OPTION UPDATE_RULE DELETE_RULE TABLE_NAME REFERENCED_TABLE_NAME
+def test comment def test PRIMARY NONE RESTRICT RESTRICT articles comments
+rename table comments to comments2;
+rename table articles to articles2;
+create table comments(
+comment int unsigned,
+content text not null,
+primary key(comment)
+);
+create table articles(
+content text not null,
+comment int unsigned,
+FOREIGN KEY (comment) REFERENCES comments (comment)
+);
+insert into comments (comment, content) values
+(1, 'ab'),(2, 'cd'),(3, 'ef');
+insert into articles (content, comment) values
+('1a', 1),('2b', 2),('2c', 2);
+select comment, content from comments;
+comment content
+1 ab
+2 cd
+3 ef
+select content, comment from articles;
+content comment
+1a 1
+2b 2
+2c 2
+select comment, content from comments2;
+comment content
+1 aaa bbb
+2 ccc ddd
+3 eee fff
+select content, comment from articles2;
+content comment
+111aaa 1
+222bbb 2
+222ccc 2
+show create table comments;
+Table Create Table
+comments CREATE TABLE `comments` (
+ `comment` int(10) unsigned NOT NULL DEFAULT '0',
+ `content` text NOT NULL,
+ PRIMARY KEY (`comment`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+show create table articles;
+Table Create Table
+articles CREATE TABLE `articles` (
+ `content` text NOT NULL,
+ `comment` int(10) unsigned DEFAULT NULL,
+ KEY `comment` (`comment`),
+ CONSTRAINT `comment` FOREIGN KEY (`comment`) REFERENCES `test`.`comments` (`comment`) ON DELETE RESTRICT ON UPDATE RESTRICT
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+show create table comments2;
+Table Create Table
+comments2 CREATE TABLE `comments2` (
+ `comment` int(10) unsigned NOT NULL DEFAULT '0',
+ `content` text NOT NULL,
+ PRIMARY KEY (`comment`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+show create table articles2;
+Table Create Table
+articles2 CREATE TABLE `articles2` (
+ `content` text NOT NULL,
+ `comment` int(10) unsigned DEFAULT NULL,
+ KEY `comment` (`comment`),
+ CONSTRAINT `comment` FOREIGN KEY (`comment`) REFERENCES `test`.`comments2` (`comment`) ON DELETE RESTRICT ON UPDATE RESTRICT
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+select * from information_schema.referential_constraints;
+CONSTRAINT_CATALOG CONSTRAINT_SCHEMA CONSTRAINT_NAME UNIQUE_CONSTRAINT_CATALOG UNIQUE_CONSTRAINT_SCHEMA UNIQUE_CONSTRAINT_NAME MATCH_OPTION UPDATE_RULE DELETE_RULE TABLE_NAME REFERENCED_TABLE_NAME
+def test comment def test PRIMARY NONE RESTRICT RESTRICT articles comments
+def test comment def test PRIMARY NONE RESTRICT RESTRICT articles2 comments2
+alter table articles drop foreign key comment;
+show create table articles;
+Table Create Table
+articles CREATE TABLE `articles` (
+ `content` text NOT NULL,
+ `comment` int(10) unsigned DEFAULT NULL,
+ KEY `comment` (`comment`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+select content, comment from articles;
+content comment
+1a 1
+2b 2
+2c 2
+alter table articles add FOREIGN KEY (comment) REFERENCES comments (comment);
+show create table articles;
+Table Create Table
+articles CREATE TABLE `articles` (
+ `content` text NOT NULL,
+ `comment` int(10) unsigned DEFAULT NULL,
+ KEY `comment` (`comment`),
+ CONSTRAINT `comment` FOREIGN KEY (`comment`) REFERENCES `test`.`comments` (`comment`) ON DELETE RESTRICT ON UPDATE RESTRICT
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+select content, comment from articles;
+content comment
+1a 1
+2b 2
+2c 2
+drop table articles2;
+drop table articles;
+drop table comments2;
+drop table comments;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_empty_query.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_empty_query.result
new file mode 100644
index 00000000000..96c6ae4150c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_empty_query.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries(
+title TEXT,
+FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+SELECT * FROM diaries;
+title
+Start groonga
+Start mroonga
+Start groonga and Ruby
+SELECT *
+FROM diaries
+WHERE MATCH(title) AGAINST("" IN BOOLEAN MODE);
+title
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_escape.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_escape.result
new file mode 100644
index 00000000000..a48eb25dece
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_escape.result
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS memos;
+SET GLOBAL mroonga_default_parser = TokenDelimit;
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT PRIMARY KEY,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=UTF8;
+INSERT INTO memos VALUES(1, "(groonga) Installed!");
+INSERT INTO memos VALUES(2, "(mroonga) Installed!");
+INSERT INTO memos VALUES(3, "(groonga) Upgraded!");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("\\(groonga\\)*" IN BOOLEAN MODE);
+id content
+1 (groonga) Installed!
+3 (groonga) Upgraded!
+DROP TABLE memos;
+SET GLOBAL mroonga_default_parser = TokenBigram;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_leading_not.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_leading_not.result
new file mode 100644
index 00000000000..d0e1a68f76b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_leading_not.result
@@ -0,0 +1,24 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT PRIMARY KEY,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("-明日 +天気" IN BOOLEAN MODE);
+id title content
+3 富士山 今日も天気がよくてきれいに見える。
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.result
new file mode 100644
index 00000000000..d10b5dad815
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Yesterday was good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+INSERT INTO memos VALUES ("Tomorrow will be fine.");
+INSERT INTO memos VALUES ("Yesterday was fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*D- fine is be" IN BOOLEAN MODE);
+content
+Yesterday was fine.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_minus_with_or.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_minus_with_or.result
new file mode 100644
index 00000000000..82c7e799fd4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_minus_with_or.result
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Yesterday was good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+INSERT INTO memos VALUES ("Tomorrow will be fine.");
+INSERT INTO memos VALUES ("Yesterday was fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*D- is OR be fine" IN BOOLEAN MODE);
+content
+Today is good day.
+Tomorrow will be good day.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.result
new file mode 100644
index 00000000000..c4c7b0e8546
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.result
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Yesterday was good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+INSERT INTO memos VALUES ("Tomorrow will be fine.");
+INSERT INTO memos VALUES ("Yesterday was fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*D- good +day be" IN BOOLEAN MODE);
+content
+Today is good day.
+Yesterday was good day.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_or_no_operator.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_or_no_operator.result
new file mode 100644
index 00000000000..f45e8fd4fb6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_or_no_operator.result
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+INSERT INTO memos VALUES ("Tomorrow will be fine.");
+INSERT INTO memos VALUES ("Yesterday was fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*DOR today good" IN BOOLEAN MODE);
+content
+Today is good day.
+Today is fine.
+Tomorrow will be good day.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_or_with_minus.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_or_with_minus.result
new file mode 100644
index 00000000000..103866902c2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_or_with_minus.result
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+INSERT INTO memos VALUES ("Tomorrow will be fine.");
+INSERT INTO memos VALUES ("Yesterday was fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*DOR today -good tomorrow" IN BOOLEAN MODE);
+content
+Tomorrow will be good day.
+Today is fine.
+Tomorrow will be fine.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_or_with_plus.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_or_with_plus.result
new file mode 100644
index 00000000000..fd52868b4bc
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_or_with_plus.result
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+INSERT INTO memos VALUES ("Tomorrow will be fine.");
+INSERT INTO memos VALUES ("Yesterday was fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*DOR today +good tomorrow" IN BOOLEAN MODE);
+content
+Today is good day.
+Tomorrow will be good day.
+Tomorrow will be fine.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.result
new file mode 100644
index 00000000000..4d1f65923c4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.result
@@ -0,0 +1,14 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*D+ today good" IN BOOLEAN MODE);
+content
+Today is good day.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.result
new file mode 100644
index 00000000000..deda6f3bb1a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.result
@@ -0,0 +1,14 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*D+ today -good is" IN BOOLEAN MODE);
+content
+Today is fine.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_plus_with_or.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_plus_with_or.result
new file mode 100644
index 00000000000..2d25388fada
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_default_operator_plus_with_or.result
@@ -0,0 +1,15 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*D+ today OR tomorrow day" IN BOOLEAN MODE);
+content
+Today is good day.
+Tomorrow will be good day.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_full_spec.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_full_spec.result
new file mode 100644
index 00000000000..b8f15d9847e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_full_spec.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id INT PRIMARY KEY,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX (title, content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+SELECT *, MATCH(title, content)
+AGAINST("*W1:10,2:2 +天気" in BOOLEAN MODE) AS score
+FROM diaries
+WHERE MATCH(title, content)
+AGAINST("*W1:10,2:2 +天気" in BOOLEAN MODE);
+id title content score
+2 天気 明日の富士山の天気について 12
+3 富士山 今日も天気がよくてきれいに見える。 2
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_no_weight.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_no_weight.result
new file mode 100644
index 00000000000..42aa068a547
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_no_weight.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id INT PRIMARY KEY,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX (title, content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+SELECT *, MATCH(title, content)
+AGAINST("*W1,2:2 +天気" in BOOLEAN MODE) AS score
+FROM diaries
+WHERE MATCH(title, content)
+AGAINST("*W1,2:2 +天気" in BOOLEAN MODE);
+id title content score
+2 天気 明日の富士山の天気について 3
+3 富士山 今日も天気がよくてきれいに見える。 2
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_omit_section.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_omit_section.result
new file mode 100644
index 00000000000..c73d2660d54
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_omit_section.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id INT PRIMARY KEY,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX (title, content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+SELECT *, MATCH(title, content)
+AGAINST("*W1:2 +天気" in BOOLEAN MODE) AS score
+FROM diaries
+WHERE MATCH(title, content)
+AGAINST("*W1:2 +天気" in BOOLEAN MODE);
+id title content score
+2 天気 明日の富士山の天気について 3
+3 富士山 今日も天気がよくてきれいに見える。 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_ten_or_more_sections.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_ten_or_more_sections.result
new file mode 100644
index 00000000000..b1b9d5f4f41
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_ten_or_more_sections.result
@@ -0,0 +1,39 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+title VARCHAR(255),
+tag1 VARCHAR(10),
+tag2 VARCHAR(10),
+tag3 VARCHAR(10),
+tag4 VARCHAR(10),
+tag5 VARCHAR(10),
+tag6 VARCHAR(10),
+tag7 VARCHAR(10),
+tag8 VARCHAR(10),
+tag9 VARCHAR(10),
+tag10 VARCHAR(10),
+FULLTEXT INDEX (tag1, tag2, tag3, tag4, tag5, tag6, tag7, tag8, tag9, tag10)
+) DEFAULT CHARSET=utf8;
+INSERT INTO memos
+VALUES("Groonga",
+"tag 1",
+"tag 2",
+"tag 3",
+"tag 4",
+"tag 5",
+"tag 6",
+"tag 7",
+"tag 8",
+"tag 9",
+"tag 10");
+SELECT title,
+MATCH(tag1, tag2, tag3, tag4, tag5, tag6, tag7, tag8, tag9, tag10)
+AGAINST("*W1:2,2:4,3:6,4:8,5:10,6:12,7:14,8:16,9:18,10:20 +tag"
+ in BOOLEAN MODE) AS score
+FROM memos
+WHERE MATCH(tag1, tag2, tag3, tag4, tag5, tag6, tag7, tag8, tag9, tag10)
+AGAINST("*W1:2,2:4,3:6,4:8,5:10,6:12,7:14,8:16,9:18,10:20 +tag"
+ in BOOLEAN MODE);
+title score
+Groonga 110
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_three_or_more_sections.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_three_or_more_sections.result
new file mode 100644
index 00000000000..02db17e61da
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_pragma_weight_three_or_more_sections.result
@@ -0,0 +1,21 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id INT PRIMARY KEY,
+title VARCHAR(255),
+category VARCHAR(10),
+content TEXT,
+FULLTEXT INDEX (title, category, content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES(1, "Hello", "日記", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気予報", "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "天気", "今日も天気がよくてきれいに見える。");
+SELECT *, MATCH(title, category, content)
+AGAINST("*W1:2,2:10,3:1 +天気" in BOOLEAN MODE) AS score
+FROM diaries
+WHERE MATCH(title, category, content)
+AGAINST("*W1:2,2:10,3:1 +天気" in BOOLEAN MODE);
+id title category content score
+2 天気予報 天気 明日の富士山の天気について 13
+3 富士山 天気 今日も天気がよくてきれいに見える。 11
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_error.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_error.result
new file mode 100644
index 00000000000..5746ce89cb7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_error.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS memos;
+SET GLOBAL mroonga_default_parser = TokenDelimit;
+SET mroonga_action_on_fulltext_query_error = ERROR;
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT PRIMARY KEY,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=UTF8;
+INSERT INTO memos VALUES(1, "(groonga) Installed!");
+INSERT INTO memos VALUES(2, "(mroonga) Installed!");
+INSERT INTO memos VALUES(3, "(groonga) Upgraded!");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("(groonga" IN BOOLEAN MODE);
+ERROR 42000: failed to parse fulltext search keyword: <(groonga>: <Syntax error: <(groonga||>>
+DROP TABLE memos;
+SET GLOBAL mroonga_default_parser = TokenBigram;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_error_and_log.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_error_and_log.result
new file mode 100644
index 00000000000..1811994b67e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_error_and_log.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS memos;
+SET GLOBAL mroonga_default_parser = TokenDelimit;
+SET mroonga_action_on_fulltext_query_error = ERROR_AND_LOG;
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT PRIMARY KEY,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=UTF8;
+INSERT INTO memos VALUES(1, "(groonga) Installed!");
+INSERT INTO memos VALUES(2, "(mroonga) Installed!");
+INSERT INTO memos VALUES(3, "(groonga) Upgraded!");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("(groonga" IN BOOLEAN MODE);
+ERROR 42000: failed to parse fulltext search keyword: <(groonga>: <Syntax error: <(groonga||>>
+DROP TABLE memos;
+SET GLOBAL mroonga_default_parser = TokenBigram;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_ignore.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_ignore.result
new file mode 100644
index 00000000000..6a4759963a3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_ignore.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS memos;
+SET GLOBAL mroonga_default_parser = TokenDelimit;
+SET mroonga_action_on_fulltext_query_error = "IGNORE";
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT PRIMARY KEY,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=UTF8;
+INSERT INTO memos VALUES(1, "(groonga) Installed!");
+INSERT INTO memos VALUES(2, "(mroonga) Installed!");
+INSERT INTO memos VALUES(3, "(groonga) Upgraded!");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("(groonga" IN BOOLEAN MODE);
+id content
+DROP TABLE memos;
+SET GLOBAL mroonga_default_parser = TokenBigram;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_ignore_and_log.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_ignore_and_log.result
new file mode 100644
index 00000000000..384d677c427
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_boolean_mode_syntax_error_ignore_and_log.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS memos;
+SET GLOBAL mroonga_default_parser = TokenDelimit;
+SET mroonga_action_on_fulltext_query_error = IGNORE_AND_LOG;
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT PRIMARY KEY,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=UTF8;
+INSERT INTO memos VALUES(1, "(groonga) Installed!");
+INSERT INTO memos VALUES(2, "(mroonga) Installed!");
+INSERT INTO memos VALUES(3, "(groonga) Upgraded!");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("(groonga" IN BOOLEAN MODE);
+id content
+DROP TABLE memos;
+SET GLOBAL mroonga_default_parser = TokenBigram;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_ascii.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_ascii.result
new file mode 100644
index 00000000000..c542ba1adae
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_ascii.result
@@ -0,0 +1,29 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3));
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"sa si su se so");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ee oo");
+select * from t1;
+c1 c2 c3
+1 10 aa ii uu ee oo
+2 20 ka ki ku ke ko
+3 30 sa si su se so
+4 40 ta ti tu te to
+5 50 aa ii uu ee oo
+select * from t1 where match(c3) against("su");
+c1 c2 c3
+3 30 sa si su se so
+select * from t1 where match(c3) against("ii");
+c1 c2 c3
+1 10 aa ii uu ee oo
+5 50 aa ii uu ee oo
+select * from t1 where match(c3) against("+su" in boolean mode);
+c1 c2 c3
+3 30 sa si su se so
+select * from t1 where match(c3) against("+ii" in boolean mode);
+c1 c2 c3
+1 10 aa ii uu ee oo
+5 50 aa ii uu ee oo
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_cp932.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_cp932.result
new file mode 100644
index 00000000000..bd208d520c5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_cp932.result
@@ -0,0 +1,18 @@
+drop table if exists t1, t2, t3;
+set names cp932;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset cp932;
+insert into t1 values(1, "̕xmR̓VCɂ‚","");
+insert into t1 values(2, "","̕xmR̓VC͕܂");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+c1 c2 c3
+1 ̕xmR̓VCɂ‚
+2 ̕xmR̓VC͕܂
+3 dummy dummy
+select * from t1 where match(c2) against("xmR");
+c1 c2 c3
+1 ̕xmR̓VCɂ‚
+select * from t1 where match(c3) against("xmR");
+c1 c2 c3
+2 ̕xmR̓VC͕܂
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_eucjpms.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_eucjpms.result
new file mode 100644
index 00000000000..51360875cf1
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_eucjpms.result
@@ -0,0 +1,18 @@
+drop table if exists t1, t2, t3;
+set names eucjpms;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset eucjpms;
+insert into t1 values(1, "ٻλŷˤĤ","");
+insert into t1 values(2, "","ٻλŷʬޤ");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+c1 c2 c3
+1 ٻλŷˤĤ
+2 ٻλŷʬޤ
+3 dummy dummy
+select * from t1 where match(c2) against("ٻλ");
+c1 c2 c3
+1 ٻλŷˤĤ
+select * from t1 where match(c3) against("ٻλ");
+c1 c2 c3
+2 ٻλŷʬޤ
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_japanese.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_japanese.result
new file mode 100644
index 00000000000..cfff3f2e29b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_japanese.result
@@ -0,0 +1,18 @@
+drop table if exists t1, t2, t3;
+set names utf8;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset utf8;
+insert into t1 values(1, "明日の富士山の天気について","あああああああ");
+insert into t1 values(2, "いいいいい","明日の富士山の天気は分かりません");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+c1 c2 c3
+1 明日の富士山の天気について あああああああ
+2 いいいいい 明日の富士山の天気は分かりません
+3 dummy dummy
+select * from t1 where match(c2) against("富士山");
+c1 c2 c3
+1 明日の富士山の天気について あああああああ
+select * from t1 where match(c3) against("富士山");
+c1 c2 c3
+2 いいいいい 明日の富士山の天気は分かりません
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_utf8mb4.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_utf8mb4.result
new file mode 100644
index 00000000000..31d45181c96
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_charset_utf8mb4.result
@@ -0,0 +1,26 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8mb4;
+CREATE TABLE diaries (
+id INT PRIMARY KEY,
+title VARCHAR(255) CHARSET utf8mb4 COLLATE utf8mb4_general_ci,
+content TEXT CHARSET utf8mb4 COLLATE utf8mb4_general_ci,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET utf8mb4;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8mb4
+INSERT INTO diaries VALUES(1, "Alphabet", "ABCDE");
+INSERT INTO diaries VALUES(2, "Mathmatics", "𝐀𝐁𝐂𝐃𝐄 | U+1D400-U+1D405");
+INSERT INTO diaries VALUES(3, "ひらがな", "あいうえお");
+SELECT *
+FROM diaries
+WHERE MATCH (content) AGAINST("ABCDE" IN BOOLEAN MODE);
+id title content
+1 Alphabet ABCDE
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_empty_query.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_empty_query.result
new file mode 100644
index 00000000000..bb7b3b63752
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_empty_query.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries(
+title TEXT,
+FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+SELECT * FROM diaries;
+title
+Start groonga
+Start mroonga
+Start groonga and Ruby
+SELECT *
+FROM diaries
+WHERE MATCH(title) AGAINST("");
+title
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_found_rows.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_found_rows.result
new file mode 100644
index 00000000000..c107991d151
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_found_rows.result
@@ -0,0 +1,42 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 11, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(6, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT SQL_CALC_FOUND_ROWS * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE) ORDER BY day LIMIT 0,5;
+id year month day title content
+5 2011 12 1 久しぶり 天気が悪いからずっと留守番。
+6 2011 12 2 初雪 今日の天気は雪!
+1 2011 11 9 Hello 今日からはじめました。
+2 2011 11 10 天気 明日の富士山の天気について
+3 2011 11 11 富士山 今日も天気がよくてきれいに見える。
+SELECT FOUND_ROWS();
+FOUND_ROWS()
+6
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_groonga_varchar_vector.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_groonga_varchar_vector.result
new file mode 100644
index 00000000000..a23d8e5b542
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_groonga_varchar_vector.result
@@ -0,0 +1,21 @@
+DROP TABLE IF EXISTS bugs, tags;
+CREATE TABLE tags (
+name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+COLLATE=utf8_bin
+COMMENT='default_tokenizer "TokenDelimit"';
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tags VARCHAR(40) COMMENT 'type "tags", flags "COLUMN_VECTOR"',
+FULLTEXT INDEX bugs_tags_index (tags) COMMENT 'table "tags"'
+) DEFAULT CHARSET=utf8;
+INSERT INTO bugs (id, tags) VALUES (1, "Linux MySQL");
+INSERT INTO bugs (id, tags) VALUES (2, "MySQL groonga");
+INSERT INTO bugs (id, tags) VALUES (3, "mroonga");
+SELECT *
+FROM bugs
+WHERE MATCH (tags) AGAINST ("MySQL" IN BOOLEAN MODE);
+id tags
+1 Linux MySQL
+2 MySQL groonga
+DROP TABLE bugs, tags;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_index_recreate.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_index_recreate.result
new file mode 100644
index 00000000000..04996d30f36
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_index_recreate.result
@@ -0,0 +1,41 @@
+drop table if exists diaries;
+set names utf8;
+create table diaries (
+id int primary key,
+title varchar(255),
+content text,
+fulltext index (title)
+) default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+select * from diaries where match(title) against("富士山");
+id title content
+3 富士山 今日もきれい。
+drop index title on diaries;
+select * from diaries where match(title) against("富士山");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+select * from diaries;
+id title content
+1 Hello はじめました。
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+create fulltext index new_title_index on diaries (title);
+select * from diaries where match(title) against("富士山");
+id title content
+3 富士山 今日もきれい。
+select * from diaries;
+id title content
+1 Hello はじめました。
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_insert_select.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_insert_select.result
new file mode 100644
index 00000000000..6c268ff7641
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_insert_select.result
@@ -0,0 +1,66 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 varchar(100), fulltext index(c2)) default charset utf8;
+create table t2 (c1 int primary key, c2 text, fulltext index(c2)) default charset utf8;
+insert into t1 values (1, "aa ii uu ee oo");
+insert into t1 values (2, "ka ki ku ke ko");
+insert into t1 values (3, "aa ii ii ii oo");
+insert into t1 values (4, "sa si su se so");
+insert into t1 values (5, "ta ti ii ii to");
+insert into t2 (c1,c2) select c1,c2 from t1;
+select * from t1;
+c1 c2
+1 aa ii uu ee oo
+2 ka ki ku ke ko
+3 aa ii ii ii oo
+4 sa si su se so
+5 ta ti ii ii to
+select * from t2;
+c1 c2
+1 aa ii uu ee oo
+2 ka ki ku ke ko
+3 aa ii ii ii oo
+4 sa si su se so
+5 ta ti ii ii to
+select * from t1 where c1=3;
+c1 c2
+3 aa ii ii ii oo
+select * from t2 where c1=3;
+c1 c2
+3 aa ii ii ii oo
+select * from t1 where c1>3 order by c1 desc;
+c1 c2
+5 ta ti ii ii to
+4 sa si su se so
+select * from t2 where c1>3 order by c1 asc;
+c1 c2
+4 sa si su se so
+5 ta ti ii ii to
+select * from t1 where c2>"s" order by c2 desc;
+c1 c2
+5 ta ti ii ii to
+4 sa si su se so
+select * from t2 where c2>"s" order by c1 asc;
+c1 c2
+4 sa si su se so
+5 ta ti ii ii to
+select * from t1 where match(c2) against("ii") order by match(c2) against("ii") desc;
+c1 c2
+3 aa ii ii ii oo
+5 ta ti ii ii to
+1 aa ii uu ee oo
+select * from t2 where match(c2) against("ii") order by match(c2) against("ii") asc;
+c1 c2
+1 aa ii uu ee oo
+5 ta ti ii ii to
+3 aa ii ii ii oo
+select c1,c2,match(c2) against("ii") from t1 where match(c2) against("ii");
+c1 c2 match(c2) against("ii")
+1 aa ii uu ee oo 174763
+3 aa ii ii ii oo 524289
+5 ta ti ii ii to 349526
+select c1,c2,match(c2) against("ii") from t2 where match(c2) against("ii");
+c1 c2 match(c2) against("ii")
+1 aa ii uu ee oo 174763
+3 aa ii ii ii oo 524289
+5 ta ti ii ii to 349526
+drop table t1,t2;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_insert_values.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_insert_values.result
new file mode 100644
index 00000000000..623c66daaf4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_insert_values.result
@@ -0,0 +1,25 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 text, fulltext index ft (c2));
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `c1` int(11) NOT NULL,
+ `c2` text,
+ PRIMARY KEY (`c1`),
+ FULLTEXT KEY `ft` (`c2`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+insert into t1 values (1, "hoge hoge");
+insert into t1 values (2, "fuga fuga");
+insert into t1 values (3, "moge moge");
+select * from t1;
+c1 c2
+1 hoge hoge
+2 fuga fuga
+3 moge moge
+flush tables;
+select * from t1;
+c1 c2
+1 hoge hoge
+2 fuga fuga
+3 moge moge
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_delete.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_delete.result
new file mode 100644
index 00000000000..843c4b958ba
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_delete.result
@@ -0,0 +1,34 @@
+drop table if exists diaries;
+set names utf8;
+create table diaries (
+id int primary key,
+title varchar(255),
+content text,
+fulltext index (title, content),
+fulltext index (title),
+fulltext index (content)
+) default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`,`content`),
+ FULLTEXT KEY `title_2` (`title`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+delete from diaries where id = 2;
+select * from diaries where match(title, content) against("富士山");
+id title content
+3 富士山 今日もきれい。
+select * from diaries where match(title) against("富士山");
+id title content
+3 富士山 今日もきれい。
+select * from diaries where match(content) against("富士山");
+id title content
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_insert.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_insert.result
new file mode 100644
index 00000000000..3856d7ecc10
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_insert.result
@@ -0,0 +1,40 @@
+drop table if exists diaries;
+set names utf8;
+create table diaries (
+id int primary key,
+title varchar(255),
+content text,
+fulltext index (title, content),
+fulltext index (title),
+fulltext index (content)
+) default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`,`content`),
+ FULLTEXT KEY `title_2` (`title`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+select * from diaries;
+id title content
+1 Hello はじめました。
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+select * from diaries where match(title, content) against("富士山");
+id title content
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+select * from diaries where match(title) against("富士山");
+id title content
+3 富士山 今日もきれい。
+select * from diaries where match(content) against("富士山");
+id title content
+2 天気 明日の富士山の天気について
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_recreate.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_recreate.result
new file mode 100644
index 00000000000..59b26574c73
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_recreate.result
@@ -0,0 +1,42 @@
+drop table if exists diaries;
+set names utf8;
+create table diaries (
+id int primary key,
+title varchar(255),
+content text,
+fulltext index (title, content),
+fulltext index (title),
+fulltext index (content)
+) default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`,`content`),
+ FULLTEXT KEY `title_2` (`title`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+select * from diaries where match(title, content) against("富士山");
+id title content
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+drop index title on diaries;
+select * from diaries where match(title, content) against("富士山");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+create fulltext index new_title_content_index on diaries (title, content);
+select * from diaries where match(title, content) against("富士山");
+id title content
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+select * from diaries;
+id title content
+1 Hello はじめました。
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_update.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_update.result
new file mode 100644
index 00000000000..d17ff6adf83
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_column_index_update.result
@@ -0,0 +1,37 @@
+drop table if exists diaries;
+set names utf8;
+create table diaries (
+id int primary key,
+title varchar(255),
+content text,
+fulltext index (title, content),
+fulltext index (title),
+fulltext index (content)
+) default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`,`content`),
+ FULLTEXT KEY `title_2` (`title`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+update diaries set title = "チョモランマ" where id = 3;
+update diaries set content = "チョモランマと富士山" where id = 1;
+select * from diaries where match(title, content) against("富士山");
+id title content
+1 Hello チョモランマと富士山
+2 天気 明日の富士山の天気について
+select * from diaries where match(title) against("富士山");
+id title content
+select * from diaries where match(content) against("富士山");
+id title content
+1 Hello チョモランマと富士山
+2 天気 明日の富士山の天気について
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_index.result
new file mode 100644
index 00000000000..81b40261a4c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_multiple_index.result
@@ -0,0 +1,33 @@
+drop table if exists diaries;
+create table diaries (
+id int primary key auto_increment,
+title text,
+body text,
+fulltext index title_index (title),
+fulltext index body_index (body)
+) default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into diaries (title, body) values ("survey", "will start groonga!");
+insert into diaries (title, body) values ("groonga (1)", "starting groonga...");
+insert into diaries (title, body) values ("groonga (2)", "started groonga.");
+select * from diaries
+where match(title) against("survey") and
+match(body) against("groonga");
+id title body
+1 survey will start groonga!
+select *, match(title) against("survey"), match(body) against("groonga")
+from diaries
+where match(title) against("survey") and
+match(body) against("groonga");
+id title body match(title) against("survey") match(body) against("groonga")
+1 survey will start groonga! 1048577 149797
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_no_primary_key.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_no_primary_key.result
new file mode 100644
index 00000000000..5f85632575b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_no_primary_key.result
@@ -0,0 +1,21 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES("Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES("天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES("富士山", "今日も天気がよくてきれいに見える。");
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("*D+ 今日 天気" IN BOOLEAN MODE);
+title content
+富士山 今日も天気がよくてきれいに見える。
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_not_match_against.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_not_match_against.result
new file mode 100644
index 00000000000..2c1666369a5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_not_match_against.result
@@ -0,0 +1,68 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3));
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,10,"ka ki ku ke ko");
+insert into t1 values(3,10,"aa ii uu ee oo");
+insert into t1 values(4,10,"ka ki ku ke ko");
+insert into t1 values(5,20,"aa ii uu ee oo");
+insert into t1 values(6,20,"ka ki ku ke ko");
+insert into t1 values(7,20,"aa ii uu ee oo");
+insert into t1 values(8,20,"ka ki ku ke ko");
+select * from t1;
+c1 c2 c3
+1 10 aa ii uu ee oo
+2 10 ka ki ku ke ko
+3 10 aa ii uu ee oo
+4 10 ka ki ku ke ko
+5 20 aa ii uu ee oo
+6 20 ka ki ku ke ko
+7 20 aa ii uu ee oo
+8 20 ka ki ku ke ko
+select * from t1 where match(c3) against("uu");
+c1 c2 c3
+1 10 aa ii uu ee oo
+3 10 aa ii uu ee oo
+5 20 aa ii uu ee oo
+7 20 aa ii uu ee oo
+select * from t1 where not match(c3) against("uu");
+c1 c2 c3
+2 10 ka ki ku ke ko
+4 10 ka ki ku ke ko
+6 20 ka ki ku ke ko
+8 20 ka ki ku ke ko
+select * from t1 where match(c3) against("dummy");
+c1 c2 c3
+select * from t1 where not match(c3) against("dummy");
+c1 c2 c3
+1 10 aa ii uu ee oo
+2 10 ka ki ku ke ko
+3 10 aa ii uu ee oo
+4 10 ka ki ku ke ko
+5 20 aa ii uu ee oo
+6 20 ka ki ku ke ko
+7 20 aa ii uu ee oo
+8 20 ka ki ku ke ko
+select * from t1 where c1 = 4 and not match(c3) against("uu");
+c1 c2 c3
+4 10 ka ki ku ke ko
+select * from t1 where c1 <= 4 and not match(c3) against("uu");
+c1 c2 c3
+2 10 ka ki ku ke ko
+4 10 ka ki ku ke ko
+select * from t1 where c1 > 4 and not match(c3) against("uu");
+c1 c2 c3
+6 20 ka ki ku ke ko
+8 20 ka ki ku ke ko
+select * from t1 where c2 = 10 and not match(c3) against("uu");
+c1 c2 c3
+2 10 ka ki ku ke ko
+4 10 ka ki ku ke ko
+select * from t1 where c2 >= 15 and not match(c3) against("uu");
+c1 c2 c3
+6 20 ka ki ku ke ko
+8 20 ka ki ku ke ko
+select * from t1 where c2 < 15 and not match(c3) against("uu");
+c1 c2 c3
+2 10 ka ki ku ke ko
+4 10 ka ki ku ke ko
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_or.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_or.result
new file mode 100644
index 00000000000..f1f7888ceeb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_or.result
@@ -0,0 +1,22 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries(
+title TEXT,
+FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+SELECT * FROM diaries;
+title
+Start groonga
+Start mroonga
+Start groonga and Ruby
+SELECT *
+FROM diaries
+WHERE MATCH(title) AGAINST("Ruby" IN BOOLEAN MODE) OR
+MATCH(title) AGAINST("mroonga" IN BOOLEAN MODE);
+title
+Start mroonga
+Start groonga and Ruby
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_different_against.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_different_against.result
new file mode 100644
index 00000000000..d1f0d6bc0ab
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_different_against.result
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries(
+title TEXT,
+FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+SELECT * FROM diaries;
+title
+Start groonga
+Start mroonga
+Start groonga and Ruby
+SELECT *, MATCH(title) AGAINST("groonga" IN BOOLEAN MODE) AS score
+FROM diaries
+WHERE MATCH(title) AGAINST("groonga mroonga" IN BOOLEAN MODE)
+ORDER BY MATCH(title) AGAINST("groonga" IN BOOLEAN MODE);
+title score
+Start mroonga 0
+Start groonga 1
+Start groonga and Ruby 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_different_match.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_different_match.result
new file mode 100644
index 00000000000..b4a07cd0b66
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_different_match.result
@@ -0,0 +1,24 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries(
+title TEXT,
+body TEXT,
+FULLTEXT KEY (title),
+FULLTEXT KEY (body)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES("Start groonga", "I read groonga's tutorial.");
+INSERT INTO diaries VALUES("Start mroonga", "I read mroonga's tutorial.");
+INSERT INTO diaries VALUES("Start groonga and Ruby", "I installed rroonga.");
+SELECT * FROM diaries;
+title body
+Start groonga I read groonga's tutorial.
+Start mroonga I read mroonga's tutorial.
+Start groonga and Ruby I installed rroonga.
+SELECT *, MATCH(body) AGAINST("groonga" IN BOOLEAN MODE) AS score
+FROM diaries
+WHERE MATCH(title) AGAINST("groonga" IN BOOLEAN MODE)
+ORDER BY MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+title body score
+Start groonga and Ruby I installed rroonga. 0
+Start groonga I read groonga's tutorial. 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_no_where.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_no_where.result
new file mode 100644
index 00000000000..125b35fb96e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_no_where.result
@@ -0,0 +1,22 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries(
+title TEXT,
+FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+SELECT * FROM diaries;
+title
+Start groonga
+Start mroonga
+Start groonga and Ruby
+SELECT *, MATCH(title) AGAINST("groonga" IN BOOLEAN MODE) AS score
+FROM diaries
+ORDER BY MATCH(title) AGAINST("groonga" IN BOOLEAN MODE);
+title score
+Start mroonga 0
+Start groonga 1
+Start groonga and Ruby 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_same_match_against.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_same_match_against.result
new file mode 100644
index 00000000000..a3a668c4445
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_order_same_match_against.result
@@ -0,0 +1,22 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries(
+title TEXT,
+FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+SELECT * FROM diaries;
+title
+Start groonga
+Start mroonga
+Start groonga and Ruby
+SELECT *, MATCH(title) AGAINST("groonga" IN BOOLEAN MODE) AS score
+FROM diaries
+WHERE MATCH(title) AGAINST("groonga" IN BOOLEAN MODE)
+ORDER BY MATCH(title) AGAINST("groonga" IN BOOLEAN MODE);
+title score
+Start groonga 1
+Start groonga and Ruby 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_parser_comment.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_parser_comment.result
new file mode 100644
index 00000000000..f2abfe85dd6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_parser_comment.result
@@ -0,0 +1,29 @@
+drop table if exists diaries;
+create table diaries (
+id int primary key auto_increment,
+body text,
+fulltext index body_index (body)
+comment 'parser "TokenBigramSplitSymbolAlphaDigit"'
+) default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `body_index` (`body`) COMMENT 'parser "TokenBigramSplitSymbolAlphaDigit"'
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into diaries (body) values ("will start groonga!");
+insert into diaries (body) values ("starting groonga...");
+insert into diaries (body) values ("started groonga.");
+select * from diaries;
+id body
+1 will start groonga!
+2 starting groonga...
+3 started groonga.
+select * from diaries where match(body) against("start");
+id body
+1 will start groonga!
+2 starting groonga...
+3 started groonga.
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_parser_default.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_parser_default.result
new file mode 100644
index 00000000000..6c04cae59f2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_parser_default.result
@@ -0,0 +1,33 @@
+drop table if exists diaries;
+set @mroonga_default_parser_backup=@@mroonga_default_parser;
+set global mroonga_default_parser=TokenBigramSplitSymbolAlphaDigit;
+create table diaries (
+id int primary key auto_increment,
+body text,
+fulltext index body_index (body)
+) default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into diaries (body) values ("will start groonga!");
+insert into diaries (body) values ("starting groonga...");
+insert into diaries (body) values ("started groonga.");
+insert into diaries (body) values ("finished groonga.");
+select * from diaries;
+id body
+1 will start groonga!
+2 starting groonga...
+3 started groonga.
+4 finished groonga.
+select * from diaries where match(body) against("start");
+id body
+1 will start groonga!
+2 starting groonga...
+3 started groonga.
+drop table diaries;
+set global mroonga_default_parser=@mroonga_default_parser_backup;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_parser_off.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_parser_off.result
new file mode 100644
index 00000000000..77765f61dc3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_parser_off.result
@@ -0,0 +1,42 @@
+DROP TABLE IF EXISTS variables;
+CREATE TABLE variables (
+id INT PRIMARY KEY AUTO_INCREMENT,
+name TEXT,
+FULLTEXT INDEX (name) COMMENT 'parser "off"'
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE variables;
+Table Create Table
+variables CREATE TABLE `variables` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `name` (`name`) COMMENT 'parser "off"'
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO variables (name) VALUES ("mroonga_database_path_prefix");
+INSERT INTO variables (name) VALUES ("mroonga_default_parser");
+INSERT INTO variables (name) VALUES ("mroonga_default_wrapper_engine");
+INSERT INTO variables (name) VALUES ("mroonga_dry_write");
+INSERT INTO variables (name) VALUES ("mroonga_enable_optimization");
+INSERT INTO variables (name) VALUES ("mroonga_libgroonga_version");
+INSERT INTO variables (name) VALUES ("mroonga_log_file");
+INSERT INTO variables (name) VALUES ("mroonga_log_level");
+INSERT INTO variables (name) VALUES ("mroonga_match_escalation_threshold");
+INSERT INTO variables (name) VALUES ("mroonga_version");
+SELECT * FROM variables;
+id name
+1 mroonga_database_path_prefix
+2 mroonga_default_parser
+3 mroonga_default_wrapper_engine
+4 mroonga_dry_write
+5 mroonga_enable_optimization
+6 mroonga_libgroonga_version
+7 mroonga_log_file
+8 mroonga_log_level
+9 mroonga_match_escalation_threshold
+10 mroonga_version
+SELECT * FROM variables
+WHERE MATCH (name) AGAINST ("mroonga_default*" IN BOOLEAN MODE);
+id name
+3 mroonga_default_wrapper_engine
+2 mroonga_default_parser
+DROP TABLE variables;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_two_inner_join.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_two_inner_join.result
new file mode 100644
index 00000000000..e39790ed5be
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_two_inner_join.result
@@ -0,0 +1,41 @@
+DROP TABLE IF EXISTS users, posts, comments;
+SET NAMES utf8;
+CREATE TABLE users (
+id int NOT NULL,
+name varchar(50) NOT NULL,
+PRIMARY KEY (id),
+KEY (name)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+CREATE TABLE posts (
+id int NOT NULL,
+content mediumtext,
+user_id int NOT NULL,
+PRIMARY KEY (id),
+FULLTEXT KEY (content)
+) DEFAULT CHARSET=utf8;
+CREATE TABLE comments (
+id int NOT NULL,
+user_id int NOT NULL,
+post_id int NOT NULL,
+PRIMARY KEY (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+INSERT INTO users VALUES (1, "Alice"),
+(2, "Bob"),
+(3, "Calros");
+INSERT INTO posts VALUES (1, "Hello!", 1),
+(2, "World!", 2),
+(3, "Great!", 3);
+INSERT INTO comments VALUES (1, 1, 1),
+(2, 2, 1),
+(3, 3, 3);
+SELECT *
+FROM comments
+INNER JOIN posts
+ON posts.id = comments.post_id AND
+MATCH (posts.content) AGAINST ("Hello!" IN BOOLEAN MODE)
+INNER JOIN users
+ON users.id = comments.user_id AND
+users.name = "Alice";
+id user_id post_id id content user_id id name
+1 1 1 1 Hello! 1 1 Alice
+DROP TABLE users, posts, comments;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_version_100_no_such_key.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_version_100_no_such_key.result
new file mode 100644
index 00000000000..d3ac2387586
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_version_100_no_such_key.result
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT,
+FULLTEXT INDEX title_index (title),
+FULLTEXT INDEX body_index (body)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+SELECT * FROM diaries FORCE INDEX(primary)
+WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE);
+ERROR HY000: Can't find FULLTEXT index matching the column list
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_version_55_no_such_key.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_version_55_no_such_key.result
new file mode 100644
index 00000000000..5f7bc98a3d3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_version_55_no_such_key.result
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT,
+FULLTEXT INDEX title_index (title),
+FULLTEXT INDEX body_index (body)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+SELECT * FROM diaries FORCE INDEX(primary)
+WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE);
+id title body
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_version_56_no_such_key.result b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_version_56_no_such_key.result
new file mode 100644
index 00000000000..d3ac2387586
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/fulltext_version_56_no_such_key.result
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT,
+FULLTEXT INDEX title_index (title),
+FULLTEXT INDEX body_index (body)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+SELECT * FROM diaries FORCE INDEX(primary)
+WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE);
+ERROR HY000: Can't find FULLTEXT index matching the column list
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_command_select.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_command_select.result
new file mode 100644
index 00000000000..1551d32c971
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_command_select.result
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries(
+title TEXT,
+FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+SELECT mroonga_command('select diaries --match_columns "title" --query "groonga"');
+mroonga_command('select diaries --match_columns "title" --query "groonga"')
+[[[2],[["_id","UInt32"],["title","LongText"]],[1,"Start groonga"],[3,"Start groonga and Ruby"]]]
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_error_query_is_missing.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_error_query_is_missing.result
new file mode 100644
index 00000000000..98213e81eb9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_error_query_is_missing.result
@@ -0,0 +1,3 @@
+SET NAMES UTF8;
+SELECT mroonga_escape() AS escaped_query;
+ERROR HY000: Can't initialize function 'mroonga_escape'; mroonga_escape(): Incorrect number of arguments: 0 for 1..2
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_error_query_is_not_string.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_error_query_is_not_string.result
new file mode 100644
index 00000000000..c1762458199
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_error_query_is_not_string.result
@@ -0,0 +1,3 @@
+SET NAMES UTF8;
+SELECT mroonga_escape(29) AS escaped_query;
+ERROR HY000: Can't initialize function 'mroonga_escape'; mroonga_escape(): The 1st argument must be query as string
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_error_target_characters_is_not_string.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_error_target_characters_is_not_string.result
new file mode 100644
index 00000000000..77dc420f0c5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_error_target_characters_is_not_string.result
@@ -0,0 +1,3 @@
+SET NAMES UTF8;
+SELECT mroonga_escape('+-><~*()\"\\:', 29) AS escaped_query;
+ERROR HY000: Can't initialize function 'mroonga_escape'; mroonga_escape(): The 2st argument must be escape target characters as string
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_success_all.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_success_all.result
new file mode 100644
index 00000000000..b002262a83f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_success_all.result
@@ -0,0 +1,4 @@
+SET NAMES UTF8;
+SELECT mroonga_escape('+-><~*()\"\\:') AS escaped_query;
+escaped_query
+\+\-\>\<\~\*\(\)\"\\\:
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_success_custom.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_success_custom.result
new file mode 100644
index 00000000000..c2e7d8f50ef
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_success_custom.result
@@ -0,0 +1,4 @@
+SET NAMES UTF8;
+SELECT mroonga_escape('+-><~*()\"\\:', '()<>~') AS escaped_query;
+escaped_query
++-\>\<\~*\(\)"\:
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_success_nested.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_success_nested.result
new file mode 100644
index 00000000000..5a57c144891
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_escape_success_nested.result
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries(
+title TEXT,
+FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+SELECT mroonga_escape(mroonga_escape('*groonga*'));
+mroonga_escape(mroonga_escape('*groonga*'))
+\\\*groonga\\\*
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_last_insert_grn_id.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_last_insert_grn_id.result
new file mode 100644
index 00000000000..42064b7f189
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_last_insert_grn_id.result
@@ -0,0 +1,28 @@
+drop table if exists t1, t2, t3;
+create table t1 (_id int, c1 int);
+select last_insert_grn_id();
+last_insert_grn_id()
+0
+insert into t1 values(null,100);
+insert into t1 values(null,100);
+select last_insert_grn_id();
+last_insert_grn_id()
+2
+insert into t1 values(null,100);
+insert into t1 values(null,100);
+select last_insert_grn_id();
+last_insert_grn_id()
+4
+insert into t1 values(null,100);
+insert into t1 values(null,100);
+select last_insert_grn_id();
+last_insert_grn_id()
+6
+insert into t1 values(null,100);
+insert into t1 values(null,100);
+select last_insert_grn_id();
+last_insert_grn_id()
+8
+select last_insert_grn_id(1);
+ERROR HY000: Can't initialize function 'last_insert_grn_id'; last_insert_grn_id must not have arguments
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_last_insert_id_reference.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_last_insert_id_reference.result
new file mode 100644
index 00000000000..b0db86cb5ec
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_last_insert_id_reference.result
@@ -0,0 +1,15 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id int AUTO_INCREMENT PRIMARY KEY
+);
+SELECT last_insert_id();
+last_insert_id()
+0
+INSERT INTO ids VALUES();
+SELECT last_insert_id();
+last_insert_id()
+1
+SELECT * FROM ids;
+id
+1
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_last_insert_id_set.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_last_insert_id_set.result
new file mode 100644
index 00000000000..ec6f2f98462
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_last_insert_id_set.result
@@ -0,0 +1,21 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id int AUTO_INCREMENT PRIMARY KEY
+);
+SELECT last_insert_id();
+last_insert_id()
+0
+SELECT last_insert_id(10);
+last_insert_id(10)
+10
+SELECT last_insert_id();
+last_insert_id()
+10
+INSERT INTO ids VALUES();
+SELECT last_insert_id();
+last_insert_id()
+1
+SELECT * FROM ids;
+id
+1
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_ascii.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_ascii.result
new file mode 100644
index 00000000000..5766f822ee3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_ascii.result
@@ -0,0 +1,120 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3));
+insert into t1 values(1,10,"aa bb cc dd ee >< ff gg hh ii jj kk ll mm nn");
+insert into t1 values(2,20,"nn mm ll kk jj >< ii hh gg ff ee dd cc bb aa");
+insert into t1 values(3,30,"cc dd ee ff gg >< hh ii jj kk ll mm nn oo pp");
+insert into t1 values(4,40,"ee ff gg hh ii >< jj kk ll mm nn oo pp qq rr");
+insert into t1 values(5,50,"AA BB CC DD EE >< FF GG HH II JJ KK LL MM NN");
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_general_ci', 0, 1, '...', '...<br>\n', 'bb', '<span class="w1">', '</span>', 'ff', '<span class="w2">', '</span>', 'dd', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'ascii_general_ci', 0, 1, '...', '...<br>\n', 'bb', '<span class="w1">', '</span>', 'ff', '<span class="w2">', '</span>', 'dd', '<span class="w3">', '</span>')
+1 10 ...a<span class="w1"> bb</span> cc<span class="w3"> dd</span>...<br>
+...e &gt;&lt;<span class="w2"> ff</span> gg...<br>
+
+2 20 ...g<span class="w2"> ff</span> ee<span class="w3"> dd</span>...<br>
+... cc<span class="w1"> bb</span> aa...<br>
+
+3 30 ...c<span class="w3"> dd</span> ee<span class="w2"> ff</span>...<br>
+
+4 40 ...ee<span class="w2"> ff</span> gg h...<br>
+
+5 50 ...A<span class="w1"> BB</span> CC<span class="w3"> DD</span>...<br>
+...E &gt;&lt;<span class="w2"> FF</span> GG...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_bin', 0, 1, '...', '...<br>\n', 'bb', '<span class="w1">', '</span>', 'ff', '<span class="w2">', '</span>', 'dd', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'ascii_bin', 0, 1, '...', '...<br>\n', 'bb', '<span class="w1">', '</span>', 'ff', '<span class="w2">', '</span>', 'dd', '<span class="w3">', '</span>')
+1 10 ... <span class="w1">bb</span> cc <span class="w3">dd</span> ...<br>
+... &gt;&lt; <span class="w2">ff</span> gg ...<br>
+
+2 20 ... <span class="w2">ff</span> ee <span class="w3">dd</span> ...<br>
+...cc <span class="w1">bb</span> aa...<br>
+
+3 30 ... <span class="w3">dd</span> ee <span class="w2">ff</span> ...<br>
+
+4 40 ...ee <span class="w2">ff</span> gg h...<br>
+
+5 50
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_general_ci', 1, 1, '...', '...<br>\n', 'bb', '<span class="w1">', '</span>', 'ff', '<span class="w2">', '</span>', 'dd', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'ascii_general_ci', 1, 1, '...', '...<br>\n', 'bb', '<span class="w1">', '</span>', 'ff', '<span class="w2">', '</span>', 'dd', '<span class="w3">', '</span>')
+1 10 ... <span class="w1">bb</span> cc <span class="w3">dd</span> ...<br>
+... &gt;&lt; <span class="w2">ff</span> gg ...<br>
+
+2 20 ... <span class="w2">ff</span> ee <span class="w3">dd</span> ...<br>
+...cc <span class="w1">bb</span> aa...<br>
+
+3 30 ... <span class="w3">dd</span> ee <span class="w2">ff</span> ...<br>
+
+4 40 ...ee <span class="w2">ff</span> gg h...<br>
+
+5 50 ... <span class="w1">BB</span> CC <span class="w3">DD</span> ...<br>
+... &gt;&lt; <span class="w2">FF</span> GG ...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_bin', 1, 1, '...', '...<br>\n', 'bb', '<span class="w1">', '</span>', 'ff', '<span class="w2">', '</span>', 'dd', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'ascii_bin', 1, 1, '...', '...<br>\n', 'bb', '<span class="w1">', '</span>', 'ff', '<span class="w2">', '</span>', 'dd', '<span class="w3">', '</span>')
+1 10 ... <span class="w1">bb</span> cc <span class="w3">dd</span> ...<br>
+... &gt;&lt; <span class="w2">ff</span> gg ...<br>
+
+2 20 ... <span class="w2">ff</span> ee <span class="w3">dd</span> ...<br>
+...cc <span class="w1">bb</span> aa...<br>
+
+3 30 ... <span class="w3">dd</span> ee <span class="w2">ff</span> ...<br>
+
+4 40 ...ee <span class="w2">ff</span> gg h...<br>
+
+5 50
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_general_ci', 0, 0, '...', '...\n', 'bb', '(w1)[', ']', 'ff', '(w2)[', ']', 'dd', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'ascii_general_ci', 0, 0, '...', '...\n', 'bb', '(w1)[', ']', 'ff', '(w2)[', ']', 'dd', '(w3)[', ']')
+1 10 ...a(w1)[ bb] cc(w3)[ dd]...
+...e ><(w2)[ ff] gg...
+
+2 20 ...g(w2)[ ff] ee(w3)[ dd]...
+... cc(w1)[ bb] aa...
+
+3 30 ...c(w3)[ dd] ee(w2)[ ff]...
+
+4 40 ...ee(w2)[ ff] gg h...
+
+5 50 ...A(w1)[ BB] CC(w3)[ DD]...
+...E ><(w2)[ FF] GG...
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_bin', 0, 0, '...', '...\n', 'bb', '(w1)[', ']', 'ff', '(w2)[', ']', 'dd', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'ascii_bin', 0, 0, '...', '...\n', 'bb', '(w1)[', ']', 'ff', '(w2)[', ']', 'dd', '(w3)[', ']')
+1 10 ... (w1)[bb] cc (w3)[dd] ...
+... >< (w2)[ff] gg ...
+
+2 20 ... (w2)[ff] ee (w3)[dd] ...
+...cc (w1)[bb] aa...
+
+3 30 ... (w3)[dd] ee (w2)[ff] ...
+
+4 40 ...ee (w2)[ff] gg h...
+
+5 50
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_general_ci', 1, 0, '...', '...\n', 'bb', '(w1)[', ']', 'ff', '(w2)[', ']', 'dd', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'ascii_general_ci', 1, 0, '...', '...\n', 'bb', '(w1)[', ']', 'ff', '(w2)[', ']', 'dd', '(w3)[', ']')
+1 10 ... (w1)[bb] cc (w3)[dd] ...
+... >< (w2)[ff] gg ...
+
+2 20 ... (w2)[ff] ee (w3)[dd] ...
+...cc (w1)[bb] aa...
+
+3 30 ... (w3)[dd] ee (w2)[ff] ...
+
+4 40 ...ee (w2)[ff] gg h...
+
+5 50 ... (w1)[BB] CC (w3)[DD] ...
+... >< (w2)[FF] GG ...
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_bin', 1, 0, '...', '...\n', 'bb', '(w1)[', ']', 'ff', '(w2)[', ']', 'dd', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'ascii_bin', 1, 0, '...', '...\n', 'bb', '(w1)[', ']', 'ff', '(w2)[', ']', 'dd', '(w3)[', ']')
+1 10 ... (w1)[bb] cc (w3)[dd] ...
+... >< (w2)[ff] gg ...
+
+2 20 ... (w2)[ff] ee (w3)[dd] ...
+...cc (w1)[bb] aa...
+
+3 30 ... (w3)[dd] ee (w2)[ff] ...
+
+4 40 ...ee (w2)[ff] gg h...
+
+5 50
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_cp932.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_cp932.result
new file mode 100644
index 00000000000..a21157a0a18
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_cp932.result
@@ -0,0 +1,91 @@
+drop table if exists t1, t2, t3;
+set names cp932;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset cp932;
+insert into t1 values(1, "","QX̕xmR̓VCɂ‚");
+insert into t1 values(2, "","QX̕xmR̓VC͕܂");
+insert into t1 values(3, "","29̕xmR̓VCɂ‚");
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_japanese_ci', 0, 1, '...', '...<br>\n', 'QX', '<span class="w1">', '</span>', 'VC', '<span class="w2">', '</span>', 'xmR', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'cp932_japanese_ci', 0, 1, '...', '...<br>\n', 'QX', '<span class="w1">', '</span>', 'VC', '<span class="w2">', '</span>', 'xmR', '<span class="w3">', '</span>')
+1 ...<span class="w1">QX</span>̕x...<br>
+...<span class="w2">VC</span>ɂ...<br>
+
+2 ...<span class="w1">QX</span>̕x...<br>
+...<span class="w2">VC</span>͕...<br>
+
+3 ...<span class="w1">29</span>̕xm...<br>
+...<span class="w2">VC</span>ɂ...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_bin', 0, 1, '...', '...<br>\n', 'QX', '<span class="w1">', '</span>', 'VC', '<span class="w2">', '</span>', 'xmR', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'cp932_bin', 0, 1, '...', '...<br>\n', 'QX', '<span class="w1">', '</span>', 'VC', '<span class="w2">', '</span>', 'xmR', '<span class="w3">', '</span>')
+1 ...<span class="w1">QX</span>̕x...<br>
+...<span class="w2">VC</span>ɂ...<br>
+
+2 ...<span class="w1">QX</span>̕x...<br>
+...<span class="w2">VC</span>͕...<br>
+
+3 ...<span class="w3">xmR</span>...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_japanese_ci', 1, 1, '...', '...<br>\n', 'QX', '<span class="w1">', '</span>', 'VC', '<span class="w2">', '</span>', 'xmR', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'cp932_japanese_ci', 1, 1, '...', '...<br>\n', 'QX', '<span class="w1">', '</span>', 'VC', '<span class="w2">', '</span>', 'xmR', '<span class="w3">', '</span>')
+1 ...<span class="w1">QX</span>̕x...<br>
+...<span class="w2">VC</span>ɂ...<br>
+
+2 ...<span class="w1">QX</span>̕x...<br>
+...<span class="w2">VC</span>͕...<br>
+
+3 ...<span class="w1">29</span>̕xm...<br>
+...<span class="w2">VC</span>ɂ...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_bin', 1, 1, '...', '...<br>\n', 'QX', '<span class="w1">', '</span>', 'VC', '<span class="w2">', '</span>', 'xmR', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'cp932_bin', 1, 1, '...', '...<br>\n', 'QX', '<span class="w1">', '</span>', 'VC', '<span class="w2">', '</span>', 'xmR', '<span class="w3">', '</span>')
+1 ...<span class="w1">QX</span>̕x...<br>
+...<span class="w2">VC</span>ɂ...<br>
+
+2 ...<span class="w1">QX</span>̕x...<br>
+...<span class="w2">VC</span>͕...<br>
+
+3 ...<span class="w3">xmR</span>...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_japanese_ci', 0, 0, '...', '...\n', 'QX', '(w1)[', ']', 'VC', '(w2)[', ']', 'xmR', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'cp932_japanese_ci', 0, 0, '...', '...\n', 'QX', '(w1)[', ']', 'VC', '(w2)[', ']', 'xmR', '(w3)[', ']')
+1 ...(w1)[QX]̕x...
+...(w2)[VC]ɂ...
+
+2 ...(w1)[QX]̕x...
+...(w2)[VC]͕...
+
+3 ...(w1)[29]̕xm...
+...(w2)[VC]ɂ...
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_bin', 0, 0, '...', '...\n', 'QX', '(w1)[', ']', 'VC', '(w2)[', ']', 'xmR', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'cp932_bin', 0, 0, '...', '...\n', 'QX', '(w1)[', ']', 'VC', '(w2)[', ']', 'xmR', '(w3)[', ']')
+1 ...(w1)[QX]̕x...
+...(w2)[VC]ɂ...
+
+2 ...(w1)[QX]̕x...
+...(w2)[VC]͕...
+
+3 ...(w3)[xmR]...
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_japanese_ci', 1, 0, '...', '...\n', 'QX', '(w1)[', ']', 'VC', '(w2)[', ']', 'xmR', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'cp932_japanese_ci', 1, 0, '...', '...\n', 'QX', '(w1)[', ']', 'VC', '(w2)[', ']', 'xmR', '(w3)[', ']')
+1 ...(w1)[QX]̕x...
+...(w2)[VC]ɂ...
+
+2 ...(w1)[QX]̕x...
+...(w2)[VC]͕...
+
+3 ...(w1)[29]̕xm...
+...(w2)[VC]ɂ...
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_bin', 1, 0, '...', '...\n', 'QX', '(w1)[', ']', 'VC', '(w2)[', ']', 'xmR', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'cp932_bin', 1, 0, '...', '...\n', 'QX', '(w1)[', ']', 'VC', '(w2)[', ']', 'xmR', '(w3)[', ']')
+1 ...(w1)[QX]̕x...
+...(w2)[VC]ɂ...
+
+2 ...(w1)[QX]̕x...
+...(w2)[VC]͕...
+
+3 ...(w3)[xmR]...
+
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_eucjpms.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_eucjpms.result
new file mode 100644
index 00000000000..e4c6a41773e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_eucjpms.result
@@ -0,0 +1,91 @@
+drop table if exists t1, t2, t3;
+set names eucjpms;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset eucjpms;
+insert into t1 values(1, "","ٻλŷˤĤ");
+insert into t1 values(2, "","ٻλŷʬޤ");
+insert into t1 values(3, "","29ٻλŷˤĤ");
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_japanese_ci', 0, 1, '...', '...<br>\n', '', '<span class="w1">', '</span>', 'ŷ', '<span class="w2">', '</span>', 'ٻλ', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'eucjpms_japanese_ci', 0, 1, '...', '...<br>\n', '', '<span class="w1">', '</span>', 'ŷ', '<span class="w2">', '</span>', 'ٻλ', '<span class="w3">', '</span>')
+1 ...<span class="w1"></span>...<br>
+...<span class="w2">ŷ</span>ˤ...<br>
+
+2 ...<span class="w1"></span>...<br>
+...<span class="w2">ŷ</span>ʬ...<br>
+
+3 ...<span class="w1">29</span>ٻ...<br>
+...<span class="w2">ŷ</span>ˤ...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_bin', 0, 1, '...', '...<br>\n', '', '<span class="w1">', '</span>', 'ŷ', '<span class="w2">', '</span>', 'ٻλ', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'eucjpms_bin', 0, 1, '...', '...<br>\n', '', '<span class="w1">', '</span>', 'ŷ', '<span class="w2">', '</span>', 'ٻλ', '<span class="w3">', '</span>')
+1 ...<span class="w1"></span>...<br>
+...<span class="w2">ŷ</span>ˤ...<br>
+
+2 ...<span class="w1"></span>...<br>
+...<span class="w2">ŷ</span>ʬ...<br>
+
+3 ...<span class="w3">ٻλ</span>...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_japanese_ci', 1, 1, '...', '...<br>\n', '', '<span class="w1">', '</span>', 'ŷ', '<span class="w2">', '</span>', 'ٻλ', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'eucjpms_japanese_ci', 1, 1, '...', '...<br>\n', '', '<span class="w1">', '</span>', 'ŷ', '<span class="w2">', '</span>', 'ٻλ', '<span class="w3">', '</span>')
+1 ...<span class="w1"></span>...<br>
+...<span class="w2">ŷ</span>ˤ...<br>
+
+2 ...<span class="w1"></span>...<br>
+...<span class="w2">ŷ</span>ʬ...<br>
+
+3 ...<span class="w1">29</span>ٻ...<br>
+...<span class="w2">ŷ</span>ˤ...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_bin', 1, 1, '...', '...<br>\n', '', '<span class="w1">', '</span>', 'ŷ', '<span class="w2">', '</span>', 'ٻλ', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'eucjpms_bin', 1, 1, '...', '...<br>\n', '', '<span class="w1">', '</span>', 'ŷ', '<span class="w2">', '</span>', 'ٻλ', '<span class="w3">', '</span>')
+1 ...<span class="w1"></span>...<br>
+...<span class="w2">ŷ</span>ˤ...<br>
+
+2 ...<span class="w1"></span>...<br>
+...<span class="w2">ŷ</span>ʬ...<br>
+
+3 ...<span class="w3">ٻλ</span>...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_japanese_ci', 0, 0, '...', '...\n', '', '(w1)[', ']', 'ŷ', '(w2)[', ']', 'ٻλ', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'eucjpms_japanese_ci', 0, 0, '...', '...\n', '', '(w1)[', ']', 'ŷ', '(w2)[', ']', 'ٻλ', '(w3)[', ']')
+1 ...(w1)[]...
+...(w2)[ŷ]ˤ...
+
+2 ...(w1)[]...
+...(w2)[ŷ]ʬ...
+
+3 ...(w1)[29]ٻ...
+...(w2)[ŷ]ˤ...
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_bin', 0, 0, '...', '...\n', '', '(w1)[', ']', 'ŷ', '(w2)[', ']', 'ٻλ', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'eucjpms_bin', 0, 0, '...', '...\n', '', '(w1)[', ']', 'ŷ', '(w2)[', ']', 'ٻλ', '(w3)[', ']')
+1 ...(w1)[]...
+...(w2)[ŷ]ˤ...
+
+2 ...(w1)[]...
+...(w2)[ŷ]ʬ...
+
+3 ...(w3)[ٻλ]...
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_japanese_ci', 1, 0, '...', '...\n', '', '(w1)[', ']', 'ŷ', '(w2)[', ']', 'ٻλ', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'eucjpms_japanese_ci', 1, 0, '...', '...\n', '', '(w1)[', ']', 'ŷ', '(w2)[', ']', 'ٻλ', '(w3)[', ']')
+1 ...(w1)[]...
+...(w2)[ŷ]ˤ...
+
+2 ...(w1)[]...
+...(w2)[ŷ]ʬ...
+
+3 ...(w1)[29]ٻ...
+...(w2)[ŷ]ˤ...
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_bin', 1, 0, '...', '...\n', '', '(w1)[', ']', 'ŷ', '(w2)[', ']', 'ٻλ', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'eucjpms_bin', 1, 0, '...', '...\n', '', '(w1)[', ']', 'ŷ', '(w2)[', ']', 'ٻλ', '(w3)[', ']')
+1 ...(w1)[]...
+...(w2)[ŷ]ˤ...
+
+2 ...(w1)[]...
+...(w2)[ŷ]ʬ...
+
+3 ...(w3)[ٻλ]...
+
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_invalid_nonexistent_charset.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_invalid_nonexistent_charset.result
new file mode 100644
index 00000000000..78cee62696f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_invalid_nonexistent_charset.result
@@ -0,0 +1,4 @@
+SET NAMES UTF8;
+SELECT mroonga_snippet("Invalid charset test", 10, 2, "nonexistent_charset",
+1, 0, "...", "...", "charset", "<", ">");
+ERROR HY000: Can't initialize function 'mroonga_snippet'; Unknown charset: <nonexistent_charset>
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_invalid_unsupported_charset.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_invalid_unsupported_charset.result
new file mode 100644
index 00000000000..bf967adbf46
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_invalid_unsupported_charset.result
@@ -0,0 +1,4 @@
+SET NAMES UTF8;
+SELECT mroonga_snippet("Unsuppported charset test", 10, 2, "big5",
+1, 0, "...", "...", "charset", "<", ">");
+ERROR HY000: Can't initialize function 'mroonga_snippet'; Unknown charset: <big5>
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_japanese.result b/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_japanese.result
new file mode 100644
index 00000000000..05a63a3d597
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/function_snippet_japanese.result
@@ -0,0 +1,95 @@
+drop table if exists t1, t2, t3;
+set names utf8;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset utf8;
+insert into t1 values(1, "あああああ","29日の富士山の天気について");
+insert into t1 values(2, "いいいいい","29日の富士山の天気は分かりません");
+insert into t1 values(3, "ううううう","29日の富士山の天気について");
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_general_ci', 0, 1, '...', '...<br>\n', '29日', '<span class="w1">', '</span>', '天気', '<span class="w2">', '</span>', '富士山', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'utf8_general_ci', 0, 1, '...', '...<br>\n', '29日', '<span class="w1">', '</span>', '天気', '<span class="w2">', '</span>', '富士山', '<span class="w3">', '</span>')
+1 あああああ ...<span class="w1">29日</span>...<br>
+...<span class="w3">富士山</span>...<br>
+
+2 いいいいい ...<span class="w1">29日</span>...<br>
+...<span class="w3">富士山</span>...<br>
+
+3 ううううう ...<span class="w1">29日</span>の...<br>
+...<span class="w2">天気</span>に...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_bin', 0, 1, '...', '...<br>\n', '29日', '<span class="w1">', '</span>', '天気', '<span class="w2">', '</span>', '富士山', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'utf8_bin', 0, 1, '...', '...<br>\n', '29日', '<span class="w1">', '</span>', '天気', '<span class="w2">', '</span>', '富士山', '<span class="w3">', '</span>')
+1 あああああ ...<span class="w1">29日</span>...<br>
+...<span class="w3">富士山</span>...<br>
+
+2 いいいいい ...<span class="w1">29日</span>...<br>
+...<span class="w3">富士山</span>...<br>
+
+3 ううううう ...<span class="w3">富士山</span>...<br>
+...<span class="w2">天気</span>に...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_general_ci', 1, 1, '...', '...<br>\n', '29日', '<span class="w1">', '</span>', '天気', '<span class="w2">', '</span>', '富士山', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'utf8_general_ci', 1, 1, '...', '...<br>\n', '29日', '<span class="w1">', '</span>', '天気', '<span class="w2">', '</span>', '富士山', '<span class="w3">', '</span>')
+1 あああああ ...<span class="w1">29日</span>...<br>
+...<span class="w3">富士山</span>...<br>
+
+2 いいいいい ...<span class="w1">29日</span>...<br>
+...<span class="w3">富士山</span>...<br>
+
+3 ううううう ...<span class="w1">29日</span>の...<br>
+...<span class="w2">天気</span>に...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_bin', 1, 1, '...', '...<br>\n', '29日', '<span class="w1">', '</span>', '天気', '<span class="w2">', '</span>', '富士山', '<span class="w3">', '</span>') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'utf8_bin', 1, 1, '...', '...<br>\n', '29日', '<span class="w1">', '</span>', '天気', '<span class="w2">', '</span>', '富士山', '<span class="w3">', '</span>')
+1 あああああ ...<span class="w1">29日</span>...<br>
+...<span class="w3">富士山</span>...<br>
+
+2 いいいいい ...<span class="w1">29日</span>...<br>
+...<span class="w3">富士山</span>...<br>
+
+3 ううううう ...<span class="w3">富士山</span>...<br>
+...<span class="w2">天気</span>に...<br>
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_general_ci', 0, 0, '...', '...\n', '29日', '(w1)[', ']', '天気', '(w2)[', ']', '富士山', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'utf8_general_ci', 0, 0, '...', '...\n', '29日', '(w1)[', ']', '天気', '(w2)[', ']', '富士山', '(w3)[', ']')
+1 あああああ ...(w1)[29日]...
+...(w3)[富士山]...
+
+2 いいいいい ...(w1)[29日]...
+...(w3)[富士山]...
+
+3 ううううう ...(w1)[29日]の...
+...(w2)[天気]に...
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_bin', 0, 0, '...', '...\n', '29日', '(w1)[', ']', '天気', '(w2)[', ']', '富士山', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'utf8_bin', 0, 0, '...', '...\n', '29日', '(w1)[', ']', '天気', '(w2)[', ']', '富士山', '(w3)[', ']')
+1 あああああ ...(w1)[29日]...
+...(w3)[富士山]...
+
+2 いいいいい ...(w1)[29日]...
+...(w3)[富士山]...
+
+3 ううううう ...(w3)[富士山]...
+...(w2)[天気]に...
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_general_ci', 1, 0, '...', '...\n', '29日', '(w1)[', ']', '天気', '(w2)[', ']', '富士山', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'utf8_general_ci', 1, 0, '...', '...\n', '29日', '(w1)[', ']', '天気', '(w2)[', ']', '富士山', '(w3)[', ']')
+1 あああああ ...(w1)[29日]...
+...(w3)[富士山]...
+
+2 いいいいい ...(w1)[29日]...
+...(w3)[富士山]...
+
+3 ううううう ...(w1)[29日]の...
+...(w2)[天気]に...
+
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_bin', 1, 0, '...', '...\n', '29日', '(w1)[', ']', '天気', '(w2)[', ']', '富士山', '(w3)[', ']') from t1;
+c1 c2 mroonga_snippet(c3, 10, 2, 'utf8_bin', 1, 0, '...', '...\n', '29日', '(w1)[', ']', '天気', '(w2)[', ']', '富士山', '(w3)[', ']')
+1 あああああ ...(w1)[29日]...
+...(w3)[富士山]...
+
+2 いいいいい ...(w1)[29日]...
+...(w3)[富士山]...
+
+3 ううううう ...(w3)[富士山]...
+...(w2)[天気]に...
+
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/geometry_bulk_insert_null.result b/storage/mroonga/mysql-test/mroonga/storage/r/geometry_bulk_insert_null.result
new file mode 100644
index 00000000000..2e9314db109
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/geometry_bulk_insert_null.result
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS shops;
+CREATE TABLE shops (
+location GEOMETRY NOT NULL
+);
+INSERT INTO shops VALUES (NULL), (NULL);
+Warnings:
+Warning 1048 Column 'location' cannot be null
+Warning 1048 Column 'location' cannot be null
+SELECT AsText(location) FROM shops;
+AsText(location)
+POINT(0 0)
+POINT(0 0)
+DROP TABLE shops;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/geometry_contains.result b/storage/mroonga/mysql-test/mroonga/storage/r/geometry_contains.result
new file mode 100644
index 00000000000..e88402a10b4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/geometry_contains.result
@@ -0,0 +1,170 @@
+DROP TABLE IF EXISTS shops;
+CREATE TABLE shops (
+id INT PRIMARY KEY AUTO_INCREMENT,
+name TEXT,
+location GEOMETRY NOT NULL,
+SPATIAL KEY location_index (location)
+);
+SHOW CREATE TABLE shops;
+Table Create Table
+shops CREATE TABLE `shops` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` text,
+ `location` geometry NOT NULL,
+ PRIMARY KEY (`id`),
+ SPATIAL KEY `location_index` (`location`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+INSERT INTO shops (name, location)
+VALUES ('nezu-no-taiyaki',
+GeomFromText('POINT(139.762573 35.720253)'));
+INSERT INTO shops (name, location)
+VALUES ('taiyaki-kataoka',
+GeomFromText('POINT(139.715591 35.712521)'));
+INSERT INTO shops (name, location)
+VALUES ('soba-taiyaki-ku',
+GeomFromText('POINT(139.659088 35.683712)'));
+INSERT INTO shops (name, location)
+VALUES ('kuruma',
+GeomFromText('POINT(139.706207 35.721516)'));
+INSERT INTO shops (name, location)
+VALUES ('hirose-ya',
+GeomFromText('POINT(139.685608 35.714844)'));
+INSERT INTO shops (name, location)
+VALUES ('sazare',
+GeomFromText('POINT(139.685043 35.714653)'));
+INSERT INTO shops (name, location)
+VALUES ('omede-taiyaki',
+GeomFromText('POINT(139.817154 35.700516)'));
+INSERT INTO shops (name, location)
+VALUES ('onaga-ya',
+GeomFromText('POINT(139.81105 35.698254)'));
+INSERT INTO shops (name, location)
+VALUES ('shiro-ya',
+GeomFromText('POINT(139.638611 35.705517)'));
+INSERT INTO shops (name, location)
+VALUES ('fuji-ya',
+GeomFromText('POINT(139.637115 35.703938)'));
+INSERT INTO shops (name, location)
+VALUES ('miyoshi',
+GeomFromText('POINT(139.537323 35.644539)'));
+INSERT INTO shops (name, location)
+VALUES ('juju-ya',
+GeomFromText('POINT(139.695755 35.628922)'));
+INSERT INTO shops (name, location)
+VALUES ('tatsumi-ya',
+GeomFromText('POINT(139.638657 35.665501)'));
+INSERT INTO shops (name, location)
+VALUES ('tetsuji',
+GeomFromText('POINT(139.76857 35.680912)'));
+INSERT INTO shops (name, location)
+VALUES ('gazuma-ya',
+GeomFromText('POINT(139.647598 35.700817)'));
+INSERT INTO shops (name, location)
+VALUES ('honma-mon',
+GeomFromText('POINT(139.652573 35.722736)'));
+INSERT INTO shops (name, location)
+VALUES ('naniwa-ya',
+GeomFromText('POINT(139.796234 35.730061)'));
+INSERT INTO shops (name, location)
+VALUES ('kuro-dai',
+GeomFromText('POINT(139.704834 35.650345)'));
+INSERT INTO shops (name, location)
+VALUES ('daruma',
+GeomFromText('POINT(139.770599 35.681461)'));
+INSERT INTO shops (name, location)
+VALUES ('yanagi-ya',
+GeomFromText('POINT(139.783981 35.685341)'));
+INSERT INTO shops (name, location)
+VALUES ('sharaku',
+GeomFromText('POINT(139.794846 35.716969)'));
+INSERT INTO shops (name, location)
+VALUES ('takane',
+GeomFromText('POINT(139.560913 35.698601)'));
+INSERT INTO shops (name, location)
+VALUES ('chiyoda',
+GeomFromText('POINT(139.652817 35.642601)'));
+INSERT INTO shops (name, location)
+VALUES ('da-ka-po',
+GeomFromText('POINT(139.727356 35.627346)'));
+INSERT INTO shops (name, location)
+VALUES ('matsushima-ya',
+GeomFromText('POINT(139.737381 35.640556)'));
+INSERT INTO shops (name, location)
+VALUES ('kazuya',
+GeomFromText('POINT(139.760895 35.673508)'));
+INSERT INTO shops (name, location)
+VALUES ('furuya-kogane-an',
+GeomFromText('POINT(139.676071 35.680603)'));
+INSERT INTO shops (name, location)
+VALUES ('hachi-no-ie',
+GeomFromText('POINT(139.668106 35.608021)'));
+INSERT INTO shops (name, location)
+VALUES ('azuki-chan',
+GeomFromText('POINT(139.673203 35.64151)'));
+INSERT INTO shops (name, location)
+VALUES ('kuriko-an',
+GeomFromText('POINT(139.796829 35.712013)'));
+INSERT INTO shops (name, location)
+VALUES ('yume-no-aru-machi-no-taiyaki-ya-san',
+GeomFromText('POINT(139.712524 35.616199)'));
+INSERT INTO shops (name, location)
+VALUES ('naze-ya',
+GeomFromText('POINT(139.665833 35.609039)'));
+INSERT INTO shops (name, location)
+VALUES ('sanoki-ya',
+GeomFromText('POINT(139.770721 35.66592)'));
+INSERT INTO shops (name, location)
+VALUES ('shigeta',
+GeomFromText('POINT(139.780273 35.672626)'));
+INSERT INTO shops (name, location)
+VALUES ('nishimi-ya',
+GeomFromText('POINT(139.774628 35.671825)'));
+INSERT INTO shops (name, location)
+VALUES ('hiiragi',
+GeomFromText('POINT(139.711517 35.647701)'));
+SELECT id, name, AsText(location) AS location_text FROM shops;
+id name location_text
+1 nezu-no-taiyaki POINT(139.76257305555555 35.72025305555556)
+2 taiyaki-kataoka POINT(139.7155911111111 35.712521111111116)
+3 soba-taiyaki-ku POINT(139.65908805555557 35.68371194444445)
+4 kuruma POINT(139.70620694444446 35.72151611111111)
+5 hirose-ya POINT(139.68560805555555 35.71484388888889)
+6 sazare POINT(139.68504305555555 35.71465305555556)
+7 omede-taiyaki POINT(139.8171538888889 35.70051611111111)
+8 onaga-ya POINT(139.81105 35.69825388888889)
+9 shiro-ya POINT(139.63861111111112 35.70551694444445)
+10 fuji-ya POINT(139.637115 35.703938055555554)
+11 miyoshi POINT(139.53732305555556 35.644538888888896)
+12 juju-ya POINT(139.69575500000002 35.62892194444445)
+13 tatsumi-ya POINT(139.63865694444445 35.66550111111111)
+14 tetsuji POINT(139.76857 35.680911944444446)
+15 gazuma-ya POINT(139.64759805555553 35.70081694444444)
+16 honma-mon POINT(139.65257305555556 35.72273611111111)
+17 naniwa-ya POINT(139.79623388888888 35.73006111111111)
+18 kuro-dai POINT(139.70483388888888 35.650345)
+19 daruma POINT(139.7705988888889 35.68146111111111)
+20 yanagi-ya POINT(139.78398111111113 35.685341111111114)
+21 sharaku POINT(139.79484611111113 35.71696888888889)
+22 takane POINT(139.56091305555555 35.69860111111112)
+23 chiyoda POINT(139.65281694444442 35.64260111111111)
+24 da-ka-po POINT(139.72735611111113 35.62734611111111)
+25 matsushima-ya POINT(139.73738111111112 35.64055611111111)
+26 kazuya POINT(139.760895 35.67350805555556)
+27 furuya-kogane-an POINT(139.67607111111113 35.68060305555556)
+28 hachi-no-ie POINT(139.66810611111111 35.608021111111114)
+29 azuki-chan POINT(139.67320305555555 35.641510000000004)
+30 kuriko-an POINT(139.79682888888888 35.71201305555556)
+31 yume-no-aru-machi-no-taiyaki-ya-san POINT(139.71252388888888 35.61619888888889)
+32 naze-ya POINT(139.66583305555557 35.60903888888889)
+33 sanoki-ya POINT(139.7707211111111 35.66592)
+34 shigeta POINT(139.78027305555557 35.67262611111111)
+35 nishimi-ya POINT(139.77462805555555 35.671825)
+36 hiiragi POINT(139.71151694444444 35.64770111111111)
+SELECT id, name, AsText(location) AS location_text FROM shops
+WHERE MBRContains(GeomFromText('LineString(139.7727 35.6684, 139.7038 35.7121)'), location)
+ORDER BY id;
+id name location_text
+14 tetsuji POINT(139.76857 35.680911944444446)
+19 daruma POINT(139.7705988888889 35.68146111111111)
+26 kazuya POINT(139.760895 35.67350805555556)
+DROP TABLE shops;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_btree_equal_datetime.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_btree_equal_datetime.result
new file mode 100644
index 00000000000..78ed69f0ef8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_btree_equal_datetime.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+created_at datetime,
+title varchar(256),
+KEY created_at_key(created_at)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES ("1000-01-01 00:00:00", "The start");
+INSERT INTO diaries VALUES ("2012-10-25 16:18:29", "Today is shiny day.");
+INSERT INTO diaries VALUES ("9999-12-31 23:59:59", "The end");
+SELECT *
+FROM diaries FORCE INDEX(created_at_key)
+WHERE created_at = "2012-10-25 16:18:29";
+created_at title
+2012-10-25 16:18:29 Today is shiny day.
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_btree_equal_time.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_btree_equal_time.result
new file mode 100644
index 00000000000..a55a184d17a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_btree_equal_time.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS timer;
+CREATE TABLE timer (
+id int PRIMARY KEY,
+elapsed time,
+KEY elapsed_key(elapsed)
+);
+INSERT INTO timer VALUES (1, "00:00:00");
+INSERT INTO timer VALUES (2, "15:11:12");
+INSERT INTO timer VALUES (3, "838:59:59");
+INSERT INTO timer VALUES (4, "-838:59:59");
+SELECT *
+FROM timer FORCE INDEX(elapsed_key)
+WHERE elapsed = "-838:59:59";
+id elapsed
+4 -838:59:59
+DROP TABLE timer;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_btree_equal_timestamp.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_btree_equal_timestamp.result
new file mode 100644
index 00000000000..57eb6ae2cee
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_btree_equal_timestamp.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+created_at timestamp,
+title varchar(256),
+KEY created_at_key(created_at)
+) DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES ("1970-01-01 12:00:00", "The start");
+INSERT INTO diaries VALUES ("2012-10-05 16:18:29", "Today is shiny day.");
+INSERT INTO diaries VALUES ("2038-01-18 15:14:07", "The end");
+SELECT *
+FROM diaries FORCE INDEX(created_at_key)
+WHERE created_at = "2012-10-05 16:18:29";
+created_at title
+2012-10-05 16:18:29 Today is shiny day.
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_btree_normal_column_insert.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_btree_normal_column_insert.result
new file mode 100644
index 00000000000..043d1e3c1bc
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_btree_normal_column_insert.result
@@ -0,0 +1,25 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 int, index using btree (c2));
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `c1` int(11) NOT NULL,
+ `c2` int(11) DEFAULT NULL,
+ PRIMARY KEY (`c1`),
+ KEY `c2` (`c2`) USING BTREE
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+insert into t1 values (1, 100);
+insert into t1 values (2, 101);
+insert into t1 values (3, 102);
+select * from t1;
+c1 c2
+1 100
+2 101
+3 102
+flush tables;
+select * from t1;
+c1 c2
+1 100
+2 101
+3 102
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_hash_id_normal.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_hash_id_normal.result
new file mode 100644
index 00000000000..5f92c086ea3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_hash_id_normal.result
@@ -0,0 +1,18 @@
+drop table if exists t1, t2, t3;
+create table t1 (_id int, a int, key (_id) using hash);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+select * from t1;
+_id a
+1 100
+2 100
+3 100
+4 100
+select * from t1 where _id = 2;
+_id a
+2 100
+select * from t1 where _id = 20;
+_id a
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_hash_id_primary.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_hash_id_primary.result
new file mode 100644
index 00000000000..508ee135ef2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_hash_id_primary.result
@@ -0,0 +1,28 @@
+drop table if exists t1, t2, t3;
+create table t1 (_id int, a int, primary key (_id) using hash);
+insert into t1 values(null, 100);
+ERROR 23000: Column '_id' cannot be null
+insert into t1 values(1,100);
+Warnings:
+Warning 1265 Data truncated for column '_id' at row 1
+insert into t1 values(1,100);
+Warnings:
+Warning 1265 Data truncated for column '_id' at row 1
+insert into t1 values(1,100);
+Warnings:
+Warning 1265 Data truncated for column '_id' at row 1
+insert into t1 values(1,100);
+Warnings:
+Warning 1265 Data truncated for column '_id' at row 1
+select * from t1;
+_id a
+1 100
+2 100
+3 100
+4 100
+select * from t1 where _id = 2;
+_id a
+2 100
+select * from t1 where _id = 20;
+_id a
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_hash_id_unique.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_hash_id_unique.result
new file mode 100644
index 00000000000..1a30a1ecbf4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_hash_id_unique.result
@@ -0,0 +1,18 @@
+drop table if exists t1, t2, t3;
+create table t1 (_id int, a int, unique key (_id) using hash);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+select * from t1;
+_id a
+1 100
+2 100
+3 100
+4 100
+select * from t1 where _id = 2;
+_id a
+2 100
+select * from t1 where _id = 20;
+_id a
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_hash_normal_column_insert.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_hash_normal_column_insert.result
new file mode 100644
index 00000000000..6e642ce1272
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_hash_normal_column_insert.result
@@ -0,0 +1,25 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 int, index using hash (c2));
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `c1` int(11) NOT NULL,
+ `c2` int(11) DEFAULT NULL,
+ PRIMARY KEY (`c1`),
+ KEY `c2` (`c2`) USING HASH
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+insert into t1 values (1, 100);
+insert into t1 values (2, 101);
+insert into t1 values (3, 102);
+select * from t1;
+c1 c2
+1 100
+2 101
+3 102
+flush tables;
+select * from t1;
+c1 c2
+1 100
+2 101
+3 102
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_delete.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_delete.result
new file mode 100644
index 00000000000..c680a3733f3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_delete.result
@@ -0,0 +1,32 @@
+drop table if exists listing;
+set names utf8;
+create table scores (
+id int primary key auto_increment not null,
+name char(30) not null,
+score int not null,
+index property (name, score)
+) default charset utf8;
+show create table scores;
+Table Create Table
+scores CREATE TABLE `scores` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` char(30) NOT NULL,
+ `score` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `property` (`name`,`score`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into scores (name, score) values("Taro Yamada", 29);
+insert into scores (name, score) values("Taro Yamada", -12);
+insert into scores (name, score) values("Jiro Yamada", 27);
+insert into scores (name, score) values("Taro Yamada", 10);
+select * from scores;
+id name score
+1 Taro Yamada 29
+2 Taro Yamada -12
+3 Jiro Yamada 27
+4 Taro Yamada 10
+delete from scores where name = "Taro Yamada" and score = 10;
+select * from scores where name = "Taro Yamada" and (score >= -12 and score < 29);
+id name score
+2 Taro Yamada -12
+drop table scores;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_smallint.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_smallint.result
new file mode 100644
index 00000000000..a627f432068
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_smallint.result
@@ -0,0 +1,65 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (
+id INT PRIMARY KEY AUTO_INCREMENT,
+c1 SMALLINT,
+c2 SMALLINT,
+KEY idx1(c1, c2)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `c1` smallint(6) DEFAULT NULL,
+ `c2` smallint(6) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `idx1` (`c1`,`c2`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO t1 (c1, c2) VALUES
+(1999, 12),
+(2000, 11),
+(2001, 10),
+(2002, 9),
+(2003, 8),
+(2004, 7),
+(2005, 6),
+(2006, 5),
+(2007, 4),
+(2008, 3),
+(2009, 2),
+(2010, 1);
+SELECT * FROM t1 WHERE c1 > 2005;
+id c1 c2
+8 2006 5
+9 2007 4
+10 2008 3
+11 2009 2
+12 2010 1
+SELECT * FROM t1 WHERE c1 >= 2005;
+id c1 c2
+7 2005 6
+8 2006 5
+9 2007 4
+10 2008 3
+11 2009 2
+12 2010 1
+SELECT * FROM t1 WHERE c1 = 2005;
+id c1 c2
+7 2005 6
+SELECT * FROM t1 WHERE c1 <= 2005;
+id c1 c2
+1 1999 12
+2 2000 11
+3 2001 10
+4 2002 9
+5 2003 8
+6 2004 7
+7 2005 6
+SELECT * FROM t1 WHERE c1 < 2005;
+id c1 c2
+1 1999 12
+2 2000 11
+3 2001 10
+4 2002 9
+5 2003 8
+6 2004 7
+DROP TABLE t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_unsigned_bigint.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_unsigned_bigint.result
new file mode 100644
index 00000000000..368f88a64e1
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_unsigned_bigint.result
@@ -0,0 +1,65 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (
+id INT PRIMARY KEY AUTO_INCREMENT,
+c1 BIGINT UNSIGNED,
+c2 BIGINT UNSIGNED,
+KEY idx1(c1, c2)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `c1` bigint(20) unsigned DEFAULT NULL,
+ `c2` bigint(20) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `idx1` (`c1`,`c2`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO t1 (c1, c2) VALUES
+(1999, 12),
+(2000, 11),
+(2001, 10),
+(2002, 9),
+(2003, 8),
+(2004, 7),
+(2005, 6),
+(2006, 5),
+(2007, 4),
+(2008, 3),
+(2009, 2),
+(2010, 1);
+SELECT * FROM t1 WHERE c1 > 2005;
+id c1 c2
+8 2006 5
+9 2007 4
+10 2008 3
+11 2009 2
+12 2010 1
+SELECT * FROM t1 WHERE c1 >= 2005;
+id c1 c2
+7 2005 6
+8 2006 5
+9 2007 4
+10 2008 3
+11 2009 2
+12 2010 1
+SELECT * FROM t1 WHERE c1 = 2005;
+id c1 c2
+7 2005 6
+SELECT * FROM t1 WHERE c1 <= 2005;
+id c1 c2
+1 1999 12
+2 2000 11
+3 2001 10
+4 2002 9
+5 2003 8
+6 2004 7
+7 2005 6
+SELECT * FROM t1 WHERE c1 < 2005;
+id c1 c2
+1 1999 12
+2 2000 11
+3 2001 10
+4 2002 9
+5 2003 8
+6 2004 7
+DROP TABLE t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_unsigned_int.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_unsigned_int.result
new file mode 100644
index 00000000000..6d5516f7eea
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_unsigned_int.result
@@ -0,0 +1,65 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (
+id INT PRIMARY KEY AUTO_INCREMENT,
+c1 INT UNSIGNED,
+c2 INT UNSIGNED,
+KEY idx1(c1, c2)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `c1` int(10) unsigned DEFAULT NULL,
+ `c2` int(10) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `idx1` (`c1`,`c2`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO t1 (c1, c2) VALUES
+(1999, 12),
+(2000, 11),
+(2001, 10),
+(2002, 9),
+(2003, 8),
+(2004, 7),
+(2005, 6),
+(2006, 5),
+(2007, 4),
+(2008, 3),
+(2009, 2),
+(2010, 1);
+SELECT * FROM t1 WHERE c1 > 2005;
+id c1 c2
+8 2006 5
+9 2007 4
+10 2008 3
+11 2009 2
+12 2010 1
+SELECT * FROM t1 WHERE c1 >= 2005;
+id c1 c2
+7 2005 6
+8 2006 5
+9 2007 4
+10 2008 3
+11 2009 2
+12 2010 1
+SELECT * FROM t1 WHERE c1 = 2005;
+id c1 c2
+7 2005 6
+SELECT * FROM t1 WHERE c1 <= 2005;
+id c1 c2
+1 1999 12
+2 2000 11
+3 2001 10
+4 2002 9
+5 2003 8
+6 2004 7
+7 2005 6
+SELECT * FROM t1 WHERE c1 < 2005;
+id c1 c2
+1 1999 12
+2 2000 11
+3 2001 10
+4 2002 9
+5 2003 8
+6 2004 7
+DROP TABLE t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_unsigned_smallint.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_unsigned_smallint.result
new file mode 100644
index 00000000000..b0edfb9eea1
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_unsigned_smallint.result
@@ -0,0 +1,65 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (
+id INT PRIMARY KEY AUTO_INCREMENT,
+c1 SMALLINT UNSIGNED,
+c2 SMALLINT UNSIGNED,
+KEY idx1(c1, c2)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `c1` smallint(5) unsigned DEFAULT NULL,
+ `c2` smallint(5) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `idx1` (`c1`,`c2`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO t1 (c1, c2) VALUES
+(1999, 12),
+(2000, 11),
+(2001, 10),
+(2002, 9),
+(2003, 8),
+(2004, 7),
+(2005, 6),
+(2006, 5),
+(2007, 4),
+(2008, 3),
+(2009, 2),
+(2010, 1);
+SELECT * FROM t1 WHERE c1 > 2005;
+id c1 c2
+8 2006 5
+9 2007 4
+10 2008 3
+11 2009 2
+12 2010 1
+SELECT * FROM t1 WHERE c1 >= 2005;
+id c1 c2
+7 2005 6
+8 2006 5
+9 2007 4
+10 2008 3
+11 2009 2
+12 2010 1
+SELECT * FROM t1 WHERE c1 = 2005;
+id c1 c2
+7 2005 6
+SELECT * FROM t1 WHERE c1 <= 2005;
+id c1 c2
+1 1999 12
+2 2000 11
+3 2001 10
+4 2002 9
+5 2003 8
+6 2004 7
+7 2005 6
+SELECT * FROM t1 WHERE c1 < 2005;
+id c1 c2
+1 1999 12
+2 2000 11
+3 2001 10
+4 2002 9
+5 2003 8
+6 2004 7
+DROP TABLE t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_varchar.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_varchar.result
new file mode 100644
index 00000000000..587e3c5fd4d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_nullable_varchar.result
@@ -0,0 +1,65 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (
+id INT PRIMARY KEY AUTO_INCREMENT,
+c1 VARCHAR(10),
+c2 VARCHAR(10),
+KEY idx1(c1, c2)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `c1` varchar(10) DEFAULT NULL,
+ `c2` varchar(10) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `idx1` (`c1`,`c2`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO t1 (c1, c2) VALUES
+('1999', '12'),
+('2000', '11'),
+('2001', '10'),
+('2002', '09'),
+('2003', '08'),
+('2004', '07'),
+('2005', '06'),
+('2006', '05'),
+('2007', '04'),
+('2008', '03'),
+('2009', '02'),
+('2010', '01');
+SELECT * FROM t1 WHERE c1 > '2005';
+id c1 c2
+8 2006 05
+9 2007 04
+10 2008 03
+11 2009 02
+12 2010 01
+SELECT * FROM t1 WHERE c1 >= '2005';
+id c1 c2
+7 2005 06
+8 2006 05
+9 2007 04
+10 2008 03
+11 2009 02
+12 2010 01
+SELECT * FROM t1 WHERE c1 = '2005';
+id c1 c2
+7 2005 06
+SELECT * FROM t1 WHERE c1 <= '2005';
+id c1 c2
+1 1999 12
+2 2000 11
+3 2001 10
+4 2002 09
+5 2003 08
+6 2004 07
+7 2005 06
+SELECT * FROM t1 WHERE c1 < '2005';
+id c1 c2
+1 1999 12
+2 2000 11
+3 2001 10
+4 2002 09
+5 2003 08
+6 2004 07
+DROP TABLE t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_primary_delete.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_primary_delete.result
new file mode 100644
index 00000000000..ac673bac52c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_primary_delete.result
@@ -0,0 +1,29 @@
+drop table if exists listing;
+set names utf8;
+create table scores (
+name char(30) not null,
+score int not null,
+primary key (name, score)
+) default charset utf8;
+show create table scores;
+Table Create Table
+scores CREATE TABLE `scores` (
+ `name` char(30) NOT NULL,
+ `score` int(11) NOT NULL,
+ PRIMARY KEY (`name`,`score`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into scores (name, score) values("Taro Yamada", 29);
+insert into scores (name, score) values("Taro Yamada", -12);
+insert into scores (name, score) values("Jiro Yamada", 27);
+insert into scores (name, score) values("Taro Yamada", 10);
+select * from scores;
+name score
+Jiro Yamada 27
+Taro Yamada -12
+Taro Yamada 10
+Taro Yamada 29
+delete from scores where name = "Taro Yamada" and score = 10;
+select * from scores where name = "Taro Yamada" and (score >= -12 and score < 29);
+name score
+Taro Yamada -12
+drop table scores;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_primary_select_int.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_primary_select_int.result
new file mode 100644
index 00000000000..20b45861e4e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_primary_select_int.result
@@ -0,0 +1,37 @@
+drop table if exists listing;
+set names utf8;
+create table scores (
+name char(30) not null,
+score int not null,
+primary key (name, score)
+) default charset utf8;
+show create table scores;
+Table Create Table
+scores CREATE TABLE `scores` (
+ `name` char(30) NOT NULL,
+ `score` int(11) NOT NULL,
+ PRIMARY KEY (`name`,`score`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into scores (name, score) values("Taro Yamada", 29);
+insert into scores (name, score) values("Taro Yamada", -12);
+insert into scores (name, score) values("Jiro Yamada", 27);
+insert into scores (name, score) values("Taro Yamada", 10);
+select * from scores;
+name score
+Jiro Yamada 27
+Taro Yamada -12
+Taro Yamada 10
+Taro Yamada 29
+select * from scores where name = "Taro Yamada";
+name score
+Taro Yamada -12
+Taro Yamada 10
+Taro Yamada 29
+select * from scores where name = "Taro Yamada" and score = 29;
+name score
+Taro Yamada 29
+select * from scores where name = "Taro Yamada" and (score >= -12 and score < 29);
+name score
+Taro Yamada -12
+Taro Yamada 10
+drop table scores;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_primary_update.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_primary_update.result
new file mode 100644
index 00000000000..86b06bc94dd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_primary_update.result
@@ -0,0 +1,32 @@
+drop table if exists listing;
+set names utf8;
+create table scores (
+name char(30) not null,
+score int not null,
+primary key (name, score)
+) default charset utf8;
+show create table scores;
+Table Create Table
+scores CREATE TABLE `scores` (
+ `name` char(30) NOT NULL,
+ `score` int(11) NOT NULL,
+ PRIMARY KEY (`name`,`score`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into scores (name, score) values("Taro Yamada", 29);
+insert into scores (name, score) values("Taro Yamada", -12);
+insert into scores (name, score) values("Jiro Yamada", 27);
+insert into scores (name, score) values("Taro Yamada", 10);
+select * from scores;
+name score
+Jiro Yamada 27
+Taro Yamada -12
+Taro Yamada 10
+Taro Yamada 29
+update scores set name = "Taro Yamada" where name = "Jiro Yamada" and score = 27;
+Warnings:
+Warning 1265 data truncated for primary key column: <name>
+select * from scores where name = "Taro Yamada" and (score >= -12 and score < 29);
+name score
+Taro Yamada -12
+Taro Yamada 10
+drop table scores;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_recreate.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_recreate.result
new file mode 100644
index 00000000000..02222965755
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_recreate.result
@@ -0,0 +1,39 @@
+drop table if exists listing;
+set names utf8;
+create table listing (
+id int primary key auto_increment not null,
+last_name char(30) not null,
+first_name char(30) not null,
+index name (last_name, first_name)
+) default charset utf8;
+show create table listing;
+Table Create Table
+listing CREATE TABLE `listing` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `last_name` char(30) NOT NULL,
+ `first_name` char(30) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `name` (`last_name`,`first_name`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into listing (last_name, first_name) values("Taro", "Yamada");
+insert into listing (last_name, first_name) values("Taro", "Suzuki");
+insert into listing (last_name, first_name) values("Jiro", "Yamada");
+insert into listing (last_name, first_name) values("Taro", "Tanaka");
+select * from listing
+where last_name = "Taro" and (first_name >= "S" and first_name <= "Y");
+id last_name first_name
+2 Taro Suzuki
+4 Taro Tanaka
+drop index name on listing;
+select * from listing
+where last_name = "Taro" and (first_name >= "S" and first_name <= "Y");
+id last_name first_name
+2 Taro Suzuki
+4 Taro Tanaka
+create index new_name_index on listing (last_name, first_name);
+select * from listing
+where last_name = "Taro" and (first_name >= "S" and first_name <= "Y");
+id last_name first_name
+2 Taro Suzuki
+4 Taro Tanaka
+drop table listing;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_replace.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_replace.result
new file mode 100644
index 00000000000..df67c8a397e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_replace.result
@@ -0,0 +1,39 @@
+DROP TABLE IF EXISTS listing;
+CREATE TABLE scores (
+id INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
+name CHAR(30) NOT NULL,
+score INT NOT NULL,
+INDEX property (NAME, SCORE)
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE scores;
+Table Create Table
+scores CREATE TABLE `scores` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` char(30) NOT NULL,
+ `score` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `property` (`name`,`score`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 29);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", -12);
+INSERT INTO scores (name, score) VALUES("Jiro Yamada", 27);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 10);
+SELECT * FROM scores;
+id name score
+1 Taro Yamada 29
+2 Taro Yamada -12
+3 Jiro Yamada 27
+4 Taro Yamada 10
+REPLACE scores (id, name, score) VALUES (3, "Taro Yamada", 28);
+SELECT * FROM scores;
+id name score
+1 Taro Yamada 29
+2 Taro Yamada -12
+3 Taro Yamada 28
+4 Taro Yamada 10
+SELECT * FROM scores WHERE name = "Taro Yamada" AND (score >= -12 AND score < 29);
+id name score
+2 Taro Yamada -12
+4 Taro Yamada 10
+3 Taro Yamada 28
+DROP TABLE scores;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_double.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_double.result
new file mode 100644
index 00000000000..c75733f49fe
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_double.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS temperatures;
+CREATE TABLE temperatures (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title VARCHAR(20),
+temperature DOUBLE,
+KEY temperature_index(temperature),
+KEY multi_index(temperature, title)
+);
+INSERT INTO temperatures VALUES (NULL, "Hot!", 28.2);
+INSERT INTO temperatures VALUES (NULL, "Snow!", -2.8);
+INSERT INTO temperatures VALUES (NULL, "Rainy!", 12.7);
+SELECT temperature FROM temperatures WHERE temperature BETWEEN 10 AND 30;
+temperature
+12.7
+28.2
+SELECT temperature FROM temperatures WHERE temperature BETWEEN -10 AND 20;
+temperature
+-2.8
+12.7
+SELECT title, temperature FROM temperatures WHERE temperature BETWEEN 10 AND 30;
+title temperature
+Rainy! 12.7
+Hot! 28.2
+SELECT title, temperature FROM temperatures WHERE temperature BETWEEN -10 AND 20;
+title temperature
+Snow! -2.8
+Rainy! 12.7
+DROP TABLE temperatures;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_float.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_float.result
new file mode 100644
index 00000000000..452cae2d651
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_float.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS temperatures;
+CREATE TABLE temperatures (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title VARCHAR(20),
+temperature FLOAT,
+KEY temperature_index(temperature),
+KEY multi_index(temperature, title)
+);
+INSERT INTO temperatures VALUES (NULL, "Hot!", 28.2);
+INSERT INTO temperatures VALUES (NULL, "Snow!", -2.8);
+INSERT INTO temperatures VALUES (NULL, "Rainy!", 12.7);
+SELECT temperature FROM temperatures WHERE temperature BETWEEN 10 AND 30;
+temperature
+12.7
+28.2
+SELECT temperature FROM temperatures WHERE temperature BETWEEN -10 AND 20;
+temperature
+-2.8
+12.7
+SELECT title, temperature FROM temperatures WHERE temperature BETWEEN 10 AND 30;
+title temperature
+Rainy! 12.7
+Hot! 28.2
+SELECT title, temperature FROM temperatures WHERE temperature BETWEEN -10 AND 20;
+title temperature
+Snow! -2.8
+Rainy! 12.7
+DROP TABLE temperatures;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_int.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_int.result
new file mode 100644
index 00000000000..ecf7706bd8e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_int.result
@@ -0,0 +1,37 @@
+DROP TABLE IF EXISTS listing;
+CREATE TABLE scores (
+id INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
+name CHAR(30) NOT NULL,
+score INT NOT NULL,
+INDEX property (score, name)
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE scores;
+Table Create Table
+scores CREATE TABLE `scores` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` char(30) NOT NULL,
+ `score` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `property` (`score`,`name`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 29);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", -12);
+INSERT INTO scores (name, score) VALUES("Jiro Yamada", 27);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 10);
+SELECT * FROM scores;
+id name score
+1 Taro Yamada 29
+2 Taro Yamada -12
+3 Jiro Yamada 27
+4 Taro Yamada 10
+SELECT * FROM scores WHERE score = 29;
+id name score
+1 Taro Yamada 29
+SELECT * FROM scores WHERE score = 29 AND name = "Taro Yamada";
+id name score
+1 Taro Yamada 29
+SELECT * FROM scores WHERE (score >= -12 AND score < 29) AND name = "Taro Yamada";
+id name score
+2 Taro Yamada -12
+4 Taro Yamada 10
+DROP TABLE scores;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_string.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_string.result
new file mode 100644
index 00000000000..ad73669e40b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_string.result
@@ -0,0 +1,40 @@
+drop table if exists listing;
+set names utf8;
+create table listing (
+id int primary key auto_increment not null,
+last_name char(30) not null,
+first_name char(30) not null,
+index name (last_name, first_name)
+) default charset utf8;
+show create table listing;
+Table Create Table
+listing CREATE TABLE `listing` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `last_name` char(30) NOT NULL,
+ `first_name` char(30) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `name` (`last_name`,`first_name`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into listing (last_name, first_name) values("Taro", "Yamada");
+insert into listing (last_name, first_name) values("Taro", "Suzuki");
+insert into listing (last_name, first_name) values("Jiro", "Yamada");
+insert into listing (last_name, first_name) values("Taro", "Tanaka");
+select * from listing;
+id last_name first_name
+1 Taro Yamada
+2 Taro Suzuki
+3 Jiro Yamada
+4 Taro Tanaka
+select * from listing where last_name = "Taro";
+id last_name first_name
+2 Taro Suzuki
+4 Taro Tanaka
+1 Taro Yamada
+select * from listing where last_name = "Taro" and first_name = "Suzuki";
+id last_name first_name
+2 Taro Suzuki
+select * from listing where last_name = "Taro" and (first_name >= "S" and first_name <= "Y");
+id last_name first_name
+2 Taro Suzuki
+4 Taro Tanaka
+drop table listing;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_varchar.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_varchar.result
new file mode 100644
index 00000000000..53fbccda2b4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_select_varchar.result
@@ -0,0 +1,39 @@
+drop table if exists scores;
+set names utf8;
+create table scores (
+given_name varchar(30) not null,
+family_name varchar(30) not null,
+score int not null,
+primary key property (given_name, family_name, score)
+) default charset utf8;
+show create table scores;
+Table Create Table
+scores CREATE TABLE `scores` (
+ `given_name` varchar(30) NOT NULL,
+ `family_name` varchar(30) NOT NULL,
+ `score` int(11) NOT NULL,
+ PRIMARY KEY (`given_name`,`family_name`,`score`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into scores values("Taro", "Yamada", 29);
+insert into scores values("Taro", "Yamada", -12);
+insert into scores values("Jiro", "Yamada", 27);
+insert into scores values("Taro", "Yamada", 10);
+select * from scores;
+given_name family_name score
+Jiro Yamada 27
+Taro Yamada -12
+Taro Yamada 10
+Taro Yamada 29
+select * from scores where given_name = "Taro" and family_name = "Yamada";
+given_name family_name score
+Taro Yamada -12
+Taro Yamada 10
+Taro Yamada 29
+select * from scores where given_name = "Taro" and family_name = "Yamada" and score = 29;
+given_name family_name score
+Taro Yamada 29
+select * from scores where given_name = "Taro" and family_name = "Yamada" and (score >= -12 and score < 29);
+given_name family_name score
+Taro Yamada -12
+Taro Yamada 10
+drop table scores;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_32bit_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_32bit_equal.result
new file mode 100644
index 00000000000..2174efc1b4f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_32bit_equal.result
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start DATE,
+end DATE,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "1000-01-01", "2012-10-05");
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+INSERT INTO ranges VALUES (2, "1000-01-01", "9999-12-31");
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+Warning 1265 Data truncated for column 'end' at row 1
+INSERT INTO ranges VALUES (3, "2012-10-25", "9999-12-31");
+Warnings:
+Warning 1265 Data truncated for column 'end' at row 1
+INSERT INTO ranges VALUES (4, "9999-12-31", "1000-01-01");
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+Warning 1265 Data truncated for column 'end' at row 1
+SELECT * FROM ranges FORCE INDEX(range_key)
+WHERE start = "1000-01-01" AND end = "9999-12-31";
+id start end
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_64bit_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_64bit_equal.result
new file mode 100644
index 00000000000..869ced05d25
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_64bit_equal.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start DATE,
+end DATE,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "1000-01-01", "2012-10-05");
+INSERT INTO ranges VALUES (2, "1000-01-01", "9999-12-31");
+INSERT INTO ranges VALUES (3, "2012-10-25", "9999-12-31");
+INSERT INTO ranges VALUES (4, "9999-12-31", "1000-01-01");
+SELECT * FROM ranges FORCE INDEX(range_key)
+WHERE start = "1000-01-01" AND end = "9999-12-31";
+id start end
+2 1000-01-01 9999-12-31
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_index_read.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_index_read.result
new file mode 100644
index 00000000000..8e480d4844a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_index_read.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start DATE,
+end DATE,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "1000-01-01", "2012-10-05");
+INSERT INTO ranges VALUES (2, "1000-01-01", "9999-12-31");
+INSERT INTO ranges VALUES (3, "2012-10-25", "9999-12-31");
+INSERT INTO ranges VALUES (4, "9999-12-31", "1000-01-01");
+SELECT start, end
+FROM ranges FORCE INDEX(range_key)
+ORDER BY start, end;
+start end
+1000-01-01 2012-10-05
+1000-01-01 9999-12-31
+2012-10-25 9999-12-31
+9999-12-31 1000-01-01
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_32bit_asc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_32bit_asc.result
new file mode 100644
index 00000000000..0a64a822fb5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_32bit_asc.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start DATE,
+end DATE,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "2012-10-25", "9999-12-31");
+Warnings:
+Warning 1265 Data truncated for column 'end' at row 1
+INSERT INTO ranges VALUES (2, "1000-01-01", "2012-10-05");
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+INSERT INTO ranges VALUES (3, "9999-12-31", "1000-01-01");
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+Warning 1265 Data truncated for column 'end' at row 1
+INSERT INTO ranges VALUES (4, "1000-01-01", "9999-12-31");
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+Warning 1265 Data truncated for column 'end' at row 1
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start, end;
+id start end
+2 1970-01-01 2012-10-05
+4 1970-01-01 1970-01-01
+1 2012-10-25 1970-01-01
+3 1970-01-01 1970-01-01
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_32bit_desc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_32bit_desc.result
new file mode 100644
index 00000000000..24439fdf5fa
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_32bit_desc.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start DATE,
+end DATE,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "2012-10-25", "9999-12-31");
+Warnings:
+Warning 1265 Data truncated for column 'end' at row 1
+INSERT INTO ranges VALUES (2, "1000-01-01", "2012-10-05");
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+INSERT INTO ranges VALUES (3, "9999-12-31", "1000-01-01");
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+Warning 1265 Data truncated for column 'end' at row 1
+INSERT INTO ranges VALUES (4, "1000-01-01", "9999-12-31");
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+Warning 1265 Data truncated for column 'end' at row 1
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start DESC, end DESC;
+id start end
+3 1970-01-01 1970-01-01
+1 2012-10-25 1970-01-01
+4 1970-01-01 1970-01-01
+2 1970-01-01 2012-10-05
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_64bit_asc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_64bit_asc.result
new file mode 100644
index 00000000000..92e7f51ff6e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_64bit_asc.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start DATE,
+end DATE,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "2012-10-25", "9999-12-31");
+INSERT INTO ranges VALUES (2, "1000-01-01", "2012-10-05");
+INSERT INTO ranges VALUES (3, "9999-12-31", "1000-01-01");
+INSERT INTO ranges VALUES (4, "1000-01-01", "9999-12-31");
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start, end;
+id start end
+2 1000-01-01 2012-10-05
+4 1000-01-01 9999-12-31
+1 2012-10-25 9999-12-31
+3 9999-12-31 1000-01-01
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_64bit_desc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_64bit_desc.result
new file mode 100644
index 00000000000..ddd694c3863
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_order_64bit_desc.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start DATE,
+end DATE,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "2012-10-25", "9999-12-31");
+INSERT INTO ranges VALUES (2, "1000-01-01", "2012-10-05");
+INSERT INTO ranges VALUES (3, "9999-12-31", "1000-01-01");
+INSERT INTO ranges VALUES (4, "1000-01-01", "9999-12-31");
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start DESC, end DESC;
+id start end
+3 9999-12-31 1000-01-01
+1 2012-10-25 9999-12-31
+4 1000-01-01 9999-12-31
+2 1000-01-01 2012-10-05
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_reinsert.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_reinsert.result
new file mode 100644
index 00000000000..bd1bdc33325
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_date_reinsert.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start DATE,
+end DATE,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "2010-01-01", "2012-10-05");
+SELECT * FROM ranges;
+id start end
+1 2010-01-01 2012-10-05
+DELETE FROM ranges WHERE id = 1;
+INSERT INTO ranges VALUES (1, "2010-01-01", "2012-10-05");
+SELECT * FROM ranges;
+id start end
+1 2010-01-01 2012-10-05
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_index_read.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_index_read.result
new file mode 100644
index 00000000000..1aa710882cb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_index_read.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id int PRIMARY KEY,
+start datetime,
+end datetime,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "1000-01-01 00:00:00", "2012-10-05 16:18:29");
+INSERT INTO ranges VALUES (2, "1000-01-01 00:00:00", "9999-12-31 23:59:59");
+INSERT INTO ranges VALUES (3, "2012-10-25 16:18:29", "9999-12-31 23:59:59");
+INSERT INTO ranges VALUES (4, "9999-12-31 23:59:59", "1000-01-01 00:00:00");
+SELECT start, end
+FROM ranges FORCE INDEX(range_key)
+ORDER BY start, end;
+start end
+1000-01-01 00:00:00 2012-10-05 16:18:29
+1000-01-01 00:00:00 9999-12-31 23:59:59
+2012-10-25 16:18:29 9999-12-31 23:59:59
+9999-12-31 23:59:59 1000-01-01 00:00:00
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_order_asc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_order_asc.result
new file mode 100644
index 00000000000..d18f1858932
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_order_asc.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id int PRIMARY KEY,
+start datetime,
+end datetime,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "2012-10-25 16:18:29", "9999-12-31 23:59:59");
+INSERT INTO ranges VALUES (2, "1000-01-01 00:00:00", "2012-10-05 16:18:29");
+INSERT INTO ranges VALUES (3, "9999-12-31 23:59:59", "1000-01-01 00:00:00");
+INSERT INTO ranges VALUES (4, "1000-01-01 00:00:00", "9999-12-31 23:59:59");
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start, end;
+id start end
+2 1000-01-01 00:00:00 2012-10-05 16:18:29
+4 1000-01-01 00:00:00 9999-12-31 23:59:59
+1 2012-10-25 16:18:29 9999-12-31 23:59:59
+3 9999-12-31 23:59:59 1000-01-01 00:00:00
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_order_desc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_order_desc.result
new file mode 100644
index 00000000000..c159aeab4ce
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_order_desc.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id int PRIMARY KEY,
+start datetime,
+end datetime,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "2012-10-25 16:18:29", "9999-12-31 23:59:59");
+INSERT INTO ranges VALUES (2, "1000-01-01 00:00:00", "2012-10-05 16:18:29");
+INSERT INTO ranges VALUES (3, "9999-12-31 23:59:59", "1000-01-01 00:00:00");
+INSERT INTO ranges VALUES (4, "1000-01-01 00:00:00", "9999-12-31 23:59:59");
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start DESC, end DESC;
+id start end
+3 9999-12-31 23:59:59 1000-01-01 00:00:00
+1 2012-10-25 16:18:29 9999-12-31 23:59:59
+4 1000-01-01 00:00:00 9999-12-31 23:59:59
+2 1000-01-01 00:00:00 2012-10-05 16:18:29
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_reinsert.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_reinsert.result
new file mode 100644
index 00000000000..50d5da49955
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_datetime_reinsert.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id int PRIMARY KEY,
+start datetime,
+end datetime,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "2010-01-01 00:00:00", "2012-10-05 23:59:59");
+SELECT * FROM ranges;
+id start end
+1 2010-01-01 00:00:00 2012-10-05 23:59:59
+DELETE FROM ranges WHERE id = 1;
+INSERT INTO ranges VALUES (1, "2010-01-01 00:00:00", "2012-10-05 23:59:59");
+SELECT * FROM ranges;
+id start end
+1 2010-01-01 00:00:00 2012-10-05 23:59:59
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_decimal.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_decimal.result
new file mode 100644
index 00000000000..015afdb5cf6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_decimal.result
@@ -0,0 +1,36 @@
+drop table if exists t1;
+create table t1 (c1 int primary key, c2 decimal(65,30), c3 decimal(65,30), unique key uk1(c2,c3));
+insert into t1 values(1,123.456,0.000000000000000000000000000001);
+insert into t1 values(2,-123.456,123.456);
+insert into t1 values(3,98765432109876543210987654321098765.432109876543210987654321098765,-123.456);
+insert into t1 values(4,-98765432109876543210987654321098765.432109876543210987654321098765,98765432109876543210987654321098765.432109876543210987654321098765);
+insert into t1 values(5,0.000000000000000000000000000001,-98765432109876543210987654321098765.432109876543210987654321098765);
+select c1, c2, c3 from t1 force index(uk1) where c2 = -98765432109876543210987654321098765.432109876543210987654321098765 and c3 = 98765432109876543210987654321098765.432109876543210987654321098765;
+c1 c2 c3
+4 -98765432109876543210987654321098765.432109876543210987654321098765 98765432109876543210987654321098765.432109876543210987654321098765
+select c1, c2, c3 from t1 force index(uk1) order by c2, c3;
+c1 c2 c3
+4 -98765432109876543210987654321098765.432109876543210987654321098765 98765432109876543210987654321098765.432109876543210987654321098765
+2 -123.456000000000000000000000000000 123.456000000000000000000000000000
+5 0.000000000000000000000000000001 -98765432109876543210987654321098765.432109876543210987654321098765
+1 123.456000000000000000000000000000 0.000000000000000000000000000001
+3 98765432109876543210987654321098765.432109876543210987654321098765 -123.456000000000000000000000000000
+select c1, c2, c3 from t1 force index(uk1) order by c2 desc, c3 desc;
+c1 c2 c3
+3 98765432109876543210987654321098765.432109876543210987654321098765 -123.456000000000000000000000000000
+1 123.456000000000000000000000000000 0.000000000000000000000000000001
+5 0.000000000000000000000000000001 -98765432109876543210987654321098765.432109876543210987654321098765
+2 -123.456000000000000000000000000000 123.456000000000000000000000000000
+4 -98765432109876543210987654321098765.432109876543210987654321098765 98765432109876543210987654321098765.432109876543210987654321098765
+select c2, c3 from t1 force index(uk1) order by c2, c3;
+c2 c3
+-98765432109876543210987654321098765.432109876543210987654321098765 98765432109876543210987654321098765.432109876543210987654321098765
+-123.456000000000000000000000000000 123.456000000000000000000000000000
+0.000000000000000000000000000001 -98765432109876543210987654321098765.432109876543210987654321098765
+123.456000000000000000000000000000 0.000000000000000000000000000001
+98765432109876543210987654321098765.432109876543210987654321098765 -123.456000000000000000000000000000
+insert into t1 values(6,123.456,0.000000000000000000000000000001);
+ERROR 23000: Duplicate entry '123.456000000000000000000000000000-0.000000000000000000000000000' for key 'uk1'
+delete from t1 where c1 = 1;
+insert into t1 values(1,123.456,0.000000000000000000000000000001);
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_index_read.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_index_read.result
new file mode 100644
index 00000000000..4e0dec32a28
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_index_read.result
@@ -0,0 +1,22 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id int PRIMARY KEY,
+start time,
+end time,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "00:00:00", "15:11:11");
+INSERT INTO ranges VALUES (2, "00:00:00", "838:59:59");
+INSERT INTO ranges VALUES (3, "15:11:12", "838:59:59");
+INSERT INTO ranges VALUES (4, "838:59:59", "00:00:00");
+INSERT INTO ranges VALUES (5, "-838:59:59", "838:59:59");
+SELECT start, end
+FROM ranges FORCE INDEX(range_key)
+ORDER BY start, end;
+start end
+-838:59:59 838:59:59
+00:00:00 15:11:11
+00:00:00 838:59:59
+15:11:12 838:59:59
+838:59:59 00:00:00
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_order_asc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_order_asc.result
new file mode 100644
index 00000000000..3c6520a69c9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_order_asc.result
@@ -0,0 +1,21 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id int PRIMARY KEY,
+start time,
+end time,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "15:11:12", "838:59:59");
+INSERT INTO ranges VALUES (2, "00:00:00", "15:11:11");
+INSERT INTO ranges VALUES (3, "838:59:59", "00:00:00");
+INSERT INTO ranges VALUES (4, "00:00:00", "838:59:59");
+INSERT INTO ranges VALUES (5, "-838:59:59", "838:59:59");
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start, end;
+id start end
+5 -838:59:59 838:59:59
+2 00:00:00 15:11:11
+4 00:00:00 838:59:59
+1 15:11:12 838:59:59
+3 838:59:59 00:00:00
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_order_desc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_order_desc.result
new file mode 100644
index 00000000000..c1be80309b8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_order_desc.result
@@ -0,0 +1,21 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id int PRIMARY KEY,
+start time,
+end time,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "15:11:12", "838:59:59");
+INSERT INTO ranges VALUES (2, "00:00:00", "15:11:11");
+INSERT INTO ranges VALUES (3, "838:59:59", "00:00:00");
+INSERT INTO ranges VALUES (4, "00:00:00", "838:59:59");
+INSERT INTO ranges VALUES (5, "-838:59:59", "838:59:59");
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start DESC, end DESC;
+id start end
+3 838:59:59 00:00:00
+1 15:11:12 838:59:59
+4 00:00:00 838:59:59
+2 00:00:00 15:11:11
+5 -838:59:59 838:59:59
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_reinsert.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_reinsert.result
new file mode 100644
index 00000000000..0613de3e2f3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_time_reinsert.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id int PRIMARY KEY,
+start time,
+end time,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "13:21:48", "15:11:12");
+SELECT * FROM ranges;
+id start end
+1 13:21:48 15:11:12
+DELETE FROM ranges WHERE id = 1;
+INSERT INTO ranges VALUES (1, "13:21:48", "15:11:12");
+SELECT * FROM ranges;
+id start end
+1 13:21:48 15:11:12
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_index_read.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_index_read.result
new file mode 100644
index 00000000000..d833fb44024
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_index_read.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id int PRIMARY KEY,
+start timestamp,
+end timestamp,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "1970-01-01 12:00:00", "2012-10-05 16:18:29");
+INSERT INTO ranges VALUES (2, "1970-01-01 12:00:00", "2038-01-18 15:14:07");
+INSERT INTO ranges VALUES (3, "2012-10-25 16:18:29", "2038-01-18 15:14:07");
+INSERT INTO ranges VALUES (4, "2038-01-18 15:14:07", "1970-01-01 12:00:00");
+SELECT start, end
+FROM ranges FORCE INDEX(range_key)
+ORDER BY start, end;
+start end
+1970-01-01 12:00:00 2012-10-05 16:18:29
+1970-01-01 12:00:00 2038-01-18 15:14:07
+2012-10-25 16:18:29 2038-01-18 15:14:07
+2038-01-18 15:14:07 1970-01-01 12:00:00
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_order_asc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_order_asc.result
new file mode 100644
index 00000000000..1e4ee102c9e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_order_asc.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id int PRIMARY KEY,
+start timestamp,
+end timestamp,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "2012-10-25 16:18:29", "2038-01-18 15:14:07");
+INSERT INTO ranges VALUES (2, "1970-01-01 12:00:00", "2012-10-05 16:18:29");
+INSERT INTO ranges VALUES (3, "2038-01-18 15:14:07", "1970-01-01 12:00:00");
+INSERT INTO ranges VALUES (4, "1970-01-01 12:00:00", "2038-01-18 15:14:07");
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start, end;
+id start end
+2 1970-01-01 12:00:00 2012-10-05 16:18:29
+4 1970-01-01 12:00:00 2038-01-18 15:14:07
+1 2012-10-25 16:18:29 2038-01-18 15:14:07
+3 2038-01-18 15:14:07 1970-01-01 12:00:00
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_order_desc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_order_desc.result
new file mode 100644
index 00000000000..23a5522320a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_order_desc.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id int PRIMARY KEY,
+start timestamp,
+end timestamp,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "2012-10-25 16:18:29", "2038-01-18 15:14:07");
+INSERT INTO ranges VALUES (2, "1970-01-01 12:00:00", "2012-10-05 16:18:29");
+INSERT INTO ranges VALUES (3, "2038-01-18 15:14:07", "1970-01-01 12:00:00");
+INSERT INTO ranges VALUES (4, "1970-01-01 12:00:00", "2038-01-18 15:14:07");
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start DESC, end DESC;
+id start end
+3 2038-01-18 15:14:07 1970-01-01 12:00:00
+1 2012-10-25 16:18:29 2038-01-18 15:14:07
+4 1970-01-01 12:00:00 2038-01-18 15:14:07
+2 1970-01-01 12:00:00 2012-10-05 16:18:29
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_reinsert.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_reinsert.result
new file mode 100644
index 00000000000..3f3277f5e64
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_timestamp_reinsert.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id int PRIMARY KEY,
+start timestamp,
+end timestamp,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, "2010-01-01 00:00:00", "2012-10-05 23:59:59");
+SELECT * FROM ranges;
+id start end
+1 2010-01-01 00:00:00 2012-10-05 23:59:59
+DELETE FROM ranges WHERE id = 1;
+INSERT INTO ranges VALUES (1, "2010-01-01 00:00:00", "2012-10-05 23:59:59");
+SELECT * FROM ranges;
+id start end
+1 2010-01-01 00:00:00 2012-10-05 23:59:59
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_varchar.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_varchar.result
new file mode 100644
index 00000000000..5c9cd959472
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_varchar.result
Binary files differ
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_32bit_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_32bit_equal.result
new file mode 100644
index 00000000000..04b3ba38395
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_32bit_equal.result
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start YEAR,
+end YEAR,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, 1901, 2012);
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+INSERT INTO ranges VALUES (2, 1901, 2155);
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+Warning 1265 Data truncated for column 'end' at row 1
+INSERT INTO ranges VALUES (3, 2012, 2155);
+Warnings:
+Warning 1265 Data truncated for column 'end' at row 1
+INSERT INTO ranges VALUES (4, 2155, 1901);
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+Warning 1265 Data truncated for column 'end' at row 1
+SELECT * FROM ranges FORCE INDEX(range_key)
+WHERE start = 1901 AND end = 2155;
+id start end
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_64bit_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_64bit_equal.result
new file mode 100644
index 00000000000..e96754e5e21
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_64bit_equal.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start YEAR,
+end YEAR,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, 1901, 2012);
+INSERT INTO ranges VALUES (2, 1901, 2155);
+INSERT INTO ranges VALUES (3, 2012, 2155);
+INSERT INTO ranges VALUES (4, 2155, 1901);
+SELECT * FROM ranges FORCE INDEX(range_key)
+WHERE start = 1901 AND end = 2155;
+id start end
+2 1901 2155
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_index_read.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_index_read.result
new file mode 100644
index 00000000000..997c5e7db8f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_index_read.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start YEAR,
+end YEAR,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, 1901, 2012);
+INSERT INTO ranges VALUES (2, 1901, 2155);
+INSERT INTO ranges VALUES (3, 2012, 2155);
+INSERT INTO ranges VALUES (4, 2155, 1901);
+SELECT start, end
+FROM ranges FORCE INDEX(range_key)
+ORDER BY start, end;
+start end
+1901 2012
+1901 2155
+2012 2155
+2155 1901
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_32bit_asc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_32bit_asc.result
new file mode 100644
index 00000000000..9a84d115a87
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_32bit_asc.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start YEAR,
+end YEAR,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, 2012, 2155);
+Warnings:
+Warning 1265 Data truncated for column 'end' at row 1
+INSERT INTO ranges VALUES (2, 1901, 2012);
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+INSERT INTO ranges VALUES (3, 2155, 1901);
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+Warning 1265 Data truncated for column 'end' at row 1
+INSERT INTO ranges VALUES (4, 1901, 2155);
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+Warning 1265 Data truncated for column 'end' at row 1
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start, end;
+id start end
+2 1970 2012
+4 1970 1970
+1 2012 1970
+3 1970 1970
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_32bit_desc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_32bit_desc.result
new file mode 100644
index 00000000000..3deb7435030
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_32bit_desc.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start YEAR,
+end YEAR,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, 2012, 2155);
+Warnings:
+Warning 1265 Data truncated for column 'end' at row 1
+INSERT INTO ranges VALUES (2, 1901, 2012);
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+INSERT INTO ranges VALUES (3, 2155, 1901);
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+Warning 1265 Data truncated for column 'end' at row 1
+INSERT INTO ranges VALUES (4, 1901, 2155);
+Warnings:
+Warning 1265 Data truncated for column 'start' at row 1
+Warning 1265 Data truncated for column 'end' at row 1
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start DESC, end DESC;
+id start end
+3 1970 1970
+1 2012 1970
+4 1970 1970
+2 1970 2012
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_64bit_asc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_64bit_asc.result
new file mode 100644
index 00000000000..335b9eb9486
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_64bit_asc.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start YEAR,
+end YEAR,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, 2012, 2155);
+INSERT INTO ranges VALUES (2, 1901, 2012);
+INSERT INTO ranges VALUES (3, 2155, 1901);
+INSERT INTO ranges VALUES (4, 1901, 2155);
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start, end;
+id start end
+2 1901 2012
+4 1901 2155
+1 2012 2155
+3 2155 1901
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_64bit_desc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_64bit_desc.result
new file mode 100644
index 00000000000..524456b74bb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_order_64bit_desc.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start YEAR,
+end YEAR,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, 2012, 2155);
+INSERT INTO ranges VALUES (2, 1901, 2012);
+INSERT INTO ranges VALUES (3, 2155, 1901);
+INSERT INTO ranges VALUES (4, 1901, 2155);
+SELECT * FROM ranges FORCE INDEX(range_key)
+ORDER BY start DESC, end DESC;
+id start end
+3 2155 1901
+1 2012 2155
+4 1901 2155
+2 1901 2012
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_reinsert.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_reinsert.result
new file mode 100644
index 00000000000..7362411d7cf
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_unique_year_reinsert.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS ranges;
+CREATE TABLE ranges (
+id INT PRIMARY KEY,
+start YEAR,
+end YEAR,
+UNIQUE KEY range_key(start, end)
+);
+INSERT INTO ranges VALUES (1, 2010, 2012);
+SELECT * FROM ranges;
+id start end
+1 2010 2012
+DELETE FROM ranges WHERE id = 1;
+INSERT INTO ranges VALUES (1, 2010, 2012);
+SELECT * FROM ranges;
+id start end
+1 2010 2012
+DROP TABLE ranges;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_update_int.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_update_int.result
new file mode 100644
index 00000000000..bfc30a1f871
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_update_int.result
@@ -0,0 +1,30 @@
+DROP TABLE IF EXISTS scores;
+CREATE TABLE scores (
+id INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
+name CHAR(30) NOT NULL,
+score INT NOT NULL,
+KEY property (score, name)
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE scores;
+Table Create Table
+scores CREATE TABLE `scores` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` char(30) NOT NULL,
+ `score` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `property` (`score`,`name`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 29);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", -12);
+INSERT INTO scores (name, score) VALUES("Jiro Yamada", 29);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 10);
+SELECT * FROM scores WHERE score = 29;
+id name score
+3 Jiro Yamada 29
+1 Taro Yamada 29
+UPDATE scores SET name = "Saburo YAMADA" WHERE id = 3;
+SELECT * FROM scores WHERE score = 29;
+id name score
+3 Saburo YAMADA 29
+1 Taro Yamada 29
+DROP TABLE scores;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_update_string.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_update_string.result
new file mode 100644
index 00000000000..550551d0052
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_multiple_column_update_string.result
@@ -0,0 +1,34 @@
+drop table if exists listing;
+set names utf8;
+create table scores (
+id int primary key auto_increment not null,
+name char(30) not null,
+score int not null,
+index property (name, score)
+) default charset utf8;
+show create table scores;
+Table Create Table
+scores CREATE TABLE `scores` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` char(30) NOT NULL,
+ `score` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `property` (`name`,`score`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into scores (name, score) values("Taro Yamada", 29);
+insert into scores (name, score) values("Taro Yamada", -12);
+insert into scores (name, score) values("Jiro Yamada", 27);
+insert into scores (name, score) values("Taro Yamada", 10);
+select * from scores;
+id name score
+1 Taro Yamada 29
+2 Taro Yamada -12
+3 Jiro Yamada 27
+4 Taro Yamada 10
+update scores set name = "Taro Yamada" where name = "Jiro Yamada" and score = 27;
+select * from scores where name = "Taro Yamada" and (score >= -12 and score < 29);
+id name score
+2 Taro Yamada -12
+4 Taro Yamada 10
+3 Taro Yamada 27
+drop table scores;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_char_exact_length.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_char_exact_length.result
new file mode 100644
index 00000000000..95d7c78bbb6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_char_exact_length.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id char(10) CHARACTER SET latin1 PRIMARY KEY
+);
+INSERT INTO ids VALUES('abcdefghij');
+INSERT INTO ids VALUES('klmnopqrst');
+INSERT INTO ids VALUES('uvwxyz0123');
+SELECT * FROM ids FORCE INDEX(PRIMARY) ORDER BY id;
+id
+abcdefghij
+klmnopqrst
+uvwxyz0123
+SELECT * FROM ids FORCE INDEX(PRIMARY) WHERE id = 'abcdefghij';
+id
+abcdefghij
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_char_null_character.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_char_null_character.result
new file mode 100644
index 00000000000..f7a802060d5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_char_null_character.result
Binary files differ
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_char_short.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_char_short.result
new file mode 100644
index 00000000000..fb08c6601bb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_char_short.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id char(6) CHARACTER SET latin1 PRIMARY KEY
+);
+INSERT INTO ids VALUES("abcdef");
+INSERT INTO ids VALUES( "cdef");
+INSERT INTO ids VALUES( "ef");
+SELECT * FROM ids FORCE INDEX(PRIMARY) ORDER BY id;
+id
+abcdef
+cdef
+ef
+SELECT * FROM ids FORCE INDEX(PRIMARY) WHERE id = "cdef";
+id
+cdef
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_date.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_date.result
new file mode 100644
index 00000000000..a460c7d99d6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_date.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+day DATE PRIMARY KEY,
+title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `day` date NOT NULL,
+ `title` text,
+ PRIMARY KEY (`day`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (day, title) VALUES ("2012-01-29", "clear day");
+INSERT INTO diaries (day, title) VALUES ("2012-01-30", "rainy day");
+INSERT INTO diaries (day, title) VALUES ("2012-01-31", "cloudy day");
+INSERT INTO diaries (day, title) VALUES ("2012-01-31", "duplicated day");
+ERROR 23000: Duplicate entry '2012-01-31' for key 'PRIMARY'
+SELECT * FROM diaries;
+day title
+2012-01-29 clear day
+2012-01-30 rainy day
+2012-01-31 cloudy day
+SELECT * FROM diaries
+WHERE day BETWEEN "2012-01-29" AND "2012-01-30";
+day title
+2012-01-29 clear day
+2012-01-30 rainy day
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_datetime_with_fractional_seconds.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_datetime_with_fractional_seconds.result
new file mode 100644
index 00000000000..ffbbfe3f460
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_datetime_with_fractional_seconds.result
@@ -0,0 +1,30 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+day DATETIME(6) PRIMARY KEY,
+title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `day` datetime(6) NOT NULL,
+ `title` text,
+ PRIMARY KEY (`day`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (day, title)
+VALUES ("2012-01-29 21:51:01.111111", "clear day");
+INSERT INTO diaries (day, title)
+VALUES ("2012-01-30 01:23:45.333", "rainy day");
+INSERT INTO diaries (day, title)
+VALUES ("2012-01-31 08:32:10.5555", "cloudy day");
+SELECT * FROM diaries;
+day title
+2012-01-29 21:51:01.111111 clear day
+2012-01-30 01:23:45.333000 rainy day
+2012-01-31 08:32:10.555500 cloudy day
+SELECT * FROM diaries
+WHERE day BETWEEN "2012-01-29 00:00:00.123456" AND
+"2012-01-31 00:00:00.999999";
+day title
+2012-01-29 21:51:01.111111 clear day
+2012-01-30 01:23:45.333000 rainy day
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_datetime_without_fractional_seconds.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_datetime_without_fractional_seconds.result
new file mode 100644
index 00000000000..76c529f7fbb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_datetime_without_fractional_seconds.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+day DATETIME PRIMARY KEY,
+title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `day` datetime NOT NULL,
+ `title` text,
+ PRIMARY KEY (`day`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (day, title)
+VALUES ("2012-01-29 21:51:01", "clear day");
+INSERT INTO diaries (day, title)
+VALUES ("2012-01-30 01:23:45", "rainy day");
+INSERT INTO diaries (day, title)
+VALUES ("2012-01-31 08:32:10", "cloudy day");
+SELECT * FROM diaries;
+day title
+2012-01-29 21:51:01 clear day
+2012-01-30 01:23:45 rainy day
+2012-01-31 08:32:10 cloudy day
+SELECT * FROM diaries
+WHERE day BETWEEN "2012-01-29 00:00:00" AND "2012-01-31 00:00:00";
+day title
+2012-01-29 21:51:01 clear day
+2012-01-30 01:23:45 rainy day
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_decimal_with_fractional_seconds.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_decimal_with_fractional_seconds.result
new file mode 100644
index 00000000000..9b1e8e7d7e8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_decimal_with_fractional_seconds.result
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS releases;
+CREATE TABLE releases (
+version DECIMAL(6, 3) PRIMARY KEY,
+message TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE releases;
+Table Create Table
+releases CREATE TABLE `releases` (
+ `version` decimal(6,3) NOT NULL,
+ `message` text,
+ PRIMARY KEY (`version`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO releases (version, message) VALUES (10.000, "10th release!");
+INSERT INTO releases (version, message) VALUES (10.001, "minor fix.");
+INSERT INTO releases (version, message) VALUES (999.999, "the last release!");
+SELECT * FROM releases;
+version message
+10.000 10th release!
+10.001 minor fix.
+999.999 the last release!
+SELECT * FROM releases WHERE version BETWEEN "9.000" AND "10.001";
+version message
+10.000 10th release!
+10.001 minor fix.
+DROP TABLE releases;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_decimal_without_fractional_seconds.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_decimal_without_fractional_seconds.result
new file mode 100644
index 00000000000..0624ab2f2b2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_decimal_without_fractional_seconds.result
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS releases;
+CREATE TABLE releases (
+version DECIMAL PRIMARY KEY,
+message TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE releases;
+Table Create Table
+releases CREATE TABLE `releases` (
+ `version` decimal(10,0) NOT NULL,
+ `message` text,
+ PRIMARY KEY (`version`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO releases (version, message) VALUES (1, "the first release!!!");
+INSERT INTO releases (version, message) VALUES (10, "10th release!");
+INSERT INTO releases (version, message) VALUES (999, "the last release!");
+SELECT * FROM releases;
+version message
+1 the first release!!!
+10 10th release!
+999 the last release!
+SELECT * FROM releases WHERE version BETWEEN "1" AND "10";
+version message
+1 the first release!!!
+10 10th release!
+DROP TABLE releases;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_time_with_fractional_seconds.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_time_with_fractional_seconds.result
new file mode 100644
index 00000000000..639114875e9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_time_with_fractional_seconds.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS running_records;
+CREATE TABLE running_records (
+time TIME(6) PRIMARY KEY,
+title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE running_records;
+Table Create Table
+running_records CREATE TABLE `running_records` (
+ `time` time(6) NOT NULL,
+ `title` text,
+ PRIMARY KEY (`time`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO running_records (time, title)
+VALUES ("01:00:00.000001", "normal condition");
+INSERT INTO running_records (time, title)
+VALUES ("12:23:34.123456", "bad condition");
+INSERT INTO running_records (time, title)
+VALUES ("-838:59:59.000000", "record failure");
+SELECT * FROM running_records;
+time title
+-838:59:59.000000 record failure
+01:00:00.000001 normal condition
+12:23:34.123456 bad condition
+SELECT * FROM running_records
+WHERE time BETWEEN "00:59:59.999999" AND "12:23:34.123456";
+time title
+01:00:00.000001 normal condition
+12:23:34.123456 bad condition
+DROP TABLE running_records;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_time_without_fractional_seconds.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_time_without_fractional_seconds.result
new file mode 100644
index 00000000000..6a89cdc33dd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_time_without_fractional_seconds.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS running_records;
+CREATE TABLE running_records (
+time TIME PRIMARY KEY,
+title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE running_records;
+Table Create Table
+running_records CREATE TABLE `running_records` (
+ `time` time NOT NULL,
+ `title` text,
+ PRIMARY KEY (`time`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO running_records (time, title)
+VALUES ("01:00:00", "normal condition");
+INSERT INTO running_records (time, title)
+VALUES ("12:23:34", "bad condition");
+INSERT INTO running_records (time, title)
+VALUES ("-838:59:59", "record failure");
+SELECT * FROM running_records;
+time title
+-838:59:59 record failure
+01:00:00 normal condition
+12:23:34 bad condition
+SELECT * FROM running_records
+WHERE time BETWEEN "00:59:59" AND "12:23:34";
+time title
+01:00:00 normal condition
+12:23:34 bad condition
+DROP TABLE running_records;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_timestamp_with_fractional_seconds.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_timestamp_with_fractional_seconds.result
new file mode 100644
index 00000000000..7040cc22b2e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_timestamp_with_fractional_seconds.result
@@ -0,0 +1,30 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+time TIMESTAMP(6) PRIMARY KEY,
+title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
+ `title` text,
+ PRIMARY KEY (`time`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (time, title)
+VALUES ("2012-01-29 21:51:01.111111", "clear day");
+INSERT INTO diaries (time, title)
+VALUES ("2012-01-30 01:23:45.333", "rainy day");
+INSERT INTO diaries (time, title)
+VALUES ("2012-01-31 08:32:10.5555", "cloudy day");
+SELECT * FROM diaries;
+time title
+2012-01-29 21:51:01.111111 clear day
+2012-01-30 01:23:45.333000 rainy day
+2012-01-31 08:32:10.555500 cloudy day
+SELECT * FROM diaries
+WHERE time BETWEEN "2012-01-29 00:00:00.123456" AND
+"2012-01-31 00:00:00.999999";
+time title
+2012-01-29 21:51:01.111111 clear day
+2012-01-30 01:23:45.333000 rainy day
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_timestamp_without_fractional_seconds.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_timestamp_without_fractional_seconds.result
new file mode 100644
index 00000000000..38017f6f8cc
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_timestamp_without_fractional_seconds.result
@@ -0,0 +1,26 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+time TIMESTAMP PRIMARY KEY,
+title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `title` text,
+ PRIMARY KEY (`time`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (time, title) VALUES ("2012-01-29 21:51:01", "clear day");
+INSERT INTO diaries (time, title) VALUES ("2012-01-30 01:23:45", "rainy day");
+INSERT INTO diaries (time, title) VALUES ("2012-01-31 08:32:10", "cloudy day");
+SELECT * FROM diaries;
+time title
+2012-01-29 21:51:01 clear day
+2012-01-30 01:23:45 rainy day
+2012-01-31 08:32:10 cloudy day
+SELECT * FROM diaries
+WHERE time BETWEEN "2012-01-29 00:00:00" AND "2012-01-31 00:00:00";
+time title
+2012-01-29 21:51:01 clear day
+2012-01-30 01:23:45 rainy day
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_varchar_null_character.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_varchar_null_character.result
new file mode 100644
index 00000000000..9079d9c4064
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_varchar_null_character.result
Binary files differ
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_year.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_year.result
new file mode 100644
index 00000000000..af9b3a2dbfb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_primary_year.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS aniversary_memos;
+CREATE TABLE aniversary_memos (
+party_year YEAR PRIMARY KEY,
+title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE aniversary_memos;
+Table Create Table
+aniversary_memos CREATE TABLE `aniversary_memos` (
+ `party_year` year(4) NOT NULL,
+ `title` text,
+ PRIMARY KEY (`party_year`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO aniversary_memos (party_year, title)
+VALUES ("11", "We need a big cake!");
+INSERT INTO aniversary_memos (party_year, title)
+VALUES ("2012", "Invitations are sent.");
+INSERT INTO aniversary_memos (party_year, title)
+VALUES ("13", "Wow! Today is the anniversary party day!");
+SELECT * FROM aniversary_memos;
+party_year title
+2011 We need a big cake!
+2012 Invitations are sent.
+2013 Wow! Today is the anniversary party day!
+SELECT * FROM aniversary_memos
+WHERE party_year BETWEEN "12" AND "2013";
+party_year title
+2012 Invitations are sent.
+2013 Wow! Today is the anniversary party day!
+DROP TABLE aniversary_memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_asc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_asc.result
new file mode 100644
index 00000000000..1ca4b145f54
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_asc.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS ids;
+SET NAMES UTF8;
+CREATE TABLE ids (
+id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+SELECT * FROM ids WHERE ids.id > 1 ORDER BY ids.id ASC LIMIT 3;
+id
+2
+3
+4
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_desc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_desc.result
new file mode 100644
index 00000000000..80dcb25ffe6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_desc.result
@@ -0,0 +1,15 @@
+DROP TABLE IF EXISTS ids;
+SET NAMES UTF8;
+CREATE TABLE ids (
+id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+SELECT * FROM ids WHERE ids.id > 3 ORDER BY ids.id DESC LIMIT 3;
+id
+5
+4
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_or_equal_asc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_or_equal_asc.result
new file mode 100644
index 00000000000..4c1ff997d79
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_or_equal_asc.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS ids;
+SET NAMES UTF8;
+CREATE TABLE ids (
+id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+SELECT * FROM ids WHERE ids.id >= 2 ORDER BY ids.id ASC LIMIT 3;
+id
+2
+3
+4
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_or_equal_desc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_or_equal_desc.result
new file mode 100644
index 00000000000..4998725aef5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_greater_than_or_equal_desc.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS ids;
+SET NAMES UTF8;
+CREATE TABLE ids (
+id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+SELECT * FROM ids WHERE ids.id >= 3 ORDER BY ids.id DESC LIMIT 3;
+id
+5
+4
+3
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_asc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_asc.result
new file mode 100644
index 00000000000..8b142f30610
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_asc.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS ids;
+SET NAMES UTF8;
+CREATE TABLE ids (
+id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+SELECT * FROM ids WHERE ids.id < 4 ORDER BY ids.id ASC LIMIT 3;
+id
+1
+2
+3
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_desc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_desc.result
new file mode 100644
index 00000000000..eaf5b87e62c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_desc.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS ids;
+SET NAMES UTF8;
+CREATE TABLE ids (
+id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+SELECT * FROM ids WHERE ids.id < 4 ORDER BY ids.id DESC LIMIT 3;
+id
+3
+2
+1
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_or_equal_asc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_or_equal_asc.result
new file mode 100644
index 00000000000..e1e96c9ca8b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_or_equal_asc.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS ids;
+SET NAMES UTF8;
+CREATE TABLE ids (
+id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+SELECT * FROM ids WHERE ids.id <= 4 ORDER BY ids.id ASC LIMIT 3;
+id
+1
+2
+3
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_or_equal_desc.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_or_equal_desc.result
new file mode 100644
index 00000000000..e8124ca14c0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_range_less_than_or_equal_desc.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS ids;
+SET NAMES UTF8;
+CREATE TABLE ids (
+id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+SELECT * FROM ids WHERE ids.id <= 4 ORDER BY ids.id DESC LIMIT 3;
+id
+4
+3
+2
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_bigint.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_bigint.result
new file mode 100644
index 00000000000..77b321015c9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_bigint.result
@@ -0,0 +1,38 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id BIGINT,
+value BIGINT,
+KEY (id, value)
+);
+INSERT INTO ids VALUES ( -1, 16);
+INSERT INTO ids VALUES ( -2, 8);
+INSERT INTO ids VALUES ( -4, 4);
+INSERT INTO ids VALUES ( -8, 2);
+INSERT INTO ids VALUES (-16, 1);
+INSERT INTO ids VALUES ( 16, -1);
+INSERT INTO ids VALUES ( 8, -2);
+INSERT INTO ids VALUES ( 4, -4);
+INSERT INTO ids VALUES ( 2, -8);
+INSERT INTO ids VALUES ( 1, -16);
+SELECT * FROM ids;
+id value
+-16 1
+-8 2
+-4 4
+-2 8
+-1 16
+1 -16
+2 -8
+4 -4
+8 -2
+16 -1
+SELECT * FROM ids WHERE id BETWEEN -4 AND 8;
+id value
+-4 4
+-2 8
+-1 16
+1 -16
+2 -8
+4 -4
+8 -2
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_bigint_unsigned.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_bigint_unsigned.result
new file mode 100644
index 00000000000..b0004a3c87a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_bigint_unsigned.result
@@ -0,0 +1,31 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id BIGINT UNSIGNED,
+value BIGINT UNSIGNED,
+KEY (id, value)
+);
+INSERT INTO ids VALUES ( 1, 1);
+INSERT INTO ids VALUES ( 2, 2);
+INSERT INTO ids VALUES ( 4, 3);
+INSERT INTO ids VALUES ( 8, 4);
+INSERT INTO ids VALUES (16, 5);
+INSERT INTO ids VALUES (32, 6);
+INSERT INTO ids VALUES (64, 7);
+INSERT INTO ids VALUES (128, 8);
+SELECT * FROM ids;
+id value
+1 1
+2 2
+4 3
+8 4
+16 5
+32 6
+64 7
+128 8
+SELECT * FROM ids WHERE id BETWEEN 4 AND 32;
+id value
+4 3
+8 4
+16 5
+32 6
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_double.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_double.result
new file mode 100644
index 00000000000..862a314c9c2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_double.result
@@ -0,0 +1,38 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id DOUBLE,
+value DOUBLE,
+KEY (id, value)
+);
+INSERT INTO ids VALUES ( -1.1, 16.16);
+INSERT INTO ids VALUES ( -2.2, 8.8);
+INSERT INTO ids VALUES ( -4.4, 4.4);
+INSERT INTO ids VALUES ( -8.8, 2.2);
+INSERT INTO ids VALUES (-16.6, 1.1);
+INSERT INTO ids VALUES ( 16.6, -1.1);
+INSERT INTO ids VALUES ( 8.8, -2.2);
+INSERT INTO ids VALUES ( 4.4, -4.4);
+INSERT INTO ids VALUES ( 2.2, -8.8);
+INSERT INTO ids VALUES ( 1.1, -16.16);
+SELECT * FROM ids;
+id value
+-16.6 1.1
+-8.8 2.2
+-4.4 4.4
+-2.2 8.8
+-1.1 16.16
+1.1 -16.16
+2.2 -8.8
+4.4 -4.4
+8.8 -2.2
+16.6 -1.1
+SELECT * FROM ids WHERE id BETWEEN -4.5 AND 8.9;
+id value
+-4.4 4.4
+-2.2 8.8
+-1.1 16.16
+1.1 -16.16
+2.2 -8.8
+4.4 -4.4
+8.8 -2.2
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_float.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_float.result
new file mode 100644
index 00000000000..04d44130ed4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_float.result
@@ -0,0 +1,38 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id FLOAT,
+value FLOAT,
+KEY (id, value)
+);
+INSERT INTO ids VALUES ( -1.1, 16.16);
+INSERT INTO ids VALUES ( -2.2, 8.8);
+INSERT INTO ids VALUES ( -4.4, 4.4);
+INSERT INTO ids VALUES ( -8.8, 2.2);
+INSERT INTO ids VALUES (-16.6, 1.1);
+INSERT INTO ids VALUES ( 16.6, -1.1);
+INSERT INTO ids VALUES ( 8.8, -2.2);
+INSERT INTO ids VALUES ( 4.4, -4.4);
+INSERT INTO ids VALUES ( 2.2, -8.8);
+INSERT INTO ids VALUES ( 1.1, -16.16);
+SELECT * FROM ids;
+id value
+-16.6 1.1
+-8.8 2.2
+-4.4 4.4
+-2.2 8.8
+-1.1 16.16
+1.1 -16.16
+2.2 -8.8
+4.4 -4.4
+8.8 -2.2
+16.6 -1.1
+SELECT * FROM ids WHERE id BETWEEN -4.5 AND 8.9;
+id value
+-4.4 4.4
+-2.2 8.8
+-1.1 16.16
+1.1 -16.16
+2.2 -8.8
+4.4 -4.4
+8.8 -2.2
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_int.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_int.result
new file mode 100644
index 00000000000..8d3f6f7ff9e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_int.result
@@ -0,0 +1,38 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id INT,
+value INT,
+KEY (id, value)
+);
+INSERT INTO ids VALUES ( -1, 16);
+INSERT INTO ids VALUES ( -2, 8);
+INSERT INTO ids VALUES ( -4, 4);
+INSERT INTO ids VALUES ( -8, 2);
+INSERT INTO ids VALUES (-16, 1);
+INSERT INTO ids VALUES ( 16, -1);
+INSERT INTO ids VALUES ( 8, -2);
+INSERT INTO ids VALUES ( 4, -4);
+INSERT INTO ids VALUES ( 2, -8);
+INSERT INTO ids VALUES ( 1, -16);
+SELECT * FROM ids;
+id value
+-16 1
+-8 2
+-4 4
+-2 8
+-1 16
+1 -16
+2 -8
+4 -4
+8 -2
+16 -1
+SELECT * FROM ids WHERE id BETWEEN -4 AND 8;
+id value
+-4 4
+-2 8
+-1 16
+1 -16
+2 -8
+4 -4
+8 -2
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_int_unsigned.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_int_unsigned.result
new file mode 100644
index 00000000000..d09f97e0d06
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_int_unsigned.result
@@ -0,0 +1,31 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id INT UNSIGNED,
+value INT UNSIGNED,
+KEY (id, value)
+);
+INSERT INTO ids VALUES ( 1, 1);
+INSERT INTO ids VALUES ( 2, 2);
+INSERT INTO ids VALUES ( 4, 3);
+INSERT INTO ids VALUES ( 8, 4);
+INSERT INTO ids VALUES (16, 5);
+INSERT INTO ids VALUES (32, 6);
+INSERT INTO ids VALUES (64, 7);
+INSERT INTO ids VALUES (128, 8);
+SELECT * FROM ids;
+id value
+1 1
+2 2
+4 3
+8 4
+16 5
+32 6
+64 7
+128 8
+SELECT * FROM ids WHERE id BETWEEN 4 AND 32;
+id value
+4 3
+8 4
+16 5
+32 6
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_mediumint.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_mediumint.result
new file mode 100644
index 00000000000..5242f10ff02
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_mediumint.result
@@ -0,0 +1,38 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id MEDIUMINT,
+value MEDIUMINT,
+KEY (id, value)
+);
+INSERT INTO ids VALUES ( -1, 16);
+INSERT INTO ids VALUES ( -2, 8);
+INSERT INTO ids VALUES ( -4, 4);
+INSERT INTO ids VALUES ( -8, 2);
+INSERT INTO ids VALUES (-16, 1);
+INSERT INTO ids VALUES ( 16, -1);
+INSERT INTO ids VALUES ( 8, -2);
+INSERT INTO ids VALUES ( 4, -4);
+INSERT INTO ids VALUES ( 2, -8);
+INSERT INTO ids VALUES ( 1, -16);
+SELECT * FROM ids;
+id value
+-16 1
+-8 2
+-4 4
+-2 8
+-1 16
+1 -16
+2 -8
+4 -4
+8 -2
+16 -1
+SELECT * FROM ids WHERE id BETWEEN -4 AND 8;
+id value
+-4 4
+-2 8
+-1 16
+1 -16
+2 -8
+4 -4
+8 -2
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_mediumint_unsigned.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_mediumint_unsigned.result
new file mode 100644
index 00000000000..41d9c0ebeeb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_mediumint_unsigned.result
@@ -0,0 +1,31 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id MEDIUMINT UNSIGNED,
+value MEDIUMINT UNSIGNED,
+KEY (id, value)
+);
+INSERT INTO ids VALUES ( 1, 1);
+INSERT INTO ids VALUES ( 2, 2);
+INSERT INTO ids VALUES ( 4, 3);
+INSERT INTO ids VALUES ( 8, 4);
+INSERT INTO ids VALUES (16, 5);
+INSERT INTO ids VALUES (32, 6);
+INSERT INTO ids VALUES (64, 7);
+INSERT INTO ids VALUES (128, 8);
+SELECT * FROM ids;
+id value
+1 1
+2 2
+4 3
+8 4
+16 5
+32 6
+64 7
+128 8
+SELECT * FROM ids WHERE id BETWEEN 4 AND 32;
+id value
+4 3
+8 4
+16 5
+32 6
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_smallint.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_smallint.result
new file mode 100644
index 00000000000..e34b7b7ddab
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_smallint.result
@@ -0,0 +1,38 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id SMALLINT,
+value SMALLINT,
+KEY (id, value)
+);
+INSERT INTO ids VALUES ( -1, 16);
+INSERT INTO ids VALUES ( -2, 8);
+INSERT INTO ids VALUES ( -4, 4);
+INSERT INTO ids VALUES ( -8, 2);
+INSERT INTO ids VALUES (-16, 1);
+INSERT INTO ids VALUES ( 16, -1);
+INSERT INTO ids VALUES ( 8, -2);
+INSERT INTO ids VALUES ( 4, -4);
+INSERT INTO ids VALUES ( 2, -8);
+INSERT INTO ids VALUES ( 1, -16);
+SELECT * FROM ids;
+id value
+-16 1
+-8 2
+-4 4
+-2 8
+-1 16
+1 -16
+2 -8
+4 -4
+8 -2
+16 -1
+SELECT * FROM ids WHERE id BETWEEN -4 AND 8;
+id value
+-4 4
+-2 8
+-1 16
+1 -16
+2 -8
+4 -4
+8 -2
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_smallint_unsigned.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_smallint_unsigned.result
new file mode 100644
index 00000000000..9014f9afaec
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_smallint_unsigned.result
@@ -0,0 +1,31 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id SMALLINT UNSIGNED,
+value SMALLINT UNSIGNED,
+KEY (id, value)
+);
+INSERT INTO ids VALUES ( 1, 1);
+INSERT INTO ids VALUES ( 2, 2);
+INSERT INTO ids VALUES ( 4, 3);
+INSERT INTO ids VALUES ( 8, 4);
+INSERT INTO ids VALUES (16, 5);
+INSERT INTO ids VALUES (32, 6);
+INSERT INTO ids VALUES (64, 7);
+INSERT INTO ids VALUES (128, 8);
+SELECT * FROM ids;
+id value
+1 1
+2 2
+4 3
+8 4
+16 5
+32 6
+64 7
+128 8
+SELECT * FROM ids WHERE id BETWEEN 4 AND 32;
+id value
+4 3
+8 4
+16 5
+32 6
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_tinyint.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_tinyint.result
new file mode 100644
index 00000000000..63b27fdd4ff
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_tinyint.result
@@ -0,0 +1,38 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id TINYINT,
+value TINYINT,
+KEY (id, value)
+);
+INSERT INTO ids VALUES ( -1, 16);
+INSERT INTO ids VALUES ( -2, 8);
+INSERT INTO ids VALUES ( -4, 4);
+INSERT INTO ids VALUES ( -8, 2);
+INSERT INTO ids VALUES (-16, 1);
+INSERT INTO ids VALUES ( 16, -1);
+INSERT INTO ids VALUES ( 8, -2);
+INSERT INTO ids VALUES ( 4, -4);
+INSERT INTO ids VALUES ( 2, -8);
+INSERT INTO ids VALUES ( 1, -16);
+SELECT * FROM ids;
+id value
+-16 1
+-8 2
+-4 4
+-2 8
+-1 16
+1 -16
+2 -8
+4 -4
+8 -2
+16 -1
+SELECT * FROM ids WHERE id BETWEEN -4 AND 8;
+id value
+-4 4
+-2 8
+-1 16
+1 -16
+2 -8
+4 -4
+8 -2
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_tinyint_unsigned.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_tinyint_unsigned.result
new file mode 100644
index 00000000000..412b9b7c0c8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_tinyint_unsigned.result
@@ -0,0 +1,31 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id TINYINT UNSIGNED,
+value TINYINT UNSIGNED,
+KEY (id, value)
+);
+INSERT INTO ids VALUES ( 1, 1);
+INSERT INTO ids VALUES ( 2, 2);
+INSERT INTO ids VALUES ( 4, 3);
+INSERT INTO ids VALUES ( 8, 4);
+INSERT INTO ids VALUES (16, 5);
+INSERT INTO ids VALUES (32, 6);
+INSERT INTO ids VALUES (64, 7);
+INSERT INTO ids VALUES (128, 8);
+SELECT * FROM ids;
+id value
+1 1
+2 2
+4 3
+8 4
+16 5
+32 6
+64 7
+128 8
+SELECT * FROM ids WHERE id BETWEEN 4 AND 32;
+id value
+4 3
+8 4
+16 5
+32 6
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_varchar.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_varchar.result
new file mode 100644
index 00000000000..c08522d0dab
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_varchar.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id VARCHAR(5),
+value VARCHAR(10),
+KEY (id, value)
+) DEFAULT CHARSET=utf8 COLLATE utf8_bin;
+INSERT INTO ids VALUES ("abc", "Abc");
+INSERT INTO ids VALUES ("acd", "aBc");
+INSERT INTO ids VALUES ("ade", "abC");
+INSERT INTO ids VALUES ("aef", "abc");
+INSERT INTO ids VALUES ("ABC", "aBC");
+INSERT INTO ids VALUES ("ACD", "AbC");
+INSERT INTO ids VALUES ("ADE", "ABc");
+INSERT INTO ids VALUES ("AEF", "ABC");
+SELECT * FROM ids;
+id value
+ABC aBC
+ACD AbC
+ADE ABc
+AEF ABC
+abc Abc
+acd aBc
+ade abC
+aef abc
+SELECT * FROM ids WHERE id BETWEEN "ab" AND "ad";
+id value
+abc Abc
+acd aBc
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_varchar_collation.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_varchar_collation.result
new file mode 100644
index 00000000000..9882f2b1598
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_multiple_varchar_collation.result
@@ -0,0 +1,31 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id VARCHAR(5),
+value VARCHAR(10),
+KEY (id, value)
+) DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;
+INSERT INTO ids VALUES ("abc", "Abc");
+INSERT INTO ids VALUES ("acd", "aBc");
+INSERT INTO ids VALUES ("ade", "abC");
+INSERT INTO ids VALUES ("aef", "abc");
+INSERT INTO ids VALUES ("ABC", "aBC");
+INSERT INTO ids VALUES ("ACD", "AbC");
+INSERT INTO ids VALUES ("ADE", "ABc");
+INSERT INTO ids VALUES ("AEF", "ABC");
+SELECT * FROM ids;
+id value
+abc Abc
+acd aBc
+ade abC
+aef abc
+ABC aBC
+ACD AbC
+ADE ABc
+AEF ABC
+SELECT * FROM ids WHERE id BETWEEN "ab" AND "ad";
+id value
+abc Abc
+ABC aBC
+acd aBc
+ACD AbC
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_normal_int.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_normal_int.result
new file mode 100644
index 00000000000..67f1f4f8268
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_normal_int.result
@@ -0,0 +1,55 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id INT,
+KEY (id)
+);
+INSERT INTO ids VALUES (1);
+INSERT INTO ids SELECT id + 1 FROM ids;
+INSERT INTO ids SELECT id + 2 FROM ids;
+INSERT INTO ids SELECT id + 4 FROM ids;
+INSERT INTO ids SELECT id + 8 FROM ids;
+INSERT INTO ids SELECT id + 16 FROM ids;
+SELECT * FROM ids;
+id
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+SELECT * FROM ids WHERE id BETWEEN 10 AND 16;
+id
+10
+11
+12
+13
+14
+15
+16
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_normal_varchar.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_normal_varchar.result
new file mode 100644
index 00000000000..2cdde6c4086
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_normal_varchar.result
@@ -0,0 +1,55 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id VARCHAR(10),
+KEY (id)
+);
+INSERT INTO ids VALUES ("1");
+INSERT INTO ids SELECT id + "1" FROM ids;
+INSERT INTO ids SELECT id + "2" FROM ids;
+INSERT INTO ids SELECT id + "4" FROM ids;
+INSERT INTO ids SELECT id + "8" FROM ids;
+INSERT INTO ids SELECT id + "16" FROM ids;
+SELECT * FROM ids;
+id
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+SELECT * FROM ids WHERE id BETWEEN "10" AND "16";
+id
+10
+11
+12
+13
+14
+15
+16
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_primary_int.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_primary_int.result
new file mode 100644
index 00000000000..23f63d2ffca
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_primary_int.result
@@ -0,0 +1,55 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id INT,
+PRIMARY KEY (id)
+);
+INSERT INTO ids VALUES (1);
+INSERT INTO ids SELECT id + 1 FROM ids;
+INSERT INTO ids SELECT id + 2 FROM ids;
+INSERT INTO ids SELECT id + 4 FROM ids;
+INSERT INTO ids SELECT id + 8 FROM ids;
+INSERT INTO ids SELECT id + 16 FROM ids;
+SELECT * FROM ids;
+id
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+SELECT * FROM ids WHERE id BETWEEN 10 AND 16;
+id
+10
+11
+12
+13
+14
+15
+16
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_read_primary_varchar.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_primary_varchar.result
new file mode 100644
index 00000000000..7fa17bd79d9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_read_primary_varchar.result
@@ -0,0 +1,55 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id VARCHAR(10),
+PRIMARY KEY (id)
+);
+INSERT INTO ids VALUES ("1");
+INSERT INTO ids SELECT id + "1" FROM ids;
+INSERT INTO ids SELECT id + "2" FROM ids;
+INSERT INTO ids SELECT id + "4" FROM ids;
+INSERT INTO ids SELECT id + "8" FROM ids;
+INSERT INTO ids SELECT id + "16" FROM ids;
+SELECT * FROM ids;
+id
+1
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+2
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+3
+30
+31
+32
+4
+5
+6
+7
+8
+9
+SELECT * FROM ids WHERE id BETWEEN "10" AND "16";
+id
+10
+11
+12
+13
+14
+15
+16
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_unique_delete_by_primary_key.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_unique_delete_by_primary_key.result
new file mode 100644
index 00000000000..4e10cf72415
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_unique_delete_by_primary_key.result
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS users;
+CREATE TABLE users (
+id int PRIMARY KEY,
+name varchar(100) NOT NULL,
+UNIQUE KEY name (name)
+) DEFAULT CHARSET=utf8;
+INSERT INTO users VALUES (1, "Alice");
+DELETE FROM users WHERE id = 1;
+INSERT INTO users VALUES (1, "Alice");
+SELECT * FROM users;
+id name
+1 Alice
+DROP TABLE users;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_unique_insert_after_error.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_unique_insert_after_error.result
new file mode 100644
index 00000000000..7b2c0b86214
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_unique_insert_after_error.result
@@ -0,0 +1,15 @@
+DROP TABLE IF EXISTS users;
+CREATE TABLE users (
+id int PRIMARY KEY,
+name varchar(100) NOT NULL,
+UNIQUE KEY name (name)
+) DEFAULT CHARSET=utf8;
+INSERT INTO users VALUES (1, "Alice");
+INSERT INTO users VALUES (1, "Bob");
+ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
+INSERT INTO users VALUES (2, "Bob");
+SELECT * FROM users;
+id name
+1 Alice
+2 Bob
+DROP TABLE users;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_unique_varchar.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_unique_varchar.result
new file mode 100644
index 00000000000..155cdf15959
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_unique_varchar.result
@@ -0,0 +1,15 @@
+DROP TABLE IF EXISTS users;
+CREATE TABLE users (
+name varchar(100) NOT NULL,
+UNIQUE KEY name (name)
+) DEFAULT CHARSET=utf8;
+INSERT INTO users VALUES ("Alice");
+INSERT INTO users VALUES ("Bob");
+SELECT * FROM users;
+name
+Alice
+Bob
+SELECT * FROM users WHERE name = "aLiCe";
+name
+Alice
+DROP TABLE users;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_update_multiple_column.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_update_multiple_column.result
new file mode 100644
index 00000000000..9165ca26fea
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_update_multiple_column.result
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS scores;
+SET NAMES utf8;
+CREATE TABLE scores (
+deleted BOOLEAN,
+value INT,
+INDEX (deleted, value)
+);
+INSERT INTO scores VALUES (FALSE, 1);
+INSERT INTO scores VALUES (FALSE, 1);
+INSERT INTO scores VALUES (FALSE, 2);
+SELECT count(*) FROM scores WHERE deleted = FALSE;
+count(*)
+3
+UPDATE scores SET deleted = TRUE WHERE value = 1;
+SELECT count(*) FROM scores WHERE deleted = FALSE;
+count(*)
+1
+DROP TABLE scores;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/index_update_single_column.result b/storage/mroonga/mysql-test/mroonga/storage/r/index_update_single_column.result
new file mode 100644
index 00000000000..b283af17f94
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/index_update_single_column.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS scores;
+SET NAMES utf8;
+CREATE TABLE scores (
+value INT,
+INDEX (value)
+);
+INSERT INTO scores VALUES (21);
+INSERT INTO scores VALUES (21);
+INSERT INTO scores VALUES (22);
+SELECT count(*) FROM scores WHERE value >= 20;
+count(*)
+3
+UPDATE scores SET value = 11 WHERE value = 21;
+SELECT count(*) FROM scores WHERE value >= 20;
+count(*)
+1
+DROP TABLE scores;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_plugins.result b/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_plugins.result
new file mode 100644
index 00000000000..46c6f6ab258
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_plugins.result
@@ -0,0 +1,4 @@
+select PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_TYPE
+from information_schema.plugins where plugin_name = "Mroonga";
+PLUGIN_NAME PLUGIN_VERSION PLUGIN_TYPE
+Mroonga 4.5 STORAGE ENGINE
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_plugins.result.in b/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_plugins.result.in
new file mode 100644
index 00000000000..f1020453183
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_plugins.result.in
@@ -0,0 +1,4 @@
+select PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_TYPE
+from information_schema.plugins where plugin_name = "Mroonga";
+PLUGIN_NAME PLUGIN_VERSION PLUGIN_TYPE
+Mroonga @MRN_PLUGIN_VERSION@ STORAGE ENGINE
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_tables_auto_increment_none.result b/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_tables_auto_increment_none.result
new file mode 100644
index 00000000000..c23dab5e4a7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_tables_auto_increment_none.result
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id INT PRIMARY KEY
+);
+SELECT AUTO_INCREMENT
+FROM INFORMATION_SCHEMA.TABLES
+WHERE TABLE_NAME = "ids";
+AUTO_INCREMENT
+NULL
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_tables_auto_increment_use.result b/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_tables_auto_increment_use.result
new file mode 100644
index 00000000000..96d1b0ad32e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_tables_auto_increment_use.result
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id INT AUTO_INCREMENT PRIMARY KEY
+);
+SELECT AUTO_INCREMENT
+FROM INFORMATION_SCHEMA.TABLES
+WHERE TABLE_NAME = "ids";
+AUTO_INCREMENT
+1
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_tables_data_length.result b/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_tables_data_length.result
new file mode 100644
index 00000000000..d58afb1f6d4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/information_schema_tables_data_length.result
@@ -0,0 +1,26 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT PRIMARY KEY,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+SELECT COUNT(*)
+FROM INFORMATION_SCHEMA.TABLES
+WHERE TABLE_NAME = "diaries" AND DATA_LENGTH > 0;
+COUNT(*)
+1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/insert_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/storage/r/insert_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..00466f19bb5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/insert_TODO_SPLIT_ME.result
@@ -0,0 +1,90 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 tinyint);
+insert into t1 values(1);
+select * from t1;
+c1
+1
+drop table t1;
+create table t1 (c1 smallint);
+insert into t1 values(1);
+select * from t1;
+c1
+1
+drop table t1;
+create table t1 (c1 mediumint);
+insert into t1 values(1);
+select * from t1;
+c1
+1
+drop table t1;
+create table t1 (c1 int);
+insert into t1 values(1);
+select * from t1;
+c1
+1
+drop table t1;
+create table t1 (c1 bigint);
+insert into t1 values(1);
+select * from t1;
+c1
+1
+drop table t1;
+create table t1 (c1 float);
+insert into t1 values(0.5);
+select * from t1;
+c1
+0.5
+drop table t1;
+create table t1 (c1 double);
+insert into t1 values(0.5);
+select * from t1;
+c1
+0.5
+drop table t1;
+create table t1 (c1 date);
+insert into t1 values("2010/03/26");
+select * from t1;
+c1
+2010-03-26
+drop table t1;
+create table t1 (c1 time);
+insert into t1 values("11:22:33");
+select * from t1;
+c1
+11:22:33
+drop table t1;
+create table t1 (c1 year);
+insert into t1 values("2010");
+select * from t1;
+c1
+2010
+drop table t1;
+create table t1 (c1 datetime);
+insert into t1 values("2010/03/26 11:22:33");
+select * from t1;
+c1
+2010-03-26 11:22:33
+drop table t1;
+create table t1 (c1 int, _id int);
+set sql_mode="";
+insert into t1 (c1,_id) values (1,1);
+Warnings:
+Warning 1265 Data truncated for column '_id' at row 1
+set sql_mode="strict_all_tables";
+insert into t1 (c1,_id) values (4,1);
+ERROR 01000: Data truncated for column '_id' at row 1
+select * from t1;
+c1 _id
+1 1
+drop table t1;
+create table t1 (c1 int primary key, c2 int);
+insert into t1 values(1,100);
+select * from t1;
+c1 c2
+1 100
+insert into t1 values(1,200);
+ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
+select * from t1;
+c1 c2
+1 100
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/insert_on_duplicate_key_update_no_primary_key_and_unique_key_twice.result b/storage/mroonga/mysql-test/mroonga/storage/r/insert_on_duplicate_key_update_no_primary_key_and_unique_key_twice.result
new file mode 100644
index 00000000000..0d2c9dd7987
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/insert_on_duplicate_key_update_no_primary_key_and_unique_key_twice.result
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS numbers;
+CREATE TABLE numbers (
+id INT,
+count INT,
+UNIQUE (id)
+);
+INSERT INTO numbers (id, count) VALUES (1, 1) ON DUPLICATE KEY UPDATE count = 2;
+INSERT INTO numbers (id, count) VALUES (1, 3) ON DUPLICATE KEY UPDATE count = 4;
+SELECT * FROM numbers;
+id count
+1 4
+INSERT INTO numbers (id, count) VALUES (2, 1) ON DUPLICATE KEY UPDATE count = 2;
+INSERT INTO numbers (id, count) VALUES (2, 3) ON DUPLICATE KEY UPDATE count = 4;
+SELECT * FROM numbers;
+id count
+1 4
+2 4
+DROP TABLE numbers;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/insert_on_duplicate_key_update_primary_key.result b/storage/mroonga/mysql-test/mroonga/storage/r/insert_on_duplicate_key_update_primary_key.result
new file mode 100644
index 00000000000..12d4b173eaa
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/insert_on_duplicate_key_update_primary_key.result
@@ -0,0 +1,26 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+day DATE PRIMARY KEY,
+title TEXT
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `day` date NOT NULL,
+ `title` text,
+ PRIMARY KEY (`day`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (day, title)
+VALUES ("2012-02-14", "clear day")
+ON DUPLICATE KEY UPDATE title = "clear day (duplicated)";
+INSERT INTO diaries (day, title)
+VALUES ("2012-02-14", "rainy day")
+ON DUPLICATE KEY UPDATE title = "rainy day (duplicated)";
+INSERT INTO diaries (day, title)
+VALUES ("2012-02-15", "cloudy day")
+ON DUPLICATE KEY UPDATE title = "cloudy day (duplicated)";
+SELECT * FROM diaries;
+day title
+2012-02-14 rainy day (duplicated)
+2012-02-15 cloudy day
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/insert_on_duplicate_key_update_unique_key.result b/storage/mroonga/mysql-test/mroonga/storage/r/insert_on_duplicate_key_update_unique_key.result
new file mode 100644
index 00000000000..288e9e3a2c8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/insert_on_duplicate_key_update_unique_key.result
@@ -0,0 +1,30 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+day DATE,
+title TEXT,
+UNIQUE KEY day (day)
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `day` date DEFAULT NULL,
+ `title` text,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `day` (`day`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (day, title)
+VALUES ("2012-02-14", "clear day")
+ON DUPLICATE KEY UPDATE title = "clear day (duplicated)";
+INSERT INTO diaries (day, title)
+VALUES ("2012-02-14", "rainy day")
+ON DUPLICATE KEY UPDATE title = "rainy day (duplicated)";
+INSERT INTO diaries (day, title)
+VALUES ("2012-02-15", "cloudy day")
+ON DUPLICATE KEY UPDATE title = "cloudy day (duplicated)";
+SELECT * FROM diaries;
+id day title
+1 2012-02-14 rainy day (duplicated)
+3 2012-02-15 cloudy day
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/like_unicode_ci.result b/storage/mroonga/mysql-test/mroonga/storage/r/like_unicode_ci.result
new file mode 100644
index 00000000000..1cc1d92451d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/like_unicode_ci.result
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS terms;
+SET NAMES utf8;
+CREATE TABLE terms (
+content varchar(64) NOT NULL COLLATE 'utf8_unicode_ci',
+INDEX (content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO terms VALUES ('track');
+INSERT INTO terms VALUES ('trackback');
+SELECT * FROM terms WHERE content LIKE 'TRACK%';
+content
+track
+trackback
+DROP TABLE terms;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/lock_tables_read.result b/storage/mroonga/mysql-test/mroonga/storage/r/lock_tables_read.result
new file mode 100644
index 00000000000..4e1db465c63
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/lock_tables_read.result
@@ -0,0 +1,7 @@
+DROP TABLE IF EXISTS counts;
+CREATE TABLE counts (
+id INT PRIMARY KEY AUTO_INCREMENT
+);
+LOCK TABLES counts READ;
+UNLOCK TABLES;
+DROP TABLE counts;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_not_optimized_disabled.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_not_optimized_disabled.result
new file mode 100644
index 00000000000..da15588ccd5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_not_optimized_disabled.result
@@ -0,0 +1,47 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SET mroonga_enable_optimization=FALSE;
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+month = 11
+ORDER BY day LIMIT 1,2;
+id year month day title content
+3 2011 11 11 富士山 今日も天気がよくてきれいに見える。
+4 2011 11 12 帰り道 今日は天気がよくてよかった。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 0
+SET mroonga_enable_optimization=TRUE;
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_not_optimized_no_limit.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_not_optimized_no_limit.result
new file mode 100644
index 00000000000..0794b104a62
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_not_optimized_no_limit.result
@@ -0,0 +1,47 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ORDER BY day;
+id year month day title content
+7 2011 12 2 初雪 今日の天気は雪!
+1 2011 11 9 Hello 今日からはじめました。
+3 2011 11 11 富士山 今日も天気がよくてきれいに見える。
+4 2011 11 12 帰り道 今日は天気がよくてよかった。
+5 2011 11 13 はれ 天気がよいのは今日までみたい。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 0
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_between.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_between.result
new file mode 100644
index 00000000000..bd34b040ad7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_between.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+date DATETIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `date` datetime DEFAULT NULL,
+ `content` text,
+ KEY `date` (`date`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:30", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:31", "Today's lucky item is flower!");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:32", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:33", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+date BETWEEN "2011-11-11 12:23:31" AND "2011-11-11 12:23:33"
+ ORDER BY id LIMIT 1,2;
+id date content
+3 2011-11-11 12:23:32 I will do something today!
+4 2011-11-11 12:23:33 I don't want to anything today...
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_between_over.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_between_over.result
new file mode 100644
index 00000000000..f495a3b5956
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_between_over.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+date DATETIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `date` datetime DEFAULT NULL,
+ `content` text,
+ KEY `date` (`date`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:30", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:31", "Today's lucky item is flower!");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:32", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:33", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+date BETWEEN "2011-11-11 12:23:31" AND "2011-11-11 12:23:43"
+ ORDER BY id LIMIT 1,2;
+id date content
+3 2011-11-11 12:23:32 I will do something today!
+4 2011-11-11 12:23:33 I don't want to anything today...
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_equal.result
new file mode 100644
index 00000000000..b5be750690d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_equal.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+date DATETIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `date` datetime DEFAULT NULL,
+ `content` text,
+ KEY `date` (`date`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:34", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:34", "Tomorrow will be fine.");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:34", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:34", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+date = "2011-11-11 12:23:34"
+ ORDER BY id LIMIT 1,2;
+id date content
+3 2011-11-11 12:23:34 I will do something today!
+4 2011-11-11 12:23:34 I don't want to anything today...
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_greater_than.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_greater_than.result
new file mode 100644
index 00000000000..6f20f3b8b35
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_greater_than.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+date DATETIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `date` datetime DEFAULT NULL,
+ `content` text,
+ KEY `date` (`date`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:30", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:31", "Today's lucky item is flower!");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:32", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:33", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+date > "2011-11-11 12:23:31"
+ ORDER BY id LIMIT 1,2;
+id date content
+4 2011-11-11 12:23:33 I don't want to anything today...
+5 2011-11-11 12:23:34 I'm sleepy today.
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_greater_than_or_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_greater_than_or_equal.result
new file mode 100644
index 00000000000..5df3c12ccd8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_greater_than_or_equal.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+date DATETIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `date` datetime DEFAULT NULL,
+ `content` text,
+ KEY `date` (`date`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:30", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:31", "Today's lucky item is flower!");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:32", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:33", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+date >= "2011-11-11 12:23:31"
+ ORDER BY id LIMIT 1,2;
+id date content
+3 2011-11-11 12:23:32 I will do something today!
+4 2011-11-11 12:23:33 I don't want to anything today...
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_less_than.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_less_than.result
new file mode 100644
index 00000000000..c12e69bf686
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_less_than.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+date DATETIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `date` datetime DEFAULT NULL,
+ `content` text,
+ KEY `date` (`date`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:30", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:31", "Today's lucky item is flower!");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:32", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:33", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+date < "2011-11-11 12:23:33"
+ ORDER BY id LIMIT 1,2;
+id date content
+2 2011-11-11 12:23:31 Today's lucky item is flower!
+3 2011-11-11 12:23:32 I will do something today!
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_less_than_or_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_less_than_or_equal.result
new file mode 100644
index 00000000000..6d00bac66eb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_datetime_less_than_or_equal.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+date DATETIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `date` datetime DEFAULT NULL,
+ `content` text,
+ KEY `date` (`date`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:30", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:31", "Today's lucky item is flower!");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:32", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:33", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+date <= "2011-11-11 12:23:33"
+ ORDER BY id LIMIT 1,2;
+id date content
+2 2011-11-11 12:23:31 Today's lucky item is flower!
+3 2011-11-11 12:23:32 I will do something today!
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_have_primary_key.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_have_primary_key.result
new file mode 100644
index 00000000000..529f0d521ee
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_have_primary_key.result
@@ -0,0 +1,43 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 11, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(6, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE) ORDER BY day LIMIT 0,5;
+id year month day title content
+5 2011 12 1 久しぶり 天気が悪いからずっと留守番。
+6 2011 12 2 初雪 今日の天気は雪!
+1 2011 11 9 Hello 今日からはじめました。
+2 2011 11 10 天気 明日の富士山の天気について
+4 2011 11 11 帰り道 今日は天気がよくてよかった。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_between.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_between.result
new file mode 100644
index 00000000000..ed86c0e1e37
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_between.result
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS memos;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT UNSIGNED,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(id)
+) DEFAULT CHARSET UTF8;
+INSERT INTO memos VALUES(1, "Today is fine.");
+INSERT INTO memos VALUES(2, "Today's lucky item is flower!");
+INSERT INTO memos VALUES(3, "I will do something today!");
+INSERT INTO memos VALUES(4, "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "I'm sleepy today.");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+id BETWEEN 2 AND 4
+ORDER BY id LIMIT 1,2;
+id content
+3 I will do something today!
+4 I don't want to anything today...
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_between_over.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_between_over.result
new file mode 100644
index 00000000000..a18e2a15863
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_between_over.result
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS memos;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT UNSIGNED,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(id)
+) DEFAULT CHARSET UTF8;
+INSERT INTO memos VALUES(1, "Today is fine.");
+INSERT INTO memos VALUES(2, "Today's lucky item is flower!");
+INSERT INTO memos VALUES(3, "I will do something today!");
+INSERT INTO memos VALUES(4, "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "I'm sleepy today.");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+id BETWEEN 2 AND 6
+ORDER BY id LIMIT 1,2;
+id content
+3 I will do something today!
+4 I don't want to anything today...
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_equal.result
new file mode 100644
index 00000000000..2320b0ffbc4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_equal.result
@@ -0,0 +1,45 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+month = 11
+ORDER BY day LIMIT 1,2;
+id year month day title content
+3 2011 11 11 富士山 今日も天気がよくてきれいに見える。
+4 2011 11 12 帰り道 今日は天気がよくてよかった。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_greater_than.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_greater_than.result
new file mode 100644
index 00000000000..c367aef808a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_greater_than.result
@@ -0,0 +1,45 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+day > 10
+ORDER BY day LIMIT 1,2;
+id year month day title content
+4 2011 11 12 帰り道 今日は天気がよくてよかった。
+5 2011 11 13 はれ 天気がよいのは今日までみたい。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_greater_than_or_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_greater_than_or_equal.result
new file mode 100644
index 00000000000..b00a4e2a2c3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_greater_than_or_equal.result
@@ -0,0 +1,45 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+day >= 10
+ORDER BY day LIMIT 1,2;
+id year month day title content
+4 2011 11 12 帰り道 今日は天気がよくてよかった。
+5 2011 11 13 はれ 天気がよいのは今日までみたい。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_less_than.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_less_than.result
new file mode 100644
index 00000000000..5bf6bca4985
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_less_than.result
@@ -0,0 +1,45 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+day < 12
+ORDER BY day LIMIT 1,2;
+id year month day title content
+1 2011 11 9 Hello 今日からはじめました。
+3 2011 11 11 富士山 今日も天気がよくてきれいに見える。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_less_than_or_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_less_than_or_equal.result
new file mode 100644
index 00000000000..5d6a19a936e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_int_less_than_or_equal.result
@@ -0,0 +1,45 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+day <= 12
+ORDER BY day LIMIT 1,2;
+id year month day title content
+1 2011 11 9 Hello 今日からはじめました。
+3 2011 11 11 富士山 今日も天気がよくてきれいに見える。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_no_primary_key.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_no_primary_key.result
new file mode 100644
index 00000000000..6ee2991e51a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_no_primary_key.result
@@ -0,0 +1,42 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 11, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(6, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE) ORDER BY day LIMIT 0,5;
+id year month day title content
+5 2011 12 1 久しぶり 天気が悪いからずっと留守番。
+6 2011 12 2 初雪 今日の天気は雪!
+1 2011 11 9 Hello 今日からはじめました。
+2 2011 11 10 天気 明日の富士山の天気について
+4 2011 11 11 帰り道 今日は天気がよくてよかった。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_no_where_clause.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_no_where_clause.result
new file mode 100644
index 00000000000..9730069c849
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_no_where_clause.result
@@ -0,0 +1,19 @@
+drop table if exists t1;
+flush status;
+create table t1 (c1 int primary key, c2 int, c3 text, _id int, key idx1(c2), fulltext index ft(c3)) default charset utf8;
+insert into t1 values(1,10,"aa ii uu ee oo",null);
+insert into t1 values(2,20,"ka ki ku ke ko",null);
+insert into t1 values(3,30,"ii si ii se ii",null);
+insert into t1 values(4,40,"ta ti tu te to",null);
+insert into t1 values(5,50,"aa ii uu ii oo",null);
+show status like 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 0
+select *, match(c3) against("ii") from t1 order by c1 desc limit 2;
+c1 c2 c3 _id match(c3) against("ii")
+5 50 aa ii uu ii oo 5 349526
+4 40 ta ti tu te to 4 0
+show status like 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_asc.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_asc.result
new file mode 100644
index 00000000000..e91e8af5391
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_asc.result
@@ -0,0 +1,43 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ORDER BY day ASC LIMIT 1;
+id year month day title content
+7 2011 12 2 初雪 今日の天気は雪!
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_desc.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_desc.result
new file mode 100644
index 00000000000..da01145e45a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_desc.result
@@ -0,0 +1,43 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ORDER BY day DESC LIMIT 1;
+id year month day title content
+5 2011 11 13 はれ 天気がよいのは今日までみたい。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_id.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_id.result
new file mode 100644
index 00000000000..bb541c014bd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_id.result
@@ -0,0 +1,46 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+_id INT,
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `_id` int(11) DEFAULT NULL,
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(NULL, 1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(NULL, 2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(NULL, 3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(NULL, 4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(NULL, 5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(NULL, 6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(NULL, 7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ORDER BY _id
+LIMIT 1;
+_id id year month day title content
+1 1 2011 11 9 Hello 今日からはじめました。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_match_against.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_match_against.result
new file mode 100644
index 00000000000..fb9393687dc
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_order_by_match_against.result
@@ -0,0 +1,44 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ORDER BY MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+LIMIT 1;
+id year month day title content
+1 2011 11 9 Hello 今日からはじめました。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_select_match_against.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_select_match_against.result
new file mode 100644
index 00000000000..75ad884a268
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_select_match_against.result
@@ -0,0 +1,45 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT *, MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ORDER BY MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+LIMIT 1;
+id year month day title content MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+1 2011 11 9 Hello 今日からはじめました。 1
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_between.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_between.result
new file mode 100644
index 00000000000..bb4ac9c3b33
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_between.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS memos;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT UNSIGNED NOT NULL,
+writing_time TIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+Table Create Table
+memos CREATE TABLE `memos` (
+ `id` int(10) unsigned NOT NULL,
+ `writing_time` time DEFAULT NULL,
+ `content` text,
+ KEY `writing_time` (`writing_time`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO memos VALUES(1, "1:23:30", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:31", "Today's lucky item is flower!");
+INSERT INTO memos VALUES(3, "1:23:32", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:33", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+writing_time BETWEEN "1:23:31" AND "1:23:33"
+ ORDER BY id LIMIT 1,2;
+id writing_time content
+3 01:23:32 I will do something today!
+4 01:23:33 I don't want to anything today...
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_between_over.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_between_over.result
new file mode 100644
index 00000000000..1b4d38e0107
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_between_over.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS memos;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT UNSIGNED NOT NULL,
+writing_time TIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+Table Create Table
+memos CREATE TABLE `memos` (
+ `id` int(10) unsigned NOT NULL,
+ `writing_time` time DEFAULT NULL,
+ `content` text,
+ KEY `writing_time` (`writing_time`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO memos VALUES(1, "1:23:30", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:31", "Today's lucky item is flower!");
+INSERT INTO memos VALUES(3, "1:23:32", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:33", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+writing_time BETWEEN "1:23:31" AND "1:23:43"
+ ORDER BY id LIMIT 1,2;
+id writing_time content
+3 01:23:32 I will do something today!
+4 01:23:33 I don't want to anything today...
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_equal.result
new file mode 100644
index 00000000000..4fff9f9185f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_equal.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS memos;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT UNSIGNED NOT NULL,
+writing_time TIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+Table Create Table
+memos CREATE TABLE `memos` (
+ `id` int(10) unsigned NOT NULL,
+ `writing_time` time DEFAULT NULL,
+ `content` text,
+ KEY `writing_time` (`writing_time`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO memos VALUES(1, "1:23:34", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:34", "Tomorrow will be fine.");
+INSERT INTO memos VALUES(3, "1:23:34", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:34", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+writing_time = "1:23:34"
+ ORDER BY id LIMIT 1,2;
+id writing_time content
+3 01:23:34 I will do something today!
+4 01:23:34 I don't want to anything today...
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_greater_than.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_greater_than.result
new file mode 100644
index 00000000000..61119b2b74e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_greater_than.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS memos;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT UNSIGNED NOT NULL,
+writing_time TIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+Table Create Table
+memos CREATE TABLE `memos` (
+ `id` int(10) unsigned NOT NULL,
+ `writing_time` time DEFAULT NULL,
+ `content` text,
+ KEY `writing_time` (`writing_time`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO memos VALUES(1, "1:23:30", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:31", "Today's lucky item is flower!" );
+INSERT INTO memos VALUES(3, "1:23:32", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:33", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+writing_time > "1:23:31"
+ ORDER BY id LIMIT 1,2;
+id writing_time content
+4 01:23:33 I don't want to anything today...
+5 01:23:34 I'm sleepy today.
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_greater_than_or_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_greater_than_or_equal.result
new file mode 100644
index 00000000000..9bf8b91ea07
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_greater_than_or_equal.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS memos;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT UNSIGNED NOT NULL,
+writing_time TIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+Table Create Table
+memos CREATE TABLE `memos` (
+ `id` int(10) unsigned NOT NULL,
+ `writing_time` time DEFAULT NULL,
+ `content` text,
+ KEY `writing_time` (`writing_time`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO memos VALUES(1, "1:23:30", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:31", "Today's lucky item is flower!" );
+INSERT INTO memos VALUES(3, "1:23:32", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:33", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+writing_time >= "1:23:31"
+ ORDER BY id LIMIT 1,2;
+id writing_time content
+3 01:23:32 I will do something today!
+4 01:23:33 I don't want to anything today...
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_less_than.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_less_than.result
new file mode 100644
index 00000000000..e4d41f5867e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_less_than.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS memos;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT UNSIGNED NOT NULL,
+writing_time TIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+Table Create Table
+memos CREATE TABLE `memos` (
+ `id` int(10) unsigned NOT NULL,
+ `writing_time` time DEFAULT NULL,
+ `content` text,
+ KEY `writing_time` (`writing_time`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO memos VALUES(1, "1:23:30", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:31", "Today's lucky item is flower!");
+INSERT INTO memos VALUES(3, "1:23:32", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:33", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+writing_time < "1:23:33"
+ ORDER BY id LIMIT 1,2;
+id writing_time content
+2 01:23:31 Today's lucky item is flower!
+3 01:23:32 I will do something today!
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_less_than_or_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_less_than_or_equal.result
new file mode 100644
index 00000000000..31a497ef8bb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_time_less_than_or_equal.result
@@ -0,0 +1,35 @@
+DROP TABLE IF EXISTS memos;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE memos (
+id INT UNSIGNED NOT NULL,
+writing_time TIME,
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+Table Create Table
+memos CREATE TABLE `memos` (
+ `id` int(10) unsigned NOT NULL,
+ `writing_time` time DEFAULT NULL,
+ `content` text,
+ KEY `writing_time` (`writing_time`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO memos VALUES(1, "1:23:30", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:31", "Today's lucky item is flower!");
+INSERT INTO memos VALUES(3, "1:23:32", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:33", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+SELECT * FROM memos
+WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+writing_time <= "1:23:33"
+ ORDER BY id LIMIT 1,2;
+id writing_time content
+2 01:23:31 Today's lucky item is flower!
+3 01:23:32 I will do something today!
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_varchar_equal_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_varchar_equal_with_index.result
new file mode 100644
index 00000000000..46c80ddcce7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_varchar_equal_with_index.result
@@ -0,0 +1,46 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(title),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `title` (`title`),
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+title = "hello"
+ ORDER BY day LIMIT 1;
+id year month day title content
+1 2011 11 9 Hello 今日からはじめました。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_varchar_equal_without_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_varchar_equal_without_index.result
new file mode 100644
index 00000000000..38263c1083f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_varchar_equal_without_index.result
@@ -0,0 +1,44 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(month),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ KEY `month` (`month`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+SELECT * FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+title = "hello"
+ ORDER BY day LIMIT 1;
+id year month day title content
+1 2011 11 9 Hello 今日からはじめました。
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 0
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_between.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_between.result
new file mode 100644
index 00000000000..5a1b1bda2ce
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_between.result
@@ -0,0 +1,30 @@
+DROP TABLE IF EXISTS mroonga_releases;
+FLUSH STATUS;
+CREATE TABLE mroonga_releases (
+id INT PRIMARY KEY AUTO_INCREMENT,
+release_title TEXT,
+release_year YEAR,
+KEY (release_year),
+FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Groonga storage engine 0.1 has been released", "10");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 4.0 will be released", "2014");
+SELECT * FROM mroonga_releases
+WHERE release_year BETWEEN "11" AND "2013" AND
+MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ORDER BY id DESC LIMIT 1,2;
+id release_title release_year
+3 Mroonga 2.0 has been released 2012
+2 Rename Groonga storage engine to Mroonga 2011
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE mroonga_releases;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_between_over.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_between_over.result
new file mode 100644
index 00000000000..b0ad41fbbba
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_between_over.result
@@ -0,0 +1,30 @@
+DROP TABLE IF EXISTS mroonga_releases;
+FLUSH STATUS;
+CREATE TABLE mroonga_releases (
+id INT PRIMARY KEY AUTO_INCREMENT,
+release_title TEXT,
+release_year YEAR,
+KEY (release_year),
+FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Groonga storage engine 0.1 has been released", "10");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 4.0 will be released", "2014");
+SELECT * FROM mroonga_releases
+WHERE release_year BETWEEN "11" AND "2015" AND
+MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ORDER BY id DESC LIMIT 1,2;
+id release_title release_year
+4 Mroonga 3.0 has been released 2013
+3 Mroonga 2.0 has been released 2012
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE mroonga_releases;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_equal.result
new file mode 100644
index 00000000000..08db6b384e8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_equal.result
@@ -0,0 +1,32 @@
+DROP TABLE IF EXISTS mroonga_releases;
+FLUSH STATUS;
+CREATE TABLE mroonga_releases (
+id INT PRIMARY KEY AUTO_INCREMENT,
+release_title TEXT,
+release_year YEAR,
+KEY (release_year),
+FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Groonga storage engine (code name Mroonga) 1.0 has been released", "11");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 1.11 has been released", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 4.0 will be released", "2014");
+SELECT * FROM mroonga_releases
+WHERE release_year = "11" AND
+MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ORDER BY id DESC LIMIT 1,2;
+id release_title release_year
+2 Rename Groonga storage engine to Mroonga 2011
+1 Groonga storage engine (code name Mroonga) 1.0 has been released 2011
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE mroonga_releases;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_greater_than.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_greater_than.result
new file mode 100644
index 00000000000..a33cd484865
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_greater_than.result
@@ -0,0 +1,30 @@
+DROP TABLE IF EXISTS mroonga_releases;
+FLUSH STATUS;
+CREATE TABLE mroonga_releases (
+id INT PRIMARY KEY AUTO_INCREMENT,
+release_title TEXT,
+release_year YEAR,
+KEY (release_year),
+FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Groonga storage engine (code name Mroonga) 0.1 has been released", "10");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 4.0 will be released", "2014");
+SELECT * FROM mroonga_releases
+WHERE release_year > "11" AND
+MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ORDER BY id ASC LIMIT 2;
+id release_title release_year
+3 Mroonga 2.0 has been released 2012
+4 Mroonga 3.0 has been released 2013
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE mroonga_releases;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_greater_than_or_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_greater_than_or_equal.result
new file mode 100644
index 00000000000..7d2471b3aa0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_greater_than_or_equal.result
@@ -0,0 +1,30 @@
+DROP TABLE IF EXISTS mroonga_releases;
+FLUSH STATUS;
+CREATE TABLE mroonga_releases (
+id INT PRIMARY KEY AUTO_INCREMENT,
+release_title TEXT,
+release_year YEAR,
+KEY (release_year),
+FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Groonga storage engine (code name Mroonga) 0.1 has been released", "10");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 4.0 will be released", "2014");
+SELECT * FROM mroonga_releases
+WHERE release_year >= "11" AND
+MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ORDER BY id ASC LIMIT 2;
+id release_title release_year
+2 Rename Groonga storage engine to Mroonga 2011
+3 Mroonga 2.0 has been released 2012
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE mroonga_releases;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_less_than.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_less_than.result
new file mode 100644
index 00000000000..cac3d6f4356
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_less_than.result
@@ -0,0 +1,30 @@
+DROP TABLE IF EXISTS mroonga_releases;
+FLUSH STATUS;
+CREATE TABLE mroonga_releases (
+id INT PRIMARY KEY AUTO_INCREMENT,
+release_title TEXT,
+release_year YEAR,
+KEY (release_year),
+FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Groonga storage engine (code name Mroonga) 0.1 has been released", "10");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 4.0 will be released", "2014");
+SELECT * FROM mroonga_releases
+WHERE release_year < "13" AND
+MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ORDER BY id DESC LIMIT 1,2;
+id release_title release_year
+2 Rename Groonga storage engine to Mroonga 2011
+1 Groonga storage engine (code name Mroonga) 0.1 has been released 2010
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE mroonga_releases;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_less_than_or_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_less_than_or_equal.result
new file mode 100644
index 00000000000..df57f51e5d6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_order_limit_optimized_year_less_than_or_equal.result
@@ -0,0 +1,30 @@
+DROP TABLE IF EXISTS mroonga_releases;
+FLUSH STATUS;
+CREATE TABLE mroonga_releases (
+id INT PRIMARY KEY AUTO_INCREMENT,
+release_title TEXT,
+release_year YEAR,
+KEY (release_year),
+FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Groonga storage engine (code name Mroonga) 0.1 has been released", "10");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+VALUES ("Mroonga 4.0 will be released", "2014");
+SELECT * FROM mroonga_releases
+WHERE release_year <= "13" AND
+MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ORDER BY id DESC LIMIT 1,2;
+id release_title release_year
+3 Mroonga 2.0 has been released 2012
+2 Rename Groonga storage engine to Mroonga 2011
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+DROP TABLE mroonga_releases;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..b66801094bd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_TODO_SPLIT_ME.result
@@ -0,0 +1,106 @@
+drop table if exists t1, t2, t3;
+flush status;
+create table t1 (c1 int primary key, c2 int, c3 text, key idx1(c2), fulltext index ft(c3));
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"sa si su se so");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ee oo");
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 0
+select * from t1;
+c1 c2 c3
+1 10 aa ii uu ee oo
+2 20 ka ki ku ke ko
+3 30 sa si su se so
+4 40 ta ti tu te to
+5 50 aa ii uu ee oo
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 0
+select count(*) from t1;
+count(*)
+5
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 0
+select * from t1 force index(primary) where c1 between 2 and 4;
+c1 c2 c3
+2 20 ka ki ku ke ko
+3 30 sa si su se so
+4 40 ta ti tu te to
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 0
+select count(*) from t1 force index(primary) where c1 between 2 and 4;
+count(*)
+3
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 1
+select c1 from t1 force index(primary) where c1 < 3;
+c1
+1
+2
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 1
+select count(c1) from t1 force index(primary) where c1 < 3;
+count(c1)
+2
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 1
+select 1 from t1 force index(primary) where c1 > 3;
+1
+1
+1
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 1
+select count(1) from t1 force index(primary) where c1 > 3;
+count(1)
+2
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 2
+select * from t1 where match(c3) against("su");
+c1 c2 c3
+3 30 sa si su se so
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 2
+select count(*) from t1 where match(c3) against("su");
+count(*)
+1
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 3
+select * from t1 where match(c3) against("+su" in boolean mode);
+c1 c2 c3
+3 30 sa si su se so
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 3
+select count(*) from t1 where match(c3) against("+su" in boolean mode);
+count(*)
+1
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 4
+select * from t1 force index(idx1) where c2 between 20 and 40;
+c1 c2 c3
+2 20 ka ki ku ke ko
+3 30 sa si su se so
+4 40 ta ti tu te to
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 4
+select count(*) from t1 force index(idx1) where c2 between 20 and 40;
+count(*)
+3
+show status like 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 5
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_after_insert_multithread.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_after_insert_multithread.result
new file mode 100644
index 00000000000..c09ec340ccb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_after_insert_multithread.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+CREATE TABLE diaries (
+title TEXT,
+FULLTEXT INDEX ft(title)
+);
+INSERT INTO diaries VALUES("Hello mroonga!");
+INSERT INTO diaries VALUES("It's funny.");
+INSERT INTO diaries VALUES("Happy birthday!");
+SHOW STATUS LIKE 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 0
+SELECT COUNT(*) FROM diaries WHERE MATCH(title) AGAINST("mroonga" IN BOOLEAN MODE);
+COUNT(*)
+1
+SHOW STATUS LIKE 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_after_insert_single_thread.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_after_insert_single_thread.result
new file mode 100644
index 00000000000..c09ec340ccb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_after_insert_single_thread.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+CREATE TABLE diaries (
+title TEXT,
+FULLTEXT INDEX ft(title)
+);
+INSERT INTO diaries VALUES("Hello mroonga!");
+INSERT INTO diaries VALUES("It's funny.");
+INSERT INTO diaries VALUES("Happy birthday!");
+SHOW STATUS LIKE 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 0
+SELECT COUNT(*) FROM diaries WHERE MATCH(title) AGAINST("mroonga" IN BOOLEAN MODE);
+COUNT(*)
+1
+SHOW STATUS LIKE 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_disabled.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_disabled.result
new file mode 100644
index 00000000000..9971d21e386
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_disabled.result
@@ -0,0 +1,32 @@
+DROP TABLE IF EXISTS diaries;
+FLUSH STATUS;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, "はれ", "天気がよいのは今日までみたい。");
+SET mroonga_enable_optimization=FALSE;
+SELECT COUNT(*) FROM diaries
+WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE);
+COUNT(*)
+4
+SHOW STATUS LIKE 'mroonga_count_skip';
+Variable_name Value
+Mroonga_count_skip 0
+SET mroonga_enable_optimization=TRUE;
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_index_view.result b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_index_view.result
new file mode 100644
index 00000000000..407347a4fa2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/optimization_skip_count_index_view.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS diaries, users;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+user_id INT NOT NULL,
+title VARCHAR(45) NOT NULL,
+KEY (user_id),
+FULLTEXT INDEX title_index (title)
+) DEFAULT CHARSET=UTF8;
+CREATE TABLE users (
+id INT PRIMARY KEY AUTO_INCREMENT,
+name VARCHAR(45) NOT NULL,
+INDEX (name)
+) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
+INSERT INTO users (id, name) VALUES (1, "Alice"), (2, "Bob");
+INSERT INTO diaries (user_id, title) VALUES (1, "survey");
+INSERT INTO diaries (user_id, title) VALUES (2, "groonga (1)");
+INSERT INTO diaries (user_id, title) VALUES (2, "groonga (2)");
+CREATE VIEW articles AS
+SELECT diaries.user_id AS user_id,
+diaries.title AS title,
+users.name AS name
+FROM diaries, users
+WHERE diaries.user_id = users.id;
+SELECT COUNT(*) FROM articles WHERE name = 'Bob';
+COUNT(*)
+2
+DROP VIEW articles;
+DROP TABLE diaries, users;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/replace_geometry.result b/storage/mroonga/mysql-test/mroonga/storage/r/replace_geometry.result
new file mode 100644
index 00000000000..94bd5167dbc
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/replace_geometry.result
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS geo_replace;
+CREATE TABLE geo_replace (
+id INT NOT NULL,
+geo GEOMETRY NOT NULL,
+PRIMARY KEY(id)
+) DEFAULT CHARSET=utf8;
+INSERT INTO geo_replace VALUES(1, POINT(100,100));
+SELECT id, ASTEXT(geo) FROM geo_replace;
+id ASTEXT(geo)
+1 POINT(100 100)
+REPLACE INTO geo_replace VALUES(1, POINT(100,200));
+SELECT id, ASTEXT(geo) FROM geo_replace;
+id ASTEXT(geo)
+1 POINT(100 200)
+INSERT INTO geo_replace VALUES(1, POINT(200,200)) ON DUPLICATE KEY UPDATE geo = POINT(200,200);
+SELECT id, ASTEXT(geo) FROM geo_replace;
+id ASTEXT(geo)
+1 POINT(200 200)
+UPDATE geo_replace SET geo = POINT(200,300);
+SELECT id, ASTEXT(geo) FROM geo_replace;
+id ASTEXT(geo)
+1 POINT(200 300)
+DROP TABLE geo_replace;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/replace_select_varchar.result b/storage/mroonga/mysql-test/mroonga/storage/r/replace_select_varchar.result
new file mode 100644
index 00000000000..9cbe11c5574
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/replace_select_varchar.result
@@ -0,0 +1,39 @@
+DROP TABLE IF EXISTS videos_master, videos_groonga;
+CREATE TABLE `videos_master` (
+`id` bigint(1) unsigned NOT NULL,
+`video_id` varchar(64) NOT NULL,
+`description` text,
+`tags_unpack` text,
+PRIMARY KEY (`video_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+CREATE TABLE `videos_groonga` (
+`id` bigint(1) unsigned NOT NULL,
+`video_id` varchar(64) NOT NULL,
+`description` text,
+`tags_unpack` text,
+PRIMARY KEY (`video_id`),
+FULLTEXT INDEX (`description`),
+FULLTEXT INDEX (`tags_unpack`)
+) DEFAULT CHARSET=utf8;
+INSERT INTO videos_master VALUES (1, "video-1", "My Familly", "familly human");
+INSERT INTO videos_master VALUES (2, "video-2", "My Cat", "family cat");
+REPLACE INTO videos_groonga
+SELECT v.id, v.video_id, v.description, NULL
+FROM videos_master AS v
+WHERE v.video_id = (video_id);
+SELECT *, MATCH(description) AGAINST("cat") FROM videos_groonga
+WHERE MATCH(description) AGAINST("cat");
+id video_id description tags_unpack MATCH(description) AGAINST("cat")
+2 video-2 My Cat 1048577
+INSERT INTO videos_master VALUES (3, "video-3", "My Dog", "family dog");
+REPLACE INTO videos_groonga
+SELECT v.id, v.video_id, v.description, NULL
+FROM videos_master AS v
+WHERE v.video_id = (video_id);
+SELECT *, MATCH(description) AGAINST("my") FROM videos_groonga
+WHERE MATCH(description) AGAINST("my");
+id video_id description tags_unpack MATCH(description) AGAINST("my")
+1 video-1 My Familly 209716
+2 video-2 My Cat 209716
+3 video-3 My Dog 209716
+DROP TABLE videos_master, videos_groonga;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/replace_text.result b/storage/mroonga/mysql-test/mroonga/storage/r/replace_text.result
new file mode 100644
index 00000000000..f2cf9667ac7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/replace_text.result
@@ -0,0 +1,33 @@
+drop table if exists diaries;
+set names utf8;
+create table diaries (
+id int primary key,
+content text,
+fulltext index (content)
+) default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into diaries values(1, "今日からはじめました。");
+insert into diaries values(2, "明日の富士山の天気について");
+insert into diaries values(3, "今日も天気がよくてきれいに見える。");
+select * from diaries;
+id content
+1 今日からはじめました。
+2 明日の富士山の天気について
+3 今日も天気がよくてきれいに見える。
+select * from diaries where match(content) against("天気");
+id content
+2 明日の富士山の天気について
+3 今日も天気がよくてきれいに見える。
+replace into diaries values(2, "明日の天気は雨みたい。");
+select * from diaries where match(content) against("天気");
+id content
+2 明日の天気は雨みたい。
+3 今日も天気がよくてきれいに見える。
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/replace_varchar.result b/storage/mroonga/mysql-test/mroonga/storage/r/replace_varchar.result
new file mode 100644
index 00000000000..090ea9b84d6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/replace_varchar.result
@@ -0,0 +1,33 @@
+drop table if exists diaries;
+set names utf8;
+create table diaries (
+id int primary key,
+content varchar(256),
+fulltext index (content)
+) default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `content` varchar(256) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into diaries values(1, "今日からはじめました。");
+insert into diaries values(2, "明日の富士山の天気について");
+insert into diaries values(3, "今日も天気がよくてきれいに見える。");
+select * from diaries;
+id content
+1 今日からはじめました。
+2 明日の富士山の天気について
+3 今日も天気がよくてきれいに見える。
+select * from diaries where match(content) against("天気");
+id content
+2 明日の富士山の天気について
+3 今日も天気がよくてきれいに見える。
+replace into diaries values(2, "明日の天気は雨みたい。");
+select * from diaries where match(content) against("天気");
+id content
+2 明日の天気は雨みたい。
+3 今日も天気がよくてきれいに見える。
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/replace_vector.result b/storage/mroonga/mysql-test/mroonga/storage/r/replace_vector.result
new file mode 100644
index 00000000000..3e987631dba
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/replace_vector.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS vector_replace;
+DROP TABLE IF EXISTS vector_replace_vec;
+CREATE TABLE vector_replace_vec (
+vec CHAR(10) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+COMMENT='default_tokenizer "TokenDelimit"';
+CREATE TABLE vector_replace (
+id INT NOT NULL,
+vec TEXT COMMENT 'flags "COLUMN_VECTOR", type "vector_replace_vec"',
+PRIMARY KEY(id)
+) DEFAULT CHARSET=utf8;
+INSERT INTO vector_replace VALUES(1, 'first second third');
+SELECT id, vec FROM vector_replace;
+id vec
+1 FIRST SECOND THIRD
+REPLACE INTO vector_replace VALUES(1, 'fourth fifth');
+SELECT id, vec FROM vector_replace;
+id vec
+1 FOURTH FIFTH
+INSERT INTO vector_replace VALUES(1, 'sixth seventh') ON DUPLICATE KEY UPDATE vec = 'sixth seventh';
+SELECT id, vec FROM vector_replace;
+id vec
+1 SIXTH SEVENTH
+UPDATE vector_replace SET vec = 'eighth nineth tenth';
+SELECT id, vec FROM vector_replace;
+id vec
+1 EIGHTH NINETH TENTH
+DROP TABLE vector_replace;
+DROP TABLE vector_replace_vec;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/select_all.result b/storage/mroonga/mysql-test/mroonga/storage/r/select_all.result
new file mode 100644
index 00000000000..18318bdbc4e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/select_all.result
@@ -0,0 +1,198 @@
+drop table if exists t1, t2, t3;
+create table t1(c1 int, c2 int, c3 int);
+insert into t1 values (1, 10, 100);
+insert into t1 values (2, 30, 500);
+insert into t1 values (5, 20, 200);
+insert into t1 values (3, 60, 300);
+insert into t1 values (4, 50, 600);
+insert into t1 values (6, 40, 400);
+select * from t1;
+c1 c2 c3
+1 10 100
+2 30 500
+5 20 200
+3 60 300
+4 50 600
+6 40 400
+select c1 from t1;
+c1
+1
+2
+5
+3
+4
+6
+select c2 from t1;
+c2
+10
+30
+20
+60
+50
+40
+select c3 from t1;
+c3
+100
+500
+200
+300
+600
+400
+select * from t1 where c1 <= 3;
+c1 c2 c3
+1 10 100
+2 30 500
+3 60 300
+select * from t1 where c2 > 40;
+c1 c2 c3
+3 60 300
+4 50 600
+select * from t1 where c3 = 300;
+c1 c2 c3
+3 60 300
+select * from t1 order by c1;
+c1 c2 c3
+1 10 100
+2 30 500
+3 60 300
+4 50 600
+5 20 200
+6 40 400
+select * from t1 order by c2 desc;
+c1 c2 c3
+3 60 300
+4 50 600
+6 40 400
+2 30 500
+5 20 200
+1 10 100
+select * from t1 order by c3, c1;
+c1 c2 c3
+1 10 100
+5 20 200
+3 60 300
+6 40 400
+2 30 500
+4 50 600
+drop table t1;
+create table t1 (c1 int, c2 varchar(100));
+insert into t1 values(1, "hoge");
+insert into t1 values(4, "hogefuga");
+insert into t1 values(2, "fuga");
+insert into t1 values(5, "moge");
+insert into t1 values(3, "mo");
+select * from t1;
+c1 c2
+1 hoge
+4 hogefuga
+2 fuga
+5 moge
+3 mo
+select * from t1 order by c1;
+c1 c2
+1 hoge
+2 fuga
+3 mo
+4 hogefuga
+5 moge
+select * from t1 order by c1 desc;
+c1 c2
+5 moge
+4 hogefuga
+3 mo
+2 fuga
+1 hoge
+select * from t1 order by c2;
+c1 c2
+2 fuga
+1 hoge
+4 hogefuga
+3 mo
+5 moge
+drop table t1;
+create table t1 (c1 int, c2 text);
+insert into t1 values(1, "hoge");
+insert into t1 values(4, "hogefuga");
+insert into t1 values(2, "fuga");
+insert into t1 values(5, "moge");
+insert into t1 values(3, "mo");
+select * from t1;
+c1 c2
+1 hoge
+4 hogefuga
+2 fuga
+5 moge
+3 mo
+drop table t1;
+create table t1 (c1 int, c2 int, c3 text);
+insert into t1 values(1, 20, "hoge");
+insert into t1 values(4, 60, "hogefuga");
+insert into t1 values(2, 50, "fuga");
+insert into t1 values(5, 30, "moge");
+insert into t1 values(3, 40, "mo");
+select * from t1 order by c1 asc;
+c1 c2 c3
+1 20 hoge
+2 50 fuga
+3 40 mo
+4 60 hogefuga
+5 30 moge
+select * from t1 order by c1 desc;
+c1 c2 c3
+5 30 moge
+4 60 hogefuga
+3 40 mo
+2 50 fuga
+1 20 hoge
+select * from t1 order by c2 asc;
+c1 c2 c3
+1 20 hoge
+5 30 moge
+3 40 mo
+2 50 fuga
+4 60 hogefuga
+select * from t1 order by c2 desc;
+c1 c2 c3
+4 60 hogefuga
+2 50 fuga
+3 40 mo
+5 30 moge
+1 20 hoge
+select * from t1 order by c3 asc;
+c1 c2 c3
+2 50 fuga
+1 20 hoge
+4 60 hogefuga
+3 40 mo
+5 30 moge
+select * from t1 order by c3 desc;
+c1 c2 c3
+5 30 moge
+3 40 mo
+4 60 hogefuga
+1 20 hoge
+2 50 fuga
+drop table t1;
+create table t1 (_id int, c1 int);
+insert into t1 values (null,100);
+insert into t1 values (null,100);
+insert into t1 values (null,100);
+insert into t1 values (null,100);
+insert into t1 values (null,100);
+select * from t1;
+_id c1
+1 100
+2 100
+3 100
+4 100
+5 100
+select * from t1 where _id < 3;
+_id c1
+1 100
+2 100
+select * from t1 where _id >= 3;
+_id c1
+3 100
+4 100
+5 100
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/select_empty_key_where_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/select_empty_key_where_equal.result
new file mode 100644
index 00000000000..e0e3e21c620
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/select_empty_key_where_equal.result
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS tags;
+CREATE TABLE tags (
+name VARCHAR(16) NOT NULL,
+KEY index_name (name)
+);
+INSERT INTO tags VALUES ('mroonga');
+INSERT INTO tags VALUES ('mysql');
+INSERT INTO tags VALUES ('');
+SELECT * FROM tags WHERE name = "";
+name
+
+DROP TABLE tags;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/select_empty_key_where_not_equal.result b/storage/mroonga/mysql-test/mroonga/storage/r/select_empty_key_where_not_equal.result
new file mode 100644
index 00000000000..3732cb28184
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/select_empty_key_where_not_equal.result
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS tags;
+CREATE TABLE tags (
+name VARCHAR(16) NOT NULL,
+KEY index_name (name)
+);
+INSERT INTO tags VALUES ('mroonga');
+INSERT INTO tags VALUES ('mysql');
+INSERT INTO tags VALUES ('');
+SELECT * FROM tags WHERE name != "";
+name
+mroonga
+mysql
+DROP TABLE tags;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/select_group_by_with_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/select_group_by_with_index.result
new file mode 100644
index 00000000000..400156cec7b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/select_group_by_with_index.result
@@ -0,0 +1,15 @@
+DROP TABLE IF EXISTS users;
+SET NAMES utf8;
+CREATE TABLE users (
+name varchar(40),
+age int,
+KEY (age)
+);
+INSERT INTO users VALUES ("Alice", 20);
+INSERT INTO users VALUES ("Bob", 20);
+INSERT INTO users VALUES ("Charry", 29);
+SELECT *, COUNT(*) FROM users GROUP BY age;
+name age COUNT(*)
+Alice 20 2
+Charry 29 1
+DROP TABLE users;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/select_group_by_without_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/select_group_by_without_index.result
new file mode 100644
index 00000000000..04f63d0e779
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/select_group_by_without_index.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS users;
+SET NAMES utf8;
+CREATE TABLE users (
+name varchar(40),
+age int
+);
+INSERT INTO users VALUES ("Alice", 20);
+INSERT INTO users VALUES ("Bob", 20);
+INSERT INTO users VALUES ("Charry", 29);
+EXPLAIN SELECT *, COUNT(*) FROM users GROUP BY age;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE users ALL NULL NULL NULL NULL 3 Using temporary; Using filesort
+SELECT *, COUNT(*) FROM users GROUP BY age;
+name age COUNT(*)
+Alice 20 2
+Charry 29 1
+DROP TABLE users;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/select_pkey.result b/storage/mroonga/mysql-test/mroonga/storage/r/select_pkey.result
new file mode 100644
index 00000000000..99f69f49aba
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/select_pkey.result
@@ -0,0 +1,27 @@
+drop table if exists t1, t2, t3;
+create table t1(c1 int primary key, c2 int, c3 int);
+insert into t1 values (1, 10, 100);
+insert into t1 values (2, 30, 500);
+insert into t1 values (5, 20, 200);
+insert into t1 values (3, 60, 300);
+insert into t1 values (4, 50, 600);
+insert into t1 values (6, 40, 400);
+select * from t1 where c1=1;
+c1 c2 c3
+1 10 100
+select * from t1 where c1=2;
+c1 c2 c3
+2 30 500
+select * from t1 where c1=3;
+c1 c2 c3
+3 60 300
+select * from t1 where c1=4;
+c1 c2 c3
+4 50 600
+select * from t1 where c1=5;
+c1 c2 c3
+5 20 200
+select * from t1 where c1=6;
+c1 c2 c3
+6 40 400
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/select_secondary_key.result b/storage/mroonga/mysql-test/mroonga/storage/r/select_secondary_key.result
new file mode 100644
index 00000000000..1c24089ef71
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/select_secondary_key.result
@@ -0,0 +1,55 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 int, c3 text, key idx1(c2), fulltext index ft(c3));
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"sa si su se so");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ee oo");
+select * from t1;
+c1 c2 c3
+1 10 aa ii uu ee oo
+2 20 ka ki ku ke ko
+3 30 sa si su se so
+4 40 ta ti tu te to
+5 50 aa ii uu ee oo
+select * from t1 force index(idx1) where c2 = 30;
+c1 c2 c3
+3 30 sa si su se so
+select * from t1 force index(idx1) where c2 = 20;
+c1 c2 c3
+2 20 ka ki ku ke ko
+insert into t1 values(6,30,"aa bb cc dd ee");
+select * from t1;
+c1 c2 c3
+1 10 aa ii uu ee oo
+2 20 ka ki ku ke ko
+3 30 sa si su se so
+4 40 ta ti tu te to
+5 50 aa ii uu ee oo
+6 30 aa bb cc dd ee
+select * from t1 force index(idx1) where c2 = 30;
+c1 c2 c3
+3 30 sa si su se so
+6 30 aa bb cc dd ee
+drop table t1;
+create table t1 (c1 varchar(5) primary key, c2 varchar(5), c3 text, key idx1(c2), fulltext index ft(c3))engine=mroonga;
+insert into t1 values('ab','ijk',"aa ii uu ee oo");
+insert into t1 values('bc','ghi',"ka ki ku ke ko");
+insert into t1 values('cd','efg',"sa si su se so");
+insert into t1 values('de','cde',"ta ti tu te to");
+insert into t1 values('ef','abc',"aa ii uu ee oo");
+select * from t1 force index(idx1) where c2 < 'e' order by c1 asc;
+c1 c2 c3
+de cde ta ti tu te to
+ef abc aa ii uu ee oo
+select * from t1 force index(idx1) where c2 > 'e' order by c1 asc;
+c1 c2 c3
+ab ijk aa ii uu ee oo
+bc ghi ka ki ku ke ko
+cd efg sa si su se so
+select * from t1 force index(idx1) where c2 between 'c' and 'h' order by c1 asc;
+c1 c2 c3
+bc ghi ka ki ku ke ko
+cd efg sa si su se so
+de cde ta ti tu te to
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/show_create_table_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/storage/r/show_create_table_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..ef87703e716
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/show_create_table_TODO_SPLIT_ME.result
@@ -0,0 +1,25 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `c1` int(11) DEFAULT NULL
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+drop table t1;
+create table t1 (c1 int, c2 int);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `c1` int(11) DEFAULT NULL,
+ `c2` int(11) DEFAULT NULL
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+drop table t1;
+create table t1 (c1 int primary key, c2 varchar(100));
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `c1` int(11) NOT NULL,
+ `c2` varchar(100) DEFAULT NULL,
+ PRIMARY KEY (`c1`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/sub_query_fulltext.result b/storage/mroonga/mysql-test/mroonga/storage/r/sub_query_fulltext.result
new file mode 100644
index 00000000000..4f24d0d74b0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/sub_query_fulltext.result
@@ -0,0 +1,44 @@
+DROP TABLE IF EXISTS diaries, users;
+CREATE TABLE users (
+id INT PRIMARY KEY AUTO_INCREMENT,
+name TEXT
+) DEFAULT CHARSET UTF8;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+user_id INT UNSIGNED NOT NULL,
+title TEXT,
+FULLTEXT INDEX (title)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `user_id` int(10) unsigned NOT NULL,
+ `title` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO users (name) VALUES ("alice");
+INSERT INTO users (name) VALUES ("bob");
+INSERT INTO users (name) VALUES ("carlos");
+SELECT * FROM users;
+id name
+1 alice
+2 bob
+3 carlos
+INSERT INTO diaries (user_id, title) VALUES (1, "Hello!");
+INSERT INTO diaries (user_id, title) VALUES (2, "my name is bob");
+INSERT INTO diaries (user_id, title) VALUES (3, "my name is carlos");
+SELECT * FROM diaries;
+id user_id title
+1 1 Hello!
+2 2 my name is bob
+3 3 my name is carlos
+SELECT * FROM users
+WHERE id IN (SELECT user_id FROM diaries
+WHERE MATCH(title) AGAINST("name"))
+ORDER BY id DESC;
+id name
+3 carlos
+2 bob
+DROP TABLE diaries, users;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/temporary_table.result b/storage/mroonga/mysql-test/mroonga/storage/r/temporary_table.result
new file mode 100644
index 00000000000..f6c036bbf9f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/temporary_table.result
@@ -0,0 +1,21 @@
+DROP TEMPORARY TABLE IF EXISTS diaries;
+CREATE TEMPORARY TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TEMPORARY TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title) VALUES ("clear day");
+INSERT INTO diaries (title) VALUES ("rainy day");
+INSERT INTO diaries (title) VALUES ("cloudy day");
+SELECT * FROM diaries;
+id title
+1 clear day
+2 rainy day
+3 cloudy day
+DROP TEMPORARY TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/truncate.result b/storage/mroonga/mysql-test/mroonga/storage/r/truncate.result
new file mode 100644
index 00000000000..0e33976eb17
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/truncate.result
@@ -0,0 +1,55 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+SELECT * FROM diaries;
+id year month day title content
+1 2011 11 9 Hello 今日からはじめました。
+2 2011 11 10 天気 明日の富士山の天気について
+3 2011 11 11 富士山 今日も天気がよくてきれいに見える。
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE);
+id year month day title content
+1 2011 11 9 Hello 今日からはじめました。
+3 2011 11 11 富士山 今日も天気がよくてきれいに見える。
+2 2011 11 10 天気 明日の富士山の天気について
+TRUNCATE TABLE diaries;
+SELECT * FROM diaries;
+id year month day title content
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE);
+id year month day title content
+INSERT INTO diaries VALUES(1, 2011, 11, 11, "帰り道", "つかれたー");
+INSERT INTO diaries VALUES(2, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(3, 2011, 12, 2, "初雪", "今年はじめての雪!");
+SELECT * FROM diaries;
+id year month day title content
+1 2011 11 11 帰り道 つかれたー
+2 2011 12 1 久しぶり 天気が悪いからずっと留守番。
+3 2011 12 2 初雪 今年はじめての雪!
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("悪い" IN BOOLEAN MODE);
+id year month day title content
+2 2011 12 1 久しぶり 天気が悪いからずっと留守番。
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/update_fulltext.result b/storage/mroonga/mysql-test/mroonga/storage/r/update_fulltext.result
new file mode 100644
index 00000000000..bf81d5e0a8d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/update_fulltext.result
@@ -0,0 +1,22 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 text, fulltext index (c2));
+insert into t1 values(10, "aa ii uu ee");
+insert into t1 values(20, "ka ki ku ke");
+insert into t1 values(30, "sa si su se");
+select * from t1;
+c1 c2
+10 aa ii uu ee
+20 ka ki ku ke
+30 sa si su se
+update t1 set c2="ta ti tu te" where c1=20;
+select * from t1;
+c1 c2
+10 aa ii uu ee
+20 ta ti tu te
+30 sa si su se
+select * from t1 where match(c2) against("ti");
+c1 c2
+20 ta ti tu te
+select * from t1 where match(c2) against("ki");
+c1 c2
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/update_id_hash_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/update_id_hash_index.result
new file mode 100644
index 00000000000..35d8843afbe
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/update_id_hash_index.result
@@ -0,0 +1,20 @@
+drop table if exists t1, t2, t3;
+create table t1 (_id int, c1 int, key (_id) using hash);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+select * from t1;
+_id c1
+1 100
+2 100
+3 100
+4 100
+update t1 set c1 = 200 where _id = 2;
+select * from t1;
+_id c1
+1 100
+2 200
+3 100
+4 100
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/update_id_unique_hash_index.result b/storage/mroonga/mysql-test/mroonga/storage/r/update_id_unique_hash_index.result
new file mode 100644
index 00000000000..dba9c964270
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/update_id_unique_hash_index.result
@@ -0,0 +1,20 @@
+drop table if exists t1, t2, t3;
+create table t1 (_id int, c1 int, unique key (_id) using hash);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+select * from t1;
+_id c1
+1 100
+2 100
+3 100
+4 100
+update t1 set c1 = 200 where _id = 2;
+select * from t1;
+_id c1
+1 100
+2 200
+3 100
+4 100
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/update_int.result b/storage/mroonga/mysql-test/mroonga/storage/r/update_int.result
new file mode 100644
index 00000000000..e022fa237bb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/update_int.result
@@ -0,0 +1,42 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int, c2 int);
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `c1` int(11) DEFAULT NULL,
+ `c2` int(11) DEFAULT NULL
+) ENGINE=Mroonga DEFAULT CHARSET=latin1
+insert into t1 values (1, 100);
+insert into t1 values (2, 101);
+insert into t1 values (3, 102);
+select * from t1;
+c1 c2
+1 100
+2 101
+3 102
+update t1 set c2=c2+100 where c1=1;
+select * from t1;
+c1 c2
+1 200
+2 101
+3 102
+update t1 set c2=c2+100 where c1=2;
+select * from t1;
+c1 c2
+1 200
+2 201
+3 102
+update t1 set c2=c2+100 where c1=3;
+select * from t1;
+c1 c2
+1 200
+2 201
+3 202
+flush tables;
+update t1 set c1=5, c2=50;
+select * from t1;
+c1 c2
+5 50
+5 50
+5 50
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/update_last_insert_grn_id.result b/storage/mroonga/mysql-test/mroonga/storage/r/update_last_insert_grn_id.result
new file mode 100644
index 00000000000..af5926313c1
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/update_last_insert_grn_id.result
@@ -0,0 +1,29 @@
+drop table if exists memos;
+create table memos (
+_id int,
+content varchar(255),
+unique key (_id) using hash
+);
+insert into memos values (null, "今夜はさんま。");
+insert into memos values (null, "明日はgroongaをアップデート。");
+insert into memos values (null, "帰りにおだんご。");
+insert into memos values (null, "金曜日は肉の日。");
+select * from memos;
+_id content
+1 今夜はさんま。
+2 明日はgroongaをアップデート。
+3 帰りにおだんご。
+4 金曜日は肉の日。
+insert into memos values (null, "冷蔵庫に牛乳が残り1本。");
+select last_insert_grn_id();
+last_insert_grn_id()
+5
+update memos set content = "冷蔵庫に牛乳はまだたくさんある。" where _id = last_insert_grn_id();
+select * from memos;
+_id content
+1 今夜はさんま。
+2 明日はgroongaをアップデート。
+3 帰りにおだんご。
+4 金曜日は肉の日。
+5 冷蔵庫に牛乳はまだたくさんある。
+drop table memos;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/update_virtual_column.result b/storage/mroonga/mysql-test/mroonga/storage/r/update_virtual_column.result
new file mode 100644
index 00000000000..11e6ee21949
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/update_virtual_column.result
@@ -0,0 +1,28 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int, _id int);
+insert into t1 values(1,null);
+insert into t1 values(2,null);
+insert into t1 values(3,null);
+select * from t1;
+c1 _id
+1 1
+2 2
+3 3
+set sql_mode="";
+update t1 set _id = 10 where c1 = 1;
+Warnings:
+Warning 1265 Data truncated for column '_id' at row 1
+select * from t1;
+c1 _id
+1 1
+2 2
+3 3
+set sql_mode="strict_all_tables";
+update t1 set _id = 11 where c1 = 1;
+ERROR 01000: Data truncated for column '_id' at row 1
+select * from t1;
+c1 _id
+1 1
+2 2
+3 3
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_database_path_prefix.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_database_path_prefix.result
new file mode 100644
index 00000000000..4536dded9d7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_database_path_prefix.result
@@ -0,0 +1,17 @@
+SET GLOBAL mroonga_database_path_prefix = "test/mroonga.data/";
+SHOW GLOBAL VARIABLES LIKE 'mroonga_database_path_prefix';
+Variable_name Value
+mroonga_database_path_prefix test/mroonga.data/
+CREATE DATABASE clean_test;
+USE clean_test;
+CREATE TABLE counts (
+id INT PRIMARY KEY AUTO_INCREMENT
+);
+INSERT INTO counts VALUES (NULL);
+SELECT * FROM counts;
+id
+1
+DROP TABLE counts;
+DROP DATABASE clean_test;
+USE test;
+SET GLOBAL mroonga_database_path_prefix = NULL;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_default_parser_new_value.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_default_parser_new_value.result
new file mode 100644
index 00000000000..959383ee6d9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_default_parser_new_value.result
@@ -0,0 +1,6 @@
+SET @mroonga_default_parser_backup = @@mroonga_default_parser;
+SET GLOBAL mroonga_default_parser = "TokenBigramSplitAlpha";
+SHOW GLOBAL VARIABLES LIKE 'mroonga_default_parser';
+Variable_name Value
+mroonga_default_parser TokenBigramSplitAlpha
+SET GLOBAL mroonga_default_parser = @mroonga_default_parser_backup;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_default_parser_same_value.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_default_parser_same_value.result
new file mode 100644
index 00000000000..7f441b4b86d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_default_parser_same_value.result
@@ -0,0 +1,4 @@
+SET GLOBAL mroonga_default_parser = "TokenBigram";
+SHOW GLOBAL VARIABLES LIKE 'mroonga_default_parser';
+Variable_name Value
+mroonga_default_parser TokenBigram
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_dry_write_delete.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_dry_write_delete.result
new file mode 100644
index 00000000000..6e690f45e61
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_dry_write_delete.result
@@ -0,0 +1,28 @@
+drop table if exists diaries;
+create table diaries (
+id int primary key auto_increment,
+body text,
+fulltext index body_index (body)
+) default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into diaries (body) values ("will start groonga!");
+select * from diaries;
+id body
+1 will start groonga!
+set mroonga_dry_write=true;
+delete from diaries where id = 1;
+select * from diaries;
+id body
+1 will start groonga!
+set mroonga_dry_write=false;
+delete from diaries where id = 1;
+select * from diaries;
+id body
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_dry_write_insert.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_dry_write_insert.result
new file mode 100644
index 00000000000..8de55efaccc
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_dry_write_insert.result
@@ -0,0 +1,30 @@
+drop table if exists diaries;
+create table diaries (
+id int primary key auto_increment,
+body text,
+fulltext index body_index (body)
+) default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into diaries (body) values ("will start groonga!");
+select * from diaries;
+id body
+1 will start groonga!
+set mroonga_dry_write=true;
+insert into diaries (body) values ("starting groonga...");
+select * from diaries;
+id body
+1 will start groonga!
+set mroonga_dry_write=false;
+insert into diaries (body) values ("started groonga.");
+select * from diaries;
+id body
+1 will start groonga!
+2 started groonga.
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_dry_write_update.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_dry_write_update.result
new file mode 100644
index 00000000000..dd3ee5b333c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_dry_write_update.result
@@ -0,0 +1,26 @@
+drop table if exists diaries;
+create table diaries (
+id int primary key auto_increment,
+body text,
+fulltext index body_index (body)
+) default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+insert into diaries (body) values ("will start groonga!");
+set mroonga_dry_write=true;
+update diaries set body = "starting groonga..." where id = 1;
+select * from diaries;
+id body
+1 will start groonga!
+set mroonga_dry_write=false;
+update diaries set body = "starting groonga..." where id = 1;
+select * from diaries;
+id body
+1 starting groonga...
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_disable.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_disable.result
new file mode 100644
index 00000000000..789316d83ca
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_disable.result
@@ -0,0 +1,4 @@
+SET GLOBAL mroonga_lock_timeout = -1;
+SHOW GLOBAL VARIABLES LIKE "mroonga_lock_timeout";
+Variable_name Value
+mroonga_lock_timeout -1
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_invalid.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_invalid.result
new file mode 100644
index 00000000000..029b1ab17f7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_invalid.result
@@ -0,0 +1,6 @@
+SET GLOBAL mroonga_lock_timeout = -2;
+Warnings:
+Warning 1292 Truncated incorrect mroonga_lock_timeout value: '-2'
+SHOW GLOBAL VARIABLES LIKE "mroonga_lock_timeout";
+Variable_name Value
+mroonga_lock_timeout -1
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_no_retry.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_no_retry.result
new file mode 100644
index 00000000000..f47a2e6625b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_no_retry.result
@@ -0,0 +1,4 @@
+SET GLOBAL mroonga_lock_timeout = 0;
+SHOW GLOBAL VARIABLES LIKE "mroonga_lock_timeout";
+Variable_name Value
+mroonga_lock_timeout 0
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_valid.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_valid.result
new file mode 100644
index 00000000000..6ec1004f752
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_lock_timeout_valid.result
@@ -0,0 +1,4 @@
+SET GLOBAL mroonga_lock_timeout = 1000;
+SHOW GLOBAL VARIABLES LIKE "mroonga_lock_timeout";
+Variable_name Value
+mroonga_lock_timeout 1000
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_log_file_new_value.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_log_file_new_value.result
new file mode 100644
index 00000000000..db4694797b2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_log_file_new_value.result
@@ -0,0 +1,6 @@
+SET @mroonga_log_file_backup = @@mroonga_log_file;
+SET GLOBAL mroonga_log_file = "new-mroonga.log";
+SHOW GLOBAL VARIABLES LIKE 'mroonga_log_file';
+Variable_name Value
+mroonga_log_file new-mroonga.log
+SET GLOBAL mroonga_log_file = @mroonga_log_file_backup;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_log_file_nonexistent_path.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_log_file_nonexistent_path.result
new file mode 100644
index 00000000000..1dbec1f850e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_log_file_nonexistent_path.result
@@ -0,0 +1,4 @@
+SET GLOBAL mroonga_log_file = "nonexistent/mroonga.log";
+SHOW GLOBAL VARIABLES LIKE 'mroonga_log_file';
+Variable_name Value
+mroonga_log_file groonga.log
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_log_file_same_value.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_log_file_same_value.result
new file mode 100644
index 00000000000..5824f09053e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_log_file_same_value.result
@@ -0,0 +1,4 @@
+SET GLOBAL mroonga_log_file = "groonga.log";
+SHOW GLOBAL VARIABLES LIKE 'mroonga_log_file';
+Variable_name Value
+mroonga_log_file groonga.log
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_log_level_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_log_level_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..cb646eceaf3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_log_level_TODO_SPLIT_ME.result
@@ -0,0 +1,49 @@
+set @mroonga_log_level_backup=@@mroonga_log_level;
+show global variables like 'mroonga_log_level';
+Variable_name Value
+mroonga_log_level NOTICE
+set global mroonga_log_level=NONE;
+show global variables like 'mroonga_log_level';
+Variable_name Value
+mroonga_log_level NONE
+set global mroonga_log_level=EMERG;
+show global variables like 'mroonga_log_level';
+Variable_name Value
+mroonga_log_level EMERG
+set global mroonga_log_level=ALERT;
+show global variables like 'mroonga_log_level';
+Variable_name Value
+mroonga_log_level ALERT
+set global mroonga_log_level=CRIT;
+show global variables like 'mroonga_log_level';
+Variable_name Value
+mroonga_log_level CRIT
+set global mroonga_log_level=ERROR;
+show global variables like 'mroonga_log_level';
+Variable_name Value
+mroonga_log_level ERROR
+set global mroonga_log_level=WARNING;
+show global variables like 'mroonga_log_level';
+Variable_name Value
+mroonga_log_level WARNING
+set global mroonga_log_level=NOTICE;
+show global variables like 'mroonga_log_level';
+Variable_name Value
+mroonga_log_level NOTICE
+set global mroonga_log_level=INFO;
+show global variables like 'mroonga_log_level';
+Variable_name Value
+mroonga_log_level INFO
+set global mroonga_log_level=DEBUG;
+show global variables like 'mroonga_log_level';
+Variable_name Value
+mroonga_log_level DEBUG
+set global mroonga_log_level=DUMP;
+show global variables like 'mroonga_log_level';
+Variable_name Value
+mroonga_log_level DUMP
+set global mroonga_log_level=dummy;
+ERROR 42000: Variable 'mroonga_log_level' can't be set to the value of 'dummy'
+set session mroonga_log_level=NOTICE;
+ERROR HY000: Variable 'mroonga_log_level' is a GLOBAL variable and should be set with SET GLOBAL
+set global mroonga_log_level=@mroonga_log_level_backup;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_match_escalation_threshold_global.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_match_escalation_threshold_global.result
new file mode 100644
index 00000000000..04b22c22ea6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_match_escalation_threshold_global.result
@@ -0,0 +1,33 @@
+DROP TABLE IF EXISTS diaries;
+SET GLOBAL mroonga_match_escalation_threshold = -1;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+tags TEXT,
+FULLTEXT INDEX tags_index (tags) COMMENT 'parser "TokenDelimit"'
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `tags` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `tags_index` (`tags`) COMMENT 'parser "TokenDelimit"'
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, tags) VALUES ("Hello groonga!", "groonga install");
+INSERT INTO diaries (title, tags) VALUES ("Hello mroonga!", "mroonga install");
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("install" IN BOOLEAN MODE);
+id title tags
+1 Hello groonga! groonga install
+2 Hello mroonga! mroonga install
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+id title tags
+SET GLOBAL mroonga_match_escalation_threshold = 0;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+id title tags
+SET mroonga_match_escalation_threshold = 0;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+id title tags
+1 Hello groonga! groonga install
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_match_escalation_threshold_session.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_match_escalation_threshold_session.result
new file mode 100644
index 00000000000..25a1aba61e2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_match_escalation_threshold_session.result
@@ -0,0 +1,33 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+tags TEXT,
+FULLTEXT INDEX tags_index (tags) COMMENT 'parser "TokenDelimit"'
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `tags` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `tags_index` (`tags`) COMMENT 'parser "TokenDelimit"'
+) ENGINE=Mroonga DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, tags) VALUES ("Hello groonga!", "groonga install");
+INSERT INTO diaries (title, tags) VALUES ("Hello mroonga!", "mroonga install");
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("install" IN BOOLEAN MODE);
+id title tags
+1 Hello groonga! groonga install
+2 Hello mroonga! mroonga install
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+id title tags
+1 Hello groonga! groonga install
+SET mroonga_match_escalation_threshold = -1;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+id title tags
+SET mroonga_match_escalation_threshold = 0;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+id title tags
+1 Hello groonga! groonga install
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_vector_column_delimiter.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_vector_column_delimiter.result
new file mode 100644
index 00000000000..f3c8f1e8628
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_vector_column_delimiter.result
@@ -0,0 +1,29 @@
+DROP TABLE IF EXISTS document;
+DROP TABLE IF EXISTS category;
+CREATE TABLE category (
+category CHAR(10) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+COMMENT='default_tokenizer "TokenDelimit"';
+CREATE TABLE document (
+id INT NOT NULL,
+title TEXT,
+categories TEXT COMMENT 'flags "COLUMN_VECTOR", type "category"',
+PRIMARY KEY(id)
+) DEFAULT CHARSET=utf8;
+SHOW GLOBAL VARIABLES LIKE 'mroonga_vector_column_delimiter';
+Variable_name Value
+mroonga_vector_column_delimiter
+INSERT INTO document VALUES(1, "Mroonga is the fastest search engine", "it database fulltext");
+SELECT id, title, categories FROM document;
+id title categories
+1 Mroonga is the fastest search engine IT DATABASE FULLTEXT
+SET GLOBAL mroonga_vector_column_delimiter = ';';
+SHOW GLOBAL VARIABLES LIKE 'mroonga_vector_column_delimiter';
+Variable_name Value
+mroonga_vector_column_delimiter ;
+SELECT id, title, categories FROM document;
+id title categories
+1 Mroonga is the fastest search engine IT;DATABASE;FULLTEXT
+DROP TABLE document;
+DROP TABLE category;
+SET GLOBAL mroonga_vector_column_delimiter = ' ';
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_version.result b/storage/mroonga/mysql-test/mroonga/storage/r/variable_version.result
new file mode 100644
index 00000000000..4c22040ab5a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_version.result
@@ -0,0 +1,3 @@
+show variables like 'mroonga_version';
+Variable_name Value
+mroonga_version 4.05
diff --git a/storage/mroonga/mysql-test/mroonga/storage/r/variable_version.result.in b/storage/mroonga/mysql-test/mroonga/storage/r/variable_version.result.in
new file mode 100644
index 00000000000..26ff300a759
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/r/variable_version.result.in
@@ -0,0 +1,3 @@
+show variables like 'mroonga_version';
+Variable_name Value
+mroonga_version @MRN_VERSION@
diff --git a/storage/mroonga/mysql-test/mroonga/storage/suite.opt b/storage/mroonga/mysql-test/mroonga/storage/suite.opt
new file mode 100644
index 00000000000..d5a1e5190a7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/suite.opt
@@ -0,0 +1 @@
+--loose-plugin-load-add=$HA_MROONGA_SO --loose-plugin-mroonga=ON
diff --git a/storage/mroonga/mysql-test/mroonga/storage/suite.pm b/storage/mroonga/mysql-test/mroonga/storage/suite.pm
new file mode 100644
index 00000000000..528ccc5d693
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/suite.pm
@@ -0,0 +1,23 @@
+package My::Suite::Mroonga;
+
+@ISA = qw(My::Suite);
+
+return "No Mroonga engine" unless $ENV{HA_MROONGA_SO} or
+ $::mysqld_variables{'mroonga'} eq "ON";
+
+sub is_default { 1 }
+
+my $groonga_normalizer_mysql_dir=$::basedir . '/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql';
+my $groonga_normalizer_mysql_install_dir=$::basedir . '/lib/groonga/plugins';
+
+if (-d $groonga_normalizer_mysql_dir)
+{
+ $ENV{GRN_PLUGINS_DIR}=$groonga_normalizer_mysql_dir;
+}
+elsif (-d $groonga_normalizer_mysql_install_dir)
+{
+ $ENV{GRN_PLUGINS_DIR}=$groonga_normalizer_mysql_install_dir;
+}
+
+bless { };
+
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_after.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_after.test
new file mode 100644
index 00000000000..e70dcb92e12
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_after.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2014 Naoya Murakami <naoya@createfield.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+ALTER TABLE diaries ADD title TEXT AFTER id;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_first.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_first.test
new file mode 100644
index 00000000000..dac06ff6719
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_first.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2014 Naoya Murakami <naoya@createfield.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+ALTER TABLE diaries ADD title TEXT FIRST;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_multiple.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_multiple.test
new file mode 100644
index 00000000000..ebf8ac5d581
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_multiple.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title) VALUES ("survey");
+SELECT * FROM diaries;
+
+ALTER TABLE diaries
+ ADD COLUMN body TEXT FIRST,
+ ADD COLUMN published BOOLEAN AFTER id,
+ ADD COLUMN created_at DATETIME;
+UPDATE diaries SET body = "will start groonga!";
+SELECT * FROM diaries;
+
+INSERT INTO diaries (title, body, published, created_at)
+ VALUES ("groonga (1)", "starting groonga...", TRUE, "2014-2-9 02:09:00");
+INSERT INTO diaries (title, body, published, created_at)
+ VALUES ("groonga (2)", "started groonga.", FALSE, "2014-2-9 12:19:00");
+SELECT * FROM diaries;
+
+SHOW CREATE TABLE diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_plain.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_plain.test
new file mode 100644
index 00000000000..01f13799c02
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_plain.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title) VALUES ("survey");
+SELECT * FROM diaries;
+
+ALTER TABLE diaries ADD COLUMN body TEXT;
+UPDATE diaries SET body = "will start groonga!";
+SELECT * FROM diaries;
+
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) values ("groonga (2)", "started groonga.");
+SELECT * FROM diaries;
+
+SHOW CREATE TABLE diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_with_flags.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_with_flags.test
new file mode 100644
index 00000000000..c93ae13a19e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_with_flags.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_query_log
+DROP DATABASE test;
+CREATE DATABASE test;
+USE test;
+--enable_query_log
+
+CREATE TABLE tags (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
+) DEFAULT CHARSET=utf8;
+
+ALTER TABLE tags ADD COLUMN name VARCHAR(64) COMMENT 'flags "COLUMN_VECTOR"';
+
+SELECT mroonga_command("dump");
+
+DROP TABLE tags;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_with_type.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_with_type.test
new file mode 100644
index 00000000000..2f39d52777a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_column_with_type.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_query_log
+DROP DATABASE test;
+CREATE DATABASE test;
+USE test;
+--enable_query_log
+
+CREATE TABLE tags (
+ id INT UNSIGNED PRIMARY KEY
+) DEFAULT CHARSET=utf8;
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY
+) DEFAULT CHARSET=utf8;
+
+ALTER TABLE bugs ADD COLUMN name VARCHAR(64) COMMENT 'type "tags"';
+
+SELECT mroonga_command("dump");
+
+DROP TABLE bugs;
+DROP TABLE tags;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_key_multiple_column_with_data.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_key_multiple_column_with_data.test
new file mode 100644
index 00000000000..ba06e55e1ba
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_add_key_multiple_column_with_data.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS scores;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE scores (
+ id BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL,
+ name CHAR(30) NOT NULL,
+ score INT NOT NULL
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE scores;
+
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 29);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", -12);
+INSERT INTO scores (name, score) VALUES("Jiro Yamada", 27);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 10);
+SELECT * FROM scores
+ WHERE name = "Taro Yamada" AND (score >= -12 AND score < 29);
+
+ALTER TABLE scores ADD KEY property (name, score);
+SELECT * FROM scores
+ WHERE name = "Taro Yamada" AND (score >= -12 AND score < 29);
+
+DROP TABLE scores;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_comment_not_for_mroonga.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_comment_not_for_mroonga.test
new file mode 100644
index 00000000000..7b9fc783244
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_comment_not_for_mroonga.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS bugs;
+--enable_warnings
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tag VARCHAR(64)
+) DEFAULT CHARSET=utf8;
+
+ALTER TABLE bugs
+ CHANGE COLUMN
+ tag
+ tag VARCHAR(64) COMMENT 'It must consist of only alphabet and number.';
+
+SHOW CREATE TABLE bugs;
+
+DROP TABLE bugs;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_have_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_have_index.test
new file mode 100644
index 00000000000..3a86d23facf
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_have_index.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS bugs;
+--enable_warnings
+
+CREATE TABLE bugs (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ title VARCHAR(32),
+ FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+
+ALTER TABLE bugs CHANGE COLUMN title title VARCHAR(64);
+
+SHOW CREATE TABLE bugs;
+
+DROP TABLE bugs;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_after.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_after.test
new file mode 100644
index 00000000000..16e98a9769e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_after.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+ALTER TABLE diaries CHANGE body description TEXT AFTER id;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, description) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_first.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_first.test
new file mode 100644
index 00000000000..86c94afd281
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_first.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+ALTER TABLE diaries CHANGE body description TEXT FIRST;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, description) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_multiple.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_multiple.test
new file mode 100644
index 00000000000..a9bbe403da1
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_multiple.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+ALTER TABLE diaries
+ CHANGE body description TEXT FIRST,
+ CHANGE title subject TEXT AFTER internal_id,
+ CHANGE id internal_id INT;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (subject, description)
+ VALUES ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_no_order.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_no_order.test
new file mode 100644
index 00000000000..12ba5606b25
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_column_rename_no_order.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2014 Naoya Murakami <naoya@createfield.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+ALTER TABLE diaries CHANGE body description TEXT;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, description) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_engine.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_engine.test
new file mode 100644
index 00000000000..867c6722d36
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_change_engine.test
@@ -0,0 +1,54 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT,
+ FULLTEXT INDEX title_index (title),
+ FULLTEXT INDEX body_index (body)
+) ENGINE MyISAM DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE) AND
+ MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+
+ALTER TABLE diaries ENGINE = mroonga;
+SHOW CREATE TABLE diaries;
+
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE) AND
+ MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("groonga" IN BOOLEAN MODE) AND
+ MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_create_fulltext.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_create_fulltext.test
new file mode 100644
index 00000000000..06cdb13ba16
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_create_fulltext.test
@@ -0,0 +1,55 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT *
+ FROM diaries
+ WHERE MATCH (title) AGAINST ("富士山");
+
+CREATE FULLTEXT INDEX title_index on diaries (title);
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+ALTER TABLE diaries DISABLE KEYS;
+
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_fulltext_ujis.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_fulltext_ujis.test
new file mode 100644
index 00000000000..d6e32e0f004
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_fulltext_ujis.test
@@ -0,0 +1,49 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES ujis;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ FULLTEXT KEY title_index (title)
+) DEFAULT CHARSET=ujis;
+
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "ŷ");
+INSERT INTO diaries VALUES (3, "ٻλ");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("ٻλ");
+
+ALTER TABLE diaries DISABLE KEYS;
+
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("ٻλ");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_fulltext_utf8.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_fulltext_utf8.test
new file mode 100644
index 00000000000..fb89d678b75
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_fulltext_utf8.test
@@ -0,0 +1,49 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ FULLTEXT KEY title_index (title)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+ALTER TABLE diaries DISABLE KEYS;
+
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_multiple_column.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_multiple_column.test
new file mode 100644
index 00000000000..2112828684a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_multiple_column.test
@@ -0,0 +1,51 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ created_at datetime,
+ KEY title_and_created_at_index (title, created_at)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_and_created_at_index)
+ WHERE title = "天気" AND
+ created_at = "2012-04-30 23:00:00";
+
+ALTER TABLE diaries DISABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_and_created_at_index)
+ WHERE title = "天気" AND
+ created_at = "2012-04-30 23:00:00";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_normal.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_normal.test
new file mode 100644
index 00000000000..f599767ab8f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_normal.test
@@ -0,0 +1,49 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ created_at datetime,
+ KEY created_at_index (created_at)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (created_at_index)
+ WHERE created_at = "2012-04-30 20:00:00";
+
+ALTER TABLE diaries DISABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (created_at_index)
+ WHERE created_at = "2012-04-30 20:00:00";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_primary.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_primary.test
new file mode 100644
index 00000000000..4bc5fb1f643
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_primary.test
@@ -0,0 +1,47 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (PRIMARY)
+ WHERE id = 2;
+
+ALTER TABLE diaries DISABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (PRIMARY)
+ WHERE id = 2;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_truncate.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_truncate.test
new file mode 100644
index 00000000000..ab990a549b1
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_truncate.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kentoku SHIBA
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS users;
+--enable_warnings
+
+SET NAMES utf8;
+
+CREATE TABLE users (
+ first_name VARCHAR(32) NOT NULL,
+ last_name VARCHAR(32) NOT NULL,
+ KEY (first_name, last_name)
+);
+
+INSERT INTO users VALUES("Taro", "Yamada");
+INSERT INTO users VALUES("Hanako", "Tanaka");
+INSERT INTO users VALUES("Joe", "Honda");
+
+SELECT * FROM users;
+
+ALTER TABLE users DISABLE KEYS;
+TRUNCATE users;
+
+SELECT * FROM users;
+
+DROP TABLE users;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_updating.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_updating.test
new file mode 100644
index 00000000000..b172ff303c1
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_disable_keys_updating.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+CREATE TABLE t1 (
+ c1 int NOT NULL,
+ c2 text NOT NULL,
+ c3 int NOT NULL,
+ c4 int NOT NULL,
+ PRIMARY KEY(c1),
+ KEY idx1(c3,c4),
+ FULLTEXT KEY ft1(c2)
+);
+INSERT INTO t1 VALUES(1, 'test1', 1, 1);
+INSERT INTO t1 VALUES(2, 'test2', 2, 2);
+INSERT INTO t1 VALUES(3, 'test3', 1, 3);
+ALTER TABLE t1 DISABLE KEYS;
+DELETE FROM t1 WHERE c1 = 2;
+UPDATE t1 SET c4 = 4 WHERE c1 = 1;
+INSERT INTO t1 VALUES(4, 'test4', 2, 4);
+DROP TABLE t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_drop_column_multiple.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_drop_column_multiple.test
new file mode 100644
index 00000000000..549a5540450
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_drop_column_multiple.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+SELECT * FROM diaries;
+
+ALTER TABLE diaries
+ DROP COLUMN title,
+ DROP COLUMN body;
+SHOW CREATE TABLE diaries;
+SELECT * FROM diaries;
+
+INSERT INTO diaries () VALUES ();
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_drop_column_one.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_drop_column_one.test
new file mode 100644
index 00000000000..1e7248125cf
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_drop_column_one.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+SELECT * FROM diaries;
+
+ALTER TABLE diaries DROP COLUMN body;
+SHOW CREATE TABLE diaries;
+SELECT * FROM diaries;
+
+INSERT INTO diaries (title) values ("groonga (1)");
+INSERT INTO diaries (title) values ("groonga (2)");
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_drop_key_multiple_column_with_data.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_drop_key_multiple_column_with_data.test
new file mode 100644
index 00000000000..992606b4f62
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_drop_key_multiple_column_with_data.test
@@ -0,0 +1,46 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS scores;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE scores (
+ id BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL,
+ name CHAR(30) NOT NULL,
+ score INT NOT NULL,
+ KEY property (name, score)
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE scores;
+
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 29);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", -12);
+INSERT INTO scores (name, score) VALUES("Jiro Yamada", 27);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 10);
+SELECT * FROM scores
+ WHERE name = "Taro Yamada" AND (score >= -12 AND score < 29);
+
+ALTER TABLE scores DROP KEY property;
+SELECT * FROM scores
+ WHERE name = "Taro Yamada" AND (score >= -12 AND score < 29);
+
+DROP TABLE scores;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_fulltext.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_fulltext.test
new file mode 100644
index 00000000000..136acfcebf2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_fulltext.test
@@ -0,0 +1,51 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ FULLTEXT KEY title_index (title)
+) DEFAULT CHARSET=utf8;
+
+ALTER TABLE diaries DISABLE KEYS;
+
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+ALTER TABLE diaries ENABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_fulltext_ujis.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_fulltext_ujis.test
new file mode 100644
index 00000000000..e14400808e5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_fulltext_ujis.test
@@ -0,0 +1,51 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES ujis;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ FULLTEXT KEY title_index (title)
+) DEFAULT CHARSET=ujis;
+
+ALTER TABLE diaries DISABLE KEYS;
+
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "ŷ");
+INSERT INTO diaries VALUES (3, "ٻλ");
+
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("ٻλ");
+
+ALTER TABLE diaries ENABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("ٻλ");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_fulltext_utf8.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_fulltext_utf8.test
new file mode 100644
index 00000000000..136acfcebf2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_fulltext_utf8.test
@@ -0,0 +1,51 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ FULLTEXT KEY title_index (title)
+) DEFAULT CHARSET=utf8;
+
+ALTER TABLE diaries DISABLE KEYS;
+
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+ALTER TABLE diaries ENABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_multiple_column.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_multiple_column.test
new file mode 100644
index 00000000000..35e6366ed62
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_multiple_column.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ created_at datetime,
+ KEY title_and_created_at_index (title, created_at)
+) DEFAULT CHARSET=utf8;
+
+ALTER TABLE diaries DISABLE KEYS;
+
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_and_created_at_index)
+ WHERE title = "天気" AND
+ created_at = "2012-04-30 23:00:00";
+
+ALTER TABLE diaries ENABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_and_created_at_index)
+ WHERE title = "天気" AND
+ created_at = "2012-04-30 23:00:00";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_normal.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_normal.test
new file mode 100644
index 00000000000..ec45b48f61e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_normal.test
@@ -0,0 +1,51 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ created_at datetime,
+ KEY created_at_index (created_at)
+) DEFAULT CHARSET=utf8;
+
+ALTER TABLE diaries DISABLE KEYS;
+
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (created_at_index)
+ WHERE created_at = "2012-04-30 20:00:00";
+
+ALTER TABLE diaries ENABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (created_at_index)
+ WHERE created_at = "2012-04-30 20:00:00";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_primary.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_primary.test
new file mode 100644
index 00000000000..b1ebb668391
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_enable_keys_primary.test
@@ -0,0 +1,49 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255)
+) DEFAULT CHARSET=utf8;
+
+ALTER TABLE diaries DISABLE KEYS;
+
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (PRIMARY)
+ WHERE id = 2;
+
+ALTER TABLE diaries ENABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (PRIMARY)
+ WHERE id = 2;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_engine_decimal.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_engine_decimal.test
new file mode 100644
index 00000000000..fc07085df5b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_engine_decimal.test
@@ -0,0 +1,46 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ temperature DECIMAL(6, 3)
+) ENGINE InnoDB DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, temperature) VALUES ("clear day", 21.281);
+SELECT * FROM diaries;
+
+ALTER TABLE diaries ENGINE = mroonga;
+SELECT * FROM diaries;
+
+INSERT INTO diaries (title, temperature) VALUES ("rainy day", 14.213);
+INSERT INTO diaries (title, temperature) VALUES ("cloudy day", 17.821);
+SELECT * FROM diaries;
+
+SHOW CREATE TABLE diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_add_no_primary_key.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_add_no_primary_key.test
new file mode 100644
index 00000000000..fb52105411d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_add_no_primary_key.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+CREATE TABLE memos (
+ content varchar(32)
+) DEFAULT CHARSET="utf8";
+
+INSERT INTO memos (content) values ("Starting Groonga...");
+INSERT INTO memos (content) values ("Started Groonga.");
+INSERT INTO memos (content) values ("Starting Mroonga...");
+
+ALTER TABLE memos ADD FULLTEXT INDEX content_index (content);
+SHOW CREATE TABLE memos;
+
+SELECT * FROM memos WHERE MATCH(content) AGAINST("groonga");
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_add_normal.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_add_normal.test
new file mode 100644
index 00000000000..3bcd1259236
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_add_normal.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2011-2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+CREATE TABLE memos (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ content TEXT
+) DEFAULT CHARSET="utf8";
+
+INSERT INTO memos (content) values ("Starting Groonga...");
+INSERT INTO memos (content) values ("Started Groonga.");
+INSERT INTO memos (content) values ("Starting Mroonga...");
+
+ALTER TABLE memos ADD FULLTEXT INDEX content_index (content);
+SHOW CREATE TABLE memos;
+
+SELECT * FROM memos WHERE MATCH(content) AGAINST("groonga");
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_add_table.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_add_table.test
new file mode 100644
index 00000000000..efd0d9928d2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_add_table.test
@@ -0,0 +1,47 @@
+# Copyright(C) 2014 HAYASHI Kentaro <hayashi@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS tags;
+DROP TABLE IF EXISTS bugs;
+--enable_warnings
+
+CREATE TABLE tags (
+ name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8 COMMENT='default_tokenizer "TokenDelimit"';
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tags VARCHAR(40) COMMENT 'type "tags"'
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO tags (name) VALUES ("Groonga");
+INSERT INTO bugs (id, tags) VALUES (1, "Groonga Mroonga");
+
+SELECT * FROM bugs;
+
+ALTER TABLE bugs ADD FULLTEXT INDEX bugs_tags_index (tags) COMMENT 'table "tags"';
+
+SELECT * FROM bugs
+ WHERE MATCH(tags) AGAINST("Groonga");
+
+DROP TABLE bugs;
+DROP TABLE tags;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_drop_table.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_drop_table.test
new file mode 100644
index 00000000000..ebc30945062
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_fulltext_drop_table.test
@@ -0,0 +1,48 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS tags;
+DROP TABLE IF EXISTS bugs;
+--enable_warnings
+
+CREATE TABLE tags (
+ name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8 COMMENT='default_tokenizer "TokenDelimit"';
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tags VARCHAR(40) COMMENT 'type "tags"',
+ FULLTEXT INDEX bugs_tags_index (tags) COMMENT 'table "tags"'
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO tags (name) VALUES ("Groonga");
+INSERT INTO bugs (id, tags) VALUES (1, "Groonga Mroonga");
+
+ALTER TABLE bugs DROP INDEX bugs_tags_index;
+ALTER TABLE bugs
+ ADD FULLTEXT INDEX bugs_tags_index (tags) COMMENT 'table "tags"';
+
+SELECT * FROM bugs
+ WHERE MATCH(tags) AGAINST("Groonga");
+
+DROP TABLE bugs;
+DROP TABLE tags;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_modify_column_after.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_modify_column_after.test
new file mode 100644
index 00000000000..710161397d5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_modify_column_after.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2014 Naoya Murakami <naoya@createfield.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+
+ALTER TABLE diaries MODIFY body TEXT AFTER id;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) values ("groonga (2)", "started groonga.");
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_modify_column_first.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_modify_column_first.test
new file mode 100644
index 00000000000..a810b7457a5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_modify_column_first.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2014 Naoya Murakami <naoya@createfield.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+
+ALTER TABLE diaries MODIFY body TEXT FIRST;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) values ("groonga (2)", "started groonga.");
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_modify_column_no_order.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_modify_column_no_order.test
new file mode 100644
index 00000000000..7dc75e180c1
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_modify_column_no_order.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga.");
+SELECT * FROM diaries;
+
+ALTER TABLE diaries MODIFY title VARCHAR(100);
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) values ("groonga (2)", "started groonga.");
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_recreate_anonymous_index_at_once.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_recreate_anonymous_index_at_once.test
new file mode 100644
index 00000000000..0c391219426
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_recreate_anonymous_index_at_once.test
@@ -0,0 +1,51 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT,
+ FULLTEXT INDEX (body)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("survey", "will start mroonga!");
+
+SELECT * FROM diaries;
+SELECT * FROM diaries
+ WHERE MATCH (body) AGAINST ("+groonga" IN BOOLEAN MODE);
+
+ALTER TABLE diaries
+ DROP INDEX body,
+ ADD FULLTEXT INDEX (body);
+
+SELECT * FROM diaries;
+SELECT * FROM diaries
+ WHERE MATCH (body) AGAINST ("+groonga" IN BOOLEAN MODE);
+
+SHOW CREATE TABLE diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_rename_table.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_rename_table.test
new file mode 100644
index 00000000000..20fa7f35463
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_rename_table.test
@@ -0,0 +1,49 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries, memos;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT,
+ FULLTEXT INDEX title_index (title),
+ FULLTEXT INDEX body_index (body)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+SELECT * FROM diaries;
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("groonga") AND
+ MATCH(body) AGAINST("starting");
+
+ALTER TABLE diaries RENAME memos;
+SELECT * FROM memos;
+SELECT * FROM memos
+ WHERE MATCH(title) AGAINST("groonga") AND
+ MATCH(body) AGAINST("starting");
+
+SHOW CREATE TABLE memos;
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_spatial.test b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_spatial.test
new file mode 100644
index 00000000000..8a41792bdae
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/alter_table_spatial.test
@@ -0,0 +1,150 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source include/have_geometry.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS shops;
+--enable_warnings
+
+CREATE TABLE shops (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ name TEXT,
+ location GEOMETRY NOT NULL
+);
+
+INSERT INTO shops (name, location)
+ VALUES ('nezu-no-taiyaki',
+ GeomFromText('POINT(139.762573 35.720253)'));
+INSERT INTO shops (name, location)
+ VALUES ('taiyaki-kataoka',
+ GeomFromText('POINT(139.715591 35.712521)'));
+INSERT INTO shops (name, location)
+ VALUES ('soba-taiyaki-ku',
+ GeomFromText('POINT(139.659088 35.683712)'));
+INSERT INTO shops (name, location)
+ VALUES ('kuruma',
+ GeomFromText('POINT(139.706207 35.721516)'));
+INSERT INTO shops (name, location)
+ VALUES ('hirose-ya',
+ GeomFromText('POINT(139.685608 35.714844)'));
+INSERT INTO shops (name, location)
+ VALUES ('sazare',
+ GeomFromText('POINT(139.685043 35.714653)'));
+INSERT INTO shops (name, location)
+ VALUES ('omede-taiyaki',
+ GeomFromText('POINT(139.817154 35.700516)'));
+INSERT INTO shops (name, location)
+ VALUES ('onaga-ya',
+ GeomFromText('POINT(139.81105 35.698254)'));
+INSERT INTO shops (name, location)
+ VALUES ('shiro-ya',
+ GeomFromText('POINT(139.638611 35.705517)'));
+INSERT INTO shops (name, location)
+ VALUES ('fuji-ya',
+ GeomFromText('POINT(139.637115 35.703938)'));
+INSERT INTO shops (name, location)
+ VALUES ('miyoshi',
+ GeomFromText('POINT(139.537323 35.644539)'));
+INSERT INTO shops (name, location)
+ VALUES ('juju-ya',
+ GeomFromText('POINT(139.695755 35.628922)'));
+INSERT INTO shops (name, location)
+ VALUES ('tatsumi-ya',
+ GeomFromText('POINT(139.638657 35.665501)'));
+INSERT INTO shops (name, location)
+ VALUES ('tetsuji',
+ GeomFromText('POINT(139.76857 35.680912)'));
+INSERT INTO shops (name, location)
+ VALUES ('gazuma-ya',
+ GeomFromText('POINT(139.647598 35.700817)'));
+INSERT INTO shops (name, location)
+ VALUES ('honma-mon',
+ GeomFromText('POINT(139.652573 35.722736)'));
+INSERT INTO shops (name, location)
+ VALUES ('naniwa-ya',
+ GeomFromText('POINT(139.796234 35.730061)'));
+INSERT INTO shops (name, location)
+ VALUES ('kuro-dai',
+ GeomFromText('POINT(139.704834 35.650345)'));
+INSERT INTO shops (name, location)
+ VALUES ('daruma',
+ GeomFromText('POINT(139.770599 35.681461)'));
+INSERT INTO shops (name, location)
+ VALUES ('yanagi-ya',
+ GeomFromText('POINT(139.783981 35.685341)'));
+INSERT INTO shops (name, location)
+ VALUES ('sharaku',
+ GeomFromText('POINT(139.794846 35.716969)'));
+INSERT INTO shops (name, location)
+ VALUES ('takane',
+ GeomFromText('POINT(139.560913 35.698601)'));
+INSERT INTO shops (name, location)
+ VALUES ('chiyoda',
+ GeomFromText('POINT(139.652817 35.642601)'));
+INSERT INTO shops (name, location)
+ VALUES ('da-ka-po',
+ GeomFromText('POINT(139.727356 35.627346)'));
+INSERT INTO shops (name, location)
+ VALUES ('matsushima-ya',
+ GeomFromText('POINT(139.737381 35.640556)'));
+INSERT INTO shops (name, location)
+ VALUES ('kazuya',
+ GeomFromText('POINT(139.760895 35.673508)'));
+INSERT INTO shops (name, location)
+ VALUES ('furuya-kogane-an',
+ GeomFromText('POINT(139.676071 35.680603)'));
+INSERT INTO shops (name, location)
+ VALUES ('hachi-no-ie',
+ GeomFromText('POINT(139.668106 35.608021)'));
+INSERT INTO shops (name, location)
+ VALUES ('azuki-chan',
+ GeomFromText('POINT(139.673203 35.64151)'));
+INSERT INTO shops (name, location)
+ VALUES ('kuriko-an',
+ GeomFromText('POINT(139.796829 35.712013)'));
+INSERT INTO shops (name, location)
+ VALUES ('yume-no-aru-machi-no-taiyaki-ya-san',
+ GeomFromText('POINT(139.712524 35.616199)'));
+INSERT INTO shops (name, location)
+ VALUES ('naze-ya',
+ GeomFromText('POINT(139.665833 35.609039)'));
+INSERT INTO shops (name, location)
+ VALUES ('sanoki-ya',
+ GeomFromText('POINT(139.770721 35.66592)'));
+INSERT INTO shops (name, location)
+ VALUES ('shigeta',
+ GeomFromText('POINT(139.780273 35.672626)'));
+INSERT INTO shops (name, location)
+ VALUES ('nishimi-ya',
+ GeomFromText('POINT(139.774628 35.671825)'));
+INSERT INTO shops (name, location)
+ VALUES ('hiiragi',
+ GeomFromText('POINT(139.711517 35.647701)'));
+
+ALTER TABLE shops ADD SPATIAL KEY location_index (location);
+
+SELECT id, name, AsText(location) AS location_text FROM shops
+ WHERE MBRContains(GeomFromText('LineString(139.7727 35.6684, 139.7038 35.7121)'), location)
+ ORDER BY id;
+
+SHOW CREATE TABLE shops;
+
+DROP TABLE shops;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/auto_increment_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/storage/t/auto_increment_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..8ce709b1187
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/auto_increment_TODO_SPLIT_ME.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2011 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (c1 int auto_increment, primary key(c1));
+insert into t1 values(null);
+select c1 from t1 order by c1 desc limit 1;
+insert into t1 values(null);
+select c1 from t1 order by c1 desc limit 1;
+insert into t1 values(10);
+select c1 from t1 order by c1 desc limit 1;
+insert into t1 values(null);
+select c1 from t1 order by c1 desc limit 1;
+insert into t1 values(6);
+select c1 from t1 order by c1 desc limit 1;
+insert into t1 values(null);
+select c1 from t1 order by c1 desc limit 1;
+drop table t1;
+
+create table t1 (c1 int, c2 int auto_increment, primary key(c1), key idx1(c2));
+insert into t1 values(1, null);
+select * from t1 order by c2 desc limit 1;
+insert into t1 values(2, null);
+select * from t1 order by c2 desc limit 1;
+insert into t1 values(3, 10);
+select * from t1 order by c2 desc limit 1;
+insert into t1 values(4, null);
+select * from t1 order by c2 desc limit 1;
+insert into t1 values(5, 6);
+select * from t1 order by c2 desc limit 1;
+insert into t1 values(6, null);
+select * from t1 order by c2 desc limit 1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/auto_increment_table_param.test b/storage/mroonga/mysql-test/mroonga/storage/t/auto_increment_table_param.test
new file mode 100644
index 00000000000..59727f4abaa
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/auto_increment_table_param.test
@@ -0,0 +1,49 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (c1 int auto_increment, primary key(c1)) auto_increment=34129;
+insert into t1 values(null);
+select c1 from t1 order by c1 desc;
+show create table t1;
+insert into t1 values(null);
+select c1 from t1 order by c1 desc;
+insert into t1 values(10);
+select c1 from t1 order by c1 desc;
+insert into t1 values(null);
+select c1 from t1 order by c1 desc;
+insert into t1 values(6);
+select c1 from t1 order by c1 desc;
+insert into t1 values(null);
+select c1 from t1 order by c1 desc;
+truncate table t1;
+insert into t1 values(null);
+select c1 from t1 order by c1 desc;
+delete from t1;
+insert into t1 values(null);
+select c1 from t1 order by c1 desc;
+rename table t1 to t2;
+insert into t2 values(null);
+select c1 from t2 order by c1 desc;
+show create table t2;
+drop table t2;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/auto_increment_text.test b/storage/mroonga/mysql-test/mroonga/storage/t/auto_increment_text.test
new file mode 100644
index 00000000000..83f9f745019
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/auto_increment_text.test
@@ -0,0 +1,33 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+create table diaries (
+ id int primary key auto_increment,
+ body text
+);
+insert into diaries (body) values ("started groonga (long text)");
+select * from diaries;
+insert into diaries (body) values ("sleeping... (short text)");
+select * from diaries;
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/binlog_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/storage/t/binlog_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..57d57347205
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/binlog_TODO_SPLIT_ME.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_log_bin.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+show variables like 'log_bin';
+
+set binlog_format="STATEMENT";
+
+create table t1 (c1 int primary key, c2 int) engine = mroonga;
+insert into t1 values(1,100);
+insert into t1 values(2,100);
+commit;
+select * from t1;
+drop table t1;
+
+set binlog_format="ROW";
+
+create table t1 (c1 int primary key, c2 int) engine = mroonga;
+insert into t1 values(1,100);
+insert into t1 values(2,100);
+commit;
+select * from t1;
+drop table t1;
+
+set binlog_format="MIXED";
+
+create table t1 (c1 int primary key, c2 int) engine = mroonga;
+insert into t1 values(1,100);
+insert into t1 values(2,100);
+commit;
+select * from t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/collation_utf8_general_ci_french.test b/storage/mroonga/mysql-test/mroonga/storage/t/collation_utf8_general_ci_french.test
new file mode 100644
index 00000000000..8b4c7d6eaf7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/collation_utf8_general_ci_french.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ content varchar(256) COLLATE utf8_general_ci,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES ("Je suis un garçon.");
+
+SELECT * FROM diaries WHERE MATCH (content) AGAINST ("garcon");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/collation_utf8_unicode_ci_french.test b/storage/mroonga/mysql-test/mroonga/storage/t/collation_utf8_unicode_ci_french.test
new file mode 100644
index 00000000000..6cebc9ebce9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/collation_utf8_unicode_ci_french.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ content varchar(256) COLLATE utf8_unicode_ci,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES ("Je suis un garçon.");
+
+SELECT * FROM diaries WHERE MATCH (content) AGAINST ("garcon");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/collation_utf8_unicode_ci_japanese.test b/storage/mroonga/mysql-test/mroonga/storage/t/collation_utf8_unicode_ci_japanese.test
new file mode 100644
index 00000000000..dacb1cba74f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/collation_utf8_unicode_ci_japanese.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ content varchar(256) COLLATE utf8_unicode_ci,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES ("ひらがなとカタカナを覚えました。");
+
+SELECT * FROM diaries WHERE MATCH (content) AGAINST ("かたかな");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_comment_index_not_for_mroonga.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_comment_index_not_for_mroonga.test
new file mode 100644
index 00000000000..620d2a9fd56
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_comment_index_not_for_mroonga.test
@@ -0,0 +1,34 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS bugs;
+--enable_warnings
+
+CREATE TABLE bugs (
+ id INT UNSIGNED,
+ INDEX (id) COMMENT 'ID search is required.'
+) DEFAULT CHARSET=utf8;
+
+SHOW CREATE TABLE bugs;
+
+DROP TABLE bugs;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_comment_normal_not_for_mroonga.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_comment_normal_not_for_mroonga.test
new file mode 100644
index 00000000000..695ac9cba61
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_comment_normal_not_for_mroonga.test
@@ -0,0 +1,34 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS bugs;
+--enable_warnings
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tag VARCHAR(64) COMMENT 'It must consist of only alphabet and number.'
+) DEFAULT CHARSET=utf8;
+
+SHOW CREATE TABLE bugs;
+
+DROP TABLE bugs;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_date_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_date_with_index.test
new file mode 100644
index 00000000000..fc2e58fdcd9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_date_with_index.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATE,
+ KEY (created_at)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at) VALUES ("clear day", "2012-01-29");
+INSERT INTO diaries (title, created_at) VALUES ("rainy day", "2012-01-30");
+INSERT INTO diaries (title, created_at) VALUES ("cloudy day", "2012-01-31");
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries WHERE created_at BETWEEN "2012-01-29" AND "2012-01-30";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_date_without_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_date_without_index.test
new file mode 100644
index 00000000000..8dae21494db
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_date_without_index.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATE
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at) VALUES ("clear day", "2012-01-29");
+INSERT INTO diaries (title, created_at) VALUES ("rainy day", "2012-01-30");
+INSERT INTO diaries (title, created_at) VALUES ("cloudy day", "2012-01-31");
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries WHERE created_at BETWEEN "2012-01-29" AND "2012-01-30";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_date_zero_date.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_date_zero_date.test
new file mode 100644
index 00000000000..b6225075959
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_date_zero_date.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS timestamps;
+--enable_warnings
+
+CREATE TABLE timestamps (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ create_dt DATE
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE timestamps;
+
+INSERT INTO timestamps (create_dt) VALUES ("2012-00-01");
+INSERT INTO timestamps (create_dt) VALUES ("2012-01-00");
+
+SELECT * FROM timestamps;
+
+SELECT * FROM timestamps WHERE create_dt = "2012-01-01";
+
+DROP TABLE timestamps;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_2038.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_2038.test
new file mode 100644
index 00000000000..9c3344cbe98
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_2038.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_32bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ('2038-01-18 03:14:07', '2038-01-18 03:14:07');
+INSERT INTO diaries (title, created_at)
+ VALUES ('2038-01-20 03:14:08', '2038-01-20 03:14:08');
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_before_unix_epoch.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_before_unix_epoch.test
new file mode 100644
index 00000000000..61eecbb2ac8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_before_unix_epoch.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2012 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_32bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ('1000-01-01 00:00:00', '1000-01-01 00:00:00');
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_max.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_max.test
new file mode 100644
index 00000000000..7bbfcbe7a3a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_max.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2012 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_32bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ('9999-12-31 23:59:59', '9999-12-31 23:59:59');
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_out_of_range.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_out_of_range.test
new file mode 100644
index 00000000000..2cc99562231
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_32bit_out_of_range.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2012 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_32bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ('2012', '2012');
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_2038.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_2038.test
new file mode 100644
index 00000000000..75c757f147a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_2038.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ('2038-01-19 03:14:07', '2038-01-19 03:14:07');
+INSERT INTO diaries (title, created_at)
+ VALUES ('2038-01-19 03:14:08', '2038-01-19 03:14:08');
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_before_unix_epoch.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_before_unix_epoch.test
new file mode 100644
index 00000000000..b2b30462ca9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_before_unix_epoch.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/skip_freebsd.inc
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ('1000-01-01 00:00:00', '1000-01-01 00:00:00');
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_max.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_max.test
new file mode 100644
index 00000000000..661d145087c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_max.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2012 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ('9999-12-31 23:59:59', '9999-12-31 23:59:59');
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_version_55_out_of_range.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_version_55_out_of_range.test
new file mode 100644
index 00000000000..14a4483a96e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_version_55_out_of_range.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_version_55.inc
+--source ../../include/mroonga/have_mysql.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ('2012', '2012');
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_version_56_or_later_out_of_range.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_version_56_or_later_out_of_range.test
new file mode 100644
index 00000000000..6b90c39a853
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_64bit_version_56_or_later_out_of_range.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/skip_freebsd.inc
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_version_56_or_later.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ('2012', '2012');
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_fractional_seconds_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_fractional_seconds_with_index.test
new file mode 100644
index 00000000000..ac10203a576
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_fractional_seconds_with_index.test
@@ -0,0 +1,47 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_fractional_seconds.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME(6),
+ KEY (created_at)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ("clear day", "2012-01-29 21:51:01.111111");
+INSERT INTO diaries (title, created_at)
+ VALUES ("rainy day", "2012-01-30 01:23:45.333");
+INSERT INTO diaries (title, created_at)
+ VALUES ("cloudy day", "2012-01-31 08:32:10.5555");
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries
+ WHERE created_at BETWEEN "2012-01-29 00:00:00.123456" AND
+ "2012-01-31 00:00:00.999999";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_fractional_seconds_without_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_fractional_seconds_without_index.test
new file mode 100644
index 00000000000..11a2b783038
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_fractional_seconds_without_index.test
@@ -0,0 +1,46 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_fractional_seconds.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME(6)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ("clear day", "2012-01-29 21:51:01.111111");
+INSERT INTO diaries (title, created_at)
+ VALUES ("rainy day", "2012-01-30 01:23:45.333");
+INSERT INTO diaries (title, created_at)
+ VALUES ("cloudy day", "2012-01-31 08:32:10.5555");
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries
+ WHERE created_at BETWEEN "2012-01-29 00:00:00.123456" AND
+ "2012-01-31 00:00:00.999999";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_freebsd_before_unix_epoch.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_freebsd_before_unix_epoch.test
new file mode 100644
index 00000000000..07b54bebc9d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_freebsd_before_unix_epoch.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_freebsd.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ('1000-01-01 00:00:00', '1000-01-01 00:00:00');
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_null.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_null.test
new file mode 100644
index 00000000000..e1db5043319
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_null.test
@@ -0,0 +1,37 @@
+# Copyright(C) 2012 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ('NULL', NULL);
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_with_index.test
new file mode 100644
index 00000000000..992249b0fae
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_with_index.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME,
+ KEY (created_at)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ("clear day", "2012-01-29 21:51:01");
+INSERT INTO diaries (title, created_at)
+ VALUES ("rainy day", "2012-01-30 01:23:45");
+INSERT INTO diaries (title, created_at)
+ VALUES ("cloudy day", "2012-01-31 08:32:10");
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries
+ WHERE created_at BETWEEN "2012-01-29 00:00:00" AND "2012-01-31 00:00:00";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_without_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_without_index.test
new file mode 100644
index 00000000000..bc0b7b2e982
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_without_index.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at)
+ VALUES ("clear day", "2012-01-29 21:51:01");
+INSERT INTO diaries (title, created_at)
+ VALUES ("rainy day", "2012-01-30 01:23:45");
+INSERT INTO diaries (title, created_at)
+ VALUES ("cloudy day", "2012-01-31 08:32:10");
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries
+ WHERE created_at BETWEEN "2012-01-29 00:00:00" AND "2012-01-31 00:00:00";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_zero_date.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_zero_date.test
new file mode 100644
index 00000000000..5c39086d452
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_datetime_zero_date.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS timestamps;
+--enable_warnings
+
+CREATE TABLE timestamps (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ create_dt DATETIME
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE timestamps;
+
+INSERT INTO timestamps (create_dt) VALUES ("2012-00-01 00:00:00");
+INSERT INTO timestamps (create_dt) VALUES ("2012-01-00 00:00:00");
+
+SELECT * FROM timestamps;
+
+SELECT * FROM timestamps WHERE create_dt = "2012-01-01 00:00:00";
+
+DROP TABLE timestamps;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_fractional_seconds_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_fractional_seconds_with_index.test
new file mode 100644
index 00000000000..92636b4d773
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_fractional_seconds_with_index.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ temperature DECIMAL(6, 3),
+ KEY (temperature)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, temperature) VALUES ("clear day", 21.281);
+INSERT INTO diaries (title, temperature) VALUES ("rainy day", 14.213);
+INSERT INTO diaries (title, temperature) VALUES ("cloudy day", 17.821);
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries WHERE temperature BETWEEN "14.213" AND "17.821";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_fractional_seconds_without_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_fractional_seconds_without_index.test
new file mode 100644
index 00000000000..b314342fdea
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_fractional_seconds_without_index.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ temperature DECIMAL(6, 3)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, temperature) VALUES ("clear day", 21.281);
+INSERT INTO diaries (title, temperature) VALUES ("rainy day", 14.213);
+INSERT INTO diaries (title, temperature) VALUES ("cloudy day", 17.821);
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries WHERE temperature BETWEEN "14.213" AND "17.821";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_with_index.test
new file mode 100644
index 00000000000..ffcf737fde9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_with_index.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ temperature DECIMAL,
+ KEY (temperature)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, temperature) VALUES ("clear day", 21);
+INSERT INTO diaries (title, temperature) VALUES ("rainy day", 14);
+INSERT INTO diaries (title, temperature) VALUES ("cloudy day", 17);
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries WHERE temperature BETWEEN "14" AND "17";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_without_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_without_index.test
new file mode 100644
index 00000000000..d7df5c3c323
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_decimal_without_index.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ temperature DECIMAL
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, temperature) VALUES ("clear day", 21);
+INSERT INTO diaries (title, temperature) VALUES ("rainy day", 14);
+INSERT INTO diaries (title, temperature) VALUES ("cloudy day", 17);
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries WHERE temperature BETWEEN "14" AND "17";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_enum_less_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_enum_less_with_index.test
new file mode 100644
index 00000000000..0d96498ee04
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_enum_less_with_index.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ size ENUM("small", "medium", "large"),
+ INDEX (size)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+
+INSERT INTO items VALUES ("t-shart for child", "small");
+INSERT INTO items VALUES ("leadies' coat", "medium");
+INSERT INTO items VALUES ("parka", "large");
+INSERT INTO items VALUES ("hat", "medium");
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE size = "medium";
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_enum_many_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_enum_many_with_index.test
new file mode 100644
index 00000000000..3f8b728105c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_enum_many_with_index.test
@@ -0,0 +1,298 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ size ENUM("size1",
+ "size2",
+ "size3",
+ "size4",
+ "size5",
+ "size6",
+ "size7",
+ "size8",
+ "size9",
+ "size10",
+ "size11",
+ "size12",
+ "size13",
+ "size14",
+ "size15",
+ "size16",
+ "size17",
+ "size18",
+ "size19",
+ "size20",
+ "size21",
+ "size22",
+ "size23",
+ "size24",
+ "size25",
+ "size26",
+ "size27",
+ "size28",
+ "size29",
+ "size30",
+ "size31",
+ "size32",
+ "size33",
+ "size34",
+ "size35",
+ "size36",
+ "size37",
+ "size38",
+ "size39",
+ "size40",
+ "size41",
+ "size42",
+ "size43",
+ "size44",
+ "size45",
+ "size46",
+ "size47",
+ "size48",
+ "size49",
+ "size50",
+ "size51",
+ "size52",
+ "size53",
+ "size54",
+ "size55",
+ "size56",
+ "size57",
+ "size58",
+ "size59",
+ "size60",
+ "size61",
+ "size62",
+ "size63",
+ "size64",
+ "size65",
+ "size66",
+ "size67",
+ "size68",
+ "size69",
+ "size70",
+ "size71",
+ "size72",
+ "size73",
+ "size74",
+ "size75",
+ "size76",
+ "size77",
+ "size78",
+ "size79",
+ "size80",
+ "size81",
+ "size82",
+ "size83",
+ "size84",
+ "size85",
+ "size86",
+ "size87",
+ "size88",
+ "size89",
+ "size90",
+ "size91",
+ "size92",
+ "size93",
+ "size94",
+ "size95",
+ "size96",
+ "size97",
+ "size98",
+ "size99",
+ "size100",
+ "size101",
+ "size102",
+ "size103",
+ "size104",
+ "size105",
+ "size106",
+ "size107",
+ "size108",
+ "size109",
+ "size110",
+ "size111",
+ "size112",
+ "size113",
+ "size114",
+ "size115",
+ "size116",
+ "size117",
+ "size118",
+ "size119",
+ "size120",
+ "size121",
+ "size122",
+ "size123",
+ "size124",
+ "size125",
+ "size126",
+ "size127",
+ "size128",
+ "size129",
+ "size130",
+ "size131",
+ "size132",
+ "size133",
+ "size134",
+ "size135",
+ "size136",
+ "size137",
+ "size138",
+ "size139",
+ "size140",
+ "size141",
+ "size142",
+ "size143",
+ "size144",
+ "size145",
+ "size146",
+ "size147",
+ "size148",
+ "size149",
+ "size150",
+ "size151",
+ "size152",
+ "size153",
+ "size154",
+ "size155",
+ "size156",
+ "size157",
+ "size158",
+ "size159",
+ "size160",
+ "size161",
+ "size162",
+ "size163",
+ "size164",
+ "size165",
+ "size166",
+ "size167",
+ "size168",
+ "size169",
+ "size170",
+ "size171",
+ "size172",
+ "size173",
+ "size174",
+ "size175",
+ "size176",
+ "size177",
+ "size178",
+ "size179",
+ "size180",
+ "size181",
+ "size182",
+ "size183",
+ "size184",
+ "size185",
+ "size186",
+ "size187",
+ "size188",
+ "size189",
+ "size190",
+ "size191",
+ "size192",
+ "size193",
+ "size194",
+ "size195",
+ "size196",
+ "size197",
+ "size198",
+ "size199",
+ "size200",
+ "size201",
+ "size202",
+ "size203",
+ "size204",
+ "size205",
+ "size206",
+ "size207",
+ "size208",
+ "size209",
+ "size210",
+ "size211",
+ "size212",
+ "size213",
+ "size214",
+ "size215",
+ "size216",
+ "size217",
+ "size218",
+ "size219",
+ "size220",
+ "size221",
+ "size222",
+ "size223",
+ "size224",
+ "size225",
+ "size226",
+ "size227",
+ "size228",
+ "size229",
+ "size230",
+ "size231",
+ "size232",
+ "size233",
+ "size234",
+ "size235",
+ "size236",
+ "size237",
+ "size238",
+ "size239",
+ "size240",
+ "size241",
+ "size242",
+ "size243",
+ "size244",
+ "size245",
+ "size246",
+ "size247",
+ "size248",
+ "size249",
+ "size250",
+ "size251",
+ "size252",
+ "size253",
+ "size254",
+ "size255",
+ "size256"),
+ INDEX (size)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+
+INSERT INTO items VALUES ("t-shart for child", "size1");
+INSERT INTO items VALUES ("leadies' coat", "size1");
+INSERT INTO items VALUES ("parka", "size256");
+INSERT INTO items VALUES ("hat", "size256");
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE size = "size1";
+
+SELECT * FROM items WHERE size = "size256";
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga__id__id.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga__id__id.test
new file mode 100644
index 00000000000..6efb3920695
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga__id__id.test
@@ -0,0 +1,34 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS contents;
+--enable_warnings
+
+CREATE TABLE contents (
+ _id INT,
+ content TEXT NOT NULL,
+ FULLTEXT INDEX(content)
+) DEFAULT CHARSET=utf8;
+INSERT INTO contents (content) VALUES ('first');
+INSERT INTO contents (content) VALUES ('second');
+SELECT _id, content FROM contents;
+
+DROP TABLE contents;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga__id_invalid_id.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga__id_invalid_id.test
new file mode 100644
index 00000000000..9b2e59615f3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga__id_invalid_id.test
@@ -0,0 +1,34 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS contents;
+--enable_warnings
+
+--error ER_CANT_CREATE_TABLE
+CREATE TABLE contents (
+ _i INT,
+ content TEXT NOT NULL,
+ FULLTEXT INDEX(content)
+) DEFAULT CHARSET=utf8;
+
+--disable_warnings
+DROP TABLE IF EXISTS contents;
+--enable_warnings
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_index_fulltext_other_table.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_index_fulltext_other_table.test
new file mode 100644
index 00000000000..805c744236f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_index_fulltext_other_table.test
@@ -0,0 +1,54 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP DATABASE IF EXISTS mroonga;
+--enable_warnings
+
+CREATE DATABASE mroonga;
+USE mroonga;
+
+CREATE TABLE tags (
+ name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+ COLLATE=utf8_bin
+ COMMENT='default_tokenizer "TokenDelimit"';
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tags TEXT COMMENT 'flags "COLUMN_VECTOR", type "tags"',
+ FULLTEXT INDEX bugs_tags_index (tags) COMMENT 'table "tags"'
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO bugs (id, tags) VALUES (1, "Linux MySQL groonga");
+
+SELECT mroonga_command("dump");
+
+SELECT *, MATCH (tags) AGAINST ("MySQL" IN BOOLEAN MODE) AS score
+ FROM bugs
+ WHERE MATCH (tags) AGAINST ("MySQL" IN BOOLEAN MODE);
+
+DROP TABLE bugs;
+DROP TABLE tags;
+
+DROP DATABASE mroonga;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_index_int_other_table.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_index_int_other_table.test
new file mode 100644
index 00000000000..91f204133dd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_index_int_other_table.test
@@ -0,0 +1,55 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP DATABASE IF EXISTS mroonga;
+--enable_warnings
+
+CREATE DATABASE mroonga;
+USE mroonga;
+
+CREATE TABLE priorities (
+ id INT PRIMARY KEY
+) DEFAULT CHARSET=utf8
+ COLLATE=utf8_bin;
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ priority INT COMMENT 'type "priorities"',
+ INDEX bugs_priority_index (priority) COMMENT 'table "priorities"'
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO bugs (id, priority) VALUES (1, 10);
+INSERT INTO bugs (id, priority) VALUES (2, 3);
+INSERT INTO bugs (id, priority) VALUES (3, -2);
+
+SELECT mroonga_command("dump");
+
+SELECT *
+ FROM bugs
+ WHERE priority = 3;
+
+DROP TABLE bugs;
+DROP TABLE priorities;
+
+DROP DATABASE mroonga;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_scalar_reference.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_scalar_reference.test
new file mode 100644
index 00000000000..6e89c70c051
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_scalar_reference.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS tags, bugs;
+--enable_warnings
+
+CREATE TABLE tags (
+ name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+ COLLATE=utf8_bin;
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tag TEXT COMMENT 'type "tags"'
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO bugs (id, tag) VALUES (1, "Linux");
+INSERT INTO bugs (id, tag) VALUES (2, "MySQL");
+INSERT INTO bugs (id, tag) VALUES (3, "groonga");
+
+SELECT * FROM bugs;
+SELECT * FROM tags;
+
+DROP TABLE bugs;
+DROP TABLE tags;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_scalar_with_not_for_mroonga_comment.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_scalar_with_not_for_mroonga_comment.test
new file mode 100644
index 00000000000..059b8bc4bfa
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_scalar_with_not_for_mroonga_comment.test
@@ -0,0 +1,48 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS tags, bugs;
+--enable_warnings
+
+CREATE TABLE tags (
+ name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+ COLLATE=utf8_bin;
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tag TEXT COMMENT 'It references to tags.name, type "tags"'
+) DEFAULT CHARSET=utf8;
+
+SHOW FULL COLUMNS FROM bugs LIKE 'tag';
+
+INSERT INTO bugs (id, tag) VALUES (1, "Linux");
+INSERT INTO bugs (id, tag) VALUES (2, "MySQL");
+INSERT INTO bugs (id, tag) VALUES (3, "groonga");
+
+SELECT * FROM bugs;
+SELECT * FROM tags;
+
+DROP TABLE bugs;
+DROP TABLE tags;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_vector_order_by_with_function.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_vector_order_by_with_function.test
new file mode 100644
index 00000000000..0d3be3d663d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_vector_order_by_with_function.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS tags, bugs;
+--enable_warnings
+
+CREATE TABLE tags (
+ name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+ COLLATE=utf8_bin
+ COMMENT='default_tokenizer "TokenDelimit"';
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tags TEXT COMMENT 'flags "COLUMN_VECTOR", type "tags"'
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO bugs (id, tags) VALUES (1, "Linux MySQL Groonga");
+INSERT INTO bugs (id, tags) VALUES (2, "MySQL Mroonga");
+INSERT INTO bugs (id, tags) VALUES (3, "Ruby Rroonga");
+
+SELECT * FROM tags ORDER BY SUBSTRING(name, 1, 1) ASC;
+
+DROP TABLE bugs;
+DROP TABLE tags;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_vector_reference.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_vector_reference.test
new file mode 100644
index 00000000000..bb66e35f2d1
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_groonga_vector_reference.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS tags, bugs;
+--enable_warnings
+
+CREATE TABLE tags (
+ name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+ COLLATE=utf8_bin
+ COMMENT='default_tokenizer "TokenDelimit"';
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tags TEXT COMMENT 'flags "COLUMN_VECTOR", type "tags"'
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO bugs (id, tags) VALUES (1, "Linux MySQL groonga");
+
+SELECT * FROM bugs;
+SELECT * FROM tags;
+
+DROP TABLE bugs;
+DROP TABLE tags;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_int_with_index_zero_value.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_int_with_index_zero_value.test
new file mode 100644
index 00000000000..aa33af05df3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_int_with_index_zero_value.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ price INT KEY
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+
+INSERT INTO items VALUES ("hamburger", 200);
+INSERT INTO items VALUES ("smile", 0);
+INSERT INTO items VALUES ("coke", 100);
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE price = 0;
+
+SELECT * FROM items WHERE price <= 100;
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_set_16_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_set_16_with_index.test
new file mode 100644
index 00000000000..9d7d40433d0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_set_16_with_index.test
@@ -0,0 +1,56 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ colors SET("black",
+ "dim gray",
+ "dark gray",
+ "gray",
+ "light gray",
+ "gainsboro",
+ "white smoke",
+ "white",
+
+ "red",
+ "orange red",
+ "dark orange",
+ "orange",
+ "gold",
+ "yellow",
+ "chartreuse",
+ "lawn green"),
+ INDEX (colors)
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+
+INSERT INTO items VALUES ("t-shart", "black,gray");
+INSERT INTO items VALUES ("hat", "white,dark gray");
+INSERT INTO items VALUES ("parka", "chartreuse,orange");
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE colors = "dark gray,white";
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_set_24_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_set_24_with_index.test
new file mode 100644
index 00000000000..c555c4ced6a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_set_24_with_index.test
@@ -0,0 +1,65 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ colors SET("black",
+ "dim gray",
+ "dark gray",
+ "gray",
+ "light gray",
+ "gainsboro",
+ "white smoke",
+ "white",
+
+ "red",
+ "orange red",
+ "dark orange",
+ "orange",
+ "gold",
+ "yellow",
+ "chartreuse",
+ "lawn green",
+
+ "green",
+ "spring green",
+ "medium spring green",
+ "cyan",
+ "deep sky blue",
+ "blue",
+ "medium blue",
+ "dark violet"),
+ INDEX (colors)
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+
+INSERT INTO items VALUES ("t-shart", "black,white");
+INSERT INTO items VALUES ("hat", "white,lawn green");
+INSERT INTO items VALUES ("parka", "gray,medium blue");
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE colors = "white,lawn green";
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_set_32_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_set_32_with_index.test
new file mode 100644
index 00000000000..0f772522eb6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_set_32_with_index.test
@@ -0,0 +1,74 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ colors SET("black",
+ "dim gray",
+ "dark gray",
+ "gray",
+ "light gray",
+ "gainsboro",
+ "white smoke",
+ "white",
+
+ "red",
+ "orange red",
+ "dark orange",
+ "orange",
+ "gold",
+ "yellow",
+ "chartreuse",
+ "lawn green",
+
+ "green",
+ "spring green",
+ "medium spring green",
+ "cyan",
+ "deep sky blue",
+ "blue",
+ "medium blue",
+ "dark violet",
+
+ "dark magenta",
+ "magenta",
+ "dark red",
+ "brown",
+ "firebrick",
+ "indian red",
+ "light coral",
+ "salmon"),
+ INDEX (colors)
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+
+INSERT INTO items VALUES ("t-shart", "black,white");
+INSERT INTO items VALUES ("hat", "white,dark violet");
+INSERT INTO items VALUES ("parka", "green,brown,red");
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE colors = "white,dark violet";
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_set_64_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_set_64_with_index.test
new file mode 100644
index 00000000000..6552c5da805
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_set_64_with_index.test
@@ -0,0 +1,110 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ colors SET("black",
+ "dim gray",
+ "dark gray",
+ "gray",
+ "light gray",
+ "gainsboro",
+ "white smoke",
+ "white",
+
+ "red",
+ "orange red",
+ "dark orange",
+ "orange",
+ "gold",
+ "yellow",
+ "chartreuse",
+ "lawn green",
+
+ "green",
+ "spring green",
+ "medium spring green",
+ "cyan",
+ "deep sky blue",
+ "blue",
+ "medium blue",
+ "dark violet",
+
+ "dark magenta",
+ "magenta",
+ "dark red",
+ "brown",
+ "firebrick",
+ "indian red",
+ "light coral",
+ "salmon",
+
+ "light salmon",
+ "tomato",
+ "coral",
+ "dark salmon",
+ "rosy brown",
+ "sienna",
+ "saddle brown",
+ "chocolate",
+
+ "peru",
+ "sandy brown",
+ "burlywood",
+ "tan",
+ "navajo white",
+ "wheat",
+ "dark goldenrod",
+ "goldenrod",
+
+ "light goldenrod",
+ "pale goldenrod",
+ "cornsilk",
+ "dark khaki",
+ "khaki",
+ "lemon chiffon",
+ "dark olive green",
+ "olive drab",
+
+ "yellow green",
+ "green yellow",
+ "light green",
+ "forest green",
+ "dark green",
+ "lime green",
+ "pale green",
+ "dark sea green"),
+ INDEX (colors)
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+
+INSERT INTO items VALUES ("t-shart", "black,white,lawn green,dark violet");
+INSERT INTO items VALUES ("hat", "white,dark violet,yellow green");
+INSERT INTO items VALUES ("parka", "green,brown,red,lime green");
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE colors = "white,dark violet,yellow green";
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_set_8_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_set_8_with_index.test
new file mode 100644
index 00000000000..bd92f23deb0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_set_8_with_index.test
@@ -0,0 +1,47 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ colors SET("black",
+ "dim gray",
+ "dark gray",
+ "gray",
+ "light gray",
+ "gainsboro",
+ "white smoke",
+ "white"),
+ INDEX (colors)
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE items;
+
+INSERT INTO items VALUES ("t-shart", "black,gray");
+INSERT INTO items VALUES ("hat", "dim gray,dark gray");
+INSERT INTO items VALUES ("parka", "white smoke,light gray");
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE colors = "dim gray,dark gray";
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_bigint_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_bigint_with_index.test
new file mode 100644
index 00000000000..c8bd7df0a15
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_bigint_with_index.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ price BIGINT KEY
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO items VALUES ("house", 9223372036854775807);
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", -9223372036854775808);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("super car", 2147483648);
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE price <= 2147483648;
+
+SELECT * FROM items WHERE price > 2147483647;
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_int_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_int_with_index.test
new file mode 100644
index 00000000000..572f46ebbb5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_int_with_index.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ price INT KEY
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO items VALUES ("car", 2147483647);
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", -2147483647);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("bike", 16777216);
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE price <= 16777216;
+
+SELECT * FROM items WHERE price > 16777215;
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_mediumint_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_mediumint_with_index.test
new file mode 100644
index 00000000000..f43395cb0aa
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_mediumint_with_index.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ price MEDIUMINT KEY
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO items VALUES ("car", 8388607);
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", -8388608);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("bike", 32768);
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE price <= 127;
+
+SELECT * FROM items WHERE price >= 32768;
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_smallint_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_smallint_with_index.test
new file mode 100644
index 00000000000..4ddd446dc46
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_smallint_with_index.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ price SMALLINT KEY
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", -32768);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("tablet PC", 20000);
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE price <= 127;
+
+SELECT * FROM items WHERE price >= 128;
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_tinyint_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_tinyint_with_index.test
new file mode 100644
index 00000000000..e2bca9328a9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_signed_tinyint_with_index.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ price TINYINT KEY
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO items VALUES ("hamburger", 120);
+INSERT INTO items VALUES ("discount", -10);
+INSERT INTO items VALUES ("coke", 100);
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE price <= 100;
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_time_fractional_seconds_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_time_fractional_seconds_with_index.test
new file mode 100644
index 00000000000..2a920d7e387
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_time_fractional_seconds_with_index.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/have_fractional_seconds.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS running_records;
+--enable_warnings
+
+CREATE TABLE running_records (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ average TIME(6),
+ max TIME(6),
+ KEY (average)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE running_records;
+
+INSERT INTO running_records (title, average, max)
+ VALUES ("normal condition", "01:00:00.000001", "01:05:00.000001");
+INSERT INTO running_records (title, average, max)
+ VALUES ("bad condition", "12:23:34.123456", "838:59:58.999999");
+INSERT INTO running_records (title, average, max)
+ VALUES ("record failure", "-838:59:59.000000", "-838:59:59.000000");
+
+SELECT * FROM running_records;
+
+SELECT * FROM running_records
+ WHERE average BETWEEN "00:59:59.999999" AND "100:10:10.101010";
+
+SELECT * FROM running_records
+ WHERE average BETWEEN "-838:59:59.000000" AND "01:00:00.000001";
+
+DROP TABLE running_records;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_time_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_time_with_index.test
new file mode 100644
index 00000000000..f5961825259
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_time_with_index.test
@@ -0,0 +1,49 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS running_records;
+--enable_warnings
+
+CREATE TABLE running_records (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ average TIME,
+ max TIME,
+ KEY (average)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE running_records;
+
+INSERT INTO running_records (title, average, max)
+ VALUES ("normal condition", "01:00:00", "01:05:00");
+INSERT INTO running_records (title, average, max)
+ VALUES ("bad condition", "12:23:34", "838:59:59");
+INSERT INTO running_records (title, average, max)
+ VALUES ("record failure", "-838:59:59", "-838:59:59");
+
+SELECT * FROM running_records;
+
+SELECT * FROM running_records
+ WHERE average BETWEEN "00:59:59" AND "100:10:10";
+
+SELECT * FROM running_records
+ WHERE average BETWEEN "-838:59:59" AND "01:00:00";
+
+DROP TABLE running_records;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_timestamp_fractional_seconds_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_timestamp_fractional_seconds_with_index.test
new file mode 100644
index 00000000000..6f5e0116eb7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_timestamp_fractional_seconds_with_index.test
@@ -0,0 +1,55 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/skip_mariadb_55.inc
+--source ../../include/mroonga/have_fractional_seconds.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at TIMESTAMP(6),
+ updated_at TIMESTAMP(6),
+ KEY (updated_at)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at, updated_at)
+ VALUES ("clear day",
+ "2012-01-29 21:51:01.111111",
+ "2012-01-29 21:51:02.222222");
+INSERT INTO diaries (title, created_at, updated_at)
+ VALUES ("rainy day",
+ "2012-01-30 01:23:45.333",
+ "2012-01-30 01:23:46.444");
+INSERT INTO diaries (title, created_at, updated_at)
+ VALUES ("cloudy day",
+ "2012-01-31 08:32:10.5555",
+ "2012-01-31 08:32:11.6666");
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries
+ WHERE updated_at BETWEEN "2012-01-29 00:00:00.123456" AND
+ "2012-01-31 00:00:00.999999";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_timestamp_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_timestamp_with_index.test
new file mode 100644
index 00000000000..ae78befc466
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_timestamp_with_index.test
@@ -0,0 +1,46 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ created_at TIMESTAMP,
+ updated_at TIMESTAMP,
+ KEY (updated_at)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, created_at, updated_at)
+ VALUES ("clear day", "2012-01-29 21:51:01", "2012-01-29 21:51:02");
+INSERT INTO diaries (title, created_at, updated_at)
+ VALUES ("rainy day", "2012-01-30 01:23:45", "2012-01-30 01:23:46");
+INSERT INTO diaries (title, created_at, updated_at)
+ VALUES ("cloudy day", "2012-01-31 08:32:10", "2012-01-31 08:32:11");
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries
+ WHERE updated_at BETWEEN "2012-01-29 00:00:00" AND "2012-01-31 00:00:00";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_tinyint_without_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_tinyint_without_index.test
new file mode 100644
index 00000000000..c658c873e02
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_tinyint_without_index.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists books;
+--enable_warnings
+
+create table books(title varchar(255), published tinyint);
+insert into books values ("MySQL", 1);
+insert into books values ("groonga", 1);
+insert into books values ("mroonga", 0);
+
+select count(*) from books where published = 0;
+select count(*) from books where published = 1;
+select count(*) from books where published != 2;
+select count(*) from books where published != 1;
+
+drop table books;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_bigint_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_bigint_with_index.test
new file mode 100644
index 00000000000..1936842707c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_bigint_with_index.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ price BIGINT UNSIGNED KEY
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO items VALUES ("house", 18446744073709551615);
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", 0);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("super car", 9223372036854775808);
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE price <= 9223372036854775808;
+
+SELECT * FROM items WHERE price > 9223372036854775807;
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_bigint_without_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_bigint_without_index.test
new file mode 100644
index 00000000000..8ac9715ad76
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_bigint_without_index.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id BIGINT UNSIGNED
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO ids VALUES (317173755057152000);
+INSERT INTO ids VALUES (317173755057152002);
+
+SELECT * FROM ids;
+
+SELECT * FROM ids WHERE id = 317173755057152000;
+
+SELECT * FROM ids WHERE id = 317173755057152002;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_int_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_int_with_index.test
new file mode 100644
index 00000000000..a97024b48d8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_int_with_index.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ price INT UNSIGNED KEY
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO items VALUES ("car", 4294967295);
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", 0);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("bike", 2147483648);
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE price <= 2147483648;
+
+SELECT * FROM items WHERE price > 2147483647;
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_mediumint_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_mediumint_with_index.test
new file mode 100644
index 00000000000..bec5917b410
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_mediumint_with_index.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ price MEDIUMINT UNSIGNED KEY
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO items VALUES ("car", 16777215);
+INSERT INTO items VALUES ("note PC", 32767);
+INSERT INTO items VALUES ("discount", 0);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("bike", 8388607);
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE price <= 8388608;
+
+SELECT * FROM items WHERE price >= 8388607;
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_smallint_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_smallint_with_index.test
new file mode 100644
index 00000000000..8e389b98881
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_smallint_with_index.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ price SMALLINT UNSIGNED KEY
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO items VALUES ("note PC", 65535);
+INSERT INTO items VALUES ("discount", 0);
+INSERT INTO items VALUES ("coke", 100);
+INSERT INTO items VALUES ("tablet PC", 32767);
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE price <= 32768;
+
+SELECT * FROM items WHERE price >= 32767;
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_tinyint_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_tinyint_with_index.test
new file mode 100644
index 00000000000..880bc2d7cd8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_unsigned_tinyint_with_index.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS items;
+--enable_warnings
+
+CREATE TABLE items (
+ name VARCHAR(255),
+ price TINYINT UNSIGNED KEY
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO items VALUES ("hamburger", 255);
+INSERT INTO items VALUES ("discount", 0);
+INSERT INTO items VALUES ("coke", 100);
+
+SELECT * FROM items;
+
+SELECT * FROM items WHERE price <= 100;
+
+DROP TABLE items;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_year_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_year_with_index.test
new file mode 100644
index 00000000000..a61dccc6439
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_year_with_index.test
@@ -0,0 +1,47 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS aniversary_memos;
+--enable_warnings
+
+CREATE TABLE aniversary_memos (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ party_year YEAR,
+ KEY (party_year)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE aniversary_memos;
+
+INSERT INTO aniversary_memos (title, party_year)
+ VALUES ("We need a big cake!", "11");
+INSERT INTO aniversary_memos (title, party_year)
+ VALUES ("Invitations are sent.", "2012");
+INSERT INTO aniversary_memos (title, party_year)
+ VALUES ("Tommorow is the anniversary party day!", "2012");
+INSERT INTO aniversary_memos (title, party_year)
+ VALUES ("Wow! Today is the anniversary party day!", "13");
+
+SELECT * FROM aniversary_memos;
+
+SELECT * FROM aniversary_memos
+ WHERE party_year BETWEEN "12" AND "2013";
+
+DROP TABLE aniversary_memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/column_year_without_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/column_year_without_index.test
new file mode 100644
index 00000000000..ce1e1c21921
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/column_year_without_index.test
@@ -0,0 +1,46 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS aniversary_memos;
+--enable_warnings
+
+CREATE TABLE aniversary_memos (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ party_year YEAR
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE aniversary_memos;
+
+INSERT INTO aniversary_memos (title, party_year)
+ VALUES ("We need a big cake!", "11");
+INSERT INTO aniversary_memos (title, party_year)
+ VALUES ("Invitations are sent.", "2012");
+INSERT INTO aniversary_memos (title, party_year)
+ VALUES ("Tommorow is the anniversary party day!", "2012");
+INSERT INTO aniversary_memos (title, party_year)
+ VALUES ("Wow! Today is the anniversary party day!", "13");
+
+SELECT * FROM aniversary_memos;
+
+SELECT * FROM aniversary_memos
+ WHERE party_year BETWEEN "12" AND "2013";
+
+DROP TABLE aniversary_memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/create_database_name_slash.test b/storage/mroonga/mysql-test/mroonga/storage/t/create_database_name_slash.test
new file mode 100644
index 00000000000..b731acda9c9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/create_database_name_slash.test
@@ -0,0 +1,60 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP DATABASE IF EXISTS `master/production`;
+DROP DATABASE IF EXISTS `master/development`;
+--enable_warnings
+
+CREATE DATABASE `master/production`;
+USE `master/production`;
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT
+) DEFAULT CHARSET=UTF8;
+
+INSERT INTO diaries (title) VALUES ("clear day (production)");
+INSERT INTO diaries (title) VALUES ("rainy day (production)");
+INSERT INTO diaries (title) VALUES ("cloudy day (production)");
+
+SELECT * FROM diaries;
+
+
+CREATE DATABASE `master/development`;
+USE `master/development`;
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT
+) DEFAULT CHARSET=UTF8;
+
+INSERT INTO diaries (title) VALUES ("clear day (development)");
+INSERT INTO diaries (title) VALUES ("rainy day (development)");
+INSERT INTO diaries (title) VALUES ("cloudy day (development)");
+
+SELECT * FROM diaries;
+
+
+USE test;
+
+DROP DATABASE `master/production`;
+DROP DATABASE `master/development`;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/create_table_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..e448a66956f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_TODO_SPLIT_ME.test
@@ -0,0 +1,147 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+# simple test
+create table t1 (c1 int);
+create table t2 (c1 int);
+create table t3 (c1 int);
+drop table t1,t2,t3;
+create table t1 (c1 int, c2 int, c3 int);
+create table t2 (c1 int primary key, c2 int, c3 int);
+drop table t1,t2;
+
+# data type support
+create table t1 (c1 bit);
+desc t1;
+drop table t1;
+create table t1 (c1 tinyint);
+desc t1;
+drop table t1;
+create table t1 (c1 smallint);
+desc t1;
+drop table t1;
+create table t1 (c1 mediumint);
+desc t1;
+drop table t1;
+create table t1 (c1 int);
+desc t1;
+drop table t1;
+create table t1 (c1 bigint);
+desc t1;
+drop table t1;
+create table t1 (c1 double);
+desc t1;
+drop table t1;
+create table t1 (c1 float);
+desc t1;
+drop table t1;
+create table t1 (c1 decimal);
+desc t1;
+drop table t1;
+create table t1 (c1 date);
+desc t1;
+drop table t1;
+create table t1 (c1 time);
+desc t1;
+drop table t1;
+create table t1 (c1 timestamp);
+desc t1;
+drop table t1;
+create table t1 (c1 datetime);
+desc t1;
+drop table t1;
+create table t1 (c1 year);
+desc t1;
+drop table t1;
+create table t1 (c1 char(10));
+desc t1;
+drop table t1;
+create table t1 (c1 varchar(10));
+desc t1;
+drop table t1;
+create table t1 (c1 binary(10));
+desc t1;
+drop table t1;
+create table t1 (c1 varbinary(10));
+desc t1;
+drop table t1;
+create table t1 (c1 tinyblob);
+desc t1;
+drop table t1;
+create table t1 (c1 blob);
+desc t1;
+drop table t1;
+create table t1 (c1 mediumblob);
+desc t1;
+drop table t1;
+create table t1 (c1 longblob);
+desc t1;
+drop table t1;
+create table t1 (c1 tinytext);
+desc t1;
+drop table t1;
+create table t1 (c1 text);
+desc t1;
+drop table t1;
+create table t1 (c1 mediumtext);
+desc t1;
+drop table t1;
+create table t1 (c1 longtext);
+desc t1;
+drop table t1;
+create table t1 (c1 enum("yes","no"));
+desc t1;
+drop table t1;
+create table t1 (c1 set("A","B","AB","O"));
+desc t1;
+drop table t1;
+
+# virtual columns
+create table t1 (c1 int, `_id` int) engine = mroonga;
+desc t1;
+drop table t1;
+
+# error
+--error ER_CANT_CREATE_TABLE
+create table t1 (c1 int, `_score` float) engine = mroonga;
+
+# checking for virtual columns
+--error ER_CANT_CREATE_TABLE
+create table t1 (c1 int, `_id` text) engine = mroonga;
+--error ER_CANT_CREATE_TABLE
+create table t1 (c1 int, `_id` int, index(`_id`)) engine = mroonga;
+
+# index for _id
+--error ER_CANT_CREATE_TABLE
+create table t1 (_id int, c1 int, primary key (_id));
+create table t1 (_id int, c1 int, primary key (_id) using hash);
+drop table t1;
+--error ER_CANT_CREATE_TABLE
+create table t1 (_id int, c1 int, unique key (_id));
+create table t1 (_id int, c1 int, unique key (_id) using hash);
+drop table t1;
+--error ER_CANT_CREATE_TABLE
+create table t1 (_id int, c1 int, key (_id));
+create table t1 (_id int, c1 int, key (_id) using hash);
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/create_table_comment_normal.test b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_comment_normal.test
new file mode 100644
index 00000000000..272485d3453
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_comment_normal.test
@@ -0,0 +1,34 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS bugs;
+--enable_warnings
+
+CREATE TABLE bugs (
+ id INT UNSIGNED
+) DEFAULT CHARSET=utf8
+ COMMENT='Free style normal comment';
+
+SHOW CREATE TABLE bugs;
+
+DROP TABLE bugs;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/create_table_default_tokenizer.test b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_default_tokenizer.test
new file mode 100644
index 00000000000..ee1afdfe4f6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_default_tokenizer.test
@@ -0,0 +1,36 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS tags;
+--enable_warnings
+
+CREATE TABLE tags (
+ name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+ COLLATE=utf8_bin
+ COMMENT='default_tokenizer "TokenDelimit"';
+
+SELECT mroonga_command("dump");
+
+DROP TABLE tags;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/create_table_normalizer_fulltext_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_normalizer_fulltext_index.test
new file mode 100644
index 00000000000..02e2cb9e81a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_normalizer_fulltext_index.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+
+CREATE TABLE diaries (
+ day DATE PRIMARY KEY,
+ content VARCHAR(64) NOT NULL,
+ FULLTEXT INDEX (content) COMMENT 'normalizer "NormalizerAuto"'
+) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+INSERT INTO diaries VALUES ("2013-04-23", "ブラックコーヒーを飲んだ。");
+
+SELECT * FROM diaries
+ WHERE MATCH (content) AGAINST ("+ふらつく" IN BOOLEAN MODE);
+SELECT * FROM diaries
+ WHERE MATCH (content) AGAINST ("+ブラック" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/create_table_normalizer_no_utf8_charset_with_utf8_normalizer.test b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_normalizer_no_utf8_charset_with_utf8_normalizer.test
new file mode 100644
index 00000000000..f28fb5b8695
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_normalizer_no_utf8_charset_with_utf8_normalizer.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES latin1;
+
+CREATE TABLE diaries (
+ day DATE PRIMARY KEY,
+ content VARCHAR(64) NOT NULL,
+ FULLTEXT INDEX (content) COMMENT 'normalizer "NormalizerMySQLGeneralCI"'
+) DEFAULT CHARSET=latin1;
+
+--error ER_ERROR_ON_WRITE
+INSERT INTO diaries VALUES ("2013-04-23", "I drunk a black cookie.");
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/create_table_reference_type.test b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_reference_type.test
new file mode 100644
index 00000000000..eb1095e75a7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_reference_type.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_query_log
+DROP DATABASE test;
+CREATE DATABASE test;
+USE test;
+--enable_query_log
+
+CREATE TABLE tags (
+ name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tag VARCHAR(64) COMMENT 'type "tags"'
+) DEFAULT CHARSET=utf8;
+
+SELECT mroonga_command("dump");
+
+DROP TABLE bugs;
+DROP TABLE tags;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/create_table_vector.test b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_vector.test
new file mode 100644
index 00000000000..440619c33c0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/create_table_vector.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS bugs;
+--enable_warnings
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tags TEXT COMMENT 'flags "COLUMN_VECTOR"'
+) DEFAULT CHARSET=utf8;
+
+SELECT mroonga_command("dump");
+
+DROP TABLE bugs;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/delete_fulltext_column.test b/storage/mroonga/mysql-test/mroonga/storage/t/delete_fulltext_column.test
new file mode 100644
index 00000000000..a43f5c6511b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/delete_fulltext_column.test
@@ -0,0 +1,36 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 text, fulltext index (c2));
+insert into t1 values(10, "aa ii uu ee");
+insert into t1 values(20, "ka ki ku ke");
+insert into t1 values(30, "sa si su se");
+
+select * from t1;
+select * from t1 where match(c2) against("ki");
+delete from t1 where c1=20;
+select * from t1;
+select * from t1 where match(c2) against("ki");
+
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/delete_index_btree_many_records.test b/storage/mroonga/mysql-test/mroonga/storage/t/delete_index_btree_many_records.test
new file mode 100644
index 00000000000..224929fe308
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/delete_index_btree_many_records.test
@@ -0,0 +1,46 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE ids (
+ id INT,
+ KEY (id)
+) DEFAULT CHARSET UTF8;
+
+INSERT INTO ids VALUES(1);
+INSERT INTO ids VALUES(2);
+INSERT INTO ids VALUES(3);
+
+SELECT * FROM ids ORDER BY id;
+
+DELETE FROM ids WHERE id = 1;
+SELECT * FROM ids ORDER BY id;
+
+DELETE FROM ids WHERE id = 2;
+SELECT * FROM ids ORDER BY id;
+
+DELETE FROM ids WHERE id = 3;
+SELECT * FROM ids ORDER BY id;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/delete_index_hash_id_no_unique.test b/storage/mroonga/mysql-test/mroonga/storage/t/delete_index_hash_id_no_unique.test
new file mode 100644
index 00000000000..a23ca3bae5c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/delete_index_hash_id_no_unique.test
@@ -0,0 +1,33 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (_id int, c1 int, key (_id) using hash);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+select * from t1;
+delete from t1 where _id = 2;
+select * from t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/delete_index_hash_id_unique.test b/storage/mroonga/mysql-test/mroonga/storage/t/delete_index_hash_id_unique.test
new file mode 100644
index 00000000000..559b3a15be6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/delete_index_hash_id_unique.test
@@ -0,0 +1,33 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (_id int, c1 int, unique key (_id) using hash);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+select * from t1;
+delete from t1 where _id = 2;
+select * from t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/delete_normal_column.test b/storage/mroonga/mysql-test/mroonga/storage/t/delete_normal_column.test
new file mode 100644
index 00000000000..2f94a8242d3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/delete_normal_column.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int, c2 int);
+show create table t1;
+insert into t1 values (1, 100);
+insert into t1 values (2, 101);
+insert into t1 values (3, 102);
+insert into t1 values (4, 102);
+select * from t1;
+
+delete from t1 where c1=3;
+select * from t1;
+
+flush tables;
+
+delete from t1 where c1=2;
+select * from t1;
+
+delete from t1;
+select * from t1;
+
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/delete_unsigned_bigint.test b/storage/mroonga/mysql-test/mroonga/storage/t/delete_unsigned_bigint.test
new file mode 100644
index 00000000000..e174710934b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/delete_unsigned_bigint.test
@@ -0,0 +1,37 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS numbers;
+--enable_warnings
+
+CREATE TABLE numbers (
+ data BIGINT UNSIGNED
+);
+
+INSERT INTO numbers VALUES(18446744073709551615);
+
+SELECT * FROM numbers ORDER BY data;
+
+DELETE FROM numbers WHERE data = 18446744073709551615;
+SELECT * FROM numbers ORDER BY data;
+
+DROP TABLE numbers;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/drop_database_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/storage/t/drop_database_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..74f840944f9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/drop_database_TODO_SPLIT_ME.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop database if exists groonga;
+--enable_warnings
+
+create database groonga;
+drop database groonga;
+
+create database groonga;
+use groonga;
+create table t1 (c1 int primary key, c2 varchar(255)) default charset utf8;
+create table t2 (c1 int primary key, c2 varchar(255)) default charset utf8;
+drop database groonga;
+
+create database groonga;
+use groonga;
+create table t1 (c1 int primary key, c2 varchar(255)) default charset utf8;
+create table t2 (c1 int primary key, c2 varchar(255)) default charset utf8;
+drop table t1, t2;
+drop database groonga;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/drop_table_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/storage/t/drop_table_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..a057f328453
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/drop_table_TODO_SPLIT_ME.test
@@ -0,0 +1,29 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists alphabet, `with-hyphen`;
+--enable_warnings
+
+create table alphabet (c1 int primary key, c2 int, c3 int);
+drop table alphabet;
+
+create table `with-hyphen` (c1 int primary key, c2 int, c3 int);
+drop table `with-hyphen`;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/flush_logs.test b/storage/mroonga/mysql-test/mroonga/storage/t/flush_logs.test
new file mode 100644
index 00000000000..964217ae713
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/flush_logs.test
@@ -0,0 +1,21 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+flush logs;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/foreign_key_create.test b/storage/mroonga/mysql-test/mroonga/storage/t/foreign_key_create.test
new file mode 100644
index 00000000000..0ab06083b7d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/foreign_key_create.test
@@ -0,0 +1,118 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_version_56.inc
+--source ../../include/mroonga/check_mariadb.inc
+
+if ($mariadb)
+{
+ skip This test is for MySQL version 5.6.x;
+}
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists articles2;
+drop table if exists articles;
+drop table if exists comments2;
+drop table if exists comments;
+--enable_warnings
+
+create table comments(
+ comment int unsigned,
+ content text not null,
+ primary key(comment)
+);
+
+create table articles(
+ content text not null,
+ comment int unsigned,
+ FOREIGN KEY (comment) REFERENCES comments (comment)
+);
+
+insert into comments (comment, content) values
+(1, 'aaa bbb'),(2, 'ccc ddd'),(3, 'eee fff');
+
+insert into articles (content, comment) values
+('111aaa', 1),('222bbb', 2),('222ccc', 2);
+
+select comment, content from comments;
+
+select content, comment from articles;
+
+show create table comments;
+
+show create table articles;
+
+select * from information_schema.referential_constraints;
+
+rename table comments to comments2;
+rename table articles to articles2;
+
+create table comments(
+ comment int unsigned,
+ content text not null,
+ primary key(comment)
+);
+
+create table articles(
+ content text not null,
+ comment int unsigned,
+ FOREIGN KEY (comment) REFERENCES comments (comment)
+);
+
+insert into comments (comment, content) values
+(1, 'ab'),(2, 'cd'),(3, 'ef');
+
+insert into articles (content, comment) values
+('1a', 1),('2b', 2),('2c', 2);
+
+select comment, content from comments;
+
+select content, comment from articles;
+
+select comment, content from comments2;
+
+select content, comment from articles2;
+
+show create table comments;
+
+show create table articles;
+
+show create table comments2;
+
+show create table articles2;
+
+select * from information_schema.referential_constraints;
+
+alter table articles drop foreign key comment;
+
+show create table articles;
+
+select content, comment from articles;
+
+alter table articles add FOREIGN KEY (comment) REFERENCES comments (comment);
+
+show create table articles;
+
+select content, comment from articles;
+
+drop table articles2;
+drop table articles;
+drop table comments2;
+drop table comments;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_empty_query.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_empty_query.test
new file mode 100644
index 00000000000..75f1c3c38ed
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_empty_query.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries(
+ title TEXT,
+ FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+
+SELECT * FROM diaries;
+
+SELECT *
+ FROM diaries
+ WHERE MATCH(title) AGAINST("" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_escape.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_escape.test
new file mode 100644
index 00000000000..1a27422aa81
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_escape.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET GLOBAL mroonga_default_parser = TokenDelimit;
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=UTF8;
+
+INSERT INTO memos VALUES(1, "(groonga) Installed!");
+INSERT INTO memos VALUES(2, "(mroonga) Installed!");
+INSERT INTO memos VALUES(3, "(groonga) Upgraded!");
+
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("\\(groonga\\)*" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+SET GLOBAL mroonga_default_parser = TokenBigram;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_leading_not.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_leading_not.test
new file mode 100644
index 00000000000..952749338ac
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_leading_not.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT PRIMARY KEY,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("-明日 +天気" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.test
new file mode 100644
index 00000000000..89adbd4b5f7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2013 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Yesterday was good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+INSERT INTO memos VALUES ("Tomorrow will be fine.");
+INSERT INTO memos VALUES ("Yesterday was fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*D- fine is be" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_minus_with_or.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_minus_with_or.test
new file mode 100644
index 00000000000..76a72a5b865
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_minus_with_or.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2013 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Yesterday was good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+INSERT INTO memos VALUES ("Tomorrow will be fine.");
+INSERT INTO memos VALUES ("Yesterday was fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*D- is OR be fine" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.test
new file mode 100644
index 00000000000..e2edfe780b3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2013 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Yesterday was good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+INSERT INTO memos VALUES ("Tomorrow will be fine.");
+INSERT INTO memos VALUES ("Yesterday was fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*D- good +day be" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_or_no_operator.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_or_no_operator.test
new file mode 100644
index 00000000000..4d0f15b203a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_or_no_operator.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2013 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+INSERT INTO memos VALUES ("Tomorrow will be fine.");
+INSERT INTO memos VALUES ("Yesterday was fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*DOR today good" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_or_with_minus.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_or_with_minus.test
new file mode 100644
index 00000000000..ed93b543869
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_or_with_minus.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2013 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+INSERT INTO memos VALUES ("Tomorrow will be fine.");
+INSERT INTO memos VALUES ("Yesterday was fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*DOR today -good tomorrow" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_or_with_plus.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_or_with_plus.test
new file mode 100644
index 00000000000..3a078c7eccb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_or_with_plus.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2013 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+INSERT INTO memos VALUES ("Tomorrow will be fine.");
+INSERT INTO memos VALUES ("Yesterday was fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*DOR today +good tomorrow" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.test
new file mode 100644
index 00000000000..fb2ec74a5e0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2013 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*D+ today good" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.test
new file mode 100644
index 00000000000..30cf3e87491
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2013 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*D+ today -good is" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_plus_with_or.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_plus_with_or.test
new file mode 100644
index 00000000000..7268cc1921c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_default_operator_plus_with_or.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2013 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO memos VALUES ("Today is good day.");
+INSERT INTO memos VALUES ("Tomorrow will be good day.");
+INSERT INTO memos VALUES ("Today is fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*D+ today OR tomorrow day" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_full_spec.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_full_spec.test
new file mode 100644
index 00000000000..bec7944a82b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_full_spec.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id INT PRIMARY KEY,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX (title, content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+
+SELECT *, MATCH(title, content)
+ AGAINST("*W1:10,2:2 +天気" in BOOLEAN MODE) AS score
+ FROM diaries
+ WHERE MATCH(title, content)
+ AGAINST("*W1:10,2:2 +天気" in BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_no_weight.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_no_weight.test
new file mode 100644
index 00000000000..fa10ae7b73b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_no_weight.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id INT PRIMARY KEY,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX (title, content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+
+SELECT *, MATCH(title, content)
+ AGAINST("*W1,2:2 +天気" in BOOLEAN MODE) AS score
+ FROM diaries
+ WHERE MATCH(title, content)
+ AGAINST("*W1,2:2 +天気" in BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_omit_section.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_omit_section.test
new file mode 100644
index 00000000000..d7f1bde6ca4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_omit_section.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id INT PRIMARY KEY,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX (title, content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+
+SELECT *, MATCH(title, content)
+ AGAINST("*W1:2 +天気" in BOOLEAN MODE) AS score
+ FROM diaries
+ WHERE MATCH(title, content)
+ AGAINST("*W1:2 +天気" in BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_ten_or_more_sections.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_ten_or_more_sections.test
new file mode 100644
index 00000000000..6df0c13f111
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_ten_or_more_sections.test
@@ -0,0 +1,63 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ title VARCHAR(255),
+ tag1 VARCHAR(10),
+ tag2 VARCHAR(10),
+ tag3 VARCHAR(10),
+ tag4 VARCHAR(10),
+ tag5 VARCHAR(10),
+ tag6 VARCHAR(10),
+ tag7 VARCHAR(10),
+ tag8 VARCHAR(10),
+ tag9 VARCHAR(10),
+ tag10 VARCHAR(10),
+ FULLTEXT INDEX (tag1, tag2, tag3, tag4, tag5, tag6, tag7, tag8, tag9, tag10)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO memos
+ VALUES("Groonga",
+ "tag 1",
+ "tag 2",
+ "tag 3",
+ "tag 4",
+ "tag 5",
+ "tag 6",
+ "tag 7",
+ "tag 8",
+ "tag 9",
+ "tag 10");
+
+SELECT title,
+ MATCH(tag1, tag2, tag3, tag4, tag5, tag6, tag7, tag8, tag9, tag10)
+ AGAINST("*W1:2,2:4,3:6,4:8,5:10,6:12,7:14,8:16,9:18,10:20 +tag"
+ in BOOLEAN MODE) AS score
+ FROM memos
+ WHERE MATCH(tag1, tag2, tag3, tag4, tag5, tag6, tag7, tag8, tag9, tag10)
+ AGAINST("*W1:2,2:4,3:6,4:8,5:10,6:12,7:14,8:16,9:18,10:20 +tag"
+ in BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_three_or_more_sections.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_three_or_more_sections.test
new file mode 100644
index 00000000000..9161ff6ff0d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_pragma_weight_three_or_more_sections.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id INT PRIMARY KEY,
+ title VARCHAR(255),
+ category VARCHAR(10),
+ content TEXT,
+ FULLTEXT INDEX (title, category, content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES(1, "Hello", "日記", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気予報", "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "天気", "今日も天気がよくてきれいに見える。");
+
+SELECT *, MATCH(title, category, content)
+ AGAINST("*W1:2,2:10,3:1 +天気" in BOOLEAN MODE) AS score
+ FROM diaries
+ WHERE MATCH(title, category, content)
+ AGAINST("*W1:2,2:10,3:1 +天気" in BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_error.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_error.test
new file mode 100644
index 00000000000..f8d5e3fe6be
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_error.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET GLOBAL mroonga_default_parser = TokenDelimit;
+SET mroonga_action_on_fulltext_query_error = ERROR;
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=UTF8;
+
+INSERT INTO memos VALUES(1, "(groonga) Installed!");
+INSERT INTO memos VALUES(2, "(mroonga) Installed!");
+INSERT INTO memos VALUES(3, "(groonga) Upgraded!");
+
+-- error ER_PARSE_ERROR
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("(groonga" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+SET GLOBAL mroonga_default_parser = TokenBigram;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_error_and_log.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_error_and_log.test
new file mode 100644
index 00000000000..1c0795bca58
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_error_and_log.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET GLOBAL mroonga_default_parser = TokenDelimit;
+SET mroonga_action_on_fulltext_query_error = ERROR_AND_LOG;
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=UTF8;
+
+INSERT INTO memos VALUES(1, "(groonga) Installed!");
+INSERT INTO memos VALUES(2, "(mroonga) Installed!");
+INSERT INTO memos VALUES(3, "(groonga) Upgraded!");
+
+-- error ER_PARSE_ERROR
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("(groonga" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+SET GLOBAL mroonga_default_parser = TokenBigram;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_ignore.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_ignore.test
new file mode 100644
index 00000000000..fef386cc42d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_ignore.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET GLOBAL mroonga_default_parser = TokenDelimit;
+SET mroonga_action_on_fulltext_query_error = "IGNORE";
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=UTF8;
+
+INSERT INTO memos VALUES(1, "(groonga) Installed!");
+INSERT INTO memos VALUES(2, "(mroonga) Installed!");
+INSERT INTO memos VALUES(3, "(groonga) Upgraded!");
+
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("(groonga" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+SET GLOBAL mroonga_default_parser = TokenBigram;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_ignore_and_log.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_ignore_and_log.test
new file mode 100644
index 00000000000..ac51f81a43a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_boolean_mode_syntax_error_ignore_and_log.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET GLOBAL mroonga_default_parser = TokenDelimit;
+SET mroonga_action_on_fulltext_query_error = IGNORE_AND_LOG;
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=UTF8;
+
+INSERT INTO memos VALUES(1, "(groonga) Installed!");
+INSERT INTO memos VALUES(2, "(mroonga) Installed!");
+INSERT INTO memos VALUES(3, "(groonga) Upgraded!");
+
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("(groonga" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+SET GLOBAL mroonga_default_parser = TokenBigram;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_ascii.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_ascii.test
new file mode 100644
index 00000000000..42ef563105d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_ascii.test
@@ -0,0 +1,36 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3));
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"sa si su se so");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ee oo");
+select * from t1;
+select * from t1 where match(c3) against("su");
+select * from t1 where match(c3) against("ii");
+select * from t1 where match(c3) against("+su" in boolean mode);
+select * from t1 where match(c3) against("+ii" in boolean mode);
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_cp932.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_cp932.test
new file mode 100644
index 00000000000..7396a66ecc0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_cp932.test
@@ -0,0 +1,34 @@
+# Copyright(C) 2011 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_cp932.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+set names cp932;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset cp932;
+insert into t1 values(1, "̕xmR̓VCɂ‚","");
+insert into t1 values(2, "","̕xmR̓VC͕܂");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+select * from t1 where match(c2) against("xmR");
+select * from t1 where match(c3) against("xmR");
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_eucjpms.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_eucjpms.test
new file mode 100644
index 00000000000..2d8b094c43d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_eucjpms.test
@@ -0,0 +1,34 @@
+# Copyright(C) 2011 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_eucjpms.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+set names eucjpms;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset eucjpms;
+insert into t1 values(1, "ٻλŷˤĤ","");
+insert into t1 values(2, "","ٻλŷʬޤ");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+select * from t1 where match(c2) against("ٻλ");
+select * from t1 where match(c3) against("ٻλ");
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_japanese.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_japanese.test
new file mode 100644
index 00000000000..4b6ca756a29
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_japanese.test
@@ -0,0 +1,33 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+set names utf8;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset utf8;
+insert into t1 values(1, "明日の富士山の天気について","あああああああ");
+insert into t1 values(2, "いいいいい","明日の富士山の天気は分かりません");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+select * from t1 where match(c2) against("富士山");
+select * from t1 where match(c3) against("富士山");
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_utf8mb4.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_utf8mb4.test
new file mode 100644
index 00000000000..d870affba4f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_charset_utf8mb4.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8mb4;
+CREATE TABLE diaries (
+ id INT PRIMARY KEY,
+ title VARCHAR(255) CHARSET utf8mb4 COLLATE utf8mb4_general_ci,
+ content TEXT CHARSET utf8mb4 COLLATE utf8mb4_general_ci,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET utf8mb4;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, "Alphabet", "ABCDE");
+INSERT INTO diaries VALUES(2, "Mathmatics", "𝐀𝐁𝐂𝐃𝐄 | U+1D400-U+1D405");
+INSERT INTO diaries VALUES(3, "ひらがな", "あいうえお");
+
+SELECT *
+ FROM diaries
+ WHERE MATCH (content) AGAINST("ABCDE" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_empty_query.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_empty_query.test
new file mode 100644
index 00000000000..59a274f942c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_empty_query.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries(
+ title TEXT,
+ FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+
+SELECT * FROM diaries;
+
+SELECT *
+ FROM diaries
+ WHERE MATCH(title) AGAINST("");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_found_rows.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_found_rows.test
new file mode 100644
index 00000000000..4d899201fb3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_found_rows.test
@@ -0,0 +1,49 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 11, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(6, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT SQL_CALC_FOUND_ROWS * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE) ORDER BY day LIMIT 0,5;
+
+SELECT FOUND_ROWS();
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_groonga_varchar_vector.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_groonga_varchar_vector.test
new file mode 100644
index 00000000000..19f82f644a6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_groonga_varchar_vector.test
@@ -0,0 +1,47 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS bugs, tags;
+--enable_warnings
+
+CREATE TABLE tags (
+ name VARCHAR(64) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+ COLLATE=utf8_bin
+ COMMENT='default_tokenizer "TokenDelimit"';
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tags VARCHAR(40) COMMENT 'type "tags", flags "COLUMN_VECTOR"',
+ FULLTEXT INDEX bugs_tags_index (tags) COMMENT 'table "tags"'
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO bugs (id, tags) VALUES (1, "Linux MySQL");
+INSERT INTO bugs (id, tags) VALUES (2, "MySQL groonga");
+INSERT INTO bugs (id, tags) VALUES (3, "mroonga");
+
+SELECT *
+ FROM bugs
+ WHERE MATCH (tags) AGAINST ("MySQL" IN BOOLEAN MODE);
+
+DROP TABLE bugs, tags;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_index_recreate.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_index_recreate.test
new file mode 100644
index 00000000000..a8d4780fee3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_index_recreate.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set names utf8;
+create table diaries (
+ id int primary key,
+ title varchar(255),
+ content text,
+ fulltext index (title)
+) default charset utf8;
+show create table diaries;
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+select * from diaries where match(title) against("富士山");
+drop index title on diaries;
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+select * from diaries where match(title) against("富士山");
+select * from diaries;
+create fulltext index new_title_index on diaries (title);
+select * from diaries where match(title) against("富士山");
+select * from diaries;
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_insert_select.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_insert_select.test
new file mode 100644
index 00000000000..641bbc3c4bd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_insert_select.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 varchar(100), fulltext index(c2)) default charset utf8;
+create table t2 (c1 int primary key, c2 text, fulltext index(c2)) default charset utf8;
+insert into t1 values (1, "aa ii uu ee oo");
+insert into t1 values (2, "ka ki ku ke ko");
+insert into t1 values (3, "aa ii ii ii oo");
+insert into t1 values (4, "sa si su se so");
+insert into t1 values (5, "ta ti ii ii to");
+insert into t2 (c1,c2) select c1,c2 from t1;
+select * from t1;
+select * from t2;
+select * from t1 where c1=3;
+select * from t2 where c1=3;
+select * from t1 where c1>3 order by c1 desc;
+select * from t2 where c1>3 order by c1 asc;
+select * from t1 where c2>"s" order by c2 desc;
+select * from t2 where c2>"s" order by c1 asc;
+select * from t1 where match(c2) against("ii") order by match(c2) against("ii") desc;
+select * from t2 where match(c2) against("ii") order by match(c2) against("ii") asc;
+select c1,c2,match(c2) against("ii") from t1 where match(c2) against("ii");
+select c1,c2,match(c2) against("ii") from t2 where match(c2) against("ii");
+drop table t1,t2;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_insert_values.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_insert_values.test
new file mode 100644
index 00000000000..0b52448bc45
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_insert_values.test
@@ -0,0 +1,33 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 text, fulltext index ft (c2));
+show create table t1;
+insert into t1 values (1, "hoge hoge");
+insert into t1 values (2, "fuga fuga");
+insert into t1 values (3, "moge moge");
+select * from t1;
+flush tables;
+select * from t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_delete.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_delete.test
new file mode 100644
index 00000000000..9c083f57a80
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_delete.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set names utf8;
+create table diaries (
+ id int primary key,
+ title varchar(255),
+ content text,
+ fulltext index (title, content),
+ fulltext index (title),
+ fulltext index (content)
+) default charset utf8;
+show create table diaries;
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+delete from diaries where id = 2;
+select * from diaries where match(title, content) against("富士山");
+select * from diaries where match(title) against("富士山");
+select * from diaries where match(content) against("富士山");
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_insert.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_insert.test
new file mode 100644
index 00000000000..ce2c2714e31
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_insert.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set names utf8;
+create table diaries (
+ id int primary key,
+ title varchar(255),
+ content text,
+ fulltext index (title, content),
+ fulltext index (title),
+ fulltext index (content)
+) default charset utf8;
+show create table diaries;
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+select * from diaries;
+select * from diaries where match(title, content) against("富士山");
+select * from diaries where match(title) against("富士山");
+select * from diaries where match(content) against("富士山");
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_recreate.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_recreate.test
new file mode 100644
index 00000000000..ffee108932a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_recreate.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set names utf8;
+create table diaries (
+ id int primary key,
+ title varchar(255),
+ content text,
+ fulltext index (title, content),
+ fulltext index (title),
+ fulltext index (content)
+) default charset utf8;
+show create table diaries;
+
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+select * from diaries where match(title, content) against("富士山");
+
+drop index title on diaries;
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+select * from diaries where match(title, content) against("富士山");
+
+create fulltext index new_title_content_index on diaries (title, content);
+select * from diaries where match(title, content) against("富士山");
+
+select * from diaries;
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_update.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_update.test
new file mode 100644
index 00000000000..e9e6aa2bb6e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_column_index_update.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set names utf8;
+create table diaries (
+ id int primary key,
+ title varchar(255),
+ content text,
+ fulltext index (title, content),
+ fulltext index (title),
+ fulltext index (content)
+) default charset utf8;
+show create table diaries;
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+update diaries set title = "チョモランマ" where id = 3;
+update diaries set content = "チョモランマと富士山" where id = 1;
+select * from diaries where match(title, content) against("富士山");
+select * from diaries where match(title) against("富士山");
+select * from diaries where match(content) against("富士山");
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_index.test
new file mode 100644
index 00000000000..19fd4f8d3ba
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_multiple_index.test
@@ -0,0 +1,47 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+create table diaries (
+ id int primary key auto_increment,
+ title text,
+ body text,
+ fulltext index title_index (title),
+ fulltext index body_index (body)
+) default charset utf8;
+show create table diaries;
+
+insert into diaries (title, body) values ("survey", "will start groonga!");
+insert into diaries (title, body) values ("groonga (1)", "starting groonga...");
+insert into diaries (title, body) values ("groonga (2)", "started groonga.");
+
+select * from diaries
+ where match(title) against("survey") and
+ match(body) against("groonga");
+
+select *, match(title) against("survey"), match(body) against("groonga")
+ from diaries
+ where match(title) against("survey") and
+ match(body) against("groonga");
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_no_primary_key.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_no_primary_key.test
new file mode 100644
index 00000000000..7ef05efa3a5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_no_primary_key.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES("Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES("天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES("富士山", "今日も天気がよくてきれいに見える。");
+
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("*D+ 今日 天気" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_not_match_against.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_not_match_against.test
new file mode 100644
index 00000000000..7b4e62ba0a9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_not_match_against.test
@@ -0,0 +1,46 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+# for "not match against"
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3));
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,10,"ka ki ku ke ko");
+insert into t1 values(3,10,"aa ii uu ee oo");
+insert into t1 values(4,10,"ka ki ku ke ko");
+insert into t1 values(5,20,"aa ii uu ee oo");
+insert into t1 values(6,20,"ka ki ku ke ko");
+insert into t1 values(7,20,"aa ii uu ee oo");
+insert into t1 values(8,20,"ka ki ku ke ko");
+select * from t1;
+select * from t1 where match(c3) against("uu");
+select * from t1 where not match(c3) against("uu");
+select * from t1 where match(c3) against("dummy");
+select * from t1 where not match(c3) against("dummy");
+select * from t1 where c1 = 4 and not match(c3) against("uu");
+select * from t1 where c1 <= 4 and not match(c3) against("uu");
+select * from t1 where c1 > 4 and not match(c3) against("uu");
+select * from t1 where c2 = 10 and not match(c3) against("uu");
+select * from t1 where c2 >= 15 and not match(c3) against("uu");
+select * from t1 where c2 < 15 and not match(c3) against("uu");
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_or.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_or.test
new file mode 100644
index 00000000000..2b0f3bbb15c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_or.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries(
+ title TEXT,
+ FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+
+SELECT * FROM diaries;
+
+SELECT *
+ FROM diaries
+ WHERE MATCH(title) AGAINST("Ruby" IN BOOLEAN MODE) OR
+ MATCH(title) AGAINST("mroonga" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_different_against.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_different_against.test
new file mode 100644
index 00000000000..f2859f99dba
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_different_against.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries(
+ title TEXT,
+ FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+
+SELECT * FROM diaries;
+
+SELECT *, MATCH(title) AGAINST("groonga" IN BOOLEAN MODE) AS score
+ FROM diaries
+ WHERE MATCH(title) AGAINST("groonga mroonga" IN BOOLEAN MODE)
+ ORDER BY MATCH(title) AGAINST("groonga" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_different_match.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_different_match.test
new file mode 100644
index 00000000000..dc97db87487
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_different_match.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2012 Kouhei Sutou
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries(
+ title TEXT,
+ body TEXT,
+ FULLTEXT KEY (title),
+ FULLTEXT KEY (body)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES("Start groonga", "I read groonga's tutorial.");
+INSERT INTO diaries VALUES("Start mroonga", "I read mroonga's tutorial.");
+INSERT INTO diaries VALUES("Start groonga and Ruby", "I installed rroonga.");
+
+SELECT * FROM diaries;
+
+SELECT *, MATCH(body) AGAINST("groonga" IN BOOLEAN MODE) AS score
+ FROM diaries
+ WHERE MATCH(title) AGAINST("groonga" IN BOOLEAN MODE)
+ ORDER BY MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_no_where.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_no_where.test
new file mode 100644
index 00000000000..50dd652e2d0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_no_where.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012 Kouhei Sutou
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries(
+ title TEXT,
+ FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+
+SELECT * FROM diaries;
+
+SELECT *, MATCH(title) AGAINST("groonga" IN BOOLEAN MODE) AS score
+ FROM diaries
+ ORDER BY MATCH(title) AGAINST("groonga" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_same_match_against.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_same_match_against.test
new file mode 100644
index 00000000000..0873849a1e7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_order_same_match_against.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries(
+ title TEXT,
+ FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+
+SELECT * FROM diaries;
+
+SELECT *, MATCH(title) AGAINST("groonga" IN BOOLEAN MODE) AS score
+ FROM diaries
+ WHERE MATCH(title) AGAINST("groonga" IN BOOLEAN MODE)
+ ORDER BY MATCH(title) AGAINST("groonga" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_parser_comment.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_parser_comment.test
new file mode 100644
index 00000000000..24cc173485a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_parser_comment.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_fulltext_index_comment.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+create table diaries (
+ id int primary key auto_increment,
+ body text,
+ fulltext index body_index (body)
+ comment 'parser "TokenBigramSplitSymbolAlphaDigit"'
+) default charset utf8;
+show create table diaries;
+insert into diaries (body) values ("will start groonga!");
+insert into diaries (body) values ("starting groonga...");
+insert into diaries (body) values ("started groonga.");
+select * from diaries;
+select * from diaries where match(body) against("start");
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_parser_default.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_parser_default.test
new file mode 100644
index 00000000000..1e08cc46221
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_parser_default.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set @mroonga_default_parser_backup=@@mroonga_default_parser;
+set global mroonga_default_parser=TokenBigramSplitSymbolAlphaDigit;
+create table diaries (
+ id int primary key auto_increment,
+ body text,
+ fulltext index body_index (body)
+) default charset utf8;
+show create table diaries;
+insert into diaries (body) values ("will start groonga!");
+insert into diaries (body) values ("starting groonga...");
+insert into diaries (body) values ("started groonga.");
+insert into diaries (body) values ("finished groonga.");
+select * from diaries;
+select * from diaries where match(body) against("start");
+drop table diaries;
+set global mroonga_default_parser=@mroonga_default_parser_backup;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_parser_off.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_parser_off.test
new file mode 100644
index 00000000000..0dcf494c684
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_parser_off.test
@@ -0,0 +1,48 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_fulltext_index_comment.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS variables;
+--enable_warnings
+
+CREATE TABLE variables (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ name TEXT,
+ FULLTEXT INDEX (name) COMMENT 'parser "off"'
+) DEFAULT CHARSET=utf8;
+SHOW CREATE TABLE variables;
+
+INSERT INTO variables (name) VALUES ("mroonga_database_path_prefix");
+INSERT INTO variables (name) VALUES ("mroonga_default_parser");
+INSERT INTO variables (name) VALUES ("mroonga_default_wrapper_engine");
+INSERT INTO variables (name) VALUES ("mroonga_dry_write");
+INSERT INTO variables (name) VALUES ("mroonga_enable_optimization");
+INSERT INTO variables (name) VALUES ("mroonga_libgroonga_version");
+INSERT INTO variables (name) VALUES ("mroonga_log_file");
+INSERT INTO variables (name) VALUES ("mroonga_log_level");
+INSERT INTO variables (name) VALUES ("mroonga_match_escalation_threshold");
+INSERT INTO variables (name) VALUES ("mroonga_version");
+
+SELECT * FROM variables;
+SELECT * FROM variables
+ WHERE MATCH (name) AGAINST ("mroonga_default*" IN BOOLEAN MODE);
+
+DROP TABLE variables;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_two_inner_join.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_two_inner_join.test
new file mode 100644
index 00000000000..745720d1fe6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_two_inner_join.test
@@ -0,0 +1,69 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS users, posts, comments;
+--enable_warnings
+
+SET NAMES utf8;
+
+CREATE TABLE users (
+ id int NOT NULL,
+ name varchar(50) NOT NULL,
+ PRIMARY KEY (id),
+ KEY (name)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE posts (
+ id int NOT NULL,
+ content mediumtext,
+ user_id int NOT NULL,
+ PRIMARY KEY (id),
+ FULLTEXT KEY (content)
+) DEFAULT CHARSET=utf8;
+
+CREATE TABLE comments (
+ id int NOT NULL,
+ user_id int NOT NULL,
+ post_id int NOT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+INSERT INTO users VALUES (1, "Alice"),
+ (2, "Bob"),
+ (3, "Calros");
+INSERT INTO posts VALUES (1, "Hello!", 1),
+ (2, "World!", 2),
+ (3, "Great!", 3);
+INSERT INTO comments VALUES (1, 1, 1),
+ (2, 2, 1),
+ (3, 3, 3);
+
+SELECT *
+ FROM comments
+ INNER JOIN posts
+ ON posts.id = comments.post_id AND
+ MATCH (posts.content) AGAINST ("Hello!" IN BOOLEAN MODE)
+ INNER JOIN users
+ ON users.id = comments.user_id AND
+ users.name = "Alice";
+
+DROP TABLE users, posts, comments;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_version_100_no_such_key.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_version_100_no_such_key.test
new file mode 100644
index 00000000000..aab09c18b79
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_version_100_no_such_key.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_version_100.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT,
+ FULLTEXT INDEX title_index (title),
+ FULLTEXT INDEX body_index (body)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+
+-- error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT * FROM diaries FORCE INDEX(primary)
+ WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_version_55_no_such_key.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_version_55_no_such_key.test
new file mode 100644
index 00000000000..d447b1f85dc
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_version_55_no_such_key.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_version_55.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT,
+ FULLTEXT INDEX title_index (title),
+ FULLTEXT INDEX body_index (body)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+
+SELECT * FROM diaries FORCE INDEX(primary)
+ WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_version_56_no_such_key.test b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_version_56_no_such_key.test
new file mode 100644
index 00000000000..1187b189244
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/fulltext_version_56_no_such_key.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_version_56.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT,
+ FULLTEXT INDEX title_index (title),
+ FULLTEXT INDEX body_index (body)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+
+-- error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT * FROM diaries FORCE INDEX(primary)
+ WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_command_select.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_command_select.test
new file mode 100644
index 00000000000..f1a1777d92b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_command_select.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries(
+ title TEXT,
+ FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+
+SELECT mroonga_command('select diaries --match_columns "title" --query "groonga"');
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_error_query_is_missing.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_error_query_is_missing.test
new file mode 100644
index 00000000000..a37e601e92d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_error_query_is_missing.test
@@ -0,0 +1,27 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+SET NAMES UTF8;
+
+-- error ER_CANT_INITIALIZE_UDF
+SELECT mroonga_escape() AS escaped_query;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_error_query_is_not_string.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_error_query_is_not_string.test
new file mode 100644
index 00000000000..683efb7e25c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_error_query_is_not_string.test
@@ -0,0 +1,27 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+SET NAMES UTF8;
+
+-- error ER_CANT_INITIALIZE_UDF
+SELECT mroonga_escape(29) AS escaped_query;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_error_target_characters_is_not_string.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_error_target_characters_is_not_string.test
new file mode 100644
index 00000000000..5e04ae2a615
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_error_target_characters_is_not_string.test
@@ -0,0 +1,27 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+SET NAMES UTF8;
+
+-- error ER_CANT_INITIALIZE_UDF
+SELECT mroonga_escape('+-><~*()\"\\:', 29) AS escaped_query;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_success_all.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_success_all.test
new file mode 100644
index 00000000000..6d328fa8434
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_success_all.test
@@ -0,0 +1,26 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+SET NAMES UTF8;
+
+SELECT mroonga_escape('+-><~*()\"\\:') AS escaped_query;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_success_custom.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_success_custom.test
new file mode 100644
index 00000000000..2ad633b43f0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_success_custom.test
@@ -0,0 +1,26 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+SET NAMES UTF8;
+
+SELECT mroonga_escape('+-><~*()\"\\:', '()<>~') AS escaped_query;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_success_nested.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_success_nested.test
new file mode 100644
index 00000000000..82f7410d909
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_escape_success_nested.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries(
+ title TEXT,
+ FULLTEXT KEY (title)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES("Start groonga");
+INSERT INTO diaries VALUES("Start mroonga");
+INSERT INTO diaries VALUES("Start groonga and Ruby");
+
+SELECT mroonga_escape(mroonga_escape('*groonga*'));
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_last_insert_grn_id.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_last_insert_grn_id.test
new file mode 100644
index 00000000000..ef6b0077976
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_last_insert_grn_id.test
@@ -0,0 +1,48 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2013 Kentoku SHIBA
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (_id int, c1 int);
+select last_insert_grn_id();
+insert into t1 values(null,100);
+insert into t1 values(null,100);
+select last_insert_grn_id();
+insert into t1 values(null,100);
+insert into t1 values(null,100);
+select last_insert_grn_id();
+insert into t1 values(null,100);
+insert into t1 values(null,100);
+select last_insert_grn_id();
+insert into t1 values(null,100);
+insert into t1 values(null,100);
+select last_insert_grn_id();
+
+--error ER_CANT_INITIALIZE_UDF
+select last_insert_grn_id(1);
+
+drop table t1;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_last_insert_id_reference.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_last_insert_id_reference.test
new file mode 100644
index 00000000000..a515757ab55
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_last_insert_id_reference.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id int AUTO_INCREMENT PRIMARY KEY
+);
+
+SELECT last_insert_id();
+
+INSERT INTO ids VALUES();
+SELECT last_insert_id();
+SELECT * FROM ids;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_last_insert_id_set.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_last_insert_id_set.test
new file mode 100644
index 00000000000..2eb75ac3b60
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_last_insert_id_set.test
@@ -0,0 +1,37 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id int AUTO_INCREMENT PRIMARY KEY
+);
+
+SELECT last_insert_id();
+SELECT last_insert_id(10);
+SELECT last_insert_id();
+
+INSERT INTO ids VALUES();
+SELECT last_insert_id();
+SELECT * FROM ids;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_ascii.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_ascii.test
new file mode 100644
index 00000000000..f273a38c13a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_ascii.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012-2013 Kentoku SHIBA
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3));
+insert into t1 values(1,10,"aa bb cc dd ee >< ff gg hh ii jj kk ll mm nn");
+insert into t1 values(2,20,"nn mm ll kk jj >< ii hh gg ff ee dd cc bb aa");
+insert into t1 values(3,30,"cc dd ee ff gg >< hh ii jj kk ll mm nn oo pp");
+insert into t1 values(4,40,"ee ff gg hh ii >< jj kk ll mm nn oo pp qq rr");
+insert into t1 values(5,50,"AA BB CC DD EE >< FF GG HH II JJ KK LL MM NN");
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_general_ci', 0, 1, '...', '...<br>\n', 'bb', '<span class="w1">', '</span>', 'ff', '<span class="w2">', '</span>', 'dd', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_bin', 0, 1, '...', '...<br>\n', 'bb', '<span class="w1">', '</span>', 'ff', '<span class="w2">', '</span>', 'dd', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_general_ci', 1, 1, '...', '...<br>\n', 'bb', '<span class="w1">', '</span>', 'ff', '<span class="w2">', '</span>', 'dd', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_bin', 1, 1, '...', '...<br>\n', 'bb', '<span class="w1">', '</span>', 'ff', '<span class="w2">', '</span>', 'dd', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_general_ci', 0, 0, '...', '...\n', 'bb', '(w1)[', ']', 'ff', '(w2)[', ']', 'dd', '(w3)[', ']') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_bin', 0, 0, '...', '...\n', 'bb', '(w1)[', ']', 'ff', '(w2)[', ']', 'dd', '(w3)[', ']') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_general_ci', 1, 0, '...', '...\n', 'bb', '(w1)[', ']', 'ff', '(w2)[', ']', 'dd', '(w3)[', ']') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'ascii_bin', 1, 0, '...', '...\n', 'bb', '(w1)[', ']', 'ff', '(w2)[', ']', 'dd', '(w3)[', ']') from t1;
+drop table t1;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_cp932.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_cp932.test
new file mode 100644
index 00000000000..eb67245e7c0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_cp932.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012-2013 Kentoku SHIBA
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source include/have_cp932.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+set names cp932;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset cp932;
+insert into t1 values(1, "","QX̕xmR̓VCɂ‚");
+insert into t1 values(2, "","QX̕xmR̓VC͕܂");
+insert into t1 values(3, "","29̕xmR̓VCɂ‚");
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_japanese_ci', 0, 1, '...', '...<br>\n', 'QX', '<span class="w1">', '</span>', 'VC', '<span class="w2">', '</span>', 'xmR', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_bin', 0, 1, '...', '...<br>\n', 'QX', '<span class="w1">', '</span>', 'VC', '<span class="w2">', '</span>', 'xmR', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_japanese_ci', 1, 1, '...', '...<br>\n', 'QX', '<span class="w1">', '</span>', 'VC', '<span class="w2">', '</span>', 'xmR', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_bin', 1, 1, '...', '...<br>\n', 'QX', '<span class="w1">', '</span>', 'VC', '<span class="w2">', '</span>', 'xmR', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_japanese_ci', 0, 0, '...', '...\n', 'QX', '(w1)[', ']', 'VC', '(w2)[', ']', 'xmR', '(w3)[', ']') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_bin', 0, 0, '...', '...\n', 'QX', '(w1)[', ']', 'VC', '(w2)[', ']', 'xmR', '(w3)[', ']') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_japanese_ci', 1, 0, '...', '...\n', 'QX', '(w1)[', ']', 'VC', '(w2)[', ']', 'xmR', '(w3)[', ']') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'cp932_bin', 1, 0, '...', '...\n', 'QX', '(w1)[', ']', 'VC', '(w2)[', ']', 'xmR', '(w3)[', ']') from t1;
+drop table t1;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_eucjpms.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_eucjpms.test
new file mode 100644
index 00000000000..8864d6adffc
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_eucjpms.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012-2013 Kentoku SHIBA
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source include/have_eucjpms.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+set names eucjpms;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset eucjpms;
+insert into t1 values(1, "","ٻλŷˤĤ");
+insert into t1 values(2, "","ٻλŷʬޤ");
+insert into t1 values(3, "","29ٻλŷˤĤ");
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_japanese_ci', 0, 1, '...', '...<br>\n', '', '<span class="w1">', '</span>', 'ŷ', '<span class="w2">', '</span>', 'ٻλ', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_bin', 0, 1, '...', '...<br>\n', '', '<span class="w1">', '</span>', 'ŷ', '<span class="w2">', '</span>', 'ٻλ', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_japanese_ci', 1, 1, '...', '...<br>\n', '', '<span class="w1">', '</span>', 'ŷ', '<span class="w2">', '</span>', 'ٻλ', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_bin', 1, 1, '...', '...<br>\n', '', '<span class="w1">', '</span>', 'ŷ', '<span class="w2">', '</span>', 'ٻλ', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_japanese_ci', 0, 0, '...', '...\n', '', '(w1)[', ']', 'ŷ', '(w2)[', ']', 'ٻλ', '(w3)[', ']') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_bin', 0, 0, '...', '...\n', '', '(w1)[', ']', 'ŷ', '(w2)[', ']', 'ٻλ', '(w3)[', ']') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_japanese_ci', 1, 0, '...', '...\n', '', '(w1)[', ']', 'ŷ', '(w2)[', ']', 'ٻλ', '(w3)[', ']') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'eucjpms_bin', 1, 0, '...', '...\n', '', '(w1)[', ']', 'ŷ', '(w2)[', ']', 'ٻλ', '(w3)[', ']') from t1;
+drop table t1;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_invalid_nonexistent_charset.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_invalid_nonexistent_charset.test
new file mode 100644
index 00000000000..bf6b4df5871
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_invalid_nonexistent_charset.test
@@ -0,0 +1,28 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+SET NAMES UTF8;
+
+--error ER_CANT_INITIALIZE_UDF
+SELECT mroonga_snippet("Invalid charset test", 10, 2, "nonexistent_charset",
+ 1, 0, "...", "...", "charset", "<", ">");
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_invalid_unsupported_charset.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_invalid_unsupported_charset.test
new file mode 100644
index 00000000000..451737c900c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_invalid_unsupported_charset.test
@@ -0,0 +1,28 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+SET NAMES UTF8;
+
+--error ER_CANT_INITIALIZE_UDF
+SELECT mroonga_snippet("Unsuppported charset test", 10, 2, "big5",
+ 1, 0, "...", "...", "charset", "<", ">");
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_japanese.test b/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_japanese.test
new file mode 100644
index 00000000000..354d5f5a2d6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/function_snippet_japanese.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012-2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+set names utf8;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset utf8;
+insert into t1 values(1, "あああああ","29日の富士山の天気について");
+insert into t1 values(2, "いいいいい","29日の富士山の天気は分かりません");
+insert into t1 values(3, "ううううう","29日の富士山の天気について");
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_general_ci', 0, 1, '...', '...<br>\n', '29日', '<span class="w1">', '</span>', '天気', '<span class="w2">', '</span>', '富士山', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_bin', 0, 1, '...', '...<br>\n', '29日', '<span class="w1">', '</span>', '天気', '<span class="w2">', '</span>', '富士山', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_general_ci', 1, 1, '...', '...<br>\n', '29日', '<span class="w1">', '</span>', '天気', '<span class="w2">', '</span>', '富士山', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_bin', 1, 1, '...', '...<br>\n', '29日', '<span class="w1">', '</span>', '天気', '<span class="w2">', '</span>', '富士山', '<span class="w3">', '</span>') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_general_ci', 0, 0, '...', '...\n', '29日', '(w1)[', ']', '天気', '(w2)[', ']', '富士山', '(w3)[', ']') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_bin', 0, 0, '...', '...\n', '29日', '(w1)[', ']', '天気', '(w2)[', ']', '富士山', '(w3)[', ']') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_general_ci', 1, 0, '...', '...\n', '29日', '(w1)[', ']', '天気', '(w2)[', ']', '富士山', '(w3)[', ']') from t1;
+select c1, c2, mroonga_snippet(c3, 10, 2, 'utf8_bin', 1, 0, '...', '...\n', '29日', '(w1)[', ']', '天気', '(w2)[', ']', '富士山', '(w3)[', ']') from t1;
+drop table t1;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/geometry_bulk_insert_null.test b/storage/mroonga/mysql-test/mroonga/storage/t/geometry_bulk_insert_null.test
new file mode 100644
index 00000000000..39462257f9d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/geometry_bulk_insert_null.test
@@ -0,0 +1,34 @@
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_geometry.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS shops;
+--enable_warnings
+
+CREATE TABLE shops (
+ location GEOMETRY NOT NULL
+);
+
+INSERT INTO shops VALUES (NULL), (NULL);
+
+SELECT AsText(location) FROM shops;
+
+DROP TABLE shops;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/geometry_contains.test b/storage/mroonga/mysql-test/mroonga/storage/t/geometry_contains.test
new file mode 100644
index 00000000000..c8ec649f181
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/geometry_contains.test
@@ -0,0 +1,148 @@
+# Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_geometry.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS shops;
+--enable_warnings
+
+CREATE TABLE shops (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ name TEXT,
+ location GEOMETRY NOT NULL,
+ SPATIAL KEY location_index (location)
+);
+SHOW CREATE TABLE shops;
+
+INSERT INTO shops (name, location)
+ VALUES ('nezu-no-taiyaki',
+ GeomFromText('POINT(139.762573 35.720253)'));
+INSERT INTO shops (name, location)
+ VALUES ('taiyaki-kataoka',
+ GeomFromText('POINT(139.715591 35.712521)'));
+INSERT INTO shops (name, location)
+ VALUES ('soba-taiyaki-ku',
+ GeomFromText('POINT(139.659088 35.683712)'));
+INSERT INTO shops (name, location)
+ VALUES ('kuruma',
+ GeomFromText('POINT(139.706207 35.721516)'));
+INSERT INTO shops (name, location)
+ VALUES ('hirose-ya',
+ GeomFromText('POINT(139.685608 35.714844)'));
+INSERT INTO shops (name, location)
+ VALUES ('sazare',
+ GeomFromText('POINT(139.685043 35.714653)'));
+INSERT INTO shops (name, location)
+ VALUES ('omede-taiyaki',
+ GeomFromText('POINT(139.817154 35.700516)'));
+INSERT INTO shops (name, location)
+ VALUES ('onaga-ya',
+ GeomFromText('POINT(139.81105 35.698254)'));
+INSERT INTO shops (name, location)
+ VALUES ('shiro-ya',
+ GeomFromText('POINT(139.638611 35.705517)'));
+INSERT INTO shops (name, location)
+ VALUES ('fuji-ya',
+ GeomFromText('POINT(139.637115 35.703938)'));
+INSERT INTO shops (name, location)
+ VALUES ('miyoshi',
+ GeomFromText('POINT(139.537323 35.644539)'));
+INSERT INTO shops (name, location)
+ VALUES ('juju-ya',
+ GeomFromText('POINT(139.695755 35.628922)'));
+INSERT INTO shops (name, location)
+ VALUES ('tatsumi-ya',
+ GeomFromText('POINT(139.638657 35.665501)'));
+INSERT INTO shops (name, location)
+ VALUES ('tetsuji',
+ GeomFromText('POINT(139.76857 35.680912)'));
+INSERT INTO shops (name, location)
+ VALUES ('gazuma-ya',
+ GeomFromText('POINT(139.647598 35.700817)'));
+INSERT INTO shops (name, location)
+ VALUES ('honma-mon',
+ GeomFromText('POINT(139.652573 35.722736)'));
+INSERT INTO shops (name, location)
+ VALUES ('naniwa-ya',
+ GeomFromText('POINT(139.796234 35.730061)'));
+INSERT INTO shops (name, location)
+ VALUES ('kuro-dai',
+ GeomFromText('POINT(139.704834 35.650345)'));
+INSERT INTO shops (name, location)
+ VALUES ('daruma',
+ GeomFromText('POINT(139.770599 35.681461)'));
+INSERT INTO shops (name, location)
+ VALUES ('yanagi-ya',
+ GeomFromText('POINT(139.783981 35.685341)'));
+INSERT INTO shops (name, location)
+ VALUES ('sharaku',
+ GeomFromText('POINT(139.794846 35.716969)'));
+INSERT INTO shops (name, location)
+ VALUES ('takane',
+ GeomFromText('POINT(139.560913 35.698601)'));
+INSERT INTO shops (name, location)
+ VALUES ('chiyoda',
+ GeomFromText('POINT(139.652817 35.642601)'));
+INSERT INTO shops (name, location)
+ VALUES ('da-ka-po',
+ GeomFromText('POINT(139.727356 35.627346)'));
+INSERT INTO shops (name, location)
+ VALUES ('matsushima-ya',
+ GeomFromText('POINT(139.737381 35.640556)'));
+INSERT INTO shops (name, location)
+ VALUES ('kazuya',
+ GeomFromText('POINT(139.760895 35.673508)'));
+INSERT INTO shops (name, location)
+ VALUES ('furuya-kogane-an',
+ GeomFromText('POINT(139.676071 35.680603)'));
+INSERT INTO shops (name, location)
+ VALUES ('hachi-no-ie',
+ GeomFromText('POINT(139.668106 35.608021)'));
+INSERT INTO shops (name, location)
+ VALUES ('azuki-chan',
+ GeomFromText('POINT(139.673203 35.64151)'));
+INSERT INTO shops (name, location)
+ VALUES ('kuriko-an',
+ GeomFromText('POINT(139.796829 35.712013)'));
+INSERT INTO shops (name, location)
+ VALUES ('yume-no-aru-machi-no-taiyaki-ya-san',
+ GeomFromText('POINT(139.712524 35.616199)'));
+INSERT INTO shops (name, location)
+ VALUES ('naze-ya',
+ GeomFromText('POINT(139.665833 35.609039)'));
+INSERT INTO shops (name, location)
+ VALUES ('sanoki-ya',
+ GeomFromText('POINT(139.770721 35.66592)'));
+INSERT INTO shops (name, location)
+ VALUES ('shigeta',
+ GeomFromText('POINT(139.780273 35.672626)'));
+INSERT INTO shops (name, location)
+ VALUES ('nishimi-ya',
+ GeomFromText('POINT(139.774628 35.671825)'));
+INSERT INTO shops (name, location)
+ VALUES ('hiiragi',
+ GeomFromText('POINT(139.711517 35.647701)'));
+
+SELECT id, name, AsText(location) AS location_text FROM shops;
+SELECT id, name, AsText(location) AS location_text FROM shops
+ WHERE MBRContains(GeomFromText('LineString(139.7727 35.6684, 139.7038 35.7121)'), location)
+ ORDER BY id;
+
+DROP TABLE shops;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_btree_equal_datetime.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_btree_equal_datetime.test
new file mode 100644
index 00000000000..2fd1fa7471c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_btree_equal_datetime.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/skip_freebsd.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ created_at datetime,
+ title varchar(256),
+ KEY created_at_key(created_at)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES ("1000-01-01 00:00:00", "The start");
+INSERT INTO diaries VALUES ("2012-10-25 16:18:29", "Today is shiny day.");
+INSERT INTO diaries VALUES ("9999-12-31 23:59:59", "The end");
+
+SELECT *
+ FROM diaries FORCE INDEX(created_at_key)
+ WHERE created_at = "2012-10-25 16:18:29";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_btree_equal_time.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_btree_equal_time.test
new file mode 100644
index 00000000000..51737fcf375
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_btree_equal_time.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS timer;
+--enable_warnings
+
+CREATE TABLE timer (
+ id int PRIMARY KEY,
+ elapsed time,
+ KEY elapsed_key(elapsed)
+);
+
+INSERT INTO timer VALUES (1, "00:00:00");
+INSERT INTO timer VALUES (2, "15:11:12");
+INSERT INTO timer VALUES (3, "838:59:59");
+INSERT INTO timer VALUES (4, "-838:59:59");
+
+SELECT *
+ FROM timer FORCE INDEX(elapsed_key)
+ WHERE elapsed = "-838:59:59";
+
+DROP TABLE timer;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_btree_equal_timestamp.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_btree_equal_timestamp.test
new file mode 100644
index 00000000000..f8848ce196d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_btree_equal_timestamp.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ created_at timestamp,
+ title varchar(256),
+ KEY created_at_key(created_at)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES ("1970-01-01 12:00:00", "The start");
+INSERT INTO diaries VALUES ("2012-10-05 16:18:29", "Today is shiny day.");
+INSERT INTO diaries VALUES ("2038-01-18 15:14:07", "The end");
+
+SELECT *
+ FROM diaries FORCE INDEX(created_at_key)
+ WHERE created_at = "2012-10-05 16:18:29";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_btree_normal_column_insert.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_btree_normal_column_insert.test
new file mode 100644
index 00000000000..0215807b9db
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_btree_normal_column_insert.test
@@ -0,0 +1,33 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 int, index using btree (c2));
+show create table t1;
+insert into t1 values (1, 100);
+insert into t1 values (2, 101);
+insert into t1 values (3, 102);
+select * from t1;
+flush tables;
+select * from t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_hash_id_normal.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_hash_id_normal.test
new file mode 100644
index 00000000000..29a937054a2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_hash_id_normal.test
@@ -0,0 +1,33 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (_id int, a int, key (_id) using hash);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+select * from t1;
+select * from t1 where _id = 2;
+select * from t1 where _id = 20;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_hash_id_primary.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_hash_id_primary.test
new file mode 100644
index 00000000000..60ca970aa45
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_hash_id_primary.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (_id int, a int, primary key (_id) using hash);
+--error ER_BAD_NULL_ERROR
+insert into t1 values(null, 100);
+insert into t1 values(1,100);
+insert into t1 values(1,100);
+insert into t1 values(1,100);
+insert into t1 values(1,100);
+select * from t1;
+select * from t1 where _id = 2;
+select * from t1 where _id = 20;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_hash_id_unique.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_hash_id_unique.test
new file mode 100644
index 00000000000..28922bc3644
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_hash_id_unique.test
@@ -0,0 +1,33 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (_id int, a int, unique key (_id) using hash);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+select * from t1;
+select * from t1 where _id = 2;
+select * from t1 where _id = 20;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_hash_normal_column_insert.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_hash_normal_column_insert.test
new file mode 100644
index 00000000000..5138c6c9698
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_hash_normal_column_insert.test
@@ -0,0 +1,33 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 int, index using hash (c2));
+show create table t1;
+insert into t1 values (1, 100);
+insert into t1 values (2, 101);
+insert into t1 values (3, 102);
+select * from t1;
+flush tables;
+select * from t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_delete.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_delete.test
new file mode 100644
index 00000000000..f39ead56541
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_delete.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists listing;
+--enable_warnings
+
+set names utf8;
+create table scores (
+ id int primary key auto_increment not null,
+ name char(30) not null,
+ score int not null,
+ index property (name, score)
+) default charset utf8;
+show create table scores;
+insert into scores (name, score) values("Taro Yamada", 29);
+insert into scores (name, score) values("Taro Yamada", -12);
+insert into scores (name, score) values("Jiro Yamada", 27);
+insert into scores (name, score) values("Taro Yamada", 10);
+select * from scores;
+delete from scores where name = "Taro Yamada" and score = 10;
+select * from scores where name = "Taro Yamada" and (score >= -12 and score < 29);
+drop table scores;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_smallint.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_smallint.test
new file mode 100644
index 00000000000..055f2b20f22
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_smallint.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+CREATE TABLE t1 (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ c1 SMALLINT,
+ c2 SMALLINT,
+ KEY idx1(c1, c2)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE t1;
+
+INSERT INTO t1 (c1, c2) VALUES
+ (1999, 12),
+ (2000, 11),
+ (2001, 10),
+ (2002, 9),
+ (2003, 8),
+ (2004, 7),
+ (2005, 6),
+ (2006, 5),
+ (2007, 4),
+ (2008, 3),
+ (2009, 2),
+ (2010, 1);
+
+SELECT * FROM t1 WHERE c1 > 2005;
+SELECT * FROM t1 WHERE c1 >= 2005;
+SELECT * FROM t1 WHERE c1 = 2005;
+SELECT * FROM t1 WHERE c1 <= 2005;
+SELECT * FROM t1 WHERE c1 < 2005;
+
+DROP TABLE t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_unsigned_bigint.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_unsigned_bigint.test
new file mode 100644
index 00000000000..9c9f3dfc283
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_unsigned_bigint.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+CREATE TABLE t1 (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ c1 BIGINT UNSIGNED,
+ c2 BIGINT UNSIGNED,
+ KEY idx1(c1, c2)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE t1;
+
+INSERT INTO t1 (c1, c2) VALUES
+ (1999, 12),
+ (2000, 11),
+ (2001, 10),
+ (2002, 9),
+ (2003, 8),
+ (2004, 7),
+ (2005, 6),
+ (2006, 5),
+ (2007, 4),
+ (2008, 3),
+ (2009, 2),
+ (2010, 1);
+
+SELECT * FROM t1 WHERE c1 > 2005;
+SELECT * FROM t1 WHERE c1 >= 2005;
+SELECT * FROM t1 WHERE c1 = 2005;
+SELECT * FROM t1 WHERE c1 <= 2005;
+SELECT * FROM t1 WHERE c1 < 2005;
+
+DROP TABLE t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_unsigned_int.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_unsigned_int.test
new file mode 100644
index 00000000000..c78aec6d6e0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_unsigned_int.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+CREATE TABLE t1 (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ c1 INT UNSIGNED,
+ c2 INT UNSIGNED,
+ KEY idx1(c1, c2)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE t1;
+
+INSERT INTO t1 (c1, c2) VALUES
+ (1999, 12),
+ (2000, 11),
+ (2001, 10),
+ (2002, 9),
+ (2003, 8),
+ (2004, 7),
+ (2005, 6),
+ (2006, 5),
+ (2007, 4),
+ (2008, 3),
+ (2009, 2),
+ (2010, 1);
+
+SELECT * FROM t1 WHERE c1 > 2005;
+SELECT * FROM t1 WHERE c1 >= 2005;
+SELECT * FROM t1 WHERE c1 = 2005;
+SELECT * FROM t1 WHERE c1 <= 2005;
+SELECT * FROM t1 WHERE c1 < 2005;
+
+DROP TABLE t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_unsigned_smallint.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_unsigned_smallint.test
new file mode 100644
index 00000000000..0b9b7dd3a3c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_unsigned_smallint.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+CREATE TABLE t1 (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ c1 SMALLINT UNSIGNED,
+ c2 SMALLINT UNSIGNED,
+ KEY idx1(c1, c2)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE t1;
+
+INSERT INTO t1 (c1, c2) VALUES
+ (1999, 12),
+ (2000, 11),
+ (2001, 10),
+ (2002, 9),
+ (2003, 8),
+ (2004, 7),
+ (2005, 6),
+ (2006, 5),
+ (2007, 4),
+ (2008, 3),
+ (2009, 2),
+ (2010, 1);
+
+SELECT * FROM t1 WHERE c1 > 2005;
+SELECT * FROM t1 WHERE c1 >= 2005;
+SELECT * FROM t1 WHERE c1 = 2005;
+SELECT * FROM t1 WHERE c1 <= 2005;
+SELECT * FROM t1 WHERE c1 < 2005;
+
+DROP TABLE t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_varchar.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_varchar.test
new file mode 100644
index 00000000000..2307c490fda
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_nullable_varchar.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+CREATE TABLE t1 (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ c1 VARCHAR(10),
+ c2 VARCHAR(10),
+ KEY idx1(c1, c2)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE t1;
+
+INSERT INTO t1 (c1, c2) VALUES
+ ('1999', '12'),
+ ('2000', '11'),
+ ('2001', '10'),
+ ('2002', '09'),
+ ('2003', '08'),
+ ('2004', '07'),
+ ('2005', '06'),
+ ('2006', '05'),
+ ('2007', '04'),
+ ('2008', '03'),
+ ('2009', '02'),
+ ('2010', '01');
+
+SELECT * FROM t1 WHERE c1 > '2005';
+SELECT * FROM t1 WHERE c1 >= '2005';
+SELECT * FROM t1 WHERE c1 = '2005';
+SELECT * FROM t1 WHERE c1 <= '2005';
+SELECT * FROM t1 WHERE c1 < '2005';
+
+DROP TABLE t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_primary_delete.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_primary_delete.test
new file mode 100644
index 00000000000..75e77b597e0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_primary_delete.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists listing;
+--enable_warnings
+
+set names utf8;
+create table scores (
+ name char(30) not null,
+ score int not null,
+ primary key (name, score)
+) default charset utf8;
+show create table scores;
+insert into scores (name, score) values("Taro Yamada", 29);
+insert into scores (name, score) values("Taro Yamada", -12);
+insert into scores (name, score) values("Jiro Yamada", 27);
+insert into scores (name, score) values("Taro Yamada", 10);
+select * from scores;
+delete from scores where name = "Taro Yamada" and score = 10;
+select * from scores where name = "Taro Yamada" and (score >= -12 and score < 29);
+drop table scores;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_primary_select_int.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_primary_select_int.test
new file mode 100644
index 00000000000..0b3a7362c0e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_primary_select_int.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists listing;
+--enable_warnings
+
+set names utf8;
+create table scores (
+ name char(30) not null,
+ score int not null,
+ primary key (name, score)
+) default charset utf8;
+show create table scores;
+insert into scores (name, score) values("Taro Yamada", 29);
+insert into scores (name, score) values("Taro Yamada", -12);
+insert into scores (name, score) values("Jiro Yamada", 27);
+insert into scores (name, score) values("Taro Yamada", 10);
+select * from scores;
+select * from scores where name = "Taro Yamada";
+select * from scores where name = "Taro Yamada" and score = 29;
+select * from scores where name = "Taro Yamada" and (score >= -12 and score < 29);
+drop table scores;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_primary_update.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_primary_update.test
new file mode 100644
index 00000000000..26c76dd5207
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_primary_update.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists listing;
+--enable_warnings
+
+set names utf8;
+create table scores (
+ name char(30) not null,
+ score int not null,
+ primary key (name, score)
+) default charset utf8;
+show create table scores;
+insert into scores (name, score) values("Taro Yamada", 29);
+insert into scores (name, score) values("Taro Yamada", -12);
+insert into scores (name, score) values("Jiro Yamada", 27);
+insert into scores (name, score) values("Taro Yamada", 10);
+select * from scores;
+update scores set name = "Taro Yamada" where name = "Jiro Yamada" and score = 27;
+select * from scores where name = "Taro Yamada" and (score >= -12 and score < 29);
+drop table scores;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_recreate.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_recreate.test
new file mode 100644
index 00000000000..90a1fdf6c49
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_recreate.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists listing;
+--enable_warnings
+
+set names utf8;
+create table listing (
+ id int primary key auto_increment not null,
+ last_name char(30) not null,
+ first_name char(30) not null,
+ index name (last_name, first_name)
+) default charset utf8;
+show create table listing;
+
+insert into listing (last_name, first_name) values("Taro", "Yamada");
+insert into listing (last_name, first_name) values("Taro", "Suzuki");
+insert into listing (last_name, first_name) values("Jiro", "Yamada");
+insert into listing (last_name, first_name) values("Taro", "Tanaka");
+
+select * from listing
+ where last_name = "Taro" and (first_name >= "S" and first_name <= "Y");
+
+drop index name on listing;
+select * from listing
+ where last_name = "Taro" and (first_name >= "S" and first_name <= "Y");
+
+create index new_name_index on listing (last_name, first_name);
+select * from listing
+ where last_name = "Taro" and (first_name >= "S" and first_name <= "Y");
+
+drop table listing;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_replace.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_replace.test
new file mode 100644
index 00000000000..b5e592938a7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_replace.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS listing;
+--enable_warnings
+
+CREATE TABLE scores (
+ id INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
+ name CHAR(30) NOT NULL,
+ score INT NOT NULL,
+ INDEX property (NAME, SCORE)
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE scores;
+
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 29);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", -12);
+INSERT INTO scores (name, score) VALUES("Jiro Yamada", 27);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 10);
+
+SELECT * FROM scores;
+REPLACE scores (id, name, score) VALUES (3, "Taro Yamada", 28);
+SELECT * FROM scores;
+SELECT * FROM scores WHERE name = "Taro Yamada" AND (score >= -12 AND score < 29);
+
+DROP TABLE scores;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_double.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_double.test
new file mode 100644
index 00000000000..4f9c60d14b2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_double.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS temperatures;
+--enable_warnings
+
+CREATE TABLE temperatures (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title VARCHAR(20),
+ temperature DOUBLE,
+ KEY temperature_index(temperature),
+ KEY multi_index(temperature, title)
+);
+
+INSERT INTO temperatures VALUES (NULL, "Hot!", 28.2);
+INSERT INTO temperatures VALUES (NULL, "Snow!", -2.8);
+INSERT INTO temperatures VALUES (NULL, "Rainy!", 12.7);
+
+SELECT temperature FROM temperatures WHERE temperature BETWEEN 10 AND 30;
+SELECT temperature FROM temperatures WHERE temperature BETWEEN -10 AND 20;
+
+SELECT title, temperature FROM temperatures WHERE temperature BETWEEN 10 AND 30;
+SELECT title, temperature FROM temperatures WHERE temperature BETWEEN -10 AND 20;
+
+DROP TABLE temperatures;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_float.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_float.test
new file mode 100644
index 00000000000..941fddbc5a0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_float.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS temperatures;
+--enable_warnings
+
+CREATE TABLE temperatures (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title VARCHAR(20),
+ temperature FLOAT,
+ KEY temperature_index(temperature),
+ KEY multi_index(temperature, title)
+);
+
+INSERT INTO temperatures VALUES (NULL, "Hot!", 28.2);
+INSERT INTO temperatures VALUES (NULL, "Snow!", -2.8);
+INSERT INTO temperatures VALUES (NULL, "Rainy!", 12.7);
+
+SELECT temperature FROM temperatures WHERE temperature BETWEEN 10 AND 30;
+SELECT temperature FROM temperatures WHERE temperature BETWEEN -10 AND 20;
+
+SELECT title, temperature FROM temperatures WHERE temperature BETWEEN 10 AND 30;
+SELECT title, temperature FROM temperatures WHERE temperature BETWEEN -10 AND 20;
+
+DROP TABLE temperatures;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_int.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_int.test
new file mode 100644
index 00000000000..478be9a90db
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_int.test
@@ -0,0 +1,46 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS listing;
+--enable_warnings
+
+CREATE TABLE scores (
+ id INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
+ name CHAR(30) NOT NULL,
+ score INT NOT NULL,
+ INDEX property (score, name)
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE scores;
+
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 29);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", -12);
+INSERT INTO scores (name, score) VALUES("Jiro Yamada", 27);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 10);
+
+SELECT * FROM scores;
+
+SELECT * FROM scores WHERE score = 29;
+
+SELECT * FROM scores WHERE score = 29 AND name = "Taro Yamada";
+
+SELECT * FROM scores WHERE (score >= -12 AND score < 29) AND name = "Taro Yamada";
+
+DROP TABLE scores;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_string.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_string.test
new file mode 100644
index 00000000000..6e0278f3910
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_string.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists listing;
+--enable_warnings
+
+set names utf8;
+create table listing (
+ id int primary key auto_increment not null,
+ last_name char(30) not null,
+ first_name char(30) not null,
+ index name (last_name, first_name)
+) default charset utf8;
+show create table listing;
+insert into listing (last_name, first_name) values("Taro", "Yamada");
+insert into listing (last_name, first_name) values("Taro", "Suzuki");
+insert into listing (last_name, first_name) values("Jiro", "Yamada");
+insert into listing (last_name, first_name) values("Taro", "Tanaka");
+select * from listing;
+select * from listing where last_name = "Taro";
+select * from listing where last_name = "Taro" and first_name = "Suzuki";
+select * from listing where last_name = "Taro" and (first_name >= "S" and first_name <= "Y");
+drop table listing;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_varchar.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_varchar.test
new file mode 100644
index 00000000000..9822ec18f4a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_select_varchar.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists scores;
+--enable_warnings
+
+set names utf8;
+create table scores (
+ given_name varchar(30) not null,
+ family_name varchar(30) not null,
+ score int not null,
+ primary key property (given_name, family_name, score)
+) default charset utf8;
+show create table scores;
+
+insert into scores values("Taro", "Yamada", 29);
+insert into scores values("Taro", "Yamada", -12);
+insert into scores values("Jiro", "Yamada", 27);
+insert into scores values("Taro", "Yamada", 10);
+
+select * from scores;
+select * from scores where given_name = "Taro" and family_name = "Yamada";
+select * from scores where given_name = "Taro" and family_name = "Yamada" and score = 29;
+select * from scores where given_name = "Taro" and family_name = "Yamada" and (score >= -12 and score < 29);
+
+drop table scores;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_32bit_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_32bit_equal.test
new file mode 100644
index 00000000000..b206333d28d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_32bit_equal.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_32bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start DATE,
+ end DATE,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "1000-01-01", "2012-10-05");
+INSERT INTO ranges VALUES (2, "1000-01-01", "9999-12-31");
+INSERT INTO ranges VALUES (3, "2012-10-25", "9999-12-31");
+INSERT INTO ranges VALUES (4, "9999-12-31", "1000-01-01");
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ WHERE start = "1000-01-01" AND end = "9999-12-31";
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_64bit_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_64bit_equal.test
new file mode 100644
index 00000000000..c425b882482
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_64bit_equal.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/skip_freebsd.inc
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start DATE,
+ end DATE,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "1000-01-01", "2012-10-05");
+INSERT INTO ranges VALUES (2, "1000-01-01", "9999-12-31");
+INSERT INTO ranges VALUES (3, "2012-10-25", "9999-12-31");
+INSERT INTO ranges VALUES (4, "9999-12-31", "1000-01-01");
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ WHERE start = "1000-01-01" AND end = "9999-12-31";
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_index_read.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_index_read.test
new file mode 100644
index 00000000000..ea095f1c085
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_index_read.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/skip_freebsd.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start DATE,
+ end DATE,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "1000-01-01", "2012-10-05");
+INSERT INTO ranges VALUES (2, "1000-01-01", "9999-12-31");
+INSERT INTO ranges VALUES (3, "2012-10-25", "9999-12-31");
+INSERT INTO ranges VALUES (4, "9999-12-31", "1000-01-01");
+
+SELECT start, end
+ FROM ranges FORCE INDEX(range_key)
+ ORDER BY start, end;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_32bit_asc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_32bit_asc.test
new file mode 100644
index 00000000000..b877ed2f01a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_32bit_asc.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_32bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start DATE,
+ end DATE,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "2012-10-25", "9999-12-31");
+INSERT INTO ranges VALUES (2, "1000-01-01", "2012-10-05");
+INSERT INTO ranges VALUES (3, "9999-12-31", "1000-01-01");
+INSERT INTO ranges VALUES (4, "1000-01-01", "9999-12-31");
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start, end;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_32bit_desc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_32bit_desc.test
new file mode 100644
index 00000000000..c3185bb0fdc
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_32bit_desc.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_32bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start DATE,
+ end DATE,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "2012-10-25", "9999-12-31");
+INSERT INTO ranges VALUES (2, "1000-01-01", "2012-10-05");
+INSERT INTO ranges VALUES (3, "9999-12-31", "1000-01-01");
+INSERT INTO ranges VALUES (4, "1000-01-01", "9999-12-31");
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start DESC, end DESC;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_64bit_asc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_64bit_asc.test
new file mode 100644
index 00000000000..7fa17dbc2b3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_64bit_asc.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/skip_freebsd.inc
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start DATE,
+ end DATE,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "2012-10-25", "9999-12-31");
+INSERT INTO ranges VALUES (2, "1000-01-01", "2012-10-05");
+INSERT INTO ranges VALUES (3, "9999-12-31", "1000-01-01");
+INSERT INTO ranges VALUES (4, "1000-01-01", "9999-12-31");
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start, end;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_64bit_desc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_64bit_desc.test
new file mode 100644
index 00000000000..ef3d9eee548
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_order_64bit_desc.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/skip_freebsd.inc
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start DATE,
+ end DATE,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "2012-10-25", "9999-12-31");
+INSERT INTO ranges VALUES (2, "1000-01-01", "2012-10-05");
+INSERT INTO ranges VALUES (3, "9999-12-31", "1000-01-01");
+INSERT INTO ranges VALUES (4, "1000-01-01", "9999-12-31");
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start DESC, end DESC;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_reinsert.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_reinsert.test
new file mode 100644
index 00000000000..58ec29f07e9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_date_reinsert.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start DATE,
+ end DATE,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "2010-01-01", "2012-10-05");
+SELECT * FROM ranges;
+
+DELETE FROM ranges WHERE id = 1;
+INSERT INTO ranges VALUES (1, "2010-01-01", "2012-10-05");
+SELECT * FROM ranges;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_index_read.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_index_read.test
new file mode 100644
index 00000000000..efa0c0288bd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_index_read.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/skip_freebsd.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id int PRIMARY KEY,
+ start datetime,
+ end datetime,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "1000-01-01 00:00:00", "2012-10-05 16:18:29");
+INSERT INTO ranges VALUES (2, "1000-01-01 00:00:00", "9999-12-31 23:59:59");
+INSERT INTO ranges VALUES (3, "2012-10-25 16:18:29", "9999-12-31 23:59:59");
+INSERT INTO ranges VALUES (4, "9999-12-31 23:59:59", "1000-01-01 00:00:00");
+
+SELECT start, end
+ FROM ranges FORCE INDEX(range_key)
+ ORDER BY start, end;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_order_asc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_order_asc.test
new file mode 100644
index 00000000000..55d910cc820
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_order_asc.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/skip_freebsd.inc
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id int PRIMARY KEY,
+ start datetime,
+ end datetime,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "2012-10-25 16:18:29", "9999-12-31 23:59:59");
+INSERT INTO ranges VALUES (2, "1000-01-01 00:00:00", "2012-10-05 16:18:29");
+INSERT INTO ranges VALUES (3, "9999-12-31 23:59:59", "1000-01-01 00:00:00");
+INSERT INTO ranges VALUES (4, "1000-01-01 00:00:00", "9999-12-31 23:59:59");
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start, end;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_order_desc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_order_desc.test
new file mode 100644
index 00000000000..5868ade2228
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_order_desc.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/skip_freebsd.inc
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id int PRIMARY KEY,
+ start datetime,
+ end datetime,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "2012-10-25 16:18:29", "9999-12-31 23:59:59");
+INSERT INTO ranges VALUES (2, "1000-01-01 00:00:00", "2012-10-05 16:18:29");
+INSERT INTO ranges VALUES (3, "9999-12-31 23:59:59", "1000-01-01 00:00:00");
+INSERT INTO ranges VALUES (4, "1000-01-01 00:00:00", "9999-12-31 23:59:59");
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start DESC, end DESC;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_reinsert.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_reinsert.test
new file mode 100644
index 00000000000..ab9ed122862
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_datetime_reinsert.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id int PRIMARY KEY,
+ start datetime,
+ end datetime,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "2010-01-01 00:00:00", "2012-10-05 23:59:59");
+SELECT * FROM ranges;
+
+DELETE FROM ranges WHERE id = 1;
+INSERT INTO ranges VALUES (1, "2010-01-01 00:00:00", "2012-10-05 23:59:59");
+SELECT * FROM ranges;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_decimal.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_decimal.test
new file mode 100644
index 00000000000..85645967fbd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_decimal.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 decimal(65,30), c3 decimal(65,30), unique key uk1(c2,c3));
+insert into t1 values(1,123.456,0.000000000000000000000000000001);
+insert into t1 values(2,-123.456,123.456);
+insert into t1 values(3,98765432109876543210987654321098765.432109876543210987654321098765,-123.456);
+insert into t1 values(4,-98765432109876543210987654321098765.432109876543210987654321098765,98765432109876543210987654321098765.432109876543210987654321098765);
+insert into t1 values(5,0.000000000000000000000000000001,-98765432109876543210987654321098765.432109876543210987654321098765);
+select c1, c2, c3 from t1 force index(uk1) where c2 = -98765432109876543210987654321098765.432109876543210987654321098765 and c3 = 98765432109876543210987654321098765.432109876543210987654321098765;
+select c1, c2, c3 from t1 force index(uk1) order by c2, c3;
+select c1, c2, c3 from t1 force index(uk1) order by c2 desc, c3 desc;
+select c2, c3 from t1 force index(uk1) order by c2, c3;
+--error ER_DUP_ENTRY
+insert into t1 values(6,123.456,0.000000000000000000000000000001);
+delete from t1 where c1 = 1;
+insert into t1 values(1,123.456,0.000000000000000000000000000001);
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_index_read.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_index_read.test
new file mode 100644
index 00000000000..f9b2d2b6f5c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_index_read.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id int PRIMARY KEY,
+ start time,
+ end time,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "00:00:00", "15:11:11");
+INSERT INTO ranges VALUES (2, "00:00:00", "838:59:59");
+INSERT INTO ranges VALUES (3, "15:11:12", "838:59:59");
+INSERT INTO ranges VALUES (4, "838:59:59", "00:00:00");
+INSERT INTO ranges VALUES (5, "-838:59:59", "838:59:59");
+
+SELECT start, end
+ FROM ranges FORCE INDEX(range_key)
+ ORDER BY start, end;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_order_asc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_order_asc.test
new file mode 100644
index 00000000000..94e2fa39946
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_order_asc.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id int PRIMARY KEY,
+ start time,
+ end time,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "15:11:12", "838:59:59");
+INSERT INTO ranges VALUES (2, "00:00:00", "15:11:11");
+INSERT INTO ranges VALUES (3, "838:59:59", "00:00:00");
+INSERT INTO ranges VALUES (4, "00:00:00", "838:59:59");
+INSERT INTO ranges VALUES (5, "-838:59:59", "838:59:59");
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start, end;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_order_desc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_order_desc.test
new file mode 100644
index 00000000000..d9d31cf3386
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_order_desc.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id int PRIMARY KEY,
+ start time,
+ end time,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "15:11:12", "838:59:59");
+INSERT INTO ranges VALUES (2, "00:00:00", "15:11:11");
+INSERT INTO ranges VALUES (3, "838:59:59", "00:00:00");
+INSERT INTO ranges VALUES (4, "00:00:00", "838:59:59");
+INSERT INTO ranges VALUES (5, "-838:59:59", "838:59:59");
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start DESC, end DESC;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_reinsert.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_reinsert.test
new file mode 100644
index 00000000000..e0a18ae792b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_time_reinsert.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id int PRIMARY KEY,
+ start time,
+ end time,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "13:21:48", "15:11:12");
+SELECT * FROM ranges;
+
+DELETE FROM ranges WHERE id = 1;
+INSERT INTO ranges VALUES (1, "13:21:48", "15:11:12");
+SELECT * FROM ranges;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_index_read.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_index_read.test
new file mode 100644
index 00000000000..c660f6aef6e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_index_read.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id int PRIMARY KEY,
+ start timestamp,
+ end timestamp,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "1970-01-01 12:00:00", "2012-10-05 16:18:29");
+INSERT INTO ranges VALUES (2, "1970-01-01 12:00:00", "2038-01-18 15:14:07");
+INSERT INTO ranges VALUES (3, "2012-10-25 16:18:29", "2038-01-18 15:14:07");
+INSERT INTO ranges VALUES (4, "2038-01-18 15:14:07", "1970-01-01 12:00:00");
+
+SELECT start, end
+ FROM ranges FORCE INDEX(range_key)
+ ORDER BY start, end;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_order_asc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_order_asc.test
new file mode 100644
index 00000000000..5ec630b56df
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_order_asc.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id int PRIMARY KEY,
+ start timestamp,
+ end timestamp,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "2012-10-25 16:18:29", "2038-01-18 15:14:07");
+INSERT INTO ranges VALUES (2, "1970-01-01 12:00:00", "2012-10-05 16:18:29");
+INSERT INTO ranges VALUES (3, "2038-01-18 15:14:07", "1970-01-01 12:00:00");
+INSERT INTO ranges VALUES (4, "1970-01-01 12:00:00", "2038-01-18 15:14:07");
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start, end;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_order_desc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_order_desc.test
new file mode 100644
index 00000000000..9ca7440da88
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_order_desc.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id int PRIMARY KEY,
+ start timestamp,
+ end timestamp,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "2012-10-25 16:18:29", "2038-01-18 15:14:07");
+INSERT INTO ranges VALUES (2, "1970-01-01 12:00:00", "2012-10-05 16:18:29");
+INSERT INTO ranges VALUES (3, "2038-01-18 15:14:07", "1970-01-01 12:00:00");
+INSERT INTO ranges VALUES (4, "1970-01-01 12:00:00", "2038-01-18 15:14:07");
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start DESC, end DESC;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_reinsert.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_reinsert.test
new file mode 100644
index 00000000000..559cf958804
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_timestamp_reinsert.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id int PRIMARY KEY,
+ start timestamp,
+ end timestamp,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, "2010-01-01 00:00:00", "2012-10-05 23:59:59");
+SELECT * FROM ranges;
+
+DELETE FROM ranges WHERE id = 1;
+INSERT INTO ranges VALUES (1, "2010-01-01 00:00:00", "2012-10-05 23:59:59");
+SELECT * FROM ranges;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_varchar.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_varchar.test
new file mode 100644
index 00000000000..900de61feac
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_varchar.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 varchar(10), c3 varchar(10), unique key uk1(c2,c3)) default charset=utf8 collate utf8_bin;
+insert into t1 values(1,'abcde','abc ');
+insert into t1 values(2,'abc\0','abcde');
+insert into t1 values(3,'abc','abc\0');
+insert into t1 values(4,'abc ','abc');
+insert into t1 values(5,'abc ','abc ');
+select c1, c2, c3 from t1 force index(uk1) where c2 = 'abc ' and c3 = 'abc';
+select c1, c2, c3 from t1 force index(uk1) order by c2, c3;
+select c1, c2, c3 from t1 force index(uk1) order by c2 desc, c3 desc;
+select c2, c3 from t1 force index(uk1) order by c2, c3;
+--error ER_DUP_ENTRY
+insert into t1 values(6,'abcde','abc ');
+delete from t1 where c1 = 1;
+insert into t1 values(1,'abcde','abc ');
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_32bit_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_32bit_equal.test
new file mode 100644
index 00000000000..3daf3fa4153
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_32bit_equal.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_32bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start YEAR,
+ end YEAR,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, 1901, 2012);
+INSERT INTO ranges VALUES (2, 1901, 2155);
+INSERT INTO ranges VALUES (3, 2012, 2155);
+INSERT INTO ranges VALUES (4, 2155, 1901);
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ WHERE start = 1901 AND end = 2155;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_64bit_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_64bit_equal.test
new file mode 100644
index 00000000000..57ab0534147
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_64bit_equal.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start YEAR,
+ end YEAR,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, 1901, 2012);
+INSERT INTO ranges VALUES (2, 1901, 2155);
+INSERT INTO ranges VALUES (3, 2012, 2155);
+INSERT INTO ranges VALUES (4, 2155, 1901);
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ WHERE start = 1901 AND end = 2155;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_index_read.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_index_read.test
new file mode 100644
index 00000000000..efa92728a63
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_index_read.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start YEAR,
+ end YEAR,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, 1901, 2012);
+INSERT INTO ranges VALUES (2, 1901, 2155);
+INSERT INTO ranges VALUES (3, 2012, 2155);
+INSERT INTO ranges VALUES (4, 2155, 1901);
+
+SELECT start, end
+ FROM ranges FORCE INDEX(range_key)
+ ORDER BY start, end;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_32bit_asc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_32bit_asc.test
new file mode 100644
index 00000000000..774c06772a7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_32bit_asc.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_32bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start YEAR,
+ end YEAR,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, 2012, 2155);
+INSERT INTO ranges VALUES (2, 1901, 2012);
+INSERT INTO ranges VALUES (3, 2155, 1901);
+INSERT INTO ranges VALUES (4, 1901, 2155);
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start, end;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_32bit_desc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_32bit_desc.test
new file mode 100644
index 00000000000..bd0d20fe973
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_32bit_desc.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_32bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start YEAR,
+ end YEAR,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, 2012, 2155);
+INSERT INTO ranges VALUES (2, 1901, 2012);
+INSERT INTO ranges VALUES (3, 2155, 1901);
+INSERT INTO ranges VALUES (4, 1901, 2155);
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start DESC, end DESC;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_64bit_asc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_64bit_asc.test
new file mode 100644
index 00000000000..225188e05c6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_64bit_asc.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start YEAR,
+ end YEAR,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, 2012, 2155);
+INSERT INTO ranges VALUES (2, 1901, 2012);
+INSERT INTO ranges VALUES (3, 2155, 1901);
+INSERT INTO ranges VALUES (4, 1901, 2155);
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start, end;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_64bit_desc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_64bit_desc.test
new file mode 100644
index 00000000000..dcf905e9286
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_order_64bit_desc.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_64bit.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start YEAR,
+ end YEAR,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, 2012, 2155);
+INSERT INTO ranges VALUES (2, 1901, 2012);
+INSERT INTO ranges VALUES (3, 2155, 1901);
+INSERT INTO ranges VALUES (4, 1901, 2155);
+
+SELECT * FROM ranges FORCE INDEX(range_key)
+ ORDER BY start DESC, end DESC;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_reinsert.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_reinsert.test
new file mode 100644
index 00000000000..250ef00b5b6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_unique_year_reinsert.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ranges;
+--enable_warnings
+
+CREATE TABLE ranges (
+ id INT PRIMARY KEY,
+ start YEAR,
+ end YEAR,
+ UNIQUE KEY range_key(start, end)
+);
+
+INSERT INTO ranges VALUES (1, 2010, 2012);
+SELECT * FROM ranges;
+
+DELETE FROM ranges WHERE id = 1;
+INSERT INTO ranges VALUES (1, 2010, 2012);
+SELECT * FROM ranges;
+
+DROP TABLE ranges;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_update_int.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_update_int.test
new file mode 100644
index 00000000000..e1efd0c992c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_update_int.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS scores;
+--enable_warnings
+
+CREATE TABLE scores (
+ id INT PRIMARY KEY AUTO_INCREMENT NOT NULL,
+ name CHAR(30) NOT NULL,
+ score INT NOT NULL,
+ KEY property (score, name)
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE scores;
+
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 29);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", -12);
+INSERT INTO scores (name, score) VALUES("Jiro Yamada", 29);
+INSERT INTO scores (name, score) VALUES("Taro Yamada", 10);
+
+SELECT * FROM scores WHERE score = 29;
+
+UPDATE scores SET name = "Saburo YAMADA" WHERE id = 3;
+SELECT * FROM scores WHERE score = 29;
+
+DROP TABLE scores;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_update_string.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_update_string.test
new file mode 100644
index 00000000000..bcbbe82914b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_multiple_column_update_string.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists listing;
+--enable_warnings
+
+set names utf8;
+create table scores (
+ id int primary key auto_increment not null,
+ name char(30) not null,
+ score int not null,
+ index property (name, score)
+) default charset utf8;
+show create table scores;
+insert into scores (name, score) values("Taro Yamada", 29);
+insert into scores (name, score) values("Taro Yamada", -12);
+insert into scores (name, score) values("Jiro Yamada", 27);
+insert into scores (name, score) values("Taro Yamada", 10);
+select * from scores;
+update scores set name = "Taro Yamada" where name = "Jiro Yamada" and score = 27;
+select * from scores where name = "Taro Yamada" and (score >= -12 and score < 29);
+drop table scores;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_char_exact_length.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_char_exact_length.test
new file mode 100644
index 00000000000..ba52b4b4138
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_char_exact_length.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id char(10) CHARACTER SET latin1 PRIMARY KEY
+);
+
+INSERT INTO ids VALUES('abcdefghij');
+INSERT INTO ids VALUES('klmnopqrst');
+INSERT INTO ids VALUES('uvwxyz0123');
+
+SELECT * FROM ids FORCE INDEX(PRIMARY) ORDER BY id;
+
+SELECT * FROM ids FORCE INDEX(PRIMARY) WHERE id = 'abcdefghij';
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_char_null_character.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_char_null_character.test
new file mode 100644
index 00000000000..bded856812c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_char_null_character.test
@@ -0,0 +1,37 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id char(7) CHARACTER SET latin1 COLLATE latin1_bin PRIMARY KEY
+);
+
+INSERT INTO ids VALUES("\0abcdef");
+INSERT INTO ids VALUES("ab\0cdef");
+INSERT INTO ids VALUES("abcd\0ef");
+
+SELECT * FROM ids FORCE INDEX(PRIMARY) ORDER BY id;
+
+SELECT * FROM ids FORCE INDEX(PRIMARY) WHERE id = "ab\0cdef";
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_char_short.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_char_short.test
new file mode 100644
index 00000000000..ae8c40b82c5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_char_short.test
@@ -0,0 +1,37 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id char(6) CHARACTER SET latin1 PRIMARY KEY
+);
+
+INSERT INTO ids VALUES("abcdef");
+INSERT INTO ids VALUES( "cdef");
+INSERT INTO ids VALUES( "ef");
+
+SELECT * FROM ids FORCE INDEX(PRIMARY) ORDER BY id;
+
+SELECT * FROM ids FORCE INDEX(PRIMARY) WHERE id = "cdef";
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_date.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_date.test
new file mode 100644
index 00000000000..e57a8491d99
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_date.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ day DATE PRIMARY KEY,
+ title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (day, title) VALUES ("2012-01-29", "clear day");
+INSERT INTO diaries (day, title) VALUES ("2012-01-30", "rainy day");
+INSERT INTO diaries (day, title) VALUES ("2012-01-31", "cloudy day");
+--error ER_DUP_ENTRY
+INSERT INTO diaries (day, title) VALUES ("2012-01-31", "duplicated day");
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries
+ WHERE day BETWEEN "2012-01-29" AND "2012-01-30";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_datetime_with_fractional_seconds.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_datetime_with_fractional_seconds.test
new file mode 100644
index 00000000000..7f140bf24c6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_datetime_with_fractional_seconds.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_fractional_seconds.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ day DATETIME(6) PRIMARY KEY,
+ title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (day, title)
+ VALUES ("2012-01-29 21:51:01.111111", "clear day");
+INSERT INTO diaries (day, title)
+ VALUES ("2012-01-30 01:23:45.333", "rainy day");
+INSERT INTO diaries (day, title)
+ VALUES ("2012-01-31 08:32:10.5555", "cloudy day");
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries
+ WHERE day BETWEEN "2012-01-29 00:00:00.123456" AND
+ "2012-01-31 00:00:00.999999";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_datetime_without_fractional_seconds.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_datetime_without_fractional_seconds.test
new file mode 100644
index 00000000000..1986e4db3db
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_datetime_without_fractional_seconds.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ day DATETIME PRIMARY KEY,
+ title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (day, title)
+ VALUES ("2012-01-29 21:51:01", "clear day");
+INSERT INTO diaries (day, title)
+ VALUES ("2012-01-30 01:23:45", "rainy day");
+INSERT INTO diaries (day, title)
+ VALUES ("2012-01-31 08:32:10", "cloudy day");
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries
+ WHERE day BETWEEN "2012-01-29 00:00:00" AND "2012-01-31 00:00:00";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_decimal_with_fractional_seconds.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_decimal_with_fractional_seconds.test
new file mode 100644
index 00000000000..d296285b391
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_decimal_with_fractional_seconds.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS releases;
+--enable_warnings
+
+CREATE TABLE releases (
+ version DECIMAL(6, 3) PRIMARY KEY,
+ message TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE releases;
+
+INSERT INTO releases (version, message) VALUES (10.000, "10th release!");
+INSERT INTO releases (version, message) VALUES (10.001, "minor fix.");
+INSERT INTO releases (version, message) VALUES (999.999, "the last release!");
+
+SELECT * FROM releases;
+
+SELECT * FROM releases WHERE version BETWEEN "9.000" AND "10.001";
+
+DROP TABLE releases;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_decimal_without_fractional_seconds.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_decimal_without_fractional_seconds.test
new file mode 100644
index 00000000000..956b9800650
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_decimal_without_fractional_seconds.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS releases;
+--enable_warnings
+
+CREATE TABLE releases (
+ version DECIMAL PRIMARY KEY,
+ message TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE releases;
+
+INSERT INTO releases (version, message) VALUES (1, "the first release!!!");
+INSERT INTO releases (version, message) VALUES (10, "10th release!");
+INSERT INTO releases (version, message) VALUES (999, "the last release!");
+
+SELECT * FROM releases;
+
+SELECT * FROM releases WHERE version BETWEEN "1" AND "10";
+
+DROP TABLE releases;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_time_with_fractional_seconds.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_time_with_fractional_seconds.test
new file mode 100644
index 00000000000..90e668e1e38
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_time_with_fractional_seconds.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/have_fractional_seconds.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS running_records;
+--enable_warnings
+
+CREATE TABLE running_records (
+ time TIME(6) PRIMARY KEY,
+ title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE running_records;
+
+INSERT INTO running_records (time, title)
+ VALUES ("01:00:00.000001", "normal condition");
+INSERT INTO running_records (time, title)
+ VALUES ("12:23:34.123456", "bad condition");
+INSERT INTO running_records (time, title)
+ VALUES ("-838:59:59.000000", "record failure");
+
+SELECT * FROM running_records;
+
+SELECT * FROM running_records
+ WHERE time BETWEEN "00:59:59.999999" AND "12:23:34.123456";
+
+DROP TABLE running_records;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_time_without_fractional_seconds.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_time_without_fractional_seconds.test
new file mode 100644
index 00000000000..f000f7390f6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_time_without_fractional_seconds.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS running_records;
+--enable_warnings
+
+CREATE TABLE running_records (
+ time TIME PRIMARY KEY,
+ title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE running_records;
+
+INSERT INTO running_records (time, title)
+ VALUES ("01:00:00", "normal condition");
+INSERT INTO running_records (time, title)
+ VALUES ("12:23:34", "bad condition");
+INSERT INTO running_records (time, title)
+ VALUES ("-838:59:59", "record failure");
+
+SELECT * FROM running_records;
+
+SELECT * FROM running_records
+ WHERE time BETWEEN "00:59:59" AND "12:23:34";
+
+DROP TABLE running_records;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_timestamp_with_fractional_seconds.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_timestamp_with_fractional_seconds.test
new file mode 100644
index 00000000000..aba635ad637
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_timestamp_with_fractional_seconds.test
@@ -0,0 +1,46 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/skip_mariadb_55.inc
+--source ../../include/mroonga/have_fractional_seconds.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ time TIMESTAMP(6) PRIMARY KEY,
+ title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (time, title)
+ VALUES ("2012-01-29 21:51:01.111111", "clear day");
+INSERT INTO diaries (time, title)
+ VALUES ("2012-01-30 01:23:45.333", "rainy day");
+INSERT INTO diaries (time, title)
+ VALUES ("2012-01-31 08:32:10.5555", "cloudy day");
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries
+ WHERE time BETWEEN "2012-01-29 00:00:00.123456" AND
+ "2012-01-31 00:00:00.999999";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_timestamp_without_fractional_seconds.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_timestamp_without_fractional_seconds.test
new file mode 100644
index 00000000000..ae61434c795
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_timestamp_without_fractional_seconds.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ time TIMESTAMP PRIMARY KEY,
+ title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (time, title) VALUES ("2012-01-29 21:51:01", "clear day");
+INSERT INTO diaries (time, title) VALUES ("2012-01-30 01:23:45", "rainy day");
+INSERT INTO diaries (time, title) VALUES ("2012-01-31 08:32:10", "cloudy day");
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries
+ WHERE time BETWEEN "2012-01-29 00:00:00" AND "2012-01-31 00:00:00";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_varchar_null_character.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_varchar_null_character.test
new file mode 100644
index 00000000000..20ae939be8b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_varchar_null_character.test
@@ -0,0 +1,37 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id varchar(7) CHARACTER SET latin1 COLLATE latin1_bin PRIMARY KEY
+);
+
+INSERT INTO ids VALUES("\0abcdef");
+INSERT INTO ids VALUES("ab\0cdef");
+INSERT INTO ids VALUES("abcd\0ef");
+
+SELECT * FROM ids FORCE INDEX(PRIMARY) ORDER BY id;
+
+SELECT * FROM ids FORCE INDEX(PRIMARY) WHERE id = "ab\0cdef";
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_year.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_year.test
new file mode 100644
index 00000000000..25f02d38e94
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_primary_year.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS aniversary_memos;
+--enable_warnings
+
+CREATE TABLE aniversary_memos (
+ party_year YEAR PRIMARY KEY,
+ title TEXT
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE aniversary_memos;
+
+INSERT INTO aniversary_memos (party_year, title)
+ VALUES ("11", "We need a big cake!");
+INSERT INTO aniversary_memos (party_year, title)
+ VALUES ("2012", "Invitations are sent.");
+INSERT INTO aniversary_memos (party_year, title)
+ VALUES ("13", "Wow! Today is the anniversary party day!");
+
+SELECT * FROM aniversary_memos;
+
+SELECT * FROM aniversary_memos
+ WHERE party_year BETWEEN "12" AND "2013";
+
+DROP TABLE aniversary_memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_asc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_asc.test
new file mode 100644
index 00000000000..a622dfbf70d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_asc.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+SET NAMES UTF8;
+
+CREATE TABLE ids (
+ id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+
+SELECT * FROM ids WHERE ids.id > 1 ORDER BY ids.id ASC LIMIT 3;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_desc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_desc.test
new file mode 100644
index 00000000000..63dc820ccfd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_desc.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+SET NAMES UTF8;
+
+CREATE TABLE ids (
+ id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+
+SELECT * FROM ids WHERE ids.id > 3 ORDER BY ids.id DESC LIMIT 3;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_or_equal_asc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_or_equal_asc.test
new file mode 100644
index 00000000000..a51e6c5da47
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_or_equal_asc.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+SET NAMES UTF8;
+
+CREATE TABLE ids (
+ id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+
+SELECT * FROM ids WHERE ids.id >= 2 ORDER BY ids.id ASC LIMIT 3;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_or_equal_desc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_or_equal_desc.test
new file mode 100644
index 00000000000..ea5c150bf0f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_greater_than_or_equal_desc.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+SET NAMES UTF8;
+
+CREATE TABLE ids (
+ id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+
+SELECT * FROM ids WHERE ids.id >= 3 ORDER BY ids.id DESC LIMIT 3;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_asc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_asc.test
new file mode 100644
index 00000000000..12761c0f4d8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_asc.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+SET NAMES UTF8;
+
+CREATE TABLE ids (
+ id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+
+SELECT * FROM ids WHERE ids.id < 4 ORDER BY ids.id ASC LIMIT 3;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_desc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_desc.test
new file mode 100644
index 00000000000..a4f4a781ead
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_desc.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+SET NAMES UTF8;
+
+CREATE TABLE ids (
+ id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+
+SELECT * FROM ids WHERE ids.id < 4 ORDER BY ids.id DESC LIMIT 3;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_or_equal_asc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_or_equal_asc.test
new file mode 100644
index 00000000000..2fba0d13f5f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_or_equal_asc.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+SET NAMES UTF8;
+
+CREATE TABLE ids (
+ id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+
+SELECT * FROM ids WHERE ids.id <= 4 ORDER BY ids.id ASC LIMIT 3;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_or_equal_desc.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_or_equal_desc.test
new file mode 100644
index 00000000000..e4661fc7047
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_range_less_than_or_equal_desc.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+SET NAMES UTF8;
+
+CREATE TABLE ids (
+ id INT(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT
+) ENGINE=Mroonga DEFAULT CHARSET=utf8;
+
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+INSERT INTO ids VALUES();
+
+SELECT * FROM ids WHERE ids.id <= 4 ORDER BY ids.id DESC LIMIT 3;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_bigint.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_bigint.test
new file mode 100644
index 00000000000..53111b576d7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_bigint.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id BIGINT,
+ value BIGINT,
+ KEY (id, value)
+);
+
+INSERT INTO ids VALUES ( -1, 16);
+INSERT INTO ids VALUES ( -2, 8);
+INSERT INTO ids VALUES ( -4, 4);
+INSERT INTO ids VALUES ( -8, 2);
+INSERT INTO ids VALUES (-16, 1);
+INSERT INTO ids VALUES ( 16, -1);
+INSERT INTO ids VALUES ( 8, -2);
+INSERT INTO ids VALUES ( 4, -4);
+INSERT INTO ids VALUES ( 2, -8);
+INSERT INTO ids VALUES ( 1, -16);
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN -4 AND 8;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_bigint_unsigned.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_bigint_unsigned.test
new file mode 100644
index 00000000000..5e93137afeb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_bigint_unsigned.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id BIGINT UNSIGNED,
+ value BIGINT UNSIGNED,
+ KEY (id, value)
+);
+
+INSERT INTO ids VALUES ( 1, 1);
+INSERT INTO ids VALUES ( 2, 2);
+INSERT INTO ids VALUES ( 4, 3);
+INSERT INTO ids VALUES ( 8, 4);
+INSERT INTO ids VALUES (16, 5);
+INSERT INTO ids VALUES (32, 6);
+INSERT INTO ids VALUES (64, 7);
+INSERT INTO ids VALUES (128, 8);
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN 4 AND 32;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_double.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_double.test
new file mode 100644
index 00000000000..0ca7d948318
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_double.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id DOUBLE,
+ value DOUBLE,
+ KEY (id, value)
+);
+
+INSERT INTO ids VALUES ( -1.1, 16.16);
+INSERT INTO ids VALUES ( -2.2, 8.8);
+INSERT INTO ids VALUES ( -4.4, 4.4);
+INSERT INTO ids VALUES ( -8.8, 2.2);
+INSERT INTO ids VALUES (-16.6, 1.1);
+INSERT INTO ids VALUES ( 16.6, -1.1);
+INSERT INTO ids VALUES ( 8.8, -2.2);
+INSERT INTO ids VALUES ( 4.4, -4.4);
+INSERT INTO ids VALUES ( 2.2, -8.8);
+INSERT INTO ids VALUES ( 1.1, -16.16);
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN -4.5 AND 8.9;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_float.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_float.test
new file mode 100644
index 00000000000..ade89f6bc56
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_float.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id FLOAT,
+ value FLOAT,
+ KEY (id, value)
+);
+
+INSERT INTO ids VALUES ( -1.1, 16.16);
+INSERT INTO ids VALUES ( -2.2, 8.8);
+INSERT INTO ids VALUES ( -4.4, 4.4);
+INSERT INTO ids VALUES ( -8.8, 2.2);
+INSERT INTO ids VALUES (-16.6, 1.1);
+INSERT INTO ids VALUES ( 16.6, -1.1);
+INSERT INTO ids VALUES ( 8.8, -2.2);
+INSERT INTO ids VALUES ( 4.4, -4.4);
+INSERT INTO ids VALUES ( 2.2, -8.8);
+INSERT INTO ids VALUES ( 1.1, -16.16);
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN -4.5 AND 8.9;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_int.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_int.test
new file mode 100644
index 00000000000..9ed4979fd8f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_int.test
@@ -0,0 +1,46 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id INT,
+ value INT,
+ KEY (id, value)
+);
+
+INSERT INTO ids VALUES ( -1, 16);
+INSERT INTO ids VALUES ( -2, 8);
+INSERT INTO ids VALUES ( -4, 4);
+INSERT INTO ids VALUES ( -8, 2);
+INSERT INTO ids VALUES (-16, 1);
+INSERT INTO ids VALUES ( 16, -1);
+INSERT INTO ids VALUES ( 8, -2);
+INSERT INTO ids VALUES ( 4, -4);
+INSERT INTO ids VALUES ( 2, -8);
+INSERT INTO ids VALUES ( 1, -16);
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN -4 AND 8;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_int_unsigned.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_int_unsigned.test
new file mode 100644
index 00000000000..f68c2f6906c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_int_unsigned.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id INT UNSIGNED,
+ value INT UNSIGNED,
+ KEY (id, value)
+);
+
+INSERT INTO ids VALUES ( 1, 1);
+INSERT INTO ids VALUES ( 2, 2);
+INSERT INTO ids VALUES ( 4, 3);
+INSERT INTO ids VALUES ( 8, 4);
+INSERT INTO ids VALUES (16, 5);
+INSERT INTO ids VALUES (32, 6);
+INSERT INTO ids VALUES (64, 7);
+INSERT INTO ids VALUES (128, 8);
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN 4 AND 32;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_mediumint.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_mediumint.test
new file mode 100644
index 00000000000..254345b6ec6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_mediumint.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id MEDIUMINT,
+ value MEDIUMINT,
+ KEY (id, value)
+);
+
+INSERT INTO ids VALUES ( -1, 16);
+INSERT INTO ids VALUES ( -2, 8);
+INSERT INTO ids VALUES ( -4, 4);
+INSERT INTO ids VALUES ( -8, 2);
+INSERT INTO ids VALUES (-16, 1);
+INSERT INTO ids VALUES ( 16, -1);
+INSERT INTO ids VALUES ( 8, -2);
+INSERT INTO ids VALUES ( 4, -4);
+INSERT INTO ids VALUES ( 2, -8);
+INSERT INTO ids VALUES ( 1, -16);
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN -4 AND 8;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_mediumint_unsigned.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_mediumint_unsigned.test
new file mode 100644
index 00000000000..8975c5120cb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_mediumint_unsigned.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id MEDIUMINT UNSIGNED,
+ value MEDIUMINT UNSIGNED,
+ KEY (id, value)
+);
+
+INSERT INTO ids VALUES ( 1, 1);
+INSERT INTO ids VALUES ( 2, 2);
+INSERT INTO ids VALUES ( 4, 3);
+INSERT INTO ids VALUES ( 8, 4);
+INSERT INTO ids VALUES (16, 5);
+INSERT INTO ids VALUES (32, 6);
+INSERT INTO ids VALUES (64, 7);
+INSERT INTO ids VALUES (128, 8);
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN 4 AND 32;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_smallint.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_smallint.test
new file mode 100644
index 00000000000..4ed8c4b366e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_smallint.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id SMALLINT,
+ value SMALLINT,
+ KEY (id, value)
+);
+
+INSERT INTO ids VALUES ( -1, 16);
+INSERT INTO ids VALUES ( -2, 8);
+INSERT INTO ids VALUES ( -4, 4);
+INSERT INTO ids VALUES ( -8, 2);
+INSERT INTO ids VALUES (-16, 1);
+INSERT INTO ids VALUES ( 16, -1);
+INSERT INTO ids VALUES ( 8, -2);
+INSERT INTO ids VALUES ( 4, -4);
+INSERT INTO ids VALUES ( 2, -8);
+INSERT INTO ids VALUES ( 1, -16);
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN -4 AND 8;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_smallint_unsigned.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_smallint_unsigned.test
new file mode 100644
index 00000000000..ef5da3e79c9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_smallint_unsigned.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id SMALLINT UNSIGNED,
+ value SMALLINT UNSIGNED,
+ KEY (id, value)
+);
+
+INSERT INTO ids VALUES ( 1, 1);
+INSERT INTO ids VALUES ( 2, 2);
+INSERT INTO ids VALUES ( 4, 3);
+INSERT INTO ids VALUES ( 8, 4);
+INSERT INTO ids VALUES (16, 5);
+INSERT INTO ids VALUES (32, 6);
+INSERT INTO ids VALUES (64, 7);
+INSERT INTO ids VALUES (128, 8);
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN 4 AND 32;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_tinyint.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_tinyint.test
new file mode 100644
index 00000000000..5c738fa567b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_tinyint.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id TINYINT,
+ value TINYINT,
+ KEY (id, value)
+);
+
+INSERT INTO ids VALUES ( -1, 16);
+INSERT INTO ids VALUES ( -2, 8);
+INSERT INTO ids VALUES ( -4, 4);
+INSERT INTO ids VALUES ( -8, 2);
+INSERT INTO ids VALUES (-16, 1);
+INSERT INTO ids VALUES ( 16, -1);
+INSERT INTO ids VALUES ( 8, -2);
+INSERT INTO ids VALUES ( 4, -4);
+INSERT INTO ids VALUES ( 2, -8);
+INSERT INTO ids VALUES ( 1, -16);
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN -4 AND 8;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_tinyint_unsigned.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_tinyint_unsigned.test
new file mode 100644
index 00000000000..7f17f0031db
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_tinyint_unsigned.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id TINYINT UNSIGNED,
+ value TINYINT UNSIGNED,
+ KEY (id, value)
+);
+
+INSERT INTO ids VALUES ( 1, 1);
+INSERT INTO ids VALUES ( 2, 2);
+INSERT INTO ids VALUES ( 4, 3);
+INSERT INTO ids VALUES ( 8, 4);
+INSERT INTO ids VALUES (16, 5);
+INSERT INTO ids VALUES (32, 6);
+INSERT INTO ids VALUES (64, 7);
+INSERT INTO ids VALUES (128, 8);
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN 4 AND 32;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_varchar.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_varchar.test
new file mode 100644
index 00000000000..ccd53a95237
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_varchar.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id VARCHAR(5),
+ value VARCHAR(10),
+ KEY (id, value)
+) DEFAULT CHARSET=utf8 COLLATE utf8_bin;
+
+INSERT INTO ids VALUES ("abc", "Abc");
+INSERT INTO ids VALUES ("acd", "aBc");
+INSERT INTO ids VALUES ("ade", "abC");
+INSERT INTO ids VALUES ("aef", "abc");
+INSERT INTO ids VALUES ("ABC", "aBC");
+INSERT INTO ids VALUES ("ACD", "AbC");
+INSERT INTO ids VALUES ("ADE", "ABc");
+INSERT INTO ids VALUES ("AEF", "ABC");
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN "ab" AND "ad";
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_varchar_collation.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_varchar_collation.test
new file mode 100644
index 00000000000..1d799e76696
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_multiple_varchar_collation.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id VARCHAR(5),
+ value VARCHAR(10),
+ KEY (id, value)
+) DEFAULT CHARSET=utf8 COLLATE utf8_unicode_ci;
+
+INSERT INTO ids VALUES ("abc", "Abc");
+INSERT INTO ids VALUES ("acd", "aBc");
+INSERT INTO ids VALUES ("ade", "abC");
+INSERT INTO ids VALUES ("aef", "abc");
+INSERT INTO ids VALUES ("ABC", "aBC");
+INSERT INTO ids VALUES ("ACD", "AbC");
+INSERT INTO ids VALUES ("ADE", "ABc");
+INSERT INTO ids VALUES ("AEF", "ABC");
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN "ab" AND "ad";
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_normal_int.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_normal_int.test
new file mode 100644
index 00000000000..a7ff9f3a600
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_normal_int.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id INT,
+ KEY (id)
+);
+
+INSERT INTO ids VALUES (1);
+INSERT INTO ids SELECT id + 1 FROM ids;
+INSERT INTO ids SELECT id + 2 FROM ids;
+INSERT INTO ids SELECT id + 4 FROM ids;
+INSERT INTO ids SELECT id + 8 FROM ids;
+INSERT INTO ids SELECT id + 16 FROM ids;
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN 10 AND 16;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_normal_varchar.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_normal_varchar.test
new file mode 100644
index 00000000000..4d094df41cd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_normal_varchar.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id VARCHAR(10),
+ KEY (id)
+);
+
+INSERT INTO ids VALUES ("1");
+INSERT INTO ids SELECT id + "1" FROM ids;
+INSERT INTO ids SELECT id + "2" FROM ids;
+INSERT INTO ids SELECT id + "4" FROM ids;
+INSERT INTO ids SELECT id + "8" FROM ids;
+INSERT INTO ids SELECT id + "16" FROM ids;
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN "10" AND "16";
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_primary_int.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_primary_int.test
new file mode 100644
index 00000000000..68f132367f2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_primary_int.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id INT,
+ PRIMARY KEY (id)
+);
+
+INSERT INTO ids VALUES (1);
+INSERT INTO ids SELECT id + 1 FROM ids;
+INSERT INTO ids SELECT id + 2 FROM ids;
+INSERT INTO ids SELECT id + 4 FROM ids;
+INSERT INTO ids SELECT id + 8 FROM ids;
+INSERT INTO ids SELECT id + 16 FROM ids;
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN 10 AND 16;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_read_primary_varchar.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_primary_varchar.test
new file mode 100644
index 00000000000..aa9ba644062
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_read_primary_varchar.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id VARCHAR(10),
+ PRIMARY KEY (id)
+);
+
+INSERT INTO ids VALUES ("1");
+INSERT INTO ids SELECT id + "1" FROM ids;
+INSERT INTO ids SELECT id + "2" FROM ids;
+INSERT INTO ids SELECT id + "4" FROM ids;
+INSERT INTO ids SELECT id + "8" FROM ids;
+INSERT INTO ids SELECT id + "16" FROM ids;
+
+SELECT * FROM ids;
+SELECT * FROM ids WHERE id BETWEEN "10" AND "16";
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_unique_delete_by_primary_key.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_unique_delete_by_primary_key.test
new file mode 100644
index 00000000000..2c1ec45c045
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_unique_delete_by_primary_key.test
@@ -0,0 +1,37 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS users;
+--enable_warnings
+
+CREATE TABLE users (
+ id int PRIMARY KEY,
+ name varchar(100) NOT NULL,
+ UNIQUE KEY name (name)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO users VALUES (1, "Alice");
+DELETE FROM users WHERE id = 1;
+INSERT INTO users VALUES (1, "Alice");
+
+SELECT * FROM users;
+
+DROP TABLE users;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_unique_insert_after_error.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_unique_insert_after_error.test
new file mode 100644
index 00000000000..6c47c877771
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_unique_insert_after_error.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS users;
+--enable_warnings
+
+CREATE TABLE users (
+ id int PRIMARY KEY,
+ name varchar(100) NOT NULL,
+ UNIQUE KEY name (name)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO users VALUES (1, "Alice");
+-- error ER_DUP_ENTRY
+INSERT INTO users VALUES (1, "Bob");
+INSERT INTO users VALUES (2, "Bob");
+
+SELECT * FROM users;
+
+DROP TABLE users;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_unique_varchar.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_unique_varchar.test
new file mode 100644
index 00000000000..21446b302f2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_unique_varchar.test
@@ -0,0 +1,36 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS users;
+--enable_warnings
+
+CREATE TABLE users (
+ name varchar(100) NOT NULL,
+ UNIQUE KEY name (name)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO users VALUES ("Alice");
+INSERT INTO users VALUES ("Bob");
+SELECT * FROM users;
+
+SELECT * FROM users WHERE name = "aLiCe";
+
+DROP TABLE users;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_update_multiple_column.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_update_multiple_column.test
new file mode 100644
index 00000000000..bbf368551af
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_update_multiple_column.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS scores;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE scores (
+ deleted BOOLEAN,
+ value INT,
+ INDEX (deleted, value)
+);
+
+INSERT INTO scores VALUES (FALSE, 1);
+INSERT INTO scores VALUES (FALSE, 1);
+INSERT INTO scores VALUES (FALSE, 2);
+
+SELECT count(*) FROM scores WHERE deleted = FALSE;
+UPDATE scores SET deleted = TRUE WHERE value = 1;
+SELECT count(*) FROM scores WHERE deleted = FALSE;
+
+DROP TABLE scores;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/index_update_single_column.test b/storage/mroonga/mysql-test/mroonga/storage/t/index_update_single_column.test
new file mode 100644
index 00000000000..a0dc2f8a952
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/index_update_single_column.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS scores;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE scores (
+ value INT,
+ INDEX (value)
+);
+
+INSERT INTO scores VALUES (21);
+INSERT INTO scores VALUES (21);
+INSERT INTO scores VALUES (22);
+
+SELECT count(*) FROM scores WHERE value >= 20;
+UPDATE scores SET value = 11 WHERE value = 21;
+SELECT count(*) FROM scores WHERE value >= 20;
+
+DROP TABLE scores;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/information_schema_plugins.test b/storage/mroonga/mysql-test/mroonga/storage/t/information_schema_plugins.test
new file mode 100644
index 00000000000..d22c6560c2c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/information_schema_plugins.test
@@ -0,0 +1,22 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+select PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_TYPE
+ from information_schema.plugins where plugin_name = "Mroonga";
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/information_schema_tables_auto_increment_none.test b/storage/mroonga/mysql-test/mroonga/storage/t/information_schema_tables_auto_increment_none.test
new file mode 100644
index 00000000000..2956e072302
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/information_schema_tables_auto_increment_none.test
@@ -0,0 +1,34 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id INT PRIMARY KEY
+);
+
+SELECT AUTO_INCREMENT
+ FROM INFORMATION_SCHEMA.TABLES
+ WHERE TABLE_NAME = "ids";
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/information_schema_tables_auto_increment_use.test b/storage/mroonga/mysql-test/mroonga/storage/t/information_schema_tables_auto_increment_use.test
new file mode 100644
index 00000000000..79e12fe67a9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/information_schema_tables_auto_increment_use.test
@@ -0,0 +1,34 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id INT AUTO_INCREMENT PRIMARY KEY
+);
+
+SELECT AUTO_INCREMENT
+ FROM INFORMATION_SCHEMA.TABLES
+ WHERE TABLE_NAME = "ids";
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/information_schema_tables_data_length.test b/storage/mroonga/mysql-test/mroonga/storage/t/information_schema_tables_data_length.test
new file mode 100644
index 00000000000..82dd4fdf1c6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/information_schema_tables_data_length.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT PRIMARY KEY,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+
+SELECT COUNT(*)
+ FROM INFORMATION_SCHEMA.TABLES
+ WHERE TABLE_NAME = "diaries" AND DATA_LENGTH > 0;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/insert_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/storage/t/insert_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..3fa85344dfa
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/insert_TODO_SPLIT_ME.test
@@ -0,0 +1,105 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+# data types
+create table t1 (c1 tinyint);
+insert into t1 values(1);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 smallint);
+insert into t1 values(1);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 mediumint);
+insert into t1 values(1);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 int);
+insert into t1 values(1);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 bigint);
+insert into t1 values(1);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 float);
+insert into t1 values(0.5);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 double);
+insert into t1 values(0.5);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 date);
+insert into t1 values("2010/03/26");
+select * from t1;
+drop table t1;
+
+create table t1 (c1 time);
+insert into t1 values("11:22:33");
+select * from t1;
+drop table t1;
+
+create table t1 (c1 year);
+insert into t1 values("2010");
+select * from t1;
+drop table t1;
+
+create table t1 (c1 datetime);
+insert into t1 values("2010/03/26 11:22:33");
+select * from t1;
+drop table t1;
+
+
+# for virtual columns
+create table t1 (c1 int, _id int);
+set sql_mode="";
+# warning WARN_DATA_TRUNCATED
+insert into t1 (c1,_id) values (1,1);
+set sql_mode="strict_all_tables";
+# We can't use WARN_DATA_TRUNCATED here because "WXXX" isn't supported
+# MySQL 5.5, 5.6 and MariaDB 5.6. MariaDB 10.0 only supports it.
+# We share this test with all MySQL servers. So we use number here.
+--error 1265
+insert into t1 (c1,_id) values (4,1);
+select * from t1;
+drop table t1;
+
+
+# duplicated key error
+create table t1 (c1 int primary key, c2 int);
+insert into t1 values(1,100);
+select * from t1;
+--error ER_DUP_ENTRY
+insert into t1 values(1,200);
+select * from t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/insert_on_duplicate_key_update_no_primary_key_and_unique_key_twice.test b/storage/mroonga/mysql-test/mroonga/storage/t/insert_on_duplicate_key_update_no_primary_key_and_unique_key_twice.test
new file mode 100644
index 00000000000..c3530dc34c0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/insert_on_duplicate_key_update_no_primary_key_and_unique_key_twice.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS numbers;
+--enable_warnings
+
+CREATE TABLE numbers (
+ id INT,
+ count INT,
+ UNIQUE (id)
+);
+
+INSERT INTO numbers (id, count) VALUES (1, 1) ON DUPLICATE KEY UPDATE count = 2;
+INSERT INTO numbers (id, count) VALUES (1, 3) ON DUPLICATE KEY UPDATE count = 4;
+
+SELECT * FROM numbers;
+
+INSERT INTO numbers (id, count) VALUES (2, 1) ON DUPLICATE KEY UPDATE count = 2;
+INSERT INTO numbers (id, count) VALUES (2, 3) ON DUPLICATE KEY UPDATE count = 4;
+
+SELECT * FROM numbers;
+
+DROP TABLE numbers;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/insert_on_duplicate_key_update_primary_key.test b/storage/mroonga/mysql-test/mroonga/storage/t/insert_on_duplicate_key_update_primary_key.test
new file mode 100644
index 00000000000..794605d742b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/insert_on_duplicate_key_update_primary_key.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ day DATE PRIMARY KEY,
+ title TEXT
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (day, title)
+ VALUES ("2012-02-14", "clear day")
+ ON DUPLICATE KEY UPDATE title = "clear day (duplicated)";
+INSERT INTO diaries (day, title)
+ VALUES ("2012-02-14", "rainy day")
+ ON DUPLICATE KEY UPDATE title = "rainy day (duplicated)";
+INSERT INTO diaries (day, title)
+ VALUES ("2012-02-15", "cloudy day")
+ ON DUPLICATE KEY UPDATE title = "cloudy day (duplicated)";
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/insert_on_duplicate_key_update_unique_key.test b/storage/mroonga/mysql-test/mroonga/storage/t/insert_on_duplicate_key_update_unique_key.test
new file mode 100644
index 00000000000..782b7bee9ca
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/insert_on_duplicate_key_update_unique_key.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ day DATE,
+ title TEXT,
+ UNIQUE KEY day (day)
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (day, title)
+ VALUES ("2012-02-14", "clear day")
+ ON DUPLICATE KEY UPDATE title = "clear day (duplicated)";
+INSERT INTO diaries (day, title)
+ VALUES ("2012-02-14", "rainy day")
+ ON DUPLICATE KEY UPDATE title = "rainy day (duplicated)";
+INSERT INTO diaries (day, title)
+ VALUES ("2012-02-15", "cloudy day")
+ ON DUPLICATE KEY UPDATE title = "cloudy day (duplicated)";
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/like_unicode_ci.test b/storage/mroonga/mysql-test/mroonga/storage/t/like_unicode_ci.test
new file mode 100644
index 00000000000..18801541b72
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/like_unicode_ci.test
@@ -0,0 +1,37 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS terms;
+--enable_warnings
+
+SET NAMES utf8;
+
+CREATE TABLE terms (
+ content varchar(64) NOT NULL COLLATE 'utf8_unicode_ci',
+ INDEX (content)
+) DEFAULT CHARSET=utf8;
+
+INSERT INTO terms VALUES ('track');
+INSERT INTO terms VALUES ('trackback');
+
+SELECT * FROM terms WHERE content LIKE 'TRACK%';
+
+DROP TABLE terms;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/lock_tables_read.test b/storage/mroonga/mysql-test/mroonga/storage/t/lock_tables_read.test
new file mode 100644
index 00000000000..b7093f82aa5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/lock_tables_read.test
@@ -0,0 +1,32 @@
+# Copyright(C) 2013 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS counts;
+--enable_warnings
+
+CREATE TABLE counts (
+ id INT PRIMARY KEY AUTO_INCREMENT
+);
+
+LOCK TABLES counts READ;
+UNLOCK TABLES;
+
+DROP TABLE counts;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_not_optimized_disabled.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_not_optimized_disabled.test
new file mode 100644
index 00000000000..8c818041a6c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_not_optimized_disabled.test
@@ -0,0 +1,60 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SET mroonga_enable_optimization=FALSE;
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+ month = 11
+ ORDER BY day LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+SET mroonga_enable_optimization=TRUE;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_not_optimized_no_limit.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_not_optimized_no_limit.test
new file mode 100644
index 00000000000..d75a6460218
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_not_optimized_no_limit.test
@@ -0,0 +1,55 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ ORDER BY day;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_between.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_between.test
new file mode 100644
index 00000000000..61e16a9444d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_between.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ date DATETIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:30", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:31", "Today's lucky item is flower!");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:32", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:33", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ date BETWEEN "2011-11-11 12:23:31" AND "2011-11-11 12:23:33"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_between_over.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_between_over.test
new file mode 100644
index 00000000000..4b75648009b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_between_over.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ date DATETIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:30", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:31", "Today's lucky item is flower!");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:32", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:33", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ date BETWEEN "2011-11-11 12:23:31" AND "2011-11-11 12:23:43"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_equal.test
new file mode 100644
index 00000000000..f3b4be95b3a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_equal.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ date DATETIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:34", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:34", "Tomorrow will be fine.");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:34", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:34", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ date = "2011-11-11 12:23:34"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_greater_than.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_greater_than.test
new file mode 100644
index 00000000000..ce5724b8b0b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_greater_than.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ date DATETIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:30", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:31", "Today's lucky item is flower!");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:32", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:33", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ date > "2011-11-11 12:23:31"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_greater_than_or_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_greater_than_or_equal.test
new file mode 100644
index 00000000000..4c55ba2a895
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_greater_than_or_equal.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ date DATETIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:30", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:31", "Today's lucky item is flower!");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:32", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:33", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ date >= "2011-11-11 12:23:31"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_less_than.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_less_than.test
new file mode 100644
index 00000000000..36f0e0ab6f2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_less_than.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ date DATETIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:30", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:31", "Today's lucky item is flower!");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:32", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:33", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ date < "2011-11-11 12:23:33"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_less_than_or_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_less_than_or_equal.test
new file mode 100644
index 00000000000..3fc1505fcc2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_datetime_less_than_or_equal.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ date DATETIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(date)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, "2011-11-11 12:23:30", "Today is fine.");
+INSERT INTO diaries VALUES(2, "2011-11-11 12:23:31", "Today's lucky item is flower!");
+INSERT INTO diaries VALUES(3, "2011-11-11 12:23:32", "I will do something today!");
+INSERT INTO diaries VALUES(4, "2011-11-11 12:23:33", "I don't want to anything today...");
+INSERT INTO diaries VALUES(5, "2011-11-11 12:23:34", "I'm sleepy today.");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ date <= "2011-11-11 12:23:33"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_have_primary_key.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_have_primary_key.test
new file mode 100644
index 00000000000..0733a48ba97
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_have_primary_key.test
@@ -0,0 +1,51 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 11, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(6, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE) ORDER BY day LIMIT 0,5;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_between.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_between.test
new file mode 100644
index 00000000000..f96043b4e37
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_between.test
@@ -0,0 +1,48 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT UNSIGNED,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(id)
+) DEFAULT CHARSET UTF8;
+
+INSERT INTO memos VALUES(1, "Today is fine.");
+INSERT INTO memos VALUES(2, "Today's lucky item is flower!");
+INSERT INTO memos VALUES(3, "I will do something today!");
+INSERT INTO memos VALUES(4, "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "I'm sleepy today.");
+
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ id BETWEEN 2 AND 4
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_between_over.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_between_over.test
new file mode 100644
index 00000000000..39caa96424c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_between_over.test
@@ -0,0 +1,48 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT UNSIGNED,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(id)
+) DEFAULT CHARSET UTF8;
+
+INSERT INTO memos VALUES(1, "Today is fine.");
+INSERT INTO memos VALUES(2, "Today's lucky item is flower!");
+INSERT INTO memos VALUES(3, "I will do something today!");
+INSERT INTO memos VALUES(4, "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "I'm sleepy today.");
+
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ id BETWEEN 2 AND 6
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_equal.test
new file mode 100644
index 00000000000..579b7b33899
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_equal.test
@@ -0,0 +1,56 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+ month = 11
+ ORDER BY day LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_greater_than.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_greater_than.test
new file mode 100644
index 00000000000..c7354438224
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_greater_than.test
@@ -0,0 +1,56 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+ day > 10
+ ORDER BY day LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_greater_than_or_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_greater_than_or_equal.test
new file mode 100644
index 00000000000..259e2e5178d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_greater_than_or_equal.test
@@ -0,0 +1,56 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+ day >= 10
+ ORDER BY day LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_less_than.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_less_than.test
new file mode 100644
index 00000000000..02c2123afef
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_less_than.test
@@ -0,0 +1,56 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+ day < 12
+ ORDER BY day LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_less_than_or_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_less_than_or_equal.test
new file mode 100644
index 00000000000..0511de74442
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_int_less_than_or_equal.test
@@ -0,0 +1,56 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+ day <= 12
+ ORDER BY day LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_no_primary_key.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_no_primary_key.test
new file mode 100644
index 00000000000..b757b39e9ce
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_no_primary_key.test
@@ -0,0 +1,51 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 11, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(6, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE) ORDER BY day LIMIT 0,5;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_no_where_clause.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_no_where_clause.test
new file mode 100644
index 00000000000..b4d7aa348f0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_no_where_clause.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+flush status;
+create table t1 (c1 int primary key, c2 int, c3 text, _id int, key idx1(c2), fulltext index ft(c3)) default charset utf8;
+insert into t1 values(1,10,"aa ii uu ee oo",null);
+insert into t1 values(2,20,"ka ki ku ke ko",null);
+insert into t1 values(3,30,"ii si ii se ii",null);
+insert into t1 values(4,40,"ta ti tu te to",null);
+insert into t1 values(5,50,"aa ii uu ii oo",null);
+
+show status like 'mroonga_fast_order_limit';
+
+select *, match(c3) against("ii") from t1 order by c1 desc limit 2;
+
+show status like 'mroonga_fast_order_limit';
+
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_asc.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_asc.test
new file mode 100644
index 00000000000..74da8581596
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_asc.test
@@ -0,0 +1,55 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ ORDER BY day ASC LIMIT 1;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_desc.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_desc.test
new file mode 100644
index 00000000000..2e5d0741c51
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_desc.test
@@ -0,0 +1,55 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ ORDER BY day DESC LIMIT 1;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_id.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_id.test
new file mode 100644
index 00000000000..d7becd3820a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_id.test
@@ -0,0 +1,57 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ _id INT,
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(NULL, 1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(NULL, 2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(NULL, 3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(NULL, 4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(NULL, 5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(NULL, 6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(NULL, 7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ ORDER BY _id
+ LIMIT 1;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_match_against.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_match_against.test
new file mode 100644
index 00000000000..827faa70fa9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_order_by_match_against.test
@@ -0,0 +1,56 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ ORDER BY MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ LIMIT 1;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_select_match_against.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_select_match_against.test
new file mode 100644
index 00000000000..04a2309e98e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_select_match_against.test
@@ -0,0 +1,57 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT *, MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ ORDER BY MATCH(content) AGAINST("今日" IN BOOLEAN MODE)
+ LIMIT 1;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_between.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_between.test
new file mode 100644
index 00000000000..8c8fafc076c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_between.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT UNSIGNED NOT NULL,
+ writing_time TIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+
+INSERT INTO memos VALUES(1, "1:23:30", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:31", "Today's lucky item is flower!");
+INSERT INTO memos VALUES(3, "1:23:32", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:33", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ writing_time BETWEEN "1:23:31" AND "1:23:33"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_between_over.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_between_over.test
new file mode 100644
index 00000000000..9f9848b7e7e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_between_over.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT UNSIGNED NOT NULL,
+ writing_time TIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+
+INSERT INTO memos VALUES(1, "1:23:30", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:31", "Today's lucky item is flower!");
+INSERT INTO memos VALUES(3, "1:23:32", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:33", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ writing_time BETWEEN "1:23:31" AND "1:23:43"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_equal.test
new file mode 100644
index 00000000000..04efd54eb48
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_equal.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT UNSIGNED NOT NULL,
+ writing_time TIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+
+INSERT INTO memos VALUES(1, "1:23:34", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:34", "Tomorrow will be fine.");
+INSERT INTO memos VALUES(3, "1:23:34", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:34", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ writing_time = "1:23:34"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_greater_than.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_greater_than.test
new file mode 100644
index 00000000000..b2ec8b78198
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_greater_than.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT UNSIGNED NOT NULL,
+ writing_time TIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+
+INSERT INTO memos VALUES(1, "1:23:30", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:31", "Today's lucky item is flower!" );
+INSERT INTO memos VALUES(3, "1:23:32", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:33", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ writing_time > "1:23:31"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_greater_than_or_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_greater_than_or_equal.test
new file mode 100644
index 00000000000..0b9964eb542
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_greater_than_or_equal.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT UNSIGNED NOT NULL,
+ writing_time TIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+
+INSERT INTO memos VALUES(1, "1:23:30", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:31", "Today's lucky item is flower!" );
+INSERT INTO memos VALUES(3, "1:23:32", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:33", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ writing_time >= "1:23:31"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_less_than.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_less_than.test
new file mode 100644
index 00000000000..ce772a2ee6d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_less_than.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT UNSIGNED NOT NULL,
+ writing_time TIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+
+INSERT INTO memos VALUES(1, "1:23:30", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:31", "Today's lucky item is flower!");
+INSERT INTO memos VALUES(3, "1:23:32", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:33", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ writing_time < "1:23:33"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_less_than_or_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_less_than_or_equal.test
new file mode 100644
index 00000000000..797bd935eaa
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_time_less_than_or_equal.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE memos (
+ id INT UNSIGNED NOT NULL,
+ writing_time TIME,
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(writing_time)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE memos;
+
+INSERT INTO memos VALUES(1, "1:23:30", "Today is fine.");
+INSERT INTO memos VALUES(2, "1:23:31", "Today's lucky item is flower!");
+INSERT INTO memos VALUES(3, "1:23:32", "I will do something today!");
+INSERT INTO memos VALUES(4, "1:23:33", "I don't want to anything today...");
+INSERT INTO memos VALUES(5, "1:23:34", "I'm sleepy today.");
+
+SELECT * FROM memos
+ WHERE MATCH(content) AGAINST("today" IN BOOLEAN MODE) AND
+ writing_time <= "1:23:33"
+ ORDER BY id LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_varchar_equal_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_varchar_equal_with_index.test
new file mode 100644
index 00000000000..e34209b841e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_varchar_equal_with_index.test
@@ -0,0 +1,57 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(title),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+ title = "hello"
+ ORDER BY day LIMIT 1;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_varchar_equal_without_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_varchar_equal_without_index.test
new file mode 100644
index 00000000000..380f323d64a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_varchar_equal_without_index.test
@@ -0,0 +1,56 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(month),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, 2011, 11, 12, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, 2011, 11, 13, "はれ", "天気がよいのは今日までみたい。");
+INSERT INTO diaries VALUES(6, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(7, 2011, 12, 2, "初雪", "今日の天気は雪!");
+
+SELECT * FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE) AND
+ title = "hello"
+ ORDER BY day LIMIT 1;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_between.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_between.test
new file mode 100644
index 00000000000..988a116731c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_between.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS mroonga_releases;
+--enable_warnings
+
+FLUSH STATUS;
+
+CREATE TABLE mroonga_releases (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ release_title TEXT,
+ release_year YEAR,
+ KEY (release_year),
+ FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Groonga storage engine 0.1 has been released", "10");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 4.0 will be released", "2014");
+
+SELECT * FROM mroonga_releases
+ WHERE release_year BETWEEN "11" AND "2013" AND
+ MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ ORDER BY id DESC LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE mroonga_releases;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_between_over.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_between_over.test
new file mode 100644
index 00000000000..d4c1d020fcc
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_between_over.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS mroonga_releases;
+--enable_warnings
+
+FLUSH STATUS;
+
+CREATE TABLE mroonga_releases (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ release_title TEXT,
+ release_year YEAR,
+ KEY (release_year),
+ FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Groonga storage engine 0.1 has been released", "10");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 4.0 will be released", "2014");
+
+SELECT * FROM mroonga_releases
+ WHERE release_year BETWEEN "11" AND "2015" AND
+ MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ ORDER BY id DESC LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE mroonga_releases;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_equal.test
new file mode 100644
index 00000000000..b1daa3a0000
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_equal.test
@@ -0,0 +1,55 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS mroonga_releases;
+--enable_warnings
+
+FLUSH STATUS;
+
+CREATE TABLE mroonga_releases (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ release_title TEXT,
+ release_year YEAR,
+ KEY (release_year),
+ FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Groonga storage engine (code name Mroonga) 1.0 has been released", "11");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 1.11 has been released", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 4.0 will be released", "2014");
+
+SELECT * FROM mroonga_releases
+ WHERE release_year = "11" AND
+ MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ ORDER BY id DESC LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE mroonga_releases;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_greater_than.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_greater_than.test
new file mode 100644
index 00000000000..bf4a18793b5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_greater_than.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS mroonga_releases;
+--enable_warnings
+
+FLUSH STATUS;
+
+CREATE TABLE mroonga_releases (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ release_title TEXT,
+ release_year YEAR,
+ KEY (release_year),
+ FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Groonga storage engine (code name Mroonga) 0.1 has been released", "10");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 4.0 will be released", "2014");
+
+SELECT * FROM mroonga_releases
+ WHERE release_year > "11" AND
+ MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ ORDER BY id ASC LIMIT 2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE mroonga_releases;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_greater_than_or_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_greater_than_or_equal.test
new file mode 100644
index 00000000000..8863d613f06
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_greater_than_or_equal.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS mroonga_releases;
+--enable_warnings
+
+FLUSH STATUS;
+
+CREATE TABLE mroonga_releases (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ release_title TEXT,
+ release_year YEAR,
+ KEY (release_year),
+ FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Groonga storage engine (code name Mroonga) 0.1 has been released", "10");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 4.0 will be released", "2014");
+
+SELECT * FROM mroonga_releases
+ WHERE release_year >= "11" AND
+ MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ ORDER BY id ASC LIMIT 2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE mroonga_releases;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_less_than.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_less_than.test
new file mode 100644
index 00000000000..2fe423ac053
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_less_than.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS mroonga_releases;
+--enable_warnings
+
+FLUSH STATUS;
+
+CREATE TABLE mroonga_releases (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ release_title TEXT,
+ release_year YEAR,
+ KEY (release_year),
+ FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Groonga storage engine (code name Mroonga) 0.1 has been released", "10");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 4.0 will be released", "2014");
+
+SELECT * FROM mroonga_releases
+ WHERE release_year < "13" AND
+ MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ ORDER BY id DESC LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE mroonga_releases;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_less_than_or_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_less_than_or_equal.test
new file mode 100644
index 00000000000..52f26e37123
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_order_limit_optimized_year_less_than_or_equal.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS mroonga_releases;
+--enable_warnings
+
+FLUSH STATUS;
+
+CREATE TABLE mroonga_releases (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ release_title TEXT,
+ release_year YEAR,
+ KEY (release_year),
+ FULLTEXT KEY (release_title)
+) DEFAULT CHARSET UTF8;
+
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Groonga storage engine (code name Mroonga) 0.1 has been released", "10");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Rename Groonga storage engine to Mroonga", "2011");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 2.0 has been released", "2012");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 3.0 has been released", "13");
+INSERT INTO mroonga_releases (release_title, release_year)
+ VALUES ("Mroonga 4.0 will be released", "2014");
+
+SELECT * FROM mroonga_releases
+ WHERE release_year <= "13" AND
+ MATCH(release_title) AGAINST("Mroonga" IN BOOLEAN MODE)
+ ORDER BY id DESC LIMIT 1,2;
+
+SHOW STATUS LIKE 'mroonga_fast_order_limit';
+
+DROP TABLE mroonga_releases;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..669868c6c7f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_TODO_SPLIT_ME.test
@@ -0,0 +1,61 @@
+# Copyright(C) 2010 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+flush status;
+create table t1 (c1 int primary key, c2 int, c3 text, key idx1(c2), fulltext index ft(c3));
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"sa si su se so");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ee oo");
+show status like 'mroonga_count_skip';
+select * from t1;
+show status like 'mroonga_count_skip';
+select count(*) from t1;
+show status like 'mroonga_count_skip';
+select * from t1 force index(primary) where c1 between 2 and 4;
+show status like 'mroonga_count_skip';
+select count(*) from t1 force index(primary) where c1 between 2 and 4;
+show status like 'mroonga_count_skip';
+select c1 from t1 force index(primary) where c1 < 3;
+show status like 'mroonga_count_skip';
+select count(c1) from t1 force index(primary) where c1 < 3;
+show status like 'mroonga_count_skip';
+select 1 from t1 force index(primary) where c1 > 3;
+show status like 'mroonga_count_skip';
+select count(1) from t1 force index(primary) where c1 > 3;
+show status like 'mroonga_count_skip';
+select * from t1 where match(c3) against("su");
+show status like 'mroonga_count_skip';
+select count(*) from t1 where match(c3) against("su");
+show status like 'mroonga_count_skip';
+select * from t1 where match(c3) against("+su" in boolean mode);
+show status like 'mroonga_count_skip';
+select count(*) from t1 where match(c3) against("+su" in boolean mode);
+show status like 'mroonga_count_skip';
+select * from t1 force index(idx1) where c2 between 20 and 40;
+show status like 'mroonga_count_skip';
+select count(*) from t1 force index(idx1) where c2 between 20 and 40;
+show status like 'mroonga_count_skip';
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_after_insert_multithread.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_after_insert_multithread.test
new file mode 100644
index 00000000000..bca2311ffbc
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_after_insert_multithread.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+CREATE TABLE diaries (
+ title TEXT,
+ FULLTEXT INDEX ft(title)
+);
+
+INSERT INTO diaries VALUES("Hello mroonga!");
+INSERT INTO diaries VALUES("It's funny.");
+
+CONNECT (thread2, localhost, root, ,);
+CONNECTION thread2;
+INSERT INTO diaries VALUES("Happy birthday!");
+DISCONNECT thread2;
+CONNECTION default;
+
+SHOW STATUS LIKE 'mroonga_count_skip';
+SELECT COUNT(*) FROM diaries WHERE MATCH(title) AGAINST("mroonga" IN BOOLEAN MODE);
+SHOW STATUS LIKE 'mroonga_count_skip';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_after_insert_single_thread.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_after_insert_single_thread.test
new file mode 100644
index 00000000000..fa960be850f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_after_insert_single_thread.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kentoku SHIBA
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+CREATE TABLE diaries (
+ title TEXT,
+ FULLTEXT INDEX ft(title)
+);
+
+INSERT INTO diaries VALUES("Hello mroonga!");
+INSERT INTO diaries VALUES("It's funny.");
+INSERT INTO diaries VALUES("Happy birthday!");
+
+SHOW STATUS LIKE 'mroonga_count_skip';
+SELECT COUNT(*) FROM diaries WHERE MATCH(title) AGAINST("mroonga" IN BOOLEAN MODE);
+SHOW STATUS LIKE 'mroonga_count_skip';
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_disabled.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_disabled.test
new file mode 100644
index 00000000000..e94702e8159
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_disabled.test
@@ -0,0 +1,51 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+FLUSH STATUS;
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+INSERT INTO diaries VALUES(4, "帰り道", "今日は天気がよくてよかった。");
+INSERT INTO diaries VALUES(5, "はれ", "天気がよいのは今日までみたい。");
+
+SET mroonga_enable_optimization=FALSE;
+
+SELECT COUNT(*) FROM diaries
+ WHERE MATCH(content) AGAINST("今日" IN BOOLEAN MODE);
+
+SHOW STATUS LIKE 'mroonga_count_skip';
+
+SET mroonga_enable_optimization=TRUE;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_index_view.test b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_index_view.test
new file mode 100644
index 00000000000..11457b1e05a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/optimization_skip_count_index_view.test
@@ -0,0 +1,56 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries, users;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ user_id INT NOT NULL,
+ title VARCHAR(45) NOT NULL,
+ KEY (user_id),
+ FULLTEXT INDEX title_index (title)
+) DEFAULT CHARSET=UTF8;
+
+CREATE TABLE users (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ name VARCHAR(45) NOT NULL,
+ INDEX (name)
+) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
+
+INSERT INTO users (id, name) VALUES (1, "Alice"), (2, "Bob");
+INSERT INTO diaries (user_id, title) VALUES (1, "survey");
+INSERT INTO diaries (user_id, title) VALUES (2, "groonga (1)");
+INSERT INTO diaries (user_id, title) VALUES (2, "groonga (2)");
+
+CREATE VIEW articles AS
+ SELECT diaries.user_id AS user_id,
+ diaries.title AS title,
+ users.name AS name
+ FROM diaries, users
+ WHERE diaries.user_id = users.id;
+
+
+SELECT COUNT(*) FROM articles WHERE name = 'Bob';
+
+DROP VIEW articles;
+DROP TABLE diaries, users;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/replace_geometry.test b/storage/mroonga/mysql-test/mroonga/storage/t/replace_geometry.test
new file mode 100644
index 00000000000..5f160af239f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/replace_geometry.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS geo_replace;
+--enable_warnings
+
+CREATE TABLE geo_replace (
+ id INT NOT NULL,
+ geo GEOMETRY NOT NULL,
+ PRIMARY KEY(id)
+) DEFAULT CHARSET=utf8;
+INSERT INTO geo_replace VALUES(1, POINT(100,100));
+SELECT id, ASTEXT(geo) FROM geo_replace;
+REPLACE INTO geo_replace VALUES(1, POINT(100,200));
+SELECT id, ASTEXT(geo) FROM geo_replace;
+INSERT INTO geo_replace VALUES(1, POINT(200,200)) ON DUPLICATE KEY UPDATE geo = POINT(200,200);
+SELECT id, ASTEXT(geo) FROM geo_replace;
+UPDATE geo_replace SET geo = POINT(200,300);
+SELECT id, ASTEXT(geo) FROM geo_replace;
+
+DROP TABLE geo_replace;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/replace_select_varchar.test b/storage/mroonga/mysql-test/mroonga/storage/t/replace_select_varchar.test
new file mode 100644
index 00000000000..192a4976cbd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/replace_select_varchar.test
@@ -0,0 +1,64 @@
+# Copyright(C) 2011 Kouhei Sutou
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Based on #910.
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS videos_master, videos_groonga;
+--enable_warnings
+
+CREATE TABLE `videos_master` (
+ `id` bigint(1) unsigned NOT NULL,
+ `video_id` varchar(64) NOT NULL,
+ `description` text,
+ `tags_unpack` text,
+ PRIMARY KEY (`video_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE `videos_groonga` (
+ `id` bigint(1) unsigned NOT NULL,
+ `video_id` varchar(64) NOT NULL,
+ `description` text,
+ `tags_unpack` text,
+ PRIMARY KEY (`video_id`),
+ FULLTEXT INDEX (`description`),
+ FULLTEXT INDEX (`tags_unpack`)
+) DEFAULT CHARSET=utf8;
+
+
+INSERT INTO videos_master VALUES (1, "video-1", "My Familly", "familly human");
+INSERT INTO videos_master VALUES (2, "video-2", "My Cat", "family cat");
+REPLACE INTO videos_groonga
+ SELECT v.id, v.video_id, v.description, NULL
+ FROM videos_master AS v
+ WHERE v.video_id = (video_id);
+SELECT *, MATCH(description) AGAINST("cat") FROM videos_groonga
+ WHERE MATCH(description) AGAINST("cat");
+
+INSERT INTO videos_master VALUES (3, "video-3", "My Dog", "family dog");
+REPLACE INTO videos_groonga
+ SELECT v.id, v.video_id, v.description, NULL
+ FROM videos_master AS v
+ WHERE v.video_id = (video_id);
+SELECT *, MATCH(description) AGAINST("my") FROM videos_groonga
+ WHERE MATCH(description) AGAINST("my");
+
+DROP TABLE videos_master, videos_groonga;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/replace_text.test b/storage/mroonga/mysql-test/mroonga/storage/t/replace_text.test
new file mode 100644
index 00000000000..6411896312c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/replace_text.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set names utf8;
+create table diaries (
+ id int primary key,
+ content text,
+ fulltext index (content)
+) default charset utf8;
+show create table diaries;
+
+insert into diaries values(1, "今日からはじめました。");
+insert into diaries values(2, "明日の富士山の天気について");
+insert into diaries values(3, "今日も天気がよくてきれいに見える。");
+
+select * from diaries;
+
+select * from diaries where match(content) against("天気");
+
+replace into diaries values(2, "明日の天気は雨みたい。");
+select * from diaries where match(content) against("天気");
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/replace_varchar.test b/storage/mroonga/mysql-test/mroonga/storage/t/replace_varchar.test
new file mode 100644
index 00000000000..0d9b82fb7d4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/replace_varchar.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set names utf8;
+create table diaries (
+ id int primary key,
+ content varchar(256),
+ fulltext index (content)
+) default charset utf8;
+show create table diaries;
+
+insert into diaries values(1, "今日からはじめました。");
+insert into diaries values(2, "明日の富士山の天気について");
+insert into diaries values(3, "今日も天気がよくてきれいに見える。");
+
+select * from diaries;
+
+select * from diaries where match(content) against("天気");
+
+replace into diaries values(2, "明日の天気は雨みたい。");
+select * from diaries where match(content) against("天気");
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/replace_vector.test b/storage/mroonga/mysql-test/mroonga/storage/t/replace_vector.test
new file mode 100644
index 00000000000..a2efa7912b5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/replace_vector.test
@@ -0,0 +1,46 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS vector_replace;
+DROP TABLE IF EXISTS vector_replace_vec;
+--enable_warnings
+
+CREATE TABLE vector_replace_vec (
+ vec CHAR(10) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+ COMMENT='default_tokenizer "TokenDelimit"';
+
+CREATE TABLE vector_replace (
+ id INT NOT NULL,
+ vec TEXT COMMENT 'flags "COLUMN_VECTOR", type "vector_replace_vec"',
+ PRIMARY KEY(id)
+) DEFAULT CHARSET=utf8;
+INSERT INTO vector_replace VALUES(1, 'first second third');
+SELECT id, vec FROM vector_replace;
+REPLACE INTO vector_replace VALUES(1, 'fourth fifth');
+SELECT id, vec FROM vector_replace;
+INSERT INTO vector_replace VALUES(1, 'sixth seventh') ON DUPLICATE KEY UPDATE vec = 'sixth seventh';
+SELECT id, vec FROM vector_replace;
+UPDATE vector_replace SET vec = 'eighth nineth tenth';
+SELECT id, vec FROM vector_replace;
+
+DROP TABLE vector_replace;
+DROP TABLE vector_replace_vec;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/select_all.test b/storage/mroonga/mysql-test/mroonga/storage/t/select_all.test
new file mode 100644
index 00000000000..8de78ca99af
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/select_all.test
@@ -0,0 +1,100 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1(c1 int, c2 int, c3 int);
+insert into t1 values (1, 10, 100);
+insert into t1 values (2, 30, 500);
+insert into t1 values (5, 20, 200);
+insert into t1 values (3, 60, 300);
+insert into t1 values (4, 50, 600);
+insert into t1 values (6, 40, 400);
+
+select * from t1;
+select c1 from t1;
+select c2 from t1;
+select c3 from t1;
+
+select * from t1 where c1 <= 3;
+select * from t1 where c2 > 40;
+select * from t1 where c3 = 300;
+
+
+select * from t1 order by c1;
+select * from t1 order by c2 desc;
+select * from t1 order by c3, c1;
+
+drop table t1;
+
+
+create table t1 (c1 int, c2 varchar(100));
+insert into t1 values(1, "hoge");
+insert into t1 values(4, "hogefuga");
+insert into t1 values(2, "fuga");
+insert into t1 values(5, "moge");
+insert into t1 values(3, "mo");
+
+select * from t1;
+select * from t1 order by c1;
+select * from t1 order by c1 desc;
+select * from t1 order by c2;
+
+drop table t1;
+
+create table t1 (c1 int, c2 text);
+insert into t1 values(1, "hoge");
+insert into t1 values(4, "hogefuga");
+insert into t1 values(2, "fuga");
+insert into t1 values(5, "moge");
+insert into t1 values(3, "mo");
+
+select * from t1;
+
+drop table t1;
+
+# ORDER BY with position
+create table t1 (c1 int, c2 int, c3 text);
+insert into t1 values(1, 20, "hoge");
+insert into t1 values(4, 60, "hogefuga");
+insert into t1 values(2, 50, "fuga");
+insert into t1 values(5, 30, "moge");
+insert into t1 values(3, 40, "mo");
+select * from t1 order by c1 asc;
+select * from t1 order by c1 desc;
+select * from t1 order by c2 asc;
+select * from t1 order by c2 desc;
+select * from t1 order by c3 asc;
+select * from t1 order by c3 desc;
+drop table t1;
+
+# _id
+create table t1 (_id int, c1 int);
+insert into t1 values (null,100);
+insert into t1 values (null,100);
+insert into t1 values (null,100);
+insert into t1 values (null,100);
+insert into t1 values (null,100);
+select * from t1;
+select * from t1 where _id < 3;
+select * from t1 where _id >= 3;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/select_empty_key_where_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/select_empty_key_where_equal.test
new file mode 100644
index 00000000000..c4ce4c0193d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/select_empty_key_where_equal.test
@@ -0,0 +1,36 @@
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS tags;
+--enable_warnings
+
+CREATE TABLE tags (
+ name VARCHAR(16) NOT NULL,
+ KEY index_name (name)
+);
+
+INSERT INTO tags VALUES ('mroonga');
+INSERT INTO tags VALUES ('mysql');
+INSERT INTO tags VALUES ('');
+
+SELECT * FROM tags WHERE name = "";
+
+DROP TABLE tags;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/select_empty_key_where_not_equal.test b/storage/mroonga/mysql-test/mroonga/storage/t/select_empty_key_where_not_equal.test
new file mode 100644
index 00000000000..8537616e89e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/select_empty_key_where_not_equal.test
@@ -0,0 +1,36 @@
+# Copyright(C) 2014 Kenji Maruyama <mmmaru777@gmail.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS tags;
+--enable_warnings
+
+CREATE TABLE tags (
+ name VARCHAR(16) NOT NULL,
+ KEY index_name (name)
+);
+
+INSERT INTO tags VALUES ('mroonga');
+INSERT INTO tags VALUES ('mysql');
+INSERT INTO tags VALUES ('');
+
+SELECT * FROM tags WHERE name != "";
+
+DROP TABLE tags;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/select_group_by_with_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/select_group_by_with_index.test
new file mode 100644
index 00000000000..569fbcfd600
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/select_group_by_with_index.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS users;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE users (
+ name varchar(40),
+ age int,
+ KEY (age)
+);
+
+INSERT INTO users VALUES ("Alice", 20);
+INSERT INTO users VALUES ("Bob", 20);
+INSERT INTO users VALUES ("Charry", 29);
+
+SELECT *, COUNT(*) FROM users GROUP BY age;
+
+DROP TABLE users;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/select_group_by_without_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/select_group_by_without_index.test
new file mode 100644
index 00000000000..ae852bfcfed
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/select_group_by_without_index.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS users;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE users (
+ name varchar(40),
+ age int
+);
+
+INSERT INTO users VALUES ("Alice", 20);
+INSERT INTO users VALUES ("Bob", 20);
+INSERT INTO users VALUES ("Charry", 29);
+
+EXPLAIN SELECT *, COUNT(*) FROM users GROUP BY age;
+
+SELECT *, COUNT(*) FROM users GROUP BY age;
+
+DROP TABLE users;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/select_pkey.test b/storage/mroonga/mysql-test/mroonga/storage/t/select_pkey.test
new file mode 100644
index 00000000000..9ebd707392d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/select_pkey.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1(c1 int primary key, c2 int, c3 int);
+insert into t1 values (1, 10, 100);
+insert into t1 values (2, 30, 500);
+insert into t1 values (5, 20, 200);
+insert into t1 values (3, 60, 300);
+insert into t1 values (4, 50, 600);
+insert into t1 values (6, 40, 400);
+
+select * from t1 where c1=1;
+select * from t1 where c1=2;
+select * from t1 where c1=3;
+select * from t1 where c1=4;
+select * from t1 where c1=5;
+select * from t1 where c1=6;
+
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/select_secondary_key.test b/storage/mroonga/mysql-test/mroonga/storage/t/select_secondary_key.test
new file mode 100644
index 00000000000..a6159bea02b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/select_secondary_key.test
@@ -0,0 +1,51 @@
+# Copyright(C) 2010 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 int, c3 text, key idx1(c2), fulltext index ft(c3));
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"sa si su se so");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ee oo");
+
+select * from t1;
+select * from t1 force index(idx1) where c2 = 30;
+select * from t1 force index(idx1) where c2 = 20;
+
+insert into t1 values(6,30,"aa bb cc dd ee");
+select * from t1;
+select * from t1 force index(idx1) where c2 = 30;
+
+drop table t1;
+
+create table t1 (c1 varchar(5) primary key, c2 varchar(5), c3 text, key idx1(c2), fulltext index ft(c3))engine=mroonga;
+insert into t1 values('ab','ijk',"aa ii uu ee oo");
+insert into t1 values('bc','ghi',"ka ki ku ke ko");
+insert into t1 values('cd','efg',"sa si su se so");
+insert into t1 values('de','cde',"ta ti tu te to");
+insert into t1 values('ef','abc',"aa ii uu ee oo");
+select * from t1 force index(idx1) where c2 < 'e' order by c1 asc;
+select * from t1 force index(idx1) where c2 > 'e' order by c1 asc;
+select * from t1 force index(idx1) where c2 between 'c' and 'h' order by c1 asc;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/show_create_table_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/storage/t/show_create_table_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..240e8c11dcf
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/show_create_table_TODO_SPLIT_ME.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int);
+show create table t1;
+drop table t1;
+
+create table t1 (c1 int, c2 int);
+show create table t1;
+drop table t1;
+
+create table t1 (c1 int primary key, c2 varchar(100));
+show create table t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/sub_query_fulltext.test b/storage/mroonga/mysql-test/mroonga/storage/t/sub_query_fulltext.test
new file mode 100644
index 00000000000..34839669f25
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/sub_query_fulltext.test
@@ -0,0 +1,55 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries, users;
+--enable_warnings
+
+CREATE TABLE users (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ name TEXT
+) DEFAULT CHARSET UTF8;
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ user_id INT UNSIGNED NOT NULL,
+ title TEXT,
+ FULLTEXT INDEX (title)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO users (name) VALUES ("alice");
+INSERT INTO users (name) VALUES ("bob");
+INSERT INTO users (name) VALUES ("carlos");
+
+SELECT * FROM users;
+
+INSERT INTO diaries (user_id, title) VALUES (1, "Hello!");
+INSERT INTO diaries (user_id, title) VALUES (2, "my name is bob");
+INSERT INTO diaries (user_id, title) VALUES (3, "my name is carlos");
+
+SELECT * FROM diaries;
+
+SELECT * FROM users
+ WHERE id IN (SELECT user_id FROM diaries
+ WHERE MATCH(title) AGAINST("name"))
+ ORDER BY id DESC;
+
+DROP TABLE diaries, users;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/temporary_table.test b/storage/mroonga/mysql-test/mroonga/storage/t/temporary_table.test
new file mode 100644
index 00000000000..56ebb430afc
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/temporary_table.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 Toshihisa Tashiro
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/skip_osx.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TEMPORARY TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TEMPORARY TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title) VALUES ("clear day");
+INSERT INTO diaries (title) VALUES ("rainy day");
+INSERT INTO diaries (title) VALUES ("cloudy day");
+
+SELECT * FROM diaries;
+
+DROP TEMPORARY TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/truncate.test b/storage/mroonga/mysql-test/mroonga/storage/t/truncate.test
new file mode 100644
index 00000000000..5833f77722a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/truncate.test
@@ -0,0 +1,56 @@
+# Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(day)
+) DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+
+SELECT * FROM diaries;
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE);
+TRUNCATE TABLE diaries;
+SELECT * FROM diaries;
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE);
+
+INSERT INTO diaries VALUES(1, 2011, 11, 11, "帰り道", "つかれたー");
+INSERT INTO diaries VALUES(2, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(3, 2011, 12, 2, "初雪", "今年はじめての雪!");
+
+SELECT * FROM diaries;
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("悪い" IN BOOLEAN MODE);
+
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/update_fulltext.test b/storage/mroonga/mysql-test/mroonga/storage/t/update_fulltext.test
new file mode 100644
index 00000000000..a6cc1ba7fb2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/update_fulltext.test
@@ -0,0 +1,36 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 text, fulltext index (c2));
+insert into t1 values(10, "aa ii uu ee");
+insert into t1 values(20, "ka ki ku ke");
+insert into t1 values(30, "sa si su se");
+
+select * from t1;
+update t1 set c2="ta ti tu te" where c1=20;
+select * from t1;
+select * from t1 where match(c2) against("ti");
+select * from t1 where match(c2) against("ki");
+
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/update_id_hash_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/update_id_hash_index.test
new file mode 100644
index 00000000000..e362984f896
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/update_id_hash_index.test
@@ -0,0 +1,33 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (_id int, c1 int, key (_id) using hash);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+select * from t1;
+update t1 set c1 = 200 where _id = 2;
+select * from t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/update_id_unique_hash_index.test b/storage/mroonga/mysql-test/mroonga/storage/t/update_id_unique_hash_index.test
new file mode 100644
index 00000000000..d8257e41fb5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/update_id_unique_hash_index.test
@@ -0,0 +1,33 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (_id int, c1 int, unique key (_id) using hash);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+insert into t1 values(null, 100);
+select * from t1;
+update t1 set c1 = 200 where _id = 2;
+select * from t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/update_int.test b/storage/mroonga/mysql-test/mroonga/storage/t/update_int.test
new file mode 100644
index 00000000000..043ae25aee5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/update_int.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int, c2 int);
+show create table t1;
+insert into t1 values (1, 100);
+insert into t1 values (2, 101);
+insert into t1 values (3, 102);
+select * from t1;
+
+update t1 set c2=c2+100 where c1=1;
+select * from t1;
+update t1 set c2=c2+100 where c1=2;
+select * from t1;
+update t1 set c2=c2+100 where c1=3;
+select * from t1;
+
+flush tables;
+
+update t1 set c1=5, c2=50;
+select * from t1;
+
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/update_last_insert_grn_id.test b/storage/mroonga/mysql-test/mroonga/storage/t/update_last_insert_grn_id.test
new file mode 100644
index 00000000000..3ad713cbbde
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/update_last_insert_grn_id.test
@@ -0,0 +1,48 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+drop table if exists memos;
+--enable_warnings
+
+create table memos (
+ _id int,
+ content varchar(255),
+ unique key (_id) using hash
+);
+
+insert into memos values (null, "今夜はさんま。");
+insert into memos values (null, "明日はgroongaをアップデート。");
+insert into memos values (null, "帰りにおだんご。");
+insert into memos values (null, "金曜日は肉の日。");
+
+select * from memos;
+
+insert into memos values (null, "冷蔵庫に牛乳が残り1本。");
+select last_insert_grn_id();
+update memos set content = "冷蔵庫に牛乳はまだたくさんある。" where _id = last_insert_grn_id();
+
+select * from memos;
+
+drop table memos;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/update_virtual_column.test b/storage/mroonga/mysql-test/mroonga/storage/t/update_virtual_column.test
new file mode 100644
index 00000000000..f5d85de4043
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/update_virtual_column.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+# for virtual columns
+create table t1 (c1 int, _id int);
+insert into t1 values(1,null);
+insert into t1 values(2,null);
+insert into t1 values(3,null);
+select * from t1;
+set sql_mode="";
+# warning WARN_DATA_TRUNCATED
+update t1 set _id = 10 where c1 = 1;
+select * from t1;
+set sql_mode="strict_all_tables";
+# We can't use WARN_DATA_TRUNCATED here because "WXXX" isn't supported
+# MySQL 5.5, 5.6 and MariaDB 5.6. MariaDB 10.0 only supports it.
+# We share this test with all MySQL servers. So we use number here.
+--error 1265
+update t1 set _id = 11 where c1 = 1;
+select * from t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_database_path_prefix.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_database_path_prefix.test
new file mode 100644
index 00000000000..c7b2094906b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_database_path_prefix.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/have_mroonga_helper.inc
+
+SET GLOBAL mroonga_database_path_prefix = "test/mroonga.data/";
+SHOW GLOBAL VARIABLES LIKE 'mroonga_database_path_prefix';
+CREATE DATABASE clean_test;
+USE clean_test;
+
+CREATE TABLE counts (
+ id INT PRIMARY KEY AUTO_INCREMENT
+);
+
+--file_exists $MYSQLD_DATADIR/test/mroonga.data/clean_test.mrn
+
+INSERT INTO counts VALUES (NULL);
+
+SELECT * FROM counts;
+
+DROP TABLE counts;
+DROP DATABASE clean_test;
+USE test;
+
+SET GLOBAL mroonga_database_path_prefix = NULL;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_default_parser_new_value.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_default_parser_new_value.test
new file mode 100644
index 00000000000..a61af87fb2b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_default_parser_new_value.test
@@ -0,0 +1,25 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+SET @mroonga_default_parser_backup = @@mroonga_default_parser;
+SET GLOBAL mroonga_default_parser = "TokenBigramSplitAlpha";
+SHOW GLOBAL VARIABLES LIKE 'mroonga_default_parser';
+SET GLOBAL mroonga_default_parser = @mroonga_default_parser_backup;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_default_parser_same_value.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_default_parser_same_value.test
new file mode 100644
index 00000000000..0e8562de92c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_default_parser_same_value.test
@@ -0,0 +1,22 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+SET GLOBAL mroonga_default_parser = "TokenBigram";
+SHOW GLOBAL VARIABLES LIKE 'mroonga_default_parser';
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_dry_write_delete.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_dry_write_delete.test
new file mode 100644
index 00000000000..b80ac2e8400
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_dry_write_delete.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+create table diaries (
+ id int primary key auto_increment,
+ body text,
+ fulltext index body_index (body)
+) default charset utf8;
+show create table diaries;
+
+insert into diaries (body) values ("will start groonga!");
+select * from diaries;
+
+set mroonga_dry_write=true;
+delete from diaries where id = 1;
+select * from diaries;
+
+set mroonga_dry_write=false;
+delete from diaries where id = 1;
+select * from diaries;
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_dry_write_insert.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_dry_write_insert.test
new file mode 100644
index 00000000000..72346c80a6d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_dry_write_insert.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+create table diaries (
+ id int primary key auto_increment,
+ body text,
+ fulltext index body_index (body)
+) default charset utf8;
+show create table diaries;
+
+insert into diaries (body) values ("will start groonga!");
+select * from diaries;
+
+set mroonga_dry_write=true;
+insert into diaries (body) values ("starting groonga...");
+select * from diaries;
+
+set mroonga_dry_write=false;
+insert into diaries (body) values ("started groonga.");
+select * from diaries;
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_dry_write_update.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_dry_write_update.test
new file mode 100644
index 00000000000..2100f61700c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_dry_write_update.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+create table diaries (
+ id int primary key auto_increment,
+ body text,
+ fulltext index body_index (body)
+) default charset utf8;
+show create table diaries;
+
+insert into diaries (body) values ("will start groonga!");
+
+set mroonga_dry_write=true;
+update diaries set body = "starting groonga..." where id = 1;
+select * from diaries;
+
+set mroonga_dry_write=false;
+update diaries set body = "starting groonga..." where id = 1;
+select * from diaries;
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_disable.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_disable.test
new file mode 100644
index 00000000000..2c2776e5fd8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_disable.test
@@ -0,0 +1,28 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_version_100_or_later.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+SET GLOBAL mroonga_lock_timeout = -1;
+SHOW GLOBAL VARIABLES LIKE "mroonga_lock_timeout";
+
+
+disable_query_log;
+SET GLOBAL mroonga_lock_timeout = 10000000;
+enable_query_log;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_invalid.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_invalid.test
new file mode 100644
index 00000000000..e2090cf9659
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_invalid.test
@@ -0,0 +1,28 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_version_100_or_later.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+SET GLOBAL mroonga_lock_timeout = -2;
+SHOW GLOBAL VARIABLES LIKE "mroonga_lock_timeout";
+
+
+disable_query_log;
+SET GLOBAL mroonga_lock_timeout = 10000000;
+enable_query_log;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_no_retry.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_no_retry.test
new file mode 100644
index 00000000000..9df4970cacd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_no_retry.test
@@ -0,0 +1,27 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+SET GLOBAL mroonga_lock_timeout = 0;
+SHOW GLOBAL VARIABLES LIKE "mroonga_lock_timeout";
+
+
+disable_query_log;
+SET GLOBAL mroonga_lock_timeout = 10000000;
+enable_query_log;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_valid.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_valid.test
new file mode 100644
index 00000000000..49394d0a40a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_lock_timeout_valid.test
@@ -0,0 +1,27 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+SET GLOBAL mroonga_lock_timeout = 1000;
+SHOW GLOBAL VARIABLES LIKE "mroonga_lock_timeout";
+
+
+disable_query_log;
+SET GLOBAL mroonga_lock_timeout = 10000000;
+enable_query_log;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_log_file_new_value.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_log_file_new_value.test
new file mode 100644
index 00000000000..77f6adb1713
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_log_file_new_value.test
@@ -0,0 +1,25 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+SET @mroonga_log_file_backup = @@mroonga_log_file;
+SET GLOBAL mroonga_log_file = "new-mroonga.log";
+SHOW GLOBAL VARIABLES LIKE 'mroonga_log_file';
+SET GLOBAL mroonga_log_file = @mroonga_log_file_backup;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_log_file_nonexistent_path.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_log_file_nonexistent_path.test
new file mode 100644
index 00000000000..254beae17a0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_log_file_nonexistent_path.test
@@ -0,0 +1,22 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+SET GLOBAL mroonga_log_file = "nonexistent/mroonga.log";
+SHOW GLOBAL VARIABLES LIKE 'mroonga_log_file';
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_log_file_same_value.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_log_file_same_value.test
new file mode 100644
index 00000000000..54116467502
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_log_file_same_value.test
@@ -0,0 +1,22 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+SET GLOBAL mroonga_log_file = "groonga.log";
+SHOW GLOBAL VARIABLES LIKE 'mroonga_log_file';
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_log_level_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_log_level_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..f736be43e6b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_log_level_TODO_SPLIT_ME.test
@@ -0,0 +1,61 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2014 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+set @mroonga_log_level_backup=@@mroonga_log_level;
+show global variables like 'mroonga_log_level';
+
+set global mroonga_log_level=NONE;
+show global variables like 'mroonga_log_level';
+
+set global mroonga_log_level=EMERG;
+show global variables like 'mroonga_log_level';
+
+set global mroonga_log_level=ALERT;
+show global variables like 'mroonga_log_level';
+
+set global mroonga_log_level=CRIT;
+show global variables like 'mroonga_log_level';
+
+set global mroonga_log_level=ERROR;
+show global variables like 'mroonga_log_level';
+
+set global mroonga_log_level=WARNING;
+show global variables like 'mroonga_log_level';
+
+set global mroonga_log_level=NOTICE;
+show global variables like 'mroonga_log_level';
+
+set global mroonga_log_level=INFO;
+show global variables like 'mroonga_log_level';
+
+set global mroonga_log_level=DEBUG;
+show global variables like 'mroonga_log_level';
+
+set global mroonga_log_level=DUMP;
+show global variables like 'mroonga_log_level';
+
+--error ER_WRONG_VALUE_FOR_VAR
+set global mroonga_log_level=dummy;
+
+--error ER_GLOBAL_VARIABLE
+set session mroonga_log_level=NOTICE;
+
+set global mroonga_log_level=@mroonga_log_level_backup;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_match_escalation_threshold_global.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_match_escalation_threshold_global.test
new file mode 100644
index 00000000000..55a318b786a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_match_escalation_threshold_global.test
@@ -0,0 +1,55 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_fulltext_index_comment.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+# MySQL <= 5.5 reports wrong a warning. :<
+# It has been fixed in MySQL >= 5.6 and MariaDB >= 5.3.
+--disable_warnings
+SET GLOBAL mroonga_match_escalation_threshold = -1;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ tags TEXT,
+ FULLTEXT INDEX tags_index (tags) COMMENT 'parser "TokenDelimit"'
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, tags) VALUES ("Hello groonga!", "groonga install");
+INSERT INTO diaries (title, tags) VALUES ("Hello mroonga!", "mroonga install");
+
+
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("install" IN BOOLEAN MODE);
+
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+
+SET GLOBAL mroonga_match_escalation_threshold = 0;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+
+SET mroonga_match_escalation_threshold = 0;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_match_escalation_threshold_session.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_match_escalation_threshold_session.test
new file mode 100644
index 00000000000..c459e50c0d7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_match_escalation_threshold_session.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_fulltext_index_comment.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ tags TEXT,
+ FULLTEXT INDEX tags_index (tags) COMMENT 'parser "TokenDelimit"'
+) DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, tags) VALUES ("Hello groonga!", "groonga install");
+INSERT INTO diaries (title, tags) VALUES ("Hello mroonga!", "mroonga install");
+
+
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("install" IN BOOLEAN MODE);
+
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+
+# MySQL <= 5.5 reports wrong a warning. :<
+# It has been fixed in MySQL >= 5.6 and MariaDB >= 5.3.
+--disable_warnings
+SET mroonga_match_escalation_threshold = -1;
+--enable_warnings
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+
+SET mroonga_match_escalation_threshold = 0;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_vector_column_delimiter.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_vector_column_delimiter.test
new file mode 100644
index 00000000000..8412e6a65fa
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_vector_column_delimiter.test
@@ -0,0 +1,54 @@
+# Copyright(C) 2014 Naoya Murakami <naoya@createfield.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/have_mroonga_helper.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS document;
+DROP TABLE IF EXISTS category;
+--enable_warnings
+
+CREATE TABLE category (
+ category CHAR(10) PRIMARY KEY
+) DEFAULT CHARSET=utf8
+ COMMENT='default_tokenizer "TokenDelimit"';
+
+CREATE TABLE document (
+ id INT NOT NULL,
+ title TEXT,
+ categories TEXT COMMENT 'flags "COLUMN_VECTOR", type "category"',
+ PRIMARY KEY(id)
+) DEFAULT CHARSET=utf8;
+
+SHOW GLOBAL VARIABLES LIKE 'mroonga_vector_column_delimiter';
+
+INSERT INTO document VALUES(1, "Mroonga is the fastest search engine", "it database fulltext");
+SELECT id, title, categories FROM document;
+
+SET GLOBAL mroonga_vector_column_delimiter = ';';
+
+SHOW GLOBAL VARIABLES LIKE 'mroonga_vector_column_delimiter';
+
+SELECT id, title, categories FROM document;
+
+DROP TABLE document;
+DROP TABLE category;
+
+SET GLOBAL mroonga_vector_column_delimiter = ' ';
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/storage/t/variable_version.test b/storage/mroonga/mysql-test/mroonga/storage/t/variable_version.test
new file mode 100644
index 00000000000..ff47f2c921d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/storage/t/variable_version.test
@@ -0,0 +1,22 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+# show variables like 'groonga%';
+show variables like 'mroonga_version';
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_add_column.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_add_column.result
new file mode 100644
index 00000000000..22dbbff96ac
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_add_column.result
@@ -0,0 +1,37 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT
+) DEFAULT CHARSET UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries (title) VALUES ("survey");
+SELECT * FROM diaries;
+id title
+1 survey
+ALTER TABLE diaries ADD COLUMN body TEXT;
+UPDATE diaries SET body = "will start groonga!";
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) values ("groonga (2)", "started groonga.");
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_change_column_comment.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_change_column_comment.result
new file mode 100644
index 00000000000..af302b5a2a5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_change_column_comment.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS bugs;
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tag VARCHAR(64)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnoDB"';
+ALTER TABLE bugs
+CHANGE COLUMN
+tag
+tag VARCHAR(64) COMMENT 'It must consist of only alphabet and number.';
+SHOW CREATE TABLE bugs;
+Table Create Table
+bugs CREATE TABLE `bugs` (
+ `id` int(10) unsigned NOT NULL,
+ `tag` varchar(64) DEFAULT NULL COMMENT 'It must consist of only alphabet and number.',
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "InnoDB"'
+DROP TABLE bugs;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_change_engine.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_change_engine.result
new file mode 100644
index 00000000000..e5ce4692aa3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_change_engine.result
@@ -0,0 +1,49 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT,
+FULLTEXT INDEX title_index (title),
+FULLTEXT INDEX body_index (body)
+) ENGINE MyISAM DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE) AND
+MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+id title body
+1 survey will start groonga!
+ALTER TABLE diaries ENGINE = mroonga COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE) AND
+MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+id title body
+1 survey will start groonga!
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("groonga" IN BOOLEAN MODE) AND
+MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+id title body
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_comment_change_engine.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_comment_change_engine.result
new file mode 100644
index 00000000000..1106fcffca2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_comment_change_engine.result
@@ -0,0 +1,40 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+id INT AUTO_INCREMENT PRIMARY KEY,
+title VARCHAR(64),
+content TEXT,
+FULLTEXT INDEX(content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnoDB"';
+SHOW CREATE TABLE memos;
+Table Create Table
+memos CREATE TABLE `memos` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` varchar(64) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "InnoDB"'
+INSERT INTO memos (title, content) VALUES ("Hello", "I start to write memos!");
+INSERT INTO memos (title, content) VALUES ("groonga", "I start to use groonga!");
+INSERT INTO memos (title, content) VALUES ("mroonga", "I use mroonga too!");
+ALTER TABLE memos COMMENT='engine "MyISAM"';
+SHOW CREATE TABLE memos;
+Table Create Table
+memos CREATE TABLE `memos` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` varchar(64) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='engine "MyISAM"'
+SELECT * FROM memos;
+id title content
+1 Hello I start to write memos!
+2 groonga I start to use groonga!
+3 mroonga I use mroonga too!
+SELECT * FROM memos WHERE MATCH(content) AGAINST("start" IN BOOLEAN MODE);
+id title content
+1 Hello I start to write memos!
+2 groonga I start to use groonga!
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_create_fulltext.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_create_fulltext.result
new file mode 100644
index 00000000000..b185bf269f7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_create_fulltext.result
@@ -0,0 +1,27 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+SELECT *
+FROM diaries
+WHERE MATCH (title) AGAINST ("富士山");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+CREATE FULLTEXT INDEX title_index on diaries (title);
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+id title
+3 富士山
+ALTER TABLE diaries DISABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_fulltext.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_fulltext.result
new file mode 100644
index 00000000000..e71132be139
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_fulltext.result
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+FULLTEXT KEY title_index (title)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+id title
+3 富士山
+ALTER TABLE diaries DISABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_multiple_column.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_multiple_column.result
new file mode 100644
index 00000000000..1fefc1d98e6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_multiple_column.result
@@ -0,0 +1,27 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+created_at datetime,
+KEY title_and_created_at_index (title, created_at)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+SELECT *
+FROM diaries
+FORCE INDEX (title_and_created_at_index)
+WHERE title = "天気" AND
+created_at = "2012-04-30 23:00:00";
+id title created_at
+2 天気 2012-04-30 23:00:00
+ALTER TABLE diaries DISABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (title_and_created_at_index)
+WHERE title = "天気" AND
+created_at = "2012-04-30 23:00:00";
+id title created_at
+2 天気 2012-04-30 23:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_normal.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_normal.result
new file mode 100644
index 00000000000..d115cefb243
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_normal.result
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+created_at datetime,
+KEY created_at_index (created_at)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+SELECT *
+FROM diaries
+FORCE INDEX (created_at_index)
+WHERE created_at = "2012-04-30 20:00:00";
+id title created_at
+1 Hello 2012-04-30 20:00:00
+ALTER TABLE diaries DISABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (created_at_index)
+WHERE created_at = "2012-04-30 20:00:00";
+id title created_at
+1 Hello 2012-04-30 20:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_primary.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_primary.result
new file mode 100644
index 00000000000..47649911bb1
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_primary.result
@@ -0,0 +1,23 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+SELECT *
+FROM diaries
+FORCE INDEX (PRIMARY)
+WHERE id = 2;
+id title
+2 天気
+ALTER TABLE diaries DISABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (PRIMARY)
+WHERE id = 2;
+id title
+2 天気
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_updating.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_updating.result
new file mode 100644
index 00000000000..06e1a12db55
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_disable_keys_updating.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS t1;
+CREATE TABLE t1 (
+c1 int NOT NULL,
+c2 text NOT NULL,
+c3 int NOT NULL,
+c4 int NOT NULL,
+PRIMARY KEY(c1),
+KEY idx1(c3,c4),
+FULLTEXT KEY ft1(c2)
+) COMMENT='ENGINE "MyISAM"' DEFAULT CHARSET=utf8;
+INSERT INTO t1 VALUES(1, 'test1', 1, 1);
+INSERT INTO t1 VALUES(2, 'test2', 2, 2);
+INSERT INTO t1 VALUES(3, 'test3', 1, 3);
+ALTER TABLE t1 DISABLE KEYS;
+DELETE FROM t1 WHERE c1 = 2;
+UPDATE t1 SET c4 = 4 WHERE c1 = 1;
+INSERT INTO t1 VALUES(4, 'test4', 2, 4);
+TRUNCATE t1;
+DROP TABLE t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_drop_column.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_drop_column.result
new file mode 100644
index 00000000000..07c149a8fa8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_drop_column.result
@@ -0,0 +1,37 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT
+) DEFAULT CHARSET UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+ALTER TABLE diaries DROP COLUMN body;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+SELECT * FROM diaries;
+id title
+1 survey
+INSERT INTO diaries (title) values ("groonga (1)");
+INSERT INTO diaries (title) values ("groonga (2)");
+SELECT * FROM diaries;
+id title
+1 survey
+2 groonga (1)
+3 groonga (2)
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_fulltext.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_fulltext.result
new file mode 100644
index 00000000000..b9a0f545965
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_fulltext.result
@@ -0,0 +1,24 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+FULLTEXT KEY title_index (title)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+ALTER TABLE diaries DISABLE KEYS;
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+ALTER TABLE diaries ENABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (title_index)
+WHERE MATCH (title) AGAINST ("富士山");
+id title
+3 富士山
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_lock_tables.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_lock_tables.result
new file mode 100644
index 00000000000..341cc4f7de7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_lock_tables.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS memos;
+CREATE TABLE IF NOT EXISTS memos (
+id VARCHAR(45) NOT NULL PRIMARY KEY,
+text TEXT NOT NULL,
+FULLTEXT KEY (text)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnoDB"';
+LOCK TABLES memos WRITE;
+ALTER TABLE memos DISABLE KEYS;
+INSERT INTO memos
+VALUES (00000, 'text0'),
+(00001, 'text1'),
+(00002, 'text2');
+ALTER TABLE memos ENABLE KEYS;
+UNLOCK TABLES;
+SELECT * FROM memos;
+id text
+0 text0
+1 text1
+2 text2
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_multiple_column.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_multiple_column.result
new file mode 100644
index 00000000000..21f6b9089bd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_multiple_column.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+created_at datetime,
+KEY title_and_created_at_index (title, created_at)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+ALTER TABLE diaries DISABLE KEYS;
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+SELECT *
+FROM diaries
+FORCE INDEX (title_and_created_at_index)
+WHERE title = "天気" AND
+created_at = "2012-04-30 23:00:00";
+id title created_at
+2 天気 2012-04-30 23:00:00
+ALTER TABLE diaries ENABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (title_and_created_at_index)
+WHERE title = "天気" AND
+created_at = "2012-04-30 23:00:00";
+id title created_at
+2 天気 2012-04-30 23:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_normal.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_normal.result
new file mode 100644
index 00000000000..2ba4f1b6386
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_normal.result
@@ -0,0 +1,26 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+created_at datetime,
+KEY created_at_index (created_at)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+ALTER TABLE diaries DISABLE KEYS;
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+SELECT *
+FROM diaries
+FORCE INDEX (created_at_index)
+WHERE created_at = "2012-04-30 20:00:00";
+id title created_at
+1 Hello 2012-04-30 20:00:00
+ALTER TABLE diaries ENABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (created_at_index)
+WHERE created_at = "2012-04-30 20:00:00";
+id title created_at
+1 Hello 2012-04-30 20:00:00
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_primary.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_primary.result
new file mode 100644
index 00000000000..a1ee9013b6b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_enable_keys_primary.result
@@ -0,0 +1,24 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+ALTER TABLE diaries DISABLE KEYS;
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+SELECT *
+FROM diaries
+FORCE INDEX (PRIMARY)
+WHERE id = 2;
+id title
+2 天気
+ALTER TABLE diaries ENABLE KEYS;
+SELECT *
+FROM diaries
+FORCE INDEX (PRIMARY)
+WHERE id = 2;
+id title
+2 天気
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_fulltext.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_fulltext.result
new file mode 100644
index 00000000000..cc64daac30a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_fulltext.result
@@ -0,0 +1,52 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+FULLTEXT INDEX title_index (title)
+) DEFAULT CHARSET UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries (title) VALUES ("survey");
+SELECT * FROM diaries;
+id title
+1 survey
+ALTER TABLE diaries ADD COLUMN body TEXT;
+UPDATE diaries SET body = "will start groonga!";
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) values ("groonga (2)", "started groonga.");
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+ALTER TABLE diaries ADD FULLTEXT INDEX body_index (body);
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("survey") AND
+MATCH(body) AGAINST("groonga");
+id title body
+1 survey will start groonga!
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("groonga") AND
+MATCH(body) AGAINST("starting");
+id title body
+2 groonga (1) starting groonga...
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_rename_table.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_rename_table.result
new file mode 100644
index 00000000000..08e526baa50
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_rename_table.result
@@ -0,0 +1,45 @@
+DROP TABLE IF EXISTS diaries, memos;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT,
+FULLTEXT INDEX title_index (title),
+FULLTEXT INDEX body_index (body)
+) DEFAULT CHARSET UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("groonga") AND
+MATCH(body) AGAINST("starting");
+id title body
+ALTER TABLE diaries RENAME memos;
+SELECT * FROM memos;
+id title body
+1 survey will start groonga!
+SELECT * FROM memos
+WHERE MATCH(title) AGAINST("groonga") AND
+MATCH(body) AGAINST("starting");
+id title body
+SHOW CREATE TABLE memos;
+Table Create Table
+memos CREATE TABLE `memos` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_spatial.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_spatial.result
new file mode 100644
index 00000000000..fc38afc72d8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/alter_table_spatial.result
@@ -0,0 +1,131 @@
+DROP TABLE IF EXISTS shops;
+CREATE TABLE shops (
+id INT PRIMARY KEY AUTO_INCREMENT,
+name TEXT,
+location GEOMETRY NOT NULL
+) COMMENT = 'ENGINE "InnoDB"';
+INSERT INTO shops (name, location)
+VALUES ('nezu-no-taiyaki',
+GeomFromText('POINT(139.762573 35.720253)'));
+INSERT INTO shops (name, location)
+VALUES ('taiyaki-kataoka',
+GeomFromText('POINT(139.715591 35.712521)'));
+INSERT INTO shops (name, location)
+VALUES ('soba-taiyaki-ku',
+GeomFromText('POINT(139.659088 35.683712)'));
+INSERT INTO shops (name, location)
+VALUES ('kuruma',
+GeomFromText('POINT(139.706207 35.721516)'));
+INSERT INTO shops (name, location)
+VALUES ('hirose-ya',
+GeomFromText('POINT(139.685608 35.714844)'));
+INSERT INTO shops (name, location)
+VALUES ('sazare',
+GeomFromText('POINT(139.685043 35.714653)'));
+INSERT INTO shops (name, location)
+VALUES ('omede-taiyaki',
+GeomFromText('POINT(139.817154 35.700516)'));
+INSERT INTO shops (name, location)
+VALUES ('onaga-ya',
+GeomFromText('POINT(139.81105 35.698254)'));
+INSERT INTO shops (name, location)
+VALUES ('shiro-ya',
+GeomFromText('POINT(139.638611 35.705517)'));
+INSERT INTO shops (name, location)
+VALUES ('fuji-ya',
+GeomFromText('POINT(139.637115 35.703938)'));
+INSERT INTO shops (name, location)
+VALUES ('miyoshi',
+GeomFromText('POINT(139.537323 35.644539)'));
+INSERT INTO shops (name, location)
+VALUES ('juju-ya',
+GeomFromText('POINT(139.695755 35.628922)'));
+INSERT INTO shops (name, location)
+VALUES ('tatsumi-ya',
+GeomFromText('POINT(139.638657 35.665501)'));
+INSERT INTO shops (name, location)
+VALUES ('tetsuji',
+GeomFromText('POINT(139.76857 35.680912)'));
+INSERT INTO shops (name, location)
+VALUES ('gazuma-ya',
+GeomFromText('POINT(139.647598 35.700817)'));
+INSERT INTO shops (name, location)
+VALUES ('honma-mon',
+GeomFromText('POINT(139.652573 35.722736)'));
+INSERT INTO shops (name, location)
+VALUES ('naniwa-ya',
+GeomFromText('POINT(139.796234 35.730061)'));
+INSERT INTO shops (name, location)
+VALUES ('kuro-dai',
+GeomFromText('POINT(139.704834 35.650345)'));
+INSERT INTO shops (name, location)
+VALUES ('daruma',
+GeomFromText('POINT(139.770599 35.681461)'));
+INSERT INTO shops (name, location)
+VALUES ('yanagi-ya',
+GeomFromText('POINT(139.783981 35.685341)'));
+INSERT INTO shops (name, location)
+VALUES ('sharaku',
+GeomFromText('POINT(139.794846 35.716969)'));
+INSERT INTO shops (name, location)
+VALUES ('takane',
+GeomFromText('POINT(139.560913 35.698601)'));
+INSERT INTO shops (name, location)
+VALUES ('chiyoda',
+GeomFromText('POINT(139.652817 35.642601)'));
+INSERT INTO shops (name, location)
+VALUES ('da-ka-po',
+GeomFromText('POINT(139.727356 35.627346)'));
+INSERT INTO shops (name, location)
+VALUES ('matsushima-ya',
+GeomFromText('POINT(139.737381 35.640556)'));
+INSERT INTO shops (name, location)
+VALUES ('kazuya',
+GeomFromText('POINT(139.760895 35.673508)'));
+INSERT INTO shops (name, location)
+VALUES ('furuya-kogane-an',
+GeomFromText('POINT(139.676071 35.680603)'));
+INSERT INTO shops (name, location)
+VALUES ('hachi-no-ie',
+GeomFromText('POINT(139.668106 35.608021)'));
+INSERT INTO shops (name, location)
+VALUES ('azuki-chan',
+GeomFromText('POINT(139.673203 35.64151)'));
+INSERT INTO shops (name, location)
+VALUES ('kuriko-an',
+GeomFromText('POINT(139.796829 35.712013)'));
+INSERT INTO shops (name, location)
+VALUES ('yume-no-aru-machi-no-taiyaki-ya-san',
+GeomFromText('POINT(139.712524 35.616199)'));
+INSERT INTO shops (name, location)
+VALUES ('naze-ya',
+GeomFromText('POINT(139.665833 35.609039)'));
+INSERT INTO shops (name, location)
+VALUES ('sanoki-ya',
+GeomFromText('POINT(139.770721 35.66592)'));
+INSERT INTO shops (name, location)
+VALUES ('shigeta',
+GeomFromText('POINT(139.780273 35.672626)'));
+INSERT INTO shops (name, location)
+VALUES ('nishimi-ya',
+GeomFromText('POINT(139.774628 35.671825)'));
+INSERT INTO shops (name, location)
+VALUES ('hiiragi',
+GeomFromText('POINT(139.711517 35.647701)'));
+ALTER TABLE shops ADD SPATIAL KEY location_index (location);
+SELECT id, name, AsText(location) AS location_text FROM shops
+WHERE MBRContains(GeomFromText('LineString(139.7727 35.6684, 139.7038 35.7121)'), location);
+id name location_text
+14 tetsuji POINT(139.76857 35.680912)
+19 daruma POINT(139.770599 35.681461)
+26 kazuya POINT(139.760895 35.673508)
+SHOW CREATE TABLE shops;
+Table Create Table
+shops CREATE TABLE `shops` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` text,
+ `location` geometry NOT NULL,
+ PRIMARY KEY (`id`),
+ SPATIAL KEY `location_index` (`location`)
+) ENGINE=Mroonga AUTO_INCREMENT=37 DEFAULT CHARSET=latin1 COMMENT='ENGINE "InnoDB"'
+DROP TABLE shops;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/auto_increment_text.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/auto_increment_text.result
new file mode 100644
index 00000000000..9d45d2fe63d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/auto_increment_text.result
@@ -0,0 +1,15 @@
+drop table if exists diaries;
+create table diaries (
+id int primary key auto_increment,
+body text
+) comment = 'engine "innodb"';
+insert into diaries (body) values ("started groonga (long text)");
+select * from diaries;
+id body
+1 started groonga (long text)
+insert into diaries (body) values ("sleeping... (short text)");
+select * from diaries;
+id body
+1 started groonga (long text)
+2 sleeping... (short text)
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/binlog_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/binlog_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..f0792094a34
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/binlog_TODO_SPLIT_ME.result
@@ -0,0 +1,34 @@
+drop table if exists t1;
+show variables like 'log_bin';
+Variable_name Value
+log_bin ON
+set binlog_format="STATEMENT";
+create table t1 (c1 int primary key, c2 int) engine = mroonga COMMENT = 'engine "innodb"';
+insert into t1 values(1,100);
+insert into t1 values(2,100);
+commit;
+select * from t1;
+c1 c2
+1 100
+2 100
+drop table t1;
+set binlog_format="ROW";
+create table t1 (c1 int primary key, c2 int) engine = mroonga COMMENT = 'engine "innodb"';
+insert into t1 values(1,100);
+insert into t1 values(2,100);
+commit;
+select * from t1;
+c1 c2
+1 100
+2 100
+drop table t1;
+set binlog_format="MIXED";
+create table t1 (c1 int primary key, c2 int) engine = mroonga COMMENT = 'engine "innodb"';
+insert into t1 values(1,100);
+insert into t1 values(2,100);
+commit;
+select * from t1;
+c1 c2
+1 100
+2 100
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/column_comment_index_not_for_mroonga.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/column_comment_index_not_for_mroonga.result
new file mode 100644
index 00000000000..b1a7c8efb89
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/column_comment_index_not_for_mroonga.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS bugs;
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tag VARCHAR(64),
+INDEX (tag) COMMENT 'Tag search is required.'
+) DEFAULT CHARSET=utf8
+COMMENT='engine "InnoDB"';
+SHOW CREATE TABLE bugs;
+Table Create Table
+bugs CREATE TABLE `bugs` (
+ `id` int(10) unsigned NOT NULL,
+ `tag` varchar(64) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `tag` (`tag`) COMMENT 'Tag search is required.'
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "InnoDB"'
+DROP TABLE bugs;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/column_normal_comment.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/column_normal_comment.result
new file mode 100644
index 00000000000..2679079664e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/column_normal_comment.result
@@ -0,0 +1,13 @@
+DROP TABLE IF EXISTS bugs;
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY,
+tag VARCHAR(64) COMMENT 'It must consist of only alphabet and number.'
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnoDB"';
+SHOW CREATE TABLE bugs;
+Table Create Table
+bugs CREATE TABLE `bugs` (
+ `id` int(10) unsigned NOT NULL,
+ `tag` varchar(64) DEFAULT NULL COMMENT 'It must consist of only alphabet and number.',
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "InnoDB"'
+DROP TABLE bugs;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/count_star_with_index.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/count_star_with_index.result
new file mode 100644
index 00000000000..6b29be5edb7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/count_star_with_index.result
@@ -0,0 +1,28 @@
+DROP TABLE IF EXISTS diaries_innodb;
+DROP TABLE IF EXISTS diaries_mroonga;
+CREATE TABLE diaries_innodb (
+id INT PRIMARY KEY AUTO_INCREMENT,
+body TEXT,
+flag TINYINT(2),
+INDEX (flag)
+) ENGINE = InnoDB DEFAULT CHARSET UTF8;
+CREATE TABLE diaries_mroonga (
+id INT PRIMARY KEY AUTO_INCREMENT,
+body TEXT,
+flag TINYINT(2),
+INDEX (flag)
+) COMMENT = 'ENGINE "InnoDB"' DEFAULT CHARSET UTF8;
+INSERT INTO diaries_innodb (body) VALUES ("will start groonga!");
+INSERT INTO diaries_innodb (body) VALUES ("starting groonga...");
+INSERT INTO diaries_innodb (body) VALUES ("started groonga.");
+INSERT INTO diaries_mroonga (body) VALUES ("will start groonga!");
+INSERT INTO diaries_mroonga (body) VALUES ("starting groonga...");
+INSERT INTO diaries_mroonga (body) VALUES ("started groonga.");
+EXPLAIN SELECT COUNT(*) FROM diaries_innodb;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE diaries_innodb index NULL flag 2 NULL 3 Using index
+EXPLAIN SELECT COUNT(*) FROM diaries_mroonga;
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE diaries_mroonga index NULL flag 2 NULL 3 Using index
+DROP TABLE diaries_innodb;
+DROP TABLE diaries_mroonga;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/create_table_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/create_table_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..b3814331038
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/create_table_TODO_SPLIT_ME.result
@@ -0,0 +1,109 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key) COMMENT = 'engine "innodb"';
+create table t2 (c1 int primary key) COMMENT = 'engine "innodb"';
+create table t3 (c1 int primary key) COMMENT = 'engine "innodb"';
+drop table t1,t2,t3;
+create table t1 (c1 int primary key, c2 int, c3 int) COMMENT = 'engine "innodb"';
+drop table t1;
+create table t1 (c1 bit primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 bit(1) NO PRI NULL
+drop table t1;
+create table t1 (c1 tinyint primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 tinyint(4) NO PRI NULL
+drop table t1;
+create table t1 (c1 smallint primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 smallint(6) NO PRI NULL
+drop table t1;
+create table t1 (c1 mediumint primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 mediumint(9) NO PRI NULL
+drop table t1;
+create table t1 (c1 int primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 int(11) NO PRI NULL
+drop table t1;
+create table t1 (c1 bigint primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 bigint(20) NO PRI NULL
+drop table t1;
+create table t1 (c1 double primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 double NO PRI NULL
+drop table t1;
+create table t1 (c1 float primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 float NO PRI NULL
+drop table t1;
+create table t1 (c1 decimal primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 decimal(10,0) NO PRI NULL
+drop table t1;
+create table t1 (c1 date primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 date NO PRI NULL
+drop table t1;
+create table t1 (c1 time primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 time NO PRI NULL
+drop table t1;
+create table t1 (c1 timestamp primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 timestamp NO PRI CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP
+drop table t1;
+create table t1 (c1 datetime primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 datetime NO PRI NULL
+drop table t1;
+create table t1 (c1 year primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 year(4) NO PRI NULL
+drop table t1;
+create table t1 (c1 char(10) primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 char(10) NO PRI NULL
+drop table t1;
+create table t1 (c1 varchar(10) primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 varchar(10) NO PRI NULL
+drop table t1;
+create table t1 (c1 binary(10) primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 binary(10) NO PRI NULL
+drop table t1;
+create table t1 (c1 varbinary(10) primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 varbinary(10) NO PRI NULL
+drop table t1;
+create table t1 (c1 enum("yes","no") primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 enum('yes','no') NO PRI NULL
+drop table t1;
+create table t1 (c1 set("A","B","AB","O") primary key) COMMENT = 'engine "innodb"';
+desc t1;
+Field Type Null Key Default Extra
+c1 set('A','B','AB','O') NO PRI NULL
+drop table t1;
+create table t1 (c1 int) COMMENT = 'engine "innodb"';
+ERROR 42000: This table type requires a primary key
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/create_table_comment_combined.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/create_table_comment_combined.result
new file mode 100644
index 00000000000..ce1b5470231
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/create_table_comment_combined.result
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS bugs;
+CREATE TABLE bugs (
+id INT UNSIGNED PRIMARY KEY
+) DEFAULT CHARSET=utf8
+COMMENT='Free style normal comment, engine "InnoDB"';
+SHOW CREATE TABLE bugs;
+Table Create Table
+bugs CREATE TABLE `bugs` (
+ `id` int(10) unsigned NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='Free style normal comment, engine "InnoDB"'
+DROP TABLE bugs;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/create_table_normalizer_fulltext_index.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/create_table_normalizer_fulltext_index.result
new file mode 100644
index 00000000000..ea992f827ca
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/create_table_normalizer_fulltext_index.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+day DATE PRIMARY KEY,
+content VARCHAR(64) NOT NULL,
+FULLTEXT INDEX (content) COMMENT 'normalizer "NormalizerAuto"'
+) COMMENT='engine "InnoDB"' DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+INSERT INTO diaries VALUES ("2013-04-23", "ブラックコーヒーを飲んだ。");
+SELECT * FROM diaries
+WHERE MATCH (content) AGAINST ("+ふらつく" IN BOOLEAN MODE);
+day content
+SELECT * FROM diaries
+WHERE MATCH (content) AGAINST ("+ブラック" IN BOOLEAN MODE);
+day content
+2013-04-23 ブラックコーヒーを飲んだ。
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/delete_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/delete_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..990537622f4
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/delete_TODO_SPLIT_ME.result
@@ -0,0 +1,55 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 int) COMMENT 'engine = "innodb"';
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `c1` int(11) NOT NULL,
+ `c2` int(11) DEFAULT NULL,
+ PRIMARY KEY (`c1`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1 COMMENT='engine = "innodb"'
+insert into t1 values (1, 100);
+insert into t1 values (2, 101);
+insert into t1 values (3, 102);
+insert into t1 values (4, 102);
+select * from t1;
+c1 c2
+1 100
+2 101
+3 102
+4 102
+delete from t1 where c1=3;
+select * from t1;
+c1 c2
+1 100
+2 101
+4 102
+flush tables;
+delete from t1 where c1=2;
+select * from t1;
+c1 c2
+1 100
+4 102
+delete from t1;
+select * from t1;
+c1 c2
+drop table t1;
+create table t1 (c1 int primary key, c2 text, fulltext index (c2)) COMMENT 'engine = "innodb"';
+insert into t1 values(10, "aa ii uu ee");
+insert into t1 values(20, "ka ki ku ke");
+insert into t1 values(30, "sa si su se");
+select * from t1;
+c1 c2
+10 aa ii uu ee
+20 ka ki ku ke
+30 sa si su se
+select * from t1 where match(c2) against("ki");
+c1 c2
+20 ka ki ku ke
+delete from t1 where c1=20;
+select * from t1;
+c1 c2
+10 aa ii uu ee
+30 sa si su se
+select * from t1 where match(c2) against("ki");
+c1 c2
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/delete_all.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/delete_all.result
new file mode 100644
index 00000000000..200e978a582
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/delete_all.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS users;
+SET NAMES utf8;
+CREATE TABLE users (
+id int PRIMARY KEY,
+name varchar(100),
+FULLTEXT INDEX (name)
+) COMMENT 'engine = "InnoDB"' DEFAULT CHARSET=utf8;
+INSERT INTO users VALUES (1, 'Alice');
+INSERT INTO users VALUES (2, 'Bob');
+INSERT INTO users VALUES (3, 'Chris');
+SELECT * FROM users;
+id name
+1 Alice
+2 Bob
+3 Chris
+DELETE FROM users;
+SELECT * FROM users;
+id name
+DROP TABLE users;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_leading_not.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_leading_not.result
new file mode 100644
index 00000000000..08c37695f5a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_leading_not.result
@@ -0,0 +1,24 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT PRIMARY KEY,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET = UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("-明日 +天気" IN BOOLEAN MODE);
+id title content
+3 富士山 今日も天気がよくてきれいに見える。
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_multiple_match_against.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_multiple_match_against.result
new file mode 100644
index 00000000000..b0bff284532
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_multiple_match_against.result
@@ -0,0 +1,32 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT PRIMARY KEY,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX (title),
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET = UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries VALUES(1, "富士山", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気 1月1日", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "天気 4月4日", "今日も天気がよくてきれいに見える。");
+SELECT COUNT(*) FROM diaries WHERE MATCH(title) AGAINST("+天気" IN BOOLEAN MODE) AND MATCH(content) AGAINST("+今日" IN BOOLEAN MODE);
+COUNT(*)
+1
+SELECT * FROM diaries WHERE MATCH(title) AGAINST("+天気" IN BOOLEAN MODE) AND MATCH(content) AGAINST("+今日" IN BOOLEAN MODE);
+id title content
+3 天気 4月4日 今日も天気がよくてきれいに見える。
+SELECT 1 FROM diaries WHERE MATCH(title) AGAINST("+天気" IN BOOLEAN MODE) AND MATCH(content) AGAINST("+今日" IN BOOLEAN MODE);
+1
+1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.result
new file mode 100644
index 00000000000..190105a3ba1
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.result
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+id INT PRIMARY KEY AUTO_INCREMENT,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Yesterday was good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be fine.");
+INSERT INTO memos VALUES (NULL, "Yesterday was fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*D- fine is be" IN BOOLEAN MODE);
+id content
+6 Yesterday was fine.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_minus_with_or.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_minus_with_or.result
new file mode 100644
index 00000000000..240da5c357f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_minus_with_or.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+id INT PRIMARY KEY AUTO_INCREMENT,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Yesterday was good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be fine.");
+INSERT INTO memos VALUES (NULL, "Yesterday was fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*D- is OR be fine" IN BOOLEAN MODE);
+id content
+1 Today is good day.
+2 Tomorrow will be good day.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.result
new file mode 100644
index 00000000000..6dedd2183eb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+id INT PRIMARY KEY AUTO_INCREMENT,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Yesterday was good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be fine.");
+INSERT INTO memos VALUES (NULL, "Yesterday was fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*D- good +day be" IN BOOLEAN MODE);
+id content
+1 Today is good day.
+3 Yesterday was good day.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_or_no_operator.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_or_no_operator.result
new file mode 100644
index 00000000000..14d30fac260
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_or_no_operator.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+id INT PRIMARY KEY AUTO_INCREMENT,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be fine.");
+INSERT INTO memos VALUES (NULL, "Yesterday was fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*DOR today good" IN BOOLEAN MODE);
+id content
+1 Today is good day.
+3 Today is fine.
+2 Tomorrow will be good day.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_or_with_minus.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_or_with_minus.result
new file mode 100644
index 00000000000..d367ba1d21f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_or_with_minus.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+id INT PRIMARY KEY AUTO_INCREMENT,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be fine.");
+INSERT INTO memos VALUES (NULL, "Yesterday was fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*DOR today -good tomorrow" IN BOOLEAN MODE);
+id content
+2 Tomorrow will be good day.
+3 Today is fine.
+4 Tomorrow will be fine.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_or_with_plus.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_or_with_plus.result
new file mode 100644
index 00000000000..6bf48ab6556
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_or_with_plus.result
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+id INT PRIMARY KEY AUTO_INCREMENT,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be fine.");
+INSERT INTO memos VALUES (NULL, "Yesterday was fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*DOR today +good tomorrow" IN BOOLEAN MODE);
+id content
+1 Today is good day.
+2 Tomorrow will be good day.
+4 Tomorrow will be fine.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.result
new file mode 100644
index 00000000000..29975d55137
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.result
@@ -0,0 +1,15 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+id INT PRIMARY KEY AUTO_INCREMENT,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*D+ today good" IN BOOLEAN MODE);
+id content
+1 Today is good day.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.result
new file mode 100644
index 00000000000..2294308ae92
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.result
@@ -0,0 +1,15 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+id INT PRIMARY KEY AUTO_INCREMENT,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*D+ today -good is" IN BOOLEAN MODE);
+id content
+3 Today is fine.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_plus_with_or.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_plus_with_or.result
new file mode 100644
index 00000000000..b61f0637bb9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_default_operator_plus_with_or.result
@@ -0,0 +1,16 @@
+DROP TABLE IF EXISTS memos;
+SET NAMES utf8;
+CREATE TABLE memos (
+id INT PRIMARY KEY AUTO_INCREMENT,
+content TEXT,
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+SELECT * FROM memos
+WHERE MATCH (content) AGAINST ("*D+ today OR tomorrow day" IN BOOLEAN MODE);
+id content
+1 Today is good day.
+2 Tomorrow will be good day.
+DROP TABLE memos;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_weight_full_spec.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_weight_full_spec.result
new file mode 100644
index 00000000000..e603cc27401
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_weight_full_spec.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id INT PRIMARY KEY,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX (title, content)
+) DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"';
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+SELECT *, MATCH(title, content)
+AGAINST("*W1:10,2:2 +天気" in BOOLEAN MODE) AS score
+FROM diaries
+WHERE MATCH(title, content)
+AGAINST("*W1:10,2:2 +天気" in BOOLEAN MODE);
+id title content score
+2 天気 明日の富士山の天気について 12
+3 富士山 今日も天気がよくてきれいに見える。 2
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_weight_no_weight.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_weight_no_weight.result
new file mode 100644
index 00000000000..6a5dbd5ca6b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_weight_no_weight.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id INT PRIMARY KEY,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX (title, content)
+) DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"';
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+SELECT *, MATCH(title, content)
+AGAINST("*W1,2:2 +天気" in BOOLEAN MODE) AS score
+FROM diaries
+WHERE MATCH(title, content)
+AGAINST("*W1,2:2 +天気" in BOOLEAN MODE);
+id title content score
+2 天気 明日の富士山の天気について 3
+3 富士山 今日も天気がよくてきれいに見える。 2
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_weight_omit_section.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_weight_omit_section.result
new file mode 100644
index 00000000000..2fe63a68f77
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_boolean_mode_pragma_weight_omit_section.result
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id INT PRIMARY KEY,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX (title, content)
+) DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"';
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+SELECT *, MATCH(title, content)
+AGAINST("*W1:2 +天気" in BOOLEAN MODE) AS score
+FROM diaries
+WHERE MATCH(title, content)
+AGAINST("*W1:2 +天気" in BOOLEAN MODE);
+id title content score
+2 天気 明日の富士山の天気について 3
+3 富士山 今日も天気がよくてきれいに見える。 1
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_ascii.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_ascii.result
new file mode 100644
index 00000000000..b58f4ad3364
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_ascii.result
@@ -0,0 +1,29 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3)) COMMENT = 'engine "innodb"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"sa si su se so");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ee oo");
+select * from t1;
+c1 c2 c3
+1 10 aa ii uu ee oo
+2 20 ka ki ku ke ko
+3 30 sa si su se so
+4 40 ta ti tu te to
+5 50 aa ii uu ee oo
+select * from t1 where match(c3) against("su");
+c1 c2 c3
+3 30 sa si su se so
+select * from t1 where match(c3) against("ii");
+c1 c2 c3
+1 10 aa ii uu ee oo
+5 50 aa ii uu ee oo
+select * from t1 where match(c3) against("+su" in boolean mode);
+c1 c2 c3
+3 30 sa si su se so
+select * from t1 where match(c3) against("+ii" in boolean mode);
+c1 c2 c3
+1 10 aa ii uu ee oo
+5 50 aa ii uu ee oo
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_cp932.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_cp932.result
new file mode 100644
index 00000000000..d7b64010ebf
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_cp932.result
@@ -0,0 +1,18 @@
+drop table if exists t1, t2, t3;
+set names cp932;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset cp932 COMMENT = 'engine "innodb"';
+insert into t1 values(1, "̕xmR̓VCɂ‚","");
+insert into t1 values(2, "","̕xmR̓VC͕܂");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+c1 c2 c3
+1 ̕xmR̓VCɂ‚
+2 ̕xmR̓VC͕܂
+3 dummy dummy
+select * from t1 where match(c2) against("xmR");
+c1 c2 c3
+1 ̕xmR̓VCɂ‚
+select * from t1 where match(c3) against("xmR");
+c1 c2 c3
+2 ̕xmR̓VC͕܂
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_eucjpms.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_eucjpms.result
new file mode 100644
index 00000000000..9aa60690513
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_eucjpms.result
@@ -0,0 +1,18 @@
+drop table if exists t1, t2, t3;
+set names eucjpms;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset eucjpms COMMENT = 'engine "innodb"';
+insert into t1 values(1, "ٻλŷˤĤ","");
+insert into t1 values(2, "","ٻλŷʬޤ");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+c1 c2 c3
+1 ٻλŷˤĤ
+2 ٻλŷʬޤ
+3 dummy dummy
+select * from t1 where match(c2) against("ٻλ");
+c1 c2 c3
+1 ٻλŷˤĤ
+select * from t1 where match(c3) against("ٻλ");
+c1 c2 c3
+2 ٻλŷʬޤ
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_japanese.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_japanese.result
new file mode 100644
index 00000000000..6c73f4627bd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_charset_japanese.result
@@ -0,0 +1,18 @@
+drop table if exists t1, t2, t3;
+set names utf8;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset utf8 COMMENT = 'engine "innodb"';
+insert into t1 values(1, "明日の富士山の天気について","あああああああ");
+insert into t1 values(2, "いいいいい","明日の富士山の天気は分かりません");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+c1 c2 c3
+1 明日の富士山の天気について あああああああ
+2 いいいいい 明日の富士山の天気は分かりません
+3 dummy dummy
+select * from t1 where match(c2) against("富士山");
+c1 c2 c3
+1 明日の富士山の天気について あああああああ
+select * from t1 where match(c3) against("富士山");
+c1 c2 c3
+2 いいいいい 明日の富士山の天気は分かりません
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_index_recreate.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_index_recreate.result
new file mode 100644
index 00000000000..fd07ea67964
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_index_recreate.result
@@ -0,0 +1,41 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES utf8;
+CREATE TABLE diaries (
+id int PRIMARY KEY,
+title varchar(255),
+content text,
+FULLTEXT INDEX (title)
+) DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries VALUES (1, "Hello", "はじめました。");
+INSERT INTO diaries VALUES (2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES (3, "富士山", "今日もきれい。");
+SELECT * FROM diaries WHERE MATCH (title) AGAINST ("富士山");
+id title content
+3 富士山 今日もきれい。
+DROP INDEX title ON diaries;
+SELECT * FROM diaries WHERE MATCH (title) AGAINST ("富士山");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+SELECT * FROM diaries;
+id title content
+1 Hello はじめました。
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+CREATE FULLTEXT INDEX new_title_index ON diaries (title);
+SELECT * FROM diaries WHERE MATCH (title) AGAINST ("富士山");
+id title content
+3 富士山 今日もきれい。
+SELECT * FROM diaries;
+id title content
+1 Hello はじめました。
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_insert_select.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_insert_select.result
new file mode 100644
index 00000000000..03300e3e9ef
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_insert_select.result
@@ -0,0 +1,66 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 varchar(100), fulltext index(c2)) default charset utf8 COMMENT = 'engine "innodb"';
+create table t2 (c1 int primary key, c2 text, fulltext index(c2)) default charset utf8 COMMENT = 'engine "innodb"';
+insert into t1 values (1, "aa ii uu ee oo");
+insert into t1 values (2, "ka ki ku ke ko");
+insert into t1 values (3, "aa ii ii ii oo");
+insert into t1 values (4, "sa si su se so");
+insert into t1 values (5, "ta ti ii ii to");
+insert into t2 (c1,c2) select c1,c2 from t1;
+select * from t1;
+c1 c2
+1 aa ii uu ee oo
+2 ka ki ku ke ko
+3 aa ii ii ii oo
+4 sa si su se so
+5 ta ti ii ii to
+select * from t2;
+c1 c2
+1 aa ii uu ee oo
+2 ka ki ku ke ko
+3 aa ii ii ii oo
+4 sa si su se so
+5 ta ti ii ii to
+select * from t1 where c1=3;
+c1 c2
+3 aa ii ii ii oo
+select * from t2 where c1=3;
+c1 c2
+3 aa ii ii ii oo
+select * from t1 where c1>3 order by c1 desc;
+c1 c2
+5 ta ti ii ii to
+4 sa si su se so
+select * from t2 where c1>3 order by c1 asc;
+c1 c2
+4 sa si su se so
+5 ta ti ii ii to
+select * from t1 where c2>"s" order by c2 desc;
+c1 c2
+5 ta ti ii ii to
+4 sa si su se so
+select * from t2 where c2>"s" order by c1 asc;
+c1 c2
+4 sa si su se so
+5 ta ti ii ii to
+select *,match(c2) against("ii") from t1 where match(c2) against("ii") order by match(c2) against("ii") desc;
+c1 c2 match(c2) against("ii")
+3 aa ii ii ii oo 524289
+5 ta ti ii ii to 349526
+1 aa ii uu ee oo 174763
+select *,match(c2) against("ii") from t2 where match(c2) against("ii") order by match(c2) against("ii") asc;
+c1 c2 match(c2) against("ii")
+1 aa ii uu ee oo 174763
+5 ta ti ii ii to 349526
+3 aa ii ii ii oo 524289
+select c1,c2,match(c2) against("ii") from t1 where match(c2) against("ii");
+c1 c2 match(c2) against("ii")
+1 aa ii uu ee oo 174763
+3 aa ii ii ii oo 524289
+5 ta ti ii ii to 349526
+select c1,c2,match(c2) against("ii") from t1 where match(c2) against("ii");
+c1 c2 match(c2) against("ii")
+1 aa ii uu ee oo 174763
+3 aa ii ii ii oo 524289
+5 ta ti ii ii to 349526
+drop table t1,t2;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_insert_values.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_insert_values.result
new file mode 100644
index 00000000000..8e1b0845e85
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_insert_values.result
@@ -0,0 +1,25 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 text, fulltext index ft (c2)) COMMENT = 'engine "innodb"';
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `c1` int(11) NOT NULL,
+ `c2` text,
+ PRIMARY KEY (`c1`),
+ FULLTEXT KEY `ft` (`c2`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1 COMMENT='engine "innodb"'
+insert into t1 values (1, "hoge hoge");
+insert into t1 values (2, "fuga fuga");
+insert into t1 values (3, "moge moge");
+select * from t1;
+c1 c2
+1 hoge hoge
+2 fuga fuga
+3 moge moge
+flush tables;
+select * from t1;
+c1 c2
+1 hoge hoge
+2 fuga fuga
+3 moge moge
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_many_records.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_many_records.result
new file mode 100644
index 00000000000..3809006038c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_many_records.result
@@ -0,0 +1,4389 @@
+drop table if exists diaries;
+set names utf8;
+create table diaries (
+id int primary key,
+title varchar(255),
+fulltext index (title)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+set autocommit=0;
+insert into diaries values(0, "2011-07-14");
+insert into diaries values(1, "2011-07-15");
+insert into diaries values(2, "2011-07-16");
+insert into diaries values(3, "2011-07-17");
+insert into diaries values(4, "2011-07-18");
+insert into diaries values(5, "2011-07-19");
+insert into diaries values(6, "2011-07-20");
+insert into diaries values(7, "2011-07-21");
+insert into diaries values(8, "2011-07-22");
+insert into diaries values(9, "2011-07-23");
+insert into diaries values(10, "2011-07-24");
+insert into diaries values(11, "2011-07-25");
+insert into diaries values(12, "2011-07-26");
+insert into diaries values(13, "2011-07-27");
+insert into diaries values(14, "2011-07-28");
+insert into diaries values(15, "2011-07-29");
+insert into diaries values(16, "2011-07-30");
+insert into diaries values(17, "2011-07-31");
+insert into diaries values(18, "2011-08-01");
+insert into diaries values(19, "2011-08-02");
+insert into diaries values(20, "2011-08-03");
+insert into diaries values(21, "2011-08-04");
+insert into diaries values(22, "2011-08-05");
+insert into diaries values(23, "2011-08-06");
+insert into diaries values(24, "2011-08-07");
+insert into diaries values(25, "2011-08-08");
+insert into diaries values(26, "2011-08-09");
+insert into diaries values(27, "2011-08-10");
+insert into diaries values(28, "2011-08-11");
+insert into diaries values(29, "2011-08-12");
+insert into diaries values(30, "2011-08-13");
+insert into diaries values(31, "2011-08-14");
+insert into diaries values(32, "2011-08-15");
+insert into diaries values(33, "2011-08-16");
+insert into diaries values(34, "2011-08-17");
+insert into diaries values(35, "2011-08-18");
+insert into diaries values(36, "2011-08-19");
+insert into diaries values(37, "2011-08-20");
+insert into diaries values(38, "2011-08-21");
+insert into diaries values(39, "2011-08-22");
+insert into diaries values(40, "2011-08-23");
+insert into diaries values(41, "2011-08-24");
+insert into diaries values(42, "2011-08-25");
+insert into diaries values(43, "2011-08-26");
+insert into diaries values(44, "2011-08-27");
+insert into diaries values(45, "2011-08-28");
+insert into diaries values(46, "2011-08-29");
+insert into diaries values(47, "2011-08-30");
+insert into diaries values(48, "2011-08-31");
+insert into diaries values(49, "2011-09-01");
+insert into diaries values(50, "2011-09-02");
+insert into diaries values(51, "2011-09-03");
+insert into diaries values(52, "2011-09-04");
+insert into diaries values(53, "2011-09-05");
+insert into diaries values(54, "2011-09-06");
+insert into diaries values(55, "2011-09-07");
+insert into diaries values(56, "2011-09-08");
+insert into diaries values(57, "2011-09-09");
+insert into diaries values(58, "2011-09-10");
+insert into diaries values(59, "2011-09-11");
+insert into diaries values(60, "2011-09-12");
+insert into diaries values(61, "2011-09-13");
+insert into diaries values(62, "2011-09-14");
+insert into diaries values(63, "2011-09-15");
+insert into diaries values(64, "2011-09-16");
+insert into diaries values(65, "2011-09-17");
+insert into diaries values(66, "2011-09-18");
+insert into diaries values(67, "2011-09-19");
+insert into diaries values(68, "2011-09-20");
+insert into diaries values(69, "2011-09-21");
+insert into diaries values(70, "2011-09-22");
+insert into diaries values(71, "2011-09-23");
+insert into diaries values(72, "2011-09-24");
+insert into diaries values(73, "2011-09-25");
+insert into diaries values(74, "2011-09-26");
+insert into diaries values(75, "2011-09-27");
+insert into diaries values(76, "2011-09-28");
+insert into diaries values(77, "2011-09-29");
+insert into diaries values(78, "2011-09-30");
+insert into diaries values(79, "2011-10-01");
+insert into diaries values(80, "2011-10-02");
+insert into diaries values(81, "2011-10-03");
+insert into diaries values(82, "2011-10-04");
+insert into diaries values(83, "2011-10-05");
+insert into diaries values(84, "2011-10-06");
+insert into diaries values(85, "2011-10-07");
+insert into diaries values(86, "2011-10-08");
+insert into diaries values(87, "2011-10-09");
+insert into diaries values(88, "2011-10-10");
+insert into diaries values(89, "2011-10-11");
+insert into diaries values(90, "2011-10-12");
+insert into diaries values(91, "2011-10-13");
+insert into diaries values(92, "2011-10-14");
+insert into diaries values(93, "2011-10-15");
+insert into diaries values(94, "2011-10-16");
+insert into diaries values(95, "2011-10-17");
+insert into diaries values(96, "2011-10-18");
+insert into diaries values(97, "2011-10-19");
+insert into diaries values(98, "2011-10-20");
+insert into diaries values(99, "2011-10-21");
+insert into diaries values(100, "2011-10-22");
+insert into diaries values(101, "2011-10-23");
+insert into diaries values(102, "2011-10-24");
+insert into diaries values(103, "2011-10-25");
+insert into diaries values(104, "2011-10-26");
+insert into diaries values(105, "2011-10-27");
+insert into diaries values(106, "2011-10-28");
+insert into diaries values(107, "2011-10-29");
+insert into diaries values(108, "2011-10-30");
+insert into diaries values(109, "2011-10-31");
+insert into diaries values(110, "2011-11-01");
+insert into diaries values(111, "2011-11-02");
+insert into diaries values(112, "2011-11-03");
+insert into diaries values(113, "2011-11-04");
+insert into diaries values(114, "2011-11-05");
+insert into diaries values(115, "2011-11-06");
+insert into diaries values(116, "2011-11-07");
+insert into diaries values(117, "2011-11-08");
+insert into diaries values(118, "2011-11-09");
+insert into diaries values(119, "2011-11-10");
+insert into diaries values(120, "2011-11-11");
+insert into diaries values(121, "2011-11-12");
+insert into diaries values(122, "2011-11-13");
+insert into diaries values(123, "2011-11-14");
+insert into diaries values(124, "2011-11-15");
+insert into diaries values(125, "2011-11-16");
+insert into diaries values(126, "2011-11-17");
+insert into diaries values(127, "2011-11-18");
+insert into diaries values(128, "2011-11-19");
+insert into diaries values(129, "2011-11-20");
+insert into diaries values(130, "2011-11-21");
+insert into diaries values(131, "2011-11-22");
+insert into diaries values(132, "2011-11-23");
+insert into diaries values(133, "2011-11-24");
+insert into diaries values(134, "2011-11-25");
+insert into diaries values(135, "2011-11-26");
+insert into diaries values(136, "2011-11-27");
+insert into diaries values(137, "2011-11-28");
+insert into diaries values(138, "2011-11-29");
+insert into diaries values(139, "2011-11-30");
+insert into diaries values(140, "2011-12-01");
+insert into diaries values(141, "2011-12-02");
+insert into diaries values(142, "2011-12-03");
+insert into diaries values(143, "2011-12-04");
+insert into diaries values(144, "2011-12-05");
+insert into diaries values(145, "2011-12-06");
+insert into diaries values(146, "2011-12-07");
+insert into diaries values(147, "2011-12-08");
+insert into diaries values(148, "2011-12-09");
+insert into diaries values(149, "2011-12-10");
+insert into diaries values(150, "2011-12-11");
+insert into diaries values(151, "2011-12-12");
+insert into diaries values(152, "2011-12-13");
+insert into diaries values(153, "2011-12-14");
+insert into diaries values(154, "2011-12-15");
+insert into diaries values(155, "2011-12-16");
+insert into diaries values(156, "2011-12-17");
+insert into diaries values(157, "2011-12-18");
+insert into diaries values(158, "2011-12-19");
+insert into diaries values(159, "2011-12-20");
+insert into diaries values(160, "2011-12-21");
+insert into diaries values(161, "2011-12-22");
+insert into diaries values(162, "2011-12-23");
+insert into diaries values(163, "2011-12-24");
+insert into diaries values(164, "2011-12-25");
+insert into diaries values(165, "2011-12-26");
+insert into diaries values(166, "2011-12-27");
+insert into diaries values(167, "2011-12-28");
+insert into diaries values(168, "2011-12-29");
+insert into diaries values(169, "2011-12-30");
+insert into diaries values(170, "2011-12-31");
+insert into diaries values(171, "2012-01-01");
+insert into diaries values(172, "2012-01-02");
+insert into diaries values(173, "2012-01-03");
+insert into diaries values(174, "2012-01-04");
+insert into diaries values(175, "2012-01-05");
+insert into diaries values(176, "2012-01-06");
+insert into diaries values(177, "2012-01-07");
+insert into diaries values(178, "2012-01-08");
+insert into diaries values(179, "2012-01-09");
+insert into diaries values(180, "2012-01-10");
+insert into diaries values(181, "2012-01-11");
+insert into diaries values(182, "2012-01-12");
+insert into diaries values(183, "2012-01-13");
+insert into diaries values(184, "2012-01-14");
+insert into diaries values(185, "2012-01-15");
+insert into diaries values(186, "2012-01-16");
+insert into diaries values(187, "2012-01-17");
+insert into diaries values(188, "2012-01-18");
+insert into diaries values(189, "2012-01-19");
+insert into diaries values(190, "2012-01-20");
+insert into diaries values(191, "2012-01-21");
+insert into diaries values(192, "2012-01-22");
+insert into diaries values(193, "2012-01-23");
+insert into diaries values(194, "2012-01-24");
+insert into diaries values(195, "2012-01-25");
+insert into diaries values(196, "2012-01-26");
+insert into diaries values(197, "2012-01-27");
+insert into diaries values(198, "2012-01-28");
+insert into diaries values(199, "2012-01-29");
+insert into diaries values(200, "2012-01-30");
+insert into diaries values(201, "2012-01-31");
+insert into diaries values(202, "2012-02-01");
+insert into diaries values(203, "2012-02-02");
+insert into diaries values(204, "2012-02-03");
+insert into diaries values(205, "2012-02-04");
+insert into diaries values(206, "2012-02-05");
+insert into diaries values(207, "2012-02-06");
+insert into diaries values(208, "2012-02-07");
+insert into diaries values(209, "2012-02-08");
+insert into diaries values(210, "2012-02-09");
+insert into diaries values(211, "2012-02-10");
+insert into diaries values(212, "2012-02-11");
+insert into diaries values(213, "2012-02-12");
+insert into diaries values(214, "2012-02-13");
+insert into diaries values(215, "2012-02-14");
+insert into diaries values(216, "2012-02-15");
+insert into diaries values(217, "2012-02-16");
+insert into diaries values(218, "2012-02-17");
+insert into diaries values(219, "2012-02-18");
+insert into diaries values(220, "2012-02-19");
+insert into diaries values(221, "2012-02-20");
+insert into diaries values(222, "2012-02-21");
+insert into diaries values(223, "2012-02-22");
+insert into diaries values(224, "2012-02-23");
+insert into diaries values(225, "2012-02-24");
+insert into diaries values(226, "2012-02-25");
+insert into diaries values(227, "2012-02-26");
+insert into diaries values(228, "2012-02-27");
+insert into diaries values(229, "2012-02-28");
+insert into diaries values(230, "2012-02-29");
+insert into diaries values(231, "2012-03-01");
+insert into diaries values(232, "2012-03-02");
+insert into diaries values(233, "2012-03-03");
+insert into diaries values(234, "2012-03-04");
+insert into diaries values(235, "2012-03-05");
+insert into diaries values(236, "2012-03-06");
+insert into diaries values(237, "2012-03-07");
+insert into diaries values(238, "2012-03-08");
+insert into diaries values(239, "2012-03-09");
+insert into diaries values(240, "2012-03-10");
+insert into diaries values(241, "2012-03-11");
+insert into diaries values(242, "2012-03-12");
+insert into diaries values(243, "2012-03-13");
+insert into diaries values(244, "2012-03-14");
+insert into diaries values(245, "2012-03-15");
+insert into diaries values(246, "2012-03-16");
+insert into diaries values(247, "2012-03-17");
+insert into diaries values(248, "2012-03-18");
+insert into diaries values(249, "2012-03-19");
+insert into diaries values(250, "2012-03-20");
+insert into diaries values(251, "2012-03-21");
+insert into diaries values(252, "2012-03-22");
+insert into diaries values(253, "2012-03-23");
+insert into diaries values(254, "2012-03-24");
+insert into diaries values(255, "2012-03-25");
+insert into diaries values(256, "2012-03-26");
+insert into diaries values(257, "2012-03-27");
+insert into diaries values(258, "2012-03-28");
+insert into diaries values(259, "2012-03-29");
+insert into diaries values(260, "2012-03-30");
+insert into diaries values(261, "2012-03-31");
+insert into diaries values(262, "2012-04-01");
+insert into diaries values(263, "2012-04-02");
+insert into diaries values(264, "2012-04-03");
+insert into diaries values(265, "2012-04-04");
+insert into diaries values(266, "2012-04-05");
+insert into diaries values(267, "2012-04-06");
+insert into diaries values(268, "2012-04-07");
+insert into diaries values(269, "2012-04-08");
+insert into diaries values(270, "2012-04-09");
+insert into diaries values(271, "2012-04-10");
+insert into diaries values(272, "2012-04-11");
+insert into diaries values(273, "2012-04-12");
+insert into diaries values(274, "2012-04-13");
+insert into diaries values(275, "2012-04-14");
+insert into diaries values(276, "2012-04-15");
+insert into diaries values(277, "2012-04-16");
+insert into diaries values(278, "2012-04-17");
+insert into diaries values(279, "2012-04-18");
+insert into diaries values(280, "2012-04-19");
+insert into diaries values(281, "2012-04-20");
+insert into diaries values(282, "2012-04-21");
+insert into diaries values(283, "2012-04-22");
+insert into diaries values(284, "2012-04-23");
+insert into diaries values(285, "2012-04-24");
+insert into diaries values(286, "2012-04-25");
+insert into diaries values(287, "2012-04-26");
+insert into diaries values(288, "2012-04-27");
+insert into diaries values(289, "2012-04-28");
+insert into diaries values(290, "2012-04-29");
+insert into diaries values(291, "2012-04-30");
+insert into diaries values(292, "2012-05-01");
+insert into diaries values(293, "2012-05-02");
+insert into diaries values(294, "2012-05-03");
+insert into diaries values(295, "2012-05-04");
+insert into diaries values(296, "2012-05-05");
+insert into diaries values(297, "2012-05-06");
+insert into diaries values(298, "2012-05-07");
+insert into diaries values(299, "2012-05-08");
+insert into diaries values(300, "2012-05-09");
+insert into diaries values(301, "2012-05-10");
+insert into diaries values(302, "2012-05-11");
+insert into diaries values(303, "2012-05-12");
+insert into diaries values(304, "2012-05-13");
+insert into diaries values(305, "2012-05-14");
+insert into diaries values(306, "2012-05-15");
+insert into diaries values(307, "2012-05-16");
+insert into diaries values(308, "2012-05-17");
+insert into diaries values(309, "2012-05-18");
+insert into diaries values(310, "2012-05-19");
+insert into diaries values(311, "2012-05-20");
+insert into diaries values(312, "2012-05-21");
+insert into diaries values(313, "2012-05-22");
+insert into diaries values(314, "2012-05-23");
+insert into diaries values(315, "2012-05-24");
+insert into diaries values(316, "2012-05-25");
+insert into diaries values(317, "2012-05-26");
+insert into diaries values(318, "2012-05-27");
+insert into diaries values(319, "2012-05-28");
+insert into diaries values(320, "2012-05-29");
+insert into diaries values(321, "2012-05-30");
+insert into diaries values(322, "2012-05-31");
+insert into diaries values(323, "2012-06-01");
+insert into diaries values(324, "2012-06-02");
+insert into diaries values(325, "2012-06-03");
+insert into diaries values(326, "2012-06-04");
+insert into diaries values(327, "2012-06-05");
+insert into diaries values(328, "2012-06-06");
+insert into diaries values(329, "2012-06-07");
+insert into diaries values(330, "2012-06-08");
+insert into diaries values(331, "2012-06-09");
+insert into diaries values(332, "2012-06-10");
+insert into diaries values(333, "2012-06-11");
+insert into diaries values(334, "2012-06-12");
+insert into diaries values(335, "2012-06-13");
+insert into diaries values(336, "2012-06-14");
+insert into diaries values(337, "2012-06-15");
+insert into diaries values(338, "2012-06-16");
+insert into diaries values(339, "2012-06-17");
+insert into diaries values(340, "2012-06-18");
+insert into diaries values(341, "2012-06-19");
+insert into diaries values(342, "2012-06-20");
+insert into diaries values(343, "2012-06-21");
+insert into diaries values(344, "2012-06-22");
+insert into diaries values(345, "2012-06-23");
+insert into diaries values(346, "2012-06-24");
+insert into diaries values(347, "2012-06-25");
+insert into diaries values(348, "2012-06-26");
+insert into diaries values(349, "2012-06-27");
+insert into diaries values(350, "2012-06-28");
+insert into diaries values(351, "2012-06-29");
+insert into diaries values(352, "2012-06-30");
+insert into diaries values(353, "2012-07-01");
+insert into diaries values(354, "2012-07-02");
+insert into diaries values(355, "2012-07-03");
+insert into diaries values(356, "2012-07-04");
+insert into diaries values(357, "2012-07-05");
+insert into diaries values(358, "2012-07-06");
+insert into diaries values(359, "2012-07-07");
+insert into diaries values(360, "2012-07-08");
+insert into diaries values(361, "2012-07-09");
+insert into diaries values(362, "2012-07-10");
+insert into diaries values(363, "2012-07-11");
+insert into diaries values(364, "2012-07-12");
+insert into diaries values(365, "2012-07-13");
+insert into diaries values(366, "2012-07-14");
+insert into diaries values(367, "2012-07-15");
+insert into diaries values(368, "2012-07-16");
+insert into diaries values(369, "2012-07-17");
+insert into diaries values(370, "2012-07-18");
+insert into diaries values(371, "2012-07-19");
+insert into diaries values(372, "2012-07-20");
+insert into diaries values(373, "2012-07-21");
+insert into diaries values(374, "2012-07-22");
+insert into diaries values(375, "2012-07-23");
+insert into diaries values(376, "2012-07-24");
+insert into diaries values(377, "2012-07-25");
+insert into diaries values(378, "2012-07-26");
+insert into diaries values(379, "2012-07-27");
+insert into diaries values(380, "2012-07-28");
+insert into diaries values(381, "2012-07-29");
+insert into diaries values(382, "2012-07-30");
+insert into diaries values(383, "2012-07-31");
+insert into diaries values(384, "2012-08-01");
+insert into diaries values(385, "2012-08-02");
+insert into diaries values(386, "2012-08-03");
+insert into diaries values(387, "2012-08-04");
+insert into diaries values(388, "2012-08-05");
+insert into diaries values(389, "2012-08-06");
+insert into diaries values(390, "2012-08-07");
+insert into diaries values(391, "2012-08-08");
+insert into diaries values(392, "2012-08-09");
+insert into diaries values(393, "2012-08-10");
+insert into diaries values(394, "2012-08-11");
+insert into diaries values(395, "2012-08-12");
+insert into diaries values(396, "2012-08-13");
+insert into diaries values(397, "2012-08-14");
+insert into diaries values(398, "2012-08-15");
+insert into diaries values(399, "2012-08-16");
+insert into diaries values(400, "2012-08-17");
+insert into diaries values(401, "2012-08-18");
+insert into diaries values(402, "2012-08-19");
+insert into diaries values(403, "2012-08-20");
+insert into diaries values(404, "2012-08-21");
+insert into diaries values(405, "2012-08-22");
+insert into diaries values(406, "2012-08-23");
+insert into diaries values(407, "2012-08-24");
+insert into diaries values(408, "2012-08-25");
+insert into diaries values(409, "2012-08-26");
+insert into diaries values(410, "2012-08-27");
+insert into diaries values(411, "2012-08-28");
+insert into diaries values(412, "2012-08-29");
+insert into diaries values(413, "2012-08-30");
+insert into diaries values(414, "2012-08-31");
+insert into diaries values(415, "2012-09-01");
+insert into diaries values(416, "2012-09-02");
+insert into diaries values(417, "2012-09-03");
+insert into diaries values(418, "2012-09-04");
+insert into diaries values(419, "2012-09-05");
+insert into diaries values(420, "2012-09-06");
+insert into diaries values(421, "2012-09-07");
+insert into diaries values(422, "2012-09-08");
+insert into diaries values(423, "2012-09-09");
+insert into diaries values(424, "2012-09-10");
+insert into diaries values(425, "2012-09-11");
+insert into diaries values(426, "2012-09-12");
+insert into diaries values(427, "2012-09-13");
+insert into diaries values(428, "2012-09-14");
+insert into diaries values(429, "2012-09-15");
+insert into diaries values(430, "2012-09-16");
+insert into diaries values(431, "2012-09-17");
+insert into diaries values(432, "2012-09-18");
+insert into diaries values(433, "2012-09-19");
+insert into diaries values(434, "2012-09-20");
+insert into diaries values(435, "2012-09-21");
+insert into diaries values(436, "2012-09-22");
+insert into diaries values(437, "2012-09-23");
+insert into diaries values(438, "2012-09-24");
+insert into diaries values(439, "2012-09-25");
+insert into diaries values(440, "2012-09-26");
+insert into diaries values(441, "2012-09-27");
+insert into diaries values(442, "2012-09-28");
+insert into diaries values(443, "2012-09-29");
+insert into diaries values(444, "2012-09-30");
+insert into diaries values(445, "2012-10-01");
+insert into diaries values(446, "2012-10-02");
+insert into diaries values(447, "2012-10-03");
+insert into diaries values(448, "2012-10-04");
+insert into diaries values(449, "2012-10-05");
+insert into diaries values(450, "2012-10-06");
+insert into diaries values(451, "2012-10-07");
+insert into diaries values(452, "2012-10-08");
+insert into diaries values(453, "2012-10-09");
+insert into diaries values(454, "2012-10-10");
+insert into diaries values(455, "2012-10-11");
+insert into diaries values(456, "2012-10-12");
+insert into diaries values(457, "2012-10-13");
+insert into diaries values(458, "2012-10-14");
+insert into diaries values(459, "2012-10-15");
+insert into diaries values(460, "2012-10-16");
+insert into diaries values(461, "2012-10-17");
+insert into diaries values(462, "2012-10-18");
+insert into diaries values(463, "2012-10-19");
+insert into diaries values(464, "2012-10-20");
+insert into diaries values(465, "2012-10-21");
+insert into diaries values(466, "2012-10-22");
+insert into diaries values(467, "2012-10-23");
+insert into diaries values(468, "2012-10-24");
+insert into diaries values(469, "2012-10-25");
+insert into diaries values(470, "2012-10-26");
+insert into diaries values(471, "2012-10-27");
+insert into diaries values(472, "2012-10-28");
+insert into diaries values(473, "2012-10-29");
+insert into diaries values(474, "2012-10-30");
+insert into diaries values(475, "2012-10-31");
+insert into diaries values(476, "2012-11-01");
+insert into diaries values(477, "2012-11-02");
+insert into diaries values(478, "2012-11-03");
+insert into diaries values(479, "2012-11-04");
+insert into diaries values(480, "2012-11-05");
+insert into diaries values(481, "2012-11-06");
+insert into diaries values(482, "2012-11-07");
+insert into diaries values(483, "2012-11-08");
+insert into diaries values(484, "2012-11-09");
+insert into diaries values(485, "2012-11-10");
+insert into diaries values(486, "2012-11-11");
+insert into diaries values(487, "2012-11-12");
+insert into diaries values(488, "2012-11-13");
+insert into diaries values(489, "2012-11-14");
+insert into diaries values(490, "2012-11-15");
+insert into diaries values(491, "2012-11-16");
+insert into diaries values(492, "2012-11-17");
+insert into diaries values(493, "2012-11-18");
+insert into diaries values(494, "2012-11-19");
+insert into diaries values(495, "2012-11-20");
+insert into diaries values(496, "2012-11-21");
+insert into diaries values(497, "2012-11-22");
+insert into diaries values(498, "2012-11-23");
+insert into diaries values(499, "2012-11-24");
+insert into diaries values(500, "2012-11-25");
+insert into diaries values(501, "2012-11-26");
+insert into diaries values(502, "2012-11-27");
+insert into diaries values(503, "2012-11-28");
+insert into diaries values(504, "2012-11-29");
+insert into diaries values(505, "2012-11-30");
+insert into diaries values(506, "2012-12-01");
+insert into diaries values(507, "2012-12-02");
+insert into diaries values(508, "2012-12-03");
+insert into diaries values(509, "2012-12-04");
+insert into diaries values(510, "2012-12-05");
+insert into diaries values(511, "2012-12-06");
+insert into diaries values(512, "2012-12-07");
+insert into diaries values(513, "2012-12-08");
+insert into diaries values(514, "2012-12-09");
+insert into diaries values(515, "2012-12-10");
+insert into diaries values(516, "2012-12-11");
+insert into diaries values(517, "2012-12-12");
+insert into diaries values(518, "2012-12-13");
+insert into diaries values(519, "2012-12-14");
+insert into diaries values(520, "2012-12-15");
+insert into diaries values(521, "2012-12-16");
+insert into diaries values(522, "2012-12-17");
+insert into diaries values(523, "2012-12-18");
+insert into diaries values(524, "2012-12-19");
+insert into diaries values(525, "2012-12-20");
+insert into diaries values(526, "2012-12-21");
+insert into diaries values(527, "2012-12-22");
+insert into diaries values(528, "2012-12-23");
+insert into diaries values(529, "2012-12-24");
+insert into diaries values(530, "2012-12-25");
+insert into diaries values(531, "2012-12-26");
+insert into diaries values(532, "2012-12-27");
+insert into diaries values(533, "2012-12-28");
+insert into diaries values(534, "2012-12-29");
+insert into diaries values(535, "2012-12-30");
+insert into diaries values(536, "2012-12-31");
+insert into diaries values(537, "2013-01-01");
+insert into diaries values(538, "2013-01-02");
+insert into diaries values(539, "2013-01-03");
+insert into diaries values(540, "2013-01-04");
+insert into diaries values(541, "2013-01-05");
+insert into diaries values(542, "2013-01-06");
+insert into diaries values(543, "2013-01-07");
+insert into diaries values(544, "2013-01-08");
+insert into diaries values(545, "2013-01-09");
+insert into diaries values(546, "2013-01-10");
+insert into diaries values(547, "2013-01-11");
+insert into diaries values(548, "2013-01-12");
+insert into diaries values(549, "2013-01-13");
+insert into diaries values(550, "2013-01-14");
+insert into diaries values(551, "2013-01-15");
+insert into diaries values(552, "2013-01-16");
+insert into diaries values(553, "2013-01-17");
+insert into diaries values(554, "2013-01-18");
+insert into diaries values(555, "2013-01-19");
+insert into diaries values(556, "2013-01-20");
+insert into diaries values(557, "2013-01-21");
+insert into diaries values(558, "2013-01-22");
+insert into diaries values(559, "2013-01-23");
+insert into diaries values(560, "2013-01-24");
+insert into diaries values(561, "2013-01-25");
+insert into diaries values(562, "2013-01-26");
+insert into diaries values(563, "2013-01-27");
+insert into diaries values(564, "2013-01-28");
+insert into diaries values(565, "2013-01-29");
+insert into diaries values(566, "2013-01-30");
+insert into diaries values(567, "2013-01-31");
+insert into diaries values(568, "2013-02-01");
+insert into diaries values(569, "2013-02-02");
+insert into diaries values(570, "2013-02-03");
+insert into diaries values(571, "2013-02-04");
+insert into diaries values(572, "2013-02-05");
+insert into diaries values(573, "2013-02-06");
+insert into diaries values(574, "2013-02-07");
+insert into diaries values(575, "2013-02-08");
+insert into diaries values(576, "2013-02-09");
+insert into diaries values(577, "2013-02-10");
+insert into diaries values(578, "2013-02-11");
+insert into diaries values(579, "2013-02-12");
+insert into diaries values(580, "2013-02-13");
+insert into diaries values(581, "2013-02-14");
+insert into diaries values(582, "2013-02-15");
+insert into diaries values(583, "2013-02-16");
+insert into diaries values(584, "2013-02-17");
+insert into diaries values(585, "2013-02-18");
+insert into diaries values(586, "2013-02-19");
+insert into diaries values(587, "2013-02-20");
+insert into diaries values(588, "2013-02-21");
+insert into diaries values(589, "2013-02-22");
+insert into diaries values(590, "2013-02-23");
+insert into diaries values(591, "2013-02-24");
+insert into diaries values(592, "2013-02-25");
+insert into diaries values(593, "2013-02-26");
+insert into diaries values(594, "2013-02-27");
+insert into diaries values(595, "2013-02-28");
+insert into diaries values(596, "2013-03-01");
+insert into diaries values(597, "2013-03-02");
+insert into diaries values(598, "2013-03-03");
+insert into diaries values(599, "2013-03-04");
+insert into diaries values(600, "2013-03-05");
+insert into diaries values(601, "2013-03-06");
+insert into diaries values(602, "2013-03-07");
+insert into diaries values(603, "2013-03-08");
+insert into diaries values(604, "2013-03-09");
+insert into diaries values(605, "2013-03-10");
+insert into diaries values(606, "2013-03-11");
+insert into diaries values(607, "2013-03-12");
+insert into diaries values(608, "2013-03-13");
+insert into diaries values(609, "2013-03-14");
+insert into diaries values(610, "2013-03-15");
+insert into diaries values(611, "2013-03-16");
+insert into diaries values(612, "2013-03-17");
+insert into diaries values(613, "2013-03-18");
+insert into diaries values(614, "2013-03-19");
+insert into diaries values(615, "2013-03-20");
+insert into diaries values(616, "2013-03-21");
+insert into diaries values(617, "2013-03-22");
+insert into diaries values(618, "2013-03-23");
+insert into diaries values(619, "2013-03-24");
+insert into diaries values(620, "2013-03-25");
+insert into diaries values(621, "2013-03-26");
+insert into diaries values(622, "2013-03-27");
+insert into diaries values(623, "2013-03-28");
+insert into diaries values(624, "2013-03-29");
+insert into diaries values(625, "2013-03-30");
+insert into diaries values(626, "2013-03-31");
+insert into diaries values(627, "2013-04-01");
+insert into diaries values(628, "2013-04-02");
+insert into diaries values(629, "2013-04-03");
+insert into diaries values(630, "2013-04-04");
+insert into diaries values(631, "2013-04-05");
+insert into diaries values(632, "2013-04-06");
+insert into diaries values(633, "2013-04-07");
+insert into diaries values(634, "2013-04-08");
+insert into diaries values(635, "2013-04-09");
+insert into diaries values(636, "2013-04-10");
+insert into diaries values(637, "2013-04-11");
+insert into diaries values(638, "2013-04-12");
+insert into diaries values(639, "2013-04-13");
+insert into diaries values(640, "2013-04-14");
+insert into diaries values(641, "2013-04-15");
+insert into diaries values(642, "2013-04-16");
+insert into diaries values(643, "2013-04-17");
+insert into diaries values(644, "2013-04-18");
+insert into diaries values(645, "2013-04-19");
+insert into diaries values(646, "2013-04-20");
+insert into diaries values(647, "2013-04-21");
+insert into diaries values(648, "2013-04-22");
+insert into diaries values(649, "2013-04-23");
+insert into diaries values(650, "2013-04-24");
+insert into diaries values(651, "2013-04-25");
+insert into diaries values(652, "2013-04-26");
+insert into diaries values(653, "2013-04-27");
+insert into diaries values(654, "2013-04-28");
+insert into diaries values(655, "2013-04-29");
+insert into diaries values(656, "2013-04-30");
+insert into diaries values(657, "2013-05-01");
+insert into diaries values(658, "2013-05-02");
+insert into diaries values(659, "2013-05-03");
+insert into diaries values(660, "2013-05-04");
+insert into diaries values(661, "2013-05-05");
+insert into diaries values(662, "2013-05-06");
+insert into diaries values(663, "2013-05-07");
+insert into diaries values(664, "2013-05-08");
+insert into diaries values(665, "2013-05-09");
+insert into diaries values(666, "2013-05-10");
+insert into diaries values(667, "2013-05-11");
+insert into diaries values(668, "2013-05-12");
+insert into diaries values(669, "2013-05-13");
+insert into diaries values(670, "2013-05-14");
+insert into diaries values(671, "2013-05-15");
+insert into diaries values(672, "2013-05-16");
+insert into diaries values(673, "2013-05-17");
+insert into diaries values(674, "2013-05-18");
+insert into diaries values(675, "2013-05-19");
+insert into diaries values(676, "2013-05-20");
+insert into diaries values(677, "2013-05-21");
+insert into diaries values(678, "2013-05-22");
+insert into diaries values(679, "2013-05-23");
+insert into diaries values(680, "2013-05-24");
+insert into diaries values(681, "2013-05-25");
+insert into diaries values(682, "2013-05-26");
+insert into diaries values(683, "2013-05-27");
+insert into diaries values(684, "2013-05-28");
+insert into diaries values(685, "2013-05-29");
+insert into diaries values(686, "2013-05-30");
+insert into diaries values(687, "2013-05-31");
+insert into diaries values(688, "2013-06-01");
+insert into diaries values(689, "2013-06-02");
+insert into diaries values(690, "2013-06-03");
+insert into diaries values(691, "2013-06-04");
+insert into diaries values(692, "2013-06-05");
+insert into diaries values(693, "2013-06-06");
+insert into diaries values(694, "2013-06-07");
+insert into diaries values(695, "2013-06-08");
+insert into diaries values(696, "2013-06-09");
+insert into diaries values(697, "2013-06-10");
+insert into diaries values(698, "2013-06-11");
+insert into diaries values(699, "2013-06-12");
+insert into diaries values(700, "2013-06-13");
+insert into diaries values(701, "2013-06-14");
+insert into diaries values(702, "2013-06-15");
+insert into diaries values(703, "2013-06-16");
+insert into diaries values(704, "2013-06-17");
+insert into diaries values(705, "2013-06-18");
+insert into diaries values(706, "2013-06-19");
+insert into diaries values(707, "2013-06-20");
+insert into diaries values(708, "2013-06-21");
+insert into diaries values(709, "2013-06-22");
+insert into diaries values(710, "2013-06-23");
+insert into diaries values(711, "2013-06-24");
+insert into diaries values(712, "2013-06-25");
+insert into diaries values(713, "2013-06-26");
+insert into diaries values(714, "2013-06-27");
+insert into diaries values(715, "2013-06-28");
+insert into diaries values(716, "2013-06-29");
+insert into diaries values(717, "2013-06-30");
+insert into diaries values(718, "2013-07-01");
+insert into diaries values(719, "2013-07-02");
+insert into diaries values(720, "2013-07-03");
+insert into diaries values(721, "2013-07-04");
+insert into diaries values(722, "2013-07-05");
+insert into diaries values(723, "2013-07-06");
+insert into diaries values(724, "2013-07-07");
+insert into diaries values(725, "2013-07-08");
+insert into diaries values(726, "2013-07-09");
+insert into diaries values(727, "2013-07-10");
+insert into diaries values(728, "2013-07-11");
+insert into diaries values(729, "2013-07-12");
+insert into diaries values(730, "2013-07-13");
+insert into diaries values(731, "2013-07-14");
+insert into diaries values(732, "2013-07-15");
+insert into diaries values(733, "2013-07-16");
+insert into diaries values(734, "2013-07-17");
+insert into diaries values(735, "2013-07-18");
+insert into diaries values(736, "2013-07-19");
+insert into diaries values(737, "2013-07-20");
+insert into diaries values(738, "2013-07-21");
+insert into diaries values(739, "2013-07-22");
+insert into diaries values(740, "2013-07-23");
+insert into diaries values(741, "2013-07-24");
+insert into diaries values(742, "2013-07-25");
+insert into diaries values(743, "2013-07-26");
+insert into diaries values(744, "2013-07-27");
+insert into diaries values(745, "2013-07-28");
+insert into diaries values(746, "2013-07-29");
+insert into diaries values(747, "2013-07-30");
+insert into diaries values(748, "2013-07-31");
+insert into diaries values(749, "2013-08-01");
+insert into diaries values(750, "2013-08-02");
+insert into diaries values(751, "2013-08-03");
+insert into diaries values(752, "2013-08-04");
+insert into diaries values(753, "2013-08-05");
+insert into diaries values(754, "2013-08-06");
+insert into diaries values(755, "2013-08-07");
+insert into diaries values(756, "2013-08-08");
+insert into diaries values(757, "2013-08-09");
+insert into diaries values(758, "2013-08-10");
+insert into diaries values(759, "2013-08-11");
+insert into diaries values(760, "2013-08-12");
+insert into diaries values(761, "2013-08-13");
+insert into diaries values(762, "2013-08-14");
+insert into diaries values(763, "2013-08-15");
+insert into diaries values(764, "2013-08-16");
+insert into diaries values(765, "2013-08-17");
+insert into diaries values(766, "2013-08-18");
+insert into diaries values(767, "2013-08-19");
+insert into diaries values(768, "2013-08-20");
+insert into diaries values(769, "2013-08-21");
+insert into diaries values(770, "2013-08-22");
+insert into diaries values(771, "2013-08-23");
+insert into diaries values(772, "2013-08-24");
+insert into diaries values(773, "2013-08-25");
+insert into diaries values(774, "2013-08-26");
+insert into diaries values(775, "2013-08-27");
+insert into diaries values(776, "2013-08-28");
+insert into diaries values(777, "2013-08-29");
+insert into diaries values(778, "2013-08-30");
+insert into diaries values(779, "2013-08-31");
+insert into diaries values(780, "2013-09-01");
+insert into diaries values(781, "2013-09-02");
+insert into diaries values(782, "2013-09-03");
+insert into diaries values(783, "2013-09-04");
+insert into diaries values(784, "2013-09-05");
+insert into diaries values(785, "2013-09-06");
+insert into diaries values(786, "2013-09-07");
+insert into diaries values(787, "2013-09-08");
+insert into diaries values(788, "2013-09-09");
+insert into diaries values(789, "2013-09-10");
+insert into diaries values(790, "2013-09-11");
+insert into diaries values(791, "2013-09-12");
+insert into diaries values(792, "2013-09-13");
+insert into diaries values(793, "2013-09-14");
+insert into diaries values(794, "2013-09-15");
+insert into diaries values(795, "2013-09-16");
+insert into diaries values(796, "2013-09-17");
+insert into diaries values(797, "2013-09-18");
+insert into diaries values(798, "2013-09-19");
+insert into diaries values(799, "2013-09-20");
+insert into diaries values(800, "2013-09-21");
+insert into diaries values(801, "2013-09-22");
+insert into diaries values(802, "2013-09-23");
+insert into diaries values(803, "2013-09-24");
+insert into diaries values(804, "2013-09-25");
+insert into diaries values(805, "2013-09-26");
+insert into diaries values(806, "2013-09-27");
+insert into diaries values(807, "2013-09-28");
+insert into diaries values(808, "2013-09-29");
+insert into diaries values(809, "2013-09-30");
+insert into diaries values(810, "2013-10-01");
+insert into diaries values(811, "2013-10-02");
+insert into diaries values(812, "2013-10-03");
+insert into diaries values(813, "2013-10-04");
+insert into diaries values(814, "2013-10-05");
+insert into diaries values(815, "2013-10-06");
+insert into diaries values(816, "2013-10-07");
+insert into diaries values(817, "2013-10-08");
+insert into diaries values(818, "2013-10-09");
+insert into diaries values(819, "2013-10-10");
+insert into diaries values(820, "2013-10-11");
+insert into diaries values(821, "2013-10-12");
+insert into diaries values(822, "2013-10-13");
+insert into diaries values(823, "2013-10-14");
+insert into diaries values(824, "2013-10-15");
+insert into diaries values(825, "2013-10-16");
+insert into diaries values(826, "2013-10-17");
+insert into diaries values(827, "2013-10-18");
+insert into diaries values(828, "2013-10-19");
+insert into diaries values(829, "2013-10-20");
+insert into diaries values(830, "2013-10-21");
+insert into diaries values(831, "2013-10-22");
+insert into diaries values(832, "2013-10-23");
+insert into diaries values(833, "2013-10-24");
+insert into diaries values(834, "2013-10-25");
+insert into diaries values(835, "2013-10-26");
+insert into diaries values(836, "2013-10-27");
+insert into diaries values(837, "2013-10-28");
+insert into diaries values(838, "2013-10-29");
+insert into diaries values(839, "2013-10-30");
+insert into diaries values(840, "2013-10-31");
+insert into diaries values(841, "2013-11-01");
+insert into diaries values(842, "2013-11-02");
+insert into diaries values(843, "2013-11-03");
+insert into diaries values(844, "2013-11-04");
+insert into diaries values(845, "2013-11-05");
+insert into diaries values(846, "2013-11-06");
+insert into diaries values(847, "2013-11-07");
+insert into diaries values(848, "2013-11-08");
+insert into diaries values(849, "2013-11-09");
+insert into diaries values(850, "2013-11-10");
+insert into diaries values(851, "2013-11-11");
+insert into diaries values(852, "2013-11-12");
+insert into diaries values(853, "2013-11-13");
+insert into diaries values(854, "2013-11-14");
+insert into diaries values(855, "2013-11-15");
+insert into diaries values(856, "2013-11-16");
+insert into diaries values(857, "2013-11-17");
+insert into diaries values(858, "2013-11-18");
+insert into diaries values(859, "2013-11-19");
+insert into diaries values(860, "2013-11-20");
+insert into diaries values(861, "2013-11-21");
+insert into diaries values(862, "2013-11-22");
+insert into diaries values(863, "2013-11-23");
+insert into diaries values(864, "2013-11-24");
+insert into diaries values(865, "2013-11-25");
+insert into diaries values(866, "2013-11-26");
+insert into diaries values(867, "2013-11-27");
+insert into diaries values(868, "2013-11-28");
+insert into diaries values(869, "2013-11-29");
+insert into diaries values(870, "2013-11-30");
+insert into diaries values(871, "2013-12-01");
+insert into diaries values(872, "2013-12-02");
+insert into diaries values(873, "2013-12-03");
+insert into diaries values(874, "2013-12-04");
+insert into diaries values(875, "2013-12-05");
+insert into diaries values(876, "2013-12-06");
+insert into diaries values(877, "2013-12-07");
+insert into diaries values(878, "2013-12-08");
+insert into diaries values(879, "2013-12-09");
+insert into diaries values(880, "2013-12-10");
+insert into diaries values(881, "2013-12-11");
+insert into diaries values(882, "2013-12-12");
+insert into diaries values(883, "2013-12-13");
+insert into diaries values(884, "2013-12-14");
+insert into diaries values(885, "2013-12-15");
+insert into diaries values(886, "2013-12-16");
+insert into diaries values(887, "2013-12-17");
+insert into diaries values(888, "2013-12-18");
+insert into diaries values(889, "2013-12-19");
+insert into diaries values(890, "2013-12-20");
+insert into diaries values(891, "2013-12-21");
+insert into diaries values(892, "2013-12-22");
+insert into diaries values(893, "2013-12-23");
+insert into diaries values(894, "2013-12-24");
+insert into diaries values(895, "2013-12-25");
+insert into diaries values(896, "2013-12-26");
+insert into diaries values(897, "2013-12-27");
+insert into diaries values(898, "2013-12-28");
+insert into diaries values(899, "2013-12-29");
+insert into diaries values(900, "2013-12-30");
+insert into diaries values(901, "2013-12-31");
+insert into diaries values(902, "2014-01-01");
+insert into diaries values(903, "2014-01-02");
+insert into diaries values(904, "2014-01-03");
+insert into diaries values(905, "2014-01-04");
+insert into diaries values(906, "2014-01-05");
+insert into diaries values(907, "2014-01-06");
+insert into diaries values(908, "2014-01-07");
+insert into diaries values(909, "2014-01-08");
+insert into diaries values(910, "2014-01-09");
+insert into diaries values(911, "2014-01-10");
+insert into diaries values(912, "2014-01-11");
+insert into diaries values(913, "2014-01-12");
+insert into diaries values(914, "2014-01-13");
+insert into diaries values(915, "2014-01-14");
+insert into diaries values(916, "2014-01-15");
+insert into diaries values(917, "2014-01-16");
+insert into diaries values(918, "2014-01-17");
+insert into diaries values(919, "2014-01-18");
+insert into diaries values(920, "2014-01-19");
+insert into diaries values(921, "2014-01-20");
+insert into diaries values(922, "2014-01-21");
+insert into diaries values(923, "2014-01-22");
+insert into diaries values(924, "2014-01-23");
+insert into diaries values(925, "2014-01-24");
+insert into diaries values(926, "2014-01-25");
+insert into diaries values(927, "2014-01-26");
+insert into diaries values(928, "2014-01-27");
+insert into diaries values(929, "2014-01-28");
+insert into diaries values(930, "2014-01-29");
+insert into diaries values(931, "2014-01-30");
+insert into diaries values(932, "2014-01-31");
+insert into diaries values(933, "2014-02-01");
+insert into diaries values(934, "2014-02-02");
+insert into diaries values(935, "2014-02-03");
+insert into diaries values(936, "2014-02-04");
+insert into diaries values(937, "2014-02-05");
+insert into diaries values(938, "2014-02-06");
+insert into diaries values(939, "2014-02-07");
+insert into diaries values(940, "2014-02-08");
+insert into diaries values(941, "2014-02-09");
+insert into diaries values(942, "2014-02-10");
+insert into diaries values(943, "2014-02-11");
+insert into diaries values(944, "2014-02-12");
+insert into diaries values(945, "2014-02-13");
+insert into diaries values(946, "2014-02-14");
+insert into diaries values(947, "2014-02-15");
+insert into diaries values(948, "2014-02-16");
+insert into diaries values(949, "2014-02-17");
+insert into diaries values(950, "2014-02-18");
+insert into diaries values(951, "2014-02-19");
+insert into diaries values(952, "2014-02-20");
+insert into diaries values(953, "2014-02-21");
+insert into diaries values(954, "2014-02-22");
+insert into diaries values(955, "2014-02-23");
+insert into diaries values(956, "2014-02-24");
+insert into diaries values(957, "2014-02-25");
+insert into diaries values(958, "2014-02-26");
+insert into diaries values(959, "2014-02-27");
+insert into diaries values(960, "2014-02-28");
+insert into diaries values(961, "2014-03-01");
+insert into diaries values(962, "2014-03-02");
+insert into diaries values(963, "2014-03-03");
+insert into diaries values(964, "2014-03-04");
+insert into diaries values(965, "2014-03-05");
+insert into diaries values(966, "2014-03-06");
+insert into diaries values(967, "2014-03-07");
+insert into diaries values(968, "2014-03-08");
+insert into diaries values(969, "2014-03-09");
+insert into diaries values(970, "2014-03-10");
+insert into diaries values(971, "2014-03-11");
+insert into diaries values(972, "2014-03-12");
+insert into diaries values(973, "2014-03-13");
+insert into diaries values(974, "2014-03-14");
+insert into diaries values(975, "2014-03-15");
+insert into diaries values(976, "2014-03-16");
+insert into diaries values(977, "2014-03-17");
+insert into diaries values(978, "2014-03-18");
+insert into diaries values(979, "2014-03-19");
+insert into diaries values(980, "2014-03-20");
+insert into diaries values(981, "2014-03-21");
+insert into diaries values(982, "2014-03-22");
+insert into diaries values(983, "2014-03-23");
+insert into diaries values(984, "2014-03-24");
+insert into diaries values(985, "2014-03-25");
+insert into diaries values(986, "2014-03-26");
+insert into diaries values(987, "2014-03-27");
+insert into diaries values(988, "2014-03-28");
+insert into diaries values(989, "2014-03-29");
+insert into diaries values(990, "2014-03-30");
+insert into diaries values(991, "2014-03-31");
+insert into diaries values(992, "2014-04-01");
+insert into diaries values(993, "2014-04-02");
+insert into diaries values(994, "2014-04-03");
+insert into diaries values(995, "2014-04-04");
+insert into diaries values(996, "2014-04-05");
+insert into diaries values(997, "2014-04-06");
+insert into diaries values(998, "2014-04-07");
+insert into diaries values(999, "2014-04-08");
+insert into diaries values(1000, "2014-04-09");
+insert into diaries values(1001, "2014-04-10");
+insert into diaries values(1002, "2014-04-11");
+insert into diaries values(1003, "2014-04-12");
+insert into diaries values(1004, "2014-04-13");
+insert into diaries values(1005, "2014-04-14");
+insert into diaries values(1006, "2014-04-15");
+insert into diaries values(1007, "2014-04-16");
+insert into diaries values(1008, "2014-04-17");
+insert into diaries values(1009, "2014-04-18");
+insert into diaries values(1010, "2014-04-19");
+insert into diaries values(1011, "2014-04-20");
+insert into diaries values(1012, "2014-04-21");
+insert into diaries values(1013, "2014-04-22");
+insert into diaries values(1014, "2014-04-23");
+insert into diaries values(1015, "2014-04-24");
+insert into diaries values(1016, "2014-04-25");
+insert into diaries values(1017, "2014-04-26");
+insert into diaries values(1018, "2014-04-27");
+insert into diaries values(1019, "2014-04-28");
+insert into diaries values(1020, "2014-04-29");
+insert into diaries values(1021, "2014-04-30");
+insert into diaries values(1022, "2014-05-01");
+insert into diaries values(1023, "2014-05-02");
+insert into diaries values(1024, "2014-05-03");
+insert into diaries values(1025, "2014-05-04");
+insert into diaries values(1026, "2014-05-05");
+insert into diaries values(1027, "2014-05-06");
+insert into diaries values(1028, "2014-05-07");
+insert into diaries values(1029, "2014-05-08");
+insert into diaries values(1030, "2014-05-09");
+insert into diaries values(1031, "2014-05-10");
+insert into diaries values(1032, "2014-05-11");
+insert into diaries values(1033, "2014-05-12");
+insert into diaries values(1034, "2014-05-13");
+insert into diaries values(1035, "2014-05-14");
+insert into diaries values(1036, "2014-05-15");
+insert into diaries values(1037, "2014-05-16");
+insert into diaries values(1038, "2014-05-17");
+insert into diaries values(1039, "2014-05-18");
+insert into diaries values(1040, "2014-05-19");
+insert into diaries values(1041, "2014-05-20");
+insert into diaries values(1042, "2014-05-21");
+insert into diaries values(1043, "2014-05-22");
+insert into diaries values(1044, "2014-05-23");
+insert into diaries values(1045, "2014-05-24");
+insert into diaries values(1046, "2014-05-25");
+insert into diaries values(1047, "2014-05-26");
+insert into diaries values(1048, "2014-05-27");
+insert into diaries values(1049, "2014-05-28");
+insert into diaries values(1050, "2014-05-29");
+insert into diaries values(1051, "2014-05-30");
+insert into diaries values(1052, "2014-05-31");
+insert into diaries values(1053, "2014-06-01");
+insert into diaries values(1054, "2014-06-02");
+insert into diaries values(1055, "2014-06-03");
+insert into diaries values(1056, "2014-06-04");
+insert into diaries values(1057, "2014-06-05");
+insert into diaries values(1058, "2014-06-06");
+insert into diaries values(1059, "2014-06-07");
+insert into diaries values(1060, "2014-06-08");
+insert into diaries values(1061, "2014-06-09");
+insert into diaries values(1062, "2014-06-10");
+insert into diaries values(1063, "2014-06-11");
+insert into diaries values(1064, "2014-06-12");
+insert into diaries values(1065, "2014-06-13");
+insert into diaries values(1066, "2014-06-14");
+insert into diaries values(1067, "2014-06-15");
+insert into diaries values(1068, "2014-06-16");
+insert into diaries values(1069, "2014-06-17");
+insert into diaries values(1070, "2014-06-18");
+insert into diaries values(1071, "2014-06-19");
+insert into diaries values(1072, "2014-06-20");
+insert into diaries values(1073, "2014-06-21");
+insert into diaries values(1074, "2014-06-22");
+insert into diaries values(1075, "2014-06-23");
+insert into diaries values(1076, "2014-06-24");
+insert into diaries values(1077, "2014-06-25");
+insert into diaries values(1078, "2014-06-26");
+insert into diaries values(1079, "2014-06-27");
+insert into diaries values(1080, "2014-06-28");
+insert into diaries values(1081, "2014-06-29");
+insert into diaries values(1082, "2014-06-30");
+insert into diaries values(1083, "2014-07-01");
+insert into diaries values(1084, "2014-07-02");
+insert into diaries values(1085, "2014-07-03");
+insert into diaries values(1086, "2014-07-04");
+insert into diaries values(1087, "2014-07-05");
+insert into diaries values(1088, "2014-07-06");
+insert into diaries values(1089, "2014-07-07");
+insert into diaries values(1090, "2014-07-08");
+insert into diaries values(1091, "2014-07-09");
+insert into diaries values(1092, "2014-07-10");
+insert into diaries values(1093, "2014-07-11");
+insert into diaries values(1094, "2014-07-12");
+insert into diaries values(1095, "2014-07-13");
+insert into diaries values(1096, "2014-07-14");
+insert into diaries values(1097, "2014-07-15");
+insert into diaries values(1098, "2014-07-16");
+insert into diaries values(1099, "2014-07-17");
+insert into diaries values(1100, "2014-07-18");
+insert into diaries values(1101, "2014-07-19");
+insert into diaries values(1102, "2014-07-20");
+insert into diaries values(1103, "2014-07-21");
+insert into diaries values(1104, "2014-07-22");
+insert into diaries values(1105, "2014-07-23");
+insert into diaries values(1106, "2014-07-24");
+insert into diaries values(1107, "2014-07-25");
+insert into diaries values(1108, "2014-07-26");
+insert into diaries values(1109, "2014-07-27");
+insert into diaries values(1110, "2014-07-28");
+insert into diaries values(1111, "2014-07-29");
+insert into diaries values(1112, "2014-07-30");
+insert into diaries values(1113, "2014-07-31");
+insert into diaries values(1114, "2014-08-01");
+insert into diaries values(1115, "2014-08-02");
+insert into diaries values(1116, "2014-08-03");
+insert into diaries values(1117, "2014-08-04");
+insert into diaries values(1118, "2014-08-05");
+insert into diaries values(1119, "2014-08-06");
+insert into diaries values(1120, "2014-08-07");
+insert into diaries values(1121, "2014-08-08");
+insert into diaries values(1122, "2014-08-09");
+insert into diaries values(1123, "2014-08-10");
+insert into diaries values(1124, "2014-08-11");
+insert into diaries values(1125, "2014-08-12");
+insert into diaries values(1126, "2014-08-13");
+insert into diaries values(1127, "2014-08-14");
+insert into diaries values(1128, "2014-08-15");
+insert into diaries values(1129, "2014-08-16");
+insert into diaries values(1130, "2014-08-17");
+insert into diaries values(1131, "2014-08-18");
+insert into diaries values(1132, "2014-08-19");
+insert into diaries values(1133, "2014-08-20");
+insert into diaries values(1134, "2014-08-21");
+insert into diaries values(1135, "2014-08-22");
+insert into diaries values(1136, "2014-08-23");
+insert into diaries values(1137, "2014-08-24");
+insert into diaries values(1138, "2014-08-25");
+insert into diaries values(1139, "2014-08-26");
+insert into diaries values(1140, "2014-08-27");
+insert into diaries values(1141, "2014-08-28");
+insert into diaries values(1142, "2014-08-29");
+insert into diaries values(1143, "2014-08-30");
+insert into diaries values(1144, "2014-08-31");
+insert into diaries values(1145, "2014-09-01");
+insert into diaries values(1146, "2014-09-02");
+insert into diaries values(1147, "2014-09-03");
+insert into diaries values(1148, "2014-09-04");
+insert into diaries values(1149, "2014-09-05");
+insert into diaries values(1150, "2014-09-06");
+insert into diaries values(1151, "2014-09-07");
+insert into diaries values(1152, "2014-09-08");
+insert into diaries values(1153, "2014-09-09");
+insert into diaries values(1154, "2014-09-10");
+insert into diaries values(1155, "2014-09-11");
+insert into diaries values(1156, "2014-09-12");
+insert into diaries values(1157, "2014-09-13");
+insert into diaries values(1158, "2014-09-14");
+insert into diaries values(1159, "2014-09-15");
+insert into diaries values(1160, "2014-09-16");
+insert into diaries values(1161, "2014-09-17");
+insert into diaries values(1162, "2014-09-18");
+insert into diaries values(1163, "2014-09-19");
+insert into diaries values(1164, "2014-09-20");
+insert into diaries values(1165, "2014-09-21");
+insert into diaries values(1166, "2014-09-22");
+insert into diaries values(1167, "2014-09-23");
+insert into diaries values(1168, "2014-09-24");
+insert into diaries values(1169, "2014-09-25");
+insert into diaries values(1170, "2014-09-26");
+insert into diaries values(1171, "2014-09-27");
+insert into diaries values(1172, "2014-09-28");
+insert into diaries values(1173, "2014-09-29");
+insert into diaries values(1174, "2014-09-30");
+insert into diaries values(1175, "2014-10-01");
+insert into diaries values(1176, "2014-10-02");
+insert into diaries values(1177, "2014-10-03");
+insert into diaries values(1178, "2014-10-04");
+insert into diaries values(1179, "2014-10-05");
+insert into diaries values(1180, "2014-10-06");
+insert into diaries values(1181, "2014-10-07");
+insert into diaries values(1182, "2014-10-08");
+insert into diaries values(1183, "2014-10-09");
+insert into diaries values(1184, "2014-10-10");
+insert into diaries values(1185, "2014-10-11");
+insert into diaries values(1186, "2014-10-12");
+insert into diaries values(1187, "2014-10-13");
+insert into diaries values(1188, "2014-10-14");
+insert into diaries values(1189, "2014-10-15");
+insert into diaries values(1190, "2014-10-16");
+insert into diaries values(1191, "2014-10-17");
+insert into diaries values(1192, "2014-10-18");
+insert into diaries values(1193, "2014-10-19");
+insert into diaries values(1194, "2014-10-20");
+insert into diaries values(1195, "2014-10-21");
+insert into diaries values(1196, "2014-10-22");
+insert into diaries values(1197, "2014-10-23");
+insert into diaries values(1198, "2014-10-24");
+insert into diaries values(1199, "2014-10-25");
+insert into diaries values(1200, "2014-10-26");
+insert into diaries values(1201, "2014-10-27");
+insert into diaries values(1202, "2014-10-28");
+insert into diaries values(1203, "2014-10-29");
+insert into diaries values(1204, "2014-10-30");
+insert into diaries values(1205, "2014-10-31");
+insert into diaries values(1206, "2014-11-01");
+insert into diaries values(1207, "2014-11-02");
+insert into diaries values(1208, "2014-11-03");
+insert into diaries values(1209, "2014-11-04");
+insert into diaries values(1210, "2014-11-05");
+insert into diaries values(1211, "2014-11-06");
+insert into diaries values(1212, "2014-11-07");
+insert into diaries values(1213, "2014-11-08");
+insert into diaries values(1214, "2014-11-09");
+insert into diaries values(1215, "2014-11-10");
+insert into diaries values(1216, "2014-11-11");
+insert into diaries values(1217, "2014-11-12");
+insert into diaries values(1218, "2014-11-13");
+insert into diaries values(1219, "2014-11-14");
+insert into diaries values(1220, "2014-11-15");
+insert into diaries values(1221, "2014-11-16");
+insert into diaries values(1222, "2014-11-17");
+insert into diaries values(1223, "2014-11-18");
+insert into diaries values(1224, "2014-11-19");
+insert into diaries values(1225, "2014-11-20");
+insert into diaries values(1226, "2014-11-21");
+insert into diaries values(1227, "2014-11-22");
+insert into diaries values(1228, "2014-11-23");
+insert into diaries values(1229, "2014-11-24");
+insert into diaries values(1230, "2014-11-25");
+insert into diaries values(1231, "2014-11-26");
+insert into diaries values(1232, "2014-11-27");
+insert into diaries values(1233, "2014-11-28");
+insert into diaries values(1234, "2014-11-29");
+insert into diaries values(1235, "2014-11-30");
+insert into diaries values(1236, "2014-12-01");
+insert into diaries values(1237, "2014-12-02");
+insert into diaries values(1238, "2014-12-03");
+insert into diaries values(1239, "2014-12-04");
+insert into diaries values(1240, "2014-12-05");
+insert into diaries values(1241, "2014-12-06");
+insert into diaries values(1242, "2014-12-07");
+insert into diaries values(1243, "2014-12-08");
+insert into diaries values(1244, "2014-12-09");
+insert into diaries values(1245, "2014-12-10");
+insert into diaries values(1246, "2014-12-11");
+insert into diaries values(1247, "2014-12-12");
+insert into diaries values(1248, "2014-12-13");
+insert into diaries values(1249, "2014-12-14");
+insert into diaries values(1250, "2014-12-15");
+insert into diaries values(1251, "2014-12-16");
+insert into diaries values(1252, "2014-12-17");
+insert into diaries values(1253, "2014-12-18");
+insert into diaries values(1254, "2014-12-19");
+insert into diaries values(1255, "2014-12-20");
+insert into diaries values(1256, "2014-12-21");
+insert into diaries values(1257, "2014-12-22");
+insert into diaries values(1258, "2014-12-23");
+insert into diaries values(1259, "2014-12-24");
+insert into diaries values(1260, "2014-12-25");
+insert into diaries values(1261, "2014-12-26");
+insert into diaries values(1262, "2014-12-27");
+insert into diaries values(1263, "2014-12-28");
+insert into diaries values(1264, "2014-12-29");
+insert into diaries values(1265, "2014-12-30");
+insert into diaries values(1266, "2014-12-31");
+insert into diaries values(1267, "2015-01-01");
+insert into diaries values(1268, "2015-01-02");
+insert into diaries values(1269, "2015-01-03");
+insert into diaries values(1270, "2015-01-04");
+insert into diaries values(1271, "2015-01-05");
+insert into diaries values(1272, "2015-01-06");
+insert into diaries values(1273, "2015-01-07");
+insert into diaries values(1274, "2015-01-08");
+insert into diaries values(1275, "2015-01-09");
+insert into diaries values(1276, "2015-01-10");
+insert into diaries values(1277, "2015-01-11");
+insert into diaries values(1278, "2015-01-12");
+insert into diaries values(1279, "2015-01-13");
+insert into diaries values(1280, "2015-01-14");
+insert into diaries values(1281, "2015-01-15");
+insert into diaries values(1282, "2015-01-16");
+insert into diaries values(1283, "2015-01-17");
+insert into diaries values(1284, "2015-01-18");
+insert into diaries values(1285, "2015-01-19");
+insert into diaries values(1286, "2015-01-20");
+insert into diaries values(1287, "2015-01-21");
+insert into diaries values(1288, "2015-01-22");
+insert into diaries values(1289, "2015-01-23");
+insert into diaries values(1290, "2015-01-24");
+insert into diaries values(1291, "2015-01-25");
+insert into diaries values(1292, "2015-01-26");
+insert into diaries values(1293, "2015-01-27");
+insert into diaries values(1294, "2015-01-28");
+insert into diaries values(1295, "2015-01-29");
+insert into diaries values(1296, "2015-01-30");
+insert into diaries values(1297, "2015-01-31");
+insert into diaries values(1298, "2015-02-01");
+insert into diaries values(1299, "2015-02-02");
+insert into diaries values(1300, "2015-02-03");
+insert into diaries values(1301, "2015-02-04");
+insert into diaries values(1302, "2015-02-05");
+insert into diaries values(1303, "2015-02-06");
+insert into diaries values(1304, "2015-02-07");
+insert into diaries values(1305, "2015-02-08");
+insert into diaries values(1306, "2015-02-09");
+insert into diaries values(1307, "2015-02-10");
+insert into diaries values(1308, "2015-02-11");
+insert into diaries values(1309, "2015-02-12");
+insert into diaries values(1310, "2015-02-13");
+insert into diaries values(1311, "2015-02-14");
+insert into diaries values(1312, "2015-02-15");
+insert into diaries values(1313, "2015-02-16");
+insert into diaries values(1314, "2015-02-17");
+insert into diaries values(1315, "2015-02-18");
+insert into diaries values(1316, "2015-02-19");
+insert into diaries values(1317, "2015-02-20");
+insert into diaries values(1318, "2015-02-21");
+insert into diaries values(1319, "2015-02-22");
+insert into diaries values(1320, "2015-02-23");
+insert into diaries values(1321, "2015-02-24");
+insert into diaries values(1322, "2015-02-25");
+insert into diaries values(1323, "2015-02-26");
+insert into diaries values(1324, "2015-02-27");
+insert into diaries values(1325, "2015-02-28");
+insert into diaries values(1326, "2015-03-01");
+insert into diaries values(1327, "2015-03-02");
+insert into diaries values(1328, "2015-03-03");
+insert into diaries values(1329, "2015-03-04");
+insert into diaries values(1330, "2015-03-05");
+insert into diaries values(1331, "2015-03-06");
+insert into diaries values(1332, "2015-03-07");
+insert into diaries values(1333, "2015-03-08");
+insert into diaries values(1334, "2015-03-09");
+insert into diaries values(1335, "2015-03-10");
+insert into diaries values(1336, "2015-03-11");
+insert into diaries values(1337, "2015-03-12");
+insert into diaries values(1338, "2015-03-13");
+insert into diaries values(1339, "2015-03-14");
+insert into diaries values(1340, "2015-03-15");
+insert into diaries values(1341, "2015-03-16");
+insert into diaries values(1342, "2015-03-17");
+insert into diaries values(1343, "2015-03-18");
+insert into diaries values(1344, "2015-03-19");
+insert into diaries values(1345, "2015-03-20");
+insert into diaries values(1346, "2015-03-21");
+insert into diaries values(1347, "2015-03-22");
+insert into diaries values(1348, "2015-03-23");
+insert into diaries values(1349, "2015-03-24");
+insert into diaries values(1350, "2015-03-25");
+insert into diaries values(1351, "2015-03-26");
+insert into diaries values(1352, "2015-03-27");
+insert into diaries values(1353, "2015-03-28");
+insert into diaries values(1354, "2015-03-29");
+insert into diaries values(1355, "2015-03-30");
+insert into diaries values(1356, "2015-03-31");
+insert into diaries values(1357, "2015-04-01");
+insert into diaries values(1358, "2015-04-02");
+insert into diaries values(1359, "2015-04-03");
+insert into diaries values(1360, "2015-04-04");
+insert into diaries values(1361, "2015-04-05");
+insert into diaries values(1362, "2015-04-06");
+insert into diaries values(1363, "2015-04-07");
+insert into diaries values(1364, "2015-04-08");
+insert into diaries values(1365, "2015-04-09");
+insert into diaries values(1366, "2015-04-10");
+insert into diaries values(1367, "2015-04-11");
+insert into diaries values(1368, "2015-04-12");
+insert into diaries values(1369, "2015-04-13");
+insert into diaries values(1370, "2015-04-14");
+insert into diaries values(1371, "2015-04-15");
+insert into diaries values(1372, "2015-04-16");
+insert into diaries values(1373, "2015-04-17");
+insert into diaries values(1374, "2015-04-18");
+insert into diaries values(1375, "2015-04-19");
+insert into diaries values(1376, "2015-04-20");
+insert into diaries values(1377, "2015-04-21");
+insert into diaries values(1378, "2015-04-22");
+insert into diaries values(1379, "2015-04-23");
+insert into diaries values(1380, "2015-04-24");
+insert into diaries values(1381, "2015-04-25");
+insert into diaries values(1382, "2015-04-26");
+insert into diaries values(1383, "2015-04-27");
+insert into diaries values(1384, "2015-04-28");
+insert into diaries values(1385, "2015-04-29");
+insert into diaries values(1386, "2015-04-30");
+insert into diaries values(1387, "2015-05-01");
+insert into diaries values(1388, "2015-05-02");
+insert into diaries values(1389, "2015-05-03");
+insert into diaries values(1390, "2015-05-04");
+insert into diaries values(1391, "2015-05-05");
+insert into diaries values(1392, "2015-05-06");
+insert into diaries values(1393, "2015-05-07");
+insert into diaries values(1394, "2015-05-08");
+insert into diaries values(1395, "2015-05-09");
+insert into diaries values(1396, "2015-05-10");
+insert into diaries values(1397, "2015-05-11");
+insert into diaries values(1398, "2015-05-12");
+insert into diaries values(1399, "2015-05-13");
+insert into diaries values(1400, "2015-05-14");
+insert into diaries values(1401, "2015-05-15");
+insert into diaries values(1402, "2015-05-16");
+insert into diaries values(1403, "2015-05-17");
+insert into diaries values(1404, "2015-05-18");
+insert into diaries values(1405, "2015-05-19");
+insert into diaries values(1406, "2015-05-20");
+insert into diaries values(1407, "2015-05-21");
+insert into diaries values(1408, "2015-05-22");
+insert into diaries values(1409, "2015-05-23");
+insert into diaries values(1410, "2015-05-24");
+insert into diaries values(1411, "2015-05-25");
+insert into diaries values(1412, "2015-05-26");
+insert into diaries values(1413, "2015-05-27");
+insert into diaries values(1414, "2015-05-28");
+insert into diaries values(1415, "2015-05-29");
+insert into diaries values(1416, "2015-05-30");
+insert into diaries values(1417, "2015-05-31");
+insert into diaries values(1418, "2015-06-01");
+insert into diaries values(1419, "2015-06-02");
+insert into diaries values(1420, "2015-06-03");
+insert into diaries values(1421, "2015-06-04");
+insert into diaries values(1422, "2015-06-05");
+insert into diaries values(1423, "2015-06-06");
+insert into diaries values(1424, "2015-06-07");
+insert into diaries values(1425, "2015-06-08");
+insert into diaries values(1426, "2015-06-09");
+insert into diaries values(1427, "2015-06-10");
+insert into diaries values(1428, "2015-06-11");
+insert into diaries values(1429, "2015-06-12");
+insert into diaries values(1430, "2015-06-13");
+insert into diaries values(1431, "2015-06-14");
+insert into diaries values(1432, "2015-06-15");
+insert into diaries values(1433, "2015-06-16");
+insert into diaries values(1434, "2015-06-17");
+insert into diaries values(1435, "2015-06-18");
+insert into diaries values(1436, "2015-06-19");
+insert into diaries values(1437, "2015-06-20");
+insert into diaries values(1438, "2015-06-21");
+insert into diaries values(1439, "2015-06-22");
+insert into diaries values(1440, "2015-06-23");
+insert into diaries values(1441, "2015-06-24");
+insert into diaries values(1442, "2015-06-25");
+insert into diaries values(1443, "2015-06-26");
+insert into diaries values(1444, "2015-06-27");
+insert into diaries values(1445, "2015-06-28");
+insert into diaries values(1446, "2015-06-29");
+insert into diaries values(1447, "2015-06-30");
+insert into diaries values(1448, "2015-07-01");
+insert into diaries values(1449, "2015-07-02");
+insert into diaries values(1450, "2015-07-03");
+insert into diaries values(1451, "2015-07-04");
+insert into diaries values(1452, "2015-07-05");
+insert into diaries values(1453, "2015-07-06");
+insert into diaries values(1454, "2015-07-07");
+insert into diaries values(1455, "2015-07-08");
+insert into diaries values(1456, "2015-07-09");
+insert into diaries values(1457, "2015-07-10");
+insert into diaries values(1458, "2015-07-11");
+insert into diaries values(1459, "2015-07-12");
+insert into diaries values(1460, "2015-07-13");
+insert into diaries values(1461, "2015-07-14");
+insert into diaries values(1462, "2015-07-15");
+insert into diaries values(1463, "2015-07-16");
+insert into diaries values(1464, "2015-07-17");
+insert into diaries values(1465, "2015-07-18");
+insert into diaries values(1466, "2015-07-19");
+insert into diaries values(1467, "2015-07-20");
+insert into diaries values(1468, "2015-07-21");
+insert into diaries values(1469, "2015-07-22");
+insert into diaries values(1470, "2015-07-23");
+insert into diaries values(1471, "2015-07-24");
+insert into diaries values(1472, "2015-07-25");
+insert into diaries values(1473, "2015-07-26");
+insert into diaries values(1474, "2015-07-27");
+insert into diaries values(1475, "2015-07-28");
+insert into diaries values(1476, "2015-07-29");
+insert into diaries values(1477, "2015-07-30");
+insert into diaries values(1478, "2015-07-31");
+insert into diaries values(1479, "2015-08-01");
+insert into diaries values(1480, "2015-08-02");
+insert into diaries values(1481, "2015-08-03");
+insert into diaries values(1482, "2015-08-04");
+insert into diaries values(1483, "2015-08-05");
+insert into diaries values(1484, "2015-08-06");
+insert into diaries values(1485, "2015-08-07");
+insert into diaries values(1486, "2015-08-08");
+insert into diaries values(1487, "2015-08-09");
+insert into diaries values(1488, "2015-08-10");
+insert into diaries values(1489, "2015-08-11");
+insert into diaries values(1490, "2015-08-12");
+insert into diaries values(1491, "2015-08-13");
+insert into diaries values(1492, "2015-08-14");
+insert into diaries values(1493, "2015-08-15");
+insert into diaries values(1494, "2015-08-16");
+insert into diaries values(1495, "2015-08-17");
+insert into diaries values(1496, "2015-08-18");
+insert into diaries values(1497, "2015-08-19");
+insert into diaries values(1498, "2015-08-20");
+insert into diaries values(1499, "2015-08-21");
+insert into diaries values(1500, "2015-08-22");
+insert into diaries values(1501, "2015-08-23");
+insert into diaries values(1502, "2015-08-24");
+insert into diaries values(1503, "2015-08-25");
+insert into diaries values(1504, "2015-08-26");
+insert into diaries values(1505, "2015-08-27");
+insert into diaries values(1506, "2015-08-28");
+insert into diaries values(1507, "2015-08-29");
+insert into diaries values(1508, "2015-08-30");
+insert into diaries values(1509, "2015-08-31");
+insert into diaries values(1510, "2015-09-01");
+insert into diaries values(1511, "2015-09-02");
+insert into diaries values(1512, "2015-09-03");
+insert into diaries values(1513, "2015-09-04");
+insert into diaries values(1514, "2015-09-05");
+insert into diaries values(1515, "2015-09-06");
+insert into diaries values(1516, "2015-09-07");
+insert into diaries values(1517, "2015-09-08");
+insert into diaries values(1518, "2015-09-09");
+insert into diaries values(1519, "2015-09-10");
+insert into diaries values(1520, "2015-09-11");
+insert into diaries values(1521, "2015-09-12");
+insert into diaries values(1522, "2015-09-13");
+insert into diaries values(1523, "2015-09-14");
+insert into diaries values(1524, "2015-09-15");
+insert into diaries values(1525, "2015-09-16");
+insert into diaries values(1526, "2015-09-17");
+insert into diaries values(1527, "2015-09-18");
+insert into diaries values(1528, "2015-09-19");
+insert into diaries values(1529, "2015-09-20");
+insert into diaries values(1530, "2015-09-21");
+insert into diaries values(1531, "2015-09-22");
+insert into diaries values(1532, "2015-09-23");
+insert into diaries values(1533, "2015-09-24");
+insert into diaries values(1534, "2015-09-25");
+insert into diaries values(1535, "2015-09-26");
+insert into diaries values(1536, "2015-09-27");
+insert into diaries values(1537, "2015-09-28");
+insert into diaries values(1538, "2015-09-29");
+insert into diaries values(1539, "2015-09-30");
+insert into diaries values(1540, "2015-10-01");
+insert into diaries values(1541, "2015-10-02");
+insert into diaries values(1542, "2015-10-03");
+insert into diaries values(1543, "2015-10-04");
+insert into diaries values(1544, "2015-10-05");
+insert into diaries values(1545, "2015-10-06");
+insert into diaries values(1546, "2015-10-07");
+insert into diaries values(1547, "2015-10-08");
+insert into diaries values(1548, "2015-10-09");
+insert into diaries values(1549, "2015-10-10");
+insert into diaries values(1550, "2015-10-11");
+insert into diaries values(1551, "2015-10-12");
+insert into diaries values(1552, "2015-10-13");
+insert into diaries values(1553, "2015-10-14");
+insert into diaries values(1554, "2015-10-15");
+insert into diaries values(1555, "2015-10-16");
+insert into diaries values(1556, "2015-10-17");
+insert into diaries values(1557, "2015-10-18");
+insert into diaries values(1558, "2015-10-19");
+insert into diaries values(1559, "2015-10-20");
+insert into diaries values(1560, "2015-10-21");
+insert into diaries values(1561, "2015-10-22");
+insert into diaries values(1562, "2015-10-23");
+insert into diaries values(1563, "2015-10-24");
+insert into diaries values(1564, "2015-10-25");
+insert into diaries values(1565, "2015-10-26");
+insert into diaries values(1566, "2015-10-27");
+insert into diaries values(1567, "2015-10-28");
+insert into diaries values(1568, "2015-10-29");
+insert into diaries values(1569, "2015-10-30");
+insert into diaries values(1570, "2015-10-31");
+insert into diaries values(1571, "2015-11-01");
+insert into diaries values(1572, "2015-11-02");
+insert into diaries values(1573, "2015-11-03");
+insert into diaries values(1574, "2015-11-04");
+insert into diaries values(1575, "2015-11-05");
+insert into diaries values(1576, "2015-11-06");
+insert into diaries values(1577, "2015-11-07");
+insert into diaries values(1578, "2015-11-08");
+insert into diaries values(1579, "2015-11-09");
+insert into diaries values(1580, "2015-11-10");
+insert into diaries values(1581, "2015-11-11");
+insert into diaries values(1582, "2015-11-12");
+insert into diaries values(1583, "2015-11-13");
+insert into diaries values(1584, "2015-11-14");
+insert into diaries values(1585, "2015-11-15");
+insert into diaries values(1586, "2015-11-16");
+insert into diaries values(1587, "2015-11-17");
+insert into diaries values(1588, "2015-11-18");
+insert into diaries values(1589, "2015-11-19");
+insert into diaries values(1590, "2015-11-20");
+insert into diaries values(1591, "2015-11-21");
+insert into diaries values(1592, "2015-11-22");
+insert into diaries values(1593, "2015-11-23");
+insert into diaries values(1594, "2015-11-24");
+insert into diaries values(1595, "2015-11-25");
+insert into diaries values(1596, "2015-11-26");
+insert into diaries values(1597, "2015-11-27");
+insert into diaries values(1598, "2015-11-28");
+insert into diaries values(1599, "2015-11-29");
+insert into diaries values(1600, "2015-11-30");
+insert into diaries values(1601, "2015-12-01");
+insert into diaries values(1602, "2015-12-02");
+insert into diaries values(1603, "2015-12-03");
+insert into diaries values(1604, "2015-12-04");
+insert into diaries values(1605, "2015-12-05");
+insert into diaries values(1606, "2015-12-06");
+insert into diaries values(1607, "2015-12-07");
+insert into diaries values(1608, "2015-12-08");
+insert into diaries values(1609, "2015-12-09");
+insert into diaries values(1610, "2015-12-10");
+insert into diaries values(1611, "2015-12-11");
+insert into diaries values(1612, "2015-12-12");
+insert into diaries values(1613, "2015-12-13");
+insert into diaries values(1614, "2015-12-14");
+insert into diaries values(1615, "2015-12-15");
+insert into diaries values(1616, "2015-12-16");
+insert into diaries values(1617, "2015-12-17");
+insert into diaries values(1618, "2015-12-18");
+insert into diaries values(1619, "2015-12-19");
+insert into diaries values(1620, "2015-12-20");
+insert into diaries values(1621, "2015-12-21");
+insert into diaries values(1622, "2015-12-22");
+insert into diaries values(1623, "2015-12-23");
+insert into diaries values(1624, "2015-12-24");
+insert into diaries values(1625, "2015-12-25");
+insert into diaries values(1626, "2015-12-26");
+insert into diaries values(1627, "2015-12-27");
+insert into diaries values(1628, "2015-12-28");
+insert into diaries values(1629, "2015-12-29");
+insert into diaries values(1630, "2015-12-30");
+insert into diaries values(1631, "2015-12-31");
+insert into diaries values(1632, "2016-01-01");
+insert into diaries values(1633, "2016-01-02");
+insert into diaries values(1634, "2016-01-03");
+insert into diaries values(1635, "2016-01-04");
+insert into diaries values(1636, "2016-01-05");
+insert into diaries values(1637, "2016-01-06");
+insert into diaries values(1638, "2016-01-07");
+insert into diaries values(1639, "2016-01-08");
+insert into diaries values(1640, "2016-01-09");
+insert into diaries values(1641, "2016-01-10");
+insert into diaries values(1642, "2016-01-11");
+insert into diaries values(1643, "2016-01-12");
+insert into diaries values(1644, "2016-01-13");
+insert into diaries values(1645, "2016-01-14");
+insert into diaries values(1646, "2016-01-15");
+insert into diaries values(1647, "2016-01-16");
+insert into diaries values(1648, "2016-01-17");
+insert into diaries values(1649, "2016-01-18");
+insert into diaries values(1650, "2016-01-19");
+insert into diaries values(1651, "2016-01-20");
+insert into diaries values(1652, "2016-01-21");
+insert into diaries values(1653, "2016-01-22");
+insert into diaries values(1654, "2016-01-23");
+insert into diaries values(1655, "2016-01-24");
+insert into diaries values(1656, "2016-01-25");
+insert into diaries values(1657, "2016-01-26");
+insert into diaries values(1658, "2016-01-27");
+insert into diaries values(1659, "2016-01-28");
+insert into diaries values(1660, "2016-01-29");
+insert into diaries values(1661, "2016-01-30");
+insert into diaries values(1662, "2016-01-31");
+insert into diaries values(1663, "2016-02-01");
+insert into diaries values(1664, "2016-02-02");
+insert into diaries values(1665, "2016-02-03");
+insert into diaries values(1666, "2016-02-04");
+insert into diaries values(1667, "2016-02-05");
+insert into diaries values(1668, "2016-02-06");
+insert into diaries values(1669, "2016-02-07");
+insert into diaries values(1670, "2016-02-08");
+insert into diaries values(1671, "2016-02-09");
+insert into diaries values(1672, "2016-02-10");
+insert into diaries values(1673, "2016-02-11");
+insert into diaries values(1674, "2016-02-12");
+insert into diaries values(1675, "2016-02-13");
+insert into diaries values(1676, "2016-02-14");
+insert into diaries values(1677, "2016-02-15");
+insert into diaries values(1678, "2016-02-16");
+insert into diaries values(1679, "2016-02-17");
+insert into diaries values(1680, "2016-02-18");
+insert into diaries values(1681, "2016-02-19");
+insert into diaries values(1682, "2016-02-20");
+insert into diaries values(1683, "2016-02-21");
+insert into diaries values(1684, "2016-02-22");
+insert into diaries values(1685, "2016-02-23");
+insert into diaries values(1686, "2016-02-24");
+insert into diaries values(1687, "2016-02-25");
+insert into diaries values(1688, "2016-02-26");
+insert into diaries values(1689, "2016-02-27");
+insert into diaries values(1690, "2016-02-28");
+insert into diaries values(1691, "2016-02-29");
+insert into diaries values(1692, "2016-03-01");
+insert into diaries values(1693, "2016-03-02");
+insert into diaries values(1694, "2016-03-03");
+insert into diaries values(1695, "2016-03-04");
+insert into diaries values(1696, "2016-03-05");
+insert into diaries values(1697, "2016-03-06");
+insert into diaries values(1698, "2016-03-07");
+insert into diaries values(1699, "2016-03-08");
+insert into diaries values(1700, "2016-03-09");
+insert into diaries values(1701, "2016-03-10");
+insert into diaries values(1702, "2016-03-11");
+insert into diaries values(1703, "2016-03-12");
+insert into diaries values(1704, "2016-03-13");
+insert into diaries values(1705, "2016-03-14");
+insert into diaries values(1706, "2016-03-15");
+insert into diaries values(1707, "2016-03-16");
+insert into diaries values(1708, "2016-03-17");
+insert into diaries values(1709, "2016-03-18");
+insert into diaries values(1710, "2016-03-19");
+insert into diaries values(1711, "2016-03-20");
+insert into diaries values(1712, "2016-03-21");
+insert into diaries values(1713, "2016-03-22");
+insert into diaries values(1714, "2016-03-23");
+insert into diaries values(1715, "2016-03-24");
+insert into diaries values(1716, "2016-03-25");
+insert into diaries values(1717, "2016-03-26");
+insert into diaries values(1718, "2016-03-27");
+insert into diaries values(1719, "2016-03-28");
+insert into diaries values(1720, "2016-03-29");
+insert into diaries values(1721, "2016-03-30");
+insert into diaries values(1722, "2016-03-31");
+insert into diaries values(1723, "2016-04-01");
+insert into diaries values(1724, "2016-04-02");
+insert into diaries values(1725, "2016-04-03");
+insert into diaries values(1726, "2016-04-04");
+insert into diaries values(1727, "2016-04-05");
+insert into diaries values(1728, "2016-04-06");
+insert into diaries values(1729, "2016-04-07");
+insert into diaries values(1730, "2016-04-08");
+insert into diaries values(1731, "2016-04-09");
+insert into diaries values(1732, "2016-04-10");
+insert into diaries values(1733, "2016-04-11");
+insert into diaries values(1734, "2016-04-12");
+insert into diaries values(1735, "2016-04-13");
+insert into diaries values(1736, "2016-04-14");
+insert into diaries values(1737, "2016-04-15");
+insert into diaries values(1738, "2016-04-16");
+insert into diaries values(1739, "2016-04-17");
+insert into diaries values(1740, "2016-04-18");
+insert into diaries values(1741, "2016-04-19");
+insert into diaries values(1742, "2016-04-20");
+insert into diaries values(1743, "2016-04-21");
+insert into diaries values(1744, "2016-04-22");
+insert into diaries values(1745, "2016-04-23");
+insert into diaries values(1746, "2016-04-24");
+insert into diaries values(1747, "2016-04-25");
+insert into diaries values(1748, "2016-04-26");
+insert into diaries values(1749, "2016-04-27");
+insert into diaries values(1750, "2016-04-28");
+insert into diaries values(1751, "2016-04-29");
+insert into diaries values(1752, "2016-04-30");
+insert into diaries values(1753, "2016-05-01");
+insert into diaries values(1754, "2016-05-02");
+insert into diaries values(1755, "2016-05-03");
+insert into diaries values(1756, "2016-05-04");
+insert into diaries values(1757, "2016-05-05");
+insert into diaries values(1758, "2016-05-06");
+insert into diaries values(1759, "2016-05-07");
+insert into diaries values(1760, "2016-05-08");
+insert into diaries values(1761, "2016-05-09");
+insert into diaries values(1762, "2016-05-10");
+insert into diaries values(1763, "2016-05-11");
+insert into diaries values(1764, "2016-05-12");
+insert into diaries values(1765, "2016-05-13");
+insert into diaries values(1766, "2016-05-14");
+insert into diaries values(1767, "2016-05-15");
+insert into diaries values(1768, "2016-05-16");
+insert into diaries values(1769, "2016-05-17");
+insert into diaries values(1770, "2016-05-18");
+insert into diaries values(1771, "2016-05-19");
+insert into diaries values(1772, "2016-05-20");
+insert into diaries values(1773, "2016-05-21");
+insert into diaries values(1774, "2016-05-22");
+insert into diaries values(1775, "2016-05-23");
+insert into diaries values(1776, "2016-05-24");
+insert into diaries values(1777, "2016-05-25");
+insert into diaries values(1778, "2016-05-26");
+insert into diaries values(1779, "2016-05-27");
+insert into diaries values(1780, "2016-05-28");
+insert into diaries values(1781, "2016-05-29");
+insert into diaries values(1782, "2016-05-30");
+insert into diaries values(1783, "2016-05-31");
+insert into diaries values(1784, "2016-06-01");
+insert into diaries values(1785, "2016-06-02");
+insert into diaries values(1786, "2016-06-03");
+insert into diaries values(1787, "2016-06-04");
+insert into diaries values(1788, "2016-06-05");
+insert into diaries values(1789, "2016-06-06");
+insert into diaries values(1790, "2016-06-07");
+insert into diaries values(1791, "2016-06-08");
+insert into diaries values(1792, "2016-06-09");
+insert into diaries values(1793, "2016-06-10");
+insert into diaries values(1794, "2016-06-11");
+insert into diaries values(1795, "2016-06-12");
+insert into diaries values(1796, "2016-06-13");
+insert into diaries values(1797, "2016-06-14");
+insert into diaries values(1798, "2016-06-15");
+insert into diaries values(1799, "2016-06-16");
+insert into diaries values(1800, "2016-06-17");
+insert into diaries values(1801, "2016-06-18");
+insert into diaries values(1802, "2016-06-19");
+insert into diaries values(1803, "2016-06-20");
+insert into diaries values(1804, "2016-06-21");
+insert into diaries values(1805, "2016-06-22");
+insert into diaries values(1806, "2016-06-23");
+insert into diaries values(1807, "2016-06-24");
+insert into diaries values(1808, "2016-06-25");
+insert into diaries values(1809, "2016-06-26");
+insert into diaries values(1810, "2016-06-27");
+insert into diaries values(1811, "2016-06-28");
+insert into diaries values(1812, "2016-06-29");
+insert into diaries values(1813, "2016-06-30");
+insert into diaries values(1814, "2016-07-01");
+insert into diaries values(1815, "2016-07-02");
+insert into diaries values(1816, "2016-07-03");
+insert into diaries values(1817, "2016-07-04");
+insert into diaries values(1818, "2016-07-05");
+insert into diaries values(1819, "2016-07-06");
+insert into diaries values(1820, "2016-07-07");
+insert into diaries values(1821, "2016-07-08");
+insert into diaries values(1822, "2016-07-09");
+insert into diaries values(1823, "2016-07-10");
+insert into diaries values(1824, "2016-07-11");
+insert into diaries values(1825, "2016-07-12");
+insert into diaries values(1826, "2016-07-13");
+insert into diaries values(1827, "2016-07-14");
+insert into diaries values(1828, "2016-07-15");
+insert into diaries values(1829, "2016-07-16");
+insert into diaries values(1830, "2016-07-17");
+insert into diaries values(1831, "2016-07-18");
+insert into diaries values(1832, "2016-07-19");
+insert into diaries values(1833, "2016-07-20");
+insert into diaries values(1834, "2016-07-21");
+insert into diaries values(1835, "2016-07-22");
+insert into diaries values(1836, "2016-07-23");
+insert into diaries values(1837, "2016-07-24");
+insert into diaries values(1838, "2016-07-25");
+insert into diaries values(1839, "2016-07-26");
+insert into diaries values(1840, "2016-07-27");
+insert into diaries values(1841, "2016-07-28");
+insert into diaries values(1842, "2016-07-29");
+insert into diaries values(1843, "2016-07-30");
+insert into diaries values(1844, "2016-07-31");
+insert into diaries values(1845, "2016-08-01");
+insert into diaries values(1846, "2016-08-02");
+insert into diaries values(1847, "2016-08-03");
+insert into diaries values(1848, "2016-08-04");
+insert into diaries values(1849, "2016-08-05");
+insert into diaries values(1850, "2016-08-06");
+insert into diaries values(1851, "2016-08-07");
+insert into diaries values(1852, "2016-08-08");
+insert into diaries values(1853, "2016-08-09");
+insert into diaries values(1854, "2016-08-10");
+insert into diaries values(1855, "2016-08-11");
+insert into diaries values(1856, "2016-08-12");
+insert into diaries values(1857, "2016-08-13");
+insert into diaries values(1858, "2016-08-14");
+insert into diaries values(1859, "2016-08-15");
+insert into diaries values(1860, "2016-08-16");
+insert into diaries values(1861, "2016-08-17");
+insert into diaries values(1862, "2016-08-18");
+insert into diaries values(1863, "2016-08-19");
+insert into diaries values(1864, "2016-08-20");
+insert into diaries values(1865, "2016-08-21");
+insert into diaries values(1866, "2016-08-22");
+insert into diaries values(1867, "2016-08-23");
+insert into diaries values(1868, "2016-08-24");
+insert into diaries values(1869, "2016-08-25");
+insert into diaries values(1870, "2016-08-26");
+insert into diaries values(1871, "2016-08-27");
+insert into diaries values(1872, "2016-08-28");
+insert into diaries values(1873, "2016-08-29");
+insert into diaries values(1874, "2016-08-30");
+insert into diaries values(1875, "2016-08-31");
+insert into diaries values(1876, "2016-09-01");
+insert into diaries values(1877, "2016-09-02");
+insert into diaries values(1878, "2016-09-03");
+insert into diaries values(1879, "2016-09-04");
+insert into diaries values(1880, "2016-09-05");
+insert into diaries values(1881, "2016-09-06");
+insert into diaries values(1882, "2016-09-07");
+insert into diaries values(1883, "2016-09-08");
+insert into diaries values(1884, "2016-09-09");
+insert into diaries values(1885, "2016-09-10");
+insert into diaries values(1886, "2016-09-11");
+insert into diaries values(1887, "2016-09-12");
+insert into diaries values(1888, "2016-09-13");
+insert into diaries values(1889, "2016-09-14");
+insert into diaries values(1890, "2016-09-15");
+insert into diaries values(1891, "2016-09-16");
+insert into diaries values(1892, "2016-09-17");
+insert into diaries values(1893, "2016-09-18");
+insert into diaries values(1894, "2016-09-19");
+insert into diaries values(1895, "2016-09-20");
+insert into diaries values(1896, "2016-09-21");
+insert into diaries values(1897, "2016-09-22");
+insert into diaries values(1898, "2016-09-23");
+insert into diaries values(1899, "2016-09-24");
+insert into diaries values(1900, "2016-09-25");
+insert into diaries values(1901, "2016-09-26");
+insert into diaries values(1902, "2016-09-27");
+insert into diaries values(1903, "2016-09-28");
+insert into diaries values(1904, "2016-09-29");
+insert into diaries values(1905, "2016-09-30");
+insert into diaries values(1906, "2016-10-01");
+insert into diaries values(1907, "2016-10-02");
+insert into diaries values(1908, "2016-10-03");
+insert into diaries values(1909, "2016-10-04");
+insert into diaries values(1910, "2016-10-05");
+insert into diaries values(1911, "2016-10-06");
+insert into diaries values(1912, "2016-10-07");
+insert into diaries values(1913, "2016-10-08");
+insert into diaries values(1914, "2016-10-09");
+insert into diaries values(1915, "2016-10-10");
+insert into diaries values(1916, "2016-10-11");
+insert into diaries values(1917, "2016-10-12");
+insert into diaries values(1918, "2016-10-13");
+insert into diaries values(1919, "2016-10-14");
+insert into diaries values(1920, "2016-10-15");
+insert into diaries values(1921, "2016-10-16");
+insert into diaries values(1922, "2016-10-17");
+insert into diaries values(1923, "2016-10-18");
+insert into diaries values(1924, "2016-10-19");
+insert into diaries values(1925, "2016-10-20");
+insert into diaries values(1926, "2016-10-21");
+insert into diaries values(1927, "2016-10-22");
+insert into diaries values(1928, "2016-10-23");
+insert into diaries values(1929, "2016-10-24");
+insert into diaries values(1930, "2016-10-25");
+insert into diaries values(1931, "2016-10-26");
+insert into diaries values(1932, "2016-10-27");
+insert into diaries values(1933, "2016-10-28");
+insert into diaries values(1934, "2016-10-29");
+insert into diaries values(1935, "2016-10-30");
+insert into diaries values(1936, "2016-10-31");
+insert into diaries values(1937, "2016-11-01");
+insert into diaries values(1938, "2016-11-02");
+insert into diaries values(1939, "2016-11-03");
+insert into diaries values(1940, "2016-11-04");
+insert into diaries values(1941, "2016-11-05");
+insert into diaries values(1942, "2016-11-06");
+insert into diaries values(1943, "2016-11-07");
+insert into diaries values(1944, "2016-11-08");
+insert into diaries values(1945, "2016-11-09");
+insert into diaries values(1946, "2016-11-10");
+insert into diaries values(1947, "2016-11-11");
+insert into diaries values(1948, "2016-11-12");
+insert into diaries values(1949, "2016-11-13");
+insert into diaries values(1950, "2016-11-14");
+insert into diaries values(1951, "2016-11-15");
+insert into diaries values(1952, "2016-11-16");
+insert into diaries values(1953, "2016-11-17");
+insert into diaries values(1954, "2016-11-18");
+insert into diaries values(1955, "2016-11-19");
+insert into diaries values(1956, "2016-11-20");
+insert into diaries values(1957, "2016-11-21");
+insert into diaries values(1958, "2016-11-22");
+insert into diaries values(1959, "2016-11-23");
+insert into diaries values(1960, "2016-11-24");
+insert into diaries values(1961, "2016-11-25");
+insert into diaries values(1962, "2016-11-26");
+insert into diaries values(1963, "2016-11-27");
+insert into diaries values(1964, "2016-11-28");
+insert into diaries values(1965, "2016-11-29");
+insert into diaries values(1966, "2016-11-30");
+insert into diaries values(1967, "2016-12-01");
+insert into diaries values(1968, "2016-12-02");
+insert into diaries values(1969, "2016-12-03");
+insert into diaries values(1970, "2016-12-04");
+insert into diaries values(1971, "2016-12-05");
+insert into diaries values(1972, "2016-12-06");
+insert into diaries values(1973, "2016-12-07");
+insert into diaries values(1974, "2016-12-08");
+insert into diaries values(1975, "2016-12-09");
+insert into diaries values(1976, "2016-12-10");
+insert into diaries values(1977, "2016-12-11");
+insert into diaries values(1978, "2016-12-12");
+insert into diaries values(1979, "2016-12-13");
+insert into diaries values(1980, "2016-12-14");
+insert into diaries values(1981, "2016-12-15");
+insert into diaries values(1982, "2016-12-16");
+insert into diaries values(1983, "2016-12-17");
+insert into diaries values(1984, "2016-12-18");
+insert into diaries values(1985, "2016-12-19");
+insert into diaries values(1986, "2016-12-20");
+insert into diaries values(1987, "2016-12-21");
+insert into diaries values(1988, "2016-12-22");
+insert into diaries values(1989, "2016-12-23");
+insert into diaries values(1990, "2016-12-24");
+insert into diaries values(1991, "2016-12-25");
+insert into diaries values(1992, "2016-12-26");
+insert into diaries values(1993, "2016-12-27");
+insert into diaries values(1994, "2016-12-28");
+insert into diaries values(1995, "2016-12-29");
+insert into diaries values(1996, "2016-12-30");
+insert into diaries values(1997, "2016-12-31");
+insert into diaries values(1998, "2017-01-01");
+insert into diaries values(1999, "2017-01-02");
+insert into diaries values(2000, "2017-01-03");
+insert into diaries values(2001, "2017-01-04");
+insert into diaries values(2002, "2017-01-05");
+insert into diaries values(2003, "2017-01-06");
+insert into diaries values(2004, "2017-01-07");
+insert into diaries values(2005, "2017-01-08");
+insert into diaries values(2006, "2017-01-09");
+insert into diaries values(2007, "2017-01-10");
+insert into diaries values(2008, "2017-01-11");
+insert into diaries values(2009, "2017-01-12");
+insert into diaries values(2010, "2017-01-13");
+insert into diaries values(2011, "2017-01-14");
+insert into diaries values(2012, "2017-01-15");
+insert into diaries values(2013, "2017-01-16");
+insert into diaries values(2014, "2017-01-17");
+insert into diaries values(2015, "2017-01-18");
+insert into diaries values(2016, "2017-01-19");
+insert into diaries values(2017, "2017-01-20");
+insert into diaries values(2018, "2017-01-21");
+insert into diaries values(2019, "2017-01-22");
+insert into diaries values(2020, "2017-01-23");
+insert into diaries values(2021, "2017-01-24");
+insert into diaries values(2022, "2017-01-25");
+insert into diaries values(2023, "2017-01-26");
+insert into diaries values(2024, "2017-01-27");
+insert into diaries values(2025, "2017-01-28");
+insert into diaries values(2026, "2017-01-29");
+insert into diaries values(2027, "2017-01-30");
+insert into diaries values(2028, "2017-01-31");
+insert into diaries values(2029, "2017-02-01");
+insert into diaries values(2030, "2017-02-02");
+insert into diaries values(2031, "2017-02-03");
+insert into diaries values(2032, "2017-02-04");
+insert into diaries values(2033, "2017-02-05");
+insert into diaries values(2034, "2017-02-06");
+insert into diaries values(2035, "2017-02-07");
+insert into diaries values(2036, "2017-02-08");
+insert into diaries values(2037, "2017-02-09");
+insert into diaries values(2038, "2017-02-10");
+insert into diaries values(2039, "2017-02-11");
+insert into diaries values(2040, "2017-02-12");
+insert into diaries values(2041, "2017-02-13");
+insert into diaries values(2042, "2017-02-14");
+insert into diaries values(2043, "2017-02-15");
+insert into diaries values(2044, "2017-02-16");
+insert into diaries values(2045, "2017-02-17");
+insert into diaries values(2046, "2017-02-18");
+insert into diaries values(2047, "2017-02-19");
+insert into diaries values(2048, "2017-02-20");
+insert into diaries values(2049, "2017-02-21");
+insert into diaries values(2050, "2017-02-22");
+insert into diaries values(2051, "2017-02-23");
+insert into diaries values(2052, "2017-02-24");
+insert into diaries values(2053, "2017-02-25");
+insert into diaries values(2054, "2017-02-26");
+insert into diaries values(2055, "2017-02-27");
+insert into diaries values(2056, "2017-02-28");
+insert into diaries values(2057, "2017-03-01");
+insert into diaries values(2058, "2017-03-02");
+insert into diaries values(2059, "2017-03-03");
+insert into diaries values(2060, "2017-03-04");
+insert into diaries values(2061, "2017-03-05");
+insert into diaries values(2062, "2017-03-06");
+insert into diaries values(2063, "2017-03-07");
+insert into diaries values(2064, "2017-03-08");
+insert into diaries values(2065, "2017-03-09");
+insert into diaries values(2066, "2017-03-10");
+insert into diaries values(2067, "2017-03-11");
+insert into diaries values(2068, "2017-03-12");
+insert into diaries values(2069, "2017-03-13");
+insert into diaries values(2070, "2017-03-14");
+insert into diaries values(2071, "2017-03-15");
+insert into diaries values(2072, "2017-03-16");
+insert into diaries values(2073, "2017-03-17");
+insert into diaries values(2074, "2017-03-18");
+insert into diaries values(2075, "2017-03-19");
+insert into diaries values(2076, "2017-03-20");
+insert into diaries values(2077, "2017-03-21");
+insert into diaries values(2078, "2017-03-22");
+insert into diaries values(2079, "2017-03-23");
+insert into diaries values(2080, "2017-03-24");
+insert into diaries values(2081, "2017-03-25");
+insert into diaries values(2082, "2017-03-26");
+insert into diaries values(2083, "2017-03-27");
+insert into diaries values(2084, "2017-03-28");
+insert into diaries values(2085, "2017-03-29");
+insert into diaries values(2086, "2017-03-30");
+insert into diaries values(2087, "2017-03-31");
+insert into diaries values(2088, "2017-04-01");
+insert into diaries values(2089, "2017-04-02");
+insert into diaries values(2090, "2017-04-03");
+insert into diaries values(2091, "2017-04-04");
+insert into diaries values(2092, "2017-04-05");
+insert into diaries values(2093, "2017-04-06");
+insert into diaries values(2094, "2017-04-07");
+insert into diaries values(2095, "2017-04-08");
+insert into diaries values(2096, "2017-04-09");
+insert into diaries values(2097, "2017-04-10");
+insert into diaries values(2098, "2017-04-11");
+insert into diaries values(2099, "2017-04-12");
+insert into diaries values(2100, "2017-04-13");
+insert into diaries values(2101, "2017-04-14");
+insert into diaries values(2102, "2017-04-15");
+insert into diaries values(2103, "2017-04-16");
+insert into diaries values(2104, "2017-04-17");
+insert into diaries values(2105, "2017-04-18");
+insert into diaries values(2106, "2017-04-19");
+insert into diaries values(2107, "2017-04-20");
+insert into diaries values(2108, "2017-04-21");
+insert into diaries values(2109, "2017-04-22");
+insert into diaries values(2110, "2017-04-23");
+insert into diaries values(2111, "2017-04-24");
+insert into diaries values(2112, "2017-04-25");
+insert into diaries values(2113, "2017-04-26");
+insert into diaries values(2114, "2017-04-27");
+insert into diaries values(2115, "2017-04-28");
+insert into diaries values(2116, "2017-04-29");
+insert into diaries values(2117, "2017-04-30");
+insert into diaries values(2118, "2017-05-01");
+insert into diaries values(2119, "2017-05-02");
+insert into diaries values(2120, "2017-05-03");
+insert into diaries values(2121, "2017-05-04");
+insert into diaries values(2122, "2017-05-05");
+insert into diaries values(2123, "2017-05-06");
+insert into diaries values(2124, "2017-05-07");
+insert into diaries values(2125, "2017-05-08");
+insert into diaries values(2126, "2017-05-09");
+insert into diaries values(2127, "2017-05-10");
+insert into diaries values(2128, "2017-05-11");
+insert into diaries values(2129, "2017-05-12");
+insert into diaries values(2130, "2017-05-13");
+insert into diaries values(2131, "2017-05-14");
+insert into diaries values(2132, "2017-05-15");
+insert into diaries values(2133, "2017-05-16");
+insert into diaries values(2134, "2017-05-17");
+insert into diaries values(2135, "2017-05-18");
+insert into diaries values(2136, "2017-05-19");
+insert into diaries values(2137, "2017-05-20");
+insert into diaries values(2138, "2017-05-21");
+insert into diaries values(2139, "2017-05-22");
+insert into diaries values(2140, "2017-05-23");
+insert into diaries values(2141, "2017-05-24");
+insert into diaries values(2142, "2017-05-25");
+insert into diaries values(2143, "2017-05-26");
+insert into diaries values(2144, "2017-05-27");
+insert into diaries values(2145, "2017-05-28");
+insert into diaries values(2146, "2017-05-29");
+insert into diaries values(2147, "2017-05-30");
+insert into diaries values(2148, "2017-05-31");
+insert into diaries values(2149, "2017-06-01");
+insert into diaries values(2150, "2017-06-02");
+insert into diaries values(2151, "2017-06-03");
+insert into diaries values(2152, "2017-06-04");
+insert into diaries values(2153, "2017-06-05");
+insert into diaries values(2154, "2017-06-06");
+insert into diaries values(2155, "2017-06-07");
+insert into diaries values(2156, "2017-06-08");
+insert into diaries values(2157, "2017-06-09");
+insert into diaries values(2158, "2017-06-10");
+insert into diaries values(2159, "2017-06-11");
+insert into diaries values(2160, "2017-06-12");
+insert into diaries values(2161, "2017-06-13");
+insert into diaries values(2162, "2017-06-14");
+insert into diaries values(2163, "2017-06-15");
+insert into diaries values(2164, "2017-06-16");
+insert into diaries values(2165, "2017-06-17");
+insert into diaries values(2166, "2017-06-18");
+insert into diaries values(2167, "2017-06-19");
+insert into diaries values(2168, "2017-06-20");
+insert into diaries values(2169, "2017-06-21");
+insert into diaries values(2170, "2017-06-22");
+insert into diaries values(2171, "2017-06-23");
+insert into diaries values(2172, "2017-06-24");
+insert into diaries values(2173, "2017-06-25");
+insert into diaries values(2174, "2017-06-26");
+insert into diaries values(2175, "2017-06-27");
+insert into diaries values(2176, "2017-06-28");
+insert into diaries values(2177, "2017-06-29");
+insert into diaries values(2178, "2017-06-30");
+insert into diaries values(2179, "2017-07-01");
+insert into diaries values(2180, "2017-07-02");
+insert into diaries values(2181, "2017-07-03");
+insert into diaries values(2182, "2017-07-04");
+insert into diaries values(2183, "2017-07-05");
+insert into diaries values(2184, "2017-07-06");
+insert into diaries values(2185, "2017-07-07");
+insert into diaries values(2186, "2017-07-08");
+insert into diaries values(2187, "2017-07-09");
+insert into diaries values(2188, "2017-07-10");
+insert into diaries values(2189, "2017-07-11");
+insert into diaries values(2190, "2017-07-12");
+insert into diaries values(2191, "2017-07-13");
+insert into diaries values(2192, "2017-07-14");
+insert into diaries values(2193, "2017-07-15");
+insert into diaries values(2194, "2017-07-16");
+insert into diaries values(2195, "2017-07-17");
+insert into diaries values(2196, "2017-07-18");
+insert into diaries values(2197, "2017-07-19");
+insert into diaries values(2198, "2017-07-20");
+insert into diaries values(2199, "2017-07-21");
+insert into diaries values(2200, "2017-07-22");
+insert into diaries values(2201, "2017-07-23");
+insert into diaries values(2202, "2017-07-24");
+insert into diaries values(2203, "2017-07-25");
+insert into diaries values(2204, "2017-07-26");
+insert into diaries values(2205, "2017-07-27");
+insert into diaries values(2206, "2017-07-28");
+insert into diaries values(2207, "2017-07-29");
+insert into diaries values(2208, "2017-07-30");
+insert into diaries values(2209, "2017-07-31");
+insert into diaries values(2210, "2017-08-01");
+insert into diaries values(2211, "2017-08-02");
+insert into diaries values(2212, "2017-08-03");
+insert into diaries values(2213, "2017-08-04");
+insert into diaries values(2214, "2017-08-05");
+insert into diaries values(2215, "2017-08-06");
+insert into diaries values(2216, "2017-08-07");
+insert into diaries values(2217, "2017-08-08");
+insert into diaries values(2218, "2017-08-09");
+insert into diaries values(2219, "2017-08-10");
+insert into diaries values(2220, "2017-08-11");
+insert into diaries values(2221, "2017-08-12");
+insert into diaries values(2222, "2017-08-13");
+insert into diaries values(2223, "2017-08-14");
+insert into diaries values(2224, "2017-08-15");
+insert into diaries values(2225, "2017-08-16");
+insert into diaries values(2226, "2017-08-17");
+insert into diaries values(2227, "2017-08-18");
+insert into diaries values(2228, "2017-08-19");
+insert into diaries values(2229, "2017-08-20");
+insert into diaries values(2230, "2017-08-21");
+insert into diaries values(2231, "2017-08-22");
+insert into diaries values(2232, "2017-08-23");
+insert into diaries values(2233, "2017-08-24");
+insert into diaries values(2234, "2017-08-25");
+insert into diaries values(2235, "2017-08-26");
+insert into diaries values(2236, "2017-08-27");
+insert into diaries values(2237, "2017-08-28");
+insert into diaries values(2238, "2017-08-29");
+insert into diaries values(2239, "2017-08-30");
+insert into diaries values(2240, "2017-08-31");
+insert into diaries values(2241, "2017-09-01");
+insert into diaries values(2242, "2017-09-02");
+insert into diaries values(2243, "2017-09-03");
+insert into diaries values(2244, "2017-09-04");
+insert into diaries values(2245, "2017-09-05");
+insert into diaries values(2246, "2017-09-06");
+insert into diaries values(2247, "2017-09-07");
+insert into diaries values(2248, "2017-09-08");
+insert into diaries values(2249, "2017-09-09");
+insert into diaries values(2250, "2017-09-10");
+insert into diaries values(2251, "2017-09-11");
+insert into diaries values(2252, "2017-09-12");
+insert into diaries values(2253, "2017-09-13");
+insert into diaries values(2254, "2017-09-14");
+insert into diaries values(2255, "2017-09-15");
+insert into diaries values(2256, "2017-09-16");
+insert into diaries values(2257, "2017-09-17");
+insert into diaries values(2258, "2017-09-18");
+insert into diaries values(2259, "2017-09-19");
+insert into diaries values(2260, "2017-09-20");
+insert into diaries values(2261, "2017-09-21");
+insert into diaries values(2262, "2017-09-22");
+insert into diaries values(2263, "2017-09-23");
+insert into diaries values(2264, "2017-09-24");
+insert into diaries values(2265, "2017-09-25");
+insert into diaries values(2266, "2017-09-26");
+insert into diaries values(2267, "2017-09-27");
+insert into diaries values(2268, "2017-09-28");
+insert into diaries values(2269, "2017-09-29");
+insert into diaries values(2270, "2017-09-30");
+insert into diaries values(2271, "2017-10-01");
+insert into diaries values(2272, "2017-10-02");
+insert into diaries values(2273, "2017-10-03");
+insert into diaries values(2274, "2017-10-04");
+insert into diaries values(2275, "2017-10-05");
+insert into diaries values(2276, "2017-10-06");
+insert into diaries values(2277, "2017-10-07");
+insert into diaries values(2278, "2017-10-08");
+insert into diaries values(2279, "2017-10-09");
+insert into diaries values(2280, "2017-10-10");
+insert into diaries values(2281, "2017-10-11");
+insert into diaries values(2282, "2017-10-12");
+insert into diaries values(2283, "2017-10-13");
+insert into diaries values(2284, "2017-10-14");
+insert into diaries values(2285, "2017-10-15");
+insert into diaries values(2286, "2017-10-16");
+insert into diaries values(2287, "2017-10-17");
+insert into diaries values(2288, "2017-10-18");
+insert into diaries values(2289, "2017-10-19");
+insert into diaries values(2290, "2017-10-20");
+insert into diaries values(2291, "2017-10-21");
+insert into diaries values(2292, "2017-10-22");
+insert into diaries values(2293, "2017-10-23");
+insert into diaries values(2294, "2017-10-24");
+insert into diaries values(2295, "2017-10-25");
+insert into diaries values(2296, "2017-10-26");
+insert into diaries values(2297, "2017-10-27");
+insert into diaries values(2298, "2017-10-28");
+insert into diaries values(2299, "2017-10-29");
+insert into diaries values(2300, "2017-10-30");
+insert into diaries values(2301, "2017-10-31");
+insert into diaries values(2302, "2017-11-01");
+insert into diaries values(2303, "2017-11-02");
+insert into diaries values(2304, "2017-11-03");
+insert into diaries values(2305, "2017-11-04");
+insert into diaries values(2306, "2017-11-05");
+insert into diaries values(2307, "2017-11-06");
+insert into diaries values(2308, "2017-11-07");
+insert into diaries values(2309, "2017-11-08");
+insert into diaries values(2310, "2017-11-09");
+insert into diaries values(2311, "2017-11-10");
+insert into diaries values(2312, "2017-11-11");
+insert into diaries values(2313, "2017-11-12");
+insert into diaries values(2314, "2017-11-13");
+insert into diaries values(2315, "2017-11-14");
+insert into diaries values(2316, "2017-11-15");
+insert into diaries values(2317, "2017-11-16");
+insert into diaries values(2318, "2017-11-17");
+insert into diaries values(2319, "2017-11-18");
+insert into diaries values(2320, "2017-11-19");
+insert into diaries values(2321, "2017-11-20");
+insert into diaries values(2322, "2017-11-21");
+insert into diaries values(2323, "2017-11-22");
+insert into diaries values(2324, "2017-11-23");
+insert into diaries values(2325, "2017-11-24");
+insert into diaries values(2326, "2017-11-25");
+insert into diaries values(2327, "2017-11-26");
+insert into diaries values(2328, "2017-11-27");
+insert into diaries values(2329, "2017-11-28");
+insert into diaries values(2330, "2017-11-29");
+insert into diaries values(2331, "2017-11-30");
+insert into diaries values(2332, "2017-12-01");
+insert into diaries values(2333, "2017-12-02");
+insert into diaries values(2334, "2017-12-03");
+insert into diaries values(2335, "2017-12-04");
+insert into diaries values(2336, "2017-12-05");
+insert into diaries values(2337, "2017-12-06");
+insert into diaries values(2338, "2017-12-07");
+insert into diaries values(2339, "2017-12-08");
+insert into diaries values(2340, "2017-12-09");
+insert into diaries values(2341, "2017-12-10");
+insert into diaries values(2342, "2017-12-11");
+insert into diaries values(2343, "2017-12-12");
+insert into diaries values(2344, "2017-12-13");
+insert into diaries values(2345, "2017-12-14");
+insert into diaries values(2346, "2017-12-15");
+insert into diaries values(2347, "2017-12-16");
+insert into diaries values(2348, "2017-12-17");
+insert into diaries values(2349, "2017-12-18");
+insert into diaries values(2350, "2017-12-19");
+insert into diaries values(2351, "2017-12-20");
+insert into diaries values(2352, "2017-12-21");
+insert into diaries values(2353, "2017-12-22");
+insert into diaries values(2354, "2017-12-23");
+insert into diaries values(2355, "2017-12-24");
+insert into diaries values(2356, "2017-12-25");
+insert into diaries values(2357, "2017-12-26");
+insert into diaries values(2358, "2017-12-27");
+insert into diaries values(2359, "2017-12-28");
+insert into diaries values(2360, "2017-12-29");
+insert into diaries values(2361, "2017-12-30");
+insert into diaries values(2362, "2017-12-31");
+insert into diaries values(2363, "2018-01-01");
+insert into diaries values(2364, "2018-01-02");
+insert into diaries values(2365, "2018-01-03");
+insert into diaries values(2366, "2018-01-04");
+insert into diaries values(2367, "2018-01-05");
+insert into diaries values(2368, "2018-01-06");
+insert into diaries values(2369, "2018-01-07");
+insert into diaries values(2370, "2018-01-08");
+insert into diaries values(2371, "2018-01-09");
+insert into diaries values(2372, "2018-01-10");
+insert into diaries values(2373, "2018-01-11");
+insert into diaries values(2374, "2018-01-12");
+insert into diaries values(2375, "2018-01-13");
+insert into diaries values(2376, "2018-01-14");
+insert into diaries values(2377, "2018-01-15");
+insert into diaries values(2378, "2018-01-16");
+insert into diaries values(2379, "2018-01-17");
+insert into diaries values(2380, "2018-01-18");
+insert into diaries values(2381, "2018-01-19");
+insert into diaries values(2382, "2018-01-20");
+insert into diaries values(2383, "2018-01-21");
+insert into diaries values(2384, "2018-01-22");
+insert into diaries values(2385, "2018-01-23");
+insert into diaries values(2386, "2018-01-24");
+insert into diaries values(2387, "2018-01-25");
+insert into diaries values(2388, "2018-01-26");
+insert into diaries values(2389, "2018-01-27");
+insert into diaries values(2390, "2018-01-28");
+insert into diaries values(2391, "2018-01-29");
+insert into diaries values(2392, "2018-01-30");
+insert into diaries values(2393, "2018-01-31");
+insert into diaries values(2394, "2018-02-01");
+insert into diaries values(2395, "2018-02-02");
+insert into diaries values(2396, "2018-02-03");
+insert into diaries values(2397, "2018-02-04");
+insert into diaries values(2398, "2018-02-05");
+insert into diaries values(2399, "2018-02-06");
+insert into diaries values(2400, "2018-02-07");
+insert into diaries values(2401, "2018-02-08");
+insert into diaries values(2402, "2018-02-09");
+insert into diaries values(2403, "2018-02-10");
+insert into diaries values(2404, "2018-02-11");
+insert into diaries values(2405, "2018-02-12");
+insert into diaries values(2406, "2018-02-13");
+insert into diaries values(2407, "2018-02-14");
+insert into diaries values(2408, "2018-02-15");
+insert into diaries values(2409, "2018-02-16");
+insert into diaries values(2410, "2018-02-17");
+insert into diaries values(2411, "2018-02-18");
+insert into diaries values(2412, "2018-02-19");
+insert into diaries values(2413, "2018-02-20");
+insert into diaries values(2414, "2018-02-21");
+insert into diaries values(2415, "2018-02-22");
+insert into diaries values(2416, "2018-02-23");
+insert into diaries values(2417, "2018-02-24");
+insert into diaries values(2418, "2018-02-25");
+insert into diaries values(2419, "2018-02-26");
+insert into diaries values(2420, "2018-02-27");
+insert into diaries values(2421, "2018-02-28");
+insert into diaries values(2422, "2018-03-01");
+insert into diaries values(2423, "2018-03-02");
+insert into diaries values(2424, "2018-03-03");
+insert into diaries values(2425, "2018-03-04");
+insert into diaries values(2426, "2018-03-05");
+insert into diaries values(2427, "2018-03-06");
+insert into diaries values(2428, "2018-03-07");
+insert into diaries values(2429, "2018-03-08");
+insert into diaries values(2430, "2018-03-09");
+insert into diaries values(2431, "2018-03-10");
+insert into diaries values(2432, "2018-03-11");
+insert into diaries values(2433, "2018-03-12");
+insert into diaries values(2434, "2018-03-13");
+insert into diaries values(2435, "2018-03-14");
+insert into diaries values(2436, "2018-03-15");
+insert into diaries values(2437, "2018-03-16");
+insert into diaries values(2438, "2018-03-17");
+insert into diaries values(2439, "2018-03-18");
+insert into diaries values(2440, "2018-03-19");
+insert into diaries values(2441, "2018-03-20");
+insert into diaries values(2442, "2018-03-21");
+insert into diaries values(2443, "2018-03-22");
+insert into diaries values(2444, "2018-03-23");
+insert into diaries values(2445, "2018-03-24");
+insert into diaries values(2446, "2018-03-25");
+insert into diaries values(2447, "2018-03-26");
+insert into diaries values(2448, "2018-03-27");
+insert into diaries values(2449, "2018-03-28");
+insert into diaries values(2450, "2018-03-29");
+insert into diaries values(2451, "2018-03-30");
+insert into diaries values(2452, "2018-03-31");
+insert into diaries values(2453, "2018-04-01");
+insert into diaries values(2454, "2018-04-02");
+insert into diaries values(2455, "2018-04-03");
+insert into diaries values(2456, "2018-04-04");
+insert into diaries values(2457, "2018-04-05");
+insert into diaries values(2458, "2018-04-06");
+insert into diaries values(2459, "2018-04-07");
+insert into diaries values(2460, "2018-04-08");
+insert into diaries values(2461, "2018-04-09");
+insert into diaries values(2462, "2018-04-10");
+insert into diaries values(2463, "2018-04-11");
+insert into diaries values(2464, "2018-04-12");
+insert into diaries values(2465, "2018-04-13");
+insert into diaries values(2466, "2018-04-14");
+insert into diaries values(2467, "2018-04-15");
+insert into diaries values(2468, "2018-04-16");
+insert into diaries values(2469, "2018-04-17");
+insert into diaries values(2470, "2018-04-18");
+insert into diaries values(2471, "2018-04-19");
+insert into diaries values(2472, "2018-04-20");
+insert into diaries values(2473, "2018-04-21");
+insert into diaries values(2474, "2018-04-22");
+insert into diaries values(2475, "2018-04-23");
+insert into diaries values(2476, "2018-04-24");
+insert into diaries values(2477, "2018-04-25");
+insert into diaries values(2478, "2018-04-26");
+insert into diaries values(2479, "2018-04-27");
+insert into diaries values(2480, "2018-04-28");
+insert into diaries values(2481, "2018-04-29");
+insert into diaries values(2482, "2018-04-30");
+insert into diaries values(2483, "2018-05-01");
+insert into diaries values(2484, "2018-05-02");
+insert into diaries values(2485, "2018-05-03");
+insert into diaries values(2486, "2018-05-04");
+insert into diaries values(2487, "2018-05-05");
+insert into diaries values(2488, "2018-05-06");
+insert into diaries values(2489, "2018-05-07");
+insert into diaries values(2490, "2018-05-08");
+insert into diaries values(2491, "2018-05-09");
+insert into diaries values(2492, "2018-05-10");
+insert into diaries values(2493, "2018-05-11");
+insert into diaries values(2494, "2018-05-12");
+insert into diaries values(2495, "2018-05-13");
+insert into diaries values(2496, "2018-05-14");
+insert into diaries values(2497, "2018-05-15");
+insert into diaries values(2498, "2018-05-16");
+insert into diaries values(2499, "2018-05-17");
+insert into diaries values(2500, "2018-05-18");
+insert into diaries values(2501, "2018-05-19");
+insert into diaries values(2502, "2018-05-20");
+insert into diaries values(2503, "2018-05-21");
+insert into diaries values(2504, "2018-05-22");
+insert into diaries values(2505, "2018-05-23");
+insert into diaries values(2506, "2018-05-24");
+insert into diaries values(2507, "2018-05-25");
+insert into diaries values(2508, "2018-05-26");
+insert into diaries values(2509, "2018-05-27");
+insert into diaries values(2510, "2018-05-28");
+insert into diaries values(2511, "2018-05-29");
+insert into diaries values(2512, "2018-05-30");
+insert into diaries values(2513, "2018-05-31");
+insert into diaries values(2514, "2018-06-01");
+insert into diaries values(2515, "2018-06-02");
+insert into diaries values(2516, "2018-06-03");
+insert into diaries values(2517, "2018-06-04");
+insert into diaries values(2518, "2018-06-05");
+insert into diaries values(2519, "2018-06-06");
+insert into diaries values(2520, "2018-06-07");
+insert into diaries values(2521, "2018-06-08");
+insert into diaries values(2522, "2018-06-09");
+insert into diaries values(2523, "2018-06-10");
+insert into diaries values(2524, "2018-06-11");
+insert into diaries values(2525, "2018-06-12");
+insert into diaries values(2526, "2018-06-13");
+insert into diaries values(2527, "2018-06-14");
+insert into diaries values(2528, "2018-06-15");
+insert into diaries values(2529, "2018-06-16");
+insert into diaries values(2530, "2018-06-17");
+insert into diaries values(2531, "2018-06-18");
+insert into diaries values(2532, "2018-06-19");
+insert into diaries values(2533, "2018-06-20");
+insert into diaries values(2534, "2018-06-21");
+insert into diaries values(2535, "2018-06-22");
+insert into diaries values(2536, "2018-06-23");
+insert into diaries values(2537, "2018-06-24");
+insert into diaries values(2538, "2018-06-25");
+insert into diaries values(2539, "2018-06-26");
+insert into diaries values(2540, "2018-06-27");
+insert into diaries values(2541, "2018-06-28");
+insert into diaries values(2542, "2018-06-29");
+insert into diaries values(2543, "2018-06-30");
+insert into diaries values(2544, "2018-07-01");
+insert into diaries values(2545, "2018-07-02");
+insert into diaries values(2546, "2018-07-03");
+insert into diaries values(2547, "2018-07-04");
+insert into diaries values(2548, "2018-07-05");
+insert into diaries values(2549, "2018-07-06");
+insert into diaries values(2550, "2018-07-07");
+insert into diaries values(2551, "2018-07-08");
+insert into diaries values(2552, "2018-07-09");
+insert into diaries values(2553, "2018-07-10");
+insert into diaries values(2554, "2018-07-11");
+insert into diaries values(2555, "2018-07-12");
+insert into diaries values(2556, "2018-07-13");
+insert into diaries values(2557, "2018-07-14");
+insert into diaries values(2558, "2018-07-15");
+insert into diaries values(2559, "2018-07-16");
+insert into diaries values(2560, "2018-07-17");
+insert into diaries values(2561, "2018-07-18");
+insert into diaries values(2562, "2018-07-19");
+insert into diaries values(2563, "2018-07-20");
+insert into diaries values(2564, "2018-07-21");
+insert into diaries values(2565, "2018-07-22");
+insert into diaries values(2566, "2018-07-23");
+insert into diaries values(2567, "2018-07-24");
+insert into diaries values(2568, "2018-07-25");
+insert into diaries values(2569, "2018-07-26");
+insert into diaries values(2570, "2018-07-27");
+insert into diaries values(2571, "2018-07-28");
+insert into diaries values(2572, "2018-07-29");
+insert into diaries values(2573, "2018-07-30");
+insert into diaries values(2574, "2018-07-31");
+insert into diaries values(2575, "2018-08-01");
+insert into diaries values(2576, "2018-08-02");
+insert into diaries values(2577, "2018-08-03");
+insert into diaries values(2578, "2018-08-04");
+insert into diaries values(2579, "2018-08-05");
+insert into diaries values(2580, "2018-08-06");
+insert into diaries values(2581, "2018-08-07");
+insert into diaries values(2582, "2018-08-08");
+insert into diaries values(2583, "2018-08-09");
+insert into diaries values(2584, "2018-08-10");
+insert into diaries values(2585, "2018-08-11");
+insert into diaries values(2586, "2018-08-12");
+insert into diaries values(2587, "2018-08-13");
+insert into diaries values(2588, "2018-08-14");
+insert into diaries values(2589, "2018-08-15");
+insert into diaries values(2590, "2018-08-16");
+insert into diaries values(2591, "2018-08-17");
+insert into diaries values(2592, "2018-08-18");
+insert into diaries values(2593, "2018-08-19");
+insert into diaries values(2594, "2018-08-20");
+insert into diaries values(2595, "2018-08-21");
+insert into diaries values(2596, "2018-08-22");
+insert into diaries values(2597, "2018-08-23");
+insert into diaries values(2598, "2018-08-24");
+insert into diaries values(2599, "2018-08-25");
+insert into diaries values(2600, "2018-08-26");
+insert into diaries values(2601, "2018-08-27");
+insert into diaries values(2602, "2018-08-28");
+insert into diaries values(2603, "2018-08-29");
+insert into diaries values(2604, "2018-08-30");
+insert into diaries values(2605, "2018-08-31");
+insert into diaries values(2606, "2018-09-01");
+insert into diaries values(2607, "2018-09-02");
+insert into diaries values(2608, "2018-09-03");
+insert into diaries values(2609, "2018-09-04");
+insert into diaries values(2610, "2018-09-05");
+insert into diaries values(2611, "2018-09-06");
+insert into diaries values(2612, "2018-09-07");
+insert into diaries values(2613, "2018-09-08");
+insert into diaries values(2614, "2018-09-09");
+insert into diaries values(2615, "2018-09-10");
+insert into diaries values(2616, "2018-09-11");
+insert into diaries values(2617, "2018-09-12");
+insert into diaries values(2618, "2018-09-13");
+insert into diaries values(2619, "2018-09-14");
+insert into diaries values(2620, "2018-09-15");
+insert into diaries values(2621, "2018-09-16");
+insert into diaries values(2622, "2018-09-17");
+insert into diaries values(2623, "2018-09-18");
+insert into diaries values(2624, "2018-09-19");
+insert into diaries values(2625, "2018-09-20");
+insert into diaries values(2626, "2018-09-21");
+insert into diaries values(2627, "2018-09-22");
+insert into diaries values(2628, "2018-09-23");
+insert into diaries values(2629, "2018-09-24");
+insert into diaries values(2630, "2018-09-25");
+insert into diaries values(2631, "2018-09-26");
+insert into diaries values(2632, "2018-09-27");
+insert into diaries values(2633, "2018-09-28");
+insert into diaries values(2634, "2018-09-29");
+insert into diaries values(2635, "2018-09-30");
+insert into diaries values(2636, "2018-10-01");
+insert into diaries values(2637, "2018-10-02");
+insert into diaries values(2638, "2018-10-03");
+insert into diaries values(2639, "2018-10-04");
+insert into diaries values(2640, "2018-10-05");
+insert into diaries values(2641, "2018-10-06");
+insert into diaries values(2642, "2018-10-07");
+insert into diaries values(2643, "2018-10-08");
+insert into diaries values(2644, "2018-10-09");
+insert into diaries values(2645, "2018-10-10");
+insert into diaries values(2646, "2018-10-11");
+insert into diaries values(2647, "2018-10-12");
+insert into diaries values(2648, "2018-10-13");
+insert into diaries values(2649, "2018-10-14");
+insert into diaries values(2650, "2018-10-15");
+insert into diaries values(2651, "2018-10-16");
+insert into diaries values(2652, "2018-10-17");
+insert into diaries values(2653, "2018-10-18");
+insert into diaries values(2654, "2018-10-19");
+insert into diaries values(2655, "2018-10-20");
+insert into diaries values(2656, "2018-10-21");
+insert into diaries values(2657, "2018-10-22");
+insert into diaries values(2658, "2018-10-23");
+insert into diaries values(2659, "2018-10-24");
+insert into diaries values(2660, "2018-10-25");
+insert into diaries values(2661, "2018-10-26");
+insert into diaries values(2662, "2018-10-27");
+insert into diaries values(2663, "2018-10-28");
+insert into diaries values(2664, "2018-10-29");
+insert into diaries values(2665, "2018-10-30");
+insert into diaries values(2666, "2018-10-31");
+insert into diaries values(2667, "2018-11-01");
+insert into diaries values(2668, "2018-11-02");
+insert into diaries values(2669, "2018-11-03");
+insert into diaries values(2670, "2018-11-04");
+insert into diaries values(2671, "2018-11-05");
+insert into diaries values(2672, "2018-11-06");
+insert into diaries values(2673, "2018-11-07");
+insert into diaries values(2674, "2018-11-08");
+insert into diaries values(2675, "2018-11-09");
+insert into diaries values(2676, "2018-11-10");
+insert into diaries values(2677, "2018-11-11");
+insert into diaries values(2678, "2018-11-12");
+insert into diaries values(2679, "2018-11-13");
+insert into diaries values(2680, "2018-11-14");
+insert into diaries values(2681, "2018-11-15");
+insert into diaries values(2682, "2018-11-16");
+insert into diaries values(2683, "2018-11-17");
+insert into diaries values(2684, "2018-11-18");
+insert into diaries values(2685, "2018-11-19");
+insert into diaries values(2686, "2018-11-20");
+insert into diaries values(2687, "2018-11-21");
+insert into diaries values(2688, "2018-11-22");
+insert into diaries values(2689, "2018-11-23");
+insert into diaries values(2690, "2018-11-24");
+insert into diaries values(2691, "2018-11-25");
+insert into diaries values(2692, "2018-11-26");
+insert into diaries values(2693, "2018-11-27");
+insert into diaries values(2694, "2018-11-28");
+insert into diaries values(2695, "2018-11-29");
+insert into diaries values(2696, "2018-11-30");
+insert into diaries values(2697, "2018-12-01");
+insert into diaries values(2698, "2018-12-02");
+insert into diaries values(2699, "2018-12-03");
+insert into diaries values(2700, "2018-12-04");
+insert into diaries values(2701, "2018-12-05");
+insert into diaries values(2702, "2018-12-06");
+insert into diaries values(2703, "2018-12-07");
+insert into diaries values(2704, "2018-12-08");
+insert into diaries values(2705, "2018-12-09");
+insert into diaries values(2706, "2018-12-10");
+insert into diaries values(2707, "2018-12-11");
+insert into diaries values(2708, "2018-12-12");
+insert into diaries values(2709, "2018-12-13");
+insert into diaries values(2710, "2018-12-14");
+insert into diaries values(2711, "2018-12-15");
+insert into diaries values(2712, "2018-12-16");
+insert into diaries values(2713, "2018-12-17");
+insert into diaries values(2714, "2018-12-18");
+insert into diaries values(2715, "2018-12-19");
+insert into diaries values(2716, "2018-12-20");
+insert into diaries values(2717, "2018-12-21");
+insert into diaries values(2718, "2018-12-22");
+insert into diaries values(2719, "2018-12-23");
+insert into diaries values(2720, "2018-12-24");
+insert into diaries values(2721, "2018-12-25");
+insert into diaries values(2722, "2018-12-26");
+insert into diaries values(2723, "2018-12-27");
+insert into diaries values(2724, "2018-12-28");
+insert into diaries values(2725, "2018-12-29");
+insert into diaries values(2726, "2018-12-30");
+insert into diaries values(2727, "2018-12-31");
+insert into diaries values(2728, "2019-01-01");
+insert into diaries values(2729, "2019-01-02");
+insert into diaries values(2730, "2019-01-03");
+insert into diaries values(2731, "2019-01-04");
+insert into diaries values(2732, "2019-01-05");
+insert into diaries values(2733, "2019-01-06");
+insert into diaries values(2734, "2019-01-07");
+insert into diaries values(2735, "2019-01-08");
+insert into diaries values(2736, "2019-01-09");
+insert into diaries values(2737, "2019-01-10");
+insert into diaries values(2738, "2019-01-11");
+insert into diaries values(2739, "2019-01-12");
+insert into diaries values(2740, "2019-01-13");
+insert into diaries values(2741, "2019-01-14");
+insert into diaries values(2742, "2019-01-15");
+insert into diaries values(2743, "2019-01-16");
+insert into diaries values(2744, "2019-01-17");
+insert into diaries values(2745, "2019-01-18");
+insert into diaries values(2746, "2019-01-19");
+insert into diaries values(2747, "2019-01-20");
+insert into diaries values(2748, "2019-01-21");
+insert into diaries values(2749, "2019-01-22");
+insert into diaries values(2750, "2019-01-23");
+insert into diaries values(2751, "2019-01-24");
+insert into diaries values(2752, "2019-01-25");
+insert into diaries values(2753, "2019-01-26");
+insert into diaries values(2754, "2019-01-27");
+insert into diaries values(2755, "2019-01-28");
+insert into diaries values(2756, "2019-01-29");
+insert into diaries values(2757, "2019-01-30");
+insert into diaries values(2758, "2019-01-31");
+insert into diaries values(2759, "2019-02-01");
+insert into diaries values(2760, "2019-02-02");
+insert into diaries values(2761, "2019-02-03");
+insert into diaries values(2762, "2019-02-04");
+insert into diaries values(2763, "2019-02-05");
+insert into diaries values(2764, "2019-02-06");
+insert into diaries values(2765, "2019-02-07");
+insert into diaries values(2766, "2019-02-08");
+insert into diaries values(2767, "2019-02-09");
+insert into diaries values(2768, "2019-02-10");
+insert into diaries values(2769, "2019-02-11");
+insert into diaries values(2770, "2019-02-12");
+insert into diaries values(2771, "2019-02-13");
+insert into diaries values(2772, "2019-02-14");
+insert into diaries values(2773, "2019-02-15");
+insert into diaries values(2774, "2019-02-16");
+insert into diaries values(2775, "2019-02-17");
+insert into diaries values(2776, "2019-02-18");
+insert into diaries values(2777, "2019-02-19");
+insert into diaries values(2778, "2019-02-20");
+insert into diaries values(2779, "2019-02-21");
+insert into diaries values(2780, "2019-02-22");
+insert into diaries values(2781, "2019-02-23");
+insert into diaries values(2782, "2019-02-24");
+insert into diaries values(2783, "2019-02-25");
+insert into diaries values(2784, "2019-02-26");
+insert into diaries values(2785, "2019-02-27");
+insert into diaries values(2786, "2019-02-28");
+insert into diaries values(2787, "2019-03-01");
+insert into diaries values(2788, "2019-03-02");
+insert into diaries values(2789, "2019-03-03");
+insert into diaries values(2790, "2019-03-04");
+insert into diaries values(2791, "2019-03-05");
+insert into diaries values(2792, "2019-03-06");
+insert into diaries values(2793, "2019-03-07");
+insert into diaries values(2794, "2019-03-08");
+insert into diaries values(2795, "2019-03-09");
+insert into diaries values(2796, "2019-03-10");
+insert into diaries values(2797, "2019-03-11");
+insert into diaries values(2798, "2019-03-12");
+insert into diaries values(2799, "2019-03-13");
+insert into diaries values(2800, "2019-03-14");
+insert into diaries values(2801, "2019-03-15");
+insert into diaries values(2802, "2019-03-16");
+insert into diaries values(2803, "2019-03-17");
+insert into diaries values(2804, "2019-03-18");
+insert into diaries values(2805, "2019-03-19");
+insert into diaries values(2806, "2019-03-20");
+insert into diaries values(2807, "2019-03-21");
+insert into diaries values(2808, "2019-03-22");
+insert into diaries values(2809, "2019-03-23");
+insert into diaries values(2810, "2019-03-24");
+insert into diaries values(2811, "2019-03-25");
+insert into diaries values(2812, "2019-03-26");
+insert into diaries values(2813, "2019-03-27");
+insert into diaries values(2814, "2019-03-28");
+insert into diaries values(2815, "2019-03-29");
+insert into diaries values(2816, "2019-03-30");
+insert into diaries values(2817, "2019-03-31");
+insert into diaries values(2818, "2019-04-01");
+insert into diaries values(2819, "2019-04-02");
+insert into diaries values(2820, "2019-04-03");
+insert into diaries values(2821, "2019-04-04");
+insert into diaries values(2822, "2019-04-05");
+insert into diaries values(2823, "2019-04-06");
+insert into diaries values(2824, "2019-04-07");
+insert into diaries values(2825, "2019-04-08");
+insert into diaries values(2826, "2019-04-09");
+insert into diaries values(2827, "2019-04-10");
+insert into diaries values(2828, "2019-04-11");
+insert into diaries values(2829, "2019-04-12");
+insert into diaries values(2830, "2019-04-13");
+insert into diaries values(2831, "2019-04-14");
+insert into diaries values(2832, "2019-04-15");
+insert into diaries values(2833, "2019-04-16");
+insert into diaries values(2834, "2019-04-17");
+insert into diaries values(2835, "2019-04-18");
+insert into diaries values(2836, "2019-04-19");
+insert into diaries values(2837, "2019-04-20");
+insert into diaries values(2838, "2019-04-21");
+insert into diaries values(2839, "2019-04-22");
+insert into diaries values(2840, "2019-04-23");
+insert into diaries values(2841, "2019-04-24");
+insert into diaries values(2842, "2019-04-25");
+insert into diaries values(2843, "2019-04-26");
+insert into diaries values(2844, "2019-04-27");
+insert into diaries values(2845, "2019-04-28");
+insert into diaries values(2846, "2019-04-29");
+insert into diaries values(2847, "2019-04-30");
+insert into diaries values(2848, "2019-05-01");
+insert into diaries values(2849, "2019-05-02");
+insert into diaries values(2850, "2019-05-03");
+insert into diaries values(2851, "2019-05-04");
+insert into diaries values(2852, "2019-05-05");
+insert into diaries values(2853, "2019-05-06");
+insert into diaries values(2854, "2019-05-07");
+insert into diaries values(2855, "2019-05-08");
+insert into diaries values(2856, "2019-05-09");
+insert into diaries values(2857, "2019-05-10");
+insert into diaries values(2858, "2019-05-11");
+insert into diaries values(2859, "2019-05-12");
+insert into diaries values(2860, "2019-05-13");
+insert into diaries values(2861, "2019-05-14");
+insert into diaries values(2862, "2019-05-15");
+insert into diaries values(2863, "2019-05-16");
+insert into diaries values(2864, "2019-05-17");
+insert into diaries values(2865, "2019-05-18");
+insert into diaries values(2866, "2019-05-19");
+insert into diaries values(2867, "2019-05-20");
+insert into diaries values(2868, "2019-05-21");
+insert into diaries values(2869, "2019-05-22");
+insert into diaries values(2870, "2019-05-23");
+insert into diaries values(2871, "2019-05-24");
+insert into diaries values(2872, "2019-05-25");
+insert into diaries values(2873, "2019-05-26");
+insert into diaries values(2874, "2019-05-27");
+insert into diaries values(2875, "2019-05-28");
+insert into diaries values(2876, "2019-05-29");
+insert into diaries values(2877, "2019-05-30");
+insert into diaries values(2878, "2019-05-31");
+insert into diaries values(2879, "2019-06-01");
+insert into diaries values(2880, "2019-06-02");
+insert into diaries values(2881, "2019-06-03");
+insert into diaries values(2882, "2019-06-04");
+insert into diaries values(2883, "2019-06-05");
+insert into diaries values(2884, "2019-06-06");
+insert into diaries values(2885, "2019-06-07");
+insert into diaries values(2886, "2019-06-08");
+insert into diaries values(2887, "2019-06-09");
+insert into diaries values(2888, "2019-06-10");
+insert into diaries values(2889, "2019-06-11");
+insert into diaries values(2890, "2019-06-12");
+insert into diaries values(2891, "2019-06-13");
+insert into diaries values(2892, "2019-06-14");
+insert into diaries values(2893, "2019-06-15");
+insert into diaries values(2894, "2019-06-16");
+insert into diaries values(2895, "2019-06-17");
+insert into diaries values(2896, "2019-06-18");
+insert into diaries values(2897, "2019-06-19");
+insert into diaries values(2898, "2019-06-20");
+insert into diaries values(2899, "2019-06-21");
+insert into diaries values(2900, "2019-06-22");
+insert into diaries values(2901, "2019-06-23");
+insert into diaries values(2902, "2019-06-24");
+insert into diaries values(2903, "2019-06-25");
+insert into diaries values(2904, "2019-06-26");
+insert into diaries values(2905, "2019-06-27");
+insert into diaries values(2906, "2019-06-28");
+insert into diaries values(2907, "2019-06-29");
+insert into diaries values(2908, "2019-06-30");
+insert into diaries values(2909, "2019-07-01");
+insert into diaries values(2910, "2019-07-02");
+insert into diaries values(2911, "2019-07-03");
+insert into diaries values(2912, "2019-07-04");
+insert into diaries values(2913, "2019-07-05");
+insert into diaries values(2914, "2019-07-06");
+insert into diaries values(2915, "2019-07-07");
+insert into diaries values(2916, "2019-07-08");
+insert into diaries values(2917, "2019-07-09");
+insert into diaries values(2918, "2019-07-10");
+insert into diaries values(2919, "2019-07-11");
+insert into diaries values(2920, "2019-07-12");
+insert into diaries values(2921, "2019-07-13");
+insert into diaries values(2922, "2019-07-14");
+insert into diaries values(2923, "2019-07-15");
+insert into diaries values(2924, "2019-07-16");
+insert into diaries values(2925, "2019-07-17");
+insert into diaries values(2926, "2019-07-18");
+insert into diaries values(2927, "2019-07-19");
+insert into diaries values(2928, "2019-07-20");
+insert into diaries values(2929, "2019-07-21");
+insert into diaries values(2930, "2019-07-22");
+insert into diaries values(2931, "2019-07-23");
+insert into diaries values(2932, "2019-07-24");
+insert into diaries values(2933, "2019-07-25");
+insert into diaries values(2934, "2019-07-26");
+insert into diaries values(2935, "2019-07-27");
+insert into diaries values(2936, "2019-07-28");
+insert into diaries values(2937, "2019-07-29");
+insert into diaries values(2938, "2019-07-30");
+insert into diaries values(2939, "2019-07-31");
+insert into diaries values(2940, "2019-08-01");
+insert into diaries values(2941, "2019-08-02");
+insert into diaries values(2942, "2019-08-03");
+insert into diaries values(2943, "2019-08-04");
+insert into diaries values(2944, "2019-08-05");
+insert into diaries values(2945, "2019-08-06");
+insert into diaries values(2946, "2019-08-07");
+insert into diaries values(2947, "2019-08-08");
+insert into diaries values(2948, "2019-08-09");
+insert into diaries values(2949, "2019-08-10");
+insert into diaries values(2950, "2019-08-11");
+insert into diaries values(2951, "2019-08-12");
+insert into diaries values(2952, "2019-08-13");
+insert into diaries values(2953, "2019-08-14");
+insert into diaries values(2954, "2019-08-15");
+insert into diaries values(2955, "2019-08-16");
+insert into diaries values(2956, "2019-08-17");
+insert into diaries values(2957, "2019-08-18");
+insert into diaries values(2958, "2019-08-19");
+insert into diaries values(2959, "2019-08-20");
+insert into diaries values(2960, "2019-08-21");
+insert into diaries values(2961, "2019-08-22");
+insert into diaries values(2962, "2019-08-23");
+insert into diaries values(2963, "2019-08-24");
+insert into diaries values(2964, "2019-08-25");
+insert into diaries values(2965, "2019-08-26");
+insert into diaries values(2966, "2019-08-27");
+insert into diaries values(2967, "2019-08-28");
+insert into diaries values(2968, "2019-08-29");
+insert into diaries values(2969, "2019-08-30");
+insert into diaries values(2970, "2019-08-31");
+insert into diaries values(2971, "2019-09-01");
+insert into diaries values(2972, "2019-09-02");
+insert into diaries values(2973, "2019-09-03");
+insert into diaries values(2974, "2019-09-04");
+insert into diaries values(2975, "2019-09-05");
+insert into diaries values(2976, "2019-09-06");
+insert into diaries values(2977, "2019-09-07");
+insert into diaries values(2978, "2019-09-08");
+insert into diaries values(2979, "2019-09-09");
+insert into diaries values(2980, "2019-09-10");
+insert into diaries values(2981, "2019-09-11");
+insert into diaries values(2982, "2019-09-12");
+insert into diaries values(2983, "2019-09-13");
+insert into diaries values(2984, "2019-09-14");
+insert into diaries values(2985, "2019-09-15");
+insert into diaries values(2986, "2019-09-16");
+insert into diaries values(2987, "2019-09-17");
+insert into diaries values(2988, "2019-09-18");
+insert into diaries values(2989, "2019-09-19");
+insert into diaries values(2990, "2019-09-20");
+insert into diaries values(2991, "2019-09-21");
+insert into diaries values(2992, "2019-09-22");
+insert into diaries values(2993, "2019-09-23");
+insert into diaries values(2994, "2019-09-24");
+insert into diaries values(2995, "2019-09-25");
+insert into diaries values(2996, "2019-09-26");
+insert into diaries values(2997, "2019-09-27");
+insert into diaries values(2998, "2019-09-28");
+insert into diaries values(2999, "2019-09-29");
+insert into diaries values(3000, "2019-09-30");
+insert into diaries values(3001, "2019-10-01");
+insert into diaries values(3002, "2019-10-02");
+insert into diaries values(3003, "2019-10-03");
+insert into diaries values(3004, "2019-10-04");
+insert into diaries values(3005, "2019-10-05");
+insert into diaries values(3006, "2019-10-06");
+insert into diaries values(3007, "2019-10-07");
+insert into diaries values(3008, "2019-10-08");
+insert into diaries values(3009, "2019-10-09");
+insert into diaries values(3010, "2019-10-10");
+insert into diaries values(3011, "2019-10-11");
+insert into diaries values(3012, "2019-10-12");
+insert into diaries values(3013, "2019-10-13");
+insert into diaries values(3014, "2019-10-14");
+insert into diaries values(3015, "2019-10-15");
+insert into diaries values(3016, "2019-10-16");
+insert into diaries values(3017, "2019-10-17");
+insert into diaries values(3018, "2019-10-18");
+insert into diaries values(3019, "2019-10-19");
+insert into diaries values(3020, "2019-10-20");
+insert into diaries values(3021, "2019-10-21");
+insert into diaries values(3022, "2019-10-22");
+insert into diaries values(3023, "2019-10-23");
+insert into diaries values(3024, "2019-10-24");
+insert into diaries values(3025, "2019-10-25");
+insert into diaries values(3026, "2019-10-26");
+insert into diaries values(3027, "2019-10-27");
+insert into diaries values(3028, "2019-10-28");
+insert into diaries values(3029, "2019-10-29");
+insert into diaries values(3030, "2019-10-30");
+insert into diaries values(3031, "2019-10-31");
+insert into diaries values(3032, "2019-11-01");
+insert into diaries values(3033, "2019-11-02");
+insert into diaries values(3034, "2019-11-03");
+insert into diaries values(3035, "2019-11-04");
+insert into diaries values(3036, "2019-11-05");
+insert into diaries values(3037, "2019-11-06");
+insert into diaries values(3038, "2019-11-07");
+insert into diaries values(3039, "2019-11-08");
+insert into diaries values(3040, "2019-11-09");
+insert into diaries values(3041, "2019-11-10");
+insert into diaries values(3042, "2019-11-11");
+insert into diaries values(3043, "2019-11-12");
+insert into diaries values(3044, "2019-11-13");
+insert into diaries values(3045, "2019-11-14");
+insert into diaries values(3046, "2019-11-15");
+insert into diaries values(3047, "2019-11-16");
+insert into diaries values(3048, "2019-11-17");
+insert into diaries values(3049, "2019-11-18");
+insert into diaries values(3050, "2019-11-19");
+insert into diaries values(3051, "2019-11-20");
+insert into diaries values(3052, "2019-11-21");
+insert into diaries values(3053, "2019-11-22");
+insert into diaries values(3054, "2019-11-23");
+insert into diaries values(3055, "2019-11-24");
+insert into diaries values(3056, "2019-11-25");
+insert into diaries values(3057, "2019-11-26");
+insert into diaries values(3058, "2019-11-27");
+insert into diaries values(3059, "2019-11-28");
+insert into diaries values(3060, "2019-11-29");
+insert into diaries values(3061, "2019-11-30");
+insert into diaries values(3062, "2019-12-01");
+insert into diaries values(3063, "2019-12-02");
+insert into diaries values(3064, "2019-12-03");
+insert into diaries values(3065, "2019-12-04");
+insert into diaries values(3066, "2019-12-05");
+insert into diaries values(3067, "2019-12-06");
+insert into diaries values(3068, "2019-12-07");
+insert into diaries values(3069, "2019-12-08");
+insert into diaries values(3070, "2019-12-09");
+insert into diaries values(3071, "2019-12-10");
+insert into diaries values(3072, "2019-12-11");
+insert into diaries values(3073, "2019-12-12");
+insert into diaries values(3074, "2019-12-13");
+insert into diaries values(3075, "2019-12-14");
+insert into diaries values(3076, "2019-12-15");
+insert into diaries values(3077, "2019-12-16");
+insert into diaries values(3078, "2019-12-17");
+insert into diaries values(3079, "2019-12-18");
+insert into diaries values(3080, "2019-12-19");
+insert into diaries values(3081, "2019-12-20");
+insert into diaries values(3082, "2019-12-21");
+insert into diaries values(3083, "2019-12-22");
+insert into diaries values(3084, "2019-12-23");
+insert into diaries values(3085, "2019-12-24");
+insert into diaries values(3086, "2019-12-25");
+insert into diaries values(3087, "2019-12-26");
+insert into diaries values(3088, "2019-12-27");
+insert into diaries values(3089, "2019-12-28");
+insert into diaries values(3090, "2019-12-29");
+insert into diaries values(3091, "2019-12-30");
+insert into diaries values(3092, "2019-12-31");
+insert into diaries values(3093, "2020-01-01");
+insert into diaries values(3094, "2020-01-02");
+insert into diaries values(3095, "2020-01-03");
+insert into diaries values(3096, "2020-01-04");
+insert into diaries values(3097, "2020-01-05");
+insert into diaries values(3098, "2020-01-06");
+insert into diaries values(3099, "2020-01-07");
+insert into diaries values(3100, "2020-01-08");
+insert into diaries values(3101, "2020-01-09");
+insert into diaries values(3102, "2020-01-10");
+insert into diaries values(3103, "2020-01-11");
+insert into diaries values(3104, "2020-01-12");
+insert into diaries values(3105, "2020-01-13");
+insert into diaries values(3106, "2020-01-14");
+insert into diaries values(3107, "2020-01-15");
+insert into diaries values(3108, "2020-01-16");
+insert into diaries values(3109, "2020-01-17");
+insert into diaries values(3110, "2020-01-18");
+insert into diaries values(3111, "2020-01-19");
+insert into diaries values(3112, "2020-01-20");
+insert into diaries values(3113, "2020-01-21");
+insert into diaries values(3114, "2020-01-22");
+insert into diaries values(3115, "2020-01-23");
+insert into diaries values(3116, "2020-01-24");
+insert into diaries values(3117, "2020-01-25");
+insert into diaries values(3118, "2020-01-26");
+insert into diaries values(3119, "2020-01-27");
+insert into diaries values(3120, "2020-01-28");
+insert into diaries values(3121, "2020-01-29");
+insert into diaries values(3122, "2020-01-30");
+insert into diaries values(3123, "2020-01-31");
+insert into diaries values(3124, "2020-02-01");
+insert into diaries values(3125, "2020-02-02");
+insert into diaries values(3126, "2020-02-03");
+insert into diaries values(3127, "2020-02-04");
+insert into diaries values(3128, "2020-02-05");
+insert into diaries values(3129, "2020-02-06");
+insert into diaries values(3130, "2020-02-07");
+insert into diaries values(3131, "2020-02-08");
+insert into diaries values(3132, "2020-02-09");
+insert into diaries values(3133, "2020-02-10");
+insert into diaries values(3134, "2020-02-11");
+insert into diaries values(3135, "2020-02-12");
+insert into diaries values(3136, "2020-02-13");
+insert into diaries values(3137, "2020-02-14");
+insert into diaries values(3138, "2020-02-15");
+insert into diaries values(3139, "2020-02-16");
+insert into diaries values(3140, "2020-02-17");
+insert into diaries values(3141, "2020-02-18");
+insert into diaries values(3142, "2020-02-19");
+insert into diaries values(3143, "2020-02-20");
+insert into diaries values(3144, "2020-02-21");
+insert into diaries values(3145, "2020-02-22");
+insert into diaries values(3146, "2020-02-23");
+insert into diaries values(3147, "2020-02-24");
+insert into diaries values(3148, "2020-02-25");
+insert into diaries values(3149, "2020-02-26");
+insert into diaries values(3150, "2020-02-27");
+insert into diaries values(3151, "2020-02-28");
+insert into diaries values(3152, "2020-02-29");
+insert into diaries values(3153, "2020-03-01");
+insert into diaries values(3154, "2020-03-02");
+insert into diaries values(3155, "2020-03-03");
+insert into diaries values(3156, "2020-03-04");
+insert into diaries values(3157, "2020-03-05");
+insert into diaries values(3158, "2020-03-06");
+insert into diaries values(3159, "2020-03-07");
+insert into diaries values(3160, "2020-03-08");
+insert into diaries values(3161, "2020-03-09");
+insert into diaries values(3162, "2020-03-10");
+insert into diaries values(3163, "2020-03-11");
+insert into diaries values(3164, "2020-03-12");
+insert into diaries values(3165, "2020-03-13");
+insert into diaries values(3166, "2020-03-14");
+insert into diaries values(3167, "2020-03-15");
+insert into diaries values(3168, "2020-03-16");
+insert into diaries values(3169, "2020-03-17");
+insert into diaries values(3170, "2020-03-18");
+insert into diaries values(3171, "2020-03-19");
+insert into diaries values(3172, "2020-03-20");
+insert into diaries values(3173, "2020-03-21");
+insert into diaries values(3174, "2020-03-22");
+insert into diaries values(3175, "2020-03-23");
+insert into diaries values(3176, "2020-03-24");
+insert into diaries values(3177, "2020-03-25");
+insert into diaries values(3178, "2020-03-26");
+insert into diaries values(3179, "2020-03-27");
+insert into diaries values(3180, "2020-03-28");
+insert into diaries values(3181, "2020-03-29");
+insert into diaries values(3182, "2020-03-30");
+insert into diaries values(3183, "2020-03-31");
+insert into diaries values(3184, "2020-04-01");
+insert into diaries values(3185, "2020-04-02");
+insert into diaries values(3186, "2020-04-03");
+insert into diaries values(3187, "2020-04-04");
+insert into diaries values(3188, "2020-04-05");
+insert into diaries values(3189, "2020-04-06");
+insert into diaries values(3190, "2020-04-07");
+insert into diaries values(3191, "2020-04-08");
+insert into diaries values(3192, "2020-04-09");
+insert into diaries values(3193, "2020-04-10");
+insert into diaries values(3194, "2020-04-11");
+insert into diaries values(3195, "2020-04-12");
+insert into diaries values(3196, "2020-04-13");
+insert into diaries values(3197, "2020-04-14");
+insert into diaries values(3198, "2020-04-15");
+insert into diaries values(3199, "2020-04-16");
+insert into diaries values(3200, "2020-04-17");
+insert into diaries values(3201, "2020-04-18");
+insert into diaries values(3202, "2020-04-19");
+insert into diaries values(3203, "2020-04-20");
+insert into diaries values(3204, "2020-04-21");
+insert into diaries values(3205, "2020-04-22");
+insert into diaries values(3206, "2020-04-23");
+insert into diaries values(3207, "2020-04-24");
+insert into diaries values(3208, "2020-04-25");
+insert into diaries values(3209, "2020-04-26");
+insert into diaries values(3210, "2020-04-27");
+insert into diaries values(3211, "2020-04-28");
+insert into diaries values(3212, "2020-04-29");
+insert into diaries values(3213, "2020-04-30");
+insert into diaries values(3214, "2020-05-01");
+insert into diaries values(3215, "2020-05-02");
+insert into diaries values(3216, "2020-05-03");
+insert into diaries values(3217, "2020-05-04");
+insert into diaries values(3218, "2020-05-05");
+insert into diaries values(3219, "2020-05-06");
+insert into diaries values(3220, "2020-05-07");
+insert into diaries values(3221, "2020-05-08");
+insert into diaries values(3222, "2020-05-09");
+insert into diaries values(3223, "2020-05-10");
+insert into diaries values(3224, "2020-05-11");
+insert into diaries values(3225, "2020-05-12");
+insert into diaries values(3226, "2020-05-13");
+insert into diaries values(3227, "2020-05-14");
+insert into diaries values(3228, "2020-05-15");
+insert into diaries values(3229, "2020-05-16");
+insert into diaries values(3230, "2020-05-17");
+insert into diaries values(3231, "2020-05-18");
+insert into diaries values(3232, "2020-05-19");
+insert into diaries values(3233, "2020-05-20");
+insert into diaries values(3234, "2020-05-21");
+insert into diaries values(3235, "2020-05-22");
+insert into diaries values(3236, "2020-05-23");
+insert into diaries values(3237, "2020-05-24");
+insert into diaries values(3238, "2020-05-25");
+insert into diaries values(3239, "2020-05-26");
+insert into diaries values(3240, "2020-05-27");
+insert into diaries values(3241, "2020-05-28");
+insert into diaries values(3242, "2020-05-29");
+insert into diaries values(3243, "2020-05-30");
+insert into diaries values(3244, "2020-05-31");
+insert into diaries values(3245, "2020-06-01");
+insert into diaries values(3246, "2020-06-02");
+insert into diaries values(3247, "2020-06-03");
+insert into diaries values(3248, "2020-06-04");
+insert into diaries values(3249, "2020-06-05");
+insert into diaries values(3250, "2020-06-06");
+insert into diaries values(3251, "2020-06-07");
+insert into diaries values(3252, "2020-06-08");
+insert into diaries values(3253, "2020-06-09");
+insert into diaries values(3254, "2020-06-10");
+insert into diaries values(3255, "2020-06-11");
+insert into diaries values(3256, "2020-06-12");
+insert into diaries values(3257, "2020-06-13");
+insert into diaries values(3258, "2020-06-14");
+insert into diaries values(3259, "2020-06-15");
+insert into diaries values(3260, "2020-06-16");
+insert into diaries values(3261, "2020-06-17");
+insert into diaries values(3262, "2020-06-18");
+insert into diaries values(3263, "2020-06-19");
+insert into diaries values(3264, "2020-06-20");
+insert into diaries values(3265, "2020-06-21");
+insert into diaries values(3266, "2020-06-22");
+insert into diaries values(3267, "2020-06-23");
+insert into diaries values(3268, "2020-06-24");
+insert into diaries values(3269, "2020-06-25");
+insert into diaries values(3270, "2020-06-26");
+insert into diaries values(3271, "2020-06-27");
+insert into diaries values(3272, "2020-06-28");
+insert into diaries values(3273, "2020-06-29");
+insert into diaries values(3274, "2020-06-30");
+insert into diaries values(3275, "2020-07-01");
+insert into diaries values(3276, "2020-07-02");
+insert into diaries values(3277, "2020-07-03");
+insert into diaries values(3278, "2020-07-04");
+insert into diaries values(3279, "2020-07-05");
+insert into diaries values(3280, "2020-07-06");
+insert into diaries values(3281, "2020-07-07");
+insert into diaries values(3282, "2020-07-08");
+insert into diaries values(3283, "2020-07-09");
+insert into diaries values(3284, "2020-07-10");
+insert into diaries values(3285, "2020-07-11");
+insert into diaries values(3286, "2020-07-12");
+insert into diaries values(3287, "2020-07-13");
+insert into diaries values(3288, "2020-07-14");
+insert into diaries values(3289, "2020-07-15");
+insert into diaries values(3290, "2020-07-16");
+insert into diaries values(3291, "2020-07-17");
+insert into diaries values(3292, "2020-07-18");
+insert into diaries values(3293, "2020-07-19");
+insert into diaries values(3294, "2020-07-20");
+insert into diaries values(3295, "2020-07-21");
+insert into diaries values(3296, "2020-07-22");
+insert into diaries values(3297, "2020-07-23");
+insert into diaries values(3298, "2020-07-24");
+insert into diaries values(3299, "2020-07-25");
+insert into diaries values(3300, "2020-07-26");
+insert into diaries values(3301, "2020-07-27");
+insert into diaries values(3302, "2020-07-28");
+insert into diaries values(3303, "2020-07-29");
+insert into diaries values(3304, "2020-07-30");
+insert into diaries values(3305, "2020-07-31");
+insert into diaries values(3306, "2020-08-01");
+insert into diaries values(3307, "2020-08-02");
+insert into diaries values(3308, "2020-08-03");
+insert into diaries values(3309, "2020-08-04");
+insert into diaries values(3310, "2020-08-05");
+insert into diaries values(3311, "2020-08-06");
+insert into diaries values(3312, "2020-08-07");
+insert into diaries values(3313, "2020-08-08");
+insert into diaries values(3314, "2020-08-09");
+insert into diaries values(3315, "2020-08-10");
+insert into diaries values(3316, "2020-08-11");
+insert into diaries values(3317, "2020-08-12");
+insert into diaries values(3318, "2020-08-13");
+insert into diaries values(3319, "2020-08-14");
+insert into diaries values(3320, "2020-08-15");
+insert into diaries values(3321, "2020-08-16");
+insert into diaries values(3322, "2020-08-17");
+insert into diaries values(3323, "2020-08-18");
+insert into diaries values(3324, "2020-08-19");
+insert into diaries values(3325, "2020-08-20");
+insert into diaries values(3326, "2020-08-21");
+insert into diaries values(3327, "2020-08-22");
+insert into diaries values(3328, "2020-08-23");
+insert into diaries values(3329, "2020-08-24");
+insert into diaries values(3330, "2020-08-25");
+insert into diaries values(3331, "2020-08-26");
+insert into diaries values(3332, "2020-08-27");
+insert into diaries values(3333, "2020-08-28");
+insert into diaries values(3334, "2020-08-29");
+insert into diaries values(3335, "2020-08-30");
+insert into diaries values(3336, "2020-08-31");
+insert into diaries values(3337, "2020-09-01");
+insert into diaries values(3338, "2020-09-02");
+insert into diaries values(3339, "2020-09-03");
+insert into diaries values(3340, "2020-09-04");
+insert into diaries values(3341, "2020-09-05");
+insert into diaries values(3342, "2020-09-06");
+insert into diaries values(3343, "2020-09-07");
+insert into diaries values(3344, "2020-09-08");
+insert into diaries values(3345, "2020-09-09");
+insert into diaries values(3346, "2020-09-10");
+insert into diaries values(3347, "2020-09-11");
+insert into diaries values(3348, "2020-09-12");
+insert into diaries values(3349, "2020-09-13");
+insert into diaries values(3350, "2020-09-14");
+insert into diaries values(3351, "2020-09-15");
+insert into diaries values(3352, "2020-09-16");
+insert into diaries values(3353, "2020-09-17");
+insert into diaries values(3354, "2020-09-18");
+insert into diaries values(3355, "2020-09-19");
+insert into diaries values(3356, "2020-09-20");
+insert into diaries values(3357, "2020-09-21");
+insert into diaries values(3358, "2020-09-22");
+insert into diaries values(3359, "2020-09-23");
+insert into diaries values(3360, "2020-09-24");
+insert into diaries values(3361, "2020-09-25");
+insert into diaries values(3362, "2020-09-26");
+insert into diaries values(3363, "2020-09-27");
+insert into diaries values(3364, "2020-09-28");
+insert into diaries values(3365, "2020-09-29");
+insert into diaries values(3366, "2020-09-30");
+insert into diaries values(3367, "2020-10-01");
+insert into diaries values(3368, "2020-10-02");
+insert into diaries values(3369, "2020-10-03");
+insert into diaries values(3370, "2020-10-04");
+insert into diaries values(3371, "2020-10-05");
+insert into diaries values(3372, "2020-10-06");
+insert into diaries values(3373, "2020-10-07");
+insert into diaries values(3374, "2020-10-08");
+insert into diaries values(3375, "2020-10-09");
+insert into diaries values(3376, "2020-10-10");
+insert into diaries values(3377, "2020-10-11");
+insert into diaries values(3378, "2020-10-12");
+insert into diaries values(3379, "2020-10-13");
+insert into diaries values(3380, "2020-10-14");
+insert into diaries values(3381, "2020-10-15");
+insert into diaries values(3382, "2020-10-16");
+insert into diaries values(3383, "2020-10-17");
+insert into diaries values(3384, "2020-10-18");
+insert into diaries values(3385, "2020-10-19");
+insert into diaries values(3386, "2020-10-20");
+insert into diaries values(3387, "2020-10-21");
+insert into diaries values(3388, "2020-10-22");
+insert into diaries values(3389, "2020-10-23");
+insert into diaries values(3390, "2020-10-24");
+insert into diaries values(3391, "2020-10-25");
+insert into diaries values(3392, "2020-10-26");
+insert into diaries values(3393, "2020-10-27");
+insert into diaries values(3394, "2020-10-28");
+insert into diaries values(3395, "2020-10-29");
+insert into diaries values(3396, "2020-10-30");
+insert into diaries values(3397, "2020-10-31");
+insert into diaries values(3398, "2020-11-01");
+insert into diaries values(3399, "2020-11-02");
+insert into diaries values(3400, "2020-11-03");
+insert into diaries values(3401, "2020-11-04");
+insert into diaries values(3402, "2020-11-05");
+insert into diaries values(3403, "2020-11-06");
+insert into diaries values(3404, "2020-11-07");
+insert into diaries values(3405, "2020-11-08");
+insert into diaries values(3406, "2020-11-09");
+insert into diaries values(3407, "2020-11-10");
+insert into diaries values(3408, "2020-11-11");
+insert into diaries values(3409, "2020-11-12");
+insert into diaries values(3410, "2020-11-13");
+insert into diaries values(3411, "2020-11-14");
+insert into diaries values(3412, "2020-11-15");
+insert into diaries values(3413, "2020-11-16");
+insert into diaries values(3414, "2020-11-17");
+insert into diaries values(3415, "2020-11-18");
+insert into diaries values(3416, "2020-11-19");
+insert into diaries values(3417, "2020-11-20");
+insert into diaries values(3418, "2020-11-21");
+insert into diaries values(3419, "2020-11-22");
+insert into diaries values(3420, "2020-11-23");
+insert into diaries values(3421, "2020-11-24");
+insert into diaries values(3422, "2020-11-25");
+insert into diaries values(3423, "2020-11-26");
+insert into diaries values(3424, "2020-11-27");
+insert into diaries values(3425, "2020-11-28");
+insert into diaries values(3426, "2020-11-29");
+insert into diaries values(3427, "2020-11-30");
+insert into diaries values(3428, "2020-12-01");
+insert into diaries values(3429, "2020-12-02");
+insert into diaries values(3430, "2020-12-03");
+insert into diaries values(3431, "2020-12-04");
+insert into diaries values(3432, "2020-12-05");
+insert into diaries values(3433, "2020-12-06");
+insert into diaries values(3434, "2020-12-07");
+insert into diaries values(3435, "2020-12-08");
+insert into diaries values(3436, "2020-12-09");
+insert into diaries values(3437, "2020-12-10");
+insert into diaries values(3438, "2020-12-11");
+insert into diaries values(3439, "2020-12-12");
+insert into diaries values(3440, "2020-12-13");
+insert into diaries values(3441, "2020-12-14");
+insert into diaries values(3442, "2020-12-15");
+insert into diaries values(3443, "2020-12-16");
+insert into diaries values(3444, "2020-12-17");
+insert into diaries values(3445, "2020-12-18");
+insert into diaries values(3446, "2020-12-19");
+insert into diaries values(3447, "2020-12-20");
+insert into diaries values(3448, "2020-12-21");
+insert into diaries values(3449, "2020-12-22");
+insert into diaries values(3450, "2020-12-23");
+insert into diaries values(3451, "2020-12-24");
+insert into diaries values(3452, "2020-12-25");
+insert into diaries values(3453, "2020-12-26");
+insert into diaries values(3454, "2020-12-27");
+insert into diaries values(3455, "2020-12-28");
+insert into diaries values(3456, "2020-12-29");
+insert into diaries values(3457, "2020-12-30");
+insert into diaries values(3458, "2020-12-31");
+insert into diaries values(3459, "2021-01-01");
+insert into diaries values(3460, "2021-01-02");
+insert into diaries values(3461, "2021-01-03");
+insert into diaries values(3462, "2021-01-04");
+insert into diaries values(3463, "2021-01-05");
+insert into diaries values(3464, "2021-01-06");
+insert into diaries values(3465, "2021-01-07");
+insert into diaries values(3466, "2021-01-08");
+insert into diaries values(3467, "2021-01-09");
+insert into diaries values(3468, "2021-01-10");
+insert into diaries values(3469, "2021-01-11");
+insert into diaries values(3470, "2021-01-12");
+insert into diaries values(3471, "2021-01-13");
+insert into diaries values(3472, "2021-01-14");
+insert into diaries values(3473, "2021-01-15");
+insert into diaries values(3474, "2021-01-16");
+insert into diaries values(3475, "2021-01-17");
+insert into diaries values(3476, "2021-01-18");
+insert into diaries values(3477, "2021-01-19");
+insert into diaries values(3478, "2021-01-20");
+insert into diaries values(3479, "2021-01-21");
+insert into diaries values(3480, "2021-01-22");
+insert into diaries values(3481, "2021-01-23");
+insert into diaries values(3482, "2021-01-24");
+insert into diaries values(3483, "2021-01-25");
+insert into diaries values(3484, "2021-01-26");
+insert into diaries values(3485, "2021-01-27");
+insert into diaries values(3486, "2021-01-28");
+insert into diaries values(3487, "2021-01-29");
+insert into diaries values(3488, "2021-01-30");
+insert into diaries values(3489, "2021-01-31");
+insert into diaries values(3490, "2021-02-01");
+insert into diaries values(3491, "2021-02-02");
+insert into diaries values(3492, "2021-02-03");
+insert into diaries values(3493, "2021-02-04");
+insert into diaries values(3494, "2021-02-05");
+insert into diaries values(3495, "2021-02-06");
+insert into diaries values(3496, "2021-02-07");
+insert into diaries values(3497, "2021-02-08");
+insert into diaries values(3498, "2021-02-09");
+insert into diaries values(3499, "2021-02-10");
+insert into diaries values(3500, "2021-02-11");
+insert into diaries values(3501, "2021-02-12");
+insert into diaries values(3502, "2021-02-13");
+insert into diaries values(3503, "2021-02-14");
+insert into diaries values(3504, "2021-02-15");
+insert into diaries values(3505, "2021-02-16");
+insert into diaries values(3506, "2021-02-17");
+insert into diaries values(3507, "2021-02-18");
+insert into diaries values(3508, "2021-02-19");
+insert into diaries values(3509, "2021-02-20");
+insert into diaries values(3510, "2021-02-21");
+insert into diaries values(3511, "2021-02-22");
+insert into diaries values(3512, "2021-02-23");
+insert into diaries values(3513, "2021-02-24");
+insert into diaries values(3514, "2021-02-25");
+insert into diaries values(3515, "2021-02-26");
+insert into diaries values(3516, "2021-02-27");
+insert into diaries values(3517, "2021-02-28");
+insert into diaries values(3518, "2021-03-01");
+insert into diaries values(3519, "2021-03-02");
+insert into diaries values(3520, "2021-03-03");
+insert into diaries values(3521, "2021-03-04");
+insert into diaries values(3522, "2021-03-05");
+insert into diaries values(3523, "2021-03-06");
+insert into diaries values(3524, "2021-03-07");
+insert into diaries values(3525, "2021-03-08");
+insert into diaries values(3526, "2021-03-09");
+insert into diaries values(3527, "2021-03-10");
+insert into diaries values(3528, "2021-03-11");
+insert into diaries values(3529, "2021-03-12");
+insert into diaries values(3530, "2021-03-13");
+insert into diaries values(3531, "2021-03-14");
+insert into diaries values(3532, "2021-03-15");
+insert into diaries values(3533, "2021-03-16");
+insert into diaries values(3534, "2021-03-17");
+insert into diaries values(3535, "2021-03-18");
+insert into diaries values(3536, "2021-03-19");
+insert into diaries values(3537, "2021-03-20");
+insert into diaries values(3538, "2021-03-21");
+insert into diaries values(3539, "2021-03-22");
+insert into diaries values(3540, "2021-03-23");
+insert into diaries values(3541, "2021-03-24");
+insert into diaries values(3542, "2021-03-25");
+insert into diaries values(3543, "2021-03-26");
+insert into diaries values(3544, "2021-03-27");
+insert into diaries values(3545, "2021-03-28");
+insert into diaries values(3546, "2021-03-29");
+insert into diaries values(3547, "2021-03-30");
+insert into diaries values(3548, "2021-03-31");
+insert into diaries values(3549, "2021-04-01");
+insert into diaries values(3550, "2021-04-02");
+insert into diaries values(3551, "2021-04-03");
+insert into diaries values(3552, "2021-04-04");
+insert into diaries values(3553, "2021-04-05");
+insert into diaries values(3554, "2021-04-06");
+insert into diaries values(3555, "2021-04-07");
+insert into diaries values(3556, "2021-04-08");
+insert into diaries values(3557, "2021-04-09");
+insert into diaries values(3558, "2021-04-10");
+insert into diaries values(3559, "2021-04-11");
+insert into diaries values(3560, "2021-04-12");
+insert into diaries values(3561, "2021-04-13");
+insert into diaries values(3562, "2021-04-14");
+insert into diaries values(3563, "2021-04-15");
+insert into diaries values(3564, "2021-04-16");
+insert into diaries values(3565, "2021-04-17");
+insert into diaries values(3566, "2021-04-18");
+insert into diaries values(3567, "2021-04-19");
+insert into diaries values(3568, "2021-04-20");
+insert into diaries values(3569, "2021-04-21");
+insert into diaries values(3570, "2021-04-22");
+insert into diaries values(3571, "2021-04-23");
+insert into diaries values(3572, "2021-04-24");
+insert into diaries values(3573, "2021-04-25");
+insert into diaries values(3574, "2021-04-26");
+insert into diaries values(3575, "2021-04-27");
+insert into diaries values(3576, "2021-04-28");
+insert into diaries values(3577, "2021-04-29");
+insert into diaries values(3578, "2021-04-30");
+insert into diaries values(3579, "2021-05-01");
+insert into diaries values(3580, "2021-05-02");
+insert into diaries values(3581, "2021-05-03");
+insert into diaries values(3582, "2021-05-04");
+insert into diaries values(3583, "2021-05-05");
+insert into diaries values(3584, "2021-05-06");
+insert into diaries values(3585, "2021-05-07");
+insert into diaries values(3586, "2021-05-08");
+insert into diaries values(3587, "2021-05-09");
+insert into diaries values(3588, "2021-05-10");
+insert into diaries values(3589, "2021-05-11");
+insert into diaries values(3590, "2021-05-12");
+insert into diaries values(3591, "2021-05-13");
+insert into diaries values(3592, "2021-05-14");
+insert into diaries values(3593, "2021-05-15");
+insert into diaries values(3594, "2021-05-16");
+insert into diaries values(3595, "2021-05-17");
+insert into diaries values(3596, "2021-05-18");
+insert into diaries values(3597, "2021-05-19");
+insert into diaries values(3598, "2021-05-20");
+insert into diaries values(3599, "2021-05-21");
+insert into diaries values(3600, "2021-05-22");
+insert into diaries values(3601, "2021-05-23");
+insert into diaries values(3602, "2021-05-24");
+insert into diaries values(3603, "2021-05-25");
+insert into diaries values(3604, "2021-05-26");
+insert into diaries values(3605, "2021-05-27");
+insert into diaries values(3606, "2021-05-28");
+insert into diaries values(3607, "2021-05-29");
+insert into diaries values(3608, "2021-05-30");
+insert into diaries values(3609, "2021-05-31");
+insert into diaries values(3610, "2021-06-01");
+insert into diaries values(3611, "2021-06-02");
+insert into diaries values(3612, "2021-06-03");
+insert into diaries values(3613, "2021-06-04");
+insert into diaries values(3614, "2021-06-05");
+insert into diaries values(3615, "2021-06-06");
+insert into diaries values(3616, "2021-06-07");
+insert into diaries values(3617, "2021-06-08");
+insert into diaries values(3618, "2021-06-09");
+insert into diaries values(3619, "2021-06-10");
+insert into diaries values(3620, "2021-06-11");
+insert into diaries values(3621, "2021-06-12");
+insert into diaries values(3622, "2021-06-13");
+insert into diaries values(3623, "2021-06-14");
+insert into diaries values(3624, "2021-06-15");
+insert into diaries values(3625, "2021-06-16");
+insert into diaries values(3626, "2021-06-17");
+insert into diaries values(3627, "2021-06-18");
+insert into diaries values(3628, "2021-06-19");
+insert into diaries values(3629, "2021-06-20");
+insert into diaries values(3630, "2021-06-21");
+insert into diaries values(3631, "2021-06-22");
+insert into diaries values(3632, "2021-06-23");
+insert into diaries values(3633, "2021-06-24");
+insert into diaries values(3634, "2021-06-25");
+insert into diaries values(3635, "2021-06-26");
+insert into diaries values(3636, "2021-06-27");
+insert into diaries values(3637, "2021-06-28");
+insert into diaries values(3638, "2021-06-29");
+insert into diaries values(3639, "2021-06-30");
+insert into diaries values(3640, "2021-07-01");
+insert into diaries values(3641, "2021-07-02");
+insert into diaries values(3642, "2021-07-03");
+insert into diaries values(3643, "2021-07-04");
+insert into diaries values(3644, "2021-07-05");
+insert into diaries values(3645, "2021-07-06");
+insert into diaries values(3646, "2021-07-07");
+insert into diaries values(3647, "2021-07-08");
+insert into diaries values(3648, "2021-07-09");
+insert into diaries values(3649, "2021-07-10");
+insert into diaries values(3650, "2021-07-11");
+insert into diaries values(3651, "2021-07-12");
+insert into diaries values(3652, "2021-07-13");
+insert into diaries values(3653, "2021-07-14");
+insert into diaries values(3654, "2021-07-15");
+insert into diaries values(3655, "2021-07-16");
+insert into diaries values(3656, "2021-07-17");
+insert into diaries values(3657, "2021-07-18");
+insert into diaries values(3658, "2021-07-19");
+insert into diaries values(3659, "2021-07-20");
+insert into diaries values(3660, "2021-07-21");
+insert into diaries values(3661, "2021-07-22");
+insert into diaries values(3662, "2021-07-23");
+insert into diaries values(3663, "2021-07-24");
+insert into diaries values(3664, "2021-07-25");
+insert into diaries values(3665, "2021-07-26");
+insert into diaries values(3666, "2021-07-27");
+insert into diaries values(3667, "2021-07-28");
+insert into diaries values(3668, "2021-07-29");
+insert into diaries values(3669, "2021-07-30");
+insert into diaries values(3670, "2021-07-31");
+insert into diaries values(3671, "2021-08-01");
+insert into diaries values(3672, "2021-08-02");
+insert into diaries values(3673, "2021-08-03");
+insert into diaries values(3674, "2021-08-04");
+insert into diaries values(3675, "2021-08-05");
+insert into diaries values(3676, "2021-08-06");
+insert into diaries values(3677, "2021-08-07");
+insert into diaries values(3678, "2021-08-08");
+insert into diaries values(3679, "2021-08-09");
+insert into diaries values(3680, "2021-08-10");
+insert into diaries values(3681, "2021-08-11");
+insert into diaries values(3682, "2021-08-12");
+insert into diaries values(3683, "2021-08-13");
+insert into diaries values(3684, "2021-08-14");
+insert into diaries values(3685, "2021-08-15");
+insert into diaries values(3686, "2021-08-16");
+insert into diaries values(3687, "2021-08-17");
+insert into diaries values(3688, "2021-08-18");
+insert into diaries values(3689, "2021-08-19");
+insert into diaries values(3690, "2021-08-20");
+insert into diaries values(3691, "2021-08-21");
+insert into diaries values(3692, "2021-08-22");
+insert into diaries values(3693, "2021-08-23");
+insert into diaries values(3694, "2021-08-24");
+insert into diaries values(3695, "2021-08-25");
+insert into diaries values(3696, "2021-08-26");
+insert into diaries values(3697, "2021-08-27");
+insert into diaries values(3698, "2021-08-28");
+insert into diaries values(3699, "2021-08-29");
+insert into diaries values(3700, "2021-08-30");
+insert into diaries values(3701, "2021-08-31");
+insert into diaries values(3702, "2021-09-01");
+insert into diaries values(3703, "2021-09-02");
+insert into diaries values(3704, "2021-09-03");
+insert into diaries values(3705, "2021-09-04");
+insert into diaries values(3706, "2021-09-05");
+insert into diaries values(3707, "2021-09-06");
+insert into diaries values(3708, "2021-09-07");
+insert into diaries values(3709, "2021-09-08");
+insert into diaries values(3710, "2021-09-09");
+insert into diaries values(3711, "2021-09-10");
+insert into diaries values(3712, "2021-09-11");
+insert into diaries values(3713, "2021-09-12");
+insert into diaries values(3714, "2021-09-13");
+insert into diaries values(3715, "2021-09-14");
+insert into diaries values(3716, "2021-09-15");
+insert into diaries values(3717, "2021-09-16");
+insert into diaries values(3718, "2021-09-17");
+insert into diaries values(3719, "2021-09-18");
+insert into diaries values(3720, "2021-09-19");
+insert into diaries values(3721, "2021-09-20");
+insert into diaries values(3722, "2021-09-21");
+insert into diaries values(3723, "2021-09-22");
+insert into diaries values(3724, "2021-09-23");
+insert into diaries values(3725, "2021-09-24");
+insert into diaries values(3726, "2021-09-25");
+insert into diaries values(3727, "2021-09-26");
+insert into diaries values(3728, "2021-09-27");
+insert into diaries values(3729, "2021-09-28");
+insert into diaries values(3730, "2021-09-29");
+insert into diaries values(3731, "2021-09-30");
+insert into diaries values(3732, "2021-10-01");
+insert into diaries values(3733, "2021-10-02");
+insert into diaries values(3734, "2021-10-03");
+insert into diaries values(3735, "2021-10-04");
+insert into diaries values(3736, "2021-10-05");
+insert into diaries values(3737, "2021-10-06");
+insert into diaries values(3738, "2021-10-07");
+insert into diaries values(3739, "2021-10-08");
+insert into diaries values(3740, "2021-10-09");
+insert into diaries values(3741, "2021-10-10");
+insert into diaries values(3742, "2021-10-11");
+insert into diaries values(3743, "2021-10-12");
+insert into diaries values(3744, "2021-10-13");
+insert into diaries values(3745, "2021-10-14");
+insert into diaries values(3746, "2021-10-15");
+insert into diaries values(3747, "2021-10-16");
+insert into diaries values(3748, "2021-10-17");
+insert into diaries values(3749, "2021-10-18");
+insert into diaries values(3750, "2021-10-19");
+insert into diaries values(3751, "2021-10-20");
+insert into diaries values(3752, "2021-10-21");
+insert into diaries values(3753, "2021-10-22");
+insert into diaries values(3754, "2021-10-23");
+insert into diaries values(3755, "2021-10-24");
+insert into diaries values(3756, "2021-10-25");
+insert into diaries values(3757, "2021-10-26");
+insert into diaries values(3758, "2021-10-27");
+insert into diaries values(3759, "2021-10-28");
+insert into diaries values(3760, "2021-10-29");
+insert into diaries values(3761, "2021-10-30");
+insert into diaries values(3762, "2021-10-31");
+insert into diaries values(3763, "2021-11-01");
+insert into diaries values(3764, "2021-11-02");
+insert into diaries values(3765, "2021-11-03");
+insert into diaries values(3766, "2021-11-04");
+insert into diaries values(3767, "2021-11-05");
+insert into diaries values(3768, "2021-11-06");
+insert into diaries values(3769, "2021-11-07");
+insert into diaries values(3770, "2021-11-08");
+insert into diaries values(3771, "2021-11-09");
+insert into diaries values(3772, "2021-11-10");
+insert into diaries values(3773, "2021-11-11");
+insert into diaries values(3774, "2021-11-12");
+insert into diaries values(3775, "2021-11-13");
+insert into diaries values(3776, "2021-11-14");
+insert into diaries values(3777, "2021-11-15");
+insert into diaries values(3778, "2021-11-16");
+insert into diaries values(3779, "2021-11-17");
+insert into diaries values(3780, "2021-11-18");
+insert into diaries values(3781, "2021-11-19");
+insert into diaries values(3782, "2021-11-20");
+insert into diaries values(3783, "2021-11-21");
+insert into diaries values(3784, "2021-11-22");
+insert into diaries values(3785, "2021-11-23");
+insert into diaries values(3786, "2021-11-24");
+insert into diaries values(3787, "2021-11-25");
+insert into diaries values(3788, "2021-11-26");
+insert into diaries values(3789, "2021-11-27");
+insert into diaries values(3790, "2021-11-28");
+insert into diaries values(3791, "2021-11-29");
+insert into diaries values(3792, "2021-11-30");
+insert into diaries values(3793, "2021-12-01");
+insert into diaries values(3794, "2021-12-02");
+insert into diaries values(3795, "2021-12-03");
+insert into diaries values(3796, "2021-12-04");
+insert into diaries values(3797, "2021-12-05");
+insert into diaries values(3798, "2021-12-06");
+insert into diaries values(3799, "2021-12-07");
+insert into diaries values(3800, "2021-12-08");
+insert into diaries values(3801, "2021-12-09");
+insert into diaries values(3802, "2021-12-10");
+insert into diaries values(3803, "2021-12-11");
+insert into diaries values(3804, "2021-12-12");
+insert into diaries values(3805, "2021-12-13");
+insert into diaries values(3806, "2021-12-14");
+insert into diaries values(3807, "2021-12-15");
+insert into diaries values(3808, "2021-12-16");
+insert into diaries values(3809, "2021-12-17");
+insert into diaries values(3810, "2021-12-18");
+insert into diaries values(3811, "2021-12-19");
+insert into diaries values(3812, "2021-12-20");
+insert into diaries values(3813, "2021-12-21");
+insert into diaries values(3814, "2021-12-22");
+insert into diaries values(3815, "2021-12-23");
+insert into diaries values(3816, "2021-12-24");
+insert into diaries values(3817, "2021-12-25");
+insert into diaries values(3818, "2021-12-26");
+insert into diaries values(3819, "2021-12-27");
+insert into diaries values(3820, "2021-12-28");
+insert into diaries values(3821, "2021-12-29");
+insert into diaries values(3822, "2021-12-30");
+insert into diaries values(3823, "2021-12-31");
+insert into diaries values(3824, "2022-01-01");
+insert into diaries values(3825, "2022-01-02");
+insert into diaries values(3826, "2022-01-03");
+insert into diaries values(3827, "2022-01-04");
+insert into diaries values(3828, "2022-01-05");
+insert into diaries values(3829, "2022-01-06");
+insert into diaries values(3830, "2022-01-07");
+insert into diaries values(3831, "2022-01-08");
+insert into diaries values(3832, "2022-01-09");
+insert into diaries values(3833, "2022-01-10");
+insert into diaries values(3834, "2022-01-11");
+insert into diaries values(3835, "2022-01-12");
+insert into diaries values(3836, "2022-01-13");
+insert into diaries values(3837, "2022-01-14");
+insert into diaries values(3838, "2022-01-15");
+insert into diaries values(3839, "2022-01-16");
+insert into diaries values(3840, "2022-01-17");
+insert into diaries values(3841, "2022-01-18");
+insert into diaries values(3842, "2022-01-19");
+insert into diaries values(3843, "2022-01-20");
+insert into diaries values(3844, "2022-01-21");
+insert into diaries values(3845, "2022-01-22");
+insert into diaries values(3846, "2022-01-23");
+insert into diaries values(3847, "2022-01-24");
+insert into diaries values(3848, "2022-01-25");
+insert into diaries values(3849, "2022-01-26");
+insert into diaries values(3850, "2022-01-27");
+insert into diaries values(3851, "2022-01-28");
+insert into diaries values(3852, "2022-01-29");
+insert into diaries values(3853, "2022-01-30");
+insert into diaries values(3854, "2022-01-31");
+insert into diaries values(3855, "2022-02-01");
+insert into diaries values(3856, "2022-02-02");
+insert into diaries values(3857, "2022-02-03");
+insert into diaries values(3858, "2022-02-04");
+insert into diaries values(3859, "2022-02-05");
+insert into diaries values(3860, "2022-02-06");
+insert into diaries values(3861, "2022-02-07");
+insert into diaries values(3862, "2022-02-08");
+insert into diaries values(3863, "2022-02-09");
+insert into diaries values(3864, "2022-02-10");
+insert into diaries values(3865, "2022-02-11");
+insert into diaries values(3866, "2022-02-12");
+insert into diaries values(3867, "2022-02-13");
+insert into diaries values(3868, "2022-02-14");
+insert into diaries values(3869, "2022-02-15");
+insert into diaries values(3870, "2022-02-16");
+insert into diaries values(3871, "2022-02-17");
+insert into diaries values(3872, "2022-02-18");
+insert into diaries values(3873, "2022-02-19");
+insert into diaries values(3874, "2022-02-20");
+insert into diaries values(3875, "2022-02-21");
+insert into diaries values(3876, "2022-02-22");
+insert into diaries values(3877, "2022-02-23");
+insert into diaries values(3878, "2022-02-24");
+insert into diaries values(3879, "2022-02-25");
+insert into diaries values(3880, "2022-02-26");
+insert into diaries values(3881, "2022-02-27");
+insert into diaries values(3882, "2022-02-28");
+insert into diaries values(3883, "2022-03-01");
+insert into diaries values(3884, "2022-03-02");
+insert into diaries values(3885, "2022-03-03");
+insert into diaries values(3886, "2022-03-04");
+insert into diaries values(3887, "2022-03-05");
+insert into diaries values(3888, "2022-03-06");
+insert into diaries values(3889, "2022-03-07");
+insert into diaries values(3890, "2022-03-08");
+insert into diaries values(3891, "2022-03-09");
+insert into diaries values(3892, "2022-03-10");
+insert into diaries values(3893, "2022-03-11");
+insert into diaries values(3894, "2022-03-12");
+insert into diaries values(3895, "2022-03-13");
+insert into diaries values(3896, "2022-03-14");
+insert into diaries values(3897, "2022-03-15");
+insert into diaries values(3898, "2022-03-16");
+insert into diaries values(3899, "2022-03-17");
+insert into diaries values(3900, "2022-03-18");
+insert into diaries values(3901, "2022-03-19");
+insert into diaries values(3902, "2022-03-20");
+insert into diaries values(3903, "2022-03-21");
+insert into diaries values(3904, "2022-03-22");
+insert into diaries values(3905, "2022-03-23");
+insert into diaries values(3906, "2022-03-24");
+insert into diaries values(3907, "2022-03-25");
+insert into diaries values(3908, "2022-03-26");
+insert into diaries values(3909, "2022-03-27");
+insert into diaries values(3910, "2022-03-28");
+insert into diaries values(3911, "2022-03-29");
+insert into diaries values(3912, "2022-03-30");
+insert into diaries values(3913, "2022-03-31");
+insert into diaries values(3914, "2022-04-01");
+insert into diaries values(3915, "2022-04-02");
+insert into diaries values(3916, "2022-04-03");
+insert into diaries values(3917, "2022-04-04");
+insert into diaries values(3918, "2022-04-05");
+insert into diaries values(3919, "2022-04-06");
+insert into diaries values(3920, "2022-04-07");
+insert into diaries values(3921, "2022-04-08");
+insert into diaries values(3922, "2022-04-09");
+insert into diaries values(3923, "2022-04-10");
+insert into diaries values(3924, "2022-04-11");
+insert into diaries values(3925, "2022-04-12");
+insert into diaries values(3926, "2022-04-13");
+insert into diaries values(3927, "2022-04-14");
+insert into diaries values(3928, "2022-04-15");
+insert into diaries values(3929, "2022-04-16");
+insert into diaries values(3930, "2022-04-17");
+insert into diaries values(3931, "2022-04-18");
+insert into diaries values(3932, "2022-04-19");
+insert into diaries values(3933, "2022-04-20");
+insert into diaries values(3934, "2022-04-21");
+insert into diaries values(3935, "2022-04-22");
+insert into diaries values(3936, "2022-04-23");
+insert into diaries values(3937, "2022-04-24");
+insert into diaries values(3938, "2022-04-25");
+insert into diaries values(3939, "2022-04-26");
+insert into diaries values(3940, "2022-04-27");
+insert into diaries values(3941, "2022-04-28");
+insert into diaries values(3942, "2022-04-29");
+insert into diaries values(3943, "2022-04-30");
+insert into diaries values(3944, "2022-05-01");
+insert into diaries values(3945, "2022-05-02");
+insert into diaries values(3946, "2022-05-03");
+insert into diaries values(3947, "2022-05-04");
+insert into diaries values(3948, "2022-05-05");
+insert into diaries values(3949, "2022-05-06");
+insert into diaries values(3950, "2022-05-07");
+insert into diaries values(3951, "2022-05-08");
+insert into diaries values(3952, "2022-05-09");
+insert into diaries values(3953, "2022-05-10");
+insert into diaries values(3954, "2022-05-11");
+insert into diaries values(3955, "2022-05-12");
+insert into diaries values(3956, "2022-05-13");
+insert into diaries values(3957, "2022-05-14");
+insert into diaries values(3958, "2022-05-15");
+insert into diaries values(3959, "2022-05-16");
+insert into diaries values(3960, "2022-05-17");
+insert into diaries values(3961, "2022-05-18");
+insert into diaries values(3962, "2022-05-19");
+insert into diaries values(3963, "2022-05-20");
+insert into diaries values(3964, "2022-05-21");
+insert into diaries values(3965, "2022-05-22");
+insert into diaries values(3966, "2022-05-23");
+insert into diaries values(3967, "2022-05-24");
+insert into diaries values(3968, "2022-05-25");
+insert into diaries values(3969, "2022-05-26");
+insert into diaries values(3970, "2022-05-27");
+insert into diaries values(3971, "2022-05-28");
+insert into diaries values(3972, "2022-05-29");
+insert into diaries values(3973, "2022-05-30");
+insert into diaries values(3974, "2022-05-31");
+insert into diaries values(3975, "2022-06-01");
+insert into diaries values(3976, "2022-06-02");
+insert into diaries values(3977, "2022-06-03");
+insert into diaries values(3978, "2022-06-04");
+insert into diaries values(3979, "2022-06-05");
+insert into diaries values(3980, "2022-06-06");
+insert into diaries values(3981, "2022-06-07");
+insert into diaries values(3982, "2022-06-08");
+insert into diaries values(3983, "2022-06-09");
+insert into diaries values(3984, "2022-06-10");
+insert into diaries values(3985, "2022-06-11");
+insert into diaries values(3986, "2022-06-12");
+insert into diaries values(3987, "2022-06-13");
+insert into diaries values(3988, "2022-06-14");
+insert into diaries values(3989, "2022-06-15");
+insert into diaries values(3990, "2022-06-16");
+insert into diaries values(3991, "2022-06-17");
+insert into diaries values(3992, "2022-06-18");
+insert into diaries values(3993, "2022-06-19");
+insert into diaries values(3994, "2022-06-20");
+insert into diaries values(3995, "2022-06-21");
+insert into diaries values(3996, "2022-06-22");
+insert into diaries values(3997, "2022-06-23");
+insert into diaries values(3998, "2022-06-24");
+insert into diaries values(3999, "2022-06-25");
+insert into diaries values(4000, "2022-06-26");
+insert into diaries values(4001, "2022-06-27");
+insert into diaries values(4002, "2022-06-28");
+insert into diaries values(4003, "2022-06-29");
+insert into diaries values(4004, "2022-06-30");
+insert into diaries values(4005, "2022-07-01");
+insert into diaries values(4006, "2022-07-02");
+insert into diaries values(4007, "2022-07-03");
+insert into diaries values(4008, "2022-07-04");
+insert into diaries values(4009, "2022-07-05");
+insert into diaries values(4010, "2022-07-06");
+insert into diaries values(4011, "2022-07-07");
+insert into diaries values(4012, "2022-07-08");
+insert into diaries values(4013, "2022-07-09");
+insert into diaries values(4014, "2022-07-10");
+insert into diaries values(4015, "2022-07-11");
+insert into diaries values(4016, "2022-07-12");
+insert into diaries values(4017, "2022-07-13");
+insert into diaries values(4018, "2022-07-14");
+insert into diaries values(4019, "2022-07-15");
+insert into diaries values(4020, "2022-07-16");
+insert into diaries values(4021, "2022-07-17");
+insert into diaries values(4022, "2022-07-18");
+insert into diaries values(4023, "2022-07-19");
+insert into diaries values(4024, "2022-07-20");
+insert into diaries values(4025, "2022-07-21");
+insert into diaries values(4026, "2022-07-22");
+insert into diaries values(4027, "2022-07-23");
+insert into diaries values(4028, "2022-07-24");
+insert into diaries values(4029, "2022-07-25");
+insert into diaries values(4030, "2022-07-26");
+insert into diaries values(4031, "2022-07-27");
+insert into diaries values(4032, "2022-07-28");
+insert into diaries values(4033, "2022-07-29");
+insert into diaries values(4034, "2022-07-30");
+insert into diaries values(4035, "2022-07-31");
+insert into diaries values(4036, "2022-08-01");
+insert into diaries values(4037, "2022-08-02");
+insert into diaries values(4038, "2022-08-03");
+insert into diaries values(4039, "2022-08-04");
+insert into diaries values(4040, "2022-08-05");
+insert into diaries values(4041, "2022-08-06");
+insert into diaries values(4042, "2022-08-07");
+insert into diaries values(4043, "2022-08-08");
+insert into diaries values(4044, "2022-08-09");
+insert into diaries values(4045, "2022-08-10");
+insert into diaries values(4046, "2022-08-11");
+insert into diaries values(4047, "2022-08-12");
+insert into diaries values(4048, "2022-08-13");
+insert into diaries values(4049, "2022-08-14");
+insert into diaries values(4050, "2022-08-15");
+insert into diaries values(4051, "2022-08-16");
+insert into diaries values(4052, "2022-08-17");
+insert into diaries values(4053, "2022-08-18");
+insert into diaries values(4054, "2022-08-19");
+insert into diaries values(4055, "2022-08-20");
+insert into diaries values(4056, "2022-08-21");
+insert into diaries values(4057, "2022-08-22");
+insert into diaries values(4058, "2022-08-23");
+insert into diaries values(4059, "2022-08-24");
+insert into diaries values(4060, "2022-08-25");
+insert into diaries values(4061, "2022-08-26");
+insert into diaries values(4062, "2022-08-27");
+insert into diaries values(4063, "2022-08-28");
+insert into diaries values(4064, "2022-08-29");
+insert into diaries values(4065, "2022-08-30");
+insert into diaries values(4066, "2022-08-31");
+insert into diaries values(4067, "2022-09-01");
+insert into diaries values(4068, "2022-09-02");
+insert into diaries values(4069, "2022-09-03");
+insert into diaries values(4070, "2022-09-04");
+insert into diaries values(4071, "2022-09-05");
+insert into diaries values(4072, "2022-09-06");
+insert into diaries values(4073, "2022-09-07");
+insert into diaries values(4074, "2022-09-08");
+insert into diaries values(4075, "2022-09-09");
+insert into diaries values(4076, "2022-09-10");
+insert into diaries values(4077, "2022-09-11");
+insert into diaries values(4078, "2022-09-12");
+insert into diaries values(4079, "2022-09-13");
+insert into diaries values(4080, "2022-09-14");
+insert into diaries values(4081, "2022-09-15");
+insert into diaries values(4082, "2022-09-16");
+insert into diaries values(4083, "2022-09-17");
+insert into diaries values(4084, "2022-09-18");
+insert into diaries values(4085, "2022-09-19");
+insert into diaries values(4086, "2022-09-20");
+insert into diaries values(4087, "2022-09-21");
+insert into diaries values(4088, "2022-09-22");
+insert into diaries values(4089, "2022-09-23");
+insert into diaries values(4090, "2022-09-24");
+insert into diaries values(4091, "2022-09-25");
+insert into diaries values(4092, "2022-09-26");
+insert into diaries values(4093, "2022-09-27");
+insert into diaries values(4094, "2022-09-28");
+insert into diaries values(4095, "2022-09-29");
+commit;
+set autocommit=1;
+select * from diaries where match(title) against("2022-09-0");
+id title
+3824 2022-01-01
+3825 2022-01-02
+3826 2022-01-03
+3827 2022-01-04
+3828 2022-01-05
+3829 2022-01-06
+3830 2022-01-07
+3831 2022-01-08
+3832 2022-01-09
+3833 2022-01-10
+3834 2022-01-11
+3835 2022-01-12
+3836 2022-01-13
+3837 2022-01-14
+3838 2022-01-15
+3839 2022-01-16
+3840 2022-01-17
+3841 2022-01-18
+3842 2022-01-19
+3843 2022-01-20
+3844 2022-01-21
+3845 2022-01-22
+3846 2022-01-23
+3847 2022-01-24
+3848 2022-01-25
+3849 2022-01-26
+3850 2022-01-27
+3851 2022-01-28
+3852 2022-01-29
+3853 2022-01-30
+3854 2022-01-31
+3855 2022-02-01
+3856 2022-02-02
+3857 2022-02-03
+3858 2022-02-04
+3859 2022-02-05
+3860 2022-02-06
+3861 2022-02-07
+3862 2022-02-08
+3863 2022-02-09
+3864 2022-02-10
+3865 2022-02-11
+3866 2022-02-12
+3867 2022-02-13
+3868 2022-02-14
+3869 2022-02-15
+3870 2022-02-16
+3871 2022-02-17
+3872 2022-02-18
+3873 2022-02-19
+3874 2022-02-20
+3875 2022-02-21
+3876 2022-02-22
+3877 2022-02-23
+3878 2022-02-24
+3879 2022-02-25
+3880 2022-02-26
+3881 2022-02-27
+3882 2022-02-28
+3883 2022-03-01
+3884 2022-03-02
+3885 2022-03-03
+3886 2022-03-04
+3887 2022-03-05
+3888 2022-03-06
+3889 2022-03-07
+3890 2022-03-08
+3891 2022-03-09
+3892 2022-03-10
+3893 2022-03-11
+3894 2022-03-12
+3895 2022-03-13
+3896 2022-03-14
+3897 2022-03-15
+3898 2022-03-16
+3899 2022-03-17
+3900 2022-03-18
+3901 2022-03-19
+3902 2022-03-20
+3903 2022-03-21
+3904 2022-03-22
+3905 2022-03-23
+3906 2022-03-24
+3907 2022-03-25
+3908 2022-03-26
+3909 2022-03-27
+3910 2022-03-28
+3911 2022-03-29
+3912 2022-03-30
+3913 2022-03-31
+3914 2022-04-01
+3915 2022-04-02
+3916 2022-04-03
+3917 2022-04-04
+3918 2022-04-05
+3919 2022-04-06
+3920 2022-04-07
+3921 2022-04-08
+3922 2022-04-09
+3923 2022-04-10
+3924 2022-04-11
+3925 2022-04-12
+3926 2022-04-13
+3927 2022-04-14
+3928 2022-04-15
+3929 2022-04-16
+3930 2022-04-17
+3931 2022-04-18
+3932 2022-04-19
+3933 2022-04-20
+3934 2022-04-21
+3935 2022-04-22
+3936 2022-04-23
+3937 2022-04-24
+3938 2022-04-25
+3939 2022-04-26
+3940 2022-04-27
+3941 2022-04-28
+3942 2022-04-29
+3943 2022-04-30
+3944 2022-05-01
+3945 2022-05-02
+3946 2022-05-03
+3947 2022-05-04
+3948 2022-05-05
+3949 2022-05-06
+3950 2022-05-07
+3951 2022-05-08
+3952 2022-05-09
+3953 2022-05-10
+3954 2022-05-11
+3955 2022-05-12
+3956 2022-05-13
+3957 2022-05-14
+3958 2022-05-15
+3959 2022-05-16
+3960 2022-05-17
+3961 2022-05-18
+3962 2022-05-19
+3963 2022-05-20
+3964 2022-05-21
+3965 2022-05-22
+3966 2022-05-23
+3967 2022-05-24
+3968 2022-05-25
+3969 2022-05-26
+3970 2022-05-27
+3971 2022-05-28
+3972 2022-05-29
+3973 2022-05-30
+3974 2022-05-31
+3975 2022-06-01
+3976 2022-06-02
+3977 2022-06-03
+3978 2022-06-04
+3979 2022-06-05
+3980 2022-06-06
+3981 2022-06-07
+3982 2022-06-08
+3983 2022-06-09
+3984 2022-06-10
+3985 2022-06-11
+3986 2022-06-12
+3987 2022-06-13
+3988 2022-06-14
+3989 2022-06-15
+3990 2022-06-16
+3991 2022-06-17
+3992 2022-06-18
+3993 2022-06-19
+3994 2022-06-20
+3995 2022-06-21
+3996 2022-06-22
+3997 2022-06-23
+3998 2022-06-24
+3999 2022-06-25
+4000 2022-06-26
+4001 2022-06-27
+4002 2022-06-28
+4003 2022-06-29
+4004 2022-06-30
+4005 2022-07-01
+4006 2022-07-02
+4007 2022-07-03
+4008 2022-07-04
+4009 2022-07-05
+4010 2022-07-06
+4011 2022-07-07
+4012 2022-07-08
+4013 2022-07-09
+4014 2022-07-10
+4015 2022-07-11
+4016 2022-07-12
+4017 2022-07-13
+4018 2022-07-14
+4019 2022-07-15
+4020 2022-07-16
+4021 2022-07-17
+4022 2022-07-18
+4023 2022-07-19
+4024 2022-07-20
+4025 2022-07-21
+4026 2022-07-22
+4027 2022-07-23
+4028 2022-07-24
+4029 2022-07-25
+4030 2022-07-26
+4031 2022-07-27
+4032 2022-07-28
+4033 2022-07-29
+4034 2022-07-30
+4035 2022-07-31
+4036 2022-08-01
+4037 2022-08-02
+4038 2022-08-03
+4039 2022-08-04
+4040 2022-08-05
+4041 2022-08-06
+4042 2022-08-07
+4043 2022-08-08
+4044 2022-08-09
+4045 2022-08-10
+4046 2022-08-11
+4047 2022-08-12
+4048 2022-08-13
+4049 2022-08-14
+4050 2022-08-15
+4051 2022-08-16
+4052 2022-08-17
+4053 2022-08-18
+4054 2022-08-19
+4055 2022-08-20
+4056 2022-08-21
+4057 2022-08-22
+4058 2022-08-23
+4059 2022-08-24
+4060 2022-08-25
+4061 2022-08-26
+4062 2022-08-27
+4063 2022-08-28
+4064 2022-08-29
+4065 2022-08-30
+4066 2022-08-31
+4067 2022-09-01
+4068 2022-09-02
+4069 2022-09-03
+4070 2022-09-04
+4071 2022-09-05
+4072 2022-09-06
+4073 2022-09-07
+4074 2022-09-08
+4075 2022-09-09
+4076 2022-09-10
+4077 2022-09-11
+4078 2022-09-12
+4079 2022-09-13
+4080 2022-09-14
+4081 2022-09-15
+4082 2022-09-16
+4083 2022-09-17
+4084 2022-09-18
+4085 2022-09-19
+4086 2022-09-20
+4087 2022-09-21
+4088 2022-09-22
+4089 2022-09-23
+4090 2022-09-24
+4091 2022-09-25
+4092 2022-09-26
+4093 2022-09-27
+4094 2022-09-28
+4095 2022-09-29
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_delete.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_delete.result
new file mode 100644
index 00000000000..64acf4aaeae
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_delete.result
@@ -0,0 +1,34 @@
+drop table if exists diaries;
+set names utf8;
+create table diaries (
+id int primary key,
+title varchar(255),
+content text,
+fulltext index (title, content),
+fulltext index (title),
+fulltext index (content)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`,`content`),
+ FULLTEXT KEY `title_2` (`title`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+delete from diaries where id = 2;
+select * from diaries where match(title, content) against("富士山");
+id title content
+3 富士山 今日もきれい。
+select * from diaries where match(title) against("富士山");
+id title content
+3 富士山 今日もきれい。
+select * from diaries where match(content) against("富士山");
+id title content
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_insert.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_insert.result
new file mode 100644
index 00000000000..c1661d7fcbf
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_insert.result
@@ -0,0 +1,40 @@
+drop table if exists diaries;
+set names utf8;
+create table diaries (
+id int primary key,
+title varchar(255),
+content text,
+fulltext index (title, content),
+fulltext index (title),
+fulltext index (content)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`,`content`),
+ FULLTEXT KEY `title_2` (`title`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+select * from diaries;
+id title content
+1 Hello はじめました。
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+select * from diaries where match(title, content) against("富士山");
+id title content
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+select * from diaries where match(title) against("富士山");
+id title content
+3 富士山 今日もきれい。
+select * from diaries where match(content) against("富士山");
+id title content
+2 天気 明日の富士山の天気について
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_recreate.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_recreate.result
new file mode 100644
index 00000000000..604aca172a5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_recreate.result
@@ -0,0 +1,42 @@
+drop table if exists diaries;
+set names utf8;
+create table diaries (
+id int primary key,
+title varchar(255),
+content text,
+fulltext index (title, content),
+fulltext index (title),
+fulltext index (content)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`,`content`),
+ FULLTEXT KEY `title_2` (`title`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+select * from diaries where match(title, content) against("富士山");
+id title content
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+drop index title on diaries;
+select * from diaries where match(title, content) against("富士山");
+ERROR HY000: Can't find FULLTEXT index matching the column list
+create fulltext index new_title_content_index on diaries (title, content);
+select * from diaries where match(title, content) against("富士山");
+id title content
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+select * from diaries;
+id title content
+1 Hello はじめました。
+2 天気 明日の富士山の天気について
+3 富士山 今日もきれい。
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_update.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_update.result
new file mode 100644
index 00000000000..6f489bf37cc
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_column_index_update.result
@@ -0,0 +1,37 @@
+drop table if exists diaries;
+set names utf8;
+create table diaries (
+id int primary key,
+title varchar(255),
+content text,
+fulltext index (title, content),
+fulltext index (title),
+fulltext index (content)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title` (`title`,`content`),
+ FULLTEXT KEY `title_2` (`title`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+update diaries set title = "チョモランマ" where id = 3;
+update diaries set content = "チョモランマと富士山" where id = 1;
+select * from diaries where match(title, content) against("富士山");
+id title content
+1 Hello チョモランマと富士山
+2 天気 明日の富士山の天気について
+select * from diaries where match(title) against("富士山");
+id title content
+select * from diaries where match(content) against("富士山");
+id title content
+1 Hello チョモランマと富士山
+2 天気 明日の富士山の天気について
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_index.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_index.result
new file mode 100644
index 00000000000..d9448ee82ba
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_multiple_index.result
@@ -0,0 +1,33 @@
+drop table if exists diaries;
+create table diaries (
+id int primary key auto_increment,
+title text,
+body text,
+fulltext index title_index (title),
+fulltext index body_index (body)
+) comment = 'engine "innodb"' default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+insert into diaries (title, body) values ("survey", "will start groonga!");
+insert into diaries (title, body) values ("groonga (1)", "starting groonga...");
+insert into diaries (title, body) values ("groonga (2)", "started groonga.");
+select * from diaries
+where match(title) against("survey") and
+match(body) against("groonga");
+id title body
+1 survey will start groonga!
+select *, match(title) against("survey"), match(body) against("groonga")
+from diaries
+where match(title) against("survey") and
+match(body) against("groonga");
+id title body match(title) against("survey") match(body) against("groonga")
+1 survey will start groonga! 1048577 149797
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_myisam.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_myisam.result
new file mode 100644
index 00000000000..de1b8d41906
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_myisam.result
@@ -0,0 +1,202 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 text, fulltext index ft (c2)) COMMENT = 'engine "myisam"';
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `c1` int(11) NOT NULL,
+ `c2` text,
+ PRIMARY KEY (`c1`),
+ FULLTEXT KEY `ft` (`c2`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1 COMMENT='engine "myisam"'
+insert into t1 values (1, "hoge hoge");
+insert into t1 values (2, "fuga fuga");
+insert into t1 values (3, "moge moge");
+select * from t1;
+c1 c2
+1 hoge hoge
+2 fuga fuga
+3 moge moge
+flush tables;
+select * from t1;
+c1 c2
+1 hoge hoge
+2 fuga fuga
+3 moge moge
+drop table t1;
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3)) COMMENT = 'engine "myisam"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"sa si su se so");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ee oo");
+select * from t1;
+c1 c2 c3
+1 10 aa ii uu ee oo
+2 20 ka ki ku ke ko
+3 30 sa si su se so
+4 40 ta ti tu te to
+5 50 aa ii uu ee oo
+select * from t1 where match(c3) against("su");
+c1 c2 c3
+3 30 sa si su se so
+select * from t1 where match(c3) against("ii");
+c1 c2 c3
+1 10 aa ii uu ee oo
+5 50 aa ii uu ee oo
+select * from t1 where match(c3) against("+su" in boolean mode);
+c1 c2 c3
+3 30 sa si su se so
+select * from t1 where match(c3) against("+ii" in boolean mode);
+c1 c2 c3
+1 10 aa ii uu ee oo
+5 50 aa ii uu ee oo
+drop table t1;
+set names utf8;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset utf8 COMMENT = 'engine "myisam"';
+insert into t1 values(1, "明日の富士山の天気について","あああああああ");
+insert into t1 values(2, "いいいいい","明日の富士山の天気は分かりません");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+c1 c2 c3
+1 明日の富士山の天気について あああああああ
+2 いいいいい 明日の富士山の天気は分かりません
+3 dummy dummy
+select * from t1 where match(c2) against("富士山");
+c1 c2 c3
+1 明日の富士山の天気について あああああああ
+select * from t1 where match(c3) against("富士山");
+c1 c2 c3
+2 いいいいい 明日の富士山の天気は分かりません
+drop table t1;
+create table t1 (c1 int primary key, c2 varchar(100), fulltext index(c2)) default charset utf8 COMMENT = 'engine "myisam"';
+create table t2 (c1 int primary key, c2 text, fulltext index(c2)) default charset utf8 COMMENT = 'engine "myisam"';
+insert into t1 values (1, "aa ii uu ee oo");
+insert into t1 values (2, "ka ki ku ke ko");
+insert into t1 values (3, "aa ii ii ii oo");
+insert into t1 values (4, "sa si su se so");
+insert into t1 values (5, "ta ti ii ii to");
+insert into t2 (c1,c2) select c1,c2 from t1;
+select * from t1;
+c1 c2
+1 aa ii uu ee oo
+2 ka ki ku ke ko
+3 aa ii ii ii oo
+4 sa si su se so
+5 ta ti ii ii to
+select * from t2;
+c1 c2
+1 aa ii uu ee oo
+2 ka ki ku ke ko
+3 aa ii ii ii oo
+4 sa si su se so
+5 ta ti ii ii to
+select * from t1 where c1=3;
+c1 c2
+3 aa ii ii ii oo
+select * from t2 where c1=3;
+c1 c2
+3 aa ii ii ii oo
+select * from t1 where c1>3 order by c1 desc;
+c1 c2
+5 ta ti ii ii to
+4 sa si su se so
+select * from t2 where c1>3 order by c1 asc;
+c1 c2
+4 sa si su se so
+5 ta ti ii ii to
+select * from t1 where c2>"s" order by c2 desc;
+c1 c2
+5 ta ti ii ii to
+4 sa si su se so
+select * from t2 where c2>"s" order by c1 asc;
+c1 c2
+4 sa si su se so
+5 ta ti ii ii to
+select *,match(c2) against("ii") from t1 where match(c2) against("ii") order by match(c2) against("ii") desc;
+c1 c2 match(c2) against("ii")
+3 aa ii ii ii oo 524289
+5 ta ti ii ii to 349526
+1 aa ii uu ee oo 174763
+select *,match(c2) against("ii") from t2 where match(c2) against("ii") order by match(c2) against("ii") asc;
+c1 c2 match(c2) against("ii")
+1 aa ii uu ee oo 174763
+5 ta ti ii ii to 349526
+3 aa ii ii ii oo 524289
+select c1,c2,match(c2) against("ii") from t1 where match(c2) against("ii");
+c1 c2 match(c2) against("ii")
+1 aa ii uu ee oo 174763
+3 aa ii ii ii oo 524289
+5 ta ti ii ii to 349526
+select c1,c2,match(c2) against("ii") from t1 where match(c2) against("ii");
+c1 c2 match(c2) against("ii")
+1 aa ii uu ee oo 174763
+3 aa ii ii ii oo 524289
+5 ta ti ii ii to 349526
+drop table t1,t2;
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3)) COMMENT = 'engine "myisam"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,10,"ka ki ku ke ko");
+insert into t1 values(3,10,"aa ii uu ee oo");
+insert into t1 values(4,10,"ka ki ku ke ko");
+insert into t1 values(5,20,"aa ii uu ee oo");
+insert into t1 values(6,20,"ka ki ku ke ko");
+insert into t1 values(7,20,"aa ii uu ee oo");
+insert into t1 values(8,20,"ka ki ku ke ko");
+select * from t1;
+c1 c2 c3
+1 10 aa ii uu ee oo
+2 10 ka ki ku ke ko
+3 10 aa ii uu ee oo
+4 10 ka ki ku ke ko
+5 20 aa ii uu ee oo
+6 20 ka ki ku ke ko
+7 20 aa ii uu ee oo
+8 20 ka ki ku ke ko
+select *,match(c3) against("uu") from t1 where match(c3) against("uu");
+c1 c2 c3 match(c3) against("uu")
+1 10 aa ii uu ee oo 131073
+3 10 aa ii uu ee oo 131073
+5 20 aa ii uu ee oo 131073
+7 20 aa ii uu ee oo 131073
+select * from t1 where not match(c3) against("uu");
+c1 c2 c3
+2 10 ka ki ku ke ko
+4 10 ka ki ku ke ko
+6 20 ka ki ku ke ko
+8 20 ka ki ku ke ko
+select *,match(c3) against("dummy") from t1 where match(c3) against("dummy");
+c1 c2 c3 match(c3) against("dummy")
+select * from t1 where not match(c3) against("dummy");
+c1 c2 c3
+1 10 aa ii uu ee oo
+2 10 ka ki ku ke ko
+3 10 aa ii uu ee oo
+4 10 ka ki ku ke ko
+5 20 aa ii uu ee oo
+6 20 ka ki ku ke ko
+7 20 aa ii uu ee oo
+8 20 ka ki ku ke ko
+select * from t1 where c1 = 4 and not match(c3) against("uu");
+c1 c2 c3
+4 10 ka ki ku ke ko
+select * from t1 where c1 <= 4 and not match(c3) against("uu");
+c1 c2 c3
+2 10 ka ki ku ke ko
+4 10 ka ki ku ke ko
+select * from t1 where c1 > 4 and not match(c3) against("uu");
+c1 c2 c3
+6 20 ka ki ku ke ko
+8 20 ka ki ku ke ko
+select * from t1 where c2 = 10 and not match(c3) against("uu");
+c1 c2 c3
+2 10 ka ki ku ke ko
+4 10 ka ki ku ke ko
+select * from t1 where c2 >= 15 and not match(c3) against("uu");
+c1 c2 c3
+6 20 ka ki ku ke ko
+8 20 ka ki ku ke ko
+select * from t1 where c2 < 15 and not match(c3) against("uu");
+c1 c2 c3
+2 10 ka ki ku ke ko
+4 10 ka ki ku ke ko
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_not_match_against.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_not_match_against.result
new file mode 100644
index 00000000000..bbe23df3e0f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_not_match_against.result
@@ -0,0 +1,68 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3)) COMMENT = 'engine "innodb"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,10,"ka ki ku ke ko");
+insert into t1 values(3,10,"aa ii uu ee oo");
+insert into t1 values(4,10,"ka ki ku ke ko");
+insert into t1 values(5,20,"aa ii uu ee oo");
+insert into t1 values(6,20,"ka ki ku ke ko");
+insert into t1 values(7,20,"aa ii uu ee oo");
+insert into t1 values(8,20,"ka ki ku ke ko");
+select * from t1;
+c1 c2 c3
+1 10 aa ii uu ee oo
+2 10 ka ki ku ke ko
+3 10 aa ii uu ee oo
+4 10 ka ki ku ke ko
+5 20 aa ii uu ee oo
+6 20 ka ki ku ke ko
+7 20 aa ii uu ee oo
+8 20 ka ki ku ke ko
+select *,match(c3) against("uu") from t1 where match(c3) against("uu");
+c1 c2 c3 match(c3) against("uu")
+1 10 aa ii uu ee oo 131073
+3 10 aa ii uu ee oo 131073
+5 20 aa ii uu ee oo 131073
+7 20 aa ii uu ee oo 131073
+select * from t1 where not match(c3) against("uu");
+c1 c2 c3
+2 10 ka ki ku ke ko
+4 10 ka ki ku ke ko
+6 20 ka ki ku ke ko
+8 20 ka ki ku ke ko
+select *,match(c3) against("dummy") from t1 where match(c3) against("dummy");
+c1 c2 c3 match(c3) against("dummy")
+select * from t1 where not match(c3) against("dummy");
+c1 c2 c3
+1 10 aa ii uu ee oo
+2 10 ka ki ku ke ko
+3 10 aa ii uu ee oo
+4 10 ka ki ku ke ko
+5 20 aa ii uu ee oo
+6 20 ka ki ku ke ko
+7 20 aa ii uu ee oo
+8 20 ka ki ku ke ko
+select * from t1 where c1 = 4 and not match(c3) against("uu");
+c1 c2 c3
+4 10 ka ki ku ke ko
+select * from t1 where c1 <= 4 and not match(c3) against("uu");
+c1 c2 c3
+2 10 ka ki ku ke ko
+4 10 ka ki ku ke ko
+select * from t1 where c1 > 4 and not match(c3) against("uu");
+c1 c2 c3
+6 20 ka ki ku ke ko
+8 20 ka ki ku ke ko
+select * from t1 where c2 = 10 and not match(c3) against("uu");
+c1 c2 c3
+2 10 ka ki ku ke ko
+4 10 ka ki ku ke ko
+select * from t1 where c2 >= 15 and not match(c3) against("uu");
+c1 c2 c3
+6 20 ka ki ku ke ko
+8 20 ka ki ku ke ko
+select * from t1 where c2 < 15 and not match(c3) against("uu");
+c1 c2 c3
+2 10 ka ki ku ke ko
+4 10 ka ki ku ke ko
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_order_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_order_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..279ad8bf985
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_order_TODO_SPLIT_ME.result
@@ -0,0 +1,50 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE ft(
+a INT,
+b TEXT,
+c TEXT,
+PRIMARY KEY(a),
+FULLTEXT KEY ftx1(b),
+FULLTEXT KEY ftx2(c)
+)ENGINE=Mroonga DEFAULT CHARSET=UTF8 COMMENT = 'engine "innodb"';
+SHOW CREATE TABLE ft;
+Table Create Table
+ft CREATE TABLE `ft` (
+ `a` int(11) NOT NULL DEFAULT '0',
+ `b` text,
+ `c` text,
+ PRIMARY KEY (`a`),
+ FULLTEXT KEY `ftx1` (`b`),
+ FULLTEXT KEY `ftx2` (`c`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+INSERT INTO ft VALUES(1,'aaaaa','abcde');
+INSERT INTO ft VALUES(2,'bbbbb','bcdef');
+INSERT INTO ft VALUES(3,'ccccc','cdefg');
+INSERT INTO ft VALUES(4,'ddddd','defgh');
+INSERT INTO ft VALUES(5,'eeeee','efghi');
+SELECT a, b, c FROM ft WHERE MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE);
+a b c
+2 bbbbb bcdef
+SELECT a, b, c FROM ft WHERE MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE) ORDER BY MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE);
+a b c
+2 bbbbb bcdef
+SELECT a, b, c FROM ft WHERE MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE) ORDER BY MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE);
+a b c
+2 bbbbb bcdef
+SELECT a, b, c FROM ft WHERE MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE);
+a b c
+SELECT a, b, c, MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE), MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE) FROM ft WHERE MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE) ORDER BY MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE);
+a b c MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE) MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE)
+2 bbbbb bcdef 1 0
+SELECT a, b, c, MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE), MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE) FROM ft WHERE MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE);
+a b c MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE) MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE)
+2 bbbbb bcdef 1 0
+SELECT a, b, c, MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE), MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE) FROM ft ORDER BY MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE), MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE), a;
+a b c MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE) MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE)
+1 aaaaa abcde 0 0
+3 ccccc cdefg 0 0
+4 ddddd defgh 0 0
+5 eeeee efghi 0 0
+2 bbbbb bcdef 1 0
+DROP TABLE ft;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_order_transaction.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_order_transaction.result
new file mode 100644
index 00000000000..3da64b2de1f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_order_transaction.result
@@ -0,0 +1,50 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT,
+FULLTEXT INDEX title_index (title),
+FULLTEXT INDEX body_index (body)
+) COMMENT = 'ENGINE "InnoDB"' DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+START TRANSACTION;
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+SELECT * FROM diaries
+WHERE MATCH(body) AGAINST("groonga")
+ORDER BY id;
+id title body
+1 survey will start groonga!
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+USE test;
+SELECT * FROM diaries
+WHERE MATCH(body) AGAINST("groonga")
+ORDER BY id;
+id title body
+COMMIT;
+SELECT * FROM diaries
+WHERE MATCH(body) AGAINST("groonga")
+ORDER BY id;
+id title body
+1 survey will start groonga!
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+SELECT * FROM diaries
+WHERE MATCH(body) AGAINST("groonga")
+ORDER BY id;
+id title body
+1 survey will start groonga!
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_parser_comment.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_parser_comment.result
new file mode 100644
index 00000000000..d7b20a3714f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/fulltext_parser_comment.result
@@ -0,0 +1,29 @@
+drop table if exists diaries;
+create table diaries (
+id int primary key auto_increment,
+body text,
+fulltext index body_index (body)
+comment 'parser "TokenBigramSplitSymbolAlphaDigit"'
+) comment = 'engine "innodb"' default charset utf8;
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `body_index` (`body`) COMMENT 'parser "TokenBigramSplitSymbolAlphaDigit"'
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+insert into diaries (body) values ("will start groonga!");
+insert into diaries (body) values ("starting groonga...");
+insert into diaries (body) values ("started groonga.");
+select * from diaries;
+id body
+1 will start groonga!
+2 starting groonga...
+3 started groonga.
+select * from diaries where match(body) against("start");
+id body
+1 will start groonga!
+2 starting groonga...
+3 started groonga.
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/function_last_insert_grn_id.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/function_last_insert_grn_id.result
new file mode 100644
index 00000000000..9edbd3c9b3b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/function_last_insert_grn_id.result
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS ids;
+DROP FUNCTION IF EXISTS last_insert_grn_id;
+CREATE TABLE ids (
+id int AUTO_INCREMENT PRIMARY KEY
+) COMMENT='ENGINE "InnoDB"';
+SELECT last_insert_grn_id();
+last_insert_grn_id()
+0
+INSERT INTO ids VALUES();
+SELECT last_insert_grn_id();
+last_insert_grn_id()
+0
+SELECT * FROM ids;
+id
+1
+DROP TABLE ids;
+DROP FUNCTION last_insert_grn_id;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/function_last_insert_id_reference.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/function_last_insert_id_reference.result
new file mode 100644
index 00000000000..d31d5454169
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/function_last_insert_id_reference.result
@@ -0,0 +1,15 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id int AUTO_INCREMENT PRIMARY KEY
+) COMMENT='ENGINE "InnoDB"';
+SELECT last_insert_id();
+last_insert_id()
+0
+INSERT INTO ids VALUES();
+SELECT last_insert_id();
+last_insert_id()
+1
+SELECT * FROM ids;
+id
+1
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/function_last_insert_id_set.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/function_last_insert_id_set.result
new file mode 100644
index 00000000000..39791b93ba2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/function_last_insert_id_set.result
@@ -0,0 +1,21 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id int AUTO_INCREMENT PRIMARY KEY
+) COMMENT='ENGINE "InnoDB"';
+SELECT last_insert_id();
+last_insert_id()
+0
+SELECT last_insert_id(10);
+last_insert_id(10)
+10
+SELECT last_insert_id();
+last_insert_id()
+10
+INSERT INTO ids VALUES();
+SELECT last_insert_id();
+last_insert_id()
+1
+SELECT * FROM ids;
+id
+1
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/geometry_contains.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/geometry_contains.result
new file mode 100644
index 00000000000..29163384e6a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/geometry_contains.result
@@ -0,0 +1,169 @@
+drop table if exists shops;
+create table shops (
+id int primary key auto_increment,
+name text,
+location geometry NOT NULL,
+spatial key location_index (location)
+) comment = 'engine "innodb"';
+show create table shops;
+Table Create Table
+shops CREATE TABLE `shops` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` text,
+ `location` geometry NOT NULL,
+ PRIMARY KEY (`id`),
+ SPATIAL KEY `location_index` (`location`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1 COMMENT='engine "innodb"'
+insert into shops (name, location)
+values ('nezu-no-taiyaki',
+GeomFromText('POINT(139.762573 35.720253)'));
+insert into shops (name, location)
+values ('taiyaki-kataoka',
+GeomFromText('POINT(139.715591 35.712521)'));
+insert into shops (name, location)
+values ('soba-taiyaki-ku',
+GeomFromText('POINT(139.659088 35.683712)'));
+insert into shops (name, location)
+values ('kuruma',
+GeomFromText('POINT(139.706207 35.721516)'));
+insert into shops (name, location)
+values ('hirose-ya',
+GeomFromText('POINT(139.685608 35.714844)'));
+insert into shops (name, location)
+values ('sazare',
+GeomFromText('POINT(139.685043 35.714653)'));
+insert into shops (name, location)
+values ('omede-taiyaki',
+GeomFromText('POINT(139.817154 35.700516)'));
+insert into shops (name, location)
+values ('onaga-ya',
+GeomFromText('POINT(139.81105 35.698254)'));
+insert into shops (name, location)
+values ('shiro-ya',
+GeomFromText('POINT(139.638611 35.705517)'));
+insert into shops (name, location)
+values ('fuji-ya',
+GeomFromText('POINT(139.637115 35.703938)'));
+insert into shops (name, location)
+values ('miyoshi',
+GeomFromText('POINT(139.537323 35.644539)'));
+insert into shops (name, location)
+values ('juju-ya',
+GeomFromText('POINT(139.695755 35.628922)'));
+insert into shops (name, location)
+values ('tatsumi-ya',
+GeomFromText('POINT(139.638657 35.665501)'));
+insert into shops (name, location)
+values ('tetsuji',
+GeomFromText('POINT(139.76857 35.680912)'));
+insert into shops (name, location)
+values ('gazuma-ya',
+GeomFromText('POINT(139.647598 35.700817)'));
+insert into shops (name, location)
+values ('honma-mon',
+GeomFromText('POINT(139.652573 35.722736)'));
+insert into shops (name, location)
+values ('naniwa-ya',
+GeomFromText('POINT(139.796234 35.730061)'));
+insert into shops (name, location)
+values ('kuro-dai',
+GeomFromText('POINT(139.704834 35.650345)'));
+insert into shops (name, location)
+values ('daruma',
+GeomFromText('POINT(139.770599 35.681461)'));
+insert into shops (name, location)
+values ('yanagi-ya',
+GeomFromText('POINT(139.783981 35.685341)'));
+insert into shops (name, location)
+values ('sharaku',
+GeomFromText('POINT(139.794846 35.716969)'));
+insert into shops (name, location)
+values ('takane',
+GeomFromText('POINT(139.560913 35.698601)'));
+insert into shops (name, location)
+values ('chiyoda',
+GeomFromText('POINT(139.652817 35.642601)'));
+insert into shops (name, location)
+values ('da-ka-po',
+GeomFromText('POINT(139.727356 35.627346)'));
+insert into shops (name, location)
+values ('matsushima-ya',
+GeomFromText('POINT(139.737381 35.640556)'));
+insert into shops (name, location)
+values ('kazuya',
+GeomFromText('POINT(139.760895 35.673508)'));
+insert into shops (name, location)
+values ('furuya-kogane-an',
+GeomFromText('POINT(139.676071 35.680603)'));
+insert into shops (name, location)
+values ('hachi-no-ie',
+GeomFromText('POINT(139.668106 35.608021)'));
+insert into shops (name, location)
+values ('azuki-chan',
+GeomFromText('POINT(139.673203 35.64151)'));
+insert into shops (name, location)
+values ('kuriko-an',
+GeomFromText('POINT(139.796829 35.712013)'));
+insert into shops (name, location)
+values ('yume-no-aru-machi-no-taiyaki-ya-san',
+GeomFromText('POINT(139.712524 35.616199)'));
+insert into shops (name, location)
+values ('naze-ya',
+GeomFromText('POINT(139.665833 35.609039)'));
+insert into shops (name, location)
+values ('sanoki-ya',
+GeomFromText('POINT(139.770721 35.66592)'));
+insert into shops (name, location)
+values ('shigeta',
+GeomFromText('POINT(139.780273 35.672626)'));
+insert into shops (name, location)
+values ('nishimi-ya',
+GeomFromText('POINT(139.774628 35.671825)'));
+insert into shops (name, location)
+values ('hiiragi',
+GeomFromText('POINT(139.711517 35.647701)'));
+select id, name, AsText(location) as location_text from shops;
+id name location_text
+1 nezu-no-taiyaki POINT(139.762573 35.720253)
+2 taiyaki-kataoka POINT(139.715591 35.712521)
+3 soba-taiyaki-ku POINT(139.659088 35.683712)
+4 kuruma POINT(139.706207 35.721516)
+5 hirose-ya POINT(139.685608 35.714844)
+6 sazare POINT(139.685043 35.714653)
+7 omede-taiyaki POINT(139.817154 35.700516)
+8 onaga-ya POINT(139.81105 35.698254)
+9 shiro-ya POINT(139.638611 35.705517)
+10 fuji-ya POINT(139.637115 35.703938)
+11 miyoshi POINT(139.537323 35.644539)
+12 juju-ya POINT(139.695755 35.628922)
+13 tatsumi-ya POINT(139.638657 35.665501)
+14 tetsuji POINT(139.76857 35.680912)
+15 gazuma-ya POINT(139.647598 35.700817)
+16 honma-mon POINT(139.652573 35.722736)
+17 naniwa-ya POINT(139.796234 35.730061)
+18 kuro-dai POINT(139.704834 35.650345)
+19 daruma POINT(139.770599 35.681461)
+20 yanagi-ya POINT(139.783981 35.685341)
+21 sharaku POINT(139.794846 35.716969)
+22 takane POINT(139.560913 35.698601)
+23 chiyoda POINT(139.652817 35.642601)
+24 da-ka-po POINT(139.727356 35.627346)
+25 matsushima-ya POINT(139.737381 35.640556)
+26 kazuya POINT(139.760895 35.673508)
+27 furuya-kogane-an POINT(139.676071 35.680603)
+28 hachi-no-ie POINT(139.668106 35.608021)
+29 azuki-chan POINT(139.673203 35.64151)
+30 kuriko-an POINT(139.796829 35.712013)
+31 yume-no-aru-machi-no-taiyaki-ya-san POINT(139.712524 35.616199)
+32 naze-ya POINT(139.665833 35.609039)
+33 sanoki-ya POINT(139.770721 35.66592)
+34 shigeta POINT(139.780273 35.672626)
+35 nishimi-ya POINT(139.774628 35.671825)
+36 hiiragi POINT(139.711517 35.647701)
+select id, name, AsText(location) as location_text from shops
+where MBRContains(GeomFromText('LineString(139.7727 35.6684, 139.7038 35.7121)'), location);
+id name location_text
+14 tetsuji POINT(139.76857 35.680912)
+19 daruma POINT(139.770599 35.681461)
+26 kazuya POINT(139.760895 35.673508)
+drop table shops;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/geometry_delete.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/geometry_delete.result
new file mode 100644
index 00000000000..53aa00ecbab
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/geometry_delete.result
@@ -0,0 +1,31 @@
+drop table if exists shops;
+create table shops (
+id int primary key auto_increment,
+name text,
+location geometry NOT NULL,
+spatial key location_index (location)
+) comment = 'engine "innodb"';
+show create table shops;
+Table Create Table
+shops CREATE TABLE `shops` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` text,
+ `location` geometry NOT NULL,
+ PRIMARY KEY (`id`),
+ SPATIAL KEY `location_index` (`location`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1 COMMENT='engine "innodb"'
+insert into shops (name, location)
+values ('sazare',
+GeomFromText('POINT(139.685043 35.714653)'));
+select id, name, AsText(location) as location_text from shops
+where MBRContains(GeomFromText('LineString(139.68466 35.71592, 139.68804 35.71411)'), location);
+id name location_text
+1 sazare POINT(139.685043 35.714653)
+delete from shops
+where MBRContains(GeomFromText('LineString(139.68466 35.71592, 139.68804 35.71411)'), location);
+select id, name, AsText(location) as location_text from shops
+where MBRContains(GeomFromText('LineString(139.68466 35.71592, 139.68804 35.71411)'), location);
+id name location_text
+select id, name, AsText(location) as location_text from shops;
+id name location_text
+drop table shops;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/geometry_update.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/geometry_update.result
new file mode 100644
index 00000000000..e982e0b95ce
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/geometry_update.result
@@ -0,0 +1,36 @@
+drop table if exists shops;
+create table shops (
+id int primary key auto_increment,
+name text,
+location geometry NOT NULL,
+spatial key location_index (location)
+) comment = 'engine "innodb"';
+show create table shops;
+Table Create Table
+shops CREATE TABLE `shops` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` text,
+ `location` geometry NOT NULL,
+ PRIMARY KEY (`id`),
+ SPATIAL KEY `location_index` (`location`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1 COMMENT='engine "innodb"'
+insert into shops (name, location)
+values ('sazare',
+GeomFromText('POINT(139.685043 35.714653)'));
+select id, name, AsText(location) as location_text from shops
+where MBRContains(GeomFromText('LineString(139.68466 35.71592, 139.68804 35.71411)'), location);
+id name location_text
+1 sazare POINT(139.685043 35.714653)
+select id, name, AsText(location) as location_text from shops
+where MBRContains(GeomFromText('LineString(139.65659 35.57903, 139.66489 35.57262)'), location);
+id name location_text
+update shops set location = GeomFromText('POINT(139.66116 35.57566)')
+where name = 'sazare';
+select id, name, AsText(location) as location_text from shops
+where MBRContains(GeomFromText('LineString(139.68466 35.71592, 139.68804 35.71411)'), location);
+id name location_text
+select id, name, AsText(location) as location_text from shops
+where MBRContains(GeomFromText('LineString(139.65659 35.57903, 139.66489 35.57262)'), location);
+id name location_text
+1 sazare POINT(139.66116 35.57566)
+drop table shops;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/index_force_index_not_used.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/index_force_index_not_used.result
new file mode 100644
index 00000000000..3fbc85ff051
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/index_force_index_not_used.result
@@ -0,0 +1,8 @@
+DROP TABLE IF EXISTS ids;
+CREATE TABLE ids (
+id INT PRIMARY KEY AUTO_INCREMENT
+) DEFAULT CHARSET UTF8 COMMENT = 'engine "InnoDB"';
+SELECT COUNT(*) FROM ids FORCE INDEX(PRIMARY);
+COUNT(*)
+0
+DROP TABLE ids;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/insert_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/insert_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..93c05bff080
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/insert_TODO_SPLIT_ME.result
@@ -0,0 +1,78 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 tinyint primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(1);
+select * from t1;
+c1
+1
+drop table t1;
+create table t1 (c1 smallint primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(1);
+select * from t1;
+c1
+1
+drop table t1;
+create table t1 (c1 mediumint primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(1);
+select * from t1;
+c1
+1
+drop table t1;
+create table t1 (c1 int primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(1);
+select * from t1;
+c1
+1
+drop table t1;
+create table t1 (c1 bigint primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(1);
+select * from t1;
+c1
+1
+drop table t1;
+create table t1 (c1 float primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(0.5);
+select * from t1;
+c1
+0.5
+drop table t1;
+create table t1 (c1 double primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(0.5);
+select * from t1;
+c1
+0.5
+drop table t1;
+create table t1 (c1 date primary key) COMMENT = 'engine "innodb"';
+insert into t1 values("2010/03/26");
+select * from t1;
+c1
+2010-03-26
+drop table t1;
+create table t1 (c1 time primary key) COMMENT = 'engine "innodb"';
+insert into t1 values("11:22:33");
+select * from t1;
+c1
+11:22:33
+drop table t1;
+create table t1 (c1 year primary key) COMMENT = 'engine "innodb"';
+insert into t1 values("2010");
+select * from t1;
+c1
+2010
+drop table t1;
+create table t1 (c1 datetime primary key) COMMENT = 'engine "innodb"';
+insert into t1 values("2010/03/26 11:22:33");
+select * from t1;
+c1
+2010-03-26 11:22:33
+drop table t1;
+create table t1 (c1 int primary key, c2 int) COMMENT = 'engine "innodb"';
+insert into t1 values(1,100);
+select * from t1;
+c1 c2
+1 100
+insert into t1 values(1,200);
+ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
+select * from t1;
+c1 c2
+1 100
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/insert_bulk.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/insert_bulk.result
new file mode 100644
index 00000000000..be61419477f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/insert_bulk.result
@@ -0,0 +1,30 @@
+drop table if exists diaries;
+set names utf8;
+create table diaries (
+id int primary key,
+content text,
+fulltext index (content)
+) default charset utf8 comment = 'engine "innodb"';
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+LOCK TABLE diaries WRITE;
+insert into diaries values(1, "今日からはじめました。");
+insert into diaries values(2, "明日の富士山の天気について");
+insert into diaries values(3, "今日も天気がよくてきれいに見える。");
+UNLOCK TABLES;
+select * from diaries;
+id content
+1 今日からはじめました。
+2 明日の富士山の天気について
+3 今日も天気がよくてきれいに見える。
+select * from diaries where match(content) against("天気");
+id content
+2 明日の富士山の天気について
+3 今日も天気がよくてきれいに見える。
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/insert_on_duplicate_key_update_multiple_column_primary_key_myisam.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/insert_on_duplicate_key_update_multiple_column_primary_key_myisam.result
new file mode 100644
index 00000000000..f0ceb937a01
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/insert_on_duplicate_key_update_multiple_column_primary_key_myisam.result
@@ -0,0 +1,36 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+date TIMESTAMP NOT NULL,
+title VARCHAR(100) NOT NULL,
+content TEXT NOT NULL,
+PRIMARY KEY (date, title)
+) DEFAULT CHARSET=UTF8 COMMENT='ENGINE "MyISAM"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `title` varchar(100) NOT NULL,
+ `content` text NOT NULL,
+ PRIMARY KEY (`date`,`title`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "MyISAM"'
+INSERT INTO diaries (date, title, content)
+VALUES ("2012-03-04", "cloudy day", "Today is cloudy day...");
+INSERT INTO diaries (date, title, content)
+VALUES ("2012-03-04", "shopping", "I buy a new shirt.");
+INSERT INTO diaries (date, title, content)
+VALUES ("2012-03-05", "rainy day", "Today is rainy day...");
+SELECT * FROM diaries;
+date title content
+2012-03-04 00:00:00 cloudy day Today is cloudy day...
+2012-03-04 00:00:00 shopping I buy a new shirt.
+2012-03-05 00:00:00 rainy day Today is rainy day...
+INSERT INTO diaries (date, title, content)
+VALUES ("2012-03-04", "shopping", "I buy new shoes.")
+ON DUPLICATE KEY UPDATE date = "2012-03-03",
+content = "I buy a new hat.";
+SELECT * FROM diaries;
+date title content
+2012-03-04 00:00:00 cloudy day Today is cloudy day...
+2012-03-03 00:00:00 shopping I buy a new hat.
+2012-03-05 00:00:00 rainy day Today is rainy day...
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/insert_on_duplicate_key_update_multiple_column_unique_index_myisam.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/insert_on_duplicate_key_update_multiple_column_unique_index_myisam.result
new file mode 100644
index 00000000000..97428b768a6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/insert_on_duplicate_key_update_multiple_column_unique_index_myisam.result
@@ -0,0 +1,39 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+date TIMESTAMP NOT NULL,
+title VARCHAR(100) NOT NULL,
+content TEXT NOT NULL,
+UNIQUE INDEX (date, title)
+) DEFAULT CHARSET=UTF8 COMMENT='ENGINE "MyISAM"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `title` varchar(100) NOT NULL,
+ `content` text NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `date` (`date`,`title`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "MyISAM"'
+INSERT INTO diaries (date, title, content)
+VALUES ("2012-03-04", "cloudy day", "Today is cloudy day...");
+INSERT INTO diaries (date, title, content)
+VALUES ("2012-03-04", "shopping", "I buy a new shirt.");
+INSERT INTO diaries (date, title, content)
+VALUES ("2012-03-05", "rainy day", "Today is rainy day...");
+SELECT * FROM diaries;
+id date title content
+1 2012-03-04 00:00:00 cloudy day Today is cloudy day...
+2 2012-03-04 00:00:00 shopping I buy a new shirt.
+3 2012-03-05 00:00:00 rainy day Today is rainy day...
+INSERT INTO diaries (date, title, content)
+VALUES ("2012-03-04", "shopping", "I buy new shoes.")
+ON DUPLICATE KEY UPDATE date = "2012-03-03",
+content = "I buy a new hat.";
+SELECT * FROM diaries;
+id date title content
+1 2012-03-04 00:00:00 cloudy day Today is cloudy day...
+2 2012-03-03 00:00:00 shopping I buy a new hat.
+3 2012-03-05 00:00:00 rainy day Today is rainy day...
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/multi_range_read_disk_sweep.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/multi_range_read_disk_sweep.result
new file mode 100644
index 00000000000..aa7a32dbd3a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/multi_range_read_disk_sweep.result
@@ -0,0 +1,18 @@
+DROP TABLE IF EXISTS integers;
+SET optimizer_switch='mrr_cost_based=off';
+CREATE TABLE integers (
+id INT PRIMARY KEY AUTO_INCREMENT,
+value INT,
+KEY (value)
+) COMMENT='engine "InnoDB"';
+INSERT INTO integers (value) VALUES (0), (1), (2), (3);
+EXPLAIN SELECT * FROM integers
+WHERE value IN (0, 2);
+id select_type table type possible_keys key key_len ref rows Extra
+1 SIMPLE integers range value value 5 NULL 2 Using where; Using MRR
+SELECT * FROM integers
+WHERE value IN (0, 2);
+id value
+1 0
+3 2
+DROP TABLE integers;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/optimization_order_limit_TODO_SPLIT_ME.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/optimization_order_limit_TODO_SPLIT_ME.result
new file mode 100644
index 00000000000..5f43aee6997
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/optimization_order_limit_TODO_SPLIT_ME.result
@@ -0,0 +1,67 @@
+drop table if exists t1;
+flush status;
+create table t1 (
+c1 int primary key,
+c2 int,
+c3 text,
+key idx1(c2),
+fulltext index ft(c3)
+) comment = 'engine "innodb"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"ii si ii se ii");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ii oo");
+select *, match(c3) against("ii") from t1
+where match(c3) against("ii") order by c1 desc limit 1;
+c1 c2 c3 match(c3) against("ii")
+5 50 aa ii uu ii oo 349526
+show status like 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 0
+select *, match(c3) against("ii") from t1
+where match(c3) against("ii") order by c1 limit 1;
+c1 c2 c3 match(c3) against("ii")
+1 10 aa ii uu ee oo 174763
+show status like 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 0
+select c3, match(c3) against("ii") from t1
+where match(c3) against("ii") order by match(c3) against("ii") desc;
+c3 match(c3) against("ii")
+ii si ii se ii 524289
+aa ii uu ii oo 349526
+aa ii uu ee oo 174763
+show status like 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 0
+select c3, match(c3) against("ii") from t1
+where match(c3) against("ii") order by match(c3) against("ii") desc limit 1, 1;
+c3 match(c3) against("ii")
+aa ii uu ii oo 349526
+show status like 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+select c3, match(c3) against("ii") from t1
+where match(c3) against("ii") order by match(c3) against("ii");
+c3 match(c3) against("ii")
+aa ii uu ee oo 174763
+aa ii uu ii oo 349526
+ii si ii se ii 524289
+show status like 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+select c3, match(c3) against("ii") from t1
+where match(c3) against("ii") order by match(c3) against("ii") limit 1;
+c3 match(c3) against("ii")
+aa ii uu ee oo 174763
+show status like 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 2
+select count(*) from t1 where match(c3) against("ii") limit 1;
+count(*)
+3
+show status like 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 2
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/optimization_order_limit_no_where_clause.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/optimization_order_limit_no_where_clause.result
new file mode 100644
index 00000000000..b31d2ee5360
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/optimization_order_limit_no_where_clause.result
@@ -0,0 +1,24 @@
+drop table if exists t1;
+flush status;
+create table t1 (
+c1 int primary key,
+c2 int,
+c3 text,
+key idx1(c2),
+fulltext index ft(c3)
+) comment = 'engine "innodb"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"ii si ii se ii");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ii oo");
+show status like 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 0
+select *, match(c3) against("ii") from t1 order by c1 desc limit 1;
+c1 c2 c3 match(c3) against("ii")
+5 50 aa ii uu ii oo 349526
+show status like 'mroonga_fast_order_limit';
+Variable_name Value
+Mroonga_fast_order_limit 1
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/repair_table_no_files.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/repair_table_no_files.result
new file mode 100644
index 00000000000..87a4fea1c3d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/repair_table_no_files.result
@@ -0,0 +1,42 @@
+DROP DATABASE IF EXISTS repair_test;
+CREATE DATABASE repair_test;
+USE repair_test;
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT,
+FULLTEXT INDEX body_index (body)
+) COMMENT = 'engine "innodb"' DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+SELECT * FROM diaries WHERE MATCH(body) AGAINST("starting");
+id title body
+2 groonga (1) starting groonga...
+FLUSH TABLES;
+SELECT * FROM diaries WHERE MATCH(body) AGAINST("starting");
+ERROR HY000: syscall error 'repair_test.mrn' (No such file or directory)
+REPAIR TABLE diaries;
+Table Op Msg_type Msg_text
+repair_test.diaries repair status OK
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+SELECT * FROM diaries WHERE MATCH(body) AGAINST("starting");
+id title body
+2 groonga (1) starting groonga...
+DROP TABLE diaries;
+DROP DATABASE repair_test;
+USE test;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/repair_table_no_index_file.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/repair_table_no_index_file.result
new file mode 100644
index 00000000000..140b5a92b75
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/repair_table_no_index_file.result
@@ -0,0 +1,42 @@
+DROP DATABASE IF EXISTS repair_test;
+CREATE DATABASE repair_test;
+USE repair_test;
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT,
+FULLTEXT INDEX body_index (body)
+) COMMENT = 'engine "innodb"' DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+SELECT * FROM diaries WHERE MATCH(body) AGAINST("starting");
+id title body
+2 groonga (1) starting groonga...
+FLUSH TABLES;
+SELECT * FROM diaries WHERE MATCH(body) AGAINST("starting");
+ERROR HY000: syscall error 'repair_test.mrn.0000104' (No such file or directory)
+REPAIR TABLE diaries;
+Table Op Msg_type Msg_text
+repair_test.diaries repair status OK
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+SELECT * FROM diaries WHERE MATCH(body) AGAINST("starting");
+id title body
+2 groonga (1) starting groonga...
+DROP TABLE diaries;
+DROP DATABASE repair_test;
+USE test;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/temporary_table.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/temporary_table.result
new file mode 100644
index 00000000000..b01e85aa483
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/temporary_table.result
@@ -0,0 +1,21 @@
+DROP TEMPORARY TABLE IF EXISTS diaries;
+CREATE TEMPORARY TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT
+) DEFAULT CHARSET=UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TEMPORARY TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries (title) VALUES ("clear day");
+INSERT INTO diaries (title) VALUES ("rainy day");
+INSERT INTO diaries (title) VALUES ("cloudy day");
+SELECT * FROM diaries;
+id title
+1 clear day
+2 rainy day
+3 cloudy day
+DROP TEMPORARY TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/transaction_query_cache.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/transaction_query_cache.result
new file mode 100644
index 00000000000..54afac7a1a7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/transaction_query_cache.result
@@ -0,0 +1,28 @@
+SET @tmp_query_cache_size = @@query_cache_size;
+SET GLOBAL query_cache_size = 1048576;
+DROP TABLE IF EXISTS simple_table;
+CREATE TABLE simple_table (
+id INT PRIMARY KEY
+) COMMENT = 'ENGINE "InnoDB"' DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE simple_table;
+Table Create Table
+simple_table CREATE TABLE `simple_table` (
+ `id` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO simple_table (id) VALUES (1),(2);
+USE test;
+START TRANSACTION;
+INSERT INTO simple_table (id) VALUES (3);
+SELECT * FROM simple_table;
+id
+1
+2
+COMMIT;
+SELECT * FROM simple_table;
+id
+1
+2
+3
+DROP TABLE simple_table;
+SET GLOBAL query_cache_size = @tmp_query_cache_size;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/transaction_rollback_delete_delete.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/transaction_rollback_delete_delete.result
new file mode 100644
index 00000000000..11cc1874257
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/transaction_rollback_delete_delete.result
@@ -0,0 +1,50 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT,
+FULLTEXT INDEX title_index (title),
+FULLTEXT INDEX body_index (body)
+) COMMENT = 'ENGINE "InnoDB"' DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("survey") AND
+MATCH(body) AGAINST("groonga");
+id title body
+1 survey will start groonga!
+START TRANSACTION;
+DELETE FROM diaries WHERE id = 1;
+ROLLBACK;
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("survey") AND
+MATCH(body) AGAINST("groonga");
+id title body
+DELETE FROM diaries WHERE id = 1;
+Warnings:
+Warning 1026 failed to get record ID for deleting from groonga: key=<>
+SELECT * FROM diaries;
+id title body
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("survey") AND
+MATCH(body) AGAINST("groonga");
+id title body
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/transaction_rollback_delete_update.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/transaction_rollback_delete_update.result
new file mode 100644
index 00000000000..7dde8518db0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/transaction_rollback_delete_update.result
@@ -0,0 +1,47 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+body TEXT,
+FULLTEXT INDEX title_index (title),
+FULLTEXT INDEX body_index (body)
+) COMMENT = 'ENGINE "InnoDB"' DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `title_index` (`title`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("survey");
+id title body
+1 survey will start groonga!
+START TRANSACTION;
+DELETE FROM diaries WHERE id = 1;
+ROLLBACK;
+SELECT * FROM diaries;
+id title body
+1 survey will start groonga!
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("survey");
+id title body
+UPDATE diaries SET title = "survey day!" WHERE id = 1;
+SELECT * FROM diaries;
+id title body
+1 survey day! will start groonga!
+2 groonga (1) starting groonga...
+3 groonga (2) started groonga.
+SELECT * FROM diaries
+WHERE MATCH(title) AGAINST("survey");
+id title body
+1 survey day! will start groonga!
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/truncate.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/truncate.result
new file mode 100644
index 00000000000..35b273d348f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/truncate.result
@@ -0,0 +1,55 @@
+DROP TABLE IF EXISTS diaries;
+SET NAMES UTF8;
+CREATE TABLE diaries (
+id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+year INT UNSIGNED,
+month INT UNSIGNED,
+day INT UNSIGNED,
+title VARCHAR(255),
+content TEXT,
+FULLTEXT INDEX(content),
+KEY(day)
+) DEFAULT CHARSET UTF8 COMMENT = 'engine "innodb"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `year` int(10) unsigned DEFAULT NULL,
+ `month` int(10) unsigned DEFAULT NULL,
+ `day` int(10) unsigned DEFAULT NULL,
+ `title` varchar(255) DEFAULT NULL,
+ `content` text,
+ PRIMARY KEY (`id`),
+ KEY `day` (`day`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+SELECT * FROM diaries;
+id year month day title content
+1 2011 11 9 Hello 今日からはじめました。
+2 2011 11 10 天気 明日の富士山の天気について
+3 2011 11 11 富士山 今日も天気がよくてきれいに見える。
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE);
+id year month day title content
+1 2011 11 9 Hello 今日からはじめました。
+3 2011 11 11 富士山 今日も天気がよくてきれいに見える。
+2 2011 11 10 天気 明日の富士山の天気について
+TRUNCATE TABLE diaries;
+SELECT * FROM diaries;
+id year month day title content
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE);
+id year month day title content
+INSERT INTO diaries VALUES(1, 2011, 11, 11, "帰り道", "つかれたー");
+INSERT INTO diaries VALUES(2, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(3, 2011, 12, 2, "初雪", "今年はじめての雪!");
+SELECT * FROM diaries;
+id year month day title content
+1 2011 11 11 帰り道 つかれたー
+2 2011 12 1 久しぶり 天気が悪いからずっと留守番。
+3 2011 12 2 初雪 今年はじめての雪!
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("悪い" IN BOOLEAN MODE);
+id year month day title content
+2 2011 12 1 久しぶり 天気が悪いからずっと留守番。
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/update_fulltext.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/update_fulltext.result
new file mode 100644
index 00000000000..655d2424ba7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/update_fulltext.result
@@ -0,0 +1,33 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 text, fulltext index (c2)) COMMENT = 'engine "innodb"';
+insert into t1 values(10, "aa ii uu ee");
+insert into t1 values(20, "ka ki ku ke");
+insert into t1 values(30, "sa si su se");
+select * from t1;
+c1 c2
+10 aa ii uu ee
+20 ka ki ku ke
+30 sa si su se
+update t1 set c2="ta ti tu te" where c1=20;
+select * from t1;
+c1 c2
+10 aa ii uu ee
+20 ta ti tu te
+30 sa si su se
+select * from t1 where match(c2) against("ti");
+c1 c2
+20 ta ti tu te
+select * from t1 where match(c2) against("ki");
+c1 c2
+update t1 set c1=40 where c1=20;
+select * from t1;
+c1 c2
+10 aa ii uu ee
+30 sa si su se
+40 ta ti tu te
+select * from t1 where match(c2) against("ti");
+c1 c2
+40 ta ti tu te
+select * from t1 where match(c2) against("ki");
+c1 c2
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/update_int.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/update_int.result
new file mode 100644
index 00000000000..5506f6b10b0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/update_int.result
@@ -0,0 +1,36 @@
+drop table if exists t1, t2, t3;
+create table t1 (c1 int primary key, c2 int) COMMENT = 'engine "innodb"';
+show create table t1;
+Table Create Table
+t1 CREATE TABLE `t1` (
+ `c1` int(11) NOT NULL,
+ `c2` int(11) DEFAULT NULL,
+ PRIMARY KEY (`c1`)
+) ENGINE=Mroonga DEFAULT CHARSET=latin1 COMMENT='engine "innodb"'
+insert into t1 values (1, 100);
+insert into t1 values (2, 101);
+insert into t1 values (3, 102);
+select * from t1;
+c1 c2
+1 100
+2 101
+3 102
+update t1 set c2=c2+100 where c1=1;
+select * from t1;
+c1 c2
+1 200
+2 101
+3 102
+update t1 set c2=c2+100 where c1=2;
+select * from t1;
+c1 c2
+1 200
+2 201
+3 102
+update t1 set c2=c2+100 where c1=3;
+select * from t1;
+c1 c2
+1 200
+2 201
+3 202
+drop table t1;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_dry_write_delete.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_dry_write_delete.result
new file mode 100644
index 00000000000..e451b2b99fe
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_dry_write_delete.result
@@ -0,0 +1,53 @@
+drop table if exists diaries;
+create table diaries (
+id int primary key auto_increment,
+body text,
+fulltext index body_index (body)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+insert into diaries (body) values ("will start groonga!");
+insert into diaries (body) values ("starting groonga...");
+insert into diaries (body) values ("started groonga.");
+select * from diaries;
+id body
+1 will start groonga!
+2 starting groonga...
+3 started groonga.
+set mroonga_dry_write=true;
+delete from diaries where id = 2;
+select * from diaries;
+id body
+1 will start groonga!
+3 started groonga.
+select * from diaries where match (body) against ("starting");
+id body
+select * from diaries where match (body) against ("started");
+id body
+3 started groonga.
+set mroonga_dry_write=false;
+delete from diaries where id = 3;
+select * from diaries;
+id body
+1 will start groonga!
+select * from diaries where match (body) against ("starting");
+id body
+select * from diaries where match (body) against ("started");
+id body
+insert into diaries (id, body) values (2, "sleeping...");
+select * from diaries;
+id body
+1 will start groonga!
+2 sleeping...
+select * from diaries where match (body) against ("starting");
+id body
+2 sleeping...
+select * from diaries where match (body) against ("started");
+id body
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_dry_write_insert.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_dry_write_insert.result
new file mode 100644
index 00000000000..f11398f3710
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_dry_write_insert.result
@@ -0,0 +1,42 @@
+drop table if exists diaries;
+create table diaries (
+id int primary key auto_increment,
+body text,
+fulltext index body_index (body)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+insert into diaries (body) values ("will start groonga!");
+select * from diaries;
+id body
+1 will start groonga!
+select * from diaries where match (body) against ("groonga");
+id body
+1 will start groonga!
+set mroonga_dry_write=true;
+insert into diaries (body) values ("starting groonga...");
+select * from diaries;
+id body
+1 will start groonga!
+2 starting groonga...
+select * from diaries where match (body) against ("groonga");
+id body
+1 will start groonga!
+set mroonga_dry_write=false;
+insert into diaries (body) values ("started groonga.");
+select * from diaries;
+id body
+1 will start groonga!
+2 starting groonga...
+3 started groonga.
+select * from diaries where match (body) against ("groonga");
+id body
+1 will start groonga!
+3 started groonga.
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_dry_write_update.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_dry_write_update.result
new file mode 100644
index 00000000000..47a844c8e68
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_dry_write_update.result
@@ -0,0 +1,39 @@
+drop table if exists diaries;
+create table diaries (
+id int primary key auto_increment,
+body text,
+fulltext index body_index (body)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `body` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `body_index` (`body`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='engine "innodb"'
+insert into diaries (body) values ("will start groonga!");
+set mroonga_dry_write=true;
+update diaries set body = "starting groonga..." where id = 1;
+select * from diaries;
+id body
+1 starting groonga...
+select * from diaries where match (body) against ("will");
+id body
+1 starting groonga...
+select * from diaries where match (body) against ("starting");
+id body
+set mroonga_dry_write=false;
+update diaries set body = "started groonga." where id = 1;
+select * from diaries;
+id body
+1 started groonga.
+select * from diaries where match (body) against ("will");
+id body
+1 started groonga.
+select * from diaries where match (body) against ("starting");
+id body
+select * from diaries where match (body) against ("started");
+id body
+1 started groonga.
+drop table diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_match_escalation_threshold_global.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_match_escalation_threshold_global.result
new file mode 100644
index 00000000000..0cf3d49c3c3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_match_escalation_threshold_global.result
@@ -0,0 +1,33 @@
+DROP TABLE IF EXISTS diaries;
+SET GLOBAL mroonga_match_escalation_threshold = -1;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+tags TEXT,
+FULLTEXT INDEX tags_index (tags) COMMENT 'parser "TokenDelimit"'
+) DEFAULT CHARSET=UTF8 COMMENT='ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `tags` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `tags_index` (`tags`) COMMENT 'parser "TokenDelimit"'
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries (title, tags) VALUES ("Hello groonga!", "groonga install");
+INSERT INTO diaries (title, tags) VALUES ("Hello mroonga!", "mroonga install");
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("install" IN BOOLEAN MODE);
+id title tags
+1 Hello groonga! groonga install
+2 Hello mroonga! mroonga install
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+id title tags
+SET GLOBAL mroonga_match_escalation_threshold = 0;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+id title tags
+SET mroonga_match_escalation_threshold = 0;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+id title tags
+1 Hello groonga! groonga install
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_match_escalation_threshold_session.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_match_escalation_threshold_session.result
new file mode 100644
index 00000000000..3d94ebc93ad
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/variable_match_escalation_threshold_session.result
@@ -0,0 +1,33 @@
+DROP TABLE IF EXISTS diaries;
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+title TEXT,
+tags TEXT,
+FULLTEXT INDEX tags_index (tags) COMMENT 'parser "TokenDelimit"'
+) DEFAULT CHARSET=UTF8 COMMENT='ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` text,
+ `tags` text,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `tags_index` (`tags`) COMMENT 'parser "TokenDelimit"'
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries (title, tags) VALUES ("Hello groonga!", "groonga install");
+INSERT INTO diaries (title, tags) VALUES ("Hello mroonga!", "mroonga install");
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("install" IN BOOLEAN MODE);
+id title tags
+1 Hello groonga! groonga install
+2 Hello mroonga! mroonga install
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+id title tags
+1 Hello groonga! groonga install
+SET mroonga_match_escalation_threshold = -1;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+id title tags
+SET mroonga_match_escalation_threshold = 0;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+id title tags
+1 Hello groonga! groonga install
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/version_55_performance_schema.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/version_55_performance_schema.result
new file mode 100644
index 00000000000..f6e3bbdca82
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/version_55_performance_schema.result
@@ -0,0 +1,38 @@
+DROP TABLE IF EXISTS diaries;
+SHOW VARIABLES LIKE 'performance_schema';
+Variable_name Value
+performance_schema ON
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+content VARCHAR(255),
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `content` varchar(255) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries (content) VALUES ("Tommorow will be shiny day!");
+SHOW TABLES FROM performance_schema;
+Tables_in_performance_schema
+cond_instances
+events_waits_current
+events_waits_history
+events_waits_history_long
+events_waits_summary_by_instance
+events_waits_summary_by_thread_by_event_name
+events_waits_summary_global_by_event_name
+file_instances
+file_summary_by_event_name
+file_summary_by_instance
+mutex_instances
+performance_timers
+rwlock_instances
+setup_consumers
+setup_instruments
+setup_timers
+threads
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/r/version_56_or_later_performance_schema.result b/storage/mroonga/mysql-test/mroonga/wrapper/r/version_56_or_later_performance_schema.result
new file mode 100644
index 00000000000..548dc32707f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/r/version_56_or_later_performance_schema.result
@@ -0,0 +1,73 @@
+DROP TABLE IF EXISTS diaries;
+SHOW VARIABLES LIKE 'performance_schema';
+Variable_name Value
+performance_schema ON
+CREATE TABLE diaries (
+id INT PRIMARY KEY AUTO_INCREMENT,
+content VARCHAR(255),
+FULLTEXT INDEX (content)
+) DEFAULT CHARSET UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+Table Create Table
+diaries CREATE TABLE `diaries` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `content` varchar(255) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ FULLTEXT KEY `content` (`content`)
+) ENGINE=Mroonga DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"'
+INSERT INTO diaries (content) VALUES ("Tommorow will be shiny day!");
+SHOW TABLES FROM performance_schema;
+Tables_in_performance_schema
+accounts
+cond_instances
+events_stages_current
+events_stages_history
+events_stages_history_long
+events_stages_summary_by_account_by_event_name
+events_stages_summary_by_host_by_event_name
+events_stages_summary_by_thread_by_event_name
+events_stages_summary_by_user_by_event_name
+events_stages_summary_global_by_event_name
+events_statements_current
+events_statements_history
+events_statements_history_long
+events_statements_summary_by_account_by_event_name
+events_statements_summary_by_digest
+events_statements_summary_by_host_by_event_name
+events_statements_summary_by_thread_by_event_name
+events_statements_summary_by_user_by_event_name
+events_statements_summary_global_by_event_name
+events_waits_current
+events_waits_history
+events_waits_history_long
+events_waits_summary_by_account_by_event_name
+events_waits_summary_by_host_by_event_name
+events_waits_summary_by_instance
+events_waits_summary_by_thread_by_event_name
+events_waits_summary_by_user_by_event_name
+events_waits_summary_global_by_event_name
+file_instances
+file_summary_by_event_name
+file_summary_by_instance
+host_cache
+hosts
+mutex_instances
+objects_summary_global_by_type
+performance_timers
+rwlock_instances
+session_account_connect_attrs
+session_connect_attrs
+setup_actors
+setup_consumers
+setup_instruments
+setup_objects
+setup_timers
+socket_instances
+socket_summary_by_event_name
+socket_summary_by_instance
+table_io_waits_summary_by_index_usage
+table_io_waits_summary_by_table
+table_lock_waits_summary_by_table
+threads
+users
+DROP TABLE diaries;
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/suite.opt b/storage/mroonga/mysql-test/mroonga/wrapper/suite.opt
new file mode 100644
index 00000000000..d5a1e5190a7
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/suite.opt
@@ -0,0 +1 @@
+--loose-plugin-load-add=$HA_MROONGA_SO --loose-plugin-mroonga=ON
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/suite.pm b/storage/mroonga/mysql-test/mroonga/wrapper/suite.pm
new file mode 100644
index 00000000000..528ccc5d693
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/suite.pm
@@ -0,0 +1,23 @@
+package My::Suite::Mroonga;
+
+@ISA = qw(My::Suite);
+
+return "No Mroonga engine" unless $ENV{HA_MROONGA_SO} or
+ $::mysqld_variables{'mroonga'} eq "ON";
+
+sub is_default { 1 }
+
+my $groonga_normalizer_mysql_dir=$::basedir . '/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql';
+my $groonga_normalizer_mysql_install_dir=$::basedir . '/lib/groonga/plugins';
+
+if (-d $groonga_normalizer_mysql_dir)
+{
+ $ENV{GRN_PLUGINS_DIR}=$groonga_normalizer_mysql_dir;
+}
+elsif (-d $groonga_normalizer_mysql_install_dir)
+{
+ $ENV{GRN_PLUGINS_DIR}=$groonga_normalizer_mysql_install_dir;
+}
+
+bless { };
+
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_add_column.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_add_column.test
new file mode 100644
index 00000000000..f25fdf95dab
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_add_column.test
@@ -0,0 +1,46 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT
+) DEFAULT CHARSET UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title) VALUES ("survey");
+SELECT * FROM diaries;
+
+ALTER TABLE diaries ADD COLUMN body TEXT;
+UPDATE diaries SET body = "will start groonga!";
+SELECT * FROM diaries;
+
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) values ("groonga (2)", "started groonga.");
+SELECT * FROM diaries;
+
+SHOW CREATE TABLE diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_change_column_comment.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_change_column_comment.test
new file mode 100644
index 00000000000..bbc5ca49818
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_change_column_comment.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS bugs;
+--enable_warnings
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tag VARCHAR(64)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnoDB"';
+
+ALTER TABLE bugs
+ CHANGE COLUMN
+ tag
+ tag VARCHAR(64) COMMENT 'It must consist of only alphabet and number.';
+
+SHOW CREATE TABLE bugs;
+
+DROP TABLE bugs;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_change_engine.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_change_engine.test
new file mode 100644
index 00000000000..38a13bf8a5e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_change_engine.test
@@ -0,0 +1,55 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT,
+ FULLTEXT INDEX title_index (title),
+ FULLTEXT INDEX body_index (body)
+) ENGINE MyISAM DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE) AND
+ MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+
+ALTER TABLE diaries ENGINE = mroonga COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("survey" IN BOOLEAN MODE) AND
+ MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("groonga" IN BOOLEAN MODE) AND
+ MATCH(body) AGAINST("groonga" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_comment_change_engine.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_comment_change_engine.test
new file mode 100644
index 00000000000..2c85cc68266
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_comment_change_engine.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ title VARCHAR(64),
+ content TEXT,
+ FULLTEXT INDEX(content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnoDB"';
+SHOW CREATE TABLE memos;
+
+INSERT INTO memos (title, content) VALUES ("Hello", "I start to write memos!");
+INSERT INTO memos (title, content) VALUES ("groonga", "I start to use groonga!");
+INSERT INTO memos (title, content) VALUES ("mroonga", "I use mroonga too!");
+
+ALTER TABLE memos COMMENT='engine "MyISAM"';
+SHOW CREATE TABLE memos;
+
+SELECT * FROM memos;
+SELECT * FROM memos WHERE MATCH(content) AGAINST("start" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_create_fulltext.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_create_fulltext.test
new file mode 100644
index 00000000000..00b6ecf565e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_create_fulltext.test
@@ -0,0 +1,56 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT *
+ FROM diaries
+ WHERE MATCH (title) AGAINST ("富士山");
+
+CREATE FULLTEXT INDEX title_index on diaries (title);
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+ALTER TABLE diaries DISABLE KEYS;
+
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_fulltext.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_fulltext.test
new file mode 100644
index 00000000000..f81b4e82b06
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_fulltext.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ FULLTEXT KEY title_index (title)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+ALTER TABLE diaries DISABLE KEYS;
+
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_multiple_column.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_multiple_column.test
new file mode 100644
index 00000000000..760a4bc99bd
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_multiple_column.test
@@ -0,0 +1,52 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ created_at datetime,
+ KEY title_and_created_at_index (title, created_at)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_and_created_at_index)
+ WHERE title = "天気" AND
+ created_at = "2012-04-30 23:00:00";
+
+ALTER TABLE diaries DISABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_and_created_at_index)
+ WHERE title = "天気" AND
+ created_at = "2012-04-30 23:00:00";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_normal.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_normal.test
new file mode 100644
index 00000000000..89a62e78baa
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_normal.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ created_at datetime,
+ KEY created_at_index (created_at)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (created_at_index)
+ WHERE created_at = "2012-04-30 20:00:00";
+
+ALTER TABLE diaries DISABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (created_at_index)
+ WHERE created_at = "2012-04-30 20:00:00";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_primary.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_primary.test
new file mode 100644
index 00000000000..63f0b8ad404
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_primary.test
@@ -0,0 +1,48 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (PRIMARY)
+ WHERE id = 2;
+
+ALTER TABLE diaries DISABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (PRIMARY)
+ WHERE id = 2;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_updating.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_updating.test
new file mode 100644
index 00000000000..9d0f1f3dc1a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_disable_keys_updating.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
+CREATE TABLE t1 (
+ c1 int NOT NULL,
+ c2 text NOT NULL,
+ c3 int NOT NULL,
+ c4 int NOT NULL,
+ PRIMARY KEY(c1),
+ KEY idx1(c3,c4),
+ FULLTEXT KEY ft1(c2)
+) COMMENT='ENGINE "MyISAM"' DEFAULT CHARSET=utf8;
+INSERT INTO t1 VALUES(1, 'test1', 1, 1);
+INSERT INTO t1 VALUES(2, 'test2', 2, 2);
+INSERT INTO t1 VALUES(3, 'test3', 1, 3);
+ALTER TABLE t1 DISABLE KEYS;
+DELETE FROM t1 WHERE c1 = 2;
+UPDATE t1 SET c4 = 4 WHERE c1 = 1;
+INSERT INTO t1 VALUES(4, 'test4', 2, 4);
+TRUNCATE t1;
+DROP TABLE t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_drop_column.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_drop_column.test
new file mode 100644
index 00000000000..41a09b8721b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_drop_column.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT
+) DEFAULT CHARSET UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+SELECT * FROM diaries;
+
+ALTER TABLE diaries DROP COLUMN body;
+SHOW CREATE TABLE diaries;
+SELECT * FROM diaries;
+
+INSERT INTO diaries (title) values ("groonga (1)");
+INSERT INTO diaries (title) values ("groonga (2)");
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_fulltext.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_fulltext.test
new file mode 100644
index 00000000000..293bc8b7963
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_fulltext.test
@@ -0,0 +1,52 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ FULLTEXT KEY title_index (title)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+
+ALTER TABLE diaries DISABLE KEYS;
+
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+ALTER TABLE diaries ENABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_index)
+ WHERE MATCH (title) AGAINST ("富士山");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_lock_tables.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_lock_tables.test
new file mode 100644
index 00000000000..0756c27f9f8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_lock_tables.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+CREATE TABLE IF NOT EXISTS memos (
+ id VARCHAR(45) NOT NULL PRIMARY KEY,
+ text TEXT NOT NULL,
+ FULLTEXT KEY (text)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnoDB"';
+
+LOCK TABLES memos WRITE;
+ALTER TABLE memos DISABLE KEYS;
+
+INSERT INTO memos
+ VALUES (00000, 'text0'),
+ (00001, 'text1'),
+ (00002, 'text2');
+
+ALTER TABLE memos ENABLE KEYS;
+UNLOCK TABLES;
+
+SELECT * FROM memos;
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_multiple_column.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_multiple_column.test
new file mode 100644
index 00000000000..f8b93e19000
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_multiple_column.test
@@ -0,0 +1,54 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ created_at datetime,
+ KEY title_and_created_at_index (title, created_at)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+
+ALTER TABLE diaries DISABLE KEYS;
+
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_and_created_at_index)
+ WHERE title = "天気" AND
+ created_at = "2012-04-30 23:00:00";
+
+ALTER TABLE diaries ENABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (title_and_created_at_index)
+ WHERE title = "天気" AND
+ created_at = "2012-04-30 23:00:00";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_normal.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_normal.test
new file mode 100644
index 00000000000..3dd0f332fe6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_normal.test
@@ -0,0 +1,52 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ created_at datetime,
+ KEY created_at_index (created_at)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+
+ALTER TABLE diaries DISABLE KEYS;
+
+INSERT INTO diaries VALUES (1, "Hello", "2012-04-30 20:00:00");
+INSERT INTO diaries VALUES (2, "天気" , "2012-04-30 23:00:00");
+INSERT INTO diaries VALUES (3, "富士山", "2012-04-30 19:00:00");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (created_at_index)
+ WHERE created_at = "2012-04-30 20:00:00";
+
+ALTER TABLE diaries ENABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (created_at_index)
+ WHERE created_at = "2012-04-30 20:00:00";
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_primary.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_primary.test
new file mode 100644
index 00000000000..bfa11c9eb8c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_enable_keys_primary.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255)
+) COMMENT='ENGINE "InnoDB"' DEFAULT CHARSET=utf8;
+
+ALTER TABLE diaries DISABLE KEYS;
+
+INSERT INTO diaries VALUES (1, "Hello");
+INSERT INTO diaries VALUES (2, "天気");
+INSERT INTO diaries VALUES (3, "富士山");
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (PRIMARY)
+ WHERE id = 2;
+
+ALTER TABLE diaries ENABLE KEYS;
+
+SELECT *
+ FROM diaries
+ FORCE INDEX (PRIMARY)
+ WHERE id = 2;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_fulltext.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_fulltext.test
new file mode 100644
index 00000000000..f4bd0b391b6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_fulltext.test
@@ -0,0 +1,57 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ FULLTEXT INDEX title_index (title)
+) DEFAULT CHARSET UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title) VALUES ("survey");
+SELECT * FROM diaries;
+
+ALTER TABLE diaries ADD COLUMN body TEXT;
+UPDATE diaries SET body = "will start groonga!";
+SELECT * FROM diaries;
+
+INSERT INTO diaries (title, body) values ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) values ("groonga (2)", "started groonga.");
+SELECT * FROM diaries;
+
+ALTER TABLE diaries ADD FULLTEXT INDEX body_index (body);
+
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("survey") AND
+ MATCH(body) AGAINST("groonga");
+
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("groonga") AND
+ MATCH(body) AGAINST("starting");
+
+SHOW CREATE TABLE diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_rename_table.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_rename_table.test
new file mode 100644
index 00000000000..595a850ae03
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_rename_table.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries, memos;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT,
+ FULLTEXT INDEX title_index (title),
+ FULLTEXT INDEX body_index (body)
+) DEFAULT CHARSET UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+SELECT * FROM diaries;
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("groonga") AND
+ MATCH(body) AGAINST("starting");
+
+ALTER TABLE diaries RENAME memos;
+SELECT * FROM memos;
+SELECT * FROM memos
+ WHERE MATCH(title) AGAINST("groonga") AND
+ MATCH(body) AGAINST("starting");
+
+SHOW CREATE TABLE memos;
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_spatial.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_spatial.test
new file mode 100644
index 00000000000..37ea8aaf149
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/alter_table_spatial.test
@@ -0,0 +1,150 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/not_embedded.inc
+--source include/have_geometry.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS shops;
+--enable_warnings
+
+CREATE TABLE shops (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ name TEXT,
+ location GEOMETRY NOT NULL
+) COMMENT = 'ENGINE "InnoDB"';
+
+INSERT INTO shops (name, location)
+ VALUES ('nezu-no-taiyaki',
+ GeomFromText('POINT(139.762573 35.720253)'));
+INSERT INTO shops (name, location)
+ VALUES ('taiyaki-kataoka',
+ GeomFromText('POINT(139.715591 35.712521)'));
+INSERT INTO shops (name, location)
+ VALUES ('soba-taiyaki-ku',
+ GeomFromText('POINT(139.659088 35.683712)'));
+INSERT INTO shops (name, location)
+ VALUES ('kuruma',
+ GeomFromText('POINT(139.706207 35.721516)'));
+INSERT INTO shops (name, location)
+ VALUES ('hirose-ya',
+ GeomFromText('POINT(139.685608 35.714844)'));
+INSERT INTO shops (name, location)
+ VALUES ('sazare',
+ GeomFromText('POINT(139.685043 35.714653)'));
+INSERT INTO shops (name, location)
+ VALUES ('omede-taiyaki',
+ GeomFromText('POINT(139.817154 35.700516)'));
+INSERT INTO shops (name, location)
+ VALUES ('onaga-ya',
+ GeomFromText('POINT(139.81105 35.698254)'));
+INSERT INTO shops (name, location)
+ VALUES ('shiro-ya',
+ GeomFromText('POINT(139.638611 35.705517)'));
+INSERT INTO shops (name, location)
+ VALUES ('fuji-ya',
+ GeomFromText('POINT(139.637115 35.703938)'));
+INSERT INTO shops (name, location)
+ VALUES ('miyoshi',
+ GeomFromText('POINT(139.537323 35.644539)'));
+INSERT INTO shops (name, location)
+ VALUES ('juju-ya',
+ GeomFromText('POINT(139.695755 35.628922)'));
+INSERT INTO shops (name, location)
+ VALUES ('tatsumi-ya',
+ GeomFromText('POINT(139.638657 35.665501)'));
+INSERT INTO shops (name, location)
+ VALUES ('tetsuji',
+ GeomFromText('POINT(139.76857 35.680912)'));
+INSERT INTO shops (name, location)
+ VALUES ('gazuma-ya',
+ GeomFromText('POINT(139.647598 35.700817)'));
+INSERT INTO shops (name, location)
+ VALUES ('honma-mon',
+ GeomFromText('POINT(139.652573 35.722736)'));
+INSERT INTO shops (name, location)
+ VALUES ('naniwa-ya',
+ GeomFromText('POINT(139.796234 35.730061)'));
+INSERT INTO shops (name, location)
+ VALUES ('kuro-dai',
+ GeomFromText('POINT(139.704834 35.650345)'));
+INSERT INTO shops (name, location)
+ VALUES ('daruma',
+ GeomFromText('POINT(139.770599 35.681461)'));
+INSERT INTO shops (name, location)
+ VALUES ('yanagi-ya',
+ GeomFromText('POINT(139.783981 35.685341)'));
+INSERT INTO shops (name, location)
+ VALUES ('sharaku',
+ GeomFromText('POINT(139.794846 35.716969)'));
+INSERT INTO shops (name, location)
+ VALUES ('takane',
+ GeomFromText('POINT(139.560913 35.698601)'));
+INSERT INTO shops (name, location)
+ VALUES ('chiyoda',
+ GeomFromText('POINT(139.652817 35.642601)'));
+INSERT INTO shops (name, location)
+ VALUES ('da-ka-po',
+ GeomFromText('POINT(139.727356 35.627346)'));
+INSERT INTO shops (name, location)
+ VALUES ('matsushima-ya',
+ GeomFromText('POINT(139.737381 35.640556)'));
+INSERT INTO shops (name, location)
+ VALUES ('kazuya',
+ GeomFromText('POINT(139.760895 35.673508)'));
+INSERT INTO shops (name, location)
+ VALUES ('furuya-kogane-an',
+ GeomFromText('POINT(139.676071 35.680603)'));
+INSERT INTO shops (name, location)
+ VALUES ('hachi-no-ie',
+ GeomFromText('POINT(139.668106 35.608021)'));
+INSERT INTO shops (name, location)
+ VALUES ('azuki-chan',
+ GeomFromText('POINT(139.673203 35.64151)'));
+INSERT INTO shops (name, location)
+ VALUES ('kuriko-an',
+ GeomFromText('POINT(139.796829 35.712013)'));
+INSERT INTO shops (name, location)
+ VALUES ('yume-no-aru-machi-no-taiyaki-ya-san',
+ GeomFromText('POINT(139.712524 35.616199)'));
+INSERT INTO shops (name, location)
+ VALUES ('naze-ya',
+ GeomFromText('POINT(139.665833 35.609039)'));
+INSERT INTO shops (name, location)
+ VALUES ('sanoki-ya',
+ GeomFromText('POINT(139.770721 35.66592)'));
+INSERT INTO shops (name, location)
+ VALUES ('shigeta',
+ GeomFromText('POINT(139.780273 35.672626)'));
+INSERT INTO shops (name, location)
+ VALUES ('nishimi-ya',
+ GeomFromText('POINT(139.774628 35.671825)'));
+INSERT INTO shops (name, location)
+ VALUES ('hiiragi',
+ GeomFromText('POINT(139.711517 35.647701)'));
+
+ALTER TABLE shops ADD SPATIAL KEY location_index (location);
+
+SELECT id, name, AsText(location) AS location_text FROM shops
+ WHERE MBRContains(GeomFromText('LineString(139.7727 35.6684, 139.7038 35.7121)'), location);
+
+SHOW CREATE TABLE shops;
+
+DROP TABLE shops;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/auto_increment_text.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/auto_increment_text.test
new file mode 100644
index 00000000000..e702cb5d591
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/auto_increment_text.test
@@ -0,0 +1,34 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+create table diaries (
+ id int primary key auto_increment,
+ body text
+) comment = 'engine "innodb"';
+insert into diaries (body) values ("started groonga (long text)");
+select * from diaries;
+insert into diaries (body) values ("sleeping... (short text)");
+select * from diaries;
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/binlog_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/binlog_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..230ab87b391
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/binlog_TODO_SPLIT_ME.test
@@ -0,0 +1,55 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/have_log_bin.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+show variables like 'log_bin';
+
+set binlog_format="STATEMENT";
+
+create table t1 (c1 int primary key, c2 int) engine = mroonga COMMENT = 'engine "innodb"';
+insert into t1 values(1,100);
+insert into t1 values(2,100);
+commit;
+select * from t1;
+drop table t1;
+
+set binlog_format="ROW";
+
+create table t1 (c1 int primary key, c2 int) engine = mroonga COMMENT = 'engine "innodb"';
+insert into t1 values(1,100);
+insert into t1 values(2,100);
+commit;
+select * from t1;
+drop table t1;
+
+set binlog_format="MIXED";
+
+create table t1 (c1 int primary key, c2 int) engine = mroonga COMMENT = 'engine "innodb"';
+insert into t1 values(1,100);
+insert into t1 values(2,100);
+commit;
+select * from t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/column_comment_index_not_for_mroonga.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/column_comment_index_not_for_mroonga.test
new file mode 100644
index 00000000000..3bc3c069705
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/column_comment_index_not_for_mroonga.test
@@ -0,0 +1,37 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS bugs;
+--enable_warnings
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tag VARCHAR(64),
+ INDEX (tag) COMMENT 'Tag search is required.'
+) DEFAULT CHARSET=utf8
+ COMMENT='engine "InnoDB"';
+
+SHOW CREATE TABLE bugs;
+
+DROP TABLE bugs;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/column_normal_comment.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/column_normal_comment.test
new file mode 100644
index 00000000000..73065ffd7bb
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/column_normal_comment.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS bugs;
+--enable_warnings
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY,
+ tag VARCHAR(64) COMMENT 'It must consist of only alphabet and number.'
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnoDB"';
+
+SHOW CREATE TABLE bugs;
+
+DROP TABLE bugs;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/count_star_with_index.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/count_star_with_index.test
new file mode 100644
index 00000000000..f8b77becdde
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/count_star_with_index.test
@@ -0,0 +1,54 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_fulltext_index_comment.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries_innodb;
+DROP TABLE IF EXISTS diaries_mroonga;
+--enable_warnings
+
+CREATE TABLE diaries_innodb (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ body TEXT,
+ flag TINYINT(2),
+ INDEX (flag)
+) ENGINE = InnoDB DEFAULT CHARSET UTF8;
+
+CREATE TABLE diaries_mroonga (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ body TEXT,
+ flag TINYINT(2),
+ INDEX (flag)
+) COMMENT = 'ENGINE "InnoDB"' DEFAULT CHARSET UTF8;
+
+INSERT INTO diaries_innodb (body) VALUES ("will start groonga!");
+INSERT INTO diaries_innodb (body) VALUES ("starting groonga...");
+INSERT INTO diaries_innodb (body) VALUES ("started groonga.");
+
+INSERT INTO diaries_mroonga (body) VALUES ("will start groonga!");
+INSERT INTO diaries_mroonga (body) VALUES ("starting groonga...");
+INSERT INTO diaries_mroonga (body) VALUES ("started groonga.");
+
+EXPLAIN SELECT COUNT(*) FROM diaries_innodb;
+EXPLAIN SELECT COUNT(*) FROM diaries_mroonga;
+
+DROP TABLE diaries_innodb;
+DROP TABLE diaries_mroonga;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/create_table_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/create_table_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..6df44e79665
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/create_table_TODO_SPLIT_ME.test
@@ -0,0 +1,99 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+# simple test
+create table t1 (c1 int primary key) COMMENT = 'engine "innodb"';
+create table t2 (c1 int primary key) COMMENT = 'engine "innodb"';
+create table t3 (c1 int primary key) COMMENT = 'engine "innodb"';
+drop table t1,t2,t3;
+create table t1 (c1 int primary key, c2 int, c3 int) COMMENT = 'engine "innodb"';
+drop table t1;
+
+# data type support
+create table t1 (c1 bit primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 tinyint primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 smallint primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 mediumint primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 int primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 bigint primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 double primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 float primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 decimal primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 date primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 time primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 timestamp primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 datetime primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 year primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 char(10) primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 varchar(10) primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 binary(10) primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 varbinary(10) primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 enum("yes","no") primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+create table t1 (c1 set("A","B","AB","O") primary key) COMMENT = 'engine "innodb"';
+desc t1;
+drop table t1;
+
+# error test
+--error ER_REQUIRES_PRIMARY_KEY
+create table t1 (c1 int) COMMENT = 'engine "innodb"';
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/create_table_comment_combined.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/create_table_comment_combined.test
new file mode 100644
index 00000000000..f9a7dc3168b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/create_table_comment_combined.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS bugs;
+--enable_warnings
+
+CREATE TABLE bugs (
+ id INT UNSIGNED PRIMARY KEY
+) DEFAULT CHARSET=utf8
+ COMMENT='Free style normal comment, engine "InnoDB"';
+
+SHOW CREATE TABLE bugs;
+
+DROP TABLE bugs;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/create_table_normalizer_fulltext_index.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/create_table_normalizer_fulltext_index.test
new file mode 100644
index 00000000000..c4265ffd22e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/create_table_normalizer_fulltext_index.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/load_mroonga_functions.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+
+CREATE TABLE diaries (
+ day DATE PRIMARY KEY,
+ content VARCHAR(64) NOT NULL,
+ FULLTEXT INDEX (content) COMMENT 'normalizer "NormalizerAuto"'
+) COMMENT='engine "InnoDB"' DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+INSERT INTO diaries VALUES ("2013-04-23", "ブラックコーヒーを飲んだ。");
+
+SELECT * FROM diaries
+ WHERE MATCH (content) AGAINST ("+ふらつく" IN BOOLEAN MODE);
+SELECT * FROM diaries
+ WHERE MATCH (content) AGAINST ("+ブラック" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/unload_mroonga_functions.inc
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/delete_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/delete_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..1e82a2dff60
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/delete_TODO_SPLIT_ME.test
@@ -0,0 +1,59 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 int) COMMENT 'engine = "innodb"';
+show create table t1;
+insert into t1 values (1, 100);
+insert into t1 values (2, 101);
+insert into t1 values (3, 102);
+insert into t1 values (4, 102);
+select * from t1;
+
+delete from t1 where c1=3;
+select * from t1;
+
+flush tables;
+
+delete from t1 where c1=2;
+select * from t1;
+
+delete from t1;
+select * from t1;
+
+drop table t1;
+
+create table t1 (c1 int primary key, c2 text, fulltext index (c2)) COMMENT 'engine = "innodb"';
+insert into t1 values(10, "aa ii uu ee");
+insert into t1 values(20, "ka ki ku ke");
+insert into t1 values(30, "sa si su se");
+
+select * from t1;
+select * from t1 where match(c2) against("ki");
+delete from t1 where c1=20;
+select * from t1;
+select * from t1 where match(c2) against("ki");
+
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/delete_all.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/delete_all.test
new file mode 100644
index 00000000000..c77a9cdeac3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/delete_all.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS users;
+--enable_warnings
+
+SET NAMES utf8;
+
+CREATE TABLE users (
+ id int PRIMARY KEY,
+ name varchar(100),
+ FULLTEXT INDEX (name)
+) COMMENT 'engine = "InnoDB"' DEFAULT CHARSET=utf8;
+
+INSERT INTO users VALUES (1, 'Alice');
+INSERT INTO users VALUES (2, 'Bob');
+INSERT INTO users VALUES (3, 'Chris');
+
+SELECT * FROM users;
+
+DELETE FROM users;
+
+SELECT * FROM users;
+
+DROP TABLE users;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_leading_not.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_leading_not.test
new file mode 100644
index 00000000000..fb55d609855
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_leading_not.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT PRIMARY KEY,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET = UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("-明日 +天気" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_multiple_match_against.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_multiple_match_against.test
new file mode 100644
index 00000000000..483cca04c8a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_multiple_match_against.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT PRIMARY KEY,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX (title),
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET = UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, "富士山", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気 1月1日", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "天気 4月4日", "今日も天気がよくてきれいに見える。");
+
+SELECT COUNT(*) FROM diaries WHERE MATCH(title) AGAINST("+天気" IN BOOLEAN MODE) AND MATCH(content) AGAINST("+今日" IN BOOLEAN MODE);
+SELECT * FROM diaries WHERE MATCH(title) AGAINST("+天気" IN BOOLEAN MODE) AND MATCH(content) AGAINST("+今日" IN BOOLEAN MODE);
+SELECT 1 FROM diaries WHERE MATCH(title) AGAINST("+天気" IN BOOLEAN MODE) AND MATCH(content) AGAINST("+今日" IN BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.test
new file mode 100644
index 00000000000..2e96b8ed4f8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_minus_no_operator.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Yesterday was good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be fine.");
+INSERT INTO memos VALUES (NULL, "Yesterday was fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*D- fine is be" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_minus_with_or.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_minus_with_or.test
new file mode 100644
index 00000000000..6eb46419c4d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_minus_with_or.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Yesterday was good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be fine.");
+INSERT INTO memos VALUES (NULL, "Yesterday was fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*D- is OR be fine" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.test
new file mode 100644
index 00000000000..301365610c5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_minus_with_plus.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Yesterday was good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be fine.");
+INSERT INTO memos VALUES (NULL, "Yesterday was fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*D- good +day be" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_or_no_operator.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_or_no_operator.test
new file mode 100644
index 00000000000..6a89d7556ab
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_or_no_operator.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be fine.");
+INSERT INTO memos VALUES (NULL, "Yesterday was fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*DOR today good" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_or_with_minus.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_or_with_minus.test
new file mode 100644
index 00000000000..790d2f1c9a9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_or_with_minus.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be fine.");
+INSERT INTO memos VALUES (NULL, "Yesterday was fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*DOR today -good tomorrow" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_or_with_plus.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_or_with_plus.test
new file mode 100644
index 00000000000..a45c414580d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_or_with_plus.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be fine.");
+INSERT INTO memos VALUES (NULL, "Yesterday was fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*DOR today +good tomorrow" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.test
new file mode 100644
index 00000000000..d013578a8f8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_plus_no_operator.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*D+ today good" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.test
new file mode 100644
index 00000000000..91b0a7f7891
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_plus_with_minus.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*D+ today -good is" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_plus_with_or.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_plus_with_or.test
new file mode 100644
index 00000000000..29c1c0d9d98
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_default_operator_plus_with_or.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS memos;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE memos (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ content TEXT,
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET=utf8 COMMENT='engine "InnODB"';
+
+INSERT INTO memos VALUES (NULL, "Today is good day.");
+INSERT INTO memos VALUES (NULL, "Tomorrow will be good day.");
+INSERT INTO memos VALUES (NULL, "Today is fine.");
+
+SELECT * FROM memos
+ WHERE MATCH (content) AGAINST ("*D+ today OR tomorrow day" IN BOOLEAN MODE);
+
+DROP TABLE memos;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_weight_full_spec.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_weight_full_spec.test
new file mode 100644
index 00000000000..027b05d5320
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_weight_full_spec.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id INT PRIMARY KEY,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX (title, content)
+) DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"';
+
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+
+SELECT *, MATCH(title, content)
+ AGAINST("*W1:10,2:2 +天気" in BOOLEAN MODE) AS score
+ FROM diaries
+ WHERE MATCH(title, content)
+ AGAINST("*W1:10,2:2 +天気" in BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_weight_no_weight.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_weight_no_weight.test
new file mode 100644
index 00000000000..41b814de80f
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_weight_no_weight.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id INT PRIMARY KEY,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX (title, content)
+) DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"';
+
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+
+SELECT *, MATCH(title, content)
+ AGAINST("*W1,2:2 +天気" in BOOLEAN MODE) AS score
+ FROM diaries
+ WHERE MATCH(title, content)
+ AGAINST("*W1,2:2 +天気" in BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_weight_omit_section.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_weight_omit_section.test
new file mode 100644
index 00000000000..1db4134d0a0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_boolean_mode_pragma_weight_omit_section.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id INT PRIMARY KEY,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX (title, content)
+) DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"';
+
+INSERT INTO diaries VALUES(1, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, "富士山", "今日も天気がよくてきれいに見える。");
+
+SELECT *, MATCH(title, content)
+ AGAINST("*W1:2 +天気" in BOOLEAN MODE) AS score
+ FROM diaries
+ WHERE MATCH(title, content)
+ AGAINST("*W1:2 +天気" in BOOLEAN MODE);
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_ascii.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_ascii.test
new file mode 100644
index 00000000000..33e23e801ff
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_ascii.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3)) COMMENT = 'engine "innodb"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"sa si su se so");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ee oo");
+select * from t1;
+select * from t1 where match(c3) against("su");
+select * from t1 where match(c3) against("ii");
+select * from t1 where match(c3) against("+su" in boolean mode);
+select * from t1 where match(c3) against("+ii" in boolean mode);
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_cp932.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_cp932.test
new file mode 100644
index 00000000000..75853611f69
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_cp932.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2011 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/have_cp932.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+set names cp932;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset cp932 COMMENT = 'engine "innodb"';
+insert into t1 values(1, "̕xmR̓VCɂ‚","");
+insert into t1 values(2, "","̕xmR̓VC͕܂");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+select * from t1 where match(c2) against("xmR");
+select * from t1 where match(c3) against("xmR");
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_eucjpms.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_eucjpms.test
new file mode 100644
index 00000000000..3f1cba869f8
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_eucjpms.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2011 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/have_eucjpms.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+set names eucjpms;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset eucjpms COMMENT = 'engine "innodb"';
+insert into t1 values(1, "ٻλŷˤĤ","");
+insert into t1 values(2, "","ٻλŷʬޤ");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+select * from t1 where match(c2) against("ٻλ");
+select * from t1 where match(c3) against("ٻλ");
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_japanese.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_japanese.test
new file mode 100644
index 00000000000..27524ac2ed0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_charset_japanese.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+set names utf8;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset utf8 COMMENT = 'engine "innodb"';
+insert into t1 values(1, "明日の富士山の天気について","あああああああ");
+insert into t1 values(2, "いいいいい","明日の富士山の天気は分かりません");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+select * from t1 where match(c2) against("富士山");
+select * from t1 where match(c3) against("富士山");
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_index_recreate.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_index_recreate.test
new file mode 100644
index 00000000000..0115ed0699c
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_index_recreate.test
@@ -0,0 +1,51 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES utf8;
+CREATE TABLE diaries (
+ id int PRIMARY KEY,
+ title varchar(255),
+ content text,
+ FULLTEXT INDEX (title)
+) DEFAULT CHARSET=utf8 COMMENT='ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES (1, "Hello", "はじめました。");
+INSERT INTO diaries VALUES (2, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES (3, "富士山", "今日もきれい。");
+
+SELECT * FROM diaries WHERE MATCH (title) AGAINST ("富士山");
+
+DROP INDEX title ON diaries;
+
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+SELECT * FROM diaries WHERE MATCH (title) AGAINST ("富士山");
+SELECT * FROM diaries;
+
+CREATE FULLTEXT INDEX new_title_index ON diaries (title);
+SELECT * FROM diaries WHERE MATCH (title) AGAINST ("富士山");
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_insert_select.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_insert_select.test
new file mode 100644
index 00000000000..6037ab61da0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_insert_select.test
@@ -0,0 +1,47 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 varchar(100), fulltext index(c2)) default charset utf8 COMMENT = 'engine "innodb"';
+create table t2 (c1 int primary key, c2 text, fulltext index(c2)) default charset utf8 COMMENT = 'engine "innodb"';
+insert into t1 values (1, "aa ii uu ee oo");
+insert into t1 values (2, "ka ki ku ke ko");
+insert into t1 values (3, "aa ii ii ii oo");
+insert into t1 values (4, "sa si su se so");
+insert into t1 values (5, "ta ti ii ii to");
+insert into t2 (c1,c2) select c1,c2 from t1;
+select * from t1;
+select * from t2;
+select * from t1 where c1=3;
+select * from t2 where c1=3;
+select * from t1 where c1>3 order by c1 desc;
+select * from t2 where c1>3 order by c1 asc;
+select * from t1 where c2>"s" order by c2 desc;
+select * from t2 where c2>"s" order by c1 asc;
+select *,match(c2) against("ii") from t1 where match(c2) against("ii") order by match(c2) against("ii") desc;
+select *,match(c2) against("ii") from t2 where match(c2) against("ii") order by match(c2) against("ii") asc;
+select c1,c2,match(c2) against("ii") from t1 where match(c2) against("ii");
+select c1,c2,match(c2) against("ii") from t1 where match(c2) against("ii");
+drop table t1,t2;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_insert_values.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_insert_values.test
new file mode 100644
index 00000000000..43237cdc96a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_insert_values.test
@@ -0,0 +1,35 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 text, fulltext index ft (c2)) COMMENT = 'engine "innodb"';
+show create table t1;
+insert into t1 values (1, "hoge hoge");
+insert into t1 values (2, "fuga fuga");
+insert into t1 values (3, "moge moge");
+select * from t1;
+flush tables;
+select * from t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_many_records.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_many_records.test
new file mode 100644
index 00000000000..c12441f3c5a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_many_records.test
@@ -0,0 +1,4136 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set names utf8;
+create table diaries (
+ id int primary key,
+ title varchar(255),
+ fulltext index (title)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+
+set autocommit=0;
+insert into diaries values(0, "2011-07-14");
+insert into diaries values(1, "2011-07-15");
+insert into diaries values(2, "2011-07-16");
+insert into diaries values(3, "2011-07-17");
+insert into diaries values(4, "2011-07-18");
+insert into diaries values(5, "2011-07-19");
+insert into diaries values(6, "2011-07-20");
+insert into diaries values(7, "2011-07-21");
+insert into diaries values(8, "2011-07-22");
+insert into diaries values(9, "2011-07-23");
+insert into diaries values(10, "2011-07-24");
+insert into diaries values(11, "2011-07-25");
+insert into diaries values(12, "2011-07-26");
+insert into diaries values(13, "2011-07-27");
+insert into diaries values(14, "2011-07-28");
+insert into diaries values(15, "2011-07-29");
+insert into diaries values(16, "2011-07-30");
+insert into diaries values(17, "2011-07-31");
+insert into diaries values(18, "2011-08-01");
+insert into diaries values(19, "2011-08-02");
+insert into diaries values(20, "2011-08-03");
+insert into diaries values(21, "2011-08-04");
+insert into diaries values(22, "2011-08-05");
+insert into diaries values(23, "2011-08-06");
+insert into diaries values(24, "2011-08-07");
+insert into diaries values(25, "2011-08-08");
+insert into diaries values(26, "2011-08-09");
+insert into diaries values(27, "2011-08-10");
+insert into diaries values(28, "2011-08-11");
+insert into diaries values(29, "2011-08-12");
+insert into diaries values(30, "2011-08-13");
+insert into diaries values(31, "2011-08-14");
+insert into diaries values(32, "2011-08-15");
+insert into diaries values(33, "2011-08-16");
+insert into diaries values(34, "2011-08-17");
+insert into diaries values(35, "2011-08-18");
+insert into diaries values(36, "2011-08-19");
+insert into diaries values(37, "2011-08-20");
+insert into diaries values(38, "2011-08-21");
+insert into diaries values(39, "2011-08-22");
+insert into diaries values(40, "2011-08-23");
+insert into diaries values(41, "2011-08-24");
+insert into diaries values(42, "2011-08-25");
+insert into diaries values(43, "2011-08-26");
+insert into diaries values(44, "2011-08-27");
+insert into diaries values(45, "2011-08-28");
+insert into diaries values(46, "2011-08-29");
+insert into diaries values(47, "2011-08-30");
+insert into diaries values(48, "2011-08-31");
+insert into diaries values(49, "2011-09-01");
+insert into diaries values(50, "2011-09-02");
+insert into diaries values(51, "2011-09-03");
+insert into diaries values(52, "2011-09-04");
+insert into diaries values(53, "2011-09-05");
+insert into diaries values(54, "2011-09-06");
+insert into diaries values(55, "2011-09-07");
+insert into diaries values(56, "2011-09-08");
+insert into diaries values(57, "2011-09-09");
+insert into diaries values(58, "2011-09-10");
+insert into diaries values(59, "2011-09-11");
+insert into diaries values(60, "2011-09-12");
+insert into diaries values(61, "2011-09-13");
+insert into diaries values(62, "2011-09-14");
+insert into diaries values(63, "2011-09-15");
+insert into diaries values(64, "2011-09-16");
+insert into diaries values(65, "2011-09-17");
+insert into diaries values(66, "2011-09-18");
+insert into diaries values(67, "2011-09-19");
+insert into diaries values(68, "2011-09-20");
+insert into diaries values(69, "2011-09-21");
+insert into diaries values(70, "2011-09-22");
+insert into diaries values(71, "2011-09-23");
+insert into diaries values(72, "2011-09-24");
+insert into diaries values(73, "2011-09-25");
+insert into diaries values(74, "2011-09-26");
+insert into diaries values(75, "2011-09-27");
+insert into diaries values(76, "2011-09-28");
+insert into diaries values(77, "2011-09-29");
+insert into diaries values(78, "2011-09-30");
+insert into diaries values(79, "2011-10-01");
+insert into diaries values(80, "2011-10-02");
+insert into diaries values(81, "2011-10-03");
+insert into diaries values(82, "2011-10-04");
+insert into diaries values(83, "2011-10-05");
+insert into diaries values(84, "2011-10-06");
+insert into diaries values(85, "2011-10-07");
+insert into diaries values(86, "2011-10-08");
+insert into diaries values(87, "2011-10-09");
+insert into diaries values(88, "2011-10-10");
+insert into diaries values(89, "2011-10-11");
+insert into diaries values(90, "2011-10-12");
+insert into diaries values(91, "2011-10-13");
+insert into diaries values(92, "2011-10-14");
+insert into diaries values(93, "2011-10-15");
+insert into diaries values(94, "2011-10-16");
+insert into diaries values(95, "2011-10-17");
+insert into diaries values(96, "2011-10-18");
+insert into diaries values(97, "2011-10-19");
+insert into diaries values(98, "2011-10-20");
+insert into diaries values(99, "2011-10-21");
+insert into diaries values(100, "2011-10-22");
+insert into diaries values(101, "2011-10-23");
+insert into diaries values(102, "2011-10-24");
+insert into diaries values(103, "2011-10-25");
+insert into diaries values(104, "2011-10-26");
+insert into diaries values(105, "2011-10-27");
+insert into diaries values(106, "2011-10-28");
+insert into diaries values(107, "2011-10-29");
+insert into diaries values(108, "2011-10-30");
+insert into diaries values(109, "2011-10-31");
+insert into diaries values(110, "2011-11-01");
+insert into diaries values(111, "2011-11-02");
+insert into diaries values(112, "2011-11-03");
+insert into diaries values(113, "2011-11-04");
+insert into diaries values(114, "2011-11-05");
+insert into diaries values(115, "2011-11-06");
+insert into diaries values(116, "2011-11-07");
+insert into diaries values(117, "2011-11-08");
+insert into diaries values(118, "2011-11-09");
+insert into diaries values(119, "2011-11-10");
+insert into diaries values(120, "2011-11-11");
+insert into diaries values(121, "2011-11-12");
+insert into diaries values(122, "2011-11-13");
+insert into diaries values(123, "2011-11-14");
+insert into diaries values(124, "2011-11-15");
+insert into diaries values(125, "2011-11-16");
+insert into diaries values(126, "2011-11-17");
+insert into diaries values(127, "2011-11-18");
+insert into diaries values(128, "2011-11-19");
+insert into diaries values(129, "2011-11-20");
+insert into diaries values(130, "2011-11-21");
+insert into diaries values(131, "2011-11-22");
+insert into diaries values(132, "2011-11-23");
+insert into diaries values(133, "2011-11-24");
+insert into diaries values(134, "2011-11-25");
+insert into diaries values(135, "2011-11-26");
+insert into diaries values(136, "2011-11-27");
+insert into diaries values(137, "2011-11-28");
+insert into diaries values(138, "2011-11-29");
+insert into diaries values(139, "2011-11-30");
+insert into diaries values(140, "2011-12-01");
+insert into diaries values(141, "2011-12-02");
+insert into diaries values(142, "2011-12-03");
+insert into diaries values(143, "2011-12-04");
+insert into diaries values(144, "2011-12-05");
+insert into diaries values(145, "2011-12-06");
+insert into diaries values(146, "2011-12-07");
+insert into diaries values(147, "2011-12-08");
+insert into diaries values(148, "2011-12-09");
+insert into diaries values(149, "2011-12-10");
+insert into diaries values(150, "2011-12-11");
+insert into diaries values(151, "2011-12-12");
+insert into diaries values(152, "2011-12-13");
+insert into diaries values(153, "2011-12-14");
+insert into diaries values(154, "2011-12-15");
+insert into diaries values(155, "2011-12-16");
+insert into diaries values(156, "2011-12-17");
+insert into diaries values(157, "2011-12-18");
+insert into diaries values(158, "2011-12-19");
+insert into diaries values(159, "2011-12-20");
+insert into diaries values(160, "2011-12-21");
+insert into diaries values(161, "2011-12-22");
+insert into diaries values(162, "2011-12-23");
+insert into diaries values(163, "2011-12-24");
+insert into diaries values(164, "2011-12-25");
+insert into diaries values(165, "2011-12-26");
+insert into diaries values(166, "2011-12-27");
+insert into diaries values(167, "2011-12-28");
+insert into diaries values(168, "2011-12-29");
+insert into diaries values(169, "2011-12-30");
+insert into diaries values(170, "2011-12-31");
+insert into diaries values(171, "2012-01-01");
+insert into diaries values(172, "2012-01-02");
+insert into diaries values(173, "2012-01-03");
+insert into diaries values(174, "2012-01-04");
+insert into diaries values(175, "2012-01-05");
+insert into diaries values(176, "2012-01-06");
+insert into diaries values(177, "2012-01-07");
+insert into diaries values(178, "2012-01-08");
+insert into diaries values(179, "2012-01-09");
+insert into diaries values(180, "2012-01-10");
+insert into diaries values(181, "2012-01-11");
+insert into diaries values(182, "2012-01-12");
+insert into diaries values(183, "2012-01-13");
+insert into diaries values(184, "2012-01-14");
+insert into diaries values(185, "2012-01-15");
+insert into diaries values(186, "2012-01-16");
+insert into diaries values(187, "2012-01-17");
+insert into diaries values(188, "2012-01-18");
+insert into diaries values(189, "2012-01-19");
+insert into diaries values(190, "2012-01-20");
+insert into diaries values(191, "2012-01-21");
+insert into diaries values(192, "2012-01-22");
+insert into diaries values(193, "2012-01-23");
+insert into diaries values(194, "2012-01-24");
+insert into diaries values(195, "2012-01-25");
+insert into diaries values(196, "2012-01-26");
+insert into diaries values(197, "2012-01-27");
+insert into diaries values(198, "2012-01-28");
+insert into diaries values(199, "2012-01-29");
+insert into diaries values(200, "2012-01-30");
+insert into diaries values(201, "2012-01-31");
+insert into diaries values(202, "2012-02-01");
+insert into diaries values(203, "2012-02-02");
+insert into diaries values(204, "2012-02-03");
+insert into diaries values(205, "2012-02-04");
+insert into diaries values(206, "2012-02-05");
+insert into diaries values(207, "2012-02-06");
+insert into diaries values(208, "2012-02-07");
+insert into diaries values(209, "2012-02-08");
+insert into diaries values(210, "2012-02-09");
+insert into diaries values(211, "2012-02-10");
+insert into diaries values(212, "2012-02-11");
+insert into diaries values(213, "2012-02-12");
+insert into diaries values(214, "2012-02-13");
+insert into diaries values(215, "2012-02-14");
+insert into diaries values(216, "2012-02-15");
+insert into diaries values(217, "2012-02-16");
+insert into diaries values(218, "2012-02-17");
+insert into diaries values(219, "2012-02-18");
+insert into diaries values(220, "2012-02-19");
+insert into diaries values(221, "2012-02-20");
+insert into diaries values(222, "2012-02-21");
+insert into diaries values(223, "2012-02-22");
+insert into diaries values(224, "2012-02-23");
+insert into diaries values(225, "2012-02-24");
+insert into diaries values(226, "2012-02-25");
+insert into diaries values(227, "2012-02-26");
+insert into diaries values(228, "2012-02-27");
+insert into diaries values(229, "2012-02-28");
+insert into diaries values(230, "2012-02-29");
+insert into diaries values(231, "2012-03-01");
+insert into diaries values(232, "2012-03-02");
+insert into diaries values(233, "2012-03-03");
+insert into diaries values(234, "2012-03-04");
+insert into diaries values(235, "2012-03-05");
+insert into diaries values(236, "2012-03-06");
+insert into diaries values(237, "2012-03-07");
+insert into diaries values(238, "2012-03-08");
+insert into diaries values(239, "2012-03-09");
+insert into diaries values(240, "2012-03-10");
+insert into diaries values(241, "2012-03-11");
+insert into diaries values(242, "2012-03-12");
+insert into diaries values(243, "2012-03-13");
+insert into diaries values(244, "2012-03-14");
+insert into diaries values(245, "2012-03-15");
+insert into diaries values(246, "2012-03-16");
+insert into diaries values(247, "2012-03-17");
+insert into diaries values(248, "2012-03-18");
+insert into diaries values(249, "2012-03-19");
+insert into diaries values(250, "2012-03-20");
+insert into diaries values(251, "2012-03-21");
+insert into diaries values(252, "2012-03-22");
+insert into diaries values(253, "2012-03-23");
+insert into diaries values(254, "2012-03-24");
+insert into diaries values(255, "2012-03-25");
+insert into diaries values(256, "2012-03-26");
+insert into diaries values(257, "2012-03-27");
+insert into diaries values(258, "2012-03-28");
+insert into diaries values(259, "2012-03-29");
+insert into diaries values(260, "2012-03-30");
+insert into diaries values(261, "2012-03-31");
+insert into diaries values(262, "2012-04-01");
+insert into diaries values(263, "2012-04-02");
+insert into diaries values(264, "2012-04-03");
+insert into diaries values(265, "2012-04-04");
+insert into diaries values(266, "2012-04-05");
+insert into diaries values(267, "2012-04-06");
+insert into diaries values(268, "2012-04-07");
+insert into diaries values(269, "2012-04-08");
+insert into diaries values(270, "2012-04-09");
+insert into diaries values(271, "2012-04-10");
+insert into diaries values(272, "2012-04-11");
+insert into diaries values(273, "2012-04-12");
+insert into diaries values(274, "2012-04-13");
+insert into diaries values(275, "2012-04-14");
+insert into diaries values(276, "2012-04-15");
+insert into diaries values(277, "2012-04-16");
+insert into diaries values(278, "2012-04-17");
+insert into diaries values(279, "2012-04-18");
+insert into diaries values(280, "2012-04-19");
+insert into diaries values(281, "2012-04-20");
+insert into diaries values(282, "2012-04-21");
+insert into diaries values(283, "2012-04-22");
+insert into diaries values(284, "2012-04-23");
+insert into diaries values(285, "2012-04-24");
+insert into diaries values(286, "2012-04-25");
+insert into diaries values(287, "2012-04-26");
+insert into diaries values(288, "2012-04-27");
+insert into diaries values(289, "2012-04-28");
+insert into diaries values(290, "2012-04-29");
+insert into diaries values(291, "2012-04-30");
+insert into diaries values(292, "2012-05-01");
+insert into diaries values(293, "2012-05-02");
+insert into diaries values(294, "2012-05-03");
+insert into diaries values(295, "2012-05-04");
+insert into diaries values(296, "2012-05-05");
+insert into diaries values(297, "2012-05-06");
+insert into diaries values(298, "2012-05-07");
+insert into diaries values(299, "2012-05-08");
+insert into diaries values(300, "2012-05-09");
+insert into diaries values(301, "2012-05-10");
+insert into diaries values(302, "2012-05-11");
+insert into diaries values(303, "2012-05-12");
+insert into diaries values(304, "2012-05-13");
+insert into diaries values(305, "2012-05-14");
+insert into diaries values(306, "2012-05-15");
+insert into diaries values(307, "2012-05-16");
+insert into diaries values(308, "2012-05-17");
+insert into diaries values(309, "2012-05-18");
+insert into diaries values(310, "2012-05-19");
+insert into diaries values(311, "2012-05-20");
+insert into diaries values(312, "2012-05-21");
+insert into diaries values(313, "2012-05-22");
+insert into diaries values(314, "2012-05-23");
+insert into diaries values(315, "2012-05-24");
+insert into diaries values(316, "2012-05-25");
+insert into diaries values(317, "2012-05-26");
+insert into diaries values(318, "2012-05-27");
+insert into diaries values(319, "2012-05-28");
+insert into diaries values(320, "2012-05-29");
+insert into diaries values(321, "2012-05-30");
+insert into diaries values(322, "2012-05-31");
+insert into diaries values(323, "2012-06-01");
+insert into diaries values(324, "2012-06-02");
+insert into diaries values(325, "2012-06-03");
+insert into diaries values(326, "2012-06-04");
+insert into diaries values(327, "2012-06-05");
+insert into diaries values(328, "2012-06-06");
+insert into diaries values(329, "2012-06-07");
+insert into diaries values(330, "2012-06-08");
+insert into diaries values(331, "2012-06-09");
+insert into diaries values(332, "2012-06-10");
+insert into diaries values(333, "2012-06-11");
+insert into diaries values(334, "2012-06-12");
+insert into diaries values(335, "2012-06-13");
+insert into diaries values(336, "2012-06-14");
+insert into diaries values(337, "2012-06-15");
+insert into diaries values(338, "2012-06-16");
+insert into diaries values(339, "2012-06-17");
+insert into diaries values(340, "2012-06-18");
+insert into diaries values(341, "2012-06-19");
+insert into diaries values(342, "2012-06-20");
+insert into diaries values(343, "2012-06-21");
+insert into diaries values(344, "2012-06-22");
+insert into diaries values(345, "2012-06-23");
+insert into diaries values(346, "2012-06-24");
+insert into diaries values(347, "2012-06-25");
+insert into diaries values(348, "2012-06-26");
+insert into diaries values(349, "2012-06-27");
+insert into diaries values(350, "2012-06-28");
+insert into diaries values(351, "2012-06-29");
+insert into diaries values(352, "2012-06-30");
+insert into diaries values(353, "2012-07-01");
+insert into diaries values(354, "2012-07-02");
+insert into diaries values(355, "2012-07-03");
+insert into diaries values(356, "2012-07-04");
+insert into diaries values(357, "2012-07-05");
+insert into diaries values(358, "2012-07-06");
+insert into diaries values(359, "2012-07-07");
+insert into diaries values(360, "2012-07-08");
+insert into diaries values(361, "2012-07-09");
+insert into diaries values(362, "2012-07-10");
+insert into diaries values(363, "2012-07-11");
+insert into diaries values(364, "2012-07-12");
+insert into diaries values(365, "2012-07-13");
+insert into diaries values(366, "2012-07-14");
+insert into diaries values(367, "2012-07-15");
+insert into diaries values(368, "2012-07-16");
+insert into diaries values(369, "2012-07-17");
+insert into diaries values(370, "2012-07-18");
+insert into diaries values(371, "2012-07-19");
+insert into diaries values(372, "2012-07-20");
+insert into diaries values(373, "2012-07-21");
+insert into diaries values(374, "2012-07-22");
+insert into diaries values(375, "2012-07-23");
+insert into diaries values(376, "2012-07-24");
+insert into diaries values(377, "2012-07-25");
+insert into diaries values(378, "2012-07-26");
+insert into diaries values(379, "2012-07-27");
+insert into diaries values(380, "2012-07-28");
+insert into diaries values(381, "2012-07-29");
+insert into diaries values(382, "2012-07-30");
+insert into diaries values(383, "2012-07-31");
+insert into diaries values(384, "2012-08-01");
+insert into diaries values(385, "2012-08-02");
+insert into diaries values(386, "2012-08-03");
+insert into diaries values(387, "2012-08-04");
+insert into diaries values(388, "2012-08-05");
+insert into diaries values(389, "2012-08-06");
+insert into diaries values(390, "2012-08-07");
+insert into diaries values(391, "2012-08-08");
+insert into diaries values(392, "2012-08-09");
+insert into diaries values(393, "2012-08-10");
+insert into diaries values(394, "2012-08-11");
+insert into diaries values(395, "2012-08-12");
+insert into diaries values(396, "2012-08-13");
+insert into diaries values(397, "2012-08-14");
+insert into diaries values(398, "2012-08-15");
+insert into diaries values(399, "2012-08-16");
+insert into diaries values(400, "2012-08-17");
+insert into diaries values(401, "2012-08-18");
+insert into diaries values(402, "2012-08-19");
+insert into diaries values(403, "2012-08-20");
+insert into diaries values(404, "2012-08-21");
+insert into diaries values(405, "2012-08-22");
+insert into diaries values(406, "2012-08-23");
+insert into diaries values(407, "2012-08-24");
+insert into diaries values(408, "2012-08-25");
+insert into diaries values(409, "2012-08-26");
+insert into diaries values(410, "2012-08-27");
+insert into diaries values(411, "2012-08-28");
+insert into diaries values(412, "2012-08-29");
+insert into diaries values(413, "2012-08-30");
+insert into diaries values(414, "2012-08-31");
+insert into diaries values(415, "2012-09-01");
+insert into diaries values(416, "2012-09-02");
+insert into diaries values(417, "2012-09-03");
+insert into diaries values(418, "2012-09-04");
+insert into diaries values(419, "2012-09-05");
+insert into diaries values(420, "2012-09-06");
+insert into diaries values(421, "2012-09-07");
+insert into diaries values(422, "2012-09-08");
+insert into diaries values(423, "2012-09-09");
+insert into diaries values(424, "2012-09-10");
+insert into diaries values(425, "2012-09-11");
+insert into diaries values(426, "2012-09-12");
+insert into diaries values(427, "2012-09-13");
+insert into diaries values(428, "2012-09-14");
+insert into diaries values(429, "2012-09-15");
+insert into diaries values(430, "2012-09-16");
+insert into diaries values(431, "2012-09-17");
+insert into diaries values(432, "2012-09-18");
+insert into diaries values(433, "2012-09-19");
+insert into diaries values(434, "2012-09-20");
+insert into diaries values(435, "2012-09-21");
+insert into diaries values(436, "2012-09-22");
+insert into diaries values(437, "2012-09-23");
+insert into diaries values(438, "2012-09-24");
+insert into diaries values(439, "2012-09-25");
+insert into diaries values(440, "2012-09-26");
+insert into diaries values(441, "2012-09-27");
+insert into diaries values(442, "2012-09-28");
+insert into diaries values(443, "2012-09-29");
+insert into diaries values(444, "2012-09-30");
+insert into diaries values(445, "2012-10-01");
+insert into diaries values(446, "2012-10-02");
+insert into diaries values(447, "2012-10-03");
+insert into diaries values(448, "2012-10-04");
+insert into diaries values(449, "2012-10-05");
+insert into diaries values(450, "2012-10-06");
+insert into diaries values(451, "2012-10-07");
+insert into diaries values(452, "2012-10-08");
+insert into diaries values(453, "2012-10-09");
+insert into diaries values(454, "2012-10-10");
+insert into diaries values(455, "2012-10-11");
+insert into diaries values(456, "2012-10-12");
+insert into diaries values(457, "2012-10-13");
+insert into diaries values(458, "2012-10-14");
+insert into diaries values(459, "2012-10-15");
+insert into diaries values(460, "2012-10-16");
+insert into diaries values(461, "2012-10-17");
+insert into diaries values(462, "2012-10-18");
+insert into diaries values(463, "2012-10-19");
+insert into diaries values(464, "2012-10-20");
+insert into diaries values(465, "2012-10-21");
+insert into diaries values(466, "2012-10-22");
+insert into diaries values(467, "2012-10-23");
+insert into diaries values(468, "2012-10-24");
+insert into diaries values(469, "2012-10-25");
+insert into diaries values(470, "2012-10-26");
+insert into diaries values(471, "2012-10-27");
+insert into diaries values(472, "2012-10-28");
+insert into diaries values(473, "2012-10-29");
+insert into diaries values(474, "2012-10-30");
+insert into diaries values(475, "2012-10-31");
+insert into diaries values(476, "2012-11-01");
+insert into diaries values(477, "2012-11-02");
+insert into diaries values(478, "2012-11-03");
+insert into diaries values(479, "2012-11-04");
+insert into diaries values(480, "2012-11-05");
+insert into diaries values(481, "2012-11-06");
+insert into diaries values(482, "2012-11-07");
+insert into diaries values(483, "2012-11-08");
+insert into diaries values(484, "2012-11-09");
+insert into diaries values(485, "2012-11-10");
+insert into diaries values(486, "2012-11-11");
+insert into diaries values(487, "2012-11-12");
+insert into diaries values(488, "2012-11-13");
+insert into diaries values(489, "2012-11-14");
+insert into diaries values(490, "2012-11-15");
+insert into diaries values(491, "2012-11-16");
+insert into diaries values(492, "2012-11-17");
+insert into diaries values(493, "2012-11-18");
+insert into diaries values(494, "2012-11-19");
+insert into diaries values(495, "2012-11-20");
+insert into diaries values(496, "2012-11-21");
+insert into diaries values(497, "2012-11-22");
+insert into diaries values(498, "2012-11-23");
+insert into diaries values(499, "2012-11-24");
+insert into diaries values(500, "2012-11-25");
+insert into diaries values(501, "2012-11-26");
+insert into diaries values(502, "2012-11-27");
+insert into diaries values(503, "2012-11-28");
+insert into diaries values(504, "2012-11-29");
+insert into diaries values(505, "2012-11-30");
+insert into diaries values(506, "2012-12-01");
+insert into diaries values(507, "2012-12-02");
+insert into diaries values(508, "2012-12-03");
+insert into diaries values(509, "2012-12-04");
+insert into diaries values(510, "2012-12-05");
+insert into diaries values(511, "2012-12-06");
+insert into diaries values(512, "2012-12-07");
+insert into diaries values(513, "2012-12-08");
+insert into diaries values(514, "2012-12-09");
+insert into diaries values(515, "2012-12-10");
+insert into diaries values(516, "2012-12-11");
+insert into diaries values(517, "2012-12-12");
+insert into diaries values(518, "2012-12-13");
+insert into diaries values(519, "2012-12-14");
+insert into diaries values(520, "2012-12-15");
+insert into diaries values(521, "2012-12-16");
+insert into diaries values(522, "2012-12-17");
+insert into diaries values(523, "2012-12-18");
+insert into diaries values(524, "2012-12-19");
+insert into diaries values(525, "2012-12-20");
+insert into diaries values(526, "2012-12-21");
+insert into diaries values(527, "2012-12-22");
+insert into diaries values(528, "2012-12-23");
+insert into diaries values(529, "2012-12-24");
+insert into diaries values(530, "2012-12-25");
+insert into diaries values(531, "2012-12-26");
+insert into diaries values(532, "2012-12-27");
+insert into diaries values(533, "2012-12-28");
+insert into diaries values(534, "2012-12-29");
+insert into diaries values(535, "2012-12-30");
+insert into diaries values(536, "2012-12-31");
+insert into diaries values(537, "2013-01-01");
+insert into diaries values(538, "2013-01-02");
+insert into diaries values(539, "2013-01-03");
+insert into diaries values(540, "2013-01-04");
+insert into diaries values(541, "2013-01-05");
+insert into diaries values(542, "2013-01-06");
+insert into diaries values(543, "2013-01-07");
+insert into diaries values(544, "2013-01-08");
+insert into diaries values(545, "2013-01-09");
+insert into diaries values(546, "2013-01-10");
+insert into diaries values(547, "2013-01-11");
+insert into diaries values(548, "2013-01-12");
+insert into diaries values(549, "2013-01-13");
+insert into diaries values(550, "2013-01-14");
+insert into diaries values(551, "2013-01-15");
+insert into diaries values(552, "2013-01-16");
+insert into diaries values(553, "2013-01-17");
+insert into diaries values(554, "2013-01-18");
+insert into diaries values(555, "2013-01-19");
+insert into diaries values(556, "2013-01-20");
+insert into diaries values(557, "2013-01-21");
+insert into diaries values(558, "2013-01-22");
+insert into diaries values(559, "2013-01-23");
+insert into diaries values(560, "2013-01-24");
+insert into diaries values(561, "2013-01-25");
+insert into diaries values(562, "2013-01-26");
+insert into diaries values(563, "2013-01-27");
+insert into diaries values(564, "2013-01-28");
+insert into diaries values(565, "2013-01-29");
+insert into diaries values(566, "2013-01-30");
+insert into diaries values(567, "2013-01-31");
+insert into diaries values(568, "2013-02-01");
+insert into diaries values(569, "2013-02-02");
+insert into diaries values(570, "2013-02-03");
+insert into diaries values(571, "2013-02-04");
+insert into diaries values(572, "2013-02-05");
+insert into diaries values(573, "2013-02-06");
+insert into diaries values(574, "2013-02-07");
+insert into diaries values(575, "2013-02-08");
+insert into diaries values(576, "2013-02-09");
+insert into diaries values(577, "2013-02-10");
+insert into diaries values(578, "2013-02-11");
+insert into diaries values(579, "2013-02-12");
+insert into diaries values(580, "2013-02-13");
+insert into diaries values(581, "2013-02-14");
+insert into diaries values(582, "2013-02-15");
+insert into diaries values(583, "2013-02-16");
+insert into diaries values(584, "2013-02-17");
+insert into diaries values(585, "2013-02-18");
+insert into diaries values(586, "2013-02-19");
+insert into diaries values(587, "2013-02-20");
+insert into diaries values(588, "2013-02-21");
+insert into diaries values(589, "2013-02-22");
+insert into diaries values(590, "2013-02-23");
+insert into diaries values(591, "2013-02-24");
+insert into diaries values(592, "2013-02-25");
+insert into diaries values(593, "2013-02-26");
+insert into diaries values(594, "2013-02-27");
+insert into diaries values(595, "2013-02-28");
+insert into diaries values(596, "2013-03-01");
+insert into diaries values(597, "2013-03-02");
+insert into diaries values(598, "2013-03-03");
+insert into diaries values(599, "2013-03-04");
+insert into diaries values(600, "2013-03-05");
+insert into diaries values(601, "2013-03-06");
+insert into diaries values(602, "2013-03-07");
+insert into diaries values(603, "2013-03-08");
+insert into diaries values(604, "2013-03-09");
+insert into diaries values(605, "2013-03-10");
+insert into diaries values(606, "2013-03-11");
+insert into diaries values(607, "2013-03-12");
+insert into diaries values(608, "2013-03-13");
+insert into diaries values(609, "2013-03-14");
+insert into diaries values(610, "2013-03-15");
+insert into diaries values(611, "2013-03-16");
+insert into diaries values(612, "2013-03-17");
+insert into diaries values(613, "2013-03-18");
+insert into diaries values(614, "2013-03-19");
+insert into diaries values(615, "2013-03-20");
+insert into diaries values(616, "2013-03-21");
+insert into diaries values(617, "2013-03-22");
+insert into diaries values(618, "2013-03-23");
+insert into diaries values(619, "2013-03-24");
+insert into diaries values(620, "2013-03-25");
+insert into diaries values(621, "2013-03-26");
+insert into diaries values(622, "2013-03-27");
+insert into diaries values(623, "2013-03-28");
+insert into diaries values(624, "2013-03-29");
+insert into diaries values(625, "2013-03-30");
+insert into diaries values(626, "2013-03-31");
+insert into diaries values(627, "2013-04-01");
+insert into diaries values(628, "2013-04-02");
+insert into diaries values(629, "2013-04-03");
+insert into diaries values(630, "2013-04-04");
+insert into diaries values(631, "2013-04-05");
+insert into diaries values(632, "2013-04-06");
+insert into diaries values(633, "2013-04-07");
+insert into diaries values(634, "2013-04-08");
+insert into diaries values(635, "2013-04-09");
+insert into diaries values(636, "2013-04-10");
+insert into diaries values(637, "2013-04-11");
+insert into diaries values(638, "2013-04-12");
+insert into diaries values(639, "2013-04-13");
+insert into diaries values(640, "2013-04-14");
+insert into diaries values(641, "2013-04-15");
+insert into diaries values(642, "2013-04-16");
+insert into diaries values(643, "2013-04-17");
+insert into diaries values(644, "2013-04-18");
+insert into diaries values(645, "2013-04-19");
+insert into diaries values(646, "2013-04-20");
+insert into diaries values(647, "2013-04-21");
+insert into diaries values(648, "2013-04-22");
+insert into diaries values(649, "2013-04-23");
+insert into diaries values(650, "2013-04-24");
+insert into diaries values(651, "2013-04-25");
+insert into diaries values(652, "2013-04-26");
+insert into diaries values(653, "2013-04-27");
+insert into diaries values(654, "2013-04-28");
+insert into diaries values(655, "2013-04-29");
+insert into diaries values(656, "2013-04-30");
+insert into diaries values(657, "2013-05-01");
+insert into diaries values(658, "2013-05-02");
+insert into diaries values(659, "2013-05-03");
+insert into diaries values(660, "2013-05-04");
+insert into diaries values(661, "2013-05-05");
+insert into diaries values(662, "2013-05-06");
+insert into diaries values(663, "2013-05-07");
+insert into diaries values(664, "2013-05-08");
+insert into diaries values(665, "2013-05-09");
+insert into diaries values(666, "2013-05-10");
+insert into diaries values(667, "2013-05-11");
+insert into diaries values(668, "2013-05-12");
+insert into diaries values(669, "2013-05-13");
+insert into diaries values(670, "2013-05-14");
+insert into diaries values(671, "2013-05-15");
+insert into diaries values(672, "2013-05-16");
+insert into diaries values(673, "2013-05-17");
+insert into diaries values(674, "2013-05-18");
+insert into diaries values(675, "2013-05-19");
+insert into diaries values(676, "2013-05-20");
+insert into diaries values(677, "2013-05-21");
+insert into diaries values(678, "2013-05-22");
+insert into diaries values(679, "2013-05-23");
+insert into diaries values(680, "2013-05-24");
+insert into diaries values(681, "2013-05-25");
+insert into diaries values(682, "2013-05-26");
+insert into diaries values(683, "2013-05-27");
+insert into diaries values(684, "2013-05-28");
+insert into diaries values(685, "2013-05-29");
+insert into diaries values(686, "2013-05-30");
+insert into diaries values(687, "2013-05-31");
+insert into diaries values(688, "2013-06-01");
+insert into diaries values(689, "2013-06-02");
+insert into diaries values(690, "2013-06-03");
+insert into diaries values(691, "2013-06-04");
+insert into diaries values(692, "2013-06-05");
+insert into diaries values(693, "2013-06-06");
+insert into diaries values(694, "2013-06-07");
+insert into diaries values(695, "2013-06-08");
+insert into diaries values(696, "2013-06-09");
+insert into diaries values(697, "2013-06-10");
+insert into diaries values(698, "2013-06-11");
+insert into diaries values(699, "2013-06-12");
+insert into diaries values(700, "2013-06-13");
+insert into diaries values(701, "2013-06-14");
+insert into diaries values(702, "2013-06-15");
+insert into diaries values(703, "2013-06-16");
+insert into diaries values(704, "2013-06-17");
+insert into diaries values(705, "2013-06-18");
+insert into diaries values(706, "2013-06-19");
+insert into diaries values(707, "2013-06-20");
+insert into diaries values(708, "2013-06-21");
+insert into diaries values(709, "2013-06-22");
+insert into diaries values(710, "2013-06-23");
+insert into diaries values(711, "2013-06-24");
+insert into diaries values(712, "2013-06-25");
+insert into diaries values(713, "2013-06-26");
+insert into diaries values(714, "2013-06-27");
+insert into diaries values(715, "2013-06-28");
+insert into diaries values(716, "2013-06-29");
+insert into diaries values(717, "2013-06-30");
+insert into diaries values(718, "2013-07-01");
+insert into diaries values(719, "2013-07-02");
+insert into diaries values(720, "2013-07-03");
+insert into diaries values(721, "2013-07-04");
+insert into diaries values(722, "2013-07-05");
+insert into diaries values(723, "2013-07-06");
+insert into diaries values(724, "2013-07-07");
+insert into diaries values(725, "2013-07-08");
+insert into diaries values(726, "2013-07-09");
+insert into diaries values(727, "2013-07-10");
+insert into diaries values(728, "2013-07-11");
+insert into diaries values(729, "2013-07-12");
+insert into diaries values(730, "2013-07-13");
+insert into diaries values(731, "2013-07-14");
+insert into diaries values(732, "2013-07-15");
+insert into diaries values(733, "2013-07-16");
+insert into diaries values(734, "2013-07-17");
+insert into diaries values(735, "2013-07-18");
+insert into diaries values(736, "2013-07-19");
+insert into diaries values(737, "2013-07-20");
+insert into diaries values(738, "2013-07-21");
+insert into diaries values(739, "2013-07-22");
+insert into diaries values(740, "2013-07-23");
+insert into diaries values(741, "2013-07-24");
+insert into diaries values(742, "2013-07-25");
+insert into diaries values(743, "2013-07-26");
+insert into diaries values(744, "2013-07-27");
+insert into diaries values(745, "2013-07-28");
+insert into diaries values(746, "2013-07-29");
+insert into diaries values(747, "2013-07-30");
+insert into diaries values(748, "2013-07-31");
+insert into diaries values(749, "2013-08-01");
+insert into diaries values(750, "2013-08-02");
+insert into diaries values(751, "2013-08-03");
+insert into diaries values(752, "2013-08-04");
+insert into diaries values(753, "2013-08-05");
+insert into diaries values(754, "2013-08-06");
+insert into diaries values(755, "2013-08-07");
+insert into diaries values(756, "2013-08-08");
+insert into diaries values(757, "2013-08-09");
+insert into diaries values(758, "2013-08-10");
+insert into diaries values(759, "2013-08-11");
+insert into diaries values(760, "2013-08-12");
+insert into diaries values(761, "2013-08-13");
+insert into diaries values(762, "2013-08-14");
+insert into diaries values(763, "2013-08-15");
+insert into diaries values(764, "2013-08-16");
+insert into diaries values(765, "2013-08-17");
+insert into diaries values(766, "2013-08-18");
+insert into diaries values(767, "2013-08-19");
+insert into diaries values(768, "2013-08-20");
+insert into diaries values(769, "2013-08-21");
+insert into diaries values(770, "2013-08-22");
+insert into diaries values(771, "2013-08-23");
+insert into diaries values(772, "2013-08-24");
+insert into diaries values(773, "2013-08-25");
+insert into diaries values(774, "2013-08-26");
+insert into diaries values(775, "2013-08-27");
+insert into diaries values(776, "2013-08-28");
+insert into diaries values(777, "2013-08-29");
+insert into diaries values(778, "2013-08-30");
+insert into diaries values(779, "2013-08-31");
+insert into diaries values(780, "2013-09-01");
+insert into diaries values(781, "2013-09-02");
+insert into diaries values(782, "2013-09-03");
+insert into diaries values(783, "2013-09-04");
+insert into diaries values(784, "2013-09-05");
+insert into diaries values(785, "2013-09-06");
+insert into diaries values(786, "2013-09-07");
+insert into diaries values(787, "2013-09-08");
+insert into diaries values(788, "2013-09-09");
+insert into diaries values(789, "2013-09-10");
+insert into diaries values(790, "2013-09-11");
+insert into diaries values(791, "2013-09-12");
+insert into diaries values(792, "2013-09-13");
+insert into diaries values(793, "2013-09-14");
+insert into diaries values(794, "2013-09-15");
+insert into diaries values(795, "2013-09-16");
+insert into diaries values(796, "2013-09-17");
+insert into diaries values(797, "2013-09-18");
+insert into diaries values(798, "2013-09-19");
+insert into diaries values(799, "2013-09-20");
+insert into diaries values(800, "2013-09-21");
+insert into diaries values(801, "2013-09-22");
+insert into diaries values(802, "2013-09-23");
+insert into diaries values(803, "2013-09-24");
+insert into diaries values(804, "2013-09-25");
+insert into diaries values(805, "2013-09-26");
+insert into diaries values(806, "2013-09-27");
+insert into diaries values(807, "2013-09-28");
+insert into diaries values(808, "2013-09-29");
+insert into diaries values(809, "2013-09-30");
+insert into diaries values(810, "2013-10-01");
+insert into diaries values(811, "2013-10-02");
+insert into diaries values(812, "2013-10-03");
+insert into diaries values(813, "2013-10-04");
+insert into diaries values(814, "2013-10-05");
+insert into diaries values(815, "2013-10-06");
+insert into diaries values(816, "2013-10-07");
+insert into diaries values(817, "2013-10-08");
+insert into diaries values(818, "2013-10-09");
+insert into diaries values(819, "2013-10-10");
+insert into diaries values(820, "2013-10-11");
+insert into diaries values(821, "2013-10-12");
+insert into diaries values(822, "2013-10-13");
+insert into diaries values(823, "2013-10-14");
+insert into diaries values(824, "2013-10-15");
+insert into diaries values(825, "2013-10-16");
+insert into diaries values(826, "2013-10-17");
+insert into diaries values(827, "2013-10-18");
+insert into diaries values(828, "2013-10-19");
+insert into diaries values(829, "2013-10-20");
+insert into diaries values(830, "2013-10-21");
+insert into diaries values(831, "2013-10-22");
+insert into diaries values(832, "2013-10-23");
+insert into diaries values(833, "2013-10-24");
+insert into diaries values(834, "2013-10-25");
+insert into diaries values(835, "2013-10-26");
+insert into diaries values(836, "2013-10-27");
+insert into diaries values(837, "2013-10-28");
+insert into diaries values(838, "2013-10-29");
+insert into diaries values(839, "2013-10-30");
+insert into diaries values(840, "2013-10-31");
+insert into diaries values(841, "2013-11-01");
+insert into diaries values(842, "2013-11-02");
+insert into diaries values(843, "2013-11-03");
+insert into diaries values(844, "2013-11-04");
+insert into diaries values(845, "2013-11-05");
+insert into diaries values(846, "2013-11-06");
+insert into diaries values(847, "2013-11-07");
+insert into diaries values(848, "2013-11-08");
+insert into diaries values(849, "2013-11-09");
+insert into diaries values(850, "2013-11-10");
+insert into diaries values(851, "2013-11-11");
+insert into diaries values(852, "2013-11-12");
+insert into diaries values(853, "2013-11-13");
+insert into diaries values(854, "2013-11-14");
+insert into diaries values(855, "2013-11-15");
+insert into diaries values(856, "2013-11-16");
+insert into diaries values(857, "2013-11-17");
+insert into diaries values(858, "2013-11-18");
+insert into diaries values(859, "2013-11-19");
+insert into diaries values(860, "2013-11-20");
+insert into diaries values(861, "2013-11-21");
+insert into diaries values(862, "2013-11-22");
+insert into diaries values(863, "2013-11-23");
+insert into diaries values(864, "2013-11-24");
+insert into diaries values(865, "2013-11-25");
+insert into diaries values(866, "2013-11-26");
+insert into diaries values(867, "2013-11-27");
+insert into diaries values(868, "2013-11-28");
+insert into diaries values(869, "2013-11-29");
+insert into diaries values(870, "2013-11-30");
+insert into diaries values(871, "2013-12-01");
+insert into diaries values(872, "2013-12-02");
+insert into diaries values(873, "2013-12-03");
+insert into diaries values(874, "2013-12-04");
+insert into diaries values(875, "2013-12-05");
+insert into diaries values(876, "2013-12-06");
+insert into diaries values(877, "2013-12-07");
+insert into diaries values(878, "2013-12-08");
+insert into diaries values(879, "2013-12-09");
+insert into diaries values(880, "2013-12-10");
+insert into diaries values(881, "2013-12-11");
+insert into diaries values(882, "2013-12-12");
+insert into diaries values(883, "2013-12-13");
+insert into diaries values(884, "2013-12-14");
+insert into diaries values(885, "2013-12-15");
+insert into diaries values(886, "2013-12-16");
+insert into diaries values(887, "2013-12-17");
+insert into diaries values(888, "2013-12-18");
+insert into diaries values(889, "2013-12-19");
+insert into diaries values(890, "2013-12-20");
+insert into diaries values(891, "2013-12-21");
+insert into diaries values(892, "2013-12-22");
+insert into diaries values(893, "2013-12-23");
+insert into diaries values(894, "2013-12-24");
+insert into diaries values(895, "2013-12-25");
+insert into diaries values(896, "2013-12-26");
+insert into diaries values(897, "2013-12-27");
+insert into diaries values(898, "2013-12-28");
+insert into diaries values(899, "2013-12-29");
+insert into diaries values(900, "2013-12-30");
+insert into diaries values(901, "2013-12-31");
+insert into diaries values(902, "2014-01-01");
+insert into diaries values(903, "2014-01-02");
+insert into diaries values(904, "2014-01-03");
+insert into diaries values(905, "2014-01-04");
+insert into diaries values(906, "2014-01-05");
+insert into diaries values(907, "2014-01-06");
+insert into diaries values(908, "2014-01-07");
+insert into diaries values(909, "2014-01-08");
+insert into diaries values(910, "2014-01-09");
+insert into diaries values(911, "2014-01-10");
+insert into diaries values(912, "2014-01-11");
+insert into diaries values(913, "2014-01-12");
+insert into diaries values(914, "2014-01-13");
+insert into diaries values(915, "2014-01-14");
+insert into diaries values(916, "2014-01-15");
+insert into diaries values(917, "2014-01-16");
+insert into diaries values(918, "2014-01-17");
+insert into diaries values(919, "2014-01-18");
+insert into diaries values(920, "2014-01-19");
+insert into diaries values(921, "2014-01-20");
+insert into diaries values(922, "2014-01-21");
+insert into diaries values(923, "2014-01-22");
+insert into diaries values(924, "2014-01-23");
+insert into diaries values(925, "2014-01-24");
+insert into diaries values(926, "2014-01-25");
+insert into diaries values(927, "2014-01-26");
+insert into diaries values(928, "2014-01-27");
+insert into diaries values(929, "2014-01-28");
+insert into diaries values(930, "2014-01-29");
+insert into diaries values(931, "2014-01-30");
+insert into diaries values(932, "2014-01-31");
+insert into diaries values(933, "2014-02-01");
+insert into diaries values(934, "2014-02-02");
+insert into diaries values(935, "2014-02-03");
+insert into diaries values(936, "2014-02-04");
+insert into diaries values(937, "2014-02-05");
+insert into diaries values(938, "2014-02-06");
+insert into diaries values(939, "2014-02-07");
+insert into diaries values(940, "2014-02-08");
+insert into diaries values(941, "2014-02-09");
+insert into diaries values(942, "2014-02-10");
+insert into diaries values(943, "2014-02-11");
+insert into diaries values(944, "2014-02-12");
+insert into diaries values(945, "2014-02-13");
+insert into diaries values(946, "2014-02-14");
+insert into diaries values(947, "2014-02-15");
+insert into diaries values(948, "2014-02-16");
+insert into diaries values(949, "2014-02-17");
+insert into diaries values(950, "2014-02-18");
+insert into diaries values(951, "2014-02-19");
+insert into diaries values(952, "2014-02-20");
+insert into diaries values(953, "2014-02-21");
+insert into diaries values(954, "2014-02-22");
+insert into diaries values(955, "2014-02-23");
+insert into diaries values(956, "2014-02-24");
+insert into diaries values(957, "2014-02-25");
+insert into diaries values(958, "2014-02-26");
+insert into diaries values(959, "2014-02-27");
+insert into diaries values(960, "2014-02-28");
+insert into diaries values(961, "2014-03-01");
+insert into diaries values(962, "2014-03-02");
+insert into diaries values(963, "2014-03-03");
+insert into diaries values(964, "2014-03-04");
+insert into diaries values(965, "2014-03-05");
+insert into diaries values(966, "2014-03-06");
+insert into diaries values(967, "2014-03-07");
+insert into diaries values(968, "2014-03-08");
+insert into diaries values(969, "2014-03-09");
+insert into diaries values(970, "2014-03-10");
+insert into diaries values(971, "2014-03-11");
+insert into diaries values(972, "2014-03-12");
+insert into diaries values(973, "2014-03-13");
+insert into diaries values(974, "2014-03-14");
+insert into diaries values(975, "2014-03-15");
+insert into diaries values(976, "2014-03-16");
+insert into diaries values(977, "2014-03-17");
+insert into diaries values(978, "2014-03-18");
+insert into diaries values(979, "2014-03-19");
+insert into diaries values(980, "2014-03-20");
+insert into diaries values(981, "2014-03-21");
+insert into diaries values(982, "2014-03-22");
+insert into diaries values(983, "2014-03-23");
+insert into diaries values(984, "2014-03-24");
+insert into diaries values(985, "2014-03-25");
+insert into diaries values(986, "2014-03-26");
+insert into diaries values(987, "2014-03-27");
+insert into diaries values(988, "2014-03-28");
+insert into diaries values(989, "2014-03-29");
+insert into diaries values(990, "2014-03-30");
+insert into diaries values(991, "2014-03-31");
+insert into diaries values(992, "2014-04-01");
+insert into diaries values(993, "2014-04-02");
+insert into diaries values(994, "2014-04-03");
+insert into diaries values(995, "2014-04-04");
+insert into diaries values(996, "2014-04-05");
+insert into diaries values(997, "2014-04-06");
+insert into diaries values(998, "2014-04-07");
+insert into diaries values(999, "2014-04-08");
+insert into diaries values(1000, "2014-04-09");
+insert into diaries values(1001, "2014-04-10");
+insert into diaries values(1002, "2014-04-11");
+insert into diaries values(1003, "2014-04-12");
+insert into diaries values(1004, "2014-04-13");
+insert into diaries values(1005, "2014-04-14");
+insert into diaries values(1006, "2014-04-15");
+insert into diaries values(1007, "2014-04-16");
+insert into diaries values(1008, "2014-04-17");
+insert into diaries values(1009, "2014-04-18");
+insert into diaries values(1010, "2014-04-19");
+insert into diaries values(1011, "2014-04-20");
+insert into diaries values(1012, "2014-04-21");
+insert into diaries values(1013, "2014-04-22");
+insert into diaries values(1014, "2014-04-23");
+insert into diaries values(1015, "2014-04-24");
+insert into diaries values(1016, "2014-04-25");
+insert into diaries values(1017, "2014-04-26");
+insert into diaries values(1018, "2014-04-27");
+insert into diaries values(1019, "2014-04-28");
+insert into diaries values(1020, "2014-04-29");
+insert into diaries values(1021, "2014-04-30");
+insert into diaries values(1022, "2014-05-01");
+insert into diaries values(1023, "2014-05-02");
+insert into diaries values(1024, "2014-05-03");
+insert into diaries values(1025, "2014-05-04");
+insert into diaries values(1026, "2014-05-05");
+insert into diaries values(1027, "2014-05-06");
+insert into diaries values(1028, "2014-05-07");
+insert into diaries values(1029, "2014-05-08");
+insert into diaries values(1030, "2014-05-09");
+insert into diaries values(1031, "2014-05-10");
+insert into diaries values(1032, "2014-05-11");
+insert into diaries values(1033, "2014-05-12");
+insert into diaries values(1034, "2014-05-13");
+insert into diaries values(1035, "2014-05-14");
+insert into diaries values(1036, "2014-05-15");
+insert into diaries values(1037, "2014-05-16");
+insert into diaries values(1038, "2014-05-17");
+insert into diaries values(1039, "2014-05-18");
+insert into diaries values(1040, "2014-05-19");
+insert into diaries values(1041, "2014-05-20");
+insert into diaries values(1042, "2014-05-21");
+insert into diaries values(1043, "2014-05-22");
+insert into diaries values(1044, "2014-05-23");
+insert into diaries values(1045, "2014-05-24");
+insert into diaries values(1046, "2014-05-25");
+insert into diaries values(1047, "2014-05-26");
+insert into diaries values(1048, "2014-05-27");
+insert into diaries values(1049, "2014-05-28");
+insert into diaries values(1050, "2014-05-29");
+insert into diaries values(1051, "2014-05-30");
+insert into diaries values(1052, "2014-05-31");
+insert into diaries values(1053, "2014-06-01");
+insert into diaries values(1054, "2014-06-02");
+insert into diaries values(1055, "2014-06-03");
+insert into diaries values(1056, "2014-06-04");
+insert into diaries values(1057, "2014-06-05");
+insert into diaries values(1058, "2014-06-06");
+insert into diaries values(1059, "2014-06-07");
+insert into diaries values(1060, "2014-06-08");
+insert into diaries values(1061, "2014-06-09");
+insert into diaries values(1062, "2014-06-10");
+insert into diaries values(1063, "2014-06-11");
+insert into diaries values(1064, "2014-06-12");
+insert into diaries values(1065, "2014-06-13");
+insert into diaries values(1066, "2014-06-14");
+insert into diaries values(1067, "2014-06-15");
+insert into diaries values(1068, "2014-06-16");
+insert into diaries values(1069, "2014-06-17");
+insert into diaries values(1070, "2014-06-18");
+insert into diaries values(1071, "2014-06-19");
+insert into diaries values(1072, "2014-06-20");
+insert into diaries values(1073, "2014-06-21");
+insert into diaries values(1074, "2014-06-22");
+insert into diaries values(1075, "2014-06-23");
+insert into diaries values(1076, "2014-06-24");
+insert into diaries values(1077, "2014-06-25");
+insert into diaries values(1078, "2014-06-26");
+insert into diaries values(1079, "2014-06-27");
+insert into diaries values(1080, "2014-06-28");
+insert into diaries values(1081, "2014-06-29");
+insert into diaries values(1082, "2014-06-30");
+insert into diaries values(1083, "2014-07-01");
+insert into diaries values(1084, "2014-07-02");
+insert into diaries values(1085, "2014-07-03");
+insert into diaries values(1086, "2014-07-04");
+insert into diaries values(1087, "2014-07-05");
+insert into diaries values(1088, "2014-07-06");
+insert into diaries values(1089, "2014-07-07");
+insert into diaries values(1090, "2014-07-08");
+insert into diaries values(1091, "2014-07-09");
+insert into diaries values(1092, "2014-07-10");
+insert into diaries values(1093, "2014-07-11");
+insert into diaries values(1094, "2014-07-12");
+insert into diaries values(1095, "2014-07-13");
+insert into diaries values(1096, "2014-07-14");
+insert into diaries values(1097, "2014-07-15");
+insert into diaries values(1098, "2014-07-16");
+insert into diaries values(1099, "2014-07-17");
+insert into diaries values(1100, "2014-07-18");
+insert into diaries values(1101, "2014-07-19");
+insert into diaries values(1102, "2014-07-20");
+insert into diaries values(1103, "2014-07-21");
+insert into diaries values(1104, "2014-07-22");
+insert into diaries values(1105, "2014-07-23");
+insert into diaries values(1106, "2014-07-24");
+insert into diaries values(1107, "2014-07-25");
+insert into diaries values(1108, "2014-07-26");
+insert into diaries values(1109, "2014-07-27");
+insert into diaries values(1110, "2014-07-28");
+insert into diaries values(1111, "2014-07-29");
+insert into diaries values(1112, "2014-07-30");
+insert into diaries values(1113, "2014-07-31");
+insert into diaries values(1114, "2014-08-01");
+insert into diaries values(1115, "2014-08-02");
+insert into diaries values(1116, "2014-08-03");
+insert into diaries values(1117, "2014-08-04");
+insert into diaries values(1118, "2014-08-05");
+insert into diaries values(1119, "2014-08-06");
+insert into diaries values(1120, "2014-08-07");
+insert into diaries values(1121, "2014-08-08");
+insert into diaries values(1122, "2014-08-09");
+insert into diaries values(1123, "2014-08-10");
+insert into diaries values(1124, "2014-08-11");
+insert into diaries values(1125, "2014-08-12");
+insert into diaries values(1126, "2014-08-13");
+insert into diaries values(1127, "2014-08-14");
+insert into diaries values(1128, "2014-08-15");
+insert into diaries values(1129, "2014-08-16");
+insert into diaries values(1130, "2014-08-17");
+insert into diaries values(1131, "2014-08-18");
+insert into diaries values(1132, "2014-08-19");
+insert into diaries values(1133, "2014-08-20");
+insert into diaries values(1134, "2014-08-21");
+insert into diaries values(1135, "2014-08-22");
+insert into diaries values(1136, "2014-08-23");
+insert into diaries values(1137, "2014-08-24");
+insert into diaries values(1138, "2014-08-25");
+insert into diaries values(1139, "2014-08-26");
+insert into diaries values(1140, "2014-08-27");
+insert into diaries values(1141, "2014-08-28");
+insert into diaries values(1142, "2014-08-29");
+insert into diaries values(1143, "2014-08-30");
+insert into diaries values(1144, "2014-08-31");
+insert into diaries values(1145, "2014-09-01");
+insert into diaries values(1146, "2014-09-02");
+insert into diaries values(1147, "2014-09-03");
+insert into diaries values(1148, "2014-09-04");
+insert into diaries values(1149, "2014-09-05");
+insert into diaries values(1150, "2014-09-06");
+insert into diaries values(1151, "2014-09-07");
+insert into diaries values(1152, "2014-09-08");
+insert into diaries values(1153, "2014-09-09");
+insert into diaries values(1154, "2014-09-10");
+insert into diaries values(1155, "2014-09-11");
+insert into diaries values(1156, "2014-09-12");
+insert into diaries values(1157, "2014-09-13");
+insert into diaries values(1158, "2014-09-14");
+insert into diaries values(1159, "2014-09-15");
+insert into diaries values(1160, "2014-09-16");
+insert into diaries values(1161, "2014-09-17");
+insert into diaries values(1162, "2014-09-18");
+insert into diaries values(1163, "2014-09-19");
+insert into diaries values(1164, "2014-09-20");
+insert into diaries values(1165, "2014-09-21");
+insert into diaries values(1166, "2014-09-22");
+insert into diaries values(1167, "2014-09-23");
+insert into diaries values(1168, "2014-09-24");
+insert into diaries values(1169, "2014-09-25");
+insert into diaries values(1170, "2014-09-26");
+insert into diaries values(1171, "2014-09-27");
+insert into diaries values(1172, "2014-09-28");
+insert into diaries values(1173, "2014-09-29");
+insert into diaries values(1174, "2014-09-30");
+insert into diaries values(1175, "2014-10-01");
+insert into diaries values(1176, "2014-10-02");
+insert into diaries values(1177, "2014-10-03");
+insert into diaries values(1178, "2014-10-04");
+insert into diaries values(1179, "2014-10-05");
+insert into diaries values(1180, "2014-10-06");
+insert into diaries values(1181, "2014-10-07");
+insert into diaries values(1182, "2014-10-08");
+insert into diaries values(1183, "2014-10-09");
+insert into diaries values(1184, "2014-10-10");
+insert into diaries values(1185, "2014-10-11");
+insert into diaries values(1186, "2014-10-12");
+insert into diaries values(1187, "2014-10-13");
+insert into diaries values(1188, "2014-10-14");
+insert into diaries values(1189, "2014-10-15");
+insert into diaries values(1190, "2014-10-16");
+insert into diaries values(1191, "2014-10-17");
+insert into diaries values(1192, "2014-10-18");
+insert into diaries values(1193, "2014-10-19");
+insert into diaries values(1194, "2014-10-20");
+insert into diaries values(1195, "2014-10-21");
+insert into diaries values(1196, "2014-10-22");
+insert into diaries values(1197, "2014-10-23");
+insert into diaries values(1198, "2014-10-24");
+insert into diaries values(1199, "2014-10-25");
+insert into diaries values(1200, "2014-10-26");
+insert into diaries values(1201, "2014-10-27");
+insert into diaries values(1202, "2014-10-28");
+insert into diaries values(1203, "2014-10-29");
+insert into diaries values(1204, "2014-10-30");
+insert into diaries values(1205, "2014-10-31");
+insert into diaries values(1206, "2014-11-01");
+insert into diaries values(1207, "2014-11-02");
+insert into diaries values(1208, "2014-11-03");
+insert into diaries values(1209, "2014-11-04");
+insert into diaries values(1210, "2014-11-05");
+insert into diaries values(1211, "2014-11-06");
+insert into diaries values(1212, "2014-11-07");
+insert into diaries values(1213, "2014-11-08");
+insert into diaries values(1214, "2014-11-09");
+insert into diaries values(1215, "2014-11-10");
+insert into diaries values(1216, "2014-11-11");
+insert into diaries values(1217, "2014-11-12");
+insert into diaries values(1218, "2014-11-13");
+insert into diaries values(1219, "2014-11-14");
+insert into diaries values(1220, "2014-11-15");
+insert into diaries values(1221, "2014-11-16");
+insert into diaries values(1222, "2014-11-17");
+insert into diaries values(1223, "2014-11-18");
+insert into diaries values(1224, "2014-11-19");
+insert into diaries values(1225, "2014-11-20");
+insert into diaries values(1226, "2014-11-21");
+insert into diaries values(1227, "2014-11-22");
+insert into diaries values(1228, "2014-11-23");
+insert into diaries values(1229, "2014-11-24");
+insert into diaries values(1230, "2014-11-25");
+insert into diaries values(1231, "2014-11-26");
+insert into diaries values(1232, "2014-11-27");
+insert into diaries values(1233, "2014-11-28");
+insert into diaries values(1234, "2014-11-29");
+insert into diaries values(1235, "2014-11-30");
+insert into diaries values(1236, "2014-12-01");
+insert into diaries values(1237, "2014-12-02");
+insert into diaries values(1238, "2014-12-03");
+insert into diaries values(1239, "2014-12-04");
+insert into diaries values(1240, "2014-12-05");
+insert into diaries values(1241, "2014-12-06");
+insert into diaries values(1242, "2014-12-07");
+insert into diaries values(1243, "2014-12-08");
+insert into diaries values(1244, "2014-12-09");
+insert into diaries values(1245, "2014-12-10");
+insert into diaries values(1246, "2014-12-11");
+insert into diaries values(1247, "2014-12-12");
+insert into diaries values(1248, "2014-12-13");
+insert into diaries values(1249, "2014-12-14");
+insert into diaries values(1250, "2014-12-15");
+insert into diaries values(1251, "2014-12-16");
+insert into diaries values(1252, "2014-12-17");
+insert into diaries values(1253, "2014-12-18");
+insert into diaries values(1254, "2014-12-19");
+insert into diaries values(1255, "2014-12-20");
+insert into diaries values(1256, "2014-12-21");
+insert into diaries values(1257, "2014-12-22");
+insert into diaries values(1258, "2014-12-23");
+insert into diaries values(1259, "2014-12-24");
+insert into diaries values(1260, "2014-12-25");
+insert into diaries values(1261, "2014-12-26");
+insert into diaries values(1262, "2014-12-27");
+insert into diaries values(1263, "2014-12-28");
+insert into diaries values(1264, "2014-12-29");
+insert into diaries values(1265, "2014-12-30");
+insert into diaries values(1266, "2014-12-31");
+insert into diaries values(1267, "2015-01-01");
+insert into diaries values(1268, "2015-01-02");
+insert into diaries values(1269, "2015-01-03");
+insert into diaries values(1270, "2015-01-04");
+insert into diaries values(1271, "2015-01-05");
+insert into diaries values(1272, "2015-01-06");
+insert into diaries values(1273, "2015-01-07");
+insert into diaries values(1274, "2015-01-08");
+insert into diaries values(1275, "2015-01-09");
+insert into diaries values(1276, "2015-01-10");
+insert into diaries values(1277, "2015-01-11");
+insert into diaries values(1278, "2015-01-12");
+insert into diaries values(1279, "2015-01-13");
+insert into diaries values(1280, "2015-01-14");
+insert into diaries values(1281, "2015-01-15");
+insert into diaries values(1282, "2015-01-16");
+insert into diaries values(1283, "2015-01-17");
+insert into diaries values(1284, "2015-01-18");
+insert into diaries values(1285, "2015-01-19");
+insert into diaries values(1286, "2015-01-20");
+insert into diaries values(1287, "2015-01-21");
+insert into diaries values(1288, "2015-01-22");
+insert into diaries values(1289, "2015-01-23");
+insert into diaries values(1290, "2015-01-24");
+insert into diaries values(1291, "2015-01-25");
+insert into diaries values(1292, "2015-01-26");
+insert into diaries values(1293, "2015-01-27");
+insert into diaries values(1294, "2015-01-28");
+insert into diaries values(1295, "2015-01-29");
+insert into diaries values(1296, "2015-01-30");
+insert into diaries values(1297, "2015-01-31");
+insert into diaries values(1298, "2015-02-01");
+insert into diaries values(1299, "2015-02-02");
+insert into diaries values(1300, "2015-02-03");
+insert into diaries values(1301, "2015-02-04");
+insert into diaries values(1302, "2015-02-05");
+insert into diaries values(1303, "2015-02-06");
+insert into diaries values(1304, "2015-02-07");
+insert into diaries values(1305, "2015-02-08");
+insert into diaries values(1306, "2015-02-09");
+insert into diaries values(1307, "2015-02-10");
+insert into diaries values(1308, "2015-02-11");
+insert into diaries values(1309, "2015-02-12");
+insert into diaries values(1310, "2015-02-13");
+insert into diaries values(1311, "2015-02-14");
+insert into diaries values(1312, "2015-02-15");
+insert into diaries values(1313, "2015-02-16");
+insert into diaries values(1314, "2015-02-17");
+insert into diaries values(1315, "2015-02-18");
+insert into diaries values(1316, "2015-02-19");
+insert into diaries values(1317, "2015-02-20");
+insert into diaries values(1318, "2015-02-21");
+insert into diaries values(1319, "2015-02-22");
+insert into diaries values(1320, "2015-02-23");
+insert into diaries values(1321, "2015-02-24");
+insert into diaries values(1322, "2015-02-25");
+insert into diaries values(1323, "2015-02-26");
+insert into diaries values(1324, "2015-02-27");
+insert into diaries values(1325, "2015-02-28");
+insert into diaries values(1326, "2015-03-01");
+insert into diaries values(1327, "2015-03-02");
+insert into diaries values(1328, "2015-03-03");
+insert into diaries values(1329, "2015-03-04");
+insert into diaries values(1330, "2015-03-05");
+insert into diaries values(1331, "2015-03-06");
+insert into diaries values(1332, "2015-03-07");
+insert into diaries values(1333, "2015-03-08");
+insert into diaries values(1334, "2015-03-09");
+insert into diaries values(1335, "2015-03-10");
+insert into diaries values(1336, "2015-03-11");
+insert into diaries values(1337, "2015-03-12");
+insert into diaries values(1338, "2015-03-13");
+insert into diaries values(1339, "2015-03-14");
+insert into diaries values(1340, "2015-03-15");
+insert into diaries values(1341, "2015-03-16");
+insert into diaries values(1342, "2015-03-17");
+insert into diaries values(1343, "2015-03-18");
+insert into diaries values(1344, "2015-03-19");
+insert into diaries values(1345, "2015-03-20");
+insert into diaries values(1346, "2015-03-21");
+insert into diaries values(1347, "2015-03-22");
+insert into diaries values(1348, "2015-03-23");
+insert into diaries values(1349, "2015-03-24");
+insert into diaries values(1350, "2015-03-25");
+insert into diaries values(1351, "2015-03-26");
+insert into diaries values(1352, "2015-03-27");
+insert into diaries values(1353, "2015-03-28");
+insert into diaries values(1354, "2015-03-29");
+insert into diaries values(1355, "2015-03-30");
+insert into diaries values(1356, "2015-03-31");
+insert into diaries values(1357, "2015-04-01");
+insert into diaries values(1358, "2015-04-02");
+insert into diaries values(1359, "2015-04-03");
+insert into diaries values(1360, "2015-04-04");
+insert into diaries values(1361, "2015-04-05");
+insert into diaries values(1362, "2015-04-06");
+insert into diaries values(1363, "2015-04-07");
+insert into diaries values(1364, "2015-04-08");
+insert into diaries values(1365, "2015-04-09");
+insert into diaries values(1366, "2015-04-10");
+insert into diaries values(1367, "2015-04-11");
+insert into diaries values(1368, "2015-04-12");
+insert into diaries values(1369, "2015-04-13");
+insert into diaries values(1370, "2015-04-14");
+insert into diaries values(1371, "2015-04-15");
+insert into diaries values(1372, "2015-04-16");
+insert into diaries values(1373, "2015-04-17");
+insert into diaries values(1374, "2015-04-18");
+insert into diaries values(1375, "2015-04-19");
+insert into diaries values(1376, "2015-04-20");
+insert into diaries values(1377, "2015-04-21");
+insert into diaries values(1378, "2015-04-22");
+insert into diaries values(1379, "2015-04-23");
+insert into diaries values(1380, "2015-04-24");
+insert into diaries values(1381, "2015-04-25");
+insert into diaries values(1382, "2015-04-26");
+insert into diaries values(1383, "2015-04-27");
+insert into diaries values(1384, "2015-04-28");
+insert into diaries values(1385, "2015-04-29");
+insert into diaries values(1386, "2015-04-30");
+insert into diaries values(1387, "2015-05-01");
+insert into diaries values(1388, "2015-05-02");
+insert into diaries values(1389, "2015-05-03");
+insert into diaries values(1390, "2015-05-04");
+insert into diaries values(1391, "2015-05-05");
+insert into diaries values(1392, "2015-05-06");
+insert into diaries values(1393, "2015-05-07");
+insert into diaries values(1394, "2015-05-08");
+insert into diaries values(1395, "2015-05-09");
+insert into diaries values(1396, "2015-05-10");
+insert into diaries values(1397, "2015-05-11");
+insert into diaries values(1398, "2015-05-12");
+insert into diaries values(1399, "2015-05-13");
+insert into diaries values(1400, "2015-05-14");
+insert into diaries values(1401, "2015-05-15");
+insert into diaries values(1402, "2015-05-16");
+insert into diaries values(1403, "2015-05-17");
+insert into diaries values(1404, "2015-05-18");
+insert into diaries values(1405, "2015-05-19");
+insert into diaries values(1406, "2015-05-20");
+insert into diaries values(1407, "2015-05-21");
+insert into diaries values(1408, "2015-05-22");
+insert into diaries values(1409, "2015-05-23");
+insert into diaries values(1410, "2015-05-24");
+insert into diaries values(1411, "2015-05-25");
+insert into diaries values(1412, "2015-05-26");
+insert into diaries values(1413, "2015-05-27");
+insert into diaries values(1414, "2015-05-28");
+insert into diaries values(1415, "2015-05-29");
+insert into diaries values(1416, "2015-05-30");
+insert into diaries values(1417, "2015-05-31");
+insert into diaries values(1418, "2015-06-01");
+insert into diaries values(1419, "2015-06-02");
+insert into diaries values(1420, "2015-06-03");
+insert into diaries values(1421, "2015-06-04");
+insert into diaries values(1422, "2015-06-05");
+insert into diaries values(1423, "2015-06-06");
+insert into diaries values(1424, "2015-06-07");
+insert into diaries values(1425, "2015-06-08");
+insert into diaries values(1426, "2015-06-09");
+insert into diaries values(1427, "2015-06-10");
+insert into diaries values(1428, "2015-06-11");
+insert into diaries values(1429, "2015-06-12");
+insert into diaries values(1430, "2015-06-13");
+insert into diaries values(1431, "2015-06-14");
+insert into diaries values(1432, "2015-06-15");
+insert into diaries values(1433, "2015-06-16");
+insert into diaries values(1434, "2015-06-17");
+insert into diaries values(1435, "2015-06-18");
+insert into diaries values(1436, "2015-06-19");
+insert into diaries values(1437, "2015-06-20");
+insert into diaries values(1438, "2015-06-21");
+insert into diaries values(1439, "2015-06-22");
+insert into diaries values(1440, "2015-06-23");
+insert into diaries values(1441, "2015-06-24");
+insert into diaries values(1442, "2015-06-25");
+insert into diaries values(1443, "2015-06-26");
+insert into diaries values(1444, "2015-06-27");
+insert into diaries values(1445, "2015-06-28");
+insert into diaries values(1446, "2015-06-29");
+insert into diaries values(1447, "2015-06-30");
+insert into diaries values(1448, "2015-07-01");
+insert into diaries values(1449, "2015-07-02");
+insert into diaries values(1450, "2015-07-03");
+insert into diaries values(1451, "2015-07-04");
+insert into diaries values(1452, "2015-07-05");
+insert into diaries values(1453, "2015-07-06");
+insert into diaries values(1454, "2015-07-07");
+insert into diaries values(1455, "2015-07-08");
+insert into diaries values(1456, "2015-07-09");
+insert into diaries values(1457, "2015-07-10");
+insert into diaries values(1458, "2015-07-11");
+insert into diaries values(1459, "2015-07-12");
+insert into diaries values(1460, "2015-07-13");
+insert into diaries values(1461, "2015-07-14");
+insert into diaries values(1462, "2015-07-15");
+insert into diaries values(1463, "2015-07-16");
+insert into diaries values(1464, "2015-07-17");
+insert into diaries values(1465, "2015-07-18");
+insert into diaries values(1466, "2015-07-19");
+insert into diaries values(1467, "2015-07-20");
+insert into diaries values(1468, "2015-07-21");
+insert into diaries values(1469, "2015-07-22");
+insert into diaries values(1470, "2015-07-23");
+insert into diaries values(1471, "2015-07-24");
+insert into diaries values(1472, "2015-07-25");
+insert into diaries values(1473, "2015-07-26");
+insert into diaries values(1474, "2015-07-27");
+insert into diaries values(1475, "2015-07-28");
+insert into diaries values(1476, "2015-07-29");
+insert into diaries values(1477, "2015-07-30");
+insert into diaries values(1478, "2015-07-31");
+insert into diaries values(1479, "2015-08-01");
+insert into diaries values(1480, "2015-08-02");
+insert into diaries values(1481, "2015-08-03");
+insert into diaries values(1482, "2015-08-04");
+insert into diaries values(1483, "2015-08-05");
+insert into diaries values(1484, "2015-08-06");
+insert into diaries values(1485, "2015-08-07");
+insert into diaries values(1486, "2015-08-08");
+insert into diaries values(1487, "2015-08-09");
+insert into diaries values(1488, "2015-08-10");
+insert into diaries values(1489, "2015-08-11");
+insert into diaries values(1490, "2015-08-12");
+insert into diaries values(1491, "2015-08-13");
+insert into diaries values(1492, "2015-08-14");
+insert into diaries values(1493, "2015-08-15");
+insert into diaries values(1494, "2015-08-16");
+insert into diaries values(1495, "2015-08-17");
+insert into diaries values(1496, "2015-08-18");
+insert into diaries values(1497, "2015-08-19");
+insert into diaries values(1498, "2015-08-20");
+insert into diaries values(1499, "2015-08-21");
+insert into diaries values(1500, "2015-08-22");
+insert into diaries values(1501, "2015-08-23");
+insert into diaries values(1502, "2015-08-24");
+insert into diaries values(1503, "2015-08-25");
+insert into diaries values(1504, "2015-08-26");
+insert into diaries values(1505, "2015-08-27");
+insert into diaries values(1506, "2015-08-28");
+insert into diaries values(1507, "2015-08-29");
+insert into diaries values(1508, "2015-08-30");
+insert into diaries values(1509, "2015-08-31");
+insert into diaries values(1510, "2015-09-01");
+insert into diaries values(1511, "2015-09-02");
+insert into diaries values(1512, "2015-09-03");
+insert into diaries values(1513, "2015-09-04");
+insert into diaries values(1514, "2015-09-05");
+insert into diaries values(1515, "2015-09-06");
+insert into diaries values(1516, "2015-09-07");
+insert into diaries values(1517, "2015-09-08");
+insert into diaries values(1518, "2015-09-09");
+insert into diaries values(1519, "2015-09-10");
+insert into diaries values(1520, "2015-09-11");
+insert into diaries values(1521, "2015-09-12");
+insert into diaries values(1522, "2015-09-13");
+insert into diaries values(1523, "2015-09-14");
+insert into diaries values(1524, "2015-09-15");
+insert into diaries values(1525, "2015-09-16");
+insert into diaries values(1526, "2015-09-17");
+insert into diaries values(1527, "2015-09-18");
+insert into diaries values(1528, "2015-09-19");
+insert into diaries values(1529, "2015-09-20");
+insert into diaries values(1530, "2015-09-21");
+insert into diaries values(1531, "2015-09-22");
+insert into diaries values(1532, "2015-09-23");
+insert into diaries values(1533, "2015-09-24");
+insert into diaries values(1534, "2015-09-25");
+insert into diaries values(1535, "2015-09-26");
+insert into diaries values(1536, "2015-09-27");
+insert into diaries values(1537, "2015-09-28");
+insert into diaries values(1538, "2015-09-29");
+insert into diaries values(1539, "2015-09-30");
+insert into diaries values(1540, "2015-10-01");
+insert into diaries values(1541, "2015-10-02");
+insert into diaries values(1542, "2015-10-03");
+insert into diaries values(1543, "2015-10-04");
+insert into diaries values(1544, "2015-10-05");
+insert into diaries values(1545, "2015-10-06");
+insert into diaries values(1546, "2015-10-07");
+insert into diaries values(1547, "2015-10-08");
+insert into diaries values(1548, "2015-10-09");
+insert into diaries values(1549, "2015-10-10");
+insert into diaries values(1550, "2015-10-11");
+insert into diaries values(1551, "2015-10-12");
+insert into diaries values(1552, "2015-10-13");
+insert into diaries values(1553, "2015-10-14");
+insert into diaries values(1554, "2015-10-15");
+insert into diaries values(1555, "2015-10-16");
+insert into diaries values(1556, "2015-10-17");
+insert into diaries values(1557, "2015-10-18");
+insert into diaries values(1558, "2015-10-19");
+insert into diaries values(1559, "2015-10-20");
+insert into diaries values(1560, "2015-10-21");
+insert into diaries values(1561, "2015-10-22");
+insert into diaries values(1562, "2015-10-23");
+insert into diaries values(1563, "2015-10-24");
+insert into diaries values(1564, "2015-10-25");
+insert into diaries values(1565, "2015-10-26");
+insert into diaries values(1566, "2015-10-27");
+insert into diaries values(1567, "2015-10-28");
+insert into diaries values(1568, "2015-10-29");
+insert into diaries values(1569, "2015-10-30");
+insert into diaries values(1570, "2015-10-31");
+insert into diaries values(1571, "2015-11-01");
+insert into diaries values(1572, "2015-11-02");
+insert into diaries values(1573, "2015-11-03");
+insert into diaries values(1574, "2015-11-04");
+insert into diaries values(1575, "2015-11-05");
+insert into diaries values(1576, "2015-11-06");
+insert into diaries values(1577, "2015-11-07");
+insert into diaries values(1578, "2015-11-08");
+insert into diaries values(1579, "2015-11-09");
+insert into diaries values(1580, "2015-11-10");
+insert into diaries values(1581, "2015-11-11");
+insert into diaries values(1582, "2015-11-12");
+insert into diaries values(1583, "2015-11-13");
+insert into diaries values(1584, "2015-11-14");
+insert into diaries values(1585, "2015-11-15");
+insert into diaries values(1586, "2015-11-16");
+insert into diaries values(1587, "2015-11-17");
+insert into diaries values(1588, "2015-11-18");
+insert into diaries values(1589, "2015-11-19");
+insert into diaries values(1590, "2015-11-20");
+insert into diaries values(1591, "2015-11-21");
+insert into diaries values(1592, "2015-11-22");
+insert into diaries values(1593, "2015-11-23");
+insert into diaries values(1594, "2015-11-24");
+insert into diaries values(1595, "2015-11-25");
+insert into diaries values(1596, "2015-11-26");
+insert into diaries values(1597, "2015-11-27");
+insert into diaries values(1598, "2015-11-28");
+insert into diaries values(1599, "2015-11-29");
+insert into diaries values(1600, "2015-11-30");
+insert into diaries values(1601, "2015-12-01");
+insert into diaries values(1602, "2015-12-02");
+insert into diaries values(1603, "2015-12-03");
+insert into diaries values(1604, "2015-12-04");
+insert into diaries values(1605, "2015-12-05");
+insert into diaries values(1606, "2015-12-06");
+insert into diaries values(1607, "2015-12-07");
+insert into diaries values(1608, "2015-12-08");
+insert into diaries values(1609, "2015-12-09");
+insert into diaries values(1610, "2015-12-10");
+insert into diaries values(1611, "2015-12-11");
+insert into diaries values(1612, "2015-12-12");
+insert into diaries values(1613, "2015-12-13");
+insert into diaries values(1614, "2015-12-14");
+insert into diaries values(1615, "2015-12-15");
+insert into diaries values(1616, "2015-12-16");
+insert into diaries values(1617, "2015-12-17");
+insert into diaries values(1618, "2015-12-18");
+insert into diaries values(1619, "2015-12-19");
+insert into diaries values(1620, "2015-12-20");
+insert into diaries values(1621, "2015-12-21");
+insert into diaries values(1622, "2015-12-22");
+insert into diaries values(1623, "2015-12-23");
+insert into diaries values(1624, "2015-12-24");
+insert into diaries values(1625, "2015-12-25");
+insert into diaries values(1626, "2015-12-26");
+insert into diaries values(1627, "2015-12-27");
+insert into diaries values(1628, "2015-12-28");
+insert into diaries values(1629, "2015-12-29");
+insert into diaries values(1630, "2015-12-30");
+insert into diaries values(1631, "2015-12-31");
+insert into diaries values(1632, "2016-01-01");
+insert into diaries values(1633, "2016-01-02");
+insert into diaries values(1634, "2016-01-03");
+insert into diaries values(1635, "2016-01-04");
+insert into diaries values(1636, "2016-01-05");
+insert into diaries values(1637, "2016-01-06");
+insert into diaries values(1638, "2016-01-07");
+insert into diaries values(1639, "2016-01-08");
+insert into diaries values(1640, "2016-01-09");
+insert into diaries values(1641, "2016-01-10");
+insert into diaries values(1642, "2016-01-11");
+insert into diaries values(1643, "2016-01-12");
+insert into diaries values(1644, "2016-01-13");
+insert into diaries values(1645, "2016-01-14");
+insert into diaries values(1646, "2016-01-15");
+insert into diaries values(1647, "2016-01-16");
+insert into diaries values(1648, "2016-01-17");
+insert into diaries values(1649, "2016-01-18");
+insert into diaries values(1650, "2016-01-19");
+insert into diaries values(1651, "2016-01-20");
+insert into diaries values(1652, "2016-01-21");
+insert into diaries values(1653, "2016-01-22");
+insert into diaries values(1654, "2016-01-23");
+insert into diaries values(1655, "2016-01-24");
+insert into diaries values(1656, "2016-01-25");
+insert into diaries values(1657, "2016-01-26");
+insert into diaries values(1658, "2016-01-27");
+insert into diaries values(1659, "2016-01-28");
+insert into diaries values(1660, "2016-01-29");
+insert into diaries values(1661, "2016-01-30");
+insert into diaries values(1662, "2016-01-31");
+insert into diaries values(1663, "2016-02-01");
+insert into diaries values(1664, "2016-02-02");
+insert into diaries values(1665, "2016-02-03");
+insert into diaries values(1666, "2016-02-04");
+insert into diaries values(1667, "2016-02-05");
+insert into diaries values(1668, "2016-02-06");
+insert into diaries values(1669, "2016-02-07");
+insert into diaries values(1670, "2016-02-08");
+insert into diaries values(1671, "2016-02-09");
+insert into diaries values(1672, "2016-02-10");
+insert into diaries values(1673, "2016-02-11");
+insert into diaries values(1674, "2016-02-12");
+insert into diaries values(1675, "2016-02-13");
+insert into diaries values(1676, "2016-02-14");
+insert into diaries values(1677, "2016-02-15");
+insert into diaries values(1678, "2016-02-16");
+insert into diaries values(1679, "2016-02-17");
+insert into diaries values(1680, "2016-02-18");
+insert into diaries values(1681, "2016-02-19");
+insert into diaries values(1682, "2016-02-20");
+insert into diaries values(1683, "2016-02-21");
+insert into diaries values(1684, "2016-02-22");
+insert into diaries values(1685, "2016-02-23");
+insert into diaries values(1686, "2016-02-24");
+insert into diaries values(1687, "2016-02-25");
+insert into diaries values(1688, "2016-02-26");
+insert into diaries values(1689, "2016-02-27");
+insert into diaries values(1690, "2016-02-28");
+insert into diaries values(1691, "2016-02-29");
+insert into diaries values(1692, "2016-03-01");
+insert into diaries values(1693, "2016-03-02");
+insert into diaries values(1694, "2016-03-03");
+insert into diaries values(1695, "2016-03-04");
+insert into diaries values(1696, "2016-03-05");
+insert into diaries values(1697, "2016-03-06");
+insert into diaries values(1698, "2016-03-07");
+insert into diaries values(1699, "2016-03-08");
+insert into diaries values(1700, "2016-03-09");
+insert into diaries values(1701, "2016-03-10");
+insert into diaries values(1702, "2016-03-11");
+insert into diaries values(1703, "2016-03-12");
+insert into diaries values(1704, "2016-03-13");
+insert into diaries values(1705, "2016-03-14");
+insert into diaries values(1706, "2016-03-15");
+insert into diaries values(1707, "2016-03-16");
+insert into diaries values(1708, "2016-03-17");
+insert into diaries values(1709, "2016-03-18");
+insert into diaries values(1710, "2016-03-19");
+insert into diaries values(1711, "2016-03-20");
+insert into diaries values(1712, "2016-03-21");
+insert into diaries values(1713, "2016-03-22");
+insert into diaries values(1714, "2016-03-23");
+insert into diaries values(1715, "2016-03-24");
+insert into diaries values(1716, "2016-03-25");
+insert into diaries values(1717, "2016-03-26");
+insert into diaries values(1718, "2016-03-27");
+insert into diaries values(1719, "2016-03-28");
+insert into diaries values(1720, "2016-03-29");
+insert into diaries values(1721, "2016-03-30");
+insert into diaries values(1722, "2016-03-31");
+insert into diaries values(1723, "2016-04-01");
+insert into diaries values(1724, "2016-04-02");
+insert into diaries values(1725, "2016-04-03");
+insert into diaries values(1726, "2016-04-04");
+insert into diaries values(1727, "2016-04-05");
+insert into diaries values(1728, "2016-04-06");
+insert into diaries values(1729, "2016-04-07");
+insert into diaries values(1730, "2016-04-08");
+insert into diaries values(1731, "2016-04-09");
+insert into diaries values(1732, "2016-04-10");
+insert into diaries values(1733, "2016-04-11");
+insert into diaries values(1734, "2016-04-12");
+insert into diaries values(1735, "2016-04-13");
+insert into diaries values(1736, "2016-04-14");
+insert into diaries values(1737, "2016-04-15");
+insert into diaries values(1738, "2016-04-16");
+insert into diaries values(1739, "2016-04-17");
+insert into diaries values(1740, "2016-04-18");
+insert into diaries values(1741, "2016-04-19");
+insert into diaries values(1742, "2016-04-20");
+insert into diaries values(1743, "2016-04-21");
+insert into diaries values(1744, "2016-04-22");
+insert into diaries values(1745, "2016-04-23");
+insert into diaries values(1746, "2016-04-24");
+insert into diaries values(1747, "2016-04-25");
+insert into diaries values(1748, "2016-04-26");
+insert into diaries values(1749, "2016-04-27");
+insert into diaries values(1750, "2016-04-28");
+insert into diaries values(1751, "2016-04-29");
+insert into diaries values(1752, "2016-04-30");
+insert into diaries values(1753, "2016-05-01");
+insert into diaries values(1754, "2016-05-02");
+insert into diaries values(1755, "2016-05-03");
+insert into diaries values(1756, "2016-05-04");
+insert into diaries values(1757, "2016-05-05");
+insert into diaries values(1758, "2016-05-06");
+insert into diaries values(1759, "2016-05-07");
+insert into diaries values(1760, "2016-05-08");
+insert into diaries values(1761, "2016-05-09");
+insert into diaries values(1762, "2016-05-10");
+insert into diaries values(1763, "2016-05-11");
+insert into diaries values(1764, "2016-05-12");
+insert into diaries values(1765, "2016-05-13");
+insert into diaries values(1766, "2016-05-14");
+insert into diaries values(1767, "2016-05-15");
+insert into diaries values(1768, "2016-05-16");
+insert into diaries values(1769, "2016-05-17");
+insert into diaries values(1770, "2016-05-18");
+insert into diaries values(1771, "2016-05-19");
+insert into diaries values(1772, "2016-05-20");
+insert into diaries values(1773, "2016-05-21");
+insert into diaries values(1774, "2016-05-22");
+insert into diaries values(1775, "2016-05-23");
+insert into diaries values(1776, "2016-05-24");
+insert into diaries values(1777, "2016-05-25");
+insert into diaries values(1778, "2016-05-26");
+insert into diaries values(1779, "2016-05-27");
+insert into diaries values(1780, "2016-05-28");
+insert into diaries values(1781, "2016-05-29");
+insert into diaries values(1782, "2016-05-30");
+insert into diaries values(1783, "2016-05-31");
+insert into diaries values(1784, "2016-06-01");
+insert into diaries values(1785, "2016-06-02");
+insert into diaries values(1786, "2016-06-03");
+insert into diaries values(1787, "2016-06-04");
+insert into diaries values(1788, "2016-06-05");
+insert into diaries values(1789, "2016-06-06");
+insert into diaries values(1790, "2016-06-07");
+insert into diaries values(1791, "2016-06-08");
+insert into diaries values(1792, "2016-06-09");
+insert into diaries values(1793, "2016-06-10");
+insert into diaries values(1794, "2016-06-11");
+insert into diaries values(1795, "2016-06-12");
+insert into diaries values(1796, "2016-06-13");
+insert into diaries values(1797, "2016-06-14");
+insert into diaries values(1798, "2016-06-15");
+insert into diaries values(1799, "2016-06-16");
+insert into diaries values(1800, "2016-06-17");
+insert into diaries values(1801, "2016-06-18");
+insert into diaries values(1802, "2016-06-19");
+insert into diaries values(1803, "2016-06-20");
+insert into diaries values(1804, "2016-06-21");
+insert into diaries values(1805, "2016-06-22");
+insert into diaries values(1806, "2016-06-23");
+insert into diaries values(1807, "2016-06-24");
+insert into diaries values(1808, "2016-06-25");
+insert into diaries values(1809, "2016-06-26");
+insert into diaries values(1810, "2016-06-27");
+insert into diaries values(1811, "2016-06-28");
+insert into diaries values(1812, "2016-06-29");
+insert into diaries values(1813, "2016-06-30");
+insert into diaries values(1814, "2016-07-01");
+insert into diaries values(1815, "2016-07-02");
+insert into diaries values(1816, "2016-07-03");
+insert into diaries values(1817, "2016-07-04");
+insert into diaries values(1818, "2016-07-05");
+insert into diaries values(1819, "2016-07-06");
+insert into diaries values(1820, "2016-07-07");
+insert into diaries values(1821, "2016-07-08");
+insert into diaries values(1822, "2016-07-09");
+insert into diaries values(1823, "2016-07-10");
+insert into diaries values(1824, "2016-07-11");
+insert into diaries values(1825, "2016-07-12");
+insert into diaries values(1826, "2016-07-13");
+insert into diaries values(1827, "2016-07-14");
+insert into diaries values(1828, "2016-07-15");
+insert into diaries values(1829, "2016-07-16");
+insert into diaries values(1830, "2016-07-17");
+insert into diaries values(1831, "2016-07-18");
+insert into diaries values(1832, "2016-07-19");
+insert into diaries values(1833, "2016-07-20");
+insert into diaries values(1834, "2016-07-21");
+insert into diaries values(1835, "2016-07-22");
+insert into diaries values(1836, "2016-07-23");
+insert into diaries values(1837, "2016-07-24");
+insert into diaries values(1838, "2016-07-25");
+insert into diaries values(1839, "2016-07-26");
+insert into diaries values(1840, "2016-07-27");
+insert into diaries values(1841, "2016-07-28");
+insert into diaries values(1842, "2016-07-29");
+insert into diaries values(1843, "2016-07-30");
+insert into diaries values(1844, "2016-07-31");
+insert into diaries values(1845, "2016-08-01");
+insert into diaries values(1846, "2016-08-02");
+insert into diaries values(1847, "2016-08-03");
+insert into diaries values(1848, "2016-08-04");
+insert into diaries values(1849, "2016-08-05");
+insert into diaries values(1850, "2016-08-06");
+insert into diaries values(1851, "2016-08-07");
+insert into diaries values(1852, "2016-08-08");
+insert into diaries values(1853, "2016-08-09");
+insert into diaries values(1854, "2016-08-10");
+insert into diaries values(1855, "2016-08-11");
+insert into diaries values(1856, "2016-08-12");
+insert into diaries values(1857, "2016-08-13");
+insert into diaries values(1858, "2016-08-14");
+insert into diaries values(1859, "2016-08-15");
+insert into diaries values(1860, "2016-08-16");
+insert into diaries values(1861, "2016-08-17");
+insert into diaries values(1862, "2016-08-18");
+insert into diaries values(1863, "2016-08-19");
+insert into diaries values(1864, "2016-08-20");
+insert into diaries values(1865, "2016-08-21");
+insert into diaries values(1866, "2016-08-22");
+insert into diaries values(1867, "2016-08-23");
+insert into diaries values(1868, "2016-08-24");
+insert into diaries values(1869, "2016-08-25");
+insert into diaries values(1870, "2016-08-26");
+insert into diaries values(1871, "2016-08-27");
+insert into diaries values(1872, "2016-08-28");
+insert into diaries values(1873, "2016-08-29");
+insert into diaries values(1874, "2016-08-30");
+insert into diaries values(1875, "2016-08-31");
+insert into diaries values(1876, "2016-09-01");
+insert into diaries values(1877, "2016-09-02");
+insert into diaries values(1878, "2016-09-03");
+insert into diaries values(1879, "2016-09-04");
+insert into diaries values(1880, "2016-09-05");
+insert into diaries values(1881, "2016-09-06");
+insert into diaries values(1882, "2016-09-07");
+insert into diaries values(1883, "2016-09-08");
+insert into diaries values(1884, "2016-09-09");
+insert into diaries values(1885, "2016-09-10");
+insert into diaries values(1886, "2016-09-11");
+insert into diaries values(1887, "2016-09-12");
+insert into diaries values(1888, "2016-09-13");
+insert into diaries values(1889, "2016-09-14");
+insert into diaries values(1890, "2016-09-15");
+insert into diaries values(1891, "2016-09-16");
+insert into diaries values(1892, "2016-09-17");
+insert into diaries values(1893, "2016-09-18");
+insert into diaries values(1894, "2016-09-19");
+insert into diaries values(1895, "2016-09-20");
+insert into diaries values(1896, "2016-09-21");
+insert into diaries values(1897, "2016-09-22");
+insert into diaries values(1898, "2016-09-23");
+insert into diaries values(1899, "2016-09-24");
+insert into diaries values(1900, "2016-09-25");
+insert into diaries values(1901, "2016-09-26");
+insert into diaries values(1902, "2016-09-27");
+insert into diaries values(1903, "2016-09-28");
+insert into diaries values(1904, "2016-09-29");
+insert into diaries values(1905, "2016-09-30");
+insert into diaries values(1906, "2016-10-01");
+insert into diaries values(1907, "2016-10-02");
+insert into diaries values(1908, "2016-10-03");
+insert into diaries values(1909, "2016-10-04");
+insert into diaries values(1910, "2016-10-05");
+insert into diaries values(1911, "2016-10-06");
+insert into diaries values(1912, "2016-10-07");
+insert into diaries values(1913, "2016-10-08");
+insert into diaries values(1914, "2016-10-09");
+insert into diaries values(1915, "2016-10-10");
+insert into diaries values(1916, "2016-10-11");
+insert into diaries values(1917, "2016-10-12");
+insert into diaries values(1918, "2016-10-13");
+insert into diaries values(1919, "2016-10-14");
+insert into diaries values(1920, "2016-10-15");
+insert into diaries values(1921, "2016-10-16");
+insert into diaries values(1922, "2016-10-17");
+insert into diaries values(1923, "2016-10-18");
+insert into diaries values(1924, "2016-10-19");
+insert into diaries values(1925, "2016-10-20");
+insert into diaries values(1926, "2016-10-21");
+insert into diaries values(1927, "2016-10-22");
+insert into diaries values(1928, "2016-10-23");
+insert into diaries values(1929, "2016-10-24");
+insert into diaries values(1930, "2016-10-25");
+insert into diaries values(1931, "2016-10-26");
+insert into diaries values(1932, "2016-10-27");
+insert into diaries values(1933, "2016-10-28");
+insert into diaries values(1934, "2016-10-29");
+insert into diaries values(1935, "2016-10-30");
+insert into diaries values(1936, "2016-10-31");
+insert into diaries values(1937, "2016-11-01");
+insert into diaries values(1938, "2016-11-02");
+insert into diaries values(1939, "2016-11-03");
+insert into diaries values(1940, "2016-11-04");
+insert into diaries values(1941, "2016-11-05");
+insert into diaries values(1942, "2016-11-06");
+insert into diaries values(1943, "2016-11-07");
+insert into diaries values(1944, "2016-11-08");
+insert into diaries values(1945, "2016-11-09");
+insert into diaries values(1946, "2016-11-10");
+insert into diaries values(1947, "2016-11-11");
+insert into diaries values(1948, "2016-11-12");
+insert into diaries values(1949, "2016-11-13");
+insert into diaries values(1950, "2016-11-14");
+insert into diaries values(1951, "2016-11-15");
+insert into diaries values(1952, "2016-11-16");
+insert into diaries values(1953, "2016-11-17");
+insert into diaries values(1954, "2016-11-18");
+insert into diaries values(1955, "2016-11-19");
+insert into diaries values(1956, "2016-11-20");
+insert into diaries values(1957, "2016-11-21");
+insert into diaries values(1958, "2016-11-22");
+insert into diaries values(1959, "2016-11-23");
+insert into diaries values(1960, "2016-11-24");
+insert into diaries values(1961, "2016-11-25");
+insert into diaries values(1962, "2016-11-26");
+insert into diaries values(1963, "2016-11-27");
+insert into diaries values(1964, "2016-11-28");
+insert into diaries values(1965, "2016-11-29");
+insert into diaries values(1966, "2016-11-30");
+insert into diaries values(1967, "2016-12-01");
+insert into diaries values(1968, "2016-12-02");
+insert into diaries values(1969, "2016-12-03");
+insert into diaries values(1970, "2016-12-04");
+insert into diaries values(1971, "2016-12-05");
+insert into diaries values(1972, "2016-12-06");
+insert into diaries values(1973, "2016-12-07");
+insert into diaries values(1974, "2016-12-08");
+insert into diaries values(1975, "2016-12-09");
+insert into diaries values(1976, "2016-12-10");
+insert into diaries values(1977, "2016-12-11");
+insert into diaries values(1978, "2016-12-12");
+insert into diaries values(1979, "2016-12-13");
+insert into diaries values(1980, "2016-12-14");
+insert into diaries values(1981, "2016-12-15");
+insert into diaries values(1982, "2016-12-16");
+insert into diaries values(1983, "2016-12-17");
+insert into diaries values(1984, "2016-12-18");
+insert into diaries values(1985, "2016-12-19");
+insert into diaries values(1986, "2016-12-20");
+insert into diaries values(1987, "2016-12-21");
+insert into diaries values(1988, "2016-12-22");
+insert into diaries values(1989, "2016-12-23");
+insert into diaries values(1990, "2016-12-24");
+insert into diaries values(1991, "2016-12-25");
+insert into diaries values(1992, "2016-12-26");
+insert into diaries values(1993, "2016-12-27");
+insert into diaries values(1994, "2016-12-28");
+insert into diaries values(1995, "2016-12-29");
+insert into diaries values(1996, "2016-12-30");
+insert into diaries values(1997, "2016-12-31");
+insert into diaries values(1998, "2017-01-01");
+insert into diaries values(1999, "2017-01-02");
+insert into diaries values(2000, "2017-01-03");
+insert into diaries values(2001, "2017-01-04");
+insert into diaries values(2002, "2017-01-05");
+insert into diaries values(2003, "2017-01-06");
+insert into diaries values(2004, "2017-01-07");
+insert into diaries values(2005, "2017-01-08");
+insert into diaries values(2006, "2017-01-09");
+insert into diaries values(2007, "2017-01-10");
+insert into diaries values(2008, "2017-01-11");
+insert into diaries values(2009, "2017-01-12");
+insert into diaries values(2010, "2017-01-13");
+insert into diaries values(2011, "2017-01-14");
+insert into diaries values(2012, "2017-01-15");
+insert into diaries values(2013, "2017-01-16");
+insert into diaries values(2014, "2017-01-17");
+insert into diaries values(2015, "2017-01-18");
+insert into diaries values(2016, "2017-01-19");
+insert into diaries values(2017, "2017-01-20");
+insert into diaries values(2018, "2017-01-21");
+insert into diaries values(2019, "2017-01-22");
+insert into diaries values(2020, "2017-01-23");
+insert into diaries values(2021, "2017-01-24");
+insert into diaries values(2022, "2017-01-25");
+insert into diaries values(2023, "2017-01-26");
+insert into diaries values(2024, "2017-01-27");
+insert into diaries values(2025, "2017-01-28");
+insert into diaries values(2026, "2017-01-29");
+insert into diaries values(2027, "2017-01-30");
+insert into diaries values(2028, "2017-01-31");
+insert into diaries values(2029, "2017-02-01");
+insert into diaries values(2030, "2017-02-02");
+insert into diaries values(2031, "2017-02-03");
+insert into diaries values(2032, "2017-02-04");
+insert into diaries values(2033, "2017-02-05");
+insert into diaries values(2034, "2017-02-06");
+insert into diaries values(2035, "2017-02-07");
+insert into diaries values(2036, "2017-02-08");
+insert into diaries values(2037, "2017-02-09");
+insert into diaries values(2038, "2017-02-10");
+insert into diaries values(2039, "2017-02-11");
+insert into diaries values(2040, "2017-02-12");
+insert into diaries values(2041, "2017-02-13");
+insert into diaries values(2042, "2017-02-14");
+insert into diaries values(2043, "2017-02-15");
+insert into diaries values(2044, "2017-02-16");
+insert into diaries values(2045, "2017-02-17");
+insert into diaries values(2046, "2017-02-18");
+insert into diaries values(2047, "2017-02-19");
+insert into diaries values(2048, "2017-02-20");
+insert into diaries values(2049, "2017-02-21");
+insert into diaries values(2050, "2017-02-22");
+insert into diaries values(2051, "2017-02-23");
+insert into diaries values(2052, "2017-02-24");
+insert into diaries values(2053, "2017-02-25");
+insert into diaries values(2054, "2017-02-26");
+insert into diaries values(2055, "2017-02-27");
+insert into diaries values(2056, "2017-02-28");
+insert into diaries values(2057, "2017-03-01");
+insert into diaries values(2058, "2017-03-02");
+insert into diaries values(2059, "2017-03-03");
+insert into diaries values(2060, "2017-03-04");
+insert into diaries values(2061, "2017-03-05");
+insert into diaries values(2062, "2017-03-06");
+insert into diaries values(2063, "2017-03-07");
+insert into diaries values(2064, "2017-03-08");
+insert into diaries values(2065, "2017-03-09");
+insert into diaries values(2066, "2017-03-10");
+insert into diaries values(2067, "2017-03-11");
+insert into diaries values(2068, "2017-03-12");
+insert into diaries values(2069, "2017-03-13");
+insert into diaries values(2070, "2017-03-14");
+insert into diaries values(2071, "2017-03-15");
+insert into diaries values(2072, "2017-03-16");
+insert into diaries values(2073, "2017-03-17");
+insert into diaries values(2074, "2017-03-18");
+insert into diaries values(2075, "2017-03-19");
+insert into diaries values(2076, "2017-03-20");
+insert into diaries values(2077, "2017-03-21");
+insert into diaries values(2078, "2017-03-22");
+insert into diaries values(2079, "2017-03-23");
+insert into diaries values(2080, "2017-03-24");
+insert into diaries values(2081, "2017-03-25");
+insert into diaries values(2082, "2017-03-26");
+insert into diaries values(2083, "2017-03-27");
+insert into diaries values(2084, "2017-03-28");
+insert into diaries values(2085, "2017-03-29");
+insert into diaries values(2086, "2017-03-30");
+insert into diaries values(2087, "2017-03-31");
+insert into diaries values(2088, "2017-04-01");
+insert into diaries values(2089, "2017-04-02");
+insert into diaries values(2090, "2017-04-03");
+insert into diaries values(2091, "2017-04-04");
+insert into diaries values(2092, "2017-04-05");
+insert into diaries values(2093, "2017-04-06");
+insert into diaries values(2094, "2017-04-07");
+insert into diaries values(2095, "2017-04-08");
+insert into diaries values(2096, "2017-04-09");
+insert into diaries values(2097, "2017-04-10");
+insert into diaries values(2098, "2017-04-11");
+insert into diaries values(2099, "2017-04-12");
+insert into diaries values(2100, "2017-04-13");
+insert into diaries values(2101, "2017-04-14");
+insert into diaries values(2102, "2017-04-15");
+insert into diaries values(2103, "2017-04-16");
+insert into diaries values(2104, "2017-04-17");
+insert into diaries values(2105, "2017-04-18");
+insert into diaries values(2106, "2017-04-19");
+insert into diaries values(2107, "2017-04-20");
+insert into diaries values(2108, "2017-04-21");
+insert into diaries values(2109, "2017-04-22");
+insert into diaries values(2110, "2017-04-23");
+insert into diaries values(2111, "2017-04-24");
+insert into diaries values(2112, "2017-04-25");
+insert into diaries values(2113, "2017-04-26");
+insert into diaries values(2114, "2017-04-27");
+insert into diaries values(2115, "2017-04-28");
+insert into diaries values(2116, "2017-04-29");
+insert into diaries values(2117, "2017-04-30");
+insert into diaries values(2118, "2017-05-01");
+insert into diaries values(2119, "2017-05-02");
+insert into diaries values(2120, "2017-05-03");
+insert into diaries values(2121, "2017-05-04");
+insert into diaries values(2122, "2017-05-05");
+insert into diaries values(2123, "2017-05-06");
+insert into diaries values(2124, "2017-05-07");
+insert into diaries values(2125, "2017-05-08");
+insert into diaries values(2126, "2017-05-09");
+insert into diaries values(2127, "2017-05-10");
+insert into diaries values(2128, "2017-05-11");
+insert into diaries values(2129, "2017-05-12");
+insert into diaries values(2130, "2017-05-13");
+insert into diaries values(2131, "2017-05-14");
+insert into diaries values(2132, "2017-05-15");
+insert into diaries values(2133, "2017-05-16");
+insert into diaries values(2134, "2017-05-17");
+insert into diaries values(2135, "2017-05-18");
+insert into diaries values(2136, "2017-05-19");
+insert into diaries values(2137, "2017-05-20");
+insert into diaries values(2138, "2017-05-21");
+insert into diaries values(2139, "2017-05-22");
+insert into diaries values(2140, "2017-05-23");
+insert into diaries values(2141, "2017-05-24");
+insert into diaries values(2142, "2017-05-25");
+insert into diaries values(2143, "2017-05-26");
+insert into diaries values(2144, "2017-05-27");
+insert into diaries values(2145, "2017-05-28");
+insert into diaries values(2146, "2017-05-29");
+insert into diaries values(2147, "2017-05-30");
+insert into diaries values(2148, "2017-05-31");
+insert into diaries values(2149, "2017-06-01");
+insert into diaries values(2150, "2017-06-02");
+insert into diaries values(2151, "2017-06-03");
+insert into diaries values(2152, "2017-06-04");
+insert into diaries values(2153, "2017-06-05");
+insert into diaries values(2154, "2017-06-06");
+insert into diaries values(2155, "2017-06-07");
+insert into diaries values(2156, "2017-06-08");
+insert into diaries values(2157, "2017-06-09");
+insert into diaries values(2158, "2017-06-10");
+insert into diaries values(2159, "2017-06-11");
+insert into diaries values(2160, "2017-06-12");
+insert into diaries values(2161, "2017-06-13");
+insert into diaries values(2162, "2017-06-14");
+insert into diaries values(2163, "2017-06-15");
+insert into diaries values(2164, "2017-06-16");
+insert into diaries values(2165, "2017-06-17");
+insert into diaries values(2166, "2017-06-18");
+insert into diaries values(2167, "2017-06-19");
+insert into diaries values(2168, "2017-06-20");
+insert into diaries values(2169, "2017-06-21");
+insert into diaries values(2170, "2017-06-22");
+insert into diaries values(2171, "2017-06-23");
+insert into diaries values(2172, "2017-06-24");
+insert into diaries values(2173, "2017-06-25");
+insert into diaries values(2174, "2017-06-26");
+insert into diaries values(2175, "2017-06-27");
+insert into diaries values(2176, "2017-06-28");
+insert into diaries values(2177, "2017-06-29");
+insert into diaries values(2178, "2017-06-30");
+insert into diaries values(2179, "2017-07-01");
+insert into diaries values(2180, "2017-07-02");
+insert into diaries values(2181, "2017-07-03");
+insert into diaries values(2182, "2017-07-04");
+insert into diaries values(2183, "2017-07-05");
+insert into diaries values(2184, "2017-07-06");
+insert into diaries values(2185, "2017-07-07");
+insert into diaries values(2186, "2017-07-08");
+insert into diaries values(2187, "2017-07-09");
+insert into diaries values(2188, "2017-07-10");
+insert into diaries values(2189, "2017-07-11");
+insert into diaries values(2190, "2017-07-12");
+insert into diaries values(2191, "2017-07-13");
+insert into diaries values(2192, "2017-07-14");
+insert into diaries values(2193, "2017-07-15");
+insert into diaries values(2194, "2017-07-16");
+insert into diaries values(2195, "2017-07-17");
+insert into diaries values(2196, "2017-07-18");
+insert into diaries values(2197, "2017-07-19");
+insert into diaries values(2198, "2017-07-20");
+insert into diaries values(2199, "2017-07-21");
+insert into diaries values(2200, "2017-07-22");
+insert into diaries values(2201, "2017-07-23");
+insert into diaries values(2202, "2017-07-24");
+insert into diaries values(2203, "2017-07-25");
+insert into diaries values(2204, "2017-07-26");
+insert into diaries values(2205, "2017-07-27");
+insert into diaries values(2206, "2017-07-28");
+insert into diaries values(2207, "2017-07-29");
+insert into diaries values(2208, "2017-07-30");
+insert into diaries values(2209, "2017-07-31");
+insert into diaries values(2210, "2017-08-01");
+insert into diaries values(2211, "2017-08-02");
+insert into diaries values(2212, "2017-08-03");
+insert into diaries values(2213, "2017-08-04");
+insert into diaries values(2214, "2017-08-05");
+insert into diaries values(2215, "2017-08-06");
+insert into diaries values(2216, "2017-08-07");
+insert into diaries values(2217, "2017-08-08");
+insert into diaries values(2218, "2017-08-09");
+insert into diaries values(2219, "2017-08-10");
+insert into diaries values(2220, "2017-08-11");
+insert into diaries values(2221, "2017-08-12");
+insert into diaries values(2222, "2017-08-13");
+insert into diaries values(2223, "2017-08-14");
+insert into diaries values(2224, "2017-08-15");
+insert into diaries values(2225, "2017-08-16");
+insert into diaries values(2226, "2017-08-17");
+insert into diaries values(2227, "2017-08-18");
+insert into diaries values(2228, "2017-08-19");
+insert into diaries values(2229, "2017-08-20");
+insert into diaries values(2230, "2017-08-21");
+insert into diaries values(2231, "2017-08-22");
+insert into diaries values(2232, "2017-08-23");
+insert into diaries values(2233, "2017-08-24");
+insert into diaries values(2234, "2017-08-25");
+insert into diaries values(2235, "2017-08-26");
+insert into diaries values(2236, "2017-08-27");
+insert into diaries values(2237, "2017-08-28");
+insert into diaries values(2238, "2017-08-29");
+insert into diaries values(2239, "2017-08-30");
+insert into diaries values(2240, "2017-08-31");
+insert into diaries values(2241, "2017-09-01");
+insert into diaries values(2242, "2017-09-02");
+insert into diaries values(2243, "2017-09-03");
+insert into diaries values(2244, "2017-09-04");
+insert into diaries values(2245, "2017-09-05");
+insert into diaries values(2246, "2017-09-06");
+insert into diaries values(2247, "2017-09-07");
+insert into diaries values(2248, "2017-09-08");
+insert into diaries values(2249, "2017-09-09");
+insert into diaries values(2250, "2017-09-10");
+insert into diaries values(2251, "2017-09-11");
+insert into diaries values(2252, "2017-09-12");
+insert into diaries values(2253, "2017-09-13");
+insert into diaries values(2254, "2017-09-14");
+insert into diaries values(2255, "2017-09-15");
+insert into diaries values(2256, "2017-09-16");
+insert into diaries values(2257, "2017-09-17");
+insert into diaries values(2258, "2017-09-18");
+insert into diaries values(2259, "2017-09-19");
+insert into diaries values(2260, "2017-09-20");
+insert into diaries values(2261, "2017-09-21");
+insert into diaries values(2262, "2017-09-22");
+insert into diaries values(2263, "2017-09-23");
+insert into diaries values(2264, "2017-09-24");
+insert into diaries values(2265, "2017-09-25");
+insert into diaries values(2266, "2017-09-26");
+insert into diaries values(2267, "2017-09-27");
+insert into diaries values(2268, "2017-09-28");
+insert into diaries values(2269, "2017-09-29");
+insert into diaries values(2270, "2017-09-30");
+insert into diaries values(2271, "2017-10-01");
+insert into diaries values(2272, "2017-10-02");
+insert into diaries values(2273, "2017-10-03");
+insert into diaries values(2274, "2017-10-04");
+insert into diaries values(2275, "2017-10-05");
+insert into diaries values(2276, "2017-10-06");
+insert into diaries values(2277, "2017-10-07");
+insert into diaries values(2278, "2017-10-08");
+insert into diaries values(2279, "2017-10-09");
+insert into diaries values(2280, "2017-10-10");
+insert into diaries values(2281, "2017-10-11");
+insert into diaries values(2282, "2017-10-12");
+insert into diaries values(2283, "2017-10-13");
+insert into diaries values(2284, "2017-10-14");
+insert into diaries values(2285, "2017-10-15");
+insert into diaries values(2286, "2017-10-16");
+insert into diaries values(2287, "2017-10-17");
+insert into diaries values(2288, "2017-10-18");
+insert into diaries values(2289, "2017-10-19");
+insert into diaries values(2290, "2017-10-20");
+insert into diaries values(2291, "2017-10-21");
+insert into diaries values(2292, "2017-10-22");
+insert into diaries values(2293, "2017-10-23");
+insert into diaries values(2294, "2017-10-24");
+insert into diaries values(2295, "2017-10-25");
+insert into diaries values(2296, "2017-10-26");
+insert into diaries values(2297, "2017-10-27");
+insert into diaries values(2298, "2017-10-28");
+insert into diaries values(2299, "2017-10-29");
+insert into diaries values(2300, "2017-10-30");
+insert into diaries values(2301, "2017-10-31");
+insert into diaries values(2302, "2017-11-01");
+insert into diaries values(2303, "2017-11-02");
+insert into diaries values(2304, "2017-11-03");
+insert into diaries values(2305, "2017-11-04");
+insert into diaries values(2306, "2017-11-05");
+insert into diaries values(2307, "2017-11-06");
+insert into diaries values(2308, "2017-11-07");
+insert into diaries values(2309, "2017-11-08");
+insert into diaries values(2310, "2017-11-09");
+insert into diaries values(2311, "2017-11-10");
+insert into diaries values(2312, "2017-11-11");
+insert into diaries values(2313, "2017-11-12");
+insert into diaries values(2314, "2017-11-13");
+insert into diaries values(2315, "2017-11-14");
+insert into diaries values(2316, "2017-11-15");
+insert into diaries values(2317, "2017-11-16");
+insert into diaries values(2318, "2017-11-17");
+insert into diaries values(2319, "2017-11-18");
+insert into diaries values(2320, "2017-11-19");
+insert into diaries values(2321, "2017-11-20");
+insert into diaries values(2322, "2017-11-21");
+insert into diaries values(2323, "2017-11-22");
+insert into diaries values(2324, "2017-11-23");
+insert into diaries values(2325, "2017-11-24");
+insert into diaries values(2326, "2017-11-25");
+insert into diaries values(2327, "2017-11-26");
+insert into diaries values(2328, "2017-11-27");
+insert into diaries values(2329, "2017-11-28");
+insert into diaries values(2330, "2017-11-29");
+insert into diaries values(2331, "2017-11-30");
+insert into diaries values(2332, "2017-12-01");
+insert into diaries values(2333, "2017-12-02");
+insert into diaries values(2334, "2017-12-03");
+insert into diaries values(2335, "2017-12-04");
+insert into diaries values(2336, "2017-12-05");
+insert into diaries values(2337, "2017-12-06");
+insert into diaries values(2338, "2017-12-07");
+insert into diaries values(2339, "2017-12-08");
+insert into diaries values(2340, "2017-12-09");
+insert into diaries values(2341, "2017-12-10");
+insert into diaries values(2342, "2017-12-11");
+insert into diaries values(2343, "2017-12-12");
+insert into diaries values(2344, "2017-12-13");
+insert into diaries values(2345, "2017-12-14");
+insert into diaries values(2346, "2017-12-15");
+insert into diaries values(2347, "2017-12-16");
+insert into diaries values(2348, "2017-12-17");
+insert into diaries values(2349, "2017-12-18");
+insert into diaries values(2350, "2017-12-19");
+insert into diaries values(2351, "2017-12-20");
+insert into diaries values(2352, "2017-12-21");
+insert into diaries values(2353, "2017-12-22");
+insert into diaries values(2354, "2017-12-23");
+insert into diaries values(2355, "2017-12-24");
+insert into diaries values(2356, "2017-12-25");
+insert into diaries values(2357, "2017-12-26");
+insert into diaries values(2358, "2017-12-27");
+insert into diaries values(2359, "2017-12-28");
+insert into diaries values(2360, "2017-12-29");
+insert into diaries values(2361, "2017-12-30");
+insert into diaries values(2362, "2017-12-31");
+insert into diaries values(2363, "2018-01-01");
+insert into diaries values(2364, "2018-01-02");
+insert into diaries values(2365, "2018-01-03");
+insert into diaries values(2366, "2018-01-04");
+insert into diaries values(2367, "2018-01-05");
+insert into diaries values(2368, "2018-01-06");
+insert into diaries values(2369, "2018-01-07");
+insert into diaries values(2370, "2018-01-08");
+insert into diaries values(2371, "2018-01-09");
+insert into diaries values(2372, "2018-01-10");
+insert into diaries values(2373, "2018-01-11");
+insert into diaries values(2374, "2018-01-12");
+insert into diaries values(2375, "2018-01-13");
+insert into diaries values(2376, "2018-01-14");
+insert into diaries values(2377, "2018-01-15");
+insert into diaries values(2378, "2018-01-16");
+insert into diaries values(2379, "2018-01-17");
+insert into diaries values(2380, "2018-01-18");
+insert into diaries values(2381, "2018-01-19");
+insert into diaries values(2382, "2018-01-20");
+insert into diaries values(2383, "2018-01-21");
+insert into diaries values(2384, "2018-01-22");
+insert into diaries values(2385, "2018-01-23");
+insert into diaries values(2386, "2018-01-24");
+insert into diaries values(2387, "2018-01-25");
+insert into diaries values(2388, "2018-01-26");
+insert into diaries values(2389, "2018-01-27");
+insert into diaries values(2390, "2018-01-28");
+insert into diaries values(2391, "2018-01-29");
+insert into diaries values(2392, "2018-01-30");
+insert into diaries values(2393, "2018-01-31");
+insert into diaries values(2394, "2018-02-01");
+insert into diaries values(2395, "2018-02-02");
+insert into diaries values(2396, "2018-02-03");
+insert into diaries values(2397, "2018-02-04");
+insert into diaries values(2398, "2018-02-05");
+insert into diaries values(2399, "2018-02-06");
+insert into diaries values(2400, "2018-02-07");
+insert into diaries values(2401, "2018-02-08");
+insert into diaries values(2402, "2018-02-09");
+insert into diaries values(2403, "2018-02-10");
+insert into diaries values(2404, "2018-02-11");
+insert into diaries values(2405, "2018-02-12");
+insert into diaries values(2406, "2018-02-13");
+insert into diaries values(2407, "2018-02-14");
+insert into diaries values(2408, "2018-02-15");
+insert into diaries values(2409, "2018-02-16");
+insert into diaries values(2410, "2018-02-17");
+insert into diaries values(2411, "2018-02-18");
+insert into diaries values(2412, "2018-02-19");
+insert into diaries values(2413, "2018-02-20");
+insert into diaries values(2414, "2018-02-21");
+insert into diaries values(2415, "2018-02-22");
+insert into diaries values(2416, "2018-02-23");
+insert into diaries values(2417, "2018-02-24");
+insert into diaries values(2418, "2018-02-25");
+insert into diaries values(2419, "2018-02-26");
+insert into diaries values(2420, "2018-02-27");
+insert into diaries values(2421, "2018-02-28");
+insert into diaries values(2422, "2018-03-01");
+insert into diaries values(2423, "2018-03-02");
+insert into diaries values(2424, "2018-03-03");
+insert into diaries values(2425, "2018-03-04");
+insert into diaries values(2426, "2018-03-05");
+insert into diaries values(2427, "2018-03-06");
+insert into diaries values(2428, "2018-03-07");
+insert into diaries values(2429, "2018-03-08");
+insert into diaries values(2430, "2018-03-09");
+insert into diaries values(2431, "2018-03-10");
+insert into diaries values(2432, "2018-03-11");
+insert into diaries values(2433, "2018-03-12");
+insert into diaries values(2434, "2018-03-13");
+insert into diaries values(2435, "2018-03-14");
+insert into diaries values(2436, "2018-03-15");
+insert into diaries values(2437, "2018-03-16");
+insert into diaries values(2438, "2018-03-17");
+insert into diaries values(2439, "2018-03-18");
+insert into diaries values(2440, "2018-03-19");
+insert into diaries values(2441, "2018-03-20");
+insert into diaries values(2442, "2018-03-21");
+insert into diaries values(2443, "2018-03-22");
+insert into diaries values(2444, "2018-03-23");
+insert into diaries values(2445, "2018-03-24");
+insert into diaries values(2446, "2018-03-25");
+insert into diaries values(2447, "2018-03-26");
+insert into diaries values(2448, "2018-03-27");
+insert into diaries values(2449, "2018-03-28");
+insert into diaries values(2450, "2018-03-29");
+insert into diaries values(2451, "2018-03-30");
+insert into diaries values(2452, "2018-03-31");
+insert into diaries values(2453, "2018-04-01");
+insert into diaries values(2454, "2018-04-02");
+insert into diaries values(2455, "2018-04-03");
+insert into diaries values(2456, "2018-04-04");
+insert into diaries values(2457, "2018-04-05");
+insert into diaries values(2458, "2018-04-06");
+insert into diaries values(2459, "2018-04-07");
+insert into diaries values(2460, "2018-04-08");
+insert into diaries values(2461, "2018-04-09");
+insert into diaries values(2462, "2018-04-10");
+insert into diaries values(2463, "2018-04-11");
+insert into diaries values(2464, "2018-04-12");
+insert into diaries values(2465, "2018-04-13");
+insert into diaries values(2466, "2018-04-14");
+insert into diaries values(2467, "2018-04-15");
+insert into diaries values(2468, "2018-04-16");
+insert into diaries values(2469, "2018-04-17");
+insert into diaries values(2470, "2018-04-18");
+insert into diaries values(2471, "2018-04-19");
+insert into diaries values(2472, "2018-04-20");
+insert into diaries values(2473, "2018-04-21");
+insert into diaries values(2474, "2018-04-22");
+insert into diaries values(2475, "2018-04-23");
+insert into diaries values(2476, "2018-04-24");
+insert into diaries values(2477, "2018-04-25");
+insert into diaries values(2478, "2018-04-26");
+insert into diaries values(2479, "2018-04-27");
+insert into diaries values(2480, "2018-04-28");
+insert into diaries values(2481, "2018-04-29");
+insert into diaries values(2482, "2018-04-30");
+insert into diaries values(2483, "2018-05-01");
+insert into diaries values(2484, "2018-05-02");
+insert into diaries values(2485, "2018-05-03");
+insert into diaries values(2486, "2018-05-04");
+insert into diaries values(2487, "2018-05-05");
+insert into diaries values(2488, "2018-05-06");
+insert into diaries values(2489, "2018-05-07");
+insert into diaries values(2490, "2018-05-08");
+insert into diaries values(2491, "2018-05-09");
+insert into diaries values(2492, "2018-05-10");
+insert into diaries values(2493, "2018-05-11");
+insert into diaries values(2494, "2018-05-12");
+insert into diaries values(2495, "2018-05-13");
+insert into diaries values(2496, "2018-05-14");
+insert into diaries values(2497, "2018-05-15");
+insert into diaries values(2498, "2018-05-16");
+insert into diaries values(2499, "2018-05-17");
+insert into diaries values(2500, "2018-05-18");
+insert into diaries values(2501, "2018-05-19");
+insert into diaries values(2502, "2018-05-20");
+insert into diaries values(2503, "2018-05-21");
+insert into diaries values(2504, "2018-05-22");
+insert into diaries values(2505, "2018-05-23");
+insert into diaries values(2506, "2018-05-24");
+insert into diaries values(2507, "2018-05-25");
+insert into diaries values(2508, "2018-05-26");
+insert into diaries values(2509, "2018-05-27");
+insert into diaries values(2510, "2018-05-28");
+insert into diaries values(2511, "2018-05-29");
+insert into diaries values(2512, "2018-05-30");
+insert into diaries values(2513, "2018-05-31");
+insert into diaries values(2514, "2018-06-01");
+insert into diaries values(2515, "2018-06-02");
+insert into diaries values(2516, "2018-06-03");
+insert into diaries values(2517, "2018-06-04");
+insert into diaries values(2518, "2018-06-05");
+insert into diaries values(2519, "2018-06-06");
+insert into diaries values(2520, "2018-06-07");
+insert into diaries values(2521, "2018-06-08");
+insert into diaries values(2522, "2018-06-09");
+insert into diaries values(2523, "2018-06-10");
+insert into diaries values(2524, "2018-06-11");
+insert into diaries values(2525, "2018-06-12");
+insert into diaries values(2526, "2018-06-13");
+insert into diaries values(2527, "2018-06-14");
+insert into diaries values(2528, "2018-06-15");
+insert into diaries values(2529, "2018-06-16");
+insert into diaries values(2530, "2018-06-17");
+insert into diaries values(2531, "2018-06-18");
+insert into diaries values(2532, "2018-06-19");
+insert into diaries values(2533, "2018-06-20");
+insert into diaries values(2534, "2018-06-21");
+insert into diaries values(2535, "2018-06-22");
+insert into diaries values(2536, "2018-06-23");
+insert into diaries values(2537, "2018-06-24");
+insert into diaries values(2538, "2018-06-25");
+insert into diaries values(2539, "2018-06-26");
+insert into diaries values(2540, "2018-06-27");
+insert into diaries values(2541, "2018-06-28");
+insert into diaries values(2542, "2018-06-29");
+insert into diaries values(2543, "2018-06-30");
+insert into diaries values(2544, "2018-07-01");
+insert into diaries values(2545, "2018-07-02");
+insert into diaries values(2546, "2018-07-03");
+insert into diaries values(2547, "2018-07-04");
+insert into diaries values(2548, "2018-07-05");
+insert into diaries values(2549, "2018-07-06");
+insert into diaries values(2550, "2018-07-07");
+insert into diaries values(2551, "2018-07-08");
+insert into diaries values(2552, "2018-07-09");
+insert into diaries values(2553, "2018-07-10");
+insert into diaries values(2554, "2018-07-11");
+insert into diaries values(2555, "2018-07-12");
+insert into diaries values(2556, "2018-07-13");
+insert into diaries values(2557, "2018-07-14");
+insert into diaries values(2558, "2018-07-15");
+insert into diaries values(2559, "2018-07-16");
+insert into diaries values(2560, "2018-07-17");
+insert into diaries values(2561, "2018-07-18");
+insert into diaries values(2562, "2018-07-19");
+insert into diaries values(2563, "2018-07-20");
+insert into diaries values(2564, "2018-07-21");
+insert into diaries values(2565, "2018-07-22");
+insert into diaries values(2566, "2018-07-23");
+insert into diaries values(2567, "2018-07-24");
+insert into diaries values(2568, "2018-07-25");
+insert into diaries values(2569, "2018-07-26");
+insert into diaries values(2570, "2018-07-27");
+insert into diaries values(2571, "2018-07-28");
+insert into diaries values(2572, "2018-07-29");
+insert into diaries values(2573, "2018-07-30");
+insert into diaries values(2574, "2018-07-31");
+insert into diaries values(2575, "2018-08-01");
+insert into diaries values(2576, "2018-08-02");
+insert into diaries values(2577, "2018-08-03");
+insert into diaries values(2578, "2018-08-04");
+insert into diaries values(2579, "2018-08-05");
+insert into diaries values(2580, "2018-08-06");
+insert into diaries values(2581, "2018-08-07");
+insert into diaries values(2582, "2018-08-08");
+insert into diaries values(2583, "2018-08-09");
+insert into diaries values(2584, "2018-08-10");
+insert into diaries values(2585, "2018-08-11");
+insert into diaries values(2586, "2018-08-12");
+insert into diaries values(2587, "2018-08-13");
+insert into diaries values(2588, "2018-08-14");
+insert into diaries values(2589, "2018-08-15");
+insert into diaries values(2590, "2018-08-16");
+insert into diaries values(2591, "2018-08-17");
+insert into diaries values(2592, "2018-08-18");
+insert into diaries values(2593, "2018-08-19");
+insert into diaries values(2594, "2018-08-20");
+insert into diaries values(2595, "2018-08-21");
+insert into diaries values(2596, "2018-08-22");
+insert into diaries values(2597, "2018-08-23");
+insert into diaries values(2598, "2018-08-24");
+insert into diaries values(2599, "2018-08-25");
+insert into diaries values(2600, "2018-08-26");
+insert into diaries values(2601, "2018-08-27");
+insert into diaries values(2602, "2018-08-28");
+insert into diaries values(2603, "2018-08-29");
+insert into diaries values(2604, "2018-08-30");
+insert into diaries values(2605, "2018-08-31");
+insert into diaries values(2606, "2018-09-01");
+insert into diaries values(2607, "2018-09-02");
+insert into diaries values(2608, "2018-09-03");
+insert into diaries values(2609, "2018-09-04");
+insert into diaries values(2610, "2018-09-05");
+insert into diaries values(2611, "2018-09-06");
+insert into diaries values(2612, "2018-09-07");
+insert into diaries values(2613, "2018-09-08");
+insert into diaries values(2614, "2018-09-09");
+insert into diaries values(2615, "2018-09-10");
+insert into diaries values(2616, "2018-09-11");
+insert into diaries values(2617, "2018-09-12");
+insert into diaries values(2618, "2018-09-13");
+insert into diaries values(2619, "2018-09-14");
+insert into diaries values(2620, "2018-09-15");
+insert into diaries values(2621, "2018-09-16");
+insert into diaries values(2622, "2018-09-17");
+insert into diaries values(2623, "2018-09-18");
+insert into diaries values(2624, "2018-09-19");
+insert into diaries values(2625, "2018-09-20");
+insert into diaries values(2626, "2018-09-21");
+insert into diaries values(2627, "2018-09-22");
+insert into diaries values(2628, "2018-09-23");
+insert into diaries values(2629, "2018-09-24");
+insert into diaries values(2630, "2018-09-25");
+insert into diaries values(2631, "2018-09-26");
+insert into diaries values(2632, "2018-09-27");
+insert into diaries values(2633, "2018-09-28");
+insert into diaries values(2634, "2018-09-29");
+insert into diaries values(2635, "2018-09-30");
+insert into diaries values(2636, "2018-10-01");
+insert into diaries values(2637, "2018-10-02");
+insert into diaries values(2638, "2018-10-03");
+insert into diaries values(2639, "2018-10-04");
+insert into diaries values(2640, "2018-10-05");
+insert into diaries values(2641, "2018-10-06");
+insert into diaries values(2642, "2018-10-07");
+insert into diaries values(2643, "2018-10-08");
+insert into diaries values(2644, "2018-10-09");
+insert into diaries values(2645, "2018-10-10");
+insert into diaries values(2646, "2018-10-11");
+insert into diaries values(2647, "2018-10-12");
+insert into diaries values(2648, "2018-10-13");
+insert into diaries values(2649, "2018-10-14");
+insert into diaries values(2650, "2018-10-15");
+insert into diaries values(2651, "2018-10-16");
+insert into diaries values(2652, "2018-10-17");
+insert into diaries values(2653, "2018-10-18");
+insert into diaries values(2654, "2018-10-19");
+insert into diaries values(2655, "2018-10-20");
+insert into diaries values(2656, "2018-10-21");
+insert into diaries values(2657, "2018-10-22");
+insert into diaries values(2658, "2018-10-23");
+insert into diaries values(2659, "2018-10-24");
+insert into diaries values(2660, "2018-10-25");
+insert into diaries values(2661, "2018-10-26");
+insert into diaries values(2662, "2018-10-27");
+insert into diaries values(2663, "2018-10-28");
+insert into diaries values(2664, "2018-10-29");
+insert into diaries values(2665, "2018-10-30");
+insert into diaries values(2666, "2018-10-31");
+insert into diaries values(2667, "2018-11-01");
+insert into diaries values(2668, "2018-11-02");
+insert into diaries values(2669, "2018-11-03");
+insert into diaries values(2670, "2018-11-04");
+insert into diaries values(2671, "2018-11-05");
+insert into diaries values(2672, "2018-11-06");
+insert into diaries values(2673, "2018-11-07");
+insert into diaries values(2674, "2018-11-08");
+insert into diaries values(2675, "2018-11-09");
+insert into diaries values(2676, "2018-11-10");
+insert into diaries values(2677, "2018-11-11");
+insert into diaries values(2678, "2018-11-12");
+insert into diaries values(2679, "2018-11-13");
+insert into diaries values(2680, "2018-11-14");
+insert into diaries values(2681, "2018-11-15");
+insert into diaries values(2682, "2018-11-16");
+insert into diaries values(2683, "2018-11-17");
+insert into diaries values(2684, "2018-11-18");
+insert into diaries values(2685, "2018-11-19");
+insert into diaries values(2686, "2018-11-20");
+insert into diaries values(2687, "2018-11-21");
+insert into diaries values(2688, "2018-11-22");
+insert into diaries values(2689, "2018-11-23");
+insert into diaries values(2690, "2018-11-24");
+insert into diaries values(2691, "2018-11-25");
+insert into diaries values(2692, "2018-11-26");
+insert into diaries values(2693, "2018-11-27");
+insert into diaries values(2694, "2018-11-28");
+insert into diaries values(2695, "2018-11-29");
+insert into diaries values(2696, "2018-11-30");
+insert into diaries values(2697, "2018-12-01");
+insert into diaries values(2698, "2018-12-02");
+insert into diaries values(2699, "2018-12-03");
+insert into diaries values(2700, "2018-12-04");
+insert into diaries values(2701, "2018-12-05");
+insert into diaries values(2702, "2018-12-06");
+insert into diaries values(2703, "2018-12-07");
+insert into diaries values(2704, "2018-12-08");
+insert into diaries values(2705, "2018-12-09");
+insert into diaries values(2706, "2018-12-10");
+insert into diaries values(2707, "2018-12-11");
+insert into diaries values(2708, "2018-12-12");
+insert into diaries values(2709, "2018-12-13");
+insert into diaries values(2710, "2018-12-14");
+insert into diaries values(2711, "2018-12-15");
+insert into diaries values(2712, "2018-12-16");
+insert into diaries values(2713, "2018-12-17");
+insert into diaries values(2714, "2018-12-18");
+insert into diaries values(2715, "2018-12-19");
+insert into diaries values(2716, "2018-12-20");
+insert into diaries values(2717, "2018-12-21");
+insert into diaries values(2718, "2018-12-22");
+insert into diaries values(2719, "2018-12-23");
+insert into diaries values(2720, "2018-12-24");
+insert into diaries values(2721, "2018-12-25");
+insert into diaries values(2722, "2018-12-26");
+insert into diaries values(2723, "2018-12-27");
+insert into diaries values(2724, "2018-12-28");
+insert into diaries values(2725, "2018-12-29");
+insert into diaries values(2726, "2018-12-30");
+insert into diaries values(2727, "2018-12-31");
+insert into diaries values(2728, "2019-01-01");
+insert into diaries values(2729, "2019-01-02");
+insert into diaries values(2730, "2019-01-03");
+insert into diaries values(2731, "2019-01-04");
+insert into diaries values(2732, "2019-01-05");
+insert into diaries values(2733, "2019-01-06");
+insert into diaries values(2734, "2019-01-07");
+insert into diaries values(2735, "2019-01-08");
+insert into diaries values(2736, "2019-01-09");
+insert into diaries values(2737, "2019-01-10");
+insert into diaries values(2738, "2019-01-11");
+insert into diaries values(2739, "2019-01-12");
+insert into diaries values(2740, "2019-01-13");
+insert into diaries values(2741, "2019-01-14");
+insert into diaries values(2742, "2019-01-15");
+insert into diaries values(2743, "2019-01-16");
+insert into diaries values(2744, "2019-01-17");
+insert into diaries values(2745, "2019-01-18");
+insert into diaries values(2746, "2019-01-19");
+insert into diaries values(2747, "2019-01-20");
+insert into diaries values(2748, "2019-01-21");
+insert into diaries values(2749, "2019-01-22");
+insert into diaries values(2750, "2019-01-23");
+insert into diaries values(2751, "2019-01-24");
+insert into diaries values(2752, "2019-01-25");
+insert into diaries values(2753, "2019-01-26");
+insert into diaries values(2754, "2019-01-27");
+insert into diaries values(2755, "2019-01-28");
+insert into diaries values(2756, "2019-01-29");
+insert into diaries values(2757, "2019-01-30");
+insert into diaries values(2758, "2019-01-31");
+insert into diaries values(2759, "2019-02-01");
+insert into diaries values(2760, "2019-02-02");
+insert into diaries values(2761, "2019-02-03");
+insert into diaries values(2762, "2019-02-04");
+insert into diaries values(2763, "2019-02-05");
+insert into diaries values(2764, "2019-02-06");
+insert into diaries values(2765, "2019-02-07");
+insert into diaries values(2766, "2019-02-08");
+insert into diaries values(2767, "2019-02-09");
+insert into diaries values(2768, "2019-02-10");
+insert into diaries values(2769, "2019-02-11");
+insert into diaries values(2770, "2019-02-12");
+insert into diaries values(2771, "2019-02-13");
+insert into diaries values(2772, "2019-02-14");
+insert into diaries values(2773, "2019-02-15");
+insert into diaries values(2774, "2019-02-16");
+insert into diaries values(2775, "2019-02-17");
+insert into diaries values(2776, "2019-02-18");
+insert into diaries values(2777, "2019-02-19");
+insert into diaries values(2778, "2019-02-20");
+insert into diaries values(2779, "2019-02-21");
+insert into diaries values(2780, "2019-02-22");
+insert into diaries values(2781, "2019-02-23");
+insert into diaries values(2782, "2019-02-24");
+insert into diaries values(2783, "2019-02-25");
+insert into diaries values(2784, "2019-02-26");
+insert into diaries values(2785, "2019-02-27");
+insert into diaries values(2786, "2019-02-28");
+insert into diaries values(2787, "2019-03-01");
+insert into diaries values(2788, "2019-03-02");
+insert into diaries values(2789, "2019-03-03");
+insert into diaries values(2790, "2019-03-04");
+insert into diaries values(2791, "2019-03-05");
+insert into diaries values(2792, "2019-03-06");
+insert into diaries values(2793, "2019-03-07");
+insert into diaries values(2794, "2019-03-08");
+insert into diaries values(2795, "2019-03-09");
+insert into diaries values(2796, "2019-03-10");
+insert into diaries values(2797, "2019-03-11");
+insert into diaries values(2798, "2019-03-12");
+insert into diaries values(2799, "2019-03-13");
+insert into diaries values(2800, "2019-03-14");
+insert into diaries values(2801, "2019-03-15");
+insert into diaries values(2802, "2019-03-16");
+insert into diaries values(2803, "2019-03-17");
+insert into diaries values(2804, "2019-03-18");
+insert into diaries values(2805, "2019-03-19");
+insert into diaries values(2806, "2019-03-20");
+insert into diaries values(2807, "2019-03-21");
+insert into diaries values(2808, "2019-03-22");
+insert into diaries values(2809, "2019-03-23");
+insert into diaries values(2810, "2019-03-24");
+insert into diaries values(2811, "2019-03-25");
+insert into diaries values(2812, "2019-03-26");
+insert into diaries values(2813, "2019-03-27");
+insert into diaries values(2814, "2019-03-28");
+insert into diaries values(2815, "2019-03-29");
+insert into diaries values(2816, "2019-03-30");
+insert into diaries values(2817, "2019-03-31");
+insert into diaries values(2818, "2019-04-01");
+insert into diaries values(2819, "2019-04-02");
+insert into diaries values(2820, "2019-04-03");
+insert into diaries values(2821, "2019-04-04");
+insert into diaries values(2822, "2019-04-05");
+insert into diaries values(2823, "2019-04-06");
+insert into diaries values(2824, "2019-04-07");
+insert into diaries values(2825, "2019-04-08");
+insert into diaries values(2826, "2019-04-09");
+insert into diaries values(2827, "2019-04-10");
+insert into diaries values(2828, "2019-04-11");
+insert into diaries values(2829, "2019-04-12");
+insert into diaries values(2830, "2019-04-13");
+insert into diaries values(2831, "2019-04-14");
+insert into diaries values(2832, "2019-04-15");
+insert into diaries values(2833, "2019-04-16");
+insert into diaries values(2834, "2019-04-17");
+insert into diaries values(2835, "2019-04-18");
+insert into diaries values(2836, "2019-04-19");
+insert into diaries values(2837, "2019-04-20");
+insert into diaries values(2838, "2019-04-21");
+insert into diaries values(2839, "2019-04-22");
+insert into diaries values(2840, "2019-04-23");
+insert into diaries values(2841, "2019-04-24");
+insert into diaries values(2842, "2019-04-25");
+insert into diaries values(2843, "2019-04-26");
+insert into diaries values(2844, "2019-04-27");
+insert into diaries values(2845, "2019-04-28");
+insert into diaries values(2846, "2019-04-29");
+insert into diaries values(2847, "2019-04-30");
+insert into diaries values(2848, "2019-05-01");
+insert into diaries values(2849, "2019-05-02");
+insert into diaries values(2850, "2019-05-03");
+insert into diaries values(2851, "2019-05-04");
+insert into diaries values(2852, "2019-05-05");
+insert into diaries values(2853, "2019-05-06");
+insert into diaries values(2854, "2019-05-07");
+insert into diaries values(2855, "2019-05-08");
+insert into diaries values(2856, "2019-05-09");
+insert into diaries values(2857, "2019-05-10");
+insert into diaries values(2858, "2019-05-11");
+insert into diaries values(2859, "2019-05-12");
+insert into diaries values(2860, "2019-05-13");
+insert into diaries values(2861, "2019-05-14");
+insert into diaries values(2862, "2019-05-15");
+insert into diaries values(2863, "2019-05-16");
+insert into diaries values(2864, "2019-05-17");
+insert into diaries values(2865, "2019-05-18");
+insert into diaries values(2866, "2019-05-19");
+insert into diaries values(2867, "2019-05-20");
+insert into diaries values(2868, "2019-05-21");
+insert into diaries values(2869, "2019-05-22");
+insert into diaries values(2870, "2019-05-23");
+insert into diaries values(2871, "2019-05-24");
+insert into diaries values(2872, "2019-05-25");
+insert into diaries values(2873, "2019-05-26");
+insert into diaries values(2874, "2019-05-27");
+insert into diaries values(2875, "2019-05-28");
+insert into diaries values(2876, "2019-05-29");
+insert into diaries values(2877, "2019-05-30");
+insert into diaries values(2878, "2019-05-31");
+insert into diaries values(2879, "2019-06-01");
+insert into diaries values(2880, "2019-06-02");
+insert into diaries values(2881, "2019-06-03");
+insert into diaries values(2882, "2019-06-04");
+insert into diaries values(2883, "2019-06-05");
+insert into diaries values(2884, "2019-06-06");
+insert into diaries values(2885, "2019-06-07");
+insert into diaries values(2886, "2019-06-08");
+insert into diaries values(2887, "2019-06-09");
+insert into diaries values(2888, "2019-06-10");
+insert into diaries values(2889, "2019-06-11");
+insert into diaries values(2890, "2019-06-12");
+insert into diaries values(2891, "2019-06-13");
+insert into diaries values(2892, "2019-06-14");
+insert into diaries values(2893, "2019-06-15");
+insert into diaries values(2894, "2019-06-16");
+insert into diaries values(2895, "2019-06-17");
+insert into diaries values(2896, "2019-06-18");
+insert into diaries values(2897, "2019-06-19");
+insert into diaries values(2898, "2019-06-20");
+insert into diaries values(2899, "2019-06-21");
+insert into diaries values(2900, "2019-06-22");
+insert into diaries values(2901, "2019-06-23");
+insert into diaries values(2902, "2019-06-24");
+insert into diaries values(2903, "2019-06-25");
+insert into diaries values(2904, "2019-06-26");
+insert into diaries values(2905, "2019-06-27");
+insert into diaries values(2906, "2019-06-28");
+insert into diaries values(2907, "2019-06-29");
+insert into diaries values(2908, "2019-06-30");
+insert into diaries values(2909, "2019-07-01");
+insert into diaries values(2910, "2019-07-02");
+insert into diaries values(2911, "2019-07-03");
+insert into diaries values(2912, "2019-07-04");
+insert into diaries values(2913, "2019-07-05");
+insert into diaries values(2914, "2019-07-06");
+insert into diaries values(2915, "2019-07-07");
+insert into diaries values(2916, "2019-07-08");
+insert into diaries values(2917, "2019-07-09");
+insert into diaries values(2918, "2019-07-10");
+insert into diaries values(2919, "2019-07-11");
+insert into diaries values(2920, "2019-07-12");
+insert into diaries values(2921, "2019-07-13");
+insert into diaries values(2922, "2019-07-14");
+insert into diaries values(2923, "2019-07-15");
+insert into diaries values(2924, "2019-07-16");
+insert into diaries values(2925, "2019-07-17");
+insert into diaries values(2926, "2019-07-18");
+insert into diaries values(2927, "2019-07-19");
+insert into diaries values(2928, "2019-07-20");
+insert into diaries values(2929, "2019-07-21");
+insert into diaries values(2930, "2019-07-22");
+insert into diaries values(2931, "2019-07-23");
+insert into diaries values(2932, "2019-07-24");
+insert into diaries values(2933, "2019-07-25");
+insert into diaries values(2934, "2019-07-26");
+insert into diaries values(2935, "2019-07-27");
+insert into diaries values(2936, "2019-07-28");
+insert into diaries values(2937, "2019-07-29");
+insert into diaries values(2938, "2019-07-30");
+insert into diaries values(2939, "2019-07-31");
+insert into diaries values(2940, "2019-08-01");
+insert into diaries values(2941, "2019-08-02");
+insert into diaries values(2942, "2019-08-03");
+insert into diaries values(2943, "2019-08-04");
+insert into diaries values(2944, "2019-08-05");
+insert into diaries values(2945, "2019-08-06");
+insert into diaries values(2946, "2019-08-07");
+insert into diaries values(2947, "2019-08-08");
+insert into diaries values(2948, "2019-08-09");
+insert into diaries values(2949, "2019-08-10");
+insert into diaries values(2950, "2019-08-11");
+insert into diaries values(2951, "2019-08-12");
+insert into diaries values(2952, "2019-08-13");
+insert into diaries values(2953, "2019-08-14");
+insert into diaries values(2954, "2019-08-15");
+insert into diaries values(2955, "2019-08-16");
+insert into diaries values(2956, "2019-08-17");
+insert into diaries values(2957, "2019-08-18");
+insert into diaries values(2958, "2019-08-19");
+insert into diaries values(2959, "2019-08-20");
+insert into diaries values(2960, "2019-08-21");
+insert into diaries values(2961, "2019-08-22");
+insert into diaries values(2962, "2019-08-23");
+insert into diaries values(2963, "2019-08-24");
+insert into diaries values(2964, "2019-08-25");
+insert into diaries values(2965, "2019-08-26");
+insert into diaries values(2966, "2019-08-27");
+insert into diaries values(2967, "2019-08-28");
+insert into diaries values(2968, "2019-08-29");
+insert into diaries values(2969, "2019-08-30");
+insert into diaries values(2970, "2019-08-31");
+insert into diaries values(2971, "2019-09-01");
+insert into diaries values(2972, "2019-09-02");
+insert into diaries values(2973, "2019-09-03");
+insert into diaries values(2974, "2019-09-04");
+insert into diaries values(2975, "2019-09-05");
+insert into diaries values(2976, "2019-09-06");
+insert into diaries values(2977, "2019-09-07");
+insert into diaries values(2978, "2019-09-08");
+insert into diaries values(2979, "2019-09-09");
+insert into diaries values(2980, "2019-09-10");
+insert into diaries values(2981, "2019-09-11");
+insert into diaries values(2982, "2019-09-12");
+insert into diaries values(2983, "2019-09-13");
+insert into diaries values(2984, "2019-09-14");
+insert into diaries values(2985, "2019-09-15");
+insert into diaries values(2986, "2019-09-16");
+insert into diaries values(2987, "2019-09-17");
+insert into diaries values(2988, "2019-09-18");
+insert into diaries values(2989, "2019-09-19");
+insert into diaries values(2990, "2019-09-20");
+insert into diaries values(2991, "2019-09-21");
+insert into diaries values(2992, "2019-09-22");
+insert into diaries values(2993, "2019-09-23");
+insert into diaries values(2994, "2019-09-24");
+insert into diaries values(2995, "2019-09-25");
+insert into diaries values(2996, "2019-09-26");
+insert into diaries values(2997, "2019-09-27");
+insert into diaries values(2998, "2019-09-28");
+insert into diaries values(2999, "2019-09-29");
+insert into diaries values(3000, "2019-09-30");
+insert into diaries values(3001, "2019-10-01");
+insert into diaries values(3002, "2019-10-02");
+insert into diaries values(3003, "2019-10-03");
+insert into diaries values(3004, "2019-10-04");
+insert into diaries values(3005, "2019-10-05");
+insert into diaries values(3006, "2019-10-06");
+insert into diaries values(3007, "2019-10-07");
+insert into diaries values(3008, "2019-10-08");
+insert into diaries values(3009, "2019-10-09");
+insert into diaries values(3010, "2019-10-10");
+insert into diaries values(3011, "2019-10-11");
+insert into diaries values(3012, "2019-10-12");
+insert into diaries values(3013, "2019-10-13");
+insert into diaries values(3014, "2019-10-14");
+insert into diaries values(3015, "2019-10-15");
+insert into diaries values(3016, "2019-10-16");
+insert into diaries values(3017, "2019-10-17");
+insert into diaries values(3018, "2019-10-18");
+insert into diaries values(3019, "2019-10-19");
+insert into diaries values(3020, "2019-10-20");
+insert into diaries values(3021, "2019-10-21");
+insert into diaries values(3022, "2019-10-22");
+insert into diaries values(3023, "2019-10-23");
+insert into diaries values(3024, "2019-10-24");
+insert into diaries values(3025, "2019-10-25");
+insert into diaries values(3026, "2019-10-26");
+insert into diaries values(3027, "2019-10-27");
+insert into diaries values(3028, "2019-10-28");
+insert into diaries values(3029, "2019-10-29");
+insert into diaries values(3030, "2019-10-30");
+insert into diaries values(3031, "2019-10-31");
+insert into diaries values(3032, "2019-11-01");
+insert into diaries values(3033, "2019-11-02");
+insert into diaries values(3034, "2019-11-03");
+insert into diaries values(3035, "2019-11-04");
+insert into diaries values(3036, "2019-11-05");
+insert into diaries values(3037, "2019-11-06");
+insert into diaries values(3038, "2019-11-07");
+insert into diaries values(3039, "2019-11-08");
+insert into diaries values(3040, "2019-11-09");
+insert into diaries values(3041, "2019-11-10");
+insert into diaries values(3042, "2019-11-11");
+insert into diaries values(3043, "2019-11-12");
+insert into diaries values(3044, "2019-11-13");
+insert into diaries values(3045, "2019-11-14");
+insert into diaries values(3046, "2019-11-15");
+insert into diaries values(3047, "2019-11-16");
+insert into diaries values(3048, "2019-11-17");
+insert into diaries values(3049, "2019-11-18");
+insert into diaries values(3050, "2019-11-19");
+insert into diaries values(3051, "2019-11-20");
+insert into diaries values(3052, "2019-11-21");
+insert into diaries values(3053, "2019-11-22");
+insert into diaries values(3054, "2019-11-23");
+insert into diaries values(3055, "2019-11-24");
+insert into diaries values(3056, "2019-11-25");
+insert into diaries values(3057, "2019-11-26");
+insert into diaries values(3058, "2019-11-27");
+insert into diaries values(3059, "2019-11-28");
+insert into diaries values(3060, "2019-11-29");
+insert into diaries values(3061, "2019-11-30");
+insert into diaries values(3062, "2019-12-01");
+insert into diaries values(3063, "2019-12-02");
+insert into diaries values(3064, "2019-12-03");
+insert into diaries values(3065, "2019-12-04");
+insert into diaries values(3066, "2019-12-05");
+insert into diaries values(3067, "2019-12-06");
+insert into diaries values(3068, "2019-12-07");
+insert into diaries values(3069, "2019-12-08");
+insert into diaries values(3070, "2019-12-09");
+insert into diaries values(3071, "2019-12-10");
+insert into diaries values(3072, "2019-12-11");
+insert into diaries values(3073, "2019-12-12");
+insert into diaries values(3074, "2019-12-13");
+insert into diaries values(3075, "2019-12-14");
+insert into diaries values(3076, "2019-12-15");
+insert into diaries values(3077, "2019-12-16");
+insert into diaries values(3078, "2019-12-17");
+insert into diaries values(3079, "2019-12-18");
+insert into diaries values(3080, "2019-12-19");
+insert into diaries values(3081, "2019-12-20");
+insert into diaries values(3082, "2019-12-21");
+insert into diaries values(3083, "2019-12-22");
+insert into diaries values(3084, "2019-12-23");
+insert into diaries values(3085, "2019-12-24");
+insert into diaries values(3086, "2019-12-25");
+insert into diaries values(3087, "2019-12-26");
+insert into diaries values(3088, "2019-12-27");
+insert into diaries values(3089, "2019-12-28");
+insert into diaries values(3090, "2019-12-29");
+insert into diaries values(3091, "2019-12-30");
+insert into diaries values(3092, "2019-12-31");
+insert into diaries values(3093, "2020-01-01");
+insert into diaries values(3094, "2020-01-02");
+insert into diaries values(3095, "2020-01-03");
+insert into diaries values(3096, "2020-01-04");
+insert into diaries values(3097, "2020-01-05");
+insert into diaries values(3098, "2020-01-06");
+insert into diaries values(3099, "2020-01-07");
+insert into diaries values(3100, "2020-01-08");
+insert into diaries values(3101, "2020-01-09");
+insert into diaries values(3102, "2020-01-10");
+insert into diaries values(3103, "2020-01-11");
+insert into diaries values(3104, "2020-01-12");
+insert into diaries values(3105, "2020-01-13");
+insert into diaries values(3106, "2020-01-14");
+insert into diaries values(3107, "2020-01-15");
+insert into diaries values(3108, "2020-01-16");
+insert into diaries values(3109, "2020-01-17");
+insert into diaries values(3110, "2020-01-18");
+insert into diaries values(3111, "2020-01-19");
+insert into diaries values(3112, "2020-01-20");
+insert into diaries values(3113, "2020-01-21");
+insert into diaries values(3114, "2020-01-22");
+insert into diaries values(3115, "2020-01-23");
+insert into diaries values(3116, "2020-01-24");
+insert into diaries values(3117, "2020-01-25");
+insert into diaries values(3118, "2020-01-26");
+insert into diaries values(3119, "2020-01-27");
+insert into diaries values(3120, "2020-01-28");
+insert into diaries values(3121, "2020-01-29");
+insert into diaries values(3122, "2020-01-30");
+insert into diaries values(3123, "2020-01-31");
+insert into diaries values(3124, "2020-02-01");
+insert into diaries values(3125, "2020-02-02");
+insert into diaries values(3126, "2020-02-03");
+insert into diaries values(3127, "2020-02-04");
+insert into diaries values(3128, "2020-02-05");
+insert into diaries values(3129, "2020-02-06");
+insert into diaries values(3130, "2020-02-07");
+insert into diaries values(3131, "2020-02-08");
+insert into diaries values(3132, "2020-02-09");
+insert into diaries values(3133, "2020-02-10");
+insert into diaries values(3134, "2020-02-11");
+insert into diaries values(3135, "2020-02-12");
+insert into diaries values(3136, "2020-02-13");
+insert into diaries values(3137, "2020-02-14");
+insert into diaries values(3138, "2020-02-15");
+insert into diaries values(3139, "2020-02-16");
+insert into diaries values(3140, "2020-02-17");
+insert into diaries values(3141, "2020-02-18");
+insert into diaries values(3142, "2020-02-19");
+insert into diaries values(3143, "2020-02-20");
+insert into diaries values(3144, "2020-02-21");
+insert into diaries values(3145, "2020-02-22");
+insert into diaries values(3146, "2020-02-23");
+insert into diaries values(3147, "2020-02-24");
+insert into diaries values(3148, "2020-02-25");
+insert into diaries values(3149, "2020-02-26");
+insert into diaries values(3150, "2020-02-27");
+insert into diaries values(3151, "2020-02-28");
+insert into diaries values(3152, "2020-02-29");
+insert into diaries values(3153, "2020-03-01");
+insert into diaries values(3154, "2020-03-02");
+insert into diaries values(3155, "2020-03-03");
+insert into diaries values(3156, "2020-03-04");
+insert into diaries values(3157, "2020-03-05");
+insert into diaries values(3158, "2020-03-06");
+insert into diaries values(3159, "2020-03-07");
+insert into diaries values(3160, "2020-03-08");
+insert into diaries values(3161, "2020-03-09");
+insert into diaries values(3162, "2020-03-10");
+insert into diaries values(3163, "2020-03-11");
+insert into diaries values(3164, "2020-03-12");
+insert into diaries values(3165, "2020-03-13");
+insert into diaries values(3166, "2020-03-14");
+insert into diaries values(3167, "2020-03-15");
+insert into diaries values(3168, "2020-03-16");
+insert into diaries values(3169, "2020-03-17");
+insert into diaries values(3170, "2020-03-18");
+insert into diaries values(3171, "2020-03-19");
+insert into diaries values(3172, "2020-03-20");
+insert into diaries values(3173, "2020-03-21");
+insert into diaries values(3174, "2020-03-22");
+insert into diaries values(3175, "2020-03-23");
+insert into diaries values(3176, "2020-03-24");
+insert into diaries values(3177, "2020-03-25");
+insert into diaries values(3178, "2020-03-26");
+insert into diaries values(3179, "2020-03-27");
+insert into diaries values(3180, "2020-03-28");
+insert into diaries values(3181, "2020-03-29");
+insert into diaries values(3182, "2020-03-30");
+insert into diaries values(3183, "2020-03-31");
+insert into diaries values(3184, "2020-04-01");
+insert into diaries values(3185, "2020-04-02");
+insert into diaries values(3186, "2020-04-03");
+insert into diaries values(3187, "2020-04-04");
+insert into diaries values(3188, "2020-04-05");
+insert into diaries values(3189, "2020-04-06");
+insert into diaries values(3190, "2020-04-07");
+insert into diaries values(3191, "2020-04-08");
+insert into diaries values(3192, "2020-04-09");
+insert into diaries values(3193, "2020-04-10");
+insert into diaries values(3194, "2020-04-11");
+insert into diaries values(3195, "2020-04-12");
+insert into diaries values(3196, "2020-04-13");
+insert into diaries values(3197, "2020-04-14");
+insert into diaries values(3198, "2020-04-15");
+insert into diaries values(3199, "2020-04-16");
+insert into diaries values(3200, "2020-04-17");
+insert into diaries values(3201, "2020-04-18");
+insert into diaries values(3202, "2020-04-19");
+insert into diaries values(3203, "2020-04-20");
+insert into diaries values(3204, "2020-04-21");
+insert into diaries values(3205, "2020-04-22");
+insert into diaries values(3206, "2020-04-23");
+insert into diaries values(3207, "2020-04-24");
+insert into diaries values(3208, "2020-04-25");
+insert into diaries values(3209, "2020-04-26");
+insert into diaries values(3210, "2020-04-27");
+insert into diaries values(3211, "2020-04-28");
+insert into diaries values(3212, "2020-04-29");
+insert into diaries values(3213, "2020-04-30");
+insert into diaries values(3214, "2020-05-01");
+insert into diaries values(3215, "2020-05-02");
+insert into diaries values(3216, "2020-05-03");
+insert into diaries values(3217, "2020-05-04");
+insert into diaries values(3218, "2020-05-05");
+insert into diaries values(3219, "2020-05-06");
+insert into diaries values(3220, "2020-05-07");
+insert into diaries values(3221, "2020-05-08");
+insert into diaries values(3222, "2020-05-09");
+insert into diaries values(3223, "2020-05-10");
+insert into diaries values(3224, "2020-05-11");
+insert into diaries values(3225, "2020-05-12");
+insert into diaries values(3226, "2020-05-13");
+insert into diaries values(3227, "2020-05-14");
+insert into diaries values(3228, "2020-05-15");
+insert into diaries values(3229, "2020-05-16");
+insert into diaries values(3230, "2020-05-17");
+insert into diaries values(3231, "2020-05-18");
+insert into diaries values(3232, "2020-05-19");
+insert into diaries values(3233, "2020-05-20");
+insert into diaries values(3234, "2020-05-21");
+insert into diaries values(3235, "2020-05-22");
+insert into diaries values(3236, "2020-05-23");
+insert into diaries values(3237, "2020-05-24");
+insert into diaries values(3238, "2020-05-25");
+insert into diaries values(3239, "2020-05-26");
+insert into diaries values(3240, "2020-05-27");
+insert into diaries values(3241, "2020-05-28");
+insert into diaries values(3242, "2020-05-29");
+insert into diaries values(3243, "2020-05-30");
+insert into diaries values(3244, "2020-05-31");
+insert into diaries values(3245, "2020-06-01");
+insert into diaries values(3246, "2020-06-02");
+insert into diaries values(3247, "2020-06-03");
+insert into diaries values(3248, "2020-06-04");
+insert into diaries values(3249, "2020-06-05");
+insert into diaries values(3250, "2020-06-06");
+insert into diaries values(3251, "2020-06-07");
+insert into diaries values(3252, "2020-06-08");
+insert into diaries values(3253, "2020-06-09");
+insert into diaries values(3254, "2020-06-10");
+insert into diaries values(3255, "2020-06-11");
+insert into diaries values(3256, "2020-06-12");
+insert into diaries values(3257, "2020-06-13");
+insert into diaries values(3258, "2020-06-14");
+insert into diaries values(3259, "2020-06-15");
+insert into diaries values(3260, "2020-06-16");
+insert into diaries values(3261, "2020-06-17");
+insert into diaries values(3262, "2020-06-18");
+insert into diaries values(3263, "2020-06-19");
+insert into diaries values(3264, "2020-06-20");
+insert into diaries values(3265, "2020-06-21");
+insert into diaries values(3266, "2020-06-22");
+insert into diaries values(3267, "2020-06-23");
+insert into diaries values(3268, "2020-06-24");
+insert into diaries values(3269, "2020-06-25");
+insert into diaries values(3270, "2020-06-26");
+insert into diaries values(3271, "2020-06-27");
+insert into diaries values(3272, "2020-06-28");
+insert into diaries values(3273, "2020-06-29");
+insert into diaries values(3274, "2020-06-30");
+insert into diaries values(3275, "2020-07-01");
+insert into diaries values(3276, "2020-07-02");
+insert into diaries values(3277, "2020-07-03");
+insert into diaries values(3278, "2020-07-04");
+insert into diaries values(3279, "2020-07-05");
+insert into diaries values(3280, "2020-07-06");
+insert into diaries values(3281, "2020-07-07");
+insert into diaries values(3282, "2020-07-08");
+insert into diaries values(3283, "2020-07-09");
+insert into diaries values(3284, "2020-07-10");
+insert into diaries values(3285, "2020-07-11");
+insert into diaries values(3286, "2020-07-12");
+insert into diaries values(3287, "2020-07-13");
+insert into diaries values(3288, "2020-07-14");
+insert into diaries values(3289, "2020-07-15");
+insert into diaries values(3290, "2020-07-16");
+insert into diaries values(3291, "2020-07-17");
+insert into diaries values(3292, "2020-07-18");
+insert into diaries values(3293, "2020-07-19");
+insert into diaries values(3294, "2020-07-20");
+insert into diaries values(3295, "2020-07-21");
+insert into diaries values(3296, "2020-07-22");
+insert into diaries values(3297, "2020-07-23");
+insert into diaries values(3298, "2020-07-24");
+insert into diaries values(3299, "2020-07-25");
+insert into diaries values(3300, "2020-07-26");
+insert into diaries values(3301, "2020-07-27");
+insert into diaries values(3302, "2020-07-28");
+insert into diaries values(3303, "2020-07-29");
+insert into diaries values(3304, "2020-07-30");
+insert into diaries values(3305, "2020-07-31");
+insert into diaries values(3306, "2020-08-01");
+insert into diaries values(3307, "2020-08-02");
+insert into diaries values(3308, "2020-08-03");
+insert into diaries values(3309, "2020-08-04");
+insert into diaries values(3310, "2020-08-05");
+insert into diaries values(3311, "2020-08-06");
+insert into diaries values(3312, "2020-08-07");
+insert into diaries values(3313, "2020-08-08");
+insert into diaries values(3314, "2020-08-09");
+insert into diaries values(3315, "2020-08-10");
+insert into diaries values(3316, "2020-08-11");
+insert into diaries values(3317, "2020-08-12");
+insert into diaries values(3318, "2020-08-13");
+insert into diaries values(3319, "2020-08-14");
+insert into diaries values(3320, "2020-08-15");
+insert into diaries values(3321, "2020-08-16");
+insert into diaries values(3322, "2020-08-17");
+insert into diaries values(3323, "2020-08-18");
+insert into diaries values(3324, "2020-08-19");
+insert into diaries values(3325, "2020-08-20");
+insert into diaries values(3326, "2020-08-21");
+insert into diaries values(3327, "2020-08-22");
+insert into diaries values(3328, "2020-08-23");
+insert into diaries values(3329, "2020-08-24");
+insert into diaries values(3330, "2020-08-25");
+insert into diaries values(3331, "2020-08-26");
+insert into diaries values(3332, "2020-08-27");
+insert into diaries values(3333, "2020-08-28");
+insert into diaries values(3334, "2020-08-29");
+insert into diaries values(3335, "2020-08-30");
+insert into diaries values(3336, "2020-08-31");
+insert into diaries values(3337, "2020-09-01");
+insert into diaries values(3338, "2020-09-02");
+insert into diaries values(3339, "2020-09-03");
+insert into diaries values(3340, "2020-09-04");
+insert into diaries values(3341, "2020-09-05");
+insert into diaries values(3342, "2020-09-06");
+insert into diaries values(3343, "2020-09-07");
+insert into diaries values(3344, "2020-09-08");
+insert into diaries values(3345, "2020-09-09");
+insert into diaries values(3346, "2020-09-10");
+insert into diaries values(3347, "2020-09-11");
+insert into diaries values(3348, "2020-09-12");
+insert into diaries values(3349, "2020-09-13");
+insert into diaries values(3350, "2020-09-14");
+insert into diaries values(3351, "2020-09-15");
+insert into diaries values(3352, "2020-09-16");
+insert into diaries values(3353, "2020-09-17");
+insert into diaries values(3354, "2020-09-18");
+insert into diaries values(3355, "2020-09-19");
+insert into diaries values(3356, "2020-09-20");
+insert into diaries values(3357, "2020-09-21");
+insert into diaries values(3358, "2020-09-22");
+insert into diaries values(3359, "2020-09-23");
+insert into diaries values(3360, "2020-09-24");
+insert into diaries values(3361, "2020-09-25");
+insert into diaries values(3362, "2020-09-26");
+insert into diaries values(3363, "2020-09-27");
+insert into diaries values(3364, "2020-09-28");
+insert into diaries values(3365, "2020-09-29");
+insert into diaries values(3366, "2020-09-30");
+insert into diaries values(3367, "2020-10-01");
+insert into diaries values(3368, "2020-10-02");
+insert into diaries values(3369, "2020-10-03");
+insert into diaries values(3370, "2020-10-04");
+insert into diaries values(3371, "2020-10-05");
+insert into diaries values(3372, "2020-10-06");
+insert into diaries values(3373, "2020-10-07");
+insert into diaries values(3374, "2020-10-08");
+insert into diaries values(3375, "2020-10-09");
+insert into diaries values(3376, "2020-10-10");
+insert into diaries values(3377, "2020-10-11");
+insert into diaries values(3378, "2020-10-12");
+insert into diaries values(3379, "2020-10-13");
+insert into diaries values(3380, "2020-10-14");
+insert into diaries values(3381, "2020-10-15");
+insert into diaries values(3382, "2020-10-16");
+insert into diaries values(3383, "2020-10-17");
+insert into diaries values(3384, "2020-10-18");
+insert into diaries values(3385, "2020-10-19");
+insert into diaries values(3386, "2020-10-20");
+insert into diaries values(3387, "2020-10-21");
+insert into diaries values(3388, "2020-10-22");
+insert into diaries values(3389, "2020-10-23");
+insert into diaries values(3390, "2020-10-24");
+insert into diaries values(3391, "2020-10-25");
+insert into diaries values(3392, "2020-10-26");
+insert into diaries values(3393, "2020-10-27");
+insert into diaries values(3394, "2020-10-28");
+insert into diaries values(3395, "2020-10-29");
+insert into diaries values(3396, "2020-10-30");
+insert into diaries values(3397, "2020-10-31");
+insert into diaries values(3398, "2020-11-01");
+insert into diaries values(3399, "2020-11-02");
+insert into diaries values(3400, "2020-11-03");
+insert into diaries values(3401, "2020-11-04");
+insert into diaries values(3402, "2020-11-05");
+insert into diaries values(3403, "2020-11-06");
+insert into diaries values(3404, "2020-11-07");
+insert into diaries values(3405, "2020-11-08");
+insert into diaries values(3406, "2020-11-09");
+insert into diaries values(3407, "2020-11-10");
+insert into diaries values(3408, "2020-11-11");
+insert into diaries values(3409, "2020-11-12");
+insert into diaries values(3410, "2020-11-13");
+insert into diaries values(3411, "2020-11-14");
+insert into diaries values(3412, "2020-11-15");
+insert into diaries values(3413, "2020-11-16");
+insert into diaries values(3414, "2020-11-17");
+insert into diaries values(3415, "2020-11-18");
+insert into diaries values(3416, "2020-11-19");
+insert into diaries values(3417, "2020-11-20");
+insert into diaries values(3418, "2020-11-21");
+insert into diaries values(3419, "2020-11-22");
+insert into diaries values(3420, "2020-11-23");
+insert into diaries values(3421, "2020-11-24");
+insert into diaries values(3422, "2020-11-25");
+insert into diaries values(3423, "2020-11-26");
+insert into diaries values(3424, "2020-11-27");
+insert into diaries values(3425, "2020-11-28");
+insert into diaries values(3426, "2020-11-29");
+insert into diaries values(3427, "2020-11-30");
+insert into diaries values(3428, "2020-12-01");
+insert into diaries values(3429, "2020-12-02");
+insert into diaries values(3430, "2020-12-03");
+insert into diaries values(3431, "2020-12-04");
+insert into diaries values(3432, "2020-12-05");
+insert into diaries values(3433, "2020-12-06");
+insert into diaries values(3434, "2020-12-07");
+insert into diaries values(3435, "2020-12-08");
+insert into diaries values(3436, "2020-12-09");
+insert into diaries values(3437, "2020-12-10");
+insert into diaries values(3438, "2020-12-11");
+insert into diaries values(3439, "2020-12-12");
+insert into diaries values(3440, "2020-12-13");
+insert into diaries values(3441, "2020-12-14");
+insert into diaries values(3442, "2020-12-15");
+insert into diaries values(3443, "2020-12-16");
+insert into diaries values(3444, "2020-12-17");
+insert into diaries values(3445, "2020-12-18");
+insert into diaries values(3446, "2020-12-19");
+insert into diaries values(3447, "2020-12-20");
+insert into diaries values(3448, "2020-12-21");
+insert into diaries values(3449, "2020-12-22");
+insert into diaries values(3450, "2020-12-23");
+insert into diaries values(3451, "2020-12-24");
+insert into diaries values(3452, "2020-12-25");
+insert into diaries values(3453, "2020-12-26");
+insert into diaries values(3454, "2020-12-27");
+insert into diaries values(3455, "2020-12-28");
+insert into diaries values(3456, "2020-12-29");
+insert into diaries values(3457, "2020-12-30");
+insert into diaries values(3458, "2020-12-31");
+insert into diaries values(3459, "2021-01-01");
+insert into diaries values(3460, "2021-01-02");
+insert into diaries values(3461, "2021-01-03");
+insert into diaries values(3462, "2021-01-04");
+insert into diaries values(3463, "2021-01-05");
+insert into diaries values(3464, "2021-01-06");
+insert into diaries values(3465, "2021-01-07");
+insert into diaries values(3466, "2021-01-08");
+insert into diaries values(3467, "2021-01-09");
+insert into diaries values(3468, "2021-01-10");
+insert into diaries values(3469, "2021-01-11");
+insert into diaries values(3470, "2021-01-12");
+insert into diaries values(3471, "2021-01-13");
+insert into diaries values(3472, "2021-01-14");
+insert into diaries values(3473, "2021-01-15");
+insert into diaries values(3474, "2021-01-16");
+insert into diaries values(3475, "2021-01-17");
+insert into diaries values(3476, "2021-01-18");
+insert into diaries values(3477, "2021-01-19");
+insert into diaries values(3478, "2021-01-20");
+insert into diaries values(3479, "2021-01-21");
+insert into diaries values(3480, "2021-01-22");
+insert into diaries values(3481, "2021-01-23");
+insert into diaries values(3482, "2021-01-24");
+insert into diaries values(3483, "2021-01-25");
+insert into diaries values(3484, "2021-01-26");
+insert into diaries values(3485, "2021-01-27");
+insert into diaries values(3486, "2021-01-28");
+insert into diaries values(3487, "2021-01-29");
+insert into diaries values(3488, "2021-01-30");
+insert into diaries values(3489, "2021-01-31");
+insert into diaries values(3490, "2021-02-01");
+insert into diaries values(3491, "2021-02-02");
+insert into diaries values(3492, "2021-02-03");
+insert into diaries values(3493, "2021-02-04");
+insert into diaries values(3494, "2021-02-05");
+insert into diaries values(3495, "2021-02-06");
+insert into diaries values(3496, "2021-02-07");
+insert into diaries values(3497, "2021-02-08");
+insert into diaries values(3498, "2021-02-09");
+insert into diaries values(3499, "2021-02-10");
+insert into diaries values(3500, "2021-02-11");
+insert into diaries values(3501, "2021-02-12");
+insert into diaries values(3502, "2021-02-13");
+insert into diaries values(3503, "2021-02-14");
+insert into diaries values(3504, "2021-02-15");
+insert into diaries values(3505, "2021-02-16");
+insert into diaries values(3506, "2021-02-17");
+insert into diaries values(3507, "2021-02-18");
+insert into diaries values(3508, "2021-02-19");
+insert into diaries values(3509, "2021-02-20");
+insert into diaries values(3510, "2021-02-21");
+insert into diaries values(3511, "2021-02-22");
+insert into diaries values(3512, "2021-02-23");
+insert into diaries values(3513, "2021-02-24");
+insert into diaries values(3514, "2021-02-25");
+insert into diaries values(3515, "2021-02-26");
+insert into diaries values(3516, "2021-02-27");
+insert into diaries values(3517, "2021-02-28");
+insert into diaries values(3518, "2021-03-01");
+insert into diaries values(3519, "2021-03-02");
+insert into diaries values(3520, "2021-03-03");
+insert into diaries values(3521, "2021-03-04");
+insert into diaries values(3522, "2021-03-05");
+insert into diaries values(3523, "2021-03-06");
+insert into diaries values(3524, "2021-03-07");
+insert into diaries values(3525, "2021-03-08");
+insert into diaries values(3526, "2021-03-09");
+insert into diaries values(3527, "2021-03-10");
+insert into diaries values(3528, "2021-03-11");
+insert into diaries values(3529, "2021-03-12");
+insert into diaries values(3530, "2021-03-13");
+insert into diaries values(3531, "2021-03-14");
+insert into diaries values(3532, "2021-03-15");
+insert into diaries values(3533, "2021-03-16");
+insert into diaries values(3534, "2021-03-17");
+insert into diaries values(3535, "2021-03-18");
+insert into diaries values(3536, "2021-03-19");
+insert into diaries values(3537, "2021-03-20");
+insert into diaries values(3538, "2021-03-21");
+insert into diaries values(3539, "2021-03-22");
+insert into diaries values(3540, "2021-03-23");
+insert into diaries values(3541, "2021-03-24");
+insert into diaries values(3542, "2021-03-25");
+insert into diaries values(3543, "2021-03-26");
+insert into diaries values(3544, "2021-03-27");
+insert into diaries values(3545, "2021-03-28");
+insert into diaries values(3546, "2021-03-29");
+insert into diaries values(3547, "2021-03-30");
+insert into diaries values(3548, "2021-03-31");
+insert into diaries values(3549, "2021-04-01");
+insert into diaries values(3550, "2021-04-02");
+insert into diaries values(3551, "2021-04-03");
+insert into diaries values(3552, "2021-04-04");
+insert into diaries values(3553, "2021-04-05");
+insert into diaries values(3554, "2021-04-06");
+insert into diaries values(3555, "2021-04-07");
+insert into diaries values(3556, "2021-04-08");
+insert into diaries values(3557, "2021-04-09");
+insert into diaries values(3558, "2021-04-10");
+insert into diaries values(3559, "2021-04-11");
+insert into diaries values(3560, "2021-04-12");
+insert into diaries values(3561, "2021-04-13");
+insert into diaries values(3562, "2021-04-14");
+insert into diaries values(3563, "2021-04-15");
+insert into diaries values(3564, "2021-04-16");
+insert into diaries values(3565, "2021-04-17");
+insert into diaries values(3566, "2021-04-18");
+insert into diaries values(3567, "2021-04-19");
+insert into diaries values(3568, "2021-04-20");
+insert into diaries values(3569, "2021-04-21");
+insert into diaries values(3570, "2021-04-22");
+insert into diaries values(3571, "2021-04-23");
+insert into diaries values(3572, "2021-04-24");
+insert into diaries values(3573, "2021-04-25");
+insert into diaries values(3574, "2021-04-26");
+insert into diaries values(3575, "2021-04-27");
+insert into diaries values(3576, "2021-04-28");
+insert into diaries values(3577, "2021-04-29");
+insert into diaries values(3578, "2021-04-30");
+insert into diaries values(3579, "2021-05-01");
+insert into diaries values(3580, "2021-05-02");
+insert into diaries values(3581, "2021-05-03");
+insert into diaries values(3582, "2021-05-04");
+insert into diaries values(3583, "2021-05-05");
+insert into diaries values(3584, "2021-05-06");
+insert into diaries values(3585, "2021-05-07");
+insert into diaries values(3586, "2021-05-08");
+insert into diaries values(3587, "2021-05-09");
+insert into diaries values(3588, "2021-05-10");
+insert into diaries values(3589, "2021-05-11");
+insert into diaries values(3590, "2021-05-12");
+insert into diaries values(3591, "2021-05-13");
+insert into diaries values(3592, "2021-05-14");
+insert into diaries values(3593, "2021-05-15");
+insert into diaries values(3594, "2021-05-16");
+insert into diaries values(3595, "2021-05-17");
+insert into diaries values(3596, "2021-05-18");
+insert into diaries values(3597, "2021-05-19");
+insert into diaries values(3598, "2021-05-20");
+insert into diaries values(3599, "2021-05-21");
+insert into diaries values(3600, "2021-05-22");
+insert into diaries values(3601, "2021-05-23");
+insert into diaries values(3602, "2021-05-24");
+insert into diaries values(3603, "2021-05-25");
+insert into diaries values(3604, "2021-05-26");
+insert into diaries values(3605, "2021-05-27");
+insert into diaries values(3606, "2021-05-28");
+insert into diaries values(3607, "2021-05-29");
+insert into diaries values(3608, "2021-05-30");
+insert into diaries values(3609, "2021-05-31");
+insert into diaries values(3610, "2021-06-01");
+insert into diaries values(3611, "2021-06-02");
+insert into diaries values(3612, "2021-06-03");
+insert into diaries values(3613, "2021-06-04");
+insert into diaries values(3614, "2021-06-05");
+insert into diaries values(3615, "2021-06-06");
+insert into diaries values(3616, "2021-06-07");
+insert into diaries values(3617, "2021-06-08");
+insert into diaries values(3618, "2021-06-09");
+insert into diaries values(3619, "2021-06-10");
+insert into diaries values(3620, "2021-06-11");
+insert into diaries values(3621, "2021-06-12");
+insert into diaries values(3622, "2021-06-13");
+insert into diaries values(3623, "2021-06-14");
+insert into diaries values(3624, "2021-06-15");
+insert into diaries values(3625, "2021-06-16");
+insert into diaries values(3626, "2021-06-17");
+insert into diaries values(3627, "2021-06-18");
+insert into diaries values(3628, "2021-06-19");
+insert into diaries values(3629, "2021-06-20");
+insert into diaries values(3630, "2021-06-21");
+insert into diaries values(3631, "2021-06-22");
+insert into diaries values(3632, "2021-06-23");
+insert into diaries values(3633, "2021-06-24");
+insert into diaries values(3634, "2021-06-25");
+insert into diaries values(3635, "2021-06-26");
+insert into diaries values(3636, "2021-06-27");
+insert into diaries values(3637, "2021-06-28");
+insert into diaries values(3638, "2021-06-29");
+insert into diaries values(3639, "2021-06-30");
+insert into diaries values(3640, "2021-07-01");
+insert into diaries values(3641, "2021-07-02");
+insert into diaries values(3642, "2021-07-03");
+insert into diaries values(3643, "2021-07-04");
+insert into diaries values(3644, "2021-07-05");
+insert into diaries values(3645, "2021-07-06");
+insert into diaries values(3646, "2021-07-07");
+insert into diaries values(3647, "2021-07-08");
+insert into diaries values(3648, "2021-07-09");
+insert into diaries values(3649, "2021-07-10");
+insert into diaries values(3650, "2021-07-11");
+insert into diaries values(3651, "2021-07-12");
+insert into diaries values(3652, "2021-07-13");
+insert into diaries values(3653, "2021-07-14");
+insert into diaries values(3654, "2021-07-15");
+insert into diaries values(3655, "2021-07-16");
+insert into diaries values(3656, "2021-07-17");
+insert into diaries values(3657, "2021-07-18");
+insert into diaries values(3658, "2021-07-19");
+insert into diaries values(3659, "2021-07-20");
+insert into diaries values(3660, "2021-07-21");
+insert into diaries values(3661, "2021-07-22");
+insert into diaries values(3662, "2021-07-23");
+insert into diaries values(3663, "2021-07-24");
+insert into diaries values(3664, "2021-07-25");
+insert into diaries values(3665, "2021-07-26");
+insert into diaries values(3666, "2021-07-27");
+insert into diaries values(3667, "2021-07-28");
+insert into diaries values(3668, "2021-07-29");
+insert into diaries values(3669, "2021-07-30");
+insert into diaries values(3670, "2021-07-31");
+insert into diaries values(3671, "2021-08-01");
+insert into diaries values(3672, "2021-08-02");
+insert into diaries values(3673, "2021-08-03");
+insert into diaries values(3674, "2021-08-04");
+insert into diaries values(3675, "2021-08-05");
+insert into diaries values(3676, "2021-08-06");
+insert into diaries values(3677, "2021-08-07");
+insert into diaries values(3678, "2021-08-08");
+insert into diaries values(3679, "2021-08-09");
+insert into diaries values(3680, "2021-08-10");
+insert into diaries values(3681, "2021-08-11");
+insert into diaries values(3682, "2021-08-12");
+insert into diaries values(3683, "2021-08-13");
+insert into diaries values(3684, "2021-08-14");
+insert into diaries values(3685, "2021-08-15");
+insert into diaries values(3686, "2021-08-16");
+insert into diaries values(3687, "2021-08-17");
+insert into diaries values(3688, "2021-08-18");
+insert into diaries values(3689, "2021-08-19");
+insert into diaries values(3690, "2021-08-20");
+insert into diaries values(3691, "2021-08-21");
+insert into diaries values(3692, "2021-08-22");
+insert into diaries values(3693, "2021-08-23");
+insert into diaries values(3694, "2021-08-24");
+insert into diaries values(3695, "2021-08-25");
+insert into diaries values(3696, "2021-08-26");
+insert into diaries values(3697, "2021-08-27");
+insert into diaries values(3698, "2021-08-28");
+insert into diaries values(3699, "2021-08-29");
+insert into diaries values(3700, "2021-08-30");
+insert into diaries values(3701, "2021-08-31");
+insert into diaries values(3702, "2021-09-01");
+insert into diaries values(3703, "2021-09-02");
+insert into diaries values(3704, "2021-09-03");
+insert into diaries values(3705, "2021-09-04");
+insert into diaries values(3706, "2021-09-05");
+insert into diaries values(3707, "2021-09-06");
+insert into diaries values(3708, "2021-09-07");
+insert into diaries values(3709, "2021-09-08");
+insert into diaries values(3710, "2021-09-09");
+insert into diaries values(3711, "2021-09-10");
+insert into diaries values(3712, "2021-09-11");
+insert into diaries values(3713, "2021-09-12");
+insert into diaries values(3714, "2021-09-13");
+insert into diaries values(3715, "2021-09-14");
+insert into diaries values(3716, "2021-09-15");
+insert into diaries values(3717, "2021-09-16");
+insert into diaries values(3718, "2021-09-17");
+insert into diaries values(3719, "2021-09-18");
+insert into diaries values(3720, "2021-09-19");
+insert into diaries values(3721, "2021-09-20");
+insert into diaries values(3722, "2021-09-21");
+insert into diaries values(3723, "2021-09-22");
+insert into diaries values(3724, "2021-09-23");
+insert into diaries values(3725, "2021-09-24");
+insert into diaries values(3726, "2021-09-25");
+insert into diaries values(3727, "2021-09-26");
+insert into diaries values(3728, "2021-09-27");
+insert into diaries values(3729, "2021-09-28");
+insert into diaries values(3730, "2021-09-29");
+insert into diaries values(3731, "2021-09-30");
+insert into diaries values(3732, "2021-10-01");
+insert into diaries values(3733, "2021-10-02");
+insert into diaries values(3734, "2021-10-03");
+insert into diaries values(3735, "2021-10-04");
+insert into diaries values(3736, "2021-10-05");
+insert into diaries values(3737, "2021-10-06");
+insert into diaries values(3738, "2021-10-07");
+insert into diaries values(3739, "2021-10-08");
+insert into diaries values(3740, "2021-10-09");
+insert into diaries values(3741, "2021-10-10");
+insert into diaries values(3742, "2021-10-11");
+insert into diaries values(3743, "2021-10-12");
+insert into diaries values(3744, "2021-10-13");
+insert into diaries values(3745, "2021-10-14");
+insert into diaries values(3746, "2021-10-15");
+insert into diaries values(3747, "2021-10-16");
+insert into diaries values(3748, "2021-10-17");
+insert into diaries values(3749, "2021-10-18");
+insert into diaries values(3750, "2021-10-19");
+insert into diaries values(3751, "2021-10-20");
+insert into diaries values(3752, "2021-10-21");
+insert into diaries values(3753, "2021-10-22");
+insert into diaries values(3754, "2021-10-23");
+insert into diaries values(3755, "2021-10-24");
+insert into diaries values(3756, "2021-10-25");
+insert into diaries values(3757, "2021-10-26");
+insert into diaries values(3758, "2021-10-27");
+insert into diaries values(3759, "2021-10-28");
+insert into diaries values(3760, "2021-10-29");
+insert into diaries values(3761, "2021-10-30");
+insert into diaries values(3762, "2021-10-31");
+insert into diaries values(3763, "2021-11-01");
+insert into diaries values(3764, "2021-11-02");
+insert into diaries values(3765, "2021-11-03");
+insert into diaries values(3766, "2021-11-04");
+insert into diaries values(3767, "2021-11-05");
+insert into diaries values(3768, "2021-11-06");
+insert into diaries values(3769, "2021-11-07");
+insert into diaries values(3770, "2021-11-08");
+insert into diaries values(3771, "2021-11-09");
+insert into diaries values(3772, "2021-11-10");
+insert into diaries values(3773, "2021-11-11");
+insert into diaries values(3774, "2021-11-12");
+insert into diaries values(3775, "2021-11-13");
+insert into diaries values(3776, "2021-11-14");
+insert into diaries values(3777, "2021-11-15");
+insert into diaries values(3778, "2021-11-16");
+insert into diaries values(3779, "2021-11-17");
+insert into diaries values(3780, "2021-11-18");
+insert into diaries values(3781, "2021-11-19");
+insert into diaries values(3782, "2021-11-20");
+insert into diaries values(3783, "2021-11-21");
+insert into diaries values(3784, "2021-11-22");
+insert into diaries values(3785, "2021-11-23");
+insert into diaries values(3786, "2021-11-24");
+insert into diaries values(3787, "2021-11-25");
+insert into diaries values(3788, "2021-11-26");
+insert into diaries values(3789, "2021-11-27");
+insert into diaries values(3790, "2021-11-28");
+insert into diaries values(3791, "2021-11-29");
+insert into diaries values(3792, "2021-11-30");
+insert into diaries values(3793, "2021-12-01");
+insert into diaries values(3794, "2021-12-02");
+insert into diaries values(3795, "2021-12-03");
+insert into diaries values(3796, "2021-12-04");
+insert into diaries values(3797, "2021-12-05");
+insert into diaries values(3798, "2021-12-06");
+insert into diaries values(3799, "2021-12-07");
+insert into diaries values(3800, "2021-12-08");
+insert into diaries values(3801, "2021-12-09");
+insert into diaries values(3802, "2021-12-10");
+insert into diaries values(3803, "2021-12-11");
+insert into diaries values(3804, "2021-12-12");
+insert into diaries values(3805, "2021-12-13");
+insert into diaries values(3806, "2021-12-14");
+insert into diaries values(3807, "2021-12-15");
+insert into diaries values(3808, "2021-12-16");
+insert into diaries values(3809, "2021-12-17");
+insert into diaries values(3810, "2021-12-18");
+insert into diaries values(3811, "2021-12-19");
+insert into diaries values(3812, "2021-12-20");
+insert into diaries values(3813, "2021-12-21");
+insert into diaries values(3814, "2021-12-22");
+insert into diaries values(3815, "2021-12-23");
+insert into diaries values(3816, "2021-12-24");
+insert into diaries values(3817, "2021-12-25");
+insert into diaries values(3818, "2021-12-26");
+insert into diaries values(3819, "2021-12-27");
+insert into diaries values(3820, "2021-12-28");
+insert into diaries values(3821, "2021-12-29");
+insert into diaries values(3822, "2021-12-30");
+insert into diaries values(3823, "2021-12-31");
+insert into diaries values(3824, "2022-01-01");
+insert into diaries values(3825, "2022-01-02");
+insert into diaries values(3826, "2022-01-03");
+insert into diaries values(3827, "2022-01-04");
+insert into diaries values(3828, "2022-01-05");
+insert into diaries values(3829, "2022-01-06");
+insert into diaries values(3830, "2022-01-07");
+insert into diaries values(3831, "2022-01-08");
+insert into diaries values(3832, "2022-01-09");
+insert into diaries values(3833, "2022-01-10");
+insert into diaries values(3834, "2022-01-11");
+insert into diaries values(3835, "2022-01-12");
+insert into diaries values(3836, "2022-01-13");
+insert into diaries values(3837, "2022-01-14");
+insert into diaries values(3838, "2022-01-15");
+insert into diaries values(3839, "2022-01-16");
+insert into diaries values(3840, "2022-01-17");
+insert into diaries values(3841, "2022-01-18");
+insert into diaries values(3842, "2022-01-19");
+insert into diaries values(3843, "2022-01-20");
+insert into diaries values(3844, "2022-01-21");
+insert into diaries values(3845, "2022-01-22");
+insert into diaries values(3846, "2022-01-23");
+insert into diaries values(3847, "2022-01-24");
+insert into diaries values(3848, "2022-01-25");
+insert into diaries values(3849, "2022-01-26");
+insert into diaries values(3850, "2022-01-27");
+insert into diaries values(3851, "2022-01-28");
+insert into diaries values(3852, "2022-01-29");
+insert into diaries values(3853, "2022-01-30");
+insert into diaries values(3854, "2022-01-31");
+insert into diaries values(3855, "2022-02-01");
+insert into diaries values(3856, "2022-02-02");
+insert into diaries values(3857, "2022-02-03");
+insert into diaries values(3858, "2022-02-04");
+insert into diaries values(3859, "2022-02-05");
+insert into diaries values(3860, "2022-02-06");
+insert into diaries values(3861, "2022-02-07");
+insert into diaries values(3862, "2022-02-08");
+insert into diaries values(3863, "2022-02-09");
+insert into diaries values(3864, "2022-02-10");
+insert into diaries values(3865, "2022-02-11");
+insert into diaries values(3866, "2022-02-12");
+insert into diaries values(3867, "2022-02-13");
+insert into diaries values(3868, "2022-02-14");
+insert into diaries values(3869, "2022-02-15");
+insert into diaries values(3870, "2022-02-16");
+insert into diaries values(3871, "2022-02-17");
+insert into diaries values(3872, "2022-02-18");
+insert into diaries values(3873, "2022-02-19");
+insert into diaries values(3874, "2022-02-20");
+insert into diaries values(3875, "2022-02-21");
+insert into diaries values(3876, "2022-02-22");
+insert into diaries values(3877, "2022-02-23");
+insert into diaries values(3878, "2022-02-24");
+insert into diaries values(3879, "2022-02-25");
+insert into diaries values(3880, "2022-02-26");
+insert into diaries values(3881, "2022-02-27");
+insert into diaries values(3882, "2022-02-28");
+insert into diaries values(3883, "2022-03-01");
+insert into diaries values(3884, "2022-03-02");
+insert into diaries values(3885, "2022-03-03");
+insert into diaries values(3886, "2022-03-04");
+insert into diaries values(3887, "2022-03-05");
+insert into diaries values(3888, "2022-03-06");
+insert into diaries values(3889, "2022-03-07");
+insert into diaries values(3890, "2022-03-08");
+insert into diaries values(3891, "2022-03-09");
+insert into diaries values(3892, "2022-03-10");
+insert into diaries values(3893, "2022-03-11");
+insert into diaries values(3894, "2022-03-12");
+insert into diaries values(3895, "2022-03-13");
+insert into diaries values(3896, "2022-03-14");
+insert into diaries values(3897, "2022-03-15");
+insert into diaries values(3898, "2022-03-16");
+insert into diaries values(3899, "2022-03-17");
+insert into diaries values(3900, "2022-03-18");
+insert into diaries values(3901, "2022-03-19");
+insert into diaries values(3902, "2022-03-20");
+insert into diaries values(3903, "2022-03-21");
+insert into diaries values(3904, "2022-03-22");
+insert into diaries values(3905, "2022-03-23");
+insert into diaries values(3906, "2022-03-24");
+insert into diaries values(3907, "2022-03-25");
+insert into diaries values(3908, "2022-03-26");
+insert into diaries values(3909, "2022-03-27");
+insert into diaries values(3910, "2022-03-28");
+insert into diaries values(3911, "2022-03-29");
+insert into diaries values(3912, "2022-03-30");
+insert into diaries values(3913, "2022-03-31");
+insert into diaries values(3914, "2022-04-01");
+insert into diaries values(3915, "2022-04-02");
+insert into diaries values(3916, "2022-04-03");
+insert into diaries values(3917, "2022-04-04");
+insert into diaries values(3918, "2022-04-05");
+insert into diaries values(3919, "2022-04-06");
+insert into diaries values(3920, "2022-04-07");
+insert into diaries values(3921, "2022-04-08");
+insert into diaries values(3922, "2022-04-09");
+insert into diaries values(3923, "2022-04-10");
+insert into diaries values(3924, "2022-04-11");
+insert into diaries values(3925, "2022-04-12");
+insert into diaries values(3926, "2022-04-13");
+insert into diaries values(3927, "2022-04-14");
+insert into diaries values(3928, "2022-04-15");
+insert into diaries values(3929, "2022-04-16");
+insert into diaries values(3930, "2022-04-17");
+insert into diaries values(3931, "2022-04-18");
+insert into diaries values(3932, "2022-04-19");
+insert into diaries values(3933, "2022-04-20");
+insert into diaries values(3934, "2022-04-21");
+insert into diaries values(3935, "2022-04-22");
+insert into diaries values(3936, "2022-04-23");
+insert into diaries values(3937, "2022-04-24");
+insert into diaries values(3938, "2022-04-25");
+insert into diaries values(3939, "2022-04-26");
+insert into diaries values(3940, "2022-04-27");
+insert into diaries values(3941, "2022-04-28");
+insert into diaries values(3942, "2022-04-29");
+insert into diaries values(3943, "2022-04-30");
+insert into diaries values(3944, "2022-05-01");
+insert into diaries values(3945, "2022-05-02");
+insert into diaries values(3946, "2022-05-03");
+insert into diaries values(3947, "2022-05-04");
+insert into diaries values(3948, "2022-05-05");
+insert into diaries values(3949, "2022-05-06");
+insert into diaries values(3950, "2022-05-07");
+insert into diaries values(3951, "2022-05-08");
+insert into diaries values(3952, "2022-05-09");
+insert into diaries values(3953, "2022-05-10");
+insert into diaries values(3954, "2022-05-11");
+insert into diaries values(3955, "2022-05-12");
+insert into diaries values(3956, "2022-05-13");
+insert into diaries values(3957, "2022-05-14");
+insert into diaries values(3958, "2022-05-15");
+insert into diaries values(3959, "2022-05-16");
+insert into diaries values(3960, "2022-05-17");
+insert into diaries values(3961, "2022-05-18");
+insert into diaries values(3962, "2022-05-19");
+insert into diaries values(3963, "2022-05-20");
+insert into diaries values(3964, "2022-05-21");
+insert into diaries values(3965, "2022-05-22");
+insert into diaries values(3966, "2022-05-23");
+insert into diaries values(3967, "2022-05-24");
+insert into diaries values(3968, "2022-05-25");
+insert into diaries values(3969, "2022-05-26");
+insert into diaries values(3970, "2022-05-27");
+insert into diaries values(3971, "2022-05-28");
+insert into diaries values(3972, "2022-05-29");
+insert into diaries values(3973, "2022-05-30");
+insert into diaries values(3974, "2022-05-31");
+insert into diaries values(3975, "2022-06-01");
+insert into diaries values(3976, "2022-06-02");
+insert into diaries values(3977, "2022-06-03");
+insert into diaries values(3978, "2022-06-04");
+insert into diaries values(3979, "2022-06-05");
+insert into diaries values(3980, "2022-06-06");
+insert into diaries values(3981, "2022-06-07");
+insert into diaries values(3982, "2022-06-08");
+insert into diaries values(3983, "2022-06-09");
+insert into diaries values(3984, "2022-06-10");
+insert into diaries values(3985, "2022-06-11");
+insert into diaries values(3986, "2022-06-12");
+insert into diaries values(3987, "2022-06-13");
+insert into diaries values(3988, "2022-06-14");
+insert into diaries values(3989, "2022-06-15");
+insert into diaries values(3990, "2022-06-16");
+insert into diaries values(3991, "2022-06-17");
+insert into diaries values(3992, "2022-06-18");
+insert into diaries values(3993, "2022-06-19");
+insert into diaries values(3994, "2022-06-20");
+insert into diaries values(3995, "2022-06-21");
+insert into diaries values(3996, "2022-06-22");
+insert into diaries values(3997, "2022-06-23");
+insert into diaries values(3998, "2022-06-24");
+insert into diaries values(3999, "2022-06-25");
+insert into diaries values(4000, "2022-06-26");
+insert into diaries values(4001, "2022-06-27");
+insert into diaries values(4002, "2022-06-28");
+insert into diaries values(4003, "2022-06-29");
+insert into diaries values(4004, "2022-06-30");
+insert into diaries values(4005, "2022-07-01");
+insert into diaries values(4006, "2022-07-02");
+insert into diaries values(4007, "2022-07-03");
+insert into diaries values(4008, "2022-07-04");
+insert into diaries values(4009, "2022-07-05");
+insert into diaries values(4010, "2022-07-06");
+insert into diaries values(4011, "2022-07-07");
+insert into diaries values(4012, "2022-07-08");
+insert into diaries values(4013, "2022-07-09");
+insert into diaries values(4014, "2022-07-10");
+insert into diaries values(4015, "2022-07-11");
+insert into diaries values(4016, "2022-07-12");
+insert into diaries values(4017, "2022-07-13");
+insert into diaries values(4018, "2022-07-14");
+insert into diaries values(4019, "2022-07-15");
+insert into diaries values(4020, "2022-07-16");
+insert into diaries values(4021, "2022-07-17");
+insert into diaries values(4022, "2022-07-18");
+insert into diaries values(4023, "2022-07-19");
+insert into diaries values(4024, "2022-07-20");
+insert into diaries values(4025, "2022-07-21");
+insert into diaries values(4026, "2022-07-22");
+insert into diaries values(4027, "2022-07-23");
+insert into diaries values(4028, "2022-07-24");
+insert into diaries values(4029, "2022-07-25");
+insert into diaries values(4030, "2022-07-26");
+insert into diaries values(4031, "2022-07-27");
+insert into diaries values(4032, "2022-07-28");
+insert into diaries values(4033, "2022-07-29");
+insert into diaries values(4034, "2022-07-30");
+insert into diaries values(4035, "2022-07-31");
+insert into diaries values(4036, "2022-08-01");
+insert into diaries values(4037, "2022-08-02");
+insert into diaries values(4038, "2022-08-03");
+insert into diaries values(4039, "2022-08-04");
+insert into diaries values(4040, "2022-08-05");
+insert into diaries values(4041, "2022-08-06");
+insert into diaries values(4042, "2022-08-07");
+insert into diaries values(4043, "2022-08-08");
+insert into diaries values(4044, "2022-08-09");
+insert into diaries values(4045, "2022-08-10");
+insert into diaries values(4046, "2022-08-11");
+insert into diaries values(4047, "2022-08-12");
+insert into diaries values(4048, "2022-08-13");
+insert into diaries values(4049, "2022-08-14");
+insert into diaries values(4050, "2022-08-15");
+insert into diaries values(4051, "2022-08-16");
+insert into diaries values(4052, "2022-08-17");
+insert into diaries values(4053, "2022-08-18");
+insert into diaries values(4054, "2022-08-19");
+insert into diaries values(4055, "2022-08-20");
+insert into diaries values(4056, "2022-08-21");
+insert into diaries values(4057, "2022-08-22");
+insert into diaries values(4058, "2022-08-23");
+insert into diaries values(4059, "2022-08-24");
+insert into diaries values(4060, "2022-08-25");
+insert into diaries values(4061, "2022-08-26");
+insert into diaries values(4062, "2022-08-27");
+insert into diaries values(4063, "2022-08-28");
+insert into diaries values(4064, "2022-08-29");
+insert into diaries values(4065, "2022-08-30");
+insert into diaries values(4066, "2022-08-31");
+insert into diaries values(4067, "2022-09-01");
+insert into diaries values(4068, "2022-09-02");
+insert into diaries values(4069, "2022-09-03");
+insert into diaries values(4070, "2022-09-04");
+insert into diaries values(4071, "2022-09-05");
+insert into diaries values(4072, "2022-09-06");
+insert into diaries values(4073, "2022-09-07");
+insert into diaries values(4074, "2022-09-08");
+insert into diaries values(4075, "2022-09-09");
+insert into diaries values(4076, "2022-09-10");
+insert into diaries values(4077, "2022-09-11");
+insert into diaries values(4078, "2022-09-12");
+insert into diaries values(4079, "2022-09-13");
+insert into diaries values(4080, "2022-09-14");
+insert into diaries values(4081, "2022-09-15");
+insert into diaries values(4082, "2022-09-16");
+insert into diaries values(4083, "2022-09-17");
+insert into diaries values(4084, "2022-09-18");
+insert into diaries values(4085, "2022-09-19");
+insert into diaries values(4086, "2022-09-20");
+insert into diaries values(4087, "2022-09-21");
+insert into diaries values(4088, "2022-09-22");
+insert into diaries values(4089, "2022-09-23");
+insert into diaries values(4090, "2022-09-24");
+insert into diaries values(4091, "2022-09-25");
+insert into diaries values(4092, "2022-09-26");
+insert into diaries values(4093, "2022-09-27");
+insert into diaries values(4094, "2022-09-28");
+insert into diaries values(4095, "2022-09-29");
+commit;
+set autocommit=1;
+
+select * from diaries where match(title) against("2022-09-0");
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_delete.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_delete.test
new file mode 100644
index 00000000000..61aa07d8da2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_delete.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set names utf8;
+create table diaries (
+ id int primary key,
+ title varchar(255),
+ content text,
+ fulltext index (title, content),
+ fulltext index (title),
+ fulltext index (content)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+delete from diaries where id = 2;
+select * from diaries where match(title, content) against("富士山");
+select * from diaries where match(title) against("富士山");
+select * from diaries where match(content) against("富士山");
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_insert.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_insert.test
new file mode 100644
index 00000000000..8ddfa82e84a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_insert.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set names utf8;
+create table diaries (
+ id int primary key,
+ title varchar(255),
+ content text,
+ fulltext index (title, content),
+ fulltext index (title),
+ fulltext index (content)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+select * from diaries;
+select * from diaries where match(title, content) against("富士山");
+select * from diaries where match(title) against("富士山");
+select * from diaries where match(content) against("富士山");
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_recreate.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_recreate.test
new file mode 100644
index 00000000000..1e2413689d6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_recreate.test
@@ -0,0 +1,51 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set names utf8;
+create table diaries (
+ id int primary key,
+ title varchar(255),
+ content text,
+ fulltext index (title, content),
+ fulltext index (title),
+ fulltext index (content)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+select * from diaries where match(title, content) against("富士山");
+
+drop index title on diaries;
+--error ER_FT_MATCHING_KEY_NOT_FOUND
+select * from diaries where match(title, content) against("富士山");
+
+create fulltext index new_title_content_index on diaries (title, content);
+select * from diaries where match(title, content) against("富士山");
+
+select * from diaries;
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_update.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_update.test
new file mode 100644
index 00000000000..44690e699a0
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_column_index_update.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set names utf8;
+create table diaries (
+ id int primary key,
+ title varchar(255),
+ content text,
+ fulltext index (title, content),
+ fulltext index (title),
+ fulltext index (content)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+insert into diaries values(1, "Hello", "はじめました。");
+insert into diaries values(2, "天気", "明日の富士山の天気について");
+insert into diaries values(3, "富士山", "今日もきれい。");
+update diaries set title = "チョモランマ" where id = 3;
+update diaries set content = "チョモランマと富士山" where id = 1;
+select * from diaries where match(title, content) against("富士山");
+select * from diaries where match(title) against("富士山");
+select * from diaries where match(content) against("富士山");
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_index.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_index.test
new file mode 100644
index 00000000000..97a8a1f07a5
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_multiple_index.test
@@ -0,0 +1,48 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+create table diaries (
+ id int primary key auto_increment,
+ title text,
+ body text,
+ fulltext index title_index (title),
+ fulltext index body_index (body)
+) comment = 'engine "innodb"' default charset utf8;
+show create table diaries;
+
+insert into diaries (title, body) values ("survey", "will start groonga!");
+insert into diaries (title, body) values ("groonga (1)", "starting groonga...");
+insert into diaries (title, body) values ("groonga (2)", "started groonga.");
+
+select * from diaries
+ where match(title) against("survey") and
+ match(body) against("groonga");
+
+select *, match(title) against("survey"), match(body) against("groonga")
+ from diaries
+ where match(title) against("survey") and
+ match(body) against("groonga");
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_myisam.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_myisam.test
new file mode 100644
index 00000000000..eda8a5f15ef
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_myisam.test
@@ -0,0 +1,102 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 text, fulltext index ft (c2)) COMMENT = 'engine "myisam"';
+show create table t1;
+insert into t1 values (1, "hoge hoge");
+insert into t1 values (2, "fuga fuga");
+insert into t1 values (3, "moge moge");
+select * from t1;
+flush tables;
+select * from t1;
+drop table t1;
+
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3)) COMMENT = 'engine "myisam"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"sa si su se so");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ee oo");
+select * from t1;
+select * from t1 where match(c3) against("su");
+select * from t1 where match(c3) against("ii");
+select * from t1 where match(c3) against("+su" in boolean mode);
+select * from t1 where match(c3) against("+ii" in boolean mode);
+drop table t1;
+
+set names utf8;
+create table t1 (c1 int primary key, c2 varchar(255), c3 text, fulltext index(c2), fulltext index(c3)) default charset utf8 COMMENT = 'engine "myisam"';
+insert into t1 values(1, "明日の富士山の天気について","あああああああ");
+insert into t1 values(2, "いいいいい","明日の富士山の天気は分かりません");
+insert into t1 values(3, "dummy", "dummy");
+select * from t1;
+select * from t1 where match(c2) against("富士山");
+select * from t1 where match(c3) against("富士山");
+drop table t1;
+
+create table t1 (c1 int primary key, c2 varchar(100), fulltext index(c2)) default charset utf8 COMMENT = 'engine "myisam"';
+create table t2 (c1 int primary key, c2 text, fulltext index(c2)) default charset utf8 COMMENT = 'engine "myisam"';
+insert into t1 values (1, "aa ii uu ee oo");
+insert into t1 values (2, "ka ki ku ke ko");
+insert into t1 values (3, "aa ii ii ii oo");
+insert into t1 values (4, "sa si su se so");
+insert into t1 values (5, "ta ti ii ii to");
+insert into t2 (c1,c2) select c1,c2 from t1;
+select * from t1;
+select * from t2;
+select * from t1 where c1=3;
+select * from t2 where c1=3;
+select * from t1 where c1>3 order by c1 desc;
+select * from t2 where c1>3 order by c1 asc;
+select * from t1 where c2>"s" order by c2 desc;
+select * from t2 where c2>"s" order by c1 asc;
+select *,match(c2) against("ii") from t1 where match(c2) against("ii") order by match(c2) against("ii") desc;
+select *,match(c2) against("ii") from t2 where match(c2) against("ii") order by match(c2) against("ii") asc;
+select c1,c2,match(c2) against("ii") from t1 where match(c2) against("ii");
+select c1,c2,match(c2) against("ii") from t1 where match(c2) against("ii");
+drop table t1,t2;
+
+# for "not match against"
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3)) COMMENT = 'engine "myisam"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,10,"ka ki ku ke ko");
+insert into t1 values(3,10,"aa ii uu ee oo");
+insert into t1 values(4,10,"ka ki ku ke ko");
+insert into t1 values(5,20,"aa ii uu ee oo");
+insert into t1 values(6,20,"ka ki ku ke ko");
+insert into t1 values(7,20,"aa ii uu ee oo");
+insert into t1 values(8,20,"ka ki ku ke ko");
+select * from t1;
+select *,match(c3) against("uu") from t1 where match(c3) against("uu");
+select * from t1 where not match(c3) against("uu");
+select *,match(c3) against("dummy") from t1 where match(c3) against("dummy");
+select * from t1 where not match(c3) against("dummy");
+select * from t1 where c1 = 4 and not match(c3) against("uu");
+select * from t1 where c1 <= 4 and not match(c3) against("uu");
+select * from t1 where c1 > 4 and not match(c3) against("uu");
+select * from t1 where c2 = 10 and not match(c3) against("uu");
+select * from t1 where c2 >= 15 and not match(c3) against("uu");
+select * from t1 where c2 < 15 and not match(c3) against("uu");
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_not_match_against.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_not_match_against.test
new file mode 100644
index 00000000000..6ce01598136
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_not_match_against.test
@@ -0,0 +1,47 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 int, c3 text, fulltext index ft(c3)) COMMENT = 'engine "innodb"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,10,"ka ki ku ke ko");
+insert into t1 values(3,10,"aa ii uu ee oo");
+insert into t1 values(4,10,"ka ki ku ke ko");
+insert into t1 values(5,20,"aa ii uu ee oo");
+insert into t1 values(6,20,"ka ki ku ke ko");
+insert into t1 values(7,20,"aa ii uu ee oo");
+insert into t1 values(8,20,"ka ki ku ke ko");
+select * from t1;
+select *,match(c3) against("uu") from t1 where match(c3) against("uu");
+select * from t1 where not match(c3) against("uu");
+select *,match(c3) against("dummy") from t1 where match(c3) against("dummy");
+select * from t1 where not match(c3) against("dummy");
+select * from t1 where c1 = 4 and not match(c3) against("uu");
+select * from t1 where c1 <= 4 and not match(c3) against("uu");
+select * from t1 where c1 > 4 and not match(c3) against("uu");
+select * from t1 where c2 = 10 and not match(c3) against("uu");
+select * from t1 where c2 >= 15 and not match(c3) against("uu");
+select * from t1 where c2 < 15 and not match(c3) against("uu");
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_order_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_order_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..5361b151661
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_order_TODO_SPLIT_ME.test
@@ -0,0 +1,51 @@
+# Copyright(C) 2012 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE ft(
+ a INT,
+ b TEXT,
+ c TEXT,
+ PRIMARY KEY(a),
+ FULLTEXT KEY ftx1(b),
+ FULLTEXT KEY ftx2(c)
+)ENGINE=Mroonga DEFAULT CHARSET=UTF8 COMMENT = 'engine "innodb"';
+SHOW CREATE TABLE ft;
+
+INSERT INTO ft VALUES(1,'aaaaa','abcde');
+INSERT INTO ft VALUES(2,'bbbbb','bcdef');
+INSERT INTO ft VALUES(3,'ccccc','cdefg');
+INSERT INTO ft VALUES(4,'ddddd','defgh');
+INSERT INTO ft VALUES(5,'eeeee','efghi');
+
+SELECT a, b, c FROM ft WHERE MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE);
+SELECT a, b, c FROM ft WHERE MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE) ORDER BY MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE);
+SELECT a, b, c FROM ft WHERE MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE) ORDER BY MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE);
+SELECT a, b, c FROM ft WHERE MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE);
+SELECT a, b, c, MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE), MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE) FROM ft WHERE MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE) ORDER BY MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE);
+SELECT a, b, c, MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE), MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE) FROM ft WHERE MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE);
+SELECT a, b, c, MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE), MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE) FROM ft ORDER BY MATCH(c) AGAINST('bbbbb' IN BOOLEAN MODE), MATCH(b) AGAINST('bbbbb' IN BOOLEAN MODE), a;
+
+DROP TABLE ft;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_order_transaction.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_order_transaction.test
new file mode 100644
index 00000000000..91e2f73da1d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_order_transaction.test
@@ -0,0 +1,64 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT,
+ FULLTEXT INDEX title_index (title),
+ FULLTEXT INDEX body_index (body)
+) COMMENT = 'ENGINE "InnoDB"' DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE diaries;
+
+START TRANSACTION;
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+
+SELECT * FROM diaries
+ WHERE MATCH(body) AGAINST("groonga")
+ ORDER BY id;
+
+CONNECT(search_connection, localhost, root);
+USE test;
+SELECT * FROM diaries
+ WHERE MATCH(body) AGAINST("groonga")
+ ORDER BY id;
+
+CONNECTION default;
+COMMIT;
+
+CONNECTION search_connection;
+SELECT * FROM diaries
+ WHERE MATCH(body) AGAINST("groonga")
+ ORDER BY id;
+DISCONNECT search_connection;
+
+CONNECTION default;
+SELECT * FROM diaries
+ WHERE MATCH(body) AGAINST("groonga")
+ ORDER BY id;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_parser_comment.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_parser_comment.test
new file mode 100644
index 00000000000..fe80aae0804
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/fulltext_parser_comment.test
@@ -0,0 +1,39 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_fulltext_index_comment.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+create table diaries (
+ id int primary key auto_increment,
+ body text,
+ fulltext index body_index (body)
+ comment 'parser "TokenBigramSplitSymbolAlphaDigit"'
+) comment = 'engine "innodb"' default charset utf8;
+show create table diaries;
+insert into diaries (body) values ("will start groonga!");
+insert into diaries (body) values ("starting groonga...");
+insert into diaries (body) values ("started groonga.");
+select * from diaries;
+select * from diaries where match(body) against("start");
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/function_last_insert_grn_id.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/function_last_insert_grn_id.test
new file mode 100644
index 00000000000..c4961756a26
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/function_last_insert_grn_id.test
@@ -0,0 +1,52 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+DROP FUNCTION IF EXISTS last_insert_grn_id;
+--enable_warnings
+
+--disable_query_log
+if ($VERSION_COMPILE_OS_WIN)
+{
+ CREATE FUNCTION last_insert_grn_id RETURNS integer SONAME 'ha_mroonga.dll';
+}
+if (!$VERSION_COMPILE_OS_WIN)
+{
+ CREATE FUNCTION last_insert_grn_id RETURNS integer SONAME 'ha_mroonga.so';
+}
+--enable_query_log
+
+CREATE TABLE ids (
+ id int AUTO_INCREMENT PRIMARY KEY
+) COMMENT='ENGINE "InnoDB"';
+
+SELECT last_insert_grn_id();
+
+INSERT INTO ids VALUES();
+SELECT last_insert_grn_id();
+SELECT * FROM ids;
+
+DROP TABLE ids;
+
+DROP FUNCTION last_insert_grn_id;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/function_last_insert_id_reference.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/function_last_insert_id_reference.test
new file mode 100644
index 00000000000..1b7c3a7f265
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/function_last_insert_id_reference.test
@@ -0,0 +1,36 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id int AUTO_INCREMENT PRIMARY KEY
+) COMMENT='ENGINE "InnoDB"';
+
+SELECT last_insert_id();
+
+INSERT INTO ids VALUES();
+SELECT last_insert_id();
+SELECT * FROM ids;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/function_last_insert_id_set.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/function_last_insert_id_set.test
new file mode 100644
index 00000000000..6dc87f740f9
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/function_last_insert_id_set.test
@@ -0,0 +1,38 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id int AUTO_INCREMENT PRIMARY KEY
+) COMMENT='ENGINE "InnoDB"';
+
+SELECT last_insert_id();
+SELECT last_insert_id(10);
+SELECT last_insert_id();
+
+INSERT INTO ids VALUES();
+SELECT last_insert_id();
+SELECT * FROM ids;
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/geometry_contains.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/geometry_contains.test
new file mode 100644
index 00000000000..4676fc61331
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/geometry_contains.test
@@ -0,0 +1,145 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/have_geometry.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists shops;
+--enable_warnings
+
+create table shops (
+ id int primary key auto_increment,
+ name text,
+ location geometry NOT NULL,
+ spatial key location_index (location)
+) comment = 'engine "innodb"';
+show create table shops;
+insert into shops (name, location)
+ values ('nezu-no-taiyaki',
+ GeomFromText('POINT(139.762573 35.720253)'));
+insert into shops (name, location)
+ values ('taiyaki-kataoka',
+ GeomFromText('POINT(139.715591 35.712521)'));
+insert into shops (name, location)
+ values ('soba-taiyaki-ku',
+ GeomFromText('POINT(139.659088 35.683712)'));
+insert into shops (name, location)
+ values ('kuruma',
+ GeomFromText('POINT(139.706207 35.721516)'));
+insert into shops (name, location)
+ values ('hirose-ya',
+ GeomFromText('POINT(139.685608 35.714844)'));
+insert into shops (name, location)
+ values ('sazare',
+ GeomFromText('POINT(139.685043 35.714653)'));
+insert into shops (name, location)
+ values ('omede-taiyaki',
+ GeomFromText('POINT(139.817154 35.700516)'));
+insert into shops (name, location)
+ values ('onaga-ya',
+ GeomFromText('POINT(139.81105 35.698254)'));
+insert into shops (name, location)
+ values ('shiro-ya',
+ GeomFromText('POINT(139.638611 35.705517)'));
+insert into shops (name, location)
+ values ('fuji-ya',
+ GeomFromText('POINT(139.637115 35.703938)'));
+insert into shops (name, location)
+ values ('miyoshi',
+ GeomFromText('POINT(139.537323 35.644539)'));
+insert into shops (name, location)
+ values ('juju-ya',
+ GeomFromText('POINT(139.695755 35.628922)'));
+insert into shops (name, location)
+ values ('tatsumi-ya',
+ GeomFromText('POINT(139.638657 35.665501)'));
+insert into shops (name, location)
+ values ('tetsuji',
+ GeomFromText('POINT(139.76857 35.680912)'));
+insert into shops (name, location)
+ values ('gazuma-ya',
+ GeomFromText('POINT(139.647598 35.700817)'));
+insert into shops (name, location)
+ values ('honma-mon',
+ GeomFromText('POINT(139.652573 35.722736)'));
+insert into shops (name, location)
+ values ('naniwa-ya',
+ GeomFromText('POINT(139.796234 35.730061)'));
+insert into shops (name, location)
+ values ('kuro-dai',
+ GeomFromText('POINT(139.704834 35.650345)'));
+insert into shops (name, location)
+ values ('daruma',
+ GeomFromText('POINT(139.770599 35.681461)'));
+insert into shops (name, location)
+ values ('yanagi-ya',
+ GeomFromText('POINT(139.783981 35.685341)'));
+insert into shops (name, location)
+ values ('sharaku',
+ GeomFromText('POINT(139.794846 35.716969)'));
+insert into shops (name, location)
+ values ('takane',
+ GeomFromText('POINT(139.560913 35.698601)'));
+insert into shops (name, location)
+ values ('chiyoda',
+ GeomFromText('POINT(139.652817 35.642601)'));
+insert into shops (name, location)
+ values ('da-ka-po',
+ GeomFromText('POINT(139.727356 35.627346)'));
+insert into shops (name, location)
+ values ('matsushima-ya',
+ GeomFromText('POINT(139.737381 35.640556)'));
+insert into shops (name, location)
+ values ('kazuya',
+ GeomFromText('POINT(139.760895 35.673508)'));
+insert into shops (name, location)
+ values ('furuya-kogane-an',
+ GeomFromText('POINT(139.676071 35.680603)'));
+insert into shops (name, location)
+ values ('hachi-no-ie',
+ GeomFromText('POINT(139.668106 35.608021)'));
+insert into shops (name, location)
+ values ('azuki-chan',
+ GeomFromText('POINT(139.673203 35.64151)'));
+insert into shops (name, location)
+ values ('kuriko-an',
+ GeomFromText('POINT(139.796829 35.712013)'));
+insert into shops (name, location)
+ values ('yume-no-aru-machi-no-taiyaki-ya-san',
+ GeomFromText('POINT(139.712524 35.616199)'));
+insert into shops (name, location)
+ values ('naze-ya',
+ GeomFromText('POINT(139.665833 35.609039)'));
+insert into shops (name, location)
+ values ('sanoki-ya',
+ GeomFromText('POINT(139.770721 35.66592)'));
+insert into shops (name, location)
+ values ('shigeta',
+ GeomFromText('POINT(139.780273 35.672626)'));
+insert into shops (name, location)
+ values ('nishimi-ya',
+ GeomFromText('POINT(139.774628 35.671825)'));
+insert into shops (name, location)
+ values ('hiiragi',
+ GeomFromText('POINT(139.711517 35.647701)'));
+select id, name, AsText(location) as location_text from shops;
+select id, name, AsText(location) as location_text from shops
+ where MBRContains(GeomFromText('LineString(139.7727 35.6684, 139.7038 35.7121)'), location);
+drop table shops;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/geometry_delete.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/geometry_delete.test
new file mode 100644
index 00000000000..25814635b22
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/geometry_delete.test
@@ -0,0 +1,48 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/have_geometry.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists shops;
+--enable_warnings
+
+create table shops (
+ id int primary key auto_increment,
+ name text,
+ location geometry NOT NULL,
+ spatial key location_index (location)
+) comment = 'engine "innodb"';
+show create table shops;
+
+insert into shops (name, location)
+ values ('sazare',
+ GeomFromText('POINT(139.685043 35.714653)'));
+select id, name, AsText(location) as location_text from shops
+ where MBRContains(GeomFromText('LineString(139.68466 35.71592, 139.68804 35.71411)'), location);
+
+delete from shops
+ where MBRContains(GeomFromText('LineString(139.68466 35.71592, 139.68804 35.71411)'), location);
+select id, name, AsText(location) as location_text from shops
+ where MBRContains(GeomFromText('LineString(139.68466 35.71592, 139.68804 35.71411)'), location);
+
+select id, name, AsText(location) as location_text from shops;
+
+drop table shops;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/geometry_update.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/geometry_update.test
new file mode 100644
index 00000000000..c6bb8a1d4e6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/geometry_update.test
@@ -0,0 +1,50 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/have_geometry.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists shops;
+--enable_warnings
+
+create table shops (
+ id int primary key auto_increment,
+ name text,
+ location geometry NOT NULL,
+ spatial key location_index (location)
+) comment = 'engine "innodb"';
+show create table shops;
+
+insert into shops (name, location)
+ values ('sazare',
+ GeomFromText('POINT(139.685043 35.714653)'));
+select id, name, AsText(location) as location_text from shops
+ where MBRContains(GeomFromText('LineString(139.68466 35.71592, 139.68804 35.71411)'), location);
+select id, name, AsText(location) as location_text from shops
+ where MBRContains(GeomFromText('LineString(139.65659 35.57903, 139.66489 35.57262)'), location);
+
+update shops set location = GeomFromText('POINT(139.66116 35.57566)')
+ where name = 'sazare';
+select id, name, AsText(location) as location_text from shops
+ where MBRContains(GeomFromText('LineString(139.68466 35.71592, 139.68804 35.71411)'), location);
+select id, name, AsText(location) as location_text from shops
+ where MBRContains(GeomFromText('LineString(139.65659 35.57903, 139.66489 35.57262)'), location);
+
+drop table shops;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/index_force_index_not_used.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/index_force_index_not_used.test
new file mode 100644
index 00000000000..037f8b02fbf
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/index_force_index_not_used.test
@@ -0,0 +1,32 @@
+# Copyright(C) 2013 Kentoku SHIBA
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS ids;
+--enable_warnings
+
+CREATE TABLE ids (
+ id INT PRIMARY KEY AUTO_INCREMENT
+) DEFAULT CHARSET UTF8 COMMENT = 'engine "InnoDB"';
+SELECT COUNT(*) FROM ids FORCE INDEX(PRIMARY);
+
+DROP TABLE ids;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/insert_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/insert_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..e619b0af930
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/insert_TODO_SPLIT_ME.test
@@ -0,0 +1,91 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2011 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+# data types
+create table t1 (c1 tinyint primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(1);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 smallint primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(1);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 mediumint primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(1);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 int primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(1);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 bigint primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(1);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 float primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(0.5);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 double primary key) COMMENT = 'engine "innodb"';
+insert into t1 values(0.5);
+select * from t1;
+drop table t1;
+
+create table t1 (c1 date primary key) COMMENT = 'engine "innodb"';
+insert into t1 values("2010/03/26");
+select * from t1;
+drop table t1;
+
+create table t1 (c1 time primary key) COMMENT = 'engine "innodb"';
+insert into t1 values("11:22:33");
+select * from t1;
+drop table t1;
+
+create table t1 (c1 year primary key) COMMENT = 'engine "innodb"';
+insert into t1 values("2010");
+select * from t1;
+drop table t1;
+
+create table t1 (c1 datetime primary key) COMMENT = 'engine "innodb"';
+insert into t1 values("2010/03/26 11:22:33");
+select * from t1;
+drop table t1;
+
+
+# duplicated key error
+create table t1 (c1 int primary key, c2 int) COMMENT = 'engine "innodb"';
+insert into t1 values(1,100);
+select * from t1;
+--error ER_DUP_ENTRY
+insert into t1 values(1,200);
+select * from t1;
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/insert_bulk.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/insert_bulk.test
new file mode 100644
index 00000000000..4087abf9bc3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/insert_bulk.test
@@ -0,0 +1,44 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+set names utf8;
+create table diaries (
+ id int primary key,
+ content text,
+ fulltext index (content)
+) default charset utf8 comment = 'engine "innodb"';
+show create table diaries;
+
+LOCK TABLE diaries WRITE;
+insert into diaries values(1, "今日からはじめました。");
+insert into diaries values(2, "明日の富士山の天気について");
+insert into diaries values(3, "今日も天気がよくてきれいに見える。");
+UNLOCK TABLES;
+
+select * from diaries;
+
+select * from diaries where match(content) against("天気");
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/insert_on_duplicate_key_update_multiple_column_primary_key_myisam.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/insert_on_duplicate_key_update_multiple_column_primary_key_myisam.test
new file mode 100644
index 00000000000..cc3bc477409
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/insert_on_duplicate_key_update_multiple_column_primary_key_myisam.test
@@ -0,0 +1,49 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ date TIMESTAMP NOT NULL,
+ title VARCHAR(100) NOT NULL,
+ content TEXT NOT NULL,
+ PRIMARY KEY (date, title)
+) DEFAULT CHARSET=UTF8 COMMENT='ENGINE "MyISAM"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (date, title, content)
+ VALUES ("2012-03-04", "cloudy day", "Today is cloudy day...");
+INSERT INTO diaries (date, title, content)
+ VALUES ("2012-03-04", "shopping", "I buy a new shirt.");
+INSERT INTO diaries (date, title, content)
+ VALUES ("2012-03-05", "rainy day", "Today is rainy day...");
+
+SELECT * FROM diaries;
+
+INSERT INTO diaries (date, title, content)
+ VALUES ("2012-03-04", "shopping", "I buy new shoes.")
+ ON DUPLICATE KEY UPDATE date = "2012-03-03",
+ content = "I buy a new hat.";
+
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/insert_on_duplicate_key_update_multiple_column_unique_index_myisam.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/insert_on_duplicate_key_update_multiple_column_unique_index_myisam.test
new file mode 100644
index 00000000000..cdcdcc796f3
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/insert_on_duplicate_key_update_multiple_column_unique_index_myisam.test
@@ -0,0 +1,49 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ date TIMESTAMP NOT NULL,
+ title VARCHAR(100) NOT NULL,
+ content TEXT NOT NULL,
+ UNIQUE INDEX (date, title)
+) DEFAULT CHARSET=UTF8 COMMENT='ENGINE "MyISAM"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (date, title, content)
+ VALUES ("2012-03-04", "cloudy day", "Today is cloudy day...");
+INSERT INTO diaries (date, title, content)
+ VALUES ("2012-03-04", "shopping", "I buy a new shirt.");
+INSERT INTO diaries (date, title, content)
+ VALUES ("2012-03-05", "rainy day", "Today is rainy day...");
+
+SELECT * FROM diaries;
+
+INSERT INTO diaries (date, title, content)
+ VALUES ("2012-03-04", "shopping", "I buy new shoes.")
+ ON DUPLICATE KEY UPDATE date = "2012-03-03",
+ content = "I buy a new hat.";
+SELECT * FROM diaries;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/multi_range_read_disk_sweep.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/multi_range_read_disk_sweep.test
new file mode 100644
index 00000000000..f5b429e523b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/multi_range_read_disk_sweep.test
@@ -0,0 +1,45 @@
+# Copyright(C) 2013 Kenji Maruyama <mmmaru777@gmail.com>
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_mysql.inc
+--source ../../include/mroonga/have_version_56_or_later.inc
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS integers;
+--enable_warnings
+
+SET optimizer_switch='mrr_cost_based=off';
+
+CREATE TABLE integers (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ value INT,
+ KEY (value)
+) COMMENT='engine "InnoDB"';
+
+INSERT INTO integers (value) VALUES (0), (1), (2), (3);
+
+EXPLAIN SELECT * FROM integers
+ WHERE value IN (0, 2);
+
+SELECT * FROM integers
+ WHERE value IN (0, 2);
+
+DROP TABLE integers;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/optimization_order_limit_TODO_SPLIT_ME.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/optimization_order_limit_TODO_SPLIT_ME.test
new file mode 100644
index 00000000000..39772e5d78d
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/optimization_order_limit_TODO_SPLIT_ME.test
@@ -0,0 +1,68 @@
+# Copyright(C) 2010 Kentoku SHIBA
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+flush status;
+create table t1 (
+ c1 int primary key,
+ c2 int,
+ c3 text,
+ key idx1(c2),
+ fulltext index ft(c3)
+) comment = 'engine "innodb"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"ii si ii se ii");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ii oo");
+
+select *, match(c3) against("ii") from t1
+ where match(c3) against("ii") order by c1 desc limit 1;
+show status like 'mroonga_fast_order_limit';
+
+select *, match(c3) against("ii") from t1
+ where match(c3) against("ii") order by c1 limit 1;
+show status like 'mroonga_fast_order_limit';
+
+select c3, match(c3) against("ii") from t1
+ where match(c3) against("ii") order by match(c3) against("ii") desc;
+show status like 'mroonga_fast_order_limit';
+
+select c3, match(c3) against("ii") from t1
+ where match(c3) against("ii") order by match(c3) against("ii") desc limit 1, 1;
+show status like 'mroonga_fast_order_limit';
+
+select c3, match(c3) against("ii") from t1
+ where match(c3) against("ii") order by match(c3) against("ii");
+show status like 'mroonga_fast_order_limit';
+
+select c3, match(c3) against("ii") from t1
+ where match(c3) against("ii") order by match(c3) against("ii") limit 1;
+show status like 'mroonga_fast_order_limit';
+
+select count(*) from t1 where match(c3) against("ii") limit 1;
+show status like 'mroonga_fast_order_limit';
+
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/optimization_order_limit_no_where_clause.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/optimization_order_limit_no_where_clause.test
new file mode 100644
index 00000000000..da37f5d6372
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/optimization_order_limit_no_where_clause.test
@@ -0,0 +1,46 @@
+# Copyright(C) 2013 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+flush status;
+create table t1 (
+ c1 int primary key,
+ c2 int,
+ c3 text,
+ key idx1(c2),
+ fulltext index ft(c3)
+) comment = 'engine "innodb"';
+insert into t1 values(1,10,"aa ii uu ee oo");
+insert into t1 values(2,20,"ka ki ku ke ko");
+insert into t1 values(3,30,"ii si ii se ii");
+insert into t1 values(4,40,"ta ti tu te to");
+insert into t1 values(5,50,"aa ii uu ii oo");
+
+show status like 'mroonga_fast_order_limit';
+
+select *, match(c3) against("ii") from t1 order by c1 desc limit 1;
+
+show status like 'mroonga_fast_order_limit';
+
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/repair_table_no_files.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/repair_table_no_files.test
new file mode 100644
index 00000000000..6489407135b
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/repair_table_no_files.test
@@ -0,0 +1,69 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/have_mroonga_helper.inc
+
+--disable_warnings
+DROP DATABASE IF EXISTS repair_test;
+CREATE DATABASE repair_test;
+USE repair_test;
+
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT,
+ FULLTEXT INDEX body_index (body)
+) COMMENT = 'engine "innodb"' DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+
+SELECT * FROM diaries WHERE MATCH(body) AGAINST("starting");
+
+--remove_file $MYSQLD_DATADIR/repair_test.mrn
+--remove_file $MYSQLD_DATADIR/repair_test.mrn.001
+--remove_file $MYSQLD_DATADIR/repair_test.mrn.0000000
+--remove_file $MYSQLD_DATADIR/repair_test.mrn.0000103
+--remove_file $MYSQLD_DATADIR/repair_test.mrn.0000104
+--remove_file $MYSQLD_DATADIR/repair_test.mrn.0000105
+--remove_file $MYSQLD_DATADIR/repair_test.mrn.0000105.c
+
+FLUSH TABLES;
+
+# Error ER_CANT_OPEN_FILE syscall error 'repair_test.mrn' (No such file or directory)
+--error ER_CANT_OPEN_FILE
+SELECT * FROM diaries WHERE MATCH(body) AGAINST("starting");
+
+REPAIR TABLE diaries;
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries WHERE MATCH(body) AGAINST("starting");
+
+DROP TABLE diaries;
+
+DROP DATABASE repair_test;
+USE test;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/repair_table_no_index_file.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/repair_table_no_index_file.test
new file mode 100644
index 00000000000..ce6e1d23280
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/repair_table_no_index_file.test
@@ -0,0 +1,63 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+--source ../../include/mroonga/have_mroonga_helper.inc
+
+--disable_warnings
+DROP DATABASE IF EXISTS repair_test;
+CREATE DATABASE repair_test;
+USE repair_test;
+
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT,
+ FULLTEXT INDEX body_index (body)
+) COMMENT = 'engine "innodb"' DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+
+SELECT * FROM diaries WHERE MATCH(body) AGAINST("starting");
+
+--remove_file $MYSQLD_DATADIR/repair_test.mrn.0000104
+
+FLUSH TABLES;
+
+# Error ER_CANT_OPEN_FILE syscall error 'repair_test.mrn.0000104' (No such file or directory)
+--error ER_CANT_OPEN_FILE
+SELECT * FROM diaries WHERE MATCH(body) AGAINST("starting");
+
+REPAIR TABLE diaries;
+
+SELECT * FROM diaries;
+
+SELECT * FROM diaries WHERE MATCH(body) AGAINST("starting");
+
+DROP TABLE diaries;
+
+DROP DATABASE repair_test;
+USE test;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/temporary_table.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/temporary_table.test
new file mode 100644
index 00000000000..0eb5ef514a2
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/temporary_table.test
@@ -0,0 +1,40 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 Toshihisa Tashiro
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/skip_osx.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TEMPORARY TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TEMPORARY TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT
+) DEFAULT CHARSET=UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title) VALUES ("clear day");
+INSERT INTO diaries (title) VALUES ("rainy day");
+INSERT INTO diaries (title) VALUES ("cloudy day");
+
+SELECT * FROM diaries;
+
+DROP TEMPORARY TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/transaction_query_cache.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/transaction_query_cache.test
new file mode 100644
index 00000000000..b2522db7919
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/transaction_query_cache.test
@@ -0,0 +1,53 @@
+# Copyright(C) 2012 Kentoku SHIBA
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+SET @tmp_query_cache_size = @@query_cache_size;
+SET GLOBAL query_cache_size = 1048576;
+
+--disable_warnings
+DROP TABLE IF EXISTS simple_table;
+--enable_warnings
+
+CREATE TABLE simple_table (
+ id INT PRIMARY KEY
+) COMMENT = 'ENGINE "InnoDB"' DEFAULT CHARSET=UTF8;
+SHOW CREATE TABLE simple_table;
+
+INSERT INTO simple_table (id) VALUES (1),(2);
+
+CONNECT(second_connection, localhost, root);
+USE test;
+START TRANSACTION;
+INSERT INTO simple_table (id) VALUES (3);
+
+CONNECTION default;
+SELECT * FROM simple_table;
+
+CONNECTION second_connection;
+COMMIT;
+
+CONNECTION default;
+SELECT * FROM simple_table;
+
+DROP TABLE simple_table;
+DISCONNECT second_connection;
+
+SET GLOBAL query_cache_size = @tmp_query_cache_size;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/transaction_rollback_delete_delete.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/transaction_rollback_delete_delete.test
new file mode 100644
index 00000000000..f0da6934f8e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/transaction_rollback_delete_delete.test
@@ -0,0 +1,59 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT,
+ FULLTEXT INDEX title_index (title),
+ FULLTEXT INDEX body_index (body)
+) COMMENT = 'ENGINE "InnoDB"' DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("survey") AND
+ MATCH(body) AGAINST("groonga");
+
+START TRANSACTION;
+DELETE FROM diaries WHERE id = 1;
+ROLLBACK;
+
+SELECT * FROM diaries;
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("survey") AND
+ MATCH(body) AGAINST("groonga");
+
+DELETE FROM diaries WHERE id = 1;
+
+SELECT * FROM diaries;
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("survey") AND
+ MATCH(body) AGAINST("groonga");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/transaction_rollback_delete_update.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/transaction_rollback_delete_update.test
new file mode 100644
index 00000000000..425a16c204e
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/transaction_rollback_delete_update.test
@@ -0,0 +1,56 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ body TEXT,
+ FULLTEXT INDEX title_index (title),
+ FULLTEXT INDEX body_index (body)
+) COMMENT = 'ENGINE "InnoDB"' DEFAULT CHARSET UTF8;
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, body) VALUES ("survey", "will start groonga!");
+INSERT INTO diaries (title, body) VALUES ("groonga (1)", "starting groonga...");
+INSERT INTO diaries (title, body) VALUES ("groonga (2)", "started groonga.");
+
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("survey");
+
+START TRANSACTION;
+DELETE FROM diaries WHERE id = 1;
+ROLLBACK;
+
+SELECT * FROM diaries;
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("survey");
+
+UPDATE diaries SET title = "survey day!" WHERE id = 1;
+
+SELECT * FROM diaries;
+SELECT * FROM diaries
+ WHERE MATCH(title) AGAINST("survey");
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/truncate.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/truncate.test
new file mode 100644
index 00000000000..987fd284f74
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/truncate.test
@@ -0,0 +1,57 @@
+# Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SET NAMES UTF8;
+CREATE TABLE diaries (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ year INT UNSIGNED,
+ month INT UNSIGNED,
+ day INT UNSIGNED,
+ title VARCHAR(255),
+ content TEXT,
+ FULLTEXT INDEX(content),
+ KEY(day)
+) DEFAULT CHARSET UTF8 COMMENT = 'engine "innodb"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries VALUES(1, 2011, 11, 9, "Hello", "今日からはじめました。");
+INSERT INTO diaries VALUES(2, 2011, 11, 10, "天気", "明日の富士山の天気について");
+INSERT INTO diaries VALUES(3, 2011, 11, 11, "富士山", "今日も天気がよくてきれいに見える。");
+
+SELECT * FROM diaries;
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE);
+TRUNCATE TABLE diaries;
+SELECT * FROM diaries;
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("今日 天気" IN BOOLEAN MODE);
+
+INSERT INTO diaries VALUES(1, 2011, 11, 11, "帰り道", "つかれたー");
+INSERT INTO diaries VALUES(2, 2011, 12, 1, "久しぶり", "天気が悪いからずっと留守番。");
+INSERT INTO diaries VALUES(3, 2011, 12, 2, "初雪", "今年はじめての雪!");
+
+SELECT * FROM diaries;
+SELECT * FROM diaries WHERE MATCH(content) AGAINST("悪い" IN BOOLEAN MODE);
+
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/update_fulltext.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/update_fulltext.test
new file mode 100644
index 00000000000..50c14184c98
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/update_fulltext.test
@@ -0,0 +1,43 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 text, fulltext index (c2)) COMMENT = 'engine "innodb"';
+insert into t1 values(10, "aa ii uu ee");
+insert into t1 values(20, "ka ki ku ke");
+insert into t1 values(30, "sa si su se");
+
+select * from t1;
+update t1 set c2="ta ti tu te" where c1=20;
+select * from t1;
+select * from t1 where match(c2) against("ti");
+select * from t1 where match(c2) against("ki");
+
+update t1 set c1=40 where c1=20;
+select * from t1;
+select * from t1 where match(c2) against("ti");
+select * from t1 where match(c2) against("ki");
+
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/update_int.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/update_int.test
new file mode 100644
index 00000000000..5c794aacf65
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/update_int.test
@@ -0,0 +1,41 @@
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3;
+--enable_warnings
+
+create table t1 (c1 int primary key, c2 int) COMMENT = 'engine "innodb"';
+show create table t1;
+insert into t1 values (1, 100);
+insert into t1 values (2, 101);
+insert into t1 values (3, 102);
+select * from t1;
+
+update t1 set c2=c2+100 where c1=1;
+select * from t1;
+update t1 set c2=c2+100 where c1=2;
+select * from t1;
+update t1 set c2=c2+100 where c1=3;
+select * from t1;
+
+drop table t1;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_dry_write_delete.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_dry_write_delete.test
new file mode 100644
index 00000000000..165c8df858a
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_dry_write_delete.test
@@ -0,0 +1,55 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+create table diaries (
+ id int primary key auto_increment,
+ body text,
+ fulltext index body_index (body)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+
+insert into diaries (body) values ("will start groonga!");
+insert into diaries (body) values ("starting groonga...");
+insert into diaries (body) values ("started groonga.");
+select * from diaries;
+
+set mroonga_dry_write=true;
+delete from diaries where id = 2;
+select * from diaries;
+select * from diaries where match (body) against ("starting");
+select * from diaries where match (body) against ("started");
+
+set mroonga_dry_write=false;
+delete from diaries where id = 3;
+select * from diaries;
+select * from diaries where match (body) against ("starting");
+select * from diaries where match (body) against ("started");
+
+insert into diaries (id, body) values (2, "sleeping...");
+select * from diaries;
+select * from diaries where match (body) against ("starting");
+select * from diaries where match (body) against ("started");
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_dry_write_insert.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_dry_write_insert.test
new file mode 100644
index 00000000000..b1222118833
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_dry_write_insert.test
@@ -0,0 +1,47 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+create table diaries (
+ id int primary key auto_increment,
+ body text,
+ fulltext index body_index (body)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+
+insert into diaries (body) values ("will start groonga!");
+select * from diaries;
+select * from diaries where match (body) against ("groonga");
+
+set mroonga_dry_write=true;
+insert into diaries (body) values ("starting groonga...");
+select * from diaries;
+select * from diaries where match (body) against ("groonga");
+
+set mroonga_dry_write=false;
+insert into diaries (body) values ("started groonga.");
+select * from diaries;
+select * from diaries where match (body) against ("groonga");
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_dry_write_update.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_dry_write_update.test
new file mode 100644
index 00000000000..6b47d18b872
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_dry_write_update.test
@@ -0,0 +1,48 @@
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+drop table if exists diaries;
+--enable_warnings
+
+create table diaries (
+ id int primary key auto_increment,
+ body text,
+ fulltext index body_index (body)
+) default charset utf8 COMMENT = 'engine "innodb"';
+show create table diaries;
+
+insert into diaries (body) values ("will start groonga!");
+
+set mroonga_dry_write=true;
+update diaries set body = "starting groonga..." where id = 1;
+select * from diaries;
+select * from diaries where match (body) against ("will");
+select * from diaries where match (body) against ("starting");
+
+set mroonga_dry_write=false;
+update diaries set body = "started groonga." where id = 1;
+select * from diaries;
+select * from diaries where match (body) against ("will");
+select * from diaries where match (body) against ("starting");
+select * from diaries where match (body) against ("started");
+
+drop table diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_match_escalation_threshold_global.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_match_escalation_threshold_global.test
new file mode 100644
index 00000000000..b272649c732
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_match_escalation_threshold_global.test
@@ -0,0 +1,56 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_fulltext_index_comment.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+# MySQL <= 5.5 reports wrong a warning. :<
+# It has been fixed in MySQL >= 5.6 and MariaDB >= 5.3.
+--disable_warnings
+SET GLOBAL mroonga_match_escalation_threshold = -1;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ tags TEXT,
+ FULLTEXT INDEX tags_index (tags) COMMENT 'parser "TokenDelimit"'
+) DEFAULT CHARSET=UTF8 COMMENT='ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, tags) VALUES ("Hello groonga!", "groonga install");
+INSERT INTO diaries (title, tags) VALUES ("Hello mroonga!", "mroonga install");
+
+
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("install" IN BOOLEAN MODE);
+
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+
+SET GLOBAL mroonga_match_escalation_threshold = 0;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+
+SET mroonga_match_escalation_threshold = 0;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_match_escalation_threshold_session.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_match_escalation_threshold_session.test
new file mode 100644
index 00000000000..bf22001bb10
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/variable_match_escalation_threshold_session.test
@@ -0,0 +1,54 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source include/have_innodb.inc
+--source ../../include/mroonga/have_fulltext_index_comment.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ title TEXT,
+ tags TEXT,
+ FULLTEXT INDEX tags_index (tags) COMMENT 'parser "TokenDelimit"'
+) DEFAULT CHARSET=UTF8 COMMENT='ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (title, tags) VALUES ("Hello groonga!", "groonga install");
+INSERT INTO diaries (title, tags) VALUES ("Hello mroonga!", "mroonga install");
+
+
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("install" IN BOOLEAN MODE);
+
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+
+# MySQL <= 5.5 reports wrong a warning. :<
+# It has been fixed in MySQL >= 5.6 and MariaDB >= 5.3.
+--disable_warnings
+SET mroonga_match_escalation_threshold = -1;
+--enable_warnings
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+
+SET mroonga_match_escalation_threshold = 0;
+SELECT * FROM diaries WHERE MATCH (tags) AGAINST ("gr" IN BOOLEAN MODE);
+
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/version_55_performance_schema.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/version_55_performance_schema.test
new file mode 100644
index 00000000000..8519457cc70
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/version_55_performance_schema.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_version_55.inc
+--source include/have_innodb.inc
+--source include/have_perfschema.inc
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SHOW VARIABLES LIKE 'performance_schema';
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ content VARCHAR(255),
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (content) VALUES ("Tommorow will be shiny day!");
+
+SHOW TABLES FROM performance_schema;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/mysql-test/mroonga/wrapper/t/version_56_or_later_performance_schema.test b/storage/mroonga/mysql-test/mroonga/wrapper/t/version_56_or_later_performance_schema.test
new file mode 100644
index 00000000000..c3d4aae5bf6
--- /dev/null
+++ b/storage/mroonga/mysql-test/mroonga/wrapper/t/version_56_or_later_performance_schema.test
@@ -0,0 +1,42 @@
+# Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+--source ../../include/mroonga/have_version_56_or_later.inc
+--source include/have_innodb.inc
+--source include/have_perfschema.inc
+--source include/not_embedded.inc
+--source ../../include/mroonga/have_mroonga.inc
+
+--disable_warnings
+DROP TABLE IF EXISTS diaries;
+--enable_warnings
+
+SHOW VARIABLES LIKE 'performance_schema';
+
+CREATE TABLE diaries (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ content VARCHAR(255),
+ FULLTEXT INDEX (content)
+) DEFAULT CHARSET UTF8 COMMENT = 'ENGINE "InnoDB"';
+SHOW CREATE TABLE diaries;
+
+INSERT INTO diaries (content) VALUES ("Tommorow will be shiny day!");
+
+SHOW TABLES FROM performance_schema;
+
+DROP TABLE diaries;
+
+--source ../../include/mroonga/have_mroonga_deinit.inc
diff --git a/storage/mroonga/packages/Makefile.am b/storage/mroonga/packages/Makefile.am
new file mode 100644
index 00000000000..fed925c3dfc
--- /dev/null
+++ b/storage/mroonga/packages/Makefile.am
@@ -0,0 +1,7 @@
+SUBDIRS = \
+ apt \
+ rpm \
+ source \
+ ubuntu \
+ windows \
+ yum
diff --git a/storage/mroonga/packages/apt/Makefile.am b/storage/mroonga/packages/apt/Makefile.am
new file mode 100644
index 00000000000..3ca74c7a145
--- /dev/null
+++ b/storage/mroonga/packages/apt/Makefile.am
@@ -0,0 +1,55 @@
+REPOSITORIES_PATH = repositories
+DISTRIBUTIONS = debian
+CHROOT_BASE = /var/lib/chroot
+ARCHITECTURES = i386 amd64
+CODES = wheezy jessie unstable
+
+all:
+
+release: build sign-packages update-repository sign-repository upload
+
+remove-existing-packages:
+ for distribution in $(DISTRIBUTIONS); do \
+ find $(REPOSITORIES_PATH)/$${distribution}/pool \
+ -type f -delete; \
+ done
+
+download:
+ for distribution in $(DISTRIBUTIONS); do \
+ rsync -avz --progress --delete \
+ $(RSYNC_PATH)/$${distribution} $(REPOSITORIES_PATH)/; \
+ done
+
+sign-packages:
+ ./sign-packages.sh '$(GPG_UID)' '$(REPOSITORIES_PATH)/' '$(CODES)'
+
+update-repository:
+ ./update-repository.sh '$(PACKAGE_NAME)' '$(REPOSITORIES_PATH)/' \
+ '$(ARCHITECTURES)' '$(CODES)'
+
+sign-repository:
+ ./sign-repository.sh '$(GPG_UID)' '$(REPOSITORIES_PATH)/' '$(CODES)'
+
+ensure-rsync-path:
+ @if test -z "$(RSYNC_PATH)"; then \
+ echo "--with-rsync-path configure option must be specified."; \
+ false; \
+ fi
+
+upload: ensure-rsync-path
+ for distribution in $(DISTRIBUTIONS); do \
+ (cd $(REPOSITORIES_PATH)/$${distribution}; \
+ rsync -avz --progress --delete \
+ dists pool $(RSYNC_PATH)/$${distribution}; \
+ ); \
+ done
+
+build: source
+ ./build-in-chroot.sh \
+ $(PACKAGE) $(VERSION) $(srcdir)/.. $(REPOSITORIES_PATH)/ \
+ $(CHROOT_BASE) '$(ARCHITECTURES)' '$(CODES)'
+
+source: ../$(PACKAGE)-$(VERSION).tar.gz
+
+../$(PACKAGE)-$(VERSION).tar.gz:
+ ln -s $(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz ../
diff --git a/storage/mroonga/packages/apt/build-deb.sh b/storage/mroonga/packages/apt/build-deb.sh
new file mode 100755
index 00000000000..a0f1f1ced03
--- /dev/null
+++ b/storage/mroonga/packages/apt/build-deb.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+
+LANG=C
+
+PACKAGE=$(cat /tmp/build-package)
+USER_NAME=$(cat /tmp/build-user)
+VERSION=$(cat /tmp/build-version)
+DEPENDED_PACKAGES=$(cat /tmp/depended-packages)
+BUILD_SCRIPT=/tmp/build-deb-in-chroot.sh
+
+mysql_server_package=mysql-server
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+grep '^deb ' /etc/apt/sources.list | \
+ sed -e 's/^deb /deb-src /' > /etc/apt/sources.list.d/base-source.list
+
+run apt-get update
+run apt-get install -V -y lsb-release
+distribution=$(lsb_release --id --short)
+code_name=$(lsb_release --codename --short)
+
+groonga_list=/etc/apt/sources.list.d/groonga.list
+if [ ! -f "${groonga_list}" ]; then
+ case ${distribution} in
+ Debian)
+ component=main
+ if [ "$code_name" = "sid" ]; then
+ code_name=unstable
+ fi
+ ;;
+ Ubuntu)
+ component=universe
+ ;;
+ esac
+ downcased_distribtion=$(echo ${distribution} | tr A-Z a-z)
+ run cat <<EOF | run tee ${groonga_list}
+deb http://packages.groonga.org/${downcased_distribtion}/ ${code_name} ${component}
+deb-src http://packages.groonga.org/${downcased_distribtion}/ ${code_name} ${component}
+EOF
+ apt-get update
+ run apt-get -V -y --allow-unauthenticated install groonga-keyring
+fi
+
+run apt-get update
+run apt-get upgrade -V -y
+
+security_list=/etc/apt/sources.list.d/security.list
+if [ ! -f "${security_list}" ]; then
+ run apt-get install -V -y lsb-release
+
+ case ${distribution} in
+ Debian)
+ if [ "${code_name}" = "sid" ]; then
+ touch "${security_list}"
+ else
+ cat <<EOF > "${security_list}"
+deb http://security.debian.org/ ${code_name}/updates main
+deb-src http://security.debian.org/ ${code_name}/updates main
+EOF
+ fi
+ ;;
+ Ubuntu)
+ cat <<EOF > "${security_list}"
+deb http://security.ubuntu.com/ubuntu ${code_name}-security main restricted
+deb-src http://security.ubuntu.com/ubuntu ${code_name}-security main restricted
+EOF
+ ;;
+ esac
+
+ run apt-get update
+ run apt-get upgrade -V -y
+fi
+
+universe_list=/etc/apt/sources.list.d/universe.list
+if [ ! -f "$universe_list}" ]; then
+ case ${distribution} in
+ Ubuntu)
+ sed -e 's/main/universe/' /etc/apt/sources.list > ${universe_list}
+ run apt-get update
+ ;;
+ esac
+fi
+
+run apt-get install -V -y devscripts ${DEPENDED_PACKAGES}
+run apt-get build-dep -y ${mysql_server_package}
+run apt-get clean
+
+if ! id $USER_NAME >/dev/null 2>&1; then
+ run useradd -m $USER_NAME
+fi
+
+cat <<EOF > $BUILD_SCRIPT
+#!/bin/sh
+
+rm -rf build
+mkdir -p build
+
+cp /tmp/${PACKAGE}-${VERSION}.tar.gz build/${PACKAGE}_${VERSION}.orig.tar.gz
+
+cd build
+
+tar xfz ${PACKAGE}_${VERSION}.orig.tar.gz
+cd ${PACKAGE}-${VERSION}/
+cp -rp /tmp/${PACKAGE}-debian debian
+# export DEB_BUILD_OPTIONS="noopt nostrip"
+MYSQL_PACKAGE_INFO=\$(apt-cache show mysql-server | grep Version | sort | tail -1)
+MYSQL_PACKAGE_VERSION=\${MYSQL_PACKAGE_INFO##Version: }
+sed -i "s/MYSQL_VERSION/\$MYSQL_PACKAGE_VERSION/" debian/control
+debuild -us -uc
+EOF
+
+run chmod +x $BUILD_SCRIPT
+run su - $USER_NAME $BUILD_SCRIPT
diff --git a/storage/mroonga/packages/apt/build-in-chroot.sh b/storage/mroonga/packages/apt/build-in-chroot.sh
new file mode 100755
index 00000000000..ab5cc589555
--- /dev/null
+++ b/storage/mroonga/packages/apt/build-in-chroot.sh
@@ -0,0 +1,134 @@
+#!/bin/sh
+
+if [ $# != 7 ]; then
+ echo "Usage: $0 PACKAGE VERSION SOURCE_DIR DESTINATION CHROOT_BASE ARCHITECTURES CODES"
+ echo " e.g.: $0 groonga 0.1.9 SOURCE_DIR repositories/ /var/lib/chroot 'i386 amd64' 'lenny unstable hardy karmic'"
+ exit 1
+fi
+
+PACKAGE=$1
+VERSION=$2
+SOURCE_DIR=$3
+DESTINATION=$4
+CHROOT_BASE=$5
+ARCHITECTURES=$6
+CODES=$7
+
+PATH=/usr/local/sbin:/usr/sbin:$PATH
+
+script_base_dir=`dirname $0`
+
+if test "$PARALLEL" = "yes"; then
+ parallel="yes"
+else
+ parallel="no"
+fi
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+run_sudo()
+{
+ run sudo "$@"
+}
+
+build_chroot()
+{
+ architecture=$1
+ code_name=$2
+
+ run_sudo debootstrap --arch $architecture $code_name $base_dir
+
+ case $code_name in
+ squeeze|wheezy|jessie|unstable)
+ run_sudo sed -i'' -e 's/us/jp/' $base_dir/etc/apt/sources.list
+ ;;
+ *)
+ run_sudo sed -i'' \
+ -e 's,http://archive,http://jp.archive,' \
+ -e 's/main$/main universe/' \
+ $base_dir/etc/apt/sources.list
+ ;;
+ esac
+
+ run_sudo sh -c "echo >> /etc/fstab"
+ run_sudo sh -c "echo /sys ${base_dir}/sys none bind 0 0 >> /etc/fstab"
+ run_sudo sh -c "echo /dev ${base_dir}/dev none bind 0 0 >> /etc/fstab"
+ run_sudo sh -c "echo devpts-chroot ${base_dir}/dev/pts devpts defaults 0 0 >> /etc/fstab"
+ run_sudo sh -c "echo proc-chroot ${base_dir}/proc proc defaults 0 0 >> /etc/fstab"
+ run_sudo mount ${base_dir}/sys
+ run_sudo mount ${base_dir}/dev
+ run_sudo mount ${base_dir}/dev/pts
+ run_sudo mount ${base_dir}/proc
+}
+
+build()
+{
+ architecture=$1
+ code_name=$2
+
+ target=${code_name}-${architecture}
+ base_dir=${CHROOT_BASE}/${target}
+ if [ ! -d $base_dir ]; then
+ run build_chroot $architecture $code_name
+ fi
+
+ case ${code_name} in
+ squeeze|wheezy|jessie|unstable)
+ distribution=debian
+ component=main
+ ;;
+ *)
+ distribution=ubuntu
+ component=universe
+ ;;
+ esac
+
+ source_dir=${SOURCE_DIR}
+ build_user=${PACKAGE}-build
+ build_user_dir=${base_dir}/home/$build_user
+ build_dir=${build_user_dir}/build
+ pool_base_dir=${DESTINATION}${distribution}/pool/${code_name}/${component}
+ package_initial=$(echo ${PACKAGE} | sed -e 's/\(.\).*/\1/')
+ pool_dir=${pool_base_dir}/${package_initial}/${PACKAGE}
+ run cp $source_dir/${PACKAGE}-${VERSION}.tar.gz \
+ ${CHROOT_BASE}/$target/tmp/
+ run rm -rf ${CHROOT_BASE}/$target/tmp/${PACKAGE}-debian
+ run cp -rp $source_dir/debian/ \
+ ${CHROOT_BASE}/$target/tmp/${PACKAGE}-debian
+ run echo $PACKAGE > ${CHROOT_BASE}/$target/tmp/build-package
+ run echo $VERSION > ${CHROOT_BASE}/$target/tmp/build-version
+ run echo $build_user > ${CHROOT_BASE}/$target/tmp/build-user
+ run cp ${script_base_dir}/${PACKAGE}-depended-packages \
+ ${CHROOT_BASE}/$target/tmp/depended-packages
+ run cp ${script_base_dir}/build-deb.sh \
+ ${CHROOT_BASE}/$target/tmp/
+ run_sudo rm -rf $build_dir
+ run_sudo su -c "/usr/sbin/chroot ${CHROOT_BASE}/$target /tmp/build-deb.sh"
+ run mkdir -p $pool_dir
+ for path in $build_dir/*; do
+ [ -f $path ] && run cp -p $path $pool_dir/
+ done
+}
+
+for architecture in $ARCHITECTURES; do
+ for code_name in $CODES; do
+ if test "$parallel" = "yes"; then
+ build $architecture $code_name &
+ else
+ mkdir -p tmp
+ build_log=tmp/build-$code_name-$architecture.log
+ build $architecture $code_name 2>&1 | tee $build_log
+ fi;
+ done;
+done
+
+if test "$parallel" = "yes"; then
+ wait
+fi
diff --git a/storage/mroonga/packages/apt/mroonga-depended-packages b/storage/mroonga/packages/apt/mroonga-depended-packages
new file mode 100644
index 00000000000..fe9e01ba19f
--- /dev/null
+++ b/storage/mroonga/packages/apt/mroonga-depended-packages
@@ -0,0 +1,11 @@
+debhelper
+autotools-dev
+libgroonga-dev
+pkg-config
+libmecab-dev
+mecab-utils
+libmysqlclient-dev
+libmysqld-dev
+libssl-dev
+groonga-normalizer-mysql
+wget
diff --git a/storage/mroonga/packages/apt/sign-packages.sh b/storage/mroonga/packages/apt/sign-packages.sh
new file mode 100755
index 00000000000..11a4aea26db
--- /dev/null
+++ b/storage/mroonga/packages/apt/sign-packages.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 3 ]; then
+ echo "Usage: $0 GPG_UID DESITINATION CODES"
+ echo " e.g.: $0 'F10399C0' repositories/ 'lenny unstable hardy karmic'"
+ exit 1
+fi
+
+GPG_UID=$1
+DESTINATION=$2
+CODES=$3
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+for code_name in ${CODES}; do
+ case ${code_name} in
+ squeeze|wheezy|jessie|unstable)
+ distribution=debian
+ ;;
+ *)
+ distribution=ubuntu
+ ;;
+ esac
+
+ base_directory=${DESTINATION}${distribution}
+ debsign -pgpg2 --re-sign -k${GPG_UID} \
+ $(find ${base_directory} -name '*.dsc' -or -name '*.changes') &
+ if [ "${PARALLEL}" != "yes" ]; then
+ wait
+ fi
+done
+
+wait
diff --git a/storage/mroonga/packages/apt/sign-repository.sh b/storage/mroonga/packages/apt/sign-repository.sh
new file mode 100755
index 00000000000..fb0de850d6f
--- /dev/null
+++ b/storage/mroonga/packages/apt/sign-repository.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 3 ]; then
+ echo "Usage: $0 GPG_UID DESTINATION CODES"
+ echo " e.g.: $0 'F10399C0' repositories/ 'lenny unstable hardy karmic'"
+ exit 1
+fi
+
+GPG_UID=$1
+DESTINATION=$2
+CODES=$3
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+for code_name in ${CODES}; do
+ case ${code_name} in
+ squeeze|wheezy|jessie|unstable)
+ distribution=debian
+ ;;
+ *)
+ distribution=ubuntu
+ ;;
+ esac
+
+ release=${DESTINATION}${distribution}/dists/${code_name}/Release
+ rm -f ${release}.gpg
+ gpg2 --sign --detach-sign --armor \
+ --local-user ${GPG_UID} \
+ --output ${release}.gpg \
+ ${release} &
+
+ if [ "${PARALLEL}" != "yes" ]; then
+ wait
+ fi
+done
+
+wait
diff --git a/storage/mroonga/packages/apt/update-repository.sh b/storage/mroonga/packages/apt/update-repository.sh
new file mode 100755
index 00000000000..da1f8cd121c
--- /dev/null
+++ b/storage/mroonga/packages/apt/update-repository.sh
@@ -0,0 +1,130 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 4 ]; then
+ echo "Usage: $0 PROJECT_NAME DESTINATION ARCHITECTURES CODES"
+ echo " e.g.: $0 mroonga repositories/ 'i386 amd64' 'lenny unstable hardy karmic'"
+ exit 1
+fi
+
+PROJECT_NAME=$1
+DESTINATION=$2
+ARCHITECTURES=$3
+CODES=$4
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+update_repository()
+{
+ distribution=$1
+ code_name=$2
+ component=$3
+
+ rm -rf dists/${code_name}
+ mkdir -p dists/${code_name}/${component}/binary-i386/
+ mkdir -p dists/${code_name}/${component}/binary-amd64/
+ mkdir -p dists/${code_name}/${component}/source/
+
+ cat <<EOF > dists/.htaccess
+Options +Indexes
+EOF
+
+ cat <<EOF > dists/${code_name}/${component}/binary-i386/Release
+Archive: ${code_name}
+Component: ${component}
+Origin: The ${PROJECT_NAME} project
+Label: The ${PROJECT_NAME} project
+Architecture: i386
+EOF
+
+ cat <<EOF > dists/${code_name}/${component}/binary-amd64/Release
+Archive: ${code_name}
+Component: ${component}
+Origin: The ${PROJECT_NAME} project
+Label: The ${PROJECT_NAME} project
+Architecture: amd64
+EOF
+
+ cat <<EOF > dists/${code_name}/${component}/source/Release
+Archive: ${code_name}
+Component: ${component}
+Origin: The ${PROJECT_NAME} project
+Label: The ${PROJECT_NAME} project
+Architecture: source
+EOF
+
+ cat <<EOF > generate-${code_name}.conf
+Dir::ArchiveDir ".";
+Dir::CacheDir ".";
+TreeDefault::Directory "pool/${code_name}/${component}";
+TreeDefault::SrcDirectory "pool/${code_name}/${component}";
+Default::Packages::Extensions ".deb";
+Default::Packages::Compress ". gzip bzip2";
+Default::Sources::Compress ". gzip bzip2";
+Default::Contents::Compress "gzip bzip2";
+
+BinDirectory "dists/${code_name}/${component}/binary-i386" {
+ Packages "dists/${code_name}/${component}/binary-i386/Packages";
+ Contents "dists/${code_name}/Contents-i386";
+ SrcPackages "dists/${code_name}/${component}/source/Sources";
+};
+
+BinDirectory "dists/${code_name}/${component}/binary-amd64" {
+ Packages "dists/${code_name}/${component}/binary-amd64/Packages";
+ Contents "dists/${code_name}/Contents-amd64";
+ SrcPackages "dists/${code_name}/${component}/source/Sources";
+};
+
+Tree "dists/${code_name}" {
+ Sections "${component}";
+ Architectures "i386 amd64 source";
+};
+EOF
+ apt-ftparchive generate generate-${code_name}.conf
+ chmod 644 dists/${code_name}/Contents-*
+
+ rm -f dists/${code_name}/Release*
+ rm -f *.db
+ cat <<EOF > release-${code_name}.conf
+APT::FTPArchive::Release::Origin "The ${PROJECT_NAME} project";
+APT::FTPArchive::Release::Label "The ${PROJECT_NAME} project";
+APT::FTPArchive::Release::Architectures "i386 amd64";
+APT::FTPArchive::Release::Codename "${code_name}";
+APT::FTPArchive::Release::Suite "${code_name}";
+APT::FTPArchive::Release::Components "${component}";
+APT::FTPArchive::Release::Description "${PACKAGE_NAME} packages";
+EOF
+ apt-ftparchive -c release-${code_name}.conf \
+ release dists/${code_name} > /tmp/Release
+ mv /tmp/Release dists/${code_name}
+}
+
+for code_name in ${CODES}; do
+ case ${code_name} in
+ squeeze|wheezy|jessie|unstable)
+ distribution=debian
+ component=main
+ ;;
+ *)
+ distribution=ubuntu
+ component=universe
+ ;;
+ esac
+
+ mkdir -p ${DESTINATION}${distribution}
+ (cd ${DESTINATION}${distribution}
+ update_repository $distribution $code_name $component) &
+ if [ "${PARALLEL}" != "yes" ]; then
+ wait
+ fi
+done
+
+wait
diff --git a/storage/mroonga/packages/check-utility.sh b/storage/mroonga/packages/check-utility.sh
new file mode 100755
index 00000000000..211e231a473
--- /dev/null
+++ b/storage/mroonga/packages/check-utility.sh
@@ -0,0 +1,665 @@
+#!/bin/sh
+
+# Usage: check-utility.sh [--install-groonga]
+# [--check-install]
+# [--check-address]
+# [--enable-repository]
+#
+# CODES="squeeze wheezy unstable lucid natty oneiric precise"
+# DISTRIBUTIONS="centos fedora"
+
+CHROOT_ROOT=/var/lib/chroot
+CHECK_ADDRESS=0
+CHECK_INSTALL=0
+CHECK_INSTALL_PACKAGE=mysql-server-mroonga
+CHECK_BUILD=0
+CHECK_DEPENDS=0
+CHECK_PROVIDES=0
+ENABLE_REPOSITORY=0
+DISABLE_REPOSITORY=0
+INSTALL_SCRIPT=0
+INSTALL_MROONGA=0
+UNINSTALL_MROONGA=0
+
+common_deb_procedure ()
+{
+ for code in $CODES; do
+ for arch in $DEB_ARCHITECTURES; do
+ root_dir=$CHROOT_ROOT/$code-$arch
+ eval $1 $code $arch $root_dir
+ done
+ done
+}
+
+common_rpm_procedure ()
+{
+ for dist in $DISTRIBUTIONS; do
+ case $dist in
+ "fedora")
+ DISTRIBUTIONS_VERSION="19"
+ ;;
+ "centos")
+ DISTRIBUTIONS_VERSION="5 6"
+ ;;
+ esac
+ for ver in $DISTRIBUTIONS_VERSION; do
+ for arch in $RPM_ARCHITECTURES; do
+ root_dir=$CHROOT_ROOT/$dist-$ver-$arch
+ eval $1 $dist $arch $ver $root_dir
+ done
+ done
+ done
+}
+
+echo_packages_repository_address ()
+{
+ root_dir=$1
+ code=$2
+ arch=$3
+ address=`grep "packages.groonga.org" $root_dir/etc/hosts | grep -v "#"`
+ if [ -z "$address" ]; then
+ echo "$code-$arch: default"
+ else
+ echo "$code-$arch: $address"
+ fi
+}
+
+setup_distributions ()
+{
+ if [ -z "$DISTRIBUTIONS" ]; then
+ DISTRIBUTIONS="centos fedora"
+ fi
+}
+
+setup_rpm_architectures ()
+{
+ if [ -z "$RPM_ARCHITECTURES" ]; then
+ RPM_ARCHITECTURES="i386 x86_64"
+ fi
+}
+
+setup_codes ()
+{
+ if [ -z "$CODES" ]; then
+ CODES="squeeze wheezy jessie unstable lucid precise quantal raring"
+ fi
+}
+setup_deb_architectures ()
+{
+ if [ -z "$DEB_ARCHITECTURES" ]; then
+ DEB_ARCHITECTURES="i386 amd64"
+ fi
+}
+
+check_packages_repository_address ()
+{
+ common_deb_procedure "check_packages_deb_repository_address"
+ common_rpm_procedure "check_packages_rpm_repository_address"
+}
+
+check_packages_deb_repository_address ()
+{
+ code=$1
+ arch=$2
+ root_dir=$4
+ echo_packages_repository_address "$root_dir" "$code" "$arch"
+}
+
+check_packages_rpm_repository_address ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ root_dir=$4
+ echo_packages_repository_address "$root_dir" "$dist-$ver" "$arch"
+}
+
+host_address ()
+{
+ ifconfig_result=`LANG=C /sbin/ifconfig wlan0`
+ inet_addr=`echo "$ifconfig_result" | grep "inet addr:192"`
+ address=`echo $inet_addr | ruby -ne '/inet addr:(.+?)\s/ =~ $_ && puts($1)'`
+ HOST_ADDRESS=$address
+}
+
+check_build_packages ()
+{
+ common_deb_procedure "check_build_deb_packages"
+ common_rpm_procedure "check_build_rpm_packages"
+}
+
+check_build_deb_packages ()
+{
+ code=$1
+ arch=$2
+ BASE_VERSION=`cat ../version`
+ RESULT_SET=`find apt/repositories -name "*$BASE_VERSION*" | grep $code | grep $arch`
+ if [ -z "$RESULT_SET" ]; then
+ printf "%8s %5s %s => 0 deb\n" $code $arch $BASE_VERSION
+ else
+ PACKAGE_COUNT=`find apt/repositories -name "*$BASE_VERSION*" | grep $code | grep $arch | wc | awk '{print \$1}'`
+ printf "%8s %5s %s => %2d debs\n" $code $arch $BASE_VERSION $PACKAGE_COUNT
+ fi
+}
+
+check_build_rpm_packages ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ BASE_VERSION=`cat ../version`
+ FIND_PATH=yum/repositories/$dist/$ver/$arch
+ RESULT_SET=`find $FIND_PATH -name "*$BASE_VERSION*"`
+ if [ -z "$RESULT_SET" ]; then
+ printf "%8s %6s %s => 0 rpm\n" $dist$ver $arch $BASE_VERSION
+ else
+ PACKAGE_COUNT=`find $FIND_PATH -name "*$BASE_VERSION*" | wc -l`
+ printf "%8s %6s %s => %2d rpms\n" $dist$ver $arch $BASE_VERSION $PACKAGE_COUNT
+ fi
+}
+
+check_depends_packages ()
+{
+ common_deb_procedure "check_depends_deb_packages"
+ common_rpm_procedure "check_depends_rpm_packages"
+}
+
+check_depends_deb_packages ()
+{
+ code=$1
+ arch=$2
+ BASE_VERSION=`cat ../version`
+ FIND_PATH=apt/repositories/*/pool/$code
+ RESULT_SET=`find $FIND_PATH -name "*$BASE_VERSION*.deb"`
+ if [ -z "$RESULT_SET" ]; then
+ printf "%8s %5s %s => 404 deb\n" $code $arch $BASE_VERSION
+ else
+ for pkg in $RESULT_SET; do
+ DEB_NAME=`basename $pkg`
+ DEPENDS=`dpkg -I $pkg | grep "Depends"`
+ printf "%8s %5s %s => %s\n" $code $arch $DEB_NAME "$DEPENDS"
+ done
+ fi
+}
+
+check_depends_rpm_packages ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ BASE_VERSION=`cat ../version`
+ FIND_PATH=yum/repositories/$dist/$ver/$arch
+ RESULT_SET=`find $FIND_PATH -name "*$BASE_VERSION*"`
+ if [ -z "$RESULT_SET" ]; then
+ printf "%8s %6s %s => 404 rpm\n" $dist$ver $arch $BASE_VERSION
+ else
+ for pkg in $RESULT_SET; do
+ RPM_NAME=`basename $pkg`
+ DEPENDS=`rpm -qp --requires $pkg | grep -i "mysql" | tr -t '\n' ' '`
+ printf "%9s %6s %s => %s\n" $dist$ver $arch $RPM_NAME "$DEPENDS"
+ done
+ fi
+}
+
+check_provided_mysql_packages ()
+{
+ common_deb_procedure "check_provided_mysql_deb_packages"
+ common_rpm_procedure "check_provided_mysql_rpm_packages"
+ for code in $CODES; do
+ echo $code
+ cat tmp/$code-amd64-mysql-server.txt
+ done
+ for dist in $DISTRIBUTIONS; do
+ echo $dist
+ cat tmp/$dist-x86_64-mysql-server.txt
+ done
+}
+
+check_provided_mysql_deb_packages ()
+{
+ code=$1
+ arch=$2
+ root_dir=$3
+ cat > tmp/check-provided-mysql.sh <<EOF
+#!/bin/sh
+apt-get update > /dev/null
+apt-cache show mysql-server | grep "Version" | head -1 > /tmp/$code-$arch-mysql-server.txt
+EOF
+ if [ -d $root_dir ]; then
+ CHECK_SCRIPT=check-provided-mysql.sh
+ echo "copy check script $CHECK_SCRIPT to $root_dir/tmp"
+ sudo rm -f $root_dir/tmp/$CHECK_SCRIPT
+ cp tmp/$CHECK_SCRIPT $root_dir/tmp
+ sudo chmod 755 $root_dir/tmp/$CHECK_SCRIPT
+ sudo chname $code-$arch chroot $root_dir /tmp/$CHECK_SCRIPT
+ cp $root_dir/tmp/$code-$arch-mysql-server.txt tmp
+ fi
+}
+
+check_provided_mysql_rpm_packages ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ root_dir=$4
+ cat > tmp/check-provided-mysql.sh <<EOF
+#!/bin/sh
+yum update > /dev/null
+yum info mysql-server | grep "Version" > /tmp/$code-$arch-mysql-server.txt
+EOF
+ if [ -d $root_dir ]; then
+ CHECK_SCRIPT=check-provided-mysql.sh
+ echo "copy check script $CHECK_SCRIPT to $root_dir/tmp"
+ sudo rm -f $root_dir/tmp/$CHECK_SCRIPT
+ cp tmp/$CHECK_SCRIPT $root_dir/tmp
+ sudo chmod 755 $root_dir/tmp/$CHECK_SCRIPT
+ sudo chname $code-$arch chroot $root_dir /tmp/$CHECK_SCRIPT
+ cp $root_dir/tmp/$code-$arch-mysql-server.txt tmp
+ fi
+}
+
+check_installed_mroonga_packages ()
+{
+ common_deb_procedure "check_installed_mroonga_deb_packages"
+ common_rpm_procedure "check_installed_mroonga_rpm_packages"
+}
+
+check_installed_mroonga_deb_packages ()
+{
+ code=$1
+ arch=$2
+ root_dir=$3
+ cat > tmp/check-deb-mroonga.sh <<EOF
+#!/bin/sh
+dpkg -l | grep $CHECK_INSTALL_PACKAGE
+EOF
+ if [ -d $root_dir ]; then
+ CHECK_SCRIPT=check-deb-mroonga.sh
+ echo "copy check script $CHECK_SCRIPT to $root_dir/tmp"
+ sudo rm -f $root_dir/tmp/$CHECK_SCRIPT
+ cp tmp/$CHECK_SCRIPT $root_dir/tmp
+ sudo chmod 755 $root_dir/tmp/$CHECK_SCRIPT
+ sudo chname $code-$arch chroot $root_dir /tmp/$CHECK_SCRIPT
+ fi
+}
+
+check_installed_mroonga_rpm_packages ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ root_dir=$4
+ cat > tmp/check-rpm-mroonga.sh <<EOF
+#!/bin/sh
+rpm -qa | grep $CHECK_INSTALL_PACKAGE
+EOF
+ CHECK_SCRIPT=check-rpm-mroonga.sh
+ if [ -d $root_dir ]; then
+ echo "copy check script $CHECK_SCRIPT to $root_dir/tmp"
+ sudo rm -f $root_dir/tmp/$CHECK_SCRIPT
+ cp tmp/$CHECK_SCRIPT $root_dir/tmp
+ sudo chmod 755 $root_dir/tmp/$CHECK_SCRIPT
+ sudo chname $code-$ver-$arch chroot $root_dir /tmp/$CHECK_SCRIPT
+ fi
+}
+
+install_mroonga_packages ()
+{
+ common_deb_procedure "install_mroonga_deb_packages"
+ common_rpm_procedure "install_mroonga_rpm_packages"
+}
+
+install_mroonga_deb_packages ()
+{
+ code=$1
+ arch=$2
+ root_dir=$4
+ cat > tmp/install-aptitude-mroonga.sh <<EOF
+#!/bin/sh
+sudo aptitude clean
+rm -f /var/lib/apt/lists/packages.groonga.org_*
+rm -f /var/lib/apt/lists/partial/packages.groonga.org_*
+sudo aptitude update
+sudo aptitude -V -D -y --allow-untrusted install groonga-keyring
+sudo aptitude update
+sudo aptitude -V -D install mysql-server-mroonga
+sudo aptitude -V -D install groonga-tokenizer-mecab
+EOF
+ cat > tmp/install-aptget-mroonga.sh <<EOF
+#!/bin/sh
+sudo apt-get clean
+rm -f /var/lib/apt/lists/packages.groonga.org_*
+rm -f /var/lib/apt/lists/partial/packages.groonga.org_*
+sudo apt-get update
+sudo apt-get -y --allow-unauthenticated install groonga-keyring
+sudo apt-get update
+sudo apt-get -V -y install mysql-server-mroonga
+sudo apt-get -V -y install groonga-tokenizer-mecab
+EOF
+ root_dir=$CHROOT_ROOT/$code-$arch
+ INSTALL_SCRIPT=""
+ case $code in
+ squeeze|unstable)
+ INSTALL_SCRIPT=install-aptitude-mroonga.sh
+ ;;
+ *)
+ INSTALL_SCRIPT=install-aptget-mroonga.sh
+ ;;
+ esac
+ if [ -d $root_dir ]; then
+ echo "copy install script $INSTALL_SCRIPT to $root_dir/tmp"
+ sudo rm -f $root_dir/tmp/$INSTALL_SCRIPT
+ cp tmp/$INSTALL_SCRIPT $root_dir/tmp
+ chmod 755 $root_dir/tmp/$INSTALL_SCRIPT
+ sudo chname $code-$arch chroot $root_dir /tmp/$INSTALL_SCRIPT
+ fi
+}
+
+install_mroonga_rpm_packages ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ root_dir=$4
+ cat > tmp/install-centos5-mroonga.sh <<EOF
+sudo rpm -ivh http://packages.groonga.org/centos/groonga-release-1.1.0-0.noarch.rpm
+sudo yum makecache
+sudo yum install -y MySQL-server
+sudo service mysql start
+sudo yum install -y mysql-mroonga
+sudo yum install -y groonga-tokenizer-mecab
+EOF
+ cat > tmp/install-centos6-mroonga.sh <<EOF
+sudo rpm -ivh http://packages.groonga.org/centos/groonga-release-1.1.0-0.noarch.rpm
+sudo yum makecache
+sudo yum install -y mysql-server
+sudo service mysql start
+sudo yum install -y mysql-mroonga
+sudo yum install -y groonga-tokenizer-mecab
+EOF
+ cat > tmp/install-fedora-mroonga.sh <<EOF
+sudo rpm -ivh http://packages.groonga.org/fedora/groonga-release-1.1.0-0.noarch.rpm
+sudo yum makecache
+sudo yum install -y mysql-mroonga
+sudo yum install -y groonga-tokenizer-mecab
+EOF
+ INSTALL_SCRIPT=""
+ case "$dist-$ver" in
+ centos-5)
+ INSTALL_SCRIPT=install-centos5-mroonga.sh
+ ;;
+ centos-6)
+ INSTALL_SCRIPT=install-centos6-mroonga.sh
+ ;;
+ fedora-18)
+ INSTALL_SCRIPT=install-fedora-mroonga.sh
+ ;;
+ *)
+ ;;
+ esac
+ if [ -d $root_dir ]; then
+ echo "copy install script $INSTALL_SCRIPT to $root_dir/tmp"
+ sudo rm -f $root_dir/tmp/$INSTALL_SCRIPT
+ cp tmp/$INSTALL_SCRIPT $root_dir/tmp
+ chmod 755 $root_dir/tmp/$INSTALL_SCRIPT
+ sudo chname $code-$ver-$arch chroot $root_dir /tmp/$INSTALL_SCRIPT
+ fi
+}
+
+
+uninstall_mroonga_packages ()
+{
+ common_deb_procedure "uninstall_mroonga_deb_packages"
+ common_rpm_procedure "uninstall_mroonga_rpm_packages"
+}
+
+uninstall_mroonga_deb_packages ()
+{
+ code=$1
+ arch=$2
+ root_dir=$4
+ UNINSTALL_SCRIPT=uninstall-deb-mroonga.sh
+ cat > $UNINSTALL_SCRIPT <<EOF
+#!/bin/sh
+sudo apt-get purge mroonga-* mysql-*
+EOF
+ if [ -d $root_dir ]; then
+ echo "copy uninstall script $UNINSTALL_SCRIPT to $root_dir/tmp"
+ sudo rm -f $root_dir/tmp/$UNINSTALL_SCRIPT
+ cp $UNINSTALL_SCRIPT $root_dir/tmp
+ chmod 755 $root_dir/tmp/$UNINSTALL_SCRIPT
+ sudo chname $code-$arch chroot $root_dir /tmp/$UNINSTALL_SCRIPT
+ fi
+}
+
+uninstall_mroonga_rpm_packages ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ root_dir=$4
+ UNINSTALL_SCRIPT=uninstall-rpm-mroonga.sh
+ cat > tmp/$UNINSTALL_SCRIPT <<EOF
+#!/bin/sh
+sudo yum remove mroonga-* mysql-*
+EOF
+ if [ -d $root_dir ]; then
+ echo "copy install script $UNINSTALL_SCRIPT to $root_dir/tmp"
+ sudo rm -f $root_dir/tmp/$UNINSTALL_SCRIPT
+ cp tmp/$UNINSTALL_SCRIPT $root_dir/tmp
+ chmod 755 $root_dir/tmp/$UNINSTALL_SCRIPT
+ sudo chname $code-$ver-$arch chroot $root_dir /tmp/$UNINSTALL_SCRIPT
+ fi
+}
+
+
+enable_temporaly_mroonga_repository ()
+{
+ cat > tmp/enable-repository.sh <<EOF
+#!/bin/sh
+
+grep -v "packages.groonga.org" /etc/hosts > /tmp/hosts
+echo "$HOST_ADDRESS packages.groonga.org" >> /tmp/hosts
+cp -f /tmp/hosts /etc/hosts
+EOF
+ common_deb_procedure "enable_temporaly_mroonga_deb_repository"
+ common_rpm_procedure "enable_temporaly_mroonga_rpm_repository"
+ check_packages_repository_address
+}
+
+enable_temporaly_mroonga_deb_repository ()
+{
+ code=$1
+ arch=$2
+ root_dir=$4
+ today=`date '+%Y%m%d.%s'`
+ if [ -d $root_dir ]; then
+ sudo cp $root_dir/etc/hosts $root_dir/etc/hosts.$today
+ sudo cp tmp/enable-repository.sh $root_dir/tmp
+ sudo chname $code-$arch chroot $root_dir /tmp/enable-repository.sh
+ fi
+}
+
+enable_temporaly_mroonga_rpm_repository ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ root_dir=$4
+ today=`date '+%Y%m%d.%s'`
+ if [ -d $root_dir ]; then
+ sudo cp $root_dir/etc/hosts $root_dir/etc/hosts.$today
+ sudo cp tmp/enable-repository.sh $root_dir/tmp
+ sudo chname $code-$arch chroot $root_dir /tmp/enable-repository.sh
+ fi
+}
+
+disable_temporaly_mroonga_repository ()
+{
+ cat > tmp/disable-repository.sh <<EOF
+#!/bin/sh
+
+grep -v "packages.groonga.org" /etc/hosts > /tmp/hosts
+cp -f /tmp/hosts /etc/hosts
+EOF
+ common_deb_procedure "disable_temporaly_mroonga_deb_repository"
+ common_rpm_procedure "disable_temporaly_mroonga_rpm_repository"
+ check_packages_repository_address
+}
+
+disable_temporaly_mroonga_deb_repository ()
+{
+ code=$1
+ arch=$2
+ root_dir=$4
+ DISABLE_SCRIPT=disable-repository.sh
+ today=`date '+%Y%m%d.%s'`
+ if [ -d $root_dir ]; then
+ sudo cp $root_dir/etc/hosts $root_dir/etc/hosts.$today
+ cp tmp/$DISABLE_SCRIPT $root_dir/tmp
+ chmod 755 $root_dir/tmp/$DISABLE_SCRIPT
+ sudo chname $code-$arch chroot $root_dir /tmp/$DISABLE_SCRIPT
+ fi
+
+}
+
+disable_temporaly_mroonga_rpm_repository ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ root_dir=$4
+ DISABLE_SCRIPT=disable-repository.sh
+ today=`date '+%Y%m%d.%s'`
+ if [ -d $root_dir ]; then
+ sudo cp $root_dir/etc/hosts $root_dir/etc/hosts.$today
+ cp tmp/$DISABLE_SCRIPT $root_dir/tmp
+ chmod 755 $root_dir/tmp/$DISABLE_SCRIPT
+ sudo chname $code-$arch chroot $root_dir /tmp/$DISABLE_SCRIPT
+ fi
+}
+
+host_address
+echo $HOST_ADDRESS
+
+while [ $# -ne 0 ]; do
+ case $1 in
+ --check-install)
+ CHECK_INSTALL=1
+ shift
+ if [ ! -z "$1" ]; then
+ case $1 in
+ groonga|mroonga|roonga|mecab|mysql)
+ CHECK_INSTALL_PACKAGE=$1
+ ;;
+ *)
+ ;;
+ esac
+ fi
+ ;;
+ --check-address)
+ CHECK_ADDRESS=1
+ shift
+ ;;
+ --check-depends)
+ CHECK_DEPENDS=1
+ shift
+ ;;
+ --check-provides)
+ CHECK_PROVIDES=1
+ shift
+ ;;
+ --check-build)
+ CHECK_BUILD=1
+ shift
+ ;;
+ --enable-repository)
+ ENABLE_REPOSITORY=1
+ shift
+ ;;
+ --disable-repository)
+ DISABLE_REPOSITORY=1
+ shift
+ ;;
+ --install-mroonga)
+ INSTALL_MROONGA=1
+ shift
+ ;;
+ --uninstall-mroonga)
+ UNINSTALL_MROONGA=1
+ shift
+ ;;
+ --code)
+ shift
+ if [ "$1" = "all" ]; then
+ setup_codes
+ else
+ CODES=$1
+ fi
+ shift
+ ;;
+ --code-arch)
+ shift
+ if [ "$1" = "all" ]; then
+ setup_deb_architectures
+ else
+ DEB_ARCHITECTURES=$1
+ fi
+ shift
+ ;;
+ --dist)
+ shift
+ if [ "$1" = "all" ]; then
+ setup_distributions
+ else
+ DISTRIBUTIONS=$1
+ fi
+ shift
+ ;;
+ --dist-arch)
+ shift
+ if [ "$1" = "all" ]; then
+ setup_rpm_architectures
+ else
+ RPM_ARCHITECTURES=$1
+ fi
+ shift
+ ;;
+ *)
+ shift
+ ;;
+ esac
+done
+
+mkdir -p tmp
+setup_deb_architectures
+setup_rpm_architectures
+
+if [ $CHECK_INSTALL -ne 0 ]; then
+ check_installed_mroonga_packages
+fi
+if [ $CHECK_ADDRESS -ne 0 ]; then
+ check_packages_repository_address
+fi
+if [ $CHECK_BUILD -ne 0 ]; then
+ check_build_packages
+fi
+if [ $CHECK_DEPENDS -ne 0 ]; then
+ check_depends_packages
+fi
+if [ $CHECK_PROVIDES -ne 0 ]; then
+ check_provided_mysql_packages
+fi
+if [ $ENABLE_REPOSITORY -ne 0 ]; then
+ enable_temporaly_mroonga_repository
+fi
+if [ $DISABLE_REPOSITORY -ne 0 ]; then
+ disable_temporaly_mroonga_repository
+fi
+if [ $INSTALL_MROONGA -ne 0 ]; then
+ install_mroonga_packages
+fi
+if [ $UNINSTALL_MROONGA -ne 0 ]; then
+ uninstall_mroonga_packages
+fi
+
diff --git a/storage/mroonga/packages/debian/apparmor/mysql-server-mroonga b/storage/mroonga/packages/debian/apparmor/mysql-server-mroonga
new file mode 100644
index 00000000000..259f8d1dc0c
--- /dev/null
+++ b/storage/mroonga/packages/debian/apparmor/mysql-server-mroonga
@@ -0,0 +1,5 @@
+/usr/lib/groonga/plugins/ r,
+/usr/lib/groonga/plugins/** rm,
+/etc/mecabrc r,
+/var/lib/mecab/dic/** r,
+#include <local/mysql-server-mroonga>
diff --git a/storage/mroonga/packages/debian/changelog b/storage/mroonga/packages/debian/changelog
new file mode 100644
index 00000000000..a846b8b5de8
--- /dev/null
+++ b/storage/mroonga/packages/debian/changelog
@@ -0,0 +1,355 @@
+mroonga (4.05-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Fri, 29 Aug 2014 00:00:00 +0900
+
+mroonga (4.04-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Tue, 29 Jul 2014 00:00:00 +0900
+
+mroonga (4.03-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 29 May 2014 00:00:00 +0900
+
+mroonga (4.02-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Tue, 29 Apr 2014 00:00:00 +0900
+
+mroonga (4.01-2) unstable; urgency=low
+
+ * Built for mysql-server 5.5.37
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Mon, 28 Apr 2014 00:00:00 +0900
+
+mroonga (4.01-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sat, 29 Mar 2014 00:00:00 +0900
+
+mroonga (4.00-2) unstable; urgency=low
+
+ * Built for mysql-server 5.5.35+dfsg-2 on Debian jessie
+ * Built for mysql-server 5.5.35+dfsg-2 on Debian sid
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Thu, 06 Mar 2014 00:00:00 +0900
+
+mroonga (4.00-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sun, 09 Feb 2014 00:00:00 +0900
+
+mroonga (3.12-2) unstable; urgency=low
+
+ * Built for mysql-server updates on Ubuntu 12.04,12.10, and 13.10.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Wed, 29 Jan 2014 13:12:56 +0900
+
+mroonga (3.12-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Wed, 29 Jan 2014 00:00:00 +0900
+
+mroonga (3.11-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sun, 29 Dec 2013 00:00:00 +0900
+
+mroonga (3.10-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Fri, 29 Nov 2013 00:00:00 +0900
+
+mroonga (3.09-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Tue, 29 Oct 2013 00:00:00 +0900
+
+mroonga (3.08-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sun, 29 Sep 2013 00:00:00 +0900
+
+mroonga (3.07-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Thu, 29 Aug 2013 00:00:00 +0900
+
+mroonga (3.06-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Mon, 29 Jul 2013 00:00:00 +0900
+
+mroonga (3.05-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sat, 29 Jun 2013 00:00:00 +0900
+
+mroonga (3.04-2) unstable; urgency=low
+
+ * Built for mysql-server 5.5.31-0ubuntu0.12.04.2 on Ubuntu 12.04 (precise)
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Thu, 13 Jun 2013 00:00:00 +0900
+
+mroonga (3.04-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Wed, 29 May 2013 00:00:00 +0900
+
+mroonga (3.03-2) unstable; urgency=low
+
+ * Built for mysql-server 5.5.31+dfsg-0+wheezy1 on Debian wheezy
+ * Built for mysql-server 5.5.31+dfsg-1 on Debian unstable
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Thu, 16 May 2013 00:00:00 +0900
+
+mroonga (3.03-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Mon, 29 Apr 2013 00:00:00 +0900
+
+mroonga (3.02-2) unstable; urgency=low
+
+ * Built for mysql-server 5.5.29-0ubuntu0.12.04.2 on Ubuntu 12.04 (precise)
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Fri, 29 Mar 2013 22:15:39 +0900
+
+mroonga (3.02-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Fri, 29 Mar 2013 00:00:00 +0900
+
+mroonga (3.01-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Thu, 28 Feb 2013 00:00:00 +0900
+
+mroonga (3.00-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sat, 09 Feb 2013 00:00:00 +0900
+
+mroonga (2.10-2) unstable; urgency=low
+
+ * Built for mysql-server 5.5.29+dfsg-1 on Debian/unstable.
+ * Built for mysql-server 5.1.67-0ubuntu0.10.04.1 on Ubuntu 10.04(lucid).
+ * Built for mysql-server 5.1.67-0ubuntu0.11.10.1 on Ubuntu 11.10(oneiric).
+ * Built for mysql-server 5.5.29-0ubuntu0.12.04.1 on Ubuntu 12.04(precise).
+ * Built for mysql-server 5.5.29-0ubuntu0.12.10.1 on Ubuntu 12.10(quantal).
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Thu, 24 Jan 2013 10:28:16 +0900
+
+mroonga (2.10-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sat, 29 Dec 2012 00:00:00 +0900
+
+mroonga (2.09-2) unstable; urgency=low
+
+ * Built for mysql-server 5.5.28-0ubuntu0.12.10.2 on Ubuntu 12.10.
+ Reported by @watanabekiyokaz
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Wed, 12 Dec 2012 13:28:00 +0900
+
+mroonga (2.09-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Thu, 29 Nov 2012 00:00:00 +0900
+
+mroonga (2.08-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Mon, 29 Oct 2012 00:00:00 +0900
+
+mroonga (2.07-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sat, 29 Sep 2012 00:00:00 +0900
+
+mroonga (2.06-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Wed, 29 Aug 2012 00:00:00 +0900
+
+mroonga (2.05-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sun, 29 Jul 2012 00:00:00 +0900
+
+mroonga (2.04-1) unstable; urgency=low
+
+ * New upstream release.
+ * Ensure deleting mroonga plugin before install.
+ Suggested by Kazuhiro Isobe. Thanks!!!
+
+ -- Kouhei Sutou <kou@clear-code.com> Fri, 29 Jun 2012 00:00:00 +0900
+
+mroonga (2.03-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Tue, 29 May 2012 00:00:00 +0900
+
+mroonga (2.02-1) unstable; urgency=low
+
+ * New upstream release.
+ * Require groonga >= 2.0.2.
+
+ -- Kouhei Sutou <kou@clear-code.com> Sun, 29 Apr 2012 00:00:00 +0900
+
+mroonga (2.01-1) unstable; urgency=low
+
+ * New upstream release.
+ * Ensure plugin is uninstalled by closing all tables use mroonga.
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 29 Mar 2012 00:00:00 +0900
+
+mroonga (2.00-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Wed, 29 Feb 2012 00:00:00 +0900
+
+mroonga (1.20-1) unstable; urgency=low
+
+ * New upstream release.
+ * Add mysql-server-mroonga-compatible package for "groonga" storage engine.
+
+ -- Kouhei Sutou <kou@clear-code.com> Sun, 29 Jan 2012 00:00:00 +0900
+
+mroonga (1.11-1) unstable; urgency=low
+
+ * New upstream release.
+ * Change apparmor configuration file name:
+ mysql-server-groonga -> mysql-server-mroonga
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 29 Dec 2011 00:00:00 +0900
+
+mroonga (1.10-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Sat, 29 Oct 2011 00:00:00 +0900
+
+groonga-storage-engine (1.0.0-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 29 Sep 2011 00:00:00 +0900
+
+groonga-storage-engine (0.9-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Mon, 29 Aug 2011 00:00:00 +0900
+
+groonga-storage-engine (0.8-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Fri, 29 Jul 2011 00:00:00 +0900
+
+groonga-storage-engine (0.7-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Wed, 29 Jun 2011 00:00:00 +0900
+
+groonga-storage-engine (0.6-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Sun, 29 May 2011 00:00:00 +0900
+
+groonga-storage-engine (0.5-4) unstable; urgency=low
+
+ * fix a typo.
+
+ -- Kouhei Sutou <kou@clear-code.com> Tue, 30 Mar 2011 01:05:00 +0900
+
+groonga-storage-engine (0.5-3) unstable; urgency=low
+
+ * fix AppArmor files.
+
+ -- Kouhei Sutou <kou@clear-code.com> Tue, 30 Mar 2011 00:59:00 +0900
+
+groonga-storage-engine (0.5-2) unstable; urgency=low
+
+ * hook script fix.
+
+ -- Kouhei Sutou <kou@clear-code.com> Tue, 30 Mar 2011 00:58:00 +0900
+
+groonga-storage-engine (0.5-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Tue, 29 Mar 2011 00:00:00 +0900
+
+groonga-storage-engine (0.4-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Mon, 29 Nov 2010 00:00:00 +0900
+
+groonga-storage-engine (0.3-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Fri, 29 Oct 2010 16:34:04 +0900
+
+groonga-storage-engine (0.2-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Sat, 25 Sep 2010 14:52:49 +0900
+
+groonga-storage-engine (0.1-4) unstable; urgency=low
+
+ * follow configure option changes.
+
+ -- Kouhei Sutou <kou@cozmixng.org> Fri, 10 Sep 2010 08:45:53 +0900
+
+groonga-storage-engine (0.1-3) unstable; urgency=low
+
+ * Use HEAD.
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 02 Sep 2010 12:03:46 +0900
+
+groonga-storage-engine (0.1-2) unstable; urgency=low
+
+ * Built with groonga 1.0.0.
+
+ -- Kouhei Sutou <kou@cozmixng.org> Mon, 30 Aug 2010 13:26:25 +0900
+
+groonga-storage-engine (0.1-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Mon, 23 Aug 2010 13:52:01 +0900
diff --git a/storage/mroonga/packages/debian/compat b/storage/mroonga/packages/debian/compat
new file mode 100644
index 00000000000..ec635144f60
--- /dev/null
+++ b/storage/mroonga/packages/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/storage/mroonga/packages/debian/control.in b/storage/mroonga/packages/debian/control.in
new file mode 100644
index 00000000000..d6d03fa9a4e
--- /dev/null
+++ b/storage/mroonga/packages/debian/control.in
@@ -0,0 +1,51 @@
+Source: mroonga
+Section: database
+Priority: optional
+Maintainer: Kouhei Sutou <kou@clear-code.com>
+Build-Depends:
+ debhelper (>= 7.0.50),
+ autotools-dev,
+ pkg-config,
+ libgroonga-dev (>= @REQUIRED_GROONGA_VERSION@),
+ groonga-normalizer-mysql,
+ libmysqlclient-dev,
+ libmysqld-dev,
+ libssl-dev,
+ wget,
+ lsb-release
+Standards-Version: 3.9.1
+Homepage: http://mroonga.org/
+
+Package: mysql-server-mroonga
+Section: database
+Architecture: any
+Replaces: mysql-server-groonga (<< 1.10-1)
+Breaks: mysql-server-groonga (<< 1.10-1)
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ libgroonga0 (>= @REQUIRED_GROONGA_VERSION@),
+ mysql-server (= MYSQL_VERSION),
+ groonga-normalizer-mysql
+Description: A fast fulltext searchable storage engine for MySQL.
+ Mroonga is a fast fulltext searchable storage engine for MySQL.
+ It is based on Groonga, a fast fulltext search engine and column store.
+ Groonga is good at real time update.
+ .
+ This package provides a MySQL storage engine as a shared library.
+ This provides "mroonga" storage engine. It means you can use
+ "ENGINE = mroonga" in "CREATE TABLE".
+
+Package: mysql-server-mroonga-doc
+Section: doc
+Architecture: all
+Replaces: mysql-server-groonga-doc (<< 1.10-1)
+Breaks: mysql-server-groonga-doc (<< 1.10-1)
+Depends:
+ ${misc:Depends}
+Description: Documentation of Mroonga.
+ Mroonga is a fast fulltext searchable storage engine for MySQL.
+ It is based on Groonga, a fast fulltext search engine and column store.
+ Groonga is good at real time update.
+ .
+ This package provides documentation of Mroonga.
diff --git a/storage/mroonga/packages/debian/copyright b/storage/mroonga/packages/debian/copyright
new file mode 100644
index 00000000000..bb41984e8e4
--- /dev/null
+++ b/storage/mroonga/packages/debian/copyright
@@ -0,0 +1,27 @@
+This work was packaged for Debian by:
+
+ Kouhei Sutou <kou@clear-code.com> on Thu, 02 Sep 2010 13:51:56 +0900.
+
+It was downloaded:
+
+ <http://github.com/mroonga/mroonga/downloads>
+
+Upstream Author(s):
+
+ Tetsuro IKEDA <ikdttr at gmail.com>
+ Daijiro MORI <morita at razil. jp>
+ Tasuku SUENAGA <a at razil. jp>
+ Kouhei Sutou <kou at clear-code. com>
+
+Copyright:
+
+ Copyright(C) 2009-2010 Tetsuro IKEDA
+
+License:
+
+ LGPLv2.1
+
+ See `/usr/share/common-licenses/LGPL-2.1'.
+
+The Debian packaging is done by Kouhei Sutou <kou@clear-code.com> in 2010,
+and put into public domain, anyone can use it for any purpose.
diff --git a/storage/mroonga/packages/debian/mysql-server-mroonga-doc.install b/storage/mroonga/packages/debian/mysql-server-mroonga-doc.install
new file mode 100644
index 00000000000..ad2e27ef7dd
--- /dev/null
+++ b/storage/mroonga/packages/debian/mysql-server-mroonga-doc.install
@@ -0,0 +1 @@
+usr/share/doc/mysql-server-mroonga-doc/
diff --git a/storage/mroonga/packages/debian/mysql-server-mroonga.install b/storage/mroonga/packages/debian/mysql-server-mroonga.install
new file mode 100644
index 00000000000..03f64cfedb4
--- /dev/null
+++ b/storage/mroonga/packages/debian/mysql-server-mroonga.install
@@ -0,0 +1,3 @@
+usr/lib/mysql/plugin/ha_mroonga.so*
+usr/share/mroonga/*
+debian/apparmor/mysql-server-mroonga etc/apparmor.d/abstractions/
diff --git a/storage/mroonga/packages/debian/mysql-server-mroonga.postinst b/storage/mroonga/packages/debian/mysql-server-mroonga.postinst
new file mode 100755
index 00000000000..9a3db8784a2
--- /dev/null
+++ b/storage/mroonga/packages/debian/mysql-server-mroonga.postinst
@@ -0,0 +1,72 @@
+#! /bin/sh
+
+set -e
+
+prevver="$2"
+
+install_plugin() {
+ cat /usr/share/mroonga/install.sql | \
+ mysql --defaults-file=/etc/mysql/debian.cnf || true
+}
+
+install_apparmor() {
+ mysql_apparmor_profile_name=usr.sbin.mysqld
+ mysql_apparmor_profile=/etc/apparmor.d/${mysql_apparmor_profile_name}
+ mysql_local_apparmor_profile=/etc/apparmor.d/local/${mysql_apparmor_profile_name}
+ apparmor_profile_name=mysql-server-mroonga
+ include_profile="#include <abstractions/${apparmor_profile_name}>"
+ local_apparmor_profile=/etc/apparmor.d/local/${apparmor_profile_name}
+ if test -f "${mysql_local_apparmor_profile}"; then
+ if ! grep -q "${include_profile}" "${mysql_local_apparmor_profile}"; then
+ echo >> "${mysql_local_apparmor_profile}"
+ echo "${include_profile}" >> "${mysql_local_apparmor_profile}"
+ fi
+ else
+ mysql_abstraction_apparmor_profile=/etc/apparmor.d/abstractions/mysql
+ mysql_plugin_dir=/usr/lib/mysql/plugin
+ if test -f "${mysql_abstraction_apparmor_profile}" && \
+ ! grep -q "${mysql_plugin_dir}" \
+ "${mysql_abstraction_apparmor_profile}"; then
+ # For Lucid.
+ cat <<EOF >> "${mysql_abstraction_apparmor_profile}"
+
+# ${apparmor_profile_name}: START
+# Added by mysql-server-mroonga.
+${mysql_plugin_dir}/ r,
+${mysql_plugin_dir}/*.so* mr,
+${include_profile}
+# ${apparmor_profile_name}: END
+EOF
+ fi
+ fi
+
+ if ! test -e "$local_apparmor_profile"; then
+ mkdir -p $(dirname "$local_apparmor_profile")
+ cat <<EOF > "$local_apparmor_profile"
+# Site-specific additions and overrides for ${apparmor_profile_name}.
+# For more details, please see /etc/apparmor.d/local/README.
+EOF
+ fi
+
+ if aa-status --enabled 2>/dev/null; then
+ apparmor_parser -r -T -W "${mysql_apparmor_profile}" || true
+ fi
+
+ true
+}
+
+case "$1" in
+ configure)
+ install_apparmor
+ install_plugin
+ ;;
+ abort-upgrade|abort-deconfigure|abort-remove)
+ :
+ ;;
+ *)
+ echo "Called with unknown argument $1, bailing out."
+ exit 1
+ ;;
+esac
+
+#DEBHELPER#
diff --git a/storage/mroonga/packages/debian/mysql-server-mroonga.postrm b/storage/mroonga/packages/debian/mysql-server-mroonga.postrm
new file mode 100755
index 00000000000..84d7f1ef4ab
--- /dev/null
+++ b/storage/mroonga/packages/debian/mysql-server-mroonga.postrm
@@ -0,0 +1,38 @@
+#! /bin/sh
+
+set -e
+
+if [ "$1" = "purge" ]; then
+ mysql_apparmor_profile_name=usr.sbin.mysqld
+ mysql_apparmor_profile=/etc/apparmor.d/${mysql_apparmor_profile_name}
+ mysql_local_apparmor_profile=/etc/apparmor.d/local/${mysql_apparmor_profile_name}
+ mysql_abstraction_apparmor_profile=/etc/apparmor.d/abstractions/mysql
+ apparmor_profile_name=mysql-server-mroonga
+ if test -f "${mysql_local_apparmor_profile}"; then
+ include_profile="#include <abstractions/${apparmor_profile_name}>"
+ if grep -q "${include_profile}" "${mysql_local_apparmor_profile}"; then
+ sed -i'' -e "s,${include_profile},," \
+ "${mysql_local_apparmor_profile}"
+ fi
+ else
+ start_marker_re="^# ${apparmor_profile_name}: START$"
+ end_marker_re="^# ${apparmor_profile_name}: END$"
+ if test -f "${mysql_abstraction_apparmor_profile}" && \
+ grep -q "${start_marker_re}" \
+ "${mysql_abstraction_apparmor_profile}"; then
+ sed -i'' -e "/${start_marker_re}/,/${end_marker_re}/d" \
+ "${mysql_abstraction_apparmor_profile}"
+ fi
+ fi
+
+ rm -f "/etc/apparmor.d/local/${apparmor_profile_name}" || true
+ rmdir /etc/apparmor.d/local 2>/dev/null || true
+
+ if aa-status --enabled 2>/dev/null; then
+ apparmor_parser -r -T -W "${mysql_apparmor_profile}" || true
+ fi
+fi
+
+#DEBHELPER#
+
+exit 0
diff --git a/storage/mroonga/packages/debian/mysql-server-mroonga.prerm b/storage/mroonga/packages/debian/mysql-server-mroonga.prerm
new file mode 100755
index 00000000000..7fad990d75f
--- /dev/null
+++ b/storage/mroonga/packages/debian/mysql-server-mroonga.prerm
@@ -0,0 +1,10 @@
+#! /bin/sh
+
+set -e
+
+cat /usr/share/mroonga/uninstall.sql | \
+ mysql --defaults-file=/etc/mysql/debian.cnf || true
+
+#DEBHELPER#
+
+exit 0
diff --git a/storage/mroonga/packages/debian/rules b/storage/mroonga/packages/debian/rules
new file mode 100755
index 00000000000..8bcd911a113
--- /dev/null
+++ b/storage/mroonga/packages/debian/rules
@@ -0,0 +1,35 @@
+#!/usr/bin/make -f
+# -*- makefile-gmake -*-
+#
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+# This has to be exported to make some magic below work.
+export DH_OPTIONS
+
+export MYSQL_VERSION := $(shell apt-cache show mysql-server-5.5 | grep Version | head -n 1 | awk '{print $$2}' | awk -F '-' '{print $$1}')
+
+%:
+ dh $@
+
+override_dh_auto_configure:
+ if [ "$$(lsb_release --id --short)" = "Ubuntu" ]; then \
+ base_url=http://archive.ubuntu.com/ubuntu; \
+ else \
+ base_url=http://ftp.debian.org/debian; \
+ fi; \
+ wget $${base_url}/pool/main/m/mysql-5.5/mysql-5.5_$(MYSQL_VERSION).orig.tar.gz
+ tar xf mysql-5.5_$(MYSQL_VERSION).orig.tar.gz
+ dh_auto_configure -- --with-mysql-source=./mysql-$(MYSQL_VERSION)
+
+# disable 'make check'.
+override_dh_auto_test:
+
+override_dh_install:
+ mv debian/tmp/usr/share/doc/mroonga/ \
+ debian/tmp/usr/share/doc/mysql-server-mroonga-doc/
+ dh_install
+# if test -x /usr/bin/dh_apparmor; then \
+# dh_apparmor \
+# -pmysql-server-mroonga \
+# --profile-name=usr.lib.mysql.plugin.ha_mroonga; \
+# fi
diff --git a/storage/mroonga/packages/rpm/Makefile.am b/storage/mroonga/packages/rpm/Makefile.am
new file mode 100644
index 00000000000..471ea985921
--- /dev/null
+++ b/storage/mroonga/packages/rpm/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = \
+ centos \
+ fedora
diff --git a/storage/mroonga/packages/rpm/centos/Makefile.am b/storage/mroonga/packages/rpm/centos/Makefile.am
new file mode 100644
index 00000000000..e7b22cfa025
--- /dev/null
+++ b/storage/mroonga/packages/rpm/centos/Makefile.am
@@ -0,0 +1,9 @@
+EXTRA_DIST = \
+ mysql55-mroonga.spec.in \
+ mysql56-community-mroonga.spec.in \
+ mariadb-mroonga.spec.in
+
+noinst_DATA = \
+ mysql55-mroonga.spec \
+ mysql56-community-mroonga.spec \
+ mariadb-mroonga.spec
diff --git a/storage/mroonga/packages/rpm/centos/mariadb-mroonga.spec.in b/storage/mroonga/packages/rpm/centos/mariadb-mroonga.spec.in
new file mode 100644
index 00000000000..5ed56b433b2
--- /dev/null
+++ b/storage/mroonga/packages/rpm/centos/mariadb-mroonga.spec.in
@@ -0,0 +1,375 @@
+%define mariadb_epoch_default 1
+%define mariadb_version_default 5.5.37
+%define mariadb_release_default 1
+%define mariadb_dist_default .el7_0
+%define mariadb_download_base_url_default http://vault.centos.org/7.0.1406/updates/Source/SPackages
+%define mariadb_spec_file_default mariadb.spec
+
+%{!?mariadb_epoch:%define mariadb_epoch %{mariadb_epoch_default}}
+%{!?mariadb_version:%define mariadb_version %{mariadb_version_default}}
+%{!?mariadb_release:%define mariadb_release %{mariadb_release_default}}
+%{!?mariadb_dist:%define mariadb_dist %{mariadb_dist_default}}
+%{!?mariadb_download_base_url:%define mariadb_download_base_url %{mariadb_download_base_url_default}}
+%{!?mariadb_spec_file:%define mariadb_spec_file %{mariadb_spec_file_default}}
+
+%define mariadb_package_version %{mariadb_epoch}:%{mariadb_version}-%{mariadb_release}%{mariadb_dist}
+
+%define groonga_required_version @REQUIRED_GROONGA_VERSION@
+
+Name: mariadb-mroonga
+Version: @VERSION@
+Release: 1%{?dist}
+Summary: A fast fulltext searchable storage engine for MariaDB
+
+Group: Applications/Databases
+License: LGPLv2.1
+URL: http://mroonga.org/
+Source0: http://packages.groonga.org/source/mroonga/mroonga-%{version}.tar.gz
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n)
+BuildRequires: groonga-devel >= %{groonga_required_version}
+BuildRequires: groonga-normalizer-mysql-devel
+BuildRequires: wget
+BuildRequires: mariadb-devel
+Requires: mariadb-server = %{mariadb_package_version}
+Requires: mariadb = %{mariadb_package_version}
+Requires: groonga-libs >= %{groonga_required_version}
+Requires: groonga-normalizer-mysql
+
+%description
+Mroonga is a fast fulltext searchable storage plugin for MariaDB.
+It is based on Groonga that is a fast fulltext search engine and
+column store. Groonga is good at real-time update.
+
+%package doc
+Summary: Documentation for Mroonga
+Group: Documentation
+License: LGPLv2.1
+
+%description doc
+Documentation for Mroonga
+
+
+%prep
+%setup -q -n mroonga-%{version}
+
+mariadb_full_version=%{mariadb_version}-%{mariadb_release}%{mariadb_dist}
+srpm=mariadb-${mariadb_full_version}.src.rpm
+if [ ! -f ../../SRPMS/$srpm ]; then
+ wget --continue -O ../../SRPMS/$srpm %{mariadb_download_base_url}/$srpm
+ rpm -Uvh ../../SRPMS/$srpm
+ rm ../../SRPMS/$srpm
+fi
+
+%build
+mariadb_source=../mariadb-%{mariadb_version}
+if [ ! -d ${mariadb_source} ]; then
+ rpmbuild -bc \
+ --define 'runselftest 0' \
+ --define 'optflags -O0' \
+ ../../SPECS/%{mariadb_spec_file}
+fi
+%configure \
+ --disable-static \
+ --with-mysql-source=${mariadb_source} \
+ %{?mroonga_configure_options}
+make %{?_smp_mflags}
+
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+rm $RPM_BUILD_ROOT%{_libdir}/mysql/plugin/*.la
+mv $RPM_BUILD_ROOT%{_datadir}/doc/mroonga/ mysql-mroonga-doc/
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+if /usr/bin/mysql -u root -e "quit"; then
+ password_option=""
+else
+ password_option="-p"
+fi
+current_version=0
+version=$(echo %{groonga_required_version} | sed -e 's/\.//g')
+required_version=$(expr $version)
+version=$(/usr/bin/mysql -e "SHOW VARIABLES LIKE 'mroonga_libgroonga_version'" | \
+ grep mroonga | cut -f 2 | sed -e 's/\.//g')
+if [ -n "$version" ]; then
+ current_version=$(expr $version)
+fi
+install_sql=%{_datadir}/mroonga/install.sql
+uninstall_sql=%{_datadir}/mroonga/uninstall.sql
+
+if [ "$1" = 2 ] ; then
+ if [ $current_version -lt $required_version ]; then
+ command="/usr/bin/mysql -u root $password_option"
+ echo "run the following command after restarting MySQL server:";
+ echo " $command < ${uninstall_sql}"
+ echo " $command < ${install_sql}"
+ exit 0
+ else
+ command="/usr/bin/mysql -u root $password_option"
+ command="${command} < ${uninstall_sql}"
+ echo $command
+ eval $command || \
+ (echo "run the following command to unregister Mroonga:"; \
+ echo " $command")
+ fi
+fi
+
+command="/usr/bin/mysql -u root $password_option < ${install_sql}"
+echo $command
+eval $command || \
+ (echo "run the following command to register Mroonga:"; \
+ echo " $command")
+
+%preun
+uninstall_sql=%{_datadir}/mroonga/uninstall.sql
+
+if mysql -u root -e "quit"; then
+ password_option=""
+else
+ password_option="-p"
+fi
+if [ "$1" = 0 ]; then
+ command="/usr/bin/mysql -u root $password_option < ${uninstall_sql}"
+ echo $command
+ eval $command || \
+ (echo "run the following command to unregister Mroonga:"; \
+ echo " $command")
+fi
+
+%files
+%defattr(-,root,root,-)
+%{_libdir}/mysql/plugin/
+%{_datadir}/mroonga/*
+%{_datadir}/man/man1/*
+%{_datadir}/man/*/man1/*
+
+%files doc
+%defattr(-,root,root,-)
+%doc README COPYING
+%doc mysql-mroonga-doc/*
+
+%changelog
+* Fri Aug 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.05-1
+- new upstream release.
+
+* Thu Aug 14 2014 Kouhei Sutou <kou@clear-code.com> - 4.04-4
+- build MariaDB for libmysqlservices.a.
+
+* Thu Aug 14 2014 Kouhei Sutou <kou@clear-code.com> - 4.04-3
+- support epoch in MariaDB.
+
+* Wed Aug 13 2014 Kouhei Sutou <kou@clear-code.com> - 4.04-2
+- build against mariadb-5.5.37-1.el7_0.
+
+* Sun Aug 10 2014 Kouhei Sutou <kou@clear-code.com> - 4.04-1
+- initial packaging for CentOS 7 based on mysql-mroogna package.
+
+* Tue Jul 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.04-1
+- new upstream release.
+
+* Thu May 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.03-1
+- new upstream release.
+
+* Tue Apr 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.02-1
+- new upstream release.
+
+* Sat Mar 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.01-1
+- new upstream release.
+
+* Thu Feb 13 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.00-2
+- use MySQL 5.1.73-3 on CentOS 6.
+
+* Sun Feb 09 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.00-1
+- new upstream release.
+
+* Wed Jan 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 3.12-1
+- new upstream release.
+
+* Sun Dec 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.11-1
+- new upstream release.
+
+* Sat Dec 7 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.10-2
+- use MySQL 5.1.71-1 on CentOS 6.
+
+* Fri Nov 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.10-1
+- new upstream release.
+
+* Tue Oct 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.09-1
+- new upstream release.
+
+* Sun Sep 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.08-1
+- new upstream release.
+- use MySQL 5.6.14-1 on CentOS 5.
+
+* Wed Sep 4 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.07-2
+- fix a bug that mroonga is removed accidentally on upgrade #1918.
+ Reported by @ceekz. Thanks!!!
+
+* Thu Aug 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.07-1
+- new upstream release.
+- use MySQL 5.6.13-1 on CentOS 5.
+
+* Mon Jul 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.06-1
+- new upstream release.
+- use MySQL 5.6.12-2 on CentOS 5.
+
+* Sat Jun 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.05-1
+- new upstream release.
+- use MySQL 5.6.12 on CentOS 5.
+
+* Wed May 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.04-1
+- new upstream release.
+
+* Fri May 10 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.03-2
+- use MySQL 5.6.11-2 on CentOS 5. see http://bugs.mysql.com/bug.php?id=69027
+ Reported by Y.Kentaro. Thanks!!!
+
+* Mon Apr 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.03-1
+- new upstream release.
+
+* Fri Mar 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.02-0
+- new upstream release.
+
+* Thu Feb 28 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.01-0
+- new upstream release.
+
+* Sat Feb 09 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.00-0
+- new upstream release.
+- require groonga 3.0.0 or later
+
+* Tue Feb 05 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 2.10-2
+- use MySQL 5.1.67-1 on CentOS 6.
+ Reported by wakisuke.ua. Thanks!!!
+
+* Sat Dec 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.10-0
+- new upstream release.
+
+* Mon Dec 10 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.09-1
+- use MySQL 5.1.66-2 on CentOS 6.
+ Reported by wakisuke.ua. Thanks!!!
+
+* Thu Nov 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.09-0
+- new upstream release.
+- use MySQL 5.5.28 on CentOS 5.
+- use MySQL 5.1.66 on CentOS 6.
+
+* Mon Oct 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.08-0
+- new upstream release.
+- add missing "DROP FUNCTION mroonga_snippet".
+ Reported by @tokuhy. Thanks!!!
+
+* Sat Sep 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.07-0
+- new upstream release.
+
+* Wed Aug 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.06-0
+- new upstream release.
+- make MySQL spec file name customizable.
+- make mroonga configure options customizable.
+- add missing mysql-devel BuildRequires. Reported by wing. Thanks!!!
+- use MySQL 5.5.27.
+
+* Sun Jul 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.05-0
+- new upstream release.
+- use MySQL 5.5.25a.
+
+* Fri Jun 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.04-0
+- new upstream release.
+- ensure deleting mroonga plugin before install.
+ Suggested by Kazuhiro Isobe. Thanks!!!
+- use MySQL 5.5.25.
+
+* Tue May 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.03-0
+- new upstream release.
+- use MySQL 5.5.24.
+- make mysql_* variables customizable
+- require groonga 2.0.3 or later.
+
+* Sun Apr 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.02-0
+- new upstream release.
+- use MySQL 5.5.23.
+- require groonga 2.0.2 or later.
+
+* Thu Mar 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.01-0
+- new upstream release.
+- ensure plugin is uninstalled by closing all tables use mroonga.
+
+* Wed Feb 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.00-0
+- new upstream release.
+- always install/uninstall plugin.
+- use MySQL 5.1.61 and 5.5.21.
+- require groonga 2.0.0 or later.
+
+* Sun Jan 29 2012 Kouhei Sutou <kou@clear-code.com> - 1.20-0
+- new upstream release.
+- require groonga 1.3.0.
+- groonga -> mroonga.
+- use MySQL 5.5.20.
+
+* Thu Dec 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.11-0
+- new upstream release.
+
+* Sat Oct 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.10-0
+- new upstream release.
+- groonga storage engine -> mroonga.
+
+* Thu Sep 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.0.0-0
+- new upstream release.
+
+* Mon Aug 29 2011 Kouhei Sutou <kou@clear-code.com> - 0.9-0
+- new upstream release.
+
+* Fri Jul 29 2011 Kouhei Sutou <kou@clear-code.com> - 0.8-0
+- new upstream release.
+
+* Wed Jun 29 2011 Kouhei Sutou <kou@clear-code.com> - 0.7-0
+- new upstream release.
+
+* Sun May 29 2011 Kouhei Sutou <kou@clear-code.com> - 0.6-0
+- new upstream release.
+
+* Tue May 17 2011 Kouhei Sutou <kou@clear-code.com> - 0.5-2
+- use MySQL 5.5.12.
+
+* Tue Mar 29 2011 Kouhei Sutou <kou@clear-code.com> - 0.5-1
+- new upstream release.
+
+* Sat Jan 29 2011 Kouhei Sutou <kou@clear-code.com> - 0.4-4
+- do not remove plugin on upgrade.
+
+* Wed Jan 12 2011 Kouhei Sutou <kou@clear-code.com> - 0.4-3
+- rebuild without debug symbol.
+
+* Thu Dec 30 2010 Kouhei Sutou <kou@clear-code.com> - 0.4-2
+- use MySQL 5.5.8-1.
+- fix SQL literal notation.
+
+* Mon Nov 29 2010 Kouhei Sutou <kou@clear-code.com> - 0.4-1
+- use the latest MySQL.
+- new upstream release.
+
+* Sun Nov 21 2010 Kouhei Sutou <kou@clear-code.com> - 0.3-2
+- install user define function.
+
+* Fri Oct 29 2010 Kouhei Sutou <kou@clear-code.com> - 0.3-1
+- new upstream release.
+
+* Fri Oct 08 2010 Kouhei Sutou <kou@clear-code.com> - 0.2-2
+- specify target MySQL version.
+- use %{version}.
+
+* Wed Sep 29 2010 Kouhei Sutou <kou@clear-code.com> - 0.2-1
+- new upstream release.
+
+* Sun Sep 12 2010 Kouhei Sutou <kou@clear-code.com> - 0.1-3
+- require MySQL-client-community.
+
+* Fri Sep 10 2010 Kouhei Sutou <kou@clear-code.com> - 0.1-2
+- use MySQL-devel-community.
+
+* Fri Sep 03 2010 Kouhei Sutou <kou@clear-code.com> - 0.1-1
+- initial packaging for CentOS.
diff --git a/storage/mroonga/packages/rpm/centos/mysql55-mroonga.spec.in b/storage/mroonga/packages/rpm/centos/mysql55-mroonga.spec.in
new file mode 100644
index 00000000000..af2a026c701
--- /dev/null
+++ b/storage/mroonga/packages/rpm/centos/mysql55-mroonga.spec.in
@@ -0,0 +1,200 @@
+%{?scl:%scl_package mroonga}
+%{!?scl:%global pkg_name %{name}}
+%{!?centos_ver:%define centos_ver 5}
+
+%if %{centos_ver} == 6
+%define mysql_version_default 5.5.37
+%define mysql_release_default 5
+%define mysql_dist_default el6.centos.alt
+%define mysql_download_base_url_default http://vault.centos.org/6.5/SCL/Source/SPackages
+%define mysql_spec_file_default mysql.spec
+%else
+%define mysql_version_default 5.5.37
+%define mysql_release_default 1
+%define mysql_dist_default el5
+%define mysql_download_base_url_default http://vault.centos.org/5.10/updates/SRPMS
+%define mysql_spec_file_default mysql.spec
+%endif
+
+%{!?mysql_version:%define mysql_version %{mysql_version_default}}
+%{!?mysql_release:%define mysql_release %{mysql_release_default}}
+%{!?mysql_dist:%define mysql_dist %{mysql_dist_default}}
+%{!?mysql_download_base_url:%define mysql_download_base_url %{mysql_download_base_url_default}}
+%{!?mysql_spec_file:%define mysql_spec_file %{mysql_spec_file_default}}
+
+%define groonga_required_version @REQUIRED_GROONGA_VERSION@
+
+Name: %{?scl_prefix}mroonga
+Version: @VERSION@
+Release: 1%{?dist}
+Summary: A fast fulltext searchable storage engine for MySQL
+
+Group: Applications/Databases
+License: LGPLv2.1
+URL: http://mroonga.org/
+Source0: http://packages.groonga.org/source/mroonga/mroonga-%{version}.tar.gz
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n)
+BuildRequires: groonga-devel >= %{groonga_required_version}
+BuildRequires: groonga-normalizer-mysql-devel
+BuildRequires: wget
+BuildRequires: which
+BuildRequires: mysql55-mysql-devel
+BuildRequires: mysql55-build
+Requires: mysql55-mysql-server = %{mysql_version}-%{mysql_release}.%{mysql_dist}
+Requires: mysql55-mysql = %{mysql_version}-%{mysql_release}.%{mysql_dist}
+Requires: groonga-libs >= %{groonga_required_version}
+Requires: groonga-normalizer-mysql
+%{?scl:Requires: %scl_runtime}
+
+%description
+Mroonga is a fast fulltext searchable storage plugin for MySQL.
+It is based on Groonga that is a fast fulltext search engine and
+column store. Groonga is good at real-time update.
+
+%package doc
+Summary: Documentation for Mroonga
+Group: Documentation
+License: LGPLv2.1
+
+%description doc
+Documentation for Mroonga
+
+
+%prep
+%setup -q -n %{pkg_name}-%{version}
+
+mysql_full_version=%{mysql_version}-%{mysql_release}.%{mysql_dist}
+srpm=mysql55-mysql-${mysql_full_version}.src.rpm
+if [ ! -f ../../SRPMS/$srpm ]; then
+ wget --continue -O ../../SRPMS/$srpm %{mysql_download_base_url}/$srpm
+ rpm -Uvh ../../SRPMS/$srpm
+fi
+
+%build
+mysql_source=../mysql-%{mysql_version}
+if [ ! -d ${mysql_source} ]; then
+ specs_dir=
+ MYSQL_RPMBUILD_TEST=no rpmbuild -bp \
+ --define 'runselftest 0' \
+ --define 'optflags -O0' \
+ ../../SPECS/%{mysql_spec_file}
+fi
+%configure --disable-static --with-mysql-source=${mysql_source} \
+ --disable-fast-mutexes \
+ --with-mysql-config=`scl enable mysql55 'which mysql_config'` \
+ %{?mroonga_configure_options}
+make %{?_smp_mflags}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+rm $RPM_BUILD_ROOT%{_libdir}/mysql/plugin/*.la
+mv $RPM_BUILD_ROOT%{_datadir}/doc/mroonga/ mysql-mroonga-doc/
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+mysql_command=`scl enable mysql55 'which mysql'`
+password_option=""
+$mysql_command -u root -e "quit"
+if [ $? -ne 0 ]; then
+ password_option="-p"
+fi
+current_version=0
+version=`echo %{groonga_required_version} | sed -e 's/\.//g'`
+required_version=`expr $version`
+version=`$mysql_command -e "SHOW VARIABLES LIKE 'mroonga_libgroonga_version'" | \
+ grep mroonga | cut -f 2 | sed -e 's/\.//g'`
+if [ -n "$version" ]; then
+ current_version=`expr $version`
+fi
+install_sql=%{_datadir}/mroonga/install.sql
+uninstall_sql=%{_datadir}/mroonga/uninstall.sql
+
+if [ "$1" = 2 ] ; then
+ if [ $current_version -lt $required_version ]; then
+ command="$mysql_command -u root $password_option"
+ echo "run the following command after restarting MySQL server:";
+ echo " $command < ${uninstall_sql}"
+ echo " $command < ${install_sql}"
+ exit 0
+ else
+ command="$mysql_command -u root $password_option < ${uninstall_sql}"
+ echo $command
+ eval $command || \
+ (echo "run the following command to unregister Mroonga:"; \
+ echo " $command")
+ fi
+fi
+command="$mysql_command -u root $password_option < ${install_sql}"
+echo $command
+eval $command || \
+ (echo "run the following command to register Mroonga:"; \
+ echo " $command")
+
+%preun
+uninstall_sql=%{_datadir}/mroonga/uninstall.sql
+mysql_command=`scl enable mysql55 'which mysql'`
+if $mysql_command -u root -e "quit"; then
+ password_option=""
+else
+ password_option="-p"
+fi
+if [ "$1" = 0 ]; then
+ command="$mysql_command -u root $password_option < ${uninstall_sql}"
+ echo $command
+ eval $command || \
+ (echo "run the following command to unregister Mroonga:"; \
+ echo " $command")
+fi
+
+%files
+%defattr(-,root,root,-)
+%{_libdir}/mysql/plugin/
+%{_datadir}/mroonga/*
+%{_datadir}/man/man1/*
+%{_datadir}/man/*/man1/*
+
+%files doc
+%defattr(-,root,root,-)
+%doc README COPYING
+%doc mysql-mroonga-doc/*
+
+%changelog
+* Fri Aug 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.05-1
+- new upstream release.
+
+* Tue Jul 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.04-1
+- new upstream release.
+
+* Thu May 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.03-2
+- build against MySQL 5.6.37. Reported by YOSHIDA Mitsuo. Thanks!!!
+
+* Thu May 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.03-1
+- new upstream release.
+
+* Tue Apr 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.02-1
+- new upstream release.
+
+* Sat Mar 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.01-1
+- new upstream release.
+
+* Thu Mar 06 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.00-2
+- use MySQL 5.5.36 on CentOS 5.
+
+* Sun Feb 09 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.00-1
+- new upstream release.
+
+* Wed Jan 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 3.12-1
+- new upstream release.
+
+* Sun Dec 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.11-1
+- new upstream release.
+
+* Fri Nov 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.10-1
+- new upstream release.
+
+* Tue Oct 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.09-1
+- initial packaging for MySQL 5.5 on CentOS 5.
diff --git a/storage/mroonga/packages/rpm/centos/mysql56-community-mroonga.spec.in b/storage/mroonga/packages/rpm/centos/mysql56-community-mroonga.spec.in
new file mode 100644
index 00000000000..6cd5d30cc84
--- /dev/null
+++ b/storage/mroonga/packages/rpm/centos/mysql56-community-mroonga.spec.in
@@ -0,0 +1,198 @@
+%{!?centos_ver:%define centos_ver 6}
+
+%if %{centos_ver} == 7
+%define mysql_version_default 5.6.20
+%define mysql_release_default 4
+%define mysql_dist_default el7
+%define mysql_download_base_url_default http://repo.mysql.com/yum/mysql-5.6-community/el/7/SRPMS
+%define mysql_spec_file_default mysql.spec
+%else
+%define mysql_version_default 5.6.20
+%define mysql_release_default 4
+%define mysql_dist_default el6
+%define mysql_download_base_url_default http://repo.mysql.com/yum/mysql-5.6-community/el/6/SRPMS
+%define mysql_spec_file_default mysql.spec
+%endif
+
+%{!?mysql_version:%define mysql_version %{mysql_version_default}}
+%{!?mysql_release:%define mysql_release %{mysql_release_default}}
+%{!?mysql_dist:%define mysql_dist %{mysql_dist_default}}
+%{!?mysql_download_base_url:%define mysql_download_base_url %{mysql_download_base_url_default}}
+%{!?mysql_spec_file:%define mysql_spec_file %{mysql_spec_file_default}}
+
+%define groonga_required_version @REQUIRED_GROONGA_VERSION@
+
+Name: mysql-community-mroonga
+Version: @VERSION@
+Release: 1%{?dist}
+Summary: A fast fulltext searchable storage engine for MySQL
+
+Group: Applications/Databases
+License: LGPLv2.1
+URL: http://mroonga.org/
+Source0: http://packages.groonga.org/source/mroonga/mroonga-%{version}.tar.gz
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n)
+BuildRequires: groonga-devel >= %{groonga_required_version}
+BuildRequires: groonga-normalizer-mysql-devel
+BuildRequires: wget
+BuildRequires: which
+BuildRequires: gcc, gcc-c++
+BuildRequires: mysql-community-devel
+Requires: mysql-community-server = %{mysql_version}-%{mysql_release}.%{mysql_dist}
+Requires: mysql-community-client = %{mysql_version}-%{mysql_release}.%{mysql_dist}
+Requires: groonga-libs >= %{groonga_required_version}
+Requires: groonga-normalizer-mysql
+
+%description
+Mroonga is a fast fulltext searchable storage plugin for MySQL.
+It is based on Groonga that is a fast fulltext search engine and
+column store. Groonga is good at real-time update.
+
+%package doc
+Summary: Documentation for Mroonga
+Group: Documentation
+License: LGPLv2.1
+
+%description doc
+Documentation for Mroonga
+
+
+%prep
+%setup -q -n mroonga-%{version}
+
+mysql_full_version=%{mysql_version}-%{mysql_release}.%{mysql_dist}
+srpm=mysql-community-${mysql_full_version}.src.rpm
+if [ ! -f ../../SRPMS/$srpm ]; then
+ wget --continue -O ../../SRPMS/$srpm %{mysql_download_base_url}/$srpm
+ rpm -Uvh ../../SRPMS/$srpm
+fi
+
+%build
+mysql_source=../mysql-%{mysql_version}/mysql-%{mysql_version}
+if [ ! -d ${mysql_source} ]; then
+ specs_dir=
+ MYSQL_RPMBUILD_TEST=no rpmbuild -bp \
+ --define 'runselftest 0' \
+ --define 'optflags -O0' \
+ ../../SPECS/%{mysql_spec_file}
+fi
+%configure --disable-static --with-mysql-source=${mysql_source} \
+ %{?mroonga_configure_options}
+make %{?_smp_mflags}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+rm $RPM_BUILD_ROOT%{_libdir}/mysql/plugin/*.la
+mv $RPM_BUILD_ROOT%{_datadir}/doc/mroonga/ mysql-mroonga-doc/
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+mysql_command=`which mysql`
+password_option=""
+$mysql_command -u root -e "quit"
+if [ $? -ne 0 ]; then
+ password_option="-p"
+fi
+current_version=0
+version=`echo %{groonga_required_version} | sed -e 's/\.//g'`
+required_version=`expr $version`
+version=`$mysql_command -e "SHOW VARIABLES LIKE 'mroonga_libgroonga_version'" | \
+ grep mroonga | cut -f 2 | sed -e 's/\.//g'`
+if [ -n "$version" ]; then
+ current_version=`expr $version`
+fi
+install_sql=%{_datadir}/mroonga/install.sql
+uninstall_sql=%{_datadir}/mroonga/uninstall.sql
+
+if [ "$1" = 2 ] ; then
+ if [ $current_version -lt $required_version ]; then
+ command="$mysql_command -u root $password_option"
+ echo "run the following command after restarting MySQL server:";
+ echo " $command < ${uninstall_sql}"
+ echo " $command < ${install_sql}"
+ exit 0
+ else
+ command="$mysql_command -u root $password_option < ${uninstall_sql}"
+ echo $command
+ eval $command || \
+ (echo "run the following command to unregister Mroonga:"; \
+ echo " $command")
+ fi
+fi
+command="$mysql_command -u root $password_option < ${install_sql}"
+echo $command
+eval $command || \
+ (echo "run the following command to register Mroonga:"; \
+ echo " $command")
+
+%preun
+uninstall_sql=%{_datadir}/mroonga/uninstall.sql
+mysql_command=`which mysql`
+if $mysql_command -u root -e "quit"; then
+ password_option=""
+else
+ password_option="-p"
+fi
+if [ "$1" = 0 ]; then
+ command="$mysql_command -u root $password_option < ${uninstall_sql}"
+ echo $command
+ eval $command || \
+ (echo "run the following command to unregister Mroonga:"; \
+ echo " $command")
+fi
+
+%files
+%defattr(-,root,root,-)
+%{_libdir}/mysql/plugin/
+%{_datadir}/mroonga/*
+%{_datadir}/man/man1/*
+%{_datadir}/man/*/man1/*
+
+%files doc
+%defattr(-,root,root,-)
+%doc README COPYING
+%doc mysql-mroonga-doc/*
+
+%changelog
+* Fri Aug 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.05-1
+- new upstream release.
+
+* Sat Aug 09 2014 Eiichi Sato <miko@cafelounge.net> - 4.04-2
+- build against MySQL 5.6.20-4 on MySQL yum repository.
+
+* Tue Jul 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.04-1
+- new upstream release.
+
+* Thu May 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.03-2
+- build against MySQL 5.6.37. Reported by YOSHIDA Mitsuo. Thanks!!!
+
+* Thu May 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.03-1
+- new upstream release.
+
+* Tue Apr 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.02-1
+- new upstream release.
+
+* Sat Mar 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.01-1
+- new upstream release.
+
+* Thu Mar 06 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.00-2
+- use MySQL 5.5.36 on CentOS 5.
+
+* Sun Feb 09 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.00-1
+- new upstream release.
+
+* Wed Jan 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 3.12-1
+- new upstream release.
+
+* Sun Dec 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.11-1
+- new upstream release.
+
+* Fri Nov 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.10-1
+- new upstream release.
+
+* Tue Oct 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.09-1
+- initial packaging for MySQL 5.5 on CentOS 5.
diff --git a/storage/mroonga/packages/rpm/fedora/Makefile.am b/storage/mroonga/packages/rpm/fedora/Makefile.am
new file mode 100644
index 00000000000..df9df6100e9
--- /dev/null
+++ b/storage/mroonga/packages/rpm/fedora/Makefile.am
@@ -0,0 +1,2 @@
+EXTRA_DIST = mysql-mroonga.spec.in mariadb-mroonga.spec.in
+noinst_DATA = mysql-mroonga.spec mariadb-mroonga.spec
diff --git a/storage/mroonga/packages/rpm/fedora/mariadb-mroonga.spec.in b/storage/mroonga/packages/rpm/fedora/mariadb-mroonga.spec.in
new file mode 100644
index 00000000000..9ee123448b2
--- /dev/null
+++ b/storage/mroonga/packages/rpm/fedora/mariadb-mroonga.spec.in
@@ -0,0 +1,189 @@
+%define mariadb_version 5.5.38
+%define mariadb_release 3%{?dist}
+#%define mariadb_download_base_url http://ftp.iij.ad.jp/pub/linux/fedora/updates/20/SRPMS
+%define mariadb_download_base_url http://ftp.riken.jp/Linux/fedora/updates/20/SRPMS
+%define groonga_required_version @REQUIRED_GROONGA_VERSION@
+
+Name: mariadb-mroonga
+Version: @VERSION@
+Release: 1%{?dist}
+Summary: A fast fulltext searchable storage engine for MariaDB
+
+Group: Applications/Databases
+License: LGPLv2
+URL: http://mroonga.org/
+Source0: http://packages.groonga.org/source/mroonga/mroonga-%{version}.tar.gz
+
+BuildRequires: groonga-devel >= %{groonga_required_version}
+BuildRequires: groonga-normalizer-mysql-devel
+BuildRequires: mariadb-devel
+BuildRequires: perl(Test::More)
+BuildRequires: perl(Env)
+BuildRequires: pam-devel
+Requires: groonga-libs >= %{groonga_required_version}
+Requires: mariadb-server = 1:%{mariadb_version}-%{mariadb_release}
+Requires: mariadb = 1:%{mariadb_version}-%{mariadb_release}
+Requires: groonga-normalizer-mysql
+Obsoletes: mysql-groonga < 1.10-0
+
+%description
+Mroonga is a fast fulltext searchable storage plugin for MariaDB.
+It is based on Groonga that is a fast fulltext search engine and
+column store. Groonga is good at real-time update.
+
+%package doc
+Summary: Documentation for Mroonga
+Group: Documentation
+License: LGPLv2
+
+%description doc
+Documentation for Mroonga
+
+%prep
+%setup -q -n mroonga-%{version}
+srpm=mariadb-%{mariadb_version}-%{mariadb_release}.src.rpm
+if [ ! -f ../../SRPMS/$srpm ]; then
+ curl --output ../../SRPMS/$srpm %{mariadb_download_base_url}/$srpm
+ rpm -Uvh ../../SRPMS/$srpm
+fi
+
+%build
+if [ ! -d ../mariadb-%{mariadb_version} ]; then
+ rpmbuild -bp --define 'runselftest 0' --define 'optflags -O0' \
+ ../../SPECS/mariadb.spec
+fi
+%configure CPPFLAGS="-DDISABLE_DTRACE" \
+ --disable-static \
+ --with-mysql-source=../mariadb-%{mariadb_version} \
+ --enable-fast-mutexes \
+ %{?mroonga_configure_options}
+make %{?_smp_mflags}
+
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+rm $RPM_BUILD_ROOT%{_libdir}/mysql/plugin/*.la
+mv $RPM_BUILD_ROOT%{_datadir}/doc/mroonga/ mariadb-mroonga-doc/
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+if /usr/bin/mysql -u root -e "quit"; then
+ password_option=""
+else
+ password_option="-p"
+fi
+current_version=0
+version=`echo %{groonga_required_version} | sed -e 's/\.//g'`
+required_version=`expr $version`
+version=`/usr/bin/mysql -e "SHOW VARIABLES LIKE 'mroonga_libgroonga_version'" | \
+ grep mroonga | cut -f 2 | sed -e 's/\.//g'`
+if [ -n "$version" ]; then
+ current_version=`expr $version`
+fi
+install_sql=%{_datadir}/mroonga/install.sql
+uninstall_sql=%{_datadir}/mroonga/uninstall.sql
+
+if [ "$1" = 2 ] ; then
+ if [ $current_version -lt $required_version ]; then
+ command="/usr/bin/mysql -u root $password_option"
+ echo "run the following command after restarting MariaDB server:";
+ echo " $command < ${uninstall_sql}"
+ echo " $command < ${install_sql}"
+ exit 0
+ else
+ command="/usr/bin/mysql -u root $password_option < ${uninstall_sql}"
+ echo $command
+ eval $command || \
+ (echo "run the following command to unregister Mroonga:"; \
+ echo " $command")
+ fi
+fi
+sql="$install_sql"
+command="/usr/bin/mysql -u root $password_option -e \"$sql\""
+echo $command
+eval $command || \
+ (echo "run the following command to register mroonga:"; \
+ echo " $command")
+
+%preun
+uninstall_sql=%{_datadir}/mroonga/uninstall.sql
+password_option=""
+mysql -u root -e "quit"
+if [ $? -ne 0 ]; then
+ password_option="-p"
+fi
+if [ "$1" = 0 ]; then
+ command="/usr/bin/mysql -u root $password_option < ${uninstall_sql}"
+ echo $command
+ eval $command || \
+ (echo "run the following command to unregister Mroonga:"; \
+ echo " $command")
+fi
+
+%files
+%defattr(-,root,root,-)
+%{_libdir}/mysql/plugin/
+%{_datadir}/mroonga/*
+%{_datadir}/man/man1/*
+%{_datadir}/man/*/man1/*
+
+%files doc
+%defattr(-,root,root,-)
+%doc README COPYING
+%doc mariadb-mroonga-doc/*
+
+%changelog
+* Fri Aug 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.05-1
+- new upstream release.
+
+* Tue Jul 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.04-1
+- new upstream release.
+
+* Thu May 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.03-1
+- new upstream release.
+
+* Tue Apr 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.02-2
+- use MariaDB 5.5.37 on Fedora 20.
+
+* Tue Apr 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.02-1
+- new upstream release.
+
+* Sat Mar 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.01-1
+- new upstream release.
+- use MariaDB 5.5.35-3 on Fedora 20.
+
+* Sun Feb 09 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.00-1
+- new upstream release.
+- use MariaDB 5.5.34-3 on Fedora 20.
+
+* Wed Jan 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 3.12-1
+- new upstream release.
+
+* Sun Dec 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.11-1
+- new upstream release.
+- support Fedora 20.
+
+* Fri Nov 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.10-1
+- new upstream release.
+
+* Tue Oct 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.09-1
+- new upstream release.
+- use MariaDB 5.5.33a on Fedora 19.
+
+* Sun Sep 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.08-1
+- new upstream release.
+
+* Wed Sep 4 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.07-2
+- fix a bug that mroonga is removed accidentally on upgrade #1918.
+ Reported by @ceekz. Thanks!!!
+
+* Thu Aug 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.07-1
+- new upstream release.
+
+* Mon Jul 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.06-1
+- new upstream release.
+- initial packaging for mariadb on Fedora 19.
+
diff --git a/storage/mroonga/packages/rpm/fedora/mysql-mroonga.spec.in b/storage/mroonga/packages/rpm/fedora/mysql-mroonga.spec.in
new file mode 100644
index 00000000000..4e28121a611
--- /dev/null
+++ b/storage/mroonga/packages/rpm/fedora/mysql-mroonga.spec.in
@@ -0,0 +1,324 @@
+%define mysql_version 5.5.38
+%define mysql_release 3%{?dist}
+#%define mysql_download_base_url http://ftp.iij.ad.jp/pub/linux/fedora/releases/20/Everything/source/SRPMS/c
+# %define mysql_download_base_url http://ftp.iij.ad.jp/pub/linux/fedora/updates/20/SRPMS
+%define mysql_download_base_url http://ftp.riken.jp/Linux/fedora/updates/20/SRPMS
+%define groonga_required_version @REQUIRED_GROONGA_VERSION@
+
+Name: mysql-mroonga
+Version: @VERSION@
+Release: 1%{?dist}
+Summary: A fast fulltext searchable storage engine for MySQL
+
+Group: Applications/Databases
+License: LGPLv2
+URL: http://mroonga.org/
+Source0: http://packages.groonga.org/source/mroonga/mroonga-%{version}.tar.gz
+
+BuildRequires: groonga-devel >= %{groonga_required_version}
+BuildRequires: groonga-normalizer-mysql-devel
+BuildRequires: community-mysql-devel
+Requires: groonga-libs >= %{groonga_required_version}
+Requires: community-mysql-server = %{mysql_version}-%{mysql_release}
+Requires: community-mysql = %{mysql_version}-%{mysql_release}
+Requires: groonga-normalizer-mysql
+Obsoletes: mysql-groonga < 1.10-0
+
+%description
+Mroonga is a fast fulltext searchable storage plugin for MySQL.
+It is based on Groonga that is a fast fulltext search engine and
+column store. Groonga is good at real-time update.
+
+%package doc
+Summary: Documentation for Mroonga
+Group: Documentation
+License: LGPLv2
+
+%description doc
+Documentation for Mroonga
+
+%prep
+%setup -q -n mroonga-%{version}
+srpm=community-mysql-%{mysql_version}-%{mysql_release}.src.rpm
+if [ ! -f ../../SRPMS/$srpm ]; then
+ curl --output ../../SRPMS/$srpm %{mysql_download_base_url}/$srpm
+ rpm -Uvh ../../SRPMS/$srpm
+fi
+
+%build
+if [ ! -d ../mysql-%{mysql_version} ]; then
+ rpmbuild -bp --define 'runselftest 0' --define 'optflags -O0' \
+ ../../SPECS/community-mysql.spec
+fi
+%configure CPPFLAGS="-DDISABLE_DTRACE" \
+ --disable-static \
+ --with-mysql-source=../mysql-%{mysql_version} \
+ --disable-fast-mutexes \
+ %{?mroonga_configure_options}
+make %{?_smp_mflags}
+
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+rm $RPM_BUILD_ROOT%{_libdir}/mysql/plugin/*.la
+mv $RPM_BUILD_ROOT%{_datadir}/doc/mroonga/ mysql-mroonga-doc/
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+if /usr/bin/mysql -u root -e "quit"; then
+ password_option=""
+else
+ password_option="-p"
+fi
+current_version=0
+version=`echo %{groonga_required_version} | sed -e 's/\.//g'`
+required_version=`expr $version`
+version=`/usr/bin/mysql -e "SHOW VARIABLES LIKE 'mroonga_libgroonga_version'" | \
+ grep mroonga | cut -f 2 | sed -e 's/\.//g'`
+if [ -n "$version" ]; then
+ current_version=`expr $version`
+fi
+install_sql=%{_datadir}/mroonga/install.sql
+uninstall_sql=%{_datadir}/mroonga/uninstall.sql
+
+if [ "$1" = 2 ] ; then
+ if [ $current_version -lt $required_version ]; then
+ command="/usr/bin/mysql -u root $password_option"
+ echo "run the following command after restarting MySQL server:";
+ echo " $command < ${uninstall_sql}"
+ echo " $command < ${install_sql}"
+ exit 0
+ else
+ command="/usr/bin/mysql -u root $password_option < ${uninstall_sql}"
+ echo $command
+ eval $command || \
+ (echo "run the following command to unregister Mroonga:"; \
+ echo " $command")
+ fi
+fi
+command="/usr/bin/mysql -u root $password_option < ${install_sql}"
+echo $command
+eval $command || \
+ (echo "run the following command to register Mroonga:"; \
+ echo " $command")
+
+%preun
+uninstall_sql=%{_datadir}/mroonga/uninstall.sql
+password_option=""
+mysql -u root -e "quit"
+if [ $? -ne 0 ]; then
+ password_option="-p"
+fi
+if [ "$1" = 0 ]; then
+ command="/usr/bin/mysql -u root $password_option < ${uninstall_sql}"
+ echo $command
+ eval $command || \
+ (echo "run the following command to unregister Mroonga:"; \
+ echo " $command")
+fi
+
+%files
+%defattr(-,root,root,-)
+%{_libdir}/mysql/plugin/
+%{_datadir}/mroonga/*
+%{_datadir}/man/man1/*
+%{_datadir}/man/*/man1/*
+
+%files doc
+%defattr(-,root,root,-)
+%doc README COPYING
+%doc mysql-mroonga-doc/*
+
+%changelog
+* Fri Aug 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.05-1
+- new upstream release.
+
+* Tue Jul 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.04-1
+- new upstream release.
+
+* Thu May 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.03-1
+- new upstream release.
+
+* Tue Apr 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.02-2
+- use Community MySQL 5.5.37 on Fedora 20.
+
+* Tue Apr 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.02-1
+- new upstream release.
+
+* Sat Mar 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.01-1
+- new upstream release.
+
+* Sun Feb 09 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.00-1
+- new upstream release.
+
+* Wed Jan 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 3.12-1
+- new upstream release.
+- use MySQL 5.5.35 on Fedora 20.
+
+* Sun Dec 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.11-1
+- new upstream release.
+- support Fedora 20.
+
+* Fri Nov 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.10-1
+- new upstream release.
+- use MySQL 5.5.34 on Fedora 19.
+
+* Tue Oct 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.09-1
+- new upstream release.
+
+* Sun Sep 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.08-1
+- new upstream release.
+- use MySQL 5.5.33 on Fedora 19.
+
+* Wed Sep 4 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.07-2
+- fix a bug that mroonga is removed accidentally on upgrade #1918.
+ Reported by @ceekz. Thanks!!!
+
+* Thu Aug 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.07-1
+- new upstream release.
+
+* Mon Jul 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.06-1
+- new upstream release.
+
+* Sat Jun 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.05-1
+- new upstream release.
+- use MySQL 5.5.32 on Fedora 18.
+
+* Wed May 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.04-1
+- new upstream release.
+- use MySQL 5.5.31 on Fedora 18.
+
+* Mon Apr 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.03-1
+- new upstream release.
+
+* Fri Mar 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.02-0
+- new upstream release.
+
+* Thu Feb 28 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.01-0
+- new upstream release.
+- use MySQL 5.5.30 on Fedora 18.
+
+* Sat Feb 09 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.00-0
+- new upstream release.
+- support Fedora 18.
+- drop Fedora 17 support.
+- require groonga 3.0.0 or later
+
+* Thu Jan 24 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 2.10-1
+- build with MySQL 5.5.29 on Fedora 17.
+
+* Sat Dec 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.10-0
+- new upstream release.
+
+* Thu Nov 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.09-0
+- new upstream release.
+
+* Mon Oct 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.08-0
+- new upstream release.
+- add missing "DROP FUNCTION mroonga_snippet".
+ Reported by @tokuhy. Thanks!!!
+- use MySQL 5.5.28.
+
+* Sat Sep 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.07-0
+- new upstream release.
+
+* Wed Aug 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.06-0
+- new upstream release.
+- use MySQL 5.5.27.
+
+* Sun Jul 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.05-0
+- new upstream release.
+- use MySQL 5.5.25a.
+
+* Fri Jun 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.04-0
+- new upstream release.
+- ensure deleting mroonga plugin before install.
+ Suggested by Kazuhiro Isobe. Thanks!!!
+- use MySQL 5.5.24.
+
+* Tue May 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.03-0
+- new upstream release.
+- use MySQL 5.5.23.
+- require groonga 2.0.3 or later.
+
+* Sun Apr 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.02-0
+- new upstream release.
+- require groonga 2.0.2 or later.
+- use MySQL 5.5.22.
+
+* Thu Mar 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.01-0
+- new upstream release.
+- ensure plugin is uninstalled by closing all tables use mroonga.
+- use MySQL 5.5.21.
+
+* Wed Feb 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.00-0
+- new upstream release.
+- always install/uninstall plugin.
+- use MySQL 5.5.20.
+- require groonga 2.0.0 or later.
+
+* Sun Jan 29 2012 Kouhei Sutou <kou@clear-code.com> - 1.20-0
+- new upstream release.
+- require groonga 1.3.0.
+- groonga -> mroonga.
+- use MySQL 5.5.19.
+
+* Thu Dec 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.11-0
+- new upstream release.
+
+* Sat Oct 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.10-0
+- new upstream release.
+- groonga storage engine -> mroonga.
+- split document package.
+
+* Thu Sep 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.0.0-0
+- new upstream release.
+
+* Mon Aug 29 2011 Kouhei Sutou <kou@clear-code.com> - 0.9-0
+- new upstream release.
+
+* Fri Jul 29 2011 Kouhei Sutou <kou@clear-code.com> - 0.8-0
+- new upstream release.
+
+* Wed Jun 29 2011 Kouhei Sutou <kou@clear-code.com> - 0.7-0
+- new upstream release.
+
+* Sun May 29 2011 Kouhei Sutou <kou@clear-code.com> - 0.6-0
+- new upstream release.
+
+* Tue Mar 29 2011 Kouhei Sutou <kou@clear-code.com> - 0.5-1
+- new upstream release.
+
+* Sat Jan 29 2011 Kouhei Sutou <kou@clear-code.com> - 0.4-3
+- do not remove plugin on upgrade.
+
+* Thu Dec 30 2010 Kouhei Sutou <kou@clear-code.com> - 0.4-2
+- fix SQL literal notation.
+
+* Mon Nov 29 2010 Kouhei Sutou <kou@clear-code.com> - 0.4-1
+- use the latest MySQL.
+- new upstream release.
+
+* Sun Nov 21 2010 Kouhei Sutou <kou@clear-code.com> - 0.3-2
+- install user define function.
+
+* Fri Oct 29 2010 Kouhei Sutou <kou@clear-code.com> - 0.3-1
+- new upstream release.
+
+* Fri Oct 08 2010 Kouhei Sutou <kou@clear-code.com> - 0.2-2
+- use %{version}.
+
+* Wed Sep 29 2010 Kouhei Sutou <kou@clear-code.com> - 0.2-1
+- new upstream release.
+
+* Sun Sep 12 2010 Kouhei Sutou <kou@clear-code.com> - 0.1-3
+- require mysql-client.
+
+* Fri Sep 10 2010 Kouhei Sutou <kou@clear-code.com> - 0.1-2
+- follow configure option changes.
+
+* Fri Sep 03 2010 Kouhei Sutou <kou@clear-code.com> - 0.1-1
+- initial packaging for Fedora.
diff --git a/storage/mroonga/packages/source/Makefile.am b/storage/mroonga/packages/source/Makefile.am
new file mode 100644
index 00000000000..dc4c17d2cd6
--- /dev/null
+++ b/storage/mroonga/packages/source/Makefile.am
@@ -0,0 +1,122 @@
+MROONGA_BASE = $(PACKAGE)-$(VERSION)
+MROONGA_TAR_GZ = $(MROONGA_BASE).tar.gz
+
+GROONGA_VERSION = 4.0.4
+GROONGA_BASE = groonga-$(GROONGA_VERSION)
+GROONGA_TAR_GZ = $(GROONGA_BASE).tar.gz
+
+GROONGA_NORMALIZER_MYSQL_VERSION = 1.0.6
+GROONGA_NORMALIZER_MYSQL_BASE = \
+ groonga-normalizer-mysql-$(GROONGA_NORMALIZER_MYSQL_VERSION)
+GROONGA_NORMALIZER_MYSQL_TAR_GZ = \
+ $(GROONGA_NORMALIZER_MYSQL_BASE).tar.gz
+
+MARIADB_VERSION = 10.0.13
+MARIADB_BASE = mariadb-$(MARIADB_VERSION)
+MARIADB_TAR_GZ = $(MARIADB_BASE).tar.gz
+
+MARIADB_WITH_MROONGA_BASE = $(MARIADB_BASE)-with-$(MROONGA_BASE)
+MARIADB_WITH_MROONGA_FOR_WINDOWS_BASE = $(MARIADB_WITH_MROONGA_BASE)-for-windows
+
+GROONGA_PROJECT_DOWNLOAD_BASE = http://packages.groonga.org/source
+GROONGA_DOWNLOAD_BASE = $(GROONGA_PROJECT_DOWNLOAD_BASE)/groonga
+GROONGA_NORMALIZER_MYSQL_DOWNLOAD_BASE = \
+ $(GROONGA_PROJECT_DOWNLOAD_BASE)/groonga-normalizer-mysql
+MARIADB_DOWNLOAD_BASE = http://ftp.yz.yamagata-u.ac.jp/pub/dbms/mariadb
+
+
+CURL = curl --fail --silent --show-error
+
+all:
+
+release: archive upload
+
+ensure-rsync-path:
+ @if test -z "$(RSYNC_PATH)"; then \
+ echo "--with-rsync-path configure option must be specified."; \
+ false; \
+ fi
+
+download: ensure-rsync-path
+ rsync -avz --progress --delete $(RSYNC_PATH)/source/mroonga/ files
+
+ARCHIVES = \
+ files/$(MROONGA_TAR_GZ) \
+ files/$(MARIADB_WITH_MROONGA_BASE).tar.gz \
+ files/$(MARIADB_WITH_MROONGA_FOR_WINDOWS_BASE).zip
+
+archive: $(ARCHIVES)
+
+upload: ensure-rsync-path
+ rsync -avz --progress --delete files/ $(RSYNC_PATH)/source/mroonga
+
+files/$(MROONGA_TAR_GZ): $(top_builddir)/$(MROONGA_TAR_GZ)
+ mkdir -p files
+ cp -p $< $@
+
+tmp/$(GROONGA_TAR_GZ):
+ mkdir -p tmp
+ $(CURL) --output $@ $(GROONGA_DOWNLOAD_BASE)/$(GROONGA_TAR_GZ)
+
+tmp/$(GROONGA_NORMALIZER_MYSQL_TAR_GZ):
+ mkdir -p tmp
+ $(CURL) --output $@ $(GROONGA_NORMALIZER_MYSQL_DOWNLOAD_BASE)/$(GROONGA_NORMALIZER_MYSQL_TAR_GZ)
+
+tmp/$(MARIADB_TAR_GZ):
+ mkdir -p tmp
+ $(CURL) --output $@ $(MARIADB_DOWNLOAD_BASE)/mariadb-$(MARIADB_VERSION)/source/$(MARIADB_TAR_GZ)
+
+MARIADB_WITH_MROONGA_ARCHIVES = \
+ tmp/$(GROONGA_TAR_GZ) \
+ tmp/$(GROONGA_NORMALIZER_MYSQL_TAR_GZ) \
+ tmp/$(MARIADB_TAR_GZ) \
+ $(top_builddir)/$(MROONGA_TAR_GZ)
+
+BUNDLED_MROONGA_PATH = $(MARIADB_BASE)/storage/$(PACKAGE)
+BUNDLED_GROONGA_PATH = $(BUNDLED_MROONGA_PATH)/vendor/groonga
+BUNDLED_GROONGA_NORMALIZER_MYSQL_PATH = \
+ $(BUNDLED_GROONGA_PATH)/vendor/plugins/groonga-normalizer-mysql
+
+tmp/$(MARIADB_WITH_MROONGA_BASE).stamp: $(MARIADB_WITH_MROONGA_ARCHIVES)
+ rm -rf $(MARIADB_BASE)
+ tar xf tmp/$(MARIADB_TAR_GZ)
+
+ tar xf $(top_builddir)/$(MROONGA_TAR_GZ)
+ mv $(MROONGA_BASE) $(BUNDLED_MROONGA_PATH)
+
+ mkdir -p $$(dirname $(BUNDLED_GROONGA_PATH))
+ tar xf tmp/$(GROONGA_TAR_GZ)
+ rm -rf $(GROONGA_BASE)/test
+ mv $(GROONGA_BASE) $(BUNDLED_GROONGA_PATH)
+
+ tar xf tmp/$(GROONGA_NORMALIZER_MYSQL_TAR_GZ)
+ rm -rf $(GROONGA_NORMALIZER_MYSQL_BASE)/test
+ mv $(GROONGA_NORMALIZER_MYSQL_BASE) $(BUNDLED_GROONGA_NORMALIZER_MYSQL_PATH)
+
+ rm -rf tmp/$(MARIADB_WITH_MROONGA_BASE)
+ mv $(MARIADB_BASE) tmp/$(MARIADB_WITH_MROONGA_BASE)
+
+ touch $@
+
+files/$(MARIADB_WITH_MROONGA_BASE).tar.gz: tmp/$(MARIADB_WITH_MROONGA_BASE).stamp
+ mkdir -p files/
+ (cd tmp && tar czf ../$@ $(MARIADB_WITH_MROONGA_BASE))
+
+PATCHES = \
+ patches/mariadb-10.0.3-windows-build.diff
+
+tmp/$(MARIADB_WITH_MROONGA_FOR_WINDOWS_BASE).stamp: tmp/$(MARIADB_WITH_MROONGA_BASE).stamp $(PATCHES)
+ rm -rf tmp/$(MARIADB_WITH_MROONGA_FOR_WINDOWS_BASE)
+ cp -a \
+ tmp/$(MARIADB_WITH_MROONGA_BASE) \
+ tmp/$(MARIADB_WITH_MROONGA_FOR_WINDOWS_BASE)
+ for patch in $(PATCHES); do \
+ (cd tmp/$(MARIADB_WITH_MROONGA_FOR_WINDOWS_BASE) && \
+ patch -p1 < $(abs_srcdir)/$${patch}); \
+ done
+
+ touch $@
+
+files/$(MARIADB_WITH_MROONGA_FOR_WINDOWS_BASE).zip: tmp/$(MARIADB_WITH_MROONGA_FOR_WINDOWS_BASE).stamp
+ mkdir -p files/
+ (cd tmp && zip -q -r ../$@ $(MARIADB_WITH_MROONGA_FOR_WINDOWS_BASE))
diff --git a/storage/mroonga/packages/source/patches/mariadb-10.0.3-windows-build.diff b/storage/mroonga/packages/source/patches/mariadb-10.0.3-windows-build.diff
new file mode 100644
index 00000000000..c135088b8cc
--- /dev/null
+++ b/storage/mroonga/packages/source/patches/mariadb-10.0.3-windows-build.diff
@@ -0,0 +1,9 @@
+diff -ur mariadb-10.0.2.orig/sql/sql_locale.cc mariadb-10.0.2/sql/sql_locale.cc
+--- mariadb-10.0.2.orig/sql/sql_locale.cc 2013-04-23 13:13:59.000000000 +0900
++++ mariadb-10.0.2/sql/sql_locale.cc 2013-05-19 12:55:27.590366542 +0900
+@@ -1,4 +1,4 @@
+-/* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
++/* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
diff --git a/storage/mroonga/packages/ubuntu/Makefile.am b/storage/mroonga/packages/ubuntu/Makefile.am
new file mode 100644
index 00000000000..af677d733e0
--- /dev/null
+++ b/storage/mroonga/packages/ubuntu/Makefile.am
@@ -0,0 +1,24 @@
+CODE_NAMES = precise,trusty
+SOURCE = ../$(PACKAGE)-$(VERSION).tar.gz
+
+all:
+
+ensure-launchpad-configuration:
+ @if test -z "$(LAUNCHPAD_UPLOADER_PGP_KEY)"; then \
+ echo "--with-launchpad-uploader-pgp-key configure option must be specified."; \
+ false; \
+ fi
+
+upload: source ensure-launchpad-configuration
+ ./upload.rb \
+ --package '$(PACKAGE)' \
+ --version '$(VERSION)' \
+ --source-archive '$(SOURCE)' \
+ --code-names '$(CODE_NAMES)' \
+ --debian-directory '$(srcdir)/../debian/' \
+ --pgp-sign-key '$(LAUNCHPAD_UPLOADER_PGP_KEY)'
+
+source: $(SOURCE)
+
+$(SOURCE):
+ ln -s $(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz $(SOURCE)
diff --git a/storage/mroonga/packages/ubuntu/upload.rb b/storage/mroonga/packages/ubuntu/upload.rb
new file mode 100755
index 00000000000..3331de6d5eb
--- /dev/null
+++ b/storage/mroonga/packages/ubuntu/upload.rb
@@ -0,0 +1,168 @@
+#!/usr/bin/env ruby
+#
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2014 HAYASHI Kentaro <hayashi@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+require "optparse"
+require "fileutils"
+require "pathname"
+require "open-uri"
+
+class Uploader
+ def initialize
+ @dput_configuration_name = "groonga-ppa"
+ end
+
+ def run
+ ensure_dput_configuration
+
+ parse_command_line!
+
+ ensure_mysql_version
+
+ @required_groonga_version = required_groonga_version
+
+ @code_names.each do |code_name|
+ upload(code_name)
+ end
+ end
+
+ private
+ def ensure_dput_configuration
+ dput_cf_path = Pathname.new("~/.dput.cf").expand_path
+ if dput_cf_path.exist?
+ dput_cf_content = dput_cf_path.read
+ else
+ dput_cf_content = ""
+ end
+ dput_cf_content.each_line do |line|
+ return if line.chomp == "[#{@dput_configuration_name}]"
+ end
+
+ dput_cf_path.open("w") do |dput_cf|
+ dput_cf.puts(dput_cf_content)
+ dput_cf.puts(<<-CONFIGURATION)
+[#{@dput_configuration_name}]
+fqdn = ppa.launchpad.net
+method = ftp
+incoming = ~groonga/ppa/ubuntu/
+login = anonymous
+allow_unsigned_uploads = 0
+ CONFIGURATION
+ end
+ end
+
+ def ensure_mysql_version
+ @mysql_version = {}
+ @code_names.each do |code_name|
+ open("http://packages.ubuntu.com/#{code_name}/allpackages?format=txt.gz") do |file|
+ file.each_line do |line|
+ @mysql_version[code_name] = $1 if line =~ /\Amysql-server \((.+?)\).+/
+ end
+ end
+ end
+ end
+
+ def parse_command_line!
+
+ parser = OptionParser.new
+ parser.on("--package=NAME",
+ "The package name") do |name|
+ @package = name
+ end
+ parser.on("--version=VERSION",
+ "The version") do |version|
+ @version = version
+ end
+ parser.on("--source-archive=ARCHIVE",
+ "The source archive") do |source_archive|
+ @source_archive = Pathname.new(source_archive).expand_path
+ end
+ parser.on("--code-names=CODE_NAME1,CODE_NAME2,CODE_NAME3,...", Array,
+ "The target code names") do |code_names|
+ @code_names = code_names
+ end
+ parser.on("--debian-directory=DIRECTORY",
+ "The debian/ directory") do |debian_directory|
+ @debian_directory = Pathname.new(debian_directory).expand_path
+ end
+ parser.on("--pgp-sign-key=KEY",
+ "The PGP key to sign .changes and .dsc") do |pgp_sign_key|
+ @pgp_sign_key = pgp_sign_key
+ end
+ parser.on("--pbuilder",
+ "Use pbuilder for build check") do |pbuilder|
+ @use_pbuilder = pbuilder
+ end
+
+ parser.parse!
+ end
+
+ def upload(code_name)
+ in_temporary_directory do
+ FileUtils.cp(@source_archive.to_s,
+ "#{@package}_#{@version}.orig.tar.gz")
+ run_command("tar", "xf", @source_archive.to_s)
+ directory_name = "#{@package}-#{@version}"
+ Dir.chdir(directory_name) do
+ FileUtils.cp_r(@debian_directory.to_s, "debian")
+ deb_version = "#{current_deb_version.succ}~#{code_name}1"
+ run_command("dch",
+ "--distribution", code_name,
+ "--newversion", deb_version,
+ "Build for #{code_name}.")
+ run_command("sed",
+ "-i", "-e", "s,MYSQL_VERSION,#{@mysql_version[code_name]},",
+ "debian/control")
+ run_command("debuild", "-S", "-sa", "-pgpg2", "-k#{@pgp_sign_key}")
+ if @use_pbuilder
+ run_command("pbuilder-dist", code_name, "build",
+ "../#{@package}_#{deb_version}.dsc")
+ else
+ run_command("dput", @dput_configuration_name,
+ "../#{@package}_#{deb_version}_source.changes")
+ end
+ end
+ end
+ end
+
+ def required_groonga_version
+ File.read("../../required_groonga_version").lines.first.chomp
+ end
+
+ def current_deb_version
+ /\((.+)\)/ =~ File.read("debian/changelog").lines.first
+ $1
+ end
+
+ def in_temporary_directory
+ name = "tmp"
+ FileUtils.rm_rf(name)
+ FileUtils.mkdir_p(name)
+ Dir.chdir(name) do
+ yield
+ end
+ end
+
+ def run_command(*command_line)
+ unless system(*command_line)
+ raise "failed to run command: #{command_line.join(' ')}"
+ end
+ end
+end
+
+uploader = Uploader.new
+uploader.run
diff --git a/storage/mroonga/packages/windows/Makefile.am b/storage/mroonga/packages/windows/Makefile.am
new file mode 100644
index 00000000000..a2ff8f59792
--- /dev/null
+++ b/storage/mroonga/packages/windows/Makefile.am
@@ -0,0 +1,12 @@
+EXTRA_DIST = \
+ README.md \
+ build-vc2010.bat \
+ build-vc2010-zip-32.bat \
+ build-vc2010-zip-64.bat \
+ build-vc2010-msi-32.bat \
+ build-vc2010-msi-64.bat \
+ build-vc2013.bat \
+ build-vc2013-zip-32.bat \
+ build-vc2013-zip-64.bat \
+ build-vc2013-msi-32.bat \
+ build-vc2013-msi-64.bat
diff --git a/storage/mroonga/packages/windows/README.md b/storage/mroonga/packages/windows/README.md
new file mode 100644
index 00000000000..f7788ffe26b
--- /dev/null
+++ b/storage/mroonga/packages/windows/README.md
@@ -0,0 +1,20 @@
+# How to build Windows binaries
+
+## Preparation
+
+TODO...
+
+## Build with Visual C++ Express
+
+You need to use Visual C++ 2012 or later to build Mroonga with Express
+edition. `build-vc2013.bat` is a build batch script to build with
+Visual C++ Express 2013.
+
+Note that you can't build MSI file with Express edition. You need to
+use Professional edition or upper editions to build MSI file.
+
+## Build with Visual C++ Professional
+
+You can build both zip file MSI file with Professional edition.
+`build-vc2010.bat` is a build batch script to build with Visual C++
+Professional 2010.
diff --git a/storage/mroonga/packages/windows/build-vc2010-msi-32.bat b/storage/mroonga/packages/windows/build-vc2010-msi-32.bat
new file mode 100644
index 00000000000..15185eaba92
--- /dev/null
+++ b/storage/mroonga/packages/windows/build-vc2010-msi-32.bat
@@ -0,0 +1,8 @@
+rmdir /S /Q build-vc2010-msi-32
+mkdir build-vc2010-msi-32
+cd build-vc2010-msi-32
+cmake ..\source -G "Visual Studio 10" > config.log
+cmake --build . --config RelWithDebInfo > build.log
+cmake --build . --config RelWithDebInfo --target msi > msi.log
+move *.msi ..\
+cd ..
diff --git a/storage/mroonga/packages/windows/build-vc2010-msi-64.bat b/storage/mroonga/packages/windows/build-vc2010-msi-64.bat
new file mode 100644
index 00000000000..ea0b7f07eb3
--- /dev/null
+++ b/storage/mroonga/packages/windows/build-vc2010-msi-64.bat
@@ -0,0 +1,8 @@
+rmdir /S /Q build-vc2010-msi-64
+mkdir build-vc2010-msi-64
+cd build-vc2010-msi-64
+cmake ..\source -G "Visual Studio 10 Win64" > config.log
+cmake --build . --config RelWithDebInfo > build.log
+cmake --build . --config RelWithDebInfo --target msi > msi.log
+move *.msi ..\
+cd ..
diff --git a/storage/mroonga/packages/windows/build-vc2010-zip-32.bat b/storage/mroonga/packages/windows/build-vc2010-zip-32.bat
new file mode 100644
index 00000000000..6942e01145d
--- /dev/null
+++ b/storage/mroonga/packages/windows/build-vc2010-zip-32.bat
@@ -0,0 +1,8 @@
+rmdir /S /Q build-vc2010-zip-32
+mkdir build-vc2010-zip-32
+cd build-vc2010-zip-32
+cmake ..\source -G "Visual Studio 10" > config.log
+cmake --build . --config RelWithDebInfo > build.log
+cmake --build . --config RelWithDebInfo --target package > zip.log
+move *.zip ..\
+cd ..
diff --git a/storage/mroonga/packages/windows/build-vc2010-zip-64.bat b/storage/mroonga/packages/windows/build-vc2010-zip-64.bat
new file mode 100644
index 00000000000..de8f1db10e0
--- /dev/null
+++ b/storage/mroonga/packages/windows/build-vc2010-zip-64.bat
@@ -0,0 +1,8 @@
+rmdir /S /Q build-vc2010-zip-64
+mkdir build-vc2010-zip-64
+cd build-vc2010-zip-64
+cmake ..\source -G "Visual Studio 10 Win64" > config.log
+cmake --build . --config RelWithDebInfo > build.log
+cmake --build . --config RelWithDebInfo --target package > zip.log
+move *.zip ..\
+cd ..
diff --git a/storage/mroonga/packages/windows/build-vc2010.bat b/storage/mroonga/packages/windows/build-vc2010.bat
new file mode 100644
index 00000000000..5fcf0639412
--- /dev/null
+++ b/storage/mroonga/packages/windows/build-vc2010.bat
@@ -0,0 +1,4 @@
+build-vc2010-zip-32.bat
+build-vc2010-zip-64.bat
+build-vc2010-msi-32.bat
+build-vc2010-msi-64.bat
diff --git a/storage/mroonga/packages/windows/build-vc2013-msi-32.bat b/storage/mroonga/packages/windows/build-vc2013-msi-32.bat
new file mode 100644
index 00000000000..22b29972885
--- /dev/null
+++ b/storage/mroonga/packages/windows/build-vc2013-msi-32.bat
@@ -0,0 +1,8 @@
+rmdir /S /Q build-vc2013-msi-32
+mkdir build-vc2013-msi-32
+cd build-vc2013-msi-32
+cmake ..\source -G "Visual Studio 12" > config.log
+cmake --build . --config RelWithDebInfo > build.log
+cmake --build . --config RelWithDebInfo --target msi > msi.log
+move *.msi ..\
+cd ..
diff --git a/storage/mroonga/packages/windows/build-vc2013-msi-64.bat b/storage/mroonga/packages/windows/build-vc2013-msi-64.bat
new file mode 100644
index 00000000000..c83a376cdb9
--- /dev/null
+++ b/storage/mroonga/packages/windows/build-vc2013-msi-64.bat
@@ -0,0 +1,8 @@
+rmdir /S /Q build-vc2013-msi-64
+mkdir build-vc2013-msi-64
+cd build-vc2013-msi-64
+cmake ..\source -G "Visual Studio 12 Win64" > config.log
+cmake --build . --config RelWithDebInfo > build.log
+cmake --build . --config RelWithDebInfo --target msi > msi.log
+move *.msi ..\
+cd ..
diff --git a/storage/mroonga/packages/windows/build-vc2013-zip-32.bat b/storage/mroonga/packages/windows/build-vc2013-zip-32.bat
new file mode 100644
index 00000000000..d6401a6bfed
--- /dev/null
+++ b/storage/mroonga/packages/windows/build-vc2013-zip-32.bat
@@ -0,0 +1,8 @@
+rmdir /S /Q build-vc2013-zip-32
+mkdir build-vc2013-zip-32
+cd build-vc2013-zip-32
+cmake ..\source -G "Visual Studio 12" > config.log
+cmake --build . --config RelWithDebInfo > build.log
+cmake --build . --config RelWithDebInfo --target package > zip.log
+move *.zip ..\
+cd ..
diff --git a/storage/mroonga/packages/windows/build-vc2013-zip-64.bat b/storage/mroonga/packages/windows/build-vc2013-zip-64.bat
new file mode 100644
index 00000000000..fd55a25d90e
--- /dev/null
+++ b/storage/mroonga/packages/windows/build-vc2013-zip-64.bat
@@ -0,0 +1,8 @@
+rmdir /S /Q build-vc2013-zip-64
+mkdir build-vc2013-zip-64
+cd build-vc2013-zip-64
+cmake ..\source -G "Visual Studio 12 Win64" > config.log
+cmake --build . --config RelWithDebInfo > build.log
+cmake --build . --config RelWithDebInfo --target package > zip.log
+move *.zip ..\
+cd ..
diff --git a/storage/mroonga/packages/windows/build-vc2013.bat b/storage/mroonga/packages/windows/build-vc2013.bat
new file mode 100644
index 00000000000..99d7e4042c5
--- /dev/null
+++ b/storage/mroonga/packages/windows/build-vc2013.bat
@@ -0,0 +1,4 @@
+build-vc2013-zip-32.bat
+build-vc2013-zip-64.bat
+REM build-vc2013-msi-32.bat
+REM build-vc2013-msi-64.bat
diff --git a/storage/mroonga/packages/yum/Makefile.am b/storage/mroonga/packages/yum/Makefile.am
new file mode 100644
index 00000000000..b110b478a14
--- /dev/null
+++ b/storage/mroonga/packages/yum/Makefile.am
@@ -0,0 +1,63 @@
+REPOSITORIES_PATH = repositories
+DISTRIBUTIONS = centos
+ARCHITECTURES = i386 x86_64
+MYSQL_VARIANTS = mysql55 mysql56-community mariadb
+SPEC_DIR = $(builddir)/../rpm/centos
+
+all:
+
+release: download build sign-packages update-repository upload
+
+remove-existing-packages:
+ for distribution in $(DISTRIBUTIONS); do \
+ find $${distribution} -name "*.rpm" -delete; \
+ done
+
+ensure-rsync-path:
+ @if test -z "$(RSYNC_PATH)"; then \
+ echo "--with-rsync-path configure option must be specified."; \
+ false; \
+ fi
+
+sign-packages:
+ ./sign-rpm.sh '$(GPG_UID)' '$(REPOSITORIES_PATH)/' '$(DISTRIBUTIONS)'
+
+update-repository:
+ ./update-repository.sh '$(REPOSITORIES_PATH)/' '$(DISTRIBUTIONS)'
+
+upload: ensure-rsync-path
+ for distribution in $(DISTRIBUTIONS); do \
+ rsync -avz --progress --delete --exclude .gitignore \
+ $(REPOSITORIES_PATH)/$${distribution}/ \
+ $(RSYNC_PATH)/$${distribution}; \
+ done
+
+download: ensure-rsync-path
+ mkdir -p $(REPOSITORIES_PATH)
+ for distribution in $(DISTRIBUTIONS); do \
+ rsync -avz --progress --delete \
+ $(RSYNC_PATH)/$${distribution}/ \
+ $(REPOSITORIES_PATH)/$${distribution}; \
+ done
+
+build: build-in-vm
+
+build-in-vm: source specs env.sh
+ ./build-in-vm.sh \
+ "$(PACKAGE)" \
+ "$(SPEC_DIR)" \
+ "$(MYSQL_VARIANTS)" \
+ "$(ARCHITECTURES)"
+
+source: tmp/$(PACKAGE)-$(VERSION).tar.gz
+
+tmp/$(PACKAGE)-$(VERSION).tar.gz: $(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz
+ mkdir -p tmp/
+ cp $(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz tmp/
+
+$(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz:
+ cd $(abs_top_builddir) && $(MAKE) dist
+
+specs: $(SPEC_DIR)/mysql55-$(PACKAGE).spec
+specs: $(SPEC_DIR)/mysql56-community-$(PACKAGE).spec
+specs: $(SPEC_DIR)/mariadb-$(PACKAGE).spec
diff --git a/storage/mroonga/packages/yum/Vagrantfile b/storage/mroonga/packages/yum/Vagrantfile
new file mode 100644
index 00000000000..aeee1ceb5b2
--- /dev/null
+++ b/storage/mroonga/packages/yum/Vagrantfile
@@ -0,0 +1,50 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
+VAGRANTFILE_API_VERSION = "2"
+
+Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+ vms = [
+ {
+ :id => "centos-5-i386",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-5.10-i386_chef-provisionerless.box",
+ },
+ {
+ :id => "centos-5-x86_64",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-5.10_chef-provisionerless.box",
+ },
+ {
+ :id => "centos-6-i386",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5-i386_chef-provisionerless.box",
+ },
+ {
+ :id => "centos-6-x86_64",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box",
+ },
+ {
+ :id => "centos-7-x86_64",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-7.0_chef-provisionerless.box",
+ },
+ ]
+
+ vms.each do |vm|
+ config.vm.define(vm[:id]) do |node|
+ node.vm.box = vm[:id]
+ node.vm.box_url = vm[:box_url]
+ node.vm.provision(:shell, :path => "build-rpm.sh")
+ node.vm.provider("virtualbox") do |virtual_box|
+ system_n_cpus = 1
+ if File.exist?("/proc/cpuinfo")
+ system_n_cpus = File.readlines("/proc/cpuinfo").grep(/^processor/).size
+ end
+ if system_n_cpus > 1
+ vm_n_cpus = system_n_cpus / 2
+ else
+ vm_n_cpus = 1
+ end
+ virtual_box.cpus = vm_n_cpus
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/packages/yum/build-in-vm.sh b/storage/mroonga/packages/yum/build-in-vm.sh
new file mode 100755
index 00000000000..5dc27ec38dd
--- /dev/null
+++ b/storage/mroonga/packages/yum/build-in-vm.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+if [ $# != 4 ]; then
+ echo "Usage: $0 PACKAGE SPEC_DIR MYSQL_VARIANTS ARCHITECTURES"
+ echo " e.g.: $0 mroonga ../rpm/centos 'mysql55 mariadb' 'i386 x86_64'"
+ exit 1
+fi
+
+PACKAGE="$1"
+SPEC_DIR="$2"
+MYSQL_VARIANTS="$3"
+ARCHITECTURES="$4"
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+run vagrant destroy --force
+
+for mysql_variant in ${MYSQL_VARIANTS}; do
+ rm -rf tmp/centos/
+ mkdir -p tmp/centos/
+ cp ${SPEC_DIR}/${mysql_variant}-${PACKAGE}.spec tmp/centos/
+
+ architectures="${ARCHITECTURES}"
+ case ${mysql_variant} in
+ mysql55)
+ centos_versions="5 6"
+ ;;
+ mysql56-community)
+ centos_versions="6 7"
+ ;;
+ mariadb)
+ centos_versions="7"
+ ;;
+ esac
+
+ for architecture in ${architectures}; do
+ for centos_version in ${centos_versions}; do
+ if [ ${mysql_variant} = mysql55 -a ${centos_version} = 6 -a ${architecture} = i386 ]; then
+ continue
+ fi
+ if [ ${centos_version} = 7 -a ${architecture} = i386 ]; then
+ continue
+ fi
+ id=centos-${centos_version}-${architecture}
+ vagrant up ${id}
+ build_status=$?
+ if [ $build_status -ne 0 ]; then
+ exit $build_status
+ fi
+ vagrant destroy --force ${id}
+ done
+ done
+done
diff --git a/storage/mroonga/packages/yum/build-rpm.sh b/storage/mroonga/packages/yum/build-rpm.sh
new file mode 100755
index 00000000000..6eaa2cce02a
--- /dev/null
+++ b/storage/mroonga/packages/yum/build-rpm.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+LANG=C
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+rpmbuild_options=
+
+. /vagrant/env.sh
+
+distribution=$(cut -d " " -f 1 /etc/redhat-release | tr "A-Z" "a-z")
+if grep -q Linux /etc/redhat-release; then
+ distribution_version=$(cut -d " " -f 4 /etc/redhat-release)
+else
+ distribution_version=$(cut -d " " -f 3 /etc/redhat-release)
+fi
+distribution_version=$(echo ${distribution_version} | sed -e 's/\..*$//g')
+
+architecture="$(arch)"
+case "${architecture}" in
+ i*86)
+ architecture=i386
+ ;;
+esac
+
+run yum groupinstall -y "Development Tools"
+run yum install -y rpm-build rpmdevtools tar wget
+
+if [ -x /usr/bin/rpmdev-setuptree ]; then
+ rm -rf .rpmmacros
+ run rpmdev-setuptree
+else
+ run cat <<EOM > ~/.rpmmacros
+%_topdir ${HOME}/rpmbuild
+EOM
+ run mkdir -p ~/rpmbuild/SOURCES
+ run mkdir -p ~/rpmbuild/SPECS
+ run mkdir -p ~/rpmbuild/BUILD
+ run mkdir -p ~/rpmbuild/RPMS
+ run mkdir -p ~/rpmbuild/SRPMS
+fi
+
+repository="/vagrant/repositories/${distribution}/${distribution_version}"
+rpm_dir="${repository}/${architecture}/Packages"
+srpm_dir="${repository}/source/SRPMS"
+run mkdir -p "${rpm_dir}" "${srpm_dir}"
+
+rpmbuild_options=""
+
+# for debug
+# rpmbuild_options="${rpmbuild_options} --define 'optflags -O0 -g3'"
+
+cd
+
+run cp /vagrant/tmp/${PACKAGE}-${VERSION}.* rpmbuild/SOURCES/
+run cp /vagrant/tmp/${distribution}/*.spec rpmbuild/SPECS/
+
+package_name=$(cd rpmbuild/SPECS; echo *.spec | sed -e 's/\.spec$//g')
+
+case ${distribution} in
+ fedora)
+ USE_MYSQLSERVICES_COMPAT=yes
+ run yum install -y mariadb-devel
+ ;;
+ centos)
+ case ${package_name} in
+ mysql55-${PACKAGE})
+ USE_MYSQLSERVICES_COMPAT=yes
+ run yum install -y scl-utils-build
+ if [ ${distribution_version} = 6 ]; then
+ run yum install -y centos-release-SCL
+ fi
+ run yum install -y mysql55-mysql-devel mysql55-build
+ ;;
+ mysql56-community-${PACKAGE})
+ release_rpm=mysql-community-release-el${distribution_version}-5.noarch.rpm
+ run yum -y install http://repo.mysql.com/${release_rpm}
+ run yum -y install mysql-community-devel
+ ;;
+ mariadb-${PACKAGE})
+ run yum -y install mariadb-devel
+ ;;
+ esac
+
+ release_rpm=groonga-release-1.1.0-1.noarch.rpm
+ wget http://packages.groonga.org/${distribution}/${release_rpm}
+ run rpm -U ${release_rpm}
+ rm -f ${release_rpm}
+ run yum makecache
+ ;;
+esac
+run yum install -y ${DEPENDED_PACKAGES}
+
+if [ "${USE_MYSQLSERVICES_COMPAT}" = "yes" ]; then
+ rpmbuild_options="$rpmbuild_options --define 'mroonga_configure_options --with-libmysqlservices-compat'"
+fi
+
+run eval rpmbuild -ba ${rpmbuild_options} rpmbuild/SPECS/${package_name}.spec
+
+run mv rpmbuild/RPMS/*/* "${rpm_dir}/"
+run mv rpmbuild/SRPMS/* "${srpm_dir}/"
diff --git a/storage/mroonga/packages/yum/env.sh.in b/storage/mroonga/packages/yum/env.sh.in
new file mode 100644
index 00000000000..90e701ec89e
--- /dev/null
+++ b/storage/mroonga/packages/yum/env.sh.in
@@ -0,0 +1,27 @@
+PACKAGE=@PACKAGE@
+VERSION=@VERSION@
+DEPENDED_PACKAGES="
+intltool
+libtool
+gcc
+gcc-c++
+make
+gperf
+readline-devel
+openssl-devel
+time
+wget
+ncurses-devel
+sudo
+pkgconfig
+tar
+cmake
+libaio-devel
+systemtap-sdt-devel
+perl-Time-HiRes
+perl-Env
+perl-Test-Simple
+pam-devel
+groonga-devel
+groonga-normalizer-mysql-devel
+"
diff --git a/storage/mroonga/packages/yum/sign-rpm.sh b/storage/mroonga/packages/yum/sign-rpm.sh
new file mode 100755
index 00000000000..511fb3df0a4
--- /dev/null
+++ b/storage/mroonga/packages/yum/sign-rpm.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 3 ]; then
+ echo "Usage: $0 GPG_UID DESTINATION DISTRIBUTIONS"
+ echo " e.g.: $0 'F10399C0' repositories/ 'fedora centos'"
+ exit 1
+fi
+
+GPG_UID=$1
+DESTINATION=$2
+DISTRIBUTIONS=$3
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+unsigned_rpms()
+{
+ while read rpm; do
+ rpm --checksig "$rpm" | grep -v 'gpg OK' | cut -d":" -f1
+ done
+}
+
+rpms=""
+for distribution in ${DISTRIBUTIONS}; do
+ rpms="${rpms} $(find ${DESTINATION}${distribution} -name '*.rpm' | unsigned_rpms)"
+done
+
+echo "NOTE: YOU JUST ENTER! YOU DON'T NEED TO INPUT PASSWORD!"
+echo " IT'S JUST FOR rpm COMMAND RESTRICTION!"
+run echo $rpms | xargs rpm \
+ -D "_gpg_name ${GPG_UID}" \
+ -D "_gpg_digest_algo sha1" \
+ -D "__gpg /usr/bin/gpg2" \
+ -D "__gpg_check_password_cmd /bin/true true" \
+ -D "__gpg_sign_cmd %{__gpg} gpg --batch --no-verbose --no-armor %{?_gpg_digest_algo:--digest-algo %{_gpg_digest_algo}} --no-secmem-warning -u \"%{_gpg_name}\" -sbo %{__signature_filename} %{__plaintext_filename}" \
+ --resign
diff --git a/storage/mroonga/packages/yum/update-repository.sh b/storage/mroonga/packages/yum/update-repository.sh
new file mode 100755
index 00000000000..630b6c87422
--- /dev/null
+++ b/storage/mroonga/packages/yum/update-repository.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 2 ]; then
+ echo "Usage: $0 DESTINATION DISTRIBUTIONS"
+ echo " e.g.: $0 repositories/ 'fedora centos'"
+ exit 1
+fi
+
+DESTINATION=$1
+DISTRIBUTIONS=$2
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+for distribution in ${DISTRIBUTIONS}; do
+ for dir in ${DESTINATION}${distribution}/*/*; do
+ # "--checksum sha" is for CentOS 5. If we drop CentOS 5 support,
+ # we can remove the option.
+ test -d $dir && run createrepo --checksum sha $dir
+ done;
+done
diff --git a/storage/mroonga/plug.in b/storage/mroonga/plug.in
new file mode 100644
index 00000000000..2c9c15f6c86
--- /dev/null
+++ b/storage/mroonga/plug.in
@@ -0,0 +1,6 @@
+MYSQL_STORAGE_ENGINE(mroonga,,[mroonga],
+[[CJK-ready fulltext search, column store]],
+[max,max-no-ndb])
+MYSQL_PLUGIN_DIRECTORY(mroonga, [storage/mroonga])
+MYSQL_PLUGIN_STATIC(mroonga, [libmroonga.a])
+MYSQL_PLUGIN_DYNAMIC(mroonga, [ha_mroonga.la])
diff --git a/storage/mroonga/plugin_version b/storage/mroonga/plugin_version
new file mode 100644
index 00000000000..958d30d86d0
--- /dev/null
+++ b/storage/mroonga/plugin_version
@@ -0,0 +1 @@
+4.5 \ No newline at end of file
diff --git a/storage/mroonga/required_groonga_normalizer_mysql_version b/storage/mroonga/required_groonga_normalizer_mysql_version
new file mode 100644
index 00000000000..af0b7ddbffd
--- /dev/null
+++ b/storage/mroonga/required_groonga_normalizer_mysql_version
@@ -0,0 +1 @@
+1.0.6
diff --git a/storage/mroonga/required_groonga_version b/storage/mroonga/required_groonga_version
new file mode 100644
index 00000000000..fcdb2e109f6
--- /dev/null
+++ b/storage/mroonga/required_groonga_version
@@ -0,0 +1 @@
+4.0.0
diff --git a/storage/mroonga/sources.am b/storage/mroonga/sources.am
new file mode 100644
index 00000000000..4082e9465a1
--- /dev/null
+++ b/storage/mroonga/sources.am
@@ -0,0 +1,13 @@
+sources = \
+ mrn_macro.hpp \
+ mrn_sys.cpp \
+ mrn_sys.hpp \
+ mrn_constants.hpp \
+ ha_mroonga.cpp \
+ ha_mroonga.hpp \
+ mrn_table.cpp \
+ mrn_table.hpp \
+ mrn_err.h \
+ mrn_mysql.h \
+ mrn_mysql_compat.h \
+ ha_mroonga.def
diff --git a/storage/mroonga/test/Makefile.am b/storage/mroonga/test/Makefile.am
new file mode 100644
index 00000000000..ce75011bee8
--- /dev/null
+++ b/storage/mroonga/test/Makefile.am
@@ -0,0 +1,14 @@
+SUBDIRS = unit
+
+TESTS = run-sql-test.sh
+TESTS_ENVIRONMENT = \
+ NO_MAKE="yes"
+
+if WITH_CUTTER
+TESTS += run-unit-test.sh
+TESTS_ENVIRONMENT += CUTTER="$(CUTTER)"
+endif
+
+EXTRA_DIST = \
+ run-unit-test.sh \
+ run-sql-test.sh
diff --git a/storage/mroonga/test/run-sql-test.sh b/storage/mroonga/test/run-sql-test.sh
new file mode 100755
index 00000000000..690f75c413b
--- /dev/null
+++ b/storage/mroonga/test/run-sql-test.sh
@@ -0,0 +1,232 @@
+#!/bin/sh
+#
+# Copyright(C) 2010 Tetsuro IKEDA
+# Copyright(C) 2010-2013 Kouhei Sutou <kou@clear-code.com>
+# Copyright(C) 2011 Kazuhiko
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+export BASE_DIR="$(cd $(dirname $0); pwd)"
+top_dir="$BASE_DIR/.."
+mroonga_test_dir="${top_dir}/mysql-test/mroonga"
+
+n_processors=1
+case `uname` in
+ Linux)
+ n_processors="$(grep '^processor' /proc/cpuinfo | wc -l)"
+ ;;
+ Darwin)
+ n_processors="$(/usr/sbin/sysctl -n hw.ncpu)"
+ ;;
+ *)
+ :
+ ;;
+esac
+
+if [ "$NO_MAKE" != "yes" ]; then
+ MAKE_ARGS=
+ if [ -n "$n_processors" ]; then
+ MAKE_ARGS="-j${n_processors}"
+ fi
+ make $MAKE_ARGS -C $top_dir > /dev/null || exit 1
+fi
+
+. "${top_dir}/config.sh"
+
+bundled_groonga_normalizer_mysql_dir="${top_dir}/vendor/groonga/vendor/plugins/groonga-normalizer-mysql"
+if [ -d "${bundled_groonga_normalizer_mysql_dir}" ]; then
+ GRN_PLUGINS_DIR="${bundled_groonga_normalizer_mysql_dir}"
+ export GRN_PLUGINS_DIR
+fi
+
+source_mysql_test_dir="${MYSQL_SOURCE_DIR}/mysql-test"
+build_mysql_test_dir="${MYSQL_BUILD_DIR}/mysql-test"
+source_test_suites_dir="${source_mysql_test_dir}/suite"
+source_test_include_dir="${source_mysql_test_dir}/include"
+build_test_suites_dir="${build_mysql_test_dir}/suite"
+build_test_include_dir="${build_mysql_test_dir}/include"
+case "${MYSQL_VERSION}" in
+ 5.1.*)
+ plugins_dir="${MYSQL_BUILD_DIR}/lib/mysql/plugin"
+ if [ ! -d "${build_test_suites_dir}" ]; then
+ mkdir -p "${build_test_suites_dir}"
+ fi
+ ;;
+ *)
+ if [ ! -d "${build_test_suites_dir}" ]; then
+ ln -s "${source_test_suites_dir}" "${build_test_suites_dir}"
+ fi
+ maria_storage_dir="${MYSQL_SOURCE_DIR}/storage/maria"
+ if [ -d "${maria_storage_dir}" ]; then
+ mariadb="yes"
+ else
+ mariadb="no"
+ fi
+ if [ "${mariadb}" = "yes" ]; then
+ if [ "${MRN_BUNDLED}" != "TRUE" ]; then
+ mariadb_mroonga_plugin_dir="${MYSQL_BUILD_DIR}/plugin/mroonga"
+ if [ ! -e "${mariadb_mroonga_plugin_dir}" ]; then
+ ln -s "${top_dir}" "${mariadb_mroonga_plugin_dir}"
+ fi
+ fi
+ plugins_dir=
+ else
+ plugins_dir="${MYSQL_SOURCE_DIR}/lib/plugin"
+ fi
+ ;;
+esac
+
+same_link_p()
+{
+ src=$1
+ dest=$2
+ if [ -L "$dest" -a "$(readlink "$dest")" = "$src" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+mroonga_mysql_test_suite_dir="${build_test_suites_dir}/mroonga"
+if ! same_link_p "${mroonga_test_dir}" "${mroonga_mysql_test_suite_dir}"; then
+ rm -rf "${mroonga_mysql_test_suite_dir}"
+ ln -s "${mroonga_test_dir}" "${mroonga_mysql_test_suite_dir}"
+fi
+
+innodb_test_suite_dir="${build_test_suites_dir}/innodb"
+mroonga_wrapper_innodb_test_suite_name="mroonga_wrapper_innodb"
+mroonga_wrapper_innodb_test_suite_dir="${build_test_suites_dir}/${mroonga_wrapper_innodb_test_suite_name}"
+mroonga_wrapper_innodb_include_dir="${mroonga_wrapper_innodb_test_suite_dir}/include/"
+if [ "$0" -nt "$(dirname "${mroonga_wrapper_innodb_test_suite_dir}")" ]; then
+ rm -rf "${mroonga_wrapper_innodb_test_suite_dir}"
+fi
+if [ ! -d "${mroonga_wrapper_innodb_test_suite_dir}" ]; then
+ cp -rp "${innodb_test_suite_dir}" "${mroonga_wrapper_innodb_test_suite_dir}"
+ mkdir -p "${mroonga_wrapper_innodb_include_dir}"
+ cp -rp "${source_test_include_dir}"/innodb[-_]*.inc \
+ "${mroonga_wrapper_innodb_include_dir}"
+ ruby -i'' \
+ -pe "\$_.gsub!(/\\bengine\\s*=\\s*innodb\\b([^;\\n]*)/i,
+ \"ENGINE=mroonga\\\1 COMMENT='ENGINE \\\"InnoDB\\\"'\")
+ \$_.gsub!(/\\b(storage_engine\\s*=\\s*)innodb\\b([^;\\n]*)/i,
+ \"\\\1mroonga\")
+ \$_.gsub!(/^(--\\s*source\\s+)(include\\/innodb)/i,
+ \"\\\1suite/mroonga_wrapper_innodb/\\\2\")
+ " \
+ ${mroonga_wrapper_innodb_test_suite_dir}/r/*.result \
+ ${mroonga_wrapper_innodb_test_suite_dir}/t/*.test \
+ ${mroonga_wrapper_innodb_test_suite_dir}/include/*.inc
+ sed -i'' \
+ -e '1 i --source ../mroonga/include/mroonga/have_mroonga.inc' \
+ ${mroonga_wrapper_innodb_test_suite_dir}/t/*.test
+fi
+
+all_test_suite_names=""
+suite_dir="${mroonga_test_dir}/.."
+cd "${suite_dir}"
+suite_dir="$(pwd)"
+for test_suite_name in \
+ $(find mroonga -type d -name 'include' '!' -prune -o \
+ -type d '!' -name 'mroonga' \
+ '!' -name 'include' \
+ '!' -name '[tr]'); do
+ if [ -n "${all_test_suite_names}" ]; then
+ all_test_suite_names="${all_test_suite_names},"
+ fi
+ all_test_suite_names="${all_test_suite_names}${test_suite_name}"
+done
+cd -
+
+if [ -n "${plugins_dir}" ]; then
+ if [ -d "${top_dir}/.libs" ]; then
+ make -C ${top_dir} \
+ install-pluginLTLIBRARIES \
+ plugindir=${plugins_dir} > /dev/null || \
+ exit 1
+ else
+ mkdir -p "${plugins_dir}"
+ cp "${top_dir}/ha_mroonga.so" "${plugins_dir}" || exit 1
+ fi
+fi
+
+test_suite_names=""
+test_names=""
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --manual-gdb|--debug)
+ n_processors=1
+ break
+ ;;
+ --*)
+ break
+ ;;
+ *)
+ case "$1" in
+ */t/*.test)
+ test_suite_name=$(echo "$1" | sed -e 's,/t/.*\.test,,g')
+ test_suite_name=$(cd "$test_suite_name" && pwd)
+ test_name=$(echo "$1" | sed -e 's,.*/t/\(.*\)\.test,\1,g')
+ ;;
+ *)
+ if [ -d "$1" ]; then
+ test_suite_name=$(cd "$1" && pwd)
+ else
+ test_suite_name="$1"
+ fi
+ test_name=""
+ ;;
+ esac
+ shift
+
+ if [ -n "${test_name}" ]; then
+ if [ -n "${test_names}" ]; then
+ test_names="${test_names}|"
+ fi
+ test_names="${test_names}.*${test_name}"
+ fi
+
+ test_suite_name=$(echo "$test_suite_name" | sed -e "s,^${suite_dir}/,,")
+ if echo "${test_suite_names}" | grep --quiet "${test_suite_name}"; then
+ continue
+ fi
+ if [ -n "${test_suite_names}" ]; then
+ test_suite_names="${test_suite_names},"
+ fi
+ test_suite_names="${test_suite_names}${test_suite_name}"
+ ;;
+ esac
+done
+
+if [ -z "$test_suite_names" ]; then
+ test_suite_names="${all_test_suite_names}"
+fi
+
+mysql_test_run_args=""
+mysql_test_run_args="${mysql_test_run_args} --mem"
+mysql_test_run_args="${mysql_test_run_args} --no-check-testcases"
+mysql_test_run_args="${mysql_test_run_args} --parallel=${n_processors}"
+mysql_test_run_args="${mysql_test_run_args} --retry=1"
+mysql_test_run_args="${mysql_test_run_args} --suite=${test_suite_names}"
+mysql_test_run_args="${mysql_test_run_args} --force"
+mysql_test_run_args="${mysql_test_run_args} --mysqld=--loose-plugin-load-add=ha_mroonga.so"
+mysql_test_run_args="${mysql_test_run_args} --mysqld=--loose-plugin-mroonga=ON"
+if [ -n "$test_names" ]; then
+ mysql_test_run_args="${mysql_test_run_args} --do-test=${test_names}"
+fi
+
+(cd "$build_mysql_test_dir" && \
+ ./mysql-test-run.pl \
+ ${mysql_test_run_args} \
+ "$@")
diff --git a/storage/mroonga/test/run-unit-test.sh b/storage/mroonga/test/run-unit-test.sh
new file mode 100755
index 00000000000..6d99513123d
--- /dev/null
+++ b/storage/mroonga/test/run-unit-test.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+export BASE_DIR="`dirname $0`"
+top_dir="$BASE_DIR/.."
+
+if test -z "$NO_MAKE"; then
+ MAKE_ARGS=
+ case `uname` in
+ Linux)
+ MAKE_ARGS="-j$(grep '^processor' /proc/cpuinfo | wc -l)"
+ ;;
+ Darwin)
+ MAKE_ARGS="-j$(/usr/sbin/sysctl -n hw.ncpu)"
+ ;;
+ *)
+ :
+ ;;
+ esac
+ make $MAKE_ARGS -C $top_dir > /dev/null || exit 1
+fi
+
+if test -z "$CUTTER"; then
+ CUTTER="`make -s -C $top_dir echo-cutter`"
+fi
+export CUTTER
+
+CUTTER_ARGS=
+CUTTER_WRAPPER=
+if test x"$STOP" = x"yes"; then
+ CUTTER_ARGS="-v v --fatal-failures"
+else
+ CUTTER_ARGS="-v v"
+fi
+
+if test x"$CUTTER_DEBUG" = x"yes"; then
+ if test x"$TUI_DEBUG" = x"yes"; then
+ CUTTER_WRAPPER="$top_dir/libtool --mode=execute gdb --tui --args"
+ else
+ CUTTER_WRAPPER="$top_dir/libtool --mode=execute gdb --args"
+ fi
+ CUTTER_ARGS="--keep-opening-modules"
+elif test x"$CUTTER_CHECK_LEAK" = x"yes"; then
+ CUTTER_WRAPPER="$top_dir/libtool --mode=execute valgrind "
+ CUTTER_WRAPPER="$CUTTER_WRAPPER --leak-check=full --show-reachable=yes -v"
+ CUTTER_ARGS="--keep-opening-modules"
+fi
+
+CUTTER_ARGS="$CUTTER_ARGS -s $BASE_DIR"
+$CUTTER_WRAPPER $CUTTER $CUTTER_ARGS "$@" $BASE_DIR
diff --git a/storage/mroonga/test/unit/Makefile.am b/storage/mroonga/test/unit/Makefile.am
new file mode 100644
index 00000000000..30c9ca20486
--- /dev/null
+++ b/storage/mroonga/test/unit/Makefile.am
@@ -0,0 +1,34 @@
+if WITH_CUTTER
+noinst_LTLIBRARIES = \
+ test_mrn_sys.la \
+ test_mrn_path_mapper.la
+endif
+
+AM_CPPFLAGS = \
+ $(GROONGA_CFLAGS) \
+ $(CPPCUTTER_CFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/lib
+
+AM_LDFLAGS = \
+ -module \
+ -rpath $(libdir) \
+ -avoid-version \
+ -no-undefined
+
+LIBS = \
+ $(CPPCUTTER_LIBS) \
+ $(GROONGA_LIBS) \
+ $(MECAB_LIBS)
+
+EXTERNAL_SRC = ../../mrn_sys.cpp
+
+test_mrn_sys_la_SOURCES = \
+ $(EXTERNAL_SRC) \
+ test_mrn_sys.cpp
+
+test_mrn_path_mapper_la_SOURCES = \
+ test_mrn_path_mapper.cpp
+
+test_mrn_path_mapper_la_LIBADD = \
+ $(top_builddir)/lib/libmrn_no_mysql.la
diff --git a/storage/mroonga/test/unit/test_mrn_path_mapper.cpp b/storage/mroonga/test/unit/test_mrn_path_mapper.cpp
new file mode 100644
index 00000000000..70009c5b32e
--- /dev/null
+++ b/storage/mroonga/test/unit/test_mrn_path_mapper.cpp
@@ -0,0 +1,116 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <cppcutter.h>
+
+#include <mrn_path_mapper.hpp>
+
+namespace test_mrn_path_mapper {
+ namespace db_path {
+ namespace without_prefix {
+ void test_normal_db() {
+ mrn::PathMapper mapper("./db/", NULL);
+ cppcut_assert_equal("db.mrn", mapper.db_path());
+ }
+
+ void test_normal_table() {
+ mrn::PathMapper mapper("./db/table", NULL);
+ cppcut_assert_equal("db.mrn", mapper.db_path());
+ }
+
+ void test_temporary_table() {
+ mrn::PathMapper mapper("/tmp/mysqld.1/#sql27c5_1_0", NULL);
+ cppcut_assert_equal("/tmp/mysqld.1/#sql27c5_1_0.mrn",
+ mapper.db_path());
+ }
+ }
+
+ namespace with_prefix {
+ void test_normal_db() {
+ mrn::PathMapper mapper("./db/", "mroonga.data/");
+ cppcut_assert_equal("mroonga.data/db.mrn", mapper.db_path());
+ }
+
+ void test_normal_table() {
+ mrn::PathMapper mapper("./db/table", "mroonga.data/");
+ cppcut_assert_equal("mroonga.data/db.mrn", mapper.db_path());
+ }
+
+ void test_temporary_table() {
+ mrn::PathMapper mapper("/tmp/mysqld.1/#sql27c5_1_0", "mroonga.data/");
+ cppcut_assert_equal("/tmp/mysqld.1/#sql27c5_1_0.mrn",
+ mapper.db_path());
+ }
+ }
+ }
+
+ namespace db_name {
+ void test_normal_db() {
+ mrn::PathMapper mapper("./db/", NULL);
+ cppcut_assert_equal("db", mapper.db_name());
+ }
+
+ void test_normal_table() {
+ mrn::PathMapper mapper("./db/table", NULL);
+ cppcut_assert_equal("db", mapper.db_name());
+ }
+
+ void test_temporary_table() {
+ mrn::PathMapper mapper("/tmp/mysqld.1/#sql27c5_1_0", NULL);
+ cppcut_assert_equal("/tmp/mysqld.1/#sql27c5_1_0",
+ mapper.db_name());
+ }
+ }
+
+ namespace table_name {
+ void test_normal_table() {
+ mrn::PathMapper mapper("./db/table", NULL);
+ cppcut_assert_equal("table", mapper.table_name());
+ }
+
+ void test_temporary_table() {
+ mrn::PathMapper mapper("/tmp/mysqld.1/#sql27c5_1_0", NULL);
+ cppcut_assert_equal("#sql27c5_1_0", mapper.table_name());
+ }
+
+ void test_underscore_start_table() {
+ mrn::PathMapper mapper("./db/_table", NULL);
+ cppcut_assert_equal("@005ftable", mapper.table_name());
+ }
+ }
+
+ namespace mysql_table_name {
+ void test_normal_table() {
+ mrn::PathMapper mapper("./db/table", NULL);
+ cppcut_assert_equal("table", mapper.mysql_table_name());
+ }
+
+ void test_temporary_table() {
+ mrn::PathMapper mapper("/tmp/mysqld.1/#sql27c5_1_0", NULL);
+ cppcut_assert_equal("#sql27c5_1_0", mapper.mysql_table_name());
+ }
+
+ void test_underscore_start_table() {
+ mrn::PathMapper mapper("./db/_table", NULL);
+ cppcut_assert_equal("_table", mapper.mysql_table_name());
+ }
+ }
+}
+
diff --git a/storage/mroonga/test/unit/test_mrn_sys.cpp b/storage/mroonga/test/unit/test_mrn_sys.cpp
new file mode 100644
index 00000000000..fcc4b768efe
--- /dev/null
+++ b/storage/mroonga/test/unit/test_mrn_sys.cpp
@@ -0,0 +1,95 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2010 Tetsuro IKEDA
+ Copyright(C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <cppcutter.h>
+
+#include <mrn_sys.hpp>
+
+static grn_ctx *ctx;
+static grn_obj *db;
+static grn_hash *hash;
+static grn_obj buffer;
+
+namespace test_mrn_sys
+{
+ void cut_startup()
+ {
+ ctx = (grn_ctx *)malloc(sizeof(grn_ctx));
+ grn_init();
+ grn_ctx_init(ctx, 0);
+ db = grn_db_create(ctx, NULL, NULL);
+ grn_ctx_use(ctx, db);
+ }
+
+ void cut_shutdown()
+ {
+ grn_obj_unlink(ctx, db);
+ grn_ctx_fin(ctx);
+ grn_fin();
+ free(ctx);
+ }
+
+ void cut_setup()
+ {
+ hash = grn_hash_create(ctx, NULL, 1024, sizeof(grn_obj *),
+ GRN_OBJ_KEY_VAR_SIZE);
+ GRN_TEXT_INIT(&buffer, 0);
+ }
+
+ void cut_teardown()
+ {
+ grn_hash_close(ctx, hash);
+ grn_obj_unlink(ctx, &buffer);
+ }
+
+ void test_mrn_hash_put()
+ {
+ const char *key = "mroonga";
+
+ cut_assert_true(mrn_hash_put(ctx, hash, key, &buffer));
+ cut_assert_false(mrn_hash_put(ctx, hash, key, &buffer));
+ }
+
+ void test_mrn_hash_get()
+ {
+ const char *key = "mroonga";
+ const char *value = "A storage engine based on groonga.";
+ grn_obj *result;
+
+ GRN_TEXT_SETS(ctx, &buffer, value);
+ GRN_TEXT_PUT(ctx, &buffer, "\0", 1);
+
+ mrn_hash_put(ctx, hash, key, &buffer);
+ cut_assert_true(mrn_hash_get(ctx, hash, key, &result));
+ cppcut_assert_equal(value, GRN_TEXT_VALUE(&buffer));
+ }
+
+ void test_mrn_hash_remove()
+ {
+ const char *key = "mroonga";
+
+ mrn_hash_put(ctx, hash, key, &buffer);
+
+ cut_assert_false(mrn_hash_remove(ctx, hash, "nonexistent"));
+ cut_assert_true(mrn_hash_remove(ctx, hash, key));
+ cut_assert_false(mrn_hash_remove(ctx, hash, key));
+ }
+}
diff --git a/storage/mroonga/tools/Makefile.am b/storage/mroonga/tools/Makefile.am
new file mode 100644
index 00000000000..b65b930029b
--- /dev/null
+++ b/storage/mroonga/tools/Makefile.am
@@ -0,0 +1,6 @@
+noinstall_ruby_scripts = \
+ prepare-sphinx-html.rb \
+ upload-to-github.rb
+
+EXTRA_DIST = \
+ $(noinstall_ruby_scripts)
diff --git a/storage/mroonga/tools/prepare-sphinx-html.rb b/storage/mroonga/tools/prepare-sphinx-html.rb
new file mode 100755
index 00000000000..76eed24a042
--- /dev/null
+++ b/storage/mroonga/tools/prepare-sphinx-html.rb
@@ -0,0 +1,183 @@
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
+
+if ARGV.size != 2
+ puts "Usage: #{$0} SOURCE_DIR DEST_DIR"
+ exit(false)
+end
+
+require 'pathname'
+require "fileutils"
+
+def fix_link(text, extension, language)
+ send("fix_#{extension}_link", text, language)
+end
+
+def fix_link_path(text)
+ text.gsub(/\b_(sources|static|images)\b/, '\1')
+end
+
+def fix_language_link(url, language)
+ url.gsub(/\A((?:\.\.\/){2,})([a-z]{2})\/html\//) do
+ relative_base_path = $1
+ link_language = $2
+ close_quote = $3
+ if language == "en"
+ relative_base_path = relative_base_path.gsub(/\A\.\.\//, '')
+ end
+ if link_language != "en"
+ relative_base_path += "#{link_language}/"
+ end
+ "#{relative_base_path}docs/"
+ end
+end
+
+def fix_html_link(html, language)
+ html = html.gsub(/(href|src)="(.+?)"/) do
+ attribute = $1
+ link = $2
+ link = fix_link_path(link)
+ link = fix_language_link(link, language)
+ "#{attribute}=\"#{link}\""
+ end
+ html.gsub(/(id="top-link" href=)"(.+?)"/) do
+ prefix = $1
+ top_path = $2.gsub(/\/index\.html\z/, '/')
+ top_path = "./" if ["index.html", "#"].include?(top_path)
+ "#{prefix}\"#{top_path}../\""
+ end
+end
+
+def add_language_annotation_to_source_label(html)
+ html.gsub(/>(ソースコードを表示)</) do
+ label = $1
+ ">#{label}(英語)<"
+ end
+end
+
+def fix_js_link(js, language)
+ fix_link_path(js)
+end
+
+LANGUAGE_TO_LOCALE = {
+ "ja" => "ja_JP",
+ "en" => "en_US",
+}
+
+def insert_facebook_html_header(html)
+ html.gsub(/<\/head>/) do
+ <<-HTML
+ <meta property="fb:page_id" content="238184682903165" /><!-- mroonga -->
+ <meta property="fb:admins" content="664204556" /><!-- kouhei.sutou -->
+ <meta property="og:type" content="product" />
+ <meta property="og:image" content="http://mroonga.org/images/logos/mroonga-icon-full-size.png" />
+ <meta property="og:site_name" content="mroonga" />
+
+ <link rel="stylesheet" href="/css/sphinx.css" type="text/css" />
+ </head>
+ HTML
+ end
+end
+
+def insert_facebook_html_fb_root(html)
+ html.gsub(/<body>/) do
+ <<-HTML
+ <body>
+ <div id="fb-root"></div>
+ HTML
+ end
+end
+
+def insert_facebook_html_buttons(html)
+ html.gsub(/(<div class="other-language-links">)/) do
+ <<-HTML
+ <div class="facebook-buttons">
+ <fb:like href="http://www.facebook.com/pages/mroonga/238184682903165"
+ layout="standard"
+ width="290"></fb:like>
+ </div>
+ #{$1}
+ HTML
+ end
+end
+
+def insert_facebook_html_footer(html, language)
+ locale = LANGUAGE_TO_LOCALE[language]
+ raise "unknown locale for language #{language.inspect}" if locale.nil?
+ html.gsub(/<\/body>/) do
+ <<-HTML
+ <script src="http://connect.facebook.net/#{locale}/all.js"></script>
+
+ <script>
+ FB.init({
+ appId : null,
+ status : true, // check login status
+ cookie : true, // enable cookies to allow the server to access the session
+ xfbml : true // parse XFBML
+ });
+ </script>
+ </body>
+ HTML
+ end
+end
+
+def insert_facebook_html(html, language)
+ html = insert_facebook_html_header(html)
+ html = insert_facebook_html_fb_root(html)
+ html = insert_facebook_html_buttons(html)
+ html = insert_facebook_html_footer(html, language)
+ html
+end
+
+source_dir, dest_dir = ARGV
+
+source_dir = Pathname.new(source_dir)
+dest_dir = Pathname.new(dest_dir)
+
+language_dirs = []
+source_dir.each_entry do |top_level_path|
+ language_dirs << top_level_path if /\A[a-z]{2}\z/ =~ top_level_path.to_s
+end
+
+language_dirs.each do |language_dir|
+ language = language_dir.to_s
+ language_source_dir = source_dir + language_dir + "html"
+ language_dest_dir = dest_dir + language_dir
+ language_source_dir.find do |source_path|
+ relative_path = source_path.relative_path_from(language_source_dir)
+ dest_path = language_dest_dir + relative_path
+ if source_path.directory?
+ dest_path.mkpath
+ else
+ case source_path.extname
+ when ".html", ".js"
+ content = source_path.read
+ extension = source_path.extname.gsub(/\A\./, '')
+ content = fix_link(content, extension, language)
+ if extension == "html"
+ content = insert_facebook_html(content, language)
+ content = add_language_annotation_to_source_label(content)
+ end
+ dest_path.open("wb") do |dest|
+ dest.print(content.strip)
+ end
+ FileUtils.touch(dest_path, :mtime => source_path.mtime)
+ else
+ case source_path.basename.to_s
+ when ".buildinfo"
+ # ignore
+ else
+ FileUtils.cp(source_path, dest_path, :preserve => true)
+ end
+ end
+ end
+ end
+end
+
+dest_dir.find do |dest_path|
+ if dest_path.directory? and /\A_/ =~ dest_path.basename.to_s
+ normalized_dest_path = dest_path + ".."
+ normalized_dest_path += dest_path.basename.to_s.gsub(/\A_/, '')
+ FileUtils.mv(dest_path, normalized_dest_path)
+ end
+end
diff --git a/storage/mroonga/tools/travis/before_script.sh b/storage/mroonga/tools/travis/before_script.sh
new file mode 100755
index 00000000000..fb0d78d4922
--- /dev/null
+++ b/storage/mroonga/tools/travis/before_script.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# Copyright(C) 2012-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# set -x
+set -e
+
+if [ "$MRN_BUNDLED" = "yes" ]; then
+ cmake . -DWITH_DEBUG=1
+else
+ ./autogen.sh
+
+ if [ -d /opt/mysql/ ]; then
+ PATH=$(echo /opt/mysql/server-*/bin/):$PATH
+ fi
+ ./configure \
+ --with-mysql-source=$PWD/vendor/mysql
+fi
diff --git a/storage/mroonga/tools/travis/install.sh b/storage/mroonga/tools/travis/install.sh
new file mode 100755
index 00000000000..64bcde764cf
--- /dev/null
+++ b/storage/mroonga/tools/travis/install.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+#
+# Copyright(C) 2012-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# set -x
+set -e
+
+mariadb_download_base=http://mirror.jmu.edu/pub/mariadb
+
+export GROONGA_MASTER=yes
+export GROONGA_NORMALIZER_MYSQL_MASTER=yes
+
+curl --silent --location https://github.com/groonga/groonga/raw/master/data/travis/setup.sh | sh
+curl --silent --location https://github.com/groonga/groonga-normalizer-mysql/raw/master/data/travis/setup.sh | sh
+# curl --silent --location https://github.com/clear-code/cutter/raw/master/data/travis/setup.sh | sh
+
+if [ ! -f /usr/lib/groonga/plugins/tokenizers/mecab.so ]; then
+ sudo apt-get -qq -y install groonga-tokenizer-mecab
+fi
+
+if [ "${MRN_BUNDLED}" = "yes" ]; then
+ mkdir -p .mroonga
+ mv * .mroonga/
+ mv .mroonga/tools ./
+ sudo apt-get -qq -y build-dep mysql-server
+ # Support MariaDB for now.
+ download_base=${mariadb_download_base}/${MYSQL_VERSION}
+ tar_gz=${MYSQL_VERSION}.tar.gz
+ curl -O ${download_base}/source/${tar_gz}
+ tar xzf $tar_gz
+ mv ${MYSQL_VERSION}/* ./
+ mv .mroonga storage/mroonga
+ rm -rf ${MYSQL_VERSION}
+else
+ mkdir -p vendor
+ cd vendor
+
+ version=$(echo "$MYSQL_VERSION" | sed -e 's/^\(mysql\|mariadb\)-//')
+ series=$(echo "$version" | sed -e 's/\.[0-9]*\(-\?[a-z]*\)\?$//g')
+ case "$MYSQL_VERSION" in
+ mysql-*)
+ sudo apt-get -qq update
+ sudo apt-get -qq -y build-dep mysql-server
+ if [ "$version" = "system" ]; then
+ sudo apt-get -qq -y install \
+ mysql-server mysql-testsuite libmysqld-dev
+ apt-get -qq source mysql-server
+ ln -s $(find . -maxdepth 1 -type d | sort | tail -1) mysql
+ else
+ download_base="http://cdn.mysql.com/Downloads/MySQL-${series}/"
+ if [ "$(uname -m)" = "x86_64" ]; then
+ architecture=x86_64
+ else
+ architecture=i686
+ fi
+ deb=mysql-${version}-debian6.0-${architecture}.deb
+ tar_gz=mysql-${version}.tar.gz
+ curl -O ${download_base}${deb} &
+ curl -O ${download_base}${tar_gz} &
+ wait
+ sudo apt-get -qq -y install libaio1
+ sudo dpkg -i $deb
+ tar xzf $tar_gz
+ ln -s mysql-${version} mysql
+ fi
+ ;;
+ mariadb-*)
+ sudo apt-get -qq -y remove --purge mysql-common
+
+ distribution=$(lsb_release --short --id | tr 'A-Z' 'a-z')
+ code_name=$(lsb_release --short --codename)
+ component=main
+ apt_url_base="${mariadb_download_base}/repo/${series}"
+ cat <<EOF | sudo tee /etc/apt/sources.list.d/mariadb.list
+deb ${apt_url_base}/${distribution}/ ${code_name} ${component}
+deb-src ${apt_url_base}/${distribution}/ ${code_name} ${component}
+EOF
+ sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 0xcbcb082a1bb943db
+ sudo apt-get -qq update
+ sudo apt-get -qq -y build-dep mariadb-server
+ sudo apt-get -qq -y install \
+ mariadb-server libmariadbclient-dev mariadb-test
+ apt-get -qq source mariadb-server
+ ln -s $(find . -maxdepth 1 -type d | sort | tail -1) mysql
+ ;;
+ esac
+
+ cd ..
+fi
diff --git a/storage/mroonga/tools/travis/script.sh b/storage/mroonga/tools/travis/script.sh
new file mode 100755
index 00000000000..9eb8a805e23
--- /dev/null
+++ b/storage/mroonga/tools/travis/script.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+#
+# Copyright(C) 2012-2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# set -x
+set -e
+
+base_dir="$(cd $(dirname $0); pwd)"
+top_dir="${base_dir}/../.."
+
+bundled_mroonga_dir="${top_dir}/storage/mroonga"
+if [ -f "${bundled_mroonga_dir}/config.sh" ]; then
+ mroonga_dir="${bundled_mroonga_dir}"
+ . "${bundled_mroonga_dir}/config.sh"
+else
+ mroonga_dir="${top_dir}"
+ . "${top_dir}/config.sh"
+fi
+
+if [ "${MRN_BUNDLED}" = "TRUE" ]; then
+ n_processors=1
+else
+ n_processors="$(grep '^processor' /proc/cpuinfo | wc -l)"
+fi
+
+build()
+{
+ make -j${n_processors} > /dev/null
+}
+
+run_unit_test()
+{
+ if [ "${MRN_BUNDLED}" != "TRUE" ]; then
+ NO_MAKE=yes ${mroonga_dir}/test/run-unit-test.sh
+ fi
+}
+
+prepare_mysql_test_dir()
+{
+ mysql_test_dir=/usr/mysql-test
+ if [ -d /usr/lib/mysql-testsuite/ ]; then
+ sudo cp -a /usr/lib/mysql-testsuite/ ${mysql_test_dir}/
+ elif [ -d /usr/share/mysql/mysql-test/ ]; then
+ sudo cp -a /usr/share/mysql/mysql-test/ ${mysql_test_dir}/
+ elif [ -d /opt/mysql/ ]; then
+ mysql_test_dir=$(echo /opt/mysql/server-*/mysql-test)
+ else
+ sudo cp -a ${MYSQL_SOURCE_DIR}/mysql-test/ ${mysql_test_dir}/
+ fi
+ sudo chown -R $(id -u):$(id -g) ${mysql_test_dir}/
+
+ cp -a ${mroonga_dir}/mysql-test/mroonga/ ${mysql_test_dir}/suite/
+}
+
+collect_test_suite_names()
+{
+ cd ${mysql_test_dir}/suite/
+ test_suite_names=""
+ for test_suite_name in $(find mroonga -type d '!' -name '[tr]'); do
+ if [ -n "${test_suite_names}" ]; then
+ test_suite_names="${test_suite_names},"
+ fi
+ test_suite_names="${test_suite_names}${test_suite_name}"
+ done
+ cd -
+}
+
+prepare_sql_test()
+{
+ sudo make install > /dev/null
+ prepare_mysql_test_dir
+ collect_test_suite_names
+}
+
+run_sql_test()
+{
+ test_args=()
+ if [ "${MRN_TEST_EMBEDDED}" = "yes" ]; then
+ test_args=("${test_args[@]}" "--embedded-server")
+ fi
+
+ if [ "${MRN_BUNDLED}" = "TRUE" ]; then
+ ${mroonga_dir}/test/run-sql-test.sh "${test_args[@]}"
+ else
+ prepare_sql_test
+
+ cd ${mysql_test_dir}/
+ ./mysql-test-run.pl \
+ "${test_args[@]}" \
+ --no-check-testcases \
+ --parallel="${n_processors}" \
+ --retry=1 \
+ --suite="${test_suite_names}" \
+ --force
+ fi
+}
+
+build
+# run_unit_test
+run_sql_test
diff --git a/storage/mroonga/tools/upload-to-github.rb b/storage/mroonga/tools/upload-to-github.rb
new file mode 100755
index 00000000000..572d65c3f0b
--- /dev/null
+++ b/storage/mroonga/tools/upload-to-github.rb
@@ -0,0 +1,42 @@
+#!/usr/bin/env ruby
+
+if ARGV.size < 1
+ puts "Usage: #{$0} USER FILE ..."
+ puts " e.g.: #{$0} kou mroonga-1.10.tar.gz ..."
+ exit false
+end
+
+require "rubygems"
+require "github_api"
+require "mime/types"
+
+user, *files = *ARGV
+
+print "password[#{user}]: "
+system("stty -echo")
+password = STDIN.gets.chomp
+system("stty echo")
+puts
+
+github = Github.new(:login => user, :password => password)
+files.each do |file|
+ content_type = MIME::Types.type_for(file)[0].to_s
+ resource = github.repos.downloads.create("mroonga", "mroonga",
+ :name => File.basename(file),
+ :size => File.size(file),
+ :description => File.basename(file),
+ :content_type => content_type)
+ p resource
+
+ system("curl",
+ "-F", "key=#{resource.path}",
+ "-F", "acl=#{resource.acl}",
+ "-F", "success_action_status=201",
+ "-F", "Filename=#{resource.name}",
+ "-F", "AWSAccessKeyId=#{resource.accesskeyid}",
+ "-F", "Policy=#{resource.policy}",
+ "-F", "Signature=#{resource.signature}",
+ "-F", "Content-Type=#{resource.mime_type}",
+ "-F", "file=@#{file}",
+ resource.s3_url)
+end
diff --git a/storage/mroonga/udf/Makefile.am b/storage/mroonga/udf/Makefile.am
new file mode 100644
index 00000000000..f3c2966f6df
--- /dev/null
+++ b/storage/mroonga/udf/Makefile.am
@@ -0,0 +1,10 @@
+AM_CPPFLAGS = \
+ $(MYSQL_INCLUDES) \
+ $(GROONGA_CFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/lib
+
+noinst_LTLIBRARIES = \
+ libmrn_udf.la
+
+include sources.am
diff --git a/storage/mroonga/udf/mrn_udf_command.cpp b/storage/mroonga/udf/mrn_udf_command.cpp
new file mode 100644
index 00000000000..67dbf8bcd1d
--- /dev/null
+++ b/storage/mroonga/udf/mrn_udf_command.cpp
@@ -0,0 +1,165 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ Copyright(C) 2010 Tetsuro IKEDA
+ Copyright(C) 2010-2013 Kentoku SHIBA
+ Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <mrn_mysql.h>
+#include <mrn_mysql_compat.h>
+#include <mrn_path_mapper.hpp>
+#include <mrn_windows.hpp>
+#include <mrn_macro.hpp>
+
+MRN_BEGIN_DECLS
+
+struct CommandInfo
+{
+ grn_ctx ctx;
+ String result;
+};
+
+MRN_API my_bool mroonga_command_init(UDF_INIT *initid, UDF_ARGS *args,
+ char *message)
+{
+ CommandInfo *info = NULL;
+
+ initid->ptr = NULL;
+ if (args->arg_count != 1) {
+ sprintf(message,
+ "mroonga_command(): Incorrect number of arguments: %u for 1",
+ args->arg_count);
+ goto error;
+ }
+ if (args->arg_type[0] != STRING_RESULT) {
+ strcpy(message,
+ "mroonga_command(): The 1st argument must be command as string");
+ goto error;
+ }
+ initid->maybe_null = 1;
+ initid->const_item = 1;
+
+ info = (CommandInfo *)my_malloc(sizeof(CommandInfo),
+ MYF(MY_WME | MY_ZEROFILL));
+ if (!info) {
+ strcpy(message, "mroonga_command(): out of memory");
+ goto error;
+ }
+
+ grn_ctx_init(&(info->ctx), 0);
+ {
+ const char *current_db_path = current_thd->db;
+ const char *action;
+ if (current_db_path) {
+ action = "open database";
+ mrn::PathMapper mapper(current_db_path);
+ grn_db_open(&(info->ctx), mapper.db_path());
+ } else {
+ action = "create anonymous database";
+ grn_db_create(&(info->ctx), NULL, NULL);
+ }
+ if (info->ctx.rc != GRN_SUCCESS) {
+ sprintf(message,
+ "mroonga_command(): failed to %s: %s",
+ action,
+ info->ctx.errbuf);
+ goto error;
+ }
+ }
+
+ initid->ptr = (char *)info;
+
+ return FALSE;
+
+error:
+ if (info) {
+ grn_obj *db;
+ db = grn_ctx_db(&(info->ctx));
+ if (db) {
+ grn_obj_close(&(info->ctx), db);
+ }
+ grn_ctx_fin(&(info->ctx));
+ my_free(info, MYF(0));
+ }
+ return TRUE;
+}
+
+MRN_API char *mroonga_command(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *length, char *is_null, char *error)
+{
+ CommandInfo *info = (CommandInfo *)initid->ptr;
+ grn_ctx *ctx = &(info->ctx);
+ char *command;
+ unsigned int command_length;
+ int flags = 0;
+
+ if (!args->args[0]) {
+ *is_null = 1;
+ return NULL;
+ }
+
+ *is_null = 0;
+ command = args->args[0];
+ command_length = args->lengths[0];
+
+ grn_ctx_send(ctx, command, command_length, 0);
+ if (ctx->rc) {
+ my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0));
+ goto error;
+ }
+
+ info->result.length(0);
+ do {
+ char *buffer;
+ unsigned int buffer_length;
+ grn_ctx_recv(ctx, &buffer, &buffer_length, &flags);
+ if (ctx->rc) {
+ my_message(ER_ERROR_ON_READ, ctx->errbuf, MYF(0));
+ goto error;
+ }
+ if (buffer_length > 0) {
+ if (info->result.reserve(buffer_length)) {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0), HA_ERR_OUT_OF_MEM);
+ goto error;
+ }
+ info->result.q_append(buffer, buffer_length);
+ }
+ } while (flags & GRN_CTX_MORE);
+
+ *length = info->result.length();
+ return (char *)(info->result.ptr());
+
+error:
+ *error = 1;
+ return NULL;
+}
+
+MRN_API void mroonga_command_deinit(UDF_INIT *initid)
+{
+ CommandInfo *info = (CommandInfo *)initid->ptr;
+ if (info) {
+ grn_obj *db = grn_ctx_db(&(info->ctx));
+ if (db) {
+ grn_obj_close(&(info->ctx), db);
+ }
+ grn_ctx_fin(&(info->ctx));
+ info->result.free();
+ my_free(info, MYF(0));
+ }
+}
+
+MRN_END_DECLS
diff --git a/storage/mroonga/udf/mrn_udf_escape.cpp b/storage/mroonga/udf/mrn_udf_escape.cpp
new file mode 100644
index 00000000000..ff997e7581f
--- /dev/null
+++ b/storage/mroonga/udf/mrn_udf_escape.cpp
@@ -0,0 +1,154 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <mrn_mysql.h>
+#include <mrn_mysql_compat.h>
+#include <mrn_path_mapper.hpp>
+#include <mrn_windows.hpp>
+#include <mrn_macro.hpp>
+
+MRN_BEGIN_DECLS
+
+struct EscapeInfo
+{
+ grn_ctx ctx;
+ grn_obj target_characters;
+ grn_obj escaped_query;
+ bool processed;
+};
+
+MRN_API my_bool mroonga_escape_init(UDF_INIT *initid, UDF_ARGS *args,
+ char *message)
+{
+ EscapeInfo *info = NULL;
+
+ initid->ptr = NULL;
+ if (!(1 <= args->arg_count && args->arg_count <= 2)) {
+ sprintf(message,
+ "mroonga_escape(): Incorrect number of arguments: %u for 1..2",
+ args->arg_count);
+ goto error;
+ }
+ if (args->arg_type[0] != STRING_RESULT) {
+ strcpy(message,
+ "mroonga_escape(): The 1st argument must be query as string");
+ goto error;
+ }
+ if (args->arg_count == 2) {
+ if (args->arg_type[1] != STRING_RESULT) {
+ strcpy(message,
+ "mroonga_escape(): "
+ "The 2st argument must be escape target characters as string");
+ goto error;
+ }
+ }
+
+ initid->maybe_null = 1;
+ initid->const_item = 1;
+
+ info = (EscapeInfo *)my_malloc(sizeof(EscapeInfo),
+ MYF(MY_WME | MY_ZEROFILL));
+ if (!info) {
+ strcpy(message, "mroonga_escape(): out of memory");
+ goto error;
+ }
+
+ grn_ctx_init(&(info->ctx), 0);
+ GRN_TEXT_INIT(&(info->target_characters), 0);
+ GRN_TEXT_INIT(&(info->escaped_query), 0);
+ info->processed = false;
+
+ initid->ptr = (char *)info;
+
+ return FALSE;
+
+error:
+ if (info) {
+ grn_ctx_fin(&(info->ctx));
+ my_free(info, MYF(0));
+ }
+ return TRUE;
+}
+
+static void escape(EscapeInfo *info, UDF_ARGS *args)
+{
+ grn_ctx *ctx = &(info->ctx);
+ char *query = args->args[0];
+ unsigned int query_length = args->lengths[0];
+
+ if (args->arg_count == 2) {
+ char *target_characters = args->args[1];
+ unsigned int target_characters_length = args->lengths[1];
+ GRN_TEXT_PUT(ctx, &(info->target_characters),
+ target_characters,
+ target_characters_length);
+ GRN_TEXT_PUTC(ctx, &(info->target_characters), '\0');
+ grn_expr_syntax_escape(ctx, query, query_length,
+ GRN_TEXT_VALUE(&(info->target_characters)),
+ GRN_QUERY_ESCAPE,
+ &(info->escaped_query));
+ } else {
+ grn_expr_syntax_escape_query(ctx, query, query_length,
+ &(info->escaped_query));
+ }
+}
+
+MRN_API char *mroonga_escape(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *length, char *is_null, char *error)
+{
+ EscapeInfo *info = (EscapeInfo *)initid->ptr;
+ grn_ctx *ctx = &(info->ctx);
+
+ if (!args->args[0]) {
+ *is_null = 1;
+ return NULL;
+ }
+
+ *is_null = 0;
+
+ if (!info->processed) {
+ escape(info, args);
+ info->processed = true;
+ }
+
+ if (ctx->rc) {
+ my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0));
+ goto error;
+ }
+
+ *length = GRN_TEXT_LEN(&(info->escaped_query));
+ return (char *)(GRN_TEXT_VALUE(&(info->escaped_query)));
+
+error:
+ *error = 1;
+ return NULL;
+}
+
+MRN_API void mroonga_escape_deinit(UDF_INIT *initid)
+{
+ EscapeInfo *info = (EscapeInfo *)initid->ptr;
+ if (info) {
+ grn_obj_unlink(&(info->ctx), &(info->target_characters));
+ grn_obj_unlink(&(info->ctx), &(info->escaped_query));
+ grn_ctx_fin(&(info->ctx));
+ my_free(info, MYF(0));
+ }
+}
+
+MRN_END_DECLS
diff --git a/storage/mroonga/udf/mrn_udf_last_insert_grn_id.cpp b/storage/mroonga/udf/mrn_udf_last_insert_grn_id.cpp
new file mode 100644
index 00000000000..b54f5c53206
--- /dev/null
+++ b/storage/mroonga/udf/mrn_udf_last_insert_grn_id.cpp
@@ -0,0 +1,54 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ Copyright(C) 2010 Tetsuro IKEDA
+ Copyright(C) 2010-2013 Kentoku SHIBA
+ Copyright(C) 2011-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <mrn_mysql.h>
+#include <mrn_windows.hpp>
+#include <mrn_table.hpp>
+#include <mrn_macro.hpp>
+
+MRN_BEGIN_DECLS
+
+MRN_API my_bool last_insert_grn_id_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ if (args->arg_count != 0) {
+ strcpy(message, "last_insert_grn_id must not have arguments");
+ return 1;
+ }
+ initid->maybe_null = 0;
+ return 0;
+}
+
+MRN_API longlong last_insert_grn_id(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
+{
+ THD *thd = current_thd;
+ st_mrn_slot_data *slot_data = mrn_get_slot_data(thd, false);
+ if (slot_data == NULL) {
+ return 0;
+ }
+ longlong last_insert_record_id = slot_data->last_insert_record_id;
+ return last_insert_record_id;
+}
+
+MRN_API void last_insert_grn_id_deinit(UDF_INIT *initid)
+{
+}
+
+MRN_END_DECLS
diff --git a/storage/mroonga/udf/mrn_udf_snippet.cpp b/storage/mroonga/udf/mrn_udf_snippet.cpp
new file mode 100644
index 00000000000..a959377d9ce
--- /dev/null
+++ b/storage/mroonga/udf/mrn_udf_snippet.cpp
@@ -0,0 +1,302 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ Copyright(C) 2010 Tetsuro IKEDA
+ Copyright(C) 2010-2013 Kentoku SHIBA
+ Copyright(C) 2011-2014 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <mrn_mysql.h>
+#include <mrn_mysql_compat.h>
+#include <mrn_err.h>
+#include <mrn_encoding.hpp>
+#include <mrn_windows.hpp>
+#include <mrn_table.hpp>
+#include <mrn_macro.hpp>
+
+MRN_BEGIN_DECLS
+
+struct st_mrn_snip_info
+{
+ grn_ctx ctx;
+ grn_obj *snippet;
+ String result_str;
+};
+
+static my_bool mrn_snippet_prepare(st_mrn_snip_info *snip_info, UDF_ARGS *args,
+ char *message, grn_obj **snippet)
+{
+ unsigned int i;
+ CHARSET_INFO *cs;
+ grn_ctx *ctx = &snip_info->ctx;
+ long long snip_max_len;
+ long long snip_max_num;
+ long long skip_leading_spaces;
+ long long html_escape;
+ int flags = GRN_SNIP_COPY_TAG;
+ grn_snip_mapping *mapping = NULL;
+ grn_rc rc;
+ String *result_str = &snip_info->result_str;
+
+ *snippet = NULL;
+ snip_max_len = *((long long *) args->args[1]);
+ snip_max_num = *((long long *) args->args[2]);
+
+ if (args->arg_type[3] == STRING_RESULT) {
+ if (!(cs = get_charset_by_name(args->args[3], MYF(0)))) {
+ snprintf(message, MYSQL_ERRMSG_SIZE,
+ "Unknown charset: <%s>", args->args[3]);
+ goto error;
+ }
+ } else {
+ uint charset_id = static_cast<uint>(*((long long *) args->args[3]));
+ if (!(cs = get_charset(charset_id, MYF(0)))) {
+ snprintf(message, MYSQL_ERRMSG_SIZE,
+ "Unknown charset ID: <%u>", charset_id);
+ goto error;
+ }
+ }
+ if (!mrn::encoding::set(ctx, cs)) {
+ snprintf(message, MYSQL_ERRMSG_SIZE,
+ "Unsupported charset: <%s>", cs->name);
+ goto error;
+ }
+
+ if (!(cs->state & (MY_CS_BINSORT | MY_CS_CSSORT))) {
+ flags |= GRN_SNIP_NORMALIZE;
+ }
+
+ skip_leading_spaces = *((long long *) args->args[4]);
+ if (skip_leading_spaces) {
+ flags |= GRN_SNIP_SKIP_LEADING_SPACES;
+ }
+
+ html_escape = *((long long *) args->args[5]);
+ if (html_escape) {
+ mapping = (grn_snip_mapping *) -1;
+ }
+
+ *snippet = grn_snip_open(ctx, flags, static_cast<unsigned int>(snip_max_len),
+ static_cast<unsigned int>(snip_max_num),
+ "", 0, "", 0, mapping);
+ if (ctx->rc) {
+ snprintf(message, MYSQL_ERRMSG_SIZE,
+ "Failed to open grn_snip: <%s>", ctx->errbuf);
+ goto error;
+ }
+
+ for (i = 8; i < args->arg_count; i += 3) {
+ rc = grn_snip_add_cond(ctx, *snippet,
+ args->args[i], args->lengths[i],
+ args->args[i + 1], args->lengths[i + 1],
+ args->args[i + 2], args->lengths[i + 2]);
+ if (rc) {
+ snprintf(message, MYSQL_ERRMSG_SIZE,
+ "Failed to add a condition to grn_snip: <%s>", ctx->errbuf);
+ goto error;
+ }
+ }
+
+ result_str->set_charset(cs);
+ return FALSE;
+
+error:
+ if (*snippet) {
+ grn_obj_close(ctx, *snippet);
+ }
+ return TRUE;
+}
+
+MRN_API my_bool mroonga_snippet_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ uint i;
+ st_mrn_snip_info *snip_info = NULL;
+ bool can_open_snippet = TRUE;
+ initid->ptr = NULL;
+ if (args->arg_count < 11 || (args->arg_count - 11) % 3)
+ {
+ sprintf(message, "Incorrect number of arguments for mroonga_snippet(): %u",
+ args->arg_count);
+ goto error;
+ }
+ if (args->arg_type[0] != STRING_RESULT) {
+ strcpy(message, "mroonga_snippet() requires string for 1st argument");
+ goto error;
+ }
+ if (args->arg_type[1] != INT_RESULT) {
+ strcpy(message, "mroonga_snippet() requires int for 2nd argument");
+ goto error;
+ }
+ if (args->arg_type[2] != INT_RESULT) {
+ strcpy(message, "mroonga_snippet() requires int for 3rd argument");
+ goto error;
+ }
+ if (
+ args->arg_type[3] != STRING_RESULT &&
+ args->arg_type[3] != INT_RESULT
+ ) {
+ strcpy(message,
+ "mroonga_snippet() requires string or int for 4th argument");
+ goto error;
+ }
+ if (args->arg_type[4] != INT_RESULT) {
+ strcpy(message, "mroonga_snippet() requires int for 5th argument");
+ goto error;
+ }
+ if (args->arg_type[5] != INT_RESULT) {
+ strcpy(message, "mroonga_snippet() requires int for 6th argument");
+ goto error;
+ }
+ for (i = 6; i < args->arg_count; i++) {
+ if (args->arg_type[i] != STRING_RESULT) {
+ sprintf(message, "mroonga_snippet() requires string for %uth argument",
+ i);
+ goto error;
+ }
+ }
+ initid->maybe_null = 1;
+ initid->const_item = 1;
+
+ if (!(snip_info = (st_mrn_snip_info *) my_malloc(sizeof(st_mrn_snip_info),
+ MYF(MY_WME | MY_ZEROFILL))))
+ {
+ strcpy(message, "mroonga_snippet() out of memory");
+ goto error;
+ }
+ grn_ctx_init(&snip_info->ctx, 0);
+ grn_db_create(&snip_info->ctx, NULL, 0);
+
+ for (i = 1; i < args->arg_count; i++) {
+ if (!args->args[i]) {
+ can_open_snippet = FALSE;
+ break;
+ }
+ }
+ if (can_open_snippet) {
+ if (mrn_snippet_prepare(snip_info, args, message, &snip_info->snippet)) {
+ goto error;
+ }
+ }
+ initid->ptr = (char *) snip_info;
+
+ return FALSE;
+
+error:
+ if (snip_info) {
+ grn_obj_close(&snip_info->ctx, grn_ctx_db(&snip_info->ctx));
+ grn_ctx_fin(&snip_info->ctx);
+ my_free(snip_info, MYF(0));
+ }
+ return TRUE;
+}
+
+MRN_API char *mroonga_snippet(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *length, char *is_null, char *error)
+{
+ st_mrn_snip_info *snip_info = (st_mrn_snip_info *) initid->ptr;
+ grn_ctx *ctx = &snip_info->ctx;
+ String *result_str = &snip_info->result_str;
+ char *target;
+ unsigned int target_length;
+ grn_obj *snippet = NULL;
+ grn_rc rc;
+ unsigned int i, n_results, max_tagged_length, result_length;
+
+ if (!args->args[0]) {
+ *is_null = 1;
+ return NULL;
+ }
+ *is_null = 0;
+ target = args->args[0];
+ target_length = args->lengths[0];
+
+ if (!snip_info->snippet) {
+ for (i = 1; i < args->arg_count; i++) {
+ if (!args->args[i]) {
+ my_printf_error(ER_MRN_INVALID_NULL_VALUE_NUM,
+ ER_MRN_INVALID_NULL_VALUE_STR, MYF(0),
+ "mroonga_snippet() arguments");
+ goto error;
+ }
+ }
+
+ if (mrn_snippet_prepare(snip_info, args, NULL, &snippet)) {
+ goto error;
+ }
+ } else {
+ snippet = snip_info->snippet;
+ }
+
+ rc = grn_snip_exec(ctx, snippet, target, target_length,
+ &n_results, &max_tagged_length);
+ if (rc) {
+ my_printf_error(ER_MRN_ERROR_FROM_GROONGA_NUM,
+ ER_MRN_ERROR_FROM_GROONGA_STR, MYF(0), ctx->errbuf);
+ goto error;
+ }
+
+ result_str->length(0);
+ if (result_str->reserve((args->lengths[6] + args->lengths[7] +
+ max_tagged_length) * n_results)) {
+ my_error(ER_OUT_OF_RESOURCES, MYF(0), HA_ERR_OUT_OF_MEM);
+ goto error;
+ }
+ for (i = 0; i < n_results; i++) {
+ result_str->q_append(args->args[6], args->lengths[6]);
+ rc = grn_snip_get_result(ctx, snippet, i,
+ (char *) result_str->ptr() + result_str->length(),
+ &result_length);
+ if (rc) {
+ my_printf_error(ER_MRN_ERROR_FROM_GROONGA_NUM,
+ ER_MRN_ERROR_FROM_GROONGA_STR, MYF(0), ctx->errbuf);
+ goto error;
+ }
+ result_str->length(result_str->length() + result_length);
+ result_str->q_append(args->args[7], args->lengths[7]);
+ }
+
+ if (!snip_info->snippet) {
+ rc = grn_obj_close(ctx, snippet);
+ if (rc) {
+ my_printf_error(ER_MRN_ERROR_FROM_GROONGA_NUM,
+ ER_MRN_ERROR_FROM_GROONGA_STR, MYF(0), ctx->errbuf);
+ goto error;
+ }
+ }
+
+ *length = result_str->length();
+ return (char *) result_str->ptr();
+
+error:
+ *error = 1;
+ return NULL;
+}
+
+MRN_API void mroonga_snippet_deinit(UDF_INIT *initid)
+{
+ st_mrn_snip_info *snip_info = (st_mrn_snip_info *) initid->ptr;
+ if (snip_info) {
+ if (snip_info->snippet) {
+ grn_obj_close(&snip_info->ctx, snip_info->snippet);
+ }
+ snip_info->result_str.free();
+ grn_obj_close(&snip_info->ctx, grn_ctx_db(&snip_info->ctx));
+ grn_ctx_fin(&snip_info->ctx);
+ my_free(snip_info, MYF(0));
+ }
+}
+
+MRN_END_DECLS
diff --git a/storage/mroonga/udf/sources.am b/storage/mroonga/udf/sources.am
new file mode 100644
index 00000000000..380ab4b60cf
--- /dev/null
+++ b/storage/mroonga/udf/sources.am
@@ -0,0 +1,5 @@
+libmrn_udf_la_SOURCES = \
+ mrn_udf_last_insert_grn_id.cpp \
+ mrn_udf_snippet.cpp \
+ mrn_udf_command.cpp \
+ mrn_udf_escape.cpp
diff --git a/storage/mroonga/vendor/groonga/CMakeLists.txt b/storage/mroonga/vendor/groonga/CMakeLists.txt
new file mode 100644
index 00000000000..649bc9a103f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/CMakeLists.txt
@@ -0,0 +1,505 @@
+# Copyright(C) 2012-2013 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+# https://buildbot.askmonty.org/buildbot/builders/work-amd64-valgrind/builds/5263/steps/compile/logs/stdio
+# says CMake 2.6.2... We want to drop old software support...
+cmake_minimum_required(VERSION 2.6.2)
+# cmake_minimum_required(VERSION 2.6.4) # CentOS 5
+set(GRN_PROJECT_NAME "groonga")
+project("${GRN_PROJECT_NAME}")
+
+file(READ "${CMAKE_CURRENT_SOURCE_DIR}/base_version" VERSION)
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/version.sh")
+ file(READ "${CMAKE_CURRENT_SOURCE_DIR}/version.sh" GRN_VERSION)
+else()
+ if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/version.sh")
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
+ execute_process(COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/version-gen.sh")
+ file(READ "${CMAKE_CURRENT_BINARY_DIR}/version.sh" GRN_VERSION)
+ else()
+ set(GRN_VERSION "${VERSION}")
+ endif()
+ endif()
+endif()
+string(REGEX REPLACE "(^.*=|\n)" "" GRN_VERSION "${GRN_VERSION}")
+
+include(CheckIncludeFile)
+include(CheckFunctionExists)
+include(CheckLibraryExists)
+include(CheckSymbolExists)
+include(CheckCCompilerFlag)
+include(CheckCXXCompilerFlag)
+include(FindPkgConfig)
+include(${CMAKE_CURRENT_SOURCE_DIR}/build/cmake_modules/ReadFileList.cmake)
+
+set(BIN_DIR "bin")
+set(SBIN_DIR "sbin")
+set(LIB_DIR "lib")
+set(INCLUDE_DIR "include")
+set(GRN_INCLUDE_DIR "include/groonga")
+set(DATA_DIR "share")
+set(GRN_DATA_DIR "${DATA_DIR}/${GRN_PROJECT_NAME}")
+set(CONFIG_DIR "etc")
+set(GRN_CONFIG_DIR "${CONFIG_DIR}/${GRN_PROJECT_NAME}")
+
+set(GRN_LOG_PATH
+ "${CMAKE_INSTALL_PREFIX}/var/log/${GRN_PROJECT_NAME}/${GRN_PROJECT_NAME}.log"
+ CACHE FILEPATH "log file path")
+set(GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD
+ 0
+ CACHE STRING "groonga default match escalation threshold")
+set(GRN_DEFAULT_DOCUMENT_ROOT_BASE
+ "html/admin"
+ CACHE PATH "groonga default document root base path")
+set(GRN_DEFAULT_RELATIVE_DOCUMENT_ROOT
+ "share/${GRN_PROJECT_NAME}/${GRN_DEFAULT_DOCUMENT_ROOT_BASE}"
+ CACHE PATH "groonga default relative document root")
+set(GRN_DEFAULT_DOCUMENT_ROOT
+ "${CMAKE_INSTALL_PREFIX}/${GRN_DATA_DIR}/${GRN_DEFAULT_DOCUMENT_ROOT_BASE}"
+ CACHE PATH "groonga default document root")
+set(GRN_STACK_SIZE
+ 1024
+ CACHE STRING
+ "DANGER!!! groonga stack size. Normarlly, you should not change this variable.")
+set(GRN_LOCK_TIMEOUT
+ 10000000
+ CACHE STRING
+ "timeout to acquire a lock.")
+set(GRN_LOCK_WAIT_TIME_NANOSECOND
+ 1000000
+ CACHE STRING
+ "wait time in nanosecond to acquire a lock.")
+set(GRN_RELATIVE_PLUGINS_DIR
+ "${LIB_DIR}/${GRN_PROJECT_NAME}/plugins")
+set(GRN_PLUGINS_DIR
+ "${CMAKE_INSTALL_PREFIX}/${GRN_RELATIVE_PLUGINS_DIR}")
+set(GRN_PLUGIN_SUFFIX "${CMAKE_SHARED_MODULE_SUFFIX}")
+set(GRN_DLL_FILENAME
+ "${CMAKE_SHARED_LIBRARY_PREFIX}groonga${CMAKE_SHARED_LIBRARY_SUFFIX}")
+set(GRN_QUERY_EXPANDER_TSV_RELATIVE_SYNONYMS_FILE "synonyms.tsv")
+set(GRN_QUERY_EXPANDER_TSV_SYNONYMS_FILE
+ "${CMAKE_INSTALL_PREFIX}/${GRN_DATA_DIR}/${GRN_QUERY_EXPANDER_TSV_RELATIVE_SYNONYMS_FILE}")
+set(GRN_RELATIVE_RUBY_SCRIPTS_DIR
+ "${LIB_DIR}/${GRN_PROJECT_NAME}/scripts/ruby")
+set(GRN_RUBY_SCRIPTS_DIR
+ "${CMAKE_INSTALL_PREFIX}/${GRN_RELATIVE_RUBY_SCRIPTS_DIR}")
+
+macro(check_cflag flag)
+ set(checking_message "checking for C flag '${flag}'")
+ string(REGEX REPLACE "[-=]" "_" temporary_variable_name ${flag})
+ string(TOUPPER "${temporary_variable_name}" temporary_variable_name)
+ set(temporary_variable_name "CFLAG${temporary_variable_name}")
+# unset(${temporary_variable_name})
+ check_c_compiler_flag(${flag} ${temporary_variable_name})
+ if(${temporary_variable_name})
+ message(STATUS "${checking_message} - available")
+ set(GRN_C_COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS} ${flag}")
+ else()
+ message(STATUS "${checking_message} - not available")
+ endif()
+endmacro()
+
+macro(check_cxxflag flag)
+ set(checking_message "checking for CXX flag '${flag}'")
+ string(REGEX REPLACE "[-=]" "_" temporary_variable_name ${flag})
+ string(TOUPPER "${temporary_variable_name}" temporary_variable_name)
+ set(temporary_variable_name "CXXFLAG${temporary_variable_name}")
+# unset(${temporary_variable_name})
+ check_cxx_compiler_flag(${flag} ${temporary_variable_name})
+ if(${temporary_variable_name})
+ message(STATUS "${checking_message} - available")
+ set(GRN_CXX_COMPILE_FLAGS "${GRN_CXX_COMPILE_FLAGS} ${flag}")
+ else()
+ message(STATUS "${checking_message} - not available")
+ endif()
+endmacro()
+
+macro(check_build_flag flag)
+ check_cflag(${flag})
+ check_cxxflag(${flag})
+endmacro()
+
+if(CMAKE_COMPILER_IS_GNUCXX)
+ check_build_flag("-Wall")
+ check_build_flag("-Wextra")
+ check_build_flag("-Wno-unused-but-set-variable")
+ check_build_flag("-Wno-unused-parameter")
+ check_build_flag("-Wno-sign-compare")
+ check_cflag("-Wno-pointer-sign")
+ check_build_flag("-Wno-missing-field-initializers")
+ check_build_flag("-Wformat=2")
+ check_build_flag("-Wstrict-aliasing=2")
+ check_build_flag("-fno-strict-aliasing")
+ check_build_flag("-Wdisabled-optimization")
+ check_build_flag("-Wfloat-equal")
+ check_build_flag("-Wpointer-arith")
+ check_cflag("-Wdeclaration-after-statement")
+ check_cflag("-Wbad-function-cast")
+ check_build_flag("-Wcast-align")
+ check_build_flag("-Wredundant-decls")
+ check_build_flag("-Wwrite-strings")
+ check_cxxflag("-fexceptions")
+ check_cxxflag("-fimplicit-templates")
+ check_build_flag("-Wno-clobbered")
+ if(MRN_GROONGA_BUNDLED)
+ check_build_flag("-fPIC")
+ endif()
+endif()
+
+option(GRN_WITH_DEBUG "enable debug build." OFF)
+if(GRN_WITH_DEBUG)
+ if(CMAKE_COMPILER_IS_GNUCXX)
+ set(GRN_C_COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS} -g3 -O0")
+ set(GRN_CXX_COMPILE_FLAGS "${GRN_CXX_COMPILE_FLAGS} -g3 -O0")
+ endif()
+endif()
+
+add_definitions(
+ -DHAVE_CONFIG_H
+ )
+
+if(CMAKE_COMPILER_IS_GNUC OR
+ CMAKE_COMPILER_IS_GNUCXX OR
+ CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ set(_GNU_SOURCE TRUE)
+endif()
+
+include_directories(
+ BEFORE
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/include
+ )
+
+macro(ac_check_headers header)
+ string(REGEX REPLACE "[/.]" "_" output_variable_name ${header})
+ string(TOUPPER "${output_variable_name}" output_variable_name)
+ set(output_variable_name "HAVE_${output_variable_name}")
+# unset(${output_variable_name})
+ check_include_file(${header} ${output_variable_name})
+endmacro()
+
+macro(ac_check_funcs function)
+ string(TOUPPER "${function}" output_variable_name)
+ set(output_variable_name "HAVE_${output_variable_name}")
+# unset(${output_variable_name})
+ check_function_exists(${function} ${output_variable_name})
+endmacro()
+
+macro(ac_check_symbols symbol files)
+ string(TOUPPER "${symbol}" output_variable_name)
+ set(output_variable_name "HAVE_${output_variable_name}")
+# unset(${output_variable_name})
+ check_symbol_exists(${symbol} ${files} ${output_variable_name})
+endmacro()
+
+macro(ac_check_lib library function)
+ string(REGEX REPLACE "[/.]" "_" output_variable_base_name ${library})
+ string(TOUPPER "${output_variable_base_name}" output_variable_base_name)
+ set(output_variable_name "HAVE_LIB${output_variable_base_name}")
+ set(location "${ARG2}")
+# unset(${output_variable_name})
+ check_library_exists(${library} ${function} "${location}"
+ ${output_variable_name})
+ if(${output_variable_name})
+ set(${output_variable_base_name}_LIBS "${library}")
+ endif()
+endmacro()
+
+include(build/ac_macros/check_headers.m4)
+include(build/ac_macros/check_functions.m4)
+
+ac_check_symbols(fpclassify math.h)
+
+ac_check_lib(dl dlopen)
+ac_check_lib(execinfo backtrace)
+if(HAVE_LIBEXECINFO)
+ set(HAVE_BACKTRACE TRUE)
+else()
+ ac_check_funcs(backtrace)
+endif()
+ac_check_lib(rt clock_gettime)
+if(HAVE_LIBRT)
+ set(HAVE_CLOCK_GETTIME TRUE)
+endif()
+if(MRN_GROONGA_BUNDLED)
+ ac_check_lib(m sincos)
+ check_library_exists(stdc++ __cxa_begin_catch "${ARG2}"
+ STDCPP)
+ if(STDCPP)
+ set(STDCPP_LIBS "stdc++")
+ endif()
+endif()
+
+if(UNIX)
+ ac_check_headers(pthread.h)
+ ac_check_lib(pthread pthread_mutex_init)
+ if(NOT ${HAVE_LIBPTHREAD})
+ message(FATAL_ERROR "No libpthread found")
+ endif()
+ ac_check_funcs(pthread_mutexattr_setpshared)
+ ac_check_funcs(pthread_condattr_setpshared)
+endif()
+
+option(GRN_WITH_NFKC "use NFKC based UTF8 normalization." ON)
+
+if(WIN32)
+ ac_check_headers(winsock2.h)
+ if(NOT ${HAVE_WINSOCK2_H})
+ message(FATAL_ERROR "No winsock2.h found")
+ endif()
+
+ # FIXME: CMake couldn't detect ws2_32.lib on Windows 8 64bit.
+ # It may be caused by missing library search path for ws2_32.lib.
+ # It seems that Visual Studio (devenv.exe) can find it but link.exe
+ # can't. "cmake --build" can find it because it uses Visual Studio
+ # internally. So we assume that we always have ws2_32.lib on Windows.
+ # ac_check_lib(ws2_32 select)
+ set(HAVE_LIBWS2_32 TRUE)
+ set(WS2_32_LIBS "ws2_32.lib")
+
+ set(USE_SELECT TRUE)
+else()
+ ac_check_headers(sys/epoll.h)
+ if(${HAVE_SYS_EPOLL_H})
+ ac_check_funcs(epoll_create)
+ if(${HAVE_EPOLL_CREATE})
+ set(USE_EPOLL TRUE)
+ endif()
+ endif()
+
+ if(NOT USE_EPOLL)
+ ac_check_headers(sys/event.h)
+ if(${HAVE_SYS_EVENT_H})
+ ac_check_funcs(kevent)
+ if(${HAVE_KEVENT})
+ set(USE_KQUEUE TRUE)
+ endif()
+ endif()
+
+ if(NOT USE_KQUEUE)
+ ac_check_headers(sys/poll.h)
+ if(${HAVE_SYS_POLL_H})
+ ac_check_funcs(poll)
+ if(${HAVE_POLL})
+ set(USE_POLL TRUE)
+ endif()
+ endif()
+
+ if(NOT USE_POLL)
+ ac_check_funcs(select)
+ if(${HAVE_SELECT})
+ set(USE_SELECT TRUE)
+ ac_check_headers(sys/select.h)
+ endif()
+
+ if(NOT USE_SELECT)
+ message(FATAL_ERROR "All epoll/kqueue/poll/select are missing")
+ endif()
+ endif()
+ endif()
+ endif()
+endif()
+
+option(GRN_WITH_ZLIB "use zlib for data compression." OFF)
+if(GRN_WITH_ZLIB)
+ ac_check_lib(z compress)
+ if(NOT HAVE_LIBZ)
+ message(FATAL_ERROR "No libz found")
+ endif()
+endif()
+
+option(GRN_WITH_LZO "use LZO for data compression." OFF)
+if(GRN_WITH_LZO)
+ ac_check_lib(lzo2 lzo1_compress)
+ if(NOT HAVE_LIBLZO2)
+ message(FATAL_ERROR "No liblzo2 found")
+ endif()
+endif()
+
+set(GRN_WITH_MECAB "auto"
+ CACHE STRING "use MeCab for morphological analysis")
+if(NOT ${GRN_WITH_MECAB} STREQUAL "no")
+ set(MECAB_CONFIG "mecab-config" CACHE FILEPATH "mecab-config path")
+ if(NOT CMAKE_CROSSCOMPILING)
+ find_program(MECAB_CONFIG_ABSOLUTE_PATH "${MECAB_CONFIG}")
+ endif()
+ if(EXISTS "${MECAB_CONFIG_ABSOLUTE_PATH}")
+ execute_process(COMMAND "${MECAB_CONFIG_ABSOLUTE_PATH}" --inc-dir
+ OUTPUT_VARIABLE MECAB_INCLUDE_DIRS
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ execute_process(COMMAND "${MECAB_CONFIG_ABSOLUTE_PATH}" --libs-only-L
+ OUTPUT_VARIABLE MECAB_LIBRARY_DIRS
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ set(MECAB_LIBRARIES "mecab")
+ ac_check_lib(${MECAB_LIBRARIES} mecab_new)
+ if(HAVE_LIBMECAB)
+ set(GRN_WITH_MECAB TRUE)
+ else()
+ if(${GRN_WITH_MECAB} STREQUAL "yes")
+ message(FATAL_ERROR
+ "No MeCab library found: "
+ "include directories: <${MECAB_INCLUDE_DIRS}>, "
+ "library directories: <${MECAB_LIBRARY_DIRS}>")
+ endif()
+ set(GRN_WITH_MECAB FALSE)
+ endif()
+ else()
+ if(${GRN_WITH_MECAB} STREQUAL "yes")
+ message(FATAL_ERROR "No mecab-config found: <${MECAB_CONFIG}>")
+ endif()
+ set(GRN_WITH_MECAB FALSE)
+ endif()
+else()
+ set(GRN_WITH_MECAB FALSE)
+endif()
+
+set(GRN_WITH_KYTEA "auto"
+ CACHE STRING "use KyTea for morphological analysis")
+if(NOT ${GRN_WITH_KYTEA} STREQUAL "no")
+ pkg_check_modules(KYTEA kytea)
+ if(KYTEA_FOUND)
+ set(GRN_WITH_KYTEA TRUE)
+ else()
+ if(${GRN_WITH_KYTEA} STREQUAL "yes")
+ message(FATAL_ERROR "No KyTea found")
+ endif()
+ set(GRN_WITH_KYTEA FALSE)
+ endif()
+else()
+ set(GRN_WITH_KYTEA FALSE)
+endif()
+
+set(GRN_WITH_ZEROMQ "auto"
+ CACHE STRING "use ZeroMQ for suggestion")
+if(NOT ${GRN_WITH_ZEROMQ} STREQUAL "no")
+ pkg_check_modules(ZEROMQ libzmq)
+ if(ZEROMQ_FOUND)
+ set(GRN_WITH_ZEROMQ TRUE)
+ else()
+ if(${GRN_WITH_ZEROMQ} STREQUAL "yes")
+ message(FATAL_ERROR "No ZeroMQ found")
+ endif()
+ set(GRN_WITH_ZEROMQ FALSE)
+ endif()
+else()
+ set(GRN_WITH_ZEROMQ FALSE)
+endif()
+
+set(GRN_WITH_LIBEVENT "auto"
+ CACHE STRING "use libevent for suggestion")
+if(NOT ${GRN_WITH_LIBEVENT} STREQUAL "no")
+ if("${GRN_WITH_LIBEVENT}" STREQUAL "yes" OR
+ "${GRN_WITH_LIBEVENT}" STREQUAL "auto")
+ set(LIBEVENT_INCLUDE_DIRS "")
+ set(LIBEVENT_LIBRARY_DIRS "")
+ else()
+ set(LIBEVENT_INCLUDE_DIRS "${GRN_WITH_LIBEVENT}/include")
+ set(LIBEVENT_LIBRARY_DIRS "${GRN_WITH_LIBEVENT}/lib")
+ endif()
+ set(CMAKE_REQUIRED_INCLUDES_SAVE ${CMAKE_REQUIRED_INCLUDES})
+ ac_check_lib(event event_init "${LIBEVENT_LIBRARY_DIRS}")
+ set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES_SAVE})
+ if(HAVE_LIBEVENT)
+ set(LIBEVENT_LIBRARIES "event")
+ set(GRN_WITH_LIBEVENT TRUE)
+ else()
+ if(${GRN_WITH_LIBEVENT} STREQUAL "yes")
+ message(FATAL_ERROR "No libevent found")
+ endif()
+ set(GRN_WITH_LIBEVENT FALSE)
+ endif()
+else()
+ set(GRN_WITH_LIBEVENT FALSE)
+endif()
+
+set(GRN_WITH_MESSAGE_PACK "auto"
+ CACHE STRING "use MessagePack for suggestion")
+if(NOT ${GRN_WITH_MESSAGE_PACK} STREQUAL "no")
+ if("${GRN_WITH_MESSAGE_PACK}" STREQUAL "yes" OR
+ "${GRN_WITH_MESSAGE_PACK}" STREQUAL "auto")
+ set(MESSAGE_PACK_INCLUDE_DIRS "")
+ set(MESSAGE_PACK_LIBRARY_DIRS "")
+ else()
+ set(MESSAGE_PACK_INCLUDE_DIRS "${GRN_WITH_MESSAGE_PACK}/include")
+ set(MESSAGE_PACK_LIBRARY_DIRS "${GRN_WITH_MESSAGE_PACK}/lib")
+ endif()
+ set(CMAKE_REQUIRED_INCLUDES_SAVE ${CMAKE_REQUIRED_INCLUDES})
+ ac_check_lib(msgpack msgpack_version "${MESSAGE_PACK_LIBRARY_DIRS}")
+ set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES_SAVE})
+ if(HAVE_LIBMSGPACK)
+ set(MESSAGE_PACK_LIBRARIES "msgpack")
+ set(GRN_WITH_MESSAGE_PACK TRUE)
+ else()
+ if(${GRN_WITH_MESSAGE_PACK} STREQUAL "yes")
+ message(FATAL_ERROR "No MessagePack found")
+ endif()
+ set(GRN_WITH_MESSAGE_PACK FALSE)
+ endif()
+else()
+ set(GRN_WITH_MESSAGE_PACK FALSE)
+endif()
+
+option(GRN_WITH_MRUBY "use mruby" OFF)
+if(GRN_WITH_MRUBY)
+ if(CMAKE_VERSION VERSION_LESS "2.8.8")
+ message(FATAL_ERROR
+ "Your CMake (${CMAKE_VERSION}) is old. "
+ "CMake 2.8.8 or later is required for mruby supported build")
+ endif()
+ set(MRUBY_INCLUDE_DIRS
+ "${CMAKE_CURRENT_SOURCE_DIR}/vendor/mruby-source/include")
+ set(MRUBY_LIBS "$<TARGET_OBJECTS:mruby>")
+else()
+ set(MRUBY_INCLUDE_DIRS "")
+ set(MRUBY_LIBS "")
+endif()
+
+add_subdirectory(vendor)
+add_subdirectory(lib)
+add_subdirectory(src)
+add_subdirectory(plugins)
+add_subdirectory(include)
+add_subdirectory(data)
+
+configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
+
+set(RUBY "ruby") # TODO: support customization
+set(GROONGA "${CMAKE_CURRENT_BINARY_DIR}/src/groonga")
+set(GROONGA_SUGGEST_CREATE_DATASET
+ "${CMAKE_CURRENT_BINARY_DIR}/src/suggest/groonga-suggest-create-dataset")
+set(GROONGA_BENCHMARK "${CMAKE_CURRENT_BINARY_DIR}/src/groonga-benchmark")
+configure_file(config.sh.in "${CMAKE_CURRENT_BINARY_DIR}/config.sh" @ONLY)
+
+set(prefix "${CMAKE_INSTALL_PREFIX}")
+set(exec_prefix "${prefix}")
+set(bindir "${CMAKE_INSTALL_PREFIX}/${BIN_DIR}")
+set(sbindir "${CMAKE_INSTALL_PREFIX}/${SBIN_DIR}")
+set(libdir "${CMAKE_INSTALL_PREFIX}/${LIB_DIR}")
+set(includedir "${CMAKE_INSTALL_PREFIX}/${INCLUDE_DIR}")
+set(datarootdir "${CMAKE_INSTALL_PREFIX}/${DATA_DIR}")
+set(datadir "${datarootdir}")
+set(expanded_pluginsdir "${GRN_PLUGINS_DIR}")
+set(GRN_EXPANDED_DEFAULT_DOCUMENT_ROOT "${GRN_DEFAULT_DOCUMENT_ROOT}")
+set(EXEEXT "${CMAKE_EXECUTABLE_SUFFIX}")
+configure_file(groonga.pc.in "${CMAKE_CURRENT_BINARY_DIR}/groonga.pc" @ONLY)
+
+if(NOT MRN_GROONGA_BUNDLED)
+ install(
+ FILES "${CMAKE_CURRENT_BINARY_DIR}/groonga.pc"
+ DESTINATION "${LIB_DIR}/pkgconfig/")
+endif()
+
+add_subdirectory(vendor/plugins)
diff --git a/storage/mroonga/vendor/groonga/Makefile.am b/storage/mroonga/vendor/groonga/Makefile.am
new file mode 100644
index 00000000000..a5bf404d471
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/Makefile.am
@@ -0,0 +1,135 @@
+# release: update-latest-release (commit) tag
+
+LOCALES = ja
+
+ACLOCAL_AMFLAGS = ${ACLOCAL_ARGS} -I .
+AUTOMAKE_OPTIONS = 1.9.6
+SUBDIRS = \
+ build \
+ include \
+ vendor \
+ lib \
+ plugins \
+ src \
+ examples \
+ test \
+ benchmark \
+ packages \
+ data \
+ tools \
+ doc
+#dist_data_DATA =
+EXTRA_DIST = \
+ README.md \
+ bindings \
+ version-gen.sh \
+ base_version \
+ gpg_uid \
+ CMakeLists.txt \
+ config.h.cmake
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = groonga.pc
+
+.PHONY: FORCE
+
+FORCE:
+
+$(srcdir)/version.sh: FORCE
+ @$(SHELL_PATH) $(srcdir)/version-gen.sh
+
+include $(srcdir)/version.sh
+
+dist-hook:
+ echo "$(GRN_VERSION)" > $(distdir)/version
+
+benchmark:
+ cd test/benchmark && $(MAKE) benchmark
+
+tag:
+ cd $(top_srcdir) && git tag v$(VERSION) -a -m 'Groonga $(VERSION)!!!'
+
+echo-version:
+ @echo $(VERSION)
+
+update-latest-release: misc
+ @if test -z "$(OLD_RELEASE)"; then \
+ echo "\$$(OLD_RELEASE) is missing"; \
+ exit 1; \
+ fi
+ @if test -z "$(OLD_RELEASE_DATE)"; then \
+ echo "\$$(OLD_RELEASE_DATE) is missing"; \
+ exit 1; \
+ fi
+ @if test -z "$(NEW_RELEASE_DATE)"; then \
+ echo "\$$(NEW_RELEASE_DATE) is missing"; \
+ exit 1; \
+ fi
+ cd $(top_srcdir) && \
+ misc/update-latest-release.rb \
+ $(PACKAGE) $(OLD_RELEASE) $(OLD_RELEASE_DATE) \
+ $(VERSION) $(NEW_RELEASE_DATE) \
+ packages/rpm/fedora/groonga.spec.in \
+ packages/rpm/centos/groonga.spec.in \
+ packages/debian/changelog \
+ doc/source/install.rst \
+ doc/source/install/*.rst \
+ doc/locale/*/LC_MESSAGES/install.po \
+ $(GROONGA_ORG_PATH)/index.html \
+ $(GROONGA_ORG_PATH)/ja/index.html
+
+update-po:
+ @for lang in $(LOCALES); do \
+ (cd $(top_srcdir)/doc/locale/$$lang/LC_MESSAGES && $(MAKE) update) \
+ done
+
+update-document:
+ @if test -z "$(GROONGA_ORG_PATH)"; then \
+ echo "\$$(GROONGA_ORG_PATH) is missing"; \
+ echo "add --with-groonga-github-com-path in configure"; \
+ exit 1; \
+ fi
+ rm -rf tmp-doc
+ mkdir tmp-doc
+ (cd doc && $(MAKE) clean-html)
+ (cd doc && $(MAKE) install docdir=$(abs_srcdir)/tmp-doc/install)
+ ruby $(srcdir)/tools/prepare-sphinx-html.rb tmp-doc/install tmp-doc/dist
+ rm -rf $(GROONGA_ORG_PATH)/docs
+ mv tmp-doc/dist/en $(GROONGA_ORG_PATH)/docs
+ for locale in `cd tmp-doc/dist; echo *`; do \
+ dest_base_dir=$(GROONGA_ORG_PATH)/$${locale}; \
+ mkdir -p $${dest_base_dir}; \
+ dest_dir=$${dest_base_dir}/docs; \
+ rm -rf $${dest_dir}; \
+ mv tmp-doc/dist/$${locale} $${dest_dir}; \
+ done
+
+
+update-version:
+ @if test -z "$(NEW_VERSION)"; then \
+ echo "\$$(NEW_VERSION) is missing"; \
+ exit 1; \
+ fi
+ @echo -n $(NEW_VERSION) > $(srcdir)/base_version
+
+update-files:
+ cd doc && $(MAKE) update-files
+ cd data/html && $(MAKE) update-files
+
+update-mruby:
+ cd $(top_srcdir)/vendor/mruby && make update
+
+update-nginx:
+ @if test -z "$(NEW_NGINX_VERSION)"; then \
+ echo "\$$(NEW_NGINX_VERSION) is missing"; \
+ exit 1; \
+ fi
+ cd $(top_srcdir)/vendor && ./update_nginx.sh $(NEW_NGINX_VERSION)
+
+misc:
+ @if test -z "$(CUTTER_SOURCE_PATH)"; then \
+ echo "\$$(CUTTER_SOURCE_PATH) is missing"; \
+ exit 1; \
+ fi
+ ln -s "$(CUTTER_SOURCE_PATH)/misc" misc
+
diff --git a/storage/mroonga/vendor/groonga/README.md b/storage/mroonga/vendor/groonga/README.md
new file mode 100644
index 00000000000..09e5764a024
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/README.md
@@ -0,0 +1,49 @@
+# README
+
+Groonga is an open-source fulltext search engine and column store.
+
+## Reference manual
+
+See doc/source/ directory or http://groonga.org/docs/.
+
+## Bundled software
+
+### mruby
+
+ * Path: vendor/mruby-source
+ * License: The MIT license. See vendor/mruby-source/MITL for details.
+
+### Onigmo
+
+ * Path: vendor/onigmo-source
+ * License: BSD license. See vendor/onigmo-source/COPYING for details.
+
+### nginx
+
+ * Path: vendor/nginx-${VERSION}
+ * License: BSD license. See vendor/nginx-${VERSION}/LICENSE for details.
+
+## Authors
+
+### Primary authors
+
+ * Daijiro MORI <morita at razil. jp>
+ * Tasuku SUENAGA <a at razil. jp>
+ * Yutaro Shimamura <yu at razil. jp>
+ * Kouhei Sutou <kou at cozmixng. org>
+ * Kazuho Oku <kazuhooku at gmail. com>
+ * Moriyoshi Koizumi <moriyoshi at gmail. com>
+
+### Patches and modules from
+
+TODO: Update or use
+https://github.com/groonga/groonga/graphs/contributors instead.
+
+ * Daisuke Maki <dmaki at cpan. org>
+ * Kazuhiro Osawa <ko at yappo. ne. jp>
+ * Hiroyuki OYAMA <oyama at module. jp>
+ * Nguyen Anh Phu <phuna at users. sourceforge. net>
+ * Hideyuki KUROSU <hideyuki. kurosu at gmail. com>
+ * Takuo Kitame <kitame at valinux. co. jp>
+ * Yoshihiro Oyama <yos-o at smilemark. com>
+ * cZfSunOs.U <sunos at saita. ma>
diff --git a/storage/mroonga/vendor/groonga/autogen.sh b/storage/mroonga/vendor/groonga/autogen.sh
new file mode 100755
index 00000000000..9b3a98eab5d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/autogen.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+./version-gen.sh
+
+case `uname -s` in
+Darwin)
+ homebrew_aclocal=/usr/local/share/aclocal
+ if [ -d $homebrew_aclocal ]; then
+ ACLOCAL_ARGS="$ACLOCAL_ARGS -I $homebrew_aclocal"
+ fi
+ gettext_aclocal="$(echo /usr/local/Cellar/gettext/*/share/aclocal)"
+ if [ -d $gettext_aclocal ]; then
+ ACLOCAL_ARGS="$ACLOCAL_ARGS -I $gettext_aclocal"
+ fi
+ ;;
+FreeBSD)
+ ACLOCAL_ARGS="$ACLOCAL_ARGS -I /usr/local/share/aclocal/"
+ ;;
+esac
+
+${AUTORECONF:-autoreconf} --force --install
diff --git a/storage/mroonga/vendor/groonga/base_version b/storage/mroonga/vendor/groonga/base_version
new file mode 100644
index 00000000000..8b2dd6c36f4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/base_version
@@ -0,0 +1 @@
+4.0.5 \ No newline at end of file
diff --git a/storage/mroonga/vendor/groonga/benchmark/Makefile.am b/storage/mroonga/vendor/groonga/benchmark/Makefile.am
new file mode 100644
index 00000000000..146bd49d8ff
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/Makefile.am
@@ -0,0 +1,89 @@
+SUBDIRS = \
+ fixtures \
+ lib
+
+NONEXISTENT_CXX_SOURCE = nonexistent.cpp
+
+if WITH_BENCHMARK
+noinst_PROGRAMS = \
+ bench-table-factory \
+ bench-geo-distance \
+ bench-geo-select \
+ bench-ctx-create \
+ bench-query-optimizer
+endif
+
+EXTRA_DIST = \
+ bench-query-optimizer-ddl.grn
+
+AM_CPPFLAGS = \
+ -I$(srcdir) \
+ -I$(srcdir)/lib \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib \
+ $(GROONGA_INCLUDEDIR)
+
+AM_CFLAGS = \
+ $(GLIB_CFLAGS)
+
+LIBS = \
+ $(top_builddir)/lib/libgroonga.la \
+ $(top_builddir)/benchmark/lib/libbenchmark.la \
+ $(GLIB_LIBS)
+
+bench_table_factory_SOURCES = bench-table-factory.c
+nodist_EXTRA_bench_table_factory_SOURCES = $(NONEXISTENT_CXX_SOURCE)
+
+bench_geo_distance_SOURCES = bench-geo-distance.c
+nodist_EXTRA_bench_geo_distance_SOURCES = $(NONEXISTENT_CXX_SOURCE)
+
+bench_geo_select_SOURCES = bench-geo-select.c
+nodist_EXTRA_bench_geo_select_SOURCES = $(NONEXISTENT_CXX_SOURCE)
+
+bench_ctx_create_SOURCES = bench-ctx-create.c
+nodist_EXTRA_bench_ctx_create_SOURCES = $(NONEXISTENT_CXX_SOURCE)
+
+bench_query_optimizer_SOURCES = bench-query-optimizer.c
+nodist_EXTRA_bench_query_optimizer_SOURCES = $(NONEXISTENT_CXX_SOURCE)
+
+benchmarks = \
+ run-bench-table-factory \
+ run-bench-geo-distance \
+ run-bench-geo-select \
+ run-bench-ctx-create \
+ run-bench-query-optimizer
+
+run-bench-table-factory: bench-table-factory
+ @echo $@:
+ ./bench-table-factory
+
+run-bench-geo-distance: bench-geo-distance
+ @echo $@:
+ ./bench-geo-distance
+
+run-bench-geo-select: bench-geo-select
+ @echo $@:
+ env \
+ RUBY="$(RUBY)" \
+ GROONGA="$(GROONGA)" \
+ srcdir="$(srcdir)" \
+ $(srcdir)/bench-geo-select.sh
+
+run-bench-ctx-create: bench-ctx-create
+ @echo $@:
+ ./bench-ctx-create
+
+run-bench-query-optimizer: bench-query-optimizer
+ @echo $@:
+ @rm -rf tmp/query-optimizer
+ @mkdir -p tmp/query-optimizer
+ @env \
+ GRN_RUBY_SCRIPTS_DIR=$(top_srcdir)/lib/mrb/scripts \
+ ../src/groonga \
+ --file $(srcdir)/bench-query-optimizer-ddl.grn \
+ -n tmp/query-optimizer/db > /dev/null
+ env \
+ GRN_RUBY_SCRIPTS_DIR=$(top_srcdir)/lib/mrb/scripts \
+ ./bench-query-optimizer
+
+benchmark: $(benchmarks)
diff --git a/storage/mroonga/vendor/groonga/benchmark/bench-ctx-create.c b/storage/mroonga/vendor/groonga/benchmark/bench-ctx-create.c
new file mode 100644
index 00000000000..3e43519071d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/bench-ctx-create.c
@@ -0,0 +1,192 @@
+/* -*- c-basic-offset: 2; coding: utf-8 -*- */
+/*
+ Copyright (C) 2013-2014 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/*
+ Groonga: 6b128f318d682e50648f3b82c5f7956f3bcb3fe8
+ CFLAGS: -O0 -g3
+ % (cd benchmark/ && make --quiet run-bench-ctx-create)
+ run-bench-ctx-create:
+ (time)
+ with mruby1: 288KB (0.0091s)
+ without mruby1: 28KB (0.0240ms)
+ with mruby2: 0KB (0.0097s)
+ without mruby2: 0KB (0.0220ms)
+
+ Groonga: c4379140c02699e3c74b94cd9e7b88d372202aa5
+ CFLAGS: -O2 -g
+ % make --quiet -C benchmark run-bench-ctx-create
+ run-bench-ctx-create:
+ (time)
+ with mruby1: 524KB (0.0041s)
+ without mruby1: 32KB (0.0220ms)
+ with mruby2: 0KB (0.0040s)
+ without mruby2: 0KB (0.0200ms)
+*/
+
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include <groonga.h>
+
+#include "lib/benchmark.h"
+
+typedef struct _BenchmarkData {
+ grn_ctx context;
+ grn_obj *database;
+ guint memory_usage_before;
+} BenchmarkData;
+
+static guint
+get_memory_usage(void)
+{
+ GRegex *vm_rss_pattern;
+ gchar *status;
+ GMatchInfo *match_info;
+ gchar *vm_rss_string;
+ guint vm_rss;
+
+ g_file_get_contents("/proc/self/status", &status, NULL, NULL);
+
+ vm_rss_pattern = g_regex_new("VmRSS:\\s*(\\d*)\\s+kB", 0, 0, NULL);
+ if (!g_regex_match(vm_rss_pattern, status, 0, &match_info)) {
+ g_print("not match...: %s\n", status);
+ return 0;
+ }
+ vm_rss_string = g_match_info_fetch(match_info, 1);
+ vm_rss = atoi(vm_rss_string);
+ g_free(vm_rss_string);
+ g_match_info_free(match_info);
+ g_regex_unref(vm_rss_pattern);
+ g_free(status);
+
+ return vm_rss;
+}
+
+static void
+bench_with_mruby(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ g_setenv("GRN_MRUBY_ENABLED", "yes", TRUE);
+ grn_ctx_init(&(data->context), 0);
+ grn_ctx_use(&(data->context), data->database);
+}
+
+static void
+bench_without_mruby(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ g_setenv("GRN_MRUBY_ENABLED", "no", TRUE);
+ grn_ctx_init(&(data->context), 0);
+ grn_ctx_use(&(data->context), data->database);
+}
+
+static void
+bench_setup(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ data->memory_usage_before = get_memory_usage();
+}
+
+static void
+bench_teardown(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ grn_ctx_fin(&(data->context));
+ g_print("%3dKB ", get_memory_usage() - data->memory_usage_before);
+}
+
+static gchar *
+get_tmp_dir(void)
+{
+ gchar *current_dir;
+ gchar *tmp_dir;
+
+ current_dir = g_get_current_dir();
+ tmp_dir = g_build_filename(current_dir, "tmp", NULL);
+ g_free(current_dir);
+
+ return tmp_dir;
+}
+
+static grn_obj *
+setup_database(grn_ctx *context)
+{
+ gchar *tmp_dir;
+ gchar *database_path;
+ grn_obj *database;
+
+ tmp_dir = get_tmp_dir();
+ database_path = g_build_filename(tmp_dir, "ctx-create", "db", NULL);
+ database = grn_db_open(context, database_path);
+
+ g_free(database_path);
+
+ return database;
+}
+
+static void
+teardown_database(grn_ctx *context, grn_obj *database)
+{
+ grn_obj_close(context, database);
+}
+
+int
+main(int argc, gchar **argv)
+{
+ grn_ctx context;
+ BenchmarkData data;
+ BenchReporter *reporter;
+ gint n = 1;
+
+ grn_init();
+ bench_init(&argc, &argv);
+
+ grn_ctx_init(&context, 0);
+
+ data.database = setup_database(&context);
+
+ reporter = bench_reporter_new();
+
+#define REGISTER(label, bench_function) \
+ bench_reporter_register(reporter, label, n, \
+ bench_setup, \
+ bench_function, \
+ bench_teardown, \
+ &data)
+ REGISTER("with mruby1", bench_with_mruby);
+ REGISTER("without mruby1", bench_without_mruby);
+ REGISTER("with mruby2", bench_with_mruby);
+ REGISTER("without mruby2", bench_without_mruby);
+#undef REGISTER
+
+ bench_reporter_run(reporter);
+ g_object_unref(reporter);
+
+ teardown_database(&context, data.database);
+
+ grn_ctx_fin(&context);
+
+ grn_fin();
+
+ return 0;
+}
diff --git a/storage/mroonga/vendor/groonga/benchmark/bench-geo-distance.c b/storage/mroonga/vendor/groonga/benchmark/bench-geo-distance.c
new file mode 100644
index 00000000000..8e1d819538b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/bench-geo-distance.c
@@ -0,0 +1,499 @@
+/* -*- c-basic-offset: 2; coding: utf-8 -*- */
+/*
+ Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/*
+ groonga: 3ad91b868909444f66a36dbcbdbe2292ed14bd72
+ CFLAGS: -O0 -g
+ CPU: AMD Athlon(tm) 64 Processor 3000+
+
+ % test/benchmark/bench-geo-distance
+ (time)
+ rectangular (WGS84): (0.0813662)
+ rectangular (TOKYO): (0.0621928)
+ spherical (WGS84): (0.0760155)
+ spherical (TOKYO): (0.0660843)
+ hubeny (WGS84): (0.110684)
+ hubeny (TOKYO): (0.0702277)
+ % test/benchmark/bench-geo-distance
+ (time)
+ rectangular (WGS84): (0.0742154)
+ rectangular (TOKYO): (0.0816863)
+ spherical (WGS84): (0.074316)
+ spherical (TOKYO): (0.0696254)
+ hubeny (WGS84): (0.0650147)
+ hubeny (TOKYO): (0.0644057)
+ % test/benchmark/bench-geo-distance
+ (time)
+ rectangular (WGS84): (0.0781161)
+ rectangular (TOKYO): (0.0706679)
+ spherical (WGS84): (0.075739)
+ spherical (TOKYO): (0.0809402)
+ hubeny (WGS84): (0.0727023)
+ hubeny (TOKYO): (0.0718146)
+ */
+
+#include <string.h>
+
+#include <db.h>
+#include <groonga.h>
+
+#include "lib/benchmark.h"
+
+#define GET(context, name) (grn_ctx_get(context, name, strlen(name)))
+
+grn_obj *grn_expr_get_value(grn_ctx *ctx, grn_obj *expr, int offset);
+
+typedef struct _BenchmarkData
+{
+ gchar *base_dir;
+ gboolean report_result;
+
+ grn_ctx *context;
+ grn_obj *database;
+ grn_obj *geo_distance_proc;
+ grn_obj *expression;
+ grn_obj *start_point;
+ grn_obj *end_point;
+} BenchmarkData;
+
+static void
+bench_geo_distance(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ grn_proc_call(data->context, data->geo_distance_proc,
+ 2, data->expression);
+}
+
+static void
+bench_setup_common(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ grn_ctx_init(data->context, GRN_CTX_USE_QL);
+ data->database = grn_db_create(data->context, NULL, NULL);
+ data->expression = grn_expr_create(data->context, NULL, 0);
+}
+
+static void
+bench_setup_points(gpointer user_data,
+ const gchar *start_point_string,
+ const gchar *end_point_string,
+ grn_builtin_type wgs84_or_tgs)
+{
+ BenchmarkData *data = user_data;
+ grn_obj start_point_text, end_point_text;
+
+ GRN_TEXT_INIT(&start_point_text, 0);
+ GRN_TEXT_INIT(&end_point_text, 0);
+ GRN_TEXT_SETS(data->context, &start_point_text, start_point_string);
+ GRN_TEXT_SETS(data->context, &end_point_text, end_point_string);
+
+ data->start_point = grn_obj_open(data->context, GRN_BULK, 0, wgs84_or_tgs);
+ data->end_point = grn_obj_open(data->context, GRN_BULK, 0, wgs84_or_tgs);
+ grn_obj_cast(data->context, &start_point_text, data->start_point, GRN_FALSE);
+ grn_obj_cast(data->context, &end_point_text, data->end_point, GRN_FALSE);
+ grn_ctx_push(data->context, data->start_point);
+ grn_ctx_push(data->context, data->end_point);
+
+ grn_obj_unlink(data->context, &start_point_text);
+ grn_obj_unlink(data->context, &end_point_text);
+}
+
+static void
+bench_setup_wgs84(gpointer user_data)
+{
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "127980000x502560000",
+ "128880000x503640000",
+ GRN_DB_WGS84_GEO_POINT);
+}
+
+static void
+bench_setup_tgs(gpointer user_data)
+{
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "127980000x502560000",
+ "128880000x503640000",
+ GRN_DB_TOKYO_GEO_POINT);
+}
+
+static void
+bench_setup_rectangular_wgs84(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_wgs84(user_data);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_tgs(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_tgs(user_data);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_1st_to_2nd_quadrant_short(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "128452975x503157902",
+ "139380000x-31920000",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_2nd_to_1st_quadrant_short(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "139380000x-31920000",
+ "128452975x503157902",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_1st_to_3rd_quadrant_short(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "128452975x503157902",
+ "-56880000x-172310000",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_3rd_to_1st_quadrant_short(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "-56880000x-172310000",
+ "128452975x503157902",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_1st_to_4th_quadrant_short(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "128452975x503157902",
+ "-122100000x66300000",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_4th_to_1st_quadrant_short(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "-122100000x66300000",
+ "128452975x503157902",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_2nd_to_4th_quadrant_short(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "139380000x-31920000",
+ "-122100000x66300000",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_4th_to_2nd_quadrant_short(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "-122100000x66300000",
+ "139380000x-31920000",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_1st_to_2nd_quadrant_long(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "128452975x503157902",
+ "135960000x-440760000",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_2nd_to_1st_quadrant_long(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "135960000x-440760000",
+ "128452975x503157902",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_2nd_to_3rd_quadrant_short(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "135960000x-440760000",
+ "-56880000x-172310000",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_3rd_to_2nd_quadrant_short(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "-56880000x-172310000",
+ "135960000x-440760000",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_3rd_to_4th_quadrant_short(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "-56880000x-172310000",
+ "-122100000x66300000",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_4th_to_3rd_quadrant_short(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "-122100000x66300000",
+ "-56880000x-172310000",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_3rd_to_4th_quadrant_long(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "-56880000x-172310000",
+ "-121926000x544351000",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_rectangular_wgs84_4th_to_3rd_quadrant_long(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_common(user_data);
+ bench_setup_points(user_data,
+ "-121926000x544351000",
+ "-56880000x-172310000",
+ GRN_DB_WGS84_GEO_POINT);
+ data->geo_distance_proc = GET(data->context, "geo_distance");
+}
+
+static void
+bench_setup_spherical_wgs84(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_wgs84(user_data);
+ data->geo_distance_proc = GET(data->context, "geo_distance2");
+}
+
+static void
+bench_setup_spherical_tgs(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_tgs(user_data);
+ data->geo_distance_proc = GET(data->context, "geo_distance2");
+}
+
+static void
+bench_setup_hubeny_wgs84(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_wgs84(user_data);
+ data->geo_distance_proc = GET(data->context, "geo_distance3");
+}
+
+static void
+bench_setup_hubeny_tgs(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ bench_setup_tgs(user_data);
+ data->geo_distance_proc = GET(data->context, "geo_distance3");
+}
+
+static void
+bench_teardown(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ if (data->report_result) {
+ grn_obj *result;
+ result = grn_expr_get_value(data->context, data->expression, 0);
+ g_print("result: %g\n", GRN_FLOAT_VALUE(result));
+ /* http://vldb.gsi.go.jp/ says '38820.79' in WGS84 and
+ '38816.42' in Tokyo geodetic system for a distance
+ between '127980000x502560000' and '128880000x503640000'. */
+ }
+
+ grn_obj_unlink(data->context, data->end_point);
+ grn_obj_unlink(data->context, data->start_point);
+ grn_obj_unlink(data->context, data->expression);
+ grn_obj_unlink(data->context, data->database);
+ grn_ctx_fin(data->context);
+}
+
+int
+main(int argc, gchar **argv)
+{
+ BenchmarkData data;
+ BenchReporter *reporter;
+ gint n = 1000;
+
+ grn_init();
+ bench_init(&argc, &argv);
+
+ data.report_result = g_getenv("GROONGA_BENCH_REPORT_RESULT") != NULL;
+ data.context = g_new(grn_ctx, 1);
+
+ {
+ const gchar *groonga_bench_n;
+ groonga_bench_n = g_getenv("GROONGA_BENCH_N");
+ if (groonga_bench_n) {
+ n = atoi(groonga_bench_n);
+ }
+ }
+
+ reporter = bench_reporter_new();
+
+#define REGISTER(label, setup) \
+ bench_reporter_register(reporter, label, n, \
+ bench_setup_ ## setup, \
+ bench_geo_distance, \
+ bench_teardown, \
+ &data)
+ REGISTER("rectangular (WGS84)", rectangular_wgs84);
+ REGISTER("rectangular (TOKYO)", rectangular_tgs);
+ REGISTER("rectangular (WGS84 Tokyo to Lisbon)",
+ rectangular_wgs84_1st_to_2nd_quadrant_short);
+ REGISTER("rectangular (WGS84 Lisbon to Tokyo)",
+ rectangular_wgs84_2nd_to_1st_quadrant_short);
+ REGISTER("rectangular (WGS84 Tokyo to San Francisco)",
+ rectangular_wgs84_1st_to_2nd_quadrant_long);
+ REGISTER("rectangular (WGS84 San Francisco to Tokyo)",
+ rectangular_wgs84_2nd_to_1st_quadrant_long);
+ REGISTER("rectangular (WGS84 Brasplia to Cape Town)",
+ rectangular_wgs84_3rd_to_4th_quadrant_short);
+ REGISTER("rectangular (WGS84 Cape Town to Brasplia)",
+ rectangular_wgs84_4th_to_3rd_quadrant_short);
+ REGISTER("rectangular (WGS84 Brasplia to Sydney)",
+ rectangular_wgs84_3rd_to_4th_quadrant_long);
+ REGISTER("rectangular (WGS84 Sydney to Brasplia)",
+ rectangular_wgs84_4th_to_3rd_quadrant_long);
+ REGISTER("rectangular (WGS84 Tokyo to Brasplia)",
+ rectangular_wgs84_1st_to_4th_quadrant_short);
+ REGISTER("rectangular (WGS84 Brasplia to Tokyo)",
+ rectangular_wgs84_4th_to_1st_quadrant_short);
+ REGISTER("rectangular (WGS84 Lisbon to Cape Town)",
+ rectangular_wgs84_2nd_to_3rd_quadrant_short);
+ REGISTER("rectangular (WGS84 Cape Town to Lisbon)",
+ rectangular_wgs84_3rd_to_2nd_quadrant_short);
+ REGISTER("rectangular (WGS84 Tokyo to Cape Town)",
+ rectangular_wgs84_1st_to_3rd_quadrant_short);
+ REGISTER("rectangular (WGS84 Cape Town to Tokyo)",
+ rectangular_wgs84_3rd_to_1st_quadrant_short);
+ REGISTER("rectangular (WGS84 Lisbon to Cape Town)",
+ rectangular_wgs84_2nd_to_4th_quadrant_short);
+ REGISTER("rectangular (WGS84 Cape Town to Lisbon)",
+ rectangular_wgs84_4th_to_2nd_quadrant_short);
+ REGISTER("spherical (WGS84)", spherical_wgs84);
+ REGISTER("spherical (TOKYO)", spherical_tgs);
+ REGISTER("hubeny (WGS84)", hubeny_wgs84);
+ REGISTER("hubeny (TOKYO)", hubeny_tgs);
+#undef REGISTER
+
+ bench_reporter_run(reporter);
+ g_object_unref(reporter);
+
+ g_free(data.context);
+
+ bench_quit();
+ grn_fin();
+
+ return 0;
+}
diff --git a/storage/mroonga/vendor/groonga/benchmark/bench-geo-select.c b/storage/mroonga/vendor/groonga/benchmark/bench-geo-select.c
new file mode 100644
index 00000000000..b934f225330
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/bench-geo-select.c
@@ -0,0 +1,269 @@
+/* -*- c-basic-offset: 2; coding: utf-8 -*- */
+/*
+ Copyright (C) 2011 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/*
+ groonga: f56f466a05d756336f26ea5c2e54e9bdf5d3d681
+ CFLAGS: -O0 -ggdb3
+ CPU: Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz stepping 05
+ % (cd test/benchmark/ && make --quiet run-bench-geo-select)
+ run-bench-geo-select:
+ (time)
+ 1st: select_in_rectangle (partial): (0.819828)
+ 2nd: select_in_rectangle (partial): (0.832293)
+ 1st: select_in_rectangle (all): (8.82504)
+ 2nd: select_in_rectangle (all): (8.97628)
+
+ % (cd test/benchmark; GRN_GEO_CURSOR_STRICTLY=yes make --quiet run-bench-geo-select)
+ run-bench-geo-select:
+ (time)
+ 1st: select_in_rectangle (partial): (0.528143)
+ 2nd: select_in_rectangle (partial): (0.518647)
+ 1st: select_in_rectangle (all): (8.77378)
+ 2nd: select_in_rectangle (all): (8.76765)
+
+ groonga: f56f466a05d756336f26ea5c2e54e9bdf5d3d681
+ CFLAGS: -O3 -ggdb3
+ CPU: Intel(R) Core(TM) i7 CPU 860 @ 2.80GHz stepping 05
+ % (cd test/benchmark/ && make --quiet run-bench-geo-select)
+ run-bench-geo-select:
+ (time)
+ 1st: select_in_rectangle (partial): (0.415439)
+ 2nd: select_in_rectangle (partial): (0.423479)
+ 1st: select_in_rectangle (all): (4.63983)
+ 2nd: select_in_rectangle (all): (4.53055)
+
+ % (cd test/benchmark; GRN_GEO_CURSOR_STRICTLY=yes make --quiet run-bench-geo-select)
+ run-bench-geo-select:
+ (time)
+ 1st: select_in_rectangle (partial): (0.26974)
+ 2nd: select_in_rectangle (partial): (0.250247)
+ 1st: select_in_rectangle (all): (4.45263)
+ 2nd: select_in_rectangle (all): (4.61558)
+*/
+
+#include <string.h>
+
+#include <db.h>
+#include <groonga.h>
+
+#include "lib/benchmark.h"
+
+#define GET(context, name) (grn_ctx_get(context, name, strlen(name)))
+
+typedef struct _BenchmarkData
+{
+ gboolean report_result;
+
+ grn_ctx *context;
+ grn_obj *database;
+ grn_obj *table;
+ grn_obj *index_column;
+ grn_obj *result;
+
+ grn_obj top_left_point;
+ grn_obj bottom_right_point;
+} BenchmarkData;
+
+static void
+set_geo_point(grn_ctx *context, grn_obj *geo_point, const gchar *geo_point_text)
+{
+ grn_obj point_text;
+
+ GRN_TEXT_INIT(&point_text, 0);
+ GRN_TEXT_PUTS(context, &point_text, geo_point_text);
+ grn_obj_cast(context, &point_text, geo_point, GRN_FALSE);
+ grn_obj_unlink(context, &point_text);
+}
+
+static void
+bench_setup_common(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+ const gchar *tokyo_station = "35.68136,139.76609";
+ const gchar *ikebukuro_station = "35.72890,139.71036";
+
+ data->result = grn_table_create(data->context, NULL, 0, NULL,
+ GRN_OBJ_TABLE_HASH_KEY | GRN_OBJ_WITH_SUBREC,
+ data->table, NULL);
+
+ set_geo_point(data->context, &(data->top_left_point),
+ ikebukuro_station);
+ set_geo_point(data->context, &(data->bottom_right_point),
+ tokyo_station);
+}
+
+static void
+bench_setup_query_partial(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+ const gchar *tokyo_station = "35.68136,139.76609";
+ const gchar *ikebukuro_station = "35.72890,139.71036";
+
+ set_geo_point(data->context, &(data->top_left_point),
+ ikebukuro_station);
+ set_geo_point(data->context, &(data->bottom_right_point),
+ tokyo_station);
+}
+
+static void
+bench_setup_query_all(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+ const gchar *tokyo_station = "35.0,140.0";
+ const gchar *ikebukuro_station = "36.0,139.0";
+
+ set_geo_point(data->context, &(data->top_left_point),
+ ikebukuro_station);
+ set_geo_point(data->context, &(data->bottom_right_point),
+ tokyo_station);
+}
+
+static void
+bench_setup_in_rectangle_partial(gpointer user_data)
+{
+ bench_setup_common(user_data);
+ bench_setup_query_partial(user_data);
+}
+
+static void
+bench_setup_in_rectangle_all(gpointer user_data)
+{
+ bench_setup_common(user_data);
+ bench_setup_query_all(user_data);
+}
+
+static void
+bench_geo_select_in_rectangle(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ grn_geo_select_in_rectangle(data->context,
+ data->index_column,
+ &(data->top_left_point),
+ &(data->bottom_right_point),
+ data->result,
+ GRN_OP_OR);
+}
+
+static void
+bench_teardown(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ if (data->report_result) {
+ g_print("result: %d\n", grn_table_size(data->context, data->result));
+ }
+
+ grn_obj_unlink(data->context, data->result);
+}
+
+static gchar *
+get_tmp_dir(void)
+{
+ gchar *current_dir;
+ gchar *tmp_dir;
+
+ current_dir = g_get_current_dir();
+ tmp_dir = g_build_filename(current_dir, "tmp", NULL);
+ g_free(current_dir);
+
+ return tmp_dir;
+}
+
+static void
+setup_database(BenchmarkData *data)
+{
+ gchar *tmp_dir;
+ gchar *database_path;
+
+ tmp_dir = get_tmp_dir();
+ database_path = g_build_filename(tmp_dir, "geo-select", "db", NULL);
+ data->database = grn_db_open(data->context, database_path);
+
+ data->table = GET(data->context, "Addresses");
+ data->index_column = GET(data->context, "Locations.address");
+
+ g_free(database_path);
+}
+
+static void
+teardown_database(BenchmarkData *data)
+{
+ grn_obj_unlink(data->context, data->index_column);
+ grn_obj_unlink(data->context, data->table);
+ grn_obj_unlink(data->context, data->database);
+}
+
+int
+main(int argc, gchar **argv)
+{
+ BenchmarkData data;
+ BenchReporter *reporter;
+ gint n = 100;
+
+ grn_init();
+ bench_init(&argc, &argv);
+
+ data.report_result = g_getenv("GROONGA_BENCH_REPORT_RESULT") != NULL;
+
+ data.context = g_new(grn_ctx, 1);
+ grn_ctx_init(data.context, 0);
+
+ setup_database(&data);
+ GRN_WGS84_GEO_POINT_INIT(&(data.top_left_point), 0);
+ GRN_WGS84_GEO_POINT_INIT(&(data.bottom_right_point), 0);
+
+ {
+ const gchar *groonga_bench_n;
+ groonga_bench_n = g_getenv("GROONGA_BENCH_N");
+ if (groonga_bench_n) {
+ n = atoi(groonga_bench_n);
+ }
+ }
+
+ reporter = bench_reporter_new();
+
+#define REGISTER(label, type, area) \
+ bench_reporter_register(reporter, \
+ label, \
+ n, \
+ bench_setup_ ## type ## _ ## area, \
+ bench_geo_select_ ## type, \
+ bench_teardown, \
+ &data)
+ REGISTER("1st: select_in_rectangle (partial)", in_rectangle, partial);
+ REGISTER("2nd: select_in_rectangle (partial)", in_rectangle, partial);
+ REGISTER("1st: select_in_rectangle (all)", in_rectangle, all);
+ REGISTER("2nd: select_in_rectangle (all)", in_rectangle, all);
+#undef REGISTER
+
+ bench_reporter_run(reporter);
+ g_object_unref(reporter);
+
+ grn_obj_unlink(data.context, &(data.top_left_point));
+ grn_obj_unlink(data.context, &(data.bottom_right_point));
+ teardown_database(&data);
+
+ grn_ctx_fin(data.context);
+ g_free(data.context);
+
+ bench_quit();
+ grn_fin();
+
+ return 0;
+}
diff --git a/storage/mroonga/vendor/groonga/benchmark/bench-geo-select.sh b/storage/mroonga/vendor/groonga/benchmark/bench-geo-select.sh
new file mode 100755
index 00000000000..7e84c8fc943
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/bench-geo-select.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+base_dir="$(dirname $0)"
+
+fixture_dir="${srcdir}/fixtures/geo-select"
+data_dir="${base_dir}/fixtures/geo-select"
+csv_xz="${fixture_dir}/13_2010.CSV.xz"
+csv="${data_dir}/13_2010.CSV"
+grn="${data_dir}/load.grn"
+
+geo_select_generate_grn_rb="${base_dir}/geo-select-generate-grn.rb"
+
+db="${base_dir}/tmp/geo-select/db"
+
+bench_geo_select="./bench-geo-select"
+
+mkdir -p "${data_dir}"
+if [ ! -s "${csv}" ] || [ "${csv}" -ot "${csv_xz}" ]; then
+ echo "extracting ${csv_xz}..."
+ xzcat "${csv_xz}" | iconv --from-code cp932 --to-code utf-8 > "${csv}"
+fi
+
+if [ ! -s "${grn}" ] || [ "${grn}" -ot "${csv}" ]; then
+ echo "generating test data..."
+ "${RUBY}" "${geo_select_generate_grn_rb}" "${csv}" "${grn}"
+fi
+
+if [ ! -s "${db}" ] || [ "${db}" -ot "${grn}" ]; then
+ echo "creating test database..."
+ rm -rf "$(dirname ${db})"
+ mkdir -p "$(dirname ${db})"
+ "${GROONGA}" -n "${db}" < "${grn}"
+fi
+
+if [ "${GROONGA_BENCH_DEBUG}" = "yes" ]; then
+ bench_geo_select="../../libtool --mode=execute gdb --args ${bench_geo_select}"
+fi
+${bench_geo_select}
diff --git a/storage/mroonga/vendor/groonga/benchmark/bench-query-optimizer-ddl.grn b/storage/mroonga/vendor/groonga/benchmark/bench-query-optimizer-ddl.grn
new file mode 100644
index 00000000000..68317ae9ee2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/bench-query-optimizer-ddl.grn
@@ -0,0 +1,16 @@
+table_create Entries TABLE_PAT_KEY ShortText
+column_create Entries name COLUMN_SCALAR ShortText
+column_create Entries description COLUMN_SCALAR Text
+column_create Entries last_modified COLUMN_SCALAR Time
+
+table_create Bigram TABLE_PAT_KEY ShortText \
+ --default_tokenizer TokenBigram \
+ --normalizer NormalizerAuto
+column_create Bigram entries_name COLUMN_INDEX|WITH_POSITION \
+ Entries name
+column_create Bigram entries_description COLUMN_INDEX|WITH_POSITION \
+ Entries description
+
+table_create Times TABLE_PAT_KEY Time
+column_create Times entries_last_modified COLUMN_INDEX \
+ Entries last_modified
diff --git a/storage/mroonga/vendor/groonga/benchmark/bench-query-optimizer.c b/storage/mroonga/vendor/groonga/benchmark/bench-query-optimizer.c
new file mode 100644
index 00000000000..d7ed91d0c56
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/bench-query-optimizer.c
@@ -0,0 +1,208 @@
+/* -*- c-basic-offset: 2; coding: utf-8 -*- */
+/*
+ Copyright (C) 2014 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/*
+ Groonga: c4379140c02699e3c74b94cd9e7b88d372202aa5
+
+ CFLAGS: -O0 -g3
+ % make --quiet -C benchmark run-bench-query-optimizer
+ run-bench-query-optimizer:
+ Process 100 times in each pattern
+ (time)
+ 1 condition: with mruby: (0.0362s)
+ 1 condition: without mruby: (0.0216s)
+ 4 conditions: with mruby: (0.0864s)
+ 4 conditions: without mruby: (0.0271s)
+
+ CFLAGS: -O2 -g
+ % make --quiet -C benchmark run-bench-query-optimizer
+ run-bench-query-optimizer:
+ Process 100 times in each pattern
+ (time)
+ 1 condition: with mruby: (0.0243s)
+ 1 condition: without mruby: (0.0159s)
+ 4 conditions: with mruby: (0.0452s)
+ 4 conditions: without mruby: (0.0188s)
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include <groonga.h>
+
+#include "lib/benchmark.h"
+
+typedef struct _BenchmarkData {
+ grn_ctx context;
+ grn_obj *database;
+ grn_bool use_mruby;
+ GString *command;
+} BenchmarkData;
+
+static void
+bench(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+ grn_ctx *context = &(data->context);
+ char *response;
+ unsigned int response_length;
+ int flags;
+
+ grn_ctx_send(context, data->command->str, data->command->len, 0);
+ grn_ctx_recv(context, &response, &response_length, &flags);
+}
+
+static void
+bench_setup(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ if (data->use_mruby) {
+ g_setenv("GRN_MRUBY_ENABLED", "yes", TRUE);
+ } else {
+ g_setenv("GRN_MRUBY_ENABLED", "no", TRUE);
+ }
+ grn_ctx_init(&(data->context), 0);
+ grn_ctx_use(&(data->context), data->database);
+}
+
+static void
+bench_teardown(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ grn_ctx_fin(&(data->context));
+}
+
+static gchar *
+get_tmp_dir(void)
+{
+ gchar *current_dir;
+ gchar *tmp_dir;
+
+ current_dir = g_get_current_dir();
+ tmp_dir = g_build_filename(current_dir, "tmp", NULL);
+ g_free(current_dir);
+
+ return tmp_dir;
+}
+
+static grn_obj *
+setup_database(grn_ctx *context)
+{
+ gchar *tmp_dir;
+ gchar *database_path;
+ grn_obj *database;
+ const gchar *warmup_command = "dump";
+ gchar *response;
+ unsigned int response_length;
+ int flags;
+
+ tmp_dir = get_tmp_dir();
+ database_path = g_build_filename(tmp_dir, "query-optimizer", "db", NULL);
+ database = grn_db_open(context, database_path);
+ grn_ctx_send(context, warmup_command, strlen(warmup_command), 0);
+ grn_ctx_recv(context, &response, &response_length, &flags);
+
+ g_free(database_path);
+
+ return database;
+}
+
+static void
+teardown_database(grn_ctx *context, grn_obj *database)
+{
+ grn_obj_close(context, database);
+}
+
+int
+main(int argc, gchar **argv)
+{
+ grn_ctx context;
+ grn_obj *database;
+ BenchReporter *reporter;
+ gint n = 100;
+
+ grn_init();
+ bench_init(&argc, &argv);
+
+ grn_ctx_init(&context, 0);
+
+ database = setup_database(&context);
+
+ reporter = bench_reporter_new();
+
+ g_print("Process %d times in each pattern\n", n);
+ {
+ BenchmarkData data_one_condition_with_mruby;
+ BenchmarkData data_one_condition_without_mruby;
+ BenchmarkData data_multiple_conditions_with_mruby;
+ BenchmarkData data_multiple_conditions_without_mruby;
+
+#define REGISTER(label, data, use_mruby_, command_) \
+ data.database = database; \
+ data.use_mruby = use_mruby_; \
+ data.command = g_string_new(command_); \
+ bench_reporter_register(reporter, label, \
+ n, \
+ bench_setup, \
+ bench, \
+ bench_teardown, \
+ &data)
+
+ REGISTER("1 condition: with mruby", data_one_condition_with_mruby,
+ GRN_TRUE,
+ "select Entries --cache no --query 'name:@Groonga'");
+ REGISTER("1 condition: without mruby", data_one_condition_without_mruby,
+ GRN_FALSE,
+ "select Entries --cache no --query 'name:@Groonga'");
+ REGISTER("4 conditions: with mruby",
+ data_multiple_conditions_with_mruby,
+ GRN_TRUE,
+ "select Entries --cache no --filter '"
+ "name @ \"Groonga\" && "
+ "description @ \"search\" && "
+ "last_modified >= \"2014-2-9 00:00:00\" && "
+ "last_modified <= \"2014-11-29 00:00:00\""
+ "'");
+ REGISTER("4 conditions: without mruby",
+ data_multiple_conditions_without_mruby,
+ GRN_FALSE,
+ "select Entries --cache no --filter '"
+ "name @ \"Groonga\" && "
+ "description @ \"search\" && "
+ "last_modified >= \"2014-2-9 00:00:00\" && "
+ "last_modified <= \"2014-11-29 00:00:00\""
+ "'");
+
+#undef REGISTER
+
+ bench_reporter_run(reporter);
+ }
+ g_object_unref(reporter);
+
+ teardown_database(&context, database);
+
+ grn_ctx_fin(&context);
+
+ grn_fin();
+
+ return 0;
+}
diff --git a/storage/mroonga/vendor/groonga/benchmark/bench-table-factory.c b/storage/mroonga/vendor/groonga/benchmark/bench-table-factory.c
new file mode 100644
index 00000000000..7e06874dffd
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/bench-table-factory.c
@@ -0,0 +1,270 @@
+/* -*- c-basic-offset: 2; coding: utf-8 -*- */
+/*
+ Copyright (C) 2008 Kouhei Sutou <kou@cozmixng.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <string.h>
+
+#include <groonga.h>
+
+#include "lib/benchmark.h"
+
+#define DEFAULT_FLAGS (GRN_OBJ_PERSISTENT | GRN_OBJ_TABLE_PAT_KEY)
+#define DEFAULT_VALUE_SIZE (1024)
+#define VALUE_TYPE_NAME "<value_type>"
+
+typedef struct _BenchmarkData
+{
+ gchar *base_dir;
+
+ grn_ctx *context;
+ const char *name;
+ unsigned name_size;
+ char *path;
+ grn_obj_flags flags;
+ grn_obj *key_type;
+ unsigned value_size;
+ grn_encoding encoding;
+} BenchmarkData;
+
+static void
+bench_normal(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+ grn_obj *table;
+ grn_obj *value_type = grn_ctx_get(data->context, VALUE_TYPE_NAME, strlen(VALUE_TYPE_NAME));
+ if (!value_type) {
+ value_type = grn_type_create(data->context, VALUE_TYPE_NAME, strlen(VALUE_TYPE_NAME),
+ 0, data->value_size);
+ }
+ table = grn_table_create(data->context,
+ data->name, data->name_size,
+ data->path, data->flags,
+ data->key_type, value_type);
+ grn_obj_close(data->context, table);
+}
+
+static void
+bench_normal_temporary(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+ grn_obj *table;
+ grn_obj *value_type = grn_ctx_get(data->context, VALUE_TYPE_NAME, strlen(VALUE_TYPE_NAME));
+ if (!value_type) {
+ value_type = grn_type_create(data->context, VALUE_TYPE_NAME, strlen(VALUE_TYPE_NAME),
+ 0, data->value_size);
+ }
+ GRN_CTX_SET_ENCODING(data->context, data->encoding);
+ table = grn_table_create(data->context,
+ data->name, data->name_size,
+ NULL, data->flags & ~GRN_OBJ_PERSISTENT,
+ data->key_type, value_type);
+ grn_obj_close(data->context, table);
+}
+
+typedef struct _grn_table_factory
+{
+ grn_ctx *context;
+ char *name;
+ unsigned name_size;
+ char *path;
+ grn_obj_flags flags;
+ grn_obj *key_type;
+ unsigned value_size;
+ grn_encoding encoding;
+} grn_table_factory;
+
+static grn_table_factory *
+grn_table_factory_create(void)
+{
+ grn_table_factory *factory;
+
+ factory = g_new0(grn_table_factory, 1);
+
+ factory->context = NULL;
+ factory->name = NULL;
+ factory->name_size = 0;
+ factory->path = NULL;
+ factory->flags = DEFAULT_FLAGS;
+ factory->key_type = NULL;
+ factory->value_size = DEFAULT_VALUE_SIZE;
+ factory->encoding = GRN_ENC_DEFAULT;
+
+ return factory;
+}
+
+static void
+grn_table_factory_set_context(grn_table_factory *factory, grn_ctx *context)
+{
+ factory->context = context;
+}
+
+static void
+grn_table_factory_set_name(grn_table_factory *factory, const char *name)
+{
+ factory->name = g_strdup(name);
+ factory->name_size = strlen(name);
+}
+
+static void
+grn_table_factory_set_path(grn_table_factory *factory, const char *path)
+{
+ factory->path = g_strdup(path);
+ if (path)
+ factory->flags |= GRN_OBJ_PERSISTENT;
+ else
+ factory->flags &= ~GRN_OBJ_PERSISTENT;
+}
+
+static void
+grn_table_factory_set_key_type(grn_table_factory *factory, grn_obj *key_type)
+{
+ factory->key_type = key_type;
+}
+
+static grn_obj *
+grn_table_factory_make(grn_table_factory *factory)
+{
+ grn_obj *value_type = grn_ctx_get(factory->context, VALUE_TYPE_NAME, strlen(VALUE_TYPE_NAME));
+ if (!value_type) {
+ value_type = grn_type_create(factory->context, VALUE_TYPE_NAME, strlen(VALUE_TYPE_NAME),
+ 0, factory->value_size);
+ }
+ GRN_CTX_SET_ENCODING(factory->context, factory->encoding);
+ return grn_table_create(factory->context,
+ factory->name, factory->name_size,
+ factory->path, factory->flags,
+ factory->key_type, value_type);
+}
+
+static void
+grn_table_factory_close(grn_table_factory *factory)
+{
+ g_free(factory->name);
+ g_free(factory->path);
+ g_free(factory);
+}
+
+static void
+bench_factory(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+ grn_table_factory *factory;
+ grn_obj *table;
+
+ factory = grn_table_factory_create();
+ grn_table_factory_set_context(factory, data->context);
+ grn_table_factory_set_name(factory, data->name);
+ grn_table_factory_set_path(factory, data->path);
+ grn_table_factory_set_key_type(factory, data->key_type);
+
+ table = grn_table_factory_make(factory);
+ grn_obj_close(data->context, table);
+
+ grn_table_factory_close(factory);
+}
+
+static void
+bench_factory_temporary(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+ grn_table_factory *factory;
+ grn_obj *table;
+
+ factory = grn_table_factory_create();
+ grn_table_factory_set_context(factory, data->context);
+ grn_table_factory_set_name(factory, data->name);
+ grn_table_factory_set_key_type(factory, data->key_type);
+
+ table = grn_table_factory_make(factory);
+ grn_obj_close(data->context, table);
+
+ grn_table_factory_close(factory);
+}
+
+static void
+bench_setup(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+ const gchar *type_name;
+
+ bench_utils_remove_path_recursive_force(data->base_dir);
+ g_mkdir_with_parents(data->base_dir, 0755);
+
+ grn_ctx_init(data->context, GRN_CTX_USE_QL);
+
+ type_name = "name";
+ data->key_type = grn_type_create(data->context,
+ type_name, strlen(type_name),
+ GRN_OBJ_KEY_UINT, sizeof(grn_id));
+
+}
+
+static void
+bench_teardown(gpointer user_data)
+{
+ BenchmarkData *data = user_data;
+
+ grn_obj_close(data->context, data->key_type);
+ grn_ctx_fin(data->context);
+ bench_utils_remove_path_recursive_force(data->base_dir);
+}
+
+int
+main(int argc, gchar **argv)
+{
+ BenchmarkData data;
+ BenchReporter *reporter;
+ gint n = 100;
+
+ grn_init();
+ bench_init(&argc, &argv);
+
+ data.context = g_new(grn_ctx, 1);
+ data.base_dir = g_build_filename(g_get_tmp_dir(), "groonga-bench", NULL);
+ data.name = "table";
+ data.name_size = strlen(data.name);
+ data.path = g_build_filename(data.base_dir, "table", NULL);
+ data.flags = DEFAULT_FLAGS;
+ data.key_type = NULL;
+ data.value_size = DEFAULT_VALUE_SIZE;
+ data.encoding = GRN_ENC_DEFAULT;
+
+ reporter = bench_reporter_new();
+ bench_reporter_register(reporter, "normal (persistent)", n,
+ bench_setup, bench_normal, bench_teardown, &data);
+ bench_reporter_register(reporter, "factory (persistent)", n,
+ bench_setup, bench_factory, bench_teardown, &data);
+ bench_reporter_register(reporter, "normal (temporary)", n,
+ bench_setup, bench_normal_temporary, bench_teardown,
+ &data);
+ bench_reporter_register(reporter, "factory (temporary)", n,
+ bench_setup, bench_factory_temporary, bench_teardown,
+ &data);
+ bench_reporter_run(reporter);
+ g_object_unref(reporter);
+
+ bench_utils_remove_path_recursive_force(data.base_dir);
+
+ g_free(data.path);
+ g_free(data.base_dir);
+ g_free(data.context);
+
+ bench_quit();
+ grn_fin();
+
+ return 0;
+}
diff --git a/storage/mroonga/vendor/groonga/benchmark/fixtures/Makefile.am b/storage/mroonga/vendor/groonga/benchmark/fixtures/Makefile.am
new file mode 100644
index 00000000000..3b7136f7c73
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/fixtures/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = \
+ geo-select
diff --git a/storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/13_2010.CSV.xz b/storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/13_2010.CSV.xz
new file mode 100644
index 00000000000..3cef52684c9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/13_2010.CSV.xz
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/Makefile.am b/storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/Makefile.am
new file mode 100644
index 00000000000..3aa25594f92
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/Makefile.am
@@ -0,0 +1,4 @@
+EXTRA_DIST = \
+ README.txt \
+ 13_2010.CSV.xz \
+ format_2010.html
diff --git a/storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/README.txt b/storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/README.txt
new file mode 100644
index 00000000000..c4a0620a3b7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/README.txt
@@ -0,0 +1,6 @@
+位置情報検索ベンチマーク用テストデータについて
+--------------------------------------------
+
+13_2010.CSV.xzは国土交通相の `位置参照情報ダウンロードサービス <http://nlftp.mlit.go.jp/isj/>`_ からダウンロードした2010年版の東京都の「大字・町丁目位置参照情報 国土交通省」です。xz圧縮しているだけでデータの編集・加工はしていません。CSVのフォーマットについてはformat_2010.htmlを参照してください。こちらもダウンロードサービスに含まれているものを編集・加工していません。
+
+`位置参照情報ダウンロードサービス利用約款 <http://nlftp.mlit.go.jp/isj/agreement.html>`_ に従い、上記のように出典を明記して利用しています。
diff --git a/storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/format_2010.html b/storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/format_2010.html
new file mode 100644
index 00000000000..5c2396c4e81
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/fixtures/geo-select/format_2010.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+<title>X惌xʒuQƏ (2010)</title>
+<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
+</head>
+
+ <body>
+ <h3 align="center">X惌xʒuQƏ (2010N) f[^`</h3>
+ <table width="90%" align="center">
+ <tr>
+ <td>
+ t@C`CSV(Camma Separated Values)łB<br/>
+ R[h́AShift-JISłB<br/>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <table width="100%" align="center" border="1">
+ <tr>
+ <th></th>
+ <th>l</th>
+ <th></th>
+ </tr>
+ <tr>
+ <td>s{</td>
+ <td><br/></td>
+ <td>"s"</td>
+ </tr>
+ <tr>
+ <td>s撬</td>
+ <td>S͌SAߎwss̋於܂ށB</td>
+ <td>"c"</td>
+ </tr>
+ <tr>
+ <td>厚E</td>
+ <td>ڂ̐͊</td>
+ <td>"֓񒚖"</td>
+ </tr>
+ <tr>
+ <td>X敄En</td>
+ <td>ƂĔpiꕔj</td>
+ <td>"1"</td>
+ </tr>
+ <tr>
+ <td>Wnԍ</td>
+ <td>ʒpWn̍Wnԍi1`19Fpj</td>
+ <td>9</td>
+ </tr>
+ <tr>
+ <td>wW</td>
+ <td>
+ ʒpWn̍Wn_̂<br/>
+ iPʁF[gAkvXlA_ȉ1ʂ܂Łj
+ </td>
+ <td>-35925.9</td>
+ </tr>
+ <tr>
+ <td>xW</td>
+ <td>
+ ʒpWn̍Wn_̂<br/>
+ iPʁF[gAvXlA_ȉ1ʂ܂Łj
+ </td>
+ <td>-7446.2</td>
+ </tr>
+ <tr>
+ <td>ܓx</td>
+ <td>\ioܓxiPʁFxA_ȉ6ʂ܂Łj</td>
+ <td>35.676155</td>
+ </tr>
+ <tr>
+ <td>ox</td>
+ <td>\ioܓxiPʁFxA_ȉ6ʂ܂Łj</td>
+ <td>139.751075</td>
+ </tr>
+ <tr>
+ <td>Z\tO</td>
+ <td>1FZ\{A0FZ\{</td>
+ <td>1</td>
+ </tr>
+ <tr>
+ <td>\tO</td>
+ <td>
+ 1‚̊X敄̑\_ɑΉtꍇȂǂɁÂ1‚ɕ֋XIɑ\tO𗧂ĂĂ܂B<br/>
+ 1F\A0F\Ȃ
+ <td>1</td>
+ </tr>
+ <tr>
+ <td>XVOtO</td>
+ <td>2007Nx2008Nxf[^Ɋ܂܂tO𗧂ĂĂ܂B<br/>
+ 1FVK쐬A2F̕ύXA3F폜A0FύXȂ</td>
+ <td>0</td>
+ </tr>
+ <tr>
+ <td>XV㗚tO</td>
+ <td>2009Nxȍ~̃f[^Ɋ܂܂tO𗧂ĂĂ܂B<br/>
+ 1FVK쐬A2F̕ύXA3F폜A0FύXȂ</td>
+ <td>0</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td align="right">
+ 23N0418쐬
+ </td>
+ </tr>
+ </table>
+</body>
+
+</html>
diff --git a/storage/mroonga/vendor/groonga/benchmark/geo-distance-summary.rb b/storage/mroonga/vendor/groonga/benchmark/geo-distance-summary.rb
new file mode 100755
index 00000000000..6559cb60f17
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/geo-distance-summary.rb
@@ -0,0 +1,140 @@
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
+
+require 'fileutils'
+require 'optparse'
+
+class BenchmarkSummary
+ attr_accessor :options
+
+ def initialize(options)
+ @options = options
+ @options[:count] ||= 10
+ end
+
+ def calc_total(data)
+ total = {}
+ @options[:count].times do |i|
+ data[i + 1].each do |key, value|
+ if total[key]
+ total[key] = total[key] + value
+ else
+ total[key] = value
+ end
+ end
+ end
+ total
+ end
+
+ def print_average(data)
+ total = calc_total(data)
+ text = "|項目|"
+ @options[:count].times do |i|
+ text << "#{i + 1}|"
+ text << "平均|\n" if i == @options[:count] - 1
+ end
+ total.each do |key, value|
+ line = [key]
+ @options[:count].times do |i|
+ data[i + 1].each do |data_key, data_value|
+ if key == data_key
+ line << data_value
+ end
+ end
+ end
+ line << [value / @options[:count].to_f]
+ text << sprintf("|%s|\n", line.join("|"))
+ end
+ puts text
+ end
+
+ def print_performance(before, after)
+ before_total = calc_total(before)
+ after_total = calc_total(after)
+ ratio = {}
+ before_total.each do |key, value|
+ ratio[key] = after_total[key] / value
+ end
+ text = "|項目|変更前|変更後|比率|備考|\n"
+ ratio.each do |key, value|
+ text << sprintf("|%s|%f|%f|%f||\n",
+ key,
+ before_total[key] / @options[:count].to_f,
+ after_total[key] / @options[:count].to_f,
+ ratio[key])
+ end
+ puts text
+ end
+
+ def parse_log(logs)
+ parse_result = {}
+ logs.each do |index, log|
+ File.open(log, "r") do |file|
+ data = file.read
+ entry = {}
+ data.split("\n").each do |line|
+ if line =~ /\s*(.+?):\s+\((.+)\)/
+ entry[$1] = $2.to_f
+ end
+ end
+ parse_result[index] = entry
+ end
+ end
+ parse_result
+ end
+end
+
+=begin
+
+Usage: geo-distance-summary.rb \
+-b run-bench-geo-distance-orig-N1000 \
+-a run-bench-geo-distance-work-N1000
+
+NOTE: expected that there are run-bench-geo-distance-orig-N1000-1.log \
+... \
+run-bench-geo-distance-orig-N1000-[N].log.
+
+=end
+
+if __FILE__ == $0
+
+ options = {}
+ parser = OptionParser.new
+ parser.on("-b", "--before PREFIX",
+ "log file name must be PREFIX-[N].log") do |prefix|
+ options[:before] = prefix
+ end
+ parser.on("-a", "--after PREFIX",
+ "log file name must be PREFIX-[N].log") do |prefix|
+ options[:after] = prefix
+ end
+ parser.on("-n", "data count") do |count|
+ options[:count] = count
+ end
+
+ parser.parse!(ARGV)
+
+ if not options.has_key?(:before) or not options.has_key?(:after)
+ puts(parser.to_s)
+ exit
+ end
+
+ bench_before_log = {}
+ bench_after_log = {}
+ Dir.glob("#{options[:before]}*.log") do |log|
+ log =~ /(.+)-(\d+)\.log$/
+ bench_before_log[$2.to_i] = log
+ end
+ Dir.glob("#{options[:after]}*.log") do |log|
+ log =~ /(.+)-(\d+)\.log$/
+ bench_after_log[$2.to_i] = log
+ end
+
+ bench = BenchmarkSummary.new(options)
+ bench_before = bench.parse_log(bench_before_log)
+ bench_after = bench.parse_log(bench_after_log)
+
+ bench.print_average(bench_before)
+ bench.print_average(bench_after)
+ bench.print_performance(bench_before, bench_after)
+end
diff --git a/storage/mroonga/vendor/groonga/benchmark/geo-select-generate-grn.rb b/storage/mroonga/vendor/groonga/benchmark/geo-select-generate-grn.rb
new file mode 100755
index 00000000000..dc90f7e65f7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/geo-select-generate-grn.rb
@@ -0,0 +1,53 @@
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
+
+if ARGV.size != 2
+ puts "Usage: #{$0} SOURCE_CSV OUTPUT_GRN"
+ puts " e.g.: #{$0} fixtures/geo-select/13_2010.CSV fixtures/geo-select/load.grn"
+ exit(false)
+end
+
+csv, grn = ARGV
+
+require "fileutils"
+require "csv"
+
+FileUtils.mkdir_p(File.dirname(grn))
+File.open(grn, "w") do |output|
+ output.print(<<-EOH.strip)
+table_create Addresses TABLE_HASH_KEY ShortText
+column_create Addresses location COLUMN_SCALAR WGS84GeoPoint
+
+table_create Locations TABLE_PAT_KEY WGS84GeoPoint
+column_create Locations address COLUMN_INDEX Addresses location
+
+load --table Addresses
+[
+["_key", "location"]
+EOH
+
+ headers = nil
+ csv_foreach_args = [csv]
+ csv_foreach_args << {:encoding => "UTF-8"} if defined?(Encoding)
+ CSV.foreach(*csv_foreach_args) do |row|
+ if headers.nil?
+ headers = row
+ else
+ record = {}
+ headers.each_with_index do |header, i|
+ record[header] = row[i]
+ end
+ central_value_p = record["代表フラグ"] == "1"
+ next unless central_value_p
+ name =
+ record["都道府県名"] + record["市区町村名"] +
+ record["大字・町丁目"] + record["街区符号・地番"]
+ location = "%sx%s" % [record["緯度"], record["経度"]]
+ output.print(",\n[\"#{name}\", \"#{location}\"]")
+ end
+ end
+ output.print(<<-EOF)
+
+]
+EOF
+end
diff --git a/storage/mroonga/vendor/groonga/benchmark/lib/Makefile.am b/storage/mroonga/vendor/groonga/benchmark/lib/Makefile.am
new file mode 100644
index 00000000000..55d113f92e9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/lib/Makefile.am
@@ -0,0 +1,21 @@
+if WITH_BENCHMARK
+noinst_LTLIBRARIES = \
+ libbenchmark.la
+endif
+
+AM_CPPFLAGS = \
+ -I$(srcdir)
+
+AM_CFLAGS = \
+ $(GLIB_CFLAGS)
+
+LIBS = \
+ $(GLIB_LIBS)
+
+libbenchmark_la_SOURCES = \
+ benchmark.c \
+ benchmark.h \
+ bench-reporter.c \
+ bench-reporter.h \
+ bench-utils.c \
+ bench-utils.h
diff --git a/storage/mroonga/vendor/groonga/benchmark/lib/bench-reporter.c b/storage/mroonga/vendor/groonga/benchmark/lib/bench-reporter.c
new file mode 100644
index 00000000000..3eb86a2645a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/lib/bench-reporter.c
@@ -0,0 +1,315 @@
+/* -*- c-basic-offset: 2; coding: utf-8 -*- */
+/*
+ Copyright (C) 2008 Kouhei Sutou <kou@cozmixng.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <string.h>
+
+#include "bench-reporter.h"
+
+typedef struct _BenchItem BenchItem;
+struct _BenchItem
+{
+ gchar *label;
+ gint n;
+ BenchSetupFunc bench_setup;
+ BenchFunc bench;
+ BenchTeardownFunc bench_teardown;
+ gpointer data;
+};
+
+static BenchItem *
+bench_item_new(const gchar *label, gint n,
+ BenchSetupFunc bench_setup,
+ BenchFunc bench,
+ BenchTeardownFunc bench_teardown,
+ gpointer data)
+{
+ BenchItem *item;
+
+ item = g_slice_new(BenchItem);
+
+ item->label = g_strdup(label);
+ item->n = n;
+ item->bench_setup = bench_setup;
+ item->bench = bench;
+ item->bench_teardown = bench_teardown;
+ item->data = data;
+
+ return item;
+}
+
+static void
+bench_item_free(BenchItem *item)
+{
+ if (item->label)
+ g_free(item->label);
+
+ g_slice_free(BenchItem, item);
+}
+
+#define BENCH_REPORTER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+ BENCH_TYPE_REPORTER, \
+ BenchReporterPrivate))
+
+typedef struct _BenchReporterPrivate BenchReporterPrivate;
+struct _BenchReporterPrivate
+{
+ GList *items;
+};
+
+G_DEFINE_TYPE(BenchReporter, bench_reporter, G_TYPE_OBJECT)
+
+static void dispose (GObject *object);
+
+static void
+bench_reporter_class_init(BenchReporterClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS(klass);
+
+ gobject_class->dispose = dispose;
+
+ g_type_class_add_private(gobject_class, sizeof(BenchReporterPrivate));
+}
+
+static void
+bench_reporter_init(BenchReporter *reporter)
+{
+ BenchReporterPrivate *priv;
+
+ priv = BENCH_REPORTER_GET_PRIVATE(reporter);
+
+ priv->items = NULL;
+}
+
+static void
+dispose(GObject *object)
+{
+ BenchReporterPrivate *priv;
+
+ priv = BENCH_REPORTER_GET_PRIVATE(object);
+
+ if (priv->items) {
+ g_list_foreach(priv->items, (GFunc)bench_item_free, NULL);
+ g_list_free(priv->items);
+ priv->items = NULL;
+ }
+
+ G_OBJECT_CLASS(bench_reporter_parent_class)->dispose(object);
+}
+
+BenchReporter *
+bench_reporter_new(void)
+{
+ return g_object_new(BENCH_TYPE_REPORTER, NULL);
+}
+
+void
+bench_reporter_register(BenchReporter *reporter,
+ const gchar *label, gint n,
+ BenchSetupFunc bench_setup,
+ BenchFunc bench,
+ BenchTeardownFunc bench_teardown,
+ gpointer data)
+{
+ BenchReporterPrivate *priv;
+
+ priv = BENCH_REPORTER_GET_PRIVATE(reporter);
+
+ priv->items = g_list_append(priv->items, bench_item_new(label, n,
+ bench_setup,
+ bench,
+ bench_teardown,
+ data));
+}
+
+#define INDENT " "
+
+static void
+print_header(BenchReporterPrivate *priv, gint max_label_length)
+{
+ gint n_spaces;
+
+ g_print(INDENT);
+ for (n_spaces = max_label_length + strlen(": ");
+ n_spaces > 0;
+ n_spaces--) {
+ g_print(" ");
+ }
+ g_print("(total) ");
+ g_print("(average) ");
+ g_print("(median)\n");
+}
+
+static void
+print_label(BenchReporterPrivate *priv, BenchItem *item, gint max_label_length)
+{
+ gint n_left_spaces;
+
+ g_print(INDENT);
+ if (item->label) {
+ n_left_spaces = max_label_length - strlen(item->label);
+ } else {
+ n_left_spaces = max_label_length;
+ }
+ for (; n_left_spaces > 0; n_left_spaces--) {
+ g_print(" ");
+ }
+ if (item->label)
+ g_print("%s", item->label);
+ g_print(": ");
+}
+
+static void
+report_elapsed_time(gdouble elapsed_time)
+{
+ gdouble one_second = 1.0;
+ gdouble one_millisecond = one_second / 1000.0;
+ gdouble one_microsecond = one_millisecond / 1000.0;
+
+ if (elapsed_time < one_microsecond) {
+ g_print("(%.8fms)", elapsed_time * 1000.0);
+ } else if (elapsed_time < one_millisecond) {
+ g_print("(%.4fms)", elapsed_time * 1000.0);
+ } else {
+ g_print("(%.4fs) ", elapsed_time);
+ }
+}
+
+static gdouble
+compute_total_elapsed_time(GArray *elapsed_times)
+{
+ guint i;
+ gdouble total = 0.0;
+
+ for (i = 0; i< elapsed_times->len; i++) {
+ gdouble elapsed_time = g_array_index(elapsed_times, gdouble, i);
+ total += elapsed_time;
+ }
+
+ return total;
+}
+
+static void
+report_elapsed_time_total(GArray *elapsed_times)
+{
+ report_elapsed_time(compute_total_elapsed_time(elapsed_times));
+}
+
+static void
+report_elapsed_time_average(GArray *elapsed_times)
+{
+ gdouble total;
+ gdouble average;
+
+ total = compute_total_elapsed_time(elapsed_times);
+ average = total / elapsed_times->len;
+ report_elapsed_time(average);
+}
+
+static gint
+compare_elapsed_time(gconstpointer a, gconstpointer b)
+{
+ const gdouble *elapsed_time1 = a;
+ const gdouble *elapsed_time2 = b;
+
+ if (*elapsed_time1 > *elapsed_time2) {
+ return 1;
+ } else if (*elapsed_time1 < *elapsed_time2) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+static void
+report_elapsed_time_median(GArray *elapsed_times)
+{
+ gdouble median;
+
+ g_array_sort(elapsed_times, compare_elapsed_time);
+ median = g_array_index(elapsed_times, gdouble, elapsed_times->len / 2);
+ report_elapsed_time(median);
+}
+
+static void
+report_elapsed_time_statistics(GArray *elapsed_times)
+{
+ report_elapsed_time_total(elapsed_times);
+ g_print(" ");
+ report_elapsed_time_average(elapsed_times);
+ g_print(" ");
+ report_elapsed_time_median(elapsed_times);
+ g_print("\n");
+}
+
+static void
+run_item(BenchReporterPrivate *priv, BenchItem *item, gint max_label_length)
+{
+ GTimer *timer;
+ GArray *elapsed_times;
+ gint i;
+
+ print_label(priv, item, max_label_length);
+
+ elapsed_times = g_array_new(FALSE, FALSE, sizeof(gdouble));
+
+ timer = g_timer_new();
+ for (i = 0; i < item->n; i++) {
+ gdouble elapsed_time;
+ if (item->bench_setup)
+ item->bench_setup(item->data);
+ g_timer_start(timer);
+ item->bench(item->data);
+ g_timer_stop(timer);
+ elapsed_time = g_timer_elapsed(timer, NULL);
+ g_array_append_val(elapsed_times, elapsed_time);
+ if (item->bench_teardown)
+ item->bench_teardown(item->data);
+ }
+ g_timer_destroy(timer);
+
+ report_elapsed_time_statistics(elapsed_times);
+
+ g_array_free(elapsed_times, TRUE);
+
+}
+
+void
+bench_reporter_run(BenchReporter *reporter)
+{
+ BenchReporterPrivate *priv;
+ GList *node;
+ gint max_label_length = 0;
+
+ priv = BENCH_REPORTER_GET_PRIVATE(reporter);
+ for (node = priv->items; node; node = g_list_next(node)) {
+ BenchItem *item = node->data;
+
+ if (item->label)
+ max_label_length = MAX(max_label_length, strlen(item->label));
+ }
+
+ print_header(priv, max_label_length);
+ for (node = priv->items; node; node = g_list_next(node)) {
+ BenchItem *item = node->data;
+
+ run_item(priv, item, max_label_length);
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/benchmark/lib/bench-reporter.h b/storage/mroonga/vendor/groonga/benchmark/lib/bench-reporter.h
new file mode 100644
index 00000000000..7ac51518d67
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/lib/bench-reporter.h
@@ -0,0 +1,64 @@
+/* -*- c-basic-offset: 2; coding: utf-8 -*- */
+/*
+ Copyright (C) 2008 Kouhei Sutou <kou@cozmixng.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef __BENCH_REPORTER_H__
+#define __BENCH_REPORTER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define BENCH_TYPE_REPORTER (bench_reporter_get_type())
+#define BENCH_REPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), BENCH_TYPE_REPORTER, BenchReporter))
+#define BENCH_REPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), BENCH_TYPE_REPORTER, BenchReporterClass))
+#define BENCH_IS_REPORTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), BENCH_TYPE_REPORTER))
+#define BENCH_IS_REPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), BENCH_TYPE_REPORTER))
+#define BENCH_REPORTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), BENCH_TYPE_REPORTER, BenchReporterClass))
+
+typedef struct _BenchReporter BenchReporter;
+typedef struct _BenchReporterClass BenchReporterClass;
+
+typedef void (*BenchSetupFunc) (gpointer user_data);
+typedef void (*BenchFunc) (gpointer user_data);
+typedef void (*BenchTeardownFunc) (gpointer user_data);
+
+struct _BenchReporter
+{
+ GObject object;
+};
+
+struct _BenchReporterClass
+{
+ GObjectClass parent_class;
+};
+
+GType bench_reporter_get_type (void) G_GNUC_CONST;
+
+BenchReporter *bench_reporter_new (void);
+void bench_reporter_register (BenchReporter *reporter,
+ const gchar *label,
+ gint n,
+ BenchSetupFunc bench_setup,
+ BenchFunc bench,
+ BenchTeardownFunc bench_teardown,
+ gpointer data);
+void bench_reporter_run (BenchReporter *reporter);
+
+#endif /* __BENCH_REPORTER_H__ */
+
+
diff --git a/storage/mroonga/vendor/groonga/benchmark/lib/bench-utils.c b/storage/mroonga/vendor/groonga/benchmark/lib/bench-utils.c
new file mode 100644
index 00000000000..a4279731374
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/lib/bench-utils.c
@@ -0,0 +1,83 @@
+/* -*- c-basic-offset: 2; coding: utf-8 -*- */
+/*
+ Copyright (C) 2008 Kouhei Sutou <kou@cozmixng.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <errno.h>
+#include <glib/gstdio.h>
+
+#include "bench-utils.h"
+
+gboolean
+bench_utils_remove_path(const gchar *path, GError **error)
+{
+ if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
+ g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
+ "path doesn't exist: %s", path);
+ return FALSE;
+ }
+
+ if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
+ if (g_rmdir(path) == -1) {
+ g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno),
+ "can't remove directory: %s", path);
+ return FALSE;
+ }
+ } else {
+ if (g_unlink(path) == -1) {
+ g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno),
+ "can't remove path: %s", path);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+bench_utils_remove_path_recursive(const gchar *path, GError **error)
+{
+ if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
+ GDir *dir;
+ const gchar *name;
+
+ dir = g_dir_open(path, 0, error);
+ if (!dir)
+ return FALSE;
+
+ while ((name = g_dir_read_name(dir))) {
+ const gchar *full_path;
+
+ full_path = g_build_filename(path, name, NULL);
+ if (!bench_utils_remove_path_recursive(full_path, error))
+ return FALSE;
+ }
+
+ g_dir_close(dir);
+
+ return bench_utils_remove_path(path, error);
+ } else {
+ return bench_utils_remove_path(path, error);
+ }
+
+ return TRUE;
+}
+
+void
+bench_utils_remove_path_recursive_force(const gchar *path)
+{
+ bench_utils_remove_path_recursive(path, NULL);
+}
diff --git a/storage/mroonga/vendor/groonga/benchmark/lib/bench-utils.h b/storage/mroonga/vendor/groonga/benchmark/lib/bench-utils.h
new file mode 100644
index 00000000000..cebf5c2e6ac
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/lib/bench-utils.h
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 2; coding: utf-8 -*- */
+/*
+ Copyright (C) 2008 Kouhei Sutou <kou@cozmixng.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef __BENCH_UTILS_H__
+#define __BENCH_UTILS_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+gboolean bench_utils_remove_path (const gchar *path,
+ GError **error);
+gboolean bench_utils_remove_path_recursive (const gchar *path,
+ GError **error);
+void bench_utils_remove_path_recursive_force (const gchar *path);
+
+G_END_DECLS
+
+#endif /* __BENCH_UTILS_H__ */
diff --git a/storage/mroonga/vendor/groonga/benchmark/lib/benchmark.c b/storage/mroonga/vendor/groonga/benchmark/lib/benchmark.c
new file mode 100644
index 00000000000..3bcbacbb8bb
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/lib/benchmark.c
@@ -0,0 +1,35 @@
+/* -*- c-basic-offset: 2; coding: utf-8 -*- */
+/*
+ Copyright (C) 2008-2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "benchmark.h"
+
+void
+bench_init(gint *argc, gchar ***argv)
+{
+#if !GLIB_CHECK_VERSION(2, 36, 0)
+ if (!g_thread_supported())
+ g_thread_init(NULL);
+
+ g_type_init();
+#endif
+}
+
+void
+bench_quit(void)
+{
+}
diff --git a/storage/mroonga/vendor/groonga/benchmark/lib/benchmark.h b/storage/mroonga/vendor/groonga/benchmark/lib/benchmark.h
new file mode 100644
index 00000000000..87f4ed871ca
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/benchmark/lib/benchmark.h
@@ -0,0 +1,32 @@
+/* -*- c-basic-offset: 2; coding: utf-8 -*- */
+/*
+ Copyright (C) 2008 Kouhei Sutou <kou@cozmixng.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef __BENCHMARK_H__
+#define __BENCHMARK_H__
+
+#include "bench-reporter.h"
+#include "bench-utils.h"
+
+G_BEGIN_DECLS
+
+void bench_init(gint *argc, gchar ***argv);
+void bench_quit(void);
+
+G_END_DECLS
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/bindings/php/config.m4 b/storage/mroonga/vendor/groonga/bindings/php/config.m4
new file mode 100644
index 00000000000..c656c9d48bf
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/bindings/php/config.m4
@@ -0,0 +1,66 @@
+
+PHP_ARG_WITH(groonga, whether groonga is available,[ --with-groonga[=DIR] With groonga support])
+
+
+if test "$PHP_GROONGA" != "no"; then
+
+
+ if test -r "$PHP_GROONGA/include/groonga.h"; then
+ PHP_GROONGA_DIR="$PHP_GROONGA"
+ PHP_ADD_INCLUDE($PHP_GROONGA_DIR/include)
+ elif test -r "$PHP_GROONGA/include/groonga/groonga.h"; then
+ PHP_GROONGA_DIR="$PHP_GROONGA"
+ PHP_ADD_INCLUDE($PHP_GROONGA_DIR/include/groonga)
+ else
+ AC_MSG_CHECKING(for groonga in default path)
+ for i in /usr /usr/local; do
+ if test -r "$i/include/groonga/groonga.h"; then
+ PHP_GROONGA_DIR=$i
+ AC_MSG_RESULT(found in $i)
+ break
+ fi
+ done
+ if test "x" = "x$PHP_GROONGA_DIR"; then
+ AC_MSG_ERROR(not found)
+ fi
+ PHP_ADD_INCLUDE($PHP_GROONGA_DIR/include)
+ fi
+
+ export OLD_CPPFLAGS="$CPPFLAGS"
+ export CPPFLAGS="$CPPFLAGS $INCLUDES -DHAVE_GROONGA"
+ AC_CHECK_HEADER([groonga.h], [], AC_MSG_ERROR('groonga.h' header not found))
+ PHP_SUBST(GROONGA_SHARED_LIBADD)
+
+
+ PHP_CHECK_LIBRARY(groonga, grn_init,
+ [
+ PHP_ADD_LIBRARY_WITH_PATH(groonga, $PHP_GROONGA_DIR/lib, GROONGA_SHARED_LIBADD)
+ ],[
+ AC_MSG_ERROR([wrong groonga lib version or lib not found])
+ ],[
+ -L$PHP_GROONGA_DIR/lib
+ ])
+ export CPPFLAGS="$OLD_CPPFLAGS"
+
+ export OLD_CPPFLAGS="$CPPFLAGS"
+ export CPPFLAGS="$CPPFLAGS $INCLUDES -DHAVE_GROONGA"
+
+ AC_MSG_CHECKING(PHP version)
+ AC_TRY_COMPILE([#include <php_version.h>], [
+#if PHP_VERSION_ID < 40000
+#error this extension requires at least PHP version 4.0.0
+#endif
+],
+[AC_MSG_RESULT(ok)],
+[AC_MSG_ERROR([need at least PHP 4.0.0])])
+
+ export CPPFLAGS="$OLD_CPPFLAGS"
+
+
+ PHP_SUBST(GROONGA_SHARED_LIBADD)
+ AC_DEFINE(HAVE_GROONGA, 1, [ ])
+
+ PHP_NEW_EXTENSION(groonga, groonga.c , $ext_shared)
+
+fi
+
diff --git a/storage/mroonga/vendor/groonga/bindings/php/config.w32 b/storage/mroonga/vendor/groonga/bindings/php/config.w32
new file mode 100644
index 00000000000..d0fab53beee
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/bindings/php/config.w32
@@ -0,0 +1,6 @@
+ARG_WITH('groonga', 'Groonga', 'no');
+
+if (PHP_GROONGA == "yes") {
+ EXTENSION("groonga", "groonga.c");
+ AC_DEFINE("HAVE_GROONGA", 1, "groonga support");
+}
diff --git a/storage/mroonga/vendor/groonga/bindings/php/groonga.c b/storage/mroonga/vendor/groonga/bindings/php/groonga.c
new file mode 100644
index 00000000000..b92cc1a2385
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/bindings/php/groonga.c
@@ -0,0 +1,218 @@
+
+#include "php_groonga.h"
+
+#if HAVE_GROONGA
+
+int le_grn_ctx;
+void grn_ctx_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ grn_ctx *ctx = (grn_ctx *)(rsrc->ptr);
+ grn_ctx_close(ctx);
+}
+
+zend_function_entry groonga_functions[] = {
+ PHP_FE(grn_ctx_init , grn_ctx_init_arg_info)
+ PHP_FE(grn_ctx_close , grn_ctx_close_arg_info)
+ PHP_FE(grn_ctx_connect , grn_ctx_connect_arg_info)
+ PHP_FE(grn_ctx_send , grn_ctx_send_arg_info)
+ PHP_FE(grn_ctx_recv , grn_ctx_recv_arg_info)
+ { NULL, NULL, NULL }
+};
+
+
+zend_module_entry groonga_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "groonga",
+ groonga_functions,
+ PHP_MINIT(groonga),
+ PHP_MSHUTDOWN(groonga),
+ PHP_RINIT(groonga),
+ PHP_RSHUTDOWN(groonga),
+ PHP_MINFO(groonga),
+ "0.1",
+ STANDARD_MODULE_PROPERTIES
+};
+
+#ifdef COMPILE_DL_GROONGA
+ZEND_GET_MODULE(groonga)
+#endif
+
+
+PHP_MINIT_FUNCTION(groonga)
+{
+ REGISTER_LONG_CONSTANT("GRN_CTX_USE_QL", GRN_CTX_USE_QL, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_CTX_BATCH_MODE", GRN_CTX_BATCH_MODE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_ENC_DEFAULT", GRN_ENC_DEFAULT, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_ENC_NONE", GRN_ENC_NONE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_ENC_EUC_JP", GRN_ENC_EUC_JP, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_ENC_UTF8", GRN_ENC_UTF8, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_ENC_SJIS", GRN_ENC_SJIS, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_ENC_LATIN1", GRN_ENC_LATIN1, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_ENC_KOI8R", GRN_ENC_KOI8R, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_CTX_MORE", GRN_CTX_MORE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_CTX_TAIL", GRN_CTX_TAIL, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_CTX_HEAD", GRN_CTX_HEAD, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_CTX_QUIET", GRN_CTX_QUIET, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_CTX_QUIT", GRN_CTX_QUIT, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("GRN_CTX_FIN", GRN_CTX_FIN, CONST_PERSISTENT | CONST_CS);
+ le_grn_ctx = zend_register_list_destructors_ex(
+ grn_ctx_dtor, NULL, "grn_ctx", module_number);
+
+ grn_init();
+
+ return SUCCESS;
+}
+
+
+PHP_MSHUTDOWN_FUNCTION(groonga)
+{
+ grn_fin();
+ return SUCCESS;
+}
+
+
+PHP_RINIT_FUNCTION(groonga)
+{
+ return SUCCESS;
+}
+
+
+PHP_RSHUTDOWN_FUNCTION(groonga)
+{
+ return SUCCESS;
+}
+
+
+PHP_MINFO_FUNCTION(groonga)
+{
+ php_info_print_box_start(0);
+ php_printf("<p>Groonga</p>\n");
+ php_printf("<p>Version 0.2 (ctx)</p>\n");
+ php_printf("<p><b>Authors:</b></p>\n");
+ php_printf("<p>yu &lt;yu@irx.jp&gt; (lead)</p>\n");
+ php_info_print_box_end();
+}
+
+
+PHP_FUNCTION(grn_ctx_init)
+{
+ grn_ctx *ctx = (grn_ctx *) malloc(sizeof(grn_ctx));
+ long res_id = -1;
+ long flags = 0;
+ grn_rc rc;
+
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags) == FAILURE) {
+ return;
+ }
+
+ if ((rc = grn_ctx_init(ctx, flags)) != GRN_SUCCESS) {
+ RETURN_FALSE;
+ }
+
+ res_id = ZEND_REGISTER_RESOURCE(return_value, ctx, le_grn_ctx);
+ RETURN_RESOURCE(res_id);
+}
+
+
+PHP_FUNCTION(grn_ctx_close)
+{
+ zval *res = NULL;
+ int res_id = -1;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &res) == FAILURE) {
+ return;
+ }
+
+ zend_list_delete(Z_LVAL_P(res)); // call grn_ctx_dtor
+ RETURN_TRUE;
+}
+
+
+PHP_FUNCTION(grn_ctx_connect)
+{
+ zval *res = NULL;
+ int res_id = -1;
+
+ grn_rc rc;
+ grn_ctx *ctx;
+ char *host = "localhost";
+ int host_len = 0;
+ long port = 10041;
+ long flags = 0;
+
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|ll", &res, &host, &host_len, &port, &flags) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ctx, grn_ctx *, &res, res_id, "grn_ctx", le_grn_ctx);
+
+ if ((rc = grn_ctx_connect(ctx, host, port, flags)) != GRN_SUCCESS) {
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+
+
+PHP_FUNCTION(grn_ctx_send)
+{
+ zval *res = NULL;
+ int res_id = -1;
+
+ grn_ctx *ctx;
+ char *query = NULL;
+ unsigned int query_len, qid;
+ long flags = 0;
+
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l", &res, &query, &query_len, &flags) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ctx, grn_ctx *, &res, res_id, "grn_ctx", le_grn_ctx);
+ qid = grn_ctx_send(ctx, query, query_len, flags);
+ if (ctx->rc != GRN_SUCCESS) {
+ RETURN_FALSE;
+ }
+
+ RETURN_LONG(qid)
+
+}
+
+
+PHP_FUNCTION(grn_ctx_recv)
+{
+ zval *res,*ret = NULL;
+ int res_id = -1;
+ grn_ctx *ctx;
+
+ char *str;
+ int flags;
+ unsigned int str_len, qid;
+
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &res) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ctx, grn_ctx *, &res, res_id, "grn_ctx", le_grn_ctx);
+
+ qid = grn_ctx_recv(ctx, &str, &str_len, &flags);
+
+ if (ctx->rc != GRN_SUCCESS) {
+ RETURN_FALSE;
+ }
+
+ MAKE_STD_ZVAL(ret);
+ array_init(ret);
+ array_init(return_value);
+
+ add_next_index_long(ret, flags);
+ add_next_index_stringl(ret, str, str_len, 1);
+
+ add_index_zval(return_value, qid, ret);
+}
+
+#endif /* HAVE_GROONGA */
diff --git a/storage/mroonga/vendor/groonga/bindings/php/php_groonga.h b/storage/mroonga/vendor/groonga/bindings/php/php_groonga.h
new file mode 100644
index 00000000000..3af4427fbdb
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/bindings/php/php_groonga.h
@@ -0,0 +1,125 @@
+/*
+ +----------------------------------------------------------------------+
+ | unknown license: |
+ +----------------------------------------------------------------------+
+ | Authors: yu <yu@irx.jp> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_GROONGA_H
+#define PHP_GROONGA_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <php.h>
+
+#ifdef HAVE_GROONGA
+
+#include <php_ini.h>
+#include <SAPI.h>
+#include <ext/standard/info.h>
+#include <Zend/zend_extensions.h>
+#ifdef __cplusplus
+} // extern "C"
+#endif
+#include <groonga.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern zend_module_entry groonga_module_entry;
+#define phpext_groonga_ptr &groonga_module_entry
+
+#ifdef PHP_WIN32
+#define PHP_GROONGA_API __declspec(dllexport)
+#else
+#define PHP_GROONGA_API
+#endif
+
+PHP_MINIT_FUNCTION(groonga);
+PHP_MSHUTDOWN_FUNCTION(groonga);
+PHP_RINIT_FUNCTION(groonga);
+PHP_RSHUTDOWN_FUNCTION(groonga);
+PHP_MINFO_FUNCTION(groonga);
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+#define FREE_RESOURCE(resource) zend_list_delete(Z_LVAL_P(resource))
+
+#define PROP_GET_LONG(name) Z_LVAL_P(zend_read_property(_this_ce, _this_zval, #name, strlen(#name), 1 TSRMLS_CC))
+#define PROP_SET_LONG(name, l) zend_update_property_long(_this_ce, _this_zval, #name, strlen(#name), l TSRMLS_CC)
+
+#define PROP_GET_DOUBLE(name) Z_DVAL_P(zend_read_property(_this_ce, _this_zval, #name, strlen(#name), 1 TSRMLS_CC))
+#define PROP_SET_DOUBLE(name, d) zend_update_property_double(_this_ce, _this_zval, #name, strlen(#name), d TSRMLS_CC)
+
+#define PROP_GET_STRING(name) Z_STRVAL_P(zend_read_property(_this_ce, _this_zval, #name, strlen(#name), 1 TSRMLS_CC))
+#define PROP_GET_STRLEN(name) Z_STRLEN_P(zend_read_property(_this_ce, _this_zval, #name, strlen(#name), 1 TSRMLS_CC))
+#define PROP_SET_STRING(name, s) zend_update_property_string(_this_ce, _this_zval, #name, strlen(#name), s TSRMLS_CC)
+#define PROP_SET_STRINGL(name, s, l) zend_update_property_stringl(_this_ce, _this_zval, #name, strlen(#name), s, l TSRMLS_CC)
+
+
+PHP_FUNCTION(grn_ctx_init);
+#if (PHP_MAJOR_VERSION >= 5)
+ZEND_BEGIN_ARG_INFO_EX(grn_ctx_init_arg_info, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 1)
+ ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO()
+#else /* PHP 4.x */
+#define grn_ctx_init_arg_info NULL
+#endif
+
+PHP_FUNCTION(grn_ctx_close);
+#if (PHP_MAJOR_VERSION >= 5)
+ZEND_BEGIN_ARG_INFO_EX(grn_ctx_close_arg_info, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 1)
+ ZEND_ARG_INFO(0, res)
+ZEND_END_ARG_INFO()
+#else /* PHP 4.x */
+#define grn_ctx_close_arg_info NULL
+#endif
+
+PHP_FUNCTION(grn_ctx_connect);
+#if (PHP_MAJOR_VERSION >= 5)
+ZEND_BEGIN_ARG_INFO_EX(grn_ctx_connect_arg_info, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 4)
+ ZEND_ARG_INFO(0, res)
+ ZEND_ARG_INFO(0, host)
+ ZEND_ARG_INFO(0, port)
+ ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO()
+#else /* PHP 4.x */
+#define grn_ctx_connect_arg_info NULL
+#endif
+
+PHP_FUNCTION(grn_ctx_send);
+#if (PHP_MAJOR_VERSION >= 5)
+ZEND_BEGIN_ARG_INFO_EX(grn_ctx_send_arg_info, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 3)
+ ZEND_ARG_INFO(0, res)
+ ZEND_ARG_INFO(0, query)
+ ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO()
+#else /* PHP 4.x */
+#define grn_ctx_send_arg_info NULL
+#endif
+
+PHP_FUNCTION(grn_ctx_recv);
+#if (PHP_MAJOR_VERSION >= 5)
+ZEND_BEGIN_ARG_INFO_EX(grn_ctx_recv_arg_info, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 1)
+ ZEND_ARG_INFO(0, res)
+ZEND_END_ARG_INFO()
+#else /* PHP 4.x */
+#define grn_ctx_recv_arg_info NULL
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* PHP_HAVE_GROONGA */
+
+#endif /* PHP_GROONGA_H */
diff --git a/storage/mroonga/vendor/groonga/bindings/php/tests/001.phpt b/storage/mroonga/vendor/groonga/bindings/php/tests/001.phpt
new file mode 100644
index 00000000000..d215b3eab79
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/bindings/php/tests/001.phpt
@@ -0,0 +1,29 @@
+--TEST--
+groonga connection
+--DESCRIPTION--
+required: variables_order includes "E"
+--SKIPIF--
+<?php
+if (!extension_loaded("groonga")) print "skip";
+if (!getenv('GROONGA_TEST_HOST')) print "skip";
+?>
+--FILE--
+<?php
+$host = getenv('GROONGA_TEST_HOST');
+$port = getenv('GROONGA_TEST_PORT') ? getenv('GROONGA_TEST_PORT') : 10041;
+
+$grn = grn_ctx_init();
+grn_ctx_connect($grn, $host, $port) or die("cannot connect groong server ({$host}:{$port})");
+grn_ctx_send($grn, 'table_list');
+var_dump(grn_ctx_recv($grn));
+grn_ctx_close($grn);
+--EXPECTF--
+array(1) {
+ [0]=>
+ array(2) {
+ [0]=>
+ int(0)
+ [1]=>
+ string(%d) "%s"
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/bindings/python/ql/groongaql.c b/storage/mroonga/vendor/groonga/bindings/python/ql/groongaql.c
new file mode 100644
index 00000000000..2eae9dacc68
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/bindings/python/ql/groongaql.c
@@ -0,0 +1,364 @@
+/* Copyright(C) 2007 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include <Python.h>
+#include <groonga.h>
+
+/* TODO: use exception */
+
+typedef struct {
+ PyObject_HEAD
+ grn_ctx ctx;
+ int closed;
+} groongaql_ContextObject;
+
+static PyTypeObject groongaql_ContextType;
+
+/* Object methods */
+
+static PyObject *
+groongaql_ContextObject_new(PyTypeObject *type, PyObject *args, PyObject *keywds)
+{
+ grn_rc rc;
+ int flags;
+ grn_encoding encoding;
+ groongaql_ContextObject *self;
+
+ static char *kwlist[] = {"flags", "encoding", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "ii", kwlist,
+ &flags, &encoding)) {
+ return NULL;
+ }
+ if (!(self = (groongaql_ContextObject *)type->tp_alloc(type, 0))) {
+ return NULL;
+ }
+ Py_BEGIN_ALLOW_THREADS
+ rc = grn_ctx_init(&self->ctx, flags);
+ GRN_CTX_SET_ENCODING(&self->ctx, encoding);
+ Py_END_ALLOW_THREADS
+ if (rc) {
+ self->ob_type->tp_free(self);
+ return NULL;
+ }
+ self->closed = 0;
+ return (PyObject *)self;
+}
+
+static void
+groongaql_ContextObject_dealloc(groongaql_ContextObject *self)
+{
+ if (!self->closed) {
+ Py_BEGIN_ALLOW_THREADS
+ grn_ctx_fin(&self->ctx);
+ Py_END_ALLOW_THREADS
+ }
+ self->ob_type->tp_free(self);
+}
+
+/* Class methods */
+
+/* instance methods */
+
+static PyObject *
+groongaql_ContextClass_ql_connect(groongaql_ContextObject *self, PyObject *args, PyObject *keywds)
+{
+ grn_rc rc;
+ int port, flags;
+ const char *host;
+ static char *kwlist[] = {"host", "port", "flags", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "sii", kwlist,
+ &host, &port, &flags)) {
+ return NULL;
+ }
+ if (self->closed) { return NULL; }
+ Py_BEGIN_ALLOW_THREADS
+ rc = grn_ctx_connect(&self->ctx, host, port, flags);
+ Py_END_ALLOW_THREADS
+ return Py_BuildValue("i", rc);
+}
+
+static PyObject *
+groongaql_Context_ql_send(groongaql_ContextObject *self, PyObject *args, PyObject *keywds)
+{
+ grn_rc rc;
+ char *str;
+ int str_len, flags;
+ static char *kwlist[] = {"str", "flags", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, keywds, "s#i", kwlist,
+ &str,
+ &str_len,
+ &flags)) {
+ return NULL;
+ }
+ if (self->closed) { return NULL; }
+ Py_BEGIN_ALLOW_THREADS
+ rc = grn_ctx_send(&self->ctx, str, str_len, flags);
+ Py_END_ALLOW_THREADS
+ return Py_BuildValue("i", rc);
+}
+
+static PyObject *
+groongaql_Context_ql_recv(groongaql_ContextObject *self)
+{
+ grn_rc rc;
+ int flags;
+ char *str;
+ unsigned int str_len;
+
+ if (self->closed) { return NULL; }
+ Py_BEGIN_ALLOW_THREADS
+ rc = grn_ctx_recv(&self->ctx, &str, &str_len, &flags);
+ Py_END_ALLOW_THREADS
+ return Py_BuildValue("(is#i)", rc, str, str_len, flags);
+}
+
+static PyObject *
+groongaql_Context_fin(groongaql_ContextObject *self)
+{
+ grn_rc rc;
+
+ if (self->closed) { return NULL; }
+ Py_BEGIN_ALLOW_THREADS
+ rc = grn_ctx_fin(&self->ctx);
+ Py_END_ALLOW_THREADS
+ if (!rc) {
+ self->closed = 1;
+ }
+ return Py_BuildValue("i", rc);
+}
+
+static PyObject *
+groongaql_Context_ql_info_get(groongaql_ContextObject *self)
+{
+ grn_rc rc;
+ grn_ctx_info info;
+
+ if (self->closed) { return NULL; }
+ rc = grn_ctx_info_get(&self->ctx, &info);
+ /* TODO: handling unsigned int properlly */
+ /* TODO: get outbuf */
+ return Py_BuildValue("{s:i,s:i,s:i}",
+ "rc", rc,
+ "com_status", info.com_status,
+ /* "outbuf", info.outbuf, */
+ "stat", info.stat
+ );
+}
+
+/* methods of classes */
+
+static PyMethodDef groongaql_Context_methods[] = {
+ {"ql_connect", (PyCFunction)groongaql_ContextClass_ql_connect,
+ METH_VARARGS | METH_KEYWORDS,
+ "Create a remote groonga context."},
+ {"ql_send", (PyCFunction)groongaql_Context_ql_send,
+ METH_VARARGS | METH_KEYWORDS,
+ "Send message to context."},
+ {"ql_recv", (PyCFunction)groongaql_Context_ql_recv,
+ METH_NOARGS,
+ "Receive message from context."},
+ {"fin", (PyCFunction)groongaql_Context_fin,
+ METH_NOARGS,
+ "Release groonga context."},
+ {"ql_info_get", (PyCFunction)groongaql_Context_ql_info_get,
+ METH_NOARGS,
+ "Get QL context information."},
+ {NULL, NULL, 0, NULL}
+};
+
+static PyMethodDef module_methods[] = {
+ {NULL, NULL, 0, NULL}
+};
+
+/* type objects */
+
+static PyTypeObject groongaql_ContextType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "groongaql.Context", /* tp_name */
+ sizeof(groongaql_ContextObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)groongaql_ContextObject_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ "groonga Context objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ groongaql_Context_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ groongaql_ContextObject_new, /* tp_new */
+};
+
+/* consts */
+
+typedef struct _ConstPair {
+ const char *name;
+ int value;
+} ConstPair;
+
+static ConstPair consts[] = {
+ /* grn_rc */
+ {"SUCCESS", GRN_SUCCESS},
+ {"END_OF_DATA", GRN_END_OF_DATA},
+ {"UNKNOWN_ERROR", GRN_UNKNOWN_ERROR},
+ {"OPERATION_NOT_PERMITTED", GRN_OPERATION_NOT_PERMITTED},
+ {"NO_SUCH_FILE_OR_DIRECTORY", GRN_NO_SUCH_FILE_OR_DIRECTORY},
+ {"NO_SUCH_PROCESS", GRN_NO_SUCH_PROCESS},
+ {"INTERRUPTED_FUNCTION_CALL", GRN_INTERRUPTED_FUNCTION_CALL},
+ {"INPUT_OUTPUT_ERROR", GRN_INPUT_OUTPUT_ERROR},
+ {"NO_SUCH_DEVICE_OR_ADDRESS", GRN_NO_SUCH_DEVICE_OR_ADDRESS},
+ {"ARG_LIST_TOO_LONG", GRN_ARG_LIST_TOO_LONG},
+ {"EXEC_FORMAT_ERROR", GRN_EXEC_FORMAT_ERROR},
+ {"BAD_FILE_DESCRIPTOR", GRN_BAD_FILE_DESCRIPTOR},
+ {"NO_CHILD_PROCESSES", GRN_NO_CHILD_PROCESSES},
+ {"RESOURCE_TEMPORARILY_UNAVAILABLE", GRN_RESOURCE_TEMPORARILY_UNAVAILABLE},
+ {"NOT_ENOUGH_SPACE", GRN_NOT_ENOUGH_SPACE},
+ {"PERMISSION_DENIED", GRN_PERMISSION_DENIED},
+ {"BAD_ADDRESS", GRN_BAD_ADDRESS},
+ {"RESOURCE_BUSY", GRN_RESOURCE_BUSY},
+ {"FILE_EXISTS", GRN_FILE_EXISTS},
+ {"IMPROPER_LINK", GRN_IMPROPER_LINK},
+ {"NO_SUCH_DEVICE", GRN_NO_SUCH_DEVICE},
+ {"NOT_A_DIRECTORY", GRN_NOT_A_DIRECTORY},
+ {"IS_A_DIRECTORY", GRN_IS_A_DIRECTORY},
+ {"INVALID_ARGUMENT", GRN_INVALID_ARGUMENT},
+ {"TOO_MANY_OPEN_FILES_IN_SYSTEM", GRN_TOO_MANY_OPEN_FILES_IN_SYSTEM},
+ {"TOO_MANY_OPEN_FILES", GRN_TOO_MANY_OPEN_FILES},
+ {"INAPPROPRIATE_I_O_CONTROL_OPERATION", GRN_INAPPROPRIATE_I_O_CONTROL_OPERATION},
+ {"FILE_TOO_LARGE", GRN_FILE_TOO_LARGE},
+ {"NO_SPACE_LEFT_ON_DEVICE", GRN_NO_SPACE_LEFT_ON_DEVICE},
+ {"INVALID_SEEK", GRN_INVALID_SEEK},
+ {"READ_ONLY_FILE_SYSTEM", GRN_READ_ONLY_FILE_SYSTEM},
+ {"TOO_MANY_LINKS", GRN_TOO_MANY_LINKS},
+ {"BROKEN_PIPE", GRN_BROKEN_PIPE},
+ {"DOMAIN_ERROR", GRN_DOMAIN_ERROR},
+ {"RESULT_TOO_LARGE", GRN_RESULT_TOO_LARGE},
+ {"RESOURCE_DEADLOCK_AVOIDED", GRN_RESOURCE_DEADLOCK_AVOIDED},
+ {"NO_MEMORY_AVAILABLE", GRN_NO_MEMORY_AVAILABLE},
+ {"FILENAME_TOO_LONG", GRN_FILENAME_TOO_LONG},
+ {"NO_LOCKS_AVAILABLE", GRN_NO_LOCKS_AVAILABLE},
+ {"FUNCTION_NOT_IMPLEMENTED", GRN_FUNCTION_NOT_IMPLEMENTED},
+ {"DIRECTORY_NOT_EMPTY", GRN_DIRECTORY_NOT_EMPTY},
+ {"ILLEGAL_BYTE_SEQUENCE", GRN_ILLEGAL_BYTE_SEQUENCE},
+ {"SOCKET_NOT_INITIALIZED", GRN_SOCKET_NOT_INITIALIZED},
+ {"OPERATION_WOULD_BLOCK", GRN_OPERATION_WOULD_BLOCK},
+ {"ADDRESS_IS_NOT_AVAILABLE", GRN_ADDRESS_IS_NOT_AVAILABLE},
+ {"NETWORK_IS_DOWN", GRN_NETWORK_IS_DOWN},
+ {"NO_BUFFER", GRN_NO_BUFFER},
+ {"SOCKET_IS_ALREADY_CONNECTED", GRN_SOCKET_IS_ALREADY_CONNECTED},
+ {"SOCKET_IS_NOT_CONNECTED", GRN_SOCKET_IS_NOT_CONNECTED},
+ {"SOCKET_IS_ALREADY_SHUTDOWNED", GRN_SOCKET_IS_ALREADY_SHUTDOWNED},
+ {"OPERATION_TIMEOUT", GRN_OPERATION_TIMEOUT},
+ {"CONNECTION_REFUSED", GRN_CONNECTION_REFUSED},
+ {"RANGE_ERROR", GRN_RANGE_ERROR},
+ {"TOKENIZER_ERROR", GRN_TOKENIZER_ERROR},
+ {"FILE_CORRUPT", GRN_FILE_CORRUPT},
+ {"INVALID_FORMAT", GRN_INVALID_FORMAT},
+ {"OBJECT_CORRUPT", GRN_OBJECT_CORRUPT},
+ {"TOO_MANY_SYMBOLIC_LINKS", GRN_TOO_MANY_SYMBOLIC_LINKS},
+ {"NOT_SOCKET", GRN_NOT_SOCKET},
+ {"OPERATION_NOT_SUPPORTED", GRN_OPERATION_NOT_SUPPORTED},
+ {"ADDRESS_IS_IN_USE", GRN_ADDRESS_IS_IN_USE},
+ {"ZLIB_ERROR", GRN_ZLIB_ERROR},
+ {"LZO_ERROR", GRN_LZO_ERROR},
+ /* grn_encoding */
+ {"ENC_DEFAULT", GRN_ENC_DEFAULT},
+ {"ENC_NONE", GRN_ENC_NONE},
+ {"ENC_EUC_JP", GRN_ENC_EUC_JP},
+ {"ENC_UTF8", GRN_ENC_UTF8},
+ {"ENC_SJIS", GRN_ENC_SJIS},
+ {"ENC_LATIN1", GRN_ENC_LATIN1},
+ {"ENC_KOI8R", GRN_ENC_KOI8R},
+ /* grn_ctx flags */
+ {"CTX_USE_QL", GRN_CTX_USE_QL},
+ {"CTX_BATCH_MODE", GRN_CTX_BATCH_MODE},
+ {"CTX_MORE", GRN_CTX_MORE},
+ {"CTX_TAIL", GRN_CTX_TAIL},
+ {"CTX_HEAD", GRN_CTX_HEAD},
+ {"CTX_QUIET", GRN_CTX_QUIET},
+ {"CTX_QUIT", GRN_CTX_QUIT},
+ {"CTX_FIN", GRN_CTX_FIN},
+ /* end */
+ {NULL, 0}
+};
+
+#ifndef PyMODINIT_FUNC
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+initgroongaql(void)
+{
+ unsigned int i;
+ PyObject *m, *dict;
+
+ if (!(m = Py_InitModule3("groongaql", module_methods,
+ "groonga ql module."))) {
+ goto exit;
+ }
+ grn_init();
+
+ /* register classes */
+
+ if (PyType_Ready(&groongaql_ContextType) < 0) { goto exit; }
+ Py_INCREF(&groongaql_ContextType);
+ PyModule_AddObject(m, "Context", (PyObject *)&groongaql_ContextType);
+
+ /* register consts */
+
+ if (!(dict = PyModule_GetDict(m))) { goto exit; }
+
+ for (i = 0; consts[i].name; i++) {
+ PyObject *v;
+ if (!(v = PyInt_FromLong(consts[i].value))) {
+ goto exit;
+ }
+ PyDict_SetItemString(dict, consts[i].name, v);
+ Py_DECREF(v);
+ }
+ if (Py_AtExit((void (*)(void))grn_fin)) { goto exit; }
+exit:
+ if (PyErr_Occurred()) {
+ PyErr_SetString(PyExc_ImportError, "groongaql: init failed");
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/bindings/python/ql/setup.py b/storage/mroonga/vendor/groonga/bindings/python/ql/setup.py
new file mode 100755
index 00000000000..3b8005540cf
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/bindings/python/ql/setup.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+from distutils.core import setup, Extension
+
+#cflags = ['-Wall', '-fPIC', '-g', '-O0']
+cflags = ['-Wall', '-fPIC', '-O3']
+
+ext = Extension('groongaql',
+ libraries = ['groonga'],
+ sources = ['groongaql.c'],
+ extra_compile_args = cflags)
+
+setup(name = 'groongaql',
+ version = '1.0',
+ description = 'python GQTP',
+ long_description = '''
+ This is a GQTP Python API package.
+ ''',
+ license='GNU LESSER GENERAL PUBLIC LICENSE',
+ author = 'Brazil',
+ author_email = 'groonga at razil.jp',
+ ext_modules = [ext]
+ )
diff --git a/storage/mroonga/vendor/groonga/build/Makefile.am b/storage/mroonga/vendor/groonga/build/Makefile.am
new file mode 100644
index 00000000000..506a11dc3b0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/build/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = \
+ cmake_modules
diff --git a/storage/mroonga/vendor/groonga/build/ac_macros/check_functions.m4 b/storage/mroonga/vendor/groonga/build/ac_macros/check_functions.m4
new file mode 100644
index 00000000000..5046e8ce6ab
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/build/ac_macros/check_functions.m4
@@ -0,0 +1,13 @@
+# -*- autoconf -*-
+
+AC_CHECK_FUNCS(_strnicmp)
+AC_CHECK_FUNCS(_strtoui64)
+AC_CHECK_FUNCS(close)
+AC_CHECK_FUNCS(gmtime_r)
+AC_CHECK_FUNCS(localtime_r)
+AC_CHECK_FUNCS(mkostemp)
+AC_CHECK_FUNCS(open)
+AC_CHECK_FUNCS(read)
+AC_CHECK_FUNCS(strncasecmp)
+AC_CHECK_FUNCS(strtoull)
+AC_CHECK_FUNCS(write)
diff --git a/storage/mroonga/vendor/groonga/build/ac_macros/check_headers.m4 b/storage/mroonga/vendor/groonga/build/ac_macros/check_headers.m4
new file mode 100644
index 00000000000..676b41f7e59
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/build/ac_macros/check_headers.m4
@@ -0,0 +1,24 @@
+# -*- autoconf -*-
+
+AC_CHECK_HEADERS(dlfcn.h)
+AC_CHECK_HEADERS(errno.h)
+AC_CHECK_HEADERS(execinfo.h)
+AC_CHECK_HEADERS(inttypes.h)
+AC_CHECK_HEADERS(netdb.h)
+AC_CHECK_HEADERS(netinet/in.h)
+AC_CHECK_HEADERS(netinet/tcp.h)
+AC_CHECK_HEADERS(signal.h)
+AC_CHECK_HEADERS(stdint.h)
+AC_CHECK_HEADERS(stdlib.h)
+AC_CHECK_HEADERS(sys/mman.h)
+AC_CHECK_HEADERS(sys/param.h)
+AC_CHECK_HEADERS(sys/resource.h)
+AC_CHECK_HEADERS(sys/socket.h)
+AC_CHECK_HEADERS(sys/sysctl.h)
+AC_CHECK_HEADERS(sys/time.h)
+AC_CHECK_HEADERS(sys/timeb.h)
+AC_CHECK_HEADERS(sys/types.h)
+AC_CHECK_HEADERS(sys/wait.h)
+AC_CHECK_HEADERS(time.h)
+AC_CHECK_HEADERS(ucontext.h)
+AC_CHECK_HEADERS(unistd.h)
diff --git a/storage/mroonga/vendor/groonga/build/cmake_modules/Makefile.am b/storage/mroonga/vendor/groonga/build/cmake_modules/Makefile.am
new file mode 100644
index 00000000000..83fb0f0c1b4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/build/cmake_modules/Makefile.am
@@ -0,0 +1,2 @@
+EXTRA_DIST = \
+ ReadFileList.cmake
diff --git a/storage/mroonga/vendor/groonga/build/cmake_modules/ReadFileList.cmake b/storage/mroonga/vendor/groonga/build/cmake_modules/ReadFileList.cmake
new file mode 100644
index 00000000000..018587991d8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/build/cmake_modules/ReadFileList.cmake
@@ -0,0 +1,27 @@
+# Copyright(C) 2012 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+macro(read_file_list file_name output_variable)
+ file(READ ${file_name} ${output_variable})
+ # Remove variable declaration at the first line:
+ # "libgroonga_la_SOURCES = \" -> ""
+ string(REGEX REPLACE "^.*=[ \t]*\\\\" ""
+ ${output_variable} "${${output_variable}}")
+ # Remove white spaces: " com.c \\\n com.h \\\n" -> "com.c\\com.h"
+ string(REGEX REPLACE "[ \t\n]" "" ${output_variable} "${${output_variable}}")
+ # Convert string to list: "com.c\\com.h" -> "com.c;com.h"
+ # NOTE: List in CMake is ";" separated string.
+ string(REGEX REPLACE "\\\\" ";" ${output_variable} "${${output_variable}}")
+endmacro()
diff --git a/storage/mroonga/vendor/groonga/build/makefiles/LC_MESSAGES.am b/storage/mroonga/vendor/groonga/build/makefiles/LC_MESSAGES.am
new file mode 100644
index 00000000000..acfc3da238e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/build/makefiles/LC_MESSAGES.am
@@ -0,0 +1,5 @@
+BUILT_SOURCES =
+EXTRA_DIST =
+SUFFIXES =
+
+include $(top_srcdir)/build/makefiles/gettext.am
diff --git a/storage/mroonga/vendor/groonga/build/makefiles/gettext.am b/storage/mroonga/vendor/groonga/build/makefiles/gettext.am
new file mode 100644
index 00000000000..9706b485dd1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/build/makefiles/gettext.am
@@ -0,0 +1,73 @@
+include $(top_srcdir)/doc/files.am
+include $(top_srcdir)/build/makefiles/sphinx-build.am
+
+EXTRA_DIST += \
+ $(po_files)
+
+if DOCUMENT_AVAILABLE
+EXTRA_DIST += \
+ $(mo_files)
+endif
+
+if DOCUMENT_BUILDABLE
+BUILT_SOURCES += \
+ pot-build-stamp \
+ edit-po-build-stamp \
+ $(mo_files)
+endif
+
+SUFFIXES += .pot .po .mo .edit
+
+.PHONY: gettext update build
+
+.pot.edit:
+ if test -f $*.po; then \
+ msgmerge \
+ --quiet \
+ --sort-by-file \
+ --output-file=$@.tmp \
+ $*.po \
+ $<; \
+ else \
+ msginit \
+ --input=$< \
+ --output-file=$@.tmp \
+ --locale=$(LOCALE) \
+ --no-translator; \
+ fi
+ (echo "# -*- po -*-"; \
+ GREP_OPTIONS= grep -v '^# -\*- po -\*-' $@.tmp | \
+ GREP_OPTIONS= grep -v '^"POT-Creation-Date:') > $@
+ rm $@.tmp
+
+.edit.po:
+ msgcat --no-location --output $@ $<
+
+.po.mo:
+ msgfmt -o $@ $<
+
+if DOCUMENT_BUILDABLE
+update: pot-build-stamp edit-po-build-stamp
+build: update $(mo_files)
+else
+update:
+build:
+endif
+
+html: build
+man: build
+pdf: build
+
+gettext:
+ rm *.pot || true
+ $(SPHINX_BUILD_COMMAND) -d doctrees -b gettext $(ALLSPHINXOPTS) .
+ xgettext --language Python --output conf.pot \
+ $(top_srcdir)/doc/source/conf.py
+
+pot-build-stamp: $(absolute_source_files)
+ $(MAKE) gettext
+ @touch $@
+
+edit-po-build-stamp: $(absolute_source_files)
+ $(MAKE) $(edit_po_files)
+ @touch $@
diff --git a/storage/mroonga/vendor/groonga/build/makefiles/locale.am b/storage/mroonga/vendor/groonga/build/makefiles/locale.am
new file mode 100644
index 00000000000..414c19a7e34
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/build/makefiles/locale.am
@@ -0,0 +1,12 @@
+SUBDIRS = LC_MESSAGES
+
+BUILT_SOURCES =
+EXTRA_DIST =
+
+include $(top_srcdir)/build/makefiles/sphinx.am
+
+init:
+ cd LC_MESSAGES && $(MAKE) $@
+
+update-po:
+ cd LC_MESSAGES && $(MAKE) update
diff --git a/storage/mroonga/vendor/groonga/build/makefiles/sphinx-build.am b/storage/mroonga/vendor/groonga/build/makefiles/sphinx-build.am
new file mode 100644
index 00000000000..e237377ba80
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/build/makefiles/sphinx-build.am
@@ -0,0 +1,19 @@
+# You can set these variables from the command line.
+DOCTREES_BASE = doctrees
+
+SPHINXOPTS =
+PAPER =
+
+# Internal variables.
+SOURCE_DIR = $(abs_top_srcdir)/doc/source
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = $(PAPEROPT_$(PAPER)) -E $(SPHINXOPTS) $(SOURCE_DIR)
+
+SPHINX_DIR = $(abs_top_builddir)/doc/sphinx
+SPHINX_BUILD_COMMAND = \
+ DOCUMENT_VERSION="$(DOCUMENT_VERSION)" \
+ DOCUMENT_VERSION_FULL="$(DOCUMENT_VERSION_FULL)" \
+ LOCALE="$(LOCALE)" \
+ PYTHONPATH="$(SPHINX_DIR):$$PYTHONPATH" \
+ $(SPHINX_BUILD)
diff --git a/storage/mroonga/vendor/groonga/build/makefiles/sphinx.am b/storage/mroonga/vendor/groonga/build/makefiles/sphinx.am
new file mode 100644
index 00000000000..f84fb23b739
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/build/makefiles/sphinx.am
@@ -0,0 +1,179 @@
+include $(top_srcdir)/doc/files.am
+include $(top_srcdir)/build/makefiles/sphinx-build.am
+
+$(html_files): html-build-stamp
+$(html_files_relative_from_locale_dir): html-build-stamp
+$(man_files): man-build-stamp
+
+am__nobase_dist_doc_locale_DATA_DIST =
+if DOCUMENT_AVAILABLE
+doc_localedir = $(docdir)/$(LOCALE)
+nobase_dist_doc_locale_DATA = \
+ $(html_files_relative_from_locale_dir)
+am__nobase_dist_doc_locale_DATA_DIST += \
+ $(nobase_dist_doc_locale_DATA)
+endif
+
+document_source_files = \
+ $(absolute_source_files) \
+ $(absolute_theme_files) \
+ $(po_files_relative_from_locale_dir) \
+ $(mo_files_relative_from_locale_dir)
+
+required_build_stamps = \
+ html-build-stamp \
+ man-build-stamp \
+ mo-build-stamp
+
+if DOCUMENT_BUILDABLE
+EXTRA_DIST += $(required_build_stamps)
+endif
+
+man_files = \
+ man/$(PACKAGE_NAME).1
+
+generated_files = \
+ $(DOCTREES_BASE) \
+ man \
+ man-build-stamp \
+ html \
+ html-build-stamp \
+ pdf \
+ pdf-build-stamp \
+ dirhtml \
+ dirhtml-build-stamp \
+ pickle \
+ pikcle-build-stamp \
+ json \
+ json-build-stamp \
+ htmlhelp \
+ htmlhelp-build-stamp \
+ qthelp \
+ qthelp-build-stamp \
+ latex \
+ latex-build-stamp \
+ changes \
+ changes-build-stamp \
+ linkcheck \
+ linkcheck-build-stamp \
+ doctest
+
+$(mo_files_relative_from_locale_dir): mo-build-stamp
+
+mo-build-stamp: $(po_files_relative_from_locale_dir)
+ cd LC_MESSAGES && $(MAKE) build
+ @touch $@
+
+if DOCUMENT_BUILDABLE
+clean-local: $(clean_targets) clean-doctrees
+
+clean-doctrees:
+ rm -rf $(DOCTREES_BASE)
+
+maintainer-clean-local:
+ rm -rf -- $(generated_files)
+endif
+
+.PHONY: help
+.PHONY: man clean-man
+.PHONY: html clean-html
+.PHONY: pdf
+.PHONY: dirhtml
+.PHONY: pickle
+.PHONY: json
+.PHONY: htmlhelp
+.PHONY: qthelp
+.PHONY: latex
+.PHONY: changes
+.PHONY: linkcheck
+.PHONY: doctest
+
+if DOCUMENT_BUILDABLE
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " man to make man files"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " rdoc to make RDoc files"
+ @echo " textile to make Textile files"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+man: man-build-stamp
+html: html-build-stamp
+dirhtml: dirhtml-build-stamp
+pickle: pickle-build-stamp
+json: json-build-stamp
+htmlhelp: htmlhelp-build-stamp
+qthelp: qthelp-build-stamp
+latex: latex-build-stamp
+rdoc: rdoc-build-stamp
+textile: textile-build-stamp
+changes: changes-build-stamp
+linkcheck: linkcheck-build-stamp
+doctest: doctest-build-stamp
+
+clean_targets = \
+ clean-man \
+ clean-html \
+ clean-dirhtml \
+ clean-pickle \
+ clean-json \
+ clean-htmlhelp \
+ clean-qthelp \
+ clean-latex \
+ clean-rdoc \
+ clean-textile \
+ clean-changes \
+ clean-linkcheck \
+ clean-doctest
+
+$(clean_targets):
+ target=`echo $@ | sed -e 's/^clean-//'`; \
+ rm -rf $${target}-build-stamp $${target}
+
+build_stamps = \
+ man-build-stamp \
+ html-build-stamp \
+ dirhtml-build-stamp \
+ pickle-build-stamp \
+ json-build-stamp \
+ htmlhelp-build-stamp \
+ qthelp-build-stamp \
+ latex-build-stamp \
+ rdoc-build-stamp \
+ textile-build-stamp \
+ changes-build-stamp \
+ linkcheck-build-stamp \
+ doctest-build-stamp
+
+$(build_stamps): $(document_source_files)
+ target=`echo $@ | sed -e 's/-build-stamp$$//'`; \
+ $(SPHINX_BUILD_COMMAND) \
+ -Dlanguage=$(LOCALE) \
+ -d $(DOCTREES_BASE)/$${target} \
+ -b $${target} \
+ $(ALLSPHINXOPTS) \
+ $${target}
+ @touch $@
+
+qthelp: qthelp-message
+qthelp-message: qthelp-build-stamp
+ @echo "Build finished; now you can run 'qcollectiongenerator' with the" \
+ ".qhcp project file in qthelp/*, like this:"
+ @echo "# qcollectiongenerator qthelp/groonga.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile qthelp/groonga.qhc"
+
+latex: latex-message
+latex-message: latex-build-stamp
+ @echo "Build finished; the LaTeX files are in latex/*."
+ @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+ "run these through (pdf)latex."
+endif
diff --git a/storage/mroonga/vendor/groonga/config.h.cmake b/storage/mroonga/vendor/groonga/config.h.cmake
new file mode 100644
index 00000000000..1744b192ad5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/config.h.cmake
@@ -0,0 +1,153 @@
+/* config.h.cmake. Generated from CMakeLists.txt by cmake. */
+
+/* general constants */
+#define CONFIGURE_OPTIONS "${CONFIGURE_OPTIONS}"
+
+#define HOST_CPU "${CMAKE_HOST_SYSTEM_PROCESSOR}"
+#define HOST_OS "${CMAKE_HOST_SYSTEM_NAME}"
+
+#define VERSION "${VERSION}"
+#define PACKAGE "${PROJECT_NAME}"
+#define PACKAGE_NAME "${PROJECT_NAME}"
+#define PACKAGE_STRING "${PROJECT_NAME} ${VERSION}"
+#define PACKAGE_TARNAME "${PROJECT_NAME}"
+#define PACKAGE_URL "${PACKAGE_URL}"
+#define PACKAGE_VERSION "${VERSION}"
+
+/* groonga related constants */
+#define GRN_CONFIG_PATH "${GRN_CONFIG_PATH}"
+#define GRN_LOG_PATH "${GRN_LOG_PATH}"
+#define GRN_VERSION "${GRN_VERSION}"
+
+#define GRN_DEFAULT_DB_KEY "${GRN_DEFAULT_DB_KEY}"
+#define GRN_DEFAULT_ENCODING "${GRN_DEFAULT_ENCODING}"
+#define GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD \
+ ${GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD}
+#define GRN_DEFAULT_RELATIVE_DOCUMENT_ROOT \
+ "${GRN_DEFAULT_RELATIVE_DOCUMENT_ROOT}"
+#define GRN_DEFAULT_DOCUMENT_ROOT \
+ "${GRN_DEFAULT_DOCUMENT_ROOT}"
+
+#define GRN_STACK_SIZE ${GRN_STACK_SIZE}
+
+#define GRN_LOCK_TIMEOUT ${GRN_LOCK_TIMEOUT}
+#define GRN_LOCK_WAIT_TIME_NANOSECOND \
+ ${GRN_LOCK_WAIT_TIME_NANOSECOND}
+
+#define GRN_RELATIVE_PLUGINS_DIR \
+ "${GRN_RELATIVE_PLUGINS_DIR}"
+#define GRN_PLUGINS_DIR "${GRN_PLUGINS_DIR}"
+#define GRN_PLUGIN_SUFFIX "${GRN_PLUGIN_SUFFIX}"
+
+#define GRN_QUERY_EXPANDER_TSV_RELATIVE_SYNONYMS_FILE "${GRN_QUERY_EXPANDER_TSV_RELATIVE_SYNONYMS_FILE}"
+#define GRN_QUERY_EXPANDER_TSV_SYNONYMS_FILE "${GRN_QUERY_EXPANDER_TSV_SYNONYMS_FILE}"
+
+#define GRN_RELATIVE_RUBY_SCRIPTS_DIR \
+ "${GRN_RELATIVE_RUBY_SCRIPTS_DIR}"
+#define GRN_RUBY_SCRIPTS_DIR "${GRN_RUBY_SCRIPTS_DIR}"
+
+#define GRN_DLL_FILENAME L"${GRN_DLL_FILENAME}"
+
+/* build switches */
+#cmakedefine USE_MEMORY_DEBUG
+#cmakedefine USE_MAP_HUGETLB
+#cmakedefine USE_AIO
+#cmakedefine USE_DYNAMIC_MALLOC_CHANGE
+#cmakedefine USE_EPOLL
+#cmakedefine USE_EXACT_ALLOC_COUNT
+#cmakedefine USE_FAIL_MALLOC
+#cmakedefine USE_FUTEX
+#cmakedefine USE_KQUEUE
+#cmakedefine USE_MSG_MORE
+#cmakedefine USE_MSG_NOSIGNAL
+#cmakedefine USE_POLL
+#cmakedefine USE_QUERY_ABORT
+#cmakedefine USE_SELECT
+
+/* compiler specific build options */
+#cmakedefine _FILE_OFFSET_BITS @_FILE_OFFSET_BITS@
+#ifndef _GNU_SOURCE
+ #cmakedefine _GNU_SOURCE
+#endif
+#cmakedefine _ISOC99_SOURCE
+#cmakedefine _LARGE_FILES
+#cmakedefine _NETBSD_SOURCE
+#cmakedefine _XOPEN_SOURCE
+#cmakedefine _XPG4_2
+#cmakedefine __EXTENSIONS__
+
+/* build environment */
+#cmakedefine WORDS_BIGENDIAN
+
+/* packages */
+#cmakedefine GRN_WITH_BENCHMARK
+#cmakedefine GRN_WITH_CUTTER
+#cmakedefine GRN_WITH_KYTEA
+#cmakedefine GRN_WITH_LIBMEMCACHED
+#cmakedefine GRN_WITH_LZO
+#cmakedefine GRN_WITH_MECAB
+#cmakedefine GRN_WITH_MESSAGE_PACK
+#cmakedefine GRN_WITH_MRUBY
+#cmakedefine GRN_WITH_NFKC
+#cmakedefine GRN_WITH_ZEROMQ
+#cmakedefine GRN_WITH_ZLIB
+
+/* headers */
+#cmakedefine HAVE_DLFCN_H
+#cmakedefine HAVE_ERRNO_H
+#cmakedefine HAVE_EXECINFO_H
+#cmakedefine HAVE_INTTYPES_H
+#cmakedefine HAVE_LINUX_FUTEX_H
+#cmakedefine HAVE_MEMORY_H
+#cmakedefine HAVE_NETDB_H
+#cmakedefine HAVE_NETINET_IN_H
+#cmakedefine HAVE_NETINET_TCP_H
+#cmakedefine HAVE_PTHREAD_H
+#cmakedefine HAVE_SIGNAL_H
+#cmakedefine HAVE_STDINT_H
+#cmakedefine HAVE_STDLIB_H
+#cmakedefine HAVE_STRINGS_H
+#cmakedefine HAVE_STRING_H
+#cmakedefine HAVE_SYS_MMAN_H
+#cmakedefine HAVE_SYS_PARAM_H
+#cmakedefine HAVE_SYS_RESOURCE_H
+#cmakedefine HAVE_SYS_SELECT_H
+#cmakedefine HAVE_SYS_SOCKET_H
+#cmakedefine HAVE_SYS_STAT_H
+#cmakedefine HAVE_SYS_SYSCALL_H
+#cmakedefine HAVE_SYS_SYSCTL_H
+#cmakedefine HAVE_SYS_TIMEB_H
+#cmakedefine HAVE_SYS_TIME_H
+#cmakedefine HAVE_SYS_TYPES_H
+#cmakedefine HAVE_SYS_WAIT_H
+#cmakedefine HAVE_TIME_H
+#cmakedefine HAVE_UCONTEXT_H
+#cmakedefine HAVE_UNISTD_H
+
+/* libraries */
+#cmakedefine HAVE_LIBEDIT
+#cmakedefine HAVE_LIBEVENT
+#cmakedefine HAVE_LIBM
+#cmakedefine HAVE_LIBRT
+
+/* structs */
+#cmakedefine HAVE_MECAB_DICTIONARY_INFO_T
+
+/* functions */
+#cmakedefine HAVE__STRNICMP
+#cmakedefine HAVE__STRTOUI64
+#cmakedefine HAVE_BACKTRACE
+#cmakedefine HAVE_CLOCK
+#cmakedefine HAVE_CLOCK_GETTIME
+#cmakedefine HAVE_CLOSE
+#cmakedefine HAVE_FPCLASSIFY
+#cmakedefine HAVE_GMTIME_R
+#cmakedefine HAVE_LOCALTIME_R
+#cmakedefine HAVE_MKOSTEMP
+#cmakedefine HAVE_OPEN
+#cmakedefine HAVE_READ
+#cmakedefine HAVE_STRNCASECMP
+#cmakedefine HAVE_STRTOULL
+#cmakedefine HAVE_WRITE
+#cmakedefine HAVE_PTHREAD_MUTEXATTR_SETPSHARED
+#cmakedefine HAVE_PTHREAD_CONDATTR_SETPSHARED
diff --git a/storage/mroonga/vendor/groonga/config.sh.in b/storage/mroonga/vendor/groonga/config.sh.in
new file mode 100644
index 00000000000..b4cec3caba1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/config.sh.in
@@ -0,0 +1,6 @@
+export CUTTER="@CUTTER@"
+export RUBY="@RUBY@"
+export GROONGA="@GROONGA@"
+export GROONGA_HTTPD="@GROONGA_HTTPD@"
+export GROONGA_SUGGEST_CREATE_DATASET="@GROONGA_SUGGEST_CREATE_DATASET@"
+export GROONGA_BENCHMARK="@GROONGA_BENCHMARK@"
diff --git a/storage/mroonga/vendor/groonga/configure.ac b/storage/mroonga/vendor/groonga/configure.ac
new file mode 100644
index 00000000000..9a07e410125
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/configure.ac
@@ -0,0 +1,1514 @@
+AC_PREREQ(2.59)
+m4_define([groonga_version], m4_include(base_version))
+AC_INIT([groonga], groonga_version, [groonga@razil.jp])
+AM_CONFIG_HEADER(config.h)
+
+AM_INIT_AUTOMAKE([foreign tar-pax subdir-objects])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+PACKAGE_TITLE=Groonga
+AC_SUBST(PACKAGE_TITLE)
+
+# for Autoconf 2.60 or earlier.
+if test -z "${datarootdir}"; then
+ datarootdir="\${prefix}/share"
+ AC_SUBST(datarootdir)
+fi
+
+# for Autoconf 2.59 or earlier.
+if test -z "${docdir}"; then
+ docdir="\${datarootdir}/doc/\${PACKAGE_TARNAME}"
+ AC_SUBST(docdir)
+fi
+
+AC_CANONICAL_HOST
+AC_DEFINE_UNQUOTED(HOST_CPU, ["$host_cpu"], [host CPU])
+AC_DEFINE_UNQUOTED(HOST_OS, ["$host_os"], [host OS])
+
+AC_MSG_CHECKING([for native Win32])
+case "$host_os" in
+ mingw*)
+ os_win32=yes
+ ;;
+ *)
+ os_win32=no
+ ;;
+esac
+AC_MSG_RESULT([$os_win32])
+
+AC_MSG_CHECKING([for some Win32 platform])
+case "$host_os" in
+ mingw*|cygwin*)
+ platform_win32=yes
+ ;;
+ *)
+ platform_win32=no
+ ;;
+esac
+AC_MSG_RESULT([$platform_win32])
+
+AM_CONDITIONAL(OS_WIN32, test "$os_win32" = "yes")
+AM_CONDITIONAL(PLATFORM_WIN32, test "$platform_win32" = "yes")
+
+AC_MSG_CHECKING([for NetBSD.])
+case "$host_os" in
+ netbsd*)
+ netbsd=yes
+ ;;
+ *)
+ netbsd=no
+ ;;
+esac
+AC_MSG_RESULT([$netbsd])
+
+AC_MSG_CHECKING([for Solaris.])
+case "$host_os" in
+ solaris*)
+ solaris=yes
+ ;;
+ *)
+ solaris=no
+ ;;
+esac
+AC_MSG_RESULT([$solaris])
+
+AC_C_BIGENDIAN
+AC_PROG_CXX
+AC_PROG_CC
+AM_PROG_CC_C_O
+m4_ifdef([PKG_PROG_PKG_CONFIG],
+ [PKG_PROG_PKG_CONFIG([0.19])
+ m4_pattern_allow(PKG_CONFIG_LIBDIR)])
+
+AC_MSG_CHECKING([for clang])
+if test "$CC" = "clang"; then
+ CLANG=yes
+else
+ CLANG=no
+fi
+AC_MSG_RESULT([$CLANG])
+
+AC_DEFUN([CHECK_CFLAG], [
+ AC_MSG_CHECKING([if gcc supports $1])
+ old_CFLAGS=$CFLAGS
+ flag=`echo '$1' | sed -e 's,^-Wno-,-W,'`
+ CFLAGS="$CFLAGS $flag -Werror"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])],
+ [check_cflag=yes],
+ [check_cflag=no])
+ CFLAGS="$old_CFLAGS"
+ if test "x$check_cflag" = "xyes"; then
+ CFLAGS="$CFLAGS $1"
+ fi
+ AC_MSG_RESULT([$check_cflag])
+])
+
+AC_DEFUN([CHECK_CXXFLAG], [
+ AC_MSG_CHECKING([if g++ supports $1])
+ old_CXXFLAGS=$CXXFLAGS
+ flag=`echo '$1' | sed -e 's,^-Wno-,-W,'`
+ CXXFLAGS="$CXXFLAGS $flag -Werror"
+ AC_LANG_PUSH([C++])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])],
+ [check_cxxflag=yes],
+ [check_cxxflag=no])
+ AC_LANG_POP([C++])
+ CXXFLAGS="$old_CXXFLAGS"
+ if test "x$check_cxxflag" = "xyes"; then
+ CXXFLAGS="$CXXFLAGS $1"
+ fi
+ AC_MSG_RESULT([$check_cxxflag])
+])
+
+AC_DEFUN([CHECK_BUILD_FLAG], [
+ CHECK_CFLAG([$1])
+ CHECK_CXXFLAG([$1])
+])
+
+AC_DEFUN([REMOVE_CXXFLAG], [
+ AC_MSG_CHECKING([whether g++ option $1 is needed to be removed])
+ if echo "$CXXFLAGS" | grep -q -- "$1"; then
+ CXXFLAGS=`echo "$CXXFLAGS" | sed -e 's,$1,,'`
+ remove_cxxflag=yes
+ else
+ remove_cxxflag=no
+ fi
+ AC_MSG_RESULT([$remove_cxxflag])
+])
+
+TEST_CFLAGS=""
+TEST_CXXFLAGS=""
+NO_STRICT_ALIASING_CFLAGS=""
+if test "$GCC" = "yes"; then
+ CHECK_BUILD_FLAG([-Wall])
+ CHECK_BUILD_FLAG([-Wextra])
+ if test "x$check_cflag" = "xno"; then
+ CHECK_BUILD_FLAG([-W])
+ fi
+ CHECK_BUILD_FLAG([-Wno-unused-but-set-variable]) # FIXME: enable it.
+ CHECK_BUILD_FLAG([-Wno-unused-parameter])
+ CHECK_BUILD_FLAG([-Wno-sign-compare])
+ CHECK_CFLAG([-Wno-pointer-sign])
+ CHECK_BUILD_FLAG([-Wno-missing-field-initializers])
+
+ CHECK_BUILD_FLAG([-Wformat=2])
+ CHECK_BUILD_FLAG([-Wstrict-aliasing=2])
+ if test "x$check_cflag" = "xyes"; then
+ NO_STRICT_ALIASING_CFLAGS="-fno-strict-aliasing"
+ fi
+ CHECK_BUILD_FLAG([-Wdisabled-optimization])
+ CHECK_BUILD_FLAG([-Wfloat-equal])
+ CHECK_BUILD_FLAG([-Wpointer-arith])
+ CHECK_CFLAG([-Wdeclaration-after-statement])
+ CHECK_CFLAG([-Wbad-function-cast])
+ if test "$CLANG" = "no"; then
+ CHECK_BUILD_FLAG([-Wcast-align])
+ fi
+ CHECK_BUILD_FLAG([-Wredundant-decls])
+# CHECK_BUILD_FLAG([-Wunsafe-loop-optimizations])
+# CHECK_BUILD_FLAG([-Wunreachable-code])
+# CHECK_BUILD_FLAG([-Wswitch-enum])
+# CHECK_BUILD_FLAG([-Wshadow])
+# CHECK_BUILD_FLAG([-Wconversion])
+ CHECK_BUILD_FLAG([-Wwrite-strings])
+# CHECK_BUILD_FLAG([-Winline])
+
+ CHECK_CXXFLAG([-fexceptions])
+ CHECK_CXXFLAG([-fimplicit-templates])
+
+ CFLAGS_for_source="$CFLAGS"
+ CXXFLAGS_for_source="$CXXFLAGS"
+ CHECK_BUILD_FLAG([-Wno-clobbered])
+ if test "x$check_cflag" = "xyes"; then
+ TEST_CFLAGS="-Wno-clobbered"
+ fi
+ if test "x$check_cxxflag" = "xyes"; then
+ TEST_CXXFLAGS="-Wno-clobbered"
+ fi
+ CFLAGS="$CFLAGS_for_source"
+ CXXFLAGS="$CXXFLAGS_for_source"
+fi
+AC_SUBST(TEST_CFLAGS)
+AC_SUBST(TEST_CXXFLAGS)
+AC_SUBST(NO_STRICT_ALIASING_CFLAGS)
+
+AC_LIBTOOL_WIN32_DLL
+AM_PROG_LIBTOOL
+m4_ifdef([LT_OUTPUT], [LT_OUTPUT])
+
+LT_CURRENT=0
+LT_REVISION=0
+LT_AGE=0
+LT_VERSION_INFO="\$(LT_CURRENT):\$(LT_REVISION):\$(LT_AGE)"
+AC_SUBST(LT_CURRENT)
+AC_SUBST(LT_REVISION)
+AC_SUBST(LT_AGE)
+AC_SUBST(LT_VERSION_INFO)
+
+GRN_DLL_FILENAME="libgroonga-\$(LT_CURRENT).dll"
+AC_SUBST(GRN_DLL_FILENAME)
+
+if test "$srcdir/version.sh"; then
+ source "$srcdir/version.sh"
+ AC_SUBST(GRN_VERSION)
+ AC_DEFINE_UNQUOTED(GRN_VERSION, ["$GRN_VERSION"], [groonga version])
+fi
+
+AC_CONFIG_FILES([
+ Makefile
+ build/Makefile
+ build/cmake_modules/Makefile
+ src/Makefile
+ src/suggest/Makefile
+ src/httpd/Makefile
+ lib/Makefile
+ lib/dat/Makefile
+ lib/mrb/Makefile
+ lib/mrb/scripts/Makefile
+ include/Makefile
+ include/groonga/Makefile
+ plugins/Makefile
+ plugins/tokenizers/Makefile
+ plugins/suggest/Makefile
+ plugins/table/Makefile
+ plugins/query_expanders/Makefile
+ plugins/ruby/Makefile
+ examples/Makefile
+ examples/dictionary/Makefile
+ examples/dictionary/edict/Makefile
+ examples/dictionary/eijiro/Makefile
+ examples/dictionary/gene95/Makefile
+ examples/dictionary/jmdict/Makefile
+ packages/Makefile
+ packages/apt/Makefile
+ packages/ubuntu/Makefile
+ packages/rpm/Makefile
+ packages/rpm/centos/Makefile
+ packages/rpm/fedora/Makefile
+ packages/yum/Makefile
+ packages/source/Makefile
+ packages/windows/Makefile
+ packages/windows/patches/Makefile
+ packages/windows/language-files/Makefile
+ packages/windows/setup-x64.nsi
+ data/Makefile
+ data/images/Makefile
+ data/images/logo/Makefile
+ data/html/Makefile
+ data/munin/Makefile
+ data/init.d/Makefile
+ data/init.d/redhat/Makefile
+ data/init.d/redhat/sysconfig/Makefile
+ data/logrotate.d/Makefile
+ data/logrotate.d/redhat/Makefile
+ data/systemd/Makefile
+ data/systemd/fedora/Makefile
+ data/systemd/fedora/sysconfig/Makefile
+ data/scripts/Makefile
+ tools/Makefile
+ doc/Makefile
+ doc/locale/Makefile
+ doc/locale/en/Makefile
+ doc/locale/en/LC_MESSAGES/Makefile
+ doc/locale/ja/Makefile
+ doc/locale/ja/LC_MESSAGES/Makefile
+ test/Makefile
+ test/unit/Makefile
+ test/unit/lib/Makefile
+ test/unit/fixtures/Makefile
+ test/unit/fixtures/inverted-index/Makefile
+ test/unit/fixtures/stress/Makefile
+ test/unit/fixtures/plugins/Makefile
+ test/unit/fixtures/geo/Makefile
+ test/unit/fixtures/story/Makefile
+ test/unit/fixtures/story/taiyaki/Makefile
+ test/unit/util/Makefile
+ test/unit/core/Makefile
+ test/unit/core/dat/Makefile
+ test/unit/story/Makefile
+ test/command/Makefile
+ benchmark/Makefile
+ benchmark/fixtures/Makefile
+ benchmark/fixtures/geo-select/Makefile
+ benchmark/lib/Makefile
+ vendor/Makefile
+ vendor/onigmo/Makefile
+ vendor/mruby/Makefile
+])
+
+if test "$GCC" = "yes"; then
+ AC_DEFINE(_GNU_SOURCE, [1], [Define to 1 if you use GCC.])
+fi
+
+if test "$netbsd" = "yes"; then
+ AC_DEFINE(_NETBSD_SOURCE, [1], [Define to 1 if you are on NetBSD.])
+fi
+
+if test "$solaris" = "yes"; then
+ AC_DEFINE(_XPG4_2, [1],
+ [Define to 1 for msghdr.msg_control if you are on Solaris.])
+ AC_DEFINE(__EXTENSIONS__, [1],
+ [Define to 1 for singal.h with _XPG4_2 if you are on Solaris.])
+fi
+
+# For debug
+AC_ARG_ENABLE(debug,
+ [AS_HELP_STRING([--enable-debug],
+ [use debug flags (default=no)])],
+ [grn_debug="$enableval"],
+ [grn_debug="no"])
+if test "x$grn_debug" != "xno"; then
+ grn_debug="yes"
+ if test "$CLANG" = "yes"; then
+ CFLAGS="$CFLAGS -O0 -g"
+ CXXFLAGS="$CXXFLAGS -O0 -g"
+ elif test "$GCC" = "yes"; then
+ CFLAGS="$CFLAGS -O0 -g3"
+ CXXFLAGS="$CXXFLAGS -O0 -g3"
+ fi
+fi
+AC_SUBST(grn_debug)
+
+AC_SEARCH_LIBS(log, m, [], [AC_MSG_ERROR("No libm found")])
+AC_MSG_CHECKING([for fpclassify])
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [#include <math.h>],
+ [if (fpclassify(0.0)) {return 0;}]
+ )],
+ [
+ AC_DEFINE(HAVE_FPCLASSIFY, [1], [use fpclassify])
+ AC_MSG_RESULT(yes)
+ ],
+ [
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [#define _ISOC99_SOURCE
+ #include <math.h>],
+ [if (fpclassify(0.0)) {return 0;}]
+ )],
+ [
+ AC_DEFINE(_ISOC99_SOURCE, [1], [Define to 1 for fpclassify])
+ AC_DEFINE(HAVE_FPCLASSIFY, [1], [use fpclassify with _ISOC99_SOURCE])
+ AC_MSG_RESULT(yes)
+ ],
+ [
+ AC_MSG_RESULT(no)
+ ])
+ ])
+
+m4_include(build/ac_macros/check_headers.m4)
+m4_include(build/ac_macros/check_functions.m4)
+
+AC_SEARCH_LIBS(backtrace, execinfo,
+ [AC_DEFINE(HAVE_BACKTRACE, [1],
+ [Define to 1 if you have the `backtrace' function.])])
+AC_SEARCH_LIBS(clock_gettime, rt,
+ [AC_DEFINE(HAVE_CLOCK_GETTIME, [1], [use clock_gettime])])
+AC_SYS_LARGEFILE
+AC_TYPE_OFF_T
+AC_TYPE_SIZE_T
+AC_CHECK_SIZEOF(off_t)
+
+# MAP_HUGETLB
+AC_ARG_ENABLE(map-hugetlb,
+ [AS_HELP_STRING([--enable-map-hugetlb],
+ [use MAP_HUGETLB. [default=no]])],
+ ,
+ [enable_map_hugetlb="no"])
+if test "x$enable_map_hugetlb" != "xno"; then
+ AC_MSG_CHECKING([for MAP_HUGETLB])
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [
+#ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif /* HAVE_SYS_MMAN_H */
+ ],
+ [MAP_HUGETLB;]
+ )],
+ [
+ AC_DEFINE(USE_MAP_HUGETLB, [1], [use MAP_HUGETLB])
+ AC_MSG_RESULT(yes)
+ ],
+ [
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR("MAP_HUGETLB isn't available.")
+ ]
+ )
+fi
+
+# log path
+AC_ARG_WITH(log_path,
+ [AS_HELP_STRING([--with-log-path=PATH],
+ [specify groonga log path.])],
+ grn_log_path="$withval",
+ grn_log_path="\$(localstatedir)/log/\$(PACKAGE_NAME)/\$(PACKAGE_NAME).log")
+AC_SUBST(grn_log_path)
+
+# default encoding
+AC_ARG_WITH(default_encoding,
+ [AS_HELP_STRING([--with-default-encoding=ENCODING],
+ [specify groonga default encoding(euc_jp/sjis/utf8/latin1/koi8r/none)])],
+ GRN_DEFAULT_ENCODING="$withval",
+ GRN_DEFAULT_ENCODING="utf8")
+AC_DEFINE_UNQUOTED(GRN_DEFAULT_ENCODING, "$GRN_DEFAULT_ENCODING", "specified default encoding")
+
+# default match escalation threshold
+AC_ARG_WITH(match_escalation_threshold,
+ [AS_HELP_STRING([--with-match-escalation-threshold=NUMBER],
+ [specify groonga default match escalation threshold])],
+ GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD="$withval",
+ GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD="0")
+AC_DEFINE_UNQUOTED(GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD, $GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD, "specified match escalation threshold")
+
+# default DB key management algorithm
+AC_ARG_WITH(default_db_key,
+ [AS_HELP_STRING([--with-default-db-key=ALGORITHM],
+ [specify groonga default DB key (pat/dat/auto)])],
+ GRN_DEFAULT_DB_KEY="$withval",
+ GRN_DEFAULT_DB_KEY="auto")
+AC_DEFINE_UNQUOTED(GRN_DEFAULT_DB_KEY, "$GRN_DEFAULT_DB_KEY",
+ "specified default DB key management algorithm")
+
+# DANGER!!!: stack size
+GRN_STACK_SIZE=1024
+AC_ARG_WITH(stack_size,
+ [AS_HELP_STRING([--with-stack-size=SIZE],
+ [DANGER!!!
+ This option specifies stack size. (default=$GRN_STACK_SIZE)
+ Normally, you should not use this option.])],
+ GRN_STACK_SIZE="$withval")
+AC_DEFINE_UNQUOTED(GRN_STACK_SIZE, [$GRN_STACK_SIZE], [stack size])
+
+# lock timeout
+GRN_LOCK_TIMEOUT=10000000
+AC_ARG_WITH(lock_timeout,
+ [AS_HELP_STRING([--with-lock-timeout=N],
+ [This option specifies how many times Groonga tries to acquire a lock.
+ Each try waits --with-lock-wait-time nanoseconds to acquire a lock.
+ It means that Groonga gives up after
+ (--with-lock-wait-time * --with-lock-timeout) nanoseconds.
+ (default=$GRN_LOCK_TIMEOUT)])],
+ GRN_LOCK_TIMEOUT="$withval")
+AC_DEFINE_UNQUOTED(GRN_LOCK_TIMEOUT,
+ [$GRN_LOCK_TIMEOUT],
+ [lock timeout])
+
+# lock wait time
+GRN_LOCK_WAIT_TIME_NANOSECOND=1000000
+AC_ARG_WITH(lock_wait_time,
+ [AS_HELP_STRING([--with-lock-wait-time=NANOSECONDS],
+ [This option specifies wait time in nanosecond to acquire a lock.
+ (default=$GRN_LOCK_WAIT_TIME_NANOSECOND)])],
+ GRN_LOCK_WAIT_TIME_NANOSECOND="$withval")
+AC_DEFINE_UNQUOTED(GRN_LOCK_WAIT_TIME_NANOSECOND,
+ [$GRN_LOCK_WAIT_TIME_NANOSECOND],
+ [lock wait time in nanosecond])
+
+if test "$os_win32" != "yes"; then
+ AC_CHECK_HEADERS(pthread.h)
+ AC_SEARCH_LIBS(pthread_create, pthread,
+ [],
+ [AC_MSG_ERROR("No libpthread found")])
+ AC_CHECK_FUNCS(pthread_mutexattr_setpshared)
+ AC_CHECK_FUNCS(pthread_condattr_setpshared)
+fi
+AC_SEARCH_LIBS(gethostbyname, nsl)
+AC_SEARCH_LIBS(socket, socket)
+AC_SEARCH_LIBS(dlopen, dl)
+
+# nfkc
+AC_ARG_ENABLE(nfkc,
+ [AS_HELP_STRING([--enable-nfkc],
+ [use nfkc based utf8 normalization. [default=yes]])],,
+ [enable_nfkc="yes"])
+if test "x$enable_nfkc" = "xyes"; then
+ AC_DEFINE(GRN_WITH_NFKC, [1], [compile with nfkc.c])
+fi
+
+# coverage
+m4_ifdef([AC_CHECK_COVERAGE], [AC_CHECK_COVERAGE])
+GENHTML_OPTIONS="--title 'groonga Code Coverage'"
+
+# microyield
+AC_MSG_CHECKING([whether enable uyield])
+AC_ARG_ENABLE(uyield,
+ [AS_HELP_STRING([--enable-uyield],
+ [build for detecting race conditions. [default=no]])],
+ ,
+ [enable_uyield="no"])
+AC_MSG_RESULT($enable_uyield)
+
+## malloc
+force_enable_dynamic_malloc_change="no"
+
+# exact-alloc-count
+AC_MSG_CHECKING([whether enable exact-alloc-count])
+AC_ARG_ENABLE(exact-alloc-count,
+ [AS_HELP_STRING([--enable-exact-alloc-count],
+ [atomic counting for memory alloc count. [default=yes]])],,
+ [enable_exact_alloc_count="yes"])
+if test "x$enable_exact_alloc_count" != "xno"; then
+ AC_DEFINE(USE_EXACT_ALLOC_COUNT, [1], [alloc_count with atomic])
+fi
+AC_MSG_RESULT($enable_exact_alloc_count)
+
+# failmalloc
+AC_MSG_CHECKING([whether enable fmalloc])
+AC_ARG_ENABLE(fmalloc,
+ [AS_HELP_STRING([--enable-fmalloc],
+ [make memory allocation failed in specified condition for debug. [default=no]])],
+ ,
+ [enable_fmalloc="no"])
+if test "x$enable_fmalloc" != "xno"; then
+ force_enable_dynamic_malloc_change="yes"
+ AC_DEFINE(USE_FAIL_MALLOC, [1], [use fmalloc])
+fi
+AC_MSG_RESULT($enable_fmalloc)
+
+# abort
+AC_MSG_CHECKING([whether enable abort])
+AC_ARG_ENABLE(abort,
+ [AS_HELP_STRING([--enable-abort],
+ [enable query abortion. [default=no]])],
+ ,
+ [enable_abort="no"])
+if test "x$enable_abort" != "xno"; then
+ force_enable_dynamic_malloc_change="yes"
+ AC_DEFINE(USE_QUERY_ABORT, [1], [use abort])
+fi
+AC_MSG_RESULT($enable_abort)
+
+# dynamic malloc change
+AC_MSG_CHECKING([whether allow dynamic memory allocation change])
+AC_ARG_ENABLE(dynamic-malloc-change,
+ [AS_HELP_STRING([--enable-dynamic-malloc-change],
+ [allow dynamic memory allocation change for testing. [default=no]])],
+ ,
+ [enable_dynamic_malloc_change="no"])
+if test "x$enable_dynamic_malloc_change" != "xyes" -a \
+ "x$force_enable_dynamic_malloc_change" = "xyes"; then
+ enable_dynamic_malloc_change="yes"
+ AC_MSG_RESULT([$enable_dynamic_malloc_change (force)])
+else
+ AC_MSG_RESULT([$enable_dynamic_malloc_change])
+fi
+
+if test "x$enable_dynamic_malloc_change" = "xyes"; then
+ AC_DEFINE(USE_DYNAMIC_MALLOC_CHANGE, [1],
+ [Define to 1 if you enable dynamic malloc change])
+fi
+
+# memory debug
+AC_MSG_CHECKING([whether debug memory management])
+AC_ARG_ENABLE(memory-debug,
+ [AS_HELP_STRING([--enable-memory-debug],
+ [debug memory management. [default=no]])],
+ ,
+ [enable_memory_debug="no"])
+AC_MSG_RESULT([$enable_memory_debug])
+
+if test "x$enable_memory_debug" = "xyes"; then
+ AC_DEFINE(USE_MEMORY_DEBUG, [1],
+ [Define to 1 if you enable debuging memory management])
+fi
+
+# epoll/kqueue/poll/select check
+AC_CHECK_HEADER(sys/epoll.h, [
+ AC_CHECK_FUNC(epoll_create, [
+ AC_TRY_RUN([
+#include <sys/epoll.h>
+int main(int argc, char **argv) { return (epoll_create(16) < 0); }
+ ],
+ [
+ have_epoll="yes"
+ AC_DEFINE(USE_EPOLL, [1], [use epoll])
+ ]
+ )
+ ])
+])
+
+if test "x$have_epoll" != "xyes"; then
+ AC_CHECK_HEADER(sys/event.h, [
+ AC_CHECK_FUNC(kevent, [
+ have_kqueue="yes"
+ AC_DEFINE(USE_KQUEUE, [1], [use kqueue])
+ ])
+ ])
+ if test "x$have_kqueue" != "xyes"; then
+ AC_CHECK_HEADER(sys/poll.h, [
+ AC_CHECK_FUNC(poll, [
+ have_poll="yes"
+ AC_DEFINE(USE_POLL, [1], [use poll])
+ ])
+ ])
+ if test "x$have_poll" != "xyes"; then
+ if test "$os_win32" = "yes"; then
+ AC_CHECK_HEADER(winsock2.h, [have_select="yes"])
+ else
+ AC_CHECK_FUNC(select, [
+ have_select="yes"
+ AC_CHECK_HEADERS(sys/select.h)
+ ])
+ fi
+ if test "x$have_select" = "xyes"; then
+ AC_DEFINE(USE_SELECT, [1], [use select])
+ else
+ AC_MSG_ERROR([epoll/kqueue/poll/select is missing.])
+ fi
+ fi
+ fi
+fi
+
+# check MSG_MORE defined
+AC_MSG_CHECKING([whether MSG_MORE defined])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+#include <sys/types.h>
+#include <sys/socket.h>
+
+int main(int argc, char **argv)
+{
+ return MSG_MORE;
+}
+ ])],
+ [
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(USE_MSG_MORE, [1], [use MSG_MORE])
+ ],
+ [
+ AC_MSG_RESULT(no)
+ ])
+
+# check MSG_NOSIGNAL defined
+AC_MSG_CHECKING([whether MSG_NOSIGNAL defined])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+#include <sys/types.h>
+#include <sys/socket.h>
+
+int main(int argc, char **argv)
+{
+ return MSG_NOSIGNAL;
+}
+ ])],
+ [
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(USE_MSG_NOSIGNAL, [1], [use MSG_NOSIGNAL])
+ ],
+ [
+ AC_MSG_RESULT(no)
+ ])
+
+# MinGW
+if test "$os_win32" = "yes"; then
+ WINDOWS_LDFLAGS="-mwindows"
+ WINDOWS_LIBS="-ladvapi32 -lws2_32"
+else
+ WINDOWS_LDFLAGS=
+ WINDOWS_LIBS=
+fi
+AC_SUBST(WINDOWS_LDFLAGS)
+AC_SUBST(WINDOWS_LIBS)
+
+# groonga binary path
+GROONGA="${ac_pwd}/src/groonga"
+AC_SUBST(GROONGA)
+
+# groonga-benchmark binary path
+GROONGA_BENCHMARK="${ac_pwd}/src/groonga-benchmark"
+AC_SUBST(GROONGA_BENCHMARK)
+
+# groonga-suggest-create-dataset binary path
+GROONGA_SUGGEST_CREATE_DATASET="${ac_pwd}/src/suggest/groonga-suggest-create-dataset"
+AC_SUBST(GROONGA_SUGGEST_CREATE_DATASET)
+
+# check Cutter with GLib support if available
+REQUIRED_MINIMUM_CUTTER_VERSION=1.1.6
+REQUIRED_MINIMUM_CPPCUTTER_VERSION=1.2.0
+m4_ifdef([AC_CHECK_GCUTTER],
+ [AC_CHECK_GCUTTER(>= $REQUIRED_MINIMUM_CUTTER_VERSION)],
+ [cutter_use_cutter="no"])
+m4_ifdef([AC_CHECK_CPPCUTTER],
+ [AC_CHECK_CPPCUTTER(>= $REQUIRED_MINIMUM_CPPCUTTER_VERSION)],
+ [cutter_use_cppcutter="no"])
+
+AM_CONDITIONAL([WITH_CUTTER], [test "$cutter_use_cutter" = "yes"])
+AM_CONDITIONAL([WITH_CPPCUTTER], [test "$cutter_use_cppcutter" = "yes"])
+if test "$cutter_use_cutter" = "yes"; then
+ AC_DEFINE(GRN_WITH_CUTTER, 1, [Define to 1 if you use Cutter])
+fi
+
+# check for benchmark
+AC_ARG_ENABLE(benchmark,
+ [AS_HELP_STRING([--disable-benchmark],
+ [don't build benchmark programs.])],,
+ [enable_benchmark="yes"])
+if test "x$enable_benchmark" = "xno"; then
+ benchmark_available="no"
+else
+ REQUIRED_GLIB_VERSION=2.14.0
+ m4_ifdef([AM_PATH_GLIB_2_0],
+ [AM_PATH_GLIB_2_0($REQUIRED_GLIB_VERSION,
+ [benchmark_available="yes"],
+ [benchmark_available="no"],
+ [gobject gthread])],
+ [benchmark_available="no"])
+ AC_MSG_CHECKING(for benchmark availablity)
+ AC_MSG_RESULT($ac_benchmark_available)
+fi
+if test "$benchmark_available" = "yes"; then
+ AC_DEFINE(GRN_WITH_BENCHMARK, 1, [Define to 1 if benchamrk is available])
+fi
+AM_CONDITIONAL([WITH_BENCHMARK], [test "$benchmark_available" = "yes"])
+
+# check Ruby for HTTP test
+ac_cv_ruby_available="no"
+AC_ARG_WITH([ruby],
+ AS_HELP_STRING([--with-ruby=PATH],
+ [Ruby interpreter path (default: no)]),
+ [RUBY="$withval"],
+ [RUBY="no"])
+
+if test "x$RUBY" = "xno"; then
+ RUBY=
+else
+ if test "x$RUBY" = "xyes"; then
+ AC_PATH_PROGS(RUBY,
+ [ruby2.1 ruby21 ruby2.0 ruby20 ruby1.9 ruby19 ruby1.9.1 ruby],
+ ruby-not-found)
+ if test "$RUBY" != "ruby-not-found"; then
+ ruby_version="`$RUBY --version`"
+ if echo "$ruby_version" | grep -q -- 'ruby \(1\.9\|2\.\)'; then
+ ac_cv_ruby_available="yes"
+ else
+ AC_MSG_WARN([$RUBY isn't Ruby 1.9 or later ($ruby_version)])
+ fi
+ fi
+ else
+ ruby_not_found_warning_message="$RUBY is not found. Disable HTTP test."
+ case "$RUBY" in
+ /*)
+ AC_CHECK_FILE([$RUBY],
+ [ac_cv_ruby_available="yes"],
+ [AC_MSG_WARN([$ruby_not_found_warning_message])
+ RUBY="$RUBY-not-found"])
+ ;;
+ *)
+ ruby_not_found="$RUBY-not-found"
+ AC_PATH_PROGS(RUBY, "$RUBY", "$ruby_not_found")
+ if test "$RUBY" = "$ruby_not_found"; then
+ AC_MSG_WARN([$ruby_not_found_warning_message])
+ else
+ ac_cv_ruby_available="yes"
+ fi
+ ;;
+ esac
+ fi
+fi
+AC_SUBST(RUBY)
+AM_CONDITIONAL([WITH_RUBY], [test "$ac_cv_ruby_available" = "yes"])
+
+AM_CONDITIONAL([WITH_UNIT_TEST],
+ [test "$cutter_use_cutter" = "yes" -o \
+ "$ac_cv_ruby_available" = "yes"])
+
+AM_CONDITIONAL([WITH_COMMAND_TEST],
+ [test "$ac_cv_ruby_available" = "yes"])
+
+# check Inkscape for generating PNG images
+inkscape_available="no"
+AC_ARG_WITH([inkscape],
+ AS_HELP_STRING([--with-inkscape=PATH],
+ [Inkscape path (default: auto)]),
+ [INKSCAPE="$withval"],
+ [INKSCAPE="yes"])
+
+if test "x$INKSCAPE" = "xno"; then
+ INKSCAPE=
+else
+ if test "x$INKSCAPE" = "xyes"; then
+ AC_PATH_PROGS(INKSCAPE, [inkscape], none)
+ if test "$INKSCAPE" != "none"; then
+ inkscape_available="yes"
+ fi
+ else
+ AC_CHECK_FILE([$INKSCAPE],
+ [inkscape_available="yes"],
+ [AC_MSG_WARN([$INKSCAPE is not found.
+ Disable PNG image generation.])])
+ fi
+fi
+AC_SUBST(INKSCAPE)
+AM_CONDITIONAL([WITH_INKSCAPE], [test "$inkscape_available" = "yes"])
+
+# check Lemon for generating .c and .h files from .y file
+lemon_available="no"
+AC_ARG_WITH([lemon],
+ AS_HELP_STRING([--with-lemon=PATH],
+ [Lemon path (default: auto)]),
+ [LEMON="$withval"],
+ [: ${LEMON:=auto}])
+
+if test "$LEMON" = "no"; then
+ LEMON=
+else
+ if test "$LEMON" = "auto"; then
+ AC_PATH_PROGS(LEMON, [lemon], none)
+ if test "$LEMON" != "none"; then
+ lemon_available="yes"
+ fi
+ elif test "$LEMON" = "yes"; then
+ AC_PATH_PROGS(LEMON, [lemon], none)
+ if test "$LEMON" = "none"; then
+ AC_MSG_WARN([lemon is not found. Disable .y compilation.])
+ else
+ lemon_available="yes"
+ fi
+ else
+ AC_CHECK_FILE([$LEMON],
+ [lemon_available="yes"],
+ [AC_MSG_WARN([$LEMON is not found. Disable .y compilation.])])
+ fi
+fi
+AC_SUBST(LEMON)
+AM_CONDITIONAL([WITH_LEMON], [test "$lemon_available" = "yes"])
+
+# libedit
+AC_ARG_ENABLE(libedit,
+ [AS_HELP_STRING([--disable-libedit],
+ [use libedit for console. [default=auto-detect]])],
+ [enable_libedit="$enableval"],
+ [enable_libedit="auto"])
+if test "x$enable_libedit" != "xno"; then
+ m4_ifdef([PKG_CHECK_MODULES], [
+ PKG_CHECK_MODULES([LIBEDIT], [libedit >= 3.0],
+ [LIBS_SAVE="$LIBS"
+ LDFLAGS_SAVE="$LDFLAGS"
+ LDFLAGS="$LIBEDIT_LIBS $LDFLAGS"
+ AC_SEARCH_LIBS(el_wline, edit,
+ [libedit_available=yes],
+ [libedit_available=no])
+ LIBS="$LIBS_SAVE"
+ LDFLAGS="$LDFLAGS_SAVE"],
+ [libedit_available=no])
+ ],
+ [libedit_available=no])
+ if test "x$libedit_available" = "xyes"; then
+ AC_DEFINE(GRN_WITH_LIBEDIT, [1], [Use libedit with multibyte support.])
+ else
+ if test "x$enable_editline" = "xyes"; then
+ AC_MSG_ERROR("No libedit found")
+ fi
+ fi
+fi
+
+# zlib
+AC_ARG_WITH(zlib,
+ [AS_HELP_STRING([--with-zlib],
+ [use zlib for data compression. [default=no]])],
+ [with_zlib="$withval"],
+ [with_zlib="no"])
+GRN_WITH_ZLIB=no
+if test "x$with_zlib" = "xyes"; then
+ AC_DEFINE(GRN_WITH_ZLIB, [1], [with zlib])
+ AC_SEARCH_LIBS(compress, z, [GRN_WITH_ZLIB=yes],
+ [AC_MSG_ERROR("No libz found")])
+else
+ AC_SEARCH_LIBS(compress, z, [GRN_WITH_ZLIB=yes], [])
+fi
+AC_SUBST(GRN_WITH_ZLIB)
+
+# LZO
+AC_ARG_WITH(lzo,
+ [AS_HELP_STRING([--with-lzo],
+ [use LZO for data compression. [default=no]])],
+ [with_lzo="$withval"],
+ [with_lzo="no"])
+if test "x$with_lzo" = "xyes"; then
+ AC_DEFINE(GRN_WITH_LZO, [1], [with lzo])
+ AC_SEARCH_LIBS(lzo1_compress, lzo2, [], [AC_MSG_ERROR("No liblzo2 found")])
+fi
+
+# MeCab
+# NOTE: MUST be checked last
+AC_ARG_WITH(mecab,
+ [AS_HELP_STRING([--with-mecab],
+ [use MeCab for morphological analysis. [default=yes]])],
+ [with_mecab="$withval"],
+ [with_mecab="yes"])
+AC_MSG_CHECKING([whether enable MeCab])
+AC_MSG_RESULT($with_mecab)
+if test "x$with_mecab" = "xyes"; then
+ # mecab-config
+ AC_ARG_WITH(mecab-config,
+ [AS_HELP_STRING([--with-mecab-config=PATH],
+ [set mecab-config location. [default=auto-detect]])],
+ [if test "$cross_compiling" = "yes"; then
+ MECAB_CONFIG="$withval"
+ else
+ AC_CHECK_FILE("$withval", MECAB_CONFIG="$withval", MECAB_CONFIG=no)
+ fi],
+ [AC_PATH_PROG(MECAB_CONFIG, mecab-config, no)])
+ if test "x$MECAB_CONFIG" = "xno"; then
+ with_mecab="no"
+ else
+ MECAB_CPPFLAGS="-I`$MECAB_CONFIG --inc-dir`"
+ MECAB_LDFLAGS="-L`$MECAB_CONFIG --libs-only-L`"
+ _SAVE_LIBS="$LIBS"
+ _SAVE_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $MECAB_LDFLAGS"
+ AC_SEARCH_LIBS(mecab_new,
+ mecab,
+ [MECAB_LIBS="-lmecab $PTHREAD_LIBS"],
+ [AC_MSG_ERROR("No libmecab found")],
+ $PTHREAD_LIBS)
+ LDFLAGS="$_SAVE_LDFLAGS"
+ LIBS="$_SAVE_LIBS"
+ _SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $MECAB_CPPFLAGS"
+ AC_CHECK_HEADER(mecab.h, , [AC_MSG_ERROR("No mecab.h found")])
+ AC_CHECK_TYPE([mecab_dictionary_info_t],
+ [AC_DEFINE([HAVE_MECAB_DICTIONARY_INFO_T],
+ [1],
+ [Define to 1 if MeCab has the type `mecab_dictionary_info_t'.])],
+ [],
+ [[#include <mecab.h>]])
+ CPPFLAGS="$_SAVE_CPPFLAGS"
+ AC_SUBST(MECAB_CPPFLAGS)
+ AC_SUBST(MECAB_LDFLAGS)
+ AC_SUBST(MECAB_LIBS)
+ fi
+fi
+if test "x$with_mecab" = "xyes"; then
+ AC_DEFINE(GRN_WITH_MECAB, [1], [use MeCab])
+fi
+AM_CONDITIONAL(WITH_MECAB, test "x$with_mecab" = "xyes")
+
+# KyTea
+REQUIRED_MINIMUM_KYTEA_VERSION=0.4.2
+AC_ARG_WITH(kytea,
+ [AS_HELP_STRING([--with-kytea],
+ [use KyTea for morphological analysis. [default=auto]])],
+ [with_kytea="$withval"],
+ [with_kytea="auto"])
+AC_MSG_CHECKING([whether enable KyTea])
+AC_MSG_RESULT($with_kytea)
+if test "x$with_kytea" != "xno"; then
+ m4_ifdef([PKG_CHECK_MODULES], [
+ PKG_CHECK_MODULES([KYTEA],
+ [kytea >= $REQUIRED_MINIMUM_KYTEA_VERSION],
+ [kytea_exists=yes],
+ [kytea_exists=no])
+ ],
+ [kytea_exists=no])
+ if test "$kytea_exists" = "no" -a "x$with_kytea" = "xyes"; then
+ AC_MSG_ERROR("No KyTea found.")
+ fi
+ with_kytea="$kytea_exists"
+fi
+if test "x$with_kytea" = "xyes"; then
+ AC_DEFINE(GRN_WITH_KYTEA, [1], [use KyTea])
+fi
+AM_CONDITIONAL(WITH_KYTEA, test "x$with_kytea" = "xyes")
+
+# futex check
+AC_ARG_ENABLE(futex,
+ [AS_HELP_STRING([--enable-futex],
+ [use futex. [default=no]])],
+ ,
+ [enable_futex="no"])
+if test "x$enable_futex" != "xno"; then
+ AC_CHECK_HEADERS(linux/futex.h sys/syscall.h, [
+ AC_DEFINE(USE_FUTEX, [1], [use futex])
+ ], [
+ AC_MSG_ERROR("linux/futex.h or sys/syscall.h not found")
+ ])
+fi
+AC_MSG_CHECKING([whether enable futex])
+AC_MSG_RESULT($enable_futex)
+
+# ZeroMQ
+AC_ARG_ENABLE(zeromq,
+ [AS_HELP_STRING([--disable-zeromq],
+ [Disable ZeroMQ used for suggestion. [default=auto-detect]])],
+ [enable_zeromq="$enableval"],
+ [enable_zeromq="auto"])
+if test "x$enable_zeromq" != "xno"; then
+ m4_ifdef([PKG_CHECK_MODULES], [
+ PKG_CHECK_MODULES([LIBZMQ],
+ [libzmq],
+ [zeromq_available=yes],
+ [zeromq_available=no])
+ ],
+ [zeromq_available=no])
+ if test "x$zeromq_available" = "xyes"; then
+ AC_DEFINE(GRN_WITH_ZEROMQ, [1], [Define to 1 if ZeroMQ is available.])
+ else
+ if test "x$enable_zeromq" = "xyes"; then
+ AC_MSG_ERROR("No ZeroMQ found")
+ fi
+ fi
+fi
+
+# libevent
+AC_ARG_WITH(libevent,
+ [AS_HELP_STRING([--without-libevent],
+ [Disable libevent used for suggestion. [default=auto]])],
+ [with_libevent="$withval"],
+ [with_libevent="auto"])
+
+# workaround for bundled groonga in MariaDB.
+if test "x$with_libevent" = "xbundled"; then
+ with_libevent=no
+fi
+
+if test "x$with_libevent" != "xno"; then
+ if test "x$with_libevent" = "xyes" -o "x$with_libevent" = "xauto"; then
+ libevent_cflags=""
+ libevent_ldflags="-levent"
+ else
+ libevent_include_dir="$with_libevent/include"
+ libevent_lib_dir="$with_libevent/lib"
+ if ! test -d "$libevent_include_dir" -a -d "$libevent_lib_dir"; then
+ AC_MSG_ERROR("No libevent found in $with_libevent.")
+ fi
+ libevent_cflags="-I$libevent_include_dir"
+ libevent_ldflags="-L$libevent_lib_dir -levent"
+ fi
+
+ _SAVE_CFLAGS="$CFLAGS"
+ _SAVE_LDFLAGS="$LDFLAGS"
+ _SAVE_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $libevent_cflags"
+ LDFLAGS="$LDFLAGS $libevent_ldflags"
+ AC_SEARCH_LIBS(event_init, event,
+ [libevent_available=yes],
+ [libevent_available=no])
+ CFLAGS="$_SAVE_CFLAGS"
+ LDFLAGS="$_SAVE_LDFLAGS"
+ LIBS="$_SAVE_LIBS"
+ if test "$libevent_available" = "yes"; then
+ AC_DEFINE(GRN_WITH_LIBEVENT, [1], [Define to 1 if libevent is available.])
+ LIBEVENT_CFLAGS="$libevent_cflags"
+ LIBEVENT_LIBS="$libevent_ldflags"
+ else
+ if test "$enable_option_checking" != "no" -a "x$with_libevent" = "xyes"; then
+ AC_MSG_ERROR("No libevent found")
+ fi
+ fi
+fi
+AC_SUBST(LIBEVENT_CFLAGS)
+AC_SUBST(LIBEVENT_LIBS)
+
+# MessagePack
+AC_ARG_WITH(message-pack,
+ [AS_HELP_STRING([--without-message-pack],
+ [Disable MessagePack used for suggestion. [default=/usr]])],
+ [with_message_pack="$withval"],
+ [with_message_pack="/usr"])
+if test "x$with_message_pack" != "xno"; then
+ _SAVE_CFLAGS="$CFLAGS"
+ _SAVE_LDFLAGS="$LDFLAGS"
+ _SAVE_LIBS="$LIBS"
+ CFLAGS="$CFLAGS -I$with_message_pack/include"
+ LDFLAGS="$LDFLAGS -L$with_message_pack/lib"
+ AC_SEARCH_LIBS(msgpack_version, msgpack,
+ [message_pack_available=yes],
+ [message_pack_available=no])
+ CFLAGS="$_SAVE_CFLAGS"
+ LDFLAGS="$_SAVE_LDFLAGS"
+ LIBS="$_SAVE_LIBS"
+ if test "x$message_pack_available" = "xyes"; then
+ AC_DEFINE(GRN_WITH_MESSAGE_PACK, [1],
+ [Define to 1 if MessagePack is available.])
+ MESSAGE_PACK_CFLAGS="-I$with_message_pack/include"
+ MESSAGE_PACK_LIBS="-L$with_message_pack/lib -lmsgpack"
+ else
+ if test "x$with_message_pack" = "xyes"; then
+ AC_MSG_ERROR("No MessagePack found")
+ fi
+ fi
+fi
+AC_SUBST(MESSAGE_PACK_CFLAGS)
+AC_SUBST(MESSAGE_PACK_LIBS)
+
+AM_CONDITIONAL([ENABLE_SUGGEST_LEARNER],
+ [test "$zeromq_available" = "yes" -a \
+ "$libevent_available" = "yes" -a \
+ "$message_pack_available" = "yes"])
+
+# Document
+AC_MSG_CHECKING([whether enable document])
+AC_ARG_ENABLE(document,
+ [AS_HELP_STRING([--enable-document],
+ [enable document generation by Sphinx. [default=auto]])],
+ [enable_document="$enableval"],
+ [enable_document="auto"])
+AC_MSG_RESULT($enable_document)
+
+document_available=no
+document_buildable=no
+have_built_document=no
+if test x"$enable_document" != x"no"; then
+ if test -f "$srcdir/doc/build-stamp"; then
+ document_available=yes
+ have_built_document=yes
+ fi
+
+ if test x"$enable_document" = x"yes"; then
+ AC_PATH_PROG(SPHINX_BUILD, sphinx-build, [])
+ if test -n "$SPHINX_BUILD"; then
+ sphinx_build_version=`"$SPHINX_BUILD" --version`
+ if ! echo "$sphinx_build_version" | grep -q ' 1\.[[23]]'; then
+ AC_MSG_ERROR([
+sphinx-build is old: $sphinx_build_version
+Sphinx 1.2 or later is required.])
+ fi
+ document_available=yes
+ document_buildable=yes
+ else
+ AC_MSG_ERROR([
+No sphinx-build found.
+Install it and try again.
+
+How to install sphinx-build:
+
+For Debian GNU/Linux based system like Ubuntu:
+ % sudo apt-get install -y python-pip
+ % sudo pip install sphinx
+
+For Red Hat based system like CentOS:
+ % sudo yum install -y python-pip
+ % sudo pip install sphinx])
+ fi
+ AC_SUBST(SPHINX_BUILD)
+ fi
+fi
+
+# Check for misc.
+AC_ARG_WITH([cutter-source-path],
+ AS_HELP_STRING([--with-cutter-source-path=PATH],
+ [Specify Cutter source path for
+ groonga's release manager.]),
+ [CUTTER_SOURCE_PATH="$withval"])
+case "$CUTTER_SOURCE_PATH" in
+ ""|/*)
+ : # do nothing
+ ;;
+ *)
+ CUTTER_SOURCE_PATH="\$(top_builddir)/${CUTTER_SOURCE_PATH}"
+ ;;
+esac
+AC_SUBST(CUTTER_SOURCE_PATH)
+
+AM_CONDITIONAL([DOCUMENT_AVAILABLE],
+ [test "${document_available}" = "yes"])
+AC_MSG_CHECKING([whether document available])
+AC_MSG_RESULT($document_available)
+
+AM_CONDITIONAL([DOCUMENT_BUILDABLE],
+ [test "${document_buildable}" = "yes"])
+AC_MSG_CHECKING([whether document buildable])
+AC_MSG_RESULT($document_buildable)
+
+AM_CONDITIONAL([HAVE_BUILT_DOCUMENT],
+ [test "${have_built_document}" = "yes"])
+AC_MSG_CHECKING([whether having built document])
+AC_MSG_RESULT($have_built_document)
+
+DOCUMENT_VERSION=groonga_version
+DOCUMENT_VERSION_FULL="$GRN_VERSION"
+AC_SUBST(DOCUMENT_VERSION)
+AC_SUBST(DOCUMENT_VERSION_FULL)
+
+# Munin plugins
+AC_MSG_CHECKING([whether install munin plugins])
+AC_ARG_WITH(munin-plugins,
+ [AS_HELP_STRING([--with-munin-plugins],
+ [install Munin plugins. [default=no]])],
+ [install_munin_plugins="$withval"],
+ [install_munin_plugins="no"])
+AC_MSG_RESULT($install_munin_plugins)
+
+AM_CONDITIONAL([INSTALL_MUNIN_PLUGINS],
+ [test "${install_munin_plugins}" = "yes"])
+
+# platform
+AC_MSG_CHECKING([whether package platform])
+AC_ARG_WITH(package-platform,
+ [AS_HELP_STRING([--with-package-platform=PLATFORM],
+ [install package platform related files. [default=no]
+ (supported package platforms: redhat, fedora)])],
+ [package_platform="$withval"],
+ [package_platform="no"])
+AC_MSG_RESULT($package_platform)
+
+AM_CONDITIONAL([REDHAT_PLATFORM],
+ [test "${package_platform}" = "redhat"])
+AM_CONDITIONAL([FEDORA_PLATFORM],
+ [test "${package_platform}" = "fedora"])
+
+# plugins check
+relative_pluginsdir_base="\$(PACKAGE)/plugins"
+AC_SUBST(relative_pluginsdir_base)
+expanded_relative_pluginsdir_base="${PACKAGE}/plugins"
+AC_SUBST(expanded_relative_pluginsdir_base)
+
+relative_pluginsdir="lib/\$(relative_pluginsdir_base)"
+AC_SUBST(relative_pluginsdir)
+
+pluginsdir="\${libdir}/\$(relative_pluginsdir_base)"
+AC_SUBST(pluginsdir)
+expanded_pluginsdir="\${libdir}/${expanded_relative_pluginsdir_base}"
+AC_SUBST(expanded_pluginsdir)
+
+tokenizers_pluginsdir="\${pluginsdir}/tokenizers"
+AC_SUBST(tokenizers_pluginsdir)
+
+query_expanders_pluginsdir="\${pluginsdir}/query_expanders"
+AC_SUBST(query_expanders_pluginsdir)
+
+suggest_pluginsdir="\${pluginsdir}/suggest"
+AC_SUBST(suggest_pluginsdir)
+
+table_pluginsdir="\${pluginsdir}/table"
+AC_SUBST(table_pluginsdir)
+
+ruby_pluginsdir="\${pluginsdir}/ruby"
+AC_SUBST(ruby_pluginsdir)
+
+AC_MSG_CHECKING(for the suffix of plugin shared libraries)
+shrext_cmds=$(./libtool --config | grep '^shrext_cmds=')
+eval $shrext_cmds
+module=yes eval suffix="$shrext_cmds"
+AC_MSG_RESULT($suffix)
+if test -z "$suffix"; then
+ AC_MSG_ERROR([can't detect plugin suffix])
+fi
+AC_DEFINE_UNQUOTED(GRN_PLUGIN_SUFFIX, ["$suffix"], "plugin suffix")
+
+# for query expanders
+GRN_QUERY_EXPANDER_TSV_RELATIVE_SYNONYMS_FILE="synonyms.tsv"
+AC_DEFINE_UNQUOTED(GRN_QUERY_EXPANDER_TSV_RELATIVE_SYNONYMS_FILE,
+ ["$GRN_QUERY_EXPANDER_TSV_RELATIVE_SYNONYMS_FILE"],
+ "The relative synonyms file for TSV query expander")
+GRN_QUERY_EXPANDER_TSV_SYNONYMS_PATH="`
+ eval echo ${sysconfdir}/${PACKAGE}/${GRN_QUERY_EXPANDER_TSV_RELATIVE_SYNONYMS_FILE}
+`"
+AC_DEFINE_UNQUOTED(GRN_QUERY_EXPANDER_TSV_SYNONYMS_FILE,
+ ["$GRN_QUERY_EXPANDER_TSV_SYNONYMS_PATH"],
+ "The default synonyms file for TSV query expander")
+
+# for examples
+examplesdir="\$(pkgdatadir)/examples"
+AC_SUBST(examplesdir)
+
+examples_dictionarydir="\$(examplesdir)/dictionary"
+AC_SUBST(examples_dictionarydir)
+
+# for ruby scripts
+relative_ruby_scriptsdir_base="\$(PACKAGE)/scripts/ruby"
+AC_SUBST(relative_ruby_scriptsdir_base)
+ruby_scriptsdir="\${libdir}/\$(relative_ruby_scriptsdir_base)"
+AC_SUBST(ruby_scriptsdir)
+
+# for document root
+GRN_DEFAULT_DOCUMENT_ROOT_BASE="html/admin"
+GRN_DEFAULT_DOCUMENT_ROOT="\${pkgdatadir}/\${GRN_DEFAULT_DOCUMENT_ROOT_BASE}"
+GRN_EXPANDED_DEFAULT_DOCUMENT_ROOT="\${datadir}/${PACKAGE}/${GRN_DEFAULT_DOCUMENT_ROOT_BASE}"
+GRN_DEFAULT_RELATIVE_DOCUMENT_ROOT="share/\$(PACKAGE)/\$(GRN_DEFAULT_DOCUMENT_ROOT_BASE)"
+AC_SUBST(GRN_DEFAULT_DOCUMENT_ROOT_BASE)
+AC_SUBST(GRN_DEFAULT_DOCUMENT_ROOT)
+AC_SUBST(GRN_EXPANDED_DEFAULT_DOCUMENT_ROOT)
+AC_SUBST(GRN_DEFAULT_RELATIVE_DOCUMENT_ROOT)
+
+# flags for compile groonga
+GRN_CFLAGS=""
+AC_SUBST(GRN_CFLAGS)
+GRN_DEFS=""
+GRN_DEFS="$GRN_DEFS -DGRN_DLL_FILENAME=L\\\"\"\$(GRN_DLL_FILENAME)\"\\\""
+GRN_DEFS="$GRN_DEFS -DGRN_PLUGINS_DIR=\\\"\"\$(pluginsdir)\"\\\""
+GRN_DEFS="$GRN_DEFS -DGRN_RELATIVE_PLUGINS_DIR=\\\"\"\$(relative_pluginsdir)\"\\\""
+GRN_DEFS="$GRN_DEFS -DGRN_RUBY_SCRIPTS_DIR=\\\"\"\$(ruby_scriptsdir)\"\\\""
+GRN_DEFS="$GRN_DEFS -DGRN_RELATIVE_RUBY_SCRIPTS_DIR=\\\"\"\$(relative_ruby_scriptsdir)\"\\\""
+GRN_DEFS="$GRN_DEFS -DGRN_LOG_PATH=\\\"\"\$(grn_log_path)\"\\\""
+GRN_DEFS="$GRN_DEFS -DGRN_DEFAULT_DOCUMENT_ROOT=\\\"\"\$(GRN_DEFAULT_DOCUMENT_ROOT)\"\\\""
+GRN_DEFS="$GRN_DEFS -DGRN_DEFAULT_RELATIVE_DOCUMENT_ROOT=\\\"\"\$(GRN_DEFAULT_RELATIVE_DOCUMENT_ROOT)\"\\\""
+AC_SUBST(GRN_DEFS)
+CFLAGS="$CFLAGS $OPT_CFLAGS "
+LIBS="$LIBS $WINDOWS_LIBS"
+AC_DEFINE_UNQUOTED(CONFIGURE_OPTIONS, "$ac_configure_args", "specified configure options")
+
+# For groonga.org
+AC_ARG_WITH(groonga-org-path,
+ [AS_HELP_STRING([--with-groonga-org-path=PATH],
+ [specify a path of the groonga.org repository to update groonga.org.])],
+ [GROONGA_ORG_PATH="$withval"],
+ [GROONGA_ORG_PATH=""])
+AC_SUBST(GROONGA_ORG_PATH)
+
+# groonga-httpd
+m4_define([nginx_version], m4_include(nginx_version))
+NGINX_VERSION=nginx_version
+AC_SUBST(NGINX_VERSION)
+
+# groonga-httpd binary path
+GROONGA_HTTPD="${ac_pwd}/vendor/nginx-${NGINX_VERSION}/objs/nginx"
+AC_SUBST(GROONGA_HTTPD)
+
+AC_ARG_ENABLE(groonga_httpd,
+ [AS_HELP_STRING([--enable-groonga-httpd],
+ [enable nginx used for groonga-httpd. [default=yes]])],
+ [enable_groonga_httpd="$enableval"],
+ [enable_groonga_httpd="yes"])
+if test "x$enable_groonga_httpd" != "xno"; then
+ enable_groonga_httpd="yes"
+ AC_CONFIG_SUBDIRS([src/httpd])
+else
+ enable_groonga_httpd="no"
+fi
+AM_CONDITIONAL(WITH_GROONGA_HTTPD, test "$enable_groonga_httpd" = "yes")
+
+# mruby
+AC_ARG_ENABLE(mruby,
+ [AS_HELP_STRING([--enable-mruby],
+ [enable mruby. [default=no]])],
+ [enable_mruby="$enableval"],
+ [enable_mruby="no"])
+
+AC_MSG_CHECKING([whether enable mruby])
+if test "x$enable_mruby" != "xyes"; then
+ enable_mruby="no"
+fi
+AC_MSG_RESULT($enable_mruby)
+
+if test "$enable_mruby" = "yes"; then
+ if test "$ac_cv_ruby_available" != "yes"; then
+ AC_MSG_ERROR(--enable-ruby requires --with-ruby)
+ fi
+ AC_DEFINE(GRN_WITH_MRUBY, [1], [Define to 1 if mruby is enabled.])
+ MRUBY_CFLAGS="-I\$(top_srcdir)/vendor/mruby-source/include"
+ MRUBY_LIBS="\$(top_builddir)/vendor/mruby/libmruby.la"
+ MRUBY_LIBS="${MRUBY_LIBS} \$(top_builddir)/vendor/onigmo-source/libonig.la"
+ AC_CONFIG_SUBDIRS([vendor/onigmo])
+else
+ MRUBY_CFLAGS=
+ MRUBY_LIBS=
+fi
+AC_SUBST(MRUBY_CFLAGS)
+AC_SUBST(MRUBY_LIBS)
+AM_CONDITIONAL(WITH_MRUBY, test "$enable_mruby" = "yes")
+
+# PCRE
+GRN_WITH_PCRE=no
+AC_ARG_WITH(pcre,
+ [AS_HELP_STRING([--without-pcre],
+ [use PCRE for groonga-httpd. [default=auto-detect]])],
+ [with_pcre="$witheval"],
+ [with_pcre="auto"])
+if test "x$with_pcre" != "xno"; then
+ m4_ifdef([PKG_CHECK_MODULES], [
+ PKG_CHECK_MODULES([PCRE], [libpcre],
+ [_PKG_CONFIG(PCRE_LIBS_ONLY_L, [libs-only-L], [libpcre])
+ PCRE_LIBS_ONLY_L="$pkg_cv_PCRE_LIBS_ONLY_L"
+ GRN_WITH_PCRE=yes],
+ [GRN_WITH_PCRE=no])
+ ],
+ [GRN_WITH_PCRE=no])
+ if test "x$with_pcre" = "xyes" -a "$GRN_WITH_PCRE" != "yes"; then
+ AC_MSG_ERROR("No PCRE found")
+ fi
+fi
+AC_SUBST(GRN_WITH_PCRE)
+AC_SUBST(PCRE_CFLAGS)
+AC_SUBST(PCRE_LIBS_ONLY_L)
+
+# For package
+AC_ARG_WITH(rsync-path,
+ [AS_HELP_STRING([--with-rsync-path=PATH],
+ [specify rsync path to upload groonga packages.])],
+ [RSYNC_PATH="$withval"],
+ [RSYNC_PATH="packages@packages.groonga.org:public"])
+AC_SUBST(RSYNC_PATH)
+
+AC_ARG_WITH(launchpad-uploader-pgp-key,
+ [AS_HELP_STRING([--with-launchpad-uploader-pgp-key=KEY],
+ [specify PGP key UID to upload Groonga packages to Launchpad.])],
+ [LAUNCHPAD_UPLOADER_PGP_KEY="$withval"],
+ [LAUNCHPAD_UPLOADER_PGP_KEY=""])
+AC_SUBST(LAUNCHPAD_UPLOADER_PGP_KEY)
+
+GPG_UID=m4_include(gpg_uid)
+AC_SUBST(GPG_UID)
+
+pkgsysconfdir="\${sysconfdir}/$PACKAGE_NAME"
+AC_SUBST(pkgsysconfdir)
+
+GRN_CONFIG_PATH="`
+ test \"$prefix\" = NONE && prefix=/usr/local
+ eval echo ${sysconfdir}/groonga/groonga.conf
+`"
+AC_DEFINE_UNQUOTED(GRN_CONFIG_PATH, ["$GRN_CONFIG_PATH"],
+ [Default command line option configuration file.])
+
+GROONGA_HTTPD_DOCUMENT_ROOT="`
+ test \"$prefix\" = NONE && prefix=/usr/local
+ eval eval eval echo ${GRN_EXPANDED_DEFAULT_DOCUMENT_ROOT}
+`"
+AC_SUBST(GROONGA_HTTPD_DOCUMENT_ROOT)
+
+GROONGA_HTTPD_DEFAULT_DATABASE_PATH="`
+ test \"$prefix\" = NONE && prefix=/usr/local
+ eval eval eval echo ${localstatedir}/lib/${PACKAGE}/db/db
+`"
+AC_SUBST(GROONGA_HTTPD_DEFAULT_DATABASE_PATH)
+
+AC_OUTPUT([
+ packages/rpm/centos/groonga.spec
+ packages/rpm/fedora/groonga.spec
+ packages/apt/debian/groonga-keyring.postrm
+ packages/yum/env.sh
+ groonga.pc
+ config.sh
+ groonga-httpd-conf.sh
+ data/groonga-httpd.conf
+ data/scripts/groonga-httpd-restart
+ ])
+
+echo "$PACKAGE_NAME $PACKAGE_VERSION configuration:"
+echo "-----------------------"
+echo " Compiler: ${CC}"
+echo " CFLAGS: ${CFLAGS}"
+echo " CXXFLAGS: ${CXXFLAGS}"
+echo " Libraries: ${LIBS}"
+echo " Stack size: ${GRN_STACK_SIZE}"
+echo " Document: ${document_available}"
+echo " buildable: ${document_buildable}"
+echo " built: ${have_built_document}"
+echo " Munin plugins: ${install_munin_plugins}"
+echo " Package platform: ${package_platform}"
+echo
+echo "Paths:"
+echo " Install path prefix: ${prefix}"
+echo " Configuration file: ${GRN_CONFIG_PATH}"
+echo
+
+echo "Tokenizers:"
+echo " MeCab: $with_mecab"
+if test "x$with_mecab" = "xyes"; then
+ echo " CPPFLAGS: $MECAB_CPPFLAGS"
+ echo " LDFLAGS: $MECAB_LDFLAGS"
+ echo " LIBS: $MECAB_LIBS"
+fi
+echo " KyTea: $with_kytea"
+if test "x$with_kytea" = "xyes"; then
+ echo " CFLAGS: $KYTEA_CFLAGS"
+ echo " LIBS: $KYTEA_LIBS"
+fi
+echo
+
+echo "Libraries:"
+echo " ZeroMQ: $zeromq_available"
+if test "x$zeromq_available" = "xyes"; then
+ echo " CFLAGS: ${LIBZMQ_CFLAGS}"
+ echo " LIBS: ${LIBZMQ_LIBS}"
+fi
+echo " libevent: $libevent_available"
+if test "x$libevent_available" = "xyes"; then
+ echo " CFLAGS: ${LIBEVENT_CFLAGS}"
+ echo " LIBS: ${LIBEVENT_LIBS}"
+fi
+echo " MessagePack: $message_pack_available"
+if test "x$message_pack_available" = "xyes"; then
+ echo " CFLAGS: ${MESSAGE_PACK_CFLAGS}"
+ echo " LIBS: ${MESSAGE_PACK_LIBS}"
+fi
+echo " mruby: $enable_mruby"
+echo
+
+echo "groonga-httpd:"
+echo " enable: $enable_groonga_httpd"
+if test "$enable_groonga_httpd" = "yes"; then
+ echo " default database path: $GROONGA_HTTPD_DEFAULT_DATABASE_PATH"
+ echo " PCRE: $WITH_PCRE"
+ if test "$WITH_PCRE" = "yes"; then
+ echo " CFLAGS: $PCRE_CFLAGS"
+ echo " LIBS only -L: $PCRE_LIBS_ONLY_L"
+ fi
+fi
+echo
+
+echo "Tools:"
+echo " Sphinx: ${SPHINX_BUILD}"
+echo " lemon: ${LEMON}"
+echo " Ruby: ${RUBY}"
+echo " Cutter: ${CUTTER}"
+echo
+
+echo "For packages:"
+echo " rsync path: ${RSYNC_PATH}"
+echo " Launchpad PGP key: ${LAUNCHPAD_UPLOADER_PGP_KEY}"
+echo " GPG UID: ${GPG_UID}"
+echo
+
+echo "Now type 'make' to build $PACKAGE_NAME $PACKAGE_VERSION!"
diff --git a/storage/mroonga/vendor/groonga/data/CMakeLists.txt b/storage/mroonga/vendor/groonga/data/CMakeLists.txt
new file mode 100644
index 00000000000..003445a27f5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/CMakeLists.txt
@@ -0,0 +1,20 @@
+# Copyright(C) 2012 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+add_subdirectory(html)
+
+if(NOT MRN_GROONGA_BUNDLED)
+ install(FILES groonga.conf DESTINATION "${GRN_CONFIG_DIR}/")
+endif()
diff --git a/storage/mroonga/vendor/groonga/data/Makefile.am b/storage/mroonga/vendor/groonga/data/Makefile.am
new file mode 100644
index 00000000000..96d1452ba1b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/Makefile.am
@@ -0,0 +1,20 @@
+SUBDIRS = \
+ images \
+ html \
+ munin \
+ init.d \
+ logrotate.d \
+ systemd \
+ scripts
+
+pkgsysconfdir = $(sysconfdir)/$(PACKAGE)
+dist_pkgsysconf_DATA = \
+ groonga.conf \
+ synonyms.tsv
+
+httpdconfdir = $(pkgsysconfdir)/httpd
+dist_httpdconf_DATA = \
+ groonga-httpd.conf
+
+EXTRA_DIST = \
+ CMakeLists.txt
diff --git a/storage/mroonga/vendor/groonga/data/account/twitter.gpg.hayashi b/storage/mroonga/vendor/groonga/data/account/twitter.gpg.hayashi
new file mode 100644
index 00000000000..900e7f6a789
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/account/twitter.gpg.hayashi
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/account/twitter.gpg.kou b/storage/mroonga/vendor/groonga/data/account/twitter.gpg.kou
new file mode 100644
index 00000000000..8d4abde7f82
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/account/twitter.gpg.kou
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/account/twitter.gpg.yuki b/storage/mroonga/vendor/groonga/data/account/twitter.gpg.yuki
new file mode 100644
index 00000000000..af7f1aefa4e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/account/twitter.gpg.yuki
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/groonga-httpd.conf.in b/storage/mroonga/vendor/groonga/data/groonga-httpd.conf.in
new file mode 100644
index 00000000000..fb3c816be6e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/groonga-httpd.conf.in
@@ -0,0 +1,57 @@
+worker_processes 1;
+
+# Match this to the file owner of groonga database files if groonga-httpd is
+# run as root.
+user groonga groonga;
+
+events {
+ worker_connections 1024;
+}
+
+http {
+ include mime.types;
+ default_type application/octet-stream;
+ sendfile on;
+ keepalive_timeout 65;
+
+ # The default groonga database path.
+ groonga_database @GROONGA_HTTPD_DEFAULT_DATABASE_PATH@;
+
+ # Create a groonga database automatically if the groonga database doesn't
+ # exist.
+ #
+ # Note that this option is danger when worker_processes is greater than 1.
+ # Because one or more worker processes may create the same groonga database
+ # at the same time. If you can create a groonga database before running
+ # groonga-httpd, you should do it.
+ groonga_database_auto_create on;
+
+ # The default groonga cache limit. The cache limit can be set
+ # for each worker. It can't be set for each groonga database.
+ # groonga_cache_limit 100;
+
+ server {
+ listen 10041;
+ server_name localhost;
+
+ location /d/ {
+ groonga on;
+ # You can disable log for groonga.
+ # groonga_log_path off;
+ # You can disable query log for groonga.
+ # groonga_query_log_path off;
+ # You can custom database path.
+ # groonga_database /path/to/groonga/db;
+ }
+
+ location / {
+ root @GROONGA_HTTPD_DOCUMENT_ROOT@;
+ index index.html;
+ }
+
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root html;
+ }
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/data/groonga.conf b/storage/mroonga/vendor/groonga/data/groonga.conf
new file mode 100644
index 00000000000..e4b538dd7ca
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/groonga.conf
@@ -0,0 +1,11 @@
+# encoding = utf-8
+# log-level = 1
+# address = 0.0.0.0
+# port = 10041
+# server-id = groonga.example.com
+# max-threads = 2
+# admin-html-path = /usr/share/groonga/admin_html
+# protocol = http
+# log-path = /var/log/groonga/groonga.log
+# query-log-path = /var/log/groonga/query.log
+# cache-limit = 100
diff --git a/storage/mroonga/vendor/groonga/data/html/CMakeLists.txt b/storage/mroonga/vendor/groonga/data/html/CMakeLists.txt
new file mode 100644
index 00000000000..1c96d8915ae
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright(C) 2012 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+if(NOT MRN_GROONGA_BUNDLED)
+ install(DIRECTORY admin DESTINATION "${GRN_DATA_DIR}/html")
+endif()
diff --git a/storage/mroonga/vendor/groonga/data/html/Makefile.am b/storage/mroonga/vendor/groonga/data/html/Makefile.am
new file mode 100644
index 00000000000..c6274225d77
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/Makefile.am
@@ -0,0 +1,9 @@
+htmldir = $(pkgdatadir)/html
+
+include files.am
+
+EXTRA_DIST = \
+ CMakeLists.txt
+
+update-files:
+ cd $(srcdir); ./update-files.sh > files.am
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/groonga-admin.css b/storage/mroonga/vendor/groonga/data/html/admin/css/groonga-admin.css
new file mode 100644
index 00000000000..1b092eb4a96
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/groonga-admin.css
@@ -0,0 +1,120 @@
+body {
+ margin: 0;
+ padding: 0;
+ background: white;
+ color: #333;
+ font-size:11px;
+}
+
+form {
+ margin: 0;
+ padding: 0;
+}
+
+img {
+ border: none;
+}
+
+a {
+ color: #2e6e9e;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+input {
+ border: 1px solid;
+}
+
+input[type="text"] {
+ width: 12em;
+}
+
+#header {
+ height: 80px;
+ background: url(../images/groonga.png) no-repeat left center;
+}
+
+#locale-list {
+ float: right;
+ width: 4em;
+ list-style: none;
+}
+
+#locale-list li {
+ float: left;
+ width: 2em;
+}
+
+#side-menu {
+ width: 12em;
+}
+
+#side-menu ul {
+ padding-left: 1.5em;
+}
+
+#left-column {
+ height: 100%;
+ vertical-align: top;
+}
+
+#right-column {
+ width: 100%;
+ height: 100%;
+ vertical-align: top;
+}
+
+#database-tabs {
+}
+
+#table-tabs {
+ display: none;
+}
+
+table.records {
+ border: #333 1px solid;
+ border-collapse: collapse;
+ border-spacing: 0;
+ margin: 0.2em;
+}
+
+table.records th {
+ color: white;
+ background-color: #5c9ccc;
+ border: #333 solid;
+ border-width: 0 0 1px 1px;
+ text-align: center;
+ white-space: nowrap;
+ padding: 0.3em;
+}
+
+table.records td {
+ background-color: white;
+ border: #333 solid;
+ border-width: 0 0 1px 1px;
+ padding: 0.3em;
+}
+
+span.pager {
+ display: inline-block;
+ border: #333 1px solid;
+ padding: 0.1em;
+ margin: 0.1em;
+ background-color: white;
+}
+
+span.pager-current {
+ display: inline-block;
+ border: #333 1px solid;
+ padding: 0.1em;
+ margin: 0.1em;
+ background-color: #333;
+}
+
+span.pager-current a {
+ color: white;
+ font-weight: bold;
+}
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_flat_0_aaaaaa_40x100.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_flat_0_aaaaaa_40x100.png
new file mode 100644
index 00000000000..5b5dab2ab7b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_flat_0_aaaaaa_40x100.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_flat_55_fbec88_40x100.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_flat_55_fbec88_40x100.png
new file mode 100644
index 00000000000..47acaadd737
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_flat_55_fbec88_40x100.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_glass_75_d0e5f5_1x400.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_glass_75_d0e5f5_1x400.png
new file mode 100644
index 00000000000..9fb564f8d0a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_glass_75_d0e5f5_1x400.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_glass_85_dfeffc_1x400.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_glass_85_dfeffc_1x400.png
new file mode 100644
index 00000000000..014951529c3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_glass_85_dfeffc_1x400.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_glass_95_fef1ec_1x400.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_glass_95_fef1ec_1x400.png
new file mode 100644
index 00000000000..4443fdc1a15
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_glass_95_fef1ec_1x400.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png
new file mode 100644
index 00000000000..0cdbda36a55
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.png
new file mode 100644
index 00000000000..4f3faf8aa8b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png
new file mode 100644
index 00000000000..38c38335d09
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_217bc0_256x240.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_217bc0_256x240.png
new file mode 100644
index 00000000000..6f4bd87c041
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_217bc0_256x240.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_2e83ff_256x240.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_2e83ff_256x240.png
new file mode 100644
index 00000000000..09d1cdc856c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_2e83ff_256x240.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_469bdd_256x240.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_469bdd_256x240.png
new file mode 100644
index 00000000000..bd2cf079add
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_469bdd_256x240.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_6da8d5_256x240.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_6da8d5_256x240.png
new file mode 100644
index 00000000000..3d6f567f4d3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_6da8d5_256x240.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_cd0a0a_256x240.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_cd0a0a_256x240.png
new file mode 100644
index 00000000000..2ab019b73ec
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_cd0a0a_256x240.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_d8e7f3_256x240.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_d8e7f3_256x240.png
new file mode 100644
index 00000000000..ad2dc6f9dbe
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_d8e7f3_256x240.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_f9bd01_256x240.png b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_f9bd01_256x240.png
new file mode 100644
index 00000000000..c7c53cb1190
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/images/ui-icons_f9bd01_256x240.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/jquery-ui-1.8.18.custom.css b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/jquery-ui-1.8.18.custom.css
new file mode 100644
index 00000000000..8015f1d654d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/css/redmond/jquery-ui-1.8.18.custom.css
@@ -0,0 +1,565 @@
+/*
+ * jQuery UI CSS Framework 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ */
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden { display: none; }
+.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
+.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.ui-helper-clearfix:before, .ui-helper-clearfix:after { content: ""; display: table; }
+.ui-helper-clearfix:after { clear: both; }
+.ui-helper-clearfix { zoom: 1; }
+.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+
+
+/*
+ * jQuery UI CSS Framework 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ *
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Lucida%20Grande,%20Lucida%20Sans,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=5px&bgColorHeader=5c9ccc&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=55&borderColorHeader=4297d7&fcHeader=ffffff&iconColorHeader=d8e7f3&bgColorContent=fcfdfd&bgTextureContent=06_inset_hard.png&bgImgOpacityContent=100&borderColorContent=a6c9e2&fcContent=222222&iconColorContent=469bdd&bgColorDefault=dfeffc&bgTextureDefault=02_glass.png&bgImgOpacityDefault=85&borderColorDefault=c5dbec&fcDefault=2e6e9e&iconColorDefault=6da8d5&bgColorHover=d0e5f5&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=79b7e7&fcHover=1d5987&iconColorHover=217bc0&bgColorActive=f5f8f9&bgTextureActive=06_inset_hard.png&bgImgOpacityActive=100&borderColorActive=79b7e7&fcActive=e17009&iconColorActive=f9bd01&bgColorHighlight=fbec88&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=55&borderColorHighlight=fad42e&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
+ */
+
+
+/* Component containers
+----------------------------------*/
+.ui-widget { font-family: Lucida Grande, Lucida Sans, Arial, sans-serif; font-size: 1.1em; }
+.ui-widget .ui-widget { font-size: 1em; }
+.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Lucida Grande, Lucida Sans, Arial, sans-serif; font-size: 1em; }
+.ui-widget-content { border: 1px solid #a6c9e2; background: #fcfdfd url(images/ui-bg_inset-hard_100_fcfdfd_1x100.png) 50% bottom repeat-x; color: #222222; }
+.ui-widget-content a { color: #222222; }
+.ui-widget-header { border: 1px solid #4297d7; background: #5c9ccc url(images/ui-bg_gloss-wave_55_5c9ccc_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
+.ui-widget-header a { color: #ffffff; }
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #c5dbec; background: #dfeffc url(images/ui-bg_glass_85_dfeffc_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #2e6e9e; }
+.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #2e6e9e; text-decoration: none; }
+.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #79b7e7; background: #d0e5f5 url(images/ui-bg_glass_75_d0e5f5_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1d5987; }
+.ui-state-hover a, .ui-state-hover a:hover { color: #1d5987; text-decoration: none; }
+.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #79b7e7; background: #f5f8f9 url(images/ui-bg_inset-hard_100_f5f8f9_1x100.png) 50% 50% repeat-x; font-weight: bold; color: #e17009; }
+.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #e17009; text-decoration: none; }
+.ui-widget :active { outline: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fad42e; background: #fbec88 url(images/ui-bg_flat_55_fbec88_40x100.png) 50% 50% repeat-x; color: #363636; }
+.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
+.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; }
+.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }
+.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }
+.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
+.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_469bdd_256x240.png); }
+.ui-widget-content .ui-icon {background-image: url(images/ui-icons_469bdd_256x240.png); }
+.ui-widget-header .ui-icon {background-image: url(images/ui-icons_d8e7f3_256x240.png); }
+.ui-state-default .ui-icon { background-image: url(images/ui-icons_6da8d5_256x240.png); }
+.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_217bc0_256x240.png); }
+.ui-state-active .ui-icon {background-image: url(images/ui-icons_f9bd01_256x240.png); }
+.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
+.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
+
+/* positioning */
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-off { background-position: -96px -144px; }
+.ui-icon-radio-on { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 5px; -webkit-border-top-left-radius: 5px; -khtml-border-top-left-radius: 5px; border-top-left-radius: 5px; }
+.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 5px; -webkit-border-top-right-radius: 5px; -khtml-border-top-right-radius: 5px; border-top-right-radius: 5px; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 5px; -webkit-border-bottom-left-radius: 5px; -khtml-border-bottom-left-radius: 5px; border-bottom-left-radius: 5px; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 5px; -webkit-border-bottom-right-radius: 5px; -khtml-border-bottom-right-radius: 5px; border-bottom-right-radius: 5px; }
+
+/* Overlays */
+.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
+.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
+ * jQuery UI Resizable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizable#theming
+ */
+.ui-resizable { position: relative;}
+.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; }
+.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
+.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
+.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
+.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
+.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
+ * jQuery UI Selectable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectable#theming
+ */
+.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
+/*
+ * jQuery UI Accordion 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion#theming
+ */
+/* IE/Win - Fix animation bug - #4615 */
+.ui-accordion { width: 100%; }
+.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.ui-accordion .ui-accordion-li-fix { display: inline; }
+.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
+.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
+.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
+.ui-accordion .ui-accordion-content-active { display: block; }
+/*
+ * jQuery UI Autocomplete 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete#theming
+ */
+.ui-autocomplete { position: absolute; cursor: default; }
+
+/* workarounds */
+* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
+
+/*
+ * jQuery UI Menu 1.8.18
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Menu#theming
+ */
+.ui-menu {
+ list-style:none;
+ padding: 2px;
+ margin: 0;
+ display:block;
+ float: left;
+}
+.ui-menu .ui-menu {
+ margin-top: -3px;
+}
+.ui-menu .ui-menu-item {
+ margin:0;
+ padding: 0;
+ zoom: 1;
+ float: left;
+ clear: left;
+ width: 100%;
+}
+.ui-menu .ui-menu-item a {
+ text-decoration:none;
+ display:block;
+ padding:.2em .4em;
+ line-height:1.5;
+ zoom:1;
+}
+.ui-menu .ui-menu-item a.ui-state-hover,
+.ui-menu .ui-menu-item a.ui-state-active {
+ font-weight: normal;
+ margin: -1px;
+}
+/*
+ * jQuery UI Button 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button#theming
+ */
+.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: hidden; *overflow: visible; } /* the overflow property removes extra width in IE */
+.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
+button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
+.ui-button-icons-only { width: 3.4em; }
+button.ui-button-icons-only { width: 3.7em; }
+
+/*button text element */
+.ui-button .ui-button-text { display: block; line-height: 1.4; }
+.ui-button-text-only .ui-button-text { padding: .4em 1em; }
+.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
+.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
+.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
+.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
+/* no icon support for input elements, provide padding by default */
+input.ui-button { padding: .4em 1em; }
+
+/*button icon element(s) */
+.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
+.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
+.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
+.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+
+/*button sets*/
+.ui-buttonset { margin-right: 7px; }
+.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
+
+/* workarounds */
+button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
+/*
+ * jQuery UI Dialog 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog#theming
+ */
+.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
+.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
+.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
+.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
+.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
+.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.ui-draggable .ui-dialog-titlebar { cursor: move; }
+/*
+ * jQuery UI Slider 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider#theming
+ */
+.ui-slider { position: relative; text-align: left; }
+.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
+
+.ui-slider-horizontal { height: .8em; }
+.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.ui-slider-vertical { width: .8em; height: 100px; }
+.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.ui-slider-vertical .ui-slider-range-max { top: 0; }/*
+ * jQuery UI Tabs 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs#theming
+ */
+.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
+.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
+.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
+.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
+.ui-tabs .ui-tabs-hide { display: none !important; }
+/*
+ * jQuery UI Datepicker 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker#theming
+ */
+.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
+.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.ui-datepicker .ui-datepicker-prev { left:2px; }
+.ui-datepicker .ui-datepicker-next { right:2px; }
+.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
+.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
+.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.ui-datepicker select.ui-datepicker-month,
+.ui-datepicker select.ui-datepicker-year { width: 49%;}
+.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
+.ui-datepicker td { border: 0; padding: 1px; }
+.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi { width:auto; }
+.ui-datepicker-multi .ui-datepicker-group { float:left; }
+.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
+
+/* RTL support */
+.ui-datepicker-rtl { direction: rtl; }
+.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.ui-datepicker-cover {
+ display: none; /*sorry for IE5*/
+ display/**/: block; /*sorry for IE5*/
+ position: absolute; /*must have*/
+ z-index: -1; /*must have*/
+ filter: mask(); /*must have*/
+ top: -4px; /*must have*/
+ left: -4px; /*must have*/
+ width: 200px; /*must have*/
+ height: 200px; /*must have*/
+}/*
+ * jQuery UI Progressbar 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar#theming
+ */
+.ui-progressbar { height:2em; text-align: left; overflow: hidden; }
+.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } \ No newline at end of file
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/favicon.ico b/storage/mroonga/vendor/groonga/data/html/admin/favicon.ico
new file mode 100644
index 00000000000..5fd703b998f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/favicon.ico
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/favicon.png b/storage/mroonga/vendor/groonga/data/html/admin/favicon.png
new file mode 100644
index 00000000000..1ec3555a100
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/favicon.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/favicon.svg b/storage/mroonga/vendor/groonga/data/html/admin/favicon.svg
new file mode 100644
index 00000000000..81cbbb68997
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/favicon.svg
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="60.000706"
+ height="60.001022"
+ id="svg5342"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="favicon.svg"
+ inkscape:export-filename="favicon.png"
+ inkscape:export-xdpi="24"
+ inkscape:export-ydpi="24">
+ <defs
+ id="defs5344">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_9_"
+ id="linearGradient5521"
+ gradientUnits="userSpaceOnUse"
+ x1="470.15039"
+ y1="437.05859"
+ x2="470.15039"
+ y2="497.05859" />
+ <linearGradient
+ id="SVGID_9_"
+ gradientUnits="userSpaceOnUse"
+ x1="470.15039"
+ y1="437.05859"
+ x2="470.15039"
+ y2="497.05859">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop4050" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop4052" />
+ </linearGradient>
+ <linearGradient
+ y2="497.05859"
+ x2="470.15039"
+ y1="437.05859"
+ x1="470.15039"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5566"
+ xlink:href="#SVGID_9_"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_9_"
+ id="linearGradient5599"
+ gradientUnits="userSpaceOnUse"
+ x1="470.15039"
+ y1="437.05859"
+ x2="470.15039"
+ y2="497.05859" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="61.872887"
+ inkscape:cy="42.401151"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2773"
+ inkscape:window-y="302"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata5347">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-343.75609,-530.41738)">
+ <g
+ id="g5587">
+ <polygon
+ style="fill:url(#linearGradient5599)"
+ id="polygon4054"
+ points="440.15,437.059 440.15,497.059 466.012,497.059 500.15,497.059 500.15,462.92 500.15,437.059 "
+ transform="translate(-96.393911,93.359403)" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ id="path4107"
+ d="m 397.29009,567.3004 c 7.502,-7.505 9.8,-20.401 -0.335,-30.538 -4.224,-4.222 -9.514,-6.337 -14.8,-6.345 h -0.005 c -5.286,-0.007 -10.567,2.093 -14.774,6.299 -2.522,2.52 -4.434,5.76 -5.428,9.33 -6.49,-0.099 -13.001,2.411 -18.191,7.53 v 36.479 c 0.114,0.121 0.227,0.242 0.344,0.361 h 25.518 11.027 c 4.434,-4.717 7.233,-11.018 7.295,-17.672 3.385,-0.898 6.606,-2.7 9.349,-5.444 z m -13.748,-2.759 c -1.121,1.873 -0.997,2.089 -0.949,5.602 0.062,3.979 -1.437,8.102 -5.189,11.854 -5.736,5.737 -16.326,7.017 -24.119,-0.776 -6.139,-6.14 -9.021,-16.475 -0.84,-24.658 3.038,-3.034 7.406,-4.827 11.998,-4.745 3.193,0.055 3.564,0.174 5.621,-0.76 2.058,-0.938 1.166,-3.019 2.396,-6.692 0.6,-1.792 1.666,-3.551 3.332,-5.224 4.451,-4.448 12.643,-5.469 18.643,0.531 4.887,4.887 6.96,12.677 0.572,19.065 -1.441,1.444 -3.284,2.509 -5.319,3.087 -3.266,0.937 -5.023,0.838 -6.146,2.716 z" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/images/groonga.png b/storage/mroonga/vendor/groonga/data/html/admin/images/groonga.png
new file mode 100644
index 00000000000..39a05b7e9e4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/images/groonga.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/images/groonga.svg b/storage/mroonga/vendor/groonga/data/html/admin/images/groonga.svg
new file mode 100644
index 00000000000..cecc4e0ee59
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/images/groonga.svg
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="232.17999"
+ height="73.112274"
+ id="svg3635"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="groonga.svg"
+ inkscape:export-filename="groonga.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs3637">
+ <linearGradient
+ gradientTransform="translate(-419.62671,208.8676)"
+ id="SVGID_1_"
+ gradientUnits="userSpaceOnUse"
+ x1="223.6167"
+ y1="76.3564"
+ x2="223.6167"
+ y2="137.38029">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop160" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop162" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_1_"
+ id="linearGradient3693"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-419.62671,208.8676)"
+ x1="223.6167"
+ y1="76.3564"
+ x2="223.6167"
+ y2="137.38029" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="202.10766"
+ inkscape:cy="3.8593771"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2411"
+ inkscape:window-y="568"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata3640">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-229.62429,-318.66319)">
+ <g
+ id="g3685"
+ transform="translate(520,37.142857)">
+ <path
+ id="path149"
+ d="m -228.72771,306.3346 c -1.336,-0.29 -2.281,-0.448 -3.442,-0.448 -2.847,0 -5.33,0.831 -7.391,2.468 -0.091,0.06 -0.192,0.14 -0.303,0.245 -0.231,0.197 -0.452,0.406 -0.668,0.621 -1.478,1.314 -2.76,1.952 -2.76,-1.235 v -0.666 c 0,-0.404 -0.327,-0.732 -0.732,-0.732 h -2.101 c -0.198,0 -0.388,0.081 -0.525,0.223 -0.139,0.143 -0.212,0.335 -0.206,0.532 0.105,3.333 0.215,6.778 0.215,9.621 v 18.75 c 0,0.404 0.328,0.731 0.732,0.731 h 2.209 c 0.404,0 0.732,-0.328 0.732,-0.731 v -9.914 c 0,-1.37 0.107,-3.805 0.309,-4.813 1.569,-7.797 4.982,-11.588 10.435,-11.588 0.979,0 1.881,0.165 2.874,0.363 0.048,0.011 0.096,0.015 0.144,0.015 0.148,0 0.294,-0.045 0.417,-0.131 0.163,-0.113 0.273,-0.288 0.306,-0.484 l 0.323,-1.994 c 0.062,-0.384 -0.187,-0.752 -0.568,-0.833 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path151"
+ d="m -139.68071,307.9266 c -2.512,-1.774 -5.292,-2.04 -6.726,-2.04 -2.91,0 -5.648,0.912 -7.822,2.495 -0.005,0.004 -0.01,0.005 -0.014,0.009 -1.004,0.648 -2.627,0.909 -2.618,-0.625 l 0,-0.021 c 0.002,-0.107 -0.003,-0.199 -0.013,-0.277 l -0.008,-0.182 c -0.019,-0.391 -0.34,-0.698 -0.731,-0.698 h -2.102 c -0.203,0 -0.396,0.084 -0.535,0.232 -0.138,0.148 -0.209,0.347 -0.195,0.549 0.152,2.28 0.214,4.171 0.214,6.524 v 21.821 c 0,0.404 0.328,0.731 0.731,0.731 h 2.209 c 0.404,0 0.732,-0.328 0.732,-0.731 v -17.511 c 0,-0.813 0.253,-1.761 0.49,-2.377 0.004,-0.008 0.006,-0.018 0.01,-0.026 1.043,-3.042 4.224,-6.12 8.417,-6.475 3.236,0.002 8.327,2.358 9.024,8.226 0.016,0.15 0.03,0.299 0.042,0.444 0.004,0.049 0.008,0.099 0.011,0.149 0.005,0.07 0.009,0.139 0.014,0.209 0.009,0.19 0.015,0.384 0.015,0.582 0,0.335 0.006,0.638 0.017,0.911 v 15.869 c 0,0.404 0.328,0.731 0.733,0.731 h 2.208 c 0.404,0 0.732,-0.328 0.732,-0.731 v -16.272 c 0,-5.375 -1.627,-9.248 -4.835,-11.516 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path153"
+ d="m -63.456714,333.2686 c -0.133,-0.113 -0.301,-0.174 -0.473,-0.174 -0.04,0 -0.08,0.003 -0.12,0.01 -0.587,0.098 -0.965,0.098 -1.442,0.098 -1.147,0 -2.177,-0.335 -2.177,-4.387 v -9.913 c 0,-2.343 -0.179,-5.368 -1.583,-7.972 -1.822,-3.382 -5.181,-5.098 -9.978,-5.098 -2.475,0 -6.217,0.516 -9.99,2.974 -0.315,0.205 -0.422,0.615 -0.25,0.949 l 0.861,1.67 c 0.098,0.189 0.272,0.326 0.478,0.375 0.058,0.014 0.116,0.021 0.173,0.021 0.151,0 0.299,-0.047 0.424,-0.135 2.314,-1.639 5.186,-2.504 8.305,-2.504 3.995,0 6.004,1.854 7,3.939 l 0,0 c 1.004,2.221 -0.561,4.404 -2.285,4.584 -0.085,0.005 -0.17,0.008 -0.254,0.013 -0.014,0 -0.028,0.001 -0.042,0 -0.055,-10e-4 -0.101,0.003 -0.143,0.009 -10.978,0.626 -16.538,4.313 -16.538,10.98 0,2.027 0.803,4.057 2.202,5.569 1.233,1.333 3.56,2.921 7.635,2.921 3.471,0 6.271,-1.112 8.325,-2.472 0.061,-0.031 0.13,-0.076 0.211,-0.141 0.023,-0.017 0.042,-0.031 0.064,-0.048 0.128,-0.088 0.256,-0.177 0.378,-0.268 1.275,-0.825 1.969,-0.351 2.623,0.504 0.002,0.002 0.006,0.005 0.008,0.007 0.84,1.14 2.161,1.718 3.962,1.718 0.846,0 1.591,-0.094 2.344,-0.294 0.32,-0.085 0.543,-0.375 0.543,-0.707 v -1.67 c -0.002,-0.214 -0.097,-0.419 -0.261,-0.558 z m -18.252,0.527 c -3.042,0 -6.111,-1.723 -6.111,-5.572 0,-4.863 5.936,-6.58 12.167,-7.043 2.32,-0.043 4.097,2.162 4.311,4.575 v 0.989 c -0.062,0.647 -0.246,1.289 -0.568,1.888 -0.162,0.263 -0.356,0.556 -0.589,0.864 -0.04,0.047 -0.075,0.093 -0.103,0.134 -1.442,1.848 -4.223,4.165 -9.107,4.165 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path155"
+ d="m -257.43671,306.5866 h -2.047 c -0.392,0 -0.715,0.311 -0.731,0.703 -0.065,1.618 -0.892,1.836 -2.704,0.766 -0.01,-0.006 -0.018,-0.014 -0.026,-0.019 -0.178,-0.119 -0.361,-0.231 -0.545,-0.339 -0.011,-0.007 -0.021,-0.012 -0.032,-0.02 -0.088,-0.059 -0.169,-0.104 -0.245,-0.14 -1.982,-1.096 -4.306,-1.65 -6.923,-1.65 -3.739,0 -7.428,1.548 -10.122,4.25 -2.083,2.087 -4.564,5.826 -4.564,11.784 0,3.818 1.347,7.413 3.792,10.122 2.594,2.875 6.234,4.458 10.249,4.458 3.11,0 5.514,-0.819 7.334,-1.897 0.004,-0.002 0.008,-0.003 0.012,-0.005 1.953,-0.873 3.601,-2.182 3.135,2.232 -0.875,5.678 -4.519,8.765 -10.482,8.765 -4.175,0 -7.31,-1.404 -9.204,-2.583 -0.117,-0.072 -0.251,-0.11 -0.387,-0.11 -0.061,0 -0.122,0.008 -0.182,0.023 -0.193,0.049 -0.358,0.176 -0.456,0.35 l -0.969,1.725 c -0.189,0.336 -0.085,0.761 0.237,0.972 2.852,1.863 6.909,2.975 10.853,2.975 2.691,0 9.293,-0.625 12.494,-6.412 1.384,-2.481 2.03,-5.968 2.03,-10.968 v -17.457 c 0,-2.474 0.07,-4.679 0.214,-6.738 0.014,-0.202 -0.057,-0.402 -0.195,-0.549 -0.139,-0.153 -0.333,-0.238 -0.536,-0.238 z m -16.892,26.028 c -0.605,-0.24 -7.609,-3.216 -7.013,-11.811 0.551,-7.946 5.723,-10.459 6.518,-10.798 1.278,-0.5 2.7,-0.769 4.241,-0.769 1.664,0 3.112,0.327 4.354,0.873 6.846,3.871 7.696,16.453 0.885,21.467 -1.592,0.999 -3.437,1.573 -5.346,1.573 -1.342,0 -2.552,-0.194 -3.639,-0.535 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path157"
+ d="m -99.588714,306.5866 h -2.046996 c -0.393,0 -0.716,0.311 -0.731,0.703 -0.066,1.618 -0.892,1.836 -2.703,0.766 -0.009,-0.005 -0.018,-0.014 -0.027,-0.019 -0.178,-0.119 -0.362,-0.231 -0.546,-0.339 -0.011,-0.008 -0.02,-0.014 -0.031,-0.02 -0.087,-0.059 -0.168,-0.104 -0.244,-0.139 -1.982,-1.097 -4.307,-1.651 -6.924,-1.651 -3.739,0 -7.429,1.548 -10.122,4.25 -2.082,2.087 -4.564,5.826 -4.564,11.784 0,3.818 1.346,7.413 3.792,10.122 2.595,2.875 6.234,4.458 10.248,4.458 3.111,0 5.514,-0.819 7.335,-1.897 0.004,-0.002 0.007,-0.003 0.012,-0.005 1.954,-0.874 3.603,-2.183 3.135,2.233 -0.875,5.678 -4.519,8.764 -10.482,8.764 -4.175,0 -7.31,-1.404 -9.204,-2.583 -0.118,-0.072 -0.251,-0.11 -0.387,-0.11 -0.06,0 -0.122,0.008 -0.182,0.023 -0.193,0.049 -0.358,0.176 -0.456,0.35 l -0.97,1.725 c -0.189,0.336 -0.085,0.761 0.237,0.972 2.853,1.863 6.91,2.975 10.854,2.975 2.689,0 9.293,-0.625 12.493,-6.412 1.384996,-2.481 2.030996,-5.968 2.030996,-10.968 v -17.457 c 0,-2.474 0.07,-4.679 0.214,-6.738 0.014,-0.202 -0.057,-0.402 -0.195,-0.549 -0.139,-0.153 -0.332,-0.238 -0.536,-0.238 z m -16.891996,26.028 c -0.605,-0.24 -7.609,-3.216 -7.013,-11.811 0.551,-7.94 5.716,-10.455 6.517,-10.798 1.278,-0.5 2.701,-0.769 4.242,-0.769 1.665,0 3.113,0.327 4.354,0.873 6.845,3.871 7.696,16.453 0.884,21.467 -1.592,1 -3.436,1.573 -5.345,1.573 -1.343,0 -2.553,-0.194 -3.639,-0.535 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path164"
+ d="m -198.36371,292.3616 c -2.337,2.336 -4.109,5.342 -5.032,8.654 -6.076,-0.093 -12.171,2.273 -17.007,7.11 -8.142,8.141 -10.674,22.913 0.667,34.253 9.542,9.543 23.793,9.687 33.145,0.333 4.421,-4.424 7.236,-10.506 7.297,-16.94 3.14,-0.835 6.125,-2.507 8.67,-5.049 6.956,-6.959 9.086,-18.919 -0.311,-28.318 -7.833,-7.83 -19.626,-7.847 -27.429,-0.043 z m 25.622,20.422 c -1.335,1.338 -3.045,2.324 -4.932,2.864 -3.027,0.867 -4.657,0.777 -5.698,2.515 -1.041,1.735 -0.926,1.94 -0.88,5.194 0.056,3.689 -1.333,7.513 -4.813,10.995 -5.32,5.318 -15.14,6.504 -22.366,-0.721 -5.693,-5.694 -8.365,-15.278 -0.778,-22.865 2.816,-2.813 6.866,-4.477 11.124,-4.402 2.961,0.053 3.306,0.161 5.212,-0.704 1.909,-0.869 1.082,-2.799 2.222,-6.205 0.556,-1.66 1.545,-3.294 3.092,-4.844 4.126,-4.126 11.721,-5.072 17.285,0.491 4.533,4.535 6.455,11.759 0.532,17.682 z"
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient3693)" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/images/loading.gif b/storage/mroonga/vendor/groonga/data/html/admin/images/loading.gif
new file mode 100644
index 00000000000..2ce2ac5f59d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/images/loading.gif
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/index.html b/storage/mroonga/vendor/groonga/data/html/admin/index.html
new file mode 100644
index 00000000000..7f9cc8740f6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/index.html
@@ -0,0 +1,297 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="ja">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta http-equiv="Content-Style-Type" content="text/css">
+<meta http-equiv="Content-Script-Type" content="text/javascript">
+<meta name="robots" content="noindex,nofollow,noarchive">
+<title>groonga admin</title>
+<link rel="shortcut icon" href="favicon.ico">
+<link rel="icon" href="favicon.png">
+<link rel="stylesheet" type="text/css" href="css/groonga-admin.css">
+<link rel="stylesheet" type="text/css" href="css/redmond/jquery-ui-1.8.18.custom.css">
+<script type="text/javascript" src="js/jquery-1.7.2.min.js"></script>
+<script type="text/javascript" src="js/jquery-ui-1.8.18.custom.min.js"></script>
+<script type="text/javascript" src="js/jquery.flot-0.7.min.js"></script>
+<script type="text/javascript" src="js/groonga-admin.js"></script>
+</head>
+<body>
+<div id="header">
+ <ul id="locale-list">
+ <li>en</li>
+ <li><a href="index.ja.html">ja</a>
+ </ul>
+</div>
+<div id="body">
+ <table>
+ <tr>
+ <td id="left-column">
+ <div id="side-menu">
+ <h2>List of view</h2>
+ <ul id="side-menu-view-list">
+ <li><a href="#side-menu-summary" id="side-menu-summary">Summary</a></li>
+ <li><a href="#side-menu-suggest" id="side-menu-suggest">Suggest</a></li>
+ </ul>
+ <h2>List of table</h2>
+ <ul id="side-menu-tablelist" />
+ </div>
+ </td>
+
+ <td id="right-column">
+ <!-- database view -->
+ <div id="database-tabs">
+ <ul>
+ <li><a href="#database-tab-summary">Summary</a></li>
+ <li><a href="#database-tab-tablelist" id="tab-tablelist-link">List table</a></li>
+ <li><a href="#database-tab-createtable">Create table</a></li>
+ </ul>
+ <div id="database-tab-summary">
+ <p>
+ Groonga administration tool.
+ </p>
+ <ul>
+ <li>Start time: <span id="status-starttime"></span></li>
+ <li>Uptime: <span id="status-uptime"></span></li>
+ <li>Number of query: <span id="status-n-queries"></span></li>
+ <li>Cache hit rate: <span id="status-cache-hit-rate"></span></li>
+ </ul>
+ <div id="throughput-chart" style="height: 300px; max-width: 500px;">
+ </div>
+ </div>
+ <div id="database-tab-tablelist">
+ <div id="tab-tablelist-table">
+ </div>
+ <input type="button" id="tablelist-remove-table" value="Remove table">
+ </div>
+ <div id="database-tab-createtable">
+ <table>
+ <tr>
+ <td>
+ <label for="createtable-name">Table name</label>
+ </td>
+ <td>
+ <input type="text" id="createtable-name">
+ </td>
+ </tr>
+ <tr>
+ <td>
+ Primary key
+ </td>
+ <td>
+ <label for="createtable-key-type">Key type:</label>
+ <select id="createtable-key-type">
+ <optgroup label="Built-in types" id="createtable-key-type-builtin">
+ </optgroup>
+ <optgroup label="Key of Table" id="createtable-key-type-table">
+ </optgroup>
+ </select>
+ <label for="createtable-key-index">Key index types:</label>
+ <select id="createtable-key-index">
+ <option value="GRN_OBJ_TABLE_PAT_KEY">Patricia trie</option>
+ <option value="GRN_OBJ_TABLE_HASH_KEY">Hash table</option>
+ <option value="GRN_OBJ_TABLE_NO_KEY">No key</option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ Flags
+ </td>
+ <td id="createtable-flags">
+ <input type="checkbox" value="GRN_OBJ_PERSISTENT" checked>Persistent</input>
+ <input type="checkbox" value="GRN_OBJ_KEY_NORMALIZE">Normalize key</input>
+ <input type="checkbox" value="GRN_OBJ_KEY_WITH_SIS">Key with suffix</input>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ Value types
+ </td>
+ <td>
+ <select id="createtable-value-type">
+ <optgroup label="Built-in types" id="createtable-value-type-builtin">
+ </optgroup>
+ <optgroup label="Use table as value type" id="createtable-value-type-table">
+ </optgroup>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ Default tokenizer
+ </td>
+ <td>
+ <select id="createtable-default-tokenizer">
+ <optgroup label="Built-in tokenizer" id="createtable-default-tokenizer-builtin">
+ </optgroup>
+ </select>
+ </td>
+ </tr>
+ </table>
+ <input type="button" id="createtable-add-table" value="Create table">
+ </div>
+ </div>
+
+ <!-- table view -->
+ <div id="table-tabs">
+ <ul>
+ <li><a href="#table-tab-recordlist" id="tab-recordlist-link">List records</a></li>
+ <li><a href="#table-tab-columnlist" id="tab-columnlist-link">List columns</a></li>
+ <li><a href="#table-tab-createrecord" id="tab-createrecord-link">Create record</a></li>
+ <li><a href="#table-tab-createcolumn">Create columns</a></li>
+ </ul>
+ <div id="table-tab-recordlist">
+ <input type="checkbox" id="table-tab-recordlist-full-checkbox" /><label for="table-tab-recordlist-full-checkbox">Advanced search</label>
+ <form id="tab-recordlist-form">
+ <div id="table-tab-recordlist-form-simple">
+ <label for="tab-recordlist-simplequery">Query: </label><input type="text" id="tab-recordlist-simplequery">
+ <select id="tab-recordlist-simplequerytype">
+ <option value="query" data-placeholder="e.g.)column:@value">query</option>
+ <option value="filter" data-placeholder="e.g.)column == &quot;value&quot;">filter</option>
+ </select>
+ <input type="checkbox" id="tab-recordlist-incremental" /><label for="tab-recordlist-incremental" id="tab-recordlist-incremental-label">Incremental search</label>
+ </div>
+ <div id="table-tab-recordlist-form-full">
+ <table>
+ <tr><td>match_columns</td><td><input type="text" id="tab-recordlist-match_columns" /></td></tr>
+ <tr><td>query</td><td><input type="text" id="tab-recordlist-query" /></td></tr>
+ <tr><td>filter</td><td><input type="text" id="tab-recordlist-filter" /></td></tr>
+ <tr><td>scorer</td><td><input type="text" id="tab-recordlist-scorer" /></td></tr>
+ <tr><td>sortby</td><td><input type="text" id="tab-recordlist-sortby" /></td></tr>
+ <tr><td>output_columns</td><td><input type="text" id="tab-recordlist-output_columns" /></td></tr>
+ <tr><td>offset</td><td><input type="text" id="tab-recordlist-offset" /></td></tr>
+ <tr><td>limit</td><td><input type="text" id="tab-recordlist-limit" /></td></tr>
+ <tr><td>drilldown</td><td><input type="text" id="tab-recordlist-drilldown" /></td></tr>
+ <tr><td>drilldown_sortby</td><td><input type="text" id="tab-recordlist-drilldown_sortby" /></td></tr>
+ <tr><td>drilldown_output_columns</td><td><input type="text" id="tab-recordlist-drilldown_output_columns" /></td></tr>
+ <tr><td>drilldown_offset</td><td><input type="text" id="tab-recordlist-drilldown_offset" /></td></tr>
+ <tr><td>drilldown_limit</td><td><input type="text" id="tab-recordlist-drilldown_limit" /></td></tr>
+ </table>
+ </div>
+ <input type="submit" id="tab-recordlist-submit" value="Search"/>
+ </form>
+ <div id="tab-recordlist-table">
+ </div>
+ <input type="button" id="recordlist-remove-record" value="Remove records">
+ </div>
+ <div id="table-tab-columnlist">
+ <div id="tab-columnlist-table">
+ </div>
+ <input type="button" id="columnlist-remove-column" value="Remove columns">
+ </div>
+ <div id="table-tab-createrecord">
+ <table id="table-createrecord">
+ </table>
+ <input type="button" id="createrecord-add-record" value="Create record">
+ </div>
+ <div id="table-tab-createcolumn">
+ <table>
+ <tr>
+ <td>
+ <label for="createcolumn-name">Column name</label>
+ </td>
+ <td>
+ <input type="text" id="createcolumn-name">
+ </td>
+ </tr>
+ <tr>
+ <td>
+ Column types
+ </td>
+ <td>
+ <label for="createcolumn-type">Types:</label>
+ <select id="createcolumn-type">
+ <optgroup label="Built-in types" id="createcolumn-type-builtin">
+ </optgroup>
+ <optgroup label="Table" id="createcolumn-type-table">
+ </optgroup>
+ </select>
+
+ <label for="createcolumn-column-type">Column stores:</label>
+ <select id="createcolumn-column-type">
+ <option value="GRN_OBJ_COLUMN_SCALAR">Scalar</option>
+ <option value="GRN_OBJ_COLUMN_VECTOR">Vector</option>
+ <option value="GRN_OBJ_COLUMN_INDEX">Inverted index</option>
+ </select>
+
+ <label for="createcolumn-compress">Compression:</label>
+ <select id="createcolumn-compress" disabled>
+ <option value="GRN_OBJ_COMPRESS_NONE">No compression</option>
+ <option value="GRN_OBJ_COMPRESS_ZLIB">zlib</option>
+ <option value="GRN_OBJ_COMPRESS_LZO">lzo</option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ Flags
+ </td>
+ <td id="createcolumn-flags">
+ <input type="checkbox" value="GRN_OBJ_PERSISTENT" checked>Persistent</input>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ Flags for inverted index
+ </td>
+ <td id="createcolumn-ii-flags">
+ <input type="checkbox" value="GRN_OBJ_WITH_SECTION">With section information</input>
+ <input type="checkbox" value="GRN_OBJ_WITH_WEIGHT">With weight information</input>
+ <input type="checkbox" value="GRN_OBJ_WITH_POSITION">With position information</input>
+ </td>
+ </tr>
+ </table>
+ <input type="button" id="createcolumn-add-column" value="Create column">
+ </div>
+ </div>
+
+ <!-- suggest view -->
+ <div id="suggest-tabs">
+ <ul>
+ <li><a href="#suggest-tab-search">Search</a></li>
+ </ul>
+ <div id="suggest-tab-search">
+ <form id="suggest-tab-search-form">
+ <p>
+ <label for="suggest-dataset">Dataset: </label>
+ <input type="text" id="suggest-dataset">
+ </p>
+ <label for="suggest-query">Suggest query: </label>
+ <input type="text" id="suggest-query">
+ <input type="button" id="suggest-submit" value="Search" />
+ </form>
+ <div id="suggest-result-tabs">
+ <ul>
+ <li><a href="#suggest-result-tab-suggest">Suggestion</a></li>
+ <li><a href="#suggest-result-tab-complete">Completion</a></li>
+ <li><a href="#suggest-result-tab-correct">Correction</a></li>
+ </ul>
+ <div id="suggest-result-tab-suggest">
+ </div>
+ <div id="suggest-result-tab-complete">
+ </div>
+ <div id="suggest-result-tab-correct">
+ </div>
+ </div>
+ </div>
+ </div>
+ </td>
+ </tr>
+ </table>
+</div>
+<div id="footer">
+Powered by <a href="http://jquery.com/">jQuery</a> and <a href="http://jqueryui.com/">jQuery UI</a>.
+</div>
+<script type="text/javascript">
+$(function() {
+ var admin = new GroongaAdmin();
+ $(location.hash).click();
+ $.ajaxSetup({
+ timeout: 10000,
+ cache: false
+ });
+});
+</script>
+</body>
+</html>
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/index.ja.html b/storage/mroonga/vendor/groonga/data/html/admin/index.ja.html
new file mode 100644
index 00000000000..f9bc42ad530
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/index.ja.html
@@ -0,0 +1,301 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="ja">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta http-equiv="Content-Style-Type" content="text/css">
+<meta http-equiv="Content-Script-Type" content="text/javascript">
+<meta name="robots" content="noindex,nofollow,noarchive">
+<title>groonga admin</title>
+<link rel="shortcut icon" href="favicon.ico">
+<link rel="icon" href="favicon.png">
+<link rel="stylesheet" type="text/css" href="css/groonga-admin.css">
+<link rel="stylesheet" type="text/css" href="css/redmond/jquery-ui-1.8.18.custom.css">
+<script type="text/javascript" src="js/jquery-1.7.2.min.js"></script>
+<script type="text/javascript" src="js/jquery-ui-1.8.18.custom.min.js"></script>
+<script type="text/javascript" src="js/jquery.flot-0.7.min.js"></script>
+<script type="text/javascript" src="js/groonga-admin.js"></script>
+</head>
+<body>
+<div id="header">
+ <ul id="locale-list">
+ <li><a href="index.html">en</a></li>
+ <li>ja</a>
+ </ul>
+</div>
+<div id="body">
+ <table>
+ <tr>
+ <td id="left-column">
+ <div id="side-menu">
+ <h2>画面一覧</h2>
+ <ul id="side-menu-view-list">
+ <li><a href="#side-menu-summary" id="side-menu-summary">サマリー</a></li>
+ <li><a href="#side-menu-suggest" id="side-menu-suggest">サジェスト</a></li>
+ </ul>
+ <h2>テーブル一覧</h2>
+ <ul id="side-menu-tablelist" />
+ </div>
+ </td>
+
+ <td id="right-column">
+ <!-- database view -->
+ <div id="database-tabs">
+ <ul>
+ <li><a href="#database-tab-summary">サマリー</a></li>
+ <li><a href="#database-tab-tablelist" id="tab-tablelist-link">テーブル一覧</a></li>
+ <li><a href="#database-tab-createtable">テーブル作成</a></li>
+ </ul>
+ <div id="database-tab-summary">
+ <p>
+ groongaの管理ツールです。
+ </p>
+ <ul>
+ <li>開始時間: <span id="status-starttime"></span></li>
+ <li>uptime: <span id="status-uptime"></span></li>
+ <li>クエリ数: <span id="status-n-queries"></span></li>
+ <li>キャッシュヒット率: <span id="status-cache-hit-rate"></span></li>
+ </ul>
+ <div id="throughput-chart" style="height: 300px; max-width: 500px;">
+ </div>
+ </div>
+ <div id="database-tab-tablelist">
+ <div id="tab-tablelist-table">
+ </div>
+ <input type="button" id="tablelist-remove-table" value="選択テーブル削除">
+ </div>
+ <div id="database-tab-createtable">
+ <table>
+ <tr>
+ <td>
+ <label for="createtable-name">テーブル名</label>
+ </td>
+ <td>
+ <input type="text" id="createtable-name">
+ </td>
+ </tr>
+ <tr>
+ <td>
+ 主キー
+ </td>
+ <td>
+ <label for="createtable-key-type">keyの型:</label>
+ <select id="createtable-key-type">
+ <optgroup label="組み込み型" id="createtable-key-type-builtin">
+ </optgroup>
+ <optgroup label="テーブル" id="createtable-key-type-table">
+ </optgroup>
+ </select>
+ <label for="createtable-key-index">keyのインデックス種類:</label>
+ <select id="createtable-key-index">
+ <option value="GRN_OBJ_TABLE_PAT_KEY">パトリシア木</option>
+ <option value="GRN_OBJ_TABLE_HASH_KEY">ハッシュテーブル</option>
+ <option value="GRN_OBJ_TABLE_NO_KEY">キーなし</option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ フラグ
+ </td>
+ <td id="createtable-flags">
+ <input type="checkbox" value="GRN_OBJ_PERSISTENT" checked>永続化</input>
+ <input type="checkbox" value="GRN_OBJ_KEY_NORMALIZE">key文字列の正規化</input>
+ <input type="checkbox" value="GRN_OBJ_KEY_WITH_SIS">key文字列のsuffix登録</input>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ valueの型
+ </td>
+ <td>
+ <select id="createtable-value-type">
+ <optgroup label="組み込み型" id="createtable-value-type-builtin">
+ </optgroup>
+ <optgroup label="テーブル" id="createtable-value-type-table">
+ </optgroup>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ デフォルトトークナイザ
+ </td>
+ <td>
+ <select id="createtable-default-tokenizer">
+ <optgroup label="組み込み" id="createtable-default-tokenizer-builtin">
+ </optgroup>
+ </select>
+ </td>
+ </tr>
+ </table>
+ <input type="button" id="createtable-add-table" value="テーブル追加">
+ </div>
+ </div>
+
+ <!-- table view -->
+ <div id="table-tabs">
+ <ul>
+ <li><a href="#table-tab-recordlist" id="tab-recordlist-link">レコード一覧</a></li>
+ <li><a href="#table-tab-columnlist" id="tab-columnlist-link">カラム一覧</a></li>
+ <li><a href="#table-tab-createrecord" id="tab-createrecord-link">レコード作成</a></li>
+ <li><a href="#table-tab-createcolumn">カラム作成</a></li>
+ </ul>
+ <div id="table-tab-recordlist">
+ <input type="checkbox" id="table-tab-recordlist-full-checkbox" /><label for="table-tab-recordlist-full-checkbox">管理モード</label>
+ <form id="tab-recordlist-form">
+ <div id="table-tab-recordlist-form-simple">
+ <label for="tab-recordlist-simplequery">検索クエリ: </label><input type="text" id="tab-recordlist-simplequery">
+ <select id="tab-recordlist-simplequerytype">
+ <option value="query" data-placeholder="例)column:@value">query</option>
+ <option value="filter" data-placeholder="例)column == &quot;value&quot;">filter</option>
+ </select>
+ <input type="checkbox" id="tab-recordlist-incremental" /><label for="tab-recordlist-incremental" id="tab-recordlist-incremental-label">インクリメンタル検索</label>
+ </div>
+ <div id="table-tab-recordlist-form-full">
+ <table>
+ <tr><td>match_columns</td><td><input type="text" id="tab-recordlist-match_columns" /></td></tr>
+ <tr><td>query</td><td><input type="text" id="tab-recordlist-query" /></td></tr>
+ <tr><td>filter</td><td><input type="text" id="tab-recordlist-filter" /></td></tr>
+ <tr><td>scorer</td><td><input type="text" id="tab-recordlist-scorer" /></td></tr>
+ <tr><td>sortby</td><td><input type="text" id="tab-recordlist-sortby" /></td></tr>
+ <tr><td>output_columns</td><td><input type="text" id="tab-recordlist-output_columns" /></td></tr>
+ <tr><td>offset</td><td><input type="text" id="tab-recordlist-offset" /></td></tr>
+ <tr><td>limit</td><td><input type="text" id="tab-recordlist-limit" /></td></tr>
+ <tr><td>drilldown</td><td><input type="text" id="tab-recordlist-drilldown" /></td></tr>
+ <tr><td>drilldown_sortby</td><td><input type="text" id="tab-recordlist-drilldown_sortby" /></td></tr>
+ <tr><td>drilldown_output_columns</td><td><input type="text" id="tab-recordlist-drilldown_output_columns" /></td></tr>
+ <tr><td>drilldown_offset</td><td><input type="text" id="tab-recordlist-drilldown_offset" /></td></tr>
+ <tr><td>drilldown_limit</td><td><input type="text" id="tab-recordlist-drilldown_limit" /></td></tr>
+ </table>
+ </div>
+ <input type="submit" id="tab-recordlist-submit" value="検索"/>
+ </form>
+ <div id="tab-recordlist-table">
+ </div>
+ <input type="button" id="recordlist-remove-record" value="選択レコード削除">
+ </div>
+ <div id="table-tab-columnlist">
+ <div id="tab-columnlist-table">
+ </div>
+ <input type="button" id="columnlist-remove-column" value="選択カラム削除">
+ </div>
+ <div id="table-tab-createrecord">
+ <table id="table-createrecord">
+ </table>
+ <input type="button" id="createrecord-add-record" value="レコード追加">
+ </div>
+ <div id="table-tab-createcolumn">
+ <table>
+ <tr>
+ <td>
+ <label for="createcolumn-name">カラム名</label>
+ </td>
+ <td>
+ <input type="text" id="createcolumn-name">
+ </td>
+ </tr>
+ <tr>
+ <td>
+ 設定
+ </td>
+ <td>
+ <label for="createcolumn-type">型:</label>
+ <select id="createcolumn-type">
+ <optgroup label="組み込み型" id="createcolumn-type-builtin">
+ </optgroup>
+ <optgroup label="テーブル" id="createcolumn-type-table">
+ </optgroup>
+ </select>
+
+ <label for="createcolumn-source">テーブル型の場合カラム</label>
+ <select id="createcolumn-source" disabled>
+ </select>
+
+ <label for="createcolumn-column-type">カラム種別:</label>
+ <select id="createcolumn-column-type">
+ <option value="GRN_OBJ_COLUMN_SCALAR">スカラ</option>
+ <option value="GRN_OBJ_COLUMN_VECTOR">ベクタ</option>
+ <option value="GRN_OBJ_COLUMN_INDEX">転置インデックス</option>
+ </select>
+
+ <label for="createcolumn-compress">圧縮:</label>
+ <select id="createcolumn-compress">
+ <option value="GRN_OBJ_COMPRESS_NONE">圧縮なし</option>
+ <option value="GRN_OBJ_COMPRESS_ZLIB">zlib</option>
+ <option value="GRN_OBJ_COMPRESS_LZO">lzo</option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ フラグ
+ </td>
+ <td id="createcolumn-flags">
+ <input type="checkbox" value="GRN_OBJ_PERSISTENT" checked>永続化</input>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ 転置インデックス用フラグ
+ </td>
+ <td id="createcolumn-ii-flags">
+ <input type="checkbox" value="GRN_OBJ_WITH_SECTION">段落情報を含める</input>
+ <input type="checkbox" value="GRN_OBJ_WITH_WEIGHT">重みを含める</input>
+ <input type="checkbox" value="GRN_OBJ_WITH_POSITION">位置情報を含める</input>
+ </td>
+ </tr>
+ </table>
+ <input type="button" id="createcolumn-add-column" value="カラム追加">
+ </div>
+ </div>
+
+ <!-- suggest view -->
+ <div id="suggest-tabs">
+ <ul>
+ <li><a href="#suggest-tab-search">検索</a></li>
+ </ul>
+ <div id="suggest-tab-search">
+ <form id="suggest-tab-search-form">
+ <p>
+ <label for="suggest-dataset">データセット: </label>
+ <input type="text" id="suggest-dataset">
+ </p>
+ <label for="suggest-query">検索クエリ: </label>
+ <input type="text" id="suggest-query">
+ <input type="button" id="suggest-submit" value="検索" />
+ </form>
+ <div id="suggest-result-tabs">
+ <ul>
+ <li><a href="#suggest-result-tab-suggest">提案</a></li>
+ <li><a href="#suggest-result-tab-complete">補完</a></li>
+ <li><a href="#suggest-result-tab-correct">補正</a></li>
+ </ul>
+ <div id="suggest-result-tab-suggest">
+ </div>
+ <div id="suggest-result-tab-complete">
+ </div>
+ <div id="suggest-result-tab-correct">
+ </div>
+ </div>
+ </div>
+ </div>
+ </td>
+ </tr>
+ </table>
+</div>
+<div id="footer">
+Powered by <a href="http://jquery.com/">jQuery</a> and <a href="http://jqueryui.com/">jQuery UI</a>.
+</div>
+<script type="text/javascript">
+$(function() {
+ var admin = new GroongaAdmin();
+ $(location.hash).click();
+ $.ajaxSetup({
+ timeout: 10000,
+ cache: false
+ });
+});
+</script>
+</body>
+</html>
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/js/groonga-admin.ja.js b/storage/mroonga/vendor/groonga/data/html/admin/js/groonga-admin.ja.js
new file mode 100644
index 00000000000..c9d11f5080b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/js/groonga-admin.ja.js
@@ -0,0 +1,1372 @@
+// -*- js2-basic-offset: 2; indent-tabs-mode: nil -*-
+
+"use strict";
+
+function prim2html(prim, limit) {
+ switch(typeof prim) {
+ case 'undefined':
+ return 'undefined';
+ case 'boolean':
+ return prim ? 'true' : 'false';
+ case 'number':
+ return String(prim);
+ case 'string':
+ if (prim.length > limit) {
+ prim = prim.substring(0, limit) + '...';
+ }
+ return escapeHTML(prim);
+ case 'array':
+ case 'object':
+ if (prim == null) {
+ return 'null';
+ } else if ($.isArray(prim)) {
+ return 'array'; /* TODO: implement */
+ } else {
+ return 'object'; /* TODO: implement */
+ }
+ default:
+ return 'ERROR';
+ }
+}
+
+function escapeHTML(str) {
+ return str.replace(/&/g, "&amp;")
+ .replace(/"/g, "&quot;")
+ .replace(/</g, "&lt;")
+ .replace(/>/g, "&gt;");
+}
+var Groonga = {
+ key_type_list: ['Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32',
+ 'Int64', 'UInt64', 'Float', 'Time', 'ShortText',
+ 'TokyoGeoPoint', 'WGS84GeoPoint'],
+ value_type_list: ['Object', 'Bool',
+ 'Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32',
+ 'Int64', 'UInt64', 'Float', 'Time'],
+ column_type_list: ['Object', 'Bool',
+ 'Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32',
+ 'Int64', 'UInt64', 'Float', 'Time', 'ShortText',
+ 'Text', 'LongText', 'TokyoGeoPoint', 'WGS84GeoPoint'],
+ tokenizer_list: ['TokenDelimit', 'TokenUnigram', 'TokenBigram', 'TokenTrigram', 'TokenMecab'],
+ GRN_OBJ_PERSISTENT: (0x01<<15),
+
+ GRN_OBJ_TABLE_TYPE_MASK: (0x07),
+ GRN_OBJ_TABLE_HASH_KEY: (0x00),
+ GRN_OBJ_TABLE_PAT_KEY: (0x01),
+ GRN_OBJ_TABLE_NO_KEY: (0x03),
+
+ GRN_OBJ_KEY_WITH_SIS: (0x01<<6),
+ GRN_OBJ_KEY_NORMALIZE: (0x01<<7),
+
+ GRN_OBJ_COLUMN_TYPE_MASK: (0x07),
+ GRN_OBJ_COLUMN_SCALAR: (0x00),
+ GRN_OBJ_COLUMN_VECTOR: (0x01),
+ GRN_OBJ_COLUMN_INDEX: (0x02),
+
+ GRN_OBJ_COMPRESS_MASK: (0x07<<4),
+ GRN_OBJ_COMPRESS_NONE: (0x00<<4),
+ GRN_OBJ_COMPRESS_ZLIB: (0x01<<4),
+ GRN_OBJ_COMPRESS_LZO: (0x02<<4),
+
+ GRN_OBJ_WITH_SECTION: (0x01<<7),
+ GRN_OBJ_WITH_WEIGHT: (0x01<<8),
+ GRN_OBJ_WITH_POSITION: (0x01<<9)
+};
+
+$.widget("ui.paginate", {
+ version: "1.0",
+ options: {
+ total: 0,
+ nItemsPerPage: 10,
+ currentPage: 0,
+ nShowLinks: 10,
+ callback: null
+ },
+ _create: function() {
+ var that = this;
+ var element = this.element;
+ element.addClass("pager");
+
+ var total = this.options.total;
+ var nItemsPerPage = this.options.nItemsPerPage;
+ var currentPage = this.options.currentPage;
+ var nShowLinks = this.options.nShowLinks;
+ var lastPage = Math.floor((total - 1) / nItemsPerPage) + 1;
+ var start = currentPage - Math.floor(nShowLinks / 2);
+ start = (start < 1) ? 1 : start;
+ var end = start + nShowLinks - 1;
+ end = (end > lastPage) ? lastPage : end;
+
+ var callback = this.options.callback;
+ if (start > 1) {
+ element.append($('<span />')
+ .addClass('pager')
+ .append($('<a />')
+ .attr('href', '#')
+ .text('1')
+ .click(function () {callback(0)})));
+ element.append($('<span />').text('....'));
+ }
+ for (var i = start; i <= end; i++) {
+ var page = $('<span />').append($('<a />')
+ .attr('href', '#')
+ .text(String(i))
+ .click(function () {
+ callback(Number($(this).text()) - 1);
+ }));
+ if (i == currentPage) {
+ page.addClass('pager-current');
+ } else {
+ page.addClass('pager');
+ }
+ element.append(page);
+ }
+ if (end < lastPage) {
+ element.append($('<span />')
+ .text('....'));
+ element.append($('<span />')
+ .addClass('pager')
+ .append($('<a />')
+ .attr('href', '#')
+ .text(String(lastPage))
+ .click(function () {callback(lastPage - 1);})));
+ }
+ }
+});
+
+function GroongaAdmin() {
+ this.current_table = null;
+ this.statusTimer = null;
+ this.semaphore = new Array();
+ this.current_status = 0;
+ this.reload_record_func = function(){};
+
+ var that = this;
+ this._initializeTabs();
+
+ $('#tab-tablelist-link').click(function() {
+ that.tablelist();
+ });
+ $('#tab-columnlist-link').click(function() {
+ that.columnlist(that.current_table);
+ });
+ $('#tab-createrecord-link').click(function() {
+ that.update_createrecord(that.current_table);
+ });
+ $('#tab-recordlist-link').click(function() {
+ that.reload_record_func();
+ });
+ $('#createtable-add-table').click(function() {
+ that.createtable();
+ });
+ $('#createrecord-add-record').click(function() {
+ that.createrecord();
+ });
+ $('#createcolumn-add-column').click(function() {
+ that.createcolumn();
+ });
+ $('#recordlist-remove-record').click(function() {
+ that.removerecord();
+ });
+ $('#columnlist-remove-column').click(function() {
+ that.removecolumn();
+ });
+ $('#tablelist-remove-table').click(function() {
+ that.removetable();
+ });
+ $('#tab-recordlist-form').submit(function() {
+ if ($('#table-tab-recordlist-full-checkbox').attr('checked')) {
+ // full
+ var d = {
+ 'table': that.current_table
+ }
+ $.each(that.SELECT_PARAMS, function(i, val) {
+ var e = $('#tab-recordlist-' + val);
+ if (e.val()) {
+ d[val] = e.val();
+ }
+ });
+ that.recordlist(d, true);
+ } else {
+ // simple
+ that.recordlist_simple(
+ that.current_table,
+ $('#tab-recordlist-simplequery').val(),
+ $('#tab-recordlist-simplequerytype').val(),
+ 1);
+ }
+ return false;
+ });
+ this._initializeSideMenu();
+ this.update_tablelist();
+
+ var e1 = $('#createtable-key-type-builtin');
+ $.each(Groonga.key_type_list, function(i, val) {
+ e1.append($('<option />').val(val).text(val));
+ });
+
+ e1 = $('#createtable-value-type-builtin');
+ e1.append($('<option />').val('').text('なし'));
+ $.each(Groonga.value_type_list, function(i, val) {
+ e1.append($('<option />').val(val).text(val));
+ });
+
+ e1 = $('#createtable-default-tokenizer-builtin');
+ e1.append($('<option />').val('').text('なし'));
+ $.each(Groonga.tokenizer_list, function(i, val) {
+ e1.append($('<option />').val(val).text(val));
+ });
+
+ e1 = $('#createcolumn-type-builtin');
+ $.each(Groonga.column_type_list, function(i, val) {
+ e1.append($('<option />').val(val).text(val));
+ });
+
+ $('#tab-recordlist-simplequerytype').change(function() {
+ if ($(this).val() == 'scorer') {
+ $('#tab-recordlist-incremental').hide();
+ $('#tab-recordlist-incremental-label').hide();
+ } else {
+ $('#tab-recordlist-incremental').show();
+ $('#tab-recordlist-incremental-label').show();
+ }
+ $('#tab-recordlist-incremental').change();
+
+ var selectedOption = $(this).find(':selected');
+ $('#tab-recordlist-simplequery').attr(
+ 'placeholder', selectedOption.data('placeholder')
+ );
+ }).change();
+
+ $('#table-tab-recordlist-full-checkbox').change(function() {
+ if ($(this).attr('checked')) {
+ $('#table-tab-recordlist-form-simple').hide();
+ $('#table-tab-recordlist-form-full').show();
+ } else {
+ $('#table-tab-recordlist-form-simple').show();
+ $('#table-tab-recordlist-form-full').hide();
+ }
+ }).change();
+
+ $('#tab-recordlist-incremental').change(function() {
+ $('#tab-recordlist-simplequery').unbind('keyup');
+ if ($(this).attr('checked') &&
+ $('#tab-recordlist-simplequerytype').val() != 'scorer') {
+ $('#tab-recordlist-simplequery').keyup(function(e) {
+ that.recordlist_simple(
+ that.current_table,
+ $('#tab-recordlist-simplequery').val(),
+ $('#tab-recordlist-simplequerytype').val(),
+ 1,
+ true);
+ });
+ }
+ }).change();
+
+ $('#createcolumn-type').change(function(e) {
+ var s = $('#createcolumn-type-table option:selected');
+ var cs = $('#createcolumn-source');
+ if (s.length > 0) {
+ cs.empty().removeAttr('disabled');
+ that.showloading(
+ $.ajax({
+ url: '/d/column_list',
+ data: {'table': s.val()},
+ dataType: 'json',
+ success: function(d) {
+ if(that.validateajax(d) < 0) { return; }
+ var idx;
+ var b = d[1];
+ $.each(b[0], function(i, val) {
+ if (val[0] == 'name') { idx = i; }
+ });
+ if (idx) {
+ b.shift();
+ $.each(b, function(i, val) {
+ cs.append($('<option />').val(val[idx]).text(val[idx]));
+ });
+ }
+ that.hideloading();
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ } else {
+ cs.empty().attr('disabled', 'disabled');
+ }
+ });
+
+ this.recordlist_count = 30;
+};
+
+jQuery.extend(GroongaAdmin.prototype, {
+ SELECT_PARAMS: [
+ 'match_columns', 'query', 'filter',
+ 'scorer',
+ 'output_columns',
+ 'sortby', 'offset', 'limit',
+ 'drilldown',
+ 'drilldown_output_columns',
+ 'drilldown_sortby', 'drilldown_offset', 'drilldown_limit'
+ ],
+ _initializeTabs: function() {
+ this._initializeDatabaseTab();
+ this._initializeTableTab();
+ this._initializeSuggestTab();
+ this._selectTab("database");
+ },
+ _initializeDatabaseTab: function() {
+ var that = this;
+
+ this._$databaseTabs = $('#database-tabs').tabs({
+ show: function(e, ui) {
+ that.stop_status_timer();
+ if (ui.panel.id == 'database-tab-summary') {
+ that.start_status_timer();
+ }
+ }
+ });
+ },
+ _initializeTableTab: function() {
+ this._$tableTabs = $('#table-tabs').tabs({
+ show: function(e, ui) {
+ }
+ });
+ },
+ _initializeSuggestTab: function() {
+ var that = this;
+
+ this._$suggestTabs = $('#suggest-tabs').tabs({
+ show: function(e, ui) {
+ }
+ });
+
+ this._initializeSuggestDatasetComplete();
+ this._initializeSuggestQueryComplete();
+ this._initializeSuggestSubmit();
+ this._initializeSuggestResult();
+ },
+ _initializeSuggestDatasetComplete: function() {
+ var that = this;
+ var $dataset = $("#suggest-dataset");
+ this._$suggestDataset = $dataset;
+ $dataset.autocomplete({
+ minLength: 0,
+ source: function (request, response) {
+ var datasets = [];
+ $.each(that._tables, function(i, table_name) {
+ var suggestTableMatch = /^item_(.+)$/.exec(table_name);
+ if (suggestTableMatch) {
+ var dataset = suggestTableMatch[1];
+ datasets.push(dataset);
+ }
+ });
+
+ datasets = $.ui.autocomplete.filter(datasets, request.term);
+ response(datasets);
+ }
+ });
+ $dataset.focus(function (event) {
+ $dataset.autocomplete("search", $dataset.val());
+ });
+ },
+ _suggestParameters: function(query, dataset, type) {
+ var nItemsPerPage = 30;
+ return {
+ query: query,
+ types: type,
+ table: "item_" + dataset,
+ column: "kana",
+ offset: 0,
+ limit: nItemsPerPage,
+ };
+ },
+ _initializeSuggestQueryComplete: function() {
+ var that = this;
+ this._$suggestQuery = $("#suggest-query").autocomplete({
+ source: function (request, response) {
+ var $dataset = $("#suggest-dataset");
+ var dataset = $dataset.val();
+ $("#suggest-submit").click();
+ $.ajax({
+ url: "/d/suggest",
+ data: that._suggestParameters(request.term, dataset, "complete"),
+ dataType: "jsonp",
+ success: function (data, textStatus, jqXHR) {
+ var completions = data[1]["complete"];
+ var items = [];
+ if (completions && completions.length > 2) {
+ completions.shift();
+ completions.shift();
+ $.each(completions, function(i, item) {
+ var key = item[0];
+ items.push(key);
+ if (items.length >= 3) {
+ return false;
+ }
+ return true;
+ });
+ }
+ response(items);
+ },
+ error: function(jqXHR, textStatus, errorThrown) {
+ }
+ });
+ }
+ });
+ },
+ _initializeSuggestSubmit: function() {
+ var that = this;
+ $("#suggest-submit").click(function (event) {
+ var dataset = $("#suggest-dataset").val();
+ var query = $("#suggest-query").val();
+ var type = that._suggestResultType;
+ var parameters = that._suggestParameters(query, dataset, type);
+ $.ajax({
+ url: "/d/suggest",
+ data: parameters,
+ dataType: "jsonp",
+ success: function (data, textStatus, jqXHR) {
+ var response = data[1][type];
+ response.shift();
+ var $result = $("#suggest-result-tab-" + type);
+ $result
+ .empty()
+ .append($("<div/>").append(that._createResultTable(response)));
+ },
+ error: function(jqXHR, textStatus, errorThrown) {
+ }
+ });
+ });
+ },
+ _initializeSuggestResult: function() {
+ var that = this;
+ $("#suggest-result-tabs").tabs({
+ show: function (event, ui) {
+ that._suggestResultType = ui.panel.id.replace(/^suggest-result-tab-/, "");
+ $("#suggest-submit").click();
+ }
+ });
+ },
+ _selectTab: function(name) {
+ this.stop_status_timer();
+ this._$databaseTabs.hide();
+ this._$tableTabs.hide();
+ this._$suggestTabs.hide();
+ switch (name) {
+ case "table":
+ this._$tableTabs.show();
+ break;
+ case "suggest":
+ this._$suggestTabs.show();
+ break;
+ case "database":
+ default:
+ this._$databaseTabs.show();
+ break;
+ }
+ },
+ _initializeSideMenu: function () {
+ var that = this;
+ $('#side-menu-summary').click(function() {
+ that.current_table = null;
+ that._selectTab("database");
+ that._$databaseTabs.tabs("select", "#database-tab-summary");
+ });
+ $('#side-menu-suggest').click(function() {
+ that.current_table = null;
+ that._selectTab("suggest");
+ });
+ },
+ start_status_timer: function() {
+ var that = this;
+ this.stop_status_timer();
+ this.status();
+ this.statusTimer = setInterval(function() {that.status()}, 1000);
+ },
+ change_status_timer: function(time) {
+ var that = this;
+ this.stop_status_timer();
+ this.statusTimer = setInterval(function() {that.status()}, time);
+ },
+ stop_status_timer: function() {
+ if (this.statusTimer) {
+ clearInterval(this.statusTimer);
+ this.statusTimer = null;
+ }
+ },
+ _createResultTable: function (result, options) {
+ var that = this;
+ if (!options) {
+ options = {};
+ }
+ var table = $('<table class="records"/>');
+ if ($.isArray(result)) {
+ var nEntries = result.length;
+ if (nEntries >= 1) {
+ var thead = $('<thead/>');
+ table.append(thead);
+ var line = result[0];
+ if ($.isArray(line)) {
+ var tr = $('<tr/>');
+ thead.append(tr);
+ var m = line.length;
+ if (options.check) {
+ tr.append($('<th/>'));
+ }
+ for (var j = 0; j < m; j++) {
+ var th = $('<th/>');
+ tr.append(th);
+ th.append(prim2html(line[j][0], 128));
+ th.append($('<br />'));
+ th.append(prim2html(line[j][1], 128));
+ }
+ if (options.button) {
+ tr.append($('<th/>'));
+ }
+ }
+ var tbody = $('<tbody>');
+ table.append(tbody);
+ for (var i = 1; i < nEntries; i++) {
+ line = result[i];
+ if ($.isArray(line)) {
+ var tr = $('<tr>');
+ table.append(tr);
+ var m = line.length;
+ switch(options.check) {// チェックボックスの値を何にするか
+ case 1: // 1番目の要素(レコード一覧の_id等)
+ case 2: // 2番目の要素(テーブル・カラム一覧のname等)
+ var td = $('<td/>');
+ tr.append(td);
+ td.append($('<input/>')
+ .attr("type", "checkbox")
+ .attr("value", line[options.check-1]));
+ break;
+ }
+ for (var j = 0; j < m; j++) {
+ var td = $('<td/>');
+ tr.append(td);
+ td.append(prim2html(line[j], 128));
+ }
+ switch(options.button) {
+ case 1: // Edit record
+ var td = $('<td/>');
+ tr.append(td);
+ td.append($('<input/>')
+ .attr("type", "button")
+ .attr("value", "編集")
+ .attr("data-record-id", line[0])
+ .click(function () {
+ that.show_edit_record($(this).attr("data-record-id"));
+ }));
+ break;
+ case 2: // Table
+ var td = $('<td/>');
+ tr.append(td);
+ td.append($('<input/>')
+ .attr("type", "button")
+ .attr("value", "詳細")
+ .attr("data-table-name", line[1])
+ .click(function () {
+ var tableName = $(this).attr("data-table-name");
+ $("#side-menu-tablelist-link-" + tableName).click();
+ }));
+ break;
+ }
+ }
+ }
+ }
+ }
+ return table;
+ },
+ show_edit_record: function(id) {
+ $('#table-tabs').tabs('select', 2);
+ this.update_createrecord(this.current_table, id);
+ },
+ format_unix_time: function(unix_time) {
+ var date = new Date();
+ date.setTime(unix_time * 1000);
+ return date.toLocaleString();
+ },
+ format_duration: function(duration_in_seconds) {
+ var duration = "";
+ var days = Math.floor(duration_in_seconds / 3600 / 24);
+ var hours = Math.floor(duration_in_seconds / 3600 % 24);
+ var minutes = Math.floor(duration_in_seconds / 60 % 60);
+ var seconds = Math.floor(duration_in_seconds % 60);
+
+ if (days > 0) {
+ duration += days;
+ if (days == 1) {
+ duration += " day, ";
+ } else {
+ duration += " days, ";
+ }
+ }
+ if (days > 0 || hours > 0) {
+ duration += hours + ":" + minutes + ":" + seconds;
+ } else if (minutes > 0) {
+ duration += minutes + ":" + seconds;
+ } else {
+ duration += seconds;
+ }
+
+ return duration;
+ },
+ maxThroughput: 0,
+ lastNQueries: -1,
+ keepLastNData: 100,
+ throughputData: [],
+ throughputChart: null,
+ updateThroughputChart: function(statusData) {
+ var maxThroughputUpdated = false;
+ if (this.lastNQueries >= 0) {
+ var throughput = statusData.n_queries - this.lastNQueries;
+ this.throughputData.push(throughput);
+ if (this.maxThroughput < throughput) {
+ this.maxThroughput = throughput;
+ maxThroughputUpdated = true;
+ }
+ }
+ if (this.throughputData.length > this.keepLastNData) {
+ this.throughputData.shift();
+ }
+ if (!this.throughputChart) {
+ this.throughputChart = $.plot($("#throughput-chart"),
+ [[]],
+ {xaxis: {min: -(this.keepLastNData - 1),
+ max: 0},
+ yaxis: {min: 0}});
+ }
+ var that = this;
+ var chartSeries = $.map(this.throughputData, function(n, i) {
+ return [[-(that.throughputData.length - i) + 1, n]];
+ });
+ this.throughputChart.setData([chartSeries]);
+ if (maxThroughputUpdated) {
+ this.throughputChart.setupGrid();
+ }
+ this.throughputChart.draw();
+ this.lastNQueries = statusData.n_queries;
+ },
+ status: function() {
+ if (this.current_status > 0) { return; }
+ this.current_status++;
+ var that = this;
+ $.ajax({
+ url: '/d/status',
+ data: {},
+ dataType: 'json',
+ success: function(b) {
+ that.current_status--;
+ if (!b) {
+ that.change_status_timer(10000);
+ return;
+ }
+ var d = b[1];
+ $('#status-starttime').text(that.format_unix_time(d.starttime));
+ $('#status-uptime').text(that.format_duration(d.uptime));
+ $('#status-n-queries').text(d.n_queries);
+ $('#status-cache-hit-rate').text(d.cache_hit_rate);
+ that.updateThroughputChart(d);
+ that.change_status_timer(1000);
+ },
+ error: function() {
+ that.current_status--;
+ that.change_status_timer(10000);
+ }
+ });
+ },
+ update_tablelist: function() {
+ var that = this;
+ this._tables = [];
+ this.showloading(
+ $.ajax({
+ url: '/d/table_list',
+ data: {},
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ d.shift();
+ var tl = $('#side-menu-tablelist').empty();
+ var tt = $('#createtable-key-type-table').empty();
+ var vt = $('#createtable-value-type-table').empty();
+ var ct = $('#createcolumn-type-table').empty();
+ var b = d.shift();
+ b.shift();
+ $.each(b, function(i, val) {
+ var table_name = val[1];
+ that._tables.push(table_name);
+ tl.append(
+ $('<li />').append(
+ $('<a />')
+ .attr('id', 'side-menu-tablelist-link-' + table_name)
+ .attr('href', '#side-menu-tablelist-' + table_name)
+ .text(table_name)
+ .click(function() {
+ that.current_table = table_name;
+ $('#database-tabs').hide();
+ $('#suggest-tabs').hide();
+ that.stop_status_timer();
+ $('#table-tabs').show();
+ that.columnlist(table_name);
+ $('#tab-recordlist-simplequery').val('');
+ that.recordlist_simple(table_name, null, null, 1);
+ that.update_createrecord(that.current_table);
+ })
+ )
+ );
+ tt.append($('<option />').val(val[1]).text(val[1]));
+ vt.append($('<option />').val(val[1]).text(val[1]));
+ ct.append($('<option />').val(val[1]).text(val[1]));
+ });
+ that.hideloading();
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ tablelist: function() {
+ $('#tab-tablelist-table').empty();
+ var that = this;
+ this.showloading(
+ $.ajax({
+ url: '/d/table_list',
+ data: {},
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ var b = d[1];
+ var table = that._createResultTable(b, {check: 2, button: 2});
+ $('#tab-tablelist-table').append($('<h1 />').text('テーブル一覧')).append(table);
+ that.hideloading();
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ recordlist_simple: function(table_name, simplequery, simplequery_type, page, hide_dialog) {
+ var d = {
+ 'table': table_name,
+ 'offset': (page - 1) * this.recordlist_count,
+ 'limit': this.recordlist_count
+ }
+ switch (simplequery_type) {
+ case 'query':
+ case 'filter':
+ case null:
+ if (simplequery) {
+ d[simplequery_type] = simplequery;
+ }
+ this.recordlist(d, true, hide_dialog);
+ break;
+ }
+ },
+ recordlist: function(params, show_pager, hide_dialog) {
+ var that = this;
+ this.reload_record_func = function(){
+ that.recordlist(params, show_pager, hide_dialog);
+ };
+ this.showloading(
+ $.ajax({
+ url: '/d/select',
+ data: params,
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d, hide_dialog) < 0) { return; }
+ var rc = d.shift();
+ if (rc[0] != 0) {
+ alert('error: ' + rc[3]);
+ that.hideloading();
+ return false;
+ }
+ var body = d.shift();
+ var recs = body.shift();
+ var all_count = recs.shift()[0];
+ var pager;
+ if (show_pager) {
+ var offset = params['offset'] || 0;
+ var rows = params['limit'] || 10;
+ if (rows < 0){
+ rows = all_count + parseInt(rows) + 1;
+ }
+ if (rows != '' && !parseInt(rows)) {
+ pager = $('<span />');
+ } else {
+ pager = $("<div/>");
+ pager.paginate({
+ total: all_count,
+ nItemsPerPage: rows,
+ currentPage: Math.floor(offset/rows)+1,
+ callback: function(page) {
+ params['offset'] = page * rows;
+ that.recordlist(params, true, false);
+ return false;
+ }
+ });
+ }
+ } else {
+ pager = $('<span />');
+ }
+ $('#tab-recordlist-table')
+ .empty()
+ .append($('<h1 />').text('レコード一覧: ' + params['table']))
+ .append($('<p />').text('総件数: ' + all_count))
+ .append(pager.clone(true))
+ .append($('<div />').append(that._createResultTable(recs, {check: 1, button: 1})))
+ .append(pager);
+ that.hideloading();
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest, hide_dialog);
+ }
+ })
+ ,hide_dialog);
+ },
+ columnlist: function(table_name) {
+ var that = this;
+ $('#tab-columnlist-table').empty();
+ this.showloading(
+ $.ajax({
+ url: '/d/column_list',
+ data: {'table': table_name},
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ var b = d[1];
+ var table = that._createResultTable(b, {check: 2});
+ $('#tab-columnlist-table')
+ .append($('<h1 />').text('カラム一覧: ' + table_name))
+ .append(table);
+ that.hideloading();
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ add_record_inputbox: function(type, value) {
+ var inputbox = null;
+ switch(type){
+ case "Bool":
+ inputbox = $('<input />')
+ .attr("type","checkbox")
+ .attr("value","true");
+ if (value) {
+ inputbox.attr("checked","");
+ }
+ break;
+ case "UInt8":
+ case "UInt16":
+ case "UInt32":
+ case "UInt64":
+ case "Int8":
+ case "Int16":
+ case "Int32":
+ case "Int64":
+ case "Float":
+ inputbox = $('<input />')
+ .attr("type", "text")
+ .val(isNaN(value) ? "" : value);
+ break;
+ case "Text":
+ case "ShortText":
+ case "LongText":
+ inputbox = $('<textarea />')
+ .attr("cols", "50")
+ .attr("rows", "2")
+ .text(value ? value : "");
+ break;
+ case "TokyoGeoPoint":
+ case "WGS84GeoPoint":
+ case "Time":
+ inputbox = $('<input />')
+ .attr("type", "text")
+ .attr("size", "40")
+ .val(value ? value : "");
+ break;
+ case "Object":
+ inputbox = $('<input />')
+ .attr("type", "text")
+ .attr("disabled", "disabled");
+ break;
+ default:
+ inputbox = $('<input />')
+ .attr("type", "text")
+ .val(value ? value : "");
+ }
+ inputbox.addClass('column_values');
+ return inputbox;
+ },
+ add_record_deletebutton: function(){
+ var ret =
+ $('<span />')
+ .append("[×]")
+ .css('cursor', 'pointer')
+ .click(function() {
+ $(this).prev().remove();
+ $(this).next().remove();
+ $(this).remove();
+ });
+ return ret;
+ },
+ update_createrecord_loadcomplete: function(d_sel, d_col) {
+ var that = this;
+ var b = d_sel[1][0];
+ var columns = $('<tbody />');
+ var listofs = b[1].length - (d_col[1].length - 1);
+ for (var i = 1; i < b[1].length; i++) {
+ var line = b[1][i];
+ var value = null;
+ if (b[2]) value = b[2][i];
+ if ($.isArray(line)) {
+ var tr = $('<tr/ >')
+ .addClass('create-record-columns')
+ .append(
+ $('<td />')
+ .addClass('columnname')
+ .append(prim2html(line[0], 128))
+ )
+ .append(
+ $('<td />')
+ .addClass('columntype')
+ .append("(")
+ .append($('<span />')
+ .append(prim2html(line[1], 128))
+ )
+ .append(")")
+ );
+ var inputtd = $('<td />').addClass('columnval');
+ if (i >= listofs && d_col[1][i - listofs + 1][4].indexOf("COLUMN_VECTOR") >= 0){
+ var type = line[1];
+ if (value != null) {
+ for (var j = 0; j < value.length; j++) {
+ inputtd
+ .append(this.add_record_inputbox(line[1], value[j]))
+ .append(this.add_record_deletebutton())
+ .append('<br />');
+ }
+ }
+ inputtd
+ .append($('<span />')
+ .append("[値を追加]")
+ .css('cursor', 'pointer')
+ .click(function() {
+ var target = $(this).parent();
+ target
+ .append(that.add_record_inputbox($(this).parent().prev().children().text()))
+ .append(that.add_record_deletebutton())
+ .append("<br />");
+ $(this).appendTo(target);
+ })
+ );
+ } else {
+ inputtd.append(this.add_record_inputbox(line[1], value));
+ if (line[0] == "_key" && value != null) {
+ inputtd.children().attr("disabled", "disabled");
+ }
+ }
+ tr.append(inputtd);
+ columns.append(tr);
+ }
+ }
+ $("#table-createrecord").append(columns);
+ this.hideloading();
+ },
+ update_createrecord: function(table_name, id) {
+ var that = this;
+ var d_sel = null;
+ var d_col = null;
+ $('#table-createrecord').empty();
+ this.showloading(
+ $.ajax({
+ url: '/d/select',
+ data: {
+ 'table' : table_name,
+ 'limit' : 1,
+ 'query' : '_id:' + id
+ },
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ d_sel = d;
+ if (d_col) {
+ that.update_createrecord_loadcomplete(d_sel, d_col);
+ }
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ this.showloading(
+ $.ajax({
+ url: '/d/column_list',
+ data: {
+ 'table' : table_name
+ },
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ d_col = d;
+ if (d_sel) {
+ that.update_createrecord_loadcomplete(d_sel, d_col);
+ }
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ createtable: function() {
+ var that = this;
+ var flags = 0;
+ $('#createtable-flags>input:checked').each(function() {
+ flags |= Groonga[$(this).val()];
+ });
+ flags |= Groonga[$('#createtable-key-index').val()];
+ this.showloading(
+ $.ajax({
+ url: '/d/table_create',
+ data: {
+ name: $('#createtable-name').val(),
+ 'flags': flags,
+ key_type: $('#createtable-key-type').val(),
+ value_type: $('#createtable-value-type').val(),
+ default_tokenizer: $('#createtable-default-tokenizer').val()
+ },
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ that.hideloading();
+ alert('テーブルを作成しました。');
+ that.update_tablelist();
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ createcolumn: function() {
+ var that = this;
+ var flags = 0;
+ $('#createcolumn-flags>input:checked').each(function() {
+ flags |= Groonga[$(this).val()];
+ });
+ $('#createcolumn-ii-flags>input:checked').each(function() {
+ flags |= Groonga[$(this).val()];
+ });
+ flags |= Groonga[$('#createcolumn-column-type').val()];
+ flags |= Groonga[$('#createcolumn-column-compress').val()];
+ var d = {
+ table: this.current_table,
+ name: $('#createcolumn-name').val(),
+ 'flags': flags,
+ type: $('#createcolumn-type').val()
+ };
+ if ($('#createcolumn-source').val()) {
+ d['source'] = $('#createcolumn-source').val();
+ }
+ this.showloading(
+ $.ajax({
+ url: '/d/column_create',
+ data: d,
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ that.hideloading();
+ alert('カラムを作成しました。');
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ createrecord_getvalue: function(type, inputbox) {
+ switch(type){
+ case "Bool":
+ if (inputbox.is('input:checked')) {
+ return true;
+ } else {
+ return false;
+ }
+ default:
+ return inputbox.val();
+ }
+ },
+ createrecord: function() {
+ var that = this;
+ var d = {};
+ $('.create-record-columns').each(function() {
+ if (!$(this).children('.columnval').children().attr('disabled')
+ || $(this).children('.columnname').text() == "_key") {
+ var type = $(this).children('.columntype').children().text();
+ if ($(this).children('.columnval').children('span').length) {
+ var arr = [];
+ $(this).children('.columnval').children('.column_values').each(function() {
+ arr.push(that.createrecord_getvalue(type, $(this)));
+ });
+ d[$(this).children('.columnname').text()] = arr;
+ } else {
+ d[$(this).children('.columnname').text()] =
+ that.createrecord_getvalue(type, $(this).children('.columnval').children());
+ }
+ }
+ });
+ this.showloading(
+ $.ajax({
+ url: '/d/load',
+ data: {
+ "table" : this.current_table,
+ "input_type" : "json",
+ "output_type" : "json",
+ "values" : JSON.stringify([d])
+ },
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ that.hideloading();
+ alert('レコードを作成しました。');
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ removerecord: function() {
+ var that = this;
+ var checklist = $("#tab-recordlist-table").find("input:checked");
+ var completecount = checklist.length;
+ if (completecount > 0) {
+ $('<div />')
+ .append("選択した" + completecount + "件のレコードを削除しますか?")
+ .dialog({
+ modal: true,
+ buttons: {
+ 'いいえ': function() {
+ $(this).dialog('close');
+ },
+ 'はい': function() {
+ $(this).dialog('close');
+ checklist.each(function(i, val) {
+ that.showloading(
+ $.ajax({
+ url: '/d/delete',
+ data: {
+ "table" : that.current_table,
+ "id" : val.value
+ },
+ dataType: 'json',
+ success: function() {
+ if (--completecount == 0) {
+ $('#tab-recordlist-form').submit();
+ alert('レコードを削除しました。');
+ } else if (completecount < 0){
+ that.hideloading();
+ }
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ completecount = 0;
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ });
+ }
+ }
+ });
+ }
+ },
+ removecolumn: function() {
+ var that = this;
+ var checklist = $("#tab-columnlist-table").find("input:checked");
+ var completecount = checklist.length;
+ if (completecount) {
+ $('<div />')
+ .append("選択した" + completecount + "件のカラムを削除しますか?")
+ .dialog({
+ modal: true,
+ buttons: {
+ 'いいえ': function() {
+ $(this).dialog('close');
+ },
+ 'はい': function() {
+ $(this).dialog('close');
+ checklist.each(function(i, val) {
+ that.showloading(
+ $.ajax({
+ url: '/d/column_remove',
+ data: {
+ "table" : that.current_table,
+ "name" : val.value
+ },
+ dataType: 'json',
+ success: function() {
+ if (!(--completecount)) {
+ that.columnlist(that.current_table);
+ alert('カラムを削除しました。');
+ } else if (completecount < 0){
+ that.hideloading();
+ }
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ completecount = 0;
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ });
+ }
+ }
+ });
+ }
+ },
+ removetable: function() {
+ var that = this;
+ var checklist = $("#tab-tablelist-table").find("input:checked");
+ var completecount = checklist.length;
+ if (completecount > 0) {
+ $('<div />')
+ .append("選択した" + completecount + "件のテーブルを削除しますか?")
+ .dialog({
+ modal: true,
+ buttons: {
+ 'いいえ': function() {
+ $(this).dialog('close');
+ },
+ 'はい': function() {
+ $(this).dialog('close');
+ checklist.each(function(i, val) {
+ that.showloading(
+ $.ajax({
+ url: '/d/table_remove',
+ data: {
+ "name" : val.value
+ },
+ dataType: 'json',
+ success: function() {
+ if (--completecount == 0) {
+ that.tablelist();
+ that.update_tablelist();
+ alert('テーブルを削除しました。');
+ } else if (completecount < 0){
+ that.hideloading();
+ }
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ completecount = 0;
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ });
+ }
+ }
+ });
+ }
+ },
+ showloading: function(obj, hide_dialog) {
+ var that = this;
+ if (obj == null) { return; }
+ this.semaphore[this.semaphore.length] = obj;
+ if ( $("#loadingdialog").size() > 0 || hide_dialog) { return; }
+ $("<div />")
+ .attr("id", "loadingdialog")
+ .attr("style", "text-align: center;")
+ .append($("<img />").attr("src", "images/loading.gif"))
+ .append(" Loading...")
+ .dialog({
+ title: "",
+ width: 200,
+ height: 110,
+ minHeight: 110,
+ modal: true,
+ resizable: false,
+ draggable: false,
+ position: ["right", "bottom"],
+ autoOpen: false,
+ buttons: {
+ '中止': function() {
+ if (obj) { obj.abort(); }
+ that.hideloading();
+ }
+ }
+ });
+ $("#loadingdialog").parents(".ui-dialog").children(".ui-dialog-titlebar").remove();
+ $("#loadingdialog").dialog("open");
+ $(".ui-widget-overlay").css("opacity", "0.0");
+ },
+ hideloading: function() {
+ for (var i = 0; i < this.semaphore.length; i++) {
+ if ( this.semaphore[i].readyState == 4) {
+ this.semaphore.splice(i, 1);
+ i--;
+ }
+ }
+ if ( this.semaphore.length == 0) {
+ $("#loadingdialog").dialog("close");
+ $("#loadingdialog").remove();
+ }
+ },
+ errorloading: function(ajax, hide_dialog) {
+ var that = this;
+ var json = null;
+ if (ajax) {
+ json = JSON.parse(ajax.responseText);
+ }
+ this.hideloading();
+ for (var i = 0; i < this.semaphore.length; i++) {
+ this.semaphore[i].abort();
+ this.semaphore.splice(i, 1);
+ i--;
+ }
+ if ( $("#loadingdialog").size() == 0 && !hide_dialog) {
+ var error_label;
+ var error_message;
+ if (json){
+ error_label = "groongaでエラーが発生しました。";
+ error_message = json[0][3] + "(" + json[0][0] + ")";
+ } else if (ajax) {
+ error_label = "通信エラーが発生しました。";
+ error_message = "" + ajax.status + ": " + ajax.statusText;
+ } else {
+ error_label = "通信エラーが発生しました。";
+ error_message = "";
+ }
+ $("<div />")
+ .append($("<div />").text(error_label))
+ .append($("<div />").text(error_message))
+ .attr("id", "loadingdialog")
+ .dialog({
+ title: "",
+ width: 340,
+ height: 160,
+ minHeight: 160,
+ modal: true,
+ resizable: false,
+ draggable: false,
+ open: function() {
+ $(this).parents(".ui-dialog").children(".ui-dialog-titlebar").remove();
+ },
+ buttons: { OK: function() { that.hideloading(); } }
+ });
+ }
+ },
+ validateajax: function(d, hide_dialog) {
+ if (!d) {
+ this.errorloading(null, hide_dialog);
+ return -1;
+ }
+ return 0;
+ }
+});
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/js/groonga-admin.js b/storage/mroonga/vendor/groonga/data/html/admin/js/groonga-admin.js
new file mode 100644
index 00000000000..8d07beadc52
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/js/groonga-admin.js
@@ -0,0 +1,1372 @@
+// -*- js2-basic-offset: 2; indent-tabs-mode: nil -*-
+
+"use strict";
+
+function prim2html(prim, limit) {
+ switch(typeof prim) {
+ case 'undefined':
+ return 'undefined';
+ case 'boolean':
+ return prim ? 'true' : 'false';
+ case 'number':
+ return String(prim);
+ case 'string':
+ if (prim.length > limit) {
+ prim = prim.substring(0, limit) + '...';
+ }
+ return escapeHTML(prim);
+ case 'array':
+ case 'object':
+ if (prim == null) {
+ return 'null';
+ } else if ($.isArray(prim)) {
+ return 'array'; /* TODO: implement */
+ } else {
+ return 'object'; /* TODO: implement */
+ }
+ default:
+ return 'ERROR';
+ }
+}
+
+function escapeHTML(str) {
+ return str.replace(/&/g, "&amp;")
+ .replace(/"/g, "&quot;")
+ .replace(/</g, "&lt;")
+ .replace(/>/g, "&gt;");
+}
+var Groonga = {
+ key_type_list: ['Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32',
+ 'Int64', 'UInt64', 'Float', 'Time', 'ShortText',
+ 'TokyoGeoPoint', 'WGS84GeoPoint'],
+ value_type_list: ['Object', 'Bool',
+ 'Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32',
+ 'Int64', 'UInt64', 'Float', 'Time'],
+ column_type_list: ['Object', 'Bool',
+ 'Int8', 'UInt8', 'Int16', 'UInt16', 'Int32', 'UInt32',
+ 'Int64', 'UInt64', 'Float', 'Time', 'ShortText',
+ 'Text', 'LongText', 'TokyoGeoPoint', 'WGS84GeoPoint'],
+ tokenizer_list: ['TokenDelimit', 'TokenUnigram', 'TokenBigram', 'TokenTrigram', 'TokenMecab'],
+ GRN_OBJ_PERSISTENT: (0x01<<15),
+
+ GRN_OBJ_TABLE_TYPE_MASK: (0x07),
+ GRN_OBJ_TABLE_HASH_KEY: (0x00),
+ GRN_OBJ_TABLE_PAT_KEY: (0x01),
+ GRN_OBJ_TABLE_NO_KEY: (0x03),
+
+ GRN_OBJ_KEY_WITH_SIS: (0x01<<6),
+ GRN_OBJ_KEY_NORMALIZE: (0x01<<7),
+
+ GRN_OBJ_COLUMN_TYPE_MASK: (0x07),
+ GRN_OBJ_COLUMN_SCALAR: (0x00),
+ GRN_OBJ_COLUMN_VECTOR: (0x01),
+ GRN_OBJ_COLUMN_INDEX: (0x02),
+
+ GRN_OBJ_COMPRESS_MASK: (0x07<<4),
+ GRN_OBJ_COMPRESS_NONE: (0x00<<4),
+ GRN_OBJ_COMPRESS_ZLIB: (0x01<<4),
+ GRN_OBJ_COMPRESS_LZO: (0x02<<4),
+
+ GRN_OBJ_WITH_SECTION: (0x01<<7),
+ GRN_OBJ_WITH_WEIGHT: (0x01<<8),
+ GRN_OBJ_WITH_POSITION: (0x01<<9)
+};
+
+$.widget("ui.paginate", {
+ version: "1.0",
+ options: {
+ total: 0,
+ nItemsPerPage: 10,
+ currentPage: 0,
+ nShowLinks: 10,
+ callback: null
+ },
+ _create: function() {
+ var that = this;
+ var element = this.element;
+ element.addClass("pager");
+
+ var total = this.options.total;
+ var nItemsPerPage = this.options.nItemsPerPage;
+ var currentPage = this.options.currentPage;
+ var nShowLinks = this.options.nShowLinks;
+ var lastPage = Math.floor((total - 1) / nItemsPerPage) + 1;
+ var start = currentPage - Math.floor(nShowLinks / 2);
+ start = (start < 1) ? 1 : start;
+ var end = start + nShowLinks - 1;
+ end = (end > lastPage) ? lastPage : end;
+
+ var callback = this.options.callback;
+ if (start > 1) {
+ element.append($('<span />')
+ .addClass('pager')
+ .append($('<a />')
+ .attr('href', '#')
+ .text('1')
+ .click(function () {callback(0)})));
+ element.append($('<span />').text('....'));
+ }
+ for (var i = start; i <= end; i++) {
+ var page = $('<span />').append($('<a />')
+ .attr('href', '#')
+ .text(String(i))
+ .click(function () {
+ callback(Number($(this).text()) - 1);
+ }));
+ if (i == currentPage) {
+ page.addClass('pager-current');
+ } else {
+ page.addClass('pager');
+ }
+ element.append(page);
+ }
+ if (end < lastPage) {
+ element.append($('<span />')
+ .text('....'));
+ element.append($('<span />')
+ .addClass('pager')
+ .append($('<a />')
+ .attr('href', '#')
+ .text(String(lastPage))
+ .click(function () {callback(lastPage - 1);})));
+ }
+ }
+});
+
+function GroongaAdmin() {
+ this.current_table = null;
+ this.statusTimer = null;
+ this.semaphore = new Array();
+ this.current_status = 0;
+ this.reload_record_func = function(){};
+
+ var that = this;
+ this._initializeTabs();
+
+ $('#tab-tablelist-link').click(function() {
+ that.tablelist();
+ });
+ $('#tab-columnlist-link').click(function() {
+ that.columnlist(that.current_table);
+ });
+ $('#tab-createrecord-link').click(function() {
+ that.update_createrecord(that.current_table);
+ });
+ $('#tab-recordlist-link').click(function() {
+ that.reload_record_func();
+ });
+ $('#createtable-add-table').click(function() {
+ that.createtable();
+ });
+ $('#createrecord-add-record').click(function() {
+ that.createrecord();
+ });
+ $('#createcolumn-add-column').click(function() {
+ that.createcolumn();
+ });
+ $('#recordlist-remove-record').click(function() {
+ that.removerecord();
+ });
+ $('#columnlist-remove-column').click(function() {
+ that.removecolumn();
+ });
+ $('#tablelist-remove-table').click(function() {
+ that.removetable();
+ });
+ $('#tab-recordlist-form').submit(function() {
+ if ($('#table-tab-recordlist-full-checkbox').attr('checked')) {
+ // full
+ var d = {
+ 'table': that.current_table
+ }
+ $.each(that.SELECT_PARAMS, function(i, val) {
+ var e = $('#tab-recordlist-' + val);
+ if (e.val()) {
+ d[val] = e.val();
+ }
+ });
+ that.recordlist(d, true);
+ } else {
+ // simple
+ that.recordlist_simple(
+ that.current_table,
+ $('#tab-recordlist-simplequery').val(),
+ $('#tab-recordlist-simplequerytype').val(),
+ 1);
+ }
+ return false;
+ });
+ this._initializeSideMenu();
+ this.update_tablelist();
+
+ var e1 = $('#createtable-key-type-builtin');
+ $.each(Groonga.key_type_list, function(i, val) {
+ e1.append($('<option />').val(val).text(val));
+ });
+
+ e1 = $('#createtable-value-type-builtin');
+ e1.append($('<option />').val('').text('None'));
+ $.each(Groonga.value_type_list, function(i, val) {
+ e1.append($('<option />').val(val).text(val));
+ });
+
+ e1 = $('#createtable-default-tokenizer-builtin');
+ e1.append($('<option />').val('').text('None'));
+ $.each(Groonga.tokenizer_list, function(i, val) {
+ e1.append($('<option />').val(val).text(val));
+ });
+
+ e1 = $('#createcolumn-type-builtin');
+ $.each(Groonga.column_type_list, function(i, val) {
+ e1.append($('<option />').val(val).text(val));
+ });
+
+ $('#tab-recordlist-simplequerytype').change(function() {
+ if ($(this).val() == 'scorer') {
+ $('#tab-recordlist-incremental').hide();
+ $('#tab-recordlist-incremental-label').hide();
+ } else {
+ $('#tab-recordlist-incremental').show();
+ $('#tab-recordlist-incremental-label').show();
+ }
+ $('#tab-recordlist-incremental').change();
+
+ var selectedOption = $(this).find(':selected');
+ $('#tab-recordlist-simplequery').attr(
+ 'placeholder', selectedOption.data('placeholder')
+ );
+ }).change();
+
+ $('#table-tab-recordlist-full-checkbox').change(function() {
+ if ($(this).attr('checked')) {
+ $('#table-tab-recordlist-form-simple').hide();
+ $('#table-tab-recordlist-form-full').show();
+ } else {
+ $('#table-tab-recordlist-form-simple').show();
+ $('#table-tab-recordlist-form-full').hide();
+ }
+ }).change();
+
+ $('#tab-recordlist-incremental').change(function() {
+ $('#tab-recordlist-simplequery').unbind('keyup');
+ if ($(this).attr('checked') &&
+ $('#tab-recordlist-simplequerytype').val() != 'scorer') {
+ $('#tab-recordlist-simplequery').keyup(function(e) {
+ that.recordlist_simple(
+ that.current_table,
+ $('#tab-recordlist-simplequery').val(),
+ $('#tab-recordlist-simplequerytype').val(),
+ 1,
+ true);
+ });
+ }
+ }).change();
+
+ $('#createcolumn-type').change(function(e) {
+ var s = $('#createcolumn-type-table option:selected');
+ var cs = $('#createcolumn-source');
+ if (s.length > 0) {
+ cs.empty().removeAttr('disabled');
+ that.showloading(
+ $.ajax({
+ url: '/d/column_list',
+ data: {'table': s.val()},
+ dataType: 'json',
+ success: function(d) {
+ if(that.validateajax(d) < 0) { return; }
+ var idx;
+ var b = d[1];
+ $.each(b[0], function(i, val) {
+ if (val[0] == 'name') { idx = i; }
+ });
+ if (idx) {
+ b.shift();
+ $.each(b, function(i, val) {
+ cs.append($('<option />').val(val[idx]).text(val[idx]));
+ });
+ }
+ that.hideloading();
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ } else {
+ cs.empty().attr('disabled', 'disabled');
+ }
+ });
+
+ this.recordlist_count = 30;
+};
+
+jQuery.extend(GroongaAdmin.prototype, {
+ SELECT_PARAMS: [
+ 'match_columns', 'query', 'filter',
+ 'scorer',
+ 'output_columns',
+ 'sortby', 'offset', 'limit',
+ 'drilldown',
+ 'drilldown_output_columns',
+ 'drilldown_sortby', 'drilldown_offset', 'drilldown_limit'
+ ],
+ _initializeTabs: function() {
+ this._initializeDatabaseTab();
+ this._initializeTableTab();
+ this._initializeSuggestTab();
+ this._selectTab("database");
+ },
+ _initializeDatabaseTab: function() {
+ var that = this;
+
+ this._$databaseTabs = $('#database-tabs').tabs({
+ show: function(e, ui) {
+ that.stop_status_timer();
+ if (ui.panel.id == 'database-tab-summary') {
+ that.start_status_timer();
+ }
+ }
+ });
+ },
+ _initializeTableTab: function() {
+ this._$tableTabs = $('#table-tabs').tabs({
+ show: function(e, ui) {
+ }
+ });
+ },
+ _initializeSuggestTab: function() {
+ var that = this;
+
+ this._$suggestTabs = $('#suggest-tabs').tabs({
+ show: function(e, ui) {
+ }
+ });
+
+ this._initializeSuggestDatasetComplete();
+ this._initializeSuggestQueryComplete();
+ this._initializeSuggestSubmit();
+ this._initializeSuggestResult();
+ },
+ _initializeSuggestDatasetComplete: function() {
+ var that = this;
+ var $dataset = $("#suggest-dataset");
+ this._$suggestDataset = $dataset;
+ $dataset.autocomplete({
+ minLength: 0,
+ source: function (request, response) {
+ var datasets = [];
+ $.each(that._tables, function(i, table_name) {
+ var suggestTableMatch = /^item_(.+)$/.exec(table_name);
+ if (suggestTableMatch) {
+ var dataset = suggestTableMatch[1];
+ datasets.push(dataset);
+ }
+ });
+
+ datasets = $.ui.autocomplete.filter(datasets, request.term);
+ response(datasets);
+ }
+ });
+ $dataset.focus(function (event) {
+ $dataset.autocomplete("search", $dataset.val());
+ });
+ },
+ _suggestParameters: function(query, dataset, type) {
+ var nItemsPerPage = 30;
+ return {
+ query: query,
+ types: type,
+ table: "item_" + dataset,
+ column: "kana",
+ offset: 0,
+ limit: nItemsPerPage,
+ };
+ },
+ _initializeSuggestQueryComplete: function() {
+ var that = this;
+ this._$suggestQuery = $("#suggest-query").autocomplete({
+ source: function (request, response) {
+ var $dataset = $("#suggest-dataset");
+ var dataset = $dataset.val();
+ $("#suggest-submit").click();
+ $.ajax({
+ url: "/d/suggest",
+ data: that._suggestParameters(request.term, dataset, "complete"),
+ dataType: "jsonp",
+ success: function (data, textStatus, jqXHR) {
+ var completions = data[1]["complete"];
+ var items = [];
+ if (completions && completions.length > 2) {
+ completions.shift();
+ completions.shift();
+ $.each(completions, function(i, item) {
+ var key = item[0];
+ items.push(key);
+ if (items.length >= 3) {
+ return false;
+ }
+ return true;
+ });
+ }
+ response(items);
+ },
+ error: function(jqXHR, textStatus, errorThrown) {
+ }
+ });
+ }
+ });
+ },
+ _initializeSuggestSubmit: function() {
+ var that = this;
+ $("#suggest-submit").click(function (event) {
+ var dataset = $("#suggest-dataset").val();
+ var query = $("#suggest-query").val();
+ var type = that._suggestResultType;
+ var parameters = that._suggestParameters(query, dataset, type);
+ $.ajax({
+ url: "/d/suggest",
+ data: parameters,
+ dataType: "jsonp",
+ success: function (data, textStatus, jqXHR) {
+ var response = data[1][type];
+ response.shift();
+ var $result = $("#suggest-result-tab-" + type);
+ $result
+ .empty()
+ .append($("<div/>").append(that._createResultTable(response)));
+ },
+ error: function(jqXHR, textStatus, errorThrown) {
+ }
+ });
+ });
+ },
+ _initializeSuggestResult: function() {
+ var that = this;
+ $("#suggest-result-tabs").tabs({
+ show: function (event, ui) {
+ that._suggestResultType = ui.panel.id.replace(/^suggest-result-tab-/, "");
+ $("#suggest-submit").click();
+ }
+ });
+ },
+ _selectTab: function(name) {
+ this.stop_status_timer();
+ this._$databaseTabs.hide();
+ this._$tableTabs.hide();
+ this._$suggestTabs.hide();
+ switch (name) {
+ case "table":
+ this._$tableTabs.show();
+ break;
+ case "suggest":
+ this._$suggestTabs.show();
+ break;
+ case "database":
+ default:
+ this._$databaseTabs.show();
+ break;
+ }
+ },
+ _initializeSideMenu: function () {
+ var that = this;
+ $('#side-menu-summary').click(function() {
+ that.current_table = null;
+ that._selectTab("database");
+ that._$databaseTabs.tabs("select", "#database-tab-summary");
+ });
+ $('#side-menu-suggest').click(function() {
+ that.current_table = null;
+ that._selectTab("suggest");
+ });
+ },
+ start_status_timer: function() {
+ var that = this;
+ this.stop_status_timer();
+ this.status();
+ this.statusTimer = setInterval(function() {that.status()}, 1000);
+ },
+ change_status_timer: function(time) {
+ var that = this;
+ this.stop_status_timer();
+ this.statusTimer = setInterval(function() {that.status()}, time);
+ },
+ stop_status_timer: function() {
+ if (this.statusTimer) {
+ clearInterval(this.statusTimer);
+ this.statusTimer = null;
+ }
+ },
+ _createResultTable: function (result, options) {
+ var that = this;
+ if (!options) {
+ options = {};
+ }
+ var table = $('<table class="records"/>');
+ if ($.isArray(result)) {
+ var nEntries = result.length;
+ if (nEntries >= 1) {
+ var thead = $('<thead/>');
+ table.append(thead);
+ var line = result[0];
+ if ($.isArray(line)) {
+ var tr = $('<tr/>');
+ thead.append(tr);
+ var m = line.length;
+ if (options.check) {
+ tr.append($('<th/>'));
+ }
+ for (var j = 0; j < m; j++) {
+ var th = $('<th/>');
+ tr.append(th);
+ th.append(prim2html(line[j][0], 128));
+ th.append($('<br />'));
+ th.append(prim2html(line[j][1], 128));
+ }
+ if (options.button) {
+ tr.append($('<th/>'));
+ }
+ }
+ var tbody = $('<tbody>');
+ table.append(tbody);
+ for (var i = 1; i < nEntries; i++) {
+ line = result[i];
+ if ($.isArray(line)) {
+ var tr = $('<tr>');
+ table.append(tr);
+ var m = line.length;
+ switch(options.check) {// チェックボックスの値を何にするか
+ case 1: // 1番目の要素(レコード一覧の_id等)
+ case 2: // 2番目の要素(テーブル・カラム一覧のname等)
+ var td = $('<td/>');
+ tr.append(td);
+ td.append($('<input/>')
+ .attr("type", "checkbox")
+ .attr("value", line[options.check-1]));
+ break;
+ }
+ for (var j = 0; j < m; j++) {
+ var td = $('<td/>');
+ tr.append(td);
+ td.append(prim2html(line[j], 128));
+ }
+ switch(options.button) {
+ case 1: // Edit record
+ var td = $('<td/>');
+ tr.append(td);
+ td.append($('<input/>')
+ .attr("type", "button")
+ .attr("value", "Edit")
+ .attr("data-record-id", line[0])
+ .click(function () {
+ that.show_edit_record($(this).attr("data-record-id"));
+ }));
+ break;
+ case 2: // Table
+ var td = $('<td/>');
+ tr.append(td);
+ td.append($('<input/>')
+ .attr("type", "button")
+ .attr("value", "Detail")
+ .attr("data-table-name", line[1])
+ .click(function () {
+ var tableName = $(this).attr("data-table-name");
+ $("#side-menu-tablelist-link-" + tableName).click();
+ }));
+ break;
+ }
+ }
+ }
+ }
+ }
+ return table;
+ },
+ show_edit_record: function(id) {
+ $('#table-tabs').tabs('select', 2);
+ this.update_createrecord(this.current_table, id);
+ },
+ format_unix_time: function(unix_time) {
+ var date = new Date();
+ date.setTime(unix_time * 1000);
+ return date.toLocaleString();
+ },
+ format_duration: function(duration_in_seconds) {
+ var duration = "";
+ var days = Math.floor(duration_in_seconds / 3600 / 24);
+ var hours = Math.floor(duration_in_seconds / 3600 % 24);
+ var minutes = Math.floor(duration_in_seconds / 60 % 60);
+ var seconds = Math.floor(duration_in_seconds % 60);
+
+ if (days > 0) {
+ duration += days;
+ if (days == 1) {
+ duration += " day, ";
+ } else {
+ duration += " days, ";
+ }
+ }
+ if (days > 0 || hours > 0) {
+ duration += hours + ":" + minutes + ":" + seconds;
+ } else if (minutes > 0) {
+ duration += minutes + ":" + seconds;
+ } else {
+ duration += seconds;
+ }
+
+ return duration;
+ },
+ maxThroughput: 0,
+ lastNQueries: -1,
+ keepLastNData: 100,
+ throughputData: [],
+ throughputChart: null,
+ updateThroughputChart: function(statusData) {
+ var maxThroughputUpdated = false;
+ if (this.lastNQueries >= 0) {
+ var throughput = statusData.n_queries - this.lastNQueries;
+ this.throughputData.push(throughput);
+ if (this.maxThroughput < throughput) {
+ this.maxThroughput = throughput;
+ maxThroughputUpdated = true;
+ }
+ }
+ if (this.throughputData.length > this.keepLastNData) {
+ this.throughputData.shift();
+ }
+ if (!this.throughputChart) {
+ this.throughputChart = $.plot($("#throughput-chart"),
+ [[]],
+ {xaxis: {min: -(this.keepLastNData - 1),
+ max: 0},
+ yaxis: {min: 0}});
+ }
+ var that = this;
+ var chartSeries = $.map(this.throughputData, function(n, i) {
+ return [[-(that.throughputData.length - i) + 1, n]];
+ });
+ this.throughputChart.setData([chartSeries]);
+ if (maxThroughputUpdated) {
+ this.throughputChart.setupGrid();
+ }
+ this.throughputChart.draw();
+ this.lastNQueries = statusData.n_queries;
+ },
+ status: function() {
+ if (this.current_status > 0) { return; }
+ this.current_status++;
+ var that = this;
+ $.ajax({
+ url: '/d/status',
+ data: {},
+ dataType: 'json',
+ success: function(b) {
+ that.current_status--;
+ if (!b) {
+ that.change_status_timer(10000);
+ return;
+ }
+ var d = b[1];
+ $('#status-starttime').text(that.format_unix_time(d.starttime));
+ $('#status-uptime').text(that.format_duration(d.uptime));
+ $('#status-n-queries').text(d.n_queries);
+ $('#status-cache-hit-rate').text(d.cache_hit_rate);
+ that.updateThroughputChart(d);
+ that.change_status_timer(1000);
+ },
+ error: function() {
+ that.current_status--;
+ that.change_status_timer(10000);
+ }
+ });
+ },
+ update_tablelist: function() {
+ var that = this;
+ this._tables = [];
+ this.showloading(
+ $.ajax({
+ url: '/d/table_list',
+ data: {},
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ d.shift();
+ var tl = $('#side-menu-tablelist').empty();
+ var tt = $('#createtable-key-type-table').empty();
+ var vt = $('#createtable-value-type-table').empty();
+ var ct = $('#createcolumn-type-table').empty();
+ var b = d.shift();
+ b.shift();
+ $.each(b, function(i, val) {
+ var table_name = val[1];
+ that._tables.push(table_name);
+ tl.append(
+ $('<li />').append(
+ $('<a />')
+ .attr('id', 'side-menu-tablelist-link-' + table_name)
+ .attr('href', '#side-menu-tablelist-' + table_name)
+ .text(table_name)
+ .click(function() {
+ that.current_table = table_name;
+ $('#database-tabs').hide();
+ $('#suggest-tabs').hide();
+ that.stop_status_timer();
+ $('#table-tabs').show();
+ that.columnlist(table_name);
+ $('#tab-recordlist-simplequery').val('');
+ that.recordlist_simple(table_name, null, null, 1);
+ that.update_createrecord(that.current_table);
+ })
+ )
+ );
+ tt.append($('<option />').val(val[1]).text(val[1]));
+ vt.append($('<option />').val(val[1]).text(val[1]));
+ ct.append($('<option />').val(val[1]).text(val[1]));
+ });
+ that.hideloading();
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ tablelist: function() {
+ $('#tab-tablelist-table').empty();
+ var that = this;
+ this.showloading(
+ $.ajax({
+ url: '/d/table_list',
+ data: {},
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ var b = d[1];
+ var table = that._createResultTable(b, {check: 2, button: 2});
+ $('#tab-tablelist-table').append($('<h1 />').text('List of table')).append(table);
+ that.hideloading();
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ recordlist_simple: function(table_name, simplequery, simplequery_type, page, hide_dialog) {
+ var d = {
+ 'table': table_name,
+ 'offset': (page - 1) * this.recordlist_count,
+ 'limit': this.recordlist_count
+ }
+ switch (simplequery_type) {
+ case 'query':
+ case 'filter':
+ case null:
+ if (simplequery) {
+ d[simplequery_type] = simplequery;
+ }
+ this.recordlist(d, true, hide_dialog);
+ break;
+ }
+ },
+ recordlist: function(params, show_pager, hide_dialog) {
+ var that = this;
+ this.reload_record_func = function(){
+ that.recordlist(params, show_pager, hide_dialog);
+ };
+ this.showloading(
+ $.ajax({
+ url: '/d/select',
+ data: params,
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d, hide_dialog) < 0) { return; }
+ var rc = d.shift();
+ if (rc[0] != 0) {
+ alert('error: ' + rc[3]);
+ that.hideloading();
+ return false;
+ }
+ var body = d.shift();
+ var recs = body.shift();
+ var all_count = recs.shift()[0];
+ var pager;
+ if (show_pager) {
+ var offset = params['offset'] || 0;
+ var rows = params['limit'] || 10;
+ if (rows < 0){
+ rows = all_count + parseInt(rows) + 1;
+ }
+ if (rows != '' && !parseInt(rows)) {
+ pager = $('<span />');
+ } else {
+ pager = $("<div/>");
+ pager.paginate({
+ total: all_count,
+ nItemsPerPage: rows,
+ currentPage: Math.floor(offset/rows)+1,
+ callback: function(page) {
+ params['offset'] = page * rows;
+ that.recordlist(params, true, false);
+ return false;
+ }
+ });
+ }
+ } else {
+ pager = $('<span />');
+ }
+ $('#tab-recordlist-table')
+ .empty()
+ .append($('<h1 />').text('List of records: ' + params['table']))
+ .append($('<p />').text('Total count: ' + all_count))
+ .append(pager.clone(true))
+ .append($('<div />').append(that._createResultTable(recs, {check: 1, button: 1})))
+ .append(pager);
+ that.hideloading();
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest, hide_dialog);
+ }
+ })
+ ,hide_dialog);
+ },
+ columnlist: function(table_name) {
+ var that = this;
+ $('#tab-columnlist-table').empty();
+ this.showloading(
+ $.ajax({
+ url: '/d/column_list',
+ data: {'table': table_name},
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ var b = d[1];
+ var table = that._createResultTable(b, {check: 2});
+ $('#tab-columnlist-table')
+ .append($('<h1 />').text('List of columns: ' + table_name))
+ .append(table);
+ that.hideloading();
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ add_record_inputbox: function(type, value) {
+ var inputbox = null;
+ switch(type){
+ case "Bool":
+ inputbox = $('<input />')
+ .attr("type","checkbox")
+ .attr("value","true");
+ if (value) {
+ inputbox.attr("checked","");
+ }
+ break;
+ case "UInt8":
+ case "UInt16":
+ case "UInt32":
+ case "UInt64":
+ case "Int8":
+ case "Int16":
+ case "Int32":
+ case "Int64":
+ case "Float":
+ inputbox = $('<input />')
+ .attr("type", "text")
+ .val(isNaN(value) ? "" : value);
+ break;
+ case "Text":
+ case "ShortText":
+ case "LongText":
+ inputbox = $('<textarea />')
+ .attr("cols", "50")
+ .attr("rows", "2")
+ .text(value ? value : "");
+ break;
+ case "TokyoGeoPoint":
+ case "WGS84GeoPoint":
+ case "Time":
+ inputbox = $('<input />')
+ .attr("type", "text")
+ .attr("size", "40")
+ .val(value ? value : "");
+ break;
+ case "Object":
+ inputbox = $('<input />')
+ .attr("type", "text")
+ .attr("disabled", "disabled");
+ break;
+ default:
+ inputbox = $('<input />')
+ .attr("type", "text")
+ .val(value ? value : "");
+ }
+ inputbox.addClass('column_values');
+ return inputbox;
+ },
+ add_record_deletebutton: function(){
+ var ret =
+ $('<span />')
+ .append("[×]")
+ .css('cursor', 'pointer')
+ .click(function() {
+ $(this).prev().remove();
+ $(this).next().remove();
+ $(this).remove();
+ });
+ return ret;
+ },
+ update_createrecord_loadcomplete: function(d_sel, d_col) {
+ var that = this;
+ var b = d_sel[1][0];
+ var columns = $('<tbody />');
+ var listofs = b[1].length - (d_col[1].length - 1);
+ for (var i = 1; i < b[1].length; i++) {
+ var line = b[1][i];
+ var value = null;
+ if (b[2]) value = b[2][i];
+ if ($.isArray(line)) {
+ var tr = $('<tr/ >')
+ .addClass('create-record-columns')
+ .append(
+ $('<td />')
+ .addClass('columnname')
+ .append(prim2html(line[0], 128))
+ )
+ .append(
+ $('<td />')
+ .addClass('columntype')
+ .append("(")
+ .append($('<span />')
+ .append(prim2html(line[1], 128))
+ )
+ .append(")")
+ );
+ var inputtd = $('<td />').addClass('columnval');
+ if (i >= listofs && d_col[1][i - listofs + 1][4].indexOf("COLUMN_VECTOR") >= 0){
+ var type = line[1];
+ if (value != null) {
+ for (var j = 0; j < value.length; j++) {
+ inputtd
+ .append(this.add_record_inputbox(line[1], value[j]))
+ .append(this.add_record_deletebutton())
+ .append('<br />');
+ }
+ }
+ inputtd
+ .append($('<span />')
+ .append("[Add value]")
+ .css('cursor', 'pointer')
+ .click(function() {
+ var target = $(this).parent();
+ target
+ .append(that.add_record_inputbox($(this).parent().prev().children().text()))
+ .append(that.add_record_deletebutton())
+ .append("<br />");
+ $(this).appendTo(target);
+ })
+ );
+ } else {
+ inputtd.append(this.add_record_inputbox(line[1], value));
+ if (line[0] == "_key" && value != null) {
+ inputtd.children().attr("disabled", "disabled");
+ }
+ }
+ tr.append(inputtd);
+ columns.append(tr);
+ }
+ }
+ $("#table-createrecord").append(columns);
+ this.hideloading();
+ },
+ update_createrecord: function(table_name, id) {
+ var that = this;
+ var d_sel = null;
+ var d_col = null;
+ $('#table-createrecord').empty();
+ this.showloading(
+ $.ajax({
+ url: '/d/select',
+ data: {
+ 'table' : table_name,
+ 'limit' : 1,
+ 'query' : '_id:' + id
+ },
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ d_sel = d;
+ if (d_col) {
+ that.update_createrecord_loadcomplete(d_sel, d_col);
+ }
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ this.showloading(
+ $.ajax({
+ url: '/d/column_list',
+ data: {
+ 'table' : table_name
+ },
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ d_col = d;
+ if (d_sel) {
+ that.update_createrecord_loadcomplete(d_sel, d_col);
+ }
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ createtable: function() {
+ var that = this;
+ var flags = 0;
+ $('#createtable-flags>input:checked').each(function() {
+ flags |= Groonga[$(this).val()];
+ });
+ flags |= Groonga[$('#createtable-key-index').val()];
+ this.showloading(
+ $.ajax({
+ url: '/d/table_create',
+ data: {
+ name: $('#createtable-name').val(),
+ 'flags': flags,
+ key_type: $('#createtable-key-type').val(),
+ value_type: $('#createtable-value-type').val(),
+ default_tokenizer: $('#createtable-default-tokenizer').val()
+ },
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ that.hideloading();
+ alert('Table is created.');
+ that.update_tablelist();
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ createcolumn: function() {
+ var that = this;
+ var flags = 0;
+ $('#createcolumn-flags>input:checked').each(function() {
+ flags |= Groonga[$(this).val()];
+ });
+ $('#createcolumn-ii-flags>input:checked').each(function() {
+ flags |= Groonga[$(this).val()];
+ });
+ flags |= Groonga[$('#createcolumn-column-type').val()];
+ flags |= Groonga[$('#createcolumn-column-compress').val()];
+ var d = {
+ table: this.current_table,
+ name: $('#createcolumn-name').val(),
+ 'flags': flags,
+ type: $('#createcolumn-type').val()
+ };
+ if ($('#createcolumn-source').val()) {
+ d['source'] = $('#createcolumn-source').val();
+ }
+ this.showloading(
+ $.ajax({
+ url: '/d/column_create',
+ data: d,
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ that.hideloading();
+ alert('Column is created.');
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ createrecord_getvalue: function(type, inputbox) {
+ switch(type){
+ case "Bool":
+ if (inputbox.is('input:checked')) {
+ return true;
+ } else {
+ return false;
+ }
+ default:
+ return inputbox.val();
+ }
+ },
+ createrecord: function() {
+ var that = this;
+ var d = {};
+ $('.create-record-columns').each(function() {
+ if (!$(this).children('.columnval').children().attr('disabled')
+ || $(this).children('.columnname').text() == "_key") {
+ var type = $(this).children('.columntype').children().text();
+ if ($(this).children('.columnval').children('span').length) {
+ var arr = [];
+ $(this).children('.columnval').children('.column_values').each(function() {
+ arr.push(that.createrecord_getvalue(type, $(this)));
+ });
+ d[$(this).children('.columnname').text()] = arr;
+ } else {
+ d[$(this).children('.columnname').text()] =
+ that.createrecord_getvalue(type, $(this).children('.columnval').children());
+ }
+ }
+ });
+ this.showloading(
+ $.ajax({
+ url: '/d/load',
+ data: {
+ "table" : this.current_table,
+ "input_type" : "json",
+ "output_type" : "json",
+ "values" : JSON.stringify([d])
+ },
+ dataType: 'json',
+ success: function(d) {
+ if (that.validateajax(d) < 0) { return; }
+ that.hideloading();
+ alert('Record is created.');
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ },
+ removerecord: function() {
+ var that = this;
+ var checklist = $("#tab-recordlist-table").find("input:checked");
+ var completecount = checklist.length;
+ if (completecount > 0) {
+ $('<div />')
+ .append("Delete selected " + completecount + " records?")
+ .dialog({
+ modal: true,
+ buttons: {
+ 'No': function() {
+ $(this).dialog('close');
+ },
+ 'Yes': function() {
+ $(this).dialog('close');
+ checklist.each(function(i, val) {
+ that.showloading(
+ $.ajax({
+ url: '/d/delete',
+ data: {
+ "table" : that.current_table,
+ "id" : val.value
+ },
+ dataType: 'json',
+ success: function() {
+ if (--completecount == 0) {
+ $('#tab-recordlist-form').submit();
+ alert('Records are deleted.');
+ } else if (completecount < 0){
+ that.hideloading();
+ }
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ completecount = 0;
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ });
+ }
+ }
+ });
+ }
+ },
+ removecolumn: function() {
+ var that = this;
+ var checklist = $("#tab-columnlist-table").find("input:checked");
+ var completecount = checklist.length;
+ if (completecount) {
+ $('<div />')
+ .append("Delete selected " + completecount + " columns?")
+ .dialog({
+ modal: true,
+ buttons: {
+ 'No': function() {
+ $(this).dialog('close');
+ },
+ 'Yes': function() {
+ $(this).dialog('close');
+ checklist.each(function(i, val) {
+ that.showloading(
+ $.ajax({
+ url: '/d/column_remove',
+ data: {
+ "table" : that.current_table,
+ "name" : val.value
+ },
+ dataType: 'json',
+ success: function() {
+ if (!(--completecount)) {
+ that.columnlist(that.current_table);
+ alert('Columns are deleted.');
+ } else if (completecount < 0){
+ that.hideloading();
+ }
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ completecount = 0;
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ });
+ }
+ }
+ });
+ }
+ },
+ removetable: function() {
+ var that = this;
+ var checklist = $("#tab-tablelist-table").find("input:checked");
+ var completecount = checklist.length;
+ if (completecount > 0) {
+ $('<div />')
+ .append("Delete selected " + completecount + " tables?")
+ .dialog({
+ modal: true,
+ buttons: {
+ 'No': function() {
+ $(this).dialog('close');
+ },
+ 'Yes': function() {
+ $(this).dialog('close');
+ checklist.each(function(i, val) {
+ that.showloading(
+ $.ajax({
+ url: '/d/table_remove',
+ data: {
+ "name" : val.value
+ },
+ dataType: 'json',
+ success: function() {
+ if (--completecount == 0) {
+ that.tablelist();
+ that.update_tablelist();
+ alert('Table are deleted.');
+ } else if (completecount < 0){
+ that.hideloading();
+ }
+ },
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ completecount = 0;
+ that.errorloading(XMLHttpRequest);
+ }
+ })
+ );
+ });
+ }
+ }
+ });
+ }
+ },
+ showloading: function(obj, hide_dialog) {
+ var that = this;
+ if (obj == null) { return; }
+ this.semaphore[this.semaphore.length] = obj;
+ if ( $("#loadingdialog").size() > 0 || hide_dialog) { return; }
+ $("<div />")
+ .attr("id", "loadingdialog")
+ .attr("style", "text-align: center;")
+ .append($("<img />").attr("src", "images/loading.gif"))
+ .append(" Loading...")
+ .dialog({
+ title: "",
+ width: 200,
+ height: 110,
+ minHeight: 110,
+ modal: true,
+ resizable: false,
+ draggable: false,
+ position: ["right", "bottom"],
+ autoOpen: false,
+ buttons: {
+ 'Abort': function() {
+ if (obj) { obj.abort(); }
+ that.hideloading();
+ }
+ }
+ });
+ $("#loadingdialog").parents(".ui-dialog").children(".ui-dialog-titlebar").remove();
+ $("#loadingdialog").dialog("open");
+ $(".ui-widget-overlay").css("opacity", "0.0");
+ },
+ hideloading: function() {
+ for (var i = 0; i < this.semaphore.length; i++) {
+ if ( this.semaphore[i].readyState == 4) {
+ this.semaphore.splice(i, 1);
+ i--;
+ }
+ }
+ if ( this.semaphore.length == 0) {
+ $("#loadingdialog").dialog("close");
+ $("#loadingdialog").remove();
+ }
+ },
+ errorloading: function(ajax, hide_dialog) {
+ var that = this;
+ var json = null;
+ if (ajax) {
+ json = JSON.parse(ajax.responseText);
+ }
+ this.hideloading();
+ for (var i = 0; i < this.semaphore.length; i++) {
+ this.semaphore[i].abort();
+ this.semaphore.splice(i, 1);
+ i--;
+ }
+ if ( $("#loadingdialog").size() == 0 && !hide_dialog) {
+ var error_label;
+ var error_message;
+ if (json){
+ error_label = "Groonga reports error.";
+ error_message = json[0][3] + "(" + json[0][0] + ")";
+ } else if (ajax) {
+ error_label = "Connection error is occured.";
+ error_message = "" + ajax.status + ": " + ajax.statusText;
+ } else {
+ error_label = "Connection error is occured.";
+ error_message = "";
+ }
+ $("<div />")
+ .append($("<div />").text(error_label))
+ .append($("<div />").text(error_message))
+ .attr("id", "loadingdialog")
+ .dialog({
+ title: "",
+ width: 340,
+ height: 160,
+ minHeight: 160,
+ modal: true,
+ resizable: false,
+ draggable: false,
+ open: function() {
+ $(this).parents(".ui-dialog").children(".ui-dialog-titlebar").remove();
+ },
+ buttons: { OK: function() { that.hideloading(); } }
+ });
+ }
+ },
+ validateajax: function(d, hide_dialog) {
+ if (!d) {
+ this.errorloading(null, hide_dialog);
+ return -1;
+ }
+ return 0;
+ }
+});
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/js/jquery-1.7.2.min.js b/storage/mroonga/vendor/groonga/data/html/admin/js/jquery-1.7.2.min.js
new file mode 100644
index 00000000000..16ad06c5aca
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/js/jquery-1.7.2.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v1.7.2 jquery.com | jquery.org/license */
+(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
+a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
+.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/js/jquery-ui-1.8.18.custom.min.js b/storage/mroonga/vendor/groonga/data/html/admin/js/jquery-ui-1.8.18.custom.min.js
new file mode 100644
index 00000000000..f00a62f133f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/js/jquery-ui-1.8.18.custom.min.js
@@ -0,0 +1,356 @@
+/*!
+ * jQuery UI 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */(function(a,b){function d(b){return!a(b).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}function c(b,c){var e=b.nodeName.toLowerCase();if("area"===e){var f=b.parentNode,g=f.name,h;if(!b.href||!g||f.nodeName.toLowerCase()!=="map")return!1;h=a("img[usemap=#"+g+"]")[0];return!!h&&d(h)}return(/input|select|textarea|button|object/.test(e)?!b.disabled:"a"==e?b.href||c:c)&&d(b)}a.ui=a.ui||{};a.ui.version||(a.extend(a.ui,{version:"1.8.18",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}}),a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(b,c){return typeof b=="number"?this.each(function(){var d=this;setTimeout(function(){a(d).focus(),c&&c.call(d)},b)}):this._focus.apply(this,arguments)},scrollParent:function(){var b;a.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?b=this.parents().filter(function(){return/(relative|absolute|fixed)/.test(a.curCSS(this,"position",1))&&/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0):b=this.parents().filter(function(){return/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!b.length?a(document):b},zIndex:function(c){if(c!==b)return this.css("zIndex",c);if(this.length){var d=a(this[0]),e,f;while(d.length&&d[0]!==document){e=d.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){f=parseInt(d.css("zIndex"),10);if(!isNaN(f)&&f!==0)return f}d=d.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),a.each(["Width","Height"],function(c,d){function h(b,c,d,f){a.each(e,function(){c-=parseFloat(a.curCSS(b,"padding"+this,!0))||0,d&&(c-=parseFloat(a.curCSS(b,"border"+this+"Width",!0))||0),f&&(c-=parseFloat(a.curCSS(b,"margin"+this,!0))||0)});return c}var e=d==="Width"?["Left","Right"]:["Top","Bottom"],f=d.toLowerCase(),g={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+d]=function(c){if(c===b)return g["inner"+d].call(this);return this.each(function(){a(this).css(f,h(this,c)+"px")})},a.fn["outer"+d]=function(b,c){if(typeof b!="number")return g["outer"+d].call(this,b);return this.each(function(){a(this).css(f,h(this,b,!0,c)+"px")})}}),a.extend(a.expr[":"],{data:function(b,c,d){return!!a.data(b,d[3])},focusable:function(b){return c(b,!isNaN(a.attr(b,"tabindex")))},tabbable:function(b){var d=a.attr(b,"tabindex"),e=isNaN(d);return(e||d>=0)&&c(b,!e)}}),a(function(){var b=document.body,c=b.appendChild(c=document.createElement("div"));c.offsetHeight,a.extend(c.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0}),a.support.minHeight=c.offsetHeight===100,a.support.selectstart="onselectstart"in c,b.removeChild(c).style.display="none"}),a.extend(a.ui,{plugin:{add:function(b,c,d){var e=a.ui[b].prototype;for(var f in d)e.plugins[f]=e.plugins[f]||[],e.plugins[f].push([c,d[f]])},call:function(a,b,c){var d=a.plugins[b];if(!!d&&!!a.element[0].parentNode)for(var e=0;e<d.length;e++)a.options[d[e][0]]&&d[e][1].apply(a.element,c)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(b,c){if(a(b).css("overflow")==="hidden")return!1;var d=c&&c==="left"?"scrollLeft":"scrollTop",e=!1;if(b[d]>0)return!0;b[d]=1,e=b[d]>0,b[d]=0;return e},isOverAxis:function(a,b,c){return a>b&&a<b+c},isOver:function(b,c,d,e,f,g){return a.ui.isOverAxis(b,d,f)&&a.ui.isOverAxis(c,e,g)}}))})(jQuery);/*!
+ * jQuery UI Widget 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */(function(a,b){if(a.cleanData){var c=a.cleanData;a.cleanData=function(b){for(var d=0,e;(e=b[d])!=null;d++)try{a(e).triggerHandler("remove")}catch(f){}c(b)}}else{var d=a.fn.remove;a.fn.remove=function(b,c){return this.each(function(){c||(!b||a.filter(b,[this]).length)&&a("*",this).add([this]).each(function(){try{a(this).triggerHandler("remove")}catch(b){}});return d.call(a(this),b,c)})}}a.widget=function(b,c,d){var e=b.split(".")[0],f;b=b.split(".")[1],f=e+"-"+b,d||(d=c,c=a.Widget),a.expr[":"][f]=function(c){return!!a.data(c,b)},a[e]=a[e]||{},a[e][b]=function(a,b){arguments.length&&this._createWidget(a,b)};var g=new c;g.options=a.extend(!0,{},g.options),a[e][b].prototype=a.extend(!0,g,{namespace:e,widgetName:b,widgetEventPrefix:a[e][b].prototype.widgetEventPrefix||b,widgetBaseClass:f},d),a.widget.bridge(b,a[e][b])},a.widget.bridge=function(c,d){a.fn[c]=function(e){var f=typeof e=="string",g=Array.prototype.slice.call(arguments,1),h=this;e=!f&&g.length?a.extend.apply(null,[!0,e].concat(g)):e;if(f&&e.charAt(0)==="_")return h;f?this.each(function(){var d=a.data(this,c),f=d&&a.isFunction(d[e])?d[e].apply(d,g):d;if(f!==d&&f!==b){h=f;return!1}}):this.each(function(){var b=a.data(this,c);b?b.option(e||{})._init():a.data(this,c,new d(e,this))});return h}},a.Widget=function(a,b){arguments.length&&this._createWidget(a,b)},a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:!1},_createWidget:function(b,c){a.data(c,this.widgetName,this),this.element=a(c),this.options=a.extend(!0,{},this.options,this._getCreateOptions(),b);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()}),this._create(),this._trigger("create"),this._init()},_getCreateOptions:function(){return a.metadata&&a.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName),this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled "+"ui-state-disabled")},widget:function(){return this.element},option:function(c,d){var e=c;if(arguments.length===0)return a.extend({},this.options);if(typeof c=="string"){if(d===b)return this.options[c];e={},e[c]=d}this._setOptions(e);return this},_setOptions:function(b){var c=this;a.each(b,function(a,b){c._setOption(a,b)});return this},_setOption:function(a,b){this.options[a]=b,a==="disabled"&&this.widget()[b?"addClass":"removeClass"](this.widgetBaseClass+"-disabled"+" "+"ui-state-disabled").attr("aria-disabled",b);return this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_trigger:function(b,c,d){var e,f,g=this.options[b];d=d||{},c=a.Event(c),c.type=(b===this.widgetEventPrefix?b:this.widgetEventPrefix+b).toLowerCase(),c.target=this.element[0],f=c.originalEvent;if(f)for(e in f)e in c||(c[e]=f[e]);this.element.trigger(c,d);return!(a.isFunction(g)&&g.call(this.element[0],c,d)===!1||c.isDefaultPrevented())}}})(jQuery);/*!
+ * jQuery UI Mouse 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ * jquery.ui.widget.js
+ */(function(a,b){var c=!1;a(document).mouseup(function(a){c=!1}),a.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var b=this;this.element.bind("mousedown."+this.widgetName,function(a){return b._mouseDown(a)}).bind("click."+this.widgetName,function(c){if(!0===a.data(c.target,b.widgetName+".preventClickEvent")){a.removeData(c.target,b.widgetName+".preventClickEvent"),c.stopImmediatePropagation();return!1}}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(b){if(!c){this._mouseStarted&&this._mouseUp(b),this._mouseDownEvent=b;var d=this,e=b.which==1,f=typeof this.options.cancel=="string"&&b.target.nodeName?a(b.target).closest(this.options.cancel).length:!1;if(!e||f||!this._mouseCapture(b))return!0;this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){d.mouseDelayMet=!0},this.options.delay));if(this._mouseDistanceMet(b)&&this._mouseDelayMet(b)){this._mouseStarted=this._mouseStart(b)!==!1;if(!this._mouseStarted){b.preventDefault();return!0}}!0===a.data(b.target,this.widgetName+".preventClickEvent")&&a.removeData(b.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(a){return d._mouseMove(a)},this._mouseUpDelegate=function(a){return d._mouseUp(a)},a(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),b.preventDefault(),c=!0;return!0}},_mouseMove:function(b){if(a.browser.msie&&!(document.documentMode>=9)&&!b.button)return this._mouseUp(b);if(this._mouseStarted){this._mouseDrag(b);return b.preventDefault()}this._mouseDistanceMet(b)&&this._mouseDelayMet(b)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,b)!==!1,this._mouseStarted?this._mouseDrag(b):this._mouseUp(b));return!this._mouseStarted},_mouseUp:function(b){a(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,b.target==this._mouseDownEvent.target&&a.data(b.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(b));return!1},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(a){return this.mouseDelayMet},_mouseStart:function(a){},_mouseDrag:function(a){},_mouseStop:function(a){},_mouseCapture:function(a){return!0}})})(jQuery);/*
+ * jQuery UI Position 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Position
+ */(function(a,b){a.ui=a.ui||{};var c=/left|center|right/,d=/top|center|bottom/,e="center",f={},g=a.fn.position,h=a.fn.offset;a.fn.position=function(b){if(!b||!b.of)return g.apply(this,arguments);b=a.extend({},b);var h=a(b.of),i=h[0],j=(b.collision||"flip").split(" "),k=b.offset?b.offset.split(" "):[0,0],l,m,n;i.nodeType===9?(l=h.width(),m=h.height(),n={top:0,left:0}):i.setTimeout?(l=h.width(),m=h.height(),n={top:h.scrollTop(),left:h.scrollLeft()}):i.preventDefault?(b.at="left top",l=m=0,n={top:b.of.pageY,left:b.of.pageX}):(l=h.outerWidth(),m=h.outerHeight(),n=h.offset()),a.each(["my","at"],function(){var a=(b[this]||"").split(" ");a.length===1&&(a=c.test(a[0])?a.concat([e]):d.test(a[0])?[e].concat(a):[e,e]),a[0]=c.test(a[0])?a[0]:e,a[1]=d.test(a[1])?a[1]:e,b[this]=a}),j.length===1&&(j[1]=j[0]),k[0]=parseInt(k[0],10)||0,k.length===1&&(k[1]=k[0]),k[1]=parseInt(k[1],10)||0,b.at[0]==="right"?n.left+=l:b.at[0]===e&&(n.left+=l/2),b.at[1]==="bottom"?n.top+=m:b.at[1]===e&&(n.top+=m/2),n.left+=k[0],n.top+=k[1];return this.each(function(){var c=a(this),d=c.outerWidth(),g=c.outerHeight(),h=parseInt(a.curCSS(this,"marginLeft",!0))||0,i=parseInt(a.curCSS(this,"marginTop",!0))||0,o=d+h+(parseInt(a.curCSS(this,"marginRight",!0))||0),p=g+i+(parseInt(a.curCSS(this,"marginBottom",!0))||0),q=a.extend({},n),r;b.my[0]==="right"?q.left-=d:b.my[0]===e&&(q.left-=d/2),b.my[1]==="bottom"?q.top-=g:b.my[1]===e&&(q.top-=g/2),f.fractions||(q.left=Math.round(q.left),q.top=Math.round(q.top)),r={left:q.left-h,top:q.top-i},a.each(["left","top"],function(c,e){a.ui.position[j[c]]&&a.ui.position[j[c]][e](q,{targetWidth:l,targetHeight:m,elemWidth:d,elemHeight:g,collisionPosition:r,collisionWidth:o,collisionHeight:p,offset:k,my:b.my,at:b.at})}),a.fn.bgiframe&&c.bgiframe(),c.offset(a.extend(q,{using:b.using}))})},a.ui.position={fit:{left:function(b,c){var d=a(window),e=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft();b.left=e>0?b.left-e:Math.max(b.left-c.collisionPosition.left,b.left)},top:function(b,c){var d=a(window),e=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop();b.top=e>0?b.top-e:Math.max(b.top-c.collisionPosition.top,b.top)}},flip:{left:function(b,c){if(c.at[0]!==e){var d=a(window),f=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft(),g=c.my[0]==="left"?-c.elemWidth:c.my[0]==="right"?c.elemWidth:0,h=c.at[0]==="left"?c.targetWidth:-c.targetWidth,i=-2*c.offset[0];b.left+=c.collisionPosition.left<0?g+h+i:f>0?g+h+i:0}},top:function(b,c){if(c.at[1]!==e){var d=a(window),f=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop(),g=c.my[1]==="top"?-c.elemHeight:c.my[1]==="bottom"?c.elemHeight:0,h=c.at[1]==="top"?c.targetHeight:-c.targetHeight,i=-2*c.offset[1];b.top+=c.collisionPosition.top<0?g+h+i:f>0?g+h+i:0}}}},a.offset.setOffset||(a.offset.setOffset=function(b,c){/static/.test(a.curCSS(b,"position"))&&(b.style.position="relative");var d=a(b),e=d.offset(),f=parseInt(a.curCSS(b,"top",!0),10)||0,g=parseInt(a.curCSS(b,"left",!0),10)||0,h={top:c.top-e.top+f,left:c.left-e.left+g};"using"in c?c.using.call(b,h):d.css(h)},a.fn.offset=function(b){var c=this[0];if(!c||!c.ownerDocument)return null;if(b)return this.each(function(){a.offset.setOffset(this,b)});return h.call(this)}),function(){var b=document.getElementsByTagName("body")[0],c=document.createElement("div"),d,e,g,h,i;d=document.createElement(b?"div":"body"),g={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},b&&a.extend(g,{position:"absolute",left:"-1000px",top:"-1000px"});for(var j in g)d.style[j]=g[j];d.appendChild(c),e=b||document.documentElement,e.insertBefore(d,e.firstChild),c.style.cssText="position: absolute; left: 10.7432222px; top: 10.432325px; height: 30px; width: 201px;",h=a(c).offset(function(a,b){return b}).offset(),d.innerHTML="",e.removeChild(d),i=h.top+h.left+(b?2e3:0),f.fractions=i>21&&i<22}()})(jQuery);/*
+ * jQuery UI Draggable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.draggable",a.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1},_create:function(){this.options.helper=="original"&&!/^(?:r|a|f)/.test(this.element.css("position"))&&(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},destroy:function(){if(!!this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy();return this}},_mouseCapture:function(b){var c=this.options;if(this.helper||c.disabled||a(b.target).is(".ui-resizable-handle"))return!1;this.handle=this._getHandle(b);if(!this.handle)return!1;c.iframeFix&&a(c.iframeFix===!0?"iframe":c.iframeFix).each(function(){a('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(a(this).offset()).appendTo("body")});return!0},_mouseStart:function(b){var c=this.options;this.helper=this._createHelper(b),this._cacheHelperProportions(),a.ui.ddmanager&&(a.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(b),this.originalPageX=b.pageX,this.originalPageY=b.pageY,c.cursorAt&&this._adjustOffsetFromHelper(c.cursorAt),c.containment&&this._setContainment();if(this._trigger("start",b)===!1){this._clear();return!1}this._cacheHelperProportions(),a.ui.ddmanager&&!c.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b),this.helper.addClass("ui-draggable-dragging"),this._mouseDrag(b,!0),a.ui.ddmanager&&a.ui.ddmanager.dragStart(this,b);return!0},_mouseDrag:function(b,c){this.position=this._generatePosition(b),this.positionAbs=this._convertPositionTo("absolute");if(!c){var d=this._uiHash();if(this._trigger("drag",b,d)===!1){this._mouseUp({});return!1}this.position=d.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";a.ui.ddmanager&&a.ui.ddmanager.drag(this,b);return!1},_mouseStop:function(b){var c=!1;a.ui.ddmanager&&!this.options.dropBehaviour&&(c=a.ui.ddmanager.drop(this,b)),this.dropped&&(c=this.dropped,this.dropped=!1);if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return!1;if(this.options.revert=="invalid"&&!c||this.options.revert=="valid"&&c||this.options.revert===!0||a.isFunction(this.options.revert)&&this.options.revert.call(this.element,c)){var d=this;a(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){d._trigger("stop",b)!==!1&&d._clear()})}else this._trigger("stop",b)!==!1&&this._clear();return!1},_mouseUp:function(b){this.options.iframeFix===!0&&a("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),a.ui.ddmanager&&a.ui.ddmanager.dragStop(this,b);return a.ui.mouse.prototype._mouseUp.call(this,b)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(b){var c=!this.options.handle||!a(this.options.handle,this.element).length?!0:!1;a(this.options.handle,this.element).find("*").andSelf().each(function(){this==b.target&&(c=!0)});return c},_createHelper:function(b){var c=this.options,d=a.isFunction(c.helper)?a(c.helper.apply(this.element[0],[b])):c.helper=="clone"?this.element.clone().removeAttr("id"):this.element;d.parents("body").length||d.appendTo(c.appendTo=="parent"?this.element[0].parentNode:c.appendTo),d[0]!=this.element[0]&&!/(fixed|absolute)/.test(d.css("position"))&&d.css("position","absolute");return d},_adjustOffsetFromHelper:function(b){typeof b=="string"&&(b=b.split(" ")),a.isArray(b)&&(b={left:+b[0],top:+b[1]||0}),"left"in b&&(this.offset.click.left=b.left+this.margins.left),"right"in b&&(this.offset.click.left=this.helperProportions.width-b.right+this.margins.left),"top"in b&&(this.offset.click.top=b.top+this.margins.top),"bottom"in b&&(this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])&&(b.left+=this.scrollParent.scrollLeft(),b.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)b={top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var b=this.options;b.containment=="parent"&&(b.containment=this.helper[0].parentNode);if(b.containment=="document"||b.containment=="window")this.containment=[b.containment=="document"?0:a(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,b.containment=="document"?0:a(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,(b.containment=="document"?0:a(window).scrollLeft())+a(b.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(b.containment=="document"?0:a(window).scrollTop())+(a(b.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(b.containment)&&b.containment.constructor!=Array){var c=a(b.containment),d=c[0];if(!d)return;var e=c.offset(),f=a(d).css("overflow")!="hidden";this.containment=[(parseInt(a(d).css("borderLeftWidth"),10)||0)+(parseInt(a(d).css("paddingLeft"),10)||0),(parseInt(a(d).css("borderTopWidth"),10)||0)+(parseInt(a(d).css("paddingTop"),10)||0),(f?Math.max(d.scrollWidth,d.offsetWidth):d.offsetWidth)-(parseInt(a(d).css("borderLeftWidth"),10)||0)-(parseInt(a(d).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(f?Math.max(d.scrollHeight,d.offsetHeight):d.offsetHeight)-(parseInt(a(d).css("borderTopWidth"),10)||0)-(parseInt(a(d).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=c}else b.containment.constructor==Array&&(this.containment=b.containment)},_convertPositionTo:function(b,c){c||(c=this.position);var d=b=="absolute"?1:-1,e=this.options,f=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName);return{top:c.top+this.offset.relative.top*d+this.offset.parent.top*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),left:c.left+this.offset.relative.left*d+this.offset.parent.left*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:function(b){var c=this.options,d=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(d[0].tagName),f=b.pageX,g=b.pageY;if(this.originalPosition){var h;if(this.containment){if(this.relative_container){var i=this.relative_container.offset();h=[this.containment[0]+i.left,this.containment[1]+i.top,this.containment[2]+i.left,this.containment[3]+i.top]}else h=this.containment;b.pageX-this.offset.click.left<h[0]&&(f=h[0]+this.offset.click.left),b.pageY-this.offset.click.top<h[1]&&(g=h[1]+this.offset.click.top),b.pageX-this.offset.click.left>h[2]&&(f=h[2]+this.offset.click.left),b.pageY-this.offset.click.top>h[3]&&(g=h[3]+this.offset.click.top)}if(c.grid){var j=c.grid[1]?this.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1]:this.originalPageY;g=h?j-this.offset.click.top<h[1]||j-this.offset.click.top>h[3]?j-this.offset.click.top<h[1]?j+c.grid[1]:j-c.grid[1]:j:j;var k=c.grid[0]?this.originalPageX+Math.round((f-this.originalPageX)/c.grid[0])*c.grid[0]:this.originalPageX;f=h?k-this.offset.click.left<h[0]||k-this.offset.click.left>h[2]?k-this.offset.click.left<h[0]?k+c.grid[0]:k-c.grid[0]:k:k}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:d.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:d.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1},_trigger:function(b,c,d){d=d||this._uiHash(),a.ui.plugin.call(this,b,[c,d]),b=="drag"&&(this.positionAbs=this._convertPositionTo("absolute"));return a.Widget.prototype._trigger.call(this,b,c,d)},plugins:{},_uiHash:function(a){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),a.extend(a.ui.draggable,{version:"1.8.18"}),a.ui.plugin.add("draggable","connectToSortable",{start:function(b,c){var d=a(this).data("draggable"),e=d.options,f=a.extend({},c,{item:d.element});d.sortables=[],a(e.connectToSortable).each(function(){var c=a.data(this,"sortable");c&&!c.options.disabled&&(d.sortables.push({instance:c,shouldRevert:c.options.revert}),c.refreshPositions(),c._trigger("activate",b,f))})},stop:function(b,c){var d=a(this).data("draggable"),e=a.extend({},c,{item:d.element});a.each(d.sortables,function(){this.instance.isOver?(this.instance.isOver=0,d.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=!0),this.instance._mouseStop(b),this.instance.options.helper=this.instance.options._helper,d.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",b,e))})},drag:function(b,c){var d=a(this).data("draggable"),e=this,f=function(b){var c=this.offset.click.top,d=this.offset.click.left,e=this.positionAbs.top,f=this.positionAbs.left,g=b.height,h=b.width,i=b.top,j=b.left;return a.ui.isOver(e+c,f+d,i,j,g,h)};a.each(d.sortables,function(f){this.instance.positionAbs=d.positionAbs,this.instance.helperProportions=d.helperProportions,this.instance.offset.click=d.offset.click,this.instance._intersectsWith(this.instance.containerCache)?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=a(e).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return c.helper[0]},b.target=this.instance.currentItem[0],this.instance._mouseCapture(b,!0),this.instance._mouseStart(b,!0,!0),this.instance.offset.click.top=d.offset.click.top,this.instance.offset.click.left=d.offset.click.left,this.instance.offset.parent.left-=d.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=d.offset.parent.top-this.instance.offset.parent.top,d._trigger("toSortable",b),d.dropped=this.instance.element,d.currentItem=d.element,this.instance.fromOutside=d),this.instance.currentItem&&this.instance._mouseDrag(b)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",b,this.instance._uiHash(this.instance)),this.instance._mouseStop(b,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),d._trigger("fromSortable",b),d.dropped=!1)})}}),a.ui.plugin.add("draggable","cursor",{start:function(b,c){var d=a("body"),e=a(this).data("draggable").options;d.css("cursor")&&(e._cursor=d.css("cursor")),d.css("cursor",e.cursor)},stop:function(b,c){var d=a(this).data("draggable").options;d._cursor&&a("body").css("cursor",d._cursor)}}),a.ui.plugin.add("draggable","opacity",{start:function(b,c){var d=a(c.helper),e=a(this).data("draggable").options;d.css("opacity")&&(e._opacity=d.css("opacity")),d.css("opacity",e.opacity)},stop:function(b,c){var d=a(this).data("draggable").options;d._opacity&&a(c.helper).css("opacity",d._opacity)}}),a.ui.plugin.add("draggable","scroll",{start:function(b,c){var d=a(this).data("draggable");d.scrollParent[0]!=document&&d.scrollParent[0].tagName!="HTML"&&(d.overflowOffset=d.scrollParent.offset())},drag:function(b,c){var d=a(this).data("draggable"),e=d.options,f=!1;if(d.scrollParent[0]!=document&&d.scrollParent[0].tagName!="HTML"){if(!e.axis||e.axis!="x")d.overflowOffset.top+d.scrollParent[0].offsetHeight-b.pageY<e.scrollSensitivity?d.scrollParent[0].scrollTop=f=d.scrollParent[0].scrollTop+e.scrollSpeed:b.pageY-d.overflowOffset.top<e.scrollSensitivity&&(d.scrollParent[0].scrollTop=f=d.scrollParent[0].scrollTop-e.scrollSpeed);if(!e.axis||e.axis!="y")d.overflowOffset.left+d.scrollParent[0].offsetWidth-b.pageX<e.scrollSensitivity?d.scrollParent[0].scrollLeft=f=d.scrollParent[0].scrollLeft+e.scrollSpeed:b.pageX-d.overflowOffset.left<e.scrollSensitivity&&(d.scrollParent[0].scrollLeft=f=d.scrollParent[0].scrollLeft-e.scrollSpeed)}else{if(!e.axis||e.axis!="x")b.pageY-a(document).scrollTop()<e.scrollSensitivity?f=a(document).scrollTop(a(document).scrollTop()-e.scrollSpeed):a(window).height()-(b.pageY-a(document).scrollTop())<e.scrollSensitivity&&(f=a(document).scrollTop(a(document).scrollTop()+e.scrollSpeed));if(!e.axis||e.axis!="y")b.pageX-a(document).scrollLeft()<e.scrollSensitivity?f=a(document).scrollLeft(a(document).scrollLeft()-e.scrollSpeed):a(window).width()-(b.pageX-a(document).scrollLeft())<e.scrollSensitivity&&(f=a(document).scrollLeft(a(document).scrollLeft()+e.scrollSpeed))}f!==!1&&a.ui.ddmanager&&!e.dropBehaviour&&a.ui.ddmanager.prepareOffsets(d,b)}}),a.ui.plugin.add("draggable","snap",{start:function(b,c){var d=a(this).data("draggable"),e=d.options;d.snapElements=[],a(e.snap.constructor!=String?e.snap.items||":data(draggable)":e.snap).each(function(){var b=a(this),c=b.offset();this!=d.element[0]&&d.snapElements.push({item:this,width:b.outerWidth(),height:b.outerHeight(),top:c.top,left:c.left})})},drag:function(b,c){var d=a(this).data("draggable"),e=d.options,f=e.snapTolerance,g=c.offset.left,h=g+d.helperProportions.width,i=c.offset.top,j=i+d.helperProportions.height;for(var k=d.snapElements.length-1;k>=0;k--){var l=d.snapElements[k].left,m=l+d.snapElements[k].width,n=d.snapElements[k].top,o=n+d.snapElements[k].height;if(!(l-f<g&&g<m+f&&n-f<i&&i<o+f||l-f<g&&g<m+f&&n-f<j&&j<o+f||l-f<h&&h<m+f&&n-f<i&&i<o+f||l-f<h&&h<m+f&&n-f<j&&j<o+f)){d.snapElements[k].snapping&&d.options.snap.release&&d.options.snap.release.call(d.element,b,a.extend(d._uiHash(),{snapItem:d.snapElements[k].item})),d.snapElements[k].snapping=!1;continue}if(e.snapMode!="inner"){var p=Math.abs(n-j)<=f,q=Math.abs(o-i)<=f,r=Math.abs(l-h)<=f,s=Math.abs(m-g)<=f;p&&(c.position.top=d._convertPositionTo("relative",{top:n-d.helperProportions.height,left:0}).top-d.margins.top),q&&(c.position.top=d._convertPositionTo("relative",{top:o,left:0}).top-d.margins.top),r&&(c.position.left=d._convertPositionTo("relative",{top:0,left:l-d.helperProportions.width}).left-d.margins.left),s&&(c.position.left=d._convertPositionTo("relative",{top:0,left:m}).left-d.margins.left)}var t=p||q||r||s;if(e.snapMode!="outer"){var p=Math.abs(n-i)<=f,q=Math.abs(o-j)<=f,r=Math.abs(l-g)<=f,s=Math.abs(m-h)<=f;p&&(c.position.top=d._convertPositionTo("relative",{top:n,left:0}).top-d.margins.top),q&&(c.position.top=d._convertPositionTo("relative",{top:o-d.helperProportions.height,left:0}).top-d.margins.top),r&&(c.position.left=d._convertPositionTo("relative",{top:0,left:l}).left-d.margins.left),s&&(c.position.left=d._convertPositionTo("relative",{top:0,left:m-d.helperProportions.width}).left-d.margins.left)}!d.snapElements[k].snapping&&(p||q||r||s||t)&&d.options.snap.snap&&d.options.snap.snap.call(d.element,b,a.extend(d._uiHash(),{snapItem:d.snapElements[k].item})),d.snapElements[k].snapping=p||q||r||s||t}}}),a.ui.plugin.add("draggable","stack",{start:function(b,c){var d=a(this).data("draggable").options,e=a.makeArray(a(d.stack)).sort(function(b,c){return(parseInt(a(b).css("zIndex"),10)||0)-(parseInt(a(c).css("zIndex"),10)||0)});if(!!e.length){var f=parseInt(e[0].style.zIndex)||0;a(e).each(function(a){this.style.zIndex=f+a}),this[0].style.zIndex=f+e.length}}}),a.ui.plugin.add("draggable","zIndex",{start:function(b,c){var d=a(c.helper),e=a(this).data("draggable").options;d.css("zIndex")&&(e._zIndex=d.css("zIndex")),d.css("zIndex",e.zIndex)},stop:function(b,c){var d=a(this).data("draggable").options;d._zIndex&&a(c.helper).css("zIndex",d._zIndex)}})})(jQuery);/*
+ * jQuery UI Droppable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Droppables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.mouse.js
+ * jquery.ui.draggable.js
+ */(function(a,b){a.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect"},_create:function(){var b=this.options,c=b.accept;this.isover=0,this.isout=1,this.accept=a.isFunction(c)?c:function(a){return a.is(c)},this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight},a.ui.ddmanager.droppables[b.scope]=a.ui.ddmanager.droppables[b.scope]||[],a.ui.ddmanager.droppables[b.scope].push(this),b.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){var b=a.ui.ddmanager.droppables[this.options.scope];for(var c=0;c<b.length;c++)b[c]==this&&b.splice(c,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(b,c){b=="accept"&&(this.accept=a.isFunction(c)?c:function(a){return a.is(c)}),a.Widget.prototype._setOption.apply(this,arguments)},_activate:function(b){var c=a.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),c&&this._trigger("activate",b,this.ui(c))},_deactivate:function(b){var c=a.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),c&&this._trigger("deactivate",b,this.ui(c))},_over:function(b){var c=a.ui.ddmanager.current;!!c&&(c.currentItem||c.element)[0]!=this.element[0]&&this.accept.call(this.element[0],c.currentItem||c.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",b,this.ui(c)))},_out:function(b){var c=a.ui.ddmanager.current;!!c&&(c.currentItem||c.element)[0]!=this.element[0]&&this.accept.call(this.element[0],c.currentItem||c.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",b,this.ui(c)))},_drop:function(b,c){var d=c||a.ui.ddmanager.current;if(!d||(d.currentItem||d.element)[0]==this.element[0])return!1;var e=!1;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var b=a.data(this,"droppable");if(b.options.greedy&&!b.options.disabled&&b.options.scope==d.options.scope&&b.accept.call(b.element[0],d.currentItem||d.element)&&a.ui.intersect(d,a.extend(b,{offset:b.element.offset()}),b.options.tolerance)){e=!0;return!1}});if(e)return!1;if(this.accept.call(this.element[0],d.currentItem||d.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",b,this.ui(d));return this.element}return!1},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}}),a.extend(a.ui.droppable,{version:"1.8.18"}),a.ui.intersect=function(b,c,d){if(!c.offset)return!1;var e=(b.positionAbs||b.position.absolute).left,f=e+b.helperProportions.width,g=(b.positionAbs||b.position.absolute).top,h=g+b.helperProportions.height,i=c.offset.left,j=i+c.proportions.width,k=c.offset.top,l=k+c.proportions.height;switch(d){case"fit":return i<=e&&f<=j&&k<=g&&h<=l;case"intersect":return i<e+b.helperProportions.width/2&&f-b.helperProportions.width/2<j&&k<g+b.helperProportions.height/2&&h-b.helperProportions.height/2<l;case"pointer":var m=(b.positionAbs||b.position.absolute).left+(b.clickOffset||b.offset.click).left,n=(b.positionAbs||b.position.absolute).top+(b.clickOffset||b.offset.click).top,o=a.ui.isOver(n,m,k,i,c.proportions.height,c.proportions.width);return o;case"touch":return(g>=k&&g<=l||h>=k&&h<=l||g<k&&h>l)&&(e>=i&&e<=j||f>=i&&f<=j||e<i&&f>j);default:return!1}},a.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(b,c){var d=a.ui.ddmanager.droppables[b.options.scope]||[],e=c?c.type:null,f=(b.currentItem||b.element).find(":data(droppable)").andSelf();droppablesLoop:for(var g=0;g<d.length;g++){if(d[g].options.disabled||b&&!d[g].accept.call(d[g].element[0],b.currentItem||b.element))continue;for(var h=0;h<f.length;h++)if(f[h]==d[g].element[0]){d[g].proportions.height=0;continue droppablesLoop}d[g].visible=d[g].element.css("display")!="none";if(!d[g].visible)continue;e=="mousedown"&&d[g]._activate.call(d[g],c),d[g].offset=d[g].element.offset(),d[g].proportions={width:d[g].element[0].offsetWidth,height:d[g].element[0].offsetHeight}}},drop:function(b,c){var d=!1;a.each(a.ui.ddmanager.droppables[b.options.scope]||[],function(){!this.options||(!this.options.disabled&&this.visible&&a.ui.intersect(b,this,this.options.tolerance)&&(d=this._drop.call(this,c)||d),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],b.currentItem||b.element)&&(this.isout=1,this.isover=0,this._deactivate.call(this,c)))});return d},dragStart:function(b,c){b.element.parents(":not(body,html)").bind("scroll.droppable",function(){b.options.refreshPositions||a.ui.ddmanager.prepareOffsets(b,c)})},drag:function(b,c){b.options.refreshPositions&&a.ui.ddmanager.prepareOffsets(b,c),a.each(a.ui.ddmanager.droppables[b.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var d=a.ui.intersect(b,this,this.options.tolerance),e=!d&&this.isover==1?"isout":d&&this.isover==0?"isover":null;if(!e)return;var f;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");g.length&&(f=a.data(g[0],"droppable"),f.greedyChild=e=="isover"?1:0)}f&&e=="isover"&&(f.isover=0,f.isout=1,f._out.call(f,c)),this[e]=1,this[e=="isout"?"isover":"isout"]=0,this[e=="isover"?"_over":"_out"].call(this,c),f&&e=="isout"&&(f.isout=0,f.isover=1,f._over.call(f,c))}})},dragStop:function(b,c){b.element.parents(":not(body,html)").unbind("scroll.droppable"),b.options.refreshPositions||a.ui.ddmanager.prepareOffsets(b,c)}}})(jQuery);/*
+ * jQuery UI Resizable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.resizable",a.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1e3},_create:function(){var b=this,c=this.options;this.element.addClass("ui-resizable"),a.extend(this,{_aspectRatio:!!c.aspectRatio,aspectRatio:c.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:c.helper||c.ghost||c.animate?c.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(a('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("resizable",this.element.data("resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=c.handles||(a(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se");if(this.handles.constructor==String){this.handles=="all"&&(this.handles="n,e,s,w,se,sw,ne,nw");var d=this.handles.split(",");this.handles={};for(var e=0;e<d.length;e++){var f=a.trim(d[e]),g="ui-resizable-"+f,h=a('<div class="ui-resizable-handle '+g+'"></div>');/sw|se|ne|nw/.test(f)&&h.css({zIndex:++c.zIndex}),"se"==f&&h.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[f]=".ui-resizable-"+f,this.element.append(h)}}this._renderAxis=function(b){b=b||this.element;for(var c in this.handles){this.handles[c].constructor==String&&(this.handles[c]=a(this.handles[c],this.element).show());if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var d=a(this.handles[c],this.element),e=0;e=/sw|ne|nw|se|n|s/.test(c)?d.outerHeight():d.outerWidth();var f=["padding",/ne|nw|n/.test(c)?"Top":/se|sw|s/.test(c)?"Bottom":/^e$/.test(c)?"Right":"Left"].join("");b.css(f,e),this._proportionallyResize()}if(!a(this.handles[c]).length)continue}},this._renderAxis(this.element),this._handles=a(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){if(!b.resizing){if(this.className)var a=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=a&&a[1]?a[1]:"se"}}),c.autoHide&&(this._handles.hide(),a(this.element).addClass("ui-resizable-autohide").hover(function(){c.disabled||(a(this).removeClass("ui-resizable-autohide"),b._handles.show())},function(){c.disabled||b.resizing||(a(this).addClass("ui-resizable-autohide"),b._handles.hide())})),this._mouseInit()},destroy:function(){this._mouseDestroy();var b=function(b){a(b).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var c=this.element;c.after(this.originalElement.css({position:c.css("position"),width:c.outerWidth(),height:c.outerHeight(),top:c.css("top"),left:c.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle),b(this.originalElement);return this},_mouseCapture:function(b){var c=!1;for(var d in this.handles)a(this.handles[d])[0]==b.target&&(c=!0);return!this.options.disabled&&c},_mouseStart:function(b){var d=this.options,e=this.element.position(),f=this.element;this.resizing=!0,this.documentScroll={top:a(document).scrollTop(),left:a(document).scrollLeft()},(f.is(".ui-draggable")||/absolute/.test(f.css("position")))&&f.css({position:"absolute",top:e.top,left:e.left}),this._renderProxy();var g=c(this.helper.css("left")),h=c(this.helper.css("top"));d.containment&&(g+=a(d.containment).scrollLeft()||0,h+=a(d.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:g,top:h},this.size=this._helper?{width:f.outerWidth(),height:f.outerHeight()}:{width:f.width(),height:f.height()},this.originalSize=this._helper?{width:f.outerWidth(),height:f.outerHeight()}:{width:f.width(),height:f.height()},this.originalPosition={left:g,top:h},this.sizeDiff={width:f.outerWidth()-f.width(),height:f.outerHeight()-f.height()},this.originalMousePosition={left:b.pageX,top:b.pageY},this.aspectRatio=typeof d.aspectRatio=="number"?d.aspectRatio:this.originalSize.width/this.originalSize.height||1;var i=a(".ui-resizable-"+this.axis).css("cursor");a("body").css("cursor",i=="auto"?this.axis+"-resize":i),f.addClass("ui-resizable-resizing"),this._propagate("start",b);return!0},_mouseDrag:function(b){var c=this.helper,d=this.options,e={},f=this,g=this.originalMousePosition,h=this.axis,i=b.pageX-g.left||0,j=b.pageY-g.top||0,k=this._change[h];if(!k)return!1;var l=k.apply(this,[b,i,j]),m=a.browser.msie&&a.browser.version<7,n=this.sizeDiff;this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)l=this._updateRatio(l,b);l=this._respectSize(l,b),this._propagate("resize",b),c.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"}),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),this._updateCache(l),this._trigger("resize",b,this.ui());return!1},_mouseStop:function(b){this.resizing=!1;var c=this.options,d=this;if(this._helper){var e=this._proportionallyResizeElements,f=e.length&&/textarea/i.test(e[0].nodeName),g=f&&a.ui.hasScroll(e[0],"left")?0:d.sizeDiff.height,h=f?0:d.sizeDiff.width,i={width:d.helper.width()-h,height:d.helper.height()-g},j=parseInt(d.element.css("left"),10)+(d.position.left-d.originalPosition.left)||null,k=parseInt(d.element.css("top"),10)+(d.position.top-d.originalPosition.top)||null;c.animate||this.element.css(a.extend(i,{top:k,left:j})),d.helper.height(d.size.height),d.helper.width(d.size.width),this._helper&&!c.animate&&this._proportionallyResize()}a("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",b),this._helper&&this.helper.remove();return!1},_updateVirtualBoundaries:function(a){var b=this.options,c,e,f,g,h;h={minWidth:d(b.minWidth)?b.minWidth:0,maxWidth:d(b.maxWidth)?b.maxWidth:Infinity,minHeight:d(b.minHeight)?b.minHeight:0,maxHeight:d(b.maxHeight)?b.maxHeight:Infinity};if(this._aspectRatio||a)c=h.minHeight*this.aspectRatio,f=h.minWidth/this.aspectRatio,e=h.maxHeight*this.aspectRatio,g=h.maxWidth/this.aspectRatio,c>h.minWidth&&(h.minWidth=c),f>h.minHeight&&(h.minHeight=f),e<h.maxWidth&&(h.maxWidth=e),g<h.maxHeight&&(h.maxHeight=g);this._vBoundaries=h},_updateCache:function(a){var b=this.options;this.offset=this.helper.offset(),d(a.left)&&(this.position.left=a.left),d(a.top)&&(this.position.top=a.top),d(a.height)&&(this.size.height=a.height),d(a.width)&&(this.size.width=a.width)},_updateRatio:function(a,b){var c=this.options,e=this.position,f=this.size,g=this.axis;d(a.height)?a.width=a.height*this.aspectRatio:d(a.width)&&(a.height=a.width/this.aspectRatio),g=="sw"&&(a.left=e.left+(f.width-a.width),a.top=null),g=="nw"&&(a.top=e.top+(f.height-a.height),a.left=e.left+(f.width-a.width));return a},_respectSize:function(a,b){var c=this.helper,e=this._vBoundaries,f=this._aspectRatio||b.shiftKey,g=this.axis,h=d(a.width)&&e.maxWidth&&e.maxWidth<a.width,i=d(a.height)&&e.maxHeight&&e.maxHeight<a.height,j=d(a.width)&&e.minWidth&&e.minWidth>a.width,k=d(a.height)&&e.minHeight&&e.minHeight>a.height;j&&(a.width=e.minWidth),k&&(a.height=e.minHeight),h&&(a.width=e.maxWidth),i&&(a.height=e.maxHeight);var l=this.originalPosition.left+this.originalSize.width,m=this.position.top+this.size.height,n=/sw|nw|w/.test(g),o=/nw|ne|n/.test(g);j&&n&&(a.left=l-e.minWidth),h&&n&&(a.left=l-e.maxWidth),k&&o&&(a.top=m-e.minHeight),i&&o&&(a.top=m-e.maxHeight);var p=!a.width&&!a.height;p&&!a.left&&a.top?a.top=null:p&&!a.top&&a.left&&(a.left=null);return a},_proportionallyResize:function(){var b=this.options;if(!!this._proportionallyResizeElements.length){var c=this.helper||this.element;for(var d=0;d<this._proportionallyResizeElements.length;d++){var e=this._proportionallyResizeElements[d];if(!this.borderDif){var f=[e.css("borderTopWidth"),e.css("borderRightWidth"),e.css("borderBottomWidth"),e.css("borderLeftWidth")],g=[e.css("paddingTop"),e.css("paddingRight"),e.css("paddingBottom"),e.css("paddingLeft")];this.borderDif=a.map(f,function(a,b){var c=parseInt(a,10)||0,d=parseInt(g[b],10)||0;return c+d})}if(a.browser.msie&&(!!a(c).is(":hidden")||!!a(c).parents(":hidden").length))continue;e.css({height:c.height()-this.borderDif[0]-this.borderDif[2]||0,width:c.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var b=this.element,c=this.options;this.elementOffset=b.offset();if(this._helper){this.helper=this.helper||a('<div style="overflow:hidden;"></div>');var d=a.browser.msie&&a.browser.version<7,e=d?1:0,f=d?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+f,height:this.element.outerHeight()+f,position:"absolute",left:this.elementOffset.left-e+"px",top:this.elementOffset.top-e+"px",zIndex:++c.zIndex}),this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(a,b,c){return{width:this.originalSize.width+b}},w:function(a,b,c){var d=this.options,e=this.originalSize,f=this.originalPosition;return{left:f.left+b,width:e.width-b}},n:function(a,b,c){var d=this.options,e=this.originalSize,f=this.originalPosition;return{top:f.top+c,height:e.height-c}},s:function(a,b,c){return{height:this.originalSize.height+c}},se:function(b,c,d){return a.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,c,d]))},sw:function(b,c,d){return a.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,c,d]))},ne:function(b,c,d){return a.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,c,d]))},nw:function(b,c,d){return a.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,c,d]))}},_propagate:function(b,c){a.ui.plugin.call(this,b,[c,this.ui()]),b!="resize"&&this._trigger(b,c,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),a.extend(a.ui.resizable,{version:"1.8.18"}),a.ui.plugin.add("resizable","alsoResize",{start:function(b,c){var d=a(this).data("resizable"),e=d.options,f=function(b){a(b).each(function(){var b=a(this);b.data("resizable-alsoresize",{width:parseInt(b.width(),10),height:parseInt(b.height(),10),left:parseInt(b.css("left"),10),top:parseInt(b.css("top"),10)})})};typeof e.alsoResize=="object"&&!e.alsoResize.parentNode?e.alsoResize.length?(e.alsoResize=e.alsoResize[0],f(e.alsoResize)):a.each(e.alsoResize,function(a){f(a)}):f(e.alsoResize)},resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.originalSize,g=d.originalPosition,h={height:d.size.height-f.height||0,width:d.size.width-f.width||0,top:d.position.top-g.top||0,left:d.position.left-g.left||0},i=function(b,d){a(b).each(function(){var b=a(this),e=a(this).data("resizable-alsoresize"),f={},g=d&&d.length?d:b.parents(c.originalElement[0]).length?["width","height"]:["width","height","top","left"];a.each(g,function(a,b){var c=(e[b]||0)+(h[b]||0);c&&c>=0&&(f[b]=c||null)}),b.css(f)})};typeof e.alsoResize=="object"&&!e.alsoResize.nodeType?a.each(e.alsoResize,function(a,b){i(a,b)}):i(e.alsoResize)},stop:function(b,c){a(this).removeData("resizable-alsoresize")}}),a.ui.plugin.add("resizable","animate",{stop:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d._proportionallyResizeElements,g=f.length&&/textarea/i.test(f[0].nodeName),h=g&&a.ui.hasScroll(f[0],"left")?0:d.sizeDiff.height,i=g?0:d.sizeDiff.width,j={width:d.size.width-i,height:d.size.height-h},k=parseInt(d.element.css("left"),10)+(d.position.left-d.originalPosition.left)||null,l=parseInt(d.element.css("top"),10)+(d.position.top-d.originalPosition.top)||null;d.element.animate(a.extend(j,l&&k?{top:l,left:k}:{}),{duration:e.animateDuration,easing:e.animateEasing,step:function(){var c={width:parseInt(d.element.css("width"),10),height:parseInt(d.element.css("height"),10),top:parseInt(d.element.css("top"),10),left:parseInt(d.element.css("left"),10)};f&&f.length&&a(f[0]).css({width:c.width,height:c.height}),d._updateCache(c),d._propagate("resize",b)}})}}),a.ui.plugin.add("resizable","containment",{start:function(b,d){var e=a(this).data("resizable"),f=e.options,g=e.element,h=f.containment,i=h instanceof a?h.get(0):/parent/.test(h)?g.parent().get(0):h;if(!!i){e.containerElement=a(i);if(/document/.test(h)||h==document)e.containerOffset={left:0,top:0},e.containerPosition={left:0,top:0},e.parentData={element:a(document),left:0,top:0,width:a(document).width(),height:a(document).height()||document.body.parentNode.scrollHeight};else{var j=a(i),k=[];a(["Top","Right","Left","Bottom"]).each(function(a,b){k[a]=c(j.css("padding"+b))}),e.containerOffset=j.offset(),e.containerPosition=j.position(),e.containerSize={height:j.innerHeight()-k[3],width:j.innerWidth()-k[1]};var l=e.containerOffset,m=e.containerSize.height,n=e.containerSize.width,o=a.ui.hasScroll(i,"left")?i.scrollWidth:n,p=a.ui.hasScroll(i)?i.scrollHeight:m;e.parentData={element:i,left:l.left,top:l.top,width:o,height:p}}}},resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.containerSize,g=d.containerOffset,h=d.size,i=d.position,j=d._aspectRatio||b.shiftKey,k={top:0,left:0},l=d.containerElement;l[0]!=document&&/static/.test(l.css("position"))&&(k=g),i.left<(d._helper?g.left:0)&&(d.size.width=d.size.width+(d._helper?d.position.left-g.left:d.position.left-k.left),j&&(d.size.height=d.size.width/e.aspectRatio),d.position.left=e.helper?g.left:0),i.top<(d._helper?g.top:0)&&(d.size.height=d.size.height+(d._helper?d.position.top-g.top:d.position.top),j&&(d.size.width=d.size.height*e.aspectRatio),d.position.top=d._helper?g.top:0),d.offset.left=d.parentData.left+d.position.left,d.offset.top=d.parentData.top+d.position.top;var m=Math.abs((d._helper?d.offset.left-k.left:d.offset.left-k.left)+d.sizeDiff.width),n=Math.abs((d._helper?d.offset.top-k.top:d.offset.top-g.top)+d.sizeDiff.height),o=d.containerElement.get(0)==d.element.parent().get(0),p=/relative|absolute/.test(d.containerElement.css("position"));o&&p&&(m-=d.parentData.left),m+d.size.width>=d.parentData.width&&(d.size.width=d.parentData.width-m,j&&(d.size.height=d.size.width/d.aspectRatio)),n+d.size.height>=d.parentData.height&&(d.size.height=d.parentData.height-n,j&&(d.size.width=d.size.height*d.aspectRatio))},stop:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.position,g=d.containerOffset,h=d.containerPosition,i=d.containerElement,j=a(d.helper),k=j.offset(),l=j.outerWidth()-d.sizeDiff.width,m=j.outerHeight()-d.sizeDiff.height;d._helper&&!e.animate&&/relative/.test(i.css("position"))&&a(this).css({left:k.left-h.left-g.left,width:l,height:m}),d._helper&&!e.animate&&/static/.test(i.css("position"))&&a(this).css({left:k.left-h.left-g.left,width:l,height:m})}}),a.ui.plugin.add("resizable","ghost",{start:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.size;d.ghost=d.originalElement.clone(),d.ghost.css({opacity:.25,display:"block",position:"relative",height:f.height,width:f.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof e.ghost=="string"?e.ghost:""),d.ghost.appendTo(d.helper)},resize:function(b,c){var d=a(this).data("resizable"),e=d.options;d.ghost&&d.ghost.css({position:"relative",height:d.size.height,width:d.size.width})},stop:function(b,c){var d=a(this).data("resizable"),e=d.options;d.ghost&&d.helper&&d.helper.get(0).removeChild(d.ghost.get(0))}}),a.ui.plugin.add("resizable","grid",{resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.size,g=d.originalSize,h=d.originalPosition,i=d.axis,j=e._aspectRatio||b.shiftKey;e.grid=typeof e.grid=="number"?[e.grid,e.grid]:e.grid;var k=Math.round((f.width-g.width)/(e.grid[0]||1))*(e.grid[0]||1),l=Math.round((f.height-g.height)/(e.grid[1]||1))*(e.grid[1]||1);/^(se|s|e)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l):/^(ne)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l,d.position.top=h.top-l):/^(sw)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l,d.position.left=h.left-k):(d.size.width=g.width+k,d.size.height=g.height+l,d.position.top=h.top-l,d.position.left=h.left-k)}});var c=function(a){return parseInt(a,10)||0},d=function(a){return!isNaN(parseInt(a,10))}})(jQuery);/*
+ * jQuery UI Selectable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.selectable",a.ui.mouse,{options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch"},_create:function(){var b=this;this.element.addClass("ui-selectable"),this.dragged=!1;var c;this.refresh=function(){c=a(b.options.filter,b.element[0]),c.addClass("ui-selectee"),c.each(function(){var b=a(this),c=b.offset();a.data(this,"selectable-item",{element:this,$element:b,left:c.left,top:c.top,right:c.left+b.outerWidth(),bottom:c.top+b.outerHeight(),startselected:!1,selected:b.hasClass("ui-selected"),selecting:b.hasClass("ui-selecting"),unselecting:b.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=c.addClass("ui-selectee"),this._mouseInit(),this.helper=a("<div class='ui-selectable-helper'></div>")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable"),this._mouseDestroy();return this},_mouseStart:function(b){var c=this;this.opos=[b.pageX,b.pageY];if(!this.options.disabled){var d=this.options;this.selectees=a(d.filter,this.element[0]),this._trigger("start",b),a(d.appendTo).append(this.helper),this.helper.css({left:b.clientX,top:b.clientY,width:0,height:0}),d.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var d=a.data(this,"selectable-item");d.startselected=!0,!b.metaKey&&!b.ctrlKey&&(d.$element.removeClass("ui-selected"),d.selected=!1,d.$element.addClass("ui-unselecting"),d.unselecting=!0,c._trigger("unselecting",b,{unselecting:d.element}))}),a(b.target).parents().andSelf().each(function(){var d=a.data(this,"selectable-item");if(d){var e=!b.metaKey&&!b.ctrlKey||!d.$element.hasClass("ui-selected");d.$element.removeClass(e?"ui-unselecting":"ui-selected").addClass(e?"ui-selecting":"ui-unselecting"),d.unselecting=!e,d.selecting=e,d.selected=e,e?c._trigger("selecting",b,{selecting:d.element}):c._trigger("unselecting",b,{unselecting:d.element});return!1}})}},_mouseDrag:function(b){var c=this;this.dragged=!0;if(!this.options.disabled){var d=this.options,e=this.opos[0],f=this.opos[1],g=b.pageX,h=b.pageY;if(e>g){var i=g;g=e,e=i}if(f>h){var i=h;h=f,f=i}this.helper.css({left:e,top:f,width:g-e,height:h-f}),this.selectees.each(function(){var i=a.data(this,"selectable-item");if(!!i&&i.element!=c.element[0]){var j=!1;d.tolerance=="touch"?j=!(i.left>g||i.right<e||i.top>h||i.bottom<f):d.tolerance=="fit"&&(j=i.left>e&&i.right<g&&i.top>f&&i.bottom<h),j?(i.selected&&(i.$element.removeClass("ui-selected"),i.selected=!1),i.unselecting&&(i.$element.removeClass("ui-unselecting"),i.unselecting=!1),i.selecting||(i.$element.addClass("ui-selecting"),i.selecting=!0,c._trigger("selecting",b,{selecting:i.element}))):(i.selecting&&((b.metaKey||b.ctrlKey)&&i.startselected?(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.$element.addClass("ui-selected"),i.selected=!0):(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.startselected&&(i.$element.addClass("ui-unselecting"),i.unselecting=!0),c._trigger("unselecting",b,{unselecting:i.element}))),i.selected&&!b.metaKey&&!b.ctrlKey&&!i.startselected&&(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,c._trigger("unselecting",b,{unselecting:i.element})))}});return!1}},_mouseStop:function(b){var c=this;this.dragged=!1;var d=this.options;a(".ui-unselecting",this.element[0]).each(function(){var d=a.data(this,"selectable-item");d.$element.removeClass("ui-unselecting"),d.unselecting=!1,d.startselected=!1,c._trigger("unselected",b,{unselected:d.element})}),a(".ui-selecting",this.element[0]).each(function(){var d=a.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass("ui-selected"),d.selecting=!1,d.selected=!0,d.startselected=!0,c._trigger("selected",b,{selected:d.element})}),this._trigger("stop",b),this.helper.remove();return!1}}),a.extend(a.ui.selectable,{version:"1.8.18"})})(jQuery);/*
+ * jQuery UI Sortable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Sortables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.sortable",a.ui.mouse,{widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3},_create:function(){var a=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):!1,this.offset=this.element.offset(),this._mouseInit(),this.ready=!0},destroy:function(){a.Widget.prototype.destroy.call(this),this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouseDestroy();for(var b=this.items.length-1;b>=0;b--)this.items[b].item.removeData(this.widgetName+"-item");return this},_setOption:function(b,c){b==="disabled"?(this.options[b]=c,this.widget()[c?"addClass":"removeClass"]("ui-sortable-disabled")):a.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(b,c){var d=this;if(this.reverting)return!1;if(this.options.disabled||this.options.type=="static")return!1;this._refreshItems(b);var e=null,f=this,g=a(b.target).parents().each(function(){if(a.data(this,d.widgetName+"-item")==f){e=a(this);return!1}});a.data(b.target,d.widgetName+"-item")==f&&(e=a(b.target));if(!e)return!1;if(this.options.handle&&!c){var h=!1;a(this.options.handle,e).find("*").andSelf().each(function(){this==b.target&&(h=!0)});if(!h)return!1}this.currentItem=e,this._removeCurrentsFromItems();return!0},_mouseStart:function(b,c,d){var e=this.options,f=this;this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(b),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this._generatePosition(b),this.originalPageX=b.pageX,this.originalPageY=b.pageY,e.cursorAt&&this._adjustOffsetFromHelper(e.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!=this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),e.containment&&this._setContainment(),e.cursor&&(a("body").css("cursor")&&(this._storedCursor=a("body").css("cursor")),a("body").css("cursor",e.cursor)),e.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",e.opacity)),e.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",e.zIndex)),this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",b,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions();if(!d)for(var g=this.containers.length-1;g>=0;g--)this.containers[g]._trigger("activate",b,f._uiHash(this));a.ui.ddmanager&&(a.ui.ddmanager.current=this),a.ui.ddmanager&&!e.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(b);return!0},_mouseDrag:function(b){this.position=this._generatePosition(b),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs);if(this.options.scroll){var c=this.options,d=!1;this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-b.pageY<c.scrollSensitivity?this.scrollParent[0].scrollTop=d=this.scrollParent[0].scrollTop+c.scrollSpeed:b.pageY-this.overflowOffset.top<c.scrollSensitivity&&(this.scrollParent[0].scrollTop=d=this.scrollParent[0].scrollTop-c.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-b.pageX<c.scrollSensitivity?this.scrollParent[0].scrollLeft=d=this.scrollParent[0].scrollLeft+c.scrollSpeed:b.pageX-this.overflowOffset.left<c.scrollSensitivity&&(this.scrollParent[0].scrollLeft=d=this.scrollParent[0].scrollLeft-c.scrollSpeed)):(b.pageY-a(document).scrollTop()<c.scrollSensitivity?d=a(document).scrollTop(a(document).scrollTop()-c.scrollSpeed):a(window).height()-(b.pageY-a(document).scrollTop())<c.scrollSensitivity&&(d=a(document).scrollTop(a(document).scrollTop()+c.scrollSpeed)),b.pageX-a(document).scrollLeft()<c.scrollSensitivity?d=a(document).scrollLeft(a(document).scrollLeft()-c.scrollSpeed):a(window).width()-(b.pageX-a(document).scrollLeft())<c.scrollSensitivity&&(d=a(document).scrollLeft(a(document).scrollLeft()+c.scrollSpeed))),d!==!1&&a.ui.ddmanager&&!c.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(var e=this.items.length-1;e>=0;e--){var f=this.items[e],g=f.item[0],h=this._intersectsWithPointer(f);if(!h)continue;if(g!=this.currentItem[0]&&this.placeholder[h==1?"next":"prev"]()[0]!=g&&!a.ui.contains(this.placeholder[0],g)&&(this.options.type=="semi-dynamic"?!a.ui.contains(this.element[0],g):!0)){this.direction=h==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(f))this._rearrange(b,f);else break;this._trigger("change",b,this._uiHash());break}}this._contactContainers(b),a.ui.ddmanager&&a.ui.ddmanager.drag(this,b),this._trigger("sort",b,this._uiHash()),this.lastPositionAbs=this.positionAbs;return!1},_mouseStop:function(b,c){if(!!b){a.ui.ddmanager&&!this.options.dropBehaviour&&a.ui.ddmanager.drop(this,b);if(this.options.revert){var d=this,e=d.placeholder.offset();d.reverting=!0,a(this.helper).animate({left:e.left-this.offset.parent.left-d.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:e.top-this.offset.parent.top-d.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){d._clear(b)})}else this._clear(b,c);return!1}},cancel:function(){var b=this;if(this.dragging){this._mouseUp({target:null}),this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("deactivate",null,b._uiHash(this)),this.containers[c].containerCache.over&&(this.containers[c]._trigger("out",null,b._uiHash(this)),this.containers[c].containerCache.over=0)}this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),a.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?a(this.domPosition.prev).after(this.currentItem):a(this.domPosition.parent).prepend(this.currentItem));return this},serialize:function(b){var c=this._getItemsAsjQuery(b&&b.connected),d=[];b=b||{},a(c).each(function(){var c=(a(b.item||this).attr(b.attribute||"id")||"").match(b.expression||/(.+)[-=_](.+)/);c&&d.push((b.key||c[1]+"[]")+"="+(b.key&&b.expression?c[1]:c[2]))}),!d.length&&b.key&&d.push(b.key+"=");return d.join("&")},toArray:function(b){var c=this._getItemsAsjQuery(b&&b.connected),d=[];b=b||{},c.each(function(){d.push(a(b.item||this).attr(b.attribute||"id")||"")});return d},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,d=this.positionAbs.top,e=d+this.helperProportions.height,f=a.left,g=f+a.width,h=a.top,i=h+a.height,j=this.offset.click.top,k=this.offset.click.left,l=d+j>h&&d+j<i&&b+k>f&&b+k<g;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?l:f<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<g&&h<d+this.helperProportions.height/2&&e-this.helperProportions.height/2<i},_intersectsWithPointer:function(b){var c=a.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,b.top,b.height),d=a.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,b.left,b.width),e=c&&d,f=this._getDragVerticalDirection(),g=this._getDragHorizontalDirection();if(!e)return!1;return this.floating?g&&g=="right"||f=="down"?2:1:f&&(f=="down"?2:1)},_intersectsWithSides:function(b){var c=a.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,b.top+b.height/2,b.height),d=a.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,b.left+b.width/2,b.width),e=this._getDragVerticalDirection(),f=this._getDragHorizontalDirection();return this.floating&&f?f=="right"&&d||f=="left"&&!d:e&&(e=="down"&&c||e=="up"&&!c)},_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a),this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(b){var c=this,d=[],e=[],f=this._connectWith();if(f&&b)for(var g=f.length-1;g>=0;g--){var h=a(f[g]);for(var i=h.length-1;i>=0;i--){var j=a.data(h[i],this.widgetName);j&&j!=this&&!j.options.disabled&&e.push([a.isFunction(j.options.items)?j.options.items.call(j.element):a(j.options.items,j.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),j])}}e.push([a.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):a(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(var g=e.length-1;g>=0;g--)e[g][0].each(function(){d.push(this)});return a(d)},_removeCurrentsFromItems:function(){var a=this.currentItem.find(":data("+this.widgetName+"-item)");for(var b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(b){this.items=[],this.containers=[this];var c=this.items,d=this,e=[[a.isFunction(this.options.items)?this.options.items.call(this.element[0],b,{item:this.currentItem}):a(this.options.items,this.element),this]],f=this._connectWith();if(f&&this.ready)for(var g=f.length-1;g>=0;g--){var h=a(f[g]);for(var i=h.length-1;i>=0;i--){var j=a.data(h[i],this.widgetName);j&&j!=this&&!j.options.disabled&&(e.push([a.isFunction(j.options.items)?j.options.items.call(j.element[0],b,{item:this.currentItem}):a(j.options.items,j.element),j]),this.containers.push(j))}}for(var g=e.length-1;g>=0;g--){var k=e[g][1],l=e[g][0];for(var i=0,m=l.length;i<m;i++){var n=a(l[i]);n.data(this.widgetName+"-item",k),c.push({item:n,instance:k,width:0,height:0,left:0,top:0})}}},refreshPositions:function(b){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());for(var c=this.items.length-1;c>=0;c--){var d=this.items[c];if(d.instance!=this.currentContainer&&this.currentContainer&&d.item[0]!=this.currentItem[0])continue;var e=this.options.toleranceElement?a(this.options.toleranceElement,d.item):d.item;b||(d.width=e.outerWidth(),d.height=e.outerHeight());var f=e.offset();d.left=f.left,d.top=f.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(var c=this.containers.length-1;c>=0;c--){var f=this.containers[c].element.offset();this.containers[c].containerCache.left=f.left,this.containers[c].containerCache.top=f.top,this.containers[c].containerCache.width=this.containers[c].element.outerWidth(),this.containers[c].containerCache.height=this.containers[c].element.outerHeight()}return this},_createPlaceholder:function(b){var c=b||this,d=c.options;if(!d.placeholder||d.placeholder.constructor==String){var e=d.placeholder;d.placeholder={element:function(){var b=a(document.createElement(c.currentItem[0].nodeName)).addClass(e||c.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];e||(b.style.visibility="hidden");return b},update:function(a,b){if(!e||!!d.forcePlaceholderSize)b.height()||b.height(c.currentItem.innerHeight()-parseInt(c.currentItem.css("paddingTop")||0,10)-parseInt(c.currentItem.css("paddingBottom")||0,10)),b.width()||b.width(c.currentItem.innerWidth()-parseInt(c.currentItem.css("paddingLeft")||0,10)-parseInt(c.currentItem.css("paddingRight")||0,10))}}}c.placeholder=a(d.placeholder.element.call(c.element,c.currentItem)),c.currentItem.after(c.placeholder),d.placeholder.update(c,c.placeholder)},_contactContainers:function(b){var c=null,d=null;for(var e=this.containers.length-1;e>=0;e--){if(a.ui.contains(this.currentItem[0],this.containers[e].element[0]))continue;if(this._intersectsWith(this.containers[e].containerCache)){if(c&&a.ui.contains(this.containers[e].element[0],c.element[0]))continue;c=this.containers[e],d=e}else this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",b,this._uiHash(this)),this.containers[e].containerCache.over=0)}if(!!c)if(this.containers.length===1)this.containers[d]._trigger("over",b,this._uiHash(this)),this.containers[d].containerCache.over=1;else if(this.currentContainer!=this.containers[d]){var f=1e4,g=null,h=this.positionAbs[this.containers[d].floating?"left":"top"];for(var i=this.items.length-1;i>=0;i--){if(!a.ui.contains(this.containers[d].element[0],this.items[i].item[0]))continue;var j=this.items[i][this.containers[d].floating?"left":"top"];Math.abs(j-h)<f&&(f=Math.abs(j-h),g=this.items[i])}if(!g&&!this.options.dropOnEmpty)return;this.currentContainer=this.containers[d],g?this._rearrange(b,g,null,!0):this._rearrange(b,null,this.containers[d].element,!0),this._trigger("change",b,this._uiHash()),this.containers[d]._trigger("change",b,this._uiHash(this)),this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[d]._trigger("over",b,this._uiHash(this)),this.containers[d].containerCache.over=1}},_createHelper:function(b){var c=this.options,d=a.isFunction(c.helper)?a(c.helper.apply(this.element[0],[b,this.currentItem])):c.helper=="clone"?this.currentItem.clone():this.currentItem;d.parents("body").length||a(c.appendTo!="parent"?c.appendTo:this.currentItem[0].parentNode)[0].appendChild(d[0]),d[0]==this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(d[0].style.width==""||c.forceHelperSize)&&d.width(this.currentItem.width()),(d[0].style.height==""||c.forceHelperSize)&&d.height(this.currentItem.height());return d},_adjustOffsetFromHelper:function(b){typeof b=="string"&&(b=b.split(" ")),a.isArray(b)&&(b={left:+b[0],top:+b[1]||0}),"left"in b&&(this.offset.click.left=b.left+this.margins.left),"right"in b&&(this.offset.click.left=this.helperProportions.width-b.right+this.margins.left),"top"in b&&(this.offset.click.top=b.top+this.margins.top),"bottom"in b&&(this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])&&(b.left+=this.scrollParent.scrollLeft(),b.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)b={top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var b=this.options;b.containment=="parent"&&(b.containment=this.helper[0].parentNode);if(b.containment=="document"||b.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,a(b.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a(b.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(b.containment)){var c=a(b.containment)[0],d=a(b.containment).offset(),e=a(c).css("overflow")!="hidden";this.containment=[d.left+(parseInt(a(c).css("borderLeftWidth"),10)||0)+(parseInt(a(c).css("paddingLeft"),10)||0)-this.margins.left,d.top+(parseInt(a(c).css("borderTopWidth"),10)||0)+(parseInt(a(c).css("paddingTop"),10)||0)-this.margins.top,d.left+(e?Math.max(c.scrollWidth,c.offsetWidth):c.offsetWidth)-(parseInt(a(c).css("borderLeftWidth"),10)||0)-(parseInt(a(c).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,d.top+(e?Math.max(c.scrollHeight,c.offsetHeight):c.offsetHeight)-(parseInt(a(c).css("borderTopWidth"),10)||0)-(parseInt(a(c).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(b,c){c||(c=this.position);var d=b=="absolute"?1:-1,e=this.options,f=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName);return{top:c.top+this.offset.relative.top*d+this.offset.parent.top*d-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),left:c.left+this.offset.relative.left*d+this.offset.parent.left*d-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:function(b){var c=this.options,d=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(d[0].tagName);this.cssPosition=="relative"&&(this.scrollParent[0]==document||this.scrollParent[0]==this.offsetParent[0])&&(this.offset.relative=this._getRelativeOffset());var f=b.pageX,g=b.pageY;if(this.originalPosition){this.containment&&(b.pageX-this.offset.click.left<this.containment[0]&&(f=this.containment[0]+this.offset.click.left),b.pageY-this.offset.click.top<this.containment[1]&&(g=this.containment[1]+this.offset.click.top),b.pageX-this.offset.click.left>this.containment[2]&&(f=this.containment[2]+this.offset.click.left),b.pageY-this.offset.click.top>this.containment[3]&&(g=this.containment[3]+this.offset.click.top));if(c.grid){var h=this.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1];g=this.containment?h-this.offset.click.top<this.containment[1]||h-this.offset.click.top>this.containment[3]?h-this.offset.click.top<this.containment[1]?h+c.grid[1]:h-c.grid[1]:h:h;var i=this.originalPageX+Math.round((f-this.originalPageX)/c.grid[0])*c.grid[0];f=this.containment?i-this.offset.click.left<this.containment[0]||i-this.offset.click.left>this.containment[2]?i-this.offset.click.left<this.containment[0]?i+c.grid[0]:i-c.grid[0]:i:i}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(a.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:d.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(a.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:d.scrollLeft())}},_rearrange:function(a,b,c,d){c?c[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],this.direction=="down"?b.item[0]:b.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var e=this,f=this.counter;window.setTimeout(function(){f==e.counter&&e.refreshPositions(!d)},0)},_clear:function(b,c){this.reverting=!1;var d=[],e=this;!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var f in this._storedCSS)if(this._storedCSS[f]=="auto"||this._storedCSS[f]=="static")this._storedCSS[f]="";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();this.fromOutside&&!c&&d.push(function(a){this._trigger("receive",a,this._uiHash(this.fromOutside))}),(this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!c&&d.push(function(a){this._trigger("update",a,this._uiHash())});if(!a.ui.contains(this.element[0],this.currentItem[0])){c||d.push(function(a){this._trigger("remove",a,this._uiHash())});for(var f=this.containers.length-1;f>=0;f--)a.ui.contains(this.containers[f].element[0],this.currentItem[0])&&!c&&(d.push(function(a){return function(b){a._trigger("receive",b,this._uiHash(this))}}.call(this,this.containers[f])),d.push(function(a){return function(b){a._trigger("update",b,this._uiHash(this))}}.call(this,this.containers[f])))}for(var f=this.containers.length-1;f>=0;f--)c||d.push(function(a){return function(b){a._trigger("deactivate",b,this._uiHash(this))}}.call(this,this.containers[f])),this.containers[f].containerCache.over&&(d.push(function(a){return function(b){a._trigger("out",b,this._uiHash(this))}}.call(this,this.containers[f])),this.containers[f].containerCache.over=0);this._storedCursor&&a("body").css("cursor",this._storedCursor),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex),this.dragging=!1;if(this.cancelHelperRemoval){if(!c){this._trigger("beforeStop",b,this._uiHash());for(var f=0;f<d.length;f++)d[f].call(this,b);this._trigger("stop",b,this._uiHash())}return!1}c||this._trigger("beforeStop",b,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!=this.currentItem[0]&&this.helper.remove(),this.helper=null;if(!c){for(var f=0;f<d.length;f++)d[f].call(this,b);this._trigger("stop",b,this._uiHash())}this.fromOutside=!1;return!0},_trigger:function(){a.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(b){var c=b||this;return{helper:c.helper,placeholder:c.placeholder||a([]),position:c.position,originalPosition:c.originalPosition,offset:c.positionAbs,item:c.currentItem,sender:b?b.element:null}}}),a.extend(a.ui.sortable,{version:"1.8.18"})})(jQuery);/*
+ * jQuery UI Accordion 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:!0,clearStyle:!1,collapsible:!1,event:"click",fillSpace:!1,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:!1,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var b=this,c=b.options;b.running=0,b.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix"),b.headers=b.element.find(c.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){c.disabled||a(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){c.disabled||a(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){c.disabled||a(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){c.disabled||a(this).removeClass("ui-state-focus")}),b.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");if(c.navigation){var d=b.element.find("a").filter(c.navigationFilter).eq(0);if(d.length){var e=d.closest(".ui-accordion-header");e.length?b.active=e:b.active=d.closest(".ui-accordion-content").prev()}}b.active=b._findActive(b.active||c.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top"),b.active.next().addClass("ui-accordion-content-active"),b._createIcons(),b.resize(),b.element.attr("role","tablist"),b.headers.attr("role","tab").bind("keydown.accordion",function(a){return b._keydown(a)}).next().attr("role","tabpanel"),b.headers.not(b.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide(),b.active.length?b.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):b.headers.eq(0).attr("tabIndex",0),a.browser.safari||b.headers.find("a").attr("tabIndex",-1),c.event&&b.headers.bind(c.event.split(" ").join(".accordion ")+".accordion",function(a){b._clickHandler.call(b,a,this),a.preventDefault()})},_createIcons:function(){var b=this.options;b.icons&&(a("<span></span>").addClass("ui-icon "+b.icons.header).prependTo(this.headers),this.active.children(".ui-icon").toggleClass(b.icons.header).toggleClass(b.icons.headerSelected),this.element.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.children(".ui-icon").remove(),this.element.removeClass("ui-accordion-icons")},destroy:function(){var b=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex"),this.headers.find("a").removeAttr("tabIndex"),this._destroyIcons();var c=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");(b.autoHeight||b.fillHeight)&&c.css("height","");return a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments),b=="active"&&this.activate(c),b=="icons"&&(this._destroyIcons(),c&&this._createIcons()),b=="disabled"&&this.headers.add(this.headers.next())[c?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(b){if(!(this.options.disabled||b.altKey||b.ctrlKey)){var c=a.ui.keyCode,d=this.headers.length,e=this.headers.index(b.target),f=!1;switch(b.keyCode){case c.RIGHT:case c.DOWN:f=this.headers[(e+1)%d];break;case c.LEFT:case c.UP:f=this.headers[(e-1+d)%d];break;case c.SPACE:case c.ENTER:this._clickHandler({target:b.target},b.target),b.preventDefault()}if(f){a(b.target).attr("tabIndex",-1),a(f).attr("tabIndex",0),f.focus();return!1}return!0}},resize:function(){var b=this.options,c;if(b.fillSpace){if(a.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}c=this.element.parent().height(),a.browser.msie&&this.element.parent().css("overflow",d),this.headers.each(function(){c-=a(this).outerHeight(!0)}),this.headers.next().each(function(){a(this).height(Math.max(0,c-a(this).innerHeight()+a(this).height()))}).css("overflow","auto")}else b.autoHeight&&(c=0,this.headers.next().each(function(){c=Math.max(c,a(this).height("").height())}).height(c));return this},activate:function(a){this.options.active=a;var b=this._findActive(a)[0];this._clickHandler({target:b},b);return this},_findActive:function(b){return b?typeof b=="number"?this.headers.filter(":eq("+b+")"):this.headers.not(this.headers.not(b)):b===!1?a([]):this.headers.filter(":eq(0)")},_clickHandler:function(b,c){var d=this.options;if(!d.disabled){if(!b.target){if(!d.collapsible)return;this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header),this.active.next().addClass("ui-accordion-content-active");var e=this.active.next(),f={options:d,newHeader:a([]),oldHeader:d.active,newContent:a([]),oldContent:e},g=this.active=a([]);this._toggle(g,e,f);return}var h=a(b.currentTarget||c),i=h[0]===this.active[0];d.active=d.collapsible&&i?!1:this.headers.index(h);if(this.running||!d.collapsible&&i)return;var j=this.active,g=h.next(),e=this.active.next(),f={options:d,newHeader:i&&d.collapsible?a([]):h,oldHeader:this.active,newContent:i&&d.collapsible?a([]):g,oldContent:e},k=this.headers.index(this.active[0])>this.headers.index(h[0]);this.active=i?a([]):h,this._toggle(g,e,f,i,k),j.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header),i||(h.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected),h.next().addClass("ui-accordion-content-active"));return}},_toggle:function(b,c,d,e,f){var g=this,h=g.options;g.toShow=b,g.toHide=c,g.data=d;var i=function(){if(!!g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data),g.running=c.size()===0?b.size():c.size();if(h.animated){var j={};h.collapsible&&e?j={toShow:a([]),toHide:c,complete:i,down:f,autoHeight:h.autoHeight||h.fillSpace}:j={toShow:b,toHide:c,complete:i,down:f,autoHeight:h.autoHeight||h.fillSpace},h.proxied||(h.proxied=h.animated),h.proxiedDuration||(h.proxiedDuration=h.duration),h.animated=a.isFunction(h.proxied)?h.proxied(j):h.proxied,h.duration=a.isFunction(h.proxiedDuration)?h.proxiedDuration(j):h.proxiedDuration;var k=a.ui.accordion.animations,l=h.duration,m=h.animated;m&&!k[m]&&!a.easing[m]&&(m="slide"),k[m]||(k[m]=function(a){this.slide(a,{easing:m,duration:l||700})}),k[m](j)}else h.collapsible&&e?b.toggle():(c.hide(),b.show()),i(!0);c.prev().attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).blur(),b.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;this.running||(this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""}),this.toHide.removeClass("ui-accordion-content-active"),this.toHide.length&&(this.toHide.parent()[0].className=this.toHide.parent()[0].className),this._trigger("change",null,this.data))}}),a.extend(a.ui.accordion,{version:"1.8.18",animations:{slide:function(b,c){b=a.extend({easing:"swing",duration:300},b,c);if(!b.toHide.size())b.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},b);else{if(!b.toShow.size()){b.toHide.animate({height:"hide",paddingTop:"hide",paddingBottom:"hide"},b);return}var d=b.toShow.css("overflow"),e=0,f={},g={},h=["height","paddingTop","paddingBottom"],i,j=b.toShow;i=j[0].style.width,j.width(j.parent().width()-parseFloat(j.css("paddingLeft"))-parseFloat(j.css("paddingRight"))-(parseFloat(j.css("borderLeftWidth"))||0)-(parseFloat(j.css("borderRightWidth"))||0)),a.each(h,function(c,d){g[d]="hide";var e=(""+a.css(b.toShow[0],d)).match(/^([\d+-.]+)(.*)$/);f[d]={value:e[1],unit:e[2]||"px"}}),b.toShow.css({height:0,overflow:"hidden"}).show(),b.toHide.filter(":hidden").each(b.complete).end().filter(":visible").animate(g,{step:function(a,c){c.prop=="height"&&(e=c.end-c.start===0?0:(c.now-c.start)/(c.end-c.start)),b.toShow[0].style[c.prop]=e*f[c.prop].value+f[c.prop].unit},duration:b.duration,easing:b.easing,complete:function(){b.autoHeight||b.toShow.css("height",""),b.toShow.css({width:i,overflow:d}),b.complete()}})}},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1e3:200})}}})})(jQuery);/*
+ * jQuery UI Autocomplete 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.position.js
+ */(function(a,b){var c=0;a.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var b=this,c=this.element[0].ownerDocument,d;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!b.options.disabled&&!b.element.propAttr("readOnly")){d=!1;var e=a.ui.keyCode;switch(c.keyCode){case e.PAGE_UP:b._move("previousPage",c);break;case e.PAGE_DOWN:b._move("nextPage",c);break;case e.UP:b._move("previous",c),c.preventDefault();break;case e.DOWN:b._move("next",c),c.preventDefault();break;case e.ENTER:case e.NUMPAD_ENTER:b.menu.active&&(d=!0,c.preventDefault());case e.TAB:if(!b.menu.active)return;b.menu.select(c);break;case e.ESCAPE:b.element.val(b.term),b.close(c);break;default:clearTimeout(b.searching),b.searching=setTimeout(function(){b.term!=b.element.val()&&(b.selectedItem=null,b.search(null,c))},b.options.delay)}}}).bind("keypress.autocomplete",function(a){d&&(d=!1,a.preventDefault())}).bind("focus.autocomplete",function(){b.options.disabled||(b.selectedItem=null,b.previous=b.element.val())}).bind("blur.autocomplete",function(a){b.options.disabled||(clearTimeout(b.searching),b.closing=setTimeout(function(){b.close(a),b._change(a)},150))}),this._initSource(),this.response=function(){return b._response.apply(b,arguments)},this.menu=a("<ul></ul>").addClass("ui-autocomplete").appendTo(a(this.options.appendTo||"body",c)[0]).mousedown(function(c){var d=b.menu.element[0];a(c.target).closest(".ui-menu-item").length||setTimeout(function(){a(document).one("mousedown",function(c){c.target!==b.element[0]&&c.target!==d&&!a.ui.contains(d,c.target)&&b.close()})},1),setTimeout(function(){clearTimeout(b.closing)},13)}).menu({focus:function(a,c){var d=c.item.data("item.autocomplete");!1!==b._trigger("focus",a,{item:d})&&/^key/.test(a.originalEvent.type)&&b.element.val(d.value)},selected:function(a,d){var e=d.item.data("item.autocomplete"),f=b.previous;b.element[0]!==c.activeElement&&(b.element.focus(),b.previous=f,setTimeout(function(){b.previous=f,b.selectedItem=e},1)),!1!==b._trigger("select",a,{item:e})&&b.element.val(e.value),b.term=b.element.val(),b.close(a),b.selectedItem=e},blur:function(a,c){b.menu.element.is(":visible")&&b.element.val()!==b.term&&b.element.val(b.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu"),a.fn.bgiframe&&this.menu.element.bgiframe(),b.beforeunloadHandler=function(){b.element.removeAttr("autocomplete")},a(window).bind("beforeunload",b.beforeunloadHandler)},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup"),this.menu.element.remove(),a(window).unbind("beforeunload",this.beforeunloadHandler),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments),b==="source"&&this._initSource(),b==="appendTo"&&this.menu.element.appendTo(a(c||"body",this.element[0].ownerDocument)[0]),b==="disabled"&&c&&this.xhr&&this.xhr.abort()},_initSource:function(){var b=this,d,e;a.isArray(this.options.source)?(d=this.options.source,this.source=function(b,c){c(a.ui.autocomplete.filter(d,b.term))}):typeof this.options.source=="string"?(e=this.options.source,this.source=function(d,f){b.xhr&&b.xhr.abort(),b.xhr=a.ajax({url:e,data:d,dataType:"json",context:{autocompleteRequest:++c},success:function(a,b){this.autocompleteRequest===c&&f(a)},error:function(){this.autocompleteRequest===c&&f([])}})}):this.source=this.options.source},search:function(a,b){a=a!=null?a:this.element.val(),this.term=this.element.val();if(a.length<this.options.minLength)return this.close(b);clearTimeout(this.closing);if(this._trigger("search",b)!==!1)return this._search(a)},_search:function(a){this.pending++,this.element.addClass("ui-autocomplete-loading"),this.source({term:a},this.response)},_response:function(a){!this.options.disabled&&a&&a.length?(a=this._normalize(a),this._suggest(a),this._trigger("open")):this.close(),this.pending--,this.pending||this.element.removeClass("ui-autocomplete-loading")},close:function(a){clearTimeout(this.closing),this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.deactivate(),this._trigger("close",a))},_change:function(a){this.previous!==this.element.val()&&this._trigger("change",a,{item:this.selectedItem})},_normalize:function(b){if(b.length&&b[0].label&&b[0].value)return b;return a.map(b,function(b){if(typeof b=="string")return{label:b,value:b};return a.extend({label:b.label||b.value,value:b.value||b.label},b)})},_suggest:function(b){var c=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(c,b),this.menu.deactivate(),this.menu.refresh(),c.show(),this._resizeMenu(),c.position(a.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next(new a.Event("mouseover"))},_resizeMenu:function(){var a=this.menu.element;a.outerWidth(Math.max(a.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(b,c){var d=this;a.each(c,function(a,c){d._renderItem(b,c)})},_renderItem:function(b,c){return a("<li></li>").data("item.autocomplete",c).append(a("<a></a>").text(c.label)).appendTo(b)},_move:function(a,b){if(!this.menu.element.is(":visible"))this.search(null,b);else{if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term),this.menu.deactivate();return}this.menu[a](b)}},widget:function(){return this.menu.element}}),a.extend(a.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&")},filter:function(b,c){var d=new RegExp(a.ui.autocomplete.escapeRegex(c),"i");return a.grep(b,function(a){return d.test(a.label||a.value||a)})}})})(jQuery),function(a){a.widget("ui.menu",{_create:function(){var b=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(c){!a(c.target).closest(".ui-menu-item a").length||(c.preventDefault(),b.select(c))}),this.refresh()},refresh:function(){var b=this,c=this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem");c.children("a").addClass("ui-corner-all").attr("tabindex",-1).mouseenter(function(c){b.activate(c,a(this).parent())}).mouseleave(function(){b.deactivate()})},activate:function(a,b){this.deactivate();if(this.hasScroll()){var c=b.offset().top-this.element.offset().top,d=this.element.scrollTop(),e=this.element.height();c<0?this.element.scrollTop(d+c):c>=e&&this.element.scrollTop(d+c-e+b.height())}this.active=b.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end(),this._trigger("focus",a,{item:b})},deactivate:function(){!this.active||(this.active.children("a").removeClass("ui-state-hover").removeAttr("id"),this._trigger("blur"),this.active=null)},next:function(a){this.move("next",".ui-menu-item:first",a)},previous:function(a){this.move("prev",".ui-menu-item:last",a)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(a,b,c){if(!this.active)this.activate(c,this.element.children(b));else{var d=this.active[a+"All"](".ui-menu-item").eq(0);d.length?this.activate(c,d):this.activate(c,this.element.children(b))}},nextPage:function(b){if(this.hasScroll()){if(!this.active||this.last()){this.activate(b,this.element.children(".ui-menu-item:first"));return}var c=this.active.offset().top,d=this.element.height(),e=this.element.children(".ui-menu-item").filter(function(){var b=a(this).offset().top-c-d+a(this).height();return b<10&&b>-10});e.length||(e=this.element.children(".ui-menu-item:last")),this.activate(b,e)}else this.activate(b,this.element.children(".ui-menu-item").filter(!this.active||this.last()?":first":":last"))},previousPage:function(b){if(this.hasScroll()){if(!this.active||this.first()){this.activate(b,this.element.children(".ui-menu-item:last"));return}var c=this.active.offset().top,d=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var b=a(this).offset().top-c+d-a(this).height();return b<10&&b>-10}),result.length||(result=this.element.children(".ui-menu-item:first")),this.activate(b,result)}else this.activate(b,this.element.children(".ui-menu-item").filter(!this.active||this.first()?":last":":first"))},hasScroll:function(){return this.element.height()<this.element[a.fn.prop?"prop":"attr"]("scrollHeight")},select:function(a){this._trigger("selected",a,{item:this.active})}})}(jQuery);/*
+ * jQuery UI Button 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */(function(a,b){var c,d,e,f,g="ui-button ui-widget ui-state-default ui-corner-all",h="ui-state-hover ui-state-active ",i="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",j=function(){var b=a(this).find(":ui-button");setTimeout(function(){b.button("refresh")},1)},k=function(b){var c=b.name,d=b.form,e=a([]);c&&(d?e=a(d).find("[name='"+c+"']"):e=a("[name='"+c+"']",b.ownerDocument).filter(function(){return!this.form}));return e};a.widget("ui.button",{options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset.button").bind("reset.button",j),typeof this.options.disabled!="boolean"?this.options.disabled=!!this.element.propAttr("disabled"):this.element.propAttr("disabled",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var b=this,h=this.options,i=this.type==="checkbox"||this.type==="radio",l="ui-state-hover"+(i?"":" ui-state-active"),m="ui-state-focus";h.label===null&&(h.label=this.buttonElement.html()),this.buttonElement.addClass(g).attr("role","button").bind("mouseenter.button",function(){h.disabled||(a(this).addClass("ui-state-hover"),this===c&&a(this).addClass("ui-state-active"))}).bind("mouseleave.button",function(){h.disabled||a(this).removeClass(l)}).bind("click.button",function(a){h.disabled&&(a.preventDefault(),a.stopImmediatePropagation())}),this.element.bind("focus.button",function(){b.buttonElement.addClass(m)}).bind("blur.button",function(){b.buttonElement.removeClass(m)}),i&&(this.element.bind("change.button",function(){f||b.refresh()}),this.buttonElement.bind("mousedown.button",function(a){h.disabled||(f=!1,d=a.pageX,e=a.pageY)}).bind("mouseup.button",function(a){!h.disabled&&(d!==a.pageX||e!==a.pageY)&&(f=!0)})),this.type==="checkbox"?this.buttonElement.bind("click.button",function(){if(h.disabled||f)return!1;a(this).toggleClass("ui-state-active"),b.buttonElement.attr("aria-pressed",b.element[0].checked)}):this.type==="radio"?this.buttonElement.bind("click.button",function(){if(h.disabled||f)return!1;a(this).addClass("ui-state-active"),b.buttonElement.attr("aria-pressed","true");var c=b.element[0];k(c).not(c).map(function(){return a(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown.button",function(){if(h.disabled)return!1;a(this).addClass("ui-state-active"),c=this,a(document).one("mouseup",function(){c=null})}).bind("mouseup.button",function(){if(h.disabled)return!1;a(this).removeClass("ui-state-active")}).bind("keydown.button",function(b){if(h.disabled)return!1;(b.keyCode==a.ui.keyCode.SPACE||b.keyCode==a.ui.keyCode.ENTER)&&a(this).addClass("ui-state-active")}).bind("keyup.button",function(){a(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(b){b.keyCode===a.ui.keyCode.SPACE&&a(this).click()})),this._setOption("disabled",h.disabled),this._resetButton()},_determineButtonType:function(){this.element.is(":checkbox")?this.type="checkbox":this.element.is(":radio")?this.type="radio":this.element.is("input")?this.type="input":this.type="button";if(this.type==="checkbox"||this.type==="radio"){var a=this.element.parents().filter(":last"),b="label[for='"+this.element.attr("id")+"']";this.buttonElement=a.find(b),this.buttonElement.length||(a=a.length?a.siblings():this.element.siblings(),this.buttonElement=a.filter(b),this.buttonElement.length||(this.buttonElement=a.find(b))),this.element.addClass("ui-helper-hidden-accessible");var c=this.element.is(":checked");c&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.attr("aria-pressed",c)}else this.buttonElement=this.element},widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(g+" "+h+" "+i).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title"),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments);b==="disabled"?c?this.element.propAttr("disabled",!0):this.element.propAttr("disabled",!1):this._resetButton()},refresh:function(){var b=this.element.is(":disabled");b!==this.options.disabled&&this._setOption("disabled",b),this.type==="radio"?k(this.element[0]).each(function(){a(this).is(":checked")?a(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):a(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):this.type==="checkbox"&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if(this.type==="input")this.options.label&&this.element.val(this.options.label);else{var b=this.buttonElement.removeClass(i),c=a("<span></span>",this.element[0].ownerDocument).addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,e=d.primary&&d.secondary,f=[];d.primary||d.secondary?(this.options.text&&f.push("ui-button-text-icon"+(e?"s":d.primary?"-primary":"-secondary")),d.primary&&b.prepend("<span class='ui-button-icon-primary ui-icon "+d.primary+"'></span>"),d.secondary&&b.append("<span class='ui-button-icon-secondary ui-icon "+d.secondary+"'></span>"),this.options.text||(f.push(e?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||b.attr("title",c))):f.push("ui-button-text-only"),b.addClass(f.join(" "))}}}),a.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c),a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var b=this.element.css("direction")==="rtl";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(b?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(b?"ui-corner-left":"ui-corner-right").end().end()},destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy"),a.Widget.prototype.destroy.call(this)}})})(jQuery);/*
+ * jQuery UI Dialog 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.button.js
+ * jquery.ui.draggable.js
+ * jquery.ui.mouse.js
+ * jquery.ui.position.js
+ * jquery.ui.resizable.js
+ */(function(a,b){var c="ui-dialog ui-widget ui-widget-content ui-corner-all ",d={buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},e={maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},f=a.attrFn||{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0,click:!0};a.widget("ui.dialog",{options:{autoOpen:!0,buttons:{},closeOnEscape:!0,closeText:"close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:!1,maxWidth:!1,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",collision:"fit",using:function(b){var c=a(this).css(b).offset().top;c<0&&a(this).css("top",b.top-c)}},resizable:!0,show:null,stack:!0,title:"",width:300,zIndex:1e3},_create:function(){this.originalTitle=this.element.attr("title"),typeof this.originalTitle!="string"&&(this.originalTitle=""),this.options.title=this.options.title||this.originalTitle;var b=this,d=b.options,e=d.title||"&#160;",f=a.ui.dialog.getTitleId(b.element),g=(b.uiDialog=a("<div></div>")).appendTo(document.body).hide().addClass(c+d.dialogClass).css({zIndex:d.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(c){d.closeOnEscape&&!c.isDefaultPrevented()&&c.keyCode&&c.keyCode===a.ui.keyCode.ESCAPE&&(b.close(c),c.preventDefault())}).attr({role:"dialog","aria-labelledby":f}).mousedown(function(a){b.moveToTop(!1,a)}),h=b.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g),i=(b.uiDialogTitlebar=a("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),j=a('<a href="#"></a>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){j.addClass("ui-state-hover")},function(){j.removeClass("ui-state-hover")}).focus(function(){j.addClass("ui-state-focus")}).blur(function(){j.removeClass("ui-state-focus")}).click(function(a){b.close(a);return!1}).appendTo(i),k=(b.uiDialogTitlebarCloseText=a("<span></span>")).addClass("ui-icon ui-icon-closethick").text(d.closeText).appendTo(j),l=a("<span></span>").addClass("ui-dialog-title").attr("id",f).html(e).prependTo(i);a.isFunction(d.beforeclose)&&!a.isFunction(d.beforeClose)&&(d.beforeClose=d.beforeclose),i.find("*").add(i).disableSelection(),d.draggable&&a.fn.draggable&&b._makeDraggable(),d.resizable&&a.fn.resizable&&b._makeResizable(),b._createButtons(d.buttons),b._isOpen=!1,a.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy(),a.uiDialog.hide(),a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"),a.uiDialog.remove(),a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(b){var c=this,d,e;if(!1!==c._trigger("beforeClose",b)){c.overlay&&c.overlay.destroy(),c.uiDialog.unbind("keypress.ui-dialog"),c._isOpen=!1,c.options.hide?c.uiDialog.hide(c.options.hide,function(){c._trigger("close",b)}):(c.uiDialog.hide(),c._trigger("close",b)),a.ui.dialog.overlay.resize(),c.options.modal&&(d=0,a(".ui-dialog").each(function(){this!==c.uiDialog[0]&&(e=a(this).css("z-index"),isNaN(e)||(d=Math.max(d,e)))}),a.ui.dialog.maxZ=d);return c}},isOpen:function(){return this._isOpen},moveToTop:function(b,c){var d=this,e=d.options,f;if(e.modal&&!b||!e.stack&&!e.modal)return d._trigger("focus",c);e.zIndex>a.ui.dialog.maxZ&&(a.ui.dialog.maxZ=e.zIndex),d.overlay&&(a.ui.dialog.maxZ+=1,d.overlay.$el.css("z-index",a.ui.dialog.overlay.maxZ=a.ui.dialog.maxZ)),f={scrollTop:d.element.scrollTop(),scrollLeft:d.element.scrollLeft()},a.ui.dialog.maxZ+=1,d.uiDialog.css("z-index",a.ui.dialog.maxZ),d.element.attr(f),d._trigger("focus",c);return d},open:function(){if(!this._isOpen){var b=this,c=b.options,d=b.uiDialog;b.overlay=c.modal?new a.ui.dialog.overlay(b):null,b._size(),b._position(c.position),d.show(c.show),b.moveToTop(!0),c.modal&&d.bind("keydown.ui-dialog",function(b){if(b.keyCode===a.ui.keyCode.TAB){var c=a(":tabbable",this),d=c.filter(":first"),e=c.filter(":last");if(b.target===e[0]&&!b.shiftKey){d.focus(1);return!1}if(b.target===d[0]&&b.shiftKey){e.focus(1);return!1}}}),a(b.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus(),b._isOpen=!0,b._trigger("open");return b}},_createButtons:function(b){var c=this,d=!1,e=a("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=a("<div></div>").addClass("ui-dialog-buttonset").appendTo(e);c.uiDialog.find(".ui-dialog-buttonpane").remove(),typeof b=="object"&&b!==null&&a.each(b,function(){return!(d=!0)}),d&&(a.each(b,function(b,d){d=a.isFunction(d)?{click:d,text:b}:d;var e=a('<button type="button"></button>').click(function(){d.click.apply(c.element[0],arguments)}).appendTo(g);a.each(d,function(a,b){a!=="click"&&(a in f?e[a](b):e.attr(a,b))}),a.fn.button&&e.button()}),e.appendTo(c.uiDialog))},_makeDraggable:function(){function f(a){return{position:a.position,offset:a.offset}}var b=this,c=b.options,d=a(document),e;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(d,g){e=c.height==="auto"?"auto":a(this).height(),a(this).height(a(this).height()).addClass("ui-dialog-dragging"),b._trigger("dragStart",d,f(g))},drag:function(a,c){b._trigger("drag",a,f(c))},stop:function(g,h){c.position=[h.position.left-d.scrollLeft(),h.position.top-d.scrollTop()],a(this).removeClass("ui-dialog-dragging").height(e),b._trigger("dragStop",g,f(h)),a.ui.dialog.overlay.resize()}})},_makeResizable:function(c){function h(a){return{originalPosition:a.originalPosition,originalSize:a.originalSize,position:a.position,size:a.size}}c=c===b?this.options.resizable:c;var d=this,e=d.options,f=d.uiDialog.css("position"),g=typeof c=="string"?c:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:g,start:function(b,c){a(this).addClass("ui-dialog-resizing"),d._trigger("resizeStart",b,h(c))},resize:function(a,b){d._trigger("resize",a,h(b))},stop:function(b,c){a(this).removeClass("ui-dialog-resizing"),e.height=a(this).height(),e.width=a(this).width(),d._trigger("resizeStop",b,h(c)),a.ui.dialog.overlay.resize()}}).css("position",f).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(b){var c=[],d=[0,0],e;if(b){if(typeof b=="string"||typeof b=="object"&&"0"in b)c=b.split?b.split(" "):[b[0],b[1]],c.length===1&&(c[1]=c[0]),a.each(["left","top"],function(a,b){+c[a]===c[a]&&(d[a]=c[a],c[a]=b)}),b={my:c.join(" "),at:c.join(" "),offset:d.join(" ")};b=a.extend({},a.ui.dialog.prototype.options.position,b)}else b=a.ui.dialog.prototype.options.position;e=this.uiDialog.is(":visible"),e||this.uiDialog.show(),this.uiDialog.css({top:0,left:0}).position(a.extend({of:window},b)),e||this.uiDialog.hide()},_setOptions:function(b){var c=this,f={},g=!1;a.each(b,function(a,b){c._setOption(a,b),a in d&&(g=!0),a in e&&(f[a]=b)}),g&&this._size(),this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",f)},_setOption:function(b,d){var e=this,f=e.uiDialog;switch(b){case"beforeclose":b="beforeClose";break;case"buttons":e._createButtons(d);break;case"closeText":e.uiDialogTitlebarCloseText.text(""+d);break;case"dialogClass":f.removeClass(e.options.dialogClass).addClass(c+d);break;case"disabled":d?f.addClass("ui-dialog-disabled"):f.removeClass("ui-dialog-disabled");break;case"draggable":var g=f.is(":data(draggable)");g&&!d&&f.draggable("destroy"),!g&&d&&e._makeDraggable();break;case"position":e._position(d);break;case"resizable":var h=f.is(":data(resizable)");h&&!d&&f.resizable("destroy"),h&&typeof d=="string"&&f.resizable("option","handles",d),!h&&d!==!1&&e._makeResizable(d);break;case"title":a(".ui-dialog-title",e.uiDialogTitlebar).html(""+(d||"&#160;"))}a.Widget.prototype._setOption.apply(e,arguments)},_size:function(){var b=this.options,c,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0}),b.minWidth>b.width&&(b.width=b.minWidth),c=this.uiDialog.css({height:"auto",width:b.width}).height(),d=Math.max(0,b.minHeight-c);if(b.height==="auto")if(a.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();var f=this.element.css("height","auto").height();e||this.uiDialog.hide(),this.element.height(Math.max(f,d))}else this.element.height(Math.max(b.height-c,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}}),a.extend(a.ui.dialog,{version:"1.8.18",uuid:0,maxZ:0,getTitleId:function(a){var b=a.attr("id");b||(this.uuid+=1,b=this.uuid);return"ui-dialog-title-"+b},overlay:function(b){this.$el=a.ui.dialog.overlay.create(b)}}),a.extend(a.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:a.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),create:function(b){this.instances.length===0&&(setTimeout(function(){a.ui.dialog.overlay.instances.length&&a(document).bind(a.ui.dialog.overlay.events,function(b){if(a(b.target).zIndex()<a.ui.dialog.overlay.maxZ)return!1})},1),a(document).bind("keydown.dialog-overlay",function(c){b.options.closeOnEscape&&!c.isDefaultPrevented()&&c.keyCode&&c.keyCode===a.ui.keyCode.ESCAPE&&(b.close(c),c.preventDefault())}),a(window).bind("resize.dialog-overlay",a.ui.dialog.overlay.resize));var c=(this.oldInstances.pop()||a("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});a.fn.bgiframe&&c.bgiframe(),this.instances.push(c);return c},destroy:function(b){var c=a.inArray(b,this.instances);c!=-1&&this.oldInstances.push(this.instances.splice(c,1)[0]),this.instances.length===0&&a([document,window]).unbind(".dialog-overlay"),b.remove();var d=0;a.each(this.instances,function(){d=Math.max(d,this.css("z-index"))}),this.maxZ=d},height:function(){var b,c;if(a.browser.msie&&a.browser.version<7){b=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),c=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return b<c?a(window).height()+"px":b+"px"}return a(document).height()+"px"},width:function(){var b,c;if(a.browser.msie){b=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth),c=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);return b<c?a(window).width()+"px":b+"px"}return a(document).width()+"px"},resize:function(){var b=a([]);a.each(a.ui.dialog.overlay.instances,function(){b=b.add(this)}),b.css({width:0,height:0}).css({width:a.ui.dialog.overlay.width(),height:a.ui.dialog.overlay.height()})}}),a.extend(a.ui.dialog.overlay.prototype,{destroy:function(){a.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);/*
+ * jQuery UI Slider 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){var c=5;a.widget("ui.slider",a.ui.mouse,{widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null},_create:function(){var b=this,d=this.options,e=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),f="<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",g=d.values&&d.values.length||1,h=[];this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"+(d.disabled?" ui-slider-disabled ui-disabled":"")),this.range=a([]),d.range&&(d.range===!0&&(d.values||(d.values=[this._valueMin(),this._valueMin()]),d.values.length&&d.values.length!==2&&(d.values=[d.values[0],d.values[0]])),this.range=a("<div></div>").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(d.range==="min"||d.range==="max"?" ui-slider-range-"+d.range:"")));for(var i=e.length;i<g;i+=1)h.push(f);this.handles=e.add(a(h.join("")).appendTo(b.element)),this.handle=this.handles.eq(0),this.handles.add(this.range).filter("a").click(function(a){a.preventDefault()}).hover(function(){d.disabled||a(this).addClass("ui-state-hover")},function(){a(this).removeClass("ui-state-hover")}).focus(function(){d.disabled?a(this).blur():(a(".ui-slider .ui-state-focus").removeClass("ui-state-focus"),a(this).addClass("ui-state-focus"))}).blur(function(){a(this).removeClass("ui-state-focus")}),this.handles.each(function(b){a(this).data("index.ui-slider-handle",b)}),this.handles.keydown(function(d){var e=a(this).data("index.ui-slider-handle"),f,g,h,i;if(!b.options.disabled){switch(d.keyCode){case a.ui.keyCode.HOME:case a.ui.keyCode.END:case a.ui.keyCode.PAGE_UP:case a.ui.keyCode.PAGE_DOWN:case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:d.preventDefault();if(!b._keySliding){b._keySliding=!0,a(this).addClass("ui-state-active"),f=b._start(d,e);if(f===!1)return}}i=b.options.step,b.options.values&&b.options.values.length?g=h=b.values(e):g=h=b.value();switch(d.keyCode){case a.ui.keyCode.HOME:h=b._valueMin();break;case a.ui.keyCode.END:h=b._valueMax();break;case a.ui.keyCode.PAGE_UP:h=b._trimAlignValue(g+(b._valueMax()-b._valueMin())/c);break;case a.ui.keyCode.PAGE_DOWN:h=b._trimAlignValue(g-(b._valueMax()-b._valueMin())/c);break;case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:if(g===b._valueMax())return;h=b._trimAlignValue(g+i);break;case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:if(g===b._valueMin())return;h=b._trimAlignValue(g-i)}b._slide(d,e,h)}}).keyup(function(c){var d=a(this).data("index.ui-slider-handle");b._keySliding&&(b._keySliding=!1,b._stop(c,d),b._change(c,d),a(this).removeClass("ui-state-active"))}),this._refreshValue(),this._animateOff=!1},destroy:function(){this.handles.remove(),this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider"),this._mouseDestroy();return this},_mouseCapture:function(b){var c=this.options,d,e,f,g,h,i,j,k,l;if(c.disabled)return!1;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),d={x:b.pageX,y:b.pageY},e=this._normValueFromMouse(d),f=this._valueMax()-this._valueMin()+1,h=this,this.handles.each(function(b){var c=Math.abs(e-h.values(b));f>c&&(f=c,g=a(this),i=b)}),c.range===!0&&this.values(1)===c.min&&(i+=1,g=a(this.handles[i])),j=this._start(b,i);if(j===!1)return!1;this._mouseSliding=!0,h._handleIndex=i,g.addClass("ui-state-active").focus(),k=g.offset(),l=!a(b.target).parents().andSelf().is(".ui-slider-handle"),this._clickOffset=l?{left:0,top:0}:{left:b.pageX-k.left-g.width()/2,top:b.pageY-k.top-g.height()/2-(parseInt(g.css("borderTopWidth"),10)||0)-(parseInt(g.css("borderBottomWidth"),10)||0)+(parseInt(g.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(b,i,e),this._animateOff=!0;return!0},_mouseStart:function(a){return!0},_mouseDrag:function(a){var b={x:a.pageX,y:a.pageY},c=this._normValueFromMouse(b);this._slide(a,this._handleIndex,c);return!1},_mouseStop:function(a){this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(a,this._handleIndex),this._change(a,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1;return!1},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b,c,d,e,f;this.orientation==="horizontal"?(b=this.elementSize.width,c=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(b=this.elementSize.height,c=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),d=c/b,d>1&&(d=1),d<0&&(d=0),this.orientation==="vertical"&&(d=1-d),e=this._valueMax()-this._valueMin(),f=this._valueMin()+d*e;return this._trimAlignValue(f)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values());return this._trigger("start",a,c)},_slide:function(a,b,c){var d,e,f;this.options.values&&this.options.values.length?(d=this.values(b?0:1),this.options.values.length===2&&this.options.range===!0&&(b===0&&c>d||b===1&&c<d)&&(c=d),c!==this.values(b)&&(e=this.values(),e[b]=c,f=this._trigger("slide",a,{handle:this.handles[b],value:c,values:e}),d=this.values(b?0:1),f!==!1&&this.values(b,c,!0))):c!==this.value()&&(f=this._trigger("slide",a,{handle:this.handles[b],value:c}),f!==!1&&this.value(c))},_stop:function(a,b){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values()),this._trigger("stop",a,c)},_change:function(a,b){if(!this._keySliding&&!this._mouseSliding){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values()),this._trigger("change",a,c)}},value:function(a){if(arguments.length)this.options.value=this._trimAlignValue(a),this._refreshValue(),this._change(null,0);else return this._value()},values:function(b,c){var d,e,f;if(arguments.length>1)this.options.values[b]=this._trimAlignValue(c),this._refreshValue(),this._change(null,b);else{if(!arguments.length)return this._values();if(!a.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(b):this.value();d=this.options.values,e=arguments[0];for(f=0;f<d.length;f+=1)d[f]=this._trimAlignValue(e[f]),this._change(null,f);this._refreshValue()}},_setOption:function(b,c){var d,e=0;a.isArray(this.options.values)&&(e=this.options.values.length),a.Widget.prototype._setOption.apply(this,arguments);switch(b){case"disabled":c?(this.handles.filter(".ui-state-focus").blur(),this.handles.removeClass("ui-state-hover"),this.handles.propAttr("disabled",!0),this.element.addClass("ui-disabled")):(this.handles.propAttr("disabled",!1),this.element.removeClass("ui-disabled"));break;case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue();break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":this._animateOff=!0,this._refreshValue();for(d=0;d<e;d+=1)this._change(null,d);this._animateOff=!1}},_value:function(){var a=this.options.value;a=this._trimAlignValue(a);return a},_values:function(a){var b,c,d;if(arguments.length){b=this.options.values[a],b=this._trimAlignValue(b);return b}c=this.options.values.slice();for(d=0;d<c.length;d+=1)c[d]=this._trimAlignValue(c[d]);return c},_trimAlignValue:function(a){if(a<=this._valueMin())return this._valueMin();if(a>=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b,d=a-c;Math.abs(c)*2>=b&&(d+=c>0?b:-b);return parseFloat(d.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var b=this.options.range,c=this.options,d=this,e=this._animateOff?!1:c.animate,f,g={},h,i,j,k;this.options.values&&this.options.values.length?this.handles.each(function(b,i){f=(d.values(b)-d._valueMin())/(d._valueMax()-d._valueMin())*100,g[d.orientation==="horizontal"?"left":"bottom"]=f+"%",a(this).stop(1,1)[e?"animate":"css"](g,c.animate),d.options.range===!0&&(d.orientation==="horizontal"?(b===0&&d.range.stop(1,1)[e?"animate":"css"]({left:f+"%"},c.animate),b===1&&d.range[e?"animate":"css"]({width:f-h+"%"},{queue:!1,duration:c.animate})):(b===0&&d.range.stop(1,1)[e?"animate":"css"]({bottom:f+"%"},c.animate),b===1&&d.range[e?"animate":"css"]({height:f-h+"%"},{queue:!1,duration:c.animate}))),h=f}):(i=this.value(),j=this._valueMin(),k=this._valueMax(),f=k!==j?(i-j)/(k-j)*100:0,g[d.orientation==="horizontal"?"left":"bottom"]=f+"%",this.handle.stop(1,1)[e?"animate":"css"](g,c.animate),b==="min"&&this.orientation==="horizontal"&&this.range.stop(1,1)[e?"animate":"css"]({width:f+"%"},c.animate),b==="max"&&this.orientation==="horizontal"&&this.range[e?"animate":"css"]({width:100-f+"%"},{queue:!1,duration:c.animate}),b==="min"&&this.orientation==="vertical"&&this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},c.animate),b==="max"&&this.orientation==="vertical"&&this.range[e?"animate":"css"]({height:100-f+"%"},{queue:!1,duration:c.animate}))}}),a.extend(a.ui.slider,{version:"1.8.18"})})(jQuery);/*
+ * jQuery UI Tabs 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */(function(a,b){function f(){return++d}function e(){return++c}var c=0,d=0;a.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:!1,cookie:null,collapsible:!1,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"<div></div>",remove:null,select:null,show:null,spinner:"<em>Loading&#8230;</em>",tabTemplate:"<li><a href='#{href}'><span>#{label}</span></a></li>"},_create:function(){this._tabify(!0)},_setOption:function(a,b){if(a=="selected"){if(this.options.collapsible&&b==this.options.selected)return;this.select(b)}else this.options[a]=b,this._tabify()},_tabId:function(a){return a.title&&a.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+e()},_sanitizeSelector:function(a){return a.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+f());return a.cookie.apply(null,[b].concat(a.makeArray(arguments)))},_ui:function(a,b){return{tab:a,panel:b,index:this.anchors.index(a)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b=a(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(c){function m(b,c){b.css("display",""),!a.support.opacity&&c.opacity&&b[0].style.removeAttribute("filter")}var d=this,e=this.options,f=/^#.+/;this.list=this.element.find("ol,ul").eq(0),this.lis=a(" > li:has(a[href])",this.list),this.anchors=this.lis.map(function(){return a("a",this)[0]}),this.panels=a([]),this.anchors.each(function(b,c){var g=a(c).attr("href"),h=g.split("#")[0],i;h&&(h===location.toString().split("#")[0]||(i=a("base")[0])&&h===i.href)&&(g=c.hash,c.href=g);if(f.test(g))d.panels=d.panels.add(d.element.find(d._sanitizeSelector(g)));else if(g&&g!=="#"){a.data(c,"href.tabs",g),a.data(c,"load.tabs",g.replace(/#.*$/,""));var j=d._tabId(c);c.href="#"+j;var k=d.element.find("#"+j);k.length||(k=a(e.panelTemplate).attr("id",j).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(d.panels[b-1]||d.list),k.data("destroy.tabs",!0)),d.panels=d.panels.add(k)}else e.disabled.push(b)}),c?(this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"),this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all"),this.lis.addClass("ui-state-default ui-corner-top"),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom"),e.selected===b?(location.hash&&this.anchors.each(function(a,b){if(b.hash==location.hash){e.selected=a;return!1}}),typeof e.selected!="number"&&e.cookie&&(e.selected=parseInt(d._cookie(),10)),typeof e.selected!="number"&&this.lis.filter(".ui-tabs-selected").length&&(e.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"))),e.selected=e.selected||(this.lis.length?0:-1)):e.selected===null&&(e.selected=-1),e.selected=e.selected>=0&&this.anchors[e.selected]||e.selected<0?e.selected:0,e.disabled=a.unique(e.disabled.concat(a.map(this.lis.filter(".ui-state-disabled"),function(a,b){return d.lis.index(a)}))).sort(),a.inArray(e.selected,e.disabled)!=-1&&e.disabled.splice(a.inArray(e.selected,e.disabled),1),this.panels.addClass("ui-tabs-hide"),this.lis.removeClass("ui-tabs-selected ui-state-active"),e.selected>=0&&this.anchors.length&&(d.element.find(d._sanitizeSelector(d.anchors[e.selected].hash)).removeClass("ui-tabs-hide"),this.lis.eq(e.selected).addClass("ui-tabs-selected ui-state-active"),d.element.queue("tabs",function(){d._trigger("show",null,d._ui(d.anchors[e.selected],d.element.find(d._sanitizeSelector(d.anchors[e.selected].hash))[0]))}),this.load(e.selected)),a(window).bind("unload",function(){d.lis.add(d.anchors).unbind(".tabs"),d.lis=d.anchors=d.panels=null})):e.selected=this.lis.index(this.lis.filter(".ui-tabs-selected")),this.element[e.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible"),e.cookie&&this._cookie(e.selected,e.cookie);for(var g=0,h;h=this.lis[g];g++)a(h)[a.inArray(g,e.disabled)!=-1&&!a(h).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");e.cache===!1&&this.anchors.removeData("cache.tabs"),this.lis.add(this.anchors).unbind(".tabs");if(e.event!=="mouseover"){var i=function(a,b){b.is(":not(.ui-state-disabled)")&&b.addClass("ui-state-"+a)},j=function(a,b){b.removeClass("ui-state-"+a)};this.lis.bind("mouseover.tabs",function(){i("hover",a(this))}),this.lis.bind("mouseout.tabs",function(){j("hover",a(this))}),this.anchors.bind("focus.tabs",function(){i("focus",a(this).closest("li"))}),this.anchors.bind("blur.tabs",function(){j("focus",a(this).closest("li"))})}var k,l;e.fx&&(a.isArray(e.fx)?(k=e.fx[0],l=e.fx[1]):k=l=e.fx);var n=l?function(b,c){a(b).closest("li").addClass("ui-tabs-selected ui-state-active"),c.hide().removeClass("ui-tabs-hide").animate(l,l.duration||"normal",function(){m(c,l),d._trigger("show",null,d._ui(b,c[0]))})}:function(b,c){a(b).closest("li").addClass("ui-tabs-selected ui-state-active"),c.removeClass("ui-tabs-hide"),d._trigger("show",null,d._ui(b,c[0]))},o=k?function(a,b){b.animate(k,k.duration||"normal",function(){d.lis.removeClass("ui-tabs-selected ui-state-active"),b.addClass("ui-tabs-hide"),m(b,k),d.element.dequeue("tabs")})}:function(a,b,c){d.lis.removeClass("ui-tabs-selected ui-state-active"),b.addClass("ui-tabs-hide"),d.element.dequeue("tabs")};this.anchors.bind(e.event+".tabs",function(){var b=this,c=a(b).closest("li"),f=d.panels.filter(":not(.ui-tabs-hide)"),g=d.element.find(d._sanitizeSelector(b.hash));if(c.hasClass("ui-tabs-selected")&&!e.collapsible||c.hasClass("ui-state-disabled")||c.hasClass("ui-state-processing")||d.panels.filter(":animated").length||d._trigger("select",null,d._ui(this,g[0]))===!1){this.blur();return!1}e.selected=d.anchors.index(this),d.abort();if(e.collapsible){if(c.hasClass("ui-tabs-selected")){e.selected=-1,e.cookie&&d._cookie(e.selected,e.cookie),d.element.queue("tabs",function(){o(b,f)}).dequeue("tabs"),this.blur();return!1}if(!f.length){e.cookie&&d._cookie(e.selected,e.cookie),d.element.queue("tabs",function(){n(b,g)}),d.load(d.anchors.index(this)),this.blur();return!1}}e.cookie&&d._cookie(e.selected,e.cookie);if(g.length)f.length&&d.element.queue("tabs",function(){o(b,f)}),d.element.queue("tabs",function(){n(b,g)}),d.load(d.anchors.index(this));else throw"jQuery UI Tabs: Mismatching fragment identifier.";a.browser.msie&&this.blur()}),this.anchors.bind("click.tabs",function(){return!1})},_getIndex:function(a){typeof a=="string"&&(a=this.anchors.index(this.anchors.filter("[href$="+a+"]")));return a},destroy:function(){var b=this.options;this.abort(),this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs"),this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all"),this.anchors.each(function(){var b=a.data(this,"href.tabs");b&&(this.href=b);var c=a(this).unbind(".tabs");a.each(["href","load","cache"],function(a,b){c.removeData(b+".tabs")})}),this.lis.unbind(".tabs").add(this.panels).each(function(){a.data(this,"destroy.tabs")?a(this).remove():a(this).removeClass(["ui-state-default","ui-corner-top","ui-tabs-selected","ui-state-active","ui-state-hover","ui-state-focus","ui-state-disabled","ui-tabs-panel","ui-widget-content","ui-corner-bottom","ui-tabs-hide"].join(" "))}),b.cookie&&this._cookie(null,b.cookie);return this},add:function(c,d,e){e===b&&(e=this.anchors.length);var f=this,g=this.options,h=a(g.tabTemplate.replace(/#\{href\}/g,c).replace(/#\{label\}/g,d)),i=c.indexOf("#")?this._tabId(a("a",h)[0]):c.replace("#","");h.addClass("ui-state-default ui-corner-top").data("destroy.tabs",!0);var j=f.element.find("#"+i);j.length||(j=a(g.panelTemplate).attr("id",i).data("destroy.tabs",!0)),j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide"),e>=this.lis.length?(h.appendTo(this.list),j.appendTo(this.list[0].parentNode)):(h.insertBefore(this.lis[e]),j.insertBefore(this.panels[e])),g.disabled=a.map(g.disabled,function(a,b){return a>=e?++a:a}),this._tabify(),this.anchors.length==1&&(g.selected=0,h.addClass("ui-tabs-selected ui-state-active"),j.removeClass("ui-tabs-hide"),this.element.queue("tabs",function(){f._trigger("show",null,f._ui(f.anchors[0],f.panels[0]))}),this.load(0)),this._trigger("add",null,this._ui(this.anchors[e],this.panels[e]));return this},remove:function(b){b=this._getIndex(b);var c=this.options,d=this.lis.eq(b).remove(),e=this.panels.eq(b).remove();d.hasClass("ui-tabs-selected")&&this.anchors.length>1&&this.select(b+(b+1<this.anchors.length?1:-1)),c.disabled=a.map(a.grep(c.disabled,function(a,c){return a!=b}),function(a,c){return a>=b?--a:a}),this._tabify(),this._trigger("remove",null,this._ui(d.find("a")[0],e[0]));return this},enable:function(b){b=this._getIndex(b);var c=this.options;if(a.inArray(b,c.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled"),c.disabled=a.grep(c.disabled,function(a,c){return a!=b}),this._trigger("enable",null,this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(a){a=this._getIndex(a);var b=this,c=this.options;a!=c.selected&&(this.lis.eq(a).addClass("ui-state-disabled"),c.disabled.push(a),c.disabled.sort(),this._trigger("disable",null,this._ui(this.anchors[a],this.panels[a])));return this},select:function(a){a=this._getIndex(a);if(a==-1)if(this.options.collapsible&&this.options.selected!=-1)a=this.options.selected;else return this;this.anchors.eq(a).trigger(this.options.event+".tabs");return this},load:function(b){b=this._getIndex(b);var c=this,d=this.options,e=this.anchors.eq(b)[0],f=a.data(e,"load.tabs");this.abort();if(!f||this.element.queue("tabs").length!==0&&a.data(e,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(d.spinner){var g=a("span",e);g.data("label.tabs",g.html()).html(d.spinner)}this.xhr=a.ajax(a.extend({},d.ajaxOptions,{url:f,success:function(f,g){c.element.find(c._sanitizeSelector(e.hash)).html(f),c._cleanup(),d.cache&&a.data(e,"cache.tabs",!0),c._trigger("load",null,c._ui(c.anchors[b],c.panels[b]));try{d.ajaxOptions.success(f,g)}catch(h){}},error:function(a,f,g){c._cleanup(),c._trigger("load",null,c._ui(c.anchors[b],c.panels[b]));try{d.ajaxOptions.error(a,f,b,e)}catch(g){}}})),c.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]),this.panels.stop(!1,!0),this.element.queue("tabs",this.element.queue("tabs").splice(-2,2)),this.xhr&&(this.xhr.abort(),delete this.xhr),this._cleanup();return this},url:function(a,b){this.anchors.eq(a).removeData("cache.tabs").data("load.tabs",b);return this},length:function(){return this.anchors.length}}),a.extend(a.ui.tabs,{version:"1.8.18"}),a.extend(a.ui.tabs.prototype,{rotation:null,rotate:function(a,b){var c=this,d=this.options,e=c._rotate||(c._rotate=function(b){clearTimeout(c.rotation),c.rotation=setTimeout(function(){var a=d.selected;c.select(++a<c.anchors.length?a:0)},a),b&&b.stopPropagation()}),f=c._unrotate||(c._unrotate=b?function(a){t=d.selected,e()}:function(a){a.clientX&&c.rotate(null)});a?(this.element.bind("tabsshow",e),this.anchors.bind(d.event+".tabs",f),e()):(clearTimeout(c.rotation),this.element.unbind("tabsshow",e),this.anchors.unbind(d.event+".tabs",f),delete this._rotate,delete this._unrotate);return this}})})(jQuery);/*
+ * jQuery UI Datepicker 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker
+ *
+ * Depends:
+ * jquery.ui.core.js
+ */(function($,undefined){function isArray(a){return a&&($.browser.safari&&typeof a=="object"&&a.length||a.constructor&&a.constructor.toString().match(/\Array\(\)/))}function extendRemove(a,b){$.extend(a,b);for(var c in b)if(b[c]==null||b[c]==undefined)a[c]=b[c];return a}function bindHover(a){var b="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return a.bind("mouseout",function(a){var c=$(a.target).closest(b);!c.length||c.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(c){var d=$(c.target).closest(b);!$.datepicker._isDisabledDatepicker(instActive.inline?a.parent()[0]:instActive.input[0])&&!!d.length&&(d.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),d.addClass("ui-state-hover"),d.hasClass("ui-datepicker-prev")&&d.addClass("ui-datepicker-prev-hover"),d.hasClass("ui-datepicker-next")&&d.addClass("ui-datepicker-next-hover"))})}function Datepicker(){this.debug=!1,this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},$.extend(this._defaults,this.regional[""]),this.dpDiv=bindHover($('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}$.extend($.ui,{datepicker:{version:"1.8.18"}});var PROP_NAME="datepicker",dpuuid=(new Date).getTime(),instActive;$.extend(Datepicker.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){extendRemove(this._defaults,a||{});return this},_attachDatepicker:function(target,settings){var inlineSettings=null;for(var attrName in this._defaults){var attrValue=target.getAttribute("date:"+attrName);if(attrValue){inlineSettings=inlineSettings||{};try{inlineSettings[attrName]=eval(attrValue)}catch(err){inlineSettings[attrName]=attrValue}}}var nodeName=target.nodeName.toLowerCase(),inline=nodeName=="div"||nodeName=="span";target.id||(this.uuid+=1,target.id="dp"+this.uuid);var inst=this._newInst($(target),inline);inst.settings=$.extend({},settings||{},inlineSettings||{}),nodeName=="input"?this._connectDatepicker(target,inst):inline&&this._inlineDatepicker(target,inst)},_newInst:function(a,b){var c=a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1");return{id:c,input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:b?bindHover($('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')):this.dpDiv}},_connectDatepicker:function(a,b){var c=$(a);b.append=$([]),b.trigger=$([]);c.hasClass(this.markerClassName)||(this._attachments(c,b),c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),this._autoSize(b),$.data(a,PROP_NAME,b),b.settings.disabled&&this._disableDatepicker(a))},_attachments:function(a,b){var c=this._get(b,"appendText"),d=this._get(b,"isRTL");b.append&&b.append.remove(),c&&(b.append=$('<span class="'+this._appendClass+'">'+c+"</span>"),a[d?"before":"after"](b.append)),a.unbind("focus",this._showDatepicker),b.trigger&&b.trigger.remove();var e=this._get(b,"showOn");(e=="focus"||e=="both")&&a.focus(this._showDatepicker);if(e=="button"||e=="both"){var f=this._get(b,"buttonText"),g=this._get(b,"buttonImage");b.trigger=$(this._get(b,"buttonImageOnly")?$("<img/>").addClass(this._triggerClass).attr({src:g,alt:f,title:f}):$('<button type="button"></button>').addClass(this._triggerClass).html(g==""?f:$("<img/>").attr({src:g,alt:f,title:f}))),a[d?"before":"after"](b.trigger),b.trigger.click(function(){$.datepicker._datepickerShowing&&$.datepicker._lastInput==a[0]?$.datepicker._hideDatepicker():$.datepicker._datepickerShowing&&$.datepicker._lastInput!=a[0]?($.datepicker._hideDatepicker(),$.datepicker._showDatepicker(a[0])):$.datepicker._showDatepicker(a[0]);return!1})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var d=function(a){var b=0,c=0;for(var d=0;d<a.length;d++)a[d].length>b&&(b=a[d].length,c=d);return c};b.setMonth(d(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort"))),b.setDate(d(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=$(a);c.hasClass(this.markerClassName)||(c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),$.data(a,PROP_NAME,b),this._setDate(b,this._getDefaultDate(b),!0),this._updateDatepicker(b),this._updateAlternate(b),b.settings.disabled&&this._disableDatepicker(a),b.dpDiv.css("display","block"))},_dialogDatepicker:function(a,b,c,d,e){var f=this._dialogInst;if(!f){this.uuid+=1;var g="dp"+this.uuid;this._dialogInput=$('<input type="text" id="'+g+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>'),this._dialogInput.keydown(this._doKeyDown),$("body").append(this._dialogInput),f=this._dialogInst=this._newInst(this._dialogInput,!1),f.settings={},$.data(this._dialogInput[0],PROP_NAME,f)}extendRemove(f.settings,d||{}),b=b&&b.constructor==Date?this._formatDate(f,b):b,this._dialogInput.val(b),this._pos=e?e.length?e:[e.pageX,e.pageY]:null;if(!this._pos){var h=document.documentElement.clientWidth,i=document.documentElement.clientHeight,j=document.documentElement.scrollLeft||document.body.scrollLeft,k=document.documentElement.scrollTop||document.body.scrollTop;this._pos=[h/2-100+j,i/2-150+k]}this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),f.settings.onSelect=c,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),$.blockUI&&$.blockUI(this.dpDiv),$.data(this._dialogInput[0],PROP_NAME,f);return this},_destroyDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!!b.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();$.removeData(a,PROP_NAME),d=="input"?(c.append.remove(),c.trigger.remove(),b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):(d=="div"||d=="span")&&b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!!b.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!1,c.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().removeClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b})}},_disableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!!b.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!0,c.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().addClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b}),this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return!1;for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]==a)return!0;return!1},_getInst:function(a){try{return $.data(a,PROP_NAME)}catch(b){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(a,b,c){var d=this._getInst(a);if(arguments.length==2&&typeof b=="string")return b=="defaults"?$.extend({},$.datepicker._defaults):d?b=="all"?$.extend({},d.settings):this._get(d,b):null;var e=b||{};typeof b=="string"&&(e={},e[b]=c);if(d){this._curInst==d&&this._hideDatepicker();var f=this._getDateDatepicker(a,!0),g=this._getMinMaxDate(d,"min"),h=this._getMinMaxDate(d,"max");extendRemove(d.settings,e),g!==null&&e.dateFormat!==undefined&&e.minDate===undefined&&(d.settings.minDate=this._formatDate(d,g)),h!==null&&e.dateFormat!==undefined&&e.maxDate===undefined&&(d.settings.maxDate=this._formatDate(d,h)),this._attachments($(a),d),this._autoSize(d),this._setDate(d,f),this._updateAlternate(d),this._updateDatepicker(d)}},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){var b=this._getInst(a);b&&this._updateDatepicker(b)},_setDateDatepicker:function(a,b){var c=this._getInst(a);c&&(this._setDate(c,b),this._updateDatepicker(c),this._updateAlternate(c))},_getDateDatepicker:function(a,b){var c=this._getInst(a);c&&!c.inline&&this._setDateFromField(c,b);return c?this._getDate(c):null},_doKeyDown:function(a){var b=$.datepicker._getInst(a.target),c=!0,d=b.dpDiv.is(".ui-datepicker-rtl");b._keyEvent=!0;if($.datepicker._datepickerShowing)switch(a.keyCode){case 9:$.datepicker._hideDatepicker(),c=!1;break;case 13:var e=$("td."+$.datepicker._dayOverClass+":not(."+$.datepicker._currentClass+")",b.dpDiv);e[0]&&$.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,e[0]);var f=$.datepicker._get(b,"onSelect");if(f){var g=$.datepicker._formatDate(b);f.apply(b.input?b.input[0]:null,[g,b])}else $.datepicker._hideDatepicker();return!1;case 27:$.datepicker._hideDatepicker();break;case 33:$.datepicker._adjustDate(a.target,a.ctrlKey?-$.datepicker._get(b,"stepBigMonths"):-$.datepicker._get(b,"stepMonths"),"M");break;case 34:$.datepicker._adjustDate(a.target,a.ctrlKey?+$.datepicker._get(b,"stepBigMonths"):+$.datepicker._get(b,"stepMonths"),"M");break;case 35:(a.ctrlKey||a.metaKey)&&$.datepicker._clearDate(a.target),c=a.ctrlKey||a.metaKey;break;case 36:(a.ctrlKey||a.metaKey)&&$.datepicker._gotoToday(a.target),c=a.ctrlKey||a.metaKey;break;case 37:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,d?1:-1,"D"),c=a.ctrlKey||a.metaKey,a.originalEvent.altKey&&$.datepicker._adjustDate(a.target,a.ctrlKey?-$.datepicker._get(b,"stepBigMonths"):-$.datepicker._get(b,"stepMonths"),"M");break;case 38:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,-7,"D"),c=a.ctrlKey||a.metaKey;break;case 39:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,d?-1:1,"D"),c=a.ctrlKey||a.metaKey,a.originalEvent.altKey&&$.datepicker._adjustDate(a.target,a.ctrlKey?+$.datepicker._get(b,"stepBigMonths"):+$.datepicker._get(b,"stepMonths"),"M");break;case 40:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,7,"D"),c=a.ctrlKey||a.metaKey;break;default:c=!1}else a.keyCode==36&&a.ctrlKey?$.datepicker._showDatepicker(this):c=!1;c&&(a.preventDefault(),a.stopPropagation())},_doKeyPress:function(a){var b=$.datepicker._getInst(a.target);if($.datepicker._get(b,"constrainInput")){var c=$.datepicker._possibleChars($.datepicker._get(b,"dateFormat")),d=String.fromCharCode(a.charCode==undefined?a.keyCode:a.charCode);return a.ctrlKey||a.metaKey||d<" "||!c||c.indexOf(d)>-1}},_doKeyUp:function(a){var b=$.datepicker._getInst(a.target);if(b.input.val()!=b.lastVal)try{var c=$.datepicker.parseDate($.datepicker._get(b,"dateFormat"),b.input?b.input.val():null,$.datepicker._getFormatConfig(b));c&&($.datepicker._setDateFromField(b),$.datepicker._updateAlternate(b),$.datepicker._updateDatepicker(b))}catch(a){$.datepicker.log(a)}return!0},_showDatepicker:function(a){a=a.target||a,a.nodeName.toLowerCase()!="input"&&(a=$("input",a.parentNode)[0]);if(!$.datepicker._isDisabledDatepicker(a)&&$.datepicker._lastInput!=a){var b=$.datepicker._getInst(a);$.datepicker._curInst&&$.datepicker._curInst!=b&&($.datepicker._curInst.dpDiv.stop(!0,!0),b&&$.datepicker._datepickerShowing&&$.datepicker._hideDatepicker($.datepicker._curInst.input[0]));var c=$.datepicker._get(b,"beforeShow"),d=c?c.apply(a,[a,b]):{};if(d===!1)return;extendRemove(b.settings,d),b.lastVal=null,$.datepicker._lastInput=a,$.datepicker._setDateFromField(b),$.datepicker._inDialog&&(a.value=""),$.datepicker._pos||($.datepicker._pos=$.datepicker._findPos(a),$.datepicker._pos[1]+=a.offsetHeight);var e=!1;$(a).parents().each(function(){e|=$(this).css("position")=="fixed";return!e}),e&&$.browser.opera&&($.datepicker._pos[0]-=document.documentElement.scrollLeft,$.datepicker._pos[1]-=document.documentElement.scrollTop);var f={left:$.datepicker._pos[0],top:$.datepicker._pos[1]};$.datepicker._pos=null,b.dpDiv.empty(),b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),$.datepicker._updateDatepicker(b),f=$.datepicker._checkOffset(b,f,e),b.dpDiv.css({position:$.datepicker._inDialog&&$.blockUI?"static":e?"fixed":"absolute",display:"none",left:f.left+"px",top:f.top+"px"});if(!b.inline){var g=$.datepicker._get(b,"showAnim"),h=$.datepicker._get(b,"duration"),i=function(){var a=b.dpDiv.find("iframe.ui-datepicker-cover");if(!!a.length){var c=$.datepicker._getBorders(b.dpDiv);a.css({left:-c[0],top:-c[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex($(a).zIndex()+1),$.datepicker._datepickerShowing=!0,$.effects&&$.effects[g]?b.dpDiv.show(g,$.datepicker._get(b,"showOptions"),h,i):b.dpDiv[g||"show"](g?h:null,i),(!g||!h)&&i(),b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus(),$.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=this;b.maxRows=4;var c=$.datepicker._getBorders(a.dpDiv);instActive=a,a.dpDiv.empty().append(this._generateHTML(a));var d=a.dpDiv.find("iframe.ui-datepicker-cover");!d.length||d.css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}),a.dpDiv.find("."+this._dayOverClass+" a").mouseover();var e=this._getNumberOfMonths(a),f=e[1],g=17;a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),f>1&&a.dpDiv.addClass("ui-datepicker-multi-"+f).css("width",g*f+"em"),a.dpDiv[(e[0]!=1||e[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi"),a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),a==$.datepicker._curInst&&$.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var h=a.yearshtml;setTimeout(function(){h===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml),h=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(a){return{thin:1,medium:2,thick:3}[a]||a};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var d=a.dpDiv.outerWidth(),e=a.dpDiv.outerHeight(),f=a.input?a.input.outerWidth():0,g=a.input?a.input.outerHeight():0,h=document.documentElement.clientWidth+$(document).scrollLeft(),i=document.documentElement.clientHeight+$(document).scrollTop();b.left-=this._get(a,"isRTL")?d-f:0,b.left-=c&&b.left==a.input.offset().left?$(document).scrollLeft():0,b.top-=c&&b.top==a.input.offset().top+g?$(document).scrollTop():0,b.left-=Math.min(b.left,b.left+d>h&&h>d?Math.abs(b.left+d-h):0),b.top-=Math.min(b.top,b.top+e>i&&i>e?Math.abs(e+g):0);return b},_findPos:function(a){var b=this._getInst(a),c=this._get(b,"isRTL");while(a&&(a.type=="hidden"||a.nodeType!=1||$.expr.filters.hidden(a)))a=a[c?"previousSibling":"nextSibling"];var d=$(a).offset();return[d.left,d.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=$.data(a,PROP_NAME))&&this._datepickerShowing){var c=this._get(b,"showAnim"),d=this._get(b,"duration"),e=this,f=function(){$.datepicker._tidyDialog(b),e._curInst=null};$.effects&&$.effects[c]?b.dpDiv.hide(c,$.datepicker._get(b,"showOptions"),d,f):b.dpDiv[c=="slideDown"?"slideUp":c=="fadeIn"?"fadeOut":"hide"](c?d:null,f),c||f(),this._datepickerShowing=!1;var g=this._get(b,"onClose");g&&g.apply(b.input?b.input[0]:null,[b.input?b.input.val():"",b]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),$.blockUI&&($.unblockUI(),$("body").append(this.dpDiv))),this._inDialog=!1}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(!!$.datepicker._curInst){var b=$(a.target),c=$.datepicker._getInst(b[0]);(b[0].id!=$.datepicker._mainDivId&&b.parents("#"+$.datepicker._mainDivId).length==0&&!b.hasClass($.datepicker.markerClassName)&&!b.closest("."+$.datepicker._triggerClass).length&&$.datepicker._datepickerShowing&&(!$.datepicker._inDialog||!$.blockUI)||b.hasClass($.datepicker.markerClassName)&&$.datepicker._curInst!=c)&&$.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){var d=$(a),e=this._getInst(d[0]);this._isDisabledDatepicker(d[0])||(this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c),this._updateDatepicker(e))},_gotoToday:function(a){var b=$(a),c=this._getInst(b[0]);if(this._get(c,"gotoCurrent")&&c.currentDay)c.selectedDay=c.currentDay,c.drawMonth=c.selectedMonth=c.currentMonth,c.drawYear=c.selectedYear=c.currentYear;else{var d=new Date;c.selectedDay=d.getDate(),c.drawMonth=c.selectedMonth=d.getMonth(),c.drawYear=c.selectedYear=d.getFullYear()}this._notifyChange(c),this._adjustDate(b)},_selectMonthYear:function(a,b,c){var d=$(a),e=this._getInst(d[0]);e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10),this._notifyChange(e),this._adjustDate(d)},_selectDay:function(a,b,c,d){var e=$(a);if(!$(d).hasClass(this._unselectableClass)&&!this._isDisabledDatepicker(e[0])){var f=this._getInst(e[0]);f.selectedDay=f.currentDay=$("a",d).html(),f.selectedMonth=f.currentMonth=b,f.selectedYear=f.currentYear=c,this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){var b=$(a),c=this._getInst(b[0]);this._selectDate(b,"")},_selectDate:function(a,b){var c=$(a),d=this._getInst(c[0]);b=b!=null?b:this._formatDate(d),d.input&&d.input.val(b),this._updateAlternate(d);var e=this._get(d,"onSelect");e?e.apply(d.input?d.input[0]:null,[b,d]):d.input&&d.input.trigger("change"),d.inline?this._updateDatepicker(d):(this._hideDatepicker(),this._lastInput=d.input[0],typeof d.input[0]!="object"&&d.input.focus(),this._lastInput=null)},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),d=this._getDate(a),e=this.formatDate(c,d,this._getFormatConfig(a));$(b).each(function(){$(this).val(e)})}},noWeekends:function(a){var b=a.getDay();return[b>0&&b<6,""]},iso8601Week:function(a){var b=new Date(a.getTime());b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();b.setMonth(0),b.setDate(1);return Math.floor(Math.round((c-b)/864e5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var d=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;d=typeof d!="string"?d:(new Date).getFullYear()%100+parseInt(d,10);var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,g=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,h=(c?c.monthNames:null)||this._defaults.monthNames,i=-1,j=-1,k=-1,l=-1,m=!1,n=function(b){var c=s+1<a.length&&a.charAt(s+1)==b;c&&s++;return c},o=function(a){var c=n(a),d=a=="@"?14:a=="!"?20:a=="y"&&c?4:a=="o"?3:2,e=new RegExp("^\\d{1,"+d+"}"),f=b.substring(r).match(e);if(!f)throw"Missing number at position "+r;r+=f[0].length;return parseInt(f[0],10)},p=function(a,c,d){var e=$.map(n(a)?d:c,function(a,b){return[[b,a]]}).sort(function(a,b){return-(a[1].length-b[1].length)}),f=-1;$.each(e,function(a,c){var d=c[1];if(b.substr(r,d.length).toLowerCase()==d.toLowerCase()){f=c[0],r+=d.length;return!1}});if(f!=-1)return f+1;throw"Unknown name at position "+r},q=function(){if(b.charAt(r)!=a.charAt(s))throw"Unexpected literal at position "+r;r++},r=0;for(var s=0;s<a.length;s++)if(m)a.charAt(s)=="'"&&!n("'")?m=!1:q();else switch(a.charAt(s)){case"d":k=o("d");break;case"D":p("D",e,f);break;case"o":l=o("o");break;case"m":j=o("m");break;case"M":j=p("M",g,h);break;case"y":i=o("y");break;case"@":var t=new Date(o("@"));i=t.getFullYear(),j=t.getMonth()+1,k=t.getDate();break;case"!":var t=new Date((o("!")-this._ticksTo1970)/1e4);i=t.getFullYear(),j=t.getMonth()+1,k=t.getDate();break;case"'":n("'")?q():m=!0;break;default:q()}if(r<b.length)throw"Extra/unparsed characters found in date: "+b.substring(r);i==-1?i=(new Date).getFullYear():i<100&&(i+=(new Date).getFullYear()-(new Date).getFullYear()%100+(i<=d?0:-100));if(l>-1){j=1,k=l;for(;;){var u=this._getDaysInMonth(i,j-1);if(k<=u)break;j++,k-=u}}var t=this._daylightSavingAdjust(new Date(i,j-1,k));if(t.getFullYear()!=i||t.getMonth()+1!=j||t.getDate()!=k)throw"Invalid date";return t},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1e7,formatDate:function(a,b,c){if(!b)return"";var d=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,e=(c?c.dayNames:null)||this._defaults.dayNames,f=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,h=function(b){var c=m+1<a.length&&a.charAt(m+1)==b;c&&m++;return c},i=function(a,b,c){var d=""+b;if(h(a))while(d.length<c)d="0"+d;return d},j=function(a,b,c,d){return h(a)?d[b]:c[b]},k="",l=!1;if(b)for(var m=0;m<a.length;m++)if(l)a.charAt(m)=="'"&&!h("'")?l=!1:k+=a.charAt(m);else switch(a.charAt(m)){case"d":k+=i("d",b.getDate(),2);break;case"D":k+=j("D",b.getDay(),d,e);break;case"o":k+=i("o",Math.round(((new Date(b.getFullYear(),b.getMonth(),b.getDate())).getTime()-(new Date(b.getFullYear(),0,0)).getTime())/864e5),3);break;case"m":k+=i("m",b.getMonth()+1,2);break;case"M":k+=j("M",b.getMonth(),f,g);break;case"y":k+=h("y")?b.getFullYear():(b.getYear()%100<10?"0":"")+b.getYear()%100;break;case"@":k+=b.getTime();break;case"!":k+=b.getTime()*1e4+this._ticksTo1970;break;case"'":h("'")?k+="'":l=!0;break;default:k+=a.charAt(m)}return k},_possibleChars:function(a){var b="",c=!1,d=function(b){var c=e+1<a.length&&a.charAt(e+1)==b;c&&e++;return c};for(var e=0;e<a.length;e++)if(c)a.charAt(e)=="'"&&!d("'")?c=!1:b+=a.charAt(e);else switch(a.charAt(e)){case"d":case"m":case"y":case"@":b+="0123456789";break;case"D":case"M":return null;case"'":d("'")?b+="'":c=!0;break;default:b+=a.charAt(e)}return b},_get:function(a,b){return a.settings[b]!==undefined?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!=a.lastVal){var c=this._get(a,"dateFormat"),d=a.lastVal=a.input?a.input.val():null,e,f;e=f=this._getDefaultDate(a);var g=this._getFormatConfig(a);try{e=this.parseDate(c,d,g)||f}catch(h){this.log(h),d=b?"":d}a.selectedDay=e.getDate(),a.drawMonth=a.selectedMonth=e.getMonth(),a.drawYear=a.selectedYear=e.getFullYear(),a.currentDay=d?e.getDate():0,a.currentMonth=d?e.getMonth():0,a.currentYear=d?e.getFullYear():0,this._adjustInstDate(a)}},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},_determineDate:function(a,b,c){var d=function(a){var b=new Date;b.setDate(b.getDate()+a);return b},e=function(b){try{return $.datepicker.parseDate($.datepicker._get(a,"dateFormat"),b,$.datepicker._getFormatConfig(a))}catch(c){}var d=(b.toLowerCase().match(/^c/)?$.datepicker._getDate(a):null)||new Date,e=d.getFullYear(),f=d.getMonth(),g=d.getDate(),h=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,i=h.exec(b);while(i){switch(i[2]||"d"){case"d":case"D":g+=parseInt(i[1],10);break;case"w":case"W":g+=parseInt(i[1],10)*7;break;case"m":case"M":f+=parseInt(i[1],10),g=Math.min(g,$.datepicker._getDaysInMonth(e,f));break;case"y":case"Y":e+=parseInt(i[1],10),g=Math.min(g,$.datepicker._getDaysInMonth(e,f))}i=h.exec(b)}return new Date(e,f,g)},f=b==null||b===""?c:typeof b=="string"?e(b):typeof b=="number"?isNaN(b)?c:d(b):new Date(b.getTime());f=f&&f.toString()=="Invalid Date"?c:f,f&&(f.setHours(0),f.setMinutes(0),f.setSeconds(0),f.setMilliseconds(0));return this._daylightSavingAdjust(f)},_daylightSavingAdjust:function(a){if(!a)return null;a.setHours(a.getHours()>12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var d=!b,e=a.selectedMonth,f=a.selectedYear,g=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=g.getDate(),a.drawMonth=a.selectedMonth=a.currentMonth=g.getMonth(),a.drawYear=a.selectedYear=a.currentYear=g.getFullYear(),(e!=a.selectedMonth||f!=a.selectedYear)&&!c&&this._notifyChange(a),this._adjustInstDate(a),a.input&&a.input.val(d?"":this._formatDate(a))},_getDate:function(a){var b=!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return b},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),d=this._get(a,"showButtonPanel"),e=this._get(a,"hideIfNoPrevNext"),f=this._get(a,"navigationAsDateFormat"),g=this._getNumberOfMonths(a),h=this._get(a,"showCurrentAtPos"),i=this._get(a,"stepMonths"),j=g[0]!=1||g[1]!=1,k=this._daylightSavingAdjust(a.currentDay?new Date(a.currentYear,a.currentMonth,a.currentDay):new Date(9999,9,9)),l=this._getMinMaxDate(a,"min"),m=this._getMinMaxDate(a,"max"),n=a.drawMonth-h,o=a.drawYear;n<0&&(n+=12,o--);if(m){var p=this._daylightSavingAdjust(new Date(m.getFullYear(),m.getMonth()-g[0]*g[1]+1,m.getDate()));p=l&&p<l?l:p;while(this._daylightSavingAdjust(new Date(o,n,1))>p)n--,n<0&&(n=11,o--)}a.drawMonth=n,a.drawYear=o;var q=this._get(a,"prevText");q=f?this.formatDate(q,this._daylightSavingAdjust(new Date(o,n-i,1)),this._getFormatConfig(a)):q;var r=this._canAdjustMonth(a,-1,o,n)?'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._adjustDate('#"+a.id+"', -"+i+", 'M');\""+' title="'+q+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+q+"</span></a>":e?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+q+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+q+"</span></a>",s=this._get(a,"nextText");s=f?this.formatDate(s,this._daylightSavingAdjust(new Date(o,n+i,1)),this._getFormatConfig(a)):s;var t=this._canAdjustMonth(a,1,o,n)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._adjustDate('#"+a.id+"', +"+i+", 'M');\""+' title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>":e?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>",u=this._get(a,"currentText"),v=this._get(a,"gotoCurrent")&&a.currentDay?k:b;u=f?this.formatDate(u,v,this._getFormatConfig(a)):u;var w=a.inline?"":'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_'+dpuuid+'.datepicker._hideDatepicker();">'+this._get(a,"closeText")+"</button>",x=d?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(c?w:"")+(this._isInRange(a,v)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._gotoToday('#"+a.id+"');\""+">"+u+"</button>":"")+(c?"":w)+"</div>":"",y=parseInt(this._get(a,"firstDay"),10);y=isNaN(y)?0:y;var z=this._get(a,"showWeek"),A=this._get(a,"dayNames"),B=this._get(a,"dayNamesShort"),C=this._get(a,"dayNamesMin"),D=this._get(a,"monthNames"),E=this._get(a,"monthNamesShort"),F=this._get(a,"beforeShowDay"),G=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths"),I=this._get(a,"calculateWeek")||this.iso8601Week,J=this._getDefaultDate(a),K="";for(var L=0;L<g[0];L++){var M="";this.maxRows=4;for(var N=0;N<g[1];N++){var O=this._daylightSavingAdjust(new Date(o,n,a.selectedDay)),P=" ui-corner-all",Q="";if(j){Q+='<div class="ui-datepicker-group';if(g[1]>1)switch(N){case 0:Q+=" ui-datepicker-group-first",P=" ui-corner-"+(c?"right":"left");break;case g[1]-1:Q+=" ui-datepicker-group-last",P=" ui-corner-"+(c?"left":"right");break;default:Q+=" ui-datepicker-group-middle",P=""}Q+='">'}Q+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+P+'">'+(/all|left/.test(P)&&L==0?c?t:r:"")+(/all|right/.test(P)&&L==0?c?r:t:"")+this._generateMonthYearHeader(a,n,o,l,m,L>0||N>0,D,E)+'</div><table class="ui-datepicker-calendar"><thead>'+"<tr>";var R=z?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":"";for(var S=0;S<7;S++){var T=(S+y)%7;R+="<th"+((S+y+6)%7>=5?' class="ui-datepicker-week-end"':"")+">"+'<span title="'+A[T]+'">'+C[T]+"</span></th>"}Q+=R+"</tr></thead><tbody>";var U=this._getDaysInMonth(o,n);o==a.selectedYear&&n==a.selectedMonth&&(a.selectedDay=Math.min(a.selectedDay,U));var V=(this._getFirstDayOfMonth(o,n)-y+7)%7,W=Math.ceil((V+U)/7),X=j?this.maxRows>W?this.maxRows:W:W;this.maxRows=X;var Y=this._daylightSavingAdjust(new Date(o,n,1-V));for(var Z=0;Z<X;Z++){Q+="<tr>";var _=z?'<td class="ui-datepicker-week-col">'+this._get(a,"calculateWeek")(Y)+"</td>":"";for(var S=0;S<7;S++){var ba=F?F.apply(a.input?a.input[0]:null,[Y]):[!0,""],bb=Y.getMonth()!=n,bc=bb&&!H||!ba[0]||l&&Y<l||m&&Y>m;_+='<td class="'+((S+y+6)%7>=5?" ui-datepicker-week-end":"")+(bb?" ui-datepicker-other-month":"")+(Y.getTime()==O.getTime()&&n==a.selectedMonth&&a._keyEvent||J.getTime()==Y.getTime()&&J.getTime()==O.getTime()?" "+this._dayOverClass:"")+(bc?" "+this._unselectableClass+" ui-state-disabled":"")+(bb&&!G?"":" "+ba[1]+(Y.getTime()==k.getTime()?" "+this._currentClass:"")+(Y.getTime()==b.getTime()?" ui-datepicker-today":""))+'"'+((!bb||G)&&ba[2]?' title="'+ba[2]+'"':"")+(bc?"":' onclick="DP_jQuery_'+dpuuid+".datepicker._selectDay('#"+a.id+"',"+Y.getMonth()+","+Y.getFullYear()+', this);return false;"')+">"+(bb&&!G?"&#xa0;":bc?'<span class="ui-state-default">'+Y.getDate()+"</span>":'<a class="ui-state-default'+(Y.getTime()==b.getTime()?" ui-state-highlight":"")+(Y.getTime()==k.getTime()?" ui-state-active":"")+(bb?" ui-priority-secondary":"")+'" href="#">'+Y.getDate()+"</a>")+"</td>",Y.setDate(Y.getDate()+1),Y=this._daylightSavingAdjust(Y)}Q+=_+"</tr>"}n++,n>11&&(n=0,o++),Q+="</tbody></table>"+(j?"</div>"+(g[0]>0&&N==g[1]-1?'<div class="ui-datepicker-row-break"></div>':""):""),M+=Q}K+=M}K+=x+($.browser.msie&&parseInt($.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':""),
+a._keyEvent=!1;return K},_generateMonthYearHeader:function(a,b,c,d,e,f,g,h){var i=this._get(a,"changeMonth"),j=this._get(a,"changeYear"),k=this._get(a,"showMonthAfterYear"),l='<div class="ui-datepicker-title">',m="";if(f||!i)m+='<span class="ui-datepicker-month">'+g[b]+"</span>";else{var n=d&&d.getFullYear()==c,o=e&&e.getFullYear()==c;m+='<select class="ui-datepicker-month" onchange="DP_jQuery_'+dpuuid+".datepicker._selectMonthYear('#"+a.id+"', this, 'M');\" "+">";for(var p=0;p<12;p++)(!n||p>=d.getMonth())&&(!o||p<=e.getMonth())&&(m+='<option value="'+p+'"'+(p==b?' selected="selected"':"")+">"+h[p]+"</option>");m+="</select>"}k||(l+=m+(f||!i||!j?"&#xa0;":""));if(!a.yearshtml){a.yearshtml="";if(f||!j)l+='<span class="ui-datepicker-year">'+c+"</span>";else{var q=this._get(a,"yearRange").split(":"),r=(new Date).getFullYear(),s=function(a){var b=a.match(/c[+-].*/)?c+parseInt(a.substring(1),10):a.match(/[+-].*/)?r+parseInt(a,10):parseInt(a,10);return isNaN(b)?r:b},t=s(q[0]),u=Math.max(t,s(q[1]||""));t=d?Math.max(t,d.getFullYear()):t,u=e?Math.min(u,e.getFullYear()):u,a.yearshtml+='<select class="ui-datepicker-year" onchange="DP_jQuery_'+dpuuid+".datepicker._selectMonthYear('#"+a.id+"', this, 'Y');\" "+">";for(;t<=u;t++)a.yearshtml+='<option value="'+t+'"'+(t==c?' selected="selected"':"")+">"+t+"</option>";a.yearshtml+="</select>",l+=a.yearshtml,a.yearshtml=null}}l+=this._get(a,"yearSuffix"),k&&(l+=(f||!i||!j?"&#xa0;":"")+m),l+="</div>";return l},_adjustInstDate:function(a,b,c){var d=a.drawYear+(c=="Y"?b:0),e=a.drawMonth+(c=="M"?b:0),f=Math.min(a.selectedDay,this._getDaysInMonth(d,e))+(c=="D"?b:0),g=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(d,e,f)));a.selectedDay=g.getDate(),a.drawMonth=a.selectedMonth=g.getMonth(),a.drawYear=a.selectedYear=g.getFullYear(),(c=="M"||c=="Y")&&this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max"),e=c&&b<c?c:b;e=d&&e>d?d:e;return e},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");b&&b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){var b=this._get(a,"numberOfMonths");return b==null?[1,1]:typeof b=="number"?[1,b]:b},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,d){var e=this._getNumberOfMonths(a),f=this._daylightSavingAdjust(new Date(c,d+(b<0?b:e[0]*e[1]),1));b<0&&f.setDate(this._getDaysInMonth(f.getFullYear(),f.getMonth()));return this._isInRange(a,f)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!d||b.getTime()<=d.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,d){b||(a.currentDay=a.selectedDay,a.currentMonth=a.selectedMonth,a.currentYear=a.selectedYear);var e=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(d,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),e,this._getFormatConfig(a))}}),$.fn.datepicker=function(a){if(!this.length)return this;$.datepicker.initialized||($(document).mousedown($.datepicker._checkExternalClick).find("body").append($.datepicker.dpDiv),$.datepicker.initialized=!0);var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return $.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return $.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b));return this.each(function(){typeof a=="string"?$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this].concat(b)):$.datepicker._attachDatepicker(this,a)})},$.datepicker=new Datepicker,$.datepicker.initialized=!1,$.datepicker.uuid=(new Date).getTime(),$.datepicker.version="1.8.18",window["DP_jQuery_"+dpuuid]=$})(jQuery);/*
+ * jQuery UI Progressbar 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()}),this.valueDiv=a("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this.oldValue=this._value(),this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove(),a.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===b)return this._value();this._setOption("value",a);return this},_setOption:function(b,c){b==="value"&&(this.options.value=c,this._refreshValue(),this._value()===this.options.max&&this._trigger("complete")),a.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;typeof a!="number"&&(a=0);return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100*this._value()/this.options.max},_refreshValue:function(){var a=this.value(),b=this._percentage();this.oldValue!==a&&(this.oldValue=a,this._trigger("change")),this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(b.toFixed(0)+"%"),this.element.attr("aria-valuenow",a)}}),a.extend(a.ui.progressbar,{version:"1.8.18"})})(jQuery);/*
+ * jQuery UI Effects 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/
+ */jQuery.effects||function(a,b){function l(b){if(!b||typeof b=="number"||a.fx.speeds[b])return!0;if(typeof b=="string"&&!a.effects[b])return!0;return!1}function k(b,c,d,e){typeof b=="object"&&(e=c,d=null,c=b,b=c.effect),a.isFunction(c)&&(e=c,d=null,c={});if(typeof c=="number"||a.fx.speeds[c])e=d,d=c,c={};a.isFunction(d)&&(e=d,d=null),c=c||{},d=d||c.duration,d=a.fx.off?0:typeof d=="number"?d:d in a.fx.speeds?a.fx.speeds[d]:a.fx.speeds._default,e=e||c.complete;return[b,c,d,e]}function j(a,b){var c={_:0},d;for(d in b)a[d]!=b[d]&&(c[d]=b[d]);return c}function i(b){var c,d;for(c in b)d=b[c],(d==null||a.isFunction(d)||c in g||/scrollbar/.test(c)||!/color/i.test(c)&&isNaN(parseFloat(d)))&&delete b[c];return b}function h(){var a=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,b={},c,d;if(a&&a.length&&a[0]&&a[a[0]]){var e=a.length;while(e--)c=a[e],typeof a[c]=="string"&&(d=c.replace(/\-(\w)/g,function(a,b){return b.toUpperCase()}),b[d]=a[c])}else for(c in a)typeof a[c]=="string"&&(b[c]=a[c]);return b}function d(b,d){var e;do{e=a.curCSS(b,d);if(e!=""&&e!="transparent"||a.nodeName(b,"body"))break;d="backgroundColor"}while(b=b.parentNode);return c(e)}function c(b){var c;if(b&&b.constructor==Array&&b.length==3)return b;if(c=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(b))return[parseInt(c[1],10),parseInt(c[2],10),parseInt(c[3],10)];if(c=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(b))return[parseFloat(c[1])*2.55,parseFloat(c[2])*2.55,parseFloat(c[3])*2.55];if(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(b))return[parseInt(c[1],16),parseInt(c[2],16),parseInt(c[3],16)];if(c=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(b))return[parseInt(c[1]+c[1],16),parseInt(c[2]+c[2],16),parseInt(c[3]+c[3],16)];if(c=/rgba\(0, 0, 0, 0\)/.exec(b))return e.transparent;return e[a.trim(b).toLowerCase()]}a.effects={},a.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","borderColor","color","outlineColor"],function(b,e){a.fx.step[e]=function(a){a.colorInit||(a.start=d(a.elem,e),a.end=c(a.end),a.colorInit=!0),a.elem.style[e]="rgb("+Math.max(Math.min(parseInt(a.pos*(a.end[0]-a.start[0])+a.start[0],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[1]-a.start[1])+a.start[1],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[2]-a.start[2])+a.start[2],10),255),0)+")"}});var e={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},f=["add","remove","toggle"],g={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};a.effects.animateClass=function(b,c,d,e){a.isFunction(d)&&(e=d,d=null);return this.queue(function(){var g=a(this),k=g.attr("style")||" ",l=i(h.call(this)),m,n=g.attr("class");a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),m=i(h.call(this)),g.attr("class",n),g.animate(j(l,m),{queue:!1,duration:c,easing:d,complete:function(){a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),typeof g.attr("style")=="object"?(g.attr("style").cssText="",g.attr("style").cssText=k):g.attr("style",k),e&&e.apply(this,arguments),a.dequeue(this)}})})},a.fn.extend({_addClass:a.fn.addClass,addClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{add:b},c,d,e]):this._addClass(b)},_removeClass:a.fn.removeClass,removeClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{remove:b},c,d,e]):this._removeClass(b)},_toggleClass:a.fn.toggleClass,toggleClass:function(c,d,e,f,g){return typeof d=="boolean"||d===b?e?a.effects.animateClass.apply(this,[d?{add:c}:{remove:c},e,f,g]):this._toggleClass(c,d):a.effects.animateClass.apply(this,[{toggle:c},d,e,f])},switchClass:function(b,c,d,e,f){return a.effects.animateClass.apply(this,[{add:c,remove:b},d,e,f])}}),a.extend(a.effects,{version:"1.8.18",save:function(a,b){for(var c=0;c<b.length;c++)b[c]!==null&&a.data("ec.storage."+b[c],a[0].style[b[c]])},restore:function(a,b){for(var c=0;c<b.length;c++)b[c]!==null&&a.css(b[c],a.data("ec.storage."+b[c]))},setMode:function(a,b){b=="toggle"&&(b=a.is(":hidden")?"show":"hide");return b},getBaseline:function(a,b){var c,d;switch(a[0]){case"top":c=0;break;case"middle":c=.5;break;case"bottom":c=1;break;default:c=a[0]/b.height}switch(a[1]){case"left":d=0;break;case"center":d=.5;break;case"right":d=1;break;default:d=a[1]/b.width}return{x:d,y:c}},createWrapper:function(b){if(b.parent().is(".ui-effects-wrapper"))return b.parent();var c={width:b.outerWidth(!0),height:b.outerHeight(!0),"float":b.css("float")},d=a("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),e=document.activeElement;b.wrap(d),(b[0]===e||a.contains(b[0],e))&&a(e).focus(),d=b.parent(),b.css("position")=="static"?(d.css({position:"relative"}),b.css({position:"relative"})):(a.extend(c,{position:b.css("position"),zIndex:b.css("z-index")}),a.each(["top","left","bottom","right"],function(a,d){c[d]=b.css(d),isNaN(parseInt(c[d],10))&&(c[d]="auto")}),b.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"}));return d.css(c).show()},removeWrapper:function(b){var c,d=document.activeElement;if(b.parent().is(".ui-effects-wrapper")){c=b.parent().replaceWith(b),(b[0]===d||a.contains(b[0],d))&&a(d).focus();return c}return b},setTransition:function(b,c,d,e){e=e||{},a.each(c,function(a,c){unit=b.cssUnit(c),unit[0]>0&&(e[c]=unit[0]*d+unit[1])});return e}}),a.fn.extend({effect:function(b,c,d,e){var f=k.apply(this,arguments),g={options:f[1],duration:f[2],callback:f[3]},h=g.options.mode,i=a.effects[b];if(a.fx.off||!i)return h?this[h](g.duration,g.callback):this.each(function(){g.callback&&g.callback.call(this)});return i.call(this,g)},_show:a.fn.show,show:function(a){if(l(a))return this._show.apply(this,arguments);var b=k.apply(this,arguments);b[1].mode="show";return this.effect.apply(this,b)},_hide:a.fn.hide,hide:function(a){if(l(a))return this._hide.apply(this,arguments);var b=k.apply(this,arguments);b[1].mode="hide";return this.effect.apply(this,b)},__toggle:a.fn.toggle,toggle:function(b){if(l(b)||typeof b=="boolean"||a.isFunction(b))return this.__toggle.apply(this,arguments);var c=k.apply(this,arguments);c[1].mode="toggle";return this.effect.apply(this,c)},cssUnit:function(b){var c=this.css(b),d=[];a.each(["em","px","%","pt"],function(a,b){c.indexOf(b)>0&&(d=[parseFloat(c),b])});return d}}),a.easing.jswing=a.easing.swing,a.extend(a.easing,{def:"easeOutQuad",swing:function(b,c,d,e,f){return a.easing[a.easing.def](b,c,d,e,f)},easeInQuad:function(a,b,c,d,e){return d*(b/=e)*b+c},easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c},easeInOutQuad:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b+c;return-d/2*(--b*(b-2)-1)+c},easeInCubic:function(a,b,c,d,e){return d*(b/=e)*b*b+c},easeOutCubic:function(a,b,c,d,e){return d*((b=b/e-1)*b*b+1)+c},easeInOutCubic:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b+c;return d/2*((b-=2)*b*b+2)+c},easeInQuart:function(a,b,c,d,e){return d*(b/=e)*b*b*b+c},easeOutQuart:function(a,b,c,d,e){return-d*((b=b/e-1)*b*b*b-1)+c},easeInOutQuart:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b*b+c;return-d/2*((b-=2)*b*b*b-2)+c},easeInQuint:function(a,b,c,d,e){return d*(b/=e)*b*b*b*b+c},easeOutQuint:function(a,b,c,d,e){return d*((b=b/e-1)*b*b*b*b+1)+c},easeInOutQuint:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b*b*b+c;return d/2*((b-=2)*b*b*b*b+2)+c},easeInSine:function(a,b,c,d,e){return-d*Math.cos(b/e*(Math.PI/2))+d+c},easeOutSine:function(a,b,c,d,e){return d*Math.sin(b/e*(Math.PI/2))+c},easeInOutSine:function(a,b,c,d,e){return-d/2*(Math.cos(Math.PI*b/e)-1)+c},easeInExpo:function(a,b,c,d,e){return b==0?c:d*Math.pow(2,10*(b/e-1))+c},easeOutExpo:function(a,b,c,d,e){return b==e?c+d:d*(-Math.pow(2,-10*b/e)+1)+c},easeInOutExpo:function(a,b,c,d,e){if(b==0)return c;if(b==e)return c+d;if((b/=e/2)<1)return d/2*Math.pow(2,10*(b-1))+c;return d/2*(-Math.pow(2,-10*--b)+2)+c},easeInCirc:function(a,b,c,d,e){return-d*(Math.sqrt(1-(b/=e)*b)-1)+c},easeOutCirc:function(a,b,c,d,e){return d*Math.sqrt(1-(b=b/e-1)*b)+c},easeInOutCirc:function(a,b,c,d,e){if((b/=e/2)<1)return-d/2*(Math.sqrt(1-b*b)-1)+c;return d/2*(Math.sqrt(1-(b-=2)*b)+1)+c},easeInElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e)==1)return c+d;g||(g=e*.3);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g))+c},easeOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e)==1)return c+d;g||(g=e*.3);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*b)*Math.sin((b*e-f)*2*Math.PI/g)+d+c},easeInOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e/2)==2)return c+d;g||(g=e*.3*1.5);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);if(b<1)return-0.5*h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g)+c;return h*Math.pow(2,-10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g)*.5+d+c},easeInBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);return e*(c/=f)*c*((g+1)*c-g)+d},easeOutBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);return e*((c=c/f-1)*c*((g+1)*c+g)+1)+d},easeInOutBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);if((c/=f/2)<1)return e/2*c*c*(((g*=1.525)+1)*c-g)+d;return e/2*((c-=2)*c*(((g*=1.525)+1)*c+g)+2)+d},easeInBounce:function(b,c,d,e,f){return e-a.easing.easeOutBounce(b,f-c,0,e,f)+d},easeOutBounce:function(a,b,c,d,e){return(b/=e)<1/2.75?d*7.5625*b*b+c:b<2/2.75?d*(7.5625*(b-=1.5/2.75)*b+.75)+c:b<2.5/2.75?d*(7.5625*(b-=2.25/2.75)*b+.9375)+c:d*(7.5625*(b-=2.625/2.75)*b+.984375)+c},easeInOutBounce:function(b,c,d,e,f){if(c<f/2)return a.easing.easeInBounce(b,c*2,0,e,f)*.5+d;return a.easing.easeOutBounce(b,c*2-f,0,e,f)*.5+e*.5+d}})}(jQuery);/*
+ * jQuery UI Effects Blind 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Blind
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.blind=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"vertical";a.effects.save(c,d),c.show();var g=a.effects.createWrapper(c).css({overflow:"hidden"}),h=f=="vertical"?"height":"width",i=f=="vertical"?g.height():g.width();e=="show"&&g.css(h,0);var j={};j[h]=e=="show"?i:0,g.animate(j,b.duration,b.options.easing,function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery);/*
+ * jQuery UI Effects Bounce 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Bounce
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.bounce=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"effect"),f=b.options.direction||"up",g=b.options.distance||20,h=b.options.times||5,i=b.duration||250;/show|hide/.test(e)&&d.push("opacity"),a.effects.save(c,d),c.show(),a.effects.createWrapper(c);var j=f=="up"||f=="down"?"top":"left",k=f=="up"||f=="left"?"pos":"neg",g=b.options.distance||(j=="top"?c.outerHeight({margin:!0})/3:c.outerWidth({margin:!0})/3);e=="show"&&c.css("opacity",0).css(j,k=="pos"?-g:g),e=="hide"&&(g=g/(h*2)),e!="hide"&&h--;if(e=="show"){var l={opacity:1};l[j]=(k=="pos"?"+=":"-=")+g,c.animate(l,i/2,b.options.easing),g=g/2,h--}for(var m=0;m<h;m++){var n={},p={};n[j]=(k=="pos"?"-=":"+=")+g,p[j]=(k=="pos"?"+=":"-=")+g,c.animate(n,i/2,b.options.easing).animate(p,i/2,b.options.easing),g=e=="hide"?g*2:g/2}if(e=="hide"){var l={opacity:0};l[j]=(k=="pos"?"-=":"+=")+g,c.animate(l,i/2,b.options.easing,function(){c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)})}else{var n={},p={};n[j]=(k=="pos"?"-=":"+=")+g,p[j]=(k=="pos"?"+=":"-=")+g,c.animate(n,i/2,b.options.easing).animate(p,i/2,b.options.easing,function(){a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)})}c.queue("fx",function(){c.dequeue()}),c.dequeue()})}})(jQuery);/*
+ * jQuery UI Effects Clip 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Clip
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.clip=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right","height","width"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"vertical";a.effects.save(c,d),c.show();var g=a.effects.createWrapper(c).css({overflow:"hidden"}),h=c[0].tagName=="IMG"?g:c,i={size:f=="vertical"?"height":"width",position:f=="vertical"?"top":"left"},j=f=="vertical"?h.height():h.width();e=="show"&&(h.css(i.size,0),h.css(i.position,j/2));var k={};k[i.size]=e=="show"?j:0,k[i.position]=e=="show"?0:j/2,h.animate(k,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Drop 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Drop
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.drop=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right","opacity"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"left";a.effects.save(c,d),c.show(),a.effects.createWrapper(c);var g=f=="up"||f=="down"?"top":"left",h=f=="up"||f=="left"?"pos":"neg",i=b.options.distance||(g=="top"?c.outerHeight({margin:!0})/2:c.outerWidth({margin:!0})/2);e=="show"&&c.css("opacity",0).css(g,h=="pos"?-i:i);var j={opacity:e=="show"?1:0};j[g]=(e=="show"?h=="pos"?"+=":"-=":h=="pos"?"-=":"+=")+i,c.animate(j,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Explode 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Explode
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.explode=function(b){return this.queue(function(){var c=b.options.pieces?Math.round(Math.sqrt(b.options.pieces)):3,d=b.options.pieces?Math.round(Math.sqrt(b.options.pieces)):3;b.options.mode=b.options.mode=="toggle"?a(this).is(":visible")?"hide":"show":b.options.mode;var e=a(this).show().css("visibility","hidden"),f=e.offset();f.top-=parseInt(e.css("marginTop"),10)||0,f.left-=parseInt(e.css("marginLeft"),10)||0;var g=e.outerWidth(!0),h=e.outerHeight(!0);for(var i=0;i<c;i++)for(var j=0;j<d;j++)e.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-j*(g/d),top:-i*(h/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:g/d,height:h/c,left:f.left+j*(g/d)+(b.options.mode=="show"?(j-Math.floor(d/2))*(g/d):0),top:f.top+i*(h/c)+(b.options.mode=="show"?(i-Math.floor(c/2))*(h/c):0),opacity:b.options.mode=="show"?0:1}).animate({left:f.left+j*(g/d)+(b.options.mode=="show"?0:(j-Math.floor(d/2))*(g/d)),top:f.top+i*(h/c)+(b.options.mode=="show"?0:(i-Math.floor(c/2))*(h/c)),opacity:b.options.mode=="show"?1:0},b.duration||500);setTimeout(function(){b.options.mode=="show"?e.css({visibility:"visible"}):e.css({visibility:"visible"}).hide(),b.callback&&b.callback.apply(e[0]),e.dequeue(),a("div.ui-effects-explode").remove()},b.duration||500)})}})(jQuery);/*
+ * jQuery UI Effects Fade 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fade
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.fade=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"hide");c.animate({opacity:d},{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Fold 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fold
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.fold=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.size||15,g=!!b.options.horizFirst,h=b.duration?b.duration/2:a.fx.speeds._default/2;a.effects.save(c,d),c.show();var i=a.effects.createWrapper(c).css({overflow:"hidden"}),j=e=="show"!=g,k=j?["width","height"]:["height","width"],l=j?[i.width(),i.height()]:[i.height(),i.width()],m=/([0-9]+)%/.exec(f);m&&(f=parseInt(m[1],10)/100*l[e=="hide"?0:1]),e=="show"&&i.css(g?{height:0,width:f}:{height:f,width:0});var n={},p={};n[k[0]]=e=="show"?l[0]:f,p[k[1]]=e=="show"?l[1]:0,i.animate(n,h,b.options.easing).animate(p,h,b.options.easing,function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery);/*
+ * jQuery UI Effects Highlight 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Highlight
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.highlight=function(b){return this.queue(function(){var c=a(this),d=["backgroundImage","backgroundColor","opacity"],e=a.effects.setMode(c,b.options.mode||"show"),f={backgroundColor:c.css("backgroundColor")};e=="hide"&&(f.opacity=0),a.effects.save(c,d),c.show().css({backgroundImage:"none",backgroundColor:b.options.color||"#ffff99"}).animate(f,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),e=="show"&&!a.support.opacity&&this.style.removeAttribute("filter"),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Pulsate 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Pulsate
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.pulsate=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"show");times=(b.options.times||5)*2-1,duration=b.duration?b.duration/2:a.fx.speeds._default/2,isVisible=c.is(":visible"),animateTo=0,isVisible||(c.css("opacity",0).show(),animateTo=1),(d=="hide"&&isVisible||d=="show"&&!isVisible)&&times--;for(var e=0;e<times;e++)c.animate({opacity:animateTo},duration,b.options.easing),animateTo=(animateTo+1)%2;c.animate({opacity:animateTo},duration,b.options.easing,function(){animateTo==0&&c.hide(),b.callback&&b.callback.apply(this,arguments)}),c.queue("fx",function(){c.dequeue()}).dequeue()})}})(jQuery);/*
+ * jQuery UI Effects Scale 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Scale
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.puff=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"hide"),e=parseInt(b.options.percent,10)||150,f=e/100,g={height:c.height(),width:c.width()};a.extend(b.options,{fade:!0,mode:d,percent:d=="hide"?e:100,from:d=="hide"?g:{height:g.height*f,width:g.width*f}}),c.effect("scale",b.options,b.duration,b.callback),c.dequeue()})},a.effects.scale=function(b){return this.queue(function(){var c=a(this),d=a.extend(!0,{},b.options),e=a.effects.setMode(c,b.options.mode||"effect"),f=parseInt(b.options.percent,10)||(parseInt(b.options.percent,10)==0?0:e=="hide"?0:100),g=b.options.direction||"both",h=b.options.origin;e!="effect"&&(d.origin=h||["middle","center"],d.restore=!0);var i={height:c.height(),width:c.width()};c.from=b.options.from||(e=="show"?{height:0,width:0}:i);var j={y:g!="horizontal"?f/100:1,x:g!="vertical"?f/100:1};c.to={height:i.height*j.y,width:i.width*j.x},b.options.fade&&(e=="show"&&(c.from.opacity=0,c.to.opacity=1),e=="hide"&&(c.from.opacity=1,c.to.opacity=0)),d.from=c.from,d.to=c.to,d.mode=e,c.effect("size",d,b.duration,b.callback),c.dequeue()})},a.effects.size=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right","width","height","overflow","opacity"],e=["position","top","bottom","left","right","overflow","opacity"],f=["width","height","overflow"],g=["fontSize"],h=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],i=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],j=a.effects.setMode(c,b.options.mode||"effect"),k=b.options.restore||!1,l=b.options.scale||"both",m=b.options.origin,n={height:c.height(),width:c.width()};c.from=b.options.from||n,c.to=b.options.to||n;if(m){var p=a.effects.getBaseline(m,n);c.from.top=(n.height-c.from.height)*p.y,c.from.left=(n.width-c.from.width)*p.x,c.to.top=(n.height-c.to.height)*p.y,c.to.left=(n.width-c.to.width)*p.x}var q={from:{y:c.from.height/n.height,x:c.from.width/n.width},to:{y:c.to.height/n.height,x:c.to.width/n.width}};if(l=="box"||l=="both")q.from.y!=q.to.y&&(d=d.concat(h),c.from=a.effects.setTransition(c,h,q.from.y,c.from),c.to=a.effects.setTransition(c,h,q.to.y,c.to)),q.from.x!=q.to.x&&(d=d.concat(i),c.from=a.effects.setTransition(c,i,q.from.x,c.from),c.to=a.effects.setTransition(c,i,q.to.x,c.to));(l=="content"||l=="both")&&q.from.y!=q.to.y&&(d=d.concat(g),c.from=a.effects.setTransition(c,g,q.from.y,c.from),c.to=a.effects.setTransition(c,g,q.to.y,c.to)),a.effects.save(c,k?d:e),c.show(),a.effects.createWrapper(c),c.css("overflow","hidden").css(c.from);if(l=="content"||l=="both")h=h.concat(["marginTop","marginBottom"]).concat(g),i=i.concat(["marginLeft","marginRight"]),f=d.concat(h).concat(i),c.find("*[width]").each(function(){child=a(this),k&&a.effects.save(child,f);var c={height:child.height(),width:child.width()};child.from={height:c.height*q.from.y,width:c.width*q.from.x},child.to={height:c.height*q.to.y,width:c.width*q.to.x},q.from.y!=q.to.y&&(child.from=a.effects.setTransition(child,h,q.from.y,child.from),child.to=a.effects.setTransition(child,h,q.to.y,child.to)),q.from.x!=q.to.x&&(child.from=a.effects.setTransition(child,i,q.from.x,child.from),child.to=a.effects.setTransition(child,i,q.to.x,child.to)),child.css(child.from),child.animate(child.to,b.duration,b.options.easing,function(){k&&a.effects.restore(child,f)})});c.animate(c.to,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){c.to.opacity===0&&c.css("opacity",c.from.opacity),j=="hide"&&c.hide(),a.effects.restore(c,k?d:e),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Shake 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Shake
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.shake=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"effect"),f=b.options.direction||"left",g=b.options.distance||20,h=b.options.times||3,i=b.duration||b.options.duration||140;a.effects.save(c,d),c.show(),a.effects.createWrapper(c);var j=f=="up"||f=="down"?"top":"left",k=f=="up"||f=="left"?"pos":"neg",l={},m={},n={};l[j]=(k=="pos"?"-=":"+=")+g,m[j]=(k=="pos"?"+=":"-=")+g*2,n[j]=(k=="pos"?"-=":"+=")+g*2,c.animate(l,i,b.options.easing);for(var p=1;p<h;p++)c.animate(m,i,b.options.easing).animate(n,i,b.options.easing);c.animate(m,i,b.options.easing).animate(l,i/2,b.options.easing,function(){a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)}),c.queue("fx",function(){c.dequeue()}),c.dequeue()})}})(jQuery);/*
+ * jQuery UI Effects Slide 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Slide
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.slide=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"show"),f=b.options.direction||"left";a.effects.save(c,d),c.show(),a.effects.createWrapper(c).css({overflow:"hidden"});var g=f=="up"||f=="down"?"top":"left",h=f=="up"||f=="left"?"pos":"neg",i=b.options.distance||(g=="top"?c.outerHeight({margin:!0}):c.outerWidth({margin:!0}));e=="show"&&c.css(g,h=="pos"?isNaN(i)?"-"+i:-i:i);var j={};j[g]=(e=="show"?h=="pos"?"+=":"-=":h=="pos"?"-=":"+=")+i,c.animate(j,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Transfer 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Transfer
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.transfer=function(b){return this.queue(function(){var c=a(this),d=a(b.options.to),e=d.offset(),f={top:e.top,left:e.left,height:d.innerHeight(),width:d.innerWidth()},g=c.offset(),h=a('<div class="ui-effects-transfer"></div>').appendTo(document.body).addClass(b.options.className).css({top:g.top,left:g.left,height:c.innerHeight(),width:c.innerWidth(),position:"absolute"}).animate(f,b.duration,b.options.easing,function(){h.remove(),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery); \ No newline at end of file
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/js/jquery.flot-0.7.min.js b/storage/mroonga/vendor/groonga/data/html/admin/js/jquery.flot-0.7.min.js
new file mode 100644
index 00000000000..4467fc5d8cd
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/js/jquery.flot-0.7.min.js
@@ -0,0 +1,6 @@
+/* Javascript plotting library for jQuery, v. 0.7.
+ *
+ * Released under the MIT license by IOLA, December 2007.
+ *
+ */
+(function(b){b.color={};b.color.make=function(d,e,g,f){var c={};c.r=d||0;c.g=e||0;c.b=g||0;c.a=f!=null?f:1;c.add=function(h,j){for(var k=0;k<h.length;++k){c[h.charAt(k)]+=j}return c.normalize()};c.scale=function(h,j){for(var k=0;k<h.length;++k){c[h.charAt(k)]*=j}return c.normalize()};c.toString=function(){if(c.a>=1){return"rgb("+[c.r,c.g,c.b].join(",")+")"}else{return"rgba("+[c.r,c.g,c.b,c.a].join(",")+")"}};c.normalize=function(){function h(k,j,l){return j<k?k:(j>l?l:j)}c.r=h(0,parseInt(c.r),255);c.g=h(0,parseInt(c.g),255);c.b=h(0,parseInt(c.b),255);c.a=h(0,c.a,1);return c};c.clone=function(){return b.color.make(c.r,c.b,c.g,c.a)};return c.normalize()};b.color.extract=function(d,e){var c;do{c=d.css(e).toLowerCase();if(c!=""&&c!="transparent"){break}d=d.parent()}while(!b.nodeName(d.get(0),"body"));if(c=="rgba(0, 0, 0, 0)"){c="transparent"}return b.color.parse(c)};b.color.parse=function(c){var d,f=b.color.make;if(d=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c)){return f(parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3],10))}if(d=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(c)){return f(parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3],10),parseFloat(d[4]))}if(d=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c)){return f(parseFloat(d[1])*2.55,parseFloat(d[2])*2.55,parseFloat(d[3])*2.55)}if(d=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(c)){return f(parseFloat(d[1])*2.55,parseFloat(d[2])*2.55,parseFloat(d[3])*2.55,parseFloat(d[4]))}if(d=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c)){return f(parseInt(d[1],16),parseInt(d[2],16),parseInt(d[3],16))}if(d=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c)){return f(parseInt(d[1]+d[1],16),parseInt(d[2]+d[2],16),parseInt(d[3]+d[3],16))}var e=b.trim(c).toLowerCase();if(e=="transparent"){return f(255,255,255,0)}else{d=a[e]||[0,0,0];return f(d[0],d[1],d[2])}};var a={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);(function(c){function b(av,ai,J,af){var Q=[],O={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:true,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:0.85},xaxis:{show:null,position:"bottom",mode:null,color:null,tickColor:null,transform:null,inverseTransform:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,reserveSpace:null,tickLength:null,alignTicksWithAxis:null,tickDecimals:null,tickSize:null,minTickSize:null,monthNames:null,timeformat:null,twelveHourClock:false},yaxis:{autoscaleMargin:0.02,position:"left"},xaxes:[],yaxes:[],series:{points:{show:false,radius:3,lineWidth:2,fill:true,fillColor:"#ffffff",symbol:"circle"},lines:{lineWidth:2,fill:false,fillColor:null,steps:false},bars:{show:false,lineWidth:2,barWidth:1,fill:true,fillColor:null,align:"left",horizontal:false},shadowSize:3},grid:{show:true,aboveData:false,color:"#545454",backgroundColor:null,borderColor:null,tickColor:null,labelMargin:5,axisMargin:8,borderWidth:2,minBorderMargin:null,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:false,hoverable:false,autoHighlight:true,mouseActiveRadius:10},hooks:{}},az=null,ad=null,y=null,H=null,A=null,p=[],aw=[],q={left:0,right:0,top:0,bottom:0},G=0,I=0,h=0,w=0,ak={processOptions:[],processRawData:[],processDatapoints:[],drawSeries:[],draw:[],bindEvents:[],drawOverlay:[],shutdown:[]},aq=this;aq.setData=aj;aq.setupGrid=t;aq.draw=W;aq.getPlaceholder=function(){return av};aq.getCanvas=function(){return az};aq.getPlotOffset=function(){return q};aq.width=function(){return h};aq.height=function(){return w};aq.offset=function(){var aB=y.offset();aB.left+=q.left;aB.top+=q.top;return aB};aq.getData=function(){return Q};aq.getAxes=function(){var aC={},aB;c.each(p.concat(aw),function(aD,aE){if(aE){aC[aE.direction+(aE.n!=1?aE.n:"")+"axis"]=aE}});return aC};aq.getXAxes=function(){return p};aq.getYAxes=function(){return aw};aq.c2p=C;aq.p2c=ar;aq.getOptions=function(){return O};aq.highlight=x;aq.unhighlight=T;aq.triggerRedrawOverlay=f;aq.pointOffset=function(aB){return{left:parseInt(p[aA(aB,"x")-1].p2c(+aB.x)+q.left),top:parseInt(aw[aA(aB,"y")-1].p2c(+aB.y)+q.top)}};aq.shutdown=ag;aq.resize=function(){B();g(az);g(ad)};aq.hooks=ak;F(aq);Z(J);X();aj(ai);t();W();ah();function an(aD,aB){aB=[aq].concat(aB);for(var aC=0;aC<aD.length;++aC){aD[aC].apply(this,aB)}}function F(){for(var aB=0;aB<af.length;++aB){var aC=af[aB];aC.init(aq);if(aC.options){c.extend(true,O,aC.options)}}}function Z(aC){var aB;c.extend(true,O,aC);if(O.xaxis.color==null){O.xaxis.color=O.grid.color}if(O.yaxis.color==null){O.yaxis.color=O.grid.color}if(O.xaxis.tickColor==null){O.xaxis.tickColor=O.grid.tickColor}if(O.yaxis.tickColor==null){O.yaxis.tickColor=O.grid.tickColor}if(O.grid.borderColor==null){O.grid.borderColor=O.grid.color}if(O.grid.tickColor==null){O.grid.tickColor=c.color.parse(O.grid.color).scale("a",0.22).toString()}for(aB=0;aB<Math.max(1,O.xaxes.length);++aB){O.xaxes[aB]=c.extend(true,{},O.xaxis,O.xaxes[aB])}for(aB=0;aB<Math.max(1,O.yaxes.length);++aB){O.yaxes[aB]=c.extend(true,{},O.yaxis,O.yaxes[aB])}if(O.xaxis.noTicks&&O.xaxis.ticks==null){O.xaxis.ticks=O.xaxis.noTicks}if(O.yaxis.noTicks&&O.yaxis.ticks==null){O.yaxis.ticks=O.yaxis.noTicks}if(O.x2axis){O.xaxes[1]=c.extend(true,{},O.xaxis,O.x2axis);O.xaxes[1].position="top"}if(O.y2axis){O.yaxes[1]=c.extend(true,{},O.yaxis,O.y2axis);O.yaxes[1].position="right"}if(O.grid.coloredAreas){O.grid.markings=O.grid.coloredAreas}if(O.grid.coloredAreasColor){O.grid.markingsColor=O.grid.coloredAreasColor}if(O.lines){c.extend(true,O.series.lines,O.lines)}if(O.points){c.extend(true,O.series.points,O.points)}if(O.bars){c.extend(true,O.series.bars,O.bars)}if(O.shadowSize!=null){O.series.shadowSize=O.shadowSize}for(aB=0;aB<O.xaxes.length;++aB){V(p,aB+1).options=O.xaxes[aB]}for(aB=0;aB<O.yaxes.length;++aB){V(aw,aB+1).options=O.yaxes[aB]}for(var aD in ak){if(O.hooks[aD]&&O.hooks[aD].length){ak[aD]=ak[aD].concat(O.hooks[aD])}}an(ak.processOptions,[O])}function aj(aB){Q=Y(aB);ax();z()}function Y(aE){var aC=[];for(var aB=0;aB<aE.length;++aB){var aD=c.extend(true,{},O.series);if(aE[aB].data!=null){aD.data=aE[aB].data;delete aE[aB].data;c.extend(true,aD,aE[aB]);aE[aB].data=aD.data}else{aD.data=aE[aB]}aC.push(aD)}return aC}function aA(aC,aD){var aB=aC[aD+"axis"];if(typeof aB=="object"){aB=aB.n}if(typeof aB!="number"){aB=1}return aB}function m(){return c.grep(p.concat(aw),function(aB){return aB})}function C(aE){var aC={},aB,aD;for(aB=0;aB<p.length;++aB){aD=p[aB];if(aD&&aD.used){aC["x"+aD.n]=aD.c2p(aE.left)}}for(aB=0;aB<aw.length;++aB){aD=aw[aB];if(aD&&aD.used){aC["y"+aD.n]=aD.c2p(aE.top)}}if(aC.x1!==undefined){aC.x=aC.x1}if(aC.y1!==undefined){aC.y=aC.y1}return aC}function ar(aF){var aD={},aC,aE,aB;for(aC=0;aC<p.length;++aC){aE=p[aC];if(aE&&aE.used){aB="x"+aE.n;if(aF[aB]==null&&aE.n==1){aB="x"}if(aF[aB]!=null){aD.left=aE.p2c(aF[aB]);break}}}for(aC=0;aC<aw.length;++aC){aE=aw[aC];if(aE&&aE.used){aB="y"+aE.n;if(aF[aB]==null&&aE.n==1){aB="y"}if(aF[aB]!=null){aD.top=aE.p2c(aF[aB]);break}}}return aD}function V(aC,aB){if(!aC[aB-1]){aC[aB-1]={n:aB,direction:aC==p?"x":"y",options:c.extend(true,{},aC==p?O.xaxis:O.yaxis)}}return aC[aB-1]}function ax(){var aG;var aM=Q.length,aB=[],aE=[];for(aG=0;aG<Q.length;++aG){var aJ=Q[aG].color;if(aJ!=null){--aM;if(typeof aJ=="number"){aE.push(aJ)}else{aB.push(c.color.parse(Q[aG].color))}}}for(aG=0;aG<aE.length;++aG){aM=Math.max(aM,aE[aG]+1)}var aC=[],aF=0;aG=0;while(aC.length<aM){var aI;if(O.colors.length==aG){aI=c.color.make(100,100,100)}else{aI=c.color.parse(O.colors[aG])}var aD=aF%2==1?-1:1;aI.scale("rgb",1+aD*Math.ceil(aF/2)*0.2);aC.push(aI);++aG;if(aG>=O.colors.length){aG=0;++aF}}var aH=0,aN;for(aG=0;aG<Q.length;++aG){aN=Q[aG];if(aN.color==null){aN.color=aC[aH].toString();++aH}else{if(typeof aN.color=="number"){aN.color=aC[aN.color].toString()}}if(aN.lines.show==null){var aL,aK=true;for(aL in aN){if(aN[aL]&&aN[aL].show){aK=false;break}}if(aK){aN.lines.show=true}}aN.xaxis=V(p,aA(aN,"x"));aN.yaxis=V(aw,aA(aN,"y"))}}function z(){var aO=Number.POSITIVE_INFINITY,aI=Number.NEGATIVE_INFINITY,aB=Number.MAX_VALUE,aU,aS,aR,aN,aD,aJ,aT,aP,aH,aG,aC,a0,aX,aL;function aF(a3,a2,a1){if(a2<a3.datamin&&a2!=-aB){a3.datamin=a2}if(a1>a3.datamax&&a1!=aB){a3.datamax=a1}}c.each(m(),function(a1,a2){a2.datamin=aO;a2.datamax=aI;a2.used=false});for(aU=0;aU<Q.length;++aU){aJ=Q[aU];aJ.datapoints={points:[]};an(ak.processRawData,[aJ,aJ.data,aJ.datapoints])}for(aU=0;aU<Q.length;++aU){aJ=Q[aU];var aZ=aJ.data,aW=aJ.datapoints.format;if(!aW){aW=[];aW.push({x:true,number:true,required:true});aW.push({y:true,number:true,required:true});if(aJ.bars.show||(aJ.lines.show&&aJ.lines.fill)){aW.push({y:true,number:true,required:false,defaultValue:0});if(aJ.bars.horizontal){delete aW[aW.length-1].y;aW[aW.length-1].x=true}}aJ.datapoints.format=aW}if(aJ.datapoints.pointsize!=null){continue}aJ.datapoints.pointsize=aW.length;aP=aJ.datapoints.pointsize;aT=aJ.datapoints.points;insertSteps=aJ.lines.show&&aJ.lines.steps;aJ.xaxis.used=aJ.yaxis.used=true;for(aS=aR=0;aS<aZ.length;++aS,aR+=aP){aL=aZ[aS];var aE=aL==null;if(!aE){for(aN=0;aN<aP;++aN){a0=aL[aN];aX=aW[aN];if(aX){if(aX.number&&a0!=null){a0=+a0;if(isNaN(a0)){a0=null}else{if(a0==Infinity){a0=aB}else{if(a0==-Infinity){a0=-aB}}}}if(a0==null){if(aX.required){aE=true}if(aX.defaultValue!=null){a0=aX.defaultValue}}}aT[aR+aN]=a0}}if(aE){for(aN=0;aN<aP;++aN){a0=aT[aR+aN];if(a0!=null){aX=aW[aN];if(aX.x){aF(aJ.xaxis,a0,a0)}if(aX.y){aF(aJ.yaxis,a0,a0)}}aT[aR+aN]=null}}else{if(insertSteps&&aR>0&&aT[aR-aP]!=null&&aT[aR-aP]!=aT[aR]&&aT[aR-aP+1]!=aT[aR+1]){for(aN=0;aN<aP;++aN){aT[aR+aP+aN]=aT[aR+aN]}aT[aR+1]=aT[aR-aP+1];aR+=aP}}}}for(aU=0;aU<Q.length;++aU){aJ=Q[aU];an(ak.processDatapoints,[aJ,aJ.datapoints])}for(aU=0;aU<Q.length;++aU){aJ=Q[aU];aT=aJ.datapoints.points,aP=aJ.datapoints.pointsize;var aK=aO,aQ=aO,aM=aI,aV=aI;for(aS=0;aS<aT.length;aS+=aP){if(aT[aS]==null){continue}for(aN=0;aN<aP;++aN){a0=aT[aS+aN];aX=aW[aN];if(!aX||a0==aB||a0==-aB){continue}if(aX.x){if(a0<aK){aK=a0}if(a0>aM){aM=a0}}if(aX.y){if(a0<aQ){aQ=a0}if(a0>aV){aV=a0}}}}if(aJ.bars.show){var aY=aJ.bars.align=="left"?0:-aJ.bars.barWidth/2;if(aJ.bars.horizontal){aQ+=aY;aV+=aY+aJ.bars.barWidth}else{aK+=aY;aM+=aY+aJ.bars.barWidth}}aF(aJ.xaxis,aK,aM);aF(aJ.yaxis,aQ,aV)}c.each(m(),function(a1,a2){if(a2.datamin==aO){a2.datamin=null}if(a2.datamax==aI){a2.datamax=null}})}function j(aB,aC){var aD=document.createElement("canvas");aD.className=aC;aD.width=G;aD.height=I;if(!aB){c(aD).css({position:"absolute",left:0,top:0})}c(aD).appendTo(av);if(!aD.getContext){aD=window.G_vmlCanvasManager.initElement(aD)}aD.getContext("2d").save();return aD}function B(){G=av.width();I=av.height();if(G<=0||I<=0){throw"Invalid dimensions for plot, width = "+G+", height = "+I}}function g(aC){if(aC.width!=G){aC.width=G}if(aC.height!=I){aC.height=I}var aB=aC.getContext("2d");aB.restore();aB.save()}function X(){var aC,aB=av.children("canvas.base"),aD=av.children("canvas.overlay");if(aB.length==0||aD==0){av.html("");av.css({padding:0});if(av.css("position")=="static"){av.css("position","relative")}B();az=j(true,"base");ad=j(false,"overlay");aC=false}else{az=aB.get(0);ad=aD.get(0);aC=true}H=az.getContext("2d");A=ad.getContext("2d");y=c([ad,az]);if(aC){av.data("plot").shutdown();aq.resize();A.clearRect(0,0,G,I);y.unbind();av.children().not([az,ad]).remove()}av.data("plot",aq)}function ah(){if(O.grid.hoverable){y.mousemove(aa);y.mouseleave(l)}if(O.grid.clickable){y.click(R)}an(ak.bindEvents,[y])}function ag(){if(M){clearTimeout(M)}y.unbind("mousemove",aa);y.unbind("mouseleave",l);y.unbind("click",R);an(ak.shutdown,[y])}function r(aG){function aC(aH){return aH}var aF,aB,aD=aG.options.transform||aC,aE=aG.options.inverseTransform;if(aG.direction=="x"){aF=aG.scale=h/Math.abs(aD(aG.max)-aD(aG.min));aB=Math.min(aD(aG.max),aD(aG.min))}else{aF=aG.scale=w/Math.abs(aD(aG.max)-aD(aG.min));aF=-aF;aB=Math.max(aD(aG.max),aD(aG.min))}if(aD==aC){aG.p2c=function(aH){return(aH-aB)*aF}}else{aG.p2c=function(aH){return(aD(aH)-aB)*aF}}if(!aE){aG.c2p=function(aH){return aB+aH/aF}}else{aG.c2p=function(aH){return aE(aB+aH/aF)}}}function L(aD){var aB=aD.options,aF,aJ=aD.ticks||[],aI=[],aE,aK=aB.labelWidth,aG=aB.labelHeight,aC;function aH(aM,aL){return c('<div style="position:absolute;top:-10000px;'+aL+'font-size:smaller"><div class="'+aD.direction+"Axis "+aD.direction+aD.n+'Axis">'+aM.join("")+"</div></div>").appendTo(av)}if(aD.direction=="x"){if(aK==null){aK=Math.floor(G/(aJ.length>0?aJ.length:1))}if(aG==null){aI=[];for(aF=0;aF<aJ.length;++aF){aE=aJ[aF].label;if(aE){aI.push('<div class="tickLabel" style="float:left;width:'+aK+'px">'+aE+"</div>")}}if(aI.length>0){aI.push('<div style="clear:left"></div>');aC=aH(aI,"width:10000px;");aG=aC.height();aC.remove()}}}else{if(aK==null||aG==null){for(aF=0;aF<aJ.length;++aF){aE=aJ[aF].label;if(aE){aI.push('<div class="tickLabel">'+aE+"</div>")}}if(aI.length>0){aC=aH(aI,"");if(aK==null){aK=aC.children().width()}if(aG==null){aG=aC.find("div.tickLabel").height()}aC.remove()}}}if(aK==null){aK=0}if(aG==null){aG=0}aD.labelWidth=aK;aD.labelHeight=aG}function au(aD){var aC=aD.labelWidth,aL=aD.labelHeight,aH=aD.options.position,aF=aD.options.tickLength,aG=O.grid.axisMargin,aJ=O.grid.labelMargin,aK=aD.direction=="x"?p:aw,aE;var aB=c.grep(aK,function(aN){return aN&&aN.options.position==aH&&aN.reserveSpace});if(c.inArray(aD,aB)==aB.length-1){aG=0}if(aF==null){aF="full"}var aI=c.grep(aK,function(aN){return aN&&aN.reserveSpace});var aM=c.inArray(aD,aI)==0;if(!aM&&aF=="full"){aF=5}if(!isNaN(+aF)){aJ+=+aF}if(aD.direction=="x"){aL+=aJ;if(aH=="bottom"){q.bottom+=aL+aG;aD.box={top:I-q.bottom,height:aL}}else{aD.box={top:q.top+aG,height:aL};q.top+=aL+aG}}else{aC+=aJ;if(aH=="left"){aD.box={left:q.left+aG,width:aC};q.left+=aC+aG}else{q.right+=aC+aG;aD.box={left:G-q.right,width:aC}}}aD.position=aH;aD.tickLength=aF;aD.box.padding=aJ;aD.innermost=aM}function U(aB){if(aB.direction=="x"){aB.box.left=q.left;aB.box.width=h}else{aB.box.top=q.top;aB.box.height=w}}function t(){var aC,aE=m();c.each(aE,function(aF,aG){aG.show=aG.options.show;if(aG.show==null){aG.show=aG.used}aG.reserveSpace=aG.show||aG.options.reserveSpace;n(aG)});allocatedAxes=c.grep(aE,function(aF){return aF.reserveSpace});q.left=q.right=q.top=q.bottom=0;if(O.grid.show){c.each(allocatedAxes,function(aF,aG){S(aG);P(aG);ap(aG,aG.ticks);L(aG)});for(aC=allocatedAxes.length-1;aC>=0;--aC){au(allocatedAxes[aC])}var aD=O.grid.minBorderMargin;if(aD==null){aD=0;for(aC=0;aC<Q.length;++aC){aD=Math.max(aD,Q[aC].points.radius+Q[aC].points.lineWidth/2)}}for(var aB in q){q[aB]+=O.grid.borderWidth;q[aB]=Math.max(aD,q[aB])}}h=G-q.left-q.right;w=I-q.bottom-q.top;c.each(aE,function(aF,aG){r(aG)});if(O.grid.show){c.each(allocatedAxes,function(aF,aG){U(aG)});k()}o()}function n(aE){var aF=aE.options,aD=+(aF.min!=null?aF.min:aE.datamin),aB=+(aF.max!=null?aF.max:aE.datamax),aH=aB-aD;if(aH==0){var aC=aB==0?1:0.01;if(aF.min==null){aD-=aC}if(aF.max==null||aF.min!=null){aB+=aC}}else{var aG=aF.autoscaleMargin;if(aG!=null){if(aF.min==null){aD-=aH*aG;if(aD<0&&aE.datamin!=null&&aE.datamin>=0){aD=0}}if(aF.max==null){aB+=aH*aG;if(aB>0&&aE.datamax!=null&&aE.datamax<=0){aB=0}}}}aE.min=aD;aE.max=aB}function S(aG){var aM=aG.options;var aH;if(typeof aM.ticks=="number"&&aM.ticks>0){aH=aM.ticks}else{aH=0.3*Math.sqrt(aG.direction=="x"?G:I)}var aT=(aG.max-aG.min)/aH,aO,aB,aN,aR,aS,aQ,aI;if(aM.mode=="time"){var aJ={second:1000,minute:60*1000,hour:60*60*1000,day:24*60*60*1000,month:30*24*60*60*1000,year:365.2425*24*60*60*1000};var aK=[[1,"second"],[2,"second"],[5,"second"],[10,"second"],[30,"second"],[1,"minute"],[2,"minute"],[5,"minute"],[10,"minute"],[30,"minute"],[1,"hour"],[2,"hour"],[4,"hour"],[8,"hour"],[12,"hour"],[1,"day"],[2,"day"],[3,"day"],[0.25,"month"],[0.5,"month"],[1,"month"],[2,"month"],[3,"month"],[6,"month"],[1,"year"]];var aC=0;if(aM.minTickSize!=null){if(typeof aM.tickSize=="number"){aC=aM.tickSize}else{aC=aM.minTickSize[0]*aJ[aM.minTickSize[1]]}}for(var aS=0;aS<aK.length-1;++aS){if(aT<(aK[aS][0]*aJ[aK[aS][1]]+aK[aS+1][0]*aJ[aK[aS+1][1]])/2&&aK[aS][0]*aJ[aK[aS][1]]>=aC){break}}aO=aK[aS][0];aN=aK[aS][1];if(aN=="year"){aQ=Math.pow(10,Math.floor(Math.log(aT/aJ.year)/Math.LN10));aI=(aT/aJ.year)/aQ;if(aI<1.5){aO=1}else{if(aI<3){aO=2}else{if(aI<7.5){aO=5}else{aO=10}}}aO*=aQ}aG.tickSize=aM.tickSize||[aO,aN];aB=function(aX){var a2=[],a0=aX.tickSize[0],a3=aX.tickSize[1],a1=new Date(aX.min);var aW=a0*aJ[a3];if(a3=="second"){a1.setUTCSeconds(a(a1.getUTCSeconds(),a0))}if(a3=="minute"){a1.setUTCMinutes(a(a1.getUTCMinutes(),a0))}if(a3=="hour"){a1.setUTCHours(a(a1.getUTCHours(),a0))}if(a3=="month"){a1.setUTCMonth(a(a1.getUTCMonth(),a0))}if(a3=="year"){a1.setUTCFullYear(a(a1.getUTCFullYear(),a0))}a1.setUTCMilliseconds(0);if(aW>=aJ.minute){a1.setUTCSeconds(0)}if(aW>=aJ.hour){a1.setUTCMinutes(0)}if(aW>=aJ.day){a1.setUTCHours(0)}if(aW>=aJ.day*4){a1.setUTCDate(1)}if(aW>=aJ.year){a1.setUTCMonth(0)}var a5=0,a4=Number.NaN,aY;do{aY=a4;a4=a1.getTime();a2.push(a4);if(a3=="month"){if(a0<1){a1.setUTCDate(1);var aV=a1.getTime();a1.setUTCMonth(a1.getUTCMonth()+1);var aZ=a1.getTime();a1.setTime(a4+a5*aJ.hour+(aZ-aV)*a0);a5=a1.getUTCHours();a1.setUTCHours(0)}else{a1.setUTCMonth(a1.getUTCMonth()+a0)}}else{if(a3=="year"){a1.setUTCFullYear(a1.getUTCFullYear()+a0)}else{a1.setTime(a4+aW)}}}while(a4<aX.max&&a4!=aY);return a2};aR=function(aV,aY){var a0=new Date(aV);if(aM.timeformat!=null){return c.plot.formatDate(a0,aM.timeformat,aM.monthNames)}var aW=aY.tickSize[0]*aJ[aY.tickSize[1]];var aX=aY.max-aY.min;var aZ=(aM.twelveHourClock)?" %p":"";if(aW<aJ.minute){fmt="%h:%M:%S"+aZ}else{if(aW<aJ.day){if(aX<2*aJ.day){fmt="%h:%M"+aZ}else{fmt="%b %d %h:%M"+aZ}}else{if(aW<aJ.month){fmt="%b %d"}else{if(aW<aJ.year){if(aX<aJ.year){fmt="%b"}else{fmt="%b %y"}}else{fmt="%y"}}}}return c.plot.formatDate(a0,fmt,aM.monthNames)}}else{var aU=aM.tickDecimals;var aP=-Math.floor(Math.log(aT)/Math.LN10);if(aU!=null&&aP>aU){aP=aU}aQ=Math.pow(10,-aP);aI=aT/aQ;if(aI<1.5){aO=1}else{if(aI<3){aO=2;if(aI>2.25&&(aU==null||aP+1<=aU)){aO=2.5;++aP}}else{if(aI<7.5){aO=5}else{aO=10}}}aO*=aQ;if(aM.minTickSize!=null&&aO<aM.minTickSize){aO=aM.minTickSize}aG.tickDecimals=Math.max(0,aU!=null?aU:aP);aG.tickSize=aM.tickSize||aO;aB=function(aX){var aZ=[];var a0=a(aX.min,aX.tickSize),aW=0,aV=Number.NaN,aY;do{aY=aV;aV=a0+aW*aX.tickSize;aZ.push(aV);++aW}while(aV<aX.max&&aV!=aY);return aZ};aR=function(aV,aW){return aV.toFixed(aW.tickDecimals)}}if(aM.alignTicksWithAxis!=null){var aF=(aG.direction=="x"?p:aw)[aM.alignTicksWithAxis-1];if(aF&&aF.used&&aF!=aG){var aL=aB(aG);if(aL.length>0){if(aM.min==null){aG.min=Math.min(aG.min,aL[0])}if(aM.max==null&&aL.length>1){aG.max=Math.max(aG.max,aL[aL.length-1])}}aB=function(aX){var aY=[],aV,aW;for(aW=0;aW<aF.ticks.length;++aW){aV=(aF.ticks[aW].v-aF.min)/(aF.max-aF.min);aV=aX.min+aV*(aX.max-aX.min);aY.push(aV)}return aY};if(aG.mode!="time"&&aM.tickDecimals==null){var aE=Math.max(0,-Math.floor(Math.log(aT)/Math.LN10)+1),aD=aB(aG);if(!(aD.length>1&&/\..*0$/.test((aD[1]-aD[0]).toFixed(aE)))){aG.tickDecimals=aE}}}}aG.tickGenerator=aB;if(c.isFunction(aM.tickFormatter)){aG.tickFormatter=function(aV,aW){return""+aM.tickFormatter(aV,aW)}}else{aG.tickFormatter=aR}}function P(aF){var aH=aF.options.ticks,aG=[];if(aH==null||(typeof aH=="number"&&aH>0)){aG=aF.tickGenerator(aF)}else{if(aH){if(c.isFunction(aH)){aG=aH({min:aF.min,max:aF.max})}else{aG=aH}}}var aE,aB;aF.ticks=[];for(aE=0;aE<aG.length;++aE){var aC=null;var aD=aG[aE];if(typeof aD=="object"){aB=+aD[0];if(aD.length>1){aC=aD[1]}}else{aB=+aD}if(aC==null){aC=aF.tickFormatter(aB,aF)}if(!isNaN(aB)){aF.ticks.push({v:aB,label:aC})}}}function ap(aB,aC){if(aB.options.autoscaleMargin&&aC.length>0){if(aB.options.min==null){aB.min=Math.min(aB.min,aC[0].v)}if(aB.options.max==null&&aC.length>1){aB.max=Math.max(aB.max,aC[aC.length-1].v)}}}function W(){H.clearRect(0,0,G,I);var aC=O.grid;if(aC.show&&aC.backgroundColor){N()}if(aC.show&&!aC.aboveData){ac()}for(var aB=0;aB<Q.length;++aB){an(ak.drawSeries,[H,Q[aB]]);d(Q[aB])}an(ak.draw,[H]);if(aC.show&&aC.aboveData){ac()}}function D(aB,aI){var aE,aH,aG,aD,aF=m();for(i=0;i<aF.length;++i){aE=aF[i];if(aE.direction==aI){aD=aI+aE.n+"axis";if(!aB[aD]&&aE.n==1){aD=aI+"axis"}if(aB[aD]){aH=aB[aD].from;aG=aB[aD].to;break}}}if(!aB[aD]){aE=aI=="x"?p[0]:aw[0];aH=aB[aI+"1"];aG=aB[aI+"2"]}if(aH!=null&&aG!=null&&aH>aG){var aC=aH;aH=aG;aG=aC}return{from:aH,to:aG,axis:aE}}function N(){H.save();H.translate(q.left,q.top);H.fillStyle=am(O.grid.backgroundColor,w,0,"rgba(255, 255, 255, 0)");H.fillRect(0,0,h,w);H.restore()}function ac(){var aF;H.save();H.translate(q.left,q.top);var aH=O.grid.markings;if(aH){if(c.isFunction(aH)){var aK=aq.getAxes();aK.xmin=aK.xaxis.min;aK.xmax=aK.xaxis.max;aK.ymin=aK.yaxis.min;aK.ymax=aK.yaxis.max;aH=aH(aK)}for(aF=0;aF<aH.length;++aF){var aD=aH[aF],aC=D(aD,"x"),aI=D(aD,"y");if(aC.from==null){aC.from=aC.axis.min}if(aC.to==null){aC.to=aC.axis.max}if(aI.from==null){aI.from=aI.axis.min}if(aI.to==null){aI.to=aI.axis.max}if(aC.to<aC.axis.min||aC.from>aC.axis.max||aI.to<aI.axis.min||aI.from>aI.axis.max){continue}aC.from=Math.max(aC.from,aC.axis.min);aC.to=Math.min(aC.to,aC.axis.max);aI.from=Math.max(aI.from,aI.axis.min);aI.to=Math.min(aI.to,aI.axis.max);if(aC.from==aC.to&&aI.from==aI.to){continue}aC.from=aC.axis.p2c(aC.from);aC.to=aC.axis.p2c(aC.to);aI.from=aI.axis.p2c(aI.from);aI.to=aI.axis.p2c(aI.to);if(aC.from==aC.to||aI.from==aI.to){H.beginPath();H.strokeStyle=aD.color||O.grid.markingsColor;H.lineWidth=aD.lineWidth||O.grid.markingsLineWidth;H.moveTo(aC.from,aI.from);H.lineTo(aC.to,aI.to);H.stroke()}else{H.fillStyle=aD.color||O.grid.markingsColor;H.fillRect(aC.from,aI.to,aC.to-aC.from,aI.from-aI.to)}}}var aK=m(),aM=O.grid.borderWidth;for(var aE=0;aE<aK.length;++aE){var aB=aK[aE],aG=aB.box,aQ=aB.tickLength,aN,aL,aP,aJ;if(!aB.show||aB.ticks.length==0){continue}H.strokeStyle=aB.options.tickColor||c.color.parse(aB.options.color).scale("a",0.22).toString();H.lineWidth=1;if(aB.direction=="x"){aN=0;if(aQ=="full"){aL=(aB.position=="top"?0:w)}else{aL=aG.top-q.top+(aB.position=="top"?aG.height:0)}}else{aL=0;if(aQ=="full"){aN=(aB.position=="left"?0:h)}else{aN=aG.left-q.left+(aB.position=="left"?aG.width:0)}}if(!aB.innermost){H.beginPath();aP=aJ=0;if(aB.direction=="x"){aP=h}else{aJ=w}if(H.lineWidth==1){aN=Math.floor(aN)+0.5;aL=Math.floor(aL)+0.5}H.moveTo(aN,aL);H.lineTo(aN+aP,aL+aJ);H.stroke()}H.beginPath();for(aF=0;aF<aB.ticks.length;++aF){var aO=aB.ticks[aF].v;aP=aJ=0;if(aO<aB.min||aO>aB.max||(aQ=="full"&&aM>0&&(aO==aB.min||aO==aB.max))){continue}if(aB.direction=="x"){aN=aB.p2c(aO);aJ=aQ=="full"?-w:aQ;if(aB.position=="top"){aJ=-aJ}}else{aL=aB.p2c(aO);aP=aQ=="full"?-h:aQ;if(aB.position=="left"){aP=-aP}}if(H.lineWidth==1){if(aB.direction=="x"){aN=Math.floor(aN)+0.5}else{aL=Math.floor(aL)+0.5}}H.moveTo(aN,aL);H.lineTo(aN+aP,aL+aJ)}H.stroke()}if(aM){H.lineWidth=aM;H.strokeStyle=O.grid.borderColor;H.strokeRect(-aM/2,-aM/2,h+aM,w+aM)}H.restore()}function k(){av.find(".tickLabels").remove();var aG=['<div class="tickLabels" style="font-size:smaller">'];var aJ=m();for(var aD=0;aD<aJ.length;++aD){var aC=aJ[aD],aF=aC.box;if(!aC.show){continue}aG.push('<div class="'+aC.direction+"Axis "+aC.direction+aC.n+'Axis" style="color:'+aC.options.color+'">');for(var aE=0;aE<aC.ticks.length;++aE){var aH=aC.ticks[aE];if(!aH.label||aH.v<aC.min||aH.v>aC.max){continue}var aK={},aI;if(aC.direction=="x"){aI="center";aK.left=Math.round(q.left+aC.p2c(aH.v)-aC.labelWidth/2);if(aC.position=="bottom"){aK.top=aF.top+aF.padding}else{aK.bottom=I-(aF.top+aF.height-aF.padding)}}else{aK.top=Math.round(q.top+aC.p2c(aH.v)-aC.labelHeight/2);if(aC.position=="left"){aK.right=G-(aF.left+aF.width-aF.padding);aI="right"}else{aK.left=aF.left+aF.padding;aI="left"}}aK.width=aC.labelWidth;var aB=["position:absolute","text-align:"+aI];for(var aL in aK){aB.push(aL+":"+aK[aL]+"px")}aG.push('<div class="tickLabel" style="'+aB.join(";")+'">'+aH.label+"</div>")}aG.push("</div>")}aG.push("</div>");av.append(aG.join(""))}function d(aB){if(aB.lines.show){at(aB)}if(aB.bars.show){e(aB)}if(aB.points.show){ao(aB)}}function at(aE){function aD(aP,aQ,aI,aU,aT){var aV=aP.points,aJ=aP.pointsize,aN=null,aM=null;H.beginPath();for(var aO=aJ;aO<aV.length;aO+=aJ){var aL=aV[aO-aJ],aS=aV[aO-aJ+1],aK=aV[aO],aR=aV[aO+1];if(aL==null||aK==null){continue}if(aS<=aR&&aS<aT.min){if(aR<aT.min){continue}aL=(aT.min-aS)/(aR-aS)*(aK-aL)+aL;aS=aT.min}else{if(aR<=aS&&aR<aT.min){if(aS<aT.min){continue}aK=(aT.min-aS)/(aR-aS)*(aK-aL)+aL;aR=aT.min}}if(aS>=aR&&aS>aT.max){if(aR>aT.max){continue}aL=(aT.max-aS)/(aR-aS)*(aK-aL)+aL;aS=aT.max}else{if(aR>=aS&&aR>aT.max){if(aS>aT.max){continue}aK=(aT.max-aS)/(aR-aS)*(aK-aL)+aL;aR=aT.max}}if(aL<=aK&&aL<aU.min){if(aK<aU.min){continue}aS=(aU.min-aL)/(aK-aL)*(aR-aS)+aS;aL=aU.min}else{if(aK<=aL&&aK<aU.min){if(aL<aU.min){continue}aR=(aU.min-aL)/(aK-aL)*(aR-aS)+aS;aK=aU.min}}if(aL>=aK&&aL>aU.max){if(aK>aU.max){continue}aS=(aU.max-aL)/(aK-aL)*(aR-aS)+aS;aL=aU.max}else{if(aK>=aL&&aK>aU.max){if(aL>aU.max){continue}aR=(aU.max-aL)/(aK-aL)*(aR-aS)+aS;aK=aU.max}}if(aL!=aN||aS!=aM){H.moveTo(aU.p2c(aL)+aQ,aT.p2c(aS)+aI)}aN=aK;aM=aR;H.lineTo(aU.p2c(aK)+aQ,aT.p2c(aR)+aI)}H.stroke()}function aF(aI,aQ,aP){var aW=aI.points,aV=aI.pointsize,aN=Math.min(Math.max(0,aP.min),aP.max),aX=0,aU,aT=false,aM=1,aL=0,aR=0;while(true){if(aV>0&&aX>aW.length+aV){break}aX+=aV;var aZ=aW[aX-aV],aK=aW[aX-aV+aM],aY=aW[aX],aJ=aW[aX+aM];if(aT){if(aV>0&&aZ!=null&&aY==null){aR=aX;aV=-aV;aM=2;continue}if(aV<0&&aX==aL+aV){H.fill();aT=false;aV=-aV;aM=1;aX=aL=aR+aV;continue}}if(aZ==null||aY==null){continue}if(aZ<=aY&&aZ<aQ.min){if(aY<aQ.min){continue}aK=(aQ.min-aZ)/(aY-aZ)*(aJ-aK)+aK;aZ=aQ.min}else{if(aY<=aZ&&aY<aQ.min){if(aZ<aQ.min){continue}aJ=(aQ.min-aZ)/(aY-aZ)*(aJ-aK)+aK;aY=aQ.min}}if(aZ>=aY&&aZ>aQ.max){if(aY>aQ.max){continue}aK=(aQ.max-aZ)/(aY-aZ)*(aJ-aK)+aK;aZ=aQ.max}else{if(aY>=aZ&&aY>aQ.max){if(aZ>aQ.max){continue}aJ=(aQ.max-aZ)/(aY-aZ)*(aJ-aK)+aK;aY=aQ.max}}if(!aT){H.beginPath();H.moveTo(aQ.p2c(aZ),aP.p2c(aN));aT=true}if(aK>=aP.max&&aJ>=aP.max){H.lineTo(aQ.p2c(aZ),aP.p2c(aP.max));H.lineTo(aQ.p2c(aY),aP.p2c(aP.max));continue}else{if(aK<=aP.min&&aJ<=aP.min){H.lineTo(aQ.p2c(aZ),aP.p2c(aP.min));H.lineTo(aQ.p2c(aY),aP.p2c(aP.min));continue}}var aO=aZ,aS=aY;if(aK<=aJ&&aK<aP.min&&aJ>=aP.min){aZ=(aP.min-aK)/(aJ-aK)*(aY-aZ)+aZ;aK=aP.min}else{if(aJ<=aK&&aJ<aP.min&&aK>=aP.min){aY=(aP.min-aK)/(aJ-aK)*(aY-aZ)+aZ;aJ=aP.min}}if(aK>=aJ&&aK>aP.max&&aJ<=aP.max){aZ=(aP.max-aK)/(aJ-aK)*(aY-aZ)+aZ;aK=aP.max}else{if(aJ>=aK&&aJ>aP.max&&aK<=aP.max){aY=(aP.max-aK)/(aJ-aK)*(aY-aZ)+aZ;aJ=aP.max}}if(aZ!=aO){H.lineTo(aQ.p2c(aO),aP.p2c(aK))}H.lineTo(aQ.p2c(aZ),aP.p2c(aK));H.lineTo(aQ.p2c(aY),aP.p2c(aJ));if(aY!=aS){H.lineTo(aQ.p2c(aY),aP.p2c(aJ));H.lineTo(aQ.p2c(aS),aP.p2c(aJ))}}}H.save();H.translate(q.left,q.top);H.lineJoin="round";var aG=aE.lines.lineWidth,aB=aE.shadowSize;if(aG>0&&aB>0){H.lineWidth=aB;H.strokeStyle="rgba(0,0,0,0.1)";var aH=Math.PI/18;aD(aE.datapoints,Math.sin(aH)*(aG/2+aB/2),Math.cos(aH)*(aG/2+aB/2),aE.xaxis,aE.yaxis);H.lineWidth=aB/2;aD(aE.datapoints,Math.sin(aH)*(aG/2+aB/4),Math.cos(aH)*(aG/2+aB/4),aE.xaxis,aE.yaxis)}H.lineWidth=aG;H.strokeStyle=aE.color;var aC=ae(aE.lines,aE.color,0,w);if(aC){H.fillStyle=aC;aF(aE.datapoints,aE.xaxis,aE.yaxis)}if(aG>0){aD(aE.datapoints,0,0,aE.xaxis,aE.yaxis)}H.restore()}function ao(aE){function aH(aN,aM,aU,aK,aS,aT,aQ,aJ){var aR=aN.points,aI=aN.pointsize;for(var aL=0;aL<aR.length;aL+=aI){var aP=aR[aL],aO=aR[aL+1];if(aP==null||aP<aT.min||aP>aT.max||aO<aQ.min||aO>aQ.max){continue}H.beginPath();aP=aT.p2c(aP);aO=aQ.p2c(aO)+aK;if(aJ=="circle"){H.arc(aP,aO,aM,0,aS?Math.PI:Math.PI*2,false)}else{aJ(H,aP,aO,aM,aS)}H.closePath();if(aU){H.fillStyle=aU;H.fill()}H.stroke()}}H.save();H.translate(q.left,q.top);var aG=aE.points.lineWidth,aC=aE.shadowSize,aB=aE.points.radius,aF=aE.points.symbol;if(aG>0&&aC>0){var aD=aC/2;H.lineWidth=aD;H.strokeStyle="rgba(0,0,0,0.1)";aH(aE.datapoints,aB,null,aD+aD/2,true,aE.xaxis,aE.yaxis,aF);H.strokeStyle="rgba(0,0,0,0.2)";aH(aE.datapoints,aB,null,aD/2,true,aE.xaxis,aE.yaxis,aF)}H.lineWidth=aG;H.strokeStyle=aE.color;aH(aE.datapoints,aB,ae(aE.points,aE.color),0,false,aE.xaxis,aE.yaxis,aF);H.restore()}function E(aN,aM,aV,aI,aQ,aF,aD,aL,aK,aU,aR,aC){var aE,aT,aJ,aP,aG,aB,aO,aH,aS;if(aR){aH=aB=aO=true;aG=false;aE=aV;aT=aN;aP=aM+aI;aJ=aM+aQ;if(aT<aE){aS=aT;aT=aE;aE=aS;aG=true;aB=false}}else{aG=aB=aO=true;aH=false;aE=aN+aI;aT=aN+aQ;aJ=aV;aP=aM;if(aP<aJ){aS=aP;aP=aJ;aJ=aS;aH=true;aO=false}}if(aT<aL.min||aE>aL.max||aP<aK.min||aJ>aK.max){return}if(aE<aL.min){aE=aL.min;aG=false}if(aT>aL.max){aT=aL.max;aB=false}if(aJ<aK.min){aJ=aK.min;aH=false}if(aP>aK.max){aP=aK.max;aO=false}aE=aL.p2c(aE);aJ=aK.p2c(aJ);aT=aL.p2c(aT);aP=aK.p2c(aP);if(aD){aU.beginPath();aU.moveTo(aE,aJ);aU.lineTo(aE,aP);aU.lineTo(aT,aP);aU.lineTo(aT,aJ);aU.fillStyle=aD(aJ,aP);aU.fill()}if(aC>0&&(aG||aB||aO||aH)){aU.beginPath();aU.moveTo(aE,aJ+aF);if(aG){aU.lineTo(aE,aP+aF)}else{aU.moveTo(aE,aP+aF)}if(aO){aU.lineTo(aT,aP+aF)}else{aU.moveTo(aT,aP+aF)}if(aB){aU.lineTo(aT,aJ+aF)}else{aU.moveTo(aT,aJ+aF)}if(aH){aU.lineTo(aE,aJ+aF)}else{aU.moveTo(aE,aJ+aF)}aU.stroke()}}function e(aD){function aC(aJ,aI,aL,aG,aK,aN,aM){var aO=aJ.points,aF=aJ.pointsize;for(var aH=0;aH<aO.length;aH+=aF){if(aO[aH]==null){continue}E(aO[aH],aO[aH+1],aO[aH+2],aI,aL,aG,aK,aN,aM,H,aD.bars.horizontal,aD.bars.lineWidth)}}H.save();H.translate(q.left,q.top);H.lineWidth=aD.bars.lineWidth;H.strokeStyle=aD.color;var aB=aD.bars.align=="left"?0:-aD.bars.barWidth/2;var aE=aD.bars.fill?function(aF,aG){return ae(aD.bars,aD.color,aF,aG)}:null;aC(aD.datapoints,aB,aB+aD.bars.barWidth,0,aE,aD.xaxis,aD.yaxis);H.restore()}function ae(aD,aB,aC,aF){var aE=aD.fill;if(!aE){return null}if(aD.fillColor){return am(aD.fillColor,aC,aF,aB)}var aG=c.color.parse(aB);aG.a=typeof aE=="number"?aE:0.4;aG.normalize();return aG.toString()}function o(){av.find(".legend").remove();if(!O.legend.show){return}var aH=[],aF=false,aN=O.legend.labelFormatter,aM,aJ;for(var aE=0;aE<Q.length;++aE){aM=Q[aE];aJ=aM.label;if(!aJ){continue}if(aE%O.legend.noColumns==0){if(aF){aH.push("</tr>")}aH.push("<tr>");aF=true}if(aN){aJ=aN(aJ,aM)}aH.push('<td class="legendColorBox"><div style="border:1px solid '+O.legend.labelBoxBorderColor+';padding:1px"><div style="width:4px;height:0;border:5px solid '+aM.color+';overflow:hidden"></div></div></td><td class="legendLabel">'+aJ+"</td>")}if(aF){aH.push("</tr>")}if(aH.length==0){return}var aL='<table style="font-size:smaller;color:'+O.grid.color+'">'+aH.join("")+"</table>";if(O.legend.container!=null){c(O.legend.container).html(aL)}else{var aI="",aC=O.legend.position,aD=O.legend.margin;if(aD[0]==null){aD=[aD,aD]}if(aC.charAt(0)=="n"){aI+="top:"+(aD[1]+q.top)+"px;"}else{if(aC.charAt(0)=="s"){aI+="bottom:"+(aD[1]+q.bottom)+"px;"}}if(aC.charAt(1)=="e"){aI+="right:"+(aD[0]+q.right)+"px;"}else{if(aC.charAt(1)=="w"){aI+="left:"+(aD[0]+q.left)+"px;"}}var aK=c('<div class="legend">'+aL.replace('style="','style="position:absolute;'+aI+";")+"</div>").appendTo(av);if(O.legend.backgroundOpacity!=0){var aG=O.legend.backgroundColor;if(aG==null){aG=O.grid.backgroundColor;if(aG&&typeof aG=="string"){aG=c.color.parse(aG)}else{aG=c.color.extract(aK,"background-color")}aG.a=1;aG=aG.toString()}var aB=aK.children();c('<div style="position:absolute;width:'+aB.width()+"px;height:"+aB.height()+"px;"+aI+"background-color:"+aG+';"> </div>').prependTo(aK).css("opacity",O.legend.backgroundOpacity)}}}var ab=[],M=null;function K(aI,aG,aD){var aO=O.grid.mouseActiveRadius,a0=aO*aO+1,aY=null,aR=false,aW,aU;for(aW=Q.length-1;aW>=0;--aW){if(!aD(Q[aW])){continue}var aP=Q[aW],aH=aP.xaxis,aF=aP.yaxis,aV=aP.datapoints.points,aT=aP.datapoints.pointsize,aQ=aH.c2p(aI),aN=aF.c2p(aG),aC=aO/aH.scale,aB=aO/aF.scale;if(aH.options.inverseTransform){aC=Number.MAX_VALUE}if(aF.options.inverseTransform){aB=Number.MAX_VALUE}if(aP.lines.show||aP.points.show){for(aU=0;aU<aV.length;aU+=aT){var aK=aV[aU],aJ=aV[aU+1];if(aK==null){continue}if(aK-aQ>aC||aK-aQ<-aC||aJ-aN>aB||aJ-aN<-aB){continue}var aM=Math.abs(aH.p2c(aK)-aI),aL=Math.abs(aF.p2c(aJ)-aG),aS=aM*aM+aL*aL;if(aS<a0){a0=aS;aY=[aW,aU/aT]}}}if(aP.bars.show&&!aY){var aE=aP.bars.align=="left"?0:-aP.bars.barWidth/2,aX=aE+aP.bars.barWidth;for(aU=0;aU<aV.length;aU+=aT){var aK=aV[aU],aJ=aV[aU+1],aZ=aV[aU+2];if(aK==null){continue}if(Q[aW].bars.horizontal?(aQ<=Math.max(aZ,aK)&&aQ>=Math.min(aZ,aK)&&aN>=aJ+aE&&aN<=aJ+aX):(aQ>=aK+aE&&aQ<=aK+aX&&aN>=Math.min(aZ,aJ)&&aN<=Math.max(aZ,aJ))){aY=[aW,aU/aT]}}}}if(aY){aW=aY[0];aU=aY[1];aT=Q[aW].datapoints.pointsize;return{datapoint:Q[aW].datapoints.points.slice(aU*aT,(aU+1)*aT),dataIndex:aU,series:Q[aW],seriesIndex:aW}}return null}function aa(aB){if(O.grid.hoverable){u("plothover",aB,function(aC){return aC.hoverable!=false})}}function l(aB){if(O.grid.hoverable){u("plothover",aB,function(aC){return false})}}function R(aB){u("plotclick",aB,function(aC){return aC.clickable!=false})}function u(aC,aB,aD){var aE=y.offset(),aH=aB.pageX-aE.left-q.left,aF=aB.pageY-aE.top-q.top,aJ=C({left:aH,top:aF});aJ.pageX=aB.pageX;aJ.pageY=aB.pageY;var aK=K(aH,aF,aD);if(aK){aK.pageX=parseInt(aK.series.xaxis.p2c(aK.datapoint[0])+aE.left+q.left);aK.pageY=parseInt(aK.series.yaxis.p2c(aK.datapoint[1])+aE.top+q.top)}if(O.grid.autoHighlight){for(var aG=0;aG<ab.length;++aG){var aI=ab[aG];if(aI.auto==aC&&!(aK&&aI.series==aK.series&&aI.point[0]==aK.datapoint[0]&&aI.point[1]==aK.datapoint[1])){T(aI.series,aI.point)}}if(aK){x(aK.series,aK.datapoint,aC)}}av.trigger(aC,[aJ,aK])}function f(){if(!M){M=setTimeout(s,30)}}function s(){M=null;A.save();A.clearRect(0,0,G,I);A.translate(q.left,q.top);var aC,aB;for(aC=0;aC<ab.length;++aC){aB=ab[aC];if(aB.series.bars.show){v(aB.series,aB.point)}else{ay(aB.series,aB.point)}}A.restore();an(ak.drawOverlay,[A])}function x(aD,aB,aF){if(typeof aD=="number"){aD=Q[aD]}if(typeof aB=="number"){var aE=aD.datapoints.pointsize;aB=aD.datapoints.points.slice(aE*aB,aE*(aB+1))}var aC=al(aD,aB);if(aC==-1){ab.push({series:aD,point:aB,auto:aF});f()}else{if(!aF){ab[aC].auto=false}}}function T(aD,aB){if(aD==null&&aB==null){ab=[];f()}if(typeof aD=="number"){aD=Q[aD]}if(typeof aB=="number"){aB=aD.data[aB]}var aC=al(aD,aB);if(aC!=-1){ab.splice(aC,1);f()}}function al(aD,aE){for(var aB=0;aB<ab.length;++aB){var aC=ab[aB];if(aC.series==aD&&aC.point[0]==aE[0]&&aC.point[1]==aE[1]){return aB}}return -1}function ay(aE,aD){var aC=aD[0],aI=aD[1],aH=aE.xaxis,aG=aE.yaxis;if(aC<aH.min||aC>aH.max||aI<aG.min||aI>aG.max){return}var aF=aE.points.radius+aE.points.lineWidth/2;A.lineWidth=aF;A.strokeStyle=c.color.parse(aE.color).scale("a",0.5).toString();var aB=1.5*aF,aC=aH.p2c(aC),aI=aG.p2c(aI);A.beginPath();if(aE.points.symbol=="circle"){A.arc(aC,aI,aB,0,2*Math.PI,false)}else{aE.points.symbol(A,aC,aI,aB,false)}A.closePath();A.stroke()}function v(aE,aB){A.lineWidth=aE.bars.lineWidth;A.strokeStyle=c.color.parse(aE.color).scale("a",0.5).toString();var aD=c.color.parse(aE.color).scale("a",0.5).toString();var aC=aE.bars.align=="left"?0:-aE.bars.barWidth/2;E(aB[0],aB[1],aB[2]||0,aC,aC+aE.bars.barWidth,0,function(){return aD},aE.xaxis,aE.yaxis,A,aE.bars.horizontal,aE.bars.lineWidth)}function am(aJ,aB,aH,aC){if(typeof aJ=="string"){return aJ}else{var aI=H.createLinearGradient(0,aH,0,aB);for(var aE=0,aD=aJ.colors.length;aE<aD;++aE){var aF=aJ.colors[aE];if(typeof aF!="string"){var aG=c.color.parse(aC);if(aF.brightness!=null){aG=aG.scale("rgb",aF.brightness)}if(aF.opacity!=null){aG.a*=aF.opacity}aF=aG.toString()}aI.addColorStop(aE/(aD-1),aF)}return aI}}}c.plot=function(g,e,d){var f=new b(c(g),e,d,c.plot.plugins);return f};c.plot.version="0.7";c.plot.plugins=[];c.plot.formatDate=function(l,f,h){var o=function(d){d=""+d;return d.length==1?"0"+d:d};var e=[];var p=false,j=false;var n=l.getUTCHours();var k=n<12;if(h==null){h=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}if(f.search(/%p|%P/)!=-1){if(n>12){n=n-12}else{if(n==0){n=12}}}for(var g=0;g<f.length;++g){var m=f.charAt(g);if(p){switch(m){case"h":m=""+n;break;case"H":m=o(n);break;case"M":m=o(l.getUTCMinutes());break;case"S":m=o(l.getUTCSeconds());break;case"d":m=""+l.getUTCDate();break;case"m":m=""+(l.getUTCMonth()+1);break;case"y":m=""+l.getUTCFullYear();break;case"b":m=""+h[l.getUTCMonth()];break;case"p":m=(k)?("am"):("pm");break;case"P":m=(k)?("AM"):("PM");break;case"0":m="";j=true;break}if(m&&j){m=o(m);j=false}e.push(m);if(!j){p=false}}else{if(m=="%"){p=true}else{e.push(m)}}}return e.join("")};function a(e,d){return d*Math.floor(e/d)}})(jQuery); \ No newline at end of file
diff --git a/storage/mroonga/vendor/groonga/data/html/admin/js/jquery.flot.license.txt b/storage/mroonga/vendor/groonga/data/html/admin/js/jquery.flot.license.txt
new file mode 100644
index 00000000000..07d5b2094d1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/admin/js/jquery.flot.license.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2007-2009 IOLA and Ole Laursen
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/storage/mroonga/vendor/groonga/data/html/files.am b/storage/mroonga/vendor/groonga/data/html/files.am
new file mode 100644
index 00000000000..401b98c46fd
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/files.am
@@ -0,0 +1,34 @@
+nobase_dist_html_DATA = \
+ admin/css/groonga-admin.css \
+ admin/css/redmond/images/ui-bg_flat_0_aaaaaa_40x100.png \
+ admin/css/redmond/images/ui-bg_flat_55_fbec88_40x100.png \
+ admin/css/redmond/images/ui-bg_glass_75_d0e5f5_1x400.png \
+ admin/css/redmond/images/ui-bg_glass_85_dfeffc_1x400.png \
+ admin/css/redmond/images/ui-bg_glass_95_fef1ec_1x400.png \
+ admin/css/redmond/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png \
+ admin/css/redmond/images/ui-bg_inset-hard_100_f5f8f9_1x100.png \
+ admin/css/redmond/images/ui-bg_inset-hard_100_fcfdfd_1x100.png \
+ admin/css/redmond/images/ui-icons_217bc0_256x240.png \
+ admin/css/redmond/images/ui-icons_2e83ff_256x240.png \
+ admin/css/redmond/images/ui-icons_469bdd_256x240.png \
+ admin/css/redmond/images/ui-icons_6da8d5_256x240.png \
+ admin/css/redmond/images/ui-icons_cd0a0a_256x240.png \
+ admin/css/redmond/images/ui-icons_d8e7f3_256x240.png \
+ admin/css/redmond/images/ui-icons_f9bd01_256x240.png \
+ admin/css/redmond/jquery-ui-1.8.18.custom.css \
+ admin/favicon.ico \
+ admin/favicon.png \
+ admin/favicon.svg \
+ admin/images/groonga.png \
+ admin/images/groonga.svg \
+ admin/images/loading.gif \
+ admin/index.html \
+ admin/index.ja.html \
+ admin/js/groonga-admin.ja.js \
+ admin/js/groonga-admin.js \
+ admin/js/jquery-1.7.2.min.js \
+ admin/js/jquery-ui-1.8.18.custom.min.js \
+ admin/js/jquery.flot-0.7.min.js \
+ admin/js/jquery.flot.license.txt \
+ $(NULL)
+
diff --git a/storage/mroonga/vendor/groonga/data/html/update-files.sh b/storage/mroonga/vendor/groonga/data/html/update-files.sh
new file mode 100755
index 00000000000..7d72fe2fe19
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/html/update-files.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+list_paths()
+{
+ variable_name=$1
+ echo "$variable_name = \\"
+ sort | \
+ sed \
+ -e 's,^,\t,' \
+ -e 's,$, \\,'
+ echo "\t\$(NULL)"
+ echo
+}
+
+find "admin" -type f | \
+ sort | \
+ list_paths "nobase_dist_html_DATA"
diff --git a/storage/mroonga/vendor/groonga/data/images/Makefile.am b/storage/mroonga/vendor/groonga/data/images/Makefile.am
new file mode 100644
index 00000000000..615d8f93fc4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = \
+ logo
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/Makefile.am b/storage/mroonga/vendor/groonga/data/images/logo/Makefile.am
new file mode 100644
index 00000000000..d5c97b03ab3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/Makefile.am
@@ -0,0 +1,22 @@
+include files.am
+
+logodir = $(pkgdatadir)/images/logo
+logo_DATA = $(image_files)
+
+EXTRA_DIST = \
+ $(logo_DATA) \
+ update-files.sh
+
+SUFFIXES =
+if WITH_INKSCAPE
+SUFFIXES += .svg .png
+.svg.png:
+ $(INKSCAPE) --export-dpi 90 --export-png $@ $<
+endif
+
+$(srcdir)/files.am: $(srcdir)/update-files.sh
+ (cd $(srcdir) && ./update-files.sh) > $(srcdir)/files.am
+
+update-files:
+ rm $(srcdir)/files.am
+ $(MAKE) $(srcdir)/files.am
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/files.am b/storage/mroonga/vendor/groonga/data/images/logo/files.am
new file mode 100644
index 00000000000..841ac55f2f5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/files.am
@@ -0,0 +1,53 @@
+image_files = \
+ groonga-icon-foreground-white.png \
+ groonga-icon-foreground-white.svg \
+ groonga-icon-full-size.png \
+ groonga-icon-full-size.svg \
+ groonga-icon.png \
+ groonga-icon.svg \
+ groonga-logo-foreground-white.png \
+ groonga-logo-foreground-white.svg \
+ groonga-logo.png \
+ groonga-logo.svg \
+ groonga-powered-by-banner-bar-foreground-white.png \
+ groonga-powered-by-banner-bar-foreground-white.svg \
+ groonga-powered-by-banner-bar.png \
+ groonga-powered-by-banner-bar.svg \
+ groonga-powered-by-banner-foreground-white.png \
+ groonga-powered-by-banner-foreground-white.svg \
+ groonga-powered-by-banner-large.png \
+ groonga-powered-by-banner-large.svg \
+ groonga-powered-by-banner.png \
+ groonga-powered-by-banner.svg \
+ mroonga-icon-foreground-white.png \
+ mroonga-icon-foreground-white.svg \
+ mroonga-icon-full-size.png \
+ mroonga-icon-full-size.svg \
+ mroonga-icon.png \
+ mroonga-icon.svg \
+ mroonga-logo-foreground-white.png \
+ mroonga-logo-foreground-white.svg \
+ mroonga-logo.png \
+ mroonga-logo.svg \
+ nroonga-icon-foreground-white.png \
+ nroonga-icon-foreground-white.svg \
+ nroonga-icon-full-size.png \
+ nroonga-icon-full-size.svg \
+ nroonga-icon.png \
+ nroonga-icon.svg \
+ nroonga-logo-foreground-white.png \
+ nroonga-logo-foreground-white.svg \
+ nroonga-logo.png \
+ nroonga-logo.svg \
+ rroonga-icon-foreground-white.png \
+ rroonga-icon-foreground-white.svg \
+ rroonga-icon-full-size.png \
+ rroonga-icon-full-size.svg \
+ rroonga-icon.png \
+ rroonga-icon.svg \
+ rroonga-logo-foreground-white.png \
+ rroonga-logo-foreground-white.svg \
+ rroonga-logo.png \
+ rroonga-logo.svg \
+ $(NULL)
+
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-foreground-white.png b/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-foreground-white.png
new file mode 100644
index 00000000000..31ee95ba5be
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-foreground-white.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-foreground-white.svg b/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-foreground-white.svg
new file mode 100644
index 00000000000..9981e64df19
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-foreground-white.svg
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="60.000706"
+ height="60.001022"
+ id="svg5342"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 41">
+ <defs
+ id="defs5344">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_9_"
+ id="linearGradient5521"
+ gradientUnits="userSpaceOnUse"
+ x1="470.15039"
+ y1="437.05859"
+ x2="470.15039"
+ y2="497.05859" />
+ <linearGradient
+ id="SVGID_9_"
+ gradientUnits="userSpaceOnUse"
+ x1="470.15039"
+ y1="437.05859"
+ x2="470.15039"
+ y2="497.05859">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop4050" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop4052" />
+ </linearGradient>
+ <linearGradient
+ y2="497.05859"
+ x2="470.15039"
+ y1="437.05859"
+ x1="470.15039"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5566"
+ xlink:href="#SVGID_9_"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_9_"
+ id="linearGradient5599"
+ gradientUnits="userSpaceOnUse"
+ x1="470.15039"
+ y1="437.05859"
+ x2="470.15039"
+ y2="497.05859" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="61.872887"
+ inkscape:cy="42.401151"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2773"
+ inkscape:window-y="302"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata5347">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-343.75609,-530.41738)">
+ <g
+ id="g5587">
+ <polygon
+ style="fill:url(#linearGradient5599)"
+ id="polygon4054"
+ points="440.15,437.059 440.15,497.059 466.012,497.059 500.15,497.059 500.15,462.92 500.15,437.059 "
+ transform="translate(-96.393911,93.359403)" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ id="path4107"
+ d="m 397.29009,567.3004 c 7.502,-7.505 9.8,-20.401 -0.335,-30.538 -4.224,-4.222 -9.514,-6.337 -14.8,-6.345 h -0.005 c -5.286,-0.007 -10.567,2.093 -14.774,6.299 -2.522,2.52 -4.434,5.76 -5.428,9.33 -6.49,-0.099 -13.001,2.411 -18.191,7.53 v 36.479 c 0.114,0.121 0.227,0.242 0.344,0.361 h 25.518 11.027 c 4.434,-4.717 7.233,-11.018 7.295,-17.672 3.385,-0.898 6.606,-2.7 9.349,-5.444 z m -13.748,-2.759 c -1.121,1.873 -0.997,2.089 -0.949,5.602 0.062,3.979 -1.437,8.102 -5.189,11.854 -5.736,5.737 -16.326,7.017 -24.119,-0.776 -6.139,-6.14 -9.021,-16.475 -0.84,-24.658 3.038,-3.034 7.406,-4.827 11.998,-4.745 3.193,0.055 3.564,0.174 5.621,-0.76 2.058,-0.938 1.166,-3.019 2.396,-6.692 0.6,-1.792 1.666,-3.551 3.332,-5.224 4.451,-4.448 12.643,-5.469 18.643,0.531 4.887,4.887 6.96,12.677 0.572,19.065 -1.441,1.444 -3.284,2.509 -5.319,3.087 -3.266,0.937 -5.023,0.838 -6.146,2.716 z" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-full-size.png b/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-full-size.png
new file mode 100644
index 00000000000..a1e60fca5b1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-full-size.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-full-size.svg b/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-full-size.svg
new file mode 100644
index 00000000000..1aea95e737f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon-full-size.svg
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="199.804"
+ height="200.85736"
+ id="svg3635"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="groonga-icon-full-size.svg"
+ inkscape:export-filename="groonga-icon-full-size.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs3637">
+ <linearGradient
+ gradientTransform="translate(-419.62671,208.8676)"
+ id="SVGID_1_"
+ gradientUnits="userSpaceOnUse"
+ x1="223.6167"
+ y1="76.3564"
+ x2="223.6167"
+ y2="137.38029">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop160" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop162" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_1_"
+ id="linearGradient2994"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(3.0240925,0,0,3.0240925,-289.39052,88.167588)"
+ x1="223.6167"
+ y1="76.3564"
+ x2="223.6167"
+ y2="137.38029" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="144.78679"
+ inkscape:cy="33.072478"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5"
+ inkscape:window-width="902"
+ inkscape:window-height="705"
+ inkscape:window-x="2174"
+ inkscape:window-y="122"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata3640">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-286.94516,-317.99663)">
+ <path
+ style="fill:url(#linearGradient2994)"
+ inkscape:connector-curvature="0"
+ d="m 379.72928,340.66117 c -7.06731,7.06429 -12.426,16.1547 -15.21724,26.1705 -18.37438,-0.28124 -36.80623,6.87377 -51.43075,21.5013 -24.62215,24.61914 -32.27916,69.29104 2.01708,103.58424 28.8559,28.85891 71.95223,29.29438 100.23355,1.00702 13.36951,-13.37859 21.88233,-31.77111 22.0668,-51.22813 9.49565,-2.52511 18.52257,-7.58139 26.21888,-15.26864 21.03559,-21.04466 27.47691,-57.2128 -0.94049,-85.63625 -23.68772,-23.67865 -59.35084,-23.73005 -82.94783,-0.13004 z m 77.4833,61.75802 c -4.03716,4.04623 -9.20837,7.028 -14.91483,8.66101 -9.15393,2.62188 -14.0832,2.34972 -17.23128,7.60558 -3.14808,5.24681 -2.80031,5.86675 -2.66121,15.70714 0.16935,11.15588 -4.03111,22.72001 -14.55495,33.2499 -16.08818,16.08212 -45.78476,19.6687 -67.63685,-2.18037 -17.21617,-17.21918 -25.29654,-46.20209 -2.35275,-69.14587 8.51585,-8.50678 20.76342,-13.53887 33.64001,-13.31207 8.95434,0.16028 9.99765,0.48689 15.76156,-2.12895 5.773,-2.62794 3.27207,-8.46443 6.71955,-18.7645 1.68139,-5.01999 4.67221,-9.96136 9.35049,-14.6487 12.4774,-12.47741 35.44539,-15.33821 52.27144,1.48483 13.70821,13.71425 19.52052,35.5603 1.60882,53.472 z"
+ id="path164" />
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon.png b/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon.png
new file mode 100644
index 00000000000..8e73658dfd2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon.svg b/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon.svg
new file mode 100644
index 00000000000..24dcf178c3b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-icon.svg
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="59.99931"
+ height="59.999016"
+ id="svg5041"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 28">
+ <defs
+ id="defs5043">
+ <linearGradient
+ id="SVGID_13_"
+ gradientUnits="userSpaceOnUse"
+ x1="147.7168"
+ y1="437.05859"
+ x2="147.7168"
+ y2="505.11819">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop4080" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop4082" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="translate(283.71192,-26.123877)"
+ y2="505.11819"
+ x2="147.7168"
+ y1="437.05859"
+ x1="147.7168"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5070"
+ xlink:href="#SVGID_13_"
+ inkscape:collect="always" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="-109.40067"
+ inkscape:cy="43.539451"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="1928"
+ inkscape:window-y="52"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata5046">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-401.42892,-410.9341)">
+ <path
+ style="fill:url(#linearGradient5070)"
+ inkscape:connector-curvature="0"
+ d="m 454.96192,447.81712 c 7.502,-7.505 9.799,-20.401 -0.335,-30.538 -4.223,-4.222 -9.513,-6.337 -14.799,-6.345 h -0.005 c -5.286,-0.007 -10.568,2.093 -14.775,6.299 -2.522,2.52 -4.433,5.76 -5.428,9.33 -6.49,-0.099 -13.001,2.411 -18.191,7.53 v 36.48 c 0.114,0.12 0.227,0.241 0.344,0.36 h 25.518 11.027 c 4.434,-4.717 7.234,-11.018 7.295,-17.672 3.385,-0.898 6.606,-2.7 9.349,-5.444 z m -13.748,-2.759 c -1.122,1.873 -0.998,2.089 -0.95,5.602 0.062,3.979 -1.436,8.102 -5.188,11.854 -5.737,5.737 -16.327,7.017 -24.119,-0.776 -6.14,-6.14 -9.021,-16.475 -0.84,-24.658 3.038,-3.034 7.405,-4.827 11.998,-4.745 3.193,0.055 3.564,0.174 5.621,-0.76 2.058,-0.938 1.166,-3.019 2.396,-6.692 0.6,-1.792 1.667,-3.551 3.333,-5.224 4.451,-4.448 12.642,-5.469 18.642,0.531 4.887,4.887 6.96,12.677 0.573,19.065 -1.442,1.444 -3.285,2.509 -5.32,3.087 -3.265,0.937 -5.023,0.838 -6.146,2.716 z"
+ id="path4084" />
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-logo-foreground-white.png b/storage/mroonga/vendor/groonga/data/images/logo/groonga-logo-foreground-white.png
new file mode 100644
index 00000000000..a9281780fbf
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-logo-foreground-white.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-logo-foreground-white.svg b/storage/mroonga/vendor/groonga/data/images/logo/groonga-logo-foreground-white.svg
new file mode 100644
index 00000000000..ad82462a218
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-logo-foreground-white.svg
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="232.18001"
+ height="73.112274"
+ id="svg4711"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 16">
+ <defs
+ id="defs4713">
+ <linearGradient
+ gradientTransform="translate(-433.94514,340.29617)"
+ id="SVGID_5_"
+ gradientUnits="userSpaceOnUse"
+ x1="589.36517"
+ y1="78"
+ x2="589.36517"
+ y2="143.0079">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop3978" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop3980" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#000000"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="249.78391"
+ inkscape:cy="36.547406"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2119"
+ inkscape:window-y="331"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata4716">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-61.052857,-412.9489)">
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 122.70286,437.76317 c -1.337,-0.29 -2.28,-0.448 -3.441,-0.448 -2.848,0 -5.33,0.831 -7.393,2.468 -0.09,0.06 -0.191,0.14 -0.302,0.245 -0.23,0.197 -0.452,0.406 -0.669,0.621 -1.477,1.314 -2.759,1.952 -2.759,-1.235 v -0.666 c 0,-0.404 -0.327,-0.732 -0.732,-0.732 h -2.1 c -0.2,0 -0.389,0.081 -0.527,0.223 -0.138,0.143 -0.212,0.335 -0.206,0.532 0.106,3.333 0.215,6.778 0.215,9.621 v 18.75 c 0,0.404 0.328,0.731 0.732,0.731 h 2.209 c 0.403,0 0.732,-0.328 0.732,-0.731 v -9.914 c 0,-1.37 0.107,-3.805 0.31,-4.813 1.568,-7.797 4.982,-11.588 10.435,-11.588 0.979,0 1.88,0.165 2.874,0.363 0.047,0.011 0.095,0.015 0.143,0.015 0.148,0 0.294,-0.045 0.417,-0.131 0.162,-0.113 0.273,-0.288 0.307,-0.484 l 0.322,-1.994 c 0.062,-0.384 -0.187,-0.752 -0.567,-0.833 z"
+ id="path3967" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 211.74986,439.35517 c -2.513,-1.774 -5.294,-2.04 -6.727,-2.04 -2.911,0 -5.648,0.912 -7.821,2.495 -0.005,0.004 -0.011,0.005 -0.015,0.009 -1.006,0.648 -2.627,0.909 -2.617,-0.625 v -0.021 c 0,-0.107 -0.005,-0.199 -0.014,-0.277 l -0.009,-0.182 c -0.018,-0.391 -0.341,-0.698 -0.731,-0.698 h -2.102 c -0.202,0 -0.396,0.084 -0.534,0.232 -0.139,0.148 -0.21,0.347 -0.195,0.549 0.152,2.28 0.212,4.171 0.212,6.524 v 21.821 c 0,0.404 0.33,0.731 0.733,0.731 h 2.209 c 0.404,0 0.731,-0.328 0.731,-0.731 v -17.511 c 0,-0.813 0.253,-1.761 0.49,-2.377 0.005,-0.008 0.007,-0.018 0.01,-0.026 1.044,-3.042 4.223,-6.12 8.417,-6.475 3.236,0.002 8.326,2.358 9.024,8.226 0.016,0.15 0.03,0.299 0.041,0.444 0.005,0.049 0.008,0.099 0.013,0.149 0.005,0.07 0.009,0.139 0.014,0.209 0.009,0.19 0.015,0.384 0.015,0.582 0,0.335 0.005,0.638 0.018,0.911 v 15.869 c 0,0.404 0.326,0.731 0.73,0.731 h 2.21 c 0.403,0 0.732,-0.328 0.732,-0.731 v -16.272 c 0.001,-5.375 -1.627,-9.248 -4.834,-11.516 z"
+ id="path3969" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 287.97386,464.69717 c -0.133,-0.113 -0.301,-0.174 -0.474,-0.174 -0.039,0 -0.079,0.003 -0.118,0.01 -0.588,0.098 -0.964,0.098 -1.444,0.098 -1.147,0 -2.177,-0.335 -2.177,-4.387 v -9.913 c 0,-2.343 -0.18,-5.368 -1.583,-7.972 -1.824,-3.382 -5.181,-5.098 -9.979,-5.098 -2.475,0 -6.218,0.516 -9.99,2.974 -0.313,0.205 -0.424,0.615 -0.252,0.949 l 0.862,1.67 c 0.098,0.189 0.272,0.326 0.479,0.375 0.058,0.014 0.116,0.021 0.174,0.021 0.15,0 0.296,-0.047 0.422,-0.135 2.314,-1.639 5.188,-2.504 8.306,-2.504 3.995,0 6.004,1.854 7,3.939 l 0,0 c 1.002,2.221 -0.563,4.404 -2.286,4.584 -0.084,0.005 -0.169,0.008 -0.253,0.013 -0.015,0 -0.027,10e-4 -0.043,0 -0.055,-0.001 -0.101,0.003 -0.143,0.009 -10.979,0.626 -16.537,4.313 -16.537,10.98 0,2.027 0.801,4.057 2.201,5.569 1.235,1.333 3.56,2.921 7.637,2.921 3.471,0 6.27,-1.112 8.324,-2.472 0.061,-0.031 0.131,-0.076 0.211,-0.141 0.021,-0.017 0.044,-0.031 0.064,-0.048 0.127,-0.088 0.257,-0.177 0.377,-0.268 1.277,-0.825 1.97,-0.351 2.623,0.504 0.002,0.002 0.006,0.005 0.008,0.007 0.839,1.14 2.162,1.718 3.963,1.718 0.847,0 1.59,-0.094 2.343,-0.294 0.322,-0.085 0.544,-0.375 0.544,-0.707 v -1.67 c 0,-0.214 -0.096,-0.419 -0.259,-0.558 z m -18.252,0.527 c -3.042,0 -6.111,-1.723 -6.111,-5.572 0,-4.863 5.936,-6.58 12.166,-7.043 2.32,-0.043 4.098,2.162 4.313,4.575 v 0.989 c -0.063,0.647 -0.246,1.289 -0.569,1.888 -0.161,0.263 -0.354,0.556 -0.588,0.864 -0.04,0.047 -0.076,0.093 -0.104,0.134 -1.442,1.848 -4.223,4.165 -9.107,4.165 z"
+ id="path3971" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 93.993857,438.01517 h -2.048 c -0.393,0 -0.716,0.311 -0.731,0.703 -0.066,1.618 -0.892,1.836 -2.703,0.766 -0.01,-0.006 -0.017,-0.014 -0.026,-0.019 -0.179,-0.119 -0.362,-0.231 -0.546,-0.339 -0.011,-0.007 -0.021,-0.012 -0.032,-0.02 -0.087,-0.059 -0.17,-0.104 -0.245,-0.14 -1.981,-1.096 -4.305,-1.65 -6.922,-1.65 -3.739,0 -7.429,1.548 -10.123,4.25 -2.083,2.087 -4.565,5.826 -4.565,11.784 0,3.818 1.349,7.413 3.793,10.122 2.595,2.875 6.234,4.458 10.248,4.458 3.111,0 5.514,-0.819 7.336,-1.897 0.003,-0.002 0.008,-0.003 0.011,-0.005 1.953,-0.873 3.603,-2.182 3.136,2.232 -0.876,5.678 -4.518,8.765 -10.482,8.765 -4.176,0 -7.309,-1.404 -9.202,-2.583 -0.118,-0.072 -0.253,-0.11 -0.388,-0.11 -0.061,0 -0.122,0.008 -0.183,0.023 -0.192,0.049 -0.357,0.176 -0.456,0.35 l -0.971,1.725 c -0.188,0.336 -0.084,0.761 0.238,0.972 2.853,1.863 6.908,2.975 10.854,2.975 2.69,0 9.293,-0.625 12.494,-6.412 1.385,-2.481 2.03,-5.968 2.03,-10.968 v -17.457 c 0,-2.474 0.069,-4.679 0.214,-6.738 0.015,-0.202 -0.055,-0.402 -0.194,-0.549 -0.141,-0.153 -0.334,-0.238 -0.537,-0.238 z m -16.892,26.028 c -0.605,-0.24 -7.608,-3.216 -7.015,-11.811 0.554,-7.946 5.725,-10.459 6.519,-10.798 1.279,-0.5 2.702,-0.769 4.241,-0.769 1.665,0 3.113,0.327 4.354,0.873 6.848,3.871 7.696,16.453 0.886,21.467 -1.592,0.999 -3.438,1.573 -5.346,1.573 -1.343,0 -2.553,-0.194 -3.639,-0.535 z"
+ id="path3973" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 251.84086,438.01517 h -2.047 c -0.392,0 -0.716,0.311 -0.731,0.703 -0.064,1.618 -0.892,1.836 -2.701,0.766 -0.011,-0.005 -0.02,-0.014 -0.028,-0.019 -0.179,-0.119 -0.362,-0.231 -0.547,-0.339 -0.01,-0.008 -0.02,-0.014 -0.031,-0.02 -0.087,-0.059 -0.168,-0.104 -0.243,-0.139 -1.982,-1.097 -4.308,-1.651 -6.925,-1.651 -3.739,0 -7.428,1.548 -10.121,4.25 -2.082,2.087 -4.564,5.826 -4.564,11.784 0,3.818 1.347,7.413 3.791,10.122 2.597,2.875 6.233,4.458 10.249,4.458 3.11,0 5.514,-0.819 7.335,-1.897 0.004,-0.002 0.007,-0.003 0.012,-0.005 1.953,-0.874 3.602,-2.183 3.134,2.233 -0.875,5.678 -4.518,8.764 -10.48,8.764 -4.176,0 -7.31,-1.404 -9.204,-2.583 -0.116,-0.072 -0.251,-0.11 -0.388,-0.11 -0.06,0 -0.123,0.008 -0.182,0.023 -0.192,0.049 -0.356,0.176 -0.456,0.35 l -0.968,1.725 c -0.19,0.336 -0.086,0.761 0.237,0.972 2.853,1.863 6.909,2.975 10.852,2.975 2.689,0 9.294,-0.625 12.493,-6.412 1.385,-2.481 2.031,-5.968 2.031,-10.968 v -17.457 c 0,-2.474 0.07,-4.679 0.216,-6.738 0.013,-0.202 -0.058,-0.402 -0.196,-0.549 -0.14,-0.153 -0.334,-0.238 -0.538,-0.238 z m -16.892,26.028 c -0.604,-0.24 -7.607,-3.216 -7.011,-11.811 0.549,-7.94 5.715,-10.455 6.518,-10.798 1.276,-0.5 2.698,-0.769 4.24,-0.769 1.663,0 3.112,0.327 4.354,0.873 6.845,3.871 7.696,16.453 0.883,21.467 -1.592,1 -3.437,1.573 -5.346,1.573 -1.341,0 -2.551,-0.194 -3.638,-0.535 z"
+ id="path3975" />
+ <path
+ style="fill:url(#SVGID_5_)"
+ inkscape:connector-curvature="0"
+ d="m 153.06686,423.79017 c -2.338,2.336 -4.11,5.342 -5.031,8.654 -6.076,-0.093 -12.172,2.273 -17.008,7.11 -8.143,8.141 -10.674,22.913 0.668,34.253 9.542,9.543 23.793,9.687 33.144,0.333 4.422,-4.424 7.237,-10.506 7.298,-16.94 3.14,-0.835 6.125,-2.507 8.67,-5.049 6.957,-6.959 9.085,-18.919 -0.311,-28.318 -7.835,-7.83 -19.628,-7.847 -27.43,-0.043 z m 25.622,20.422 c -1.335,1.338 -3.046,2.324 -4.934,2.864 -3.026,0.867 -4.655,0.777 -5.696,2.515 -1.042,1.735 -0.925,1.94 -0.881,5.194 0.056,3.689 -1.332,7.513 -4.813,10.995 -5.319,5.318 -15.139,6.504 -22.366,-0.721 -5.692,-5.694 -8.363,-15.278 -0.776,-22.865 2.816,-2.813 6.867,-4.477 11.123,-4.402 2.961,0.053 3.306,0.161 5.213,-0.704 1.907,-0.869 1.082,-2.799 2.222,-6.205 0.557,-1.66 1.544,-3.294 3.093,-4.844 4.125,-4.126 11.721,-5.072 17.284,0.491 4.532,4.535 6.456,11.759 0.531,17.682 z"
+ id="path3982" />
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-logo.png b/storage/mroonga/vendor/groonga/data/images/logo/groonga-logo.png
new file mode 100644
index 00000000000..2dfa328e6cf
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-logo.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-logo.svg b/storage/mroonga/vendor/groonga/data/images/logo/groonga-logo.svg
new file mode 100644
index 00000000000..d0243133111
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-logo.svg
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="232.17999"
+ height="73.112274"
+ id="svg3635"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 1">
+ <defs
+ id="defs3637">
+ <linearGradient
+ gradientTransform="translate(-419.62671,208.8676)"
+ id="SVGID_1_"
+ gradientUnits="userSpaceOnUse"
+ x1="223.6167"
+ y1="76.3564"
+ x2="223.6167"
+ y2="137.38029">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop160" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop162" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_1_"
+ id="linearGradient3693"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-419.62671,208.8676)"
+ x1="223.6167"
+ y1="76.3564"
+ x2="223.6167"
+ y2="137.38029" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="202.10766"
+ inkscape:cy="3.8593771"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2411"
+ inkscape:window-y="568"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata3640">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-229.62429,-318.66319)">
+ <g
+ id="g3685"
+ transform="translate(520,37.142857)">
+ <path
+ id="path149"
+ d="m -228.72771,306.3346 c -1.336,-0.29 -2.281,-0.448 -3.442,-0.448 -2.847,0 -5.33,0.831 -7.391,2.468 -0.091,0.06 -0.192,0.14 -0.303,0.245 -0.231,0.197 -0.452,0.406 -0.668,0.621 -1.478,1.314 -2.76,1.952 -2.76,-1.235 v -0.666 c 0,-0.404 -0.327,-0.732 -0.732,-0.732 h -2.101 c -0.198,0 -0.388,0.081 -0.525,0.223 -0.139,0.143 -0.212,0.335 -0.206,0.532 0.105,3.333 0.215,6.778 0.215,9.621 v 18.75 c 0,0.404 0.328,0.731 0.732,0.731 h 2.209 c 0.404,0 0.732,-0.328 0.732,-0.731 v -9.914 c 0,-1.37 0.107,-3.805 0.309,-4.813 1.569,-7.797 4.982,-11.588 10.435,-11.588 0.979,0 1.881,0.165 2.874,0.363 0.048,0.011 0.096,0.015 0.144,0.015 0.148,0 0.294,-0.045 0.417,-0.131 0.163,-0.113 0.273,-0.288 0.306,-0.484 l 0.323,-1.994 c 0.062,-0.384 -0.187,-0.752 -0.568,-0.833 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path151"
+ d="m -139.68071,307.9266 c -2.512,-1.774 -5.292,-2.04 -6.726,-2.04 -2.91,0 -5.648,0.912 -7.822,2.495 -0.005,0.004 -0.01,0.005 -0.014,0.009 -1.004,0.648 -2.627,0.909 -2.618,-0.625 l 0,-0.021 c 0.002,-0.107 -0.003,-0.199 -0.013,-0.277 l -0.008,-0.182 c -0.019,-0.391 -0.34,-0.698 -0.731,-0.698 h -2.102 c -0.203,0 -0.396,0.084 -0.535,0.232 -0.138,0.148 -0.209,0.347 -0.195,0.549 0.152,2.28 0.214,4.171 0.214,6.524 v 21.821 c 0,0.404 0.328,0.731 0.731,0.731 h 2.209 c 0.404,0 0.732,-0.328 0.732,-0.731 v -17.511 c 0,-0.813 0.253,-1.761 0.49,-2.377 0.004,-0.008 0.006,-0.018 0.01,-0.026 1.043,-3.042 4.224,-6.12 8.417,-6.475 3.236,0.002 8.327,2.358 9.024,8.226 0.016,0.15 0.03,0.299 0.042,0.444 0.004,0.049 0.008,0.099 0.011,0.149 0.005,0.07 0.009,0.139 0.014,0.209 0.009,0.19 0.015,0.384 0.015,0.582 0,0.335 0.006,0.638 0.017,0.911 v 15.869 c 0,0.404 0.328,0.731 0.733,0.731 h 2.208 c 0.404,0 0.732,-0.328 0.732,-0.731 v -16.272 c 0,-5.375 -1.627,-9.248 -4.835,-11.516 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path153"
+ d="m -63.456714,333.2686 c -0.133,-0.113 -0.301,-0.174 -0.473,-0.174 -0.04,0 -0.08,0.003 -0.12,0.01 -0.587,0.098 -0.965,0.098 -1.442,0.098 -1.147,0 -2.177,-0.335 -2.177,-4.387 v -9.913 c 0,-2.343 -0.179,-5.368 -1.583,-7.972 -1.822,-3.382 -5.181,-5.098 -9.978,-5.098 -2.475,0 -6.217,0.516 -9.99,2.974 -0.315,0.205 -0.422,0.615 -0.25,0.949 l 0.861,1.67 c 0.098,0.189 0.272,0.326 0.478,0.375 0.058,0.014 0.116,0.021 0.173,0.021 0.151,0 0.299,-0.047 0.424,-0.135 2.314,-1.639 5.186,-2.504 8.305,-2.504 3.995,0 6.004,1.854 7,3.939 l 0,0 c 1.004,2.221 -0.561,4.404 -2.285,4.584 -0.085,0.005 -0.17,0.008 -0.254,0.013 -0.014,0 -0.028,0.001 -0.042,0 -0.055,-10e-4 -0.101,0.003 -0.143,0.009 -10.978,0.626 -16.538,4.313 -16.538,10.98 0,2.027 0.803,4.057 2.202,5.569 1.233,1.333 3.56,2.921 7.635,2.921 3.471,0 6.271,-1.112 8.325,-2.472 0.061,-0.031 0.13,-0.076 0.211,-0.141 0.023,-0.017 0.042,-0.031 0.064,-0.048 0.128,-0.088 0.256,-0.177 0.378,-0.268 1.275,-0.825 1.969,-0.351 2.623,0.504 0.002,0.002 0.006,0.005 0.008,0.007 0.84,1.14 2.161,1.718 3.962,1.718 0.846,0 1.591,-0.094 2.344,-0.294 0.32,-0.085 0.543,-0.375 0.543,-0.707 v -1.67 c -0.002,-0.214 -0.097,-0.419 -0.261,-0.558 z m -18.252,0.527 c -3.042,0 -6.111,-1.723 -6.111,-5.572 0,-4.863 5.936,-6.58 12.167,-7.043 2.32,-0.043 4.097,2.162 4.311,4.575 v 0.989 c -0.062,0.647 -0.246,1.289 -0.568,1.888 -0.162,0.263 -0.356,0.556 -0.589,0.864 -0.04,0.047 -0.075,0.093 -0.103,0.134 -1.442,1.848 -4.223,4.165 -9.107,4.165 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path155"
+ d="m -257.43671,306.5866 h -2.047 c -0.392,0 -0.715,0.311 -0.731,0.703 -0.065,1.618 -0.892,1.836 -2.704,0.766 -0.01,-0.006 -0.018,-0.014 -0.026,-0.019 -0.178,-0.119 -0.361,-0.231 -0.545,-0.339 -0.011,-0.007 -0.021,-0.012 -0.032,-0.02 -0.088,-0.059 -0.169,-0.104 -0.245,-0.14 -1.982,-1.096 -4.306,-1.65 -6.923,-1.65 -3.739,0 -7.428,1.548 -10.122,4.25 -2.083,2.087 -4.564,5.826 -4.564,11.784 0,3.818 1.347,7.413 3.792,10.122 2.594,2.875 6.234,4.458 10.249,4.458 3.11,0 5.514,-0.819 7.334,-1.897 0.004,-0.002 0.008,-0.003 0.012,-0.005 1.953,-0.873 3.601,-2.182 3.135,2.232 -0.875,5.678 -4.519,8.765 -10.482,8.765 -4.175,0 -7.31,-1.404 -9.204,-2.583 -0.117,-0.072 -0.251,-0.11 -0.387,-0.11 -0.061,0 -0.122,0.008 -0.182,0.023 -0.193,0.049 -0.358,0.176 -0.456,0.35 l -0.969,1.725 c -0.189,0.336 -0.085,0.761 0.237,0.972 2.852,1.863 6.909,2.975 10.853,2.975 2.691,0 9.293,-0.625 12.494,-6.412 1.384,-2.481 2.03,-5.968 2.03,-10.968 v -17.457 c 0,-2.474 0.07,-4.679 0.214,-6.738 0.014,-0.202 -0.057,-0.402 -0.195,-0.549 -0.139,-0.153 -0.333,-0.238 -0.536,-0.238 z m -16.892,26.028 c -0.605,-0.24 -7.609,-3.216 -7.013,-11.811 0.551,-7.946 5.723,-10.459 6.518,-10.798 1.278,-0.5 2.7,-0.769 4.241,-0.769 1.664,0 3.112,0.327 4.354,0.873 6.846,3.871 7.696,16.453 0.885,21.467 -1.592,0.999 -3.437,1.573 -5.346,1.573 -1.342,0 -2.552,-0.194 -3.639,-0.535 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path157"
+ d="m -99.588714,306.5866 h -2.046996 c -0.393,0 -0.716,0.311 -0.731,0.703 -0.066,1.618 -0.892,1.836 -2.703,0.766 -0.009,-0.005 -0.018,-0.014 -0.027,-0.019 -0.178,-0.119 -0.362,-0.231 -0.546,-0.339 -0.011,-0.008 -0.02,-0.014 -0.031,-0.02 -0.087,-0.059 -0.168,-0.104 -0.244,-0.139 -1.982,-1.097 -4.307,-1.651 -6.924,-1.651 -3.739,0 -7.429,1.548 -10.122,4.25 -2.082,2.087 -4.564,5.826 -4.564,11.784 0,3.818 1.346,7.413 3.792,10.122 2.595,2.875 6.234,4.458 10.248,4.458 3.111,0 5.514,-0.819 7.335,-1.897 0.004,-0.002 0.007,-0.003 0.012,-0.005 1.954,-0.874 3.603,-2.183 3.135,2.233 -0.875,5.678 -4.519,8.764 -10.482,8.764 -4.175,0 -7.31,-1.404 -9.204,-2.583 -0.118,-0.072 -0.251,-0.11 -0.387,-0.11 -0.06,0 -0.122,0.008 -0.182,0.023 -0.193,0.049 -0.358,0.176 -0.456,0.35 l -0.97,1.725 c -0.189,0.336 -0.085,0.761 0.237,0.972 2.853,1.863 6.91,2.975 10.854,2.975 2.689,0 9.293,-0.625 12.493,-6.412 1.384996,-2.481 2.030996,-5.968 2.030996,-10.968 v -17.457 c 0,-2.474 0.07,-4.679 0.214,-6.738 0.014,-0.202 -0.057,-0.402 -0.195,-0.549 -0.139,-0.153 -0.332,-0.238 -0.536,-0.238 z m -16.891996,26.028 c -0.605,-0.24 -7.609,-3.216 -7.013,-11.811 0.551,-7.94 5.716,-10.455 6.517,-10.798 1.278,-0.5 2.701,-0.769 4.242,-0.769 1.665,0 3.113,0.327 4.354,0.873 6.845,3.871 7.696,16.453 0.884,21.467 -1.592,1 -3.436,1.573 -5.345,1.573 -1.343,0 -2.553,-0.194 -3.639,-0.535 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path164"
+ d="m -198.36371,292.3616 c -2.337,2.336 -4.109,5.342 -5.032,8.654 -6.076,-0.093 -12.171,2.273 -17.007,7.11 -8.142,8.141 -10.674,22.913 0.667,34.253 9.542,9.543 23.793,9.687 33.145,0.333 4.421,-4.424 7.236,-10.506 7.297,-16.94 3.14,-0.835 6.125,-2.507 8.67,-5.049 6.956,-6.959 9.086,-18.919 -0.311,-28.318 -7.833,-7.83 -19.626,-7.847 -27.429,-0.043 z m 25.622,20.422 c -1.335,1.338 -3.045,2.324 -4.932,2.864 -3.027,0.867 -4.657,0.777 -5.698,2.515 -1.041,1.735 -0.926,1.94 -0.88,5.194 0.056,3.689 -1.333,7.513 -4.813,10.995 -5.32,5.318 -15.14,6.504 -22.366,-0.721 -5.693,-5.694 -8.365,-15.278 -0.778,-22.865 2.816,-2.813 6.866,-4.477 11.124,-4.402 2.961,0.053 3.306,0.161 5.212,-0.704 1.909,-0.869 1.082,-2.799 2.222,-6.205 0.556,-1.66 1.545,-3.294 3.092,-4.844 4.126,-4.126 11.721,-5.072 17.285,0.491 4.533,4.535 6.455,11.759 0.532,17.682 z"
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient3693)" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar-foreground-white.png b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar-foreground-white.png
new file mode 100644
index 00000000000..5f065e9c1b4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar-foreground-white.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar-foreground-white.svg b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar-foreground-white.svg
new file mode 100644
index 00000000000..777f19f6f1b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar-foreground-white.svg
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="120"
+ height="60"
+ id="svg7234"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 71">
+ <defs
+ id="defs7236">
+ <linearGradient
+ gradientTransform="translate(-170,-202.49496)"
+ id="SVGID_21_"
+ gradientUnits="userSpaceOnUse"
+ x1="459.35349"
+ y1="540.44342"
+ x2="459.35349"
+ y2="570.87878">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop4296" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop4298" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="135"
+ inkscape:cy="-132.85715"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="1928"
+ inkscape:window-y="52"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata7239">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-240,-319.50504)">
+ <rect
+ style="fill:#00293f"
+ x="240"
+ y="319.50504"
+ width="120"
+ height="60"
+ id="rect4240" />
+ <rect
+ x="240"
+ y="319.50504"
+ width="120"
+ height="14"
+ id="rect4283" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 273.035,348.47704 c -0.666,-0.145 -1.137,-0.223 -1.716,-0.223 -1.421,0 -2.659,0.414 -3.687,1.229 -0.045,0.031 -0.096,0.071 -0.15,0.123 -0.115,0.099 -0.227,0.202 -0.334,0.309 -0.737,0.656 -1.377,0.975 -1.377,-0.614 v -0.333 c 0,-0.201 -0.162,-0.365 -0.365,-0.365 h -1.047 c -0.1,0 -0.193,0.04 -0.262,0.112 -0.07,0.07 -0.106,0.167 -0.104,0.265 0.053,1.661 0.107,3.38 0.107,4.798 v 9.352 c 0,0.202 0.164,0.366 0.365,0.366 h 1.102 c 0.201,0 0.365,-0.164 0.365,-0.366 v -4.944 c 0,-0.684 0.053,-1.896 0.154,-2.4 0.781,-3.89 2.484,-5.779 5.204,-5.779 0.488,0 0.938,0.082 1.433,0.181 0.023,0.006 0.049,0.008 0.072,0.008 0.073,0 0.146,-0.022 0.208,-0.065 0.081,-0.056 0.137,-0.144 0.152,-0.242 l 0.161,-0.993 c 0.034,-0.196 -0.091,-0.377 -0.281,-0.419 z"
+ id="path4285" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 317.447,349.27104 c -1.253,-0.885 -2.64,-1.017 -3.354,-1.017 -1.452,0 -2.817,0.455 -3.901,1.244 -0.002,0.002 -0.005,0.002 -0.007,0.005 -0.501,0.322 -1.311,0.452 -1.306,-0.313 v -0.009 c 0,-0.055 -0.002,-0.101 -0.008,-0.139 l -0.004,-0.091 c -0.008,-0.194 -0.169,-0.349 -0.364,-0.349 h -1.048 c -0.102,0 -0.197,0.041 -0.266,0.115 -0.069,0.074 -0.105,0.173 -0.098,0.274 0.075,1.138 0.105,2.08 0.105,3.254 v 10.883 c 0,0.202 0.164,0.366 0.365,0.366 h 1.102 c 0.201,0 0.365,-0.164 0.365,-0.366 v -8.732 c 0,-0.406 0.127,-0.879 0.244,-1.186 0.002,-0.005 0.004,-0.01 0.006,-0.013 0.52,-1.518 2.105,-3.053 4.197,-3.229 1.613,0.001 4.152,1.176 4.5,4.102 0.008,0.075 0.016,0.149 0.021,0.223 0.002,0.023 0.004,0.048 0.006,0.074 0.003,0.034 0.004,0.068 0.008,0.104 0.004,0.095 0.007,0.191 0.007,0.29 0,0.167 0.003,0.317 0.009,0.454 v 7.914 c 0,0.202 0.162,0.366 0.365,0.366 h 1.102 c 0.201,0 0.365,-0.164 0.365,-0.366 v -8.115 c 10e-4,-2.68 -0.811,-4.612 -2.411,-5.743 z"
+ id="path4287" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 356.461,361.91004 c -0.066,-0.056 -0.15,-0.087 -0.235,-0.087 -0.021,0 -0.04,0.002 -0.06,0.006 -0.293,0.049 -0.481,0.049 -0.72,0.049 -0.573,0 -1.086,-0.167 -1.086,-2.189 v -4.942 c 0,-1.169 -0.089,-2.677 -0.79,-3.977 -0.908,-1.687 -2.584,-2.543 -4.977,-2.543 -1.234,0 -3.101,0.258 -4.982,1.483 -0.157,0.102 -0.211,0.307 -0.125,0.474 l 0.43,0.833 c 0.049,0.094 0.136,0.162 0.238,0.187 0.028,0.007 0.057,0.011 0.086,0.011 0.076,0 0.149,-0.023 0.211,-0.067 1.154,-0.817 2.588,-1.249 4.143,-1.249 1.992,0 2.995,0.925 3.491,1.965 l 0,0 c 0.501,1.107 -0.28,2.196 -1.14,2.287 -0.042,0.002 -0.084,0.004 -0.127,0.006 -0.006,0 -0.014,0.001 -0.021,0 -0.028,0 -0.051,0.001 -0.071,0.005 -5.475,0.312 -8.248,2.151 -8.248,5.476 0,1.012 0.4,2.024 1.098,2.778 0.615,0.664 1.776,1.456 3.809,1.456 1.731,0 3.127,-0.554 4.152,-1.232 0.029,-0.016 0.064,-0.038 0.105,-0.069 0.011,-0.01 0.021,-0.017 0.031,-0.025 0.064,-0.043 0.129,-0.088 0.188,-0.134 0.636,-0.41 0.981,-0.175 1.309,0.252 10e-4,10e-4 0.003,0.002 0.004,0.003 0.419,0.569 1.077,0.857 1.976,0.857 0.422,0 0.793,-0.047 1.17,-0.146 0.158,-0.042 0.271,-0.188 0.271,-0.353 v -0.833 c 0,-0.111 -0.048,-0.211 -0.13,-0.282 z m -9.103,0.263 c -1.517,0 -3.048,-0.858 -3.048,-2.778 0,-2.426 2.96,-3.281 6.068,-3.514 1.157,-0.021 2.043,1.078 2.149,2.282 v 0.493 c -0.03,0.323 -0.122,0.644 -0.283,0.942 -0.081,0.13 -0.177,0.277 -0.294,0.431 -0.02,0.023 -0.037,0.047 -0.051,0.066 -0.717,0.923 -2.105,2.078 -4.541,2.078 z"
+ id="path4289" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 258.717,348.60304 h -1.021 c -0.195,0 -0.355,0.154 -0.364,0.351 -0.032,0.807 -0.444,0.915 -1.349,0.383 -0.004,-0.004 -0.009,-0.008 -0.013,-0.011 -0.089,-0.059 -0.181,-0.114 -0.272,-0.168 -0.006,-0.004 -0.011,-0.006 -0.016,-0.011 -0.043,-0.029 -0.084,-0.052 -0.123,-0.069 -0.988,-0.547 -2.146,-0.823 -3.453,-0.823 -1.864,0 -3.703,0.772 -5.047,2.119 -1.039,1.041 -2.277,2.906 -2.277,5.877 0,1.905 0.672,3.697 1.892,5.048 1.294,1.435 3.109,2.224 5.111,2.224 1.551,0 2.75,-0.409 3.658,-0.946 0.002,-0.001 0.004,-0.001 0.007,-0.001 0.973,-0.437 1.795,-1.09 1.563,1.112 -0.436,2.832 -2.254,4.372 -5.228,4.372 -2.082,0 -3.646,-0.701 -4.591,-1.288 -0.059,-0.037 -0.125,-0.057 -0.193,-0.057 -0.029,0 -0.061,0.005 -0.09,0.012 -0.097,0.024 -0.179,0.088 -0.228,0.175 l -0.483,0.86 c -0.095,0.167 -0.043,0.38 0.118,0.484 1.423,0.929 3.446,1.483 5.413,1.483 1.342,0 4.635,-0.312 6.231,-3.198 0.69,-1.237 1.013,-2.976 1.013,-5.47 v -8.707 c 0,-1.233 0.035,-2.332 0.107,-3.36 0.006,-0.1 -0.029,-0.2 -0.098,-0.273 -0.07,-0.075 -0.166,-0.118 -0.267,-0.118 z m -8.425,12.981 c -0.302,-0.12 -3.795,-1.604 -3.498,-5.891 0.275,-3.963 2.854,-5.217 3.251,-5.387 0.638,-0.248 1.348,-0.382 2.115,-0.382 0.83,0 1.553,0.162 2.171,0.436 3.415,1.93 3.839,8.205 0.442,10.706 -0.795,0.498 -1.715,0.784 -2.668,0.784 -0.667,0.001 -1.271,-0.096 -1.813,-0.266 z"
+ id="path4291" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 337.443,348.60304 h -1.021 c -0.195,0 -0.356,0.154 -0.364,0.351 -0.034,0.807 -0.445,0.915 -1.349,0.383 -0.004,-0.004 -0.009,-0.008 -0.014,-0.011 -0.088,-0.059 -0.18,-0.114 -0.272,-0.168 -0.005,-0.004 -0.01,-0.008 -0.015,-0.011 -0.043,-0.029 -0.084,-0.052 -0.123,-0.069 -0.988,-0.547 -2.146,-0.823 -3.452,-0.823 -1.864,0 -3.706,0.772 -5.049,2.119 -1.038,1.041 -2.276,2.906 -2.276,5.877 0,1.905 0.672,3.697 1.891,5.048 1.295,1.435 3.109,2.224 5.112,2.224 1.552,0 2.749,-0.409 3.658,-0.946 0.001,-0.001 0.003,-0.001 0.006,-0.001 0.974,-0.438 1.796,-1.09 1.563,1.112 -0.438,2.832 -2.254,4.372 -5.228,4.372 -2.083,0 -3.646,-0.701 -4.591,-1.288 -0.059,-0.037 -0.125,-0.057 -0.193,-0.057 -0.029,0 -0.061,0.005 -0.09,0.012 -0.098,0.024 -0.18,0.088 -0.228,0.175 l -0.483,0.86 c -0.095,0.167 -0.043,0.38 0.117,0.484 1.424,0.929 3.446,1.483 5.414,1.483 1.342,0 4.635,-0.312 6.23,-3.198 0.691,-1.237 1.014,-2.976 1.014,-5.47 v -8.707 c 0,-1.233 0.035,-2.332 0.105,-3.36 0.008,-0.1 -0.027,-0.2 -0.097,-0.273 -0.067,-0.075 -0.163,-0.118 -0.265,-0.118 z m -8.424,12.981 c -0.302,-0.12 -3.796,-1.604 -3.498,-5.891 0.274,-3.96 2.851,-5.215 3.25,-5.385 0.638,-0.25 1.347,-0.384 2.116,-0.384 0.83,0 1.553,0.162 2.172,0.436 3.413,1.93 3.838,8.205 0.439,10.706 -0.793,0.498 -1.713,0.784 -2.665,0.784 -0.669,0.001 -1.272,-0.096 -1.814,-0.266 z"
+ id="path4293" />
+ <path
+ style="fill:url(#SVGID_21_)"
+ inkscape:connector-curvature="0"
+ d="m 288.18,341.50904 c -1.166,1.164 -2.05,2.664 -2.51,4.316 -3.031,-0.047 -6.07,1.134 -8.482,3.546 -4.062,4.06 -5.324,11.427 0.332,17.083 4.76,4.761 11.867,4.831 16.531,0.167 2.205,-2.207 3.609,-5.239 3.64,-8.449 1.565,-0.416 3.056,-1.25 4.324,-2.519 3.47,-3.471 4.531,-9.436 -0.155,-14.124 -3.907,-3.905 -9.79,-3.914 -13.68,-0.02 z m 12.779,10.184 c -0.667,0.667 -1.52,1.159 -2.461,1.428 -1.51,0.434 -2.322,0.389 -2.842,1.255 -0.52,0.866 -0.461,0.967 -0.438,2.59 0.027,1.841 -0.665,3.747 -2.4,5.484 -2.653,2.652 -7.551,3.244 -11.155,-0.359 -2.839,-2.841 -4.172,-7.62 -0.387,-11.404 1.404,-1.402 3.424,-2.233 5.547,-2.195 1.477,0.027 1.649,0.08 2.6,-0.351 0.953,-0.433 0.54,-1.397 1.109,-3.095 0.276,-0.827 0.77,-1.643 1.541,-2.416 2.059,-2.058 5.846,-2.53 8.621,0.245 2.259,2.261 3.218,5.864 0.265,8.818 z"
+ id="path4300" />
+ <g
+ transform="translate(-170,-202.49496)"
+ id="g4302">
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 416.381,533.79 v -5.23 c 0,-0.65 -0.01,-1.18 -0.031,-1.59 h 0.664 l 0.039,0.84 h 0.017 c 0.323,-0.633 0.803,-0.95 1.44,-0.95 0.484,0 0.889,0.229 1.211,0.688 0.323,0.459 0.484,1.046 0.484,1.763 0,0.797 -0.174,1.431 -0.521,1.902 -0.347,0.471 -0.782,0.708 -1.303,0.708 -0.264,0 -0.506,-0.067 -0.726,-0.2 -0.22,-0.134 -0.392,-0.323 -0.515,-0.57 h -0.016 v 2.641 h -0.743 z m 0.744,-4.761 v 0.78 c 0,0.39 0.109,0.718 0.326,0.982 0.217,0.264 0.479,0.397 0.786,0.397 0.373,0 0.669,-0.166 0.888,-0.497 0.219,-0.332 0.328,-0.779 0.328,-1.343 0,-0.507 -0.107,-0.925 -0.324,-1.255 -0.216,-0.33 -0.502,-0.495 -0.859,-0.495 -0.229,0 -0.44,0.082 -0.633,0.247 -0.191,0.165 -0.324,0.367 -0.399,0.605 -0.075,0.238 -0.113,0.433 -0.113,0.579 z"
+ id="path4304" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 425.245,529.34 c 0,0.52 -0.087,0.975 -0.26,1.364 -0.173,0.391 -0.416,0.69 -0.728,0.9 -0.313,0.21 -0.65,0.315 -1.013,0.315 -0.565,0 -1.032,-0.231 -1.399,-0.692 -0.369,-0.462 -0.553,-1.062 -0.553,-1.798 0,-0.783 0.19,-1.407 0.57,-1.873 0.38,-0.465 0.859,-0.697 1.438,-0.697 0.576,0 1.045,0.231 1.404,0.692 0.361,0.463 0.541,1.058 0.541,1.789 z m -3.192,0.059 c 0,0.53 0.113,0.968 0.34,1.313 0.228,0.345 0.512,0.518 0.853,0.518 0.35,0 0.641,-0.173 0.874,-0.52 0.233,-0.347 0.35,-0.794 0.35,-1.341 0,-0.503 -0.107,-0.932 -0.321,-1.287 -0.215,-0.354 -0.508,-0.532 -0.878,-0.532 -0.377,0 -0.673,0.176 -0.891,0.527 -0.218,0.352 -0.327,0.792 -0.327,1.322 z"
+ id="path4306" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 426.174,526.97 h 0.76 c 0.506,2.347 0.778,3.673 0.816,3.979 h 0.016 c 0.029,-0.26 0.359,-1.586 0.992,-3.979 h 0.631 c 0.574,2.243 0.895,3.57 0.961,3.979 h 0.016 c 0.074,-0.5 0.168,-0.989 0.28,-1.47 l 0.584,-2.51 h 0.735 l -1.279,4.84 h -0.688 c -0.44,-1.693 -0.688,-2.656 -0.743,-2.888 -0.057,-0.231 -0.121,-0.559 -0.192,-0.982 h -0.017 c -0.088,0.504 -0.203,1.017 -0.348,1.54 l -0.645,2.33 h -0.688 l -1.191,-4.839 z"
+ id="path4308" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 436.396,529.579 h -2.808 c 0.011,0.523 0.14,0.925 0.388,1.203 0.248,0.278 0.57,0.417 0.965,0.417 0.429,0 0.805,-0.083 1.127,-0.25 l 0.129,0.65 c -0.382,0.213 -0.832,0.32 -1.353,0.32 -0.603,0 -1.083,-0.224 -1.44,-0.67 -0.357,-0.447 -0.535,-1.04 -0.535,-1.78 0,-0.74 0.171,-1.36 0.514,-1.86 0.343,-0.5 0.803,-0.75 1.383,-0.75 0.533,0 0.941,0.223 1.228,0.668 0.285,0.444 0.428,0.989 0.428,1.632 -0.001,0.184 -0.009,0.324 -0.026,0.42 z m -2.798,-0.67 h 2.104 c 0,-0.416 -0.086,-0.752 -0.258,-1.007 -0.172,-0.255 -0.417,-0.383 -0.734,-0.383 -0.301,0 -0.551,0.128 -0.748,0.383 -0.197,0.255 -0.319,0.591 -0.364,1.007 z"
+ id="path4310" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 437.797,531.81 v -3.3 c 0,-0.627 -0.011,-1.141 -0.032,-1.54 h 0.656 l 0.048,0.97 c 0.099,-0.34 0.25,-0.604 0.452,-0.795 0.202,-0.191 0.423,-0.285 0.659,-0.285 0.051,0 0.118,0.007 0.201,0.021 v 0.88 c -0.072,-0.021 -0.158,-0.03 -0.256,-0.03 -0.27,0 -0.502,0.132 -0.695,0.396 -0.193,0.263 -0.289,0.638 -0.289,1.125 v 2.56 h -0.744 z"
+ id="path4312" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 444.229,529.579 h -2.808 c 0.011,0.523 0.14,0.925 0.388,1.203 0.248,0.278 0.57,0.417 0.965,0.417 0.429,0 0.805,-0.083 1.127,-0.25 l 0.129,0.65 c -0.382,0.213 -0.832,0.32 -1.353,0.32 -0.603,0 -1.083,-0.224 -1.44,-0.67 -0.357,-0.447 -0.535,-1.04 -0.535,-1.78 0,-0.74 0.171,-1.36 0.514,-1.86 0.343,-0.5 0.803,-0.75 1.383,-0.75 0.533,0 0.941,0.223 1.228,0.668 0.285,0.444 0.428,0.989 0.428,1.632 -0.002,0.184 -0.01,0.324 -0.026,0.42 z m -2.799,-0.67 h 2.104 c 0,-0.416 -0.086,-0.752 -0.258,-1.007 -0.172,-0.255 -0.417,-0.383 -0.734,-0.383 -0.301,0 -0.551,0.128 -0.748,0.383 -0.197,0.255 -0.319,0.591 -0.364,1.007 z"
+ id="path4314" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 448.365,524.689 h 0.744 v 5.851 c 0,0.539 0.01,0.963 0.031,1.27 h -0.664 l -0.056,-0.86 c -0.128,0.307 -0.312,0.545 -0.552,0.716 -0.24,0.17 -0.512,0.255 -0.816,0.255 -0.49,0 -0.9,-0.231 -1.229,-0.692 -0.328,-0.462 -0.491,-1.048 -0.491,-1.758 0,-0.771 0.173,-1.397 0.518,-1.883 0.346,-0.484 0.772,-0.728 1.282,-0.728 0.271,0 0.516,0.067 0.73,0.2 0.215,0.134 0.381,0.313 0.502,0.54 v -2.911 z m 0,5.04 v -0.77 c 0,-0.41 -0.106,-0.741 -0.318,-0.993 -0.212,-0.251 -0.467,-0.377 -0.762,-0.377 -0.36,0 -0.65,0.168 -0.87,0.505 -0.22,0.337 -0.33,0.774 -0.33,1.314 0,0.507 0.104,0.927 0.312,1.261 0.209,0.333 0.499,0.5 0.873,0.5 0.285,0 0.539,-0.131 0.762,-0.393 0.223,-0.262 0.333,-0.61 0.333,-1.047 z"
+ id="path4316" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 453.014,530.54 v -5.851 h 0.76 v 3.07 c 0.282,-0.601 0.738,-0.9 1.367,-0.9 0.496,0 0.903,0.229 1.221,0.688 0.316,0.459 0.476,1.05 0.476,1.772 0,0.79 -0.175,1.421 -0.526,1.893 -0.35,0.472 -0.777,0.708 -1.281,0.708 -0.605,0 -1.062,-0.304 -1.368,-0.91 l -0.032,0.8 h -0.648 c 0.019,-0.307 0.031,-0.731 0.031,-1.27 z m 0.744,-1.5 v 0.789 c 0,0.36 0.106,0.676 0.321,0.945 0.215,0.271 0.48,0.405 0.798,0.405 0.371,0 0.664,-0.164 0.883,-0.492 0.217,-0.329 0.325,-0.771 0.325,-1.328 0,-0.517 -0.108,-0.94 -0.323,-1.272 -0.217,-0.331 -0.506,-0.497 -0.869,-0.497 -0.319,0 -0.589,0.146 -0.808,0.438 -0.219,0.29 -0.327,0.628 -0.327,1.012 z"
+ id="path4318" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 457.693,526.97 h 0.816 l 0.879,2.885 c 0.075,0.24 0.163,0.549 0.265,0.925 0.104,-0.45 0.184,-0.774 0.239,-0.975 l 0.793,-2.835 h 0.799 l -1.096,3.46 c -0.35,1.1 -0.633,1.86 -0.85,2.28 -0.217,0.42 -0.453,0.73 -0.708,0.932 -0.255,0.202 -0.493,0.328 -0.714,0.378 l -0.176,-0.78 c 0.402,-0.156 0.717,-0.427 0.941,-0.81 0.226,-0.384 0.338,-0.64 0.338,-0.771 0,-0.057 -0.016,-0.133 -0.048,-0.229 l -1.478,-4.46 z"
+ id="path4320" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar.png b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar.png
new file mode 100644
index 00000000000..4ec0662794c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar.svg b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar.svg
new file mode 100644
index 00000000000..37bca2676f2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-bar.svg
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="121"
+ height="61"
+ id="svg7089"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 68">
+ <defs
+ id="defs7091">
+ <linearGradient
+ id="SVGID_17_"
+ gradientUnits="userSpaceOnUse"
+ x1="329.353"
+ y1="540.44342"
+ x2="329.353"
+ y2="570.87878">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop4136" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop4138" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.98994949"
+ inkscape:cx="-139.11021"
+ inkscape:cy="77.765252"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2060"
+ inkscape:window-y="202"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata7094">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-673.78571,-579.00507)">
+ <rect
+ style="fill:#ffffff;stroke:#0088d3;stroke-miterlimit:10"
+ x="674.28571"
+ y="579.50507"
+ stroke-miterlimit="10"
+ width="120"
+ height="60"
+ id="rect4119" />
+ <rect
+ style="fill:#0088d3"
+ x="674.28571"
+ y="579.50507"
+ width="120"
+ height="14"
+ id="rect4121" />
+ <g
+ transform="translate(394.28571,57.50504)"
+ id="g4123">
+ <path
+ inkscape:connector-curvature="0"
+ d="m 313.036,550.972 c -0.667,-0.145 -1.138,-0.223 -1.716,-0.223 -1.42,0 -2.659,0.414 -3.686,1.229 -0.046,0.031 -0.097,0.071 -0.151,0.123 -0.115,0.099 -0.226,0.202 -0.333,0.309 -0.737,0.656 -1.376,0.975 -1.376,-0.614 v -0.333 c 0,-0.201 -0.163,-0.365 -0.365,-0.365 h -1.048 c -0.099,0 -0.193,0.04 -0.262,0.112 -0.069,0.07 -0.106,0.167 -0.103,0.265 0.053,1.661 0.108,3.38 0.108,4.798 v 9.352 c 0,0.202 0.163,0.366 0.364,0.366 h 1.102 c 0.201,0 0.365,-0.164 0.365,-0.366 v -4.944 c 0,-0.684 0.053,-1.896 0.154,-2.4 0.782,-3.89 2.485,-5.779 5.204,-5.779 0.488,0 0.938,0.082 1.433,0.181 0.023,0.006 0.048,0.008 0.072,0.008 0.073,0 0.146,-0.022 0.208,-0.065 0.081,-0.056 0.137,-0.144 0.152,-0.242 l 0.161,-0.993 c 0.031,-0.196 -0.094,-0.377 -0.283,-0.419 z"
+ id="path4125" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 357.447,551.766 c -1.253,-0.885 -2.64,-1.017 -3.354,-1.017 -1.452,0 -2.817,0.455 -3.901,1.244 -0.002,0.002 -0.005,0.002 -0.007,0.005 -0.501,0.322 -1.311,0.452 -1.306,-0.313 v -0.009 c 0,-0.055 -0.002,-0.101 -0.007,-0.139 l -0.004,-0.091 c -0.009,-0.194 -0.169,-0.349 -0.365,-0.349 h -1.048 c -0.101,0 -0.197,0.041 -0.266,0.115 -0.069,0.074 -0.105,0.173 -0.098,0.274 0.076,1.138 0.106,2.08 0.106,3.254 v 10.883 c 0,0.202 0.164,0.366 0.365,0.366 h 1.101 c 0.202,0 0.365,-0.164 0.365,-0.366 v -8.732 c 0,-0.406 0.126,-0.879 0.244,-1.186 0.002,-0.005 0.003,-0.01 0.005,-0.013 0.521,-1.518 2.106,-3.053 4.197,-3.229 1.614,0.001 4.153,1.176 4.501,4.102 0.008,0.075 0.016,0.149 0.021,0.223 0.001,0.023 0.004,0.048 0.006,0.074 0.002,0.034 0.004,0.068 0.007,0.104 0.005,0.095 0.007,0.191 0.007,0.29 0,0.167 0.003,0.317 0.008,0.454 v 7.914 c 0,0.202 0.163,0.366 0.366,0.366 h 1.101 c 0.202,0 0.365,-0.164 0.365,-0.366 v -8.115 c 0.003,-2.68 -0.809,-4.612 -2.409,-5.743 z"
+ id="path4127" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 396.461,564.405 c -0.066,-0.056 -0.15,-0.087 -0.236,-0.087 -0.02,0 -0.04,0.002 -0.06,0.006 -0.292,0.049 -0.481,0.049 -0.719,0.049 -0.573,0 -1.086,-0.167 -1.086,-2.189 v -4.942 c 0,-1.169 -0.089,-2.677 -0.79,-3.977 -0.909,-1.687 -2.583,-2.543 -4.976,-2.543 -1.235,0 -3.101,0.258 -4.982,1.483 -0.158,0.102 -0.211,0.307 -0.125,0.474 l 0.43,0.833 c 0.049,0.094 0.136,0.162 0.238,0.187 0.029,0.007 0.058,0.011 0.086,0.011 0.076,0 0.149,-0.023 0.211,-0.067 1.154,-0.817 2.587,-1.249 4.143,-1.249 1.992,0 2.995,0.925 3.491,1.965 l 0,0 c 0.5,1.107 -0.28,2.196 -1.14,2.287 -0.042,0.002 -0.084,0.004 -0.126,0.006 -0.006,0 -0.015,10e-4 -0.021,0 -0.028,0 -0.051,10e-4 -0.071,0.005 -5.475,0.312 -8.249,2.151 -8.249,5.476 0,1.012 0.4,2.024 1.099,2.778 0.615,0.664 1.776,1.456 3.808,1.456 1.732,0 3.128,-0.554 4.153,-1.232 0.03,-0.016 0.064,-0.038 0.105,-0.069 0.011,-0.01 0.021,-0.017 0.032,-0.025 0.063,-0.043 0.128,-0.088 0.188,-0.134 0.636,-0.41 0.982,-0.175 1.309,0.252 10e-4,10e-4 0.003,0.002 0.004,0.003 0.419,0.569 1.077,0.857 1.976,0.857 0.422,0 0.793,-0.047 1.169,-0.146 0.159,-0.042 0.271,-0.188 0.271,-0.353 v -0.833 c -0.002,-0.111 -0.05,-0.211 -0.132,-0.282 z m -9.103,0.263 c -1.517,0 -3.048,-0.858 -3.048,-2.778 0,-2.426 2.96,-3.281 6.068,-3.514 1.158,-0.021 2.043,1.078 2.15,2.282 v 0.493 c -0.031,0.323 -0.122,0.644 -0.283,0.942 -0.081,0.13 -0.177,0.277 -0.293,0.431 -0.02,0.023 -0.038,0.047 -0.051,0.066 -0.72,0.923 -2.107,2.078 -4.543,2.078 z"
+ id="path4129" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 298.717,551.098 h -1.021 c -0.195,0 -0.356,0.154 -0.365,0.351 -0.032,0.807 -0.445,0.915 -1.349,0.383 -0.004,-0.004 -0.009,-0.008 -0.013,-0.011 -0.089,-0.059 -0.181,-0.114 -0.272,-0.168 -0.005,-0.004 -0.011,-0.006 -0.016,-0.011 -0.043,-0.029 -0.084,-0.052 -0.123,-0.069 -0.988,-0.547 -2.147,-0.823 -3.453,-0.823 -1.865,0 -3.704,0.772 -5.048,2.119 -1.039,1.041 -2.277,2.906 -2.277,5.877 0,1.905 0.672,3.697 1.892,5.048 1.294,1.435 3.109,2.224 5.111,2.224 1.551,0 2.75,-0.409 3.658,-0.946 0.002,-10e-4 0.004,-10e-4 0.006,-10e-4 0.974,-0.437 1.796,-1.09 1.563,1.112 -0.436,2.832 -2.254,4.372 -5.228,4.372 -2.082,0 -3.646,-0.701 -4.591,-1.288 -0.058,-0.037 -0.125,-0.057 -0.193,-0.057 -0.03,0 -0.061,0.005 -0.09,0.012 -0.097,0.024 -0.179,0.088 -0.228,0.175 l -0.483,0.86 c -0.095,0.167 -0.042,0.38 0.118,0.484 1.422,0.929 3.446,1.483 5.413,1.483 1.342,0 4.635,-0.312 6.231,-3.198 0.69,-1.237 1.013,-2.976 1.013,-5.47 v -8.707 c 0,-1.233 0.035,-2.332 0.107,-3.36 0.006,-0.1 -0.029,-0.2 -0.098,-0.273 -0.066,-0.075 -0.163,-0.118 -0.264,-0.118 z m -8.425,12.981 c -0.302,-0.12 -3.795,-1.604 -3.498,-5.891 0.275,-3.963 2.854,-5.217 3.251,-5.387 0.638,-0.248 1.347,-0.382 2.115,-0.382 0.83,0 1.553,0.162 2.171,0.436 3.415,1.93 3.839,8.205 0.442,10.706 -0.794,0.498 -1.714,0.784 -2.667,0.784 -0.668,10e-4 -1.272,-0.096 -1.814,-0.266 z"
+ id="path4131" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 377.443,551.098 h -1.021 c -0.196,0 -0.357,0.154 -0.365,0.351 -0.034,0.807 -0.445,0.915 -1.349,0.383 -0.004,-0.004 -0.009,-0.008 -0.013,-0.011 -0.089,-0.059 -0.181,-0.114 -0.273,-0.168 -0.005,-0.004 -0.01,-0.008 -0.015,-0.011 -0.043,-0.029 -0.083,-0.052 -0.122,-0.069 -0.988,-0.547 -2.147,-0.823 -3.453,-0.823 -1.865,0 -3.706,0.772 -5.049,2.119 -1.039,1.041 -2.276,2.906 -2.276,5.877 0,1.905 0.672,3.697 1.891,5.048 1.294,1.435 3.109,2.224 5.112,2.224 1.551,0 2.75,-0.409 3.658,-0.946 10e-4,-10e-4 0.003,-10e-4 0.006,-10e-4 0.974,-0.438 1.796,-1.09 1.563,1.112 -0.437,2.832 -2.253,4.372 -5.227,4.372 -2.083,0 -3.646,-0.701 -4.591,-1.288 -0.058,-0.037 -0.125,-0.057 -0.193,-0.057 -0.03,0 -0.061,0.005 -0.09,0.012 -0.097,0.024 -0.179,0.088 -0.227,0.175 l -0.484,0.86 c -0.094,0.167 -0.042,0.38 0.118,0.484 1.423,0.929 3.446,1.483 5.414,1.483 1.341,0 4.635,-0.312 6.23,-3.198 0.691,-1.237 1.013,-2.976 1.013,-5.47 v -8.707 c 0,-1.233 0.035,-2.332 0.106,-3.36 0.008,-0.1 -0.028,-0.2 -0.097,-0.273 -0.068,-0.075 -0.164,-0.118 -0.266,-0.118 z m -8.424,12.981 c -0.302,-0.12 -3.795,-1.604 -3.498,-5.891 0.274,-3.96 2.851,-5.215 3.25,-5.385 0.637,-0.25 1.347,-0.384 2.116,-0.384 0.831,0 1.553,0.162 2.172,0.436 3.414,1.93 3.838,8.205 0.44,10.706 -0.794,0.498 -1.714,0.784 -2.666,0.784 -0.669,10e-4 -1.273,-0.096 -1.814,-0.266 z"
+ id="path4133" />
+ <linearGradient
+ id="linearGradient7072"
+ gradientUnits="userSpaceOnUse"
+ x1="329.353"
+ y1="540.44342"
+ x2="329.353"
+ y2="570.87878">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop7074" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop7076" />
+ </linearGradient>
+ <path
+ style="fill:url(#SVGID_17_)"
+ inkscape:connector-curvature="0"
+ d="m 328.179,544.004 c -1.166,1.164 -2.049,2.664 -2.51,4.316 -3.03,-0.047 -6.07,1.134 -8.482,3.546 -4.062,4.06 -5.324,11.427 0.333,17.083 4.759,4.761 11.867,4.831 16.531,0.167 2.206,-2.207 3.61,-5.239 3.64,-8.449 1.566,-0.416 3.055,-1.25 4.324,-2.519 3.469,-3.471 4.531,-9.436 -0.155,-14.124 -3.907,-3.905 -9.789,-3.914 -13.681,-0.02 z m 12.779,10.184 c -0.667,0.667 -1.519,1.159 -2.46,1.428 -1.51,0.434 -2.322,0.389 -2.842,1.255 -0.519,0.866 -0.461,0.967 -0.438,2.59 0.027,1.841 -0.665,3.747 -2.4,5.484 -2.653,2.652 -7.551,3.244 -11.155,-0.359 -2.839,-2.841 -4.172,-7.62 -0.388,-11.404 1.404,-1.402 3.424,-2.233 5.548,-2.195 1.477,0.027 1.649,0.08 2.6,-0.351 0.952,-0.433 0.54,-1.397 1.108,-3.095 0.277,-0.827 0.771,-1.643 1.542,-2.416 2.058,-2.058 5.846,-2.53 8.621,0.245 2.26,2.261 3.218,5.864 0.264,8.818 z"
+ id="path4140" />
+ </g>
+ <g
+ transform="translate(394.28571,57.50504)"
+ id="g4220">
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 286.381,533.79 v -5.23 c 0,-0.65 -0.011,-1.18 -0.032,-1.59 h 0.664 l 0.04,0.84 h 0.016 c 0.323,-0.633 0.803,-0.95 1.44,-0.95 0.485,0 0.889,0.229 1.212,0.688 0.323,0.459 0.484,1.046 0.484,1.763 0,0.797 -0.174,1.431 -0.522,1.902 -0.348,0.472 -0.782,0.708 -1.302,0.708 -0.264,0 -0.506,-0.067 -0.726,-0.2 -0.22,-0.134 -0.392,-0.323 -0.514,-0.57 h -0.016 v 2.641 h -0.744 z m 0.744,-4.761 v 0.78 c 0,0.39 0.108,0.718 0.326,0.982 0.218,0.264 0.479,0.397 0.786,0.397 0.373,0 0.669,-0.166 0.888,-0.497 0.218,-0.332 0.328,-0.779 0.328,-1.343 0,-0.507 -0.108,-0.925 -0.324,-1.255 -0.216,-0.33 -0.503,-0.495 -0.86,-0.495 -0.229,0 -0.44,0.082 -0.632,0.247 -0.192,0.165 -0.325,0.367 -0.4,0.605 -0.075,0.238 -0.112,0.433 -0.112,0.579 z"
+ id="path4222" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 295.245,529.34 c 0,0.52 -0.086,0.975 -0.26,1.364 -0.173,0.391 -0.416,0.69 -0.728,0.9 -0.312,0.21 -0.649,0.315 -1.012,0.315 -0.565,0 -1.032,-0.231 -1.4,-0.692 -0.368,-0.462 -0.552,-1.062 -0.552,-1.798 0,-0.783 0.19,-1.407 0.57,-1.873 0.38,-0.465 0.859,-0.697 1.438,-0.697 0.576,0 1.044,0.231 1.404,0.692 0.36,0.463 0.54,1.058 0.54,1.789 z m -3.192,0.059 c 0,0.53 0.113,0.968 0.34,1.313 0.227,0.345 0.511,0.518 0.852,0.518 0.35,0 0.641,-0.173 0.874,-0.52 0.233,-0.347 0.35,-0.794 0.35,-1.341 0,-0.503 -0.107,-0.932 -0.322,-1.287 -0.215,-0.354 -0.508,-0.532 -0.878,-0.532 -0.376,0 -0.672,0.176 -0.89,0.527 -0.218,0.351 -0.326,0.792 -0.326,1.322 z"
+ id="path4224" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 296.173,526.97 h 0.76 c 0.507,2.347 0.779,3.673 0.816,3.979 h 0.016 c 0.029,-0.26 0.36,-1.586 0.992,-3.979 h 0.632 c 0.573,2.243 0.894,3.57 0.96,3.979 h 0.016 c 0.075,-0.5 0.168,-0.989 0.28,-1.47 l 0.584,-2.51 h 0.736 l -1.28,4.84 h -0.688 c -0.44,-1.693 -0.688,-2.656 -0.744,-2.888 -0.056,-0.231 -0.12,-0.559 -0.192,-0.982 h -0.016 c -0.088,0.504 -0.204,1.017 -0.348,1.54 l -0.644,2.33 h -0.688 l -1.192,-4.839 z"
+ id="path4226" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 306.397,529.579 h -2.808 c 0.011,0.523 0.14,0.925 0.388,1.203 0.248,0.278 0.569,0.417 0.964,0.417 0.429,0 0.805,-0.083 1.128,-0.25 l 0.128,0.65 c -0.381,0.213 -0.832,0.32 -1.352,0.32 -0.603,0 -1.083,-0.224 -1.44,-0.67 -0.357,-0.447 -0.536,-1.04 -0.536,-1.78 0,-0.74 0.171,-1.36 0.514,-1.86 0.343,-0.5 0.803,-0.75 1.382,-0.75 0.533,0 0.942,0.223 1.228,0.668 0.285,0.444 0.428,0.989 0.428,1.632 0,0.184 -0.008,0.324 -0.024,0.42 z m -2.8,-0.67 h 2.104 c 0,-0.416 -0.086,-0.752 -0.258,-1.007 -0.172,-0.255 -0.417,-0.383 -0.734,-0.383 -0.301,0 -0.551,0.128 -0.748,0.383 -0.197,0.255 -0.319,0.591 -0.364,1.007 z"
+ id="path4228" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 307.797,531.81 v -3.3 c 0,-0.627 -0.011,-1.141 -0.032,-1.54 h 0.656 l 0.048,0.97 c 0.099,-0.34 0.25,-0.604 0.452,-0.795 0.202,-0.191 0.422,-0.285 0.66,-0.285 0.051,0 0.118,0.007 0.2,0.021 v 0.88 c -0.072,-0.021 -0.157,-0.03 -0.256,-0.03 -0.27,0 -0.501,0.132 -0.694,0.396 -0.193,0.263 -0.29,0.638 -0.29,1.125 v 2.56 h -0.744 z"
+ id="path4230" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 314.229,529.579 h -2.808 c 0.011,0.523 0.14,0.925 0.388,1.203 0.248,0.278 0.569,0.417 0.964,0.417 0.429,0 0.805,-0.083 1.128,-0.25 l 0.128,0.65 c -0.381,0.213 -0.832,0.32 -1.352,0.32 -0.603,0 -1.083,-0.224 -1.44,-0.67 -0.357,-0.447 -0.536,-1.04 -0.536,-1.78 0,-0.74 0.171,-1.36 0.514,-1.86 0.343,-0.5 0.803,-0.75 1.382,-0.75 0.533,0 0.942,0.223 1.228,0.668 0.285,0.444 0.428,0.989 0.428,1.632 0,0.184 -0.008,0.324 -0.024,0.42 z m -2.8,-0.67 h 2.104 c 0,-0.416 -0.086,-0.752 -0.258,-1.007 -0.172,-0.255 -0.417,-0.383 -0.734,-0.383 -0.301,0 -0.551,0.128 -0.748,0.383 -0.197,0.255 -0.319,0.591 -0.364,1.007 z"
+ id="path4232" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 318.365,524.689 h 0.744 v 5.851 c 0,0.539 0.011,0.963 0.032,1.27 h -0.664 l -0.056,-0.86 c -0.128,0.307 -0.312,0.545 -0.552,0.716 -0.24,0.17 -0.512,0.255 -0.816,0.255 -0.491,0 -0.9,-0.231 -1.228,-0.692 -0.328,-0.462 -0.492,-1.048 -0.492,-1.758 0,-0.771 0.172,-1.397 0.518,-1.883 0.346,-0.484 0.773,-0.728 1.282,-0.728 0.272,0 0.515,0.067 0.73,0.2 0.215,0.134 0.382,0.313 0.502,0.54 v -2.911 z m 0,5.04 v -0.77 c 0,-0.41 -0.106,-0.741 -0.318,-0.993 -0.212,-0.251 -0.466,-0.377 -0.762,-0.377 -0.36,0 -0.65,0.168 -0.87,0.505 -0.22,0.337 -0.33,0.774 -0.33,1.314 0,0.507 0.104,0.927 0.312,1.261 0.208,0.333 0.499,0.5 0.872,0.5 0.285,0 0.539,-0.131 0.762,-0.393 0.223,-0.262 0.334,-0.61 0.334,-1.047 z"
+ id="path4234" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 323.013,530.54 v -5.851 h 0.76 v 3.07 c 0.283,-0.601 0.739,-0.9 1.368,-0.9 0.496,0 0.903,0.229 1.22,0.688 0.317,0.459 0.476,1.05 0.476,1.772 0,0.79 -0.175,1.421 -0.526,1.893 -0.351,0.472 -0.778,0.708 -1.282,0.708 -0.605,0 -1.062,-0.304 -1.368,-0.91 l -0.032,0.8 h -0.648 c 0.021,-0.307 0.032,-0.731 0.032,-1.27 z m 0.744,-1.5 v 0.789 c 0,0.36 0.107,0.676 0.322,0.945 0.215,0.271 0.48,0.405 0.798,0.405 0.371,0 0.665,-0.164 0.882,-0.492 0.217,-0.329 0.326,-0.771 0.326,-1.328 0,-0.517 -0.108,-0.94 -0.324,-1.272 -0.216,-0.331 -0.505,-0.497 -0.868,-0.497 -0.32,0 -0.589,0.146 -0.808,0.438 -0.219,0.29 -0.328,0.628 -0.328,1.012 z"
+ id="path4236" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 327.693,526.97 h 0.816 l 0.88,2.885 c 0.075,0.24 0.163,0.549 0.264,0.925 0.104,-0.45 0.184,-0.774 0.24,-0.975 l 0.792,-2.835 h 0.8 l -1.096,3.46 c -0.35,1.1 -0.633,1.86 -0.85,2.28 -0.217,0.42 -0.453,0.73 -0.708,0.932 -0.255,0.202 -0.493,0.328 -0.714,0.378 l -0.176,-0.78 c 0.403,-0.156 0.717,-0.427 0.942,-0.81 0.225,-0.384 0.338,-0.64 0.338,-0.771 0,-0.057 -0.016,-0.133 -0.048,-0.229 l -1.48,-4.46 z"
+ id="path4238" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-foreground-white.png b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-foreground-white.png
new file mode 100644
index 00000000000..bc275e523cb
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-foreground-white.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-foreground-white.svg b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-foreground-white.svg
new file mode 100644
index 00000000000..5d5ec8f3117
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-foreground-white.svg
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="88"
+ height="31"
+ id="svg6853"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 63">
+ <defs
+ id="defs6855">
+ <linearGradient
+ id="SVGID_23_"
+ gradientUnits="userSpaceOnUse"
+ x1="81.903801"
+ y1="561.35449"
+ x2="81.903801"
+ y2="584.04199">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop4378" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop4380" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="70.516543"
+ inkscape:cy="5.8887068"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="1928"
+ inkscape:window-y="52"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata6858">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-276,-331.14789)">
+ <rect
+ style="fill:#00293f"
+ x="276"
+ y="331.14789"
+ width="88"
+ height="31"
+ id="rect4363" />
+ <g
+ transform="translate(230,-223.8521)"
+ id="g4365">
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 70.143,568.477 c -0.48,-0.104 -0.82,-0.159 -1.238,-0.159 -1.022,0 -1.916,0.297 -2.656,0.886 -0.033,0.021 -0.069,0.051 -0.108,0.089 -0.083,0.071 -0.163,0.145 -0.241,0.223 -0.531,0.472 -0.992,0.7 -0.992,-0.444 v -0.239 c 0,-0.146 -0.118,-0.264 -0.263,-0.264 h -0.756 c -0.071,0 -0.139,0.03 -0.188,0.08 -0.05,0.051 -0.076,0.12 -0.074,0.192 0.039,1.197 0.078,2.436 0.078,3.458 v 6.74 c 0,0.146 0.118,0.264 0.263,0.264 h 0.795 c 0.145,0 0.263,-0.118 0.263,-0.264 v -3.563 c 0,-0.493 0.039,-1.368 0.111,-1.73 0.564,-2.804 1.792,-4.166 3.751,-4.166 0.352,0 0.676,0.06 1.033,0.131 0.017,0.003 0.034,0.005 0.051,0.005 0.053,0 0.105,-0.017 0.15,-0.047 0.059,-0.041 0.098,-0.104 0.11,-0.174 l 0.116,-0.717 c 0.021,-0.138 -0.068,-0.272 -0.205,-0.301 z"
+ id="path4367" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 102.151,569.049 c -0.903,-0.637 -1.902,-0.731 -2.417,-0.731 -1.046,0 -2.03,0.327 -2.812,0.896 -0.002,0 -0.003,10e-4 -0.005,0.002 -0.361,0.232 -0.945,0.326 -0.941,-0.224 v -0.009 c 0,-0.038 -0.001,-0.07 -0.005,-0.1 l -0.003,-0.064 c -0.006,-0.142 -0.122,-0.252 -0.262,-0.252 H 94.95 c -0.073,0 -0.142,0.031 -0.192,0.084 -0.05,0.054 -0.075,0.124 -0.071,0.196 0.054,0.82 0.078,1.5 0.078,2.346 v 7.845 c 0,0.146 0.117,0.264 0.263,0.264 h 0.794 c 0.146,0 0.263,-0.118 0.263,-0.264 v -6.295 c 0,-0.293 0.091,-0.634 0.176,-0.855 0.002,-0.003 0.002,-0.005 0.003,-0.008 0.375,-1.094 1.519,-2.2 3.025,-2.327 1.164,10e-4 2.993,0.847 3.244,2.955 0.006,0.055 0.011,0.107 0.016,0.161 10e-4,0.018 0.003,0.035 0.004,0.054 0.002,0.024 0.003,0.05 0.005,0.074 0.003,0.068 0.005,0.137 0.005,0.21 0,0.12 10e-4,0.228 0.006,0.327 v 5.704 c 0,0.146 0.118,0.264 0.263,0.264 h 0.794 c 0.145,0 0.263,-0.118 0.263,-0.264 v -5.851 c 0.001,-1.93 -0.584,-3.322 -1.738,-4.138 z"
+ id="path4369" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 129.567,578.159 c -0.047,-0.04 -0.108,-0.063 -0.169,-0.063 -0.015,0 -0.029,10e-4 -0.043,0.005 -0.211,0.035 -0.347,0.035 -0.519,0.035 -0.413,0 -0.783,-0.121 -0.783,-1.577 v -3.563 c 0,-0.843 -0.063,-1.93 -0.568,-2.866 -0.656,-1.215 -1.862,-1.831 -3.586,-1.831 -0.891,0 -2.236,0.184 -3.592,1.068 -0.113,0.073 -0.152,0.222 -0.09,0.341 l 0.311,0.6 c 0.034,0.069 0.098,0.119 0.171,0.137 0.021,0.004 0.042,0.007 0.062,0.007 0.055,0 0.108,-0.018 0.152,-0.049 0.832,-0.589 1.865,-0.9 2.986,-0.9 1.436,0 2.158,0.667 2.516,1.416 l 0,0 c 0.36,0.798 -0.203,1.583 -0.822,1.647 -0.031,0.004 -0.061,0.005 -0.091,0.006 -0.005,0 -0.011,10e-4 -0.016,0 -0.019,0 -0.036,0.002 -0.051,0.003 -3.946,0.225 -5.944,1.55 -5.944,3.947 0,0.728 0.288,1.457 0.792,2.001 0.443,0.479 1.28,1.051 2.744,1.051 1.248,0 2.254,-0.4 2.993,-0.889 0.022,-0.012 0.047,-0.027 0.076,-0.051 0.009,-0.006 0.016,-0.013 0.023,-0.019 0.046,-0.031 0.092,-0.063 0.136,-0.096 0.458,-0.296 0.708,-0.126 0.943,0.182 0.001,0 0.002,10e-4 0.003,0.002 0.302,0.411 0.776,0.619 1.424,0.619 0.304,0 0.572,-0.035 0.842,-0.106 0.116,-0.031 0.195,-0.136 0.195,-0.254 v -0.603 c -0.001,-0.078 -0.036,-0.151 -0.095,-0.2 z m -6.56,0.19 c -1.094,0 -2.197,-0.619 -2.197,-2.002 0,-1.749 2.134,-2.366 4.373,-2.534 0.835,-0.015 1.474,0.778 1.551,1.646 v 0.355 c -0.023,0.232 -0.089,0.463 -0.204,0.678 -0.058,0.096 -0.128,0.201 -0.212,0.312 -0.014,0.017 -0.027,0.033 -0.037,0.049 -0.519,0.663 -1.519,1.496 -3.274,1.496 z"
+ id="path4371" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 59.823,568.567 h -0.736 c -0.142,0 -0.257,0.111 -0.263,0.253 -0.023,0.582 -0.32,0.66 -0.972,0.275 -0.003,-0.003 -0.006,-0.006 -0.01,-0.008 -0.064,-0.042 -0.129,-0.082 -0.195,-0.121 -0.003,-0.002 -0.008,-0.005 -0.011,-0.008 -0.032,-0.021 -0.061,-0.036 -0.089,-0.05 -0.712,-0.395 -1.547,-0.592 -2.488,-0.592 -1.344,0 -2.67,0.556 -3.638,1.527 -0.75,0.75 -1.642,2.093 -1.642,4.235 0,1.373 0.484,2.664 1.363,3.638 0.932,1.032 2.24,1.604 3.684,1.604 1.118,0 1.982,-0.296 2.637,-0.684 10e-4,0 0.003,-10e-4 0.004,-10e-4 0.702,-0.313 1.294,-0.784 1.126,0.802 -0.313,2.042 -1.624,3.15 -3.768,3.15 -1.5,0 -2.628,-0.504 -3.309,-0.928 -0.042,-0.026 -0.09,-0.041 -0.139,-0.041 -0.021,0 -0.043,0.005 -0.065,0.01 -0.07,0.018 -0.128,0.063 -0.164,0.126 l -0.349,0.619 c -0.068,0.121 -0.031,0.274 0.084,0.35 1.026,0.67 2.484,1.069 3.902,1.069 0.968,0 3.34,-0.225 4.492,-2.305 0.497,-0.892 0.729,-2.146 0.729,-3.942 v -6.275 c 0,-0.889 0.025,-1.683 0.077,-2.423 0.005,-0.072 -0.02,-0.144 -0.07,-0.196 -0.047,-0.052 -0.117,-0.084 -0.19,-0.084 z m -6.072,9.356 c -0.218,-0.087 -2.735,-1.155 -2.521,-4.245 0.199,-2.855 2.058,-3.758 2.344,-3.882 0.458,-0.179 0.97,-0.276 1.524,-0.276 0.599,0 1.119,0.118 1.565,0.315 2.46,1.392 2.767,5.913 0.319,7.716 -0.573,0.358 -1.235,0.565 -1.922,0.565 -0.483,0 -0.919,-0.068 -1.309,-0.193 z"
+ id="path4373" />
+ <path
+ style="fill:#ffffff"
+ inkscape:connector-curvature="0"
+ d="m 116.563,568.567 h -0.736 c -0.141,0 -0.257,0.111 -0.263,0.253 -0.024,0.582 -0.321,0.66 -0.971,0.275 -0.004,-0.003 -0.007,-0.006 -0.011,-0.008 -0.063,-0.042 -0.13,-0.082 -0.196,-0.121 -0.003,-0.002 -0.007,-0.005 -0.011,-0.008 -0.031,-0.021 -0.06,-0.036 -0.087,-0.05 -0.713,-0.395 -1.548,-0.592 -2.489,-0.592 -1.344,0 -2.67,0.556 -3.638,1.527 -0.749,0.75 -1.642,2.093 -1.642,4.235 0,1.373 0.484,2.664 1.363,3.638 0.933,1.032 2.241,1.604 3.684,1.604 1.118,0 1.982,-0.296 2.637,-0.684 10e-4,0 0.002,-10e-4 0.003,-10e-4 0.702,-0.314 1.295,-0.785 1.127,0.802 -0.315,2.042 -1.625,3.15 -3.768,3.15 -1.5,0 -2.627,-0.504 -3.308,-0.928 -0.043,-0.026 -0.09,-0.041 -0.139,-0.041 -0.022,0 -0.044,0.005 -0.065,0.01 -0.07,0.018 -0.129,0.063 -0.164,0.126 l -0.349,0.619 c -0.069,0.121 -0.031,0.274 0.084,0.35 1.026,0.67 2.484,1.069 3.902,1.069 0.966,0 3.34,-0.225 4.491,-2.305 0.497,-0.892 0.73,-2.146 0.73,-3.942 v -6.275 c 0,-0.889 0.025,-1.683 0.077,-2.423 0.005,-0.072 -0.021,-0.144 -0.07,-0.196 -0.048,-0.052 -0.116,-0.084 -0.191,-0.084 z m -6.072,9.356 c -0.218,-0.087 -2.735,-1.155 -2.521,-4.245 0.198,-2.854 2.054,-3.757 2.342,-3.882 0.459,-0.179 0.971,-0.276 1.525,-0.276 0.599,0 1.119,0.118 1.566,0.315 2.459,1.392 2.766,5.913 0.317,7.716 -0.572,0.359 -1.235,0.565 -1.921,0.565 -0.482,0 -0.917,-0.068 -1.308,-0.193 z"
+ id="path4375" />
+ <linearGradient
+ id="linearGradient6836"
+ gradientUnits="userSpaceOnUse"
+ x1="81.903801"
+ y1="561.35449"
+ x2="81.903801"
+ y2="584.04199">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop6838" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop6840" />
+ </linearGradient>
+ <path
+ style="fill:url(#SVGID_23_)"
+ inkscape:connector-curvature="0"
+ d="m 81.057,563.455 c -0.84,0.839 -1.477,1.92 -1.809,3.11 -2.184,-0.034 -4.375,0.816 -6.113,2.557 -2.927,2.926 -3.837,8.236 0.24,12.311 3.43,3.433 8.553,3.483 11.915,0.121 1.589,-1.59 2.602,-3.776 2.623,-6.09 1.128,-0.3 2.202,-0.901 3.117,-1.814 2.501,-2.501 3.266,-6.8 -0.112,-10.181 -2.816,-2.814 -7.056,-2.82 -9.861,-0.014 z m 9.211,7.341 c -0.48,0.479 -1.095,0.834 -1.773,1.029 -1.088,0.312 -1.674,0.279 -2.048,0.903 -0.374,0.624 -0.333,0.698 -0.316,1.868 0.02,1.325 -0.479,2.699 -1.73,3.952 -1.913,1.911 -5.442,2.338 -8.04,-0.26 -2.045,-2.048 -3.006,-5.492 -0.28,-8.22 1.013,-1.011 2.469,-1.609 4,-1.581 1.064,0.018 1.188,0.057 1.873,-0.253 0.686,-0.313 0.388,-1.007 0.798,-2.23 0.2,-0.598 0.556,-1.185 1.112,-1.742 1.483,-1.483 4.213,-1.823 6.213,0.177 1.629,1.629 2.319,4.227 0.191,6.357 z"
+ id="path4382" />
+ </g>
+ <g
+ transform="translate(230,-223.8521)"
+ id="g4384">
+ <path
+ style="fill:#3fa9f5"
+ inkscape:connector-curvature="0"
+ d="m 94.988,565.938 v -4.184 c 0,-0.521 -0.008,-0.944 -0.025,-1.272 h 0.531 l 0.032,0.672 h 0.013 c 0.258,-0.507 0.642,-0.76 1.152,-0.76 0.388,0 0.711,0.184 0.969,0.55 0.258,0.367 0.387,0.837 0.387,1.41 0,0.638 -0.139,1.145 -0.417,1.522 -0.278,0.377 -0.625,0.565 -1.042,0.565 -0.211,0 -0.405,-0.054 -0.581,-0.16 -0.176,-0.106 -0.313,-0.259 -0.411,-0.456 h -0.013 v 2.112 h -0.595 z m 0.595,-3.807 v 0.624 c 0,0.312 0.087,0.573 0.261,0.785 0.174,0.213 0.383,0.318 0.629,0.318 0.299,0 0.536,-0.133 0.71,-0.397 0.175,-0.266 0.262,-0.624 0.262,-1.074 0,-0.405 -0.086,-0.74 -0.259,-1.004 -0.173,-0.265 -0.402,-0.396 -0.688,-0.396 -0.183,0 -0.352,0.066 -0.505,0.198 -0.154,0.132 -0.26,0.293 -0.32,0.484 -0.06,0.19 -0.09,0.344 -0.09,0.462 z"
+ id="path4386" />
+ <path
+ style="fill:#3fa9f5"
+ inkscape:connector-curvature="0"
+ d="m 101.823,562.379 c 0,0.416 -0.069,0.779 -0.208,1.092 -0.139,0.312 -0.333,0.552 -0.583,0.72 -0.25,0.168 -0.52,0.252 -0.81,0.252 -0.452,0 -0.826,-0.185 -1.12,-0.554 -0.294,-0.369 -0.441,-0.849 -0.441,-1.438 0,-0.626 0.152,-1.126 0.456,-1.498 0.304,-0.372 0.688,-0.558 1.15,-0.558 0.461,0 0.835,0.185 1.123,0.554 0.288,0.369 0.433,0.846 0.433,1.43 z m -2.553,0.048 c 0,0.424 0.091,0.773 0.272,1.05 0.182,0.276 0.409,0.414 0.682,0.414 0.28,0 0.513,-0.139 0.699,-0.416 0.186,-0.277 0.28,-0.635 0.28,-1.072 0,-0.402 -0.086,-0.746 -0.258,-1.03 -0.172,-0.283 -0.406,-0.426 -0.702,-0.426 -0.301,0 -0.539,0.141 -0.712,0.422 -0.173,0.281 -0.261,0.634 -0.261,1.058 z"
+ id="path4388" />
+ <path
+ style="fill:#3fa9f5"
+ inkscape:connector-curvature="0"
+ d="m 102.31,560.482 h 0.608 c 0.405,1.878 0.623,2.938 0.653,3.184 h 0.013 c 0.023,-0.208 0.288,-1.269 0.794,-3.184 h 0.505 c 0.458,1.795 0.715,2.856 0.768,3.184 h 0.013 c 0.06,-0.399 0.134,-0.792 0.224,-1.176 l 0.467,-2.008 h 0.589 l -1.024,3.872 h -0.55 c -0.352,-1.354 -0.55,-2.125 -0.595,-2.31 -0.045,-0.186 -0.096,-0.447 -0.153,-0.786 h -0.013 c -0.07,0.402 -0.163,0.813 -0.278,1.231 l -0.515,1.864 h -0.55 l -0.956,-3.871 z"
+ id="path4390" />
+ <path
+ style="fill:#3fa9f5"
+ inkscape:connector-curvature="0"
+ d="m 110.232,562.57 h -2.246 c 0.008,0.419 0.112,0.739 0.31,0.962 0.199,0.223 0.456,0.334 0.771,0.334 0.343,0 0.644,-0.066 0.902,-0.2 l 0.103,0.521 c -0.305,0.171 -0.666,0.256 -1.082,0.256 -0.482,0 -0.866,-0.179 -1.152,-0.536 -0.286,-0.357 -0.429,-0.832 -0.429,-1.424 0,-0.592 0.137,-1.088 0.411,-1.488 0.274,-0.399 0.643,-0.6 1.105,-0.6 0.427,0 0.754,0.178 0.982,0.534 0.229,0.355 0.343,0.791 0.343,1.306 0.002,0.146 -0.004,0.258 -0.018,0.335 z m -2.239,-0.536 h 1.683 c 0,-0.333 -0.069,-0.602 -0.207,-0.806 -0.137,-0.204 -0.333,-0.306 -0.587,-0.306 -0.241,0 -0.441,0.102 -0.599,0.306 -0.158,0.204 -0.254,0.473 -0.29,0.806 z"
+ id="path4392" />
+ <path
+ style="fill:#3fa9f5"
+ inkscape:connector-curvature="0"
+ d="m 111.097,564.354 v -2.64 c 0,-0.502 -0.008,-0.912 -0.025,-1.232 h 0.524 l 0.039,0.776 c 0.079,-0.272 0.2,-0.484 0.362,-0.637 0.162,-0.151 0.338,-0.228 0.528,-0.228 0.041,0 0.094,0.005 0.16,0.016 v 0.704 c -0.058,-0.016 -0.126,-0.023 -0.205,-0.023 -0.215,0 -0.4,0.105 -0.555,0.315 -0.155,0.211 -0.232,0.511 -0.232,0.9 v 2.048 h -0.596 z"
+ id="path4394" />
+ <path
+ style="fill:#3fa9f5"
+ inkscape:connector-curvature="0"
+ d="m 115.986,562.57 h -2.246 c 0.008,0.419 0.112,0.739 0.31,0.962 0.199,0.223 0.456,0.334 0.771,0.334 0.343,0 0.644,-0.066 0.902,-0.2 l 0.103,0.521 c -0.305,0.171 -0.666,0.256 -1.082,0.256 -0.482,0 -0.866,-0.179 -1.152,-0.536 -0.286,-0.357 -0.429,-0.832 -0.429,-1.424 0,-0.592 0.137,-1.088 0.411,-1.488 0.274,-0.399 0.643,-0.6 1.105,-0.6 0.427,0 0.754,0.178 0.982,0.534 0.229,0.355 0.343,0.791 0.343,1.306 0.002,0.146 -0.004,0.258 -0.018,0.335 z m -2.239,-0.536 h 1.683 c 0,-0.333 -0.069,-0.602 -0.207,-0.806 -0.137,-0.204 -0.333,-0.306 -0.587,-0.306 -0.241,0 -0.441,0.102 -0.599,0.306 -0.158,0.204 -0.254,0.473 -0.29,0.806 z"
+ id="path4396" />
+ <path
+ style="fill:#3fa9f5"
+ inkscape:connector-curvature="0"
+ d="m 119.04,558.658 h 0.595 v 4.681 c 0,0.432 0.008,0.771 0.025,1.016 h -0.531 l -0.045,-0.688 c -0.102,0.246 -0.25,0.437 -0.441,0.572 -0.191,0.135 -0.41,0.204 -0.653,0.204 -0.393,0 -0.72,-0.185 -0.982,-0.554 -0.262,-0.369 -0.394,-0.838 -0.394,-1.406 0,-0.616 0.138,-1.118 0.415,-1.506 0.277,-0.388 0.618,-0.582 1.025,-0.582 0.218,0 0.412,0.054 0.584,0.16 0.172,0.106 0.306,0.251 0.402,0.432 v -2.329 z m 0,4.032 v -0.616 c 0,-0.328 -0.085,-0.593 -0.254,-0.794 -0.17,-0.201 -0.373,-0.302 -0.61,-0.302 -0.288,0 -0.52,0.135 -0.696,0.404 -0.176,0.27 -0.264,0.62 -0.264,1.052 0,0.405 0.083,0.741 0.25,1.008 0.167,0.267 0.399,0.4 0.698,0.4 0.228,0 0.432,-0.104 0.609,-0.314 0.177,-0.209 0.267,-0.488 0.267,-0.838 z"
+ id="path4398" />
+ <path
+ style="fill:#3fa9f5"
+ inkscape:connector-curvature="0"
+ d="m 122.246,563.339 v -4.681 h 0.608 v 2.456 c 0.226,-0.479 0.591,-0.72 1.095,-0.72 0.396,0 0.722,0.184 0.976,0.55 0.254,0.367 0.381,0.84 0.381,1.418 0,0.632 -0.14,1.137 -0.421,1.515 -0.28,0.377 -0.622,0.565 -1.025,0.565 -0.484,0 -0.849,-0.242 -1.094,-0.728 l -0.026,0.64 h -0.518 c 0.015,-0.245 0.024,-0.583 0.024,-1.015 z m 0.595,-1.2 v 0.632 c 0,0.288 0.086,0.54 0.258,0.756 0.171,0.216 0.384,0.324 0.638,0.324 0.296,0 0.532,-0.132 0.706,-0.395 0.174,-0.262 0.261,-0.616 0.261,-1.062 0,-0.413 -0.086,-0.753 -0.259,-1.018 -0.173,-0.266 -0.404,-0.398 -0.694,-0.398 -0.256,0 -0.471,0.116 -0.646,0.35 -0.177,0.234 -0.264,0.504 -0.264,0.811 z"
+ id="path4400" />
+ <path
+ style="fill:#3fa9f5"
+ inkscape:connector-curvature="0"
+ d="m 125.733,560.482 h 0.653 l 0.704,2.308 c 0.06,0.192 0.13,0.439 0.211,0.74 0.083,-0.359 0.147,-0.62 0.192,-0.779 l 0.634,-2.269 h 0.64 l -0.877,2.769 c -0.279,0.88 -0.506,1.487 -0.68,1.823 -0.174,0.336 -0.362,0.585 -0.566,0.746 -0.204,0.161 -0.394,0.262 -0.571,0.302 l -0.141,-0.624 c 0.322,-0.125 0.573,-0.341 0.754,-0.647 0.18,-0.307 0.27,-0.512 0.27,-0.616 0,-0.045 -0.013,-0.106 -0.038,-0.184 l -1.185,-3.569 z"
+ id="path4402" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-large.png b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-large.png
new file mode 100644
index 00000000000..fc88a02e329
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-large.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-large.svg b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-large.svg
new file mode 100644
index 00000000000..c5c2f5d2d2c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner-large.svg
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="235"
+ height="61"
+ id="svg7375"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 74">
+ <defs
+ id="defs7377">
+ <linearGradient
+ id="SVGID_20_"
+ gradientUnits="userSpaceOnUse"
+ x1="680.95117"
+ y1="529.94818"
+ x2="680.95117"
+ y2="567.36438">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop4257" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop4259" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="189.64285"
+ inkscape:cy="-109.5"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2035"
+ inkscape:window-y="260"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata7380">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-185.35715,-341.86218)">
+ <rect
+ style="fill:#ffffff;stroke:#0088d3;stroke-miterlimit:10"
+ x="185.85715"
+ y="342.36218"
+ stroke-miterlimit="10"
+ width="234"
+ height="60"
+ id="rect4242" />
+ <g
+ transform="translate(-354.14286,-179.63782)"
+ id="g4244">
+ <path
+ inkscape:connector-curvature="0"
+ d="m 660.891,542.893 c -0.819,-0.179 -1.397,-0.274 -2.11,-0.274 -1.745,0 -3.269,0.51 -4.532,1.513 -0.055,0.036 -0.116,0.086 -0.186,0.149 -0.141,0.121 -0.276,0.249 -0.409,0.381 -0.905,0.806 -1.691,1.197 -1.691,-0.757 v -0.408 c 0,-0.248 -0.201,-0.449 -0.449,-0.449 h -1.288 c -0.122,0 -0.237,0.05 -0.322,0.138 -0.085,0.087 -0.13,0.204 -0.126,0.325 0.063,2.043 0.132,4.156 0.132,5.899 v 11.497 c 0,0.247 0.201,0.448 0.449,0.448 h 1.354 c 0.249,0 0.449,-0.201 0.449,-0.448 v -6.08 c 0,-0.84 0.065,-2.332 0.189,-2.951 0.962,-4.78 3.055,-7.104 6.397,-7.104 0.601,0 1.153,0.102 1.763,0.222 0.029,0.008 0.059,0.011 0.087,0.011 0.091,0 0.181,-0.028 0.257,-0.081 0.1,-0.069 0.167,-0.177 0.188,-0.297 l 0.197,-1.222 c 0.037,-0.237 -0.116,-0.462 -0.349,-0.512 z"
+ id="path4246" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 715.489,543.868 c -1.541,-1.087 -3.246,-1.25 -4.124,-1.25 -1.784,0 -3.464,0.559 -4.797,1.528 -0.003,0.004 -0.005,0.005 -0.009,0.006 -0.616,0.397 -1.61,0.558 -1.604,-0.384 v -0.012 c 0,-0.065 -0.002,-0.122 -0.008,-0.171 l -0.006,-0.109 c -0.011,-0.24 -0.208,-0.43 -0.448,-0.43 h -1.288 c -0.124,0 -0.243,0.052 -0.328,0.143 -0.084,0.092 -0.127,0.212 -0.119,0.337 0.094,1.397 0.13,2.558 0.13,4 v 13.38 c 0,0.247 0.201,0.448 0.449,0.448 h 1.354 c 0.248,0 0.449,-0.201 0.449,-0.448 V 550.17 c 0,-0.499 0.155,-1.08 0.3,-1.458 0.003,-0.006 0.004,-0.011 0.007,-0.017 0.639,-1.864 2.589,-3.752 5.159,-3.97 1.986,0.002 5.106,1.445 5.534,5.043 0.01,0.093 0.02,0.184 0.025,0.272 0.002,0.03 0.006,0.061 0.008,0.092 0.003,0.043 0.006,0.085 0.008,0.129 0.006,0.116 0.011,0.235 0.011,0.355 0,0.205 0.003,0.392 0.009,0.559 v 9.73 c 0,0.247 0.201,0.448 0.45,0.448 h 1.353 c 0.248,0 0.45,-0.201 0.45,-0.448 v -9.977 c 0,-3.293 -0.999,-5.669 -2.965,-7.06 z"
+ id="path4248" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 762.451,559.407 c -0.082,-0.068 -0.185,-0.106 -0.29,-0.106 -0.024,0 -0.05,0.002 -0.073,0.007 -0.359,0.059 -0.592,0.059 -0.885,0.059 -0.704,0 -1.335,-0.205 -1.335,-2.689 v -6.079 c 0,-1.437 -0.109,-3.29 -0.97,-4.887 -1.118,-2.074 -3.178,-3.125 -6.118,-3.125 -1.519,0 -3.813,0.314 -6.126,1.822 -0.193,0.126 -0.26,0.377 -0.154,0.583 l 0.527,1.023 c 0.062,0.115 0.168,0.2 0.294,0.229 0.036,0.01 0.071,0.015 0.106,0.015 0.093,0 0.183,-0.029 0.26,-0.083 1.418,-1.005 3.18,-1.536 5.093,-1.536 2.449,0 3.681,1.138 4.291,2.416 l 0,0 c 0.615,1.361 -0.345,2.699 -1.4,2.812 -0.053,0.002 -0.104,0.004 -0.156,0.006 -0.009,0 -0.018,0.003 -0.026,0 -0.033,0 -0.062,0.003 -0.087,0.007 -6.732,0.384 -10.142,2.645 -10.142,6.731 0,1.243 0.493,2.488 1.352,3.416 0.757,0.816 2.183,1.79 4.682,1.79 2.128,0 3.845,-0.682 5.104,-1.515 0.037,-0.02 0.079,-0.048 0.129,-0.088 0.015,-0.01 0.026,-0.019 0.04,-0.029 0.077,-0.054 0.157,-0.108 0.231,-0.164 0.782,-0.505 1.206,-0.216 1.608,0.31 0.001,10e-4 0.003,0.003 0.004,0.005 0.516,0.698 1.325,1.053 2.43,1.053 0.52,0 0.975,-0.059 1.437,-0.181 0.196,-0.052 0.334,-0.229 0.334,-0.434 v -1.023 c -10e-4,-0.135 -0.059,-0.26 -0.16,-0.345 z m -11.191,0.323 c -1.864,0 -3.747,-1.057 -3.747,-3.417 0,-2.981 3.64,-4.034 7.461,-4.318 1.422,-0.026 2.511,1.325 2.643,2.806 v 0.606 c -0.038,0.396 -0.15,0.789 -0.348,1.157 -0.1,0.161 -0.219,0.342 -0.361,0.529 -0.023,0.029 -0.047,0.058 -0.063,0.083 -0.885,1.134 -2.591,2.554 -5.585,2.554 z"
+ id="path4250" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 643.287,543.047 h -1.255 c -0.24,0 -0.438,0.189 -0.448,0.431 -0.041,0.993 -0.547,1.126 -1.657,0.471 -0.006,-0.005 -0.011,-0.009 -0.017,-0.012 -0.108,-0.073 -0.221,-0.143 -0.333,-0.208 -0.009,-0.005 -0.014,-0.008 -0.021,-0.013 -0.054,-0.035 -0.104,-0.064 -0.15,-0.085 -1.215,-0.674 -2.641,-1.013 -4.245,-1.013 -2.292,0 -4.554,0.949 -6.206,2.605 -1.276,1.279 -2.799,3.571 -2.799,7.226 0,2.342 0.826,4.544 2.324,6.205 1.591,1.763 3.824,2.733 6.284,2.733 1.908,0 3.382,-0.502 4.498,-1.163 0.001,-0.002 0.005,-0.002 0.007,-0.003 1.197,-0.535 2.209,-1.337 1.922,1.369 -0.535,3.481 -2.77,5.374 -6.427,5.374 -2.56,0 -4.481,-0.862 -5.644,-1.584 -0.071,-0.045 -0.153,-0.068 -0.236,-0.068 -0.037,0 -0.074,0.006 -0.111,0.016 -0.118,0.028 -0.22,0.106 -0.28,0.213 l -0.594,1.059 c -0.116,0.205 -0.054,0.467 0.146,0.595 1.749,1.143 4.236,1.824 6.654,1.824 1.649,0 5.697,-0.384 7.66,-3.932 0.85,-1.521 1.245,-3.659 1.245,-6.725 v -10.704 c 0,-1.516 0.043,-2.868 0.132,-4.131 0.009,-0.123 -0.035,-0.246 -0.12,-0.337 -0.085,-0.091 -0.204,-0.143 -0.329,-0.143 z m -10.356,15.958 c -0.37,-0.146 -4.665,-1.972 -4.3,-7.24 0.338,-4.872 3.508,-6.414 3.996,-6.622 0.784,-0.306 1.656,-0.471 2.601,-0.471 1.02,0 1.908,0.2 2.67,0.535 4.196,2.372 4.718,10.088 0.542,13.162 -0.977,0.613 -2.107,0.965 -3.278,0.965 -0.822,0 -1.564,-0.12 -2.231,-0.329 z"
+ id="path4252" />
+ <path
+ inkscape:connector-curvature="0"
+ d="m 740.07,543.047 h -1.255 c -0.24,0 -0.438,0.189 -0.448,0.431 -0.04,0.993 -0.547,1.126 -1.657,0.471 -0.005,-0.005 -0.011,-0.009 -0.016,-0.012 -0.109,-0.073 -0.223,-0.143 -0.336,-0.208 -0.006,-0.005 -0.012,-0.008 -0.019,-0.013 -0.054,-0.035 -0.104,-0.064 -0.15,-0.085 -1.216,-0.674 -2.64,-1.013 -4.245,-1.013 -2.292,0 -4.555,0.949 -6.206,2.605 -1.276,1.279 -2.799,3.571 -2.799,7.226 0,2.342 0.825,4.544 2.324,6.205 1.592,1.763 3.822,2.733 6.284,2.733 1.907,0 3.381,-0.502 4.497,-1.163 0.003,-0.002 0.006,-0.002 0.008,-0.003 1.197,-0.536 2.209,-1.339 1.922,1.369 -0.537,3.481 -2.771,5.374 -6.427,5.374 -2.56,0 -4.48,-0.862 -5.643,-1.584 -0.072,-0.045 -0.155,-0.068 -0.237,-0.068 -0.038,0 -0.076,0.006 -0.112,0.016 -0.118,0.028 -0.22,0.106 -0.278,0.213 l -0.596,1.059 c -0.116,0.205 -0.053,0.467 0.146,0.595 1.748,1.143 4.235,1.824 6.654,1.824 1.649,0 5.698,-0.384 7.66,-3.932 0.849,-1.521 1.245,-3.659 1.245,-6.725 v -10.704 c 0,-1.516 0.043,-2.868 0.131,-4.131 0.01,-0.123 -0.035,-0.246 -0.119,-0.337 -0.084,-0.09 -0.202,-0.143 -0.328,-0.143 z m -10.355,15.958 c -0.373,-0.146 -4.667,-1.972 -4.302,-7.24 0.338,-4.869 3.506,-6.411 3.996,-6.621 0.784,-0.307 1.656,-0.472 2.601,-0.472 1.021,0 1.909,0.2 2.671,0.535 4.196,2.372 4.718,10.088 0.54,13.162 -0.976,0.613 -2.105,0.965 -3.276,0.965 -0.824,0 -1.565,-0.12 -2.23,-0.329 z"
+ id="path4254" />
+ <linearGradient
+ id="linearGradient7358"
+ gradientUnits="userSpaceOnUse"
+ x1="680.95117"
+ y1="529.94818"
+ x2="680.95117"
+ y2="567.36438">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop7360" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop7362" />
+ </linearGradient>
+ <path
+ style="fill:url(#SVGID_20_)"
+ inkscape:connector-curvature="0"
+ d="m 679.508,534.324 c -1.433,1.434 -2.521,3.276 -3.086,5.308 -3.726,-0.058 -7.462,1.394 -10.427,4.359 -4.993,4.99 -6.546,14.049 0.409,21.002 5.851,5.851 14.588,5.939 20.322,0.205 2.711,-2.714 4.438,-6.442 4.475,-10.387 1.925,-0.514 3.756,-1.538 5.316,-3.097 4.264,-4.268 5.57,-11.6 -0.19,-17.362 -4.805,-4.801 -12.034,-4.813 -16.819,-0.028 z m 15.711,12.524 c -0.82,0.819 -1.868,1.424 -3.024,1.755 -1.856,0.531 -2.856,0.477 -3.494,1.542 -0.639,1.063 -0.567,1.188 -0.54,3.185 0.035,2.262 -0.817,4.606 -2.95,6.742 -3.263,3.26 -9.283,3.987 -13.713,-0.442 -3.492,-3.492 -5.13,-9.367 -0.479,-14.02 1.728,-1.726 4.211,-2.746 6.822,-2.699 1.814,0.033 2.026,0.099 3.195,-0.432 1.171,-0.532 0.663,-1.717 1.362,-3.804 0.341,-1.019 0.947,-2.021 1.896,-2.971 2.53,-2.529 7.187,-3.11 10.598,0.302 2.78,2.779 3.958,7.209 0.327,10.842 z"
+ id="path4261" />
+ </g>
+ <g
+ transform="translate(-354.14286,-179.63782)"
+ id="g4263">
+ <path
+ style="fill:#b3b3b3"
+ inkscape:connector-curvature="0"
+ d="m 550.712,558.316 v -7.322 c 0,-0.91 -0.015,-1.652 -0.044,-2.227 h 0.929 l 0.056,1.177 h 0.023 c 0.451,-0.887 1.123,-1.33 2.016,-1.33 0.68,0 1.245,0.32 1.697,0.962 0.451,0.642 0.677,1.465 0.677,2.468 0,1.115 -0.243,2.003 -0.73,2.663 -0.487,0.661 -1.095,0.991 -1.823,0.991 -0.369,0 -0.708,-0.094 -1.016,-0.28 -0.309,-0.187 -0.549,-0.452 -0.721,-0.798 h -0.021 v 3.696 h -1.043 z m 1.042,-6.664 v 1.092 c 0,0.546 0.152,1.004 0.456,1.375 0.304,0.371 0.671,0.557 1.101,0.557 0.522,0 0.938,-0.231 1.243,-0.696 0.306,-0.464 0.459,-1.091 0.459,-1.88 0,-0.709 -0.151,-1.295 -0.454,-1.757 -0.302,-0.462 -0.703,-0.692 -1.203,-0.692 -0.321,0 -0.616,0.115 -0.885,0.346 -0.27,0.231 -0.455,0.514 -0.561,0.848 -0.104,0.332 -0.156,0.601 -0.156,0.807 z"
+ id="path4265" />
+ <path
+ style="fill:#b3b3b3"
+ inkscape:connector-curvature="0"
+ d="m 563.121,552.086 c 0,0.728 -0.121,1.365 -0.363,1.911 -0.243,0.546 -0.582,0.966 -1.02,1.26 -0.438,0.294 -0.909,0.441 -1.417,0.441 -0.792,0 -1.444,-0.323 -1.96,-0.97 -0.516,-0.647 -0.772,-1.485 -0.772,-2.517 0,-1.097 0.266,-1.971 0.798,-2.621 0.531,-0.651 1.203,-0.977 2.014,-0.977 0.806,0 1.461,0.323 1.965,0.969 0.503,0.647 0.755,1.481 0.755,2.504 z m -4.469,0.084 c 0,0.742 0.159,1.354 0.477,1.838 0.316,0.482 0.715,0.725 1.192,0.725 0.489,0 0.897,-0.243 1.224,-0.729 0.327,-0.486 0.49,-1.11 0.49,-1.876 0,-0.704 -0.15,-1.306 -0.451,-1.803 -0.301,-0.497 -0.71,-0.745 -1.229,-0.745 -0.527,0 -0.942,0.246 -1.246,0.738 -0.304,0.493 -0.457,1.11 -0.457,1.852 z"
+ id="path4267" />
+ <path
+ style="fill:#b3b3b3"
+ inkscape:connector-curvature="0"
+ d="m 564.421,548.768 h 1.063 c 0.71,3.286 1.09,5.144 1.143,5.572 h 0.022 c 0.042,-0.364 0.505,-2.222 1.39,-5.572 h 0.885 c 0.803,3.141 1.25,4.998 1.344,5.572 h 0.021 c 0.105,-0.7 0.236,-1.386 0.393,-2.058 l 0.817,-3.515 h 1.03 l -1.791,6.776 h -0.964 c -0.616,-2.37 -0.964,-3.718 -1.042,-4.043 -0.078,-0.324 -0.168,-0.782 -0.269,-1.375 h -0.022 c -0.123,0.705 -0.285,1.424 -0.486,2.156 l -0.902,3.262 h -0.963 l -1.669,-6.775 z"
+ id="path4269" />
+ <path
+ style="fill:#b3b3b3"
+ inkscape:connector-curvature="0"
+ d="m 578.734,552.422 h -3.932 c 0.015,0.732 0.195,1.294 0.543,1.684 0.348,0.39 0.797,0.585 1.35,0.585 0.602,0 1.127,-0.117 1.58,-0.351 l 0.179,0.91 c -0.534,0.299 -1.165,0.448 -1.894,0.448 -0.844,0 -1.516,-0.313 -2.016,-0.938 -0.5,-0.625 -0.75,-1.456 -0.75,-2.492 0,-1.035 0.24,-1.903 0.719,-2.604 0.48,-0.7 1.125,-1.05 1.936,-1.05 0.746,0 1.32,0.312 1.719,0.935 0.4,0.623 0.6,1.385 0.6,2.285 0,0.257 -0.012,0.453 -0.034,0.588 z m -3.92,-0.938 h 2.945 c 0,-0.584 -0.121,-1.054 -0.361,-1.411 -0.24,-0.356 -0.584,-0.535 -1.027,-0.535 -0.422,0 -0.771,0.179 -1.047,0.535 -0.277,0.358 -0.447,0.827 -0.51,1.411 z"
+ id="path4271" />
+ <path
+ style="fill:#b3b3b3"
+ inkscape:connector-curvature="0"
+ d="m 580.693,555.544 v -4.62 c 0,-0.877 -0.014,-1.596 -0.044,-2.156 h 0.918 l 0.067,1.358 c 0.139,-0.476 0.35,-0.847 0.633,-1.113 0.283,-0.266 0.592,-0.398 0.924,-0.398 0.071,0 0.164,0.009 0.28,0.027 v 1.232 c -0.101,-0.028 -0.22,-0.042 -0.358,-0.042 -0.377,0 -0.701,0.185 -0.972,0.553 -0.271,0.369 -0.405,0.894 -0.405,1.575 v 3.584 h -1.043 z"
+ id="path4273" />
+ <path
+ style="fill:#b3b3b3"
+ inkscape:connector-curvature="0"
+ d="m 589.699,552.422 h -3.932 c 0.015,0.732 0.195,1.294 0.543,1.684 0.348,0.39 0.797,0.585 1.35,0.585 0.602,0 1.127,-0.117 1.58,-0.351 l 0.179,0.91 c -0.534,0.299 -1.165,0.448 -1.894,0.448 -0.844,0 -1.516,-0.313 -2.016,-0.938 -0.5,-0.625 -0.75,-1.456 -0.75,-2.492 0,-1.035 0.24,-1.903 0.719,-2.604 0.48,-0.7 1.125,-1.05 1.936,-1.05 0.746,0 1.32,0.312 1.719,0.935 0.4,0.623 0.6,1.385 0.6,2.285 -10e-4,0.257 -0.012,0.453 -0.034,0.588 z m -3.92,-0.938 h 2.945 c 0,-0.584 -0.121,-1.054 -0.361,-1.411 -0.24,-0.356 -0.584,-0.535 -1.027,-0.535 -0.422,0 -0.771,0.179 -1.047,0.535 -0.277,0.358 -0.447,0.827 -0.51,1.411 z"
+ id="path4275" />
+ <path
+ style="fill:#b3b3b3"
+ inkscape:connector-curvature="0"
+ d="m 595.49,545.576 h 1.041 v 8.189 c 0,0.756 0.016,1.349 0.045,1.778 h -0.93 l -0.078,-1.204 c -0.18,0.43 -0.438,0.763 -0.773,1.001 -0.335,0.238 -0.717,0.357 -1.143,0.357 -0.687,0 -1.26,-0.323 -1.719,-0.97 -0.459,-0.647 -0.688,-1.467 -0.688,-2.461 0,-1.077 0.241,-1.956 0.726,-2.635 0.482,-0.68 1.081,-1.019 1.795,-1.019 0.38,0 0.721,0.093 1.021,0.279 0.301,0.188 0.535,0.439 0.703,0.757 v -4.072 z m 0,7.056 v -1.078 c 0,-0.574 -0.148,-1.037 -0.445,-1.39 -0.297,-0.352 -0.652,-0.528 -1.067,-0.528 -0.504,0 -0.909,0.236 -1.218,0.707 -0.309,0.472 -0.462,1.085 -0.462,1.841 0,0.71 0.146,1.298 0.437,1.765 0.291,0.467 0.698,0.699 1.221,0.699 0.4,0 0.756,-0.183 1.066,-0.549 0.312,-0.367 0.468,-0.856 0.468,-1.467 z"
+ id="path4277" />
+ <path
+ style="fill:#b3b3b3"
+ inkscape:connector-curvature="0"
+ d="m 601.996,553.766 v -8.189 h 1.064 v 4.298 c 0.396,-0.84 1.033,-1.26 1.915,-1.26 0.694,0 1.264,0.32 1.708,0.962 0.444,0.642 0.666,1.469 0.666,2.481 0,1.106 -0.245,1.989 -0.736,2.649 -0.49,0.661 -1.089,0.991 -1.795,0.991 -0.848,0 -1.486,-0.425 -1.915,-1.274 l -0.044,1.12 h -0.908 c 0.03,-0.43 0.045,-1.023 0.045,-1.778 z m 1.042,-2.1 v 1.105 c 0,0.505 0.149,0.945 0.45,1.323 0.301,0.378 0.674,0.567 1.117,0.567 0.52,0 0.931,-0.229 1.235,-0.689 0.304,-0.46 0.456,-1.079 0.456,-1.858 0,-0.724 -0.15,-1.317 -0.453,-1.782 -0.303,-0.464 -0.708,-0.696 -1.215,-0.696 -0.449,0 -0.826,0.204 -1.132,0.612 -0.305,0.409 -0.458,0.881 -0.458,1.418 z"
+ id="path4279" />
+ <path
+ style="fill:#b3b3b3"
+ inkscape:connector-curvature="0"
+ d="m 608.548,548.768 h 1.143 l 1.231,4.039 c 0.105,0.336 0.229,0.768 0.37,1.295 0.146,-0.63 0.258,-1.085 0.336,-1.364 l 1.108,-3.97 h 1.121 l -1.535,4.845 c -0.488,1.54 -0.886,2.604 -1.189,3.191 -0.305,0.588 -0.635,1.023 -0.992,1.306 -0.355,0.282 -0.689,0.459 -0.999,0.528 l -0.246,-1.092 c 0.563,-0.22 1.003,-0.598 1.318,-1.134 0.315,-0.537 0.474,-0.896 0.474,-1.078 0,-0.079 -0.022,-0.187 -0.067,-0.322 l -2.073,-6.244 z"
+ id="path4281" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner.png b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner.png
new file mode 100644
index 00000000000..d430e64ea88
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner.svg b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner.svg
new file mode 100644
index 00000000000..672e4cf2735
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/groonga-powered-by-banner.svg
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="89"
+ height="32"
+ id="svg5897"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 59">
+ <defs
+ id="defs5899">
+ <linearGradient
+ id="SVGID_19_"
+ gradientUnits="userSpaceOnUse"
+ x1="81.903801"
+ y1="521.35449"
+ x2="81.903801"
+ y2="544.04199">
+ <stop
+ offset="0"
+ style="stop-color:#3FA9F5"
+ id="stop4174" />
+ <stop
+ offset="1"
+ style="stop-color:#0071BC"
+ id="stop4176" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_19_"
+ id="linearGradient6749"
+ gradientUnits="userSpaceOnUse"
+ x1="81.903801"
+ y1="521.35449"
+ x2="81.903801"
+ y2="544.04199" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_19_"
+ id="linearGradient6795"
+ gradientUnits="userSpaceOnUse"
+ x1="81.903801"
+ y1="521.35449"
+ x2="81.903801"
+ y2="544.04199" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="46.971944"
+ inkscape:cy="73.799943"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="1928"
+ inkscape:window-y="52"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata5902">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-309.78571,-682.07648)">
+ <g
+ id="g6772">
+ <rect
+ id="rect4115"
+ height="31"
+ width="88"
+ stroke-miterlimit="10"
+ y="682.57648"
+ x="310.28571"
+ style="fill:#ffffff;stroke:#0088d3;stroke-miterlimit:10" />
+ <g
+ id="g4161"
+ transform="translate(264.28571,167.57647)">
+ <path
+ id="path4163"
+ d="m 70.143,528.477 c -0.48,-0.104 -0.82,-0.159 -1.238,-0.159 -1.022,0 -1.916,0.297 -2.656,0.886 -0.033,0.021 -0.069,0.051 -0.108,0.089 -0.083,0.071 -0.163,0.145 -0.241,0.223 -0.531,0.472 -0.992,0.7 -0.992,-0.444 v -0.239 c 0,-0.146 -0.118,-0.264 -0.263,-0.264 h -0.756 c -0.071,0 -0.139,0.03 -0.188,0.08 -0.05,0.051 -0.076,0.12 -0.074,0.192 0.039,1.197 0.078,2.436 0.078,3.458 v 6.74 c 0,0.146 0.118,0.264 0.263,0.264 h 0.795 c 0.145,0 0.263,-0.118 0.263,-0.264 v -3.563 c 0,-0.493 0.039,-1.368 0.111,-1.73 0.564,-2.804 1.792,-4.166 3.751,-4.166 0.352,0 0.676,0.06 1.033,0.131 0.017,0.003 0.034,0.005 0.051,0.005 0.053,0 0.105,-0.017 0.15,-0.047 0.059,-0.041 0.098,-0.104 0.11,-0.174 l 0.116,-0.717 c 0.021,-0.138 -0.068,-0.272 -0.205,-0.301 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path4165"
+ d="m 102.151,529.049 c -0.903,-0.637 -1.902,-0.731 -2.417,-0.731 -1.046,0 -2.03,0.327 -2.812,0.896 -0.002,0 -0.003,10e-4 -0.005,0.002 -0.361,0.232 -0.945,0.326 -0.941,-0.224 v -0.009 c 0,-0.038 -0.001,-0.07 -0.005,-0.1 l -0.003,-0.064 c -0.006,-0.142 -0.122,-0.252 -0.262,-0.252 H 94.95 c -0.073,0 -0.142,0.031 -0.192,0.084 -0.05,0.054 -0.075,0.124 -0.071,0.196 0.054,0.82 0.078,1.5 0.078,2.346 v 7.845 c 0,0.146 0.117,0.264 0.263,0.264 h 0.794 c 0.146,0 0.263,-0.118 0.263,-0.264 v -6.295 c 0,-0.293 0.091,-0.634 0.176,-0.855 0.002,-0.003 0.002,-0.005 0.003,-0.008 0.375,-1.094 1.519,-2.2 3.025,-2.327 1.164,10e-4 2.993,0.847 3.244,2.955 0.006,0.055 0.011,0.107 0.016,0.161 10e-4,0.018 0.003,0.035 0.004,0.054 0.002,0.024 0.003,0.05 0.005,0.074 0.003,0.068 0.005,0.137 0.005,0.21 0,0.12 10e-4,0.228 0.006,0.327 v 5.704 c 0,0.146 0.118,0.264 0.263,0.264 h 0.794 c 0.145,0 0.263,-0.118 0.263,-0.264 v -5.851 c 0.001,-1.93 -0.584,-3.322 -1.738,-4.138 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path4167"
+ d="m 129.567,538.159 c -0.047,-0.04 -0.108,-0.063 -0.169,-0.063 -0.015,0 -0.029,10e-4 -0.043,0.005 -0.211,0.035 -0.347,0.035 -0.519,0.035 -0.413,0 -0.783,-0.121 -0.783,-1.577 v -3.563 c 0,-0.843 -0.063,-1.93 -0.568,-2.866 -0.656,-1.215 -1.862,-1.831 -3.586,-1.831 -0.891,0 -2.236,0.184 -3.592,1.068 -0.113,0.073 -0.152,0.222 -0.09,0.341 l 0.311,0.6 c 0.034,0.069 0.098,0.119 0.171,0.137 0.021,0.004 0.042,0.007 0.062,0.007 0.055,0 0.108,-0.018 0.152,-0.049 0.832,-0.589 1.865,-0.9 2.986,-0.9 1.436,0 2.158,0.667 2.516,1.416 l 0,0 c 0.36,0.798 -0.203,1.583 -0.822,1.647 -0.031,0.004 -0.061,0.005 -0.091,0.006 -0.005,0 -0.011,10e-4 -0.016,0 -0.019,0 -0.036,0.002 -0.051,0.003 -3.946,0.225 -5.944,1.55 -5.944,3.947 0,0.728 0.288,1.457 0.792,2.001 0.443,0.479 1.28,1.051 2.744,1.051 1.248,0 2.254,-0.4 2.993,-0.889 0.022,-0.012 0.047,-0.027 0.076,-0.051 0.009,-0.006 0.016,-0.013 0.023,-0.019 0.046,-0.031 0.092,-0.063 0.136,-0.096 0.458,-0.296 0.708,-0.126 0.943,0.182 0.001,0 0.002,10e-4 0.003,0.002 0.302,0.411 0.776,0.619 1.424,0.619 0.304,0 0.572,-0.035 0.842,-0.106 0.116,-0.031 0.195,-0.136 0.195,-0.254 v -0.603 c -0.001,-0.078 -0.036,-0.151 -0.095,-0.2 z m -6.56,0.19 c -1.094,0 -2.197,-0.619 -2.197,-2.002 0,-1.749 2.134,-2.366 4.373,-2.534 0.835,-0.015 1.474,0.778 1.551,1.646 v 0.355 c -0.023,0.232 -0.089,0.463 -0.204,0.678 -0.058,0.096 -0.128,0.201 -0.212,0.312 -0.014,0.017 -0.027,0.033 -0.037,0.049 -0.519,0.663 -1.519,1.496 -3.274,1.496 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path4169"
+ d="m 59.823,528.567 h -0.736 c -0.142,0 -0.257,0.111 -0.263,0.253 -0.023,0.582 -0.32,0.66 -0.972,0.275 -0.003,-0.003 -0.006,-0.006 -0.01,-0.008 -0.064,-0.042 -0.129,-0.082 -0.195,-0.121 -0.003,-0.002 -0.008,-0.005 -0.011,-0.008 -0.032,-0.021 -0.061,-0.036 -0.089,-0.05 -0.712,-0.395 -1.547,-0.592 -2.488,-0.592 -1.344,0 -2.67,0.556 -3.638,1.527 -0.75,0.75 -1.642,2.093 -1.642,4.235 0,1.373 0.484,2.664 1.363,3.638 0.932,1.032 2.24,1.604 3.684,1.604 1.118,0 1.982,-0.296 2.637,-0.684 10e-4,0 0.003,-10e-4 0.004,-10e-4 0.702,-0.313 1.294,-0.784 1.126,0.802 -0.313,2.042 -1.624,3.15 -3.768,3.15 -1.5,0 -2.628,-0.504 -3.309,-0.928 -0.042,-0.026 -0.09,-0.041 -0.139,-0.041 -0.021,0 -0.043,0.005 -0.065,0.01 -0.07,0.018 -0.128,0.063 -0.164,0.126 l -0.349,0.619 c -0.068,0.121 -0.031,0.274 0.084,0.35 1.026,0.67 2.484,1.069 3.902,1.069 0.968,0 3.34,-0.225 4.492,-2.305 0.497,-0.892 0.729,-2.146 0.729,-3.942 v -6.275 c 0,-0.889 0.025,-1.683 0.077,-2.423 0.005,-0.072 -0.02,-0.144 -0.07,-0.196 -0.047,-0.052 -0.117,-0.084 -0.19,-0.084 z m -6.072,9.356 c -0.218,-0.087 -2.735,-1.155 -2.521,-4.245 0.199,-2.855 2.058,-3.758 2.344,-3.882 0.458,-0.179 0.97,-0.276 1.524,-0.276 0.599,0 1.119,0.118 1.565,0.315 2.46,1.392 2.767,5.913 0.319,7.716 -0.573,0.358 -1.235,0.565 -1.922,0.565 -0.483,0 -0.919,-0.068 -1.309,-0.193 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path4171"
+ d="m 116.563,528.567 h -0.736 c -0.141,0 -0.257,0.111 -0.263,0.253 -0.024,0.582 -0.321,0.66 -0.971,0.275 -0.004,-0.003 -0.007,-0.006 -0.011,-0.008 -0.063,-0.042 -0.13,-0.082 -0.196,-0.121 -0.003,-0.002 -0.007,-0.005 -0.011,-0.008 -0.031,-0.021 -0.06,-0.036 -0.087,-0.05 -0.713,-0.395 -1.548,-0.592 -2.489,-0.592 -1.344,0 -2.67,0.556 -3.638,1.527 -0.749,0.75 -1.642,2.093 -1.642,4.235 0,1.373 0.484,2.664 1.363,3.638 0.933,1.032 2.241,1.604 3.684,1.604 1.118,0 1.982,-0.296 2.637,-0.684 10e-4,0 0.002,-10e-4 0.003,-10e-4 0.702,-0.314 1.295,-0.785 1.127,0.802 -0.315,2.042 -1.625,3.15 -3.768,3.15 -1.5,0 -2.627,-0.504 -3.308,-0.928 -0.043,-0.026 -0.09,-0.041 -0.139,-0.041 -0.022,0 -0.044,0.005 -0.065,0.01 -0.07,0.018 -0.129,0.063 -0.164,0.126 l -0.349,0.619 c -0.069,0.121 -0.031,0.274 0.084,0.35 1.026,0.67 2.484,1.069 3.902,1.069 0.966,0 3.34,-0.225 4.491,-2.305 0.497,-0.892 0.73,-2.146 0.73,-3.942 v -6.275 c 0,-0.889 0.025,-1.683 0.077,-2.423 0.005,-0.072 -0.021,-0.144 -0.07,-0.196 -0.048,-0.052 -0.116,-0.084 -0.191,-0.084 z m -6.072,9.356 c -0.218,-0.087 -2.735,-1.155 -2.521,-4.245 0.198,-2.854 2.054,-3.757 2.342,-3.882 0.459,-0.179 0.971,-0.276 1.525,-0.276 0.599,0 1.119,0.118 1.566,0.315 2.459,1.392 2.766,5.913 0.317,7.716 -0.572,0.359 -1.235,0.565 -1.921,0.565 -0.482,0 -0.917,-0.068 -1.308,-0.193 z"
+ inkscape:connector-curvature="0" />
+ <linearGradient
+ y2="544.04199"
+ x2="81.903801"
+ y1="521.35449"
+ x1="81.903801"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5880">
+ <stop
+ id="stop5882"
+ style="stop-color:#3FA9F5"
+ offset="0" />
+ <stop
+ id="stop5884"
+ style="stop-color:#0071BC"
+ offset="1" />
+ </linearGradient>
+ <path
+ id="path4178"
+ d="m 81.057,523.455 c -0.84,0.839 -1.477,1.92 -1.809,3.11 -2.184,-0.034 -4.375,0.816 -6.113,2.557 -2.927,2.926 -3.837,8.236 0.24,12.311 3.43,3.433 8.553,3.483 11.915,0.121 1.589,-1.59 2.602,-3.776 2.623,-6.09 1.128,-0.3 2.202,-0.901 3.117,-1.814 2.501,-2.501 3.266,-6.8 -0.112,-10.181 -2.816,-2.814 -7.056,-2.82 -9.861,-0.014 z m 9.211,7.341 c -0.48,0.479 -1.095,0.834 -1.773,1.029 -1.088,0.312 -1.674,0.279 -2.048,0.903 -0.374,0.624 -0.333,0.698 -0.316,1.868 0.02,1.325 -0.479,2.699 -1.73,3.952 -1.913,1.911 -5.442,2.338 -8.04,-0.26 -2.045,-2.048 -3.006,-5.492 -0.28,-8.22 1.013,-1.011 2.469,-1.609 4,-1.581 1.064,0.018 1.188,0.057 1.873,-0.253 0.686,-0.313 0.388,-1.007 0.798,-2.23 0.2,-0.598 0.556,-1.185 1.112,-1.742 1.483,-1.483 4.213,-1.823 6.213,0.177 1.629,1.629 2.319,4.227 0.191,6.357 z"
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient6795)" />
+ </g>
+ <g
+ id="g4180"
+ transform="translate(264.28571,167.57647)">
+ <path
+ id="path4182"
+ d="m 94.988,525.938 v -4.184 c 0,-0.521 -0.008,-0.944 -0.025,-1.272 h 0.531 l 0.032,0.672 h 0.013 c 0.258,-0.507 0.642,-0.76 1.152,-0.76 0.388,0 0.711,0.184 0.969,0.55 0.258,0.367 0.387,0.837 0.387,1.41 0,0.638 -0.139,1.145 -0.417,1.522 -0.278,0.377 -0.625,0.565 -1.042,0.565 -0.211,0 -0.405,-0.054 -0.581,-0.16 -0.176,-0.106 -0.313,-0.259 -0.411,-0.456 h -0.013 v 2.112 h -0.595 z m 0.595,-3.807 v 0.624 c 0,0.312 0.087,0.573 0.261,0.785 0.174,0.213 0.383,0.318 0.629,0.318 0.299,0 0.536,-0.133 0.71,-0.397 0.175,-0.266 0.262,-0.624 0.262,-1.074 0,-0.405 -0.086,-0.74 -0.259,-1.004 -0.173,-0.265 -0.402,-0.396 -0.688,-0.396 -0.183,0 -0.352,0.066 -0.505,0.198 -0.154,0.132 -0.26,0.293 -0.32,0.484 -0.06,0.19 -0.09,0.344 -0.09,0.462 z"
+ inkscape:connector-curvature="0"
+ style="fill:#b3b3b3" />
+ <path
+ id="path4184"
+ d="m 101.823,522.379 c 0,0.416 -0.069,0.779 -0.208,1.092 -0.139,0.312 -0.333,0.552 -0.583,0.72 -0.25,0.168 -0.52,0.252 -0.81,0.252 -0.452,0 -0.826,-0.185 -1.12,-0.554 -0.294,-0.369 -0.441,-0.849 -0.441,-1.438 0,-0.626 0.152,-1.126 0.456,-1.498 0.304,-0.372 0.688,-0.558 1.15,-0.558 0.461,0 0.835,0.185 1.123,0.554 0.288,0.369 0.433,0.846 0.433,1.43 z m -2.553,0.048 c 0,0.424 0.091,0.773 0.272,1.05 0.182,0.276 0.409,0.414 0.682,0.414 0.28,0 0.513,-0.139 0.699,-0.416 0.186,-0.277 0.28,-0.635 0.28,-1.072 0,-0.402 -0.086,-0.746 -0.258,-1.03 -0.172,-0.283 -0.406,-0.426 -0.702,-0.426 -0.301,0 -0.539,0.141 -0.712,0.422 -0.173,0.281 -0.261,0.634 -0.261,1.058 z"
+ inkscape:connector-curvature="0"
+ style="fill:#b3b3b3" />
+ <path
+ id="path4186"
+ d="m 102.31,520.482 h 0.608 c 0.405,1.878 0.623,2.938 0.653,3.184 h 0.013 c 0.023,-0.208 0.288,-1.269 0.794,-3.184 h 0.505 c 0.458,1.795 0.715,2.856 0.768,3.184 h 0.013 c 0.06,-0.399 0.134,-0.792 0.224,-1.176 l 0.467,-2.008 h 0.589 l -1.024,3.872 h -0.55 c -0.352,-1.354 -0.55,-2.125 -0.595,-2.31 -0.045,-0.186 -0.096,-0.447 -0.153,-0.786 h -0.013 c -0.07,0.402 -0.163,0.813 -0.278,1.231 l -0.515,1.864 h -0.55 l -0.956,-3.871 z"
+ inkscape:connector-curvature="0"
+ style="fill:#b3b3b3" />
+ <path
+ id="path4188"
+ d="m 110.232,522.57 h -2.246 c 0.008,0.419 0.112,0.739 0.31,0.962 0.199,0.223 0.456,0.334 0.771,0.334 0.343,0 0.644,-0.066 0.902,-0.2 l 0.103,0.521 c -0.305,0.171 -0.666,0.256 -1.082,0.256 -0.482,0 -0.866,-0.179 -1.152,-0.536 -0.286,-0.357 -0.429,-0.832 -0.429,-1.424 0,-0.592 0.137,-1.088 0.411,-1.488 0.274,-0.399 0.643,-0.6 1.105,-0.6 0.427,0 0.754,0.178 0.982,0.534 0.229,0.355 0.343,0.791 0.343,1.306 0.002,0.146 -0.004,0.258 -0.018,0.335 z m -2.239,-0.536 h 1.683 c 0,-0.333 -0.069,-0.602 -0.207,-0.806 -0.137,-0.204 -0.333,-0.306 -0.587,-0.306 -0.241,0 -0.441,0.102 -0.599,0.306 -0.158,0.204 -0.254,0.473 -0.29,0.806 z"
+ inkscape:connector-curvature="0"
+ style="fill:#b3b3b3" />
+ <path
+ id="path4190"
+ d="m 111.097,524.354 v -2.64 c 0,-0.502 -0.008,-0.912 -0.025,-1.232 h 0.524 l 0.039,0.776 c 0.079,-0.272 0.2,-0.484 0.362,-0.637 0.162,-0.151 0.338,-0.228 0.528,-0.228 0.041,0 0.094,0.005 0.16,0.016 v 0.704 c -0.058,-0.016 -0.126,-0.023 -0.205,-0.023 -0.215,0 -0.4,0.105 -0.555,0.315 -0.155,0.211 -0.232,0.511 -0.232,0.9 v 2.048 h -0.596 z"
+ inkscape:connector-curvature="0"
+ style="fill:#b3b3b3" />
+ <path
+ id="path4192"
+ d="m 115.986,522.57 h -2.246 c 0.008,0.419 0.112,0.739 0.31,0.962 0.199,0.223 0.456,0.334 0.771,0.334 0.343,0 0.644,-0.066 0.902,-0.2 l 0.103,0.521 c -0.305,0.171 -0.666,0.256 -1.082,0.256 -0.482,0 -0.866,-0.179 -1.152,-0.536 -0.286,-0.357 -0.429,-0.832 -0.429,-1.424 0,-0.592 0.137,-1.088 0.411,-1.488 0.274,-0.399 0.643,-0.6 1.105,-0.6 0.427,0 0.754,0.178 0.982,0.534 0.229,0.355 0.343,0.791 0.343,1.306 0.002,0.146 -0.004,0.258 -0.018,0.335 z m -2.239,-0.536 h 1.683 c 0,-0.333 -0.069,-0.602 -0.207,-0.806 -0.137,-0.204 -0.333,-0.306 -0.587,-0.306 -0.241,0 -0.441,0.102 -0.599,0.306 -0.158,0.204 -0.254,0.473 -0.29,0.806 z"
+ inkscape:connector-curvature="0"
+ style="fill:#b3b3b3" />
+ <path
+ id="path4194"
+ d="m 119.04,518.658 h 0.595 v 4.681 c 0,0.432 0.008,0.771 0.025,1.016 h -0.531 l -0.045,-0.688 c -0.102,0.246 -0.25,0.437 -0.441,0.572 -0.191,0.135 -0.41,0.204 -0.653,0.204 -0.393,0 -0.72,-0.185 -0.982,-0.554 -0.262,-0.369 -0.394,-0.838 -0.394,-1.406 0,-0.616 0.138,-1.118 0.415,-1.506 0.277,-0.388 0.618,-0.582 1.025,-0.582 0.218,0 0.412,0.054 0.584,0.16 0.172,0.106 0.306,0.251 0.402,0.432 v -2.329 z m 0,4.032 v -0.616 c 0,-0.328 -0.085,-0.593 -0.254,-0.794 -0.17,-0.201 -0.373,-0.302 -0.61,-0.302 -0.288,0 -0.52,0.135 -0.696,0.404 -0.176,0.27 -0.264,0.62 -0.264,1.052 0,0.405 0.083,0.741 0.25,1.008 0.167,0.267 0.399,0.4 0.698,0.4 0.228,0 0.432,-0.104 0.609,-0.314 0.177,-0.209 0.267,-0.488 0.267,-0.838 z"
+ inkscape:connector-curvature="0"
+ style="fill:#b3b3b3" />
+ <path
+ id="path4196"
+ d="m 122.246,523.339 v -4.681 h 0.608 v 2.456 c 0.226,-0.479 0.591,-0.72 1.095,-0.72 0.396,0 0.722,0.184 0.976,0.55 0.254,0.367 0.381,0.84 0.381,1.418 0,0.632 -0.14,1.137 -0.421,1.515 -0.28,0.377 -0.622,0.565 -1.025,0.565 -0.484,0 -0.849,-0.242 -1.094,-0.728 l -0.026,0.64 h -0.518 c 0.015,-0.245 0.024,-0.583 0.024,-1.015 z m 0.595,-1.2 v 0.632 c 0,0.288 0.086,0.54 0.258,0.756 0.171,0.216 0.384,0.324 0.638,0.324 0.296,0 0.532,-0.132 0.706,-0.395 0.174,-0.262 0.261,-0.616 0.261,-1.062 0,-0.413 -0.086,-0.753 -0.259,-1.018 -0.173,-0.266 -0.404,-0.398 -0.694,-0.398 -0.256,0 -0.471,0.116 -0.646,0.35 -0.177,0.234 -0.264,0.504 -0.264,0.811 z"
+ inkscape:connector-curvature="0"
+ style="fill:#b3b3b3" />
+ <path
+ id="path4198"
+ d="m 125.733,520.482 h 0.653 l 0.704,2.308 c 0.06,0.192 0.13,0.439 0.211,0.74 0.083,-0.359 0.147,-0.62 0.192,-0.779 l 0.634,-2.269 h 0.64 l -0.877,2.769 c -0.279,0.88 -0.506,1.487 -0.68,1.823 -0.174,0.336 -0.362,0.585 -0.566,0.746 -0.204,0.161 -0.394,0.262 -0.571,0.302 l -0.141,-0.624 c 0.322,-0.125 0.573,-0.341 0.754,-0.647 0.18,-0.307 0.27,-0.512 0.27,-0.616 0,-0.045 -0.013,-0.106 -0.038,-0.184 l -1.185,-3.569 z"
+ inkscape:connector-curvature="0"
+ style="fill:#b3b3b3" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-foreground-white.png b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-foreground-white.png
new file mode 100644
index 00000000000..67c29d5b79b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-foreground-white.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-foreground-white.svg b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-foreground-white.svg
new file mode 100644
index 00000000000..0f1d0d74141
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-foreground-white.svg
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="60.000706"
+ height="60.001019"
+ id="svg5656"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 50">
+ <defs
+ id="defs5658">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_10_"
+ id="linearGradient5523"
+ gradientUnits="userSpaceOnUse"
+ x1="550.15039"
+ y1="437.05859"
+ x2="550.15039"
+ y2="497.05859" />
+ <linearGradient
+ id="SVGID_10_"
+ gradientUnits="userSpaceOnUse"
+ x1="550.15039"
+ y1="437.05859"
+ x2="550.15039"
+ y2="497.05859">
+ <stop
+ offset="0"
+ style="stop-color:#F8BB5E"
+ id="stop4057" />
+ <stop
+ offset="1"
+ style="stop-color:#FF931E"
+ id="stop4059" />
+ </linearGradient>
+ <linearGradient
+ y2="497.05859"
+ x2="550.15039"
+ y1="437.05859"
+ x1="550.15039"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5646"
+ xlink:href="#SVGID_10_"
+ inkscape:collect="always" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="5.5307199"
+ inkscape:cy="22.804138"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="1932"
+ inkscape:window-y="78"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata5661">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-569.99965,-562.36167)">
+ <polygon
+ transform="translate(49.849648,125.30369)"
+ points="520.15,437.059 520.15,497.059 546.012,497.059 580.15,497.059 580.15,462.92 580.15,437.059 "
+ id="polygon4061"
+ style="fill:url(#linearGradient5646)" />
+ <path
+ d="m 623.53365,599.24469 c 7.502,-7.505 9.8,-20.401 -0.335,-30.538 -4.224,-4.222 -9.514,-6.337 -14.8,-6.345 h -0.005 c -5.286,-0.007 -10.567,2.093 -14.774,6.299 -2.522,2.52 -4.434,5.76 -5.428,9.33 -6.49,-0.099 -13.001,2.411 -18.191,7.53 v 36.479 c 0.114,0.121 0.227,0.242 0.344,0.361 h 25.518 11.027 c 4.434,-4.717 7.233,-11.018 7.295,-17.672 3.385,-0.898 6.606,-2.7 9.349,-5.444 z m -13.748,-2.759 c -1.121,1.873 -0.997,2.089 -0.949,5.602 0.062,3.979 -1.437,8.102 -5.189,11.854 -5.736,5.737 -16.326,7.017 -24.119,-0.776 -6.139,-6.14 -9.021,-16.475 -0.84,-24.658 3.038,-3.034 7.406,-4.827 11.998,-4.745 3.193,0.055 3.564,0.174 5.621,-0.76 2.058,-0.938 1.166,-3.019 2.396,-6.692 0.6,-1.792 1.666,-3.551 3.332,-5.224 4.451,-4.448 12.643,-5.469 18.643,0.531 4.887,4.887 6.96,12.677 0.572,19.065 -1.441,1.444 -3.284,2.509 -5.319,3.087 -3.266,0.937 -5.023,0.838 -6.146,2.716 z"
+ id="path4109"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-full-size.png b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-full-size.png
new file mode 100644
index 00000000000..d938d6e8fa8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-full-size.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-full-size.svg b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-full-size.svg
new file mode 100644
index 00000000000..feae36b6604
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon-full-size.svg
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="199.80432"
+ height="200.86072"
+ id="svg4429"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="mroonga-icon-full-size.svg"
+ inkscape:export-filename="mroonga-icon-full-size.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs4431">
+ <linearGradient
+ gradientTransform="translate(405.03952,235.75369)"
+ id="SVGID_2_"
+ gradientUnits="userSpaceOnUse"
+ x1="223.6167"
+ y1="166.2236"
+ x2="223.6167"
+ y2="228.2177">
+ <stop
+ offset="0"
+ style="stop-color:#F8BB5E"
+ id="stop3925" />
+ <stop
+ offset="1"
+ style="stop-color:#FF931E"
+ id="stop3927" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_2_"
+ id="linearGradient2994"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(3.0240976,0,0,3.0240976,-75.850759,-189.87639)"
+ x1="223.6167"
+ y1="166.2236"
+ x2="223.6167"
+ y2="228.2177" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="-3.669379"
+ inkscape:cy="107.05295"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5"
+ inkscape:window-width="1244"
+ inkscape:window-height="632"
+ inkscape:window-x="1984"
+ inkscape:window-y="341"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata4434">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-500.4859,-304.25654)">
+ <path
+ style="fill:url(#linearGradient2994)"
+ inkscape:connector-curvature="0"
+ d="m 593.27016,326.92074 c -7.06731,7.06732 -12.42602,16.15777 -15.21726,26.17355 -18.37442,-0.28423 -36.8063,6.87078 -51.43081,21.50135 -24.62221,24.61919 -32.27923,69.28813 2.01704,103.5844 28.85597,28.85899 71.95238,29.29444 100.23374,1.01007 13.36953,-13.37859 21.88235,-31.7742 22.06684,-51.23124 9.49566,-2.52813 18.5226,-7.58139 26.21894,-15.26867 21.0356,-21.04469 27.47693,-57.21288 -0.94049,-85.63639 -23.68778,-23.68172 -59.35096,-23.73314 -82.948,-0.13301 z m 77.48344,61.76115 c -4.03718,4.04019 -9.20837,7.02799 -14.91486,8.65497 -9.15391,2.62493 -14.08323,2.35275 -17.2313,7.60563 -3.14808,5.24982 -2.80031,5.86977 -2.66122,15.70715 0.16937,11.15587 -4.03108,22.72305 -14.55499,33.25298 -16.08816,16.08214 -45.78482,19.66872 -67.63693,-2.18343 -17.2162,-17.22223 -25.29659,-46.20216 -2.35278,-69.14598 8.51589,-8.50678 20.76348,-13.53887 33.6401,-13.30903 8.95432,0.15417 9.99763,0.48383 15.76157,-2.12898 5.77301,-2.63097 3.27209,-8.46749 6.71954,-18.76453 1.68142,-5.01999 4.67222,-9.96441 9.35054,-14.64875 12.47743,-12.47741 35.44542,-15.33821 52.27151,1.48182 13.70824,13.71431 19.52054,35.56339 1.60882,53.47815 z"
+ id="path3929" />
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon.png b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon.png
new file mode 100644
index 00000000000..c5e25639dd9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon.svg b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon.svg
new file mode 100644
index 00000000000..bd65f23fc48
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-icon.svg
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="59.99931"
+ height="59.999016"
+ id="svg5138"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 32">
+ <defs
+ id="defs5140">
+ <linearGradient
+ id="SVGID_14_"
+ gradientUnits="userSpaceOnUse"
+ x1="227.7168"
+ y1="437.05859"
+ x2="227.7168"
+ y2="505.11819">
+ <stop
+ offset="0"
+ style="stop-color:#F8BB5E"
+ id="stop4087" />
+ <stop
+ offset="1"
+ style="stop-color:#FF931E"
+ id="stop4089" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="translate(297.99763,51.01898)"
+ y2="505.11819"
+ x2="227.7168"
+ y1="437.05859"
+ x1="227.7168"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5136"
+ xlink:href="#SVGID_14_"
+ inkscape:collect="always" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="24.432041"
+ inkscape:cy="35.346817"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2666"
+ inkscape:window-y="446"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata5143">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-495.71463,-488.07696)">
+ <path
+ style="fill:url(#linearGradient5136)"
+ inkscape:connector-curvature="0"
+ d="m 549.24763,524.95998 c 7.502,-7.505 9.799,-20.401 -0.335,-30.538 -4.223,-4.222 -9.513,-6.337 -14.799,-6.345 h -0.005 c -5.286,-0.007 -10.568,2.093 -14.775,6.299 -2.522,2.52 -4.433,5.76 -5.428,9.33 -6.49,-0.099 -13.001,2.411 -18.191,7.53 v 36.48 c 0.114,0.12 0.227,0.241 0.344,0.36 h 25.518 11.027 c 4.434,-4.717 7.234,-11.018 7.295,-17.672 3.385,-0.898 6.606,-2.7 9.349,-5.444 z m -13.748,-2.759 c -1.122,1.873 -0.998,2.089 -0.95,5.602 0.062,3.979 -1.436,8.102 -5.188,11.854 -5.737,5.737 -16.327,7.017 -24.119,-0.776 -6.14,-6.14 -9.021,-16.475 -0.84,-24.658 3.038,-3.034 7.405,-4.827 11.998,-4.745 3.193,0.055 3.564,0.174 5.621,-0.76 2.058,-0.938 1.166,-3.019 2.396,-6.692 0.6,-1.792 1.667,-3.551 3.333,-5.224 4.451,-4.448 12.642,-5.469 18.642,0.531 4.887,4.887 6.96,12.677 0.573,19.065 -1.442,1.444 -3.285,2.509 -5.32,3.087 -3.265,0.937 -5.023,0.838 -6.146,2.716 z"
+ id="path4091" />
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo-foreground-white.png b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo-foreground-white.png
new file mode 100644
index 00000000000..7267a3650c0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo-foreground-white.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo-foreground-white.svg b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo-foreground-white.svg
new file mode 100644
index 00000000000..e1926079fc5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo-foreground-white.svg
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="247.22577"
+ height="73.113281"
+ id="svg4780"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 19">
+ <defs
+ id="defs4782">
+ <linearGradient
+ gradientTransform="translate(-217.85083,355.80569)"
+ id="SVGID_6_"
+ gradientUnits="userSpaceOnUse"
+ x1="589.38281"
+ y1="162.9478"
+ x2="589.38281"
+ y2="231.95531">
+ <stop
+ offset="0"
+ style="stop-color:#F8BB5E"
+ id="stop3993" />
+ <stop
+ offset="1"
+ style="stop-color:#FF931E"
+ id="stop3995" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_6_"
+ id="linearGradient4819"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-217.85083,355.80569)"
+ x1="589.38281"
+ y1="162.9478"
+ x2="589.38281"
+ y2="231.95531" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#000000"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.98994949"
+ inkscape:cx="249.54354"
+ inkscape:cy="3.6354025"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-bottom="5"
+ fit-margin-right="5"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="1928"
+ inkscape:window-y="52"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata4785">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-262.1014,-515.80554)">
+ <g
+ id="g4811">
+ <path
+ id="path3984"
+ d="m 338.79716,540.67169 c -1.337,-0.291 -2.28,-0.448 -3.441,-0.448 -2.848,0 -5.33,0.831 -7.393,2.466 -0.09,0.062 -0.191,0.14 -0.302,0.247 -0.23,0.196 -0.452,0.406 -0.669,0.621 -1.477,1.313 -2.759,1.951 -2.759,-1.236 v -0.666 c 0,-0.405 -0.327,-0.732 -0.732,-0.732 h -2.1 c -0.2,0 -0.389,0.08 -0.527,0.223 -0.138,0.142 -0.212,0.334 -0.206,0.532 0.106,3.332 0.215,6.777 0.215,9.621 v 18.749 c 0,0.405 0.328,0.732 0.732,0.732 h 2.209 c 0.403,0 0.732,-0.327 0.732,-0.732 v -9.913 c 0,-1.37 0.107,-3.805 0.31,-4.813 1.568,-7.797 4.982,-11.588 10.435,-11.588 0.979,0 1.88,0.164 2.874,0.362 0.047,0.01 0.095,0.015 0.143,0.015 0.148,0 0.294,-0.045 0.417,-0.131 0.162,-0.112 0.273,-0.288 0.307,-0.484 l 0.322,-1.994 c 0.062,-0.383 -0.187,-0.749 -0.567,-0.831 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path3986"
+ d="m 427.84417,542.26369 c -2.513,-1.776 -5.294,-2.04 -6.727,-2.04 -2.911,0 -5.648,0.912 -7.821,2.495 -0.005,0.003 -0.011,0.004 -0.015,0.008 -1.006,0.649 -2.627,0.91 -2.617,-0.625 v -0.022 c 0,-0.105 -0.005,-0.197 -0.014,-0.276 l -0.009,-0.182 c -0.018,-0.391 -0.341,-0.698 -0.731,-0.698 h -2.102 c -0.202,0 -0.396,0.083 -0.534,0.231 -0.139,0.148 -0.21,0.347 -0.195,0.55 0.152,2.28 0.212,4.171 0.212,6.524 v 21.82 c 0,0.405 0.33,0.732 0.733,0.732 h 2.209 c 0.404,0 0.731,-0.327 0.731,-0.732 v -17.51 c 0,-0.813 0.253,-1.762 0.49,-2.378 0.005,-0.009 0.007,-0.016 0.01,-0.024 1.044,-3.043 4.223,-6.121 8.417,-6.475 3.236,0.002 8.326,2.358 9.024,8.226 0.016,0.15 0.03,0.299 0.041,0.443 0.005,0.049 0.008,0.099 0.013,0.149 0.005,0.07 0.009,0.14 0.014,0.208 0.009,0.192 0.015,0.386 0.015,0.583 0,0.335 0.005,0.637 0.018,0.91 v 15.868 c 0,0.405 0.326,0.732 0.73,0.732 h 2.21 c 0.403,0 0.732,-0.327 0.732,-0.732 v -16.271 c 10e-4,-5.372 -1.627,-9.246 -4.834,-11.514 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path3988"
+ d="m 504.06817,567.60569 c -0.133,-0.113 -0.301,-0.173 -0.474,-0.173 -0.039,0 -0.079,0.003 -0.118,0.01 -0.588,0.098 -0.964,0.098 -1.444,0.098 -1.147,0 -2.177,-0.336 -2.177,-4.387 v -9.913 c 0,-2.343 -0.18,-5.368 -1.583,-7.971 -1.824,-3.384 -5.181,-5.1 -9.979,-5.1 -2.475,0 -6.218,0.516 -9.99,2.975 -0.313,0.205 -0.424,0.615 -0.252,0.949 l 0.862,1.669 c 0.098,0.189 0.272,0.326 0.479,0.376 0.058,0.015 0.116,0.021 0.174,0.021 0.15,0 0.296,-0.046 0.422,-0.135 2.314,-1.639 5.188,-2.506 8.306,-2.506 3.995,0 6.004,1.854 7,3.94 v 0 c 1.002,2.22 -0.563,4.403 -2.286,4.584 -0.084,0.004 -0.169,0.008 -0.253,0.013 -0.015,-10e-4 -0.027,10e-4 -0.043,0 -0.055,-10e-4 -0.101,10e-4 -0.143,0.009 -10.979,0.626 -16.537,4.313 -16.537,10.98 0,2.028 0.801,4.057 2.201,5.568 1.235,1.334 3.56,2.922 7.637,2.922 3.471,0 6.27,-1.111 8.324,-2.472 0.061,-0.032 0.131,-0.077 0.211,-0.141 0.021,-0.018 0.044,-0.031 0.064,-0.048 0.127,-0.089 0.257,-0.177 0.377,-0.268 1.277,-0.824 1.97,-0.351 2.623,0.503 0.002,0.003 0.006,0.005 0.008,0.008 0.839,1.141 2.162,1.717 3.963,1.717 0.847,0 1.59,-0.093 2.343,-0.293 0.322,-0.085 0.544,-0.375 0.544,-0.708 v -1.669 c 0,-0.214 -0.096,-0.419 -0.259,-0.558 z m -18.252,0.527 c -3.042,0 -6.111,-1.722 -6.111,-5.572 0,-4.862 5.936,-6.58 12.166,-7.043 2.32,-0.043 4.098,2.161 4.313,4.575 v 0.989 c -0.063,0.647 -0.246,1.289 -0.569,1.888 -0.161,0.265 -0.354,0.557 -0.588,0.865 -0.04,0.048 -0.076,0.093 -0.104,0.133 -1.442,1.847 -4.223,4.165 -9.107,4.165 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path3990"
+ d="m 467.93517,540.92469 h -2.047 c -0.392,0 -0.716,0.31 -0.731,0.703 -0.064,1.619 -0.892,1.836 -2.701,0.765 -0.011,-0.005 -0.02,-0.013 -0.028,-0.02 -0.179,-0.118 -0.362,-0.23 -0.547,-0.337 -0.01,-0.008 -0.02,-0.014 -0.031,-0.021 -0.087,-0.057 -0.168,-0.103 -0.243,-0.139 -1.982,-1.096 -4.308,-1.65 -6.925,-1.65 -3.739,0 -7.428,1.548 -10.121,4.249 -2.082,2.087 -4.564,5.827 -4.564,11.784 0,3.818 1.347,7.413 3.791,10.121 2.597,2.875 6.233,4.458 10.249,4.458 3.11,0 5.514,-0.819 7.335,-1.896 0.004,-0.002 0.007,-0.003 0.012,-0.005 1.953,-0.875 3.602,-2.184 3.134,2.233 -0.875,5.677 -4.518,8.764 -10.48,8.764 -4.176,0 -7.31,-1.405 -9.204,-2.584 -0.116,-0.073 -0.251,-0.109 -0.388,-0.109 -0.06,0 -0.123,0.006 -0.182,0.023 -0.192,0.049 -0.356,0.176 -0.456,0.35 l -0.968,1.724 c -0.19,0.336 -0.086,0.76 0.237,0.972 2.853,1.862 6.909,2.974 10.852,2.974 2.689,0 9.294,-0.625 12.493,-6.412 1.385,-2.481 2.031,-5.968 2.031,-10.969 v -17.456 c 0,-2.474 0.07,-4.678 0.216,-6.738 0.013,-0.203 -0.058,-0.401 -0.196,-0.55 -0.138,-0.149 -0.334,-0.234 -0.538,-0.234 z m -16.892,26.027 c -0.604,-0.24 -7.607,-3.216 -7.011,-11.812 0.549,-7.939 5.715,-10.454 6.518,-10.798 1.276,-0.5 2.698,-0.768 4.24,-0.768 1.663,0 3.112,0.327 4.354,0.873 6.845,3.871 7.696,16.452 0.883,21.467 -1.592,1 -3.437,1.572 -5.346,1.572 -1.341,0 -2.551,-0.192 -3.638,-0.534 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path3997"
+ d="m 369.17717,526.64669 c -2.33801,2.337 -4.11001,5.343 -5.03101,8.655 -6.076,-0.094 -12.172,2.272 -17.008,7.11 -8.143,8.141 -10.674,22.912 0.668,34.253 9.542,9.543 23.79301,9.687 33.14401,0.334 4.422,-4.424 7.237,-10.507 7.298,-16.941 3.14,-0.836 6.125,-2.507 8.67,-5.049 6.957,-6.959 9.085,-18.919 -0.311,-28.318 -7.834,-7.831 -19.627,-7.848 -27.43,-0.044 z m 25.622,20.423 c -1.335,1.336 -3.046,2.324 -4.934,2.862 -3.026,0.868 -4.655,0.778 -5.696,2.515 -1.042,1.736 -0.925,1.941 -0.881,5.194 0.056,3.689 -1.332,7.514 -4.813,10.996 -5.319,5.318 -15.13901,6.504 -22.36601,-0.722 -5.692,-5.695 -8.363,-15.278 -0.776,-22.865 2.816,-2.813 6.867,-4.477 11.123,-4.401 2.96101,0.051 3.30601,0.16 5.21301,-0.704 1.907,-0.87 1.082,-2.8 2.222,-6.205 0.557,-1.66 1.544,-3.295 3.093,-4.844 4.125,-4.126 11.721,-5.072 17.284,0.49 4.532,4.534 6.456,11.76 0.531,17.684 z"
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient4819)" />
+ <path
+ id="path4033"
+ d="m 299.94416,540.22369 c -3.604,0 -5.781,1.055 -7.803,2.675 -0.001,10e-4 -0.001,10e-4 -0.001,0.002 -2.141,1.482 -3.268,1.373 -5.032,0.007 -0.009,-0.007 -0.017,-0.009 -0.025,-0.015 -1.785,-1.704 -4.11,-2.669 -6.695,-2.669 -2.797,0 -5.132,0.737 -7.113,2.275 -0.042,0.029 -0.086,0.062 -0.129,0.102 -0.878,0.785 -2.286,1.01 -2.444,-0.238 l -0.017,-0.316 c 0.002,-0.087 -0.002,-0.167 -0.01,-0.234 l -0.01,-0.19 c -0.018,-0.391 -0.34,-0.697 -0.73,-0.697 h -2.101 c -0.205,0 -0.397,0.083 -0.536,0.232 -0.139,0.149 -0.209,0.347 -0.195,0.55 0.148,2.118 0.214,4.13 0.214,6.523 v 21.82 c 0,0.405 0.329,0.732 0.732,0.732 h 2.21 c 0.403,0 0.73,-0.327 0.73,-0.732 v -17.457 c 0,-1.086 0.266,-1.975 0.487,-2.529 1.183,-3.106 4.305,-6.436 8.372,-6.436 4.431,0 7.295,3.476 7.295,8.857 v 17.564 c 0,0.405 0.328,0.732 0.731,0.732 h 2.211 c 0.403,0 0.731,-0.327 0.731,-0.732 v -18.317 c 0,-1.093 0.289,-1.956 0.54,-2.585 1.118,-2.651 4.064,-5.519 7.887,-5.519 4.983,0 7.727,3.681 7.727,10.366 v 16.055 c 0,0.405 0.327,0.732 0.733,0.732 h 2.208 c 0.404,0 0.732,-0.327 0.732,-0.732 v -16.001 c 10e-4,-12.828 -8.188,-13.825 -10.699,-13.825 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo.ai b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo.ai
new file mode 100644
index 00000000000..234bfbc63dc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo.ai
@@ -0,0 +1,243 @@
+%PDF-1.5 %
+1 0 obj <</Metadata 2 0 R/OCProperties<</D<</ON[5 0 R]/Order 6 0 R/RBGroups[]>>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <</Length 13943/Subtype/XML/Type/Metadata>>stream
+<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
+<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.0-c060 61.134777, 2010/02/12-17:32:00 ">
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <rdf:Description rdf:about=""
+ xmlns:xmp="http://ns.adobe.com/xap/1.0/"
+ xmlns:xmpGImg="http://ns.adobe.com/xap/1.0/g/img/">
+ <xmp:CreatorTool>Adobe Illustrator CS5</xmp:CreatorTool>
+ <xmp:CreateDate>2013-04-03T11:08:21+09:00</xmp:CreateDate>
+ <xmp:ModifyDate>2013-04-03T11:08:21+09:00</xmp:ModifyDate>
+ <xmp:MetadataDate>2013-04-03T11:08:21+09:00</xmp:MetadataDate>
+ <xmp:Thumbnails>
+ <rdf:Alt>
+ <rdf:li rdf:parseType="Resource">
+ <xmpGImg:width>256</xmpGImg:width>
+ <xmpGImg:height>68</xmpGImg:height>
+ <xmpGImg:format>JPEG</xmpGImg:format>
+ <xmpGImg:image>/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA&#xA;AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK&#xA;DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f&#xA;Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgARAEAAwER&#xA;AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA&#xA;AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB&#xA;UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE&#xA;1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ&#xA;qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy&#xA;obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp&#xA;0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo&#xA;+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7&#xA;FXYq7FXYq7FXYqhtQ1C2sLYzzmijZVHVj4DKNRqI4o8Um3DhlklQYXqPmXUbxiFcwQ9o4zTb3bqc&#xA;5nUdpZch2PCO4O7w6KEOllu18t6xdJ6vpiNW3BlPEn6Nzji7NzZBdV71nrccNr+SJMPmbRx6oJeB&#xA;ftAH1Ep7r1Hzy/g1Wm35x+YauLBm26/Isg0XXbfUoytPTuUHxxV7eK+2bjRa6Ocd0u512p0ssR74&#xA;pnmc4rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVWvJHGKuw&#xA;QeLED9eCUgOZSIk8mAeZdUN7qThWrBATHEB02+030nOR7S1Pi5T/ADY7B6LRYOCHmUw8naVHcSPf&#xA;TLyWFuMSnpz6k/RtmX2PpRMnJLpy97j9o5zEcA6sxzpHSOxVh3mGxbSL6HUrL92jtuo6B+tPkw7Z&#xA;znaGA6fIMuPYX9v7XdaPL40Djmyuyuo7u0iuY/syqGA8K9R9Gb/DlGSAkOrqMuMwkYnorZYwdirs&#xA;VdirsVdirsVdirsVdirsVdirsVef+Ufz2/LTzTpuralYap9XstEEbajNeobZUSXl6bAv9oMUIFN6&#xA;7U3GKsF1n/nMz8rrK6aCxtNT1RENPrUUMcUTDxX1pEk+9BirOvy2/PT8vfzCka10S7kg1RFLtpd6&#xA;ghuSg6sgDOkgHfg5p3xVG/mH+b/kLyBCh8xaiI7uZecGnQL611IvTkI1+yv+U5C++KvMbb/nNT8s&#xA;pLsRy6Xq8FuTQXDRW7U92RZyQPlXFXtPlPzh5b826PHrHl6/j1DT5CV9WOoKuACUkRgHRhUfCwBx&#xA;VOMVYbafm35LuvzDuPIEVxL/AIjt1ZniaJhESsQmKrJ0r6bVxVJPzC/5yK/LPyNevp2oXkt/q0W0&#xA;2n6cizSRnwkZmjiQ/wCSX5e2KpX+XH/OT3kjz15otvLVlp+o2WpXgkNs1wkJhb0YmlcFo5GZTwQ0&#xA;+GmKvXbm5gtYHnncRxIKsxyGTJGETKRoBnCBmaG5YTq3nG8uWaOyJt4OgYf3jfT+z9Gczq+15zNQ&#xA;9Mftd7p+zYx3n6j9iV2thqupOXhiec1+KQnavu7Gn45gYtPlzG4gy8/2uXkzY8Q3ICYDydrhpVIx&#xA;83H8Myx2Pn7h83G/lLF5su0LTG07TktnYPJUtIy9KnwrnRaHTHDjETzdNqs/iz4hyTDMtxnYqkXn&#xA;R410UhvtNIgT57n9Vc1XbJAwb94dh2YCcvwQej63aaR5LvNX1BmSw0qK4urh1BYiGBDNIVUbnYHb&#xA;D2PInAPIle0hWX4IDy7+dP5c675TufNcGqpaaLZzNbXM16Pq7JKqq/Di27FlcFeNa9t82jr3ner/&#xA;APOZv5X2d00FjZanqcan/eqOGKKJvdRNIkn/AASDFWZ/l3/zkH+Wnnu8XT9LvZLPVpKmLTdQQQzP&#xA;T/fZVpInP+SrlvbFXpOKuxV5x+YH/OQP5ZeRrl7HVNQa71WP+80zT0E86+zklIo2/wAl3B9sVYLZ&#xA;f85p/lnNcenc6Xq1rETQTmK3cD3ZVm5fdXFXoN1+fP5WQ+UH82R63HdaUkiQskCs1yJZDRUa3IWR&#xA;T1PxAbb4qyzyx5k0vzNoFjr2lM76fqEYmt2kQxuVJI3VtxuMVUPNnnbyn5R0/wDSHmTVINNtTsjS&#xA;kl3I6iONQ0kh9lU4q8c1P/nND8sba4MVlp+q38akg3CxQxIfAqJJQ/3qMVZB5H/5yk/KzzXqEOm+&#xA;vcaNqFwwjgi1KNY0kkboqzRvLGKnYcytcVevYq/N78pPy11H8xPOdt5ftZDb2/FrjUbzjy9G2jID&#xA;vTuxLBVHiRir7q8t/kn+Vnl/Sk0608t2NwgXjLcXsEV1PKabmSSVWJr4CijsBir5y/5yh/JzS/I8&#xA;unedvJ0baVa3Fx9Xu7a2ZkWC5KtJFLBQ1jVgjAgUCkCnXFUX/wA44fkXZedrWTz956aXVbeaZo9O&#xA;tLiR3+sGE8HnuGJ5OoccFWv7JrtTFX0RrP5O/ldq+lvpl15X01Ldl4I1vbRW8se1AY5YlR0I9jir&#xA;5t/Kwap+UP8AzkZP5FkuXm0TV5Ba1bpIk0ZlspqdOaswjani2KvsPFXwP+fuq635e/5yB8yahpV5&#xA;Lp+pRSRGG7tnKSIs9jEDxdaEVjkIxV7p+Rv/ADjT5astBtfMfnixXV/MWpKLo2V6PUgtkk+JVeJt&#xA;pJSDVzIDQ7AbVKr2fTvIfkjTNRj1PTfL+m2Oowq0cV5bWkEMqow4lQ8aq1KbYqxzzdrTXl81rG3+&#xA;i2xKgDozjZm+joM5LtbWHJk4B9Mfvek7O0vBDiP1S+5R8saJ+lLsmWotIKGUjbkT0UfPvlfZui8e&#xA;e/0R5/qZ67VeFHb6i9EiiiijWOJAkaiioooAPYDOvjERFAUHmZSJNnmuySHYq7FUNeanYWSFrqdI&#xA;qfsk/EfkvU5Tm1OPGLkQG3FgnM+kW8/8xa+2q3Q4ApaxVESHqa9WPuc5LtDXHPLb6Ryek0Wj8GO/&#xA;1HmmfmfTBF+UHmG0mBBm0bUPVA2I9W2k2+YBpnTdnYTjwxB58/m6DW5RPKSOT4s/Ij8n7j8y/NUl&#xA;pNK1toemqs+rXKU9Ti5IjhjqCOchU7nYAE+AOa4r7V0n8l/yp0vS/wBG23lXTZLcqFka4to7iV6C&#xA;lXlmDyMfpxV85/8AOR//ADj/AKf5NtU88+ShJZafbzR/X7FGY/VZGcCK4gcnkq+oVUrX4SRTboq+&#xA;h/yT88zedvy00bXbo11B42t9QPStxbsYneg2HqcQ9PfFWG/85Qfm9eeRvKsGlaLMYfMGvepHDcqa&#xA;Pb2yACWVCPsuSwVD23I3XFWP/kN/zjV5ft9DtfNHnmyXVtc1NRcx6feD1ILeOX4k9WNqiSVgavzq&#xA;BWlKiuKvYNT/ACo/LLUrJ7O88raW0EgoeFpDE6+6SRqjofdWBxV8S/n/APlAfy283JbWbSTaBqaN&#xA;PpU0m7LxNJIHbbk0ZI37qR3rir7G/IH/AMk15T/5gV/4k2Ktfmd+SXlD8xtR0e819rhf0SXHp28n&#xA;ATxSUJickEgclG60brv4Kpvov5V/lvolusGmeWdNgVRx5m2jklYf5csgeRv9kxxV49/zkz+R3kf/&#xA;AANf+btFsbfRdW0kJLKLZFgguYmdY2R414oH+KqsBUnY1rsqzD/nF/zvf+bPyptH1GVp7/SJ5NMn&#xA;nc1eRYlSSJmJ3J9KVVJ70riryL/nB2KM6z5slKj1FtrRVbuFaSUkfTxGKvrfFXiv/OXoH/Km7j2v&#xA;rSn/AARxVkH/ADjd/wCSS8rf8YJv+omXFXpWKvlH8+II4/8AnKr8vnX7Uz6K7/MapIn6kGKvq7FX&#xA;xj+YXl6HzF/zmMdIuFD2099pj3EbdHih0+3mkQ/6yRkYq+zsVQ+o3JttPubgdYoncfNVJGU6jJwY&#xA;5S7gW3BDjmI95eSFySSTUnqc4C3s6eieR40XQldftSSOzfMHj+oZ1/YsQMF95LzPasic1dwCf5tn&#xA;WuxV2KpJ5wvri00V2gYo8jrGXGxANSaH6M1na2aWPCTHYk05/ZuKM8tHoLeblySSTUnqc423qaZP&#xA;5U8sy3UqX14nG1Qho42G8h7Gn8v683fZfZpmRkmPSOXn+x1PaGuEAYR+r7v2p5+Yf/KAeZv+2Vff&#xA;9Qz51TzrxH/nCO3hXyT5guQoE0mpLG79yscCFR9Bkb78VfR2KvPv+cgER/yZ82BlDAWRNCK7q6kH&#xA;6CMVYZ/zhsT/AMqjm9tVuaf8i4cVeX/85DwjXf8AnJfy3ol6a2UjaTY8T09K4uiXqPf1Tir7EAAF&#xA;BsB0GKuxV89/85rWVtJ+W2kXj0FzBq8cULGtSsttOXUU8fSU7+GKvRfyB/8AJNeU/wDmBX/iTYqp&#xA;/m9+d3lX8s7GI6grX2sXalrLSIGCyOoJHqSMa+nHUU5UJJ6A0NFXiVr+bP8AzlT5/H1nydoK6Xpk&#xA;u9vcx28SoyV6i41AmKT5oPorirCvzi8uf85KWvloXvny7uL3y/6iG4WGeKSCKSoWP1ooOIHxUo1C&#xA;te9Tir1v/nCX/lAdd/7ap/6hosVYr/zg5/x1fN3/ABgsv+JzYq+tMVeK/wDOXv8A5Ju5/wCY60/4&#xA;kcVZB/zjd/5JLyt/xgm/6iZcVelYq+Vfz9/9an/Lj/ty/wDdXmxV9VYq+S9T/wDW40/4z2//AHRU&#xA;xV9aYqgdcjaTRr5F3YwSUHjRScxtbEnDMD+aXI0hrLEn+cHkvLOBt7Smb/l9qiGOfTnNHB9aGvcG&#xA;gYfR1zpewtSKOM8+Y/S6DtnTmxkHuLMc6J0TsVdirEfzCvkW1t7IH95I/qsPBVBUfeT+Gc/29mAh&#xA;GHUm3d9jYSZGfQbNeStE06bTxfXEAlnMjCNnqVCrQbKfh649j6PHLHxyFyte1NVOM+CJoUy/OgdI&#xA;x/8AMP8A5QDzN/2yr7/qGfFXiv8AzhL/AMoDrv8A21T/ANQ0WKvorFWAfn9/5JrzZ/zAt/xJcVYT&#xA;/wA4bf8Ako5v+2rc/wDJqHFWBf8AOYnlnU9J82eXvzB01CqBY7WadRVY7u1kae3Zvd1JA/1MVfRH&#xA;5afmHonn3ypZ67pcq85EC31oGBkt7gCjxOOo3Hwk/aWhxVlWKvjz/nMb8ytO1nWNP8m6XOtxFozv&#xA;capIhDILthwSIEH7UScufu1OoOKvoT8gf/JNeU/+YFf+JNir59isNP8AOP8AzmHe2fmgCa1tbmZb&#xA;aynAMcosbesEfE7cTw9Qj9revXFX2AqqqhVAVVFFUbAAdhirzr/nILzPoWg/lP5hXVJEEmp2U+n6&#xA;fbsRzluLiMonBT19Mt6h8AMVedf84Ssv+A9eWo5DVale4Bt4qH8MVV/+cWfyl8+eQtS8yyeaNPWy&#xA;ivUto7N1ngmEpiaUsQIncqKMPtAdcVfQmKvMv+ci/JXmPzl+Wdzo3l22F5qZubeZLYyRxcljf4qP&#xA;KyJUA13YYqm/5LeWdY8sflfoGhazCLfU7KBxdQK6yBGeZ5OPNCykgOK0JGKs2xV4F+bn5V+efMP5&#xA;+eSvNekaeLjQdJ/Rv6RuzPBH6X1XUJLiX927rI1I3BHFTir33FXz3eflL58m/wCcqV89pp6/4VDx&#xA;SHUDPB0TTFtiPR5+tX1Vp9j3xV9CYq4gEEEVB2IxIUPI9e0yTTNTmtWB4A8oW8Y2+yf4ZwOt0xw5&#xA;THp09z22jzjNjEuvX3oS1u57W4juIHKSxHkjDscox5JQkJRNEN+TEJxMTyL0XQ/Omm30ax3Tra3f&#xA;Rlc0Rj4qx2+g512j7Xx5RUzwz+x5bV9l5MZuI4o/ayFWVlDKQVO4I6ZtgbdYQk+s+a9K02Nh6guL&#xA;n9mCMgmv+URULmv1faeLCOfFLuH42c7S9nZcp5VHvLze9vbzVdQaaSslxOwVEXoK7KqjOPzZp58n&#xA;Ed5S/FPVYsUcMKG0Q9V0iwGn6Zb2Y3MSAOR0LHdj/wAETnc6TB4WKMO4f2vG6nN4mQy70ZmQ0JR5&#xA;w0661LylrenWih7u9sLq3t0JCgySwsiAk7CrNirzD/nFz8u/N/kfyfqth5osRp95dagZ4YfWhmJi&#xA;9GNOXKB5FHxKdq1xV7NirEfzb8var5j/AC28w6JpMYm1K+tHjtYWZUDvUMF5MQorSm5pirF/+caf&#xA;I3mjyX+XL6R5lsxY6i9/PcC3EsU1I3SNVJaFpE34Hvir0TzH5c0XzJot1outWqXmm3icJ4H+8MpG&#xA;6sp3VhuDuMVfMF//AM40/m95C12XV/ys1314X2SFpUt7kp1EcySj6tMB4sR/qjFURcaD/wA5reYo&#xA;Rpmo3i6VayLwluln063JB2JMljznH+wGKsC/Oj/nHyx/LXyLpus3OtSalr99frbXESoI7fg0Msrs&#xA;gPKRirIo5E716DFX1h+SdmbT8o/KMJBUtpdtLQkE/voxL2/18VeYfn3+QPmTWfM0P5gfl/N6PmWH&#xA;03urQSCB5JIABFPBI1FEgVQrKxAIHWvVVjEP5n/85gQW405/KhnuV/dm/fTnLk9OXKN1t/pC0xV2&#xA;mf8AOOf5tfmPqn6b/NnW5LJVQrb2iNFNOobeiRxf6NAnQmlSe4HXFUB5X/Kb/nJ78tdX1Gw8ki3u&#xA;NKvZEL3vqWRgkEZYRyGK5YSxtxb4gq/S1AcVfXeKuxV2KuxV2KuxV2KuxV2KuxVJ/Mvl6HWbQLUR&#xA;3UVTBL7n9lv8k5r+0NBHUQrlIci52h1pwS74nmHl17Z3djcNb3UZimTqp/WD3GcVmwzxy4ZCi9hi&#xA;yxyR4omwocsqbad6jBStTxPUdsNo4XAkkACpOwAwBXoHkzynJaldSv043FP9HgPVK/tN/leA7fPp&#xA;1XZPZhh+8yfV0Hd5+95rtTtET/dw+nqe9mGdA6J2KuxV2KuxV2KuxVjn5g+e9G8i+VbvzJrAke0t&#xA;eCiGEBpZJJGCIiAlRuT3OwxVJfKH56/lX5qtkk0/X7a3uHA5WN862lwrH9nhKVD08ULD3xVPNW/M&#xA;TyHpFo93qXmHT7aBBUs1zESdq0VQxZj7KCcVfKf5i+aNR/5yC/M/SfLPlSKYeW9NZv8AS3QrSN2U&#xA;XF7Ip+yoVQsatufYtTFX2PY2VtY2VvZWyenbWsaQwRjoqRqFUfQBiqtirsVdirsVdirsVdirsVdi&#xA;rsVdirsVdirsVdiqU+Yv8P8A1T/cz6fp7+ny/vK9+HH4vuzB1/5fg/fVX2/Dq5uh8fi/dXf2fHo8&#xA;w1T9Desf0Z6/pV/3fx/Dj/HOK1Pg3+64q86ew0/i1+84b8kLB6Pqj1+XpftcKcvortlMKv1cvJun&#xA;denm9A8o/wCDvUX6lX6/+z9ap6v+w/Y/4HfOq7L/ACd+j6/6XP4dPk8x2l+br1/R/R5fHr82X5v3&#xA;RuxV2KuxV2KuxV2KuxVKfNf+F/8AD95/in6p+geH+nfpDh9W41FOfqfD9qlPfpvir4t/Mb/oVb69&#xA;L/hz9O+tVv8Ajm+n9S5f9H372lenHan0YqxTy3/yob6+v+IP8S/VarT6v9Sp139Svxcf9XfFX2j+&#xA;Sv8AyqH/AA0f+Va/VvqNV+u8OX1v1KGn1r1f31evHlt/Ltir0LFXYq7FXYq7FX//2Q==</xmpGImg:image>
+ </rdf:li>
+ </rdf:Alt>
+ </xmp:Thumbnails>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xmpTPg="http://ns.adobe.com/xap/1.0/t/pg/"
+ xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
+ xmlns:xmpG="http://ns.adobe.com/xap/1.0/g/">
+ <xmpTPg:NPages>1</xmpTPg:NPages>
+ <xmpTPg:HasVisibleTransparency>False</xmpTPg:HasVisibleTransparency>
+ <xmpTPg:HasVisibleOverprint>False</xmpTPg:HasVisibleOverprint>
+ <xmpTPg:MaxPageSize rdf:parseType="Resource">
+ <stDim:w>87.251177</stDim:w>
+ <stDim:h>25.792741</stDim:h>
+ <stDim:unit>Millimeters</stDim:unit>
+ </xmpTPg:MaxPageSize>
+ <xmpTPg:PlateNames>
+ <rdf:Seq>
+ <rdf:li>Cyan</rdf:li>
+ <rdf:li>Magenta</rdf:li>
+ <rdf:li>Yellow</rdf:li>
+ <rdf:li>Black</rdf:li>
+ </rdf:Seq>
+ </xmpTPg:PlateNames>
+ <xmpTPg:SwatchGroups>
+ <rdf:Seq>
+ <rdf:li rdf:parseType="Resource">
+ <xmpG:groupName>初期設定のスウォッチグループ</xmpG:groupName>
+ <xmpG:groupType>0</xmpG:groupType>
+ </rdf:li>
+ </rdf:Seq>
+ </xmpTPg:SwatchGroups>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:illustrator="http://ns.adobe.com/illustrator/1.0/">
+ <illustrator:Type>Document</illustrator:Type>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <dc:format>application/pdf</dc:format>
+ <dc:title>
+ <rdf:Alt>
+ <rdf:li xml:lang="x-default">mroonga-logo</rdf:li>
+ </rdf:Alt>
+ </dc:title>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">
+ <xmpMM:RenditionClass>proof:pdf</xmpMM:RenditionClass>
+ <xmpMM:DocumentID>uuid:4379d2f2-92f2-dc4a-80ad-17a20c304810</xmpMM:DocumentID>
+ <xmpMM:InstanceID>uuid:05546b07-0c26-2747-aadd-acdb3045814f</xmpMM:InstanceID>
+ </rdf:Description>
+ <rdf:Description rdf:about=""
+ xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
+ <pdf:Producer>Adobe PDF library 9.90</pdf:Producer>
+ </rdf:Description>
+ </rdf:RDF>
+</x:xmpmeta>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<?xpacket end="w"?> endstream endobj 3 0 obj <</Count 1/Kids[7 0 R]/Type/Pages>> endobj 7 0 obj <</ArtBox[5.09961 5.0 242.326 68.1133]/BleedBox[0.0 0.0 247.326 73.1133]/Contents 8 0 R/LastModified(D:20130403110821+09'00')/MediaBox[0.0 0.0 247.326 73.1133]/Parent 3 0 R/PieceInfo<</Illustrator 9 0 R>>/Resources<</ExtGState<</GS0 10 0 R>>/Properties<</MC0 5 0 R>>/Shading<</Sh0 11 0 R>>>>/Thumb 12 0 R/TrimBox[0.0 0.0 247.326 73.1133]/Type/Page>> endobj 8 0 obj <</Filter/FlateDecode/Length 3431>>stream
+HlWˎl9WsN;moHBXF0RbS+:v>"#_=n14qzo<xo?ۗRrV1ͳn{vY'W7s뚛ӧw+tbZU,'4XtB-L_p3q|٣NzA;F}:,1.sS}s82'\ ϳ&峬RƵ}G¾Q羞F:}}kd̼Q t @k5ph>7]
+dt2 4llxfC2.a>\ZX,x6ua$]L`ܤ?AʋAÛM_g WlsxD\{uAfZ5ڕvVFx~7"YកzK)Xa\
+МjB=M#ܠ
+ѠRd\
+4GƥWbh|ped&i\KO֜u]`n02ɖK]dĎJ4eѲTAvȅә|F/~-H2Fڅؘ%ך
+n^*M:=/O!fpl98ITD% wPȨ))4ǜLEX*Ng6[ ARbwCDOLh`;i!V~JhCI39~6.D~B3iqM0_ZbæTG?(7MOzۦC=``+-Lt$C
+:@ I9Mt'xO-Jhъ'#2) h *\V*8!3'Q60CGPTf'Bz 4ʢ+j:.!rCd SeMTlP
+Zntb UHA[G6ڂ(f]ǒ??YtwϷ- r [H.n81x
+췴 ;ǎnN!vJ(} TlH} 82azD4
+NQ\\u ,X(E{j4Ӣڄr&7)Ri?Ik1_|!
+)D-SUkс}&I1I-%0:pp)ѹ)$236%$83e+T;CH
+cvCXZ!93=RZUL~1aɮn0(
+Qah"W]A0aʼvjMӆG6z`DžD>SԕVA&6pY +E`刱iq榳HbDHævɹ;`r~Ş]o??6wy:yz)g_o lSBECA?woY FހvsXL rѸ[wm7/=R*{N)p5†+mhHJ`saAɫrYP%+YW3} ֦a%)nRyU}բ"%A")Lz^"J>VYp0΀ M7F5j4>V d^r]ONʱZt¥O47HsR2+z+aI RH(4(jP(ve|`gt {`Q7݂8B74@[Bcۿ7X_RPma70$ՏtNim-bGCL͚96@cذ673NjQL[ݰѐr{tLC_
+  tdBU񡙗C:=<Cn3aE ⒐;F~v+
+8;VGKd0Te-#XdEtNUCiWkORq"Vh&W#Td^iJ23NbJHJCsPi.X2-+Ss6VZ9dPW\CoS1
+LJg-V)Y*lkesN.`^A?7AC@InQC3+KOU-Fom!s%T7S='~> endstream endobj 13 0 obj [/Indexed/DeviceRGB 255 14 0 R] endobj 14 0 obj <</Filter[/ASCII85Decode/FlateDecode]/Length 428>>stream
+8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0
+b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup`
+E1r!/,*0[*9.aFIR2&b-C#s<Xl5FH@[<=!#6V)uDBXnIr.F>oRZ7Dl%MLY\.?d>Mn
+6%Q2oYfNRF$$+ON<+]RUJmC0I<jlL.oXisZ;SYU[/7#<&37rclQKqeJe#,UF7Rgb1
+VNWFKf>nDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j<etJICj7e7nPMb=O6S7UOH<
+PO7r\I.Hu&e0d&E<.')fERr/l+*W,)q^D*ai5<uuLX.7g/>$XKrcYp0n+Xl_nU*O(
+l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 11 0 obj <</AntiAlias false/ColorSpace 15 0 R/Coords[0.0 0.0 1.0 0.0]/Domain[0.0 1.0]/Extend[true true]/Function 16 0 R/ShadingType 2>> endobj 15 0 obj /DeviceRGB endobj 16 0 obj <</Bounds[]/Domain[0.0 1.0]/Encode[0.0 1.0]/FunctionType 3/Functions[17 0 R]>> endobj 17 0 obj <</C0[0.972549 0.733337 0.368622]/C1[1.0 0.576477 0.117645]/Domain[0.0 1.0]/FunctionType 2/N 1.0>> endobj 5 0 obj <</Intent 18 0 R/Name(svg4429)/Type/OCG/Usage 19 0 R>> endobj 18 0 obj [/View/Design] endobj 19 0 obj <</CreatorInfo<</Creator(Adobe Illustrator 15.0)/Subtype/Artwork>>>> endobj 10 0 obj <</AIS false/BM/Normal/CA 1.0/OP false/OPM 1/SA true/SMask/None/Type/ExtGState/ca 1.0/op false>> endobj 9 0 obj <</LastModified(D:20130403110821+09'00')/Private 20 0 R>> endobj 20 0 obj <</AIMetaData 21 0 R/AIPrivateData1 22 0 R/AIPrivateData2 23 0 R/ContainerVersion 11/CreatorVersion 15/NumBlock 2/RoundtripStreamType 1/RoundtripVersion 15>> endobj 21 0 obj <</Length 1005>>stream
+%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 15.0 %%AI8_CreatorVersion: 15.0.2 %%For: (SHIDARA Yoji) () %%Title: (mroonga-logo.svg) %%CreationDate: 2013/04/03 11:08 %%Canvassize: 16383 %%BoundingBox: 178 388 417 453 %%HiResBoundingBox: 178.9365 388.9434 416.1631 452.0566 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 11.0 %AI12_BuildNumber: 399 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([レジストレーション]) %AI3_Cropmarks: 173.8369 383.9434 421.1631 457.0566 %AI3_TemplateBox: 297.5 420.5 297.5 420.5 %AI3_TileBox: -114.9409 132.5 704.2715 708.5 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 1 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: -974 1133 1 2546 1413 26 0 0 2567 42 0 0 0 1 1 0 1 1 0 1 %AI5_OpenViewLayers: 7 %%PageOrigin:0 0 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 22 0 obj <</Length 5618>>stream
+%%BoundingBox: 178 388 417 453 %%HiResBoundingBox: 178.9365 388.9434 416.1631 452.0566 %AI7_Thumbnail: 128 36 8 %%BeginData: 5468 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD3AFFCFC9C9C8C8C2C9C9FD76FFCAC9C2C89FC8C2C89FC8C2C9CA %FD72FFCFC8C1C8C8C8C2C9C9C9C2C8C1C8CAFD70FFA8C89FC89FC8C8CFCA %FFFFFFA7C89FC8A8FD6FFFC9C2C8C2C8C9FD08FFC9C1C9FD6EFFC99FC8C2 %C2C9FD09FFCFC89FC9FD6DFFC2C8C2C8C2FD0BFFCAC8C1FD6CFFA7C89FC8 %9FC9FD0CFFC2C1A7FD68FFC9C9C8C8C2C8C2C8CAFD0CFFCFC1C9FD65FFC9 %C89FC8C1C29FC8C1C29FCFFD0CFFCAC29FFD3EFFA8FD09FFA8FD13FFA8FD %07FFC8C8C1C8C1C8C1C8C1C8C1C9FD0EFFC2C8CAFD09FFA8FD11FFA8FD11 %FFA8FD08FF5252FFFF5227F8F8F87DA8FFFFFF7D52F8F8F8277DFD07FFA8 %5252FFFFA82727F8F827FD04FFCFC2C19FC8C1C1A0C9C9CAA7CFCFFD0DFF %A7C2C1CAFFA827A8FFFF5227F8F8F852A8FD0AFFA85227F8F8F8527DFFA8 %5252FD06FFA85227F8F8F82752FD05FFF8F87D27FD06F852A8FF52FD07F8 %52FD06FFA8F827FFA8FD06F8FD04FFC2C8C1C8C1C8C9FD14FFCEC1C8CAFF %52F827A8FD07F827A8FD08FFA8FD08F82752F827FD04FFA852F8F8F827FD %04F852FD04FFFD05F87DA8A852F8F827F8F8F852A8A87D52F8F87DFD05FF %A8F8F852F8F827A8A8A87DFFFFFFA0C2C1C29FC8CAFD15FF9FC8C1CFFF7D %FD04F827A8A8A827F8F8F8A8FD06FF7DF8F827FD04A87DFD04F827FD04FF %A8F8F852A8A8FFA852F8F852FFFFFFF8F8F827FD05FF7DFD04F8A8FD05FF %52F827FD06FFFD04F852FD07FFC9C1C1C8C1C8CAFD15FFC8C2C1C8FFFF7D %F8F8F852FD05FF7DF8F852FD05FFA8F8F852FD06FFA8F8F8F852FD05FF7D %A8FD06FF7DF8F8A8FFFFF8F8F8FD07FF27F8F87DFD06FFA8F8F8A8FD04FF %A8F8F8F827FD07FFCAC2C1C29FC1C9FD15FFA0C1C1C1A0FFFF7DF8F852FD %07FF52F8F8FD05FF27F827FD08FF52F8F827FD0EFF27F87DFFFFF8F87DFD %07FF7DF827A8FD07FF27F87DFD05FFF8F8F8A8FD07FFC9C1C8C1C8C2FD13 %FFCFC9C2C8C1C8C1FFFFFF7DF8F8FD09FFF8F8A8FFFFFFA8F8F8A8FD08FF %A8F8F852FD0EFF27F852FFFFF8F8A8FD07FF7DF8F8FD08FF52F87DFD04FF %A827F827FD08FFC1C2C1C29FC9FD11FFCA9FC2C1C19FC8C1C9FFFFFF7DF8 %27FD09FF27F87DFFFFFF7DF827FD0AFF27F827FD0BFFA8A852F8F852FFFF %F8F8FD08FFA8F827FD08FF52F852FD05FFF8F852FD07FFCAC2C1C8C1C2CA %FD11FFC1C1C1C8C1C8C1C8FD04FF7DF852FD09FF27F87DFFFFFF27F852FD %0AFF7DF852FD07FFA85227FD06F852FFFFF8F8A8FD07FF7DF8F8FD08FF7D %F87DFD04FFA8F8F8A8FD07FFC9C1C19FC1C1CFFD10FFA7C1C1C19FC2C1C2 %CAFD04FF7DF852FD09FF27F87DFFFFFF27F87DFD0AFF52F827FD05FFA827 %FD05F827F8F8F852FFFFF8F8FD08FFA8F827FD08FF52F852FD05FFF8F8A8 %FD07FFC9C1C1C8C1C8CFFD10FFCFC1C8C1C8C1C8CAFD05FF7DF852FD09FF %27F87DFFFFFF27F852FD0AFF7DF852FD04FFA827F8F852A8A8FFFF7DF8F8 %52FFFFF8F8A8FD07FF7DF8F8FD08FF7DF87DFD04FFA8F8F8A8FD07FFC9C1 %C1C1C2C1CFFD10FFC9FD04C1C9CFFD06FF7DF852FD09FF52F87DFFFFFF52 %F852FD0AFF52F827FD04FF27F827A8FD06FF52F852FFFFF8F8A8FD07FFA8 %F827FD08FF52F852FD05FFF8F8A8FD07FFC8C1C1C2C1C1C9FD10FFC9C1C2 %C9FD09FF7DF852FD09FF27F87DFFFFFF52F8F8FD0AFF27F852FD04FFF8F8 %A8FD07FF52F852FFFFF8F8A8FD07FF7DF8F8FD08FF7DF87DFD04FFA8F8F8 %A8FD07FFC998C1C1C19FC8FD10FFC2C19FCFFD09FF7DF852FD09FF27F87D %FFFFFFA8F8F87DFD08FFA8F8F827FFFFFF7DF827FD08FF52F852FFFFF8F8 %FD08FFA8F827FD08FF52F852FD05FFF8F8A8FD07FFC9C1C1C2C1C8C1CFFD %0EFFCFC2C1C8CAFD09FF7DF852FD09FF27F87DFD04FF52F827A8FD07FF27 %F8F852FFFFFFA8F827FD07FFA8F8F852FFFFF8F8A8FD07FF7DF8F8FD08FF %7DF87DFD04FFA8F8F8A8FD07FFCFFD07C1FD0EFFC9C1C1C1FD0AFF7DF852 %FD09FF27F87DFD04FFA8F8F8277DFD05FF27F8F8F827FFFFFFA8F8F8A8FD %05FFA8F8F8F852FFFFF8F8FD08FFA8F827FD08FF52F852FD05FFF8F8A8FD %08FFC8C1C2FD04C1C2FD0CFFCFC1C2C1C8FD0AFF7DF852FD09FF27F87DFD %05FF7DF8F8F8527D7D52FD05F852FD04FF27F8F87DA8FFA87DFD05F87D7D %F8F8A8FD07FF7DF8F8FD08FF52F852FD04FFA8F8F8A8FD08FFC9C19FC1C1 %C19FC1C2FD0AFFCFC1C19FC1C9FD0AFF7DF852FD09FF27F87DFD06FFA827 %FD06F827A852F827FD05FFFD08F852A827F8F8F87D52FD09FF527DFD08FF %A852A8FD05FF527DFD0AFFC1C1C1C2C1C2C1C1C2CFFD07FFC9FD05C1FD0B %FFA8527DFD09FF7D52A8FD08FFA85252527DA8FFFF7DF852FD06FF5227F8 %272752A8FFFFFF52527DFD29FFC9BAFD08C1C8C9CAC9CAC8C2BAC1C1C1BA %C9FD2AFF27F87DFD09FFA8FD33FFC8C1C2FD0FC1C2C1C8FD2AFFA8F8F8A8 %FD3DFFCAC298C1C1C199C1C1C199C1C1C199C1C1C198C1CAFD2AFF52F827 %FD3FFFCFC8C1C1C1C2C1C1C1C2C1C1C1C2C1C1C1C8CFFD21FF277DFD07FF %7DF8F87DFD40FFCAC8BAFD0BC1BAC2CAFD21FFA8F8F827527D7D7D5227F8 %F852FD43FFCFFD0BC1C9FD24FF7D27FD07F8277DFD46FFC9C89FC1C1C199 %C2C9FD28FFA852522727527DA8FD16FFFF %%EndData endstream endobj 23 0 obj <</Length 26246>>stream
+%AI12_CompressedDatax콇kɒ? 6=l{o[4m4>
+ջ<zZKyzi;߽nB U83a8.1
+V(7+w:_cVr^ZoZ?@yE D|U?uu*/X1
+
+'yOϿ_ww)~/koO+8:Y݀9 T9-BMھ5^RkehQ,AҜŨl]RլQe)UA`>95jyE
+geRh5z]BL^KM:8lךg 2EU`.y 8'
+D:D 3;eml 1:a6`~KO~Uke.?bO5CZS:Z~F;Lzz,6; ]=ʣ kѓ0KGZmуGZn>bI(`4Z-Nz'?OTۅ3,uZz1&8YC7ނjV/7˲Q,kqR i8 ^; 0v%*qT=
+L+W`OxL_dC}vYkV;l(|B\8d\)Z<:Va+C!]^BoV 刼<lҩ÷Ͻ9 ::`Q j*koɗVn+~λ'7(i/OFڅSGluFK[{jެ>ջ5}mC㧯v}-fd]\\&7rf^_TՔxĘ9>YH G??@\_l F!*
+O>% '$'GTd<|
+RI*09YSd5&rNç SK
+p>UrɓQds<|
+b(`s\ 91'5J.
+"|Jy&<pПB,_O`-p | 
+
+%+GʊgX˲Jj>|^>ST4_><X
+A@ւٚ0rC0\dK 6T-F6[7ɏR O?.
+0*˖FLxLuk?gNL绗y<fۘcZT^덪Vku\V7:w\-MtEiNU":2vOk[KZq\
+rasPwڍSH2HaE="2
+#nL.ntGZ%?%n`9[6՘5r
+e%X
+<',m.КjDE+jn?tMPjtI
+ۊ DlGkYVֆ~N2n[ ~LٖvZT:!srZ:ʪH˳
+al
+'eV<&$#%*bZe% R#mVշŢ*9YԆhO& UtW" CNWo]c5k^..F`Kj#B'p@ay$oFF fkg9Yr-T#;26aͿiqFx% D}qw% kS!XVoooEdMMDN O(!ڱG=3e.9(ljbCh&}?|nbg.)g7;dEsjWˣK,N.-9rgFpg 0ų}t>=\K!t؁wT<5.ēƂnE4[4^o_(j{ОKNvaxza<Kr3Wl;NS n:Yomkst{J#4~̌gāG/NWiJrvjc茣QM{U}{bUUkuWK0yT(=|Y%v@?M:T+C у Q?<mjS{ NZzKC'MlnF{MiYiy Lwlt+5YI~uQ7Ns> 97:nԥ%fglcE;{ӂMez1ҹ;GTW,{ՍW[VW+oҜH?E.u#\C+]P7VTPOX۶kz+[[a޳<{s;+i$l'5S1r
+Sϖ'F1TI&GdZC
+z/
+S^>+.o$2y
+XNҴ|:)]|:G٬_9ō~.KzG.Ft
+TJvv`YMyThJɷXBlNvnąOԜxN;;кnyw23WZM7w^¼酶ݩp}ǔN<!,uoS)`EJ Ngk/>NdNh;suKtܺ1mw{rZz1T:{oJH?m0{7Et4u#MKiuN7YNc &Ŝ.:^v&wowg˾nHD?7ybJtowTU<;c[^On73 2m~çcֳ9)J~XKNo,ʜS]"3w/ߏg:-1+ޝnl(EɧSek٣'E.otz\pj9u{NW5ws'zoK0"锛]Kn9;Mk)]g{)ntXmK' .NdKӻ{N/o`~Vx.{3RݩV_O.n3+1*=S7;e:3y=ކN>e ~zjȚSfnw<'V59z
+rTZH \f|׶!ʼ65nK$UصO홉Tzavyp֛y|$_,z=Er5d)ϩis.)~O9/|$/+cssZ=tk!/z?ϖ>ԉϝWNyZ|)^OƮws7&u?} i[\K~]5IϧEr`cpo'<ݷWq1!-ޙ-x={\J%V&==6.ӵvjM!GknM& Lit;N,N.'.OѸ<Hޤzm[rw@zkk79+Y&z#0өL})d̝ojѢnLpmGNDP,|>yصS!ud~ѷSr-(M~N7'DNJԮnyGԇԻ5:l8{gT8x!33SVˍNWwoDg)|:kl'NVTv Yh$>r)j+ ,mϸf]
+ 5~!O^)ZD.'oyn3|}t7O!˟6/lN%r"y,bȽŖgVNޑugƵr6:.%Y.stmm06l]glFyak<ho_?7vkhA:Vq{ʔ/h^0h17pNF=Cא@jsqdA!PyB)&
+^icݯ+
+^|tyʎ>b#_u䟷gvڋ7 |㔼%,_R)XN/]ڱx$ȗ
+t-_1 >? R8< NUC*R-I2=KSQqgXnОT&$ۻ +o #hcX/n8yx)xt~
+p )¶'@џ=[.y2-,7,> :<f9ttT=.82 :g9ttxr3\قCgȡ Π2.8d !.8Nϰ9.8N߭9.8XIC9.KuXgSTGUέ(BXKTь;"thO/(r,yѬ,y<YXϲ47;$=+g$<s.:| !E| q xCO$in ^CYFuCA^R`{slU^4u6P(?{ GU-$FL
+d_L( ɘ c[!C`0As:~eg?ÀxM]grE͗ ~b/[Kp 7\ƞ\zrf&^Ix\ bz_;#f]G/2^,9/槹lV0SQHv^Y+$8 8$A.gD#`H!)+\qHA͎O.9*1|f5$:/Alyd|W~LxȘv5$;ٝ#Ouݑ;^7h6 !vwvt#= -G&<%LC GF@ Șg܅"&,>(9Ǯ vBJFR9Va4Q8rO3jwHh$r607
+hKQh{wspc"ՅN",ݑc׉ü:[h;^.PJ=OͺQ,eQ9T>\-F2Ɲ7"Eh;#npE^qg 3.$jLpVTox{F\D9^ƙm[=)7J9&ec"$[C׻"!/Zc8 ËVN9#팚cO[E谒4E<]1"=0-hoH a"͈-;.ut`m=)VbXNvϖfvYR }b+04-0kly$pihy BUiAퟑz9ۏ1fBcHKF*;)?"Y VHvb!GÌ##5hCO?q,CSC6XKɅe Ѷz22--ŢD8
+,Sᜫr)eU
+7]9N+}׸RW*=rFW jG*kW9~P^8T306.26:SypBߩιD
+ ],& 5BrkHAKTtLo$gC#d7rS4ts{E~asTm細>zzԛ͗nKUnvm@q `Lv>)`kKkd ;\ө4j]a;K};K_\,}|d#uL
+a _sU%p]=#>;3+袻I>'}͝+Ҕ<£85wEEwC*9BD F>窽;<HɯE
+jŖa7D.1߳#N8V`bcO;"Ed/*?`^j4$@[K#l>(?~
+Rݣf\j>Q7uEJdaw"]4<7@ZYlGඎ(r4~!8+ǵ@\ 5ח0qvÝo~u7܍kp|na1MycK2*w^` v]lCr.2 >6pM=sagFO-O>ME5B2ǐX{AjG<状
+vFH5igZl%\/۽be k̠IL2@SXn;/b 46*"$1!Jc
+́Lv_?cV
+8M1̧QVT|:Hᵥzݔ7hkxbxK>)Y<TД):J,dH7 wݣ3\7]SYA:zLaѫ?#ͬil&靭ׅO§ݒvt%ev1mD98ݻ̜:o7nyԴ|8xNJ|ʡg^,AaKLܹԴjƷSt;v/4{ǠkU[f`ö41au W{>M&}SӴ#Ǽ2Wo [Oꔜ EpȔZgg^~H= gŃM_:./;Uw{747Η"[QSS݋Ԣ:t1'yFwKkEgxKyM|rG3An1 q{HS>|d jPs}3Ka*א,?,Q=IGζ3⏧UVV@UYi^F@QZ=!FHu 2 ^6hin~8Let(ƹ1V۾}q <
+[g _ G
+/R7}|$z7U}ߛ0ؽ~d!G/xCŌS^`Kp1g+MFCOc2~dy &Ϧ[t`k{w rG}|#=̛͡gN^V >y/9R3Cz_Ȼ@e7a:oK~zC~W> #u{0do Yi#"",7^`5ǕY?~Nl5ϝ=܊}|c~|
+odxç4~}9O[^F\Y|l{ba|D/lal{㊶*o$2~zWΓ~uYrEG/8!W"x_pqdxӣ8~C0|^|hcAE^``xGaOHh{B\9Ԧ[ ʐN-kƱb˜ AH8<W.[3&7bfv?Fq܋vYL,bV;ۙV<USԹODtbӋV6<ݛӽx[;UՃۭS|z\oof{C~>Z|~4ܞYM'&[V&L.O{yu~?1T͂/MmOprE`JS:,1SfUZum3pO虖kU'dsEcx!?h)&sXdH<>{jźjfs'b Y V8}=6<czzn&qu4,7r͝UAٗ{ ange3_L']8O@jNe+o_>Ј:[sh[e-Ltf~38Y.w=_˕얬m^&[r蕒[l~i%_/gXDyXyP"U]RXD=6~>&{j8LFR'a?OX'sc7ܭ^Yy,=nv ƽk|4Osj>Hê=zY}61ۯƃܛ,eʛ`<%G%K3:LDzg=r<fEL $,M
+5N5u\L;#RFI,~TbƷEޠ[lf2IkAV&*I}:˾:GB\0/H^\W}3@ WZE R7WR/TE[ӿn抩{ .Eç lSp 0ԕIm8h|j ayrx9G~G闹Qy=qj`Ƽ{ &@ w.37P5y{=]ИdOngO'ZX }7} _S{5s=~\^9mYN5e +3i[~^k|$&q"YC U" ]{-(nu1q|z$pwLQ_X'z ?\A/=إ={s=Z|MN~ee`kƐwG~O/o*t=vhvY}HCd\s&|%sd6 <݆۴
+ןsfZ,Z-Z?#cTÉ4J#ϥx>.lKŝBk#ENnI˼ZT* E~~^smqquoLJAD+vPzDs
+HS|>6ۼ-rM޲WcdX.Ϧ`UKqujltktidcXޒQfz!MPtQ<29֛z90V˻Ӆ[D7
+#P4)?f..5c _Tc(IP=_rtzнv2חfN n8!D?bW#/ڤ/)4sGE1{9Yؾvz)mzɪ\;+يOQםM [w`B0C
+脆r
+'Ys:n
+9ԭ-1"Szh}>ʾZhrlixԢ,`~ cUInNI5pK=2) cWL^ou|>e˓¦umB½HdG&7m a 2h@X(Ѽc0q1 KoM s.,(;KZ
+57a!aBDKHغKO~>7^̧5H۽)M|\KFh"rU:4K XA&H`ZUD> k$qRERzXdz`J].̾&\zL3@, ;7Dy&"7ۀE_blW=½<QarN&hPTmV疝ͭф45w^%£ޤ'h,\Ǡ7D@, 4`Ff$0K!\'ƶ=v1pY[˰f[B7}bk>%{O/󣍡.Xy(LTŨ-jzƅ u$21D6I~fdTHoXSvG0$qfAOHg$"iJbz&ɥ';8Bfg{AA>mXʘYMHfrd@ɊDH<̀-fRn=Ygw`Gs<ɼس<!Ɍi=rz:G<`i
+6Zm@YR@K 䜝qvPYFVIxb{oyM,ԢDsfC ZXysN'gVi%G.;{f;9{4]ۼdO{rw =sld{Vlf^.꩕yd/;/Y#$o~Dן*d?ɶkϪʹOAmZO},Z|JeY7~&uh?ח2colP~fE2c4ǥNj|a^IJ ŚSX3d]g
+lq2#~@RgX\/v7Ph3{Ћs+։:nwEJ #4a'7K7Q]"<(On;wN xYkSyH0-HF`Gݟ@cHکXcQWw#`v$Tio`,&,t57[EE*$TOESe`e
+ݰ[S<y[U %儴H WvoQ7OY .9jT:5) 7yMi*dW󧢓JJd7 2k|ZVtN7{QK-jΩM{62,m}(6X"%nć\+i16N? |YNs?D,{%;߽{X!պz[o5ڗ]oKqr< a6<Jv!w{Rb)6j8T<s͗xF-fF~]+v|֟¸2UZoTZS5NZoKV+d[^n9[^dn9Pr Dzwn ]m„nXU,w^֡UQ?|Tw5-cI Jk,1ÌwvLO/[KtJ i77~0%2:ҝVOԞ#t
+E&]VUX0aE>D2XUU3 Eha-L} pw/ c&of24?+ WZ[ViݩFFj%SzC'æ̷
+w(ۧ
+r/ij^<Tz?<;5R|u0
+~W}C9o1
+ZZ@"׻_:ɦ຿AuAO5Bhiȿoh~ڀ
+
+Q{F9vL`kmZ)V;ۀ)_oz}} (oNQZhHq, X+Ex`M^^U-R[`öf\ՠOPl[Z6Z'SX>lc1&rq&Ȫ)EeXU/HU9F8~P}e;&^E'IijJ\4'pr= jSE</9AI"#Ie(ӬVIҼ)ZlZUJ/`À](H|12)b%N"$FH ")T VeY_$hd0%Qfy(P
+'xZd)XҌ$CwQ
+)>`2X\Z$DJdF%$S OGa&X"2mHyp='j *qg҂hx=Z$#C 
+e+<tT
+R6(bI(QE*AW,S^ ȑ{ +Ljc
+#Z,#z XVI3%^Ki (Qh2 tTP(c{L!MdӬRX)$R
+ r2aP` &tbϱvm e9 Zfmi]"D|<
+يx{vh JJaYF0AJ$ EX^- Ń\̂XA `¶7&-`dZ
+1"̊p(<GJ@qђ2A
++)6*Q&̼>Agq)"m%}eXxIۢe@:"Rx?{pTwT`X
+YFe/@JZ,>ReBUڕX"G&%Y¢xDSD8:Ud,΀sK, UTHWFr%n|"j,
+Y`팀EC7(0j sepEQN&D42^A`ǡR=^xp'C (1 +@(!B*9+<DߖҪ "+KP ˆLA1e%tDhKsoT{ȹjQ#ccA
+HVd; H eFW8 pZ$h2lt(PwdM -Q"J(L82 ʺdfi[bH/PCIu-
+H(hdIJ*Sk g*ĸD mJ@'*s: @cW8Y$ KXZJkԪAI*TNy]te@Լw`(#GU[ Nm0xu?J)
+QFa N2tD2(ˉ K!{sTߩS{^@eО@P6/B)h2gX KF@2) .ZXFAPC$ s(aF!A((*K<RF/ )!+7Lx,8jGxɠx3;
+D&2c2HXH%G K!%(m]$\
+bJ(1\DŽT7L8/nEⶑ$!XaXDN2a@{/
+݉ dY걼j<:`DBVaŃ=%E0
+O6/2SFJ"U!
+BTm@ʌrz$%mB^q)a+A,QaC#hKC Vq &RQuNAgYZ&0wH@ Rz.IC#ZbTkZZZ)hK
+t/7.}K,vqgh"JYgn -|;(,.j(Q#% m)IJDR ZvDMFh f V-)F$'L-<'W8^1BK(mQ@1l5%<!|N$`(/Ml.Rۀ{ ˙TA߽o|
+~wICV
+qi7{=C=25_&VQ@'>(?,!
+<C7Gd+R1FއB\l8'`(ԇ
+:iq+eg IKsN6vޑ$KX $
+M#ЀC... KX)z  uNQ&NKoQWyܸڹ޶m(  &(zn(C}P(ftpKZ&i_6A"E]~,\)c XZb
+|շk̺{2|FɳwõtEv2Y~#Ρxئ߈f:MV➸!F=)`b.!%hmNIi 24kXH=qj9&"V$YsGC9exՙLX_T(]-#Jķw6Y BU 'd\]pZj^ԕu19WpWs}[ڮ׾ *wZUJLMpVy a+a+bQZblق%@lUtq: 69$7bTAYU,eBK&{g\3!>V;c'UU:CZ9$D$&$JTVHf-SNA<4$0[V"LqOqciFu>tdy"O^
+)c# 11Kl"0>t7&{!n˱`q2xzq6]3 w0baFZ
+0000000016 00000 n
+0000000144 00000 n
+0000014164 00000 n
+0000000000 00000 f
+0000019272 00000 n
+0000052841 00000 n
+0000014215 00000 n
+0000014580 00000 n
+0000019571 00000 n
+0000019458 00000 n
+0000018892 00000 n
+0000018080 00000 n
+0000018331 00000 n
+0000018379 00000 n
+0000019035 00000 n
+0000019062 00000 n
+0000019157 00000 n
+0000019342 00000 n
+0000019373 00000 n
+0000019644 00000 n
+0000019818 00000 n
+0000020874 00000 n
+0000026543 00000 n
+0000052864 00000 n
+trailer <</Size 25/Root 1 0 R/Info 24 0 R/ID[<628E0D96B8764CF1B62B34BF8A303D42><7E679379DE5E47D8B7B5A8C6AA8AD1BC>]>> startxref 53040 %%EOF \ No newline at end of file
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo.png b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo.png
new file mode 100644
index 00000000000..300bb1ca537
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo.svg b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo.svg
new file mode 100644
index 00000000000..302f718356c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/mroonga-logo.svg
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="247.32675"
+ height="73.113281"
+ id="svg4429"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 7">
+ <defs
+ id="defs4431">
+ <linearGradient
+ gradientTransform="translate(405.03952,235.75369)"
+ id="SVGID_2_"
+ gradientUnits="userSpaceOnUse"
+ x1="223.6167"
+ y1="166.2236"
+ x2="223.6167"
+ y2="228.2177">
+ <stop
+ offset="0"
+ style="stop-color:#F8BB5E"
+ id="stop3925" />
+ <stop
+ offset="1"
+ style="stop-color:#FF931E"
+ id="stop3927" />
+ </linearGradient>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="-17.227243"
+ inkscape:cy="76.496767"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="5"
+ fit-margin-left="5.1"
+ fit-margin-right="5"
+ fit-margin-bottom="5"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2109"
+ inkscape:window-y="329"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata4434">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-519.14376,-395.80554)">
+ <g
+ id="g4460">
+ <path
+ id="path3916"
+ d="m 595.93852,420.61969 c -1.336,-0.291 -2.281,-0.448 -3.442,-0.448 -2.847,0 -5.33,0.831 -7.391,2.466 -0.091,0.062 -0.192,0.14 -0.303,0.247 -0.231,0.196 -0.452,0.406 -0.668,0.621 -1.478,1.313 -2.76,1.951 -2.76,-1.236 v -0.666 c 0,-0.405 -0.327,-0.732 -0.732,-0.732 h -2.101 c -0.198,0 -0.388,0.08 -0.525,0.223 -0.139,0.142 -0.212,0.334 -0.206,0.532 0.105,3.332 0.215,6.777 0.215,9.621 v 18.749 c 0,0.405 0.328,0.732 0.732,0.732 h 2.209 c 0.404,0 0.732,-0.327 0.732,-0.732 v -9.913 c 0,-1.37 0.107,-3.805 0.309,-4.813 1.569,-7.797 4.982,-11.588 10.435,-11.588 0.979,0 1.881,0.164 2.874,0.362 0.048,0.01 0.096,0.015 0.144,0.015 0.148,0 0.294,-0.045 0.417,-0.131 0.163,-0.112 0.273,-0.288 0.306,-0.484 l 0.323,-1.994 c 0.062,-0.383 -0.187,-0.749 -0.568,-0.831 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3918"
+ d="m 684.98552,422.21169 c -2.512,-1.776 -5.292,-2.04 -6.726,-2.04 -2.91,0 -5.648,0.912 -7.822,2.495 -0.005,0.003 -0.01,0.004 -0.014,0.008 -1.004,0.649 -2.627,0.91 -2.618,-0.625 l 0,-0.022 c 0.002,-0.105 -0.003,-0.197 -0.013,-0.276 l -0.008,-0.182 c -0.019,-0.391 -0.34,-0.698 -0.731,-0.698 h -2.102 c -0.203,0 -0.396,0.083 -0.535,0.231 -0.138,0.148 -0.209,0.347 -0.195,0.55 0.152,2.28 0.214,4.171 0.214,6.524 v 21.82 c 0,0.405 0.328,0.732 0.731,0.732 h 2.209 c 0.404,0 0.732,-0.327 0.732,-0.732 v -17.51 c 0,-0.813 0.253,-1.762 0.49,-2.378 0.004,-0.009 0.006,-0.016 0.01,-0.024 1.043,-3.043 4.224,-6.121 8.417,-6.475 3.236,0.002 8.327,2.358 9.024,8.226 0.016,0.15 0.03,0.299 0.042,0.443 0.004,0.049 0.008,0.099 0.011,0.149 0.005,0.07 0.009,0.14 0.014,0.208 0.009,0.192 0.015,0.386 0.015,0.583 0,0.335 0.006,0.637 0.017,0.91 v 15.868 c 0,0.405 0.328,0.732 0.733,0.732 h 2.208 c 0.404,0 0.732,-0.327 0.732,-0.732 v -16.271 c 0,-5.372 -1.627,-9.246 -4.835,-11.514 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3920"
+ d="m 761.20952,447.55369 c -0.133,-0.113 -0.301,-0.173 -0.473,-0.173 -0.04,0 -0.08,0.003 -0.12,0.01 -0.587,0.098 -0.965,0.098 -1.442,0.098 -1.147,0 -2.177,-0.336 -2.177,-4.387 v -9.913 c 0,-2.343 -0.179,-5.368 -1.583,-7.971 -1.822,-3.384 -5.181,-5.1 -9.978,-5.1 -2.475,0 -6.217,0.516 -9.99,2.975 -0.315,0.205 -0.422,0.615 -0.25,0.949 l 0.861,1.669 c 0.098,0.189 0.272,0.326 0.478,0.376 0.058,0.015 0.116,0.021 0.173,0.021 0.151,0 0.299,-0.046 0.424,-0.135 2.314,-1.639 5.186,-2.506 8.305,-2.506 3.995,0 6.004,1.854 7,3.94 v 0 c 1.004,2.22 -0.561,4.403 -2.285,4.584 -0.085,0.004 -0.17,0.008 -0.254,0.013 -0.014,-0.001 -0.028,10e-4 -0.042,0 -0.055,-0.001 -0.101,10e-4 -0.143,0.009 -10.978,0.626 -16.538,4.313 -16.538,10.98 0,2.028 0.803,4.057 2.202,5.568 1.233,1.334 3.56,2.922 7.635,2.922 3.471,0 6.271,-1.111 8.325,-2.472 0.061,-0.032 0.13,-0.077 0.211,-0.141 0.023,-0.018 0.042,-0.031 0.064,-0.048 0.128,-0.089 0.256,-0.177 0.378,-0.268 1.275,-0.824 1.969,-0.351 2.623,0.503 0.002,0.003 0.006,0.005 0.008,0.008 0.84,1.141 2.161,1.717 3.962,1.717 0.846,0 1.591,-0.093 2.344,-0.293 0.32,-0.085 0.543,-0.375 0.543,-0.708 v -1.669 c -0.002,-0.214 -0.097,-0.419 -0.261,-0.558 z m -18.252,0.527 c -3.042,0 -6.111,-1.722 -6.111,-5.572 0,-4.862 5.936,-6.58 12.167,-7.043 2.32,-0.043 4.097,2.161 4.311,4.575 v 0.989 c -0.062,0.647 -0.246,1.289 -0.568,1.888 -0.162,0.265 -0.356,0.557 -0.589,0.865 -0.04,0.048 -0.075,0.093 -0.103,0.133 -1.442,1.847 -4.223,4.165 -9.107,4.165 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3922"
+ d="m 725.07752,420.87269 h -2.047 c -0.393,0 -0.716,0.31 -0.731,0.703 -0.066,1.619 -0.892,1.836 -2.703,0.765 -0.009,-0.005 -0.018,-0.013 -0.027,-0.02 -0.178,-0.118 -0.362,-0.23 -0.546,-0.337 -0.011,-0.008 -0.02,-0.014 -0.031,-0.021 -0.087,-0.057 -0.168,-0.103 -0.244,-0.139 -1.982,-1.096 -4.307,-1.65 -6.924,-1.65 -3.739,0 -7.429,1.548 -10.122,4.249 -2.082,2.087 -4.564,5.827 -4.564,11.784 0,3.818 1.346,7.413 3.792,10.121 2.595,2.875 6.234,4.458 10.248,4.458 3.111,0 5.514,-0.819 7.335,-1.896 0.004,-0.002 0.007,-0.003 0.012,-0.005 1.954,-0.875 3.603,-2.184 3.135,2.233 -0.875,5.677 -4.519,8.764 -10.482,8.764 -4.175,0 -7.31,-1.405 -9.204,-2.584 -0.118,-0.073 -0.251,-0.109 -0.387,-0.109 -0.06,0 -0.122,0.006 -0.182,0.023 -0.193,0.049 -0.358,0.176 -0.456,0.35 l -0.97,1.724 c -0.189,0.336 -0.085,0.76 0.237,0.972 2.853,1.862 6.91,2.974 10.854,2.974 2.689,0 9.293,-0.625 12.493,-6.412 1.385,-2.481 2.031,-5.968 2.031,-10.969 v -17.456 c 0,-2.474 0.07,-4.678 0.214,-6.738 0.014,-0.203 -0.057,-0.401 -0.195,-0.55 -0.139,-0.151 -0.332,-0.234 -0.536,-0.234 z m -16.892,26.027 c -0.605,-0.24 -7.609,-3.216 -7.013,-11.812 0.551,-7.939 5.716,-10.454 6.517,-10.798 1.278,-0.5 2.701,-0.768 4.242,-0.768 1.665,0 3.113,0.327 4.354,0.873 6.845,3.871 7.696,16.452 0.884,21.467 -1.592,1 -3.436,1.572 -5.345,1.572 -1.343,0 -2.553,-0.192 -3.639,-0.534 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3929"
+ d="m 626.30252,406.64669 c -2.337,2.337 -4.109,5.343 -5.032,8.655 -6.076,-0.094 -12.171,2.272 -17.007,7.11 -8.142,8.141 -10.674,22.912 0.667,34.253 9.542,9.543 23.793,9.687 33.145,0.334 4.421,-4.424 7.236,-10.507 7.297,-16.941 3.14,-0.836 6.125,-2.507 8.67,-5.049 6.956,-6.959 9.086,-18.919 -0.311,-28.318 -7.833,-7.831 -19.626,-7.848 -27.429,-0.044 z m 25.622,20.423 c -1.335,1.336 -3.045,2.324 -4.932,2.862 -3.027,0.868 -4.657,0.778 -5.698,2.515 -1.041,1.736 -0.926,1.941 -0.88,5.194 0.056,3.689 -1.333,7.514 -4.813,10.996 -5.32,5.318 -15.14,6.504 -22.366,-0.722 -5.693,-5.695 -8.365,-15.278 -0.778,-22.865 2.816,-2.813 6.866,-4.477 11.124,-4.401 2.961,0.051 3.306,0.16 5.212,-0.704 1.909,-0.87 1.082,-2.8 2.222,-6.205 0.556,-1.66 1.545,-3.295 3.092,-4.844 4.126,-4.126 11.721,-5.072 17.285,0.49 4.533,4.535 6.455,11.76 0.532,17.684 z"
+ inkscape:connector-curvature="0"
+ style="fill:url(#SVGID_2_)" />
+ <path
+ id="path3965"
+ d="m 557.08552,420.17169 c -3.604,0 -5.78,1.055 -7.803,2.675 0,10e-4 -10e-4,10e-4 -10e-4,0.002 -2.14,1.482 -3.267,1.373 -5.033,0.007 -0.008,-0.007 -0.015,-0.009 -0.023,-0.015 -1.786,-1.704 -4.111,-2.669 -6.696,-2.669 -2.797,0 -5.133,0.737 -7.113,2.275 -0.042,0.029 -0.085,0.062 -0.129,0.102 -0.878,0.785 -2.285,1.01 -2.444,-0.238 l -0.015,-0.316 c 0,-0.087 -0.002,-0.167 -0.011,-0.234 l -0.009,-0.19 c -0.019,-0.391 -0.341,-0.697 -0.732,-0.697 h -2.1 c -0.204,0 -0.397,0.083 -0.536,0.232 -0.139,0.149 -0.209,0.347 -0.195,0.55 0.148,2.118 0.214,4.13 0.214,6.523 v 21.82 c 0,0.405 0.328,0.732 0.732,0.732 h 2.209 c 0.404,0 0.732,-0.327 0.732,-0.732 v -17.457 c 0,-1.086 0.264,-1.975 0.486,-2.529 1.183,-3.106 4.304,-6.436 8.371,-6.436 4.432,0 7.296,3.476 7.296,8.857 v 17.564 c 0,0.405 0.328,0.732 0.732,0.732 h 2.21 c 0.404,0 0.732,-0.327 0.732,-0.732 v -18.317 c 0,-1.093 0.288,-1.956 0.54,-2.585 1.117,-2.651 4.065,-5.519 7.887,-5.519 4.983,0 7.727,3.681 7.727,10.366 v 16.055 c 0,0.405 0.328,0.732 0.732,0.732 h 2.209 c 0.403,0 0.731,-0.327 0.731,-0.732 v -16.001 c 0,-12.828 -8.189,-13.825 -10.7,-13.825 z"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-foreground-white.png b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-foreground-white.png
new file mode 100644
index 00000000000..200af027c95
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-foreground-white.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-foreground-white.svg b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-foreground-white.svg
new file mode 100644
index 00000000000..bd9b8f5de35
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-foreground-white.svg
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="60.000706"
+ height="60.001022"
+ id="svg5730"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 53">
+ <defs
+ id="defs5732">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_11_"
+ id="linearGradient5525"
+ gradientUnits="userSpaceOnUse"
+ x1="630.15039"
+ y1="437.05859"
+ x2="630.15039"
+ y2="497.05859" />
+ <linearGradient
+ id="SVGID_11_"
+ gradientUnits="userSpaceOnUse"
+ x1="630.15039"
+ y1="437.05859"
+ x2="630.15039"
+ y2="497.05859">
+ <stop
+ offset="0"
+ style="stop-color:#8CC84B"
+ id="stop4064" />
+ <stop
+ offset="1"
+ style="stop-color:#6BA025"
+ id="stop4066" />
+ </linearGradient>
+ <linearGradient
+ y2="497.05859"
+ x2="630.15039"
+ y1="437.05859"
+ x1="630.15039"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5728"
+ xlink:href="#SVGID_11_"
+ inkscape:collect="always" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="153.57178"
+ inkscape:cy="-47.142348"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="1928"
+ inkscape:window-y="52"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata5735">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-221.42822,-405.21881)">
+ <polygon
+ transform="translate(-378.72178,-31.839165)"
+ points="600.15,437.059 600.15,497.059 626.012,497.059 660.15,497.059 660.15,462.92 660.15,437.059 "
+ id="polygon4068"
+ style="fill:url(#linearGradient5728)" />
+ <path
+ d="m 274.96222,442.10183 c 7.502,-7.505 9.8,-20.401 -0.335,-30.538 -4.224,-4.222 -9.514,-6.337 -14.8,-6.345 h -0.005 c -5.286,-0.007 -10.567,2.093 -14.774,6.299 -2.522,2.52 -4.434,5.76 -5.428,9.33 -6.49,-0.099 -13.001,2.411 -18.191,7.53 v 36.479 c 0.114,0.121 0.227,0.242 0.344,0.361 h 25.518 11.027 c 4.434,-4.717 7.233,-11.018 7.295,-17.672 3.385,-0.898 6.606,-2.7 9.349,-5.444 z m -13.748,-2.759 c -1.121,1.873 -0.997,2.089 -0.949,5.602 0.062,3.979 -1.437,8.102 -5.189,11.854 -5.736,5.737 -16.326,7.017 -24.119,-0.776 -6.139,-6.14 -9.021,-16.475 -0.84,-24.658 3.038,-3.034 7.406,-4.827 11.998,-4.745 3.193,0.055 3.564,0.174 5.621,-0.76 2.058,-0.938 1.166,-3.019 2.396,-6.692 0.6,-1.792 1.666,-3.551 3.332,-5.224 4.451,-4.448 12.643,-5.469 18.643,0.531 4.887,4.887 6.96,12.677 0.572,19.065 -1.441,1.444 -3.284,2.509 -5.319,3.087 -3.266,0.937 -5.023,0.838 -6.146,2.716 z"
+ id="path4111"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-full-size.png b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-full-size.png
new file mode 100644
index 00000000000..19af1caf322
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-full-size.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-full-size.svg b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-full-size.svg
new file mode 100644
index 00000000000..fa72e426df5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon-full-size.svg
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="199.804"
+ height="200.85701"
+ id="svg4522"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="nroonga-icon-full-size.svg"
+ inkscape:export-filename="nroonga-icon-full-size.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs4524">
+ <linearGradient
+ gradientTransform="translate(496.09341,431.09054)"
+ id="SVGID_3_"
+ gradientUnits="userSpaceOnUse"
+ x1="223.6167"
+ y1="254.24409"
+ x2="223.6167"
+ y2="319.0174">
+ <stop
+ offset="0"
+ style="stop-color:#8CC84B"
+ id="stop3942" />
+ <stop
+ offset="1"
+ style="stop-color:#6BA025"
+ id="stop3944" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_3_"
+ id="linearGradient4561"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(496.09341,431.09054)"
+ x1="223.6167"
+ y1="254.24409"
+ x2="223.6167"
+ y2="319.0174" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_3_"
+ id="linearGradient3123"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(3.0240925,0,0,3.0240925,43.472464,-149.79555)"
+ x1="223.6167"
+ y1="254.24409"
+ x2="223.6167"
+ y2="319.0174" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.9899495"
+ inkscape:cx="143.90298"
+ inkscape:cy="78.573219"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="5"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2200"
+ inkscape:window-y="217"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata4527">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-619.80813,-617.64797)">
+ <path
+ style="fill:url(#linearGradient3123)"
+ inkscape:connector-curvature="0"
+ d="m 712.59225,640.31214 c -7.0673,7.06428 -12.426,16.1547 -15.21723,26.17352 -18.37439,-0.28427 -36.80623,6.87376 -51.43074,21.49827 -24.62217,24.61611 -32.27917,69.28801 2.01707,103.58424 28.85589,28.85589 71.95223,29.29741 100.23354,1.00703 13.36952,-13.37557 21.88234,-31.7681 22.06681,-51.22813 9.49565,-2.52814 18.52256,-7.58443 26.21888,-15.26562 21.03559,-21.04769 27.4769,-57.21281 -0.94049,-85.63625 -23.68772,-23.68167 -59.35084,-23.73308 -82.94784,-0.13306 z m 77.4833,61.75499 c -4.03716,4.04321 -9.20836,7.02799 -14.91482,8.65495 -9.15393,2.62189 -14.0832,2.35577 -17.23128,7.60862 -3.14808,5.2468 -2.80031,5.86674 -2.66121,15.70714 0.16935,11.15285 -4.03111,22.72 -14.55495,33.24989 -16.08817,16.08213 -45.78476,19.66568 -67.63686,-2.18339 -17.21616,-17.21918 -25.29653,-46.20209 -2.35274,-69.14285 8.51584,-8.50678 20.76342,-13.53887 33.64001,-13.31206 8.95433,0.15725 9.99765,0.4899 15.76157,-2.12594 5.77299,-2.63096 3.27206,-8.47048 6.71953,-18.76449 1.68139,-5.01999 4.67222,-9.96741 9.35049,-14.65173 12.47741,-12.47438 35.44539,-15.3382 52.27144,1.48181 13.70821,13.72031 19.52052,35.56333 1.60882,53.47805 z"
+ id="path3946" />
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon.png b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon.png
new file mode 100644
index 00000000000..4a851d78640
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon.svg b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon.svg
new file mode 100644
index 00000000000..5492d40f6bc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-icon.svg
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="59.99931"
+ height="59.999016"
+ id="svg5211"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 35">
+ <defs
+ id="defs5213">
+ <linearGradient
+ id="SVGID_15_"
+ gradientUnits="userSpaceOnUse"
+ x1="307.7168"
+ y1="437.05859"
+ x2="307.7168"
+ y2="505.11819">
+ <stop
+ offset="0"
+ style="stop-color:#8CC84B"
+ id="stop4094" />
+ <stop
+ offset="1"
+ style="stop-color:#6BA025"
+ id="stop4096" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="translate(-104.85951,28.161837)"
+ y2="505.11819"
+ x2="307.7168"
+ y1="437.05859"
+ x1="307.7168"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5209"
+ xlink:href="#SVGID_15_"
+ inkscape:collect="always" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="100.06073"
+ inkscape:cy="2.7065346"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2484"
+ inkscape:window-y="361"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata5216">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-172.85749,-465.21981)">
+ <path
+ style="fill:url(#linearGradient5209)"
+ inkscape:connector-curvature="0"
+ d="m 226.39049,502.10283 c 7.502,-7.505 9.799,-20.401 -0.335,-30.538 -4.223,-4.222 -9.513,-6.337 -14.799,-6.345 h -0.005 c -5.286,-0.007 -10.568,2.093 -14.775,6.299 -2.522,2.52 -4.433,5.76 -5.428,9.33 -6.49,-0.099 -13.001,2.411 -18.191,7.53 v 36.48 c 0.114,0.12 0.227,0.241 0.344,0.36 h 25.518 11.027 c 4.434,-4.717 7.234,-11.018 7.295,-17.672 3.385,-0.898 6.606,-2.7 9.349,-5.444 z m -13.748,-2.759 c -1.122,1.873 -0.998,2.089 -0.95,5.602 0.062,3.979 -1.436,8.102 -5.188,11.854 -5.737,5.737 -16.327,7.017 -24.119,-0.776 -6.14,-6.14 -9.021,-16.475 -0.84,-24.658 3.038,-3.034 7.405,-4.827 11.998,-4.745 3.193,0.055 3.564,0.174 5.621,-0.76 2.058,-0.938 1.166,-3.019 2.396,-6.692 0.6,-1.792 1.667,-3.551 3.333,-5.224 4.451,-4.448 12.642,-5.469 18.642,0.531 4.887,4.887 6.96,12.677 0.573,19.065 -1.442,1.444 -3.285,2.509 -5.32,3.087 -3.265,0.937 -5.023,0.838 -6.146,2.716 z"
+ id="path4098" />
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo-foreground-white.png b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo-foreground-white.png
new file mode 100644
index 00000000000..9813997cce6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo-foreground-white.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo-foreground-white.svg b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo-foreground-white.svg
new file mode 100644
index 00000000000..53376b4b55d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo-foreground-white.svg
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="229.3342"
+ height="73.11216"
+ id="svg4859"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 22">
+ <defs
+ id="defs4861">
+ <linearGradient
+ gradientTransform="translate(-243.93947,236.80482)"
+ id="SVGID_7_"
+ gradientUnits="userSpaceOnUse"
+ x1="589.36517"
+ y1="253"
+ x2="589.36517"
+ y2="319.03049">
+ <stop
+ offset="0"
+ style="stop-color:#8CC84B"
+ id="stop4010" />
+ <stop
+ offset="1"
+ style="stop-color:#6BA025"
+ id="stop4012" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_7_"
+ id="linearGradient4898"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-243.93947,236.80482)"
+ x1="589.36517"
+ y1="253"
+ x2="589.36517"
+ y2="319.03049" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#000000"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="121.09567"
+ inkscape:cy="47.984651"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-bottom="5"
+ fit-margin-right="5"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2267"
+ inkscape:window-y="391"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata4864">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-253.90433,-487.23467)">
+ <g
+ id="g4890">
+ <path
+ id="path3999"
+ d="m 312.70853,512.04782 c -1.337,-0.292 -2.28,-0.447 -3.441,-0.447 -2.848,0 -5.33,0.83 -7.393,2.465 -0.09,0.061 -0.191,0.141 -0.302,0.247 -0.23,0.197 -0.452,0.406 -0.669,0.622 -1.477,1.313 -2.759,1.949 -2.759,-1.236 v -0.667 c 0,-0.403 -0.327,-0.73 -0.732,-0.73 h -2.1 c -0.2,0 -0.389,0.079 -0.527,0.222 -0.138,0.142 -0.212,0.333 -0.206,0.532 0.106,3.332 0.215,6.777 0.215,9.622 v 18.75 c 0,0.402 0.328,0.732 0.732,0.732 h 2.209 c 0.403,0 0.732,-0.33 0.732,-0.732 v -9.914 c 0,-1.372 0.107,-3.806 0.31,-4.813 1.568,-7.798 4.982,-11.589 10.435,-11.589 0.979,0 1.88,0.163 2.874,0.363 0.047,0.01 0.095,0.014 0.143,0.014 0.148,0 0.294,-0.043 0.417,-0.13 0.162,-0.114 0.273,-0.289 0.307,-0.484 l 0.322,-1.995 c 0.062,-0.383 -0.187,-0.751 -0.567,-0.832 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path4001"
+ d="m 401.75553,513.63982 c -2.513,-1.775 -5.294,-2.04 -6.727,-2.04 -2.911,0 -5.648,0.911 -7.821,2.494 -0.005,0.003 -0.011,0.005 -0.015,0.009 -1.006,0.647 -2.627,0.908 -2.617,-0.625 v -0.022 c 0,-0.106 -0.005,-0.198 -0.014,-0.276 l -0.009,-0.182 c -0.018,-0.391 -0.341,-0.697 -0.731,-0.697 h -2.102 c -0.202,0 -0.396,0.083 -0.534,0.231 -0.139,0.146 -0.21,0.346 -0.195,0.548 0.152,2.281 0.212,4.171 0.212,6.524 v 21.821 c 0,0.402 0.33,0.732 0.733,0.732 h 2.209 c 0.404,0 0.731,-0.33 0.731,-0.732 v -17.511 c 0,-0.813 0.253,-1.763 0.49,-2.378 0.005,-0.009 0.007,-0.016 0.01,-0.024 1.044,-3.044 4.223,-6.12 8.417,-6.475 3.236,0.002 8.326,2.359 9.024,8.225 0.016,0.149 0.03,0.298 0.041,0.443 0.005,0.05 0.008,0.098 0.013,0.148 0.005,0.073 0.009,0.142 0.014,0.21 0.009,0.189 0.015,0.384 0.015,0.583 0,0.334 0.005,0.638 0.018,0.91 v 15.87 c 0,0.402 0.326,0.732 0.73,0.732 h 2.21 c 0.403,0 0.732,-0.33 0.732,-0.732 v -16.271 c 10e-4,-5.373 -1.627,-9.248 -4.834,-11.515 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path4003"
+ d="m 279.67053,513.63982 c -2.512,-1.775 -5.293,-2.04 -6.726,-2.04 -2.91,0 -5.649,0.911 -7.822,2.494 -0.006,0.003 -0.009,0.005 -0.015,0.009 -1.005,0.647 -2.627,0.908 -2.617,-0.625 l -10e-4,-0.022 c 0.002,-0.106 -0.002,-0.198 -0.013,-0.276 l -0.007,-0.182 c -0.021,-0.391 -0.342,-0.697 -0.732,-0.697 h -2.101 c -0.204,0 -0.397,0.083 -0.535,0.231 -0.139,0.146 -0.211,0.346 -0.195,0.548 0.151,2.281 0.215,4.171 0.215,6.524 v 21.821 c 0,0.402 0.327,0.732 0.729,0.732 h 2.21 c 0.403,0 0.732,-0.33 0.732,-0.732 v -17.511 c 0,-0.813 0.253,-1.763 0.489,-2.378 0.004,-0.009 0.007,-0.016 0.009,-0.024 1.044,-3.044 4.224,-6.12 8.418,-6.475 3.236,0.002 8.325,2.359 9.023,8.225 0.017,0.149 0.03,0.298 0.043,0.443 0.004,0.05 0.008,0.098 0.012,0.148 0.004,0.073 0.01,0.142 0.014,0.21 0.008,0.189 0.015,0.384 0.015,0.583 0,0.334 0.005,0.638 0.018,0.91 v 15.87 c 0,0.402 0.327,0.732 0.73,0.732 h 2.209 c 0.404,0 0.733,-0.33 0.733,-0.732 v -16.271 c 10e-4,-5.373 -1.627,-9.248 -4.835,-11.515 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path4005"
+ d="m 477.97953,538.98182 c -0.133,-0.113 -0.301,-0.174 -0.474,-0.174 -0.039,0 -0.079,0.004 -0.118,0.011 -0.588,0.099 -0.964,0.099 -1.444,0.099 -1.147,0 -2.177,-0.337 -2.177,-4.387 v -9.914 c 0,-2.344 -0.18,-5.368 -1.583,-7.971 -1.824,-3.384 -5.181,-5.101 -9.979,-5.101 -2.475,0 -6.218,0.516 -9.99,2.975 -0.313,0.204 -0.424,0.615 -0.252,0.949 l 0.862,1.671 c 0.098,0.188 0.272,0.324 0.479,0.375 0.058,0.013 0.116,0.021 0.174,0.021 0.15,0 0.296,-0.048 0.422,-0.134 2.314,-1.64 5.188,-2.507 8.306,-2.507 3.995,0 6.004,1.856 7,3.942 l 0,0 c 1.002,2.219 -0.563,4.403 -2.286,4.584 -0.084,0.004 -0.169,0.007 -0.253,0.012 -0.015,0 -0.027,10e-4 -0.043,0 -0.055,-0.002 -0.101,0.002 -0.143,0.009 -10.979,0.627 -16.537,4.313 -16.537,10.979 0,2.029 0.801,4.056 2.201,5.57 1.235,1.332 3.56,2.922 7.637,2.922 3.471,0 6.27,-1.112 8.324,-2.474 0.061,-0.03 0.131,-0.076 0.211,-0.141 0.021,-0.017 0.044,-0.03 0.064,-0.047 0.127,-0.091 0.257,-0.177 0.377,-0.27 1.277,-0.824 1.97,-0.35 2.623,0.505 0.002,10e-4 0.006,0.004 0.008,0.008 0.839,1.141 2.162,1.717 3.963,1.717 0.847,0 1.59,-0.094 2.343,-0.293 0.322,-0.086 0.544,-0.376 0.544,-0.708 v -1.67 c 0,-0.214 -0.096,-0.419 -0.259,-0.558 z m -18.252,0.528 c -3.042,0 -6.111,-1.724 -6.111,-5.573 0,-4.863 5.936,-6.58 12.166,-7.043 2.32,-0.043 4.098,2.161 4.313,4.575 v 0.989 c -0.063,0.646 -0.246,1.289 -0.569,1.888 -0.161,0.265 -0.354,0.556 -0.588,0.864 -0.04,0.049 -0.076,0.094 -0.104,0.135 -1.442,1.847 -4.223,4.165 -9.107,4.165 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path4007"
+ d="m 441.84653,512.30182 h -2.047 c -0.392,0 -0.716,0.31 -0.731,0.702 -0.064,1.618 -0.892,1.835 -2.701,0.766 -0.011,-0.006 -0.02,-0.014 -0.028,-0.019 -0.179,-0.121 -0.362,-0.23 -0.547,-0.339 -0.01,-0.009 -0.02,-0.014 -0.031,-0.021 -0.087,-0.057 -0.168,-0.102 -0.243,-0.139 -1.982,-1.096 -4.308,-1.65 -6.925,-1.65 -3.739,0 -7.428,1.547 -10.121,4.249 -2.082,2.087 -4.564,5.826 -4.564,11.783 0,3.819 1.347,7.415 3.791,10.124 2.597,2.872 6.233,4.456 10.249,4.456 3.11,0 5.514,-0.82 7.335,-1.896 0.004,-0.002 0.007,-0.004 0.012,-0.005 1.953,-0.873 3.602,-2.182 3.134,2.234 -0.875,5.678 -4.518,8.765 -10.48,8.765 -4.176,0 -7.31,-1.406 -9.204,-2.584 -0.116,-0.074 -0.251,-0.11 -0.388,-0.11 -0.06,0 -0.123,0.007 -0.182,0.021 -0.192,0.051 -0.356,0.176 -0.456,0.351 l -0.968,1.725 c -0.19,0.335 -0.086,0.761 0.237,0.972 2.853,1.862 6.909,2.975 10.852,2.975 2.689,0 9.294,-0.624 12.493,-6.413 1.385,-2.48 2.031,-5.968 2.031,-10.968 v -17.456 c 0,-2.475 0.07,-4.678 0.216,-6.738 0.013,-0.202 -0.058,-0.402 -0.196,-0.55 -0.138,-0.148 -0.334,-0.235 -0.538,-0.235 z m -16.892,26.024 c -0.604,-0.238 -7.607,-3.214 -7.011,-11.809 0.549,-7.941 5.715,-10.457 6.518,-10.798 1.276,-0.499 2.698,-0.77 4.24,-0.77 1.663,0 3.112,0.328 4.354,0.875 6.845,3.87 7.696,16.453 0.883,21.465 -1.592,1 -3.437,1.574 -5.346,1.574 -1.341,0 -2.551,-0.195 -3.638,-0.537 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path4014"
+ d="m 343.07253,498.07582 c -2.338,2.336 -4.11,5.342 -5.031,8.655 -6.076,-0.094 -12.172,2.273 -17.008,7.109 -8.143,8.14 -10.674,22.912 0.668,34.253 9.542,9.542 23.793,9.688 33.144,0.333 4.422,-4.423 7.237,-10.505 7.298,-16.94 3.14,-0.836 6.125,-2.508 8.67,-5.048 6.957,-6.96 9.085,-18.919 -0.311,-28.318 -7.835,-7.831 -19.628,-7.848 -27.43,-0.044 z m 25.622,20.421 c -1.335,1.337 -3.046,2.324 -4.934,2.862 -3.026,0.867 -4.655,0.779 -5.696,2.516 -1.042,1.735 -0.925,1.94 -0.881,5.194 0.056,3.688 -1.332,7.513 -4.813,10.995 -5.319,5.318 -15.139,6.503 -22.366,-0.722 -5.692,-5.694 -8.363,-15.278 -0.776,-22.864 2.816,-2.813 6.867,-4.477 11.123,-4.402 2.961,0.052 3.306,0.162 5.213,-0.703 1.907,-0.87 1.082,-2.801 2.222,-6.205 0.557,-1.66 1.544,-3.296 3.093,-4.845 4.125,-4.125 11.721,-5.072 17.284,0.49 4.532,4.537 6.456,11.76 0.531,17.684 z"
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient4898)" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo.png b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo.png
new file mode 100644
index 00000000000..d5362b91c39
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo.svg b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo.svg
new file mode 100644
index 00000000000..7b0b870b43b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/nroonga-logo.svg
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="229.33453"
+ height="73.012161"
+ id="svg4522"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 10">
+ <defs
+ id="defs4524">
+ <linearGradient
+ gradientTransform="translate(496.09341,431.09054)"
+ id="SVGID_3_"
+ gradientUnits="userSpaceOnUse"
+ x1="223.6167"
+ y1="254.24409"
+ x2="223.6167"
+ y2="319.0174">
+ <stop
+ offset="0"
+ style="stop-color:#8CC84B"
+ id="stop3942" />
+ <stop
+ offset="1"
+ style="stop-color:#6BA025"
+ id="stop3944" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_3_"
+ id="linearGradient4561"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(496.09341,431.09054)"
+ x1="223.6167"
+ y1="254.24409"
+ x2="223.6167"
+ y2="319.0174" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="101.4407"
+ inkscape:cy="55.868778"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-right="5"
+ fit-margin-bottom="4.9"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="1920"
+ inkscape:window-y="260"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata4527">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-628.18988,-681.52039)">
+ <g
+ id="g4553">
+ <path
+ id="path3931"
+ d="m 686.99241,706.33354 c -1.336,-0.292 -2.281,-0.447 -3.442,-0.447 -2.847,0 -5.33,0.83 -7.391,2.465 -0.091,0.061 -0.192,0.141 -0.303,0.247 -0.231,0.197 -0.452,0.406 -0.668,0.622 -1.478,1.313 -2.76,1.949 -2.76,-1.236 v -0.667 c 0,-0.403 -0.327,-0.73 -0.732,-0.73 h -2.101 c -0.198,0 -0.388,0.079 -0.525,0.222 -0.139,0.142 -0.212,0.333 -0.206,0.532 0.105,3.332 0.215,6.777 0.215,9.622 v 18.75 c 0,0.402 0.328,0.732 0.732,0.732 h 2.209 c 0.404,0 0.732,-0.33 0.732,-0.732 v -9.914 c 0,-1.372 0.107,-3.806 0.309,-4.813 1.569,-7.798 4.982,-11.589 10.435,-11.589 0.979,0 1.881,0.163 2.874,0.363 0.048,0.01 0.096,0.014 0.144,0.014 0.148,0 0.294,-0.043 0.417,-0.13 0.163,-0.114 0.273,-0.289 0.306,-0.484 l 0.323,-1.995 c 0.062,-0.383 -0.187,-0.751 -0.568,-0.832 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3933"
+ d="m 776.03941,707.92554 c -2.512,-1.775 -5.292,-2.04 -6.726,-2.04 -2.91,0 -5.648,0.911 -7.822,2.494 -0.005,0.003 -0.01,0.005 -0.014,0.009 -1.004,0.647 -2.627,0.908 -2.618,-0.625 l 0,-0.022 c 0.002,-0.106 -0.003,-0.198 -0.013,-0.276 l -0.008,-0.182 c -0.019,-0.391 -0.34,-0.697 -0.731,-0.697 h -2.102 c -0.203,0 -0.396,0.083 -0.535,0.231 -0.138,0.146 -0.209,0.346 -0.195,0.548 0.152,2.281 0.214,4.171 0.214,6.524 v 21.821 c 0,0.402 0.328,0.732 0.731,0.732 h 2.209 c 0.404,0 0.732,-0.33 0.732,-0.732 v -17.511 c 0,-0.813 0.253,-1.763 0.49,-2.378 0.004,-0.009 0.006,-0.016 0.01,-0.024 1.043,-3.044 4.224,-6.12 8.417,-6.475 3.236,0.002 8.327,2.359 9.024,8.225 0.016,0.149 0.03,0.298 0.042,0.443 0.004,0.05 0.008,0.098 0.011,0.148 0.005,0.073 0.009,0.142 0.014,0.21 0.009,0.189 0.015,0.384 0.015,0.583 0,0.334 0.006,0.638 0.017,0.91 v 15.87 c 0,0.402 0.328,0.732 0.733,0.732 h 2.208 c 0.404,0 0.732,-0.33 0.732,-0.732 v -16.271 c 0,-5.373 -1.627,-9.248 -4.835,-11.515 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3935"
+ d="m 653.95541,707.92554 c -2.512,-1.775 -5.292,-2.04 -6.726,-2.04 -2.91,0 -5.649,0.911 -7.821,2.494 -0.006,0.003 -0.01,0.005 -0.016,0.009 -1.003,0.647 -2.626,0.908 -2.616,-0.625 l -0.001,-0.022 c 0.001,-0.106 -0.002,-0.198 -0.013,-0.276 l -0.008,-0.182 c -0.018,-0.391 -0.34,-0.697 -0.731,-0.697 h -2.102 c -0.202,0 -0.396,0.083 -0.534,0.231 -0.139,0.146 -0.209,0.346 -0.196,0.548 0.152,2.281 0.213,4.171 0.213,6.524 v 21.821 c 0,0.402 0.328,0.732 0.732,0.732 h 2.209 c 0.404,0 0.732,-0.33 0.732,-0.732 v -17.511 c 0,-0.813 0.252,-1.763 0.489,-2.378 0.003,-0.009 0.007,-0.016 0.01,-0.024 1.043,-3.044 4.224,-6.12 8.417,-6.475 3.236,0.002 8.326,2.359 9.024,8.225 0.017,0.149 0.031,0.298 0.042,0.443 0.004,0.05 0.009,0.098 0.012,0.148 0.005,0.073 0.01,0.142 0.014,0.21 0.009,0.189 0.015,0.384 0.015,0.583 0,0.334 0.006,0.638 0.018,0.91 v 15.87 c 0,0.402 0.327,0.732 0.732,0.732 h 2.208 c 0.405,0 0.732,-0.33 0.732,-0.732 v -16.271 c 0,-5.373 -1.627,-9.248 -4.835,-11.515 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3937"
+ d="m 852.26341,733.26754 c -0.133,-0.113 -0.301,-0.174 -0.473,-0.174 -0.04,0 -0.08,0.004 -0.12,0.011 -0.587,0.099 -0.965,0.099 -1.442,0.099 -1.147,0 -2.177,-0.337 -2.177,-4.387 v -9.914 c 0,-2.344 -0.179,-5.368 -1.583,-7.971 -1.822,-3.384 -5.181,-5.101 -9.978,-5.101 -2.475,0 -6.217,0.516 -9.99,2.975 -0.315,0.204 -0.422,0.615 -0.25,0.949 l 0.861,1.671 c 0.098,0.188 0.272,0.324 0.478,0.375 0.058,0.013 0.116,0.021 0.173,0.021 0.151,0 0.299,-0.048 0.424,-0.134 2.314,-1.64 5.186,-2.507 8.305,-2.507 3.995,0 6.004,1.856 7,3.942 l 0,0 c 1.004,2.219 -0.561,4.403 -2.285,4.584 -0.085,0.004 -0.17,0.007 -0.254,0.012 -0.014,0 -0.028,0.001 -0.042,0 -0.055,-0.002 -0.101,0.002 -0.143,0.009 -10.978,0.627 -16.538,4.313 -16.538,10.979 0,2.029 0.803,4.056 2.202,5.57 1.233,1.332 3.56,2.922 7.635,2.922 3.471,0 6.271,-1.112 8.325,-2.474 0.061,-0.03 0.13,-0.076 0.211,-0.141 0.023,-0.017 0.042,-0.03 0.064,-0.047 0.128,-0.091 0.256,-0.177 0.378,-0.27 1.275,-0.824 1.969,-0.35 2.623,0.505 0.002,0.001 0.006,0.004 0.008,0.008 0.84,1.141 2.161,1.717 3.962,1.717 0.846,0 1.591,-0.094 2.344,-0.293 0.32,-0.086 0.543,-0.376 0.543,-0.708 v -1.67 c -0.002,-0.214 -0.097,-0.419 -0.261,-0.558 z m -18.252,0.528 c -3.042,0 -6.111,-1.724 -6.111,-5.573 0,-4.863 5.936,-6.58 12.167,-7.043 2.32,-0.043 4.097,2.161 4.311,4.575 v 0.989 c -0.062,0.646 -0.246,1.289 -0.568,1.888 -0.162,0.265 -0.356,0.556 -0.589,0.864 -0.04,0.049 -0.075,0.094 -0.103,0.135 -1.442,1.847 -4.223,4.165 -9.107,4.165 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3939"
+ d="m 816.13141,706.58754 h -2.047 c -0.393,0 -0.716,0.31 -0.731,0.702 -0.066,1.618 -0.892,1.835 -2.703,0.766 -0.009,-0.006 -0.018,-0.014 -0.027,-0.019 -0.178,-0.121 -0.362,-0.23 -0.546,-0.339 -0.011,-0.009 -0.02,-0.014 -0.031,-0.021 -0.087,-0.057 -0.168,-0.102 -0.244,-0.139 -1.982,-1.096 -4.307,-1.65 -6.924,-1.65 -3.739,0 -7.429,1.547 -10.122,4.249 -2.082,2.087 -4.564,5.826 -4.564,11.783 0,3.819 1.346,7.415 3.792,10.124 2.595,2.872 6.234,4.456 10.248,4.456 3.111,0 5.514,-0.82 7.335,-1.896 0.004,-0.002 0.007,-0.004 0.012,-0.005 1.954,-0.873 3.603,-2.182 3.135,2.234 -0.875,5.678 -4.519,8.765 -10.482,8.765 -4.175,0 -7.31,-1.406 -9.204,-2.584 -0.118,-0.074 -0.251,-0.11 -0.387,-0.11 -0.06,0 -0.122,0.007 -0.182,0.021 -0.193,0.051 -0.358,0.176 -0.456,0.351 l -0.97,1.725 c -0.189,0.335 -0.085,0.761 0.237,0.972 2.853,1.862 6.91,2.975 10.854,2.975 2.689,0 9.293,-0.624 12.493,-6.413 1.385,-2.48 2.031,-5.968 2.031,-10.968 v -17.456 c 0,-2.475 0.07,-4.678 0.214,-6.738 0.014,-0.202 -0.057,-0.402 -0.195,-0.55 -0.139,-0.152 -0.332,-0.235 -0.536,-0.235 z m -16.892,26.024 c -0.605,-0.238 -7.609,-3.214 -7.013,-11.809 0.551,-7.941 5.716,-10.457 6.517,-10.798 1.278,-0.499 2.701,-0.77 4.242,-0.77 1.665,0 3.113,0.328 4.354,0.875 6.845,3.87 7.696,16.453 0.884,21.465 -1.592,1 -3.436,1.574 -5.345,1.574 -1.343,0 -2.553,-0.195 -3.639,-0.537 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3946"
+ d="m 717.35641,692.36154 c -2.337,2.336 -4.109,5.342 -5.032,8.655 -6.076,-0.094 -12.171,2.273 -17.007,7.109 -8.142,8.14 -10.674,22.912 0.667,34.253 9.542,9.542 23.793,9.688 33.145,0.333 4.421,-4.423 7.236,-10.505 7.297,-16.94 3.14,-0.836 6.125,-2.508 8.67,-5.048 6.956,-6.96 9.086,-18.919 -0.311,-28.318 -7.833,-7.831 -19.626,-7.848 -27.429,-0.044 z m 25.622,20.421 c -1.335,1.337 -3.045,2.324 -4.932,2.862 -3.027,0.867 -4.657,0.779 -5.698,2.516 -1.041,1.735 -0.926,1.94 -0.88,5.194 0.056,3.688 -1.333,7.513 -4.813,10.995 -5.32,5.318 -15.14,6.503 -22.366,-0.722 -5.693,-5.694 -8.365,-15.278 -0.778,-22.864 2.816,-2.813 6.866,-4.477 11.124,-4.402 2.961,0.052 3.306,0.162 5.212,-0.703 1.909,-0.87 1.082,-2.801 2.222,-6.205 0.556,-1.66 1.545,-3.296 3.092,-4.845 4.126,-4.125 11.721,-5.072 17.285,0.49 4.533,4.537 6.455,11.76 0.532,17.684 z"
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient4561)" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-foreground-white.png b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-foreground-white.png
new file mode 100644
index 00000000000..75b4be8bbfb
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-foreground-white.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-foreground-white.svg b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-foreground-white.svg
new file mode 100644
index 00000000000..6b853e29ec2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-foreground-white.svg
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="60.000706"
+ height="60.001022"
+ id="svg5804"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 56">
+ <defs
+ id="defs5806">
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_12_"
+ id="linearGradient5527"
+ gradientUnits="userSpaceOnUse"
+ x1="710.15039"
+ y1="437.05859"
+ x2="710.15039"
+ y2="497.05859" />
+ <linearGradient
+ id="SVGID_12_"
+ gradientUnits="userSpaceOnUse"
+ x1="710.15039"
+ y1="437.05859"
+ x2="710.15039"
+ y2="497.05859">
+ <stop
+ offset="0"
+ style="stop-color:#AE0000"
+ id="stop4071" />
+ <stop
+ offset="1"
+ style="stop-color:#740000"
+ id="stop4073" />
+ </linearGradient>
+ <linearGradient
+ y2="497.05859"
+ x2="710.15039"
+ y1="437.05859"
+ x1="710.15039"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5802"
+ xlink:href="#SVGID_12_"
+ inkscape:collect="always" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="82.14321"
+ inkscape:cy="1.4290814"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="1956"
+ inkscape:window-y="265"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata5809">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-292.85679,-453.79024)">
+ <polygon
+ transform="translate(-387.29321,16.732264)"
+ points="680.15,437.059 680.15,497.059 706.012,497.059 740.15,497.059 740.15,462.92 740.15,437.059 "
+ id="polygon4075"
+ style="fill:url(#linearGradient5802)" />
+ <path
+ d="m 346.39079,490.67326 c 7.502,-7.505 9.8,-20.401 -0.335,-30.538 -4.224,-4.222 -9.514,-6.337 -14.8,-6.345 h -0.005 c -5.286,-0.007 -10.567,2.093 -14.774,6.299 -2.522,2.52 -4.434,5.76 -5.428,9.33 -6.49,-0.099 -13.001,2.411 -18.191,7.53 v 36.479 c 0.114,0.121 0.227,0.242 0.344,0.361 h 25.518 11.027 c 4.434,-4.717 7.233,-11.018 7.295,-17.672 3.385,-0.898 6.606,-2.7 9.349,-5.444 z m -13.748,-2.759 c -1.121,1.873 -0.997,2.089 -0.949,5.602 0.062,3.979 -1.437,8.102 -5.189,11.854 -5.736,5.737 -16.326,7.017 -24.119,-0.776 -6.139,-6.14 -9.021,-16.475 -0.84,-24.658 3.038,-3.034 7.406,-4.827 11.998,-4.745 3.193,0.055 3.564,0.174 5.621,-0.76 2.058,-0.938 1.166,-3.019 2.396,-6.692 0.6,-1.792 1.666,-3.551 3.332,-5.224 4.451,-4.448 12.643,-5.469 18.643,0.531 4.887,4.887 6.96,12.677 0.572,19.065 -1.441,1.444 -3.284,2.509 -5.319,3.087 -3.266,0.937 -5.023,0.838 -6.146,2.716 z"
+ id="path4113"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-full-size.png b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-full-size.png
new file mode 100644
index 00000000000..4a478047725
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-full-size.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-full-size.svg b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-full-size.svg
new file mode 100644
index 00000000000..1db5c145fc1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon-full-size.svg
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="199.804"
+ height="200.85513"
+ id="svg4616"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="rroonga-icon-full-size.svg"
+ inkscape:export-filename="rroonga-icon-full-size.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <defs
+ id="defs4618">
+ <linearGradient
+ gradientTransform="translate(279.85374,93.654148)"
+ id="SVGID_4_"
+ gradientUnits="userSpaceOnUse"
+ x1="223.6167"
+ y1="340.418"
+ x2="223.6167"
+ y2="403.2283">
+ <stop
+ offset="0"
+ style="stop-color:#AE0000"
+ id="stop3959" />
+ <stop
+ offset="1"
+ style="stop-color:#740000"
+ id="stop3961" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_4_"
+ id="linearGradient4655"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(279.85374,93.654148)"
+ x1="223.6167"
+ y1="340.418"
+ x2="223.6167"
+ y2="403.2283" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_4_"
+ id="linearGradient3223"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(3.0240925,0,0,3.0240925,-172.76721,-661.31971)"
+ x1="223.6167"
+ y1="340.418"
+ x2="223.6167"
+ y2="403.2283" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="-28.568467"
+ inkscape:cy="54.713275"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-bottom="5"
+ fit-margin-right="5"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2065"
+ inkscape:window-y="414"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata4621">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-403.56846,-366.22033)">
+ <path
+ style="fill:url(#linearGradient3223)"
+ inkscape:connector-curvature="0"
+ d="m 496.35258,388.87808 c -7.0673,7.07335 -12.426,16.1547 -15.21723,26.17957 -18.37439,-0.28427 -36.80623,6.86771 -51.43074,21.49827 -24.62217,24.61914 -32.27917,69.28499 2.01707,103.5782 28.85589,28.86496 71.95223,29.2974 100.23354,1.01609 13.36952,-13.37858 21.88234,-31.77414 22.06681,-51.22813 9.49565,-2.53116 18.52256,-7.58442 26.21888,-15.26864 21.03559,-21.04466 27.4769,-57.21281 -0.94049,-85.63625 -23.68772,-23.67865 -59.35084,-23.73308 -82.94784,-0.13911 z m 77.4833,61.76104 c -4.03716,4.04926 -9.20836,7.02799 -14.91482,8.661 -9.15393,2.62491 -14.0832,2.3467 -17.23128,7.6056 -3.14808,5.24982 -2.80031,5.86976 -2.66121,15.70411 0.16935,11.15588 -4.03111,22.72605 -14.55495,33.25292 -16.08817,16.0791 -45.78476,19.6687 -67.63686,-2.1834 -17.21616,-17.21615 -25.29653,-46.20208 -2.35274,-69.14285 8.51584,-8.50979 20.76342,-13.54491 33.64001,-13.31508 8.95433,0.16028 9.99765,0.48688 15.76157,-2.12593 5.77299,-2.62492 3.27206,-8.46444 6.71953,-18.7645 1.68139,-5.01999 4.67222,-9.96136 9.35049,-14.6487 12.47741,-12.47438 35.44539,-15.33518 52.27144,1.48483 13.70821,13.71426 19.52052,35.56333 1.60882,53.472 z"
+ id="path3963" />
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon.png b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon.png
new file mode 100644
index 00000000000..ed60fbd066f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon.svg b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon.svg
new file mode 100644
index 00000000000..b7e1594a5d1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-icon.svg
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="59.999706"
+ height="59.999016"
+ id="svg5279"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 38">
+ <defs
+ id="defs5281">
+ <linearGradient
+ id="SVGID_16_"
+ gradientUnits="userSpaceOnUse"
+ x1="387.71631"
+ y1="437.05859"
+ x2="387.71631"
+ y2="505.11819">
+ <stop
+ offset="0"
+ style="stop-color:#AE0000"
+ id="stop4101" />
+ <stop
+ offset="1"
+ style="stop-color:#740000"
+ id="stop4103" />
+ </linearGradient>
+ <linearGradient
+ gradientTransform="translate(-244.85971,159.59041)"
+ y2="505.11819"
+ x2="387.71631"
+ y1="437.05859"
+ x1="387.71631"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient5277"
+ xlink:href="#SVGID_16_"
+ inkscape:collect="always" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="91.150426"
+ inkscape:cy="12.210734"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2611"
+ inkscape:window-y="426"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata5284">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-112.85729,-596.64839)">
+ <path
+ style="fill:url(#linearGradient5277)"
+ inkscape:connector-curvature="0"
+ d="m 166.39029,633.53141 c 7.502,-7.505 9.8,-20.401 -0.335,-30.538 -4.224,-4.222 -9.514,-6.337 -14.8,-6.345 h -0.005 c -5.286,-0.007 -10.567,2.093 -14.774,6.299 -2.522,2.52 -4.433,5.76 -5.428,9.33 -6.49,-0.099 -13.001,2.411 -18.191,7.53 v 36.48 c 0.114,0.12 0.227,0.241 0.344,0.36 h 25.518 11.027 c 4.434,-4.717 7.234,-11.018 7.295,-17.672 3.385,-0.898 6.607,-2.7 9.349,-5.444 z m -13.748,-2.759 c -1.121,1.873 -0.997,2.089 -0.949,5.602 0.062,3.979 -1.437,8.102 -5.189,11.854 -5.737,5.737 -16.327,7.017 -24.119,-0.776 -6.14,-6.14 -9.021,-16.475 -0.84,-24.658 3.038,-3.034 7.405,-4.827 11.998,-4.745 3.193,0.055 3.564,0.174 5.621,-0.76 2.058,-0.938 1.166,-3.019 2.396,-6.692 0.6,-1.792 1.667,-3.551 3.333,-5.224 4.451,-4.448 12.642,-5.469 18.642,0.531 4.887,4.887 6.96,12.677 0.572,19.065 -1.441,1.444 -3.284,2.509 -5.319,3.087 -3.265,0.937 -5.023,0.838 -6.146,2.716 z"
+ id="path4105" />
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo-foreground-white.png b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo-foreground-white.png
new file mode 100644
index 00000000000..72f9bdf52fa
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo-foreground-white.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo-foreground-white.svg b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo-foreground-white.svg
new file mode 100644
index 00000000000..df19d65d1fa
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo-foreground-white.svg
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="219.71045"
+ height="73.111534"
+ id="svg4954"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 25">
+ <defs
+ id="defs4956">
+ <linearGradient
+ gradientTransform="translate(-325.8942,122.22558)"
+ id="SVGID_8_"
+ gradientUnits="userSpaceOnUse"
+ x1="589.36517"
+ y1="341"
+ x2="589.36517"
+ y2="405.3815">
+ <stop
+ offset="0"
+ style="stop-color:#AE0000"
+ id="stop4027" />
+ <stop
+ offset="1"
+ style="stop-color:#740000"
+ id="stop4029" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_8_"
+ id="linearGradient4993"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-325.8942,122.22558)"
+ x1="589.36517"
+ y1="341"
+ x2="589.36517"
+ y2="405.3815" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#000000"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="1"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="193.42665"
+ inkscape:cy="19.412904"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-bottom="5"
+ fit-margin-right="5"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2188"
+ inkscape:window-y="270"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata4959">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-181.57334,-458.66356)">
+ <g
+ id="g4985">
+ <path
+ id="path4016"
+ d="m 230.7538,483.47858 c -1.337,-0.292 -2.28,-0.45 -3.441,-0.45 -2.848,0 -5.33,0.832 -7.393,2.467 -0.09,0.062 -0.191,0.142 -0.302,0.247 -0.23,0.197 -0.452,0.405 -0.669,0.621 -1.477,1.313 -2.759,1.95 -2.759,-1.235 v -0.666 c 0,-0.404 -0.327,-0.733 -0.732,-0.733 h -2.1 c -0.2,0 -0.389,0.081 -0.527,0.223 -0.138,0.143 -0.212,0.335 -0.206,0.533 0.106,3.333 0.215,6.778 0.215,9.622 v 18.749 c 0,0.403 0.328,0.731 0.732,0.731 h 2.209 c 0.403,0 0.732,-0.328 0.732,-0.731 v -9.913 c 0,-1.371 0.107,-3.805 0.31,-4.813 1.568,-7.798 4.982,-11.59 10.435,-11.59 0.979,0 1.88,0.164 2.874,0.364 0.047,0.01 0.095,0.013 0.143,0.013 0.148,0 0.294,-0.043 0.417,-0.129 0.162,-0.113 0.273,-0.288 0.307,-0.485 l 0.322,-1.993 c 0.062,-0.386 -0.187,-0.752 -0.567,-0.832 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path4018"
+ d="m 204.7028,483.47858 c -1.338,-0.292 -2.282,-0.45 -3.443,-0.45 -2.846,0 -5.331,0.832 -7.39,2.467 -0.092,0.062 -0.193,0.142 -0.305,0.247 -0.229,0.197 -0.452,0.405 -0.669,0.621 -1.477,1.313 -2.759,1.95 -2.759,-1.235 v -0.666 c 0,-0.404 -0.327,-0.733 -0.731,-0.733 h -2.102 c -0.198,0 -0.387,0.081 -0.525,0.223 -0.138,0.143 -0.212,0.335 -0.205,0.533 0.105,3.333 0.215,6.778 0.215,9.622 v 18.749 c 0,0.403 0.327,0.731 0.732,0.731 h 2.21 c 0.402,0 0.729,-0.328 0.729,-0.731 v -9.913 c 0,-1.371 0.108,-3.805 0.31,-4.813 1.568,-7.798 4.983,-11.59 10.436,-11.59 0.978,0 1.88,0.164 2.873,0.364 0.048,0.01 0.096,0.013 0.144,0.013 0.148,0 0.294,-0.043 0.416,-0.129 0.165,-0.113 0.275,-0.288 0.307,-0.485 l 0.323,-1.993 c 0.061,-0.386 -0.187,-0.752 -0.566,-0.832 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path4020"
+ d="m 319.8008,485.06958 c -2.513,-1.775 -5.294,-2.041 -6.727,-2.041 -2.911,0 -5.648,0.913 -7.821,2.496 -0.005,0.003 -0.011,0.003 -0.015,0.007 -1.006,0.649 -2.627,0.911 -2.617,-0.624 v -0.021 c 0,-0.107 -0.005,-0.198 -0.014,-0.278 l -0.009,-0.181 c -0.018,-0.391 -0.341,-0.699 -0.731,-0.699 h -2.102 c -0.202,0 -0.396,0.086 -0.534,0.232 -0.139,0.148 -0.21,0.348 -0.195,0.549 0.152,2.282 0.212,4.17 0.212,6.526 v 21.819 c 0,0.403 0.33,0.731 0.733,0.731 h 2.209 c 0.404,0 0.731,-0.328 0.731,-0.731 v -17.51 c 0,-0.815 0.253,-1.763 0.49,-2.378 0.005,-0.01 0.007,-0.016 0.01,-0.026 1.044,-3.042 4.223,-6.118 8.417,-6.474 3.236,0 8.326,2.358 9.024,8.226 0.016,0.15 0.03,0.299 0.041,0.443 0.005,0.05 0.008,0.099 0.013,0.148 0.005,0.07 0.009,0.14 0.014,0.209 0.009,0.19 0.015,0.386 0.015,0.584 0,0.332 0.005,0.636 0.018,0.909 v 15.868 c 0,0.403 0.326,0.731 0.73,0.731 h 2.21 c 0.403,0 0.732,-0.328 0.732,-0.731 v -16.271 c 0.001,-5.372 -1.627,-9.246 -4.834,-11.513 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path4022"
+ d="m 396.0248,510.41058 c -0.133,-0.111 -0.301,-0.173 -0.474,-0.173 -0.039,0 -0.079,0.005 -0.118,0.01 -0.588,0.097 -0.964,0.097 -1.444,0.097 -1.147,0 -2.177,-0.334 -2.177,-4.386 v -9.913 c 0,-2.344 -0.18,-5.367 -1.583,-7.971 -1.824,-3.384 -5.181,-5.099 -9.979,-5.099 -2.475,0 -6.218,0.516 -9.99,2.974 -0.313,0.205 -0.424,0.614 -0.252,0.949 l 0.862,1.67 c 0.098,0.188 0.272,0.325 0.479,0.377 0.058,0.013 0.116,0.019 0.174,0.019 0.15,0 0.296,-0.046 0.422,-0.133 2.314,-1.64 5.188,-2.506 8.306,-2.506 3.995,0 6.004,1.854 7,3.939 0,0 0,0 0,0.002 1.002,2.219 -0.563,4.402 -2.286,4.584 -0.084,0.005 -0.169,0.006 -0.253,0.012 -0.015,0 -0.027,0 -0.043,0 -0.055,-0.002 -0.101,0.002 -0.143,0.009 -10.979,0.628 -16.537,4.312 -16.537,10.98 0,2.028 0.801,4.057 2.201,5.569 1.235,1.332 3.56,2.92 7.637,2.92 3.471,0 6.27,-1.111 8.324,-2.472 0.061,-0.032 0.131,-0.076 0.211,-0.141 0.021,-0.017 0.044,-0.029 0.064,-0.047 0.127,-0.09 0.257,-0.18 0.377,-0.269 1.277,-0.825 1.97,-0.351 2.623,0.502 0.002,0.005 0.006,0.006 0.008,0.008 0.839,1.143 2.162,1.72 3.963,1.72 0.847,0 1.59,-0.094 2.343,-0.296 0.322,-0.086 0.544,-0.374 0.544,-0.706 v -1.672 c 0,-0.215 -0.096,-0.417 -0.259,-0.557 z m -18.252,0.529 c -3.042,0 -6.111,-1.725 -6.111,-5.573 0,-4.862 5.936,-6.58 12.166,-7.043 2.32,-0.043 4.098,2.161 4.313,4.575 v 0.988 c -0.063,0.647 -0.246,1.289 -0.569,1.889 -0.161,0.263 -0.354,0.556 -0.588,0.864 -0.04,0.048 -0.076,0.092 -0.104,0.134 -1.442,1.848 -4.223,4.166 -9.107,4.166 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path4024"
+ d="m 359.8918,483.72858 h -2.047 c -0.392,0 -0.716,0.31 -0.731,0.704 -0.064,1.617 -0.892,1.835 -2.701,0.766 -0.011,-0.008 -0.02,-0.013 -0.028,-0.021 -0.179,-0.118 -0.362,-0.229 -0.547,-0.339 -0.01,-0.005 -0.02,-0.011 -0.031,-0.019 -0.087,-0.059 -0.168,-0.102 -0.243,-0.139 -1.982,-1.097 -4.308,-1.652 -6.925,-1.652 -3.739,0 -7.428,1.549 -10.121,4.249 -2.082,2.089 -4.564,5.828 -4.564,11.785 0,3.816 1.347,7.414 3.791,10.122 2.597,2.873 6.233,4.458 10.249,4.458 3.11,0 5.514,-0.822 7.335,-1.898 0.004,0 0.007,-0.003 0.012,-0.004 1.953,-0.874 3.602,-2.182 3.134,2.233 -0.875,5.677 -4.518,8.764 -10.48,8.764 -4.176,0 -7.31,-1.406 -9.204,-2.583 -0.116,-0.073 -0.251,-0.111 -0.388,-0.111 -0.06,0 -0.123,0.008 -0.182,0.022 -0.192,0.051 -0.356,0.177 -0.456,0.351 l -0.968,1.725 c -0.19,0.335 -0.086,0.763 0.237,0.972 2.853,1.864 6.909,2.976 10.852,2.976 2.689,0 9.294,-0.624 12.493,-6.413 1.385,-2.482 2.031,-5.968 2.031,-10.968 v -17.456 c 0,-2.476 0.07,-4.679 0.216,-6.738 0.013,-0.203 -0.058,-0.401 -0.196,-0.55 -0.138,-0.149 -0.334,-0.236 -0.538,-0.236 z m -16.892,26.028 c -0.604,-0.24 -7.607,-3.216 -7.011,-11.813 0.549,-7.939 5.715,-10.454 6.518,-10.796 1.276,-0.501 2.698,-0.77 4.24,-0.77 1.663,0 3.112,0.327 4.354,0.871 6.845,3.873 7.696,16.454 0.883,21.469 -1.592,1 -3.437,1.573 -5.346,1.573 -1.341,0.001 -2.551,-0.192 -3.638,-0.534 z"
+ inkscape:connector-curvature="0"
+ style="fill:#ffffff" />
+ <path
+ id="path4031"
+ d="m 261.1178,469.50258 c -2.338,2.339 -4.11,5.342 -5.031,8.657 -6.076,-0.094 -12.172,2.271 -17.008,7.109 -8.143,8.141 -10.674,22.911 0.668,34.251 9.542,9.545 23.793,9.688 33.144,0.336 4.422,-4.424 7.237,-10.507 7.298,-16.94 3.14,-0.837 6.125,-2.508 8.67,-5.049 6.957,-6.959 9.085,-18.919 -0.311,-28.318 -7.835,-7.83 -19.628,-7.848 -27.43,-0.046 z m 25.622,20.423 c -1.335,1.339 -3.046,2.324 -4.934,2.864 -3.026,0.868 -4.655,0.776 -5.696,2.515 -1.042,1.736 -0.925,1.941 -0.881,5.193 0.056,3.689 -1.332,7.515 -4.813,10.996 -5.319,5.317 -15.139,6.504 -22.366,-0.722 -5.692,-5.693 -8.363,-15.278 -0.776,-22.864 2.816,-2.814 6.867,-4.479 11.123,-4.403 2.961,0.053 3.306,0.161 5.213,-0.703 1.907,-0.868 1.082,-2.799 2.222,-6.205 0.557,-1.66 1.544,-3.294 3.093,-4.844 4.125,-4.125 11.721,-5.071 17.284,0.491 4.532,4.535 6.456,11.76 0.531,17.682 z"
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient4993)" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo.png b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo.png
new file mode 100644
index 00000000000..54f6db6be99
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo.svg b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo.svg
new file mode 100644
index 00000000000..6fa698cdc9e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/rroonga-logo.svg
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="219.71234"
+ height="73.111534"
+ id="svg4616"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="新規ドキュメント 13">
+ <defs
+ id="defs4618">
+ <linearGradient
+ gradientTransform="translate(279.85374,93.654148)"
+ id="SVGID_4_"
+ gradientUnits="userSpaceOnUse"
+ x1="223.6167"
+ y1="340.418"
+ x2="223.6167"
+ y2="403.2283">
+ <stop
+ offset="0"
+ style="stop-color:#AE0000"
+ id="stop3959" />
+ <stop
+ offset="1"
+ style="stop-color:#740000"
+ id="stop3961" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#SVGID_4_"
+ id="linearGradient4655"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(279.85374,93.654148)"
+ x1="223.6167"
+ y1="340.418"
+ x2="223.6167"
+ y2="403.2283" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="-46.572402"
+ inkscape:cy="-9.1585194"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="5"
+ fit-margin-left="5"
+ fit-margin-bottom="5"
+ fit-margin-right="5"
+ inkscape:window-width="902"
+ inkscape:window-height="442"
+ inkscape:window-x="2065"
+ inkscape:window-y="414"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata4621">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="レイヤー 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-421.5724,-430.09213)">
+ <g
+ id="g4647">
+ <path
+ id="path3948"
+ d="m 470.75274,454.90715 c -1.336,-0.292 -2.281,-0.45 -3.442,-0.45 -2.847,0 -5.33,0.832 -7.391,2.467 -0.091,0.062 -0.192,0.142 -0.303,0.247 -0.231,0.197 -0.452,0.405 -0.668,0.621 -1.478,1.313 -2.76,1.95 -2.76,-1.235 v -0.666 c 0,-0.404 -0.327,-0.733 -0.732,-0.733 h -2.101 c -0.198,0 -0.388,0.081 -0.525,0.223 -0.139,0.143 -0.212,0.335 -0.206,0.533 0.105,3.333 0.215,6.778 0.215,9.622 v 18.749 c 0,0.403 0.328,0.731 0.732,0.731 h 2.209 c 0.404,0 0.732,-0.328 0.732,-0.731 v -9.913 c 0,-1.371 0.107,-3.805 0.309,-4.813 1.569,-7.798 4.982,-11.59 10.435,-11.59 0.979,0 1.881,0.164 2.874,0.364 0.048,0.01 0.096,0.013 0.144,0.013 0.148,0 0.294,-0.043 0.417,-0.129 0.163,-0.113 0.273,-0.288 0.306,-0.485 l 0.323,-1.993 c 0.062,-0.386 -0.187,-0.752 -0.568,-0.832 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3950"
+ d="m 444.70074,454.90715 c -1.337,-0.292 -2.281,-0.45 -3.442,-0.45 -2.847,0 -5.33,0.832 -7.391,2.467 -0.092,0.062 -0.192,0.142 -0.303,0.247 -0.231,0.197 -0.452,0.405 -0.668,0.621 -1.478,1.313 -2.759,1.95 -2.759,-1.235 v -0.666 c 0,-0.404 -0.327,-0.733 -0.732,-0.733 h -2.101 c -0.198,0 -0.388,0.081 -0.526,0.223 -0.138,0.143 -0.212,0.335 -0.206,0.533 0.105,3.333 0.215,6.778 0.215,9.622 v 18.749 c 0,0.403 0.327,0.731 0.732,0.731 h 2.209 c 0.404,0 0.732,-0.328 0.732,-0.731 v -9.913 c 0,-1.371 0.107,-3.805 0.309,-4.813 1.569,-7.798 4.983,-11.59 10.435,-11.59 0.979,0 1.881,0.164 2.874,0.364 0.048,0.01 0.096,0.013 0.144,0.013 0.148,0 0.294,-0.043 0.417,-0.129 0.163,-0.113 0.273,-0.288 0.306,-0.485 l 0.323,-1.993 c 0.061,-0.386 -0.188,-0.752 -0.568,-0.832 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3952"
+ d="m 559.79974,456.49815 c -2.512,-1.775 -5.292,-2.041 -6.726,-2.041 -2.91,0 -5.648,0.913 -7.822,2.496 -0.005,0.003 -0.01,0.003 -0.014,0.007 -1.004,0.649 -2.627,0.911 -2.618,-0.624 l 0,-0.021 c 0.002,-0.107 -0.003,-0.198 -0.013,-0.278 l -0.008,-0.181 c -0.019,-0.391 -0.34,-0.699 -0.731,-0.699 h -2.102 c -0.203,0 -0.396,0.086 -0.535,0.232 -0.138,0.148 -0.209,0.348 -0.195,0.549 0.152,2.282 0.214,4.17 0.214,6.526 v 21.819 c 0,0.403 0.328,0.731 0.731,0.731 h 2.209 c 0.404,0 0.732,-0.328 0.732,-0.731 v -17.51 c 0,-0.815 0.253,-1.763 0.49,-2.378 0.004,-0.01 0.006,-0.016 0.01,-0.026 1.043,-3.042 4.224,-6.118 8.417,-6.474 3.236,0 8.327,2.358 9.024,8.226 0.016,0.15 0.03,0.299 0.042,0.443 0.004,0.05 0.008,0.099 0.011,0.148 0.005,0.07 0.009,0.14 0.014,0.209 0.009,0.19 0.015,0.386 0.015,0.584 0,0.332 0.006,0.636 0.017,0.909 v 15.868 c 0,0.403 0.328,0.731 0.733,0.731 h 2.208 c 0.404,0 0.732,-0.328 0.732,-0.731 v -16.271 c 0,-5.372 -1.627,-9.246 -4.835,-11.513 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3954"
+ d="m 636.02374,481.83915 c -0.133,-0.111 -0.301,-0.173 -0.473,-0.173 -0.04,0 -0.08,0.005 -0.12,0.01 -0.587,0.097 -0.965,0.097 -1.442,0.097 -1.147,0 -2.177,-0.334 -2.177,-4.386 v -9.913 c 0,-2.344 -0.179,-5.367 -1.583,-7.971 -1.822,-3.384 -5.181,-5.099 -9.978,-5.099 -2.475,0 -6.217,0.516 -9.99,2.974 -0.315,0.205 -0.422,0.614 -0.25,0.949 l 0.861,1.67 c 0.098,0.188 0.272,0.325 0.478,0.377 0.058,0.013 0.116,0.019 0.173,0.019 0.151,0 0.299,-0.046 0.424,-0.133 2.314,-1.64 5.186,-2.506 8.305,-2.506 3.995,0 6.004,1.854 7,3.939 0,0 0,0 0,0.002 1.004,2.219 -0.561,4.402 -2.285,4.584 -0.085,0.005 -0.17,0.006 -0.254,0.012 -0.014,0 -0.028,0 -0.042,0 -0.055,-0.002 -0.101,0.002 -0.143,0.009 -10.978,0.628 -16.538,4.312 -16.538,10.98 0,2.028 0.803,4.057 2.202,5.569 1.233,1.332 3.56,2.92 7.635,2.92 3.471,0 6.271,-1.111 8.325,-2.472 0.061,-0.032 0.13,-0.076 0.211,-0.141 0.023,-0.017 0.042,-0.029 0.064,-0.047 0.128,-0.09 0.256,-0.18 0.378,-0.269 1.275,-0.825 1.969,-0.351 2.623,0.502 0.002,0.005 0.006,0.006 0.008,0.008 0.84,1.143 2.161,1.72 3.962,1.72 0.846,0 1.591,-0.094 2.344,-0.296 0.32,-0.086 0.543,-0.374 0.543,-0.706 v -1.672 c -0.002,-0.215 -0.097,-0.417 -0.261,-0.557 z m -18.252,0.529 c -3.042,0 -6.111,-1.725 -6.111,-5.573 0,-4.862 5.936,-6.58 12.167,-7.043 2.32,-0.043 4.097,2.161 4.311,4.575 v 0.988 c -0.062,0.647 -0.246,1.289 -0.568,1.889 -0.162,0.263 -0.356,0.556 -0.589,0.864 -0.04,0.048 -0.075,0.092 -0.103,0.134 -1.442,1.848 -4.223,4.166 -9.107,4.166 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3956"
+ d="m 599.89174,455.15715 h -2.047 c -0.393,0 -0.716,0.31 -0.731,0.704 -0.066,1.617 -0.892,1.835 -2.703,0.766 -0.009,-0.008 -0.018,-0.013 -0.027,-0.021 -0.178,-0.118 -0.362,-0.229 -0.546,-0.339 -0.011,-0.005 -0.02,-0.011 -0.031,-0.019 -0.087,-0.059 -0.168,-0.102 -0.244,-0.139 -1.982,-1.097 -4.307,-1.652 -6.924,-1.652 -3.739,0 -7.429,1.549 -10.122,4.249 -2.082,2.089 -4.564,5.828 -4.564,11.785 0,3.816 1.346,7.414 3.792,10.122 2.595,2.873 6.234,4.458 10.248,4.458 3.111,0 5.514,-0.822 7.335,-1.898 0.004,0 0.007,-0.003 0.012,-0.004 1.954,-0.874 3.603,-2.182 3.135,2.233 -0.875,5.677 -4.519,8.764 -10.482,8.764 -4.175,0 -7.31,-1.406 -9.204,-2.583 -0.118,-0.073 -0.251,-0.111 -0.387,-0.111 -0.06,0 -0.122,0.008 -0.182,0.022 -0.193,0.051 -0.358,0.177 -0.456,0.351 l -0.97,1.725 c -0.189,0.335 -0.085,0.763 0.237,0.972 2.853,1.864 6.91,2.976 10.854,2.976 2.689,0 9.293,-0.624 12.493,-6.413 1.385,-2.482 2.031,-5.968 2.031,-10.968 v -17.456 c 0,-2.476 0.07,-4.679 0.214,-6.738 0.014,-0.203 -0.057,-0.401 -0.195,-0.55 -0.139,-0.15 -0.332,-0.236 -0.536,-0.236 z m -16.892,26.028 c -0.605,-0.24 -7.609,-3.216 -7.013,-11.813 0.551,-7.939 5.716,-10.454 6.517,-10.796 1.278,-0.501 2.701,-0.77 4.242,-0.77 1.665,0 3.113,0.327 4.354,0.871 6.845,3.873 7.696,16.454 0.884,21.469 -1.592,1 -3.436,1.573 -5.345,1.573 -1.343,10e-4 -2.553,-0.192 -3.639,-0.534 z"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3963"
+ d="m 501.11674,440.93115 c -2.337,2.339 -4.109,5.342 -5.032,8.657 -6.076,-0.094 -12.171,2.271 -17.007,7.109 -8.142,8.141 -10.674,22.911 0.667,34.251 9.542,9.545 23.793,9.688 33.145,0.336 4.421,-4.424 7.236,-10.507 7.297,-16.94 3.14,-0.837 6.125,-2.508 8.67,-5.049 6.956,-6.959 9.086,-18.919 -0.311,-28.318 -7.833,-7.83 -19.626,-7.848 -27.429,-0.046 z m 25.622,20.423 c -1.335,1.339 -3.045,2.324 -4.932,2.864 -3.027,0.868 -4.657,0.776 -5.698,2.515 -1.041,1.736 -0.926,1.941 -0.88,5.193 0.056,3.689 -1.333,7.515 -4.813,10.996 -5.32,5.317 -15.14,6.504 -22.366,-0.722 -5.693,-5.693 -8.365,-15.278 -0.778,-22.864 2.816,-2.814 6.866,-4.479 11.124,-4.403 2.961,0.053 3.306,0.161 5.212,-0.703 1.909,-0.868 1.082,-2.799 2.222,-6.205 0.556,-1.66 1.545,-3.294 3.092,-4.844 4.126,-4.125 11.721,-5.071 17.285,0.491 4.533,4.535 6.455,11.76 0.532,17.682 z"
+ inkscape:connector-curvature="0"
+ style="fill:url(#linearGradient4655)" />
+ </g>
+ </g>
+</svg>
diff --git a/storage/mroonga/vendor/groonga/data/images/logo/update-files.sh b/storage/mroonga/vendor/groonga/data/images/logo/update-files.sh
new file mode 100755
index 00000000000..03f68f0afee
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/images/logo/update-files.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+list_paths()
+{
+ variable_name=$1
+ echo "$variable_name = \\"
+ sort | \
+ sed \
+ -e 's,^,\t,' \
+ -e 's,$, \\,'
+ echo " \$(NULL)"
+ echo
+}
+
+# image files.
+ls *.svg *.png | \
+ list_paths "image_files"
diff --git a/storage/mroonga/vendor/groonga/data/munin/Makefile.am b/storage/mroonga/vendor/groonga/data/munin/Makefile.am
new file mode 100644
index 00000000000..329dea592d9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/munin/Makefile.am
@@ -0,0 +1,12 @@
+if INSTALL_MUNIN_PLUGINS
+munin_pluginsdir = $(datarootdir)/$(PACKAGE)/munin/plugins
+dist_munin_plugins_SCRIPTS = \
+ groonga_cpu_load_ \
+ groonga_cpu_time_ \
+ groonga_status_ \
+ groonga_memory_ \
+ groonga_n_records_ \
+ groonga_query_performance_ \
+ groonga_disk_ \
+ groonga_throughput_
+endif
diff --git a/storage/mroonga/vendor/groonga/data/munin/groonga_cpu_load_ b/storage/mroonga/vendor/groonga/data/munin/groonga_cpu_load_
new file mode 100755
index 00000000000..17161acb00b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/munin/groonga_cpu_load_
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+#%# family=auto
+#%# capabilities=autoconf suggest
+
+case "$1" in
+ autoconf|detect)
+ pid_paths=
+ if [ -n "${pid_path}" ]; then
+ pid_paths="${pid_paths}${pid_paths:+ }${pid_path}"
+ fi
+ # For backward compatibility. Remove me when 5.0.0.
+ if [ -n "${pid_file}" ]; then
+ pid_paths="${pid_paths}${pid_paths:+ }${pid_file}"
+ fi
+ if [ -n "${http_pid_path}" ]; then
+ pid_paths="${pid_paths}${pid_paths:+ }${http_pid_path}"
+ fi
+ if [ -n "${httpd_pid_path}" ]; then
+ pid_paths="${pid_paths}${pid_paths:+ }${httpd_pid_path}"
+ fi
+ if [ -n "${gqtp_pid_path}" ]; then
+ pid_paths="${pid_paths}${pid_paths:+ }${gqtp_pid_path}"
+ fi
+ if [ -z "${pid_paths}" ]; then
+ message="no (No PID path is specified. Specify "
+ message="${message} env.pid_path, env.http_pid_path, "
+ message="${message} env.httpd_pid_path and/or env.gqtp_pid_path.)"
+ echo "${message}"
+ exit 1
+ fi
+
+ for _pid_path in ${pid_paths}; do
+ if [ -f "${_pid_path}" ]; then
+ echo "yes"
+ exit 0
+ fi
+ done
+
+ echo "no (All PID paths don't exist: ${pid_paths})"
+ exit 1
+ ;;
+ suggest)
+ if [ -n "${http_pid_path}" -a -f "${http_pid_path}" ]; then
+ echo "http"
+ fi
+ if [ -n "${httpd_pid_path}" -a -f "${httpd_pid_path}" ]; then
+ echo "httpd"
+ fi
+ if [ -n "${gqtp_pid_path}" -a -f "${gqtp_pid_path}" ]; then
+ echo "gqtp"
+ fi
+ exit 0
+ ;;
+ config)
+ if [ -z "${label}" ]; then
+ title="groonga: CPU load"
+ else
+ title="groonga: ${label}: CPU load"
+ fi
+ cat <<EOF
+graph_title ${title}
+graph_vlabel CPU load (%)
+graph_category groonga
+graph_info groonga CPU load
+
+cpu_load.label CPU load
+cpu_load.type GAUGE
+EOF
+ exit 0
+ ;;
+ *)
+ ;;
+esac
+
+server_type="${0##*_}"
+if [ -n "${server_type}" ]; then
+ pid_path_variable_name="${server_type}_pid_path"
+else
+ # For backward compatibility. Remove me when 5.0.0.
+ if [ -z "${pid_path}" -a -n "${pid_file}" ]; then
+ pid_path_variable_name="pid_file"
+ else
+ pid_path_variable_name="pid_path"
+ fi
+fi
+_pid_path=$(eval "echo \${${pid_path_variable_name}}")
+if [ -z "${_pid_path}" ]; then
+ echo "PID path isn't specified by env.${pid_path_variable_name}" 1>&2
+ exit 1
+fi
+if [ ! -f "${_pid_path}" ]; then
+ echo "PID path doesn't exist: ${_pid_path}" 1>&2
+ exit 1
+fi
+
+groonga_pid=$(cat ${_pid_path})
+top_for_groonga=$(top -b -n 1 -p ${groonga_pid} | grep ${groonga_pid})
+load_in_percent=$(echo ${top_for_groonga} | sed -r -e 's/ +/ /g' | cut -d' ' -f 9)
+echo "cpu_load.value ${load_in_percent}"
diff --git a/storage/mroonga/vendor/groonga/data/munin/groonga_cpu_time_ b/storage/mroonga/vendor/groonga/data/munin/groonga_cpu_time_
new file mode 100755
index 00000000000..5c60c17db55
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/munin/groonga_cpu_time_
@@ -0,0 +1,122 @@
+#!/usr/bin/env ruby
+
+#%# family=auto
+#%# capabilities=autoconf suggest
+
+label = ENV["label"]
+
+command = ARGV.shift
+
+def autoconf
+ pid_path_variable_names = [
+ "pid_path",
+ "pid_file", # For backward compatibility. Remove me when 5.0.0.
+ "http_pid_path",
+ "httpd_pid_path",
+ "gqtp_pid_path",
+ ]
+ pid_paths = pid_path_variable_names.collect do |variable_name|
+ ENV[variable_name]
+ end
+ pid_paths = pid_paths.compact
+ if pid_paths.empty?
+ variable_names = pid_path_variable_names.collect do |name|
+ "env.#{name}"
+ end
+ variable_names_label = variable_names[0..-2].join(", ")
+ variable_names_label << " and/or #{variable_names.last}"
+ puts("no (No PID path is specified. Specify #{variable_names_label}.)")
+ exit(false)
+ end
+ pid_paths.each do |pid_path|
+ next unless File.exist?(pid_path)
+ puts("yes")
+ exit(true)
+ end
+ pid_paths_label = pid_paths.join(", ")
+ puts("no (All PID paths don't exist: #{pid_paths_label})")
+ exit(false)
+end
+
+def suggest
+ exist_p = lambda do |variable_name|
+ pid_path = ENV[variable_name]
+ pid_path and File.exist?(pid_path)
+ end
+ if exist_p.call("http_pid_path")
+ puts("http")
+ end
+ if exist_p.call("httpd_pid_path")
+ puts("httpd")
+ end
+ if exist_p.call("gqtp_pid_path")
+ puts("gqtp")
+ end
+ exit(true)
+end
+
+def target_pid_path
+ if /_([^_]+)\z/ =~ $0
+ service_type = $1
+ pid_path_variable_name = "#{service_type}_pid_path"
+ pid_path = ENV[pid_path_variable_name]
+ else
+ pid_path_variable_name = "pid_path"
+ pid_path = ENV[pid_path_variable_name]
+ # For backward compatibility. Remove me when 5.0.0.
+ pid_path ||= ENV["pid_file"]
+ end
+
+ if pid_path.nil?
+ $stderr.puts("PID path isn't specified by env.#{pid_path_variable_name}")
+ exit(false)
+ end
+
+ unless File.exist?(pid_path)
+ $stderr.puts("PID path doesn't exist: #{pid_path}")
+ exit(false)
+ end
+
+ pid_path
+end
+
+case command
+when "autoconf", "detect"
+ autoconf
+when "suggest"
+ suggest
+when "config"
+ if label
+ title = "groonga: #{label}: CPU time"
+ else
+ title = "groonga: CPU time"
+ end
+ puts(<<EOF)
+graph_title #{title}
+graph_vlabel CPU time (days)
+graph_category groonga
+graph_info groonga CPU time
+
+cpu_time.label CPU time
+cpu_time.type GAUGE
+EOF
+ exit(true)
+end
+
+groonga_pid = File.read(target_pid_path).strip
+time = `ps h -o time -p #{groonga_pid}`.chomp
+if /\A(?:(\d+)-)?(\d+):(\d+):(\d+)\z/ =~ time
+ day, hours, minutes, seconds, = $1, $2, $3, $4
+ day = (day || 0).to_i
+ hours = hours.to_i
+ minutes = minutes.to_i
+ seconds = seconds.to_i
+ time_in_seconds = seconds + minutes * 60 + hours * 60 * 60
+ day_in_seconds = 60 * 60 * 24
+ fraction_in_day = time_in_seconds.to_f / day_in_seconds.to_f
+ cpu_time_in_day = day + fraction_in_day
+ puts("cpu_time.value #{cpu_time_in_day}")
+else
+ $stderr.puts("invalid time format: <#{time}>")
+ exit(false)
+end
diff --git a/storage/mroonga/vendor/groonga/data/munin/groonga_disk_ b/storage/mroonga/vendor/groonga/data/munin/groonga_disk_
new file mode 100755
index 00000000000..c65aad52b2b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/munin/groonga_disk_
@@ -0,0 +1,246 @@
+#!/usr/bin/env ruby
+
+#%# family=auto
+#%# capabilities=autoconf suggest
+
+require 'shellwords'
+begin
+ require 'json'
+rescue LoadError
+ require 'rubygems'
+ require 'json'
+end
+require 'English'
+
+label = ENV["label"]
+@groonga = ENV["groonga"] || "groonga"
+@du = ENV["du"] || "du"
+
+command = ARGV.shift
+
+def parse(success, result)
+ if success
+ begin
+ status, body = JSON.parse(result)
+ return_code, start_time, elapsed, error_message = status
+ if return_code.zero?
+ [success, body]
+ else
+ [false, error_message]
+ end
+ rescue JSON::ParserError
+ [false, $!.message]
+ end
+ else
+ [success, result]
+ end
+end
+
+def run(command, *args)
+ database_path = Shellwords.shellescape(@database_path)
+ result = `#{@groonga} #{database_path} #{command} #{args.join(' ')} 2>&1`
+ parse($?.success?, result)
+end
+
+def parse_list(header, list)
+ list.collect do |item|
+ parsed_item = {}
+ header.each_with_index do |(name, type), i|
+ parsed_item[name] = item[i]
+ end
+ parsed_item
+ end
+end
+
+def schema
+ tables = []
+ success, table_list_body = run("table_list")
+ unless success
+ puts("error: #{table_list_body}")
+ exit(false)
+ end
+ parse_list(table_list_body[0], table_list_body[1..-1]).each do |table|
+ table_name = table["name"]
+ table["key"] = "table_#{table_name}"
+ success, column_list_body = run("column_list", table_name)
+ unless success
+ puts("error: #{column_list_body}")
+ exit(false)
+ end
+ table["columns"] = parse_list(column_list_body[0], column_list_body[1..-1])
+ table["columns"].each do |column|
+ column["key"] = "column_#{table_name}_#{column['name']}"
+ column["full_name"] = "#{table_name}.#{column['name']}"
+ end
+ tables << table
+ end
+ tables
+end
+
+def parse_du_result(result)
+ usages = {}
+ result.each_line do |line|
+ if /\A(\d+)\s+/ =~ line
+ usage = $1
+ path = $POSTMATCH.strip
+ usages[path] = usage.to_i
+ end
+ end
+ usages
+end
+
+def compute_size(usages, base_path)
+ usage = 0
+ return usage if base_path.empty?
+
+ usages.each do |path, size|
+ usage += size if path.start_with?(base_path)
+ end
+ usage
+end
+
+def setup_database_path
+ if /_([^_]+)\z/ =~ $0
+ service_type = $1
+ database_path_variable_name = "#{service_type}_database_path"
+ database_path = ENV[database_path_variable_name]
+ else
+ database_path_variable_name = "database_path"
+ database_path = ENV[database_path_variable_name]
+ # For backward compatibility. Remove me when 5.0.0.
+ database_path ||= ENV["path"]
+ end
+
+ if database_path.nil?
+ key = "env.#{database_path_variable_name}"
+ $stderr.puts("Database path isn't specified by #{key}")
+ exit(false)
+ end
+
+ unless File.exist?(database_path)
+ $stderr.puts("Database path doesn't exist: #{database_path}")
+ exit(false)
+ end
+
+ @database_path = database_path
+end
+
+def autoconf
+ database_path_variable_names = [
+ "database_path",
+ "path", # For backward compatibility. Remove me when 5.0.0.
+ "http_database_path",
+ "httpd_database_path",
+ "gqtp_database_path",
+ ]
+ database_paths = database_path_variable_names.collect do |variable_name|
+ ENV[variable_name]
+ end
+ database_paths = database_paths.compact
+ if database_paths.empty?
+ variable_names = database_path_variable_names.collect do |name|
+ "env.#{name}"
+ end
+ variable_names_label = variable_names[0..-2].join(", ")
+ variable_names_label << " and/or #{variable_names.last}"
+ puts("no (No database path is specified. Specify #{variable_names_label}.)")
+ exit(false)
+ end
+ database_paths.each do |database_path|
+ next unless File.exist?(database_path)
+ puts("yes")
+ exit(true)
+ end
+ database_paths_label = database_paths.join(", ")
+ puts("no (All database paths don't exist: #{database_paths_label})")
+ exit(false)
+end
+
+def suggest
+ exist_p = lambda do |variable_name|
+ database_path = ENV[variable_name]
+ database_path and File.exist?(database_path)
+ end
+ if exist_p.call("http_database_path")
+ puts("http")
+ end
+ if exist_p.call("httpd_database_path")
+ puts("httpd")
+ end
+ if exist_p.call("gqtp_database_path")
+ puts("gqtp")
+ end
+ exit(true)
+end
+
+case command
+when "autoconf", "detect"
+ autoconf
+when "suggest"
+ suggest
+when "config"
+ setup_database_path
+ if label.nil?
+ title = "groonga: disk usage"
+ else
+ title = "groonga: #{label}: disk usage"
+ end
+ puts(<<EOF)
+graph_title #{title}
+graph_vlabel Bytes
+graph_category groonga
+graph_info disk usage in groonga tables and columns
+graph_args --base 1024
+graph_total Total
+
+database.label Database
+database.draw AREA
+EOF
+ schema.each do |table|
+ table_key = table["key"]
+ table_name = table["name"]
+ puts(<<EOF)
+
+#{table_key}.label #{table_name}
+#{table_key}.draw STACK
+EOF
+ table["columns"].each do |column|
+ column_key = column["key"]
+ column_name = column["full_name"]
+ puts(<<EOF)
+
+#{column_key}.label #{column_name}
+#{column_key}.draw STACK
+EOF
+ end
+ end
+ exit(true)
+end
+
+setup_database_path
+database_path = Shellwords.shellescape(@database_path)
+du_result = `#{@du} -B1 #{database_path}*`
+unless $?.success?
+ $stderr.puts("error: #{du_result}")
+ exit(false)
+end
+usages = parse_du_result(du_result)
+usage = compute_size(usages, @database_path)
+puts(<<EOF)
+database.value #{usage}
+EOF
+schema.each do |table|
+ table_key = table["key"]
+ table_name = table["name"]
+ usage = compute_size(usages, table["path"])
+ puts(<<EOF)
+#{table_key}.value #{usage}
+EOF
+ table["columns"].each do |column|
+ column_key = column["key"]
+ usage = compute_size(usages, column["path"])
+ puts(<<EOF)
+#{column_key}.value #{usage}
+EOF
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/data/munin/groonga_memory_ b/storage/mroonga/vendor/groonga/data/munin/groonga_memory_
new file mode 100755
index 00000000000..675154bcbf6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/munin/groonga_memory_
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+#%# family=auto
+#%# capabilities=autoconf suggest
+
+case "$1" in
+ autoconf|detect)
+ pid_paths=
+ if [ -n "${pid_path}" ]; then
+ pid_paths="${pid_paths}${pid_paths:+ }${pid_path}"
+ fi
+ # For backward compatibility. Remove me when 5.0.0.
+ if [ -n "${pid_file}" ]; then
+ pid_paths="${pid_paths}${pid_paths:+ }${pid_file}"
+ fi
+ if [ -n "${http_pid_path}" ]; then
+ pid_paths="${pid_paths}${pid_paths:+ }${http_pid_path}"
+ fi
+ if [ -n "${httpd_pid_path}" ]; then
+ pid_paths="${pid_paths}${pid_paths:+ }${httpd_pid_path}"
+ fi
+ if [ -n "${gqtp_pid_path}" ]; then
+ pid_paths="${pid_paths}${pid_paths:+ }${gqtp_pid_path}"
+ fi
+ if [ -z "${pid_paths}" ]; then
+ message="no (No PID path is specified. Specify "
+ message="${message} env.pid_path, env.http_pid_path, "
+ message="${message} env.httpd_pid_path and/or env.gqtp_pid_path.)"
+ echo "${message}"
+ exit 1
+ fi
+
+ for _pid_path in ${pid_paths}; do
+ if [ -f "${_pid_path}" ]; then
+ echo "yes"
+ exit 0
+ fi
+ done
+
+ echo "no (All PID paths don't exist: ${pid_paths})"
+ exit 1
+ ;;
+ suggest)
+ if [ -n "${http_pid_path}" -a -f "${http_pid_path}" ]; then
+ echo "http"
+ fi
+ if [ -n "${httpd_pid_path}" -a -f "${httpd_pid_path}" ]; then
+ echo "httpd"
+ fi
+ if [ -n "${gqtp_pid_path}" -a -f "${gqtp_pid_path}" ]; then
+ echo "gqtp"
+ fi
+ exit 0
+ ;;
+ config)
+ if [ -z "${label}" ]; then
+ title="groonga: memory usage"
+ else
+ title="groonga: ${label}: memory usage"
+ fi
+ cat <<EOF
+graph_title ${title}
+graph_vlabel memory usage
+graph_category groonga
+graph_info groonga memory usage
+
+rss.label resident set size
+rss.type GAUGE
+vsz.label virtual memory size
+vsz.type GAUGE
+EOF
+ exit 0
+ ;;
+ *)
+ ;;
+esac
+
+server_type="${0##*_}"
+if [ -n "${server_type}" ]; then
+ pid_path_variable_name="${server_type}_pid_path"
+else
+ # For backward compatibility. Remove me when 5.0.0.
+ if [ -z "${pid_path}" -a -n "${pid_file}" ]; then
+ pid_path_variable_name="pid_file"
+ else
+ pid_path_variable_name="pid_path"
+ fi
+fi
+_pid_path=$(eval "echo \${${pid_path_variable_name}}")
+if [ -z "${_pid_path}" ]; then
+ echo "PID path isn't specified by env.${pid_path_variable_name}" 1>&2
+ exit 1
+fi
+if [ ! -f "${_pid_path}" ]; then
+ echo "PID path doesn't exist: ${_pid_path}" 1>&2
+ exit 1
+fi
+
+groonga_pid=$(cat ${_pid_path})
+read rss_in_kb vsz_in_kb <<EOC
+$(ps h -o rss,vsz -p ${groonga_pid})
+EOC
+echo "rss.value ${rss_in_kb}000"
+echo "vsz.value ${vsz_in_kb}000"
diff --git a/storage/mroonga/vendor/groonga/data/munin/groonga_n_records_ b/storage/mroonga/vendor/groonga/data/munin/groonga_n_records_
new file mode 100755
index 00000000000..c131c481efa
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/munin/groonga_n_records_
@@ -0,0 +1,256 @@
+#!/usr/bin/env ruby
+
+#%# family=auto
+#%# capabilities=autoconf suggest
+
+require 'shellwords'
+require 'net/http'
+require 'erb'
+begin
+ require 'json'
+rescue LoadError
+ require 'rubygems'
+ require 'json'
+end
+
+label = ENV["label"]
+@groonga = ENV["groonga"] || "groonga"
+@default_host = ENV["host"] || "127.0.0.1"
+@default_http_port = 10041
+@default_gqtp_port = 10043
+@database_path = nil
+
+command = ARGV.shift
+
+def parse(success, result)
+ if success
+ begin
+ status, body = JSON.parse(result)
+ return_code, start_time, elapsed, error_message = status
+ if return_code.zero?
+ [success, body]
+ else
+ [false, error_message]
+ end
+ rescue JSON::ParserError
+ [false, "#{$!.message}: #{result}"]
+ end
+ else
+ [success, result]
+ end
+end
+
+def run_http(host, port, command, *args)
+ path = "/d/#{command}"
+ unless args.empty?
+ parameters = args.each_slice(2).collect do |key, value|
+ key = key.gsub(/\A--/, "")
+ "#{ERB::Util.u(key)}=#{ERB::Util.u(value)}"
+ end
+ path << "?" << parameters.join("&")
+ end
+ response = Net::HTTP.start(host, port) do |http|
+ http.get(path)
+ end
+ if response.is_a?(Net::HTTPSuccess)
+ parse(true, response.body)
+ else
+ [false, "#{response.code}: #{response.body}"]
+ end
+end
+
+def run_gqtp(host, port, command, *args)
+ groonga = "#{@groonga} -p #{port} -c #{host}"
+ result = `#{groonga} #{command} #{args.join(' ')} 2>&1`
+ parse($?.success?, result)
+end
+
+def run_command_line(database_path, command, *args)
+ database_path = Shellwords.shellescape(database_path)
+ result = `#{@groonga} #{database_path} #{command} #{args.join(' ')} 2>&1`
+ parse($?.success?, result)
+end
+
+def run(command, *args)
+ case @service_type
+ when "http"
+ run_http(@http_host, @http_port, command, *args)
+ when "httpd"
+ run_http(@httpd_host, @httpd_port, command, *args)
+ when "gqtp"
+ run_gqtp(@gqtp_host, @gqtp_port, command, *args)
+ else
+ run_command_line(@database_path, command, *args)
+ end
+end
+
+def exclude_tables
+ case @service_type
+ when "http"
+ @http_exclude_tables
+ when "httpd"
+ @httpd_exclude_tables
+ when "gqtp"
+ @gqtp_exclude_tables
+ else
+ @exclude_tables
+ end
+end
+
+def parse_list(header, list)
+ list.collect do |item|
+ parsed_item = {}
+ header.each_with_index do |(name, type), i|
+ parsed_item[name] = item[i]
+ end
+ parsed_item
+ end
+end
+
+def table_list
+ success, body = run("table_list")
+ unless success
+ puts("error: #{body}")
+ exit(false)
+ end
+ tables = parse_list(body[0], body[1..-1])
+ tables.reject do |table|
+ name = table["name"]
+ exclude_tables.include?(name)
+ end
+end
+
+def setup_service_type
+ if /_([^_]+)\z/ =~ $0
+ @service_type = $1
+ else
+ @service_type = nil
+ end
+end
+
+def parse_exclude_tables(raw_exclude_tables_value)
+ (raw_exclude_tables_value || "").split(/\s*,\s*/)
+end
+
+def setup_command_line
+ @database_path = ENV["database_path"]
+ # For backward compatibility. Remove me when 5.0.0.
+ @database_path ||= ENV["path"]
+ @exclude_tables = parse_exclude_tables(ENV["exclude_tables"])
+end
+
+def setup_http
+ @http_host = ENV["http_host"] || @default_host
+ @http_port = (ENV["http_port"] || @default_http_port).to_i
+ @http_exclude_tables = parse_exclude_tables(ENV["http_exclude_tables"])
+end
+
+def setup_httpd
+ @httpd_host = ENV["httpd_host"] || @default_host
+ @httpd_port = (ENV["httpd_port"] || @default_http_port).to_i
+ @httpd_exclude_tables = parse_exclude_tables(ENV["httpd_exclude_tables"])
+end
+
+def setup_gqtp
+ @gqtp_host = ENV["gqtp_host"] || @default_host
+ @gqtp_port = (ENV["gqtp_port"] || @default_gqtp_port).to_i
+ @gqtp_exclude_tables = parse_exclude_tables(ENV["gqtp_exclude_tables"])
+end
+
+def setup
+ setup_service_type
+ setup_command_line
+ setup_http
+ setup_httpd
+ setup_gqtp
+end
+
+def autoconf
+ results = []
+
+ if @database_path and File.exist?(@database_path)
+ results << run_command_line(@database_path, "status")
+ end
+
+ if @http_host and @http_port
+ results << run_http(@http_host, @http_port, "status")
+ end
+
+ if @httpd_host and @httpd_port
+ results << run_http(@httpd_host, @httpd_port, "status")
+ end
+
+ if @gqtp_host and @gqtp_port
+ results << run_gqtp(@gqtp_host, @gqtp_port, "status")
+ end
+
+ if results.any? {|success, _| success}
+ puts("yes")
+ exit(true)
+ else
+ errors = results.collect do |_, result|
+ result
+ end
+ error_detail = errors.join(", ")
+ puts("no (#{error_detail})")
+ exit(false)
+ end
+end
+
+def suggest
+ if @http_host and @http_port
+ success, _ = run_http(@http_host, @http_port, "status")
+ puts("http") if success
+ end
+ if @httpd_host and @httpd_port
+ success, _ = run_http(@httpd_host, @httpd_port, "status")
+ puts("httpd") if success
+ end
+ if @gqtp_host and @gqtp_port
+ success, _ = run_gqtp(@gqtp_host, @gqtp_port, "status")
+ puts("gqtp") if success
+ end
+ exit(true)
+end
+
+setup
+case command
+when "autoconf", "detect"
+ autoconf
+when "suggest"
+ suggest
+when "config"
+ if label.nil?
+ title = "groonga: number of records"
+ else
+ title = "groonga: #{label}: number of records"
+ end
+ puts(<<EOF)
+graph_title #{title}
+graph_vlabel records
+graph_category groonga
+graph_info number of records in groonga table
+EOF
+ table_list.each do |table|
+ name = table["name"]
+ puts(<<EOF)
+
+#{name}.label #{name}
+#{name}.type GAUGE
+EOF
+ end
+ exit(true)
+end
+
+table_list.each do |table|
+ name = table["name"]
+ success, body = run("select", "--table", name, "--limit", "0")
+ unless success
+ puts("error: #{body}")
+ exit(false)
+ end
+ n_records = body[0][0][0]
+ puts(<<EOF)
+#{name}.value #{n_records}
+EOF
+end
diff --git a/storage/mroonga/vendor/groonga/data/munin/groonga_query_performance_ b/storage/mroonga/vendor/groonga/data/munin/groonga_query_performance_
new file mode 100755
index 00000000000..1e3a33b78a3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/munin/groonga_query_performance_
@@ -0,0 +1,198 @@
+#!/usr/bin/env ruby
+
+#%# family=auto
+#%# capabilities=autoconf suggest
+
+require 'English'
+require 'strscan'
+
+label = ENV["label"]
+
+command = ARGV.shift
+
+def autoconf
+ query_log_path_variable_names = [
+ "query_log_path",
+ "log_path", # For backward compatibility. Remove me when 5.0.0.
+ "http_query_log_path",
+ "httpd_query_log_path",
+ "gqtp_query_log_path",
+ ]
+ query_log_paths = query_log_path_variable_names.collect do |variable_name|
+ ENV[variable_name]
+ end
+ query_log_paths = query_log_paths.compact
+ if query_log_paths.empty?
+ variable_names = query_log_path_variable_names.collect do |name|
+ "env.#{name}"
+ end
+ variable_names_label = variable_names[0..-2].join(", ")
+ variable_names_label << " and/or #{variable_names.last}"
+ puts("no (No query log path is specified. Specify #{variable_names_label}.)")
+ exit(false)
+ end
+ query_log_paths.each do |query_log_path|
+ next unless File.exist?(query_log_path)
+ puts("yes")
+ exit(true)
+ end
+ query_log_paths_label = query_log_paths.join(", ")
+ puts("no (All query log paths don't exist: #{query_log_paths_label})")
+ exit(false)
+end
+
+def suggest
+ exist_p = lambda do |variable_name|
+ query_log_path = ENV[variable_name]
+ query_log_path and File.exist?(query_log_path)
+ end
+ if exist_p.call("http_query_log_path")
+ puts("http")
+ end
+ if exist_p.call("httpd_query_log_path")
+ puts("httpd")
+ end
+ if exist_p.call("gqtp_query_log_path")
+ puts("gqtp")
+ end
+ exit(true)
+end
+
+def target_query_log_path
+ if /_([^_]+)\z/ =~ $0
+ service_type = $1
+ query_log_path_variable_name = "#{service_type}_query_log_path"
+ query_log_path = ENV[query_log_path_variable_name]
+ else
+ query_log_path_variable_name = "query_log_path"
+ query_log_path = ENV[query_log_path_variable_name]
+ # For backward compatibility. Remove me when 5.0.0.
+ query_log_path ||= ENV["log_file"]
+ end
+
+ if query_log_path.nil?
+ key = "env.#{query_log_path_variable_name}"
+ $stderr.puts("Query log path isn't specified by #{key}")
+ exit(false)
+ end
+
+ unless File.exist?(query_log_path)
+ $stderr.puts("Query log path doesn't exist: #{query_log_path}")
+ exit(false)
+ end
+
+ query_log_path
+end
+
+class ReverseLineReader
+ def initialize(io)
+ @io = io
+ @io.seek(0, IO::SEEK_END)
+ @buffer = ""
+ @data = ""
+ end
+
+ def each
+ separator = $/
+ separator_length = separator.length
+ while read_to_buffer
+ loop do
+ index = @buffer.rindex(separator, @buffer.length - 1 - separator_length)
+ break if index.nil? or index.zero?
+ last_line = @buffer.slice!((index + separator_length)..-1)
+ yield(last_line)
+ end
+ end
+ yield(@buffer) unless @buffer.empty?
+ end
+
+ private
+ BYTES_PER_READ = 4096
+ def read
+ position = @io.pos
+ if position < BYTES_PER_READ
+ bytes_per_read = position
+ else
+ bytes_per_read = BYTES_PER_READ
+ end
+
+ if bytes_per_read.zero?
+ @data.replace("")
+ else
+ @io.seek(-bytes_per_read, IO::SEEK_CUR)
+ @io.read(bytes_per_read, @data)
+ @io.seek(-bytes_per_read, IO::SEEK_CUR)
+ end
+
+ @data
+ end
+
+ def read_to_buffer
+ data = read
+ if data.empty?
+ false
+ else
+ @buffer.insert(0, data)
+ true
+ end
+ end
+end
+
+case command
+when "autoconf", "detect"
+ autoconf
+when "suggest"
+ suggest
+when "config"
+ if label.nil?
+ title = "groonga: query performance"
+ else
+ title = "groonga: #{label}: query performance"
+ end
+ puts(<<EOF)
+graph_title #{title}
+graph_vlabel seconds
+graph_category groonga
+graph_info groonga query performance
+
+longest.label Longest
+average.label Average
+median.label Median
+EOF
+ exit(true)
+end
+
+span = 60 * 5 # 5min
+mega = 1_000_000.0
+now = Time.now
+elapsed_times = []
+File.open(target_query_log_path) do |log_file|
+ ReverseLineReader.new(log_file).each do |line|
+ case line
+ when /\A(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)\.(\d+)\|([\da-f])+\|<(\d+) rc=0$/
+ _, year, month, day, hour, minutes, seconds, milliseconds,
+ context, elapsed = $LAST_MATCH_INFO.to_a
+ time_stamp = Time.local(year, month, day,
+ hour, minutes, seconds, milliseconds)
+ difference = now - time_stamp
+ break if difference > span
+ elapsed_in_micro_seconds = elapsed.to_i / mega
+ elapsed_times << elapsed_in_micro_seconds
+ end
+ end
+end
+
+sorted_elapsed_times = elapsed_times.sort
+if sorted_elapsed_times.empty?
+ longest = 0
+ average = 0
+ median = 0
+else
+ longest = sorted_elapsed_times.last
+ average = sorted_elapsed_times.inject(&:+) / sorted_elapsed_times.size.to_f
+ median = sorted_elapsed_times[sorted_elapsed_times.size / 2]
+end
+
+puts("longest.value #{longest}")
+puts("average.value #{average}")
+puts("median.value #{median}")
diff --git a/storage/mroonga/vendor/groonga/data/munin/groonga_status_ b/storage/mroonga/vendor/groonga/data/munin/groonga_status_
new file mode 100755
index 00000000000..22cdde272db
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/munin/groonga_status_
@@ -0,0 +1,176 @@
+#!/usr/bin/env ruby
+
+#%# family=auto
+#%# capabilities=autoconf suggest
+
+require 'net/http'
+begin
+ require 'json'
+rescue LoadError
+ require 'rubygems'
+ require 'json'
+end
+
+label = ENV["label"]
+@groonga = ENV["groonga"] || "groonga"
+@default_host = ENV["host"] || "127.0.0.1"
+@default_http_port = 10041
+@default_gqtp_port = 10043
+
+command = ARGV.shift
+
+def parse(success, result)
+ if success
+ begin
+ status, body = JSON.parse(result)
+ return_code, start_time, elapsed, error_message = status
+ if return_code.zero?
+ [success, body]
+ else
+ [false, error_message]
+ end
+ rescue JSON::ParserError
+ [false, $!.message]
+ end
+ else
+ [success, result]
+ end
+end
+
+def run_http(host, port, command)
+ path = "/d/#{command}"
+ response = Net::HTTP.start(host, port) do |http|
+ http.get(path)
+ end
+ if response.is_a?(Net::HTTPSuccess)
+ parse(true, response.body)
+ else
+ [false, "#{response.code}: #{response.body}"]
+ end
+end
+
+def run_gqtp(host, port, command)
+ groonga = "#{@groonga} -p #{port} -c #{host}"
+ result = `#{groonga} #{command} 2>&1`
+ parse($?.success?, result)
+end
+
+def run(command)
+ case @service_type
+ when "http"
+ run_http(@http_host, @http_port, command)
+ when "httpd"
+ run_http(@httpd_host, @httpd_port, command)
+ when "gqtp"
+ run_gqtp(@gqtp_host, @gqtp_port, command)
+ else
+ [false, "unknown service type: #{@service_type}"]
+ end
+end
+
+def setup_service_type
+ if /_([^_]+)\z/ =~ $0
+ @service_type = $1
+ else
+ @service_type = nil
+ end
+end
+
+def setup_http
+ @http_host = ENV["http_host"] || @default_host
+ @http_port = (ENV["http_port"] || @default_http_port).to_i
+end
+
+def setup_httpd
+ @httpd_host = ENV["httpd_host"] || @default_host
+ @httpd_port = (ENV["httpd_port"] || @default_http_port).to_i
+end
+
+def setup_gqtp
+ @gqtp_host = ENV["gqtp_host"] || @default_host
+ @gqtp_port = (ENV["gqtp_port"] || @default_gqtp_port).to_i
+end
+
+def setup
+ setup_service_type
+ setup_http
+ setup_httpd
+ setup_gqtp
+end
+
+def autoconf
+ results = []
+
+ if @http_host and @http_port
+ results << run_http(@http_host, @http_port, "status")
+ end
+
+ if @httpd_host and @httpd_port
+ results << run_http(@httpd_host, @httpd_port, "status")
+ end
+
+ if @gqtp_host and @gqtp_port
+ results << run_gqtp(@gqtp_host, @gqtp_port, "status")
+ end
+
+ if results.any? {|success, _| success}
+ puts("yes")
+ exit(true)
+ else
+ errors = results.collect do |_, result|
+ result
+ end
+ error_detail = errors.join(", ")
+ puts("no (#{error_detail})")
+ exit(false)
+ end
+end
+
+def suggest
+ if @http_host and @http_port
+ success, _ = run_http(@http_host, @http_port, "status")
+ puts("http") if success
+ end
+ if @httpd_host and @httpd_port
+ success, _ = run_http(@httpd_host, @httpd_port, "status")
+ puts("httpd") if success
+ end
+ if @gqtp_host and @gqtp_port
+ success, _ = run_gqtp(@gqtp_host, @gqtp_port, "status")
+ puts("gqtp") if success
+ end
+ exit(true)
+end
+
+setup
+case command
+when "autoconf", "detect"
+ autoconf
+when "suggest"
+ suggest
+when "config"
+ if label.nil?
+ title = "groonga: status"
+ else
+ title = "groonga: #{label}: status"
+ end
+ puts(<<EOF)
+graph_title #{title}
+graph_vlabel status
+graph_category groonga
+graph_info groonga status
+
+alloc_count.label alloc count
+alloc_count.type GAUGE
+EOF
+ exit(true)
+end
+
+success, body = run("status")
+unless success
+ puts("error: #{body}")
+ exit(false)
+end
+puts(<<EOF)
+alloc_count.value #{body["alloc_count"]}
+EOF
diff --git a/storage/mroonga/vendor/groonga/data/munin/groonga_throughput_ b/storage/mroonga/vendor/groonga/data/munin/groonga_throughput_
new file mode 100755
index 00000000000..241ae8e5794
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/munin/groonga_throughput_
@@ -0,0 +1,176 @@
+#!/usr/bin/env ruby
+
+#%# family=auto
+#%# capabilities=autoconf suggest
+
+require 'net/http'
+begin
+ require 'json'
+rescue LoadError
+ require 'rubygems'
+ require 'json'
+end
+
+label = ENV["label"]
+@groonga = ENV["groonga"] || "groonga"
+@default_host = ENV["host"] || "127.0.0.1"
+@default_http_port = 10041
+@default_gqtp_port = 10043
+
+command = ARGV.shift
+
+def parse(success, result)
+ if success
+ begin
+ status, body = JSON.parse(result)
+ return_code, start_time, elapsed, error_message = status
+ if return_code.zero?
+ [success, body]
+ else
+ [false, error_message]
+ end
+ rescue JSON::ParserError
+ [false, $!.message]
+ end
+ else
+ [success, result]
+ end
+end
+
+def run_http(host, port, command)
+ path = "/d/#{command}"
+ response = Net::HTTP.start(host, port) do |http|
+ http.get(path)
+ end
+ if response.is_a?(Net::HTTPSuccess)
+ parse(true, response.body)
+ else
+ [false, "#{response.code}: #{response.body}"]
+ end
+end
+
+def run_gqtp(host, port, command)
+ groonga = "#{@groonga} -p #{port} -c #{host}"
+ result = `#{groonga} #{command} 2>&1`
+ parse($?.success?, result)
+end
+
+def run(command)
+ case @service_type
+ when "http"
+ run_http(@http_host, @http_port, command)
+ when "httpd"
+ run_http(@httpd_host, @httpd_port, command)
+ when "gqtp"
+ run_gqtp(@gqtp_host, @gqtp_port, command)
+ else
+ [false, "unknown service type: #{@service_type}"]
+ end
+end
+
+def setup_service_type
+ if /_([^_]+)\z/ =~ $0
+ @service_type = $1
+ else
+ @service_type = nil
+ end
+end
+
+def setup_http
+ @http_host = ENV["http_host"] || @default_host
+ @http_port = (ENV["http_port"] || @default_http_port).to_i
+end
+
+def setup_httpd
+ @httpd_host = ENV["httpd_host"] || @default_host
+ @httpd_port = (ENV["httpd_port"] || @default_http_port).to_i
+end
+
+def setup_gqtp
+ @gqtp_host = ENV["gqtp_host"] || @default_host
+ @gqtp_port = (ENV["gqtp_port"] || @default_gqtp_port).to_i
+end
+
+def setup
+ setup_service_type
+ setup_http
+ setup_httpd
+ setup_gqtp
+end
+
+def autoconf
+ results = []
+
+ if @http_host and @http_port
+ results << run_http(@http_host, @http_port, "status")
+ end
+
+ if @httpd_host and @httpd_port
+ results << run_http(@httpd_host, @httpd_port, "status")
+ end
+
+ if @gqtp_host and @gqtp_port
+ results << run_gqtp(@gqtp_host, @gqtp_port, "status")
+ end
+
+ if results.any? {|success, _| success}
+ puts("yes")
+ exit(true)
+ else
+ errors = results.collect do |_, result|
+ result
+ end
+ error_detail = errors.join(", ")
+ puts("no (#{error_detail})")
+ exit(false)
+ end
+end
+
+def suggest
+ if @http_host and @http_port
+ success, _ = run_http(@http_host, @http_port, "status")
+ puts("http") if success
+ end
+ if @httpd_host and @httpd_port
+ success, _ = run_http(@httpd_host, @httpd_port, "status")
+ puts("httpd") if success
+ end
+ if @gqtp_host and @gqtp_port
+ success, _ = run_gqtp(@gqtp_host, @gqtp_port, "status")
+ puts("gqtp") if success
+ end
+ exit(true)
+end
+
+setup
+case command
+when "autoconf", "detect"
+ autoconf
+when "suggest"
+ suggest
+when "config"
+ if label.nil?
+ title = "groonga: throughput"
+ else
+ title = "groonga: #{label}: throughput"
+ end
+ puts(<<EOF)
+graph_title #{title}
+graph_vlabel queries per ${graph_period}
+graph_category groonga
+graph_info groonga throughput
+
+n_queries.label N queries
+n_queries.type COUNTER
+EOF
+ exit(true)
+end
+
+success, body = run("status")
+unless success
+ puts("error: #{body}")
+ exit(false)
+end
+puts(<<EOF)
+n_queries.value #{body["n_queries"]}
+EOF
diff --git a/storage/mroonga/vendor/groonga/data/scripts/Makefile.am b/storage/mroonga/vendor/groonga/data/scripts/Makefile.am
new file mode 100644
index 00000000000..a9b50395c3e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/scripts/Makefile.am
@@ -0,0 +1 @@
+dist_sbin_SCRIPTS = groonga-httpd-restart
diff --git a/storage/mroonga/vendor/groonga/data/scripts/groonga-httpd-restart.in b/storage/mroonga/vendor/groonga/data/scripts/groonga-httpd-restart.in
new file mode 100755
index 00000000000..6fe9c88f929
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/scripts/groonga-httpd-restart.in
@@ -0,0 +1,106 @@
+#!/bin/sh
+# -*- indent-tabs-mode: nil; sh-indentation: 4 -*-
+#
+# Copyright(C) 2012-2013 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+prefix=@prefix@
+
+SERVICE_NAME=groonga-httpd
+GROONGA_HTTPD=${SERVICE_NAME}
+PID_FILE=@localstatedir@/run/groonga/${GROONGA_HTTPD}.pid
+TIMEOUT=3
+
+# Source configuration.
+if [ -f /etc/default/${SERVICE_NAME} ]; then
+ . /etc/default/${SERVICE_NAME}
+elif [ -f /etc/sysconfig/${SERVICE_NAME} ]; then
+ . /etc/sysconfig/${SERVICE_NAME}
+fi
+
+OLD_PID_FILE=${PID_FILE}.oldbin
+
+wait_until () {
+ for n in $(seq ${TIMEOUT}); do
+ if "$@"; then
+ return 0
+ fi
+ sleep 1
+ done
+ return 1
+}
+
+wait_while () {
+ for n in $(seq ${TIMEOUT}); do
+ if ! "$@"; then
+ return 0
+ fi
+ sleep 1
+ done
+ return 1
+}
+
+start_master () {
+ local pid=$1
+ kill -USR2 ${pid}
+}
+
+switch_worker () {
+ local pid=$1
+ kill -WINCH ${pid}
+}
+
+stop_master () {
+ local pid=$1
+ kill -QUIT ${pid}
+}
+
+if [ ! -f "${PID_FILE}" ]; then
+ echo "PID file isn't found. groonga-httpd may not be running: <${PID_FILE}>"
+ exit 1
+fi
+
+OLD_PID=$(cat ${PID_FILE})
+start_master ${OLD_PID}
+
+if ! wait_until [ -f ${OLD_PID_FILE} ]; then
+ echo "Failed to create old PID file: <${PID_FILE}>"
+ exit 1
+fi
+
+if ! wait_until [ -f ${PID_FILE} ]; then
+ echo "Failed to start new groonga-httpd master."
+ exit 1
+fi
+
+NEW_PID=$(cat ${PID_FILE})
+
+OLD_WORKER_PROCESSES=$(pgrep -P ${OLD_PID} | grep -v ${NEW_PID})
+switch_worker ${OLD_PID}
+for pid in ${OLD_WORKER_PROCESSES}; do
+ wait_while ps --pid=${pid} > /dev/null
+done
+OLD_WORKER_PROCESSES=$(pgrep -P ${OLD_PID} | grep -v ${NEW_PID})
+if [ -n "${OLD_WORKER_PROCESSES}" ]; then
+ echo "Failed to stop old groonga-httpd worker process."
+ stop_master ${NEW_PID}
+ echo "Rollback to old groonga-httpd master."
+ exit 2
+fi
+
+stop_master ${OLD_PID}
+exit $?
+
diff --git a/storage/mroonga/vendor/groonga/data/synonyms.tsv b/storage/mroonga/vendor/groonga/data/synonyms.tsv
new file mode 100644
index 00000000000..17e2923440c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/synonyms.tsv
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+#
+# key[TAB]synonym1[TAB]synonym2[TAB]...
+#
+#groonga groonga rroonga mroonga
diff --git a/storage/mroonga/vendor/groonga/data/systemd/Makefile.am b/storage/mroonga/vendor/groonga/data/systemd/Makefile.am
new file mode 100644
index 00000000000..5e83c678fbe
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/systemd/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = fedora
diff --git a/storage/mroonga/vendor/groonga/data/systemd/fedora/Makefile.am b/storage/mroonga/vendor/groonga/data/systemd/fedora/Makefile.am
new file mode 100644
index 00000000000..97caba36347
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/systemd/fedora/Makefile.am
@@ -0,0 +1,13 @@
+SUBDIRS = sysconfig
+
+services = \
+ groonga-server-http.service \
+ groonga-server-gqtp.service \
+ groonga-httpd.service
+
+if FEDORA_PLATFORM
+unitdir = /usr/lib/systemd/system
+dist_unit_DATA = $(services)
+else
+EXTRA_DIST = $(services)
+endif
diff --git a/storage/mroonga/vendor/groonga/data/systemd/fedora/groonga-httpd.service b/storage/mroonga/vendor/groonga/data/systemd/fedora/groonga-httpd.service
new file mode 100644
index 00000000000..96129dcbe68
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/systemd/fedora/groonga-httpd.service
@@ -0,0 +1,14 @@
+[Unit]
+Description=Groonga HTTP Server
+After=network.target
+Conflicts=groonga.service
+
+[Service]
+Type=forking
+User=root
+Group=root
+ExecStart=/usr/sbin/groonga-httpd
+ExecStop=/usr/sbin/groonga-httpd -s stop
+
+[Install]
+WantedBy=multi-user.target
diff --git a/storage/mroonga/vendor/groonga/data/systemd/fedora/groonga-server-gqtp.service b/storage/mroonga/vendor/groonga/data/systemd/fedora/groonga-server-gqtp.service
new file mode 100644
index 00000000000..666c1609d42
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/systemd/fedora/groonga-server-gqtp.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Groonga Text Searching Engine
+After=network.target
+
+[Service]
+Type=forking
+EnvironmentFile=-/etc/sysconfig/groonga-server-gqtp
+User=groonga
+Group=groonga
+ExecStart=/usr/bin/groonga -d --pid-path /var/run/groonga/groonga-gqtp.pid --bind-address $ADDRESS --log-path $LOG_PATH --query-log-path $QUERY_LOG_PATH --protocol $PROTOCOL --port $PORT $DATABASE
+
+[Install]
+WantedBy=multi-user.target
diff --git a/storage/mroonga/vendor/groonga/data/systemd/fedora/groonga-server-http.service b/storage/mroonga/vendor/groonga/data/systemd/fedora/groonga-server-http.service
new file mode 100644
index 00000000000..6638e03ae3c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/systemd/fedora/groonga-server-http.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Groonga Text Searching Engine
+After=network.target
+
+[Service]
+Type=forking
+EnvironmentFile=-/etc/sysconfig/groonga-server-http
+User=groonga
+Group=groonga
+ExecStart=/usr/bin/groonga -d --pid-path /var/run/groonga/groonga-http.pid --bind-address $ADDRESS --log-path $LOG_PATH --query-log-path $QUERY_LOG_PATH --protocol $PROTOCOL --port $PORT $DATABASE
+
+[Install]
+WantedBy=multi-user.target
diff --git a/storage/mroonga/vendor/groonga/data/systemd/fedora/sysconfig/Makefile.am b/storage/mroonga/vendor/groonga/data/systemd/fedora/sysconfig/Makefile.am
new file mode 100644
index 00000000000..5de8250e5c7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/systemd/fedora/sysconfig/Makefile.am
@@ -0,0 +1,8 @@
+data = groonga-server-http groonga-server-gqtp
+
+if FEDORA_PLATFORM
+sysconfigdir = $(sysconfdir)/sysconfig
+dist_sysconfig_DATA = $(data)
+else
+EXTRA_DIST = $(data)
+endif
diff --git a/storage/mroonga/vendor/groonga/data/systemd/fedora/sysconfig/groonga-server-gqtp b/storage/mroonga/vendor/groonga/data/systemd/fedora/sysconfig/groonga-server-gqtp
new file mode 100644
index 00000000000..f9fb2c70660
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/systemd/fedora/sysconfig/groonga-server-gqtp
@@ -0,0 +1,9 @@
+# Default
+ADDRESS=127.0.0.1
+DATABASE=/var/lib/groonga/db/db
+LOG_PATH=/var/log/groonga/groonga-gqtp.log
+QUERY_LOG_PATH=/var/log/groonga/query-gqtp.log
+PROTOCOL=gqtp
+PORT=10043
+GRN_QUERY_EXPANDER_TSV_SYNONYMS_FILE=/usr/share/groonga/synonyms.tsv
+#GRN_JA_SKIP_SAME_VALUE_PUT=yes
diff --git a/storage/mroonga/vendor/groonga/data/systemd/fedora/sysconfig/groonga-server-http b/storage/mroonga/vendor/groonga/data/systemd/fedora/sysconfig/groonga-server-http
new file mode 100644
index 00000000000..0b917b4732b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/systemd/fedora/sysconfig/groonga-server-http
@@ -0,0 +1,9 @@
+# Default
+ADDRESS=127.0.0.1
+DATABASE=/var/lib/groonga/db/db
+LOG_PATH=/var/log/groonga/groonga-http.log
+QUERY_LOG_PATH=/var/log/groonga/query-http.log
+PROTOCOL=http
+PORT=10041
+GRN_QUERY_EXPANDER_TSV_SYNONYMS_FILE=/usr/share/groonga/synonyms.tsv
+#GRN_JA_SKIP_SAME_VALUE_PUT=yes
diff --git a/storage/mroonga/vendor/groonga/data/travis/setup.sh b/storage/mroonga/vendor/groonga/data/travis/setup.sh
new file mode 100755
index 00000000000..285776ee590
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/data/travis/setup.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+#
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+# set -x
+set -e
+
+if [ "$GROONGA_MASTER" = "yes" ]; then
+ sudo apt-get install -qq -y -V autotools-dev pkg-config libmecab-dev \
+ libmsgpack-dev libevent-dev
+ git clone --depth 1 https://github.com/groonga/groonga.git
+ cd groonga
+ ./autogen.sh
+ ./configure --prefix=/usr --localstatedir=/var --enable-debug
+ make -j$(grep '^processor' /proc/cpuinfo | wc -l) > /dev/null
+ sudo make install > /dev/null
+ cd ..
+else
+ sudo apt-get purge libzmq3
+
+ distribution=$(lsb_release --short --id | tr 'A-Z' 'a-z')
+ case $distribution in
+ debian)
+ code_name=$(lsb_release --short --codename)
+ component=main
+ apt_url_base=http://packages.groonga.org
+ cat <<EOF | sudo tee /etc/apt/sources.list.d/groonga.list
+deb ${apt_url_base}/${distribution}/ ${code_name} ${component}
+deb-src ${apt_url_base}/${distribution}/ ${code_name} ${component}
+EOF
+ sudo apt-get update -qq
+ sudo apt-get install -qq -y --allow-unauthenticated groonga-keyring
+ ;;
+ ubuntu)
+ sudo apt-get install -qq -y -V software-properties-common
+ sudo add-apt-repository -y ppa:groonga/ppa
+ ;;
+ esac
+
+ sudo apt-get update -qq
+ sudo apt-get install -qq -y -V groonga libgroonga-dev
+fi
diff --git a/storage/mroonga/vendor/groonga/examples/Makefile.am b/storage/mroonga/vendor/groonga/examples/Makefile.am
new file mode 100644
index 00000000000..f436342d053
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = dictionary
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/Makefile.am b/storage/mroonga/vendor/groonga/examples/dictionary/Makefile.am
new file mode 100644
index 00000000000..8d71ed61944
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/Makefile.am
@@ -0,0 +1,34 @@
+SUBDIRS = \
+ edict \
+ eijiro \
+ gene95 \
+ jmdict
+
+dist_examples_dictionary_SCRIPTS = \
+ init-db.sh
+
+nobase_dist_examples_dictionary_DATA = \
+ readme.txt \
+ $(html_files)
+
+# find html -type f | sort | sed -e 's,^,\t,g'
+html_files = \
+ html/css/dictionary.css \
+ html/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png \
+ html/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png \
+ html/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png \
+ html/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png \
+ html/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png \
+ html/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png \
+ html/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png \
+ html/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png \
+ html/css/smoothness/images/ui-icons_222222_256x240.png \
+ html/css/smoothness/images/ui-icons_2e83ff_256x240.png \
+ html/css/smoothness/images/ui-icons_454545_256x240.png \
+ html/css/smoothness/images/ui-icons_888888_256x240.png \
+ html/css/smoothness/images/ui-icons_cd0a0a_256x240.png \
+ html/css/smoothness/jquery-ui-1.8.12.custom.css \
+ html/index.html \
+ html/js/dictionary.js \
+ html/js/jquery-1.7.2.min.js \
+ html/js/jquery-ui-1.8.18.custom.min.js
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/edict/Makefile.am b/storage/mroonga/vendor/groonga/examples/dictionary/edict/Makefile.am
new file mode 100644
index 00000000000..376f9d520ab
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/edict/Makefile.am
@@ -0,0 +1,4 @@
+edictdir = $(examples_dictionarydir)/edict
+dist_edict_SCRIPTS = \
+ edict2grn.rb \
+ edict-import.sh
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/edict/edict-import.sh b/storage/mroonga/vendor/groonga/examples/dictionary/edict/edict-import.sh
new file mode 100755
index 00000000000..b98397be05a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/edict/edict-import.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+base_dir=$(dirname $0)
+
+if [ 1 != $# -a 2 != $# ]; then
+ echo "usage: $0 db_path [edict.gz_path]"
+ exit 1
+fi
+
+if [ -z $2 ]; then
+ edict_gz=edict.gz
+ if [ ! -f $edict_gz ]; then
+ wget -O $edict_gz http://ftp.monash.edu.au/pub/nihongo/edict.gz
+ fi
+else
+ edict_gz=$2
+fi
+
+if zcat $edict_gz | ${base_dir}/edict2grn.rb | groonga $1 > /dev/null; then
+ echo "edict data loaded."
+fi
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/edict/edict2grn.rb b/storage/mroonga/vendor/groonga/examples/dictionary/edict/edict2grn.rb
new file mode 100755
index 00000000000..664b12c2148
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/edict/edict2grn.rb
@@ -0,0 +1,56 @@
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
+
+$KCODE = 'u'
+
+require 'English'
+require 'kconv'
+
+class String
+ def to_json
+ a = split(//).map {|char|
+ case char
+ when '"' then '\\"'
+ when '\\' then '\\\\'
+ when "\b" then '\b'
+ when "\f" then '\f'
+ when "\n" then '\n'
+ when "\r" then ''
+ when "\t" then '\t'
+ else char
+ end
+ }
+ "\"#{a.join('')}\""
+ end
+end
+
+class Array
+ def to_json
+ '[' + map {|element|
+ element.to_json
+ }.join(',') + ']'
+ end
+end
+
+puts <<END
+column_create item_dictionary edict_desc COLUMN_SCALAR ShortText
+column_create bigram item_dictionary_edict_desc COLUMN_INDEX|WITH_POSITION item_dictionary edict_desc
+load --table item_dictionary
+[["_key","edict_desc","kana"],
+END
+
+while !STDIN.eof?
+ line = Kconv.toutf8(gets)
+ key, body = line.strip.split('/', 2)
+ key = key.strip
+ if /\s*\[(.+)\]\z/ =~ key
+ key = $PREMATCH
+ reading = $1
+ body = "[#{reading}] #{body}"
+ kana = NKF.nkf("-Ww --katakana", reading)
+ else
+ kana = NKF.nkf("-Ww --katakana", key)
+ end
+ puts [key, body, kana].to_json
+end
+puts ']'
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/eijiro/Makefile.am b/storage/mroonga/vendor/groonga/examples/dictionary/eijiro/Makefile.am
new file mode 100644
index 00000000000..4059a529cef
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/eijiro/Makefile.am
@@ -0,0 +1,4 @@
+eijirodir = $(examples_dictionarydir)/eijiro
+dist_eijiro_SCRIPTS = \
+ eijiro2grn.rb \
+ eijiro-import.sh
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/eijiro/eijiro-import.sh b/storage/mroonga/vendor/groonga/examples/dictionary/eijiro/eijiro-import.sh
new file mode 100755
index 00000000000..4042d7fbf56
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/eijiro/eijiro-import.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+base_dir=$(dirname $0)
+
+if [ 2 != $# ]; then
+ echo "usage: $0 db_path eijiro.csv_path"
+ exit 1
+fi
+
+if iconv -f UCS2 -t UTF8 $2 | ${base_dir}/eijiro2grn.rb | groonga $1 > /dev/null; then
+ echo "eijiro data loaded."
+fi
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/eijiro/eijiro2grn.rb b/storage/mroonga/vendor/groonga/examples/dictionary/eijiro/eijiro2grn.rb
new file mode 100755
index 00000000000..62c1e1309bf
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/eijiro/eijiro2grn.rb
@@ -0,0 +1,61 @@
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
+
+$KCODE = 'u'
+
+require 'rubygems'
+require 'fastercsv'
+
+class String
+ def to_json
+ a = split(//).map {|char|
+ case char
+ when '"' then '\\"'
+ when '\\' then '\\\\'
+ when "\b" then '\b'
+ when "\f" then '\f'
+ when "\n" then '\n'
+ when "\r" then ''
+ when "\t" then '\t'
+ else char
+ end
+ }
+ "\"#{a.join('')}\""
+ end
+end
+
+class Array
+ def to_json
+ '[' + map {|element|
+ element.to_json
+ }.join(',') + ']'
+ end
+end
+
+puts <<END
+column_create item_dictionary eijiro_trans COLUMN_SCALAR ShortText
+column_create item_dictionary eijiro_exp COLUMN_SCALAR ShortText
+column_create item_dictionary eijiro_level COLUMN_SCALAR Int32
+column_create item_dictionary eijiro_memory COLUMN_SCALAR Int32
+column_create item_dictionary eijiro_modify COLUMN_SCALAR Int32
+column_create item_dictionary eijiro_pron COLUMN_SCALAR ShortText
+column_create item_dictionary eijiro_filelink COLUMN_SCALAR ShortText
+column_create bigram item_dictionary_eijiro_trans COLUMN_INDEX|WITH_POSITION item_dictionary eijiro_trans
+load --table item_dictionary
+[["_key","norm","eijiro_trans","eijiro_exp","eijiro_level","eijiro_memory","eijiro_modify","eijiro_pron","eijiro_filelink","kana"],
+END
+
+n = 0
+FasterCSV.new(ARGF, :row_sep => "\r\n").each {|l|
+ if n > 0
+ keyword,word,trans,exp,level,memory,modify,pron,filelink = l
+ kana = ''
+ if trans =~ /【@】(.*?)(【|$)/
+ kana = $1.split("、")
+ end
+ puts [word,keyword,trans,exp,level,memory,modify,pron,filelink,kana].map{|e| e || ''}.to_json
+ end
+ n += 1
+}
+
+puts "]"
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/gene95/Makefile.am b/storage/mroonga/vendor/groonga/examples/dictionary/gene95/Makefile.am
new file mode 100644
index 00000000000..e89f13f595c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/gene95/Makefile.am
@@ -0,0 +1,4 @@
+gene95dir = $(examples_dictionarydir)/gene95
+dist_gene95_SCRIPTS = \
+ gene2grn.rb \
+ gene-import.sh
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/gene95/gene-import.sh b/storage/mroonga/vendor/groonga/examples/dictionary/gene95/gene-import.sh
new file mode 100755
index 00000000000..488d6c83adc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/gene95/gene-import.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+base_dir=$(dirname $0)
+
+if [ 1 != $# -a 2 != $# ]; then
+ echo "usage: $0 db_path [gene.txt_path]"
+ exit 1
+fi
+
+if [ -z $2 ]; then
+ dictionary_dir=gene95-dictionary
+ gene_txt=${dictionary_dir}/gene.txt
+ if [ ! -f $gene_txt ]; then
+ gene95_tar_gz=gene95.tar.gz
+ wget -O $gene95_tar_gz \
+ http://www.namazu.org/~tsuchiya/sdic/data/gene95.tar.gz
+ mkdir -p ${dictionary_dir}
+ tar xvzf ${gene95_tar_gz} -C ${dictionary_dir}
+ fi
+else
+ gene_txt=$2
+fi
+
+if cat $gene_txt | ${base_dir}/gene2grn.rb | groonga $1 > /dev/null; then
+ echo "gene95 data loaded."
+fi
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/gene95/gene2grn.rb b/storage/mroonga/vendor/groonga/examples/dictionary/gene95/gene2grn.rb
new file mode 100755
index 00000000000..0d10cfd1085
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/gene95/gene2grn.rb
@@ -0,0 +1,46 @@
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
+
+$KCODE = 'u'
+
+require 'kconv'
+
+class String
+ def to_json
+ a = split(//).map {|char|
+ case char
+ when '"' then '\\"'
+ when '\\' then '\\\\'
+ when "\b" then '\b'
+ when "\f" then '\f'
+ when "\n" then '\n'
+ when "\r" then ''
+ when "\t" then '\t'
+ else char
+ end
+ }
+ "\"#{a.join('')}\""
+ end
+end
+
+class Array
+ def to_json
+ '[' + map {|element|
+ element.to_json
+ }.join(',') + ']'
+ end
+end
+
+puts <<END
+column_create item_dictionary gene95_desc COLUMN_SCALAR ShortText
+column_create bigram item_dictionary_gene95_desc COLUMN_INDEX|WITH_POSITION item_dictionary gene95_desc
+load --table item_dictionary
+[["_key","gene95_desc"],
+END
+
+while !STDIN.eof?
+ key = Kconv.toutf8(gets.strip)
+ body = Kconv.toutf8(gets.strip)
+ puts [key, body].to_json
+end
+puts ']'
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/dictionary.css b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/dictionary.css
new file mode 100644
index 00000000000..72b5a6749b3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/dictionary.css
@@ -0,0 +1,3 @@
+#result {
+ margin-top: 7em;
+}
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png
new file mode 100644
index 00000000000..5b5dab2ab7b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png
new file mode 100644
index 00000000000..ac8b229af95
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png
new file mode 100644
index 00000000000..ad3d6346e00
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png
new file mode 100644
index 00000000000..42ccba269b6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png
new file mode 100644
index 00000000000..5a46b47cb16
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png
new file mode 100644
index 00000000000..86c2baa655e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png
new file mode 100644
index 00000000000..4443fdc1a15
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png
new file mode 100644
index 00000000000..7c9fa6c6edc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_222222_256x240.png b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_222222_256x240.png
new file mode 100644
index 00000000000..b273ff111d2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_222222_256x240.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_2e83ff_256x240.png b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_2e83ff_256x240.png
new file mode 100644
index 00000000000..09d1cdc856c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_2e83ff_256x240.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_454545_256x240.png b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_454545_256x240.png
new file mode 100644
index 00000000000..59bd45b907c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_454545_256x240.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_888888_256x240.png b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_888888_256x240.png
new file mode 100644
index 00000000000..6d02426c114
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_888888_256x240.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_cd0a0a_256x240.png b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_cd0a0a_256x240.png
new file mode 100644
index 00000000000..2ab019b73ec
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/images/ui-icons_cd0a0a_256x240.png
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/jquery-ui-1.8.12.custom.css b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/jquery-ui-1.8.12.custom.css
new file mode 100644
index 00000000000..c85aabaec06
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/css/smoothness/jquery-ui-1.8.12.custom.css
@@ -0,0 +1,578 @@
+/*
+ * jQuery UI CSS Framework 1.8.12
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ */
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden { display: none; }
+.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
+.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.ui-helper-clearfix { display: inline-block; }
+/* required comment for clearfix to work in Opera \*/
+* html .ui-helper-clearfix { height:1%; }
+.ui-helper-clearfix { display:block; }
+/* end clearfix */
+.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+
+
+/*
+ * jQuery UI CSS Framework 1.8.12
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ *
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=03_highlight_soft.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=02_glass.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=02_glass.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=02_glass.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
+ */
+
+
+/* Component containers
+----------------------------------*/
+.ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; }
+.ui-widget .ui-widget { font-size: 1em; }
+.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
+.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; }
+.ui-widget-content a { color: #222222; }
+.ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; }
+.ui-widget-header a { color: #222222; }
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #555555; }
+.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; }
+.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
+.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; }
+.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: normal; color: #212121; }
+.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; }
+.ui-widget :active { outline: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x; color: #363636; }
+.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
+.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x; color: #cd0a0a; }
+.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }
+.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }
+.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
+.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); }
+.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
+.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); }
+.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); }
+.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
+
+/* positioning */
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-off { background-position: -96px -144px; }
+.ui-icon-radio-on { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; }
+.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }
+.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
+.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
+.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }
+.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
+.ui-corner-right { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
+.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
+.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; }
+
+/* Overlays */
+.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
+.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
+ * jQuery UI Resizable 1.8.12
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizable#theming
+ */
+.ui-resizable { position: relative;}
+.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;
+ /* http://bugs.jqueryui.com/ticket/7233
+ - Resizable: resizable handles fail to work in IE if transparent and content overlaps
+ */
+ background-image:url(data:);
+}
+.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
+.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
+.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
+.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
+.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
+ * jQuery UI Selectable 1.8.12
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectable#theming
+ */
+.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
+/*
+ * jQuery UI Accordion 1.8.12
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion#theming
+ */
+/* IE/Win - Fix animation bug - #4615 */
+.ui-accordion { width: 100%; }
+.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.ui-accordion .ui-accordion-li-fix { display: inline; }
+.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
+.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
+.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
+.ui-accordion .ui-accordion-content-active { display: block; }
+/*
+ * jQuery UI Autocomplete 1.8.12
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete#theming
+ */
+.ui-autocomplete { position: absolute; cursor: default; }
+
+/* workarounds */
+* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
+
+/*
+ * jQuery UI Menu 1.8.12
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Menu#theming
+ */
+.ui-menu {
+ list-style:none;
+ padding: 2px;
+ margin: 0;
+ display:block;
+ float: left;
+}
+.ui-menu .ui-menu {
+ margin-top: -3px;
+}
+.ui-menu .ui-menu-item {
+ margin:0;
+ padding: 0;
+ zoom: 1;
+ float: left;
+ clear: left;
+ width: 100%;
+}
+.ui-menu .ui-menu-item a {
+ text-decoration:none;
+ display:block;
+ padding:.2em .4em;
+ line-height:1.5;
+ zoom:1;
+}
+.ui-menu .ui-menu-item a.ui-state-hover,
+.ui-menu .ui-menu-item a.ui-state-active {
+ font-weight: normal;
+ margin: -1px;
+}
+/*
+ * jQuery UI Button 1.8.12
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button#theming
+ */
+.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
+.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
+button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
+.ui-button-icons-only { width: 3.4em; }
+button.ui-button-icons-only { width: 3.7em; }
+
+/*button text element */
+.ui-button .ui-button-text { display: block; line-height: 1.4; }
+.ui-button-text-only .ui-button-text { padding: .4em 1em; }
+.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
+.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
+.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
+.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
+/* no icon support for input elements, provide padding by default */
+input.ui-button { padding: .4em 1em; }
+
+/*button icon element(s) */
+.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
+.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
+.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
+.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+
+/*button sets*/
+.ui-buttonset { margin-right: 7px; }
+.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
+
+/* workarounds */
+button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
+/*
+ * jQuery UI Dialog 1.8.12
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog#theming
+ */
+.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
+.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
+.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
+.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
+.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
+.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.ui-draggable .ui-dialog-titlebar { cursor: move; }
+/*
+ * jQuery UI Slider 1.8.12
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider#theming
+ */
+.ui-slider { position: relative; text-align: left; }
+.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
+
+.ui-slider-horizontal { height: .8em; }
+.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.ui-slider-vertical { width: .8em; height: 100px; }
+.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.ui-slider-vertical .ui-slider-range-max { top: 0; }/*
+ * jQuery UI Tabs 1.8.12
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs#theming
+ */
+.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
+.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
+.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
+.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
+.ui-tabs .ui-tabs-hide { display: none !important; }
+/*
+ * jQuery UI Datepicker 1.8.12
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker#theming
+ */
+.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
+.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.ui-datepicker .ui-datepicker-prev { left:2px; }
+.ui-datepicker .ui-datepicker-next { right:2px; }
+.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
+.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
+.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.ui-datepicker select.ui-datepicker-month,
+.ui-datepicker select.ui-datepicker-year { width: 49%;}
+.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
+.ui-datepicker td { border: 0; padding: 1px; }
+.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi { width:auto; }
+.ui-datepicker-multi .ui-datepicker-group { float:left; }
+.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.ui-datepicker-row-break { clear:both; width:100%; }
+
+/* RTL support */
+.ui-datepicker-rtl { direction: rtl; }
+.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.ui-datepicker-cover {
+ display: none; /*sorry for IE5*/
+ display/**/: block; /*sorry for IE5*/
+ position: absolute; /*must have*/
+ z-index: -1; /*must have*/
+ filter: mask(); /*must have*/
+ top: -4px; /*must have*/
+ left: -4px; /*must have*/
+ width: 200px; /*must have*/
+ height: 200px; /*must have*/
+}/*
+ * jQuery UI Progressbar 1.8.12
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar#theming
+ */
+.ui-progressbar { height:2em; text-align: left; }
+.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; } \ No newline at end of file
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/index.html b/storage/mroonga/vendor/groonga/examples/dictionary/html/index.html
new file mode 100644
index 00000000000..a96042bc4ee
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/index.html
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
+<head>
+<meta http-equiv="Content-Language" content="ja" />
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<title>groonga dictionary search</title>
+<meta http-equiv="content-style-type" content="text/css" />
+<meta http-equiv="content-script-type" content="text/javascript" />
+<link type="text/css" href="css/smoothness/jquery-ui-1.8.12.custom.css" rel="stylesheet" />
+<link type="text/css" rel="stylesheet" href="css/dictionary.css" />
+</head>
+<body>
+<form action="javascript:(function(){$('.search').blur()})()" name="search" id="search">
+<input type="text" size="60" maxlength="60" name="key" class="search" />
+<input type="submit" value="検索"/>
+</form>
+<script type="text/javascript" src="js/jquery-1.7.2.min.js"></script>
+<script type="text/javascript" src="js/jquery-ui-1.8.18.custom.min.js"></script>
+<script type="text/javascript" src="js/dictionary.js"></script>
+<script type="text/javascript">
+$(document).ready(function(){
+ $(".search").autocomplete({source: dictionarySource("http://" + location.host + "/d/suggest")});
+});
+</script>
+<div id="result"></div>
+</body>
+</html>
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/js/dictionary.js b/storage/mroonga/vendor/groonga/examples/dictionary/html/js/dictionary.js
new file mode 100644
index 00000000000..850c64cc667
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/js/dictionary.js
@@ -0,0 +1,82 @@
+function dictionarySource(url) {
+ function displayItems(items) {
+ var results = $("<dl />");
+ $.each(items,
+ function(i, val) {
+ results.append($("<dt />")
+ .append($("<span />")
+ .text(val[0])
+ .click(function() {
+ $(".search").val($(this).text());
+ $("#search").submit();
+ })));
+ results.append($("<dd />")
+ .append($("<span />").text(val[1]))
+ .append($("<span />").text(val[2]))
+ );
+ });
+ $("#result")
+ .empty()
+ .append(results);
+ };
+
+ var request_index = 0;
+ var columns = "_key,gene95_desc,edict_desc";
+ var xhr;
+ function source(request, response) {
+ function onSuccess(data, status) {
+ if (this.autocomplete_request != request_index) {
+ return;
+ }
+ var completions = data[1]["complete"];
+ var items = [];
+ if (completions && completions.length > 2) {
+ completions.shift();
+ completions.shift();
+ $.each(completions,
+ function(i, item) {
+ var key = item[0];
+ items.push(key);
+ if (items.length >= 3) {
+ return false;
+ }
+ return true;
+ });
+ }
+ if (completions.length > 0) {
+ displayItems(completions);
+ }
+ response(items);
+ }
+
+ function onError() {
+ if (this.autocomplete_request != request_index) {
+ return;
+ }
+ response([]);
+ }
+
+ if (xhr) {
+ xhr.abort();
+ }
+ xhr = $.ajax(url,
+ {
+ data: {
+ query: request.term,
+ types: 'complete',
+ table: 'item_dictionary',
+ column: 'kana',
+ limit: 25,
+ output_columns: columns,
+ frequency_threshold: 1,
+ prefix_search: "yes"
+ },
+ dataType: "jsonp",
+ autocomplete_request: ++request_index,
+ success: onSuccess,
+ error: onError
+ });
+ };
+
+ return source;
+}
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/js/jquery-1.7.2.min.js b/storage/mroonga/vendor/groonga/examples/dictionary/html/js/jquery-1.7.2.min.js
new file mode 100644
index 00000000000..16ad06c5aca
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/js/jquery-1.7.2.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v1.7.2 jquery.com | jquery.org/license */
+(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
+a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
+.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/html/js/jquery-ui-1.8.18.custom.min.js b/storage/mroonga/vendor/groonga/examples/dictionary/html/js/jquery-ui-1.8.18.custom.min.js
new file mode 100644
index 00000000000..f00a62f133f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/html/js/jquery-ui-1.8.18.custom.min.js
@@ -0,0 +1,356 @@
+/*!
+ * jQuery UI 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */(function(a,b){function d(b){return!a(b).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}function c(b,c){var e=b.nodeName.toLowerCase();if("area"===e){var f=b.parentNode,g=f.name,h;if(!b.href||!g||f.nodeName.toLowerCase()!=="map")return!1;h=a("img[usemap=#"+g+"]")[0];return!!h&&d(h)}return(/input|select|textarea|button|object/.test(e)?!b.disabled:"a"==e?b.href||c:c)&&d(b)}a.ui=a.ui||{};a.ui.version||(a.extend(a.ui,{version:"1.8.18",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}}),a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(b,c){return typeof b=="number"?this.each(function(){var d=this;setTimeout(function(){a(d).focus(),c&&c.call(d)},b)}):this._focus.apply(this,arguments)},scrollParent:function(){var b;a.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?b=this.parents().filter(function(){return/(relative|absolute|fixed)/.test(a.curCSS(this,"position",1))&&/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0):b=this.parents().filter(function(){return/(auto|scroll)/.test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!b.length?a(document):b},zIndex:function(c){if(c!==b)return this.css("zIndex",c);if(this.length){var d=a(this[0]),e,f;while(d.length&&d[0]!==document){e=d.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){f=parseInt(d.css("zIndex"),10);if(!isNaN(f)&&f!==0)return f}d=d.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),a.each(["Width","Height"],function(c,d){function h(b,c,d,f){a.each(e,function(){c-=parseFloat(a.curCSS(b,"padding"+this,!0))||0,d&&(c-=parseFloat(a.curCSS(b,"border"+this+"Width",!0))||0),f&&(c-=parseFloat(a.curCSS(b,"margin"+this,!0))||0)});return c}var e=d==="Width"?["Left","Right"]:["Top","Bottom"],f=d.toLowerCase(),g={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};a.fn["inner"+d]=function(c){if(c===b)return g["inner"+d].call(this);return this.each(function(){a(this).css(f,h(this,c)+"px")})},a.fn["outer"+d]=function(b,c){if(typeof b!="number")return g["outer"+d].call(this,b);return this.each(function(){a(this).css(f,h(this,b,!0,c)+"px")})}}),a.extend(a.expr[":"],{data:function(b,c,d){return!!a.data(b,d[3])},focusable:function(b){return c(b,!isNaN(a.attr(b,"tabindex")))},tabbable:function(b){var d=a.attr(b,"tabindex"),e=isNaN(d);return(e||d>=0)&&c(b,!e)}}),a(function(){var b=document.body,c=b.appendChild(c=document.createElement("div"));c.offsetHeight,a.extend(c.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0}),a.support.minHeight=c.offsetHeight===100,a.support.selectstart="onselectstart"in c,b.removeChild(c).style.display="none"}),a.extend(a.ui,{plugin:{add:function(b,c,d){var e=a.ui[b].prototype;for(var f in d)e.plugins[f]=e.plugins[f]||[],e.plugins[f].push([c,d[f]])},call:function(a,b,c){var d=a.plugins[b];if(!!d&&!!a.element[0].parentNode)for(var e=0;e<d.length;e++)a.options[d[e][0]]&&d[e][1].apply(a.element,c)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(b,c){if(a(b).css("overflow")==="hidden")return!1;var d=c&&c==="left"?"scrollLeft":"scrollTop",e=!1;if(b[d]>0)return!0;b[d]=1,e=b[d]>0,b[d]=0;return e},isOverAxis:function(a,b,c){return a>b&&a<b+c},isOver:function(b,c,d,e,f,g){return a.ui.isOverAxis(b,d,f)&&a.ui.isOverAxis(c,e,g)}}))})(jQuery);/*!
+ * jQuery UI Widget 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */(function(a,b){if(a.cleanData){var c=a.cleanData;a.cleanData=function(b){for(var d=0,e;(e=b[d])!=null;d++)try{a(e).triggerHandler("remove")}catch(f){}c(b)}}else{var d=a.fn.remove;a.fn.remove=function(b,c){return this.each(function(){c||(!b||a.filter(b,[this]).length)&&a("*",this).add([this]).each(function(){try{a(this).triggerHandler("remove")}catch(b){}});return d.call(a(this),b,c)})}}a.widget=function(b,c,d){var e=b.split(".")[0],f;b=b.split(".")[1],f=e+"-"+b,d||(d=c,c=a.Widget),a.expr[":"][f]=function(c){return!!a.data(c,b)},a[e]=a[e]||{},a[e][b]=function(a,b){arguments.length&&this._createWidget(a,b)};var g=new c;g.options=a.extend(!0,{},g.options),a[e][b].prototype=a.extend(!0,g,{namespace:e,widgetName:b,widgetEventPrefix:a[e][b].prototype.widgetEventPrefix||b,widgetBaseClass:f},d),a.widget.bridge(b,a[e][b])},a.widget.bridge=function(c,d){a.fn[c]=function(e){var f=typeof e=="string",g=Array.prototype.slice.call(arguments,1),h=this;e=!f&&g.length?a.extend.apply(null,[!0,e].concat(g)):e;if(f&&e.charAt(0)==="_")return h;f?this.each(function(){var d=a.data(this,c),f=d&&a.isFunction(d[e])?d[e].apply(d,g):d;if(f!==d&&f!==b){h=f;return!1}}):this.each(function(){var b=a.data(this,c);b?b.option(e||{})._init():a.data(this,c,new d(e,this))});return h}},a.Widget=function(a,b){arguments.length&&this._createWidget(a,b)},a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:!1},_createWidget:function(b,c){a.data(c,this.widgetName,this),this.element=a(c),this.options=a.extend(!0,{},this.options,this._getCreateOptions(),b);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()}),this._create(),this._trigger("create"),this._init()},_getCreateOptions:function(){return a.metadata&&a.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName),this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled "+"ui-state-disabled")},widget:function(){return this.element},option:function(c,d){var e=c;if(arguments.length===0)return a.extend({},this.options);if(typeof c=="string"){if(d===b)return this.options[c];e={},e[c]=d}this._setOptions(e);return this},_setOptions:function(b){var c=this;a.each(b,function(a,b){c._setOption(a,b)});return this},_setOption:function(a,b){this.options[a]=b,a==="disabled"&&this.widget()[b?"addClass":"removeClass"](this.widgetBaseClass+"-disabled"+" "+"ui-state-disabled").attr("aria-disabled",b);return this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_trigger:function(b,c,d){var e,f,g=this.options[b];d=d||{},c=a.Event(c),c.type=(b===this.widgetEventPrefix?b:this.widgetEventPrefix+b).toLowerCase(),c.target=this.element[0],f=c.originalEvent;if(f)for(e in f)e in c||(c[e]=f[e]);this.element.trigger(c,d);return!(a.isFunction(g)&&g.call(this.element[0],c,d)===!1||c.isDefaultPrevented())}}})(jQuery);/*!
+ * jQuery UI Mouse 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ * jquery.ui.widget.js
+ */(function(a,b){var c=!1;a(document).mouseup(function(a){c=!1}),a.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var b=this;this.element.bind("mousedown."+this.widgetName,function(a){return b._mouseDown(a)}).bind("click."+this.widgetName,function(c){if(!0===a.data(c.target,b.widgetName+".preventClickEvent")){a.removeData(c.target,b.widgetName+".preventClickEvent"),c.stopImmediatePropagation();return!1}}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(b){if(!c){this._mouseStarted&&this._mouseUp(b),this._mouseDownEvent=b;var d=this,e=b.which==1,f=typeof this.options.cancel=="string"&&b.target.nodeName?a(b.target).closest(this.options.cancel).length:!1;if(!e||f||!this._mouseCapture(b))return!0;this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){d.mouseDelayMet=!0},this.options.delay));if(this._mouseDistanceMet(b)&&this._mouseDelayMet(b)){this._mouseStarted=this._mouseStart(b)!==!1;if(!this._mouseStarted){b.preventDefault();return!0}}!0===a.data(b.target,this.widgetName+".preventClickEvent")&&a.removeData(b.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(a){return d._mouseMove(a)},this._mouseUpDelegate=function(a){return d._mouseUp(a)},a(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),b.preventDefault(),c=!0;return!0}},_mouseMove:function(b){if(a.browser.msie&&!(document.documentMode>=9)&&!b.button)return this._mouseUp(b);if(this._mouseStarted){this._mouseDrag(b);return b.preventDefault()}this._mouseDistanceMet(b)&&this._mouseDelayMet(b)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,b)!==!1,this._mouseStarted?this._mouseDrag(b):this._mouseUp(b));return!this._mouseStarted},_mouseUp:function(b){a(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,b.target==this._mouseDownEvent.target&&a.data(b.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(b));return!1},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(a){return this.mouseDelayMet},_mouseStart:function(a){},_mouseDrag:function(a){},_mouseStop:function(a){},_mouseCapture:function(a){return!0}})})(jQuery);/*
+ * jQuery UI Position 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Position
+ */(function(a,b){a.ui=a.ui||{};var c=/left|center|right/,d=/top|center|bottom/,e="center",f={},g=a.fn.position,h=a.fn.offset;a.fn.position=function(b){if(!b||!b.of)return g.apply(this,arguments);b=a.extend({},b);var h=a(b.of),i=h[0],j=(b.collision||"flip").split(" "),k=b.offset?b.offset.split(" "):[0,0],l,m,n;i.nodeType===9?(l=h.width(),m=h.height(),n={top:0,left:0}):i.setTimeout?(l=h.width(),m=h.height(),n={top:h.scrollTop(),left:h.scrollLeft()}):i.preventDefault?(b.at="left top",l=m=0,n={top:b.of.pageY,left:b.of.pageX}):(l=h.outerWidth(),m=h.outerHeight(),n=h.offset()),a.each(["my","at"],function(){var a=(b[this]||"").split(" ");a.length===1&&(a=c.test(a[0])?a.concat([e]):d.test(a[0])?[e].concat(a):[e,e]),a[0]=c.test(a[0])?a[0]:e,a[1]=d.test(a[1])?a[1]:e,b[this]=a}),j.length===1&&(j[1]=j[0]),k[0]=parseInt(k[0],10)||0,k.length===1&&(k[1]=k[0]),k[1]=parseInt(k[1],10)||0,b.at[0]==="right"?n.left+=l:b.at[0]===e&&(n.left+=l/2),b.at[1]==="bottom"?n.top+=m:b.at[1]===e&&(n.top+=m/2),n.left+=k[0],n.top+=k[1];return this.each(function(){var c=a(this),d=c.outerWidth(),g=c.outerHeight(),h=parseInt(a.curCSS(this,"marginLeft",!0))||0,i=parseInt(a.curCSS(this,"marginTop",!0))||0,o=d+h+(parseInt(a.curCSS(this,"marginRight",!0))||0),p=g+i+(parseInt(a.curCSS(this,"marginBottom",!0))||0),q=a.extend({},n),r;b.my[0]==="right"?q.left-=d:b.my[0]===e&&(q.left-=d/2),b.my[1]==="bottom"?q.top-=g:b.my[1]===e&&(q.top-=g/2),f.fractions||(q.left=Math.round(q.left),q.top=Math.round(q.top)),r={left:q.left-h,top:q.top-i},a.each(["left","top"],function(c,e){a.ui.position[j[c]]&&a.ui.position[j[c]][e](q,{targetWidth:l,targetHeight:m,elemWidth:d,elemHeight:g,collisionPosition:r,collisionWidth:o,collisionHeight:p,offset:k,my:b.my,at:b.at})}),a.fn.bgiframe&&c.bgiframe(),c.offset(a.extend(q,{using:b.using}))})},a.ui.position={fit:{left:function(b,c){var d=a(window),e=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft();b.left=e>0?b.left-e:Math.max(b.left-c.collisionPosition.left,b.left)},top:function(b,c){var d=a(window),e=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop();b.top=e>0?b.top-e:Math.max(b.top-c.collisionPosition.top,b.top)}},flip:{left:function(b,c){if(c.at[0]!==e){var d=a(window),f=c.collisionPosition.left+c.collisionWidth-d.width()-d.scrollLeft(),g=c.my[0]==="left"?-c.elemWidth:c.my[0]==="right"?c.elemWidth:0,h=c.at[0]==="left"?c.targetWidth:-c.targetWidth,i=-2*c.offset[0];b.left+=c.collisionPosition.left<0?g+h+i:f>0?g+h+i:0}},top:function(b,c){if(c.at[1]!==e){var d=a(window),f=c.collisionPosition.top+c.collisionHeight-d.height()-d.scrollTop(),g=c.my[1]==="top"?-c.elemHeight:c.my[1]==="bottom"?c.elemHeight:0,h=c.at[1]==="top"?c.targetHeight:-c.targetHeight,i=-2*c.offset[1];b.top+=c.collisionPosition.top<0?g+h+i:f>0?g+h+i:0}}}},a.offset.setOffset||(a.offset.setOffset=function(b,c){/static/.test(a.curCSS(b,"position"))&&(b.style.position="relative");var d=a(b),e=d.offset(),f=parseInt(a.curCSS(b,"top",!0),10)||0,g=parseInt(a.curCSS(b,"left",!0),10)||0,h={top:c.top-e.top+f,left:c.left-e.left+g};"using"in c?c.using.call(b,h):d.css(h)},a.fn.offset=function(b){var c=this[0];if(!c||!c.ownerDocument)return null;if(b)return this.each(function(){a.offset.setOffset(this,b)});return h.call(this)}),function(){var b=document.getElementsByTagName("body")[0],c=document.createElement("div"),d,e,g,h,i;d=document.createElement(b?"div":"body"),g={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},b&&a.extend(g,{position:"absolute",left:"-1000px",top:"-1000px"});for(var j in g)d.style[j]=g[j];d.appendChild(c),e=b||document.documentElement,e.insertBefore(d,e.firstChild),c.style.cssText="position: absolute; left: 10.7432222px; top: 10.432325px; height: 30px; width: 201px;",h=a(c).offset(function(a,b){return b}).offset(),d.innerHTML="",e.removeChild(d),i=h.top+h.left+(b?2e3:0),f.fractions=i>21&&i<22}()})(jQuery);/*
+ * jQuery UI Draggable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.draggable",a.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1},_create:function(){this.options.helper=="original"&&!/^(?:r|a|f)/.test(this.element.css("position"))&&(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},destroy:function(){if(!!this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy();return this}},_mouseCapture:function(b){var c=this.options;if(this.helper||c.disabled||a(b.target).is(".ui-resizable-handle"))return!1;this.handle=this._getHandle(b);if(!this.handle)return!1;c.iframeFix&&a(c.iframeFix===!0?"iframe":c.iframeFix).each(function(){a('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(a(this).offset()).appendTo("body")});return!0},_mouseStart:function(b){var c=this.options;this.helper=this._createHelper(b),this._cacheHelperProportions(),a.ui.ddmanager&&(a.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(b),this.originalPageX=b.pageX,this.originalPageY=b.pageY,c.cursorAt&&this._adjustOffsetFromHelper(c.cursorAt),c.containment&&this._setContainment();if(this._trigger("start",b)===!1){this._clear();return!1}this._cacheHelperProportions(),a.ui.ddmanager&&!c.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b),this.helper.addClass("ui-draggable-dragging"),this._mouseDrag(b,!0),a.ui.ddmanager&&a.ui.ddmanager.dragStart(this,b);return!0},_mouseDrag:function(b,c){this.position=this._generatePosition(b),this.positionAbs=this._convertPositionTo("absolute");if(!c){var d=this._uiHash();if(this._trigger("drag",b,d)===!1){this._mouseUp({});return!1}this.position=d.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";a.ui.ddmanager&&a.ui.ddmanager.drag(this,b);return!1},_mouseStop:function(b){var c=!1;a.ui.ddmanager&&!this.options.dropBehaviour&&(c=a.ui.ddmanager.drop(this,b)),this.dropped&&(c=this.dropped,this.dropped=!1);if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return!1;if(this.options.revert=="invalid"&&!c||this.options.revert=="valid"&&c||this.options.revert===!0||a.isFunction(this.options.revert)&&this.options.revert.call(this.element,c)){var d=this;a(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){d._trigger("stop",b)!==!1&&d._clear()})}else this._trigger("stop",b)!==!1&&this._clear();return!1},_mouseUp:function(b){this.options.iframeFix===!0&&a("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),a.ui.ddmanager&&a.ui.ddmanager.dragStop(this,b);return a.ui.mouse.prototype._mouseUp.call(this,b)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(b){var c=!this.options.handle||!a(this.options.handle,this.element).length?!0:!1;a(this.options.handle,this.element).find("*").andSelf().each(function(){this==b.target&&(c=!0)});return c},_createHelper:function(b){var c=this.options,d=a.isFunction(c.helper)?a(c.helper.apply(this.element[0],[b])):c.helper=="clone"?this.element.clone().removeAttr("id"):this.element;d.parents("body").length||d.appendTo(c.appendTo=="parent"?this.element[0].parentNode:c.appendTo),d[0]!=this.element[0]&&!/(fixed|absolute)/.test(d.css("position"))&&d.css("position","absolute");return d},_adjustOffsetFromHelper:function(b){typeof b=="string"&&(b=b.split(" ")),a.isArray(b)&&(b={left:+b[0],top:+b[1]||0}),"left"in b&&(this.offset.click.left=b.left+this.margins.left),"right"in b&&(this.offset.click.left=this.helperProportions.width-b.right+this.margins.left),"top"in b&&(this.offset.click.top=b.top+this.margins.top),"bottom"in b&&(this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])&&(b.left+=this.scrollParent.scrollLeft(),b.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)b={top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var b=this.options;b.containment=="parent"&&(b.containment=this.helper[0].parentNode);if(b.containment=="document"||b.containment=="window")this.containment=[b.containment=="document"?0:a(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,b.containment=="document"?0:a(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,(b.containment=="document"?0:a(window).scrollLeft())+a(b.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(b.containment=="document"?0:a(window).scrollTop())+(a(b.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(b.containment)&&b.containment.constructor!=Array){var c=a(b.containment),d=c[0];if(!d)return;var e=c.offset(),f=a(d).css("overflow")!="hidden";this.containment=[(parseInt(a(d).css("borderLeftWidth"),10)||0)+(parseInt(a(d).css("paddingLeft"),10)||0),(parseInt(a(d).css("borderTopWidth"),10)||0)+(parseInt(a(d).css("paddingTop"),10)||0),(f?Math.max(d.scrollWidth,d.offsetWidth):d.offsetWidth)-(parseInt(a(d).css("borderLeftWidth"),10)||0)-(parseInt(a(d).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(f?Math.max(d.scrollHeight,d.offsetHeight):d.offsetHeight)-(parseInt(a(d).css("borderTopWidth"),10)||0)-(parseInt(a(d).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=c}else b.containment.constructor==Array&&(this.containment=b.containment)},_convertPositionTo:function(b,c){c||(c=this.position);var d=b=="absolute"?1:-1,e=this.options,f=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName);return{top:c.top+this.offset.relative.top*d+this.offset.parent.top*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),left:c.left+this.offset.relative.left*d+this.offset.parent.left*d-(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:function(b){var c=this.options,d=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(d[0].tagName),f=b.pageX,g=b.pageY;if(this.originalPosition){var h;if(this.containment){if(this.relative_container){var i=this.relative_container.offset();h=[this.containment[0]+i.left,this.containment[1]+i.top,this.containment[2]+i.left,this.containment[3]+i.top]}else h=this.containment;b.pageX-this.offset.click.left<h[0]&&(f=h[0]+this.offset.click.left),b.pageY-this.offset.click.top<h[1]&&(g=h[1]+this.offset.click.top),b.pageX-this.offset.click.left>h[2]&&(f=h[2]+this.offset.click.left),b.pageY-this.offset.click.top>h[3]&&(g=h[3]+this.offset.click.top)}if(c.grid){var j=c.grid[1]?this.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1]:this.originalPageY;g=h?j-this.offset.click.top<h[1]||j-this.offset.click.top>h[3]?j-this.offset.click.top<h[1]?j+c.grid[1]:j-c.grid[1]:j:j;var k=c.grid[0]?this.originalPageX+Math.round((f-this.originalPageX)/c.grid[0])*c.grid[0]:this.originalPageX;f=h?k-this.offset.click.left<h[0]||k-this.offset.click.left>h[2]?k-this.offset.click.left<h[0]?k+c.grid[0]:k-c.grid[0]:k:k}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:d.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(a.browser.safari&&a.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:d.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1},_trigger:function(b,c,d){d=d||this._uiHash(),a.ui.plugin.call(this,b,[c,d]),b=="drag"&&(this.positionAbs=this._convertPositionTo("absolute"));return a.Widget.prototype._trigger.call(this,b,c,d)},plugins:{},_uiHash:function(a){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),a.extend(a.ui.draggable,{version:"1.8.18"}),a.ui.plugin.add("draggable","connectToSortable",{start:function(b,c){var d=a(this).data("draggable"),e=d.options,f=a.extend({},c,{item:d.element});d.sortables=[],a(e.connectToSortable).each(function(){var c=a.data(this,"sortable");c&&!c.options.disabled&&(d.sortables.push({instance:c,shouldRevert:c.options.revert}),c.refreshPositions(),c._trigger("activate",b,f))})},stop:function(b,c){var d=a(this).data("draggable"),e=a.extend({},c,{item:d.element});a.each(d.sortables,function(){this.instance.isOver?(this.instance.isOver=0,d.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=!0),this.instance._mouseStop(b),this.instance.options.helper=this.instance.options._helper,d.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",b,e))})},drag:function(b,c){var d=a(this).data("draggable"),e=this,f=function(b){var c=this.offset.click.top,d=this.offset.click.left,e=this.positionAbs.top,f=this.positionAbs.left,g=b.height,h=b.width,i=b.top,j=b.left;return a.ui.isOver(e+c,f+d,i,j,g,h)};a.each(d.sortables,function(f){this.instance.positionAbs=d.positionAbs,this.instance.helperProportions=d.helperProportions,this.instance.offset.click=d.offset.click,this.instance._intersectsWith(this.instance.containerCache)?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=a(e).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return c.helper[0]},b.target=this.instance.currentItem[0],this.instance._mouseCapture(b,!0),this.instance._mouseStart(b,!0,!0),this.instance.offset.click.top=d.offset.click.top,this.instance.offset.click.left=d.offset.click.left,this.instance.offset.parent.left-=d.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=d.offset.parent.top-this.instance.offset.parent.top,d._trigger("toSortable",b),d.dropped=this.instance.element,d.currentItem=d.element,this.instance.fromOutside=d),this.instance.currentItem&&this.instance._mouseDrag(b)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",b,this.instance._uiHash(this.instance)),this.instance._mouseStop(b,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),d._trigger("fromSortable",b),d.dropped=!1)})}}),a.ui.plugin.add("draggable","cursor",{start:function(b,c){var d=a("body"),e=a(this).data("draggable").options;d.css("cursor")&&(e._cursor=d.css("cursor")),d.css("cursor",e.cursor)},stop:function(b,c){var d=a(this).data("draggable").options;d._cursor&&a("body").css("cursor",d._cursor)}}),a.ui.plugin.add("draggable","opacity",{start:function(b,c){var d=a(c.helper),e=a(this).data("draggable").options;d.css("opacity")&&(e._opacity=d.css("opacity")),d.css("opacity",e.opacity)},stop:function(b,c){var d=a(this).data("draggable").options;d._opacity&&a(c.helper).css("opacity",d._opacity)}}),a.ui.plugin.add("draggable","scroll",{start:function(b,c){var d=a(this).data("draggable");d.scrollParent[0]!=document&&d.scrollParent[0].tagName!="HTML"&&(d.overflowOffset=d.scrollParent.offset())},drag:function(b,c){var d=a(this).data("draggable"),e=d.options,f=!1;if(d.scrollParent[0]!=document&&d.scrollParent[0].tagName!="HTML"){if(!e.axis||e.axis!="x")d.overflowOffset.top+d.scrollParent[0].offsetHeight-b.pageY<e.scrollSensitivity?d.scrollParent[0].scrollTop=f=d.scrollParent[0].scrollTop+e.scrollSpeed:b.pageY-d.overflowOffset.top<e.scrollSensitivity&&(d.scrollParent[0].scrollTop=f=d.scrollParent[0].scrollTop-e.scrollSpeed);if(!e.axis||e.axis!="y")d.overflowOffset.left+d.scrollParent[0].offsetWidth-b.pageX<e.scrollSensitivity?d.scrollParent[0].scrollLeft=f=d.scrollParent[0].scrollLeft+e.scrollSpeed:b.pageX-d.overflowOffset.left<e.scrollSensitivity&&(d.scrollParent[0].scrollLeft=f=d.scrollParent[0].scrollLeft-e.scrollSpeed)}else{if(!e.axis||e.axis!="x")b.pageY-a(document).scrollTop()<e.scrollSensitivity?f=a(document).scrollTop(a(document).scrollTop()-e.scrollSpeed):a(window).height()-(b.pageY-a(document).scrollTop())<e.scrollSensitivity&&(f=a(document).scrollTop(a(document).scrollTop()+e.scrollSpeed));if(!e.axis||e.axis!="y")b.pageX-a(document).scrollLeft()<e.scrollSensitivity?f=a(document).scrollLeft(a(document).scrollLeft()-e.scrollSpeed):a(window).width()-(b.pageX-a(document).scrollLeft())<e.scrollSensitivity&&(f=a(document).scrollLeft(a(document).scrollLeft()+e.scrollSpeed))}f!==!1&&a.ui.ddmanager&&!e.dropBehaviour&&a.ui.ddmanager.prepareOffsets(d,b)}}),a.ui.plugin.add("draggable","snap",{start:function(b,c){var d=a(this).data("draggable"),e=d.options;d.snapElements=[],a(e.snap.constructor!=String?e.snap.items||":data(draggable)":e.snap).each(function(){var b=a(this),c=b.offset();this!=d.element[0]&&d.snapElements.push({item:this,width:b.outerWidth(),height:b.outerHeight(),top:c.top,left:c.left})})},drag:function(b,c){var d=a(this).data("draggable"),e=d.options,f=e.snapTolerance,g=c.offset.left,h=g+d.helperProportions.width,i=c.offset.top,j=i+d.helperProportions.height;for(var k=d.snapElements.length-1;k>=0;k--){var l=d.snapElements[k].left,m=l+d.snapElements[k].width,n=d.snapElements[k].top,o=n+d.snapElements[k].height;if(!(l-f<g&&g<m+f&&n-f<i&&i<o+f||l-f<g&&g<m+f&&n-f<j&&j<o+f||l-f<h&&h<m+f&&n-f<i&&i<o+f||l-f<h&&h<m+f&&n-f<j&&j<o+f)){d.snapElements[k].snapping&&d.options.snap.release&&d.options.snap.release.call(d.element,b,a.extend(d._uiHash(),{snapItem:d.snapElements[k].item})),d.snapElements[k].snapping=!1;continue}if(e.snapMode!="inner"){var p=Math.abs(n-j)<=f,q=Math.abs(o-i)<=f,r=Math.abs(l-h)<=f,s=Math.abs(m-g)<=f;p&&(c.position.top=d._convertPositionTo("relative",{top:n-d.helperProportions.height,left:0}).top-d.margins.top),q&&(c.position.top=d._convertPositionTo("relative",{top:o,left:0}).top-d.margins.top),r&&(c.position.left=d._convertPositionTo("relative",{top:0,left:l-d.helperProportions.width}).left-d.margins.left),s&&(c.position.left=d._convertPositionTo("relative",{top:0,left:m}).left-d.margins.left)}var t=p||q||r||s;if(e.snapMode!="outer"){var p=Math.abs(n-i)<=f,q=Math.abs(o-j)<=f,r=Math.abs(l-g)<=f,s=Math.abs(m-h)<=f;p&&(c.position.top=d._convertPositionTo("relative",{top:n,left:0}).top-d.margins.top),q&&(c.position.top=d._convertPositionTo("relative",{top:o-d.helperProportions.height,left:0}).top-d.margins.top),r&&(c.position.left=d._convertPositionTo("relative",{top:0,left:l}).left-d.margins.left),s&&(c.position.left=d._convertPositionTo("relative",{top:0,left:m-d.helperProportions.width}).left-d.margins.left)}!d.snapElements[k].snapping&&(p||q||r||s||t)&&d.options.snap.snap&&d.options.snap.snap.call(d.element,b,a.extend(d._uiHash(),{snapItem:d.snapElements[k].item})),d.snapElements[k].snapping=p||q||r||s||t}}}),a.ui.plugin.add("draggable","stack",{start:function(b,c){var d=a(this).data("draggable").options,e=a.makeArray(a(d.stack)).sort(function(b,c){return(parseInt(a(b).css("zIndex"),10)||0)-(parseInt(a(c).css("zIndex"),10)||0)});if(!!e.length){var f=parseInt(e[0].style.zIndex)||0;a(e).each(function(a){this.style.zIndex=f+a}),this[0].style.zIndex=f+e.length}}}),a.ui.plugin.add("draggable","zIndex",{start:function(b,c){var d=a(c.helper),e=a(this).data("draggable").options;d.css("zIndex")&&(e._zIndex=d.css("zIndex")),d.css("zIndex",e.zIndex)},stop:function(b,c){var d=a(this).data("draggable").options;d._zIndex&&a(c.helper).css("zIndex",d._zIndex)}})})(jQuery);/*
+ * jQuery UI Droppable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Droppables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.mouse.js
+ * jquery.ui.draggable.js
+ */(function(a,b){a.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect"},_create:function(){var b=this.options,c=b.accept;this.isover=0,this.isout=1,this.accept=a.isFunction(c)?c:function(a){return a.is(c)},this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight},a.ui.ddmanager.droppables[b.scope]=a.ui.ddmanager.droppables[b.scope]||[],a.ui.ddmanager.droppables[b.scope].push(this),b.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){var b=a.ui.ddmanager.droppables[this.options.scope];for(var c=0;c<b.length;c++)b[c]==this&&b.splice(c,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(b,c){b=="accept"&&(this.accept=a.isFunction(c)?c:function(a){return a.is(c)}),a.Widget.prototype._setOption.apply(this,arguments)},_activate:function(b){var c=a.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),c&&this._trigger("activate",b,this.ui(c))},_deactivate:function(b){var c=a.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),c&&this._trigger("deactivate",b,this.ui(c))},_over:function(b){var c=a.ui.ddmanager.current;!!c&&(c.currentItem||c.element)[0]!=this.element[0]&&this.accept.call(this.element[0],c.currentItem||c.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",b,this.ui(c)))},_out:function(b){var c=a.ui.ddmanager.current;!!c&&(c.currentItem||c.element)[0]!=this.element[0]&&this.accept.call(this.element[0],c.currentItem||c.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",b,this.ui(c)))},_drop:function(b,c){var d=c||a.ui.ddmanager.current;if(!d||(d.currentItem||d.element)[0]==this.element[0])return!1;var e=!1;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var b=a.data(this,"droppable");if(b.options.greedy&&!b.options.disabled&&b.options.scope==d.options.scope&&b.accept.call(b.element[0],d.currentItem||d.element)&&a.ui.intersect(d,a.extend(b,{offset:b.element.offset()}),b.options.tolerance)){e=!0;return!1}});if(e)return!1;if(this.accept.call(this.element[0],d.currentItem||d.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",b,this.ui(d));return this.element}return!1},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}}),a.extend(a.ui.droppable,{version:"1.8.18"}),a.ui.intersect=function(b,c,d){if(!c.offset)return!1;var e=(b.positionAbs||b.position.absolute).left,f=e+b.helperProportions.width,g=(b.positionAbs||b.position.absolute).top,h=g+b.helperProportions.height,i=c.offset.left,j=i+c.proportions.width,k=c.offset.top,l=k+c.proportions.height;switch(d){case"fit":return i<=e&&f<=j&&k<=g&&h<=l;case"intersect":return i<e+b.helperProportions.width/2&&f-b.helperProportions.width/2<j&&k<g+b.helperProportions.height/2&&h-b.helperProportions.height/2<l;case"pointer":var m=(b.positionAbs||b.position.absolute).left+(b.clickOffset||b.offset.click).left,n=(b.positionAbs||b.position.absolute).top+(b.clickOffset||b.offset.click).top,o=a.ui.isOver(n,m,k,i,c.proportions.height,c.proportions.width);return o;case"touch":return(g>=k&&g<=l||h>=k&&h<=l||g<k&&h>l)&&(e>=i&&e<=j||f>=i&&f<=j||e<i&&f>j);default:return!1}},a.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(b,c){var d=a.ui.ddmanager.droppables[b.options.scope]||[],e=c?c.type:null,f=(b.currentItem||b.element).find(":data(droppable)").andSelf();droppablesLoop:for(var g=0;g<d.length;g++){if(d[g].options.disabled||b&&!d[g].accept.call(d[g].element[0],b.currentItem||b.element))continue;for(var h=0;h<f.length;h++)if(f[h]==d[g].element[0]){d[g].proportions.height=0;continue droppablesLoop}d[g].visible=d[g].element.css("display")!="none";if(!d[g].visible)continue;e=="mousedown"&&d[g]._activate.call(d[g],c),d[g].offset=d[g].element.offset(),d[g].proportions={width:d[g].element[0].offsetWidth,height:d[g].element[0].offsetHeight}}},drop:function(b,c){var d=!1;a.each(a.ui.ddmanager.droppables[b.options.scope]||[],function(){!this.options||(!this.options.disabled&&this.visible&&a.ui.intersect(b,this,this.options.tolerance)&&(d=this._drop.call(this,c)||d),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],b.currentItem||b.element)&&(this.isout=1,this.isover=0,this._deactivate.call(this,c)))});return d},dragStart:function(b,c){b.element.parents(":not(body,html)").bind("scroll.droppable",function(){b.options.refreshPositions||a.ui.ddmanager.prepareOffsets(b,c)})},drag:function(b,c){b.options.refreshPositions&&a.ui.ddmanager.prepareOffsets(b,c),a.each(a.ui.ddmanager.droppables[b.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var d=a.ui.intersect(b,this,this.options.tolerance),e=!d&&this.isover==1?"isout":d&&this.isover==0?"isover":null;if(!e)return;var f;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");g.length&&(f=a.data(g[0],"droppable"),f.greedyChild=e=="isover"?1:0)}f&&e=="isover"&&(f.isover=0,f.isout=1,f._out.call(f,c)),this[e]=1,this[e=="isout"?"isover":"isout"]=0,this[e=="isover"?"_over":"_out"].call(this,c),f&&e=="isout"&&(f.isout=0,f.isover=1,f._over.call(f,c))}})},dragStop:function(b,c){b.element.parents(":not(body,html)").unbind("scroll.droppable"),b.options.refreshPositions||a.ui.ddmanager.prepareOffsets(b,c)}}})(jQuery);/*
+ * jQuery UI Resizable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.resizable",a.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1e3},_create:function(){var b=this,c=this.options;this.element.addClass("ui-resizable"),a.extend(this,{_aspectRatio:!!c.aspectRatio,aspectRatio:c.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:c.helper||c.ghost||c.animate?c.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(a('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("resizable",this.element.data("resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=c.handles||(a(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se");if(this.handles.constructor==String){this.handles=="all"&&(this.handles="n,e,s,w,se,sw,ne,nw");var d=this.handles.split(",");this.handles={};for(var e=0;e<d.length;e++){var f=a.trim(d[e]),g="ui-resizable-"+f,h=a('<div class="ui-resizable-handle '+g+'"></div>');/sw|se|ne|nw/.test(f)&&h.css({zIndex:++c.zIndex}),"se"==f&&h.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[f]=".ui-resizable-"+f,this.element.append(h)}}this._renderAxis=function(b){b=b||this.element;for(var c in this.handles){this.handles[c].constructor==String&&(this.handles[c]=a(this.handles[c],this.element).show());if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var d=a(this.handles[c],this.element),e=0;e=/sw|ne|nw|se|n|s/.test(c)?d.outerHeight():d.outerWidth();var f=["padding",/ne|nw|n/.test(c)?"Top":/se|sw|s/.test(c)?"Bottom":/^e$/.test(c)?"Right":"Left"].join("");b.css(f,e),this._proportionallyResize()}if(!a(this.handles[c]).length)continue}},this._renderAxis(this.element),this._handles=a(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){if(!b.resizing){if(this.className)var a=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=a&&a[1]?a[1]:"se"}}),c.autoHide&&(this._handles.hide(),a(this.element).addClass("ui-resizable-autohide").hover(function(){c.disabled||(a(this).removeClass("ui-resizable-autohide"),b._handles.show())},function(){c.disabled||b.resizing||(a(this).addClass("ui-resizable-autohide"),b._handles.hide())})),this._mouseInit()},destroy:function(){this._mouseDestroy();var b=function(b){a(b).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var c=this.element;c.after(this.originalElement.css({position:c.css("position"),width:c.outerWidth(),height:c.outerHeight(),top:c.css("top"),left:c.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle),b(this.originalElement);return this},_mouseCapture:function(b){var c=!1;for(var d in this.handles)a(this.handles[d])[0]==b.target&&(c=!0);return!this.options.disabled&&c},_mouseStart:function(b){var d=this.options,e=this.element.position(),f=this.element;this.resizing=!0,this.documentScroll={top:a(document).scrollTop(),left:a(document).scrollLeft()},(f.is(".ui-draggable")||/absolute/.test(f.css("position")))&&f.css({position:"absolute",top:e.top,left:e.left}),this._renderProxy();var g=c(this.helper.css("left")),h=c(this.helper.css("top"));d.containment&&(g+=a(d.containment).scrollLeft()||0,h+=a(d.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:g,top:h},this.size=this._helper?{width:f.outerWidth(),height:f.outerHeight()}:{width:f.width(),height:f.height()},this.originalSize=this._helper?{width:f.outerWidth(),height:f.outerHeight()}:{width:f.width(),height:f.height()},this.originalPosition={left:g,top:h},this.sizeDiff={width:f.outerWidth()-f.width(),height:f.outerHeight()-f.height()},this.originalMousePosition={left:b.pageX,top:b.pageY},this.aspectRatio=typeof d.aspectRatio=="number"?d.aspectRatio:this.originalSize.width/this.originalSize.height||1;var i=a(".ui-resizable-"+this.axis).css("cursor");a("body").css("cursor",i=="auto"?this.axis+"-resize":i),f.addClass("ui-resizable-resizing"),this._propagate("start",b);return!0},_mouseDrag:function(b){var c=this.helper,d=this.options,e={},f=this,g=this.originalMousePosition,h=this.axis,i=b.pageX-g.left||0,j=b.pageY-g.top||0,k=this._change[h];if(!k)return!1;var l=k.apply(this,[b,i,j]),m=a.browser.msie&&a.browser.version<7,n=this.sizeDiff;this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)l=this._updateRatio(l,b);l=this._respectSize(l,b),this._propagate("resize",b),c.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"}),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),this._updateCache(l),this._trigger("resize",b,this.ui());return!1},_mouseStop:function(b){this.resizing=!1;var c=this.options,d=this;if(this._helper){var e=this._proportionallyResizeElements,f=e.length&&/textarea/i.test(e[0].nodeName),g=f&&a.ui.hasScroll(e[0],"left")?0:d.sizeDiff.height,h=f?0:d.sizeDiff.width,i={width:d.helper.width()-h,height:d.helper.height()-g},j=parseInt(d.element.css("left"),10)+(d.position.left-d.originalPosition.left)||null,k=parseInt(d.element.css("top"),10)+(d.position.top-d.originalPosition.top)||null;c.animate||this.element.css(a.extend(i,{top:k,left:j})),d.helper.height(d.size.height),d.helper.width(d.size.width),this._helper&&!c.animate&&this._proportionallyResize()}a("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",b),this._helper&&this.helper.remove();return!1},_updateVirtualBoundaries:function(a){var b=this.options,c,e,f,g,h;h={minWidth:d(b.minWidth)?b.minWidth:0,maxWidth:d(b.maxWidth)?b.maxWidth:Infinity,minHeight:d(b.minHeight)?b.minHeight:0,maxHeight:d(b.maxHeight)?b.maxHeight:Infinity};if(this._aspectRatio||a)c=h.minHeight*this.aspectRatio,f=h.minWidth/this.aspectRatio,e=h.maxHeight*this.aspectRatio,g=h.maxWidth/this.aspectRatio,c>h.minWidth&&(h.minWidth=c),f>h.minHeight&&(h.minHeight=f),e<h.maxWidth&&(h.maxWidth=e),g<h.maxHeight&&(h.maxHeight=g);this._vBoundaries=h},_updateCache:function(a){var b=this.options;this.offset=this.helper.offset(),d(a.left)&&(this.position.left=a.left),d(a.top)&&(this.position.top=a.top),d(a.height)&&(this.size.height=a.height),d(a.width)&&(this.size.width=a.width)},_updateRatio:function(a,b){var c=this.options,e=this.position,f=this.size,g=this.axis;d(a.height)?a.width=a.height*this.aspectRatio:d(a.width)&&(a.height=a.width/this.aspectRatio),g=="sw"&&(a.left=e.left+(f.width-a.width),a.top=null),g=="nw"&&(a.top=e.top+(f.height-a.height),a.left=e.left+(f.width-a.width));return a},_respectSize:function(a,b){var c=this.helper,e=this._vBoundaries,f=this._aspectRatio||b.shiftKey,g=this.axis,h=d(a.width)&&e.maxWidth&&e.maxWidth<a.width,i=d(a.height)&&e.maxHeight&&e.maxHeight<a.height,j=d(a.width)&&e.minWidth&&e.minWidth>a.width,k=d(a.height)&&e.minHeight&&e.minHeight>a.height;j&&(a.width=e.minWidth),k&&(a.height=e.minHeight),h&&(a.width=e.maxWidth),i&&(a.height=e.maxHeight);var l=this.originalPosition.left+this.originalSize.width,m=this.position.top+this.size.height,n=/sw|nw|w/.test(g),o=/nw|ne|n/.test(g);j&&n&&(a.left=l-e.minWidth),h&&n&&(a.left=l-e.maxWidth),k&&o&&(a.top=m-e.minHeight),i&&o&&(a.top=m-e.maxHeight);var p=!a.width&&!a.height;p&&!a.left&&a.top?a.top=null:p&&!a.top&&a.left&&(a.left=null);return a},_proportionallyResize:function(){var b=this.options;if(!!this._proportionallyResizeElements.length){var c=this.helper||this.element;for(var d=0;d<this._proportionallyResizeElements.length;d++){var e=this._proportionallyResizeElements[d];if(!this.borderDif){var f=[e.css("borderTopWidth"),e.css("borderRightWidth"),e.css("borderBottomWidth"),e.css("borderLeftWidth")],g=[e.css("paddingTop"),e.css("paddingRight"),e.css("paddingBottom"),e.css("paddingLeft")];this.borderDif=a.map(f,function(a,b){var c=parseInt(a,10)||0,d=parseInt(g[b],10)||0;return c+d})}if(a.browser.msie&&(!!a(c).is(":hidden")||!!a(c).parents(":hidden").length))continue;e.css({height:c.height()-this.borderDif[0]-this.borderDif[2]||0,width:c.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var b=this.element,c=this.options;this.elementOffset=b.offset();if(this._helper){this.helper=this.helper||a('<div style="overflow:hidden;"></div>');var d=a.browser.msie&&a.browser.version<7,e=d?1:0,f=d?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+f,height:this.element.outerHeight()+f,position:"absolute",left:this.elementOffset.left-e+"px",top:this.elementOffset.top-e+"px",zIndex:++c.zIndex}),this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(a,b,c){return{width:this.originalSize.width+b}},w:function(a,b,c){var d=this.options,e=this.originalSize,f=this.originalPosition;return{left:f.left+b,width:e.width-b}},n:function(a,b,c){var d=this.options,e=this.originalSize,f=this.originalPosition;return{top:f.top+c,height:e.height-c}},s:function(a,b,c){return{height:this.originalSize.height+c}},se:function(b,c,d){return a.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,c,d]))},sw:function(b,c,d){return a.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,c,d]))},ne:function(b,c,d){return a.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,c,d]))},nw:function(b,c,d){return a.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,c,d]))}},_propagate:function(b,c){a.ui.plugin.call(this,b,[c,this.ui()]),b!="resize"&&this._trigger(b,c,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),a.extend(a.ui.resizable,{version:"1.8.18"}),a.ui.plugin.add("resizable","alsoResize",{start:function(b,c){var d=a(this).data("resizable"),e=d.options,f=function(b){a(b).each(function(){var b=a(this);b.data("resizable-alsoresize",{width:parseInt(b.width(),10),height:parseInt(b.height(),10),left:parseInt(b.css("left"),10),top:parseInt(b.css("top"),10)})})};typeof e.alsoResize=="object"&&!e.alsoResize.parentNode?e.alsoResize.length?(e.alsoResize=e.alsoResize[0],f(e.alsoResize)):a.each(e.alsoResize,function(a){f(a)}):f(e.alsoResize)},resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.originalSize,g=d.originalPosition,h={height:d.size.height-f.height||0,width:d.size.width-f.width||0,top:d.position.top-g.top||0,left:d.position.left-g.left||0},i=function(b,d){a(b).each(function(){var b=a(this),e=a(this).data("resizable-alsoresize"),f={},g=d&&d.length?d:b.parents(c.originalElement[0]).length?["width","height"]:["width","height","top","left"];a.each(g,function(a,b){var c=(e[b]||0)+(h[b]||0);c&&c>=0&&(f[b]=c||null)}),b.css(f)})};typeof e.alsoResize=="object"&&!e.alsoResize.nodeType?a.each(e.alsoResize,function(a,b){i(a,b)}):i(e.alsoResize)},stop:function(b,c){a(this).removeData("resizable-alsoresize")}}),a.ui.plugin.add("resizable","animate",{stop:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d._proportionallyResizeElements,g=f.length&&/textarea/i.test(f[0].nodeName),h=g&&a.ui.hasScroll(f[0],"left")?0:d.sizeDiff.height,i=g?0:d.sizeDiff.width,j={width:d.size.width-i,height:d.size.height-h},k=parseInt(d.element.css("left"),10)+(d.position.left-d.originalPosition.left)||null,l=parseInt(d.element.css("top"),10)+(d.position.top-d.originalPosition.top)||null;d.element.animate(a.extend(j,l&&k?{top:l,left:k}:{}),{duration:e.animateDuration,easing:e.animateEasing,step:function(){var c={width:parseInt(d.element.css("width"),10),height:parseInt(d.element.css("height"),10),top:parseInt(d.element.css("top"),10),left:parseInt(d.element.css("left"),10)};f&&f.length&&a(f[0]).css({width:c.width,height:c.height}),d._updateCache(c),d._propagate("resize",b)}})}}),a.ui.plugin.add("resizable","containment",{start:function(b,d){var e=a(this).data("resizable"),f=e.options,g=e.element,h=f.containment,i=h instanceof a?h.get(0):/parent/.test(h)?g.parent().get(0):h;if(!!i){e.containerElement=a(i);if(/document/.test(h)||h==document)e.containerOffset={left:0,top:0},e.containerPosition={left:0,top:0},e.parentData={element:a(document),left:0,top:0,width:a(document).width(),height:a(document).height()||document.body.parentNode.scrollHeight};else{var j=a(i),k=[];a(["Top","Right","Left","Bottom"]).each(function(a,b){k[a]=c(j.css("padding"+b))}),e.containerOffset=j.offset(),e.containerPosition=j.position(),e.containerSize={height:j.innerHeight()-k[3],width:j.innerWidth()-k[1]};var l=e.containerOffset,m=e.containerSize.height,n=e.containerSize.width,o=a.ui.hasScroll(i,"left")?i.scrollWidth:n,p=a.ui.hasScroll(i)?i.scrollHeight:m;e.parentData={element:i,left:l.left,top:l.top,width:o,height:p}}}},resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.containerSize,g=d.containerOffset,h=d.size,i=d.position,j=d._aspectRatio||b.shiftKey,k={top:0,left:0},l=d.containerElement;l[0]!=document&&/static/.test(l.css("position"))&&(k=g),i.left<(d._helper?g.left:0)&&(d.size.width=d.size.width+(d._helper?d.position.left-g.left:d.position.left-k.left),j&&(d.size.height=d.size.width/e.aspectRatio),d.position.left=e.helper?g.left:0),i.top<(d._helper?g.top:0)&&(d.size.height=d.size.height+(d._helper?d.position.top-g.top:d.position.top),j&&(d.size.width=d.size.height*e.aspectRatio),d.position.top=d._helper?g.top:0),d.offset.left=d.parentData.left+d.position.left,d.offset.top=d.parentData.top+d.position.top;var m=Math.abs((d._helper?d.offset.left-k.left:d.offset.left-k.left)+d.sizeDiff.width),n=Math.abs((d._helper?d.offset.top-k.top:d.offset.top-g.top)+d.sizeDiff.height),o=d.containerElement.get(0)==d.element.parent().get(0),p=/relative|absolute/.test(d.containerElement.css("position"));o&&p&&(m-=d.parentData.left),m+d.size.width>=d.parentData.width&&(d.size.width=d.parentData.width-m,j&&(d.size.height=d.size.width/d.aspectRatio)),n+d.size.height>=d.parentData.height&&(d.size.height=d.parentData.height-n,j&&(d.size.width=d.size.height*d.aspectRatio))},stop:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.position,g=d.containerOffset,h=d.containerPosition,i=d.containerElement,j=a(d.helper),k=j.offset(),l=j.outerWidth()-d.sizeDiff.width,m=j.outerHeight()-d.sizeDiff.height;d._helper&&!e.animate&&/relative/.test(i.css("position"))&&a(this).css({left:k.left-h.left-g.left,width:l,height:m}),d._helper&&!e.animate&&/static/.test(i.css("position"))&&a(this).css({left:k.left-h.left-g.left,width:l,height:m})}}),a.ui.plugin.add("resizable","ghost",{start:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.size;d.ghost=d.originalElement.clone(),d.ghost.css({opacity:.25,display:"block",position:"relative",height:f.height,width:f.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof e.ghost=="string"?e.ghost:""),d.ghost.appendTo(d.helper)},resize:function(b,c){var d=a(this).data("resizable"),e=d.options;d.ghost&&d.ghost.css({position:"relative",height:d.size.height,width:d.size.width})},stop:function(b,c){var d=a(this).data("resizable"),e=d.options;d.ghost&&d.helper&&d.helper.get(0).removeChild(d.ghost.get(0))}}),a.ui.plugin.add("resizable","grid",{resize:function(b,c){var d=a(this).data("resizable"),e=d.options,f=d.size,g=d.originalSize,h=d.originalPosition,i=d.axis,j=e._aspectRatio||b.shiftKey;e.grid=typeof e.grid=="number"?[e.grid,e.grid]:e.grid;var k=Math.round((f.width-g.width)/(e.grid[0]||1))*(e.grid[0]||1),l=Math.round((f.height-g.height)/(e.grid[1]||1))*(e.grid[1]||1);/^(se|s|e)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l):/^(ne)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l,d.position.top=h.top-l):/^(sw)$/.test(i)?(d.size.width=g.width+k,d.size.height=g.height+l,d.position.left=h.left-k):(d.size.width=g.width+k,d.size.height=g.height+l,d.position.top=h.top-l,d.position.left=h.left-k)}});var c=function(a){return parseInt(a,10)||0},d=function(a){return!isNaN(parseInt(a,10))}})(jQuery);/*
+ * jQuery UI Selectable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.selectable",a.ui.mouse,{options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch"},_create:function(){var b=this;this.element.addClass("ui-selectable"),this.dragged=!1;var c;this.refresh=function(){c=a(b.options.filter,b.element[0]),c.addClass("ui-selectee"),c.each(function(){var b=a(this),c=b.offset();a.data(this,"selectable-item",{element:this,$element:b,left:c.left,top:c.top,right:c.left+b.outerWidth(),bottom:c.top+b.outerHeight(),startselected:!1,selected:b.hasClass("ui-selected"),selecting:b.hasClass("ui-selecting"),unselecting:b.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=c.addClass("ui-selectee"),this._mouseInit(),this.helper=a("<div class='ui-selectable-helper'></div>")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable"),this._mouseDestroy();return this},_mouseStart:function(b){var c=this;this.opos=[b.pageX,b.pageY];if(!this.options.disabled){var d=this.options;this.selectees=a(d.filter,this.element[0]),this._trigger("start",b),a(d.appendTo).append(this.helper),this.helper.css({left:b.clientX,top:b.clientY,width:0,height:0}),d.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var d=a.data(this,"selectable-item");d.startselected=!0,!b.metaKey&&!b.ctrlKey&&(d.$element.removeClass("ui-selected"),d.selected=!1,d.$element.addClass("ui-unselecting"),d.unselecting=!0,c._trigger("unselecting",b,{unselecting:d.element}))}),a(b.target).parents().andSelf().each(function(){var d=a.data(this,"selectable-item");if(d){var e=!b.metaKey&&!b.ctrlKey||!d.$element.hasClass("ui-selected");d.$element.removeClass(e?"ui-unselecting":"ui-selected").addClass(e?"ui-selecting":"ui-unselecting"),d.unselecting=!e,d.selecting=e,d.selected=e,e?c._trigger("selecting",b,{selecting:d.element}):c._trigger("unselecting",b,{unselecting:d.element});return!1}})}},_mouseDrag:function(b){var c=this;this.dragged=!0;if(!this.options.disabled){var d=this.options,e=this.opos[0],f=this.opos[1],g=b.pageX,h=b.pageY;if(e>g){var i=g;g=e,e=i}if(f>h){var i=h;h=f,f=i}this.helper.css({left:e,top:f,width:g-e,height:h-f}),this.selectees.each(function(){var i=a.data(this,"selectable-item");if(!!i&&i.element!=c.element[0]){var j=!1;d.tolerance=="touch"?j=!(i.left>g||i.right<e||i.top>h||i.bottom<f):d.tolerance=="fit"&&(j=i.left>e&&i.right<g&&i.top>f&&i.bottom<h),j?(i.selected&&(i.$element.removeClass("ui-selected"),i.selected=!1),i.unselecting&&(i.$element.removeClass("ui-unselecting"),i.unselecting=!1),i.selecting||(i.$element.addClass("ui-selecting"),i.selecting=!0,c._trigger("selecting",b,{selecting:i.element}))):(i.selecting&&((b.metaKey||b.ctrlKey)&&i.startselected?(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.$element.addClass("ui-selected"),i.selected=!0):(i.$element.removeClass("ui-selecting"),i.selecting=!1,i.startselected&&(i.$element.addClass("ui-unselecting"),i.unselecting=!0),c._trigger("unselecting",b,{unselecting:i.element}))),i.selected&&!b.metaKey&&!b.ctrlKey&&!i.startselected&&(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,c._trigger("unselecting",b,{unselecting:i.element})))}});return!1}},_mouseStop:function(b){var c=this;this.dragged=!1;var d=this.options;a(".ui-unselecting",this.element[0]).each(function(){var d=a.data(this,"selectable-item");d.$element.removeClass("ui-unselecting"),d.unselecting=!1,d.startselected=!1,c._trigger("unselected",b,{unselected:d.element})}),a(".ui-selecting",this.element[0]).each(function(){var d=a.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass("ui-selected"),d.selecting=!1,d.selected=!0,d.startselected=!0,c._trigger("selected",b,{selected:d.element})}),this._trigger("stop",b),this.helper.remove();return!1}}),a.extend(a.ui.selectable,{version:"1.8.18"})})(jQuery);/*
+ * jQuery UI Sortable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Sortables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.sortable",a.ui.mouse,{widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3},_create:function(){var a=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):!1,this.offset=this.element.offset(),this._mouseInit(),this.ready=!0},destroy:function(){a.Widget.prototype.destroy.call(this),this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouseDestroy();for(var b=this.items.length-1;b>=0;b--)this.items[b].item.removeData(this.widgetName+"-item");return this},_setOption:function(b,c){b==="disabled"?(this.options[b]=c,this.widget()[c?"addClass":"removeClass"]("ui-sortable-disabled")):a.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(b,c){var d=this;if(this.reverting)return!1;if(this.options.disabled||this.options.type=="static")return!1;this._refreshItems(b);var e=null,f=this,g=a(b.target).parents().each(function(){if(a.data(this,d.widgetName+"-item")==f){e=a(this);return!1}});a.data(b.target,d.widgetName+"-item")==f&&(e=a(b.target));if(!e)return!1;if(this.options.handle&&!c){var h=!1;a(this.options.handle,e).find("*").andSelf().each(function(){this==b.target&&(h=!0)});if(!h)return!1}this.currentItem=e,this._removeCurrentsFromItems();return!0},_mouseStart:function(b,c,d){var e=this.options,f=this;this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(b),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),a.extend(this.offset,{click:{left:b.pageX-this.offset.left,top:b.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this._generatePosition(b),this.originalPageX=b.pageX,this.originalPageY=b.pageY,e.cursorAt&&this._adjustOffsetFromHelper(e.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!=this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),e.containment&&this._setContainment(),e.cursor&&(a("body").css("cursor")&&(this._storedCursor=a("body").css("cursor")),a("body").css("cursor",e.cursor)),e.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",e.opacity)),e.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",e.zIndex)),this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",b,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions();if(!d)for(var g=this.containers.length-1;g>=0;g--)this.containers[g]._trigger("activate",b,f._uiHash(this));a.ui.ddmanager&&(a.ui.ddmanager.current=this),a.ui.ddmanager&&!e.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(b);return!0},_mouseDrag:function(b){this.position=this._generatePosition(b),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs);if(this.options.scroll){var c=this.options,d=!1;this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-b.pageY<c.scrollSensitivity?this.scrollParent[0].scrollTop=d=this.scrollParent[0].scrollTop+c.scrollSpeed:b.pageY-this.overflowOffset.top<c.scrollSensitivity&&(this.scrollParent[0].scrollTop=d=this.scrollParent[0].scrollTop-c.scrollSpeed),this.overflowOffset.left+this.scrollParent[0].offsetWidth-b.pageX<c.scrollSensitivity?this.scrollParent[0].scrollLeft=d=this.scrollParent[0].scrollLeft+c.scrollSpeed:b.pageX-this.overflowOffset.left<c.scrollSensitivity&&(this.scrollParent[0].scrollLeft=d=this.scrollParent[0].scrollLeft-c.scrollSpeed)):(b.pageY-a(document).scrollTop()<c.scrollSensitivity?d=a(document).scrollTop(a(document).scrollTop()-c.scrollSpeed):a(window).height()-(b.pageY-a(document).scrollTop())<c.scrollSensitivity&&(d=a(document).scrollTop(a(document).scrollTop()+c.scrollSpeed)),b.pageX-a(document).scrollLeft()<c.scrollSensitivity?d=a(document).scrollLeft(a(document).scrollLeft()-c.scrollSpeed):a(window).width()-(b.pageX-a(document).scrollLeft())<c.scrollSensitivity&&(d=a(document).scrollLeft(a(document).scrollLeft()+c.scrollSpeed))),d!==!1&&a.ui.ddmanager&&!c.dropBehaviour&&a.ui.ddmanager.prepareOffsets(this,b)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(var e=this.items.length-1;e>=0;e--){var f=this.items[e],g=f.item[0],h=this._intersectsWithPointer(f);if(!h)continue;if(g!=this.currentItem[0]&&this.placeholder[h==1?"next":"prev"]()[0]!=g&&!a.ui.contains(this.placeholder[0],g)&&(this.options.type=="semi-dynamic"?!a.ui.contains(this.element[0],g):!0)){this.direction=h==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(f))this._rearrange(b,f);else break;this._trigger("change",b,this._uiHash());break}}this._contactContainers(b),a.ui.ddmanager&&a.ui.ddmanager.drag(this,b),this._trigger("sort",b,this._uiHash()),this.lastPositionAbs=this.positionAbs;return!1},_mouseStop:function(b,c){if(!!b){a.ui.ddmanager&&!this.options.dropBehaviour&&a.ui.ddmanager.drop(this,b);if(this.options.revert){var d=this,e=d.placeholder.offset();d.reverting=!0,a(this.helper).animate({left:e.left-this.offset.parent.left-d.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:e.top-this.offset.parent.top-d.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){d._clear(b)})}else this._clear(b,c);return!1}},cancel:function(){var b=this;if(this.dragging){this._mouseUp({target:null}),this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("deactivate",null,b._uiHash(this)),this.containers[c].containerCache.over&&(this.containers[c]._trigger("out",null,b._uiHash(this)),this.containers[c].containerCache.over=0)}this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),a.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?a(this.domPosition.prev).after(this.currentItem):a(this.domPosition.parent).prepend(this.currentItem));return this},serialize:function(b){var c=this._getItemsAsjQuery(b&&b.connected),d=[];b=b||{},a(c).each(function(){var c=(a(b.item||this).attr(b.attribute||"id")||"").match(b.expression||/(.+)[-=_](.+)/);c&&d.push((b.key||c[1]+"[]")+"="+(b.key&&b.expression?c[1]:c[2]))}),!d.length&&b.key&&d.push(b.key+"=");return d.join("&")},toArray:function(b){var c=this._getItemsAsjQuery(b&&b.connected),d=[];b=b||{},c.each(function(){d.push(a(b.item||this).attr(b.attribute||"id")||"")});return d},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,d=this.positionAbs.top,e=d+this.helperProportions.height,f=a.left,g=f+a.width,h=a.top,i=h+a.height,j=this.offset.click.top,k=this.offset.click.left,l=d+j>h&&d+j<i&&b+k>f&&b+k<g;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?l:f<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<g&&h<d+this.helperProportions.height/2&&e-this.helperProportions.height/2<i},_intersectsWithPointer:function(b){var c=a.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,b.top,b.height),d=a.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,b.left,b.width),e=c&&d,f=this._getDragVerticalDirection(),g=this._getDragHorizontalDirection();if(!e)return!1;return this.floating?g&&g=="right"||f=="down"?2:1:f&&(f=="down"?2:1)},_intersectsWithSides:function(b){var c=a.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,b.top+b.height/2,b.height),d=a.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,b.left+b.width/2,b.width),e=this._getDragVerticalDirection(),f=this._getDragHorizontalDirection();return this.floating&&f?f=="right"&&d||f=="left"&&!d:e&&(e=="down"&&c||e=="up"&&!c)},_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a),this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(b){var c=this,d=[],e=[],f=this._connectWith();if(f&&b)for(var g=f.length-1;g>=0;g--){var h=a(f[g]);for(var i=h.length-1;i>=0;i--){var j=a.data(h[i],this.widgetName);j&&j!=this&&!j.options.disabled&&e.push([a.isFunction(j.options.items)?j.options.items.call(j.element):a(j.options.items,j.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),j])}}e.push([a.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):a(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(var g=e.length-1;g>=0;g--)e[g][0].each(function(){d.push(this)});return a(d)},_removeCurrentsFromItems:function(){var a=this.currentItem.find(":data("+this.widgetName+"-item)");for(var b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(b){this.items=[],this.containers=[this];var c=this.items,d=this,e=[[a.isFunction(this.options.items)?this.options.items.call(this.element[0],b,{item:this.currentItem}):a(this.options.items,this.element),this]],f=this._connectWith();if(f&&this.ready)for(var g=f.length-1;g>=0;g--){var h=a(f[g]);for(var i=h.length-1;i>=0;i--){var j=a.data(h[i],this.widgetName);j&&j!=this&&!j.options.disabled&&(e.push([a.isFunction(j.options.items)?j.options.items.call(j.element[0],b,{item:this.currentItem}):a(j.options.items,j.element),j]),this.containers.push(j))}}for(var g=e.length-1;g>=0;g--){var k=e[g][1],l=e[g][0];for(var i=0,m=l.length;i<m;i++){var n=a(l[i]);n.data(this.widgetName+"-item",k),c.push({item:n,instance:k,width:0,height:0,left:0,top:0})}}},refreshPositions:function(b){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());for(var c=this.items.length-1;c>=0;c--){var d=this.items[c];if(d.instance!=this.currentContainer&&this.currentContainer&&d.item[0]!=this.currentItem[0])continue;var e=this.options.toleranceElement?a(this.options.toleranceElement,d.item):d.item;b||(d.width=e.outerWidth(),d.height=e.outerHeight());var f=e.offset();d.left=f.left,d.top=f.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(var c=this.containers.length-1;c>=0;c--){var f=this.containers[c].element.offset();this.containers[c].containerCache.left=f.left,this.containers[c].containerCache.top=f.top,this.containers[c].containerCache.width=this.containers[c].element.outerWidth(),this.containers[c].containerCache.height=this.containers[c].element.outerHeight()}return this},_createPlaceholder:function(b){var c=b||this,d=c.options;if(!d.placeholder||d.placeholder.constructor==String){var e=d.placeholder;d.placeholder={element:function(){var b=a(document.createElement(c.currentItem[0].nodeName)).addClass(e||c.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];e||(b.style.visibility="hidden");return b},update:function(a,b){if(!e||!!d.forcePlaceholderSize)b.height()||b.height(c.currentItem.innerHeight()-parseInt(c.currentItem.css("paddingTop")||0,10)-parseInt(c.currentItem.css("paddingBottom")||0,10)),b.width()||b.width(c.currentItem.innerWidth()-parseInt(c.currentItem.css("paddingLeft")||0,10)-parseInt(c.currentItem.css("paddingRight")||0,10))}}}c.placeholder=a(d.placeholder.element.call(c.element,c.currentItem)),c.currentItem.after(c.placeholder),d.placeholder.update(c,c.placeholder)},_contactContainers:function(b){var c=null,d=null;for(var e=this.containers.length-1;e>=0;e--){if(a.ui.contains(this.currentItem[0],this.containers[e].element[0]))continue;if(this._intersectsWith(this.containers[e].containerCache)){if(c&&a.ui.contains(this.containers[e].element[0],c.element[0]))continue;c=this.containers[e],d=e}else this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",b,this._uiHash(this)),this.containers[e].containerCache.over=0)}if(!!c)if(this.containers.length===1)this.containers[d]._trigger("over",b,this._uiHash(this)),this.containers[d].containerCache.over=1;else if(this.currentContainer!=this.containers[d]){var f=1e4,g=null,h=this.positionAbs[this.containers[d].floating?"left":"top"];for(var i=this.items.length-1;i>=0;i--){if(!a.ui.contains(this.containers[d].element[0],this.items[i].item[0]))continue;var j=this.items[i][this.containers[d].floating?"left":"top"];Math.abs(j-h)<f&&(f=Math.abs(j-h),g=this.items[i])}if(!g&&!this.options.dropOnEmpty)return;this.currentContainer=this.containers[d],g?this._rearrange(b,g,null,!0):this._rearrange(b,null,this.containers[d].element,!0),this._trigger("change",b,this._uiHash()),this.containers[d]._trigger("change",b,this._uiHash(this)),this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[d]._trigger("over",b,this._uiHash(this)),this.containers[d].containerCache.over=1}},_createHelper:function(b){var c=this.options,d=a.isFunction(c.helper)?a(c.helper.apply(this.element[0],[b,this.currentItem])):c.helper=="clone"?this.currentItem.clone():this.currentItem;d.parents("body").length||a(c.appendTo!="parent"?c.appendTo:this.currentItem[0].parentNode)[0].appendChild(d[0]),d[0]==this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(d[0].style.width==""||c.forceHelperSize)&&d.width(this.currentItem.width()),(d[0].style.height==""||c.forceHelperSize)&&d.height(this.currentItem.height());return d},_adjustOffsetFromHelper:function(b){typeof b=="string"&&(b=b.split(" ")),a.isArray(b)&&(b={left:+b[0],top:+b[1]||0}),"left"in b&&(this.offset.click.left=b.left+this.margins.left),"right"in b&&(this.offset.click.left=this.helperProportions.width-b.right+this.margins.left),"top"in b&&(this.offset.click.top=b.top+this.margins.top),"bottom"in b&&(this.offset.click.top=this.helperProportions.height-b.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var b=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&a.ui.contains(this.scrollParent[0],this.offsetParent[0])&&(b.left+=this.scrollParent.scrollLeft(),b.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&a.browser.msie)b={top:0,left:0};return{top:b.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:b.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var b=this.options;b.containment=="parent"&&(b.containment=this.helper[0].parentNode);if(b.containment=="document"||b.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,a(b.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a(b.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(b.containment)){var c=a(b.containment)[0],d=a(b.containment).offset(),e=a(c).css("overflow")!="hidden";this.containment=[d.left+(parseInt(a(c).css("borderLeftWidth"),10)||0)+(parseInt(a(c).css("paddingLeft"),10)||0)-this.margins.left,d.top+(parseInt(a(c).css("borderTopWidth"),10)||0)+(parseInt(a(c).css("paddingTop"),10)||0)-this.margins.top,d.left+(e?Math.max(c.scrollWidth,c.offsetWidth):c.offsetWidth)-(parseInt(a(c).css("borderLeftWidth"),10)||0)-(parseInt(a(c).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,d.top+(e?Math.max(c.scrollHeight,c.offsetHeight):c.offsetHeight)-(parseInt(a(c).css("borderTopWidth"),10)||0)-(parseInt(a(c).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(b,c){c||(c=this.position);var d=b=="absolute"?1:-1,e=this.options,f=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,g=/(html|body)/i.test(f[0].tagName);return{top:c.top+this.offset.relative.top*d+this.offset.parent.top*d-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():g?0:f.scrollTop())*d),left:c.left+this.offset.relative.left*d+this.offset.parent.left*d-(a.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():g?0:f.scrollLeft())*d)}},_generatePosition:function(b){var c=this.options,d=this.cssPosition=="absolute"&&(this.scrollParent[0]==document||!a.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(d[0].tagName);this.cssPosition=="relative"&&(this.scrollParent[0]==document||this.scrollParent[0]==this.offsetParent[0])&&(this.offset.relative=this._getRelativeOffset());var f=b.pageX,g=b.pageY;if(this.originalPosition){this.containment&&(b.pageX-this.offset.click.left<this.containment[0]&&(f=this.containment[0]+this.offset.click.left),b.pageY-this.offset.click.top<this.containment[1]&&(g=this.containment[1]+this.offset.click.top),b.pageX-this.offset.click.left>this.containment[2]&&(f=this.containment[2]+this.offset.click.left),b.pageY-this.offset.click.top>this.containment[3]&&(g=this.containment[3]+this.offset.click.top));if(c.grid){var h=this.originalPageY+Math.round((g-this.originalPageY)/c.grid[1])*c.grid[1];g=this.containment?h-this.offset.click.top<this.containment[1]||h-this.offset.click.top>this.containment[3]?h-this.offset.click.top<this.containment[1]?h+c.grid[1]:h-c.grid[1]:h:h;var i=this.originalPageX+Math.round((f-this.originalPageX)/c.grid[0])*c.grid[0];f=this.containment?i-this.offset.click.left<this.containment[0]||i-this.offset.click.left>this.containment[2]?i-this.offset.click.left<this.containment[0]?i+c.grid[0]:i-c.grid[0]:i:i}}return{top:g-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(a.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:d.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(a.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:d.scrollLeft())}},_rearrange:function(a,b,c,d){c?c[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],this.direction=="down"?b.item[0]:b.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var e=this,f=this.counter;window.setTimeout(function(){f==e.counter&&e.refreshPositions(!d)},0)},_clear:function(b,c){this.reverting=!1;var d=[],e=this;!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var f in this._storedCSS)if(this._storedCSS[f]=="auto"||this._storedCSS[f]=="static")this._storedCSS[f]="";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();this.fromOutside&&!c&&d.push(function(a){this._trigger("receive",a,this._uiHash(this.fromOutside))}),(this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!c&&d.push(function(a){this._trigger("update",a,this._uiHash())});if(!a.ui.contains(this.element[0],this.currentItem[0])){c||d.push(function(a){this._trigger("remove",a,this._uiHash())});for(var f=this.containers.length-1;f>=0;f--)a.ui.contains(this.containers[f].element[0],this.currentItem[0])&&!c&&(d.push(function(a){return function(b){a._trigger("receive",b,this._uiHash(this))}}.call(this,this.containers[f])),d.push(function(a){return function(b){a._trigger("update",b,this._uiHash(this))}}.call(this,this.containers[f])))}for(var f=this.containers.length-1;f>=0;f--)c||d.push(function(a){return function(b){a._trigger("deactivate",b,this._uiHash(this))}}.call(this,this.containers[f])),this.containers[f].containerCache.over&&(d.push(function(a){return function(b){a._trigger("out",b,this._uiHash(this))}}.call(this,this.containers[f])),this.containers[f].containerCache.over=0);this._storedCursor&&a("body").css("cursor",this._storedCursor),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex),this.dragging=!1;if(this.cancelHelperRemoval){if(!c){this._trigger("beforeStop",b,this._uiHash());for(var f=0;f<d.length;f++)d[f].call(this,b);this._trigger("stop",b,this._uiHash())}return!1}c||this._trigger("beforeStop",b,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!=this.currentItem[0]&&this.helper.remove(),this.helper=null;if(!c){for(var f=0;f<d.length;f++)d[f].call(this,b);this._trigger("stop",b,this._uiHash())}this.fromOutside=!1;return!0},_trigger:function(){a.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(b){var c=b||this;return{helper:c.helper,placeholder:c.placeholder||a([]),position:c.position,originalPosition:c.originalPosition,offset:c.positionAbs,item:c.currentItem,sender:b?b.element:null}}}),a.extend(a.ui.sortable,{version:"1.8.18"})})(jQuery);/*
+ * jQuery UI Accordion 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:!0,clearStyle:!1,collapsible:!1,event:"click",fillSpace:!1,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:!1,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var b=this,c=b.options;b.running=0,b.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix"),b.headers=b.element.find(c.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){c.disabled||a(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){c.disabled||a(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){c.disabled||a(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){c.disabled||a(this).removeClass("ui-state-focus")}),b.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");if(c.navigation){var d=b.element.find("a").filter(c.navigationFilter).eq(0);if(d.length){var e=d.closest(".ui-accordion-header");e.length?b.active=e:b.active=d.closest(".ui-accordion-content").prev()}}b.active=b._findActive(b.active||c.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top"),b.active.next().addClass("ui-accordion-content-active"),b._createIcons(),b.resize(),b.element.attr("role","tablist"),b.headers.attr("role","tab").bind("keydown.accordion",function(a){return b._keydown(a)}).next().attr("role","tabpanel"),b.headers.not(b.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide(),b.active.length?b.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):b.headers.eq(0).attr("tabIndex",0),a.browser.safari||b.headers.find("a").attr("tabIndex",-1),c.event&&b.headers.bind(c.event.split(" ").join(".accordion ")+".accordion",function(a){b._clickHandler.call(b,a,this),a.preventDefault()})},_createIcons:function(){var b=this.options;b.icons&&(a("<span></span>").addClass("ui-icon "+b.icons.header).prependTo(this.headers),this.active.children(".ui-icon").toggleClass(b.icons.header).toggleClass(b.icons.headerSelected),this.element.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.children(".ui-icon").remove(),this.element.removeClass("ui-accordion-icons")},destroy:function(){var b=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex"),this.headers.find("a").removeAttr("tabIndex"),this._destroyIcons();var c=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");(b.autoHeight||b.fillHeight)&&c.css("height","");return a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments),b=="active"&&this.activate(c),b=="icons"&&(this._destroyIcons(),c&&this._createIcons()),b=="disabled"&&this.headers.add(this.headers.next())[c?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(b){if(!(this.options.disabled||b.altKey||b.ctrlKey)){var c=a.ui.keyCode,d=this.headers.length,e=this.headers.index(b.target),f=!1;switch(b.keyCode){case c.RIGHT:case c.DOWN:f=this.headers[(e+1)%d];break;case c.LEFT:case c.UP:f=this.headers[(e-1+d)%d];break;case c.SPACE:case c.ENTER:this._clickHandler({target:b.target},b.target),b.preventDefault()}if(f){a(b.target).attr("tabIndex",-1),a(f).attr("tabIndex",0),f.focus();return!1}return!0}},resize:function(){var b=this.options,c;if(b.fillSpace){if(a.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}c=this.element.parent().height(),a.browser.msie&&this.element.parent().css("overflow",d),this.headers.each(function(){c-=a(this).outerHeight(!0)}),this.headers.next().each(function(){a(this).height(Math.max(0,c-a(this).innerHeight()+a(this).height()))}).css("overflow","auto")}else b.autoHeight&&(c=0,this.headers.next().each(function(){c=Math.max(c,a(this).height("").height())}).height(c));return this},activate:function(a){this.options.active=a;var b=this._findActive(a)[0];this._clickHandler({target:b},b);return this},_findActive:function(b){return b?typeof b=="number"?this.headers.filter(":eq("+b+")"):this.headers.not(this.headers.not(b)):b===!1?a([]):this.headers.filter(":eq(0)")},_clickHandler:function(b,c){var d=this.options;if(!d.disabled){if(!b.target){if(!d.collapsible)return;this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header),this.active.next().addClass("ui-accordion-content-active");var e=this.active.next(),f={options:d,newHeader:a([]),oldHeader:d.active,newContent:a([]),oldContent:e},g=this.active=a([]);this._toggle(g,e,f);return}var h=a(b.currentTarget||c),i=h[0]===this.active[0];d.active=d.collapsible&&i?!1:this.headers.index(h);if(this.running||!d.collapsible&&i)return;var j=this.active,g=h.next(),e=this.active.next(),f={options:d,newHeader:i&&d.collapsible?a([]):h,oldHeader:this.active,newContent:i&&d.collapsible?a([]):g,oldContent:e},k=this.headers.index(this.active[0])>this.headers.index(h[0]);this.active=i?a([]):h,this._toggle(g,e,f,i,k),j.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header),i||(h.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected),h.next().addClass("ui-accordion-content-active"));return}},_toggle:function(b,c,d,e,f){var g=this,h=g.options;g.toShow=b,g.toHide=c,g.data=d;var i=function(){if(!!g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data),g.running=c.size()===0?b.size():c.size();if(h.animated){var j={};h.collapsible&&e?j={toShow:a([]),toHide:c,complete:i,down:f,autoHeight:h.autoHeight||h.fillSpace}:j={toShow:b,toHide:c,complete:i,down:f,autoHeight:h.autoHeight||h.fillSpace},h.proxied||(h.proxied=h.animated),h.proxiedDuration||(h.proxiedDuration=h.duration),h.animated=a.isFunction(h.proxied)?h.proxied(j):h.proxied,h.duration=a.isFunction(h.proxiedDuration)?h.proxiedDuration(j):h.proxiedDuration;var k=a.ui.accordion.animations,l=h.duration,m=h.animated;m&&!k[m]&&!a.easing[m]&&(m="slide"),k[m]||(k[m]=function(a){this.slide(a,{easing:m,duration:l||700})}),k[m](j)}else h.collapsible&&e?b.toggle():(c.hide(),b.show()),i(!0);c.prev().attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).blur(),b.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;this.running||(this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""}),this.toHide.removeClass("ui-accordion-content-active"),this.toHide.length&&(this.toHide.parent()[0].className=this.toHide.parent()[0].className),this._trigger("change",null,this.data))}}),a.extend(a.ui.accordion,{version:"1.8.18",animations:{slide:function(b,c){b=a.extend({easing:"swing",duration:300},b,c);if(!b.toHide.size())b.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},b);else{if(!b.toShow.size()){b.toHide.animate({height:"hide",paddingTop:"hide",paddingBottom:"hide"},b);return}var d=b.toShow.css("overflow"),e=0,f={},g={},h=["height","paddingTop","paddingBottom"],i,j=b.toShow;i=j[0].style.width,j.width(j.parent().width()-parseFloat(j.css("paddingLeft"))-parseFloat(j.css("paddingRight"))-(parseFloat(j.css("borderLeftWidth"))||0)-(parseFloat(j.css("borderRightWidth"))||0)),a.each(h,function(c,d){g[d]="hide";var e=(""+a.css(b.toShow[0],d)).match(/^([\d+-.]+)(.*)$/);f[d]={value:e[1],unit:e[2]||"px"}}),b.toShow.css({height:0,overflow:"hidden"}).show(),b.toHide.filter(":hidden").each(b.complete).end().filter(":visible").animate(g,{step:function(a,c){c.prop=="height"&&(e=c.end-c.start===0?0:(c.now-c.start)/(c.end-c.start)),b.toShow[0].style[c.prop]=e*f[c.prop].value+f[c.prop].unit},duration:b.duration,easing:b.easing,complete:function(){b.autoHeight||b.toShow.css("height",""),b.toShow.css({width:i,overflow:d}),b.complete()}})}},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1e3:200})}}})})(jQuery);/*
+ * jQuery UI Autocomplete 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.position.js
+ */(function(a,b){var c=0;a.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var b=this,c=this.element[0].ownerDocument,d;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!b.options.disabled&&!b.element.propAttr("readOnly")){d=!1;var e=a.ui.keyCode;switch(c.keyCode){case e.PAGE_UP:b._move("previousPage",c);break;case e.PAGE_DOWN:b._move("nextPage",c);break;case e.UP:b._move("previous",c),c.preventDefault();break;case e.DOWN:b._move("next",c),c.preventDefault();break;case e.ENTER:case e.NUMPAD_ENTER:b.menu.active&&(d=!0,c.preventDefault());case e.TAB:if(!b.menu.active)return;b.menu.select(c);break;case e.ESCAPE:b.element.val(b.term),b.close(c);break;default:clearTimeout(b.searching),b.searching=setTimeout(function(){b.term!=b.element.val()&&(b.selectedItem=null,b.search(null,c))},b.options.delay)}}}).bind("keypress.autocomplete",function(a){d&&(d=!1,a.preventDefault())}).bind("focus.autocomplete",function(){b.options.disabled||(b.selectedItem=null,b.previous=b.element.val())}).bind("blur.autocomplete",function(a){b.options.disabled||(clearTimeout(b.searching),b.closing=setTimeout(function(){b.close(a),b._change(a)},150))}),this._initSource(),this.response=function(){return b._response.apply(b,arguments)},this.menu=a("<ul></ul>").addClass("ui-autocomplete").appendTo(a(this.options.appendTo||"body",c)[0]).mousedown(function(c){var d=b.menu.element[0];a(c.target).closest(".ui-menu-item").length||setTimeout(function(){a(document).one("mousedown",function(c){c.target!==b.element[0]&&c.target!==d&&!a.ui.contains(d,c.target)&&b.close()})},1),setTimeout(function(){clearTimeout(b.closing)},13)}).menu({focus:function(a,c){var d=c.item.data("item.autocomplete");!1!==b._trigger("focus",a,{item:d})&&/^key/.test(a.originalEvent.type)&&b.element.val(d.value)},selected:function(a,d){var e=d.item.data("item.autocomplete"),f=b.previous;b.element[0]!==c.activeElement&&(b.element.focus(),b.previous=f,setTimeout(function(){b.previous=f,b.selectedItem=e},1)),!1!==b._trigger("select",a,{item:e})&&b.element.val(e.value),b.term=b.element.val(),b.close(a),b.selectedItem=e},blur:function(a,c){b.menu.element.is(":visible")&&b.element.val()!==b.term&&b.element.val(b.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu"),a.fn.bgiframe&&this.menu.element.bgiframe(),b.beforeunloadHandler=function(){b.element.removeAttr("autocomplete")},a(window).bind("beforeunload",b.beforeunloadHandler)},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup"),this.menu.element.remove(),a(window).unbind("beforeunload",this.beforeunloadHandler),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments),b==="source"&&this._initSource(),b==="appendTo"&&this.menu.element.appendTo(a(c||"body",this.element[0].ownerDocument)[0]),b==="disabled"&&c&&this.xhr&&this.xhr.abort()},_initSource:function(){var b=this,d,e;a.isArray(this.options.source)?(d=this.options.source,this.source=function(b,c){c(a.ui.autocomplete.filter(d,b.term))}):typeof this.options.source=="string"?(e=this.options.source,this.source=function(d,f){b.xhr&&b.xhr.abort(),b.xhr=a.ajax({url:e,data:d,dataType:"json",context:{autocompleteRequest:++c},success:function(a,b){this.autocompleteRequest===c&&f(a)},error:function(){this.autocompleteRequest===c&&f([])}})}):this.source=this.options.source},search:function(a,b){a=a!=null?a:this.element.val(),this.term=this.element.val();if(a.length<this.options.minLength)return this.close(b);clearTimeout(this.closing);if(this._trigger("search",b)!==!1)return this._search(a)},_search:function(a){this.pending++,this.element.addClass("ui-autocomplete-loading"),this.source({term:a},this.response)},_response:function(a){!this.options.disabled&&a&&a.length?(a=this._normalize(a),this._suggest(a),this._trigger("open")):this.close(),this.pending--,this.pending||this.element.removeClass("ui-autocomplete-loading")},close:function(a){clearTimeout(this.closing),this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.deactivate(),this._trigger("close",a))},_change:function(a){this.previous!==this.element.val()&&this._trigger("change",a,{item:this.selectedItem})},_normalize:function(b){if(b.length&&b[0].label&&b[0].value)return b;return a.map(b,function(b){if(typeof b=="string")return{label:b,value:b};return a.extend({label:b.label||b.value,value:b.value||b.label},b)})},_suggest:function(b){var c=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(c,b),this.menu.deactivate(),this.menu.refresh(),c.show(),this._resizeMenu(),c.position(a.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next(new a.Event("mouseover"))},_resizeMenu:function(){var a=this.menu.element;a.outerWidth(Math.max(a.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(b,c){var d=this;a.each(c,function(a,c){d._renderItem(b,c)})},_renderItem:function(b,c){return a("<li></li>").data("item.autocomplete",c).append(a("<a></a>").text(c.label)).appendTo(b)},_move:function(a,b){if(!this.menu.element.is(":visible"))this.search(null,b);else{if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term),this.menu.deactivate();return}this.menu[a](b)}},widget:function(){return this.menu.element}}),a.extend(a.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&")},filter:function(b,c){var d=new RegExp(a.ui.autocomplete.escapeRegex(c),"i");return a.grep(b,function(a){return d.test(a.label||a.value||a)})}})})(jQuery),function(a){a.widget("ui.menu",{_create:function(){var b=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(c){!a(c.target).closest(".ui-menu-item a").length||(c.preventDefault(),b.select(c))}),this.refresh()},refresh:function(){var b=this,c=this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem");c.children("a").addClass("ui-corner-all").attr("tabindex",-1).mouseenter(function(c){b.activate(c,a(this).parent())}).mouseleave(function(){b.deactivate()})},activate:function(a,b){this.deactivate();if(this.hasScroll()){var c=b.offset().top-this.element.offset().top,d=this.element.scrollTop(),e=this.element.height();c<0?this.element.scrollTop(d+c):c>=e&&this.element.scrollTop(d+c-e+b.height())}this.active=b.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end(),this._trigger("focus",a,{item:b})},deactivate:function(){!this.active||(this.active.children("a").removeClass("ui-state-hover").removeAttr("id"),this._trigger("blur"),this.active=null)},next:function(a){this.move("next",".ui-menu-item:first",a)},previous:function(a){this.move("prev",".ui-menu-item:last",a)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(a,b,c){if(!this.active)this.activate(c,this.element.children(b));else{var d=this.active[a+"All"](".ui-menu-item").eq(0);d.length?this.activate(c,d):this.activate(c,this.element.children(b))}},nextPage:function(b){if(this.hasScroll()){if(!this.active||this.last()){this.activate(b,this.element.children(".ui-menu-item:first"));return}var c=this.active.offset().top,d=this.element.height(),e=this.element.children(".ui-menu-item").filter(function(){var b=a(this).offset().top-c-d+a(this).height();return b<10&&b>-10});e.length||(e=this.element.children(".ui-menu-item:last")),this.activate(b,e)}else this.activate(b,this.element.children(".ui-menu-item").filter(!this.active||this.last()?":first":":last"))},previousPage:function(b){if(this.hasScroll()){if(!this.active||this.first()){this.activate(b,this.element.children(".ui-menu-item:last"));return}var c=this.active.offset().top,d=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var b=a(this).offset().top-c+d-a(this).height();return b<10&&b>-10}),result.length||(result=this.element.children(".ui-menu-item:first")),this.activate(b,result)}else this.activate(b,this.element.children(".ui-menu-item").filter(!this.active||this.first()?":last":":first"))},hasScroll:function(){return this.element.height()<this.element[a.fn.prop?"prop":"attr"]("scrollHeight")},select:function(a){this._trigger("selected",a,{item:this.active})}})}(jQuery);/*
+ * jQuery UI Button 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */(function(a,b){var c,d,e,f,g="ui-button ui-widget ui-state-default ui-corner-all",h="ui-state-hover ui-state-active ",i="ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",j=function(){var b=a(this).find(":ui-button");setTimeout(function(){b.button("refresh")},1)},k=function(b){var c=b.name,d=b.form,e=a([]);c&&(d?e=a(d).find("[name='"+c+"']"):e=a("[name='"+c+"']",b.ownerDocument).filter(function(){return!this.form}));return e};a.widget("ui.button",{options:{disabled:null,text:!0,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset.button").bind("reset.button",j),typeof this.options.disabled!="boolean"?this.options.disabled=!!this.element.propAttr("disabled"):this.element.propAttr("disabled",this.options.disabled),this._determineButtonType(),this.hasTitle=!!this.buttonElement.attr("title");var b=this,h=this.options,i=this.type==="checkbox"||this.type==="radio",l="ui-state-hover"+(i?"":" ui-state-active"),m="ui-state-focus";h.label===null&&(h.label=this.buttonElement.html()),this.buttonElement.addClass(g).attr("role","button").bind("mouseenter.button",function(){h.disabled||(a(this).addClass("ui-state-hover"),this===c&&a(this).addClass("ui-state-active"))}).bind("mouseleave.button",function(){h.disabled||a(this).removeClass(l)}).bind("click.button",function(a){h.disabled&&(a.preventDefault(),a.stopImmediatePropagation())}),this.element.bind("focus.button",function(){b.buttonElement.addClass(m)}).bind("blur.button",function(){b.buttonElement.removeClass(m)}),i&&(this.element.bind("change.button",function(){f||b.refresh()}),this.buttonElement.bind("mousedown.button",function(a){h.disabled||(f=!1,d=a.pageX,e=a.pageY)}).bind("mouseup.button",function(a){!h.disabled&&(d!==a.pageX||e!==a.pageY)&&(f=!0)})),this.type==="checkbox"?this.buttonElement.bind("click.button",function(){if(h.disabled||f)return!1;a(this).toggleClass("ui-state-active"),b.buttonElement.attr("aria-pressed",b.element[0].checked)}):this.type==="radio"?this.buttonElement.bind("click.button",function(){if(h.disabled||f)return!1;a(this).addClass("ui-state-active"),b.buttonElement.attr("aria-pressed","true");var c=b.element[0];k(c).not(c).map(function(){return a(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed","false")}):(this.buttonElement.bind("mousedown.button",function(){if(h.disabled)return!1;a(this).addClass("ui-state-active"),c=this,a(document).one("mouseup",function(){c=null})}).bind("mouseup.button",function(){if(h.disabled)return!1;a(this).removeClass("ui-state-active")}).bind("keydown.button",function(b){if(h.disabled)return!1;(b.keyCode==a.ui.keyCode.SPACE||b.keyCode==a.ui.keyCode.ENTER)&&a(this).addClass("ui-state-active")}).bind("keyup.button",function(){a(this).removeClass("ui-state-active")}),this.buttonElement.is("a")&&this.buttonElement.keyup(function(b){b.keyCode===a.ui.keyCode.SPACE&&a(this).click()})),this._setOption("disabled",h.disabled),this._resetButton()},_determineButtonType:function(){this.element.is(":checkbox")?this.type="checkbox":this.element.is(":radio")?this.type="radio":this.element.is("input")?this.type="input":this.type="button";if(this.type==="checkbox"||this.type==="radio"){var a=this.element.parents().filter(":last"),b="label[for='"+this.element.attr("id")+"']";this.buttonElement=a.find(b),this.buttonElement.length||(a=a.length?a.siblings():this.element.siblings(),this.buttonElement=a.filter(b),this.buttonElement.length||(this.buttonElement=a.find(b))),this.element.addClass("ui-helper-hidden-accessible");var c=this.element.is(":checked");c&&this.buttonElement.addClass("ui-state-active"),this.buttonElement.attr("aria-pressed",c)}else this.buttonElement=this.element},widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible"),this.buttonElement.removeClass(g+" "+h+" "+i).removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html()),this.hasTitle||this.buttonElement.removeAttr("title"),a.Widget.prototype.destroy.call(this)},_setOption:function(b,c){a.Widget.prototype._setOption.apply(this,arguments);b==="disabled"?c?this.element.propAttr("disabled",!0):this.element.propAttr("disabled",!1):this._resetButton()},refresh:function(){var b=this.element.is(":disabled");b!==this.options.disabled&&this._setOption("disabled",b),this.type==="radio"?k(this.element[0]).each(function(){a(this).is(":checked")?a(this).button("widget").addClass("ui-state-active").attr("aria-pressed","true"):a(this).button("widget").removeClass("ui-state-active").attr("aria-pressed","false")}):this.type==="checkbox"&&(this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed","true"):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed","false"))},_resetButton:function(){if(this.type==="input")this.options.label&&this.element.val(this.options.label);else{var b=this.buttonElement.removeClass(i),c=a("<span></span>",this.element[0].ownerDocument).addClass("ui-button-text").html(this.options.label).appendTo(b.empty()).text(),d=this.options.icons,e=d.primary&&d.secondary,f=[];d.primary||d.secondary?(this.options.text&&f.push("ui-button-text-icon"+(e?"s":d.primary?"-primary":"-secondary")),d.primary&&b.prepend("<span class='ui-button-icon-primary ui-icon "+d.primary+"'></span>"),d.secondary&&b.append("<span class='ui-button-icon-secondary ui-icon "+d.secondary+"'></span>"),this.options.text||(f.push(e?"ui-button-icons-only":"ui-button-icon-only"),this.hasTitle||b.attr("title",c))):f.push("ui-button-text-only"),b.addClass(f.join(" "))}}}),a.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(b,c){b==="disabled"&&this.buttons.button("option",b,c),a.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var b=this.element.css("direction")==="rtl";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(b?"ui-corner-right":"ui-corner-left").end().filter(":last").addClass(b?"ui-corner-left":"ui-corner-right").end().end()},destroy:function(){this.element.removeClass("ui-buttonset"),this.buttons.map(function(){return a(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy"),a.Widget.prototype.destroy.call(this)}})})(jQuery);/*
+ * jQuery UI Dialog 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.button.js
+ * jquery.ui.draggable.js
+ * jquery.ui.mouse.js
+ * jquery.ui.position.js
+ * jquery.ui.resizable.js
+ */(function(a,b){var c="ui-dialog ui-widget ui-widget-content ui-corner-all ",d={buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},e={maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},f=a.attrFn||{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0,click:!0};a.widget("ui.dialog",{options:{autoOpen:!0,buttons:{},closeOnEscape:!0,closeText:"close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:!1,maxWidth:!1,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",collision:"fit",using:function(b){var c=a(this).css(b).offset().top;c<0&&a(this).css("top",b.top-c)}},resizable:!0,show:null,stack:!0,title:"",width:300,zIndex:1e3},_create:function(){this.originalTitle=this.element.attr("title"),typeof this.originalTitle!="string"&&(this.originalTitle=""),this.options.title=this.options.title||this.originalTitle;var b=this,d=b.options,e=d.title||"&#160;",f=a.ui.dialog.getTitleId(b.element),g=(b.uiDialog=a("<div></div>")).appendTo(document.body).hide().addClass(c+d.dialogClass).css({zIndex:d.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(c){d.closeOnEscape&&!c.isDefaultPrevented()&&c.keyCode&&c.keyCode===a.ui.keyCode.ESCAPE&&(b.close(c),c.preventDefault())}).attr({role:"dialog","aria-labelledby":f}).mousedown(function(a){b.moveToTop(!1,a)}),h=b.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g),i=(b.uiDialogTitlebar=a("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),j=a('<a href="#"></a>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){j.addClass("ui-state-hover")},function(){j.removeClass("ui-state-hover")}).focus(function(){j.addClass("ui-state-focus")}).blur(function(){j.removeClass("ui-state-focus")}).click(function(a){b.close(a);return!1}).appendTo(i),k=(b.uiDialogTitlebarCloseText=a("<span></span>")).addClass("ui-icon ui-icon-closethick").text(d.closeText).appendTo(j),l=a("<span></span>").addClass("ui-dialog-title").attr("id",f).html(e).prependTo(i);a.isFunction(d.beforeclose)&&!a.isFunction(d.beforeClose)&&(d.beforeClose=d.beforeclose),i.find("*").add(i).disableSelection(),d.draggable&&a.fn.draggable&&b._makeDraggable(),d.resizable&&a.fn.resizable&&b._makeResizable(),b._createButtons(d.buttons),b._isOpen=!1,a.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy(),a.uiDialog.hide(),a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"),a.uiDialog.remove(),a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(b){var c=this,d,e;if(!1!==c._trigger("beforeClose",b)){c.overlay&&c.overlay.destroy(),c.uiDialog.unbind("keypress.ui-dialog"),c._isOpen=!1,c.options.hide?c.uiDialog.hide(c.options.hide,function(){c._trigger("close",b)}):(c.uiDialog.hide(),c._trigger("close",b)),a.ui.dialog.overlay.resize(),c.options.modal&&(d=0,a(".ui-dialog").each(function(){this!==c.uiDialog[0]&&(e=a(this).css("z-index"),isNaN(e)||(d=Math.max(d,e)))}),a.ui.dialog.maxZ=d);return c}},isOpen:function(){return this._isOpen},moveToTop:function(b,c){var d=this,e=d.options,f;if(e.modal&&!b||!e.stack&&!e.modal)return d._trigger("focus",c);e.zIndex>a.ui.dialog.maxZ&&(a.ui.dialog.maxZ=e.zIndex),d.overlay&&(a.ui.dialog.maxZ+=1,d.overlay.$el.css("z-index",a.ui.dialog.overlay.maxZ=a.ui.dialog.maxZ)),f={scrollTop:d.element.scrollTop(),scrollLeft:d.element.scrollLeft()},a.ui.dialog.maxZ+=1,d.uiDialog.css("z-index",a.ui.dialog.maxZ),d.element.attr(f),d._trigger("focus",c);return d},open:function(){if(!this._isOpen){var b=this,c=b.options,d=b.uiDialog;b.overlay=c.modal?new a.ui.dialog.overlay(b):null,b._size(),b._position(c.position),d.show(c.show),b.moveToTop(!0),c.modal&&d.bind("keydown.ui-dialog",function(b){if(b.keyCode===a.ui.keyCode.TAB){var c=a(":tabbable",this),d=c.filter(":first"),e=c.filter(":last");if(b.target===e[0]&&!b.shiftKey){d.focus(1);return!1}if(b.target===d[0]&&b.shiftKey){e.focus(1);return!1}}}),a(b.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus(),b._isOpen=!0,b._trigger("open");return b}},_createButtons:function(b){var c=this,d=!1,e=a("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=a("<div></div>").addClass("ui-dialog-buttonset").appendTo(e);c.uiDialog.find(".ui-dialog-buttonpane").remove(),typeof b=="object"&&b!==null&&a.each(b,function(){return!(d=!0)}),d&&(a.each(b,function(b,d){d=a.isFunction(d)?{click:d,text:b}:d;var e=a('<button type="button"></button>').click(function(){d.click.apply(c.element[0],arguments)}).appendTo(g);a.each(d,function(a,b){a!=="click"&&(a in f?e[a](b):e.attr(a,b))}),a.fn.button&&e.button()}),e.appendTo(c.uiDialog))},_makeDraggable:function(){function f(a){return{position:a.position,offset:a.offset}}var b=this,c=b.options,d=a(document),e;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(d,g){e=c.height==="auto"?"auto":a(this).height(),a(this).height(a(this).height()).addClass("ui-dialog-dragging"),b._trigger("dragStart",d,f(g))},drag:function(a,c){b._trigger("drag",a,f(c))},stop:function(g,h){c.position=[h.position.left-d.scrollLeft(),h.position.top-d.scrollTop()],a(this).removeClass("ui-dialog-dragging").height(e),b._trigger("dragStop",g,f(h)),a.ui.dialog.overlay.resize()}})},_makeResizable:function(c){function h(a){return{originalPosition:a.originalPosition,originalSize:a.originalSize,position:a.position,size:a.size}}c=c===b?this.options.resizable:c;var d=this,e=d.options,f=d.uiDialog.css("position"),g=typeof c=="string"?c:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:g,start:function(b,c){a(this).addClass("ui-dialog-resizing"),d._trigger("resizeStart",b,h(c))},resize:function(a,b){d._trigger("resize",a,h(b))},stop:function(b,c){a(this).removeClass("ui-dialog-resizing"),e.height=a(this).height(),e.width=a(this).width(),d._trigger("resizeStop",b,h(c)),a.ui.dialog.overlay.resize()}}).css("position",f).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(b){var c=[],d=[0,0],e;if(b){if(typeof b=="string"||typeof b=="object"&&"0"in b)c=b.split?b.split(" "):[b[0],b[1]],c.length===1&&(c[1]=c[0]),a.each(["left","top"],function(a,b){+c[a]===c[a]&&(d[a]=c[a],c[a]=b)}),b={my:c.join(" "),at:c.join(" "),offset:d.join(" ")};b=a.extend({},a.ui.dialog.prototype.options.position,b)}else b=a.ui.dialog.prototype.options.position;e=this.uiDialog.is(":visible"),e||this.uiDialog.show(),this.uiDialog.css({top:0,left:0}).position(a.extend({of:window},b)),e||this.uiDialog.hide()},_setOptions:function(b){var c=this,f={},g=!1;a.each(b,function(a,b){c._setOption(a,b),a in d&&(g=!0),a in e&&(f[a]=b)}),g&&this._size(),this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",f)},_setOption:function(b,d){var e=this,f=e.uiDialog;switch(b){case"beforeclose":b="beforeClose";break;case"buttons":e._createButtons(d);break;case"closeText":e.uiDialogTitlebarCloseText.text(""+d);break;case"dialogClass":f.removeClass(e.options.dialogClass).addClass(c+d);break;case"disabled":d?f.addClass("ui-dialog-disabled"):f.removeClass("ui-dialog-disabled");break;case"draggable":var g=f.is(":data(draggable)");g&&!d&&f.draggable("destroy"),!g&&d&&e._makeDraggable();break;case"position":e._position(d);break;case"resizable":var h=f.is(":data(resizable)");h&&!d&&f.resizable("destroy"),h&&typeof d=="string"&&f.resizable("option","handles",d),!h&&d!==!1&&e._makeResizable(d);break;case"title":a(".ui-dialog-title",e.uiDialogTitlebar).html(""+(d||"&#160;"))}a.Widget.prototype._setOption.apply(e,arguments)},_size:function(){var b=this.options,c,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0}),b.minWidth>b.width&&(b.width=b.minWidth),c=this.uiDialog.css({height:"auto",width:b.width}).height(),d=Math.max(0,b.minHeight-c);if(b.height==="auto")if(a.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();var f=this.element.css("height","auto").height();e||this.uiDialog.hide(),this.element.height(Math.max(f,d))}else this.element.height(Math.max(b.height-c,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}}),a.extend(a.ui.dialog,{version:"1.8.18",uuid:0,maxZ:0,getTitleId:function(a){var b=a.attr("id");b||(this.uuid+=1,b=this.uuid);return"ui-dialog-title-"+b},overlay:function(b){this.$el=a.ui.dialog.overlay.create(b)}}),a.extend(a.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:a.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),create:function(b){this.instances.length===0&&(setTimeout(function(){a.ui.dialog.overlay.instances.length&&a(document).bind(a.ui.dialog.overlay.events,function(b){if(a(b.target).zIndex()<a.ui.dialog.overlay.maxZ)return!1})},1),a(document).bind("keydown.dialog-overlay",function(c){b.options.closeOnEscape&&!c.isDefaultPrevented()&&c.keyCode&&c.keyCode===a.ui.keyCode.ESCAPE&&(b.close(c),c.preventDefault())}),a(window).bind("resize.dialog-overlay",a.ui.dialog.overlay.resize));var c=(this.oldInstances.pop()||a("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),height:this.height()});a.fn.bgiframe&&c.bgiframe(),this.instances.push(c);return c},destroy:function(b){var c=a.inArray(b,this.instances);c!=-1&&this.oldInstances.push(this.instances.splice(c,1)[0]),this.instances.length===0&&a([document,window]).unbind(".dialog-overlay"),b.remove();var d=0;a.each(this.instances,function(){d=Math.max(d,this.css("z-index"))}),this.maxZ=d},height:function(){var b,c;if(a.browser.msie&&a.browser.version<7){b=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),c=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return b<c?a(window).height()+"px":b+"px"}return a(document).height()+"px"},width:function(){var b,c;if(a.browser.msie){b=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth),c=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);return b<c?a(window).width()+"px":b+"px"}return a(document).width()+"px"},resize:function(){var b=a([]);a.each(a.ui.dialog.overlay.instances,function(){b=b.add(this)}),b.css({width:0,height:0}).css({width:a.ui.dialog.overlay.width(),height:a.ui.dialog.overlay.height()})}}),a.extend(a.ui.dialog.overlay.prototype,{destroy:function(){a.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);/*
+ * jQuery UI Slider 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */(function(a,b){var c=5;a.widget("ui.slider",a.ui.mouse,{widgetEventPrefix:"slide",options:{animate:!1,distance:0,max:100,min:0,orientation:"horizontal",range:!1,step:1,value:0,values:null},_create:function(){var b=this,d=this.options,e=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),f="<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",g=d.values&&d.values.length||1,h=[];this._keySliding=!1,this._mouseSliding=!1,this._animateOff=!0,this._handleIndex=null,this._detectOrientation(),this._mouseInit(),this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget"+" ui-widget-content"+" ui-corner-all"+(d.disabled?" ui-slider-disabled ui-disabled":"")),this.range=a([]),d.range&&(d.range===!0&&(d.values||(d.values=[this._valueMin(),this._valueMin()]),d.values.length&&d.values.length!==2&&(d.values=[d.values[0],d.values[0]])),this.range=a("<div></div>").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(d.range==="min"||d.range==="max"?" ui-slider-range-"+d.range:"")));for(var i=e.length;i<g;i+=1)h.push(f);this.handles=e.add(a(h.join("")).appendTo(b.element)),this.handle=this.handles.eq(0),this.handles.add(this.range).filter("a").click(function(a){a.preventDefault()}).hover(function(){d.disabled||a(this).addClass("ui-state-hover")},function(){a(this).removeClass("ui-state-hover")}).focus(function(){d.disabled?a(this).blur():(a(".ui-slider .ui-state-focus").removeClass("ui-state-focus"),a(this).addClass("ui-state-focus"))}).blur(function(){a(this).removeClass("ui-state-focus")}),this.handles.each(function(b){a(this).data("index.ui-slider-handle",b)}),this.handles.keydown(function(d){var e=a(this).data("index.ui-slider-handle"),f,g,h,i;if(!b.options.disabled){switch(d.keyCode){case a.ui.keyCode.HOME:case a.ui.keyCode.END:case a.ui.keyCode.PAGE_UP:case a.ui.keyCode.PAGE_DOWN:case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:d.preventDefault();if(!b._keySliding){b._keySliding=!0,a(this).addClass("ui-state-active"),f=b._start(d,e);if(f===!1)return}}i=b.options.step,b.options.values&&b.options.values.length?g=h=b.values(e):g=h=b.value();switch(d.keyCode){case a.ui.keyCode.HOME:h=b._valueMin();break;case a.ui.keyCode.END:h=b._valueMax();break;case a.ui.keyCode.PAGE_UP:h=b._trimAlignValue(g+(b._valueMax()-b._valueMin())/c);break;case a.ui.keyCode.PAGE_DOWN:h=b._trimAlignValue(g-(b._valueMax()-b._valueMin())/c);break;case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:if(g===b._valueMax())return;h=b._trimAlignValue(g+i);break;case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:if(g===b._valueMin())return;h=b._trimAlignValue(g-i)}b._slide(d,e,h)}}).keyup(function(c){var d=a(this).data("index.ui-slider-handle");b._keySliding&&(b._keySliding=!1,b._stop(c,d),b._change(c,d),a(this).removeClass("ui-state-active"))}),this._refreshValue(),this._animateOff=!1},destroy:function(){this.handles.remove(),this.range.remove(),this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider"),this._mouseDestroy();return this},_mouseCapture:function(b){var c=this.options,d,e,f,g,h,i,j,k,l;if(c.disabled)return!1;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()},this.elementOffset=this.element.offset(),d={x:b.pageX,y:b.pageY},e=this._normValueFromMouse(d),f=this._valueMax()-this._valueMin()+1,h=this,this.handles.each(function(b){var c=Math.abs(e-h.values(b));f>c&&(f=c,g=a(this),i=b)}),c.range===!0&&this.values(1)===c.min&&(i+=1,g=a(this.handles[i])),j=this._start(b,i);if(j===!1)return!1;this._mouseSliding=!0,h._handleIndex=i,g.addClass("ui-state-active").focus(),k=g.offset(),l=!a(b.target).parents().andSelf().is(".ui-slider-handle"),this._clickOffset=l?{left:0,top:0}:{left:b.pageX-k.left-g.width()/2,top:b.pageY-k.top-g.height()/2-(parseInt(g.css("borderTopWidth"),10)||0)-(parseInt(g.css("borderBottomWidth"),10)||0)+(parseInt(g.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(b,i,e),this._animateOff=!0;return!0},_mouseStart:function(a){return!0},_mouseDrag:function(a){var b={x:a.pageX,y:a.pageY},c=this._normValueFromMouse(b);this._slide(a,this._handleIndex,c);return!1},_mouseStop:function(a){this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(a,this._handleIndex),this._change(a,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1;return!1},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(a){var b,c,d,e,f;this.orientation==="horizontal"?(b=this.elementSize.width,c=a.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(b=this.elementSize.height,c=a.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),d=c/b,d>1&&(d=1),d<0&&(d=0),this.orientation==="vertical"&&(d=1-d),e=this._valueMax()-this._valueMin(),f=this._valueMin()+d*e;return this._trimAlignValue(f)},_start:function(a,b){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values());return this._trigger("start",a,c)},_slide:function(a,b,c){var d,e,f;this.options.values&&this.options.values.length?(d=this.values(b?0:1),this.options.values.length===2&&this.options.range===!0&&(b===0&&c>d||b===1&&c<d)&&(c=d),c!==this.values(b)&&(e=this.values(),e[b]=c,f=this._trigger("slide",a,{handle:this.handles[b],value:c,values:e}),d=this.values(b?0:1),f!==!1&&this.values(b,c,!0))):c!==this.value()&&(f=this._trigger("slide",a,{handle:this.handles[b],value:c}),f!==!1&&this.value(c))},_stop:function(a,b){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values()),this._trigger("stop",a,c)},_change:function(a,b){if(!this._keySliding&&!this._mouseSliding){var c={handle:this.handles[b],value:this.value()};this.options.values&&this.options.values.length&&(c.value=this.values(b),c.values=this.values()),this._trigger("change",a,c)}},value:function(a){if(arguments.length)this.options.value=this._trimAlignValue(a),this._refreshValue(),this._change(null,0);else return this._value()},values:function(b,c){var d,e,f;if(arguments.length>1)this.options.values[b]=this._trimAlignValue(c),this._refreshValue(),this._change(null,b);else{if(!arguments.length)return this._values();if(!a.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(b):this.value();d=this.options.values,e=arguments[0];for(f=0;f<d.length;f+=1)d[f]=this._trimAlignValue(e[f]),this._change(null,f);this._refreshValue()}},_setOption:function(b,c){var d,e=0;a.isArray(this.options.values)&&(e=this.options.values.length),a.Widget.prototype._setOption.apply(this,arguments);switch(b){case"disabled":c?(this.handles.filter(".ui-state-focus").blur(),this.handles.removeClass("ui-state-hover"),this.handles.propAttr("disabled",!0),this.element.addClass("ui-disabled")):(this.handles.propAttr("disabled",!1),this.element.removeClass("ui-disabled"));break;case"orientation":this._detectOrientation(),this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation),this._refreshValue();break;case"value":this._animateOff=!0,this._refreshValue(),this._change(null,0),this._animateOff=!1;break;case"values":this._animateOff=!0,this._refreshValue();for(d=0;d<e;d+=1)this._change(null,d);this._animateOff=!1}},_value:function(){var a=this.options.value;a=this._trimAlignValue(a);return a},_values:function(a){var b,c,d;if(arguments.length){b=this.options.values[a],b=this._trimAlignValue(b);return b}c=this.options.values.slice();for(d=0;d<c.length;d+=1)c[d]=this._trimAlignValue(c[d]);return c},_trimAlignValue:function(a){if(a<=this._valueMin())return this._valueMin();if(a>=this._valueMax())return this._valueMax();var b=this.options.step>0?this.options.step:1,c=(a-this._valueMin())%b,d=a-c;Math.abs(c)*2>=b&&(d+=c>0?b:-b);return parseFloat(d.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var b=this.options.range,c=this.options,d=this,e=this._animateOff?!1:c.animate,f,g={},h,i,j,k;this.options.values&&this.options.values.length?this.handles.each(function(b,i){f=(d.values(b)-d._valueMin())/(d._valueMax()-d._valueMin())*100,g[d.orientation==="horizontal"?"left":"bottom"]=f+"%",a(this).stop(1,1)[e?"animate":"css"](g,c.animate),d.options.range===!0&&(d.orientation==="horizontal"?(b===0&&d.range.stop(1,1)[e?"animate":"css"]({left:f+"%"},c.animate),b===1&&d.range[e?"animate":"css"]({width:f-h+"%"},{queue:!1,duration:c.animate})):(b===0&&d.range.stop(1,1)[e?"animate":"css"]({bottom:f+"%"},c.animate),b===1&&d.range[e?"animate":"css"]({height:f-h+"%"},{queue:!1,duration:c.animate}))),h=f}):(i=this.value(),j=this._valueMin(),k=this._valueMax(),f=k!==j?(i-j)/(k-j)*100:0,g[d.orientation==="horizontal"?"left":"bottom"]=f+"%",this.handle.stop(1,1)[e?"animate":"css"](g,c.animate),b==="min"&&this.orientation==="horizontal"&&this.range.stop(1,1)[e?"animate":"css"]({width:f+"%"},c.animate),b==="max"&&this.orientation==="horizontal"&&this.range[e?"animate":"css"]({width:100-f+"%"},{queue:!1,duration:c.animate}),b==="min"&&this.orientation==="vertical"&&this.range.stop(1,1)[e?"animate":"css"]({height:f+"%"},c.animate),b==="max"&&this.orientation==="vertical"&&this.range[e?"animate":"css"]({height:100-f+"%"},{queue:!1,duration:c.animate}))}}),a.extend(a.ui.slider,{version:"1.8.18"})})(jQuery);/*
+ * jQuery UI Tabs 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */(function(a,b){function f(){return++d}function e(){return++c}var c=0,d=0;a.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:!1,cookie:null,collapsible:!1,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"<div></div>",remove:null,select:null,show:null,spinner:"<em>Loading&#8230;</em>",tabTemplate:"<li><a href='#{href}'><span>#{label}</span></a></li>"},_create:function(){this._tabify(!0)},_setOption:function(a,b){if(a=="selected"){if(this.options.collapsible&&b==this.options.selected)return;this.select(b)}else this.options[a]=b,this._tabify()},_tabId:function(a){return a.title&&a.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+e()},_sanitizeSelector:function(a){return a.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+f());return a.cookie.apply(null,[b].concat(a.makeArray(arguments)))},_ui:function(a,b){return{tab:a,panel:b,index:this.anchors.index(a)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b=a(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(c){function m(b,c){b.css("display",""),!a.support.opacity&&c.opacity&&b[0].style.removeAttribute("filter")}var d=this,e=this.options,f=/^#.+/;this.list=this.element.find("ol,ul").eq(0),this.lis=a(" > li:has(a[href])",this.list),this.anchors=this.lis.map(function(){return a("a",this)[0]}),this.panels=a([]),this.anchors.each(function(b,c){var g=a(c).attr("href"),h=g.split("#")[0],i;h&&(h===location.toString().split("#")[0]||(i=a("base")[0])&&h===i.href)&&(g=c.hash,c.href=g);if(f.test(g))d.panels=d.panels.add(d.element.find(d._sanitizeSelector(g)));else if(g&&g!=="#"){a.data(c,"href.tabs",g),a.data(c,"load.tabs",g.replace(/#.*$/,""));var j=d._tabId(c);c.href="#"+j;var k=d.element.find("#"+j);k.length||(k=a(e.panelTemplate).attr("id",j).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(d.panels[b-1]||d.list),k.data("destroy.tabs",!0)),d.panels=d.panels.add(k)}else e.disabled.push(b)}),c?(this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all"),this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all"),this.lis.addClass("ui-state-default ui-corner-top"),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom"),e.selected===b?(location.hash&&this.anchors.each(function(a,b){if(b.hash==location.hash){e.selected=a;return!1}}),typeof e.selected!="number"&&e.cookie&&(e.selected=parseInt(d._cookie(),10)),typeof e.selected!="number"&&this.lis.filter(".ui-tabs-selected").length&&(e.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"))),e.selected=e.selected||(this.lis.length?0:-1)):e.selected===null&&(e.selected=-1),e.selected=e.selected>=0&&this.anchors[e.selected]||e.selected<0?e.selected:0,e.disabled=a.unique(e.disabled.concat(a.map(this.lis.filter(".ui-state-disabled"),function(a,b){return d.lis.index(a)}))).sort(),a.inArray(e.selected,e.disabled)!=-1&&e.disabled.splice(a.inArray(e.selected,e.disabled),1),this.panels.addClass("ui-tabs-hide"),this.lis.removeClass("ui-tabs-selected ui-state-active"),e.selected>=0&&this.anchors.length&&(d.element.find(d._sanitizeSelector(d.anchors[e.selected].hash)).removeClass("ui-tabs-hide"),this.lis.eq(e.selected).addClass("ui-tabs-selected ui-state-active"),d.element.queue("tabs",function(){d._trigger("show",null,d._ui(d.anchors[e.selected],d.element.find(d._sanitizeSelector(d.anchors[e.selected].hash))[0]))}),this.load(e.selected)),a(window).bind("unload",function(){d.lis.add(d.anchors).unbind(".tabs"),d.lis=d.anchors=d.panels=null})):e.selected=this.lis.index(this.lis.filter(".ui-tabs-selected")),this.element[e.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible"),e.cookie&&this._cookie(e.selected,e.cookie);for(var g=0,h;h=this.lis[g];g++)a(h)[a.inArray(g,e.disabled)!=-1&&!a(h).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");e.cache===!1&&this.anchors.removeData("cache.tabs"),this.lis.add(this.anchors).unbind(".tabs");if(e.event!=="mouseover"){var i=function(a,b){b.is(":not(.ui-state-disabled)")&&b.addClass("ui-state-"+a)},j=function(a,b){b.removeClass("ui-state-"+a)};this.lis.bind("mouseover.tabs",function(){i("hover",a(this))}),this.lis.bind("mouseout.tabs",function(){j("hover",a(this))}),this.anchors.bind("focus.tabs",function(){i("focus",a(this).closest("li"))}),this.anchors.bind("blur.tabs",function(){j("focus",a(this).closest("li"))})}var k,l;e.fx&&(a.isArray(e.fx)?(k=e.fx[0],l=e.fx[1]):k=l=e.fx);var n=l?function(b,c){a(b).closest("li").addClass("ui-tabs-selected ui-state-active"),c.hide().removeClass("ui-tabs-hide").animate(l,l.duration||"normal",function(){m(c,l),d._trigger("show",null,d._ui(b,c[0]))})}:function(b,c){a(b).closest("li").addClass("ui-tabs-selected ui-state-active"),c.removeClass("ui-tabs-hide"),d._trigger("show",null,d._ui(b,c[0]))},o=k?function(a,b){b.animate(k,k.duration||"normal",function(){d.lis.removeClass("ui-tabs-selected ui-state-active"),b.addClass("ui-tabs-hide"),m(b,k),d.element.dequeue("tabs")})}:function(a,b,c){d.lis.removeClass("ui-tabs-selected ui-state-active"),b.addClass("ui-tabs-hide"),d.element.dequeue("tabs")};this.anchors.bind(e.event+".tabs",function(){var b=this,c=a(b).closest("li"),f=d.panels.filter(":not(.ui-tabs-hide)"),g=d.element.find(d._sanitizeSelector(b.hash));if(c.hasClass("ui-tabs-selected")&&!e.collapsible||c.hasClass("ui-state-disabled")||c.hasClass("ui-state-processing")||d.panels.filter(":animated").length||d._trigger("select",null,d._ui(this,g[0]))===!1){this.blur();return!1}e.selected=d.anchors.index(this),d.abort();if(e.collapsible){if(c.hasClass("ui-tabs-selected")){e.selected=-1,e.cookie&&d._cookie(e.selected,e.cookie),d.element.queue("tabs",function(){o(b,f)}).dequeue("tabs"),this.blur();return!1}if(!f.length){e.cookie&&d._cookie(e.selected,e.cookie),d.element.queue("tabs",function(){n(b,g)}),d.load(d.anchors.index(this)),this.blur();return!1}}e.cookie&&d._cookie(e.selected,e.cookie);if(g.length)f.length&&d.element.queue("tabs",function(){o(b,f)}),d.element.queue("tabs",function(){n(b,g)}),d.load(d.anchors.index(this));else throw"jQuery UI Tabs: Mismatching fragment identifier.";a.browser.msie&&this.blur()}),this.anchors.bind("click.tabs",function(){return!1})},_getIndex:function(a){typeof a=="string"&&(a=this.anchors.index(this.anchors.filter("[href$="+a+"]")));return a},destroy:function(){var b=this.options;this.abort(),this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs"),this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all"),this.anchors.each(function(){var b=a.data(this,"href.tabs");b&&(this.href=b);var c=a(this).unbind(".tabs");a.each(["href","load","cache"],function(a,b){c.removeData(b+".tabs")})}),this.lis.unbind(".tabs").add(this.panels).each(function(){a.data(this,"destroy.tabs")?a(this).remove():a(this).removeClass(["ui-state-default","ui-corner-top","ui-tabs-selected","ui-state-active","ui-state-hover","ui-state-focus","ui-state-disabled","ui-tabs-panel","ui-widget-content","ui-corner-bottom","ui-tabs-hide"].join(" "))}),b.cookie&&this._cookie(null,b.cookie);return this},add:function(c,d,e){e===b&&(e=this.anchors.length);var f=this,g=this.options,h=a(g.tabTemplate.replace(/#\{href\}/g,c).replace(/#\{label\}/g,d)),i=c.indexOf("#")?this._tabId(a("a",h)[0]):c.replace("#","");h.addClass("ui-state-default ui-corner-top").data("destroy.tabs",!0);var j=f.element.find("#"+i);j.length||(j=a(g.panelTemplate).attr("id",i).data("destroy.tabs",!0)),j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide"),e>=this.lis.length?(h.appendTo(this.list),j.appendTo(this.list[0].parentNode)):(h.insertBefore(this.lis[e]),j.insertBefore(this.panels[e])),g.disabled=a.map(g.disabled,function(a,b){return a>=e?++a:a}),this._tabify(),this.anchors.length==1&&(g.selected=0,h.addClass("ui-tabs-selected ui-state-active"),j.removeClass("ui-tabs-hide"),this.element.queue("tabs",function(){f._trigger("show",null,f._ui(f.anchors[0],f.panels[0]))}),this.load(0)),this._trigger("add",null,this._ui(this.anchors[e],this.panels[e]));return this},remove:function(b){b=this._getIndex(b);var c=this.options,d=this.lis.eq(b).remove(),e=this.panels.eq(b).remove();d.hasClass("ui-tabs-selected")&&this.anchors.length>1&&this.select(b+(b+1<this.anchors.length?1:-1)),c.disabled=a.map(a.grep(c.disabled,function(a,c){return a!=b}),function(a,c){return a>=b?--a:a}),this._tabify(),this._trigger("remove",null,this._ui(d.find("a")[0],e[0]));return this},enable:function(b){b=this._getIndex(b);var c=this.options;if(a.inArray(b,c.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled"),c.disabled=a.grep(c.disabled,function(a,c){return a!=b}),this._trigger("enable",null,this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(a){a=this._getIndex(a);var b=this,c=this.options;a!=c.selected&&(this.lis.eq(a).addClass("ui-state-disabled"),c.disabled.push(a),c.disabled.sort(),this._trigger("disable",null,this._ui(this.anchors[a],this.panels[a])));return this},select:function(a){a=this._getIndex(a);if(a==-1)if(this.options.collapsible&&this.options.selected!=-1)a=this.options.selected;else return this;this.anchors.eq(a).trigger(this.options.event+".tabs");return this},load:function(b){b=this._getIndex(b);var c=this,d=this.options,e=this.anchors.eq(b)[0],f=a.data(e,"load.tabs");this.abort();if(!f||this.element.queue("tabs").length!==0&&a.data(e,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(d.spinner){var g=a("span",e);g.data("label.tabs",g.html()).html(d.spinner)}this.xhr=a.ajax(a.extend({},d.ajaxOptions,{url:f,success:function(f,g){c.element.find(c._sanitizeSelector(e.hash)).html(f),c._cleanup(),d.cache&&a.data(e,"cache.tabs",!0),c._trigger("load",null,c._ui(c.anchors[b],c.panels[b]));try{d.ajaxOptions.success(f,g)}catch(h){}},error:function(a,f,g){c._cleanup(),c._trigger("load",null,c._ui(c.anchors[b],c.panels[b]));try{d.ajaxOptions.error(a,f,b,e)}catch(g){}}})),c.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]),this.panels.stop(!1,!0),this.element.queue("tabs",this.element.queue("tabs").splice(-2,2)),this.xhr&&(this.xhr.abort(),delete this.xhr),this._cleanup();return this},url:function(a,b){this.anchors.eq(a).removeData("cache.tabs").data("load.tabs",b);return this},length:function(){return this.anchors.length}}),a.extend(a.ui.tabs,{version:"1.8.18"}),a.extend(a.ui.tabs.prototype,{rotation:null,rotate:function(a,b){var c=this,d=this.options,e=c._rotate||(c._rotate=function(b){clearTimeout(c.rotation),c.rotation=setTimeout(function(){var a=d.selected;c.select(++a<c.anchors.length?a:0)},a),b&&b.stopPropagation()}),f=c._unrotate||(c._unrotate=b?function(a){t=d.selected,e()}:function(a){a.clientX&&c.rotate(null)});a?(this.element.bind("tabsshow",e),this.anchors.bind(d.event+".tabs",f),e()):(clearTimeout(c.rotation),this.element.unbind("tabsshow",e),this.anchors.unbind(d.event+".tabs",f),delete this._rotate,delete this._unrotate);return this}})})(jQuery);/*
+ * jQuery UI Datepicker 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker
+ *
+ * Depends:
+ * jquery.ui.core.js
+ */(function($,undefined){function isArray(a){return a&&($.browser.safari&&typeof a=="object"&&a.length||a.constructor&&a.constructor.toString().match(/\Array\(\)/))}function extendRemove(a,b){$.extend(a,b);for(var c in b)if(b[c]==null||b[c]==undefined)a[c]=b[c];return a}function bindHover(a){var b="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return a.bind("mouseout",function(a){var c=$(a.target).closest(b);!c.length||c.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(c){var d=$(c.target).closest(b);!$.datepicker._isDisabledDatepicker(instActive.inline?a.parent()[0]:instActive.input[0])&&!!d.length&&(d.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),d.addClass("ui-state-hover"),d.hasClass("ui-datepicker-prev")&&d.addClass("ui-datepicker-prev-hover"),d.hasClass("ui-datepicker-next")&&d.addClass("ui-datepicker-next-hover"))})}function Datepicker(){this.debug=!1,this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},$.extend(this._defaults,this.regional[""]),this.dpDiv=bindHover($('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}$.extend($.ui,{datepicker:{version:"1.8.18"}});var PROP_NAME="datepicker",dpuuid=(new Date).getTime(),instActive;$.extend(Datepicker.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){extendRemove(this._defaults,a||{});return this},_attachDatepicker:function(target,settings){var inlineSettings=null;for(var attrName in this._defaults){var attrValue=target.getAttribute("date:"+attrName);if(attrValue){inlineSettings=inlineSettings||{};try{inlineSettings[attrName]=eval(attrValue)}catch(err){inlineSettings[attrName]=attrValue}}}var nodeName=target.nodeName.toLowerCase(),inline=nodeName=="div"||nodeName=="span";target.id||(this.uuid+=1,target.id="dp"+this.uuid);var inst=this._newInst($(target),inline);inst.settings=$.extend({},settings||{},inlineSettings||{}),nodeName=="input"?this._connectDatepicker(target,inst):inline&&this._inlineDatepicker(target,inst)},_newInst:function(a,b){var c=a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1");return{id:c,input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:b?bindHover($('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')):this.dpDiv}},_connectDatepicker:function(a,b){var c=$(a);b.append=$([]),b.trigger=$([]);c.hasClass(this.markerClassName)||(this._attachments(c,b),c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),this._autoSize(b),$.data(a,PROP_NAME,b),b.settings.disabled&&this._disableDatepicker(a))},_attachments:function(a,b){var c=this._get(b,"appendText"),d=this._get(b,"isRTL");b.append&&b.append.remove(),c&&(b.append=$('<span class="'+this._appendClass+'">'+c+"</span>"),a[d?"before":"after"](b.append)),a.unbind("focus",this._showDatepicker),b.trigger&&b.trigger.remove();var e=this._get(b,"showOn");(e=="focus"||e=="both")&&a.focus(this._showDatepicker);if(e=="button"||e=="both"){var f=this._get(b,"buttonText"),g=this._get(b,"buttonImage");b.trigger=$(this._get(b,"buttonImageOnly")?$("<img/>").addClass(this._triggerClass).attr({src:g,alt:f,title:f}):$('<button type="button"></button>').addClass(this._triggerClass).html(g==""?f:$("<img/>").attr({src:g,alt:f,title:f}))),a[d?"before":"after"](b.trigger),b.trigger.click(function(){$.datepicker._datepickerShowing&&$.datepicker._lastInput==a[0]?$.datepicker._hideDatepicker():$.datepicker._datepickerShowing&&$.datepicker._lastInput!=a[0]?($.datepicker._hideDatepicker(),$.datepicker._showDatepicker(a[0])):$.datepicker._showDatepicker(a[0]);return!1})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var d=function(a){var b=0,c=0;for(var d=0;d<a.length;d++)a[d].length>b&&(b=a[d].length,c=d);return c};b.setMonth(d(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort"))),b.setDate(d(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=$(a);c.hasClass(this.markerClassName)||(c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(a,c,d){b.settings[c]=d}).bind("getData.datepicker",function(a,c){return this._get(b,c)}),$.data(a,PROP_NAME,b),this._setDate(b,this._getDefaultDate(b),!0),this._updateDatepicker(b),this._updateAlternate(b),b.settings.disabled&&this._disableDatepicker(a),b.dpDiv.css("display","block"))},_dialogDatepicker:function(a,b,c,d,e){var f=this._dialogInst;if(!f){this.uuid+=1;var g="dp"+this.uuid;this._dialogInput=$('<input type="text" id="'+g+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>'),this._dialogInput.keydown(this._doKeyDown),$("body").append(this._dialogInput),f=this._dialogInst=this._newInst(this._dialogInput,!1),f.settings={},$.data(this._dialogInput[0],PROP_NAME,f)}extendRemove(f.settings,d||{}),b=b&&b.constructor==Date?this._formatDate(f,b):b,this._dialogInput.val(b),this._pos=e?e.length?e:[e.pageX,e.pageY]:null;if(!this._pos){var h=document.documentElement.clientWidth,i=document.documentElement.clientHeight,j=document.documentElement.scrollLeft||document.body.scrollLeft,k=document.documentElement.scrollTop||document.body.scrollTop;this._pos=[h/2-100+j,i/2-150+k]}this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),f.settings.onSelect=c,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),$.blockUI&&$.blockUI(this.dpDiv),$.data(this._dialogInput[0],PROP_NAME,f);return this},_destroyDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!!b.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();$.removeData(a,PROP_NAME),d=="input"?(c.append.remove(),c.trigger.remove(),b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):(d=="div"||d=="span")&&b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!!b.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!1,c.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().removeClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b})}},_disableDatepicker:function(a){var b=$(a),c=$.data(a,PROP_NAME);if(!!b.hasClass(this.markerClassName)){var d=a.nodeName.toLowerCase();if(d=="input")a.disabled=!0,c.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"});else if(d=="div"||d=="span"){var e=b.children("."+this._inlineClass);e.children().addClass("ui-state-disabled"),e.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=$.map(this._disabledInputs,function(b){return b==a?null:b}),this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return!1;for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]==a)return!0;return!1},_getInst:function(a){try{return $.data(a,PROP_NAME)}catch(b){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(a,b,c){var d=this._getInst(a);if(arguments.length==2&&typeof b=="string")return b=="defaults"?$.extend({},$.datepicker._defaults):d?b=="all"?$.extend({},d.settings):this._get(d,b):null;var e=b||{};typeof b=="string"&&(e={},e[b]=c);if(d){this._curInst==d&&this._hideDatepicker();var f=this._getDateDatepicker(a,!0),g=this._getMinMaxDate(d,"min"),h=this._getMinMaxDate(d,"max");extendRemove(d.settings,e),g!==null&&e.dateFormat!==undefined&&e.minDate===undefined&&(d.settings.minDate=this._formatDate(d,g)),h!==null&&e.dateFormat!==undefined&&e.maxDate===undefined&&(d.settings.maxDate=this._formatDate(d,h)),this._attachments($(a),d),this._autoSize(d),this._setDate(d,f),this._updateAlternate(d),this._updateDatepicker(d)}},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){var b=this._getInst(a);b&&this._updateDatepicker(b)},_setDateDatepicker:function(a,b){var c=this._getInst(a);c&&(this._setDate(c,b),this._updateDatepicker(c),this._updateAlternate(c))},_getDateDatepicker:function(a,b){var c=this._getInst(a);c&&!c.inline&&this._setDateFromField(c,b);return c?this._getDate(c):null},_doKeyDown:function(a){var b=$.datepicker._getInst(a.target),c=!0,d=b.dpDiv.is(".ui-datepicker-rtl");b._keyEvent=!0;if($.datepicker._datepickerShowing)switch(a.keyCode){case 9:$.datepicker._hideDatepicker(),c=!1;break;case 13:var e=$("td."+$.datepicker._dayOverClass+":not(."+$.datepicker._currentClass+")",b.dpDiv);e[0]&&$.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,e[0]);var f=$.datepicker._get(b,"onSelect");if(f){var g=$.datepicker._formatDate(b);f.apply(b.input?b.input[0]:null,[g,b])}else $.datepicker._hideDatepicker();return!1;case 27:$.datepicker._hideDatepicker();break;case 33:$.datepicker._adjustDate(a.target,a.ctrlKey?-$.datepicker._get(b,"stepBigMonths"):-$.datepicker._get(b,"stepMonths"),"M");break;case 34:$.datepicker._adjustDate(a.target,a.ctrlKey?+$.datepicker._get(b,"stepBigMonths"):+$.datepicker._get(b,"stepMonths"),"M");break;case 35:(a.ctrlKey||a.metaKey)&&$.datepicker._clearDate(a.target),c=a.ctrlKey||a.metaKey;break;case 36:(a.ctrlKey||a.metaKey)&&$.datepicker._gotoToday(a.target),c=a.ctrlKey||a.metaKey;break;case 37:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,d?1:-1,"D"),c=a.ctrlKey||a.metaKey,a.originalEvent.altKey&&$.datepicker._adjustDate(a.target,a.ctrlKey?-$.datepicker._get(b,"stepBigMonths"):-$.datepicker._get(b,"stepMonths"),"M");break;case 38:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,-7,"D"),c=a.ctrlKey||a.metaKey;break;case 39:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,d?-1:1,"D"),c=a.ctrlKey||a.metaKey,a.originalEvent.altKey&&$.datepicker._adjustDate(a.target,a.ctrlKey?+$.datepicker._get(b,"stepBigMonths"):+$.datepicker._get(b,"stepMonths"),"M");break;case 40:(a.ctrlKey||a.metaKey)&&$.datepicker._adjustDate(a.target,7,"D"),c=a.ctrlKey||a.metaKey;break;default:c=!1}else a.keyCode==36&&a.ctrlKey?$.datepicker._showDatepicker(this):c=!1;c&&(a.preventDefault(),a.stopPropagation())},_doKeyPress:function(a){var b=$.datepicker._getInst(a.target);if($.datepicker._get(b,"constrainInput")){var c=$.datepicker._possibleChars($.datepicker._get(b,"dateFormat")),d=String.fromCharCode(a.charCode==undefined?a.keyCode:a.charCode);return a.ctrlKey||a.metaKey||d<" "||!c||c.indexOf(d)>-1}},_doKeyUp:function(a){var b=$.datepicker._getInst(a.target);if(b.input.val()!=b.lastVal)try{var c=$.datepicker.parseDate($.datepicker._get(b,"dateFormat"),b.input?b.input.val():null,$.datepicker._getFormatConfig(b));c&&($.datepicker._setDateFromField(b),$.datepicker._updateAlternate(b),$.datepicker._updateDatepicker(b))}catch(a){$.datepicker.log(a)}return!0},_showDatepicker:function(a){a=a.target||a,a.nodeName.toLowerCase()!="input"&&(a=$("input",a.parentNode)[0]);if(!$.datepicker._isDisabledDatepicker(a)&&$.datepicker._lastInput!=a){var b=$.datepicker._getInst(a);$.datepicker._curInst&&$.datepicker._curInst!=b&&($.datepicker._curInst.dpDiv.stop(!0,!0),b&&$.datepicker._datepickerShowing&&$.datepicker._hideDatepicker($.datepicker._curInst.input[0]));var c=$.datepicker._get(b,"beforeShow"),d=c?c.apply(a,[a,b]):{};if(d===!1)return;extendRemove(b.settings,d),b.lastVal=null,$.datepicker._lastInput=a,$.datepicker._setDateFromField(b),$.datepicker._inDialog&&(a.value=""),$.datepicker._pos||($.datepicker._pos=$.datepicker._findPos(a),$.datepicker._pos[1]+=a.offsetHeight);var e=!1;$(a).parents().each(function(){e|=$(this).css("position")=="fixed";return!e}),e&&$.browser.opera&&($.datepicker._pos[0]-=document.documentElement.scrollLeft,$.datepicker._pos[1]-=document.documentElement.scrollTop);var f={left:$.datepicker._pos[0],top:$.datepicker._pos[1]};$.datepicker._pos=null,b.dpDiv.empty(),b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),$.datepicker._updateDatepicker(b),f=$.datepicker._checkOffset(b,f,e),b.dpDiv.css({position:$.datepicker._inDialog&&$.blockUI?"static":e?"fixed":"absolute",display:"none",left:f.left+"px",top:f.top+"px"});if(!b.inline){var g=$.datepicker._get(b,"showAnim"),h=$.datepicker._get(b,"duration"),i=function(){var a=b.dpDiv.find("iframe.ui-datepicker-cover");if(!!a.length){var c=$.datepicker._getBorders(b.dpDiv);a.css({left:-c[0],top:-c[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex($(a).zIndex()+1),$.datepicker._datepickerShowing=!0,$.effects&&$.effects[g]?b.dpDiv.show(g,$.datepicker._get(b,"showOptions"),h,i):b.dpDiv[g||"show"](g?h:null,i),(!g||!h)&&i(),b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus(),$.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=this;b.maxRows=4;var c=$.datepicker._getBorders(a.dpDiv);instActive=a,a.dpDiv.empty().append(this._generateHTML(a));var d=a.dpDiv.find("iframe.ui-datepicker-cover");!d.length||d.css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()}),a.dpDiv.find("."+this._dayOverClass+" a").mouseover();var e=this._getNumberOfMonths(a),f=e[1],g=17;a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),f>1&&a.dpDiv.addClass("ui-datepicker-multi-"+f).css("width",g*f+"em"),a.dpDiv[(e[0]!=1||e[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi"),a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),a==$.datepicker._curInst&&$.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var h=a.yearshtml;setTimeout(function(){h===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml),h=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(a){return{thin:1,medium:2,thick:3}[a]||a};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var d=a.dpDiv.outerWidth(),e=a.dpDiv.outerHeight(),f=a.input?a.input.outerWidth():0,g=a.input?a.input.outerHeight():0,h=document.documentElement.clientWidth+$(document).scrollLeft(),i=document.documentElement.clientHeight+$(document).scrollTop();b.left-=this._get(a,"isRTL")?d-f:0,b.left-=c&&b.left==a.input.offset().left?$(document).scrollLeft():0,b.top-=c&&b.top==a.input.offset().top+g?$(document).scrollTop():0,b.left-=Math.min(b.left,b.left+d>h&&h>d?Math.abs(b.left+d-h):0),b.top-=Math.min(b.top,b.top+e>i&&i>e?Math.abs(e+g):0);return b},_findPos:function(a){var b=this._getInst(a),c=this._get(b,"isRTL");while(a&&(a.type=="hidden"||a.nodeType!=1||$.expr.filters.hidden(a)))a=a[c?"previousSibling":"nextSibling"];var d=$(a).offset();return[d.left,d.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=$.data(a,PROP_NAME))&&this._datepickerShowing){var c=this._get(b,"showAnim"),d=this._get(b,"duration"),e=this,f=function(){$.datepicker._tidyDialog(b),e._curInst=null};$.effects&&$.effects[c]?b.dpDiv.hide(c,$.datepicker._get(b,"showOptions"),d,f):b.dpDiv[c=="slideDown"?"slideUp":c=="fadeIn"?"fadeOut":"hide"](c?d:null,f),c||f(),this._datepickerShowing=!1;var g=this._get(b,"onClose");g&&g.apply(b.input?b.input[0]:null,[b.input?b.input.val():"",b]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),$.blockUI&&($.unblockUI(),$("body").append(this.dpDiv))),this._inDialog=!1}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(!!$.datepicker._curInst){var b=$(a.target),c=$.datepicker._getInst(b[0]);(b[0].id!=$.datepicker._mainDivId&&b.parents("#"+$.datepicker._mainDivId).length==0&&!b.hasClass($.datepicker.markerClassName)&&!b.closest("."+$.datepicker._triggerClass).length&&$.datepicker._datepickerShowing&&(!$.datepicker._inDialog||!$.blockUI)||b.hasClass($.datepicker.markerClassName)&&$.datepicker._curInst!=c)&&$.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){var d=$(a),e=this._getInst(d[0]);this._isDisabledDatepicker(d[0])||(this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c),this._updateDatepicker(e))},_gotoToday:function(a){var b=$(a),c=this._getInst(b[0]);if(this._get(c,"gotoCurrent")&&c.currentDay)c.selectedDay=c.currentDay,c.drawMonth=c.selectedMonth=c.currentMonth,c.drawYear=c.selectedYear=c.currentYear;else{var d=new Date;c.selectedDay=d.getDate(),c.drawMonth=c.selectedMonth=d.getMonth(),c.drawYear=c.selectedYear=d.getFullYear()}this._notifyChange(c),this._adjustDate(b)},_selectMonthYear:function(a,b,c){var d=$(a),e=this._getInst(d[0]);e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10),this._notifyChange(e),this._adjustDate(d)},_selectDay:function(a,b,c,d){var e=$(a);if(!$(d).hasClass(this._unselectableClass)&&!this._isDisabledDatepicker(e[0])){var f=this._getInst(e[0]);f.selectedDay=f.currentDay=$("a",d).html(),f.selectedMonth=f.currentMonth=b,f.selectedYear=f.currentYear=c,this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){var b=$(a),c=this._getInst(b[0]);this._selectDate(b,"")},_selectDate:function(a,b){var c=$(a),d=this._getInst(c[0]);b=b!=null?b:this._formatDate(d),d.input&&d.input.val(b),this._updateAlternate(d);var e=this._get(d,"onSelect");e?e.apply(d.input?d.input[0]:null,[b,d]):d.input&&d.input.trigger("change"),d.inline?this._updateDatepicker(d):(this._hideDatepicker(),this._lastInput=d.input[0],typeof d.input[0]!="object"&&d.input.focus(),this._lastInput=null)},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),d=this._getDate(a),e=this.formatDate(c,d,this._getFormatConfig(a));$(b).each(function(){$(this).val(e)})}},noWeekends:function(a){var b=a.getDay();return[b>0&&b<6,""]},iso8601Week:function(a){var b=new Date(a.getTime());b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();b.setMonth(0),b.setDate(1);return Math.floor(Math.round((c-b)/864e5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var d=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;d=typeof d!="string"?d:(new Date).getFullYear()%100+parseInt(d,10);var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,g=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,h=(c?c.monthNames:null)||this._defaults.monthNames,i=-1,j=-1,k=-1,l=-1,m=!1,n=function(b){var c=s+1<a.length&&a.charAt(s+1)==b;c&&s++;return c},o=function(a){var c=n(a),d=a=="@"?14:a=="!"?20:a=="y"&&c?4:a=="o"?3:2,e=new RegExp("^\\d{1,"+d+"}"),f=b.substring(r).match(e);if(!f)throw"Missing number at position "+r;r+=f[0].length;return parseInt(f[0],10)},p=function(a,c,d){var e=$.map(n(a)?d:c,function(a,b){return[[b,a]]}).sort(function(a,b){return-(a[1].length-b[1].length)}),f=-1;$.each(e,function(a,c){var d=c[1];if(b.substr(r,d.length).toLowerCase()==d.toLowerCase()){f=c[0],r+=d.length;return!1}});if(f!=-1)return f+1;throw"Unknown name at position "+r},q=function(){if(b.charAt(r)!=a.charAt(s))throw"Unexpected literal at position "+r;r++},r=0;for(var s=0;s<a.length;s++)if(m)a.charAt(s)=="'"&&!n("'")?m=!1:q();else switch(a.charAt(s)){case"d":k=o("d");break;case"D":p("D",e,f);break;case"o":l=o("o");break;case"m":j=o("m");break;case"M":j=p("M",g,h);break;case"y":i=o("y");break;case"@":var t=new Date(o("@"));i=t.getFullYear(),j=t.getMonth()+1,k=t.getDate();break;case"!":var t=new Date((o("!")-this._ticksTo1970)/1e4);i=t.getFullYear(),j=t.getMonth()+1,k=t.getDate();break;case"'":n("'")?q():m=!0;break;default:q()}if(r<b.length)throw"Extra/unparsed characters found in date: "+b.substring(r);i==-1?i=(new Date).getFullYear():i<100&&(i+=(new Date).getFullYear()-(new Date).getFullYear()%100+(i<=d?0:-100));if(l>-1){j=1,k=l;for(;;){var u=this._getDaysInMonth(i,j-1);if(k<=u)break;j++,k-=u}}var t=this._daylightSavingAdjust(new Date(i,j-1,k));if(t.getFullYear()!=i||t.getMonth()+1!=j||t.getDate()!=k)throw"Invalid date";return t},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1e7,formatDate:function(a,b,c){if(!b)return"";var d=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,e=(c?c.dayNames:null)||this._defaults.dayNames,f=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,h=function(b){var c=m+1<a.length&&a.charAt(m+1)==b;c&&m++;return c},i=function(a,b,c){var d=""+b;if(h(a))while(d.length<c)d="0"+d;return d},j=function(a,b,c,d){return h(a)?d[b]:c[b]},k="",l=!1;if(b)for(var m=0;m<a.length;m++)if(l)a.charAt(m)=="'"&&!h("'")?l=!1:k+=a.charAt(m);else switch(a.charAt(m)){case"d":k+=i("d",b.getDate(),2);break;case"D":k+=j("D",b.getDay(),d,e);break;case"o":k+=i("o",Math.round(((new Date(b.getFullYear(),b.getMonth(),b.getDate())).getTime()-(new Date(b.getFullYear(),0,0)).getTime())/864e5),3);break;case"m":k+=i("m",b.getMonth()+1,2);break;case"M":k+=j("M",b.getMonth(),f,g);break;case"y":k+=h("y")?b.getFullYear():(b.getYear()%100<10?"0":"")+b.getYear()%100;break;case"@":k+=b.getTime();break;case"!":k+=b.getTime()*1e4+this._ticksTo1970;break;case"'":h("'")?k+="'":l=!0;break;default:k+=a.charAt(m)}return k},_possibleChars:function(a){var b="",c=!1,d=function(b){var c=e+1<a.length&&a.charAt(e+1)==b;c&&e++;return c};for(var e=0;e<a.length;e++)if(c)a.charAt(e)=="'"&&!d("'")?c=!1:b+=a.charAt(e);else switch(a.charAt(e)){case"d":case"m":case"y":case"@":b+="0123456789";break;case"D":case"M":return null;case"'":d("'")?b+="'":c=!0;break;default:b+=a.charAt(e)}return b},_get:function(a,b){return a.settings[b]!==undefined?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!=a.lastVal){var c=this._get(a,"dateFormat"),d=a.lastVal=a.input?a.input.val():null,e,f;e=f=this._getDefaultDate(a);var g=this._getFormatConfig(a);try{e=this.parseDate(c,d,g)||f}catch(h){this.log(h),d=b?"":d}a.selectedDay=e.getDate(),a.drawMonth=a.selectedMonth=e.getMonth(),a.drawYear=a.selectedYear=e.getFullYear(),a.currentDay=d?e.getDate():0,a.currentMonth=d?e.getMonth():0,a.currentYear=d?e.getFullYear():0,this._adjustInstDate(a)}},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},_determineDate:function(a,b,c){var d=function(a){var b=new Date;b.setDate(b.getDate()+a);return b},e=function(b){try{return $.datepicker.parseDate($.datepicker._get(a,"dateFormat"),b,$.datepicker._getFormatConfig(a))}catch(c){}var d=(b.toLowerCase().match(/^c/)?$.datepicker._getDate(a):null)||new Date,e=d.getFullYear(),f=d.getMonth(),g=d.getDate(),h=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,i=h.exec(b);while(i){switch(i[2]||"d"){case"d":case"D":g+=parseInt(i[1],10);break;case"w":case"W":g+=parseInt(i[1],10)*7;break;case"m":case"M":f+=parseInt(i[1],10),g=Math.min(g,$.datepicker._getDaysInMonth(e,f));break;case"y":case"Y":e+=parseInt(i[1],10),g=Math.min(g,$.datepicker._getDaysInMonth(e,f))}i=h.exec(b)}return new Date(e,f,g)},f=b==null||b===""?c:typeof b=="string"?e(b):typeof b=="number"?isNaN(b)?c:d(b):new Date(b.getTime());f=f&&f.toString()=="Invalid Date"?c:f,f&&(f.setHours(0),f.setMinutes(0),f.setSeconds(0),f.setMilliseconds(0));return this._daylightSavingAdjust(f)},_daylightSavingAdjust:function(a){if(!a)return null;a.setHours(a.getHours()>12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var d=!b,e=a.selectedMonth,f=a.selectedYear,g=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=a.currentDay=g.getDate(),a.drawMonth=a.selectedMonth=a.currentMonth=g.getMonth(),a.drawYear=a.selectedYear=a.currentYear=g.getFullYear(),(e!=a.selectedMonth||f!=a.selectedYear)&&!c&&this._notifyChange(a),this._adjustInstDate(a),a.input&&a.input.val(d?"":this._formatDate(a))},_getDate:function(a){var b=!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return b},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),d=this._get(a,"showButtonPanel"),e=this._get(a,"hideIfNoPrevNext"),f=this._get(a,"navigationAsDateFormat"),g=this._getNumberOfMonths(a),h=this._get(a,"showCurrentAtPos"),i=this._get(a,"stepMonths"),j=g[0]!=1||g[1]!=1,k=this._daylightSavingAdjust(a.currentDay?new Date(a.currentYear,a.currentMonth,a.currentDay):new Date(9999,9,9)),l=this._getMinMaxDate(a,"min"),m=this._getMinMaxDate(a,"max"),n=a.drawMonth-h,o=a.drawYear;n<0&&(n+=12,o--);if(m){var p=this._daylightSavingAdjust(new Date(m.getFullYear(),m.getMonth()-g[0]*g[1]+1,m.getDate()));p=l&&p<l?l:p;while(this._daylightSavingAdjust(new Date(o,n,1))>p)n--,n<0&&(n=11,o--)}a.drawMonth=n,a.drawYear=o;var q=this._get(a,"prevText");q=f?this.formatDate(q,this._daylightSavingAdjust(new Date(o,n-i,1)),this._getFormatConfig(a)):q;var r=this._canAdjustMonth(a,-1,o,n)?'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._adjustDate('#"+a.id+"', -"+i+", 'M');\""+' title="'+q+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+q+"</span></a>":e?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+q+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+q+"</span></a>",s=this._get(a,"nextText");s=f?this.formatDate(s,this._daylightSavingAdjust(new Date(o,n+i,1)),this._getFormatConfig(a)):s;var t=this._canAdjustMonth(a,1,o,n)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._adjustDate('#"+a.id+"', +"+i+", 'M');\""+' title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>":e?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>",u=this._get(a,"currentText"),v=this._get(a,"gotoCurrent")&&a.currentDay?k:b;u=f?this.formatDate(u,v,this._getFormatConfig(a)):u;var w=a.inline?"":'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_'+dpuuid+'.datepicker._hideDatepicker();">'+this._get(a,"closeText")+"</button>",x=d?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(c?w:"")+(this._isInRange(a,v)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_'+dpuuid+".datepicker._gotoToday('#"+a.id+"');\""+">"+u+"</button>":"")+(c?"":w)+"</div>":"",y=parseInt(this._get(a,"firstDay"),10);y=isNaN(y)?0:y;var z=this._get(a,"showWeek"),A=this._get(a,"dayNames"),B=this._get(a,"dayNamesShort"),C=this._get(a,"dayNamesMin"),D=this._get(a,"monthNames"),E=this._get(a,"monthNamesShort"),F=this._get(a,"beforeShowDay"),G=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths"),I=this._get(a,"calculateWeek")||this.iso8601Week,J=this._getDefaultDate(a),K="";for(var L=0;L<g[0];L++){var M="";this.maxRows=4;for(var N=0;N<g[1];N++){var O=this._daylightSavingAdjust(new Date(o,n,a.selectedDay)),P=" ui-corner-all",Q="";if(j){Q+='<div class="ui-datepicker-group';if(g[1]>1)switch(N){case 0:Q+=" ui-datepicker-group-first",P=" ui-corner-"+(c?"right":"left");break;case g[1]-1:Q+=" ui-datepicker-group-last",P=" ui-corner-"+(c?"left":"right");break;default:Q+=" ui-datepicker-group-middle",P=""}Q+='">'}Q+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+P+'">'+(/all|left/.test(P)&&L==0?c?t:r:"")+(/all|right/.test(P)&&L==0?c?r:t:"")+this._generateMonthYearHeader(a,n,o,l,m,L>0||N>0,D,E)+'</div><table class="ui-datepicker-calendar"><thead>'+"<tr>";var R=z?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":"";for(var S=0;S<7;S++){var T=(S+y)%7;R+="<th"+((S+y+6)%7>=5?' class="ui-datepicker-week-end"':"")+">"+'<span title="'+A[T]+'">'+C[T]+"</span></th>"}Q+=R+"</tr></thead><tbody>";var U=this._getDaysInMonth(o,n);o==a.selectedYear&&n==a.selectedMonth&&(a.selectedDay=Math.min(a.selectedDay,U));var V=(this._getFirstDayOfMonth(o,n)-y+7)%7,W=Math.ceil((V+U)/7),X=j?this.maxRows>W?this.maxRows:W:W;this.maxRows=X;var Y=this._daylightSavingAdjust(new Date(o,n,1-V));for(var Z=0;Z<X;Z++){Q+="<tr>";var _=z?'<td class="ui-datepicker-week-col">'+this._get(a,"calculateWeek")(Y)+"</td>":"";for(var S=0;S<7;S++){var ba=F?F.apply(a.input?a.input[0]:null,[Y]):[!0,""],bb=Y.getMonth()!=n,bc=bb&&!H||!ba[0]||l&&Y<l||m&&Y>m;_+='<td class="'+((S+y+6)%7>=5?" ui-datepicker-week-end":"")+(bb?" ui-datepicker-other-month":"")+(Y.getTime()==O.getTime()&&n==a.selectedMonth&&a._keyEvent||J.getTime()==Y.getTime()&&J.getTime()==O.getTime()?" "+this._dayOverClass:"")+(bc?" "+this._unselectableClass+" ui-state-disabled":"")+(bb&&!G?"":" "+ba[1]+(Y.getTime()==k.getTime()?" "+this._currentClass:"")+(Y.getTime()==b.getTime()?" ui-datepicker-today":""))+'"'+((!bb||G)&&ba[2]?' title="'+ba[2]+'"':"")+(bc?"":' onclick="DP_jQuery_'+dpuuid+".datepicker._selectDay('#"+a.id+"',"+Y.getMonth()+","+Y.getFullYear()+', this);return false;"')+">"+(bb&&!G?"&#xa0;":bc?'<span class="ui-state-default">'+Y.getDate()+"</span>":'<a class="ui-state-default'+(Y.getTime()==b.getTime()?" ui-state-highlight":"")+(Y.getTime()==k.getTime()?" ui-state-active":"")+(bb?" ui-priority-secondary":"")+'" href="#">'+Y.getDate()+"</a>")+"</td>",Y.setDate(Y.getDate()+1),Y=this._daylightSavingAdjust(Y)}Q+=_+"</tr>"}n++,n>11&&(n=0,o++),Q+="</tbody></table>"+(j?"</div>"+(g[0]>0&&N==g[1]-1?'<div class="ui-datepicker-row-break"></div>':""):""),M+=Q}K+=M}K+=x+($.browser.msie&&parseInt($.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':""),
+a._keyEvent=!1;return K},_generateMonthYearHeader:function(a,b,c,d,e,f,g,h){var i=this._get(a,"changeMonth"),j=this._get(a,"changeYear"),k=this._get(a,"showMonthAfterYear"),l='<div class="ui-datepicker-title">',m="";if(f||!i)m+='<span class="ui-datepicker-month">'+g[b]+"</span>";else{var n=d&&d.getFullYear()==c,o=e&&e.getFullYear()==c;m+='<select class="ui-datepicker-month" onchange="DP_jQuery_'+dpuuid+".datepicker._selectMonthYear('#"+a.id+"', this, 'M');\" "+">";for(var p=0;p<12;p++)(!n||p>=d.getMonth())&&(!o||p<=e.getMonth())&&(m+='<option value="'+p+'"'+(p==b?' selected="selected"':"")+">"+h[p]+"</option>");m+="</select>"}k||(l+=m+(f||!i||!j?"&#xa0;":""));if(!a.yearshtml){a.yearshtml="";if(f||!j)l+='<span class="ui-datepicker-year">'+c+"</span>";else{var q=this._get(a,"yearRange").split(":"),r=(new Date).getFullYear(),s=function(a){var b=a.match(/c[+-].*/)?c+parseInt(a.substring(1),10):a.match(/[+-].*/)?r+parseInt(a,10):parseInt(a,10);return isNaN(b)?r:b},t=s(q[0]),u=Math.max(t,s(q[1]||""));t=d?Math.max(t,d.getFullYear()):t,u=e?Math.min(u,e.getFullYear()):u,a.yearshtml+='<select class="ui-datepicker-year" onchange="DP_jQuery_'+dpuuid+".datepicker._selectMonthYear('#"+a.id+"', this, 'Y');\" "+">";for(;t<=u;t++)a.yearshtml+='<option value="'+t+'"'+(t==c?' selected="selected"':"")+">"+t+"</option>";a.yearshtml+="</select>",l+=a.yearshtml,a.yearshtml=null}}l+=this._get(a,"yearSuffix"),k&&(l+=(f||!i||!j?"&#xa0;":"")+m),l+="</div>";return l},_adjustInstDate:function(a,b,c){var d=a.drawYear+(c=="Y"?b:0),e=a.drawMonth+(c=="M"?b:0),f=Math.min(a.selectedDay,this._getDaysInMonth(d,e))+(c=="D"?b:0),g=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(d,e,f)));a.selectedDay=g.getDate(),a.drawMonth=a.selectedMonth=g.getMonth(),a.drawYear=a.selectedYear=g.getFullYear(),(c=="M"||c=="Y")&&this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max"),e=c&&b<c?c:b;e=d&&e>d?d:e;return e},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");b&&b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){var b=this._get(a,"numberOfMonths");return b==null?[1,1]:typeof b=="number"?[1,b]:b},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,d){var e=this._getNumberOfMonths(a),f=this._daylightSavingAdjust(new Date(c,d+(b<0?b:e[0]*e[1]),1));b<0&&f.setDate(this._getDaysInMonth(f.getFullYear(),f.getMonth()));return this._isInRange(a,f)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min"),d=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!d||b.getTime()<=d.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,d){b||(a.currentDay=a.selectedDay,a.currentMonth=a.selectedMonth,a.currentYear=a.selectedYear);var e=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(d,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),e,this._getFormatConfig(a))}}),$.fn.datepicker=function(a){if(!this.length)return this;$.datepicker.initialized||($(document).mousedown($.datepicker._checkExternalClick).find("body").append($.datepicker.dpDiv),$.datepicker.initialized=!0);var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return $.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return $.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this[0]].concat(b));return this.each(function(){typeof a=="string"?$.datepicker["_"+a+"Datepicker"].apply($.datepicker,[this].concat(b)):$.datepicker._attachDatepicker(this,a)})},$.datepicker=new Datepicker,$.datepicker.initialized=!1,$.datepicker.uuid=(new Date).getTime(),$.datepicker.version="1.8.18",window["DP_jQuery_"+dpuuid]=$})(jQuery);/*
+ * jQuery UI Progressbar 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */(function(a,b){a.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()}),this.valueDiv=a("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element),this.oldValue=this._value(),this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove(),a.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===b)return this._value();this._setOption("value",a);return this},_setOption:function(b,c){b==="value"&&(this.options.value=c,this._refreshValue(),this._value()===this.options.max&&this._trigger("complete")),a.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;typeof a!="number"&&(a=0);return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100*this._value()/this.options.max},_refreshValue:function(){var a=this.value(),b=this._percentage();this.oldValue!==a&&(this.oldValue=a,this._trigger("change")),this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(b.toFixed(0)+"%"),this.element.attr("aria-valuenow",a)}}),a.extend(a.ui.progressbar,{version:"1.8.18"})})(jQuery);/*
+ * jQuery UI Effects 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/
+ */jQuery.effects||function(a,b){function l(b){if(!b||typeof b=="number"||a.fx.speeds[b])return!0;if(typeof b=="string"&&!a.effects[b])return!0;return!1}function k(b,c,d,e){typeof b=="object"&&(e=c,d=null,c=b,b=c.effect),a.isFunction(c)&&(e=c,d=null,c={});if(typeof c=="number"||a.fx.speeds[c])e=d,d=c,c={};a.isFunction(d)&&(e=d,d=null),c=c||{},d=d||c.duration,d=a.fx.off?0:typeof d=="number"?d:d in a.fx.speeds?a.fx.speeds[d]:a.fx.speeds._default,e=e||c.complete;return[b,c,d,e]}function j(a,b){var c={_:0},d;for(d in b)a[d]!=b[d]&&(c[d]=b[d]);return c}function i(b){var c,d;for(c in b)d=b[c],(d==null||a.isFunction(d)||c in g||/scrollbar/.test(c)||!/color/i.test(c)&&isNaN(parseFloat(d)))&&delete b[c];return b}function h(){var a=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,b={},c,d;if(a&&a.length&&a[0]&&a[a[0]]){var e=a.length;while(e--)c=a[e],typeof a[c]=="string"&&(d=c.replace(/\-(\w)/g,function(a,b){return b.toUpperCase()}),b[d]=a[c])}else for(c in a)typeof a[c]=="string"&&(b[c]=a[c]);return b}function d(b,d){var e;do{e=a.curCSS(b,d);if(e!=""&&e!="transparent"||a.nodeName(b,"body"))break;d="backgroundColor"}while(b=b.parentNode);return c(e)}function c(b){var c;if(b&&b.constructor==Array&&b.length==3)return b;if(c=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(b))return[parseInt(c[1],10),parseInt(c[2],10),parseInt(c[3],10)];if(c=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(b))return[parseFloat(c[1])*2.55,parseFloat(c[2])*2.55,parseFloat(c[3])*2.55];if(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(b))return[parseInt(c[1],16),parseInt(c[2],16),parseInt(c[3],16)];if(c=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(b))return[parseInt(c[1]+c[1],16),parseInt(c[2]+c[2],16),parseInt(c[3]+c[3],16)];if(c=/rgba\(0, 0, 0, 0\)/.exec(b))return e.transparent;return e[a.trim(b).toLowerCase()]}a.effects={},a.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor","borderTopColor","borderColor","color","outlineColor"],function(b,e){a.fx.step[e]=function(a){a.colorInit||(a.start=d(a.elem,e),a.end=c(a.end),a.colorInit=!0),a.elem.style[e]="rgb("+Math.max(Math.min(parseInt(a.pos*(a.end[0]-a.start[0])+a.start[0],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[1]-a.start[1])+a.start[1],10),255),0)+","+Math.max(Math.min(parseInt(a.pos*(a.end[2]-a.start[2])+a.start[2],10),255),0)+")"}});var e={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},f=["add","remove","toggle"],g={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};a.effects.animateClass=function(b,c,d,e){a.isFunction(d)&&(e=d,d=null);return this.queue(function(){var g=a(this),k=g.attr("style")||" ",l=i(h.call(this)),m,n=g.attr("class");a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),m=i(h.call(this)),g.attr("class",n),g.animate(j(l,m),{queue:!1,duration:c,easing:d,complete:function(){a.each(f,function(a,c){b[c]&&g[c+"Class"](b[c])}),typeof g.attr("style")=="object"?(g.attr("style").cssText="",g.attr("style").cssText=k):g.attr("style",k),e&&e.apply(this,arguments),a.dequeue(this)}})})},a.fn.extend({_addClass:a.fn.addClass,addClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{add:b},c,d,e]):this._addClass(b)},_removeClass:a.fn.removeClass,removeClass:function(b,c,d,e){return c?a.effects.animateClass.apply(this,[{remove:b},c,d,e]):this._removeClass(b)},_toggleClass:a.fn.toggleClass,toggleClass:function(c,d,e,f,g){return typeof d=="boolean"||d===b?e?a.effects.animateClass.apply(this,[d?{add:c}:{remove:c},e,f,g]):this._toggleClass(c,d):a.effects.animateClass.apply(this,[{toggle:c},d,e,f])},switchClass:function(b,c,d,e,f){return a.effects.animateClass.apply(this,[{add:c,remove:b},d,e,f])}}),a.extend(a.effects,{version:"1.8.18",save:function(a,b){for(var c=0;c<b.length;c++)b[c]!==null&&a.data("ec.storage."+b[c],a[0].style[b[c]])},restore:function(a,b){for(var c=0;c<b.length;c++)b[c]!==null&&a.css(b[c],a.data("ec.storage."+b[c]))},setMode:function(a,b){b=="toggle"&&(b=a.is(":hidden")?"show":"hide");return b},getBaseline:function(a,b){var c,d;switch(a[0]){case"top":c=0;break;case"middle":c=.5;break;case"bottom":c=1;break;default:c=a[0]/b.height}switch(a[1]){case"left":d=0;break;case"center":d=.5;break;case"right":d=1;break;default:d=a[1]/b.width}return{x:d,y:c}},createWrapper:function(b){if(b.parent().is(".ui-effects-wrapper"))return b.parent();var c={width:b.outerWidth(!0),height:b.outerHeight(!0),"float":b.css("float")},d=a("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),e=document.activeElement;b.wrap(d),(b[0]===e||a.contains(b[0],e))&&a(e).focus(),d=b.parent(),b.css("position")=="static"?(d.css({position:"relative"}),b.css({position:"relative"})):(a.extend(c,{position:b.css("position"),zIndex:b.css("z-index")}),a.each(["top","left","bottom","right"],function(a,d){c[d]=b.css(d),isNaN(parseInt(c[d],10))&&(c[d]="auto")}),b.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"}));return d.css(c).show()},removeWrapper:function(b){var c,d=document.activeElement;if(b.parent().is(".ui-effects-wrapper")){c=b.parent().replaceWith(b),(b[0]===d||a.contains(b[0],d))&&a(d).focus();return c}return b},setTransition:function(b,c,d,e){e=e||{},a.each(c,function(a,c){unit=b.cssUnit(c),unit[0]>0&&(e[c]=unit[0]*d+unit[1])});return e}}),a.fn.extend({effect:function(b,c,d,e){var f=k.apply(this,arguments),g={options:f[1],duration:f[2],callback:f[3]},h=g.options.mode,i=a.effects[b];if(a.fx.off||!i)return h?this[h](g.duration,g.callback):this.each(function(){g.callback&&g.callback.call(this)});return i.call(this,g)},_show:a.fn.show,show:function(a){if(l(a))return this._show.apply(this,arguments);var b=k.apply(this,arguments);b[1].mode="show";return this.effect.apply(this,b)},_hide:a.fn.hide,hide:function(a){if(l(a))return this._hide.apply(this,arguments);var b=k.apply(this,arguments);b[1].mode="hide";return this.effect.apply(this,b)},__toggle:a.fn.toggle,toggle:function(b){if(l(b)||typeof b=="boolean"||a.isFunction(b))return this.__toggle.apply(this,arguments);var c=k.apply(this,arguments);c[1].mode="toggle";return this.effect.apply(this,c)},cssUnit:function(b){var c=this.css(b),d=[];a.each(["em","px","%","pt"],function(a,b){c.indexOf(b)>0&&(d=[parseFloat(c),b])});return d}}),a.easing.jswing=a.easing.swing,a.extend(a.easing,{def:"easeOutQuad",swing:function(b,c,d,e,f){return a.easing[a.easing.def](b,c,d,e,f)},easeInQuad:function(a,b,c,d,e){return d*(b/=e)*b+c},easeOutQuad:function(a,b,c,d,e){return-d*(b/=e)*(b-2)+c},easeInOutQuad:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b+c;return-d/2*(--b*(b-2)-1)+c},easeInCubic:function(a,b,c,d,e){return d*(b/=e)*b*b+c},easeOutCubic:function(a,b,c,d,e){return d*((b=b/e-1)*b*b+1)+c},easeInOutCubic:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b+c;return d/2*((b-=2)*b*b+2)+c},easeInQuart:function(a,b,c,d,e){return d*(b/=e)*b*b*b+c},easeOutQuart:function(a,b,c,d,e){return-d*((b=b/e-1)*b*b*b-1)+c},easeInOutQuart:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b*b+c;return-d/2*((b-=2)*b*b*b-2)+c},easeInQuint:function(a,b,c,d,e){return d*(b/=e)*b*b*b*b+c},easeOutQuint:function(a,b,c,d,e){return d*((b=b/e-1)*b*b*b*b+1)+c},easeInOutQuint:function(a,b,c,d,e){if((b/=e/2)<1)return d/2*b*b*b*b*b+c;return d/2*((b-=2)*b*b*b*b+2)+c},easeInSine:function(a,b,c,d,e){return-d*Math.cos(b/e*(Math.PI/2))+d+c},easeOutSine:function(a,b,c,d,e){return d*Math.sin(b/e*(Math.PI/2))+c},easeInOutSine:function(a,b,c,d,e){return-d/2*(Math.cos(Math.PI*b/e)-1)+c},easeInExpo:function(a,b,c,d,e){return b==0?c:d*Math.pow(2,10*(b/e-1))+c},easeOutExpo:function(a,b,c,d,e){return b==e?c+d:d*(-Math.pow(2,-10*b/e)+1)+c},easeInOutExpo:function(a,b,c,d,e){if(b==0)return c;if(b==e)return c+d;if((b/=e/2)<1)return d/2*Math.pow(2,10*(b-1))+c;return d/2*(-Math.pow(2,-10*--b)+2)+c},easeInCirc:function(a,b,c,d,e){return-d*(Math.sqrt(1-(b/=e)*b)-1)+c},easeOutCirc:function(a,b,c,d,e){return d*Math.sqrt(1-(b=b/e-1)*b)+c},easeInOutCirc:function(a,b,c,d,e){if((b/=e/2)<1)return-d/2*(Math.sqrt(1-b*b)-1)+c;return d/2*(Math.sqrt(1-(b-=2)*b)+1)+c},easeInElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e)==1)return c+d;g||(g=e*.3);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return-(h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g))+c},easeOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e)==1)return c+d;g||(g=e*.3);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*b)*Math.sin((b*e-f)*2*Math.PI/g)+d+c},easeInOutElastic:function(a,b,c,d,e){var f=1.70158,g=0,h=d;if(b==0)return c;if((b/=e/2)==2)return c+d;g||(g=e*.3*1.5);if(h<Math.abs(d)){h=d;var f=g/4}else var f=g/(2*Math.PI)*Math.asin(d/h);if(b<1)return-0.5*h*Math.pow(2,10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g)+c;return h*Math.pow(2,-10*(b-=1))*Math.sin((b*e-f)*2*Math.PI/g)*.5+d+c},easeInBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);return e*(c/=f)*c*((g+1)*c-g)+d},easeOutBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);return e*((c=c/f-1)*c*((g+1)*c+g)+1)+d},easeInOutBack:function(a,c,d,e,f,g){g==b&&(g=1.70158);if((c/=f/2)<1)return e/2*c*c*(((g*=1.525)+1)*c-g)+d;return e/2*((c-=2)*c*(((g*=1.525)+1)*c+g)+2)+d},easeInBounce:function(b,c,d,e,f){return e-a.easing.easeOutBounce(b,f-c,0,e,f)+d},easeOutBounce:function(a,b,c,d,e){return(b/=e)<1/2.75?d*7.5625*b*b+c:b<2/2.75?d*(7.5625*(b-=1.5/2.75)*b+.75)+c:b<2.5/2.75?d*(7.5625*(b-=2.25/2.75)*b+.9375)+c:d*(7.5625*(b-=2.625/2.75)*b+.984375)+c},easeInOutBounce:function(b,c,d,e,f){if(c<f/2)return a.easing.easeInBounce(b,c*2,0,e,f)*.5+d;return a.easing.easeOutBounce(b,c*2-f,0,e,f)*.5+e*.5+d}})}(jQuery);/*
+ * jQuery UI Effects Blind 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Blind
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.blind=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"vertical";a.effects.save(c,d),c.show();var g=a.effects.createWrapper(c).css({overflow:"hidden"}),h=f=="vertical"?"height":"width",i=f=="vertical"?g.height():g.width();e=="show"&&g.css(h,0);var j={};j[h]=e=="show"?i:0,g.animate(j,b.duration,b.options.easing,function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery);/*
+ * jQuery UI Effects Bounce 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Bounce
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.bounce=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"effect"),f=b.options.direction||"up",g=b.options.distance||20,h=b.options.times||5,i=b.duration||250;/show|hide/.test(e)&&d.push("opacity"),a.effects.save(c,d),c.show(),a.effects.createWrapper(c);var j=f=="up"||f=="down"?"top":"left",k=f=="up"||f=="left"?"pos":"neg",g=b.options.distance||(j=="top"?c.outerHeight({margin:!0})/3:c.outerWidth({margin:!0})/3);e=="show"&&c.css("opacity",0).css(j,k=="pos"?-g:g),e=="hide"&&(g=g/(h*2)),e!="hide"&&h--;if(e=="show"){var l={opacity:1};l[j]=(k=="pos"?"+=":"-=")+g,c.animate(l,i/2,b.options.easing),g=g/2,h--}for(var m=0;m<h;m++){var n={},p={};n[j]=(k=="pos"?"-=":"+=")+g,p[j]=(k=="pos"?"+=":"-=")+g,c.animate(n,i/2,b.options.easing).animate(p,i/2,b.options.easing),g=e=="hide"?g*2:g/2}if(e=="hide"){var l={opacity:0};l[j]=(k=="pos"?"-=":"+=")+g,c.animate(l,i/2,b.options.easing,function(){c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)})}else{var n={},p={};n[j]=(k=="pos"?"-=":"+=")+g,p[j]=(k=="pos"?"+=":"-=")+g,c.animate(n,i/2,b.options.easing).animate(p,i/2,b.options.easing,function(){a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)})}c.queue("fx",function(){c.dequeue()}),c.dequeue()})}})(jQuery);/*
+ * jQuery UI Effects Clip 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Clip
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.clip=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right","height","width"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"vertical";a.effects.save(c,d),c.show();var g=a.effects.createWrapper(c).css({overflow:"hidden"}),h=c[0].tagName=="IMG"?g:c,i={size:f=="vertical"?"height":"width",position:f=="vertical"?"top":"left"},j=f=="vertical"?h.height():h.width();e=="show"&&(h.css(i.size,0),h.css(i.position,j/2));var k={};k[i.size]=e=="show"?j:0,k[i.position]=e=="show"?0:j/2,h.animate(k,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Drop 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Drop
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.drop=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right","opacity"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.direction||"left";a.effects.save(c,d),c.show(),a.effects.createWrapper(c);var g=f=="up"||f=="down"?"top":"left",h=f=="up"||f=="left"?"pos":"neg",i=b.options.distance||(g=="top"?c.outerHeight({margin:!0})/2:c.outerWidth({margin:!0})/2);e=="show"&&c.css("opacity",0).css(g,h=="pos"?-i:i);var j={opacity:e=="show"?1:0};j[g]=(e=="show"?h=="pos"?"+=":"-=":h=="pos"?"-=":"+=")+i,c.animate(j,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Explode 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Explode
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.explode=function(b){return this.queue(function(){var c=b.options.pieces?Math.round(Math.sqrt(b.options.pieces)):3,d=b.options.pieces?Math.round(Math.sqrt(b.options.pieces)):3;b.options.mode=b.options.mode=="toggle"?a(this).is(":visible")?"hide":"show":b.options.mode;var e=a(this).show().css("visibility","hidden"),f=e.offset();f.top-=parseInt(e.css("marginTop"),10)||0,f.left-=parseInt(e.css("marginLeft"),10)||0;var g=e.outerWidth(!0),h=e.outerHeight(!0);for(var i=0;i<c;i++)for(var j=0;j<d;j++)e.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-j*(g/d),top:-i*(h/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:g/d,height:h/c,left:f.left+j*(g/d)+(b.options.mode=="show"?(j-Math.floor(d/2))*(g/d):0),top:f.top+i*(h/c)+(b.options.mode=="show"?(i-Math.floor(c/2))*(h/c):0),opacity:b.options.mode=="show"?0:1}).animate({left:f.left+j*(g/d)+(b.options.mode=="show"?0:(j-Math.floor(d/2))*(g/d)),top:f.top+i*(h/c)+(b.options.mode=="show"?0:(i-Math.floor(c/2))*(h/c)),opacity:b.options.mode=="show"?1:0},b.duration||500);setTimeout(function(){b.options.mode=="show"?e.css({visibility:"visible"}):e.css({visibility:"visible"}).hide(),b.callback&&b.callback.apply(e[0]),e.dequeue(),a("div.ui-effects-explode").remove()},b.duration||500)})}})(jQuery);/*
+ * jQuery UI Effects Fade 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fade
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.fade=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"hide");c.animate({opacity:d},{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Fold 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fold
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.fold=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"hide"),f=b.options.size||15,g=!!b.options.horizFirst,h=b.duration?b.duration/2:a.fx.speeds._default/2;a.effects.save(c,d),c.show();var i=a.effects.createWrapper(c).css({overflow:"hidden"}),j=e=="show"!=g,k=j?["width","height"]:["height","width"],l=j?[i.width(),i.height()]:[i.height(),i.width()],m=/([0-9]+)%/.exec(f);m&&(f=parseInt(m[1],10)/100*l[e=="hide"?0:1]),e=="show"&&i.css(g?{height:0,width:f}:{height:f,width:0});var n={},p={};n[k[0]]=e=="show"?l[0]:f,p[k[1]]=e=="show"?l[1]:0,i.animate(n,h,b.options.easing).animate(p,h,b.options.easing,function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery);/*
+ * jQuery UI Effects Highlight 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Highlight
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.highlight=function(b){return this.queue(function(){var c=a(this),d=["backgroundImage","backgroundColor","opacity"],e=a.effects.setMode(c,b.options.mode||"show"),f={backgroundColor:c.css("backgroundColor")};e=="hide"&&(f.opacity=0),a.effects.save(c,d),c.show().css({backgroundImage:"none",backgroundColor:b.options.color||"#ffff99"}).animate(f,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),e=="show"&&!a.support.opacity&&this.style.removeAttribute("filter"),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Pulsate 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Pulsate
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.pulsate=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"show");times=(b.options.times||5)*2-1,duration=b.duration?b.duration/2:a.fx.speeds._default/2,isVisible=c.is(":visible"),animateTo=0,isVisible||(c.css("opacity",0).show(),animateTo=1),(d=="hide"&&isVisible||d=="show"&&!isVisible)&&times--;for(var e=0;e<times;e++)c.animate({opacity:animateTo},duration,b.options.easing),animateTo=(animateTo+1)%2;c.animate({opacity:animateTo},duration,b.options.easing,function(){animateTo==0&&c.hide(),b.callback&&b.callback.apply(this,arguments)}),c.queue("fx",function(){c.dequeue()}).dequeue()})}})(jQuery);/*
+ * jQuery UI Effects Scale 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Scale
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.puff=function(b){return this.queue(function(){var c=a(this),d=a.effects.setMode(c,b.options.mode||"hide"),e=parseInt(b.options.percent,10)||150,f=e/100,g={height:c.height(),width:c.width()};a.extend(b.options,{fade:!0,mode:d,percent:d=="hide"?e:100,from:d=="hide"?g:{height:g.height*f,width:g.width*f}}),c.effect("scale",b.options,b.duration,b.callback),c.dequeue()})},a.effects.scale=function(b){return this.queue(function(){var c=a(this),d=a.extend(!0,{},b.options),e=a.effects.setMode(c,b.options.mode||"effect"),f=parseInt(b.options.percent,10)||(parseInt(b.options.percent,10)==0?0:e=="hide"?0:100),g=b.options.direction||"both",h=b.options.origin;e!="effect"&&(d.origin=h||["middle","center"],d.restore=!0);var i={height:c.height(),width:c.width()};c.from=b.options.from||(e=="show"?{height:0,width:0}:i);var j={y:g!="horizontal"?f/100:1,x:g!="vertical"?f/100:1};c.to={height:i.height*j.y,width:i.width*j.x},b.options.fade&&(e=="show"&&(c.from.opacity=0,c.to.opacity=1),e=="hide"&&(c.from.opacity=1,c.to.opacity=0)),d.from=c.from,d.to=c.to,d.mode=e,c.effect("size",d,b.duration,b.callback),c.dequeue()})},a.effects.size=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right","width","height","overflow","opacity"],e=["position","top","bottom","left","right","overflow","opacity"],f=["width","height","overflow"],g=["fontSize"],h=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],i=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],j=a.effects.setMode(c,b.options.mode||"effect"),k=b.options.restore||!1,l=b.options.scale||"both",m=b.options.origin,n={height:c.height(),width:c.width()};c.from=b.options.from||n,c.to=b.options.to||n;if(m){var p=a.effects.getBaseline(m,n);c.from.top=(n.height-c.from.height)*p.y,c.from.left=(n.width-c.from.width)*p.x,c.to.top=(n.height-c.to.height)*p.y,c.to.left=(n.width-c.to.width)*p.x}var q={from:{y:c.from.height/n.height,x:c.from.width/n.width},to:{y:c.to.height/n.height,x:c.to.width/n.width}};if(l=="box"||l=="both")q.from.y!=q.to.y&&(d=d.concat(h),c.from=a.effects.setTransition(c,h,q.from.y,c.from),c.to=a.effects.setTransition(c,h,q.to.y,c.to)),q.from.x!=q.to.x&&(d=d.concat(i),c.from=a.effects.setTransition(c,i,q.from.x,c.from),c.to=a.effects.setTransition(c,i,q.to.x,c.to));(l=="content"||l=="both")&&q.from.y!=q.to.y&&(d=d.concat(g),c.from=a.effects.setTransition(c,g,q.from.y,c.from),c.to=a.effects.setTransition(c,g,q.to.y,c.to)),a.effects.save(c,k?d:e),c.show(),a.effects.createWrapper(c),c.css("overflow","hidden").css(c.from);if(l=="content"||l=="both")h=h.concat(["marginTop","marginBottom"]).concat(g),i=i.concat(["marginLeft","marginRight"]),f=d.concat(h).concat(i),c.find("*[width]").each(function(){child=a(this),k&&a.effects.save(child,f);var c={height:child.height(),width:child.width()};child.from={height:c.height*q.from.y,width:c.width*q.from.x},child.to={height:c.height*q.to.y,width:c.width*q.to.x},q.from.y!=q.to.y&&(child.from=a.effects.setTransition(child,h,q.from.y,child.from),child.to=a.effects.setTransition(child,h,q.to.y,child.to)),q.from.x!=q.to.x&&(child.from=a.effects.setTransition(child,i,q.from.x,child.from),child.to=a.effects.setTransition(child,i,q.to.x,child.to)),child.css(child.from),child.animate(child.to,b.duration,b.options.easing,function(){k&&a.effects.restore(child,f)})});c.animate(c.to,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){c.to.opacity===0&&c.css("opacity",c.from.opacity),j=="hide"&&c.hide(),a.effects.restore(c,k?d:e),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Shake 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Shake
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.shake=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"effect"),f=b.options.direction||"left",g=b.options.distance||20,h=b.options.times||3,i=b.duration||b.options.duration||140;a.effects.save(c,d),c.show(),a.effects.createWrapper(c);var j=f=="up"||f=="down"?"top":"left",k=f=="up"||f=="left"?"pos":"neg",l={},m={},n={};l[j]=(k=="pos"?"-=":"+=")+g,m[j]=(k=="pos"?"+=":"-=")+g*2,n[j]=(k=="pos"?"-=":"+=")+g*2,c.animate(l,i,b.options.easing);for(var p=1;p<h;p++)c.animate(m,i,b.options.easing).animate(n,i,b.options.easing);c.animate(m,i,b.options.easing).animate(l,i/2,b.options.easing,function(){a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments)}),c.queue("fx",function(){c.dequeue()}),c.dequeue()})}})(jQuery);/*
+ * jQuery UI Effects Slide 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Slide
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.slide=function(b){return this.queue(function(){var c=a(this),d=["position","top","bottom","left","right"],e=a.effects.setMode(c,b.options.mode||"show"),f=b.options.direction||"left";a.effects.save(c,d),c.show(),a.effects.createWrapper(c).css({overflow:"hidden"});var g=f=="up"||f=="down"?"top":"left",h=f=="up"||f=="left"?"pos":"neg",i=b.options.distance||(g=="top"?c.outerHeight({margin:!0}):c.outerWidth({margin:!0}));e=="show"&&c.css(g,h=="pos"?isNaN(i)?"-"+i:-i:i);var j={};j[g]=(e=="show"?h=="pos"?"+=":"-=":h=="pos"?"-=":"+=")+i,c.animate(j,{queue:!1,duration:b.duration,easing:b.options.easing,complete:function(){e=="hide"&&c.hide(),a.effects.restore(c,d),a.effects.removeWrapper(c),b.callback&&b.callback.apply(this,arguments),c.dequeue()}})})}})(jQuery);/*
+ * jQuery UI Effects Transfer 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Transfer
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */(function(a,b){a.effects.transfer=function(b){return this.queue(function(){var c=a(this),d=a(b.options.to),e=d.offset(),f={top:e.top,left:e.left,height:d.innerHeight(),width:d.innerWidth()},g=c.offset(),h=a('<div class="ui-effects-transfer"></div>').appendTo(document.body).addClass(b.options.className).css({top:g.top,left:g.left,height:c.innerHeight(),width:c.innerWidth(),position:"absolute"}).animate(f,b.duration,b.options.easing,function(){h.remove(),b.callback&&b.callback.apply(c[0],arguments),c.dequeue()})})}})(jQuery); \ No newline at end of file
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/init-db.sh b/storage/mroonga/vendor/groonga/examples/dictionary/init-db.sh
new file mode 100755
index 00000000000..351d90e7b30
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/init-db.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+if [ 1 != $# ]; then
+ echo "usage: $0 db_path"
+ exit 1
+fi
+
+if groonga-suggest-create-dataset $1 dictionary > /dev/null; then
+ echo "db initialized."
+fi
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/jmdict/Makefile.am b/storage/mroonga/vendor/groonga/examples/dictionary/jmdict/Makefile.am
new file mode 100644
index 00000000000..70b4a5bc010
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/jmdict/Makefile.am
@@ -0,0 +1,3 @@
+jmdictdir = $(examples_dictionarydir)/jmdict
+dist_jmdict_SCRIPTS = \
+ jmdict.rb
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/jmdict/jmdict.rb b/storage/mroonga/vendor/groonga/examples/dictionary/jmdict/jmdict.rb
new file mode 100755
index 00000000000..bf8926783cb
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/jmdict/jmdict.rb
@@ -0,0 +1,42 @@
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
+
+require 'rexml/document'
+require 'rexml/parsers/streamparser'
+require 'rexml/parsers/baseparser'
+require 'rexml/streamlistener'
+
+#REXML::Document.new(STDIN)
+
+class MyListener
+ include REXML::StreamListener
+ def tag_start(name, attrs)
+ # p name, attrs
+ case name
+ when 'entry'
+ @n = 0
+ end
+ end
+ def tag_end name
+ # p "tag_end: #{x}"
+ case name
+ when 'sense'
+ @n += 1
+ when 'entry'
+ @n_ents += 1
+ puts "#{@ent}:#{@n}" if (@n > 8)
+ when 'ent_seq'
+ @ent = @text
+ end
+ end
+
+ def text(text)
+ @text = text
+ end
+
+ def xmldecl(version, encoding, standalone)
+ @n_ents = 0
+ end
+end
+
+REXML::Parsers::StreamParser.new(STDIN, MyListener.new).parse
diff --git a/storage/mroonga/vendor/groonga/examples/dictionary/readme.txt b/storage/mroonga/vendor/groonga/examples/dictionary/readme.txt
new file mode 100644
index 00000000000..555706e0061
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/examples/dictionary/readme.txt
@@ -0,0 +1,71 @@
+.. highlightlang:: none
+
+辞書検索ツール
+==============
+
+名前
+----
+
+groonga辞書検索ツール
+
+説明
+----
+
+様々な商用・非商用の辞書ファイルをインポートしてgroongaで検索できるようにします。
+
+対応している辞書
+++++++++++++++++
+
+現状では下記の辞書に対応しています。
+
+* EDICT
+
+EDICTは、Monash大学Jim Breen教授が提供している和英辞書です。下記から入手できます。
+
+http://ftp.monash.edu.au/pub/nihongo/edict.gz
+
+* GENE95
+
+GENE95は、Kurumiさん(NiftyID: GGD00145)が作成された英和辞書です。下記から入手できます。
+
+http://www.namazu.org/~tsuchiya/sdic/data/gene95.tar.gz
+
+* 英辞郎
+
+英辞郎は、EDPという団体によって編纂されている英和・和英辞書です。
+
+http://www.eijiro.jp/
+
+書店やオンラインショップなどで購入できます。
+
+データベースの初期化
+++++++++++++++++++++
+
+本ディレクトリで下記のように実行し、辞書データを格納するデータベースファイルを下記のようにして初期化します。
+
+ ./init-db.sh データベースパス名
+
+このようにして作成したデータベースについて、様々な辞書のデータをインポートすることができます。
+
+インポートの方法
+++++++++++++++++
+
+* EDICT
+
+edictディレクトリ配下で以下のように実行します。 edict.gzは自動でダウンロードします。
+
+ ./edict-import.sh データベースパス名
+
+* GENE95
+
+gene95ディレクトリ配下で下記のように実行します。 gene95.tar.gzは自動でダウンロードします。
+
+ ./gene-import.sh データベースパス名
+
+* 英辞郎
+
+英辞郎に付属のPDICツールを用いてCSVファイル形式に辞書をエクスポートします。(このとき「登録項目」ですべての項目を出力するようにします) eijiroディレクトリ配下で下記のように実行します。
+
+ ./eijiro-import.sh データベースパス名 出力したCSVファイルのパス名
+
+(英辞郎第四版で動作を確認しています)
diff --git a/storage/mroonga/vendor/groonga/gpg_uid b/storage/mroonga/vendor/groonga/gpg_uid
new file mode 100644
index 00000000000..7c1a800ba92
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/gpg_uid
@@ -0,0 +1 @@
+45499429
diff --git a/storage/mroonga/vendor/groonga/groonga-httpd-conf.sh.in b/storage/mroonga/vendor/groonga/groonga-httpd-conf.sh.in
new file mode 100644
index 00000000000..22a90d82430
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/groonga-httpd-conf.sh.in
@@ -0,0 +1,26 @@
+prefix="@prefix@"
+exec_prefix="@exec_prefix@"
+sbindir="@sbindir@"
+libdir="@libdir@"
+sysconfdir="@sysconfdir@"
+pkgsysconfdir="@pkgsysconfdir@"
+localstatedir="@localstatedir@"
+
+SED="@SED@"
+
+export GROONGA_HTTPD_MODULE_PATH="@abs_top_srcdir@/src/httpd/nginx-module"
+export GROONGA_HTTPD_IN_TREE_INCLUDE_PATH="@abs_top_srcdir@/include"
+export GROONGA_HTTPD_IN_TREE_LINK_PATH="@abs_top_builddir@/lib/.libs"
+export GROONGA_HTTPD_PREFIX="${pkgsysconfdir}/httpd"
+export GROONGA_HTTPD_BIN_PATH="${sbindir}/groonga-httpd"
+export GROONGA_HTTPD_CONF_PATH="${pkgsysconfdir}/httpd/groonga-httpd.conf"
+export GROONGA_HTTPD_ERROR_LOG_PATH="${localstatedir}/log/groonga/httpd/error.log"
+export GROONGA_HTTPD_HTTP_LOG_PATH="${localstatedir}/log/groonga/httpd/access.log"
+export GROONGA_HTTPD_GROONGA_LOG_PATH="${localstatedir}/log/groonga/httpd/groonga.log"
+export GROONGA_HTTPD_GROONGA_QUERY_LOG_PATH="${localstatedir}/log/groonga/httpd/groonga-query.log"
+export GROONGA_HTTPD_PID_PATH="${localstatedir}/run/groonga/groonga-httpd.pid"
+export GROONGA_HTTPD_DEBUG="@grn_debug@"
+export GROONGA_HTTPD_WITH_PCRE="@GRN_WITH_PCRE@"
+export GROONGA_HTTPD_PCRE_CFLAGS="@PCRE_CFLAGS@"
+export GROONGA_HTTPD_PCRE_LIBS_ONLY_L="@PCRE_LIBS_ONLY_L@"
+export GROONGA_HTTPD_WITH_ZLIB="@GRN_WITH_ZLIB@"
diff --git a/storage/mroonga/vendor/groonga/groonga.pc.in b/storage/mroonga/vendor/groonga/groonga.pc.in
new file mode 100644
index 00000000000..869285d48e6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/groonga.pc.in
@@ -0,0 +1,20 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+bindir=@bindir@
+sbindir=@sbindir@
+libdir=@libdir@
+includedir=@includedir@
+datarootdir=@datarootdir@
+datadir=@datadir@
+document_root=@GRN_EXPANDED_DEFAULT_DOCUMENT_ROOT@
+pluginsdir=@expanded_pluginsdir@
+groonga=${bindir}/groonga@EXEEXT@
+groonga_httpd=${sbindir}/groonga-httpd@EXEEXT@
+groonga_suggest_create_dataset=${bindir}/groonga-suggest-create-dataset@EXEEXT@
+groonga_version=@GRN_VERSION@
+
+Name: Groonga
+Description: An Embeddable Fulltext Search Engine
+Version: @VERSION@
+Libs: -L${libdir} -lgroonga
+Cflags: -I${includedir}/groonga
diff --git a/storage/mroonga/vendor/groonga/include/CMakeLists.txt b/storage/mroonga/vendor/groonga/include/CMakeLists.txt
new file mode 100644
index 00000000000..96e22474cbf
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/include/CMakeLists.txt
@@ -0,0 +1,20 @@
+# Copyright(C) 2012 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+if(NOT MRN_GROONGA_BUNDLED)
+ install(FILES groonga.h DESTINATION "${GRN_INCLUDE_DIR}")
+ install(DIRECTORY groonga DESTINATION "${GRN_INCLUDE_DIR}"
+ FILES_MATCHING PATTERN "*.h")
+endif()
diff --git a/storage/mroonga/vendor/groonga/include/Makefile.am b/storage/mroonga/vendor/groonga/include/Makefile.am
new file mode 100644
index 00000000000..19ced0d2d6c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/include/Makefile.am
@@ -0,0 +1,6 @@
+SUBDIRS = groonga
+
+pkginclude_HEADERS = groonga.h
+
+EXTRA_DIST = \
+ CMakeLists.txt
diff --git a/storage/mroonga/vendor/groonga/include/groonga.h b/storage/mroonga/vendor/groonga/include/groonga.h
new file mode 100644
index 00000000000..7ac7f8704a7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/include/groonga.h
@@ -0,0 +1,2101 @@
+/*
+ Copyright(C) 2009-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GROONGA_H
+#define GROONGA_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+
+#ifndef GRN_API
+#if defined(_WIN32) || defined(_WIN64)
+#define GRN_API __declspec(dllimport)
+#else
+#define GRN_API
+#endif /* defined(_WIN32) || defined(_WIN64) */
+#endif /* GRN_API */
+
+typedef unsigned int grn_id;
+typedef unsigned char grn_bool;
+
+#define GRN_ID_NIL (0x00)
+#define GRN_ID_MAX (0x3fffffff)
+
+#define GRN_TRUE (1)
+#define GRN_FALSE (0)
+
+typedef enum {
+ GRN_SUCCESS = 0,
+ GRN_END_OF_DATA = 1,
+ GRN_UNKNOWN_ERROR = -1,
+ GRN_OPERATION_NOT_PERMITTED = -2,
+ GRN_NO_SUCH_FILE_OR_DIRECTORY = -3,
+ GRN_NO_SUCH_PROCESS = -4,
+ GRN_INTERRUPTED_FUNCTION_CALL = -5,
+ GRN_INPUT_OUTPUT_ERROR = -6,
+ GRN_NO_SUCH_DEVICE_OR_ADDRESS = -7,
+ GRN_ARG_LIST_TOO_LONG = -8,
+ GRN_EXEC_FORMAT_ERROR = -9,
+ GRN_BAD_FILE_DESCRIPTOR = -10,
+ GRN_NO_CHILD_PROCESSES = -11,
+ GRN_RESOURCE_TEMPORARILY_UNAVAILABLE = -12,
+ GRN_NOT_ENOUGH_SPACE = -13,
+ GRN_PERMISSION_DENIED = -14,
+ GRN_BAD_ADDRESS = -15,
+ GRN_RESOURCE_BUSY = -16,
+ GRN_FILE_EXISTS = -17,
+ GRN_IMPROPER_LINK = -18,
+ GRN_NO_SUCH_DEVICE = -19,
+ GRN_NOT_A_DIRECTORY = -20,
+ GRN_IS_A_DIRECTORY = -21,
+ GRN_INVALID_ARGUMENT = -22,
+ GRN_TOO_MANY_OPEN_FILES_IN_SYSTEM = -23,
+ GRN_TOO_MANY_OPEN_FILES = -24,
+ GRN_INAPPROPRIATE_I_O_CONTROL_OPERATION = -25,
+ GRN_FILE_TOO_LARGE = -26,
+ GRN_NO_SPACE_LEFT_ON_DEVICE = -27,
+ GRN_INVALID_SEEK = -28,
+ GRN_READ_ONLY_FILE_SYSTEM = -29,
+ GRN_TOO_MANY_LINKS = -30,
+ GRN_BROKEN_PIPE = -31,
+ GRN_DOMAIN_ERROR = -32,
+ GRN_RESULT_TOO_LARGE = -33,
+ GRN_RESOURCE_DEADLOCK_AVOIDED = -34,
+ GRN_NO_MEMORY_AVAILABLE = -35,
+ GRN_FILENAME_TOO_LONG = -36,
+ GRN_NO_LOCKS_AVAILABLE = -37,
+ GRN_FUNCTION_NOT_IMPLEMENTED = -38,
+ GRN_DIRECTORY_NOT_EMPTY = -39,
+ GRN_ILLEGAL_BYTE_SEQUENCE = -40,
+ GRN_SOCKET_NOT_INITIALIZED = -41,
+ GRN_OPERATION_WOULD_BLOCK = -42,
+ GRN_ADDRESS_IS_NOT_AVAILABLE = -43,
+ GRN_NETWORK_IS_DOWN = -44,
+ GRN_NO_BUFFER = -45,
+ GRN_SOCKET_IS_ALREADY_CONNECTED = -46,
+ GRN_SOCKET_IS_NOT_CONNECTED = -47,
+ GRN_SOCKET_IS_ALREADY_SHUTDOWNED = -48,
+ GRN_OPERATION_TIMEOUT = -49,
+ GRN_CONNECTION_REFUSED = -50,
+ GRN_RANGE_ERROR = -51,
+ GRN_TOKENIZER_ERROR = -52,
+ GRN_FILE_CORRUPT = -53,
+ GRN_INVALID_FORMAT = -54,
+ GRN_OBJECT_CORRUPT = -55,
+ GRN_TOO_MANY_SYMBOLIC_LINKS = -56,
+ GRN_NOT_SOCKET = -57,
+ GRN_OPERATION_NOT_SUPPORTED = -58,
+ GRN_ADDRESS_IS_IN_USE = -59,
+ GRN_ZLIB_ERROR = -60,
+ GRN_LZO_ERROR = -61,
+ GRN_STACK_OVER_FLOW = -62,
+ GRN_SYNTAX_ERROR = -63,
+ GRN_RETRY_MAX = -64,
+ GRN_INCOMPATIBLE_FILE_FORMAT = -65,
+ GRN_UPDATE_NOT_ALLOWED = -66,
+ GRN_TOO_SMALL_OFFSET = -67,
+ GRN_TOO_LARGE_OFFSET = -68,
+ GRN_TOO_SMALL_LIMIT = -69,
+ GRN_CAS_ERROR = -70,
+ GRN_UNSUPPORTED_COMMAND_VERSION = -71,
+ GRN_NORMALIZER_ERROR = -72,
+} grn_rc;
+
+GRN_API grn_rc grn_init(void);
+GRN_API grn_rc grn_fin(void);
+
+typedef enum {
+ GRN_ENC_DEFAULT = 0,
+ GRN_ENC_NONE,
+ GRN_ENC_EUC_JP,
+ GRN_ENC_UTF8,
+ GRN_ENC_SJIS,
+ GRN_ENC_LATIN1,
+ GRN_ENC_KOI8R
+} grn_encoding;
+
+typedef enum {
+ GRN_COMMAND_VERSION_DEFAULT = 0,
+ GRN_COMMAND_VERSION_1,
+ GRN_COMMAND_VERSION_2
+} grn_command_version;
+
+#define GRN_COMMAND_VERSION_MIN GRN_COMMAND_VERSION_1
+#define GRN_COMMAND_VERSION_STABLE GRN_COMMAND_VERSION_1
+#define GRN_COMMAND_VERSION_MAX GRN_COMMAND_VERSION_2
+
+typedef enum {
+ GRN_LOG_NONE = 0,
+ GRN_LOG_EMERG,
+ GRN_LOG_ALERT,
+ GRN_LOG_CRIT,
+ GRN_LOG_ERROR,
+ GRN_LOG_WARNING,
+ GRN_LOG_NOTICE,
+ GRN_LOG_INFO,
+ GRN_LOG_DEBUG,
+ GRN_LOG_DUMP
+} grn_log_level;
+
+/* query log flags */
+#define GRN_QUERY_LOG_NONE (0x00)
+#define GRN_QUERY_LOG_COMMAND (0x01<<0)
+#define GRN_QUERY_LOG_RESULT_CODE (0x01<<1)
+#define GRN_QUERY_LOG_DESTINATION (0x01<<2)
+#define GRN_QUERY_LOG_CACHE (0x01<<3)
+#define GRN_QUERY_LOG_SIZE (0x01<<4)
+#define GRN_QUERY_LOG_SCORE (0x01<<5)
+#define GRN_QUERY_LOG_ALL\
+ (GRN_QUERY_LOG_COMMAND |\
+ GRN_QUERY_LOG_RESULT_CODE |\
+ GRN_QUERY_LOG_DESTINATION |\
+ GRN_QUERY_LOG_CACHE |\
+ GRN_QUERY_LOG_SIZE |\
+ GRN_QUERY_LOG_SCORE)
+#define GRN_QUERY_LOG_DEFAULT GRN_QUERY_LOG_ALL
+
+typedef enum {
+ GRN_CONTENT_NONE = 0,
+ GRN_CONTENT_TSV,
+ GRN_CONTENT_JSON,
+ GRN_CONTENT_XML,
+ GRN_CONTENT_MSGPACK
+} grn_content_type;
+
+typedef struct _grn_obj grn_obj;
+typedef struct _grn_ctx grn_ctx;
+
+#define GRN_CTX_MSGSIZE (0x80)
+#define GRN_CTX_FIN (0xff)
+
+typedef union {
+ int int_value;
+ grn_id id;
+ void *ptr;
+} grn_user_data;
+
+typedef grn_obj *grn_proc_func(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data);
+
+struct _grn_ctx {
+ grn_rc rc;
+ int flags;
+ grn_encoding encoding;
+ unsigned char ntrace;
+ unsigned char errlvl;
+ unsigned char stat;
+ unsigned int seqno;
+ unsigned int subno;
+ unsigned int seqno2;
+ unsigned int errline;
+ grn_user_data user_data;
+ grn_ctx *prev;
+ grn_ctx *next;
+ const char *errfile;
+ const char *errfunc;
+ struct _grn_ctx_impl *impl;
+ void *trace[16];
+ char errbuf[GRN_CTX_MSGSIZE];
+};
+
+#define GRN_CTX_USER_DATA(ctx) (&((ctx)->user_data))
+
+/* Deprecated since 4.0.3. Don't use it. */
+#define GRN_CTX_USE_QL (0x03)
+/* Deprecated since 4.0.3. Don't use it. */
+#define GRN_CTX_BATCH_MODE (0x04)
+#define GRN_CTX_PER_DB (0x08)
+
+GRN_API grn_rc grn_ctx_init(grn_ctx *ctx, int flags);
+GRN_API grn_rc grn_ctx_fin(grn_ctx *ctx);
+GRN_API grn_ctx *grn_ctx_open(int flags);
+GRN_API grn_rc grn_ctx_close(grn_ctx *ctx);
+GRN_API grn_rc grn_ctx_set_finalizer(grn_ctx *ctx, grn_proc_func *func);
+
+GRN_API grn_encoding grn_get_default_encoding(void);
+GRN_API grn_rc grn_set_default_encoding(grn_encoding encoding);
+
+#define GRN_CTX_GET_ENCODING(ctx) ((ctx)->encoding)
+#define GRN_CTX_SET_ENCODING(ctx,enc) \
+ ((ctx)->encoding = (enc == GRN_ENC_DEFAULT) ? grn_get_default_encoding() : enc)
+
+GRN_API const char *grn_get_version(void);
+GRN_API const char *grn_get_package(void);
+
+GRN_API grn_command_version grn_get_default_command_version(void);
+GRN_API grn_rc grn_set_default_command_version(grn_command_version version);
+GRN_API grn_command_version grn_ctx_get_command_version(grn_ctx *ctx);
+GRN_API grn_rc grn_ctx_set_command_version(grn_ctx *ctx, grn_command_version version);
+GRN_API long long int grn_ctx_get_match_escalation_threshold(grn_ctx *ctx);
+GRN_API grn_rc grn_ctx_set_match_escalation_threshold(grn_ctx *ctx, long long int threshold);
+GRN_API long long int grn_get_default_match_escalation_threshold(void);
+GRN_API grn_rc grn_set_default_match_escalation_threshold(long long int threshold);
+
+GRN_API int grn_get_lock_timeout(void);
+GRN_API grn_rc grn_set_lock_timeout(int timeout);
+
+/* cache */
+#define GRN_CACHE_DEFAULT_MAX_N_ENTRIES 100
+typedef struct _grn_cache grn_cache;
+
+GRN_API grn_cache *grn_cache_open(grn_ctx *ctx);
+GRN_API grn_rc grn_cache_close(grn_ctx *ctx, grn_cache *cache);
+
+GRN_API grn_rc grn_cache_current_set(grn_ctx *ctx, grn_cache *cache);
+GRN_API grn_cache *grn_cache_current_get(grn_ctx *ctx);
+
+GRN_API grn_rc grn_cache_set_max_n_entries(grn_ctx *ctx,
+ grn_cache *cache,
+ unsigned int n);
+GRN_API unsigned int grn_cache_get_max_n_entries(grn_ctx *ctx,
+ grn_cache *cache);
+
+/* grn_encoding */
+
+GRN_API const char *grn_encoding_to_string(grn_encoding encoding);
+GRN_API grn_encoding grn_encoding_parse(const char *name);
+
+/* obj */
+
+typedef unsigned short int grn_obj_flags;
+
+#define GRN_OBJ_TABLE_TYPE_MASK (0x07)
+#define GRN_OBJ_TABLE_HASH_KEY (0x00)
+#define GRN_OBJ_TABLE_PAT_KEY (0x01)
+#define GRN_OBJ_TABLE_DAT_KEY (0x02)
+#define GRN_OBJ_TABLE_NO_KEY (0x03)
+
+#define GRN_OBJ_KEY_MASK (0x07<<3)
+#define GRN_OBJ_KEY_UINT (0x00<<3)
+#define GRN_OBJ_KEY_INT (0x01<<3)
+#define GRN_OBJ_KEY_FLOAT (0x02<<3)
+#define GRN_OBJ_KEY_GEO_POINT (0x03<<3)
+
+#define GRN_OBJ_KEY_WITH_SIS (0x01<<6)
+#define GRN_OBJ_KEY_NORMALIZE (0x01<<7)
+
+#define GRN_OBJ_COLUMN_TYPE_MASK (0x07)
+#define GRN_OBJ_COLUMN_SCALAR (0x00)
+#define GRN_OBJ_COLUMN_VECTOR (0x01)
+#define GRN_OBJ_COLUMN_INDEX (0x02)
+
+#define GRN_OBJ_COMPRESS_MASK (0x07<<4)
+#define GRN_OBJ_COMPRESS_NONE (0x00<<4)
+#define GRN_OBJ_COMPRESS_ZLIB (0x01<<4)
+#define GRN_OBJ_COMPRESS_LZO (0x02<<4)
+
+#define GRN_OBJ_WITH_SECTION (0x01<<7)
+#define GRN_OBJ_WITH_WEIGHT (0x01<<8)
+#define GRN_OBJ_WITH_POSITION (0x01<<9)
+#define GRN_OBJ_RING_BUFFER (0x01<<10)
+
+#define GRN_OBJ_UNIT_MASK (0x0f<<8)
+#define GRN_OBJ_UNIT_DOCUMENT_NONE (0x00<<8)
+#define GRN_OBJ_UNIT_DOCUMENT_SECTION (0x01<<8)
+#define GRN_OBJ_UNIT_DOCUMENT_POSITION (0x02<<8)
+#define GRN_OBJ_UNIT_SECTION_NONE (0x03<<8)
+#define GRN_OBJ_UNIT_SECTION_POSITION (0x04<<8)
+#define GRN_OBJ_UNIT_POSITION_NONE (0x05<<8)
+#define GRN_OBJ_UNIT_USERDEF_DOCUMENT (0x06<<8)
+#define GRN_OBJ_UNIT_USERDEF_SECTION (0x07<<8)
+#define GRN_OBJ_UNIT_USERDEF_POSITION (0x08<<8)
+
+#define GRN_OBJ_NO_SUBREC (0x00<<13)
+#define GRN_OBJ_WITH_SUBREC (0x01<<13)
+
+#define GRN_OBJ_KEY_VAR_SIZE (0x01<<14)
+
+#define GRN_OBJ_TEMPORARY (0x00<<15)
+#define GRN_OBJ_PERSISTENT (0x01<<15)
+
+/* obj types */
+
+#define GRN_VOID (0x00)
+#define GRN_BULK (0x02)
+#define GRN_PTR (0x03)
+#define GRN_UVECTOR (0x04) /* vector of grn_id */
+#define GRN_PVECTOR (0x05) /* vector of grn_obj* */
+#define GRN_VECTOR (0x06) /* vector of arbitrary data */
+#define GRN_MSG (0x07)
+#define GRN_QUERY (0x08)
+#define GRN_ACCESSOR (0x09)
+#define GRN_SNIP (0x0b)
+#define GRN_PATSNIP (0x0c)
+#define GRN_STRING (0x0d)
+#define GRN_CURSOR_TABLE_HASH_KEY (0x10)
+#define GRN_CURSOR_TABLE_PAT_KEY (0x11)
+#define GRN_CURSOR_TABLE_DAT_KEY (0x12)
+#define GRN_CURSOR_TABLE_NO_KEY (0x13)
+#define GRN_CURSOR_COLUMN_INDEX (0x18)
+#define GRN_CURSOR_COLUMN_GEO_INDEX (0x1a)
+#define GRN_TYPE (0x20)
+#define GRN_PROC (0x21)
+#define GRN_EXPR (0x22)
+#define GRN_TABLE_HASH_KEY (0x30)
+#define GRN_TABLE_PAT_KEY (0x31)
+#define GRN_TABLE_DAT_KEY (0x32)
+#define GRN_TABLE_NO_KEY (0x33)
+#define GRN_DB (0x37)
+#define GRN_COLUMN_FIX_SIZE (0x40)
+#define GRN_COLUMN_VAR_SIZE (0x41)
+#define GRN_COLUMN_INDEX (0x48)
+
+typedef struct _grn_section grn_section;
+typedef struct _grn_obj_header grn_obj_header;
+
+struct _grn_section {
+ unsigned int offset;
+ unsigned int length;
+ unsigned int weight;
+ grn_id domain;
+};
+
+struct _grn_obj_header {
+ unsigned char type;
+ unsigned char impl_flags;
+ grn_obj_flags flags;
+ grn_id domain;
+};
+
+struct _grn_obj {
+ grn_obj_header header;
+ union {
+ struct {
+ char *head;
+ char *curr;
+ char *tail;
+ } b;
+ struct {
+ grn_obj *body;
+ grn_section *sections;
+ int n_sections;
+ } v;
+ } u;
+};
+
+#define GRN_OBJ_REFER (0x01<<0)
+#define GRN_OBJ_OUTPLACE (0x01<<1)
+
+#define GRN_OBJ_INIT(obj,obj_type,obj_flags,obj_domain) do { \
+ (obj)->header.type = (obj_type);\
+ (obj)->header.impl_flags = (obj_flags);\
+ (obj)->header.flags = 0;\
+ (obj)->header.domain = (obj_domain);\
+ (obj)->u.b.head = NULL;\
+ (obj)->u.b.curr = NULL;\
+ (obj)->u.b.tail = NULL;\
+} while (0)
+
+#define GRN_OBJ_FIN(ctx,obj) (grn_obj_close((ctx), (obj)))
+
+typedef struct _grn_db_create_optarg grn_db_create_optarg;
+
+struct _grn_db_create_optarg {
+ char **builtin_type_names;
+ int n_builtin_type_names;
+};
+
+GRN_API grn_obj *grn_db_create(grn_ctx *ctx, const char *path, grn_db_create_optarg *optarg);
+
+#define GRN_DB_OPEN_OR_CREATE(ctx,path,optarg,db) \
+ (((db) = grn_db_open((ctx), (path))) || (db = grn_db_create((ctx), (path), (optarg))))
+
+GRN_API grn_obj *grn_db_open(grn_ctx *ctx, const char *path);
+GRN_API void grn_db_touch(grn_ctx *ctx, grn_obj *db);
+
+GRN_API grn_rc grn_ctx_use(grn_ctx *ctx, grn_obj *db);
+GRN_API grn_obj *grn_ctx_db(grn_ctx *ctx);
+GRN_API grn_obj *grn_ctx_get(grn_ctx *ctx, const char *name, int name_size);
+
+typedef enum {
+ GRN_DB_VOID = 0,
+ GRN_DB_DB,
+ GRN_DB_OBJECT,
+ GRN_DB_BOOL,
+ GRN_DB_INT8,
+ GRN_DB_UINT8,
+ GRN_DB_INT16,
+ GRN_DB_UINT16,
+ GRN_DB_INT32,
+ GRN_DB_UINT32,
+ GRN_DB_INT64,
+ GRN_DB_UINT64,
+ GRN_DB_FLOAT,
+ GRN_DB_TIME,
+ GRN_DB_SHORT_TEXT,
+ GRN_DB_TEXT,
+ GRN_DB_LONG_TEXT,
+ GRN_DB_TOKYO_GEO_POINT,
+ GRN_DB_WGS84_GEO_POINT
+} grn_builtin_type;
+
+typedef enum {
+ GRN_DB_MECAB = 64,
+ GRN_DB_DELIMIT,
+ GRN_DB_UNIGRAM,
+ GRN_DB_BIGRAM,
+ GRN_DB_TRIGRAM
+} grn_builtin_tokenizer;
+
+GRN_API grn_obj *grn_ctx_at(grn_ctx *ctx, grn_id id);
+
+GRN_API grn_obj *grn_type_create(grn_ctx *ctx, const char *name, unsigned int name_size,
+ grn_obj_flags flags, unsigned int size);
+
+GRN_API grn_rc grn_plugin_register(grn_ctx *ctx, const char *name);
+GRN_API grn_rc grn_plugin_register_by_path(grn_ctx *ctx, const char *path);
+GRN_API const char *grn_plugin_get_system_plugins_dir(void);
+GRN_API const char *grn_plugin_get_suffix(void);
+
+typedef struct {
+ const char *name;
+ unsigned int name_size;
+ grn_obj value;
+} grn_expr_var;
+
+typedef grn_rc (*grn_plugin_func)(grn_ctx *ctx);
+
+typedef enum {
+ GRN_PROC_INVALID = 0,
+ GRN_PROC_TOKENIZER,
+ GRN_PROC_COMMAND,
+ GRN_PROC_FUNCTION,
+ GRN_PROC_HOOK,
+ GRN_PROC_NORMALIZER
+} grn_proc_type;
+
+GRN_API grn_obj *grn_proc_create(grn_ctx *ctx,
+ const char *name, int name_size, grn_proc_type type,
+ grn_proc_func *init, grn_proc_func *next, grn_proc_func *fin,
+ unsigned int nvars, grn_expr_var *vars);
+GRN_API grn_obj *grn_proc_get_info(grn_ctx *ctx, grn_user_data *user_data,
+ grn_expr_var **vars, unsigned int *nvars, grn_obj **caller);
+GRN_API grn_proc_type grn_proc_get_type(grn_ctx *ctx, grn_obj *proc);
+
+/*-------------------------------------------------------------
+ * API for table
+ */
+
+#define GRN_TABLE_MAX_KEY_SIZE (0x1000)
+
+GRN_API grn_obj *grn_table_create(grn_ctx *ctx,
+ const char *name, unsigned int name_size,
+ const char *path, grn_obj_flags flags,
+ grn_obj *key_type, grn_obj *value_type);
+
+#define GRN_TABLE_OPEN_OR_CREATE(ctx,name,name_size,path,flags,key_type,value_type,table) \
+ (((table) = grn_ctx_get((ctx), (name), (name_size))) ||\
+ ((table) = grn_table_create((ctx), (name), (name_size), (path), (flags), (key_type), (value_type))))
+
+/* TODO: int *added -> grn_bool *added */
+GRN_API grn_id grn_table_add(grn_ctx *ctx, grn_obj *table,
+ const void *key, unsigned int key_size, int *added);
+GRN_API grn_id grn_table_get(grn_ctx *ctx, grn_obj *table,
+ const void *key, unsigned int key_size);
+GRN_API grn_id grn_table_at(grn_ctx *ctx, grn_obj *table, grn_id id);
+GRN_API grn_id grn_table_lcp_search(grn_ctx *ctx, grn_obj *table,
+ const void *key, unsigned int key_size);
+GRN_API int grn_table_get_key(grn_ctx *ctx, grn_obj *table,
+ grn_id id, void *keybuf, int buf_size);
+GRN_API grn_rc grn_table_delete(grn_ctx *ctx, grn_obj *table,
+ const void *key, unsigned int key_size);
+GRN_API grn_rc grn_table_delete_by_id(grn_ctx *ctx, grn_obj *table, grn_id id);
+GRN_API grn_rc grn_table_update_by_id(grn_ctx *ctx, grn_obj *table, grn_id id,
+ const void *dest_key, unsigned int dest_key_size);
+GRN_API grn_rc grn_table_update(grn_ctx *ctx, grn_obj *table,
+ const void *src_key, unsigned int src_key_size,
+ const void *dest_key, unsigned int dest_key_size);
+GRN_API grn_rc grn_table_truncate(grn_ctx *ctx, grn_obj *table);
+
+typedef grn_obj grn_table_cursor;
+
+#define GRN_CURSOR_ASCENDING (0x00<<0)
+#define GRN_CURSOR_DESCENDING (0x01<<0)
+#define GRN_CURSOR_GE (0x00<<1)
+#define GRN_CURSOR_GT (0x01<<1)
+#define GRN_CURSOR_LE (0x00<<2)
+#define GRN_CURSOR_LT (0x01<<2)
+#define GRN_CURSOR_BY_KEY (0x00<<3)
+#define GRN_CURSOR_BY_ID (0x01<<3)
+#define GRN_CURSOR_PREFIX (0x01<<4)
+#define GRN_CURSOR_SIZE_BY_BIT (0x01<<5)
+#define GRN_CURSOR_RK (0x01<<6)
+
+GRN_API grn_table_cursor *grn_table_cursor_open(grn_ctx *ctx, grn_obj *table,
+ const void *min, unsigned int min_size,
+ const void *max, unsigned int max_size,
+ int offset, int limit, int flags);
+GRN_API grn_rc grn_table_cursor_close(grn_ctx *ctx, grn_table_cursor *tc);
+GRN_API grn_id grn_table_cursor_next(grn_ctx *ctx, grn_table_cursor *tc);
+GRN_API int grn_table_cursor_get_key(grn_ctx *ctx, grn_table_cursor *tc, void **key);
+GRN_API int grn_table_cursor_get_value(grn_ctx *ctx, grn_table_cursor *tc, void **value);
+GRN_API grn_rc grn_table_cursor_set_value(grn_ctx *ctx, grn_table_cursor *tc,
+ const void *value, int flags);
+GRN_API grn_rc grn_table_cursor_delete(grn_ctx *ctx, grn_table_cursor *tc);
+GRN_API grn_obj *grn_table_cursor_table(grn_ctx *ctx, grn_table_cursor *tc);
+
+typedef struct {
+ grn_id rid;
+ grn_id sid;
+ unsigned int pos;
+ unsigned int tf;
+ unsigned int weight;
+ unsigned int rest;
+} grn_posting;
+
+GRN_API grn_obj *grn_index_cursor_open(grn_ctx *ctx, grn_table_cursor *tc, grn_obj *index,
+ grn_id rid_min, grn_id rid_max, int flags);
+GRN_API grn_posting *grn_index_cursor_next(grn_ctx *ctx, grn_obj *ic, grn_id *tid);
+
+#define GRN_TABLE_EACH(ctx,table,head,tail,id,key,key_size,value,block) do {\
+ (ctx)->errlvl = GRN_LOG_NOTICE;\
+ (ctx)->rc = GRN_SUCCESS;\
+ if ((ctx)->seqno & 1) {\
+ (ctx)->subno++;\
+ } else {\
+ (ctx)->seqno++;\
+ }\
+ if (table) {\
+ switch ((table)->header.type) {\
+ case GRN_TABLE_PAT_KEY :\
+ GRN_PAT_EACH((ctx), (grn_pat *)(table), (id), (key), (key_size), (value), block);\
+ break;\
+ case GRN_TABLE_DAT_KEY :\
+ GRN_DAT_EACH((ctx), (grn_dat *)(table), (id), (key), (key_size), block);\
+ break;\
+ case GRN_TABLE_HASH_KEY :\
+ GRN_HASH_EACH((ctx), (grn_hash *)(table), (id), (key), (key_size), (value), block);\
+ break;\
+ case GRN_TABLE_NO_KEY :\
+ GRN_ARRAY_EACH((ctx), (grn_array *)(table), (head), (tail), (id), (value), block);\
+ break;\
+ }\
+ }\
+ if ((ctx)->subno) {\
+ (ctx)->subno--;\
+ } else {\
+ (ctx)->seqno++;\
+ }\
+} while (0)
+
+typedef struct _grn_table_sort_key grn_table_sort_key;
+typedef unsigned char grn_table_sort_flags;
+
+#define GRN_TABLE_SORT_ASC (0x00<<0)
+#define GRN_TABLE_SORT_DESC (0x01<<0)
+
+struct _grn_table_sort_key {
+ grn_obj *key;
+ grn_table_sort_flags flags;
+ int offset;
+};
+
+GRN_API int grn_table_sort(grn_ctx *ctx, grn_obj *table, int offset, int limit,
+ grn_obj *result, grn_table_sort_key *keys, int n_keys);
+
+typedef struct _grn_table_group_result grn_table_group_result;
+typedef unsigned int grn_table_group_flags;
+
+#define GRN_TABLE_GROUP_CALC_COUNT (0x01<<3)
+#define GRN_TABLE_GROUP_CALC_MAX (0x01<<4)
+#define GRN_TABLE_GROUP_CALC_MIN (0x01<<5)
+#define GRN_TABLE_GROUP_CALC_SUM (0x01<<6)
+#define GRN_TABLE_GROUP_CALC_AVG (0x01<<7)
+
+typedef enum {
+ GRN_OP_PUSH = 0,
+ GRN_OP_POP,
+ GRN_OP_NOP,
+ GRN_OP_CALL,
+ GRN_OP_INTERN,
+ GRN_OP_GET_REF,
+ GRN_OP_GET_VALUE,
+ GRN_OP_AND,
+ GRN_OP_AND_NOT,
+ /* Deprecated. Just for backward compatibility. */
+#define GRN_OP_BUT GRN_OP_AND_NOT
+ GRN_OP_OR,
+ GRN_OP_ASSIGN,
+ GRN_OP_STAR_ASSIGN,
+ GRN_OP_SLASH_ASSIGN,
+ GRN_OP_MOD_ASSIGN,
+ GRN_OP_PLUS_ASSIGN,
+ GRN_OP_MINUS_ASSIGN,
+ GRN_OP_SHIFTL_ASSIGN,
+ GRN_OP_SHIFTR_ASSIGN,
+ GRN_OP_SHIFTRR_ASSIGN,
+ GRN_OP_AND_ASSIGN,
+ GRN_OP_XOR_ASSIGN,
+ GRN_OP_OR_ASSIGN,
+ GRN_OP_JUMP,
+ GRN_OP_CJUMP,
+ GRN_OP_COMMA,
+ GRN_OP_BITWISE_OR,
+ GRN_OP_BITWISE_XOR,
+ GRN_OP_BITWISE_AND,
+ GRN_OP_BITWISE_NOT,
+ GRN_OP_EQUAL,
+ GRN_OP_NOT_EQUAL,
+ GRN_OP_LESS,
+ GRN_OP_GREATER,
+ GRN_OP_LESS_EQUAL,
+ GRN_OP_GREATER_EQUAL,
+ GRN_OP_IN,
+ GRN_OP_MATCH,
+ GRN_OP_NEAR,
+ GRN_OP_NEAR2,
+ GRN_OP_SIMILAR,
+ GRN_OP_TERM_EXTRACT,
+ GRN_OP_SHIFTL,
+ GRN_OP_SHIFTR,
+ GRN_OP_SHIFTRR,
+ GRN_OP_PLUS,
+ GRN_OP_MINUS,
+ GRN_OP_STAR,
+ GRN_OP_SLASH,
+ GRN_OP_MOD,
+ GRN_OP_DELETE,
+ GRN_OP_INCR,
+ GRN_OP_DECR,
+ GRN_OP_INCR_POST,
+ GRN_OP_DECR_POST,
+ GRN_OP_NOT,
+ GRN_OP_ADJUST,
+ GRN_OP_EXACT,
+ GRN_OP_LCP,
+ GRN_OP_PARTIAL,
+ GRN_OP_UNSPLIT,
+ GRN_OP_PREFIX,
+ GRN_OP_SUFFIX,
+ GRN_OP_GEO_DISTANCE1,
+ GRN_OP_GEO_DISTANCE2,
+ GRN_OP_GEO_DISTANCE3,
+ GRN_OP_GEO_DISTANCE4,
+ GRN_OP_GEO_WITHINP5,
+ GRN_OP_GEO_WITHINP6,
+ GRN_OP_GEO_WITHINP8,
+ GRN_OP_OBJ_SEARCH,
+ GRN_OP_EXPR_GET_VAR,
+ GRN_OP_TABLE_CREATE,
+ GRN_OP_TABLE_SELECT,
+ GRN_OP_TABLE_SORT,
+ GRN_OP_TABLE_GROUP,
+ GRN_OP_JSON_PUT,
+ GRN_OP_GET_MEMBER
+} grn_operator;
+
+struct _grn_table_group_result {
+ grn_obj *table;
+ unsigned char key_begin;
+ unsigned char key_end;
+ int limit;
+ grn_table_group_flags flags;
+ grn_operator op;
+};
+
+GRN_API grn_rc grn_table_group(grn_ctx *ctx, grn_obj *table,
+ grn_table_sort_key *keys, int n_keys,
+ grn_table_group_result *results, int n_results);
+GRN_API grn_rc grn_table_setoperation(grn_ctx *ctx, grn_obj *table1, grn_obj *table2,
+ grn_obj *res, grn_operator op);
+GRN_API grn_rc grn_table_difference(grn_ctx *ctx, grn_obj *table1, grn_obj *table2,
+ grn_obj *res1, grn_obj *res2);
+GRN_API int grn_table_columns(grn_ctx *ctx, grn_obj *table,
+ const char *name, unsigned int name_size,
+ grn_obj *res);
+
+GRN_API grn_obj *grn_obj_column(grn_ctx *ctx, grn_obj *table,
+ const char *name, unsigned int name_size);
+
+GRN_API unsigned int grn_table_size(grn_ctx *ctx, grn_obj *table);
+
+/*-------------------------------------------------------------
+ * API for column
+ */
+
+#define GRN_COLUMN_NAME_ID "_id"
+#define GRN_COLUMN_NAME_ID_LEN (sizeof(GRN_COLUMN_NAME_ID) - 1)
+#define GRN_COLUMN_NAME_KEY "_key"
+#define GRN_COLUMN_NAME_KEY_LEN (sizeof(GRN_COLUMN_NAME_KEY) - 1)
+#define GRN_COLUMN_NAME_VALUE "_value"
+#define GRN_COLUMN_NAME_VALUE_LEN (sizeof(GRN_COLUMN_NAME_VALUE) - 1)
+#define GRN_COLUMN_NAME_SCORE "_score"
+#define GRN_COLUMN_NAME_SCORE_LEN (sizeof(GRN_COLUMN_NAME_SCORE) - 1)
+#define GRN_COLUMN_NAME_NSUBRECS "_nsubrecs"
+#define GRN_COLUMN_NAME_NSUBRECS_LEN (sizeof(GRN_COLUMN_NAME_NSUBRECS) - 1)
+
+GRN_API grn_obj *grn_column_create(grn_ctx *ctx, grn_obj *table,
+ const char *name, unsigned int name_size,
+ const char *path, grn_obj_flags flags, grn_obj *type);
+
+#define GRN_COLUMN_OPEN_OR_CREATE(ctx,table,name,name_size,path,flags,type,column) \
+ (((column) = grn_obj_column((ctx), (table), (name), (name_size))) ||\
+ ((column) = grn_column_create((ctx), (table), (name), (name_size), (path), (flags), (type))))
+
+GRN_API grn_rc grn_column_index_update(grn_ctx *ctx, grn_obj *column,
+ grn_id id, unsigned int section,
+ grn_obj *oldvalue, grn_obj *newvalue);
+GRN_API grn_obj *grn_column_table(grn_ctx *ctx, grn_obj *column);
+
+/*-------------------------------------------------------------
+ * API for db, table and/or column
+ */
+
+typedef enum {
+ GRN_INFO_ENCODING = 0,
+ GRN_INFO_SOURCE,
+ GRN_INFO_DEFAULT_TOKENIZER,
+ GRN_INFO_ELEMENT_SIZE,
+ GRN_INFO_CURR_MAX,
+ GRN_INFO_MAX_ELEMENT_SIZE,
+ GRN_INFO_SEG_SIZE,
+ GRN_INFO_CHUNK_SIZE,
+ GRN_INFO_MAX_SECTION,
+ GRN_INFO_HOOK_LOCAL_DATA,
+ GRN_INFO_ELEMENT_A,
+ GRN_INFO_ELEMENT_CHUNK,
+ GRN_INFO_ELEMENT_CHUNK_SIZE,
+ GRN_INFO_ELEMENT_BUFFER_FREE,
+ GRN_INFO_ELEMENT_NTERMS,
+ GRN_INFO_ELEMENT_NTERMS_VOID,
+ GRN_INFO_ELEMENT_SIZE_IN_CHUNK,
+ GRN_INFO_ELEMENT_POS_IN_CHUNK,
+ GRN_INFO_ELEMENT_SIZE_IN_BUFFER,
+ GRN_INFO_ELEMENT_POS_IN_BUFFER,
+ GRN_INFO_ELEMENT_ESTIMATE_SIZE,
+ GRN_INFO_NGRAM_UNIT_SIZE,
+ /*
+ GRN_INFO_VERSION,
+ GRN_INFO_CONFIGURE_OPTIONS,
+ GRN_INFO_CONFIG_PATH,
+ */
+ GRN_INFO_PARTIAL_MATCH_THRESHOLD,
+ GRN_INFO_II_SPLIT_THRESHOLD,
+ GRN_INFO_SUPPORT_ZLIB,
+ GRN_INFO_SUPPORT_LZO,
+ GRN_INFO_NORMALIZER
+} grn_info_type;
+
+GRN_API grn_obj *grn_obj_get_info(grn_ctx *ctx, grn_obj *obj, grn_info_type type, grn_obj *valuebuf);
+GRN_API grn_rc grn_obj_set_info(grn_ctx *ctx, grn_obj *obj, grn_info_type type, grn_obj *value);
+GRN_API grn_obj *grn_obj_get_element_info(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_info_type type, grn_obj *value);
+GRN_API grn_rc grn_obj_set_element_info(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_info_type type, grn_obj *value);
+
+GRN_API grn_bool grn_obj_is_builtin(grn_ctx *ctx, grn_obj *obj);
+
+GRN_API grn_obj *grn_obj_get_value(grn_ctx *ctx, grn_obj *obj, grn_id id, grn_obj *value);
+GRN_API int grn_obj_get_values(grn_ctx *ctx, grn_obj *obj, grn_id offset, void **values);
+
+#define GRN_COLUMN_EACH(ctx,column,id,value,block) do {\
+ int _n;\
+ grn_id id = 1;\
+ while ((_n = grn_obj_get_values(ctx, column, id, (void **)&value)) > 0) {\
+ for (; _n; _n--, id++, value++) {\
+ block\
+ }\
+ }\
+} while (0)
+
+#define GRN_OBJ_SET_MASK (0x07)
+#define GRN_OBJ_SET (0x01)
+#define GRN_OBJ_INCR (0x02)
+#define GRN_OBJ_DECR (0x03)
+#define GRN_OBJ_APPEND (0x04)
+#define GRN_OBJ_PREPEND (0x05)
+#define GRN_OBJ_GET (0x01<<4)
+#define GRN_OBJ_COMPARE (0x01<<5)
+#define GRN_OBJ_LOCK (0x01<<6)
+#define GRN_OBJ_UNLOCK (0x01<<7)
+
+GRN_API grn_rc grn_obj_set_value(grn_ctx *ctx, grn_obj *obj, grn_id id, grn_obj *value, int flags);
+GRN_API grn_rc grn_obj_remove(grn_ctx *ctx, grn_obj *obj);
+GRN_API grn_rc grn_obj_rename(grn_ctx *ctx, grn_obj *obj,
+ const char *name, unsigned int name_size);
+GRN_API grn_rc grn_table_rename(grn_ctx *ctx, grn_obj *table,
+ const char *name, unsigned int name_size);
+
+GRN_API grn_rc grn_column_rename(grn_ctx *ctx, grn_obj *column,
+ const char *name, unsigned int name_size);
+
+GRN_API grn_rc grn_obj_close(grn_ctx *ctx, grn_obj *obj);
+GRN_API grn_rc grn_obj_reinit(grn_ctx *ctx, grn_obj *obj, grn_id domain, unsigned char flags);
+GRN_API void grn_obj_unlink(grn_ctx *ctx, grn_obj *obj);
+
+GRN_API grn_user_data *grn_obj_user_data(grn_ctx *ctx, grn_obj *obj);
+
+GRN_API grn_rc grn_obj_set_finalizer(grn_ctx *ctx, grn_obj *obj, grn_proc_func *func);
+
+GRN_API const char *grn_obj_path(grn_ctx *ctx, grn_obj *obj);
+GRN_API int grn_obj_name(grn_ctx *ctx, grn_obj *obj, char *namebuf, int buf_size);
+
+GRN_API int grn_column_name(grn_ctx *ctx, grn_obj *obj, char *namebuf, int buf_size);
+
+GRN_API grn_id grn_obj_get_range(grn_ctx *ctx, grn_obj *obj);
+
+#define GRN_OBJ_GET_DOMAIN(obj) \
+ ((obj)->header.type == GRN_TABLE_NO_KEY ? GRN_ID_NIL : (obj)->header.domain)
+
+GRN_API int grn_obj_expire(grn_ctx *ctx, grn_obj *obj, int threshold);
+GRN_API int grn_obj_check(grn_ctx *ctx, grn_obj *obj);
+GRN_API grn_rc grn_obj_lock(grn_ctx *ctx, grn_obj *obj, grn_id id, int timeout);
+GRN_API grn_rc grn_obj_unlock(grn_ctx *ctx, grn_obj *obj, grn_id id);
+GRN_API grn_rc grn_obj_clear_lock(grn_ctx *ctx, grn_obj *obj);
+GRN_API unsigned int grn_obj_is_locked(grn_ctx *ctx, grn_obj *obj);
+GRN_API int grn_obj_defrag(grn_ctx *ctx, grn_obj *obj, int threshold);
+
+GRN_API grn_obj *grn_obj_db(grn_ctx *ctx, grn_obj *obj);
+
+GRN_API grn_id grn_obj_id(grn_ctx *ctx, grn_obj *obj);
+
+typedef struct _grn_search_optarg grn_search_optarg;
+
+struct _grn_search_optarg {
+ grn_operator mode;
+ int similarity_threshold;
+ int max_interval;
+ int *weight_vector;
+ int vector_size;
+ grn_obj *proc;
+ int max_size;
+};
+
+GRN_API grn_rc grn_obj_search(grn_ctx *ctx, grn_obj *obj, grn_obj *query,
+ grn_obj *res, grn_operator op, grn_search_optarg *optarg);
+
+typedef grn_rc grn_selector_func(grn_ctx *ctx, grn_obj *table, grn_obj *index,
+ int nargs, grn_obj **args,
+ grn_obj *res, grn_operator op);
+
+GRN_API grn_rc grn_proc_set_selector(grn_ctx *ctx, grn_obj *proc,
+ grn_selector_func selector);
+
+/*-------------------------------------------------------------
+ * grn_vector
+*/
+
+GRN_API unsigned int grn_vector_size(grn_ctx *ctx, grn_obj *vector);
+
+GRN_API grn_rc grn_vector_add_element(grn_ctx *ctx, grn_obj *vector,
+ const char *str, unsigned int str_len,
+ unsigned int weight, grn_id domain);
+
+GRN_API unsigned int grn_vector_get_element(grn_ctx *ctx, grn_obj *vector,
+ unsigned int offset, const char **str,
+ unsigned int *weight, grn_id *domain);
+
+/*-------------------------------------------------------------
+ * grn_uvector
+*/
+
+GRN_API unsigned int grn_uvector_size(grn_ctx *ctx, grn_obj *uvector);
+
+GRN_API grn_rc grn_uvector_add_element(grn_ctx *ctx, grn_obj *vector,
+ grn_id id, unsigned int weight);
+
+GRN_API grn_id grn_uvector_get_element(grn_ctx *ctx, grn_obj *uvector,
+ unsigned int offset,
+ unsigned int *weight);
+
+/*-------------------------------------------------------------
+ * API for hook
+ */
+
+GRN_API int grn_proc_call_next(grn_ctx *ctx, grn_obj *exec_info, grn_obj *in, grn_obj *out);
+GRN_API void *grn_proc_get_ctx_local_data(grn_ctx *ctx, grn_obj *exec_info);
+GRN_API void *grn_proc_get_hook_local_data(grn_ctx *ctx, grn_obj *exec_info);
+
+typedef enum {
+ GRN_HOOK_SET = 0,
+ GRN_HOOK_GET,
+ GRN_HOOK_INSERT,
+ GRN_HOOK_DELETE,
+ GRN_HOOK_SELECT
+} grn_hook_entry;
+
+GRN_API grn_rc grn_obj_add_hook(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry,
+ int offset, grn_obj *proc, grn_obj *data);
+GRN_API int grn_obj_get_nhooks(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry);
+GRN_API grn_obj *grn_obj_get_hook(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry,
+ int offset, grn_obj *data);
+GRN_API grn_rc grn_obj_delete_hook(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry, int offset);
+
+GRN_API grn_obj *grn_obj_open(grn_ctx *ctx, unsigned char type, grn_obj_flags flags, grn_id domain);
+
+GRN_API int grn_column_index(grn_ctx *ctx, grn_obj *column, grn_operator op,
+ grn_obj **indexbuf, int buf_size, int *section);
+
+GRN_API grn_rc grn_obj_delete_by_id(grn_ctx *ctx, grn_obj *db, grn_id id, grn_bool removep);
+GRN_API grn_rc grn_obj_path_by_id(grn_ctx *ctx, grn_obj *db, grn_id id, char *buffer);
+
+/* geo */
+
+typedef struct {
+ int latitude;
+ int longitude;
+} grn_geo_point;
+
+GRN_API grn_rc grn_geo_select_in_rectangle(grn_ctx *ctx,
+ grn_obj *index,
+ grn_obj *top_left_point,
+ grn_obj *bottom_right_point,
+ grn_obj *res,
+ grn_operator op);
+GRN_API int grn_geo_estimate_in_rectangle(grn_ctx *ctx,
+ grn_obj *index,
+ grn_obj *top_left_point,
+ grn_obj *bottom_right_point);
+GRN_API grn_obj *grn_geo_cursor_open_in_rectangle(grn_ctx *ctx,
+ grn_obj *index,
+ grn_obj *top_left_point,
+ grn_obj *bottom_right_point,
+ int offset,
+ int limit);
+GRN_API grn_posting *grn_geo_cursor_next(grn_ctx *ctx, grn_obj *cursor);
+
+
+/* query & snippet */
+
+#ifndef GRN_QUERY_AND
+#define GRN_QUERY_AND '+'
+#endif /* GRN_QUERY_AND */
+#ifndef GRN_QUERY_AND_NOT
+# ifdef GRN_QUERY_BUT
+ /* Deprecated. Just for backward compatibility. */
+# define GRN_QUERY_AND_NOT GRN_QUERY_BUT
+# else
+# define GRN_QUERY_AND_NOT '-'
+# endif /* GRN_QUERY_BUT */
+#endif /* GRN_QUERY_AND_NOT */
+#ifndef GRN_QUERY_ADJ_INC
+#define GRN_QUERY_ADJ_INC '>'
+#endif /* GRN_QUERY_ADJ_POS2 */
+#ifndef GRN_QUERY_ADJ_DEC
+#define GRN_QUERY_ADJ_DEC '<'
+#endif /* GRN_QUERY_ADJ_POS1 */
+#ifndef GRN_QUERY_ADJ_NEG
+#define GRN_QUERY_ADJ_NEG '~'
+#endif /* GRN_QUERY_ADJ_NEG */
+#ifndef GRN_QUERY_PREFIX
+#define GRN_QUERY_PREFIX '*'
+#endif /* GRN_QUERY_PREFIX */
+#ifndef GRN_QUERY_PARENL
+#define GRN_QUERY_PARENL '('
+#endif /* GRN_QUERY_PARENL */
+#ifndef GRN_QUERY_PARENR
+#define GRN_QUERY_PARENR ')'
+#endif /* GRN_QUERY_PARENR */
+#ifndef GRN_QUERY_QUOTEL
+#define GRN_QUERY_QUOTEL '"'
+#endif /* GRN_QUERY_QUOTEL */
+#ifndef GRN_QUERY_QUOTER
+#define GRN_QUERY_QUOTER '"'
+#endif /* GRN_QUERY_QUOTER */
+#ifndef GRN_QUERY_ESCAPE
+#define GRN_QUERY_ESCAPE '\\'
+#endif /* GRN_QUERY_ESCAPE */
+#ifndef GRN_QUERY_COLUMN
+#define GRN_QUERY_COLUMN ':'
+#endif /* GRN_QUERY_COLUMN */
+
+typedef struct _grn_snip_mapping grn_snip_mapping;
+
+struct _grn_snip_mapping {
+ void *dummy;
+};
+
+#define GRN_SNIP_NORMALIZE (0x01<<0)
+#define GRN_SNIP_COPY_TAG (0x01<<1)
+#define GRN_SNIP_SKIP_LEADING_SPACES (0x01<<2)
+
+#define GRN_SNIP_MAPPING_HTML_ESCAPE ((grn_snip_mapping *)-1)
+
+GRN_API grn_obj *grn_snip_open(grn_ctx *ctx, int flags, unsigned int width,
+ unsigned int max_results,
+ const char *defaultopentag, unsigned int defaultopentag_len,
+ const char *defaultclosetag, unsigned int defaultclosetag_len,
+ grn_snip_mapping *mapping);
+GRN_API grn_rc grn_snip_add_cond(grn_ctx *ctx, grn_obj *snip,
+ const char *keyword, unsigned int keyword_len,
+ const char *opentag, unsigned int opentag_len,
+ const char *closetag, unsigned int closetag_len);
+GRN_API grn_rc grn_snip_set_normalizer(grn_ctx *ctx, grn_obj *snip,
+ grn_obj *normalizer);
+GRN_API grn_obj *grn_snip_get_normalizer(grn_ctx *ctx, grn_obj *snip);
+GRN_API grn_rc grn_snip_exec(grn_ctx *ctx, grn_obj *snip,
+ const char *string, unsigned int string_len,
+ unsigned int *nresults, unsigned int *max_tagged_len);
+GRN_API grn_rc grn_snip_get_result(grn_ctx *ctx, grn_obj *snip, const unsigned int index,
+ char *result, unsigned int *result_len);
+
+/* log */
+
+#define GRN_LOG_TIME (0x01<<0)
+#define GRN_LOG_TITLE (0x01<<1)
+#define GRN_LOG_MESSAGE (0x01<<2)
+#define GRN_LOG_LOCATION (0x01<<3)
+
+/* Deprecated since 2.1.2. Use grn_logger instead. */
+typedef struct _grn_logger_info grn_logger_info;
+
+/* Deprecated since 2.1.2. Use grn_logger instead. */
+struct _grn_logger_info {
+ grn_log_level max_level;
+ int flags;
+ void (*func)(int, const char *, const char *, const char *, const char *, void *);
+ void *func_arg;
+};
+
+/* Deprecated since 2.1.2. Use grn_logger_set() instead. */
+GRN_API grn_rc grn_logger_info_set(grn_ctx *ctx, const grn_logger_info *info);
+
+typedef struct _grn_logger grn_logger;
+
+struct _grn_logger {
+ grn_log_level max_level;
+ int flags;
+ void *user_data;
+ void (*log)(grn_ctx *ctx, grn_log_level level,
+ const char *timestamp, const char *title, const char *message,
+ const char *location, void *user_data);
+ void (*reopen)(grn_ctx *ctx, void *user_data);
+ void (*fin)(grn_ctx *ctx, void *user_data);
+};
+
+GRN_API grn_rc grn_logger_set(grn_ctx *ctx, const grn_logger *logger);
+
+GRN_API void grn_logger_set_max_level(grn_ctx *ctx, grn_log_level max_level);
+GRN_API grn_log_level grn_logger_get_max_level(grn_ctx *ctx);
+
+#ifdef __GNUC__
+#define GRN_ATTRIBUTE_PRINTF(fmt_pos) \
+ __attribute__ ((format(printf, fmt_pos, fmt_pos + 1)))
+#else
+#define GRN_ATTRIBUTE_PRINTF(fmt_pos)
+#endif /* __GNUC__ */
+
+GRN_API void grn_logger_put(grn_ctx *ctx, grn_log_level level,
+ const char *file, int line, const char *func, const char *fmt, ...) GRN_ATTRIBUTE_PRINTF(6);
+GRN_API void grn_logger_reopen(grn_ctx *ctx);
+
+GRN_API grn_bool grn_logger_pass(grn_ctx *ctx, grn_log_level level);
+
+#ifndef GRN_LOG_DEFAULT_LEVEL
+#define GRN_LOG_DEFAULT_LEVEL GRN_LOG_NOTICE
+#endif /* GRN_LOG_DEFAULT_LEVEL */
+
+GRN_API void grn_default_logger_set_max_level(grn_log_level level);
+GRN_API grn_log_level grn_default_logger_get_max_level(void);
+GRN_API void grn_default_logger_set_path(const char *path);
+GRN_API const char *grn_default_logger_get_path(void);
+
+#define GRN_LOG(ctx,level,...) do {\
+ if (grn_logger_pass(ctx, level)) {\
+ grn_logger_put(ctx, (level), __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__); \
+ }\
+} while (0)
+
+typedef struct _grn_query_logger grn_query_logger;
+
+struct _grn_query_logger {
+ unsigned int flags;
+ void *user_data;
+ void (*log)(grn_ctx *ctx, unsigned int flag,
+ const char *timestamp, const char *info, const char *message,
+ void *user_data);
+ void (*reopen)(grn_ctx *ctx, void *user_data);
+ void (*fin)(grn_ctx *ctx, void *user_data);
+};
+
+GRN_API grn_rc grn_query_logger_set(grn_ctx *ctx, const grn_query_logger *logger);
+
+GRN_API void grn_query_logger_put(grn_ctx *ctx, unsigned int flag,
+ const char *mark,
+ const char *format, ...) GRN_ATTRIBUTE_PRINTF(4);
+GRN_API void grn_query_logger_reopen(grn_ctx *ctx);
+
+GRN_API grn_bool grn_query_logger_pass(grn_ctx *ctx, unsigned int flag);
+
+GRN_API void grn_default_query_logger_set_flags(unsigned int flags);
+GRN_API unsigned int grn_default_query_logger_get_flags(void);
+GRN_API void grn_default_query_logger_set_path(const char *path);
+GRN_API const char *grn_default_query_logger_get_path(void);
+
+#define GRN_QUERY_LOG(ctx, flag, mark, format, ...) do {\
+ if (grn_query_logger_pass(ctx, flag)) {\
+ grn_query_logger_put(ctx, (flag), (mark), format, __VA_ARGS__);\
+ }\
+} while (0)
+
+/* grn_bulk */
+
+#define GRN_BULK_BUFSIZE (sizeof(grn_obj) - sizeof(grn_obj_header))
+/* This assumes that GRN_BULK_BUFSIZE is less than 32 (= 0x20). */
+#define GRN_BULK_BUFSIZE_MAX 0x1f
+#define GRN_BULK_SIZE_IN_FLAGS(flags) ((flags) & GRN_BULK_BUFSIZE_MAX)
+#define GRN_BULK_OUTP(bulk) ((bulk)->header.impl_flags & GRN_OBJ_OUTPLACE)
+#define GRN_BULK_REWIND(bulk) do {\
+ if ((bulk)->header.type == GRN_VECTOR) {\
+ grn_obj *_body = (bulk)->u.v.body;\
+ if (_body) {\
+ if (GRN_BULK_OUTP(_body)) {\
+ (_body)->u.b.curr = (_body)->u.b.head;\
+ } else {\
+ (_body)->header.flags &= ~GRN_BULK_BUFSIZE_MAX;\
+ }\
+ }\
+ (bulk)->u.v.n_sections = 0;\
+ } else {\
+ if (GRN_BULK_OUTP(bulk)) {\
+ (bulk)->u.b.curr = (bulk)->u.b.head;\
+ } else {\
+ (bulk)->header.flags &= ~GRN_BULK_BUFSIZE_MAX;\
+ }\
+ }\
+} while (0)
+#define GRN_BULK_WSIZE(bulk) \
+ (GRN_BULK_OUTP(bulk)\
+ ? ((bulk)->u.b.tail - (bulk)->u.b.head)\
+ : GRN_BULK_BUFSIZE)
+#define GRN_BULK_REST(bulk) \
+ (GRN_BULK_OUTP(bulk)\
+ ? ((bulk)->u.b.tail - (bulk)->u.b.curr)\
+ : GRN_BULK_BUFSIZE - (bulk)->header.flags)
+#define GRN_BULK_VSIZE(bulk) \
+ (GRN_BULK_OUTP(bulk)\
+ ? ((bulk)->u.b.curr - (bulk)->u.b.head)\
+ : GRN_BULK_SIZE_IN_FLAGS((bulk)->header.flags))
+#define GRN_BULK_EMPTYP(bulk) \
+ (GRN_BULK_OUTP(bulk)\
+ ? ((bulk)->u.b.curr == (bulk)->u.b.head)\
+ : !(GRN_BULK_SIZE_IN_FLAGS((bulk)->header.flags)))
+#define GRN_BULK_HEAD(bulk) \
+ (GRN_BULK_OUTP(bulk)\
+ ? ((bulk)->u.b.head)\
+ : (char *)&((bulk)->u.b.head))
+#define GRN_BULK_CURR(bulk) \
+ (GRN_BULK_OUTP(bulk)\
+ ? ((bulk)->u.b.curr)\
+ : (char *)&((bulk)->u.b.head) + GRN_BULK_SIZE_IN_FLAGS((bulk)->header.flags))
+#define GRN_BULK_TAIL(bulk) \
+ (GRN_BULK_OUTP(bulk)\
+ ? ((bulk)->u.b.tail)\
+ : (char *)&((bulk)[1]))
+
+GRN_API grn_rc grn_bulk_reinit(grn_ctx *ctx, grn_obj *bulk, unsigned int size);
+GRN_API grn_rc grn_bulk_resize(grn_ctx *ctx, grn_obj *bulk, unsigned int newsize);
+GRN_API grn_rc grn_bulk_write(grn_ctx *ctx, grn_obj *bulk,
+ const char *str, unsigned int len);
+GRN_API grn_rc grn_bulk_write_from(grn_ctx *ctx, grn_obj *bulk,
+ const char *str, unsigned int from, unsigned int len);
+GRN_API grn_rc grn_bulk_reserve(grn_ctx *ctx, grn_obj *bulk, unsigned int len);
+GRN_API grn_rc grn_bulk_space(grn_ctx *ctx, grn_obj *bulk, unsigned int len);
+GRN_API grn_rc grn_bulk_truncate(grn_ctx *ctx, grn_obj *bulk, unsigned int len);
+GRN_API grn_rc grn_bulk_fin(grn_ctx *ctx, grn_obj *bulk);
+
+/* grn_text */
+
+GRN_API grn_rc grn_text_itoa(grn_ctx *ctx, grn_obj *bulk, int i);
+GRN_API grn_rc grn_text_itoa_padded(grn_ctx *ctx, grn_obj *bulk, int i, char ch, unsigned int len);
+GRN_API grn_rc grn_text_lltoa(grn_ctx *ctx, grn_obj *bulk, long long int i);
+GRN_API grn_rc grn_text_ftoa(grn_ctx *ctx, grn_obj *bulk, double d);
+GRN_API grn_rc grn_text_itoh(grn_ctx *ctx, grn_obj *bulk, int i, unsigned int len);
+GRN_API grn_rc grn_text_itob(grn_ctx *ctx, grn_obj *bulk, grn_id id);
+GRN_API grn_rc grn_text_lltob32h(grn_ctx *ctx, grn_obj *bulk, long long int i);
+GRN_API grn_rc grn_text_benc(grn_ctx *ctx, grn_obj *bulk, unsigned int v);
+GRN_API grn_rc grn_text_esc(grn_ctx *ctx, grn_obj *bulk, const char *s, unsigned int len);
+GRN_API grn_rc grn_text_urlenc(grn_ctx *ctx, grn_obj *buf,
+ const char *str, unsigned int len);
+GRN_API const char *grn_text_urldec(grn_ctx *ctx, grn_obj *buf,
+ const char *s, const char *e, char d);
+GRN_API grn_rc grn_text_escape_xml(grn_ctx *ctx, grn_obj *buf,
+ const char *s, unsigned int len);
+GRN_API grn_rc grn_text_time2rfc1123(grn_ctx *ctx, grn_obj *bulk, int sec);
+GRN_API grn_rc grn_text_printf(grn_ctx *ctx, grn_obj *bulk,
+ const char *format, ...) GRN_ATTRIBUTE_PRINTF(3);
+GRN_API grn_rc grn_text_vprintf(grn_ctx *ctx, grn_obj *bulk,
+ const char *format, va_list args);
+
+typedef struct _grn_obj_format grn_obj_format;
+
+#define GRN_OBJ_FORMAT_WITH_COLUMN_NAMES (0x01<<0)
+#define GRN_OBJ_FORMAT_AS_ARRAY (0x01<<3)
+/* Deprecated since 4.0.1. It will be removed at 5.0.0.
+ Use GRN_OBJ_FORMAT_AS_ARRAY instead.*/
+#define GRN_OBJ_FORMAT_ASARRAY GRN_OBJ_FORMAT_AS_ARRAY
+#define GRN_OBJ_FORMAT_WITH_WEIGHT (0x01<<4)
+
+struct _grn_obj_format {
+ grn_obj columns;
+ const void *min;
+ const void *max;
+ unsigned int min_size;
+ unsigned int max_size;
+ int nhits;
+ int offset;
+ int limit;
+ int hits_offset;
+ int flags;
+ grn_obj *expression;
+};
+
+#define GRN_OBJ_FORMAT_INIT(format,format_nhits,format_offset,format_limit,format_hits_offset) do { \
+ GRN_PTR_INIT(&(format)->columns, GRN_OBJ_VECTOR, GRN_ID_NIL);\
+ (format)->nhits = (format_nhits);\
+ (format)->offset = (format_offset);\
+ (format)->limit = (format_limit);\
+ (format)->hits_offset = (format_hits_offset);\
+ (format)->flags = 0;\
+ (format)->expression = NULL;\
+} while (0)
+
+#define GRN_OBJ_FORMAT_FIN(ctx,format) do {\
+ int ncolumns = GRN_BULK_VSIZE(&(format)->columns) / sizeof(grn_obj *);\
+ grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&(format)->columns);\
+ while (ncolumns--) { grn_obj_unlink((ctx), *columns++); }\
+ GRN_OBJ_FIN((ctx), &(format)->columns);\
+ if ((format)->expression) { GRN_OBJ_FIN((ctx), (format)->expression); } \
+} while (0)
+
+GRN_API void grn_output_obj(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ grn_obj *obj, grn_obj_format *format);
+GRN_API void grn_output_envelope(grn_ctx *ctx, grn_rc rc,
+ grn_obj *head, grn_obj *body, grn_obj *foot,
+ const char *file, int line);
+
+GRN_API void grn_ctx_output_array_open(grn_ctx *ctx,
+ const char *name, int nelements);
+GRN_API void grn_ctx_output_array_close(grn_ctx *ctx);
+GRN_API void grn_ctx_output_map_open(grn_ctx *ctx,
+ const char *name, int nelements);
+GRN_API void grn_ctx_output_map_close(grn_ctx *ctx);
+GRN_API void grn_ctx_output_int32(grn_ctx *ctx, int value);
+GRN_API void grn_ctx_output_int64(grn_ctx *ctx, long long int value);
+GRN_API void grn_ctx_output_float(grn_ctx *ctx, double value);
+GRN_API void grn_ctx_output_cstr(grn_ctx *ctx, const char *value);
+GRN_API void grn_ctx_output_str(grn_ctx *ctx,
+ const char *value, unsigned int value_len);
+GRN_API void grn_ctx_output_bool(grn_ctx *ctx, grn_bool value);
+GRN_API void grn_ctx_output_obj(grn_ctx *ctx,
+ grn_obj *value, grn_obj_format *format);
+
+
+GRN_API const char *grn_ctx_get_mime_type(grn_ctx *ctx);
+GRN_API void grn_ctx_recv_handler_set(grn_ctx *,
+ void (*func)(grn_ctx *, int, void *),
+ void *func_arg);
+
+/* obsolete */
+GRN_API grn_rc grn_text_otoj(grn_ctx *ctx, grn_obj *bulk, grn_obj *obj,
+ grn_obj_format *format);
+
+/* various values exchanged via grn_obj */
+
+#define GRN_OBJ_DO_SHALLOW_COPY (GRN_OBJ_REFER|GRN_OBJ_OUTPLACE)
+#define GRN_OBJ_VECTOR (0x01<<7)
+
+#define GRN_OBJ_MUTABLE(obj) ((obj) && (obj)->header.type <= GRN_VECTOR)
+
+#define GRN_VALUE_FIX_SIZE_INIT(obj,flags,domain)\
+ GRN_OBJ_INIT((obj), ((flags) & GRN_OBJ_VECTOR) ? GRN_UVECTOR : GRN_BULK,\
+ ((flags) & GRN_OBJ_DO_SHALLOW_COPY), (domain))
+#define GRN_VALUE_VAR_SIZE_INIT(obj,flags,domain)\
+ GRN_OBJ_INIT((obj), ((flags) & GRN_OBJ_VECTOR) ? GRN_VECTOR : GRN_BULK,\
+ ((flags) & GRN_OBJ_DO_SHALLOW_COPY), (domain))
+
+#define GRN_VOID_INIT(obj) GRN_OBJ_INIT((obj), GRN_VOID, 0, GRN_DB_VOID)
+#define GRN_TEXT_INIT(obj,flags) \
+ GRN_VALUE_VAR_SIZE_INIT(obj, flags, GRN_DB_TEXT)
+#define GRN_SHORT_TEXT_INIT(obj,flags) \
+ GRN_VALUE_VAR_SIZE_INIT(obj, flags, GRN_DB_SHORT_TEXT)
+#define GRN_LONG_TEXT_INIT(obj,flags) \
+ GRN_VALUE_VAR_SIZE_INIT(obj, flags, GRN_DB_LONG_TEXT)
+#define GRN_TEXT_SET_REF(obj,str,len) do {\
+ (obj)->u.b.head = (char *)(str);\
+ (obj)->u.b.curr = (char *)(str) + (len);\
+} while (0)
+#define GRN_TEXT_SET(ctx,obj,str,len) do {\
+ if ((obj)->header.impl_flags & GRN_OBJ_REFER) {\
+ GRN_TEXT_SET_REF((obj), (str), (len));\
+ } else {\
+ grn_bulk_write_from((ctx), (obj), (const char *)(str), 0, (unsigned int)(len));\
+ }\
+} while (0)
+#define GRN_TEXT_PUT(ctx,obj,str,len) \
+ grn_bulk_write((ctx), (obj), (const char *)(str), (unsigned int)(len))
+#define GRN_TEXT_PUTC(ctx,obj,c) do {\
+ char _c = (c); grn_bulk_write((ctx), (obj), &_c, 1);\
+} while (0)
+
+#define GRN_TEXT_PUTS(ctx,obj,str) GRN_TEXT_PUT((ctx), (obj), (str), strlen(str))
+#define GRN_TEXT_SETS(ctx,obj,str) GRN_TEXT_SET((ctx), (obj), (str), strlen(str))
+#define GRN_TEXT_VALUE(obj) GRN_BULK_HEAD(obj)
+#define GRN_TEXT_LEN(obj) GRN_BULK_VSIZE(obj)
+
+#define GRN_BOOL_INIT(obj,flags) \
+ GRN_VALUE_FIX_SIZE_INIT(obj, flags, GRN_DB_BOOL)
+#define GRN_INT8_INIT(obj,flags) \
+ GRN_VALUE_FIX_SIZE_INIT(obj, flags, GRN_DB_INT8)
+#define GRN_UINT8_INIT(obj,flags) \
+ GRN_VALUE_FIX_SIZE_INIT(obj, flags, GRN_DB_UINT8)
+#define GRN_INT16_INIT(obj,flags) \
+ GRN_VALUE_FIX_SIZE_INIT(obj, flags, GRN_DB_INT16)
+#define GRN_UINT16_INIT(obj,flags) \
+ GRN_VALUE_FIX_SIZE_INIT(obj, flags, GRN_DB_UINT16)
+#define GRN_INT32_INIT(obj,flags) \
+ GRN_VALUE_FIX_SIZE_INIT(obj, flags, GRN_DB_INT32)
+#define GRN_UINT32_INIT(obj,flags) \
+ GRN_VALUE_FIX_SIZE_INIT(obj, flags, GRN_DB_UINT32)
+#define GRN_INT64_INIT(obj,flags) \
+ GRN_VALUE_FIX_SIZE_INIT(obj, flags, GRN_DB_INT64)
+#define GRN_UINT64_INIT(obj,flags) \
+ GRN_VALUE_FIX_SIZE_INIT(obj, flags, GRN_DB_UINT64)
+#define GRN_FLOAT_INIT(obj,flags) \
+ GRN_VALUE_FIX_SIZE_INIT(obj, flags, GRN_DB_FLOAT)
+#define GRN_TIME_INIT(obj,flags) \
+ GRN_VALUE_FIX_SIZE_INIT(obj, flags, GRN_DB_TIME)
+#define GRN_RECORD_INIT GRN_VALUE_FIX_SIZE_INIT
+#define GRN_PTR_INIT(obj,flags,domain)\
+ GRN_OBJ_INIT((obj), ((flags) & GRN_OBJ_VECTOR) ? GRN_PVECTOR : GRN_PTR,\
+ ((flags) & GRN_OBJ_DO_SHALLOW_COPY), (domain))
+#define GRN_TOKYO_GEO_POINT_INIT(obj,flags) \
+ GRN_VALUE_FIX_SIZE_INIT(obj, flags, GRN_DB_TOKYO_GEO_POINT)
+#define GRN_WGS84_GEO_POINT_INIT(obj,flags) \
+ GRN_VALUE_FIX_SIZE_INIT(obj, flags, GRN_DB_WGS84_GEO_POINT)
+
+#define GRN_BOOL_SET(ctx,obj,val) do {\
+ unsigned char _val = (unsigned char)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val, 0, sizeof(unsigned char));\
+} while (0)
+#define GRN_INT8_SET(ctx,obj,val) do {\
+ signed char _val = (signed char)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val, 0, sizeof(signed char));\
+} while (0)
+#define GRN_UINT8_SET(ctx,obj,val) do {\
+ unsigned char _val = (unsigned char)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val, 0, sizeof(unsigned char));\
+} while (0)
+#define GRN_INT16_SET(ctx,obj,val) do {\
+ signed short _val = (signed short)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val, 0, sizeof(signed short));\
+} while (0)
+#define GRN_UINT16_SET(ctx,obj,val) do {\
+ unsigned short _val = (unsigned short)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val, 0, sizeof(unsigned short));\
+} while (0)
+#define GRN_INT32_SET(ctx,obj,val) do {\
+ int _val = (int)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val, 0, sizeof(int));\
+} while (0)
+#define GRN_UINT32_SET(ctx,obj,val) do {\
+ unsigned int _val = (unsigned int)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val, 0, sizeof(unsigned int));\
+} while (0)
+#define GRN_INT64_SET(ctx,obj,val) do {\
+ long long int _val = (long long int)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val, 0, sizeof(long long int));\
+} while (0)
+#define GRN_UINT64_SET(ctx,obj,val) do {\
+ long long unsigned int _val = (long long unsigned int)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val, 0, sizeof(long long unsigned int));\
+} while (0)
+#define GRN_FLOAT_SET(ctx,obj,val) do {\
+ double _val = (double)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val, 0, sizeof(double));\
+} while (0)
+#define GRN_TIME_SET GRN_INT64_SET
+#define GRN_RECORD_SET(ctx,obj,val) do {\
+ grn_id _val = (grn_id)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val, 0, sizeof(grn_id));\
+} while (0)
+#define GRN_PTR_SET(ctx,obj,val) do {\
+ grn_obj *_val = (grn_obj *)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val, 0, sizeof(grn_obj *));\
+} while (0)
+
+#define GRN_GEO_DEGREE2MSEC(degree)\
+ ((int)((degree) * 3600 * 1000 + ((degree) > 0 ? 0.5 : -0.5)))
+#define GRN_GEO_MSEC2DEGREE(msec)\
+ ((((int)(msec)) / 3600.0) * 0.001)
+
+#define GRN_GEO_POINT_SET(ctx,obj,_latitude,_longitude) do {\
+ grn_geo_point _val;\
+ _val.latitude = (int)(_latitude);\
+ _val.longitude = (int)(_longitude);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val, 0, sizeof(grn_geo_point));\
+} while (0)
+
+#define GRN_BOOL_SET_AT(ctx,obj,offset,val) do {\
+ unsigned char _val = (unsigned char)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val,\
+ (offset), sizeof(unsigned char));\
+} while (0)
+#define GRN_INT8_SET_AT(ctx,obj,offset,val) do {\
+ signed char _val = (signed char)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val,\
+ (offset) * sizeof(signed char), sizeof(signed char));\
+} while (0)
+#define GRN_UINT8_SET_AT(ctx,obj,offset,val) do { \
+ unsigned char _val = (unsigned char)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val,\
+ (offset) * sizeof(unsigned char), sizeof(unsigned char));\
+} while (0)
+#define GRN_INT16_SET_AT(ctx,obj,offset,val) do {\
+ signed short _val = (signed short)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val,\
+ (offset) * sizeof(signed short), sizeof(signed short));\
+} while (0)
+#define GRN_UINT16_SET_AT(ctx,obj,offset,val) do { \
+ unsigned short _val = (unsigned short)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val,\
+ (offset) * sizeof(unsigned short), sizeof(unsigned short));\
+} while (0)
+#define GRN_INT32_SET_AT(ctx,obj,offset,val) do {\
+ int _val = (int)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val,\
+ (offset) * sizeof(int), sizeof(int));\
+} while (0)
+#define GRN_UINT32_SET_AT(ctx,obj,offset,val) do { \
+ unsigned int _val = (unsigned int)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val,\
+ (offset) * sizeof(unsigned int), sizeof(unsigned int));\
+} while (0)
+#define GRN_INT64_SET_AT(ctx,obj,offset,val) do {\
+ long long int _val = (long long int)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val,\
+ (offset) * sizeof(long long int), sizeof(long long int));\
+} while (0)
+#define GRN_UINT64_SET_AT(ctx,obj,offset,val) do {\
+ long long unsigned int _val = (long long unsigned int)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val,\
+ (offset) * sizeof(long long unsigned int),\
+ sizeof(long long unsigned int));\
+} while (0)
+#define GRN_FLOAT_SET_AT(ctx,obj,offset,val) do {\
+ double _val = (double)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val,\
+ (offset) * sizeof(double), sizeof(double));\
+} while (0)
+#define GRN_TIME_SET_AT GRN_INT64_SET_AT
+#define GRN_RECORD_SET_AT(ctx,obj,offset,val) do {\
+ grn_id _val = (grn_id)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val,\
+ (offset) * sizeof(grn_id), sizeof(grn_id));\
+} while (0)
+#define GRN_PTR_SET_AT(ctx,obj,offset,val) do {\
+ grn_obj *_val = (grn_obj *)(val);\
+ grn_bulk_write_from((ctx), (obj), (char *)&_val,\
+ (offset) * sizeof(grn_obj *), sizeof(grn_obj *));\
+} while (0)
+
+#define GRN_TIME_USEC_PER_SEC 1000000
+#define GRN_TIME_PACK(sec, usec) ((long long int)(sec) * GRN_TIME_USEC_PER_SEC + (usec))
+#define GRN_TIME_UNPACK(time_value, sec, usec) do {\
+ sec = (time_value) / GRN_TIME_USEC_PER_SEC;\
+ usec = (time_value) % GRN_TIME_USEC_PER_SEC;\
+} while (0)
+
+GRN_API void grn_time_now(grn_ctx *ctx, grn_obj *obj);
+
+#define GRN_TIME_NOW(ctx,obj) (grn_time_now((ctx), (obj)))
+
+#define GRN_BOOL_VALUE(obj) (*((unsigned char *)GRN_BULK_HEAD(obj)))
+#define GRN_INT8_VALUE(obj) (*((signed char *)GRN_BULK_HEAD(obj)))
+#define GRN_UINT8_VALUE(obj) (*((unsigned char *)GRN_BULK_HEAD(obj)))
+#define GRN_INT16_VALUE(obj) (*((signed short *)GRN_BULK_HEAD(obj)))
+#define GRN_UINT16_VALUE(obj) (*((unsigned short *)GRN_BULK_HEAD(obj)))
+#define GRN_INT32_VALUE(obj) (*((int *)GRN_BULK_HEAD(obj)))
+#define GRN_UINT32_VALUE(obj) (*((unsigned int *)GRN_BULK_HEAD(obj)))
+#define GRN_INT64_VALUE(obj) (*((long long int *)GRN_BULK_HEAD(obj)))
+#define GRN_UINT64_VALUE(obj) (*((long long unsigned int *)GRN_BULK_HEAD(obj)))
+#define GRN_FLOAT_VALUE(obj) (*((double *)GRN_BULK_HEAD(obj)))
+#define GRN_TIME_VALUE GRN_INT64_VALUE
+#define GRN_RECORD_VALUE(obj) (*((grn_id *)GRN_BULK_HEAD(obj)))
+#define GRN_PTR_VALUE(obj) (*((grn_obj **)GRN_BULK_HEAD(obj)))
+#define GRN_GEO_POINT_VALUE(obj,_latitude,_longitude) do {\
+ grn_geo_point *_val = (grn_geo_point *)GRN_BULK_HEAD(obj);\
+ _latitude = _val->latitude;\
+ _longitude = _val->longitude;\
+} while (0)
+
+#define GRN_BOOL_VALUE_AT(obj,offset) (((unsigned char *)GRN_BULK_HEAD(obj))[offset])
+#define GRN_INT8_VALUE_AT(obj,offset) (((signed char *)GRN_BULK_HEAD(obj))[offset])
+#define GRN_UINT8_VALUE_AT(obj,offset) (((unsigned char *)GRN_BULK_HEAD(obj))[offset])
+#define GRN_INT16_VALUE_AT(obj,offset) (((signed short *)GRN_BULK_HEAD(obj))[offset])
+#define GRN_UINT16_VALUE_AT(obj,offset) (((unsigned short *)GRN_BULK_HEAD(obj))[offset])
+#define GRN_INT32_VALUE_AT(obj,offset) (((int *)GRN_BULK_HEAD(obj))[offset])
+#define GRN_UINT32_VALUE_AT(obj,offset) (((unsigned int *)GRN_BULK_HEAD(obj))[offset])
+#define GRN_INT64_VALUE_AT(obj,offset) (((long long int *)GRN_BULK_HEAD(obj))[offset])
+#define GRN_UINT64_VALUE_AT(obj,offset) (((long long unsigned int *)GRN_BULK_HEAD(obj))[offset])
+#define GRN_FLOAT_VALUE_AT(obj,offset) (((double *)GRN_BULK_HEAD(obj))[offset])
+#define GRN_TIME_VALUE_AT GRN_INT64_VALUE_AT
+#define GRN_RECORD_VALUE_AT(obj,offset) (((grn_id *)GRN_BULK_HEAD(obj))[offset])
+#define GRN_PTR_VALUE_AT(obj,offset) (((grn_obj **)GRN_BULK_HEAD(obj))[offset])
+
+#define GRN_BOOL_PUT(ctx,obj,val) do {\
+ unsigned char _val = (unsigned char)(val);\
+ grn_bulk_write((ctx), (obj), (char *)&_val, sizeof(unsigned char));\
+} while (0)
+#define GRN_INT8_PUT(ctx,obj,val) do {\
+ signed char _val = (signed char)(val); grn_bulk_write((ctx), (obj), (char *)&_val, sizeof(signed char));\
+} while (0)
+#define GRN_UINT8_PUT(ctx,obj,val) do {\
+ unsigned char _val = (unsigned char)(val);\
+ grn_bulk_write((ctx), (obj), (char *)&_val, sizeof(unsigned char));\
+} while (0)
+#define GRN_INT16_PUT(ctx,obj,val) do {\
+ signed short _val = (signed short)(val); grn_bulk_write((ctx), (obj), (char *)&_val, sizeof(signed short));\
+} while (0)
+#define GRN_UINT16_PUT(ctx,obj,val) do {\
+ unsigned short _val = (unsigned short)(val);\
+ grn_bulk_write((ctx), (obj), (char *)&_val, sizeof(unsigned short));\
+} while (0)
+#define GRN_INT32_PUT(ctx,obj,val) do {\
+ int _val = (int)(val); grn_bulk_write((ctx), (obj), (char *)&_val, sizeof(int));\
+} while (0)
+#define GRN_UINT32_PUT(ctx,obj,val) do {\
+ unsigned int _val = (unsigned int)(val);\
+ grn_bulk_write((ctx), (obj), (char *)&_val, sizeof(unsigned int));\
+} while (0)
+#define GRN_INT64_PUT(ctx,obj,val) do {\
+ long long int _val = (long long int)(val);\
+ grn_bulk_write((ctx), (obj), (char *)&_val, sizeof(long long int));\
+} while (0)
+#define GRN_UINT64_PUT(ctx,obj,val) do {\
+ long long unsigned int _val = (long long unsigned int)(val);\
+ grn_bulk_write((ctx), (obj), (char *)&_val, sizeof(long long unsigned int));\
+} while (0)
+#define GRN_FLOAT_PUT(ctx,obj,val) do {\
+ double _val = (double)(val); grn_bulk_write((ctx), (obj), (char *)&_val, sizeof(double));\
+} while (0)
+#define GRN_TIME_PUT GRN_INT64_PUT
+#define GRN_RECORD_PUT(ctx,obj,val) do {\
+ grn_id _val = (grn_id)(val); grn_bulk_write((ctx), (obj), (char *)&_val, sizeof(grn_id));\
+} while (0)
+#define GRN_PTR_PUT(ctx,obj,val) do {\
+ grn_obj *_val = (grn_obj *)(val);\
+ grn_bulk_write((ctx), (obj), (char *)&_val, sizeof(grn_obj *));\
+} while (0)
+
+/* grn_str: deprecated. use grn_string instead. */
+
+typedef struct {
+ const char *orig;
+ char *norm;
+ short *checks;
+ unsigned char *ctypes;
+ int flags;
+ unsigned int orig_blen;
+ unsigned int norm_blen;
+ unsigned int length;
+ grn_encoding encoding;
+} grn_str;
+
+#define GRN_STR_REMOVEBLANK (0x01<<0)
+#define GRN_STR_WITH_CTYPES (0x01<<1)
+#define GRN_STR_WITH_CHECKS (0x01<<2)
+#define GRN_STR_NORMALIZE GRN_OBJ_KEY_NORMALIZE
+
+GRN_API grn_str *grn_str_open(grn_ctx *ctx, const char *str, unsigned int str_len,
+ int flags);
+GRN_API grn_rc grn_str_close(grn_ctx *ctx, grn_str *nstr);
+
+/* grn_string */
+
+#define GRN_STRING_REMOVE_BLANK (0x01<<0)
+#define GRN_STRING_WITH_TYPES (0x01<<1)
+#define GRN_STRING_WITH_CHECKS (0x01<<2)
+#define GRN_STRING_REMOVE_TOKENIZED_DELIMITER (0x01<<3)
+
+#define GRN_NORMALIZER_AUTO ((grn_obj *)1)
+
+#define GRN_CHAR_BLANK 0x80
+#define GRN_CHAR_IS_BLANK(c) ((c) & (GRN_CHAR_BLANK))
+#define GRN_CHAR_TYPE(c) ((c) & 0x7f)
+
+typedef enum {
+ GRN_CHAR_NULL = 0,
+ GRN_CHAR_ALPHA,
+ GRN_CHAR_DIGIT,
+ GRN_CHAR_SYMBOL,
+ GRN_CHAR_HIRAGANA,
+ GRN_CHAR_KATAKANA,
+ GRN_CHAR_KANJI,
+ GRN_CHAR_OTHERS
+} grn_char_type;
+
+GRN_API grn_obj *grn_string_open(grn_ctx *ctx,
+ const char *string,
+ unsigned int length_in_bytes,
+ grn_obj *normalizer, int flags);
+GRN_API grn_rc grn_string_get_original(grn_ctx *ctx, grn_obj *string,
+ const char **original,
+ unsigned int *length_in_bytes);
+GRN_API int grn_string_get_flags(grn_ctx *ctx, grn_obj *string);
+GRN_API grn_rc grn_string_get_normalized(grn_ctx *ctx, grn_obj *string,
+ const char **normalized,
+ unsigned int *length_in_bytes,
+ unsigned int *n_characters);
+GRN_API grn_rc grn_string_set_normalized(grn_ctx *ctx, grn_obj *string,
+ char *normalized,
+ unsigned int length_in_bytes,
+ unsigned int n_characters);
+GRN_API const short *grn_string_get_checks(grn_ctx *ctx, grn_obj *string);
+GRN_API grn_rc grn_string_set_checks(grn_ctx *ctx,
+ grn_obj *string,
+ short *checks);
+GRN_API const unsigned char *grn_string_get_types(grn_ctx *ctx, grn_obj *string);
+GRN_API grn_rc grn_string_set_types(grn_ctx *ctx,
+ grn_obj *string,
+ unsigned char *types);
+GRN_API grn_encoding grn_string_get_encoding(grn_ctx *ctx, grn_obj *string);
+
+
+GRN_API int grn_charlen(grn_ctx *ctx, const char *str, const char *end);
+
+/* expr */
+
+GRN_API grn_obj *grn_expr_create(grn_ctx *ctx, const char *name, unsigned int name_size);
+GRN_API grn_rc grn_expr_close(grn_ctx *ctx, grn_obj *expr);
+GRN_API grn_obj *grn_expr_add_var(grn_ctx *ctx, grn_obj *expr,
+ const char *name, unsigned int name_size);
+GRN_API grn_obj *grn_expr_get_var(grn_ctx *ctx, grn_obj *expr,
+ const char *name, unsigned int name_size);
+GRN_API grn_obj *grn_expr_get_var_by_offset(grn_ctx *ctx, grn_obj *expr, unsigned int offset);
+
+GRN_API grn_obj *grn_expr_append_obj(grn_ctx *ctx, grn_obj *expr, grn_obj *obj,
+ grn_operator op, int nargs);
+GRN_API grn_obj *grn_expr_append_const(grn_ctx *ctx, grn_obj *expr, grn_obj *obj,
+ grn_operator op, int nargs);
+GRN_API grn_obj *grn_expr_append_const_str(grn_ctx *ctx, grn_obj *expr,
+ const char *str, unsigned int str_size,
+ grn_operator op, int nargs);
+GRN_API grn_obj *grn_expr_append_const_int(grn_ctx *ctx, grn_obj *expr, int i,
+ grn_operator op, int nargs);
+GRN_API grn_rc grn_expr_append_op(grn_ctx *ctx, grn_obj *expr, grn_operator op, int nargs);
+
+GRN_API grn_rc grn_expr_syntax_escape(grn_ctx *ctx,
+ const char *query, int query_size,
+ const char *target_characters,
+ char escape_character,
+ grn_obj *escaped_query);
+GRN_API grn_rc grn_expr_syntax_escape_query(grn_ctx *ctx,
+ const char *query, int query_size,
+ grn_obj *escaped_query);
+
+GRN_API grn_rc grn_expr_compile(grn_ctx *ctx, grn_obj *expr);
+GRN_API grn_obj *grn_expr_exec(grn_ctx *ctx, grn_obj *expr, int nargs);
+GRN_API grn_rc grn_ctx_push(grn_ctx *ctx, grn_obj *obj);
+GRN_API grn_obj *grn_ctx_pop(grn_ctx *ctx);
+
+GRN_API grn_obj *grn_expr_alloc(grn_ctx *ctx, grn_obj *expr,
+ grn_id domain, grn_obj_flags flags);
+
+GRN_API grn_obj *grn_table_select(grn_ctx *ctx, grn_obj *table, grn_obj *expr,
+ grn_obj *res, grn_operator op);
+
+GRN_API int grn_obj_columns(grn_ctx *ctx, grn_obj *table,
+ const char *str, unsigned int str_size, grn_obj *res);
+
+#define GRN_EXPR_CREATE_FOR_QUERY(ctx,table,expr,var) do {\
+ if (((expr) = grn_expr_create((ctx), NULL, 0)) &&\
+ ((var) = grn_expr_add_var((ctx), (expr), NULL, 0))) {\
+ GRN_RECORD_INIT((var), 0, grn_obj_id((ctx), (table)));\
+ } else {\
+ (var) = NULL;\
+ }\
+} while (0)
+
+typedef unsigned int grn_expr_flags;
+
+#define GRN_EXPR_SYNTAX_QUERY (0x00)
+#define GRN_EXPR_SYNTAX_SCRIPT (0x01)
+#define GRN_EXPR_SYNTAX_OUTPUT_COLUMNS (0x20)
+#define GRN_EXPR_SYNTAX_ADJUSTER (0x40)
+#define GRN_EXPR_ALLOW_PRAGMA (0x02)
+#define GRN_EXPR_ALLOW_COLUMN (0x04)
+#define GRN_EXPR_ALLOW_UPDATE (0x08)
+#define GRN_EXPR_ALLOW_LEADING_NOT (0x10)
+
+GRN_API grn_rc grn_expr_parse(grn_ctx *ctx, grn_obj *expr,
+ const char *str, unsigned int str_size,
+ grn_obj *default_column, grn_operator default_mode,
+ grn_operator default_op, grn_expr_flags flags);
+
+GRN_API grn_obj *grn_expr_snip(grn_ctx *ctx, grn_obj *expr, int flags,
+ unsigned int width, unsigned int max_results,
+ unsigned int n_tags,
+ const char **opentags, unsigned int *opentag_lens,
+ const char **closetags, unsigned int *closetag_lens,
+ grn_snip_mapping *mapping);
+GRN_API grn_rc grn_expr_snip_add_conditions(grn_ctx *ctx,
+ grn_obj *expr,
+ grn_obj *snip,
+ unsigned int n_tags,
+ const char **opentags,
+ unsigned int *opentag_lens,
+ const char **closetags,
+ unsigned int *closetag_lens);
+
+GRN_API grn_table_sort_key *grn_table_sort_key_from_str(grn_ctx *ctx,
+ const char *str, unsigned int str_size,
+ grn_obj *table, unsigned int *nkeys);
+GRN_API grn_rc grn_table_sort_key_close(grn_ctx *ctx,
+ grn_table_sort_key *keys, unsigned int nkeys);
+
+GRN_API grn_bool grn_table_is_grouped(grn_ctx *ctx, grn_obj *table);
+
+GRN_API unsigned int grn_table_max_n_subrecs(grn_ctx *ctx, grn_obj *table);
+
+GRN_API grn_obj *grn_table_create_for_group(grn_ctx *ctx,
+ const char *name,
+ unsigned int name_size,
+ const char *path,
+ grn_obj *group_key,
+ grn_obj *value_type,
+ unsigned int max_n_subrecs);
+
+GRN_API unsigned int grn_table_get_subrecs(grn_ctx *ctx, grn_obj *table,
+ grn_id id, grn_id *subrecbuf,
+ int *scorebuf, int buf_size);
+
+GRN_API grn_obj *grn_table_tokenize(grn_ctx *ctx, grn_obj *table,
+ const char *str, unsigned int str_len,
+ grn_obj *buf, grn_bool addp);
+
+GRN_API grn_rc grn_load(grn_ctx *ctx, grn_content_type input_type,
+ const char *table, unsigned int table_len,
+ const char *columns, unsigned int columns_len,
+ const char *values, unsigned int values_len,
+ const char *ifexists, unsigned int ifexists_len,
+ const char *each, unsigned int each_len);
+
+#define GRN_CTX_MORE (0x01<<0)
+#define GRN_CTX_TAIL (0x01<<1)
+#define GRN_CTX_HEAD (0x01<<2)
+#define GRN_CTX_QUIET (0x01<<3)
+#define GRN_CTX_QUIT (0x01<<4)
+
+GRN_API grn_rc grn_ctx_connect(grn_ctx *ctx, const char *host, int port, int flags);
+GRN_API unsigned int grn_ctx_send(grn_ctx *ctx, const char *str, unsigned int str_len, int flags);
+GRN_API unsigned int grn_ctx_recv(grn_ctx *ctx, char **str, unsigned int *str_len, int *flags);
+
+typedef struct _grn_ctx_info grn_ctx_info;
+
+struct _grn_ctx_info {
+ int fd;
+ unsigned int com_status;
+ grn_obj *outbuf;
+ unsigned char stat;
+};
+
+GRN_API grn_rc grn_ctx_info_get(grn_ctx *ctx, grn_ctx_info *info);
+
+GRN_API grn_rc grn_set_segv_handler(void);
+GRN_API grn_rc grn_set_int_handler(void);
+GRN_API grn_rc grn_set_term_handler(void);
+
+/* hash */
+
+typedef struct _grn_hash grn_hash;
+typedef struct _grn_hash_cursor grn_hash_cursor;
+
+GRN_API grn_hash *grn_hash_create(grn_ctx *ctx, const char *path, unsigned int key_size,
+ unsigned int value_size, unsigned int flags);
+
+GRN_API grn_hash *grn_hash_open(grn_ctx *ctx, const char *path);
+
+GRN_API grn_rc grn_hash_close(grn_ctx *ctx, grn_hash *hash);
+
+GRN_API grn_id grn_hash_add(grn_ctx *ctx, grn_hash *hash, const void *key,
+ unsigned int key_size, void **value, int *added);
+GRN_API grn_id grn_hash_get(grn_ctx *ctx, grn_hash *hash, const void *key,
+ unsigned int key_size, void **value);
+
+GRN_API int grn_hash_get_key(grn_ctx *ctx, grn_hash *hash, grn_id id, void *keybuf, int bufsize);
+GRN_API int grn_hash_get_key2(grn_ctx *ctx, grn_hash *hash, grn_id id, grn_obj *bulk);
+GRN_API int grn_hash_get_value(grn_ctx *ctx, grn_hash *hash, grn_id id, void *valuebuf);
+GRN_API grn_rc grn_hash_set_value(grn_ctx *ctx, grn_hash *hash, grn_id id,
+ const void *value, int flags);
+
+typedef struct _grn_table_delete_optarg grn_table_delete_optarg;
+
+struct _grn_table_delete_optarg {
+ int flags;
+ int (*func)(grn_ctx *ctx, grn_obj *, grn_id, void *);
+ void *func_arg;
+};
+
+GRN_API grn_rc grn_hash_delete_by_id(grn_ctx *ctx, grn_hash *hash, grn_id id,
+ grn_table_delete_optarg *optarg);
+GRN_API grn_rc grn_hash_delete(grn_ctx *ctx, grn_hash *hash,
+ const void *key, unsigned int key_size,
+ grn_table_delete_optarg *optarg);
+
+GRN_API grn_hash_cursor *grn_hash_cursor_open(grn_ctx *ctx, grn_hash *hash,
+ const void *min, unsigned int min_size,
+ const void *max, unsigned int max_size,
+ int offset, int limit, int flags);
+GRN_API grn_id grn_hash_cursor_next(grn_ctx *ctx, grn_hash_cursor *c);
+GRN_API void grn_hash_cursor_close(grn_ctx *ctx, grn_hash_cursor *c);
+
+GRN_API int grn_hash_cursor_get_key(grn_ctx *ctx, grn_hash_cursor *c, void **key);
+GRN_API int grn_hash_cursor_get_value(grn_ctx *ctx, grn_hash_cursor *c, void **value);
+GRN_API grn_rc grn_hash_cursor_set_value(grn_ctx *ctx, grn_hash_cursor *c,
+ const void *value, int flags);
+
+GRN_API int grn_hash_cursor_get_key_value(grn_ctx *ctx, grn_hash_cursor *c,
+ void **key, unsigned int *key_size, void **value);
+
+GRN_API grn_rc grn_hash_cursor_delete(grn_ctx *ctx, grn_hash_cursor *c,
+ grn_table_delete_optarg *optarg);
+
+#define GRN_HASH_EACH(ctx,hash,id,key,key_size,value,block) do {\
+ grn_hash_cursor *_sc = grn_hash_cursor_open(ctx, hash, NULL, 0, NULL, 0, 0, -1, 0); \
+ if (_sc) {\
+ grn_id id;\
+ while ((id = grn_hash_cursor_next(ctx, _sc))) {\
+ grn_hash_cursor_get_key_value(ctx, _sc, (void **)(key),\
+ (key_size), (void **)(value));\
+ block\
+ }\
+ grn_hash_cursor_close(ctx, _sc);\
+ }\
+} while (0)
+
+/* array */
+
+typedef struct _grn_array grn_array;
+typedef struct _grn_array_cursor grn_array_cursor;
+
+GRN_API grn_array *grn_array_create(grn_ctx *ctx, const char *path,
+ unsigned int value_size, unsigned int flags);
+GRN_API grn_array *grn_array_open(grn_ctx *ctx, const char *path);
+GRN_API grn_rc grn_array_close(grn_ctx *ctx, grn_array *array);
+GRN_API grn_id grn_array_add(grn_ctx *ctx, grn_array *array, void **value);
+GRN_API grn_id grn_array_push(grn_ctx *ctx, grn_array *array,
+ void (*func)(grn_ctx *ctx, grn_array *array,
+ grn_id id, void *func_arg),
+ void *func_arg);
+GRN_API grn_id grn_array_pull(grn_ctx *ctx, grn_array *array, grn_bool blockp,
+ void (*func)(grn_ctx *ctx, grn_array *array,
+ grn_id id, void *func_arg),
+ void *func_arg);
+GRN_API void grn_array_unblock(grn_ctx *ctx, grn_array *array);
+GRN_API int grn_array_get_value(grn_ctx *ctx, grn_array *array, grn_id id, void *valuebuf);
+GRN_API grn_rc grn_array_set_value(grn_ctx *ctx, grn_array *array, grn_id id,
+ const void *value, int flags);
+GRN_API grn_array_cursor *grn_array_cursor_open(grn_ctx *ctx, grn_array *array,
+ grn_id min, grn_id max,
+ int offset, int limit, int flags);
+GRN_API grn_id grn_array_cursor_next(grn_ctx *ctx, grn_array_cursor *cursor);
+GRN_API int grn_array_cursor_get_value(grn_ctx *ctx, grn_array_cursor *cursor, void **value);
+GRN_API grn_rc grn_array_cursor_set_value(grn_ctx *ctx, grn_array_cursor *cursor,
+ const void *value, int flags);
+GRN_API grn_rc grn_array_cursor_delete(grn_ctx *ctx, grn_array_cursor *cursor,
+ grn_table_delete_optarg *optarg);
+GRN_API void grn_array_cursor_close(grn_ctx *ctx, grn_array_cursor *cursor);
+GRN_API grn_rc grn_array_delete_by_id(grn_ctx *ctx, grn_array *array, grn_id id,
+ grn_table_delete_optarg *optarg);
+
+GRN_API grn_id grn_array_next(grn_ctx *ctx, grn_array *array, grn_id id);
+
+GRN_API void *_grn_array_get_value(grn_ctx *ctx, grn_array *array, grn_id id);
+
+#define GRN_ARRAY_EACH(ctx,array,head,tail,id,value,block) do {\
+ grn_array_cursor *_sc = grn_array_cursor_open(ctx, array, head, tail, 0, -1, 0); \
+ if (_sc) {\
+ grn_id id;\
+ while ((id = grn_array_cursor_next(ctx, _sc))) {\
+ grn_array_cursor_get_value(ctx, _sc, (void **)(value));\
+ block\
+ }\
+ grn_array_cursor_close(ctx, _sc); \
+ }\
+} while (0)
+
+/* pat */
+
+typedef struct _grn_pat grn_pat;
+typedef struct _grn_pat_cursor grn_pat_cursor;
+
+GRN_API grn_pat *grn_pat_create(grn_ctx *ctx, const char *path, unsigned int key_size,
+ unsigned int value_size, unsigned int flags);
+
+GRN_API grn_pat *grn_pat_open(grn_ctx *ctx, const char *path);
+
+GRN_API grn_rc grn_pat_close(grn_ctx *ctx, grn_pat *pat);
+
+GRN_API grn_rc grn_pat_remove(grn_ctx *ctx, const char *path);
+
+GRN_API grn_id grn_pat_get(grn_ctx *ctx, grn_pat *pat, const void *key,
+ unsigned int key_size, void **value);
+GRN_API grn_id grn_pat_add(grn_ctx *ctx, grn_pat *pat, const void *key,
+ unsigned int key_size, void **value, int *added);
+
+GRN_API int grn_pat_get_key(grn_ctx *ctx, grn_pat *pat, grn_id id, void *keybuf, int bufsize);
+GRN_API int grn_pat_get_key2(grn_ctx *ctx, grn_pat *pat, grn_id id, grn_obj *bulk);
+GRN_API int grn_pat_get_value(grn_ctx *ctx, grn_pat *pat, grn_id id, void *valuebuf);
+GRN_API grn_rc grn_pat_set_value(grn_ctx *ctx, grn_pat *pat, grn_id id,
+ const void *value, int flags);
+
+GRN_API grn_rc grn_pat_delete_by_id(grn_ctx *ctx, grn_pat *pat, grn_id id,
+ grn_table_delete_optarg *optarg);
+GRN_API grn_rc grn_pat_delete(grn_ctx *ctx, grn_pat *pat, const void *key, unsigned int key_size,
+ grn_table_delete_optarg *optarg);
+GRN_API int grn_pat_delete_with_sis(grn_ctx *ctx, grn_pat *pat, grn_id id,
+ grn_table_delete_optarg *optarg);
+
+typedef struct _grn_pat_scan_hit grn_pat_scan_hit;
+
+struct _grn_pat_scan_hit {
+ grn_id id;
+ unsigned int offset;
+ unsigned int length;
+};
+
+GRN_API int grn_pat_scan(grn_ctx *ctx, grn_pat *pat, const char *str, unsigned int str_len,
+ grn_pat_scan_hit *sh, unsigned int sh_size, const char **rest);
+
+GRN_API grn_rc grn_pat_prefix_search(grn_ctx *ctx, grn_pat *pat,
+ const void *key, unsigned int key_size, grn_hash *h);
+GRN_API grn_rc grn_pat_suffix_search(grn_ctx *ctx, grn_pat *pat,
+ const void *key, unsigned int key_size, grn_hash *h);
+GRN_API grn_id grn_pat_lcp_search(grn_ctx *ctx, grn_pat *pat,
+ const void *key, unsigned int key_size);
+
+GRN_API unsigned int grn_pat_size(grn_ctx *ctx, grn_pat *pat);
+
+GRN_API grn_pat_cursor *grn_pat_cursor_open(grn_ctx *ctx, grn_pat *pat,
+ const void *min, unsigned int min_size,
+ const void *max, unsigned int max_size,
+ int offset, int limit, int flags);
+GRN_API grn_id grn_pat_cursor_next(grn_ctx *ctx, grn_pat_cursor *c);
+GRN_API void grn_pat_cursor_close(grn_ctx *ctx, grn_pat_cursor *c);
+
+GRN_API int grn_pat_cursor_get_key(grn_ctx *ctx, grn_pat_cursor *c, void **key);
+GRN_API int grn_pat_cursor_get_value(grn_ctx *ctx, grn_pat_cursor *c, void **value);
+
+GRN_API int grn_pat_cursor_get_key_value(grn_ctx *ctx, grn_pat_cursor *c,
+ void **key, unsigned int *key_size, void **value);
+GRN_API grn_rc grn_pat_cursor_set_value(grn_ctx *ctx, grn_pat_cursor *c,
+ const void *value, int flags);
+GRN_API grn_rc grn_pat_cursor_delete(grn_ctx *ctx, grn_pat_cursor *c,
+ grn_table_delete_optarg *optarg);
+
+#define GRN_PAT_EACH(ctx,pat,id,key,key_size,value,block) do { \
+ grn_pat_cursor *_sc = grn_pat_cursor_open(ctx, pat, NULL, 0, NULL, 0, 0, -1, 0); \
+ if (_sc) {\
+ grn_id id;\
+ while ((id = grn_pat_cursor_next(ctx, _sc))) {\
+ grn_pat_cursor_get_key_value(ctx, _sc, (void **)(key),\
+ (key_size), (void **)(value));\
+ block\
+ }\
+ grn_pat_cursor_close(ctx, _sc);\
+ }\
+} while (0)
+
+/* dat */
+
+typedef struct _grn_dat grn_dat;
+typedef struct _grn_dat_cursor grn_dat_cursor;
+
+GRN_API grn_dat *grn_dat_create(grn_ctx *ctx, const char *path, unsigned int key_size,
+ unsigned int value_size, unsigned int flags);
+
+GRN_API grn_dat *grn_dat_open(grn_ctx *ctx, const char *path);
+
+GRN_API grn_rc grn_dat_close(grn_ctx *ctx, grn_dat *dat);
+
+GRN_API grn_rc grn_dat_remove(grn_ctx *ctx, const char *path);
+
+GRN_API grn_id grn_dat_get(grn_ctx *ctx, grn_dat *dat, const void *key,
+ unsigned int key_size, void **value);
+GRN_API grn_id grn_dat_add(grn_ctx *ctx, grn_dat *dat, const void *key,
+ unsigned int key_size, void **value, int *added);
+
+GRN_API int grn_dat_get_key(grn_ctx *ctx, grn_dat *dat, grn_id id, void *keybuf, int bufsize);
+GRN_API int grn_dat_get_key2(grn_ctx *ctx, grn_dat *dat, grn_id id, grn_obj *bulk);
+
+GRN_API grn_rc grn_dat_delete_by_id(grn_ctx *ctx, grn_dat *dat, grn_id id,
+ grn_table_delete_optarg *optarg);
+GRN_API grn_rc grn_dat_delete(grn_ctx *ctx, grn_dat *dat, const void *key, unsigned int key_size,
+ grn_table_delete_optarg *optarg);
+
+GRN_API grn_rc grn_dat_update_by_id(grn_ctx *ctx, grn_dat *dat, grn_id src_key_id,
+ const void *dest_key, unsigned int dest_key_size);
+GRN_API grn_rc grn_dat_update(grn_ctx *ctx, grn_dat *dat,
+ const void *src_key, unsigned int src_key_size,
+ const void *dest_key, unsigned int dest_key_size);
+
+GRN_API unsigned int grn_dat_size(grn_ctx *ctx, grn_dat *dat);
+
+GRN_API grn_dat_cursor *grn_dat_cursor_open(grn_ctx *ctx, grn_dat *dat,
+ const void *min, unsigned int min_size,
+ const void *max, unsigned int max_size,
+ int offset, int limit, int flags);
+GRN_API grn_id grn_dat_cursor_next(grn_ctx *ctx, grn_dat_cursor *c);
+GRN_API void grn_dat_cursor_close(grn_ctx *ctx, grn_dat_cursor *c);
+
+GRN_API int grn_dat_cursor_get_key(grn_ctx *ctx, grn_dat_cursor *c, const void **key);
+GRN_API grn_rc grn_dat_cursor_delete(grn_ctx *ctx, grn_dat_cursor *c,
+ grn_table_delete_optarg *optarg);
+
+#define GRN_DAT_EACH(ctx,dat,id,key,key_size,block) do {\
+ grn_dat_cursor *_sc = grn_dat_cursor_open(ctx, dat, NULL, 0, NULL, 0, 0, -1, 0);\
+ if (_sc) {\
+ grn_id id;\
+ unsigned int *_ks = (key_size);\
+ if (_ks) {\
+ while ((id = grn_dat_cursor_next(ctx, _sc))) {\
+ int _ks_raw = grn_dat_cursor_get_key(ctx, _sc, (const void **)(key));\
+ *(_ks) = (unsigned int)_ks_raw;\
+ block\
+ }\
+ } else {\
+ while ((id = grn_dat_cursor_next(ctx, _sc))) {\
+ grn_dat_cursor_get_key(ctx, _sc, (const void **)(key));\
+ block\
+ }\
+ }\
+ grn_dat_cursor_close(ctx, _sc);\
+ }\
+} while (0)
+
+/* buffered index builder */
+
+typedef struct _grn_ii grn_ii;
+typedef struct _grn_ii_buffer grn_ii_buffer;
+
+grn_ii_buffer *grn_ii_buffer_open(grn_ctx *ctx, grn_ii *ii,
+ long long unsigned int update_buffer_size);
+grn_rc grn_ii_buffer_append(grn_ctx *ctx, grn_ii_buffer *ii_buffer,
+ grn_id rid, unsigned int section, grn_obj *value);
+grn_rc grn_ii_buffer_commit(grn_ctx *ctx, grn_ii_buffer *ii_buffer);
+grn_rc grn_ii_buffer_close(grn_ctx *ctx, grn_ii_buffer *ii_buffer);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GROONGA_H */
diff --git a/storage/mroonga/vendor/groonga/include/groonga/Makefile.am b/storage/mroonga/vendor/groonga/include/groonga/Makefile.am
new file mode 100644
index 00000000000..baf9ca5bd1b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/include/groonga/Makefile.am
@@ -0,0 +1,6 @@
+groonga_includedir = $(pkgincludedir)/groonga
+groonga_include_HEADERS = \
+ plugin.h \
+ tokenizer.h \
+ nfkc.h \
+ normalizer.h
diff --git a/storage/mroonga/vendor/groonga/include/groonga/nfkc.h b/storage/mroonga/vendor/groonga/include/groonga/nfkc.h
new file mode 100644
index 00000000000..3b7e294fb97
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/include/groonga/nfkc.h
@@ -0,0 +1,32 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_NFKC_H
+#define GRN_NFKC_H
+
+#include <groonga.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+GRN_API grn_char_type grn_nfkc_char_type(const unsigned char *str);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_NFKC_H */
diff --git a/storage/mroonga/vendor/groonga/include/groonga/normalizer.h b/storage/mroonga/vendor/groonga/include/groonga/normalizer.h
new file mode 100644
index 00000000000..3ec843c6e07
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/include/groonga/normalizer.h
@@ -0,0 +1,55 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GROONGA_NORMALIER_H
+#define GROONGA_NORMALIER_H
+
+#include <stddef.h>
+
+#include <groonga/plugin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ grn_normalizer_register() registers a normalizer to the database
+ which is associated with `ctx'. `name_ptr' and `name_length' specify
+ the normalizer name. `name_length' can be `-1'. `-1' means that
+ `name_ptr` is NULL-terminated. Alphabetic letters ('A'-'Z' and
+ 'a'-'z'), digits ('0'-'9') and an underscore ('_') are capable
+ characters. `init', `next' and `fin' specify the normalizer
+ functions. `init' is called for initializing a tokenizer for a
+ document or query. `next' is called for extracting tokens one by
+ one. `fin' is called for finalizing a
+ tokenizer. grn_tokenizer_register() returns GRN_SUCCESS on success,
+ an error code on failure. See "groonga.h" for more details of
+ grn_proc_func and grn_user_data, that is used as an argument of
+ grn_proc_func.
+ */
+GRN_PLUGIN_EXPORT grn_rc grn_normalizer_register(grn_ctx *ctx,
+ const char *name_ptr,
+ int name_length,
+ grn_proc_func *init,
+ grn_proc_func *next,
+ grn_proc_func *fin);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* GROONGA_NORMALIER_H */
diff --git a/storage/mroonga/vendor/groonga/include/groonga/plugin.h b/storage/mroonga/vendor/groonga/include/groonga/plugin.h
new file mode 100644
index 00000000000..98676acf6be
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/include/groonga/plugin.h
@@ -0,0 +1,152 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2010-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_PLUGIN_H
+#define GRN_PLUGIN_H
+
+#include <stddef.h>
+
+#include <groonga.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GRN_PLUGIN_INIT grn_plugin_impl_init
+#define GRN_PLUGIN_REGISTER grn_plugin_impl_register
+#define GRN_PLUGIN_FIN grn_plugin_impl_fin
+
+#if defined(_WIN32) || defined(_WIN64)
+# define GRN_PLUGIN_EXPORT __declspec(dllexport)
+#else /* defined(_WIN32) || defined(_WIN64) */
+# define GRN_PLUGIN_EXPORT
+#endif /* defined(_WIN32) || defined(_WIN64) */
+
+GRN_PLUGIN_EXPORT grn_rc GRN_PLUGIN_INIT(grn_ctx *ctx);
+GRN_PLUGIN_EXPORT grn_rc GRN_PLUGIN_REGISTER(grn_ctx *ctx);
+GRN_PLUGIN_EXPORT grn_rc GRN_PLUGIN_FIN(grn_ctx *ctx);
+
+/*
+ Don't call these functions directly. Use GRN_PLUGIN_MALLOC(),
+ GRN_PLUGIN_REALLOC() and GRN_PLUGIN_FREE() instead.
+ */
+GRN_API void *grn_plugin_malloc(grn_ctx *ctx, size_t size, const char *file,
+ int line, const char *func);
+GRN_API void *grn_plugin_realloc(grn_ctx *ctx, void *ptr, size_t size,
+ const char *file, int line, const char *func);
+GRN_API void grn_plugin_free(grn_ctx *ctx, void *ptr, const char *file,
+ int line, const char *func);
+
+#define GRN_PLUGIN_MALLOC(ctx, size) \
+ grn_plugin_malloc((ctx), (size), __FILE__, __LINE__, __FUNCTION__)
+#define GRN_PLUGIN_REALLOC(ctx, ptr, size) \
+ grn_plugin_realloc((ctx), (ptr), (size), __FILE__, __LINE__, __FUNCTION__)
+#define GRN_PLUGIN_FREE(ctx, ptr) \
+ grn_plugin_free((ctx), (ptr), __FILE__, __LINE__, __FUNCTION__)
+
+#define GRN_PLUGIN_LOG(ctx, level, ...) \
+ GRN_LOG((ctx), (level), __VA_ARGS__)
+
+/*
+ Don't call grn_plugin_set_error() directly. This function is used in
+ GRN_PLUGIN_SET_ERROR().
+ */
+GRN_API void grn_plugin_set_error(grn_ctx *ctx, grn_log_level level,
+ grn_rc error_code,
+ const char *file, int line, const char *func,
+ const char *format, ...) GRN_ATTRIBUTE_PRINTF(7);
+
+/*
+ Don't call these functions directly. grn_plugin_backtrace() and
+ grn_plugin_logtrace() are used in GRN_PLUGIN_SET_ERROR().
+ */
+GRN_API void grn_plugin_backtrace(grn_ctx *ctx);
+GRN_API void grn_plugin_logtrace(grn_ctx *ctx, grn_log_level level);
+
+/*
+ Don't use GRN_PLUGIN_SET_ERROR() directly. This macro is used in
+ GRN_PLUGIN_ERROR().
+ */
+#define GRN_PLUGIN_SET_ERROR(ctx, level, error_code, ...) do { \
+ grn_plugin_set_error(ctx, level, error_code, \
+ __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__); \
+ GRN_LOG(ctx, level, __VA_ARGS__); \
+ grn_plugin_backtrace(ctx); \
+ grn_plugin_logtrace(ctx, level); \
+} while (0)
+
+#define GRN_PLUGIN_ERROR(ctx, error_code, ...) \
+ GRN_PLUGIN_SET_ERROR(ctx, GRN_LOG_ERROR, error_code, __VA_ARGS__)
+
+typedef struct _grn_plugin_mutex grn_plugin_mutex;
+
+GRN_API grn_plugin_mutex *grn_plugin_mutex_open(grn_ctx *ctx);
+
+/*
+ grn_plugin_mutex_create() is deprecated. Use grn_plugin_mutex_open()
+ instead.
+*/
+GRN_API grn_plugin_mutex *grn_plugin_mutex_create(grn_ctx *ctx);
+
+GRN_API void grn_plugin_mutex_close(grn_ctx *ctx, grn_plugin_mutex *mutex);
+
+/*
+ grn_plugin_mutex_destroy() is deprecated. Use grn_plugin_mutex_close()
+ instead.
+*/
+GRN_API void grn_plugin_mutex_destroy(grn_ctx *ctx, grn_plugin_mutex *mutex);
+
+GRN_API void grn_plugin_mutex_lock(grn_ctx *ctx, grn_plugin_mutex *mutex);
+
+GRN_API void grn_plugin_mutex_unlock(grn_ctx *ctx, grn_plugin_mutex *mutex);
+
+GRN_API grn_obj *grn_plugin_proc_alloc(grn_ctx *ctx, grn_user_data *user_data,
+ grn_id domain, grn_obj_flags flags);
+
+GRN_API grn_obj *grn_plugin_proc_get_var(grn_ctx *ctx, grn_user_data *user_data,
+ const char *name, int name_size);
+
+GRN_API grn_obj *grn_plugin_proc_get_var_by_offset(grn_ctx *ctx,
+ grn_user_data *user_data,
+ unsigned int offset);
+
+GRN_API const char *grn_plugin_win32_base_dir(void);
+
+GRN_API int grn_plugin_charlen(grn_ctx *ctx, const char *str_ptr,
+ unsigned int str_length, grn_encoding encoding);
+
+GRN_API int grn_plugin_isspace(grn_ctx *ctx, const char *str_ptr,
+ unsigned int str_length, grn_encoding encoding);
+
+GRN_API grn_rc grn_plugin_expr_var_init(grn_ctx *ctx,
+ grn_expr_var *var,
+ const char *name,
+ int name_size);
+
+GRN_API grn_obj * grn_plugin_command_create(grn_ctx *ctx,
+ const char *name,
+ int name_size,
+ grn_proc_func func,
+ unsigned int n_vars,
+ grn_expr_var *vars);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_PLUGIN_H */
diff --git a/storage/mroonga/vendor/groonga/include/groonga/tokenizer.h b/storage/mroonga/vendor/groonga/include/groonga/tokenizer.h
new file mode 100644
index 00000000000..c2e05d6e0a2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/include/groonga/tokenizer.h
@@ -0,0 +1,225 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_PLUGIN_TOKENIZER_H
+#define GRN_PLUGIN_TOKENIZER_H
+
+#include <stddef.h>
+
+#include <groonga/plugin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GRN_TOKENIZER_TOKENIZED_DELIMITER_UTF8 "\xEF\xBF\xBE"
+#define GRN_TOKENIZER_TOKENIZED_DELIMITER_UTF8_LEN 3
+
+/*
+ grn_tokenizer_charlen() returns the length (#bytes) of the first character
+ in the string specified by `str_ptr' and `str_length'. If the starting bytes
+ are invalid as a character, grn_tokenizer_charlen() returns 0. See
+ grn_encoding in "groonga.h" for more details of `encoding'
+
+ Deprecated. Use grn_plugin_charlen() instead.
+ */
+int grn_tokenizer_charlen(grn_ctx *ctx, const char *str_ptr,
+ unsigned int str_length, grn_encoding encoding);
+
+/*
+ grn_tokenizer_isspace() returns the length (#bytes) of the first character
+ in the string specified by `str_ptr' and `str_length' if it is a space
+ character. Otherwise, grn_tokenizer_isspace() returns 0.
+
+ Deprecated. Use grn_plugin_isspace() instead.
+ */
+int grn_tokenizer_isspace(grn_ctx *ctx, const char *str_ptr,
+ unsigned int str_length, grn_encoding encoding);
+
+/*
+ grn_tokenizer_is_tokenized_delimiter() returns whether is the first
+ character in the string specified by `str_ptr' and `str_length' the
+ special tokenized delimiter character or not.
+ */
+grn_bool grn_tokenizer_is_tokenized_delimiter(grn_ctx *ctx,
+ const char *str_ptr,
+ unsigned int str_length,
+ grn_encoding encoding);
+
+/*
+ grn_tokenizer_have_tokenized_delimiter() returns whether is there
+ the special delimiter character in the string specified by `str_ptr'
+ and `str_length' the special tokenized delimiter character or not.
+ */
+GRN_PLUGIN_EXPORT grn_bool grn_tokenizer_have_tokenized_delimiter(grn_ctx *ctx,
+ const char *str_ptr,
+ unsigned int str_length,
+ grn_encoding encoding);
+
+/*
+ grn_tokenizer_query is a structure for storing a query. See the following
+ functions.
+ */
+typedef struct _grn_tokenizer_query grn_tokenizer_query;
+
+struct _grn_tokenizer_query {
+ grn_obj *normalized_query;
+ char *query_buf;
+ const char *ptr;
+ unsigned int length;
+ grn_encoding encoding;
+ unsigned int flags;
+ grn_bool have_tokenized_delimiter;
+ unsigned int token_mode;
+};
+
+/*
+ grn_tokenizer_query_open() parses `args' and returns a new object of
+ grn_tokenizer_query. The new object stores information of the query.
+ grn_tokenizer_query_open() normalizes the query if the target table
+ requires normalization. grn_tokenizer_query_open() returns NULL if
+ something goes wrong. Note that grn_tokenizer_query_open() must be called
+ just once in the function that initializes a tokenizer.
+
+ See `GRN_STRING_*' flags for `normalize_flags'.
+ */
+GRN_PLUGIN_EXPORT grn_tokenizer_query *grn_tokenizer_query_open(grn_ctx *ctx,
+ int num_args, grn_obj **args,
+ unsigned int normalize_flags);
+
+/*
+ grn_tokenizer_query_create() is deprecated. Use grn_tokenizer_query_open()
+ instead.
+*/
+
+grn_tokenizer_query *grn_tokenizer_query_create(grn_ctx *ctx,
+ int num_args, grn_obj **args);
+
+/*
+ grn_tokenizer_query_close() finalizes an object of grn_tokenizer_query
+ and then frees memory allocated for that object.
+ */
+GRN_PLUGIN_EXPORT void grn_tokenizer_query_close(grn_ctx *ctx, grn_tokenizer_query *query);
+
+/*
+ grn_tokenizer_query_destroy() is deprecated. Use grn_tokenizer_query_close()
+ instead.
+ */
+void grn_tokenizer_query_destroy(grn_ctx *ctx, grn_tokenizer_query *query);
+
+/*
+ grn_tokenizer_token is needed to return tokens. A grn_tokenizer_token object
+ stores a token to be returned and it must be maintained until a request for
+ next token or finalization comes.
+ */
+typedef struct _grn_tokenizer_token grn_tokenizer_token;
+
+struct _grn_tokenizer_token {
+ grn_obj str;
+ grn_obj status;
+};
+
+/*
+ grn_tokenizer_token_init() initializes `token'. Note that an initialized
+ object must be finalized by grn_tokenizer_token_fin().
+ */
+GRN_PLUGIN_EXPORT void grn_tokenizer_token_init(grn_ctx *ctx, grn_tokenizer_token *token);
+
+/*
+ grn_tokenizer_token_fin() finalizes `token' that has been initialized by
+ grn_tokenizer_token_init().
+ */
+GRN_PLUGIN_EXPORT void grn_tokenizer_token_fin(grn_ctx *ctx, grn_tokenizer_token *token);
+
+/*
+ * grn_tokenizer_status is a flag set for tokenizer status codes.
+ * If a document or query contains no tokens, push an empty string with
+ * GRN_TOKENIZER_TOKEN_LAST as a token.
+ */
+typedef unsigned int grn_tokenizer_status;
+
+/* GRN_TOKENIZER_TOKEN_CONTINUE means that the next token is not the last one. */
+#define GRN_TOKENIZER_TOKEN_CONTINUE (0)
+/* GRN_TOKENIZER_TOKEN_LAST means that the next token is the last one. */
+#define GRN_TOKENIZER_TOKEN_LAST (0x01L<<0)
+/* GRN_TOKENIZER_TOKEN_OVERLAP means that ... */
+#define GRN_TOKENIZER_TOKEN_OVERLAP (0x01L<<1)
+/* GRN_TOKENIZER_TOKEN_UNMATURED means that ... */
+#define GRN_TOKENIZER_TOKEN_UNMATURED (0x01L<<2)
+/* GRN_TOKENIZER_TOKEN_REACH_END means that ... */
+#define GRN_TOKENIZER_TOKEN_REACH_END (0x01L<<3)
+/* GRN_TOKENIZER_TOKEN_SKIP means that the token is skipped */
+#define GRN_TOKENIZER_TOKEN_SKIP (0x01L<<4)
+/* GRN_TOKENIZER_TOKEN_SKIP_WITH_POSITION means that the token and postion is skipped */
+#define GRN_TOKENIZER_TOKEN_SKIP_WITH_POSITION (0x01L<<5)
+
+/*
+ * GRN_TOKENIZER_CONTINUE and GRN_TOKENIZER_LAST are deprecated. They
+ * are just for backward compatibility. Use
+ * GRN_TOKENIZER_TOKEN_CONTINUE and GRN_TOKENIZER_TOKEN_LAST
+ * instead.
+ */
+#define GRN_TOKENIZER_CONTINUE GRN_TOKENIZER_TOKEN_CONTINUE
+#define GRN_TOKENIZER_LAST GRN_TOKENIZER_TOKEN_LAST
+
+/*
+ grn_tokenizer_token_push() pushes the next token into `token'. Note that
+ grn_tokenizer_token_push() does not make a copy of the given string. This
+ means that you have to maintain a memory space allocated to the string.
+ Also note that the grn_tokenizer_token object must be maintained until the
+ request for the next token or finalization comes. See grn_tokenizer_status in
+ this header for more details of `status'.
+ */
+GRN_PLUGIN_EXPORT void grn_tokenizer_token_push(grn_ctx *ctx, grn_tokenizer_token *token,
+ const char *str_ptr, unsigned int str_length,
+ grn_tokenizer_status status);
+
+/*
+ grn_tokenizer_tokenized_delimiter_next() extracts the next token
+ from the string specified by `str_ptr' and `str_length' and pushes
+ the next token into `token'. It returns the string after the next
+ token. The returned string may be `NULL' when all tokens are
+ extracted.
+ */
+GRN_PLUGIN_EXPORT const char *grn_tokenizer_tokenized_delimiter_next(grn_ctx *ctx,
+ grn_tokenizer_token *token,
+ const char *str_ptr,
+ unsigned int str_length,
+ grn_encoding encoding);
+
+/*
+ grn_tokenizer_register() registers a plugin to the database which is
+ associated with `ctx'. `plugin_name_ptr' and `plugin_name_length' specify the
+ plugin name. Alphabetic letters ('A'-'Z' and 'a'-'z'), digits ('0'-'9') and
+ an underscore ('_') are capable characters. `init', `next' and `fin' specify
+ the plugin functions. `init' is called for initializing a tokenizer for a
+ document or query. `next' is called for extracting tokens one by one. `fin'
+ is called for finalizing a tokenizer. grn_tokenizer_register() returns
+ GRN_SUCCESS on success, an error code on failure. See "groonga.h" for more
+ details of grn_proc_func and grn_user_data, that is used as an argument of
+ grn_proc_func.
+ */
+GRN_PLUGIN_EXPORT grn_rc grn_tokenizer_register(grn_ctx *ctx, const char *plugin_name_ptr,
+ unsigned int plugin_name_length,
+ grn_proc_func *init, grn_proc_func *next,
+ grn_proc_func *fin);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* GRN_PLUGIN_TOKENIZER_H */
diff --git a/storage/mroonga/vendor/groonga/lib/CMakeLists.txt b/storage/mroonga/vendor/groonga/lib/CMakeLists.txt
new file mode 100644
index 00000000000..5be82fa6620
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/CMakeLists.txt
@@ -0,0 +1,90 @@
+# Copyright(C) 2012-2013 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+add_definitions(
+ -DGRN_DAT_EXPORT
+ )
+
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/dat
+ ${MRUBY_INCLUDE_DIRS})
+
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/sources.am LIBGROONGA_SOURCES)
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/dat/sources.am LIBGRNDAT_SOURCES)
+string(REGEX REPLACE "([^;]+)" "dat/\\1"
+ LIBGRNDAT_SOURCES "${LIBGRNDAT_SOURCES}")
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/mrb/sources.am LIBGRNMRB_SOURCES)
+string(REGEX REPLACE "([^;]+)" "mrb/\\1"
+ LIBGRNMRB_SOURCES "${LIBGRNMRB_SOURCES}")
+
+set_source_files_properties(${LIBGROONGA_SOURCES} ${LIBGRNMRB_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+set_source_files_properties(dat.cpp ${LIBGRNDAT_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_CXX_COMPILE_FLAGS}")
+
+if(MRN_GROONGA_BUNDLED)
+ add_library(libgroonga STATIC
+ ${LIBGROONGA_SOURCES}
+ ${LIBGRNDAT_SOURCES}
+ ${LIBGRNMRB_SOURCES}
+ ${MRUBY_LIBS})
+else()
+ add_library(libgroonga SHARED
+ ${LIBGROONGA_SOURCES}
+ ${LIBGRNDAT_SOURCES}
+ ${LIBGRNMRB_SOURCES}
+ ${MRUBY_LIBS})
+endif()
+set_target_properties(libgroonga PROPERTIES OUTPUT_NAME "groonga")
+
+if(NOT MRN_GROONGA_BUNDLED)
+ target_link_libraries(libgroonga
+ ${EXECINFO_LIBS}
+ ${RT_LIBS}
+ ${PTHREAD_LIBS}
+ ${Z_LIBS}
+ ${LZO2_LIBS}
+ ${DL_LIBS}
+ ${WS2_32_LIBS})
+ install(TARGETS libgroonga
+ ARCHIVE DESTINATION "${LIB_DIR}"
+ LIBRARY DESTINATION "${LIB_DIR}"
+ RUNTIME DESTINATION "${BIN_DIR}")
+else()
+ target_link_libraries(libgroonga
+ ${EXECINFO_LIBS}
+ ${RT_LIBS}
+ ${PTHREAD_LIBS}
+ ${Z_LIBS}
+ ${LZO2_LIBS}
+ ${DL_LIBS}
+ ${M_LIBS}
+ ${STDCPP_LIBS}
+ ${WS2_32_LIBS})
+endif()
+
+if(GRN_WITH_MRUBY)
+ read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/mrb/scripts/sources.am
+ RUBY_SCRIPTS)
+ string(REGEX REPLACE "([^;]+)" "mrb/scripts/\\1"
+ RUBY_SCRIPTS "${RUBY_SCRIPTS}")
+ if(NOT MRN_GROONGA_BUNDLED)
+ install(
+ FILES ${RUBY_SCRIPTS}
+ DESTINATION "${GRN_RELATIVE_RUBY_SCRIPTS_DIR}")
+ endif()
+endif()
diff --git a/storage/mroonga/vendor/groonga/lib/Makefile.am b/storage/mroonga/vendor/groonga/lib/Makefile.am
new file mode 100644
index 00000000000..047c41a25da
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/Makefile.am
@@ -0,0 +1,46 @@
+SUBDIRS = \
+ dat \
+ mrb
+
+lib_LTLIBRARIES = libgroonga.la
+
+include $(top_srcdir)/version.sh
+AM_CFLAGS = \
+ $(NO_STRICT_ALIASING_CFLAGS) \
+ $(COVERAGE_CFLAGS) \
+ $(GRN_CFLAGS) \
+ $(MESSAGE_PACK_CFLAGS) \
+ $(MRUBY_CFLAGS)
+DEFAULT_INCLUDES = -I$(top_builddir) -I$(top_srcdir)/include
+DEFS += -D_REENTRANT $(GRN_DEFS) -DGRN_DAT_EXPORT
+
+include sources.am
+
+libgroonga_la_LDFLAGS = \
+ -version-info $(LT_VERSION_INFO) \
+ -no-undefined \
+ $(WINDOWS_LDFLAGS)
+
+libgroonga_la_LIBADD = \
+ dat/libgrndat.la \
+ mrb/libgrnmrb.la \
+ $(MESSAGE_PACK_LIBS) \
+ $(MRUBY_LIBS)
+
+if WITH_LEMON
+BUILT_SOURCES = \
+ ecmascript.c
+
+SUFFIXES = .lemon .c
+
+.lemon.c:
+ $(LEMON) $<
+endif
+
+EXTRA_DIST = \
+ ecmascript.c \
+ ecmascript.h \
+ ecmascript.lemon \
+ CMakeLists.txt
+
+CLEANFILES = *.gcno *.gcda
diff --git a/storage/mroonga/vendor/groonga/lib/com.c b/storage/mroonga/vendor/groonga/lib/com.c
new file mode 100644
index 00000000000..172ad91ed1e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/com.c
@@ -0,0 +1,1161 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "groonga_in.h"
+
+#include <stdio.h>
+#include <string.h>
+#include "ctx_impl.h"
+
+#ifdef WIN32
+# include <ws2tcpip.h>
+#else
+# ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+# endif /* HAVE_SYS_SOCKET_H */
+# ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+# endif /* HAVE_NETINET_IN_H */
+# ifdef HAVE_NETINET_TCP_H
+# include <netinet/tcp.h>
+# endif /* HAVE_NETINET_TCP_H */
+# ifdef HAVE_SIGNAL_H
+# include <signal.h>
+# endif /* HAVE_SIGNAL_H */
+#endif /* WIN32 */
+
+#include "ctx.h"
+#include "com.h"
+
+#ifndef PF_INET
+#define PF_INET AF_INET
+#endif /* PF_INET */
+
+#ifndef SOL_TCP
+# ifdef IPPROTO_TCP
+# define SOL_TCP IPPROTO_TCP
+# else
+# define SOL_TCP 6
+# endif /* IPPROTO_TCP */
+#endif /* SOL_TCP */
+
+#ifndef USE_MSG_MORE
+# ifdef MSG_MORE
+# undef MSG_MORE
+# endif
+# define MSG_MORE 0
+#endif /* USE_MSG_MORE */
+
+
+#ifndef USE_MSG_NOSIGNAL
+# ifdef MSG_NOSIGNAL
+# undef MSG_NOSIGNAL
+# endif
+# define MSG_NOSIGNAL 0
+#endif /* USE_MSG_NOSIGNAL */
+/******* grn_com_queue ********/
+
+grn_rc
+grn_com_queue_enque(grn_ctx *ctx, grn_com_queue *q, grn_com_queue_entry *e)
+{
+ CRITICAL_SECTION_ENTER(q->cs);
+ e->next = NULL;
+ *q->tail = e;
+ q->tail = &e->next;
+ CRITICAL_SECTION_LEAVE(q->cs);
+ /*
+ uint8_t i = q->last + 1;
+ e->next = NULL;
+ if (q->first == i || q->next) {
+ CRITICAL_SECTION_ENTER(q->cs);
+ if (q->first == i || q->next) {
+ *q->tail = e;
+ q->tail = &e->next;
+ } else {
+ q->bins[q->last] = e;
+ q->last = i;
+ }
+ CRITICAL_SECTION_LEAVE(q->cs);
+ } else {
+ q->bins[q->last] = e;
+ q->last = i;
+ }
+ */
+ return GRN_SUCCESS;
+}
+
+grn_com_queue_entry *
+grn_com_queue_deque(grn_ctx *ctx, grn_com_queue *q)
+{
+ grn_com_queue_entry *e = NULL;
+
+ CRITICAL_SECTION_ENTER(q->cs);
+ if (q->next) {
+ e = q->next;
+ if (!(q->next = e->next)) { q->tail = &q->next; }
+ }
+ CRITICAL_SECTION_LEAVE(q->cs);
+
+ /*
+ if (q->first == q->last) {
+ if (q->next) {
+ CRITICAL_SECTION_ENTER(q->cs);
+ e = q->next;
+ if (!(q->next = e->next)) { q->tail = &q->next; }
+ CRITICAL_SECTION_LEAVE(q->cs);
+ }
+ } else {
+ e = q->bins[q->first++];
+ }
+ */
+ return e;
+}
+
+/******* grn_msg ********/
+
+grn_obj *
+grn_msg_open(grn_ctx *ctx, grn_com *com, grn_com_queue *old)
+{
+ grn_msg *msg = NULL;
+ if (old && (msg = (grn_msg *)grn_com_queue_deque(ctx, old))) {
+ if (msg->ctx != ctx) {
+ ERR(GRN_INVALID_ARGUMENT, "ctx unmatch");
+ return NULL;
+ }
+ GRN_BULK_REWIND(&msg->qe.obj);
+ } else if ((msg = GRN_MALLOCN(grn_msg, 1))) {
+ GRN_OBJ_INIT(&msg->qe.obj, GRN_MSG, 0, GRN_DB_TEXT);
+ msg->qe.obj.header.impl_flags |= GRN_OBJ_ALLOCATED;
+ msg->ctx = ctx;
+ }
+ msg->qe.next = NULL;
+ msg->u.peer = com;
+ msg->old = old;
+ memset(&msg->header, 0, sizeof(grn_com_header));
+ return (grn_obj *)msg;
+}
+
+grn_obj *
+grn_msg_open_for_reply(grn_ctx *ctx, grn_obj *query, grn_com_queue *old)
+{
+ grn_msg *req = (grn_msg *)query, *msg = NULL;
+ if (req && (msg = (grn_msg *)grn_msg_open(ctx, req->u.peer, old))) {
+ msg->edge_id = req->edge_id;
+ msg->header.proto = req->header.proto == GRN_COM_PROTO_MBREQ
+ ? GRN_COM_PROTO_MBRES : req->header.proto;
+ }
+ return (grn_obj *)msg;
+}
+
+grn_rc
+grn_msg_close(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_msg *msg = (grn_msg *)obj;
+ if (ctx == msg->ctx) { return grn_obj_close(ctx, obj); }
+ return grn_com_queue_enque(ctx, msg->old, (grn_com_queue_entry *)msg);
+}
+
+grn_rc
+grn_msg_set_property(grn_ctx *ctx, grn_obj *obj,
+ uint16_t status, uint32_t key_size, uint8_t extra_size)
+{
+ grn_com_header *header = &((grn_msg *)obj)->header;
+ header->status = htons(status);
+ header->keylen = htons(key_size);
+ header->level = extra_size;
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_msg_send(grn_ctx *ctx, grn_obj *msg, int flags)
+{
+ grn_rc rc;
+ grn_msg *m = (grn_msg *)msg;
+ grn_com *peer = m->u.peer;
+ grn_com_header *header = &m->header;
+ if (GRN_COM_QUEUE_EMPTYP(&peer->new_)) {
+ switch (header->proto) {
+ case GRN_COM_PROTO_HTTP :
+ {
+ ssize_t ret;
+ ret = send(peer->fd, GRN_BULK_HEAD(msg), GRN_BULK_VSIZE(msg), MSG_NOSIGNAL);
+ if (ret == -1) { SERR("send"); }
+ if (ctx->rc != GRN_OPERATION_WOULD_BLOCK) {
+ grn_com_queue_enque(ctx, m->old, (grn_com_queue_entry *)msg);
+ return ctx->rc;
+ }
+ }
+ break;
+ case GRN_COM_PROTO_GQTP :
+ {
+ if ((flags & GRN_CTX_MORE)) { flags |= GRN_CTX_QUIET; }
+ if (ctx->stat == GRN_CTX_QUIT) { flags |= GRN_CTX_QUIT; }
+ header->qtype = (uint8_t) ctx->impl->output_type;
+ header->keylen = 0;
+ header->level = 0;
+ header->flags = flags;
+ header->status = htons((uint16_t)ctx->rc);
+ header->opaque = 0;
+ header->cas = 0;
+ //todo : MSG_DONTWAIT
+ rc = grn_com_send(ctx, peer, header,
+ GRN_BULK_HEAD(msg), GRN_BULK_VSIZE(msg), 0);
+ if (rc != GRN_OPERATION_WOULD_BLOCK) {
+ grn_com_queue_enque(ctx, m->old, (grn_com_queue_entry *)msg);
+ return rc;
+ }
+ }
+ break;
+ case GRN_COM_PROTO_MBREQ :
+ return GRN_FUNCTION_NOT_IMPLEMENTED;
+ case GRN_COM_PROTO_MBRES :
+ rc = grn_com_send(ctx, peer, header,
+ GRN_BULK_HEAD(msg), GRN_BULK_VSIZE(msg),
+ (flags & GRN_CTX_MORE) ? MSG_MORE :0);
+ if (rc != GRN_OPERATION_WOULD_BLOCK) {
+ grn_com_queue_enque(ctx, m->old, (grn_com_queue_entry *)msg);
+ return rc;
+ }
+ break;
+ default :
+ return GRN_INVALID_ARGUMENT;
+ }
+ }
+ MUTEX_LOCK(peer->ev->mutex);
+ rc = grn_com_queue_enque(ctx, &peer->new_, (grn_com_queue_entry *)msg);
+ COND_SIGNAL(peer->ev->cond);
+ MUTEX_UNLOCK(peer->ev->mutex);
+ return rc;
+}
+
+/******* grn_com ********/
+
+grn_rc
+grn_com_init(void)
+{
+#ifdef WIN32
+ WSADATA wd;
+ if (WSAStartup(MAKEWORD(2, 0), &wd) != 0) {
+ grn_ctx *ctx = &grn_gctx;
+ SERR("WSAStartup");
+ }
+#else /* WIN32 */
+#ifndef USE_MSG_NOSIGNAL
+ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
+ grn_ctx *ctx = &grn_gctx;
+ SERR("signal");
+ }
+#endif /* USE_MSG_NOSIGNAL */
+#endif /* WIN32 */
+ return grn_gctx.rc;
+}
+
+void
+grn_com_fin(void)
+{
+#ifdef WIN32
+ WSACleanup();
+#endif /* WIN32 */
+}
+
+grn_rc
+grn_com_event_init(grn_ctx *ctx, grn_com_event *ev, int max_nevents, int data_size)
+{
+ ev->max_nevents = max_nevents;
+ if ((ev->hash = grn_hash_create(ctx, NULL, sizeof(grn_sock), data_size, 0))) {
+ MUTEX_INIT(ev->mutex);
+ COND_INIT(ev->cond);
+ GRN_COM_QUEUE_INIT(&ev->recv_old);
+#ifndef USE_SELECT
+#ifdef USE_EPOLL
+ if ((ev->events = GRN_MALLOC(sizeof(struct epoll_event) * max_nevents))) {
+ if ((ev->epfd = epoll_create(max_nevents)) != -1) {
+ goto exit;
+ } else {
+ SERR("epoll_create");
+ }
+ GRN_FREE(ev->events);
+ }
+#else /* USE_EPOLL */
+#ifdef USE_KQUEUE
+ if ((ev->events = GRN_MALLOC(sizeof(struct kevent) * max_nevents))) {
+ if ((ev->kqfd = kqueue()) != -1) {
+ goto exit;
+ } else {
+ SERR("kqueue");
+ }
+ GRN_FREE(ev->events);
+ }
+#else /* USE_KQUEUE */
+ if ((ev->events = GRN_MALLOC(sizeof(struct pollfd) * max_nevents))) {
+ goto exit;
+ }
+#endif /* USE_KQUEUE*/
+#endif /* USE_EPOLL */
+ grn_hash_close(ctx, ev->hash);
+ ev->hash = NULL;
+ ev->events = NULL;
+#else /* USE_SELECT */
+
+#endif /* USE_SELECT */
+ }
+exit :
+ return ctx->rc;
+}
+
+grn_rc
+grn_com_event_fin(grn_ctx *ctx, grn_com_event *ev)
+{
+ grn_obj *msg;
+ while ((msg = (grn_obj *)grn_com_queue_deque(ctx, &ev->recv_old))) {
+ grn_msg_close(ctx, msg);
+ }
+ if (ev->hash) { grn_hash_close(ctx, ev->hash); }
+#ifndef USE_SELECT
+ if (ev->events) { GRN_FREE(ev->events); }
+#ifdef USE_EPOLL
+ GRN_CLOSE(ev->epfd);
+#endif /* USE_EPOLL */
+#ifdef USE_KQUEUE
+ GRN_CLOSE(ev->kqfd);
+#endif /* USE_KQUEUE*/
+#endif /* USE_SELECT */
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_com_event_add(grn_ctx *ctx, grn_com_event *ev, grn_sock fd, int events, grn_com **com)
+{
+ grn_com *c;
+ /* todo : expand events */
+ if (!ev || *ev->hash->n_entries == ev->max_nevents) {
+ if (ev) { GRN_LOG(ctx, GRN_LOG_ERROR, "too many connections (%d)", ev->max_nevents); }
+ return GRN_INVALID_ARGUMENT;
+ }
+#ifdef USE_EPOLL
+ {
+ struct epoll_event e;
+ memset(&e, 0, sizeof(struct epoll_event));
+ e.data.fd = (fd);
+ e.events = (__uint32_t) events;
+ if (epoll_ctl(ev->epfd, EPOLL_CTL_ADD, (fd), &e) == -1) {
+ SERR("epoll_ctl");
+ return ctx->rc;
+ }
+ }
+#endif /* USE_EPOLL*/
+#ifdef USE_KQUEUE
+ {
+ struct kevent e;
+ /* todo: udata should have fd */
+ EV_SET(&e, (fd), events, EV_ADD, 0, 0, NULL);
+ if (kevent(ev->kqfd, &e, 1, NULL, 0, NULL) == -1) {
+ SERR("kevent");
+ return ctx->rc;
+ }
+ }
+#endif /* USE_KQUEUE */
+ {
+ if (grn_hash_add(ctx, ev->hash, &fd, sizeof(grn_sock), (void **)&c, NULL)) {
+ c->ev = ev;
+ c->fd = fd;
+ c->events = events;
+ if (com) { *com = c; }
+ }
+ }
+ return ctx->rc;
+}
+
+grn_rc
+grn_com_event_mod(grn_ctx *ctx, grn_com_event *ev, grn_sock fd, int events, grn_com **com)
+{
+ grn_com *c;
+ if (!ev) { return GRN_INVALID_ARGUMENT; }
+ if (grn_hash_get(ctx, ev->hash, &fd, sizeof(grn_sock), (void **)&c)) {
+ if (c->fd != fd) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "grn_com_event_mod fd unmatch %d != %d", c->fd, fd);
+ return GRN_OBJECT_CORRUPT;
+ }
+ if (com) { *com = c; }
+ if (c->events != events) {
+#ifdef USE_EPOLL
+ struct epoll_event e;
+ memset(&e, 0, sizeof(struct epoll_event));
+ e.data.fd = (fd);
+ e.events = (__uint32_t) events;
+ if (epoll_ctl(ev->epfd, EPOLL_CTL_MOD, (fd), &e) == -1) {
+ SERR("epoll_ctl");
+ return ctx->rc;
+ }
+#endif /* USE_EPOLL*/
+#ifdef USE_KQUEUE
+ // experimental
+ struct kevent e[2];
+ EV_SET(&e[0], (fd), GRN_COM_POLLIN|GRN_COM_POLLOUT, EV_DELETE, 0, 0, NULL);
+ EV_SET(&e[1], (fd), events, EV_ADD, 0, 0, NULL);
+ if (kevent(ev->kqfd, e, 2, NULL, 0, NULL) == -1) {
+ SERR("kevent");
+ return ctx->rc;
+ }
+#endif /* USE_KQUEUE */
+ c->events = events;
+ }
+ return GRN_SUCCESS;
+ }
+ return GRN_INVALID_ARGUMENT;
+}
+
+grn_rc
+grn_com_event_del(grn_ctx *ctx, grn_com_event *ev, grn_sock fd)
+{
+ if (!ev) { return GRN_INVALID_ARGUMENT; }
+ {
+ grn_com *c;
+ grn_id id = grn_hash_get(ctx, ev->hash, &fd, sizeof(grn_sock), (void **)&c);
+ if (id) {
+#ifdef USE_EPOLL
+ if (!c->closed) {
+ struct epoll_event e;
+ memset(&e, 0, sizeof(struct epoll_event));
+ e.data.fd = fd;
+ e.events = c->events;
+ if (epoll_ctl(ev->epfd, EPOLL_CTL_DEL, fd, &e) == -1) {
+ SERR("epoll_ctl");
+ return ctx->rc;
+ }
+ }
+#endif /* USE_EPOLL*/
+#ifdef USE_KQUEUE
+ struct kevent e;
+ EV_SET(&e, (fd), c->events, EV_DELETE, 0, 0, NULL);
+ if (kevent(ev->kqfd, &e, 1, NULL, 0, NULL) == -1) {
+ SERR("kevent");
+ return ctx->rc;
+ }
+#endif /* USE_KQUEUE */
+ return grn_hash_delete_by_id(ctx, ev->hash, id, NULL);
+ } else {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "%04x| fd(%d) not found in ev(%p)", getpid(), fd, ev);
+ return GRN_INVALID_ARGUMENT;
+ }
+ }
+}
+
+#define LISTEN_BACKLOG 0x1000
+
+grn_rc
+grn_com_event_start_accept(grn_ctx *ctx, grn_com_event *ev)
+{
+ grn_com *com = ev->acceptor;
+
+ if (com->accepting) {return ctx->rc;}
+
+ GRN_API_ENTER;
+ if (!grn_com_event_mod(ctx, ev, com->fd, GRN_COM_POLLIN, NULL)) {
+ if (listen(com->fd, LISTEN_BACKLOG) == 0) {
+ com->accepting = GRN_TRUE;
+ } else {
+ SERR("listen - start accept");
+ }
+ }
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_rc
+grn_com_event_stop_accept(grn_ctx *ctx, grn_com_event *ev)
+{
+ grn_com *com = ev->acceptor;
+
+ if (!com->accepting) {return ctx->rc;}
+
+ GRN_API_ENTER;
+ if (!grn_com_event_mod(ctx, ev, com->fd, 0, NULL)) {
+ if (listen(com->fd, 0) == 0) {
+ com->accepting = GRN_FALSE;
+ } else {
+ SERR("listen - disable accept");
+ }
+ }
+ GRN_API_RETURN(ctx->rc);
+}
+
+static void
+grn_com_receiver(grn_ctx *ctx, grn_com *com)
+{
+ grn_com_event *ev = com->ev;
+ ERRCLR(ctx);
+ if (ev->acceptor == com) {
+ grn_com *ncs;
+ grn_sock fd = accept(com->fd, NULL, NULL);
+ if (fd == -1) {
+ if (errno == EMFILE) {
+ grn_com_event_stop_accept(ctx, ev);
+ } else {
+ SERR("accept");
+ }
+ return;
+ }
+ if (grn_com_event_add(ctx, ev, fd, GRN_COM_POLLIN, (grn_com **)&ncs)) {
+ grn_sock_close(fd);
+ return;
+ }
+ ncs->has_sid = 0;
+ ncs->closed = 0;
+ ncs->opaque = NULL;
+ GRN_COM_QUEUE_INIT(&ncs->new_);
+ // GRN_LOG(ctx, GRN_LOG_NOTICE, "accepted (%d)", fd);
+ return;
+ } else {
+ grn_msg *msg = (grn_msg *)grn_msg_open(ctx, com, &ev->recv_old);
+ grn_com_recv(ctx, msg->u.peer, &msg->header, (grn_obj *)msg);
+ if (msg->u.peer /* is_edge_request(msg)*/) {
+ memcpy(&msg->edge_id, &ev->curr_edge_id, sizeof(grn_com_addr));
+ if (!com->has_sid) {
+ com->has_sid = 1;
+ com->sid = ev->curr_edge_id.sid++;
+ }
+ msg->edge_id.sid = com->sid;
+ }
+ msg->acceptor = ev->acceptor;
+ ev->msg_handler(ctx, (grn_obj *)msg);
+ }
+}
+
+grn_rc
+grn_com_event_poll(grn_ctx *ctx, grn_com_event *ev, int timeout)
+{
+ int nevents;
+ grn_com *com;
+#ifdef USE_SELECT
+ uint32_t dummy;
+ grn_sock *pfd;
+ int nfds = 0;
+ fd_set rfds;
+ fd_set wfds;
+ struct timeval tv;
+ if (timeout >= 0) {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ }
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ ctx->errlvl = GRN_OK;
+ ctx->rc = GRN_SUCCESS;
+ GRN_HASH_EACH(ctx, ev->hash, eh, &pfd, &dummy, &com, {
+ if ((com->events & GRN_COM_POLLIN)) { FD_SET(*pfd, &rfds); }
+ if ((com->events & GRN_COM_POLLOUT)) { FD_SET(*pfd, &wfds); }
+ if (*pfd > nfds) { nfds = *pfd; }
+ });
+ nevents = select(nfds + 1, &rfds, &wfds, NULL, (timeout >= 0) ? &tv : NULL);
+ if (nevents < 0) {
+ SERR("select");
+ if (ctx->rc == GRN_INTERRUPTED_FUNCTION_CALL) { ERRCLR(ctx); }
+ return ctx->rc;
+ }
+ if (timeout < 0 && !nevents) { GRN_LOG(ctx, GRN_LOG_NOTICE, "select returns 0 events"); }
+ GRN_HASH_EACH(ctx, ev->hash, eh, &pfd, &dummy, &com, {
+ if (FD_ISSET(*pfd, &rfds)) { grn_com_receiver(ctx, com); }
+ });
+#else /* USE_SELECT */
+#ifdef USE_EPOLL
+ struct epoll_event *ep;
+ ctx->errlvl = GRN_OK;
+ ctx->rc = GRN_SUCCESS;
+ nevents = epoll_wait(ev->epfd, ev->events, ev->max_nevents, timeout);
+ if (nevents < 0) {
+ SERR("epoll_wait");
+ }
+#else /* USE_EPOLL */
+#ifdef USE_KQUEUE
+ struct kevent *ep;
+ struct timespec tv;
+ if (timeout >= 0) {
+ tv.tv_sec = timeout / 1000;
+ tv.tv_nsec = (timeout % 1000) * 1000;
+ }
+ nevents = kevent(ev->kqfd, NULL, 0, ev->events, ev->max_nevents, &tv);
+ if (nevents < 0) {
+ SERR("kevent");
+ }
+#else /* USE_KQUEUE */
+ uint32_t dummy;
+ int nfd = 0, *pfd;
+ struct pollfd *ep = ev->events;
+ ctx->errlvl = GRN_OK;
+ ctx->rc = GRN_SUCCESS;
+ GRN_HASH_EACH(ctx, ev->hash, eh, &pfd, &dummy, &com, {
+ ep->fd = *pfd;
+ // ep->events =(short) com->events;
+ ep->events = POLLIN;
+ ep->revents = 0;
+ ep++;
+ nfd++;
+ });
+ nevents = poll(ev->events, nfd, timeout);
+ if (nevents < 0) {
+ SERR("poll");
+ }
+#endif /* USE_KQUEUE */
+#endif /* USE_EPOLL */
+ if (ctx->rc != GRN_SUCCESS) {
+ if (ctx->rc == GRN_INTERRUPTED_FUNCTION_CALL) {
+ ERRCLR(ctx);
+ }
+ return ctx->rc;
+ }
+ if (timeout < 0 && !nevents) { GRN_LOG(ctx, GRN_LOG_NOTICE, "poll returns 0 events"); }
+ for (ep = ev->events; nevents; ep++) {
+ int efd;
+#ifdef USE_EPOLL
+ efd = ep->data.fd;
+ nevents--;
+ // todo : com = ep->data.ptr;
+ if (!grn_hash_get(ctx, ev->hash, &efd, sizeof(grn_sock), (void *)&com)) {
+ struct epoll_event e;
+ GRN_LOG(ctx, GRN_LOG_ERROR, "fd(%d) not found in ev->hash", efd);
+ memset(&e, 0, sizeof(struct epoll_event));
+ e.data.fd = efd;
+ e.events = ep->events;
+ if (epoll_ctl(ev->epfd, EPOLL_CTL_DEL, efd, &e) == -1) { SERR("epoll_ctl"); }
+ if (grn_sock_close(efd) == -1) { SERR("close"); }
+ continue;
+ }
+ if ((ep->events & GRN_COM_POLLIN)) { grn_com_receiver(ctx, com); }
+#else /* USE_EPOLL */
+#ifdef USE_KQUEUE
+ efd = ep->ident;
+ nevents--;
+ // todo : com = ep->udata;
+ if (!grn_hash_get(ctx, ev->hash, &efd, sizeof(grn_sock), (void *)&com)) {
+ struct kevent e;
+ GRN_LOG(ctx, GRN_LOG_ERROR, "fd(%d) not found in ev->set", efd);
+ EV_SET(&e, efd, ep->filter, EV_DELETE, 0, 0, NULL);
+ if (kevent(ev->kqfd, &e, 1, NULL, 0, NULL) == -1) { SERR("kevent"); }
+ if (grn_sock_close(efd) == -1) { SERR("close"); }
+ continue;
+ }
+ if ((ep->filter == GRN_COM_POLLIN)) { grn_com_receiver(ctx, com); }
+#else
+ efd = ep->fd;
+ if (!(ep->events & ep->revents)) { continue; }
+ nevents--;
+ if (!grn_hash_get(ctx, ev->hash, &efd, sizeof(grn_sock), (void *)&com)) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "fd(%d) not found in ev->hash", efd);
+ if (grn_sock_close(efd) == -1) { SERR("close"); }
+ continue;
+ }
+ if ((ep->revents & GRN_COM_POLLIN)) { grn_com_receiver(ctx, com); }
+#endif /* USE_KQUEUE */
+#endif /* USE_EPOLL */
+ }
+#endif /* USE_SELECT */
+ /* todo :
+ while (!(msg = (grn_com_msg *)grn_com_queue_deque(&recv_old))) {
+ grn_msg_close(ctx, msg);
+ }
+ */
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_com_send_http(grn_ctx *ctx, grn_com *cs, const char *path, uint32_t path_len, int flags)
+{
+ ssize_t ret;
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_PUTS(ctx, &buf, "GET ");
+ grn_bulk_write(ctx, &buf, path, path_len);
+ GRN_TEXT_PUTS(ctx, &buf, " HTTP/1.0\r\n\r\n");
+ // todo : refine
+ if ((ret = send(cs->fd, GRN_BULK_HEAD(&buf), GRN_BULK_VSIZE(&buf), MSG_NOSIGNAL|flags)) == -1) {
+ SERR("send");
+ }
+ if (ret != GRN_BULK_VSIZE(&buf)) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "send %d != %d", (int)ret, (int)GRN_BULK_VSIZE(&buf));
+ }
+ grn_obj_close(ctx, &buf);
+ return ctx->rc;
+}
+
+grn_rc
+grn_com_send(grn_ctx *ctx, grn_com *cs,
+ grn_com_header *header, const char *body, uint32_t size, int flags)
+{
+ grn_rc rc = GRN_SUCCESS;
+ size_t whole_size = sizeof(grn_com_header) + size;
+ ssize_t ret;
+ header->size = htonl(size);
+ GRN_LOG(ctx, GRN_LOG_INFO, "send (%d,%x,%d,%02x,%02x,%04x)", size, header->flags, header->proto, header->qtype, header->level, header->status);
+
+ if (size) {
+#ifdef WIN32
+ WSABUF wsabufs[2];
+ DWORD n_sent;
+ wsabufs[0].buf = (char *)header;
+ wsabufs[0].len = sizeof(grn_com_header);
+ wsabufs[1].buf = (char *)body;
+ wsabufs[1].len = size;
+ if (WSASend(cs->fd, wsabufs, 2, &n_sent, 0, NULL, NULL) == SOCKET_ERROR) {
+ SERR("WSASend");
+ }
+ ret = n_sent;
+#else /* WIN32 */
+ struct iovec msg_iov[2];
+ struct msghdr msg;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = msg_iov;
+ msg.msg_iovlen = 2;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ msg_iov[0].iov_base = header;
+ msg_iov[0].iov_len = sizeof(grn_com_header);
+ msg_iov[1].iov_base = (char *)body;
+ msg_iov[1].iov_len = size;
+ if ((ret = sendmsg(cs->fd, &msg, MSG_NOSIGNAL|flags)) == -1) {
+ SERR("sendmsg");
+ rc = ctx->rc;
+ }
+#endif /* WIN32 */
+ } else {
+ if ((ret = send(cs->fd, (const void *)header, whole_size, MSG_NOSIGNAL|flags)) == -1) {
+ SERR("send");
+ rc = ctx->rc;
+ }
+ }
+ if (ret != whole_size) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "sendmsg(%d): %" GRN_FMT_LLD " < %" GRN_FMT_LLU,
+ cs->fd, (long long int)ret, (unsigned long long int)whole_size);
+ rc = ctx->rc;
+ }
+ return rc;
+}
+
+#define RETRY_MAX 10
+
+static const char *
+scan_delimiter(const char *p, const char *e)
+{
+ while (p + 4 <= e) {
+ if (p[3] == '\n') {
+ if (p[2] == '\r') {
+ if (p[1] == '\n') {
+ if (p[0] == '\r') { return p + 4; } else { p += 2; }
+ } else { p += 2; }
+ } else { p += 4; }
+ } else { p += p[3] == '\r' ? 1 : 4; }
+ }
+ return NULL;
+}
+
+#define BUFSIZE 4096
+
+static grn_rc
+grn_com_recv_text(grn_ctx *ctx, grn_com *com,
+ grn_com_header *header, grn_obj *buf, ssize_t ret)
+{
+ const char *p;
+ int retry = 0;
+ grn_bulk_write(ctx, buf, (char *)header, ret);
+ if ((p = scan_delimiter(GRN_BULK_HEAD(buf), GRN_BULK_CURR(buf)))) {
+ header->qtype = *GRN_BULK_HEAD(buf);
+ header->proto = GRN_COM_PROTO_HTTP;
+ header->size = GRN_BULK_VSIZE(buf);
+ goto exit;
+ }
+ for (;;) {
+ if (grn_bulk_reserve(ctx, buf, BUFSIZE)) { return ctx->rc; }
+ if ((ret = recv(com->fd, GRN_BULK_CURR(buf), BUFSIZE, 0)) < 0) {
+ SERR("recv text");
+ if (ctx->rc == GRN_OPERATION_WOULD_BLOCK ||
+ ctx->rc == GRN_INTERRUPTED_FUNCTION_CALL) {
+ ERRCLR(ctx);
+ continue;
+ }
+ goto exit;
+ }
+ if (ret) {
+ off_t o = GRN_BULK_VSIZE(buf);
+ p = GRN_BULK_CURR(buf);
+ GRN_BULK_INCR_LEN(buf, ret);
+ if (scan_delimiter(p - (o > 3 ? 3 : o), p + ret)) {
+ break;
+ }
+ } else {
+ if (++retry > RETRY_MAX) {
+ // ERR(GRN_RETRY_MAX, "retry max in recv text");
+ goto exit;
+ }
+ }
+ }
+ header->qtype = *GRN_BULK_HEAD(buf);
+ header->proto = GRN_COM_PROTO_HTTP;
+ header->size = GRN_BULK_VSIZE(buf);
+exit :
+ if (header->qtype == 'H') {
+ //todo : refine
+ /*
+ GRN_BULK_REWIND(buf);
+ grn_bulk_reserve(ctx, buf, BUFSIZE);
+ if ((ret = recv(com->fd, GRN_BULK_CURR(buf), BUFSIZE, 0)) < 0) {
+ SERR("recv text body");
+ } else {
+ GRN_BULK_CURR(buf) += ret;
+ }
+ */
+ }
+ return ctx->rc;
+}
+
+grn_rc
+grn_com_recv(grn_ctx *ctx, grn_com *com, grn_com_header *header, grn_obj *buf)
+{
+ ssize_t ret;
+ int retry = 0;
+ byte *p = (byte *)header;
+ size_t rest = sizeof(grn_com_header);
+ do {
+ if ((ret = recv(com->fd, p, rest, 0)) < 0) {
+ SERR("recv size");
+ GRN_LOG(ctx, GRN_LOG_ERROR, "recv error (%d)", com->fd);
+ if (ctx->rc == GRN_OPERATION_WOULD_BLOCK ||
+ ctx->rc == GRN_INTERRUPTED_FUNCTION_CALL) {
+ ERRCLR(ctx);
+ continue;
+ }
+ goto exit;
+ }
+ if (ret) {
+ if (header->proto < 0x80) {
+ return grn_com_recv_text(ctx, com, header, buf, ret);
+ }
+ rest -= ret, p += ret;
+ } else {
+ if (++retry > RETRY_MAX) {
+ // ERR(GRN_RETRY_MAX, "retry max in recv header (%d)", com->fd);
+ goto exit;
+ }
+ }
+ } while (rest);
+ GRN_LOG(ctx, GRN_LOG_INFO, "recv (%d,%x,%d,%02x,%02x,%04x)", ntohl(header->size), header->flags, header->proto, header->qtype, header->level, header->status);
+ {
+ uint8_t proto = header->proto;
+ size_t value_size = ntohl(header->size);
+ GRN_BULK_REWIND(buf);
+ switch (proto) {
+ case GRN_COM_PROTO_GQTP :
+ case GRN_COM_PROTO_MBREQ :
+ if (GRN_BULK_WSIZE(buf) < value_size) {
+ if ((grn_bulk_resize(ctx, buf, value_size))) {
+ goto exit;
+ }
+ }
+ retry = 0;
+ for (rest = value_size; rest;) {
+ if ((ret = recv(com->fd, GRN_BULK_CURR(buf), rest, MSG_WAITALL)) < 0) {
+ SERR("recv body");
+ if (ctx->rc == GRN_OPERATION_WOULD_BLOCK ||
+ ctx->rc == GRN_INTERRUPTED_FUNCTION_CALL) {
+ ERRCLR(ctx);
+ continue;
+ }
+ goto exit;
+ }
+ if (ret) {
+ rest -= ret;
+ GRN_BULK_INCR_LEN(buf, ret);
+ } else {
+ if (++retry > RETRY_MAX) {
+ // ERR(GRN_RETRY_MAX, "retry max in recv body");
+ goto exit;
+ }
+ }
+ }
+ break;
+ default :
+ GRN_LOG(ctx, GRN_LOG_ERROR, "illegal header: %d", proto);
+ ctx->rc = GRN_INVALID_FORMAT;
+ goto exit;
+ }
+ }
+exit :
+ return ctx->rc;
+}
+
+grn_com *
+grn_com_copen(grn_ctx *ctx, grn_com_event *ev, const char *dest, int port)
+{
+ grn_sock fd = -1;
+ grn_com *cs = NULL;
+
+ struct addrinfo hints, *addrinfo_list, *addrinfo_ptr;
+ char port_string[16];
+ int getaddrinfo_result;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+#ifdef AI_NUMERICSERV
+ hints.ai_flags = AI_NUMERICSERV;
+#endif
+ snprintf(port_string, sizeof(port_string), "%d", port);
+
+ getaddrinfo_result = getaddrinfo(dest, port_string, &hints, &addrinfo_list);
+ if (getaddrinfo_result != 0) {
+ switch (getaddrinfo_result) {
+#ifdef EAI_MEMORY
+ case EAI_MEMORY:
+ ERR(GRN_NO_MEMORY_AVAILABLE, "getaddrinfo: <%s:%s>: %s",
+ dest, port_string, gai_strerror(getaddrinfo_result));
+ break;
+#endif
+#ifdef EAI_SYSTEM
+ case EAI_SYSTEM:
+ SERR("getaddrinfo");
+ break;
+#endif
+ default:
+ ERR(GRN_INVALID_ARGUMENT, "getaddrinfo: <%s:%s>: %s",
+ dest, port_string, gai_strerror(getaddrinfo_result));
+ break;
+ }
+ return NULL;
+ }
+
+ for (addrinfo_ptr = addrinfo_list; addrinfo_ptr;
+ addrinfo_ptr = addrinfo_ptr->ai_next) {
+ static const int value = 1;
+ fd = socket(addrinfo_ptr->ai_family, addrinfo_ptr->ai_socktype,
+ addrinfo_ptr->ai_protocol);
+ if (fd == -1) {
+ SERR("socket");
+ } else if (setsockopt(fd, 6, TCP_NODELAY, &value, sizeof(value))) {
+ SERR("setsockopt");
+ grn_sock_close(fd);
+ } else if (connect(fd, addrinfo_ptr->ai_addr, addrinfo_ptr->ai_addrlen)) {
+ SERR("connect");
+ grn_sock_close(fd);
+ } else {
+ break;
+ }
+ }
+
+ freeaddrinfo(addrinfo_list);
+
+ if (!addrinfo_ptr) {
+ return NULL;
+ }
+ ctx->errlvl = GRN_OK;
+ ctx->rc = GRN_SUCCESS;
+
+ if (ev) {
+ grn_com_event_add(ctx, ev, fd, GRN_COM_POLLIN, &cs);
+ } else {
+ cs = GRN_CALLOC(sizeof(grn_com));
+ if (cs) {
+ cs->fd = fd;
+ }
+ }
+ if (!cs) {
+ grn_sock_close(fd);
+ }
+ return cs;
+}
+
+void
+grn_com_close_(grn_ctx *ctx, grn_com *com)
+{
+ grn_sock fd = com->fd;
+ if (shutdown(fd, SHUT_RDWR) == -1) { /* SERR("shutdown"); */ }
+ if (grn_sock_close(fd) == -1) {
+ SERR("close");
+ } else {
+ com->closed = 1;
+ }
+}
+
+grn_rc
+grn_com_close(grn_ctx *ctx, grn_com *com)
+{
+ grn_sock fd = com->fd;
+ grn_com_event *ev = com->ev;
+ if (ev) {
+ grn_com *acceptor = ev->acceptor;
+ grn_com_event_del(ctx, ev, fd);
+ if (acceptor) { grn_com_event_start_accept(ctx, ev); }
+ }
+ if (!com->closed) { grn_com_close_(ctx, com); }
+ if (!ev) { GRN_FREE(com); }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_com_sopen(grn_ctx *ctx, grn_com_event *ev,
+ const char *bind_address, int port, grn_msg_handler *func,
+ struct hostent *he)
+{
+ grn_sock lfd = -1;
+ grn_com *cs = NULL;
+ int getaddrinfo_result;
+ struct addrinfo *bind_address_info = NULL;
+ struct addrinfo hints;
+ char port_string[6]; /* ceil(log10(65535)) + 1 ('\0')*/
+
+ GRN_API_ENTER;
+ if (!bind_address) {
+ bind_address = "0.0.0.0";
+ }
+ snprintf(port_string, sizeof(port_string), "%d", port);
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+#ifdef AI_NUMERICSERV
+ hints.ai_flags = AI_NUMERICSERV;
+#endif
+ getaddrinfo_result = getaddrinfo(bind_address, port_string,
+ &hints, &bind_address_info);
+ if (getaddrinfo_result != 0) {
+ switch (getaddrinfo_result) {
+#ifdef EAI_MEMORY
+ case EAI_MEMORY:
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "getaddrinfo: <%s:%s>: %s",
+ bind_address, port_string, gai_strerror(getaddrinfo_result));
+ break;
+#endif
+#ifdef EAI_SYSTEM
+ case EAI_SYSTEM:
+ SERR("getaddrinfo");
+ break;
+#endif
+ default:
+ ERR(GRN_INVALID_ARGUMENT,
+ "getaddrinfo: <%s:%s>: %s",
+ bind_address, port_string, gai_strerror(getaddrinfo_result));
+ break;
+ }
+ goto exit;
+ }
+ if ((lfd = socket(bind_address_info->ai_family, SOCK_STREAM, 0)) == -1) {
+ SERR("socket");
+ goto exit;
+ }
+ memcpy(&ev->curr_edge_id.addr, he->h_addr, he->h_length);
+ ev->curr_edge_id.port = htons(port);
+ ev->curr_edge_id.sid = 0;
+ {
+ int v = 1;
+ if (setsockopt(lfd, SOL_TCP, TCP_NODELAY, (void *) &v, sizeof(int)) == -1) {
+ SERR("setsockopt");
+ goto exit;
+ }
+ if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (void *) &v, sizeof(int)) == -1) {
+ SERR("setsockopt");
+ goto exit;
+ }
+ }
+ if (bind(lfd, bind_address_info->ai_addr, bind_address_info->ai_addrlen) < 0) {
+ SERR("bind");
+ goto exit;
+ }
+ if (listen(lfd, LISTEN_BACKLOG) < 0) {
+ SERR("listen");
+ goto exit;
+ }
+ if (ev) {
+ if (grn_com_event_add(ctx, ev, lfd, GRN_COM_POLLIN, &cs)) { goto exit; }
+ ev->acceptor = cs;
+ ev->msg_handler = func;
+ cs->has_sid = 0;
+ cs->closed = 0;
+ cs->opaque = NULL;
+ GRN_COM_QUEUE_INIT(&cs->new_);
+ } else {
+ if (!(cs = GRN_MALLOC(sizeof(grn_com)))) { goto exit; }
+ cs->fd = lfd;
+ }
+ cs->accepting = GRN_TRUE;
+exit :
+ if (!cs && lfd != 1) { grn_sock_close(lfd); }
+ if (bind_address_info) { freeaddrinfo(bind_address_info); }
+ GRN_API_RETURN(ctx->rc);
+}
+
+
+grn_hash *grn_edges = NULL;
+void (*grn_dispatcher)(grn_ctx *ctx, grn_edge *edge);
+
+void
+grn_edges_init(grn_ctx *ctx, void (*dispatcher)(grn_ctx *ctx, grn_edge *edge))
+{
+ grn_edges = grn_hash_create(ctx, NULL, sizeof(grn_com_addr), sizeof(grn_edge), 0);
+ grn_dispatcher = dispatcher;
+}
+
+void
+grn_edges_fin(grn_ctx *ctx)
+{
+ grn_hash_close(ctx, grn_edges);
+}
+
+grn_edge *
+grn_edges_add(grn_ctx *ctx, grn_com_addr *addr, int *added)
+{
+ if (grn_io_lock(ctx, grn_edges->io, grn_lock_timeout)) {
+ return NULL;
+ } else {
+ grn_edge *edge;
+ grn_id id = grn_hash_add(ctx, grn_edges, addr, sizeof(grn_com_addr),
+ (void **)&edge, added);
+ grn_io_unlock(grn_edges->io);
+ if (id) { edge->id = id; }
+ return edge;
+ }
+}
+
+void
+grn_edges_delete(grn_ctx *ctx, grn_edge *edge)
+{
+ if (!grn_io_lock(ctx, grn_edges->io, grn_lock_timeout)) {
+ grn_hash_delete_by_id(ctx, grn_edges, edge->id, NULL);
+ grn_io_unlock(grn_edges->io);
+ }
+}
+
+grn_edge *
+grn_edges_add_communicator(grn_ctx *ctx, grn_com_addr *addr)
+{
+ int added;
+ grn_edge *edge = grn_edges_add(ctx, addr, &added);
+ if (added) {
+ grn_ctx_init(&edge->ctx, 0);
+ GRN_COM_QUEUE_INIT(&edge->recv_new);
+ GRN_COM_QUEUE_INIT(&edge->send_old);
+ edge->com = NULL;
+ edge->stat = 0 /*EDGE_IDLE*/;
+ edge->flags = GRN_EDGE_COMMUNICATOR;
+ }
+ return edge;
+}
+
+void
+grn_edge_dispatch(grn_ctx *ctx, grn_edge *edge, grn_obj *msg)
+{
+ grn_com_queue_enque(ctx, &edge->recv_new, (grn_com_queue_entry *)msg);
+ grn_dispatcher(ctx, edge);
+}
diff --git a/storage/mroonga/vendor/groonga/lib/com.h b/storage/mroonga/vendor/groonga/lib/com.h
new file mode 100644
index 00000000000..c0690ccfe76
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/com.h
@@ -0,0 +1,260 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_COM_H
+#define GRN_COM_H
+
+#ifndef GROONGA_H
+#include "groonga_in.h"
+#endif /* GROONGA_H */
+
+#ifndef GRN_STR_H
+#include "str.h"
+#endif /* GRN_STR_H */
+
+#ifndef GRN_HASH_H
+#include "hash.h"
+#endif /* GRN_HASH_H */
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif /* HAVE_NETDB_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/******* grn_com_queue ********/
+
+typedef struct _grn_com_queue grn_com_queue;
+typedef struct _grn_com_queue_entry grn_com_queue_entry;
+
+#define GRN_COM_QUEUE_BINSIZE (0x100)
+
+struct _grn_com_queue_entry {
+ grn_obj obj;
+ struct _grn_com_queue_entry *next;
+};
+
+struct _grn_com_queue {
+ grn_com_queue_entry *bins[GRN_COM_QUEUE_BINSIZE];
+ grn_com_queue_entry *next;
+ grn_com_queue_entry **tail;
+ uint8_t first;
+ uint8_t last;
+ grn_critical_section cs;
+};
+
+#define GRN_COM_QUEUE_INIT(q) do {\
+ (q)->next = NULL;\
+ (q)->tail = &(q)->next;\
+ (q)->first = 0;\
+ (q)->last = 0;\
+ CRITICAL_SECTION_INIT((q)->cs);\
+} while (0)
+
+#define GRN_COM_QUEUE_EMPTYP(q) (((q)->first == (q)->last) && !(q)->next)
+
+GRN_API grn_rc grn_com_queue_enque(grn_ctx *ctx, grn_com_queue *q, grn_com_queue_entry *e);
+GRN_API grn_com_queue_entry *grn_com_queue_deque(grn_ctx *ctx, grn_com_queue *q);
+
+/******* grn_com ********/
+
+#ifdef USE_SELECT
+# ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# endif /* HAVE_SYS_SELECT_H */
+# define GRN_COM_POLLIN 1
+# define GRN_COM_POLLOUT 2
+#else /* USE_SELECT */
+# ifdef USE_EPOLL
+# include <sys/epoll.h>
+# define GRN_COM_POLLIN EPOLLIN
+# define GRN_COM_POLLOUT EPOLLOUT
+# else /* USE_EPOLL */
+# ifdef USE_KQUEUE
+# include <sys/event.h>
+# define GRN_COM_POLLIN EVFILT_READ
+# define GRN_COM_POLLOUT EVFILT_WRITE
+# else /* USE_KQUEUE */
+# include <sys/poll.h>
+# define GRN_COM_POLLIN POLLIN
+# define GRN_COM_POLLOUT POLLOUT
+# endif /* USE_KQUEUE */
+# endif /* USE_EPOLL */
+#endif /* USE_SELECT */
+
+typedef struct _grn_com grn_com;
+typedef struct _grn_com_event grn_com_event;
+typedef struct _grn_com_addr grn_com_addr;
+typedef void grn_com_callback(grn_ctx *ctx, grn_com_event *, grn_com *);
+typedef void grn_msg_handler(grn_ctx *ctx, grn_obj *msg);
+
+enum {
+ grn_com_ok = 0,
+ grn_com_emem,
+ grn_com_erecv_head,
+ grn_com_erecv_body,
+ grn_com_eproto,
+};
+
+struct _grn_com_addr {
+ uint32_t addr;
+ uint16_t port;
+ uint16_t sid;
+};
+
+struct _grn_com {
+ grn_sock fd;
+ int events;
+ uint16_t sid;
+ uint8_t has_sid;
+ uint8_t closed;
+ grn_com_queue new_;
+ grn_com_event *ev;
+ void *opaque;
+ grn_bool accepting;
+};
+
+struct _grn_com_event {
+ struct _grn_hash *hash;
+ int max_nevents;
+ grn_ctx *ctx;
+ grn_mutex mutex;
+ grn_cond cond;
+ grn_com_queue recv_old;
+ grn_msg_handler *msg_handler;
+ grn_com_addr curr_edge_id;
+ grn_com *acceptor;
+ void *opaque;
+#ifndef USE_SELECT
+#ifdef USE_EPOLL
+ int epfd;
+ struct epoll_event *events;
+#else /* USE_EPOLL */
+#ifdef USE_KQUEUE
+ int kqfd;
+ struct kevent *events;
+#else /* USE_KQUEUE */
+ int dummy; /* dummy */
+ struct pollfd *events;
+#endif /* USE_KQUEUE */
+#endif /* USE_EPOLL */
+#endif /* USE_SELECT */
+};
+
+grn_rc grn_com_init(void);
+void grn_com_fin(void);
+GRN_API grn_rc grn_com_event_init(grn_ctx *ctx, grn_com_event *ev, int max_nevents, int data_size);
+GRN_API grn_rc grn_com_event_fin(grn_ctx *ctx, grn_com_event *ev);
+GRN_API grn_rc grn_com_event_start_accept(grn_ctx *ctx, grn_com_event *ev);
+grn_rc grn_com_event_stop_accept(grn_ctx *ctx, grn_com_event *ev);
+grn_rc grn_com_event_add(grn_ctx *ctx, grn_com_event *ev, grn_sock fd, int events, grn_com **com);
+grn_rc grn_com_event_mod(grn_ctx *ctx, grn_com_event *ev, grn_sock fd, int events, grn_com **com);
+GRN_API grn_rc grn_com_event_del(grn_ctx *ctx, grn_com_event *ev, grn_sock fd);
+GRN_API grn_rc grn_com_event_poll(grn_ctx *ctx, grn_com_event *ev, int timeout);
+grn_rc grn_com_event_each(grn_ctx *ctx, grn_com_event *ev, grn_com_callback *func);
+
+/******* grn_com_gqtp ********/
+
+#define GRN_COM_PROTO_HTTP 0x47
+#define GRN_COM_PROTO_GQTP 0xc7
+#define GRN_COM_PROTO_MBREQ 0x80
+#define GRN_COM_PROTO_MBRES 0x81
+
+typedef struct _grn_com_header grn_com_header;
+
+struct _grn_com_header {
+ uint8_t proto;
+ uint8_t qtype;
+ uint16_t keylen;
+ uint8_t level;
+ uint8_t flags;
+ uint16_t status;
+ uint32_t size;
+ uint32_t opaque;
+ uint64_t cas;
+};
+
+GRN_API grn_com *grn_com_copen(grn_ctx *ctx, grn_com_event *ev, const char *dest, int port);
+GRN_API grn_rc grn_com_sopen(grn_ctx *ctx, grn_com_event *ev,
+ const char *bind_address, int port,
+ grn_msg_handler *func, struct hostent *he);
+
+GRN_API void grn_com_close_(grn_ctx *ctx, grn_com *com);
+GRN_API grn_rc grn_com_close(grn_ctx *ctx, grn_com *com);
+
+GRN_API grn_rc grn_com_send(grn_ctx *ctx, grn_com *cs,
+ grn_com_header *header, const char *body, uint32_t size, int flags);
+grn_rc grn_com_recv(grn_ctx *ctx, grn_com *cs, grn_com_header *header, grn_obj *buf);
+GRN_API grn_rc grn_com_send_http(grn_ctx *ctx, grn_com *cs, const char *path, uint32_t path_len, int flags);
+
+/******* grn_msg ********/
+
+typedef struct _grn_msg grn_msg;
+
+struct _grn_msg {
+ grn_com_queue_entry qe;
+ union {
+ grn_com *peer;
+ grn_sock fd;
+ } u;
+ grn_ctx *ctx;
+ grn_com_queue *old;
+ grn_com_header header;
+ grn_com_addr edge_id;
+ grn_com *acceptor;
+};
+
+GRN_API grn_rc grn_msg_send(grn_ctx *ctx, grn_obj *msg, int flags);
+GRN_API grn_obj *grn_msg_open_for_reply(grn_ctx *ctx, grn_obj *query, grn_com_queue *old);
+GRN_API grn_obj *grn_msg_open(grn_ctx *ctx, grn_com *com, grn_com_queue *old);
+GRN_API grn_rc grn_msg_set_property(grn_ctx *ctx, grn_obj *obj,
+ uint16_t status, uint32_t key_size, uint8_t extra_size);
+GRN_API grn_rc grn_msg_close(grn_ctx *ctx, grn_obj *msg);
+
+/******* grn_edge ********/
+
+#define GRN_EDGE_WORKER 0
+#define GRN_EDGE_COMMUNICATOR 1
+
+typedef struct {
+ grn_com_queue_entry eq;
+ grn_ctx ctx;
+ grn_com_queue recv_new;
+ grn_com_queue send_old;
+ grn_com *com;
+ grn_com_addr *addr;
+ grn_msg *msg;
+ uint8_t stat;
+ uint8_t flags;
+ grn_id id;
+} grn_edge;
+
+GRN_VAR grn_hash *grn_edges;
+GRN_API void grn_edges_init(grn_ctx *ctx, void (*dispatcher)(grn_ctx *ctx, grn_edge *edge));
+GRN_API void grn_edges_fin(grn_ctx *ctx);
+GRN_API grn_edge *grn_edges_add(grn_ctx *ctx, grn_com_addr *addr, int *added);
+grn_edge *grn_edges_add_communicator(grn_ctx *ctx, grn_com_addr *addr);
+GRN_API void grn_edges_delete(grn_ctx *ctx, grn_edge *edge);
+void grn_edge_dispatch(grn_ctx *ctx, grn_edge *edge, grn_obj *msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_COM_H */
diff --git a/storage/mroonga/vendor/groonga/lib/ctx.c b/storage/mroonga/vendor/groonga/lib/ctx.c
new file mode 100644
index 00000000000..a419081635f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/ctx.c
@@ -0,0 +1,2992 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2009-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "groonga_in.h"
+#include <string.h>
+#include "token.h"
+#include "ctx_impl.h"
+#include "pat.h"
+#include "plugin_in.h"
+#include "snip.h"
+#include "output.h"
+#include "normalizer_in.h"
+#include "ctx_impl_mrb.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#define GRN_CTX_INITIALIZER(enc) \
+ { GRN_SUCCESS, 0, enc, 0, GRN_LOG_NOTICE,\
+ GRN_CTX_FIN, 0, 0, 0, 0, {0}, NULL, NULL, NULL, NULL, NULL }
+
+#define GRN_CTX_CLOSED(ctx) ((ctx)->stat == GRN_CTX_FIN)
+
+#ifdef USE_EXACT_ALLOC_COUNT
+#define GRN_ADD_ALLOC_COUNT(count) do { \
+ uint32_t alloced; \
+ GRN_ATOMIC_ADD_EX(&alloc_count, count, alloced); \
+} while (0)
+#else /* USE_EXACT_ALLOC_COUNT */
+#define GRN_ADD_ALLOC_COUNT(count) do { \
+ alloc_count += count; \
+} while (0)
+#endif
+
+grn_ctx grn_gctx = GRN_CTX_INITIALIZER(GRN_ENC_DEFAULT);
+int grn_pagesize;
+grn_critical_section grn_glock;
+uint32_t grn_gtick;
+int grn_lock_timeout = GRN_LOCK_TIMEOUT;
+
+#ifdef USE_UYIELD
+int grn_uyield_count = 0;
+#endif
+
+void
+grn_sleep(uint32_t seconds)
+{
+#ifdef WIN32
+ Sleep(seconds * 1000);
+#else // WIN32
+ sleep(seconds);
+#endif // WIN32
+}
+
+void
+grn_nanosleep(uint64_t nanoseconds)
+{
+#ifdef WIN32
+ Sleep((DWORD)(nanoseconds / 1000000));
+#else // WIN32
+ struct timespec interval;
+ interval.tv_sec = (time_t)(nanoseconds / 1000000000);
+ interval.tv_nsec = (long)(nanoseconds % 1000000000);
+ nanosleep(&interval, NULL);
+#endif // WIN32
+}
+
+/* fixme by 2038 */
+
+grn_rc
+grn_timeval_now(grn_ctx *ctx, grn_timeval *tv)
+{
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec t;
+ if (clock_gettime(CLOCK_REALTIME, &t)) {
+ SERR("clock_gettime");
+ } else {
+ tv->tv_sec = t.tv_sec;
+ tv->tv_nsec = t.tv_nsec;
+ }
+ return ctx->rc;
+#else /* HAVE_CLOCK_GETTIME */
+#ifdef WIN32
+ time_t t;
+ struct _timeb tb;
+ time(&t);
+ _ftime(&tb);
+ tv->tv_sec = t;
+ tv->tv_nsec = tb.millitm * (GRN_TIME_NSEC_PER_SEC / 1000);
+ return GRN_SUCCESS;
+#else /* WIN32 */
+ struct timeval t;
+ if (gettimeofday(&t, NULL)) {
+ SERR("gettimeofday");
+ } else {
+ tv->tv_sec = t.tv_sec;
+ tv->tv_nsec = GRN_TIME_USEC_TO_NSEC(t.tv_usec);
+ }
+ return ctx->rc;
+#endif /* WIN32 */
+#endif /* HAVE_CLOCK_GETTIME */
+}
+
+void
+grn_time_now(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_timeval tv;
+ grn_timeval_now(ctx, &tv);
+ GRN_TIME_SET(ctx, obj, GRN_TIME_PACK(tv.tv_sec,
+ GRN_TIME_NSEC_TO_USEC(tv.tv_nsec)));
+}
+
+grn_rc
+grn_timeval2str(grn_ctx *ctx, grn_timeval *tv, char *buf)
+{
+ struct tm *ltm;
+#ifdef HAVE_LOCALTIME_R
+ struct tm tm;
+ time_t t = tv->tv_sec;
+ ltm = localtime_r(&t, &tm);
+#else /* HAVE_LOCALTIME_R */
+ time_t tvsec = (time_t) tv->tv_sec;
+ ltm = localtime(&tvsec);
+#endif /* HAVE_LOCALTIME_R */
+ if (!ltm) { SERR("localtime"); }
+ snprintf(buf, GRN_TIMEVAL_STR_SIZE - 1, GRN_TIMEVAL_STR_FORMAT,
+ ltm->tm_year + 1900, ltm->tm_mon + 1, ltm->tm_mday,
+ ltm->tm_hour, ltm->tm_min, ltm->tm_sec,
+ (int)(GRN_TIME_NSEC_TO_USEC(tv->tv_nsec)));
+ buf[GRN_TIMEVAL_STR_SIZE - 1] = '\0';
+ return ctx->rc;
+}
+
+grn_rc
+grn_str2timeval(const char *str, uint32_t str_len, grn_timeval *tv)
+{
+ struct tm tm;
+ const char *r1, *r2, *rend = str + str_len;
+ uint32_t uv;
+ memset(&tm, 0, sizeof(struct tm));
+
+ tm.tm_year = (int)grn_atoui(str, rend, &r1) - 1900;
+ if ((r1 + 1) >= rend || (*r1 != '/' && *r1 != '-') ||
+ tm.tm_year < 0) { return GRN_INVALID_ARGUMENT; }
+ r1++;
+ tm.tm_mon = (int)grn_atoui(r1, rend, &r1) - 1;
+ if ((r1 + 1) >= rend || (*r1 != '/' && *r1 != '-') ||
+ tm.tm_mon < 0 || tm.tm_mon >= 12) { return GRN_INVALID_ARGUMENT; }
+ r1++;
+ tm.tm_mday = (int)grn_atoui(r1, rend, &r1);
+ if ((r1 + 1) >= rend || *r1 != ' ' ||
+ tm.tm_mday < 1 || tm.tm_mday > 31) { return GRN_INVALID_ARGUMENT; }
+
+ tm.tm_hour = (int)grn_atoui(++r1, rend, &r2);
+ if ((r2 + 1) >= rend || r1 == r2 || *r2 != ':' ||
+ tm.tm_hour < 0 || tm.tm_hour >= 24) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ r1 = r2 + 1;
+ tm.tm_min = (int)grn_atoui(r1, rend, &r2);
+ if ((r2 + 1) >= rend || r1 == r2 || *r2 != ':' ||
+ tm.tm_min < 0 || tm.tm_min >= 60) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ r1 = r2 + 1;
+ tm.tm_sec = (int)grn_atoui(r1, rend, &r2);
+ if (r1 == r2 ||
+ tm.tm_sec < 0 || tm.tm_sec > 61 /* leap 2sec */) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ r1 = r2;
+ tm.tm_yday = -1;
+ tm.tm_isdst = -1;
+
+ /* tm_yday is set appropriately (0-365) on successful completion. */
+ tv->tv_sec = mktime(&tm);
+ if (tm.tm_yday == -1) { return GRN_INVALID_ARGUMENT; }
+ if ((r1 + 1) < rend && *r1 == '.') { r1++; }
+ uv = grn_atoi(r1, rend, &r2);
+ while (r2 < r1 + 6) {
+ uv *= 10;
+ r2++;
+ }
+ if (uv >= GRN_TIME_USEC_PER_SEC) { return GRN_INVALID_ARGUMENT; }
+ tv->tv_nsec = GRN_TIME_USEC_TO_NSEC(uv);
+ return GRN_SUCCESS;
+}
+
+#ifdef USE_MEMORY_DEBUG
+inline static void
+grn_alloc_info_set_backtrace(char *buffer, size_t size)
+{
+# define N_TRACE_LEVEL 100
+ static void *trace[N_TRACE_LEVEL];
+ char **symbols;
+ int i, n, rest;
+
+ rest = size;
+ n = backtrace(trace, N_TRACE_LEVEL);
+ symbols = backtrace_symbols(trace, n);
+ if (symbols) {
+ for (i = 0; i < n; i++) {
+ int symbol_length;
+
+ symbol_length = strlen(symbols[i]);
+ if (symbol_length + 2 > rest) {
+ break;
+ }
+ memcpy(buffer, symbols[i], symbol_length);
+ buffer += symbol_length;
+ rest -= symbol_length;
+ buffer[0] = '\n';
+ buffer++;
+ rest--;
+ buffer[0] = '\0';
+ rest--;
+ }
+ free(symbols);
+ } else {
+ buffer[0] = '\0';
+ }
+# undef N_TRACE_LEVEL
+}
+
+inline static void
+grn_alloc_info_add(void *address)
+{
+ grn_ctx *ctx;
+ grn_alloc_info *new_alloc_info;
+
+ ctx = &grn_gctx;
+ if (!ctx->impl) { return; }
+
+ new_alloc_info = malloc(sizeof(grn_alloc_info));
+ new_alloc_info->address = address;
+ new_alloc_info->freed = GRN_FALSE;
+ grn_alloc_info_set_backtrace(new_alloc_info->alloc_backtrace,
+ sizeof(new_alloc_info->alloc_backtrace));
+ new_alloc_info->next = ctx->impl->alloc_info;
+ ctx->impl->alloc_info = new_alloc_info;
+}
+
+inline static void
+grn_alloc_info_change(void *old_address, void *new_address)
+{
+ grn_ctx *ctx;
+ grn_alloc_info *alloc_info;
+
+ ctx = &grn_gctx;
+ if (!ctx->impl) { return; }
+
+ alloc_info = ctx->impl->alloc_info;
+ for (; alloc_info; alloc_info = alloc_info->next) {
+ if (alloc_info->address == old_address) {
+ alloc_info->address = new_address;
+ grn_alloc_info_set_backtrace(alloc_info->alloc_backtrace,
+ sizeof(alloc_info->alloc_backtrace));
+ }
+ }
+}
+
+inline static void
+grn_alloc_info_dump(grn_ctx *ctx)
+{
+ int i = 0;
+ grn_alloc_info *alloc_info;
+
+ if (!ctx) { return; }
+ if (!ctx->impl) { return; }
+
+ alloc_info = ctx->impl->alloc_info;
+ for (; alloc_info; alloc_info = alloc_info->next) {
+ if (alloc_info->freed) {
+ printf("address[%d][freed]: %p\n", i, alloc_info->address);
+ } else {
+ printf("address[%d][not-freed]: %p:\n%s",
+ i, alloc_info->address, alloc_info->alloc_backtrace);
+ }
+ i++;
+ }
+}
+
+inline static void
+grn_alloc_info_check(void *address)
+{
+ grn_ctx *ctx;
+ grn_alloc_info *alloc_info;
+
+ ctx = &grn_gctx;
+ if (!ctx->impl) { return; }
+ /* grn_alloc_info_dump(ctx); */
+
+ alloc_info = ctx->impl->alloc_info;
+ for (; alloc_info; alloc_info = alloc_info->next) {
+ if (alloc_info->address == address) {
+ if (alloc_info->freed) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "double free: (%p):\nalloc backtrace:\n%sfree backtrace:\n%s",
+ alloc_info->address,
+ alloc_info->alloc_backtrace,
+ alloc_info->free_backtrace);
+ } else {
+ alloc_info->freed = GRN_TRUE;
+ grn_alloc_info_set_backtrace(alloc_info->free_backtrace,
+ sizeof(alloc_info->free_backtrace));
+ }
+ return;
+ }
+ }
+}
+
+inline static void
+grn_alloc_info_free(grn_ctx *ctx)
+{
+ grn_alloc_info *alloc_info;
+
+ if (!ctx) { return; }
+ if (!ctx->impl) { return; }
+
+ alloc_info = ctx->impl->alloc_info;
+ while (alloc_info) {
+ grn_alloc_info *current_alloc_info = alloc_info;
+ alloc_info = alloc_info->next;
+ current_alloc_info->next = NULL;
+ free(current_alloc_info);
+ }
+ ctx->impl->alloc_info = NULL;
+}
+
+#else /* USE_MEMORY_DEBUG */
+# define grn_alloc_info_add(address)
+# define grn_alloc_info_change(old_address, new_address)
+# define grn_alloc_info_check(address)
+# define grn_alloc_info_dump(ctx)
+# define grn_alloc_info_free(ctx)
+#endif /* USE_MEMORY_DEBUG */
+
+#ifdef USE_FAIL_MALLOC
+int grn_fmalloc_prob = 0;
+char *grn_fmalloc_func = NULL;
+char *grn_fmalloc_file = NULL;
+int grn_fmalloc_line = 0;
+#endif /* USE_FAIL_MALLOC */
+
+#define GRN_CTX_SEGMENT_SIZE (1<<22)
+#define GRN_CTX_SEGMENT_MASK (GRN_CTX_SEGMENT_SIZE - 1)
+
+#define GRN_CTX_SEGMENT_WORD (1<<31)
+#define GRN_CTX_SEGMENT_VLEN (1<<30)
+#define GRN_CTX_SEGMENT_LIFO (1<<29)
+#define GRN_CTX_SEGMENT_DIRTY (1<<28)
+
+#ifdef USE_DYNAMIC_MALLOC_CHANGE
+static void
+grn_ctx_impl_init_malloc(grn_ctx *ctx)
+{
+# ifdef USE_FAIL_MALLOC
+ ctx->impl->malloc_func = grn_malloc_fail;
+ ctx->impl->calloc_func = grn_calloc_fail;
+ ctx->impl->realloc_func = grn_realloc_fail;
+ ctx->impl->strdup_func = grn_strdup_fail;
+# else
+ ctx->impl->malloc_func = grn_malloc_default;
+ ctx->impl->calloc_func = grn_calloc_default;
+ ctx->impl->realloc_func = grn_realloc_default;
+ ctx->impl->strdup_func = grn_strdup_default;
+# endif
+}
+#endif
+
+static void
+grn_loader_init(grn_loader *loader)
+{
+ GRN_TEXT_INIT(&loader->values, 0);
+ GRN_UINT32_INIT(&loader->level, GRN_OBJ_VECTOR);
+ GRN_PTR_INIT(&loader->columns, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ loader->key_offset = -1;
+ loader->table = NULL;
+ loader->last = NULL;
+ loader->ifexists = NULL;
+ loader->each = NULL;
+ loader->values_size = 0;
+ loader->nrecords = 0;
+ loader->stat = GRN_LOADER_BEGIN;
+}
+
+void
+grn_ctx_loader_clear(grn_ctx *ctx)
+{
+ grn_loader *loader = &ctx->impl->loader;
+ grn_obj *v = (grn_obj *)(GRN_BULK_HEAD(&loader->values));
+ grn_obj *ve = (grn_obj *)(GRN_BULK_CURR(&loader->values));
+ grn_obj **p = (grn_obj **)GRN_BULK_HEAD(&loader->columns);
+ uint32_t i = GRN_BULK_VSIZE(&loader->columns) / sizeof(grn_obj *);
+ if (ctx->impl->db) { while (i--) { grn_obj_unlink(ctx, *p++); } }
+ if (loader->ifexists) { grn_obj_unlink(ctx, loader->ifexists); }
+ if (loader->each) { grn_obj_unlink(ctx, loader->each); }
+ while (v < ve) { GRN_OBJ_FIN(ctx, v++); }
+ GRN_OBJ_FIN(ctx, &loader->values);
+ GRN_OBJ_FIN(ctx, &loader->level);
+ GRN_OBJ_FIN(ctx, &loader->columns);
+ grn_loader_init(loader);
+}
+
+#define IMPL_SIZE ((sizeof(struct _grn_ctx_impl) + (grn_pagesize - 1)) & ~(grn_pagesize - 1))
+
+#ifdef GRN_WITH_MESSAGE_PACK
+static inline int
+grn_msgpack_buffer_write(void *data, const char *buf, unsigned int len)
+{
+ grn_ctx *ctx = (grn_ctx *)data;
+ return grn_bulk_write(ctx, ctx->impl->outbuf, buf, len);
+}
+#endif
+
+static void
+grn_ctx_impl_init(grn_ctx *ctx)
+{
+
+ grn_io_mapinfo mi;
+ if (!(ctx->impl = grn_io_anon_map(ctx, &mi, IMPL_SIZE))) {
+ ctx->impl = NULL;
+ return;
+ }
+#ifdef USE_DYNAMIC_MALLOC_CHANGE
+ grn_ctx_impl_init_malloc(ctx);
+#endif
+#ifdef USE_MEMORY_DEBUG
+ ctx->impl->alloc_info = NULL;
+#endif
+ ctx->impl->encoding = ctx->encoding;
+ ctx->impl->lifoseg = -1;
+ ctx->impl->currseg = -1;
+ CRITICAL_SECTION_INIT(ctx->impl->lock);
+ if (!(ctx->impl->values = grn_array_create(ctx, NULL, sizeof(grn_db_obj *),
+ GRN_ARRAY_TINY))) {
+ CRITICAL_SECTION_FIN(ctx->impl->lock);
+ grn_io_anon_unmap(ctx, &mi, IMPL_SIZE);
+ ctx->impl = NULL;
+ return;
+ }
+ if (!(ctx->impl->ios = grn_hash_create(ctx, NULL, GRN_TABLE_MAX_KEY_SIZE,
+ sizeof(grn_io *),
+ GRN_OBJ_KEY_VAR_SIZE|GRN_HASH_TINY))) {
+ grn_array_close(ctx, ctx->impl->values);
+ CRITICAL_SECTION_FIN(ctx->impl->lock);
+ grn_io_anon_unmap(ctx, &mi, IMPL_SIZE);
+ ctx->impl = NULL;
+ return;
+ }
+ ctx->impl->db = NULL;
+
+ ctx->impl->expr_vars = grn_hash_create(ctx, NULL, sizeof(grn_id), sizeof(grn_obj *), 0);
+ ctx->impl->stack_curr = 0;
+ ctx->impl->curr_expr = NULL;
+ ctx->impl->qe_next = NULL;
+ ctx->impl->parser = NULL;
+
+ GRN_TEXT_INIT(&ctx->impl->names, GRN_OBJ_VECTOR);
+ GRN_UINT32_INIT(&ctx->impl->levels, GRN_OBJ_VECTOR);
+
+ if (ctx == &grn_gctx) {
+ ctx->impl->command_version = GRN_COMMAND_VERSION_STABLE;
+ } else {
+ ctx->impl->command_version = grn_get_default_command_version();
+ }
+
+ if (ctx == &grn_gctx) {
+ ctx->impl->match_escalation_threshold =
+ GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD;
+ } else {
+ ctx->impl->match_escalation_threshold =
+ grn_get_default_match_escalation_threshold();
+ }
+
+ ctx->impl->finalizer = NULL;
+
+ ctx->impl->com = NULL;
+ ctx->impl->outbuf = grn_obj_open(ctx, GRN_BULK, 0, 0);
+ ctx->impl->output = NULL;
+ ctx->impl->data.ptr = NULL;
+ ctx->impl->tv.tv_sec = 0;
+ ctx->impl->tv.tv_nsec = 0;
+ ctx->impl->edge = NULL;
+ grn_loader_init(&ctx->impl->loader);
+ ctx->impl->plugin_path = NULL;
+
+ GRN_TEXT_INIT(&ctx->impl->query_log_buf, 0);
+
+ ctx->impl->previous_errbuf[0] = '\0';
+ ctx->impl->n_same_error_messages = 0;
+
+#ifdef GRN_WITH_MESSAGE_PACK
+ msgpack_packer_init(&ctx->impl->msgpacker, ctx, grn_msgpack_buffer_write);
+#endif
+
+ grn_ctx_impl_mrb_init(ctx);
+}
+
+void
+grn_ctx_set_next_expr(grn_ctx *ctx, grn_obj *expr)
+{
+ ctx->impl->qe_next = expr;
+}
+
+static void
+grn_ctx_impl_clear_n_same_error_mssagges(grn_ctx *ctx)
+{
+ if (ctx->impl->n_same_error_messages == 0) {
+ return;
+ }
+
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "(%u same messages are truncated)",
+ ctx->impl->n_same_error_messages);
+ ctx->impl->n_same_error_messages = 0;
+}
+
+grn_bool
+grn_ctx_impl_should_log(grn_ctx *ctx)
+{
+ if (!ctx->impl) {
+ return GRN_TRUE;
+ }
+
+ if (strcmp(ctx->errbuf, ctx->impl->previous_errbuf) == 0) {
+ ctx->impl->n_same_error_messages++;
+ return GRN_FALSE;
+ }
+
+ return GRN_TRUE;
+}
+
+void
+grn_ctx_impl_set_current_error_message(grn_ctx *ctx)
+{
+ if (!ctx->impl) {
+ return;
+ }
+
+ grn_ctx_impl_clear_n_same_error_mssagges(ctx);
+ strcpy(ctx->impl->previous_errbuf, ctx->errbuf);
+}
+
+grn_rc
+grn_ctx_init(grn_ctx *ctx, int flags)
+{
+ if (!ctx) { return GRN_INVALID_ARGUMENT; }
+ // if (ctx->stat != GRN_CTX_FIN) { return GRN_INVALID_ARGUMENT; }
+ ERRCLR(ctx);
+ ctx->flags = flags;
+ if (getenv("GRN_CTX_PER_DB") && strcmp(getenv("GRN_CTX_PER_DB"), "yes") == 0) {
+ ctx->flags |= GRN_CTX_PER_DB;
+ }
+ if (ERRP(ctx, GRN_ERROR)) { return ctx->rc; }
+ ctx->stat = GRN_CTX_INITED;
+ ctx->encoding = grn_gctx.encoding;
+ ctx->seqno = 0;
+ ctx->seqno2 = 0;
+ ctx->subno = 0;
+ ctx->impl = NULL;
+ ctx->user_data.ptr = NULL;
+ CRITICAL_SECTION_ENTER(grn_glock);
+ ctx->next = grn_gctx.next;
+ ctx->prev = &grn_gctx;
+ grn_gctx.next->prev = ctx;
+ grn_gctx.next = ctx;
+ CRITICAL_SECTION_LEAVE(grn_glock);
+ ctx->errline = 0;
+ ctx->errfile = "";
+ ctx->errfunc = "";
+ ctx->trace[0] = NULL;
+ ctx->errbuf[0] = '\0';
+ return ctx->rc;
+}
+
+grn_ctx *
+grn_ctx_open(int flags)
+{
+ grn_ctx *ctx = GRN_GMALLOCN(grn_ctx, 1);
+ if (ctx) {
+ grn_ctx_init(ctx, flags|GRN_CTX_ALLOCATED);
+ if (ERRP(ctx, GRN_ERROR)) {
+ grn_ctx_fin(ctx);
+ GRN_GFREE(ctx);
+ ctx = NULL;
+ }
+ }
+ return ctx;
+}
+
+grn_rc
+grn_ctx_fin(grn_ctx *ctx)
+{
+ grn_rc rc = GRN_SUCCESS;
+ if (!ctx) { return GRN_INVALID_ARGUMENT; }
+ if (ctx->stat == GRN_CTX_FIN) { return GRN_INVALID_ARGUMENT; }
+ if (!(ctx->flags & GRN_CTX_ALLOCATED)) {
+ CRITICAL_SECTION_ENTER(grn_glock);
+ ctx->next->prev = ctx->prev;
+ ctx->prev->next = ctx->next;
+ CRITICAL_SECTION_LEAVE(grn_glock);
+ }
+ if (ctx->impl) {
+ grn_ctx_impl_clear_n_same_error_mssagges(ctx);
+ if (ctx->impl->finalizer) {
+ ctx->impl->finalizer(ctx, 0, NULL, &(ctx->user_data));
+ }
+ grn_ctx_impl_mrb_fin(ctx);
+ grn_ctx_loader_clear(ctx);
+ if (ctx->impl->parser) {
+ grn_expr_parser_close(ctx);
+ }
+ if (ctx->impl->values) {
+#ifndef USE_MEMORY_DEBUG
+ grn_db_obj *o;
+ GRN_ARRAY_EACH(ctx, ctx->impl->values, 0, 0, id, &o, {
+ grn_obj_close(ctx, *((grn_obj **)o));
+ });
+#endif
+ grn_array_close(ctx, ctx->impl->values);
+ }
+ if (ctx->impl->ios) {
+ grn_hash_close(ctx, ctx->impl->ios);
+ }
+ if (ctx->impl->com) {
+ if (ctx->stat != GRN_CTX_QUIT) {
+ int flags;
+ char *str;
+ unsigned int str_len;
+ grn_ctx_send(ctx, "quit", 4, GRN_CTX_HEAD);
+ grn_ctx_recv(ctx, &str, &str_len, &flags);
+ }
+ grn_ctx_send(ctx, "ACK", 3, GRN_CTX_HEAD);
+ rc = grn_com_close(ctx, ctx->impl->com);
+ }
+ GRN_OBJ_FIN(ctx, &ctx->impl->names);
+ GRN_OBJ_FIN(ctx, &ctx->impl->levels);
+ GRN_OBJ_FIN(ctx, &ctx->impl->query_log_buf);
+ rc = grn_obj_close(ctx, ctx->impl->outbuf);
+ {
+ grn_hash **vp;
+ grn_obj *value;
+ GRN_HASH_EACH(ctx, ctx->impl->expr_vars, eid, NULL, NULL, &vp, {
+ if (*vp) {
+ GRN_HASH_EACH(ctx, *vp, id, NULL, NULL, &value, {
+ GRN_OBJ_FIN(ctx, value);
+ });
+ }
+ grn_hash_close(ctx, *vp);
+ });
+ }
+ grn_hash_close(ctx, ctx->impl->expr_vars);
+ if (ctx->impl->db && ctx->flags & GRN_CTX_PER_DB) {
+ grn_obj *db = ctx->impl->db;
+ ctx->impl->db = NULL;
+ grn_obj_close(ctx, db);
+ }
+ {
+ int i;
+ grn_io_mapinfo *mi;
+ for (i = 0, mi = ctx->impl->segs; i < GRN_CTX_N_SEGMENTS; i++, mi++) {
+ if (mi->map) {
+ //GRN_LOG(ctx, GRN_LOG_NOTICE, "unmap in ctx_fin(%d,%d,%d)", i, (mi->count & GRN_CTX_SEGMENT_MASK), mi->nref);
+ if (mi->count & GRN_CTX_SEGMENT_VLEN) {
+ grn_io_anon_unmap(ctx, mi, mi->nref * grn_pagesize);
+ } else {
+ grn_io_anon_unmap(ctx, mi, GRN_CTX_SEGMENT_SIZE);
+ }
+ }
+ }
+ }
+ grn_alloc_info_dump(ctx);
+ grn_alloc_info_free(ctx);
+ CRITICAL_SECTION_FIN(ctx->impl->lock);
+ {
+ grn_io_mapinfo mi;
+ mi.map = (void *)ctx->impl;
+ grn_io_anon_unmap(ctx, &mi, IMPL_SIZE);
+ }
+ ctx->impl = NULL;
+ }
+ ctx->stat = GRN_CTX_FIN;
+ return rc;
+}
+
+grn_rc
+grn_ctx_set_finalizer(grn_ctx *ctx, grn_proc_func *finalizer)
+{
+ if (!ctx) { return GRN_INVALID_ARGUMENT; }
+ if (!ctx->impl) {
+ grn_ctx_impl_init(ctx);
+ if (ERRP(ctx, GRN_ERROR)) { return ctx->rc; }
+ }
+ ctx->impl->finalizer = finalizer;
+ return GRN_SUCCESS;
+}
+
+grn_timeval grn_starttime;
+
+static char *default_logger_path = NULL;
+static FILE *default_logger_file = NULL;
+static grn_critical_section default_logger_lock;
+
+static void
+default_logger_log(grn_ctx *ctx, grn_log_level level,
+ const char *timestamp, const char *title,
+ const char *message, const char *location, void *user_data)
+{
+ const char slev[] = " EACewnid-";
+ if (default_logger_path) {
+ CRITICAL_SECTION_ENTER(default_logger_lock);
+ if (!default_logger_file) {
+ default_logger_file = fopen(default_logger_path, "a");
+ }
+ if (default_logger_file) {
+ if (location && *location) {
+ fprintf(default_logger_file, "%s|%c|%s %s %s\n",
+ timestamp, *(slev + level), title, message, location);
+ } else {
+ fprintf(default_logger_file, "%s|%c|%s %s\n", timestamp,
+ *(slev + level), title, message);
+ }
+ fflush(default_logger_file);
+ }
+ CRITICAL_SECTION_LEAVE(default_logger_lock);
+ }
+}
+
+static void
+default_logger_reopen(grn_ctx *ctx, void *user_data)
+{
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "log will be closed.");
+ CRITICAL_SECTION_ENTER(default_logger_lock);
+ if (default_logger_file) {
+ fclose(default_logger_file);
+ default_logger_file = NULL;
+ }
+ CRITICAL_SECTION_LEAVE(default_logger_lock);
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "log opened.");
+}
+
+static void
+default_logger_fin(grn_ctx *ctx, void *user_data)
+{
+ CRITICAL_SECTION_ENTER(default_logger_lock);
+ if (default_logger_file) {
+ fclose(default_logger_file);
+ default_logger_file = NULL;
+ }
+ CRITICAL_SECTION_LEAVE(default_logger_lock);
+}
+
+static grn_logger default_logger = {
+ GRN_LOG_DEFAULT_LEVEL,
+ GRN_LOG_TIME|GRN_LOG_MESSAGE,
+ NULL,
+ default_logger_log,
+ default_logger_reopen,
+ default_logger_fin
+};
+
+static grn_logger current_logger = {
+ GRN_LOG_DEFAULT_LEVEL,
+ GRN_LOG_TIME|GRN_LOG_MESSAGE,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+void
+grn_default_logger_set_max_level(grn_log_level max_level)
+{
+ default_logger.max_level = max_level;
+ if (current_logger.log == default_logger_log) {
+ current_logger.max_level = max_level;
+ }
+}
+
+grn_log_level
+grn_default_logger_get_max_level(void)
+{
+ return default_logger.max_level;
+}
+
+void
+grn_default_logger_set_path(const char *path)
+{
+ if (default_logger_path) {
+ free(default_logger_path);
+ }
+
+ if (path) {
+ default_logger_path = strdup(path);
+ } else {
+ default_logger_path = NULL;
+ }
+}
+
+const char *
+grn_default_logger_get_path(void)
+{
+ return default_logger_path;
+}
+
+void
+grn_logger_reopen(grn_ctx *ctx)
+{
+ if (current_logger.reopen) {
+ current_logger.reopen(ctx, current_logger.user_data);
+ }
+}
+
+static void
+grn_logger_fin(grn_ctx *ctx)
+{
+ if (current_logger.fin) {
+ current_logger.fin(ctx, current_logger.user_data);
+ }
+}
+
+static void
+logger_info_func_wrapper(grn_ctx *ctx, grn_log_level level,
+ const char *timestamp, const char *title,
+ const char *message, const char *location,
+ void *user_data)
+{
+ grn_logger_info *info = user_data;
+ info->func(level, timestamp, title, message, location, info->func_arg);
+}
+
+/* Deprecated since 2.1.2. */
+grn_rc
+grn_logger_info_set(grn_ctx *ctx, const grn_logger_info *info)
+{
+ if (info) {
+ grn_logger logger;
+
+ memset(&logger, 0, sizeof(grn_logger));
+ logger.max_level = info->max_level;
+ logger.flags = info->flags;
+ if (info->func) {
+ logger.log = logger_info_func_wrapper;
+ logger.user_data = (grn_logger_info *)info;
+ } else {
+ logger.log = default_logger_log;
+ logger.reopen = default_logger_reopen;
+ logger.fin = default_logger_fin;
+ }
+ return grn_logger_set(ctx, &logger);
+ } else {
+ return grn_logger_set(ctx, NULL);
+ }
+}
+
+grn_rc
+grn_logger_set(grn_ctx *ctx, const grn_logger *logger)
+{
+ grn_logger_fin(ctx);
+ if (logger) {
+ current_logger = *logger;
+ } else {
+ current_logger = default_logger;
+ }
+ return GRN_SUCCESS;
+}
+
+void
+grn_logger_set_max_level(grn_ctx *ctx, grn_log_level max_level)
+{
+ current_logger.max_level = max_level;
+}
+
+grn_log_level
+grn_logger_get_max_level(grn_ctx *ctx)
+{
+ return current_logger.max_level;
+}
+
+grn_bool
+grn_logger_pass(grn_ctx *ctx, grn_log_level level)
+{
+ return level <= current_logger.max_level;
+}
+
+#define TBUFSIZE GRN_TIMEVAL_STR_SIZE
+#define MBUFSIZE 0x1000
+#define LBUFSIZE 0x400
+
+void
+grn_logger_put(grn_ctx *ctx, grn_log_level level,
+ const char *file, int line, const char *func, const char *fmt, ...)
+{
+ if (level <= current_logger.max_level && current_logger.log) {
+ char tbuf[TBUFSIZE];
+ char mbuf[MBUFSIZE];
+ char lbuf[LBUFSIZE];
+ tbuf[0] = '\0';
+ if (current_logger.flags & GRN_LOG_TIME) {
+ grn_timeval tv;
+ grn_timeval_now(ctx, &tv);
+ grn_timeval2str(ctx, &tv, tbuf);
+ }
+ if (current_logger.flags & GRN_LOG_MESSAGE) {
+ va_list argp;
+ va_start(argp, fmt);
+ vsnprintf(mbuf, MBUFSIZE - 1, fmt, argp);
+ va_end(argp);
+ mbuf[MBUFSIZE - 1] = '\0';
+ } else {
+ mbuf[0] = '\0';
+ }
+ if (current_logger.flags & GRN_LOG_LOCATION) {
+ snprintf(lbuf, LBUFSIZE - 1, "%d %s:%d %s()", getpid(), file, line, func);
+ lbuf[LBUFSIZE - 1] = '\0';
+ } else {
+ lbuf[0] = '\0';
+ }
+ current_logger.log(ctx, level, tbuf, "", mbuf, lbuf,
+ current_logger.user_data);
+ }
+}
+
+static void
+logger_init(void)
+{
+ if (!default_logger_path) {
+ default_logger_path = strdup(GRN_LOG_PATH);
+ }
+ memcpy(&current_logger, &default_logger, sizeof(grn_logger));
+ CRITICAL_SECTION_INIT(default_logger_lock);
+}
+
+static void
+logger_fin(grn_ctx *ctx)
+{
+ grn_logger_fin(ctx);
+ if (default_logger_path) {
+ free(default_logger_path);
+ default_logger_path = NULL;
+ }
+ CRITICAL_SECTION_FIN(default_logger_lock);
+}
+
+
+static char *default_query_logger_path = NULL;
+static FILE *default_query_logger_file = NULL;
+static grn_critical_section default_query_logger_lock;
+
+static void
+default_query_logger_log(grn_ctx *ctx, unsigned int flag,
+ const char *timestamp, const char *info,
+ const char *message, void *user_data)
+{
+ if (default_query_logger_path) {
+ CRITICAL_SECTION_ENTER(default_query_logger_lock);
+ if (!default_query_logger_file) {
+ default_query_logger_file = fopen(default_query_logger_path, "a");
+ }
+ if (default_query_logger_file) {
+ fprintf(default_query_logger_file, "%s|%s%s\n", timestamp, info, message);
+ fflush(default_query_logger_file);
+ }
+ CRITICAL_SECTION_LEAVE(default_query_logger_lock);
+ }
+}
+
+static void
+default_query_logger_close(grn_ctx *ctx, void *user_data)
+{
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_DESTINATION, " ",
+ "query log will be closed: <%s>", default_query_logger_path);
+ CRITICAL_SECTION_ENTER(default_query_logger_lock);
+ if (default_query_logger_file) {
+ fclose(default_query_logger_file);
+ default_query_logger_file = NULL;
+ }
+ CRITICAL_SECTION_LEAVE(default_query_logger_lock);
+}
+
+static void
+default_query_logger_reopen(grn_ctx *ctx, void *user_data)
+{
+ default_query_logger_close(ctx, user_data);
+ if (default_query_logger_path) {
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_DESTINATION, " ",
+ "query log is opened: <%s>", default_query_logger_path);
+ }
+}
+
+static void
+default_query_logger_fin(grn_ctx *ctx, void *user_data)
+{
+ if (default_query_logger_file) {
+ default_query_logger_close(ctx, user_data);
+ }
+}
+
+static grn_query_logger default_query_logger = {
+ GRN_QUERY_LOG_DEFAULT,
+ NULL,
+ default_query_logger_log,
+ default_query_logger_reopen,
+ default_query_logger_fin
+};
+
+static grn_query_logger current_query_logger = {
+ GRN_QUERY_LOG_DEFAULT,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+void
+grn_default_query_logger_set_flags(unsigned int flags)
+{
+ default_query_logger.flags = flags;
+ if (current_query_logger.log == default_query_logger_log) {
+ current_query_logger.flags = flags;
+ }
+}
+
+unsigned int
+grn_default_query_logger_get_flags(void)
+{
+ return default_query_logger.flags;
+}
+
+void
+grn_default_query_logger_set_path(const char *path)
+{
+ if (default_query_logger_path) {
+ free(default_query_logger_path);
+ }
+
+ if (path) {
+ default_query_logger_path = strdup(path);
+ } else {
+ default_query_logger_path = NULL;
+ }
+}
+
+const char *
+grn_default_query_logger_get_path(void)
+{
+ return default_query_logger_path;
+}
+
+void
+grn_query_logger_reopen(grn_ctx *ctx)
+{
+ if (current_query_logger.reopen) {
+ current_query_logger.reopen(ctx, current_query_logger.user_data);
+ }
+}
+
+static void
+grn_query_logger_fin(grn_ctx *ctx)
+{
+ if (current_query_logger.fin) {
+ current_query_logger.fin(ctx, current_query_logger.user_data);
+ }
+}
+
+grn_rc
+grn_query_logger_set(grn_ctx *ctx, const grn_query_logger *logger)
+{
+ grn_query_logger_fin(ctx);
+ if (logger) {
+ current_query_logger = *logger;
+ } else {
+ current_query_logger = default_query_logger;
+ }
+ return GRN_SUCCESS;
+}
+
+grn_bool
+grn_query_logger_pass(grn_ctx *ctx, unsigned int flag)
+{
+ return current_query_logger.flags & flag;
+}
+
+#define TIMESTAMP_BUFFER_SIZE TBUFSIZE
+/* 8+a(%p) + 1(|) + 1(mark) + 15(elapsed time) = 25+a */
+#define INFO_BUFFER_SIZE 40
+
+void
+grn_query_logger_put(grn_ctx *ctx, unsigned int flag, const char *mark,
+ const char *format, ...)
+{
+ char timestamp[TIMESTAMP_BUFFER_SIZE];
+ char info[INFO_BUFFER_SIZE];
+ grn_obj *message = &ctx->impl->query_log_buf;
+
+ if (!current_query_logger.log) {
+ return;
+ }
+
+ {
+ grn_timeval tv;
+ timestamp[0] = '\0';
+ grn_timeval_now(ctx, &tv);
+ grn_timeval2str(ctx, &tv, timestamp);
+ }
+
+ if (flag & (GRN_QUERY_LOG_COMMAND | GRN_QUERY_LOG_DESTINATION)) {
+ snprintf(info, INFO_BUFFER_SIZE - 1, "%p|%s", ctx, mark);
+ info[INFO_BUFFER_SIZE - 1] = '\0';
+ } else {
+ grn_timeval tv;
+ uint64_t elapsed_time;
+ grn_timeval_now(ctx, &tv);
+ elapsed_time =
+ (uint64_t)(tv.tv_sec - ctx->impl->tv.tv_sec) * GRN_TIME_NSEC_PER_SEC +
+ (tv.tv_nsec - ctx->impl->tv.tv_nsec);
+
+ snprintf(info, INFO_BUFFER_SIZE - 1,
+ "%p|%s%015" GRN_FMT_INT64U " ", ctx, mark, elapsed_time);
+ info[INFO_BUFFER_SIZE - 1] = '\0';
+ }
+
+ {
+ va_list args;
+
+ va_start(args, format);
+ GRN_BULK_REWIND(message);
+ grn_text_vprintf(ctx, message, format, args);
+ va_end(args);
+ GRN_TEXT_PUTC(ctx, message, '\0');
+ }
+
+ current_query_logger.log(ctx, flag, timestamp, info, GRN_TEXT_VALUE(message),
+ current_query_logger.user_data);
+}
+
+static void
+query_logger_init(void)
+{
+ memcpy(&current_query_logger, &default_query_logger, sizeof(grn_query_logger));
+ CRITICAL_SECTION_INIT(default_query_logger_lock);
+}
+
+static void
+query_logger_fin(grn_ctx *ctx)
+{
+ grn_query_logger_fin(ctx);
+ if (default_query_logger_path) {
+ free(default_query_logger_path);
+ }
+ CRITICAL_SECTION_FIN(default_query_logger_lock);
+}
+
+void
+grn_log_reopen(grn_ctx *ctx)
+{
+ grn_logger_reopen(ctx);
+ grn_query_logger_reopen(ctx);
+}
+
+
+static void
+check_overcommit_memory(grn_ctx *ctx)
+{
+ FILE *file;
+ int value;
+ file = fopen("/proc/sys/vm/overcommit_memory", "r");
+ if (!file) { return; }
+ value = fgetc(file);
+ if (value != '1') {
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "vm.overcommit_memory kernel parameter should be 1: <%c>: "
+ "See INFO level log to resolve this",
+ value);
+ GRN_LOG(ctx, GRN_LOG_INFO,
+ "Some processings with vm.overcommit_memory != 1 "
+ "may break DB under low memory condition.");
+ GRN_LOG(ctx, GRN_LOG_INFO,
+ "To set vm.overcommit_memory to 1");
+ GRN_LOG(ctx, GRN_LOG_INFO,
+ "add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and "
+ "restart your system or");
+ GRN_LOG(ctx, GRN_LOG_INFO,
+ "run 'sudo /sbin/sysctl vm.overcommit_memory=1' command.");
+ }
+ fclose(file);
+}
+
+static void
+check_grn_ja_skip_same_value_put(grn_ctx *ctx)
+{
+ const char *grn_ja_skip_same_value_put_env;
+
+ grn_ja_skip_same_value_put_env = getenv("GRN_JA_SKIP_SAME_VALUE_PUT");
+ if (grn_ja_skip_same_value_put_env &&
+ strcmp(grn_ja_skip_same_value_put_env, "no") == 0) {
+ grn_ja_skip_same_value_put = GRN_FALSE;
+ }
+}
+
+grn_rc
+grn_init(void)
+{
+ grn_rc rc;
+ grn_ctx *ctx = &grn_gctx;
+ logger_init();
+ query_logger_init();
+ CRITICAL_SECTION_INIT(grn_glock);
+ grn_gtick = 0;
+ ctx->next = ctx;
+ ctx->prev = ctx;
+ grn_ctx_init(ctx, 0);
+ ctx->encoding = grn_encoding_parse(GRN_DEFAULT_ENCODING);
+ grn_timeval_now(ctx, &grn_starttime);
+#ifdef WIN32
+ {
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ grn_pagesize = si.dwAllocationGranularity;
+ }
+#else /* WIN32 */
+ if ((grn_pagesize = sysconf(_SC_PAGESIZE)) == -1) {
+ SERR("_SC_PAGESIZE");
+ return ctx->rc;
+ }
+#endif /* WIN32 */
+ if (grn_pagesize & (grn_pagesize - 1)) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "pagesize=%x", grn_pagesize);
+ }
+ // expand_stack();
+#ifdef USE_FAIL_MALLOC
+ if (getenv("GRN_FMALLOC_PROB")) {
+ grn_fmalloc_prob = strtod(getenv("GRN_FMALLOC_PROB"), 0) * RAND_MAX;
+ if (getenv("GRN_FMALLOC_SEED")) {
+ srand((unsigned int)atoi(getenv("GRN_FMALLOC_SEED")));
+ } else {
+ srand((unsigned int)time(NULL));
+ }
+ }
+ if (getenv("GRN_FMALLOC_FUNC")) {
+ grn_fmalloc_func = getenv("GRN_FMALLOC_FUNC");
+ }
+ if (getenv("GRN_FMALLOC_FILE")) {
+ grn_fmalloc_file = getenv("GRN_FMALLOC_FILE");
+ }
+ if (getenv("GRN_FMALLOC_LINE")) {
+ grn_fmalloc_line = atoi(getenv("GRN_FMALLOC_LINE"));
+ }
+#endif /* USE_FAIL_MALLOC */
+ if ((rc = grn_com_init())) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_com_init failed (%d)", rc);
+ return rc;
+ }
+ grn_ctx_impl_init(ctx);
+ if ((rc = grn_io_init())) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "io initialize failed (%d)", rc);
+ return rc;
+ }
+ if ((rc = grn_plugins_init())) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "plugins initialize failed (%d)", rc);
+ return rc;
+ }
+ if ((rc = grn_normalizer_init())) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_normalizer_init failed (%d)", rc);
+ return rc;
+ }
+ if ((rc = grn_token_init())) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_token_init failed (%d)", rc);
+ return rc;
+ }
+ /*
+ if ((rc = grn_index_init())) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "index initialize failed (%d)", rc);
+ return rc;
+ }
+ */
+ grn_cache_init();
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "grn_init");
+ check_overcommit_memory(ctx);
+ check_grn_ja_skip_same_value_put(ctx);
+ return rc;
+}
+
+grn_encoding
+grn_get_default_encoding(void)
+{
+ return grn_gctx.encoding;
+}
+
+grn_rc
+grn_set_default_encoding(grn_encoding encoding)
+{
+ switch (encoding) {
+ case GRN_ENC_DEFAULT :
+ grn_gctx.encoding = grn_encoding_parse(GRN_DEFAULT_ENCODING);
+ return GRN_SUCCESS;
+ case GRN_ENC_NONE :
+ case GRN_ENC_EUC_JP :
+ case GRN_ENC_UTF8 :
+ case GRN_ENC_SJIS :
+ case GRN_ENC_LATIN1 :
+ case GRN_ENC_KOI8R :
+ grn_gctx.encoding = encoding;
+ return GRN_SUCCESS;
+ default :
+ return GRN_INVALID_ARGUMENT;
+ }
+}
+
+grn_command_version
+grn_get_default_command_version(void)
+{
+ return grn_ctx_get_command_version(&grn_gctx);
+}
+
+grn_rc
+grn_set_default_command_version(grn_command_version version)
+{
+ return grn_ctx_set_command_version(&grn_gctx, version);
+}
+
+long long int
+grn_get_default_match_escalation_threshold(void)
+{
+ return grn_ctx_get_match_escalation_threshold(&grn_gctx);
+}
+
+grn_rc
+grn_set_default_match_escalation_threshold(long long int threshold)
+{
+ return grn_ctx_set_match_escalation_threshold(&grn_gctx, threshold);
+}
+
+int
+grn_get_lock_timeout(void)
+{
+ return grn_lock_timeout;
+}
+
+grn_rc
+grn_set_lock_timeout(int timeout)
+{
+ grn_lock_timeout = timeout;
+ return GRN_SUCCESS;
+}
+
+static int alloc_count = 0;
+
+grn_rc
+grn_fin(void)
+{
+ grn_ctx *ctx, *ctx_;
+ if (grn_gctx.stat == GRN_CTX_FIN) { return GRN_INVALID_ARGUMENT; }
+ for (ctx = grn_gctx.next; ctx != &grn_gctx; ctx = ctx_) {
+ ctx_ = ctx->next;
+ if (ctx->stat != GRN_CTX_FIN) { grn_ctx_fin(ctx); }
+ if (ctx->flags & GRN_CTX_ALLOCATED) {
+ ctx->next->prev = ctx->prev;
+ ctx->prev->next = ctx->next;
+ GRN_GFREE(ctx);
+ }
+ }
+ query_logger_fin(ctx);
+ grn_cache_fin();
+ grn_token_fin();
+ grn_normalizer_fin();
+ grn_plugins_fin();
+ grn_io_fin();
+ grn_ctx_fin(ctx);
+ grn_com_fin();
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "grn_fin (%d)", alloc_count);
+ logger_fin(ctx);
+ CRITICAL_SECTION_FIN(grn_glock);
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ctx_connect(grn_ctx *ctx, const char *host, int port, int flags)
+{
+ GRN_API_ENTER;
+ if (!ctx->impl) { grn_ctx_impl_init(ctx); }
+ if (!ctx->impl) { goto exit; }
+ {
+ grn_com *com = grn_com_copen(ctx, NULL, host, port);
+ if (com) {
+ ctx->impl->com = com;
+ }
+ }
+exit :
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_rc
+grn_ctx_close(grn_ctx *ctx)
+{
+ grn_rc rc = grn_ctx_fin(ctx);
+ ctx->next->prev = ctx->prev;
+ ctx->prev->next = ctx->next;
+ GRN_GFREE(ctx);
+ return rc;
+}
+
+grn_command_version
+grn_ctx_get_command_version(grn_ctx *ctx)
+{
+ if (ctx->impl) {
+ return ctx->impl->command_version;
+ } else {
+ return GRN_COMMAND_VERSION_STABLE;
+ }
+}
+
+const char *
+grn_ctx_get_mime_type(grn_ctx *ctx)
+{
+ if (ctx->impl) {
+ return ctx->impl->mime_type;
+ } else {
+ return NULL;
+ }
+}
+
+grn_rc
+grn_ctx_set_command_version(grn_ctx *ctx, grn_command_version version)
+{
+ switch (version) {
+ case GRN_COMMAND_VERSION_DEFAULT :
+ ctx->impl->command_version = GRN_COMMAND_VERSION_STABLE;
+ return GRN_SUCCESS;
+ default :
+ if (GRN_COMMAND_VERSION_MIN <= version &&
+ version <= GRN_COMMAND_VERSION_MAX) {
+ ctx->impl->command_version = version;
+ return GRN_SUCCESS;
+ } else {
+ return GRN_UNSUPPORTED_COMMAND_VERSION;
+ }
+ }
+}
+
+long long int
+grn_ctx_get_match_escalation_threshold(grn_ctx *ctx)
+{
+ if (ctx->impl) {
+ return ctx->impl->match_escalation_threshold;
+ } else {
+ return GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD;
+ }
+}
+
+grn_rc
+grn_ctx_set_match_escalation_threshold(grn_ctx *ctx, long long int threshold)
+{
+ ctx->impl->match_escalation_threshold = threshold;
+ return GRN_SUCCESS;
+}
+
+grn_content_type
+grn_get_ctype(grn_obj *var)
+{
+ grn_content_type ct = GRN_CONTENT_JSON;
+ if (var->header.domain == GRN_DB_INT32) {
+ ct = GRN_INT32_VALUE(var);
+ } else if (GRN_TEXT_LEN(var)) {
+ switch (*(GRN_TEXT_VALUE(var))) {
+ case 't' :
+ case 'T' :
+ ct = GRN_CONTENT_TSV;
+ break;
+ case 'j' :
+ case 'J' :
+ ct = GRN_CONTENT_JSON;
+ break;
+ case 'x' :
+ case 'X' :
+ ct = GRN_CONTENT_XML;
+ break;
+ }
+ }
+ return ct;
+}
+
+static void
+get_content_mime_type(grn_ctx *ctx, const char *p, const char *pe)
+{
+ ctx->impl->output_type = GRN_CONTENT_NONE;
+ ctx->impl->mime_type = "application/octet-stream";
+
+ if (p + 2 <= pe) {
+ switch (*p) {
+ case 'c' :
+ if (p + 3 == pe && !memcmp(p, "css", 3)) {
+ ctx->impl->output_type = GRN_CONTENT_NONE;
+ ctx->impl->mime_type = "text/css";
+ }
+ break;
+ case 'g' :
+ if (p + 3 == pe && !memcmp(p, "gif", 3)) {
+ ctx->impl->output_type = GRN_CONTENT_NONE;
+ ctx->impl->mime_type = "image/gif";
+ }
+ break;
+ case 'h' :
+ if (p + 4 == pe && !memcmp(p, "html", 4)) {
+ ctx->impl->output_type = GRN_CONTENT_NONE;
+ ctx->impl->mime_type = "text/html";
+ }
+ break;
+ case 'j' :
+ if (!memcmp(p, "js", 2)) {
+ if (p + 2 == pe) {
+ ctx->impl->output_type = GRN_CONTENT_NONE;
+ ctx->impl->mime_type = "text/javascript";
+ } else if (p + 4 == pe && !memcmp(p + 2, "on", 2)) {
+ ctx->impl->output_type = GRN_CONTENT_JSON;
+ ctx->impl->mime_type = "application/json";
+ }
+ } else if (p + 3 == pe && !memcmp(p, "jpg", 3)) {
+ ctx->impl->output_type = GRN_CONTENT_NONE;
+ ctx->impl->mime_type = "image/jpeg";
+ }
+ break;
+#ifdef GRN_WITH_MESSAGE_PACK
+ case 'm' :
+ if (p + 7 == pe && !memcmp(p, "msgpack", 7)) {
+ ctx->impl->output_type = GRN_CONTENT_MSGPACK;
+ ctx->impl->mime_type = "application/x-msgpack";
+ }
+ break;
+#endif
+ case 'p' :
+ if (p + 3 == pe && !memcmp(p, "png", 3)) {
+ ctx->impl->output_type = GRN_CONTENT_NONE;
+ ctx->impl->mime_type = "image/png";
+ }
+ break;
+ case 't' :
+ if (p + 3 == pe && !memcmp(p, "txt", 3)) {
+ ctx->impl->output_type = GRN_CONTENT_NONE;
+ ctx->impl->mime_type = "text/plain";
+ } else if (p + 3 == pe && !memcmp(p, "tsv", 3)) {
+ ctx->impl->output_type = GRN_CONTENT_TSV;
+ ctx->impl->mime_type = "text/plain";
+ }
+ break;
+ case 'x':
+ if (p + 3 == pe && !memcmp(p, "xml", 3)) {
+ ctx->impl->output_type = GRN_CONTENT_XML;
+ ctx->impl->mime_type = "text/xml";
+ }
+ break;
+ }
+ }
+}
+
+static void
+grn_str_get_mime_type(grn_ctx *ctx, const char *p, const char *pe,
+ const char **key_end, const char **filename_end)
+{
+ const char *pd = NULL;
+ for (; p < pe && *p != '?' && *p != '#'; p++) {
+ if (*p == '.') { pd = p; }
+ }
+ *filename_end = p;
+ if (pd && pd < p) {
+ get_content_mime_type(ctx, pd + 1, p);
+ *key_end = pd;
+ } else {
+ *key_end = pe;
+ }
+}
+
+static void
+get_command_version(grn_ctx *ctx, const char *p, const char *pe)
+{
+ grn_command_version version;
+ const char *rest;
+
+ version = grn_atoui(p, pe, &rest);
+ if (pe == rest) {
+ grn_rc rc;
+ rc = grn_ctx_set_command_version(ctx, version);
+ if (rc == GRN_UNSUPPORTED_COMMAND_VERSION) {
+ ERR(rc,
+ "unsupported command version is specified: %d: "
+ "stable command version: %d: "
+ "available command versions: %d-%d",
+ version,
+ GRN_COMMAND_VERSION_STABLE,
+ GRN_COMMAND_VERSION_MIN, GRN_COMMAND_VERSION_MAX);
+ }
+ }
+}
+
+#define INDEX_HTML "index.html"
+#define OUTPUT_TYPE "output_type"
+#define COMMAND_VERSION "command_version"
+#define EXPR_MISSING "expr_missing"
+#define OUTPUT_TYPE_LEN (sizeof(OUTPUT_TYPE) - 1)
+#define COMMAND_VERSION_LEN (sizeof(COMMAND_VERSION) - 1)
+
+#define HTTP_QUERY_PAIR_DELIMITER "="
+#define HTTP_QUERY_PAIRS_DELIMITERS "&;"
+
+static inline int
+command_proc_p(grn_obj *expr)
+{
+ return (expr->header.type == GRN_PROC &&
+ ((grn_proc *)expr)->type == GRN_PROC_COMMAND);
+}
+
+grn_obj *
+grn_ctx_qe_exec_uri(grn_ctx *ctx, const char *path, uint32_t path_len)
+{
+ grn_obj buf, *expr, *val;
+ const char *p = path, *e = path + path_len, *v, *key_end, *filename_end;
+ GRN_TEXT_INIT(&buf, 0);
+ p = grn_text_urldec(ctx, &buf, p, e, '?');
+ if (!GRN_TEXT_LEN(&buf)) { GRN_TEXT_SETS(ctx, &buf, INDEX_HTML); }
+ v = GRN_TEXT_VALUE(&buf);
+ grn_str_get_mime_type(ctx, v, GRN_BULK_CURR(&buf), &key_end, &filename_end);
+ if ((GRN_TEXT_LEN(&buf) >= 2 && v[0] == 'd' && v[1] == '/')) {
+ const char *command_name = v + 2;
+ int command_name_size = key_end - command_name;
+ expr = grn_ctx_get(ctx, command_name, command_name_size);
+ if (expr && command_proc_p(expr)) {
+ while (p < e) {
+ int l;
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_cgidec(ctx, &buf, p, e, HTTP_QUERY_PAIR_DELIMITER);
+ v = GRN_TEXT_VALUE(&buf);
+ l = GRN_TEXT_LEN(&buf);
+ if (l == OUTPUT_TYPE_LEN && !memcmp(v, OUTPUT_TYPE, OUTPUT_TYPE_LEN)) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_cgidec(ctx, &buf, p, e, HTTP_QUERY_PAIRS_DELIMITERS);
+ v = GRN_TEXT_VALUE(&buf);
+ get_content_mime_type(ctx, v, GRN_BULK_CURR(&buf));
+ } else if (l == COMMAND_VERSION_LEN &&
+ !memcmp(v, COMMAND_VERSION, COMMAND_VERSION_LEN)) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_cgidec(ctx, &buf, p, e, HTTP_QUERY_PAIRS_DELIMITERS);
+ get_command_version(ctx, GRN_TEXT_VALUE(&buf), GRN_BULK_CURR(&buf));
+ if (ctx->rc) { goto exit; }
+ } else {
+ if (!(val = grn_expr_get_or_add_var(ctx, expr, v, l))) {
+ val = &buf;
+ }
+ grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
+ p = grn_text_cgidec(ctx, val, p, e, HTTP_QUERY_PAIRS_DELIMITERS);
+ }
+ }
+ ctx->impl->curr_expr = expr;
+ grn_expr_exec(ctx, expr, 0);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "invalid command name: %.*s",
+ command_name_size, command_name);
+ }
+ } else if ((expr = grn_ctx_get(ctx, GRN_EXPR_MISSING_NAME,
+ strlen(GRN_EXPR_MISSING_NAME)))) {
+ if ((val = grn_expr_get_var_by_offset(ctx, expr, 0))) {
+ grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
+ GRN_TEXT_SET(ctx, val, v, filename_end - v);
+ }
+ ctx->impl->curr_expr = expr;
+ grn_expr_exec(ctx, expr, 0);
+ }
+exit :
+ GRN_OBJ_FIN(ctx, &buf);
+ return expr;
+}
+
+grn_obj *
+grn_ctx_qe_exec(grn_ctx *ctx, const char *str, uint32_t str_len)
+{
+ char tok_type;
+ int offset = 0;
+ grn_obj buf, *expr = NULL, *val = NULL;
+ const char *p = str, *e = str + str_len, *v;
+ GRN_TEXT_INIT(&buf, 0);
+ p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
+ expr = grn_ctx_get(ctx, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ while (p < e) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
+ v = GRN_TEXT_VALUE(&buf);
+ switch (tok_type) {
+ case GRN_TOK_VOID :
+ p = e;
+ break;
+ case GRN_TOK_SYMBOL :
+ if (GRN_TEXT_LEN(&buf) > 2 && v[0] == '-' && v[1] == '-') {
+ int l = GRN_TEXT_LEN(&buf) - 2;
+ v += 2;
+ if (l == OUTPUT_TYPE_LEN && !memcmp(v, OUTPUT_TYPE, OUTPUT_TYPE_LEN)) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
+ v = GRN_TEXT_VALUE(&buf);
+ get_content_mime_type(ctx, v, GRN_BULK_CURR(&buf));
+ } else if (l == COMMAND_VERSION_LEN &&
+ !memcmp(v, COMMAND_VERSION, COMMAND_VERSION_LEN)) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
+ get_command_version(ctx, GRN_TEXT_VALUE(&buf), GRN_BULK_CURR(&buf));
+ if (ctx->rc) { goto exit; }
+ } else if (expr && (val = grn_expr_get_or_add_var(ctx, expr, v, l))) {
+ grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
+ p = grn_text_unesc_tok(ctx, val, p, e, &tok_type);
+ } else {
+ p = e;
+ }
+ break;
+ }
+ // fallthru
+ case GRN_TOK_STRING :
+ case GRN_TOK_QUOTE :
+ if (expr && (val = grn_expr_get_var_by_offset(ctx, expr, offset++))) {
+ grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
+ GRN_TEXT_PUT(ctx, val, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ } else {
+ p = e;
+ }
+ break;
+ }
+ }
+ ctx->impl->curr_expr = expr;
+ if (expr && command_proc_p(expr)) {
+ grn_expr_exec(ctx, expr, 0);
+ } else {
+ GRN_BULK_REWIND(&buf);
+ grn_text_unesc_tok(ctx, &buf, str, str + str_len, &tok_type);
+ if (GRN_TEXT_LEN(&buf)) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid command name: %.*s",
+ (int)GRN_TEXT_LEN(&buf), GRN_TEXT_VALUE(&buf));
+ }
+ }
+exit :
+ GRN_OBJ_FIN(ctx, &buf);
+ return expr;
+}
+
+grn_rc
+grn_ctx_sendv(grn_ctx *ctx, int argc, char **argv, int flags)
+{
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ while (argc--) {
+ // todo : encode into json like syntax
+ GRN_TEXT_PUTS(ctx, &buf, *argv);
+ argv++;
+ if (argc) { GRN_TEXT_PUTC(ctx, &buf, ' '); }
+ }
+ grn_ctx_send(ctx, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf), flags);
+ GRN_OBJ_FIN(ctx, &buf);
+ return ctx->rc;
+}
+
+static int
+comment_command_p(const char *command, unsigned int length)
+{
+ const char *p, *e;
+
+ e = command + length;
+ for (p = command; p < e; p++) {
+ switch (*p) {
+ case '#' :
+ return GRN_TRUE;
+ case ' ' :
+ case '\t' :
+ break;
+ default :
+ return GRN_FALSE;
+ }
+ }
+ return GRN_FALSE;
+}
+
+unsigned int
+grn_ctx_send(grn_ctx *ctx, const char *str, unsigned int str_len, int flags)
+{
+ if (!ctx) { return 0; }
+ GRN_API_ENTER;
+ if (ctx->impl) {
+ if (ctx->impl->com) {
+ grn_rc rc;
+ grn_com_header sheader;
+ grn_timeval_now(ctx, &ctx->impl->tv);
+ if ((flags & GRN_CTX_MORE)) { flags |= GRN_CTX_QUIET; }
+ if (ctx->stat == GRN_CTX_QUIT) { flags |= GRN_CTX_QUIT; }
+ sheader.proto = GRN_COM_PROTO_GQTP;
+ sheader.qtype = 0;
+ sheader.keylen = 0;
+ sheader.level = 0;
+ sheader.flags = flags;
+ sheader.status = 0;
+ sheader.opaque = 0;
+ sheader.cas = 0;
+ if ((rc = grn_com_send(ctx, ctx->impl->com, &sheader, (char *)str, str_len, 0))) {
+ ERR(rc, "grn_com_send failed");
+ }
+ goto exit;
+ } else {
+ grn_obj *expr = NULL;
+ if (comment_command_p(str, str_len)) { goto output; };
+ if (ctx->impl->qe_next) {
+ grn_obj *val;
+ expr = ctx->impl->qe_next;
+ ctx->impl->qe_next = NULL;
+ if ((val = grn_expr_get_var_by_offset(ctx, expr, 0))) {
+ grn_obj_reinit(ctx, val, GRN_DB_TEXT, 0);
+ GRN_TEXT_PUT(ctx, val, str, str_len);
+ }
+ grn_expr_exec(ctx, expr, 0);
+ } else {
+ ctx->impl->mime_type = "application/json";
+ ctx->impl->output_type = GRN_CONTENT_JSON;
+ grn_timeval_now(ctx, &ctx->impl->tv);
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_COMMAND,
+ ">", "%.*s", str_len, str);
+ if (str_len && *str == '/') {
+ expr = grn_ctx_qe_exec_uri(ctx, str + 1, str_len - 1);
+ } else {
+ expr = grn_ctx_qe_exec(ctx, str, str_len);
+ }
+ }
+ if (ctx->stat == GRN_CTX_QUITTING) { ctx->stat = GRN_CTX_QUIT; }
+ if (ctx->impl->qe_next) {
+ ERRCLR(ctx);
+ } else {
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_RESULT_CODE,
+ "<", "rc=%d", ctx->rc);
+ }
+ output :
+ if (!ERRP(ctx, GRN_CRIT)) {
+ if (!(flags & GRN_CTX_QUIET) && ctx->impl->output) {
+ ctx->impl->output(ctx, GRN_CTX_TAIL, ctx->impl->data.ptr);
+ }
+ }
+ if (expr) { grn_expr_clear_vars(ctx, expr); }
+ goto exit;
+ }
+ }
+ ERR(GRN_INVALID_ARGUMENT, "invalid ctx assigned");
+exit :
+ GRN_API_RETURN(0);
+}
+
+unsigned int
+grn_ctx_recv(grn_ctx *ctx, char **str, unsigned int *str_len, int *flags)
+{
+ if (!ctx) { return GRN_INVALID_ARGUMENT; }
+ if (ctx->stat == GRN_CTX_QUIT) {
+ *str = NULL;
+ *str_len = 0;
+ *flags = GRN_CTX_QUIT;
+ return 0;
+ }
+ GRN_API_ENTER;
+ if (ctx->impl) {
+ if (ctx->impl->com) {
+ grn_com_header header;
+ if (grn_com_recv(ctx, ctx->impl->com, &header, ctx->impl->outbuf)) {
+ *str = NULL;
+ *str_len = 0;
+ *flags = 0;
+ } else {
+ *str = GRN_BULK_HEAD(ctx->impl->outbuf);
+ *str_len = GRN_BULK_VSIZE(ctx->impl->outbuf);
+ if (header.flags & GRN_CTX_QUIT) {
+ ctx->stat = GRN_CTX_QUIT;
+ *flags = GRN_CTX_QUIT;
+ } else {
+ *flags = (header.flags & GRN_CTX_TAIL) ? 0 : GRN_CTX_MORE;
+ }
+ ctx->impl->output_type = header.qtype;
+ ctx->rc = (int16_t)ntohs(header.status);
+ ctx->errbuf[0] = '\0';
+ ctx->errline = 0;
+ ctx->errfile = NULL;
+ ctx->errfunc = NULL;
+ }
+ goto exit;
+ } else {
+ grn_obj *buf = ctx->impl->outbuf;
+ unsigned int head = 0, tail = GRN_BULK_VSIZE(buf);
+ *str = GRN_BULK_HEAD(buf) + head;
+ *str_len = tail - head;
+ GRN_BULK_REWIND(ctx->impl->outbuf);
+ goto exit;
+ }
+ }
+ ERR(GRN_INVALID_ARGUMENT, "invalid ctx assigned");
+exit :
+ GRN_API_RETURN(0);
+}
+
+void
+grn_ctx_stream_out_func(grn_ctx *ctx, int flags, void *stream)
+{
+ if (ctx && ctx->impl) {
+ grn_obj *buf = ctx->impl->outbuf;
+ uint32_t size = GRN_BULK_VSIZE(buf);
+ if (size) {
+ if (fwrite(GRN_BULK_HEAD(buf), 1, size, (FILE *)stream)) {
+ fputc('\n', (FILE *)stream);
+ fflush((FILE *)stream);
+ }
+ GRN_BULK_REWIND(buf);
+ }
+ }
+}
+
+void
+grn_ctx_recv_handler_set(grn_ctx *ctx, void (*func)(grn_ctx *, int, void *), void *func_arg)
+{
+ if (ctx && ctx->impl) {
+ ctx->impl->output = func;
+ ctx->impl->data.ptr = func_arg;
+ }
+}
+
+grn_rc
+grn_ctx_info_get(grn_ctx *ctx, grn_ctx_info *info)
+{
+ if (!ctx || !ctx->impl) { return GRN_INVALID_ARGUMENT; }
+ if (ctx->impl->com) {
+ info->fd = ctx->impl->com->fd;
+ info->com_status = ctx->impl->com_status;
+ info->outbuf = ctx->impl->outbuf;
+ info->stat = ctx->stat;
+ } else {
+ info->fd = -1;
+ info->com_status = 0;
+ info->outbuf = ctx->impl->outbuf;
+ info->stat = ctx->stat;
+ }
+ return GRN_SUCCESS;
+}
+
+
+typedef struct _grn_cache_entry grn_cache_entry;
+
+struct _grn_cache {
+ grn_cache_entry *next;
+ grn_cache_entry *prev;
+ grn_hash *hash;
+ grn_mutex mutex;
+ uint32_t max_nentries;
+ uint32_t nfetches;
+ uint32_t nhits;
+};
+
+struct _grn_cache_entry {
+ grn_cache_entry *next;
+ grn_cache_entry *prev;
+ grn_obj *value;
+ grn_timeval tv;
+ grn_id id;
+ uint32_t nref;
+};
+
+static grn_cache *grn_cache_current = NULL;
+static grn_cache *grn_cache_default = NULL;
+
+grn_cache *
+grn_cache_open(grn_ctx *ctx)
+{
+ grn_cache *cache = NULL;
+
+ GRN_API_ENTER;
+ cache = GRN_MALLOC(sizeof(grn_cache));
+ if (!cache) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "[cache] failed to allocate grn_cache");
+ goto exit;
+ }
+
+ cache->next = (grn_cache_entry*)cache;
+ cache->prev = (grn_cache_entry *)cache;
+ cache->hash = grn_hash_create(&grn_gctx, NULL, GRN_TABLE_MAX_KEY_SIZE,
+ sizeof(grn_cache_entry), GRN_OBJ_KEY_VAR_SIZE);
+ MUTEX_INIT(cache->mutex);
+ cache->max_nentries = GRN_CACHE_DEFAULT_MAX_N_ENTRIES;
+ cache->nfetches = 0;
+ cache->nhits = 0;
+
+exit :
+ GRN_API_RETURN(cache);
+}
+
+grn_rc
+grn_cache_close(grn_ctx *ctx, grn_cache *cache)
+{
+ grn_ctx *ctx_original = ctx;
+ grn_cache_entry *vp;
+
+ GRN_API_ENTER;
+
+ ctx = &grn_gctx;
+ GRN_HASH_EACH(ctx, cache->hash, id, NULL, NULL, &vp, {
+ grn_obj_close(ctx, vp->value);
+ });
+ grn_hash_close(ctx, cache->hash);
+ MUTEX_FIN(cache->mutex);
+ ctx = ctx_original;
+ GRN_FREE(cache);
+
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_rc
+grn_cache_current_set(grn_ctx *ctx, grn_cache *cache)
+{
+ grn_cache_current = cache;
+ return GRN_SUCCESS;
+}
+
+grn_cache *
+grn_cache_current_get(grn_ctx *ctx)
+{
+ return grn_cache_current;
+}
+
+void
+grn_cache_init(void)
+{
+ grn_cache_default = grn_cache_open(&grn_gctx);
+ grn_cache_current_set(&grn_gctx, grn_cache_default);
+}
+
+grn_rc
+grn_cache_set_max_n_entries(grn_ctx *ctx, grn_cache *cache, unsigned int n)
+{
+ if (!cache) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ cache->max_nentries = n;
+ return GRN_SUCCESS;
+}
+
+uint32_t
+grn_cache_get_max_n_entries(grn_ctx *ctx, grn_cache *cache)
+{
+ if (!cache) {
+ return 0;
+ }
+ return cache->max_nentries;
+}
+
+void
+grn_cache_get_statistics(grn_ctx *ctx, grn_cache *cache,
+ grn_cache_statistics *statistics)
+{
+ MUTEX_LOCK(cache->mutex);
+ statistics->nentries = GRN_HASH_SIZE(cache->hash);
+ statistics->max_nentries = cache->max_nentries;
+ statistics->nfetches = cache->nfetches;
+ statistics->nhits = cache->nhits;
+ MUTEX_UNLOCK(cache->mutex);
+}
+
+static void
+grn_cache_expire_entry(grn_cache *cache, grn_cache_entry *ce)
+{
+ if (!ce->nref) {
+ ce->prev->next = ce->next;
+ ce->next->prev = ce->prev;
+ grn_obj_close(&grn_gctx, ce->value);
+ grn_hash_delete_by_id(&grn_gctx, cache->hash, ce->id, NULL);
+ }
+}
+
+grn_obj *
+grn_cache_fetch(grn_ctx *ctx, grn_cache *cache,
+ const char *str, uint32_t str_len)
+{
+ grn_cache_entry *ce;
+ grn_obj *obj = NULL;
+ if (!ctx->impl || !ctx->impl->db) { return obj; }
+ MUTEX_LOCK(cache->mutex);
+ cache->nfetches++;
+ if (grn_hash_get(&grn_gctx, cache->hash, str, str_len, (void **)&ce)) {
+ if (ce->tv.tv_sec <= grn_db_lastmod(ctx->impl->db)) {
+ grn_cache_expire_entry(cache, ce);
+ goto exit;
+ }
+ ce->nref++;
+ obj = ce->value;
+ ce->prev->next = ce->next;
+ ce->next->prev = ce->prev;
+ {
+ grn_cache_entry *ce0 = (grn_cache_entry *)cache;
+ ce->next = ce0->next;
+ ce->prev = ce0;
+ ce0->next->prev = ce;
+ ce0->next = ce;
+ }
+ cache->nhits++;
+ }
+exit :
+ MUTEX_UNLOCK(cache->mutex);
+ return obj;
+}
+
+void
+grn_cache_unref(grn_ctx *ctx, grn_cache *cache,
+ const char *str, uint32_t str_len)
+{
+ grn_cache_entry *ce;
+ ctx = &grn_gctx;
+ MUTEX_LOCK(cache->mutex);
+ if (grn_hash_get(ctx, cache->hash, str, str_len, (void **)&ce)) {
+ if (ce->nref) { ce->nref--; }
+ }
+ MUTEX_UNLOCK(cache->mutex);
+}
+
+void
+grn_cache_update(grn_ctx *ctx, grn_cache *cache,
+ const char *str, uint32_t str_len, grn_obj *value)
+{
+ grn_id id;
+ int added = 0;
+ grn_cache_entry *ce;
+ grn_rc rc = GRN_SUCCESS;
+ grn_obj *old = NULL, *obj;
+ if (!ctx->impl || !cache->max_nentries) { return; }
+ if (!(obj = grn_obj_open(&grn_gctx, GRN_BULK, 0, GRN_DB_TEXT))) { return; }
+ GRN_TEXT_PUT(&grn_gctx, obj, GRN_TEXT_VALUE(value), GRN_TEXT_LEN(value));
+ MUTEX_LOCK(cache->mutex);
+ if ((id = grn_hash_add(&grn_gctx, cache->hash, str, str_len, (void **)&ce, &added))) {
+ if (!added) {
+ if (ce->nref) {
+ rc = GRN_RESOURCE_BUSY;
+ goto exit;
+ }
+ old = ce->value;
+ ce->prev->next = ce->next;
+ ce->next->prev = ce->prev;
+ }
+ ce->id = id;
+ ce->value = obj;
+ ce->tv = ctx->impl->tv;
+ ce->nref = 0;
+ {
+ grn_cache_entry *ce0 = (grn_cache_entry *)cache;
+ ce->next = ce0->next;
+ ce->prev = ce0;
+ ce0->next->prev = ce;
+ ce0->next = ce;
+ }
+ if (GRN_HASH_SIZE(cache->hash) > cache->max_nentries) {
+ grn_cache_expire_entry(cache, cache->prev);
+ }
+ } else {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+exit :
+ MUTEX_UNLOCK(cache->mutex);
+ if (rc) { grn_obj_close(&grn_gctx, obj); }
+ if (old) { grn_obj_close(&grn_gctx, old); }
+}
+
+void
+grn_cache_expire(grn_cache *cache, int32_t size)
+{
+ grn_cache_entry *ce0 = (grn_cache_entry *)cache;
+ MUTEX_LOCK(cache->mutex);
+ while (ce0 != ce0->prev && size--) {
+ grn_cache_expire_entry(cache, ce0->prev);
+ }
+ MUTEX_UNLOCK(cache->mutex);
+}
+
+void
+grn_cache_fin(void)
+{
+ grn_cache_current_set(&grn_gctx, NULL);
+ grn_cache_close(&grn_gctx, grn_cache_default);
+}
+
+/**** memory allocation ****/
+
+#define ALIGN_SIZE (1<<3)
+#define ALIGN_MASK (ALIGN_SIZE-1)
+#define GRN_CTX_ALLOC_CLEAR 1
+
+void *
+grn_ctx_alloc(grn_ctx *ctx, size_t size, int flags,
+ const char* file, int line, const char *func)
+{
+ void *res = NULL;
+ if (!ctx) { return res; }
+ if (!ctx->impl) {
+ grn_ctx_impl_init(ctx);
+ if (ERRP(ctx, GRN_ERROR)) { return res; }
+ }
+ CRITICAL_SECTION_ENTER(ctx->impl->lock);
+ {
+ int32_t i;
+ int32_t *header;
+ grn_io_mapinfo *mi;
+ size = ((size + ALIGN_MASK) & ~ALIGN_MASK) + ALIGN_SIZE;
+ if (size > GRN_CTX_SEGMENT_SIZE) {
+ uint64_t npages = (size + (grn_pagesize - 1)) / grn_pagesize;
+ if (npages >= (1LL<<32)) {
+ MERR("too long request size=%zu", size);
+ goto exit;
+ }
+ for (i = 0, mi = ctx->impl->segs;; i++, mi++) {
+ if (i >= GRN_CTX_N_SEGMENTS) {
+ MERR("all segments are full");
+ goto exit;
+ }
+ if (!mi->map) { break; }
+ }
+ if (!grn_io_anon_map(ctx, mi, npages * grn_pagesize)) { goto exit; }
+ //GRN_LOG(ctx, GRN_LOG_NOTICE, "map i=%d (%d)", i, npages * grn_pagesize);
+ mi->nref = (uint32_t) npages;
+ mi->count = GRN_CTX_SEGMENT_VLEN;
+ ctx->impl->currseg = -1;
+ header = mi->map;
+ header[0] = i;
+ header[1] = (int32_t) size;
+ } else {
+ i = ctx->impl->currseg;
+ mi = &ctx->impl->segs[i];
+ if (i < 0 || size + mi->nref > GRN_CTX_SEGMENT_SIZE) {
+ for (i = 0, mi = ctx->impl->segs;; i++, mi++) {
+ if (i >= GRN_CTX_N_SEGMENTS) {
+ MERR("all segments are full");
+ goto exit;
+ }
+ if (!mi->map) { break; }
+ }
+ if (!grn_io_anon_map(ctx, mi, GRN_CTX_SEGMENT_SIZE)) { goto exit; }
+ //GRN_LOG(ctx, GRN_LOG_NOTICE, "map i=%d", i);
+ mi->nref = 0;
+ mi->count = GRN_CTX_SEGMENT_WORD;
+ ctx->impl->currseg = i;
+ }
+ header = (int32_t *)((byte *)mi->map + mi->nref);
+ mi->nref += size;
+ mi->count++;
+ header[0] = i;
+ header[1] = (int32_t) size;
+ if ((flags & GRN_CTX_ALLOC_CLEAR) &&
+ (mi->count & GRN_CTX_SEGMENT_DIRTY) && (size > ALIGN_SIZE)) {
+ memset(&header[2], 0, size - ALIGN_SIZE);
+ }
+ }
+ /*
+ {
+ char g = (ctx == &grn_gctx) ? 'g' : ' ';
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "+%c(%p) %s:%d(%s) (%d:%d)%p mi(%d:%d)", g, ctx, file, line, func, header[0], header[1], &header[2], mi->nref, (mi->count & GRN_CTX_SEGMENT_MASK));
+ }
+ */
+ res = &header[2];
+ }
+exit :
+ CRITICAL_SECTION_LEAVE(ctx->impl->lock);
+ return res;
+}
+
+void *
+grn_ctx_malloc(grn_ctx *ctx, size_t size,
+ const char* file, int line, const char *func)
+{
+ return grn_ctx_alloc(ctx, size, 0, file, line, func);
+}
+
+void *
+grn_ctx_calloc(grn_ctx *ctx, size_t size,
+ const char* file, int line, const char *func)
+{
+ return grn_ctx_alloc(ctx, size, GRN_CTX_ALLOC_CLEAR, file, line, func);
+}
+
+void *
+grn_ctx_realloc(grn_ctx *ctx, void *ptr, size_t size,
+ const char* file, int line, const char *func)
+{
+ void *res = NULL;
+ if (size) {
+ /* todo : expand if possible */
+ res = grn_ctx_alloc(ctx, size, 0, file, line, func);
+ if (res && ptr) {
+ int32_t *header = &((int32_t *)ptr)[-2];
+ size_t size_ = header[1];
+ memcpy(res, ptr, size_ > size ? size : size_);
+ grn_ctx_free(ctx, ptr, file, line, func);
+ }
+ } else {
+ grn_ctx_free(ctx, ptr, file, line, func);
+ }
+ return res;
+}
+
+char *
+grn_ctx_strdup(grn_ctx *ctx, const char *s, const char* file, int line, const char *func)
+{
+ void *res = NULL;
+ if (s) {
+ size_t size = strlen(s) + 1;
+ if ((res = grn_ctx_alloc(ctx, size, 0, file, line, func))) {
+ memcpy(res, s, size);
+ }
+ }
+ return res;
+}
+
+void
+grn_ctx_free(grn_ctx *ctx, void *ptr,
+ const char* file, int line, const char *func)
+{
+ if (!ctx) { return; }
+ if (!ctx->impl) {
+ ERR(GRN_INVALID_ARGUMENT,"ctx without impl passed.");
+ return;
+ }
+ CRITICAL_SECTION_ENTER(ctx->impl->lock);
+ if (ptr) {
+ int32_t *header = &((int32_t *)ptr)[-2];
+
+ if (header[0] >= GRN_CTX_N_SEGMENTS) {
+ ERR(GRN_INVALID_ARGUMENT,"invalid ptr passed. ptr=%p seg=%d", ptr, *header);
+ goto exit;
+ }
+ /*
+ {
+ int32_t i = header[0];
+ char c = 'X', g = (ctx == &grn_gctx) ? 'g' : ' ';
+ grn_io_mapinfo *mi = &ctx->impl->segs[i];
+ if (!(mi->count & GRN_CTX_SEGMENT_VLEN) &&
+ mi->map <= (void *)header && (char *)header < ((char *)mi->map + GRN_CTX_SEGMENT_SIZE)) { c = '-'; }
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "%c%c(%p) %s:%d(%s) (%d:%d)%p mi(%d:%d)", c, g, ctx, file, line, func, header[0], header[1], &header[2], mi->nref, (mi->count & GRN_CTX_SEGMENT_MASK));
+ }
+ */
+ {
+ int32_t i = header[0];
+ grn_io_mapinfo *mi = &ctx->impl->segs[i];
+ if (mi->count & GRN_CTX_SEGMENT_VLEN) {
+ if (mi->map != header) {
+ ERR(GRN_INVALID_ARGUMENT,"invalid ptr passed.. ptr=%p seg=%d", ptr, i);
+ goto exit;
+ }
+ //GRN_LOG(ctx, GRN_LOG_NOTICE, "umap i=%d (%d)", i, mi->nref * grn_pagesize);
+ grn_io_anon_unmap(ctx, mi, mi->nref * grn_pagesize);
+ mi->map = NULL;
+ } else {
+ if (!mi->map) {
+ ERR(GRN_INVALID_ARGUMENT,"invalid ptr passed... ptr=%p seg=%d", ptr, i);
+ goto exit;
+ }
+ mi->count--;
+ if (!(mi->count & GRN_CTX_SEGMENT_MASK)) {
+ //GRN_LOG(ctx, GRN_LOG_NOTICE, "umap i=%d", i);
+ if (i == ctx->impl->currseg) {
+ mi->count |= GRN_CTX_SEGMENT_DIRTY;
+ mi->nref = 0;
+ } else {
+ grn_io_anon_unmap(ctx, mi, GRN_CTX_SEGMENT_SIZE);
+ mi->map = NULL;
+ }
+ }
+ }
+ }
+ }
+exit :
+ CRITICAL_SECTION_LEAVE(ctx->impl->lock);
+}
+
+#define DB_P(s) ((s) && (s)->header.type == GRN_DB)
+
+grn_rc
+grn_ctx_use(grn_ctx *ctx, grn_obj *db)
+{
+ GRN_API_ENTER;
+ if (db && !DB_P(db)) {
+ ctx->rc = GRN_INVALID_ARGUMENT;
+ } else {
+ if (!ctx->impl) { grn_ctx_impl_init(ctx); }
+ if (!ctx->rc) {
+ ctx->impl->db = db;
+ if (db) {
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ grn_obj_get_info(ctx, db, GRN_INFO_ENCODING, &buf);
+ ctx->encoding = *(grn_encoding *)GRN_BULK_HEAD(&buf);
+ grn_obj_close(ctx, &buf);
+ }
+ }
+ }
+ GRN_API_RETURN(ctx->rc);
+}
+
+void *
+grn_ctx_alloc_lifo(grn_ctx *ctx, size_t size,
+ const char* file, int line, const char *func)
+{
+ if (!ctx) { return NULL; }
+ if (!ctx->impl) {
+ grn_ctx_impl_init(ctx);
+ if (ERRP(ctx, GRN_ERROR)) { return NULL; }
+ }
+ {
+ int32_t i = ctx->impl->lifoseg;
+ grn_io_mapinfo *mi = &ctx->impl->segs[i];
+ if (size > GRN_CTX_SEGMENT_SIZE) {
+ uint64_t npages = (size + (grn_pagesize - 1)) / grn_pagesize;
+ if (npages >= (1LL<<32)) {
+ MERR("too long request size=%zu", size);
+ return NULL;
+ }
+ for (;;) {
+ if (++i >= GRN_CTX_N_SEGMENTS) {
+ MERR("all segments are full");
+ return NULL;
+ }
+ mi++;
+ if (!mi->map) { break; }
+ }
+ if (!grn_io_anon_map(ctx, mi, npages * grn_pagesize)) { return NULL; }
+ mi->nref = (uint32_t) npages;
+ mi->count = GRN_CTX_SEGMENT_VLEN|GRN_CTX_SEGMENT_LIFO;
+ ctx->impl->lifoseg = i;
+ return mi->map;
+ } else {
+ size = (size + ALIGN_MASK) & ~ALIGN_MASK;
+ if (i < 0 || (mi->count & GRN_CTX_SEGMENT_VLEN) || size + mi->nref > GRN_CTX_SEGMENT_SIZE) {
+ for (;;) {
+ if (++i >= GRN_CTX_N_SEGMENTS) {
+ MERR("all segments are full");
+ return NULL;
+ }
+ if (!(++mi)->map) { break; }
+ }
+ if (!grn_io_anon_map(ctx, mi, GRN_CTX_SEGMENT_SIZE)) { return NULL; }
+ mi->nref = 0;
+ mi->count = GRN_CTX_SEGMENT_WORD|GRN_CTX_SEGMENT_LIFO;
+ ctx->impl->lifoseg = i;
+ }
+ {
+ uint32_t u = mi->nref;
+ mi->nref += size;
+ return (byte *)mi->map + u;
+ }
+ }
+ }
+}
+
+void
+grn_ctx_free_lifo(grn_ctx *ctx, void *ptr,
+ const char* file, int line, const char *func)
+{
+ if (!ctx) { return; }
+ if (!ctx->impl) {
+ ERR(GRN_INVALID_ARGUMENT,"ctx without impl passed.");
+ return;
+ }
+ {
+ int32_t i = ctx->impl->lifoseg, done = 0;
+ grn_io_mapinfo *mi = &ctx->impl->segs[i];
+ if (i < 0) {
+ ERR(GRN_INVALID_ARGUMENT, "lifo buffer is void");
+ return;
+ }
+ for (; i >= 0; i--, mi--) {
+ if (!(mi->count & GRN_CTX_SEGMENT_LIFO)) { continue; }
+ if (done) { break; }
+ if (mi->count & GRN_CTX_SEGMENT_VLEN) {
+ if (mi->map == ptr) { done = 1; }
+ grn_io_anon_unmap(ctx, mi, mi->nref * grn_pagesize);
+ mi->map = NULL;
+ } else {
+ if (mi->map == ptr) {
+ done = 1;
+ } else {
+ if (mi->map < ptr && ptr < (void *)((byte*)mi->map + mi->nref)) {
+ mi->nref = (uint32_t) ((uintptr_t)ptr - (uintptr_t)mi->map);
+ break;
+ }
+ }
+ grn_io_anon_unmap(ctx, mi, GRN_CTX_SEGMENT_SIZE);
+ mi->map = NULL;
+ }
+ }
+ ctx->impl->lifoseg = i;
+ }
+}
+
+#if USE_DYNAMIC_MALLOC_CHANGE
+grn_malloc_func
+grn_ctx_get_malloc(grn_ctx *ctx)
+{
+ if (!ctx || !ctx->impl) { return NULL; }
+ return ctx->impl->malloc_func;
+}
+
+void
+grn_ctx_set_malloc(grn_ctx *ctx, grn_malloc_func malloc_func)
+{
+ if (!ctx || !ctx->impl) { return; }
+ ctx->impl->malloc_func = malloc_func;
+}
+
+grn_calloc_func
+grn_ctx_get_calloc(grn_ctx *ctx)
+{
+ if (!ctx || !ctx->impl) { return NULL; }
+ return ctx->impl->calloc_func;
+}
+
+void
+grn_ctx_set_calloc(grn_ctx *ctx, grn_calloc_func calloc_func)
+{
+ if (!ctx || !ctx->impl) { return; }
+ ctx->impl->calloc_func = calloc_func;
+}
+
+grn_realloc_func
+grn_ctx_get_realloc(grn_ctx *ctx)
+{
+ if (!ctx || !ctx->impl) { return NULL; }
+ return ctx->impl->realloc_func;
+}
+
+void
+grn_ctx_set_realloc(grn_ctx *ctx, grn_realloc_func realloc_func)
+{
+ if (!ctx || !ctx->impl) { return; }
+ ctx->impl->realloc_func = realloc_func;
+}
+
+grn_strdup_func
+grn_ctx_get_strdup(grn_ctx *ctx)
+{
+ if (!ctx || !ctx->impl) { return NULL; }
+ return ctx->impl->strdup_func;
+}
+
+void
+grn_ctx_set_strdup(grn_ctx *ctx, grn_strdup_func strdup_func)
+{
+ if (!ctx || !ctx->impl) { return; }
+ ctx->impl->strdup_func = strdup_func;
+}
+
+void *
+grn_malloc(grn_ctx *ctx, size_t size, const char* file, int line, const char *func)
+{
+ if (ctx && ctx->impl && ctx->impl->malloc_func) {
+ return ctx->impl->malloc_func(ctx, size, file, line, func);
+ } else {
+ return grn_malloc_default(ctx, size, file, line, func);
+ }
+}
+
+void *
+grn_calloc(grn_ctx *ctx, size_t size, const char* file, int line, const char *func)
+{
+ if (ctx && ctx->impl && ctx->impl->calloc_func) {
+ return ctx->impl->calloc_func(ctx, size, file, line, func);
+ } else {
+ return grn_calloc_default(ctx, size, file, line, func);
+ }
+}
+
+void *
+grn_realloc(grn_ctx *ctx, void *ptr, size_t size, const char* file, int line, const char *func)
+{
+ if (ctx && ctx->impl && ctx->impl->realloc_func) {
+ return ctx->impl->realloc_func(ctx, ptr, size, file, line, func);
+ } else {
+ return grn_realloc_default(ctx, ptr, size, file, line, func);
+ }
+}
+
+char *
+grn_strdup(grn_ctx *ctx, const char *string, const char* file, int line, const char *func)
+{
+ if (ctx && ctx->impl && ctx->impl->strdup_func) {
+ return ctx->impl->strdup_func(ctx, string, file, line, func);
+ } else {
+ return grn_strdup_default(ctx, string, file, line, func);
+ }
+}
+#endif
+
+void *
+grn_malloc_default(grn_ctx *ctx, size_t size, const char* file, int line, const char *func)
+{
+ if (!ctx) { return NULL; }
+ {
+ void *res = malloc(size);
+ if (res) {
+ GRN_ADD_ALLOC_COUNT(1);
+ grn_alloc_info_add(res);
+ } else {
+ if (!(res = malloc(size))) {
+ MERR("malloc fail (%zu)=%p (%s:%d) <%d>",
+ size, res, file, line, alloc_count);
+ } else {
+ GRN_ADD_ALLOC_COUNT(1);
+ grn_alloc_info_add(res);
+ }
+ }
+ return res;
+ }
+}
+
+void *
+grn_calloc_default(grn_ctx *ctx, size_t size, const char* file, int line, const char *func)
+{
+ if (!ctx) { return NULL; }
+ {
+ void *res = calloc(size, 1);
+ if (res) {
+ GRN_ADD_ALLOC_COUNT(1);
+ grn_alloc_info_add(res);
+ } else {
+ if (!(res = calloc(size, 1))) {
+ MERR("calloc fail (%" GRN_FMT_LLU ")=%p (%s:%d) <%" GRN_FMT_LLU ">",
+ (unsigned long long int)size, res, file, line,
+ (unsigned long long int)alloc_count);
+ } else {
+ GRN_ADD_ALLOC_COUNT(1);
+ grn_alloc_info_add(res);
+ }
+ }
+ return res;
+ }
+}
+
+void
+grn_free_default(grn_ctx *ctx, void *ptr, const char* file, int line, const char *func)
+{
+ if (!ctx) { return; }
+ grn_alloc_info_check(ptr);
+ {
+ free(ptr);
+ if (ptr) {
+ GRN_ADD_ALLOC_COUNT(-1);
+ } else {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "free fail (%p) (%s:%d) <%d>", ptr, file, line, alloc_count);
+ }
+ }
+}
+
+void *
+grn_realloc_default(grn_ctx *ctx, void *ptr, size_t size, const char* file, int line, const char *func)
+{
+ void *res;
+ if (!ctx) { return NULL; }
+ if (size) {
+ if (!(res = realloc(ptr, size))) {
+ if (!(res = realloc(ptr, size))) {
+ MERR("realloc fail (%p,%zu)=%p (%s:%d) <%d>", ptr, size, res, file, line, alloc_count);
+ return NULL;
+ }
+ }
+ if (ptr) {
+ grn_alloc_info_change(ptr, res);
+ } else {
+ GRN_ADD_ALLOC_COUNT(1);
+ grn_alloc_info_add(res);
+ }
+ } else {
+ if (!ptr) { return NULL; }
+ grn_alloc_info_check(ptr);
+ GRN_ADD_ALLOC_COUNT(-1);
+ free(ptr);
+ res = NULL;
+ }
+ return res;
+}
+
+int
+grn_alloc_count(void)
+{
+ return alloc_count;
+}
+
+char *
+grn_strdup_default(grn_ctx *ctx, const char *s, const char* file, int line, const char *func)
+{
+ if (!ctx) { return NULL; }
+ {
+ char *res = strdup(s);
+ if (res) {
+ GRN_ADD_ALLOC_COUNT(1);
+ } else {
+ if (!(res = strdup(s))) {
+ MERR("strdup(%p)=%p (%s:%d) <%d>", s, res, file, line, alloc_count);
+ }
+ }
+ return res;
+ }
+}
+
+#ifdef USE_FAIL_MALLOC
+int
+grn_fail_malloc_check(size_t size, const char *file, int line, const char *func)
+{
+ if ((grn_fmalloc_file && strcmp(file, grn_fmalloc_file)) ||
+ (grn_fmalloc_line && line != grn_fmalloc_line) ||
+ (grn_fmalloc_func && strcmp(func, grn_fmalloc_func))) {
+ return 1;
+ }
+ if (grn_fmalloc_prob && grn_fmalloc_prob >= rand()) {
+ return 0;
+ }
+ return 1;
+}
+
+void *
+grn_malloc_fail(grn_ctx *ctx, size_t size, const char* file, int line, const char *func)
+{
+ if (grn_fail_malloc_check(size, file, line, func)) {
+ return grn_malloc_default(ctx, size, file, line, func);
+ } else {
+ MERR("fail_malloc (%d) (%s:%d@%s) <%d>", size, file, line, func, alloc_count);
+ return NULL;
+ }
+}
+
+void *
+grn_calloc_fail(grn_ctx *ctx, size_t size, const char* file, int line, const char *func)
+{
+ if (grn_fail_malloc_check(size, file, line, func)) {
+ return grn_calloc_default(ctx, size, file, line, func);
+ } else {
+ MERR("fail_calloc (%d) (%s:%d@%s) <%d>", size, file, line, func, alloc_count);
+ return NULL;
+ }
+}
+
+void *
+grn_realloc_fail(grn_ctx *ctx, void *ptr, size_t size, const char* file, int line,
+ const char *func)
+{
+ if (grn_fail_malloc_check(size, file, line, func)) {
+ return grn_realloc_default(ctx, ptr, size, file, line, func);
+ } else {
+ MERR("fail_realloc (%p,%zu) (%s:%d@%s) <%d>", ptr, size, file, line, func, alloc_count);
+ return NULL;
+ }
+}
+
+char *
+grn_strdup_fail(grn_ctx *ctx, const char *s, const char* file, int line, const char *func)
+{
+ if (grn_fail_malloc_check(strlen(s), file, line, func)) {
+ return grn_strdup_default(ctx, s, file, line, func);
+ } else {
+ MERR("fail_strdup(%p) (%s:%d@%s) <%d>", s, file, line, func, alloc_count);
+ return NULL;
+ }
+}
+#endif /* USE_FAIL_MALLOC */
+
+/* don't handle error inside logger functions */
+
+void
+grn_ctx_log(grn_ctx *ctx, const char *fmt, ...)
+{
+ va_list argp;
+ va_start(argp, fmt);
+ vsnprintf(ctx->errbuf, GRN_CTX_MSGSIZE, fmt, argp);
+ va_end(argp);
+}
+
+void
+grn_assert(grn_ctx *ctx, int cond, const char* file, int line, const char* func)
+{
+ if (!cond) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "ASSERT fail on %s %s:%d", func, file, line);
+ }
+}
+
+const char *
+grn_get_version(void)
+{
+ return GRN_VERSION;
+}
+
+const char *
+grn_get_package(void)
+{
+ return PACKAGE;
+}
+
+#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
+static int segv_received = 0;
+static void
+segv_handler(int signal_number, siginfo_t *info, void *context)
+{
+ grn_ctx *ctx = &grn_gctx;
+
+ if (segv_received) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "SEGV received in SEGV handler.");
+ exit(EXIT_FAILURE);
+ }
+ segv_received = 1;
+
+ GRN_LOG(ctx, GRN_LOG_CRIT, "-- CRASHED!!! --");
+#ifdef HAVE_BACKTRACE
+# define N_TRACE_LEVEL 1024
+ {
+ static void *trace[N_TRACE_LEVEL];
+ int n = backtrace(trace, N_TRACE_LEVEL);
+ char **symbols = backtrace_symbols(trace, n);
+ int i;
+
+ if (symbols) {
+ for (i = 0; i < n; i++) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "%s", symbols[i]);
+ }
+ free(symbols);
+ }
+ }
+#else /* HAVE_BACKTRACE */
+ GRN_LOG(ctx, GRN_LOG_CRIT, "backtrace() isn't available.");
+#endif /* HAVE_BACKTRACE */
+ GRN_LOG(ctx, GRN_LOG_CRIT, "----------------");
+ abort();
+}
+#endif /* defined(HAVE_SIGNAL_H) && !defined(WIN32) */
+
+grn_rc
+grn_set_segv_handler(void)
+{
+ grn_rc rc = GRN_SUCCESS;
+#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
+ grn_ctx *ctx = &grn_gctx;
+ struct sigaction action;
+
+ sigemptyset(&action.sa_mask);
+ action.sa_sigaction = segv_handler;
+ action.sa_flags = SA_SIGINFO | SA_ONSTACK;
+
+ if (sigaction(SIGSEGV, &action, NULL)) {
+ SERR("failed to set SIGSEGV action");
+ rc = ctx->rc;
+ };
+#endif
+ return rc;
+}
+
+#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
+static struct sigaction old_int_handler;
+static void
+int_handler(int signal_number, siginfo_t *info, void *context)
+{
+ grn_gctx.stat = GRN_CTX_QUIT;
+ sigaction(signal_number, &old_int_handler, NULL);
+}
+
+static struct sigaction old_term_handler;
+static void
+term_handler(int signal_number, siginfo_t *info, void *context)
+{
+ grn_gctx.stat = GRN_CTX_QUIT;
+ sigaction(signal_number, &old_term_handler, NULL);
+}
+#endif /* defined(HAVE_SIGNAL_H) && !defined(WIN32) */
+
+grn_rc
+grn_set_int_handler(void)
+{
+ grn_rc rc = GRN_SUCCESS;
+#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
+ grn_ctx *ctx = &grn_gctx;
+ struct sigaction action;
+
+ sigemptyset(&action.sa_mask);
+ action.sa_sigaction = int_handler;
+ action.sa_flags = SA_SIGINFO;
+
+ if (sigaction(SIGINT, &action, &old_int_handler)) {
+ SERR("failed to set SIGINT action");
+ rc = ctx->rc;
+ }
+#endif
+ return rc;
+}
+
+grn_rc
+grn_set_term_handler(void)
+{
+ grn_rc rc = GRN_SUCCESS;
+#if defined(HAVE_SIGNAL_H) && !defined(WIN32)
+ grn_ctx *ctx = &grn_gctx;
+ struct sigaction action;
+
+ sigemptyset(&action.sa_mask);
+ action.sa_sigaction = term_handler;
+ action.sa_flags = SA_SIGINFO;
+
+ if (sigaction(SIGTERM, &action, &old_term_handler)) {
+ SERR("failed to set SIGTERM action");
+ rc = ctx->rc;
+ }
+#endif
+ return rc;
+}
+
+void
+grn_ctx_output_array_open(grn_ctx *ctx, const char *name, int nelements)
+{
+ grn_output_array_open(ctx, ctx->impl->outbuf, ctx->impl->output_type,
+ name, nelements);
+}
+
+void
+grn_ctx_output_array_close(grn_ctx *ctx)
+{
+ grn_output_array_close(ctx, ctx->impl->outbuf, ctx->impl->output_type);
+}
+
+void
+grn_ctx_output_map_open(grn_ctx *ctx, const char *name, int nelements)
+{
+ grn_output_map_open(ctx, ctx->impl->outbuf, ctx->impl->output_type,
+ name, nelements);
+}
+
+void
+grn_ctx_output_map_close(grn_ctx *ctx)
+{
+ grn_output_map_close(ctx, ctx->impl->outbuf, ctx->impl->output_type);
+}
+
+void
+grn_ctx_output_int32(grn_ctx *ctx, int value)
+{
+ grn_output_int32(ctx, ctx->impl->outbuf, ctx->impl->output_type, value);
+}
+
+void
+grn_ctx_output_int64(grn_ctx *ctx, long long int value)
+{
+ grn_output_int64(ctx, ctx->impl->outbuf, ctx->impl->output_type, value);
+}
+
+void
+grn_ctx_output_float(grn_ctx *ctx, double value)
+{
+ grn_output_float(ctx, ctx->impl->outbuf, ctx->impl->output_type, value);
+}
+
+void
+grn_ctx_output_cstr(grn_ctx *ctx, const char *value)
+{
+ grn_output_cstr(ctx, ctx->impl->outbuf, ctx->impl->output_type, value);
+}
+
+void
+grn_ctx_output_str(grn_ctx *ctx, const char *value, unsigned int value_len)
+{
+ grn_output_str(ctx, ctx->impl->outbuf, ctx->impl->output_type,
+ value, value_len);
+}
+
+void
+grn_ctx_output_bool(grn_ctx *ctx, grn_bool value)
+{
+ grn_output_bool(ctx, ctx->impl->outbuf, ctx->impl->output_type, value);
+}
+
+void
+grn_ctx_output_obj(grn_ctx *ctx, grn_obj *value, grn_obj_format *format)
+{
+ grn_output_obj(ctx, ctx->impl->outbuf, ctx->impl->output_type,
+ value, format);
+}
diff --git a/storage/mroonga/vendor/groonga/lib/ctx.h b/storage/mroonga/vendor/groonga/lib/ctx.h
new file mode 100644
index 00000000000..656abfa94b9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/ctx.h
@@ -0,0 +1,506 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2009-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_CTX_H
+#define GRN_CTX_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif /* HAVE_ERRNO_H */
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#define GRN_BREAK_POINT raise(SIGTRAP)
+#endif /* HAVE_SIGNAL_H */
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif /* HAVE_EXECINFO_H */
+
+#ifndef GRN_IO_H
+#include "io.h"
+#endif /* GRN_IO_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**** api in/out ****/
+
+#define GRN_API_ENTER do {\
+ if ((ctx)->seqno & 1) {\
+ (ctx)->subno++;\
+ } else {\
+ (ctx)->errlvl = GRN_OK;\
+ (ctx)->rc = GRN_SUCCESS;\
+ (ctx)->seqno++;\
+ }\
+ GRN_TEST_YIELD();\
+} while (0)
+
+/* CAUTION!! : pass only variables or constants as r */
+#define GRN_API_RETURN(r) do {\
+ if (ctx->subno) {\
+ ctx->subno--;\
+ } else {\
+ ctx->seqno++;\
+ }\
+ GRN_TEST_YIELD();\
+ return r;\
+} while (0)
+
+/**** error handling ****/
+
+#define GRN_EMERG GRN_LOG_EMERG
+#define GRN_ALERT GRN_LOG_ALERT
+#define GRN_CRIT GRN_LOG_CRIT
+#define GRN_ERROR GRN_LOG_ERROR
+#define GRN_WARN GRN_LOG_WARNING
+#define GRN_OK GRN_LOG_NOTICE
+
+#define ERRCLR(ctx) do {\
+ if (ctx) {\
+ ((grn_ctx *)ctx)->errlvl = GRN_OK;\
+ ((grn_ctx *)ctx)->rc = GRN_SUCCESS;\
+ }\
+ errno = 0;\
+ grn_gctx.errlvl = GRN_OK;\
+ grn_gctx.rc = GRN_SUCCESS;\
+} while (0)
+
+#ifdef HAVE_BACKTRACE
+#define BACKTRACE(ctx) ((ctx)->ntrace = (unsigned char)backtrace((ctx)->trace, 16))
+#else /* HAVE_BACKTRACE */
+#define BACKTRACE(ctx)
+#endif /* HAVE_BACKTRACE */
+
+GRN_API grn_bool grn_ctx_impl_should_log(grn_ctx *ctx);
+GRN_API void grn_ctx_impl_set_current_error_message(grn_ctx *ctx);
+
+#ifdef HAVE_BACKTRACE
+#define LOGTRACE(ctx,lvl) do {\
+ int i;\
+ char **p;\
+ BACKTRACE(ctx);\
+ p = backtrace_symbols((ctx)->trace, (ctx)->ntrace);\
+ for (i = 0; i < (ctx)->ntrace; i++) {\
+ GRN_LOG((ctx), lvl, "%s", p[i]);\
+ }\
+ free(p);\
+} while (0)
+#else /* HAVE_BACKTRACE */
+#define LOGTRACE(ctx,msg)
+#endif /* HAVE_BACKTRACE */
+
+#define ERRSET(ctx,lvl,r,...) do {\
+ grn_ctx *ctx_ = (grn_ctx *)ctx;\
+ ctx_->errlvl = (lvl);\
+ ctx_->rc = (r);\
+ ctx_->errfile = __FILE__;\
+ ctx_->errline = __LINE__;\
+ ctx_->errfunc = __FUNCTION__;\
+ grn_ctx_log(ctx, __VA_ARGS__);\
+ if (grn_ctx_impl_should_log(ctx)) {\
+ grn_ctx_impl_set_current_error_message(ctx);\
+ GRN_LOG(ctx, lvl, __VA_ARGS__);\
+ BACKTRACE(ctx);\
+ if (lvl <= GRN_LOG_ERROR) { LOGTRACE(ctx, lvl); }\
+ }\
+} while (0)
+
+#define ERRP(ctx,lvl) \
+ (((ctx) && ((grn_ctx *)(ctx))->errlvl <= (lvl)) || (grn_gctx.errlvl <= (lvl)))
+
+#ifdef ERR
+# undef ERR
+#endif /* ERR */
+#define ERR(rc,...) ERRSET(ctx, GRN_ERROR, (rc), __VA_ARGS__)
+#define WARN(rc,...) ERRSET(ctx, GRN_WARN, (rc), __VA_ARGS__)
+#define MERR(...) ERRSET(ctx, GRN_ALERT, GRN_NO_MEMORY_AVAILABLE, __VA_ARGS__)
+#define ALERT(...) ERRSET(ctx, GRN_ALERT, GRN_SUCCESS, __VA_ARGS__)
+
+#define ERR_CAST(column, range, element) do {\
+ grn_obj inspected;\
+ char column_name[GRN_TABLE_MAX_KEY_SIZE];\
+ int column_name_size;\
+ char range_name[GRN_TABLE_MAX_KEY_SIZE];\
+ int range_name_size;\
+ GRN_TEXT_INIT(&inspected, 0);\
+ grn_inspect(ctx, &inspected, element);\
+ column_name_size = grn_obj_name(ctx, column, column_name,\
+ GRN_TABLE_MAX_KEY_SIZE);\
+ range_name_size = grn_obj_name(ctx, range, range_name,\
+ GRN_TABLE_MAX_KEY_SIZE);\
+ ERR(GRN_INVALID_ARGUMENT, "<%.*s>: failed to cast to <%.*s>: <%.*s>",\
+ column_name_size, column_name,\
+ range_name_size, range_name,\
+ (int)GRN_TEXT_LEN(&inspected), GRN_TEXT_VALUE(&inspected));\
+ GRN_OBJ_FIN(ctx, &inspected);\
+} while (0)
+
+#ifdef WIN32
+#define SERR(str) do {\
+ grn_rc rc;\
+ const char *m;\
+ int e = WSAGetLastError();\
+ switch (e) {\
+ case WSANOTINITIALISED :\
+ rc = GRN_SOCKET_NOT_INITIALIZED;\
+ m = "please call grn_com_init first";\
+ break;\
+ case WSAEFAULT :\
+ rc = GRN_BAD_ADDRESS;\
+ m = "bad address";\
+ break;\
+ case WSAEINVAL :\
+ rc = GRN_INVALID_ARGUMENT;\
+ m = "invalid argument";\
+ break;\
+ case WSAEMFILE :\
+ rc = GRN_TOO_MANY_OPEN_FILES;\
+ m = "too many sockets";\
+ break;\
+ case WSAEWOULDBLOCK :\
+ rc = GRN_OPERATION_WOULD_BLOCK;\
+ m = "operation would block";\
+ break;\
+ case WSAENOTSOCK :\
+ rc = GRN_NOT_SOCKET;\
+ m = "given fd is not socket fd";\
+ break;\
+ case WSAEOPNOTSUPP :\
+ rc = GRN_OPERATION_NOT_SUPPORTED;\
+ m = "operation is not supported";\
+ break;\
+ case WSAEADDRINUSE :\
+ rc = GRN_ADDRESS_IS_IN_USE;\
+ m = "address is already in use";\
+ break;\
+ case WSAEADDRNOTAVAIL :\
+ rc = GRN_ADDRESS_IS_NOT_AVAILABLE;\
+ m = "address is not available";\
+ break;\
+ case WSAENETDOWN :\
+ rc = GRN_NETWORK_IS_DOWN;\
+ m = "network is down";\
+ break;\
+ case WSAENOBUFS :\
+ rc = GRN_NO_BUFFER;\
+ m = "no buffer";\
+ break;\
+ case WSAEISCONN :\
+ rc = GRN_SOCKET_IS_ALREADY_CONNECTED;\
+ m = "socket is already connected";\
+ break;\
+ case WSAENOTCONN :\
+ rc = GRN_SOCKET_IS_NOT_CONNECTED;\
+ m = "socket is not connected";\
+ break;\
+ case WSAESHUTDOWN :\
+ rc = GRN_SOCKET_IS_ALREADY_SHUTDOWNED;\
+ m = "socket is already shutdowned";\
+ break;\
+ case WSAETIMEDOUT :\
+ rc = GRN_OPERATION_TIMEOUT;\
+ m = "connection time out";\
+ break;\
+ case WSAECONNREFUSED :\
+ rc = GRN_CONNECTION_REFUSED;\
+ m = "connection refused";\
+ break;\
+ case WSAEINTR :\
+ rc = GRN_INTERRUPTED_FUNCTION_CALL;\
+ m = "interrupted function call";\
+ break;\
+ default:\
+ rc = GRN_UNKNOWN_ERROR;\
+ m = "unknown error";\
+ break;\
+ }\
+ ERR(rc, "syscall error '%s' (%s)", str, m);\
+} while (0)
+#else /* WIN32 */
+#define SERR(str) do {\
+ grn_rc rc;\
+ switch (errno) {\
+ case ELOOP : rc = GRN_TOO_MANY_SYMBOLIC_LINKS; break;\
+ case ENAMETOOLONG : rc = GRN_FILENAME_TOO_LONG; break;\
+ case ENOENT : rc = GRN_NO_SUCH_FILE_OR_DIRECTORY; break;\
+ case ENOMEM : rc = GRN_NO_MEMORY_AVAILABLE; break;\
+ case ENOTDIR : rc = GRN_NOT_A_DIRECTORY; break;\
+ case EPERM : rc = GRN_OPERATION_NOT_PERMITTED; break;\
+ case ESRCH : rc = GRN_NO_SUCH_PROCESS; break;\
+ case EINTR : rc = GRN_INTERRUPTED_FUNCTION_CALL; break;\
+ case EIO : rc = GRN_INPUT_OUTPUT_ERROR; break;\
+ case ENXIO : rc = GRN_NO_SUCH_DEVICE_OR_ADDRESS; break;\
+ case E2BIG : rc = GRN_ARG_LIST_TOO_LONG; break;\
+ case ENOEXEC : rc = GRN_EXEC_FORMAT_ERROR; break;\
+ case EBADF : rc = GRN_BAD_FILE_DESCRIPTOR; break;\
+ case ECHILD : rc = GRN_NO_CHILD_PROCESSES; break;\
+ case EACCES : rc = GRN_PERMISSION_DENIED; break;\
+ case EFAULT : rc = GRN_BAD_ADDRESS; break;\
+ case EBUSY : rc = GRN_RESOURCE_BUSY; break;\
+ case EEXIST : rc = GRN_FILE_EXISTS; break;\
+ case ENODEV : rc = GRN_NO_SUCH_DEVICE; break;\
+ case EISDIR : rc = GRN_IS_A_DIRECTORY; break;\
+ case EINVAL : rc = GRN_INVALID_ARGUMENT; break;\
+ case EMFILE : rc = GRN_TOO_MANY_OPEN_FILES; break;\
+ case EFBIG : rc = GRN_FILE_TOO_LARGE; break;\
+ case ENOSPC : rc = GRN_NO_SPACE_LEFT_ON_DEVICE; break;\
+ case EROFS : rc = GRN_READ_ONLY_FILE_SYSTEM; break;\
+ case EMLINK : rc = GRN_TOO_MANY_LINKS; break;\
+ case EPIPE : rc = GRN_BROKEN_PIPE; break;\
+ case EDOM : rc = GRN_DOMAIN_ERROR; break;\
+ case ERANGE : rc = GRN_RANGE_ERROR; break;\
+ case ENOTSOCK : rc = GRN_NOT_SOCKET; break;\
+ case EADDRINUSE : rc = GRN_ADDRESS_IS_IN_USE; break;\
+ case ENETDOWN : rc = GRN_NETWORK_IS_DOWN; break;\
+ case ENOBUFS : rc = GRN_NO_BUFFER; break;\
+ case EISCONN : rc = GRN_SOCKET_IS_ALREADY_CONNECTED; break;\
+ case ENOTCONN : rc = GRN_SOCKET_IS_NOT_CONNECTED; break;\
+ /*\
+ case ESOCKTNOSUPPORT :\
+ case EOPNOTSUPP :\
+ case EPFNOSUPPORT :\
+ */\
+ case EPROTONOSUPPORT : rc = GRN_OPERATION_NOT_SUPPORTED; break;\
+ case ESHUTDOWN : rc = GRN_SOCKET_IS_ALREADY_SHUTDOWNED; break;\
+ case ETIMEDOUT : rc = GRN_OPERATION_TIMEOUT; break;\
+ case ECONNREFUSED: rc = GRN_CONNECTION_REFUSED; break;\
+ case EAGAIN: rc = GRN_OPERATION_WOULD_BLOCK; break;\
+ default : rc = GRN_UNKNOWN_ERROR; break;\
+ }\
+ ERR(rc, "syscall error '%s' (%s)", str, strerror(errno));\
+} while (0)
+#endif /* WIN32 */
+
+#define GERR(rc,...) ERRSET(&grn_gctx, GRN_ERROR, (rc), __VA_ARGS__)
+#define GMERR(...) ERRSET(&grn_gctx, GRN_ALERT, GRN_NO_MEMORY_AVAILABLE, __VA_ARGS__)
+
+#define GRN_MALLOC(s) grn_malloc(ctx,s,__FILE__,__LINE__,__FUNCTION__)
+#define GRN_CALLOC(s) grn_calloc(ctx,s,__FILE__,__LINE__,__FUNCTION__)
+#define GRN_REALLOC(p,s) grn_realloc(ctx,p,s,__FILE__,__LINE__,__FUNCTION__)
+#define GRN_STRDUP(s) grn_strdup(ctx,s,__FILE__,__LINE__,__FUNCTION__)
+#define GRN_GMALLOC(s) grn_malloc(&grn_gctx,s,__FILE__,__LINE__,__FUNCTION__)
+#define GRN_GCALLOC(s) grn_calloc(&grn_gctx,s,__FILE__,__LINE__,__FUNCTION__)
+#define GRN_GREALLOC(p,s) grn_realloc(&grn_gctx,p,s,__FILE__,__LINE__,__FUNCTION__)
+#define GRN_GSTRDUP(s) grn_strdup(&grn_gctx,s,__FILE__,__LINE__,__FUNCTION__)
+#define GRN_FREE(p) grn_free(ctx,p,__FILE__,__LINE__,__FUNCTION__)
+#define GRN_MALLOCN(t,n) ((t *)(GRN_MALLOC(sizeof(t) * (n))))
+#define GRN_GFREE(p) grn_free(&grn_gctx,p,__FILE__,__LINE__,__FUNCTION__)
+#define GRN_GMALLOCN(t,n) ((t *)(GRN_GMALLOC(sizeof(t) * (n))))
+
+#ifdef DEBUG
+#define GRN_ASSERT(s) grn_assert(ctx,(s),__FILE__,__LINE__,__FUNCTION__)
+#else
+#define GRN_ASSERT(s)
+#endif
+
+#define GRN_CTX_ALLOC(ctx,s) grn_ctx_calloc(ctx,s,__FILE__,__LINE__,__FUNCTION__)
+#define GRN_CTX_FREE(ctx,p) grn_ctx_free(ctx,p,__FILE__,__LINE__,__FUNCTION__)
+#define GRN_CTX_ALLOC_L(ctx,s) grn_ctx_alloc_lifo(ctx,s,f,__FILE__,__LINE__,__FUNCTION__)
+#define GRN_CTX_FREE_L(ctx,p) grn_ctx_free_lifo(ctx,p,__FILE__,__LINE__,__FUNCTION__)
+
+void *grn_ctx_alloc(grn_ctx *ctx, size_t size, int flags,
+ const char* file, int line, const char *func);
+void *grn_ctx_malloc(grn_ctx *ctx, size_t size,
+ const char* file, int line, const char *func);
+void *grn_ctx_calloc(grn_ctx *ctx, size_t size,
+ const char* file, int line, const char *func);
+void *grn_ctx_realloc(grn_ctx *ctx, void *ptr, size_t size,
+ const char* file, int line, const char *func);
+char *grn_ctx_strdup(grn_ctx *ctx, const char *s,
+ const char* file, int line, const char *func);
+void grn_ctx_free(grn_ctx *ctx, void *ptr,
+ const char* file, int line, const char *func);
+void *grn_ctx_alloc_lifo(grn_ctx *ctx, size_t size,
+ const char* file, int line, const char *func);
+void grn_ctx_free_lifo(grn_ctx *ctx, void *ptr,
+ const char* file, int line, const char *func);
+
+#ifdef USE_DYNAMIC_MALLOC_CHANGE
+typedef void *(*grn_malloc_func) (grn_ctx *ctx, size_t size,
+ const char *file, int line, const char *func);
+typedef void *(*grn_calloc_func) (grn_ctx *ctx, size_t size,
+ const char *file, int line, const char *func);
+typedef void *(*grn_realloc_func) (grn_ctx *ctx, void *ptr, size_t size,
+ const char *file, int line, const char *func);
+typedef char *(*grn_strdup_func) (grn_ctx *ctx, const char *string,
+ const char *file, int line, const char *func);
+grn_malloc_func grn_ctx_get_malloc(grn_ctx *ctx);
+void grn_ctx_set_malloc(grn_ctx *ctx, grn_malloc_func malloc_func);
+grn_calloc_func grn_ctx_get_calloc(grn_ctx *ctx);
+void grn_ctx_set_calloc(grn_ctx *ctx, grn_calloc_func calloc_func);
+grn_realloc_func grn_ctx_get_realloc(grn_ctx *ctx);
+void grn_ctx_set_realloc(grn_ctx *ctx, grn_realloc_func realloc_func);
+grn_strdup_func grn_ctx_get_strdup(grn_ctx *ctx);
+void grn_ctx_set_strdup(grn_ctx *ctx, grn_strdup_func strdup_func);
+
+void *grn_malloc(grn_ctx *ctx, size_t size, const char* file, int line, const char *func);
+void *grn_calloc(grn_ctx *ctx, size_t size, const char* file, int line, const char *func);
+void *grn_realloc(grn_ctx *ctx, void *ptr, size_t size, const char* file, int line, const char *func);
+char *grn_strdup(grn_ctx *ctx, const char *s, const char* file, int line, const char *func);
+#else
+# define grn_malloc grn_malloc_default
+# define grn_calloc grn_calloc_default
+# define grn_realloc grn_realloc_default
+# define grn_strdup grn_strdup_default
+# define grn_free grn_free_default
+#endif
+
+GRN_API void *grn_malloc_default(grn_ctx *ctx, size_t size, const char* file, int line, const char *func);
+void *grn_calloc_default(grn_ctx *ctx, size_t size, const char* file, int line, const char *func);
+void *grn_realloc_default(grn_ctx *ctx, void *ptr, size_t size, const char* file, int line, const char *func);
+GRN_API char *grn_strdup_default(grn_ctx *ctx, const char *s, const char* file, int line, const char *func);
+GRN_API void grn_free_default(grn_ctx *ctx, void *ptr, const char* file, int line, const char *func);
+
+#ifdef USE_FAIL_MALLOC
+int grn_fail_malloc_check(size_t size, const char *file, int line, const char *func);
+void *grn_malloc_fail(grn_ctx *ctx, size_t size, const char* file, int line, const char *func);
+void *grn_calloc_fail(grn_ctx *ctx, size_t size, const char* file, int line, const char *func);
+void *grn_realloc_fail(grn_ctx *ctx, void *ptr, size_t size, const char* file, int line, const char *func);
+char *grn_strdup_fail(grn_ctx *ctx, const char *s, const char* file, int line, const char *func);
+#endif
+
+void grn_assert(grn_ctx *ctx, int cond, const char* file, int line, const char* func);
+
+/**** grn_ctx ****/
+
+GRN_VAR grn_ctx grn_gctx;
+extern int grn_pagesize;
+extern grn_critical_section grn_glock;
+extern uint32_t grn_gtick;
+extern int grn_lock_timeout;
+
+#define GRN_CTX_ALLOCATED (0x80)
+#define GRN_CTX_TEMPORARY_DISABLE_II_RESOLVE_SEL_AND (0x40)
+
+typedef struct {
+ int64_t tv_sec;
+ int32_t tv_nsec;
+} grn_timeval;
+
+extern grn_timeval grn_starttime;
+
+#ifndef GRN_TIMEVAL_STR_SIZE
+#define GRN_TIMEVAL_STR_SIZE 0x100
+#endif /* GRN_TIMEVAL_STR_SIZE */
+#ifndef GRN_TIMEVAL_STR_FORMAT
+#define GRN_TIMEVAL_STR_FORMAT "%04d-%02d-%02d %02d:%02d:%02d.%06d"
+#endif /* GRN_TIMEVAL_STR_FORMAT */
+#define GRN_TIME_NSEC_PER_SEC 1000000000
+#define GRN_TIME_NSEC_PER_SEC_F 1000000000.0
+#define GRN_TIME_NSEC_PER_USEC (GRN_TIME_NSEC_PER_SEC / GRN_TIME_USEC_PER_SEC)
+#define GRN_TIME_NSEC_TO_USEC(nsec) ((nsec) / GRN_TIME_NSEC_PER_USEC)
+#define GRN_TIME_USEC_TO_NSEC(usec) ((usec) * GRN_TIME_NSEC_PER_USEC)
+
+GRN_API grn_rc grn_timeval_now(grn_ctx *ctx, grn_timeval *tv);
+GRN_API grn_rc grn_timeval2str(grn_ctx *ctx, grn_timeval *tv, char *buf);
+grn_rc grn_str2timeval(const char *str, uint32_t str_len, grn_timeval *tv);
+
+GRN_API void grn_ctx_log(grn_ctx *ctx, const char *fmt, ...) GRN_ATTRIBUTE_PRINTF(2);
+void grn_ctx_loader_clear(grn_ctx *ctx);
+void grn_log_reopen(grn_ctx *ctx);
+
+GRN_API grn_rc grn_ctx_sendv(grn_ctx *ctx, int argc, char **argv, int flags);
+GRN_API void grn_ctx_set_next_expr(grn_ctx *ctx, grn_obj *expr);
+
+int grn_alloc_count(void);
+
+grn_content_type grn_get_ctype(grn_obj *var);
+
+/**** db_obj ****/
+
+/* flag values used for grn_obj.header.impl_flags */
+
+#define GRN_OBJ_ALLOCATED (0x01<<2) /* allocated by ctx */
+#define GRN_OBJ_EXPRVALUE (0x01<<3) /* value allocated by grn_expr */
+#define GRN_OBJ_EXPRCONST (0x01<<4) /* constant allocated by grn_expr */
+
+typedef struct _grn_hook grn_hook;
+
+typedef struct {
+ grn_obj_header header;
+ grn_id range; /* table: type of subrecords, column: type of values */
+ /* -- compatible with grn_accessor -- */
+ grn_id id;
+ grn_obj *db;
+ grn_user_data user_data;
+ grn_proc_func *finalizer;
+ grn_hook *hooks[5];
+ void *source;
+ uint32_t source_size;
+ uint32_t max_n_subrecs;
+ uint8_t subrec_size;
+ uint8_t subrec_offset;
+ uint8_t record_unit;
+ uint8_t subrec_unit;
+ // grn_obj_flags flags;
+} grn_db_obj;
+
+#define GRN_DB_OBJ_SET_TYPE(db_obj,obj_type) do {\
+ (db_obj)->obj.header.type = (obj_type);\
+ (db_obj)->obj.header.impl_flags = 0;\
+ (db_obj)->obj.header.flags = 0;\
+ (db_obj)->obj.id = GRN_ID_NIL;\
+ (db_obj)->obj.user_data.ptr = NULL;\
+ (db_obj)->obj.finalizer = NULL;\
+ (db_obj)->obj.hooks[0] = NULL;\
+ (db_obj)->obj.hooks[1] = NULL;\
+ (db_obj)->obj.hooks[2] = NULL;\
+ (db_obj)->obj.hooks[3] = NULL;\
+ (db_obj)->obj.hooks[4] = NULL;\
+ (db_obj)->obj.source = NULL;\
+ (db_obj)->obj.source_size = 0;\
+} while (0)
+
+/**** cache ****/
+
+typedef struct {
+ uint32_t nentries;
+ uint32_t max_nentries;
+ uint32_t nfetches;
+ uint32_t nhits;
+} grn_cache_statistics;
+
+void grn_cache_init(void);
+grn_obj *grn_cache_fetch(grn_ctx *ctx, grn_cache *cache,
+ const char *str, uint32_t str_size);
+void grn_cache_unref(grn_ctx *ctx, grn_cache *cache,
+ const char *str, uint32_t str_size);
+void grn_cache_update(grn_ctx *ctx, grn_cache *cache,
+ const char *str, uint32_t str_size, grn_obj *value);
+void grn_cache_expire(grn_cache *cache, int32_t size);
+void grn_cache_fin(void);
+void grn_cache_get_statistics(grn_ctx *ctx, grn_cache *cache,
+ grn_cache_statistics *statistics);
+
+/**** receive handler ****/
+
+GRN_API void grn_ctx_stream_out_func(grn_ctx *c, int flags, void *stream);
+
+grn_rc grn_db_init_builtin_procs(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_CTX_H */
diff --git a/storage/mroonga/vendor/groonga/lib/ctx_impl.h b/storage/mroonga/vendor/groonga/lib/ctx_impl.h
new file mode 100644
index 00000000000..33f1402e6e1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/ctx_impl.h
@@ -0,0 +1,188 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2009-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_CTX_IMPL_H
+#define GRN_CTX_IMPL_H
+
+#ifndef GRN_CTX_H
+#include "ctx.h"
+#endif /* GRN_CTX_H */
+
+#ifndef GRN_COM_H
+#include "com.h"
+#endif /* GRN_COM_H */
+
+#ifdef GRN_WITH_MESSAGE_PACK
+#include <msgpack.h>
+#endif
+
+#ifdef GRN_WITH_MRUBY
+# include <mruby.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**** grn_expr ****/
+
+#define GRN_EXPR_MISSING_NAME "expr_missing"
+
+/**** grn_ctx_impl ****/
+
+#define GRN_CTX_INITED 0x00
+#define GRN_CTX_QUITTING 0x0f
+
+typedef enum {
+ GRN_LOADER_BEGIN = 0,
+ GRN_LOADER_TOKEN,
+ GRN_LOADER_STRING,
+ GRN_LOADER_SYMBOL,
+ GRN_LOADER_NUMBER,
+ GRN_LOADER_STRING_ESC,
+ GRN_LOADER_UNICODE0,
+ GRN_LOADER_UNICODE1,
+ GRN_LOADER_UNICODE2,
+ GRN_LOADER_UNICODE3,
+ GRN_LOADER_END
+} grn_loader_stat;
+
+typedef struct {
+ grn_obj values;
+ grn_obj level;
+ grn_obj columns;
+ uint32_t emit_level;
+ int32_t key_offset;
+ grn_obj *table;
+ grn_obj *last;
+ grn_obj *ifexists;
+ grn_obj *each;
+ uint32_t unichar;
+ uint32_t values_size;
+ uint32_t nrecords;
+ grn_loader_stat stat;
+ grn_content_type input_type;
+} grn_loader;
+
+#define GRN_CTX_N_SEGMENTS 512
+
+#ifdef USE_MEMORY_DEBUG
+typedef struct _grn_alloc_info grn_alloc_info;
+struct _grn_alloc_info
+{
+ void *address;
+ int freed;
+ char alloc_backtrace[4096];
+ char free_backtrace[4096];
+ grn_alloc_info *next;
+};
+#endif
+
+#ifdef GRN_WITH_MRUBY
+typedef struct _grn_mrb_data grn_mrb_data;
+struct _grn_mrb_data {
+ mrb_state *state;
+ struct RClass *module;
+ struct RClass *object_class;
+};
+#endif
+
+struct _grn_ctx_impl {
+ grn_encoding encoding;
+
+ /* memory pool portion */
+ int32_t lifoseg;
+ int32_t currseg;
+ grn_critical_section lock;
+ grn_io_mapinfo segs[GRN_CTX_N_SEGMENTS];
+
+#ifdef USE_DYNAMIC_MALLOC_CHANGE
+ /* memory allocation portion */
+ grn_malloc_func malloc_func;
+ grn_calloc_func calloc_func;
+ grn_realloc_func realloc_func;
+ grn_strdup_func strdup_func;
+#endif
+
+#ifdef USE_MEMORY_DEBUG
+ /* memory debug portion */
+ grn_alloc_info *alloc_info;
+#endif
+
+ /* expression portion */
+ grn_obj *stack[GRN_STACK_SIZE];
+ uint32_t stack_curr;
+ grn_hash *expr_vars;
+ grn_obj *curr_expr;
+ grn_obj *qe_next;
+ void *parser;
+ grn_timeval tv;
+
+ /* loader portion */
+ grn_edge *edge;
+ grn_loader loader;
+
+ /* plugin portion */
+ const char *plugin_path;
+
+ /* output portion */
+ grn_content_type output_type;
+ const char *mime_type;
+ grn_obj names;
+ grn_obj levels;
+
+ /* command portion */
+ grn_command_version command_version;
+
+ /* match escalation portion */
+ int64_t match_escalation_threshold;
+
+ /* lifetime portion */
+ grn_proc_func *finalizer;
+
+ grn_obj *db;
+ grn_array *values; /* temporary objects */
+ grn_hash *ios; /* IOs */
+ grn_obj *outbuf;
+ void (*output)(grn_ctx *, int, void *);
+ grn_com *com;
+ unsigned int com_status;
+ union {
+ void *ptr;
+ int fd;
+ uint32_t u32;
+ uint64_t u64;
+ } data;
+
+ grn_obj query_log_buf;
+
+ char previous_errbuf[GRN_CTX_MSGSIZE];
+ unsigned int n_same_error_messages;
+
+#ifdef GRN_WITH_MESSAGE_PACK
+ msgpack_packer msgpacker;
+#endif
+#ifdef GRN_WITH_MRUBY
+ grn_mrb_data mrb;
+#endif
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_CTX_IMPL_H */
diff --git a/storage/mroonga/vendor/groonga/lib/ctx_impl_mrb.c b/storage/mroonga/vendor/groonga/lib/ctx_impl_mrb.c
new file mode 100644
index 00000000000..657e95ae4ce
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/ctx_impl_mrb.c
@@ -0,0 +1,104 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "ctx_impl_mrb.h"
+#include "ctx_impl.h"
+
+#include "mrb.h"
+#include "mrb/mrb_id.h"
+#include "mrb/mrb_operator.h"
+#include "mrb/mrb_ctx.h"
+#include "mrb/mrb_logger.h"
+#include "mrb/mrb_void.h"
+#include "mrb/mrb_bulk.h"
+#include "mrb/mrb_obj.h"
+#include "mrb/mrb_column.h"
+#include "mrb/mrb_fixed_size_column.h"
+#include "mrb/mrb_variable_size_column.h"
+#include "mrb/mrb_index_column.h"
+#include "mrb/mrb_expr.h"
+#include "mrb/mrb_accessor.h"
+#include "mrb/mrb_procedure.h"
+
+#ifdef GRN_WITH_MRUBY
+static void
+grn_ctx_impl_mrb_init_bindings(grn_ctx *ctx)
+{
+ mrb_state *mrb = ctx->impl->mrb.state;
+
+ mrb->ud = ctx;
+ ctx->impl->mrb.module = mrb_define_module(mrb, "Groonga");
+
+ grn_mrb_load(ctx, "backtrace_entry.rb");
+
+ grn_mrb_id_init(ctx);
+ grn_mrb_operator_init(ctx);
+ grn_mrb_ctx_init(ctx);
+ grn_mrb_logger_init(ctx);
+ grn_mrb_void_init(ctx);
+ grn_mrb_bulk_init(ctx);
+ grn_mrb_obj_init(ctx);
+ grn_mrb_column_init(ctx);
+ grn_mrb_fixed_size_column_init(ctx);
+ grn_mrb_variable_size_column_init(ctx);
+ grn_mrb_index_column_init(ctx);
+ grn_mrb_expr_init(ctx);
+ grn_mrb_accessor_init(ctx);
+ grn_mrb_procedure_init(ctx);
+}
+
+static void
+grn_ctx_impl_mrb_init_eval(grn_ctx *ctx)
+{
+ grn_mrb_load(ctx, "eval_context.rb");
+}
+
+void
+grn_ctx_impl_mrb_init(grn_ctx *ctx)
+{
+ const char *grn_mruby_enabled;
+ grn_mruby_enabled = getenv("GRN_MRUBY_ENABLED");
+ if (grn_mruby_enabled && strcmp(grn_mruby_enabled, "no") == 0) {
+ ctx->impl->mrb.state = NULL;
+ ctx->impl->mrb.module = NULL;
+ } else {
+ ctx->impl->mrb.state = mrb_open();
+ grn_ctx_impl_mrb_init_bindings(ctx);
+ grn_ctx_impl_mrb_init_eval(ctx);
+ }
+}
+
+void
+grn_ctx_impl_mrb_fin(grn_ctx *ctx)
+{
+ if (ctx->impl->mrb.state) {
+ mrb_close(ctx->impl->mrb.state);
+ ctx->impl->mrb.state = NULL;
+ }
+}
+#else
+void
+grn_ctx_impl_mrb_init(grn_ctx *ctx)
+{
+}
+
+void
+grn_ctx_impl_mrb_fin(grn_ctx *ctx)
+{
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/ctx_impl_mrb.h b/storage/mroonga/vendor/groonga/lib/ctx_impl_mrb.h
new file mode 100644
index 00000000000..6810d1fc097
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/ctx_impl_mrb.h
@@ -0,0 +1,36 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_CTX_IMPL_MRB_H
+#define GRN_CTX_IMPL_MRB_H
+
+#include "groonga_in.h"
+#include "ctx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_ctx_impl_mrb_init(grn_ctx *ctx);
+void grn_ctx_impl_mrb_fin(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_CTX_IMPL_MRB_H */
diff --git a/storage/mroonga/vendor/groonga/lib/dat.cpp b/storage/mroonga/vendor/groonga/lib/dat.cpp
new file mode 100644
index 00000000000..2d335e1f2f1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat.cpp
@@ -0,0 +1,1099 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "groonga_in.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <cstring>
+#include <new>
+#include "str.h"
+#include "io.h"
+#include "dat.h"
+#include "util.h"
+#include "normalizer_in.h"
+
+#include "dat/trie.hpp"
+#include "dat/cursor-factory.hpp"
+
+namespace {
+
+const uint32_t FILE_ID_LENGTH = 3;
+
+class CriticalSection {
+ public:
+ CriticalSection() : lock_(NULL) {}
+ explicit CriticalSection(grn_critical_section *lock) : lock_(lock) {
+ CRITICAL_SECTION_ENTER(*lock_);
+ }
+ ~CriticalSection() {
+ leave();
+ }
+
+ void enter(grn_critical_section *lock) {
+ leave();
+ lock_ = lock;
+ }
+ void leave() {
+ if (lock_ != NULL) {
+ CRITICAL_SECTION_LEAVE(*lock_);
+ lock_ = NULL;
+ }
+ }
+
+ private:
+ grn_critical_section *lock_;
+
+ // Disallows copy and assignment.
+ CriticalSection(const CriticalSection &);
+ CriticalSection &operator=(const CriticalSection &);
+};
+
+/*
+ grn_dat_remove_file() removes a file specified by `path' and then returns
+ true on success, false on failure. Note that grn_dat_remove_file() does not
+ change `ctx->rc'.
+ */
+bool
+grn_dat_remove_file(grn_ctx *ctx, const char *path)
+{
+ struct stat stat;
+ return !::stat(path, &stat) && !unlink(path);
+}
+
+grn_rc
+grn_dat_translate_error_code(grn::dat::ErrorCode error_code) {
+ switch (error_code) {
+ case grn::dat::PARAM_ERROR: {
+ return GRN_INVALID_ARGUMENT;
+ }
+ case grn::dat::IO_ERROR: {
+ return GRN_INPUT_OUTPUT_ERROR;
+ }
+ case grn::dat::FORMAT_ERROR: {
+ return GRN_INVALID_FORMAT;
+ }
+ case grn::dat::MEMORY_ERROR: {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ case grn::dat::SIZE_ERROR:
+ case grn::dat::UNEXPECTED_ERROR: {
+ return GRN_UNKNOWN_ERROR;
+ }
+ case grn::dat::STATUS_ERROR: {
+ return GRN_FILE_CORRUPT;
+ }
+ default: {
+ return GRN_UNKNOWN_ERROR;
+ }
+ }
+}
+
+void
+grn_dat_init(grn_ctx *, grn_dat *dat)
+{
+ GRN_DB_OBJ_SET_TYPE(dat, GRN_TABLE_DAT_KEY);
+ dat->io = NULL;
+ dat->header = NULL;
+ dat->file_id = 0;
+ dat->encoding = GRN_ENC_DEFAULT;
+ dat->trie = NULL;
+ dat->old_trie = NULL;
+ dat->tokenizer = NULL;
+ CRITICAL_SECTION_INIT(dat->lock);
+}
+
+void
+grn_dat_fin(grn_ctx *ctx, grn_dat *dat)
+{
+ CRITICAL_SECTION_FIN(dat->lock);
+ delete static_cast<grn::dat::Trie *>(dat->old_trie);
+ delete static_cast<grn::dat::Trie *>(dat->trie);
+ dat->old_trie = NULL;
+ dat->trie = NULL;
+ if (dat->io) {
+ grn_io_close(ctx, dat->io);
+ dat->io = NULL;
+ }
+}
+
+/*
+ grn_dat_generate_trie_path() generates the path from `base_path' and
+ `file_id'. The generated path is stored in `trie_path'.
+ */
+void
+grn_dat_generate_trie_path(const char *base_path, char *trie_path, uint32_t file_id)
+{
+ if (!base_path || !base_path[0]) {
+ trie_path[0] = '\0';
+ return;
+ }
+ const size_t len = std::strlen(base_path);
+ std::memcpy(trie_path, base_path, len);
+ trie_path[len] = '.';
+ grn_itoh(file_id % (1U << (4 * FILE_ID_LENGTH)),
+ trie_path + len + 1, FILE_ID_LENGTH);
+ trie_path[len + 1 + FILE_ID_LENGTH] = '\0';
+}
+
+bool
+grn_dat_open_trie_if_needed(grn_ctx *ctx, grn_dat *dat)
+{
+ if (!dat) {
+ ERR(GRN_INVALID_ARGUMENT, "dat is null");
+ return false;
+ }
+
+ const uint32_t file_id = dat->header->file_id;
+ if (!file_id || (dat->trie && (file_id <= dat->file_id))) {
+ /*
+ There is no need to open file when no trie file is available or the
+ current trie file is the latest one.
+ */
+ return true;
+ }
+
+ CriticalSection critical_section(&dat->lock);
+
+ if (dat->trie && (file_id <= dat->file_id)) {
+ /*
+ There is no need to open file if the latest file has been opened by
+ another thread.
+ */
+ return true;
+ }
+
+ char trie_path[PATH_MAX];
+ grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, file_id);
+ grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ grn::dat::Trie * const old_trie = static_cast<grn::dat::Trie *>(dat->old_trie);
+ grn::dat::Trie * const new_trie = new (std::nothrow) grn::dat::Trie;
+ if (!new_trie) {
+ MERR("new grn::dat::Trie failed");
+ return false;
+ }
+
+ try {
+ new_trie->open(trie_path);
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::Trie::open failed");
+ delete new_trie;
+ return false;
+ }
+
+ dat->old_trie = trie;
+ dat->trie = new_trie;
+ dat->file_id = file_id;
+
+ critical_section.leave();
+
+ delete old_trie;
+ if (file_id >= 3) {
+ grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, file_id - 2);
+ grn_dat_remove_file(ctx, trie_path);
+ }
+ return true;
+}
+
+bool grn_dat_rebuild_trie(grn_ctx *ctx, grn_dat *dat) {
+ grn::dat::Trie * const new_trie = new (std::nothrow) grn::dat::Trie;
+ if (!new_trie) {
+ MERR("new grn::dat::Trie failed");
+ return false;
+ }
+
+ const uint32_t file_id = dat->header->file_id;
+ try {
+ char trie_path[PATH_MAX];
+ grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, file_id + 1);
+ const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ new_trie->create(*trie, trie_path, trie->file_size() * 2);
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::Trie::open failed");
+ delete new_trie;
+ return false;
+ }
+
+ grn::dat::Trie * const old_trie = static_cast<grn::dat::Trie *>(dat->old_trie);
+ dat->old_trie = dat->trie;
+ dat->trie = new_trie;
+ dat->header->file_id = dat->file_id = file_id + 1;
+
+ delete old_trie;
+ if (file_id >= 2) {
+ char trie_path[PATH_MAX];
+ grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, file_id - 1);
+ grn_dat_remove_file(ctx, trie_path);
+ }
+ return true;
+}
+
+void grn_dat_cursor_init(grn_ctx *, grn_dat_cursor *cursor) {
+ GRN_DB_OBJ_SET_TYPE(cursor, GRN_CURSOR_TABLE_DAT_KEY);
+ cursor->dat = NULL;
+ cursor->cursor = NULL;
+ cursor->key = &grn::dat::Key::invalid_key();
+ cursor->curr_rec = GRN_ID_NIL;
+}
+
+void grn_dat_cursor_fin(grn_ctx *, grn_dat_cursor *cursor) {
+ delete static_cast<grn::dat::Cursor *>(cursor->cursor);
+ cursor->dat = NULL;
+ cursor->cursor = NULL;
+ cursor->key = &grn::dat::Key::invalid_key();
+ cursor->curr_rec = GRN_ID_NIL;
+}
+
+} // namespace
+
+extern "C" {
+
+grn_dat *
+grn_dat_create(grn_ctx *ctx, const char *path, uint32_t,
+ uint32_t, uint32_t flags)
+{
+ if (path) {
+ if (path[0] == '\0') {
+ path = NULL;
+ } else if (std::strlen(path) >= (PATH_MAX - (FILE_ID_LENGTH + 1))) {
+ ERR(GRN_FILENAME_TOO_LONG, "too long path");
+ return NULL;
+ }
+ }
+
+ grn_dat * const dat = static_cast<grn_dat *>(GRN_MALLOC(sizeof(grn_dat)));
+ if (!dat) {
+ return NULL;
+ }
+ grn_dat_init(ctx, dat);
+
+ dat->io = grn_io_create(ctx, path, sizeof(struct grn_dat_header),
+ 4096, 0, grn_io_auto, GRN_IO_EXPIRE_SEGMENT);
+ if (!dat->io) {
+ GRN_FREE(dat);
+ return NULL;
+ }
+ grn_io_set_type(dat->io, GRN_TABLE_DAT_KEY);
+
+ dat->header = static_cast<struct grn_dat_header *>(grn_io_header(dat->io));
+ if (!dat->header) {
+ grn_io_close(ctx, dat->io);
+ grn_dat_remove_file(ctx, path);
+ GRN_FREE(dat);
+ return NULL;
+ }
+ const grn_encoding encoding = (ctx->encoding != GRN_ENC_DEFAULT) ?
+ ctx->encoding : grn_gctx.encoding;
+ dat->header->flags = flags;
+ dat->header->encoding = encoding;
+ dat->header->tokenizer = GRN_ID_NIL;
+ dat->header->file_id = 0;
+ if (dat->header->flags & GRN_OBJ_KEY_NORMALIZE) {
+ dat->header->flags &= ~GRN_OBJ_KEY_NORMALIZE;
+ dat->normalizer = grn_ctx_get(ctx, GRN_NORMALIZER_AUTO_NAME, -1);
+ dat->header->normalizer = grn_obj_id(ctx, dat->normalizer);
+ } else {
+ dat->normalizer = NULL;
+ dat->header->normalizer = GRN_ID_NIL;
+ }
+ dat->encoding = encoding;
+ dat->tokenizer = NULL;
+
+ dat->obj.header.flags = dat->header->flags;
+
+ return dat;
+}
+
+grn_dat *
+grn_dat_open(grn_ctx *ctx, const char *path)
+{
+ if (path && (std::strlen(path) >= (PATH_MAX - (FILE_ID_LENGTH + 1)))) {
+ ERR(GRN_FILENAME_TOO_LONG, "too long path");
+ return NULL;
+ }
+
+ grn_dat * const dat = static_cast<grn_dat *>(GRN_MALLOC(sizeof(grn_dat)));
+ if (!dat) {
+ return NULL;
+ }
+
+ grn_dat_init(ctx, dat);
+ dat->io = grn_io_open(ctx, path, grn_io_auto);
+ if (!dat->io) {
+ GRN_FREE(dat);
+ return NULL;
+ }
+
+ dat->header = (struct grn_dat_header *)grn_io_header(dat->io);
+ if (!dat->header) {
+ grn_io_close(ctx, dat->io);
+ GRN_FREE(dat);
+ return NULL;
+ }
+ dat->file_id = dat->header->file_id;
+ dat->encoding = dat->header->encoding;
+ dat->tokenizer = grn_ctx_at(ctx, dat->header->tokenizer);
+ if (dat->header->flags & GRN_OBJ_KEY_NORMALIZE) {
+ dat->header->flags &= ~GRN_OBJ_KEY_NORMALIZE;
+ dat->normalizer = grn_ctx_get(ctx, GRN_NORMALIZER_AUTO_NAME, -1);
+ dat->header->normalizer = grn_obj_id(ctx, dat->normalizer);
+ } else {
+ dat->normalizer = grn_ctx_at(ctx, dat->header->normalizer);
+ }
+ dat->obj.header.flags = dat->header->flags;
+ return dat;
+}
+
+grn_rc
+grn_dat_close(grn_ctx *ctx, grn_dat *dat)
+{
+ if (dat) {
+ grn_dat_fin(ctx, dat);
+ GRN_FREE(dat);
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_dat_remove(grn_ctx *ctx, const char *path)
+{
+ if (!path) {
+ ERR(GRN_INVALID_ARGUMENT, "path is null");
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ grn_dat * const dat = grn_dat_open(ctx, path);
+ if (!dat) {
+ return ctx->rc;
+ }
+ const uint32_t file_id = dat->header->file_id;
+ grn_dat_close(ctx, dat);
+
+ /*
+ grn_dat_remove() tries to remove (file_id + 1)th trie file because
+ grn::dat::Trie::create() might leave an incomplete file on failure.
+ */
+ char trie_path[PATH_MAX];
+ grn_dat_generate_trie_path(path, trie_path, file_id + 1);
+ grn_dat_remove_file(ctx, trie_path);
+ for (uint32_t i = file_id; i > 0; --i) {
+ grn_dat_generate_trie_path(path, trie_path, i);
+ if (!grn_dat_remove_file(ctx, trie_path)) {
+ break;
+ }
+ }
+
+ /*
+ grn_io_remove() reports an error when it fails to remove `path'.
+ */
+ return grn_io_remove(ctx, path);
+}
+
+grn_id
+grn_dat_get(grn_ctx *ctx, grn_dat *dat, const void *key,
+ unsigned int key_size, void **)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return GRN_ID_NIL;
+ }
+ const grn::dat::Trie * const trie = static_cast<const grn::dat::Trie *>(dat->trie);
+ if (!trie) {
+ return GRN_ID_NIL;
+ }
+ grn::dat::UInt32 key_pos;
+ try {
+ if (trie->search(key, key_size, &key_pos)) {
+ return trie->get_key(key_pos).id();
+ }
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::Trie::search failed");
+ }
+ return GRN_ID_NIL;
+}
+
+grn_id
+grn_dat_add(grn_ctx *ctx, grn_dat *dat, const void *key,
+ unsigned int key_size, void **, int *added)
+{
+ if (!key_size) {
+ return GRN_ID_NIL;
+ } else if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return GRN_ID_NIL;
+ }
+
+ if (!dat->trie) {
+ char trie_path[PATH_MAX];
+ grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, 1);
+ grn::dat::Trie * const new_trie = new (std::nothrow) grn::dat::Trie;
+ if (!new_trie) {
+ MERR("new grn::dat::Trie failed");
+ return GRN_ID_NIL;
+ }
+ try {
+ new_trie->create(trie_path);
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::Trie::create failed");
+ delete new_trie;
+ return GRN_ID_NIL;
+ }
+ dat->trie = new_trie;
+ dat->file_id = dat->header->file_id = 1;
+ }
+
+ grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ try {
+ grn::dat::UInt32 key_pos;
+ const bool res = trie->insert(key, key_size, &key_pos);
+ if (added) {
+ *added = res ? 1 : 0;
+ }
+ return trie->get_key(key_pos).id();
+ } catch (const grn::dat::SizeError &) {
+ if (!grn_dat_rebuild_trie(ctx, dat)) {
+ return GRN_ID_NIL;
+ }
+ grn::dat::Trie * const new_trie = static_cast<grn::dat::Trie *>(dat->trie);
+ grn::dat::UInt32 key_pos;
+ const bool res = new_trie->insert(key, key_size, &key_pos);
+ if (added) {
+ *added = res ? 1 : 0;
+ }
+ return new_trie->get_key(key_pos).id();
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::Trie::insert failed");
+ return GRN_ID_NIL;
+ }
+}
+
+int
+grn_dat_get_key(grn_ctx *ctx, grn_dat *dat, grn_id id, void *keybuf, int bufsize)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return 0;
+ }
+ const grn::dat::Trie * const trie = static_cast<const grn::dat::Trie *>(dat->trie);
+ if (!trie) {
+ return 0;
+ }
+ const grn::dat::Key &key = trie->ith_key(id);
+ if (!key.is_valid()) {
+ return 0;
+ }
+ if (keybuf && (bufsize >= (int)key.length())) {
+ std::memcpy(keybuf, key.ptr(), key.length());
+ }
+ return (int)key.length();
+}
+
+int
+grn_dat_get_key2(grn_ctx *ctx, grn_dat *dat, grn_id id, grn_obj *bulk)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return 0;
+ }
+ const grn::dat::Trie * const trie = static_cast<const grn::dat::Trie *>(dat->trie);
+ if (!trie) {
+ return 0;
+ }
+ const grn::dat::Key &key = trie->ith_key(id);
+ if (!key.is_valid()) {
+ return 0;
+ }
+ if (bulk->header.impl_flags & GRN_OBJ_REFER) {
+ bulk->u.b.head = static_cast<char *>(const_cast<void *>(key.ptr()));
+ bulk->u.b.curr = bulk->u.b.head + key.length();
+ } else {
+ grn_bulk_write(ctx, bulk, static_cast<const char *>(key.ptr()), key.length());
+ }
+ return (int)key.length();
+}
+
+grn_rc
+grn_dat_delete_by_id(grn_ctx *ctx, grn_dat *dat, grn_id id,
+ grn_table_delete_optarg *optarg)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return ctx->rc;
+ } else if (!dat->trie || (id == GRN_ID_NIL)) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ if (optarg && optarg->func) {
+ const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie->ith_entry(id).is_valid()) {
+ return GRN_INVALID_ARGUMENT;
+ } else if (!optarg->func(ctx, reinterpret_cast<grn_obj *>(dat), id, optarg->func_arg)) {
+ return GRN_SUCCESS;
+ }
+ }
+
+ try {
+ grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie->remove(id)) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::Trie::remove failed");
+ return ctx->rc;
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_dat_delete(grn_ctx *ctx, grn_dat *dat, const void *key, unsigned int key_size,
+ grn_table_delete_optarg *optarg)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return ctx->rc;
+ } else if (!dat->trie || !key || !key_size) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ if (optarg && optarg->func) {
+ try {
+ const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ grn::dat::UInt32 key_pos;
+ if (!trie->search(key, key_size, &key_pos)) {
+ return GRN_INVALID_ARGUMENT;
+ } else if (!optarg->func(ctx, reinterpret_cast<grn_obj *>(dat),
+ trie->get_key(key_pos).id(), optarg->func_arg)) {
+ return GRN_SUCCESS;
+ }
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::Trie::search failed");
+ return ctx->rc;
+ }
+ }
+
+ try {
+ grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie->remove(key, key_size)) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::Trie::remove failed");
+ return ctx->rc;
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_dat_update_by_id(grn_ctx *ctx, grn_dat *dat, grn_id src_key_id,
+ const void *dest_key, unsigned int dest_key_size)
+{
+ if (!dest_key_size) {
+ return GRN_INVALID_ARGUMENT;
+ } else if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return ctx->rc;
+ } else if (!dat->trie) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ try {
+ try {
+ grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie->update(src_key_id, dest_key, dest_key_size)) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ } catch (const grn::dat::SizeError &) {
+ if (!grn_dat_rebuild_trie(ctx, dat)) {
+ return ctx->rc;
+ }
+ grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie->update(src_key_id, dest_key, dest_key_size)) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ }
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::Trie::update failed");
+ return ctx->rc;
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_dat_update(grn_ctx *ctx, grn_dat *dat,
+ const void *src_key, unsigned int src_key_size,
+ const void *dest_key, unsigned int dest_key_size)
+{
+ if (!dest_key_size) {
+ return GRN_INVALID_ARGUMENT;
+ } else if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return ctx->rc;
+ } else if (!dat->trie) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ try {
+ try {
+ grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie->update(src_key, src_key_size, dest_key, dest_key_size)) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ } catch (const grn::dat::SizeError &) {
+ if (!grn_dat_rebuild_trie(ctx, dat)) {
+ return ctx->rc;
+ }
+ grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie->update(src_key, src_key_size, dest_key, dest_key_size)) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ }
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::Trie::update failed");
+ return ctx->rc;
+ }
+ return GRN_SUCCESS;
+}
+
+int
+grn_dat_scan(grn_ctx *ctx, grn_dat *dat, const char *str,
+ unsigned int str_size, grn_dat_scan_hit *scan_hits,
+ unsigned int max_num_scan_hits, const char **str_rest)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat) || !str ||
+ !(dat->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) || !scan_hits) {
+ return -1;
+ }
+
+ grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie) {
+ return -1;
+ }
+
+ if (!max_num_scan_hits || !str_size) {
+ if (str_rest) {
+ *str_rest = str;
+ }
+ return 0;
+ }
+
+ unsigned int num_scan_hits = 0;
+ try {
+ if (dat->normalizer) {
+ int flags = GRN_STRING_WITH_CHECKS;
+ grn_obj * const normalized_string = grn_string_open(ctx, str, str_size,
+ dat->normalizer,
+ flags);
+ if (!normalized_string) {
+ fprintf(stderr, "error: grn_string_open() failed!\n");
+ return -1;
+ }
+ grn_string_get_normalized(ctx, normalized_string, &str, &str_size, NULL);
+ const short *checks = grn_string_get_checks(ctx, normalized_string);
+ unsigned int offset = 0;
+ while (str_size) {
+ if (*checks) {
+ grn::dat::UInt32 key_pos;
+ if (trie->lcp_search(str, str_size, &key_pos)) {
+ const grn::dat::Key &key = trie->get_key(key_pos);
+ const grn::dat::UInt32 key_length = key.length();
+ if ((key_length == str_size) || (checks[key_length])) {
+ unsigned int length = 0;
+ for (grn::dat::UInt32 i = 0; i < key_length; ++i) {
+ if (checks[i] > 0) {
+ length += checks[i];
+ }
+ }
+ scan_hits[num_scan_hits].id = key.id();
+ scan_hits[num_scan_hits].offset = offset;
+ scan_hits[num_scan_hits].length = length;
+ offset += length;
+ str += key_length;
+ str_size -= key_length;
+ checks += key_length;
+ if (++num_scan_hits >= max_num_scan_hits) {
+ break;
+ }
+ continue;
+ }
+ }
+ if (*checks > 0) {
+ offset += *checks;
+ }
+ }
+ ++str;
+ --str_size;
+ ++checks;
+ }
+ if (str_rest) {
+ grn_string_get_original(ctx, normalized_string, str_rest, NULL);
+ *str_rest += offset;
+ }
+ grn_obj_close(ctx, normalized_string);
+ } else {
+ const char * const begin = str;
+ while (str_size) {
+ grn::dat::UInt32 key_pos;
+ if (trie->lcp_search(str, str_size, &key_pos)) {
+ const grn::dat::Key &key = trie->get_key(key_pos);
+ scan_hits[num_scan_hits].id = key.id();
+ scan_hits[num_scan_hits].offset = str - begin;
+ scan_hits[num_scan_hits].length = key.length();
+ str += key.length();
+ str_size -= key.length();
+ if (++num_scan_hits >= max_num_scan_hits) {
+ break;
+ }
+ } else {
+ const int char_length = grn_charlen(ctx, str, str + str_size);
+ if (char_length) {
+ str += char_length;
+ str_size -= char_length;
+ } else {
+ ++str;
+ --str_size;
+ }
+ }
+ }
+ if (str_rest) {
+ *str_rest = str;
+ }
+ }
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::lcp_search failed");
+ return -1;
+ }
+ return static_cast<int>(num_scan_hits);
+}
+
+grn_id
+grn_dat_lcp_search(grn_ctx *ctx, grn_dat *dat,
+ const void *key, unsigned int key_size)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat) || !key ||
+ !(dat->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE)) {
+ return GRN_ID_NIL;
+ }
+
+ grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie) {
+ return GRN_ID_NIL;
+ }
+
+ try {
+ grn::dat::UInt32 key_pos;
+ if (!trie->lcp_search(key, key_size, &key_pos)) {
+ return GRN_ID_NIL;
+ }
+ return trie->get_key(key_pos).id();
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::PrefixCursor::open failed");
+ return GRN_ID_NIL;
+ }
+}
+
+unsigned int
+grn_dat_size(grn_ctx *ctx, grn_dat *dat)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return 0;
+ }
+ const grn::dat::Trie * const trie = static_cast<const grn::dat::Trie *>(dat->trie);
+ if (trie) {
+ return trie->num_keys();
+ }
+ return 0;
+}
+
+grn_dat_cursor *
+grn_dat_cursor_open(grn_ctx *ctx, grn_dat *dat,
+ const void *min, unsigned int min_size,
+ const void *max, unsigned int max_size,
+ int offset, int limit, int flags)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return NULL;
+ }
+
+ grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie) {
+ grn_dat_cursor * const dc =
+ static_cast<grn_dat_cursor *>(GRN_MALLOC(sizeof(grn_dat_cursor)));
+ if (dc) {
+ grn_dat_cursor_init(ctx, dc);
+ }
+ return dc;
+ }
+
+ grn_dat_cursor * const dc =
+ static_cast<grn_dat_cursor *>(GRN_MALLOC(sizeof(grn_dat_cursor)));
+ if (!dc) {
+ return NULL;
+ }
+ grn_dat_cursor_init(ctx, dc);
+
+ try {
+ if ((flags & GRN_CURSOR_BY_ID) != 0) {
+ dc->cursor = grn::dat::CursorFactory::open(*trie,
+ min, min_size, max, max_size, offset, limit,
+ grn::dat::ID_RANGE_CURSOR |
+ ((flags & GRN_CURSOR_DESCENDING) ? grn::dat::DESCENDING_CURSOR : 0) |
+ ((flags & GRN_CURSOR_GT) ? grn::dat::EXCEPT_LOWER_BOUND : 0) |
+ ((flags & GRN_CURSOR_LT) ? grn::dat::EXCEPT_UPPER_BOUND : 0));
+ } else if ((flags & GRN_CURSOR_PREFIX) != 0) {
+ if (max && max_size) {
+ if ((dat->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) != 0) {
+ dc->cursor = grn::dat::CursorFactory::open(*trie,
+ NULL, min_size, max, max_size, offset, limit,
+ grn::dat::PREFIX_CURSOR | grn::dat::DESCENDING_CURSOR);
+ } else {
+ // TODO: near
+ }
+ } else if (min && min_size) {
+ if ((flags & GRN_CURSOR_RK) != 0) {
+ // TODO: rk search
+ } else {
+ dc->cursor = grn::dat::CursorFactory::open(*trie,
+ min, min_size, NULL, 0, offset, limit,
+ grn::dat::PREDICTIVE_CURSOR |
+ ((flags & GRN_CURSOR_DESCENDING) ? grn::dat::DESCENDING_CURSOR : 0) |
+ ((flags & GRN_CURSOR_GT) ? grn::dat::EXCEPT_EXACT_MATCH : 0));
+ }
+ }
+ } else {
+ dc->cursor = grn::dat::CursorFactory::open(*trie,
+ min, min_size, max, max_size, offset, limit,
+ grn::dat::KEY_RANGE_CURSOR |
+ ((flags & GRN_CURSOR_DESCENDING) ? grn::dat::DESCENDING_CURSOR : 0) |
+ ((flags & GRN_CURSOR_GT) ? grn::dat::EXCEPT_LOWER_BOUND : 0) |
+ ((flags & GRN_CURSOR_LT) ? grn::dat::EXCEPT_UPPER_BOUND : 0));
+ }
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::CursorFactory::open failed");
+ GRN_FREE(dc);
+ return NULL;
+ }
+ if (!dc->cursor) {
+ ERR(GRN_INVALID_ARGUMENT, "unsupported query");
+ GRN_FREE(dc);
+ return NULL;
+ }
+ dc->dat = dat;
+ return dc;
+}
+
+grn_id
+grn_dat_cursor_next(grn_ctx *ctx, grn_dat_cursor *c)
+{
+ if (!c || !c->cursor) {
+ return GRN_ID_NIL;
+ }
+ try {
+ grn::dat::Cursor * const cursor = static_cast<grn::dat::Cursor *>(c->cursor);
+ const grn::dat::Key &key = cursor->next();
+ c->key = &key;
+ c->curr_rec = key.is_valid() ? key.id() : GRN_ID_NIL;
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::Cursor::next failed");
+ return GRN_ID_NIL;
+ }
+ return c->curr_rec;
+}
+
+void
+grn_dat_cursor_close(grn_ctx *ctx, grn_dat_cursor *c)
+{
+ if (c) {
+ grn_dat_cursor_fin(ctx, c);
+ GRN_FREE(c);
+ }
+}
+
+int
+grn_dat_cursor_get_key(grn_ctx *ctx, grn_dat_cursor *c, const void **key)
+{
+ if (c) {
+ const grn::dat::Key &key_ref = *static_cast<const grn::dat::Key *>(c->key);
+ if (key_ref.is_valid()) {
+ *key = key_ref.ptr();
+ return (int)key_ref.length();
+ }
+ }
+ return 0;
+}
+
+grn_rc
+grn_dat_cursor_delete(grn_ctx *ctx, grn_dat_cursor *c,
+ grn_table_delete_optarg *optarg)
+{
+ if (!c || !c->cursor) {
+ return GRN_INVALID_ARGUMENT;
+ } else if (!grn_dat_open_trie_if_needed(ctx, c->dat)) {
+ return ctx->rc;
+ }
+ grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(c->dat->trie);
+ if (!trie) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ try {
+ if (trie->remove(c->curr_rec)) {
+ return GRN_SUCCESS;
+ }
+ } catch (const grn::dat::Exception &ex) {
+ ERR(grn_dat_translate_error_code(ex.code()),
+ "grn::dat::Trie::remove failed");
+ return GRN_INVALID_ARGUMENT;
+ }
+ return GRN_INVALID_ARGUMENT;
+}
+
+grn_id
+grn_dat_curr_id(grn_ctx *ctx, grn_dat *dat)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return GRN_ID_NIL;
+ }
+ const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (trie) {
+ return trie->max_key_id();
+ }
+ return GRN_ID_NIL;
+}
+
+grn_rc
+grn_dat_truncate(grn_ctx *ctx, grn_dat *dat)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return ctx->rc;
+ }
+ const grn::dat::Trie * const trie = static_cast<const grn::dat::Trie *>(dat->trie);
+ if (!trie || !trie->max_key_id()) {
+ return GRN_SUCCESS;
+ }
+
+ char trie_path[PATH_MAX];
+ grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, dat->header->file_id + 1);
+ try {
+ grn::dat::Trie().create(trie_path);
+ } catch (const grn::dat::Exception &ex) {
+ const grn_rc error_code = grn_dat_translate_error_code(ex.code());
+ ERR(error_code, "grn::dat::Trie::create failed");
+ return error_code;
+ }
+ ++dat->header->file_id;
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return ctx->rc;
+ }
+ return GRN_SUCCESS;
+}
+
+const char *
+_grn_dat_key(grn_ctx *ctx, grn_dat *dat, grn_id id, uint32_t *key_size)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return NULL;
+ }
+ const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie) {
+ return NULL;
+ }
+ const grn::dat::Key &key = trie->ith_key(id);
+ if (!key.is_valid()) {
+ return NULL;
+ }
+ *key_size = key.length();
+ return static_cast<const char *>(key.ptr());
+}
+
+grn_id
+grn_dat_next(grn_ctx *ctx, grn_dat *dat, grn_id id)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return GRN_ID_NIL;
+ }
+ const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie) {
+ return GRN_ID_NIL;
+ }
+ while (id < trie->max_key_id()) {
+ if (trie->ith_key(++id).is_valid()) {
+ return id;
+ }
+ }
+ return GRN_ID_NIL;
+}
+
+grn_id
+grn_dat_at(grn_ctx *ctx, grn_dat *dat, grn_id id)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return GRN_ID_NIL;
+ }
+ const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie) {
+ return GRN_ID_NIL;
+ }
+ const grn::dat::Key &key = trie->ith_key(id);
+ if (!key.is_valid()) {
+ return GRN_ID_NIL;
+ }
+ return id;
+}
+
+grn_rc
+grn_dat_clear_status_flags(grn_ctx *ctx, grn_dat *dat)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return ctx->rc;
+ }
+ grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ trie->clear_status_flags();
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_dat_repair(grn_ctx *ctx, grn_dat *dat)
+{
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return ctx->rc;
+ }
+ const grn::dat::Trie * const trie = static_cast<grn::dat::Trie *>(dat->trie);
+ if (!trie) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ char trie_path[PATH_MAX];
+ grn_dat_generate_trie_path(grn_io_path(dat->io), trie_path, dat->header->file_id + 1);
+ try {
+ grn::dat::Trie().repair(*trie, trie_path);
+ } catch (const grn::dat::Exception &ex) {
+ const grn_rc error_code = grn_dat_translate_error_code(ex.code());
+ ERR(error_code, "grn::dat::Trie::create failed");
+ return error_code;
+ }
+ ++dat->header->file_id;
+ if (!grn_dat_open_trie_if_needed(ctx, dat)) {
+ return ctx->rc;
+ }
+ return GRN_SUCCESS;
+}
+
+} // extern "C"
diff --git a/storage/mroonga/vendor/groonga/lib/dat.h b/storage/mroonga/vendor/groonga/lib/dat.h
new file mode 100644
index 00000000000..00c71df4c3b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat.h
@@ -0,0 +1,99 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_DAT_H
+#define GRN_DAT_H
+
+#ifndef GROONGA_IN_H
+# include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#include "db.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _grn_dat {
+ grn_db_obj obj;
+ grn_io *io;
+ struct grn_dat_header *header;
+ uint32_t file_id;
+ grn_encoding encoding;
+ void *trie;
+ void *old_trie;
+ grn_obj *tokenizer;
+ grn_obj *normalizer;
+ grn_critical_section lock;
+};
+
+struct grn_dat_header {
+ uint32_t flags;
+ grn_encoding encoding;
+ grn_id tokenizer;
+ uint32_t file_id;
+ grn_id normalizer;
+ uint32_t reserved[235];
+};
+
+struct _grn_dat_cursor {
+ grn_db_obj obj;
+ grn_dat *dat;
+ void *cursor;
+ const void *key;
+ grn_id curr_rec;
+};
+
+typedef struct _grn_dat_scan_hit grn_dat_scan_hit;
+
+struct _grn_dat_scan_hit {
+ grn_id id;
+ unsigned int offset;
+ unsigned int length;
+};
+
+GRN_API int grn_dat_scan(grn_ctx *ctx, grn_dat *dat, const char *str,
+ unsigned int str_size, grn_dat_scan_hit *scan_hits,
+ unsigned int max_num_scan_hits, const char **str_rest);
+GRN_API grn_id grn_dat_lcp_search(grn_ctx *ctx, grn_dat *dat,
+ const void *key, unsigned int key_size);
+
+GRN_API grn_id grn_dat_curr_id(grn_ctx *ctx, grn_dat *dat);
+
+/*
+ Currently, grn_dat_truncate() is available if the grn_dat object is
+ associated with a file.
+ */
+GRN_API grn_rc grn_dat_truncate(grn_ctx *ctx, grn_dat *dat);
+
+GRN_API const char *_grn_dat_key(grn_ctx *ctx, grn_dat *dat, grn_id id,
+ uint32_t *key_size);
+GRN_API grn_id grn_dat_next(grn_ctx *ctx, grn_dat *dat, grn_id id);
+GRN_API grn_id grn_dat_at(grn_ctx *ctx, grn_dat *dat, grn_id id);
+
+GRN_API grn_rc grn_dat_clear_status_flags(grn_ctx *ctx, grn_dat *dat);
+
+/*
+ Currently, grn_dat_repair() is available if the grn_dat object is associated
+ with a file.
+ */
+GRN_API grn_rc grn_dat_repair(grn_ctx *ctx, grn_dat *dat);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_DAT_H */
diff --git a/storage/mroonga/vendor/groonga/lib/dat/Makefile.am b/storage/mroonga/vendor/groonga/lib/dat/Makefile.am
new file mode 100644
index 00000000000..6bad7730de4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/Makefile.am
@@ -0,0 +1,12 @@
+DEFS += -D_REENTRANT $(GRN_DEFS) -DGRN_DAT_EXPORT
+
+DEFAULT_INCLUDES = \
+ -I$(top_builddir) \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib
+
+noinst_LTLIBRARIES = libgrndat.la
+
+include sources.am
+
+CLEANFILES = *.gcno *.gcda
diff --git a/storage/mroonga/vendor/groonga/lib/dat/array.hpp b/storage/mroonga/vendor/groonga/lib/dat/array.hpp
new file mode 100644
index 00000000000..5536552b36d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/array.hpp
@@ -0,0 +1,100 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_ARRAY_HPP_
+#define GRN_DAT_ARRAY_HPP_
+
+#include "dat.hpp"
+
+namespace grn {
+namespace dat {
+
+// This class is used to detect an out-of-range access in debug mode.
+template <typename T>
+class GRN_DAT_API Array {
+ public:
+ Array() : ptr_(NULL), size_(0) {}
+ Array(void *ptr, UInt32 size) : ptr_(static_cast<T *>(ptr)), size_(size) {
+ GRN_DAT_DEBUG_THROW_IF((ptr == NULL) && (size != 0));
+ }
+ template <UInt32 U>
+ explicit Array(T (&array)[U]) : ptr_(array), size_(U) {}
+ ~Array() {}
+
+ const T &operator[](UInt32 i) const {
+ GRN_DAT_DEBUG_THROW_IF(i >= size_);
+ return ptr_[i];
+ }
+ T &operator[](UInt32 i) {
+ GRN_DAT_DEBUG_THROW_IF(i >= size_);
+ return ptr_[i];
+ }
+
+ const T *begin() const {
+ return ptr();
+ }
+ T *begin() {
+ return ptr();
+ }
+
+ const T *end() const {
+ return ptr() + size();
+ }
+ T *end() {
+ return ptr() + size();
+ }
+
+ void assign(void *ptr, UInt32 size) {
+ GRN_DAT_DEBUG_THROW_IF((ptr == NULL) && (size != 0));
+ ptr_ = static_cast<T *>(ptr);
+ size_ = size;
+ }
+ template <UInt32 U>
+ void assign(T (&array)[U]) {
+ assign(array, U);
+ }
+
+ void swap(Array *rhs) {
+ T * const temp_ptr = ptr_;
+ ptr_ = rhs->ptr_;
+ rhs->ptr_ = temp_ptr;
+
+ const UInt32 temp_size = size_;
+ size_ = rhs->size_;
+ rhs->size_ = temp_size;
+ }
+
+ T *ptr() const {
+ return ptr_;
+ }
+ UInt32 size() const {
+ return size_;
+ }
+
+ private:
+ T *ptr_;
+ UInt32 size_;
+
+ // Disallows copy and assignment.
+ Array(const Array &);
+ Array &operator=(const Array &);
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_ARRAY_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/base.hpp b/storage/mroonga/vendor/groonga/lib/dat/base.hpp
new file mode 100644
index 00000000000..577e69ed62d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/base.hpp
@@ -0,0 +1,69 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_BASE_HPP_
+#define GRN_DAT_BASE_HPP_
+
+#include "dat.hpp"
+
+namespace grn {
+namespace dat {
+
+// The most significant bit represents whether or not the node is a linker.
+// BASE of a linker represents the position of its associated key and BASE of
+// a non-linker represents the offset to its child nodes.
+class GRN_DAT_API Base {
+ public:
+ Base() : value_(0) {}
+
+ bool operator==(const Base &rhs) const {
+ return value_ == rhs.value_;
+ }
+
+ bool is_linker() const {
+ return (value_ & IS_LINKER_FLAG) == IS_LINKER_FLAG;
+ }
+ UInt32 offset() const {
+ GRN_DAT_DEBUG_THROW_IF(is_linker());
+ return value_;
+ }
+ UInt32 key_pos() const {
+ GRN_DAT_DEBUG_THROW_IF(!is_linker());
+ return value_ & ~IS_LINKER_FLAG;
+ }
+
+ void set_offset(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF((x & IS_LINKER_FLAG) != 0);
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_OFFSET);
+ value_ = x;
+ }
+ void set_key_pos(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF((x & IS_LINKER_FLAG) != 0);
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_OFFSET);
+ value_ = IS_LINKER_FLAG | x;
+ }
+
+ private:
+ UInt32 value_;
+
+ static const UInt32 IS_LINKER_FLAG = 0x80000000U;
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_BASE_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/block.hpp b/storage/mroonga/vendor/groonga/lib/dat/block.hpp
new file mode 100644
index 00000000000..4675083ecea
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/block.hpp
@@ -0,0 +1,96 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_BLOCK_HPP_
+#define GRN_DAT_BLOCK_HPP_
+
+#include "dat.hpp"
+
+namespace grn {
+namespace dat {
+
+class GRN_DAT_API Block {
+ public:
+ Block() : next_(0), prev_(0), first_phantom_(0), num_phantoms_(0) {}
+
+ // Blocks in the same level are stored in a doubly-linked list which is
+ // represented by the following next() and prev().
+ UInt32 next() const {
+ return next_ / BLOCK_SIZE;
+ }
+ UInt32 prev() const {
+ return prev_ / BLOCK_SIZE;
+ }
+
+ // A level indicates how easyily find_offset() can find a good offset in that
+ // block. It is easier in lower level blocks.
+ UInt32 level() const {
+ return next_ & BLOCK_MASK;
+ }
+ // A block level rises when find_offset() fails to find a good offset
+ // MAX_FAILURE_COUNT times in that block.
+ UInt32 failure_count() const {
+ return prev_ & BLOCK_MASK;
+ }
+
+ UInt32 first_phantom() const {
+ return first_phantom_;
+ }
+ UInt32 num_phantoms() const {
+ return num_phantoms_;
+ }
+
+ void set_next(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_BLOCK_ID);
+ next_ = (next_ & BLOCK_MASK) | (x * BLOCK_SIZE);
+ }
+ void set_prev(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_BLOCK_ID);
+ prev_ = (prev_ & BLOCK_MASK) | (x * BLOCK_SIZE);
+ }
+
+ void set_level(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_BLOCK_LEVEL);
+ GRN_DAT_DEBUG_THROW_IF(x > BLOCK_MASK);
+ next_ = (next_ & ~BLOCK_MASK) | x;
+ }
+ void set_failure_count(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_FAILURE_COUNT);
+ GRN_DAT_DEBUG_THROW_IF(x > BLOCK_MASK);
+ prev_ = (prev_ & ~BLOCK_MASK) | x;
+ }
+
+ void set_first_phantom(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x >= BLOCK_SIZE);
+ first_phantom_ = (UInt16)x;
+ }
+ void set_num_phantoms(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > BLOCK_SIZE);
+ num_phantoms_ = (UInt16)x;
+ }
+
+ private:
+ UInt32 next_;
+ UInt32 prev_;
+ UInt16 first_phantom_;
+ UInt16 num_phantoms_;
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_BLOCK_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/check.hpp b/storage/mroonga/vendor/groonga/lib/dat/check.hpp
new file mode 100644
index 00000000000..f7e57874ca2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/check.hpp
@@ -0,0 +1,151 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_CHECK_HPP_
+#define GRN_DAT_CHECK_HPP_
+
+#include "dat.hpp"
+
+namespace grn {
+namespace dat {
+
+class GRN_DAT_API Check {
+ public:
+ Check() : value_(0) {}
+
+ bool operator==(const Check &rhs) const {
+ return value_ == rhs.value_;
+ }
+
+ // The most significant bit represents whether or not the node ID is used as
+ // an offset. Note that the MSB is independent of the other bits.
+ bool is_offset() const {
+ return (value_ & IS_OFFSET_FLAG) == IS_OFFSET_FLAG;
+ }
+
+ UInt32 except_is_offset() const {
+ GRN_DAT_DEBUG_THROW_IF(is_phantom());
+ return value_ & ~IS_OFFSET_FLAG;
+ }
+
+ // A phantom node is a node that has never been used, and such a node is also
+ // called an empty element. Phantom nodes form a doubly linked list in each
+ // block, and the linked list is represented by next() and prev().
+ bool is_phantom() const {
+ return (value_ & IS_PHANTOM_FLAG) == IS_PHANTOM_FLAG;
+ }
+
+ UInt32 next() const {
+ GRN_DAT_DEBUG_THROW_IF(!is_phantom());
+ return (value_ >> NEXT_SHIFT) & BLOCK_MASK;
+ }
+ UInt32 prev() const {
+ GRN_DAT_DEBUG_THROW_IF(!is_phantom());
+ return (value_ >> PREV_SHIFT) & BLOCK_MASK;
+ }
+
+ // A label is attached to each non-phantom node. A label is represented by
+ // a byte except for a terminal label '\256'. Note that a phantom node always
+ // returns an invalid label with its phantom bit flag so as to reject invalid
+ // transitions.
+ UInt32 label() const {
+ return value_ & (IS_PHANTOM_FLAG | LABEL_MASK);
+ }
+
+ // A non-phantom node has the labels of the first child and the next sibling.
+ // Note that INVALID_LABEL is stored if the node has no child nodes or has
+ // no more siblings.
+ UInt32 child() const {
+ return (value_ >> CHILD_SHIFT) & LABEL_MASK;
+ }
+ UInt32 sibling() const {
+ return (value_ >> SIBLING_SHIFT) & LABEL_MASK;
+ }
+
+ void set_is_offset(bool x) {
+ if (x) {
+ GRN_DAT_DEBUG_THROW_IF(is_offset());
+ value_ |= IS_OFFSET_FLAG;
+ } else {
+ GRN_DAT_DEBUG_THROW_IF(!is_offset());
+ value_ &= ~IS_OFFSET_FLAG;
+ }
+ }
+
+ void set_except_is_offset(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(is_phantom());
+ GRN_DAT_DEBUG_THROW_IF((x & IS_OFFSET_FLAG) == IS_OFFSET_FLAG);
+ value_ = (value_ & IS_OFFSET_FLAG) | x;
+ }
+
+ // To reject a transition to an incomplete node, set_is_phantom() invalidates
+ // its label and links when it becomes non-phantom.
+ void set_is_phantom(bool x) {
+ if (x) {
+ GRN_DAT_DEBUG_THROW_IF(is_phantom());
+ value_ |= IS_PHANTOM_FLAG;
+ } else {
+ GRN_DAT_DEBUG_THROW_IF(!is_phantom());
+ value_ = (value_ & IS_OFFSET_FLAG) | (INVALID_LABEL << CHILD_SHIFT) |
+ (INVALID_LABEL << SIBLING_SHIFT) | INVALID_LABEL;
+ }
+ }
+
+ void set_next(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(!is_phantom());
+ GRN_DAT_DEBUG_THROW_IF(x > BLOCK_MASK);
+ value_ = (value_ & ~(BLOCK_MASK << NEXT_SHIFT)) | (x << NEXT_SHIFT);
+ }
+ void set_prev(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(!is_phantom());
+ GRN_DAT_DEBUG_THROW_IF(x > BLOCK_MASK);
+ value_ = (value_ & ~(BLOCK_MASK << PREV_SHIFT)) | (x << PREV_SHIFT);
+ }
+
+ void set_label(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(is_phantom());
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_LABEL);
+ value_ = (value_ & ~LABEL_MASK) | x;
+ }
+
+ void set_child(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(is_phantom());
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_LABEL);
+ value_ = (value_ & ~(LABEL_MASK << CHILD_SHIFT)) | (x << CHILD_SHIFT);
+ }
+ void set_sibling(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(is_phantom());
+ GRN_DAT_DEBUG_THROW_IF(label() > MAX_LABEL);
+ GRN_DAT_DEBUG_THROW_IF((sibling() != INVALID_LABEL) && (x == INVALID_LABEL));
+ value_ = (value_ & ~(LABEL_MASK << SIBLING_SHIFT)) | (x << SIBLING_SHIFT);
+ }
+
+ private:
+ UInt32 value_;
+
+ static const UInt32 IS_OFFSET_FLAG = 1U << 31;
+ static const UInt32 IS_PHANTOM_FLAG = 1U << 30;
+ static const UInt32 NEXT_SHIFT = 9;
+ static const UInt32 PREV_SHIFT = 18;
+ static const UInt32 CHILD_SHIFT = 9;
+ static const UInt32 SIBLING_SHIFT = 18;
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_CHECK_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/cursor-factory.cpp b/storage/mroonga/vendor/groonga/lib/dat/cursor-factory.cpp
new file mode 100644
index 00000000000..6dab51a2978
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/cursor-factory.cpp
@@ -0,0 +1,94 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "cursor-factory.hpp"
+#include "id-cursor.hpp"
+#include "key-cursor.hpp"
+#include "prefix-cursor.hpp"
+#include "predictive-cursor.hpp"
+
+#include <new>
+
+namespace grn {
+namespace dat {
+
+Cursor *CursorFactory::open(const Trie &trie,
+ const void *min_ptr, UInt32 min_length,
+ const void *max_ptr, UInt32 max_length,
+ UInt32 offset,
+ UInt32 limit,
+ UInt32 flags) {
+ GRN_DAT_THROW_IF(PARAM_ERROR, &trie == NULL);
+
+ const UInt32 cursor_type = flags & CURSOR_TYPE_MASK;
+ switch (cursor_type) {
+ case ID_RANGE_CURSOR: {
+ IdCursor *cursor = new (std::nothrow) IdCursor;
+ GRN_DAT_THROW_IF(MEMORY_ERROR, cursor == NULL);
+ try {
+ cursor->open(trie, String(min_ptr, min_length),
+ String(max_ptr, max_length), offset, limit, flags);
+ } catch (...) {
+ delete cursor;
+ throw;
+ }
+ return cursor;
+ }
+ case KEY_RANGE_CURSOR: {
+ KeyCursor *cursor = new (std::nothrow) KeyCursor;
+ GRN_DAT_THROW_IF(MEMORY_ERROR, cursor == NULL);
+ try {
+ cursor->open(trie, String(min_ptr, min_length),
+ String(max_ptr, max_length), offset, limit, flags);
+ } catch (...) {
+ delete cursor;
+ throw;
+ }
+ return cursor;
+ }
+ case PREFIX_CURSOR: {
+ PrefixCursor *cursor = new (std::nothrow) PrefixCursor;
+ GRN_DAT_THROW_IF(MEMORY_ERROR, cursor == NULL);
+ try {
+ cursor->open(trie, String(max_ptr, max_length), min_length,
+ offset, limit, flags);
+ } catch (...) {
+ delete cursor;
+ throw;
+ }
+ return cursor;
+ }
+ case PREDICTIVE_CURSOR: {
+ PredictiveCursor *cursor = new (std::nothrow) PredictiveCursor;
+ GRN_DAT_THROW_IF(MEMORY_ERROR, cursor == NULL);
+ try {
+ cursor->open(trie, String(min_ptr, min_length),
+ offset, limit, flags);
+ } catch (...) {
+ delete cursor;
+ throw;
+ }
+ return cursor;
+ }
+ default: {
+ GRN_DAT_THROW(PARAM_ERROR, "unknown cursor type");
+ }
+ }
+}
+
+} // namespace dat
+} // namespace grn
diff --git a/storage/mroonga/vendor/groonga/lib/dat/cursor-factory.hpp b/storage/mroonga/vendor/groonga/lib/dat/cursor-factory.hpp
new file mode 100644
index 00000000000..c79ac4e89c7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/cursor-factory.hpp
@@ -0,0 +1,46 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_CURSOR_FACTORY_HPP_
+#define GRN_DAT_CURSOR_FACTORY_HPP_
+
+#include "cursor.hpp"
+
+namespace grn {
+namespace dat {
+
+class Trie;
+
+class GRN_DAT_API CursorFactory {
+ public:
+ static Cursor *open(const Trie &trie,
+ const void *min_ptr, UInt32 min_length,
+ const void *max_ptr, UInt32 max_length,
+ UInt32 offset = 0,
+ UInt32 limit = MAX_UINT32,
+ UInt32 flags = 0);
+
+ private:
+ // Disallows copy and assignment.
+ CursorFactory(const CursorFactory &);
+ CursorFactory &operator=(const CursorFactory &);
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_CURSOR_FACTORY_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/cursor.hpp b/storage/mroonga/vendor/groonga/lib/dat/cursor.hpp
new file mode 100644
index 00000000000..0a4887e1a74
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/cursor.hpp
@@ -0,0 +1,48 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_CURSOR_HPP_
+#define GRN_DAT_CURSOR_HPP_
+
+#include "key.hpp"
+
+namespace grn {
+namespace dat {
+
+class GRN_DAT_API Cursor {
+ public:
+ Cursor() {}
+ virtual ~Cursor() {}
+
+ virtual void close() = 0;
+
+ virtual const Key &next() = 0;
+
+ virtual UInt32 offset() const = 0;
+ virtual UInt32 limit() const = 0;
+ virtual UInt32 flags() const = 0;
+
+ private:
+ // Disallows copy and assignment.
+ Cursor(const Cursor &);
+ Cursor &operator=(const Cursor &);
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_CURSOR_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/dat.hpp b/storage/mroonga/vendor/groonga/lib/dat/dat.hpp
new file mode 100644
index 00000000000..a2b225a9cf4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/dat.hpp
@@ -0,0 +1,255 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_COMMON_HPP_
+#define GRN_DAT_COMMON_HPP_
+
+#ifndef _MSC_VER
+# include <stddef.h>
+# include <stdint.h>
+#endif // _MSC_VER
+
+#include <cstddef>
+#include <exception>
+
+#ifdef _DEBUG
+# include <iostream>
+#endif // _DEBUG
+
+#ifndef GRN_DAT_API
+# ifdef WIN32
+# ifdef GRN_DAT_EXPORT
+# define GRN_DAT_API __declspec(dllexport)
+# else // GRN_DAT_EXPORT
+# define GRN_DAT_API __declspec(dllimport)
+# endif // GRN_DAT_EXPORT
+# else // WIN32
+# define GRN_DAT_API
+# endif // WIN32
+#endif // GRN_DAT_API
+
+namespace grn {
+namespace dat {
+
+#ifdef _MSC_VER
+typedef unsigned __int8 UInt8;
+typedef unsigned __int16 UInt16;
+typedef unsigned __int32 UInt32;
+typedef unsigned __int64 UInt64;
+#else // _MSC_VER
+typedef ::uint8_t UInt8;
+typedef ::uint16_t UInt16;
+typedef ::uint32_t UInt32;
+typedef ::uint64_t UInt64;
+#endif // _MSC_VER
+
+const UInt8 MAX_UINT8 = static_cast<UInt8>(0xFFU);
+const UInt16 MAX_UINT16 = static_cast<UInt16>(0xFFFFU);
+const UInt32 MAX_UINT32 = static_cast<UInt32>(0xFFFFFFFFU);
+const UInt64 MAX_UINT64 = static_cast<UInt64>(0xFFFFFFFFFFFFFFFFULL);
+
+// If a key is a prefix of another key, such a key is associated with a special
+// terminal node which has TERMINAL_LABEL.
+const UInt16 TERMINAL_LABEL = 0x100;
+const UInt16 MIN_LABEL = '\0';
+const UInt16 MAX_LABEL = TERMINAL_LABEL;
+const UInt32 INVALID_LABEL = 0x1FF;
+const UInt32 LABEL_MASK = 0x1FF;
+
+// The MSB of BASE is used to represent whether the node is a linker node or
+// not and the other 31 bits represent the offset to its child nodes. So, the
+// number of nodes is limited to 2^31.
+const UInt32 ROOT_NODE_ID = 0;
+const UInt32 MAX_NODE_ID = 0x7FFFFFFF;
+const UInt32 MAX_NUM_NODES = MAX_NODE_ID + 1;
+const UInt32 INVALID_NODE_ID = MAX_NODE_ID + 1;
+
+// 0 is reserved for non-linker leaf nodes. For example, the root node of an
+// initial double-array is a non-linker leaf node.
+const UInt32 MAX_OFFSET = MAX_NODE_ID;
+const UInt32 INVALID_OFFSET = 0;
+
+// Phantom nodes are managed in each block because siblings are always put in
+// the same block.
+const UInt32 BLOCK_SIZE = 0x200;
+const UInt32 BLOCK_MASK = 0x1FF;
+const UInt32 MAX_BLOCK_ID = MAX_NODE_ID / BLOCK_SIZE;
+const UInt32 MAX_NUM_BLOCKS = MAX_BLOCK_ID + 1;
+
+// Blocks are divided by their levels, which indicate how easily update
+// operations can find a good offset in them. The level of a block rises when
+// find_offset() fails in that block many times. MAX_FAILURE_COUNT is the
+// threshold. Also, in order to limit the time cost, find_offset() scans at
+// most MAX_BLOCK_COUNT blocks.
+// Larger parameters bring more chances of finding good offsets but it leads to
+// more node renumberings, which are costly operations, and thus results in
+// a degradation of space/time efficiencies.
+const UInt32 MAX_FAILURE_COUNT = 4;
+const UInt32 MAX_BLOCK_COUNT = 16;
+const UInt32 MAX_BLOCK_LEVEL = 5;
+
+// Blocks in the same level compose a doubly linked list. The entry block of
+// a linked list is called a leader. INVALID_LEADER means that a linked list is
+// empty and there exists no leader.
+const UInt32 INVALID_LEADER = 0x7FFFFFFF;
+
+const UInt32 MIN_KEY_ID = 1;
+const UInt32 MAX_KEY_ID = MAX_NODE_ID;
+const UInt32 INVALID_KEY_ID = 0;
+
+// A key length is represented as a 12-bit unsigned integer in Key.
+// A key ID is represented as a 28-bit unsigned integer in Key.
+const UInt32 MAX_KEY_LENGTH = (1U << 12) - 1;
+const UInt32 MAX_NUM_KEYS = (1U << 28) - 1;
+
+const UInt64 MIN_FILE_SIZE = 1 << 16;
+const UInt64 DEFAULT_FILE_SIZE = 1 << 20;
+const UInt64 MAX_FILE_SIZE = (UInt64)1 << 40;
+const double DEFAULT_NUM_NODES_PER_KEY = 4.0;
+const double DEFAULT_AVERAGE_KEY_LENGTH = 16.0;
+const UInt32 MAX_KEY_BUF_SIZE = 0x80000000U;
+const UInt32 MAX_TOTAL_KEY_LENGTH = 0xFFFFFFFFU;
+
+const UInt32 ID_RANGE_CURSOR = 0x00001;
+const UInt32 KEY_RANGE_CURSOR = 0x00002;
+const UInt32 PREFIX_CURSOR = 0x00004;
+const UInt32 PREDICTIVE_CURSOR = 0x00008;
+const UInt32 CURSOR_TYPE_MASK = 0x000FF;
+
+const UInt32 ASCENDING_CURSOR = 0x00100;
+const UInt32 DESCENDING_CURSOR = 0x00200;
+const UInt32 CURSOR_ORDER_MASK = 0x00F00;
+
+const UInt32 EXCEPT_LOWER_BOUND = 0x01000;
+const UInt32 EXCEPT_UPPER_BOUND = 0x02000;
+const UInt32 EXCEPT_EXACT_MATCH = 0x04000;
+const UInt32 CURSOR_OPTIONS_MASK = 0xFF000;
+
+const UInt32 REMOVING_FLAG = 1U << 0;
+const UInt32 INSERTING_FLAG = 1U << 1;
+const UInt32 UPDATING_FLAG = 1U << 2;
+const UInt32 CHANGING_MASK = REMOVING_FLAG | INSERTING_FLAG | UPDATING_FLAG;
+
+const UInt32 MKQ_SORT_THRESHOLD = 10;
+
+enum ErrorCode {
+ PARAM_ERROR = -1,
+ IO_ERROR = -2,
+ FORMAT_ERROR = -3,
+ MEMORY_ERROR = -4,
+ SIZE_ERROR = -5,
+ UNEXPECTED_ERROR = -6,
+ STATUS_ERROR = -7
+};
+
+class Exception : public std::exception {
+ public:
+ Exception() throw()
+ : std::exception(),
+ file_(""),
+ line_(-1),
+ what_("") {}
+ Exception(const char *file, int line, const char *what) throw()
+ : std::exception(),
+ file_(file),
+ line_(line),
+ what_((what != NULL) ? what : "") {}
+ Exception(const Exception &ex) throw()
+ : std::exception(ex),
+ file_(ex.file_),
+ line_(ex.line_),
+ what_(ex.what_) {}
+ virtual ~Exception() throw() {}
+
+ virtual Exception &operator=(const Exception &ex) throw() {
+ file_ = ex.file_;
+ line_ = ex.line_;
+ what_ = ex.what_;
+ return *this;
+ }
+
+ virtual ErrorCode code() const throw() = 0;
+ virtual const char *file() const throw() {
+ return file_;
+ }
+ virtual int line() const throw() {
+ return line_;
+ }
+ virtual const char *what() const throw() {
+ return what_;
+ }
+
+ private:
+ const char *file_;
+ int line_;
+ const char *what_;
+};
+
+template <ErrorCode T>
+class Error : public Exception {
+ public:
+ Error() throw()
+ : Exception() {}
+ Error(const char *file, int line, const char *what) throw()
+ : Exception(file, line, what) {}
+ Error(const Error &ex) throw()
+ : Exception(ex) {}
+ virtual ~Error() throw() {}
+
+ virtual Error &operator=(const Error &ex) throw() {
+ *static_cast<Exception *>(this) = ex;
+ return *this;
+ }
+
+ virtual ErrorCode code() const throw() {
+ return T;
+ }
+};
+
+typedef Error<PARAM_ERROR> ParamError;
+typedef Error<IO_ERROR> IOError;
+typedef Error<FORMAT_ERROR> FormatError;
+typedef Error<MEMORY_ERROR> MemoryError;
+typedef Error<SIZE_ERROR> SizeError;
+typedef Error<UNEXPECTED_ERROR> UnexpectedError;
+typedef Error<STATUS_ERROR> StatusError;
+
+#define GRN_DAT_INT_TO_STR(value) #value
+#define GRN_DAT_LINE_TO_STR(line) GRN_DAT_INT_TO_STR(line)
+#define GRN_DAT_LINE_STR GRN_DAT_LINE_TO_STR(__LINE__)
+
+#define GRN_DAT_THROW(code, msg)\
+ (throw grn::dat::Error<code>(__FILE__, __LINE__,\
+ __FILE__ ":" GRN_DAT_LINE_STR ": " #code ": " msg))
+#define GRN_DAT_THROW_IF(code, cond)\
+ (void)((!(cond)) || (GRN_DAT_THROW(code, #cond), 0))
+
+#ifdef _DEBUG
+ #define GRN_DAT_DEBUG_THROW_IF(cond)\
+ GRN_DAT_THROW_IF(grn::dat::UNEXPECTED_ERROR, cond)
+ #define GRN_DAT_DEBUG_LOG(var)\
+ (std::clog << __FILE__ ":" GRN_DAT_LINE_STR ": " #var ": "\
+ << (var) << std::endl)
+#else // _DEBUG
+ #define GRN_DAT_DEBUG_THROW_IF(cond)
+ #define GRN_DAT_DEBUG_LOG(var)
+#endif // _DEBUG
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_COMMON_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/entry.hpp b/storage/mroonga/vendor/groonga/lib/dat/entry.hpp
new file mode 100644
index 00000000000..0c0b3ad41ad
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/entry.hpp
@@ -0,0 +1,61 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_ENTRY_HPP_
+#define GRN_DAT_ENTRY_HPP_
+
+#include "dat.hpp"
+
+namespace grn {
+namespace dat {
+
+// The most significant bit represents whether or not the entry is valid.
+// A valid entry stores the position of its associated key and an invalid entry
+// stores the index of the next invalid entry.
+class GRN_DAT_API Entry {
+ public:
+ Entry() : value_(0) {}
+
+ bool is_valid() const {
+ return (value_ & IS_VALID_FLAG) == IS_VALID_FLAG;
+ }
+ UInt32 key_pos() const {
+ GRN_DAT_DEBUG_THROW_IF(!is_valid());
+ return value_ & ~IS_VALID_FLAG;
+ }
+ UInt32 next() const {
+ GRN_DAT_DEBUG_THROW_IF(is_valid());
+ return value_;
+ }
+
+ void set_key_pos(UInt32 x) {
+ value_ = IS_VALID_FLAG | x;
+ }
+ void set_next(UInt32 x) {
+ value_ = x;
+ }
+
+ private:
+ UInt32 value_;
+
+ static const UInt32 IS_VALID_FLAG = 0x80000000U;
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_ENTRY_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/file-impl.cpp b/storage/mroonga/vendor/groonga/lib/dat/file-impl.cpp
new file mode 100644
index 00000000000..6382ae965a9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/file-impl.cpp
@@ -0,0 +1,244 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "file-impl.hpp"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef WIN32
+# ifdef min
+# undef min
+# endif // min
+# ifdef max
+# undef max
+# endif // max
+#else // WIN32
+# include <fcntl.h>
+# include <sys/mman.h>
+# include <unistd.h>
+#endif // WIN32
+
+#include <algorithm>
+#include <limits>
+
+namespace grn {
+namespace dat {
+
+#ifdef WIN32
+
+FileImpl::FileImpl()
+ : ptr_(NULL),
+ size_(0),
+ file_(INVALID_HANDLE_VALUE),
+ map_(INVALID_HANDLE_VALUE),
+ addr_(NULL) {}
+
+FileImpl::~FileImpl() {
+ if (addr_ != NULL) {
+ ::UnmapViewOfFile(addr_);
+ }
+
+ if (map_ != INVALID_HANDLE_VALUE) {
+ ::CloseHandle(map_);
+ }
+
+ if (file_ != INVALID_HANDLE_VALUE) {
+ ::CloseHandle(file_);
+ }
+}
+
+#else // WIN32
+
+FileImpl::FileImpl()
+ : ptr_(NULL),
+ size_(0),
+ fd_(-1),
+ addr_(MAP_FAILED),
+ length_(0) {}
+
+FileImpl::~FileImpl() {
+ if (addr_ != MAP_FAILED) {
+ ::munmap(addr_, length_);
+ }
+
+ if (fd_ != -1) {
+ ::close(fd_);
+ }
+}
+
+#endif // WIN32
+
+void FileImpl::create(const char *path, UInt64 size) {
+ GRN_DAT_THROW_IF(PARAM_ERROR, size == 0);
+ GRN_DAT_THROW_IF(PARAM_ERROR,
+ size > static_cast<UInt64>(std::numeric_limits< ::size_t>::max()));
+
+ FileImpl new_impl;
+ new_impl.create_(path, size);
+ new_impl.swap(this);
+}
+
+void FileImpl::open(const char *path) {
+ GRN_DAT_THROW_IF(PARAM_ERROR, path == NULL);
+ GRN_DAT_THROW_IF(PARAM_ERROR, path[0] == '\0');
+
+ FileImpl new_impl;
+ new_impl.open_(path);
+ new_impl.swap(this);
+}
+
+void FileImpl::close() {
+ FileImpl new_impl;
+ new_impl.swap(this);
+}
+
+#ifdef WIN32
+
+void FileImpl::swap(FileImpl *rhs) {
+ std::swap(ptr_, rhs->ptr_);
+ std::swap(size_, rhs->size_);
+ std::swap(file_, rhs->file_);
+ std::swap(map_, rhs->map_);
+ std::swap(addr_, rhs->addr_);
+}
+
+void FileImpl::create_(const char *path, UInt64 size) {
+ if ((path != NULL) && (path[0] != '\0')) {
+ file_ = ::CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ GRN_DAT_THROW_IF(IO_ERROR, file_ == INVALID_HANDLE_VALUE);
+
+ const LONG size_low = static_cast<LONG>(size & 0xFFFFFFFFU);
+ LONG size_high = static_cast<LONG>(size >> 32);
+ const DWORD file_pos = ::SetFilePointer(file_, size_low, &size_high,
+ FILE_BEGIN);
+ GRN_DAT_THROW_IF(IO_ERROR, (file_pos == INVALID_SET_FILE_POINTER) &&
+ (::GetLastError() != 0));
+ GRN_DAT_THROW_IF(IO_ERROR, ::SetEndOfFile(file_) == 0);
+
+ map_ = ::CreateFileMapping(file_, NULL, PAGE_READWRITE, 0, 0, NULL);
+ GRN_DAT_THROW_IF(IO_ERROR, map_ == INVALID_HANDLE_VALUE);
+ } else {
+ const DWORD size_low = static_cast<DWORD>(size & 0xFFFFFFFFU);
+ const DWORD size_high = static_cast<DWORD>(size >> 32);
+
+ map_ = ::CreateFileMapping(file_, NULL, PAGE_READWRITE,
+ size_high, size_low, NULL);
+ GRN_DAT_THROW_IF(IO_ERROR, map_ == INVALID_HANDLE_VALUE);
+ }
+
+ addr_ = ::MapViewOfFile(map_, FILE_MAP_WRITE, 0, 0, 0);
+ GRN_DAT_THROW_IF(IO_ERROR, addr_ == NULL);
+
+ ptr_ = addr_;
+ size_ = static_cast< ::size_t>(size);
+}
+
+void FileImpl::open_(const char *path) {
+#ifdef _MSC_VER
+ struct __stat64 st;
+ GRN_DAT_THROW_IF(IO_ERROR, ::_stat64(path, &st) == -1);
+#else // _MSC_VER
+ struct _stat st;
+ GRN_DAT_THROW_IF(IO_ERROR, ::_stat(path, &st) == -1);
+#endif // _MSC_VER
+ GRN_DAT_THROW_IF(IO_ERROR, st.st_size == 0);
+ GRN_DAT_THROW_IF(IO_ERROR,
+ static_cast<UInt64>(st.st_size) > std::numeric_limits< ::size_t>::max());
+
+ file_ = ::CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ GRN_DAT_THROW_IF(IO_ERROR, file_ == NULL);
+
+ map_ = ::CreateFileMapping(file_, NULL, PAGE_READWRITE, 0, 0, NULL);
+ GRN_DAT_THROW_IF(IO_ERROR, map_ == NULL);
+
+ addr_ = ::MapViewOfFile(map_, FILE_MAP_WRITE, 0, 0, 0);
+ GRN_DAT_THROW_IF(IO_ERROR, addr_ == NULL);
+
+ ptr_ = addr_;
+ size_ = static_cast< ::size_t>(st.st_size);
+}
+
+#else // WIN32
+
+void FileImpl::swap(FileImpl *rhs) {
+ std::swap(ptr_, rhs->ptr_);
+ std::swap(size_, rhs->size_);
+ std::swap(fd_, rhs->fd_);
+ std::swap(addr_, rhs->addr_);
+ std::swap(length_, rhs->length_);
+}
+
+void FileImpl::create_(const char *path, UInt64 size) {
+ GRN_DAT_THROW_IF(PARAM_ERROR,
+ size > static_cast<UInt64>(std::numeric_limits< ::off_t>::max()));
+
+ if ((path != NULL) && (path[0] != '\0')) {
+ fd_ = ::open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
+ GRN_DAT_THROW_IF(IO_ERROR, fd_ == -1);
+
+ const ::off_t file_size = static_cast< ::off_t>(size);
+ GRN_DAT_THROW_IF(IO_ERROR, ::ftruncate(fd_, file_size) == -1);
+ }
+
+#ifdef MAP_ANONYMOUS
+ const int flags = (fd_ == -1) ? (MAP_PRIVATE | MAP_ANONYMOUS) : MAP_SHARED;
+#else // MAP_ANONYMOUS
+ const int flags = (fd_ == -1) ? (MAP_PRIVATE | MAP_ANON) : MAP_SHARED;
+#endif // MAP_ANONYMOUS
+
+ length_ = static_cast< ::size_t>(size);
+#ifdef USE_MAP_HUGETLB
+ addr_ = ::mmap(NULL, length_, PROT_READ | PROT_WRITE,
+ flags | MAP_HUGETLB, fd_, 0);
+#endif // USE_MAP_HUGETLB
+ if (addr_ == MAP_FAILED) {
+ addr_ = ::mmap(NULL, length_, PROT_READ | PROT_WRITE, flags, fd_, 0);
+ GRN_DAT_THROW_IF(IO_ERROR, addr_ == MAP_FAILED);
+ }
+
+ ptr_ = addr_;
+ size_ = length_;
+}
+
+void FileImpl::open_(const char *path) {
+ struct stat st;
+ GRN_DAT_THROW_IF(IO_ERROR, ::stat(path, &st) == -1);
+ GRN_DAT_THROW_IF(IO_ERROR, (st.st_mode & S_IFMT) != S_IFREG);
+ GRN_DAT_THROW_IF(IO_ERROR, st.st_size == 0);
+ GRN_DAT_THROW_IF(IO_ERROR,
+ static_cast<UInt64>(st.st_size) > std::numeric_limits< ::size_t>::max());
+
+ fd_ = ::open(path, O_RDWR);
+ GRN_DAT_THROW_IF(IO_ERROR, fd_ == -1);
+
+ length_ = static_cast<std::size_t>(st.st_size);
+ addr_ = ::mmap(NULL, length_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0);
+ GRN_DAT_THROW_IF(IO_ERROR, addr_ == MAP_FAILED);
+
+ ptr_ = addr_;
+ size_ = length_;
+}
+
+#endif // WIN32
+
+} // namespace dat
+} // namespace grn
diff --git a/storage/mroonga/vendor/groonga/lib/dat/file-impl.hpp b/storage/mroonga/vendor/groonga/lib/dat/file-impl.hpp
new file mode 100644
index 00000000000..f4e0543635a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/file-impl.hpp
@@ -0,0 +1,73 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_FILE_IMPL_HPP_
+#define GRN_DAT_FILE_IMPL_HPP_
+
+#ifdef WIN32
+# include <windows.h>
+#endif // WIN32
+
+#include "dat.hpp"
+
+namespace grn {
+namespace dat {
+
+class FileImpl {
+ public:
+ FileImpl();
+ ~FileImpl();
+
+ void create(const char *path, UInt64 size);
+ void open(const char *path);
+ void close();
+
+ void *ptr() const {
+ return ptr_;
+ }
+ UInt64 size() const {
+ return size_;
+ }
+
+ void swap(FileImpl *rhs);
+
+ private:
+ void *ptr_;
+ UInt64 size_;
+
+#ifdef WIN32
+ HANDLE file_;
+ HANDLE map_;
+ LPVOID addr_;
+#else // WIN32
+ int fd_;
+ void *addr_;
+ ::size_t length_;
+#endif // WIN32
+
+ void create_(const char *path, UInt64 size);
+ void open_(const char *path);
+
+ // Disallows copy and assignment.
+ FileImpl(const FileImpl &);
+ FileImpl &operator=(const FileImpl &);
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_FILE_IMPL_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/file.cpp b/storage/mroonga/vendor/groonga/lib/dat/file.cpp
new file mode 100644
index 00000000000..57bfcb9ece6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/file.cpp
@@ -0,0 +1,67 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "file.hpp"
+#include "file-impl.hpp"
+
+#include <new>
+
+namespace grn {
+namespace dat {
+
+File::File() : impl_(NULL) {}
+
+File::~File() {
+ delete impl_;
+}
+
+void File::create(const char *path, UInt64 size) {
+ File new_file;
+ new_file.impl_ = new (std::nothrow) FileImpl;
+ GRN_DAT_THROW_IF(MEMORY_ERROR, new_file.impl_ == NULL);
+ new_file.impl_->create(path, size);
+ new_file.swap(this);
+}
+
+void File::open(const char *path) {
+ File new_file;
+ new_file.impl_ = new (std::nothrow) FileImpl;
+ GRN_DAT_THROW_IF(MEMORY_ERROR, new_file.impl_ == NULL);
+ new_file.impl_->open(path);
+ new_file.swap(this);
+}
+
+void File::close() {
+ File().swap(this);
+}
+
+void *File::ptr() const {
+ return (impl_ != NULL) ? impl_->ptr() : NULL;
+}
+
+UInt64 File::size() const {
+ return (impl_ != NULL) ? impl_->size() : 0;
+}
+
+void File::swap(File *rhs) {
+ FileImpl * const temp = impl_;
+ impl_ = rhs->impl_;
+ rhs->impl_ = temp;
+}
+
+} // namespace dat
+} // namespace grn
diff --git a/storage/mroonga/vendor/groonga/lib/dat/file.hpp b/storage/mroonga/vendor/groonga/lib/dat/file.hpp
new file mode 100644
index 00000000000..c2be8d86da3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/file.hpp
@@ -0,0 +1,60 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_FILE_HPP_
+#define GRN_DAT_FILE_HPP_
+
+#include "dat.hpp"
+
+namespace grn {
+namespace dat {
+
+// This implementation class hides environment dependent codes required for
+// memory-mapped I/O.
+class FileImpl;
+
+class GRN_DAT_API File {
+ public:
+ File();
+ ~File();
+
+ // This function creates a file and maps the entire file to a certain range
+ // of the address space. Note that a file is truncated if exists.
+ void create(const char *path, UInt64 size);
+
+ // This function opens a file and maps the entire file to a certain range of
+ // the address space.
+ void open(const char *path);
+ void close();
+
+ void *ptr() const;
+ UInt64 size() const;
+
+ void swap(File *rhs);
+
+ private:
+ FileImpl *impl_;
+
+ // Disallows copy and assignment.
+ File(const File &);
+ File &operator=(const File &);
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_FILE_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/header.hpp b/storage/mroonga/vendor/groonga/lib/dat/header.hpp
new file mode 100644
index 00000000000..4f383ac8bf9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/header.hpp
@@ -0,0 +1,181 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_HPP_HEADER_HPP_
+#define GRN_DAT_HPP_HEADER_HPP_
+
+#include "dat.hpp"
+
+namespace grn {
+namespace dat {
+
+class GRN_DAT_API Header {
+ public:
+ Header()
+ : file_size_(0),
+ total_key_length_(0),
+ next_key_id_(grn::dat::MIN_KEY_ID),
+ max_key_id_(0),
+ num_keys_(0),
+ max_num_keys_(0),
+ num_phantoms_(0),
+ num_zombies_(0),
+ num_blocks_(0),
+ max_num_blocks_(0),
+ next_key_pos_(0),
+ key_buf_size_(0),
+ status_flags_(0) {
+ for (UInt32 i = 0; i <= MAX_BLOCK_LEVEL; ++i) {
+ leaders_[i] = INVALID_LEADER;
+ }
+ for (UInt32 i = 0; i < (sizeof(reserved_) / sizeof(*reserved_)); ++i) {
+ reserved_[i] = 0;
+ }
+ }
+
+ UInt64 file_size() const {
+ return file_size_;
+ }
+ UInt32 total_key_length() const {
+ return total_key_length_;
+ }
+ UInt32 min_key_id() const {
+ return MIN_KEY_ID;
+ }
+ UInt32 next_key_id() const {
+ return next_key_id_;
+ }
+ UInt32 max_key_id() const {
+ return max_key_id_;
+ }
+ UInt32 num_keys() const {
+ return num_keys_;
+ }
+ UInt32 max_num_keys() const {
+ return max_num_keys_;
+ }
+ UInt32 num_nodes() const {
+ return num_blocks() * BLOCK_SIZE;
+ }
+ UInt32 num_phantoms() const {
+ return num_phantoms_;
+ }
+ UInt32 num_zombies() const {
+ return num_zombies_;
+ }
+ UInt32 max_num_nodes() const {
+ return max_num_blocks() * BLOCK_SIZE;
+ }
+ UInt32 num_blocks() const {
+ return num_blocks_;
+ }
+ UInt32 max_num_blocks() const {
+ return max_num_blocks_;
+ }
+ UInt32 next_key_pos() const {
+ return next_key_pos_;
+ }
+ UInt32 key_buf_size() const {
+ return key_buf_size_;
+ }
+ UInt32 status_flags() const {
+ return status_flags_;
+ }
+ UInt32 ith_leader(UInt32 i) const {
+ GRN_DAT_DEBUG_THROW_IF(i > MAX_BLOCK_LEVEL);
+ return leaders_[i];
+ }
+
+ void set_file_size(UInt64 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_FILE_SIZE);
+ file_size_ = x;
+ }
+ void set_total_key_length(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_TOTAL_KEY_LENGTH);
+ total_key_length_ = x;
+ }
+ void set_next_key_id(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF((x - 1) > MAX_KEY_ID);
+ next_key_id_ = x;
+ }
+ void set_max_key_id(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_KEY_ID);
+ max_key_id_ = x;
+ }
+ void set_num_keys(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_NUM_KEYS);
+ num_keys_ = x;
+ }
+ void set_max_num_keys(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_NUM_KEYS);
+ max_num_keys_ = x;
+ }
+ void set_num_phantoms(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > max_num_nodes());
+ num_phantoms_ = x;
+ }
+ void set_num_zombies(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > max_num_nodes());
+ num_zombies_ = x;
+ }
+ void set_num_blocks(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > max_num_blocks());
+ num_blocks_ = x;
+ }
+ void set_max_num_blocks(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_NUM_BLOCKS);
+ max_num_blocks_ = x;
+ }
+ void set_next_key_pos(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > key_buf_size());
+ next_key_pos_ = x;
+ }
+ void set_key_buf_size(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(x > MAX_KEY_BUF_SIZE);
+ key_buf_size_ = x;
+ }
+ void set_status_flags(UInt32 x) {
+ status_flags_ = x;
+ }
+ void set_ith_leader(UInt32 i, UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(i > MAX_BLOCK_LEVEL);
+ GRN_DAT_DEBUG_THROW_IF((x != INVALID_LEADER) && (x >= num_blocks()));
+ leaders_[i] = x;
+ }
+
+ private:
+ UInt64 file_size_;
+ UInt32 total_key_length_;
+ UInt32 next_key_id_;
+ UInt32 max_key_id_;
+ UInt32 num_keys_;
+ UInt32 max_num_keys_;
+ UInt32 num_phantoms_;
+ UInt32 num_zombies_;
+ UInt32 num_blocks_;
+ UInt32 max_num_blocks_;
+ UInt32 next_key_pos_;
+ UInt32 key_buf_size_;
+ UInt32 leaders_[MAX_BLOCK_LEVEL + 1];
+ UInt32 status_flags_;
+ UInt32 reserved_[12];
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_HPP_HEADER_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/id-cursor.cpp b/storage/mroonga/vendor/groonga/lib/dat/id-cursor.cpp
new file mode 100644
index 00000000000..784175f37ab
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/id-cursor.cpp
@@ -0,0 +1,184 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "id-cursor.hpp"
+
+#include <algorithm>
+
+#include "trie.hpp"
+
+namespace grn {
+namespace dat {
+
+IdCursor::IdCursor()
+ : trie_(NULL),
+ offset_(0),
+ limit_(MAX_UINT32),
+ flags_(ID_RANGE_CURSOR),
+ cur_(INVALID_KEY_ID),
+ end_(INVALID_KEY_ID),
+ count_(0) {}
+
+IdCursor::~IdCursor() {}
+
+void IdCursor::open(const Trie &trie,
+ const String &min_str,
+ const String &max_str,
+ UInt32 offset,
+ UInt32 limit,
+ UInt32 flags) {
+ UInt32 min_id = INVALID_KEY_ID;
+ if (min_str.ptr() != NULL) {
+ UInt32 key_pos;
+ GRN_DAT_THROW_IF(PARAM_ERROR,
+ !trie.search(min_str.ptr(), min_str.length(), &key_pos));
+ min_id = trie.get_key(key_pos).id();
+ }
+
+ UInt32 max_id = INVALID_KEY_ID;
+ if (max_str.ptr() != NULL) {
+ UInt32 key_pos;
+ GRN_DAT_THROW_IF(PARAM_ERROR,
+ !trie.search(max_str.ptr(), max_str.length(), &key_pos));
+ max_id = trie.get_key(key_pos).id();
+ }
+
+ open(trie, min_id, max_id, offset, limit, flags);
+}
+
+void IdCursor::open(const Trie &trie,
+ UInt32 min_id,
+ UInt32 max_id,
+ UInt32 offset,
+ UInt32 limit,
+ UInt32 flags) {
+ flags = fix_flags(flags);
+
+ IdCursor new_cursor(trie, offset, limit, flags);
+ new_cursor.init(min_id, max_id);
+ new_cursor.swap(this);
+}
+
+void IdCursor::close() {
+ IdCursor new_cursor;
+ new_cursor.swap(this);
+}
+
+const Key &IdCursor::next() {
+ if (count_ >= limit_) {
+ return Key::invalid_key();
+ }
+ while (cur_ != end_) {
+ const Key &key = trie_->ith_key(cur_);
+ if ((flags_ & ASCENDING_CURSOR) == ASCENDING_CURSOR) {
+ ++cur_;
+ } else {
+ --cur_;
+ }
+ if (key.is_valid()) {
+ ++count_;
+ return key;
+ }
+ }
+ return Key::invalid_key();
+}
+
+IdCursor::IdCursor(const Trie &trie,
+ UInt32 offset,
+ UInt32 limit,
+ UInt32 flags)
+ : trie_(&trie),
+ offset_(offset),
+ limit_(limit),
+ flags_(flags),
+ cur_(INVALID_KEY_ID),
+ end_(INVALID_KEY_ID),
+ count_(0) {}
+
+UInt32 IdCursor::fix_flags(UInt32 flags) const {
+ const UInt32 cursor_type = flags & CURSOR_TYPE_MASK;
+ GRN_DAT_THROW_IF(PARAM_ERROR, (cursor_type != 0) &&
+ (cursor_type != ID_RANGE_CURSOR));
+ flags |= ID_RANGE_CURSOR;
+
+ const UInt32 cursor_order = flags & CURSOR_ORDER_MASK;
+ GRN_DAT_THROW_IF(PARAM_ERROR, (cursor_order != 0) &&
+ (cursor_order != ASCENDING_CURSOR) &&
+ (cursor_order != DESCENDING_CURSOR));
+ if (cursor_order == 0) {
+ flags |= ASCENDING_CURSOR;
+ }
+
+ const UInt32 cursor_options = flags & CURSOR_OPTIONS_MASK;
+ GRN_DAT_THROW_IF(PARAM_ERROR,
+ cursor_options & ~(EXCEPT_LOWER_BOUND | EXCEPT_UPPER_BOUND));
+
+ return flags;
+}
+
+void IdCursor::init(UInt32 min_id, UInt32 max_id) {
+ if (min_id == INVALID_KEY_ID) {
+ min_id = trie_->min_key_id();
+ } else if ((flags_ & EXCEPT_LOWER_BOUND) == EXCEPT_LOWER_BOUND) {
+ ++min_id;
+ }
+
+ if (max_id == INVALID_KEY_ID) {
+ max_id = trie_->max_key_id();
+ } else if ((flags_ & EXCEPT_UPPER_BOUND) == EXCEPT_UPPER_BOUND) {
+ --max_id;
+ }
+
+ if ((max_id < min_id) || ((max_id - min_id) < offset_)) {
+ return;
+ }
+
+ if ((flags_ & ASCENDING_CURSOR) == ASCENDING_CURSOR) {
+ cur_ = min_id;
+ end_ = max_id + 1;
+ for (UInt32 i = 0; (i < offset_) && (cur_ != end_); ++i) {
+ while (cur_ != end_) {
+ if (trie_->ith_key(cur_++).is_valid()) {
+ break;
+ }
+ }
+ }
+ } else {
+ cur_ = max_id;
+ end_ = min_id - 1;
+ for (UInt32 i = 0; (i < offset_) && (cur_ != end_); ++i) {
+ while (cur_ != end_) {
+ if (trie_->ith_key(cur_--).is_valid()) {
+ break;
+ }
+ }
+ }
+ }
+}
+
+void IdCursor::swap(IdCursor *cursor) {
+ std::swap(trie_, cursor->trie_);
+ std::swap(offset_, cursor->offset_);
+ std::swap(limit_, cursor->limit_);
+ std::swap(flags_, cursor->flags_);
+ std::swap(cur_, cursor->cur_);
+ std::swap(end_, cursor->end_);
+ std::swap(count_, cursor->count_);
+}
+
+} // namespace dat
+} // namespace grn
diff --git a/storage/mroonga/vendor/groonga/lib/dat/id-cursor.hpp b/storage/mroonga/vendor/groonga/lib/dat/id-cursor.hpp
new file mode 100644
index 00000000000..aabd734e92e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/id-cursor.hpp
@@ -0,0 +1,85 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_ID_CURSOR_HPP_
+#define GRN_DAT_ID_CURSOR_HPP_
+
+#include "cursor.hpp"
+
+namespace grn {
+namespace dat {
+
+class Trie;
+
+class GRN_DAT_API IdCursor : public Cursor {
+ public:
+ IdCursor();
+ ~IdCursor();
+
+ void open(const Trie &trie,
+ const String &min_str,
+ const String &max_str,
+ UInt32 offset = 0,
+ UInt32 limit = MAX_UINT32,
+ UInt32 flags = 0);
+
+ void open(const Trie &trie,
+ UInt32 min_id,
+ UInt32 max_id,
+ UInt32 offset = 0,
+ UInt32 limit = MAX_UINT32,
+ UInt32 flags = 0);
+
+ void close();
+
+ const Key &next();
+
+ UInt32 offset() const {
+ return offset_;
+ }
+ UInt32 limit() const {
+ return limit_;
+ }
+ UInt32 flags() const {
+ return flags_;
+ }
+
+ private:
+ const Trie *trie_;
+ UInt32 offset_;
+ UInt32 limit_;
+ UInt32 flags_;
+
+ UInt32 cur_;
+ UInt32 end_;
+ UInt32 count_;
+
+ IdCursor(const Trie &trie, UInt32 offset, UInt32 limit, UInt32 flags);
+
+ UInt32 fix_flags(UInt32 flags) const;
+ void init(UInt32 min_id, UInt32 max_id);
+ void swap(IdCursor *cursor);
+
+ // Disallows copy and assignment.
+ IdCursor(const IdCursor &);
+ IdCursor &operator=(const IdCursor &);
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_ID_CURSOR_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/key-cursor.cpp b/storage/mroonga/vendor/groonga/lib/dat/key-cursor.cpp
new file mode 100644
index 00000000000..90ba25eca53
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/key-cursor.cpp
@@ -0,0 +1,349 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "key-cursor.hpp"
+
+#include <algorithm>
+#include <cstring>
+
+#include "trie.hpp"
+
+namespace grn {
+namespace dat {
+
+KeyCursor::KeyCursor()
+ : trie_(NULL),
+ offset_(0),
+ limit_(MAX_UINT32),
+ flags_(KEY_RANGE_CURSOR),
+ buf_(),
+ count_(0),
+ max_count_(0),
+ finished_(false),
+ end_buf_(NULL),
+ end_str_() {}
+
+KeyCursor::~KeyCursor() {
+ if (end_buf_ != NULL) {
+ delete [] end_buf_;
+ }
+}
+
+void KeyCursor::open(const Trie &trie,
+ const String &min_str,
+ const String &max_str,
+ UInt32 offset,
+ UInt32 limit,
+ UInt32 flags) {
+ GRN_DAT_THROW_IF(PARAM_ERROR,
+ (min_str.ptr() == NULL) && (min_str.length() != 0));
+ GRN_DAT_THROW_IF(PARAM_ERROR,
+ (max_str.ptr() == NULL) && (max_str.length() != 0));
+
+ flags = fix_flags(flags);
+ KeyCursor new_cursor(trie, offset, limit, flags);
+ new_cursor.init(min_str, max_str);
+ new_cursor.swap(this);
+}
+
+void KeyCursor::close() {
+ KeyCursor new_cursor;
+ new_cursor.swap(this);
+}
+
+const Key &KeyCursor::next() {
+ if (finished_ || (count_ >= max_count_)) {
+ return Key::invalid_key();
+ }
+
+ if ((flags_ & ASCENDING_CURSOR) == ASCENDING_CURSOR) {
+ return ascending_next();
+ } else {
+ return descending_next();
+ }
+}
+
+KeyCursor::KeyCursor(const Trie &trie,
+ UInt32 offset, UInt32 limit, UInt32 flags)
+ : trie_(&trie),
+ offset_(offset),
+ limit_(limit),
+ flags_(flags),
+ buf_(),
+ count_(0),
+ max_count_(0),
+ finished_(false),
+ end_buf_(NULL),
+ end_str_() {}
+
+UInt32 KeyCursor::fix_flags(UInt32 flags) const {
+ const UInt32 cursor_type = flags & CURSOR_TYPE_MASK;
+ GRN_DAT_THROW_IF(PARAM_ERROR, (cursor_type != 0) &&
+ (cursor_type != KEY_RANGE_CURSOR));
+ flags |= KEY_RANGE_CURSOR;
+
+ const UInt32 cursor_order = flags & CURSOR_ORDER_MASK;
+ GRN_DAT_THROW_IF(PARAM_ERROR, (cursor_order != 0) &&
+ (cursor_order != ASCENDING_CURSOR) &&
+ (cursor_order != DESCENDING_CURSOR));
+ if (cursor_order == 0) {
+ flags |= ASCENDING_CURSOR;
+ }
+
+ const UInt32 cursor_options = flags & CURSOR_OPTIONS_MASK;
+ GRN_DAT_THROW_IF(PARAM_ERROR,
+ cursor_options & ~(EXCEPT_LOWER_BOUND | EXCEPT_UPPER_BOUND));
+
+ return flags;
+}
+
+void KeyCursor::init(const String &min_str, const String &max_str) {
+ if (offset_ > (MAX_UINT32 - limit_)) {
+ max_count_ = MAX_UINT32;
+ } else {
+ max_count_ = offset_ + limit_;
+ }
+
+ if (limit_ == 0) {
+ return;
+ }
+
+ if ((flags_ & ASCENDING_CURSOR) == ASCENDING_CURSOR) {
+ ascending_init(min_str, max_str);
+ } else {
+ descending_init(min_str, max_str);
+ }
+}
+
+void KeyCursor::ascending_init(const String &min_str, const String &max_str) {
+ if (max_str.ptr() != NULL) {
+ if (max_str.length() != 0) {
+ end_buf_ = new UInt8[max_str.length()];
+ std::memcpy(end_buf_, max_str.ptr(), max_str.length());
+ end_str_.assign(end_buf_, max_str.length());
+ }
+ }
+
+ if ((min_str.ptr() == NULL) || (min_str.length() == 0)) {
+ buf_.push_back(ROOT_NODE_ID);
+ return;
+ }
+
+ UInt32 node_id = ROOT_NODE_ID;
+ Node node;
+ for (UInt32 i = 0; i < min_str.length(); ++i) {
+ node = trie_->ith_node(node_id);
+ if (node.is_linker()) {
+ const Key &key = trie_->get_key(node.key_pos());
+ const int result = key.str().compare(min_str, i);
+ if ((result > 0) || ((result == 0) &&
+ ((flags_ & EXCEPT_LOWER_BOUND) != EXCEPT_LOWER_BOUND))) {
+ buf_.push_back(node_id);
+ } else if (node.sibling() != INVALID_LABEL) {
+ buf_.push_back(node_id ^ node.label() ^ node.sibling());
+ }
+ return;
+ } else if (node.sibling() != INVALID_LABEL) {
+ buf_.push_back(node_id ^ node.label() ^ node.sibling());
+ }
+
+ node_id = node.offset() ^ min_str[i];
+ if (trie_->ith_node(node_id).label() != min_str[i]) {
+ UInt16 label = node.child();
+ if (label == TERMINAL_LABEL) {
+ label = trie_->ith_node(node.offset() ^ label).sibling();
+ }
+ while (label != INVALID_LABEL) {
+ if (label > min_str[i]) {
+ buf_.push_back(node.offset() ^ label);
+ break;
+ }
+ label = trie_->ith_node(node.offset() ^ label).sibling();
+ }
+ return;
+ }
+ }
+
+ node = trie_->ith_node(node_id);
+ if (node.is_linker()) {
+ const Key &key = trie_->get_key(node.key_pos());
+ if ((key.length() != min_str.length()) ||
+ ((flags_ & EXCEPT_LOWER_BOUND) != EXCEPT_LOWER_BOUND)) {
+ buf_.push_back(node_id);
+ } else if (node.sibling() != INVALID_LABEL) {
+ buf_.push_back(node_id ^ node.label() ^ node.sibling());
+ }
+ return;
+ } else if (node.sibling() != INVALID_LABEL) {
+ buf_.push_back(node_id ^ node.label() ^ node.sibling());
+ }
+
+ UInt16 label = node.child();
+ if ((label == TERMINAL_LABEL) &&
+ ((flags_ & EXCEPT_LOWER_BOUND) == EXCEPT_LOWER_BOUND)) {
+ label = trie_->ith_node(node.offset() ^ label).sibling();
+ }
+ if (label != INVALID_LABEL) {
+ buf_.push_back(node.offset() ^ label);
+ }
+}
+
+void KeyCursor::descending_init(const String &min_str, const String &max_str) {
+ if (min_str.ptr() != NULL) {
+ if (min_str.length() != 0) {
+ end_buf_ = new UInt8[min_str.length()];
+ std::memcpy(end_buf_, min_str.ptr(), min_str.length());
+ end_str_.assign(end_buf_, min_str.length());
+ }
+ }
+
+ if ((max_str.ptr() == NULL) || (max_str.length() == 0)) {
+ buf_.push_back(ROOT_NODE_ID);
+ return;
+ }
+
+ UInt32 node_id = ROOT_NODE_ID;
+ for (UInt32 i = 0; i < max_str.length(); ++i) {
+ const Base base = trie_->ith_node(node_id).base();
+ if (base.is_linker()) {
+ const Key &key = trie_->get_key(base.key_pos());
+ const int result = key.str().compare(max_str, i);
+ if ((result < 0) || ((result == 0) &&
+ ((flags_ & EXCEPT_UPPER_BOUND) != EXCEPT_UPPER_BOUND))) {
+ buf_.push_back(node_id | POST_ORDER_FLAG);
+ }
+ return;
+ }
+
+ UInt32 label = trie_->ith_node(node_id).child();
+ if (label == TERMINAL_LABEL) {
+ node_id = base.offset() ^ label;
+ buf_.push_back(node_id | POST_ORDER_FLAG);
+ label = trie_->ith_node(node_id).sibling();
+ }
+ while (label != INVALID_LABEL) {
+ node_id = base.offset() ^ label;
+ if (label < max_str[i]) {
+ buf_.push_back(node_id);
+ } else if (label > max_str[i]) {
+ return;
+ } else {
+ break;
+ }
+ label = trie_->ith_node(node_id).sibling();
+ }
+ if (label == INVALID_LABEL) {
+ return;
+ }
+ }
+
+ const Base base = trie_->ith_node(node_id).base();
+ if (base.is_linker()) {
+ const Key &key = trie_->get_key(base.key_pos());
+ if ((key.length() == max_str.length()) &&
+ ((flags_ & EXCEPT_UPPER_BOUND) != EXCEPT_UPPER_BOUND)) {
+ buf_.push_back(node_id | POST_ORDER_FLAG);
+ }
+ return;
+ }
+
+ UInt16 label = trie_->ith_node(node_id).child();
+ if ((label == TERMINAL_LABEL) &&
+ ((flags_ & EXCEPT_UPPER_BOUND) != EXCEPT_UPPER_BOUND)) {
+ buf_.push_back((base.offset() ^ label) | POST_ORDER_FLAG);
+ }
+}
+
+void KeyCursor::swap(KeyCursor *cursor) {
+ std::swap(trie_, cursor->trie_);
+ std::swap(offset_, cursor->offset_);
+ std::swap(limit_, cursor->limit_);
+ std::swap(flags_, cursor->flags_);
+ buf_.swap(&cursor->buf_);
+ std::swap(count_, cursor->count_);
+ std::swap(max_count_, cursor->max_count_);
+ std::swap(finished_, cursor->finished_);
+ std::swap(end_buf_, cursor->end_buf_);
+ end_str_.swap(&cursor->end_str_);
+}
+
+const Key &KeyCursor::ascending_next() {
+ while (!buf_.empty()) {
+ const UInt32 node_id = buf_.back();
+ buf_.pop_back();
+
+ const Node node = trie_->ith_node(node_id);
+ if (node.sibling() != INVALID_LABEL) {
+ buf_.push_back(node_id ^ node.label() ^ node.sibling());
+ }
+
+ if (node.is_linker()) {
+ const Key &key = trie_->get_key(node.key_pos());
+ if (end_buf_ != NULL) {
+ const int result = key.str().compare(end_str_);
+ if ((result > 0) || ((result == 0) &&
+ ((flags_ & EXCEPT_UPPER_BOUND) == EXCEPT_UPPER_BOUND))) {
+ finished_ = true;
+ return Key::invalid_key();
+ }
+ }
+ if (count_++ >= offset_) {
+ return key;
+ }
+ } else if (node.child() != INVALID_LABEL) {
+ buf_.push_back(node.offset() ^ node.child());
+ }
+ }
+ return Key::invalid_key();
+}
+
+const Key &KeyCursor::descending_next() {
+ while (!buf_.empty()) {
+ const bool post_order = (buf_.back() & POST_ORDER_FLAG) == POST_ORDER_FLAG;
+ const UInt32 node_id = buf_.back() & ~POST_ORDER_FLAG;
+
+ const Base base = trie_->ith_node(node_id).base();
+ if (post_order) {
+ buf_.pop_back();
+ if (base.is_linker()) {
+ const Key &key = trie_->get_key(base.key_pos());
+ if (end_buf_ != NULL) {
+ const int result = key.str().compare(end_str_);
+ if ((result < 0) || ((result == 0) &&
+ ((flags_ & EXCEPT_LOWER_BOUND) == EXCEPT_LOWER_BOUND))) {
+ finished_ = true;
+ return Key::invalid_key();
+ }
+ }
+ if (count_++ >= offset_) {
+ return key;
+ }
+ }
+ } else {
+ buf_.back() |= POST_ORDER_FLAG;
+ UInt16 label = trie_->ith_node(node_id).child();
+ while (label != INVALID_LABEL) {
+ buf_.push_back(base.offset() ^ label);
+ label = trie_->ith_node(base.offset() ^ label).sibling();
+ }
+ }
+ }
+ return Key::invalid_key();
+}
+
+} // namespace dat
+} // namespace grn
diff --git a/storage/mroonga/vendor/groonga/lib/dat/key-cursor.hpp b/storage/mroonga/vendor/groonga/lib/dat/key-cursor.hpp
new file mode 100644
index 00000000000..adce41c3123
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/key-cursor.hpp
@@ -0,0 +1,90 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_KEY_CURSOR_HPP_
+#define GRN_DAT_KEY_CURSOR_HPP_
+
+#include "cursor.hpp"
+#include "vector.hpp"
+
+namespace grn {
+namespace dat {
+
+class Trie;
+
+class GRN_DAT_API KeyCursor : public Cursor {
+ public:
+ KeyCursor();
+ ~KeyCursor();
+
+ void open(const Trie &trie,
+ const String &min_str,
+ const String &max_str,
+ UInt32 offset = 0,
+ UInt32 limit = MAX_UINT32,
+ UInt32 flags = 0);
+
+ void close();
+
+ const Key &next();
+
+ UInt32 offset() const {
+ return offset_;
+ }
+ UInt32 limit() const {
+ return limit_;
+ }
+ UInt32 flags() const {
+ return flags_;
+ }
+
+ private:
+ const Trie *trie_;
+ UInt32 offset_;
+ UInt32 limit_;
+ UInt32 flags_;
+
+ Vector<UInt32> buf_;
+ UInt32 count_;
+ UInt32 max_count_;
+ bool finished_;
+ UInt8 *end_buf_;
+ String end_str_;
+
+ KeyCursor(const Trie &trie,
+ UInt32 offset, UInt32 limit, UInt32 flags);
+
+ UInt32 fix_flags(UInt32 flags) const;
+ void init(const String &min_str, const String &max_str);
+ void ascending_init(const String &min_str, const String &max_str);
+ void descending_init(const String &min_str, const String &max_str);
+ void swap(KeyCursor *cursor);
+
+ const Key &ascending_next();
+ const Key &descending_next();
+
+ static const UInt32 POST_ORDER_FLAG = 0x80000000U;
+
+ // Disallows copy and assignment.
+ KeyCursor(const KeyCursor &);
+ KeyCursor &operator=(const KeyCursor &);
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_KEY_CURSOR_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/key.hpp b/storage/mroonga/vendor/groonga/lib/dat/key.hpp
new file mode 100644
index 00000000000..21ae474cf44
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/key.hpp
@@ -0,0 +1,112 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_KEY_HPP_
+#define GRN_DAT_KEY_HPP_
+
+#include "string.hpp"
+
+namespace grn {
+namespace dat {
+
+class GRN_DAT_API Key {
+ public:
+ const UInt8 &operator[](UInt32 i) const {
+ GRN_DAT_DEBUG_THROW_IF(i >= length());
+ return buf_[i];
+ }
+
+ bool is_valid() const {
+ return id() != INVALID_KEY_ID;
+ }
+
+ String str() const {
+ return String(ptr(), length());
+ }
+
+ const void *ptr() const {
+ return buf_;
+ }
+ UInt32 length() const {
+ return (length_high_ << 4) | (id_and_length_low_ & 0x0F);
+ }
+ UInt32 id() const {
+ return id_and_length_low_ >> 4;
+ }
+
+ bool equals_to(const void *ptr, UInt32 length, UInt32 offset = 0) const {
+ if (length != this->length()) {
+ return false;
+ }
+ for ( ; offset < length; ++offset) {
+ if ((*this)[offset] != static_cast<const UInt8 *>(ptr)[offset]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Creates an object of Key from given parameters. Then, the created object
+ // is embedded into a specified buffer.
+ static const Key &create(UInt32 *buf, UInt32 key_id,
+ const void *key_ptr, UInt32 key_length) {
+ GRN_DAT_DEBUG_THROW_IF(buf == NULL);
+ GRN_DAT_DEBUG_THROW_IF(key_id > MAX_KEY_ID);
+ GRN_DAT_DEBUG_THROW_IF((key_ptr == NULL) && (key_length != 0));
+ GRN_DAT_DEBUG_THROW_IF(key_length > MAX_KEY_LENGTH);
+
+ *buf = (key_id << 4) | (key_length & 0x0F);
+ UInt8 *ptr = reinterpret_cast<UInt8 *>(buf + 1);
+ *ptr++ = key_length >> 4;
+ for (UInt32 i = 0; i < key_length; ++i) {
+ ptr[i] = static_cast<const UInt8 *>(key_ptr)[i];
+ }
+ return *reinterpret_cast<const Key *>(buf);
+ }
+
+ // Calculates how many UInt32s are required for a string. It is guaranteed
+ // that the estimated size is not less than the actual size.
+ static UInt32 estimate_size(UInt32 length) {
+ return 2 + (length / sizeof(UInt32));
+ }
+
+ // Returns a reference to an invalid key.
+ static const Key &invalid_key() {
+ static const Key invalid_key;
+ return invalid_key;
+// static const UInt32 invalid_key_buf[2] = { INVALID_KEY_ID << 4, 0 };
+// return *reinterpret_cast<const Key *>(invalid_key_buf);
+ }
+
+ private:
+ UInt32 id_and_length_low_;
+ UInt8 length_high_;
+ UInt8 buf_[3];
+
+ // Disallows instantiation.
+ Key() : id_and_length_low_(INVALID_KEY_ID << 4), length_high_(0) {}
+ ~Key() {}
+
+ // Disallows copy and assignment.
+ Key(const Key &);
+ Key &operator=(const Key &);
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_KEY_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/node.hpp b/storage/mroonga/vendor/groonga/lib/dat/node.hpp
new file mode 100644
index 00000000000..45ae5089b3f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/node.hpp
@@ -0,0 +1,129 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_NODE_HPP_
+#define GRN_DAT_NODE_HPP_
+
+// See base.hpp and check.hpp for details.
+#include "base.hpp"
+#include "check.hpp"
+
+namespace grn {
+namespace dat {
+
+class GRN_DAT_API Node {
+ public:
+ Node() : base_(), check_() {}
+
+ Base base() const {
+ return base_;
+ }
+ bool is_linker() const {
+ GRN_DAT_DEBUG_THROW_IF(is_phantom());
+ return base_.is_linker();
+ }
+ UInt32 offset() const {
+ GRN_DAT_DEBUG_THROW_IF(is_phantom());
+ return base_.offset();
+ }
+ UInt32 key_pos() const {
+ GRN_DAT_DEBUG_THROW_IF(is_phantom());
+ return base_.key_pos();
+ }
+
+ Check check() const {
+ return check_;
+ }
+ bool is_offset() const {
+ return check_.is_offset();
+ }
+ UInt32 except_is_offset() const {
+ return check_.except_is_offset();
+ }
+ bool is_phantom() const {
+ return check_.is_phantom();
+ }
+ UInt32 next() const {
+ return check_.next();
+ }
+ UInt32 prev() const {
+ return check_.prev();
+ }
+ UInt32 label() const {
+ return check_.label();
+ }
+ UInt32 child() const {
+ return check_.child();
+ }
+ UInt32 sibling() const {
+ return check_.sibling();
+ }
+
+ void set_base(Base x) {
+ GRN_DAT_DEBUG_THROW_IF(is_phantom());
+ base_ = x;
+ }
+ void set_offset(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(is_phantom());
+ base_.set_offset(x);
+ }
+ void set_key_pos(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(is_phantom());
+ base_.set_key_pos(x);
+ }
+
+ void set_check(Check x) {
+ check_ = x;
+ }
+ void set_is_offset(bool x) {
+ check_.set_is_offset(x);
+ }
+ void set_except_is_offset(UInt32 x) {
+ check_.set_except_is_offset(x);
+ }
+ void set_is_phantom(bool x) {
+ GRN_DAT_DEBUG_THROW_IF(base_.offset() != INVALID_OFFSET);
+ check_.set_is_phantom(x);
+ }
+ void set_next(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(base_.offset() != INVALID_OFFSET);
+ check_.set_next(x);
+ }
+ void set_prev(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(base_.offset() != INVALID_OFFSET);
+ check_.set_prev(x);
+ }
+ void set_label(UInt32 x) {
+ GRN_DAT_DEBUG_THROW_IF(offset() != INVALID_OFFSET);
+ check_.set_label(x);
+ }
+ void set_child(UInt32 x) {
+ check_.set_child(x);
+ }
+ void set_sibling(UInt32 x) {
+ check_.set_sibling(x);
+ }
+
+ private:
+ Base base_;
+ Check check_;
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_NODE_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/predictive-cursor.cpp b/storage/mroonga/vendor/groonga/lib/dat/predictive-cursor.cpp
new file mode 100644
index 00000000000..4737d841269
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/predictive-cursor.cpp
@@ -0,0 +1,206 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "predictive-cursor.hpp"
+
+#include <algorithm>
+#include <cstring>
+
+#include "trie.hpp"
+
+namespace grn {
+namespace dat {
+
+PredictiveCursor::PredictiveCursor()
+ : trie_(NULL),
+ offset_(0),
+ limit_(MAX_UINT32),
+ flags_(PREDICTIVE_CURSOR),
+ buf_(),
+ cur_(0),
+ end_(0),
+ min_length_(0) {}
+
+PredictiveCursor::~PredictiveCursor() {}
+
+void PredictiveCursor::open(const Trie &trie,
+ const String &str,
+ UInt32 offset,
+ UInt32 limit,
+ UInt32 flags) {
+ GRN_DAT_THROW_IF(PARAM_ERROR, (str.ptr() == NULL) && (str.length() != 0));
+
+ flags = fix_flags(flags);
+ PredictiveCursor new_cursor(trie, offset, limit, flags);
+ new_cursor.init(str);
+ new_cursor.swap(this);
+}
+
+void PredictiveCursor::close() {
+ PredictiveCursor new_cursor;
+ new_cursor.swap(this);
+}
+
+const Key &PredictiveCursor::next() {
+ if (cur_ == end_) {
+ return Key::invalid_key();
+ }
+
+ if ((flags_ & ASCENDING_CURSOR) == ASCENDING_CURSOR) {
+ return ascending_next();
+ } else {
+ return descending_next();
+ }
+}
+
+PredictiveCursor::PredictiveCursor(const Trie &trie,
+ UInt32 offset, UInt32 limit, UInt32 flags)
+ : trie_(&trie),
+ offset_(offset),
+ limit_(limit),
+ flags_(flags),
+ buf_(),
+ cur_(0),
+ end_(0),
+ min_length_(0) {}
+
+UInt32 PredictiveCursor::fix_flags(UInt32 flags) const {
+ const UInt32 cursor_type = flags & CURSOR_TYPE_MASK;
+ GRN_DAT_THROW_IF(PARAM_ERROR, (cursor_type != 0) &&
+ (cursor_type != PREDICTIVE_CURSOR));
+ flags |= PREDICTIVE_CURSOR;
+
+ const UInt32 cursor_order = flags & CURSOR_ORDER_MASK;
+ GRN_DAT_THROW_IF(PARAM_ERROR, (cursor_order != 0) &&
+ (cursor_order != ASCENDING_CURSOR) &&
+ (cursor_order != DESCENDING_CURSOR));
+ if (cursor_order == 0) {
+ flags |= ASCENDING_CURSOR;
+ }
+
+ const UInt32 cursor_options = flags & CURSOR_OPTIONS_MASK;
+ GRN_DAT_THROW_IF(PARAM_ERROR, cursor_options & ~(EXCEPT_EXACT_MATCH));
+
+ return flags;
+}
+
+void PredictiveCursor::init(const String &str) {
+ if (limit_ == 0) {
+ return;
+ }
+
+ min_length_ = str.length();
+ if ((flags_ & EXCEPT_EXACT_MATCH) == EXCEPT_EXACT_MATCH) {
+ ++min_length_;
+ }
+ end_ = (offset_ > (MAX_UINT32 - limit_)) ? MAX_UINT32 : (offset_ + limit_);
+
+ UInt32 node_id = ROOT_NODE_ID;
+ for (UInt32 i = 0; i < str.length(); ++i) {
+ const Base base = trie_->ith_node(node_id).base();
+ if (base.is_linker()) {
+ if (offset_ == 0) {
+ const Key &key = trie_->get_key(base.key_pos());
+ if ((key.length() >= str.length()) &&
+ (key.str().substr(0, str.length()).compare(str, i) == 0)) {
+ if ((flags_ & ASCENDING_CURSOR) == ASCENDING_CURSOR) {
+ node_id |= IS_ROOT_FLAG;
+ }
+ buf_.push_back(node_id);
+ }
+ }
+ return;
+ }
+
+ node_id = base.offset() ^ str[i];
+ if (trie_->ith_node(node_id).label() != str[i]) {
+ return;
+ }
+ }
+
+ if ((flags_ & ASCENDING_CURSOR) == ASCENDING_CURSOR) {
+ node_id |= IS_ROOT_FLAG;
+ }
+ buf_.push_back(node_id);
+}
+
+void PredictiveCursor::swap(PredictiveCursor *cursor) {
+ std::swap(trie_, cursor->trie_);
+ std::swap(offset_, cursor->offset_);
+ std::swap(limit_, cursor->limit_);
+ std::swap(flags_, cursor->flags_);
+ buf_.swap(&cursor->buf_);
+ std::swap(cur_, cursor->cur_);
+ std::swap(end_, cursor->end_);
+ std::swap(min_length_, cursor->min_length_);
+}
+
+const Key &PredictiveCursor::ascending_next() {
+ while (!buf_.empty()) {
+ const bool is_root = (buf_.back() & IS_ROOT_FLAG) == IS_ROOT_FLAG;
+ const UInt32 node_id = buf_.back() & ~IS_ROOT_FLAG;
+ buf_.pop_back();
+
+ const Node node = trie_->ith_node(node_id);
+ if (!is_root && (node.sibling() != INVALID_LABEL)) {
+ buf_.push_back(node_id ^ node.label() ^ node.sibling());
+ }
+
+ if (node.is_linker()) {
+ const Key &key = trie_->get_key(node.key_pos());
+ if (key.length() >= min_length_) {
+ if (cur_++ >= offset_) {
+ return key;
+ }
+ }
+ } else if (node.child() != INVALID_LABEL) {
+ buf_.push_back(node.offset() ^ node.child());
+ }
+ }
+ return Key::invalid_key();
+}
+
+const Key &PredictiveCursor::descending_next() {
+ while (!buf_.empty()) {
+ const bool post_order = (buf_.back() & POST_ORDER_FLAG) == POST_ORDER_FLAG;
+ const UInt32 node_id = buf_.back() & ~POST_ORDER_FLAG;
+
+ const Base base = trie_->ith_node(node_id).base();
+ if (post_order) {
+ buf_.pop_back();
+ if (base.is_linker()) {
+ const Key &key = trie_->get_key(base.key_pos());
+ if (key.length() >= min_length_) {
+ if (cur_++ >= offset_) {
+ return key;
+ }
+ }
+ }
+ } else {
+ buf_.back() |= POST_ORDER_FLAG;
+ UInt16 label = trie_->ith_node(node_id).child();
+ while (label != INVALID_LABEL) {
+ buf_.push_back(base.offset() ^ label);
+ label = trie_->ith_node(base.offset() ^ label).sibling();
+ }
+ }
+ }
+ return Key::invalid_key();
+}
+
+} // namespace dat
+} // namespace grn
diff --git a/storage/mroonga/vendor/groonga/lib/dat/predictive-cursor.hpp b/storage/mroonga/vendor/groonga/lib/dat/predictive-cursor.hpp
new file mode 100644
index 00000000000..e4041ff22f8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/predictive-cursor.hpp
@@ -0,0 +1,86 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_PREDICTIVE_CURSOR_HPP_
+#define GRN_DAT_PREDICTIVE_CURSOR_HPP_
+
+#include "cursor.hpp"
+#include "vector.hpp"
+
+namespace grn {
+namespace dat {
+
+class Trie;
+
+class GRN_DAT_API PredictiveCursor : public Cursor {
+ public:
+ PredictiveCursor();
+ ~PredictiveCursor();
+
+ void open(const Trie &trie,
+ const String &str,
+ UInt32 offset = 0,
+ UInt32 limit = MAX_UINT32,
+ UInt32 flags = 0);
+
+ void close();
+
+ const Key &next();
+
+ UInt32 offset() const {
+ return offset_;
+ }
+ UInt32 limit() const {
+ return limit_;
+ }
+ UInt32 flags() const {
+ return flags_;
+ }
+
+ private:
+ const Trie *trie_;
+ UInt32 offset_;
+ UInt32 limit_;
+ UInt32 flags_;
+
+ Vector<UInt32> buf_;
+ UInt32 cur_;
+ UInt32 end_;
+ UInt32 min_length_;
+
+ PredictiveCursor(const Trie &trie,
+ UInt32 offset, UInt32 limit, UInt32 flags);
+
+ UInt32 fix_flags(UInt32 flags) const;
+ void init(const String &str);
+ void swap(PredictiveCursor *cursor);
+
+ const Key &ascending_next();
+ const Key &descending_next();
+
+ static const UInt32 IS_ROOT_FLAG = 0x80000000U;
+ static const UInt32 POST_ORDER_FLAG = 0x80000000U;
+
+ // Disallows copy and assignment.
+ PredictiveCursor(const PredictiveCursor &);
+ PredictiveCursor &operator=(const PredictiveCursor &);
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_PREDICTIVE_CURSOR_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/prefix-cursor.cpp b/storage/mroonga/vendor/groonga/lib/dat/prefix-cursor.cpp
new file mode 100644
index 00000000000..7f994b5dc57
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/prefix-cursor.cpp
@@ -0,0 +1,175 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "prefix-cursor.hpp"
+
+#include <algorithm>
+
+#include "trie.hpp"
+
+namespace grn {
+namespace dat {
+
+PrefixCursor::PrefixCursor()
+ : trie_(NULL),
+ offset_(0),
+ limit_(MAX_UINT32),
+ flags_(PREFIX_CURSOR),
+ buf_(),
+ cur_(0),
+ end_(0) {}
+
+PrefixCursor::~PrefixCursor() {}
+
+void PrefixCursor::open(const Trie &trie,
+ const String &str,
+ UInt32 min_length,
+ UInt32 offset,
+ UInt32 limit,
+ UInt32 flags) {
+ GRN_DAT_THROW_IF(PARAM_ERROR, (str.ptr() == NULL) && (str.length() != 0));
+ GRN_DAT_THROW_IF(PARAM_ERROR, min_length > str.length());
+
+ flags = fix_flags(flags);
+ PrefixCursor new_cursor(trie, offset, limit, flags);
+ new_cursor.init(str, min_length);
+ new_cursor.swap(this);
+}
+
+void PrefixCursor::close() {
+ PrefixCursor new_cursor;
+ new_cursor.swap(this);
+}
+
+const Key &PrefixCursor::next() {
+ if (cur_ == end_) {
+ return Key::invalid_key();
+ }
+ if ((flags_ & ASCENDING_CURSOR) == ASCENDING_CURSOR) {
+ return trie_->get_key(buf_[cur_++]);
+ } else {
+ return trie_->get_key(buf_[--cur_]);
+ }
+}
+
+PrefixCursor::PrefixCursor(const Trie &trie,
+ UInt32 offset, UInt32 limit, UInt32 flags)
+ : trie_(&trie),
+ offset_(offset),
+ limit_(limit),
+ flags_(flags),
+ buf_(),
+ cur_(0),
+ end_(0) {}
+
+UInt32 PrefixCursor::fix_flags(UInt32 flags) const {
+ const UInt32 cursor_type = flags & CURSOR_TYPE_MASK;
+ GRN_DAT_THROW_IF(PARAM_ERROR, (cursor_type != 0) &&
+ (cursor_type != PREFIX_CURSOR));
+ flags |= PREFIX_CURSOR;
+
+ const UInt32 cursor_order = flags & CURSOR_ORDER_MASK;
+ GRN_DAT_THROW_IF(PARAM_ERROR, (cursor_order != 0) &&
+ (cursor_order != ASCENDING_CURSOR) &&
+ (cursor_order != DESCENDING_CURSOR));
+ if (cursor_order == 0) {
+ flags |= ASCENDING_CURSOR;
+ }
+
+ const UInt32 cursor_options = flags & CURSOR_OPTIONS_MASK;
+ GRN_DAT_THROW_IF(PARAM_ERROR, cursor_options & ~EXCEPT_EXACT_MATCH);
+
+ return flags;
+}
+
+void PrefixCursor::init(const String &str, UInt32 min_length) {
+ if ((limit_ == 0) || (offset_ > (str.length() - min_length))) {
+ return;
+ }
+
+ UInt32 node_id = ROOT_NODE_ID;
+ UInt32 i;
+ for (i = 0; i < str.length(); ++i) {
+ const Base base = trie_->ith_node(node_id).base();
+ if (base.is_linker()) {
+ const Key &key = trie_->get_key(base.key_pos());
+ if ((key.length() >= min_length) && (key.length() <= str.length()) &&
+ (str.substr(0, key.length()).compare(key.str(), i) == 0) &&
+ ((key.length() < str.length()) ||
+ ((flags_ & EXCEPT_EXACT_MATCH) != EXCEPT_EXACT_MATCH))) {
+ buf_.push_back(base.key_pos());
+ }
+ break;
+ }
+
+ if ((i >= min_length) &&
+ (trie_->ith_node(node_id).child() == TERMINAL_LABEL)) {
+ const Base linker_base =
+ trie_->ith_node(base.offset() ^ TERMINAL_LABEL).base();
+ if (linker_base.is_linker()) {
+ buf_.push_back(linker_base.key_pos());
+ }
+ }
+
+ node_id = base.offset() ^ str[i];
+ if (trie_->ith_node(node_id).label() != str[i]) {
+ break;
+ }
+ }
+
+ if ((i == str.length()) &&
+ ((flags_ & EXCEPT_EXACT_MATCH) != EXCEPT_EXACT_MATCH)) {
+ const Base base = trie_->ith_node(node_id).base();
+ if (base.is_linker()) {
+ const Key &key = trie_->get_key(base.key_pos());
+ if ((key.length() >= min_length) && (key.length() <= str.length())) {
+ buf_.push_back(base.key_pos());
+ }
+ } else if (trie_->ith_node(node_id).child() == TERMINAL_LABEL) {
+ const Base linker_base =
+ trie_->ith_node(base.offset() ^ TERMINAL_LABEL).base();
+ if (linker_base.is_linker()) {
+ buf_.push_back(linker_base.key_pos());
+ }
+ }
+ }
+
+ if (buf_.size() <= offset_) {
+ return;
+ }
+
+ if ((flags_ & ASCENDING_CURSOR) == ASCENDING_CURSOR) {
+ cur_ = offset_;
+ end_ = (limit_ < (buf_.size() - cur_)) ? (cur_ + limit_) : buf_.size();
+ } else {
+ cur_ = buf_.size() - offset_;
+ end_ = (limit_ < cur_) ? (cur_ - limit_) : 0;
+ }
+}
+
+void PrefixCursor::swap(PrefixCursor *cursor) {
+ std::swap(trie_, cursor->trie_);
+ std::swap(offset_, cursor->offset_);
+ std::swap(limit_, cursor->limit_);
+ std::swap(flags_, cursor->flags_);
+ buf_.swap(&cursor->buf_);
+ std::swap(cur_, cursor->cur_);
+ std::swap(end_, cursor->end_);
+}
+
+} // namespace dat
+} // namespace grn
diff --git a/storage/mroonga/vendor/groonga/lib/dat/prefix-cursor.hpp b/storage/mroonga/vendor/groonga/lib/dat/prefix-cursor.hpp
new file mode 100644
index 00000000000..7a84228cefa
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/prefix-cursor.hpp
@@ -0,0 +1,80 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_PREFIX_CURSOR_HPP_
+#define GRN_DAT_PREFIX_CURSOR_HPP_
+
+#include "cursor.hpp"
+#include "vector.hpp"
+
+namespace grn {
+namespace dat {
+
+class Trie;
+
+class GRN_DAT_API PrefixCursor : public Cursor {
+ public:
+ PrefixCursor();
+ ~PrefixCursor();
+
+ void open(const Trie &trie,
+ const String &str,
+ UInt32 min_length = 0,
+ UInt32 offset = 0,
+ UInt32 limit = MAX_UINT32,
+ UInt32 flags = 0);
+
+ void close();
+
+ const Key &next();
+
+ UInt32 offset() const {
+ return offset_;
+ }
+ UInt32 limit() const {
+ return limit_;
+ }
+ UInt32 flags() const {
+ return flags_;
+ }
+
+ private:
+ const Trie *trie_;
+ UInt32 offset_;
+ UInt32 limit_;
+ UInt32 flags_;
+
+ Vector<UInt32> buf_;
+ UInt32 cur_;
+ UInt32 end_;
+
+ PrefixCursor(const Trie &trie,
+ UInt32 offset, UInt32 limit, UInt32 flags);
+
+ UInt32 fix_flags(UInt32 flags) const;
+ void init(const String &str, UInt32 min_length);
+ void swap(PrefixCursor *cursor);
+
+ // Disallows copy and assignment.
+ PrefixCursor(const PrefixCursor &);
+ PrefixCursor &operator=(const PrefixCursor &);
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_PREFIX_CURSOR_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/sources.am b/storage/mroonga/vendor/groonga/lib/dat/sources.am
new file mode 100644
index 00000000000..26c9f09fde1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/sources.am
@@ -0,0 +1,29 @@
+libgrndat_la_SOURCES = \
+ cursor-factory.cpp \
+ file-impl.cpp \
+ file.cpp \
+ id-cursor.cpp \
+ key-cursor.cpp \
+ predictive-cursor.cpp \
+ prefix-cursor.cpp \
+ trie.cpp \
+ array.hpp \
+ base.hpp \
+ block.hpp \
+ check.hpp \
+ cursor-factory.hpp \
+ cursor.hpp \
+ dat.hpp \
+ entry.hpp \
+ file-impl.hpp \
+ file.hpp \
+ header.hpp \
+ id-cursor.hpp \
+ key-cursor.hpp \
+ key.hpp \
+ node.hpp \
+ predictive-cursor.hpp \
+ prefix-cursor.hpp \
+ string.hpp \
+ trie.hpp \
+ vector.hpp
diff --git a/storage/mroonga/vendor/groonga/lib/dat/string.hpp b/storage/mroonga/vendor/groonga/lib/dat/string.hpp
new file mode 100644
index 00000000000..ecbb1e79d04
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/string.hpp
@@ -0,0 +1,175 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_STRING_HPP_
+#define GRN_DAT_STRING_HPP_
+
+#include "dat.hpp"
+
+namespace grn {
+namespace dat {
+
+class GRN_DAT_API String {
+ public:
+ String()
+ : ptr_(NULL),
+ length_(0) {}
+ String(const void *ptr, UInt32 length)
+ : ptr_(static_cast<const UInt8 *>(ptr)),
+ length_(length) {}
+ template <UInt32 T>
+ explicit String(const char (&str)[T])
+ : ptr_(reinterpret_cast<const UInt8 *>(str)),
+ length_(T - 1) {}
+ String(const String &rhs)
+ : ptr_(rhs.ptr_),
+ length_(rhs.length_) {}
+
+ String &operator=(const String &rhs) {
+ set_ptr(rhs.ptr());
+ set_length(rhs.length());
+ return *this;
+ }
+
+ const UInt8 &operator[](UInt32 i) const {
+ GRN_DAT_DEBUG_THROW_IF(i >= length_);
+ return ptr_[i];
+ }
+
+ const void *ptr() const {
+ return ptr_;
+ }
+ UInt32 length() const {
+ return length_;
+ }
+
+ void set_ptr(const void *x) {
+ ptr_ = static_cast<const UInt8 *>(x);
+ }
+ void set_length(UInt32 x) {
+ length_ = x;
+ }
+
+ void assign(const void *ptr, UInt32 length) {
+ set_ptr(ptr);
+ set_length(length);
+ }
+
+ String substr(UInt32 offset = 0) const {
+ return String(ptr_ + offset, length_ - offset);
+ }
+ String substr(UInt32 offset, UInt32 length) const {
+ return String(ptr_ + offset, length);
+ }
+
+ // This function returns an integer as follows:
+ // - a negative value if *this < rhs,
+ // - zero if *this == rhs,
+ // - a positive value if *this > rhs,
+ // but if the offset is too large, the result is undefined.
+ int compare(const String &rhs, UInt32 offset = 0) const {
+ GRN_DAT_DEBUG_THROW_IF(offset > length());
+ GRN_DAT_DEBUG_THROW_IF(offset > rhs.length());
+
+ for (UInt32 i = offset; i < length(); ++i) {
+ if (i >= rhs.length()) {
+ return 1;
+ } else if ((*this)[i] != rhs[i]) {
+ return (*this)[i] - rhs[i];
+ }
+ }
+ return (length() == rhs.length()) ? 0 : -1;
+ }
+
+ bool starts_with(const String &str) const {
+ if (length() < str.length()) {
+ return false;
+ }
+ for (UInt32 i = 0; i < str.length(); ++i) {
+ if ((*this)[i] != str[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool ends_with(const String &str) const {
+ if (length() < str.length()) {
+ return false;
+ }
+ UInt32 offset = length() - str.length();
+ for (UInt32 i = 0; i < str.length(); ++i) {
+ if ((*this)[offset + i] != str[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void swap(String *rhs) {
+ const UInt8 * const ptr_temp = ptr_;
+ ptr_ = rhs->ptr_;
+ rhs->ptr_ = ptr_temp;
+
+ const UInt32 length_temp = length_;
+ length_ = rhs->length_;
+ rhs->length_ = length_temp;
+ }
+
+ private:
+ const UInt8 *ptr_;
+ UInt32 length_;
+};
+
+inline bool operator==(const String &lhs, const String &rhs) {
+ if (lhs.length() != rhs.length()) {
+ return false;
+ } else if (lhs.ptr() == rhs.ptr()) {
+ return true;
+ }
+ for (UInt32 i = 0; i < lhs.length(); ++i) {
+ if (lhs[i] != rhs[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool operator!=(const String &lhs, const String &rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator<(const String &lhs, const String &rhs) {
+ return lhs.compare(rhs) < 0;
+}
+
+inline bool operator>(const String &lhs, const String &rhs) {
+ return rhs < lhs;
+}
+
+inline bool operator<=(const String &lhs, const String &rhs) {
+ return !(lhs > rhs);
+}
+
+inline bool operator>=(const String &lhs, const String &rhs) {
+ return !(lhs < rhs);
+}
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_STRING_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/trie.cpp b/storage/mroonga/vendor/groonga/lib/dat/trie.cpp
new file mode 100644
index 00000000000..82c8c273286
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/trie.cpp
@@ -0,0 +1,1213 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "trie.hpp"
+
+#include <algorithm>
+#include <cstring>
+
+#include "vector.hpp"
+
+namespace grn {
+namespace dat {
+namespace {
+
+class StatusFlagManager {
+ public:
+ StatusFlagManager(Header *header, UInt32 status_flag)
+ : header_(header), status_flag_(status_flag) {
+ header_->set_status_flags(header_->status_flags() | status_flag_);
+ }
+ ~StatusFlagManager() {
+ header_->set_status_flags(header_->status_flags() & ~status_flag_);
+ }
+
+ private:
+ Header *header_;
+ UInt32 status_flag_;
+
+ // Disallows copy and assignment.
+ StatusFlagManager(const StatusFlagManager &);
+ StatusFlagManager &operator=(const StatusFlagManager &);
+};
+
+} // namespace
+
+Trie::Trie()
+ : file_(),
+ header_(NULL),
+ nodes_(),
+ blocks_(),
+ entries_(),
+ key_buf_() {}
+
+Trie::~Trie() {}
+
+void Trie::create(const char *file_name,
+ UInt64 file_size,
+ UInt32 max_num_keys,
+ double num_nodes_per_key,
+ double average_key_length) {
+ GRN_DAT_THROW_IF(PARAM_ERROR, (file_size != 0) && (max_num_keys != 0));
+
+ if (num_nodes_per_key < 1.0) {
+ num_nodes_per_key = DEFAULT_NUM_NODES_PER_KEY;
+ }
+ GRN_DAT_THROW_IF(PARAM_ERROR, num_nodes_per_key < 1.0);
+
+ if (average_key_length < 1.0) {
+ average_key_length = DEFAULT_AVERAGE_KEY_LENGTH;
+ }
+ GRN_DAT_THROW_IF(PARAM_ERROR, average_key_length < 1.0);
+ GRN_DAT_THROW_IF(PARAM_ERROR, average_key_length > MAX_KEY_LENGTH);
+
+ if (max_num_keys == 0) {
+ if (file_size == 0) {
+ file_size = DEFAULT_FILE_SIZE;
+ } else {
+ GRN_DAT_THROW_IF(PARAM_ERROR, file_size < MIN_FILE_SIZE);
+ GRN_DAT_THROW_IF(PARAM_ERROR, file_size > MAX_FILE_SIZE);
+ }
+ } else {
+ GRN_DAT_THROW_IF(PARAM_ERROR, max_num_keys > MAX_NUM_KEYS);
+ }
+
+ Trie new_trie;
+ new_trie.create_file(file_name, file_size, max_num_keys,
+ num_nodes_per_key, average_key_length);
+ new_trie.swap(this);
+}
+
+void Trie::create(const Trie &trie,
+ const char *file_name,
+ UInt64 file_size,
+ UInt32 max_num_keys,
+ double num_nodes_per_key,
+ double average_key_length) {
+ GRN_DAT_THROW_IF(PARAM_ERROR, (file_size != 0) && (max_num_keys != 0));
+
+ if (num_nodes_per_key < 1.0) {
+ if (trie.num_keys() == 0) {
+ num_nodes_per_key = DEFAULT_NUM_NODES_PER_KEY;
+ } else {
+ num_nodes_per_key = 1.0 * trie.num_nodes() / trie.num_keys();
+ }
+ }
+ GRN_DAT_THROW_IF(PARAM_ERROR, num_nodes_per_key < 1.0);
+
+ if (average_key_length < 1.0) {
+ if (trie.num_keys() == 0) {
+ average_key_length = DEFAULT_AVERAGE_KEY_LENGTH;
+ } else {
+ average_key_length = 1.0 * trie.total_key_length() / trie.num_keys();
+ }
+ }
+ GRN_DAT_THROW_IF(PARAM_ERROR, average_key_length < 1.0);
+ GRN_DAT_THROW_IF(PARAM_ERROR, average_key_length > MAX_KEY_LENGTH);
+
+ if (max_num_keys == 0) {
+ if (file_size == 0) {
+ file_size = trie.file_size();
+ }
+ GRN_DAT_THROW_IF(PARAM_ERROR, file_size < MIN_FILE_SIZE);
+ GRN_DAT_THROW_IF(PARAM_ERROR, file_size > MAX_FILE_SIZE);
+ GRN_DAT_THROW_IF(PARAM_ERROR, file_size < trie.virtual_size());
+ } else {
+ GRN_DAT_THROW_IF(PARAM_ERROR, max_num_keys < trie.num_keys());
+ GRN_DAT_THROW_IF(PARAM_ERROR, max_num_keys < trie.max_key_id());
+ GRN_DAT_THROW_IF(PARAM_ERROR, max_num_keys > MAX_NUM_KEYS);
+ }
+
+ Trie new_trie;
+ new_trie.create_file(file_name, file_size, max_num_keys,
+ num_nodes_per_key, average_key_length);
+ new_trie.build_from_trie(trie);
+ new_trie.swap(this);
+}
+
+void Trie::repair(const Trie &trie, const char *file_name) {
+ Trie new_trie;
+ new_trie.create_file(file_name, trie.file_size(), trie.max_num_keys(),
+ trie.max_num_blocks(), trie.key_buf_size());
+ new_trie.repair_trie(trie);
+ new_trie.swap(this);
+}
+
+void Trie::open(const char *file_name) {
+ GRN_DAT_THROW_IF(PARAM_ERROR, file_name == NULL);
+
+ Trie new_trie;
+ new_trie.open_file(file_name);
+ new_trie.swap(this);
+}
+
+void Trie::close() {
+ Trie().swap(this);
+}
+
+void Trie::swap(Trie *trie) {
+ file_.swap(&trie->file_);
+ std::swap(header_, trie->header_);
+ nodes_.swap(&trie->nodes_);
+ blocks_.swap(&trie->blocks_);
+ entries_.swap(&trie->entries_);
+ key_buf_.swap(&trie->key_buf_);
+}
+
+void Trie::create_file(const char *file_name,
+ UInt64 file_size,
+ UInt32 max_num_keys,
+ double num_nodes_per_key,
+ double average_key_length) {
+ GRN_DAT_THROW_IF(PARAM_ERROR, (file_size == 0) && (max_num_keys == 0));
+ GRN_DAT_THROW_IF(PARAM_ERROR, (file_size != 0) && (max_num_keys != 0));
+ if (max_num_keys == 0) {
+ const UInt64 avail = file_size - sizeof(Header);
+ const double num_bytes_per_key = (sizeof(Node) * num_nodes_per_key)
+ + (1.0 * sizeof(Block) / BLOCK_SIZE * num_nodes_per_key)
+ + sizeof(Entry)
+ + sizeof(UInt32) + sizeof(UInt8) + average_key_length + 1.5;
+ if ((avail / num_bytes_per_key) > MAX_NUM_KEYS) {
+ max_num_keys = MAX_NUM_KEYS;
+ } else {
+ max_num_keys = (UInt32)(avail / num_bytes_per_key);
+ }
+ GRN_DAT_THROW_IF(PARAM_ERROR, max_num_keys == 0);
+ }
+
+ UInt32 max_num_blocks;
+ {
+ const double max_num_nodes = num_nodes_per_key * max_num_keys;
+ GRN_DAT_THROW_IF(PARAM_ERROR, (max_num_nodes - 1.0) >= MAX_NUM_NODES);
+ max_num_blocks = ((UInt32)max_num_nodes + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ GRN_DAT_THROW_IF(PARAM_ERROR, max_num_blocks == 0);
+ GRN_DAT_THROW_IF(PARAM_ERROR, max_num_blocks > MAX_NUM_BLOCKS);
+ }
+
+ UInt32 key_buf_size;
+ if (file_size == 0) {
+ const double total_key_length = average_key_length * max_num_keys;
+ GRN_DAT_THROW_IF(PARAM_ERROR,
+ (total_key_length - 1.0) >= MAX_TOTAL_KEY_LENGTH);
+
+ // The last term is the estimated number of bytes that will be used for
+ // 32-bit alignment.
+ const UInt64 total_num_bytes = (UInt64)(total_key_length)
+ + (UInt64)(sizeof(UInt32) + sizeof(UInt8)) * max_num_keys
+ + (UInt32)(max_num_keys * 1.5);
+ GRN_DAT_THROW_IF(PARAM_ERROR,
+ (total_num_bytes / sizeof(UInt32)) >= MAX_KEY_BUF_SIZE);
+ key_buf_size = (UInt32)(total_num_bytes / sizeof(UInt32));
+
+ file_size = sizeof(Header)
+ + (sizeof(Block) * max_num_blocks)
+ + (sizeof(Node) * BLOCK_SIZE * max_num_blocks)
+ + (sizeof(Entry) * max_num_keys)
+ + (sizeof(UInt32) * key_buf_size);
+ } else {
+ const UInt64 avail = file_size - sizeof(Header)
+ - (sizeof(Block) * max_num_blocks)
+ - (sizeof(Node) * BLOCK_SIZE * max_num_blocks)
+ - (sizeof(Entry) * max_num_keys);
+ GRN_DAT_THROW_IF(PARAM_ERROR, (avail / sizeof(UInt32)) > MAX_KEY_BUF_SIZE);
+ key_buf_size = (UInt32)(avail / sizeof(UInt32));
+ }
+
+ create_file(file_name, file_size, max_num_keys,
+ max_num_blocks, key_buf_size);
+}
+
+void Trie::create_file(const char *file_name,
+ UInt64 file_size,
+ UInt32 max_num_keys,
+ UInt32 max_num_blocks,
+ UInt32 key_buf_size) {
+ GRN_DAT_THROW_IF(PARAM_ERROR, file_size < (sizeof(Header)
+ + (sizeof(Block) * max_num_blocks)
+ + (sizeof(Node) * BLOCK_SIZE * max_num_blocks)
+ + (sizeof(Entry) * max_num_keys)
+ + (sizeof(UInt32) * key_buf_size)));
+
+ file_.create(file_name, file_size);
+
+ Header * const header = static_cast<Header *>(file_.ptr());
+ *header = Header();
+ header->set_file_size(file_size);
+ header->set_max_num_keys(max_num_keys);
+ header->set_max_num_blocks(max_num_blocks);
+ header->set_key_buf_size(key_buf_size);
+
+ map_address(file_.ptr());
+
+ reserve_node(ROOT_NODE_ID);
+ ith_node(INVALID_OFFSET).set_is_offset(true);
+}
+
+void Trie::open_file(const char *file_name) {
+ GRN_DAT_THROW_IF(PARAM_ERROR, file_name == NULL);
+
+ file_.open(file_name);
+ map_address(file_.ptr());
+ GRN_DAT_THROW_IF(FORMAT_ERROR, file_size() != file_.size());
+}
+
+void Trie::map_address(void *address) {
+ GRN_DAT_THROW_IF(PARAM_ERROR, address == NULL);
+
+ header_ = static_cast<Header *>(address);
+ nodes_.assign(header_ + 1, max_num_nodes());
+ blocks_.assign(nodes_.end(), max_num_blocks());
+ entries_.assign(reinterpret_cast<Entry *>(blocks_.end()) - 1,
+ max_num_keys() + 1);
+ key_buf_.assign(entries_.end(), key_buf_size());
+
+ GRN_DAT_THROW_IF(UNEXPECTED_ERROR, static_cast<void *>(key_buf_.end())
+ > static_cast<void *>(static_cast<char *>(address) + file_size()));
+}
+
+void Trie::build_from_trie(const Trie &trie) {
+ GRN_DAT_THROW_IF(SIZE_ERROR, max_num_keys() < trie.num_keys());
+ GRN_DAT_THROW_IF(SIZE_ERROR, max_num_keys() < trie.max_key_id());
+
+ header_->set_total_key_length(trie.total_key_length());
+ header_->set_num_keys(trie.num_keys());
+ header_->set_max_key_id(trie.max_key_id());
+ header_->set_next_key_id(trie.next_key_id());
+ for (UInt32 i = min_key_id(); i <= max_key_id(); ++i) {
+ ith_entry(i) = trie.ith_entry(i);
+ }
+ build_from_trie(trie, ROOT_NODE_ID, ROOT_NODE_ID);
+}
+
+void Trie::build_from_trie(const Trie &trie, UInt32 src, UInt32 dest) {
+ // Keys are sorted in lexicographic order.
+ if (trie.ith_node(src).is_linker()) {
+ const Key &key = trie.get_key(trie.ith_node(src).key_pos());
+ Key::create(key_buf_.ptr() + next_key_pos(),
+ key.id(), key.ptr(), key.length());
+ ith_node(dest).set_key_pos(next_key_pos());
+ ith_entry(key.id()).set_key_pos(next_key_pos());
+ header_->set_next_key_pos(
+ next_key_pos() + Key::estimate_size(key.length()));
+ return;
+ }
+
+ const UInt32 src_offset = trie.ith_node(src).offset();
+ UInt32 dest_offset;
+ {
+ UInt16 labels[MAX_LABEL + 1];
+ UInt32 num_labels = 0;
+
+ UInt32 label = trie.ith_node(src).child();
+ while (label != INVALID_LABEL) {
+ GRN_DAT_DEBUG_THROW_IF(label > MAX_LABEL);
+ const UInt32 child = src_offset ^ label;
+ if (trie.ith_node(child).is_linker() ||
+ (trie.ith_node(child).child() != INVALID_LABEL)) {
+ labels[num_labels++] = label;
+ }
+ label = trie.ith_node(child).sibling();
+ }
+ if (num_labels == 0) {
+ return;
+ }
+
+ dest_offset = find_offset(labels, num_labels);
+ for (UInt32 i = 0; i < num_labels; ++i) {
+ const UInt32 child = dest_offset ^ labels[i];
+ reserve_node(child);
+ ith_node(child).set_label(labels[i]);
+ if ((i + 1) < num_labels) {
+ ith_node(child).set_sibling(labels[i + 1]);
+ }
+ }
+
+ GRN_DAT_DEBUG_THROW_IF(ith_node(dest_offset).is_offset());
+ ith_node(dest_offset).set_is_offset(true);
+ ith_node(dest).set_offset(dest_offset);
+ ith_node(dest).set_child(labels[0]);
+ }
+
+ UInt32 label = ith_node(dest).child();
+ while (label != INVALID_LABEL) {
+ build_from_trie(trie, src_offset ^ label, dest_offset ^ label);
+ label = ith_node(dest_offset ^ label).sibling();
+ }
+}
+
+void Trie::repair_trie(const Trie &trie) {
+ Vector<UInt32> valid_ids;
+ header_->set_max_key_id(trie.max_key_id());
+ header_->set_next_key_id(trie.max_key_id() + 1);
+ UInt32 prev_invalid_key_id = INVALID_KEY_ID;
+ for (UInt32 i = min_key_id(); i <= max_key_id(); ++i) {
+ const Entry &entry = trie.ith_entry(i);
+ if (entry.is_valid()) {
+ valid_ids.push_back(i);
+ ith_entry(i) = entry;
+ const Key &key = trie.get_key(entry.key_pos());
+ Key::create(key_buf_.ptr() + next_key_pos(),
+ key.id(), key.ptr(), key.length());
+ ith_entry(i).set_key_pos(next_key_pos());
+ header_->set_next_key_pos(
+ next_key_pos() + Key::estimate_size(key.length()));
+ header_->set_total_key_length(
+ total_key_length() + key.length());
+ header_->set_num_keys(num_keys() + 1);
+ } else {
+ if (prev_invalid_key_id == INVALID_KEY_ID) {
+ header_->set_next_key_id(i);
+ } else {
+ ith_entry(prev_invalid_key_id).set_next(i);
+ }
+ prev_invalid_key_id = i;
+ }
+ }
+ if (prev_invalid_key_id != INVALID_KEY_ID) {
+ ith_entry(prev_invalid_key_id).set_next(max_key_id() + 1);
+ }
+ mkq_sort(valid_ids.begin(), valid_ids.end(), 0);
+ build_from_keys(valid_ids.begin(), valid_ids.end(), 0, ROOT_NODE_ID);
+}
+
+void Trie::build_from_keys(const UInt32 *begin, const UInt32 *end,
+ UInt32 depth, UInt32 node_id) {
+ if ((end - begin) == 1) {
+ ith_node(node_id).set_key_pos(ith_entry(*begin).key_pos());
+ return;
+ }
+
+ UInt32 offset;
+ {
+ UInt16 labels[MAX_LABEL + 2];
+ UInt32 num_labels = 0;
+
+ const UInt32 *it = begin;
+ if (ith_key(*it).length() == depth) {
+ labels[num_labels++] = TERMINAL_LABEL;
+ ++it;
+ }
+
+ labels[num_labels++] = (UInt8)ith_key(*it)[depth];
+ for (++it; it < end; ++it) {
+ const Key &key = ith_key(*it);
+ if ((UInt8)key[depth] != labels[num_labels - 1]) {
+ labels[num_labels++] = (UInt8)key[depth];
+ }
+ }
+ labels[num_labels] = INVALID_LABEL;
+
+ offset = find_offset(labels, num_labels);
+ ith_node(node_id).set_child(labels[0]);
+ for (UInt32 i = 0; i < num_labels; ++i) {
+ const UInt32 next = offset ^ labels[i];
+ reserve_node(next);
+ ith_node(next).set_label(labels[i]);
+ ith_node(next).set_sibling(labels[i + 1]);
+ }
+
+ if (offset >= num_nodes()) {
+ reserve_block(num_blocks());
+ }
+ ith_node(offset).set_is_offset(true);
+ ith_node(node_id).set_offset(offset);
+ }
+
+ if (ith_key(*begin).length() == depth) {
+ build_from_keys(begin, begin + 1, depth + 1, offset ^ TERMINAL_LABEL);
+ ++begin;
+ }
+
+ UInt16 label = ith_key(*begin)[depth];
+ for (const UInt32 *it = begin + 1; it < end; ++it) {
+ const Key &key = ith_key(*it);
+ if ((UInt8)key[depth] != label) {
+ build_from_keys(begin, it, depth + 1, offset ^ label);
+ label = (UInt8)key[depth];
+ begin = it;
+ }
+ }
+ build_from_keys(begin, end, depth + 1, offset ^ label);
+}
+
+void Trie::mkq_sort(UInt32 *l, UInt32 *r, UInt32 depth) {
+ while ((r - l) >= MKQ_SORT_THRESHOLD) {
+ UInt32 *pl = l;
+ UInt32 *pr = r;
+ UInt32 *pivot_l = l;
+ UInt32 *pivot_r = r;
+
+ const int pivot = get_median(*l, *(l + (r - l) / 2), *(r - 1), depth);
+ for ( ; ; ) {
+ while (pl < pr) {
+ const int label = get_label(*pl, depth);
+ if (label > pivot) {
+ break;
+ } else if (label == pivot) {
+ swap_ids(pl, pivot_l);
+ ++pivot_l;
+ }
+ ++pl;
+ }
+ while (pl < pr) {
+ const int label = get_label(*--pr, depth);
+ if (label < pivot) {
+ break;
+ } else if (label == pivot) {
+ swap_ids(pr, --pivot_r);
+ }
+ }
+ if (pl >= pr) {
+ break;
+ }
+ swap_ids(pl, pr);
+ ++pl;
+ }
+ while (pivot_l > l) {
+ swap_ids(--pivot_l, --pl);
+ }
+ while (pivot_r < r) {
+ swap_ids(pivot_r, pr);
+ ++pivot_r;
+ ++pr;
+ }
+
+ if (((pl - l) > (pr - pl)) || ((r - pr) > (pr - pl))) {
+ if ((pr - pl) > 1) {
+ mkq_sort(pl, pr, depth + 1);
+ }
+
+ if ((pl - l) < (r - pr)) {
+ if ((pl - l) > 1) {
+ mkq_sort(l, pl, depth);
+ }
+ l = pr;
+ } else {
+ if ((r - pr) > 1) {
+ mkq_sort(pr, r, depth);
+ }
+ r = pl;
+ }
+ } else {
+ if ((pl - l) > 1) {
+ mkq_sort(l, pl, depth);
+ }
+
+ if ((r - pr) > 1) {
+ mkq_sort(pr, r, depth);
+ }
+
+ l = pl, r = pr;
+ if ((pr - pl) > 1) {
+ ++depth;
+ }
+ }
+ }
+
+ if ((r - l) > 1) {
+ insertion_sort(l, r, depth);
+ }
+}
+
+void Trie::insertion_sort(UInt32 *l, UInt32 *r, UInt32 depth) {
+ for (UInt32 *i = l + 1; i < r; ++i) {
+ for (UInt32 *j = i; j > l; --j) {
+ if (less_than(*(j - 1), *j, depth)) {
+ break;
+ }
+ swap_ids(j - 1, j);
+ }
+ }
+}
+
+int Trie::get_median(UInt32 a, UInt32 b, UInt32 c, UInt32 depth) const {
+ const int x = get_label(a, depth);
+ const int y = get_label(b, depth);
+ const int z = get_label(c, depth);
+ if (x < y) {
+ if (y < z) {
+ return y;
+ } else if (x < z) {
+ return z;
+ }
+ return x;
+ } else if (x < z) {
+ return x;
+ } else if (y < z) {
+ return z;
+ }
+ return y;
+}
+
+int Trie::get_label(UInt32 key_id, UInt32 depth) const {
+ const Key &key = ith_key(key_id);
+ if (depth == key.length()) {
+ return -1;
+ } else {
+ return (UInt8)key[depth];
+ }
+}
+
+bool Trie::less_than(UInt32 lhs, UInt32 rhs, UInt32 depth) const {
+ const Key &lhs_key = ith_key(lhs);
+ const Key &rhs_key = ith_key(rhs);
+ const UInt32 length = (lhs_key.length() < rhs_key.length()) ?
+ lhs_key.length() : rhs_key.length();
+ for (UInt32 i = depth; i < length; ++i) {
+ if (lhs_key[i] != rhs_key[i]) {
+ return (UInt8)lhs_key[i] < (UInt8)rhs_key[i];
+ }
+ }
+ return lhs_key.length() < rhs_key.length();
+}
+
+void Trie::swap_ids(UInt32 *lhs, UInt32 *rhs) {
+ UInt32 temp = *lhs;
+ *lhs = *rhs;
+ *rhs = temp;
+}
+
+bool Trie::search_key(const UInt8 *ptr, UInt32 length, UInt32 *key_pos) const {
+ GRN_DAT_DEBUG_THROW_IF((ptr == NULL) && (length != 0));
+
+ UInt32 node_id = ROOT_NODE_ID;
+ UInt32 query_pos = 0;
+ if (!search_linker(ptr, length, node_id, query_pos)) {
+ return false;
+ }
+
+ const Base base = ith_node(node_id).base();
+ if (!base.is_linker()) {
+ return false;
+ }
+
+ if (get_key(base.key_pos()).equals_to(ptr, length, query_pos)) {
+ if (key_pos != NULL) {
+ *key_pos = base.key_pos();
+ }
+ return true;
+ }
+ return false;
+}
+
+bool Trie::search_linker(const UInt8 *ptr, UInt32 length,
+ UInt32 &node_id, UInt32 &query_pos) const {
+ GRN_DAT_DEBUG_THROW_IF((ptr == NULL) && (length != 0));
+
+ for ( ; query_pos < length; ++query_pos) {
+ const Base base = ith_node(node_id).base();
+ if (base.is_linker()) {
+ return true;
+ }
+
+ const UInt32 next = base.offset() ^ ptr[query_pos];
+ if (ith_node(next).label() != ptr[query_pos]) {
+ return false;
+ }
+ node_id = next;
+ }
+
+ const Base base = ith_node(node_id).base();
+ if (base.is_linker()) {
+ return true;
+ }
+
+ const UInt32 next = base.offset() ^ TERMINAL_LABEL;
+ if (ith_node(next).label() != TERMINAL_LABEL) {
+ return false;
+ }
+ node_id = next;
+ return ith_node(next).is_linker();
+}
+
+bool Trie::lcp_search_key(const UInt8 *ptr, UInt32 length,
+ UInt32 *key_pos) const {
+ GRN_DAT_DEBUG_THROW_IF((ptr == NULL) && (length != 0));
+
+ bool found = false;
+ UInt32 node_id = ROOT_NODE_ID;
+ UInt32 query_pos = 0;
+
+ for ( ; query_pos < length; ++query_pos) {
+ const Base base = ith_node(node_id).base();
+ if (base.is_linker()) {
+ const Key &key = get_key(base.key_pos());
+ if ((key.length() <= length) &&
+ key.equals_to(ptr, key.length(), query_pos)) {
+ if (key_pos != NULL) {
+ *key_pos = base.key_pos();
+ }
+ found = true;
+ }
+ return found;
+ }
+
+ if (ith_node(node_id).child() == TERMINAL_LABEL) {
+ const Base linker_base =
+ ith_node(base.offset() ^ TERMINAL_LABEL).base();
+ if (linker_base.is_linker()) {
+ if (key_pos != NULL) {
+ *key_pos = linker_base.key_pos();
+ }
+ found = true;
+ }
+ }
+
+ node_id = base.offset() ^ ptr[query_pos];
+ if (ith_node(node_id).label() != ptr[query_pos]) {
+ return found;
+ }
+ }
+
+ const Base base = ith_node(node_id).base();
+ if (base.is_linker()) {
+ const Key &key = get_key(base.key_pos());
+ if (key.length() <= length) {
+ if (key_pos != NULL) {
+ *key_pos = base.key_pos();
+ }
+ found = true;
+ }
+ } else if (ith_node(node_id).child() == TERMINAL_LABEL) {
+ const Base linker_base = ith_node(base.offset() ^ TERMINAL_LABEL).base();
+ if (linker_base.is_linker()) {
+ if (key_pos != NULL) {
+ *key_pos = linker_base.key_pos();
+ }
+ found = true;
+ }
+ }
+ return found;
+}
+
+bool Trie::remove_key(const UInt8 *ptr, UInt32 length) {
+ GRN_DAT_THROW_IF(STATUS_ERROR, (status_flags() & CHANGING_MASK) != 0);
+ StatusFlagManager status_flag_manager(header_, REMOVING_FLAG);
+
+ GRN_DAT_DEBUG_THROW_IF((ptr == NULL) && (length != 0));
+
+ UInt32 node_id = ROOT_NODE_ID;
+ UInt32 query_pos = 0;
+ if (!search_linker(ptr, length, node_id, query_pos)) {
+ return false;
+ }
+
+ const UInt32 key_pos = ith_node(node_id).key_pos();
+ if (!get_key(key_pos).equals_to(ptr, length, query_pos)) {
+ return false;
+ }
+
+ const UInt32 key_id = get_key(key_pos).id();
+ ith_node(node_id).set_offset(INVALID_OFFSET);
+ ith_entry(key_id).set_next(next_key_id());
+
+ header_->set_next_key_id(key_id);
+ header_->set_total_key_length(total_key_length() - length);
+ header_->set_num_keys(num_keys() - 1);
+ return true;
+}
+
+bool Trie::insert_key(const UInt8 *ptr, UInt32 length, UInt32 *key_pos) {
+ GRN_DAT_THROW_IF(STATUS_ERROR, (status_flags() & CHANGING_MASK) != 0);
+ StatusFlagManager status_flag_manager(header_, INSERTING_FLAG);
+
+ GRN_DAT_DEBUG_THROW_IF((ptr == NULL) && (length != 0));
+
+ UInt32 node_id = ROOT_NODE_ID;
+ UInt32 query_pos = 0;
+
+ search_linker(ptr, length, node_id, query_pos);
+ if (!insert_linker(ptr, length, node_id, query_pos)) {
+ if (key_pos != NULL) {
+ *key_pos = ith_node(node_id).key_pos();
+ }
+ return false;
+ }
+
+ const UInt32 new_key_id = next_key_id();
+ const UInt32 new_key_pos = append_key(ptr, length, new_key_id);
+
+ header_->set_total_key_length(total_key_length() + length);
+ header_->set_num_keys(num_keys() + 1);
+ if (new_key_id > max_key_id()) {
+ header_->set_max_key_id(new_key_id);
+ header_->set_next_key_id(new_key_id + 1);
+ } else {
+ header_->set_next_key_id(ith_entry(new_key_id).next());
+ }
+
+ ith_entry(new_key_id).set_key_pos(new_key_pos);
+ ith_node(node_id).set_key_pos(new_key_pos);
+ if (key_pos != NULL) {
+ *key_pos = new_key_pos;
+ }
+ return true;
+}
+
+bool Trie::insert_linker(const UInt8 *ptr, UInt32 length,
+ UInt32 &node_id, UInt32 query_pos) {
+ if (ith_node(node_id).is_linker()) {
+ const Key &key = get_key(ith_node(node_id).key_pos());
+ UInt32 i = query_pos;
+ while ((i < length) && (i < key.length())) {
+ if (ptr[i] != key[i]) {
+ break;
+ }
+ ++i;
+ }
+ if ((i == length) && (i == key.length())) {
+ return false;
+ }
+ GRN_DAT_THROW_IF(SIZE_ERROR, num_keys() >= max_num_keys());
+ GRN_DAT_DEBUG_THROW_IF(next_key_id() > max_num_keys());
+
+ for (UInt32 j = query_pos; j < i; ++j) {
+ node_id = insert_node(node_id, ptr[j]);
+ }
+ node_id = separate(ptr, length, node_id, i);
+ return true;
+ } else if (ith_node(node_id).label() == TERMINAL_LABEL) {
+ return true;
+ } else {
+ GRN_DAT_THROW_IF(SIZE_ERROR, num_keys() >= max_num_keys());
+ const UInt16 label = (query_pos < length) ?
+ (UInt16)ptr[query_pos] : (UInt16)TERMINAL_LABEL;
+ const Base base = ith_node(node_id).base();
+ if ((base.offset() == INVALID_OFFSET) ||
+ !ith_node(base.offset() ^ label).is_phantom()) {
+ resolve(node_id, label);
+ }
+ node_id = insert_node(node_id, label);
+ return true;
+ }
+}
+
+bool Trie::update_key(const Key &key, const UInt8 *ptr, UInt32 length,
+ UInt32 *key_pos) {
+ GRN_DAT_THROW_IF(STATUS_ERROR, (status_flags() & CHANGING_MASK) != 0);
+ StatusFlagManager status_flag_manager(header_, UPDATING_FLAG);
+
+ GRN_DAT_DEBUG_THROW_IF((ptr == NULL) && (length != 0));
+
+ if (!key.is_valid()) {
+ return false;
+ }
+
+ UInt32 node_id = ROOT_NODE_ID;
+ UInt32 query_pos = 0;
+
+ search_linker(ptr, length, node_id, query_pos);
+ if (!insert_linker(ptr, length, node_id, query_pos)) {
+ if (key_pos != NULL) {
+ *key_pos = ith_node(node_id).key_pos();
+ }
+ return false;
+ }
+
+ const UInt32 new_key_pos = append_key(ptr, length, key.id());
+ header_->set_total_key_length(total_key_length() + length - key.length());
+ ith_entry(key.id()).set_key_pos(new_key_pos);
+ ith_node(node_id).set_key_pos(new_key_pos);
+ if (key_pos != NULL) {
+ *key_pos = new_key_pos;
+ }
+
+ node_id = ROOT_NODE_ID;
+ query_pos = 0;
+ GRN_DAT_THROW_IF(UNEXPECTED_ERROR,
+ !search_linker(static_cast<const UInt8 *>(key.ptr()), key.length(),
+ node_id, query_pos));
+ ith_node(node_id).set_offset(INVALID_OFFSET);
+ return true;
+}
+
+UInt32 Trie::insert_node(UInt32 node_id, UInt16 label) {
+ GRN_DAT_DEBUG_THROW_IF(node_id >= num_nodes());
+ GRN_DAT_DEBUG_THROW_IF(label > MAX_LABEL);
+
+ const Base base = ith_node(node_id).base();
+ UInt32 offset;
+ if (base.is_linker() || (base.offset() == INVALID_OFFSET)) {
+ offset = find_offset(&label, 1);
+ } else {
+ offset = base.offset();
+ }
+
+ const UInt32 next = offset ^ label;
+ reserve_node(next);
+
+ ith_node(next).set_label(label);
+ if (base.is_linker()) {
+ GRN_DAT_DEBUG_THROW_IF(ith_node(offset).is_offset());
+ ith_node(offset).set_is_offset(true);
+ ith_node(next).set_key_pos(base.key_pos());
+ } else if (base.offset() == INVALID_OFFSET) {
+ GRN_DAT_DEBUG_THROW_IF(ith_node(offset).is_offset());
+ ith_node(offset).set_is_offset(true);
+ } else {
+ GRN_DAT_DEBUG_THROW_IF(!ith_node(offset).is_offset());
+ }
+ ith_node(node_id).set_offset(offset);
+
+ const UInt32 child_label = ith_node(node_id).child();
+ GRN_DAT_DEBUG_THROW_IF(child_label == label);
+ if (child_label == INVALID_LABEL) {
+ ith_node(node_id).set_child(label);
+ } else if ((label == TERMINAL_LABEL) ||
+ ((child_label != TERMINAL_LABEL) && (label < child_label))) {
+ GRN_DAT_DEBUG_THROW_IF(ith_node(offset ^ child_label).is_phantom());
+ GRN_DAT_DEBUG_THROW_IF(ith_node(offset ^ child_label).label() != child_label);
+ ith_node(next).set_sibling(child_label);
+ ith_node(node_id).set_child(label);
+ } else {
+ UInt32 prev = offset ^ child_label;
+ GRN_DAT_DEBUG_THROW_IF(ith_node(prev).label() != child_label);
+ UInt32 sibling_label = ith_node(prev).sibling();
+ while (label > sibling_label) {
+ prev = offset ^ sibling_label;
+ GRN_DAT_DEBUG_THROW_IF(ith_node(prev).label() != sibling_label);
+ sibling_label = ith_node(prev).sibling();
+ }
+ GRN_DAT_DEBUG_THROW_IF(label == sibling_label);
+ ith_node(next).set_sibling(ith_node(prev).sibling());
+ ith_node(prev).set_sibling(label);
+ }
+ return next;
+}
+
+UInt32 Trie::append_key(const UInt8 *ptr, UInt32 length, UInt32 key_id) {
+ GRN_DAT_THROW_IF(SIZE_ERROR, key_id > max_num_keys());
+
+ const UInt32 key_pos = next_key_pos();
+ const UInt32 key_size = Key::estimate_size(length);
+
+ GRN_DAT_THROW_IF(SIZE_ERROR, key_size > (key_buf_size() - key_pos));
+ Key::create(key_buf_.ptr() + key_pos, key_id, ptr, length);
+
+ header_->set_next_key_pos(key_pos + key_size);
+ return key_pos;
+}
+
+UInt32 Trie::separate(const UInt8 *ptr, UInt32 length,
+ UInt32 node_id, UInt32 i) {
+ GRN_DAT_DEBUG_THROW_IF(node_id >= num_nodes());
+ GRN_DAT_DEBUG_THROW_IF(!ith_node(node_id).is_linker());
+ GRN_DAT_DEBUG_THROW_IF(i > length);
+
+ const UInt32 key_pos = ith_node(node_id).key_pos();
+ const Key &key = get_key(key_pos);
+
+ UInt16 labels[2];
+ labels[0] = (i < key.length()) ? (UInt16)key[i] : (UInt16)TERMINAL_LABEL;
+ labels[1] = (i < length) ? (UInt16)ptr[i] : (UInt16)TERMINAL_LABEL;
+ GRN_DAT_DEBUG_THROW_IF(labels[0] == labels[1]);
+
+ const UInt32 offset = find_offset(labels, 2);
+
+ UInt32 next = offset ^ labels[0];
+ reserve_node(next);
+ GRN_DAT_DEBUG_THROW_IF(ith_node(offset).is_offset());
+
+ ith_node(next).set_label(labels[0]);
+ ith_node(next).set_key_pos(key_pos);
+
+ next = offset ^ labels[1];
+ reserve_node(next);
+
+ ith_node(next).set_label(labels[1]);
+
+ ith_node(offset).set_is_offset(true);
+ ith_node(node_id).set_offset(offset);
+
+ if ((labels[0] == TERMINAL_LABEL) ||
+ ((labels[1] != TERMINAL_LABEL) && (labels[0] < labels[1]))) {
+ ith_node(node_id).set_child(labels[0]);
+ ith_node(offset ^ labels[0]).set_sibling(labels[1]);
+ } else {
+ ith_node(node_id).set_child(labels[1]);
+ ith_node(offset ^ labels[1]).set_sibling(labels[0]);
+ }
+ return next;
+}
+
+void Trie::resolve(UInt32 node_id, UInt16 label) {
+ GRN_DAT_DEBUG_THROW_IF(node_id >= num_nodes());
+ GRN_DAT_DEBUG_THROW_IF(ith_node(node_id).is_linker());
+ GRN_DAT_DEBUG_THROW_IF(label > MAX_LABEL);
+
+ UInt32 offset = ith_node(node_id).offset();
+ if (offset != INVALID_OFFSET) {
+ UInt16 labels[MAX_LABEL + 1];
+ UInt32 num_labels = 0;
+
+ UInt32 next_label = ith_node(node_id).child();
+ GRN_DAT_DEBUG_THROW_IF(next_label == INVALID_LABEL);
+ while (next_label != INVALID_LABEL) {
+ GRN_DAT_DEBUG_THROW_IF(next_label > MAX_LABEL);
+ labels[num_labels++] = next_label;
+ next_label = ith_node(offset ^ next_label).sibling();
+ }
+ GRN_DAT_DEBUG_THROW_IF(num_labels == 0);
+
+ labels[num_labels] = label;
+ offset = find_offset(labels, num_labels + 1);
+ migrate_nodes(node_id, offset, labels, num_labels);
+ } else {
+ offset = find_offset(&label, 1);
+ if (offset >= num_nodes()) {
+ GRN_DAT_DEBUG_THROW_IF((offset / BLOCK_SIZE) != num_blocks());
+ reserve_block(num_blocks());
+ }
+ ith_node(offset).set_is_offset(true);
+ ith_node(node_id).set_offset(offset);
+ }
+}
+
+void Trie::migrate_nodes(UInt32 node_id, UInt32 dest_offset,
+ const UInt16 *labels, UInt32 num_labels) {
+ GRN_DAT_DEBUG_THROW_IF(node_id >= num_nodes());
+ GRN_DAT_DEBUG_THROW_IF(ith_node(node_id).is_linker());
+ GRN_DAT_DEBUG_THROW_IF(labels == NULL);
+ GRN_DAT_DEBUG_THROW_IF(num_labels == 0);
+ GRN_DAT_DEBUG_THROW_IF(num_labels > (MAX_LABEL + 1));
+
+ const UInt32 src_offset = ith_node(node_id).offset();
+ GRN_DAT_DEBUG_THROW_IF(src_offset == INVALID_OFFSET);
+ GRN_DAT_DEBUG_THROW_IF(!ith_node(src_offset).is_offset());
+
+ for (UInt32 i = 0; i < num_labels; ++i) {
+ const UInt32 src_node_id = src_offset ^ labels[i];
+ const UInt32 dest_node_id = dest_offset ^ labels[i];
+ GRN_DAT_DEBUG_THROW_IF(ith_node(src_node_id).is_phantom());
+ GRN_DAT_DEBUG_THROW_IF(ith_node(src_node_id).label() != labels[i]);
+
+ reserve_node(dest_node_id);
+ ith_node(dest_node_id).set_except_is_offset(
+ ith_node(src_node_id).except_is_offset());
+ ith_node(dest_node_id).set_base(ith_node(src_node_id).base());
+ }
+ header_->set_num_zombies(num_zombies() + num_labels);
+
+ GRN_DAT_DEBUG_THROW_IF(ith_node(dest_offset).is_offset());
+ ith_node(dest_offset).set_is_offset(true);
+ ith_node(node_id).set_offset(dest_offset);
+}
+
+UInt32 Trie::find_offset(const UInt16 *labels, UInt32 num_labels) {
+ GRN_DAT_DEBUG_THROW_IF(labels == NULL);
+ GRN_DAT_DEBUG_THROW_IF(num_labels == 0);
+ GRN_DAT_DEBUG_THROW_IF(num_labels > (MAX_LABEL + 1));
+
+ // Blocks are tested in descending order of level. Basically, a lower level
+ // block contains more phantom nodes.
+ UInt32 level = 1;
+ while (num_labels >= (1U << level)) {
+ ++level;
+ }
+ level = (level < MAX_BLOCK_LEVEL) ? (MAX_BLOCK_LEVEL - level) : 0;
+
+ UInt32 block_count = 0;
+ do {
+ UInt32 leader = header_->ith_leader(level);
+ if (leader == INVALID_LEADER) {
+ // This level has no blocks and it is thus skipped.
+ continue;
+ }
+
+ UInt32 block_id = leader;
+ do {
+ const Block &block = ith_block(block_id);
+ GRN_DAT_DEBUG_THROW_IF(block.level() != level);
+
+ const UInt32 first = (block_id * BLOCK_SIZE) | block.first_phantom();
+ UInt32 node_id = first;
+ do {
+ GRN_DAT_DEBUG_THROW_IF(!ith_node(node_id).is_phantom());
+ const UInt32 offset = node_id ^ labels[0];
+ if (!ith_node(offset).is_offset()) {
+ UInt32 i = 1;
+ for ( ; i < num_labels; ++i) {
+ if (!ith_node(offset ^ labels[i]).is_phantom()) {
+ break;
+ }
+ }
+ if (i >= num_labels) {
+ return offset;
+ }
+ }
+ node_id = (block_id * BLOCK_SIZE) | ith_node(node_id).next();
+ } while (node_id != first);
+
+ const UInt32 prev = block_id;
+ const UInt32 next = block.next();
+ block_id = next;
+ ith_block(prev).set_failure_count(ith_block(prev).failure_count() + 1);
+
+ // The level of a block is updated when this function fails many times,
+ // actually MAX_FAILURE_COUNT times, in that block.
+ if (ith_block(prev).failure_count() == MAX_FAILURE_COUNT) {
+ update_block_level(prev, level + 1);
+ if (next == leader) {
+ break;
+ } else {
+ // Note that the leader might be updated in the level update.
+ leader = header_->ith_leader(level);
+ continue;
+ }
+ }
+ } while ((++block_count < MAX_BLOCK_COUNT) && (block_id != leader));
+ } while ((block_count < MAX_BLOCK_COUNT) && (level-- != 0));
+
+ return num_nodes() ^ labels[0];
+}
+
+void Trie::reserve_node(UInt32 node_id) {
+ GRN_DAT_DEBUG_THROW_IF(node_id > num_nodes());
+ if (node_id >= num_nodes()) {
+ reserve_block(node_id / BLOCK_SIZE);
+ }
+
+ Node &node = ith_node(node_id);
+ GRN_DAT_DEBUG_THROW_IF(!node.is_phantom());
+
+ const UInt32 block_id = node_id / BLOCK_SIZE;
+ Block &block = ith_block(block_id);
+ GRN_DAT_DEBUG_THROW_IF(block.num_phantoms() == 0);
+
+ const UInt32 next = (block_id * BLOCK_SIZE) | node.next();
+ const UInt32 prev = (block_id * BLOCK_SIZE) | node.prev();
+ GRN_DAT_DEBUG_THROW_IF(next >= num_nodes());
+ GRN_DAT_DEBUG_THROW_IF(prev >= num_nodes());
+
+ if ((node_id & BLOCK_MASK) == block.first_phantom()) {
+ // The first phantom node is removed from the block and the second phantom
+ // node comes first.
+ block.set_first_phantom(next & BLOCK_MASK);
+ }
+
+ ith_node(next).set_prev(prev & BLOCK_MASK);
+ ith_node(prev).set_next(next & BLOCK_MASK);
+
+ if (block.level() != MAX_BLOCK_LEVEL) {
+ const UInt32 threshold = 1U << ((MAX_BLOCK_LEVEL - block.level() - 1) * 2);
+ if (block.num_phantoms() == threshold) {
+ update_block_level(block_id, block.level() + 1);
+ }
+ }
+ block.set_num_phantoms(block.num_phantoms() - 1);
+
+ node.set_is_phantom(false);
+
+ GRN_DAT_DEBUG_THROW_IF(node.offset() != INVALID_OFFSET);
+ GRN_DAT_DEBUG_THROW_IF(node.label() != INVALID_LABEL);
+
+ header_->set_num_phantoms(num_phantoms() - 1);
+}
+
+void Trie::reserve_block(UInt32 block_id) {
+ GRN_DAT_DEBUG_THROW_IF(block_id != num_blocks());
+ GRN_DAT_THROW_IF(SIZE_ERROR, block_id >= max_num_blocks());
+
+ header_->set_num_blocks(block_id + 1);
+ ith_block(block_id).set_failure_count(0);
+ ith_block(block_id).set_first_phantom(0);
+ ith_block(block_id).set_num_phantoms(BLOCK_SIZE);
+
+ const UInt32 begin = block_id * BLOCK_SIZE;
+ const UInt32 end = begin + BLOCK_SIZE;
+ GRN_DAT_DEBUG_THROW_IF(end != num_nodes());
+
+ Base base;
+ base.set_offset(INVALID_OFFSET);
+
+ Check check;
+ check.set_is_phantom(true);
+
+ for (UInt32 i = begin; i < end; ++i) {
+ check.set_prev((i - 1) & BLOCK_MASK);
+ check.set_next((i + 1) & BLOCK_MASK);
+ ith_node(i).set_base(base);
+ ith_node(i).set_check(check);
+ }
+
+ // The level of the new block is 0.
+ set_block_level(block_id, 0);
+ header_->set_num_phantoms(num_phantoms() + BLOCK_SIZE);
+}
+
+void Trie::update_block_level(UInt32 block_id, UInt32 level) {
+ GRN_DAT_DEBUG_THROW_IF(block_id >= num_blocks());
+ GRN_DAT_DEBUG_THROW_IF(level > MAX_BLOCK_LEVEL);
+
+ unset_block_level(block_id);
+ set_block_level(block_id, level);
+}
+
+void Trie::set_block_level(UInt32 block_id, UInt32 level) {
+ GRN_DAT_DEBUG_THROW_IF(block_id >= num_blocks());
+ GRN_DAT_DEBUG_THROW_IF(level > MAX_BLOCK_LEVEL);
+
+ const UInt32 leader = header_->ith_leader(level);
+ if (leader == INVALID_LEADER) {
+ // The new block becomes the only one block of the linked list.
+ ith_block(block_id).set_next(block_id);
+ ith_block(block_id).set_prev(block_id);
+ header_->set_ith_leader(level, block_id);
+ } else {
+ // The new block is added to the end of the list.
+ const UInt32 next = leader;
+ const UInt32 prev = ith_block(leader).prev();
+ GRN_DAT_DEBUG_THROW_IF(next >= num_blocks());
+ GRN_DAT_DEBUG_THROW_IF(prev >= num_blocks());
+ ith_block(block_id).set_next(next);
+ ith_block(block_id).set_prev(prev);
+ ith_block(next).set_prev(block_id);
+ ith_block(prev).set_next(block_id);
+ }
+ ith_block(block_id).set_level(level);
+ ith_block(block_id).set_failure_count(0);
+}
+
+void Trie::unset_block_level(UInt32 block_id) {
+ GRN_DAT_DEBUG_THROW_IF(block_id >= num_blocks());
+
+ const UInt32 level = ith_block(block_id).level();
+ GRN_DAT_DEBUG_THROW_IF(level > MAX_BLOCK_LEVEL);
+
+ const UInt32 leader = header_->ith_leader(level);
+ GRN_DAT_DEBUG_THROW_IF(leader == INVALID_LEADER);
+
+ const UInt32 next = ith_block(block_id).next();
+ const UInt32 prev = ith_block(block_id).prev();
+ GRN_DAT_DEBUG_THROW_IF(next >= num_blocks());
+ GRN_DAT_DEBUG_THROW_IF(prev >= num_blocks());
+
+ if (next == block_id) {
+ // The linked list becomes empty.
+ header_->set_ith_leader(level, INVALID_LEADER);
+ } else {
+ ith_block(next).set_prev(prev);
+ ith_block(prev).set_next(next);
+ if (block_id == leader) {
+ // The second block becomes the leader of the linked list.
+ header_->set_ith_leader(level, next);
+ }
+ }
+}
+
+} // namespace dat
+} // namespace grn
diff --git a/storage/mroonga/vendor/groonga/lib/dat/trie.hpp b/storage/mroonga/vendor/groonga/lib/dat/trie.hpp
new file mode 100644
index 00000000000..6bd307bb70e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/trie.hpp
@@ -0,0 +1,285 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_TRIE_HPP_
+#define GRN_DAT_TRIE_HPP_
+
+#include "array.hpp"
+#include "header.hpp"
+#include "node.hpp"
+#include "block.hpp"
+#include "entry.hpp"
+#include "key.hpp"
+#include "file.hpp"
+
+namespace grn {
+namespace dat {
+
+class GRN_DAT_API Trie {
+ public:
+ Trie();
+ ~Trie();
+
+ void create(const char *file_name = NULL,
+ UInt64 file_size = 0,
+ UInt32 max_num_keys = 0,
+ double num_nodes_per_key = 0.0,
+ double average_key_length = 0.0);
+
+ void create(const Trie &trie,
+ const char *file_name = NULL,
+ UInt64 file_size = 0,
+ UInt32 max_num_keys = 0,
+ double num_nodes_per_key = 0.0,
+ double average_key_length = 0.0);
+
+ void repair(const Trie &trie, const char *file_name = NULL);
+
+ void open(const char *file_name);
+ void close();
+
+ void swap(Trie *trie);
+
+ // Users can access a key by its position or ID.
+ const Key &get_key(UInt32 key_pos) const {
+ GRN_DAT_DEBUG_THROW_IF(key_pos >= next_key_pos());
+ return *reinterpret_cast<const Key *>(key_buf_.ptr() + key_pos);
+ }
+ // If a specified ID is invalid, e.g. the key is already deleted, this
+ // function returns a reference to an invalid key object whose id() returns
+ // INVALID_KEY_ID.
+ const Key &ith_key(UInt32 key_id) const {
+ if ((key_id >= min_key_id()) && (key_id <= max_key_id()) &&
+ ith_entry(key_id).is_valid()) {
+ return get_key(ith_entry(key_id).key_pos());
+ }
+ return Key::invalid_key();
+ }
+
+ bool search(const void *ptr, UInt32 length, UInt32 *key_pos = NULL) const {
+ return search_key(static_cast<const UInt8 *>(ptr), length, key_pos);
+ }
+ // Longest prefix match search.
+ bool lcp_search(const void *ptr, UInt32 length,
+ UInt32 *key_pos = NULL) const {
+ return lcp_search_key(static_cast<const UInt8 *>(ptr), length, key_pos);
+ }
+
+ bool remove(UInt32 key_id) {
+ const Key &key = ith_key(key_id);
+ if (key.is_valid()) {
+ return remove(key.ptr(), key.length());
+ }
+ return false;
+ }
+ bool remove(const void *ptr, UInt32 length) {
+ return remove_key(static_cast<const UInt8 *>(ptr), length);
+ }
+
+ bool insert(const void *ptr, UInt32 length, UInt32 *key_pos = NULL) {
+ return insert_key(static_cast<const UInt8 *>(ptr), length, key_pos);
+ }
+
+ bool update(UInt32 key_id, const void *ptr, UInt32 length,
+ UInt32 *key_pos = NULL) {
+ return update_key(ith_key(key_id), static_cast<const UInt8 *>(ptr),
+ length, key_pos);
+ }
+ bool update(const void *src_ptr, UInt32 src_length,
+ const void *dest_ptr, UInt32 dest_length,
+ UInt32 *key_pos = NULL) {
+ UInt32 src_key_pos;
+ if (!search(src_ptr, src_length, &src_key_pos)) {
+ return false;
+ }
+ const Key &src_key = get_key(src_key_pos);
+ return update_key(src_key, static_cast<const UInt8 *>(dest_ptr),
+ dest_length, key_pos);
+ }
+
+ const Node &ith_node(UInt32 i) const {
+ GRN_DAT_DEBUG_THROW_IF(i >= num_nodes());
+ return nodes_[i];
+ }
+ const Block &ith_block(UInt32 i) const {
+ GRN_DAT_DEBUG_THROW_IF(i >= num_blocks());
+ return blocks_[i];
+ }
+ const Entry &ith_entry(UInt32 i) const {
+ GRN_DAT_DEBUG_THROW_IF(i < min_key_id());
+ GRN_DAT_DEBUG_THROW_IF(i > max_key_id());
+ return entries_[i];
+ }
+
+ const Header &header() const {
+ return *header_;
+ }
+
+ UInt64 file_size() const {
+ return header_->file_size();
+ }
+ UInt64 virtual_size() const {
+ return sizeof(Header)
+ + (sizeof(Entry) * num_keys())
+ + (sizeof(Block) * num_blocks())
+ + (sizeof(Node) * num_nodes())
+ + total_key_length();
+ }
+ UInt32 total_key_length() const {
+ return header_->total_key_length();
+ }
+ UInt32 num_keys() const {
+ return header_->num_keys();
+ }
+ UInt32 min_key_id() const {
+ return header_->min_key_id();
+ }
+ UInt32 next_key_id() const {
+ return header_->next_key_id();
+ }
+ UInt32 max_key_id() const {
+ return header_->max_key_id();
+ }
+ UInt32 max_num_keys() const {
+ return header_->max_num_keys();
+ }
+ UInt32 num_nodes() const {
+ return header_->num_nodes();
+ }
+ UInt32 num_phantoms() const {
+ return header_->num_phantoms();
+ }
+ UInt32 num_zombies() const {
+ return header_->num_zombies();
+ }
+ UInt32 max_num_nodes() const {
+ return header_->max_num_nodes();
+ }
+ UInt32 num_blocks() const {
+ return header_->num_blocks();
+ }
+ UInt32 max_num_blocks() const {
+ return header_->max_num_blocks();
+ }
+ UInt32 next_key_pos() const {
+ return header_->next_key_pos();
+ }
+ UInt32 key_buf_size() const {
+ return header_->key_buf_size();
+ }
+ UInt32 status_flags() const {
+ return header_->status_flags();
+ }
+
+ void clear_status_flags() {
+ header_->set_status_flags(status_flags() & ~CHANGING_MASK);
+ }
+
+ private:
+ File file_;
+ Header *header_;
+ Array<Node> nodes_;
+ Array<Block> blocks_;
+ Array<Entry> entries_;
+ Array<UInt32> key_buf_;
+
+ void create_file(const char *file_name,
+ UInt64 file_size,
+ UInt32 max_num_keys,
+ double num_nodes_per_key,
+ double average_key_length);
+ void create_file(const char *file_name,
+ UInt64 file_size,
+ UInt32 max_num_keys,
+ UInt32 max_num_blocks,
+ UInt32 key_buf_size);
+
+ void open_file(const char *file_name);
+
+ void map_address(void *address);
+
+ void build_from_trie(const Trie &trie);
+ void build_from_trie(const Trie &trie, UInt32 src, UInt32 dest);
+
+ void repair_trie(const Trie &trie);
+ void build_from_keys(const UInt32 *begin, const UInt32 *end,
+ UInt32 depth, UInt32 node_id);
+
+ void mkq_sort(UInt32 *l, UInt32 *r, UInt32 depth);
+ void insertion_sort(UInt32 *l, UInt32 *r, UInt32 depth);
+
+ inline int get_label(UInt32 key_id, UInt32 depth) const;
+ inline int get_median(UInt32 a, UInt32 b, UInt32 c, UInt32 depth) const;
+ inline bool less_than(UInt32 lhs, UInt32 rhs, UInt32 depth) const;
+ inline static void swap_ids(UInt32 *lhs, UInt32 *rhs);
+
+ bool search_key(const UInt8 *ptr, UInt32 length, UInt32 *key_pos) const;
+ bool search_linker(const UInt8 *ptr, UInt32 length,
+ UInt32 &node_id, UInt32 &query_pos) const;
+
+ bool lcp_search_key(const UInt8 *ptr, UInt32 length, UInt32 *key_pos) const;
+
+ bool remove_key(const UInt8 *ptr, UInt32 length);
+
+ bool insert_key(const UInt8 *ptr, UInt32 length, UInt32 *key_pos);
+ bool insert_linker(const UInt8 *ptr, UInt32 length,
+ UInt32 &node_id, UInt32 query_pos);
+
+ bool update_key(const Key &key, const UInt8 *ptr, UInt32 length,
+ UInt32 *key_pos);
+
+ UInt32 insert_node(UInt32 node_id, UInt16 label);
+ UInt32 append_key(const UInt8 *ptr, UInt32 length, UInt32 key_id);
+
+ UInt32 separate(const UInt8 *ptr, UInt32 length,
+ UInt32 node_id, UInt32 i);
+ void resolve(UInt32 node_id, UInt16 label);
+ void migrate_nodes(UInt32 node_id, UInt32 dest_offset,
+ const UInt16 *labels, UInt32 num_labels);
+
+ UInt32 find_offset(const UInt16 *labels, UInt32 num_labels);
+
+ void reserve_node(UInt32 node_id);
+ void reserve_block(UInt32 block_id);
+
+ void update_block_level(UInt32 block_id, UInt32 level);
+ void set_block_level(UInt32 block_id, UInt32 level);
+ void unset_block_level(UInt32 block_id);
+
+ Node &ith_node(UInt32 i) {
+ GRN_DAT_DEBUG_THROW_IF(i >= num_nodes());
+ return nodes_[i];
+ }
+ Block &ith_block(UInt32 i) {
+ GRN_DAT_DEBUG_THROW_IF(i >= num_blocks());
+ return blocks_[i];
+ }
+ Entry &ith_entry(UInt32 i) {
+ GRN_DAT_DEBUG_THROW_IF(i < min_key_id());
+ GRN_DAT_DEBUG_THROW_IF(i > (max_key_id() + 1));
+ return entries_[i];
+ }
+
+ // Disallows copy and assignment.
+ Trie(const Trie &);
+ Trie &operator=(const Trie &);
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_TRIE_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/dat/vector.hpp b/storage/mroonga/vendor/groonga/lib/dat/vector.hpp
new file mode 100644
index 00000000000..4f32c590e0b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/dat/vector.hpp
@@ -0,0 +1,193 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_DAT_VECTOR_HPP_
+#define GRN_DAT_VECTOR_HPP_
+
+#include "dat.hpp"
+
+#include <new>
+
+namespace grn {
+namespace dat {
+
+template <typename T>
+class GRN_DAT_API Vector {
+ public:
+ Vector() : buf_(NULL), size_(0), capacity_(0) {}
+ ~Vector() {
+ for (UInt32 i = 0; i < size(); ++i) {
+ buf_[i].~T();
+ }
+ delete [] reinterpret_cast<char *>(buf_);
+ }
+
+ const T &operator[](UInt32 i) const {
+ GRN_DAT_DEBUG_THROW_IF(i >= size());
+ return buf_[i];
+ }
+ T &operator[](UInt32 i) {
+ GRN_DAT_DEBUG_THROW_IF(i >= size());
+ return buf_[i];
+ }
+
+ const T &front() const {
+ GRN_DAT_DEBUG_THROW_IF(empty());
+ return buf_[0];
+ }
+ T &front() {
+ GRN_DAT_DEBUG_THROW_IF(empty());
+ return buf_[0];
+ }
+
+ const T &back() const {
+ GRN_DAT_DEBUG_THROW_IF(empty());
+ return buf_[size() - 1];
+ }
+ T &back() {
+ GRN_DAT_DEBUG_THROW_IF(empty());
+ return buf_[size() - 1];
+ }
+
+ const T *begin() const {
+ return buf_;
+ }
+ T *begin() {
+ return buf_;
+ }
+
+ const T *end() const {
+ return buf_ + size_;
+ }
+ T *end() {
+ return buf_ + size_;
+ }
+
+ void push_back() {
+ reserve(size() + 1);
+ new (&buf_[size()]) T;
+ ++size_;
+ }
+ void push_back(const T &x) {
+ reserve(size() + 1);
+ new (&buf_[size()]) T(x);
+ ++size_;
+ }
+
+ void pop_back() {
+ GRN_DAT_DEBUG_THROW_IF(empty());
+ back().~T();
+ --size_;
+ }
+
+ void clear() {
+ resize(0);
+ }
+
+ void resize(UInt32 new_size) {
+ if (new_size > capacity()) {
+ reserve(new_size);
+ }
+ for (UInt32 i = size(); i < new_size; ++i) {
+ new (&buf_[i]) T;
+ }
+ for (UInt32 i = new_size; i < size(); ++i) {
+ buf_[i].~T();
+ }
+ size_ = new_size;
+ }
+ template <typename U>
+ void resize(UInt32 new_size, const U &value) {
+ if (new_size > capacity()) {
+ reserve(new_size);
+ }
+ for (UInt32 i = size(); i < new_size; ++i) {
+ new (&buf_[i]) T(value);
+ }
+ for (UInt32 i = new_size; i < size(); ++i) {
+ buf_[i].~T();
+ }
+ size_ = new_size;
+ }
+
+ void reserve(UInt32 new_capacity) {
+ if (new_capacity <= capacity()) {
+ return;
+ } else if ((new_capacity / 2) < capacity()) {
+ if (capacity() < (MAX_UINT32 / 2)) {
+ new_capacity = capacity() * 2;
+ } else {
+ new_capacity = MAX_UINT32;
+ }
+ }
+
+ T *new_buf = reinterpret_cast<T *>(
+ new (std::nothrow) char[sizeof(new_capacity) * new_capacity]);
+ GRN_DAT_THROW_IF(MEMORY_ERROR, new_buf == NULL);
+
+ for (UInt32 i = 0; i < size(); ++i) {
+ new (&new_buf[i]) T(buf_[i]);
+ }
+ for (UInt32 i = 0; i < size(); ++i) {
+ buf_[i].~T();
+ }
+
+ T *old_buf = buf_;
+ buf_ = new_buf;
+ delete [] reinterpret_cast<char *>(old_buf);
+
+ capacity_ = new_capacity;
+ }
+
+ void swap(Vector *rhs) {
+ T * const temp_buf = buf_;
+ buf_ = rhs->buf_;
+ rhs->buf_ = temp_buf;
+
+ const UInt32 temp_size = size_;
+ size_ = rhs->size_;
+ rhs->size_ = temp_size;
+
+ const UInt32 temp_capacity = capacity_;
+ capacity_ = rhs->capacity_;
+ rhs->capacity_ = temp_capacity;
+ }
+
+ bool empty() const {
+ return size_ == 0;
+ }
+ UInt32 size() const {
+ return size_;
+ }
+ UInt32 capacity() const {
+ return capacity_;
+ }
+
+ private:
+ T *buf_;
+ UInt32 size_;
+ UInt32 capacity_;
+
+ // Disallows copy and assignment.
+ Vector(const Vector &);
+ Vector &operator=(const Vector &);
+};
+
+} // namespace dat
+} // namespace grn
+
+#endif // GRN_DAT_VECTOR_HPP_
diff --git a/storage/mroonga/vendor/groonga/lib/db.c b/storage/mroonga/vendor/groonga/lib/db.c
new file mode 100644
index 00000000000..28ec256c036
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/db.c
@@ -0,0 +1,10615 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "groonga_in.h"
+#include "db.h"
+#include "hash.h"
+#include "pat.h"
+#include "dat.h"
+#include "ii.h"
+#include "ctx_impl.h"
+#include "token.h"
+#include "proc.h"
+#include "plugin_in.h"
+#include "geo.h"
+#include "snip.h"
+#include "string_in.h"
+#include "normalizer_in.h"
+#include "util.h"
+#include <string.h>
+#include <float.h>
+
+typedef struct {
+ grn_id id;
+ unsigned int weight;
+} weight_uvector_entry;
+
+#define IS_WEIGHT_UVECTOR(obj) ((obj)->header.flags & GRN_OBJ_WITH_WEIGHT)
+
+#define NEXT_ADDR(p) (((byte *)(p)) + sizeof(*(p)))
+
+#define GRN_TABLE_GROUPED (0x01<<0)
+#define GRN_TABLE_IS_GROUPED(table)\
+ ((table)->header.impl_flags & GRN_TABLE_GROUPED)
+#define GRN_TABLE_GROUPED_ON(table)\
+ ((table)->header.impl_flags |= GRN_TABLE_GROUPED)
+
+#define WITH_NORMALIZE(table,key,key_size,block) do {\
+ if ((table)->normalizer && key && key_size > 0) {\
+ grn_obj *nstr;\
+ if ((nstr = grn_string_open(ctx, key, key_size,\
+ (table)->normalizer, 0))) {\
+ const char *key;\
+ unsigned int key_size;\
+ grn_string_get_normalized(ctx, nstr, &key, &key_size, NULL);\
+ block\
+ grn_obj_close(ctx, nstr);\
+ }\
+ } else {\
+ block\
+ }\
+} while (0)
+
+inline static grn_id
+grn_table_add_v_inline(grn_ctx *ctx, grn_obj *table, const void *key, int key_size,
+ void **value, int *added);
+inline static void
+grn_table_add_subrec_inline(grn_obj *table, grn_rset_recinfo *ri, int score,
+ grn_rset_posinfo *pi, int dir);
+inline static grn_id
+grn_table_cursor_next_inline(grn_ctx *ctx, grn_table_cursor *tc);
+inline static int
+grn_table_cursor_get_value_inline(grn_ctx *ctx, grn_table_cursor *tc, void **value);
+
+static void grn_obj_ensure_bulk(grn_ctx *ctx, grn_obj *obj);
+static void grn_obj_ensure_vector(grn_ctx *ctx, grn_obj *obj);
+
+inline static void
+gen_pathname(const char *path, char *buffer, int fno)
+{
+ size_t len = strlen(path);
+ memcpy(buffer, path, len);
+ if (fno >= 0) {
+ buffer[len] = '.';
+ grn_itoh(fno, buffer + len + 1, 7);
+ buffer[len + 8] = '\0';
+ } else {
+ buffer[len] = '\0';
+ }
+}
+
+static grn_bool
+is_text_object(grn_obj *object)
+{
+ if (!object) {
+ return GRN_FALSE;
+ }
+
+ if (object->header.type != GRN_BULK) {
+ return GRN_FALSE;
+ }
+
+ switch (object->header.domain) {
+ case GRN_DB_SHORT_TEXT:
+ case GRN_DB_TEXT:
+ case GRN_DB_LONG_TEXT:
+ return GRN_TRUE;
+ default:
+ return GRN_FALSE;
+ }
+}
+
+static void
+limited_size_inspect(grn_ctx *ctx, grn_obj *buffer, grn_obj *object)
+{
+ unsigned int original_size = 0;
+ unsigned int max_size = GRN_CTX_MSGSIZE / 2;
+
+ if (object) {
+ original_size = GRN_BULK_VSIZE(object);
+ }
+
+ if (original_size > max_size && is_text_object(object)) {
+ grn_text_esc(ctx, buffer, GRN_TEXT_VALUE(object), max_size);
+ GRN_TEXT_PUTS(ctx, buffer, "...(");
+ grn_text_lltoa(ctx, buffer, original_size);
+ GRN_TEXT_PUTS(ctx, buffer, ")");
+ } else {
+ grn_inspect(ctx, buffer, object);
+ }
+}
+
+typedef struct {
+ grn_obj *ptr;
+ uint32_t lock;
+ uint32_t done;
+} db_value;
+
+grn_obj *
+grn_db_create(grn_ctx *ctx, const char *path, grn_db_create_optarg *optarg)
+{
+ grn_db *s;
+ GRN_API_ENTER;
+ if (!path || strlen(path) <= PATH_MAX - 14) {
+ if ((s = GRN_MALLOC(sizeof(grn_db)))) {
+ grn_bool use_default_db_key = GRN_TRUE;
+ grn_bool use_pat_as_db_keys = GRN_FALSE;
+ if (getenv("GRN_DB_KEY")) {
+ if (!strcmp(getenv("GRN_DB_KEY"), "pat")) {
+ use_default_db_key = GRN_FALSE;
+ use_pat_as_db_keys = GRN_TRUE;
+ } else if (!strcmp(getenv("GRN_DB_KEY"), "dat")) {
+ use_default_db_key = GRN_FALSE;
+ }
+ }
+ if (use_default_db_key && !strcmp(GRN_DEFAULT_DB_KEY, "pat")) {
+ use_pat_as_db_keys = GRN_TRUE;
+ }
+ grn_tiny_array_init(ctx, &s->values, sizeof(db_value),
+ GRN_TINY_ARRAY_CLEAR|
+ GRN_TINY_ARRAY_THREADSAFE|
+ GRN_TINY_ARRAY_USE_MALLOC);
+ if (use_pat_as_db_keys) {
+ s->keys = (grn_obj *)grn_pat_create(ctx, path, GRN_TABLE_MAX_KEY_SIZE,
+ 0, GRN_OBJ_KEY_VAR_SIZE);
+ } else {
+ s->keys = (grn_obj *)grn_dat_create(ctx, path, GRN_TABLE_MAX_KEY_SIZE,
+ 0, GRN_OBJ_KEY_VAR_SIZE);
+ }
+ if (s->keys) {
+ CRITICAL_SECTION_INIT(s->lock);
+ GRN_DB_OBJ_SET_TYPE(s, GRN_DB);
+ s->obj.db = (grn_obj *)s;
+ s->obj.header.domain = GRN_ID_NIL;
+ DB_OBJ(&s->obj)->range = GRN_ID_NIL;
+ // prepare builtin classes and load builtin plugins.
+ if (path) {
+ char specs_path[PATH_MAX];
+ gen_pathname(path, specs_path, 0);
+ if ((s->specs = grn_ja_create(ctx, specs_path, 65536, 0))) {
+ grn_ctx_use(ctx, (grn_obj *)s);
+ grn_db_init_builtin_types(ctx);
+ GRN_API_RETURN((grn_obj *)s);
+ } else {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "failed to create specs: <%s>", specs_path);
+ }
+ } else {
+ s->specs = NULL;
+ grn_ctx_use(ctx, (grn_obj *)s);
+ grn_db_init_builtin_types(ctx);
+ GRN_API_RETURN((grn_obj *)s);
+ }
+ if (use_pat_as_db_keys) {
+ grn_pat_close(ctx, (grn_pat *)s->keys);
+ grn_pat_remove(ctx, path);
+ } else {
+ grn_dat_close(ctx, (grn_dat *)s->keys);
+ grn_dat_remove(ctx, path);
+ }
+ }
+ grn_tiny_array_fin(&s->values);
+ GRN_FREE(s);
+ } else {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "grn_db alloc failed");
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "too long path");
+ }
+ GRN_API_RETURN(NULL);
+}
+
+grn_obj *
+grn_db_open(grn_ctx *ctx, const char *path)
+{
+ grn_db *s;
+ GRN_API_ENTER;
+ if (path && strlen(path) <= PATH_MAX - 14) {
+ if ((s = GRN_MALLOC(sizeof(grn_db)))) {
+ uint32_t type = grn_io_detect_type(ctx, path);
+ grn_tiny_array_init(ctx, &s->values, sizeof(db_value),
+ GRN_TINY_ARRAY_CLEAR|
+ GRN_TINY_ARRAY_THREADSAFE|
+ GRN_TINY_ARRAY_USE_MALLOC);
+ switch (type) {
+ case GRN_TABLE_PAT_KEY :
+ s->keys = (grn_obj *)grn_pat_open(ctx, path);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ s->keys = (grn_obj *)grn_dat_open(ctx, path);
+ break;
+ default :
+ s->keys = NULL;
+ break;
+ }
+ if (s->keys) {
+ char specs_path[PATH_MAX];
+ gen_pathname(path, specs_path, 0);
+ if ((s->specs = grn_ja_open(ctx, specs_path))) {
+ CRITICAL_SECTION_INIT(s->lock);
+ GRN_DB_OBJ_SET_TYPE(s, GRN_DB);
+ s->obj.db = (grn_obj *)s;
+ s->obj.header.domain = GRN_ID_NIL;
+ DB_OBJ(&s->obj)->range = GRN_ID_NIL;
+ grn_ctx_use(ctx, (grn_obj *)s);
+#ifdef GRN_WITH_MECAB
+ if (grn_db_init_mecab_tokenizer(ctx)) {
+ ERRCLR(ctx);
+ }
+#endif
+ grn_db_init_builtin_tokenizers(ctx);
+ grn_db_init_builtin_normalizers(ctx);
+ grn_db_init_builtin_query(ctx);
+ GRN_API_RETURN((grn_obj *)s);
+ }
+ switch (type) {
+ case GRN_TABLE_PAT_KEY :
+ grn_pat_close(ctx, (grn_pat *)s->keys);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ grn_dat_close(ctx, (grn_dat *)s->keys);
+ break;
+ }
+ }
+ grn_tiny_array_fin(&s->values);
+ GRN_FREE(s);
+ } else {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "grn_db alloc failed");
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "inappropriate path");
+ }
+ GRN_API_RETURN(NULL);
+}
+
+static grn_id
+grn_db_curr_id(grn_ctx *ctx, grn_obj *db)
+{
+ grn_id curr_id = GRN_ID_NIL;
+ grn_db *s = (grn_db *)db;
+ switch (s->keys->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ curr_id = grn_pat_curr_id(ctx, (grn_pat *)s->keys);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ curr_id = grn_dat_curr_id(ctx, (grn_dat *)s->keys);
+ break;
+ }
+ return curr_id;
+}
+
+/* s must be validated by caller */
+grn_rc
+grn_db_close(grn_ctx *ctx, grn_obj *db)
+{
+ grn_id id;
+ db_value *vp;
+ grn_db *s = (grn_db *)db;
+ grn_bool ctx_used_db;
+ if (!s) { return GRN_INVALID_ARGUMENT; }
+ GRN_API_ENTER;
+ ctx_used_db = ctx->impl && ctx->impl->db == db;
+ if (ctx_used_db) {
+ grn_ctx_loader_clear(ctx);
+ if (ctx->impl->parser) {
+ grn_expr_parser_close(ctx);
+ }
+ if (ctx->impl->values) {
+ grn_db_obj *o;
+ GRN_ARRAY_EACH(ctx, ctx->impl->values, 0, 0, id, &o, {
+ grn_obj_close(ctx, *((grn_obj **)o));
+ });
+ grn_array_truncate(ctx, ctx->impl->values);
+ }
+ }
+ GRN_TINY_ARRAY_EACH(&s->values, 1, grn_db_curr_id(ctx, db), id, vp, {
+ if (vp->ptr) { grn_obj_close(ctx, vp->ptr); }
+ });
+/* grn_tiny_array_fin should be refined.. */
+#ifdef WIN32
+ {
+ grn_tiny_array *a = &s->values;
+ CRITICAL_SECTION_FIN(a->lock);
+ }
+#endif
+ grn_tiny_array_fin(&s->values);
+ switch (s->keys->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ grn_pat_close(ctx, (grn_pat *)s->keys);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ grn_dat_close(ctx, (grn_dat *)s->keys);
+ break;
+ }
+ CRITICAL_SECTION_FIN(s->lock);
+ if (s->specs) { grn_ja_close(ctx, s->specs); }
+ GRN_FREE(s);
+ if (ctx_used_db) {
+ grn_cache *cache;
+ cache = grn_cache_current_get(ctx);
+ if (cache) {
+ grn_cache_expire(cache, -1);
+ }
+ ctx->impl->db = NULL;
+ }
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+grn_obj *
+grn_ctx_get(grn_ctx *ctx, const char *name, int name_size)
+{
+ grn_id id;
+ grn_obj *obj = NULL;
+ grn_obj *db;
+ if (!ctx || !ctx->impl || !(db = ctx->impl->db)) {
+ return NULL;
+ }
+ GRN_API_ENTER;
+ if (GRN_DB_P(db)) {
+ grn_db *s = (grn_db *)db;
+ if (name_size < 0) {
+ name_size = strlen(name);
+ }
+ if ((id = grn_table_get(ctx, s->keys, name, name_size))) {
+ obj = grn_ctx_at(ctx, id);
+ }
+ }
+ GRN_API_RETURN(obj);
+}
+
+grn_obj *
+grn_ctx_db(grn_ctx *ctx)
+{
+ return (ctx && ctx->impl) ? ctx->impl->db : NULL;
+}
+
+grn_obj *
+grn_db_keys(grn_obj *s)
+{
+ return (grn_obj *)(((grn_db *)s)->keys);
+}
+
+static grn_io*
+grn_obj_io(grn_obj *obj)
+{
+ grn_io *io = NULL;
+ if (obj) {
+ if (obj->header.type == GRN_DB) { obj = ((grn_db *)obj)->keys; }
+ switch (obj->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ io = ((grn_pat *)obj)->io;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ io = ((grn_dat *)obj)->io;
+ break;
+ case GRN_TABLE_HASH_KEY :
+ io = ((grn_hash *)obj)->io;
+ break;
+ case GRN_TABLE_NO_KEY :
+ io = ((grn_array *)obj)->io;
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ io = ((grn_ja *)obj)->io;
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ io = ((grn_ra *)obj)->io;
+ break;
+ case GRN_COLUMN_INDEX :
+ io = ((grn_ii *)obj)->seg;
+ break;
+ }
+ }
+ return io;
+}
+
+uint32_t
+grn_db_lastmod(grn_obj *s)
+{
+ return grn_obj_io(((grn_db *)s)->keys)->header->lastmod;
+}
+
+void
+grn_db_touch(grn_ctx *ctx, grn_obj *s)
+{
+ grn_timeval tv;
+ grn_timeval_now(ctx, &tv);
+ grn_obj_io(s)->header->lastmod = tv.tv_sec;
+}
+
+#define IS_TEMP(obj) (DB_OBJ(obj)->id & GRN_OBJ_TMP_OBJECT)
+
+void
+grn_obj_touch(grn_ctx *ctx, grn_obj *obj, grn_timeval *tv)
+{
+ grn_timeval tv_;
+ if (!tv) {
+ grn_timeval_now(ctx, &tv_);
+ tv = &tv_;
+ }
+ if (obj) {
+ switch (obj->header.type) {
+ case GRN_DB :
+ grn_obj_io(obj)->header->lastmod = tv->tv_sec;
+ break;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ case GRN_COLUMN_VAR_SIZE :
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_INDEX :
+ if (!IS_TEMP(obj)) {
+ grn_obj_io(DB_OBJ(obj)->db)->header->lastmod = tv->tv_sec;
+ }
+ break;
+ }
+ }
+}
+
+grn_rc
+grn_db_check_name(grn_ctx *ctx, const char *name, unsigned int name_size)
+{
+ int len;
+ const char *name_end = name + name_size;
+ if (name_size > 0 &&
+ *name == GRN_DB_PSEUDO_COLUMN_PREFIX) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ while (name < name_end) {
+ char c = *name;
+ if ((unsigned int)((c | 0x20) - 'a') >= 26u &&
+ (unsigned int)(c - '0') >= 10u &&
+ c != '_' &&
+ c != '-' &&
+ c != '#' &&
+ c != '@') {
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (!(len = grn_charlen(ctx, name, name_end))) { break; }
+ name += len;
+ }
+ return GRN_SUCCESS;
+}
+
+grn_obj *
+grn_type_create(grn_ctx *ctx, const char *name, unsigned int name_size,
+ grn_obj_flags flags, unsigned int size)
+{
+ grn_id id;
+ struct _grn_type *res = NULL;
+ grn_obj *db;
+ if (!ctx || !ctx->impl || !(db = ctx->impl->db)) {
+ ERR(GRN_INVALID_ARGUMENT, "db not initialized");
+ return NULL;
+ }
+ GRN_API_ENTER;
+ if (grn_db_check_name(ctx, name, name_size)) {
+ GRN_DB_CHECK_NAME_ERR("[type][create]", name, name_size);
+ GRN_API_RETURN(NULL);
+ }
+ if (!GRN_DB_P(db)) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid db assigned");
+ GRN_API_RETURN(NULL);
+ }
+ id = grn_obj_register(ctx, db, name, name_size);
+ if (id && (res = GRN_MALLOC(sizeof(grn_db_obj)))) {
+ GRN_DB_OBJ_SET_TYPE(res, GRN_TYPE);
+ res->obj.header.flags = flags;
+ res->obj.header.domain = GRN_ID_NIL;
+ GRN_TYPE_SIZE(&res->obj) = size;
+ if (grn_db_obj_init(ctx, db, id, DB_OBJ(res))) {
+ // grn_obj_delete(ctx, db, id);
+ GRN_FREE(res);
+ GRN_API_RETURN(NULL);
+ }
+ }
+ GRN_API_RETURN((grn_obj *)res);
+}
+
+static grn_obj *
+grn_type_open(grn_ctx *ctx, grn_obj_spec *spec)
+{
+ struct _grn_type *res;
+ res = GRN_MALLOC(sizeof(struct _grn_type));
+ if (res) {
+ GRN_DB_OBJ_SET_TYPE(res, GRN_TYPE);
+ res->obj.header = spec->header;
+ GRN_TYPE_SIZE(&res->obj) = GRN_TYPE_SIZE(spec);
+ }
+ return (grn_obj *)res;
+}
+
+grn_obj *
+grn_proc_create(grn_ctx *ctx, const char *name, int name_size, grn_proc_type type,
+ grn_proc_func *init, grn_proc_func *next, grn_proc_func *fin,
+ unsigned int nvars, grn_expr_var *vars)
+{
+ grn_proc *res = NULL;
+ grn_id id = GRN_ID_NIL;
+ grn_id range = GRN_ID_NIL;
+ int added = 0;
+ grn_obj *db;
+ const char *path = ctx->impl->plugin_path;
+ if (!ctx || !ctx->impl || !(db = ctx->impl->db)) {
+ ERR(GRN_INVALID_ARGUMENT, "db not initialized");
+ return NULL;
+ }
+ GRN_API_ENTER;
+ if (path) {
+ range = grn_plugin_reference(ctx, path);
+ }
+ if (name_size < 0) {
+ name_size = strlen(name);
+ }
+ if (grn_db_check_name(ctx, name, name_size)) {
+ GRN_DB_CHECK_NAME_ERR("[proc][create]", name, name_size);
+ GRN_API_RETURN(NULL);
+ }
+ if (!GRN_DB_P(db)) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid db assigned");
+ GRN_API_RETURN(NULL);
+ }
+ if (name && name_size) {
+ grn_db *s = (grn_db *)db;
+ if (!(id = grn_table_get(ctx, s->keys, name, name_size))) {
+ if (!(id = grn_table_add(ctx, s->keys, name, name_size, &added))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "grn_table_add failed");
+ GRN_API_RETURN(NULL);
+ }
+ }
+ if (!added) {
+ db_value *vp;
+ if ((vp = grn_tiny_array_at(&s->values, id)) && (res = (grn_proc *)vp->ptr)) {
+ if (res->funcs[PROC_INIT] ||
+ res->funcs[PROC_NEXT] ||
+ res->funcs[PROC_FIN]) {
+ ERR(GRN_INVALID_ARGUMENT, "already used name");
+ GRN_API_RETURN(NULL);
+ }
+ } else {
+ added = 1;
+ }
+ }
+ } else if (ctx->impl && ctx->impl->values) {
+ id = grn_array_add(ctx, ctx->impl->values, NULL) | GRN_OBJ_TMP_OBJECT;
+ added = 1;
+ }
+ if (!res) { res = GRN_MALLOCN(grn_proc, 1); }
+ if (res) {
+ GRN_DB_OBJ_SET_TYPE(res, GRN_PROC);
+ res->obj.db = db;
+ res->obj.id = id;
+ res->obj.header.domain = GRN_ID_NIL;
+ res->obj.header.flags = path ? GRN_OBJ_CUSTOM_NAME : 0;
+ res->obj.range = range;
+ res->type = type;
+ res->funcs[PROC_INIT] = init;
+ res->funcs[PROC_NEXT] = next;
+ res->funcs[PROC_FIN] = fin;
+ res->selector = NULL;
+ GRN_TEXT_INIT(&res->name_buf, 0);
+ res->vars = NULL;
+ res->nvars = 0;
+ if (added) {
+ if (grn_db_obj_init(ctx, db, id, DB_OBJ(res))) {
+ // grn_obj_delete(ctx, db, id);
+ GRN_FREE(res);
+ GRN_API_RETURN(NULL);
+ }
+ }
+ while (nvars--) {
+ grn_obj *v = grn_expr_add_var(ctx, (grn_obj *)res, vars->name, vars->name_size);
+ GRN_OBJ_INIT(v, vars->value.header.type, 0, vars->value.header.domain);
+ GRN_TEXT_PUT(ctx, v, GRN_TEXT_VALUE(&vars->value), GRN_TEXT_LEN(&vars->value));
+ vars++;
+ }
+ }
+ GRN_API_RETURN((grn_obj *)res);
+}
+
+/* grn_table */
+
+static void
+calc_rec_size(grn_obj_flags flags, uint32_t max_n_subrecs, uint32_t range_size,
+ uint8_t *subrec_size, uint8_t *subrec_offset,
+ uint32_t *key_size, uint32_t *value_size)
+{
+ *subrec_size = 0;
+ *subrec_offset = 0;
+ if (flags & GRN_OBJ_WITH_SUBREC) {
+ switch (flags & GRN_OBJ_UNIT_MASK) {
+ case GRN_OBJ_UNIT_DOCUMENT_NONE :
+ break;
+ case GRN_OBJ_UNIT_DOCUMENT_SECTION :
+ *subrec_offset = sizeof(grn_id);
+ *subrec_size = sizeof(uint32_t);
+ break;
+ case GRN_OBJ_UNIT_DOCUMENT_POSITION :
+ *subrec_offset = sizeof(grn_id);
+ *subrec_size = sizeof(uint32_t) + sizeof(uint32_t);
+ break;
+ case GRN_OBJ_UNIT_SECTION_NONE :
+ *key_size += sizeof(uint32_t);
+ break;
+ case GRN_OBJ_UNIT_SECTION_POSITION :
+ *key_size += sizeof(uint32_t);
+ *subrec_offset = sizeof(grn_id) + sizeof(uint32_t);
+ *subrec_size = sizeof(uint32_t);
+ break;
+ case GRN_OBJ_UNIT_POSITION_NONE :
+ *key_size += sizeof(uint32_t) + sizeof(uint32_t);
+ break;
+ case GRN_OBJ_UNIT_USERDEF_DOCUMENT :
+ *subrec_size = range_size;
+ break;
+ case GRN_OBJ_UNIT_USERDEF_SECTION :
+ *subrec_size = range_size + sizeof(uint32_t);
+ break;
+ case GRN_OBJ_UNIT_USERDEF_POSITION :
+ *subrec_size = range_size + sizeof(uint32_t) + sizeof(uint32_t);
+ break;
+ }
+ *value_size = (uintptr_t)GRN_RSET_SUBRECS_NTH((((grn_rset_recinfo *)0)->subrecs),
+ *subrec_size, max_n_subrecs);
+ } else {
+ *value_size = range_size;
+ }
+}
+
+static void _grn_obj_remove(grn_ctx *ctx, grn_obj *obj);
+
+static grn_rc
+grn_table_create_validate(grn_ctx *ctx, const char *name, unsigned int name_size,
+ const char *path, grn_obj_flags flags,
+ grn_obj *key_type, grn_obj *value_type)
+{
+ switch (flags & GRN_OBJ_TABLE_TYPE_MASK) {
+ case GRN_OBJ_TABLE_HASH_KEY :
+ if (flags & GRN_OBJ_KEY_WITH_SIS) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] "
+ "key with SIS isn't available for hash table: <%.*s>",
+ name_size, name);
+ }
+ break;
+ case GRN_OBJ_TABLE_PAT_KEY :
+ break;
+ case GRN_OBJ_TABLE_DAT_KEY :
+ break;
+ case GRN_OBJ_TABLE_NO_KEY :
+ if (key_type) {
+ int key_name_size;
+ char key_name[GRN_TABLE_MAX_KEY_SIZE];
+ key_name_size = grn_obj_name(ctx, key_type, key_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] "
+ "key isn't available for no key table: <%.*s> (%.*s)",
+ name_size, name, key_name_size, key_name);
+ } else if (flags & GRN_OBJ_KEY_WITH_SIS) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] "
+ "key with SIS isn't available for no key table: <%.*s>",
+ name_size, name);
+ } else if (flags & GRN_OBJ_KEY_NORMALIZE) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] "
+ "key normalization isn't available for no key table: <%.*s>",
+ name_size, name);
+ }
+ break;
+ }
+ return ctx->rc;
+}
+
+static grn_obj *
+grn_table_create_with_max_n_subrecs(grn_ctx *ctx, const char *name,
+ unsigned int name_size, const char *path,
+ grn_obj_flags flags, grn_obj *key_type,
+ grn_obj *value_type, uint32_t max_n_subrecs)
+{
+ grn_id id;
+ grn_id domain = GRN_ID_NIL, range = GRN_ID_NIL;
+ uint32_t key_size, value_size = 0, range_size = 0;
+ uint8_t subrec_size, subrec_offset;
+ grn_obj *res = NULL;
+ grn_obj *db;
+ char buffer[PATH_MAX];
+ if (!ctx->impl || !(db = ctx->impl->db)) {
+ ERR(GRN_INVALID_ARGUMENT, "[table][create] db not initialized");
+ return NULL;
+ }
+ if (grn_db_check_name(ctx, name, name_size)) {
+ GRN_DB_CHECK_NAME_ERR("[table][create]", name, name_size);
+ return NULL;
+ }
+ if (!GRN_DB_P(db)) {
+ ERR(GRN_INVALID_ARGUMENT, "[table][create] invalid db assigned");
+ return NULL;
+ }
+ if (grn_table_create_validate(ctx, name, name_size, path, flags,
+ key_type, value_type)) {
+ return NULL;
+ }
+ if (key_type) {
+ domain = DB_OBJ(key_type)->id;
+ switch (key_type->header.type) {
+ case GRN_TYPE :
+ {
+ grn_db_obj *t = (grn_db_obj *)key_type;
+ flags |= t->header.flags;
+ key_size = GRN_TYPE_SIZE(t);
+ if (key_size > GRN_TABLE_MAX_KEY_SIZE) {
+ int type_name_size;
+ char type_name[GRN_TABLE_MAX_KEY_SIZE];
+ type_name_size = grn_obj_name(ctx, key_type, type_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] key size too big: <%.*s> <%.*s>(%u) (max:%u)",
+ name_size, name,
+ type_name_size, type_name,
+ key_size, GRN_TABLE_MAX_KEY_SIZE);
+ return NULL;
+ }
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ key_size = sizeof(grn_id);
+ break;
+ default :
+ {
+ int key_name_size;
+ char key_name[GRN_TABLE_MAX_KEY_SIZE];
+ key_name_size = grn_obj_name(ctx, key_type, key_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] key type must be type or table: <%.*s> (%.*s)",
+ name_size, name, key_name_size, key_name);
+ return NULL;
+ }
+ break;
+ }
+ } else {
+ key_size = (flags & GRN_OBJ_KEY_VAR_SIZE) ? GRN_TABLE_MAX_KEY_SIZE : sizeof(grn_id);
+ }
+ if (value_type) {
+ range = DB_OBJ(value_type)->id;
+ switch (value_type->header.type) {
+ case GRN_TYPE :
+ {
+ grn_db_obj *t = (grn_db_obj *)value_type;
+ if (t->header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ int type_name_size;
+ char type_name[GRN_TABLE_MAX_KEY_SIZE];
+ type_name_size = grn_obj_name(ctx, value_type, type_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] value type must be fixed size: <%.*s> (%.*s)",
+ name_size, name, type_name_size, type_name);
+ return NULL;
+ }
+ range_size = GRN_TYPE_SIZE(t);
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ range_size = sizeof(grn_id);
+ break;
+ default :
+ {
+ int value_name_size;
+ char value_name[GRN_TABLE_MAX_KEY_SIZE];
+ value_name_size = grn_obj_name(ctx, value_type, value_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] value type must be type or table: <%.*s> (%.*s)",
+ name_size, name, value_name_size, value_name);
+ return NULL;
+ }
+ break;
+ }
+ }
+
+ id = grn_obj_register(ctx, db, name, name_size);
+ if (ERRP(ctx, GRN_ERROR)) { return NULL; }
+ if (GRN_OBJ_PERSISTENT & flags) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "DDL:table_create %.*s", name_size, name);
+ if (!path) {
+ if (GRN_DB_PERSISTENT_P(db)) {
+ gen_pathname(grn_obj_io(db)->path, buffer, id);
+ path = buffer;
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "path not assigned for persistent table");
+ return NULL;
+ }
+ } else {
+ flags |= GRN_OBJ_CUSTOM_NAME;
+ }
+ } else {
+ if (path) {
+ ERR(GRN_INVALID_ARGUMENT, "path assigned for temporary table");
+ return NULL;
+ }
+ if (GRN_DB_PERSISTENT_P(db) && name && name_size) {
+ ERR(GRN_INVALID_ARGUMENT, "name assigned for temporary table");
+ return NULL;
+ }
+ }
+ calc_rec_size(flags, max_n_subrecs, range_size, &subrec_size,
+ &subrec_offset, &key_size, &value_size);
+ switch (flags & GRN_OBJ_TABLE_TYPE_MASK) {
+ case GRN_OBJ_TABLE_HASH_KEY :
+ res = (grn_obj *)grn_hash_create(ctx, path, key_size, value_size, flags);
+ break;
+ case GRN_OBJ_TABLE_PAT_KEY :
+ res = (grn_obj *)grn_pat_create(ctx, path, key_size, value_size, flags);
+ break;
+ case GRN_OBJ_TABLE_DAT_KEY :
+ res = (grn_obj *)grn_dat_create(ctx, path, key_size, value_size, flags);
+ break;
+ case GRN_OBJ_TABLE_NO_KEY :
+ domain = range;
+ res = (grn_obj *)grn_array_create(ctx, path, value_size, flags);
+ break;
+ }
+ if (res) {
+ DB_OBJ(res)->header.impl_flags = 0;
+ DB_OBJ(res)->header.domain = domain;
+ DB_OBJ(res)->range = range;
+ DB_OBJ(res)->max_n_subrecs = max_n_subrecs;
+ DB_OBJ(res)->subrec_size = subrec_size;
+ DB_OBJ(res)->subrec_offset = subrec_offset;
+ if (grn_db_obj_init(ctx, db, id, DB_OBJ(res))) {
+ _grn_obj_remove(ctx, res);
+ res = NULL;
+ }
+ } else {
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ }
+ return res;
+}
+
+grn_obj *
+grn_table_create(grn_ctx *ctx, const char *name, unsigned int name_size,
+ const char *path, grn_obj_flags flags,
+ grn_obj *key_type, grn_obj *value_type)
+{
+ grn_obj *res;
+ GRN_API_ENTER;
+ res = grn_table_create_with_max_n_subrecs(ctx, name, name_size, path,
+ flags, key_type, value_type, 0);
+ GRN_API_RETURN(res);
+}
+
+grn_obj *
+grn_table_create_for_group(grn_ctx *ctx, const char *name,
+ unsigned int name_size, const char *path,
+ grn_obj *group_key, grn_obj *value_type,
+ unsigned int max_n_subrecs)
+{
+ grn_obj *res = NULL;
+ grn_obj *key_type;
+ GRN_API_ENTER;
+ key_type = grn_ctx_at(ctx, grn_obj_get_range(ctx, group_key));
+ if (key_type) {
+ res = grn_table_create_with_max_n_subrecs(ctx, name, name_size, path,
+ GRN_TABLE_HASH_KEY|
+ GRN_OBJ_WITH_SUBREC|
+ GRN_OBJ_UNIT_USERDEF_DOCUMENT,
+ key_type, value_type, max_n_subrecs);
+ }
+ GRN_API_RETURN(res);
+}
+
+unsigned int
+grn_table_get_subrecs(grn_ctx *ctx, grn_obj *table, grn_id id,
+ grn_id *subrecbuf, int *scorebuf, int buf_size)
+{
+ unsigned int count = 0;
+ GRN_API_ENTER;
+ if (GRN_OBJ_TABLEP(table)) {
+ uint32_t value_size;
+ grn_rset_recinfo *ri;
+ uint32_t subrec_size = DB_OBJ(table)->subrec_size;
+ uint32_t max_n_subrecs = DB_OBJ(table)->max_n_subrecs;
+ if (subrec_size < sizeof(grn_id)) { goto exit; }
+ if (!max_n_subrecs) { goto exit; }
+ ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, table, id, &value_size);
+ if (ri) {
+ byte *psubrec = (byte *)ri->subrecs;
+ uint32_t n_subrecs = (uint32_t)GRN_RSET_N_SUBRECS(ri);
+ uint32_t limit = value_size / (GRN_RSET_SCORE_SIZE + subrec_size);
+ if (limit > buf_size) {
+ limit = buf_size;
+ }
+ if (limit > n_subrecs) {
+ limit = n_subrecs;
+ }
+ if (limit > max_n_subrecs) {
+ limit = max_n_subrecs;
+ }
+ for (; count < limit; count++) {
+ if (scorebuf) {
+ scorebuf[count] = *((int *)psubrec);
+ }
+ psubrec += GRN_RSET_SCORE_SIZE;
+ if (subrecbuf) {
+ subrecbuf[count] = *((grn_id *)psubrec);
+ }
+ psubrec += subrec_size;
+ }
+ }
+ }
+exit :
+ GRN_API_RETURN(count);
+}
+
+grn_obj *
+grn_table_open(grn_ctx *ctx, const char *name, unsigned int name_size, const char *path)
+{
+ grn_obj *db;
+ if (!ctx->impl || !(db = ctx->impl->db)) {
+ ERR(GRN_INVALID_ARGUMENT, "db not initialized");
+ return NULL;
+ }
+ GRN_API_ENTER;
+ if (!GRN_DB_P(db)) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid db assigned");
+ GRN_API_RETURN(NULL);
+ } else {
+ grn_obj *res = grn_ctx_get(ctx, name, name_size);
+ if (res) {
+ const char *path2 = grn_obj_path(ctx, res);
+ if (path && (!path2 || strcmp(path, path2))) {
+ ERR(GRN_INVALID_ARGUMENT, "path unmatch");
+ GRN_API_RETURN(NULL);
+ }
+ } else if (path) {
+ uint32_t type = grn_io_detect_type(ctx, path);
+ if (!type) { GRN_API_RETURN(NULL); }
+ switch (type) {
+ case GRN_TABLE_HASH_KEY :
+ res = (grn_obj *)grn_hash_open(ctx, path);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ res = (grn_obj *)grn_pat_open(ctx, path);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ res = (grn_obj *)grn_dat_open(ctx, path);
+ break;
+ case GRN_TABLE_NO_KEY :
+ res = (grn_obj *)grn_array_open(ctx, path);
+ break;
+ }
+ if (res) {
+ grn_id id = grn_obj_register(ctx, db, name, name_size);
+ res->header.flags |= GRN_OBJ_CUSTOM_NAME;
+ res->header.domain = GRN_ID_NIL; /* unknown */
+ DB_OBJ(res)->range = GRN_ID_NIL; /* unknown */
+ grn_db_obj_init(ctx, db, id, DB_OBJ(res));
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "path is missing");
+ }
+ GRN_API_RETURN(res);
+ }
+}
+
+grn_id
+grn_table_lcp_search(grn_ctx *ctx, grn_obj *table, const void *key, unsigned int key_size)
+{
+ grn_id id = GRN_ID_NIL;
+ GRN_API_ENTER;
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ {
+ grn_pat *pat = (grn_pat *)table;
+ WITH_NORMALIZE(pat, key, key_size, {
+ id = grn_pat_lcp_search(ctx, pat, key, key_size);
+ });
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ {
+ grn_dat *dat = (grn_dat *)table;
+ WITH_NORMALIZE(dat, key, key_size, {
+ id = grn_dat_lcp_search(ctx, dat, key, key_size);
+ });
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ {
+ grn_hash *hash = (grn_hash *)table;
+ WITH_NORMALIZE(hash, key, key_size, {
+ id = grn_hash_get(ctx, hash, key, key_size, NULL);
+ });
+ }
+ break;
+ }
+ GRN_API_RETURN(id);
+}
+
+typedef struct {
+ grn_id target;
+ unsigned int section;
+} default_set_value_hook_data;
+
+struct _grn_hook {
+ grn_hook *next;
+ grn_proc *proc;
+ uint32_t hld_size;
+};
+
+static grn_obj *
+default_set_value_hook(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_proc_ctx *pctx = (grn_proc_ctx *)user_data;
+ if (!pctx) {
+ ERR(GRN_INVALID_ARGUMENT, "default_set_value_hook failed");
+ } else {
+ grn_obj *flags = grn_ctx_pop(ctx);
+ grn_obj *newvalue = grn_ctx_pop(ctx);
+ grn_obj *oldvalue = grn_ctx_pop(ctx);
+ grn_obj *id = grn_ctx_pop(ctx);
+ grn_hook *h = pctx->currh;
+ default_set_value_hook_data *data = (void *)NEXT_ADDR(h);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ int section = data->section;
+ if (flags) { /* todo */ }
+ if (target) {
+ switch (target->header.type) {
+ case GRN_COLUMN_INDEX :
+ grn_ii_column_update(ctx, (grn_ii *)target,
+ GRN_UINT32_VALUE(id),
+ section, oldvalue, newvalue, NULL);
+ }
+ }
+ }
+ return NULL;
+}
+
+grn_id
+grn_table_add(grn_ctx *ctx, grn_obj *table, const void *key, unsigned int key_size, int *added)
+{
+ grn_id id = GRN_ID_NIL;
+ GRN_API_ENTER;
+ if (table) {
+ int added_ = 0;
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ {
+ grn_pat *pat = (grn_pat *)table;
+ WITH_NORMALIZE(pat, key, key_size, {
+ if (pat->io && !(pat->io->flags & GRN_IO_TEMPORARY)) {
+ if (grn_io_lock(ctx, pat->io, grn_lock_timeout)) {
+ id = GRN_ID_NIL;
+ } else {
+ id = grn_pat_add(ctx, pat, key, key_size, NULL, &added_);
+ grn_io_unlock(pat->io);
+ }
+ } else {
+ id = grn_pat_add(ctx, pat, key, key_size, NULL, &added_);
+ }
+ });
+ if (added) { *added = added_; }
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ {
+ grn_dat *dat = (grn_dat *)table;
+ WITH_NORMALIZE(dat, key, key_size, {
+ if (dat->io && !(dat->io->flags & GRN_IO_TEMPORARY)) {
+ if (grn_io_lock(ctx, dat->io, grn_lock_timeout)) {
+ id = GRN_ID_NIL;
+ } else {
+ id = grn_dat_add(ctx, dat, key, key_size, NULL, &added_);
+ grn_io_unlock(dat->io);
+ }
+ } else {
+ id = grn_dat_add(ctx, dat, key, key_size, NULL, &added_);
+ }
+ });
+ if (added) { *added = added_; }
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ {
+ grn_hash *hash = (grn_hash *)table;
+ WITH_NORMALIZE(hash, key, key_size, {
+ if (hash->io && !(hash->io->flags & GRN_IO_TEMPORARY)) {
+ if (grn_io_lock(ctx, hash->io, grn_lock_timeout)) {
+ id = GRN_ID_NIL;
+ } else {
+ id = grn_hash_add(ctx, hash, key, key_size, NULL, &added_);
+ grn_io_unlock(hash->io);
+ }
+ } else {
+ id = grn_hash_add(ctx, hash, key, key_size, NULL, &added_);
+ }
+ });
+ if (added) { *added = added_; }
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ {
+ grn_array *array = (grn_array *)table;
+ if (array->io && !(array->io->flags & GRN_IO_TEMPORARY)) {
+ if (grn_io_lock(ctx, array->io, grn_lock_timeout)) {
+ id = GRN_ID_NIL;
+ } else {
+ id = grn_array_add(ctx, array, NULL);
+ grn_io_unlock(array->io);
+ }
+ } else {
+ id = grn_array_add(ctx, array, NULL);
+ }
+ added_ = id ? 1 : 0;
+ if (added) { *added = added_; }
+ }
+ break;
+ }
+ if (added_) {
+ grn_hook *hooks = DB_OBJ(table)->hooks[GRN_HOOK_INSERT];
+ if (hooks) {
+ // todo : grn_proc_ctx_open()
+ grn_obj id_, flags_, oldvalue_, value_;
+ grn_proc_ctx pctx = {{0}, hooks->proc, NULL, hooks, hooks, PROC_INIT, 4, 4};
+ GRN_UINT32_INIT(&id_, 0);
+ GRN_UINT32_INIT(&flags_, 0);
+ GRN_TEXT_INIT(&oldvalue_, 0);
+ GRN_TEXT_INIT(&value_, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET_REF(&value_, key, key_size);
+ GRN_UINT32_SET(ctx, &id_, id);
+ GRN_UINT32_SET(ctx, &flags_, GRN_OBJ_SET);
+ while (hooks) {
+ grn_ctx_push(ctx, &id_);
+ grn_ctx_push(ctx, &oldvalue_);
+ grn_ctx_push(ctx, &value_);
+ grn_ctx_push(ctx, &flags_);
+ pctx.caller = NULL;
+ pctx.currh = hooks;
+ if (hooks->proc) {
+ hooks->proc->funcs[PROC_INIT](ctx, 1, &table, &pctx.user_data);
+ } else {
+ default_set_value_hook(ctx, 1, &table, &pctx.user_data);
+ }
+ if (ctx->rc) { break; }
+ hooks = hooks->next;
+ pctx.offset++;
+ }
+ }
+ }
+ }
+ GRN_API_RETURN(id);
+}
+
+grn_id
+grn_table_get_by_key(grn_ctx *ctx, grn_obj *table, grn_obj *key)
+{
+ grn_id id = GRN_ID_NIL;
+ if (table->header.domain == key->header.domain) {
+ id = grn_table_get(ctx, table, GRN_TEXT_VALUE(key), GRN_TEXT_LEN(key));
+ } else {
+ grn_rc rc;
+ grn_obj buf;
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, table->header.domain);
+ if ((rc = grn_obj_cast(ctx, key, &buf, GRN_TRUE))) {
+ ERR(rc, "cast failed");
+ } else {
+ id = grn_table_get(ctx, table, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ return id;
+}
+
+grn_id
+grn_table_add_by_key(grn_ctx *ctx, grn_obj *table, grn_obj *key, int *added)
+{
+ grn_id id = GRN_ID_NIL;
+ if (table->header.domain == key->header.domain) {
+ id = grn_table_add(ctx, table, GRN_TEXT_VALUE(key), GRN_TEXT_LEN(key), added);
+ } else {
+ grn_rc rc;
+ grn_obj buf;
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, table->header.domain);
+ if ((rc = grn_obj_cast(ctx, key, &buf, GRN_TRUE))) {
+ ERR(rc, "cast failed");
+ } else {
+ id = grn_table_add(ctx, table, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf), added);
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ return id;
+}
+
+grn_id
+grn_table_get(grn_ctx *ctx, grn_obj *table, const void *key, unsigned int key_size)
+{
+ grn_id id = GRN_ID_NIL;
+ GRN_API_ENTER;
+ if (table) {
+ if (table->header.type == GRN_DB) {
+ grn_db *db = (grn_db *)table;
+ table = db->keys;
+ }
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ WITH_NORMALIZE((grn_pat *)table, key, key_size, {
+ id = grn_pat_get(ctx, (grn_pat *)table, key, key_size, NULL);
+ });
+ break;
+ case GRN_TABLE_DAT_KEY :
+ WITH_NORMALIZE((grn_dat *)table, key, key_size, {
+ id = grn_dat_get(ctx, (grn_dat *)table, key, key_size, NULL);
+ });
+ break;
+ case GRN_TABLE_HASH_KEY :
+ WITH_NORMALIZE((grn_hash *)table, key, key_size, {
+ id = grn_hash_get(ctx, (grn_hash *)table, key, key_size, NULL);
+ });
+ break;
+ }
+ }
+ GRN_API_RETURN(id);
+}
+
+grn_id
+grn_table_at(grn_ctx *ctx, grn_obj *table, grn_id id)
+{
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_DB :
+ {
+ grn_db *db = (grn_db *)table;
+ id = grn_table_at(ctx, db->keys, id);
+ }
+ break;
+ case GRN_TABLE_PAT_KEY :
+ id = grn_pat_at(ctx, (grn_pat *)table, id);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ id = grn_dat_at(ctx, (grn_dat *)table, id);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ id = grn_hash_at(ctx, (grn_hash *)table, id);
+ break;
+ case GRN_TABLE_NO_KEY :
+ id = grn_array_at(ctx, (grn_array *)table, id);
+ break;
+ default :
+ id = GRN_ID_NIL;
+ }
+ }
+ GRN_API_RETURN(id);
+}
+
+inline static grn_id
+grn_table_add_v_inline(grn_ctx *ctx, grn_obj *table, const void *key, int key_size,
+ void **value, int *added)
+{
+ grn_id id = GRN_ID_NIL;
+ if (!key || !key_size) { return GRN_ID_NIL; }
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ WITH_NORMALIZE((grn_pat *)table, key, key_size, {
+ id = grn_pat_add(ctx, (grn_pat *)table, key, key_size, value, added);
+ });
+ break;
+ case GRN_TABLE_DAT_KEY :
+ WITH_NORMALIZE((grn_dat *)table, key, key_size, {
+ id = grn_dat_add(ctx, (grn_dat *)table, key, key_size, value, added);
+ });
+ break;
+ case GRN_TABLE_HASH_KEY :
+ WITH_NORMALIZE((grn_hash *)table, key, key_size, {
+ id = grn_hash_add(ctx, (grn_hash *)table, key, key_size, value, added);
+ });
+ break;
+ case GRN_TABLE_NO_KEY :
+ id = grn_array_add(ctx, (grn_array *)table, value);
+ if (added) { *added = id ? 1 : 0; }
+ break;
+ }
+ }
+ return id;
+}
+
+grn_id
+grn_table_add_v(grn_ctx *ctx, grn_obj *table, const void *key, int key_size,
+ void **value, int *added) {
+ grn_id id;
+ GRN_API_ENTER;
+ id = grn_table_add_v_inline(ctx, table, key, key_size, value, added);
+ GRN_API_RETURN(id);
+}
+
+grn_id
+grn_table_get_v(grn_ctx *ctx, grn_obj *table, const void *key, int key_size,
+ void **value)
+{
+ grn_id id = GRN_ID_NIL;
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ WITH_NORMALIZE((grn_pat *)table, key, key_size, {
+ id = grn_pat_get(ctx, (grn_pat *)table, key, key_size, value);
+ });
+ break;
+ case GRN_TABLE_DAT_KEY :
+ WITH_NORMALIZE((grn_dat *)table, key, key_size, {
+ id = grn_dat_get(ctx, (grn_dat *)table, key, key_size, value);
+ });
+ break;
+ case GRN_TABLE_HASH_KEY :
+ WITH_NORMALIZE((grn_hash *)table, key, key_size, {
+ id = grn_hash_get(ctx, (grn_hash *)table, key, key_size, value);
+ });
+ break;
+ }
+ }
+ GRN_API_RETURN(id);
+}
+
+int
+grn_table_get_key(grn_ctx *ctx, grn_obj *table, grn_id id, void *keybuf, int buf_size)
+{
+ int r = 0;
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ r = grn_hash_get_key(ctx, (grn_hash *)table, id, keybuf, buf_size);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ r = grn_pat_get_key(ctx, (grn_pat *)table, id, keybuf, buf_size);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ r = grn_dat_get_key(ctx, (grn_dat *)table, id, keybuf, buf_size);
+ break;
+ case GRN_TABLE_NO_KEY :
+ {
+ grn_array *a = (grn_array *)table;
+ if (a->obj.header.domain) {
+ if (buf_size >= a->value_size) {
+ r = grn_array_get_value(ctx, a, id, keybuf);
+ } else {
+ r = a->value_size;
+ }
+ }
+ }
+ break;
+ }
+ }
+ GRN_API_RETURN(r);
+}
+
+int
+grn_table_get_key2(grn_ctx *ctx, grn_obj *table, grn_id id, grn_obj *bulk)
+{
+ int r = 0;
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ r = grn_hash_get_key2(ctx, (grn_hash *)table, id, bulk);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ r = grn_pat_get_key2(ctx, (grn_pat *)table, id, bulk);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ r = grn_dat_get_key2(ctx, (grn_dat *)table, id, bulk);
+ break;
+ case GRN_TABLE_NO_KEY :
+ {
+ grn_array *a = (grn_array *)table;
+ if (a->obj.header.domain) {
+ if (!grn_bulk_space(ctx, bulk, a->value_size)) {
+ char *curr = GRN_BULK_CURR(bulk);
+ r = grn_array_get_value(ctx, a, id, curr - a->value_size);
+ }
+ }
+ }
+ break;
+ }
+ }
+ GRN_API_RETURN(r);
+}
+
+static grn_rc
+grn_obj_clear_value(grn_ctx *ctx, grn_obj *obj, grn_id id)
+{
+ grn_rc rc = GRN_SUCCESS;
+ if (GRN_DB_OBJP(obj)) {
+ grn_obj buf;
+ grn_id range = DB_OBJ(obj)->range;
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, range);
+ switch (obj->header.type) {
+ case GRN_COLUMN_VAR_SIZE :
+ case GRN_COLUMN_FIX_SIZE :
+ rc = grn_obj_set_value(ctx, obj, id, &buf, GRN_OBJ_SET);
+ break;
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ return rc;
+}
+
+static void
+call_delete_hook(grn_ctx *ctx, grn_obj *table, grn_id rid, const void *key, unsigned int key_size)
+{
+ if (rid) {
+ grn_hook *hooks = DB_OBJ(table)->hooks[GRN_HOOK_DELETE];
+ if (hooks) {
+ // todo : grn_proc_ctx_open()
+ grn_obj id_, flags_, oldvalue_, value_;
+ grn_proc_ctx pctx = {{0}, hooks->proc, NULL, hooks, hooks, PROC_INIT, 4, 4};
+ GRN_UINT32_INIT(&id_, 0);
+ GRN_UINT32_INIT(&flags_, 0);
+ GRN_TEXT_INIT(&oldvalue_, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_INIT(&value_, 0);
+ GRN_TEXT_SET_REF(&oldvalue_, key, key_size);
+ GRN_UINT32_SET(ctx, &id_, rid);
+ GRN_UINT32_SET(ctx, &flags_, GRN_OBJ_SET);
+ grn_ctx_push(ctx, &id_);
+ grn_ctx_push(ctx, &oldvalue_);
+ grn_ctx_push(ctx, &value_);
+ grn_ctx_push(ctx, &flags_);
+ while (hooks) {
+ pctx.caller = NULL;
+ pctx.currh = hooks;
+ if (hooks->proc) {
+ hooks->proc->funcs[PROC_INIT](ctx, 1, &table, &pctx.user_data);
+ } else {
+ default_set_value_hook(ctx, 1, &table, &pctx.user_data);
+ }
+ if (ctx->rc) { break; }
+ hooks = hooks->next;
+ pctx.offset++;
+ }
+ }
+ }
+}
+
+static void
+clear_column_values(grn_ctx *ctx, grn_obj *table, grn_id rid)
+{
+ if (rid) {
+ grn_hash *cols;
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ if (grn_table_columns(ctx, table, "", 0, (grn_obj *)cols)) {
+ grn_id *key;
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (col) { grn_obj_clear_value(ctx, col, rid); }
+ });
+ }
+ grn_hash_close(ctx, cols);
+ }
+ }
+}
+
+static void
+delete_reference_records_in_index(grn_ctx *ctx, grn_obj *table, grn_id id,
+ grn_obj *index)
+{
+ grn_ii *ii = (grn_ii *)index;
+ grn_ii_cursor *ii_cursor = NULL;
+ grn_ii_posting *posting;
+ grn_obj source_ids;
+ unsigned int i, n_ids;
+ grn_obj sources;
+ grn_bool have_reference_source = GRN_FALSE;
+
+ GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR);
+ GRN_PTR_INIT(&sources, GRN_OBJ_VECTOR, 0);
+
+ grn_obj_get_info(ctx, index, GRN_INFO_SOURCE, &source_ids);
+ n_ids = GRN_BULK_VSIZE(&source_ids) / sizeof(grn_id);
+ if (n_ids == 0) {
+ goto exit;
+ }
+
+ for (i = 0; i < n_ids; i++) {
+ grn_id source_id;
+ grn_obj *source;
+
+ source_id = GRN_UINT32_VALUE_AT(&source_ids, i);
+ source = grn_ctx_at(ctx, source_id);
+ if (grn_obj_get_range(ctx, source) == index->header.domain) {
+ GRN_PTR_PUT(ctx, &sources, source);
+ have_reference_source = GRN_TRUE;
+ } else {
+ grn_obj_unlink(ctx, source);
+ GRN_PTR_PUT(ctx, &sources, NULL);
+ }
+ }
+
+ if (!have_reference_source) {
+ goto exit;
+ }
+
+ ii_cursor = grn_ii_cursor_open(ctx, ii, id, GRN_ID_NIL, GRN_ID_MAX,
+ ii->n_elements, 0);
+ if (!ii_cursor) {
+ goto exit;
+ }
+
+ while ((posting = grn_ii_cursor_next(ctx, ii_cursor))) {
+ grn_obj *source = GRN_PTR_VALUE_AT(&sources, posting->sid - 1);
+ if (!source) {
+ continue;
+ }
+ switch (source->header.type) {
+ case GRN_COLUMN_VAR_SIZE :
+ switch (source->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) {
+ case GRN_OBJ_COLUMN_SCALAR :
+ grn_obj_clear_value(ctx, source, posting->rid);
+ break;
+ case GRN_OBJ_COLUMN_VECTOR :
+ {
+ grn_obj value;
+ grn_obj new_value;
+ GRN_TEXT_INIT(&value, 0);
+ grn_obj_get_value(ctx, source, posting->rid, &value);
+ if (value.header.type == GRN_UVECTOR) {
+ int i, n_ids;
+ GRN_RECORD_INIT(&new_value, GRN_OBJ_VECTOR, value.header.domain);
+ n_ids = GRN_BULK_VSIZE(&value) / sizeof(grn_id);
+ for (i = 0; i < n_ids; i++) {
+ grn_id reference_id = GRN_RECORD_VALUE_AT(&value, i);
+ if (reference_id == id) {
+ continue;
+ }
+ GRN_RECORD_PUT(ctx, &new_value, reference_id);
+ }
+ } else {
+ unsigned int i, n_elements;
+ GRN_TEXT_INIT(&new_value, GRN_OBJ_VECTOR);
+ n_elements = grn_vector_size(ctx, &value);
+ for (i = 0; i < n_elements; i++) {
+ const char *content;
+ unsigned int content_length;
+ unsigned int weight;
+ grn_id domain;
+ content_length =
+ grn_vector_get_element(ctx, &value, i,
+ &content, &weight, &domain);
+ if (grn_table_get(ctx, table, content, content_length) == id) {
+ continue;
+ }
+ grn_vector_add_element(ctx, &new_value, content, content_length,
+ weight, domain);
+ }
+ }
+ grn_obj_set_value(ctx, source, posting->rid, &new_value,
+ GRN_OBJ_SET);
+ GRN_OBJ_FIN(ctx, &new_value);
+ GRN_OBJ_FIN(ctx, &value);
+ }
+ break;
+ }
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ grn_obj_clear_value(ctx, source, posting->rid);
+ break;
+ }
+ }
+
+exit:
+ if (ii_cursor) {
+ grn_ii_cursor_close(ctx, ii_cursor);
+ }
+ grn_obj_unlink(ctx, &source_ids);
+ {
+ int i, n_sources;
+ n_sources = GRN_BULK_VSIZE(&sources) / sizeof(grn_obj *);
+ for (i = 0; i < n_sources; i++) {
+ grn_obj *source = GRN_PTR_VALUE_AT(&sources, i);
+ grn_obj_unlink(ctx, source);
+ }
+ grn_obj_unlink(ctx, &sources);
+ }
+}
+
+static grn_rc
+delete_reference_records(grn_ctx *ctx, grn_obj *table, grn_id id)
+{
+ grn_hash *cols;
+ grn_id *key;
+
+ cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY);
+ if (!cols) {
+ return ctx->rc;
+ }
+
+ if (!grn_table_columns(ctx, table, "", 0, (grn_obj *)cols)) {
+ grn_hash_close(ctx, cols);
+ return ctx->rc;
+ }
+
+ GRN_HASH_EACH(ctx, cols, tid, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (!col) {
+ continue;
+ }
+ if (col->header.type != GRN_COLUMN_INDEX) {
+ grn_obj_unlink(ctx, col);
+ continue;
+ }
+ delete_reference_records_in_index(ctx, table, id, col);
+ grn_obj_unlink(ctx, col);
+ if (ctx->rc != GRN_SUCCESS) {
+ break;
+ }
+ });
+
+ grn_hash_close(ctx, cols);
+
+ return ctx->rc;
+}
+
+grn_rc
+grn_table_delete(grn_ctx *ctx, grn_obj *table, const void *key, unsigned int key_size)
+{
+ grn_id rid = GRN_ID_NIL;
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (table) {
+ if (key && key_size) { rid = grn_table_get(ctx, table, key, key_size); }
+ if (rid) {
+ rc = delete_reference_records(ctx, table, rid);
+ if (rc != GRN_SUCCESS) {
+ goto exit;
+ }
+ call_delete_hook(ctx, table, rid, key, key_size);
+ clear_column_values(ctx, table, rid);
+ switch (table->header.type) {
+ case GRN_DB :
+ /* todo : delete tables and columns from db */
+ break;
+ case GRN_TABLE_PAT_KEY :
+ WITH_NORMALIZE((grn_pat *)table, key, key_size, {
+ grn_pat *pat = (grn_pat *)table;
+ if (pat->io && !(pat->io->flags & GRN_IO_TEMPORARY)) {
+ if (!(rc = grn_io_lock(ctx, pat->io, grn_lock_timeout))) {
+ rc = grn_pat_delete(ctx, pat, key, key_size, NULL);
+ grn_io_unlock(pat->io);
+ }
+ } else {
+ rc = grn_pat_delete(ctx, pat, key, key_size, NULL);
+ }
+ });
+ break;
+ case GRN_TABLE_DAT_KEY :
+ WITH_NORMALIZE((grn_dat *)table, key, key_size, {
+ grn_dat *dat = (grn_dat *)table;
+ if (dat->io && !(dat->io->flags & GRN_IO_TEMPORARY)) {
+ if (!(rc = grn_io_lock(ctx, dat->io, grn_lock_timeout))) {
+ rc = grn_dat_delete(ctx, dat, key, key_size, NULL);
+ grn_io_unlock(dat->io);
+ }
+ } else {
+ rc = grn_dat_delete(ctx, dat, key, key_size, NULL);
+ }
+ });
+ break;
+ case GRN_TABLE_HASH_KEY :
+ WITH_NORMALIZE((grn_hash *)table, key, key_size, {
+ grn_hash *hash = (grn_hash *)table;
+ if (hash->io && !(hash->io->flags & GRN_IO_TEMPORARY)) {
+ if (!(rc = grn_io_lock(ctx, hash->io, grn_lock_timeout))) {
+ rc = grn_hash_delete(ctx, hash, key, key_size, NULL);
+ grn_io_unlock(hash->io);
+ }
+ } else {
+ rc = grn_hash_delete(ctx, hash, key, key_size, NULL);
+ }
+ });
+ break;
+ }
+ grn_obj_touch(ctx, table, NULL);
+ }
+ }
+exit:
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+_grn_table_delete_by_id(grn_ctx *ctx, grn_obj *table, grn_id id,
+ grn_table_delete_optarg *optarg)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ if (table) {
+ const void *key;
+ unsigned int key_size;
+ if (id) {
+ rc = delete_reference_records(ctx, table, id);
+ if (rc != GRN_SUCCESS) {
+ goto exit;
+ }
+ if ((key = _grn_table_key(ctx, table, id, &key_size))) {
+ call_delete_hook(ctx, table, id, key, key_size);
+ }
+ // todo : support optarg
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ rc = grn_pat_delete_by_id(ctx, (grn_pat *)table, id, optarg);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ rc = grn_dat_delete_by_id(ctx, (grn_dat *)table, id, optarg);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ rc = grn_hash_delete_by_id(ctx, (grn_hash *)table, id, optarg);
+ break;
+ case GRN_TABLE_NO_KEY :
+ rc = grn_array_delete_by_id(ctx, (grn_array *)table, id, optarg);
+ break;
+ }
+ if (rc == GRN_SUCCESS) {
+ clear_column_values(ctx, table, id);
+ }
+ }
+ }
+exit:
+ return rc;
+}
+
+grn_rc
+grn_table_delete_by_id(grn_ctx *ctx, grn_obj *table, grn_id id)
+{
+ grn_rc rc;
+ grn_io *io;
+ GRN_API_ENTER;
+ if ((io = grn_obj_io(table)) && !(io->flags & GRN_IO_TEMPORARY)) {
+ if (!(rc = grn_io_lock(ctx, io, grn_lock_timeout))) {
+ rc = _grn_table_delete_by_id(ctx, table, id, NULL);
+ grn_io_unlock(io);
+ }
+ } else {
+ rc = _grn_table_delete_by_id(ctx, table, id, NULL);
+ }
+ grn_obj_touch(ctx, table, NULL);
+ GRN_API_RETURN(rc);
+}
+
+grn_rc grn_ii_truncate(grn_ctx *ctx, grn_ii *ii);
+grn_rc grn_ja_truncate(grn_ctx *ctx, grn_ja *ja);
+grn_rc grn_ra_truncate(grn_ctx *ctx, grn_ra *ra);
+
+grn_rc
+grn_column_truncate(grn_ctx *ctx, grn_obj *column)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (column) {
+ grn_hook *hooks;
+ switch (column->header.type) {
+ case GRN_COLUMN_INDEX :
+ rc = grn_ii_truncate(ctx, (grn_ii *)column);
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ for (hooks = DB_OBJ(column)->hooks[GRN_HOOK_SET]; hooks; hooks = hooks->next) {
+ default_set_value_hook_data *data = (void *)NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if ((rc = grn_ii_truncate(ctx, (grn_ii *)target))) { goto exit; }
+ }
+ rc = grn_ja_truncate(ctx, (grn_ja *)column);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ for (hooks = DB_OBJ(column)->hooks[GRN_HOOK_SET]; hooks; hooks = hooks->next) {
+ default_set_value_hook_data *data = (void *)NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if ((rc = grn_ii_truncate(ctx, (grn_ii *)target))) { goto exit; }
+ }
+ rc = grn_ra_truncate(ctx, (grn_ra *)column);
+ break;
+ }
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_truncate(grn_ctx *ctx, grn_obj *table)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (table) {
+ grn_hook *hooks;
+ grn_hash *cols;
+ grn_obj *tokenizer;
+ grn_obj *normalizer;
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ if (grn_table_columns(ctx, table, "", 0, (grn_obj *)cols)) {
+ grn_id *key;
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (col) { grn_column_truncate(ctx, col); }
+ });
+ }
+ grn_hash_close(ctx, cols);
+ }
+ grn_table_get_info(ctx, table, NULL, NULL, &tokenizer, &normalizer);
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ for (hooks = DB_OBJ(table)->hooks[GRN_HOOK_INSERT]; hooks; hooks = hooks->next) {
+ default_set_value_hook_data *data = (void *)NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if ((rc = grn_ii_truncate(ctx, (grn_ii *)target))) { goto exit; }
+ }
+ rc = grn_pat_truncate(ctx, (grn_pat *)table);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ for (hooks = DB_OBJ(table)->hooks[GRN_HOOK_INSERT]; hooks; hooks = hooks->next) {
+ default_set_value_hook_data *data = (void *)NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if ((rc = grn_ii_truncate(ctx, (grn_ii *)target))) { goto exit; }
+ }
+ rc = grn_dat_truncate(ctx, (grn_dat *)table);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ for (hooks = DB_OBJ(table)->hooks[GRN_HOOK_INSERT]; hooks; hooks = hooks->next) {
+ default_set_value_hook_data *data = (void *)NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if ((rc = grn_ii_truncate(ctx, (grn_ii *)target))) { goto exit; }
+ }
+ rc = grn_hash_truncate(ctx, (grn_hash *)table);
+ break;
+ case GRN_TABLE_NO_KEY :
+ rc = grn_array_truncate(ctx, (grn_array *)table);
+ break;
+ }
+ grn_obj_set_info(ctx, table, GRN_INFO_DEFAULT_TOKENIZER, tokenizer);
+ grn_obj_set_info(ctx, table, GRN_INFO_NORMALIZER, normalizer);
+ grn_obj_touch(ctx, table, NULL);
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_get_info(grn_ctx *ctx, grn_obj *table, grn_obj_flags *flags,
+ grn_encoding *encoding, grn_obj **tokenizer,
+ grn_obj **normalizer)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ if (flags) { *flags = ((grn_pat *)table)->obj.header.flags; }
+ if (encoding) { *encoding = ((grn_pat *)table)->encoding; }
+ if (tokenizer) { *tokenizer = ((grn_pat *)table)->tokenizer; }
+ if (normalizer) { *normalizer = ((grn_pat *)table)->normalizer; }
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ if (flags) { *flags = ((grn_dat *)table)->obj.header.flags; }
+ if (encoding) { *encoding = ((grn_dat *)table)->encoding; }
+ if (tokenizer) { *tokenizer = ((grn_dat *)table)->tokenizer; }
+ if (normalizer) { *normalizer = ((grn_dat *)table)->normalizer; }
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_HASH_KEY :
+ if (flags) { *flags = ((grn_hash *)table)->obj.header.flags; }
+ if (encoding) { *encoding = ((grn_hash *)table)->encoding; }
+ if (tokenizer) { *tokenizer = ((grn_hash *)table)->tokenizer; }
+ if (normalizer) { *normalizer = ((grn_hash *)table)->normalizer; }
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_NO_KEY :
+ if (flags) { *flags = 0; }
+ if (encoding) { *encoding = GRN_ENC_NONE; }
+ if (tokenizer) { *tokenizer = grn_token_uvector; }
+ if (normalizer) { *normalizer = NULL; }
+ rc = GRN_SUCCESS;
+ break;
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+unsigned int
+grn_table_size(grn_ctx *ctx, grn_obj *table)
+{
+ unsigned int n = 0;
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_DB :
+ n = grn_table_size(ctx, ((grn_db *)table)->keys);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ n = grn_pat_size(ctx, (grn_pat *)table);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ n = grn_dat_size(ctx, (grn_dat *)table);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ n = GRN_HASH_SIZE((grn_hash *)table);
+ break;
+ case GRN_TABLE_NO_KEY :
+ n = GRN_ARRAY_SIZE((grn_array *)table);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "not supported");
+ break;
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "invalid table assigned");
+ }
+ GRN_API_RETURN(n);
+}
+
+inline static void
+subrecs_push(byte *subrecs, int size, int n_subrecs, int score, void *body, int dir)
+{
+ byte *v;
+ int *c2;
+ int n = n_subrecs - 1, n2;
+ while (n) {
+ n2 = (n - 1) >> 1;
+ c2 = GRN_RSET_SUBRECS_NTH(subrecs,size,n2);
+ if (GRN_RSET_SUBRECS_CMP(score, *c2, dir) >= 0) { break; }
+ GRN_RSET_SUBRECS_COPY(subrecs,size,n,c2);
+ n = n2;
+ }
+ v = subrecs + n * (GRN_RSET_SCORE_SIZE + size);
+ *((int *)v) = score;
+ memcpy(v + GRN_RSET_SCORE_SIZE, body, size);
+}
+
+inline static void
+subrecs_replace_min(byte *subrecs, int size, int n_subrecs, int score, void *body, int dir)
+{
+ byte *v;
+ int n = 0, n1, n2, *c1, *c2;
+ for (;;) {
+ n1 = n * 2 + 1;
+ n2 = n1 + 1;
+ c1 = n1 < n_subrecs ? GRN_RSET_SUBRECS_NTH(subrecs,size,n1) : NULL;
+ c2 = n2 < n_subrecs ? GRN_RSET_SUBRECS_NTH(subrecs,size,n2) : NULL;
+ if (c1 && GRN_RSET_SUBRECS_CMP(score, *c1, dir) > 0) {
+ if (c2 &&
+ GRN_RSET_SUBRECS_CMP(score, *c2, dir) > 0 &&
+ GRN_RSET_SUBRECS_CMP(*c1, *c2, dir) > 0) {
+ GRN_RSET_SUBRECS_COPY(subrecs,size,n,c2);
+ n = n2;
+ } else {
+ GRN_RSET_SUBRECS_COPY(subrecs,size,n,c1);
+ n = n1;
+ }
+ } else {
+ if (c2 && GRN_RSET_SUBRECS_CMP(score, *c2, dir) > 0) {
+ GRN_RSET_SUBRECS_COPY(subrecs,size,n,c2);
+ n = n2;
+ } else {
+ break;
+ }
+ }
+ }
+ v = subrecs + n * (GRN_RSET_SCORE_SIZE + size);
+ memcpy(v, &score, GRN_RSET_SCORE_SIZE);
+ memcpy(v + GRN_RSET_SCORE_SIZE, body, size);
+}
+
+inline static void
+grn_table_add_subrec_inline(grn_obj *table, grn_rset_recinfo *ri, int score,
+ grn_rset_posinfo *pi, int dir)
+{
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ int limit = DB_OBJ(table)->max_n_subrecs;
+ ri->score += score;
+ ri->n_subrecs += 1;
+ if (limit) {
+ int subrec_size = DB_OBJ(table)->subrec_size;
+ int n_subrecs = GRN_RSET_N_SUBRECS(ri);
+ if (pi) {
+ byte *body = (byte *)pi + DB_OBJ(table)->subrec_offset;
+ if (limit < n_subrecs) {
+ if (GRN_RSET_SUBRECS_CMP(score, *ri->subrecs, dir) > 0) {
+ subrecs_replace_min((byte *)ri->subrecs, subrec_size, limit, score, body, dir);
+ }
+ } else {
+ subrecs_push((byte *)ri->subrecs, subrec_size, n_subrecs, score, body, dir);
+ }
+ }
+ }
+ }
+}
+
+void
+grn_table_add_subrec(grn_obj *table, grn_rset_recinfo *ri, int score,
+ grn_rset_posinfo *pi, int dir)
+{
+ grn_table_add_subrec_inline(table, ri, score, pi, dir);
+}
+
+grn_table_cursor *
+grn_table_cursor_open(grn_ctx *ctx, grn_obj *table,
+ const void *min, unsigned int min_size,
+ const void *max, unsigned int max_size,
+ int offset, int limit, int flags)
+{
+ grn_rc rc;
+ grn_table_cursor *tc = NULL;
+ unsigned int table_size;
+ if (!table) { return tc; }
+ GRN_API_ENTER;
+ table_size = grn_table_size(ctx, table);
+ if (flags & GRN_CURSOR_PREFIX) {
+ if (offset < 0) {
+ ERR(GRN_TOO_SMALL_OFFSET,
+ "can't use negative offset with GRN_CURSOR_PREFIX: %d", offset);
+ } else if (offset != 0 && offset >= table_size) {
+ ERR(GRN_TOO_LARGE_OFFSET,
+ "offset is rather than table size: offset:%d, table_size:%d",
+ offset, table_size);
+ } else {
+ if (limit < -1) {
+ ERR(GRN_TOO_SMALL_LIMIT,
+ "can't use small limit rather than -1 with GRN_CURSOR_PREFIX: %d",
+ limit);
+ } else if (limit == -1) {
+ limit = table_size;
+ }
+ }
+ } else {
+ rc = grn_normalize_offset_and_limit(ctx, table_size, &offset, &limit);
+ if (rc) {
+ ERR(rc, "grn_normalize_offset_and_limit failed");
+ }
+ }
+ if (!ctx->rc) {
+ if (table->header.type == GRN_DB) { table = ((grn_db *)table)->keys; }
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ {
+ grn_pat *pat = (grn_pat *)table;
+ WITH_NORMALIZE(pat, min, min_size, {
+ WITH_NORMALIZE(pat, max, max_size, {
+ grn_pat_cursor *pat_cursor;
+ pat_cursor = grn_pat_cursor_open(ctx, pat,
+ min, min_size,
+ max, max_size,
+ offset, limit, flags);
+ tc = (grn_table_cursor *)pat_cursor;
+ });
+ });
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ {
+ grn_dat *dat = (grn_dat *)table;
+ WITH_NORMALIZE(dat, min, min_size, {
+ WITH_NORMALIZE(dat, max, max_size, {
+ grn_dat_cursor *dat_cursor;
+ dat_cursor = grn_dat_cursor_open(ctx, dat,
+ min, min_size,
+ max, max_size,
+ offset, limit, flags);
+ tc = (grn_table_cursor *)dat_cursor;
+ });
+ });
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ {
+ grn_hash *hash = (grn_hash *)table;
+ WITH_NORMALIZE(hash, min, min_size, {
+ WITH_NORMALIZE(hash, max, max_size, {
+ grn_hash_cursor *hash_cursor;
+ hash_cursor = grn_hash_cursor_open(ctx, hash,
+ min, min_size,
+ max, max_size,
+ offset, limit, flags);
+ tc = (grn_table_cursor *)hash_cursor;
+ });
+ });
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ tc = (grn_table_cursor *)grn_array_cursor_open(ctx, (grn_array *)table,
+ GRN_ID_NIL, GRN_ID_NIL,
+ offset, limit, flags);
+ break;
+ }
+ }
+ if (tc) {
+ grn_id id = grn_obj_register(ctx, ctx->impl->db, NULL, 0);
+ DB_OBJ(tc)->header.domain = GRN_ID_NIL;
+ DB_OBJ(tc)->range = GRN_ID_NIL;
+ grn_db_obj_init(ctx, ctx->impl->db, id, DB_OBJ(tc));
+ }
+ GRN_API_RETURN(tc);
+}
+
+grn_table_cursor *
+grn_table_cursor_open_by_id(grn_ctx *ctx, grn_obj *table,
+ grn_id min, grn_id max, int flags)
+{
+ grn_table_cursor *tc = NULL;
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ tc = (grn_table_cursor *)grn_pat_cursor_open(ctx, (grn_pat *)table,
+ NULL, 0, NULL, 0, 0, -1, flags);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ tc = (grn_table_cursor *)grn_dat_cursor_open(ctx, (grn_dat *)table,
+ NULL, 0, NULL, 0, 0, -1, flags);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ tc = (grn_table_cursor *)grn_hash_cursor_open(ctx, (grn_hash *)table,
+ NULL, 0, NULL, 0, 0, -1, flags);
+ break;
+ case GRN_TABLE_NO_KEY :
+ tc = (grn_table_cursor *)grn_array_cursor_open(ctx, (grn_array *)table,
+ min, max, 0, -1, flags);
+ break;
+ }
+ }
+ GRN_API_RETURN(tc);
+}
+
+grn_rc
+grn_table_cursor_close(grn_ctx *ctx, grn_table_cursor *tc)
+{
+ grn_rc rc = GRN_SUCCESS;
+ GRN_API_ENTER;
+ if (!tc) {
+ ERR(GRN_INVALID_ARGUMENT, "tc is null");
+ rc = GRN_INVALID_ARGUMENT;
+ } else {
+ {
+ if (DB_OBJ(tc)->finalizer) {
+ DB_OBJ(tc)->finalizer(ctx, 1, (grn_obj **)&tc, &DB_OBJ(tc)->user_data);
+ }
+ if (DB_OBJ(tc)->source) {
+ GRN_FREE(DB_OBJ(tc)->source);
+ }
+ /*
+ grn_hook_entry entry;
+ for (entry = 0; entry < N_HOOK_ENTRIES; entry++) {
+ grn_hook_free(ctx, DB_OBJ(tc)->hooks[entry]);
+ }
+ */
+ grn_obj_delete_by_id(ctx, DB_OBJ(tc)->db, DB_OBJ(tc)->id, GRN_FALSE);
+ }
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ grn_pat_cursor_close(ctx, (grn_pat_cursor *)tc);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ grn_dat_cursor_close(ctx, (grn_dat_cursor *)tc);
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ grn_hash_cursor_close(ctx, (grn_hash_cursor *)tc);
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ grn_array_cursor_close(ctx, (grn_array_cursor *)tc);
+ break;
+ default :
+ rc = GRN_INVALID_ARGUMENT;
+ break;
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+inline static grn_id
+grn_table_cursor_next_inline(grn_ctx *ctx, grn_table_cursor *tc)
+{
+ grn_id id = GRN_ID_NIL;
+ if (!tc) {
+ ERR(GRN_INVALID_ARGUMENT, "tc is null");
+ } else {
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ id = grn_pat_cursor_next(ctx, (grn_pat_cursor *)tc);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ id = grn_dat_cursor_next(ctx, (grn_dat_cursor *)tc);
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ id = grn_hash_cursor_next(ctx, (grn_hash_cursor *)tc);
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ id = grn_array_cursor_next(ctx, (grn_array_cursor *)tc);
+ break;
+ case GRN_CURSOR_COLUMN_INDEX :
+ {
+ grn_posting *ip = grn_index_cursor_next(ctx, (grn_obj *)tc, NULL);
+ if (ip) { id = ip->rid; }
+ }
+ break;
+ }
+ }
+ return id;
+}
+
+grn_id
+grn_table_cursor_next(grn_ctx *ctx, grn_table_cursor *tc)
+{
+ grn_id id;
+ GRN_API_ENTER;
+ id = grn_table_cursor_next_inline(ctx, tc);
+ GRN_API_RETURN(id);
+}
+
+int
+grn_table_cursor_get_key(grn_ctx *ctx, grn_table_cursor *tc, void **key)
+{
+ int len = 0;
+ GRN_API_ENTER;
+ if (!tc) {
+ ERR(GRN_INVALID_ARGUMENT, "tc is null");
+ } else {
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ len = grn_pat_cursor_get_key(ctx, (grn_pat_cursor *)tc, key);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ len = grn_dat_cursor_get_key(ctx, (grn_dat_cursor *)tc, (const void **)key);
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ len = grn_hash_cursor_get_key(ctx, (grn_hash_cursor *)tc, key);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "invalid type %d", tc->header.type);
+ break;
+ }
+ }
+ GRN_API_RETURN(len);
+}
+
+inline static int
+grn_table_cursor_get_value_inline(grn_ctx *ctx, grn_table_cursor *tc, void **value)
+{
+ int len = 0;
+ if (!tc) {
+ ERR(GRN_INVALID_ARGUMENT, "tc is null");
+ } else {
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ len = grn_pat_cursor_get_value(ctx, (grn_pat_cursor *)tc, value);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ *value = NULL;
+ len = 0;
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ len = grn_hash_cursor_get_value(ctx, (grn_hash_cursor *)tc, value);
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ len = grn_array_cursor_get_value(ctx, (grn_array_cursor *)tc, value);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "invalid type %d", tc->header.type);
+ break;
+ }
+ }
+ return len;
+}
+
+int
+grn_table_cursor_get_value(grn_ctx *ctx, grn_table_cursor *tc, void **value)
+{
+ int len;
+ GRN_API_ENTER;
+ len = grn_table_cursor_get_value_inline(ctx, tc, value);
+ GRN_API_RETURN(len);
+}
+
+grn_rc
+grn_table_cursor_set_value(grn_ctx *ctx, grn_table_cursor *tc,
+ const void *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (!tc) {
+ ERR(GRN_INVALID_ARGUMENT, "tc is null");
+ } else {
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ rc = grn_pat_cursor_set_value(ctx, (grn_pat_cursor *)tc, value, flags);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ rc = GRN_OPERATION_NOT_SUPPORTED;
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ rc = grn_hash_cursor_set_value(ctx, (grn_hash_cursor *)tc, value, flags);
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ rc = grn_array_cursor_set_value(ctx, (grn_array_cursor *)tc, value, flags);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "invalid type %d", tc->header.type);
+ break;
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_cursor_delete(grn_ctx *ctx, grn_table_cursor *tc)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (!tc) {
+ ERR(GRN_INVALID_ARGUMENT, "tc is null");
+ } else {
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ rc = grn_pat_cursor_delete(ctx, (grn_pat_cursor *)tc, NULL);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ rc = GRN_OPERATION_NOT_SUPPORTED;
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ rc = grn_hash_cursor_delete(ctx, (grn_hash_cursor *)tc, NULL);
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ rc = grn_array_cursor_delete(ctx, (grn_array_cursor *)tc, NULL);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "invalid type %d", tc->header.type);
+ break;
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_obj *
+grn_table_cursor_table(grn_ctx *ctx, grn_table_cursor *tc)
+{
+ grn_obj *obj = NULL;
+ GRN_API_ENTER;
+ if (!tc) {
+ ERR(GRN_INVALID_ARGUMENT, "tc is null");
+ } else {
+ switch (tc->header.type) {
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ obj = (grn_obj *)(((grn_pat_cursor *)tc)->pat);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ obj = (grn_obj *)(((grn_dat_cursor *)tc)->dat);
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ obj = (grn_obj *)(((grn_hash_cursor *)tc)->hash);
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ obj = (grn_obj *)(((grn_array_cursor *)tc)->array);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "invalid type %d", tc->header.type);
+ break;
+ }
+ }
+ GRN_API_RETURN(obj);
+}
+
+typedef struct {
+ grn_db_obj obj;
+ grn_obj *index;
+ grn_table_cursor *tc;
+ grn_ii_cursor *iic;
+ grn_id tid;
+ grn_id rid_min;
+ grn_id rid_max;
+ int flags;
+} grn_index_cursor;
+
+grn_obj *
+grn_index_cursor_open(grn_ctx *ctx, grn_table_cursor *tc,
+ grn_obj *index, grn_id rid_min, grn_id rid_max, int flags)
+{
+ grn_index_cursor *ic = NULL;
+ GRN_API_ENTER;
+ if (tc && (ic = GRN_MALLOCN(grn_index_cursor, 1))) {
+ ic->tc = tc;
+ ic->index = index;
+ ic->iic = NULL;
+ ic->tid = GRN_ID_NIL;
+ ic->rid_min = rid_min;
+ ic->rid_max = rid_max;
+ ic->flags = flags;
+ GRN_DB_OBJ_SET_TYPE(ic, GRN_CURSOR_COLUMN_INDEX);
+ {
+ grn_id id = grn_obj_register(ctx, ctx->impl->db, NULL, 0);
+ DB_OBJ(ic)->header.domain = GRN_ID_NIL;
+ DB_OBJ(ic)->range = GRN_ID_NIL;
+ grn_db_obj_init(ctx, ctx->impl->db, id, DB_OBJ(ic));
+ }
+ }
+ GRN_API_RETURN((grn_obj *)ic);
+}
+
+grn_posting *
+grn_index_cursor_next(grn_ctx *ctx, grn_obj *c, grn_id *tid)
+{
+ grn_ii_posting *ip = NULL;
+ grn_index_cursor *ic = (grn_index_cursor *)c;
+ GRN_API_ENTER;
+ if (ic->iic) {
+ if (ic->flags & GRN_OBJ_WITH_POSITION) {
+ ip = grn_ii_cursor_next_pos(ctx, ic->iic);
+ while (!ip && grn_ii_cursor_next(ctx, ic->iic)) {
+ ip = grn_ii_cursor_next_pos(ctx, ic->iic);
+ break;
+ }
+ } else {
+ ip = grn_ii_cursor_next(ctx, ic->iic);
+ }
+ }
+ if (!ip) {
+ while ((ic->tid = grn_table_cursor_next_inline(ctx, ic->tc))) {
+ grn_ii *ii = (grn_ii *)ic->index;
+ if (ic->iic) { grn_ii_cursor_close(ctx, ic->iic); }
+ if ((ic->iic = grn_ii_cursor_open(ctx, ii, ic->tid,
+ ic->rid_min, ic->rid_max,
+ ii->n_elements, ic->flags))) {
+ ip = grn_ii_cursor_next(ctx, ic->iic);
+ if (ip && ic->flags & GRN_OBJ_WITH_POSITION) {
+ ip = grn_ii_cursor_next_pos(ctx, ic->iic);
+ }
+ if (ip) {
+ break;
+ }
+ }
+ }
+ }
+ if (tid) { *tid = ic->tid; }
+ GRN_API_RETURN((grn_posting *)ip);
+}
+
+grn_rc
+grn_table_search(grn_ctx *ctx, grn_obj *table, const void *key, uint32_t key_size,
+ grn_operator mode, grn_obj *res, grn_operator op)
+{
+ grn_rc rc = GRN_SUCCESS;
+ GRN_API_ENTER;
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ {
+ grn_pat *pat = (grn_pat *)table;
+ WITH_NORMALIZE(pat, key, key_size, {
+ switch (mode) {
+ case GRN_OP_EXACT :
+ {
+ grn_id id = grn_pat_get(ctx, pat, key, key_size, NULL);
+ if (id) { grn_table_add(ctx, res, &id, sizeof(grn_id), NULL); }
+ }
+ // todo : support op;
+ break;
+ case GRN_OP_LCP :
+ {
+ grn_id id = grn_pat_lcp_search(ctx, pat, key, key_size);
+ if (id) { grn_table_add(ctx, res, &id, sizeof(grn_id), NULL); }
+ }
+ // todo : support op;
+ break;
+ case GRN_OP_SUFFIX :
+ rc = grn_pat_suffix_search(ctx, pat, key, key_size, (grn_hash *)res);
+ // todo : support op;
+ break;
+ case GRN_OP_PREFIX :
+ rc = grn_pat_prefix_search(ctx, pat, key, key_size, (grn_hash *)res);
+ // todo : support op;
+ break;
+ case GRN_OP_TERM_EXTRACT :
+ {
+ int len;
+ grn_id tid;
+ const char *sp = key;
+ const char *se = sp + key_size;
+ for (; sp < se; sp += len) {
+ if ((tid = grn_pat_lcp_search(ctx, pat, sp, se - sp))) {
+ grn_table_add(ctx, res, &tid, sizeof(grn_id), NULL);
+ /* todo : nsubrec++ if GRN_OBJ_TABLE_SUBSET assigned */
+ }
+ if (!(len = grn_charlen(ctx, sp, se))) { break; }
+ }
+ }
+ // todo : support op;
+ break;
+ default :
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc, "invalid mode %d", mode);
+ }
+ });
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ {
+ grn_dat *dat = (grn_dat *)table;
+ WITH_NORMALIZE(dat, key, key_size, {
+ switch (mode) {
+ case GRN_OP_EXACT :
+ {
+ grn_id id = grn_dat_get(ctx, dat, key, key_size, NULL);
+ if (id) { grn_table_add(ctx, res, &id, sizeof(grn_id), NULL); }
+ }
+ break;
+ case GRN_OP_PREFIX :
+ {
+ grn_dat_cursor *dc = grn_dat_cursor_open(ctx, dat, key, key_size, NULL, 0,
+ 0, -1, GRN_CURSOR_PREFIX);
+ if (dc) {
+ grn_id id;
+ while ((id = grn_dat_cursor_next(ctx, dc))) {
+ grn_table_add(ctx, res, &id, sizeof(grn_id), NULL);
+ }
+ grn_dat_cursor_close(ctx, dc);
+ }
+ }
+ break;
+ case GRN_OP_LCP :
+ {
+ grn_id id = grn_dat_lcp_search(ctx, dat, key, key_size);
+ if (id) { grn_table_add(ctx, res, &id, sizeof(grn_id), NULL); }
+ }
+ break;
+ case GRN_OP_TERM_EXTRACT :
+ {
+ int len;
+ grn_id tid;
+ const char *sp = key;
+ const char *se = sp + key_size;
+ for (; sp < se; sp += len) {
+ if ((tid = grn_dat_lcp_search(ctx, dat, sp, se - sp))) {
+ grn_table_add(ctx, res, &tid, sizeof(grn_id), NULL);
+ /* todo : nsubrec++ if GRN_OBJ_TABLE_SUBSET assigned */
+ }
+ if (!(len = grn_charlen(ctx, sp, se))) { break; }
+ }
+ }
+ // todo : support op;
+ break;
+ default :
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc, "invalid mode %d", mode);
+ }
+ });
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ {
+ grn_hash *hash = (grn_hash *)table;
+ grn_id id = GRN_ID_NIL;
+ WITH_NORMALIZE(hash, key, key_size, {
+ id = grn_hash_get(ctx, hash, key, key_size, NULL);
+ });
+ if (id) { grn_table_add(ctx, res, &id, sizeof(grn_id), NULL); }
+ }
+ break;
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_id
+grn_table_next(grn_ctx *ctx, grn_obj *table, grn_id id)
+{
+ grn_id r = GRN_ID_NIL;
+ GRN_API_ENTER;
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ r = grn_pat_next(ctx, (grn_pat *)table, id);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ r = grn_dat_next(ctx, (grn_dat *)table, id);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ r = grn_hash_next(ctx, (grn_hash *)table, id);
+ break;
+ case GRN_TABLE_NO_KEY :
+ r = grn_array_next(ctx, (grn_array *)table, id);
+ break;
+ }
+ }
+ GRN_API_RETURN(r);
+}
+
+grn_rc
+grn_accessor_resolve(grn_ctx *ctx, grn_obj *accessor, int deep,
+ grn_obj *base_res, grn_obj **res,
+ grn_search_optarg *optarg)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_accessor *a;
+ grn_obj accessor_stack;
+ int i, n_accessors;
+ grn_obj *current_res = base_res;
+
+ GRN_PTR_INIT(&accessor_stack, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ n_accessors = 0;
+ for (a = (grn_accessor *)accessor; a; a = a->next) {
+ if (deep == n_accessors) {
+ break;
+ }
+ GRN_PTR_PUT(ctx, &accessor_stack, a);
+ n_accessors++;
+ }
+
+ for (i = n_accessors; i > 0; i--) {
+ grn_obj *index;
+ grn_operator index_op = GRN_OP_MATCH;
+
+ a = (grn_accessor *)GRN_PTR_VALUE_AT(&accessor_stack, i - 1);
+ if (grn_column_index(ctx, a->obj, index_op, &index, 1, NULL) == 0) {
+ rc = GRN_INVALID_ARGUMENT;
+ break;
+ }
+
+ {
+ grn_id *tid;
+ grn_obj *domain;
+ grn_obj *next_res;
+ grn_search_optarg next_optarg;
+ grn_rset_recinfo *recinfo;
+ if (optarg) {
+ next_optarg = *optarg;
+ next_optarg.mode = GRN_OP_EXACT;
+ } else {
+ memset(&next_optarg, 0, sizeof(grn_search_optarg));
+ }
+ {
+ grn_obj *range = grn_ctx_at(ctx, DB_OBJ(index)->range);
+ next_res = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC,
+ range, NULL);
+ rc = ctx->rc;
+ grn_obj_unlink(ctx, range);
+ if (!next_res) {
+ if (current_res != base_res) {
+ grn_obj_unlink(ctx, current_res);
+ }
+ break;
+ }
+ }
+ domain = grn_ctx_at(ctx, index->header.domain);
+ GRN_HASH_EACH(ctx, (grn_hash *)current_res, id, &tid, NULL, &recinfo, {
+ next_optarg.weight_vector = NULL;
+ next_optarg.vector_size = recinfo->score;
+ if (domain->header.type == GRN_TABLE_NO_KEY) {
+ rc = grn_ii_sel(ctx, (grn_ii *)index,
+ (const char *)tid, sizeof(grn_id),
+ (grn_hash *)next_res, GRN_OP_OR,
+ &next_optarg);
+ } else {
+ char key[GRN_TABLE_MAX_KEY_SIZE];
+ int key_len;
+ key_len = grn_table_get_key(ctx, domain, *tid,
+ key, GRN_TABLE_MAX_KEY_SIZE);
+ rc = grn_ii_sel(ctx, (grn_ii *)index, key, key_len,
+ (grn_hash *)next_res, GRN_OP_OR,
+ &next_optarg);
+ }
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+ });
+ grn_obj_unlink(ctx, domain);
+ if (current_res != base_res) {
+ grn_obj_unlink(ctx, current_res);
+ }
+ if (rc != GRN_SUCCESS) {
+ grn_obj_unlink(ctx, next_res);
+ break;
+ }
+ current_res = next_res;
+ }
+ }
+
+ if (rc == GRN_SUCCESS && current_res != base_res) {
+ *res = current_res;
+ } else {
+ *res = NULL;
+ if (rc == GRN_SUCCESS) {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ }
+
+ GRN_OBJ_FIN(ctx, &accessor_stack);
+ return rc;
+}
+
+static inline grn_rc
+grn_obj_search_accessor(grn_ctx *ctx, grn_obj *obj, grn_obj *query,
+ grn_obj *res, grn_operator op, grn_search_optarg *optarg)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_accessor *a;
+ grn_obj *last_obj = NULL;
+ int n_accessors;
+
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ if (!a->next) {
+ last_obj = a->obj;
+ }
+ }
+ n_accessors = 0;
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ n_accessors++;
+ if (GRN_OBJ_INDEX_COLUMNP(a->obj)) {
+ break;
+ }
+ }
+
+ {
+ grn_obj *index;
+ grn_operator index_op = GRN_OP_MATCH;
+ if (optarg && optarg->mode != GRN_OP_EXACT) {
+ index_op = optarg->mode;
+ }
+ if (grn_column_index(ctx, last_obj, index_op, &index, 1, NULL) == 0) {
+ rc = GRN_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ if (n_accessors == 1) {
+ rc = grn_obj_search(ctx, index, query, res, op, optarg);
+ } else {
+ grn_obj *base_res;
+ grn_obj *resolve_res = NULL;
+ grn_obj *range = grn_ctx_at(ctx, DB_OBJ(index)->range);
+ base_res = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC,
+ range,
+ NULL);
+ rc = ctx->rc;
+ grn_obj_unlink(ctx, range);
+ if (!base_res) {
+ goto exit;
+ }
+ rc = grn_obj_search(ctx, index, query, base_res, GRN_OP_OR, optarg);
+ if (rc != GRN_SUCCESS) {
+ grn_obj_unlink(ctx, base_res);
+ goto exit;
+ }
+ rc = grn_accessor_resolve(ctx, obj, n_accessors - 1, base_res,
+ &resolve_res, optarg);
+ if (resolve_res) {
+ grn_id *record_id;
+ grn_rset_recinfo *recinfo;
+ GRN_HASH_EACH(ctx, (grn_hash *)resolve_res, id, &record_id, NULL,
+ &recinfo, {
+ grn_ii_posting posting;
+ posting.rid = *record_id;
+ posting.sid = 1;
+ posting.pos = 0;
+ posting.weight = recinfo->score - 1;
+ grn_ii_posting_add(ctx, &posting, (grn_hash *)res, op);
+ });
+ grn_ii_resolve_sel_and(ctx, (grn_hash *)res, op);
+ grn_obj_unlink(ctx, resolve_res);
+ }
+ grn_obj_unlink(ctx, base_res);
+ }
+ }
+
+exit :
+ return rc;
+}
+
+static grn_rc
+grn_obj_search_column_index_by_id(grn_ctx *ctx, grn_obj *obj,
+ grn_id tid,
+ grn_obj *res, grn_operator op,
+ grn_search_optarg *optarg)
+{
+ grn_ii_cursor *c = grn_ii_cursor_open(ctx, (grn_ii *)obj, tid,
+ GRN_ID_NIL, GRN_ID_MAX, 1, 0);
+ if (c) {
+ grn_ii_posting *pos;
+ grn_hash *s = (grn_hash *)res;
+ while ((pos = grn_ii_cursor_next(ctx, c))) {
+ /* todo: support orgarg(op)
+ res_add(ctx, s, (grn_rset_posinfo *) pos,
+ get_weight(ctx, s, pos->rid, pos->sid, wvm, optarg), op);
+ */
+ grn_hash_add(ctx, s, pos, s->key_size, NULL, NULL);
+ }
+ grn_ii_cursor_close(ctx, c);
+ }
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_obj_search_column_index_by_key(grn_ctx *ctx, grn_obj *obj,
+ grn_obj *query,
+ grn_obj *res, grn_operator op,
+ grn_search_optarg *optarg)
+{
+ grn_rc rc;
+ unsigned int key_type = GRN_ID_NIL;
+ const char *key;
+ unsigned int key_len;
+ grn_obj *table;
+ grn_obj casted_query;
+ grn_bool need_cast = GRN_FALSE;
+
+ table = grn_ctx_at(ctx, obj->header.domain);
+ if (table) {
+ key_type = table->header.domain;
+ need_cast = (query->header.domain != key_type);
+ grn_obj_unlink(ctx, table);
+ }
+ if (need_cast) {
+ GRN_OBJ_INIT(&casted_query, GRN_BULK, 0, key_type);
+ rc = grn_obj_cast(ctx, query, &casted_query, GRN_FALSE);
+ if (rc == GRN_SUCCESS) {
+ key = GRN_BULK_HEAD(&casted_query);
+ key_len = GRN_BULK_VSIZE(&casted_query);
+ }
+ } else {
+ rc = GRN_SUCCESS;
+ key = GRN_BULK_HEAD(query);
+ key_len = GRN_BULK_VSIZE(query);
+ }
+ if (rc == GRN_SUCCESS) {
+ rc = grn_ii_sel(ctx, (grn_ii *)obj, key, key_len,
+ (grn_hash *)res, op, optarg);
+ }
+ if (need_cast) {
+ GRN_OBJ_FIN(ctx, &casted_query);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_obj_search_column_index(grn_ctx *ctx, grn_obj *obj, grn_obj *query,
+ grn_obj *res, grn_operator op,
+ grn_search_optarg *optarg)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+
+ if (DB_OBJ(obj)->range == res->header.domain) {
+ switch (query->header.type) {
+ case GRN_BULK :
+ if (query->header.domain == obj->header.domain &&
+ GRN_BULK_VSIZE(query) == sizeof(grn_id)) {
+ grn_id tid = GRN_RECORD_VALUE(query);
+ rc = grn_obj_search_column_index_by_id(ctx, obj, tid, res, op, optarg);
+ } else {
+ rc = grn_obj_search_column_index_by_key(ctx, obj, query,
+ res, op, optarg);
+ }
+ break;
+ case GRN_QUERY :
+ rc = GRN_FUNCTION_NOT_IMPLEMENTED;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+grn_rc
+grn_obj_search(grn_ctx *ctx, grn_obj *obj, grn_obj *query,
+ grn_obj *res, grn_operator op, grn_search_optarg *optarg)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (GRN_ACCESSORP(obj)) {
+ rc = grn_obj_search_accessor(ctx, obj, query, res, op, optarg);
+ } else if (GRN_DB_OBJP(obj)) {
+ switch (obj->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_HASH_KEY :
+ {
+ const void *key = GRN_BULK_HEAD(query);
+ uint32_t key_size = GRN_BULK_VSIZE(query);
+ grn_operator mode = optarg ? optarg->mode : GRN_OP_EXACT;
+ if (key && key_size) {
+ rc = grn_table_search(ctx, obj, key, key_size, mode, res, op);
+ }
+ }
+ break;
+ case GRN_COLUMN_INDEX :
+ rc = grn_obj_search_column_index(ctx, obj, query, res, op, optarg);
+ break;
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+#define GRN_TABLE_GROUP_BY_KEY 0
+#define GRN_TABLE_GROUP_BY_VALUE 1
+#define GRN_TABLE_GROUP_BY_COLUMN_VALUE 2
+
+#define GRN_TABLE_GROUP_FILTER_PREFIX 0
+#define GRN_TABLE_GROUP_FILTER_SUFFIX (1L<<2)
+
+static int
+accelerated_table_group(grn_ctx *ctx, grn_obj *table, grn_obj *key, grn_obj *res)
+{
+ if (key->header.type == GRN_ACCESSOR) {
+ grn_accessor *a = (grn_accessor *)key;
+ if (a->action == GRN_ACCESSOR_GET_KEY &&
+ a->next && a->next->action == GRN_ACCESSOR_GET_COLUMN_VALUE &&
+ a->next->obj && !a->next->next) {
+ grn_obj *range = grn_ctx_at(ctx, grn_obj_get_range(ctx, key));
+ int idp = GRN_OBJ_TABLEP(range);
+ grn_table_cursor *tc;
+ if ((tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, 0))) {
+ switch (a->next->obj->header.type) {
+ case GRN_COLUMN_FIX_SIZE :
+ {
+ grn_id id;
+ grn_ra *ra = (grn_ra *)a->next->obj;
+ unsigned int element_size = (ra)->header->element_size;
+ grn_ra_cache cache;
+ GRN_RA_CACHE_INIT(ra, &cache);
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ void *v, *value;
+ grn_id *id_;
+ uint32_t key_size;
+ grn_rset_recinfo *ri = NULL;
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+ id_ = (grn_id *)_grn_table_key(ctx, table, id, &key_size);
+ v = grn_ra_ref_cache(ctx, ra, *id_, &cache);
+ if (idp && *((grn_id *)v) &&
+ grn_table_at(ctx, range, *((grn_id *)v)) == GRN_ID_NIL) {
+ continue;
+ }
+ if ((!idp || *((grn_id *)v)) &&
+ grn_table_add_v_inline(ctx, res, v, element_size, &value, NULL)) {
+ grn_table_add_subrec_inline(res, value, ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0);
+ }
+ }
+ GRN_RA_CACHE_FIN(ra, &cache);
+ }
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ if (idp) { /* todo : support other type */
+ grn_id id;
+ grn_ja *ja = (grn_ja *)a->next->obj;
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ grn_io_win jw;
+ unsigned int len = 0;
+ void *value;
+ grn_id *v, *id_;
+ uint32_t key_size;
+ grn_rset_recinfo *ri = NULL;
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+ id_ = (grn_id *)_grn_table_key(ctx, table, id, &key_size);
+ if ((v = grn_ja_ref(ctx, ja, *id_, &jw, &len))) {
+ while (len) {
+ if ((*v != GRN_ID_NIL) &&
+ grn_table_add_v_inline(ctx, res, v, sizeof(grn_id), &value, NULL)) {
+ grn_table_add_subrec_inline(res, value, ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0);
+ }
+ v++;
+ len -= sizeof(grn_id);
+ }
+ grn_ja_unref(ctx, &jw);
+ }
+ }
+ } else {
+ return 0;
+ }
+ break;
+ default :
+ return 0;
+ }
+ grn_table_cursor_close(ctx, tc);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+grn_rc
+grn_table_group_with_range_gap(grn_ctx *ctx, grn_obj *table,
+ grn_table_sort_key *group_key,
+ grn_obj *res, uint32_t range_gap)
+{
+ grn_obj *key = group_key->key;
+ if (key->header.type == GRN_ACCESSOR) {
+ grn_accessor *a = (grn_accessor *)key;
+ if (a->action == GRN_ACCESSOR_GET_KEY &&
+ a->next && a->next->action == GRN_ACCESSOR_GET_COLUMN_VALUE &&
+ a->next->obj && !a->next->next) {
+ grn_obj *range = grn_ctx_at(ctx, grn_obj_get_range(ctx, key));
+ int idp = GRN_OBJ_TABLEP(range);
+ grn_table_cursor *tc;
+ if ((tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL,
+ 0, 0, -1, 0))) {
+ switch (a->next->obj->header.type) {
+ case GRN_COLUMN_FIX_SIZE :
+ {
+ grn_id id;
+ grn_ra *ra = (grn_ra *)a->next->obj;
+ unsigned int element_size = (ra)->header->element_size;
+ grn_ra_cache cache;
+ GRN_RA_CACHE_INIT(ra, &cache);
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ void *v, *value;
+ grn_id *id_;
+ uint32_t key_size;
+ grn_rset_recinfo *ri = NULL;
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+ id_ = (grn_id *)_grn_table_key(ctx, table, id, &key_size);
+ v = grn_ra_ref_cache(ctx, ra, *id_, &cache);
+ if (idp && *((grn_id *)v) &&
+ grn_table_at(ctx, range, *((grn_id *)v)) == GRN_ID_NIL) {
+ continue;
+ }
+ if ((!idp || *((grn_id *)v))) {
+ grn_id id;
+ if (element_size == sizeof(uint32_t)) {
+ uint32_t quantized = (*(uint32_t *)v);
+ quantized -= quantized % range_gap;
+ id = grn_table_add_v_inline(ctx, res, &quantized,
+ element_size, &value, NULL);
+ } else {
+ id = grn_table_add_v_inline(ctx, res, v,
+ element_size, &value, NULL);
+ }
+ if (id) {
+ grn_table_add_subrec_inline(res, value,
+ ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0);
+ }
+ }
+ }
+ GRN_RA_CACHE_FIN(ra, &cache);
+ }
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ if (idp) { /* todo : support other type */
+ grn_id id;
+ grn_ja *ja = (grn_ja *)a->next->obj;
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ grn_io_win jw;
+ unsigned int len = 0;
+ void *value;
+ grn_id *v, *id_;
+ uint32_t key_size;
+ grn_rset_recinfo *ri = NULL;
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+ id_ = (grn_id *)_grn_table_key(ctx, table, id, &key_size);
+ if ((v = grn_ja_ref(ctx, ja, *id_, &jw, &len))) {
+ while (len) {
+ if ((*v != GRN_ID_NIL) &&
+ grn_table_add_v_inline(ctx, res, v, sizeof(grn_id), &value, NULL)) {
+ grn_table_add_subrec_inline(res, value, ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0);
+ }
+ v++;
+ len -= sizeof(grn_id);
+ }
+ grn_ja_unref(ctx, &jw);
+ }
+ }
+ } else {
+ return 0;
+ }
+ break;
+ default :
+ return 0;
+ }
+ grn_table_cursor_close(ctx, tc);
+ GRN_TABLE_GROUPED_ON(res);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+grn_rc
+grn_table_group(grn_ctx *ctx, grn_obj *table,
+ grn_table_sort_key *keys, int n_keys,
+ grn_table_group_result *results, int n_results)
+{
+ grn_rc rc = GRN_SUCCESS;
+ if (!table || !n_keys || !n_results) {
+ ERR(GRN_INVALID_ARGUMENT, "table or n_keys or n_results is void");
+ return GRN_INVALID_ARGUMENT;
+ }
+ GRN_API_ENTER;
+ {
+ int k, r;
+ void *key;
+ grn_obj bulk;
+ grn_table_cursor *tc;
+ grn_table_sort_key *kp;
+ grn_table_group_result *rp;
+ for (k = 0, kp = keys; k < n_keys; k++, kp++) {
+ if ((kp->flags & GRN_TABLE_GROUP_BY_COLUMN_VALUE) && !kp->key) {
+ ERR(GRN_INVALID_ARGUMENT, "column missing in (%d)", k);
+ goto exit;
+ }
+ }
+ for (r = 0, rp = results; r < n_results; r++, rp++) {
+ if (!rp->table) {
+ ERR(GRN_INVALID_ARGUMENT, "table missing in (%d)", r);
+ goto exit;
+ }
+ }
+ GRN_TEXT_INIT(&bulk, 0);
+ if (n_keys == 1 && n_results == 1) {
+ if (!accelerated_table_group(ctx, table, keys->key, results->table)) {
+ if ((tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ grn_obj *range = grn_ctx_at(ctx, grn_obj_get_range(ctx, keys->key));
+ int idp = GRN_OBJ_TABLEP(range);
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ void *value;
+ grn_rset_recinfo *ri = NULL;
+ GRN_BULK_REWIND(&bulk);
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+ grn_obj_get_value(ctx, keys->key, id, &bulk);
+ switch (bulk.header.type) {
+ case GRN_UVECTOR :
+ {
+ // todo : support objects except grn_id
+ grn_id *v = (grn_id *)GRN_BULK_HEAD(&bulk);
+ grn_id *ve = (grn_id *)GRN_BULK_CURR(&bulk);
+ while (v < ve) {
+ if ((*v != GRN_ID_NIL) &&
+ grn_table_add_v_inline(ctx, results->table, v, sizeof(grn_id), &value, NULL)) {
+ grn_table_add_subrec_inline(results->table, value, ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0);
+ }
+ v++;
+ }
+ }
+ break;
+ case GRN_VECTOR :
+ ERR(GRN_OPERATION_NOT_SUPPORTED, "sorry.. not implemented yet");
+ /* todo */
+ break;
+ case GRN_BULK :
+ {
+ if ((!idp || *((grn_id *)GRN_BULK_HEAD(&bulk))) &&
+ grn_table_add_v_inline(ctx, results->table,
+ GRN_BULK_HEAD(&bulk), GRN_BULK_VSIZE(&bulk), &value, NULL)) {
+ grn_table_add_subrec_inline(results->table, value, ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0);
+ }
+ }
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "invalid column");
+ break;
+ }
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ }
+ } else {
+ if ((tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next_inline(ctx, tc))) {
+ grn_rset_recinfo *ri = NULL;
+ GRN_BULK_REWIND(&bulk);
+ if (DB_OBJ(table)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_cursor_get_value_inline(ctx, tc, (void **)&ri);
+ }
+ for (k = 0, kp = keys; k < n_keys; k++, kp++) {
+ kp->offset = GRN_BULK_VSIZE(&bulk);
+ grn_obj_get_value(ctx, kp->key, id, &bulk);
+ }
+ for (r = 0, rp = results; r < n_results; r++, rp++) {
+ void *value;
+ int begin = keys[rp->key_begin].offset;
+ int end = rp->key_end >= n_keys
+ ? GRN_BULK_VSIZE(&bulk)
+ : keys[rp->key_end].offset;
+ key = GRN_BULK_HEAD(&bulk) + begin;
+ // todo : cut off GRN_ID_NIL
+ if (grn_table_add_v_inline(ctx, rp->table, key, end - begin, &value, NULL)) {
+ grn_table_add_subrec_inline(rp->table, value, ri ? ri->score : 0,
+ (grn_rset_posinfo *)&id, 0);
+ }
+ }
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ }
+ grn_obj_close(ctx, &bulk);
+ for (r = 0, rp = results; r < n_results; r++, rp++) {
+ GRN_TABLE_GROUPED_ON(rp->table);
+ }
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_setoperation(grn_ctx *ctx, grn_obj *table1, grn_obj *table2, grn_obj *res,
+ grn_operator op)
+{
+ grn_rc rc = GRN_SUCCESS;
+ void *key = NULL, *value1 = NULL, *value2 = NULL;
+ uint32_t value_size = 0;
+ uint32_t key_size = 0;
+ grn_bool have_subrec;
+ if (table1 != res) {
+ if (table2 == res) {
+ grn_obj *t = table1;
+ table1 = table2;
+ table2 = t;
+ } else {
+ return GRN_INVALID_ARGUMENT;
+ }
+ }
+ have_subrec = ((DB_OBJ(table1)->header.flags & GRN_OBJ_WITH_SUBREC) &&
+ (DB_OBJ(table2)->header.flags & GRN_OBJ_WITH_SUBREC));
+ switch (table1->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ value_size = ((grn_hash *)table1)->value_size;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ value_size = ((grn_pat *)table1)->value_size;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ value_size = 0;
+ break;
+ case GRN_TABLE_NO_KEY :
+ value_size = ((grn_array *)table1)->value_size;
+ break;
+ }
+ switch (table2->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ if (value_size < ((grn_hash *)table2)->value_size) {
+ value_size = ((grn_hash *)table2)->value_size;
+ }
+ break;
+ case GRN_TABLE_PAT_KEY :
+ if (value_size < ((grn_pat *)table2)->value_size) {
+ value_size = ((grn_pat *)table2)->value_size;
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ value_size = 0;
+ break;
+ case GRN_TABLE_NO_KEY :
+ if (value_size < ((grn_array *)table2)->value_size) {
+ value_size = ((grn_array *)table2)->value_size;
+ }
+ break;
+ }
+ switch (op) {
+ case GRN_OP_OR :
+ if (have_subrec) {
+ int added;
+ GRN_TABLE_EACH(ctx, table2, 0, 0, id, &key, &key_size, &value2, {
+ if (grn_table_add_v_inline(ctx, table1, key, key_size, &value1, &added)) {
+ if (added) {
+ memcpy(value1, value2, value_size);
+ } else {
+ grn_rset_recinfo *ri1 = value1;
+ grn_rset_recinfo *ri2 = value2;
+ grn_table_add_subrec_inline(table1, ri1, ri2->score, NULL, 0);
+ }
+ }
+ });
+ } else {
+ GRN_TABLE_EACH(ctx, table2, 0, 0, id, &key, &key_size, &value2, {
+ if (grn_table_add_v_inline(ctx, table1, key, key_size, &value1, NULL)) {
+ memcpy(value1, value2, value_size);
+ }
+ });
+ }
+ break;
+ case GRN_OP_AND :
+ if (have_subrec) {
+ GRN_TABLE_EACH(ctx, table1, 0, 0, id, &key, &key_size, &value1, {
+ if (grn_table_get_v(ctx, table2, key, key_size, &value2)) {
+ grn_rset_recinfo *ri1 = value1;
+ grn_rset_recinfo *ri2 = value2;
+ ri1->score += ri2->score;
+ } else {
+ _grn_table_delete_by_id(ctx, table1, id, NULL);
+ }
+ });
+ } else {
+ GRN_TABLE_EACH(ctx, table1, 0, 0, id, &key, &key_size, &value1, {
+ if (!grn_table_get_v(ctx, table2, key, key_size, &value2)) {
+ _grn_table_delete_by_id(ctx, table1, id, NULL);
+ }
+ });
+ }
+ break;
+ case GRN_OP_AND_NOT :
+ GRN_TABLE_EACH(ctx, table2, 0, 0, id, &key, &key_size, &value2, {
+ grn_table_delete(ctx, table1, key, key_size);
+ });
+ break;
+ case GRN_OP_ADJUST :
+ GRN_TABLE_EACH(ctx, table2, 0, 0, id, &key, &key_size, &value2, {
+ if (grn_table_get_v(ctx, table1, key, key_size, &value1)) {
+ memcpy(value1, value2, value_size);
+ }
+ });
+ break;
+ default :
+ break;
+ }
+ return rc;
+}
+
+grn_rc
+grn_table_difference(grn_ctx *ctx, grn_obj *table1, grn_obj *table2,
+ grn_obj *res1, grn_obj *res2)
+{
+ void *key = NULL;
+ uint32_t key_size = 0;
+ if (table1 != res1 || table2 != res2) { return GRN_INVALID_ARGUMENT; }
+ if (grn_table_size(ctx, table1) > grn_table_size(ctx, table2)) {
+ GRN_TABLE_EACH(ctx, table2, 0, 0, id, &key, &key_size, NULL, {
+ grn_id id1;
+ if ((id1 = grn_table_get(ctx, table1, key, key_size))) {
+ _grn_table_delete_by_id(ctx, table1, id1, NULL);
+ _grn_table_delete_by_id(ctx, table2, id, NULL);
+ }
+ });
+ } else {
+ GRN_TABLE_EACH(ctx, table1, 0, 0, id, &key, &key_size, NULL, {
+ grn_id id2;
+ if ((id2 = grn_table_get(ctx, table2, key, key_size))) {
+ _grn_table_delete_by_id(ctx, table1, id, NULL);
+ _grn_table_delete_by_id(ctx, table2, id2, NULL);
+ }
+ });
+ }
+ return GRN_SUCCESS;
+}
+
+static grn_obj *grn_obj_get_accessor(grn_ctx *ctx, grn_obj *obj,
+ const char *name, unsigned int name_size);
+
+static grn_obj *
+grn_obj_column_(grn_ctx *ctx, grn_obj *table, const char *name, unsigned int name_size)
+{
+ grn_obj *column = NULL;
+ char buf[GRN_TABLE_MAX_KEY_SIZE];
+ int len = grn_obj_name(ctx, table, buf, GRN_TABLE_MAX_KEY_SIZE);
+ if (len) {
+ buf[len++] = GRN_DB_DELIMITER;
+ if (len + name_size <= GRN_TABLE_MAX_KEY_SIZE) {
+ memcpy(buf + len, name, name_size);
+ column = grn_ctx_get(ctx, buf, len + name_size);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "name is too long");
+ }
+ } else {
+ /* todo : support temporary table */
+ }
+ return column;
+}
+
+grn_obj *
+grn_obj_column(grn_ctx *ctx, grn_obj *table, const char *name, unsigned int name_size)
+{
+ grn_obj *column = NULL;
+ GRN_API_ENTER;
+ if (GRN_OBJ_TABLEP(table)) {
+ if (grn_db_check_name(ctx, name, name_size) ||
+ !(column = grn_obj_column_(ctx, table, name, name_size))) {
+ column = grn_obj_get_accessor(ctx, table, name, name_size);
+ }
+ } else if (GRN_ACCESSORP(table)) {
+ column = grn_obj_get_accessor(ctx, table, name, name_size);
+ }
+ GRN_API_RETURN(column);
+}
+
+int
+grn_table_columns(grn_ctx *ctx, grn_obj *table, const char *name, unsigned int name_size,
+ grn_obj *res)
+{
+ int n = 0;
+ GRN_API_ENTER;
+ if (GRN_OBJ_TABLEP(table) && DB_OBJ(table)->id &&
+ !(DB_OBJ(table)->id & GRN_OBJ_TMP_OBJECT)) {
+ grn_db *s = (grn_db *)DB_OBJ(table)->db;
+ if (s->keys) {
+ grn_obj bulk;
+ GRN_TEXT_INIT(&bulk, 0);
+ grn_table_get_key2(ctx, s->keys, DB_OBJ(table)->id, &bulk);
+ GRN_TEXT_PUTC(ctx, &bulk, GRN_DB_DELIMITER);
+ grn_bulk_write(ctx, &bulk, name, name_size);
+ grn_table_search(ctx, s->keys, GRN_BULK_HEAD(&bulk), GRN_BULK_VSIZE(&bulk),
+ GRN_OP_PREFIX, res, GRN_OP_OR);
+ grn_obj_close(ctx, &bulk);
+ n = grn_table_size(ctx, res);
+ }
+ }
+ GRN_API_RETURN(n);
+}
+
+const char *
+_grn_table_key(grn_ctx *ctx, grn_obj *table, grn_id id, uint32_t *key_size)
+{
+ GRN_ASSERT(table);
+ if (table->header.type == GRN_DB) { table = ((grn_db *)table)->keys; }
+ switch (table->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ return _grn_hash_key(ctx, (grn_hash *)table, id, key_size);
+ case GRN_TABLE_PAT_KEY :
+ return _grn_pat_key(ctx, (grn_pat *)table, id, key_size);
+ case GRN_TABLE_DAT_KEY :
+ return _grn_dat_key(ctx, (grn_dat *)table, id, key_size);
+ case GRN_TABLE_NO_KEY :
+ {
+ grn_array *a = (grn_array *)table;
+ const char *v;
+ if (a->obj.header.domain && a->value_size &&
+ (v = _grn_array_get_value(ctx, a, id))) {
+ *key_size = a->value_size;
+ return v;
+ } else {
+ *key_size = 0;
+ }
+ }
+ break;
+ }
+ return NULL;
+}
+
+/* column */
+
+grn_obj *
+grn_column_create(grn_ctx *ctx, grn_obj *table,
+ const char *name, unsigned int name_size,
+ const char *path, grn_obj_flags flags, grn_obj *type)
+{
+ grn_db *s;
+ uint32_t value_size;
+ grn_obj *db, *res = NULL;
+ grn_id id = GRN_ID_NIL;
+ grn_id range = GRN_ID_NIL;
+ grn_id domain = GRN_ID_NIL;
+ char fullname[GRN_TABLE_MAX_KEY_SIZE];
+ char buffer[PATH_MAX];
+ grn_bool ja_p = GRN_FALSE;
+ GRN_API_ENTER;
+ if (!table) {
+ ERR(GRN_INVALID_ARGUMENT, "[column][create] table is missing");
+ goto exit;
+ }
+ if (!type) {
+ ERR(GRN_INVALID_ARGUMENT, "[column][create] type is missing");
+ goto exit;
+ }
+ if (!name || !name_size) {
+ ERR(GRN_INVALID_ARGUMENT, "[column][create] name is missing");
+ goto exit;
+ }
+ db = DB_OBJ(table)->db;
+ s = (grn_db *)db;
+ if (!GRN_DB_P(s)) {
+ int table_name_len;
+ char table_name[GRN_TABLE_MAX_KEY_SIZE];
+ table_name_len = grn_obj_name(ctx, table, table_name, GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][create] invalid db assigned: <%.*s>.<%.*s>",
+ table_name_len, table_name, name_size, name);
+ goto exit;
+ }
+ if (DB_OBJ(table)->id & GRN_OBJ_TMP_OBJECT) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][create] temporary table doesn't support column: <%.*s>",
+ name_size, name);
+ goto exit;
+ }
+ {
+ uint32_t s = 0;
+ const char *n = _grn_table_key(ctx, ctx->impl->db, DB_OBJ(table)->id, &s);
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "DDL:column_create %.*s %.*s", s, n, name_size, name);
+ }
+ if (grn_db_check_name(ctx, name, name_size)) {
+ GRN_DB_CHECK_NAME_ERR("[column][create]", name, name_size);
+ goto exit;
+ }
+ if ((domain = DB_OBJ(table)->id)) {
+ int len = grn_table_get_key(ctx, s->keys, domain, fullname, GRN_TABLE_MAX_KEY_SIZE);
+ if (name_size + 1 + len > GRN_TABLE_MAX_KEY_SIZE) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][create] too long column name: required name_size(%d) < %d"
+ ": <%.*s>.<%.*s>",
+ name_size, GRN_TABLE_MAX_KEY_SIZE - 1 - len,
+ len, fullname, name_size, name);
+ goto exit;
+ }
+ fullname[len] = GRN_DB_DELIMITER;
+ memcpy(fullname + len + 1, name, name_size);
+ name_size += len + 1;
+ } else {
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED,
+ "[column][create] [todo] table-less column isn't supported yet");
+ goto exit;
+ }
+ range = DB_OBJ(type)->id;
+ switch (type->header.type) {
+ case GRN_TYPE :
+ {
+ grn_db_obj *t = (grn_db_obj *)type;
+ flags |= t->header.flags;
+ value_size = GRN_TYPE_SIZE(t);
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ value_size = sizeof(grn_id);
+ break;
+ default :
+ /*
+ if (type == grn_type_any) {
+ value_size = sizeof(grn_id) + sizeof(grn_id);
+ }
+ */
+ value_size = sizeof(grn_id);
+ }
+ id = grn_obj_register(ctx, db, fullname, name_size);
+ if (ERRP(ctx, GRN_ERROR)) { goto exit; }
+ if (GRN_OBJ_PERSISTENT & flags) {
+ if (!path) {
+ if (GRN_DB_PERSISTENT_P(db)) {
+ gen_pathname(grn_obj_io(db)->path, buffer, id);
+ path = buffer;
+ } else {
+ int table_name_len;
+ char table_name[GRN_TABLE_MAX_KEY_SIZE];
+ table_name_len = grn_obj_name(ctx, table, table_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][create] path not assigned for persistent column"
+ ": <%.*s>.<%.*s>",
+ table_name_len, table_name, name_size, name);
+ goto exit;
+ }
+ } else {
+ flags |= GRN_OBJ_CUSTOM_NAME;
+ }
+ } else {
+ if (path) {
+ int table_name_len;
+ char table_name[GRN_TABLE_MAX_KEY_SIZE];
+ table_name_len = grn_obj_name(ctx, table, table_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][create] path assigned for temporary column"
+ ": <%.*s>.<%.*s>",
+ table_name_len, table_name, name_size, name);
+ goto exit;
+ }
+ }
+ switch (flags & GRN_OBJ_COLUMN_TYPE_MASK) {
+ case GRN_OBJ_COLUMN_SCALAR :
+ if ((flags & GRN_OBJ_KEY_VAR_SIZE) || value_size > sizeof(int64_t)) {
+ res = (grn_obj *)grn_ja_create(ctx, path, value_size, flags);
+ ja_p = GRN_TRUE;
+ } else {
+ res = (grn_obj *)grn_ra_create(ctx, path, value_size);
+ }
+ break;
+ case GRN_OBJ_COLUMN_VECTOR :
+ res = (grn_obj *)grn_ja_create(ctx, path, value_size * 30/*todo*/, flags);
+ ja_p = GRN_TRUE;
+ //todo : zlib support
+ break;
+ case GRN_OBJ_COLUMN_INDEX :
+ res = (grn_obj *)grn_ii_create(ctx, path, table, flags); //todo : ii layout support
+ break;
+ }
+ if (res) {
+ DB_OBJ(res)->header.domain = domain;
+ DB_OBJ(res)->header.impl_flags = 0;
+ DB_OBJ(res)->range = range;
+ DB_OBJ(res)->header.flags = flags;
+ res->header.flags = flags;
+ if (ja_p) {
+ grn_bool zlib_p = GRN_FALSE;
+ grn_bool lzo_p = GRN_FALSE;
+#ifdef GRN_WITH_ZLIB
+ if (flags & GRN_OBJ_COMPRESS_ZLIB) {
+ zlib_p = GRN_TRUE;
+ }
+#endif /* GRN_WITH_ZLIB */
+#ifdef GRN_WITH_LZO
+ if (flags & GRN_OBJ_COMPRESS_LZO) {
+ lzo_p = GRN_TRUE;
+ }
+#endif /* GRN_WITH_LZO */
+ if (zlib_p || lzo_p) {
+ int table_name_len;
+ char table_name[GRN_TABLE_MAX_KEY_SIZE];
+ table_name_len = grn_obj_name(ctx, table, table_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "[column][create] "
+ "%s compressed column will leaks memories: <%.*s>.<%.*s>",
+ zlib_p ? "zlib" : "lzo",
+ table_name_len, table_name, name_size, name);
+ }
+ }
+ if (grn_db_obj_init(ctx, db, id, DB_OBJ(res))) {
+ _grn_obj_remove(ctx, res);
+ res = NULL;
+ }
+ grn_obj_touch(ctx, res, NULL);
+ }
+exit :
+ if (!res && id) { grn_obj_delete_by_id(ctx, db, id, GRN_TRUE); }
+ GRN_API_RETURN(res);
+}
+
+grn_obj *
+grn_column_open(grn_ctx *ctx, grn_obj *table,
+ const char *name, unsigned int name_size,
+ const char *path, grn_obj *type)
+{
+ grn_id domain;
+ grn_obj *res = NULL;
+ grn_db *s;
+ char fullname[GRN_TABLE_MAX_KEY_SIZE];
+ GRN_API_ENTER;
+ if (!table || !type || !name || !name_size) {
+ ERR(GRN_INVALID_ARGUMENT, "missing type or name");
+ goto exit;
+ }
+ s = (grn_db *)DB_OBJ(table)->db;
+ if (!GRN_DB_P(s)) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid db assigned");
+ goto exit;
+ }
+ if (grn_db_check_name(ctx, name, name_size)) {
+ GRN_DB_CHECK_NAME_ERR("[column][open]", name, name_size);
+ goto exit;
+ }
+ if ((domain = DB_OBJ(table)->id)) {
+ int len = grn_table_get_key(ctx, s->keys, domain, fullname, GRN_TABLE_MAX_KEY_SIZE);
+ if (name_size + 1 + len > GRN_TABLE_MAX_KEY_SIZE) {
+ ERR(GRN_INVALID_ARGUMENT, "too long column name");
+ goto exit;
+ }
+ fullname[len] = GRN_DB_DELIMITER;
+ memcpy(fullname + len + 1, name, name_size);
+ name_size += len + 1;
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "todo : not supported yet");
+ goto exit;
+ }
+ res = grn_ctx_get(ctx, fullname, name_size);
+ if (res) {
+ const char *path2 = grn_obj_path(ctx, res);
+ if (path && (!path2 || strcmp(path, path2))) { goto exit; }
+ } else if (path) {
+ uint32_t dbtype = grn_io_detect_type(ctx, path);
+ if (!dbtype) { goto exit; }
+ switch (dbtype) {
+ case GRN_COLUMN_VAR_SIZE :
+ res = (grn_obj *)grn_ja_open(ctx, path);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ res = (grn_obj *)grn_ra_open(ctx, path);
+ break;
+ case GRN_COLUMN_INDEX :
+ res = (grn_obj *)grn_ii_open(ctx, path, table);
+ break;
+ }
+ if (res) {
+ grn_id id = grn_obj_register(ctx, (grn_obj *)s, fullname, name_size);
+ DB_OBJ(res)->header.domain = domain;
+ DB_OBJ(res)->range = DB_OBJ(type)->id;
+ res->header.flags |= GRN_OBJ_CUSTOM_NAME;
+ grn_db_obj_init(ctx, (grn_obj *)s, id, DB_OBJ(res));
+ }
+ }
+exit :
+ GRN_API_RETURN(res);
+}
+
+/*
+typedef struct {
+ grn_id id;
+ int flags;
+} grn_column_set_value_arg;
+
+static grn_rc
+default_column_set_value(grn_ctx *ctx, grn_proc_ctx *pctx, grn_obj *in, grn_obj *out)
+{
+ grn_user_data *data = grn_proc_ctx_get_local_data(pctx);
+ if (data) {
+ grn_column_set_value_arg *arg = data->ptr;
+ unsigned int value_size = in->u.p.size; //todo
+ if (!pctx->obj) { return GRN_ID_NIL; }
+ switch (pctx->obj->header.type) {
+ case GRN_COLUMN_VAR_SIZE :
+ return grn_ja_put(ctx, (grn_ja *)pctx->obj, arg->id,
+ in->u.p.ptr, value_size, 0, NULL); // todo type->flag
+ case GRN_COLUMN_FIX_SIZE :
+ if (((grn_ra *)pctx->obj)->header->element_size < value_size) {
+ ERR(GRN_INVALID_ARGUMENT, "too long value (%d)", value_size);
+ return GRN_INVALID_ARGUMENT;
+ } else {
+ void *v = grn_ra_ref(ctx, (grn_ra *)pctx->obj, arg->id);
+ if (!v) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "ra get failed");
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ memcpy(v, in->u.p.ptr, value_size);
+ grn_ra_unref(ctx, (grn_ra *)pctx->obj, arg->id);
+ }
+ break;
+ case GRN_COLUMN_INDEX :
+ // todo : how??
+ break;
+ }
+ return GRN_SUCCESS;
+ } else {
+ ERR(GRN_OBJECT_CORRUPT, "grn_proc_ctx_get_local_data failed");
+ return ctx->rc;
+ }
+}
+*/
+
+/**** grn_vector ****/
+
+//#define VECTOR(obj) ((grn_vector *)obj)
+
+/*
+#define INITIAL_VECTOR_SIZE 256
+
+int
+grn_vector_delimit(grn_ctx *ctx, grn_obj *vector)
+{
+ grn_vector *v = VECTOR(vector);
+ uint32_t *offsets;
+ if (!(v->n_entries & (INITIAL_VECTOR_SIZE - 1))) {
+ offsets = GRN_REALLOC(v->offsets, sizeof(uint32_t) *
+ (v->n_entries + INITIAL_VECTOR_SIZE));
+ if (!offsets) { return -1; }
+ v->offsets = offsets;
+ }
+ v->offsets[v->n_entries] = GRN_BULK_VSIZE(vector);
+ return ++(v->n_entries);
+}
+*/
+
+static unsigned int
+grn_uvector_size_internal(grn_ctx *ctx, grn_obj *uvector)
+{
+ unsigned int size;
+
+ if (IS_WEIGHT_UVECTOR(uvector)) {
+ size = GRN_BULK_VSIZE(uvector) / sizeof(weight_uvector_entry);
+ } else {
+ size = GRN_BULK_VSIZE(uvector) / sizeof(grn_id);
+ }
+
+ return size;
+}
+
+unsigned int
+grn_vector_size(grn_ctx *ctx, grn_obj *vector)
+{
+ unsigned int size;
+ if (!vector) {
+ ERR(GRN_INVALID_ARGUMENT, "vector is null");
+ return 0;
+ }
+ GRN_API_ENTER;
+ switch (vector->header.type) {
+ case GRN_BULK :
+ size = GRN_BULK_VSIZE(vector);
+ break;
+ case GRN_UVECTOR :
+ size = grn_uvector_size_internal(ctx, vector);
+ break;
+ case GRN_VECTOR :
+ size = vector->u.v.n_sections;
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "not vector");
+ size = 0;
+ break;
+ }
+ GRN_API_RETURN(size);
+}
+
+static grn_obj *
+grn_vector_body(grn_ctx *ctx, grn_obj *v)
+{
+ if (!v) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid argument");
+ return NULL;
+ }
+ switch (v->header.type) {
+ case GRN_VECTOR :
+ if (!v->u.v.body) {
+ v->u.v.body = grn_obj_open(ctx, GRN_BULK, 0, v->header.domain);
+ }
+ return v->u.v.body;
+ case GRN_BULK :
+ case GRN_UVECTOR :
+ return v;
+ default :
+ return NULL;
+ }
+}
+
+unsigned int
+grn_vector_get_element(grn_ctx *ctx, grn_obj *vector,
+ unsigned int offset, const char **str,
+ unsigned int *weight, grn_id *domain)
+{
+ unsigned int length = 0;
+ GRN_API_ENTER;
+ if (!vector || vector->header.type != GRN_VECTOR) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid vector");
+ goto exit;
+ }
+ if (vector->u.v.n_sections <= offset) {
+ ERR(GRN_RANGE_ERROR, "offset out of range");
+ goto exit;
+ }
+ {
+ grn_section *vp = &vector->u.v.sections[offset];
+ grn_obj *body = grn_vector_body(ctx, vector);
+ *str = GRN_BULK_HEAD(body) + vp->offset;
+ if (weight) { *weight = vp->weight; }
+ if (domain) { *domain = vp->domain; }
+ length = vp->length;
+ }
+exit :
+ GRN_API_RETURN(length);
+}
+
+unsigned int
+grn_vector_pop_element(grn_ctx *ctx, grn_obj *vector,
+ const char **str, unsigned int *weight, grn_id *domain)
+{
+ unsigned int offset, length = 0;
+ GRN_API_ENTER;
+ if (!vector || vector->header.type != GRN_VECTOR) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid vector");
+ goto exit;
+ }
+ if (!vector->u.v.n_sections) {
+ ERR(GRN_RANGE_ERROR, "offset out of range");
+ goto exit;
+ }
+ offset = --vector->u.v.n_sections;
+ {
+ grn_section *vp = &vector->u.v.sections[offset];
+ grn_obj *body = grn_vector_body(ctx, vector);
+ *str = GRN_BULK_HEAD(body) + vp->offset;
+ if (weight) { *weight = vp->weight; }
+ if (domain) { *domain = vp->domain; }
+ length = vp->length;
+ grn_bulk_truncate(ctx, body, vp->offset);
+ }
+exit :
+ GRN_API_RETURN(length);
+}
+
+#define W_SECTIONS_UNIT 8
+#define S_SECTIONS_UNIT (1 << W_SECTIONS_UNIT)
+#define M_SECTIONS_UNIT (S_SECTIONS_UNIT - 1)
+
+grn_rc
+grn_vector_delimit(grn_ctx *ctx, grn_obj *v, unsigned int weight, grn_id domain)
+{
+ if (v->header.type != GRN_VECTOR) { return GRN_INVALID_ARGUMENT; }
+ if (!(v->u.v.n_sections & M_SECTIONS_UNIT)) {
+ grn_section *vp = GRN_REALLOC(v->u.v.sections, sizeof(grn_section) *
+ (v->u.v.n_sections + S_SECTIONS_UNIT));
+ if (!vp) { return GRN_NO_MEMORY_AVAILABLE; }
+ v->u.v.sections = vp;
+ }
+ {
+ grn_obj *body = grn_vector_body(ctx, v);
+ grn_section *vp = &v->u.v.sections[v->u.v.n_sections];
+ vp->offset = v->u.v.n_sections ? vp[-1].offset + vp[-1].length : 0;
+ vp->length = GRN_BULK_VSIZE(body) - vp->offset;
+ vp->weight = weight;
+ vp->domain = domain;
+ }
+ v->u.v.n_sections++;
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_vector_decode(grn_ctx *ctx, grn_obj *v, const char *data, uint32_t data_size)
+{
+ uint8_t *p = (uint8_t *)data;
+ uint8_t *pe = p + data_size;
+ uint32_t n, n0 = v->u.v.n_sections;
+ GRN_B_DEC(n, p);
+ if (((n0 + M_SECTIONS_UNIT) >> W_SECTIONS_UNIT) !=
+ ((n0 + n + M_SECTIONS_UNIT) >> W_SECTIONS_UNIT)) {
+ grn_section *vp = GRN_REALLOC(v->u.v.sections, sizeof(grn_section) *
+ ((n0 + n + M_SECTIONS_UNIT) & ~M_SECTIONS_UNIT));
+ if (!vp) { return GRN_NO_MEMORY_AVAILABLE; }
+ v->u.v.sections = vp;
+ }
+ {
+ grn_section *vp;
+ uint32_t o = 0, l, i;
+ for (i = n, vp = v->u.v.sections + n0; i; i--, vp++) {
+ if (pe <= p) { return GRN_INVALID_ARGUMENT; }
+ GRN_B_DEC(l, p);
+ vp->length = l;
+ vp->offset = o;
+ vp->weight = 0;
+ vp->domain = 0;
+ o += l;
+ }
+ if (pe < p + o) { return GRN_INVALID_ARGUMENT; }
+ {
+ grn_obj *body = grn_vector_body(ctx, v);
+ grn_bulk_write(ctx, body, (char *)p, o);
+ }
+ p += o;
+ if (p < pe) {
+ for (i = n, vp = v->u.v.sections + n0; i; i--, vp++) {
+ if (pe <= p) { return GRN_INVALID_ARGUMENT; }
+ GRN_B_DEC(vp->weight, p);
+ GRN_B_DEC(vp->domain, p);
+ }
+ }
+ }
+ v->u.v.n_sections += n;
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_vector_add_element(grn_ctx *ctx, grn_obj *vector,
+ const char *str, unsigned int str_len,
+ unsigned int weight, grn_id domain)
+{
+ grn_obj *body;
+ GRN_API_ENTER;
+ if (!vector) {
+ ERR(GRN_INVALID_ARGUMENT, "vector is null");
+ goto exit;
+ }
+ if ((body = grn_vector_body(ctx, vector))) {
+ grn_bulk_write(ctx, body, str, str_len);
+ grn_vector_delimit(ctx, vector, weight, domain);
+ }
+exit :
+ GRN_API_RETURN(ctx->rc);
+}
+
+/*
+grn_obj *
+grn_sections_to_vector(grn_ctx *ctx, grn_obj *sections)
+{
+ grn_obj *vector = grn_vector_open(ctx, 0);
+ if (vector) {
+ grn_section *vp;
+ int i;
+ for (i = sections->u.v.n_sections, vp = sections->u.v.sections; i; i--, vp++) {
+ grn_text_benc(ctx, vector, vp->weight);
+ grn_text_benc(ctx, vector, vp->domain);
+ grn_bulk_write(ctx, vector, vp->str, vp->str_len);
+ grn_vector_delimit(ctx, vector);
+ }
+ }
+ return vector;
+}
+
+grn_obj *
+grn_vector_to_sections(grn_ctx *ctx, grn_obj *vector, grn_obj *sections)
+{
+ if (!sections) {
+ sections = grn_obj_open(ctx, GRN_VECTOR, GRN_OBJ_DO_SHALLOW_COPY, 0);
+ }
+ if (sections) {
+ int i, n = grn_vector_size(ctx, vector);
+ sections->u.v.src = vector;
+ for (i = 0; i < n; i++) {
+ unsigned int size;
+ const uint8_t *pe, *p = (uint8_t *)grn_vector_fetch(ctx, vector, i, &size);
+ if (p) {
+ grn_id domain;
+ unsigned int weight;
+ pe = p + size;
+ if (p < pe) {
+ GRN_B_DEC(weight, p);
+ if (p < pe) {
+ GRN_B_DEC(domain, p);
+ if (p <= pe) {
+ grn_vector_add(ctx, sections, (char *)p, pe - p, weight, domain);
+ }
+ }
+ }
+ }
+ }
+ }
+ return sections;
+}
+*/
+
+/**** uvector ****/
+
+unsigned int
+grn_uvector_size(grn_ctx *ctx, grn_obj *uvector)
+{
+ unsigned int size;
+
+ if (!uvector) {
+ ERR(GRN_INVALID_ARGUMENT, "uvector must not be NULL");
+ return 0;
+ }
+
+ if (uvector->header.type != GRN_UVECTOR) {
+ grn_obj type_name;
+ GRN_TEXT_INIT(&type_name, 0);
+ grn_inspect_type(ctx, &type_name, uvector->header.type);
+ ERR(GRN_INVALID_ARGUMENT, "must be GRN_UVECTOR: %.*s",
+ (int)GRN_TEXT_LEN(&type_name), GRN_TEXT_VALUE(&type_name));
+ GRN_OBJ_FIN(ctx, &type_name);
+ return 0;
+ }
+
+ GRN_API_ENTER;
+ size = grn_uvector_size_internal(ctx, uvector);
+ GRN_API_RETURN(size);
+}
+
+
+grn_rc
+grn_uvector_add_element(grn_ctx *ctx, grn_obj *uvector,
+ grn_id id, unsigned int weight)
+{
+ GRN_API_ENTER;
+ if (!uvector) {
+ ERR(GRN_INVALID_ARGUMENT, "uvector is null");
+ goto exit;
+ }
+ if (IS_WEIGHT_UVECTOR(uvector)) {
+ weight_uvector_entry entry;
+ entry.id = id;
+ entry.weight = weight;
+ grn_bulk_write(ctx, uvector,
+ (const char *)&entry, sizeof(weight_uvector_entry));
+ } else {
+ grn_bulk_write(ctx, uvector,
+ (const char *)&id, sizeof(grn_id));
+ }
+exit :
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_id
+grn_uvector_get_element(grn_ctx *ctx, grn_obj *uvector,
+ unsigned int offset, unsigned int *weight)
+{
+ grn_id id = GRN_ID_NIL;
+
+ GRN_API_ENTER;
+ if (!uvector || uvector->header.type != GRN_UVECTOR) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid uvector");
+ goto exit;
+ }
+
+ if (IS_WEIGHT_UVECTOR(uvector)) {
+ const weight_uvector_entry *entry;
+ const weight_uvector_entry *entries_start;
+ const weight_uvector_entry *entries_end;
+
+ entries_start = (const weight_uvector_entry *)GRN_BULK_HEAD(uvector);
+ entries_end = (const weight_uvector_entry *)GRN_BULK_CURR(uvector);
+ if (offset > entries_end - entries_start) {
+ ERR(GRN_RANGE_ERROR, "offset out of range");
+ goto exit;
+ }
+
+ entry = entries_start + offset;
+ id = entry->id;
+ if (weight) { *weight = entry->weight; }
+ } else {
+ const grn_id *ids_start;
+ const grn_id *ids_end;
+
+ ids_start = (const grn_id *)GRN_BULK_HEAD(uvector);
+ ids_end = (const grn_id *)GRN_BULK_CURR(uvector);
+ if (offset > ids_end - ids_start) {
+ ERR(GRN_RANGE_ERROR, "offset out of range");
+ goto exit;
+ }
+ id = ids_start[offset];
+ if (weight) { *weight = 0; }
+ }
+exit :
+ GRN_API_RETURN(id);
+}
+
+/**** accessor ****/
+
+static grn_accessor *
+accessor_new(grn_ctx *ctx)
+{
+ grn_accessor *res = GRN_MALLOCN(grn_accessor, 1);
+ if (res) {
+ res->header.type = GRN_ACCESSOR;
+ res->header.impl_flags = GRN_OBJ_ALLOCATED;
+ res->header.flags = 0;
+ res->header.domain = GRN_ID_NIL;
+ res->action = GRN_ACCESSOR_VOID;
+ res->offset = 0;
+ res->next = NULL;
+ }
+ return res;
+}
+
+static grn_obj *
+grn_obj_get_accessor(grn_ctx *ctx, grn_obj *obj, const char *name, unsigned int name_size)
+{
+ grn_accessor *res = NULL, **rp = NULL, **rp0 = NULL;
+ grn_bool is_chained = GRN_FALSE;
+ if (!obj) { return NULL; }
+ GRN_API_ENTER;
+ if (obj->header.type == GRN_ACCESSOR) {
+ is_chained = GRN_TRUE;
+ for (rp0 = (grn_accessor **)&obj; *rp0; rp0 = &(*rp0)->next) {
+ res = *rp0;
+ }
+ switch (res->action) {
+ case GRN_ACCESSOR_GET_KEY :
+ obj = grn_ctx_at(ctx, res->obj->header.domain);
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ case GRN_ACCESSOR_GET_SCORE :
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ obj = grn_ctx_at(ctx, DB_OBJ(res->obj)->range);
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ obj = grn_ctx_at(ctx, DB_OBJ(res->obj)->range);
+ break;
+ case GRN_ACCESSOR_LOOKUP :
+ /* todo */
+ break;
+ case GRN_ACCESSOR_FUNCALL :
+ /* todo */
+ break;
+ }
+ }
+ if (!obj) {
+ res = NULL;
+ goto exit;
+ }
+ {
+ size_t len;
+ const char *sp, *se = name + name_size;
+ if (*name == GRN_DB_DELIMITER) { name++; }
+ for (sp = name; (len = grn_charlen(ctx, sp, se)); sp += len) {
+ if (*sp == GRN_DB_DELIMITER) { break; }
+ }
+ if (!(len = sp - name)) { goto exit; }
+ if (*name == GRN_DB_PSEUDO_COLUMN_PREFIX) { /* pseudo column */
+ int done = 0;
+ if (len < 2) { goto exit; }
+ switch (name[1]) {
+ case 'k' : /* key */
+ if (len != GRN_COLUMN_NAME_KEY_LEN ||
+ memcmp(name, GRN_COLUMN_NAME_KEY, GRN_COLUMN_NAME_KEY_LEN)) {
+ goto exit;
+ }
+ for (rp = &res; !done; rp = &(*rp)->next) {
+ *rp = accessor_new(ctx);
+ (*rp)->obj = obj;
+ if (!(obj = grn_ctx_at(ctx, obj->header.domain))) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ switch (obj->header.type) {
+ case GRN_DB :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ rp = &(*rp)->next;
+ *rp = accessor_new(ctx);
+ (*rp)->obj = obj;
+ (*rp)->action = GRN_ACCESSOR_GET_DB_OBJ;
+ done++;
+ break;
+ case GRN_TYPE :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ done++;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_HASH_KEY :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ break;
+ case GRN_TABLE_NO_KEY :
+ if (obj->header.domain) {
+ (*rp)->action = GRN_ACCESSOR_GET_VALUE;
+ break;
+ }
+ /* fallthru */
+ default :
+ /* lookup failed */
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ }
+ break;
+ case 'i' : /* id */
+ if (len != GRN_COLUMN_NAME_ID_LEN ||
+ memcmp(name, GRN_COLUMN_NAME_ID, GRN_COLUMN_NAME_ID_LEN)) {
+ goto exit;
+ }
+ for (rp = &res; !done; rp = &(*rp)->next) {
+ *rp = accessor_new(ctx);
+ (*rp)->obj = obj;
+ if (!obj->header.domain) {
+ (*rp)->action = GRN_ACCESSOR_GET_ID;
+ done++;
+ } else {
+ if (!(obj = grn_ctx_at(ctx, obj->header.domain))) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ switch (obj->header.type) {
+ case GRN_DB :
+ case GRN_TYPE :
+ (*rp)->action = GRN_ACCESSOR_GET_ID;
+ done++;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_NO_KEY :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ break;
+ default :
+ /* lookup failed */
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ }
+ }
+ break;
+ case 'v' : /* value */
+ if (len != GRN_COLUMN_NAME_VALUE_LEN ||
+ memcmp(name, GRN_COLUMN_NAME_VALUE, GRN_COLUMN_NAME_VALUE_LEN)) {
+ goto exit;
+ }
+ for (rp = &res; !done; rp = &(*rp)->next) {
+ *rp = accessor_new(ctx);
+ (*rp)->obj = obj;
+ if (!obj->header.domain) {
+ if (DB_OBJ((*rp)->obj)->range) {
+ (*rp)->action = GRN_ACCESSOR_GET_VALUE;
+ done++;
+ } else {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ done++;
+ } else {
+ if (!(obj = grn_ctx_at(ctx, obj->header.domain))) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ switch (obj->header.type) {
+ case GRN_DB :
+ case GRN_TYPE :
+ if (DB_OBJ((*rp)->obj)->range) {
+ (*rp)->action = GRN_ACCESSOR_GET_VALUE;
+ done++;
+ } else {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ break;
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_NO_KEY :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ break;
+ default :
+ /* lookup failed */
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ }
+ }
+ break;
+ case 's' : /* score */
+ if (len != GRN_COLUMN_NAME_SCORE_LEN ||
+ memcmp(name, GRN_COLUMN_NAME_SCORE, GRN_COLUMN_NAME_SCORE_LEN)) {
+ goto exit;
+ }
+ for (rp = &res; !done; rp = &(*rp)->next) {
+ *rp = accessor_new(ctx);
+ (*rp)->obj = obj;
+ if (DB_OBJ(obj)->header.flags & GRN_OBJ_WITH_SUBREC) {
+ (*rp)->action = GRN_ACCESSOR_GET_SCORE;
+ done++;
+ } else {
+ switch (obj->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_HASH_KEY :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ break;
+ case GRN_TABLE_NO_KEY :
+ if (obj->header.domain) {
+ (*rp)->action = GRN_ACCESSOR_GET_VALUE;
+ break;
+ }
+ /* fallthru */
+ default :
+ /* lookup failed */
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ if (!(obj = grn_ctx_at(ctx, obj->header.domain))) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ }
+ }
+ break;
+ case 'n' : /* nsubrecs */
+ if (len != GRN_COLUMN_NAME_NSUBRECS_LEN ||
+ memcmp(name,
+ GRN_COLUMN_NAME_NSUBRECS,
+ GRN_COLUMN_NAME_NSUBRECS_LEN)) {
+ goto exit;
+ }
+ for (rp = &res; !done; rp = &(*rp)->next) {
+ *rp = accessor_new(ctx);
+ (*rp)->obj = obj;
+ if (GRN_TABLE_IS_GROUPED(obj)) {
+ (*rp)->action = GRN_ACCESSOR_GET_NSUBRECS;
+ done++;
+ } else {
+ switch (obj->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_HASH_KEY :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ break;
+ case GRN_TABLE_NO_KEY :
+ if (obj->header.domain) {
+ (*rp)->action = GRN_ACCESSOR_GET_VALUE;
+ break;
+ }
+ /* fallthru */
+ default :
+ /* lookup failed */
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ if (!(obj = grn_ctx_at(ctx, obj->header.domain))) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ }
+ }
+ break;
+ default :
+ res = NULL;
+ goto exit;
+ }
+ } else {
+ /* if obj->header.type == GRN_TYPE ... lookup table */
+ for (rp = &res; ; rp = &(*rp)->next) {
+ grn_obj *column = grn_obj_column_(ctx, obj, name, len);
+ if (column) {
+ *rp = accessor_new(ctx);
+ (*rp)->obj = column;
+ /*
+ switch (column->header.type) {
+ case GRN_COLUMN_VAR_SIZE :
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ break;
+ case GRN_COLUMN_INDEX :
+ break;
+ }
+ */
+ (*rp)->action = GRN_ACCESSOR_GET_COLUMN_VALUE;
+ break;
+ } else {
+ if (!obj->header.domain) {
+ // ERR(GRN_INVALID_ARGUMENT, "no such column: <%s>", name);
+ if (!is_chained) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ }
+ res = NULL;
+ goto exit;
+ }
+ *rp = accessor_new(ctx);
+ (*rp)->obj = obj;
+ if (!(obj = grn_ctx_at(ctx, obj->header.domain))) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ switch (obj->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_NO_KEY :
+ (*rp)->action = GRN_ACCESSOR_GET_KEY;
+ break;
+ default :
+ /* lookup failed */
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ }
+ }
+ }
+ if (sp != se) {
+ if (!grn_obj_get_accessor(ctx, (grn_obj *)res, sp, se - sp)) {
+ if (!is_chained) {
+ grn_obj_close(ctx, (grn_obj *)res);
+ res = NULL;
+ goto exit;
+ }
+ }
+ }
+ }
+ if (rp0) { *rp0 = res; }
+ exit :
+ GRN_API_RETURN((grn_obj *)res);
+}
+
+inline static grn_bool
+grn_column_is_vector(grn_ctx *ctx, grn_obj *column)
+{
+ grn_obj_flags type;
+
+ if (column->header.type != GRN_COLUMN_VAR_SIZE) {
+ return GRN_FALSE;
+ }
+
+ type = column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK;
+ return type == GRN_OBJ_COLUMN_VECTOR;
+}
+
+inline static void
+grn_obj_get_range_info(grn_ctx *ctx, grn_obj *obj,
+ grn_id *range_id, grn_obj_flags *range_flags)
+{
+ if (GRN_DB_OBJP(obj)) {
+ *range_id = DB_OBJ(obj)->range;
+ if (grn_column_is_vector(ctx, obj)) {
+ *range_flags = GRN_OBJ_VECTOR;
+ }
+ } else if (obj->header.type == GRN_ACCESSOR) {
+ grn_accessor *a;
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ *range_id = GRN_DB_UINT32;
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ if (GRN_DB_OBJP(a->obj)) {
+ *range_id = DB_OBJ(a->obj)->range;
+ }
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ *range_id = GRN_DB_INT32;
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ if (GRN_DB_OBJP(a->obj)) {
+ *range_id = DB_OBJ(a->obj)->range;
+ if (grn_column_is_vector(ctx, a->obj)) {
+ *range_flags = GRN_OBJ_VECTOR;
+ }
+ }
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ if (GRN_DB_OBJP(a->obj)) { *range_id = DB_OBJ(a->obj)->header.domain; }
+ break;
+ default :
+ if (GRN_DB_OBJP(a->obj)) { *range_id = DB_OBJ(a->obj)->range; }
+ break;
+ }
+ }
+ }
+}
+
+grn_id
+grn_obj_get_range(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_id range_id = GRN_ID_NIL;
+ grn_obj_flags range_flags = 0;
+
+ grn_obj_get_range_info(ctx, obj, &range_id, &range_flags);
+
+ return range_id;
+}
+
+int
+grn_obj_is_persistent(grn_ctx *ctx, grn_obj *obj)
+{
+ int res = 0;
+ if (GRN_DB_OBJP(obj)) {
+ res = IS_TEMP(obj) ? 0 : 1;
+ } else if (obj->header.type == GRN_ACCESSOR) {
+ grn_accessor *a;
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_SCORE :
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ res = 0;
+ break;
+ case GRN_ACCESSOR_GET_ID :
+ case GRN_ACCESSOR_GET_VALUE :
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ case GRN_ACCESSOR_GET_KEY :
+ if (GRN_DB_OBJP(a->obj)) { res = IS_TEMP(obj) ? 0 : 1; }
+ break;
+ default :
+ if (GRN_DB_OBJP(a->obj)) { res = IS_TEMP(obj) ? 0 : 1; }
+ break;
+ }
+ }
+ }
+ return res;
+}
+
+#define SRC2RECORD() do {\
+ grn_obj *table = grn_ctx_at(ctx, dest->header.domain);\
+ if (GRN_OBJ_TABLEP(table)) {\
+ grn_obj *p_key = src;\
+ grn_id id;\
+ if (table->header.type != GRN_TABLE_NO_KEY) {\
+ grn_obj key;\
+ GRN_OBJ_INIT(&key, GRN_BULK, 0, table->header.domain);\
+ if (src->header.domain != table->header.domain) {\
+ grn_obj_cast(ctx, src, &key, GRN_TRUE);\
+ p_key = &key;\
+ }\
+ if (GRN_BULK_VSIZE(p_key)) {\
+ id = addp ? grn_table_add_by_key(ctx, table, p_key, NULL)\
+ : grn_table_get_by_key(ctx, table, p_key);\
+ if (id) { GRN_RECORD_SET(ctx, dest, id); }\
+ } else {\
+ GRN_RECORD_SET(ctx, dest, GRN_ID_NIL);\
+ }\
+ GRN_OBJ_FIN(ctx, &key);\
+ } else {\
+ grn_obj record_id;\
+ GRN_UINT32_INIT(&record_id, 0);\
+ grn_obj_cast(ctx, src, &record_id, GRN_TRUE);\
+ id = GRN_UINT32_VALUE(&record_id);\
+ if (id) { GRN_RECORD_SET(ctx, dest, id); }\
+ }\
+ } else {\
+ rc = GRN_FUNCTION_NOT_IMPLEMENTED;\
+ }\
+} while (0)
+
+inline static grn_rc
+grn_obj_cast_bool(grn_ctx *ctx, grn_obj *src, grn_obj *dest, grn_bool addp)
+{
+ grn_rc rc = GRN_SUCCESS;
+
+ switch (dest->header.domain) {
+ case GRN_DB_BOOL :
+ GRN_BOOL_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_INT8 :
+ GRN_INT8_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_UINT8 :
+ GRN_UINT8_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_INT16 :
+ GRN_INT16_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_UINT16 :
+ GRN_UINT16_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_INT32 :
+ GRN_INT32_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_UINT32 :
+ GRN_UINT32_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_INT64 :
+ GRN_INT64_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_UINT64 :
+ GRN_UINT64_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_FLOAT :
+ GRN_FLOAT_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_TIME :
+ GRN_TIME_SET(ctx, dest, GRN_BOOL_VALUE(src));
+ break;
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ {
+ const char *bool_text;
+ bool_text = GRN_BOOL_VALUE(src) ? "true" : "false";
+ GRN_TEXT_PUTS(ctx, dest, bool_text);
+ }
+ break;
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ rc = GRN_INVALID_ARGUMENT;
+ break;
+ default :
+ SRC2RECORD();
+ break;
+ }
+ return rc;
+}
+
+#define NUM2DEST(getvalue,totext,tobool,totime,tofloat)\
+ switch (dest->header.domain) {\
+ case GRN_DB_BOOL :\
+ tobool(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_INT8 :\
+ GRN_INT8_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_UINT8 :\
+ GRN_UINT8_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_INT16 :\
+ GRN_INT16_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_UINT16 :\
+ GRN_UINT16_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_INT32 :\
+ GRN_INT32_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_UINT32 :\
+ GRN_UINT32_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_TIME :\
+ totime(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_INT64 :\
+ GRN_INT64_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_UINT64 :\
+ GRN_UINT64_SET(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_FLOAT :\
+ tofloat(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_SHORT_TEXT :\
+ case GRN_DB_TEXT :\
+ case GRN_DB_LONG_TEXT :\
+ totext(ctx, dest, getvalue(src));\
+ break;\
+ case GRN_DB_TOKYO_GEO_POINT :\
+ case GRN_DB_WGS84_GEO_POINT :\
+ rc = GRN_INVALID_ARGUMENT;\
+ break;\
+ default :\
+ SRC2RECORD();\
+ break;\
+ }
+
+#define TEXT2DEST(type,tonum,setvalue) do {\
+ const char *cur, *str = GRN_TEXT_VALUE(src);\
+ const char *str_end = GRN_BULK_CURR(src);\
+ type i = tonum(str, str_end, &cur);\
+ if (cur == str_end) {\
+ setvalue(ctx, dest, i);\
+ } else if (cur != str) {\
+ const char *rest;\
+ grn_obj buf;\
+ GRN_VOID_INIT(&buf);\
+ rc = grn_aton(ctx, str, str_end, &rest, &buf);\
+ if (!rc) {\
+ rc = grn_obj_cast(ctx, &buf, dest, addp);\
+ }\
+ GRN_OBJ_FIN(ctx, &buf);\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+} while (0)
+
+#define NUM2BOOL(ctx, dest, value) GRN_BOOL_SET(ctx, dest, value != 0)
+#define FLOAT2BOOL(ctx, dest, value) do {\
+ double value_ = value;\
+ GRN_BOOL_SET(ctx, dest, value_ < -DBL_EPSILON || DBL_EPSILON < value_);\
+} while (0)
+
+#define NUM2TIME(ctx, dest, value)\
+ GRN_TIME_SET(ctx, dest, (long long int)(value) * GRN_TIME_USEC_PER_SEC);
+#define TIME2TIME(ctx, dest, value)\
+ GRN_TIME_SET(ctx, dest, value);
+#define FLOAT2TIME(ctx, dest, value)\
+ GRN_TIME_SET(ctx, dest, (long long int)(value * GRN_TIME_USEC_PER_SEC));
+
+#define NUM2FLOAT(ctx, dest, value)\
+ GRN_FLOAT_SET(ctx, dest, value);
+#define TIME2FLOAT(ctx, dest, value)\
+ GRN_FLOAT_SET(ctx, dest, (double)(value) / GRN_TIME_USEC_PER_SEC);
+#define FLOAT2FLOAT(ctx, dest, value)\
+ GRN_FLOAT_SET(ctx, dest, value);
+
+grn_rc
+grn_obj_cast(grn_ctx *ctx, grn_obj *src, grn_obj *dest, grn_bool addp)
+{
+ grn_rc rc = GRN_SUCCESS;
+ switch (src->header.domain) {
+ case GRN_DB_BOOL :
+ rc = grn_obj_cast_bool(ctx, src, dest, addp);
+ break;
+ case GRN_DB_INT8 :
+ NUM2DEST(GRN_INT8_VALUE, grn_text_itoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_UINT8 :
+ NUM2DEST(GRN_UINT8_VALUE, grn_text_lltoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_INT16 :
+ NUM2DEST(GRN_INT16_VALUE, grn_text_itoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_UINT16 :
+ NUM2DEST(GRN_UINT16_VALUE, grn_text_lltoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_INT32 :
+ NUM2DEST(GRN_INT32_VALUE, grn_text_itoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_UINT32 :
+ NUM2DEST(GRN_UINT32_VALUE, grn_text_lltoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_INT64 :
+ NUM2DEST(GRN_INT64_VALUE, grn_text_lltoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_TIME :
+ NUM2DEST(GRN_TIME_VALUE, grn_text_lltoa, NUM2BOOL, TIME2TIME, TIME2FLOAT);
+ break;
+ case GRN_DB_UINT64 :
+ NUM2DEST(GRN_UINT64_VALUE, grn_text_lltoa, NUM2BOOL, NUM2TIME, NUM2FLOAT);
+ break;
+ case GRN_DB_FLOAT :
+ NUM2DEST(GRN_FLOAT_VALUE, grn_text_ftoa, FLOAT2BOOL, FLOAT2TIME,
+ FLOAT2FLOAT);
+ break;
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ switch (dest->header.domain) {
+ case GRN_DB_BOOL :
+ GRN_BOOL_SET(ctx, dest, GRN_TEXT_LEN(src) > 0);
+ break;
+ case GRN_DB_INT8 :
+ TEXT2DEST(int8_t, grn_atoi8, GRN_INT8_SET);
+ break;
+ case GRN_DB_UINT8 :
+ TEXT2DEST(uint8_t, grn_atoui8, GRN_UINT8_SET);
+ break;
+ case GRN_DB_INT16 :
+ TEXT2DEST(int16_t, grn_atoi16, GRN_INT16_SET);
+ break;
+ case GRN_DB_UINT16 :
+ TEXT2DEST(uint16_t, grn_atoui16, GRN_UINT16_SET);
+ break;
+ case GRN_DB_INT32 :
+ TEXT2DEST(int32_t, grn_atoi, GRN_INT32_SET);
+ break;
+ case GRN_DB_UINT32 :
+ TEXT2DEST(uint32_t, grn_atoui, GRN_UINT32_SET);
+ break;
+ case GRN_DB_TIME :
+ {
+ grn_timeval v;
+ int len = GRN_TEXT_LEN(src);
+ char *str = GRN_TEXT_VALUE(src);
+ if (grn_str2timeval(str, len, &v)) {
+ double d;
+ char *end;
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_PUT(ctx, &buf, str, len);
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ errno = 0;
+ d = strtod(GRN_TEXT_VALUE(&buf), &end);
+ if (!errno && end + 1 == GRN_BULK_CURR(&buf)) {
+ v.tv_sec = d;
+ v.tv_nsec = ((d - v.tv_sec) * GRN_TIME_NSEC_PER_SEC);
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ GRN_TIME_SET(ctx, dest,
+ GRN_TIME_PACK((int64_t)v.tv_sec,
+ GRN_TIME_NSEC_TO_USEC(v.tv_nsec)));
+ }
+ break;
+ case GRN_DB_INT64 :
+ TEXT2DEST(int64_t, grn_atoll, GRN_INT64_SET);
+ break;
+ case GRN_DB_UINT64 :
+ TEXT2DEST(int64_t, grn_atoll, GRN_UINT64_SET);
+ break;
+ case GRN_DB_FLOAT :
+ {
+ double d;
+ char *end;
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_PUT(ctx, &buf, GRN_TEXT_VALUE(src), GRN_TEXT_LEN(src));
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ errno = 0;
+ d = strtod(GRN_TEXT_VALUE(&buf), &end);
+ if (!errno && end + 1 == GRN_BULK_CURR(&buf)) {
+ GRN_FLOAT_SET(ctx, dest, d);
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ break;
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ GRN_TEXT_PUT(ctx, dest, GRN_TEXT_VALUE(src), GRN_TEXT_LEN(src));
+ break;
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ {
+ int latitude, longitude;
+ double degree;
+ const char *cur, *str = GRN_TEXT_VALUE(src);
+ const char *str_end = GRN_BULK_CURR(src);
+ if (str == str_end) {
+ GRN_GEO_POINT_SET(ctx, dest, 0, 0);
+ } else {
+ char *end;
+ grn_obj buf, *buf_p = NULL;
+ latitude = grn_atoi(str, str_end, &cur);
+ if (cur < str_end && cur[0] == '.') {
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_PUT(ctx, &buf, str, GRN_TEXT_LEN(src));
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ buf_p = &buf;
+ errno = 0;
+ degree = strtod(GRN_TEXT_VALUE(buf_p), &end);
+ if (errno) {
+ rc = GRN_INVALID_ARGUMENT;
+ } else {
+ latitude = GRN_GEO_DEGREE2MSEC(degree);
+ cur = str + (end - GRN_TEXT_VALUE(buf_p));
+ }
+ }
+ if (!rc && (cur[0] == 'x' || cur[0] == ',') && cur + 1 < str_end) {
+ const char *c = cur + 1;
+ longitude = grn_atoi(c, str_end, &cur);
+ if (cur < str_end && cur[0] == '.') {
+ if (!buf_p) {
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_PUT(ctx, &buf, str, GRN_TEXT_LEN(src));
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ buf_p = &buf;
+ }
+ errno = 0;
+ degree = strtod(GRN_TEXT_VALUE(buf_p) + (c - str), &end);
+ if (errno) {
+ rc = GRN_INVALID_ARGUMENT;
+ } else {
+ longitude = GRN_GEO_DEGREE2MSEC(degree);
+ cur = str + (end - GRN_TEXT_VALUE(buf_p));
+ }
+ }
+ if (!rc && cur == str_end) {
+ if ((GRN_GEO_MIN_LATITUDE <= latitude &&
+ latitude <= GRN_GEO_MAX_LATITUDE) &&
+ (GRN_GEO_MIN_LONGITUDE <= longitude &&
+ longitude <= GRN_GEO_MAX_LONGITUDE)) {
+ GRN_GEO_POINT_SET(ctx, dest, latitude, longitude);
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ if (buf_p) { GRN_OBJ_FIN(ctx, buf_p); }
+ }
+ }
+ break;
+ default :
+ SRC2RECORD();
+ break;
+ }
+ break;
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ if (src->header.domain == dest->header.domain) {
+ GRN_TEXT_PUT(ctx, dest, GRN_TEXT_VALUE(src), GRN_TEXT_LEN(src));
+ } else {
+ int latitude, longitude;
+ double latitude_in_degree, longitude_in_degree;
+ GRN_GEO_POINT_VALUE(src, latitude, longitude);
+ latitude_in_degree = GRN_GEO_MSEC2DEGREE(latitude);
+ longitude_in_degree = GRN_GEO_MSEC2DEGREE(longitude);
+ /* TokyoGeoPoint <-> WGS84GeoPoint is based on
+ http://www.jalan.net/jw/jwp0200/jww0203.do
+
+ jx: longitude in degree in Tokyo Geodetic System.
+ jy: latitude in degree in Tokyo Geodetic System.
+ wx: longitude in degree in WGS 84.
+ wy: latitude in degree in WGS 84.
+
+ jy = wy * 1.000106961 - wx * 0.000017467 - 0.004602017
+ jx = wx * 1.000083049 + wy * 0.000046047 - 0.010041046
+
+ wy = jy - jy * 0.00010695 + jx * 0.000017464 + 0.0046017
+ wx = jx - jy * 0.000046038 - jx * 0.000083043 + 0.010040
+ */
+ if (dest->header.domain == GRN_DB_TOKYO_GEO_POINT) {
+ double wgs84_latitude_in_degree = latitude_in_degree;
+ double wgs84_longitude_in_degree = longitude_in_degree;
+ int tokyo_latitude, tokyo_longitude;
+ double tokyo_latitude_in_degree, tokyo_longitude_in_degree;
+ tokyo_latitude_in_degree =
+ wgs84_latitude_in_degree * 1.000106961 -
+ wgs84_longitude_in_degree * 0.000017467 -
+ 0.004602017;
+ tokyo_longitude_in_degree =
+ wgs84_longitude_in_degree * 1.000083049 +
+ wgs84_latitude_in_degree * 0.000046047 -
+ 0.010041046;
+ tokyo_latitude = GRN_GEO_DEGREE2MSEC(tokyo_latitude_in_degree);
+ tokyo_longitude = GRN_GEO_DEGREE2MSEC(tokyo_longitude_in_degree);
+ GRN_GEO_POINT_SET(ctx, dest, tokyo_latitude, tokyo_longitude);
+ } else {
+ double tokyo_latitude_in_degree = latitude_in_degree;
+ double tokyo_longitude_in_degree = longitude_in_degree;
+ int wgs84_latitude, wgs84_longitude;
+ double wgs84_latitude_in_degree, wgs84_longitude_in_degree;
+ wgs84_latitude_in_degree =
+ tokyo_latitude_in_degree -
+ tokyo_latitude_in_degree * 0.00010695 +
+ tokyo_longitude_in_degree * 0.000017464 +
+ 0.0046017;
+ wgs84_longitude_in_degree =
+ tokyo_longitude_in_degree -
+ tokyo_latitude_in_degree * 0.000046038 -
+ tokyo_longitude_in_degree * 0.000083043 +
+ 0.010040;
+ wgs84_latitude = GRN_GEO_DEGREE2MSEC(wgs84_latitude_in_degree);
+ wgs84_longitude = GRN_GEO_DEGREE2MSEC(wgs84_longitude_in_degree);
+ GRN_GEO_POINT_SET(ctx, dest, wgs84_latitude, wgs84_longitude);
+ }
+ }
+ break;
+ case GRN_VOID :
+ rc = grn_obj_reinit(ctx, dest, dest->header.domain, dest->header.flags);
+ break;
+ default :
+ rc = GRN_FUNCTION_NOT_IMPLEMENTED;
+ break;
+ }
+ return rc;
+}
+
+const char *
+grn_accessor_get_value_(grn_ctx *ctx, grn_accessor *a, grn_id id, uint32_t *size)
+{
+ const char *value = NULL;
+ for (;;) {
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ value = (const char *)(uintptr_t)id;
+ *size = GRN_OBJ_GET_VALUE_IMD;
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ value = _grn_table_key(ctx, a->obj, id, size);
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ value = grn_obj_get_value_(ctx, a->obj, id, size);
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ if ((value = grn_obj_get_value_(ctx, a->obj, id, size))) {
+ value = (const char *)&((grn_rset_recinfo *)value)->score;
+ *size = sizeof(int);
+ }
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ if ((value = grn_obj_get_value_(ctx, a->obj, id, size))) {
+ value = (const char *)&((grn_rset_recinfo *)value)->n_subrecs;
+ *size = sizeof(int);
+ }
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ /* todo : support vector */
+ value = grn_obj_get_value_(ctx, a->obj, id, size);
+ break;
+ case GRN_ACCESSOR_GET_DB_OBJ :
+ value = _grn_table_key(ctx, ((grn_db *)ctx->impl->db)->keys, id, size);
+ break;
+ case GRN_ACCESSOR_LOOKUP :
+ /* todo */
+ break;
+ case GRN_ACCESSOR_FUNCALL :
+ /* todo */
+ break;
+ }
+ if (value && (a = a->next)) {
+ id = *((grn_id *)value);
+ } else {
+ break;
+ }
+ }
+ return value;
+}
+
+static grn_obj *
+grn_accessor_get_value(grn_ctx *ctx, grn_accessor *a, grn_id id, grn_obj *value)
+{
+ uint32_t vs = 0;
+ uint32_t size0;
+ void *vp = NULL;
+ if (!value) {
+ if (!(value = grn_obj_open(ctx, GRN_BULK, 0, 0))) { return NULL; }
+ } else {
+ value->header.type = GRN_BULK;
+ }
+ size0 = GRN_BULK_VSIZE(value);
+ for (;;) {
+ grn_bulk_truncate(ctx, value, size0);
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ GRN_UINT32_PUT(ctx, value, id);
+ vp = GRN_BULK_HEAD(value) + size0;
+ vs = GRN_BULK_VSIZE(value) - size0;
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ grn_table_get_key2(ctx, a->obj, id, value);
+ vp = GRN_BULK_HEAD(value) + size0;
+ vs = GRN_BULK_VSIZE(value) - size0;
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ grn_obj_get_value(ctx, a->obj, id, value);
+ vp = GRN_BULK_HEAD(value) + size0;
+ vs = GRN_BULK_VSIZE(value) - size0;
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ grn_obj_get_value(ctx, a->obj, id, value);
+ {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
+ GRN_INT32_PUT(ctx, value, ri->score);
+ }
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
+ GRN_INT32_PUT(ctx, value, ri->n_subrecs);
+ }
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ /* todo : support vector */
+ grn_obj_get_value(ctx, a->obj, id, value);
+ vp = GRN_BULK_HEAD(value) + size0;
+ vs = GRN_BULK_VSIZE(value) - size0;
+ break;
+ case GRN_ACCESSOR_GET_DB_OBJ :
+ value = grn_ctx_at(ctx, id);
+ grn_obj_close(ctx, value);
+ return value;
+ break;
+ case GRN_ACCESSOR_LOOKUP :
+ /* todo */
+ break;
+ case GRN_ACCESSOR_FUNCALL :
+ /* todo */
+ break;
+ }
+ if ((a = a->next)) {
+ id = *((grn_id *)vp);
+ } else {
+ break;
+ }
+ }
+ return value;
+}
+
+static grn_rc
+grn_accessor_set_value(grn_ctx *ctx, grn_accessor *a, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_SUCCESS;
+ if (!value) { value = grn_obj_open(ctx, GRN_BULK, 0, 0); }
+ if (value) {
+ grn_obj buf;
+ void *vp = NULL;
+ GRN_TEXT_INIT(&buf, 0);
+ for (;;) {
+ GRN_BULK_REWIND(&buf);
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_KEY :
+ grn_table_get_key2(ctx, a->obj, id, &buf);
+ vp = GRN_BULK_HEAD(&buf);
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ if (a->next) {
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ vp = GRN_BULK_HEAD(&buf);
+ } else {
+ rc = grn_obj_set_value(ctx, a->obj, id, value, flags);
+ }
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ {
+ grn_rset_recinfo *ri;
+ if (a->next) {
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ ri = (grn_rset_recinfo *)GRN_BULK_HEAD(&buf);
+ vp = &ri->score;
+ } else {
+ uint32_t size;
+ if ((ri = (grn_rset_recinfo *) grn_obj_get_value_(ctx, a->obj, id, &size))) {
+ vp = &ri->score;
+ // todo : flags support
+ if (value->header.domain == GRN_DB_INT32) {
+ memcpy(vp, GRN_BULK_HEAD(value), sizeof(int));
+ } else {
+ grn_obj buf;
+ GRN_INT32_INIT(&buf, 0);
+ grn_obj_cast(ctx, value, &buf, GRN_FALSE);
+ memcpy(vp, GRN_BULK_HEAD(&buf), sizeof(int));
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ }
+ }
+ }
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)GRN_BULK_HEAD(&buf);
+ vp = &ri->n_subrecs;
+ }
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ /* todo : support vector */
+ if (a->next) {
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ vp = GRN_BULK_HEAD(&buf);
+ } else {
+ rc = grn_obj_set_value(ctx, a->obj, id, value, flags);
+ }
+ break;
+ case GRN_ACCESSOR_LOOKUP :
+ /* todo */
+ break;
+ case GRN_ACCESSOR_FUNCALL :
+ /* todo */
+ break;
+ }
+ if ((a = a->next)) {
+ id = *((grn_id *)vp);
+ } else {
+ break;
+ }
+ }
+ grn_obj_close(ctx, &buf);
+ }
+ return rc;
+}
+
+#define INCRDECR(op) \
+ switch (DB_OBJ(obj)->range) {\
+ case GRN_DB_INT8 :\
+ if (s == sizeof(int8_t)) {\
+ int8_t *vp = (int8_t *)p;\
+ *vp op *(int8_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_UINT8 :\
+ if (s == sizeof(uint8_t)) {\
+ uint8_t *vp = (uint8_t *)p;\
+ *vp op *(int8_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_INT16 :\
+ if (s == sizeof(int16_t)) {\
+ int16_t *vp = (int16_t *)p;\
+ *vp op *(int16_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_UINT16 :\
+ if (s == sizeof(uint16_t)) {\
+ uint16_t *vp = (uint16_t *)p;\
+ *vp op *(int16_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_INT32 :\
+ if (s == sizeof(int32_t)) {\
+ int32_t *vp = (int32_t *)p;\
+ *vp op *(int32_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_UINT32 :\
+ if (s == sizeof(uint32_t)) {\
+ uint32_t *vp = (uint32_t *)p;\
+ *vp op *(int32_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_INT64 :\
+ case GRN_DB_TIME :\
+ if (s == sizeof(int64_t)) {\
+ int64_t *vp = (int64_t *)p;\
+ *vp op *(int64_t *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ case GRN_DB_FLOAT :\
+ if (s == sizeof(double)) {\
+ double *vp = (double *)p;\
+ *vp op *(double *)v;\
+ rc = GRN_SUCCESS;\
+ } else {\
+ rc = GRN_INVALID_ARGUMENT;\
+ }\
+ break;\
+ default :\
+ rc = GRN_OPERATION_NOT_SUPPORTED;\
+ break;\
+ }
+
+uint32_t
+grn_obj_size(grn_ctx *ctx, grn_obj *obj)
+{
+ if (!obj) { return 0; }
+ switch (obj->header.type) {
+ case GRN_VOID :
+ case GRN_BULK :
+ case GRN_PTR :
+ case GRN_UVECTOR :
+ case GRN_PVECTOR :
+ case GRN_MSG :
+ return GRN_BULK_VSIZE(obj);
+ case GRN_VECTOR :
+ return obj->u.v.body ? GRN_BULK_VSIZE(obj->u.v.body) : 0;
+ default :
+ return 0;
+ }
+}
+
+inline static int
+call_hook(grn_ctx *ctx, grn_obj *obj, grn_id id, grn_obj *value, int flags)
+{
+ grn_hook *hooks = DB_OBJ(obj)->hooks[GRN_HOOK_SET];
+ void *v = GRN_BULK_HEAD(value);
+ unsigned int s = grn_obj_size(ctx, value);
+ if (hooks || obj->header.type == GRN_COLUMN_VAR_SIZE) {
+ grn_obj oldbuf, *oldvalue;
+ GRN_TEXT_INIT(&oldbuf, 0);
+ oldvalue = grn_obj_get_value(ctx, obj, id, &oldbuf);
+ if (flags & GRN_OBJ_SET) {
+ void *ov;
+ unsigned int os;
+ ov = GRN_BULK_HEAD(oldvalue);
+ os = grn_obj_size(ctx, oldvalue);
+ if ((ov && v && os == s && !memcmp(ov, v, s)) &&
+ !(obj->header.type == GRN_COLUMN_FIX_SIZE &&
+ grn_bulk_is_zero(ctx, value))) {
+ grn_obj_close(ctx, oldvalue);
+ return 0;
+ }
+ }
+ if (hooks) {
+ // todo : grn_proc_ctx_open()
+ grn_obj id_, flags_;
+ grn_proc_ctx pctx = {{0}, hooks->proc, NULL, hooks, hooks, PROC_INIT, 4, 4};
+ GRN_UINT32_INIT(&id_, 0);
+ GRN_UINT32_INIT(&flags_, 0);
+ GRN_UINT32_SET(ctx, &id_, id);
+ GRN_UINT32_SET(ctx, &flags_, flags);
+ while (hooks) {
+ grn_ctx_push(ctx, &id_);
+ grn_ctx_push(ctx, oldvalue);
+ grn_ctx_push(ctx, value);
+ grn_ctx_push(ctx, &flags_);
+ pctx.caller = NULL;
+ pctx.currh = hooks;
+ if (hooks->proc) {
+ hooks->proc->funcs[PROC_INIT](ctx, 1, &obj, &pctx.user_data);
+ } else {
+ default_set_value_hook(ctx, 1, &obj, &pctx.user_data);
+ }
+ if (ctx->rc) {
+ grn_obj_close(ctx, oldvalue);
+ return 1;
+ }
+ hooks = hooks->next;
+ pctx.offset++;
+ }
+ }
+ grn_obj_close(ctx, oldvalue);
+ }
+ return 0;
+}
+
+inline static int
+call_hook_for_build(grn_ctx *ctx, grn_obj *obj, grn_id id, grn_obj *value, int flags)
+{
+ grn_hook *hooks = DB_OBJ(obj)->hooks[GRN_HOOK_SET];
+
+ if (hooks || obj->header.type == GRN_COLUMN_VAR_SIZE) {
+ grn_obj oldvalue;
+ GRN_TEXT_INIT(&oldvalue, 0);
+
+ if (hooks) {
+ // todo : grn_proc_ctx_open()
+ grn_obj id_, flags_;
+ grn_proc_ctx pctx = {{0}, hooks->proc, NULL, hooks, hooks, PROC_INIT, 4, 4};
+ GRN_UINT32_INIT(&id_, 0);
+ GRN_UINT32_INIT(&flags_, 0);
+ GRN_UINT32_SET(ctx, &id_, id);
+ GRN_UINT32_SET(ctx, &flags_, flags);
+ while (hooks) {
+ grn_ctx_push(ctx, &id_);
+ grn_ctx_push(ctx, &oldvalue);
+ grn_ctx_push(ctx, value);
+ grn_ctx_push(ctx, &flags_);
+ pctx.caller = NULL;
+ pctx.currh = hooks;
+ if (hooks->proc) {
+ hooks->proc->funcs[PROC_INIT](ctx, 1, &obj, &pctx.user_data);
+ } else {
+ default_set_value_hook(ctx, 1, &obj, &pctx.user_data);
+ }
+ if (ctx->rc) {
+ grn_obj_close(ctx, &oldvalue);
+ return 1;
+ }
+ hooks = hooks->next;
+ pctx.offset++;
+ }
+ }
+ grn_obj_close(ctx, &oldvalue);
+ }
+ return 0;
+}
+
+static grn_rc
+grn_obj_set_value_table_pat_key(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_id range = DB_OBJ(obj)->range;
+ void *v = GRN_BULK_HEAD(value);
+ grn_obj buf;
+
+ if (call_hook(ctx, obj, id, value, flags)) {
+ return rc;
+ }
+
+ if (range != value->header.domain) {
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, range);
+ if (grn_obj_cast(ctx, value, &buf, GRN_TRUE) == GRN_SUCCESS) {
+ v = GRN_BULK_HEAD(&buf);
+ }
+ }
+ rc = grn_pat_set_value(ctx, (grn_pat *)obj, id, v, flags);
+ if (range != value->header.domain) {
+ grn_obj_close(ctx, &buf);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_table_hash_key(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_id range = DB_OBJ(obj)->range;
+ void *v = GRN_BULK_HEAD(value);
+ grn_obj buf;
+
+ if (call_hook(ctx, obj, id, value, flags)) {
+ return rc;
+ }
+
+ if (range != value->header.domain) {
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, range);
+ if (grn_obj_cast(ctx, value, &buf, GRN_TRUE) == GRN_SUCCESS) {
+ v = GRN_BULK_HEAD(&buf);
+ }
+ }
+ rc = grn_hash_set_value(ctx, (grn_hash *)obj, id, v, flags);
+ if (range != value->header.domain) {
+ grn_obj_close(ctx, &buf);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_table_no_key(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_id range = DB_OBJ(obj)->range;
+ void *v = GRN_BULK_HEAD(value);
+ grn_obj buf;
+
+ if (call_hook(ctx, obj, id, value, flags)) {
+ return rc;
+ }
+
+ if (range != value->header.domain) {
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, range);
+ if (grn_obj_cast(ctx, value, &buf, GRN_TRUE) == GRN_SUCCESS) {
+ v = GRN_BULK_HEAD(&buf);
+ }
+ }
+ rc = grn_array_set_value(ctx, (grn_array *)obj, id, v, flags);
+ if (range != value->header.domain) {
+ grn_obj_close(ctx, &buf);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_column_var_size_scalar(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_id range = DB_OBJ(obj)->range;
+ void *v = GRN_BULK_HEAD(value);
+ unsigned int s = grn_obj_size(ctx, value);
+ grn_obj buf;
+ grn_id buf_domain = GRN_DB_VOID;
+
+ if (call_hook(ctx, obj, id, value, flags)) {
+ return rc;
+ }
+
+ switch (flags & GRN_OBJ_SET_MASK) {
+ case GRN_OBJ_INCR :
+ case GRN_OBJ_DECR :
+ if (value->header.domain == GRN_DB_INT32 ||
+ value->header.domain == GRN_DB_INT64) {
+ /* do nothing */
+ } else if (GRN_DB_INT8 <= value->header.domain &&
+ value->header.domain < GRN_DB_INT32) {
+ buf_domain = GRN_DB_INT32;
+ } else {
+ buf_domain = GRN_DB_INT64;
+ }
+ break;
+ default :
+ if (range != value->header.domain) {
+ buf_domain = range;
+ }
+ break;
+ }
+
+ if (buf_domain != GRN_DB_VOID) {
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, buf_domain);
+ if (grn_obj_cast(ctx, value, &buf, GRN_TRUE) == GRN_SUCCESS) {
+ v = GRN_BULK_HEAD(&buf);
+ s = GRN_BULK_VSIZE(&buf);
+ }
+ }
+
+ rc = grn_ja_put(ctx, (grn_ja *)obj, id, v, s, flags, NULL);
+
+ if (buf_domain != GRN_DB_VOID) {
+ grn_obj_close(ctx, &buf);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_column_var_size_vector_uvector(grn_ctx *ctx, grn_obj *column,
+ grn_id id, grn_obj *value,
+ int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_obj uvector;
+ grn_obj_flags uvector_flags = 0;
+ grn_bool need_convert = GRN_FALSE;
+ void *raw_value;
+ unsigned int size;
+
+ if (column->header.flags & GRN_OBJ_WITH_WEIGHT) {
+ if (!IS_WEIGHT_UVECTOR(value)) {
+ need_convert = GRN_TRUE;
+ }
+ } else {
+ if (IS_WEIGHT_UVECTOR(value)) {
+ need_convert = GRN_TRUE;
+ uvector_flags = GRN_OBJ_WITH_WEIGHT;
+ }
+ }
+
+ if (need_convert) {
+ unsigned int i, n;
+ GRN_VALUE_FIX_SIZE_INIT(&uvector, GRN_OBJ_VECTOR, value->header.domain);
+ uvector.header.flags |= uvector_flags;
+ n = grn_uvector_size(ctx, value);
+ for (i = 0; i < n; i++) {
+ grn_id id;
+ unsigned int weight = 0;
+ id = grn_uvector_get_element(ctx, value, i, NULL);
+ grn_uvector_add_element(ctx, &uvector, id, weight);
+ }
+ raw_value = GRN_BULK_HEAD(&uvector);
+ size = GRN_BULK_VSIZE(&uvector);
+ } else {
+ raw_value = GRN_BULK_HEAD(value);
+ size = GRN_BULK_VSIZE(value);
+ }
+
+ rc = grn_ja_put(ctx, (grn_ja *)column, id, raw_value, size, flags, NULL);
+
+ if (need_convert) {
+ GRN_OBJ_FIN(ctx, &uvector);
+ }
+
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_column_var_size_vector(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_id range = DB_OBJ(obj)->range;
+ void *v = GRN_BULK_HEAD(value);
+ unsigned int s = grn_obj_size(ctx, value);
+ grn_obj *lexicon = grn_ctx_at(ctx, range);
+
+ if (call_hook(ctx, obj, id, value, flags)) {
+ return rc;
+ }
+
+ if (value->header.type == GRN_UVECTOR) {
+ rc = grn_obj_set_value_column_var_size_vector_uvector(ctx, obj,
+ id, value,
+ flags);
+ return rc;
+ }
+
+ if (GRN_OBJ_TABLEP(lexicon)) {
+ grn_obj uvector;
+ GRN_RECORD_INIT(&uvector, GRN_OBJ_VECTOR, range);
+ if (obj->header.flags & GRN_OBJ_WITH_WEIGHT) {
+ uvector.header.flags |= GRN_OBJ_WITH_WEIGHT;
+ }
+ switch (value->header.type) {
+ case GRN_BULK :
+ {
+ unsigned int token_flags = 0;
+ grn_token *token;
+ if (v && s &&
+ (token = grn_token_open(ctx, lexicon, v, s,
+ GRN_TOKEN_ADD, token_flags))) {
+ while (!token->status) {
+ grn_id tid = grn_token_next(ctx, token);
+ grn_uvector_add_element(ctx, &uvector, tid, 0);
+ }
+ grn_token_close(ctx, token);
+ }
+ rc = grn_ja_put(ctx, (grn_ja *)obj, id,
+ GRN_BULK_HEAD(&uvector), GRN_BULK_VSIZE(&uvector),
+ flags, NULL);
+ }
+ break;
+ case GRN_VECTOR :
+ {
+ unsigned int n;
+ n = grn_vector_size(ctx, value);
+ if (n > 0) {
+ unsigned int i;
+ grn_obj value_buf, cast_buf;
+ GRN_OBJ_INIT(&value_buf, GRN_BULK, 0, GRN_DB_VOID);
+ GRN_OBJ_INIT(&cast_buf, GRN_BULK, 0, lexicon->header.domain);
+ for (i = 0; i < n; i++) {
+ grn_id tid;
+ const char *element;
+ unsigned int element_length;
+ unsigned int weight;
+ grn_id element_domain;
+
+ element_length = grn_vector_get_element(ctx, value, i,
+ &element, &weight,
+ &element_domain);
+ if (element_domain != lexicon->header.domain) {
+ GRN_BULK_REWIND(&cast_buf);
+ GRN_BULK_REWIND(&value_buf);
+ grn_bulk_write(ctx, &value_buf, element, element_length);
+ value_buf.header.domain = element_domain;
+ rc = grn_obj_cast(ctx, &value_buf, &cast_buf, GRN_TRUE);
+ if (rc) {
+ grn_obj *range_obj;
+ range_obj = grn_ctx_at(ctx, range);
+ ERR_CAST(obj, range_obj, &value_buf);
+ grn_obj_unlink(ctx, range_obj);
+ } else {
+ element = GRN_BULK_HEAD(&cast_buf);
+ element_length = GRN_BULK_VSIZE(&cast_buf);
+ }
+ } else {
+ rc = GRN_SUCCESS;
+ }
+ if (rc) {
+ continue;
+ }
+ tid = grn_table_add(ctx, lexicon, element, element_length, NULL);
+ grn_uvector_add_element(ctx, &uvector, tid, weight);
+ }
+ GRN_OBJ_FIN(ctx, &value_buf);
+ GRN_OBJ_FIN(ctx, &cast_buf);
+ }
+ }
+ rc = grn_ja_put(ctx, (grn_ja *)obj, id,
+ GRN_BULK_HEAD(&uvector), GRN_BULK_VSIZE(&uvector),
+ flags, NULL);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "vector, uvector or bulk required");
+ break;
+ }
+ grn_obj_close(ctx, &uvector);
+ } else {
+ switch (value->header.type) {
+ case GRN_BULK :
+ if (!GRN_BULK_VSIZE(value)) {
+ rc = grn_ja_put(ctx, (grn_ja *)obj, id, NULL, 0, flags, NULL);
+ } else {
+ grn_obj v;
+ GRN_OBJ_INIT(&v, GRN_VECTOR, GRN_OBJ_DO_SHALLOW_COPY, GRN_DB_TEXT);
+ v.u.v.body = value;
+ grn_vector_delimit(ctx, &v, 0, GRN_ID_NIL);
+ rc = grn_ja_putv(ctx, (grn_ja *)obj, id, &v, 0);
+ grn_obj_close(ctx, &v);
+ }
+ break;
+ case GRN_VECTOR :
+ rc = grn_ja_putv(ctx, (grn_ja *)obj, id, value, 0);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "vector or bulk required");
+ break;
+ }
+ }
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_column_fix_size(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_id range = DB_OBJ(obj)->range;
+ void *v = GRN_BULK_HEAD(value);
+ unsigned int s = grn_obj_size(ctx, value);
+ grn_obj buf, *value_ = value;
+ uint32_t element_size = ((grn_ra *)obj)->header->element_size;
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, range);
+ if (range != value->header.domain) {
+ rc = grn_obj_cast(ctx, value, &buf, GRN_TRUE);
+ if (rc) {
+ grn_obj *range_obj;
+ range_obj = grn_ctx_at(ctx, range);
+ ERR_CAST(obj, range_obj, value);
+ grn_obj_unlink(ctx, range_obj);
+ } else {
+ value_ = &buf;
+ v = GRN_BULK_HEAD(&buf);
+ s = GRN_BULK_VSIZE(&buf);
+ }
+ } else {
+ rc = GRN_SUCCESS;
+ }
+ if (rc) {
+ /* do nothing because it already has error. */
+ } else if (element_size < s) {
+ ERR(GRN_INVALID_ARGUMENT, "too long value (%d)", s);
+ } else {
+ void *p = grn_ra_ref(ctx, (grn_ra *)obj, id);
+ if (!p) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "ra get failed");
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ return rc;
+ }
+ switch (flags & GRN_OBJ_SET_MASK) {
+ case GRN_OBJ_SET :
+ if (call_hook(ctx, obj, id, value_, flags)) {
+ GRN_OBJ_FIN(ctx, &buf);
+ grn_ra_unref(ctx, (grn_ra *)obj, id);
+ return rc;
+ }
+ if (element_size != s) {
+ if (!s) {
+ memset(p, 0, element_size);
+ } else {
+ void *b;
+ if ((b = GRN_CALLOC(element_size))) {
+ memcpy(b, v, s);
+ memcpy(p, b, element_size);
+ GRN_FREE(b);
+ }
+ }
+ } else {
+ memcpy(p, v, s);
+ }
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_OBJ_INCR :
+ /* todo : support hook */
+ INCRDECR(+=);
+ break;
+ case GRN_OBJ_DECR :
+ /* todo : support hook */
+ INCRDECR(-=);
+ break;
+ default :
+ rc = GRN_OPERATION_NOT_SUPPORTED;
+ break;
+ }
+ grn_ra_unref(ctx, (grn_ra *)obj, id);
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ return rc;
+}
+
+static grn_rc
+grn_obj_set_value_column_index(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ char column_name[GRN_TABLE_MAX_KEY_SIZE];
+ int column_name_size;
+ column_name_size = grn_obj_name(ctx, obj, column_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "can't set value to index column directly: <%.*s>",
+ column_name_size, column_name);
+ return ctx->rc;
+}
+
+grn_rc
+grn_obj_set_value(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_obj *value, int flags)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (!GRN_DB_OBJP(obj)) {
+ if (obj->header.type == GRN_ACCESSOR) {
+ rc = grn_accessor_set_value(ctx, (grn_accessor *)obj, id, value, flags);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "not db_obj");
+ }
+ } else {
+ switch (obj->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ rc = grn_obj_set_value_table_pat_key(ctx, obj, id, value, flags);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ rc = GRN_OPERATION_NOT_SUPPORTED;
+ break;
+ case GRN_TABLE_HASH_KEY :
+ rc = grn_obj_set_value_table_hash_key(ctx, obj, id, value, flags);
+ break;
+ case GRN_TABLE_NO_KEY :
+ rc = grn_obj_set_value_table_no_key(ctx, obj, id, value, flags);
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ switch (obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) {
+ case GRN_OBJ_COLUMN_SCALAR :
+ rc = grn_obj_set_value_column_var_size_scalar(ctx, obj, id, value,
+ flags);
+ break;
+ case GRN_OBJ_COLUMN_VECTOR :
+ rc = grn_obj_set_value_column_var_size_vector(ctx, obj, id, value,
+ flags);
+ break;
+ default :
+ ERR(GRN_FILE_CORRUPT, "invalid GRN_OBJ_COLUMN_TYPE");
+ break;
+ }
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ rc = grn_obj_set_value_column_fix_size(ctx, obj, id, value, flags);
+ break;
+ case GRN_COLUMN_INDEX :
+ rc = grn_obj_set_value_column_index(ctx, obj, id, value, flags);
+ break;
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+const char *
+grn_obj_get_value_(grn_ctx *ctx, grn_obj *obj, grn_id id, uint32_t *size)
+{
+ const char *value = NULL;
+ *size = 0;
+ switch (obj->header.type) {
+ case GRN_ACCESSOR :
+ value = grn_accessor_get_value_(ctx, (grn_accessor *)obj, id, size);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ value = grn_pat_get_value_(ctx, (grn_pat *)obj, id, size);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "GRN_TABLE_DAT_KEY not supported");
+ break;
+ case GRN_TABLE_HASH_KEY :
+ value = grn_hash_get_value_(ctx, (grn_hash *)obj, id, size);
+ break;
+ case GRN_TABLE_NO_KEY :
+ if ((value = _grn_array_get_value(ctx, (grn_array *)obj, id))) {
+ *size = ((grn_array *)obj)->value_size;
+ }
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ {
+ grn_io_win jw;
+ if ((value = grn_ja_ref(ctx, (grn_ja *)obj, id, &jw, size))) {
+ grn_ja_unref(ctx, &jw);
+ }
+ }
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ if ((value = grn_ra_ref(ctx, (grn_ra *)obj, id))) {
+ grn_ra_unref(ctx, (grn_ra *)obj, id);
+ *size = ((grn_ra *)obj)->header->element_size;
+ }
+ break;
+ case GRN_COLUMN_INDEX :
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "todo: GRN_COLUMN_INDEX");
+ break;
+ }
+ return value;
+}
+
+static void
+grn_obj_get_value_column_index(grn_ctx *ctx, grn_obj *index_column,
+ grn_id id, grn_obj *value)
+{
+ grn_ii *ii = (grn_ii *)index_column;
+ grn_obj_ensure_bulk(ctx, value);
+ GRN_UINT32_SET(ctx, value, grn_ii_estimate_size(ctx, ii, id));
+ value->header.domain = GRN_DB_UINT32;
+}
+
+static grn_obj *
+grn_obj_get_value_column_vector(grn_ctx *ctx, grn_obj *obj,
+ grn_id id, grn_obj *value)
+{
+ grn_obj *lexicon;
+
+ lexicon = grn_ctx_at(ctx, DB_OBJ(obj)->range);
+ if (lexicon && !GRN_OBJ_TABLEP(lexicon) &&
+ (lexicon->header.flags & GRN_OBJ_KEY_VAR_SIZE)) {
+ grn_obj v_;
+ grn_obj_ensure_vector(ctx, value);
+ GRN_TEXT_INIT(&v_, 0);
+ grn_ja_get_value(ctx, (grn_ja *)obj, id, &v_);
+ grn_vector_decode(ctx, value, GRN_TEXT_VALUE(&v_), GRN_TEXT_LEN(&v_));
+ GRN_OBJ_FIN(ctx, &v_);
+ } else {
+ grn_obj_ensure_bulk(ctx, value);
+ grn_ja_get_value(ctx, (grn_ja *)obj, id, value);
+ value->header.type = GRN_UVECTOR;
+ if (obj->header.flags & GRN_OBJ_WITH_WEIGHT) {
+ value->header.flags |= GRN_OBJ_WITH_WEIGHT;
+ } else {
+ value->header.flags &= ~GRN_OBJ_WITH_WEIGHT;
+ }
+ }
+
+ return value;
+}
+
+grn_obj *
+grn_obj_get_value(grn_ctx *ctx, grn_obj *obj, grn_id id, grn_obj *value)
+{
+ GRN_API_ENTER;
+ if (!id) { goto exit; }
+ if (!obj) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_value failed");
+ goto exit;
+ }
+ if (!value) {
+ if (!(value = grn_obj_open(ctx, GRN_BULK, 0, 0))) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_value failed");
+ goto exit;
+ }
+ }
+ switch (value->header.type) {
+ case GRN_VOID :
+ GRN_TEXT_INIT(value, 0);
+ break;
+ case GRN_BULK :
+ case GRN_VECTOR :
+ case GRN_UVECTOR :
+ case GRN_MSG :
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_value failed");
+ goto exit;
+ }
+ switch (obj->header.type) {
+ case GRN_ACCESSOR :
+ grn_obj_ensure_bulk(ctx, value);
+ value = grn_accessor_get_value(ctx, (grn_accessor *)obj, id, value);
+ value->header.domain = grn_obj_get_range(ctx, obj);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ {
+ grn_pat *pat = (grn_pat *)obj;
+ uint32_t size = pat->value_size;
+ grn_obj_ensure_bulk(ctx, value);
+ if (grn_bulk_space(ctx, value, size)) {
+ MERR("grn_bulk_space failed");
+ goto exit;
+ }
+ {
+ char *curr = GRN_BULK_CURR(value);
+ grn_pat_get_value(ctx, pat, id, curr - size);
+ }
+ value->header.type = GRN_BULK;
+ value->header.domain = grn_obj_get_range(ctx, obj);
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "GRN_TABLE_DAT_KEY not supported");
+ break;
+ case GRN_TABLE_HASH_KEY :
+ {
+ grn_hash *hash = (grn_hash *)obj;
+ uint32_t size = hash->value_size;
+ grn_obj_ensure_bulk(ctx, value);
+ if (grn_bulk_space(ctx, value, size)) {
+ MERR("grn_bulk_space failed");
+ goto exit;
+ }
+ {
+ char *curr = GRN_BULK_CURR(value);
+ grn_hash_get_value(ctx, hash, id, curr - size);
+ }
+ value->header.type = GRN_BULK;
+ value->header.domain = grn_obj_get_range(ctx, obj);
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ {
+ grn_array *array = (grn_array *)obj;
+ uint32_t size = array->value_size;
+ grn_obj_ensure_bulk(ctx, value);
+ if (grn_bulk_space(ctx, value, size)) {
+ MERR("grn_bulk_space failed");
+ goto exit;
+ }
+ {
+ char *curr = GRN_BULK_CURR(value);
+ grn_array_get_value(ctx, array, id, curr - size);
+ }
+ value->header.type = GRN_BULK;
+ value->header.domain = grn_obj_get_range(ctx, obj);
+ }
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ switch (obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) {
+ case GRN_OBJ_COLUMN_VECTOR :
+ grn_obj_get_value_column_vector(ctx, obj, id, value);
+ break;
+ case GRN_OBJ_COLUMN_SCALAR :
+ grn_obj_ensure_bulk(ctx, value);
+ grn_ja_get_value(ctx, (grn_ja *)obj, id, value);
+ value->header.type = GRN_BULK;
+ break;
+ default :
+ ERR(GRN_FILE_CORRUPT, "invalid GRN_OBJ_COLUMN_TYPE");
+ break;
+ }
+ value->header.domain = grn_obj_get_range(ctx, obj);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ {
+ unsigned int element_size;
+ void *v = grn_ra_ref(ctx, (grn_ra *)obj, id);
+ grn_obj_ensure_bulk(ctx, value);
+ value->header.type = GRN_BULK;
+ value->header.domain = grn_obj_get_range(ctx, obj);
+ if (v) {
+ element_size = ((grn_ra *)obj)->header->element_size;
+ grn_bulk_write(ctx, value, v, element_size);
+ grn_ra_unref(ctx, (grn_ra *)obj, id);
+ }
+ }
+ break;
+ case GRN_COLUMN_INDEX :
+ grn_obj_get_value_column_index(ctx, obj, id, value);
+ break;
+ }
+exit :
+ GRN_API_RETURN(value);
+}
+
+int
+grn_obj_get_values(grn_ctx *ctx, grn_obj *obj, grn_id offset, void **values)
+{
+ int nrecords = -1;
+ GRN_API_ENTER;
+ if (obj->header.type == GRN_COLUMN_FIX_SIZE) {
+ grn_obj *domain = grn_column_table(ctx, obj);
+ if (domain) {
+ int table_size = (int)grn_table_size(ctx, domain);
+ if (0 < offset && offset <= table_size) {
+ grn_ra *ra = (grn_ra *)obj;
+ void *p = grn_ra_ref(ctx, ra, offset);
+ if (p) {
+ if ((offset >> ra->element_width) == (table_size >> ra->element_width)) {
+ nrecords = (table_size & ra->element_mask) + 1 - (offset & ra->element_mask);
+ } else {
+ nrecords = ra->element_mask + 1 - (offset & ra->element_mask);
+ }
+ if (values) { *values = p; }
+ grn_ra_unref(ctx, ra, offset);
+ } else {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "ra get failed");
+ }
+ } else {
+ nrecords = 0;
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "no domain found");
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "obj is not a fix sized column");
+ }
+ GRN_API_RETURN(nrecords);
+}
+
+grn_rc
+grn_column_index_update(grn_ctx *ctx, grn_obj *column,
+ grn_id id, unsigned int section,
+ grn_obj *oldvalue, grn_obj *newvalue)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (column->header.type != GRN_COLUMN_INDEX) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid column assigned");
+ } else {
+ rc = grn_ii_column_update(ctx, (grn_ii *)column, id, section, oldvalue, newvalue, NULL);
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_obj *
+grn_column_table(grn_ctx *ctx, grn_obj *column)
+{
+ grn_obj *obj = NULL;
+ grn_db_obj *col = DB_OBJ(column);
+ GRN_API_ENTER;
+ if (col) {
+ obj = grn_ctx_at(ctx, col->header.domain);
+ }
+ GRN_API_RETURN(obj);
+}
+
+grn_obj *
+grn_obj_get_info(grn_ctx *ctx, grn_obj *obj, grn_info_type type, grn_obj *valuebuf)
+{
+ GRN_API_ENTER;
+ switch (type) {
+ case GRN_INFO_SUPPORT_ZLIB :
+ if (!valuebuf && !(valuebuf = grn_obj_open(ctx, GRN_BULK, 0, GRN_DB_BOOL))) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "failed to open value buffer for GRN_INFO_ZLIB_SUPPORT");
+ goto exit;
+ }
+#ifdef GRN_WITH_ZLIB
+ GRN_BOOL_PUT(ctx, valuebuf, GRN_TRUE);
+#else
+ GRN_BOOL_PUT(ctx, valuebuf, GRN_FALSE);
+#endif
+ break;
+ case GRN_INFO_SUPPORT_LZO :
+ if (!valuebuf && !(valuebuf = grn_obj_open(ctx, GRN_BULK, 0, GRN_DB_BOOL))) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "failed to open value buffer for GRN_INFO_LZO_SUPPORT");
+ goto exit;
+ }
+#ifdef GRN_WITH_LZO
+ GRN_BOOL_PUT(ctx, valuebuf, GRN_TRUE);
+#else /* GRN_WITH_LZO */
+ GRN_BOOL_PUT(ctx, valuebuf, GRN_FALSE);
+#endif /* GRN_WITH_LZO */
+ break;
+ default :
+ if (!obj) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_info failed");
+ goto exit;
+ }
+ switch (type) {
+ case GRN_INFO_ENCODING :
+ if (!valuebuf) {
+ if (!(valuebuf = grn_obj_open(ctx, GRN_BULK, 0, 0))) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_info failed");
+ goto exit;
+ }
+ }
+ {
+ grn_encoding enc;
+ if (obj->header.type == GRN_DB) { obj = ((grn_db *)obj)->keys; }
+ switch (obj->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ enc = ((grn_pat *)obj)->encoding;
+ grn_bulk_write(ctx, valuebuf, (const char *)&enc, sizeof(grn_encoding));
+ break;
+ case GRN_TABLE_DAT_KEY :
+ enc = ((grn_dat *)obj)->encoding;
+ grn_bulk_write(ctx, valuebuf, (const char *)&enc, sizeof(grn_encoding));
+ break;
+ case GRN_TABLE_HASH_KEY :
+ enc = ((grn_hash *)obj)->encoding;
+ grn_bulk_write(ctx, valuebuf, (const char *)&enc, sizeof(grn_encoding));
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_info failed");
+ }
+ }
+ break;
+ case GRN_INFO_SOURCE :
+ if (!valuebuf) {
+ if (!(valuebuf = grn_obj_open(ctx, GRN_BULK, 0, 0))) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_info failed");
+ goto exit;
+ }
+ }
+ if (!GRN_DB_OBJP(obj)) {
+ ERR(GRN_INVALID_ARGUMENT, "only db_obj can accept GRN_INFO_SOURCE");
+ goto exit;
+ }
+ grn_bulk_write(ctx, valuebuf, DB_OBJ(obj)->source, DB_OBJ(obj)->source_size);
+ break;
+ case GRN_INFO_DEFAULT_TOKENIZER :
+ switch (DB_OBJ(obj)->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ valuebuf = ((grn_hash *)obj)->tokenizer;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ valuebuf = ((grn_pat *)obj)->tokenizer;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ valuebuf = ((grn_dat *)obj)->tokenizer;
+ break;
+ }
+ break;
+ case GRN_INFO_NORMALIZER :
+ switch (DB_OBJ(obj)->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ valuebuf = ((grn_hash *)obj)->normalizer;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ valuebuf = ((grn_pat *)obj)->normalizer;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ valuebuf = ((grn_dat *)obj)->normalizer;
+ break;
+ }
+ break;
+ default :
+ /* todo */
+ break;
+ }
+ }
+exit :
+ GRN_API_RETURN(valuebuf);
+}
+
+static void
+build_index(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_obj *src, **cp, **col, *target;
+ grn_id *s = DB_OBJ(obj)->source;
+ if (!(DB_OBJ(obj)->source_size) || !s) { return; }
+ if ((src = grn_ctx_at(ctx, *s))) {
+ target = GRN_OBJ_TABLEP(src) ? src : grn_ctx_at(ctx, src->header.domain);
+ if (target) {
+ int i, ncol = DB_OBJ(obj)->source_size / sizeof(grn_id);
+ grn_obj_flags flags;
+ grn_ii *ii = (grn_ii *)obj;
+ grn_bool use_grn_ii_build;
+ grn_table_get_info(ctx, ii->lexicon, &flags, NULL, NULL, NULL);
+ switch (flags & GRN_OBJ_TABLE_TYPE_MASK) {
+ case GRN_OBJ_TABLE_PAT_KEY :
+ case GRN_OBJ_TABLE_DAT_KEY :
+ use_grn_ii_build = GRN_TRUE;
+ break;
+ default :
+ use_grn_ii_build = GRN_FALSE;
+ }
+ if ((ii->header->flags & GRN_OBJ_WITH_WEIGHT)) {
+ use_grn_ii_build = GRN_FALSE;
+ }
+ if ((col = GRN_MALLOC(ncol * sizeof(grn_obj *)))) {
+ for (cp = col, i = ncol; i; s++, cp++, i--) {
+ if (!(*cp = grn_ctx_at(ctx, *s))) {
+ ERR(GRN_INVALID_ARGUMENT, "source invalid, n=%d",i);
+ GRN_FREE(col);
+ return;
+ }
+ if (GRN_OBJ_TABLEP(grn_ctx_at(ctx, DB_OBJ(*cp)->range))) {
+ use_grn_ii_build = GRN_FALSE;
+ }
+ }
+ if (use_grn_ii_build) {
+ uint64_t sparsity = 10;
+ if (getenv("GRN_INDEX_SPARSITY")) {
+ uint64_t v;
+ errno = 0;
+ v = strtoull(getenv("GRN_INDEX_SPARSITY"), NULL, 0);
+ if (!errno) { sparsity = v; }
+ }
+ grn_ii_build(ctx, ii, sparsity);
+ } else {
+ grn_table_cursor *tc;
+ if ((tc = grn_table_cursor_open(ctx, target, NULL, 0, NULL, 0,
+ 0, -1, GRN_CURSOR_BY_ID))) {
+ grn_id id;
+ grn_obj rv;
+ GRN_TEXT_INIT(&rv, 0);
+ while ((id = grn_table_cursor_next_inline(ctx, tc)) != GRN_ID_NIL) {
+ for (cp = col, i = ncol; i; i--, cp++) {
+ GRN_BULK_REWIND(&rv);
+ if (GRN_OBJ_TABLEP(*cp)) {
+ grn_table_get_key2(ctx, *cp, id, &rv);
+ } else {
+ grn_obj_get_value(ctx, *cp, id, &rv);
+ }
+ call_hook_for_build(ctx, *cp, id, &rv, 0);
+ }
+ }
+ GRN_OBJ_FIN(ctx, &rv);
+ grn_table_cursor_close(ctx, tc);
+ }
+ }
+ GRN_FREE(col);
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "invalid target");
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "invalid source");
+ }
+}
+
+static void
+update_source_hook(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_id *s = DB_OBJ(obj)->source;
+ int i, n = DB_OBJ(obj)->source_size / sizeof(grn_id);
+ default_set_value_hook_data hook_data = { DB_OBJ(obj)->id, 0 };
+ grn_obj *source, data;
+ GRN_TEXT_INIT(&data, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET_REF(&data, &hook_data, sizeof(hook_data));
+ for (i = 1; i <= n; i++, s++) {
+ hook_data.section = i;
+ if ((source = grn_ctx_at(ctx, *s))) {
+ switch (source->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ grn_obj_add_hook(ctx, source, GRN_HOOK_INSERT, 0, NULL, &data);
+ grn_obj_add_hook(ctx, source, GRN_HOOK_DELETE, 0, NULL, &data);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ case GRN_COLUMN_INDEX :
+ grn_obj_add_hook(ctx, source, GRN_HOOK_SET, 0, NULL, &data);
+ break;
+ default :
+ /* invalid target */
+ break;
+ }
+ }
+ }
+ grn_obj_close(ctx, &data);
+}
+
+static void
+del_hook(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry, grn_obj *hld)
+{
+ int i;
+ void *hld_value = NULL;
+ uint32_t hld_size = 0;
+ grn_hook **last;
+ hld_value = GRN_BULK_HEAD(hld);
+ hld_size = GRN_BULK_VSIZE(hld);
+ if (!hld_size) { return; }
+ for (i = 0, last = &DB_OBJ(obj)->hooks[entry]; *last; i++, last = &(*last)->next) {
+ if (!memcmp(NEXT_ADDR(*last), hld_value, hld_size)) {
+ grn_obj_delete_hook(ctx, obj, entry, i);
+ return;
+ }
+ }
+}
+
+static void
+delete_source_hook(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_id *s = DB_OBJ(obj)->source;
+ int i, n = DB_OBJ(obj)->source_size / sizeof(grn_id);
+ default_set_value_hook_data hook_data = { DB_OBJ(obj)->id, 0 };
+ grn_obj *source, data;
+ GRN_TEXT_INIT(&data, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET_REF(&data, &hook_data, sizeof(hook_data));
+ for (i = 1; i <= n; i++, s++) {
+ hook_data.section = i;
+ if ((source = grn_ctx_at(ctx, *s))) {
+ switch (source->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ del_hook(ctx, source, GRN_HOOK_INSERT, &data);
+ del_hook(ctx, source, GRN_HOOK_DELETE, &data);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ del_hook(ctx, source, GRN_HOOK_SET, &data);
+ break;
+ default :
+ /* invalid target */
+ break;
+ }
+ }
+ }
+ grn_obj_close(ctx, &data);
+}
+
+#define N_HOOK_ENTRIES 5
+
+grn_rc
+grn_hook_pack(grn_ctx *ctx, grn_db_obj *obj, grn_obj *buf)
+{
+ grn_rc rc;
+ grn_hook_entry e;
+ for (e = 0; e < N_HOOK_ENTRIES; e++) {
+ grn_hook *hooks;
+ for (hooks = obj->hooks[e]; hooks; hooks = hooks->next) {
+ grn_id id = hooks->proc ? hooks->proc->obj.id : 0;
+ if ((rc = grn_text_benc(ctx, buf, id + 1))) { goto exit; }
+ if ((rc = grn_text_benc(ctx, buf, hooks->hld_size))) { goto exit; }
+ if ((rc = grn_bulk_write(ctx, buf, (char *)NEXT_ADDR(hooks), hooks->hld_size))) { goto exit; }
+ }
+ if ((rc = grn_text_benc(ctx, buf, 0))) { goto exit; }
+ }
+exit :
+ return rc;
+}
+
+static grn_rc
+grn_hook_unpack(grn_ctx *ctx, grn_db_obj *obj, const char *buf, uint32_t buf_size)
+{
+ grn_hook_entry e;
+ const uint8_t *p = (uint8_t *)buf, *pe = p + buf_size;
+ for (e = 0; e < N_HOOK_ENTRIES; e++) {
+ grn_hook *new, **last = &obj->hooks[e];
+ for (;;) {
+ grn_id id;
+ uint32_t hld_size;
+ GRN_B_DEC(id, p);
+ if (!id--) { break; }
+ if (p >= pe) { return GRN_FILE_CORRUPT; }
+ GRN_B_DEC(hld_size, p);
+ if (p >= pe) { return GRN_FILE_CORRUPT; }
+ if (!(new = GRN_MALLOC(sizeof(grn_hook) + hld_size))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ if (id) {
+ new->proc = (grn_proc *)grn_ctx_at(ctx, id);
+ if (!new->proc) {
+ GRN_FREE(new);
+ return ctx->rc;
+ }
+ } else {
+ new->proc = NULL;
+ }
+ if ((new->hld_size = hld_size)) {
+ memcpy(NEXT_ADDR(new), p, hld_size);
+ p += hld_size;
+ }
+ *last = new;
+ last = &new->next;
+ if (p >= pe) { return GRN_FILE_CORRUPT; }
+ }
+ *last = NULL;
+ }
+ return GRN_SUCCESS;
+}
+
+void
+grn_obj_spec_save(grn_ctx *ctx, grn_db_obj *obj)
+{
+ grn_db *s;
+ grn_obj v, *b;
+ grn_obj_spec spec;
+ if (obj->id & GRN_OBJ_TMP_OBJECT) { return; }
+ if (!ctx->impl || !GRN_DB_OBJP(obj)) { return; }
+ if (!(s = (grn_db *)ctx->impl->db) || !s->specs) { return; }
+ GRN_OBJ_INIT(&v, GRN_VECTOR, 0, GRN_DB_TEXT);
+ if (!(b = grn_vector_body(ctx, &v))) { return; }
+ spec.header = obj->header;
+ spec.range = obj->range;
+ grn_bulk_write(ctx, b, (void *)&spec, sizeof(grn_obj_spec));
+ grn_vector_delimit(ctx, &v, 0, 0);
+ if (obj->header.flags & GRN_OBJ_CUSTOM_NAME) {
+ GRN_TEXT_PUTS(ctx, b, grn_obj_path(ctx, (grn_obj *)obj));
+ }
+ grn_vector_delimit(ctx, &v, 0, 0);
+ grn_bulk_write(ctx, b, obj->source, obj->source_size);
+ grn_vector_delimit(ctx, &v, 0, 0);
+ grn_hook_pack(ctx, obj, b);
+ grn_vector_delimit(ctx, &v, 0, 0);
+ switch (obj->header.type) {
+ case GRN_EXPR :
+ grn_expr_pack(ctx, b, (grn_obj *)obj);
+ grn_vector_delimit(ctx, &v, 0, 0);
+ break;
+ }
+ grn_ja_putv(ctx, s->specs, obj->id, &v, 0);
+ grn_obj_close(ctx, &v);
+}
+
+inline static grn_rc
+grn_obj_set_info_source_validate_report_error(grn_ctx *ctx,
+ grn_obj *column,
+ grn_obj *table_domain,
+ grn_obj *source,
+ grn_id source_type_id)
+{
+ char column_name[GRN_TABLE_MAX_KEY_SIZE];
+ char table_domain_name[GRN_TABLE_MAX_KEY_SIZE];
+ char source_name[GRN_TABLE_MAX_KEY_SIZE];
+ char source_type_name[GRN_TABLE_MAX_KEY_SIZE];
+ int column_name_size;
+ int table_domain_name_size;
+ int source_name_size;
+ int source_type_name_size;
+ grn_obj *source_type;
+
+ column_name_size = grn_obj_name(ctx, column,
+ column_name, GRN_TABLE_MAX_KEY_SIZE);
+ source_name_size = grn_obj_name(ctx, source,
+ source_name, GRN_TABLE_MAX_KEY_SIZE);
+ if (GRN_OBJ_TABLEP(source)) {
+ source_name[source_name_size] = '\0';
+ strncat(source_name, "._key",
+ GRN_TABLE_MAX_KEY_SIZE - source_name_size - 1);
+ source_name_size = strlen(source_name);
+ }
+ table_domain_name_size = grn_obj_name(ctx, table_domain,
+ table_domain_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ source_type = grn_ctx_at(ctx, source_type_id);
+ if (source_type) {
+ source_type_name_size = grn_obj_name(ctx, source_type,
+ source_type_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ grn_obj_unlink(ctx, source_type);
+ } else {
+ strncpy(source_type_name, "(nil)", GRN_TABLE_MAX_KEY_SIZE);
+ source_type_name_size = strlen(source_type_name);
+ }
+ ERR(GRN_INVALID_ARGUMENT,
+ "grn_obj_set_info(): GRN_INFO_SOURCE: "
+ "source type must equal to index table's key type: "
+ "source:<%.*s(%.*s)> index:<%.*s(%.*s)>",
+ source_name_size, source_name,
+ source_type_name_size, source_type_name,
+ column_name_size, column_name,
+ table_domain_name_size, table_domain_name);
+ return ctx->rc;
+}
+
+inline static grn_rc
+grn_obj_set_info_source_validate(grn_ctx *ctx, grn_obj *obj, grn_obj *value)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_id table_id;
+ grn_obj *table = NULL;
+ grn_id table_domain_id;
+ grn_obj *table_domain = NULL;
+ grn_id *source_ids;
+ int i, n_source_ids;
+
+ table_id = obj->header.domain;
+ table = grn_ctx_at(ctx, table_id);
+ if (!table) {
+ goto exit;
+ }
+
+ table_domain_id = table->header.domain;
+ table_domain = grn_ctx_at(ctx, table_domain_id);
+ if (!table_domain) {
+ goto exit;
+ }
+
+ source_ids = (grn_id *)GRN_BULK_HEAD(value);
+ n_source_ids = GRN_BULK_VSIZE(value) / sizeof(grn_id);
+ if (n_source_ids > 1 && !(obj->header.flags & GRN_OBJ_WITH_SECTION)) {
+ char index_name[GRN_TABLE_MAX_KEY_SIZE];
+ int index_name_size;
+ index_name_size = grn_obj_name(ctx, obj,
+ index_name, GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "grn_obj_set_info(): GRN_INFO_SOURCE: "
+ "multi column index must be created with WITH_SECTION flag: <%.*s>",
+ index_name_size, index_name);
+ goto exit;
+ }
+
+ if (!GRN_OBJ_TABLEP(table_domain)) {
+ goto exit;
+ }
+
+ for (i = 0; i < n_source_ids; i++) {
+ grn_id source_id = source_ids[i];
+ grn_obj *source;
+ grn_id source_type_id;
+
+ source = grn_ctx_at(ctx, source_id);
+ if (!source) {
+ continue;
+ }
+ if (GRN_OBJ_TABLEP(source)) {
+ source_type_id = source->header.domain;
+ } else {
+ source_type_id = DB_OBJ(source)->range;
+ }
+ if (table_domain_id != source_type_id) {
+ rc = grn_obj_set_info_source_validate_report_error(ctx,
+ obj,
+ table_domain,
+ source,
+ source_type_id);
+ }
+ grn_obj_unlink(ctx, source);
+ if (rc != GRN_SUCCESS) {
+ goto exit;
+ }
+ }
+
+exit:
+ if (table) {
+ grn_obj_unlink(ctx, table);
+ }
+ if (table_domain) {
+ grn_obj_unlink(ctx, table_domain);
+ }
+ return ctx->rc;
+}
+
+inline static void
+grn_obj_set_info_source_log(grn_ctx *ctx, grn_obj *obj, grn_obj *value)
+{
+ grn_obj buf;
+ grn_id *vp = (grn_id *)GRN_BULK_HEAD(value);
+ uint32_t vs = GRN_BULK_VSIZE(value), s = 0;
+ const char *n = _grn_table_key(ctx, ctx->impl->db, DB_OBJ(obj)->id, &s);
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_PUT(ctx, &buf, n, s);
+ GRN_TEXT_PUTC(ctx, &buf, ' ');
+ while (vs) {
+ n = _grn_table_key(ctx, ctx->impl->db, *vp++, &s);
+ GRN_TEXT_PUT(ctx, &buf, n, s);
+ vs -= sizeof(grn_id);
+ if (vs) { GRN_TEXT_PUTC(ctx, &buf, ','); }
+ }
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "DDL:set_source %.*s",
+ (int)GRN_BULK_VSIZE(&buf), GRN_BULK_HEAD(&buf));
+ GRN_OBJ_FIN(ctx, &buf);
+}
+
+inline static grn_rc
+grn_obj_set_info_source_update(grn_ctx *ctx, grn_obj *obj, grn_obj *value)
+{
+ void *v = GRN_BULK_HEAD(value);
+ uint32_t s = GRN_BULK_VSIZE(value);
+ if (s) {
+ void *v2 = GRN_MALLOC(s);
+ if (!v2) {
+ return ctx->rc;
+ }
+ memcpy(v2, v, s);
+ if (DB_OBJ(obj)->source) { GRN_FREE(DB_OBJ(obj)->source); }
+ DB_OBJ(obj)->source = v2;
+ DB_OBJ(obj)->source_size = s;
+
+ if (obj->header.type == GRN_COLUMN_INDEX) {
+ update_source_hook(ctx, obj);
+ build_index(ctx, obj);
+ }
+ } else {
+ DB_OBJ(obj)->source = NULL;
+ DB_OBJ(obj)->source_size = 0;
+ }
+
+ return GRN_SUCCESS;
+}
+
+inline static grn_rc
+grn_obj_set_info_source(grn_ctx *ctx, grn_obj *obj, grn_obj *value)
+{
+ grn_rc rc;
+
+ rc = grn_obj_set_info_source_validate(ctx, obj, value);
+ if (rc != GRN_SUCCESS) {
+ return rc;
+ }
+ grn_obj_set_info_source_log(ctx, obj, value);
+ rc = grn_obj_set_info_source_update(ctx, obj, value);
+ if (rc != GRN_SUCCESS) {
+ return rc;
+ }
+ grn_obj_spec_save(ctx, DB_OBJ(obj));
+
+ return rc;
+}
+
+grn_rc
+grn_obj_set_info(grn_ctx *ctx, grn_obj *obj, grn_info_type type, grn_obj *value)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (!obj) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_set_info failed");
+ goto exit;
+ }
+ switch (type) {
+ case GRN_INFO_SOURCE :
+ if (!GRN_DB_OBJP(obj)) {
+ ERR(GRN_INVALID_ARGUMENT, "only db_obj can accept GRN_INFO_SOURCE");
+ goto exit;
+ }
+ rc = grn_obj_set_info_source(ctx, obj, value);
+ break;
+ case GRN_INFO_DEFAULT_TOKENIZER :
+ if (!value || DB_OBJ(value)->header.type == GRN_PROC) {
+ switch (DB_OBJ(obj)->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ ((grn_hash *)obj)->tokenizer = value;
+ ((grn_hash *)obj)->header->tokenizer = grn_obj_id(ctx, value);
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ ((grn_pat *)obj)->tokenizer = value;
+ ((grn_pat *)obj)->header->tokenizer = grn_obj_id(ctx, value);
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ ((grn_dat *)obj)->tokenizer = value;
+ ((grn_dat *)obj)->header->tokenizer = grn_obj_id(ctx, value);
+ rc = GRN_SUCCESS;
+ break;
+ }
+ }
+ break;
+ case GRN_INFO_NORMALIZER :
+ if (!value || DB_OBJ(value)->header.type == GRN_PROC) {
+ switch (DB_OBJ(obj)->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ ((grn_hash *)obj)->normalizer = value;
+ ((grn_hash *)obj)->header->normalizer = grn_obj_id(ctx, value);
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_PAT_KEY :
+ ((grn_pat *)obj)->normalizer = value;
+ ((grn_pat *)obj)->header->normalizer = grn_obj_id(ctx, value);
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_TABLE_DAT_KEY :
+ ((grn_dat *)obj)->normalizer = value;
+ ((grn_dat *)obj)->header->normalizer = grn_obj_id(ctx, value);
+ rc = GRN_SUCCESS;
+ break;
+ }
+ }
+ break;
+ default :
+ /* todo */
+ break;
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+grn_obj *
+grn_obj_get_element_info(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_info_type type, grn_obj *valuebuf)
+{
+ GRN_API_ENTER;
+ GRN_API_RETURN(valuebuf);
+}
+
+grn_rc
+grn_obj_set_element_info(grn_ctx *ctx, grn_obj *obj, grn_id id,
+ grn_info_type type, grn_obj *value)
+{
+ GRN_API_ENTER;
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+grn_bool
+grn_obj_is_builtin(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_id id;
+
+ if (!obj) { return GRN_FALSE; }
+
+ id = grn_obj_id(ctx, obj);
+ if (id == GRN_ID_NIL) {
+ return GRN_FALSE;
+ } else {
+ return id < GRN_N_RESERVED_TYPES;
+ }
+}
+
+static void
+grn_hook_free(grn_ctx *ctx, grn_hook *h)
+{
+ grn_hook *curr, *next;
+ for (curr = h; curr; curr = next) {
+ next = curr->next;
+ GRN_FREE(curr);
+ }
+}
+
+grn_rc
+grn_obj_add_hook(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry,
+ int offset, grn_obj *proc, grn_obj *hld)
+{
+ grn_rc rc = GRN_SUCCESS;
+ GRN_API_ENTER;
+ if (!GRN_DB_OBJP(obj)) {
+ rc = GRN_INVALID_ARGUMENT;
+ } else {
+ int i;
+ void *hld_value = NULL;
+ uint32_t hld_size = 0;
+ grn_hook *new, **last = &DB_OBJ(obj)->hooks[entry];
+ if (hld) {
+ hld_value = GRN_BULK_HEAD(hld);
+ hld_size = GRN_BULK_VSIZE(hld);
+ }
+ if (!(new = GRN_MALLOC(sizeof(grn_hook) + hld_size))) {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ new->proc = (grn_proc *)proc;
+ new->hld_size = hld_size;
+ if (hld_size) {
+ memcpy(NEXT_ADDR(new), hld_value, hld_size);
+ }
+ for (i = 0; i != offset && *last; i++) { last = &(*last)->next; }
+ new->next = *last;
+ *last = new;
+ grn_obj_spec_save(ctx, DB_OBJ(obj));
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+int
+grn_obj_get_nhooks(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry)
+{
+ int res = 0;
+ GRN_API_ENTER;
+ {
+ grn_hook *hook = DB_OBJ(obj)->hooks[entry];
+ while (hook) {
+ res++;
+ hook = hook->next;
+ }
+ }
+ GRN_API_RETURN(res);
+}
+
+grn_obj *
+grn_obj_get_hook(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry,
+ int offset, grn_obj *hldbuf)
+{
+ grn_obj *res = NULL;
+ GRN_API_ENTER;
+ {
+ int i;
+ grn_hook *hook = DB_OBJ(obj)->hooks[entry];
+ for (i = 0; i < offset; i++) {
+ hook = hook->next;
+ if (!hook) { return NULL; }
+ }
+ res = (grn_obj *)hook->proc;
+ grn_bulk_write(ctx, hldbuf, (char *)NEXT_ADDR(hook), hook->hld_size);
+ }
+ GRN_API_RETURN(res);
+}
+
+grn_rc
+grn_obj_delete_hook(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry, int offset)
+{
+ GRN_API_ENTER;
+ {
+ int i = 0;
+ grn_hook *h, **last = &DB_OBJ(obj)->hooks[entry];
+ for (;;) {
+ if (!(h = *last)) { return GRN_INVALID_ARGUMENT; }
+ if (++i > offset) { break; }
+ last = &h->next;
+ }
+ *last = h->next;
+ GRN_FREE(h);
+ }
+ grn_obj_spec_save(ctx, DB_OBJ(obj));
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+static void
+remove_index(grn_ctx *ctx, grn_obj *obj, grn_hook_entry entry)
+{
+ grn_hook *h0, *hooks = DB_OBJ(obj)->hooks[entry];
+ DB_OBJ(obj)->hooks[entry] = NULL; /* avoid mutual recursive call */
+ while (hooks) {
+ default_set_value_hook_data *data = (void *)NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (!target) {
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+ int length;
+ length = grn_obj_name(ctx, obj, name, GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_UNKNOWN_ERROR,
+ "[column][remove][index] "
+ "hook has a dangling reference: %.*s", length, name);
+ } else if (target->header.type == GRN_COLUMN_INDEX) {
+ //TODO: multicolumn MULTI_COLUMN_INDEXP
+ _grn_obj_remove(ctx, target);
+ } else {
+ //TODO: err
+ char fn[GRN_TABLE_MAX_KEY_SIZE];
+ int flen;
+ flen = grn_obj_name(ctx, target, fn, GRN_TABLE_MAX_KEY_SIZE);
+ fn[flen] = '\0';
+ ERR(GRN_UNKNOWN_ERROR, "column has unsupported hooks, col=%s",fn);
+ }
+ h0 = hooks;
+ hooks = hooks->next;
+ GRN_FREE(h0);
+ }
+}
+
+static void
+remove_columns(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_hash *cols;
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ if (grn_table_columns(ctx, obj, "", 0, (grn_obj *)cols)) {
+ grn_id *key;
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (col) { _grn_obj_remove(ctx, col); }
+ });
+ }
+ grn_hash_close(ctx, cols);
+ }
+}
+
+static void
+_grn_obj_remove_db_index_columns(grn_ctx *ctx, grn_obj *db)
+{
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next_inline(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *obj = grn_ctx_at(ctx, id);
+ if (obj && obj->header.type == GRN_COLUMN_INDEX) {
+ _grn_obj_remove(ctx, obj);
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+}
+
+static void
+_grn_obj_remove_db_reference_columns(grn_ctx *ctx, grn_obj *db)
+{
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next_inline(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *obj = grn_ctx_at(ctx, id);
+ grn_obj *range = NULL;
+
+ if (!obj) {
+ continue;
+ }
+
+ switch (obj->header.type) {
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ if (!DB_OBJ(obj)->range) {
+ break;
+ }
+
+ range = grn_ctx_at(ctx, DB_OBJ(obj)->range);
+ if (!range) {
+ break;
+ }
+
+ switch (range->header.type) {
+ case GRN_TABLE_NO_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ _grn_obj_remove(ctx, obj);
+ break;
+ }
+ break;
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+}
+
+static void
+_grn_obj_remove_db_reference_tables(grn_ctx *ctx, grn_obj *db)
+{
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next_inline(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *obj = grn_ctx_at(ctx, id);
+ grn_obj *domain = NULL;
+
+ if (!obj) {
+ continue;
+ }
+
+ switch (obj->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ if (!obj->header.domain) {
+ break;
+ }
+
+ domain = grn_ctx_at(ctx, obj->header.domain);
+ if (!domain) {
+ break;
+ }
+
+ switch (domain->header.type) {
+ case GRN_TABLE_NO_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ _grn_obj_remove(ctx, obj);
+ break;
+ }
+ break;
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+}
+
+static void
+_grn_obj_remove_db_all_tables(grn_ctx *ctx, grn_obj *db)
+{
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next_inline(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *obj = grn_ctx_at(ctx, id);
+
+ if (!obj) {
+ continue;
+ }
+
+ switch (obj->header.type) {
+ case GRN_TABLE_NO_KEY :
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ _grn_obj_remove(ctx, obj);
+ break;
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+}
+
+static void
+_grn_obj_remove_db(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ const char *io_spath;
+ char *spath;
+ grn_db *s = (grn_db *)db;
+ unsigned char key_type;
+
+ if (s->specs &&
+ (io_spath = grn_obj_path(ctx, (grn_obj *)s->specs)) && *io_spath != '\0') {
+ if (!(spath = GRN_STRDUP(io_spath))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "cannot duplicate path: <%s>", io_spath);
+ return;
+ }
+ } else {
+ spath = NULL;
+ }
+
+ key_type = s->keys->header.type;
+
+ _grn_obj_remove_db_index_columns(ctx, db);
+ _grn_obj_remove_db_reference_columns(ctx, db);
+ _grn_obj_remove_db_reference_tables(ctx, db);
+ _grn_obj_remove_db_all_tables(ctx, db);
+
+ grn_obj_close(ctx, obj);
+
+ if (spath) {
+ grn_ja_remove(ctx, spath);
+ GRN_FREE(spath);
+ }
+
+ if (path) {
+ switch (key_type) {
+ case GRN_TABLE_PAT_KEY :
+ grn_pat_remove(ctx, path);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ grn_dat_remove(ctx, path);
+ break;
+ }
+ }
+}
+
+static grn_bool
+is_removable_table(grn_ctx *ctx, grn_obj *table, grn_obj *db)
+{
+ grn_bool removable = GRN_TRUE;
+ grn_id table_id;
+ char table_name[GRN_TABLE_MAX_KEY_SIZE];
+ int table_name_size;
+ grn_table_cursor *cursor;
+
+ table_id = DB_OBJ(table)->id;
+ table_name_size = grn_obj_name(ctx, table, table_name, GRN_TABLE_MAX_KEY_SIZE);
+ if ((cursor = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1,
+ GRN_CURSOR_BY_ID))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) {
+ grn_obj *object;
+
+ object = grn_ctx_at(ctx, id);
+ if (!object) {
+ ERRCLR(ctx);
+ continue;
+ }
+
+ switch (object->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ if (DB_OBJ(object)->id == table_id) {
+ break;
+ }
+
+ if (object->header.domain == table_id) {
+ char reference_table_name[GRN_TABLE_MAX_KEY_SIZE];
+ int reference_table_name_size;
+ reference_table_name_size =
+ grn_obj_name(ctx, object, reference_table_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_OPERATION_NOT_PERMITTED,
+ "[table][remove] a table that references the table exists: "
+ "<%.*s._key> -> <%.*s>",
+ reference_table_name_size, reference_table_name,
+ table_name_size, table_name);
+ removable = GRN_FALSE;
+ }
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ case GRN_COLUMN_FIX_SIZE :
+ if (object->header.domain == table_id) {
+ break;
+ }
+ if (DB_OBJ(object)->range == table_id) {
+ char column_name[GRN_TABLE_MAX_KEY_SIZE];
+ int column_name_size;
+ column_name_size = grn_obj_name(ctx, object, column_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_OPERATION_NOT_PERMITTED,
+ "[table][remove] a column that references the table exists: "
+ "<%.*s> -> <%.*s>",
+ column_name_size, column_name,
+ table_name_size, table_name);
+ removable = GRN_FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+ grn_obj_unlink(ctx, object);
+
+ if (!removable) {
+ break;
+ }
+ }
+ grn_table_cursor_close(ctx, cursor);
+ }
+
+ return removable;
+}
+
+static void
+_grn_obj_remove_pat(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ if (!is_removable_table(ctx, obj, db)) {
+ return;
+ }
+ remove_index(ctx, obj, GRN_HOOK_INSERT);
+ remove_columns(ctx, obj);
+ grn_obj_close(ctx, obj);
+ if (path) {
+ grn_ja_put(ctx, ((grn_db *)db)->specs, id, NULL, 0, GRN_OBJ_SET, NULL);
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ grn_pat_remove(ctx, path);
+ }
+ grn_obj_touch(ctx, db, NULL);
+}
+
+static void
+_grn_obj_remove_dat(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ if (!is_removable_table(ctx, obj, db)) {
+ return;
+ }
+ remove_index(ctx, obj, GRN_HOOK_INSERT);
+ remove_columns(ctx, obj);
+ grn_obj_close(ctx, obj);
+ if (path) {
+ grn_ja_put(ctx, ((grn_db *)db)->specs, id, NULL, 0, GRN_OBJ_SET, NULL);
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ grn_dat_remove(ctx, path);
+ }
+ grn_obj_touch(ctx, db, NULL);
+}
+
+static void
+_grn_obj_remove_hash(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ if (!is_removable_table(ctx, obj, db)) {
+ return;
+ }
+ remove_index(ctx, obj, GRN_HOOK_INSERT);
+ remove_columns(ctx, obj);
+ grn_obj_close(ctx, obj);
+ if (path) {
+ grn_ja_put(ctx, ((grn_db *)db)->specs, id, NULL, 0, GRN_OBJ_SET, NULL);
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ grn_hash_remove(ctx, path);
+ }
+ grn_obj_touch(ctx, db, NULL);
+}
+
+static void
+_grn_obj_remove_array(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ if (!is_removable_table(ctx, obj, db)) {
+ return;
+ }
+ remove_columns(ctx, obj);
+ grn_obj_close(ctx, obj);
+ if (path) {
+ grn_ja_put(ctx, ((grn_db *)db)->specs, id, NULL, 0, GRN_OBJ_SET, NULL);
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ grn_array_remove(ctx, path);
+ }
+ grn_obj_touch(ctx, db, NULL);
+}
+
+static void
+_grn_obj_remove_ja(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ remove_index(ctx, obj, GRN_HOOK_SET);
+ grn_obj_close(ctx, obj);
+ if (path) {
+ grn_ja_put(ctx, ((grn_db *)db)->specs, id, NULL, 0, GRN_OBJ_SET, NULL);
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ grn_ja_remove(ctx, path);
+ }
+ grn_obj_touch(ctx, db, NULL);
+}
+
+static void
+_grn_obj_remove_ra(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ remove_index(ctx, obj, GRN_HOOK_SET);
+ grn_obj_close(ctx, obj);
+ if (path) {
+ grn_ja_put(ctx, ((grn_db *)db)->specs, id, NULL, 0, GRN_OBJ_SET, NULL);
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ grn_ra_remove(ctx, path);
+ }
+ grn_obj_touch(ctx, db, NULL);
+}
+
+static void
+_grn_obj_remove_index(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ delete_source_hook(ctx, obj);
+ grn_obj_close(ctx, obj);
+ if (path) {
+ grn_ja_put(ctx, ((grn_db *)db)->specs, id, NULL, 0, GRN_OBJ_SET, NULL);
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ grn_ii_remove(ctx, path);
+ }
+ grn_obj_touch(ctx, db, NULL);
+}
+
+static void
+_grn_obj_remove_db_obj(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ grn_obj_close(ctx, obj);
+ if (!(id & GRN_OBJ_TMP_OBJECT)) {
+ grn_ja_put(ctx, ((grn_db *)db)->specs, id, NULL, 0, GRN_OBJ_SET, NULL);
+ grn_obj_delete_by_id(ctx, db, id, GRN_TRUE);
+ }
+ if (path) {
+ grn_io_remove(ctx, path);
+ }
+ grn_obj_touch(ctx, db, NULL);
+}
+
+static void
+_grn_obj_remove_other(grn_ctx *ctx, grn_obj *obj, grn_obj *db, grn_id id,
+ const char *path)
+{
+ grn_obj_close(ctx, obj);
+}
+
+static void
+_grn_obj_remove(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_id id = GRN_ID_NIL;
+ grn_obj *db = NULL;
+ const char *io_path;
+ char *path;
+ if (ctx->impl && ctx->impl->db) {
+ uint32_t s = 0;
+ const char *n = _grn_table_key(ctx, ctx->impl->db, DB_OBJ(obj)->id, &s);
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "DDL:obj_remove %.*s", s, n);
+ }
+ if ((io_path = grn_obj_path(ctx, obj)) && *io_path != '\0') {
+ if (!(path = GRN_STRDUP(io_path))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "cannot duplicate path: <%s>", io_path);
+ return;
+ }
+ } else {
+ path = NULL;
+ }
+ if (GRN_DB_OBJP(obj)) {
+ id = DB_OBJ(obj)->id;
+ db = DB_OBJ(obj)->db;
+ }
+ switch (obj->header.type) {
+ case GRN_DB :
+ _grn_obj_remove_db(ctx, obj, db, id, path);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ _grn_obj_remove_pat(ctx, obj, db, id, path);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ _grn_obj_remove_dat(ctx, obj, db, id, path);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ _grn_obj_remove_hash(ctx, obj, db, id, path);
+ break;
+ case GRN_TABLE_NO_KEY :
+ _grn_obj_remove_array(ctx, obj, db, id, path);
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ _grn_obj_remove_ja(ctx, obj, db, id, path);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ _grn_obj_remove_ra(ctx, obj, db, id, path);
+ break;
+ case GRN_COLUMN_INDEX :
+ _grn_obj_remove_index(ctx, obj, db, id, path);
+ break;
+ default :
+ if (GRN_DB_OBJP(obj)) {
+ _grn_obj_remove_db_obj(ctx, obj, db, id, path);
+ } else {
+ _grn_obj_remove_other(ctx, obj, db, id, path);
+ }
+ }
+ if (path) { GRN_FREE(path); }
+}
+
+grn_rc
+grn_obj_remove(grn_ctx *ctx, grn_obj *obj)
+{
+ GRN_API_ENTER;
+ if (ctx->impl && ctx->impl->db && ctx->impl->db != obj) {
+ grn_io *io = grn_obj_io(ctx->impl->db);
+ if (!grn_io_lock(ctx, io, grn_lock_timeout)) {
+ _grn_obj_remove(ctx, obj);
+ grn_io_unlock(io);
+ }
+ } else {
+ _grn_obj_remove(ctx, obj);
+ }
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_rc
+grn_table_update_by_id(grn_ctx *ctx, grn_obj *table, grn_id id,
+ const void *dest_key, unsigned int dest_key_size)
+{
+ grn_rc rc = GRN_OPERATION_NOT_SUPPORTED;
+ GRN_API_ENTER;
+ if (table->header.type == GRN_TABLE_DAT_KEY) {
+ grn_dat *dat = (grn_dat *)table;
+ if (dat->io && !(dat->io->flags & GRN_IO_TEMPORARY)) {
+ if (grn_io_lock(ctx, dat->io, grn_lock_timeout)) {
+ rc = ctx->rc;
+ } else {
+ rc = grn_dat_update_by_id(ctx, dat, id, dest_key, dest_key_size);
+ grn_io_unlock(dat->io);
+ }
+ } else {
+ rc = grn_dat_update_by_id(ctx, dat, id, dest_key, dest_key_size);
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_update(grn_ctx *ctx, grn_obj *table,
+ const void *src_key, unsigned int src_key_size,
+ const void *dest_key, unsigned int dest_key_size)
+{
+ grn_rc rc = GRN_OPERATION_NOT_SUPPORTED;
+ GRN_API_ENTER;
+ if (table->header.type == GRN_TABLE_DAT_KEY) {
+ rc = grn_dat_update(ctx, (grn_dat *)table,
+ src_key, src_key_size,
+ dest_key, dest_key_size);
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_obj_rename(grn_ctx *ctx, grn_obj *obj, const char *name, unsigned int name_size)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (ctx && ctx->impl && GRN_DB_P(ctx->impl->db) && GRN_DB_OBJP(obj) && !IS_TEMP(obj)) {
+ grn_db *s = (grn_db *)ctx->impl->db;
+ grn_obj *keys = (grn_obj *)s->keys;
+ rc = grn_table_update_by_id(ctx, keys, DB_OBJ(obj)->id, name, name_size);
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_table_rename(grn_ctx *ctx, grn_obj *table, const char *name, unsigned int name_size)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_hash *cols;
+
+ GRN_API_ENTER;
+
+ if (!GRN_OBJ_TABLEP(table)) {
+ char table_name[GRN_TABLE_MAX_KEY_SIZE];
+ int table_name_size;
+ table_name_size = grn_obj_name(ctx, table, table_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[table][rename] isn't table: <%.*s> -> <%.*s>",
+ table_name_size, table_name,
+ name_size, name);
+ goto exit;
+ }
+ if (IS_TEMP(table)) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[table][rename] temporary table doesn't have name: "
+ "(anonymous) -> <%.*s>",
+ name_size, name);
+ goto exit;
+ }
+
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ grn_table_columns(ctx, table, "", 0, (grn_obj *)cols);
+ if (!(rc = grn_obj_rename(ctx, table, name, name_size))) {
+ grn_id *key;
+ char fullname[GRN_TABLE_MAX_KEY_SIZE];
+ memcpy(fullname, name, name_size);
+ fullname[name_size] = GRN_DB_DELIMITER;
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (col) {
+ int colname_len = grn_column_name(ctx, col, fullname + name_size + 1,
+ GRN_TABLE_MAX_KEY_SIZE - name_size - 1);
+ if (colname_len) {
+ if ((rc = grn_obj_rename(ctx, col, fullname,
+ name_size + 1 + colname_len))) {
+ break;
+ }
+ }
+ }
+ });
+ }
+ grn_hash_close(ctx, cols);
+ }
+exit:
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_column_rename(grn_ctx *ctx, grn_obj *column, const char *name, unsigned int name_size)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(column)) {
+ char fullname[GRN_TABLE_MAX_KEY_SIZE];
+ grn_db *s = (grn_db *)DB_OBJ(column)->db;
+ int len = grn_table_get_key(ctx, s->keys, DB_OBJ(column)->header.domain,
+ fullname, GRN_TABLE_MAX_KEY_SIZE);
+ if (name_size + 1 + len > GRN_TABLE_MAX_KEY_SIZE) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][rename] too long column name: required name_size(%d) < %d"
+ ": <%.*s>.<%.*s>",
+ name_size, GRN_TABLE_MAX_KEY_SIZE - 1 - len,
+ len, fullname, name_size, name);
+ goto exit;
+ }
+ fullname[len] = GRN_DB_DELIMITER;
+ memcpy(fullname + len + 1, name, name_size);
+ name_size += len + 1;
+ rc = grn_obj_rename(ctx, column, fullname, name_size);
+ }
+exit :
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_obj_path_rename(grn_ctx *ctx, const char *old_path, const char *new_path)
+{
+ GRN_API_ENTER;
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+/* db must be validated by caller */
+grn_id
+grn_obj_register(grn_ctx *ctx, grn_obj *db, const char *name, unsigned int name_size)
+{
+ grn_id id = GRN_ID_NIL;
+ if (name && name_size) {
+ grn_db *s = (grn_db *)db;
+ int added;
+ if (!(id = grn_table_add(ctx, s->keys, name, name_size, &added))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "grn_table_add failed: <%.*s>", name_size, name);
+ } else if (!added) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "already used name was assigned: <%.*s>", name_size, name);
+ id = GRN_ID_NIL;
+ }
+ } else if (ctx->impl && ctx->impl->values) {
+ id = grn_array_add(ctx, ctx->impl->values, NULL) | GRN_OBJ_TMP_OBJECT;
+ }
+ return id;
+}
+
+grn_rc
+grn_obj_delete_by_id(grn_ctx *ctx, grn_obj *db, grn_id id, grn_bool removep)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (id) {
+ if (id & GRN_OBJ_TMP_OBJECT) {
+ if (ctx->impl && ctx->impl->values) {
+ rc = grn_array_delete_by_id(ctx, ctx->impl->values,
+ id & ~GRN_OBJ_TMP_OBJECT, NULL);
+ }
+ } else {
+ db_value *vp;
+ grn_db *s = (grn_db *)db;
+ if ((vp = grn_tiny_array_at(&s->values, id))) {
+ GRN_ASSERT(!vp->lock);
+ vp->lock = 0;
+ vp->ptr = NULL;
+ vp->done = 0;
+ }
+ if (removep) {
+ switch (s->keys->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ rc = grn_pat_delete_by_id(ctx, (grn_pat *)s->keys, id, NULL);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ rc = grn_dat_delete_by_id(ctx, (grn_dat *)s->keys, id, NULL);
+ break;
+ }
+ } else {
+ rc = GRN_SUCCESS;
+ }
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+
+grn_rc
+grn_obj_path_by_id(grn_ctx *ctx, grn_obj *db, grn_id id, char *buffer)
+{
+ grn_rc rc = GRN_SUCCESS;
+ GRN_API_ENTER;
+ if (!GRN_DB_P(db) || !buffer) {
+ rc = GRN_INVALID_ARGUMENT;
+ } else {
+ gen_pathname(grn_obj_io(db)->path, buffer, id);
+ }
+ GRN_API_RETURN(rc);
+}
+
+/* db must be validated by caller */
+grn_rc
+grn_db_obj_init(grn_ctx *ctx, grn_obj *db, grn_id id, grn_db_obj *obj)
+{
+ grn_rc rc = GRN_SUCCESS;
+ if (id) {
+ if (id & GRN_OBJ_TMP_OBJECT) {
+ if (ctx->impl && ctx->impl->values) {
+ rc = grn_array_set_value(ctx, ctx->impl->values,
+ id & ~GRN_OBJ_TMP_OBJECT, &obj, GRN_OBJ_SET);
+ }
+ } else {
+ db_value *vp;
+ vp = grn_tiny_array_at(&((grn_db *)db)->values, id);
+ if (!vp) {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ ERR(rc, "grn_tiny_array_at failed (%d)", id);
+ return rc;
+ }
+ vp->lock = 1;
+ vp->ptr = (grn_obj *)obj;
+ }
+ }
+ obj->id = id;
+ obj->db = db;
+ obj->source = NULL;
+ obj->source_size = 0;
+ {
+ grn_hook_entry entry;
+ for (entry = 0; entry < N_HOOK_ENTRIES; entry++) {
+ obj->hooks[entry] = NULL;
+ }
+ }
+ grn_obj_spec_save(ctx, obj);
+ return rc;
+}
+
+#define GET_PATH(spec,buffer,s,id) do {\
+ if (spec->header.flags & GRN_OBJ_CUSTOM_NAME) {\
+ const char *path;\
+ unsigned int size = grn_vector_get_element(ctx, &v, 1, &path, NULL, NULL); \
+ if (size > PATH_MAX) { ERR(GRN_FILENAME_TOO_LONG, "too long path"); }\
+ memcpy(buffer, path, size);\
+ buffer[size] = '\0';\
+ } else {\
+ gen_pathname(grn_obj_io(s->keys)->path, buffer, id); \
+ }\
+} while (0)
+
+#define UNPACK_INFO() do {\
+ if (vp->ptr) {\
+ grn_db_obj *r = DB_OBJ(vp->ptr);\
+ r->header = spec->header;\
+ r->id = id;\
+ r->range = spec->range;\
+ r->db = (grn_obj *)s;\
+ size = grn_vector_get_element(ctx, &v, 2, &p, NULL, NULL);\
+ if (size) {\
+ if ((r->source = GRN_MALLOC(size))) {\
+ memcpy(r->source, p, size);\
+ r->source_size = size;\
+ }\
+ }\
+ size = grn_vector_get_element(ctx, &v, 3, &p, NULL, NULL);\
+ grn_hook_unpack(ctx, r, p, size);\
+ }\
+} while (0)
+
+grn_obj *
+grn_ctx_at(grn_ctx *ctx, grn_id id)
+{
+ grn_obj *res = NULL;
+ if (!ctx || !ctx->impl || !id) { return res; }
+ GRN_API_ENTER;
+ if (id & GRN_OBJ_TMP_OBJECT) {
+ if (ctx->impl->values) {
+ grn_obj **tmp_obj;
+ tmp_obj = _grn_array_get_value(ctx, ctx->impl->values, id & ~GRN_OBJ_TMP_OBJECT);
+ if (tmp_obj) {
+ res = *tmp_obj;
+ }
+ }
+ } else {
+ grn_db *s = (grn_db *)ctx->impl->db;
+ if (s) {
+ db_value *vp;
+ uint32_t l, *pl, ntrial;
+ if (!(vp = grn_tiny_array_at(&s->values, id))) { goto exit; }
+#ifdef USE_NREF
+ pl = &vp->lock;
+ for (ntrial = 0;; ntrial++) {
+ GRN_ATOMIC_ADD_EX(pl, 1, l);
+ if (l < GRN_IO_MAX_REF) { break; }
+ if (ntrial >= 10) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "max trial in ctx_at(%p,%d)", vp->ptr, vp->lock);
+ break;
+ }
+ GRN_ATOMIC_ADD_EX(pl, -1, l);
+ GRN_FUTEX_WAIT(pl);
+ }
+#endif /* USE_NREF */
+ if (s->specs && !vp->ptr /* && !vp->done */) {
+#ifndef USE_NREF
+ pl = &vp->lock;
+ for (ntrial = 0;; ntrial++) {
+ GRN_ATOMIC_ADD_EX(pl, 1, l);
+ if (l < GRN_IO_MAX_REF) { break; }
+ if (ntrial >= 10) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "max trial in ctx_at(%p,%d)", vp->ptr, vp->lock);
+ break;
+ }
+ GRN_ATOMIC_ADD_EX(pl, -1, l);
+ GRN_FUTEX_WAIT(pl);
+ }
+#endif /* USE_NREF */
+ if (!l) {
+ grn_io_win jw;
+ uint32_t value_len;
+ char *value = grn_ja_ref(ctx, s->specs, id, &jw, &value_len);
+ if (value) {
+ grn_obj v;
+ GRN_OBJ_INIT(&v, GRN_VECTOR, 0, GRN_DB_TEXT);
+ if (!grn_vector_decode(ctx, &v, value, value_len)) {
+ const char *p;
+ uint32_t size;
+ grn_obj_spec *spec;
+ char buffer[PATH_MAX];
+ size = grn_vector_get_element(ctx, &v, 0, (const char **)&spec, NULL, NULL);
+ if (size) {
+ switch (spec->header.type) {
+ case GRN_TYPE :
+ vp->ptr = (grn_obj *)grn_type_open(ctx, spec);
+ UNPACK_INFO();
+ break;
+ case GRN_TABLE_HASH_KEY :
+ GET_PATH(spec, buffer, s, id);
+ vp->ptr = (grn_obj *)grn_hash_open(ctx, buffer);
+ if (vp->ptr) {
+ grn_obj_flags flags = vp->ptr->header.flags;
+ UNPACK_INFO();
+ vp->ptr->header.flags = flags;
+ }
+ break;
+ case GRN_TABLE_PAT_KEY :
+ GET_PATH(spec, buffer, s, id);
+ vp->ptr = (grn_obj *)grn_pat_open(ctx, buffer);
+ if (vp->ptr) {
+ grn_obj_flags flags = vp->ptr->header.flags;
+ UNPACK_INFO();
+ vp->ptr->header.flags = flags;
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ GET_PATH(spec, buffer, s, id);
+ vp->ptr = (grn_obj *)grn_dat_open(ctx, buffer);
+ if (vp->ptr) {
+ grn_obj_flags flags = vp->ptr->header.flags;
+ UNPACK_INFO();
+ vp->ptr->header.flags = flags;
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ GET_PATH(spec, buffer, s, id);
+ vp->ptr = (grn_obj *)grn_array_open(ctx, buffer);
+ UNPACK_INFO();
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ GET_PATH(spec, buffer, s, id);
+ vp->ptr = (grn_obj *)grn_ja_open(ctx, buffer);
+ UNPACK_INFO();
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ GET_PATH(spec, buffer, s, id);
+ vp->ptr = (grn_obj *)grn_ra_open(ctx, buffer);
+ UNPACK_INFO();
+ break;
+ case GRN_COLUMN_INDEX :
+ GET_PATH(spec, buffer, s, id);
+ {
+ grn_obj *table = grn_ctx_at(ctx, spec->header.domain);
+ vp->ptr = (grn_obj *)grn_ii_open(ctx, buffer, table);
+ }
+ UNPACK_INFO();
+ break;
+ case GRN_PROC :
+ GET_PATH(spec, buffer, s, id);
+ grn_plugin_register(ctx, buffer);
+ break;
+ case GRN_EXPR :
+ {
+ uint8_t *u;
+ size = grn_vector_get_element(ctx, &v, 4, &p, NULL, NULL);
+ u = (uint8_t *)p;
+ vp->ptr = grn_expr_open(ctx, spec, u, u + size);
+ }
+ break;
+ }
+ }
+ grn_obj_close(ctx, &v);
+ }
+ grn_ja_unref(ctx, &jw);
+ }
+#ifndef USE_NREF
+ GRN_ATOMIC_ADD_EX(pl, -1, l);
+#endif /* USE_NREF */
+ vp->done = 1;
+ GRN_FUTEX_WAKE(&vp->ptr);
+ } else {
+ for (ntrial = 0; !vp->ptr; ntrial++) {
+ if (ntrial >= 1000) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "max trial in ctx_at(%d,%p,%d)!", id, vp->ptr, vp->lock);
+ break;
+ }
+ GRN_FUTEX_WAIT(&vp->ptr);
+ }
+ }
+ }
+ res = vp->ptr;
+ }
+ }
+exit :
+ GRN_API_RETURN(res);
+}
+
+grn_obj *
+grn_obj_open(grn_ctx *ctx, unsigned char type, grn_obj_flags flags, grn_id domain)
+{
+ grn_obj *obj = GRN_MALLOCN(grn_obj, 1);
+ if (obj) {
+ GRN_OBJ_INIT(obj, type, flags, domain);
+ obj->header.impl_flags |= GRN_OBJ_ALLOCATED;
+ }
+ return obj;
+}
+
+grn_obj *
+grn_obj_graft(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_obj *new = grn_obj_open(ctx, obj->header.type, obj->header.impl_flags, obj->header.domain);
+ if (new) {
+ /* todo : deep copy if (obj->header.impl_flags & GRN_OBJ_DO_SHALLOW_COPY) */
+ new->u.b.head = obj->u.b.head;
+ new->u.b.curr = obj->u.b.curr;
+ new->u.b.tail = obj->u.b.tail;
+ obj->u.b.head = NULL;
+ obj->u.b.curr = NULL;
+ obj->u.b.tail = NULL;
+ }
+ return new;
+}
+
+grn_rc
+grn_obj_close(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ GRN_API_ENTER;
+ if (obj) {
+ if (GRN_DB_OBJP(obj)) {
+ grn_hook_entry entry;
+ if (DB_OBJ(obj)->finalizer) {
+ DB_OBJ(obj)->finalizer(ctx, 1, &obj, &DB_OBJ(obj)->user_data);
+ }
+ if (DB_OBJ(obj)->source) {
+ GRN_FREE(DB_OBJ(obj)->source);
+ }
+ for (entry = 0; entry < N_HOOK_ENTRIES; entry++) {
+ grn_hook_free(ctx, DB_OBJ(obj)->hooks[entry]);
+ }
+ grn_obj_delete_by_id(ctx, DB_OBJ(obj)->db, DB_OBJ(obj)->id, GRN_FALSE);
+ }
+ switch (obj->header.type) {
+ case GRN_VECTOR :
+ if (obj->u.v.body && !(obj->header.impl_flags & GRN_OBJ_REFER)) {
+ grn_obj_close(ctx, obj->u.v.body);
+ }
+ if (obj->u.v.sections) { GRN_FREE(obj->u.v.sections); }
+ if (obj->header.impl_flags & GRN_OBJ_ALLOCATED) { GRN_FREE(obj); }
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_VOID :
+ case GRN_BULK :
+ case GRN_PTR :
+ case GRN_UVECTOR :
+ case GRN_PVECTOR :
+ case GRN_MSG :
+ obj->header.type = GRN_VOID;
+ rc = grn_bulk_fin(ctx, obj);
+ if (obj->header.impl_flags & GRN_OBJ_ALLOCATED) { GRN_FREE(obj); }
+ break;
+ case GRN_ACCESSOR :
+ {
+ grn_accessor *p, *n;
+ for (p = (grn_accessor *)obj; p; p = n) {
+ n = p->next;
+ GRN_FREE(p);
+ }
+ }
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_SNIP :
+ rc = grn_snip_close(ctx, (grn_snip *)obj);
+ break;
+ case GRN_STRING :
+ rc = grn_string_close(ctx, obj);
+ break;
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ grn_pat_cursor_close(ctx, (grn_pat_cursor *)obj);
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ grn_dat_cursor_close(ctx, (grn_dat_cursor *)obj);
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ grn_hash_cursor_close(ctx, (grn_hash_cursor *)obj);
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ grn_array_cursor_close(ctx, (grn_array_cursor *)obj);
+ break;
+ case GRN_CURSOR_COLUMN_INDEX :
+ {
+ grn_index_cursor *ic = (grn_index_cursor *)obj;
+ if (ic->iic) { grn_ii_cursor_close(ctx, ic->iic); }
+ GRN_FREE(ic);
+ }
+ break;
+ case GRN_CURSOR_COLUMN_GEO_INDEX :
+ grn_geo_cursor_close(ctx, obj);
+ break;
+ case GRN_TYPE :
+ GRN_FREE(obj);
+ rc = GRN_SUCCESS;
+ break;
+ case GRN_DB :
+ rc = grn_db_close(ctx, obj);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ rc = grn_pat_close(ctx, (grn_pat *)obj);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ rc = grn_dat_close(ctx, (grn_dat *)obj);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ rc = grn_hash_close(ctx, (grn_hash *)obj);
+ break;
+ case GRN_TABLE_NO_KEY :
+ rc = grn_array_close(ctx, (grn_array *)obj);
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ rc = grn_ja_close(ctx, (grn_ja *)obj);
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ rc = grn_ra_close(ctx, (grn_ra *)obj);
+ break;
+ case GRN_COLUMN_INDEX :
+ rc = grn_ii_close(ctx, (grn_ii *)obj);
+ break;
+ case GRN_PROC :
+ {
+ uint32_t i;
+ grn_proc *p = (grn_proc *)obj;
+ /*
+ if (obj->header.domain) {
+ grn_hash_delete(ctx, ctx->impl->qe, &obj->header.domain, sizeof(grn_id), NULL);
+ }
+ */
+ for (i = 0; i < p->nvars; i++) {
+ grn_obj_close(ctx, &p->vars[i].value);
+ }
+ GRN_REALLOC(p->vars, 0);
+ grn_obj_close(ctx, &p->name_buf);
+ if (p->obj.range != GRN_ID_NIL) {
+ grn_plugin_close(ctx, p->obj.range);
+ }
+ GRN_FREE(obj);
+ rc = GRN_SUCCESS;
+ }
+ break;
+ case GRN_EXPR :
+ rc = grn_expr_close(ctx, obj);
+ break;
+ }
+ }
+ GRN_API_RETURN(rc);
+}
+
+void
+grn_obj_unlink(grn_ctx *ctx, grn_obj *obj)
+{
+ if (obj &&
+ (!GRN_DB_OBJP(obj) ||
+ (((grn_db_obj *)obj)->id & GRN_OBJ_TMP_OBJECT) ||
+ (((grn_db_obj *)obj)->id == GRN_ID_NIL) ||
+ obj->header.type == GRN_DB)) {
+ grn_obj_close(ctx, obj);
+ } else if (GRN_DB_OBJP(obj)) {
+#ifdef USE_NREF
+ grn_db_obj *dob = DB_OBJ(obj);
+ grn_db *s = (grn_db *)dob->db;
+ db_value *vp = grn_tiny_array_at(&s->values, dob->id);
+ if (vp) {
+ uint32_t l, *pl = &vp->lock;
+ if (!vp->lock) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "invalid unlink(%p,%d)", obj, vp->lock);
+ return;
+ }
+ GRN_ATOMIC_ADD_EX(pl, -1, l);
+ if (l == 1) {
+ GRN_ATOMIC_ADD_EX(pl, GRN_IO_MAX_REF, l);
+ if (l == GRN_IO_MAX_REF) {
+#ifdef CALL_FINALIZER
+ grn_obj_close(ctx, obj);
+ vp->done = 0;
+ if (dob->finalizer) {
+ dob->finalizer(ctx, 1, &obj, &dob->user_data);
+ dob->finalizer = NULL;
+ dob->user_data.ptr = NULL;
+ }
+#endif /* CALL_FINALIZER */
+ }
+ GRN_ATOMIC_ADD_EX(pl, -GRN_IO_MAX_REF, l);
+ GRN_FUTEX_WAKE(pl);
+ }
+ }
+#endif /* USE_NREF */
+ }
+}
+
+#define VECTOR_CLEAR(ctx,obj) do {\
+ if ((obj)->u.v.body && !((obj)->header.impl_flags & GRN_OBJ_REFER)) {\
+ grn_obj_close((ctx), (obj)->u.v.body);\
+ }\
+ if ((obj)->u.v.sections) { GRN_FREE((obj)->u.v.sections); }\
+ (obj)->header.impl_flags &= ~GRN_OBJ_DO_SHALLOW_COPY;\
+ (obj)->u.b.head = NULL;\
+ (obj)->u.b.curr = NULL;\
+ (obj)->u.b.tail = NULL;\
+} while (0)
+
+static void
+grn_obj_ensure_vector(grn_ctx *ctx, grn_obj *obj)
+{
+ if (obj->header.type != GRN_VECTOR) { grn_bulk_fin(ctx, obj); }
+ obj->header.type = GRN_VECTOR;
+ obj->header.flags &= ~GRN_OBJ_WITH_WEIGHT;
+}
+
+static void
+grn_obj_ensure_bulk(grn_ctx *ctx, grn_obj *obj)
+{
+ if (obj->header.type == GRN_VECTOR) { VECTOR_CLEAR(ctx, obj); }
+ obj->header.type = GRN_BULK;
+ obj->header.flags &= ~GRN_OBJ_WITH_WEIGHT;
+}
+
+grn_rc
+grn_obj_reinit(grn_ctx *ctx, grn_obj *obj, grn_id domain, unsigned char flags)
+{
+ if (!GRN_OBJ_MUTABLE(obj)) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid obj assigned");
+ } else {
+ switch (domain) {
+ case GRN_DB_VOID :
+ if (obj->header.type == GRN_VECTOR) { VECTOR_CLEAR(ctx, obj); }
+ obj->header.type = GRN_VOID;
+ obj->header.domain = domain;
+ GRN_BULK_REWIND(obj);
+ break;
+ case GRN_DB_OBJECT :
+ case GRN_DB_BOOL :
+ case GRN_DB_INT8 :
+ case GRN_DB_UINT8 :
+ case GRN_DB_INT16 :
+ case GRN_DB_UINT16 :
+ case GRN_DB_INT32 :
+ case GRN_DB_UINT32 :
+ case GRN_DB_INT64 :
+ case GRN_DB_UINT64 :
+ case GRN_DB_FLOAT :
+ case GRN_DB_TIME :
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ if (obj->header.type == GRN_VECTOR) { VECTOR_CLEAR(ctx, obj); }
+ obj->header.type = (flags & GRN_OBJ_VECTOR) ? GRN_UVECTOR : GRN_BULK;
+ obj->header.domain = domain;
+ GRN_BULK_REWIND(obj);
+ break;
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ if (flags & GRN_OBJ_VECTOR) {
+ if (obj->header.type != GRN_VECTOR) { grn_bulk_fin(ctx, obj); }
+ obj->header.type = GRN_VECTOR;
+ if (obj->u.v.body) {
+ grn_obj_reinit(ctx, obj->u.v.body, domain, 0);
+ }
+ obj->u.v.n_sections = 0;
+ } else {
+ if (obj->header.type == GRN_VECTOR) { VECTOR_CLEAR(ctx, obj); }
+ obj->header.type = GRN_BULK;
+ }
+ obj->header.domain = domain;
+ GRN_BULK_REWIND(obj);
+ break;
+ default :
+ {
+ grn_obj *d = grn_ctx_at(ctx, domain);
+ if (!d) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid domain assigned");
+ } else {
+ if (d->header.type == GRN_TYPE && (d->header.flags & GRN_OBJ_KEY_VAR_SIZE)) {
+ if (flags & GRN_OBJ_VECTOR) {
+ if (obj->header.type != GRN_VECTOR) { grn_bulk_fin(ctx, obj); }
+ obj->header.type = GRN_VECTOR;
+ } else {
+ if (obj->header.type == GRN_VECTOR) { VECTOR_CLEAR(ctx, obj); }
+ obj->header.type = GRN_BULK;
+ }
+ } else {
+ if (obj->header.type == GRN_VECTOR) { VECTOR_CLEAR(ctx, obj); }
+ obj->header.type = (flags & GRN_OBJ_VECTOR) ? GRN_UVECTOR : GRN_BULK;
+ }
+ obj->header.domain = domain;
+ GRN_BULK_REWIND(obj);
+ }
+ }
+ break;
+ }
+ }
+ return ctx->rc;
+}
+
+grn_rc
+grn_obj_reinit_for(grn_ctx *ctx, grn_obj *obj, grn_obj *domain_obj)
+{
+ grn_id domain = GRN_ID_NIL;
+ grn_obj_flags flags = 0;
+
+ if (!GRN_DB_OBJP(domain_obj) && domain_obj->header.type != GRN_ACCESSOR) {
+ grn_obj inspected;
+ GRN_TEXT_INIT(&inspected, 0);
+ limited_size_inspect(ctx, &inspected, domain_obj);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[reinit] invalid domain object: <%.*s>",
+ (int)GRN_TEXT_LEN(&inspected), GRN_TEXT_VALUE(&inspected));
+ GRN_OBJ_FIN(ctx, &inspected);
+ return ctx->rc;
+ }
+
+ grn_obj_get_range_info(ctx, domain_obj, &domain, &flags);
+ if (GRN_OBJ_TABLEP(domain_obj) &&
+ domain_obj->header.type != GRN_TABLE_NO_KEY) {
+ domain = domain_obj->header.domain;
+ }
+ return grn_obj_reinit(ctx, obj, domain, flags);
+}
+
+const char *
+grn_obj_path(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_io *io;
+ const char *path = NULL;
+ GRN_API_ENTER;
+ if (obj->header.type == GRN_PROC) {
+ path = grn_plugin_path(ctx, DB_OBJ(obj)->range);
+ GRN_API_RETURN(path);
+ }
+ io = grn_obj_io(obj);
+ if (io && !(io->flags & GRN_IO_TEMPORARY)) { path = io->path; }
+ GRN_API_RETURN(path);
+}
+
+int
+grn_obj_name(grn_ctx *ctx, grn_obj *obj, char *namebuf, int buf_size)
+{
+ int len = 0;
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(obj)) {
+ if (DB_OBJ(obj)->id) {
+ grn_db *s = (grn_db *)DB_OBJ(obj)->db;
+ if (!(DB_OBJ(obj)->id & GRN_OBJ_TMP_OBJECT)) {
+ len = grn_table_get_key(ctx, s->keys, DB_OBJ(obj)->id, namebuf, buf_size);
+ }
+ }
+ }
+ GRN_API_RETURN(len);
+}
+
+int
+grn_column_name(grn_ctx *ctx, grn_obj *obj, char *namebuf, int buf_size)
+{
+ int len = 0;
+ char buf[GRN_TABLE_MAX_KEY_SIZE];
+ if (!obj) { return len; }
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(obj)) {
+ if (DB_OBJ(obj)->id && DB_OBJ(obj)->id < GRN_ID_MAX) {
+ grn_db *s = (grn_db *)DB_OBJ(obj)->db;
+ len = grn_table_get_key(ctx, s->keys, DB_OBJ(obj)->id, buf, GRN_TABLE_MAX_KEY_SIZE);
+ if (len) {
+ int cl;
+ char *p = buf, *p0 = p, *pe = p + len;
+ for (; p < pe && (cl = grn_charlen(ctx, p, pe)); p += cl) {
+ if (*p == GRN_DB_DELIMITER && cl == 1) { p0 = p + cl; }
+ }
+ len = pe - p0;
+ if (len && len <= buf_size) {
+ memcpy(namebuf, p0, len);
+ }
+ }
+ }
+ } else if (obj->header.type == GRN_ACCESSOR) {
+ const char *name = NULL;
+ grn_accessor *a;
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ name = GRN_COLUMN_NAME_ID;
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ name = GRN_COLUMN_NAME_KEY;
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ name = GRN_COLUMN_NAME_VALUE;
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ name = GRN_COLUMN_NAME_SCORE;
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ name = GRN_COLUMN_NAME_NSUBRECS;
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ case GRN_ACCESSOR_GET_DB_OBJ :
+ case GRN_ACCESSOR_LOOKUP :
+ case GRN_ACCESSOR_FUNCALL :
+ break;
+ }
+ }
+ if (name) {
+ len = strlen(name);
+ if (len <= buf_size) {
+ memcpy(namebuf, name, len);
+ }
+ }
+ }
+ GRN_API_RETURN(len);
+}
+
+grn_rc
+grn_column_name_(grn_ctx *ctx, grn_obj *obj, grn_obj *buf)
+{
+ if (GRN_DB_OBJP(obj)) {
+ if (DB_OBJ(obj)->id && DB_OBJ(obj)->id < GRN_ID_MAX) {
+ uint32_t len;
+ grn_db *s = (grn_db *)DB_OBJ(obj)->db;
+ const char *p = _grn_table_key(ctx, s->keys, DB_OBJ(obj)->id, &len);
+ if (len) {
+ int cl;
+ const char *p0 = p, *pe = p + len;
+ for (; p < pe && (cl = grn_charlen(ctx, p, pe)); p += cl) {
+ if (*p == GRN_DB_DELIMITER && cl == 1) { p0 = p + cl; }
+ }
+ GRN_TEXT_PUT(ctx, buf, p0, pe - p0);
+ }
+ }
+ } else if (obj->header.type == GRN_ACCESSOR) {
+ grn_accessor *a;
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ GRN_TEXT_PUT(ctx, buf, GRN_COLUMN_NAME_ID, GRN_COLUMN_NAME_ID_LEN);
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ if (!a->next) {
+ GRN_TEXT_PUT(ctx, buf, GRN_COLUMN_NAME_KEY, GRN_COLUMN_NAME_KEY_LEN);
+ }
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ if (!a->next) {
+ GRN_TEXT_PUT(ctx, buf,
+ GRN_COLUMN_NAME_VALUE,
+ GRN_COLUMN_NAME_VALUE_LEN);
+ }
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ GRN_TEXT_PUT(ctx, buf,
+ GRN_COLUMN_NAME_SCORE,
+ GRN_COLUMN_NAME_SCORE_LEN);
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ GRN_TEXT_PUT(ctx, buf,
+ GRN_COLUMN_NAME_NSUBRECS,
+ GRN_COLUMN_NAME_NSUBRECS_LEN);
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ grn_column_name_(ctx, a->obj, buf);
+ if (a->next) { GRN_TEXT_PUTC(ctx, buf, '.'); }
+ break;
+ case GRN_ACCESSOR_GET_DB_OBJ :
+ case GRN_ACCESSOR_LOOKUP :
+ case GRN_ACCESSOR_FUNCALL :
+ break;
+ }
+ }
+ }
+ return ctx->rc;
+}
+
+int
+grn_obj_expire(grn_ctx *ctx, grn_obj *obj, int threshold)
+{
+ GRN_API_ENTER;
+ GRN_API_RETURN(0);
+}
+
+int
+grn_obj_check(grn_ctx *ctx, grn_obj *obj)
+{
+ GRN_API_ENTER;
+ GRN_API_RETURN(0);
+}
+
+grn_rc
+grn_obj_lock(grn_ctx *ctx, grn_obj *obj, grn_id id, int timeout)
+{
+ grn_rc rc = GRN_SUCCESS;
+ GRN_API_ENTER;
+ rc = grn_io_lock(ctx, grn_obj_io(obj), timeout);
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_obj_unlock(grn_ctx *ctx, grn_obj *obj, grn_id id)
+{
+ GRN_API_ENTER;
+ grn_io_unlock(grn_obj_io(obj));
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+grn_user_data *
+grn_obj_user_data(grn_ctx *ctx, grn_obj *obj)
+{
+ if (!GRN_DB_OBJP(obj)) { return NULL; }
+ return &DB_OBJ(obj)->user_data;
+}
+
+grn_rc
+grn_obj_set_finalizer(grn_ctx *ctx, grn_obj *obj, grn_proc_func *func)
+{
+ if (!GRN_DB_OBJP(obj)) { return GRN_INVALID_ARGUMENT; }
+ DB_OBJ(obj)->finalizer = func;
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_obj_clear_lock(grn_ctx *ctx, grn_obj *obj)
+{
+ GRN_API_ENTER;
+ switch (obj->header.type) {
+ case GRN_DB:
+ {
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, obj, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next_inline(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *tbl = grn_ctx_at(ctx, id);
+ if (tbl) {
+ switch (tbl->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY:
+ case GRN_TABLE_DAT_KEY:
+ case GRN_TABLE_NO_KEY:
+ grn_obj_clear_lock(ctx, tbl);
+ }
+ } else {
+ if (ctx->rc != GRN_SUCCESS) {
+ ERRCLR(ctx);
+ }
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+ }
+ grn_io_clear_lock(grn_obj_io(obj));
+ break;
+ case GRN_TABLE_NO_KEY :
+ grn_array_queue_lock_clear(ctx, (grn_array *)obj);
+ /* fallthru */
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ {
+ grn_hash *cols;
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ if (grn_table_columns(ctx, obj, "", 0, (grn_obj *)cols)) {
+ grn_id *key;
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (col) { grn_obj_clear_lock(ctx, col); }
+ });
+ }
+ grn_hash_close(ctx, cols);
+ }
+ grn_io_clear_lock(grn_obj_io(obj));
+ }
+ break;
+ case GRN_COLUMN_FIX_SIZE:
+ case GRN_COLUMN_VAR_SIZE:
+ case GRN_COLUMN_INDEX:
+ grn_io_clear_lock(grn_obj_io(obj));
+ break;
+ }
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+unsigned int
+grn_obj_is_locked(grn_ctx *ctx, grn_obj *obj)
+{
+ unsigned int res = 0;
+ GRN_API_ENTER;
+ res = grn_io_is_locked(grn_obj_io(obj));
+ GRN_API_RETURN(res);
+}
+
+grn_obj *
+grn_obj_db(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_obj *db = NULL;
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(obj)) { db = DB_OBJ(obj)->db; }
+ GRN_API_RETURN(db);
+}
+
+grn_id
+grn_obj_id(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_id id = GRN_ID_NIL;
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(obj)) {
+ id = DB_OBJ(obj)->id;
+ }
+ GRN_API_RETURN(id);
+}
+
+int
+grn_obj_defrag(grn_ctx *ctx, grn_obj *obj, int threshold)
+{
+ int r = 0;
+ GRN_API_ENTER;
+ switch (obj->header.type) {
+ case GRN_DB:
+ {
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, obj, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next_inline(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *ja = grn_ctx_at(ctx, id);
+ if (ja && ja->header.type == GRN_COLUMN_VAR_SIZE) {
+ r += grn_ja_defrag(ctx, (grn_ja *)ja, threshold);
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ {
+ grn_hash *cols;
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ if (grn_table_columns(ctx, obj, "", 0, (grn_obj *)cols)) {
+ grn_id *key;
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (col) {
+ r += grn_obj_defrag(ctx, col, threshold);
+ grn_obj_unlink(ctx, col);
+ }
+ });
+ }
+ grn_hash_close(ctx, cols);
+ }
+ }
+ break;
+ case GRN_COLUMN_VAR_SIZE:
+ r = grn_ja_defrag(ctx, (grn_ja *)obj, threshold);
+ break;
+ }
+ GRN_API_RETURN(r);
+}
+
+/**** sort ****/
+
+typedef struct {
+ grn_id id;
+ uint32_t size;
+ const void *value;
+} sort_entry;
+
+enum {
+ KEY_ID = 0,
+ KEY_BULK,
+ KEY_INT8,
+ KEY_INT16,
+ KEY_INT32,
+ KEY_INT64,
+ KEY_UINT8,
+ KEY_UINT16,
+ KEY_UINT32,
+ KEY_UINT64,
+ KEY_FLOAT32,
+ KEY_FLOAT64,
+};
+
+#define CMPNUM(type) do {\
+ if (as) {\
+ if (bs) {\
+ type va = *((type *)(ap));\
+ type vb = *((type *)(bp));\
+ if (va != vb) { return va > vb; }\
+ } else {\
+ return 1;\
+ }\
+ } else {\
+ if (bs) { return 0; }\
+ }\
+} while (0)
+
+inline static int
+compare_value(grn_ctx *ctx, sort_entry *a, sort_entry *b,
+ grn_table_sort_key *keys, int n_keys)
+{
+ int i;
+ uint8_t type;
+ uint32_t as, bs;
+ const unsigned char *ap, *bp;
+ for (i = 0; i < n_keys; i++, keys++) {
+ if (i) {
+ const char *ap_raw, *bp_raw;
+ if (keys->flags & GRN_TABLE_SORT_DESC) {
+ ap_raw = grn_obj_get_value_(ctx, keys->key, b->id, &as);
+ bp_raw = grn_obj_get_value_(ctx, keys->key, a->id, &bs);
+ } else {
+ ap_raw = grn_obj_get_value_(ctx, keys->key, a->id, &as);
+ bp_raw = grn_obj_get_value_(ctx, keys->key, b->id, &bs);
+ }
+ ap = (const unsigned char *)ap_raw;
+ bp = (const unsigned char *)bp_raw;
+ } else {
+ if (keys->flags & GRN_TABLE_SORT_DESC) {
+ ap = b->value; as = b->size;
+ bp = a->value; bs = a->size;
+ } else {
+ ap = a->value; as = a->size;
+ bp = b->value; bs = b->size;
+ }
+ }
+ type = keys->offset;
+ switch (type) {
+ case KEY_ID :
+ if (ap != bp) { return ap > bp; }
+ break;
+ case KEY_BULK :
+ for (;; ap++, bp++, as--, bs--) {
+ if (!as) { if (bs) { return 0; } else { break; } }
+ if (!bs) { return 1; }
+ if (*ap < *bp) { return 0; }
+ if (*ap > *bp) { return 1; }
+ }
+ break;
+ case KEY_INT8 :
+ CMPNUM(int8_t);
+ break;
+ case KEY_INT16 :
+ CMPNUM(int16_t);
+ break;
+ case KEY_INT32 :
+ CMPNUM(int32_t);
+ break;
+ case KEY_INT64 :
+ CMPNUM(int64_t);
+ break;
+ case KEY_UINT8 :
+ CMPNUM(uint8_t);
+ break;
+ case KEY_UINT16 :
+ CMPNUM(uint16_t);
+ break;
+ case KEY_UINT32 :
+ CMPNUM(uint32_t);
+ break;
+ case KEY_UINT64 :
+ CMPNUM(uint64_t);
+ break;
+ case KEY_FLOAT32 :
+ if (as) {
+ if (bs) {
+ float va = *((float *)(ap));
+ float vb = *((float *)(bp));
+ if (va < vb || va > vb) { return va > vb; }
+ } else {
+ return 1;
+ }
+ } else {
+ if (bs) { return 0; }
+ }
+ break;
+ case KEY_FLOAT64 :
+ if (as) {
+ if (bs) {
+ double va = *((double *)(ap));
+ double vb = *((double *)(bp));
+ if (va < vb || va > vb) { return va > vb; }
+ } else {
+ return 1;
+ }
+ } else {
+ if (bs) { return 0; }
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+inline static void
+swap(sort_entry *a, sort_entry *b)
+{
+ sort_entry c_ = *a;
+ *a = *b;
+ *b = c_;
+}
+
+inline static sort_entry *
+part(grn_ctx *ctx, sort_entry *b, sort_entry *e, grn_table_sort_key *keys, int n_keys)
+{
+ sort_entry *c;
+ intptr_t d = e - b;
+ if (compare_value(ctx, b, e, keys, n_keys)) {
+ swap(b, e);
+ }
+ if (d < 2) { return NULL; }
+ c = b + (d >> 1);
+ if (compare_value(ctx, b, c, keys, n_keys)) {
+ swap(b, c);
+ } else {
+ if (compare_value(ctx, c, e, keys, n_keys)) {
+ swap(c, e);
+ }
+ }
+ if (d < 3) { return NULL; }
+ b++;
+ swap(b, c);
+ c = b;
+ for (;;) {
+ do {
+ b++;
+ } while (compare_value(ctx, c, b, keys, n_keys));
+ do {
+ e--;
+ } while (compare_value(ctx, e, c, keys, n_keys));
+ if (b >= e) { break; }
+ swap(b, e);
+ }
+ swap(c, e);
+ return e;
+}
+
+static void
+_sort(grn_ctx *ctx, sort_entry *head, sort_entry *tail, int from, int to,
+ grn_table_sort_key *keys, int n_keys)
+{
+ sort_entry *c;
+ if (head < tail && (c = part(ctx, head, tail, keys, n_keys))) {
+ intptr_t m = c - head + 1;
+ if (from < m - 1) { _sort(ctx, head, c - 1, from, to, keys, n_keys); }
+ if (m < to) { _sort(ctx, c + 1, tail, from - m, to - m, keys, n_keys); }
+ }
+}
+
+static sort_entry *
+pack(grn_ctx *ctx, grn_obj *table, sort_entry *head, sort_entry *tail,
+ grn_table_sort_key *keys, int n_keys)
+{
+ int i = 0;
+ sort_entry e, c;
+ grn_table_cursor *tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, 0);
+ if (!tc) { return NULL; }
+ if ((c.id = grn_table_cursor_next_inline(ctx, tc))) {
+ c.value = grn_obj_get_value_(ctx, keys->key, c.id, &c.size);
+ while ((e.id = grn_table_cursor_next_inline(ctx, tc))) {
+ e.value = grn_obj_get_value_(ctx, keys->key, e.id, &e.size);
+ if (compare_value(ctx, &c, &e, keys, n_keys)) {
+ *head++ = e;
+ } else {
+ *tail-- = e;
+ }
+ i++;
+ }
+ *head = c;
+ i++;
+ }
+ grn_table_cursor_close(ctx, tc);
+ return i > 2 ? head : NULL;
+}
+
+static int
+range_is_idp(grn_obj *obj)
+{
+ if (obj && obj->header.type == GRN_ACCESSOR) {
+ grn_accessor *a;
+ for (a = (grn_accessor *)obj; a; a = a->next) {
+ if (a->action == GRN_ACCESSOR_GET_ID) { return 1; }
+ }
+ }
+ return 0;
+}
+
+int
+grn_table_sort(grn_ctx *ctx, grn_obj *table, int offset, int limit,
+ grn_obj *result, grn_table_sort_key *keys, int n_keys)
+{
+ grn_rc rc;
+ grn_obj *index;
+ int n, e, i = 0;
+ sort_entry *array, *ep;
+ GRN_API_ENTER;
+ if (!n_keys || !keys) {
+ WARN(GRN_INVALID_ARGUMENT, "keys is null");
+ goto exit;
+ }
+ if (!table) {
+ WARN(GRN_INVALID_ARGUMENT, "table is null");
+ goto exit;
+ }
+ if (!(result && result->header.type == GRN_TABLE_NO_KEY)) {
+ WARN(GRN_INVALID_ARGUMENT, "result is not a array");
+ goto exit;
+ }
+ n = grn_table_size(ctx, table);
+ if ((rc = grn_normalize_offset_and_limit(ctx, n, &offset, &limit))) {
+ ERR(rc, "grn_normalize_offset_and_limit failed");
+ goto exit;
+ } else {
+ e = offset + limit;
+ }
+ if (keys->flags & GRN_TABLE_SORT_GEO) {
+ i = grn_geo_table_sort(ctx, table, offset, limit, result, keys, n_keys);
+ goto exit;
+ }
+ if (n_keys == 1 && !GRN_ACCESSORP(keys->key) &&
+ grn_column_index(ctx, keys->key, GRN_OP_LESS, &index, 1, NULL)) {
+ grn_id tid;
+ grn_pat *lexicon = (grn_pat *)grn_ctx_at(ctx, index->header.domain);
+ grn_pat_cursor *pc = grn_pat_cursor_open(ctx, lexicon, NULL, 0, NULL, 0,
+ 0 /* offset : can be used in unique index */,
+ -1 /* limit : can be used in unique index */,
+ (keys->flags & GRN_TABLE_SORT_DESC)
+ ? GRN_CURSOR_DESCENDING
+ : GRN_CURSOR_ASCENDING);
+ if (pc) {
+ while (i < e && (tid = grn_pat_cursor_next(ctx, pc))) {
+ grn_ii_cursor *ic = grn_ii_cursor_open(ctx, (grn_ii *)index, tid, 0, 0, 1, 0);
+ if (ic) {
+ grn_ii_posting *posting;
+ while (i < e && (posting = grn_ii_cursor_next(ctx, ic))) {
+ if (offset <= i) {
+ grn_id *v;
+ if (!grn_array_add(ctx, (grn_array *)result, (void **)&v)) { break; }
+ *v = posting->rid;
+ }
+ i++;
+ }
+ grn_ii_cursor_close(ctx, ic);
+ }
+ }
+ grn_pat_cursor_close(ctx, pc);
+ }
+ } else {
+ int j;
+ grn_table_sort_key *kp;
+ for (kp = keys, j = n_keys; j; kp++, j--) {
+ if (range_is_idp(kp->key)) {
+ kp->offset = KEY_ID;
+ } else {
+ grn_obj *range = grn_ctx_at(ctx, grn_obj_get_range(ctx, kp->key));
+ if (range->header.type == GRN_TYPE) {
+ if (range->header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ kp->offset = KEY_BULK;
+ } else {
+ uint8_t key_type = range->header.flags & GRN_OBJ_KEY_MASK;
+ switch (key_type) {
+ case GRN_OBJ_KEY_UINT :
+ case GRN_OBJ_KEY_GEO_POINT :
+ switch (GRN_TYPE_SIZE(DB_OBJ(range))) {
+ case 1 :
+ kp->offset = KEY_UINT8;
+ break;
+ case 2 :
+ kp->offset = KEY_UINT16;
+ break;
+ case 4 :
+ kp->offset = KEY_UINT32;
+ break;
+ case 8 :
+ kp->offset = KEY_UINT64;
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "unsupported uint value");
+ goto exit;
+ }
+ break;
+ case GRN_OBJ_KEY_INT :
+ switch (GRN_TYPE_SIZE(DB_OBJ(range))) {
+ case 1 :
+ kp->offset = KEY_INT8;
+ break;
+ case 2 :
+ kp->offset = KEY_INT16;
+ break;
+ case 4 :
+ kp->offset = KEY_INT32;
+ break;
+ case 8 :
+ kp->offset = KEY_INT64;
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "unsupported int value");
+ goto exit;
+ }
+ break;
+ case GRN_OBJ_KEY_FLOAT :
+ switch (GRN_TYPE_SIZE(DB_OBJ(range))) {
+ case 4 :
+ kp->offset = KEY_FLOAT32;
+ break;
+ case 8 :
+ kp->offset = KEY_FLOAT64;
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "unsupported float value");
+ goto exit;
+ }
+ break;
+ }
+ }
+ } else {
+ kp->offset = KEY_UINT32;
+ }
+ }
+ }
+ if (!(array = GRN_MALLOC(sizeof(sort_entry) * n))) {
+ goto exit;
+ }
+ if ((ep = pack(ctx, table, array, array + n - 1, keys, n_keys))) {
+ intptr_t m = ep - array + 1;
+ if (offset < m - 1) { _sort(ctx, array, ep - 1, offset, e, keys, n_keys); }
+ if (m < e) { _sort(ctx, ep + 1, array + n - 1, offset - m, e - m, keys, n_keys); }
+ }
+ {
+ grn_id *v;
+ for (i = 0, ep = array + offset; i < limit && ep < array + n; i++, ep++) {
+ if (!grn_array_add(ctx, (grn_array *)result, (void **)&v)) { break; }
+ *v = ep->id;
+ }
+ GRN_FREE(array);
+ }
+ }
+exit :
+ GRN_API_RETURN(i);
+}
+
+static grn_obj *
+deftype(grn_ctx *ctx, const char *name,
+ grn_obj_flags flags, unsigned int size)
+{
+ grn_obj *o = grn_ctx_get(ctx, name, strlen(name));
+ if (!o) { o = grn_type_create(ctx, name, strlen(name), flags, size); }
+ return o;
+}
+
+grn_rc
+grn_db_init_builtin_types(grn_ctx *ctx)
+{
+ grn_id id;
+ grn_obj *obj, *db = ctx->impl->db;
+ char buf[] = "Sys00";
+ grn_obj_register(ctx, db, buf, 5);
+ obj = deftype(ctx, "Object",
+ GRN_OBJ_KEY_UINT, sizeof(uint64_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_OBJECT) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Bool",
+ GRN_OBJ_KEY_UINT, sizeof(uint8_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_BOOL) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Int8",
+ GRN_OBJ_KEY_INT, sizeof(int8_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_INT8) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "UInt8",
+ GRN_OBJ_KEY_UINT, sizeof(uint8_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_UINT8) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Int16",
+ GRN_OBJ_KEY_INT, sizeof(int16_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_INT16) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "UInt16",
+ GRN_OBJ_KEY_UINT, sizeof(uint16_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_UINT16) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Int32",
+ GRN_OBJ_KEY_INT, sizeof(int32_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_INT32) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "UInt32",
+ GRN_OBJ_KEY_UINT, sizeof(uint32_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_UINT32) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Int64",
+ GRN_OBJ_KEY_INT, sizeof(int64_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_INT64) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "UInt64",
+ GRN_OBJ_KEY_UINT, sizeof(uint64_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_UINT64) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Float",
+ GRN_OBJ_KEY_FLOAT, sizeof(double));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_FLOAT) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Time",
+ GRN_OBJ_KEY_INT, sizeof(int64_t));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_TIME) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "ShortText",
+ GRN_OBJ_KEY_VAR_SIZE, GRN_TABLE_MAX_KEY_SIZE);
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_SHORT_TEXT) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "Text",
+ GRN_OBJ_KEY_VAR_SIZE, 1 << 16);
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_TEXT) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "LongText",
+ GRN_OBJ_KEY_VAR_SIZE, 1 << 31);
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_LONG_TEXT) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "TokyoGeoPoint",
+ GRN_OBJ_KEY_GEO_POINT, sizeof(grn_geo_point));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_TOKYO_GEO_POINT) { return GRN_FILE_CORRUPT; }
+ obj = deftype(ctx, "WGS84GeoPoint",
+ GRN_OBJ_KEY_GEO_POINT, sizeof(grn_geo_point));
+ if (!obj || DB_OBJ(obj)->id != GRN_DB_WGS84_GEO_POINT) { return GRN_FILE_CORRUPT; }
+ for (id = grn_db_curr_id(ctx, db) + 1; id < GRN_DB_MECAB; id++) {
+ grn_itoh(id, buf + 3, 2);
+ grn_obj_register(ctx, db, buf, 5);
+ }
+#ifdef GRN_WITH_MECAB
+ if (grn_db_init_mecab_tokenizer(ctx)) {
+ ERRCLR(ctx);
+#endif
+ grn_obj_register(ctx, db, "TokenMecab", 10);
+#ifdef GRN_WITH_MECAB
+ }
+#endif
+ grn_db_init_builtin_tokenizers(ctx);
+ grn_db_init_builtin_normalizers(ctx);
+ for (id = grn_db_curr_id(ctx, db) + 1; id < 128; id++) {
+ grn_itoh(id, buf + 3, 2);
+ grn_obj_register(ctx, db, buf, 5);
+ }
+ grn_db_init_builtin_query(ctx);
+ for (id = grn_db_curr_id(ctx, db) + 1; id < GRN_N_RESERVED_TYPES; id++) {
+ grn_itoh(id, buf + 3, 2);
+ grn_obj_register(ctx, db, buf, 5);
+ }
+ return ctx->rc;
+}
+
+#define MULTI_COLUMN_INDEXP(i) (DB_OBJ(i)->source_size > sizeof(grn_id))
+
+static inline int
+grn_column_index_column_equal(grn_ctx *ctx, grn_obj *obj, grn_operator op,
+ grn_obj **indexbuf, int buf_size, int *section)
+{
+ int n = 0;
+ grn_obj **ip = indexbuf;
+ grn_hook *hooks;
+
+ for (hooks = DB_OBJ(obj)->hooks[GRN_HOOK_SET]; hooks; hooks = hooks->next) {
+ default_set_value_hook_data *data = (void *)NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if (section) { *section = (MULTI_COLUMN_INDEXP(target)) ? data->section : 0; }
+ if (obj->header.type != GRN_COLUMN_FIX_SIZE) {
+ grn_obj *tokenizer, *lexicon = grn_ctx_at(ctx, target->header.domain);
+ if (!lexicon) { continue; }
+ grn_table_get_info(ctx, lexicon, NULL, NULL, &tokenizer, NULL);
+ if (tokenizer) { continue; }
+ }
+ if (n < buf_size) {
+ *ip++ = target;
+ }
+ n++;
+ }
+
+ return n;
+}
+
+static inline int
+grn_column_index_column_match(grn_ctx *ctx, grn_obj *obj, grn_operator op,
+ grn_obj **indexbuf, int buf_size, int *section)
+{
+ int n = 0;
+ grn_obj **ip = indexbuf;
+ grn_hook *hooks;
+
+ for (hooks = DB_OBJ(obj)->hooks[GRN_HOOK_SET]; hooks; hooks = hooks->next) {
+ default_set_value_hook_data *data = (void *)NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if (section) { *section = (MULTI_COLUMN_INDEXP(target)) ? data->section : 0; }
+ if (n < buf_size) {
+ *ip++ = target;
+ }
+ n++;
+ }
+
+ return n;
+}
+
+static inline int
+grn_column_index_column_range(grn_ctx *ctx, grn_obj *obj, grn_operator op,
+ grn_obj **indexbuf, int buf_size, int *section)
+{
+ int n = 0;
+ grn_obj **ip = indexbuf;
+ grn_hook_entry hook_entry;
+ grn_hook *hooks;
+
+ switch (obj->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ hook_entry = GRN_HOOK_INSERT;
+ break;
+ default :
+ hook_entry = GRN_HOOK_SET;
+ break;
+ }
+
+ for (hooks = DB_OBJ(obj)->hooks[hook_entry]; hooks; hooks = hooks->next) {
+ default_set_value_hook_data *data = (void *)NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+ if (section) { *section = (MULTI_COLUMN_INDEXP(target)) ? data->section : 0; }
+ {
+ grn_obj *tokenizer, *lexicon = grn_ctx_at(ctx, target->header.domain);
+ if (!lexicon) { continue; }
+ if (lexicon->header.type != GRN_TABLE_PAT_KEY) { continue; }
+ /* FIXME: GRN_TABLE_DAT_KEY should be supported */
+ grn_table_get_info(ctx, lexicon, NULL, NULL, &tokenizer, NULL);
+ if (tokenizer) { continue; }
+ }
+ if (n < buf_size) {
+ *ip++ = target;
+ }
+ n++;
+ }
+
+ return n;
+}
+
+static inline grn_bool
+is_valid_match_index(grn_ctx *ctx, grn_obj *index_column)
+{
+ return GRN_TRUE;
+}
+
+static inline grn_bool
+is_valid_range_index(grn_ctx *ctx, grn_obj *index_column)
+{
+ grn_obj *tokenizer;
+ grn_obj *lexicon;
+
+ lexicon = grn_ctx_at(ctx, index_column->header.domain);
+ if (!lexicon) { return GRN_FALSE; }
+ /* FIXME: GRN_TABLE_DAT_KEY should be supported */
+ if (lexicon->header.type != GRN_TABLE_PAT_KEY) {
+ grn_obj_unlink(ctx, lexicon);
+ return GRN_FALSE;
+ }
+
+ grn_table_get_info(ctx, lexicon, NULL, NULL, &tokenizer, NULL);
+ grn_obj_unlink(ctx, lexicon);
+ if (tokenizer) { return GRN_FALSE; }
+
+ return GRN_TRUE;
+}
+
+static grn_bool
+is_valid_index(grn_ctx *ctx, grn_obj *index_column, grn_operator op)
+{
+ switch (op) {
+ case GRN_OP_MATCH :
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ case GRN_OP_SIMILAR :
+ return is_valid_match_index(ctx, index_column);
+ break;
+ case GRN_OP_LESS :
+ case GRN_OP_GREATER :
+ case GRN_OP_LESS_EQUAL :
+ case GRN_OP_GREATER_EQUAL :
+ case GRN_OP_CALL :
+ return is_valid_range_index(ctx, index_column);
+ break;
+ default :
+ return GRN_FALSE;
+ break;
+ }
+}
+
+static int
+find_section(grn_ctx *ctx, grn_obj *index_column, grn_obj *indexed_column)
+{
+ int section = 0;
+ grn_id indexed_column_id;
+ grn_id *source_ids;
+ int i, n_source_ids;
+
+ indexed_column_id = DB_OBJ(indexed_column)->id;
+
+ source_ids = DB_OBJ(index_column)->source;
+ n_source_ids = DB_OBJ(index_column)->source_size / sizeof(grn_id);
+ for (i = 0; i < n_source_ids; i++) {
+ grn_id source_id = source_ids[i];
+ if (source_id == indexed_column_id) {
+ section = i + 1;
+ break;
+ }
+ }
+
+ return section;
+}
+
+static int
+grn_column_index_accessor_index_column(grn_ctx *ctx, grn_accessor *a,
+ grn_operator op,
+ grn_obj **indexbuf, int buf_size,
+ int *section)
+{
+ grn_obj *index_column = a->obj;
+
+ if (!is_valid_index(ctx, index_column, op)) {
+ return 0;
+ }
+
+ if (a->next) {
+ int specified_section;
+ grn_bool is_invalid_section;
+ if (a->next->next) {
+ return 0;
+ }
+ specified_section = find_section(ctx, index_column, a->next->obj);
+ is_invalid_section = (specified_section == 0);
+ if (is_invalid_section) {
+ return 0;
+ }
+ if (section) {
+ *section = specified_section;
+ }
+ }
+ if (buf_size > 0) {
+ *indexbuf = index_column;
+ }
+
+ return 1;
+}
+
+static inline int
+grn_column_index_accessor(grn_ctx *ctx, grn_obj *obj, grn_operator op,
+ grn_obj **indexbuf, int buf_size, int *section)
+{
+ int n = 0;
+ grn_obj **ip = indexbuf;
+ grn_accessor *a = (grn_accessor *)obj;
+
+ while (a) {
+ grn_hook *hooks;
+ grn_bool found = GRN_FALSE;
+ grn_hook_entry entry = -1;
+
+ if (a->action == GRN_ACCESSOR_GET_COLUMN_VALUE &&
+ GRN_OBJ_INDEX_COLUMNP(a->obj)) {
+ return grn_column_index_accessor_index_column(ctx, a, op, indexbuf,
+ buf_size, section);
+ }
+
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_KEY :
+ entry = GRN_HOOK_INSERT;
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ entry = GRN_HOOK_SET;
+ break;
+ default :
+ break;
+ }
+
+ if (entry == -1) {
+ break;
+ }
+
+ for (hooks = DB_OBJ(a->obj)->hooks[entry]; hooks; hooks = hooks->next) {
+ default_set_value_hook_data *data = (void *)NEXT_ADDR(hooks);
+ grn_obj *target = grn_ctx_at(ctx, data->target);
+
+ if (target->header.type != GRN_COLUMN_INDEX) { continue; }
+
+ found = GRN_TRUE;
+ if (!a->next) {
+ if (!is_valid_index(ctx, target, op)) {
+ continue;
+ }
+
+ if (section) {
+ *section = (MULTI_COLUMN_INDEXP(target)) ? data->section : 0;
+ }
+ if (n < buf_size) {
+ *ip++ = target;
+ }
+ n++;
+ }
+ }
+
+ if (!found) {
+ break;
+ }
+ a = a->next;
+ }
+
+ return n;
+}
+
+int
+grn_column_index(grn_ctx *ctx, grn_obj *obj, grn_operator op,
+ grn_obj **indexbuf, int buf_size, int *section)
+{
+ int n = 0;
+ GRN_API_ENTER;
+ if (GRN_DB_OBJP(obj)) {
+ switch (op) {
+ case GRN_OP_EQUAL :
+ n = grn_column_index_column_equal(ctx, obj, op,
+ indexbuf, buf_size, section);
+ break;
+ case GRN_OP_PREFIX :
+ case GRN_OP_SUFFIX :
+ case GRN_OP_MATCH :
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ case GRN_OP_SIMILAR :
+ n = grn_column_index_column_match(ctx, obj, op,
+ indexbuf, buf_size, section);
+ break;
+ case GRN_OP_LESS :
+ case GRN_OP_GREATER :
+ case GRN_OP_LESS_EQUAL :
+ case GRN_OP_GREATER_EQUAL :
+ case GRN_OP_CALL :
+ n = grn_column_index_column_range(ctx, obj, op,
+ indexbuf, buf_size, section);
+ break;
+ default :
+ break;
+ }
+ } else if (GRN_ACCESSORP(obj)) {
+ if (section) {
+ *section = 0;
+ }
+ switch (op) {
+ case GRN_OP_EQUAL :
+ case GRN_OP_TERM_EXTRACT :
+ if (buf_size) { indexbuf[n] = obj; }
+ n++;
+ break;
+ case GRN_OP_PREFIX :
+ {
+ grn_accessor *a = (grn_accessor *)obj;
+ if (a->action == GRN_ACCESSOR_GET_KEY) {
+ if (a->obj->header.type == GRN_TABLE_PAT_KEY) {
+ if (buf_size) { indexbuf[n] = obj; }
+ n++;
+ }
+ /* FIXME: GRN_TABLE_DAT_KEY should be supported */
+ }
+ }
+ break;
+ case GRN_OP_SUFFIX :
+ {
+ grn_accessor *a = (grn_accessor *)obj;
+ if (a->action == GRN_ACCESSOR_GET_KEY) {
+ if (a->obj->header.type == GRN_TABLE_PAT_KEY &&
+ a->obj->header.flags & GRN_OBJ_KEY_WITH_SIS) {
+ if (buf_size) { indexbuf[n] = obj; }
+ n++;
+ }
+ }
+ }
+ break;
+ case GRN_OP_MATCH :
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ case GRN_OP_SIMILAR :
+ case GRN_OP_LESS :
+ case GRN_OP_GREATER :
+ case GRN_OP_LESS_EQUAL :
+ case GRN_OP_GREATER_EQUAL :
+ case GRN_OP_CALL :
+ n = grn_column_index_accessor(ctx, obj, op, indexbuf, buf_size, section);
+ break;
+ default :
+ break;
+ }
+ }
+ GRN_API_RETURN(n);
+}
+
+/* todo : refine */
+static int
+tokenize(const char *str, size_t str_len, const char **tokbuf, int buf_size, const char **rest)
+{
+ const char **tok = tokbuf, **tok_end = tokbuf + buf_size;
+ if (buf_size > 0) {
+ const char *str_end = str + str_len;
+ while (str < str_end && (' ' == *str || ',' == *str)) { str++; }
+ for (;;) {
+ if (str == str_end) {
+ *tok++ = str;
+ break;
+ }
+ if (' ' == *str || ',' == *str) {
+ // *str = '\0';
+ *tok++ = str;
+ if (tok == tok_end) { break; }
+ do { str++; } while (str < str_end && (' ' == *str || ',' == *str));
+ } else {
+ str++;
+ }
+ }
+ }
+ if (rest) { *rest = str; }
+ return tok - tokbuf;
+}
+
+grn_rc
+grn_obj_columns(grn_ctx *ctx, grn_obj *table,
+ const char *str, unsigned int str_size, grn_obj *res)
+{
+ grn_obj *col;
+ const char *p = (char *)str, *q, *r, *pe = p + str_size, *tokbuf[256];
+ while (p < pe) {
+ int i, n = tokenize(p, pe - p, tokbuf, 256, &q);
+ for (i = 0; i < n; i++) {
+ r = tokbuf[i];
+ while (p < r && (' ' == *p || ',' == *p)) { p++; }
+ if (p < r) {
+ if (r[-1] == '*') {
+ grn_hash *cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY);
+ if (cols) {
+ grn_id *key;
+ grn_table_columns(ctx, table, p, r - p - 1, (grn_obj *)cols);
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ if ((col = grn_ctx_at(ctx, *key))) { GRN_PTR_PUT(ctx, res, col); }
+ });
+ grn_hash_close(ctx, cols);
+ }
+ {
+ grn_obj *type = grn_ctx_at(ctx, table->header.domain);
+ if (GRN_OBJ_TABLEP(type)) {
+ grn_obj *ai = grn_obj_column(ctx, table,
+ GRN_COLUMN_NAME_ID,
+ GRN_COLUMN_NAME_ID_LEN);
+ if (ai) {
+ if (ai->header.type == GRN_ACCESSOR) {
+ cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY);
+ if (cols) {
+ grn_id *key;
+ grn_accessor *a, *ac;
+ grn_obj *target_table = table;
+ for (a = (grn_accessor *)ai; a; a = a->next) {
+ target_table = a->obj;
+ }
+ grn_table_columns(ctx, target_table,
+ p, r - p - 1, (grn_obj *)cols);
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ if ((col = grn_ctx_at(ctx, *key))) {
+ ac = accessor_new(ctx);
+ GRN_PTR_PUT(ctx, res, (grn_obj *)ac);
+ for (a = (grn_accessor *)ai; a; a = a->next) {
+ if (a->action != GRN_ACCESSOR_GET_ID) {
+ ac->action = a->action;
+ ac->obj = a->obj;
+ ac->next = accessor_new(ctx);
+ if (!(ac = ac->next)) { break; }
+ } else {
+ ac->action = GRN_ACCESSOR_GET_COLUMN_VALUE;
+ ac->obj = col;
+ ac->next = NULL;
+ break;
+ }
+ }
+ }
+ });
+ grn_hash_close(ctx, cols);
+ }
+ }
+ grn_obj_unlink(ctx, ai);
+ }
+ }
+ }
+ } else if ((col = grn_obj_column(ctx, table, p, r - p))) {
+ GRN_PTR_PUT(ctx, res, col);
+ }
+ }
+ p = r;
+ }
+ p = q;
+ }
+ return ctx->rc;
+}
+
+static grn_table_sort_key *
+grn_table_sort_key_from_str_geo(grn_ctx *ctx, const char *str, unsigned int str_size,
+ grn_obj *table, unsigned int *nkeys)
+{
+ const char **tokbuf;
+ const char *p = str, *pe = str + str_size;
+ grn_table_sort_key *keys = NULL, *k = NULL;
+ while ((*p++ != '(')) { if (p == pe) { return NULL; } }
+ str = p;
+ while ((*p != ')')) { if (++p == pe) { return NULL; } }
+ str_size = p - str;
+ p = str;
+ if ((tokbuf = GRN_MALLOCN(const char *, str_size))) {
+ grn_id domain = GRN_ID_NIL;
+ int i, n = tokenize(str, str_size, tokbuf, str_size, NULL);
+ if ((keys = GRN_MALLOCN(grn_table_sort_key, n))) {
+ k = keys;
+ for (i = 0; i < n; i++) {
+ const char *r = tokbuf[i];
+ while (p < r && (' ' == *p || ',' == *p)) { p++; }
+ if (p < r) {
+ k->flags = GRN_TABLE_SORT_ASC;
+ k->offset = 0;
+ if (*p == '+') {
+ p++;
+ } else if (*p == '-') {
+ k->flags = GRN_TABLE_SORT_DESC;
+ p++;
+ }
+ if (k == keys) {
+ if (!(k->key = grn_obj_column(ctx, table, p, r - p))) {
+ WARN(GRN_INVALID_ARGUMENT, "invalid sort key: <%.*s>(<%.*s>)",
+ (int)(tokbuf[i] - p), p, str_size, str);
+ break;
+ }
+ domain = grn_obj_get_range(ctx, k->key);
+ } else {
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET(ctx, &buf, p + 1, r - p - 2); /* should be quoted */
+ k->key = grn_obj_open(ctx, GRN_BULK, 0, domain);
+ grn_obj_cast(ctx, &buf, k->key, GRN_FALSE);
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ k->flags |= GRN_TABLE_SORT_GEO;
+ k++;
+ }
+ p = r;
+ }
+ }
+ GRN_FREE(tokbuf);
+ }
+ if (!ctx->rc && k - keys > 0) {
+ *nkeys = k - keys;
+ } else {
+ grn_table_sort_key_close(ctx, keys, k - keys);
+ *nkeys = 0;
+ keys = NULL;
+ }
+ return keys;
+}
+
+grn_table_sort_key *
+grn_table_sort_key_from_str(grn_ctx *ctx, const char *str, unsigned int str_size,
+ grn_obj *table, unsigned int *nkeys)
+{
+ const char *p = str;
+ const char **tokbuf;
+ grn_table_sort_key *keys = NULL, *k = NULL;
+ if ((keys = grn_table_sort_key_from_str_geo(ctx, str, str_size, table, nkeys))) {
+ return keys;
+ }
+ if ((tokbuf = GRN_MALLOCN(const char *, str_size))) {
+ int i, n = tokenize(str, str_size, tokbuf, str_size, NULL);
+ if ((keys = GRN_MALLOCN(grn_table_sort_key, n))) {
+ k = keys;
+ for (i = 0; i < n; i++) {
+ const char *r = tokbuf[i];
+ while (p < r && (' ' == *p || ',' == *p)) { p++; }
+ if (p < r) {
+ k->flags = GRN_TABLE_SORT_ASC;
+ k->offset = 0;
+ if (*p == '+') {
+ p++;
+ } else if (*p == '-') {
+ k->flags = GRN_TABLE_SORT_DESC;
+ p++;
+ }
+ if ((k->key = grn_obj_column(ctx, table, p, r - p))) {
+ k++;
+ } else {
+ if (r - p == GRN_COLUMN_NAME_SCORE_LEN &&
+ memcmp(p, GRN_COLUMN_NAME_SCORE, GRN_COLUMN_NAME_SCORE_LEN) == 0) {
+ GRN_LOG(ctx, GRN_WARN,
+ "ignore invalid sort key: <%.*s>(<%.*s>)",
+ (int)(r - p), p, str_size, str);
+ } else {
+ WARN(GRN_INVALID_ARGUMENT,
+ "invalid sort key: <%.*s>(<%.*s>)",
+ (int)(r - p), p, str_size, str);
+ break;
+ }
+ }
+ }
+ p = r;
+ }
+ }
+ GRN_FREE(tokbuf);
+ }
+ if (!ctx->rc && k - keys > 0) {
+ *nkeys = k - keys;
+ } else {
+ grn_table_sort_key_close(ctx, keys, k - keys);
+ *nkeys = 0;
+ keys = NULL;
+ }
+ return keys;
+}
+
+grn_rc
+grn_table_sort_key_close(grn_ctx *ctx, grn_table_sort_key *keys, unsigned int nkeys)
+{
+ int i;
+ if (keys) {
+ for (i = 0; i < nkeys; i++) {
+ grn_obj_unlink(ctx, keys[i].key);
+ }
+ GRN_FREE(keys);
+ }
+ return ctx->rc;
+}
+
+grn_bool
+grn_table_is_grouped(grn_ctx *ctx, grn_obj *table)
+{
+ if (GRN_OBJ_TABLEP(table) && GRN_TABLE_IS_GROUPED(table)) {
+ return GRN_TRUE;
+ }
+ return GRN_FALSE;
+}
+
+unsigned int
+grn_table_max_n_subrecs(grn_ctx *ctx, grn_obj *table)
+{
+ if (GRN_OBJ_TABLEP(table)) {
+ return DB_OBJ(table)->max_n_subrecs;
+ }
+ return 0;
+}
+
+grn_obj *
+grn_table_tokenize(grn_ctx *ctx, grn_obj *table,
+ const char *str, unsigned int str_len,
+ grn_obj *buf, grn_bool addp)
+{
+ grn_token *token = NULL;
+ grn_token_mode mode = addp ? GRN_TOKEN_ADD : GRN_TOKEN_GET;
+ GRN_API_ENTER;
+ if (!(token = grn_token_open(ctx, table, str, str_len, mode, 0))) {
+ goto exit;
+ }
+ if (buf) {
+ GRN_BULK_REWIND(buf);
+ } else {
+ if (!(buf = grn_obj_open(ctx, GRN_UVECTOR, 0, DB_OBJ(table)->id))) {
+ goto exit;
+ }
+ }
+ while (token->status != GRN_TOKEN_DONE) {
+ grn_id tid;
+ if ((tid = grn_token_next(ctx, token))) {
+ GRN_RECORD_PUT(ctx, buf, tid);
+ }
+ }
+exit :
+ if (token) {
+ grn_token_close(ctx, token);
+ }
+ GRN_API_RETURN(buf);
+}
+
+/* grn_load */
+
+static grn_obj *
+values_add(grn_ctx *ctx, grn_loader *loader)
+{
+ grn_obj *res;
+ uint32_t curr_size = loader->values_size * sizeof(grn_obj);
+ if (curr_size < GRN_TEXT_LEN(&loader->values)) {
+ res = (grn_obj *)(GRN_TEXT_VALUE(&loader->values) + curr_size);
+ res->header.domain = GRN_DB_TEXT;
+ GRN_BULK_REWIND(res);
+ } else {
+ if (grn_bulk_space(ctx, &loader->values, sizeof(grn_obj))) { return NULL; }
+ res = (grn_obj *)(GRN_TEXT_VALUE(&loader->values) + curr_size);
+ GRN_TEXT_INIT(res, 0);
+ }
+ loader->values_size++;
+ loader->last = res;
+ return res;
+}
+
+static grn_obj *
+values_next(grn_ctx *ctx, grn_obj *value)
+{
+ if (value->header.domain == GRN_JSON_LOAD_OPEN_BRACKET ||
+ value->header.domain == GRN_JSON_LOAD_OPEN_BRACE) {
+ value += GRN_UINT32_VALUE(value);
+ }
+ return value + 1;
+}
+
+static int
+values_len(grn_ctx *ctx, grn_obj *head, grn_obj *tail)
+{
+ int len;
+ for (len = 0; head < tail; head = values_next(ctx, head), len++) ;
+ return len;
+}
+
+static grn_id
+loader_add(grn_ctx *ctx, grn_obj *key)
+{
+ int added = 0;
+ grn_loader *loader = &ctx->impl->loader;
+ grn_id id = grn_table_add_by_key(ctx, loader->table, key, &added);
+ if (!added && loader->ifexists) {
+ grn_obj *v = grn_expr_get_var_by_offset(ctx, loader->ifexists, 0);
+ grn_obj *result;
+ unsigned int result_boolean;
+ GRN_RECORD_SET(ctx, v, id);
+ result = grn_expr_exec(ctx, loader->ifexists, 0);
+ GRN_TRUEP(ctx, result, result_boolean);
+ if (!result_boolean) { id = 0; }
+ }
+ return id;
+}
+
+static void
+set_vector(grn_ctx *ctx, grn_obj *column, grn_id id, grn_obj *vector)
+{
+ int n = GRN_UINT32_VALUE(vector);
+ grn_obj buf, *v = vector + 1;
+ grn_id range_id;
+ grn_obj *range;
+
+ range_id = DB_OBJ(column)->range;
+ range = grn_ctx_at(ctx, range_id);
+ if (GRN_OBJ_TABLEP(range)) {
+ GRN_RECORD_INIT(&buf, GRN_OBJ_VECTOR, range_id);
+ while (n--) {
+ grn_bool cast_failed = GRN_FALSE;
+ grn_obj record, *element = v;
+ if (range_id != element->header.domain) {
+ GRN_RECORD_INIT(&record, 0, range_id);
+ if (grn_obj_cast(ctx, element, &record, GRN_TRUE)) {
+ cast_failed = GRN_TRUE;
+ ERR_CAST(column, range, element);
+ }
+ element = &record;
+ }
+ if (!cast_failed) {
+ GRN_UINT32_PUT(ctx, &buf, GRN_RECORD_VALUE(element));
+ }
+ if (element == &record) { GRN_OBJ_FIN(ctx, element); }
+ v = values_next(ctx, v);
+ }
+ } else {
+ if (((struct _grn_type *)range)->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ GRN_TEXT_INIT(&buf, GRN_OBJ_VECTOR);
+ while (n--) {
+ if (v->header.domain == GRN_DB_TEXT) {
+ grn_bool cast_failed = GRN_FALSE;
+ grn_obj casted_element, *element = v;
+ if (range_id != element->header.domain) {
+ GRN_OBJ_INIT(&casted_element, GRN_BULK, 0, range_id);
+ if (grn_obj_cast(ctx, element, &casted_element, GRN_TRUE)) {
+ cast_failed = GRN_TRUE;
+ ERR_CAST(column, range, element);
+ }
+ element = &casted_element;
+ }
+ if (!cast_failed) {
+ grn_vector_add_element(ctx, &buf,
+ GRN_TEXT_VALUE(element),
+ GRN_TEXT_LEN(element), 0,
+ element->header.domain);
+ }
+ if (element == &casted_element) { GRN_OBJ_FIN(ctx, element); }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "bad syntax.");
+ }
+ v = values_next(ctx, v);
+ }
+ } else {
+ grn_id value_size = ((grn_db_obj *)range)->range;
+ GRN_VALUE_FIX_SIZE_INIT(&buf, GRN_OBJ_VECTOR, range_id);
+ while (n--) {
+ grn_bool cast_failed = GRN_FALSE;
+ grn_obj casted_element, *element = v;
+ if (range_id != element->header.domain) {
+ GRN_OBJ_INIT(&casted_element, GRN_BULK, 0, range_id);
+ if (grn_obj_cast(ctx, element, &casted_element, GRN_TRUE)) {
+ cast_failed = GRN_TRUE;
+ ERR_CAST(column, range, element);
+ }
+ element = &casted_element;
+ }
+ if (!cast_failed) {
+ grn_bulk_write(ctx, &buf, GRN_TEXT_VALUE(element), value_size);
+ }
+ if (element == &casted_element) { GRN_OBJ_FIN(ctx, element); }
+ v = values_next(ctx, v);
+ }
+ }
+ }
+ grn_obj_set_value(ctx, column, id, &buf, GRN_OBJ_SET);
+ GRN_OBJ_FIN(ctx, &buf);
+}
+
+static void
+set_weight_vector(grn_ctx *ctx, grn_obj *column, grn_id id, grn_obj *index_value)
+{
+ if (!GRN_OBJ_WEIGHT_VECTOR_COLUMNP(column)) {
+ char column_name[GRN_TABLE_MAX_KEY_SIZE];
+ int column_name_size;
+ column_name_size = grn_obj_name(ctx, column, column_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "<%.*s>: columns except weight vector column don't support object value",
+ column_name_size, column_name);
+ return;
+ }
+
+ {
+ unsigned int i, n;
+ grn_obj vector;
+ grn_obj weight_buffer;
+
+ n = GRN_UINT32_VALUE(index_value);
+ GRN_TEXT_INIT(&vector, GRN_OBJ_VECTOR);
+ GRN_UINT32_INIT(&weight_buffer, 0);
+ for (i = 0; i < n; i += 2) {
+ grn_rc rc;
+ grn_obj *key, *weight;
+
+ key = index_value + 1 + i;
+ weight = key + 1;
+
+ GRN_BULK_REWIND(&weight_buffer);
+ rc = grn_obj_cast(ctx, weight, &weight_buffer, GRN_TRUE);
+ if (rc != GRN_SUCCESS) {
+ grn_obj *range;
+ range = grn_ctx_at(ctx, weight_buffer.header.domain);
+ ERR_CAST(column, range, weight);
+ grn_obj_unlink(ctx, range);
+ break;
+ }
+ grn_vector_add_element(ctx, &vector,
+ GRN_BULK_HEAD(key), GRN_BULK_VSIZE(key),
+ GRN_UINT32_VALUE(&weight_buffer),
+ key->header.domain);
+ }
+ grn_obj_set_value(ctx, column, id, &vector, GRN_OBJ_SET);
+ GRN_OBJ_FIN(ctx, &vector);
+ }
+}
+
+static inline int
+name_equal(const char *p, unsigned int size, const char *name)
+{
+ if (strlen(name) != size) { return 0; }
+ if (*p != GRN_DB_PSEUDO_COLUMN_PREFIX) { return 0; }
+ return !memcmp(p + 1, name + 1, size - 1);
+}
+
+static void
+report_set_column_value_failure(grn_ctx *ctx,
+ grn_obj *key,
+ const char *column_name,
+ unsigned int column_name_size,
+ grn_obj *column_value)
+{
+ grn_obj key_inspected, column_value_inspected;
+
+ GRN_TEXT_INIT(&key_inspected, 0);
+ GRN_TEXT_INIT(&column_value_inspected, 0);
+ limited_size_inspect(ctx, &key_inspected, key);
+ limited_size_inspect(ctx, &column_value_inspected, column_value);
+ GRN_LOG(ctx, GRN_LOG_ERROR,
+ "[table][load] failed to set column value: %s: "
+ "key: <%.*s>, column: <%.*s>, value: <%.*s>",
+ ctx->errbuf,
+ (int)GRN_TEXT_LEN(&key_inspected),
+ GRN_TEXT_VALUE(&key_inspected),
+ column_name_size,
+ column_name,
+ (int)GRN_TEXT_LEN(&column_value_inspected),
+ GRN_TEXT_VALUE(&column_value_inspected));
+ GRN_OBJ_FIN(ctx, &key_inspected);
+ GRN_OBJ_FIN(ctx, &column_value_inspected);
+}
+
+static void
+bracket_close(grn_ctx *ctx, grn_loader *loader)
+{
+ grn_obj *value, *col, *ve;
+ grn_id id = GRN_ID_NIL;
+ grn_obj *key_value = NULL;
+ grn_obj **cols = (grn_obj **)GRN_BULK_HEAD(&loader->columns);
+ uint32_t begin, ndata, ncols = GRN_BULK_VSIZE(&loader->columns) / sizeof(grn_obj *);
+ GRN_UINT32_POP(&loader->level, begin);
+ value = ((grn_obj *)(GRN_TEXT_VALUE(&loader->values))) + begin;
+ ve = ((grn_obj *)(GRN_TEXT_VALUE(&loader->values))) + loader->values_size;
+ GRN_ASSERT(value->header.domain == GRN_JSON_LOAD_OPEN_BRACKET);
+ GRN_UINT32_SET(ctx, value, loader->values_size - begin - 1);
+ value++;
+ if (GRN_BULK_VSIZE(&loader->level) <= sizeof(uint32_t) * loader->emit_level) {
+ ndata = values_len(ctx, value, ve);
+ if (loader->table) {
+ switch (loader->table->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ if (loader->key_offset != -1 && ndata == ncols + 1) {
+ key_value = value + loader->key_offset;
+ id = loader_add(ctx, key_value);
+ } else if (loader->key_offset == -1) {
+ int i = 0;
+ grn_obj *key_column_name = NULL;
+ while (ndata--) {
+ char *column_name = GRN_TEXT_VALUE(value);
+ unsigned int column_name_size = GRN_TEXT_LEN(value);
+ if (value->header.domain == GRN_DB_TEXT &&
+ (name_equal(column_name, column_name_size,
+ GRN_COLUMN_NAME_KEY) ||
+ name_equal(column_name, column_name_size,
+ GRN_COLUMN_NAME_ID))) {
+ if (loader->key_offset != -1) {
+ GRN_LOG(ctx, GRN_LOG_ERROR,
+ "duplicated key columns: <%.*s> at %d and <%.*s> at %i",
+ (int)GRN_TEXT_LEN(key_column_name),
+ GRN_TEXT_VALUE(key_column_name),
+ loader->key_offset,
+ column_name_size, column_name, i);
+ return;
+ }
+ key_column_name = value;
+ loader->key_offset = i;
+ } else {
+ col = grn_obj_column(ctx, loader->table,
+ column_name, column_name_size);
+ if (!col) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "nonexistent column: <%.*s>",
+ column_name_size, column_name);
+ return;
+ }
+ GRN_PTR_PUT(ctx, &loader->columns, col);
+ }
+ value++;
+ i++;
+ }
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ if ((GRN_BULK_VSIZE(&loader->level)) > 0 &&
+ (ndata == 0 || ndata == ncols)) {
+ id = grn_table_add(ctx, loader->table, NULL, 0, NULL);
+ } else if (!ncols) {
+ while (ndata--) {
+ if (value->header.domain == GRN_DB_TEXT) {
+ char *column_name = GRN_TEXT_VALUE(value);
+ unsigned int column_name_size = GRN_TEXT_LEN(value);
+ col = grn_obj_column(ctx, loader->table,
+ column_name, column_name_size);
+ if (!col) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "nonexistent column: <%.*s>",
+ column_name_size, column_name);
+ return;
+ }
+ GRN_PTR_PUT(ctx, &loader->columns, col);
+ value++;
+ } else {
+ grn_obj buffer;
+ GRN_TEXT_INIT(&buffer, 0);
+ grn_inspect(ctx, &buffer, value);
+ ERR(GRN_INVALID_ARGUMENT,
+ "column name must be string: <%.*s>",
+ (int)GRN_TEXT_LEN(&buffer), GRN_TEXT_VALUE(&buffer));
+ GRN_OBJ_FIN(ctx, &buffer);
+ return;
+ }
+ }
+ }
+ break;
+ default :
+ break;
+ }
+ if (id) {
+ int i = 0;
+ while (ndata--) {
+ grn_obj *column;
+ if ((loader->table->header.type == GRN_TABLE_HASH_KEY ||
+ loader->table->header.type == GRN_TABLE_PAT_KEY ||
+ loader->table->header.type == GRN_TABLE_DAT_KEY) &&
+ i == loader->key_offset) {
+ /* skip this value, because it's already used as key value */
+ value = values_next(ctx, value);
+ i++;
+ continue;
+ }
+
+ column = *cols;
+ if (value->header.domain == GRN_JSON_LOAD_OPEN_BRACKET) {
+ set_vector(ctx, column, id, value);
+ } else if (value->header.domain == GRN_JSON_LOAD_OPEN_BRACE) {
+ set_weight_vector(ctx, column, id, value);
+ } else {
+ grn_obj_set_value(ctx, column, id, value, GRN_OBJ_SET);
+ }
+ if (ctx->rc != GRN_SUCCESS) {
+ char column_name[GRN_TABLE_MAX_KEY_SIZE];
+ unsigned int column_name_size;
+ column_name_size = grn_obj_name(ctx, column, column_name,
+ GRN_TABLE_MAX_KEY_SIZE);
+ report_set_column_value_failure(ctx, key_value,
+ column_name, column_name_size,
+ value);
+ ERRCLR(ctx);
+ }
+ value = values_next(ctx, value);
+ cols++;
+ i++;
+ }
+ if (loader->each) {
+ grn_obj *v = grn_expr_get_var_by_offset(ctx, loader->each, 0);
+ GRN_RECORD_SET(ctx, v, id);
+ grn_expr_exec(ctx, loader->each, 0);
+ }
+ loader->nrecords++;
+ }
+ }
+ loader->values_size = begin;
+ }
+}
+
+static void
+brace_close(grn_ctx *ctx, grn_loader *loader)
+{
+ uint32_t begin;
+ grn_obj *key_value = NULL;
+ grn_obj *value, *ve;
+ grn_id id = GRN_ID_NIL;
+ GRN_UINT32_POP(&loader->level, begin);
+ value = ((grn_obj *)(GRN_TEXT_VALUE(&loader->values))) + begin;
+ ve = ((grn_obj *)(GRN_TEXT_VALUE(&loader->values))) + loader->values_size;
+ GRN_ASSERT(value->header.domain == GRN_JSON_LOAD_OPEN_BRACE);
+ GRN_UINT32_SET(ctx, value, loader->values_size - begin - 1);
+ value++;
+ if (GRN_BULK_VSIZE(&loader->level) <= sizeof(uint32_t) * loader->emit_level) {
+ if (loader->table) {
+ switch (loader->table->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ {
+ grn_obj *v, *key_column_name = NULL;
+ for (v = value; v + 1 < ve; v = values_next(ctx, v)) {
+ char *column_name = GRN_TEXT_VALUE(v);
+ unsigned int column_name_size = GRN_TEXT_LEN(v);
+ if (v->header.domain == GRN_DB_TEXT &&
+ (name_equal(column_name, column_name_size,
+ GRN_COLUMN_NAME_KEY) ||
+ name_equal(column_name, column_name_size,
+ GRN_COLUMN_NAME_ID))) {
+ if (key_column_name) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "duplicated key columns: %.*s and %.*s",
+ (int)GRN_TEXT_LEN(key_column_name),
+ GRN_TEXT_VALUE(key_column_name),
+ column_name_size, column_name);
+ return;
+ }
+ key_column_name = value;
+ v++;
+ key_value = v;
+ id = loader_add(ctx, key_value);
+ } else {
+ v = values_next(ctx, v);
+ }
+ }
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ id = grn_table_add(ctx, loader->table, NULL, 0, NULL);
+ break;
+ default :
+ break;
+ }
+ if (id) {
+ grn_obj *col;
+ const char *name;
+ unsigned int name_size;
+ while (value + 1 < ve) {
+ if (value->header.domain != GRN_DB_TEXT) { break; /* error */ }
+ name = GRN_TEXT_VALUE(value);
+ name_size = GRN_TEXT_LEN(value);
+ col = grn_obj_column(ctx, loader->table, name, name_size);
+ value++;
+ /* auto column create
+ if (!col) {
+ if (value->header.domain == GRN_JSON_LOAD_OPEN_BRACKET) {
+ grn_obj *v = value + 1;
+ col = grn_column_create(ctx, loader->table, name, name_size,
+ NULL, GRN_OBJ_PERSISTENT|GRN_OBJ_COLUMN_VECTOR,
+ grn_ctx_at(ctx, v->header.domain));
+ } else {
+ col = grn_column_create(ctx, loader->table, name, name_size,
+ NULL, GRN_OBJ_PERSISTENT,
+ grn_ctx_at(ctx, value->header.domain));
+ }
+ }
+ */
+ if (col) {
+ if (value->header.domain == GRN_JSON_LOAD_OPEN_BRACKET) {
+ set_vector(ctx, col, id, value);
+ } else if (value->header.domain == GRN_JSON_LOAD_OPEN_BRACE) {
+ set_weight_vector(ctx, col, id, value);
+ } else {
+ grn_obj_set_value(ctx, col, id, value, GRN_OBJ_SET);
+ }
+ if (ctx->rc != GRN_SUCCESS) {
+ report_set_column_value_failure(ctx, key_value,
+ name, name_size, value);
+ ERRCLR(ctx);
+ }
+ grn_obj_unlink(ctx, col);
+ } else {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "invalid column('%.*s')", (int)name_size, name);
+ }
+ value = values_next(ctx, value);
+ }
+ if (loader->each) {
+ grn_obj *v = grn_expr_get_var_by_offset(ctx, loader->each, 0);
+ GRN_RECORD_SET(ctx, v, id);
+ grn_expr_exec(ctx, loader->each, 0);
+ }
+ loader->nrecords++;
+ } else {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "neither _key nor _id is assigned");
+ }
+ }
+ loader->values_size = begin;
+ }
+}
+
+#define JSON_READ_OPEN_BRACKET() do {\
+ GRN_UINT32_PUT(ctx, &loader->level, loader->values_size);\
+ values_add(ctx, loader);\
+ loader->last->header.domain = GRN_JSON_LOAD_OPEN_BRACKET;\
+ loader->stat = GRN_LOADER_TOKEN;\
+ str++;\
+} while (0)
+
+#define JSON_READ_OPEN_BRACE() do {\
+ GRN_UINT32_PUT(ctx, &loader->level, loader->values_size);\
+ values_add(ctx, loader);\
+ loader->last->header.domain = GRN_JSON_LOAD_OPEN_BRACE;\
+ loader->stat = GRN_LOADER_TOKEN;\
+ str++;\
+} while (0)
+
+static void
+json_read(grn_ctx *ctx, grn_loader *loader, const char *str, unsigned int str_len)
+{
+ const char *const beg = str;
+ char c;
+ int len;
+ const char *se = str + str_len;
+ while (str < se) {
+ c = *str;
+ switch (loader->stat) {
+ case GRN_LOADER_BEGIN :
+ if ((len = grn_isspace(str, ctx->encoding))) {
+ str += len;
+ c = *str;
+ continue;
+ }
+ switch (c) {
+ case '[' :
+ JSON_READ_OPEN_BRACKET();
+ break;
+ case '{' :
+ JSON_READ_OPEN_BRACE();
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT,
+ "JSON must start with '[' or '{': <%.*s>", str_len, beg);
+ loader->stat = GRN_LOADER_END;
+ break;
+ }
+ break;
+ case GRN_LOADER_TOKEN :
+ if ((len = grn_isspace(str, ctx->encoding))) {
+ str += len;
+ c = *str;
+ continue;
+ }
+ switch (c) {
+ case '"' :
+ loader->stat = GRN_LOADER_STRING;
+ values_add(ctx, loader);
+ str++;
+ break;
+ case '[' :
+ JSON_READ_OPEN_BRACKET();
+ break;
+ case '{' :
+ JSON_READ_OPEN_BRACE();
+ break;
+ case ':' :
+ str++;
+ break;
+ case ',' :
+ str++;
+ break;
+ case ']' :
+ bracket_close(ctx, loader);
+ loader->stat = GRN_BULK_VSIZE(&loader->level) ? GRN_LOADER_TOKEN : GRN_LOADER_END;
+ str++;
+ break;
+ case '}' :
+ brace_close(ctx, loader);
+ loader->stat = GRN_BULK_VSIZE(&loader->level) ? GRN_LOADER_TOKEN : GRN_LOADER_END;
+ str++;
+ break;
+ case '+' : case '-' : case '0' : case '1' : case '2' : case '3' :
+ case '4' : case '5' : case '6' : case '7' : case '8' : case '9' :
+ loader->stat = GRN_LOADER_NUMBER;
+ values_add(ctx, loader);
+ break;
+ default :
+ if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('_' == c)) {
+ loader->stat = GRN_LOADER_SYMBOL;
+ values_add(ctx, loader);
+ } else {
+ if ((len = grn_charlen(ctx, str, se))) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "ignored invalid char('%c') at", c);
+ GRN_LOG(ctx, GRN_LOG_ERROR, "%.*s", (int)(str - beg) + len, beg);
+ GRN_LOG(ctx, GRN_LOG_ERROR, "%*s", (int)(str - beg) + 1, "^");
+ str += len;
+ } else {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "ignored invalid char(\\x%.2x) after", c);
+ GRN_LOG(ctx, GRN_LOG_ERROR, "%.*s", (int)(str - beg), beg);
+ str = se;
+ }
+ }
+ break;
+ }
+ break;
+ case GRN_LOADER_SYMBOL :
+ if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') ||
+ ('0' <= c && c <= '9') || ('_' == c)) {
+ GRN_TEXT_PUTC(ctx, loader->last, c);
+ str++;
+ } else {
+ char *v = GRN_TEXT_VALUE(loader->last);
+ switch (*v) {
+ case 'n' :
+ if (GRN_TEXT_LEN(loader->last) == 4 && !memcmp(v, "null", 4)) {
+ loader->last->header.domain = GRN_DB_VOID;
+ GRN_BULK_REWIND(loader->last);
+ }
+ break;
+ case 't' :
+ if (GRN_TEXT_LEN(loader->last) == 4 && !memcmp(v, "true", 4)) {
+ loader->last->header.domain = GRN_DB_BOOL;
+ GRN_BOOL_SET(ctx, loader->last, GRN_TRUE);
+ }
+ break;
+ case 'f' :
+ if (GRN_TEXT_LEN(loader->last) == 5 && !memcmp(v, "false", 5)) {
+ loader->last->header.domain = GRN_DB_BOOL;
+ GRN_BOOL_SET(ctx, loader->last, GRN_FALSE);
+ }
+ break;
+ default :
+ break;
+ }
+ loader->stat = GRN_BULK_VSIZE(&loader->level) ? GRN_LOADER_TOKEN : GRN_LOADER_END;
+ }
+ break;
+ case GRN_LOADER_NUMBER :
+ switch (c) {
+ case '+' : case '-' : case '.' : case 'e' : case 'E' :
+ case '0' : case '1' : case '2' : case '3' : case '4' :
+ case '5' : case '6' : case '7' : case '8' : case '9' :
+ GRN_TEXT_PUTC(ctx, loader->last, c);
+ str++;
+ break;
+ default :
+ {
+ const char *cur, *str = GRN_BULK_HEAD(loader->last);
+ const char *str_end = GRN_BULK_CURR(loader->last);
+ int64_t i = grn_atoll(str, str_end, &cur);
+ if (cur == str_end) {
+ loader->last->header.domain = GRN_DB_INT64;
+ GRN_INT64_SET(ctx, loader->last, i);
+ } else if (cur != str) {
+ double d;
+ char *end;
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_PUT(ctx, &buf, str, GRN_BULK_VSIZE(loader->last));
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ errno = 0;
+ d = strtod(GRN_TEXT_VALUE(&buf), &end);
+ if (!errno && end + 1 == GRN_BULK_CURR(&buf)) {
+ loader->last->header.domain = GRN_DB_FLOAT;
+ GRN_FLOAT_SET(ctx, loader->last, d);
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ }
+ loader->stat = GRN_BULK_VSIZE(&loader->level) ? GRN_LOADER_TOKEN : GRN_LOADER_END;
+ break;
+ }
+ break;
+ case GRN_LOADER_STRING :
+ switch (c) {
+ case '\\' :
+ loader->stat = GRN_LOADER_STRING_ESC;
+ str++;
+ break;
+ case '"' :
+ str++;
+ loader->stat = GRN_BULK_VSIZE(&loader->level) ? GRN_LOADER_TOKEN : GRN_LOADER_END;
+ /*
+ *(GRN_BULK_CURR(loader->last)) = '\0';
+ GRN_LOG(ctx, GRN_LOG_ALERT, "read str(%s)", GRN_TEXT_VALUE(loader->last));
+ */
+ break;
+ default :
+ if ((len = grn_charlen(ctx, str, se))) {
+ GRN_TEXT_PUT(ctx, loader->last, str, len);
+ str += len;
+ } else {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "ignored invalid char(\\x%.2x) after", c);
+ GRN_LOG(ctx, GRN_LOG_ERROR, "%.*s", (int)(str - beg), beg);
+ str = se;
+ }
+ break;
+ }
+ break;
+ case GRN_LOADER_STRING_ESC :
+ switch (c) {
+ case 'b' :
+ GRN_TEXT_PUTC(ctx, loader->last, '\b');
+ loader->stat = GRN_LOADER_STRING;
+ break;
+ case 'f' :
+ GRN_TEXT_PUTC(ctx, loader->last, '\f');
+ loader->stat = GRN_LOADER_STRING;
+ break;
+ case 'n' :
+ GRN_TEXT_PUTC(ctx, loader->last, '\n');
+ loader->stat = GRN_LOADER_STRING;
+ break;
+ case 'r' :
+ GRN_TEXT_PUTC(ctx, loader->last, '\r');
+ loader->stat = GRN_LOADER_STRING;
+ break;
+ case 't' :
+ GRN_TEXT_PUTC(ctx, loader->last, '\t');
+ loader->stat = GRN_LOADER_STRING;
+ break;
+ case 'u' :
+ loader->stat = GRN_LOADER_UNICODE0;
+ break;
+ default :
+ GRN_TEXT_PUTC(ctx, loader->last, c);
+ loader->stat = GRN_LOADER_STRING;
+ break;
+ }
+ str++;
+ break;
+ case GRN_LOADER_UNICODE0 :
+ switch (c) {
+ case '0' : case '1' : case '2' : case '3' : case '4' :
+ case '5' : case '6' : case '7' : case '8' : case '9' :
+ loader->unichar = (c - '0') * 0x1000;
+ break;
+ case 'a' : case 'b' : case 'c' : case 'd' : case 'e' : case 'f' :
+ loader->unichar = (c - 'a' + 10) * 0x1000;
+ break;
+ case 'A' : case 'B' : case 'C' : case 'D' : case 'E' : case 'F' :
+ loader->unichar = (c - 'A' + 10) * 0x1000;
+ break;
+ default :
+ ;// todo : error
+ }
+ loader->stat = GRN_LOADER_UNICODE1;
+ str++;
+ break;
+ case GRN_LOADER_UNICODE1 :
+ switch (c) {
+ case '0' : case '1' : case '2' : case '3' : case '4' :
+ case '5' : case '6' : case '7' : case '8' : case '9' :
+ loader->unichar += (c - '0') * 0x100;
+ break;
+ case 'a' : case 'b' : case 'c' : case 'd' : case 'e' : case 'f' :
+ loader->unichar += (c - 'a' + 10) * 0x100;
+ break;
+ case 'A' : case 'B' : case 'C' : case 'D' : case 'E' : case 'F' :
+ loader->unichar += (c - 'A' + 10) * 0x100;
+ break;
+ default :
+ ;// todo : error
+ }
+ loader->stat = GRN_LOADER_UNICODE2;
+ str++;
+ break;
+ case GRN_LOADER_UNICODE2 :
+ switch (c) {
+ case '0' : case '1' : case '2' : case '3' : case '4' :
+ case '5' : case '6' : case '7' : case '8' : case '9' :
+ loader->unichar += (c - '0') * 0x10;
+ break;
+ case 'a' : case 'b' : case 'c' : case 'd' : case 'e' : case 'f' :
+ loader->unichar += (c - 'a' + 10) * 0x10;
+ break;
+ case 'A' : case 'B' : case 'C' : case 'D' : case 'E' : case 'F' :
+ loader->unichar += (c - 'A' + 10) * 0x10;
+ break;
+ default :
+ ;// todo : error
+ }
+ loader->stat = GRN_LOADER_UNICODE3;
+ str++;
+ break;
+ case GRN_LOADER_UNICODE3 :
+ switch (c) {
+ case '0' : case '1' : case '2' : case '3' : case '4' :
+ case '5' : case '6' : case '7' : case '8' : case '9' :
+ loader->unichar += (c - '0');
+ break;
+ case 'a' : case 'b' : case 'c' : case 'd' : case 'e' : case 'f' :
+ loader->unichar += (c - 'a' + 10);
+ break;
+ case 'A' : case 'B' : case 'C' : case 'D' : case 'E' : case 'F' :
+ loader->unichar += (c - 'A' + 10);
+ break;
+ default :
+ ;// todo : error
+ }
+ {
+ uint32_t u = loader->unichar;
+ if (u < 0x80) {
+ GRN_TEXT_PUTC(ctx, loader->last, u);
+ } else {
+ if (u < 0x800) {
+ GRN_TEXT_PUTC(ctx, loader->last, ((u >> 6) & 0x1f) | 0xc0);
+ } else {
+ GRN_TEXT_PUTC(ctx, loader->last, (u >> 12) | 0xe0);
+ GRN_TEXT_PUTC(ctx, loader->last, ((u >> 6) & 0x3f) | 0x80);
+ }
+ GRN_TEXT_PUTC(ctx, loader->last, (u & 0x3f) | 0x80);
+ }
+ }
+ loader->stat = GRN_LOADER_STRING;
+ str++;
+ break;
+ case GRN_LOADER_END :
+ str = se;
+ break;
+ }
+ }
+}
+
+#undef JSON_READ_OPEN_BRACKET
+#undef JSON_READ_OPEN_BRACE
+
+static grn_rc
+parse_load_columns(grn_ctx *ctx, grn_obj *table,
+ const char *str, unsigned int str_size, grn_obj *res)
+{
+ const char *p = (char *)str, *q, *r, *pe = p + str_size, *tokbuf[256];
+ while (p < pe) {
+ int i, n = tokenize(p, pe - p, tokbuf, 256, &q);
+ for (i = 0; i < n; i++) {
+ grn_obj *col;
+ r = tokbuf[i];
+ while (p < r && (' ' == *p || ',' == *p)) { p++; }
+ col = grn_obj_column(ctx, table, p, r - p);
+ if (!col) {
+ ERR(GRN_INVALID_ARGUMENT, "nonexistent column: <%.*s>", (int)(r - p), p);
+ goto exit;
+ }
+ GRN_PTR_PUT(ctx, res, col);
+ p = r;
+ }
+ p = q;
+ }
+exit:
+ return ctx->rc;
+}
+
+static grn_com_addr *addr;
+
+void
+grn_load_(grn_ctx *ctx, grn_content_type input_type,
+ const char *table, unsigned int table_len,
+ const char *columns, unsigned int columns_len,
+ const char *values, unsigned int values_len,
+ const char *ifexists, unsigned int ifexists_len,
+ const char *each, unsigned int each_len,
+ uint32_t emit_level)
+{
+ grn_loader *loader;
+ loader = &ctx->impl->loader;
+ loader->emit_level = emit_level;
+ if (ctx->impl->edge) {
+ grn_edge *edge = grn_edges_add_communicator(ctx, addr);
+ grn_obj *msg = grn_msg_open(ctx, edge->com, &ctx->impl->edge->send_old);
+ /* build msg */
+ grn_edge_dispatch(ctx, edge, msg);
+ }
+ if (table && table_len) {
+ grn_ctx_loader_clear(ctx);
+ loader->input_type = input_type;
+ if (grn_db_check_name(ctx, table, table_len)) {
+ GRN_DB_CHECK_NAME_ERR("[table][load]", table, table_len);
+ loader->stat = GRN_LOADER_END;
+ return;
+ }
+ loader->table = grn_ctx_get(ctx, table, table_len);
+ if (!loader->table) {
+ ERR(GRN_INVALID_ARGUMENT, "nonexistent table: <%.*s>", table_len, table);
+ loader->stat = GRN_LOADER_END;
+ return;
+ }
+ if (loader->table && columns && columns_len) {
+ int i, n_columns;
+ grn_obj parsed_columns;
+
+ GRN_PTR_INIT(&parsed_columns, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ if (parse_load_columns(ctx, loader->table, columns, columns_len,
+ &parsed_columns)) {
+ loader->stat = GRN_LOADER_END;
+ return;
+ }
+ n_columns = GRN_BULK_VSIZE(&parsed_columns) / sizeof(grn_obj *);
+ for (i = 0; i < n_columns; i++) {
+ grn_obj *column;
+ column = GRN_PTR_VALUE_AT(&parsed_columns, i);
+ if (column->header.type == GRN_ACCESSOR &&
+ ((grn_accessor *)column)->action == GRN_ACCESSOR_GET_KEY) {
+ loader->key_offset = i;
+ grn_obj_unlink(ctx, column);
+ } else {
+ GRN_PTR_PUT(ctx, &loader->columns, column);
+ }
+ }
+ GRN_OBJ_FIN(ctx, &parsed_columns);
+ }
+ if (ifexists && ifexists_len) {
+ grn_obj *v;
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, loader->table, loader->ifexists, v);
+ if (loader->ifexists && v) {
+ grn_expr_parse(ctx, loader->ifexists, ifexists, ifexists_len,
+ NULL, GRN_OP_EQUAL, GRN_OP_AND,
+ GRN_EXPR_SYNTAX_SCRIPT|GRN_EXPR_ALLOW_UPDATE);
+ }
+ }
+ if (each && each_len) {
+ grn_obj *v;
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, loader->table, loader->each, v);
+ if (loader->each && v) {
+ grn_expr_parse(ctx, loader->each, each, each_len,
+ NULL, GRN_OP_EQUAL, GRN_OP_AND,
+ GRN_EXPR_SYNTAX_SCRIPT|GRN_EXPR_ALLOW_UPDATE);
+ }
+ }
+ } else {
+ if (!loader->table) {
+ ERR(GRN_INVALID_ARGUMENT, "mandatory \"table\" parameter is absent");
+ loader->stat = GRN_LOADER_END;
+ return;
+ }
+ input_type = loader->input_type;
+ }
+ switch (input_type) {
+ case GRN_CONTENT_JSON :
+ json_read(ctx, loader, values, values_len);
+ break;
+ case GRN_CONTENT_NONE :
+ case GRN_CONTENT_TSV :
+ case GRN_CONTENT_XML :
+ case GRN_CONTENT_MSGPACK :
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "unsupported input_type");
+ // todo
+ break;
+ }
+}
+
+grn_rc
+grn_load(grn_ctx *ctx, grn_content_type input_type,
+ const char *table, unsigned int table_len,
+ const char *columns, unsigned int columns_len,
+ const char *values, unsigned int values_len,
+ const char *ifexists, unsigned int ifexists_len,
+ const char *each, unsigned int each_len)
+{
+ if (!ctx || !ctx->impl) {
+ ERR(GRN_INVALID_ARGUMENT, "db not initialized");
+ return ctx->rc;
+ }
+ GRN_API_ENTER;
+ grn_load_(ctx, input_type, table, table_len,
+ columns, columns_len, values, values_len,
+ ifexists, ifexists_len, each, each_len, 1);
+ GRN_API_RETURN(ctx->rc);
+}
diff --git a/storage/mroonga/vendor/groonga/lib/db.h b/storage/mroonga/vendor/groonga/lib/db.h
new file mode 100644
index 00000000000..d48a625ef44
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/db.h
@@ -0,0 +1,469 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_DB_H
+#define GRN_DB_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifndef GRN_CTX_H
+#include "ctx.h"
+#endif /* GRN_CTX_H */
+
+#ifndef GRN_STORE_H
+#include "store.h"
+#endif /* GRN_STORE_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GRN_DB_DELIMITER '.'
+#define GRN_DB_PSEUDO_COLUMN_PREFIX '_'
+
+#define GRN_N_RESERVED_TYPES 256
+
+typedef struct {
+ int score;
+ int n_subrecs;
+ int subrecs[1];
+} grn_rset_recinfo;
+
+typedef struct {
+ grn_id rid;
+ uint32_t sid;
+ uint32_t pos;
+} grn_rset_posinfo;
+
+#define GRN_RSET_UTIL_BIT (0x80000000)
+
+#define GRN_RSET_SCORE_SIZE (sizeof(int))
+
+#define GRN_RSET_N_SUBRECS(ri) ((ri)->n_subrecs & ~GRN_RSET_UTIL_BIT)
+
+#define GRN_RSET_SUBRECS_CMP(a,b,dir) (((a) - (b))*(dir))
+#define GRN_RSET_SUBRECS_NTH(subrecs,size,n) \
+ ((int *)((byte *)subrecs + n * (GRN_RSET_SCORE_SIZE + size)))
+#define GRN_RSET_SUBRECS_COPY(subrecs,size,n,src) \
+ (memcpy(GRN_RSET_SUBRECS_NTH(subrecs, size, n), src, GRN_RSET_SCORE_SIZE + size))
+
+#define GRN_JSON_LOAD_OPEN_BRACKET 0x40000000
+#define GRN_JSON_LOAD_OPEN_BRACE 0x40000001
+
+typedef struct _grn_db grn_db;
+typedef struct _grn_proc grn_proc;
+
+struct _grn_db {
+ grn_db_obj obj;
+ grn_obj *keys;
+ grn_ja *specs;
+ grn_tiny_array values;
+ grn_critical_section lock;
+};
+
+typedef struct {
+ grn_obj_header header;
+ grn_id range;
+} grn_obj_spec;
+
+GRN_API grn_rc grn_db_close(grn_ctx *ctx, grn_obj *db);
+
+grn_obj *grn_db_keys(grn_obj *s);
+
+uint32_t grn_db_lastmod(grn_obj *s);
+
+grn_rc _grn_table_delete_by_id(grn_ctx *ctx, grn_obj *table, grn_id id,
+ grn_table_delete_optarg *optarg);
+
+grn_id grn_table_get_v(grn_ctx *ctx, grn_obj *table, const void *key, int key_size,
+ void **value);
+grn_id grn_table_add_v(grn_ctx *ctx, grn_obj *table, const void *key, int key_size,
+ void **value, int *added);
+GRN_API grn_rc grn_table_get_info(grn_ctx *ctx, grn_obj *table, grn_obj_flags *flags,
+ grn_encoding *encoding, grn_obj **tokenizer,
+ grn_obj **normalizer);
+const char *_grn_table_key(grn_ctx *ctx, grn_obj *table, grn_id id, uint32_t *key_size);
+
+grn_rc grn_table_search(grn_ctx *ctx, grn_obj *table,
+ const void *key, uint32_t key_size,
+ grn_operator mode, grn_obj *res, grn_operator op);
+
+grn_id grn_table_next(grn_ctx *ctx, grn_obj *table, grn_id id);
+
+int grn_table_get_key2(grn_ctx *ctx, grn_obj *table, grn_id id, grn_obj *bulk);
+
+grn_table_cursor *grn_table_cursor_open_by_id(grn_ctx *ctx, grn_obj *table,
+ grn_id min, grn_id max, int flags);
+
+void grn_table_add_subrec(grn_obj *table, grn_rset_recinfo *ri, int score,
+ grn_rset_posinfo *pi, int dir);
+
+grn_obj *grn_obj_graft(grn_ctx *ctx, grn_obj *obj);
+
+grn_rc grn_column_name_(grn_ctx *ctx, grn_obj *obj, grn_obj *buf);
+
+
+typedef enum {
+ PROC_INIT = 0,
+ PROC_NEXT,
+ PROC_FIN
+} grn_proc_phase;
+
+struct _grn_type {
+ grn_db_obj obj;
+};
+
+#define GRN_TYPE_SIZE(type) ((type)->range)
+
+#define GRN_TABLE_SORT_GEO (0x02<<0)
+
+#define GRN_OBJ_TMP_OBJECT 0x80000000
+
+#define GRN_DB_OBJP(obj) \
+ (obj &&\
+ ((GRN_SNIP == ((grn_db_obj *)obj)->header.type) ||\
+ ((GRN_CURSOR_TABLE_HASH_KEY <= ((grn_db_obj *)obj)->header.type) &&\
+ (((grn_db_obj *)obj)->header.type <= GRN_COLUMN_INDEX))))
+
+#define GRN_OBJ_TABLEP(obj) \
+ (obj &&\
+ (GRN_TABLE_HASH_KEY <= ((grn_db_obj *)obj)->header.type) &&\
+ (((grn_db_obj *)obj)->header.type <= GRN_DB))
+
+#define GRN_OBJ_INDEX_COLUMNP(obj) \
+ (obj &&\
+ DB_OBJ(obj)->header.type == GRN_COLUMN_INDEX)
+
+#define GRN_OBJ_VECTOR_COLUMNP(obj) \
+ (obj &&\
+ DB_OBJ(obj)->header.type == GRN_COLUMN_VAR_SIZE &&\
+ (DB_OBJ(obj)->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR)
+
+#define GRN_OBJ_WEIGHT_VECTOR_COLUMNP(obj) \
+ (GRN_OBJ_VECTOR_COLUMNP(obj) &&\
+ (DB_OBJ(obj)->header.flags & GRN_OBJ_WITH_WEIGHT))
+
+typedef struct _grn_proc_ctx grn_proc_ctx;
+
+struct _grn_proc_ctx {
+ grn_user_data user_data;
+ grn_proc *proc;
+ grn_obj *caller;
+ // grn_obj *obj;
+ grn_hook *hooks;
+ grn_hook *currh;
+ grn_proc_phase phase;
+ unsigned short nargs;
+ unsigned short offset;
+ grn_user_data data[16];
+};
+
+struct _grn_proc {
+ grn_db_obj obj;
+ grn_obj name_buf;
+ grn_expr_var *vars;
+ uint32_t nvars;
+ /* -- compatible with grn_expr -- */
+ grn_proc_type type;
+ grn_proc_func *funcs[3];
+
+ grn_selector_func *selector;
+
+ grn_id module;
+ // uint32_t nargs;
+ // uint32_t nresults;
+ // grn_obj results[16];
+};
+
+#define GRN_PROC_GET_VAR(name) (grn_proc_get_var(ctx, user_data, name, strlen(name)))
+#define GRN_PROC_GET_VAR_BY_OFFSET(offset) (grn_proc_get_var_by_offset(ctx, user_data, offset))
+#define GRN_PROC_GET_OR_ADD_VAR(name) (grn_proc_get_or_add_var(ctx, user_data, name, strlen(name)))
+#define GRN_PROC_ALLOC(domain, flags) (grn_proc_alloc(ctx, user_data, domain, flags))
+
+grn_obj *grn_proc_get_var(grn_ctx *ctx, grn_user_data *user_data,
+ const char *name, unsigned int name_size);
+
+GRN_API grn_obj *grn_proc_get_var_by_offset(grn_ctx *ctx, grn_user_data *user_data,
+ unsigned int offset);
+GRN_API grn_obj *grn_proc_get_or_add_var(grn_ctx *ctx, grn_user_data *user_data,
+ const char *name, unsigned int name_size);
+
+GRN_API grn_obj *grn_proc_alloc(grn_ctx *ctx, grn_user_data *user_data,
+ grn_id domain, grn_obj_flags flags);
+
+GRN_API grn_rc grn_proc_call(grn_ctx *ctx, grn_obj *proc,
+ int nargs, grn_obj *caller);
+
+grn_obj *grn_expr_get_or_add_var(grn_ctx *ctx, grn_obj *expr,
+ const char *name, unsigned int name_size);
+
+
+typedef struct _grn_accessor grn_accessor;
+
+struct _grn_accessor {
+ grn_obj_header header;
+ grn_id range;
+ /* -- compatible with grn_db_obj -- */
+ uint8_t action;
+ int offset;
+ grn_obj *obj;
+ grn_accessor *next;
+};
+
+enum {
+ GRN_ACCESSOR_VOID = 0,
+ GRN_ACCESSOR_GET_ID,
+ GRN_ACCESSOR_GET_KEY,
+ GRN_ACCESSOR_GET_VALUE,
+ GRN_ACCESSOR_GET_SCORE,
+ GRN_ACCESSOR_GET_NSUBRECS,
+ GRN_ACCESSOR_GET_COLUMN_VALUE,
+ GRN_ACCESSOR_GET_DB_OBJ,
+ GRN_ACCESSOR_LOOKUP,
+ GRN_ACCESSOR_FUNCALL
+};
+
+#define DB_OBJ(obj) ((grn_db_obj *)obj)
+
+GRN_API const char *grn_obj_get_value_(grn_ctx *ctx, grn_obj *obj, grn_id id, uint32_t *size);
+
+/* vector */
+
+/*
+typedef struct _grn_vector grn_vector;
+
+struct _grn_vector {
+ grn_obj str;
+ uint32_t *offsets;
+ int n_entries;
+};
+
+const char *grn_vector_fetch(grn_ctx *ctx, grn_obj *vector, int i, unsigned int *size);
+int grn_vector_delimit(grn_ctx *ctx, grn_obj *vector);
+int grn_vector_size(grn_ctx *ctx, grn_obj *vector);
+*/
+
+unsigned int grn_vector_pop_element(grn_ctx *ctx, grn_obj *vector,
+ const char **str, unsigned int *weight, grn_id *domain);
+
+grn_rc grn_vector_delimit(grn_ctx *ctx, grn_obj *v, unsigned int weight, grn_id domain);
+grn_rc grn_vector_decode(grn_ctx *ctx, grn_obj *v, const char *data, uint32_t data_size);
+
+
+grn_rc grn_db_init_builtin_types(grn_ctx *ctx);
+
+/* flag value used for grn_obj.header.flags */
+
+#define GRN_OBJ_CUSTOM_NAME (0x01<<12) /* db_obj which has custom name */
+
+#define GRN_OBJ_RESOLVE(ctx,obj) \
+ (((obj)->header.type != GRN_PTR)\
+ ? (obj)\
+ : GRN_PTR_VALUE(obj)\
+ ? GRN_PTR_VALUE(obj)\
+ : grn_ctx_at((ctx), (obj)->header.domain))
+
+/* expr */
+
+typedef struct _grn_expr grn_expr;
+
+#define GRN_EXPR_CODE_RELATIONAL_EXPRESSION (0x01)
+
+typedef struct {
+ grn_obj *value;
+ int32_t nargs;
+ grn_operator op;
+ uint8_t flags;
+ int32_t modify;
+} grn_expr_code;
+
+struct _grn_expr {
+ grn_db_obj obj;
+ grn_obj name_buf;
+ grn_expr_var *vars;
+ uint32_t nvars;
+ /* -- compatible with grn_proc -- */
+
+ uint16_t cacheable;
+ uint16_t taintable;
+ grn_obj *consts;
+ grn_obj *values;
+ grn_expr_code *codes;
+ uint32_t nconsts;
+ uint32_t values_curr;
+ uint32_t values_tail;
+ uint32_t values_size;
+ uint32_t codes_curr;
+ uint32_t codes_size;
+
+ grn_obj objs;
+ grn_obj dfi;
+ grn_expr_code *code0;
+};
+
+GRN_API grn_rc grn_expr_clear_vars(grn_ctx *ctx, grn_obj *expr);
+
+grn_rc grn_expr_parser_close(grn_ctx *ctx);
+GRN_API grn_rc grn_obj_cast(grn_ctx *ctx, grn_obj *src, grn_obj *dest, grn_bool addp);
+
+/**
+ * grn_table_open:
+ * @name: 開こうとするtableの名前。NULLなら無名tableとなる。
+ * @path: 開こうとするtableのファイルパス。
+ *
+ * ctxが使用するdbの中でnameに対応付けて既存のtableを開く。
+ * dbに登録されている名前付きの永続テーブルを開く場合はgrn_ctx_get()を使用するのが望ましい。
+ **/
+GRN_API grn_obj *grn_table_open(grn_ctx *ctx,
+ const char *name, unsigned int name_size,
+ const char *path);
+
+/**
+ * grn_column_open:
+ * @table: 対象table
+ * @name: カラム名
+ * @path: カラムを格納するファイルパス。
+ * @type: カラム値の型。
+ *
+ * 既存の永続的なcolumnを、tableのnameに対応するcolumnとして開く
+ * 永続dbに登録されている永続テーブルのカラムを開く場合はgrn_ctx_get()を使用するのが望ましい。
+ **/
+grn_obj *grn_column_open(grn_ctx *ctx, grn_obj *table,
+ const char *name, unsigned int name_size,
+ const char *path, grn_obj *type);
+
+/**
+ * grn_obj_path_rename:
+ * @old_path: 旧ファイルパス
+ * @new_path: 新ファイルパス
+ *
+ * old_pathに該当するオブジェクトのファイル名をnew_pathに変更する。
+ **/
+grn_rc grn_obj_path_rename(grn_ctx *ctx, const char *old_path, const char *new_path);
+
+grn_rc grn_db_check_name(grn_ctx *ctx, const char *name, unsigned int name_size);
+#define GRN_DB_CHECK_NAME_ERR(error_context, name, name_size) \
+ ERR(GRN_INVALID_ARGUMENT,\
+ "%s name can't start with '%c' and contains only 0-9, A-Z, a-z, #, @, - or _: <%.*s>",\
+ error_context, GRN_DB_PSEUDO_COLUMN_PREFIX, name_size, name)
+
+#define GRN_DB_P(s) ((s) && ((grn_db *)s)->obj.header.type == GRN_DB)
+#define GRN_DB_PERSISTENT_P(s) (((grn_db *)s)->specs)
+
+#define GRN_OBJ_GET_VALUE_IMD (0xffffffffU)
+
+grn_rc grn_db_obj_init(grn_ctx *ctx, grn_obj *db, grn_id id, grn_db_obj *obj);
+
+#define GRN_ACCESSORP(obj) \
+ ((obj) && (((grn_obj *)(obj))->header.type == GRN_ACCESSOR))
+
+#define GRN_TRUEP(ctx, v, result) do {\
+ switch (v->header.type) { \
+ case GRN_BULK : \
+ switch (v->header.domain) { \
+ case GRN_DB_BOOL : \
+ result = GRN_BOOL_VALUE(v); \
+ break; \
+ case GRN_DB_INT32 : \
+ result = GRN_INT32_VALUE(v) != 0; \
+ break; \
+ case GRN_DB_UINT32 : \
+ result = GRN_UINT32_VALUE(v) != 0; \
+ break; \
+ case GRN_DB_FLOAT : \
+ { \
+ double float_value; \
+ float_value = GRN_FLOAT_VALUE(v); \
+ result = (float_value < -DBL_EPSILON || \
+ DBL_EPSILON < float_value); \
+ } \
+ break; \
+ case GRN_DB_SHORT_TEXT : \
+ case GRN_DB_TEXT : \
+ case GRN_DB_LONG_TEXT : \
+ result = GRN_TEXT_LEN(v) != 0; \
+ break; \
+ default : \
+ result = GRN_FALSE; \
+ break; \
+ } \
+ break; \
+ case GRN_VECTOR : \
+ result = GRN_TRUE; \
+ break; \
+ default : \
+ result = GRN_FALSE; \
+ break; \
+ } \
+} while (0)
+
+grn_id grn_obj_register(grn_ctx *ctx, grn_obj *db, const char *name, unsigned int name_size);
+int grn_obj_is_persistent(grn_ctx *ctx, grn_obj *obj);
+void grn_obj_spec_save(grn_ctx *ctx, grn_db_obj *obj);
+
+grn_rc grn_obj_reinit_for(grn_ctx *ctx, grn_obj *obj, grn_obj *domain_obj);
+
+#define GRN_INT32_POP(obj,value) do {\
+ if (GRN_BULK_VSIZE(obj) >= sizeof(int32_t)) {\
+ GRN_BULK_INCR_LEN((obj), -(sizeof(int32_t)));\
+ value = *(int32_t *)(GRN_BULK_CURR(obj));\
+ } else {\
+ value = 0;\
+ }\
+} while (0)
+
+#define GRN_UINT32_POP(obj,value) do {\
+ if (GRN_BULK_VSIZE(obj) >= sizeof(uint32_t)) {\
+ GRN_BULK_INCR_LEN((obj), -(sizeof(uint32_t)));\
+ value = *(uint32_t *)(GRN_BULK_CURR(obj));\
+ } else {\
+ value = 0;\
+ }\
+} while (0)
+
+void grn_expr_pack(grn_ctx *ctx, grn_obj *buf, grn_obj *expr);
+GRN_API grn_rc grn_expr_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *expr);
+grn_obj *grn_expr_open(grn_ctx *ctx, grn_obj_spec *spec, const uint8_t *p, const uint8_t *pe);
+
+GRN_API void grn_load_(grn_ctx *ctx, grn_content_type input_type,
+ const char *table, unsigned int table_len,
+ const char *columns, unsigned int columns_len,
+ const char *values, unsigned int values_len,
+ const char *ifexists, unsigned int ifexists_len,
+ const char *each, unsigned int each_len,
+ uint32_t emit_level);
+
+GRN_API grn_rc grn_table_group_with_range_gap(grn_ctx *ctx, grn_obj *table,
+ grn_table_sort_key *group_key,
+ grn_obj *result_set,
+ uint32_t range_gap);
+
+GRN_API grn_rc grn_column_filter(grn_ctx *ctx, grn_obj *column,
+ grn_operator op,
+ grn_obj *value, grn_obj *result_set,
+ grn_operator set_op);
+
+grn_rc grn_accessor_resolve(grn_ctx *ctx, grn_obj *accessor, int deep,
+ grn_obj *base_res, grn_obj **res,
+ grn_search_optarg *optarg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_DB_H */
diff --git a/storage/mroonga/vendor/groonga/lib/ecmascript.c b/storage/mroonga/vendor/groonga/lib/ecmascript.c
new file mode 100644
index 00000000000..fd74f95b815
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/ecmascript.c
@@ -0,0 +1,2242 @@
+/* Driver template for the LEMON parser generator.
+** The author disclaims copyright to this source code.
+*/
+/* First off, code is included that follows the "include" declaration
+** in the input grammar file. */
+#include <stdio.h>
+#line 3 "ecmascript.lemon"
+
+#define assert GRN_ASSERT
+#line 11 "ecmascript.c"
+/* Next is all token values, in a form suitable for use by makeheaders.
+** This section will be null unless lemon is run with the -m switch.
+*/
+/*
+** These constants (all generated automatically by the parser generator)
+** specify the various kinds of tokens (terminals) that the parser
+** understands.
+**
+** Each symbol here is a terminal symbol in the grammar.
+*/
+/* Make sure the INTERFACE macro is defined.
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/* The next thing included is series of defines which control
+** various aspects of the generated parser.
+** YYCODETYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 terminals
+** and nonterminals. "int" is used otherwise.
+** YYNOCODE is a number of type YYCODETYPE which corresponds
+** to no legal terminal or nonterminal number. This
+** number is used to fill in empty slots of the hash
+** table.
+** YYFALLBACK If defined, this indicates that one or more tokens
+** have fall-back values which should be used if the
+** original value of the token will not parse.
+** YYACTIONTYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 rules and
+** states combined. "int" is used otherwise.
+** grn_expr_parserTOKENTYPE is the data type used for minor tokens given
+** directly to the parser from the tokenizer.
+** YYMINORTYPE is the data type used for all minor tokens.
+** This is typically a union of many types, one of
+** which is grn_expr_parserTOKENTYPE. The entry in the union
+** for base tokens is called "yy0".
+** YYSTACKDEPTH is the maximum depth of the parser's stack. If
+** zero the stack is dynamically sized using realloc()
+** grn_expr_parserARG_SDECL A static variable declaration for the %extra_argument
+** grn_expr_parserARG_PDECL A parameter declaration for the %extra_argument
+** grn_expr_parserARG_STORE Code to store %extra_argument into yypParser
+** grn_expr_parserARG_FETCH Code to extract %extra_argument from yypParser
+** YYNSTATE the combined number of states.
+** YYNRULE the number of rules in the grammar
+** YYERRORSYMBOL is the code number of the error symbol. If not
+** defined, then do no error processing.
+*/
+#define YYCODETYPE unsigned char
+#define YYNOCODE 113
+#define YYACTIONTYPE unsigned short int
+#define grn_expr_parserTOKENTYPE int
+typedef union {
+ int yyinit;
+ grn_expr_parserTOKENTYPE yy0;
+ void * yy81;
+} YYMINORTYPE;
+#ifndef YYSTACKDEPTH
+#define YYSTACKDEPTH 100
+#endif
+#define grn_expr_parserARG_SDECL efs_info *efsi ;
+#define grn_expr_parserARG_PDECL , efs_info *efsi
+#define grn_expr_parserARG_FETCH efs_info *efsi = yypParser->efsi
+#define grn_expr_parserARG_STORE yypParser->efsi = efsi
+#define YYNSTATE 223
+#define YYNRULE 131
+#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
+#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
+#define YY_ERROR_ACTION (YYNSTATE+YYNRULE)
+
+/* The yyzerominor constant is used to initialize instances of
+** YYMINORTYPE objects to zero. */
+static const YYMINORTYPE yyzerominor = { 0 };
+
+/* Define the yytestcase() macro to be a no-op if is not already defined
+** otherwise.
+**
+** Applications can choose to define yytestcase() in the %include section
+** to a macro that can assist in verifying code coverage. For production
+** code the yytestcase() macro should be turned off. But it is useful
+** for testing.
+*/
+#ifndef yytestcase
+# define yytestcase(X)
+#endif
+
+
+/* Next are the tables used to determine what action to take based on the
+** current state and lookahead token. These tables are used to implement
+** functions that take a state number and lookahead value and return an
+** action integer.
+**
+** Suppose the action integer is N. Then the action is determined as
+** follows
+**
+** 0 <= N < YYNSTATE Shift N. That is, push the lookahead
+** token onto the stack and goto state N.
+**
+** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE.
+**
+** N == YYNSTATE+YYNRULE A syntax error has occurred.
+**
+** N == YYNSTATE+YYNRULE+1 The parser accepts its input.
+**
+** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused
+** 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
+**
+** yy_action[ yy_shift_ofst[S] + X ]
+**
+** 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 formula above is 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
+** YY_SHIFT_USE_DFLT.
+**
+** The following are the tables generated in this section:
+**
+** yy_action[] A single table containing all actions.
+** yy_lookahead[] A table containing the lookahead for each entry in
+** yy_action. Used to detect hash collisions.
+** yy_shift_ofst[] For each state, the offset into yy_action for
+** shifting terminals.
+** yy_reduce_ofst[] For each state, the offset into yy_action for
+** shifting non-terminals after a reduce.
+** yy_default[] Default action for each state.
+*/
+#define YY_ACTTAB_COUNT (1610)
+static const YYACTIONTYPE yy_action[] = {
+ /* 0 */ 2, 70, 52, 51, 50, 220, 1, 75, 79, 123,
+ /* 10 */ 4, 219, 69, 355, 76, 107, 78, 150, 219, 189,
+ /* 20 */ 192, 213, 84, 121, 120, 133, 132, 131, 115, 85,
+ /* 30 */ 98, 111, 99, 178, 209, 195, 73, 188, 184, 188,
+ /* 40 */ 184, 220, 71, 25, 79, 138, 9, 32, 69, 64,
+ /* 50 */ 63, 215, 33, 28, 67, 66, 65, 62, 61, 60,
+ /* 60 */ 59, 58, 57, 183, 182, 181, 180, 179, 3, 75,
+ /* 70 */ 113, 34, 6, 219, 189, 192, 213, 84, 121, 120,
+ /* 80 */ 133, 132, 131, 115, 85, 98, 111, 99, 178, 209,
+ /* 90 */ 195, 73, 187, 105, 188, 184, 220, 1, 28, 79,
+ /* 100 */ 123, 4, 122, 69, 31, 30, 189, 192, 213, 84,
+ /* 110 */ 121, 120, 133, 132, 131, 115, 85, 98, 111, 99,
+ /* 120 */ 178, 209, 195, 73, 139, 127, 188, 184, 36, 35,
+ /* 130 */ 110, 68, 56, 55, 8, 24, 129, 197, 196, 29,
+ /* 140 */ 64, 63, 54, 53, 124, 67, 66, 65, 62, 61,
+ /* 150 */ 60, 59, 58, 57, 183, 182, 181, 180, 179, 3,
+ /* 160 */ 7, 26, 165, 185, 83, 142, 127, 176, 189, 166,
+ /* 170 */ 213, 84, 121, 120, 133, 132, 131, 115, 85, 98,
+ /* 180 */ 111, 99, 178, 209, 195, 73, 173, 130, 188, 184,
+ /* 190 */ 11, 82, 81, 80, 77, 220, 71, 148, 79, 138,
+ /* 200 */ 9, 171, 69, 64, 63, 174, 28, 72, 67, 66,
+ /* 210 */ 65, 62, 61, 60, 59, 58, 57, 183, 182, 181,
+ /* 220 */ 180, 179, 3, 177, 7, 194, 193, 185, 83, 106,
+ /* 230 */ 126, 176, 189, 144, 213, 84, 121, 120, 133, 132,
+ /* 240 */ 131, 115, 85, 98, 111, 99, 178, 209, 195, 73,
+ /* 250 */ 164, 224, 188, 184, 141, 171, 23, 171, 10, 110,
+ /* 260 */ 143, 226, 191, 140, 221, 28, 218, 64, 63, 125,
+ /* 270 */ 356, 356, 67, 66, 65, 62, 61, 60, 59, 58,
+ /* 280 */ 57, 183, 182, 181, 180, 179, 3, 170, 7, 122,
+ /* 290 */ 217, 185, 83, 189, 192, 213, 84, 121, 120, 133,
+ /* 300 */ 132, 131, 115, 85, 98, 111, 99, 178, 209, 195,
+ /* 310 */ 73, 74, 216, 188, 184, 225, 49, 48, 47, 46,
+ /* 320 */ 45, 44, 43, 42, 41, 40, 39, 38, 37, 5,
+ /* 330 */ 149, 64, 63, 146, 222, 356, 67, 66, 65, 62,
+ /* 340 */ 61, 60, 59, 58, 57, 183, 182, 181, 180, 179,
+ /* 350 */ 3, 116, 356, 145, 356, 189, 192, 213, 84, 121,
+ /* 360 */ 120, 133, 132, 131, 115, 85, 98, 111, 99, 178,
+ /* 370 */ 209, 195, 73, 113, 356, 188, 184, 189, 192, 213,
+ /* 380 */ 84, 121, 120, 133, 132, 131, 115, 85, 98, 111,
+ /* 390 */ 99, 178, 209, 195, 73, 356, 356, 188, 184, 223,
+ /* 400 */ 356, 356, 81, 80, 77, 220, 71, 356, 79, 138,
+ /* 410 */ 9, 356, 69, 189, 162, 213, 84, 121, 120, 133,
+ /* 420 */ 132, 131, 115, 85, 98, 111, 99, 178, 209, 195,
+ /* 430 */ 73, 356, 7, 188, 184, 185, 83, 356, 356, 167,
+ /* 440 */ 109, 189, 144, 213, 84, 121, 120, 133, 132, 131,
+ /* 450 */ 115, 85, 98, 111, 99, 178, 209, 195, 73, 356,
+ /* 460 */ 7, 188, 184, 185, 83, 356, 356, 356, 356, 147,
+ /* 470 */ 356, 356, 356, 356, 356, 64, 63, 356, 356, 356,
+ /* 480 */ 67, 66, 65, 62, 61, 60, 59, 58, 57, 183,
+ /* 490 */ 182, 181, 180, 179, 3, 356, 356, 356, 356, 356,
+ /* 500 */ 356, 356, 356, 64, 63, 356, 356, 167, 67, 66,
+ /* 510 */ 65, 62, 61, 60, 59, 58, 57, 183, 182, 181,
+ /* 520 */ 180, 179, 3, 189, 214, 213, 84, 121, 120, 133,
+ /* 530 */ 132, 131, 115, 85, 98, 111, 99, 178, 209, 195,
+ /* 540 */ 73, 356, 356, 188, 184, 189, 212, 213, 84, 121,
+ /* 550 */ 120, 133, 132, 131, 115, 85, 98, 111, 99, 178,
+ /* 560 */ 209, 195, 73, 168, 356, 188, 184, 189, 137, 213,
+ /* 570 */ 84, 121, 120, 133, 132, 131, 115, 85, 98, 111,
+ /* 580 */ 99, 178, 209, 195, 73, 356, 356, 188, 184, 356,
+ /* 590 */ 189, 211, 213, 84, 121, 120, 133, 132, 131, 115,
+ /* 600 */ 85, 98, 111, 99, 178, 209, 195, 73, 356, 356,
+ /* 610 */ 188, 184, 189, 172, 213, 84, 121, 120, 133, 132,
+ /* 620 */ 131, 115, 85, 98, 111, 99, 178, 209, 195, 73,
+ /* 630 */ 356, 356, 188, 184, 189, 163, 213, 84, 121, 120,
+ /* 640 */ 133, 132, 131, 115, 85, 98, 111, 99, 178, 209,
+ /* 650 */ 195, 73, 356, 356, 188, 184, 189, 161, 213, 84,
+ /* 660 */ 121, 120, 133, 132, 131, 115, 85, 98, 111, 99,
+ /* 670 */ 178, 209, 195, 73, 356, 356, 188, 184, 189, 160,
+ /* 680 */ 213, 84, 121, 120, 133, 132, 131, 115, 85, 98,
+ /* 690 */ 111, 99, 178, 209, 195, 73, 356, 356, 188, 184,
+ /* 700 */ 189, 159, 213, 84, 121, 120, 133, 132, 131, 115,
+ /* 710 */ 85, 98, 111, 99, 178, 209, 195, 73, 356, 356,
+ /* 720 */ 188, 184, 189, 158, 213, 84, 121, 120, 133, 132,
+ /* 730 */ 131, 115, 85, 98, 111, 99, 178, 209, 195, 73,
+ /* 740 */ 356, 356, 188, 184, 189, 157, 213, 84, 121, 120,
+ /* 750 */ 133, 132, 131, 115, 85, 98, 111, 99, 178, 209,
+ /* 760 */ 195, 73, 356, 356, 188, 184, 189, 156, 213, 84,
+ /* 770 */ 121, 120, 133, 132, 131, 115, 85, 98, 111, 99,
+ /* 780 */ 178, 209, 195, 73, 356, 356, 188, 184, 189, 155,
+ /* 790 */ 213, 84, 121, 120, 133, 132, 131, 115, 85, 98,
+ /* 800 */ 111, 99, 178, 209, 195, 73, 356, 356, 188, 184,
+ /* 810 */ 189, 154, 213, 84, 121, 120, 133, 132, 131, 115,
+ /* 820 */ 85, 98, 111, 99, 178, 209, 195, 73, 356, 356,
+ /* 830 */ 188, 184, 189, 153, 213, 84, 121, 120, 133, 132,
+ /* 840 */ 131, 115, 85, 98, 111, 99, 178, 209, 195, 73,
+ /* 850 */ 356, 356, 188, 184, 189, 152, 213, 84, 121, 120,
+ /* 860 */ 133, 132, 131, 115, 85, 98, 111, 99, 178, 209,
+ /* 870 */ 195, 73, 356, 356, 188, 184, 189, 151, 213, 84,
+ /* 880 */ 121, 120, 133, 132, 131, 115, 85, 98, 111, 99,
+ /* 890 */ 178, 209, 195, 73, 356, 356, 188, 184, 189, 175,
+ /* 900 */ 213, 84, 121, 120, 133, 132, 131, 115, 85, 98,
+ /* 910 */ 111, 99, 178, 209, 195, 73, 356, 356, 188, 184,
+ /* 920 */ 189, 169, 213, 84, 121, 120, 133, 132, 131, 115,
+ /* 930 */ 85, 98, 111, 99, 178, 209, 195, 73, 356, 189,
+ /* 940 */ 188, 184, 117, 356, 108, 133, 132, 131, 115, 85,
+ /* 950 */ 98, 111, 99, 178, 209, 195, 73, 356, 189, 188,
+ /* 960 */ 184, 117, 356, 356, 136, 132, 131, 115, 85, 98,
+ /* 970 */ 111, 99, 178, 209, 195, 73, 356, 356, 188, 184,
+ /* 980 */ 189, 356, 356, 117, 356, 356, 128, 132, 131, 115,
+ /* 990 */ 85, 98, 111, 99, 178, 209, 195, 73, 356, 356,
+ /* 1000 */ 188, 184, 189, 356, 356, 117, 356, 356, 356, 135,
+ /* 1010 */ 131, 115, 85, 98, 111, 99, 178, 209, 195, 73,
+ /* 1020 */ 356, 356, 188, 184, 356, 27, 22, 21, 20, 19,
+ /* 1030 */ 18, 17, 16, 15, 14, 13, 12, 189, 356, 356,
+ /* 1040 */ 117, 356, 356, 356, 356, 134, 115, 85, 98, 111,
+ /* 1050 */ 99, 178, 209, 195, 73, 356, 356, 188, 184, 356,
+ /* 1060 */ 189, 356, 356, 117, 356, 356, 197, 196, 356, 119,
+ /* 1070 */ 85, 98, 111, 99, 178, 209, 195, 73, 356, 189,
+ /* 1080 */ 188, 184, 117, 7, 356, 356, 185, 83, 356, 87,
+ /* 1090 */ 98, 111, 99, 178, 209, 195, 73, 356, 189, 188,
+ /* 1100 */ 184, 117, 356, 356, 356, 356, 356, 356, 86, 98,
+ /* 1110 */ 111, 99, 178, 209, 195, 73, 356, 189, 188, 184,
+ /* 1120 */ 117, 356, 356, 356, 356, 356, 356, 356, 104, 111,
+ /* 1130 */ 99, 178, 209, 195, 73, 356, 189, 188, 184, 117,
+ /* 1140 */ 183, 182, 181, 180, 179, 3, 356, 102, 111, 99,
+ /* 1150 */ 178, 209, 195, 73, 356, 189, 188, 184, 117, 356,
+ /* 1160 */ 356, 356, 356, 356, 356, 356, 100, 111, 99, 178,
+ /* 1170 */ 209, 195, 73, 356, 189, 188, 184, 117, 356, 356,
+ /* 1180 */ 356, 356, 356, 356, 356, 97, 111, 99, 178, 209,
+ /* 1190 */ 195, 73, 356, 189, 188, 184, 117, 356, 356, 356,
+ /* 1200 */ 356, 356, 356, 356, 96, 111, 99, 178, 209, 195,
+ /* 1210 */ 73, 356, 189, 188, 184, 117, 356, 356, 356, 356,
+ /* 1220 */ 356, 356, 356, 95, 111, 99, 178, 209, 195, 73,
+ /* 1230 */ 356, 189, 188, 184, 117, 356, 356, 356, 356, 356,
+ /* 1240 */ 356, 356, 94, 111, 99, 178, 209, 195, 73, 356,
+ /* 1250 */ 189, 188, 184, 117, 356, 356, 356, 356, 356, 356,
+ /* 1260 */ 356, 93, 111, 99, 178, 209, 195, 73, 356, 189,
+ /* 1270 */ 188, 184, 117, 356, 356, 356, 356, 356, 356, 356,
+ /* 1280 */ 92, 111, 99, 178, 209, 195, 73, 356, 189, 188,
+ /* 1290 */ 184, 117, 356, 356, 356, 356, 356, 356, 356, 91,
+ /* 1300 */ 111, 99, 178, 209, 195, 73, 356, 189, 188, 184,
+ /* 1310 */ 117, 356, 356, 356, 356, 356, 356, 356, 90, 111,
+ /* 1320 */ 99, 178, 209, 195, 73, 356, 189, 188, 184, 117,
+ /* 1330 */ 356, 356, 356, 356, 356, 356, 356, 89, 111, 99,
+ /* 1340 */ 178, 209, 195, 73, 356, 189, 188, 184, 117, 356,
+ /* 1350 */ 356, 356, 356, 356, 356, 356, 88, 111, 99, 178,
+ /* 1360 */ 209, 195, 73, 356, 189, 188, 184, 117, 356, 356,
+ /* 1370 */ 356, 356, 356, 356, 356, 356, 118, 99, 178, 209,
+ /* 1380 */ 195, 73, 356, 189, 188, 184, 117, 356, 356, 356,
+ /* 1390 */ 356, 356, 356, 356, 356, 114, 99, 178, 209, 195,
+ /* 1400 */ 73, 356, 189, 188, 184, 117, 356, 356, 356, 356,
+ /* 1410 */ 356, 356, 356, 356, 112, 99, 178, 209, 195, 73,
+ /* 1420 */ 356, 189, 188, 184, 117, 356, 356, 356, 356, 356,
+ /* 1430 */ 189, 356, 356, 117, 103, 178, 209, 195, 73, 356,
+ /* 1440 */ 356, 188, 184, 101, 178, 209, 195, 73, 356, 189,
+ /* 1450 */ 188, 184, 117, 356, 356, 356, 356, 356, 189, 356,
+ /* 1460 */ 356, 117, 356, 210, 209, 195, 73, 356, 189, 188,
+ /* 1470 */ 184, 117, 208, 209, 195, 73, 356, 189, 188, 184,
+ /* 1480 */ 117, 356, 207, 209, 195, 73, 356, 189, 188, 184,
+ /* 1490 */ 117, 206, 209, 195, 73, 356, 189, 188, 184, 117,
+ /* 1500 */ 356, 205, 209, 195, 73, 356, 189, 188, 184, 117,
+ /* 1510 */ 204, 209, 195, 73, 356, 189, 188, 184, 117, 356,
+ /* 1520 */ 203, 209, 195, 73, 356, 189, 188, 184, 117, 202,
+ /* 1530 */ 209, 195, 73, 356, 189, 188, 184, 117, 356, 201,
+ /* 1540 */ 209, 195, 73, 356, 356, 188, 184, 356, 200, 209,
+ /* 1550 */ 195, 73, 356, 189, 188, 184, 117, 356, 356, 356,
+ /* 1560 */ 356, 189, 356, 356, 117, 356, 356, 199, 209, 195,
+ /* 1570 */ 73, 356, 356, 188, 184, 198, 209, 195, 73, 356,
+ /* 1580 */ 189, 188, 184, 117, 356, 356, 356, 356, 189, 356,
+ /* 1590 */ 356, 117, 356, 356, 190, 209, 195, 73, 356, 356,
+ /* 1600 */ 188, 184, 186, 209, 195, 73, 356, 356, 188, 184,
+};
+static const YYCODETYPE yy_lookahead[] = {
+ /* 0 */ 1, 2, 47, 48, 49, 6, 7, 76, 9, 10,
+ /* 10 */ 11, 80, 13, 75, 76, 77, 9, 81, 80, 81,
+ /* 20 */ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
+ /* 30 */ 92, 93, 94, 95, 96, 97, 98, 101, 102, 101,
+ /* 40 */ 102, 6, 7, 28, 9, 10, 11, 29, 13, 50,
+ /* 50 */ 51, 12, 30, 14, 55, 56, 57, 58, 59, 60,
+ /* 60 */ 61, 62, 63, 64, 65, 66, 67, 68, 69, 76,
+ /* 70 */ 77, 31, 7, 80, 81, 82, 83, 84, 85, 86,
+ /* 80 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+ /* 90 */ 97, 98, 8, 79, 101, 102, 6, 7, 14, 9,
+ /* 100 */ 10, 11, 77, 13, 3, 4, 81, 82, 83, 84,
+ /* 110 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ /* 120 */ 95, 96, 97, 98, 110, 111, 101, 102, 32, 33,
+ /* 130 */ 105, 52, 53, 54, 69, 28, 71, 56, 57, 5,
+ /* 140 */ 50, 51, 50, 51, 39, 55, 56, 57, 58, 59,
+ /* 150 */ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ /* 160 */ 7, 27, 10, 10, 11, 110, 111, 14, 81, 82,
+ /* 170 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ /* 180 */ 93, 94, 95, 96, 97, 98, 106, 107, 101, 102,
+ /* 190 */ 103, 104, 3, 4, 5, 6, 7, 8, 9, 10,
+ /* 200 */ 11, 10, 13, 50, 51, 12, 14, 14, 55, 56,
+ /* 210 */ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
+ /* 220 */ 67, 68, 69, 70, 7, 99, 100, 10, 11, 78,
+ /* 230 */ 52, 14, 81, 82, 83, 84, 85, 86, 87, 88,
+ /* 240 */ 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
+ /* 250 */ 8, 0, 101, 102, 64, 64, 14, 66, 103, 105,
+ /* 260 */ 109, 0, 70, 66, 80, 14, 80, 50, 51, 10,
+ /* 270 */ 112, 112, 55, 56, 57, 58, 59, 60, 61, 62,
+ /* 280 */ 63, 64, 65, 66, 67, 68, 69, 70, 7, 77,
+ /* 290 */ 80, 10, 11, 81, 82, 83, 84, 85, 86, 87,
+ /* 300 */ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
+ /* 310 */ 98, 50, 80, 101, 102, 0, 34, 35, 36, 37,
+ /* 320 */ 38, 39, 40, 41, 42, 43, 44, 45, 46, 14,
+ /* 330 */ 80, 50, 51, 52, 80, 112, 55, 56, 57, 58,
+ /* 340 */ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
+ /* 350 */ 69, 77, 112, 72, 112, 81, 82, 83, 84, 85,
+ /* 360 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ /* 370 */ 96, 97, 98, 77, 112, 101, 102, 81, 82, 83,
+ /* 380 */ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
+ /* 390 */ 94, 95, 96, 97, 98, 112, 112, 101, 102, 0,
+ /* 400 */ 112, 112, 3, 4, 5, 6, 7, 112, 9, 10,
+ /* 410 */ 11, 112, 13, 81, 82, 83, 84, 85, 86, 87,
+ /* 420 */ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
+ /* 430 */ 98, 112, 7, 101, 102, 10, 11, 112, 112, 14,
+ /* 440 */ 108, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ /* 450 */ 90, 91, 92, 93, 94, 95, 96, 97, 98, 112,
+ /* 460 */ 7, 101, 102, 10, 11, 112, 112, 112, 112, 109,
+ /* 470 */ 112, 112, 112, 112, 112, 50, 51, 112, 112, 112,
+ /* 480 */ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ /* 490 */ 65, 66, 67, 68, 69, 112, 112, 112, 112, 112,
+ /* 500 */ 112, 112, 112, 50, 51, 112, 112, 14, 55, 56,
+ /* 510 */ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66,
+ /* 520 */ 67, 68, 69, 81, 82, 83, 84, 85, 86, 87,
+ /* 530 */ 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
+ /* 540 */ 98, 112, 112, 101, 102, 81, 82, 83, 84, 85,
+ /* 550 */ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ /* 560 */ 96, 97, 98, 70, 112, 101, 102, 81, 82, 83,
+ /* 570 */ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
+ /* 580 */ 94, 95, 96, 97, 98, 112, 112, 101, 102, 112,
+ /* 590 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
+ /* 600 */ 91, 92, 93, 94, 95, 96, 97, 98, 112, 112,
+ /* 610 */ 101, 102, 81, 82, 83, 84, 85, 86, 87, 88,
+ /* 620 */ 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
+ /* 630 */ 112, 112, 101, 102, 81, 82, 83, 84, 85, 86,
+ /* 640 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+ /* 650 */ 97, 98, 112, 112, 101, 102, 81, 82, 83, 84,
+ /* 660 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ /* 670 */ 95, 96, 97, 98, 112, 112, 101, 102, 81, 82,
+ /* 680 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ /* 690 */ 93, 94, 95, 96, 97, 98, 112, 112, 101, 102,
+ /* 700 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
+ /* 710 */ 91, 92, 93, 94, 95, 96, 97, 98, 112, 112,
+ /* 720 */ 101, 102, 81, 82, 83, 84, 85, 86, 87, 88,
+ /* 730 */ 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
+ /* 740 */ 112, 112, 101, 102, 81, 82, 83, 84, 85, 86,
+ /* 750 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+ /* 760 */ 97, 98, 112, 112, 101, 102, 81, 82, 83, 84,
+ /* 770 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ /* 780 */ 95, 96, 97, 98, 112, 112, 101, 102, 81, 82,
+ /* 790 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ /* 800 */ 93, 94, 95, 96, 97, 98, 112, 112, 101, 102,
+ /* 810 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
+ /* 820 */ 91, 92, 93, 94, 95, 96, 97, 98, 112, 112,
+ /* 830 */ 101, 102, 81, 82, 83, 84, 85, 86, 87, 88,
+ /* 840 */ 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
+ /* 850 */ 112, 112, 101, 102, 81, 82, 83, 84, 85, 86,
+ /* 860 */ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+ /* 870 */ 97, 98, 112, 112, 101, 102, 81, 82, 83, 84,
+ /* 880 */ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ /* 890 */ 95, 96, 97, 98, 112, 112, 101, 102, 81, 82,
+ /* 900 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ /* 910 */ 93, 94, 95, 96, 97, 98, 112, 112, 101, 102,
+ /* 920 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
+ /* 930 */ 91, 92, 93, 94, 95, 96, 97, 98, 112, 81,
+ /* 940 */ 101, 102, 84, 112, 86, 87, 88, 89, 90, 91,
+ /* 950 */ 92, 93, 94, 95, 96, 97, 98, 112, 81, 101,
+ /* 960 */ 102, 84, 112, 112, 87, 88, 89, 90, 91, 92,
+ /* 970 */ 93, 94, 95, 96, 97, 98, 112, 112, 101, 102,
+ /* 980 */ 81, 112, 112, 84, 112, 112, 87, 88, 89, 90,
+ /* 990 */ 91, 92, 93, 94, 95, 96, 97, 98, 112, 112,
+ /* 1000 */ 101, 102, 81, 112, 112, 84, 112, 112, 112, 88,
+ /* 1010 */ 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
+ /* 1020 */ 112, 112, 101, 102, 112, 15, 16, 17, 18, 19,
+ /* 1030 */ 20, 21, 22, 23, 24, 25, 26, 81, 112, 112,
+ /* 1040 */ 84, 112, 112, 112, 112, 89, 90, 91, 92, 93,
+ /* 1050 */ 94, 95, 96, 97, 98, 112, 112, 101, 102, 112,
+ /* 1060 */ 81, 112, 112, 84, 112, 112, 56, 57, 112, 90,
+ /* 1070 */ 91, 92, 93, 94, 95, 96, 97, 98, 112, 81,
+ /* 1080 */ 101, 102, 84, 7, 112, 112, 10, 11, 112, 91,
+ /* 1090 */ 92, 93, 94, 95, 96, 97, 98, 112, 81, 101,
+ /* 1100 */ 102, 84, 112, 112, 112, 112, 112, 112, 91, 92,
+ /* 1110 */ 93, 94, 95, 96, 97, 98, 112, 81, 101, 102,
+ /* 1120 */ 84, 112, 112, 112, 112, 112, 112, 112, 92, 93,
+ /* 1130 */ 94, 95, 96, 97, 98, 112, 81, 101, 102, 84,
+ /* 1140 */ 64, 65, 66, 67, 68, 69, 112, 92, 93, 94,
+ /* 1150 */ 95, 96, 97, 98, 112, 81, 101, 102, 84, 112,
+ /* 1160 */ 112, 112, 112, 112, 112, 112, 92, 93, 94, 95,
+ /* 1170 */ 96, 97, 98, 112, 81, 101, 102, 84, 112, 112,
+ /* 1180 */ 112, 112, 112, 112, 112, 92, 93, 94, 95, 96,
+ /* 1190 */ 97, 98, 112, 81, 101, 102, 84, 112, 112, 112,
+ /* 1200 */ 112, 112, 112, 112, 92, 93, 94, 95, 96, 97,
+ /* 1210 */ 98, 112, 81, 101, 102, 84, 112, 112, 112, 112,
+ /* 1220 */ 112, 112, 112, 92, 93, 94, 95, 96, 97, 98,
+ /* 1230 */ 112, 81, 101, 102, 84, 112, 112, 112, 112, 112,
+ /* 1240 */ 112, 112, 92, 93, 94, 95, 96, 97, 98, 112,
+ /* 1250 */ 81, 101, 102, 84, 112, 112, 112, 112, 112, 112,
+ /* 1260 */ 112, 92, 93, 94, 95, 96, 97, 98, 112, 81,
+ /* 1270 */ 101, 102, 84, 112, 112, 112, 112, 112, 112, 112,
+ /* 1280 */ 92, 93, 94, 95, 96, 97, 98, 112, 81, 101,
+ /* 1290 */ 102, 84, 112, 112, 112, 112, 112, 112, 112, 92,
+ /* 1300 */ 93, 94, 95, 96, 97, 98, 112, 81, 101, 102,
+ /* 1310 */ 84, 112, 112, 112, 112, 112, 112, 112, 92, 93,
+ /* 1320 */ 94, 95, 96, 97, 98, 112, 81, 101, 102, 84,
+ /* 1330 */ 112, 112, 112, 112, 112, 112, 112, 92, 93, 94,
+ /* 1340 */ 95, 96, 97, 98, 112, 81, 101, 102, 84, 112,
+ /* 1350 */ 112, 112, 112, 112, 112, 112, 92, 93, 94, 95,
+ /* 1360 */ 96, 97, 98, 112, 81, 101, 102, 84, 112, 112,
+ /* 1370 */ 112, 112, 112, 112, 112, 112, 93, 94, 95, 96,
+ /* 1380 */ 97, 98, 112, 81, 101, 102, 84, 112, 112, 112,
+ /* 1390 */ 112, 112, 112, 112, 112, 93, 94, 95, 96, 97,
+ /* 1400 */ 98, 112, 81, 101, 102, 84, 112, 112, 112, 112,
+ /* 1410 */ 112, 112, 112, 112, 93, 94, 95, 96, 97, 98,
+ /* 1420 */ 112, 81, 101, 102, 84, 112, 112, 112, 112, 112,
+ /* 1430 */ 81, 112, 112, 84, 94, 95, 96, 97, 98, 112,
+ /* 1440 */ 112, 101, 102, 94, 95, 96, 97, 98, 112, 81,
+ /* 1450 */ 101, 102, 84, 112, 112, 112, 112, 112, 81, 112,
+ /* 1460 */ 112, 84, 112, 95, 96, 97, 98, 112, 81, 101,
+ /* 1470 */ 102, 84, 95, 96, 97, 98, 112, 81, 101, 102,
+ /* 1480 */ 84, 112, 95, 96, 97, 98, 112, 81, 101, 102,
+ /* 1490 */ 84, 95, 96, 97, 98, 112, 81, 101, 102, 84,
+ /* 1500 */ 112, 95, 96, 97, 98, 112, 81, 101, 102, 84,
+ /* 1510 */ 95, 96, 97, 98, 112, 81, 101, 102, 84, 112,
+ /* 1520 */ 95, 96, 97, 98, 112, 81, 101, 102, 84, 95,
+ /* 1530 */ 96, 97, 98, 112, 81, 101, 102, 84, 112, 95,
+ /* 1540 */ 96, 97, 98, 112, 112, 101, 102, 112, 95, 96,
+ /* 1550 */ 97, 98, 112, 81, 101, 102, 84, 112, 112, 112,
+ /* 1560 */ 112, 81, 112, 112, 84, 112, 112, 95, 96, 97,
+ /* 1570 */ 98, 112, 112, 101, 102, 95, 96, 97, 98, 112,
+ /* 1580 */ 81, 101, 102, 84, 112, 112, 112, 112, 81, 112,
+ /* 1590 */ 112, 84, 112, 112, 95, 96, 97, 98, 112, 112,
+ /* 1600 */ 101, 102, 95, 96, 97, 98, 112, 112, 101, 102,
+};
+#define YY_SHIFT_USE_DFLT (-46)
+#define YY_SHIFT_COUNT (138)
+#define YY_SHIFT_MIN (-45)
+#define YY_SHIFT_MAX (1076)
+static const short yy_shift_ofst[] = {
+ /* 0 */ -1, 90, 281, 425, 453, 281, 453, 453, 453, 453,
+ /* 10 */ 217, 153, 453, 453, 453, 453, 453, 453, 453, 453,
+ /* 20 */ 453, 453, 453, 453, 453, 453, 453, 453, 453, 453,
+ /* 30 */ 453, 453, 453, 453, 453, 453, 453, 453, 453, 453,
+ /* 40 */ 453, 453, 453, 453, 453, 453, 453, 453, 453, 453,
+ /* 50 */ 453, 453, 453, 453, 453, 453, 453, 453, 453, 453,
+ /* 60 */ 453, 453, 453, 453, 453, 453, 453, 453, 453, 1076,
+ /* 70 */ 259, 35, 191, 65, 259, 189, 399, 35, 35, 35,
+ /* 80 */ 35, 35, 493, -46, 1010, 282, 282, 282, -45, -45,
+ /* 90 */ -45, -45, -45, -45, -45, -45, -45, -45, -45, 79,
+ /* 100 */ -45, 79, -45, 79, -45, 261, 315, 251, 101, 242,
+ /* 110 */ 193, 92, 92, 84, 92, 96, 192, 81, 92, 96,
+ /* 120 */ 101, 134, 39, 7, 197, 105, 190, 178, 18, 152,
+ /* 130 */ 107, 40, 22, 18, 40, 22, 18, 15, 7,
+};
+#define YY_REDUCE_USE_DFLT (-70)
+#define YY_REDUCE_COUNT (83)
+#define YY_REDUCE_MIN (-69)
+#define YY_REDUCE_MAX (1507)
+static const short yy_reduce_ofst[] = {
+ /* 0 */ -62, -7, 151, 87, 25, 360, 332, 296, 274, 212,
+ /* 10 */ 839, 817, 795, 773, 751, 729, 707, 685, 663, 641,
+ /* 20 */ 619, 597, 575, 553, 531, 509, 486, 464, 442, 858,
+ /* 30 */ 899, 877, 921, 956, 979, 1017, 998, 1264, 1245, 1226,
+ /* 40 */ 1207, 1188, 1169, 1150, 1131, 1112, 1093, 1074, 1055, 1036,
+ /* 50 */ 1321, 1302, 1283, 1349, 1340, 1507, 1499, 1480, 1472, 1453,
+ /* 60 */ 1444, 1434, 1425, 1415, 1406, 1396, 1387, 1377, 1368, -64,
+ /* 70 */ 14, -69, 80, 126, 55, 254, 254, 250, 232, 210,
+ /* 80 */ 186, 184, 155, 154,
+};
+static const YYACTIONTYPE yy_default[] = {
+ /* 0 */ 354, 354, 342, 354, 332, 354, 339, 354, 354, 354,
+ /* 10 */ 354, 354, 354, 354, 354, 354, 354, 354, 354, 354,
+ /* 20 */ 354, 354, 354, 354, 354, 354, 354, 354, 354, 354,
+ /* 30 */ 354, 354, 354, 354, 354, 354, 354, 354, 354, 354,
+ /* 40 */ 354, 354, 354, 354, 354, 354, 354, 354, 354, 354,
+ /* 50 */ 354, 354, 354, 354, 354, 354, 354, 354, 354, 354,
+ /* 60 */ 354, 354, 354, 354, 354, 354, 354, 354, 354, 354,
+ /* 70 */ 348, 354, 354, 310, 354, 354, 354, 354, 354, 354,
+ /* 80 */ 354, 354, 354, 332, 306, 266, 268, 267, 282, 281,
+ /* 90 */ 280, 279, 278, 277, 276, 275, 274, 273, 269, 287,
+ /* 100 */ 272, 289, 271, 288, 270, 354, 354, 354, 256, 354,
+ /* 110 */ 354, 283, 286, 354, 285, 264, 354, 306, 284, 265,
+ /* 120 */ 255, 253, 354, 316, 354, 354, 354, 351, 259, 354,
+ /* 130 */ 354, 262, 260, 257, 263, 261, 258, 354, 354, 349,
+ /* 140 */ 353, 352, 350, 343, 347, 346, 345, 344, 233, 231,
+ /* 150 */ 237, 252, 251, 250, 249, 248, 247, 246, 245, 244,
+ /* 160 */ 243, 242, 340, 341, 338, 337, 328, 326, 325, 330,
+ /* 170 */ 324, 335, 334, 333, 331, 329, 327, 323, 290, 322,
+ /* 180 */ 321, 320, 319, 318, 317, 316, 293, 315, 314, 312,
+ /* 190 */ 292, 336, 238, 313, 311, 309, 308, 307, 305, 304,
+ /* 200 */ 303, 302, 301, 300, 299, 298, 297, 296, 295, 294,
+ /* 210 */ 291, 254, 241, 240, 239, 236, 235, 234, 230, 227,
+ /* 220 */ 232, 229, 228,
+};
+
+/* The next table maps tokens into fallback tokens. If a construct
+** like the following:
+**
+** %fallback ID X Y Z.
+**
+** appears in the grammar, then ID becomes a fallback token for X, Y,
+** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
+** but it does not parse, the type of the token is changed to ID and
+** the parse is retried before an error is thrown.
+*/
+#ifdef YYFALLBACK
+static const YYCODETYPE yyFallback[] = {
+};
+#endif /* YYFALLBACK */
+
+/* The following structure represents a single element of the
+** parser's stack. Information stored includes:
+**
+** + The state number for the parser at this level of the stack.
+**
+** + The value of the token stored at this level of the stack.
+** (In other words, the "major" token.)
+**
+** + The semantic value stored at this level of the stack. This is
+** the information used by the action routines in the grammar.
+** It is sometimes called the "minor" token.
+*/
+struct yyStackEntry {
+ YYACTIONTYPE stateno; /* The state-number */
+ YYCODETYPE major; /* The major token value. This is the code
+ ** number for the token at this stack level */
+ YYMINORTYPE minor; /* The user-supplied minor token value. This
+ ** is the value of the token */
+};
+typedef struct yyStackEntry yyStackEntry;
+
+/* The state of the parser is completely contained in an instance of
+** the following structure */
+struct yyParser {
+ int yyidx; /* Index of top element in stack */
+#ifdef YYTRACKMAXSTACKDEPTH
+ int yyidxMax; /* Maximum value of yyidx */
+#endif
+ int yyerrcnt; /* Shifts left before out of the error */
+ grn_expr_parserARG_SDECL /* A place to hold %extra_argument */
+#if YYSTACKDEPTH<=0
+ int yystksz; /* Current side of the stack */
+ yyStackEntry *yystack; /* The parser's stack */
+#else
+ yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */
+#endif
+};
+typedef struct yyParser yyParser;
+
+#ifndef NDEBUG
+#include <stdio.h>
+static FILE *yyTraceFILE = 0;
+static char *yyTracePrompt = 0;
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/*
+** Turn parser tracing on by giving a stream to which to write the trace
+** and a prompt to preface each trace message. Tracing is turned off
+** by making either argument NULL
+**
+** Inputs:
+** <ul>
+** <li> A FILE* to which trace output should be written.
+** If NULL, then tracing is turned off.
+** <li> A prefix string written at the beginning of every
+** line of trace output. If NULL, then tracing is
+** turned off.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void grn_expr_parserTrace(FILE *TraceFILE, char *zTracePrompt){
+ yyTraceFILE = TraceFILE;
+ yyTracePrompt = zTracePrompt;
+ if( yyTraceFILE==0 ) yyTracePrompt = 0;
+ else if( yyTracePrompt==0 ) yyTraceFILE = 0;
+}
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing shifts, the names of all terminals and nonterminals
+** are required. The following table supplies these names */
+static const char *const yyTokenName[] = {
+ "$", "START_OUTPUT_COLUMNS", "START_ADJUSTER", "LOGICAL_AND",
+ "LOGICAL_AND_NOT", "LOGICAL_OR", "QSTRING", "PARENL",
+ "PARENR", "RELATIVE_OP", "IDENTIFIER", "BRACEL",
+ "BRACER", "EVAL", "COMMA", "ASSIGN",
+ "STAR_ASSIGN", "SLASH_ASSIGN", "MOD_ASSIGN", "PLUS_ASSIGN",
+ "MINUS_ASSIGN", "SHIFTL_ASSIGN", "SHIFTR_ASSIGN", "SHIFTRR_ASSIGN",
+ "AND_ASSIGN", "XOR_ASSIGN", "OR_ASSIGN", "QUESTION",
+ "COLON", "BITWISE_OR", "BITWISE_XOR", "BITWISE_AND",
+ "EQUAL", "NOT_EQUAL", "LESS", "GREATER",
+ "LESS_EQUAL", "GREATER_EQUAL", "IN", "MATCH",
+ "NEAR", "NEAR2", "SIMILAR", "TERM_EXTRACT",
+ "LCP", "PREFIX", "SUFFIX", "SHIFTL",
+ "SHIFTR", "SHIFTRR", "PLUS", "MINUS",
+ "STAR", "SLASH", "MOD", "DELETE",
+ "INCR", "DECR", "NOT", "BITWISE_NOT",
+ "ADJUST", "EXACT", "PARTIAL", "UNSPLIT",
+ "DECIMAL", "HEX_INTEGER", "STRING", "BOOLEAN",
+ "NULL", "BRACKETL", "BRACKETR", "DOT",
+ "NONEXISTENT_COLUMN", "error", "suppress_unused_variable_warning", "input",
+ "query", "expression", "output_columns", "adjuster",
+ "query_element", "primary_expression", "assignment_expression", "conditional_expression",
+ "lefthand_side_expression", "logical_or_expression", "logical_and_expression", "bitwise_or_expression",
+ "bitwise_xor_expression", "bitwise_and_expression", "equality_expression", "relational_expression",
+ "shift_expression", "additive_expression", "multiplicative_expression", "unary_expression",
+ "postfix_expression", "call_expression", "member_expression", "arguments",
+ "member_expression_part", "object_literal", "array_literal", "elision",
+ "element_list", "property_name_and_value_list", "property_name_and_value", "property_name",
+ "argument_list", "output_column", "adjust_expression", "adjust_match_expression",
+};
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing reduce actions, the names of all rules are required.
+*/
+static const char *const yyRuleName[] = {
+ /* 0 */ "input ::= query",
+ /* 1 */ "input ::= expression",
+ /* 2 */ "input ::= START_OUTPUT_COLUMNS output_columns",
+ /* 3 */ "input ::= START_ADJUSTER adjuster",
+ /* 4 */ "query ::= query_element",
+ /* 5 */ "query ::= query query_element",
+ /* 6 */ "query ::= query LOGICAL_AND query_element",
+ /* 7 */ "query ::= query LOGICAL_AND_NOT query_element",
+ /* 8 */ "query ::= query LOGICAL_OR query_element",
+ /* 9 */ "query_element ::= QSTRING",
+ /* 10 */ "query_element ::= PARENL query PARENR",
+ /* 11 */ "query_element ::= RELATIVE_OP query_element",
+ /* 12 */ "query_element ::= IDENTIFIER RELATIVE_OP query_element",
+ /* 13 */ "query_element ::= BRACEL expression BRACER",
+ /* 14 */ "query_element ::= EVAL primary_expression",
+ /* 15 */ "expression ::= assignment_expression",
+ /* 16 */ "expression ::= expression COMMA assignment_expression",
+ /* 17 */ "assignment_expression ::= conditional_expression",
+ /* 18 */ "assignment_expression ::= lefthand_side_expression ASSIGN assignment_expression",
+ /* 19 */ "assignment_expression ::= lefthand_side_expression STAR_ASSIGN assignment_expression",
+ /* 20 */ "assignment_expression ::= lefthand_side_expression SLASH_ASSIGN assignment_expression",
+ /* 21 */ "assignment_expression ::= lefthand_side_expression MOD_ASSIGN assignment_expression",
+ /* 22 */ "assignment_expression ::= lefthand_side_expression PLUS_ASSIGN assignment_expression",
+ /* 23 */ "assignment_expression ::= lefthand_side_expression MINUS_ASSIGN assignment_expression",
+ /* 24 */ "assignment_expression ::= lefthand_side_expression SHIFTL_ASSIGN assignment_expression",
+ /* 25 */ "assignment_expression ::= lefthand_side_expression SHIFTR_ASSIGN assignment_expression",
+ /* 26 */ "assignment_expression ::= lefthand_side_expression SHIFTRR_ASSIGN assignment_expression",
+ /* 27 */ "assignment_expression ::= lefthand_side_expression AND_ASSIGN assignment_expression",
+ /* 28 */ "assignment_expression ::= lefthand_side_expression XOR_ASSIGN assignment_expression",
+ /* 29 */ "assignment_expression ::= lefthand_side_expression OR_ASSIGN assignment_expression",
+ /* 30 */ "conditional_expression ::= logical_or_expression",
+ /* 31 */ "conditional_expression ::= logical_or_expression QUESTION assignment_expression COLON assignment_expression",
+ /* 32 */ "logical_or_expression ::= logical_and_expression",
+ /* 33 */ "logical_or_expression ::= logical_or_expression LOGICAL_OR logical_and_expression",
+ /* 34 */ "logical_and_expression ::= bitwise_or_expression",
+ /* 35 */ "logical_and_expression ::= logical_and_expression LOGICAL_AND bitwise_or_expression",
+ /* 36 */ "logical_and_expression ::= logical_and_expression LOGICAL_AND_NOT bitwise_or_expression",
+ /* 37 */ "bitwise_or_expression ::= bitwise_xor_expression",
+ /* 38 */ "bitwise_or_expression ::= bitwise_or_expression BITWISE_OR bitwise_xor_expression",
+ /* 39 */ "bitwise_xor_expression ::= bitwise_and_expression",
+ /* 40 */ "bitwise_xor_expression ::= bitwise_xor_expression BITWISE_XOR bitwise_and_expression",
+ /* 41 */ "bitwise_and_expression ::= equality_expression",
+ /* 42 */ "bitwise_and_expression ::= bitwise_and_expression BITWISE_AND equality_expression",
+ /* 43 */ "equality_expression ::= relational_expression",
+ /* 44 */ "equality_expression ::= equality_expression EQUAL relational_expression",
+ /* 45 */ "equality_expression ::= equality_expression NOT_EQUAL relational_expression",
+ /* 46 */ "relational_expression ::= shift_expression",
+ /* 47 */ "relational_expression ::= relational_expression LESS shift_expression",
+ /* 48 */ "relational_expression ::= relational_expression GREATER shift_expression",
+ /* 49 */ "relational_expression ::= relational_expression LESS_EQUAL shift_expression",
+ /* 50 */ "relational_expression ::= relational_expression GREATER_EQUAL shift_expression",
+ /* 51 */ "relational_expression ::= relational_expression IN shift_expression",
+ /* 52 */ "relational_expression ::= relational_expression MATCH shift_expression",
+ /* 53 */ "relational_expression ::= relational_expression NEAR shift_expression",
+ /* 54 */ "relational_expression ::= relational_expression NEAR2 shift_expression",
+ /* 55 */ "relational_expression ::= relational_expression SIMILAR shift_expression",
+ /* 56 */ "relational_expression ::= relational_expression TERM_EXTRACT shift_expression",
+ /* 57 */ "relational_expression ::= relational_expression LCP shift_expression",
+ /* 58 */ "relational_expression ::= relational_expression PREFIX shift_expression",
+ /* 59 */ "relational_expression ::= relational_expression SUFFIX shift_expression",
+ /* 60 */ "shift_expression ::= additive_expression",
+ /* 61 */ "shift_expression ::= shift_expression SHIFTL additive_expression",
+ /* 62 */ "shift_expression ::= shift_expression SHIFTR additive_expression",
+ /* 63 */ "shift_expression ::= shift_expression SHIFTRR additive_expression",
+ /* 64 */ "additive_expression ::= multiplicative_expression",
+ /* 65 */ "additive_expression ::= additive_expression PLUS multiplicative_expression",
+ /* 66 */ "additive_expression ::= additive_expression MINUS multiplicative_expression",
+ /* 67 */ "multiplicative_expression ::= unary_expression",
+ /* 68 */ "multiplicative_expression ::= multiplicative_expression STAR unary_expression",
+ /* 69 */ "multiplicative_expression ::= multiplicative_expression SLASH unary_expression",
+ /* 70 */ "multiplicative_expression ::= multiplicative_expression MOD unary_expression",
+ /* 71 */ "unary_expression ::= postfix_expression",
+ /* 72 */ "unary_expression ::= DELETE unary_expression",
+ /* 73 */ "unary_expression ::= INCR unary_expression",
+ /* 74 */ "unary_expression ::= DECR unary_expression",
+ /* 75 */ "unary_expression ::= PLUS unary_expression",
+ /* 76 */ "unary_expression ::= MINUS unary_expression",
+ /* 77 */ "unary_expression ::= NOT unary_expression",
+ /* 78 */ "unary_expression ::= BITWISE_NOT unary_expression",
+ /* 79 */ "unary_expression ::= ADJUST unary_expression",
+ /* 80 */ "unary_expression ::= EXACT unary_expression",
+ /* 81 */ "unary_expression ::= PARTIAL unary_expression",
+ /* 82 */ "unary_expression ::= UNSPLIT unary_expression",
+ /* 83 */ "postfix_expression ::= lefthand_side_expression",
+ /* 84 */ "postfix_expression ::= lefthand_side_expression INCR",
+ /* 85 */ "postfix_expression ::= lefthand_side_expression DECR",
+ /* 86 */ "lefthand_side_expression ::= call_expression",
+ /* 87 */ "lefthand_side_expression ::= member_expression",
+ /* 88 */ "call_expression ::= member_expression arguments",
+ /* 89 */ "member_expression ::= primary_expression",
+ /* 90 */ "member_expression ::= member_expression member_expression_part",
+ /* 91 */ "primary_expression ::= object_literal",
+ /* 92 */ "primary_expression ::= PARENL expression PARENR",
+ /* 93 */ "primary_expression ::= IDENTIFIER",
+ /* 94 */ "primary_expression ::= array_literal",
+ /* 95 */ "primary_expression ::= DECIMAL",
+ /* 96 */ "primary_expression ::= HEX_INTEGER",
+ /* 97 */ "primary_expression ::= STRING",
+ /* 98 */ "primary_expression ::= BOOLEAN",
+ /* 99 */ "primary_expression ::= NULL",
+ /* 100 */ "array_literal ::= BRACKETL elision BRACKETR",
+ /* 101 */ "array_literal ::= BRACKETL element_list elision BRACKETR",
+ /* 102 */ "array_literal ::= BRACKETL element_list BRACKETR",
+ /* 103 */ "elision ::= COMMA",
+ /* 104 */ "elision ::= elision COMMA",
+ /* 105 */ "element_list ::= assignment_expression",
+ /* 106 */ "element_list ::= elision assignment_expression",
+ /* 107 */ "element_list ::= element_list elision assignment_expression",
+ /* 108 */ "object_literal ::= BRACEL property_name_and_value_list BRACER",
+ /* 109 */ "property_name_and_value_list ::=",
+ /* 110 */ "property_name_and_value_list ::= property_name_and_value_list COMMA property_name_and_value",
+ /* 111 */ "property_name_and_value ::= property_name COLON assignment_expression",
+ /* 112 */ "property_name ::= IDENTIFIER|STRING|DECIMAL",
+ /* 113 */ "member_expression_part ::= BRACKETL expression BRACKETR",
+ /* 114 */ "member_expression_part ::= DOT IDENTIFIER",
+ /* 115 */ "arguments ::= PARENL argument_list PARENR",
+ /* 116 */ "argument_list ::=",
+ /* 117 */ "argument_list ::= assignment_expression",
+ /* 118 */ "argument_list ::= argument_list COMMA assignment_expression",
+ /* 119 */ "output_columns ::=",
+ /* 120 */ "output_columns ::= output_column",
+ /* 121 */ "output_columns ::= output_columns COMMA output_column",
+ /* 122 */ "output_column ::= STAR",
+ /* 123 */ "output_column ::= NONEXISTENT_COLUMN",
+ /* 124 */ "output_column ::= assignment_expression",
+ /* 125 */ "adjuster ::=",
+ /* 126 */ "adjuster ::= adjust_expression",
+ /* 127 */ "adjuster ::= adjuster PLUS adjust_expression",
+ /* 128 */ "adjust_expression ::= adjust_match_expression",
+ /* 129 */ "adjust_expression ::= adjust_match_expression STAR DECIMAL",
+ /* 130 */ "adjust_match_expression ::= IDENTIFIER MATCH STRING",
+};
+#endif /* NDEBUG */
+
+
+#if YYSTACKDEPTH<=0
+/*
+** Try to increase the size of the parser stack.
+*/
+static void yyGrowStack(yyParser *p){
+ int newSize;
+ yyStackEntry *pNew;
+
+ newSize = p->yystksz*2 + 100;
+ pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
+ if( pNew ){
+ p->yystack = pNew;
+ p->yystksz = newSize;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sStack grows to %d entries!\n",
+ yyTracePrompt, p->yystksz);
+ }
+#endif
+ }
+}
+#endif
+
+/*
+** This function allocates a new parser.
+** The only argument is a pointer to a function which works like
+** malloc.
+**
+** Inputs:
+** A pointer to the function used to allocate memory.
+**
+** Outputs:
+** A pointer to a parser. This pointer is used in subsequent calls
+** to grn_expr_parser and grn_expr_parserFree.
+*/
+void *grn_expr_parserAlloc(void *(*mallocProc)(size_t)){
+ yyParser *pParser;
+ pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) );
+ if( pParser ){
+ pParser->yyidx = -1;
+#ifdef YYTRACKMAXSTACKDEPTH
+ pParser->yyidxMax = 0;
+#endif
+#if YYSTACKDEPTH<=0
+ pParser->yystack = NULL;
+ pParser->yystksz = 0;
+ yyGrowStack(pParser);
+#endif
+ }
+ return pParser;
+}
+
+/* The following function deletes the value associated with a
+** symbol. The symbol can be either a terminal or nonterminal.
+** "yymajor" is the symbol code, and "yypminor" is a pointer to
+** the value.
+*/
+static void yy_destructor(
+ yyParser *yypParser, /* The parser */
+ YYCODETYPE yymajor, /* Type code for object to destroy */
+ YYMINORTYPE *yypminor /* The object to be destroyed */
+){
+ grn_expr_parserARG_FETCH;
+ switch( yymajor ){
+ /* Here is inserted the actions which take place when a
+ ** terminal or non-terminal is destroyed. This can happen
+ ** when the symbol is popped from the stack during a
+ ** reduce or during error processing or when a parser is
+ ** being destroyed before it is finished parsing.
+ **
+ ** Note: during a reduce, the only symbols destroyed are those
+ ** which appear on the RHS of the rule, but which are not used
+ ** inside the C code.
+ */
+ case 74: /* suppress_unused_variable_warning */
+{
+#line 10 "ecmascript.lemon"
+
+ (void)efsi;
+
+#line 875 "ecmascript.c"
+}
+ break;
+ default: break; /* If no destructor action specified: do nothing */
+ }
+}
+
+/*
+** Pop the parser's stack once.
+**
+** If there is a destructor routine associated with the token which
+** is popped from the stack, then call it.
+**
+** Return the major token number for the symbol popped.
+*/
+static int yy_pop_parser_stack(yyParser *pParser){
+ YYCODETYPE yymajor;
+ yyStackEntry *yytos = &pParser->yystack[pParser->yyidx];
+
+ if( pParser->yyidx<0 ) return 0;
+#ifndef NDEBUG
+ if( yyTraceFILE && pParser->yyidx>=0 ){
+ fprintf(yyTraceFILE,"%sPopping %s\n",
+ yyTracePrompt,
+ yyTokenName[yytos->major]);
+ }
+#endif
+ yymajor = yytos->major;
+ yy_destructor(pParser, yymajor, &yytos->minor);
+ pParser->yyidx--;
+ return yymajor;
+}
+
+/*
+** Deallocate and destroy a parser. Destructors are all called for
+** all stack elements before shutting the parser down.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser. This should be a pointer
+** obtained from grn_expr_parserAlloc.
+** <li> A pointer to a function used to reclaim memory obtained
+** from malloc.
+** </ul>
+*/
+void grn_expr_parserFree(
+ void *p, /* The parser to be deleted */
+ void (*freeProc)(void*) /* Function used to reclaim memory */
+){
+ yyParser *pParser = (yyParser*)p;
+ if( pParser==0 ) return;
+ while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser);
+#if YYSTACKDEPTH<=0
+ free(pParser->yystack);
+#endif
+ (*freeProc)((void*)pParser);
+}
+
+/*
+** Return the peak depth of the stack for a parser.
+*/
+#ifdef YYTRACKMAXSTACKDEPTH
+int grn_expr_parserStackPeak(void *p){
+ yyParser *pParser = (yyParser*)p;
+ return pParser->yyidxMax;
+}
+#endif
+
+/*
+** Find the appropriate action for a parser given the terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead. If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_shift_action(
+ yyParser *pParser, /* The parser */
+ YYCODETYPE iLookAhead /* The look-ahead token */
+){
+ int i;
+ int stateno = pParser->yystack[pParser->yyidx].stateno;
+
+ if( stateno>YY_SHIFT_COUNT
+ || (i = yy_shift_ofst[stateno])==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 ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
+ yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
+ }
+#endif
+ return yy_find_shift_action(pParser, iFallback);
+ }
+#endif
+#ifdef YYWILDCARD
+ {
+ int j = i - iLookAhead + YYWILDCARD;
+ if(
+#if YY_SHIFT_MIN+YYWILDCARD<0
+ j>=0 &&
+#endif
+#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
+ j<YY_ACTTAB_COUNT &&
+#endif
+ yy_lookahead[j]==YYWILDCARD
+ ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
+ yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]);
+ }
+#endif /* NDEBUG */
+ return yy_action[j];
+ }
+ }
+#endif /* YYWILDCARD */
+ }
+ return yy_default[stateno];
+ }else{
+ return yy_action[i];
+ }
+}
+
+/*
+** Find the appropriate action for a parser given the non-terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead. If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_reduce_action(
+ int stateno, /* Current state number */
+ YYCODETYPE iLookAhead /* The look-ahead token */
+){
+ int i;
+#ifdef YYERRORSYMBOL
+ if( stateno>YY_REDUCE_COUNT ){
+ return yy_default[stateno];
+ }
+#else
+ assert( stateno<=YY_REDUCE_COUNT );
+#endif
+ i = yy_reduce_ofst[stateno];
+ assert( i!=YY_REDUCE_USE_DFLT );
+ assert( iLookAhead!=YYNOCODE );
+ i += iLookAhead;
+#ifdef YYERRORSYMBOL
+ if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
+ return yy_default[stateno];
+ }
+#else
+ assert( i>=0 && i<YY_ACTTAB_COUNT );
+ assert( yy_lookahead[i]==iLookAhead );
+#endif
+ return yy_action[i];
+}
+
+/*
+** The following routine is called if the stack overflows.
+*/
+static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
+ grn_expr_parserARG_FETCH;
+ yypParser->yyidx--;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will execute if the parser
+ ** stack every overflows */
+ grn_expr_parserARG_STORE; /* Suppress warning about unused %extra_argument var */
+}
+
+/*
+** Perform a shift action.
+*/
+static void yy_shift(
+ yyParser *yypParser, /* The parser to be shifted */
+ int yyNewState, /* The new state to shift in */
+ int yyMajor, /* The major token to shift in */
+ YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */
+){
+ yyStackEntry *yytos;
+ yypParser->yyidx++;
+#ifdef YYTRACKMAXSTACKDEPTH
+ if( yypParser->yyidx>yypParser->yyidxMax ){
+ yypParser->yyidxMax = yypParser->yyidx;
+ }
+#endif
+#if YYSTACKDEPTH>0
+ if( yypParser->yyidx>=YYSTACKDEPTH ){
+ yyStackOverflow(yypParser, yypMinor);
+ return;
+ }
+#else
+ if( yypParser->yyidx>=yypParser->yystksz ){
+ yyGrowStack(yypParser);
+ if( yypParser->yyidx>=yypParser->yystksz ){
+ yyStackOverflow(yypParser, yypMinor);
+ return;
+ }
+ }
+#endif
+ yytos = &yypParser->yystack[yypParser->yyidx];
+ yytos->stateno = (YYACTIONTYPE)yyNewState;
+ yytos->major = (YYCODETYPE)yyMajor;
+ yytos->minor = *yypMinor;
+#ifndef NDEBUG
+ if( yyTraceFILE && yypParser->yyidx>0 ){
+ int i;
+ fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
+ fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
+ for(i=1; i<=yypParser->yyidx; i++)
+ fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
+ fprintf(yyTraceFILE,"\n");
+ }
+#endif
+}
+
+/* The following table contains information about every rule that
+** is used during the reduce.
+*/
+static const struct {
+ YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
+ unsigned char nrhs; /* Number of right-hand side symbols in the rule */
+} yyRuleInfo[] = {
+ { 75, 1 },
+ { 75, 1 },
+ { 75, 2 },
+ { 75, 2 },
+ { 76, 1 },
+ { 76, 2 },
+ { 76, 3 },
+ { 76, 3 },
+ { 76, 3 },
+ { 80, 1 },
+ { 80, 3 },
+ { 80, 2 },
+ { 80, 3 },
+ { 80, 3 },
+ { 80, 2 },
+ { 77, 1 },
+ { 77, 3 },
+ { 82, 1 },
+ { 82, 3 },
+ { 82, 3 },
+ { 82, 3 },
+ { 82, 3 },
+ { 82, 3 },
+ { 82, 3 },
+ { 82, 3 },
+ { 82, 3 },
+ { 82, 3 },
+ { 82, 3 },
+ { 82, 3 },
+ { 82, 3 },
+ { 83, 1 },
+ { 83, 5 },
+ { 85, 1 },
+ { 85, 3 },
+ { 86, 1 },
+ { 86, 3 },
+ { 86, 3 },
+ { 87, 1 },
+ { 87, 3 },
+ { 88, 1 },
+ { 88, 3 },
+ { 89, 1 },
+ { 89, 3 },
+ { 90, 1 },
+ { 90, 3 },
+ { 90, 3 },
+ { 91, 1 },
+ { 91, 3 },
+ { 91, 3 },
+ { 91, 3 },
+ { 91, 3 },
+ { 91, 3 },
+ { 91, 3 },
+ { 91, 3 },
+ { 91, 3 },
+ { 91, 3 },
+ { 91, 3 },
+ { 91, 3 },
+ { 91, 3 },
+ { 91, 3 },
+ { 92, 1 },
+ { 92, 3 },
+ { 92, 3 },
+ { 92, 3 },
+ { 93, 1 },
+ { 93, 3 },
+ { 93, 3 },
+ { 94, 1 },
+ { 94, 3 },
+ { 94, 3 },
+ { 94, 3 },
+ { 95, 1 },
+ { 95, 2 },
+ { 95, 2 },
+ { 95, 2 },
+ { 95, 2 },
+ { 95, 2 },
+ { 95, 2 },
+ { 95, 2 },
+ { 95, 2 },
+ { 95, 2 },
+ { 95, 2 },
+ { 95, 2 },
+ { 96, 1 },
+ { 96, 2 },
+ { 96, 2 },
+ { 84, 1 },
+ { 84, 1 },
+ { 97, 2 },
+ { 98, 1 },
+ { 98, 2 },
+ { 81, 1 },
+ { 81, 3 },
+ { 81, 1 },
+ { 81, 1 },
+ { 81, 1 },
+ { 81, 1 },
+ { 81, 1 },
+ { 81, 1 },
+ { 81, 1 },
+ { 102, 3 },
+ { 102, 4 },
+ { 102, 3 },
+ { 103, 1 },
+ { 103, 2 },
+ { 104, 1 },
+ { 104, 2 },
+ { 104, 3 },
+ { 101, 3 },
+ { 105, 0 },
+ { 105, 3 },
+ { 106, 3 },
+ { 107, 1 },
+ { 100, 3 },
+ { 100, 2 },
+ { 99, 3 },
+ { 108, 0 },
+ { 108, 1 },
+ { 108, 3 },
+ { 78, 0 },
+ { 78, 1 },
+ { 78, 3 },
+ { 109, 1 },
+ { 109, 1 },
+ { 109, 1 },
+ { 79, 0 },
+ { 79, 1 },
+ { 79, 3 },
+ { 110, 1 },
+ { 110, 3 },
+ { 111, 3 },
+};
+
+static void yy_accept(yyParser*); /* Forward Declaration */
+
+/*
+** Perform a reduce action and the shift that must immediately
+** follow the reduce.
+*/
+static void yy_reduce(
+ yyParser *yypParser, /* The parser */
+ int yyruleno /* Number of the rule by which to reduce */
+){
+ int yygoto; /* The next state */
+ int yyact; /* The next action */
+ YYMINORTYPE yygotominor; /* The LHS of the rule reduced */
+ yyStackEntry *yymsp; /* The top of the parser's stack */
+ int yysize; /* Amount to pop the stack */
+ grn_expr_parserARG_FETCH;
+ yymsp = &yypParser->yystack[yypParser->yyidx];
+#ifndef NDEBUG
+ if( yyTraceFILE && yyruleno>=0
+ && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
+ fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt,
+ yyRuleName[yyruleno]);
+ }
+#endif /* NDEBUG */
+
+ /* Silence complaints from purify about yygotominor being uninitialized
+ ** in some cases when it is copied into the stack after the following
+ ** switch. yygotominor is uninitialized when a rule reduces that does
+ ** not set the value of its left-hand side nonterminal. Leaving the
+ ** value of the nonterminal uninitialized is utterly harmless as long
+ ** as the value is never used. So really the only thing this code
+ ** accomplishes is to quieten purify.
+ **
+ ** 2007-01-16: The wireshark project (www.wireshark.org) reports that
+ ** without this code, their parser segfaults. I'm not sure what there
+ ** parser is doing to make this happen. This is the second bug report
+ ** from wireshark this week. Clearly they are stressing Lemon in ways
+ ** that it has not been previously stressed... (SQLite ticket #2172)
+ */
+ /*memset(&yygotominor, 0, sizeof(yygotominor));*/
+ yygotominor = yyzerominor;
+
+
+ switch( yyruleno ){
+ /* Beginning here are the reduction cases. A typical example
+ ** follows:
+ ** case 0:
+ ** #line <lineno> <grammarfile>
+ ** { ... } // User supplied code
+ ** #line <lineno> <thisfile>
+ ** break;
+ */
+ case 5: /* query ::= query query_element */
+#line 45 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, grn_int32_value_at(&efsi->op_stack, -1), 2);
+}
+#line 1303 "ecmascript.c"
+ break;
+ case 6: /* query ::= query LOGICAL_AND query_element */
+ case 35: /* logical_and_expression ::= logical_and_expression LOGICAL_AND bitwise_or_expression */ yytestcase(yyruleno==35);
+#line 48 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_AND, 2);
+}
+#line 1311 "ecmascript.c"
+ break;
+ case 7: /* query ::= query LOGICAL_AND_NOT query_element */
+ case 36: /* logical_and_expression ::= logical_and_expression LOGICAL_AND_NOT bitwise_or_expression */ yytestcase(yyruleno==36);
+#line 51 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_AND_NOT, 2);
+}
+#line 1319 "ecmascript.c"
+ break;
+ case 8: /* query ::= query LOGICAL_OR query_element */
+ case 33: /* logical_or_expression ::= logical_or_expression LOGICAL_OR logical_and_expression */ yytestcase(yyruleno==33);
+#line 54 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_OR, 2);
+}
+#line 1327 "ecmascript.c"
+ break;
+ case 11: /* query_element ::= RELATIVE_OP query_element */
+#line 61 "ecmascript.lemon"
+{
+ int mode;
+ GRN_INT32_POP(&efsi->mode_stack, mode);
+}
+#line 1335 "ecmascript.c"
+ break;
+ case 12: /* query_element ::= IDENTIFIER RELATIVE_OP query_element */
+#line 65 "ecmascript.lemon"
+{
+ int mode;
+ grn_obj *c;
+ GRN_PTR_POP(&efsi->column_stack, c);
+ GRN_INT32_POP(&efsi->mode_stack, mode);
+ switch (mode) {
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ {
+ int max_interval;
+ GRN_INT32_POP(&efsi->max_interval_stack, max_interval);
+ }
+ break;
+ default :
+ break;
+ }
+}
+#line 1356 "ecmascript.c"
+ break;
+ case 13: /* query_element ::= BRACEL expression BRACER */
+ case 14: /* query_element ::= EVAL primary_expression */ yytestcase(yyruleno==14);
+#line 82 "ecmascript.lemon"
+{
+ efsi->flags = efsi->default_flags;
+}
+#line 1364 "ecmascript.c"
+ break;
+ case 16: /* expression ::= expression COMMA assignment_expression */
+#line 90 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_COMMA, 2);
+}
+#line 1371 "ecmascript.c"
+ break;
+ case 18: /* assignment_expression ::= lefthand_side_expression ASSIGN assignment_expression */
+#line 95 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_ASSIGN, 2);
+}
+#line 1378 "ecmascript.c"
+ break;
+ case 19: /* assignment_expression ::= lefthand_side_expression STAR_ASSIGN assignment_expression */
+#line 98 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_STAR_ASSIGN, 2);
+}
+#line 1385 "ecmascript.c"
+ break;
+ case 20: /* assignment_expression ::= lefthand_side_expression SLASH_ASSIGN assignment_expression */
+#line 101 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SLASH_ASSIGN, 2);
+}
+#line 1392 "ecmascript.c"
+ break;
+ case 21: /* assignment_expression ::= lefthand_side_expression MOD_ASSIGN assignment_expression */
+#line 104 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MOD_ASSIGN, 2);
+}
+#line 1399 "ecmascript.c"
+ break;
+ case 22: /* assignment_expression ::= lefthand_side_expression PLUS_ASSIGN assignment_expression */
+#line 107 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PLUS_ASSIGN, 2);
+}
+#line 1406 "ecmascript.c"
+ break;
+ case 23: /* assignment_expression ::= lefthand_side_expression MINUS_ASSIGN assignment_expression */
+#line 110 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MINUS_ASSIGN, 2);
+}
+#line 1413 "ecmascript.c"
+ break;
+ case 24: /* assignment_expression ::= lefthand_side_expression SHIFTL_ASSIGN assignment_expression */
+#line 113 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTL_ASSIGN, 2);
+}
+#line 1420 "ecmascript.c"
+ break;
+ case 25: /* assignment_expression ::= lefthand_side_expression SHIFTR_ASSIGN assignment_expression */
+#line 116 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTR_ASSIGN, 2);
+}
+#line 1427 "ecmascript.c"
+ break;
+ case 26: /* assignment_expression ::= lefthand_side_expression SHIFTRR_ASSIGN assignment_expression */
+#line 119 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTRR_ASSIGN, 2);
+}
+#line 1434 "ecmascript.c"
+ break;
+ case 27: /* assignment_expression ::= lefthand_side_expression AND_ASSIGN assignment_expression */
+#line 122 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_AND_ASSIGN, 2);
+}
+#line 1441 "ecmascript.c"
+ break;
+ case 28: /* assignment_expression ::= lefthand_side_expression XOR_ASSIGN assignment_expression */
+#line 125 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_XOR_ASSIGN, 2);
+}
+#line 1448 "ecmascript.c"
+ break;
+ case 29: /* assignment_expression ::= lefthand_side_expression OR_ASSIGN assignment_expression */
+#line 128 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_OR_ASSIGN, 2);
+}
+#line 1455 "ecmascript.c"
+ break;
+ case 31: /* conditional_expression ::= logical_or_expression QUESTION assignment_expression COLON assignment_expression */
+#line 133 "ecmascript.lemon"
+{
+ grn_expr *e = (grn_expr *)efsi->e;
+ e->codes[yymsp[-3].minor.yy0].nargs = yymsp[-1].minor.yy0 - yymsp[-3].minor.yy0;
+ e->codes[yymsp[-1].minor.yy0].nargs = e->codes_curr - yymsp[-1].minor.yy0 - 1;
+}
+#line 1464 "ecmascript.c"
+ break;
+ case 38: /* bitwise_or_expression ::= bitwise_or_expression BITWISE_OR bitwise_xor_expression */
+#line 153 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_BITWISE_OR, 2);
+}
+#line 1471 "ecmascript.c"
+ break;
+ case 40: /* bitwise_xor_expression ::= bitwise_xor_expression BITWISE_XOR bitwise_and_expression */
+#line 158 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_BITWISE_XOR, 2);
+}
+#line 1478 "ecmascript.c"
+ break;
+ case 42: /* bitwise_and_expression ::= bitwise_and_expression BITWISE_AND equality_expression */
+#line 163 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_BITWISE_AND, 2);
+}
+#line 1485 "ecmascript.c"
+ break;
+ case 44: /* equality_expression ::= equality_expression EQUAL relational_expression */
+#line 168 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_EQUAL, 2);
+}
+#line 1492 "ecmascript.c"
+ break;
+ case 45: /* equality_expression ::= equality_expression NOT_EQUAL relational_expression */
+#line 171 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NOT_EQUAL, 2);
+}
+#line 1499 "ecmascript.c"
+ break;
+ case 47: /* relational_expression ::= relational_expression LESS shift_expression */
+#line 176 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_LESS, 2);
+}
+#line 1506 "ecmascript.c"
+ break;
+ case 48: /* relational_expression ::= relational_expression GREATER shift_expression */
+#line 179 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_GREATER, 2);
+}
+#line 1513 "ecmascript.c"
+ break;
+ case 49: /* relational_expression ::= relational_expression LESS_EQUAL shift_expression */
+#line 182 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_LESS_EQUAL, 2);
+}
+#line 1520 "ecmascript.c"
+ break;
+ case 50: /* relational_expression ::= relational_expression GREATER_EQUAL shift_expression */
+#line 185 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_GREATER_EQUAL, 2);
+}
+#line 1527 "ecmascript.c"
+ break;
+ case 51: /* relational_expression ::= relational_expression IN shift_expression */
+#line 188 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_IN, 2);
+}
+#line 1534 "ecmascript.c"
+ break;
+ case 52: /* relational_expression ::= relational_expression MATCH shift_expression */
+ case 130: /* adjust_match_expression ::= IDENTIFIER MATCH STRING */ yytestcase(yyruleno==130);
+#line 191 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MATCH, 2);
+}
+#line 1542 "ecmascript.c"
+ break;
+ case 53: /* relational_expression ::= relational_expression NEAR shift_expression */
+#line 194 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NEAR, 2);
+}
+#line 1549 "ecmascript.c"
+ break;
+ case 54: /* relational_expression ::= relational_expression NEAR2 shift_expression */
+#line 197 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NEAR2, 2);
+}
+#line 1556 "ecmascript.c"
+ break;
+ case 55: /* relational_expression ::= relational_expression SIMILAR shift_expression */
+#line 200 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SIMILAR, 2);
+}
+#line 1563 "ecmascript.c"
+ break;
+ case 56: /* relational_expression ::= relational_expression TERM_EXTRACT shift_expression */
+#line 203 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_TERM_EXTRACT, 2);
+}
+#line 1570 "ecmascript.c"
+ break;
+ case 57: /* relational_expression ::= relational_expression LCP shift_expression */
+#line 206 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_LCP, 2);
+}
+#line 1577 "ecmascript.c"
+ break;
+ case 58: /* relational_expression ::= relational_expression PREFIX shift_expression */
+#line 209 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PREFIX, 2);
+}
+#line 1584 "ecmascript.c"
+ break;
+ case 59: /* relational_expression ::= relational_expression SUFFIX shift_expression */
+#line 212 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SUFFIX, 2);
+}
+#line 1591 "ecmascript.c"
+ break;
+ case 61: /* shift_expression ::= shift_expression SHIFTL additive_expression */
+#line 217 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTL, 2);
+}
+#line 1598 "ecmascript.c"
+ break;
+ case 62: /* shift_expression ::= shift_expression SHIFTR additive_expression */
+#line 220 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTR, 2);
+}
+#line 1605 "ecmascript.c"
+ break;
+ case 63: /* shift_expression ::= shift_expression SHIFTRR additive_expression */
+#line 223 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTRR, 2);
+}
+#line 1612 "ecmascript.c"
+ break;
+ case 65: /* additive_expression ::= additive_expression PLUS multiplicative_expression */
+ case 127: /* adjuster ::= adjuster PLUS adjust_expression */ yytestcase(yyruleno==127);
+#line 228 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PLUS, 2);
+}
+#line 1620 "ecmascript.c"
+ break;
+ case 66: /* additive_expression ::= additive_expression MINUS multiplicative_expression */
+#line 231 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MINUS, 2);
+}
+#line 1627 "ecmascript.c"
+ break;
+ case 68: /* multiplicative_expression ::= multiplicative_expression STAR unary_expression */
+ case 129: /* adjust_expression ::= adjust_match_expression STAR DECIMAL */ yytestcase(yyruleno==129);
+#line 236 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_STAR, 2);
+}
+#line 1635 "ecmascript.c"
+ break;
+ case 69: /* multiplicative_expression ::= multiplicative_expression SLASH unary_expression */
+#line 239 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SLASH, 2);
+}
+#line 1642 "ecmascript.c"
+ break;
+ case 70: /* multiplicative_expression ::= multiplicative_expression MOD unary_expression */
+#line 242 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MOD, 2);
+}
+#line 1649 "ecmascript.c"
+ break;
+ case 72: /* unary_expression ::= DELETE unary_expression */
+#line 247 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_DELETE, 1);
+}
+#line 1656 "ecmascript.c"
+ break;
+ case 73: /* unary_expression ::= INCR unary_expression */
+#line 250 "ecmascript.lemon"
+{
+ grn_ctx *ctx = efsi->ctx;
+ grn_expr *e = (grn_expr *)(efsi->e);
+ grn_expr_dfi *dfi_;
+ unsigned int const_p;
+
+ DFI_POP(e, dfi_);
+ const_p = CONSTP(dfi_->code->value);
+ DFI_PUT(e, dfi_->type, dfi_->domain, dfi_->code);
+ if (const_p) {
+ ERR(GRN_SYNTAX_ERROR,
+ "constant can't be incremented (%.*s)",
+ (int)(efsi->str_end - efsi->str), efsi->str);
+ } else {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_INCR, 1);
+ }
+}
+#line 1677 "ecmascript.c"
+ break;
+ case 74: /* unary_expression ::= DECR unary_expression */
+#line 267 "ecmascript.lemon"
+{
+ grn_ctx *ctx = efsi->ctx;
+ grn_expr *e = (grn_expr *)(efsi->e);
+ grn_expr_dfi *dfi_;
+ unsigned int const_p;
+
+ DFI_POP(e, dfi_);
+ const_p = CONSTP(dfi_->code->value);
+ DFI_PUT(e, dfi_->type, dfi_->domain, dfi_->code);
+ if (const_p) {
+ ERR(GRN_SYNTAX_ERROR,
+ "constant can't be decremented (%.*s)",
+ (int)(efsi->str_end - efsi->str), efsi->str);
+ } else {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_DECR, 1);
+ }
+}
+#line 1698 "ecmascript.c"
+ break;
+ case 75: /* unary_expression ::= PLUS unary_expression */
+#line 284 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PLUS, 1);
+}
+#line 1705 "ecmascript.c"
+ break;
+ case 76: /* unary_expression ::= MINUS unary_expression */
+#line 287 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MINUS, 1);
+}
+#line 1712 "ecmascript.c"
+ break;
+ case 77: /* unary_expression ::= NOT unary_expression */
+#line 290 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NOT, 1);
+}
+#line 1719 "ecmascript.c"
+ break;
+ case 78: /* unary_expression ::= BITWISE_NOT unary_expression */
+#line 293 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_BITWISE_NOT, 1);
+}
+#line 1726 "ecmascript.c"
+ break;
+ case 79: /* unary_expression ::= ADJUST unary_expression */
+#line 296 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_ADJUST, 1);
+}
+#line 1733 "ecmascript.c"
+ break;
+ case 80: /* unary_expression ::= EXACT unary_expression */
+#line 299 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_EXACT, 1);
+}
+#line 1740 "ecmascript.c"
+ break;
+ case 81: /* unary_expression ::= PARTIAL unary_expression */
+#line 302 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PARTIAL, 1);
+}
+#line 1747 "ecmascript.c"
+ break;
+ case 82: /* unary_expression ::= UNSPLIT unary_expression */
+#line 305 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_UNSPLIT, 1);
+}
+#line 1754 "ecmascript.c"
+ break;
+ case 84: /* postfix_expression ::= lefthand_side_expression INCR */
+#line 310 "ecmascript.lemon"
+{
+ grn_ctx *ctx = efsi->ctx;
+ grn_expr *e = (grn_expr *)(efsi->e);
+ grn_expr_dfi *dfi_;
+ unsigned int const_p;
+
+ DFI_POP(e, dfi_);
+ const_p = CONSTP(dfi_->code->value);
+ DFI_PUT(e, dfi_->type, dfi_->domain, dfi_->code);
+ if (const_p) {
+ ERR(GRN_SYNTAX_ERROR,
+ "constant can't be incremented (%.*s)",
+ (int)(efsi->str_end - efsi->str), efsi->str);
+ } else {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_INCR_POST, 1);
+ }
+}
+#line 1775 "ecmascript.c"
+ break;
+ case 85: /* postfix_expression ::= lefthand_side_expression DECR */
+#line 327 "ecmascript.lemon"
+{
+ grn_ctx *ctx = efsi->ctx;
+ grn_expr *e = (grn_expr *)(efsi->e);
+ grn_expr_dfi *dfi_;
+ unsigned int const_p;
+
+ DFI_POP(e, dfi_);
+ const_p = CONSTP(dfi_->code->value);
+ DFI_PUT(e, dfi_->type, dfi_->domain, dfi_->code);
+ if (const_p) {
+ ERR(GRN_SYNTAX_ERROR,
+ "constant can't be decremented (%.*s)",
+ (int)(efsi->str_end - efsi->str), efsi->str);
+ } else {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_DECR_POST, 1);
+ }
+}
+#line 1796 "ecmascript.c"
+ break;
+ case 88: /* call_expression ::= member_expression arguments */
+#line 348 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_CALL, yymsp[0].minor.yy0);
+}
+#line 1803 "ecmascript.c"
+ break;
+ case 113: /* member_expression_part ::= BRACKETL expression BRACKETR */
+#line 384 "ecmascript.lemon"
+{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_GET_MEMBER, 2);
+}
+#line 1810 "ecmascript.c"
+ break;
+ case 115: /* arguments ::= PARENL argument_list PARENR */
+#line 389 "ecmascript.lemon"
+{ yygotominor.yy0 = yymsp[-1].minor.yy0; }
+#line 1815 "ecmascript.c"
+ break;
+ case 116: /* argument_list ::= */
+#line 390 "ecmascript.lemon"
+{ yygotominor.yy0 = 0; }
+#line 1820 "ecmascript.c"
+ break;
+ case 117: /* argument_list ::= assignment_expression */
+#line 391 "ecmascript.lemon"
+{ yygotominor.yy0 = 1; }
+#line 1825 "ecmascript.c"
+ break;
+ case 118: /* argument_list ::= argument_list COMMA assignment_expression */
+#line 392 "ecmascript.lemon"
+{ yygotominor.yy0 = yymsp[-2].minor.yy0 + 1; }
+#line 1830 "ecmascript.c"
+ break;
+ case 119: /* output_columns ::= */
+#line 394 "ecmascript.lemon"
+{
+ yygotominor.yy0 = 0;
+}
+#line 1837 "ecmascript.c"
+ break;
+ case 120: /* output_columns ::= output_column */
+#line 397 "ecmascript.lemon"
+{
+ if (yymsp[0].minor.yy0) {
+ yygotominor.yy0 = 0;
+ } else {
+ yygotominor.yy0 = 1;
+ }
+}
+#line 1848 "ecmascript.c"
+ break;
+ case 121: /* output_columns ::= output_columns COMMA output_column */
+#line 405 "ecmascript.lemon"
+{
+ if (yymsp[0].minor.yy0) {
+ yygotominor.yy0 = yymsp[-2].minor.yy0;
+ } else {
+ if (yymsp[-2].minor.yy0 == 1) {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_COMMA, 2);
+ }
+ yygotominor.yy0 = 1;
+ }
+}
+#line 1862 "ecmascript.c"
+ break;
+ case 122: /* output_column ::= STAR */
+#line 416 "ecmascript.lemon"
+{
+ grn_ctx *ctx = efsi->ctx;
+ grn_obj *expr = efsi->e;
+ grn_expr *e = (grn_expr *)expr;
+ grn_obj *variable = grn_expr_get_var_by_offset(ctx, expr, 0);
+ if (variable) {
+ grn_id table_id = GRN_OBJ_GET_DOMAIN(variable);
+ grn_obj *table = grn_ctx_at(ctx, table_id);
+ grn_obj columns_buffer;
+ grn_obj **columns;
+ int i, n_columns;
+
+ GRN_PTR_INIT(&columns_buffer, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ grn_obj_columns(ctx, table, "*", strlen("*"), &columns_buffer);
+ n_columns = GRN_BULK_VSIZE(&columns_buffer) / sizeof(grn_obj *);
+ columns = (grn_obj **)GRN_BULK_HEAD(&columns_buffer);
+
+ for (i = 0; i < n_columns; i++) {
+ if (i > 0) {
+ grn_expr_append_op(ctx, expr, GRN_OP_COMMA, 2);
+ }
+ grn_expr_append_const(ctx, expr, columns[i], GRN_OP_GET_VALUE, 1);
+ GRN_PTR_PUT(ctx, &e->objs, columns[i]);
+ }
+
+ GRN_OBJ_FIN(ctx, &columns_buffer);
+
+ if (n_columns > 0) {
+ yygotominor.yy0 = GRN_FALSE;
+ } else {
+ yygotominor.yy0 = GRN_TRUE;
+ }
+ } else {
+ /* TODO: report error */
+ yygotominor.yy0 = GRN_TRUE;
+ }
+}
+#line 1903 "ecmascript.c"
+ break;
+ case 123: /* output_column ::= NONEXISTENT_COLUMN */
+#line 453 "ecmascript.lemon"
+{
+ yygotominor.yy0 = GRN_TRUE;
+}
+#line 1910 "ecmascript.c"
+ break;
+ case 124: /* output_column ::= assignment_expression */
+#line 456 "ecmascript.lemon"
+{
+ yygotominor.yy0 = GRN_FALSE;
+}
+#line 1917 "ecmascript.c"
+ break;
+ default:
+ /* (0) input ::= query */ yytestcase(yyruleno==0);
+ /* (1) input ::= expression */ yytestcase(yyruleno==1);
+ /* (2) input ::= START_OUTPUT_COLUMNS output_columns */ yytestcase(yyruleno==2);
+ /* (3) input ::= START_ADJUSTER adjuster */ yytestcase(yyruleno==3);
+ /* (4) query ::= query_element */ yytestcase(yyruleno==4);
+ /* (9) query_element ::= QSTRING */ yytestcase(yyruleno==9);
+ /* (10) query_element ::= PARENL query PARENR */ yytestcase(yyruleno==10);
+ /* (15) expression ::= assignment_expression */ yytestcase(yyruleno==15);
+ /* (17) assignment_expression ::= conditional_expression */ yytestcase(yyruleno==17);
+ /* (30) conditional_expression ::= logical_or_expression */ yytestcase(yyruleno==30);
+ /* (32) logical_or_expression ::= logical_and_expression */ yytestcase(yyruleno==32);
+ /* (34) logical_and_expression ::= bitwise_or_expression */ yytestcase(yyruleno==34);
+ /* (37) bitwise_or_expression ::= bitwise_xor_expression */ yytestcase(yyruleno==37);
+ /* (39) bitwise_xor_expression ::= bitwise_and_expression */ yytestcase(yyruleno==39);
+ /* (41) bitwise_and_expression ::= equality_expression */ yytestcase(yyruleno==41);
+ /* (43) equality_expression ::= relational_expression */ yytestcase(yyruleno==43);
+ /* (46) relational_expression ::= shift_expression */ yytestcase(yyruleno==46);
+ /* (60) shift_expression ::= additive_expression */ yytestcase(yyruleno==60);
+ /* (64) additive_expression ::= multiplicative_expression */ yytestcase(yyruleno==64);
+ /* (67) multiplicative_expression ::= unary_expression */ yytestcase(yyruleno==67);
+ /* (71) unary_expression ::= postfix_expression */ yytestcase(yyruleno==71);
+ /* (83) postfix_expression ::= lefthand_side_expression */ yytestcase(yyruleno==83);
+ /* (86) lefthand_side_expression ::= call_expression */ yytestcase(yyruleno==86);
+ /* (87) lefthand_side_expression ::= member_expression */ yytestcase(yyruleno==87);
+ /* (89) member_expression ::= primary_expression */ yytestcase(yyruleno==89);
+ /* (90) member_expression ::= member_expression member_expression_part */ yytestcase(yyruleno==90);
+ /* (91) primary_expression ::= object_literal */ yytestcase(yyruleno==91);
+ /* (92) primary_expression ::= PARENL expression PARENR */ yytestcase(yyruleno==92);
+ /* (93) primary_expression ::= IDENTIFIER */ yytestcase(yyruleno==93);
+ /* (94) primary_expression ::= array_literal */ yytestcase(yyruleno==94);
+ /* (95) primary_expression ::= DECIMAL */ yytestcase(yyruleno==95);
+ /* (96) primary_expression ::= HEX_INTEGER */ yytestcase(yyruleno==96);
+ /* (97) primary_expression ::= STRING */ yytestcase(yyruleno==97);
+ /* (98) primary_expression ::= BOOLEAN */ yytestcase(yyruleno==98);
+ /* (99) primary_expression ::= NULL */ yytestcase(yyruleno==99);
+ /* (100) array_literal ::= BRACKETL elision BRACKETR */ yytestcase(yyruleno==100);
+ /* (101) array_literal ::= BRACKETL element_list elision BRACKETR */ yytestcase(yyruleno==101);
+ /* (102) array_literal ::= BRACKETL element_list BRACKETR */ yytestcase(yyruleno==102);
+ /* (103) elision ::= COMMA */ yytestcase(yyruleno==103);
+ /* (104) elision ::= elision COMMA */ yytestcase(yyruleno==104);
+ /* (105) element_list ::= assignment_expression */ yytestcase(yyruleno==105);
+ /* (106) element_list ::= elision assignment_expression */ yytestcase(yyruleno==106);
+ /* (107) element_list ::= element_list elision assignment_expression */ yytestcase(yyruleno==107);
+ /* (108) object_literal ::= BRACEL property_name_and_value_list BRACER */ yytestcase(yyruleno==108);
+ /* (109) property_name_and_value_list ::= */ yytestcase(yyruleno==109);
+ /* (110) property_name_and_value_list ::= property_name_and_value_list COMMA property_name_and_value */ yytestcase(yyruleno==110);
+ /* (111) property_name_and_value ::= property_name COLON assignment_expression */ yytestcase(yyruleno==111);
+ /* (112) property_name ::= IDENTIFIER|STRING|DECIMAL */ yytestcase(yyruleno==112);
+ /* (114) member_expression_part ::= DOT IDENTIFIER */ yytestcase(yyruleno==114);
+ /* (125) adjuster ::= */ yytestcase(yyruleno==125);
+ /* (126) adjuster ::= adjust_expression */ yytestcase(yyruleno==126);
+ /* (128) adjust_expression ::= adjust_match_expression */ yytestcase(yyruleno==128);
+ break;
+ };
+ yygoto = yyRuleInfo[yyruleno].lhs;
+ yysize = yyRuleInfo[yyruleno].nrhs;
+ yypParser->yyidx -= yysize;
+ yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto);
+ if( yyact < YYNSTATE ){
+#ifdef NDEBUG
+ /* If we are not debugging and the reduce action popped at least
+ ** one element off the stack, then we can push the new element back
+ ** onto the stack here, and skip the stack overflow test in yy_shift().
+ ** That gives a significant speed improvement. */
+ if( yysize ){
+ yypParser->yyidx++;
+ yymsp -= yysize-1;
+ yymsp->stateno = (YYACTIONTYPE)yyact;
+ yymsp->major = (YYCODETYPE)yygoto;
+ yymsp->minor = yygotominor;
+ }else
+#endif
+ {
+ yy_shift(yypParser,yyact,yygoto,&yygotominor);
+ }
+ }else{
+ assert( yyact == YYNSTATE + YYNRULE + 1 );
+ yy_accept(yypParser);
+ }
+}
+
+/*
+** The following code executes when the parse fails
+*/
+#ifndef YYNOERRORRECOVERY
+static void yy_parse_failed(
+ yyParser *yypParser /* The parser */
+){
+ grn_expr_parserARG_FETCH;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser fails */
+ grn_expr_parserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+#endif /* YYNOERRORRECOVERY */
+
+/*
+** The following code executes when a syntax error first occurs.
+*/
+static void yy_syntax_error(
+ yyParser *yypParser, /* The parser */
+ int yymajor, /* The major type of the error token */
+ YYMINORTYPE yyminor /* The minor type of the error token */
+){
+ grn_expr_parserARG_FETCH;
+#define TOKEN (yyminor.yy0)
+#line 16 "ecmascript.lemon"
+
+ {
+ grn_ctx *ctx = efsi->ctx;
+ if (ctx->rc == GRN_SUCCESS) {
+ grn_obj message;
+ GRN_TEXT_INIT(&message, 0);
+ GRN_TEXT_PUT(ctx, &message, efsi->str, efsi->cur - efsi->str);
+ GRN_TEXT_PUTC(ctx, &message, '|');
+ if (efsi->cur < efsi->str_end) {
+ GRN_TEXT_PUTC(ctx, &message, efsi->cur[0]);
+ GRN_TEXT_PUTC(ctx, &message, '|');
+ GRN_TEXT_PUT(ctx, &message,
+ efsi->cur + 1, efsi->str_end - (efsi->cur + 1));
+ } else {
+ GRN_TEXT_PUTC(ctx, &message, '|');
+ }
+ ERR(GRN_SYNTAX_ERROR, "Syntax error: <%.*s>",
+ (int)GRN_TEXT_LEN(&message), GRN_TEXT_VALUE(&message));
+ GRN_OBJ_FIN(ctx, &message);
+ }
+ }
+#line 2053 "ecmascript.c"
+ grn_expr_parserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following is executed when the parser accepts
+*/
+static void yy_accept(
+ yyParser *yypParser /* The parser */
+){
+ grn_expr_parserARG_FETCH;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser accepts */
+ grn_expr_parserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/* The main parser program.
+** The first argument is a pointer to a structure obtained from
+** "grn_expr_parserAlloc" which describes the current state of the parser.
+** The second argument is the major token number. The third is
+** the minor token. The fourth optional argument is whatever the
+** user wants (and specified in the grammar) and is available for
+** use by the action routines.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser (an opaque structure.)
+** <li> The major token number.
+** <li> The minor token number.
+** <li> An option argument of a grammar-specified type.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void grn_expr_parser(
+ void *yyp, /* The parser */
+ int yymajor, /* The major token code number */
+ grn_expr_parserTOKENTYPE yyminor /* The value for the token */
+ grn_expr_parserARG_PDECL /* Optional %extra_argument parameter */
+){
+ YYMINORTYPE yyminorunion;
+ int yyact; /* The parser action. */
+ int yyendofinput; /* True if we are at the end of input */
+#ifdef YYERRORSYMBOL
+ int yyerrorhit = 0; /* True if yymajor has invoked an error */
+#endif
+ yyParser *yypParser; /* The parser */
+
+ /* (re)initialize the parser, if necessary */
+ yypParser = (yyParser*)yyp;
+ if( yypParser->yyidx<0 ){
+#if YYSTACKDEPTH<=0
+ if( yypParser->yystksz <=0 ){
+ /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/
+ yyminorunion = yyzerominor;
+ yyStackOverflow(yypParser, &yyminorunion);
+ return;
+ }
+#endif
+ yypParser->yyidx = 0;
+ yypParser->yyerrcnt = -1;
+ yypParser->yystack[0].stateno = 0;
+ yypParser->yystack[0].major = 0;
+ }
+ yyminorunion.yy0 = yyminor;
+ yyendofinput = (yymajor==0);
+ grn_expr_parserARG_STORE;
+
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+
+ do{
+ yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
+ if( yyact<YYNSTATE ){
+ assert( !yyendofinput ); /* Impossible to shift the $ token */
+ yy_shift(yypParser,yyact,yymajor,&yyminorunion);
+ yypParser->yyerrcnt--;
+ yymajor = YYNOCODE;
+ }else if( yyact < YYNSTATE + YYNRULE ){
+ yy_reduce(yypParser,yyact-YYNSTATE);
+ }else{
+ assert( yyact == YY_ERROR_ACTION );
+#ifdef YYERRORSYMBOL
+ int yymx;
+#endif
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
+ }
+#endif
+#ifdef YYERRORSYMBOL
+ /* A syntax error has occurred.
+ ** The response to an error depends upon whether or not the
+ ** grammar defines an error token "ERROR".
+ **
+ ** This is what we do if the grammar does define ERROR:
+ **
+ ** * Call the %syntax_error function.
+ **
+ ** * Begin popping the stack until we enter a state where
+ ** it is legal to shift the error symbol, then shift
+ ** the error symbol.
+ **
+ ** * Set the error count to three.
+ **
+ ** * Begin accepting and shifting new tokens. No new error
+ ** processing will occur until three tokens have been
+ ** shifted successfully.
+ **
+ */
+ if( yypParser->yyerrcnt<0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion);
+ }
+ yymx = yypParser->yystack[yypParser->yyidx].major;
+ if( yymx==YYERRORSYMBOL || yyerrorhit ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sDiscard input token %s\n",
+ yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+ yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion);
+ yymajor = YYNOCODE;
+ }else{
+ while(
+ yypParser->yyidx >= 0 &&
+ yymx != YYERRORSYMBOL &&
+ (yyact = yy_find_reduce_action(
+ yypParser->yystack[yypParser->yyidx].stateno,
+ YYERRORSYMBOL)) >= YYNSTATE
+ ){
+ yy_pop_parser_stack(yypParser);
+ }
+ if( yypParser->yyidx < 0 || yymajor==0 ){
+ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
+ yy_parse_failed(yypParser);
+ yymajor = YYNOCODE;
+ }else if( yymx!=YYERRORSYMBOL ){
+ YYMINORTYPE u2;
+ u2.YYERRSYMDT = 0;
+ yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
+ }
+ }
+ yypParser->yyerrcnt = 3;
+ yyerrorhit = 1;
+#elif defined(YYNOERRORRECOVERY)
+ /* If the YYNOERRORRECOVERY macro is defined, then do not attempt to
+ ** do any kind of error recovery. Instead, simply invoke the syntax
+ ** error routine and continue going as if nothing had happened.
+ **
+ ** Applications can set this macro (for example inside %include) if
+ ** they intend to abandon the parse upon the first syntax error seen.
+ */
+ yy_syntax_error(yypParser,yymajor,yyminorunion);
+ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
+ yymajor = YYNOCODE;
+
+#else /* YYERRORSYMBOL is not defined */
+ /* This is what we do if the grammar does not define ERROR:
+ **
+ ** * Report an error message, and throw away the input token.
+ **
+ ** * If the input token is $, then fail the parse.
+ **
+ ** As before, subsequent error messages are suppressed until
+ ** three input tokens have been successfully shifted.
+ */
+ if( yypParser->yyerrcnt<=0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion);
+ }
+ yypParser->yyerrcnt = 3;
+ yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
+ if( yyendofinput ){
+ yy_parse_failed(yypParser);
+ }
+ yymajor = YYNOCODE;
+#endif
+ }
+ }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
+ return;
+}
diff --git a/storage/mroonga/vendor/groonga/lib/ecmascript.h b/storage/mroonga/vendor/groonga/lib/ecmascript.h
new file mode 100644
index 00000000000..c8e74b2fb8f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/ecmascript.h
@@ -0,0 +1,72 @@
+#define GRN_EXPR_TOKEN_START_OUTPUT_COLUMNS 1
+#define GRN_EXPR_TOKEN_START_ADJUSTER 2
+#define GRN_EXPR_TOKEN_LOGICAL_AND 3
+#define GRN_EXPR_TOKEN_LOGICAL_AND_NOT 4
+#define GRN_EXPR_TOKEN_LOGICAL_OR 5
+#define GRN_EXPR_TOKEN_QSTRING 6
+#define GRN_EXPR_TOKEN_PARENL 7
+#define GRN_EXPR_TOKEN_PARENR 8
+#define GRN_EXPR_TOKEN_RELATIVE_OP 9
+#define GRN_EXPR_TOKEN_IDENTIFIER 10
+#define GRN_EXPR_TOKEN_BRACEL 11
+#define GRN_EXPR_TOKEN_BRACER 12
+#define GRN_EXPR_TOKEN_EVAL 13
+#define GRN_EXPR_TOKEN_COMMA 14
+#define GRN_EXPR_TOKEN_ASSIGN 15
+#define GRN_EXPR_TOKEN_STAR_ASSIGN 16
+#define GRN_EXPR_TOKEN_SLASH_ASSIGN 17
+#define GRN_EXPR_TOKEN_MOD_ASSIGN 18
+#define GRN_EXPR_TOKEN_PLUS_ASSIGN 19
+#define GRN_EXPR_TOKEN_MINUS_ASSIGN 20
+#define GRN_EXPR_TOKEN_SHIFTL_ASSIGN 21
+#define GRN_EXPR_TOKEN_SHIFTR_ASSIGN 22
+#define GRN_EXPR_TOKEN_SHIFTRR_ASSIGN 23
+#define GRN_EXPR_TOKEN_AND_ASSIGN 24
+#define GRN_EXPR_TOKEN_XOR_ASSIGN 25
+#define GRN_EXPR_TOKEN_OR_ASSIGN 26
+#define GRN_EXPR_TOKEN_QUESTION 27
+#define GRN_EXPR_TOKEN_COLON 28
+#define GRN_EXPR_TOKEN_BITWISE_OR 29
+#define GRN_EXPR_TOKEN_BITWISE_XOR 30
+#define GRN_EXPR_TOKEN_BITWISE_AND 31
+#define GRN_EXPR_TOKEN_EQUAL 32
+#define GRN_EXPR_TOKEN_NOT_EQUAL 33
+#define GRN_EXPR_TOKEN_LESS 34
+#define GRN_EXPR_TOKEN_GREATER 35
+#define GRN_EXPR_TOKEN_LESS_EQUAL 36
+#define GRN_EXPR_TOKEN_GREATER_EQUAL 37
+#define GRN_EXPR_TOKEN_IN 38
+#define GRN_EXPR_TOKEN_MATCH 39
+#define GRN_EXPR_TOKEN_NEAR 40
+#define GRN_EXPR_TOKEN_NEAR2 41
+#define GRN_EXPR_TOKEN_SIMILAR 42
+#define GRN_EXPR_TOKEN_TERM_EXTRACT 43
+#define GRN_EXPR_TOKEN_LCP 44
+#define GRN_EXPR_TOKEN_PREFIX 45
+#define GRN_EXPR_TOKEN_SUFFIX 46
+#define GRN_EXPR_TOKEN_SHIFTL 47
+#define GRN_EXPR_TOKEN_SHIFTR 48
+#define GRN_EXPR_TOKEN_SHIFTRR 49
+#define GRN_EXPR_TOKEN_PLUS 50
+#define GRN_EXPR_TOKEN_MINUS 51
+#define GRN_EXPR_TOKEN_STAR 52
+#define GRN_EXPR_TOKEN_SLASH 53
+#define GRN_EXPR_TOKEN_MOD 54
+#define GRN_EXPR_TOKEN_DELETE 55
+#define GRN_EXPR_TOKEN_INCR 56
+#define GRN_EXPR_TOKEN_DECR 57
+#define GRN_EXPR_TOKEN_NOT 58
+#define GRN_EXPR_TOKEN_BITWISE_NOT 59
+#define GRN_EXPR_TOKEN_ADJUST 60
+#define GRN_EXPR_TOKEN_EXACT 61
+#define GRN_EXPR_TOKEN_PARTIAL 62
+#define GRN_EXPR_TOKEN_UNSPLIT 63
+#define GRN_EXPR_TOKEN_DECIMAL 64
+#define GRN_EXPR_TOKEN_HEX_INTEGER 65
+#define GRN_EXPR_TOKEN_STRING 66
+#define GRN_EXPR_TOKEN_BOOLEAN 67
+#define GRN_EXPR_TOKEN_NULL 68
+#define GRN_EXPR_TOKEN_BRACKETL 69
+#define GRN_EXPR_TOKEN_BRACKETR 70
+#define GRN_EXPR_TOKEN_DOT 71
+#define GRN_EXPR_TOKEN_NONEXISTENT_COLUMN 72
diff --git a/storage/mroonga/vendor/groonga/lib/ecmascript.lemon b/storage/mroonga/vendor/groonga/lib/ecmascript.lemon
new file mode 100644
index 00000000000..669269973dc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/ecmascript.lemon
@@ -0,0 +1,473 @@
+%name grn_expr_parser
+%token_prefix GRN_EXPR_TOKEN_
+%include {
+#define assert GRN_ASSERT
+}
+
+%token_type { int }
+
+%type suppress_unused_variable_warning { void * }
+%destructor suppress_unused_variable_warning {
+ (void)efsi;
+}
+
+%extra_argument { efs_info *efsi }
+
+%syntax_error {
+ {
+ grn_ctx *ctx = efsi->ctx;
+ if (ctx->rc == GRN_SUCCESS) {
+ grn_obj message;
+ GRN_TEXT_INIT(&message, 0);
+ GRN_TEXT_PUT(ctx, &message, efsi->str, efsi->cur - efsi->str);
+ GRN_TEXT_PUTC(ctx, &message, '|');
+ if (efsi->cur < efsi->str_end) {
+ GRN_TEXT_PUTC(ctx, &message, efsi->cur[0]);
+ GRN_TEXT_PUTC(ctx, &message, '|');
+ GRN_TEXT_PUT(ctx, &message,
+ efsi->cur + 1, efsi->str_end - (efsi->cur + 1));
+ } else {
+ GRN_TEXT_PUTC(ctx, &message, '|');
+ }
+ ERR(GRN_SYNTAX_ERROR, "Syntax error: <%.*s>",
+ (int)GRN_TEXT_LEN(&message), GRN_TEXT_VALUE(&message));
+ GRN_OBJ_FIN(ctx, &message);
+ }
+ }
+}
+
+input ::= query.
+input ::= expression.
+input ::= START_OUTPUT_COLUMNS output_columns.
+input ::= START_ADJUSTER adjuster.
+
+query ::= query_element.
+query ::= query query_element. {
+ grn_expr_append_op(efsi->ctx, efsi->e, grn_int32_value_at(&efsi->op_stack, -1), 2);
+}
+query ::= query LOGICAL_AND query_element. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_AND, 2);
+}
+query ::= query LOGICAL_AND_NOT query_element.{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_AND_NOT, 2);
+}
+query ::= query LOGICAL_OR query_element.{
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_OR, 2);
+}
+
+query_element ::= QSTRING.
+query_element ::= PARENL query PARENR.
+
+query_element ::= RELATIVE_OP query_element.{
+ int mode;
+ GRN_INT32_POP(&efsi->mode_stack, mode);
+}
+query_element ::= IDENTIFIER RELATIVE_OP query_element. {
+ int mode;
+ grn_obj *c;
+ GRN_PTR_POP(&efsi->column_stack, c);
+ GRN_INT32_POP(&efsi->mode_stack, mode);
+ switch (mode) {
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ {
+ int max_interval;
+ GRN_INT32_POP(&efsi->max_interval_stack, max_interval);
+ }
+ break;
+ default :
+ break;
+ }
+}
+query_element ::= BRACEL expression BRACER. {
+ efsi->flags = efsi->default_flags;
+}
+query_element ::= EVAL primary_expression. {
+ efsi->flags = efsi->default_flags;
+}
+
+expression ::= assignment_expression.
+expression ::= expression COMMA assignment_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_COMMA, 2);
+}
+
+assignment_expression ::= conditional_expression.
+assignment_expression ::= lefthand_side_expression ASSIGN assignment_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_ASSIGN, 2);
+}
+assignment_expression ::= lefthand_side_expression STAR_ASSIGN assignment_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_STAR_ASSIGN, 2);
+}
+assignment_expression ::= lefthand_side_expression SLASH_ASSIGN assignment_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SLASH_ASSIGN, 2);
+}
+assignment_expression ::= lefthand_side_expression MOD_ASSIGN assignment_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MOD_ASSIGN, 2);
+}
+assignment_expression ::= lefthand_side_expression PLUS_ASSIGN assignment_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PLUS_ASSIGN, 2);
+}
+assignment_expression ::= lefthand_side_expression MINUS_ASSIGN assignment_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MINUS_ASSIGN, 2);
+}
+assignment_expression ::= lefthand_side_expression SHIFTL_ASSIGN assignment_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTL_ASSIGN, 2);
+}
+assignment_expression ::= lefthand_side_expression SHIFTR_ASSIGN assignment_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTR_ASSIGN, 2);
+}
+assignment_expression ::= lefthand_side_expression SHIFTRR_ASSIGN assignment_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTRR_ASSIGN, 2);
+}
+assignment_expression ::= lefthand_side_expression AND_ASSIGN assignment_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_AND_ASSIGN, 2);
+}
+assignment_expression ::= lefthand_side_expression XOR_ASSIGN assignment_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_XOR_ASSIGN, 2);
+}
+assignment_expression ::= lefthand_side_expression OR_ASSIGN assignment_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_OR_ASSIGN, 2);
+}
+
+conditional_expression ::= logical_or_expression.
+conditional_expression ::= logical_or_expression QUESTION(A) assignment_expression COLON(B) assignment_expression. {
+ grn_expr *e = (grn_expr *)efsi->e;
+ e->codes[A].nargs = B - A;
+ e->codes[B].nargs = e->codes_curr - B - 1;
+}
+
+logical_or_expression ::= logical_and_expression.
+logical_or_expression ::= logical_or_expression LOGICAL_OR logical_and_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_OR, 2);
+}
+
+logical_and_expression ::= bitwise_or_expression.
+logical_and_expression ::= logical_and_expression LOGICAL_AND bitwise_or_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_AND, 2);
+}
+logical_and_expression ::= logical_and_expression LOGICAL_AND_NOT bitwise_or_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_AND_NOT, 2);
+}
+
+bitwise_or_expression ::= bitwise_xor_expression.
+bitwise_or_expression ::= bitwise_or_expression BITWISE_OR bitwise_xor_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_BITWISE_OR, 2);
+}
+
+bitwise_xor_expression ::= bitwise_and_expression.
+bitwise_xor_expression ::= bitwise_xor_expression BITWISE_XOR bitwise_and_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_BITWISE_XOR, 2);
+}
+
+bitwise_and_expression ::= equality_expression.
+bitwise_and_expression ::= bitwise_and_expression BITWISE_AND equality_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_BITWISE_AND, 2);
+}
+
+equality_expression ::= relational_expression.
+equality_expression ::= equality_expression EQUAL relational_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_EQUAL, 2);
+}
+equality_expression ::= equality_expression NOT_EQUAL relational_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NOT_EQUAL, 2);
+}
+
+relational_expression ::= shift_expression.
+relational_expression ::= relational_expression LESS shift_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_LESS, 2);
+}
+relational_expression ::= relational_expression GREATER shift_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_GREATER, 2);
+}
+relational_expression ::= relational_expression LESS_EQUAL shift_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_LESS_EQUAL, 2);
+}
+relational_expression ::= relational_expression GREATER_EQUAL shift_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_GREATER_EQUAL, 2);
+}
+relational_expression ::= relational_expression IN shift_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_IN, 2);
+}
+relational_expression ::= relational_expression MATCH shift_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MATCH, 2);
+}
+relational_expression ::= relational_expression NEAR shift_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NEAR, 2);
+}
+relational_expression ::= relational_expression NEAR2 shift_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NEAR2, 2);
+}
+relational_expression ::= relational_expression SIMILAR shift_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SIMILAR, 2);
+}
+relational_expression ::= relational_expression TERM_EXTRACT shift_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_TERM_EXTRACT, 2);
+}
+relational_expression ::= relational_expression LCP shift_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_LCP, 2);
+}
+relational_expression ::= relational_expression PREFIX shift_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PREFIX, 2);
+}
+relational_expression ::= relational_expression SUFFIX shift_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SUFFIX, 2);
+}
+
+shift_expression ::= additive_expression.
+shift_expression ::= shift_expression SHIFTL additive_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTL, 2);
+}
+shift_expression ::= shift_expression SHIFTR additive_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTR, 2);
+}
+shift_expression ::= shift_expression SHIFTRR additive_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SHIFTRR, 2);
+}
+
+additive_expression ::= multiplicative_expression.
+additive_expression ::= additive_expression PLUS multiplicative_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PLUS, 2);
+}
+additive_expression ::= additive_expression MINUS multiplicative_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MINUS, 2);
+}
+
+multiplicative_expression ::= unary_expression.
+multiplicative_expression ::= multiplicative_expression STAR unary_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_STAR, 2);
+}
+multiplicative_expression ::= multiplicative_expression SLASH unary_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_SLASH, 2);
+}
+multiplicative_expression ::= multiplicative_expression MOD unary_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MOD, 2);
+}
+
+unary_expression ::= postfix_expression.
+unary_expression ::= DELETE unary_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_DELETE, 1);
+}
+unary_expression ::= INCR unary_expression. {
+ grn_ctx *ctx = efsi->ctx;
+ grn_expr *e = (grn_expr *)(efsi->e);
+ grn_expr_dfi *dfi_;
+ unsigned int const_p;
+
+ DFI_POP(e, dfi_);
+ const_p = CONSTP(dfi_->code->value);
+ DFI_PUT(e, dfi_->type, dfi_->domain, dfi_->code);
+ if (const_p) {
+ ERR(GRN_SYNTAX_ERROR,
+ "constant can't be incremented (%.*s)",
+ (int)(efsi->str_end - efsi->str), efsi->str);
+ } else {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_INCR, 1);
+ }
+}
+unary_expression ::= DECR unary_expression. {
+ grn_ctx *ctx = efsi->ctx;
+ grn_expr *e = (grn_expr *)(efsi->e);
+ grn_expr_dfi *dfi_;
+ unsigned int const_p;
+
+ DFI_POP(e, dfi_);
+ const_p = CONSTP(dfi_->code->value);
+ DFI_PUT(e, dfi_->type, dfi_->domain, dfi_->code);
+ if (const_p) {
+ ERR(GRN_SYNTAX_ERROR,
+ "constant can't be decremented (%.*s)",
+ (int)(efsi->str_end - efsi->str), efsi->str);
+ } else {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_DECR, 1);
+ }
+}
+unary_expression ::= PLUS unary_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PLUS, 1);
+}
+unary_expression ::= MINUS unary_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MINUS, 1);
+}
+unary_expression ::= NOT unary_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_NOT, 1);
+}
+unary_expression ::= BITWISE_NOT unary_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_BITWISE_NOT, 1);
+}
+unary_expression ::= ADJUST unary_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_ADJUST, 1);
+}
+unary_expression ::= EXACT unary_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_EXACT, 1);
+}
+unary_expression ::= PARTIAL unary_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PARTIAL, 1);
+}
+unary_expression ::= UNSPLIT unary_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_UNSPLIT, 1);
+}
+
+postfix_expression ::= lefthand_side_expression.
+postfix_expression ::= lefthand_side_expression INCR. {
+ grn_ctx *ctx = efsi->ctx;
+ grn_expr *e = (grn_expr *)(efsi->e);
+ grn_expr_dfi *dfi_;
+ unsigned int const_p;
+
+ DFI_POP(e, dfi_);
+ const_p = CONSTP(dfi_->code->value);
+ DFI_PUT(e, dfi_->type, dfi_->domain, dfi_->code);
+ if (const_p) {
+ ERR(GRN_SYNTAX_ERROR,
+ "constant can't be incremented (%.*s)",
+ (int)(efsi->str_end - efsi->str), efsi->str);
+ } else {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_INCR_POST, 1);
+ }
+}
+postfix_expression ::= lefthand_side_expression DECR. {
+ grn_ctx *ctx = efsi->ctx;
+ grn_expr *e = (grn_expr *)(efsi->e);
+ grn_expr_dfi *dfi_;
+ unsigned int const_p;
+
+ DFI_POP(e, dfi_);
+ const_p = CONSTP(dfi_->code->value);
+ DFI_PUT(e, dfi_->type, dfi_->domain, dfi_->code);
+ if (const_p) {
+ ERR(GRN_SYNTAX_ERROR,
+ "constant can't be decremented (%.*s)",
+ (int)(efsi->str_end - efsi->str), efsi->str);
+ } else {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_DECR_POST, 1);
+ }
+}
+
+lefthand_side_expression ::= call_expression.
+lefthand_side_expression ::= member_expression.
+
+call_expression ::= member_expression arguments(A). {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_CALL, A);
+}
+
+member_expression ::= primary_expression.
+member_expression ::= member_expression member_expression_part.
+
+primary_expression ::= object_literal.
+primary_expression ::= PARENL expression PARENR.
+primary_expression ::= IDENTIFIER.
+primary_expression ::= array_literal.
+primary_expression ::= DECIMAL.
+primary_expression ::= HEX_INTEGER.
+primary_expression ::= STRING.
+primary_expression ::= BOOLEAN.
+primary_expression ::= NULL.
+
+array_literal ::= BRACKETL elision BRACKETR.
+array_literal ::= BRACKETL element_list elision BRACKETR.
+array_literal ::= BRACKETL element_list BRACKETR.
+
+elision ::= COMMA.
+elision ::= elision COMMA.
+
+element_list ::= assignment_expression.
+element_list ::= elision assignment_expression.
+element_list ::= element_list elision assignment_expression.
+
+object_literal ::= BRACEL property_name_and_value_list BRACER.
+
+property_name_and_value_list ::= .
+property_name_and_value_list ::= property_name_and_value_list COMMA property_name_and_value.
+
+property_name_and_value ::= property_name COLON assignment_expression.
+property_name ::= IDENTIFIER|STRING|DECIMAL.
+
+member_expression_part ::= BRACKETL expression BRACKETR. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_GET_MEMBER, 2);
+}
+member_expression_part ::= DOT IDENTIFIER.
+
+arguments(A) ::= PARENL argument_list(B) PARENR. { A = B; }
+argument_list(A) ::= . { A = 0; }
+argument_list(A) ::= assignment_expression. { A = 1; }
+argument_list(A) ::= argument_list(B) COMMA assignment_expression. { A = B + 1; }
+
+output_columns(N_STACKED_COLUMNS) ::= . {
+ N_STACKED_COLUMNS = 0;
+}
+output_columns(N_STACKED_COLUMNS) ::= output_column(IGNORED). {
+ if (IGNORED) {
+ N_STACKED_COLUMNS = 0;
+ } else {
+ N_STACKED_COLUMNS = 1;
+ }
+}
+output_columns(N_STACKED_COLUMNS) ::=
+ output_columns(SUB_N_STACKED_COLUMNS) COMMA output_column(IGNORED). {
+ if (IGNORED) {
+ N_STACKED_COLUMNS = SUB_N_STACKED_COLUMNS;
+ } else {
+ if (SUB_N_STACKED_COLUMNS == 1) {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_COMMA, 2);
+ }
+ N_STACKED_COLUMNS = 1;
+ }
+}
+
+output_column(IGNORED) ::= STAR. {
+ grn_ctx *ctx = efsi->ctx;
+ grn_obj *expr = efsi->e;
+ grn_expr *e = (grn_expr *)expr;
+ grn_obj *variable = grn_expr_get_var_by_offset(ctx, expr, 0);
+ if (variable) {
+ grn_id table_id = GRN_OBJ_GET_DOMAIN(variable);
+ grn_obj *table = grn_ctx_at(ctx, table_id);
+ grn_obj columns_buffer;
+ grn_obj **columns;
+ int i, n_columns;
+
+ GRN_PTR_INIT(&columns_buffer, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ grn_obj_columns(ctx, table, "*", strlen("*"), &columns_buffer);
+ n_columns = GRN_BULK_VSIZE(&columns_buffer) / sizeof(grn_obj *);
+ columns = (grn_obj **)GRN_BULK_HEAD(&columns_buffer);
+
+ for (i = 0; i < n_columns; i++) {
+ if (i > 0) {
+ grn_expr_append_op(ctx, expr, GRN_OP_COMMA, 2);
+ }
+ grn_expr_append_const(ctx, expr, columns[i], GRN_OP_GET_VALUE, 1);
+ GRN_PTR_PUT(ctx, &e->objs, columns[i]);
+ }
+
+ GRN_OBJ_FIN(ctx, &columns_buffer);
+
+ if (n_columns > 0) {
+ IGNORED = GRN_FALSE;
+ } else {
+ IGNORED = GRN_TRUE;
+ }
+ } else {
+ /* TODO: report error */
+ IGNORED = GRN_TRUE;
+ }
+}
+output_column(IGNORED) ::= NONEXISTENT_COLUMN. {
+ IGNORED = GRN_TRUE;
+}
+output_column(IGNORED) ::= assignment_expression. {
+ IGNORED = GRN_FALSE;
+}
+
+adjuster ::= .
+adjuster ::= adjust_expression.
+adjuster ::= adjuster PLUS adjust_expression. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_PLUS, 2);
+}
+
+adjust_expression ::= adjust_match_expression.
+adjust_expression ::= adjust_match_expression STAR DECIMAL. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_STAR, 2);
+}
+
+adjust_match_expression ::= IDENTIFIER MATCH STRING. {
+ grn_expr_append_op(efsi->ctx, efsi->e, GRN_OP_MATCH, 2);
+}
diff --git a/storage/mroonga/vendor/groonga/lib/error.c b/storage/mroonga/vendor/groonga/lib/error.c
new file mode 100644
index 00000000000..322b29bff7a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/error.c
@@ -0,0 +1,53 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "error.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif /* HAVE_ERRNO_H */
+
+#include <string.h>
+
+#ifdef WIN32
+const char *
+grn_current_error_message(void)
+{
+# define ERROR_MESSAGE_BUFFER_SIZE 4096
+ int error_code = WSAGetLastError();
+ static char message[ERROR_MESSAGE_BUFFER_SIZE];
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error_code,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ message,
+ ERROR_MESSAGE_BUFFER_SIZE,
+ NULL);
+
+ return message;
+
+# undef ERROR_MESSAGE_BUFFER_SIZE
+}
+#else
+const char *
+grn_current_error_message(void)
+{
+ return strerror(errno);
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/error.h b/storage/mroonga/vendor/groonga/lib/error.h
new file mode 100644
index 00000000000..f425e4644f1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/error.h
@@ -0,0 +1,35 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_ERROR_H
+#define GRN_ERROR_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char *grn_current_error_message(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_ERROR_H */
diff --git a/storage/mroonga/vendor/groonga/lib/expr.c b/storage/mroonga/vendor/groonga/lib/expr.c
new file mode 100644
index 00000000000..8010f5ddbc0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/expr.c
@@ -0,0 +1,7085 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2010-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "groonga_in.h"
+#include "db.h"
+#include "ctx_impl.h"
+#include <string.h>
+#include <float.h>
+#include "ii.h"
+#include "geo.h"
+#include "expr.h"
+#include "util.h"
+#include "normalizer_in.h"
+#include "mrb.h"
+#include "mrb/mrb_expr.h"
+
+static inline int
+function_proc_p(grn_obj *obj)
+{
+ return (obj &&
+ obj->header.type == GRN_PROC &&
+ ((grn_proc *)obj)->type == GRN_PROC_FUNCTION);
+}
+
+static inline int
+selector_proc_p(grn_obj *obj)
+{
+ return (function_proc_p(obj) && ((grn_proc *)obj)->selector);
+}
+
+grn_obj *
+grn_expr_alloc(grn_ctx *ctx, grn_obj *expr, grn_id domain, grn_obj_flags flags)
+{
+ grn_obj *res = NULL;
+ grn_expr *e = (grn_expr *)expr;
+ if (e) {
+ if (e->values_curr >= e->values_size) {
+ // todo : expand values.
+ ERR(GRN_NO_MEMORY_AVAILABLE, "no more e->values");
+ return NULL;
+ }
+ res = &e->values[e->values_curr++];
+ if (e->values_curr > e->values_tail) { e->values_tail = e->values_curr; }
+ grn_obj_reinit(ctx, res, domain, flags);
+ }
+ return res;
+}
+
+static grn_hash *
+grn_expr_get_vars(grn_ctx *ctx, grn_obj *expr, unsigned int *nvars)
+{
+ grn_hash *vars = NULL;
+ if (expr->header.type == GRN_PROC || expr->header.type == GRN_EXPR) {
+ grn_id id = DB_OBJ(expr)->id;
+ grn_expr *e = (grn_expr *)expr;
+ int added = 0;
+ grn_hash **vp;
+ if (grn_hash_add(ctx, ctx->impl->expr_vars, &id, sizeof(grn_id), (void **)&vp, &added)) {
+ if (!*vp) {
+ *vp = grn_hash_create(ctx, NULL, GRN_TABLE_MAX_KEY_SIZE, sizeof(grn_obj),
+ GRN_OBJ_KEY_VAR_SIZE|GRN_OBJ_TEMPORARY|GRN_HASH_TINY);
+ if (*vp) {
+ uint32_t i;
+ grn_obj *value;
+ grn_expr_var *v;
+ for (v = e->vars, i = e->nvars; i; v++, i--) {
+ grn_hash_add(ctx, *vp, v->name, v->name_size, (void **)&value, &added);
+ GRN_OBJ_INIT(value, v->value.header.type, 0, v->value.header.domain);
+ GRN_TEXT_PUT(ctx, value, GRN_TEXT_VALUE(&v->value), GRN_TEXT_LEN(&v->value));
+ }
+ }
+ }
+ vars = *vp;
+ }
+ }
+ *nvars = vars ? GRN_HASH_SIZE(vars) : 0;
+ return vars;
+}
+
+grn_rc
+grn_expr_clear_vars(grn_ctx *ctx, grn_obj *expr)
+{
+ if (expr->header.type == GRN_PROC || expr->header.type == GRN_EXPR) {
+ grn_hash **vp;
+ grn_id eid, id = DB_OBJ(expr)->id;
+ if ((eid = grn_hash_get(ctx, ctx->impl->expr_vars, &id, sizeof(grn_id), (void **)&vp))) {
+ if (*vp) {
+ grn_obj *value;
+ GRN_HASH_EACH(ctx, *vp, i, NULL, NULL, (void **)&value, {
+ GRN_OBJ_FIN(ctx, value);
+ });
+ grn_hash_close(ctx, *vp);
+ }
+ grn_hash_delete_by_id(ctx, ctx->impl->expr_vars, eid, NULL);
+ }
+ }
+ return ctx->rc;
+}
+
+grn_obj *
+grn_proc_get_info(grn_ctx *ctx, grn_user_data *user_data,
+ grn_expr_var **vars, unsigned int *nvars, grn_obj **caller)
+{
+ grn_proc_ctx *pctx = (grn_proc_ctx *)user_data;
+ if (caller) { *caller = pctx->caller; }
+ if (pctx->proc) {
+ if (vars) {
+ *vars = pctx->proc->vars;
+ // *vars = grn_expr_get_vars(ctx, (grn_obj *)pctx->proc, nvars);
+ }
+ if (nvars) { *nvars = pctx->proc->nvars; }
+ } else {
+ if (vars) { *vars = NULL; }
+ if (nvars) { *nvars = 0; }
+ }
+ return (grn_obj *)pctx->proc;
+}
+
+grn_obj *
+grn_proc_get_var(grn_ctx *ctx, grn_user_data *user_data, const char *name, unsigned int name_size)
+{
+ grn_proc_ctx *pctx = (grn_proc_ctx *)user_data;
+ return pctx->proc ? grn_expr_get_var(ctx, (grn_obj *)pctx->proc, name, name_size) : NULL;
+}
+
+grn_obj *
+grn_proc_get_var_by_offset(grn_ctx *ctx, grn_user_data *user_data, unsigned int offset)
+{
+ grn_proc_ctx *pctx = (grn_proc_ctx *)user_data;
+ return pctx->proc ? grn_expr_get_var_by_offset(ctx, (grn_obj *)pctx->proc, offset) : NULL;
+}
+
+grn_obj *
+grn_proc_get_or_add_var(grn_ctx *ctx, grn_user_data *user_data,
+ const char *name, unsigned int name_size)
+{
+ grn_proc_ctx *pctx = (grn_proc_ctx *)user_data;
+ return pctx->proc ? grn_expr_get_or_add_var(ctx, (grn_obj *)pctx->proc, name, name_size) : NULL;
+}
+
+grn_obj *
+grn_proc_alloc(grn_ctx *ctx, grn_user_data *user_data, grn_id domain, grn_obj_flags flags)
+{
+ grn_proc_ctx *pctx = (grn_proc_ctx *)user_data;
+ return pctx->caller ? grn_expr_alloc(ctx, (grn_obj *)pctx->caller, domain, flags) : NULL;
+}
+
+grn_proc_type
+grn_proc_get_type(grn_ctx *ctx, grn_obj *proc)
+{
+ grn_proc *proc_ = (grn_proc *)proc;
+ return proc_ ? proc_->type : GRN_PROC_INVALID;
+}
+
+grn_rc
+grn_proc_set_selector(grn_ctx *ctx, grn_obj *proc, grn_selector_func selector)
+{
+ grn_proc *proc_ = (grn_proc *)proc;
+ if (!function_proc_p(proc)) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ proc_->selector = selector;
+ return GRN_SUCCESS;
+}
+
+/* grn_expr */
+
+static const char *opstrs[] = {
+ "PUSH",
+ "POP",
+ "NOP",
+ "CALL",
+ "INTERN",
+ "GET_REF",
+ "GET_VALUE",
+ "AND",
+ "AND_NOT",
+ "OR",
+ "ASSIGN",
+ "STAR_ASSIGN",
+ "SLASH_ASSIGN",
+ "MOD_ASSIGN",
+ "PLUS_ASSIGN",
+ "MINUS_ASSIGN",
+ "SHIFTL_ASSIGN",
+ "SHIFTR_ASSIGN",
+ "SHIFTRR_ASSIGN",
+ "AND_ASSIGN",
+ "XOR_ASSIGN",
+ "OR_ASSIGN",
+ "JUMP",
+ "CJUMP",
+ "COMMA",
+ "BITWISE_OR",
+ "BITWISE_XOR",
+ "BITWISE_AND",
+ "BITWISE_NOT",
+ "EQUAL",
+ "NOT_EQUAL",
+ "LESS",
+ "GREATER",
+ "LESS_EQUAL",
+ "GREATER_EQUAL",
+ "IN",
+ "MATCH",
+ "NEAR",
+ "NEAR2",
+ "SIMILAR",
+ "TERM_EXTRACT",
+ "SHIFTL",
+ "SHIFTR",
+ "SHIFTRR",
+ "PLUS",
+ "MINUS",
+ "STAR",
+ "SLASH",
+ "MOD",
+ "DELETE",
+ "INCR",
+ "DECR",
+ "INCR_POST",
+ "DECR_POST",
+ "NOT",
+ "ADJUST",
+ "EXACT",
+ "LCP",
+ "PARTIAL",
+ "UNSPLIT",
+ "PREFIX",
+ "SUFFIX",
+ "GEO_DISTANCE1",
+ "GEO_DISTANCE2",
+ "GEO_DISTANCE3",
+ "GEO_DISTANCE4",
+ "GEO_WITHINP5",
+ "GEO_WITHINP6",
+ "GEO_WITHINP8",
+ "OBJ_SEARCH",
+ "EXPR_GET_VAR",
+ "TABLE_CREATE",
+ "TABLE_SELECT",
+ "TABLE_SORT",
+ "TABLE_GROUP",
+ "JSON_PUT"
+};
+
+static void
+put_value(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ int len;
+ char namebuf[GRN_TABLE_MAX_KEY_SIZE];
+ if ((len = grn_column_name(ctx, obj, namebuf, GRN_TABLE_MAX_KEY_SIZE))) {
+ GRN_TEXT_PUT(ctx, buf, namebuf, len);
+ } else {
+ grn_text_otoj(ctx, buf, obj, NULL);
+ }
+}
+
+grn_rc
+grn_expr_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *expr)
+{
+ uint32_t i, j;
+ grn_expr_var *var;
+ grn_expr_code *code;
+ grn_expr *e = (grn_expr *)expr;
+ grn_hash *vars = grn_expr_get_vars(ctx, expr, &i);
+ GRN_TEXT_PUTS(ctx, buf, "noname");
+ GRN_TEXT_PUTC(ctx, buf, '(');
+ {
+ int i = 0;
+ grn_obj *value;
+ const char *name;
+ uint32_t name_len;
+ GRN_HASH_EACH(ctx, vars, id, &name, &name_len, &value, {
+ if (i++) { GRN_TEXT_PUTC(ctx, buf, ','); }
+ GRN_TEXT_PUT(ctx, buf, name, name_len);
+ GRN_TEXT_PUTC(ctx, buf, ':');
+ put_value(ctx, buf, value);
+ });
+ }
+ GRN_TEXT_PUTC(ctx, buf, ')');
+ GRN_TEXT_PUTC(ctx, buf, '{');
+ for (j = 0, code = e->codes; j < e->codes_curr; j++, code++) {
+ if (j) { GRN_TEXT_PUTC(ctx, buf, ','); }
+ grn_text_itoa(ctx, buf, code->modify);
+ if (code->op == GRN_OP_PUSH) {
+ for (i = 0, var = e->vars; i < e->nvars; i++, var++) {
+ if (&var->value == code->value) {
+ GRN_TEXT_PUTC(ctx, buf, '?');
+ if (var->name_size) {
+ GRN_TEXT_PUT(ctx, buf, var->name, var->name_size);
+ } else {
+ grn_text_itoa(ctx, buf, (int)i);
+ }
+ break;
+ }
+ }
+ if (i == e->nvars) {
+ put_value(ctx, buf, code->value);
+ }
+ } else {
+ if (code->value) {
+ put_value(ctx, buf, code->value);
+ GRN_TEXT_PUTC(ctx, buf, ' ');
+ }
+ GRN_TEXT_PUTS(ctx, buf, opstrs[code->op]);
+ }
+ }
+ GRN_TEXT_PUTC(ctx, buf, '}');
+ return GRN_SUCCESS;
+}
+
+grn_obj *
+grn_ctx_pop(grn_ctx *ctx)
+{
+ if (ctx && ctx->impl && ctx->impl->stack_curr) {
+ return ctx->impl->stack[--ctx->impl->stack_curr];
+ }
+ return NULL;
+}
+
+grn_rc
+grn_ctx_push(grn_ctx *ctx, grn_obj *obj)
+{
+ if (ctx && ctx->impl && ctx->impl->stack_curr < GRN_STACK_SIZE) {
+ ctx->impl->stack[ctx->impl->stack_curr++] = obj;
+ return GRN_SUCCESS;
+ }
+ return GRN_STACK_OVER_FLOW;
+}
+
+static grn_obj *
+const_new(grn_ctx *ctx, grn_expr *e)
+{
+ if (!e->consts) {
+ if (!(e->consts = GRN_MALLOCN(grn_obj, GRN_STACK_SIZE))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "malloc failed");
+ return NULL;
+ }
+ }
+ if (e->nconsts < GRN_STACK_SIZE) {
+ return e->consts + e->nconsts++;
+ } else {
+ ERR(GRN_STACK_OVER_FLOW, "too many constants.");
+ return NULL;
+ }
+}
+
+void
+grn_obj_pack(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_text_benc(ctx, buf, obj->header.type);
+ if (GRN_DB_OBJP(obj)) {
+ grn_text_benc(ctx, buf, DB_OBJ(obj)->id);
+ } else {
+ // todo : support vector, query, accessor, snip..
+ uint32_t vs = GRN_BULK_VSIZE(obj);
+ grn_text_benc(ctx, buf, obj->header.domain);
+ grn_text_benc(ctx, buf, vs);
+ if (vs) { GRN_TEXT_PUT(ctx, buf, GRN_BULK_HEAD(obj), vs); }
+ }
+}
+
+const uint8_t *
+grn_obj_unpack(grn_ctx *ctx, const uint8_t *p, const uint8_t *pe, uint8_t type, uint8_t flags, grn_obj *obj)
+{
+ grn_id domain;
+ uint32_t vs;
+ GRN_B_DEC(domain, p);
+ GRN_OBJ_INIT(obj, type, flags, domain);
+ GRN_B_DEC(vs, p);
+ if (pe < p + vs) {
+ ERR(GRN_INVALID_FORMAT, "benced image is corrupt");
+ return p;
+ }
+ grn_bulk_write(ctx, obj, (const char *)p, vs);
+ return p + vs;
+}
+
+typedef enum {
+ GRN_EXPR_PACK_TYPE_NULL = 0,
+ GRN_EXPR_PACK_TYPE_VARIABLE = 1,
+ GRN_EXPR_PACK_TYPE_OTHERS = 2
+} grn_expr_pack_type;
+
+void
+grn_expr_pack(grn_ctx *ctx, grn_obj *buf, grn_obj *expr)
+{
+ grn_expr_code *c;
+ grn_expr_var *v;
+ grn_expr *e = (grn_expr *)expr;
+ uint32_t i, j;
+ grn_text_benc(ctx, buf, e->nvars);
+ for (i = e->nvars, v = e->vars; i; i--, v++) {
+ grn_text_benc(ctx, buf, v->name_size);
+ if (v->name_size) { GRN_TEXT_PUT(ctx, buf, v->name, v->name_size); }
+ grn_obj_pack(ctx, buf, &v->value);
+ }
+ i = e->codes_curr;
+ grn_text_benc(ctx, buf, i);
+ for (c = e->codes; i; i--, c++) {
+ grn_text_benc(ctx, buf, c->op);
+ grn_text_benc(ctx, buf, c->nargs);
+ if (!c->value) {
+ grn_text_benc(ctx, buf, GRN_EXPR_PACK_TYPE_NULL);
+ } else {
+ for (j = 0, v = e->vars; j < e->nvars; j++, v++) {
+ if (&v->value == c->value) {
+ grn_text_benc(ctx, buf, GRN_EXPR_PACK_TYPE_VARIABLE);
+ grn_text_benc(ctx, buf, j);
+ break;
+ }
+ }
+ if (j == e->nvars) {
+ grn_text_benc(ctx, buf, GRN_EXPR_PACK_TYPE_OTHERS);
+ grn_obj_pack(ctx, buf, c->value);
+ }
+ }
+ }
+}
+
+const uint8_t *
+grn_expr_unpack(grn_ctx *ctx, const uint8_t *p, const uint8_t *pe, grn_obj *expr)
+{
+ grn_obj *v;
+ grn_expr_pack_type type;
+ uint32_t i, n, ns;
+ grn_expr_code *code;
+ grn_expr *e = (grn_expr *)expr;
+ GRN_B_DEC(n, p);
+ for (i = 0; i < n; i++) {
+ uint32_t object_type;
+ GRN_B_DEC(ns, p);
+ v = grn_expr_add_var(ctx, expr, ns ? (const char *)p : NULL, ns);
+ p += ns;
+ GRN_B_DEC(object_type, p);
+ if (GRN_TYPE <= object_type && object_type <= GRN_COLUMN_INDEX) { /* error */ }
+ p = grn_obj_unpack(ctx, p, pe, object_type, 0, v);
+ if (pe < p) {
+ ERR(GRN_INVALID_FORMAT, "benced image is corrupt");
+ return p;
+ }
+ }
+ GRN_B_DEC(n, p);
+ /* confirm e->codes_size >= n */
+ e->codes_curr = n;
+ for (i = 0, code = e->codes; i < n; i++, code++) {
+ GRN_B_DEC(code->op, p);
+ GRN_B_DEC(code->nargs, p);
+ GRN_B_DEC(type, p);
+ switch (type) {
+ case GRN_EXPR_PACK_TYPE_NULL :
+ code->value = NULL;
+ break;
+ case GRN_EXPR_PACK_TYPE_VARIABLE :
+ {
+ uint32_t offset;
+ GRN_B_DEC(offset, p);
+ code->value = &e->vars[i].value;
+ }
+ break;
+ case GRN_EXPR_PACK_TYPE_OTHERS :
+ {
+ uint32_t object_type;
+ GRN_B_DEC(object_type, p);
+ if (GRN_TYPE <= object_type && object_type <= GRN_COLUMN_INDEX) {
+ grn_id id;
+ GRN_B_DEC(id, p);
+ code->value = grn_ctx_at(ctx, id);
+ } else {
+ if (!(v = const_new(ctx, e))) { return NULL; }
+ p = grn_obj_unpack(ctx, p, pe, object_type, GRN_OBJ_EXPRCONST, v);
+ code->value = v;
+ }
+ }
+ break;
+ }
+ if (pe < p) {
+ ERR(GRN_INVALID_FORMAT, "benced image is corrupt");
+ return p;
+ }
+ }
+ return p;
+}
+
+grn_obj *
+grn_expr_open(grn_ctx *ctx, grn_obj_spec *spec, const uint8_t *p, const uint8_t *pe)
+{
+ grn_expr *expr = NULL;
+ if ((expr = GRN_MALLOCN(grn_expr, 1))) {
+ int size = 256;
+ expr->consts = NULL;
+ expr->nconsts = 0;
+ GRN_TEXT_INIT(&expr->name_buf, 0);
+ GRN_TEXT_INIT(&expr->dfi, 0);
+ GRN_PTR_INIT(&expr->objs, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ expr->vars = NULL;
+ expr->nvars = 0;
+ GRN_DB_OBJ_SET_TYPE(expr, GRN_EXPR);
+ if ((expr->values = GRN_MALLOCN(grn_obj, size))) {
+ int i;
+ for (i = 0; i < size; i++) {
+ GRN_OBJ_INIT(&expr->values[i], GRN_BULK, GRN_OBJ_EXPRVALUE, GRN_ID_NIL);
+ }
+ expr->values_curr = 0;
+ expr->values_tail = 0;
+ expr->values_size = size;
+ if ((expr->codes = GRN_MALLOCN(grn_expr_code, size))) {
+ expr->codes_curr = 0;
+ expr->codes_size = size;
+ expr->obj.header = spec->header;
+ if (grn_expr_unpack(ctx, p, pe, (grn_obj *)expr) == pe) {
+ goto exit;
+ } else {
+ ERR(GRN_INVALID_FORMAT, "benced image is corrupt");
+ }
+ GRN_FREE(expr->codes);
+ }
+ GRN_FREE(expr->values);
+ }
+ GRN_FREE(expr);
+ expr = NULL;
+ }
+exit :
+ return (grn_obj *)expr;
+}
+
+/* data flow info */
+typedef struct {
+ grn_expr_code *code;
+ grn_id domain;
+ unsigned char type;
+} grn_expr_dfi;
+
+#define DFI_POP(e,d) do {\
+ if (GRN_BULK_VSIZE(&(e)->dfi) >= sizeof(grn_expr_dfi)) {\
+ GRN_BULK_INCR_LEN((&(e)->dfi), -(sizeof(grn_expr_dfi)));\
+ (d) = (grn_expr_dfi *)(GRN_BULK_CURR(&(e)->dfi));\
+ (e)->code0 = (d)->code;\
+ } else {\
+ (d) = NULL;\
+ (e)->code0 = NULL;\
+ }\
+} while (0)
+
+#define DFI_PUT(e,t,d,c) do {\
+ grn_expr_dfi dfi;\
+ dfi.type = (t);\
+ dfi.domain = (d);\
+ dfi.code = (c);\
+ if ((e)->code0) { (e)->code0->modify = (c) ? ((c) - (e)->code0) : 0; }\
+ grn_bulk_write(ctx, &(e)->dfi, (char *)&dfi, sizeof(grn_expr_dfi));\
+ (e)->code0 = NULL;\
+} while (0)
+
+grn_expr_dfi *
+dfi_value_at(grn_expr *expr, int offset)
+{
+ grn_obj *obj = &expr->dfi;
+ int size = GRN_BULK_VSIZE(obj) / sizeof(grn_expr_dfi);
+ if (offset < 0) { offset = size + offset; }
+ return (0 <= offset && offset < size)
+ ? &(((grn_expr_dfi *)GRN_BULK_HEAD(obj))[offset])
+ : NULL;
+}
+
+grn_obj *
+grn_expr_create(grn_ctx *ctx, const char *name, unsigned int name_size)
+{
+ grn_id id;
+ grn_obj *db;
+ grn_expr *expr = NULL;
+ if (!ctx || !ctx->impl || !(db = ctx->impl->db)) {
+ ERR(GRN_INVALID_ARGUMENT, "db not initialized");
+ return NULL;
+ }
+ if (name_size) {
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED,
+ "[expr][create] anonymous expression isn't implemented yet");
+ return NULL;
+ }
+ GRN_API_ENTER;
+ if (grn_db_check_name(ctx, name, name_size)) {
+ GRN_DB_CHECK_NAME_ERR("[expr][create]", name, name_size);
+ GRN_API_RETURN(NULL);
+ }
+ if (!GRN_DB_P(db)) {
+ ERR(GRN_INVALID_ARGUMENT, "named expr is not supported");
+ GRN_API_RETURN(NULL);
+ }
+ id = grn_obj_register(ctx, db, name, name_size);
+ if (id && (expr = GRN_MALLOCN(grn_expr, 1))) {
+ int size = GRN_STACK_SIZE;
+ expr->consts = NULL;
+ expr->nconsts = 0;
+ GRN_TEXT_INIT(&expr->name_buf, 0);
+ GRN_TEXT_INIT(&expr->dfi, 0);
+ GRN_PTR_INIT(&expr->objs, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ expr->code0 = NULL;
+ expr->vars = NULL;
+ expr->nvars = 0;
+ expr->cacheable = 1;
+ expr->taintable = 0;
+ expr->values_curr = 0;
+ expr->values_tail = 0;
+ expr->values_size = size;
+ expr->codes_curr = 0;
+ expr->codes_size = size;
+ GRN_DB_OBJ_SET_TYPE(expr, GRN_EXPR);
+ expr->obj.header.domain = GRN_ID_NIL;
+ expr->obj.range = GRN_ID_NIL;
+ if (!grn_db_obj_init(ctx, db, id, DB_OBJ(expr))) {
+ if ((expr->values = GRN_MALLOCN(grn_obj, size))) {
+ int i;
+ for (i = 0; i < size; i++) {
+ GRN_OBJ_INIT(&expr->values[i], GRN_BULK, GRN_OBJ_EXPRVALUE, GRN_ID_NIL);
+ }
+ if ((expr->codes = GRN_MALLOCN(grn_expr_code, size))) {
+ goto exit;
+ }
+ GRN_FREE(expr->values);
+ }
+ }
+ GRN_FREE(expr);
+ expr = NULL;
+ }
+exit :
+ GRN_API_RETURN((grn_obj *)expr);
+}
+
+#define GRN_PTR_POP(obj,value) do {\
+ if (GRN_BULK_VSIZE(obj) >= sizeof(grn_obj *)) {\
+ GRN_BULK_INCR_LEN((obj), -(sizeof(grn_obj *)));\
+ value = *(grn_obj **)(GRN_BULK_CURR(obj));\
+ } else {\
+ value = NULL;\
+ }\
+} while (0)
+
+grn_rc
+grn_expr_close(grn_ctx *ctx, grn_obj *expr)
+{
+ uint32_t i;
+ grn_expr *e = (grn_expr *)expr;
+ GRN_API_ENTER;
+ /*
+ if (e->obj.header.domain) {
+ grn_hash_delete(ctx, ctx->impl->qe, &e->obj.header.domain, sizeof(grn_id), NULL);
+ }
+ */
+ grn_expr_clear_vars(ctx, expr);
+ for (i = 0; i < e->nconsts; i++) {
+ grn_obj_close(ctx, &e->consts[i]);
+ }
+ if (e->consts) { GRN_FREE(e->consts); }
+ grn_obj_close(ctx, &e->name_buf);
+ grn_obj_close(ctx, &e->dfi);
+ for (;;) {
+ grn_obj *obj;
+ GRN_PTR_POP(&e->objs, obj);
+ if (obj) {
+#ifdef USE_MEMORY_DEBUG
+ grn_obj_unlink(ctx, obj);
+#else
+ if (obj->header.type) {
+ grn_obj_unlink(ctx, obj);
+ } else {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "GRN_VOID object is tried to be unlinked");
+ }
+#endif
+ } else { break; }
+ }
+ grn_obj_close(ctx, &e->objs);
+ for (i = 0; i < e->nvars; i++) {
+ grn_obj_close(ctx, &e->vars[i].value);
+ }
+ if (e->vars) { GRN_FREE(e->vars); }
+ for (i = 0; i < e->values_tail; i++) {
+ grn_obj_close(ctx, &e->values[i]);
+ }
+ GRN_FREE(e->values);
+ GRN_FREE(e->codes);
+ GRN_FREE(e);
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_obj *
+grn_expr_add_var(grn_ctx *ctx, grn_obj *expr, const char *name, unsigned int name_size)
+{
+ uint32_t i;
+ char *p;
+ grn_expr_var *v;
+ grn_obj *res = NULL;
+ grn_expr *e = (grn_expr *)expr;
+ GRN_API_ENTER;
+ if (DB_OBJ(expr)->id & GRN_OBJ_TMP_OBJECT) {
+ res = grn_expr_get_or_add_var(ctx, expr, name, name_size);
+ } else {
+ if (!e->vars) {
+ if (!(e->vars = GRN_MALLOCN(grn_expr_var, GRN_STACK_SIZE))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "malloc failed");
+ }
+ }
+ if (e->vars && e->nvars < GRN_STACK_SIZE) {
+ v = e->vars + e->nvars++;
+ if (name_size) {
+ GRN_TEXT_PUT(ctx, &e->name_buf, name, name_size);
+ } else {
+ uint32_t ol = GRN_TEXT_LEN(&e->name_buf);
+ GRN_TEXT_PUTC(ctx, &e->name_buf, '$');
+ grn_text_itoa(ctx, &e->name_buf, e->nvars);
+ name_size = GRN_TEXT_LEN(&e->name_buf) - ol;
+ }
+ v->name_size = name_size;
+ res = &v->value;
+ GRN_VOID_INIT(res);
+ for (i = e->nvars, p = GRN_TEXT_VALUE(&e->name_buf), v = e->vars; i; i--, v++) {
+ v->name = p;
+ p += v->name_size;
+ }
+ }
+ }
+ GRN_API_RETURN(res);
+}
+
+grn_obj *
+grn_expr_get_var(grn_ctx *ctx, grn_obj *expr, const char *name, unsigned int name_size)
+{
+ uint32_t n;
+ grn_obj *res = NULL;
+ grn_hash *vars = grn_expr_get_vars(ctx, expr, &n);
+ if (vars) { grn_hash_get(ctx, vars, name, name_size, (void **)&res); }
+ return res;
+}
+
+grn_obj *
+grn_expr_get_or_add_var(grn_ctx *ctx, grn_obj *expr, const char *name, unsigned int name_size)
+{
+ uint32_t n;
+ grn_obj *res = NULL;
+ grn_hash *vars = grn_expr_get_vars(ctx, expr, &n);
+ if (vars) {
+ int added = 0;
+ char name_buf[16];
+ if (!name_size) {
+ char *rest;
+ name_buf[0] = '$';
+ grn_itoa((int)GRN_HASH_SIZE(vars) + 1, name_buf + 1, name_buf + 16, &rest);
+ name_size = rest - name_buf;
+ name = name_buf;
+ }
+ grn_hash_add(ctx, vars, name, name_size, (void **)&res, &added);
+ if (added) { GRN_TEXT_INIT(res, 0); }
+ }
+ return res;
+}
+
+grn_obj *
+grn_expr_get_var_by_offset(grn_ctx *ctx, grn_obj *expr, unsigned int offset)
+{
+ uint32_t n;
+ grn_obj *res = NULL;
+ grn_hash *vars = grn_expr_get_vars(ctx, expr, &n);
+ if (vars) { res = (grn_obj *)grn_hash_get_value_(ctx, vars, offset + 1, &n); }
+ return res;
+}
+
+#define EXPRVP(x) ((x)->header.impl_flags & GRN_OBJ_EXPRVALUE)
+
+#define CONSTP(obj) ((obj) && ((obj)->header.impl_flags & GRN_OBJ_EXPRCONST))
+
+#define PUSH_CODE(e,o,v,n,c) do {\
+ (c) = &(e)->codes[e->codes_curr++];\
+ (c)->value = (v);\
+ (c)->nargs = (n);\
+ (c)->op = (o);\
+ (c)->flags = 0;\
+ (c)->modify = 0;\
+} while (0)
+
+#define APPEND_UNARY_MINUS_OP(e) do { \
+ grn_expr_code *code_; \
+ grn_id domain; \
+ unsigned char type; \
+ grn_obj *x; \
+ DFI_POP(e, dfi); \
+ code_ = dfi->code; \
+ domain = dfi->domain; \
+ type = dfi->type; \
+ x = code_->value; \
+ if (CONSTP(x)) { \
+ switch (domain) { \
+ case GRN_DB_UINT32: \
+ { \
+ unsigned int value; \
+ value = GRN_UINT32_VALUE(x); \
+ if (value > (unsigned int)0x80000000) { \
+ domain = GRN_DB_INT64; \
+ x->header.domain = domain; \
+ GRN_INT64_SET(ctx, x, -((long long int)value)); \
+ } else { \
+ domain = GRN_DB_INT32; \
+ x->header.domain = domain; \
+ GRN_INT32_SET(ctx, x, -((int)value)); \
+ } \
+ } \
+ break; \
+ case GRN_DB_INT64: \
+ GRN_INT64_SET(ctx, x, -GRN_INT64_VALUE(x)); \
+ break; \
+ case GRN_DB_FLOAT: \
+ GRN_FLOAT_SET(ctx, x, -GRN_FLOAT_VALUE(x)); \
+ break; \
+ default: \
+ PUSH_CODE(e, op, obj, nargs, code); \
+ break; \
+ } \
+ } else { \
+ PUSH_CODE(e, op, obj, nargs, code); \
+ } \
+ DFI_PUT(e, type, domain, code_); \
+} while (0)
+
+#define PUSH_N_ARGS_ARITHMETIC_OP(e, op, obj, nargs, code) do { \
+ PUSH_CODE(e, op, obj, nargs, code); \
+ { \
+ int i = nargs; \
+ while (i--) { \
+ DFI_POP(e, dfi); \
+ } \
+ } \
+ DFI_PUT(e, type, domain, code); \
+} while (0)
+
+grn_obj *
+grn_expr_append_obj(grn_ctx *ctx, grn_obj *expr, grn_obj *obj, grn_operator op, int nargs)
+{
+ uint8_t type = GRN_VOID;
+ grn_id domain = GRN_ID_NIL;
+ grn_expr_dfi *dfi;
+ grn_expr_code *code;
+ grn_obj *res = NULL;
+ grn_expr *e = (grn_expr *)expr;
+ GRN_API_ENTER;
+ if (e->codes_curr >= e->codes_size) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "stack is full");
+ goto exit;
+ }
+ {
+ switch (op) {
+ case GRN_OP_PUSH :
+ if (obj) {
+ PUSH_CODE(e, op, obj, nargs, code);
+ DFI_PUT(e, obj->header.type, GRN_OBJ_GET_DOMAIN(obj), code);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "obj not assigned for GRN_OP_PUSH");
+ goto exit;
+ }
+ break;
+ case GRN_OP_NOP :
+ /* nop */
+ break;
+ case GRN_OP_POP :
+ if (obj) {
+ ERR(GRN_INVALID_ARGUMENT, "obj assigned for GRN_OP_POP");
+ goto exit;
+ } else {
+ PUSH_CODE(e, op, obj, nargs, code);
+ DFI_POP(e, dfi);
+ }
+ break;
+ case GRN_OP_CALL :
+ {
+ grn_obj *proc = NULL;
+ if (e->codes_curr - nargs > 0) {
+ int i;
+ grn_expr_code *code;
+ code = &(e->codes[e->codes_curr - 1]);
+ for (i = 0; i < nargs; i++) {
+ int rest_n_codes = 1;
+ while (rest_n_codes > 0) {
+ if (!code->value) {
+ rest_n_codes += code->nargs;
+ }
+ rest_n_codes--;
+ code--;
+ }
+ }
+ proc = code->value;
+ }
+ if (!proc) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid function call expression");
+ goto exit;
+ }
+ if (!function_proc_p(proc)) {
+ grn_obj buffer;
+
+ GRN_TEXT_INIT(&buffer, 0);
+ switch (proc->header.type) {
+ case GRN_TABLE_HASH_KEY:
+ case GRN_TABLE_PAT_KEY:
+ case GRN_TABLE_NO_KEY:
+ case GRN_COLUMN_FIX_SIZE:
+ case GRN_COLUMN_VAR_SIZE:
+ case GRN_COLUMN_INDEX:
+ grn_inspect_name(ctx, &buffer, proc);
+ break;
+ default:
+ grn_inspect(ctx, &buffer, proc);
+ break;
+ }
+ ERR(GRN_INVALID_ARGUMENT, "invalid function: <%.*s>",
+ (int)GRN_TEXT_LEN(&buffer), GRN_TEXT_VALUE(&buffer));
+ GRN_OBJ_FIN(ctx, &buffer);
+ goto exit;
+ }
+ }
+ PUSH_CODE(e, op, obj, nargs, code);
+ {
+ int i = nargs;
+ while (i--) { DFI_POP(e, dfi); }
+ }
+ if (!obj) { DFI_POP(e, dfi); }
+ // todo : increment e->values_tail.
+ DFI_PUT(e, type, domain, code); /* cannot identify type of return value */
+ e->cacheable = 0;
+ break;
+ case GRN_OP_INTERN :
+ if (obj && CONSTP(obj)) {
+ grn_obj *value;
+ value = grn_expr_get_var(ctx, expr, GRN_TEXT_VALUE(obj), GRN_TEXT_LEN(obj));
+ if (!value) { value = grn_ctx_get(ctx, GRN_TEXT_VALUE(obj), GRN_TEXT_LEN(obj)); }
+ if (value) {
+ obj = value;
+ op = GRN_OP_PUSH;
+ type = obj->header.type;
+ domain = GRN_OBJ_GET_DOMAIN(obj);
+ }
+ }
+ PUSH_CODE(e, op, obj, nargs, code);
+ DFI_PUT(e, type, domain, code);
+ break;
+ case GRN_OP_EQUAL :
+ PUSH_CODE(e, op, obj, nargs, code);
+ if (nargs) {
+ grn_id xd, yd = GRN_ID_NIL;
+ grn_obj *x, *y = NULL;
+ int i = nargs - 1;
+ if (obj) {
+ xd = GRN_OBJ_GET_DOMAIN(obj);
+ x = obj;
+ } else {
+ DFI_POP(e, dfi);
+ x = dfi->code->value;
+ xd = dfi->domain;
+ }
+ while (i--) {
+ DFI_POP(e, dfi);
+ y = dfi->code->value;
+ yd = dfi->domain;
+ }
+ if (CONSTP(x)) {
+ if (CONSTP(y)) {
+ /* todo */
+ } else {
+ grn_obj dest;
+ if (xd != yd) {
+ GRN_OBJ_INIT(&dest, GRN_BULK, 0, yd);
+ if (!grn_obj_cast(ctx, x, &dest, GRN_FALSE)) {
+ grn_obj_reinit(ctx, x, yd, 0);
+ grn_bulk_write(ctx, x, GRN_BULK_HEAD(&dest), GRN_BULK_VSIZE(&dest));
+ }
+ GRN_OBJ_FIN(ctx, &dest);
+ }
+ }
+ } else {
+ if (CONSTP(y)) {
+ grn_obj dest;
+ if (xd != yd) {
+ GRN_OBJ_INIT(&dest, GRN_BULK, 0, xd);
+ if (!grn_obj_cast(ctx, y, &dest, GRN_FALSE)) {
+ grn_obj_reinit(ctx, y, xd, 0);
+ grn_bulk_write(ctx, y, GRN_BULK_HEAD(&dest), GRN_BULK_VSIZE(&dest));
+ }
+ GRN_OBJ_FIN(ctx, &dest);
+ }
+ }
+ }
+ }
+ DFI_PUT(e, type, domain, code);
+ break;
+ case GRN_OP_TABLE_CREATE :
+ case GRN_OP_EXPR_GET_VAR :
+ case GRN_OP_MATCH :
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ case GRN_OP_SIMILAR :
+ case GRN_OP_PREFIX :
+ case GRN_OP_SUFFIX :
+ case GRN_OP_NOT_EQUAL :
+ case GRN_OP_LESS :
+ case GRN_OP_GREATER :
+ case GRN_OP_LESS_EQUAL :
+ case GRN_OP_GREATER_EQUAL :
+ case GRN_OP_GEO_DISTANCE1 :
+ case GRN_OP_GEO_DISTANCE2 :
+ case GRN_OP_GEO_DISTANCE3 :
+ case GRN_OP_GEO_DISTANCE4 :
+ case GRN_OP_GEO_WITHINP5 :
+ case GRN_OP_GEO_WITHINP6 :
+ case GRN_OP_GEO_WITHINP8 :
+ case GRN_OP_OBJ_SEARCH :
+ case GRN_OP_TABLE_SELECT :
+ case GRN_OP_TABLE_SORT :
+ case GRN_OP_TABLE_GROUP :
+ case GRN_OP_JSON_PUT :
+ case GRN_OP_GET_REF :
+ case GRN_OP_ADJUST :
+ case GRN_OP_TERM_EXTRACT :
+ PUSH_CODE(e, op, obj, nargs, code);
+ if (nargs) {
+ int i = nargs - 1;
+ if (!obj) { DFI_POP(e, dfi); }
+ while (i--) { DFI_POP(e, dfi); }
+ }
+ DFI_PUT(e, type, domain, code);
+ break;
+ case GRN_OP_AND :
+ case GRN_OP_OR :
+ case GRN_OP_AND_NOT :
+ PUSH_CODE(e, op, obj, nargs, code);
+ if (nargs != 2) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "nargs(%d) != 2 in relative op", nargs);
+ }
+ if (obj) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "obj assigned to relative op");
+ }
+ {
+ int i = nargs;
+ while (i--) {
+ DFI_POP(e, dfi);
+ if (dfi) {
+ dfi->code->flags |= GRN_EXPR_CODE_RELATIONAL_EXPRESSION;
+ } else {
+ ERR(GRN_SYNTAX_ERROR, "stack under flow in relative op");
+ }
+ }
+ }
+ DFI_PUT(e, type, domain, code);
+ break;
+ case GRN_OP_NOT :
+ if (nargs == 1) {
+ PUSH_CODE(e, op, obj, nargs, code);
+ }
+ break;
+ case GRN_OP_PLUS :
+ if (nargs > 1) {
+ PUSH_N_ARGS_ARITHMETIC_OP(e, op, obj, nargs, code);
+ }
+ break;
+ case GRN_OP_MINUS :
+ if (nargs == 1) {
+ APPEND_UNARY_MINUS_OP(e);
+ } else {
+ PUSH_N_ARGS_ARITHMETIC_OP(e, op, obj, nargs, code);
+ }
+ break;
+ case GRN_OP_BITWISE_NOT :
+ PUSH_CODE(e, op, obj, nargs, code);
+ break;
+ case GRN_OP_STAR :
+ case GRN_OP_SLASH :
+ case GRN_OP_MOD :
+ case GRN_OP_SHIFTL :
+ case GRN_OP_SHIFTR :
+ case GRN_OP_SHIFTRR :
+ case GRN_OP_BITWISE_OR :
+ case GRN_OP_BITWISE_XOR :
+ case GRN_OP_BITWISE_AND :
+ PUSH_N_ARGS_ARITHMETIC_OP(e, op, obj, nargs, code);
+ break;
+ case GRN_OP_INCR :
+ case GRN_OP_DECR :
+ case GRN_OP_INCR_POST :
+ case GRN_OP_DECR_POST :
+ {
+ DFI_POP(e, dfi);
+ if (dfi) {
+ type = dfi->type;
+ domain = dfi->domain;
+ if (dfi->code) {
+ if (dfi->code->op == GRN_OP_GET_VALUE) {
+ dfi->code->op = GRN_OP_GET_REF;
+ }
+ if (dfi->code->value && grn_obj_is_persistent(ctx, dfi->code->value)) {
+ e->cacheable = 0;
+ e->taintable = 1;
+ }
+ }
+ }
+ PUSH_CODE(e, op, obj, nargs, code);
+ }
+ DFI_PUT(e, type, domain, code);
+ break;
+ case GRN_OP_GET_VALUE :
+ {
+ grn_id vdomain = GRN_ID_NIL;
+ if (obj) {
+ if (nargs == 1) {
+ grn_obj *v = grn_expr_get_var_by_offset(ctx, expr, 0);
+ if (v) { vdomain = GRN_OBJ_GET_DOMAIN(v); }
+ } else {
+ DFI_POP(e, dfi);
+ vdomain = dfi->domain;
+ }
+ if (vdomain && CONSTP(obj) && obj->header.type == GRN_BULK) {
+ grn_obj *table = grn_ctx_at(ctx, vdomain);
+ grn_obj *col = grn_obj_column(ctx, table, GRN_BULK_HEAD(obj), GRN_BULK_VSIZE(obj));
+ if (col) {
+ obj = col;
+ type = col->header.type;
+ domain = grn_obj_get_range(ctx, col);
+ GRN_PTR_PUT(ctx, &(e->objs), col);
+ }
+ } else {
+ domain = grn_obj_get_range(ctx, obj);
+ }
+ PUSH_CODE(e, op, obj, nargs, code);
+ } else {
+ grn_expr_dfi *dfi0;
+ DFI_POP(e, dfi0);
+ if (nargs == 1) {
+ grn_obj *v = grn_expr_get_var_by_offset(ctx, expr, 0);
+ if (v) { vdomain = GRN_OBJ_GET_DOMAIN(v); }
+ } else {
+ DFI_POP(e, dfi);
+ vdomain = dfi->domain;
+ }
+ if (dfi0->code->op == GRN_OP_PUSH) {
+ dfi0->code->op = op;
+ dfi0->code->nargs = nargs;
+ obj = dfi0->code->value;
+ if (vdomain && obj && CONSTP(obj) && obj->header.type == GRN_BULK) {
+ grn_obj *table = grn_ctx_at(ctx, vdomain);
+ grn_obj *col = grn_obj_column(ctx, table, GRN_BULK_HEAD(obj), GRN_BULK_VSIZE(obj));
+ if (col) {
+ dfi0->code->value = col;
+ type = col->header.type;
+ domain = grn_obj_get_range(ctx, col);
+ grn_obj_unlink(ctx, col);
+ }
+ } else {
+ domain = grn_obj_get_range(ctx, obj);
+ }
+ code = dfi0->code;
+ } else {
+ PUSH_CODE(e, op, obj, nargs, code);
+ }
+ }
+ }
+ DFI_PUT(e, type, domain, code);
+ break;
+ case GRN_OP_ASSIGN :
+ case GRN_OP_STAR_ASSIGN :
+ case GRN_OP_SLASH_ASSIGN :
+ case GRN_OP_MOD_ASSIGN :
+ case GRN_OP_PLUS_ASSIGN :
+ case GRN_OP_MINUS_ASSIGN :
+ case GRN_OP_SHIFTL_ASSIGN :
+ case GRN_OP_SHIFTR_ASSIGN :
+ case GRN_OP_SHIFTRR_ASSIGN :
+ case GRN_OP_AND_ASSIGN :
+ case GRN_OP_OR_ASSIGN :
+ case GRN_OP_XOR_ASSIGN :
+ {
+ if (obj) {
+ type = obj->header.type;
+ domain = GRN_OBJ_GET_DOMAIN(obj);
+ } else {
+ DFI_POP(e, dfi);
+ if (dfi) {
+ type = dfi->type;
+ domain = dfi->domain;
+ }
+ }
+ DFI_POP(e, dfi);
+ if (dfi && (dfi->code)) {
+ if (dfi->code->op == GRN_OP_GET_VALUE) {
+ dfi->code->op = GRN_OP_GET_REF;
+ }
+ if (dfi->code->value && grn_obj_is_persistent(ctx, dfi->code->value)) {
+ e->cacheable = 0;
+ e->taintable = 1;
+ }
+ }
+ PUSH_CODE(e, op, obj, nargs, code);
+ }
+ DFI_PUT(e, type, domain, code);
+ break;
+ case GRN_OP_JUMP :
+ DFI_POP(e, dfi);
+ PUSH_CODE(e, op, obj, nargs, code);
+ break;
+ case GRN_OP_CJUMP :
+ DFI_POP(e, dfi);
+ PUSH_CODE(e, op, obj, nargs, code);
+ break;
+ case GRN_OP_COMMA :
+ PUSH_CODE(e, op, obj, nargs, code);
+ break;
+ case GRN_OP_GET_MEMBER :
+ DFI_POP(e, dfi);
+ DFI_POP(e, dfi);
+ if (dfi) {
+ type = dfi->type;
+ domain = dfi->domain;
+ if (dfi->code) {
+ if (dfi->code->op == GRN_OP_GET_VALUE) {
+ dfi->code->op = GRN_OP_GET_REF;
+ }
+ }
+ }
+ PUSH_CODE(e, op, obj, nargs, code);
+ DFI_PUT(e, type, domain, code);
+ break;
+ default :
+ break;
+ }
+ }
+exit :
+ if (!ctx->rc) { res = obj; }
+ GRN_API_RETURN(res);
+}
+#undef PUSH_N_ARGS_ARITHMETIC_OP
+#undef APPEND_UNARY_MINUS_OP
+
+grn_obj *
+grn_expr_append_const(grn_ctx *ctx, grn_obj *expr, grn_obj *obj,
+ grn_operator op, int nargs)
+{
+ grn_obj *res = NULL;
+ grn_expr *e = (grn_expr *)expr;
+ GRN_API_ENTER;
+ if (!obj) {
+ ERR(GRN_SYNTAX_ERROR, "constant is null");
+ goto exit;
+ }
+ if (GRN_DB_OBJP(obj) || GRN_ACCESSORP(obj)) {
+ res = obj;
+ } else {
+ if ((res = const_new(ctx, e))) {
+ switch (obj->header.type) {
+ case GRN_VOID :
+ case GRN_BULK :
+ case GRN_UVECTOR :
+ GRN_OBJ_INIT(res, obj->header.type, 0, obj->header.domain);
+ grn_bulk_write(ctx, res, GRN_BULK_HEAD(obj), GRN_BULK_VSIZE(obj));
+ break;
+ default :
+ res = NULL;
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "unsupported type");
+ goto exit;
+ }
+ res->header.impl_flags |= GRN_OBJ_EXPRCONST;
+ }
+ }
+ grn_expr_append_obj(ctx, expr, res, op, nargs); /* constant */
+exit :
+ GRN_API_RETURN(res);
+}
+
+static grn_obj *
+grn_expr_add_str(grn_ctx *ctx, grn_obj *expr, const char *str, unsigned int str_size)
+{
+ grn_obj *res = NULL;
+ grn_expr *e = (grn_expr *)expr;
+ if ((res = const_new(ctx, e))) {
+ GRN_TEXT_INIT(res, 0);
+ grn_bulk_write(ctx, res, str, str_size);
+ res->header.impl_flags |= GRN_OBJ_EXPRCONST;
+ }
+ return res;
+}
+
+grn_obj *
+grn_expr_append_const_str(grn_ctx *ctx, grn_obj *expr, const char *str, unsigned int str_size,
+ grn_operator op, int nargs)
+{
+ grn_obj *res;
+ GRN_API_ENTER;
+ res = grn_expr_add_str(ctx, expr, str, str_size);
+ grn_expr_append_obj(ctx, expr, res, op, nargs); /* constant */
+ GRN_API_RETURN(res);
+}
+
+grn_obj *
+grn_expr_append_const_int(grn_ctx *ctx, grn_obj *expr, int i,
+ grn_operator op, int nargs)
+{
+ grn_obj *res = NULL;
+ grn_expr *e = (grn_expr *)expr;
+ GRN_API_ENTER;
+ if ((res = const_new(ctx, e))) {
+ GRN_INT32_INIT(res, 0);
+ GRN_INT32_SET(ctx, res, i);
+ res->header.impl_flags |= GRN_OBJ_EXPRCONST;
+ }
+ grn_expr_append_obj(ctx, expr, res, op, nargs); /* constant */
+ GRN_API_RETURN(res);
+}
+
+grn_rc
+grn_expr_append_op(grn_ctx *ctx, grn_obj *expr, grn_operator op, int nargs)
+{
+ grn_expr_append_obj(ctx, expr, NULL, op, nargs);
+ return ctx->rc;
+}
+
+grn_rc
+grn_expr_compile(grn_ctx *ctx, grn_obj *expr)
+{
+ grn_obj_spec_save(ctx, DB_OBJ(expr));
+ return ctx->rc;
+}
+
+#define WITH_SPSAVE(block) do {\
+ ctx->impl->stack_curr = sp - ctx->impl->stack;\
+ e->values_curr = vp - e->values;\
+ block\
+ vp = e->values + e->values_curr;\
+ sp = ctx->impl->stack + ctx->impl->stack_curr;\
+ s0 = sp[-1];\
+ s1 = sp[-2];\
+} while (0)
+
+#define DO_COMPARE_SUB_NUMERIC(y,op) do {\
+ switch ((y)->header.domain) {\
+ case GRN_DB_INT8 :\
+ r = (x_ op GRN_INT8_VALUE(y));\
+ break;\
+ case GRN_DB_UINT8 :\
+ r = (x_ op GRN_UINT8_VALUE(y));\
+ break;\
+ case GRN_DB_INT16 :\
+ r = (x_ op GRN_INT16_VALUE(y));\
+ break;\
+ case GRN_DB_UINT16 :\
+ r = (x_ op GRN_UINT16_VALUE(y));\
+ break;\
+ case GRN_DB_INT32 :\
+ r = (x_ op GRN_INT32_VALUE(y));\
+ break;\
+ case GRN_DB_UINT32 :\
+ r = (x_ op GRN_UINT32_VALUE(y));\
+ break;\
+ case GRN_DB_INT64 :\
+ r = (x_ op GRN_INT64_VALUE(y));\
+ break;\
+ case GRN_DB_TIME :\
+ r = (GRN_TIME_PACK(x_,0) op GRN_INT64_VALUE(y));\
+ break;\
+ case GRN_DB_UINT64 :\
+ r = (x_ op GRN_UINT64_VALUE(y));\
+ break;\
+ case GRN_DB_FLOAT :\
+ r = (x_ op GRN_FLOAT_VALUE(y));\
+ break;\
+ default :\
+ r = 0;\
+ break;\
+ }\
+} while (0)
+
+#define DO_COMPARE_SUB(op) do {\
+ switch (y->header.domain) {\
+ case GRN_DB_SHORT_TEXT :\
+ case GRN_DB_TEXT :\
+ case GRN_DB_LONG_TEXT :\
+ {\
+ grn_obj y_;\
+ GRN_OBJ_INIT(&y_, GRN_BULK, 0, x->header.domain);\
+ if (grn_obj_cast(ctx, y, &y_, GRN_FALSE)) {\
+ r = 0;\
+ } else {\
+ DO_COMPARE_SUB_NUMERIC(&y_, op);\
+ }\
+ GRN_OBJ_FIN(ctx, &y_);\
+ }\
+ break;\
+ default :\
+ DO_COMPARE_SUB_NUMERIC(y,op);\
+ break;\
+ }\
+} while (0)
+
+#define DO_COMPARE_BUILTIN(x,y,r,op) do {\
+ switch (x->header.domain) {\
+ case GRN_DB_INT8 :\
+ {\
+ int8_t x_ = GRN_INT8_VALUE(x);\
+ DO_COMPARE_SUB(op);\
+ }\
+ break;\
+ case GRN_DB_UINT8 :\
+ {\
+ uint8_t x_ = GRN_UINT8_VALUE(x);\
+ DO_COMPARE_SUB(op);\
+ }\
+ break;\
+ case GRN_DB_INT16 :\
+ {\
+ int16_t x_ = GRN_INT16_VALUE(x);\
+ DO_COMPARE_SUB(op);\
+ }\
+ break;\
+ case GRN_DB_UINT16 :\
+ {\
+ uint16_t x_ = GRN_UINT16_VALUE(x);\
+ DO_COMPARE_SUB(op);\
+ }\
+ break;\
+ case GRN_DB_INT32 :\
+ {\
+ int32_t x_ = GRN_INT32_VALUE(x);\
+ DO_COMPARE_SUB(op);\
+ }\
+ break;\
+ case GRN_DB_UINT32 :\
+ {\
+ uint32_t x_ = GRN_UINT32_VALUE(x);\
+ DO_COMPARE_SUB(op);\
+ }\
+ break;\
+ case GRN_DB_TIME :\
+ {\
+ int64_t x_ = GRN_INT64_VALUE(x);\
+ switch (y->header.domain) {\
+ case GRN_DB_INT32 :\
+ r = (x_ op GRN_TIME_PACK(GRN_INT32_VALUE(y), 0));\
+ break;\
+ case GRN_DB_UINT32 :\
+ r = (x_ op GRN_TIME_PACK(GRN_UINT32_VALUE(y), 0));\
+ break;\
+ case GRN_DB_INT64 :\
+ case GRN_DB_TIME :\
+ r = (x_ op GRN_INT64_VALUE(y));\
+ break;\
+ case GRN_DB_UINT64 :\
+ r = (x_ op GRN_UINT64_VALUE(y));\
+ break;\
+ case GRN_DB_FLOAT :\
+ r = (x_ op GRN_TIME_PACK(GRN_FLOAT_VALUE(y), 0));\
+ break;\
+ case GRN_DB_SHORT_TEXT :\
+ case GRN_DB_TEXT :\
+ case GRN_DB_LONG_TEXT :\
+ {\
+ const char *p_ = GRN_TEXT_VALUE(y);\
+ int i_ = grn_atoi(p_, p_ + GRN_TEXT_LEN(y), NULL);\
+ r = (x_ op GRN_TIME_PACK(i_, 0));\
+ }\
+ break;\
+ default :\
+ r = 0;\
+ break;\
+ }\
+ }\
+ break;\
+ case GRN_DB_INT64 :\
+ {\
+ int64_t x_ = GRN_INT64_VALUE(x);\
+ DO_COMPARE_SUB(op);\
+ }\
+ break;\
+ case GRN_DB_UINT64 :\
+ {\
+ uint64_t x_ = GRN_UINT64_VALUE(x);\
+ DO_COMPARE_SUB(op);\
+ }\
+ break;\
+ case GRN_DB_FLOAT :\
+ {\
+ double x_ = GRN_FLOAT_VALUE(x);\
+ DO_COMPARE_SUB(op);\
+ }\
+ break;\
+ case GRN_DB_SHORT_TEXT :\
+ case GRN_DB_TEXT :\
+ case GRN_DB_LONG_TEXT :\
+ if (GRN_DB_SHORT_TEXT <= y->header.domain && y->header.domain <= GRN_DB_LONG_TEXT) {\
+ int r_;\
+ uint32_t la = GRN_TEXT_LEN(x), lb = GRN_TEXT_LEN(y);\
+ if (la > lb) {\
+ if (!(r_ = memcmp(GRN_TEXT_VALUE(x), GRN_TEXT_VALUE(y), lb))) {\
+ r_ = 1;\
+ }\
+ } else {\
+ if (!(r_ = memcmp(GRN_TEXT_VALUE(x), GRN_TEXT_VALUE(y), la))) {\
+ r_ = la == lb ? 0 : -1;\
+ }\
+ }\
+ r = (r_ op 0);\
+ } else {\
+ const char *q_ = GRN_TEXT_VALUE(x);\
+ int x_ = grn_atoi(q_, q_ + GRN_TEXT_LEN(x), NULL);\
+ DO_COMPARE_SUB(op);\
+ }\
+ break;\
+ default :\
+ r = 0;\
+ break;\
+ }\
+} while (0)
+
+#define DO_COMPARE(x, y, r, op) do {\
+ if (x->header.domain >= GRN_N_RESERVED_TYPES) {\
+ grn_obj *table;\
+ table = grn_ctx_at(ctx, x->header.domain);\
+ switch (table->header.type) {\
+ case GRN_TABLE_HASH_KEY :\
+ case GRN_TABLE_PAT_KEY :\
+ {\
+ grn_obj key;\
+ int length;\
+ GRN_OBJ_INIT(&key, GRN_BULK, 0, table->header.domain);\
+ length = grn_table_get_key2(ctx, table, GRN_RECORD_VALUE(x), &key);\
+ if (length > 0) {\
+ grn_obj *x_original = x;\
+ x = &key;\
+ DO_COMPARE_BUILTIN((&key), y, r, op);\
+ x = x_original;\
+ } else {\
+ r = 0;\
+ }\
+ GRN_OBJ_FIN(ctx, &key);\
+ }\
+ break;\
+ default :\
+ r = 0;\
+ break;\
+ }\
+ grn_obj_unlink(ctx, table);\
+ } else {\
+ DO_COMPARE_BUILTIN(x, y, r, op);\
+ }\
+} while (0)
+
+#define DO_EQ_SUB do {\
+ switch (y->header.domain) {\
+ case GRN_DB_INT8 :\
+ r = (x_ == GRN_INT8_VALUE(y));\
+ break;\
+ case GRN_DB_UINT8 :\
+ r = (x_ == GRN_UINT8_VALUE(y));\
+ break;\
+ case GRN_DB_INT16 :\
+ r = (x_ == GRN_INT16_VALUE(y));\
+ break;\
+ case GRN_DB_UINT16 :\
+ r = (x_ == GRN_UINT16_VALUE(y));\
+ break;\
+ case GRN_DB_INT32 :\
+ r = (x_ == GRN_INT32_VALUE(y));\
+ break;\
+ case GRN_DB_UINT32 :\
+ r = (x_ == GRN_UINT32_VALUE(y));\
+ break;\
+ case GRN_DB_INT64 :\
+ r = (x_ == GRN_INT64_VALUE(y));\
+ break;\
+ case GRN_DB_TIME :\
+ r = (GRN_TIME_PACK(x_,0) == GRN_INT64_VALUE(y));\
+ break;\
+ case GRN_DB_UINT64 :\
+ r = (x_ == GRN_UINT64_VALUE(y));\
+ break;\
+ case GRN_DB_FLOAT :\
+ r = ((x_ <= GRN_FLOAT_VALUE(y)) && (x_ >= GRN_FLOAT_VALUE(y)));\
+ break;\
+ case GRN_DB_SHORT_TEXT :\
+ case GRN_DB_TEXT :\
+ case GRN_DB_LONG_TEXT :\
+ {\
+ const char *p_ = GRN_TEXT_VALUE(y);\
+ int i_ = grn_atoi(p_, p_ + GRN_TEXT_LEN(y), NULL);\
+ r = (x_ == i_);\
+ }\
+ break;\
+ default :\
+ r = 0;\
+ break;\
+ }\
+} while (0)
+
+#define DO_EQ(x,y,r) do {\
+ switch (x->header.domain) {\
+ case GRN_DB_VOID :\
+ r = 0;\
+ break;\
+ case GRN_DB_INT8 :\
+ {\
+ int8_t x_ = GRN_INT8_VALUE(x);\
+ DO_EQ_SUB;\
+ }\
+ break;\
+ case GRN_DB_UINT8 :\
+ {\
+ uint8_t x_ = GRN_UINT8_VALUE(x);\
+ DO_EQ_SUB;\
+ }\
+ break;\
+ case GRN_DB_INT16 :\
+ {\
+ int16_t x_ = GRN_INT16_VALUE(x);\
+ DO_EQ_SUB;\
+ }\
+ break;\
+ case GRN_DB_UINT16 :\
+ {\
+ uint16_t x_ = GRN_UINT16_VALUE(x);\
+ DO_EQ_SUB;\
+ }\
+ break;\
+ case GRN_DB_INT32 :\
+ {\
+ int32_t x_ = GRN_INT32_VALUE(x);\
+ DO_EQ_SUB;\
+ }\
+ break;\
+ case GRN_DB_UINT32 :\
+ {\
+ uint32_t x_ = GRN_UINT32_VALUE(x);\
+ DO_EQ_SUB;\
+ }\
+ break;\
+ case GRN_DB_INT64 :\
+ {\
+ int64_t x_ = GRN_INT64_VALUE(x);\
+ DO_EQ_SUB;\
+ }\
+ break;\
+ case GRN_DB_TIME :\
+ {\
+ int64_t x_ = GRN_INT64_VALUE(x);\
+ switch (y->header.domain) {\
+ case GRN_DB_INT32 :\
+ r = (x_ == GRN_TIME_PACK(GRN_INT32_VALUE(y), 0));\
+ break;\
+ case GRN_DB_UINT32 :\
+ r = (x_ == GRN_TIME_PACK(GRN_UINT32_VALUE(y), 0));\
+ break;\
+ case GRN_DB_INT64 :\
+ case GRN_DB_TIME :\
+ r = (x_ == GRN_INT64_VALUE(y));\
+ break;\
+ case GRN_DB_UINT64 :\
+ r = (x_ == GRN_UINT64_VALUE(y));\
+ break;\
+ case GRN_DB_FLOAT :\
+ r = (x_ == GRN_TIME_PACK(GRN_FLOAT_VALUE(y), 0));\
+ break;\
+ case GRN_DB_SHORT_TEXT :\
+ case GRN_DB_TEXT :\
+ case GRN_DB_LONG_TEXT :\
+ {\
+ const char *p_ = GRN_TEXT_VALUE(y);\
+ int i_ = grn_atoi(p_, p_ + GRN_TEXT_LEN(y), NULL);\
+ r = (x_ == GRN_TIME_PACK(i_, 0));\
+ }\
+ break;\
+ default :\
+ r = 0;\
+ break;\
+ }\
+ }\
+ break;\
+ case GRN_DB_UINT64 :\
+ {\
+ uint64_t x_ = GRN_UINT64_VALUE(x);\
+ DO_EQ_SUB;\
+ }\
+ break;\
+ case GRN_DB_FLOAT :\
+ {\
+ double x_ = GRN_FLOAT_VALUE(x);\
+ switch (y->header.domain) {\
+ case GRN_DB_INT32 :\
+ r = ((x_ <= GRN_INT32_VALUE(y)) && (x_ >= GRN_INT32_VALUE(y)));\
+ break;\
+ case GRN_DB_UINT32 :\
+ r = ((x_ <= GRN_UINT32_VALUE(y)) && (x_ >= GRN_UINT32_VALUE(y)));\
+ break;\
+ case GRN_DB_INT64 :\
+ case GRN_DB_TIME :\
+ r = ((x_ <= GRN_INT64_VALUE(y)) && (x_ >= GRN_INT64_VALUE(y)));\
+ break;\
+ case GRN_DB_UINT64 :\
+ r = ((x_ <= GRN_UINT64_VALUE(y)) && (x_ >= GRN_UINT64_VALUE(y)));\
+ break;\
+ case GRN_DB_FLOAT :\
+ r = ((x_ <= GRN_FLOAT_VALUE(y)) && (x_ >= GRN_FLOAT_VALUE(y)));\
+ break;\
+ case GRN_DB_SHORT_TEXT :\
+ case GRN_DB_TEXT :\
+ case GRN_DB_LONG_TEXT :\
+ {\
+ const char *p_ = GRN_TEXT_VALUE(y);\
+ int i_ = grn_atoi(p_, p_ + GRN_TEXT_LEN(y), NULL);\
+ r = (x_ <= i_ && x_ >= i_);\
+ }\
+ break;\
+ default :\
+ r = 0;\
+ break;\
+ }\
+ }\
+ break;\
+ case GRN_DB_SHORT_TEXT :\
+ case GRN_DB_TEXT :\
+ case GRN_DB_LONG_TEXT :\
+ if (GRN_DB_SHORT_TEXT <= y->header.domain && y->header.domain <= GRN_DB_LONG_TEXT) {\
+ uint32_t la = GRN_TEXT_LEN(x), lb = GRN_TEXT_LEN(y);\
+ r = (la == lb && !memcmp(GRN_TEXT_VALUE(x), GRN_TEXT_VALUE(y), lb));\
+ } else {\
+ const char *q_ = GRN_TEXT_VALUE(x);\
+ int x_ = grn_atoi(q_, q_ + GRN_TEXT_LEN(x), NULL);\
+ DO_EQ_SUB;\
+ }\
+ break;\
+ default :\
+ if ((x->header.domain == y->header.domain)) {\
+ r = (GRN_BULK_VSIZE(x) == GRN_BULK_VSIZE(y) &&\
+ !(memcmp(GRN_BULK_HEAD(x), GRN_BULK_HEAD(y), GRN_BULK_VSIZE(x))));\
+ } else {\
+ grn_obj dest;\
+ if (x->header.domain < y->header.domain) {\
+ GRN_OBJ_INIT(&dest, GRN_BULK, 0, y->header.domain);\
+ if (!grn_obj_cast(ctx, x, &dest, GRN_FALSE)) {\
+ r = (GRN_BULK_VSIZE(&dest) == GRN_BULK_VSIZE(y) &&\
+ !memcmp(GRN_BULK_HEAD(&dest), GRN_BULK_HEAD(y), GRN_BULK_VSIZE(y))); \
+ }\
+ } else {\
+ GRN_OBJ_INIT(&dest, GRN_BULK, 0, x->header.domain);\
+ if (!grn_obj_cast(ctx, y, &dest, GRN_FALSE)) {\
+ r = (GRN_BULK_VSIZE(&dest) == GRN_BULK_VSIZE(x) &&\
+ !memcmp(GRN_BULK_HEAD(&dest), GRN_BULK_HEAD(x), GRN_BULK_VSIZE(x))); \
+ }\
+ }\
+ GRN_OBJ_FIN(ctx, &dest);\
+ }\
+ break;\
+ }\
+} while (0)
+
+#define GEO_RESOLUTION 3600000
+#define GEO_RADIOUS 6357303
+#define GEO_BES_C1 6334834
+#define GEO_BES_C2 6377397
+#define GEO_BES_C3 0.006674
+#define GEO_GRS_C1 6335439
+#define GEO_GRS_C2 6378137
+#define GEO_GRS_C3 0.006694
+#define GEO_INT2RAD(x) ((M_PI * x) / (GEO_RESOLUTION * 180))
+
+#define VAR_SET_VALUE(ctx,var,value) do {\
+ if (GRN_DB_OBJP(value)) {\
+ (var)->header.type = GRN_PTR;\
+ (var)->header.domain = DB_OBJ(value)->id;\
+ GRN_PTR_SET(ctx, (var), (value));\
+ } else {\
+ (var)->header.type = (value)->header.type;\
+ (var)->header.domain = (value)->header.domain;\
+ GRN_TEXT_SET(ctx, (var), GRN_TEXT_VALUE(value), GRN_TEXT_LEN(value));\
+ }\
+} while (0)
+
+grn_rc
+grn_proc_call(grn_ctx *ctx, grn_obj *proc, int nargs, grn_obj *caller)
+{
+ grn_proc_ctx pctx;
+ grn_obj *obj = NULL, **args;
+ grn_proc *p = (grn_proc *)proc;
+ if (nargs > ctx->impl->stack_curr) { return GRN_INVALID_ARGUMENT; }
+ GRN_API_ENTER;
+ args = ctx->impl->stack + ctx->impl->stack_curr - nargs;
+ pctx.proc = p;
+ pctx.caller = caller;
+ pctx.user_data.ptr = NULL;
+ if (p->funcs[PROC_INIT]) {
+ obj = p->funcs[PROC_INIT](ctx, nargs, args, &pctx.user_data);
+ }
+ pctx.phase = PROC_NEXT;
+ if (p->funcs[PROC_NEXT]) {
+ obj = p->funcs[PROC_NEXT](ctx, nargs, args, &pctx.user_data);
+ }
+ pctx.phase = PROC_FIN;
+ if (p->funcs[PROC_FIN]) {
+ obj = p->funcs[PROC_FIN](ctx, nargs, args, &pctx.user_data);
+ }
+ ctx->impl->stack_curr -= nargs;
+ grn_ctx_push(ctx, obj);
+ GRN_API_RETURN(ctx->rc);
+}
+
+#define PUSH1(v) do {\
+ if (EXPRVP(v)) { vp++; }\
+ s1 = s0;\
+ *sp++ = s0 = v;\
+} while (0)
+
+#define POP1(v) do {\
+ if (EXPRVP(s0)) { vp--; }\
+ v = s0;\
+ s0 = s1;\
+ sp--;\
+ if (sp < s_) { ERR(GRN_INVALID_ARGUMENT, "stack underflow"); goto exit; }\
+ s1 = sp[-2];\
+} while (0)
+
+#define ALLOC1(value) do {\
+ s1 = s0;\
+ *sp++ = s0 = value = vp++;\
+ if (vp - e->values > e->values_tail) { e->values_tail = vp - e->values; }\
+} while (0)
+
+#define POP1ALLOC1(arg,value) do {\
+ arg = s0;\
+ if (EXPRVP(s0)) {\
+ value = s0;\
+ } else {\
+ if (sp < s_ + 1) { ERR(GRN_INVALID_ARGUMENT, "stack underflow"); goto exit; }\
+ sp[-1] = s0 = value = vp++;\
+ s0->header.impl_flags |= GRN_OBJ_EXPRVALUE;\
+ }\
+} while (0)
+
+#define POP2ALLOC1(arg1,arg2,value) do {\
+ if (EXPRVP(s0)) { vp--; }\
+ if (EXPRVP(s1)) { vp--; }\
+ arg2 = s0;\
+ arg1 = s1;\
+ sp--;\
+ if (sp < s_ + 1) { ERR(GRN_INVALID_ARGUMENT, "stack underflow"); goto exit; }\
+ s1 = sp[-2];\
+ sp[-1] = s0 = value = vp++;\
+ s0->header.impl_flags |= GRN_OBJ_EXPRVALUE;\
+} while (0)
+
+#define INTEGER_ARITHMETIC_OPERATION_PLUS(x, y) ((x) + (y))
+#define FLOAT_ARITHMETIC_OPERATION_PLUS(x, y) ((double)(x) + (double)(y))
+#define INTEGER_ARITHMETIC_OPERATION_MINUS(x, y) ((x) - (y))
+#define FLOAT_ARITHMETIC_OPERATION_MINUS(x, y) ((double)(x) - (double)(y))
+#define INTEGER_ARITHMETIC_OPERATION_STAR(x, y) ((x) * (y))
+#define FLOAT_ARITHMETIC_OPERATION_STAR(x, y) ((double)(x) * (double)(y))
+#define INTEGER_ARITHMETIC_OPERATION_SLASH(x, y) ((x) / (y))
+#define FLOAT_ARITHMETIC_OPERATION_SLASH(x, y) ((double)(x) / (double)(y))
+#define INTEGER_ARITHMETIC_OPERATION_MOD(x, y) ((x) % (y))
+#define FLOAT_ARITHMETIC_OPERATION_MOD(x, y) (fmod((x), (y)))
+#define INTEGER_ARITHMETIC_OPERATION_SHIFTL(x, y) ((x) << (y))
+#define FLOAT_ARITHMETIC_OPERATION_SHIFTL(x, y) \
+ ((long long int)(x) << (long long int)(y))
+#define INTEGER_ARITHMETIC_OPERATION_SHIFTR(x, y) ((x) >> (y))
+#define FLOAT_ARITHMETIC_OPERATION_SHIFTR(x, y) \
+ ((long long int)(x) >> (long long int)(y))
+#define INTEGER8_ARITHMETIC_OPERATION_SHIFTRR(x, y) \
+ ((uint8_t)(x) >> (y))
+#define INTEGER16_ARITHMETIC_OPERATION_SHIFTRR(x, y) \
+ ((uint16_t)(x) >> (y))
+#define INTEGER32_ARITHMETIC_OPERATION_SHIFTRR(x, y) \
+ ((unsigned int)(x) >> (y))
+#define INTEGER64_ARITHMETIC_OPERATION_SHIFTRR(x, y) \
+ ((long long unsigned int)(x) >> (y))
+#define FLOAT_ARITHMETIC_OPERATION_SHIFTRR(x, y) \
+ ((long long unsigned int)(x) >> (long long unsigned int)(y))
+
+#define INTEGER_ARITHMETIC_OPERATION_BITWISE_OR(x, y) ((x) | (y))
+#define FLOAT_ARITHMETIC_OPERATION_BITWISE_OR(x, y) \
+ ((long long int)(x) | (long long int)(y))
+#define INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR(x, y) ((x) ^ (y))
+#define FLOAT_ARITHMETIC_OPERATION_BITWISE_XOR(x, y) \
+ ((long long int)(x) ^ (long long int)(y))
+#define INTEGER_ARITHMETIC_OPERATION_BITWISE_AND(x, y) ((x) & (y))
+#define FLOAT_ARITHMETIC_OPERATION_BITWISE_AND(x, y) \
+ ((long long int)(x) & (long long int)(y))
+
+#define INTEGER_UNARY_ARITHMETIC_OPERATION_MINUS(x) (-(x))
+#define FLOAT_UNARY_ARITHMETIC_OPERATION_MINUS(x) (-(x))
+#define INTEGER_UNARY_ARITHMETIC_OPERATION_BITWISE_NOT(x) (~(x))
+#define FLOAT_UNARY_ARITHMETIC_OPERATION_BITWISE_NOT(x) \
+ (~((long long int)(x)))
+
+#define TEXT_ARITHMETIC_OPERATION(operator) do { \
+ long long int x_; \
+ long long int y_; \
+ \
+ res->header.domain = GRN_DB_INT64; \
+ \
+ GRN_INT64_SET(ctx, res, 0); \
+ grn_obj_cast(ctx, x, res, GRN_FALSE); \
+ x_ = GRN_INT64_VALUE(res); \
+ \
+ GRN_INT64_SET(ctx, res, 0); \
+ grn_obj_cast(ctx, y, res, GRN_FALSE); \
+ y_ = GRN_INT64_VALUE(res); \
+ \
+ GRN_INT64_SET(ctx, res, x_ operator y_); \
+} while (0)
+
+#define TEXT_UNARY_ARITHMETIC_OPERATION(unary_operator) do { \
+ long long int x_; \
+ \
+ res->header.domain = GRN_DB_INT64; \
+ \
+ GRN_INT64_SET(ctx, res, 0); \
+ grn_obj_cast(ctx, x, res, GRN_FALSE); \
+ x_ = GRN_INT64_VALUE(res); \
+ \
+ GRN_INT64_SET(ctx, res, unary_operator x_); \
+} while (0)
+
+#define ARITHMETIC_OPERATION_NO_CHECK(y) do {} while (0)
+#define ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y) do { \
+ if ((long long int)y == 0) { \
+ ERR(GRN_INVALID_ARGUMENT, "divisor should not be 0"); \
+ goto exit; \
+ } \
+} while (0)
+
+
+#define NUMERIC_ARITHMETIC_OPERATION_DISPATCH(set, get, x_, y, res, \
+ integer_operation, \
+ float_operation, \
+ right_expression_check, \
+ invalid_type_error) do { \
+ switch (y->header.domain) { \
+ case GRN_DB_INT8 : \
+ { \
+ int8_t y_; \
+ y_ = GRN_INT8_VALUE(y); \
+ right_expression_check(y_); \
+ set(ctx, res, integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_UINT8 : \
+ { \
+ uint8_t y_; \
+ y_ = GRN_UINT8_VALUE(y); \
+ right_expression_check(y_); \
+ set(ctx, res, integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_INT16 : \
+ { \
+ int16_t y_; \
+ y_ = GRN_INT16_VALUE(y); \
+ right_expression_check(y_); \
+ set(ctx, res, integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_UINT16 : \
+ { \
+ uint16_t y_; \
+ y_ = GRN_UINT16_VALUE(y); \
+ right_expression_check(y_); \
+ set(ctx, res, integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_INT32 : \
+ { \
+ int y_; \
+ y_ = GRN_INT32_VALUE(y); \
+ right_expression_check(y_); \
+ set(ctx, res, integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_UINT32 : \
+ { \
+ unsigned int y_; \
+ y_ = GRN_UINT32_VALUE(y); \
+ right_expression_check(y_); \
+ set(ctx, res, integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_TIME : \
+ { \
+ long long int y_; \
+ y_ = GRN_TIME_VALUE(y); \
+ right_expression_check(y_); \
+ set(ctx, res, integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_INT64 : \
+ { \
+ long long int y_; \
+ y_ = GRN_INT64_VALUE(y); \
+ right_expression_check(y_); \
+ set(ctx, res, integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_UINT64 : \
+ { \
+ long long unsigned int y_; \
+ y_ = GRN_UINT64_VALUE(y); \
+ right_expression_check(y_); \
+ set(ctx, res, integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_FLOAT : \
+ { \
+ double y_; \
+ y_ = GRN_FLOAT_VALUE(y); \
+ right_expression_check(y_); \
+ res->header.domain = GRN_DB_FLOAT; \
+ GRN_FLOAT_SET(ctx, res, float_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_SHORT_TEXT : \
+ case GRN_DB_TEXT : \
+ case GRN_DB_LONG_TEXT : \
+ set(ctx, res, 0); \
+ if (grn_obj_cast(ctx, y, res, GRN_FALSE)) { \
+ ERR(GRN_INVALID_ARGUMENT, \
+ "not a numerical format: <%.*s>", \
+ (int)GRN_TEXT_LEN(y), GRN_TEXT_VALUE(y)); \
+ goto exit; \
+ } \
+ set(ctx, res, integer_operation(x_, get(res))); \
+ break; \
+ default : \
+ invalid_type_error; \
+ break; \
+ } \
+} while (0)
+
+
+#define ARITHMETIC_OPERATION_DISPATCH(x, y, res, \
+ integer8_operation, \
+ integer16_operation, \
+ integer32_operation, \
+ integer64_operation, \
+ float_operation, \
+ left_expression_check, \
+ right_expression_check, \
+ text_operation, \
+ invalid_type_error) do { \
+ switch (x->header.domain) { \
+ case GRN_DB_INT8 : \
+ { \
+ int8_t x_; \
+ x_ = GRN_INT8_VALUE(x); \
+ left_expression_check(x_); \
+ NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_INT8_SET, \
+ GRN_INT8_VALUE, \
+ x_, y, res, \
+ integer8_operation, \
+ float_operation, \
+ right_expression_check, \
+ invalid_type_error); \
+ } \
+ break; \
+ case GRN_DB_UINT8 : \
+ { \
+ uint8_t x_; \
+ x_ = GRN_UINT8_VALUE(x); \
+ left_expression_check(x_); \
+ NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_UINT8_SET, \
+ GRN_UINT8_VALUE, \
+ x_, y, res, \
+ integer8_operation, \
+ float_operation, \
+ right_expression_check, \
+ invalid_type_error); \
+ } \
+ break; \
+ case GRN_DB_INT16 : \
+ { \
+ int16_t x_; \
+ x_ = GRN_INT16_VALUE(x); \
+ left_expression_check(x_); \
+ NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_INT16_SET, \
+ GRN_INT16_VALUE, \
+ x_, y, res, \
+ integer16_operation, \
+ float_operation, \
+ right_expression_check, \
+ invalid_type_error); \
+ } \
+ break; \
+ case GRN_DB_UINT16 : \
+ { \
+ uint16_t x_; \
+ x_ = GRN_UINT16_VALUE(x); \
+ left_expression_check(x_); \
+ NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_UINT16_SET, \
+ GRN_UINT16_VALUE, \
+ x_, y, res, \
+ integer16_operation, \
+ float_operation, \
+ right_expression_check, \
+ invalid_type_error); \
+ } \
+ break; \
+ case GRN_DB_INT32 : \
+ { \
+ int x_; \
+ x_ = GRN_INT32_VALUE(x); \
+ left_expression_check(x_); \
+ NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_INT32_SET, \
+ GRN_INT32_VALUE, \
+ x_, y, res, \
+ integer32_operation, \
+ float_operation, \
+ right_expression_check, \
+ invalid_type_error); \
+ } \
+ break; \
+ case GRN_DB_UINT32 : \
+ { \
+ unsigned int x_; \
+ x_ = GRN_UINT32_VALUE(x); \
+ left_expression_check(x_); \
+ NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_UINT32_SET, \
+ GRN_UINT32_VALUE, \
+ x_, y, res, \
+ integer32_operation, \
+ float_operation, \
+ right_expression_check, \
+ invalid_type_error); \
+ } \
+ break; \
+ case GRN_DB_INT64 : \
+ { \
+ long long int x_; \
+ x_ = GRN_INT64_VALUE(x); \
+ left_expression_check(x_); \
+ NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_UINT64_SET, \
+ GRN_UINT64_VALUE, \
+ x_, y, res, \
+ integer64_operation, \
+ float_operation, \
+ right_expression_check, \
+ invalid_type_error); \
+ } \
+ break; \
+ case GRN_DB_TIME : \
+ { \
+ long long int x_; \
+ x_ = GRN_TIME_VALUE(x); \
+ left_expression_check(x_); \
+ NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_TIME_SET, \
+ GRN_TIME_VALUE, \
+ x_, y, res, \
+ integer64_operation, \
+ float_operation, \
+ right_expression_check, \
+ invalid_type_error); \
+ } \
+ break; \
+ case GRN_DB_UINT64 : \
+ { \
+ long long unsigned int x_; \
+ x_ = GRN_UINT64_VALUE(x); \
+ left_expression_check(x_); \
+ NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_UINT64_SET, \
+ GRN_UINT64_VALUE, \
+ x_, y, res, \
+ integer64_operation, \
+ float_operation, \
+ right_expression_check, \
+ invalid_type_error); \
+ } \
+ break; \
+ case GRN_DB_FLOAT : \
+ { \
+ double x_; \
+ x_ = GRN_FLOAT_VALUE(x); \
+ left_expression_check(x_); \
+ NUMERIC_ARITHMETIC_OPERATION_DISPATCH(GRN_FLOAT_SET, \
+ GRN_FLOAT_VALUE, \
+ x_, y, res, \
+ float_operation, \
+ float_operation, \
+ right_expression_check, \
+ invalid_type_error); \
+ } \
+ break; \
+ case GRN_DB_SHORT_TEXT : \
+ case GRN_DB_TEXT : \
+ case GRN_DB_LONG_TEXT : \
+ text_operation; \
+ break; \
+ default: \
+ invalid_type_error; \
+ break; \
+ } \
+ code++; \
+} while (0)
+
+#define ARITHMETIC_BINARY_OPERATION_DISPATCH(integer8_operation, \
+ integer16_operation, \
+ integer32_operation, \
+ integer64_operation, \
+ float_operation, \
+ left_expression_check, \
+ right_expression_check, \
+ text_operation, \
+ invalid_type_error) do { \
+ grn_obj *x, *y; \
+ \
+ POP2ALLOC1(x, y, res); \
+ if (y != res) { \
+ res->header.domain = x->header.domain; \
+ } \
+ ARITHMETIC_OPERATION_DISPATCH(x, y, res, \
+ integer8_operation, \
+ integer16_operation, \
+ integer32_operation, \
+ integer64_operation, \
+ float_operation, \
+ left_expression_check, \
+ right_expression_check, \
+ text_operation, \
+ invalid_type_error); \
+ if (y == res) { \
+ res->header.domain = x->header.domain; \
+ } \
+} while (0)
+
+#define SIGNED_INTEGER_DIVISION_OPERATION_SLASH(x, y) \
+ ((y == -1) ? -(x) : (x) / (y))
+#define UNSIGNED_INTEGER_DIVISION_OPERATION_SLASH(x, y) ((x) / (y))
+#define FLOAT_DIVISION_OPERATION_SLASH(x, y) ((double)(x) / (double)(y))
+#define SIGNED_INTEGER_DIVISION_OPERATION_MOD(x, y) ((y == -1) ? 0 : (x) % (y))
+#define UNSIGNED_INTEGER_DIVISION_OPERATION_MOD(x, y) ((x) % (y))
+#define FLOAT_DIVISION_OPERATION_MOD(x, y) (fmod((x), (y)))
+
+#define DIVISION_OPERATION_DISPATCH_RIGHT(set, get, x_, y, res, \
+ signed_integer_operation, \
+ unsigned_integer_operation, \
+ float_operation) do { \
+ switch (y->header.domain) { \
+ case GRN_DB_INT8 : \
+ { \
+ int y_; \
+ y_ = GRN_INT8_VALUE(y); \
+ ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_); \
+ set(ctx, res, signed_integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_UINT8 : \
+ { \
+ int y_; \
+ y_ = GRN_UINT8_VALUE(y); \
+ ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_); \
+ set(ctx, res, signed_integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_INT16 : \
+ { \
+ int y_; \
+ y_ = GRN_INT16_VALUE(y); \
+ ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_); \
+ set(ctx, res, signed_integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_UINT16 : \
+ { \
+ int y_; \
+ y_ = GRN_UINT16_VALUE(y); \
+ ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_); \
+ set(ctx, res, signed_integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_INT32 : \
+ { \
+ int y_; \
+ y_ = GRN_INT32_VALUE(y); \
+ ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_); \
+ set(ctx, res, signed_integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_UINT32 : \
+ { \
+ unsigned int y_; \
+ y_ = GRN_UINT32_VALUE(y); \
+ ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_); \
+ set(ctx, res, unsigned_integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_TIME : \
+ { \
+ long long int y_; \
+ y_ = GRN_TIME_VALUE(y); \
+ ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_); \
+ set(ctx, res, signed_integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_INT64 : \
+ { \
+ long long int y_; \
+ y_ = GRN_INT64_VALUE(y); \
+ ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_); \
+ set(ctx, res, signed_integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_UINT64 : \
+ { \
+ long long unsigned int y_; \
+ y_ = GRN_UINT64_VALUE(y); \
+ ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_); \
+ set(ctx, res, unsigned_integer_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_FLOAT : \
+ { \
+ double y_; \
+ y_ = GRN_FLOAT_VALUE(y); \
+ ARITHMETIC_OPERATION_ZERO_DIVISION_CHECK(y_); \
+ res->header.domain = GRN_DB_FLOAT; \
+ GRN_FLOAT_SET(ctx, res, float_operation(x_, y_)); \
+ } \
+ break; \
+ case GRN_DB_SHORT_TEXT : \
+ case GRN_DB_TEXT : \
+ case GRN_DB_LONG_TEXT : \
+ set(ctx, res, 0); \
+ if (grn_obj_cast(ctx, y, res, GRN_FALSE)) { \
+ ERR(GRN_INVALID_ARGUMENT, \
+ "not a numerical format: <%.*s>", \
+ (int)GRN_TEXT_LEN(y), GRN_TEXT_VALUE(y)); \
+ goto exit; \
+ } \
+ /* The following "+ 0" is needed to suppress warnings that say */ \
+ /* comparison is always false due to limited range of data type */ \
+ set(ctx, res, signed_integer_operation(x_, (get(res) + 0))); \
+ break; \
+ default : \
+ break; \
+ } \
+} while (0)
+
+#define DIVISION_OPERATION_DISPATCH_LEFT(x, y, res, \
+ signed_integer_operation, \
+ unsigned_integer_operation, \
+ float_operation, \
+ invalid_type_error) do { \
+ switch (x->header.domain) { \
+ case GRN_DB_INT8 : \
+ { \
+ int x_; \
+ x_ = GRN_INT8_VALUE(x); \
+ DIVISION_OPERATION_DISPATCH_RIGHT(GRN_INT8_SET, \
+ GRN_INT8_VALUE, \
+ x_, y, res, \
+ signed_integer_operation, \
+ unsigned_integer_operation, \
+ float_operation); \
+ } \
+ break; \
+ case GRN_DB_UINT8 : \
+ { \
+ int x_; \
+ x_ = GRN_UINT8_VALUE(x); \
+ DIVISION_OPERATION_DISPATCH_RIGHT(GRN_UINT8_SET, \
+ (int)GRN_UINT8_VALUE, \
+ x_, y, res, \
+ signed_integer_operation, \
+ unsigned_integer_operation, \
+ float_operation); \
+ } \
+ break; \
+ case GRN_DB_INT16 : \
+ { \
+ int x_; \
+ x_ = GRN_INT16_VALUE(x); \
+ DIVISION_OPERATION_DISPATCH_RIGHT(GRN_INT16_SET, \
+ GRN_INT16_VALUE, \
+ x_, y, res, \
+ signed_integer_operation, \
+ unsigned_integer_operation, \
+ float_operation); \
+ } \
+ break; \
+ case GRN_DB_UINT16 : \
+ { \
+ int x_; \
+ x_ = GRN_UINT16_VALUE(x); \
+ DIVISION_OPERATION_DISPATCH_RIGHT(GRN_UINT16_SET, \
+ (int)GRN_UINT16_VALUE, \
+ x_, y, res, \
+ signed_integer_operation, \
+ unsigned_integer_operation, \
+ float_operation); \
+ } \
+ break; \
+ case GRN_DB_INT32 : \
+ { \
+ int x_; \
+ x_ = GRN_INT32_VALUE(x); \
+ DIVISION_OPERATION_DISPATCH_RIGHT(GRN_INT32_SET, \
+ GRN_INT32_VALUE, \
+ x_, y, res, \
+ signed_integer_operation, \
+ unsigned_integer_operation, \
+ float_operation); \
+ } \
+ break; \
+ case GRN_DB_UINT32 : \
+ { \
+ unsigned int x_; \
+ x_ = GRN_UINT32_VALUE(x); \
+ DIVISION_OPERATION_DISPATCH_RIGHT(GRN_UINT32_SET, \
+ GRN_UINT32_VALUE, \
+ x_, y, res, \
+ unsigned_integer_operation, \
+ unsigned_integer_operation, \
+ float_operation); \
+ } \
+ break; \
+ case GRN_DB_INT64 : \
+ { \
+ long long int x_; \
+ x_ = GRN_INT64_VALUE(x); \
+ DIVISION_OPERATION_DISPATCH_RIGHT(GRN_INT64_SET, \
+ GRN_INT64_VALUE, \
+ x_, y, res, \
+ signed_integer_operation, \
+ unsigned_integer_operation, \
+ float_operation); \
+ } \
+ break; \
+ case GRN_DB_TIME : \
+ { \
+ long long int x_; \
+ x_ = GRN_TIME_VALUE(x); \
+ DIVISION_OPERATION_DISPATCH_RIGHT(GRN_TIME_SET, \
+ GRN_TIME_VALUE, \
+ x_, y, res, \
+ signed_integer_operation, \
+ unsigned_integer_operation, \
+ float_operation); \
+ } \
+ break; \
+ case GRN_DB_UINT64 : \
+ { \
+ long long unsigned int x_; \
+ x_ = GRN_UINT64_VALUE(x); \
+ DIVISION_OPERATION_DISPATCH_RIGHT(GRN_UINT64_SET, \
+ GRN_UINT64_VALUE, \
+ x_, y, res, \
+ unsigned_integer_operation, \
+ unsigned_integer_operation, \
+ float_operation); \
+ } \
+ break; \
+ case GRN_DB_FLOAT : \
+ { \
+ double x_; \
+ x_ = GRN_FLOAT_VALUE(x); \
+ DIVISION_OPERATION_DISPATCH_RIGHT(GRN_FLOAT_SET, \
+ GRN_FLOAT_VALUE, \
+ x_, y, res, \
+ float_operation, \
+ float_operation, \
+ float_operation); \
+ } \
+ break; \
+ case GRN_DB_SHORT_TEXT : \
+ case GRN_DB_TEXT : \
+ case GRN_DB_LONG_TEXT : \
+ invalid_type_error; \
+ break; \
+ default: \
+ break; \
+ } \
+ code++; \
+} while (0)
+
+#define DIVISION_OPERATION_DISPATCH(signed_integer_operation, \
+ unsigned_integer_operation, \
+ float_operation, \
+ invalid_type_error) do { \
+ grn_obj *x, *y; \
+ \
+ POP2ALLOC1(x, y, res); \
+ if (y != res) { \
+ res->header.domain = x->header.domain; \
+ } \
+ DIVISION_OPERATION_DISPATCH_LEFT(x, y, res, \
+ signed_integer_operation, \
+ unsigned_integer_operation, \
+ float_operation, \
+ invalid_type_error); \
+ if (y == res) { \
+ res->header.domain = x->header.domain; \
+ } \
+} while (0)
+
+#define ARITHMETIC_UNARY_OPERATION_DISPATCH(integer_operation, \
+ float_operation, \
+ left_expression_check, \
+ right_expression_check, \
+ text_operation, \
+ invalid_type_error) do { \
+ grn_obj *x; \
+ POP1ALLOC1(x, res); \
+ res->header.domain = x->header.domain; \
+ switch (x->header.domain) { \
+ case GRN_DB_INT8 : \
+ { \
+ int8_t x_; \
+ x_ = GRN_INT8_VALUE(x); \
+ left_expression_check(x_); \
+ GRN_INT8_SET(ctx, res, integer_operation(x_)); \
+ } \
+ break; \
+ case GRN_DB_UINT8 : \
+ { \
+ int16_t x_; \
+ x_ = GRN_UINT8_VALUE(x); \
+ left_expression_check(x_); \
+ GRN_INT16_SET(ctx, res, integer_operation(x_)); \
+ res->header.domain = GRN_DB_INT16; \
+ } \
+ break; \
+ case GRN_DB_INT16 : \
+ { \
+ int16_t x_; \
+ x_ = GRN_INT16_VALUE(x); \
+ left_expression_check(x_); \
+ GRN_INT16_SET(ctx, res, integer_operation(x_)); \
+ } \
+ break; \
+ case GRN_DB_UINT16 : \
+ { \
+ int x_; \
+ x_ = GRN_UINT16_VALUE(x); \
+ left_expression_check(x_); \
+ GRN_INT32_SET(ctx, res, integer_operation(x_)); \
+ res->header.domain = GRN_DB_INT32; \
+ } \
+ break; \
+ case GRN_DB_INT32 : \
+ { \
+ int x_; \
+ x_ = GRN_INT32_VALUE(x); \
+ left_expression_check(x_); \
+ GRN_INT32_SET(ctx, res, integer_operation(x_)); \
+ } \
+ break; \
+ case GRN_DB_UINT32 : \
+ { \
+ long long int x_; \
+ x_ = GRN_UINT32_VALUE(x); \
+ left_expression_check(x_); \
+ GRN_INT64_SET(ctx, res, integer_operation(x_)); \
+ res->header.domain = GRN_DB_INT64; \
+ } \
+ break; \
+ case GRN_DB_INT64 : \
+ { \
+ long long int x_; \
+ x_ = GRN_INT64_VALUE(x); \
+ left_expression_check(x_); \
+ GRN_INT64_SET(ctx, res, integer_operation(x_)); \
+ } \
+ break; \
+ case GRN_DB_TIME : \
+ { \
+ long long int x_; \
+ x_ = GRN_TIME_VALUE(x); \
+ left_expression_check(x_); \
+ GRN_TIME_SET(ctx, res, integer_operation(x_)); \
+ } \
+ break; \
+ case GRN_DB_UINT64 : \
+ { \
+ long long unsigned int x_; \
+ x_ = GRN_UINT64_VALUE(x); \
+ left_expression_check(x_); \
+ if (x_ > (long long unsigned int)INT64_MAX) { \
+ ERR(GRN_INVALID_ARGUMENT, \
+ "too large UInt64 value to inverse sign: " \
+ "<%" GRN_FMT_LLU ">", \
+ x_); \
+ goto exit; \
+ } else { \
+ long long int signed_x_; \
+ signed_x_ = x_; \
+ GRN_INT64_SET(ctx, res, integer_operation(signed_x_)); \
+ res->header.domain = GRN_DB_INT64; \
+ } \
+ } \
+ break; \
+ case GRN_DB_FLOAT : \
+ { \
+ double x_; \
+ x_ = GRN_FLOAT_VALUE(x); \
+ left_expression_check(x_); \
+ GRN_FLOAT_SET(ctx, res, float_operation(x_)); \
+ } \
+ break; \
+ case GRN_DB_SHORT_TEXT : \
+ case GRN_DB_TEXT : \
+ case GRN_DB_LONG_TEXT : \
+ text_operation; \
+ break; \
+ default: \
+ invalid_type_error; \
+ break; \
+ } \
+ code++; \
+} while (0)
+
+#define EXEC_OPERATE(operate_sentence, assign_sentence) \
+ operate_sentence \
+ assign_sentence
+
+#define EXEC_OPERATE_POST(operate_sentence, assign_sentence) \
+ assign_sentence \
+ operate_sentence
+
+#define UNARY_OPERATE_AND_ASSIGN_DISPATCH(exec_operate, delta, \
+ set_flags) do { \
+ grn_obj *var, *col, value; \
+ grn_id rid; \
+ \
+ POP1ALLOC1(var, res); \
+ if (var->header.type != GRN_PTR) { \
+ ERR(GRN_INVALID_ARGUMENT, "invalid variable type: 0x%0x", \
+ var->header.type); \
+ goto exit; \
+ } \
+ if (GRN_BULK_VSIZE(var) != (sizeof(grn_obj *) + sizeof(grn_id))) { \
+ ERR(GRN_INVALID_ARGUMENT, \
+ "invalid variable size: expected: %zu, actual: %zu", \
+ (sizeof(grn_obj *) + sizeof(grn_id)), GRN_BULK_VSIZE(var)); \
+ goto exit; \
+ } \
+ col = GRN_PTR_VALUE(var); \
+ rid = *(grn_id *)(GRN_BULK_HEAD(var) + sizeof(grn_obj *)); \
+ res->header.type = GRN_VOID; \
+ res->header.domain = DB_OBJ(col)->range; \
+ switch (DB_OBJ(col)->range) { \
+ case GRN_DB_INT32 : \
+ GRN_INT32_INIT(&value, 0); \
+ GRN_INT32_SET(ctx, &value, delta); \
+ break; \
+ case GRN_DB_UINT32 : \
+ GRN_UINT32_INIT(&value, 0); \
+ GRN_UINT32_SET(ctx, &value, delta); \
+ break; \
+ case GRN_DB_INT64 : \
+ GRN_INT64_INIT(&value, 0); \
+ GRN_INT64_SET(ctx, &value, delta); \
+ break; \
+ case GRN_DB_UINT64 : \
+ GRN_UINT64_INIT(&value, 0); \
+ GRN_UINT64_SET(ctx, &value, delta); \
+ break; \
+ case GRN_DB_FLOAT : \
+ GRN_FLOAT_INIT(&value, 0); \
+ GRN_FLOAT_SET(ctx, &value, delta); \
+ break; \
+ case GRN_DB_TIME : \
+ GRN_TIME_INIT(&value, 0); \
+ GRN_TIME_SET(ctx, &value, GRN_TIME_PACK(delta, 0)); \
+ break; \
+ default: \
+ ERR(GRN_INVALID_ARGUMENT, \
+ "invalid increment target type: %d " \
+ "(FIXME: type name is needed)", DB_OBJ(col)->range); \
+ goto exit; \
+ break; \
+ } \
+ exec_operate(grn_obj_set_value(ctx, col, rid, &value, set_flags);, \
+ grn_obj_get_value(ctx, col, rid, res);); \
+ code++; \
+} while (0)
+
+#define ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(integer8_operation, \
+ integer16_operation, \
+ integer32_operation, \
+ integer64_operation, \
+ float_operation, \
+ left_expression_check, \
+ right_expression_check,\
+ text_operation) do { \
+ grn_obj *value, *var, *res; \
+ if (code->value) { \
+ value = code->value; \
+ POP1ALLOC1(var, res); \
+ } else { \
+ POP2ALLOC1(var, value, res); \
+ } \
+ if (var->header.type == GRN_PTR && \
+ GRN_BULK_VSIZE(var) == (sizeof(grn_obj *) + sizeof(grn_id))) { \
+ grn_obj *col = GRN_PTR_VALUE(var); \
+ grn_id rid = *(grn_id *)(GRN_BULK_HEAD(var) + sizeof(grn_obj *)); \
+ grn_obj variable_value, casted_value; \
+ grn_id domain; \
+ \
+ value = GRN_OBJ_RESOLVE(ctx, value); \
+ \
+ domain = grn_obj_get_range(ctx, col); \
+ GRN_OBJ_INIT(&variable_value, GRN_BULK, 0, domain); \
+ grn_obj_get_value(ctx, col, rid, &variable_value); \
+ \
+ GRN_OBJ_INIT(&casted_value, GRN_BULK, 0, domain); \
+ if (grn_obj_cast(ctx, value, &casted_value, GRN_FALSE)) { \
+ ERR(GRN_INVALID_ARGUMENT, "invalid value: string"); \
+ GRN_OBJ_FIN(ctx, &variable_value); \
+ GRN_OBJ_FIN(ctx, &casted_value); \
+ POP1(res); \
+ goto exit; \
+ } \
+ grn_obj_reinit(ctx, res, domain, 0); \
+ ARITHMETIC_OPERATION_DISPATCH((&variable_value), (&casted_value), \
+ res, \
+ integer8_operation, \
+ integer16_operation, \
+ integer32_operation, \
+ integer64_operation, \
+ float_operation, \
+ left_expression_check, \
+ right_expression_check, \
+ text_operation,); \
+ grn_obj_set_value(ctx, col, rid, res, GRN_OBJ_SET); \
+ GRN_OBJ_FIN(ctx, (&variable_value)); \
+ GRN_OBJ_FIN(ctx, (&casted_value)); \
+ } else { \
+ ERR(GRN_INVALID_ARGUMENT, "left hand expression isn't column."); \
+ POP1(res); \
+ } \
+} while (0)
+
+static grn_bool
+pseudo_query_scan(grn_ctx *ctx, grn_obj *x, grn_obj *y)
+{
+ grn_obj *normalizer;
+ grn_obj *a = NULL, *b = NULL;
+ grn_bool matched = GRN_FALSE;
+
+ normalizer = grn_ctx_get(ctx, GRN_NORMALIZER_AUTO_NAME, -1);
+ switch (x->header.domain) {
+ case GRN_DB_SHORT_TEXT:
+ case GRN_DB_TEXT:
+ case GRN_DB_LONG_TEXT:
+ a = grn_string_open(ctx, GRN_TEXT_VALUE(x), GRN_TEXT_LEN(x),
+ normalizer, 0);
+ break;
+ default:
+ break;
+ }
+
+ switch (y->header.domain) {
+ case GRN_DB_SHORT_TEXT:
+ case GRN_DB_TEXT:
+ case GRN_DB_LONG_TEXT:
+ b = grn_string_open(ctx, GRN_TEXT_VALUE(y), GRN_TEXT_LEN(y),
+ normalizer, 0);
+ break;
+ default:
+ break;
+ }
+
+ /* normalized str doesn't contain '\0'. */
+ if (a && b) {
+ const char *a_norm, *b_norm;
+ grn_string_get_normalized(ctx, a, &a_norm, NULL, NULL);
+ grn_string_get_normalized(ctx, b, &b_norm, NULL, NULL);
+ matched = (strstr(a_norm, b_norm) != NULL);
+ }
+
+ if (a) { grn_obj_close(ctx, a); }
+ if (b) { grn_obj_close(ctx, b); }
+
+ if (normalizer) { grn_obj_unlink(ctx, normalizer); }
+
+ return matched;
+}
+
+inline static void
+grn_expr_exec_get_member(grn_ctx *ctx,
+ grn_obj *expr,
+ grn_obj *column_and_record_id,
+ grn_obj *index,
+ grn_obj *result)
+{
+ grn_obj *column;
+ grn_id record_id;
+ grn_obj values;
+ int i;
+
+ column = GRN_PTR_VALUE(column_and_record_id);
+ record_id = *((grn_id *)(&(GRN_PTR_VALUE_AT(column_and_record_id, 1))));
+ GRN_TEXT_INIT(&values, 0);
+ grn_obj_get_value(ctx, column, record_id, &values);
+
+ grn_obj_reinit(ctx, result, DB_OBJ(column)->range, 0);
+ i = GRN_UINT32_VALUE(index);
+ if (values.header.type == GRN_UVECTOR) {
+ int n_elements;
+ n_elements = GRN_BULK_VSIZE(&values) / sizeof(grn_id);
+ if (n_elements > i) {
+ grn_id value;
+ value = GRN_RECORD_VALUE_AT(&values, i);
+ GRN_RECORD_SET(ctx, result, value);
+ }
+ } else {
+ if (values.u.v.n_sections > i) {
+ grn_section *section = &(values.u.v.sections[i]);
+ grn_obj *body = values.u.v.body;
+ const char *value;
+ value = GRN_BULK_HEAD(body) + section->offset;
+ grn_bulk_write(ctx, result, value, section->length);
+ }
+ }
+
+ GRN_OBJ_FIN(ctx, &values);
+}
+
+grn_obj *
+grn_expr_exec(grn_ctx *ctx, grn_obj *expr, int nargs)
+{
+ grn_obj *val = NULL;
+ uint32_t stack_curr = ctx->impl->stack_curr;
+ GRN_API_ENTER;
+ if (expr->header.type == GRN_PROC) {
+ grn_proc_call(ctx, expr, nargs, expr);
+ } else {
+ grn_expr *e = (grn_expr *)expr;
+ register grn_obj **s_ = ctx->impl->stack, *s0 = NULL, *s1 = NULL, **sp, *vp = e->values;
+ grn_obj *res = NULL, *v0 = grn_expr_get_var_by_offset(ctx, expr, 0);
+ grn_expr_code *code = e->codes, *ce = &e->codes[e->codes_curr];
+ sp = s_ + stack_curr;
+ while (code < ce) {
+ switch (code->op) {
+ case GRN_OP_NOP :
+ code++;
+ break;
+ case GRN_OP_PUSH :
+ PUSH1(code->value);
+ code++;
+ break;
+ case GRN_OP_POP :
+ {
+ grn_obj *obj;
+ POP1(obj);
+ code++;
+ }
+ break;
+ case GRN_OP_GET_REF :
+ {
+ grn_obj *col, *rec;
+ if (code->nargs == 1) {
+ rec = v0;
+ if (code->value) {
+ col = code->value;
+ ALLOC1(res);
+ } else {
+ POP1ALLOC1(col, res);
+ }
+ } else {
+ if (code->value) {
+ col = code->value;
+ POP1ALLOC1(rec, res);
+ } else {
+ POP2ALLOC1(rec, col, res);
+ }
+ }
+ if (col->header.type == GRN_BULK) {
+ grn_obj *table = grn_ctx_at(ctx, GRN_OBJ_GET_DOMAIN(rec));
+ col = grn_obj_column(ctx, table, GRN_BULK_HEAD(col), GRN_BULK_VSIZE(col));
+ if (col) { GRN_PTR_PUT(ctx, &e->objs, col); }
+ }
+ if (col) {
+ res->header.type = GRN_PTR;
+ res->header.domain = GRN_ID_NIL;
+ GRN_PTR_SET(ctx, res, col);
+ GRN_UINT32_PUT(ctx, res, GRN_RECORD_VALUE(rec));
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "col resolve failed");
+ goto exit;
+ }
+ code++;
+ }
+ break;
+ case GRN_OP_CALL :
+ {
+ grn_obj *proc;
+ if (code->value) {
+ if (sp < s_ + code->nargs) {
+ ERR(GRN_INVALID_ARGUMENT, "stack error");
+ goto exit;
+ }
+ proc = code->value;
+ WITH_SPSAVE({
+ grn_proc_call(ctx, proc, code->nargs, expr);
+ });
+ } else {
+ int offset = code->nargs + 1;
+ if (sp < s_ + offset) {
+ ERR(GRN_INVALID_ARGUMENT, "stack error");
+ goto exit;
+ }
+ proc = sp[-offset];
+ WITH_SPSAVE({
+ grn_proc_call(ctx, proc, code->nargs, expr);
+ });
+ if (ctx->rc) {
+ goto exit;
+ }
+ POP1(res);
+ {
+ grn_obj *proc_;
+ POP1(proc_);
+ if (proc != proc_) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "stack may be corrupt");
+ }
+ }
+ PUSH1(res);
+ }
+ }
+ code++;
+ break;
+ case GRN_OP_INTERN :
+ {
+ grn_obj *obj;
+ POP1(obj);
+ obj = GRN_OBJ_RESOLVE(ctx, obj);
+ res = grn_expr_get_var(ctx, expr, GRN_TEXT_VALUE(obj), GRN_TEXT_LEN(obj));
+ if (!res) { res = grn_ctx_get(ctx, GRN_TEXT_VALUE(obj), GRN_TEXT_LEN(obj)); }
+ if (!res) {
+ ERR(GRN_INVALID_ARGUMENT, "intern failed");
+ goto exit;
+ }
+ PUSH1(res);
+ }
+ code++;
+ break;
+ case GRN_OP_TABLE_CREATE :
+ {
+ grn_obj *value_type, *key_type, *flags, *name;
+ POP1(value_type);
+ value_type = GRN_OBJ_RESOLVE(ctx, value_type);
+ POP1(key_type);
+ key_type = GRN_OBJ_RESOLVE(ctx, key_type);
+ POP1(flags);
+ flags = GRN_OBJ_RESOLVE(ctx, flags);
+ POP1(name);
+ name = GRN_OBJ_RESOLVE(ctx, name);
+ res = grn_table_create(ctx, GRN_TEXT_VALUE(name), GRN_TEXT_LEN(name),
+ NULL, GRN_UINT32_VALUE(flags),
+ key_type, value_type);
+ PUSH1(res);
+ }
+ code++;
+ break;
+ case GRN_OP_EXPR_GET_VAR :
+ {
+ grn_obj *name, *expr;
+ POP1(name);
+ name = GRN_OBJ_RESOLVE(ctx, name);
+ POP1(expr);
+ expr = GRN_OBJ_RESOLVE(ctx, expr);
+ switch (name->header.domain) {
+ case GRN_DB_INT32 :
+ res = grn_expr_get_var_by_offset(ctx, expr, (unsigned int) GRN_INT32_VALUE(name));
+ break;
+ case GRN_DB_UINT32 :
+ res = grn_expr_get_var_by_offset(ctx, expr, (unsigned int) GRN_UINT32_VALUE(name));
+ break;
+ case GRN_DB_INT64 :
+ res = grn_expr_get_var_by_offset(ctx, expr, (unsigned int) GRN_INT64_VALUE(name));
+ break;
+ case GRN_DB_UINT64 :
+ res = grn_expr_get_var_by_offset(ctx, expr, (unsigned int) GRN_UINT64_VALUE(name));
+ break;
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ res = grn_expr_get_var(ctx, expr, GRN_TEXT_VALUE(name), GRN_TEXT_LEN(name));
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "invalid type");
+ goto exit;
+ }
+ PUSH1(res);
+ }
+ code++;
+ break;
+ case GRN_OP_ASSIGN :
+ {
+ grn_obj *value, *var;
+ if (code->value) {
+ value = code->value;
+ } else {
+ POP1(value);
+ }
+ value = GRN_OBJ_RESOLVE(ctx, value);
+ POP1(var);
+ // var = GRN_OBJ_RESOLVE(ctx, var);
+ if (var->header.type == GRN_PTR &&
+ GRN_BULK_VSIZE(var) == (sizeof(grn_obj *) + sizeof(grn_id))) {
+ grn_obj *col = GRN_PTR_VALUE(var);
+ grn_id rid = *(grn_id *)(GRN_BULK_HEAD(var) + sizeof(grn_obj *));
+ grn_obj_set_value(ctx, col, rid, value, GRN_OBJ_SET);
+ } else {
+ VAR_SET_VALUE(ctx, var, value);
+ }
+ PUSH1(value);
+ }
+ code++;
+ break;
+ case GRN_OP_STAR_ASSIGN :
+ ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_STAR,
+ INTEGER_ARITHMETIC_OPERATION_STAR,
+ INTEGER_ARITHMETIC_OPERATION_STAR,
+ INTEGER_ARITHMETIC_OPERATION_STAR,
+ FLOAT_ARITHMETIC_OPERATION_STAR,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ ERR(GRN_INVALID_ARGUMENT, "variable *= \"string\" isn't supported");
+ goto exit;
+ });
+ break;
+ case GRN_OP_SLASH_ASSIGN :
+ ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_SLASH,
+ INTEGER_ARITHMETIC_OPERATION_SLASH,
+ INTEGER_ARITHMETIC_OPERATION_SLASH,
+ INTEGER_ARITHMETIC_OPERATION_SLASH,
+ FLOAT_ARITHMETIC_OPERATION_SLASH,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ ERR(GRN_INVALID_ARGUMENT, "variable /= \"string\" isn't supported");
+ goto exit;
+ });
+ break;
+ case GRN_OP_MOD_ASSIGN :
+ ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_MOD,
+ INTEGER_ARITHMETIC_OPERATION_MOD,
+ INTEGER_ARITHMETIC_OPERATION_MOD,
+ INTEGER_ARITHMETIC_OPERATION_MOD,
+ FLOAT_ARITHMETIC_OPERATION_MOD,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ ERR(GRN_INVALID_ARGUMENT, "variable %%= \"string\" isn't supported");
+ goto exit;
+ });
+ break;
+ case GRN_OP_PLUS_ASSIGN :
+ ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_PLUS,
+ INTEGER_ARITHMETIC_OPERATION_PLUS,
+ INTEGER_ARITHMETIC_OPERATION_PLUS,
+ INTEGER_ARITHMETIC_OPERATION_PLUS,
+ FLOAT_ARITHMETIC_OPERATION_PLUS,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ ERR(GRN_INVALID_ARGUMENT, "variable += \"string\" isn't supported");
+ goto exit;
+ });
+ break;
+ case GRN_OP_MINUS_ASSIGN :
+ ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_MINUS,
+ INTEGER_ARITHMETIC_OPERATION_MINUS,
+ INTEGER_ARITHMETIC_OPERATION_MINUS,
+ INTEGER_ARITHMETIC_OPERATION_MINUS,
+ FLOAT_ARITHMETIC_OPERATION_MINUS,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ ERR(GRN_INVALID_ARGUMENT, "variable -= \"string\" isn't supported");
+ goto exit;
+ });
+ break;
+ case GRN_OP_SHIFTL_ASSIGN :
+ ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_SHIFTL,
+ INTEGER_ARITHMETIC_OPERATION_SHIFTL,
+ INTEGER_ARITHMETIC_OPERATION_SHIFTL,
+ INTEGER_ARITHMETIC_OPERATION_SHIFTL,
+ FLOAT_ARITHMETIC_OPERATION_SHIFTL,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ ERR(GRN_INVALID_ARGUMENT, "variable <<= \"string\" isn't supported");
+ goto exit;
+ });
+ break;
+ case GRN_OP_SHIFTR_ASSIGN :
+ ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_SHIFTR,
+ INTEGER_ARITHMETIC_OPERATION_SHIFTR,
+ INTEGER_ARITHMETIC_OPERATION_SHIFTR,
+ INTEGER_ARITHMETIC_OPERATION_SHIFTR,
+ FLOAT_ARITHMETIC_OPERATION_SHIFTR,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ ERR(GRN_INVALID_ARGUMENT, "variable >>= \"string\" isn't supported");
+ goto exit;
+ });
+ break;
+ case GRN_OP_SHIFTRR_ASSIGN :
+ ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
+ INTEGER8_ARITHMETIC_OPERATION_SHIFTRR,
+ INTEGER16_ARITHMETIC_OPERATION_SHIFTRR,
+ INTEGER32_ARITHMETIC_OPERATION_SHIFTRR,
+ INTEGER64_ARITHMETIC_OPERATION_SHIFTRR,
+ FLOAT_ARITHMETIC_OPERATION_SHIFTRR,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ ERR(GRN_INVALID_ARGUMENT,
+ "variable >>>= \"string\" isn't supported");
+ goto exit;
+ });
+ break;
+ case GRN_OP_AND_ASSIGN :
+ ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
+ FLOAT_ARITHMETIC_OPERATION_BITWISE_AND,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ ERR(GRN_INVALID_ARGUMENT, "variable &= \"string\" isn't supported");
+ goto exit;
+ });
+ break;
+ case GRN_OP_OR_ASSIGN :
+ ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
+ FLOAT_ARITHMETIC_OPERATION_BITWISE_OR,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ ERR(GRN_INVALID_ARGUMENT, "variable |= \"string\" isn't supported");
+ goto exit;
+ });
+ break;
+ case GRN_OP_XOR_ASSIGN :
+ ARITHMETIC_OPERATION_AND_ASSIGN_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
+ FLOAT_ARITHMETIC_OPERATION_BITWISE_XOR,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ ERR(GRN_INVALID_ARGUMENT, "variable ^= \"string\" isn't supported");
+ goto exit;
+ });
+ break;
+ case GRN_OP_JUMP :
+ code += code->nargs + 1;
+ break;
+ case GRN_OP_CJUMP :
+ {
+ grn_obj *v;
+ unsigned int v_boolean;
+ POP1(v);
+ GRN_TRUEP(ctx, v, v_boolean);
+ if (!v_boolean) { code += code->nargs; }
+ }
+ code++;
+ break;
+ case GRN_OP_GET_VALUE :
+ {
+ grn_obj *col, *rec;
+ grn_obj pat_value;
+ GRN_TEXT_INIT(&pat_value, 0);
+ do {
+ uint32_t size;
+ const char *value;
+ grn_bool is_pat_key_accessor = GRN_FALSE;
+ if (code->nargs == 1) {
+ rec = v0;
+ if (code->value) {
+ col = code->value;
+ ALLOC1(res);
+ } else {
+ POP1ALLOC1(col, res);
+ }
+ } else {
+ if (code->value) {
+ col = code->value;
+ POP1ALLOC1(rec, res);
+ } else {
+ POP2ALLOC1(rec, col, res);
+ }
+ }
+ if (col->header.type == GRN_BULK) {
+ grn_obj *table = grn_ctx_at(ctx, GRN_OBJ_GET_DOMAIN(rec));
+ col = grn_obj_column(ctx, table, GRN_BULK_HEAD(col), GRN_BULK_VSIZE(col));
+ if (col) { GRN_PTR_PUT(ctx, &e->objs, col); }
+ }
+ if (col) {
+ grn_obj_reinit_for(ctx, res, col);
+ if (col->header.type == GRN_ACCESSOR &&
+ ((grn_accessor *)col)->action == GRN_ACCESSOR_GET_KEY &&
+ ((grn_accessor *)col)->obj->header.type == GRN_TABLE_PAT_KEY) {
+ is_pat_key_accessor = GRN_TRUE;
+ GRN_BULK_REWIND(&pat_value);
+ grn_obj_get_value(ctx, col, GRN_RECORD_VALUE(rec), &pat_value);
+ value = GRN_BULK_HEAD(&pat_value);
+ size = GRN_BULK_VSIZE(&pat_value);
+ } else {
+ value = grn_obj_get_value_(ctx, col, GRN_RECORD_VALUE(rec),
+ &size);
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "col resolve failed");
+ GRN_OBJ_FIN(ctx, &pat_value);
+ goto exit;
+ }
+ if (!is_pat_key_accessor && size == GRN_OBJ_GET_VALUE_IMD) {
+ GRN_RECORD_SET(ctx, res, (uintptr_t)value);
+ } else {
+ if (value) {
+ if (res->header.type == GRN_VECTOR) {
+ grn_vector_decode(ctx, res, value, size);
+ } else {
+ grn_bulk_write_from(ctx, res, value, 0, size);
+ }
+ }
+ }
+ code++;
+ } while (code < ce && code->op == GRN_OP_GET_VALUE);
+ GRN_OBJ_FIN(ctx, &pat_value);
+ }
+ break;
+ case GRN_OP_OBJ_SEARCH :
+ {
+ grn_obj *op, *query, *index;
+ // todo : grn_search_optarg optarg;
+ POP1(op);
+ op = GRN_OBJ_RESOLVE(ctx, op);
+ POP1(res);
+ res = GRN_OBJ_RESOLVE(ctx, res);
+ POP1(query);
+ query = GRN_OBJ_RESOLVE(ctx, query);
+ POP1(index);
+ index = GRN_OBJ_RESOLVE(ctx, index);
+ grn_obj_search(ctx, index, query, res,
+ (grn_operator)GRN_UINT32_VALUE(op), NULL);
+ }
+ code++;
+ break;
+ case GRN_OP_TABLE_SELECT :
+ {
+ grn_obj *op, *res, *expr, *table;
+ POP1(op);
+ op = GRN_OBJ_RESOLVE(ctx, op);
+ POP1(res);
+ res = GRN_OBJ_RESOLVE(ctx, res);
+ POP1(expr);
+ expr = GRN_OBJ_RESOLVE(ctx, expr);
+ POP1(table);
+ table = GRN_OBJ_RESOLVE(ctx, table);
+ WITH_SPSAVE({
+ grn_table_select(ctx, table, expr, res, (grn_operator)GRN_UINT32_VALUE(op));
+ });
+ PUSH1(res);
+ }
+ code++;
+ break;
+ case GRN_OP_TABLE_SORT :
+ {
+ grn_obj *keys_, *res, *limit, *table;
+ POP1(keys_);
+ keys_ = GRN_OBJ_RESOLVE(ctx, keys_);
+ POP1(res);
+ res = GRN_OBJ_RESOLVE(ctx, res);
+ POP1(limit);
+ limit = GRN_OBJ_RESOLVE(ctx, limit);
+ POP1(table);
+ table = GRN_OBJ_RESOLVE(ctx, table);
+ {
+ grn_table_sort_key *keys;
+ const char *p = GRN_BULK_HEAD(keys_), *tokbuf[256];
+ int n = grn_str_tok(p, GRN_BULK_VSIZE(keys_), ' ', tokbuf, 256, NULL);
+ if ((keys = GRN_MALLOCN(grn_table_sort_key, n))) {
+ int i, n_keys = 0;
+ for (i = 0; i < n; i++) {
+ uint32_t len = (uint32_t) (tokbuf[i] - p);
+ grn_obj *col = grn_obj_column(ctx, table, p, len);
+ if (col) {
+ keys[n_keys].key = col;
+ keys[n_keys].flags = GRN_TABLE_SORT_ASC;
+ keys[n_keys].offset = 0;
+ n_keys++;
+ } else {
+ if (p[0] == ':' && p[1] == 'd' && len == 2 && n_keys) {
+ keys[n_keys - 1].flags |= GRN_TABLE_SORT_DESC;
+ }
+ }
+ p = tokbuf[i] + 1;
+ }
+ WITH_SPSAVE({
+ grn_table_sort(ctx, table, 0, GRN_INT32_VALUE(limit), res, keys, n_keys);
+ });
+ for (i = 0; i < n_keys; i++) {
+ grn_obj_unlink(ctx, keys[i].key);
+ }
+ GRN_FREE(keys);
+ }
+ }
+ }
+ code++;
+ break;
+ case GRN_OP_TABLE_GROUP :
+ {
+ grn_obj *res, *keys_, *table;
+ POP1(res);
+ res = GRN_OBJ_RESOLVE(ctx, res);
+ POP1(keys_);
+ keys_ = GRN_OBJ_RESOLVE(ctx, keys_);
+ POP1(table);
+ table = GRN_OBJ_RESOLVE(ctx, table);
+ {
+ grn_table_sort_key *keys;
+ grn_table_group_result results;
+ const char *p = GRN_BULK_HEAD(keys_), *tokbuf[256];
+ int n = grn_str_tok(p, GRN_BULK_VSIZE(keys_), ' ', tokbuf, 256, NULL);
+ if ((keys = GRN_MALLOCN(grn_table_sort_key, n))) {
+ int i, n_keys = 0;
+ for (i = 0; i < n; i++) {
+ uint32_t len = (uint32_t) (tokbuf[i] - p);
+ grn_obj *col = grn_obj_column(ctx, table, p, len);
+ if (col) {
+ keys[n_keys].key = col;
+ keys[n_keys].flags = GRN_TABLE_SORT_ASC;
+ keys[n_keys].offset = 0;
+ n_keys++;
+ } else if (n_keys) {
+ if (p[0] == ':' && p[1] == 'd' && len == 2) {
+ keys[n_keys - 1].flags |= GRN_TABLE_SORT_DESC;
+ } else {
+ keys[n_keys - 1].offset = grn_atoi(p, p + len, NULL);
+ }
+ }
+ p = tokbuf[i] + 1;
+ }
+ /* todo : support multi-results */
+ results.table = res;
+ results.key_begin = 0;
+ results.key_end = 0;
+ results.limit = 0;
+ results.flags = 0;
+ results.op = GRN_OP_OR;
+ WITH_SPSAVE({
+ grn_table_group(ctx, table, keys, n_keys, &results, 1);
+ });
+ for (i = 0; i < n_keys; i++) {
+ grn_obj_unlink(ctx, keys[i].key);
+ }
+ GRN_FREE(keys);
+ }
+ }
+ }
+ code++;
+ break;
+ case GRN_OP_JSON_PUT :
+ {
+ grn_obj_format format;
+ grn_obj *str, *table, *res;
+ POP1(res);
+ res = GRN_OBJ_RESOLVE(ctx, res);
+ POP1(str);
+ str = GRN_OBJ_RESOLVE(ctx, str);
+ POP1(table);
+ table = GRN_OBJ_RESOLVE(ctx, table);
+ GRN_OBJ_FORMAT_INIT(&format, grn_table_size(ctx, table), 0, -1, 0);
+ format.flags = 0;
+ grn_obj_columns(ctx, table,
+ GRN_TEXT_VALUE(str), GRN_TEXT_LEN(str), &format.columns);
+ grn_text_otoj(ctx, res, table, &format);
+ GRN_OBJ_FORMAT_FIN(ctx, &format);
+ }
+ code++;
+ break;
+ case GRN_OP_AND :
+ {
+ grn_obj *x, *y;
+ unsigned int x_boolean, y_boolean;
+ grn_obj *result = NULL;
+ POP2ALLOC1(x, y, res);
+ GRN_TRUEP(ctx, x, x_boolean);
+ if (x_boolean) {
+ GRN_TRUEP(ctx, y, y_boolean);
+ if (y_boolean) {
+ result = y;
+ }
+ }
+ if (result) {
+ if (res != result) {
+ grn_obj_reinit(ctx, res, result->header.domain, 0);
+ grn_obj_cast(ctx, result, res, GRN_FALSE);
+ }
+ } else {
+ grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
+ GRN_BOOL_SET(ctx, res, GRN_FALSE);
+ }
+ }
+ code++;
+ break;
+ case GRN_OP_OR :
+ {
+ grn_obj *x, *y;
+ unsigned int x_boolean, y_boolean;
+ grn_obj *result;
+ POP2ALLOC1(x, y, res);
+ GRN_TRUEP(ctx, x, x_boolean);
+ if (x_boolean) {
+ result = x;
+ } else {
+ GRN_TRUEP(ctx, y, y_boolean);
+ if (y_boolean) {
+ result = y;
+ } else {
+ result = NULL;
+ }
+ }
+ if (result) {
+ if (res != result) {
+ grn_obj_reinit(ctx, res, result->header.domain, 0);
+ grn_obj_cast(ctx, result, res, GRN_FALSE);
+ }
+ } else {
+ grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
+ GRN_BOOL_SET(ctx, res, GRN_FALSE);
+ }
+ }
+ code++;
+ break;
+ case GRN_OP_AND_NOT :
+ {
+ grn_obj *x, *y;
+ POP2ALLOC1(x, y, res);
+ if (GRN_INT32_VALUE(x) == 0 || GRN_INT32_VALUE(y) == 1) {
+ GRN_INT32_SET(ctx, res, 0);
+ } else {
+ GRN_INT32_SET(ctx, res, 1);
+ }
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_INT32;
+ }
+ code++;
+ break;
+ case GRN_OP_ADJUST :
+ {
+ /* todo */
+ }
+ code++;
+ break;
+ case GRN_OP_MATCH :
+ {
+ grn_obj *x, *y;
+ grn_bool matched;
+ POP1(y);
+ POP1(x);
+ WITH_SPSAVE({
+ matched = pseudo_query_scan(ctx, x, y);
+ });
+ ALLOC1(res);
+ grn_obj_reinit(ctx, res, GRN_DB_INT32, 0);
+ GRN_INT32_SET(ctx, res, matched ? 1 : 0);
+ }
+ code++;
+ break;
+ case GRN_OP_EQUAL :
+ {
+ int r = GRN_FALSE;
+ grn_obj *x, *y;
+ POP2ALLOC1(x, y, res);
+ DO_EQ(x, y, r);
+ grn_obj_reinit(ctx, res, GRN_DB_INT32, 0);
+ GRN_INT32_SET(ctx, res, r);
+ }
+ code++;
+ break;
+ case GRN_OP_NOT_EQUAL :
+ {
+ int r = GRN_FALSE;
+ grn_obj *x, *y;
+ POP2ALLOC1(x, y, res);
+ DO_EQ(x, y, r);
+ grn_obj_reinit(ctx, res, GRN_DB_INT32, 0);
+ GRN_INT32_SET(ctx, res, 1 - r);
+ }
+ code++;
+ break;
+ case GRN_OP_PREFIX :
+ {
+ grn_obj *x, *y;
+ POP2ALLOC1(x, y, res);
+ GRN_INT32_SET(ctx, res,
+ (GRN_TEXT_LEN(x) >= GRN_TEXT_LEN(y) &&
+ !memcmp(GRN_TEXT_VALUE(x), GRN_TEXT_VALUE(y), GRN_TEXT_LEN(y))));
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_INT32;
+ }
+ code++;
+ break;
+ case GRN_OP_SUFFIX :
+ {
+ grn_obj *x, *y;
+ POP2ALLOC1(x, y, res);
+ GRN_INT32_SET(ctx, res,
+ (GRN_TEXT_LEN(x) >= GRN_TEXT_LEN(y) &&
+ !memcmp(GRN_TEXT_VALUE(x) + GRN_TEXT_LEN(x) - GRN_TEXT_LEN(y),
+ GRN_TEXT_VALUE(y), GRN_TEXT_LEN(y))));
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_INT32;
+ }
+ code++;
+ break;
+ case GRN_OP_LESS :
+ {
+ int r;
+ grn_obj *x, *y;
+ POP2ALLOC1(x, y, res);
+ DO_COMPARE(x, y, r, <);
+ GRN_INT32_SET(ctx, res, r);
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_INT32;
+ }
+ code++;
+ break;
+ case GRN_OP_GREATER :
+ {
+ int r;
+ grn_obj *x, *y;
+ POP2ALLOC1(x, y, res);
+ DO_COMPARE(x, y, r, >);
+ GRN_INT32_SET(ctx, res, r);
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_INT32;
+ }
+ code++;
+ break;
+ case GRN_OP_LESS_EQUAL :
+ {
+ int r;
+ grn_obj *x, *y;
+ POP2ALLOC1(x, y, res);
+ DO_COMPARE(x, y, r, <=);
+ GRN_INT32_SET(ctx, res, r);
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_INT32;
+ }
+ code++;
+ break;
+ case GRN_OP_GREATER_EQUAL :
+ {
+ int r;
+ grn_obj *x, *y;
+ POP2ALLOC1(x, y, res);
+ DO_COMPARE(x, y, r, >=);
+ GRN_INT32_SET(ctx, res, r);
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_INT32;
+ }
+ code++;
+ break;
+ case GRN_OP_GEO_DISTANCE1 :
+ {
+ grn_obj *e;
+ double lng1, lat1, lng2, lat2, x, y, d;
+ POP1(e);
+ lng1 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lat1 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lng2 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1ALLOC1(e, res);
+ lat2 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ x = (lng2 - lng1) * cos((lat1 + lat2) * 0.5);
+ y = (lat2 - lat1);
+ d = sqrt((x * x) + (y * y)) * GEO_RADIOUS;
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_FLOAT;
+ GRN_FLOAT_SET(ctx, res, d);
+ }
+ code++;
+ break;
+ case GRN_OP_GEO_DISTANCE2 :
+ {
+ grn_obj *e;
+ double lng1, lat1, lng2, lat2, x, y, d;
+ POP1(e);
+ lng1 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lat1 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lng2 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1ALLOC1(e, res);
+ lat2 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ x = sin(fabs(lng2 - lng1) * 0.5);
+ y = sin(fabs(lat2 - lat1) * 0.5);
+ d = asin(sqrt((y * y) + cos(lat1) * cos(lat2) * x * x)) * 2 * GEO_RADIOUS;
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_FLOAT;
+ GRN_FLOAT_SET(ctx, res, d);
+ }
+ code++;
+ break;
+ case GRN_OP_GEO_DISTANCE3 :
+ {
+ grn_obj *e;
+ double lng1, lat1, lng2, lat2, p, q, m, n, x, y, d;
+ POP1(e);
+ lng1 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lat1 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lng2 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1ALLOC1(e, res);
+ lat2 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ p = (lat1 + lat2) * 0.5;
+ q = (1 - GEO_BES_C3 * sin(p) * sin(p));
+ m = GEO_BES_C1 / sqrt(q * q * q);
+ n = GEO_BES_C2 / sqrt(q);
+ x = n * cos(p) * fabs(lng1 - lng2);
+ y = m * fabs(lat1 - lat2);
+ d = sqrt((x * x) + (y * y));
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_FLOAT;
+ GRN_FLOAT_SET(ctx, res, d);
+ }
+ code++;
+ break;
+ case GRN_OP_GEO_DISTANCE4 :
+ {
+ grn_obj *e;
+ double lng1, lat1, lng2, lat2, p, q, m, n, x, y, d;
+ POP1(e);
+ lng1 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lat1 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lng2 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1ALLOC1(e, res);
+ lat2 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ p = (lat1 + lat2) * 0.5;
+ q = (1 - GEO_GRS_C3 * sin(p) * sin(p));
+ m = GEO_GRS_C1 / sqrt(q * q * q);
+ n = GEO_GRS_C2 / sqrt(q);
+ x = n * cos(p) * fabs(lng1 - lng2);
+ y = m * fabs(lat1 - lat2);
+ d = sqrt((x * x) + (y * y));
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_FLOAT;
+ GRN_FLOAT_SET(ctx, res, d);
+ }
+ code++;
+ break;
+ case GRN_OP_GEO_WITHINP5 :
+ {
+ int r;
+ grn_obj *e;
+ double lng0, lat0, lng1, lat1, x, y, d;
+ POP1(e);
+ lng0 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lat0 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lng1 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lat1 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1ALLOC1(e, res);
+ x = (lng1 - lng0) * cos((lat0 + lat1) * 0.5);
+ y = (lat1 - lat0);
+ d = sqrt((x * x) + (y * y)) * GEO_RADIOUS;
+ switch (e->header.domain) {
+ case GRN_DB_INT32 :
+ r = d <= GRN_INT32_VALUE(e);
+ break;
+ case GRN_DB_FLOAT :
+ r = d <= GRN_FLOAT_VALUE(e);
+ break;
+ default :
+ r = 0;
+ break;
+ }
+ GRN_INT32_SET(ctx, res, r);
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_INT32;
+ }
+ code++;
+ break;
+ case GRN_OP_GEO_WITHINP6 :
+ {
+ int r;
+ grn_obj *e;
+ double lng0, lat0, lng1, lat1, lng2, lat2, x, y, d;
+ POP1(e);
+ lng0 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lat0 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lng1 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lat1 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1(e);
+ lng2 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ POP1ALLOC1(e, res);
+ lat2 = GEO_INT2RAD(GRN_INT32_VALUE(e));
+ x = (lng1 - lng0) * cos((lat0 + lat1) * 0.5);
+ y = (lat1 - lat0);
+ d = (x * x) + (y * y);
+ x = (lng2 - lng1) * cos((lat1 + lat2) * 0.5);
+ y = (lat2 - lat1);
+ r = d <= (x * x) + (y * y);
+ GRN_INT32_SET(ctx, res, r);
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_INT32;
+ }
+ code++;
+ break;
+ case GRN_OP_GEO_WITHINP8 :
+ {
+ int r;
+ grn_obj *e;
+ int64_t ln0, la0, ln1, la1, ln2, la2, ln3, la3;
+ POP1(e);
+ ln0 = GRN_INT32_VALUE(e);
+ POP1(e);
+ la0 = GRN_INT32_VALUE(e);
+ POP1(e);
+ ln1 = GRN_INT32_VALUE(e);
+ POP1(e);
+ la1 = GRN_INT32_VALUE(e);
+ POP1(e);
+ ln2 = GRN_INT32_VALUE(e);
+ POP1(e);
+ la2 = GRN_INT32_VALUE(e);
+ POP1(e);
+ ln3 = GRN_INT32_VALUE(e);
+ POP1ALLOC1(e, res);
+ la3 = GRN_INT32_VALUE(e);
+ r = ((ln2 <= ln0) && (ln0 <= ln3) && (la2 <= la0) && (la0 <= la3));
+ GRN_INT32_SET(ctx, res, r);
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_INT32;
+ }
+ code++;
+ break;
+ case GRN_OP_PLUS :
+ ARITHMETIC_BINARY_OPERATION_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_PLUS,
+ INTEGER_ARITHMETIC_OPERATION_PLUS,
+ INTEGER_ARITHMETIC_OPERATION_PLUS,
+ INTEGER_ARITHMETIC_OPERATION_PLUS,
+ FLOAT_ARITHMETIC_OPERATION_PLUS,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ if (x == res) {
+ grn_obj_cast(ctx, y, res, GRN_FALSE);
+ } else if (y == res) {
+ grn_obj buffer;
+ GRN_TEXT_INIT(&buffer, 0);
+ grn_obj_cast(ctx, x, &buffer, GRN_FALSE);
+ grn_obj_cast(ctx, y, &buffer, GRN_FALSE);
+ GRN_BULK_REWIND(res);
+ grn_obj_cast(ctx, &buffer, res, GRN_FALSE);
+ GRN_OBJ_FIN(ctx, &buffer);
+ } else {
+ GRN_BULK_REWIND(res);
+ grn_obj_cast(ctx, x, res, GRN_FALSE);
+ grn_obj_cast(ctx, y, res, GRN_FALSE);
+ }
+ }
+ ,);
+ break;
+ case GRN_OP_MINUS :
+ if (code->nargs == 1) {
+ ARITHMETIC_UNARY_OPERATION_DISPATCH(
+ INTEGER_UNARY_ARITHMETIC_OPERATION_MINUS,
+ FLOAT_UNARY_ARITHMETIC_OPERATION_MINUS,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ long long int x_;
+
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_INT64;
+
+ GRN_INT64_SET(ctx, res, 0);
+ grn_obj_cast(ctx, x, res, GRN_FALSE);
+ x_ = GRN_INT64_VALUE(res);
+
+ GRN_INT64_SET(ctx, res, -x_);
+ }
+ ,);
+ } else {
+ ARITHMETIC_BINARY_OPERATION_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_MINUS,
+ INTEGER_ARITHMETIC_OPERATION_MINUS,
+ INTEGER_ARITHMETIC_OPERATION_MINUS,
+ INTEGER_ARITHMETIC_OPERATION_MINUS,
+ FLOAT_ARITHMETIC_OPERATION_MINUS,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ ERR(GRN_INVALID_ARGUMENT,
+ "\"string\" - \"string\" "
+ "isn't supported");
+ goto exit;
+ }
+ ,);
+ }
+ break;
+ case GRN_OP_STAR :
+ ARITHMETIC_BINARY_OPERATION_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_STAR,
+ INTEGER_ARITHMETIC_OPERATION_STAR,
+ INTEGER_ARITHMETIC_OPERATION_STAR,
+ INTEGER_ARITHMETIC_OPERATION_STAR,
+ FLOAT_ARITHMETIC_OPERATION_STAR,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ ERR(GRN_INVALID_ARGUMENT,
+ "\"string\" * \"string\" "
+ "isn't supported");
+ goto exit;
+ }
+ ,);
+ break;
+ case GRN_OP_SLASH :
+ DIVISION_OPERATION_DISPATCH(
+ SIGNED_INTEGER_DIVISION_OPERATION_SLASH,
+ UNSIGNED_INTEGER_DIVISION_OPERATION_SLASH,
+ FLOAT_DIVISION_OPERATION_SLASH,
+ {
+ ERR(GRN_INVALID_ARGUMENT,
+ "\"string\" / \"string\" "
+ "isn't supported");
+ goto exit;
+ });
+ break;
+ case GRN_OP_MOD :
+ DIVISION_OPERATION_DISPATCH(
+ SIGNED_INTEGER_DIVISION_OPERATION_MOD,
+ UNSIGNED_INTEGER_DIVISION_OPERATION_MOD,
+ FLOAT_DIVISION_OPERATION_MOD,
+ {
+ ERR(GRN_INVALID_ARGUMENT,
+ "\"string\" %% \"string\" "
+ "isn't supported");
+ goto exit;
+ });
+ break;
+ case GRN_OP_BITWISE_NOT :
+ ARITHMETIC_UNARY_OPERATION_DISPATCH(
+ INTEGER_UNARY_ARITHMETIC_OPERATION_BITWISE_NOT,
+ FLOAT_UNARY_ARITHMETIC_OPERATION_BITWISE_NOT,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ TEXT_UNARY_ARITHMETIC_OPERATION(~),);
+ break;
+ case GRN_OP_BITWISE_OR :
+ ARITHMETIC_BINARY_OPERATION_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_OR,
+ FLOAT_ARITHMETIC_OPERATION_BITWISE_OR,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ TEXT_ARITHMETIC_OPERATION(|),);
+ break;
+ case GRN_OP_BITWISE_XOR :
+ ARITHMETIC_BINARY_OPERATION_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_XOR,
+ FLOAT_ARITHMETIC_OPERATION_BITWISE_XOR,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ TEXT_ARITHMETIC_OPERATION(^),);
+ break;
+ case GRN_OP_BITWISE_AND :
+ ARITHMETIC_BINARY_OPERATION_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
+ INTEGER_ARITHMETIC_OPERATION_BITWISE_AND,
+ FLOAT_ARITHMETIC_OPERATION_BITWISE_AND,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ TEXT_ARITHMETIC_OPERATION(&),);
+ break;
+ case GRN_OP_SHIFTL :
+ ARITHMETIC_BINARY_OPERATION_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_SHIFTL,
+ INTEGER_ARITHMETIC_OPERATION_SHIFTL,
+ INTEGER_ARITHMETIC_OPERATION_SHIFTL,
+ INTEGER_ARITHMETIC_OPERATION_SHIFTL,
+ FLOAT_ARITHMETIC_OPERATION_SHIFTL,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ TEXT_ARITHMETIC_OPERATION(<<),);
+ break;
+ case GRN_OP_SHIFTR :
+ ARITHMETIC_BINARY_OPERATION_DISPATCH(
+ INTEGER_ARITHMETIC_OPERATION_SHIFTR,
+ INTEGER_ARITHMETIC_OPERATION_SHIFTR,
+ INTEGER_ARITHMETIC_OPERATION_SHIFTR,
+ INTEGER_ARITHMETIC_OPERATION_SHIFTR,
+ FLOAT_ARITHMETIC_OPERATION_SHIFTR,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ TEXT_ARITHMETIC_OPERATION(>>),);
+ break;
+ case GRN_OP_SHIFTRR :
+ ARITHMETIC_BINARY_OPERATION_DISPATCH(
+ INTEGER8_ARITHMETIC_OPERATION_SHIFTRR,
+ INTEGER16_ARITHMETIC_OPERATION_SHIFTRR,
+ INTEGER32_ARITHMETIC_OPERATION_SHIFTRR,
+ INTEGER64_ARITHMETIC_OPERATION_SHIFTRR,
+ FLOAT_ARITHMETIC_OPERATION_SHIFTRR,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ ARITHMETIC_OPERATION_NO_CHECK,
+ {
+ long long unsigned int x_;
+ long long unsigned int y_;
+
+ res->header.type = GRN_BULK;
+ res->header.domain = GRN_DB_INT64;
+
+ GRN_INT64_SET(ctx, res, 0);
+ grn_obj_cast(ctx, x, res, GRN_FALSE);
+ x_ = GRN_INT64_VALUE(res);
+
+ GRN_INT64_SET(ctx, res, 0);
+ grn_obj_cast(ctx, y, res, GRN_FALSE);
+ y_ = GRN_INT64_VALUE(res);
+
+ GRN_INT64_SET(ctx, res, x_ >> y_);
+ }
+ ,);
+ break;
+ case GRN_OP_INCR :
+ UNARY_OPERATE_AND_ASSIGN_DISPATCH(EXEC_OPERATE, 1, GRN_OBJ_INCR);
+ break;
+ case GRN_OP_DECR :
+ UNARY_OPERATE_AND_ASSIGN_DISPATCH(EXEC_OPERATE, 1, GRN_OBJ_DECR);
+ break;
+ case GRN_OP_INCR_POST :
+ UNARY_OPERATE_AND_ASSIGN_DISPATCH(EXEC_OPERATE_POST, 1, GRN_OBJ_INCR);
+ break;
+ case GRN_OP_DECR_POST :
+ UNARY_OPERATE_AND_ASSIGN_DISPATCH(EXEC_OPERATE_POST, 1, GRN_OBJ_DECR);
+ break;
+ case GRN_OP_NOT :
+ {
+ grn_obj *value;
+ POP1ALLOC1(value, res);
+ grn_obj_reinit(ctx, res, GRN_DB_BOOL, 0);
+ grn_obj_cast(ctx, value, res, GRN_FALSE);
+ GRN_BOOL_SET(ctx, res, !GRN_BOOL_VALUE(res));
+ }
+ code++;
+ break;
+ case GRN_OP_GET_MEMBER :
+ {
+ grn_obj *column_and_record_id, *index;
+ POP2ALLOC1(column_and_record_id, index, res);
+ grn_expr_exec_get_member(ctx, expr, column_and_record_id, index, res);
+ code++;
+ }
+ break;
+ default :
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED, "not implemented operator assigned");
+ goto exit;
+ break;
+ }
+ }
+ ctx->impl->stack_curr = sp - s_;
+ }
+ if (ctx->impl->stack_curr + nargs > stack_curr) {
+ val = grn_ctx_pop(ctx);
+ }
+exit :
+ if (ctx->impl->stack_curr + nargs > stack_curr) {
+ /*
+ GRN_LOG(ctx, GRN_LOG_WARNING, "nargs=%d stack balance=%d",
+ nargs, stack_curr - ctx->impl->stack_curr);
+ */
+ ctx->impl->stack_curr = stack_curr - nargs;
+ }
+ GRN_API_RETURN(val);
+}
+
+grn_obj *
+grn_expr_get_value(grn_ctx *ctx, grn_obj *expr, int offset)
+{
+ grn_obj *res = NULL;
+ grn_expr *e = (grn_expr *)expr;
+ GRN_API_ENTER;
+ if (0 <= offset && offset < e->values_size) {
+ res = &e->values[offset];
+ }
+ GRN_API_RETURN(res);
+}
+
+#define DEFAULT_WEIGHT 5
+#define DEFAULT_DECAYSTEP 2
+#define DEFAULT_MAX_INTERVAL 10
+#define DEFAULT_SIMILARITY_THRESHOLD 10
+#define DEFAULT_TERM_EXTRACT_POLICY 0
+#define DEFAULT_WEIGHT_VECTOR_SIZE 4096
+
+struct _grn_scan_info {
+ uint32_t start;
+ uint32_t end;
+ int32_t nargs;
+ int flags;
+ grn_operator op;
+ grn_operator logical_op;
+ grn_obj wv;
+ grn_obj index;
+ grn_obj *query;
+ grn_obj *args[8];
+ int max_interval;
+};
+
+#define SI_FREE(si) do {\
+ GRN_OBJ_FIN(ctx, &(si)->wv);\
+ GRN_OBJ_FIN(ctx, &(si)->index);\
+ GRN_FREE(si);\
+} while (0)
+
+#define SI_ALLOC(si, i, st) do {\
+ if (!((si) = GRN_MALLOCN(scan_info, 1))) {\
+ int j;\
+ for (j = 0; j < i; j++) { SI_FREE(sis[j]); }\
+ GRN_FREE(sis);\
+ return NULL;\
+ }\
+ GRN_INT32_INIT(&(si)->wv, GRN_OBJ_VECTOR);\
+ GRN_PTR_INIT(&(si)->index, GRN_OBJ_VECTOR, GRN_ID_NIL);\
+ (si)->logical_op = GRN_OP_OR;\
+ (si)->flags = SCAN_PUSH;\
+ (si)->nargs = 0;\
+ (si)->max_interval = DEFAULT_MAX_INTERVAL;\
+ (si)->start = (st);\
+} while (0)
+
+static scan_info **
+put_logical_op(grn_ctx *ctx, scan_info **sis, int *ip, grn_operator op, int start)
+{
+ int nparens = 1, ndifops = 0, i = *ip, j = i, r = 0;
+ while (j--) {
+ scan_info *s_ = sis[j];
+ if (s_->flags & SCAN_POP) {
+ ndifops++;
+ nparens++;
+ } else {
+ if (s_->flags & SCAN_PUSH) {
+ if (!(--nparens)) {
+ if (!r) {
+ if (ndifops) {
+ if (j && op != GRN_OP_AND_NOT) {
+ nparens = 1;
+ ndifops = 0;
+ r = j;
+ } else {
+ SI_ALLOC(s_, i, start);
+ s_->flags = SCAN_POP;
+ s_->logical_op = op;
+ sis[i++] = s_;
+ *ip = i;
+ break;
+ }
+ } else {
+ s_->flags &= ~SCAN_PUSH;
+ s_->logical_op = op;
+ break;
+ }
+ } else {
+ if (ndifops) {
+ SI_ALLOC(s_, i, start);
+ s_->flags = SCAN_POP;
+ s_->logical_op = op;
+ sis[i++] = s_;
+ *ip = i;
+ } else {
+ s_->flags &= ~SCAN_PUSH;
+ s_->logical_op = op;
+ memcpy(&sis[i], &sis[j], sizeof(scan_info *) * (r - j));
+ memmove(&sis[j], &sis[r], sizeof(scan_info *) * (i - r));
+ memcpy(&sis[i + j - r], &sis[i], sizeof(scan_info *) * (r - j));
+ }
+ break;
+ }
+ }
+ } else {
+ if ((op == GRN_OP_AND_NOT) || (op != s_->logical_op)) {
+ ndifops++;
+ }
+ }
+ }
+ }
+ if (j < 0) {
+ ERR(GRN_INVALID_ARGUMENT, "unmatched nesting level");
+ for (j = 0; j < i; j++) { SI_FREE(sis[j]); }
+ GRN_FREE(sis);
+ return NULL;
+ }
+ return sis;
+}
+
+
+#define EXPRLOG(name,expr) do {\
+ grn_obj strbuf;\
+ GRN_TEXT_INIT(&strbuf, 0);\
+ grn_expr_inspect(ctx, &strbuf, (expr));\
+ GRN_TEXT_PUTC(ctx, &strbuf, '\0');\
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "%s=(%s)", (name), GRN_TEXT_VALUE(&strbuf));\
+ GRN_OBJ_FIN(ctx, &strbuf);\
+} while (0)
+
+static void
+scan_info_put_index(grn_ctx *ctx, scan_info *si, grn_obj *index, uint32_t sid, int32_t weight)
+{
+ GRN_PTR_PUT(ctx, &si->index, index);
+ GRN_UINT32_PUT(ctx, &si->wv, sid);
+ GRN_INT32_PUT(ctx, &si->wv, weight);
+ {
+ int i, ni = (GRN_BULK_VSIZE(&si->index) / sizeof(grn_obj *)) - 1;
+ grn_obj **pi = &GRN_PTR_VALUE_AT(&si->index, ni);
+ for (i = 0; i < ni; i++, pi--) {
+ if (index == pi[-1]) {
+ if (i) {
+ int32_t *pw = &GRN_INT32_VALUE_AT(&si->wv, (ni - i) * 2);
+ memmove(pw + 2, pw, sizeof(int32_t) * 2 * i);
+ pw[0] = (int32_t) sid;
+ pw[1] = weight;
+ memmove(pi + 1, pi, sizeof(grn_obj *) * i);
+ pi[0] = index;
+ }
+ return;
+ }
+ }
+ }
+}
+
+static int32_t
+get_weight(grn_ctx *ctx, grn_expr_code *ec)
+{
+ if (ec->modify == 2 && ec[2].op == GRN_OP_STAR &&
+ ec[1].value && ec[1].value->header.type == GRN_BULK) {
+ if (ec[1].value->header.domain == GRN_DB_INT32 ||
+ ec[1].value->header.domain == GRN_DB_UINT32) {
+ return GRN_INT32_VALUE(ec[1].value);
+ } else {
+ int32_t weight = 1;
+ grn_obj weight_buffer;
+ GRN_INT32_INIT(&weight_buffer, 0);
+ if (!grn_obj_cast(ctx, ec[1].value, &weight_buffer, GRN_FALSE)) {
+ weight = GRN_INT32_VALUE(&weight_buffer);
+ }
+ grn_obj_unlink(ctx, &weight_buffer);
+ return weight;
+ }
+ } else {
+ return 1;
+ }
+}
+
+scan_info *
+grn_scan_info_open(grn_ctx *ctx, int start)
+{
+ scan_info *si = GRN_MALLOCN(scan_info, 1);
+
+ if (!si) {
+ return NULL;
+ }
+
+ GRN_INT32_INIT(&si->wv, GRN_OBJ_VECTOR);
+ GRN_PTR_INIT(&si->index, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ si->logical_op = GRN_OP_OR;
+ si->flags = SCAN_PUSH;
+ si->nargs = 0;
+ si->max_interval = DEFAULT_MAX_INTERVAL;
+ si->start = start;
+
+ return si;
+}
+
+void
+grn_scan_info_close(grn_ctx *ctx, scan_info *si)
+{
+ SI_FREE(si);
+}
+
+void
+grn_scan_info_put_index(grn_ctx *ctx, scan_info *si, grn_obj *index, uint32_t sid, int32_t weight)
+{
+ scan_info_put_index(ctx, si, index, sid, weight);
+}
+
+scan_info **
+grn_scan_info_put_logical_op(grn_ctx *ctx, scan_info **sis, int *ip,
+ grn_operator op, int start)
+{
+ return put_logical_op(ctx, sis, ip, op, start);
+}
+
+int32_t
+grn_expr_code_get_weight(grn_ctx *ctx, grn_expr_code *ec)
+{
+ return get_weight(ctx, ec);
+}
+
+int
+grn_scan_info_get_flags(scan_info *si)
+{
+ return si->flags;
+}
+
+void
+grn_scan_info_set_flags(scan_info *si, int flags)
+{
+ si->flags = flags;
+}
+
+grn_operator
+grn_scan_info_get_logical_op(scan_info *si)
+{
+ return si->logical_op;
+}
+
+void
+grn_scan_info_set_logical_op(scan_info *si, grn_operator logical_op)
+{
+ si->logical_op = logical_op;
+}
+
+grn_operator
+grn_scan_info_get_op(scan_info *si)
+{
+ return si->op;
+}
+
+void
+grn_scan_info_set_op(scan_info *si, grn_operator op)
+{
+ si->op = op;
+}
+
+void
+grn_scan_info_set_end(scan_info *si, uint32_t end)
+{
+ si->end = end;
+}
+
+void
+grn_scan_info_set_query(scan_info *si, grn_obj *query)
+{
+ si->query = query;
+}
+
+int
+grn_scan_info_get_max_interval(scan_info *si)
+{
+ return si->max_interval;
+}
+
+void
+grn_scan_info_set_max_interval(scan_info *si, int max_interval)
+{
+ si->max_interval = max_interval;
+}
+
+grn_bool
+grn_scan_info_push_arg(scan_info *si, grn_obj *arg)
+{
+ if (si->nargs >= 8) {
+ return GRN_FALSE;
+ }
+
+ si->args[si->nargs++] = arg;
+ return GRN_TRUE;
+}
+
+grn_obj *
+grn_scan_info_get_arg(grn_ctx *ctx, scan_info *si, int i)
+{
+ if (i >= si->nargs) {
+ return NULL;
+ }
+ return si->args[i];
+}
+
+static uint32_t
+scan_info_build_find_index_column_index(grn_ctx *ctx,
+ scan_info *si,
+ grn_expr_code *ec,
+ uint32_t n_rest_codes,
+ grn_operator op)
+{
+ uint32_t offset = 0;
+ grn_obj *index;
+ int sid = 0;
+ int32_t weight = 0;
+
+ index = ec->value;
+ if (n_rest_codes > 2 &&
+ ec[1].value &&
+ ec[1].value->header.domain == GRN_DB_UINT32 &&
+ ec[2].op == GRN_OP_GET_MEMBER) {
+ sid = GRN_UINT32_VALUE(ec[1].value) + 1;
+ offset = 2;
+ weight = get_weight(ctx, ec + offset);
+ } else {
+ weight = get_weight(ctx, ec);
+ }
+ scan_info_put_index(ctx, si, index, sid, weight);
+
+ return offset;
+}
+
+static scan_info **
+scan_info_build(grn_ctx *ctx, grn_obj *expr, int *n,
+ grn_operator op, uint32_t size)
+{
+ grn_obj *var;
+ scan_stat stat;
+ int i, m = 0, o = 0;
+ scan_info **sis, *si = NULL;
+ grn_expr_code *c, *ce;
+ grn_expr *e = (grn_expr *)expr;
+#ifdef GRN_WITH_MRUBY
+ if (ctx->impl->mrb.state) {
+ return grn_mrb_scan_info_build(ctx, expr, n, op, size);
+ }
+#endif
+ if (!(var = grn_expr_get_var_by_offset(ctx, expr, 0))) { return NULL; }
+ for (stat = SCAN_START, c = e->codes, ce = &e->codes[e->codes_curr]; c < ce; c++) {
+ switch (c->op) {
+ case GRN_OP_MATCH :
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ case GRN_OP_SIMILAR :
+ case GRN_OP_PREFIX :
+ case GRN_OP_SUFFIX :
+ case GRN_OP_EQUAL :
+ case GRN_OP_NOT_EQUAL :
+ case GRN_OP_LESS :
+ case GRN_OP_GREATER :
+ case GRN_OP_LESS_EQUAL :
+ case GRN_OP_GREATER_EQUAL :
+ case GRN_OP_GEO_WITHINP5 :
+ case GRN_OP_GEO_WITHINP6 :
+ case GRN_OP_GEO_WITHINP8 :
+ case GRN_OP_TERM_EXTRACT :
+ if (stat < SCAN_COL1 || SCAN_CONST < stat) { return NULL; }
+ stat = SCAN_START;
+ m++;
+ break;
+ case GRN_OP_AND :
+ case GRN_OP_OR :
+ case GRN_OP_AND_NOT :
+ case GRN_OP_ADJUST :
+ if (stat != SCAN_START) { return NULL; }
+ o++;
+ if (o >= m) { return NULL; }
+ break;
+ case GRN_OP_PUSH :
+ stat = (c->value == var) ? SCAN_VAR : SCAN_CONST;
+ break;
+ case GRN_OP_GET_VALUE :
+ switch (stat) {
+ case SCAN_START :
+ case SCAN_CONST :
+ case SCAN_VAR :
+ stat = SCAN_COL1;
+ break;
+ case SCAN_COL1 :
+ stat = SCAN_COL2;
+ break;
+ case SCAN_COL2 :
+ break;
+ default :
+ return NULL;
+ break;
+ }
+ break;
+ case GRN_OP_CALL :
+ if ((c->flags & GRN_EXPR_CODE_RELATIONAL_EXPRESSION) || c + 1 == ce) {
+ stat = SCAN_START;
+ m++;
+ } else {
+ stat = SCAN_COL2;
+ }
+ break;
+ default :
+ return NULL;
+ break;
+ }
+ }
+ if (stat || m != o + 1) { return NULL; }
+ if (!(sis = GRN_MALLOCN(scan_info *, m + m + o))) { return NULL; }
+ for (i = 0, stat = SCAN_START, c = e->codes, ce = &e->codes[e->codes_curr]; c < ce; c++) {
+ switch (c->op) {
+ case GRN_OP_MATCH :
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ case GRN_OP_SIMILAR :
+ case GRN_OP_PREFIX :
+ case GRN_OP_SUFFIX :
+ case GRN_OP_EQUAL :
+ case GRN_OP_NOT_EQUAL :
+ case GRN_OP_LESS :
+ case GRN_OP_GREATER :
+ case GRN_OP_LESS_EQUAL :
+ case GRN_OP_GREATER_EQUAL :
+ case GRN_OP_GEO_WITHINP5 :
+ case GRN_OP_GEO_WITHINP6 :
+ case GRN_OP_GEO_WITHINP8 :
+ case GRN_OP_TERM_EXTRACT :
+ stat = SCAN_START;
+ si->op = c->op;
+ si->end = c - e->codes;
+ sis[i++] = si;
+ {
+ int sid;
+ grn_obj *index, **p = si->args, **pe = si->args + si->nargs;
+ for (; p < pe; p++) {
+ if ((*p)->header.type == GRN_EXPR) {
+ uint32_t j;
+ grn_expr_code *ec;
+ grn_expr *e = (grn_expr *)(*p);
+ for (j = e->codes_curr, ec = e->codes; j--; ec++) {
+ if (ec->value) {
+ switch (ec->value->header.type) {
+ case GRN_ACCESSOR :
+ if (grn_column_index(ctx, ec->value, c->op, &index, 1, &sid)) {
+ int32_t weight = get_weight(ctx, ec);
+ si->flags |= SCAN_ACCESSOR;
+ if (((grn_accessor *)ec->value)->next) {
+ scan_info_put_index(ctx, si, ec->value, sid, weight);
+ } else {
+ scan_info_put_index(ctx, si, index, sid, weight);
+ }
+ }
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ if (grn_column_index(ctx, ec->value, c->op, &index, 1, &sid)) {
+ scan_info_put_index(ctx, si, index, sid, get_weight(ctx, ec));
+ }
+ break;
+ case GRN_COLUMN_INDEX :
+ {
+ uint32_t offset;
+ offset = scan_info_build_find_index_column_index(ctx, si, ec,
+ j, c->op);
+ j -= offset;
+ ec += offset;
+ }
+ break;
+ }
+ }
+ }
+ } else if (GRN_DB_OBJP(*p)) {
+ if (grn_column_index(ctx, *p, c->op, &index, 1, &sid)) {
+ scan_info_put_index(ctx, si, index, sid, 1);
+ }
+ } else if (GRN_ACCESSORP(*p)) {
+ si->flags |= SCAN_ACCESSOR;
+ if (grn_column_index(ctx, *p, c->op, &index, 1, &sid)) {
+ if (((grn_accessor *)(*p))->next) {
+ scan_info_put_index(ctx, si, *p, sid, 1);
+ } else {
+ scan_info_put_index(ctx, si, index, sid, 1);
+ }
+ }
+ } else {
+ switch (c->op) {
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ if (si->nargs == 3 &&
+ *p == si->args[2] &&
+ (*p)->header.domain == GRN_DB_INT32) {
+ si->max_interval = GRN_INT32_VALUE(*p);
+ } else {
+ si->query = *p;
+ }
+ break;
+ default :
+ si->query = *p;
+ break;
+ }
+ }
+ }
+ }
+ si = NULL;
+ break;
+ case GRN_OP_AND :
+ case GRN_OP_OR :
+ case GRN_OP_AND_NOT :
+ case GRN_OP_ADJUST :
+ if (!put_logical_op(ctx, sis, &i, c->op, c - e->codes)) { return NULL; }
+ stat = SCAN_START;
+ break;
+ case GRN_OP_PUSH :
+ if (!si) { SI_ALLOC(si, i, c - e->codes); }
+ if (c->value == var) {
+ stat = SCAN_VAR;
+ } else {
+ if (si->nargs < 8) {
+ si->args[si->nargs++] = c->value;
+ }
+ if (stat == SCAN_START) { si->flags |= SCAN_PRE_CONST; }
+ stat = SCAN_CONST;
+ }
+ break;
+ case GRN_OP_GET_VALUE :
+ switch (stat) {
+ case SCAN_START :
+ if (!si) { SI_ALLOC(si, i, c - e->codes); }
+ // fallthru
+ case SCAN_CONST :
+ case SCAN_VAR :
+ stat = SCAN_COL1;
+ if (si->nargs < 8) {
+ si->args[si->nargs++] = c->value;
+ }
+ break;
+ case SCAN_COL1 :
+ {
+ int j;
+ grn_obj inspected;
+ GRN_TEXT_INIT(&inspected, 0);
+ GRN_TEXT_PUTS(ctx, &inspected, "<");
+ grn_inspect_name(ctx, &inspected, c->value);
+ GRN_TEXT_PUTS(ctx, &inspected, ">: <");
+ grn_inspect(ctx, &inspected, expr);
+ GRN_TEXT_PUTS(ctx, &inspected, ">");
+ ERR(GRN_INVALID_ARGUMENT,
+ "invalid expression: can't use column as a value: %.*s",
+ (int)GRN_TEXT_LEN(&inspected), GRN_TEXT_VALUE(&inspected));
+ GRN_OBJ_FIN(ctx, &inspected);
+ for (j = 0; j < i; j++) { SI_FREE(sis[j]); }
+ GRN_FREE(sis);
+ return NULL;
+ }
+ stat = SCAN_COL2;
+ break;
+ case SCAN_COL2 :
+ break;
+ default :
+ break;
+ }
+ break;
+ case GRN_OP_CALL :
+ if (!si) { SI_ALLOC(si, i, c - e->codes); }
+ if ((c->flags & GRN_EXPR_CODE_RELATIONAL_EXPRESSION) || c + 1 == ce) {
+ stat = SCAN_START;
+ si->op = c->op;
+ si->end = c - e->codes;
+ sis[i++] = si;
+ /* better index resolving framework for functions should be implemented */
+ {
+ int sid;
+ grn_obj *index, **p = si->args, **pe = si->args + si->nargs;
+ for (; p < pe; p++) {
+ if (GRN_DB_OBJP(*p)) {
+ if (grn_column_index(ctx, *p, c->op, &index, 1, &sid)) {
+ scan_info_put_index(ctx, si, index, sid, 1);
+ }
+ } else if (GRN_ACCESSORP(*p)) {
+ si->flags |= SCAN_ACCESSOR;
+ if (grn_column_index(ctx, *p, c->op, &index, 1, &sid)) {
+ scan_info_put_index(ctx, si, index, sid, 1);
+ }
+ } else {
+ si->query = *p;
+ }
+ }
+ }
+ si = NULL;
+ } else {
+ stat = SCAN_COL2;
+ }
+ break;
+ default :
+ break;
+ }
+ }
+ if (op == GRN_OP_OR && !size) {
+ // for debug
+ if (!(sis[0]->flags & SCAN_PUSH) || (sis[0]->logical_op != op)) {
+ int j;
+ ERR(GRN_INVALID_ARGUMENT, "invalid expr");
+ for (j = 0; j < i; j++) { SI_FREE(sis[j]); }
+ GRN_FREE(sis);
+ return NULL;
+ } else {
+ sis[0]->flags &= ~SCAN_PUSH;
+ sis[0]->logical_op = op;
+ }
+ } else {
+ if (!put_logical_op(ctx, sis, &i, op, c - e->codes)) { return NULL; }
+ }
+ *n = i;
+ return sis;
+}
+
+inline static int32_t
+exec_result_to_score(grn_ctx *ctx, grn_obj *result, grn_obj *score_buffer)
+{
+ if (!result) {
+ return 0;
+ }
+
+ switch (result->header.type) {
+ case GRN_VOID :
+ return 0;
+ case GRN_BULK :
+ if (grn_obj_cast(ctx, result, score_buffer, GRN_FALSE) != GRN_SUCCESS) {
+ return 1;
+ }
+ return GRN_INT32_VALUE(score_buffer);
+ case GRN_UVECTOR :
+ case GRN_PVECTOR :
+ case GRN_VECTOR :
+ return 1;
+ default :
+ return 1; /* TODO: 1 is reasonable? */
+ }
+}
+
+static void
+grn_table_select_(grn_ctx *ctx, grn_obj *table, grn_obj *expr, grn_obj *v,
+ grn_obj *res, grn_operator op)
+{
+ int32_t score;
+ grn_id id, *idp;
+ grn_table_cursor *tc;
+ grn_hash_cursor *hc;
+ grn_hash *s = (grn_hash *)res;
+ grn_obj *r;
+ grn_obj score_buffer;
+ GRN_RECORD_INIT(v, 0, grn_obj_id(ctx, table));
+ GRN_INT32_INIT(&score_buffer, 0);
+ switch (op) {
+ case GRN_OP_OR :
+ if ((tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1, 0))) {
+ while ((id = grn_table_cursor_next(ctx, tc))) {
+ GRN_RECORD_SET(ctx, v, id);
+ r = grn_expr_exec(ctx, expr, 0);
+ score = exec_result_to_score(ctx, r, &score_buffer);
+ if (score > 0) {
+ grn_rset_recinfo *ri;
+ if (grn_hash_add(ctx, s, &id, s->key_size, (void **)&ri, NULL)) {
+ grn_table_add_subrec(res, ri, score, (grn_rset_posinfo *)&id, 1);
+ }
+ }
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ break;
+ case GRN_OP_AND :
+ if ((hc = grn_hash_cursor_open(ctx, s, NULL, 0, NULL, 0, 0, -1, 0))) {
+ while (grn_hash_cursor_next(ctx, hc)) {
+ grn_hash_cursor_get_key(ctx, hc, (void **) &idp);
+ GRN_RECORD_SET(ctx, v, *idp);
+ r = grn_expr_exec(ctx, expr, 0);
+ score = exec_result_to_score(ctx, r, &score_buffer);
+ if (score > 0) {
+ grn_rset_recinfo *ri;
+ grn_hash_cursor_get_value(ctx, hc, (void **) &ri);
+ grn_table_add_subrec(res, ri, score, (grn_rset_posinfo *)idp, 1);
+ } else {
+ grn_hash_cursor_delete(ctx, hc, NULL);
+ }
+ }
+ grn_hash_cursor_close(ctx, hc);
+ }
+ break;
+ case GRN_OP_AND_NOT :
+ if ((hc = grn_hash_cursor_open(ctx, s, NULL, 0, NULL, 0, 0, -1, 0))) {
+ while (grn_hash_cursor_next(ctx, hc)) {
+ grn_hash_cursor_get_key(ctx, hc, (void **) &idp);
+ GRN_RECORD_SET(ctx, v, *idp);
+ r = grn_expr_exec(ctx, expr, 0);
+ score = exec_result_to_score(ctx, r, &score_buffer);
+ if (score > 0) {
+ grn_hash_cursor_delete(ctx, hc, NULL);
+ }
+ }
+ grn_hash_cursor_close(ctx, hc);
+ }
+ break;
+ case GRN_OP_ADJUST :
+ if ((hc = grn_hash_cursor_open(ctx, s, NULL, 0, NULL, 0, 0, -1, 0))) {
+ while (grn_hash_cursor_next(ctx, hc)) {
+ grn_hash_cursor_get_key(ctx, hc, (void **) &idp);
+ GRN_RECORD_SET(ctx, v, *idp);
+ r = grn_expr_exec(ctx, expr, 0);
+ score = exec_result_to_score(ctx, r, &score_buffer);
+ if (score > 0) {
+ grn_rset_recinfo *ri;
+ grn_hash_cursor_get_value(ctx, hc, (void **) &ri);
+ grn_table_add_subrec(res, ri, score, (grn_rset_posinfo *)idp, 1);
+ }
+ }
+ grn_hash_cursor_close(ctx, hc);
+ }
+ break;
+ default :
+ break;
+ }
+ GRN_OBJ_FIN(ctx, &score_buffer);
+}
+
+static inline grn_bool
+grn_table_select_index_range_column(grn_ctx *ctx, grn_obj *table,
+ grn_obj *index,
+ scan_info *si, grn_operator logical_op,
+ grn_obj *res)
+{
+ grn_bool processed = GRN_FALSE;
+ grn_obj *index_table;
+ grn_obj range;
+
+ index_table = grn_ctx_at(ctx, index->header.domain);
+ if (!index_table) {
+ return GRN_FALSE;
+ }
+
+ GRN_OBJ_INIT(&range, GRN_BULK, 0, index_table->header.domain);
+ if (grn_obj_cast(ctx, si->query, &range, GRN_FALSE) == GRN_SUCCESS) {
+ grn_table_cursor *cursor;
+ const void *min = NULL, *max = NULL;
+ unsigned int min_size = 0, max_size = 0;
+ int offset = 0;
+ int limit = -1;
+ int flags = GRN_CURSOR_ASCENDING;
+
+ switch (si->op) {
+ case GRN_OP_LESS :
+ flags |= GRN_CURSOR_LT;
+ max = GRN_BULK_HEAD(&range);
+ max_size = GRN_BULK_VSIZE(&range);
+ break;
+ case GRN_OP_GREATER :
+ flags |= GRN_CURSOR_GT;
+ min = GRN_BULK_HEAD(&range);
+ min_size = GRN_BULK_VSIZE(&range);
+ break;
+ case GRN_OP_LESS_EQUAL :
+ flags |= GRN_CURSOR_LE;
+ max = GRN_BULK_HEAD(&range);
+ max_size = GRN_BULK_VSIZE(&range);
+ break;
+ case GRN_OP_GREATER_EQUAL :
+ flags |= GRN_CURSOR_GE;
+ min = GRN_BULK_HEAD(&range);
+ min_size = GRN_BULK_VSIZE(&range);
+ break;
+ default :
+ break;
+ }
+ cursor = grn_table_cursor_open(ctx, index_table,
+ min, min_size, max, max_size,
+ offset, limit, flags);
+ if (cursor) {
+ grn_id index_id;
+ while ((index_id = grn_table_cursor_next(ctx, cursor))) {
+ grn_ii_at(ctx, (grn_ii *)index, index_id,
+ (grn_hash *)res, logical_op);
+ }
+ grn_table_cursor_close(ctx, cursor);
+ processed = GRN_TRUE;
+ }
+
+ grn_ii_resolve_sel_and(ctx, (grn_hash *)res, logical_op);
+ }
+ GRN_OBJ_FIN(ctx, &range);
+
+ grn_obj_unlink(ctx, index_table);
+
+ return processed;
+}
+
+static inline grn_bool
+grn_table_select_index_range_accessor(grn_ctx *ctx, grn_obj *table,
+ grn_obj *accessor_stack,
+ scan_info *si, grn_obj *res)
+{
+ int n_accessors;
+ grn_obj *current_res = NULL;
+
+ n_accessors = GRN_BULK_VSIZE(accessor_stack) / sizeof(grn_obj *);
+
+ {
+ grn_accessor *last_accessor;
+ grn_obj *target;
+ grn_obj *index;
+ grn_obj *range;
+
+ last_accessor = (grn_accessor *)GRN_PTR_VALUE_AT(accessor_stack,
+ n_accessors - 1);
+ target = last_accessor->obj;
+ if (grn_column_index(ctx, target, si->op, &index, 1, NULL) == 0) {
+ return GRN_FALSE;
+ }
+
+ range = grn_ctx_at(ctx, DB_OBJ(index)->range);
+ current_res = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC,
+ range,
+ NULL);
+ grn_obj_unlink(ctx, range);
+ if (!current_res) {
+ return GRN_FALSE;
+ }
+ if (!grn_table_select_index_range_column(ctx, table, index, si, GRN_OP_OR,
+ current_res)) {
+ grn_obj_unlink(ctx, current_res);
+ return GRN_FALSE;
+ }
+ }
+
+ {
+ int i;
+
+ for (i = n_accessors - 1; i > 0; i--) {
+ grn_rc rc = GRN_SUCCESS;
+ grn_accessor *accessor;
+ grn_obj *index;
+ grn_obj *domain;
+ grn_obj *target;
+ grn_obj *next_res;
+ grn_operator next_op;
+ grn_id *next_record_id = NULL;
+
+ accessor = (grn_accessor *)GRN_PTR_VALUE_AT(accessor_stack, i - 1);
+ target = accessor->obj;
+ if (grn_column_index(ctx, target, GRN_OP_EQUAL, &index, 1, NULL) == 0) {
+ grn_obj_unlink(ctx, current_res);
+ current_res = NULL;
+ break;
+ }
+
+ {
+ grn_obj *range;
+ range = grn_ctx_at(ctx, DB_OBJ(index)->range);
+ next_res = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC,
+ range,
+ NULL);
+ grn_obj_unlink(ctx, range);
+ if (!next_res) {
+ grn_obj_unlink(ctx, current_res);
+ current_res = NULL;
+ break;
+ }
+ next_op = GRN_OP_OR;
+ }
+
+ domain = grn_ctx_at(ctx, index->header.domain);
+ GRN_HASH_EACH(ctx, (grn_hash *)current_res, id, &next_record_id,
+ NULL, NULL, {
+ if (domain->header.type == GRN_TABLE_NO_KEY) {
+ rc = grn_ii_sel(ctx, (grn_ii *)index,
+ (const char *)next_record_id, sizeof(grn_id),
+ (grn_hash *)next_res, next_op, NULL);
+ } else {
+ char key[GRN_TABLE_MAX_KEY_SIZE];
+ int key_len;
+ key_len = grn_table_get_key(ctx, domain, *next_record_id,
+ key, GRN_TABLE_MAX_KEY_SIZE);
+ rc = grn_ii_sel(ctx, (grn_ii *)index, key, key_len,
+ (grn_hash *)next_res, next_op, NULL);
+ }
+ if (rc != GRN_SUCCESS) {
+ break;
+ }
+ });
+ grn_obj_unlink(ctx, domain);
+ grn_obj_unlink(ctx, current_res);
+
+ if (rc == GRN_SUCCESS) {
+ if (i == 1) {
+ grn_table_setoperation(ctx, res, next_res, res, si->logical_op);
+ grn_obj_unlink(ctx, next_res);
+ current_res = res;
+ } else {
+ current_res = next_res;
+ }
+ } else {
+ if (res != next_res) {
+ grn_obj_unlink(ctx, next_res);
+ }
+ current_res = NULL;
+ break;
+ }
+ }
+ }
+
+ return current_res == res;
+}
+
+static inline grn_bool
+grn_table_select_index_range(grn_ctx *ctx, grn_obj *table, grn_obj *index,
+ scan_info *si, grn_obj *res)
+{
+ if (si->flags & SCAN_ACCESSOR) {
+ grn_bool processed;
+ grn_accessor *accessor = (grn_accessor *)index;
+ grn_accessor *a;
+ grn_obj accessor_stack;
+
+ if (index->header.type != GRN_ACCESSOR) {
+ return GRN_FALSE;
+ }
+
+ GRN_PTR_INIT(&accessor_stack, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ for (a = accessor; a; a = a->next) {
+ GRN_PTR_PUT(ctx, &accessor_stack, a);
+ }
+ processed = grn_table_select_index_range_accessor(ctx, table,
+ &accessor_stack,
+ si, res);
+ GRN_OBJ_FIN(ctx, &accessor_stack);
+ return processed;
+ } else {
+ return grn_table_select_index_range_column(ctx, table, index, si,
+ si->logical_op, res);
+ }
+}
+
+static inline grn_bool
+grn_table_select_index(grn_ctx *ctx, grn_obj *table, scan_info *si,
+ grn_obj *res)
+{
+ grn_bool processed = GRN_FALSE;
+ if (GRN_BULK_VSIZE(&si->index)) {
+ grn_obj *index = GRN_PTR_VALUE(&si->index);
+ switch (si->op) {
+ case GRN_OP_EQUAL :
+ if (GRN_BULK_VSIZE(si->query) == 0) {
+ /* We can't use index for empty value. */
+ return GRN_FALSE;
+ }
+ if (si->flags & SCAN_ACCESSOR) {
+ if (index->header.type == GRN_ACCESSOR &&
+ !((grn_accessor *)index)->next) {
+ grn_obj dest;
+ grn_accessor *a = (grn_accessor *)index;
+ grn_ii_posting posting;
+ posting.sid = 1;
+ posting.pos = 0;
+ posting.weight = 0;
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ GRN_UINT32_INIT(&dest, 0);
+ if (!grn_obj_cast(ctx, si->query, &dest, GRN_FALSE)) {
+ posting.rid = GRN_UINT32_VALUE(&dest);
+ if (posting.rid) {
+ if (posting.rid == grn_table_at(ctx, table, posting.rid)) {
+ grn_ii_posting_add(ctx, &posting, (grn_hash *)res,
+ si->logical_op);
+ }
+ }
+ processed = GRN_TRUE;
+ }
+ grn_ii_resolve_sel_and(ctx, (grn_hash *)res, si->logical_op);
+ GRN_OBJ_FIN(ctx, &dest);
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ GRN_OBJ_INIT(&dest, GRN_BULK, 0, table->header.domain);
+ if (!grn_obj_cast(ctx, si->query, &dest, GRN_FALSE)) {
+ if ((posting.rid = grn_table_get(ctx, table,
+ GRN_BULK_HEAD(&dest),
+ GRN_BULK_VSIZE(&dest)))) {
+ grn_ii_posting_add(ctx, &posting, (grn_hash *)res,
+ si->logical_op);
+ }
+ processed = GRN_TRUE;
+ }
+ grn_ii_resolve_sel_and(ctx, (grn_hash *)res, si->logical_op);
+ GRN_OBJ_FIN(ctx, &dest);
+ break;
+ }
+ }
+ } else {
+ grn_obj *domain = grn_ctx_at(ctx, index->header.domain);
+ if (domain) {
+ grn_id tid;
+ if (GRN_OBJ_GET_DOMAIN(si->query) == DB_OBJ(domain)->id) {
+ tid = GRN_RECORD_VALUE(si->query);
+ } else {
+ tid = grn_table_get(ctx, domain,
+ GRN_BULK_HEAD(si->query),
+ GRN_BULK_VSIZE(si->query));
+ }
+ grn_ii_at(ctx, (grn_ii *)index, tid, (grn_hash *)res, si->logical_op);
+ }
+ grn_ii_resolve_sel_and(ctx, (grn_hash *)res, si->logical_op);
+ processed = GRN_TRUE;
+ }
+ break;
+ case GRN_OP_SUFFIX :
+ {
+ grn_obj *domain;
+ if (si->flags & SCAN_ACCESSOR) {
+ domain = table;
+ } else {
+ domain = grn_ctx_at(ctx, index->header.domain);
+ }
+ if (domain->header.type != GRN_TABLE_PAT_KEY) {
+ break;
+ }
+ if (!(domain->header.flags & GRN_OBJ_KEY_WITH_SIS)) {
+ break;
+ }
+ }
+ /* fallthru */
+ case GRN_OP_PREFIX :
+ if (si->flags & SCAN_ACCESSOR) {
+ if (index->header.type == GRN_ACCESSOR &&
+ !((grn_accessor *)index)->next) {
+ grn_obj dest;
+ grn_accessor *a = (grn_accessor *)index;
+ grn_ii_posting posting;
+ posting.sid = 1;
+ posting.pos = 0;
+ posting.weight = 0;
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ /* todo */
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ GRN_OBJ_INIT(&dest, GRN_BULK, 0, table->header.domain);
+ if (!grn_obj_cast(ctx, si->query, &dest, GRN_FALSE)) {
+ grn_hash *pres;
+ if ((pres = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY))) {
+ grn_id *key;
+ grn_table_search(ctx, table,
+ GRN_BULK_HEAD(&dest), GRN_BULK_VSIZE(&dest),
+ si->op, (grn_obj *)pres, GRN_OP_OR);
+ GRN_HASH_EACH(ctx, pres, id, &key, NULL, NULL, {
+ posting.rid = *key;
+ grn_ii_posting_add(ctx, &posting, (grn_hash *)res,
+ si->logical_op);
+ });
+ grn_hash_close(ctx, pres);
+ }
+ processed = GRN_TRUE;
+ }
+ grn_ii_resolve_sel_and(ctx, (grn_hash *)res, si->logical_op);
+ GRN_OBJ_FIN(ctx, &dest);
+ break;
+ }
+ }
+ } else {
+ grn_obj *i = GRN_PTR_VALUE(&si->index);
+ grn_obj *domain = grn_ctx_at(ctx, i->header.domain);
+ if (domain) {
+ grn_hash *pres;
+ if ((pres = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY))) {
+ grn_id *key;
+ grn_table_search(ctx, domain,
+ GRN_BULK_HEAD(si->query),
+ GRN_BULK_VSIZE(si->query),
+ si->op, (grn_obj *)pres, GRN_OP_OR);
+ grn_obj_unlink(ctx, domain);
+ GRN_HASH_EACH(ctx, pres, id, &key, NULL, NULL, {
+ grn_ii_at(ctx, (grn_ii *)index, *key, (grn_hash *)res, si->logical_op);
+ });
+ grn_hash_close(ctx, pres);
+ }
+ grn_obj_unlink(ctx, domain);
+ }
+ grn_ii_resolve_sel_and(ctx, (grn_hash *)res, si->logical_op);
+ processed = GRN_TRUE;
+ }
+ break;
+ case GRN_OP_MATCH :
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ case GRN_OP_SIMILAR :
+ {
+ grn_obj wv, **ip = &GRN_PTR_VALUE(&si->index);
+ int j = GRN_BULK_VSIZE(&si->index)/sizeof(grn_obj *);
+ int32_t *wp = &GRN_INT32_VALUE(&si->wv);
+ grn_search_optarg optarg;
+ GRN_INT32_INIT(&wv, GRN_OBJ_VECTOR);
+ if (si->op == GRN_OP_MATCH) {
+ optarg.mode = GRN_OP_EXACT;
+ } else {
+ optarg.mode = si->op;
+ }
+ optarg.similarity_threshold = 0;
+ switch (si->op) {
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ optarg.max_interval = si->max_interval;
+ break;
+ default :
+ optarg.max_interval = 0;
+ break;
+ }
+ optarg.weight_vector = (int *)GRN_BULK_HEAD(&wv);
+ /* optarg.vector_size = GRN_BULK_VSIZE(&si->wv); */
+ optarg.vector_size = 1;
+ optarg.proc = NULL;
+ optarg.max_size = 0;
+ ctx->flags |= GRN_CTX_TEMPORARY_DISABLE_II_RESOLVE_SEL_AND;
+ for (; j--; ip++, wp += 2) {
+ uint32_t sid = (uint32_t) wp[0];
+ int32_t weight = wp[1];
+ if (sid) {
+ int weight_index = sid - 1;
+ GRN_INT32_SET_AT(ctx, &wv, weight_index, weight);
+ optarg.weight_vector = &GRN_INT32_VALUE(&wv);
+ optarg.vector_size = GRN_BULK_VSIZE(&wv)/sizeof(int32_t);
+ } else {
+ optarg.weight_vector = NULL;
+ optarg.vector_size = weight;
+ }
+ if (j) {
+ if (sid && ip[0] == ip[1]) { continue; }
+ } else {
+ ctx->flags &= ~GRN_CTX_TEMPORARY_DISABLE_II_RESOLVE_SEL_AND;
+ }
+ grn_obj_search(ctx, ip[0], si->query, res, si->logical_op, &optarg);
+ if (optarg.weight_vector) {
+ int i;
+ for (i = 0; i < optarg.vector_size; i++) {
+ optarg.weight_vector[i] = 0;
+ }
+ }
+ GRN_BULK_REWIND(&wv);
+ }
+ GRN_OBJ_FIN(ctx, &wv);
+ }
+ processed = GRN_TRUE;
+ break;
+ case GRN_OP_TERM_EXTRACT :
+ if (si->flags & SCAN_ACCESSOR) {
+ if (index->header.type == GRN_ACCESSOR &&
+ !((grn_accessor *)index)->next) {
+ grn_accessor *a = (grn_accessor *)index;
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_KEY :
+ grn_table_search(ctx, table,
+ GRN_TEXT_VALUE(si->query), GRN_TEXT_LEN(si->query),
+ GRN_OP_TERM_EXTRACT, res, si->logical_op);
+ processed = GRN_TRUE;
+ break;
+ }
+ }
+ }
+ break;
+ case GRN_OP_CALL :
+ if (selector_proc_p(si->args[0])) {
+ grn_rc rc;
+ grn_proc *proc = (grn_proc *)(si->args[0]);
+ rc = proc->selector(ctx, table, index, si->nargs, si->args,
+ res, si->logical_op);
+ if (rc) {
+ /* TODO: report error */
+ } else {
+ processed = GRN_TRUE;
+ }
+ }
+ break;
+ case GRN_OP_LESS :
+ case GRN_OP_GREATER :
+ case GRN_OP_LESS_EQUAL :
+ case GRN_OP_GREATER_EQUAL :
+ processed = grn_table_select_index_range(ctx, table, index, si, res);
+ break;
+ default :
+ /* todo : implement */
+ /* todo : handle SCAN_PRE_CONST */
+ break;
+ }
+ } else {
+ switch (si->op) {
+ case GRN_OP_CALL :
+ if (selector_proc_p(si->args[0])) {
+ grn_rc rc;
+ grn_proc *proc = (grn_proc *)(si->args[0]);
+ rc = proc->selector(ctx, table, NULL, si->nargs, si->args,
+ res, si->logical_op);
+ if (rc) {
+ /* TODO: report error */
+ } else {
+ processed = GRN_TRUE;
+ }
+ }
+ default :
+ break;
+ }
+ }
+ return processed;
+}
+
+grn_obj *
+grn_table_select(grn_ctx *ctx, grn_obj *table, grn_obj *expr,
+ grn_obj *res, grn_operator op)
+{
+ grn_obj *v;
+ unsigned int res_size;
+ grn_bool res_created = GRN_FALSE;
+ if (res) {
+ if (res->header.type != GRN_TABLE_HASH_KEY ||
+ (res->header.domain != DB_OBJ(table)->id)) {
+ ERR(GRN_INVALID_ARGUMENT, "hash table required");
+ return NULL;
+ }
+ } else {
+ if (!(res = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, table, NULL))) {
+ return NULL;
+ }
+ res_created = GRN_TRUE;
+ }
+ if (!(v = grn_expr_get_var_by_offset(ctx, expr, 0))) {
+ ERR(GRN_INVALID_ARGUMENT, "at least one variable must be defined");
+ return NULL;
+ }
+ GRN_API_ENTER;
+ res_size = GRN_HASH_SIZE((grn_hash *)res);
+ if (op == GRN_OP_OR || res_size) {
+ int i, n;
+ scan_info **sis;
+ if ((sis = scan_info_build(ctx, expr, &n, op, res_size))) {
+ grn_obj res_stack;
+ grn_expr *e = (grn_expr *)expr;
+ grn_expr_code *codes = e->codes;
+ uint32_t codes_curr = e->codes_curr;
+ GRN_PTR_INIT(&res_stack, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ for (i = 0; i < n; i++) {
+ scan_info *si = sis[i];
+ if (si->flags & SCAN_POP) {
+ grn_obj *res_;
+ GRN_PTR_POP(&res_stack, res_);
+ grn_table_setoperation(ctx, res_, res, res_, si->logical_op);
+ grn_obj_close(ctx, res);
+ res = res_;
+ } else {
+ grn_bool processed = GRN_FALSE;
+ if (si->flags & SCAN_PUSH) {
+ grn_obj *res_ = NULL;
+ res_ = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, table, NULL);
+ if (!res_) {
+ int i = 0;
+ if (!res_created) { i++; }
+ for (; i < GRN_BULK_VSIZE(&res_stack) / sizeof(grn_obj *); i++) {
+ grn_obj *stacked_res;
+ stacked_res = *((grn_obj **)GRN_BULK_HEAD(&res_stack) + i);
+ grn_obj_close(ctx, stacked_res);
+ }
+ break;
+ }
+ GRN_PTR_PUT(ctx, &res_stack, res);
+ res = res_;
+ }
+ processed = grn_table_select_index(ctx, table, si, res);
+ if (!processed) {
+ if (ctx->rc) { break; }
+ e->codes = codes + si->start;
+ e->codes_curr = si->end - si->start + 1;
+ grn_table_select_(ctx, table, expr, v, res, si->logical_op);
+ }
+ }
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
+ ":", "filter(%d)", grn_table_size(ctx, res));
+ }
+ for (i = 0; i < n; i++) {
+ scan_info *si = sis[i];
+ SI_FREE(si);
+ }
+ GRN_OBJ_FIN(ctx, &res_stack);
+ GRN_FREE(sis);
+ e->codes = codes;
+ e->codes_curr = codes_curr;
+ } else {
+ if (!ctx->rc) {
+ grn_table_select_(ctx, table, expr, v, res, op);
+ }
+ }
+ }
+ GRN_API_RETURN(res);
+}
+
+/* grn_expr_parse */
+
+grn_obj *
+grn_ptr_value_at(grn_obj *obj, int offset)
+{
+ int size = GRN_BULK_VSIZE(obj) / sizeof(grn_obj *);
+ if (offset < 0) { offset = size + offset; }
+ return (0 <= offset && offset < size)
+ ? (((grn_obj **)GRN_BULK_HEAD(obj))[offset])
+ : NULL;
+}
+
+int32_t
+grn_int32_value_at(grn_obj *obj, int offset)
+{
+ int size = GRN_BULK_VSIZE(obj) / sizeof(int32_t);
+ if (offset < 0) { offset = size + offset; }
+ return (0 <= offset && offset < size)
+ ? (((int32_t *)GRN_BULK_HEAD(obj))[offset])
+ : 0;
+}
+
+/* grn_expr_create_from_str */
+
+#include "snip.h"
+
+typedef struct {
+ grn_ctx *ctx;
+ grn_obj *e;
+ grn_obj *v;
+ const char *str;
+ const char *cur;
+ const char *str_end;
+ grn_obj *table;
+ grn_obj *default_column;
+ grn_obj buf;
+ grn_obj token_stack;
+ grn_obj column_stack;
+ grn_obj op_stack;
+ grn_obj mode_stack;
+ grn_obj max_interval_stack;
+ grn_operator default_op;
+ grn_select_optarg opt;
+ grn_operator default_mode;
+ grn_expr_flags flags;
+ grn_expr_flags default_flags;
+ int escalation_threshold;
+ int escalation_decaystep;
+ int weight_offset;
+ grn_hash *weight_set;
+ snip_cond *snip_conds;
+} efs_info;
+
+typedef struct {
+ grn_operator op;
+ int weight;
+} efs_op;
+
+inline static void
+skip_space(grn_ctx *ctx, efs_info *q)
+{
+ unsigned int len;
+ while (q->cur < q->str_end && grn_isspace(q->cur, ctx->encoding)) {
+ /* null check and length check */
+ if (!(len = grn_charlen(ctx, q->cur, q->str_end))) {
+ q->cur = q->str_end;
+ break;
+ }
+ q->cur += len;
+ }
+}
+
+static grn_rc get_expr(grn_ctx *ctx, efs_info *q, grn_obj *column, grn_operator mode);
+static grn_rc get_token(grn_ctx *ctx, efs_info *q, efs_op *op, grn_obj *column, grn_operator mode);
+
+static grn_rc
+get_phrase(grn_ctx *ctx, efs_info *q, grn_obj *column, int mode, int option)
+{
+ const char *start, *s;
+ start = s = q->cur;
+ GRN_BULK_REWIND(&q->buf);
+ while (1) {
+ unsigned int len;
+ if (s >= q->str_end) {
+ q->cur = s;
+ break;
+ }
+ len = grn_charlen(ctx, s, q->str_end);
+ if (len == 0) {
+ /* invalid string containing malformed multibyte char */
+ return GRN_END_OF_DATA;
+ } else if (len == 1) {
+ if (*s == GRN_QUERY_QUOTER) {
+ q->cur = s + 1;
+ break;
+ } else if (*s == GRN_QUERY_ESCAPE && s + 1 < q->str_end) {
+ s++;
+ len = grn_charlen(ctx, s, q->str_end);
+ }
+ }
+ GRN_TEXT_PUT(ctx, &q->buf, s, len);
+ s += len;
+ }
+ grn_expr_append_obj(ctx, q->e, q->v, GRN_OP_PUSH, 1);
+ grn_expr_append_const(ctx, q->e, column, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, GRN_OP_GET_VALUE, 2);
+ grn_expr_append_const(ctx, q->e, &q->buf, GRN_OP_PUSH, 1);
+ if (mode == GRN_OP_MATCH || mode == GRN_OP_EXACT) {
+ grn_expr_append_op(ctx, q->e, mode, 2);
+ } else {
+ grn_expr_append_const_int(ctx, q->e, option, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, mode, 3);
+ }
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+get_geocond(grn_ctx *ctx, efs_info *q, grn_obj *longitude, grn_obj *latitude)
+{
+ unsigned int len;
+ const char *start = q->cur, *end;
+ for (end = q->cur;; ) {
+ /* null check and length check */
+ if (!(len = grn_charlen(ctx, end, q->str_end))) {
+ q->cur = q->str_end;
+ break;
+ }
+ if (grn_isspace(end, ctx->encoding) ||
+ *end == GRN_QUERY_PARENR) {
+ q->cur = end;
+ break;
+ }
+ }
+ {
+ const char *tokbuf[8];
+ int32_t lng0, lat0, lng1, lat1, lng2, lat2, r;
+ int32_t n = grn_str_tok((char *)start, end - start, ',', tokbuf, 8, NULL);
+ switch (n) {
+ case 3 :
+ lng0 = grn_atoi(start, tokbuf[0], NULL);
+ lat0 = grn_atoi(tokbuf[0] + 1, tokbuf[1], NULL);
+ r = grn_atoi(tokbuf[1] + 1, tokbuf[2], NULL);
+ grn_expr_append_obj(ctx, q->e, q->v, GRN_OP_PUSH, 1);
+ grn_expr_append_const(ctx, q->e, longitude, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, GRN_OP_GET_VALUE, 2);
+ grn_expr_append_obj(ctx, q->e, q->v, GRN_OP_PUSH, 1);
+ grn_expr_append_const(ctx, q->e, latitude, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, GRN_OP_GET_VALUE, 2);
+ grn_expr_append_const_int(ctx, q->e, lng0, GRN_OP_PUSH, 1);
+ grn_expr_append_const_int(ctx, q->e, lat0, GRN_OP_PUSH, 1);
+ grn_expr_append_const_int(ctx, q->e, r, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, GRN_OP_GEO_WITHINP5, 5);
+ break;
+ case 4 :
+ lng0 = grn_atoi(start, tokbuf[0], NULL);
+ lat0 = grn_atoi(tokbuf[0] + 1, tokbuf[1], NULL);
+ lng1 = grn_atoi(tokbuf[1] + 1, tokbuf[2], NULL);
+ lat1 = grn_atoi(tokbuf[2] + 1, tokbuf[3], NULL);
+ grn_expr_append_obj(ctx, q->e, q->v, GRN_OP_PUSH, 1);
+ grn_expr_append_const(ctx, q->e, longitude, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, GRN_OP_GET_VALUE, 2);
+ grn_expr_append_obj(ctx, q->e, q->v, GRN_OP_PUSH, 1);
+ grn_expr_append_const(ctx, q->e, latitude, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, GRN_OP_GET_VALUE, 2);
+ grn_expr_append_const_int(ctx, q->e, lng0, GRN_OP_PUSH, 1);
+ grn_expr_append_const_int(ctx, q->e, lat0, GRN_OP_PUSH, 1);
+ grn_expr_append_const_int(ctx, q->e, lng1, GRN_OP_PUSH, 1);
+ grn_expr_append_const_int(ctx, q->e, lat1, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, GRN_OP_GEO_WITHINP6, 6);
+ break;
+ case 6 :
+ lng0 = grn_atoi(start, tokbuf[0], NULL);
+ lat0 = grn_atoi(tokbuf[0] + 1, tokbuf[1], NULL);
+ lng1 = grn_atoi(tokbuf[1] + 1, tokbuf[2], NULL);
+ lat1 = grn_atoi(tokbuf[2] + 1, tokbuf[3], NULL);
+ lng2 = grn_atoi(tokbuf[3] + 1, tokbuf[4], NULL);
+ lat2 = grn_atoi(tokbuf[4] + 1, tokbuf[5], NULL);
+ grn_expr_append_obj(ctx, q->e, q->v, GRN_OP_PUSH, 1);
+ grn_expr_append_const(ctx, q->e, longitude, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, GRN_OP_GET_VALUE, 2);
+ grn_expr_append_obj(ctx, q->e, q->v, GRN_OP_PUSH, 1);
+ grn_expr_append_const(ctx, q->e, latitude, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, GRN_OP_GET_VALUE, 2);
+ grn_expr_append_const_int(ctx, q->e, lng0, GRN_OP_PUSH, 1);
+ grn_expr_append_const_int(ctx, q->e, lat0, GRN_OP_PUSH, 1);
+ grn_expr_append_const_int(ctx, q->e, lng1, GRN_OP_PUSH, 1);
+ grn_expr_append_const_int(ctx, q->e, lat1, GRN_OP_PUSH, 1);
+ grn_expr_append_const_int(ctx, q->e, lng2, GRN_OP_PUSH, 1);
+ grn_expr_append_const_int(ctx, q->e, lat2, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, GRN_OP_GEO_WITHINP8, 8);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "invalid geocond");
+ break;
+ }
+ }
+ return ctx->rc;
+}
+
+static grn_rc
+get_word(grn_ctx *ctx, efs_info *q, grn_obj *column, int mode, int option)
+{
+ const char *start = q->cur, *end;
+ unsigned int len;
+ for (end = q->cur;; ) {
+ /* null check and length check */
+ if (!(len = grn_charlen(ctx, end, q->str_end))) {
+ q->cur = q->str_end;
+ break;
+ }
+ if (grn_isspace(end, ctx->encoding) ||
+ *end == GRN_QUERY_PARENR) {
+ q->cur = end;
+ break;
+ }
+ if (*end == GRN_QUERY_COLUMN) {
+ grn_obj *c = grn_obj_column(ctx, q->table, start, end - start);
+ if (c && end + 1 < q->str_end) {
+ efs_op op;
+ switch (end[1]) {
+ case '!' :
+ mode = GRN_OP_NOT_EQUAL;
+ q->cur = end + 2;
+ break;
+ case '=' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ mode = GRN_OP_ASSIGN;
+ q->cur = end + 2;
+ } else {
+ get_token(ctx, q, &op, c, mode);
+ }
+ break;
+ case '<' :
+ if (end + 2 < q->str_end && end[2] == '=') {
+ mode = GRN_OP_LESS_EQUAL;
+ q->cur = end + 3;
+ } else {
+ mode = GRN_OP_LESS;
+ q->cur = end + 2;
+ }
+ break;
+ case '>' :
+ if (end + 2 < q->str_end && end[2] == '=') {
+ mode = GRN_OP_GREATER_EQUAL;
+ q->cur = end + 3;
+ } else {
+ mode = GRN_OP_GREATER;
+ q->cur = end + 2;
+ }
+ break;
+ case '%' :
+ mode = GRN_OP_MATCH;
+ q->cur = end + 2;
+ break;
+ case '@' :
+ q->cur = end + 2;
+ return get_geocond(ctx, q, column, c);
+ break;
+ default :
+ mode = GRN_OP_EQUAL;
+ q->cur = end + 1;
+ break;
+ }
+ return get_token(ctx, q, &op, c, mode);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "column lookup failed");
+ return ctx->rc;
+ }
+ } else if (*end == GRN_QUERY_PREFIX) {
+ mode = GRN_OP_PREFIX;
+ q->cur = end + 1;
+ break;
+ }
+ end += len;
+ }
+ if (!column) {
+ ERR(GRN_INVALID_ARGUMENT, "column missing");
+ return ctx->rc;
+ }
+ if (mode == GRN_OP_ASSIGN) {
+ grn_expr_append_obj(ctx, q->e, q->v, GRN_OP_PUSH, 1);
+ grn_expr_append_const(ctx, q->e, column, GRN_OP_PUSH, 1);
+ grn_expr_append_const_str(ctx, q->e, start, end - start, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, GRN_OP_ASSIGN, 2);
+ } else {
+ grn_expr_append_obj(ctx, q->e, q->v, GRN_OP_PUSH, 1);
+ grn_expr_append_const(ctx, q->e, column, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, GRN_OP_GET_VALUE, 2);
+ grn_expr_append_const_str(ctx, q->e, start, end - start, GRN_OP_PUSH, 1);
+ switch (mode) {
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ case GRN_OP_SIMILAR :
+ case GRN_OP_TERM_EXTRACT :
+ grn_expr_append_const_int(ctx, q->e, option, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, mode, 3);
+ break;
+ default :
+ grn_expr_append_op(ctx, q->e, mode, 2);
+ break;
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+static grn_bool
+get_op(efs_info *q, efs_op *op, grn_operator *mode, int *option)
+{
+ grn_bool found = GRN_TRUE;
+ const char *start, *end = q->cur;
+ switch (*end) {
+ case 'S' :
+ *mode = GRN_OP_SIMILAR;
+ start = ++end;
+ *option = grn_atoi(start, q->str_end, (const char **)&end);
+ if (start == end) { *option = DEFAULT_SIMILARITY_THRESHOLD; }
+ q->cur = end;
+ break;
+ case 'N' :
+ *mode = GRN_OP_NEAR;
+ start = ++end;
+ *option = grn_atoi(start, q->str_end, (const char **)&end);
+ if (start == end) { *option = DEFAULT_MAX_INTERVAL; }
+ q->cur = end;
+ break;
+ case 'n' :
+ *mode = GRN_OP_NEAR2;
+ start = ++end;
+ *option = grn_atoi(start, q->str_end, (const char **)&end);
+ if (start == end) { *option = DEFAULT_MAX_INTERVAL; }
+ q->cur = end;
+ break;
+ case 'T' :
+ *mode = GRN_OP_TERM_EXTRACT;
+ start = ++end;
+ *option = grn_atoi(start, q->str_end, (const char **)&end);
+ if (start == end) { *option = DEFAULT_TERM_EXTRACT_POLICY; }
+ q->cur = end;
+ break;
+ case 'X' : /* force exact mode */
+ op->op = GRN_OP_AND;
+ *mode = GRN_OP_EXACT;
+ *option = 0;
+ start = ++end;
+ q->cur = end;
+ break;
+ default :
+ found = GRN_FALSE;
+ break;
+ }
+ return found;
+}
+
+static grn_rc
+get_token(grn_ctx *ctx, efs_info *q, efs_op *op, grn_obj *column, grn_operator mode)
+{
+ int option = 0;
+ op->op = q->default_op;
+ op->weight = DEFAULT_WEIGHT;
+ for (;;) {
+ skip_space(ctx, q);
+ if (q->cur >= q->str_end) { return GRN_END_OF_DATA; }
+ switch (*q->cur) {
+ case '\0' :
+ return GRN_END_OF_DATA;
+ break;
+ case GRN_QUERY_PARENR :
+ q->cur++;
+ return GRN_END_OF_DATA;
+ break;
+ case GRN_QUERY_QUOTEL :
+ q->cur++;
+ return get_phrase(ctx, q, column, mode, option);
+ break;
+ case GRN_QUERY_PREFIX :
+ q->cur++;
+ get_op(q, op, &mode, &option);
+ break;
+ case GRN_QUERY_AND :
+ q->cur++;
+ op->op = GRN_OP_AND;
+ break;
+ case GRN_QUERY_AND_NOT :
+ q->cur++;
+ op->op = GRN_OP_AND_NOT;
+ break;
+ case GRN_QUERY_ADJ_INC :
+ q->cur++;
+ if (op->weight < 127) { op->weight++; }
+ op->op = GRN_OP_ADJUST;
+ break;
+ case GRN_QUERY_ADJ_DEC :
+ q->cur++;
+ if (op->weight > -128) { op->weight--; }
+ op->op = GRN_OP_ADJUST;
+ break;
+ case GRN_QUERY_ADJ_NEG :
+ q->cur++;
+ op->op = GRN_OP_ADJUST;
+ op->weight = -1;
+ break;
+ case GRN_QUERY_PARENL :
+ q->cur++;
+ return get_expr(ctx, q, column, mode);
+ break;
+ case 'O' :
+ if (q->cur[1] == 'R' && q->cur[2] == ' ') {
+ q->cur += 2;
+ op->op = GRN_OP_OR;
+ break;
+ }
+ /* fallthru */
+ default :
+ return get_word(ctx, q, column, mode, option);
+ break;
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+get_expr(grn_ctx *ctx, efs_info *q, grn_obj *column, grn_operator mode)
+{
+ efs_op op;
+ grn_rc rc = get_token(ctx, q, &op, column, mode);
+ if (rc) { return rc; }
+ while (!(rc = get_token(ctx, q, &op, column, mode))) {
+ if (op.op == GRN_OP_ADJUST) {
+ grn_expr_append_const_int(ctx, q->e, op.weight, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, op.op, 3);
+ } else {
+ grn_expr_append_op(ctx, q->e, op.op, 2);
+ }
+ }
+ return rc;
+}
+
+#define DISABLE_UNUSED_CODE 1
+#ifndef DISABLE_UNUSED_CODE
+static const char *
+get_weight_vector(grn_ctx *ctx, efs_info *query, const char *source)
+{
+ const char *p;
+
+ if (!query->opt.weight_vector &&
+ !query->weight_set &&
+ !(query->opt.weight_vector = GRN_CALLOC(sizeof(int) * DEFAULT_WEIGHT_VECTOR_SIZE))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "get_weight_vector malloc fail");
+ return source;
+ }
+ for (p = source; p < query->str_end; ) {
+ unsigned int key;
+ int value;
+
+ /* key, key is not zero */
+ key = grn_atoui(p, query->str_end, &p);
+ if (!key || key > GRN_ID_MAX) { break; }
+
+ /* value */
+ if (*p == ':') {
+ p++;
+ value = grn_atoi(p, query->str_end, &p);
+ } else {
+ value = 1;
+ }
+
+ if (query->weight_set) {
+ int *pval;
+ if (grn_hash_add(ctx, query->weight_set, &key, sizeof(unsigned int), (void **)&pval, NULL)) {
+ *pval = value;
+ }
+ } else if (key < DEFAULT_WEIGHT_VECTOR_SIZE) {
+ query->opt.weight_vector[key - 1] = value;
+ } else {
+ GRN_FREE(query->opt.weight_vector);
+ query->opt.weight_vector = NULL;
+ if (!(query->weight_set = grn_hash_create(ctx, NULL, sizeof(unsigned int), sizeof(int),
+ 0))) {
+ return source;
+ }
+ p = source; /* reparse */
+ continue;
+ }
+ if (*p != ',') { break; }
+ p++;
+ }
+ return p;
+}
+
+static void
+get_pragma(grn_ctx *ctx, efs_info *q)
+{
+ const char *start, *end = q->cur;
+ while (end < q->str_end && *end == GRN_QUERY_PREFIX) {
+ if (++end >= q->str_end) { break; }
+ switch (*end) {
+ case 'E' :
+ start = ++end;
+ q->escalation_threshold = grn_atoi(start, q->str_end, (const char **)&end);
+ while (end < q->str_end && (('0' <= *end && *end <= '9') || *end == '-')) { end++; }
+ if (*end == ',') {
+ start = ++end;
+ q->escalation_decaystep = grn_atoi(start, q->str_end, (const char **)&end);
+ }
+ q->cur = end;
+ break;
+ case 'D' :
+ start = ++end;
+ while (end < q->str_end && *end != GRN_QUERY_PREFIX && !grn_isspace(end, ctx->encoding)) {
+ end++;
+ }
+ if (end > start) {
+ switch (*start) {
+ case 'O' :
+ q->default_op = GRN_OP_OR;
+ break;
+ case GRN_QUERY_AND :
+ q->default_op = GRN_OP_AND;
+ break;
+ case GRN_QUERY_AND_NOT :
+ q->default_op = GRN_OP_AND_NOT;
+ break;
+ case GRN_QUERY_ADJ_INC :
+ q->default_op = GRN_OP_ADJUST;
+ break;
+ }
+ }
+ q->cur = end;
+ break;
+ case 'W' :
+ start = ++end;
+ end = (char *)get_weight_vector(ctx, q, start);
+ q->cur = end;
+ break;
+ }
+ }
+}
+
+static int
+section_weight_cb(grn_ctx *ctx, grn_hash *r, const void *rid, int sid, void *arg)
+{
+ int *w;
+ grn_hash *s = (grn_hash *)arg;
+ if (s && grn_hash_get(ctx, s, &sid, sizeof(grn_id), (void **)&w)) {
+ return *w;
+ } else {
+ return 0;
+ }
+}
+#endif
+
+#include "ecmascript.h"
+#include "ecmascript.c"
+
+static grn_rc
+grn_expr_parser_open(grn_ctx *ctx)
+{
+ if (!ctx->impl->parser) {
+ yyParser *pParser = GRN_MALLOCN(yyParser, 1);
+ if (pParser) {
+ pParser->yyidx = -1;
+#if YYSTACKDEPTH<=0
+ yyGrowStack(pParser);
+#endif
+ ctx->impl->parser = pParser;
+ }
+ }
+ return ctx->rc;
+}
+
+#define PARSE(token) grn_expr_parser(ctx->impl->parser, (token), 0, q)
+
+static void
+accept_query_string(grn_ctx *ctx, efs_info *efsi,
+ const char *str, unsigned int str_size)
+{
+ grn_obj *column, *token;
+ grn_operator mode;
+
+ GRN_PTR_PUT(ctx, &efsi->token_stack,
+ grn_expr_add_str(ctx, efsi->e, str, str_size));
+ {
+ efs_info *q = efsi;
+ PARSE(GRN_EXPR_TOKEN_QSTRING);
+ }
+
+ GRN_PTR_POP(&efsi->token_stack, token);
+ column = grn_ptr_value_at(&efsi->column_stack, -1);
+ grn_expr_append_const(efsi->ctx, efsi->e, column, GRN_OP_GET_VALUE, 1);
+ grn_expr_append_obj(efsi->ctx, efsi->e, token, GRN_OP_PUSH, 1);
+
+ mode = grn_int32_value_at(&efsi->mode_stack, -1);
+ switch (mode) {
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ {
+ int max_interval;
+ max_interval = grn_int32_value_at(&efsi->max_interval_stack, -1);
+ grn_expr_append_const_int(efsi->ctx, efsi->e, max_interval,
+ GRN_OP_PUSH, 1);
+ grn_expr_append_op(efsi->ctx, efsi->e, mode, 3);
+ }
+ break;
+ default :
+ grn_expr_append_op(efsi->ctx, efsi->e, mode, 2);
+ break;
+ }
+}
+
+static grn_rc
+get_word_(grn_ctx *ctx, efs_info *q)
+{
+ const char *end;
+ unsigned int len;
+ GRN_BULK_REWIND(&q->buf);
+ for (end = q->cur;; ) {
+ /* null check and length check */
+ if (!(len = grn_charlen(ctx, end, q->str_end))) {
+ q->cur = q->str_end;
+ break;
+ }
+ if (grn_isspace(end, ctx->encoding) ||
+ *end == GRN_QUERY_PARENL || *end == GRN_QUERY_PARENR) {
+ q->cur = end;
+ break;
+ }
+ if (q->flags & GRN_EXPR_ALLOW_COLUMN && *end == GRN_QUERY_COLUMN) {
+ grn_operator mode;
+ grn_obj *c = grn_obj_column(ctx, q->table,
+ GRN_TEXT_VALUE(&q->buf),
+ GRN_TEXT_LEN(&q->buf));
+ if (c && end + 1 < q->str_end) {
+ // efs_op op;
+ switch (end[1]) {
+ case '!' :
+ mode = GRN_OP_NOT_EQUAL;
+ q->cur = end + 2;
+ break;
+ case '=' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ mode = GRN_OP_ASSIGN;
+ q->cur = end + 2;
+ } else {
+ mode = GRN_OP_EQUAL;
+ q->cur = end + 1;
+ }
+ break;
+ case '<' :
+ if (end + 2 < q->str_end && end[2] == '=') {
+ mode = GRN_OP_LESS_EQUAL;
+ q->cur = end + 3;
+ } else {
+ mode = GRN_OP_LESS;
+ q->cur = end + 2;
+ }
+ break;
+ case '>' :
+ if (end + 2 < q->str_end && end[2] == '=') {
+ mode = GRN_OP_GREATER_EQUAL;
+ q->cur = end + 3;
+ } else {
+ mode = GRN_OP_GREATER;
+ q->cur = end + 2;
+ }
+ break;
+ case '@' :
+ mode = GRN_OP_MATCH;
+ q->cur = end + 2;
+ break;
+ case '^' :
+ mode = GRN_OP_PREFIX;
+ q->cur = end + 2;
+ break;
+ case '$' :
+ mode = GRN_OP_SUFFIX;
+ q->cur = end + 2;
+ break;
+ default :
+ mode = GRN_OP_EQUAL;
+ q->cur = end + 1;
+ break;
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "column lookup failed");
+ q->cur = q->str_end;
+ return ctx->rc;
+ }
+ PARSE(GRN_EXPR_TOKEN_IDENTIFIER);
+ PARSE(GRN_EXPR_TOKEN_RELATIVE_OP);
+
+ GRN_PTR_PUT(ctx, &((grn_expr *)(q->e))->objs, c);
+ GRN_PTR_PUT(ctx, &q->column_stack, c);
+ GRN_INT32_PUT(ctx, &q->mode_stack, mode);
+
+ return GRN_SUCCESS;
+ } else if (*end == GRN_QUERY_PREFIX) {
+ q->cur = end + 1;
+ GRN_INT32_PUT(ctx, &q->mode_stack, GRN_OP_PREFIX);
+ break;
+ } else if (*end == GRN_QUERY_ESCAPE) {
+ end += len;
+ if (!(len = grn_charlen(ctx, end, q->str_end))) {
+ q->cur = q->str_end;
+ break;
+ }
+ }
+ GRN_TEXT_PUT(ctx, &q->buf, end, len);
+ end += len;
+ }
+ accept_query_string(ctx, q, GRN_TEXT_VALUE(&q->buf), GRN_TEXT_LEN(&q->buf));
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+parse_query(grn_ctx *ctx, efs_info *q)
+{
+ int option = 0;
+ grn_operator mode;
+ efs_op op_, *op = &op_;
+ grn_bool first_token = GRN_TRUE;
+ grn_bool block_started = GRN_FALSE;
+
+ op->op = q->default_op;
+ op->weight = DEFAULT_WEIGHT;
+ while (!ctx->rc) {
+ skip_space(ctx, q);
+ if (q->cur >= q->str_end) { goto exit; }
+ switch (*q->cur) {
+ case '\0' :
+ goto exit;
+ break;
+ case GRN_QUERY_PARENR :
+ PARSE(GRN_EXPR_TOKEN_PARENR);
+ q->cur++;
+ break;
+ case GRN_QUERY_QUOTEL :
+ q->cur++;
+
+ {
+ const char *start, *s;
+ start = s = q->cur;
+ GRN_BULK_REWIND(&q->buf);
+ while (1) {
+ unsigned int len;
+ if (s >= q->str_end) {
+ q->cur = s;
+ break;
+ }
+ len = grn_charlen(ctx, s, q->str_end);
+ if (len == 0) {
+ /* invalid string containing malformed multibyte char */
+ goto exit;
+ } else if (len == 1) {
+ if (*s == GRN_QUERY_QUOTER) {
+ q->cur = s + 1;
+ break;
+ } else if (*s == GRN_QUERY_ESCAPE && s + 1 < q->str_end) {
+ s++;
+ len = grn_charlen(ctx, s, q->str_end);
+ }
+ }
+ GRN_TEXT_PUT(ctx, &q->buf, s, len);
+ s += len;
+
+ }
+ accept_query_string(ctx, q,
+ GRN_TEXT_VALUE(&q->buf), GRN_TEXT_LEN(&q->buf));
+ }
+
+ break;
+ case GRN_QUERY_PREFIX :
+ q->cur++;
+ if (get_op(q, op, &mode, &option)) {
+ switch (mode) {
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ GRN_INT32_PUT(ctx, &q->max_interval_stack, option);
+ break;
+ default :
+ break;
+ }
+ GRN_INT32_PUT(ctx, &q->mode_stack, mode);
+ PARSE(GRN_EXPR_TOKEN_RELATIVE_OP);
+ } else {
+ q->cur--;
+ get_word_(ctx, q);
+ }
+ break;
+ case GRN_QUERY_AND :
+ if (!first_token) {
+ op->op = GRN_OP_AND;
+ PARSE(GRN_EXPR_TOKEN_LOGICAL_AND);
+ }
+ q->cur++;
+ break;
+ case GRN_QUERY_AND_NOT :
+ if (first_token && (q->flags & GRN_EXPR_ALLOW_LEADING_NOT)) {
+ grn_obj *all_records = grn_ctx_get(ctx, "all_records", 11);
+ if (all_records) {
+ /* dummy token */
+ PARSE(GRN_EXPR_TOKEN_QSTRING);
+ grn_expr_append_obj(ctx, q->e, all_records, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, q->e, GRN_OP_CALL, 0);
+ }
+ }
+ op->op = GRN_OP_AND_NOT;
+ PARSE(GRN_EXPR_TOKEN_LOGICAL_AND_NOT);
+ q->cur++;
+ break;
+ case GRN_QUERY_ADJ_INC :
+ if (op->weight < 127) { op->weight++; }
+ op->op = GRN_OP_ADJUST;
+ PARSE(GRN_EXPR_TOKEN_ADJUST);
+ q->cur++;
+ break;
+ case GRN_QUERY_ADJ_DEC :
+ if (op->weight > -128) { op->weight--; }
+ op->op = GRN_OP_ADJUST;
+ PARSE(GRN_EXPR_TOKEN_ADJUST);
+ q->cur++;
+ break;
+ case GRN_QUERY_ADJ_NEG :
+ op->op = GRN_OP_ADJUST;
+ op->weight = -1;
+ PARSE(GRN_EXPR_TOKEN_ADJUST);
+ q->cur++;
+ break;
+ case GRN_QUERY_PARENL :
+ PARSE(GRN_EXPR_TOKEN_PARENL);
+ q->cur++;
+ block_started = GRN_TRUE;
+ break;
+ case 'O' :
+ if (q->cur[1] == 'R' && q->cur[2] == ' ') {
+ PARSE(GRN_EXPR_TOKEN_LOGICAL_OR);
+ q->cur += 2;
+ break;
+ }
+ /* fallthru */
+ default :
+ get_word_(ctx, q);
+ break;
+ }
+ first_token = block_started;
+ block_started = GRN_FALSE;
+ }
+exit :
+ PARSE(0);
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+get_string(grn_ctx *ctx, efs_info *q, char quote)
+{
+ const char *s;
+ unsigned int len;
+ grn_rc rc = GRN_END_OF_DATA;
+ GRN_BULK_REWIND(&q->buf);
+ for (s = q->cur + 1; s < q->str_end; s += len) {
+ if (!(len = grn_charlen(ctx, s, q->str_end))) { break; }
+ if (len == 1) {
+ if (*s == quote) {
+ s++;
+ rc = GRN_SUCCESS;
+ break;
+ }
+ if (*s == GRN_QUERY_ESCAPE && s + 1 < q->str_end) {
+ s++;
+ if (!(len = grn_charlen(ctx, s, q->str_end))) { break; }
+ }
+ }
+ GRN_TEXT_PUT(ctx, &q->buf, s, len);
+ }
+ q->cur = s;
+ return rc;
+}
+
+static grn_obj *
+resolve_top_level_name(grn_ctx *ctx, const char *name, unsigned int name_size)
+{
+ unsigned int i;
+ unsigned int first_delimiter_position = 0;
+ unsigned int n_delimiters = 0;
+ grn_obj *top_level_object;
+ grn_obj *object;
+
+ for (i = 0; i < name_size; i++) {
+ if (name[i] != GRN_DB_DELIMITER) {
+ continue;
+ }
+
+ if (n_delimiters == 0) {
+ first_delimiter_position = i;
+ }
+ n_delimiters++;
+ }
+
+ if (n_delimiters < 2) {
+ return grn_ctx_get(ctx, name, name_size);
+ }
+
+ top_level_object = grn_ctx_get(ctx, name, first_delimiter_position);
+ if (!top_level_object) {
+ return NULL;
+ }
+ object = grn_obj_column(ctx, top_level_object,
+ name + first_delimiter_position + 1,
+ name_size - first_delimiter_position - 1);
+ grn_obj_unlink(ctx, top_level_object);
+ return object;
+}
+
+static grn_rc
+get_identifier(grn_ctx *ctx, efs_info *q)
+{
+ const char *s;
+ unsigned int len;
+ grn_rc rc = GRN_SUCCESS;
+ for (s = q->cur; s < q->str_end; s += len) {
+ if (!(len = grn_charlen(ctx, s, q->str_end))) {
+ rc = GRN_END_OF_DATA;
+ goto exit;
+ }
+ if (grn_isspace(s, ctx->encoding)) { goto done; }
+ if (len == 1) {
+ switch (*s) {
+ case '\0' : case '(' : case ')' : case '{' : case '}' :
+ case '[' : case ']' : case ',' : case ':' : case '@' :
+ case '?' : case '"' : case '*' : case '+' : case '-' :
+ case '|' : case '/' : case '%' : case '!' : case '^' :
+ case '&' : case '>' : case '<' : case '=' : case '~' :
+ /* case '.' : */
+ goto done;
+ break;
+ }
+ }
+ }
+done :
+ len = s - q->cur;
+ switch (*q->cur) {
+ case 'd' :
+ if (len == 6 && !memcmp(q->cur, "delete", 6)) {
+ PARSE(GRN_EXPR_TOKEN_DELETE);
+ goto exit;
+ }
+ break;
+ case 'f' :
+ if (len == 5 && !memcmp(q->cur, "false", 5)) {
+ grn_obj buf;
+ PARSE(GRN_EXPR_TOKEN_BOOLEAN);
+ GRN_BOOL_INIT(&buf, 0);
+ GRN_BOOL_SET(ctx, &buf, 0);
+ grn_expr_append_const(ctx, q->e, &buf, GRN_OP_PUSH, 1);
+ GRN_OBJ_FIN(ctx, &buf);
+ goto exit;
+ }
+ break;
+ case 'i' :
+ if (len == 2 && !memcmp(q->cur, "in", 2)) {
+ PARSE(GRN_EXPR_TOKEN_IN);
+ goto exit;
+ }
+ break;
+ case 'n' :
+ if (len == 4 && !memcmp(q->cur, "null", 4)) {
+ grn_obj buf;
+ PARSE(GRN_EXPR_TOKEN_NULL);
+ GRN_VOID_INIT(&buf);
+ grn_expr_append_const(ctx, q->e, &buf, GRN_OP_PUSH, 1);
+ GRN_OBJ_FIN(ctx, &buf);
+ goto exit;
+ }
+ break;
+ case 't' :
+ if (len == 4 && !memcmp(q->cur, "true", 4)) {
+ grn_obj buf;
+ PARSE(GRN_EXPR_TOKEN_BOOLEAN);
+ GRN_BOOL_INIT(&buf, 0);
+ GRN_BOOL_SET(ctx, &buf, 1);
+ grn_expr_append_const(ctx, q->e, &buf, GRN_OP_PUSH, 1);
+ GRN_OBJ_FIN(ctx, &buf);
+ goto exit;
+ }
+ break;
+ }
+ {
+ grn_obj *obj;
+ const char *name = q->cur;
+ unsigned int name_size = s - q->cur;
+ if ((obj = grn_expr_get_var(ctx, q->e, name, name_size))) {
+ PARSE(GRN_EXPR_TOKEN_IDENTIFIER);
+ grn_expr_append_obj(ctx, q->e, obj, GRN_OP_PUSH, 1);
+ goto exit;
+ }
+ if ((obj = grn_obj_column(ctx, q->table, name, name_size))) {
+ GRN_PTR_PUT(ctx, &((grn_expr *)q->e)->objs, obj);
+ PARSE(GRN_EXPR_TOKEN_IDENTIFIER);
+ grn_expr_append_obj(ctx, q->e, obj, GRN_OP_GET_VALUE, 1);
+ goto exit;
+ }
+ if ((obj = resolve_top_level_name(ctx, name, name_size))) {
+ GRN_PTR_PUT(ctx, &((grn_expr *)q->e)->objs, obj);
+ PARSE(GRN_EXPR_TOKEN_IDENTIFIER);
+ grn_expr_append_obj(ctx, q->e, obj, GRN_OP_PUSH, 1);
+ goto exit;
+ }
+ if (q->flags & GRN_EXPR_SYNTAX_OUTPUT_COLUMNS) {
+ PARSE(GRN_EXPR_TOKEN_NONEXISTENT_COLUMN);
+ } else {
+ rc = GRN_SYNTAX_ERROR;
+ }
+ }
+exit :
+ q->cur = s;
+ return rc;
+}
+
+static void
+set_tos_minor_to_curr(grn_ctx *ctx, efs_info *q)
+{
+ yyParser *pParser = ctx->impl->parser;
+ yyStackEntry *yytos = &pParser->yystack[pParser->yyidx];
+ yytos->minor.yy0 = ((grn_expr *)(q->e))->codes_curr;
+}
+
+static grn_rc
+parse_script(grn_ctx *ctx, efs_info *q)
+{
+ grn_rc rc = GRN_SUCCESS;
+ for (;;) {
+ skip_space(ctx, q);
+ if (q->cur >= q->str_end) { rc = GRN_END_OF_DATA; goto exit; }
+ switch (*q->cur) {
+ case '\0' :
+ rc = GRN_END_OF_DATA;
+ goto exit;
+ break;
+ case '(' :
+ PARSE(GRN_EXPR_TOKEN_PARENL);
+ q->cur++;
+ break;
+ case ')' :
+ PARSE(GRN_EXPR_TOKEN_PARENR);
+ q->cur++;
+ break;
+ case '{' :
+ PARSE(GRN_EXPR_TOKEN_BRACEL);
+ q->cur++;
+ break;
+ case '}' :
+ PARSE(GRN_EXPR_TOKEN_BRACER);
+ q->cur++;
+ break;
+ case '[' :
+ PARSE(GRN_EXPR_TOKEN_BRACKETL);
+ q->cur++;
+ break;
+ case ']' :
+ PARSE(GRN_EXPR_TOKEN_BRACKETR);
+ q->cur++;
+ break;
+ case ',' :
+ PARSE(GRN_EXPR_TOKEN_COMMA);
+ q->cur++;
+ break;
+ case '.' :
+ PARSE(GRN_EXPR_TOKEN_DOT);
+ q->cur++;
+ break;
+ case ':' :
+ PARSE(GRN_EXPR_TOKEN_COLON);
+ q->cur++;
+ set_tos_minor_to_curr(ctx, q);
+ grn_expr_append_op(ctx, q->e, GRN_OP_JUMP, 0);
+ break;
+ case '@' :
+ switch (q->cur[1]) {
+ case '^' :
+ PARSE(GRN_EXPR_TOKEN_PREFIX);
+ q->cur += 2;
+ break;
+ case '$' :
+ PARSE(GRN_EXPR_TOKEN_SUFFIX);
+ q->cur += 2;
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_MATCH);
+ q->cur++;
+ break;
+ }
+ break;
+ case '~' :
+ PARSE(GRN_EXPR_TOKEN_BITWISE_NOT);
+ q->cur++;
+ break;
+ case '?' :
+ PARSE(GRN_EXPR_TOKEN_QUESTION);
+ q->cur++;
+ set_tos_minor_to_curr(ctx, q);
+ grn_expr_append_op(ctx, q->e, GRN_OP_CJUMP, 0);
+ break;
+ case '"' :
+ if ((rc = get_string(ctx, q, '"'))) { goto exit; }
+ PARSE(GRN_EXPR_TOKEN_STRING);
+ grn_expr_append_const(ctx, q->e, &q->buf, GRN_OP_PUSH, 1);
+ break;
+ case '\'' :
+ if ((rc = get_string(ctx, q, '\''))) { goto exit; }
+ PARSE(GRN_EXPR_TOKEN_STRING);
+ grn_expr_append_const(ctx, q->e, &q->buf, GRN_OP_PUSH, 1);
+ break;
+ case '*' :
+ switch (q->cur[1]) {
+ case 'N' :
+ PARSE(GRN_EXPR_TOKEN_NEAR);
+ q->cur += 2;
+ break;
+ case 'S' :
+ PARSE(GRN_EXPR_TOKEN_SIMILAR);
+ q->cur += 2;
+ break;
+ case 'T' :
+ PARSE(GRN_EXPR_TOKEN_TERM_EXTRACT);
+ q->cur += 2;
+ break;
+ case '>' :
+ PARSE(GRN_EXPR_TOKEN_ADJUST);
+ q->cur += 2;
+ break;
+ case '<' :
+ PARSE(GRN_EXPR_TOKEN_ADJUST);
+ q->cur += 2;
+ break;
+ case '~' :
+ PARSE(GRN_EXPR_TOKEN_ADJUST);
+ q->cur += 2;
+ break;
+ case '=' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ PARSE(GRN_EXPR_TOKEN_STAR_ASSIGN);
+ q->cur += 2;
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'*=' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_STAR);
+ q->cur++;
+ break;
+ }
+ break;
+ case '+' :
+ switch (q->cur[1]) {
+ case '+' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ PARSE(GRN_EXPR_TOKEN_INCR);
+ q->cur += 2;
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'++' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ case '=' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ PARSE(GRN_EXPR_TOKEN_PLUS_ASSIGN);
+ q->cur += 2;
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'+=' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_PLUS);
+ q->cur++;
+ break;
+ }
+ break;
+ case '-' :
+ switch (q->cur[1]) {
+ case '-' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ PARSE(GRN_EXPR_TOKEN_DECR);
+ q->cur += 2;
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'--' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ case '=' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ PARSE(GRN_EXPR_TOKEN_MINUS_ASSIGN);
+ q->cur += 2;
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'-=' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_MINUS);
+ q->cur++;
+ break;
+ }
+ break;
+ case '|' :
+ switch (q->cur[1]) {
+ case '|' :
+ PARSE(GRN_EXPR_TOKEN_LOGICAL_OR);
+ q->cur += 2;
+ break;
+ case '=' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ PARSE(GRN_EXPR_TOKEN_OR_ASSIGN);
+ q->cur += 2;
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'|=' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_BITWISE_OR);
+ q->cur++;
+ break;
+ }
+ break;
+ case '/' :
+ switch (q->cur[1]) {
+ case '=' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ PARSE(GRN_EXPR_TOKEN_SLASH_ASSIGN);
+ q->cur += 2;
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'/=' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_SLASH);
+ q->cur++;
+ break;
+ }
+ break;
+ case '%' :
+ switch (q->cur[1]) {
+ case '=' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ PARSE(GRN_EXPR_TOKEN_MOD_ASSIGN);
+ q->cur += 2;
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'%%=' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_MOD);
+ q->cur++;
+ break;
+ }
+ break;
+ case '!' :
+ switch (q->cur[1]) {
+ case '=' :
+ PARSE(GRN_EXPR_TOKEN_NOT_EQUAL);
+ q->cur += 2;
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_NOT);
+ q->cur++;
+ break;
+ }
+ break;
+ case '^' :
+ switch (q->cur[1]) {
+ case '=' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ q->cur += 2;
+ PARSE(GRN_EXPR_TOKEN_XOR_ASSIGN);
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'^=' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_BITWISE_XOR);
+ q->cur++;
+ break;
+ }
+ break;
+ case '&' :
+ switch (q->cur[1]) {
+ case '&' :
+ PARSE(GRN_EXPR_TOKEN_LOGICAL_AND);
+ q->cur += 2;
+ break;
+ case '=' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ PARSE(GRN_EXPR_TOKEN_AND_ASSIGN);
+ q->cur += 2;
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'&=' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ case '!' :
+ PARSE(GRN_EXPR_TOKEN_LOGICAL_AND_NOT);
+ q->cur += 2;
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_BITWISE_AND);
+ q->cur++;
+ break;
+ }
+ break;
+ case '>' :
+ switch (q->cur[1]) {
+ case '>' :
+ switch (q->cur[2]) {
+ case '>' :
+ switch (q->cur[3]) {
+ case '=' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ PARSE(GRN_EXPR_TOKEN_SHIFTRR_ASSIGN);
+ q->cur += 4;
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'>>>=' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_SHIFTRR);
+ q->cur += 3;
+ break;
+ }
+ break;
+ case '=' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ PARSE(GRN_EXPR_TOKEN_SHIFTR_ASSIGN);
+ q->cur += 3;
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'>>=' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_SHIFTR);
+ q->cur += 2;
+ break;
+ }
+ break;
+ case '=' :
+ PARSE(GRN_EXPR_TOKEN_GREATER_EQUAL);
+ q->cur += 2;
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_GREATER);
+ q->cur++;
+ break;
+ }
+ break;
+ case '<' :
+ switch (q->cur[1]) {
+ case '<' :
+ switch (q->cur[2]) {
+ case '=' :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ PARSE(GRN_EXPR_TOKEN_SHIFTL_ASSIGN);
+ q->cur += 3;
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'<<=' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_SHIFTL);
+ q->cur += 2;
+ break;
+ }
+ break;
+ case '=' :
+ PARSE(GRN_EXPR_TOKEN_LESS_EQUAL);
+ q->cur += 2;
+ break;
+ default :
+ PARSE(GRN_EXPR_TOKEN_LESS);
+ q->cur++;
+ break;
+ }
+ break;
+ case '=' :
+ switch (q->cur[1]) {
+ case '=' :
+ PARSE(GRN_EXPR_TOKEN_EQUAL);
+ q->cur += 2;
+ break;
+ default :
+ if (q->flags & GRN_EXPR_ALLOW_UPDATE) {
+ PARSE(GRN_EXPR_TOKEN_ASSIGN);
+ q->cur++;
+ } else {
+ ERR(GRN_UPDATE_NOT_ALLOWED,
+ "'=' is not allowed (%.*s)", (int)(q->str_end - q->str), q->str);
+ }
+ break;
+ }
+ break;
+ case '0' : case '1' : case '2' : case '3' : case '4' :
+ case '5' : case '6' : case '7' : case '8' : case '9' :
+ {
+ const char *rest;
+ int64_t int64 = grn_atoll(q->cur, q->str_end, &rest);
+ // checks to see grn_atoll was appropriate
+ // (NOTE: *q->cur begins with a digit. Thus, grn_atoll parses at leaset
+ // one char.)
+ if (q->str_end != rest &&
+ (*rest == '.' || *rest == 'e' || *rest == 'E' ||
+ (*rest >= '0' && *rest <= '9'))) {
+ char *rest_float;
+ double d = strtod(q->cur, &rest_float);
+ grn_obj floatbuf;
+ GRN_FLOAT_INIT(&floatbuf, 0);
+ GRN_FLOAT_SET(ctx, &floatbuf, d);
+ grn_expr_append_const(ctx, q->e, &floatbuf, GRN_OP_PUSH, 1);
+ rest = rest_float;
+ } else {
+ const char *rest64 = rest;
+ unsigned int uint32 = grn_atoui(q->cur, q->str_end, &rest);
+ // checks to see grn_atoi failed (see above NOTE)
+ if ((int64 > UINT32_MAX) ||
+ (q->str_end != rest && *rest >= '0' && *rest <= '9')) {
+ grn_obj int64buf;
+ GRN_INT64_INIT(&int64buf, 0);
+ GRN_INT64_SET(ctx, &int64buf, int64);
+ grn_expr_append_const(ctx, q->e, &int64buf, GRN_OP_PUSH, 1);
+ rest = rest64;
+ } else {
+ grn_obj uint32buf;
+ GRN_UINT32_INIT(&uint32buf, 0);
+ GRN_UINT32_SET(ctx, &uint32buf, uint32);
+ grn_expr_append_const(ctx, q->e, &uint32buf, GRN_OP_PUSH, 1);
+ }
+ }
+ PARSE(GRN_EXPR_TOKEN_DECIMAL);
+ q->cur = rest;
+ }
+ break;
+ default :
+ if ((rc = get_identifier(ctx, q))) { goto exit; }
+ break;
+ }
+ if (ctx->rc) { rc = ctx->rc; break; }
+ }
+exit :
+ PARSE(0);
+ return rc;
+}
+
+grn_rc
+grn_expr_parse(grn_ctx *ctx, grn_obj *expr,
+ const char *str, unsigned int str_size,
+ grn_obj *default_column, grn_operator default_mode,
+ grn_operator default_op, grn_expr_flags flags)
+{
+ efs_info efsi;
+ if (grn_expr_parser_open(ctx)) { return ctx->rc; }
+ GRN_API_ENTER;
+ efsi.ctx = ctx;
+ efsi.str = str;
+ if ((efsi.v = grn_expr_get_var_by_offset(ctx, expr, 0)) &&
+ (efsi.table = grn_ctx_at(ctx, efsi.v->header.domain))) {
+ GRN_TEXT_INIT(&efsi.buf, 0);
+ GRN_INT32_INIT(&efsi.op_stack, GRN_OBJ_VECTOR);
+ GRN_INT32_INIT(&efsi.mode_stack, GRN_OBJ_VECTOR);
+ GRN_INT32_INIT(&efsi.max_interval_stack, GRN_OBJ_VECTOR);
+ GRN_PTR_INIT(&efsi.column_stack, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ GRN_PTR_INIT(&efsi.token_stack, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ efsi.e = expr;
+ efsi.str = str;
+ efsi.cur = str;
+ efsi.str_end = str + str_size;
+ efsi.default_column = default_column;
+ GRN_PTR_PUT(ctx, &efsi.column_stack, default_column);
+ GRN_INT32_PUT(ctx, &efsi.op_stack, default_op);
+ GRN_INT32_PUT(ctx, &efsi.mode_stack, default_mode);
+ efsi.default_flags = efsi.flags = flags;
+ efsi.escalation_threshold = GRN_DEFAULT_MATCH_ESCALATION_THRESHOLD;
+ efsi.escalation_decaystep = DEFAULT_DECAYSTEP;
+ efsi.weight_offset = 0;
+ efsi.opt.weight_vector = NULL;
+ efsi.weight_set = NULL;
+
+ if (flags & (GRN_EXPR_SYNTAX_SCRIPT |
+ GRN_EXPR_SYNTAX_OUTPUT_COLUMNS |
+ GRN_EXPR_SYNTAX_ADJUSTER)) {
+ efs_info *q = &efsi;
+ if (flags & GRN_EXPR_SYNTAX_OUTPUT_COLUMNS) {
+ PARSE(GRN_EXPR_TOKEN_START_OUTPUT_COLUMNS);
+ } else if (flags & GRN_EXPR_SYNTAX_ADJUSTER) {
+ PARSE(GRN_EXPR_TOKEN_START_ADJUSTER);
+ }
+ parse_script(ctx, &efsi);
+ } else {
+ parse_query(ctx, &efsi);
+ }
+
+ /*
+ grn_obj strbuf;
+ GRN_TEXT_INIT(&strbuf, 0);
+ grn_expr_inspect(ctx, &strbuf, expr);
+ GRN_TEXT_PUTC(ctx, &strbuf, '\0');
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "query=(%s)", GRN_TEXT_VALUE(&strbuf));
+ GRN_OBJ_FIN(ctx, &strbuf);
+ */
+
+ /*
+ efsi.opt.vector_size = DEFAULT_WEIGHT_VECTOR_SIZE;
+ efsi.opt.func = efsi.weight_set ? section_weight_cb : NULL;
+ efsi.opt.func_arg = efsi.weight_set;
+ efsi.snip_conds = NULL;
+ */
+ GRN_OBJ_FIN(ctx, &efsi.op_stack);
+ GRN_OBJ_FIN(ctx, &efsi.mode_stack);
+ GRN_OBJ_FIN(ctx, &efsi.max_interval_stack);
+ GRN_OBJ_FIN(ctx, &efsi.column_stack);
+ GRN_OBJ_FIN(ctx, &efsi.token_stack);
+ GRN_OBJ_FIN(ctx, &efsi.buf);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "variable is not defined correctly");
+ }
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_rc
+grn_expr_parser_close(grn_ctx *ctx)
+{
+ if (ctx->impl->parser) {
+ yyParser *pParser = (yyParser*)ctx->impl->parser;
+ while (pParser->yyidx >= 0) yy_pop_parser_stack(pParser);
+#if YYSTACKDEPTH<=0
+ free(pParser->yystack);
+#endif
+ GRN_FREE(pParser);
+ ctx->impl->parser = NULL;
+ }
+ return ctx->rc;
+}
+
+grn_rc
+grn_expr_get_keywords(grn_ctx *ctx, grn_obj *expr, grn_obj *keywords)
+{
+ int i, n;
+ scan_info **sis, *si;
+ GRN_API_ENTER;
+ if ((sis = scan_info_build(ctx, expr, &n, GRN_OP_OR, 0))) {
+ int butp = 0, nparens = 0, npbut = 0;
+ grn_obj but_stack;
+ GRN_UINT32_INIT(&but_stack, GRN_OBJ_VECTOR);
+ for (i = n; i--;) {
+ si = sis[i];
+ if (si->flags & SCAN_POP) {
+ nparens++;
+ if (si->logical_op == GRN_OP_AND_NOT) {
+ GRN_UINT32_PUT(ctx, &but_stack, npbut);
+ npbut = nparens;
+ butp = 1 - butp;
+ }
+ } else {
+ if (si->op == GRN_OP_MATCH && si->query) {
+ if (butp == (si->logical_op == GRN_OP_AND_NOT)) {
+ GRN_PTR_PUT(ctx, keywords, si->query);
+ }
+ }
+ if (si->flags & SCAN_PUSH) {
+ if (nparens == npbut) {
+ butp = 1 - butp;
+ GRN_UINT32_POP(&but_stack, npbut);
+ }
+ nparens--;
+ }
+ }
+ }
+ GRN_OBJ_FIN(ctx, &but_stack);
+ for (i = n; i--;) { SI_FREE(sis[i]); }
+ GRN_FREE(sis);
+ }
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+grn_rc
+grn_expr_snip_add_conditions(grn_ctx *ctx, grn_obj *expr, grn_obj *snip,
+ unsigned int n_tags,
+ const char **opentags, unsigned int *opentag_lens,
+ const char **closetags, unsigned int *closetag_lens)
+{
+ grn_rc rc;
+ grn_obj keywords;
+
+ GRN_API_ENTER;
+
+ GRN_PTR_INIT(&keywords, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ rc = grn_expr_get_keywords(ctx, expr, &keywords);
+ if (rc != GRN_SUCCESS) {
+ GRN_OBJ_FIN(ctx, &keywords);
+ GRN_API_RETURN(rc);
+ }
+
+ if (n_tags) {
+ int i;
+ for (i = 0;; i = (i + 1) % n_tags) {
+ grn_obj *keyword;
+ GRN_PTR_POP(&keywords, keyword);
+ if (!keyword) { break; }
+ grn_snip_add_cond(ctx, snip,
+ GRN_TEXT_VALUE(keyword), GRN_TEXT_LEN(keyword),
+ opentags[i], opentag_lens[i],
+ closetags[i], closetag_lens[i]);
+ }
+ } else {
+ for (;;) {
+ grn_obj *keyword;
+ GRN_PTR_POP(&keywords, keyword);
+ if (!keyword) { break; }
+ grn_snip_add_cond(ctx, snip,
+ GRN_TEXT_VALUE(keyword), GRN_TEXT_LEN(keyword),
+ NULL, 0, NULL, 0);
+ }
+ }
+ GRN_OBJ_FIN(ctx, &keywords);
+
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+grn_obj *
+grn_expr_snip(grn_ctx *ctx, grn_obj *expr, int flags,
+ unsigned int width, unsigned int max_results,
+ unsigned int n_tags,
+ const char **opentags, unsigned int *opentag_lens,
+ const char **closetags, unsigned int *closetag_lens,
+ grn_snip_mapping *mapping)
+{
+ grn_obj *res = NULL;
+ GRN_API_ENTER;
+ if ((res = grn_snip_open(ctx, flags, width, max_results,
+ NULL, 0, NULL, 0, mapping))) {
+ grn_expr_snip_add_conditions(ctx, expr, res,
+ n_tags,
+ opentags, opentag_lens,
+ closetags, closetag_lens);
+ }
+ GRN_API_RETURN(res);
+}
+
+/*
+ So far, grn_column_filter() is nothing but a very rough prototype.
+ Although GRN_COLUMN_EACH() can accelerate many range queries,
+ the following stuff must be resolved one by one.
+
+ * support accessors as column
+ * support tables which have deleted records
+ * support various operators
+ * support various column types
+*/
+grn_rc
+grn_column_filter(grn_ctx *ctx, grn_obj *column,
+ grn_operator operator,
+ grn_obj *value, grn_obj *result_set,
+ grn_operator set_operation)
+{
+ uint32_t *vp;
+ grn_ii_posting posting;
+ uint32_t value_ = grn_atoui(GRN_TEXT_VALUE(value), GRN_BULK_CURR(value), NULL);
+ posting.sid = 1;
+ posting.pos = 0;
+ posting.weight = 0;
+ GRN_COLUMN_EACH(ctx, column, id, vp, {
+ if (*vp < value_) {
+ posting.rid = id;
+ grn_ii_posting_add(ctx, &posting, (grn_hash *)result_set, set_operation);
+ }
+ });
+ grn_ii_resolve_sel_and(ctx, (grn_hash *)result_set, set_operation);
+ return ctx->rc;
+}
+
+grn_rc
+grn_expr_syntax_escape(grn_ctx *ctx, const char *string, int string_size,
+ const char *target_characters,
+ char escape_character,
+ grn_obj *escaped_string)
+{
+ grn_rc rc = GRN_SUCCESS;
+ const char *current, *string_end;
+
+ if (!string) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ GRN_API_ENTER;
+ if (string_size < 0) {
+ string_size = strlen(string);
+ }
+ string_end = string + string_size;
+
+ current = string;
+ while (current < string_end) {
+ unsigned int char_size;
+ char_size = grn_charlen(ctx, current, string_end);
+ switch (char_size) {
+ case 0 :
+ /* string includes malformed multibyte character. */
+ return GRN_INVALID_ARGUMENT;
+ break;
+ case 1 :
+ if (strchr(target_characters, *current)) {
+ GRN_TEXT_PUTC(ctx, escaped_string, escape_character);
+ }
+ GRN_TEXT_PUT(ctx, escaped_string, current, char_size);
+ current += char_size;
+ break;
+ default :
+ GRN_TEXT_PUT(ctx, escaped_string, current, char_size);
+ current += char_size;
+ break;
+ }
+ }
+
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_expr_syntax_escape_query(grn_ctx *ctx, const char *query, int query_size,
+ grn_obj *escaped_query)
+{
+ const char target_characters[] = {
+ GRN_QUERY_AND,
+ GRN_QUERY_AND_NOT,
+ GRN_QUERY_ADJ_INC,
+ GRN_QUERY_ADJ_DEC,
+ GRN_QUERY_ADJ_NEG,
+ GRN_QUERY_PREFIX,
+ GRN_QUERY_PARENL,
+ GRN_QUERY_PARENR,
+ GRN_QUERY_QUOTEL,
+ GRN_QUERY_ESCAPE,
+ GRN_QUERY_COLUMN,
+ '\0',
+ };
+ return grn_expr_syntax_escape(ctx, query, query_size,
+ target_characters, GRN_QUERY_ESCAPE,
+ escaped_query);
+}
diff --git a/storage/mroonga/vendor/groonga/lib/expr.h b/storage/mroonga/vendor/groonga/lib/expr.h
new file mode 100644
index 00000000000..a42be924f58
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/expr.h
@@ -0,0 +1,70 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_EXPR_H
+#define GRN_EXPR_H
+
+#include "db.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SCAN_ACCESSOR (0x01)
+#define SCAN_PUSH (0x02)
+#define SCAN_POP (0x04)
+#define SCAN_PRE_CONST (0x08)
+
+typedef enum {
+ SCAN_START = 0,
+ SCAN_VAR,
+ SCAN_COL1,
+ SCAN_COL2,
+ SCAN_CONST
+} scan_stat;
+
+typedef struct _grn_scan_info scan_info;
+typedef grn_bool (*grn_scan_info_each_arg_callback)(grn_ctx *ctx, grn_obj *obj, void *user_data);
+
+scan_info *grn_scan_info_open(grn_ctx *ctx, int start);
+void grn_scan_info_close(grn_ctx *ctx, scan_info *si);
+void grn_scan_info_put_index(grn_ctx *ctx, scan_info *si, grn_obj *index,
+ uint32_t sid, int32_t weight);
+scan_info **grn_scan_info_put_logical_op(grn_ctx *ctx, scan_info **sis, int *ip,
+ grn_operator op, int start);
+int grn_scan_info_get_flags(scan_info *si);
+void grn_scan_info_set_flags(scan_info *si, int flags);
+grn_operator grn_scan_info_get_logical_op(scan_info *si);
+void grn_scan_info_set_logical_op(scan_info *si, grn_operator logical_op);
+grn_operator grn_scan_info_get_op(scan_info *si);
+void grn_scan_info_set_op(scan_info *si, grn_operator op);
+void grn_scan_info_set_end(scan_info *si, uint32_t end);
+void grn_scan_info_set_query(scan_info *si, grn_obj *query);
+int grn_scan_info_get_max_interval(scan_info *si);
+void grn_scan_info_set_max_interval(scan_info *si, int max_interval);
+grn_bool grn_scan_info_push_arg(scan_info *si, grn_obj *arg);
+grn_obj *grn_scan_info_get_arg(grn_ctx *ctx, scan_info *si, int i);
+
+int32_t grn_expr_code_get_weight(grn_ctx *ctx, grn_expr_code *ec);
+grn_rc grn_expr_get_keywords(grn_ctx *ctx, grn_obj *expr, grn_obj *keywords);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_EXPR_H */
diff --git a/storage/mroonga/vendor/groonga/lib/geo.c b/storage/mroonga/vendor/groonga/lib/geo.c
new file mode 100644
index 00000000000..1bb621487a8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/geo.c
@@ -0,0 +1,2682 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "geo.h"
+#include "pat.h"
+#include "util.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#define GRN_GEO_POINT_IN_NORTH_EAST(point) \
+ ((point)->latitude >= 0 && (point)->longitude >= 0)
+#define GRN_GEO_POINT_IN_NORTH_WEST(point) \
+ ((point)->latitude >= 0 && (point)->longitude < 0)
+#define GRN_GEO_POINT_IN_SOUTH_WEST(point) \
+ ((point)->latitude < 0 && (point)->longitude < 0)
+#define GRN_GEO_POINT_IN_SOUTH_EAST(point) \
+ ((point)->latitude < 0 && (point)->longitude >= 0)
+
+#define GRN_GEO_LONGITUDE_IS_WRAPPED(top_left, bottom_right) \
+ ((top_left)->longitude > 0 && (bottom_right)->longitude < 0)
+
+typedef struct {
+ grn_id id;
+ double d;
+} geo_entry;
+
+typedef struct
+{
+ grn_geo_point key;
+ int key_size;
+} mesh_entry;
+
+typedef struct {
+ grn_obj *pat;
+ grn_obj top_left_point_buffer;
+ grn_obj bottom_right_point_buffer;
+ grn_geo_point *top_left;
+ grn_geo_point *bottom_right;
+} in_rectangle_data;
+
+typedef struct {
+ grn_geo_point min;
+ grn_geo_point max;
+ int rectangle_common_bit;
+ uint8_t rectangle_common_key[sizeof(grn_geo_point)];
+} in_rectangle_area_data;
+
+static int
+compute_diff_bit(uint8_t *geo_key1, uint8_t *geo_key2)
+{
+ int i, j, diff_bit = 0;
+
+ for (i = 0; i < sizeof(grn_geo_point); i++) {
+ if (geo_key1[i] != geo_key2[i]) {
+ diff_bit = 8;
+ for (j = 0; j < 8; j++) {
+ if ((geo_key1[i] & (1 << (7 - j))) != (geo_key2[i] & (1 << (7 - j)))) {
+ diff_bit = j;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return i * 8 + diff_bit;
+}
+
+static void
+compute_min_and_max_key(uint8_t *key_base, int diff_bit,
+ uint8_t *key_min, uint8_t *key_max)
+{
+ int diff_byte, diff_bit_mask;
+
+ diff_byte = diff_bit / 8;
+ diff_bit_mask = 0xff >> (diff_bit % 8);
+
+ if (diff_byte == sizeof(grn_geo_point)) {
+ if (key_min) { memcpy(key_min, key_base, diff_byte); }
+ if (key_max) { memcpy(key_max, key_base, diff_byte); }
+ } else {
+ if (key_min) {
+ memcpy(key_min, key_base, diff_byte + 1);
+ key_min[diff_byte] &= ~diff_bit_mask;
+ memset(key_min + diff_byte + 1, 0,
+ sizeof(grn_geo_point) - diff_byte - 1);
+ }
+
+ if (key_max) {
+ memcpy(key_max, key_base, diff_byte + 1);
+ key_max[diff_byte] |= diff_bit_mask;
+ memset(key_max + diff_byte + 1, 0xff,
+ sizeof(grn_geo_point) - diff_byte - 1);
+ }
+ }
+}
+
+static void
+compute_min_and_max(grn_geo_point *base_point, int diff_bit,
+ grn_geo_point *geo_min, grn_geo_point *geo_max)
+{
+ uint8_t geo_key_base[sizeof(grn_geo_point)];
+ uint8_t geo_key_min[sizeof(grn_geo_point)];
+ uint8_t geo_key_max[sizeof(grn_geo_point)];
+
+ grn_gton(geo_key_base, base_point, sizeof(grn_geo_point));
+ compute_min_and_max_key(geo_key_base, diff_bit,
+ geo_min ? geo_key_min : NULL,
+ geo_max ? geo_key_max : NULL);
+ if (geo_min) {
+ grn_ntog((uint8_t *)geo_min, geo_key_min, sizeof(grn_geo_point));
+ }
+ if (geo_max) {
+ grn_ntog((uint8_t *)geo_max, geo_key_max, sizeof(grn_geo_point));
+ }
+}
+
+/* #define GEO_DEBUG */
+
+#ifdef GEO_DEBUG
+#include <stdio.h>
+
+static void
+inspect_mesh(grn_ctx *ctx, grn_geo_point *key, int key_size, int n)
+{
+ grn_geo_point min, max;
+
+ printf("mesh: %d:%d\n", n, key_size);
+
+ printf("key: ");
+ grn_p_geo_point(ctx, key);
+
+ compute_min_and_max(key, key_size, &min, &max);
+ printf("min: ");
+ grn_p_geo_point(ctx, &min);
+ printf("max: ");
+ grn_p_geo_point(ctx, &max);
+}
+
+static void
+inspect_mesh_entry(grn_ctx *ctx, mesh_entry *entries, int n)
+{
+ mesh_entry *entry;
+
+ entry = entries + n;
+ inspect_mesh(ctx, &(entry->key), entry->key_size, n);
+}
+
+static void
+inspect_tid(grn_ctx *ctx, grn_id tid, grn_geo_point *point, double d)
+{
+ printf("tid: %d:%g", tid, d);
+ grn_p_geo_point(ctx, point);
+}
+
+static void
+inspect_key(grn_ctx *ctx, uint8_t *key)
+{
+ int i;
+ for (i = 0; i < 8; i++) {
+ int j;
+ for (j = 0; j < 8; j++) {
+ printf("%d", (key[i] & (1 << (7 - j))) >> (7 - j));
+ }
+ printf(" ");
+ }
+ printf("\n");
+}
+
+static void
+print_key_mark(grn_ctx *ctx, int target_bit)
+{
+ int i;
+
+ for (i = 0; i < target_bit; i++) {
+ printf(" ");
+ if (i > 0 && i % 8 == 0) {
+ printf(" ");
+ }
+ }
+ if (i > 0 && i % 8 == 0) {
+ printf(" ");
+ }
+ printf("^\n");
+}
+
+static void
+inspect_cursor_entry(grn_ctx *ctx, grn_geo_cursor_entry *entry)
+{
+ grn_geo_point point;
+
+ printf("entry: ");
+ grn_ntog((uint8_t *)&point, entry->key, sizeof(grn_geo_point));
+ grn_p_geo_point(ctx, &point);
+ inspect_key(ctx, entry->key);
+ print_key_mark(ctx, entry->target_bit);
+
+ printf(" target bit: %d\n", entry->target_bit);
+
+#define INSPECT_STATUS_FLAG(name) \
+ ((entry->status_flags & GRN_GEO_CURSOR_ENTRY_STATUS_ ## name) ? "true" : "false")
+
+ printf(" top included: %s\n", INSPECT_STATUS_FLAG(TOP_INCLUDED));
+ printf("bottom included: %s\n", INSPECT_STATUS_FLAG(BOTTOM_INCLUDED));
+ printf(" left included: %s\n", INSPECT_STATUS_FLAG(LEFT_INCLUDED));
+ printf(" right included: %s\n", INSPECT_STATUS_FLAG(RIGHT_INCLUDED));
+ printf(" latitude inner: %s\n", INSPECT_STATUS_FLAG(LATITUDE_INNER));
+ printf("longitude inner: %s\n", INSPECT_STATUS_FLAG(LONGITUDE_INNER));
+
+#undef INSPECT_STATUS_FLAG
+}
+
+static void
+inspect_cursor_entry_targets(grn_ctx *ctx, grn_geo_cursor_entry *entry,
+ uint8_t *top_left_key, uint8_t *bottom_right_key,
+ grn_geo_cursor_entry *next_entry0,
+ grn_geo_cursor_entry *next_entry1)
+{
+ printf("entry: ");
+ inspect_key(ctx, entry->key);
+ printf("top-left: ");
+ inspect_key(ctx, top_left_key);
+ printf("bottom-right: ");
+ inspect_key(ctx, bottom_right_key);
+ printf("next-entry-0: ");
+ inspect_key(ctx, next_entry0->key);
+ printf("next-entry-1: ");
+ inspect_key(ctx, next_entry1->key);
+ printf(" ");
+ print_key_mark(ctx, entry->target_bit + 1);
+}
+#else
+# define inspect_mesh(...)
+# define inspect_mesh_entry(...)
+# define inspect_tid(...)
+# define inspect_key(...)
+# define print_key_mark(...)
+# define inspect_cursor_entry(...)
+# define inspect_cursor_entry_targets(...)
+#endif
+
+static int
+grn_geo_table_sort_detect_far_point(grn_ctx *ctx, grn_obj *table, grn_obj *index,
+ grn_pat *pat, geo_entry *entries,
+ grn_pat_cursor *pc, int n,
+ grn_bool accessorp,
+ grn_geo_point *base_point,
+ double *d_far, int *diff_bit)
+{
+ int i = 0, diff_bit_prev, diff_bit_current;
+ grn_id tid;
+ geo_entry *ep, *p;
+ double d;
+ uint8_t geo_key_prev[sizeof(grn_geo_point)];
+ uint8_t geo_key_curr[sizeof(grn_geo_point)];
+ grn_geo_point point;
+
+ *d_far = 0.0;
+ grn_gton(geo_key_curr, base_point, sizeof(grn_geo_point));
+ *diff_bit = sizeof(grn_geo_point) * 8;
+ diff_bit_current = sizeof(grn_geo_point) * 8;
+ memcpy(&point, base_point, sizeof(grn_geo_point));
+ ep = entries;
+ inspect_mesh(ctx, &point, *diff_bit, -1);
+ while ((tid = grn_pat_cursor_next(ctx, pc))) {
+ grn_ii_cursor *ic = grn_ii_cursor_open(ctx, (grn_ii *)index, tid, 0, 0, 1, 0);
+ if (ic) {
+ grn_ii_posting *posting;
+ grn_gton(geo_key_prev, &point, sizeof(grn_geo_point));
+ grn_pat_get_key(ctx, pat, tid, &point, sizeof(grn_geo_point));
+ grn_gton(geo_key_curr, &point, sizeof(grn_geo_point));
+ d = grn_geo_distance_rectangle_raw(ctx, base_point, &point);
+ inspect_tid(ctx, tid, &point, d);
+
+ diff_bit_prev = diff_bit_current;
+ diff_bit_current = compute_diff_bit(geo_key_curr, geo_key_prev);
+#ifdef GEO_DEBUG
+ printf("diff: %d:%d:%d\n", *diff_bit, diff_bit_prev, diff_bit_current);
+#endif
+ if ((diff_bit_current % 2) == 1) {
+ diff_bit_current--;
+ }
+ if (diff_bit_current < diff_bit_prev && *diff_bit > diff_bit_current) {
+ if (i == n) {
+ grn_ii_cursor_close(ctx, ic);
+ break;
+ }
+ *diff_bit = diff_bit_current;
+ }
+
+ if (d > *d_far) {
+ *d_far = d;
+ }
+ while ((posting = grn_ii_cursor_next(ctx, ic))) {
+ grn_id rid = accessorp
+ ? grn_table_get(ctx, table, &posting->rid, sizeof(grn_id))
+ : posting->rid;
+ if (rid) {
+ for (p = ep; entries < p && p[-1].d > d; p--) {
+ p->id = p[-1].id;
+ p->d = p[-1].d;
+ }
+ p->id = rid;
+ p->d = d;
+ if (i < n) {
+ ep++;
+ i++;
+ }
+ }
+ }
+ grn_ii_cursor_close(ctx, ic);
+ }
+ }
+
+ return i;
+}
+
+typedef enum {
+ MESH_LEFT_TOP,
+ MESH_RIGHT_TOP,
+ MESH_RIGHT_BOTTOM,
+ MESH_LEFT_BOTTOM
+} mesh_position;
+
+/*
+ meshes should have
+ 86 >= spaces when include_base_point_hash == GRN_FALSE,
+ 87 >= spaces when include_base_point_hash == GRN_TRUE.
+*/
+static int
+grn_geo_get_meshes_for_circle(grn_ctx *ctx, grn_geo_point *base_point,
+ double d_far, int diff_bit,
+ int include_base_point_mesh,
+ mesh_entry *meshes)
+{
+ double d;
+ int n_meshes;
+ int lat_diff, lng_diff;
+ mesh_position position;
+ grn_geo_point geo_base, geo_min, geo_max;
+
+ compute_min_and_max(base_point, diff_bit - 2, &geo_min, &geo_max);
+
+ lat_diff = (geo_max.latitude - geo_min.latitude + 1) / 2;
+ lng_diff = (geo_max.longitude - geo_min.longitude + 1) / 2;
+ geo_base.latitude = geo_min.latitude + lat_diff;
+ geo_base.longitude = geo_min.longitude + lng_diff;
+ if (base_point->latitude >= geo_base.latitude) {
+ if (base_point->longitude >= geo_base.longitude) {
+ position = MESH_RIGHT_TOP;
+ } else {
+ position = MESH_LEFT_TOP;
+ }
+ } else {
+ if (base_point->longitude >= geo_base.longitude) {
+ position = MESH_RIGHT_BOTTOM;
+ } else {
+ position = MESH_LEFT_BOTTOM;
+ }
+ }
+ /*
+ base_point: b
+ geo_min: i
+ geo_max: a
+ geo_base: x: must be at the left bottom in the top right mesh.
+
+ e.g.: base_point is at the left bottom mesh case:
+ +------+------+
+ | | a|
+ | |x |
+ ^+------+------+
+ || | |
+ lng_diff || b | |
+ \/i------+------+
+ <------>
+ lat_diff
+
+ grn_min + lat_diff -> the right mesh.
+ grn_min + lng_diff -> the top mesh.
+ */
+#ifdef GEO_DEBUG
+ grn_p_geo_point(ctx, base_point);
+ printf("base: ");
+ grn_p_geo_point(ctx, &geo_base);
+ printf("min: ");
+ grn_p_geo_point(ctx, &geo_min);
+ printf("max: ");
+ grn_p_geo_point(ctx, &geo_max);
+ printf("diff: %d (%d, %d)\n", diff_bit, lat_diff, lng_diff);
+ switch (position) {
+ case MESH_LEFT_TOP :
+ printf("position: left-top\n");
+ break;
+ case MESH_RIGHT_TOP :
+ printf("position: right-top\n");
+ break;
+ case MESH_RIGHT_BOTTOM :
+ printf("position: right-bottom\n");
+ break;
+ case MESH_LEFT_BOTTOM :
+ printf("position: left-bottom\n");
+ break;
+ }
+#endif
+
+ n_meshes = 0;
+
+#define add_mesh(lat_diff_,lng_diff_,key_size_) do {\
+ meshes[n_meshes].key.latitude = geo_base.latitude + (lat_diff_);\
+ meshes[n_meshes].key.longitude = geo_base.longitude + (lng_diff_);\
+ meshes[n_meshes].key_size = key_size_;\
+ n_meshes++;\
+} while (0)
+
+ if (include_base_point_mesh || position != MESH_LEFT_TOP) {
+ add_mesh(0, -lng_diff, diff_bit);
+ }
+ if (include_base_point_mesh || position != MESH_RIGHT_TOP) {
+ add_mesh(0, 0, diff_bit);
+ }
+ if (include_base_point_mesh || position != MESH_RIGHT_BOTTOM) {
+ add_mesh(-lat_diff, 0, diff_bit);
+ }
+ if (include_base_point_mesh || position != MESH_LEFT_BOTTOM) {
+ add_mesh(-lat_diff, -lng_diff, diff_bit);
+ }
+
+ /*
+ b: base_point
+ x: geo_base
+ 0-83: sub meshes. 0-83 are added order.
+
+ j: -5 -4 -3 -2 -1 0 1 2 3 4
+ +---+---+---+---+---+---+---+---+---+---+
+ |74 |75 |76 |77 |78 |79 |80 |81 |82 |83 | 4
+ +---+---+---+---+---+---+---+---+---+---+
+ |64 |65 |66 |67 |68 |69 |70 |71 |72 |73 | 3
+ +---+---+---+---+---+---+---+---+---+---+
+ |54 |55 |56 |57 |58 |59 |60 |61 |62 |63 | 2
+ +---+---+---+---+---+---+---+---+---+---+
+ |48 |49 |50 | b | |51 |52 |53 | 1
+ +---+---+---+ | +---+---+---+
+ |42 |43 |44 | |x |45 |46 |47 | 0
+ +---+---+---+-------+-------+---+---+---+
+ |36 |37 |38 | | |39 |40 |41 | -1
+ +---+---+---+ base meshes +---+---+---+
+ |30 |31 |32 | | |33 |34 |35 | -2
+ +---+---+---+---+---+---+---+---+---+---+
+ |20 |21 |22 |23 |24 |25 |26 |27 |28 |29 | -3
+ +---+---+---+---+---+---+---+---+---+---+
+ |10 |11 |12 |13 |14 |15 |16 |17 |18 |19 | -4
+ +---+---+---+---+---+---+---+---+---+---+
+ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -5
+ +---+---+---+---+---+---+---+---+---+---+
+ i
+ */
+ {
+ int i, j, n_sub_meshes, lat, lat_min, lat_max, lng, lng_min, lng_max;
+ n_sub_meshes = 0;
+ for (i = -5; i < 5; i++) {
+ lat_min = ((lat_diff + 1) / 2) * i;
+ lat_max = ((lat_diff + 1) / 2) * (i + 1) - 1;
+ for (j = -5; j < 5; j++) {
+ if (-3 < i && i < 2 && -3 < j && j < 2) {
+ continue;
+ }
+ lng_min = ((lng_diff + 1) / 2) * j;
+ lng_max = ((lng_diff + 1) / 2) * (j + 1) - 1;
+ if (base_point->latitude <= geo_base.latitude + lat_min) {
+ lat = geo_base.latitude + lat_min;
+ } else if (geo_base.latitude + lat_max < base_point->latitude) {
+ lat = geo_base.latitude + lat_max;
+ } else {
+ lat = base_point->latitude;
+ }
+ if (base_point->longitude <= geo_base.longitude + lng_min) {
+ lng = geo_base.longitude + lng_min;
+ } else if (geo_base.longitude + lng_max < base_point->longitude) {
+ lng = geo_base.longitude + lng_max;
+ } else {
+ lng = base_point->longitude;
+ }
+ meshes[n_meshes].key.latitude = lat;
+ meshes[n_meshes].key.longitude = lng;
+ d = grn_geo_distance_rectangle_raw(ctx, base_point,
+ &(meshes[n_meshes].key));
+ if (d < d_far) {
+#ifdef GEO_DEBUG
+ printf("sub-mesh: %d: (%d,%d): (%d,%d;%d,%d)\n",
+ n_sub_meshes, base_point->latitude, base_point->longitude,
+ geo_base.latitude + lat_min,
+ geo_base.latitude + lat_max,
+ geo_base.longitude + lng_min,
+ geo_base.longitude + lng_max);
+ grn_p_geo_point(ctx, &(meshes[n_meshes].key));
+#endif
+ meshes[n_meshes].key_size = diff_bit + 2;
+ n_meshes++;
+ }
+ n_sub_meshes++;
+ }
+ }
+ }
+
+#undef add_mesh
+
+ return n_meshes;
+}
+
+static int
+grn_geo_table_sort_collect_points(grn_ctx *ctx, grn_obj *table, grn_obj *index,
+ grn_pat *pat,
+ geo_entry *entries, int n_entries,
+ int n, grn_bool accessorp,
+ grn_geo_point *base_point,
+ double d_far, int diff_bit)
+{
+ int n_meshes;
+ mesh_entry meshes[86];
+ geo_entry *ep, *p;
+
+ n_meshes = grn_geo_get_meshes_for_circle(ctx, base_point, d_far, diff_bit,
+ GRN_FALSE, meshes);
+
+ ep = entries + n_entries;
+ while (n_meshes--) {
+ grn_id tid;
+ grn_pat_cursor *pc = grn_pat_cursor_open(ctx, pat,
+ &(meshes[n_meshes].key),
+ meshes[n_meshes].key_size,
+ NULL, 0,
+ 0, -1,
+ GRN_CURSOR_PREFIX|GRN_CURSOR_SIZE_BY_BIT);
+ inspect_mesh_entry(ctx, meshes, n_meshes);
+ if (pc) {
+ while ((tid = grn_pat_cursor_next(ctx, pc))) {
+ grn_ii_cursor *ic = grn_ii_cursor_open(ctx, (grn_ii *)index, tid, 0, 0, 1, 0);
+ if (ic) {
+ double d;
+ grn_geo_point pos;
+ grn_ii_posting *posting;
+ grn_pat_get_key(ctx, pat, tid, &pos, sizeof(grn_geo_point));
+ d = grn_geo_distance_rectangle_raw(ctx, base_point, &pos);
+ inspect_tid(ctx, tid, &pos, d);
+ while ((posting = grn_ii_cursor_next(ctx, ic))) {
+ grn_id rid = accessorp
+ ? grn_table_get(ctx, table, &posting->rid, sizeof(grn_id))
+ : posting->rid;
+ if (rid) {
+ for (p = ep; entries < p && p[-1].d > d; p--) {
+ p->id = p[-1].id;
+ p->d = p[-1].d;
+ }
+ p->id = rid;
+ p->d = d;
+ if (n_entries < n) {
+ ep++;
+ n_entries++;
+ }
+ }
+ }
+ grn_ii_cursor_close(ctx, ic);
+ }
+ }
+ grn_pat_cursor_close(ctx, pc);
+ }
+ }
+ return n_entries;
+}
+
+static inline grn_obj *
+find_geo_sort_index(grn_ctx *ctx, grn_obj *key)
+{
+ grn_obj *index = NULL;
+
+ if (GRN_ACCESSORP(key)) {
+ grn_accessor *accessor = (grn_accessor *)key;
+ if (accessor->action != GRN_ACCESSOR_GET_KEY) {
+ return NULL;
+ }
+ if (!(DB_OBJ(accessor->obj)->id & GRN_OBJ_TMP_OBJECT)) {
+ return NULL;
+ }
+ if (accessor->obj->header.type != GRN_TABLE_HASH_KEY) {
+ return NULL;
+ }
+ if (!accessor->next) {
+ return NULL;
+ }
+ grn_column_index(ctx, accessor->next->obj, GRN_OP_LESS, &index, 1, NULL);
+ } else {
+ grn_column_index(ctx, key, GRN_OP_LESS, &index, 1, NULL);
+ }
+
+ return index;
+}
+
+static inline int
+grn_geo_table_sort_by_distance(grn_ctx *ctx,
+ grn_obj *table,
+ grn_obj *index,
+ grn_pat *pat,
+ grn_pat_cursor *pc,
+ grn_bool accessorp,
+ grn_geo_point *base_point,
+ int offset,
+ int limit,
+ grn_obj *result)
+{
+ int n_entries = 0, e = offset + limit;
+ geo_entry *entries;
+
+ if ((entries = GRN_MALLOC(sizeof(geo_entry) * (e + 1)))) {
+ int n, diff_bit;
+ double d_far;
+ geo_entry *ep;
+ grn_bool need_not_indexed_records;
+ grn_hash *indexed_records = NULL;
+
+ n = grn_geo_table_sort_detect_far_point(ctx, table, index, pat,
+ entries, pc, e, accessorp,
+ base_point,
+ &d_far, &diff_bit);
+ if (diff_bit > 0) {
+ n = grn_geo_table_sort_collect_points(ctx, table, index, pat,
+ entries, n, e, accessorp,
+ base_point, d_far, diff_bit);
+ }
+ need_not_indexed_records = offset + limit > n;
+ if (need_not_indexed_records) {
+ indexed_records = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY);
+ }
+ for (ep = entries + offset;
+ n_entries < limit && ep < entries + n;
+ n_entries++, ep++) {
+ grn_id *sorted_id;
+ if (!grn_array_add(ctx, (grn_array *)result, (void **)&sorted_id)) {
+ if (indexed_records) {
+ grn_hash_close(ctx, indexed_records);
+ indexed_records = NULL;
+ }
+ break;
+ }
+ *sorted_id = ep->id;
+ if (indexed_records) {
+ grn_hash_add(ctx, indexed_records, &(ep->id), sizeof(grn_id),
+ NULL, NULL);
+ }
+ }
+ GRN_FREE(entries);
+ if (indexed_records) {
+ GRN_TABLE_EACH(ctx, table, GRN_ID_NIL, GRN_ID_MAX, id, NULL, NULL, NULL, {
+ if (!grn_hash_get(ctx, indexed_records, &id, sizeof(grn_id), NULL)) {
+ grn_id *sorted_id;
+ if (grn_array_add(ctx, (grn_array *)result, (void **)&sorted_id)) {
+ *sorted_id = id;
+ }
+ n_entries++;
+ if (n_entries == limit) {
+ break;
+ }
+ };
+ });
+ grn_hash_close(ctx, indexed_records);
+ }
+ }
+
+ return n_entries;
+}
+
+int
+grn_geo_table_sort(grn_ctx *ctx, grn_obj *table, int offset, int limit,
+ grn_obj *result, grn_table_sort_key *keys, int n_keys)
+{
+ grn_obj *index;
+ int i = 0, e = offset + limit;
+ grn_bool accessorp = GRN_ACCESSORP(keys->key);
+ if (n_keys == 2 && (index = find_geo_sort_index(ctx, keys->key))) {
+ grn_id tid;
+ grn_obj *arg = keys[1].key;
+ grn_pat *pat = (grn_pat *)grn_ctx_at(ctx, index->header.domain);
+ grn_id domain = pat->obj.header.domain;
+ grn_pat_cursor *pc = grn_pat_cursor_open(ctx, pat, NULL, 0,
+ GRN_BULK_HEAD(arg), GRN_BULK_VSIZE(arg),
+ 0, -1, GRN_CURSOR_PREFIX);
+ if (pc) {
+ if (domain != GRN_DB_TOKYO_GEO_POINT && domain != GRN_DB_WGS84_GEO_POINT) {
+ while (i < e && (tid = grn_pat_cursor_next(ctx, pc))) {
+ grn_ii_cursor *ic = grn_ii_cursor_open(ctx, (grn_ii *)index, tid, 0, 0, 1, 0);
+ if (ic) {
+ grn_ii_posting *posting;
+ while (i < e && (posting = grn_ii_cursor_next(ctx, ic))) {
+ if (offset <= i) {
+ grn_id *v;
+ if (!grn_array_add(ctx, (grn_array *)result, (void **)&v)) { break; }
+ *v = posting->rid;
+ }
+ i++;
+ }
+ grn_ii_cursor_close(ctx, ic);
+ }
+ }
+ } else {
+ grn_geo_point *base_point = (grn_geo_point *)GRN_BULK_HEAD(arg);
+ i = grn_geo_table_sort_by_distance(ctx, table, index, pat,
+ pc, accessorp, base_point,
+ offset, limit, result);
+ }
+ grn_pat_cursor_close(ctx, pc);
+ }
+ }
+ return i;
+}
+
+grn_rc
+grn_geo_resolve_approximate_type(grn_ctx *ctx, grn_obj *type_name,
+ grn_geo_approximate_type *type)
+{
+ grn_rc rc;
+ grn_obj approximate_type;
+
+ GRN_TEXT_INIT(&approximate_type, 0);
+ rc = grn_obj_cast(ctx, type_name, &approximate_type, GRN_FALSE);
+ if (rc == GRN_SUCCESS) {
+ const char *name;
+ unsigned int size;
+ name = GRN_TEXT_VALUE(&approximate_type);
+ size = GRN_TEXT_LEN(&approximate_type);
+ if ((strncmp("rectangle", name, size) == 0) ||
+ (strncmp("rect", name, size) == 0)) {
+ *type = GRN_GEO_APPROXIMATE_RECTANGLE;
+ } else if ((strncmp("sphere", name, size) == 0) ||
+ (strncmp("sphr", name, size) == 0)) {
+ *type = GRN_GEO_APPROXIMATE_SPHERE;
+ } else if ((strncmp("ellipsoid", name, size) == 0) ||
+ (strncmp("ellip", name, size) == 0)) {
+ *type = GRN_GEO_APPROXIMATE_ELLIPSOID;
+ } else {
+ ERR(GRN_INVALID_ARGUMENT,
+ "geo distance approximate type must be one of "
+ "[rectangle, rect, sphere, sphr, ellipsoid, ellip]"
+ ": <%.*s>",
+ size, name);
+ }
+ }
+ GRN_OBJ_FIN(ctx, &approximate_type);
+
+ return rc;
+}
+
+typedef double (*grn_geo_distance_raw_func)(grn_ctx *ctx,
+ grn_geo_point *point1,
+ grn_geo_point *point2);
+
+grn_rc
+grn_selector_geo_in_circle(grn_ctx *ctx, grn_obj *table, grn_obj *index,
+ int nargs, grn_obj **args,
+ grn_obj *res, grn_operator op)
+{
+ grn_geo_approximate_type type = GRN_GEO_APPROXIMATE_RECTANGLE;
+
+ if (!(nargs == 4 || nargs == 5)) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "geo_in_circle(): requires 3 or 4 arguments but was <%d> arguments",
+ nargs - 1);
+ return ctx->rc;
+ }
+
+ if (!index) {
+ grn_obj *point_column;
+ char column_name[GRN_TABLE_MAX_KEY_SIZE];
+ int column_name_size;
+ point_column = args[1];
+ column_name_size = grn_obj_name(ctx, point_column,
+ column_name, GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "geo_in_circle(): index for <%.*s> is missing",
+ column_name_size, column_name);
+ return ctx->rc;
+ }
+
+ if (nargs == 5) {
+ if (grn_geo_resolve_approximate_type(ctx, args[4], &type) != GRN_SUCCESS) {
+ return ctx->rc;
+ }
+ }
+
+ {
+ grn_obj *center_point, *distance;
+ center_point = args[2];
+ distance = args[3];
+ grn_geo_select_in_circle(ctx, index, center_point, distance, type, res, op);
+ }
+
+ return ctx->rc;
+}
+
+static grn_geo_distance_raw_func
+grn_geo_resolve_distance_raw_func (grn_ctx *ctx,
+ grn_geo_approximate_type approximate_type,
+ grn_id domain)
+{
+ grn_geo_distance_raw_func distance_raw_func = NULL;
+
+ switch (approximate_type) {
+ case GRN_GEO_APPROXIMATE_RECTANGLE :
+ distance_raw_func = grn_geo_distance_rectangle_raw;
+ break;
+ case GRN_GEO_APPROXIMATE_SPHERE :
+ distance_raw_func = grn_geo_distance_sphere_raw;
+ break;
+ case GRN_GEO_APPROXIMATE_ELLIPSOID :
+ if (domain == GRN_DB_WGS84_GEO_POINT) {
+ distance_raw_func = grn_geo_distance_ellipsoid_raw_wgs84;
+ } else {
+ distance_raw_func = grn_geo_distance_ellipsoid_raw_tokyo;
+ }
+ break;
+ default :
+ break;
+ }
+
+ return distance_raw_func;
+}
+
+grn_rc
+grn_geo_select_in_circle(grn_ctx *ctx, grn_obj *index,
+ grn_obj *center_point, grn_obj *distance,
+ grn_geo_approximate_type approximate_type,
+ grn_obj *res, grn_operator op)
+{
+ grn_id domain;
+ double center_longitude, center_latitude;
+ double d;
+ grn_obj *pat, *point_on_circle = NULL, center_point_, point_on_circle_;
+ grn_geo_point *center, on_circle;
+ grn_geo_distance_raw_func distance_raw_func;
+ pat = grn_ctx_at(ctx, index->header.domain);
+ domain = pat->header.domain;
+ if (domain != GRN_DB_TOKYO_GEO_POINT && domain != GRN_DB_WGS84_GEO_POINT) {
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+ int name_size = 0;
+ grn_obj *domain_object;
+ domain_object = grn_ctx_at(ctx, domain);
+ if (domain_object) {
+ name_size = grn_obj_name(ctx, domain_object, name, GRN_TABLE_MAX_KEY_SIZE);
+ grn_obj_unlink(ctx, domain_object);
+ } else {
+ strcpy(name, "(null)");
+ name_size = strlen(name);
+ }
+ ERR(GRN_INVALID_ARGUMENT,
+ "geo_in_circle(): index table must be "
+ "TokyoGeoPoint or WGS84GeoPoint key type table: <%.*s>",
+ name_size, name);
+ goto exit;
+ }
+
+ if (center_point->header.domain != domain) {
+ GRN_OBJ_INIT(&center_point_, GRN_BULK, 0, domain);
+ if (grn_obj_cast(ctx, center_point, &center_point_, GRN_FALSE)) { goto exit; }
+ center_point = &center_point_;
+ }
+ center = GRN_GEO_POINT_VALUE_RAW(center_point);
+ center_longitude = GRN_GEO_INT2RAD(center->longitude);
+ center_latitude = GRN_GEO_INT2RAD(center->latitude);
+
+ distance_raw_func = grn_geo_resolve_distance_raw_func(ctx,
+ approximate_type,
+ domain);
+ if (!distance_raw_func) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "unknown approximate type: <%d>", approximate_type);
+ goto exit;
+ }
+
+ switch (distance->header.domain) {
+ case GRN_DB_INT32 :
+ d = GRN_INT32_VALUE(distance);
+ on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d / (double)GRN_GEO_RADIUS);
+ on_circle.longitude = center->longitude;
+ break;
+ case GRN_DB_UINT32 :
+ d = GRN_UINT32_VALUE(distance);
+ on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d / (double)GRN_GEO_RADIUS);
+ on_circle.longitude = center->longitude;
+ break;
+ case GRN_DB_INT64 :
+ d = GRN_INT64_VALUE(distance);
+ on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d / (double)GRN_GEO_RADIUS);
+ on_circle.longitude = center->longitude;
+ break;
+ case GRN_DB_UINT64 :
+ d = GRN_UINT64_VALUE(distance);
+ on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d / (double)GRN_GEO_RADIUS);
+ on_circle.longitude = center->longitude;
+ break;
+ case GRN_DB_FLOAT :
+ d = GRN_FLOAT_VALUE(distance);
+ on_circle.latitude = center->latitude + GRN_GEO_RAD2INT(d / (double)GRN_GEO_RADIUS);
+ on_circle.longitude = center->longitude;
+ break;
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ GRN_OBJ_INIT(&point_on_circle_, GRN_BULK, 0, domain);
+ if (grn_obj_cast(ctx, distance, &point_on_circle_, GRN_FALSE)) { goto exit; }
+ point_on_circle = &point_on_circle_;
+ /* fallthru */
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ if (!point_on_circle) {
+ if (domain != distance->header.domain) { /* todo */ goto exit; }
+ point_on_circle = distance;
+ }
+ GRN_GEO_POINT_VALUE(point_on_circle,
+ on_circle.latitude, on_circle.longitude);
+ d = distance_raw_func(ctx, center, &on_circle);
+ if (point_on_circle == &point_on_circle_) {
+ grn_obj_unlink(ctx, point_on_circle);
+ }
+ break;
+ default :
+ goto exit;
+ }
+ {
+ int n_meshes, diff_bit;
+ double d_far;
+ mesh_entry meshes[87];
+ uint8_t geo_key1[sizeof(grn_geo_point)];
+ uint8_t geo_key2[sizeof(grn_geo_point)];
+
+ d_far = grn_geo_distance_rectangle_raw(ctx, center, &on_circle);
+ grn_gton(geo_key1, center, sizeof(grn_geo_point));
+ grn_gton(geo_key2, &on_circle, sizeof(grn_geo_point));
+ diff_bit = compute_diff_bit(geo_key1, geo_key2);
+#ifdef GEO_DEBUG
+ printf("center point: ");
+ grn_p_geo_point(ctx, center);
+ printf("point on circle: ");
+ grn_p_geo_point(ctx, &on_circle);
+ printf("diff: %d\n", diff_bit);
+#endif
+ if ((diff_bit % 2) == 1) {
+ diff_bit--;
+ }
+ n_meshes = grn_geo_get_meshes_for_circle(ctx, center,
+ d_far, diff_bit, GRN_TRUE,
+ meshes);
+ while (n_meshes--) {
+ grn_table_cursor *tc;
+ tc = grn_table_cursor_open(ctx, pat,
+ &(meshes[n_meshes].key),
+ meshes[n_meshes].key_size,
+ NULL, 0,
+ 0, -1,
+ GRN_CURSOR_PREFIX|GRN_CURSOR_SIZE_BY_BIT);
+ inspect_mesh_entry(ctx, meshes, n_meshes);
+ if (tc) {
+ grn_id tid;
+ grn_geo_point point;
+ while ((tid = grn_table_cursor_next(ctx, tc))) {
+ double point_distance;
+ grn_table_get_key(ctx, pat, tid, &point, sizeof(grn_geo_point));
+ point_distance = distance_raw_func(ctx, &point, center);
+ if (point_distance <= d) {
+ inspect_tid(ctx, tid, &point, point_distance);
+ grn_ii_at(ctx, (grn_ii *)index, tid, (grn_hash *)res, op);
+ }
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ }
+ }
+exit :
+ grn_ii_resolve_sel_and(ctx, (grn_hash *)res, op);
+ return ctx->rc;
+}
+
+grn_rc
+grn_selector_geo_in_rectangle(grn_ctx *ctx, grn_obj *table, grn_obj *index,
+ int nargs, grn_obj **args,
+ grn_obj *res, grn_operator op)
+{
+ if (nargs == 4) {
+ grn_obj *top_left_point, *bottom_right_point;
+ top_left_point = args[2];
+ bottom_right_point = args[3];
+ grn_geo_select_in_rectangle(ctx, index,
+ top_left_point, bottom_right_point,
+ res, op);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT,
+ "geo_in_rectangle(): requires 3 arguments but was <%d> arguments",
+ nargs - 1);
+ }
+ return ctx->rc;
+}
+
+static void
+in_rectangle_data_fill(grn_ctx *ctx, grn_obj *index,
+ grn_obj *top_left_point,
+ grn_obj *bottom_right_point,
+ const char *process_name,
+ in_rectangle_data *data)
+{
+ grn_id domain;
+ const char *domain_name;
+
+ data->pat = grn_ctx_at(ctx, index->header.domain);
+ domain = data->pat->header.domain;
+ if (domain != GRN_DB_TOKYO_GEO_POINT && domain != GRN_DB_WGS84_GEO_POINT) {
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+ int name_size = 0;
+ grn_obj *domain_object;
+ domain_object = grn_ctx_at(ctx, domain);
+ if (domain_object) {
+ name_size = grn_obj_name(ctx, domain_object, name, GRN_TABLE_MAX_KEY_SIZE);
+ grn_obj_unlink(ctx, domain_object);
+ } else {
+ strcpy(name, "(null)");
+ name_size = strlen(name);
+ }
+ ERR(GRN_INVALID_ARGUMENT,
+ "%s: index table must be "
+ "TokyoGeoPoint or WGS84GeoPoint key type table: <%.*s>",
+ process_name, name_size, name);
+ return;
+ }
+
+ if (domain == GRN_DB_TOKYO_GEO_POINT) {
+ domain_name = "TokyoGeoPoint";
+ } else {
+ domain_name = "WGS84GeoPoint";
+ }
+
+ if (top_left_point->header.domain != domain) {
+ grn_obj_reinit(ctx, &(data->top_left_point_buffer), domain, GRN_BULK);
+ if (grn_obj_cast(ctx, top_left_point, &(data->top_left_point_buffer),
+ GRN_FALSE)) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "%s: failed to cast to %s: <%.*s>",
+ process_name, domain_name,
+ (int)GRN_TEXT_LEN(top_left_point),
+ GRN_TEXT_VALUE(top_left_point));
+ return;
+ }
+ top_left_point = &(data->top_left_point_buffer);
+ }
+ data->top_left = GRN_GEO_POINT_VALUE_RAW(top_left_point);
+
+ if (bottom_right_point->header.domain != domain) {
+ grn_obj_reinit(ctx, &(data->bottom_right_point_buffer), domain, GRN_BULK);
+ if (grn_obj_cast(ctx, bottom_right_point, &(data->bottom_right_point_buffer),
+ GRN_FALSE)) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "%s: failed to cast to %s: <%.*s>",
+ process_name, domain_name,
+ (int)GRN_TEXT_LEN(bottom_right_point),
+ GRN_TEXT_VALUE(bottom_right_point));
+ return;
+ }
+ bottom_right_point = &(data->bottom_right_point_buffer);
+ }
+ data->bottom_right = GRN_GEO_POINT_VALUE_RAW(bottom_right_point);
+}
+
+static void
+in_rectangle_data_validate(grn_ctx *ctx,
+ const char *process_name,
+ in_rectangle_data *data)
+{
+ grn_geo_point *top_left, *bottom_right;
+
+ top_left = data->top_left;
+ bottom_right = data->bottom_right;
+
+ if (top_left->latitude >= GRN_GEO_MAX_LATITUDE) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "%s: top left point's latitude is too big: "
+ "<%d>(max:%d): (%d,%d) (%d,%d)",
+ process_name,
+ GRN_GEO_MAX_LATITUDE, top_left->latitude,
+ top_left->latitude, top_left->longitude,
+ bottom_right->latitude, bottom_right->longitude);
+ return;
+ }
+
+ if (top_left->latitude <= GRN_GEO_MIN_LATITUDE) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "%s: top left point's latitude is too small: "
+ "<%d>(min:%d): (%d,%d) (%d,%d)",
+ process_name,
+ GRN_GEO_MIN_LATITUDE, top_left->latitude,
+ top_left->latitude, top_left->longitude,
+ bottom_right->latitude, bottom_right->longitude);
+ return;
+ }
+
+ if (top_left->longitude >= GRN_GEO_MAX_LONGITUDE) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "%s: top left point's longitude is too big: "
+ "<%d>(max:%d): (%d,%d) (%d,%d)",
+ process_name,
+ GRN_GEO_MAX_LONGITUDE, top_left->longitude,
+ top_left->latitude, top_left->longitude,
+ bottom_right->latitude, bottom_right->longitude);
+ return;
+ }
+
+ if (top_left->longitude <= GRN_GEO_MIN_LONGITUDE) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "%s: top left point's longitude is too small: "
+ "<%d>(min:%d): (%d,%d) (%d,%d)",
+ process_name,
+ GRN_GEO_MIN_LONGITUDE, top_left->longitude,
+ top_left->latitude, top_left->longitude,
+ bottom_right->latitude, bottom_right->longitude);
+ return;
+ }
+
+ if (bottom_right->latitude >= GRN_GEO_MAX_LATITUDE) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "%s: bottom right point's latitude is too big: "
+ "<%d>(max:%d): (%d,%d) (%d,%d)",
+ process_name,
+ GRN_GEO_MAX_LATITUDE, bottom_right->latitude,
+ top_left->latitude, top_left->longitude,
+ bottom_right->latitude, bottom_right->longitude);
+ return;
+ }
+
+ if (bottom_right->latitude <= GRN_GEO_MIN_LATITUDE) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "%s: bottom right point's latitude is too small: "
+ "<%d>(min:%d): (%d,%d) (%d,%d)",
+ process_name,
+ GRN_GEO_MIN_LATITUDE, bottom_right->latitude,
+ top_left->latitude, top_left->longitude,
+ bottom_right->latitude, bottom_right->longitude);
+ return;
+ }
+
+ if (bottom_right->longitude >= GRN_GEO_MAX_LONGITUDE) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "%s: bottom right point's longitude is too big: "
+ "<%d>(max:%d): (%d,%d) (%d,%d)",
+ process_name,
+ GRN_GEO_MAX_LONGITUDE, bottom_right->longitude,
+ top_left->latitude, top_left->longitude,
+ bottom_right->latitude, bottom_right->longitude);
+ return;
+ }
+
+ if (bottom_right->longitude <= GRN_GEO_MIN_LONGITUDE) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "%s: bottom right point's longitude is too small: "
+ "<%d>(min:%d): (%d,%d) (%d,%d)",
+ process_name,
+ GRN_GEO_MIN_LONGITUDE, bottom_right->longitude,
+ top_left->latitude, top_left->longitude,
+ bottom_right->latitude, bottom_right->longitude);
+ return;
+ }
+}
+
+static void
+in_rectangle_area_data_compute(grn_ctx *ctx,
+ grn_geo_point *top_left,
+ grn_geo_point *bottom_right,
+ in_rectangle_area_data *data)
+{
+ int latitude_distance, longitude_distance;
+ int diff_bit;
+ grn_geo_point base;
+ grn_geo_point *geo_point_input;
+ uint8_t geo_key_input[sizeof(grn_geo_point)];
+ uint8_t geo_key_base[sizeof(grn_geo_point)];
+ uint8_t geo_key_top_left[sizeof(grn_geo_point)];
+ uint8_t geo_key_bottom_right[sizeof(grn_geo_point)];
+
+ latitude_distance = top_left->latitude - bottom_right->latitude;
+ longitude_distance = bottom_right->longitude - top_left->longitude;
+ if (latitude_distance > longitude_distance) {
+ geo_point_input = bottom_right;
+ base.latitude = bottom_right->latitude;
+ base.longitude = bottom_right->longitude - longitude_distance;
+ } else {
+ geo_point_input = top_left;
+ base.latitude = top_left->latitude - latitude_distance;
+ base.longitude = top_left->longitude;
+ }
+ grn_gton(geo_key_input, geo_point_input, sizeof(grn_geo_point));
+ grn_gton(geo_key_base, &base, sizeof(grn_geo_point));
+ diff_bit = compute_diff_bit(geo_key_input, geo_key_base);
+ compute_min_and_max(&base, diff_bit, &(data->min), &(data->max));
+
+ grn_gton(geo_key_top_left, top_left, sizeof(grn_geo_point));
+ grn_gton(geo_key_bottom_right, bottom_right, sizeof(grn_geo_point));
+ data->rectangle_common_bit =
+ compute_diff_bit(geo_key_top_left, geo_key_bottom_right) - 1;
+ compute_min_and_max_key(geo_key_top_left, data->rectangle_common_bit + 1,
+ data->rectangle_common_key, NULL);
+
+#ifdef GEO_DEBUG
+ printf("base: ");
+ grn_p_geo_point(ctx, &base);
+ printf("min: ");
+ grn_p_geo_point(ctx, &(data->min));
+ printf("max: ");
+ grn_p_geo_point(ctx, &(data->max));
+ printf("top-left: ");
+ grn_p_geo_point(ctx, top_left);
+ printf("bottom-right: ");
+ grn_p_geo_point(ctx, bottom_right);
+ printf("rectangle-common-bit:%10d\n", data->rectangle_common_bit);
+ printf("distance(latitude): %10d\n", latitude_distance);
+ printf("distance(longitude): %10d\n", longitude_distance);
+#endif
+}
+
+static grn_rc
+in_rectangle_data_prepare(grn_ctx *ctx, grn_obj *index,
+ grn_obj *top_left_point,
+ grn_obj *bottom_right_point,
+ const char *process_name,
+ in_rectangle_data *data)
+{
+ if (!index) {
+ ERR(GRN_INVALID_ARGUMENT, "%s: index column is missing", process_name);
+ goto exit;
+ }
+
+ in_rectangle_data_fill(ctx, index, top_left_point, bottom_right_point,
+ process_name, data);
+ if (ctx->rc != GRN_SUCCESS) {
+ goto exit;
+ }
+
+ in_rectangle_data_validate(ctx, process_name, data);
+ if (ctx->rc != GRN_SUCCESS) {
+ goto exit;
+ }
+
+exit :
+ return ctx->rc;
+}
+
+#define SAME_BIT_P(a, b, n_bit)\
+ ((((uint8_t *)(a))[(n_bit) / 8] & (1 << (7 - ((n_bit) % 8)))) ==\
+ (((uint8_t *)(b))[(n_bit) / 8] & (1 << (7 - ((n_bit) % 8)))))
+
+#define CURSOR_ENTRY_UPDATE_STATUS(entry, name, other_key) do {\
+ if (SAME_BIT_P((entry)->key, (other_key), (entry)->target_bit)) {\
+ (entry)->status_flags |= GRN_GEO_CURSOR_ENTRY_STATUS_ ## name;\
+ } else {\
+ (entry)->status_flags &= ~GRN_GEO_CURSOR_ENTRY_STATUS_ ## name;\
+ }\
+} while (0)
+
+#define CURSOR_ENTRY_CHECK_STATUS(entry, name)\
+ ((entry)->status_flags & GRN_GEO_CURSOR_ENTRY_STATUS_ ## name)
+#define CURSOR_ENTRY_IS_INNER(entry)\
+ (((entry)->status_flags &\
+ (GRN_GEO_CURSOR_ENTRY_STATUS_LATITUDE_INNER |\
+ GRN_GEO_CURSOR_ENTRY_STATUS_LONGITUDE_INNER)) ==\
+ (GRN_GEO_CURSOR_ENTRY_STATUS_LATITUDE_INNER |\
+ GRN_GEO_CURSOR_ENTRY_STATUS_LONGITUDE_INNER))
+#define CURSOR_ENTRY_INCLUDED_IN_LATITUDE_DIRECTION(entry)\
+ ((entry)->status_flags &\
+ (GRN_GEO_CURSOR_ENTRY_STATUS_LATITUDE_INNER |\
+ GRN_GEO_CURSOR_ENTRY_STATUS_TOP_INCLUDED |\
+ GRN_GEO_CURSOR_ENTRY_STATUS_BOTTOM_INCLUDED))
+#define CURSOR_ENTRY_INCLUDED_IN_LONGITUDE_DIRECTION(entry)\
+ ((entry)->status_flags &\
+ (GRN_GEO_CURSOR_ENTRY_STATUS_LONGITUDE_INNER |\
+ GRN_GEO_CURSOR_ENTRY_STATUS_LEFT_INCLUDED |\
+ GRN_GEO_CURSOR_ENTRY_STATUS_RIGHT_INCLUDED))
+
+#define SET_N_BIT(a, n_bit)\
+ (((uint8_t *)(a))[((n_bit) / 8)] ^= (1 << (7 - ((n_bit) % 8))))
+
+#define N_BIT(a, n_bit)\
+ ((((uint8_t *)(a))[((n_bit) / 8)] &\
+ (1 << (7 - ((n_bit) % 8)))) >> (1 << (7 - ((n_bit) % 8))))
+
+static grn_bool
+extract_rectangle_in_area(grn_ctx *ctx,
+ grn_geo_area_type area_type,
+ const grn_geo_point *top_left,
+ const grn_geo_point *bottom_right,
+ grn_geo_point *area_top_left,
+ grn_geo_point *area_bottom_right)
+{
+ grn_bool out_of_area = GRN_FALSE;
+ grn_bool cover_all_areas = GRN_FALSE;
+
+ if ((GRN_GEO_POINT_IN_NORTH_WEST(top_left) &&
+ GRN_GEO_POINT_IN_SOUTH_EAST(bottom_right)) ||
+ (GRN_GEO_POINT_IN_NORTH_EAST(top_left) &&
+ GRN_GEO_POINT_IN_SOUTH_WEST(bottom_right))) {
+ cover_all_areas = GRN_TRUE;
+ }
+
+ switch (area_type) {
+ case GRN_GEO_AREA_NORTH_EAST :
+ if (cover_all_areas ||
+ GRN_GEO_POINT_IN_NORTH_EAST(top_left) ||
+ GRN_GEO_POINT_IN_NORTH_EAST(bottom_right)) {
+ area_top_left->latitude = MAX(top_left->latitude, 0);
+ area_bottom_right->latitude = MAX(bottom_right->latitude, 0);
+ if (GRN_GEO_LONGITUDE_IS_WRAPPED(top_left, bottom_right)) {
+ area_top_left->longitude = top_left->longitude;
+ area_bottom_right->longitude = GRN_GEO_MAX_LONGITUDE;
+ } else {
+ area_top_left->longitude = MAX(top_left->longitude, 0);
+ area_bottom_right->longitude = MAX(bottom_right->longitude, 0);
+ }
+ } else {
+ out_of_area = GRN_TRUE;
+ }
+ break;
+ case GRN_GEO_AREA_NORTH_WEST :
+ if (cover_all_areas ||
+ GRN_GEO_POINT_IN_NORTH_WEST(top_left) ||
+ GRN_GEO_POINT_IN_NORTH_WEST(bottom_right)) {
+ area_top_left->latitude = MAX(top_left->latitude, 0);
+ area_bottom_right->latitude = MAX(bottom_right->latitude, 0);
+ if (GRN_GEO_LONGITUDE_IS_WRAPPED(top_left, bottom_right)) {
+ area_top_left->longitude = GRN_GEO_MIN_LONGITUDE;
+ area_bottom_right->longitude = bottom_right->longitude;
+ } else {
+ area_top_left->longitude = MIN(top_left->longitude, -1);
+ area_bottom_right->longitude = MIN(bottom_right->longitude, -1);
+ }
+ } else {
+ out_of_area = GRN_TRUE;
+ }
+ break;
+ case GRN_GEO_AREA_SOUTH_WEST :
+ if (cover_all_areas ||
+ GRN_GEO_POINT_IN_SOUTH_WEST(top_left) ||
+ GRN_GEO_POINT_IN_SOUTH_WEST(bottom_right)) {
+ area_top_left->latitude = MIN(top_left->latitude, -1);
+ area_bottom_right->latitude = MIN(bottom_right->latitude, -1);
+ if (GRN_GEO_LONGITUDE_IS_WRAPPED(top_left, bottom_right)) {
+ area_top_left->longitude = GRN_GEO_MIN_LONGITUDE;
+ area_bottom_right->longitude = bottom_right->longitude;
+ } else {
+ area_top_left->longitude = MIN(top_left->longitude, -1);
+ area_bottom_right->longitude = MIN(bottom_right->longitude, -1);
+ }
+ } else {
+ out_of_area = GRN_TRUE;
+ }
+ break;
+ case GRN_GEO_AREA_SOUTH_EAST :
+ if (cover_all_areas ||
+ GRN_GEO_POINT_IN_SOUTH_EAST(top_left) ||
+ GRN_GEO_POINT_IN_SOUTH_EAST(bottom_right)) {
+ area_top_left->latitude = MIN(top_left->latitude, -1);
+ area_bottom_right->latitude = MIN(bottom_right->latitude, -1);
+ if (GRN_GEO_LONGITUDE_IS_WRAPPED(top_left, bottom_right)) {
+ area_top_left->longitude = top_left->longitude;
+ area_bottom_right->longitude = GRN_GEO_MAX_LONGITUDE;
+ } else {
+ area_top_left->longitude = MAX(top_left->longitude, 0);
+ area_bottom_right->longitude = MAX(bottom_right->longitude, 0);
+ }
+ } else {
+ out_of_area = GRN_TRUE;
+ }
+ break;
+ default :
+ out_of_area = GRN_TRUE;
+ break;
+ }
+
+ return out_of_area;
+}
+
+static void
+grn_geo_cursor_area_init(grn_ctx *ctx,
+ grn_geo_cursor_area *area,
+ grn_geo_area_type area_type,
+ const grn_geo_point *top_left,
+ const grn_geo_point *bottom_right)
+{
+ grn_geo_point area_top_left, area_bottom_right;
+ in_rectangle_area_data data;
+ grn_geo_cursor_entry *entry;
+ grn_bool out_of_area;
+
+ out_of_area = extract_rectangle_in_area(ctx,
+ area_type,
+ top_left,
+ bottom_right,
+ &area_top_left,
+ &area_bottom_right);
+ if (out_of_area) {
+ area->current_entry = -1;
+ return;
+ }
+
+ area->current_entry = 0;
+ memcpy(&(area->top_left), &area_top_left, sizeof(grn_geo_point));
+ memcpy(&(area->bottom_right), &area_bottom_right, sizeof(grn_geo_point));
+ grn_gton(area->top_left_key, &area_top_left, sizeof(grn_geo_point));
+ grn_gton(area->bottom_right_key, &area_bottom_right, sizeof(grn_geo_point));
+
+ entry = &(area->entries[area->current_entry]);
+ in_rectangle_area_data_compute(ctx,
+ &area_top_left,
+ &area_bottom_right,
+ &data);
+ entry->target_bit = data.rectangle_common_bit;
+ memcpy(entry->key, data.rectangle_common_key, sizeof(grn_geo_point));
+ entry->status_flags =
+ GRN_GEO_CURSOR_ENTRY_STATUS_TOP_INCLUDED |
+ GRN_GEO_CURSOR_ENTRY_STATUS_BOTTOM_INCLUDED |
+ GRN_GEO_CURSOR_ENTRY_STATUS_LEFT_INCLUDED |
+ GRN_GEO_CURSOR_ENTRY_STATUS_RIGHT_INCLUDED;
+ if (data.min.latitude == area_bottom_right.latitude &&
+ data.max.latitude == area_top_left.latitude) {
+ entry->status_flags |= GRN_GEO_CURSOR_ENTRY_STATUS_LATITUDE_INNER;
+ }
+ if (data.min.longitude == area_top_left.longitude &&
+ data.max.longitude == area_bottom_right.longitude) {
+ entry->status_flags |= GRN_GEO_CURSOR_ENTRY_STATUS_LONGITUDE_INNER;
+ }
+}
+
+grn_obj *
+grn_geo_cursor_open_in_rectangle(grn_ctx *ctx,
+ grn_obj *index,
+ grn_obj *top_left_point,
+ grn_obj *bottom_right_point,
+ int offset,
+ int limit)
+{
+ grn_geo_cursor_in_rectangle *cursor = NULL;
+ in_rectangle_data data;
+
+ GRN_API_ENTER;
+ GRN_VOID_INIT(&(data.top_left_point_buffer));
+ GRN_VOID_INIT(&(data.bottom_right_point_buffer));
+ if (in_rectangle_data_prepare(ctx, index, top_left_point, bottom_right_point,
+ "geo_in_rectangle()", &data)) {
+ goto exit;
+ }
+
+ cursor = GRN_MALLOCN(grn_geo_cursor_in_rectangle, 1);
+ if (!cursor) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[geo][cursor][in-rectangle] failed to allocate memory for geo cursor");
+ goto exit;
+ }
+
+ cursor->pat = data.pat;
+ cursor->index = index;
+ memcpy(&(cursor->top_left), data.top_left, sizeof(grn_geo_point));
+ memcpy(&(cursor->bottom_right), data.bottom_right, sizeof(grn_geo_point));
+ cursor->pat_cursor = NULL;
+ cursor->ii_cursor = NULL;
+ cursor->offset = offset;
+ cursor->rest = limit;
+
+ cursor->current_area = GRN_GEO_AREA_NORTH_EAST;
+ {
+ grn_geo_area_type area_type;
+ const grn_geo_point *top_left = &(cursor->top_left);
+ const grn_geo_point *bottom_right = &(cursor->bottom_right);
+ for (area_type = GRN_GEO_AREA_NORTH_EAST;
+ area_type < GRN_GEO_AREA_LAST;
+ area_type++) {
+ grn_geo_cursor_area_init(ctx, &(cursor->areas[area_type]),
+ area_type, top_left, bottom_right);
+ }
+ }
+ {
+ const char *minimum_reduce_bit_env;
+ cursor->minimum_reduce_bit = 0;
+ minimum_reduce_bit_env = getenv("GRN_GEO_IN_RECTANGLE_MINIMUM_REDUCE_BIT");
+ if (minimum_reduce_bit_env) {
+ cursor->minimum_reduce_bit = atoi(minimum_reduce_bit_env);
+ }
+ if (cursor->minimum_reduce_bit < 1) {
+ cursor->minimum_reduce_bit = 1;
+ }
+ }
+ GRN_DB_OBJ_SET_TYPE(cursor, GRN_CURSOR_COLUMN_GEO_INDEX);
+ {
+ grn_obj *db;
+ grn_id id;
+ db = grn_ctx_db(ctx);
+ id = grn_obj_register(ctx, db, NULL, 0);
+ DB_OBJ(cursor)->header.domain = GRN_ID_NIL;
+ DB_OBJ(cursor)->range = GRN_ID_NIL;
+ grn_db_obj_init(ctx, db, id, DB_OBJ(cursor));
+ }
+
+exit :
+ grn_obj_unlink(ctx, &(data.top_left_point_buffer));
+ grn_obj_unlink(ctx, &(data.bottom_right_point_buffer));
+ GRN_API_RETURN((grn_obj *)cursor);
+}
+
+static inline grn_bool
+grn_geo_cursor_entry_next_push(grn_ctx *ctx,
+ grn_geo_cursor_in_rectangle *cursor,
+ grn_geo_cursor_entry *entry)
+{
+ grn_geo_cursor_entry *next_entry;
+ grn_geo_point entry_base;
+ grn_table_cursor *pat_cursor;
+ grn_bool pushed = GRN_FALSE;
+
+ grn_ntog((uint8_t*)(&entry_base), entry->key, sizeof(grn_geo_point));
+ pat_cursor = grn_table_cursor_open(ctx,
+ cursor->pat,
+ &entry_base,
+ entry->target_bit + 1,
+ NULL, 0,
+ 0, -1,
+ GRN_CURSOR_PREFIX|GRN_CURSOR_SIZE_BY_BIT);
+ if (pat_cursor) {
+ if (grn_table_cursor_next(ctx, pat_cursor)) {
+ grn_geo_cursor_area *area;
+ area = &(cursor->areas[cursor->current_area]);
+ next_entry = &(area->entries[++area->current_entry]);
+ memcpy(next_entry, entry, sizeof(grn_geo_cursor_entry));
+ pushed = GRN_TRUE;
+ }
+ grn_table_cursor_close(ctx, pat_cursor);
+ }
+
+ return pushed;
+}
+
+static inline grn_bool
+grn_geo_cursor_entry_next(grn_ctx *ctx,
+ grn_geo_cursor_in_rectangle *cursor,
+ grn_geo_cursor_entry *entry)
+{
+ uint8_t *top_left_key;
+ uint8_t *bottom_right_key;
+ int max_target_bit = GRN_GEO_KEY_MAX_BITS - cursor->minimum_reduce_bit;
+ grn_geo_cursor_area *area = NULL;
+
+ while (cursor->current_area < GRN_GEO_AREA_LAST) {
+ area = &(cursor->areas[cursor->current_area]);
+ if (area->current_entry >= 0) {
+ break;
+ }
+ cursor->current_area++;
+ area = NULL;
+ }
+
+ if (!area) {
+ return GRN_FALSE;
+ }
+
+ top_left_key = area->top_left_key;
+ bottom_right_key = area->bottom_right_key;
+ memcpy(entry,
+ &(area->entries[area->current_entry--]),
+ sizeof(grn_geo_cursor_entry));
+ while (GRN_TRUE) {
+ grn_geo_cursor_entry next_entry0, next_entry1;
+ grn_bool pushed = GRN_FALSE;
+
+ /*
+ top_left_key: tl
+ bottom_right_key: br
+
+ e.g.: top_left_key is at the top left sub mesh and
+ bottom_right_key is at the bottom right sub mesh.
+ top_left_key is also at the top left - bottom right
+ sub-sub mesh and
+ bottom_right_key is at the bottom right - bottom left
+ sub-sub mesh.
+
+ ^latitude +----+----+----+----+
+ | 1 |1010|1011|1110|1111|
+ | | | | | |
+ | 1 +----+----+----+----+
+ \/ 0 |1000|1001|1100|1101|
+ | | tl | | |
+ +----+----+----+----+
+ 1 |0010|0011|0110|0111|
+ | | | | |
+ 0 +----+----+----+----+
+ 0 |0000|0001|0100|0101|
+ | | | br | |
+ +----+----+----+----+
+ 0 1 0 1
+ |-------| |-------|
+ 0 1
+ <------>
+ longitude
+
+ entry.target_bit + 1 -> next_entry0
+ entry.target_bit + 1 and entry.key ^ (entry.target_bit + 1) in bit
+ -> next_entry1
+
+ entry: represents the biggest mesh.
+ (1010, 1011, 1110, 1111,
+ 1000, 1001, 1100, 1101,
+ 0010, 0011, 0110, 0111,
+ 0000, 0001, 0100, 0101)
+ next_entry0: represents bottom sub-mesh.
+ (0010, 0011, 0110, 0111,
+ 0000, 0001, 0100, 0101)
+ next_entry1: represents top sub-mesh.
+ (1010, 1011, 1110, 1111,
+ 1000, 1001, 1100, 1101)
+
+ entry->status_flags = TOP_INCLUDED |
+ BOTTOM_INCLUDED |
+ LEFT_INCLUDED |
+ RIGHT_INCLUDED
+ next_entry0->status_flags = BOTTOM_INCLUDED |
+ LEFT_INCLUDED |
+ RIGHT_INCLUDED
+ next_entry1->status_flags = TOP_INCLUDED |
+ LEFT_INCLUDED |
+ RIGHT_INCLUDED
+
+ Both next_entry1 and next_entry0 are pushed to the stack in cursor.
+ */
+
+#ifdef GEO_DEBUG
+ inspect_cursor_entry(ctx, entry);
+#endif
+
+ if (entry->target_bit >= max_target_bit) {
+#ifdef GEO_DEBUG
+ printf("%d: force stopping to reduce a mesh\n", entry->target_bit);
+#endif
+ break;
+ }
+
+ if (CURSOR_ENTRY_IS_INNER(entry)) {
+#ifdef GEO_DEBUG
+ printf("%d: inner entries\n", entry->target_bit);
+#endif
+ break;
+ }
+
+ memcpy(&next_entry0, entry, sizeof(grn_geo_cursor_entry));
+ next_entry0.target_bit++;
+ memcpy(&next_entry1, entry, sizeof(grn_geo_cursor_entry));
+ next_entry1.target_bit++;
+ SET_N_BIT(next_entry1.key, next_entry1.target_bit);
+
+#ifdef GEO_DEBUG
+ inspect_cursor_entry_targets(ctx, entry, top_left_key, bottom_right_key,
+ &next_entry0, &next_entry1);
+#endif
+
+ if ((entry->target_bit + 1) % 2 == 0) {
+ if (CURSOR_ENTRY_CHECK_STATUS(entry, TOP_INCLUDED)) {
+ CURSOR_ENTRY_UPDATE_STATUS(&next_entry0, TOP_INCLUDED, top_left_key);
+ CURSOR_ENTRY_UPDATE_STATUS(&next_entry1, TOP_INCLUDED, top_left_key);
+ }
+ if (CURSOR_ENTRY_CHECK_STATUS(entry, BOTTOM_INCLUDED)) {
+ CURSOR_ENTRY_UPDATE_STATUS(&next_entry0, BOTTOM_INCLUDED,
+ bottom_right_key);
+ CURSOR_ENTRY_UPDATE_STATUS(&next_entry1, BOTTOM_INCLUDED,
+ bottom_right_key);
+ }
+
+ if (CURSOR_ENTRY_CHECK_STATUS(entry, TOP_INCLUDED) &&
+ !CURSOR_ENTRY_CHECK_STATUS(entry, BOTTOM_INCLUDED) &&
+ CURSOR_ENTRY_CHECK_STATUS(&next_entry1, TOP_INCLUDED)) {
+ next_entry0.status_flags |= GRN_GEO_CURSOR_ENTRY_STATUS_LATITUDE_INNER;
+ } else if (!CURSOR_ENTRY_CHECK_STATUS(entry, TOP_INCLUDED) &&
+ CURSOR_ENTRY_CHECK_STATUS(entry, BOTTOM_INCLUDED) &&
+ CURSOR_ENTRY_CHECK_STATUS(&next_entry0, BOTTOM_INCLUDED)) {
+ next_entry1.status_flags |= GRN_GEO_CURSOR_ENTRY_STATUS_LATITUDE_INNER;
+ }
+
+ if (CURSOR_ENTRY_INCLUDED_IN_LATITUDE_DIRECTION(&next_entry1)) {
+ if (grn_geo_cursor_entry_next_push(ctx, cursor, &next_entry1)) {
+ pushed = GRN_TRUE;
+#ifdef GEO_DEBUG
+ printf("%d: latitude: push 1\n", next_entry1.target_bit);
+#endif
+ }
+ }
+ if (CURSOR_ENTRY_INCLUDED_IN_LATITUDE_DIRECTION(&next_entry0)) {
+ if (grn_geo_cursor_entry_next_push(ctx, cursor, &next_entry0)) {
+ pushed = GRN_TRUE;
+#ifdef GEO_DEBUG
+ printf("%d: latitude: push 0\n", next_entry0.target_bit);
+#endif
+ }
+ }
+ } else {
+ if (CURSOR_ENTRY_CHECK_STATUS(entry, RIGHT_INCLUDED)) {
+ CURSOR_ENTRY_UPDATE_STATUS(&next_entry0, RIGHT_INCLUDED,
+ bottom_right_key);
+ CURSOR_ENTRY_UPDATE_STATUS(&next_entry1, RIGHT_INCLUDED,
+ bottom_right_key);
+ }
+ if (CURSOR_ENTRY_CHECK_STATUS(entry, LEFT_INCLUDED)) {
+ CURSOR_ENTRY_UPDATE_STATUS(&next_entry0, LEFT_INCLUDED, top_left_key);
+ CURSOR_ENTRY_UPDATE_STATUS(&next_entry1, LEFT_INCLUDED, top_left_key);
+ }
+
+ if (CURSOR_ENTRY_CHECK_STATUS(entry, LEFT_INCLUDED) &&
+ !CURSOR_ENTRY_CHECK_STATUS(entry, RIGHT_INCLUDED) &&
+ CURSOR_ENTRY_CHECK_STATUS(&next_entry0, LEFT_INCLUDED)) {
+ next_entry1.status_flags |= GRN_GEO_CURSOR_ENTRY_STATUS_LONGITUDE_INNER;
+ } else if (!CURSOR_ENTRY_CHECK_STATUS(entry, LEFT_INCLUDED) &&
+ CURSOR_ENTRY_CHECK_STATUS(entry, RIGHT_INCLUDED) &&
+ CURSOR_ENTRY_CHECK_STATUS(&next_entry1, RIGHT_INCLUDED)) {
+ next_entry0.status_flags |= GRN_GEO_CURSOR_ENTRY_STATUS_LONGITUDE_INNER;
+ }
+
+ if (CURSOR_ENTRY_INCLUDED_IN_LONGITUDE_DIRECTION(&next_entry1)) {
+ if (grn_geo_cursor_entry_next_push(ctx, cursor, &next_entry1)) {
+ pushed = GRN_TRUE;
+#ifdef GEO_DEBUG
+ printf("%d: longitude: push 1\n", next_entry1.target_bit);
+#endif
+ }
+ }
+ if (CURSOR_ENTRY_INCLUDED_IN_LONGITUDE_DIRECTION(&next_entry0)) {
+ if (grn_geo_cursor_entry_next_push(ctx, cursor, &next_entry0)) {
+ pushed = GRN_TRUE;
+#ifdef GEO_DEBUG
+ printf("%d: longitude: push 0\n", next_entry0.target_bit);
+#endif
+ }
+ }
+ }
+
+ if (pushed) {
+#ifdef GEO_DEBUG
+ int i;
+
+ printf("%d: pushed\n", entry->target_bit);
+ printf("stack:\n");
+ for (i = area->current_entry; i >= 0; i--) {
+ grn_geo_cursor_entry *stack_entry;
+ stack_entry = &(area->entries[i]);
+ printf("%2d: ", i);
+ inspect_key(ctx, stack_entry->key);
+ printf(" ");
+ print_key_mark(ctx, stack_entry->target_bit);
+ }
+#endif
+ memcpy(entry,
+ &(area->entries[area->current_entry--]),
+ sizeof(grn_geo_cursor_entry));
+#ifdef GEO_DEBUG
+ printf("%d: pop entry\n", entry->target_bit);
+#endif
+ } else {
+ break;
+ }
+ }
+
+#ifdef GEO_DEBUG
+ printf("found:\n");
+ inspect_cursor_entry(ctx, entry);
+#endif
+
+ return GRN_TRUE;
+}
+
+typedef grn_bool (*grn_geo_cursor_callback)(grn_ctx *ctx, grn_ii_posting *posting, void *user_data);
+
+static void
+grn_geo_cursor_each(grn_ctx *ctx, grn_obj *geo_cursor,
+ grn_geo_cursor_callback callback, void *user_data)
+{
+ grn_geo_cursor_in_rectangle *cursor;
+ grn_obj *pat;
+ grn_table_cursor *pat_cursor;
+ grn_ii *ii;
+ grn_ii_cursor *ii_cursor;
+ grn_ii_posting *posting = NULL;
+ grn_geo_point *current, *top_left, *bottom_right;
+ grn_id index_id;
+
+ cursor = (grn_geo_cursor_in_rectangle *)geo_cursor;
+ if (cursor->rest == 0) {
+ return;
+ }
+
+ pat = cursor->pat;
+ pat_cursor = cursor->pat_cursor;
+ ii = (grn_ii *)(cursor->index);
+ ii_cursor = cursor->ii_cursor;
+ current = &(cursor->current);
+ top_left = &(cursor->top_left);
+ bottom_right = &(cursor->bottom_right);
+
+ while (GRN_TRUE) {
+ if (!pat_cursor) {
+ grn_geo_cursor_entry entry;
+ grn_geo_point entry_base;
+ if (!grn_geo_cursor_entry_next(ctx, cursor, &entry)) {
+ cursor->rest = 0;
+ return;
+ }
+ grn_ntog((uint8_t*)(&entry_base), entry.key, sizeof(grn_geo_point));
+ if (!(cursor->pat_cursor = pat_cursor =
+ grn_table_cursor_open(ctx,
+ pat,
+ &entry_base,
+ entry.target_bit + 1,
+ NULL, 0,
+ 0, -1,
+ GRN_CURSOR_PREFIX|GRN_CURSOR_SIZE_BY_BIT))) {
+ cursor->rest = 0;
+ return;
+ }
+#ifdef GEO_DEBUG
+ inspect_mesh(ctx, &entry_base, entry.target_bit, 0);
+#endif
+ }
+
+ while (ii_cursor || (index_id = grn_table_cursor_next(ctx, pat_cursor))) {
+ if (!ii_cursor) {
+ grn_table_get_key(ctx, pat, index_id, current, sizeof(grn_geo_point));
+ if (grn_geo_in_rectangle_raw(ctx, current, top_left, bottom_right)) {
+ inspect_tid(ctx, index_id, current, 0);
+ if (!(cursor->ii_cursor = ii_cursor =
+ grn_ii_cursor_open(ctx,
+ ii,
+ index_id,
+ GRN_ID_NIL,
+ GRN_ID_MAX,
+ ii->n_elements,
+ 0))) {
+ continue;
+ }
+ } else {
+ continue;
+ }
+ }
+
+ while ((posting = grn_ii_cursor_next(ctx, ii_cursor))) {
+ if (cursor->offset == 0) {
+ grn_bool keep_each;
+ keep_each = callback(ctx, posting, user_data);
+ if (cursor->rest > 0) {
+ if (--(cursor->rest) == 0) {
+ keep_each = GRN_FALSE;
+ }
+ }
+ if (!keep_each) {
+ return;
+ }
+ } else {
+ cursor->offset--;
+ }
+ }
+ grn_ii_cursor_close(ctx, ii_cursor);
+ cursor->ii_cursor = ii_cursor = NULL;
+ }
+ grn_table_cursor_close(ctx, pat_cursor);
+ cursor->pat_cursor = pat_cursor = NULL;
+ }
+}
+
+static grn_bool
+grn_geo_cursor_next_callback(grn_ctx *ctx, grn_ii_posting *posting,
+ void *user_data)
+{
+ grn_ii_posting **return_posting = user_data;
+ *return_posting = posting;
+ return GRN_FALSE;
+}
+
+grn_posting *
+grn_geo_cursor_next(grn_ctx *ctx, grn_obj *geo_cursor)
+{
+ grn_ii_posting *posting = NULL;
+ grn_geo_cursor_each(ctx, geo_cursor, grn_geo_cursor_next_callback, &posting);
+ return (grn_posting *)posting;
+}
+
+grn_rc
+grn_geo_cursor_close(grn_ctx *ctx, grn_obj *geo_cursor)
+{
+ grn_geo_cursor_in_rectangle *cursor;
+
+ if (!geo_cursor) { return GRN_INVALID_ARGUMENT; }
+
+ cursor = (grn_geo_cursor_in_rectangle *)geo_cursor;
+ if (cursor->pat) { grn_obj_unlink(ctx, cursor->pat); }
+ if (cursor->index) { grn_obj_unlink(ctx, cursor->index); }
+ if (cursor->pat_cursor) { grn_table_cursor_close(ctx, cursor->pat_cursor); }
+ if (cursor->ii_cursor) { grn_ii_cursor_close(ctx, cursor->ii_cursor); }
+ GRN_FREE(cursor);
+
+ return GRN_SUCCESS;
+}
+
+typedef struct {
+ grn_hash *res;
+ grn_operator op;
+} grn_geo_select_in_rectangle_data;
+
+static grn_bool
+grn_geo_select_in_rectangle_callback(grn_ctx *ctx, grn_ii_posting *posting,
+ void *user_data)
+{
+ grn_geo_select_in_rectangle_data *data = user_data;
+ grn_ii_posting_add(ctx, posting, data->res, data->op);
+ return GRN_TRUE;
+}
+
+grn_rc
+grn_geo_select_in_rectangle(grn_ctx *ctx, grn_obj *index,
+ grn_obj *top_left_point,
+ grn_obj *bottom_right_point,
+ grn_obj *res, grn_operator op)
+{
+ grn_obj *cursor;
+
+ cursor = grn_geo_cursor_open_in_rectangle(ctx, index,
+ top_left_point, bottom_right_point,
+ 0, -1);
+ if (cursor) {
+ grn_geo_select_in_rectangle_data data;
+ data.res = (grn_hash *)res;
+ data.op = op;
+ grn_geo_cursor_each(ctx, cursor, grn_geo_select_in_rectangle_callback,
+ &data);
+ grn_obj_unlink(ctx, cursor);
+ grn_ii_resolve_sel_and(ctx, (grn_hash *)res, op);
+ }
+
+ return ctx->rc;
+}
+
+static grn_rc
+geo_point_get(grn_ctx *ctx, grn_obj *pat, int flags, grn_geo_point *geo_point)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_id id;
+ grn_table_cursor *cursor = NULL;
+
+ cursor = grn_table_cursor_open(ctx, pat,
+ NULL, 0,
+ NULL, 0,
+ 0, 1,
+ GRN_CURSOR_BY_KEY | flags);
+ if (!cursor) {
+ rc = ctx->rc;
+ goto exit;
+ }
+
+ id = grn_table_cursor_next(ctx, cursor);
+ if (id == GRN_ID_NIL) {
+ rc = GRN_END_OF_DATA;
+ } else {
+ void *key;
+ int key_size;
+ key_size = grn_table_cursor_get_key(ctx, cursor, &key);
+ memcpy(geo_point, key, key_size);
+ }
+
+exit:
+ if (cursor) {
+ grn_table_cursor_close(ctx, cursor);
+ }
+ return rc;
+}
+
+int
+grn_geo_estimate_in_rectangle(grn_ctx *ctx,
+ grn_obj *index,
+ grn_obj *top_left_point,
+ grn_obj *bottom_right_point)
+{
+ int n = 0;
+ int total_records;
+ grn_rc rc;
+ in_rectangle_data data;
+
+ GRN_VOID_INIT(&(data.top_left_point_buffer));
+ GRN_VOID_INIT(&(data.bottom_right_point_buffer));
+ if (in_rectangle_data_prepare(ctx, index, top_left_point, bottom_right_point,
+ "grn_geo_estimate_in_rectangle()", &data)) {
+ n = -1;
+ goto exit;
+ }
+
+ total_records = grn_table_size(ctx, data.pat);
+ if (total_records > 0) {
+ grn_geo_point min, max;
+ int select_latitude_distance, select_longitude_distance;
+ int total_latitude_distance, total_longitude_distance;
+ double select_ratio;
+ double estimated_n_records;
+ in_rectangle_area_data area_data;
+
+ rc = geo_point_get(ctx, data.pat, GRN_CURSOR_ASCENDING, &min);
+ if (!rc) {
+ rc = geo_point_get(ctx, data.pat, GRN_CURSOR_DESCENDING, &max);
+ }
+ if (rc) {
+ if (rc == GRN_END_OF_DATA) {
+ n = total_records;
+ rc = GRN_SUCCESS;
+ } else {
+ n = -1;
+ }
+ goto exit;
+ }
+
+ in_rectangle_area_data_compute(ctx,
+ data.top_left,
+ data.bottom_right,
+ &area_data);
+ select_latitude_distance =
+ abs(area_data.max.latitude - area_data.min.latitude);
+ select_longitude_distance =
+ abs(area_data.max.longitude - area_data.min.longitude);
+ total_latitude_distance = abs(max.latitude - min.latitude);
+ total_longitude_distance = abs(max.longitude - min.longitude);
+
+ select_ratio = 1.0;
+ if (select_latitude_distance < total_latitude_distance) {
+ select_ratio *= ((double)select_latitude_distance /
+ (double)total_latitude_distance);
+ }
+ if (select_longitude_distance < total_longitude_distance) {
+ select_ratio *= ((double)select_longitude_distance /
+ (double)total_longitude_distance);
+ }
+ estimated_n_records = ceil(total_records * select_ratio);
+ n = (int)estimated_n_records;
+ }
+
+exit :
+ grn_obj_unlink(ctx, &(data.top_left_point_buffer));
+ grn_obj_unlink(ctx, &(data.bottom_right_point_buffer));
+ return n;
+}
+
+grn_bool
+grn_geo_in_circle(grn_ctx *ctx, grn_obj *point, grn_obj *center,
+ grn_obj *radius_or_point,
+ grn_geo_approximate_type approximate_type)
+{
+ grn_bool r = GRN_FALSE;
+ grn_obj center_, radius_or_point_;
+ grn_id domain = point->header.domain;
+ if (domain == GRN_DB_TOKYO_GEO_POINT || domain == GRN_DB_WGS84_GEO_POINT) {
+ grn_geo_distance_raw_func distance_raw_func;
+ double d;
+ if (center->header.domain != domain) {
+ GRN_OBJ_INIT(&center_, GRN_BULK, 0, domain);
+ if (grn_obj_cast(ctx, center, &center_, GRN_FALSE)) { goto exit; }
+ center = &center_;
+ }
+
+ distance_raw_func = grn_geo_resolve_distance_raw_func(ctx,
+ approximate_type,
+ domain);
+ if (!distance_raw_func) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "unknown approximate type: <%d>", approximate_type);
+ goto exit;
+ }
+ d = distance_raw_func(ctx,
+ GRN_GEO_POINT_VALUE_RAW(point),
+ GRN_GEO_POINT_VALUE_RAW(center));
+ switch (radius_or_point->header.domain) {
+ case GRN_DB_INT32 :
+ r = d <= GRN_INT32_VALUE(radius_or_point);
+ break;
+ case GRN_DB_UINT32 :
+ r = d <= GRN_UINT32_VALUE(radius_or_point);
+ break;
+ case GRN_DB_INT64 :
+ r = d <= GRN_INT64_VALUE(radius_or_point);
+ break;
+ case GRN_DB_UINT64 :
+ r = d <= GRN_UINT64_VALUE(radius_or_point);
+ break;
+ case GRN_DB_FLOAT :
+ r = d <= GRN_FLOAT_VALUE(radius_or_point);
+ break;
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ GRN_OBJ_INIT(&radius_or_point_, GRN_BULK, 0, domain);
+ if (grn_obj_cast(ctx, radius_or_point, &radius_or_point_, GRN_FALSE)) { goto exit; }
+ radius_or_point = &radius_or_point_;
+ /* fallthru */
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ if (domain != radius_or_point->header.domain) { /* todo */ goto exit; }
+ r = d <= distance_raw_func(ctx,
+ GRN_GEO_POINT_VALUE_RAW(radius_or_point),
+ GRN_GEO_POINT_VALUE_RAW(center));
+ break;
+ default :
+ goto exit;
+ }
+ } else {
+ /* todo */
+ }
+exit :
+ return r;
+}
+
+grn_bool
+grn_geo_in_rectangle_raw(grn_ctx *ctx, grn_geo_point *point,
+ grn_geo_point *top_left, grn_geo_point *bottom_right)
+{
+ if (point->latitude > top_left->latitude) {
+ return GRN_FALSE;
+ }
+ if (point->latitude < bottom_right->latitude) {
+ return GRN_FALSE;
+ }
+
+ if (GRN_GEO_LONGITUDE_IS_WRAPPED(top_left, bottom_right)) {
+ if (point->longitude >= top_left->longitude) {
+ return GRN_TRUE;
+ }
+ if (point->longitude <= bottom_right->longitude) {
+ return GRN_TRUE;
+ }
+ return GRN_FALSE;
+ } else {
+ if (point->longitude < top_left->longitude) {
+ return GRN_FALSE;
+ }
+ if (point->longitude > bottom_right->longitude) {
+ return GRN_FALSE;
+ }
+ return GRN_TRUE;
+ }
+}
+
+grn_bool
+grn_geo_in_rectangle(grn_ctx *ctx, grn_obj *point,
+ grn_obj *top_left, grn_obj *bottom_right)
+{
+ grn_bool r = GRN_FALSE;
+ grn_obj top_left_, bottom_right_;
+ grn_id domain = point->header.domain;
+ if (domain == GRN_DB_TOKYO_GEO_POINT || domain == GRN_DB_WGS84_GEO_POINT) {
+ if (top_left->header.domain != domain) {
+ GRN_OBJ_INIT(&top_left_, GRN_BULK, 0, domain);
+ if (grn_obj_cast(ctx, top_left, &top_left_, GRN_FALSE)) { goto exit; }
+ top_left = &top_left_;
+ }
+ if (bottom_right->header.domain != domain) {
+ GRN_OBJ_INIT(&bottom_right_, GRN_BULK, 0, domain);
+ if (grn_obj_cast(ctx, bottom_right, &bottom_right_, GRN_FALSE)) { goto exit; }
+ bottom_right = &bottom_right_;
+ }
+ r = grn_geo_in_rectangle_raw(ctx,
+ GRN_GEO_POINT_VALUE_RAW(point),
+ GRN_GEO_POINT_VALUE_RAW(top_left),
+ GRN_GEO_POINT_VALUE_RAW(bottom_right));
+ } else {
+ /* todo */
+ }
+exit :
+ return r;
+}
+
+typedef enum {
+ LONGITUDE_SHORT,
+ LONGITUDE_LONG,
+} distance_type;
+
+typedef enum {
+ QUADRANT_1ST,
+ QUADRANT_2ND,
+ QUADRANT_3RD,
+ QUADRANT_4TH,
+ QUADRANT_1ST_TO_2ND,
+ QUADRANT_1ST_TO_3RD,
+ QUADRANT_1ST_TO_4TH,
+ QUADRANT_2ND_TO_1ST,
+ QUADRANT_2ND_TO_3RD,
+ QUADRANT_2ND_TO_4TH,
+ QUADRANT_3RD_TO_1ST,
+ QUADRANT_3RD_TO_2ND,
+ QUADRANT_3RD_TO_4TH,
+ QUADRANT_4TH_TO_1ST,
+ QUADRANT_4TH_TO_2ND,
+ QUADRANT_4TH_TO_3RD,
+} quadrant_type;
+
+static distance_type
+geo_longitude_distance_type(int start_longitude, int end_longitude)
+{
+ int diff_longitude;
+ int east_to_west;
+ int west_to_east;
+ if (start_longitude >= 0) {
+ diff_longitude = abs(start_longitude - end_longitude);
+ } else {
+ diff_longitude = abs(end_longitude - start_longitude);
+ }
+ east_to_west = start_longitude > 0 && end_longitude < 0;
+ west_to_east = start_longitude < 0 && end_longitude > 0;
+ if (start_longitude != end_longitude &&
+ (east_to_west || west_to_east) &&
+ diff_longitude > 180 * GRN_GEO_RESOLUTION) {
+ return LONGITUDE_LONG;
+ } else {
+ return LONGITUDE_SHORT;
+ }
+}
+
+static inline quadrant_type
+geo_quadrant_type(grn_geo_point *point1, grn_geo_point *point2)
+{
+#define QUADRANT_1ST_WITH_AXIS(point) \
+ (point->longitude >= 0) && (point->latitude >= 0)
+#define QUADRANT_2ND_WITH_AXIS(point) \
+ (point->longitude <= 0) && (point->latitude >= 0)
+#define QUADRANT_3RD_WITH_AXIS(point) \
+ (point->longitude <= 0) && (point->latitude <= 0)
+#define QUADRANT_4TH_WITH_AXIS(point) \
+ (point->longitude >= 0) && (point->latitude <= 0)
+
+ if (QUADRANT_1ST_WITH_AXIS(point1) && QUADRANT_1ST_WITH_AXIS(point2)) {
+ return QUADRANT_1ST;
+ } else if (QUADRANT_2ND_WITH_AXIS(point1) && QUADRANT_2ND_WITH_AXIS(point2)) {
+ return QUADRANT_2ND;
+ } else if (QUADRANT_3RD_WITH_AXIS(point1) && QUADRANT_3RD_WITH_AXIS(point2)) {
+ return QUADRANT_3RD;
+ } else if (QUADRANT_4TH_WITH_AXIS(point1) && QUADRANT_4TH_WITH_AXIS(point2)) {
+ return QUADRANT_4TH;
+ } else {
+ if (point1->longitude > 0 && point2->longitude < 0 &&
+ point1->latitude >= 0 && point2->latitude >= 0) {
+ return QUADRANT_1ST_TO_2ND;
+ } else if (point1->longitude < 0 && point2->longitude > 0 &&
+ point1->latitude >= 0 && point2->latitude >= 0) {
+ return QUADRANT_2ND_TO_1ST;
+ } else if (point1->longitude < 0 && point2->longitude > 0 &&
+ point1->latitude <= 0 && point2->latitude <= 0) {
+ return QUADRANT_3RD_TO_4TH;
+ } else if (point1->longitude > 0 && point2->longitude < 0 &&
+ point1->latitude <= 0 && point2->latitude <= 0) {
+ return QUADRANT_4TH_TO_3RD;
+ } else if (point1->longitude >= 0 && point2->longitude >= 0 &&
+ point1->latitude > 0 && point2->latitude < 0) {
+ return QUADRANT_1ST_TO_4TH;
+ } else if (point1->longitude >= 0 && point2->longitude >= 0 &&
+ point1->latitude < 0 && point2->latitude > 0) {
+ return QUADRANT_4TH_TO_1ST;
+ } else if (point1->longitude <= 0 && point2->longitude <= 0 &&
+ point1->latitude > 0 && point2->latitude < 0) {
+ return QUADRANT_2ND_TO_3RD;
+ } else if (point1->longitude <= 0 && point2->longitude <= 0 &&
+ point1->latitude < 0 && point2->latitude > 0) {
+ return QUADRANT_3RD_TO_2ND;
+ } else if (point1->longitude >= 0 && point2->longitude <= 0 &&
+ point1->latitude > 0 && point2->latitude < 0) {
+ return QUADRANT_1ST_TO_3RD;
+ } else if (point1->longitude <= 0 && point2->longitude >= 0 &&
+ point1->latitude < 0 && point2->latitude > 0) {
+ return QUADRANT_3RD_TO_1ST;
+ } else if (point1->longitude <= 0 && point2->longitude >= 0 &&
+ point1->latitude > 0 && point2->latitude < 0) {
+ return QUADRANT_2ND_TO_4TH;
+ } else if (point1->longitude >= 0 && point2->longitude <= 0 &&
+ point1->latitude < 0 && point2->latitude > 0) {
+ return QUADRANT_4TH_TO_2ND;
+ } else {
+ /* FIXME */
+ return QUADRANT_1ST;
+ }
+ }
+#undef QUADRANT_1ST_WITH_AXIS
+#undef QUADRANT_2ND_WITH_AXIS
+#undef QUADRANT_3RD_WITH_AXIS
+#undef QUADRANT_4TH_WITH_AXIS
+}
+
+static inline double
+geo_distance_rectangle_square_root(double start_longitude, double start_latitude,
+ double end_longitude, double end_latitude)
+{
+ double diff_longitude;
+ double x, y;
+
+ diff_longitude = end_longitude - start_longitude;
+ x = diff_longitude * cos((start_latitude + end_latitude) * 0.5);
+ y = end_latitude - start_latitude;
+ return sqrt((x * x) + (y * y));
+}
+
+static inline double
+geo_distance_rectangle_short_dist_type(quadrant_type quad_type,
+ double lng1, double lat1,
+ double lng2, double lat2)
+{
+ double distance;
+ double longitude_delta, latitude_delta;
+
+ if (quad_type == QUADRANT_1ST_TO_4TH ||
+ quad_type == QUADRANT_4TH_TO_1ST ||
+ quad_type == QUADRANT_2ND_TO_3RD ||
+ quad_type == QUADRANT_3RD_TO_2ND) {
+ longitude_delta = lng2 - lng1;
+ if (longitude_delta > 0 || longitude_delta < 0) {
+ if (lat2 > lat1) {
+ distance = geo_distance_rectangle_square_root(lng1,
+ lat1,
+ lng2,
+ lat2) * GRN_GEO_RADIUS;
+ } else {
+ distance = geo_distance_rectangle_square_root(lng2,
+ lat2,
+ lng1,
+ lat1) * GRN_GEO_RADIUS;
+ }
+ } else {
+ latitude_delta = fabs(lat1) + fabs(lat2);
+ distance = sqrt(latitude_delta * latitude_delta) * GRN_GEO_RADIUS;
+ }
+ } else if (quad_type == QUADRANT_1ST_TO_3RD ||
+ quad_type == QUADRANT_2ND_TO_4TH) {
+ distance = geo_distance_rectangle_square_root(lng1,
+ lat1,
+ lng2,
+ lat2) * GRN_GEO_RADIUS;
+ } else if (quad_type == QUADRANT_3RD_TO_1ST ||
+ quad_type == QUADRANT_4TH_TO_2ND) {
+ distance = geo_distance_rectangle_square_root(lng2,
+ lat2,
+ lng1,
+ lat1) * GRN_GEO_RADIUS;
+ } else if (quad_type == QUADRANT_1ST_TO_2ND ||
+ quad_type == QUADRANT_2ND_TO_1ST ||
+ quad_type == QUADRANT_3RD_TO_4TH ||
+ quad_type == QUADRANT_4TH_TO_3RD) {
+ if (lat2 > lat1) {
+ distance = geo_distance_rectangle_square_root(lng1,
+ lat1,
+ lng2,
+ lat2) * GRN_GEO_RADIUS;
+ } else if (lat2 < lat1) {
+ distance = geo_distance_rectangle_square_root(lng2,
+ lat2,
+ lng1,
+ lat1) * GRN_GEO_RADIUS;
+ } else {
+ longitude_delta = lng2 - lng1;
+ distance = longitude_delta * cos(lat1);
+ distance = sqrt(distance * distance) * GRN_GEO_RADIUS;
+ }
+ } else {
+ distance = geo_distance_rectangle_square_root(lng1,
+ lat1,
+ lng2,
+ lat2) * GRN_GEO_RADIUS;
+ }
+ return distance;
+}
+
+static inline double
+geo_distance_rectangle_long_dist_type(quadrant_type quad_type,
+ double lng1, double lat1,
+ double lng2, double lat2)
+{
+#define M_2PI 6.28318530717958647692
+
+ double distance;
+
+ if (quad_type == QUADRANT_1ST_TO_2ND ||
+ quad_type == QUADRANT_4TH_TO_3RD) {
+ if (lat1 > lat2) {
+ distance = geo_distance_rectangle_square_root(lng2 + M_2PI,
+ lat2,
+ lng1,
+ lat1) * GRN_GEO_RADIUS;
+ } else {
+ distance = geo_distance_rectangle_square_root(lng1,
+ lat1,
+ lng2 + M_2PI,
+ lat2) * GRN_GEO_RADIUS;
+ }
+ } else if (quad_type == QUADRANT_2ND_TO_1ST ||
+ quad_type == QUADRANT_3RD_TO_4TH) {
+ if (lat1 > lat2) {
+ distance = geo_distance_rectangle_square_root(lng2,
+ lat2,
+ lng1 + M_2PI,
+ lat1) * GRN_GEO_RADIUS;
+ } else {
+ distance = geo_distance_rectangle_square_root(lng1 + M_2PI,
+ lat1,
+ lng2,
+ lat2) * GRN_GEO_RADIUS;
+ }
+ } else if (quad_type == QUADRANT_1ST_TO_3RD) {
+ distance = geo_distance_rectangle_square_root(lng2 + M_2PI,
+ lat2,
+ lng1,
+ lat1) * GRN_GEO_RADIUS;
+ } else if (quad_type == QUADRANT_3RD_TO_1ST) {
+ distance = geo_distance_rectangle_square_root(lng1 + M_2PI,
+ lat1,
+ lng2,
+ lat2) * GRN_GEO_RADIUS;
+ } else if (quad_type == QUADRANT_2ND_TO_4TH) {
+ distance = geo_distance_rectangle_square_root(lng2,
+ lat2,
+ lng1 + M_2PI,
+ lat1) * GRN_GEO_RADIUS;
+ } else if (quad_type == QUADRANT_4TH_TO_2ND) {
+ distance = geo_distance_rectangle_square_root(lng1,
+ lat1,
+ lng2 + M_2PI,
+ lat2) * GRN_GEO_RADIUS;
+ } else {
+ if (lng1 > lng2) {
+ distance = geo_distance_rectangle_square_root(lng1,
+ lat1,
+ lng2 + M_2PI,
+ lat2) * GRN_GEO_RADIUS;
+ } else {
+ distance = geo_distance_rectangle_square_root(lng2,
+ lat2,
+ lng1 + M_2PI,
+ lat1) * GRN_GEO_RADIUS;
+ }
+ }
+ return distance;
+#undef M_2PI
+}
+
+double
+grn_geo_distance_rectangle_raw(grn_ctx *ctx,
+ grn_geo_point *point1, grn_geo_point *point2)
+{
+
+ double lng1, lat1, lng2, lat2, distance;
+ distance_type dist_type;
+ quadrant_type quad_type;
+
+ lat1 = GRN_GEO_INT2RAD(point1->latitude);
+ lng1 = GRN_GEO_INT2RAD(point1->longitude);
+ lat2 = GRN_GEO_INT2RAD(point2->latitude);
+ lng2 = GRN_GEO_INT2RAD(point2->longitude);
+ quad_type = geo_quadrant_type(point1, point2);
+ if (quad_type <= QUADRANT_4TH) {
+ distance = geo_distance_rectangle_square_root(lng1,
+ lat1,
+ lng2,
+ lat2) * GRN_GEO_RADIUS;
+ } else {
+ dist_type = geo_longitude_distance_type(point1->longitude,
+ point2->longitude);
+ if (dist_type == LONGITUDE_SHORT) {
+ distance = geo_distance_rectangle_short_dist_type(quad_type,
+ lng1, lat1,
+ lng2, lat2);
+ } else {
+ distance = geo_distance_rectangle_long_dist_type(quad_type,
+ lng1, lat1,
+ lng2, lat2);
+ }
+ }
+ return distance;
+}
+
+double
+grn_geo_distance_sphere_raw(grn_ctx *ctx,
+ grn_geo_point *point1, grn_geo_point *point2)
+{
+ double lng1, lat1, lng2, lat2, x, y;
+
+ lat1 = GRN_GEO_INT2RAD(point1->latitude);
+ lng1 = GRN_GEO_INT2RAD(point1->longitude);
+ lat2 = GRN_GEO_INT2RAD(point2->latitude);
+ lng2 = GRN_GEO_INT2RAD(point2->longitude);
+ x = sin(fabs(lng2 - lng1) * 0.5);
+ y = sin(fabs(lat2 - lat1) * 0.5);
+ return asin(sqrt((y * y) + cos(lat1) * cos(lat2) * x * x)) * 2 * GRN_GEO_RADIUS;
+}
+
+double
+grn_geo_distance_ellipsoid_raw(grn_ctx *ctx,
+ grn_geo_point *point1, grn_geo_point *point2,
+ int c1, int c2, double c3)
+{
+ double lng1, lat1, lng2, lat2, p, q, r, m, n, x, y;
+
+ lat1 = GRN_GEO_INT2RAD(point1->latitude);
+ lng1 = GRN_GEO_INT2RAD(point1->longitude);
+ lat2 = GRN_GEO_INT2RAD(point2->latitude);
+ lng2 = GRN_GEO_INT2RAD(point2->longitude);
+ p = (lat1 + lat2) * 0.5;
+ q = (1 - c3 * sin(p) * sin(p));
+ r = sqrt(q);
+ m = c1 / (q * r);
+ n = c2 / r;
+ x = n * cos(p) * fabs(lng1 - lng2);
+ y = m * fabs(lat1 - lat2);
+ return sqrt((x * x) + (y * y));
+}
+
+double
+grn_geo_distance_ellipsoid_raw_tokyo(grn_ctx *ctx,
+ grn_geo_point *point1,
+ grn_geo_point *point2)
+{
+ return grn_geo_distance_ellipsoid_raw(ctx, point1, point2,
+ GRN_GEO_BES_C1,
+ GRN_GEO_BES_C2,
+ GRN_GEO_BES_C3);
+}
+
+double
+grn_geo_distance_ellipsoid_raw_wgs84(grn_ctx *ctx,
+ grn_geo_point *point1,
+ grn_geo_point *point2)
+{
+ return grn_geo_distance_ellipsoid_raw(ctx, point1, point2,
+ GRN_GEO_GRS_C1,
+ GRN_GEO_GRS_C2,
+ GRN_GEO_GRS_C3);
+}
+
+double
+grn_geo_distance(grn_ctx *ctx, grn_obj *point1, grn_obj *point2,
+ grn_geo_approximate_type type)
+{
+ double d = 0.0;
+
+ switch (type) {
+ case GRN_GEO_APPROXIMATE_RECTANGLE :
+ d = grn_geo_distance_rectangle(ctx, point1, point2);
+ break;
+ case GRN_GEO_APPROXIMATE_SPHERE :
+ d = grn_geo_distance_sphere(ctx, point1, point2);
+ break;
+ case GRN_GEO_APPROXIMATE_ELLIPSOID :
+ d = grn_geo_distance_ellipsoid(ctx, point1, point2);
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "unknown approximate type: <%d>", type);
+ break;
+ }
+ return d;
+}
+
+double
+grn_geo_distance_rectangle(grn_ctx *ctx, grn_obj *point1, grn_obj *point2)
+{
+ double d = 0;
+ grn_bool point1_initialized = GRN_FALSE;
+ grn_bool point2_initialized = GRN_FALSE;
+ grn_obj point1_, point2_;
+ grn_id domain1 = point1->header.domain;
+ grn_id domain2 = point2->header.domain;
+ if (domain1 == GRN_DB_TOKYO_GEO_POINT || domain1 == GRN_DB_WGS84_GEO_POINT) {
+ if (domain1 != domain2) {
+ GRN_OBJ_INIT(&point2_, GRN_BULK, 0, domain1);
+ point2_initialized = GRN_TRUE;
+ if (grn_obj_cast(ctx, point2, &point2_, GRN_FALSE)) { goto exit; }
+ point2 = &point2_;
+ }
+ } else if (domain2 == GRN_DB_TOKYO_GEO_POINT ||
+ domain2 == GRN_DB_WGS84_GEO_POINT) {
+ GRN_OBJ_INIT(&point1_, GRN_BULK, 0, domain2);
+ point1_initialized = GRN_TRUE;
+ if (grn_obj_cast(ctx, point1, &point1_, GRN_FALSE)) { goto exit; }
+ point1 = &point1_;
+ } else if ((GRN_DB_SHORT_TEXT <= domain1 && domain1 <= GRN_DB_LONG_TEXT) &&
+ (GRN_DB_SHORT_TEXT <= domain2 && domain2 <= GRN_DB_LONG_TEXT)) {
+ GRN_OBJ_INIT(&point1_, GRN_BULK, 0, GRN_DB_WGS84_GEO_POINT);
+ point1_initialized = GRN_TRUE;
+ if (grn_obj_cast(ctx, point1, &point1_, GRN_FALSE)) { goto exit; }
+ point1 = &point1_;
+
+ GRN_OBJ_INIT(&point2_, GRN_BULK, 0, GRN_DB_WGS84_GEO_POINT);
+ point2_initialized = GRN_TRUE;
+ if (grn_obj_cast(ctx, point2, &point2_, GRN_FALSE)) { goto exit; }
+ point2 = &point2_;
+ } else {
+ goto exit;
+ }
+ d = grn_geo_distance_rectangle_raw(ctx,
+ GRN_GEO_POINT_VALUE_RAW(point1),
+ GRN_GEO_POINT_VALUE_RAW(point2));
+exit :
+ if (point1_initialized) {
+ GRN_OBJ_FIN(ctx, &point1_);
+ }
+ if (point2_initialized) {
+ GRN_OBJ_FIN(ctx, &point2_);
+ }
+ return d;
+}
+
+double
+grn_geo_distance_sphere(grn_ctx *ctx, grn_obj *point1, grn_obj *point2)
+{
+ double d = 0;
+ grn_bool point2_initialized = GRN_FALSE;
+ grn_obj point2_;
+ grn_id domain = point1->header.domain;
+ if (domain == GRN_DB_TOKYO_GEO_POINT || domain == GRN_DB_WGS84_GEO_POINT) {
+ if (point2->header.domain != domain) {
+ GRN_OBJ_INIT(&point2_, GRN_BULK, 0, domain);
+ point2_initialized = GRN_TRUE;
+ if (grn_obj_cast(ctx, point2, &point2_, GRN_FALSE)) { goto exit; }
+ point2 = &point2_;
+ }
+ d = grn_geo_distance_sphere_raw(ctx,
+ GRN_GEO_POINT_VALUE_RAW(point1),
+ GRN_GEO_POINT_VALUE_RAW(point2));
+ } else {
+ /* todo */
+ }
+exit :
+ if (point2_initialized) {
+ GRN_OBJ_FIN(ctx, &point2_);
+ }
+ return d;
+}
+
+double
+grn_geo_distance_ellipsoid(grn_ctx *ctx, grn_obj *point1, grn_obj *point2)
+{
+ double d = 0;
+ grn_bool point2_initialized = GRN_FALSE;
+ grn_obj point2_;
+ grn_id domain = point1->header.domain;
+ if (domain == GRN_DB_TOKYO_GEO_POINT || domain == GRN_DB_WGS84_GEO_POINT) {
+ if (point2->header.domain != domain) {
+ GRN_OBJ_INIT(&point2_, GRN_BULK, 0, domain);
+ point2_initialized = GRN_TRUE;
+ if (grn_obj_cast(ctx, point2, &point2_, GRN_FALSE)) { goto exit; }
+ point2 = &point2_;
+ }
+ if (domain == GRN_DB_TOKYO_GEO_POINT) {
+ d = grn_geo_distance_ellipsoid_raw_tokyo(ctx,
+ GRN_GEO_POINT_VALUE_RAW(point1),
+ GRN_GEO_POINT_VALUE_RAW(point2));
+ } else {
+ d = grn_geo_distance_ellipsoid_raw_wgs84(ctx,
+ GRN_GEO_POINT_VALUE_RAW(point1),
+ GRN_GEO_POINT_VALUE_RAW(point2));
+ }
+ } else {
+ /* todo */
+ }
+exit :
+ if (point2_initialized) {
+ GRN_OBJ_FIN(ctx, &point2_);
+ }
+ return d;
+}
diff --git a/storage/mroonga/vendor/groonga/lib/geo.h b/storage/mroonga/vendor/groonga/lib/geo.h
new file mode 100644
index 00000000000..b1e04061876
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/geo.h
@@ -0,0 +1,207 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_GEO_H
+#define GRN_GEO_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#include "ii.h"
+#include "db.h"
+
+#if defined(WIN32) || defined(__sun)
+# define _USE_MATH_DEFINES
+# ifndef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+# endif
+
+# ifndef MIN
+# define MIN(a, b) ((a) < (b) ? (a) : (b))
+# endif
+#endif /* WIN32 or __sun */
+#include <math.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GRN_GEO_RESOLUTION 3600000
+#define GRN_GEO_RADIUS 6357303
+#define GRN_GEO_BES_C1 6334834
+#define GRN_GEO_BES_C2 6377397
+#define GRN_GEO_BES_C3 0.006674
+#define GRN_GEO_GRS_C1 6335439
+#define GRN_GEO_GRS_C2 6378137
+#define GRN_GEO_GRS_C3 0.006694
+#define GRN_GEO_INT2RAD(x) ((M_PI / (GRN_GEO_RESOLUTION * 180)) * (x))
+#define GRN_GEO_RAD2INT(x) ((int)(((GRN_GEO_RESOLUTION * 180) / M_PI) * (x)))
+
+#define GRN_GEO_MAX_LATITUDE 324000000 /* 90 * 60 * 60 * 1000 */
+#define GRN_GEO_MAX_LONGITUDE (648000000 - 1) /* 180 * 60 * 60 * 1000 - 1 */
+#define GRN_GEO_MIN_LATITUDE -GRN_GEO_MAX_LATITUDE
+#define GRN_GEO_MIN_LONGITUDE -GRN_GEO_MAX_LONGITUDE
+
+#define GRN_GEO_POINT_VALUE_RAW(obj) (grn_geo_point *)GRN_BULK_HEAD(obj)
+#define GRN_GEO_POINT_VALUE_RADIUS(obj,_latitude,_longitude) do {\
+ grn_geo_point *_val = (grn_geo_point *)GRN_BULK_HEAD(obj);\
+ _latitude = GRN_GEO_INT2RAD(_val->latitude);\
+ _longitude = GRN_GEO_INT2RAD(_val->longitude);\
+} while (0)
+
+#define GRN_GEO_KEY_MAX_BITS 64
+
+typedef enum {
+ GRN_GEO_APPROXIMATE_RECTANGLE,
+ GRN_GEO_APPROXIMATE_SPHERE,
+ GRN_GEO_APPROXIMATE_ELLIPSOID
+} grn_geo_approximate_type;
+
+typedef enum {
+ GRN_GEO_CURSOR_ENTRY_STATUS_NONE = 0,
+ GRN_GEO_CURSOR_ENTRY_STATUS_TOP_INCLUDED = 1 << 0,
+ GRN_GEO_CURSOR_ENTRY_STATUS_BOTTOM_INCLUDED = 1 << 1,
+ GRN_GEO_CURSOR_ENTRY_STATUS_LEFT_INCLUDED = 1 << 2,
+ GRN_GEO_CURSOR_ENTRY_STATUS_RIGHT_INCLUDED = 1 << 3,
+ GRN_GEO_CURSOR_ENTRY_STATUS_LATITUDE_INNER = 1 << 4,
+ GRN_GEO_CURSOR_ENTRY_STATUS_LONGITUDE_INNER = 1 << 5
+} grn_geo_cursor_entry_status_flag;
+
+typedef enum {
+ GRN_GEO_AREA_NORTH_EAST,
+ GRN_GEO_AREA_NORTH_WEST,
+ GRN_GEO_AREA_SOUTH_WEST,
+ GRN_GEO_AREA_SOUTH_EAST,
+ GRN_GEO_AREA_LAST
+} grn_geo_area_type;
+
+#define GRN_GEO_N_AREAS GRN_GEO_AREA_LAST
+
+typedef struct {
+ uint8_t key[sizeof(grn_geo_point)];
+ int target_bit;
+ int status_flags;
+} grn_geo_cursor_entry;
+
+typedef struct {
+ grn_geo_point top_left;
+ grn_geo_point bottom_right;
+ uint8_t top_left_key[sizeof(grn_geo_point)];
+ uint8_t bottom_right_key[sizeof(grn_geo_point)];
+ int current_entry;
+ grn_geo_cursor_entry entries[GRN_GEO_KEY_MAX_BITS];
+} grn_geo_cursor_area;
+
+typedef struct {
+ grn_db_obj obj;
+ grn_obj *pat;
+ grn_obj *index;
+ grn_geo_point top_left;
+ grn_geo_point bottom_right;
+ grn_geo_point current;
+ grn_table_cursor *pat_cursor;
+ grn_ii_cursor *ii_cursor;
+ int offset;
+ int rest;
+ int minimum_reduce_bit;
+ grn_geo_area_type current_area;
+ grn_geo_cursor_area areas[GRN_GEO_N_AREAS];
+} grn_geo_cursor_in_rectangle;
+
+grn_rc grn_geo_cursor_close(grn_ctx *ctx, grn_obj *geo_cursor);
+
+
+int grn_geo_table_sort(grn_ctx *ctx, grn_obj *table, int offset, int limit,
+ grn_obj *result, grn_table_sort_key *keys, int n_keys);
+
+grn_rc grn_geo_resolve_approximate_type(grn_ctx *ctx, grn_obj *type_name,
+ grn_geo_approximate_type *type);
+
+/**
+ * grn_geo_select_in_circle:
+ * @index: the index column for TokyoGeoPoint or WGS84GeoPpoint type.
+ * @center_point: the center point of the target circle. (ShortText, Text,
+ * LongText, TokyoGeoPoint or WGS84GeoPoint)
+ * @distance: the radius of the target circle (Int32,
+ * UInt32, Int64, UInt64 or Float) or the point
+ * on the circumference of the target circle. (ShortText, Text, LongText,
+ * TokyoGeoPoint or WGS84GeoPoint)
+ * @approximate_type: the approximate type to compute
+ * distance.
+ * @res: the table to store found record IDs. It must be
+ * GRN_TABLE_HASH_KEY type table.
+ * @op: the operator for matched records.
+ *
+ * It selects records that are in the circle specified by
+ * @center_point and @distance from @center_point. Records
+ * are searched by @index. Found records are added to @res
+ * table with @op operation.
+ **/
+grn_rc grn_geo_select_in_circle(grn_ctx *ctx,
+ grn_obj *index,
+ grn_obj *center_point,
+ grn_obj *distance,
+ grn_geo_approximate_type approximate_type,
+ grn_obj *res,
+ grn_operator op);
+
+grn_rc grn_selector_geo_in_circle(grn_ctx *ctx, grn_obj *table, grn_obj *index,
+ int nargs, grn_obj **args,
+ grn_obj *res, grn_operator op);
+grn_rc grn_selector_geo_in_rectangle(grn_ctx *ctx,
+ grn_obj *table, grn_obj *index,
+ int nargs, grn_obj **args,
+ grn_obj *res, grn_operator op);
+
+GRN_API grn_bool grn_geo_in_circle(grn_ctx *ctx, grn_obj *point, grn_obj *center,
+ grn_obj *radius_or_point,
+ grn_geo_approximate_type approximate_type);
+GRN_API grn_bool grn_geo_in_rectangle(grn_ctx *ctx, grn_obj *point,
+ grn_obj *top_left, grn_obj *bottom_right);
+grn_bool grn_geo_in_rectangle_raw(grn_ctx *ctx, grn_geo_point *point,
+ grn_geo_point *top_left,
+ grn_geo_point *bottom_right);
+double grn_geo_distance(grn_ctx *ctx, grn_obj *point1, grn_obj *point2,
+ grn_geo_approximate_type type);
+GRN_API double grn_geo_distance_rectangle(grn_ctx *ctx, grn_obj *point1,
+ grn_obj *point2);
+GRN_API double grn_geo_distance_sphere(grn_ctx *ctx, grn_obj *point1,
+ grn_obj *point2);
+GRN_API double grn_geo_distance_ellipsoid(grn_ctx *ctx, grn_obj *point1,
+ grn_obj *point2);
+double grn_geo_distance_rectangle_raw(grn_ctx *ctx,
+ grn_geo_point *point1,
+ grn_geo_point *point2);
+double grn_geo_distance_sphere_raw(grn_ctx *ctx,
+ grn_geo_point *point1,
+ grn_geo_point *point2);
+double grn_geo_distance_ellipsoid_raw(grn_ctx *ctx,
+ grn_geo_point *point1,
+ grn_geo_point *point2,
+ int c1, int c2, double c3);
+double grn_geo_distance_ellipsoid_raw_tokyo(grn_ctx *ctx,
+ grn_geo_point *point1,
+ grn_geo_point *point2);
+double grn_geo_distance_ellipsoid_raw_wgs84(grn_ctx *ctx,
+ grn_geo_point *point1,
+ grn_geo_point *point2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_GEO_H */
diff --git a/storage/mroonga/vendor/groonga/lib/groonga_in.h b/storage/mroonga/vendor/groonga/lib/groonga_in.h
new file mode 100644
index 00000000000..322cd9dd950
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/groonga_in.h
@@ -0,0 +1,746 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GROONGA_IN_H
+#define GROONGA_IN_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifdef __cplusplus
+#define __STDC_LIMIT_MACROS
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif /* HAVE_STDLIB_H */
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif /* HAVE_STDINT_H */
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif /* HAVE_SYS_TYPES_H */
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif /* HAVE_SYS_MMAN_H */
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif /* HAVE_SYS_TIME_H */
+#ifdef HAVE_SYS_TIMEB_H
+#include <sys/timeb.h>
+#endif /* HAVE_SYS_TIMEB_H */
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif /* HAVE_SYS_RESOURCE_H */
+
+#ifdef WIN32
+# define GRN_API __declspec(dllexport)
+#ifdef GROONGA_MAIN
+# define GRN_VAR __declspec(dllimport)
+#else
+# define GRN_VAR __declspec(dllexport) extern
+#endif /* GROONGA_MAIN */
+#else
+# define GRN_API
+# define GRN_VAR extern
+#endif
+
+#ifdef HAVE_OPEN
+# define GRN_OPEN(pathname, ...) open(pathname, __VA_ARGS__)
+#else
+# define GRN_OPEN(pathname, ...) _open(pathname, __VA_ARGS__)
+#endif /* HAVE_OPEN */
+
+#ifdef HAVE_CLOSE
+# define GRN_CLOSE(fd) close(fd)
+#else
+# define GRN_CLOSE(fd) _close(fd)
+#endif /* HAVE_CLOSE */
+
+#ifdef HAVE_READ
+# define GRN_READ(fd, buf, count) read(fd, buf, count)
+#else
+# define GRN_READ(fd, buf, count) _read(fd, buf, count)
+#endif /* HAVE_READ */
+
+#ifdef HAVE_WRITE
+# define GRN_WRITE(fd, buf, count) write(fd, buf, count)
+#else
+# define GRN_WRITE(fd, buf, count) _write(fd, buf, count)
+#endif /* HAVE_WRITE */
+
+#ifdef WIN32
+
+#if defined(__GNUC__) && !defined(WINVER)
+# include <w32api.h>
+# define WINVER WindowsXP
+#endif /* defined(__GNUC__) && !defined(WINVER) */
+
+#include <basetsd.h>
+#include <process.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#include <stddef.h>
+#include <windef.h>
+#include <float.h>
+#include <time.h>
+#include <sys/types.h>
+
+#ifndef __GNUC__
+# define PATH_MAX (MAX_PATH - 1)
+# ifndef __cplusplus
+# define inline _inline
+# endif
+#endif
+
+#ifndef __GNUC__
+# define snprintf(str, size, ...) _snprintf(str, size, __VA_ARGS__)
+#endif /* __GNUC__ */
+#if !defined(__GNUC__) && _MSC_VER < 1500
+# define vsnprintf(str, size, format, ap) _vsnprintf(str, size, format, ap)
+#endif /* !defined(__GNUC__) && _MSC_VER < 1500 */
+#define unlink(pathname) _unlink(pathname)
+#define lseek(fd, offset, whence) _lseek(fd, offset, whence)
+#define getpid() _getpid()
+#if !defined(__GNUC__) && _MSC_VER < 1400
+# define fstat(fd, buf) _fstat(fd, buf)
+#endif /* !defined(__GNUC__) && _MSC_VER < 1400 */
+#if !defined(strcasecmp)
+# define strcasecmp(s1, s2) _stricmp(s1, s2)
+#endif /* !defined(strcasecmp) */
+
+#ifdef __GNUC__
+#include <stdint.h>
+#else
+#define uint8_t UINT8
+#define int8_t INT8
+#define int_least8_t INT8
+#define uint_least8_t UINT8
+#define int16_t INT16
+#define uint16_t UINT16
+#define int32_t INT32
+#define uint32_t UINT32
+#define int64_t INT64
+#define uint64_t UINT64
+#define ssize_t SSIZE_T
+#define pid_t int
+#endif
+
+#undef MSG_WAITALL
+#define MSG_WAITALL 0 /* before Vista, not supported... */
+#define SHUT_RDWR SD_BOTH
+
+typedef SOCKET grn_sock;
+#define grn_sock_close(sock) closesocket(sock)
+
+#define CALLBACK __stdcall
+
+#ifndef __GNUC__
+#include <intrin.h>
+#include <sys/timeb.h>
+#include <errno.h>
+#endif
+
+#else /* WIN32 */
+
+#define GROONGA_API
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+# ifndef PATH_MAX
+# if defined(MAXPATHLEN)
+# define PATH_MAX MAXPATHLEN
+# else /* MAXPATHLEN */
+# define PATH_MAX 1024
+# endif /* MAXPATHLEN */
+# endif /* PATH_MAX */
+# ifndef INT_LEAST8_MAX
+typedef char int_least8_t;
+# endif /* INT_LEAST8_MAX */
+# ifndef UINT_LEAST8_MAX
+typedef unsigned char uint_least8_t;
+# endif /* UINT_LEAST8_MAX */
+typedef int grn_sock;
+# define grn_sock_close(sock) close(sock)
+# define CALLBACK
+
+#endif /* WIN32 */
+
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif /* INT8_MAX */
+
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif /* INT8_MIN */
+
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif /* INT16_MAX */
+
+#ifndef INT16_MIN
+#define INT16_MIN (-32768)
+#endif /* INT16_MIN */
+
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif /* INT32_MAX */
+
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483648)
+#endif /* INT32_MIN */
+
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295)
+#endif /* UINT32_MAX */
+
+#ifndef INT64_MAX
+#define INT64_MAX (9223372036854775807)
+#endif /* INT64_MAX */
+
+#ifndef INT64_MIN
+#define INT64_MIN (-9223372036854775808)
+#endif /* INT64_MIN */
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+typedef pthread_t grn_thread;
+#define THREAD_CREATE(thread,func,arg) \
+ (pthread_create(&(thread), NULL, (func), (arg)))
+#define THREAD_JOIN(thread) (pthread_join(thread, NULL))
+typedef pthread_mutex_t grn_mutex;
+#define MUTEX_INIT(m) pthread_mutex_init(&m, NULL)
+#define MUTEX_LOCK(m) pthread_mutex_lock(&m)
+#define MUTEX_UNLOCK(m) pthread_mutex_unlock(&m)
+#define MUTEX_FIN(m)
+#ifdef HAVE_PTHREAD_MUTEXATTR_SETPSHARED
+# define MUTEX_INIT_SHARED(m) do {\
+ pthread_mutexattr_t mutexattr;\
+ pthread_mutexattr_init(&mutexattr);\
+ pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);\
+ pthread_mutex_init(&m, &mutexattr);\
+} while (0)
+#else
+# define MUTEX_INIT_SHARED MUTEX_INIT
+#endif /* HAVE_PTHREAD_MUTEXATTR_SETPSHARED */
+
+typedef pthread_mutex_t grn_critical_section;
+#define CRITICAL_SECTION_INIT(cs) pthread_mutex_init(&(cs), NULL)
+#define CRITICAL_SECTION_ENTER(cs) pthread_mutex_lock(&(cs))
+#define CRITICAL_SECTION_LEAVE(cs) pthread_mutex_unlock(&(cs))
+#define CRITICAL_SECTION_FIN(cs)
+
+typedef pthread_cond_t grn_cond;
+#define COND_INIT(c) pthread_cond_init(&c, NULL)
+#define COND_SIGNAL(c) pthread_cond_signal(&c)
+#define COND_WAIT(c,m) pthread_cond_wait(&c, &m)
+#define COND_BROADCAST(c) pthread_cond_broadcast(&c)
+#ifdef HAVE_PTHREAD_CONDATTR_SETPSHARED
+# define COND_INIT_SHARED(c) do {\
+ pthread_condattr_t condattr;\
+ pthread_condattr_init(&condattr);\
+ pthread_condattr_setpshared(&condattr, PTHREAD_PROCESS_SHARED);\
+ pthread_cond_init(&c, &condattr);\
+} while (0)
+#else
+# define COND_INIT_SHARED COND_INIT
+#endif /* HAVE_PTHREAD_CONDATTR_SETPSHARE */
+
+typedef pthread_key_t grn_thread_key;
+#define THREAD_KEY_CREATE(key, destr) pthread_key_create(key, destr)
+#define THREAD_KEY_DELETE(key) pthread_key_delete(key)
+#define THREAD_SETSPECIFIC(key, value) pthread_setspecific(key, value)
+#define THREAD_GETSPECIFIC(key) pthread_getspecific(key)
+
+#if USE_UYIELD
+ extern int grn_uyield_count;
+ #define GRN_TEST_YIELD() do {\
+ if (((++grn_uyield_count) & (0x20 - 1)) == 0) {\
+ sched_yield();\
+ if(grn_uyield_count > 0x1000) {\
+ grn_uyield_count = (uint32_t)time(NULL) % 0x1000;\
+ }\
+ }\
+ } while (0)
+
+ #undef assert
+ #define assert(assert_expr) do {\
+ if (!(assert_expr)){\
+ fprintf(stderr, "assertion failed: %s\n", #assert_expr);\
+ abort();\
+ }\
+ GRN_TEST_YIELD();\
+ } while (0)
+
+ #define if(if_cond) \
+ if ((((++grn_uyield_count) & (0x100 - 1)) != 0 || (sched_yield() * 0) == 0) && (if_cond))
+ #define while(while_cond) \
+ while ((((++grn_uyield_count) & (0x100 - 1)) != 0 || (sched_yield() * 0) == 0) && (while_cond))
+
+ #if !defined(_POSIX_PRIORITY_SCHEDULING)
+ #define sched_yield() grn_nanosleep(1000000 * 20)
+ #endif
+#else /* USE_UYIELD */
+ #define GRN_TEST_YIELD() do {} while (0)
+#endif /* USE_UYIELD */
+
+#else /* HAVE_PTHREAD_H */
+
+/* todo */
+typedef int grn_thread_key;
+#define THREAD_KEY_CREATE(key,destr)
+#define THREAD_KEY_DELETE(key)
+#define THREAD_SETSPECIFIC(key)
+#define THREAD_GETSPECIFIC(key,value)
+
+#ifdef WIN32
+typedef uintptr_t grn_thread;
+#define THREAD_CREATE(thread,func,arg) \
+ (((thread)=_beginthreadex(NULL, 0, (func), (arg), 0, NULL)) == NULL)
+#define THREAD_JOIN(thread) \
+ (WaitForSingleObject((thread), INFINITE) == WAIT_FAILED)
+typedef HANDLE grn_mutex;
+#define MUTEX_INIT(m) ((m) = CreateMutex(0, FALSE, NULL))
+#define MUTEX_LOCK(m) WaitForSingleObject((m), INFINITE)
+#define MUTEX_UNLOCK(m) ReleaseMutex(m)
+#define MUTEX_FIN(m) CloseHandle(m)
+typedef CRITICAL_SECTION grn_critical_section;
+#define CRITICAL_SECTION_INIT(cs) InitializeCriticalSection(&(cs))
+#define CRITICAL_SECTION_ENTER(cs) EnterCriticalSection(&(cs))
+#define CRITICAL_SECTION_LEAVE(cs) LeaveCriticalSection(&(cs))
+#define CRITICAL_SECTION_FIN(cs) DeleteCriticalSection(&(cs))
+
+typedef struct
+{
+ int waiters_count_;
+ HANDLE waiters_count_lock_;
+ HANDLE sema_;
+ HANDLE waiters_done_;
+ size_t was_broadcast_;
+} grn_cond;
+
+#define COND_INIT(c) do { \
+ (c).waiters_count_ = 0; \
+ (c).sema_ = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); \
+ MUTEX_INIT((c).waiters_count_lock_); \
+ (c).waiters_done_ = CreateEvent(NULL, FALSE, FALSE, NULL); \
+} while (0)
+
+#define COND_SIGNAL(c) do { \
+ MUTEX_LOCK((c).waiters_count_lock_); \
+ { \
+ int have_waiters = (c).waiters_count_ > 0; \
+ MUTEX_UNLOCK((c).waiters_count_lock_); \
+ if (have_waiters) { \
+ ReleaseSemaphore((c).sema_, 1, 0); \
+ } \
+ } \
+} while (0)
+
+#define COND_BROADCAST(c) do { \
+ MUTEX_LOCK((c).waiters_count_lock_); \
+ { \
+ int have_waiters = (c).waiters_count_ > 0; \
+ if ((c).waiters_count_ > 0) { \
+ (c).was_broadcast_ = 1; \
+ have_waiters = 1; \
+ } \
+ if (have_waiters) { \
+ ReleaseSemaphore((c).sema_, (c).waiters_count_, 0); \
+ MUTEX_UNLOCK((c).waiters_count_lock_); \
+ WaitForSingleObject((c).waiters_done_, INFINITE); \
+ (c).was_broadcast_ = 0; \
+ } \
+ else { \
+ MUTEX_UNLOCK((c).waiters_count_lock_); \
+ } \
+ } \
+} while (0)
+
+#define COND_WAIT(c,m) do { \
+ MUTEX_LOCK((c).waiters_count_lock_); \
+ (c).waiters_count_++; \
+ MUTEX_UNLOCK((c).waiters_count_lock_); \
+ SignalObjectAndWait((m), (c).sema_, INFINITE, FALSE); \
+ MUTEX_LOCK((c).waiters_count_lock_); \
+ (c).waiters_count_--; \
+ { \
+ int last_waiter = (c).was_broadcast_ && (c).waiters_count_ == 0; \
+ MUTEX_UNLOCK((c).waiters_count_lock_); \
+ if (last_waiter) { \
+ SignalObjectAndWait((c).waiters_done_, (m), INFINITE, FALSE); \
+ } \
+ else { \
+ WaitForSingleObject((m), FALSE); \
+ } \
+ } \
+} while (0)
+
+#else /* WIN32 */
+/* todo */
+typedef int grn_cond;
+#define COND_INIT(c) ((c) = 0)
+#define COND_SIGNAL(c)
+#define COND_WAIT(c,m) do { \
+ MUTEX_UNLOCK(m); \
+ grn_nanosleep(1000000); \
+ MUTEX_LOCK(m); \
+} while (0)
+/* todo : must be enhanced! */
+
+#endif /* WIN32 */
+
+#define MUTEX_INIT_SHARED MUTEX_INIT
+#define COND_INIT_SHARED COND_INIT
+
+#define GRN_TEST_YIELD() do {} while (0)
+
+#endif /* HAVE_PTHREAD_H */
+
+/* format string for printf */
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+# define GRN_FMT_INT32D PRId32
+# define GRN_FMT_INT32U PRIu32
+# define GRN_FMT_INT64D PRId64
+# define GRN_FMT_INT64U PRIu64
+# define GRN_FMT_LLD "lld"
+# define GRN_FMT_LLU "llu"
+#else /* HAVE_INTTYPES_H */
+# ifdef WIN32
+# define GRN_FMT_INT32D "I32d"
+# define GRN_FMT_INT32U "I32u"
+# define GRN_FMT_INT64D "I64d"
+# define GRN_FMT_INT64U "I64u"
+# define GRN_FMT_LLD "I64d"
+# define GRN_FMT_LLU "I64u"
+# else /* WIN32 */
+# define GRN_FMT_INT32D "d"
+# define GRN_FMT_INT32U "u"
+# ifdef __x86_64__
+# define GRN_FMT_INT64D "ld"
+# define GRN_FMT_INT64U "lu"
+# else /* __x86_64__ */
+# define GRN_FMT_INT64D "lld"
+# define GRN_FMT_INT64U "llu"
+# endif /* __x86_64__ */
+# define GRN_FMT_LLD "lld"
+# define GRN_FMT_LLU "llu"
+# endif /* WIN32 */
+#endif /* HAVE_INTTYPES_H */
+
+#ifdef __GNUC__
+# if (defined(__i386__) || defined(__x86_64__)) /* ATOMIC ADD */
+/*
+ * GRN_ATOMIC_ADD_EX() performs { r = *p; *p += i; } atomically.
+ */
+# define GRN_ATOMIC_ADD_EX(p, i, r) \
+ __asm__ __volatile__ ("lock xaddl %0, %1" : "=r"(r), "+m"(*p) : "0"(i))
+/*
+ * GRN_BIT_SCAN_REV() finds the most significant 1 bit of `v'. Then, `r' is set
+ * to the index of the found bit. Note that `v' must not be 0.
+ */
+# define GRN_BIT_SCAN_REV(v, r) \
+ __asm__ __volatile__ ("bsrl %1, %%eax; movl %%eax, %0" : "=r"(r) : "r"(v) : "%eax")
+/*
+ * GRN_BIT_SCAN_REV0() is similar to GRN_BIT_SCAN_REV() but if `v' is 0, `r' is
+ * set to 0.
+ */
+# define GRN_BIT_SCAN_REV0(v, r) \
+ __asm__ __volatile__ ("bsrl %1, %%eax; cmovzl %1, %%eax; movl %%eax, %0" : "=r"(r) : "r"(v) : "%eax", "cc")
+# elif (defined(__PPC__) || defined(__ppc__)) /* ATOMIC ADD */
+# define GRN_ATOMIC_ADD_EX(p,i,r) \
+ __asm__ __volatile__ ("\n1:\n\tlwarx %0, 0, %1\n\tadd %0, %0, %2\n\tstwcx. %0, 0, %1\n\tbne- 1b\n\tsub %0, %0, %2" : "=&r" (r) : "r" (p), "r" (i) : "cc", "memory")
+/* todo */
+# define GRN_BIT_SCAN_REV(v,r) for (r = 31; r && !((1 << r) & v); r--)
+# define GRN_BIT_SCAN_REV0(v,r) GRN_BIT_SCAN_REV(v,r)
+# elif (defined(__sun) && defined(__SVR4)) /* ATOMIC ADD */
+# include <atomic.h>
+# define GRN_ATOMIC_ADD_EX(p,i,r) \
+ (r = atomic_add_32_nv(p, i) - i)
+/* todo */
+# define GRN_BIT_SCAN_REV(v,r) for (r = 31; r && !((1 << r) & v); r--)
+# define GRN_BIT_SCAN_REV0(v,r) GRN_BIT_SCAN_REV(v,r)
+# else /* ATOMIC ADD */
+/* todo */
+# define GRN_BIT_SCAN_REV(v,r) for (r = 31; r && !((1 << r) & v); r--)
+# define GRN_BIT_SCAN_REV0(v,r) GRN_BIT_SCAN_REV(v,r)
+# endif /* ATOMIC ADD */
+
+# ifdef __i386__ /* ATOMIC 64BIT SET */
+# define GRN_SET_64BIT(p,v) \
+ __asm__ __volatile__ ("\txchgl %%esi, %%ebx\n1:\n\tmovl (%0), %%eax\n\tmovl 4(%0), %%edx\n\tlock; cmpxchg8b (%0)\n\tjnz 1b\n\txchgl %%ebx, %%esi" : : "D"(p), "S"(*(((uint32_t *)&(v))+0)), "c"(*(((uint32_t *)&(v))+1)) : "ax", "dx", "memory")
+# elif defined(__x86_64__) /* ATOMIC 64BIT SET */
+# define GRN_SET_64BIT(p,v) \
+ (*(p) = (v))
+# elif (defined(__sun) && defined(__SVR4)) /* ATOMIC 64BIT SET */
+/* todo */
+# define GRN_SET_64BIT(p,v) \
+ (void)atomic_swap_64(p, v)
+# endif /* ATOMIC 64BIT SET */
+
+# ifdef HAVE_MKOSTEMP
+# define GRN_MKOSTEMP(template,flags,mode) mkostemp(template,flags)
+# else /* HAVE_MKOSTEMP */
+# define GRN_MKOSTEMP(template,flags,mode) \
+ (mktemp(template), GRN_OPEN((template),flags,mode))
+# endif /* HAVE_MKOSTEMP */
+
+#elif (defined(WIN32) || defined (_WIN64)) /* __GNUC__ */
+
+# define GRN_ATOMIC_ADD_EX(p,i,r) \
+ ((r) = (uint32_t)InterlockedExchangeAdd((int32_t *)(p), (int32_t)(i)))
+# if defined(_WIN64) /* ATOMIC 64BIT SET */
+# define GRN_SET_64BIT(p,v) \
+ (*(p) = (v))
+# else /* ATOMIC 64BIT SET */
+# define GRN_SET_64BIT(p,v) do {\
+ uint32_t v1, v2; \
+ uint64_t *p2= (p); \
+ v1 = *(((uint32_t *)&(v))+0);\
+ v2 = *(((uint32_t *)&(v))+1);\
+ __asm _set_loop: \
+ __asm mov esi, p2 \
+ __asm mov ebx, v1 \
+ __asm mov ecx, v2 \
+ __asm mov eax, dword ptr [esi] \
+ __asm mov edx, dword ptr [esi + 4] \
+ __asm lock cmpxchg8b qword ptr [esi] \
+ __asm jnz _set_loop \
+} while (0)
+/* TODO: use _InterlockedCompareExchange64 or inline asm */
+# endif /* ATOMIC 64BIT SET */
+
+/* todo */
+# define GRN_BIT_SCAN_REV(v,r) for (r = 31; r && !((1 << r) & v); r--)
+# define GRN_BIT_SCAN_REV0(v,r) GRN_BIT_SCAN_REV(v,r)
+
+# define GRN_MKOSTEMP(template,flags,mode) \
+ (mktemp(template), GRN_OPEN((template),((flags)|O_BINARY),mode))
+
+#else /* __GNUC__ */
+
+# if (defined(__sun) && defined(__SVR4)) /* ATOMIC ADD */
+# define __FUNCTION__ ""
+# include <atomic.h>
+# define GRN_ATOMIC_ADD_EX(p,i,r) \
+ (r = atomic_add_32_nv(p, i) - i)
+/* todo */
+# define GRN_BIT_SCAN_REV(v,r) for (r = 31; r && !((1 << r) & v); r--)
+# define GRN_BIT_SCAN_REV0(v,r) GRN_BIT_SCAN_REV(v,r)
+/* todo */
+# define GRN_SET_64BIT(p,v) \
+ (void)atomic_swap_64(p, v)
+# endif /* ATOMIC ADD */
+/* todo */
+# define GRN_BIT_SCAN_REV(v,r) for (r = 31; r && !((1 << r) & v); r--)
+# define GRN_BIT_SCAN_REV0(v,r) GRN_BIT_SCAN_REV(v,r)
+
+# define GRN_MKOSTEMP(template,flags,mode) \
+ (mktemp(template), GRN_OPEN((template),flags,mode))
+
+#endif /* __GNUC__ */
+
+typedef uint8_t byte;
+
+#define GRN_ID_WIDTH 30
+
+#ifdef __GNUC__
+inline static int
+grn_str_greater(const uint8_t *ap, uint32_t as, const uint8_t *bp, uint32_t bs)
+{
+ for (;; ap++, bp++, as--, bs--) {
+ if (!as) { return 0; }
+ if (!bs) { return 1; }
+ if (*ap < *bp) { return 0; }
+ if (*ap > *bp) { return 1; }
+ }
+}
+#else /* __GNUC__ */
+# define grn_str_greater(ap,as,bp,bs)\
+ (((as) > (bs)) ? (memcmp((ap), (bp), (bs)) >= 0) : (memcmp((ap), (bp), (as)) > 0))
+#endif /* __GNUC__ */
+
+#ifdef WORDS_BIGENDIAN
+# define grn_hton(buf,key,size) do {\
+ uint32_t size_ = (uint32_t)size;\
+ uint8_t *buf_ = (uint8_t *)buf;\
+ uint8_t *key_ = (uint8_t *)key;\
+ while (size_--) { *buf_++ = *key_++; }\
+} while (0)
+# define grn_ntohi(buf,key,size) do {\
+ uint32_t size_ = (uint32_t)size;\
+ uint8_t *buf_ = (uint8_t *)buf;\
+ uint8_t *key_ = (uint8_t *)key;\
+ if (size_) { *buf_++ = 0x80 ^ *key_++; size_--; }\
+ while (size_) { *buf_++ = *key_++; size_--; }\
+} while (0)
+#else /* WORDS_BIGENDIAN */
+# define grn_hton(buf,key,size) do {\
+ uint32_t size_ = (uint32_t)size;\
+ uint8_t *buf_ = (uint8_t *)buf;\
+ uint8_t *key_ = (uint8_t *)key + size;\
+ while (size_--) { *buf_++ = *(--key_); }\
+} while (0)
+# define grn_ntohi(buf,key,size) do {\
+ uint32_t size_ = (uint32_t)size;\
+ uint8_t *buf_ = (uint8_t *)buf;\
+ uint8_t *key_ = (uint8_t *)key + size;\
+ while (size_ > 1) { *buf_++ = *(--key_); size_--; }\
+ if (size_) { *buf_ = 0x80 ^ *(--key_); } \
+} while (0)
+#endif /* WORDS_BIGENDIAN */
+#define grn_ntoh(buf,key,size) grn_hton(buf,key,size)
+
+#ifndef __GNUC_PREREQ
+# if defined(__GNUC__) && defined(__GNUC_MINOR__)
+# define __GNUC_PREREQ(maj, min) \
+ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+# else
+# define __GNUC_PREREQ(maj, min) 0
+# endif /* defined(__GNUC__) && defined(__GNUC_MINOR__) */
+#endif /* __GNUC_PREREQ */
+
+#ifdef _MSC_VER
+# define grn_bswap_uint64(in, out) ((out) = _byteswap_uint64(in))
+#else /* _MSC_VER */
+# if defined(__GNUC__) && __GNUC_PREREQ(4, 3)
+# define grn_bswap_uint64(in, out) ((out) = __builtin_bswap64(in))
+# else /* defined(__GNUC__) && __GNUC_PREREQ(4, 3) */
+# define grn_bswap_uint64(in, out) do {\
+ uint64_t temp_ = (in);\
+ (out) = (temp_ << 56) |\
+ ((temp_ & (0xFFULL << 8)) << 40) |\
+ ((temp_ & (0xFFULL << 16)) << 24) |\
+ ((temp_ & (0xFFULL << 24)) << 8) |\
+ ((temp_ & (0xFFULL << 32)) >> 8) |\
+ ((temp_ & (0xFFULL << 40)) >> 24) |\
+ ((temp_ & (0xFFULL << 48)) >> 40) |\
+ (temp_ >> 56);\
+} while (0)
+# endif /* __GNUC__ */
+#endif /* _MSC_VER */
+
+#ifdef WORDS_BIGENDIAN
+# define grn_hton_uint64(in, out) ((out) = (in))
+#else /* WORDS_BIGENDIAN */
+# define grn_hton_uint64(in, out) grn_bswap_uint64(in, out)
+#endif /* WORDS_BIGENDIAN */
+#define grn_ntoh_uint64(in, out) grn_hton_uint64(in, out)
+
+#define grn_gton(keybuf,key,size) do {\
+ const grn_geo_point *point_ = (const grn_geo_point *)key;\
+ uint64_t la_ = (uint32_t)point_->latitude;\
+ uint64_t lo_ = (uint32_t)point_->longitude;\
+ uint64_t result_;\
+ la_ = (la_ | (la_ << 16)) & 0x0000FFFF0000FFFFULL;\
+ la_ = (la_ | (la_ << 8)) & 0x00FF00FF00FF00FFULL;\
+ la_ = (la_ | (la_ << 4)) & 0x0F0F0F0F0F0F0F0FULL;\
+ la_ = (la_ | (la_ << 2)) & 0x3333333333333333ULL;\
+ la_ = (la_ | (la_ << 1)) & 0x5555555555555555ULL;\
+ lo_ = (lo_ | (lo_ << 16)) & 0x0000FFFF0000FFFFULL;\
+ lo_ = (lo_ | (lo_ << 8)) & 0x00FF00FF00FF00FFULL;\
+ lo_ = (lo_ | (lo_ << 4)) & 0x0F0F0F0F0F0F0F0FULL;\
+ lo_ = (lo_ | (lo_ << 2)) & 0x3333333333333333ULL;\
+ lo_ = (lo_ | (lo_ << 1)) & 0x5555555555555555ULL;\
+ result_ = (la_ << 1) | lo_;\
+ grn_hton_uint64(result_, result_);\
+ memcpy(keybuf, &result_, sizeof(result_));\
+} while (0)
+
+#define grn_ntog(keybuf,key,size) do {\
+ grn_geo_point *point_ = (grn_geo_point *)keybuf;\
+ uint64_t key_ = *(const uint64_t *)key;\
+ uint64_t la_, lo_;\
+ grn_ntoh_uint64(key_, key_);\
+ la_ = (key_ >> 1) & 0x5555555555555555ULL;\
+ lo_ = key_ & 0x5555555555555555ULL;\
+ la_ = (la_ | (la_ >> 1)) & 0x3333333333333333ULL;\
+ la_ = (la_ | (la_ >> 2)) & 0x0F0F0F0F0F0F0F0FULL;\
+ la_ = (la_ | (la_ >> 4)) & 0x00FF00FF00FF00FFULL;\
+ la_ = (la_ | (la_ >> 8)) & 0x0000FFFF0000FFFFULL;\
+ la_ = (la_ | (la_ >> 16)) & 0x00000000FFFFFFFFULL;\
+ lo_ = (lo_ | (lo_ >> 1)) & 0x3333333333333333ULL;\
+ lo_ = (lo_ | (lo_ >> 2)) & 0x0F0F0F0F0F0F0F0FULL;\
+ lo_ = (lo_ | (lo_ >> 4)) & 0x00FF00FF00FF00FFULL;\
+ lo_ = (lo_ | (lo_ >> 8)) & 0x0000FFFF0000FFFFULL;\
+ lo_ = (lo_ | (lo_ >> 16)) & 0x00000000FFFFFFFFULL;\
+ point_->latitude = la_;\
+ point_->longitude = lo_;\
+} while (0)
+
+#ifdef HAVE__STRTOUI64
+# define strtoull(nptr,endptr,base) _strtoui64(nptr,endptr,base)
+#endif /* HAVE__STRTOUI64 */
+
+#ifdef USE_FUTEX
+# include <linux/futex.h>
+# include <sys/syscall.h>
+
+# define GRN_FUTEX_WAIT(p) do {\
+ int err;\
+ struct timespec timeout = {1, 0};\
+ while (1) {\
+ if (!(err = syscall(SYS_futex, p, FUTEX_WAIT, *p, &timeout))) {\
+ break;\
+ }\
+ if (err == ETIMEDOUT) {\
+ GRN_LOG(ctx, GRN_LOG_CRIT, "timeout in GRN_FUTEX_WAIT(%p)", p);\
+ break;\
+ } else if (err != EWOULDBLOCK) {\
+ GRN_LOG(ctx, GRN_LOG_CRIT, "error %d in GRN_FUTEX_WAIT(%p)", err);\
+ break;\
+ }\
+ }\
+} while(0)
+
+# define GRN_FUTEX_WAKE(p) syscall(SYS_futex, p, FUTEX_WAKE, 1)
+#else /* USE_FUTEX */
+# define GRN_FUTEX_WAIT(p) grn_nanosleep(1000000)
+# define GRN_FUTEX_WAKE(p)
+#endif /* USE_FUTEX */
+
+#ifndef HOST_NAME_MAX
+# ifdef _POSIX_HOST_NAME_MAX
+# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
+# else /* POSIX_HOST_NAME_MAX */
+# define HOST_NAME_MAX 128
+# endif /* POSIX_HOST_NAME_MAX */
+#endif /* HOST_NAME_MAX */
+
+GRN_API void grn_sleep(uint32_t seconds);
+GRN_API void grn_nanosleep(uint64_t nanoseconds);
+
+#ifndef GROONGA_H
+# include "groonga.h"
+#endif /* GROONGA_H */
+
+#endif /* GROONGA_IN_H */
diff --git a/storage/mroonga/vendor/groonga/lib/hash.c b/storage/mroonga/vendor/groonga/lib/hash.c
new file mode 100644
index 00000000000..79402848f55
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/hash.c
@@ -0,0 +1,3342 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "hash.h"
+#include "output.h"
+#include <string.h>
+#include <limits.h>
+
+#include "store.h"
+#include "normalizer_in.h"
+
+/* grn_tiny_array */
+
+/* Requirements: id != GRN_ID_NIL. */
+inline static int
+grn_tiny_array_get_block_id(grn_id id)
+{
+ int most_significant_one_bit_offset;
+ GRN_BIT_SCAN_REV(id, most_significant_one_bit_offset);
+ return most_significant_one_bit_offset >> GRN_TINY_ARRAY_FACTOR;
+}
+
+/* Requirements: id != GRN_ID_NIL. */
+inline static void *
+grn_tiny_array_get(grn_tiny_array *array, grn_id id) {
+ const int block_id = grn_tiny_array_get_block_id(id);
+ uint8_t * const block = (uint8_t *)array->blocks[block_id];
+ if (block) {
+ const size_t offset = GRN_TINY_ARRAY_GET_OFFSET(block_id);
+ return block + (id - offset) * array->element_size;
+ }
+ return NULL;
+}
+
+/* Requirements: id != GRN_ID_NIL. */
+inline static void *
+grn_tiny_array_put(grn_tiny_array *array, grn_id id) {
+ const int block_id = grn_tiny_array_get_block_id(id);
+ void ** const block = &array->blocks[block_id];
+ const size_t offset = GRN_TINY_ARRAY_GET_OFFSET(block_id);
+ if (!*block) {
+ grn_ctx * const ctx = array->ctx;
+ if (array->flags & GRN_TINY_ARRAY_THREADSAFE) {
+ CRITICAL_SECTION_ENTER(array->lock);
+ }
+ if (!*block) {
+ const size_t block_size =
+ GRN_TINY_ARRAY_GET_BLOCK_SIZE(block_id) * array->element_size;
+ if (array->flags & GRN_TINY_ARRAY_USE_MALLOC) {
+ if (array->flags & GRN_TINY_ARRAY_CLEAR) {
+ *block = GRN_CALLOC(block_size);
+ } else {
+ *block = GRN_MALLOC(block_size);
+ }
+ } else {
+ *block = GRN_CTX_ALLOC(ctx, block_size);
+ }
+ }
+ if (array->flags & GRN_TINY_ARRAY_THREADSAFE) {
+ CRITICAL_SECTION_LEAVE(array->lock);
+ }
+ if (!*block) {
+ return NULL;
+ }
+ }
+ if (id > array->max) {
+ array->max = id;
+ }
+ return (uint8_t *)*block + (id - offset) * array->element_size;
+}
+
+inline static void *
+grn_tiny_array_at_inline(grn_tiny_array *array, grn_id id)
+{
+ return id ? grn_tiny_array_put(array, id) : NULL;
+}
+
+inline static void *
+grn_tiny_array_next(grn_tiny_array *array)
+{
+ return grn_tiny_array_put(array, array->max + 1);
+}
+
+void
+grn_tiny_array_init(grn_ctx *ctx, grn_tiny_array *array,
+ uint16_t element_size, uint16_t flags)
+{
+ array->ctx = ctx;
+ array->max = 0;
+ array->element_size = element_size;
+ array->flags = flags;
+ memset(array->blocks, 0, sizeof(array->blocks));
+ if (flags & GRN_TINY_ARRAY_THREADSAFE) {
+ CRITICAL_SECTION_INIT(array->lock);
+ }
+}
+
+void
+grn_tiny_array_fin(grn_tiny_array *array)
+{
+ int block_id;
+ grn_ctx * const ctx = array->ctx;
+ for (block_id = 0; block_id < GRN_TINY_ARRAY_NUM_BLOCKS; block_id++) {
+ if (array->blocks[block_id]) {
+ if (array->flags & GRN_TINY_ARRAY_USE_MALLOC) {
+ GRN_FREE(array->blocks[block_id]);
+ } else {
+ GRN_CTX_FREE(ctx, array->blocks[block_id]);
+ }
+ array->blocks[block_id] = NULL;
+ }
+ }
+}
+
+void *
+grn_tiny_array_at(grn_tiny_array *array, grn_id id)
+{
+ return grn_tiny_array_at_inline(array, id);
+}
+
+grn_id
+grn_tiny_array_id(grn_tiny_array *array, const void *element_address)
+{
+ const uint8_t * const ptr = (const uint8_t *)element_address;
+ uint32_t block_id, offset = 1;
+ for (block_id = 0; block_id < GRN_TINY_ARRAY_NUM_BLOCKS; block_id++) {
+ const uint32_t block_size = GRN_TINY_ARRAY_GET_BLOCK_SIZE(block_id);
+ const uint8_t * const block = (const uint8_t *)array->blocks[block_id];
+ if (block) {
+ if (block <= ptr && ptr < (block + block_size * array->element_size)) {
+ return offset + ((ptr - block) / array->element_size);
+ }
+ }
+ offset += block_size;
+ }
+ return GRN_ID_NIL;
+}
+
+/* grn_tiny_bitmap */
+
+static void
+grn_tiny_bitmap_init(grn_ctx *ctx, grn_tiny_bitmap *bitmap)
+{
+ bitmap->ctx = ctx;
+ memset(bitmap->blocks, 0, sizeof(bitmap->blocks));
+}
+
+static void
+grn_tiny_bitmap_fin(grn_tiny_bitmap *bitmap)
+{
+ int block_id;
+ grn_ctx * const ctx = bitmap->ctx;
+ for (block_id = 0; block_id < GRN_TINY_ARRAY_NUM_BLOCKS; block_id++) {
+ if (bitmap->blocks[block_id]) {
+ GRN_CTX_FREE(ctx, bitmap->blocks[block_id]);
+ bitmap->blocks[block_id] = NULL;
+ }
+ }
+}
+
+/* Requirements: bit_id != GRN_ID_NIL. */
+inline static uint8_t *
+grn_tiny_bitmap_get_byte(grn_tiny_bitmap *bitmap, grn_id bit_id) {
+ const uint32_t byte_id = (bit_id >> 3) + 1;
+ const int block_id = grn_tiny_array_get_block_id(byte_id);
+ uint8_t * const block = (uint8_t *)bitmap->blocks[block_id];
+ if (block) {
+ const size_t offset = GRN_TINY_ARRAY_GET_OFFSET(block_id);
+ return block + byte_id - offset;
+ }
+ return NULL;
+}
+
+/* Requirements: bit_id != GRN_ID_NIL. */
+inline static uint8_t *
+grn_tiny_bitmap_put_byte(grn_tiny_bitmap *bitmap, grn_id bit_id) {
+ const uint32_t byte_id = (bit_id >> 3) + 1;
+ const int block_id = grn_tiny_array_get_block_id(byte_id);
+ void ** const block = &bitmap->blocks[block_id];
+ const size_t offset = GRN_TINY_ARRAY_GET_OFFSET(block_id);
+ if (!*block) {
+ grn_ctx * const ctx = bitmap->ctx;
+ *block = GRN_CTX_ALLOC(ctx, GRN_TINY_ARRAY_GET_BLOCK_SIZE(block_id));
+ if (!*block) {
+ return NULL;
+ }
+ }
+ return (uint8_t *)*block + byte_id - offset;
+}
+
+/* Requirements: bit_id != GRN_ID_NIL. */
+/* Return value: 1/0 on success, -1 on failure. */
+inline static int
+grn_tiny_bitmap_get(grn_tiny_bitmap *bitmap, grn_id bit_id)
+{
+ uint8_t * const ptr = grn_tiny_bitmap_get_byte(bitmap, bit_id);
+ return ptr ? ((*ptr >> (bit_id & 7)) & 1) : -1;
+}
+
+/* Requirements: bit_id != GRN_ID_NIL. */
+/* Return value: 1/0 on success, -1 on failure. */
+/* Note: A bitmap is extended if needed. */
+inline static int
+grn_tiny_bitmap_put(grn_tiny_bitmap *bitmap, grn_id bit_id)
+{
+ uint8_t * const ptr = grn_tiny_bitmap_put_byte(bitmap, bit_id);
+ return ptr ? ((*ptr >> (bit_id & 7)) & 1) : -1;
+}
+
+/* Requirements: bit_id != GRN_ID_NIL. */
+inline static uint8_t *
+grn_tiny_bitmap_get_and_set(grn_tiny_bitmap *bitmap, grn_id bit_id,
+ grn_bool bit)
+{
+ uint8_t * const ptr = grn_tiny_bitmap_get_byte(bitmap, bit_id);
+ if (ptr) {
+ /* This branch will be removed because the given `bit' is constant. */
+ if (bit) {
+ *ptr |= 1 << (bit_id & 7);
+ } else {
+ *ptr &= ~(1 << (bit_id & 7));
+ }
+ }
+ return ptr;
+}
+
+/* Requirements: bit_id != GRN_ID_NIL. */
+/* Note: A bitmap is extended if needed. */
+inline static uint8_t *
+grn_tiny_bitmap_put_and_set(grn_tiny_bitmap *bitmap, grn_id bit_id,
+ grn_bool bit)
+{
+ uint8_t * const ptr = grn_tiny_bitmap_put_byte(bitmap, bit_id);
+ if (ptr) {
+ /* This branch will be removed because the given `bit' is constant. */
+ if (bit) {
+ *ptr |= 1 << (bit_id & 7);
+ } else {
+ *ptr &= ~(1 << (bit_id & 7));
+ }
+ }
+ return ptr;
+}
+
+/* grn_io_array */
+
+#define GRN_ARRAY_MAX (GRN_ID_MAX - 8)
+
+inline static void *
+grn_io_array_at_inline(grn_ctx *ctx, grn_io *io, uint32_t segment_id,
+ uint32_t offset, int flags)
+{
+ void *ptr;
+ GRN_IO_ARRAY_AT(io, segment_id, offset, &flags, ptr);
+ return ptr;
+}
+
+/*
+ * grn_io_array_bit_at() returns 1/0 on success, -1 on failure.
+ */
+inline static int
+grn_io_array_bit_at(grn_ctx *ctx, grn_io *io,
+ uint32_t segment_id, uint32_t offset)
+{
+ uint8_t * const ptr = (uint8_t *)grn_io_array_at_inline(
+ ctx, io, segment_id, (offset >> 3) + 1, 0);
+ return ptr ? ((*ptr >> (offset & 7)) & 1) : -1;
+}
+
+/*
+ * The following functions, grn_io_array_bit_*(), return a non-NULL pointer on
+ * success, a NULL pointer on failure.
+ */
+inline static void *
+grn_io_array_bit_on(grn_ctx *ctx, grn_io *io,
+ uint32_t segment_id, uint32_t offset)
+{
+ uint8_t * const ptr = (uint8_t *)grn_io_array_at_inline(
+ ctx, io, segment_id, (offset >> 3) + 1, GRN_TABLE_ADD);
+ if (ptr) {
+ *ptr |= 1 << (offset & 7);
+ }
+ return ptr;
+}
+
+inline static void *
+grn_io_array_bit_off(grn_ctx *ctx, grn_io *io,
+ uint32_t segment_id, uint32_t offset)
+{
+ uint8_t * const ptr = (uint8_t *)grn_io_array_at_inline(
+ ctx, io, segment_id, (offset >> 3) + 1, GRN_TABLE_ADD);
+ if (ptr) {
+ *ptr &= ~(1 << (offset & 7));
+ }
+ return ptr;
+}
+
+inline static void *
+grn_io_array_bit_flip(grn_ctx *ctx, grn_io *io,
+ uint32_t segment_id, uint32_t offset)
+{
+ uint8_t * const ptr = (uint8_t *)grn_io_array_at_inline(
+ ctx, io, segment_id, (offset >> 3) + 1, GRN_TABLE_ADD);
+ if (ptr) {
+ *ptr ^= 1 << (offset & 7);
+ }
+ return ptr;
+}
+
+/* grn_table_queue */
+
+static void
+grn_table_queue_lock_init(grn_ctx *ctx, grn_table_queue *queue)
+{
+ MUTEX_INIT_SHARED(queue->mutex);
+ COND_INIT_SHARED(queue->cond);
+}
+
+static void
+grn_table_queue_init(grn_ctx *ctx, grn_table_queue *queue)
+{
+ queue->head = 0;
+ queue->tail = 0;
+ queue->cap = GRN_ARRAY_MAX;
+ queue->unblock_requested = GRN_FALSE;
+ grn_table_queue_lock_init(ctx, queue);
+}
+
+uint32_t
+grn_table_queue_size(grn_table_queue *queue)
+{
+ return (queue->head < queue->tail)
+ ? 2 * queue->cap + queue->head - queue->tail
+ : queue->head - queue->tail;
+}
+
+void
+grn_table_queue_head_increment(grn_table_queue *queue)
+{
+ if (queue->head == 2 * queue->cap) {
+ queue->head = 1;
+ } else {
+ queue->head++;
+ }
+}
+
+void
+grn_table_queue_tail_increment(grn_table_queue *queue)
+{
+ if (queue->tail == 2 * queue->cap) {
+ queue->tail = 1;
+ } else {
+ queue->tail++;
+ }
+}
+
+grn_id
+grn_table_queue_head(grn_table_queue *queue)
+{
+ return queue->head > queue->cap
+ ? queue->head - queue->cap
+ : queue->head;
+}
+
+grn_id
+grn_table_queue_tail(grn_table_queue *queue)
+{
+ return queue->tail > queue->cap
+ ? queue->tail - queue->cap
+ : queue->tail;
+}
+
+/* grn_array */
+
+#define GRN_ARRAY_SEGMENT_SIZE 0x400000
+
+/* Header of grn_io-based grn_array. */
+struct grn_array_header {
+ uint32_t flags;
+ uint32_t curr_rec;
+ uint32_t value_size;
+ uint32_t n_entries;
+ uint32_t n_garbages;
+ grn_id garbages;
+ uint32_t lock;
+ uint32_t reserved[9];
+ grn_table_queue queue;
+};
+
+/*
+ * A grn_io-based grn_array consists of the following 2 segments.
+ * GRN_ARRAY_VALUE_SEGMENT: stores values.
+ * GRN_ARRAY_BITMAP_SEGMENT: stores whether entries are valid or not.
+ */
+enum {
+ GRN_ARRAY_VALUE_SEGMENT = 0,
+ GRN_ARRAY_BITMAP_SEGMENT = 1
+};
+
+inline static grn_bool
+grn_array_is_io_array(grn_array *array)
+{
+ return array->io != NULL;
+}
+
+inline static void *
+grn_array_io_entry_at(grn_ctx *ctx, grn_array *array, grn_id id, int flags)
+{
+ return grn_io_array_at_inline(ctx, array->io, GRN_ARRAY_VALUE_SEGMENT, id, flags);
+}
+
+inline static void *
+grn_array_entry_at(grn_ctx *ctx, grn_array *array, grn_id id, int flags)
+{
+ if (grn_array_is_io_array(array)) {
+ return grn_array_io_entry_at(ctx, array, id, flags);
+ } else {
+ return grn_tiny_array_at_inline(&array->array, id);
+ }
+}
+
+/* grn_array_bitmap_at() returns 1/0 on success, -1 on failure. */
+inline static int
+grn_array_bitmap_at(grn_ctx *ctx, grn_array *array, grn_id id)
+{
+ if (grn_array_is_io_array(array)) {
+ return grn_io_array_bit_at(ctx, array->io, GRN_ARRAY_BITMAP_SEGMENT, id);
+ } else {
+ return grn_tiny_bitmap_put(&array->bitmap, id);
+ }
+}
+
+static grn_rc
+grn_array_init_tiny_array(grn_ctx *ctx, grn_array *array, const char *path,
+ uint32_t value_size, uint32_t flags)
+{
+ if (path) {
+ ERR(GRN_INVALID_ARGUMENT, "failed to create tiny array");
+ return ctx->rc;
+ }
+ array->obj.header.flags = flags;
+ array->ctx = ctx;
+ array->value_size = value_size;
+ array->n_keys = 0;
+ array->keys = NULL;
+ array->n_garbages = &array->n_garbages_buf;
+ array->n_entries = &array->n_entries_buf;
+ array->n_garbages_buf = 0;
+ array->n_entries_buf = 0;
+ array->io = NULL;
+ array->garbages = GRN_ID_NIL;
+ grn_tiny_array_init(ctx, &array->array, value_size, GRN_TINY_ARRAY_CLEAR);
+ grn_tiny_bitmap_init(ctx, &array->bitmap);
+ return GRN_SUCCESS;
+}
+
+static grn_io *
+grn_array_create_io_array(grn_ctx *ctx, const char *path, uint32_t value_size)
+{
+ uint32_t w_of_element = 0;
+ grn_io_array_spec array_spec[2];
+
+ while ((1U << w_of_element) < value_size) {
+ w_of_element++;
+ }
+
+ array_spec[GRN_ARRAY_VALUE_SEGMENT].w_of_element = w_of_element;
+ array_spec[GRN_ARRAY_VALUE_SEGMENT].max_n_segments =
+ 1U << (30 - (22 - w_of_element));
+ array_spec[GRN_ARRAY_BITMAP_SEGMENT].w_of_element = 0;
+ array_spec[GRN_ARRAY_BITMAP_SEGMENT].max_n_segments = 1U << (30 - (22 + 3));
+ return grn_io_create_with_array(ctx, path, sizeof(struct grn_array_header),
+ GRN_ARRAY_SEGMENT_SIZE, grn_io_auto,
+ 2, array_spec);
+}
+
+static grn_rc
+grn_array_init_io_array(grn_ctx *ctx, grn_array *array, const char *path,
+ uint32_t value_size, uint32_t flags)
+{
+ grn_io *io;
+ struct grn_array_header *header;
+
+ io = grn_array_create_io_array(ctx, path, value_size);
+ if (!io) {
+ return ctx->rc;
+ }
+ grn_io_set_type(io, GRN_TABLE_NO_KEY);
+
+ header = grn_io_header(io);
+ header->flags = flags;
+ header->curr_rec = 0;
+ header->lock = 0;
+ header->value_size = value_size;
+ header->n_entries = 0;
+ header->n_garbages = 0;
+ header->garbages = GRN_ID_NIL;
+ grn_table_queue_init(ctx, &header->queue);
+ array->obj.header.flags = flags;
+ array->ctx = ctx;
+ array->value_size = value_size;
+ array->n_keys = 0;
+ array->keys = NULL;
+ array->n_garbages = &header->n_garbages;
+ array->n_entries = &header->n_entries;
+ array->io = io;
+ array->header = header;
+ array->lock = &header->lock;
+ return GRN_SUCCESS;
+}
+
+void
+grn_array_queue_lock_clear(grn_ctx *ctx, grn_array *array)
+{
+ struct grn_array_header *header;
+ header = grn_io_header(array->io);
+ grn_table_queue_lock_init(ctx, &header->queue);
+}
+
+grn_table_queue *
+grn_array_queue(grn_ctx *ctx, grn_array *array)
+{
+ if (grn_array_is_io_array(array)) {
+ struct grn_array_header *header;
+ header = grn_io_header(array->io);
+ return &header->queue;
+ } else {
+ return NULL;
+ }
+}
+
+static grn_rc
+grn_array_init(grn_ctx *ctx, grn_array *array,
+ const char *path, uint32_t value_size, uint32_t flags)
+{
+ if (flags & GRN_ARRAY_TINY) {
+ return grn_array_init_tiny_array(ctx, array, path, value_size, flags);
+ } else {
+ return grn_array_init_io_array(ctx, array, path, value_size, flags);
+ }
+}
+
+grn_array *
+grn_array_create(grn_ctx *ctx, const char *path, uint32_t value_size, uint32_t flags)
+{
+ if (ctx) {
+ grn_array * const array = (grn_array *)GRN_MALLOC(sizeof(grn_array));
+ if (array) {
+ GRN_DB_OBJ_SET_TYPE(array, GRN_TABLE_NO_KEY);
+ if (!grn_array_init(ctx, array, path, value_size, flags)) {
+ return array;
+ }
+ GRN_FREE(array);
+ }
+ }
+ return NULL;
+}
+
+grn_array *
+grn_array_open(grn_ctx *ctx, const char *path)
+{
+ if (ctx) {
+ grn_io * const io = grn_io_open(ctx, path, grn_io_auto);
+ if (io) {
+ struct grn_array_header * const header = grn_io_header(io);
+ if (grn_io_get_type(io) == GRN_TABLE_NO_KEY) {
+ grn_array * const array = (grn_array *)GRN_MALLOC(sizeof(grn_array));
+ if (array) {
+ if (!(header->flags & GRN_ARRAY_TINY)) {
+ GRN_DB_OBJ_SET_TYPE(array, GRN_TABLE_NO_KEY);
+ array->obj.header.flags = header->flags;
+ array->ctx = ctx;
+ array->value_size = header->value_size;
+ array->n_keys = 0;
+ array->keys = NULL;
+ array->n_garbages = &header->n_garbages;
+ array->n_entries = &header->n_entries;
+ array->io = io;
+ array->header = header;
+ array->lock = &header->lock;
+ return array;
+ } else {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "invalid array flags. (%x)", header->flags);
+ }
+ GRN_FREE(array);
+ }
+ } else {
+ ERR(GRN_INVALID_FORMAT, "file type unmatch");
+ }
+ grn_io_close(ctx, io);
+ }
+ }
+ return NULL;
+}
+
+grn_rc
+grn_array_close(grn_ctx *ctx, grn_array *array)
+{
+ grn_rc rc = GRN_SUCCESS;
+ if (!ctx || !array) { return GRN_INVALID_ARGUMENT; }
+ if (array->keys) { GRN_FREE(array->keys); }
+ if (grn_array_is_io_array(array)) {
+ rc = grn_io_close(ctx, array->io);
+ } else {
+ GRN_ASSERT(ctx == array->ctx);
+ grn_tiny_array_fin(&array->array);
+ grn_tiny_bitmap_fin(&array->bitmap);
+ }
+ GRN_FREE(array);
+ return rc;
+}
+
+grn_rc
+grn_array_remove(grn_ctx *ctx, const char *path)
+{
+ if (!ctx || !path) { return GRN_INVALID_ARGUMENT; }
+ return grn_io_remove(ctx, path);
+}
+
+grn_rc
+grn_array_truncate(grn_ctx *ctx, grn_array *array)
+{
+ grn_rc rc = GRN_SUCCESS;
+ char *path = NULL;
+ uint32_t value_size, flags;
+
+ if (!ctx || !array) { return GRN_INVALID_ARGUMENT; }
+ if (grn_array_is_io_array(array)) {
+ const char * const io_path = grn_io_path(array->io);
+ if (io_path && *io_path) {
+ path = GRN_STRDUP(io_path);
+ if (!path) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "cannot duplicate path: <%s>", io_path);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ }
+ value_size = array->value_size;
+ flags = array->obj.header.flags;
+
+ if (grn_array_is_io_array(array)) {
+ rc = grn_io_close(ctx, array->io);
+ if (!rc) {
+ array->io = NULL;
+ if (path) {
+ rc = grn_io_remove(ctx, path);
+ }
+ }
+ }
+ if (!rc) {
+ rc = grn_array_init(ctx, array, path, value_size, flags);
+ }
+ if (path) { GRN_FREE(path); }
+ return rc;
+}
+
+inline static grn_id
+grn_array_get_max_id(grn_array *array)
+{
+ return grn_array_is_io_array(array) ? array->header->curr_rec : array->array.max;
+}
+
+inline static void *
+grn_array_get_value_inline(grn_ctx *ctx, grn_array *array, grn_id id)
+{
+ if (!ctx || !array) {
+ return NULL;
+ }
+ if (*array->n_garbages) {
+ /*
+ * grn_array_bitmap_at() is a time-consuming function, so it is called only
+ * when there are garbages in the array.
+ */
+ if (grn_array_bitmap_at(ctx, array, id) != 1) {
+ return NULL;
+ }
+ } else if (id == 0 || id > grn_array_get_max_id(array)) {
+ return NULL;
+ }
+ return grn_array_entry_at(ctx, array, id, 0);
+}
+
+int
+grn_array_get_value(grn_ctx *ctx, grn_array *array, grn_id id, void *valuebuf)
+{
+ void * const value = grn_array_get_value_inline(ctx, array, id);
+ if (value) {
+ if (valuebuf) {
+ memcpy(valuebuf, value, array->value_size);
+ }
+ return array->value_size;
+ }
+ return 0;
+}
+
+void *
+_grn_array_get_value(grn_ctx *ctx, grn_array *array, grn_id id)
+{
+ return grn_array_get_value_inline(ctx, array, id);
+}
+
+inline static grn_rc
+grn_array_set_value_inline(grn_ctx *ctx, grn_array *array, grn_id id,
+ const void *value, int flags)
+{
+ void * const entry = grn_array_entry_at(ctx, array, id, 0);
+ if (!entry) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+
+ switch ((flags & GRN_OBJ_SET_MASK)) {
+ case GRN_OBJ_SET :
+ memcpy(entry, value, array->value_size);
+ return GRN_SUCCESS;
+ case GRN_OBJ_INCR :
+ switch (array->value_size) {
+ case sizeof(int32_t) :
+ *((int32_t *)entry) += *((int32_t *)value);
+ return GRN_SUCCESS;
+ case sizeof(int64_t) :
+ *((int64_t *)entry) += *((int64_t *)value);
+ return GRN_SUCCESS;
+ default :
+ return GRN_INVALID_ARGUMENT;
+ }
+ break;
+ case GRN_OBJ_DECR :
+ switch (array->value_size) {
+ case sizeof(int32_t) :
+ *((int32_t *)entry) -= *((int32_t *)value);
+ return GRN_SUCCESS;
+ case sizeof(int64_t) :
+ *((int64_t *)entry) -= *((int64_t *)value);
+ return GRN_SUCCESS;
+ default :
+ return GRN_INVALID_ARGUMENT;
+ }
+ break;
+ default :
+ /* todo : support other types. */
+ return GRN_INVALID_ARGUMENT;
+ }
+}
+
+grn_rc
+grn_array_set_value(grn_ctx *ctx, grn_array *array, grn_id id,
+ const void *value, int flags)
+{
+ if (!ctx || !array || !value) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (*array->n_garbages) {
+ /*
+ * grn_array_bitmap_at() is a time-consuming function, so it is called only
+ * when there are garbages in the array.
+ */
+ if (grn_array_bitmap_at(ctx, array, id) != 1) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ } else if (id == 0 || id > grn_array_get_max_id(array)) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ return grn_array_set_value_inline(ctx, array, id, value, flags);
+}
+
+grn_rc
+grn_array_delete_by_id(grn_ctx *ctx, grn_array *array, grn_id id,
+ grn_table_delete_optarg *optarg)
+{
+ if (!ctx || !array) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (grn_array_bitmap_at(ctx, array, id) != 1) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ {
+ grn_rc rc = GRN_SUCCESS;
+ /* lock */
+ if (grn_array_is_io_array(array)) {
+ if (array->value_size >= sizeof(grn_id)) {
+ struct grn_array_header * const header = array->header;
+ void * const entry = grn_array_io_entry_at(ctx, array, id, 0);
+ if (!entry) {
+ rc = GRN_INVALID_ARGUMENT;
+ } else {
+ *((grn_id *)entry) = header->garbages;
+ header->garbages = id;
+ }
+ }
+ if (!rc) {
+ (*array->n_entries)--;
+ (*array->n_garbages)++;
+ /*
+ * The following grn_io_array_bit_off() fails iff a problem has
+ * occurred after the above grn_array_bitmap_at(). That is to say,
+ * an unexpected case.
+ */
+ grn_io_array_bit_off(ctx, array->io, GRN_ARRAY_BITMAP_SEGMENT, id);
+ }
+ } else {
+ if (array->value_size >= sizeof(grn_id)) {
+ void * const entry = grn_tiny_array_get(&array->array, id);
+ if (!entry) {
+ rc = GRN_INVALID_ARGUMENT;
+ } else {
+ *((grn_id *)entry) = array->garbages;
+ array->garbages = id;
+ }
+ }
+ if (!rc) {
+ (*array->n_entries)--;
+ (*array->n_garbages)++;
+ /*
+ * The following grn_io_array_bit_off() fails iff a problem has
+ * occurred after the above grn_array_bitmap_at(). That is to say,
+ * an unexpected case.
+ */
+ grn_tiny_bitmap_get_and_set(&array->bitmap, id, 0);
+ }
+ }
+ /* unlock */
+ return rc;
+ }
+}
+
+grn_id
+grn_array_at(grn_ctx *ctx, grn_array *array, grn_id id)
+{
+ if (*array->n_garbages) {
+ /*
+ * grn_array_bitmap_at() is a time-consuming function, so it is called only
+ * when there are garbages in the array.
+ */
+ if (grn_array_bitmap_at(ctx, array, id) != 1) {
+ return GRN_ID_NIL;
+ }
+ } else if (id > grn_array_get_max_id(array)) {
+ return GRN_ID_NIL;
+ }
+ return id;
+}
+
+grn_rc
+grn_array_copy_sort_key(grn_ctx *ctx, grn_array *array,
+ grn_table_sort_key *keys, int n_keys)
+{
+ array->keys = (grn_table_sort_key *)GRN_MALLOCN(grn_table_sort_key, n_keys);
+ if (!array->keys) {
+ return ctx->rc;
+ }
+ memcpy(array->keys, keys, sizeof(grn_table_sort_key) * n_keys);
+ array->n_keys = n_keys;
+ return GRN_SUCCESS;
+}
+
+void
+grn_array_cursor_close(grn_ctx *ctx, grn_array_cursor *cursor)
+{
+ GRN_ASSERT(cursor->ctx == ctx);
+ GRN_FREE(cursor);
+}
+
+grn_array_cursor *
+grn_array_cursor_open(grn_ctx *ctx, grn_array *array, grn_id min, grn_id max,
+ int offset, int limit, int flags)
+{
+ grn_array_cursor *cursor;
+ if (!array || !ctx) { return NULL; }
+
+ cursor = (grn_array_cursor *)GRN_MALLOCN(grn_array_cursor, 1);
+ if (!cursor) { return NULL; }
+
+ GRN_DB_OBJ_SET_TYPE(cursor, GRN_CURSOR_TABLE_NO_KEY);
+ cursor->array = array;
+ cursor->ctx = ctx;
+ cursor->obj.header.flags = flags;
+ cursor->obj.header.domain = GRN_ID_NIL;
+
+ if (flags & GRN_CURSOR_DESCENDING) {
+ cursor->dir = -1;
+ if (max) {
+ cursor->curr_rec = max;
+ if (!(flags & GRN_CURSOR_LT)) { cursor->curr_rec++; }
+ } else {
+ cursor->curr_rec = grn_array_get_max_id(array) + 1;
+ }
+ if (min) {
+ cursor->tail = min;
+ if ((flags & GRN_CURSOR_GT)) { cursor->tail++; }
+ } else {
+ cursor->tail = GRN_ID_NIL + 1;
+ }
+ if (cursor->curr_rec < cursor->tail) { cursor->tail = cursor->curr_rec; }
+ } else {
+ cursor->dir = 1;
+ if (min) {
+ cursor->curr_rec = min;
+ if (!(flags & GRN_CURSOR_GT)) { cursor->curr_rec--; }
+ } else {
+ cursor->curr_rec = GRN_ID_NIL;
+ }
+ if (max) {
+ cursor->tail = max;
+ if ((flags & GRN_CURSOR_LT)) { cursor->tail--; }
+ } else {
+ cursor->tail = grn_array_get_max_id(array);
+ }
+ if (cursor->tail < cursor->curr_rec) { cursor->tail = cursor->curr_rec; }
+ }
+
+ if (*array->n_garbages) {
+ while (offset && cursor->curr_rec != cursor->tail) {
+ cursor->curr_rec += cursor->dir;
+ if (grn_array_bitmap_at(ctx, cursor->array, cursor->curr_rec) == 1) {
+ offset--;
+ }
+ }
+ } else {
+ cursor->curr_rec += cursor->dir * offset;
+ }
+ cursor->rest = (limit < 0) ? GRN_ARRAY_MAX : limit;
+ return cursor;
+}
+
+grn_id
+grn_array_cursor_next(grn_ctx *ctx, grn_array_cursor *cursor)
+{
+ if (cursor && cursor->rest) {
+ while (cursor->curr_rec != cursor->tail) {
+ cursor->curr_rec += cursor->dir;
+ if (*cursor->array->n_garbages) {
+ if (grn_array_bitmap_at(ctx, cursor->array, cursor->curr_rec) != 1) {
+ continue;
+ }
+ }
+ cursor->rest--;
+ return cursor->curr_rec;
+ }
+ }
+ return GRN_ID_NIL;
+}
+
+grn_id
+grn_array_next(grn_ctx *ctx, grn_array *array, grn_id id)
+{
+ const grn_id max_id = grn_array_get_max_id(array);
+ while (++id <= max_id) {
+ if (!*array->n_garbages ||
+ grn_array_bitmap_at(ctx, array, id) == 1) {
+ return id;
+ }
+ }
+ return GRN_ID_NIL;
+}
+
+int
+grn_array_cursor_get_value(grn_ctx *ctx, grn_array_cursor *cursor, void **value)
+{
+ if (cursor && value) {
+ void * const entry = grn_array_entry_at(ctx, cursor->array, cursor->curr_rec, 0);
+ if (entry) {
+ *value = entry;
+ return cursor->array->value_size;
+ }
+ }
+ return 0;
+}
+
+grn_rc
+grn_array_cursor_set_value(grn_ctx *ctx, grn_array_cursor *cursor,
+ const void *value, int flags)
+{
+ return grn_array_set_value_inline(ctx, cursor->array, cursor->curr_rec,
+ value, flags);
+}
+
+grn_rc
+grn_array_cursor_delete(grn_ctx *ctx, grn_array_cursor *cursor,
+ grn_table_delete_optarg *optarg)
+{
+ return grn_array_delete_by_id(ctx, cursor->array, cursor->curr_rec, optarg);
+}
+
+inline static grn_id
+grn_array_add_to_tiny_array(grn_ctx *ctx, grn_array *array, void **value)
+{
+ grn_id id = array->garbages;
+ void *entry;
+ if (id) {
+ /* These operations fail iff the array is broken. */
+ entry = grn_tiny_array_get(&array->array, id);
+ if (!entry) {
+ return GRN_ID_NIL;
+ }
+ array->garbages = *(grn_id *)entry;
+ memset(entry, 0, array->value_size);
+ (*array->n_garbages)--;
+ if (!grn_tiny_bitmap_get_and_set(&array->bitmap, id, 1)) {
+ /* Actually, it is difficult to recover from this error. */
+ *(grn_id *)entry = array->garbages;
+ array->garbages = id;
+ (*array->n_garbages)++;
+ return GRN_ID_NIL;
+ }
+ } else {
+ id = array->array.max + 1;
+ if (!grn_tiny_bitmap_put_and_set(&array->bitmap, id, 1)) {
+ return GRN_ID_NIL;
+ }
+ entry = grn_tiny_array_put(&array->array, id);
+ if (!entry) {
+ grn_tiny_bitmap_get_and_set(&array->bitmap, id, 0);
+ return GRN_ID_NIL;
+ }
+ array->array.max = id;
+ }
+ (*array->n_entries)++;
+ if (value) { *value = entry; }
+ return id;
+}
+
+inline static grn_id
+grn_array_add_to_io_array(grn_ctx *ctx, grn_array *array, void **value)
+{
+ struct grn_array_header * const header = array->header;
+ grn_id id = header->garbages;
+ void *entry;
+ if (id) {
+ /* These operations fail iff the array is broken. */
+ entry = grn_array_io_entry_at(ctx, array, id, GRN_TABLE_ADD);
+ if (!entry) {
+ return GRN_ID_NIL;
+ }
+ header->garbages = *(grn_id *)entry;
+ memset(entry, 0, header->value_size);
+ (*array->n_garbages)--;
+ if (!grn_io_array_bit_on(ctx, array->io, GRN_ARRAY_BITMAP_SEGMENT, id)) {
+ /* Actually, it is difficult to recover from this error. */
+ *(grn_id *)entry = array->garbages;
+ array->garbages = id;
+ (*array->n_garbages)++;
+ return GRN_ID_NIL;
+ }
+ } else {
+ if (header->curr_rec >= GRN_ARRAY_MAX) { return GRN_ID_NIL; }
+ id = header->curr_rec + 1;
+ if (!grn_io_array_bit_on(ctx, array->io, GRN_ARRAY_BITMAP_SEGMENT, id)) {
+ return GRN_ID_NIL;
+ }
+ entry = grn_array_io_entry_at(ctx, array, id, GRN_TABLE_ADD);
+ if (!entry) {
+ grn_io_array_bit_off(ctx, array->io, GRN_ARRAY_BITMAP_SEGMENT, id);
+ return GRN_ID_NIL;
+ }
+ header->curr_rec = id;
+ }
+ (*array->n_entries)++;
+ if (value) { *value = entry; }
+ return id;
+}
+
+void
+grn_array_clear_curr_rec(grn_ctx *ctx, grn_array *array)
+{
+ struct grn_array_header * const header = array->header;
+ header->curr_rec = GRN_ID_NIL;
+}
+
+grn_id
+grn_array_add(grn_ctx *ctx, grn_array *array, void **value)
+{
+ if (ctx && array) {
+ if (grn_array_is_io_array(array)) {
+ return grn_array_add_to_io_array(ctx, array, value);
+ } else {
+ return grn_array_add_to_tiny_array(ctx, array, value);
+ }
+ }
+ return GRN_ID_NIL;
+}
+
+grn_id
+grn_array_push(grn_ctx *ctx, grn_array *array,
+ void (*func)(grn_ctx *, grn_array *, grn_id, void *),
+ void *func_arg)
+{
+ grn_id id = GRN_ID_NIL;
+ grn_table_queue *queue = grn_array_queue(ctx, array);
+ if (queue) {
+ MUTEX_LOCK(queue->mutex);
+ if (grn_table_queue_head(queue) == queue->cap) {
+ grn_array_clear_curr_rec(ctx, array);
+ }
+ id = grn_array_add(ctx, array, NULL);
+ if (func) {
+ func(ctx, array, id, func_arg);
+ }
+ if (grn_table_queue_size(queue) == queue->cap) {
+ grn_table_queue_tail_increment(queue);
+ }
+ grn_table_queue_head_increment(queue);
+ COND_SIGNAL(queue->cond);
+ MUTEX_UNLOCK(queue->mutex);
+ } else {
+ ERR(GRN_OPERATION_NOT_SUPPORTED, "only persistent arrays support push");
+ }
+ return id;
+}
+
+grn_id
+grn_array_pull(grn_ctx *ctx, grn_array *array, grn_bool blockp,
+ void (*func)(grn_ctx *, grn_array *, grn_id, void *),
+ void *func_arg)
+{
+ grn_id id = GRN_ID_NIL;
+ grn_table_queue *queue = grn_array_queue(ctx, array);
+ if (queue) {
+ MUTEX_LOCK(queue->mutex);
+ queue->unblock_requested = GRN_FALSE;
+ while (grn_table_queue_size(queue) == 0) {
+ if (!blockp || queue->unblock_requested) {
+ MUTEX_UNLOCK(queue->mutex);
+ GRN_OUTPUT_BOOL(0);
+ return id;
+ }
+ COND_WAIT(queue->cond, queue->mutex);
+ }
+ grn_table_queue_tail_increment(queue);
+ id = grn_table_queue_tail(queue);
+ if (func) {
+ func(ctx, array, id, func_arg);
+ }
+ MUTEX_UNLOCK(queue->mutex);
+ } else {
+ ERR(GRN_OPERATION_NOT_SUPPORTED, "only persistent arrays support pull");
+ }
+ return id;
+}
+
+void
+grn_array_unblock(grn_ctx *ctx, grn_array *array)
+{
+ grn_table_queue *queue = grn_array_queue(ctx, array);
+ if (!queue) {
+ return;
+ }
+
+ queue->unblock_requested = GRN_TRUE;
+ COND_BROADCAST(queue->cond);
+}
+
+/* grn_hash : hash table */
+
+#define GRN_HASH_MAX_SEGMENT 0x400
+#define GRN_HASH_HEADER_SIZE 0x9000
+#define GRN_HASH_SEGMENT_SIZE 0x400000
+#define W_OF_KEY_IN_A_SEGMENT 22
+#define IDX_MASK_IN_A_SEGMENT 0xfffff
+
+typedef struct {
+ uint8_t key[4];
+ uint8_t value[1];
+} grn_plain_hash_entry;
+
+typedef struct {
+ uint32_t hash_value;
+ uint8_t key_and_value[1];
+} grn_rich_hash_entry;
+
+typedef struct {
+ uint32_t hash_value;
+ uint16_t flag;
+ uint16_t key_size;
+ union {
+ uint8_t buf[sizeof(uint32_t)];
+ uint32_t offset;
+ } key;
+ uint8_t value[1];
+} grn_io_hash_entry;
+
+typedef struct {
+ uint32_t hash_value;
+ uint16_t flag;
+ uint16_t key_size;
+ union {
+ uint8_t buf[sizeof(void *)];
+ void *ptr;
+ } key;
+ uint8_t value[1];
+} grn_tiny_hash_entry;
+
+/*
+ * hash_value is valid even if the entry is grn_plain_hash_entry. In this case,
+ * its hash_value equals its key.
+ * flag, key_size and key.buf are valid if the entry has a variable length key.
+ */
+typedef struct {
+ uint32_t hash_value;
+ uint16_t flag;
+ uint16_t key_size;
+} grn_hash_entry_header;
+
+typedef union {
+ uint32_t hash_value;
+ grn_hash_entry_header header;
+ grn_plain_hash_entry plain_entry;
+ grn_rich_hash_entry rich_entry;
+ grn_io_hash_entry io_entry;
+ grn_tiny_hash_entry tiny_entry;
+} grn_hash_entry;
+
+typedef struct {
+ uint32_t key;
+ uint8_t dummy[1];
+} entry;
+
+typedef struct {
+ uint32_t key;
+ uint16_t flag;
+ uint16_t size;
+ uint32_t str;
+ uint8_t dummy[1];
+} entry_str;
+
+typedef struct {
+ uint32_t key;
+ uint16_t flag;
+ uint16_t size;
+ char *str;
+ uint8_t dummy[1];
+} entry_astr;
+
+#define LOGICAL_MAX_SEGMENT ((GRN_HASH_MAX_SEGMENT) * 4)
+
+enum {
+ GRN_HASH_KEY_SEGMENT = 0,
+ GRN_HASH_ENTRY_SEGMENT = 1,
+ GRN_HASH_INDEX_SEGMENT = 2,
+ GRN_HASH_BITMAP_SEGMENT = 3
+};
+
+inline static grn_bool
+grn_hash_is_io_hash(grn_hash *hash)
+{
+ return hash->io != NULL;
+}
+
+inline static void *
+grn_io_hash_entry_at(grn_ctx *ctx, grn_hash *hash, grn_id id, int flags)
+{
+ return grn_io_array_at_inline(ctx, hash->io, GRN_HASH_ENTRY_SEGMENT, id, flags);
+}
+
+/* todo : error handling */
+inline static void *
+grn_hash_entry_at(grn_ctx *ctx, grn_hash *hash, grn_id id, int flags)
+{
+ if (grn_hash_is_io_hash(hash)) {
+ return grn_io_hash_entry_at(ctx, hash, id, flags);
+ } else {
+ return grn_tiny_array_at_inline(&hash->a, id);
+ }
+}
+
+inline static grn_bool
+grn_hash_bitmap_at(grn_ctx *ctx, grn_hash *hash, grn_id id)
+{
+ if (grn_hash_is_io_hash(hash)) {
+ return grn_io_array_bit_at(ctx, hash->io, GRN_HASH_BITMAP_SEGMENT, id) == 1;
+ } else {
+ return grn_tiny_bitmap_put(&hash->bitmap, id) == 1;
+ }
+}
+
+inline static grn_id *
+grn_io_hash_idx_at(grn_ctx *ctx, grn_hash *hash, grn_id id)
+{
+ return grn_io_array_at_inline(ctx, hash->io, GRN_HASH_INDEX_SEGMENT,
+ id, GRN_TABLE_ADD);
+}
+
+inline static grn_id *
+grn_hash_idx_at(grn_ctx *ctx, grn_hash *hash, grn_id id)
+{
+ if (grn_hash_is_io_hash(hash)) {
+ id = (id & *hash->max_offset) + hash->header->idx_offset;
+ return grn_io_hash_idx_at(ctx, hash, id);
+ } else {
+ return hash->index + (id & *hash->max_offset);
+ }
+}
+
+inline static void *
+grn_io_hash_key_at(grn_ctx *ctx, grn_hash *hash, uint32_t pos)
+{
+ return grn_io_array_at_inline(ctx, hash->io, GRN_HASH_KEY_SEGMENT,
+ pos, GRN_TABLE_ADD);
+}
+
+#define HASH_IMMEDIATE 1
+
+#define MAX_INDEX_SIZE ((GRN_HASH_MAX_SEGMENT * (IDX_MASK_IN_A_SEGMENT + 1)) >> 1)
+
+inline static uint16_t
+grn_hash_entry_get_key_size(grn_hash *hash, grn_hash_entry *entry)
+{
+ if (hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ return entry->header.key_size;
+ } else {
+ return hash->key_size;
+ }
+}
+
+inline static char *
+grn_hash_entry_get_key(grn_ctx *ctx, grn_hash *hash, grn_hash_entry *entry)
+{
+ if (hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ if (grn_hash_is_io_hash(hash)) {
+ if (entry->io_entry.flag & HASH_IMMEDIATE) {
+ return (char *)entry->io_entry.key.buf;
+ } else {
+ return (char *)grn_io_hash_key_at(ctx, hash, entry->io_entry.key.offset);
+ }
+ } else {
+ if (entry->tiny_entry.flag & HASH_IMMEDIATE) {
+ return (char *)entry->tiny_entry.key.buf;
+ } else {
+ return entry->tiny_entry.key.ptr;
+ }
+ }
+ } else {
+ if (hash->key_size == sizeof(uint32_t)) {
+ return (char *)entry->plain_entry.key;
+ } else {
+ return (char *)entry->rich_entry.key_and_value;
+ }
+ }
+}
+
+inline static void *
+grn_hash_entry_get_value(grn_hash *hash, grn_hash_entry *entry)
+{
+ if (hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ if (grn_hash_is_io_hash(hash)) {
+ return entry->io_entry.value;
+ } else {
+ return entry->tiny_entry.value;
+ }
+ } else {
+ if (hash->key_size == sizeof(uint32_t)) {
+ return entry->plain_entry.value;
+ } else {
+ return entry->rich_entry.key_and_value + hash->key_size;
+ }
+ }
+}
+
+inline static grn_rc
+grn_io_hash_entry_put_key(grn_ctx *ctx, grn_hash *hash,
+ grn_io_hash_entry *entry,
+ const void *key, unsigned int key_size)
+{
+ uint32_t key_offset;
+ if (entry->key_size) {
+ key_offset = entry->key.offset;
+ } else {
+ uint32_t segment_id;
+ if (key_size >= GRN_HASH_SEGMENT_SIZE) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ key_offset = hash->header->curr_key;
+ segment_id = (key_offset + key_size) >> W_OF_KEY_IN_A_SEGMENT;
+ if ((key_offset >> W_OF_KEY_IN_A_SEGMENT) != segment_id) {
+ key_offset = hash->header->curr_key = segment_id << W_OF_KEY_IN_A_SEGMENT;
+ }
+ hash->header->curr_key += key_size;
+ entry->key.offset = key_offset;
+ }
+
+ {
+ void * const key_ptr = grn_io_hash_key_at(ctx, hash, key_offset);
+ if (!key_ptr) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ memcpy(key_ptr, key, key_size);
+ }
+ return GRN_SUCCESS;
+}
+
+inline static grn_rc
+grn_hash_entry_put_key(grn_ctx *ctx, grn_hash *hash,
+ grn_hash_entry *entry, uint32_t hash_value,
+ const void *key, unsigned int key_size)
+{
+ if (hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ if (grn_hash_is_io_hash(hash)) {
+ if (key_size <= sizeof(entry->io_entry.key.buf)) {
+ memcpy(entry->io_entry.key.buf, key, key_size);
+ entry->io_entry.flag = HASH_IMMEDIATE;
+ } else {
+ const grn_rc rc =
+ grn_io_hash_entry_put_key(ctx, hash, (grn_io_hash_entry *)entry,
+ key, key_size);
+ if (rc) {
+ return rc;
+ }
+ entry->io_entry.flag = 0;
+ }
+ entry->io_entry.hash_value = hash_value;
+ entry->io_entry.key_size = key_size;
+ } else {
+ if (key_size <= sizeof(entry->tiny_entry.key.buf)) {
+ memcpy(entry->tiny_entry.key.buf, key, key_size);
+ entry->tiny_entry.flag = HASH_IMMEDIATE;
+ } else {
+ grn_ctx * const ctx = hash->ctx;
+ entry->tiny_entry.key.ptr = GRN_CTX_ALLOC(ctx, key_size);
+ if (!entry->tiny_entry.key.ptr) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ memcpy(entry->tiny_entry.key.ptr, key, key_size);
+ entry->tiny_entry.flag = 0;
+ }
+ entry->tiny_entry.hash_value = hash_value;
+ entry->tiny_entry.key_size = key_size;
+ }
+ } else {
+ if (hash->key_size == sizeof(uint32_t)) {
+ *(uint32_t *)entry->plain_entry.key = hash_value;
+ } else {
+ entry->rich_entry.hash_value = hash_value;
+ memcpy(entry->rich_entry.key_and_value, key, key_size);
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+/*
+ * grn_hash_entry_compare_key() returns GRN_TRUE if the entry key equals the
+ * specified key, or GRN_FALSE otherwise.
+ */
+inline static grn_bool
+grn_hash_entry_compare_key(grn_ctx *ctx, grn_hash *hash,
+ grn_hash_entry *entry, uint32_t hash_value,
+ const void *key, unsigned int key_size)
+{
+ if (hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ if (entry->hash_value != hash_value ||
+ entry->header.key_size != key_size) {
+ return GRN_FALSE;
+ }
+ if (grn_hash_is_io_hash(hash)) {
+ if (entry->io_entry.flag & HASH_IMMEDIATE) {
+ return !memcmp(key, entry->io_entry.key.buf, key_size);
+ } else {
+ const void * const entry_key_ptr =
+ grn_io_hash_key_at(ctx, hash, entry->io_entry.key.offset);
+ return !memcmp(key, entry_key_ptr, key_size);
+ }
+ } else {
+ if (entry->tiny_entry.flag & HASH_IMMEDIATE) {
+ return !memcmp(key, entry->tiny_entry.key.buf, key_size);
+ } else {
+ return !memcmp(key, entry->tiny_entry.key.ptr, key_size);
+ }
+ }
+ } else {
+ if (entry->hash_value != hash_value) {
+ return GRN_FALSE;
+ }
+ if (key_size == sizeof(uint32_t)) {
+ return GRN_TRUE;
+ } else {
+ return !memcmp(key, entry->rich_entry.key_and_value, key_size);
+ }
+ }
+}
+
+inline static char *
+get_key(grn_ctx *ctx, grn_hash *hash, entry_str *n)
+{
+ return grn_hash_entry_get_key(ctx, hash, (grn_hash_entry *)n);
+}
+
+inline static void *
+get_value(grn_hash *hash, entry_str *n)
+{
+ return grn_hash_entry_get_value(hash, (grn_hash_entry *)n);
+}
+
+inline static grn_rc
+put_key(grn_ctx *ctx, grn_hash *hash, entry_str *n, uint32_t h,
+ const char *key, unsigned int len)
+{
+ return grn_hash_entry_put_key(ctx, hash, (grn_hash_entry *)n, h, key, len);
+}
+
+inline static int
+match_key(grn_ctx *ctx, grn_hash *hash, entry_str *ee, uint32_t h,
+ const char *key, unsigned int len)
+{
+ return grn_hash_entry_compare_key(ctx, hash, (grn_hash_entry *)ee,
+ h, key, len);
+}
+
+#define GARBAGE (0xffffffff)
+
+inline static uint32_t
+grn_io_hash_calculate_entry_size(uint32_t key_size, uint32_t value_size,
+ uint32_t flags)
+{
+ if (flags & GRN_OBJ_KEY_VAR_SIZE) {
+ return (uintptr_t)((grn_io_hash_entry *)0)->value + value_size;
+ } else {
+ if (key_size == sizeof(uint32_t)) {
+ return (uintptr_t)((grn_plain_hash_entry *)0)->value + value_size;
+ } else {
+ return (uintptr_t)((grn_rich_hash_entry *)0)->key_and_value
+ + key_size + value_size;
+ }
+ }
+}
+
+static grn_io *
+grn_io_hash_create_io(grn_ctx *ctx, const char *path, uint32_t entry_size)
+{
+ uint32_t w_of_element = 0;
+ grn_io_array_spec array_spec[4];
+
+ while ((1U << w_of_element) < entry_size) {
+ w_of_element++;
+ }
+
+ array_spec[GRN_HASH_KEY_SEGMENT].w_of_element = 0;
+ array_spec[GRN_HASH_KEY_SEGMENT].max_n_segments = 0x400;
+ array_spec[GRN_HASH_ENTRY_SEGMENT].w_of_element = w_of_element;
+ array_spec[GRN_HASH_ENTRY_SEGMENT].max_n_segments =
+ 1U << (30 - (22 - w_of_element));
+ array_spec[GRN_HASH_INDEX_SEGMENT].w_of_element = 2;
+ array_spec[GRN_HASH_INDEX_SEGMENT].max_n_segments = 1U << (30 - (22 - 2));
+ array_spec[GRN_HASH_BITMAP_SEGMENT].w_of_element = 0;
+ array_spec[GRN_HASH_BITMAP_SEGMENT].max_n_segments = 1U << (30 - (22 + 3));
+ return grn_io_create_with_array(ctx, path, GRN_HASH_HEADER_SIZE,
+ GRN_HASH_SEGMENT_SIZE,
+ grn_io_auto, 4, array_spec);
+}
+
+static grn_rc
+grn_io_hash_init(grn_ctx *ctx, grn_hash *hash, const char *path,
+ uint32_t key_size, uint32_t value_size, uint32_t flags,
+ grn_encoding encoding, uint32_t init_size)
+{
+ grn_io *io;
+ struct grn_hash_header *header;
+ uint32_t entry_size, max_offset;
+
+ entry_size = grn_io_hash_calculate_entry_size(key_size, value_size, flags);
+
+ io = grn_io_hash_create_io(ctx, path, entry_size);
+ if (!io) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ grn_io_set_type(io, GRN_TABLE_HASH_KEY);
+
+ max_offset = IDX_MASK_IN_A_SEGMENT + 1;
+ while (max_offset < init_size * 2) {
+ max_offset *= 2;
+ }
+ max_offset--;
+
+ if (encoding == GRN_ENC_DEFAULT) {
+ encoding = ctx->encoding;
+ }
+
+ header = grn_io_header(io);
+ header->flags = flags;
+ header->encoding = encoding;
+ header->key_size = key_size;
+ header->curr_rec = 0;
+ header->curr_key = 0;
+ header->lock = 0;
+ header->idx_offset = 0;
+ header->value_size = value_size;
+ header->entry_size = entry_size;
+ header->max_offset = max_offset;
+ header->n_entries = 0;
+ header->n_garbages = 0;
+ header->tokenizer = GRN_ID_NIL;
+ if (header->flags & GRN_OBJ_KEY_NORMALIZE) {
+ header->flags &= ~GRN_OBJ_KEY_NORMALIZE;
+ hash->normalizer = grn_ctx_get(ctx, GRN_NORMALIZER_AUTO_NAME, -1);
+ header->normalizer = grn_obj_id(ctx, hash->normalizer);
+ } else {
+ hash->normalizer = NULL;
+ header->normalizer = GRN_ID_NIL;
+ }
+ grn_table_queue_init(ctx, &header->queue);
+
+ hash->obj.header.flags = header->flags;
+ hash->ctx = ctx;
+ hash->key_size = key_size;
+ hash->encoding = encoding;
+ hash->value_size = value_size;
+ hash->entry_size = entry_size;
+ hash->n_garbages = &header->n_garbages;
+ hash->n_entries = &header->n_entries;
+ hash->max_offset = &header->max_offset;
+ hash->io = io;
+ hash->header = header;
+ hash->lock = &header->lock;
+ hash->tokenizer = NULL;
+ return GRN_SUCCESS;
+}
+
+#define INITIAL_INDEX_SIZE 256U
+
+static uint32_t
+grn_tiny_hash_calculate_entry_size(uint32_t key_size, uint32_t value_size,
+ uint32_t flags)
+{
+ uint32_t entry_size;
+ if (flags & GRN_OBJ_KEY_VAR_SIZE) {
+ entry_size = (uintptr_t)((grn_tiny_hash_entry *)0)->value + value_size;
+ } else {
+ if (key_size == sizeof(uint32_t)) {
+ entry_size = (uintptr_t)((grn_plain_hash_entry *)0)->value + value_size;
+ } else {
+ entry_size = (uintptr_t)((grn_rich_hash_entry *)0)->key_and_value
+ + key_size + value_size;
+ }
+ }
+ if (entry_size != sizeof(uint32_t)) {
+ entry_size += sizeof(uintptr_t) - 1;
+ entry_size &= ~(sizeof(uintptr_t) - 1);
+ }
+ return entry_size;
+}
+
+static grn_rc
+grn_tiny_hash_init(grn_ctx *ctx, grn_hash *hash, const char *path,
+ uint32_t key_size, uint32_t value_size, uint32_t flags,
+ grn_encoding encoding)
+{
+ uint32_t entry_size;
+
+ if (path) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ hash->index = GRN_CTX_ALLOC(ctx, INITIAL_INDEX_SIZE * sizeof(grn_id));
+ if (!hash->index) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+
+ entry_size = grn_tiny_hash_calculate_entry_size(key_size, value_size, flags);
+ hash->obj.header.flags = flags;
+ hash->ctx = ctx;
+ hash->key_size = key_size;
+ hash->encoding = encoding;
+ hash->value_size = value_size;
+ hash->entry_size = entry_size;
+ hash->n_garbages = &hash->n_garbages_;
+ hash->n_entries = &hash->n_entries_;
+ hash->max_offset = &hash->max_offset_;
+ hash->max_offset_ = INITIAL_INDEX_SIZE - 1;
+ hash->io = NULL;
+ hash->n_garbages_ = 0;
+ hash->n_entries_ = 0;
+ hash->garbages = GRN_ID_NIL;
+ hash->tokenizer = NULL;
+ hash->normalizer = NULL;
+ grn_tiny_array_init(ctx, &hash->a, entry_size, GRN_TINY_ARRAY_CLEAR);
+ grn_tiny_bitmap_init(ctx, &hash->bitmap);
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_hash_init(grn_ctx *ctx, grn_hash *hash, const char *path,
+ uint32_t key_size, uint32_t value_size, uint32_t flags)
+{
+ if (flags & GRN_HASH_TINY) {
+ return grn_tiny_hash_init(ctx, hash, path, key_size, value_size,
+ flags, ctx->encoding);
+ } else {
+ return grn_io_hash_init(ctx, hash, path, key_size, value_size,
+ flags, ctx->encoding, 0);
+ }
+}
+
+grn_hash *
+grn_hash_create(grn_ctx *ctx, const char *path, uint32_t key_size, uint32_t value_size,
+ uint32_t flags)
+{
+ grn_hash *hash;
+ if (!ctx) {
+ return NULL;
+ }
+ if (key_size > GRN_HASH_MAX_KEY_SIZE) {
+ return NULL;
+ }
+ hash = (grn_hash *)GRN_MALLOC(sizeof(grn_hash));
+ if (!hash) {
+ return NULL;
+ }
+ GRN_DB_OBJ_SET_TYPE(hash, GRN_TABLE_HASH_KEY);
+ if (grn_hash_init(ctx, hash, path, key_size, value_size, flags)) {
+ GRN_FREE(hash);
+ return NULL;
+ }
+ return hash;
+}
+
+grn_hash *
+grn_hash_open(grn_ctx *ctx, const char *path)
+{
+ if (ctx) {
+ grn_io * const io = grn_io_open(ctx, path, grn_io_auto);
+ if (io) {
+ struct grn_hash_header * const header = grn_io_header(io);
+ if (grn_io_get_type(io) == GRN_TABLE_HASH_KEY) {
+ grn_hash * const hash = (grn_hash *)GRN_MALLOC(sizeof(grn_hash));
+ if (hash) {
+ if (!(header->flags & GRN_HASH_TINY)) {
+ GRN_DB_OBJ_SET_TYPE(hash, GRN_TABLE_HASH_KEY);
+ hash->ctx = ctx;
+ hash->key_size = header->key_size;
+ hash->encoding = header->encoding;
+ hash->value_size = header->value_size;
+ hash->entry_size = header->entry_size;
+ hash->n_garbages = &header->n_garbages;
+ hash->n_entries = &header->n_entries;
+ hash->max_offset = &header->max_offset;
+ hash->io = io;
+ hash->header = header;
+ hash->lock = &header->lock;
+ hash->tokenizer = grn_ctx_at(ctx, header->tokenizer);
+ if (header->flags & GRN_OBJ_KEY_NORMALIZE) {
+ header->flags &= ~GRN_OBJ_KEY_NORMALIZE;
+ hash->normalizer = grn_ctx_get(ctx, GRN_NORMALIZER_AUTO_NAME, -1);
+ header->normalizer = grn_obj_id(ctx, hash->normalizer);
+ } else {
+ hash->normalizer = grn_ctx_at(ctx, header->normalizer);
+ }
+ hash->obj.header.flags = header->flags;
+ return hash;
+ } else {
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "invalid hash flag. (%x)", header->flags);
+ }
+ GRN_FREE(hash);
+ }
+ } else {
+ ERR(GRN_INVALID_FORMAT, "file type unmatch");
+ }
+ grn_io_close(ctx, io);
+ }
+ }
+ return NULL;
+}
+
+static grn_rc
+grn_tiny_hash_fin(grn_ctx *ctx, grn_hash *hash)
+{
+ if (!hash->index) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ if (hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ uint32_t num_remaining_entries = *hash->n_entries;
+ grn_id *hash_ptr;
+ for (hash_ptr = hash->index; num_remaining_entries; hash_ptr++) {
+ const grn_id id = *hash_ptr;
+ if (id && id != GARBAGE) {
+ grn_tiny_hash_entry * const entry =
+ (grn_tiny_hash_entry *)grn_tiny_array_get(&hash->a, id);
+ GRN_ASSERT(entry);
+ num_remaining_entries--;
+ if (entry && !(entry->flag & HASH_IMMEDIATE)) {
+ GRN_CTX_FREE(ctx, entry->key.ptr);
+ }
+ }
+ }
+ }
+ grn_tiny_array_fin(&hash->a);
+ grn_tiny_bitmap_fin(&hash->bitmap);
+ GRN_CTX_FREE(ctx, hash->index);
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_hash_close(grn_ctx *ctx, grn_hash *hash)
+{
+ grn_rc rc;
+ if (!ctx || !hash) { return GRN_INVALID_ARGUMENT; }
+ if (grn_hash_is_io_hash(hash)) {
+ rc = grn_io_close(ctx, hash->io);
+ } else {
+ GRN_ASSERT(ctx == hash->ctx);
+ rc = grn_tiny_hash_fin(ctx, hash);
+ }
+ GRN_FREE(hash);
+ return rc;
+}
+
+grn_rc
+grn_hash_remove(grn_ctx *ctx, const char *path)
+{
+ if (!ctx || !path) { return GRN_INVALID_ARGUMENT; }
+ return grn_io_remove(ctx, path);
+}
+
+grn_rc
+grn_hash_truncate(grn_ctx *ctx, grn_hash *hash)
+{
+ grn_rc rc = GRN_SUCCESS;
+ char *path = NULL;
+ uint32_t key_size, value_size, flags;
+
+ if (!ctx || !hash) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ if (grn_hash_is_io_hash(hash)) {
+ const char * const io_path = grn_io_path(hash->io);
+ if (io_path && *io_path) {
+ path = GRN_STRDUP(io_path);
+ if (!path) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "cannot duplicate path: <%s>", io_path);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ }
+ key_size = hash->key_size;
+ value_size = hash->value_size;
+ flags = hash->obj.header.flags;
+
+ if (grn_hash_is_io_hash(hash)) {
+ rc = grn_io_close(ctx, hash->io);
+ if (!rc) {
+ hash->io = NULL;
+ if (path) {
+ rc = grn_io_remove(ctx, path);
+ }
+ }
+ }
+ if (!rc) {
+ rc = grn_hash_init(ctx, hash, path, key_size, value_size, flags);
+ }
+ if (path) {
+ GRN_FREE(path);
+ }
+ return rc;
+}
+
+inline static uint32_t
+grn_hash_calculate_hash_value(const void *ptr, uint32_t size)
+{
+ uint32_t i;
+ uint32_t hash_value = 0;
+ for (i = 0; i < size; i++) {
+ hash_value = (hash_value * 1021) + ((const uint8_t *)ptr)[i];
+ }
+ return hash_value;
+}
+
+inline static uint32_t
+grn_hash_calculate_step(uint32_t hash_value)
+{
+ return (hash_value >> 2) | 0x1010101;
+}
+
+static grn_rc
+grn_hash_reset(grn_ctx *ctx, grn_hash *hash, uint32_t expected_n_entries)
+{
+ grn_id *new_index = NULL;
+ uint32_t new_index_size = INITIAL_INDEX_SIZE;
+ grn_id *src_ptr = NULL, *dest_ptr = NULL;
+ uint32_t src_offset = 0, dest_offset = 0;
+ const uint32_t n_entries = *hash->n_entries;
+ const uint32_t max_offset = *hash->max_offset;
+
+ if (!expected_n_entries) {
+ expected_n_entries = n_entries * 2;
+ }
+ if (expected_n_entries > INT_MAX) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ while (new_index_size <= expected_n_entries) {
+ new_index_size *= 2;
+ }
+
+ if (grn_hash_is_io_hash(hash)) {
+ uint32_t i;
+ src_offset = hash->header->idx_offset;
+ dest_offset = MAX_INDEX_SIZE - src_offset;
+ for (i = 0; i < new_index_size; i += (IDX_MASK_IN_A_SEGMENT + 1)) {
+ /*
+ * The following grn_io_hash_idx_at() allocates memory for a new segment
+ * and returns a pointer to the new segment. It's actually bad manners
+ * but faster than calling grn_io_hash_idx_at() for each element.
+ */
+ dest_ptr = grn_io_hash_idx_at(ctx, hash, i + dest_offset);
+ if (!dest_ptr) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ memset(dest_ptr, 0, GRN_HASH_SEGMENT_SIZE);
+ }
+ } else {
+ GRN_ASSERT(ctx == hash->ctx);
+ new_index = GRN_CTX_ALLOC(ctx, new_index_size * sizeof(grn_id));
+ if (!new_index) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ src_ptr = hash->index;
+ }
+
+ {
+ uint32_t src_pos, count;
+ const uint32_t new_max_offset = new_index_size - 1;
+ for (count = 0, src_pos = 0; count < n_entries && src_pos <= max_offset;
+ src_pos++, src_ptr++) {
+ uint32_t i, step;
+ grn_id entry_id;
+ grn_hash_entry *entry;
+ if (grn_hash_is_io_hash(hash) && !(src_pos & IDX_MASK_IN_A_SEGMENT)) {
+ src_ptr = grn_io_hash_idx_at(ctx, hash, src_pos + src_offset);
+ if (!src_ptr) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ entry_id = *src_ptr;
+ if (!entry_id || (entry_id == GARBAGE)) {
+ continue;
+ }
+ entry = grn_hash_entry_at(ctx, hash, entry_id, GRN_TABLE_ADD);
+ if (!entry) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ step = grn_hash_calculate_step(entry->hash_value);
+ for (i = entry->hash_value; ; i += step) {
+ i &= new_max_offset;
+ if (grn_hash_is_io_hash(hash)) {
+ dest_ptr = grn_io_hash_idx_at(ctx, hash, i + dest_offset);
+ if (!dest_ptr) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ } else {
+ dest_ptr = new_index + i;
+ }
+ if (!*dest_ptr) {
+ break;
+ }
+ }
+ *dest_ptr = entry_id;
+ count++;
+ }
+ *hash->max_offset = new_max_offset;
+ *hash->n_garbages = 0;
+ }
+
+ if (grn_hash_is_io_hash(hash)) {
+ hash->header->idx_offset = dest_offset;
+ } else {
+ grn_id * const old_index = hash->index;
+ hash->index = new_index;
+ GRN_CTX_FREE(ctx, old_index);
+ }
+
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_hash_lock(grn_ctx *ctx, grn_hash *hash, int timeout)
+{
+ static int _ncalls = 0, _ncolls = 0;
+ uint32_t count;
+ _ncalls++;
+ for (count = 0;; count++) {
+ uint32_t lock;
+ GRN_ATOMIC_ADD_EX(hash->lock, 1, lock);
+ if (lock) {
+ GRN_ATOMIC_ADD_EX(hash->lock, -1, lock);
+ if (!timeout || (timeout > 0 && timeout == count)) { break; }
+ if (!(++_ncolls % 1000000) && (_ncolls > _ncalls)) {
+ if (_ncolls < 0 || _ncalls < 0) {
+ _ncolls = 0; _ncalls = 0;
+ } else {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "hash(%p) collisions(%d/%d)", hash, _ncolls, _ncalls);
+ }
+ }
+ grn_nanosleep(GRN_LOCK_WAIT_TIME_NANOSECOND);
+ continue;
+ }
+ return GRN_SUCCESS;
+ }
+ ERR(GRN_RESOURCE_DEADLOCK_AVOIDED, "grn_hash_lock");
+ return ctx->rc;
+}
+
+grn_rc
+grn_hash_unlock(grn_ctx *ctx, grn_hash *hash)
+{
+ uint32_t lock;
+ GRN_ATOMIC_ADD_EX(hash->lock, -1, lock);
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_hash_clear_lock(grn_ctx *ctx, grn_hash *hash)
+{
+ *hash->lock = 0;
+ return GRN_SUCCESS;
+}
+
+inline static grn_id
+grn_io_hash_add(grn_ctx *ctx, grn_hash *hash, uint32_t hash_value,
+ const void *key, unsigned int key_size, void **value)
+{
+ grn_id entry_id;
+ grn_hash_entry *entry;
+ struct grn_hash_header * const header = hash->header;
+
+ entry_id = header->garbages[key_size - 1];
+ if (entry_id) {
+ entry = grn_io_hash_entry_at(ctx, hash, entry_id, GRN_TABLE_ADD);
+ if (!entry) {
+ return GRN_ID_NIL;
+ }
+ header->garbages[key_size - 1] = *(grn_id *)entry;
+ if (hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ /* keep entry->io_entry's hash_value, flag, key_size and key. */
+ memset(entry->io_entry.value, 0, header->value_size);
+ } else {
+ memset(entry, 0, header->entry_size);
+ }
+ } else {
+ entry_id = header->curr_rec + 1;
+ entry = grn_hash_entry_at(ctx, hash, entry_id, GRN_TABLE_ADD);
+ if (!entry) {
+ return GRN_ID_NIL;
+ }
+ header->curr_rec = entry_id;
+ }
+
+ if (!grn_io_array_bit_on(ctx, hash->io, GRN_HASH_BITMAP_SEGMENT, entry_id)) {
+ /* TODO: error handling. */
+ }
+
+ if (grn_hash_entry_put_key(ctx, hash, entry, hash_value, key, key_size)) {
+ /* TODO: error handling. */
+ }
+
+ if (value) {
+ *value = grn_hash_entry_get_value(hash, entry);
+ }
+ return entry_id;
+}
+
+inline static grn_id
+grn_tiny_hash_add(grn_ctx *ctx, grn_hash *hash, uint32_t hash_value,
+ const void *key, unsigned int key_size, void **value)
+{
+ grn_id entry_id;
+ grn_hash_entry *entry;
+ if (hash->garbages) {
+ entry_id = hash->garbages;
+ entry = (grn_hash_entry *)grn_tiny_array_get(&hash->a, entry_id);
+ hash->garbages = *(grn_id *)entry;
+ memset(entry, 0, hash->entry_size);
+ } else {
+ entry_id = hash->a.max + 1;
+ entry = (grn_hash_entry *)grn_tiny_array_put(&hash->a, entry_id);
+ if (!entry) {
+ return GRN_ID_NIL;
+ }
+ }
+
+ if (!grn_tiny_bitmap_put_and_set(&hash->bitmap, entry_id, 1)) {
+ /* TODO: error handling. */
+ }
+
+ if (grn_hash_entry_put_key(ctx, hash, entry, hash_value, key, key_size)) {
+ /* TODO: error handling. */
+ }
+
+ if (value) {
+ *value = grn_hash_entry_get_value(hash, entry);
+ }
+ return entry_id;
+}
+
+grn_id
+grn_hash_add(grn_ctx *ctx, grn_hash *hash, const void *key,
+ unsigned int key_size, void **value, int *added)
+{
+ uint32_t hash_value;
+ if (!key || !key_size) {
+ return GRN_ID_NIL;
+ }
+ if (hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ if (key_size > hash->key_size) {
+ ERR(GRN_INVALID_ARGUMENT, "too long key");
+ return GRN_ID_NIL;
+ }
+ hash_value = grn_hash_calculate_hash_value(key, key_size);
+ } else {
+ if (key_size != hash->key_size) {
+ ERR(GRN_INVALID_ARGUMENT, "key size unmatch");
+ return GRN_ID_NIL;
+ }
+ if (key_size == sizeof(uint32_t)) {
+ hash_value = *((uint32_t *)key);
+ } else {
+ hash_value = grn_hash_calculate_hash_value(key, key_size);
+ }
+ }
+
+ {
+ uint32_t i;
+ const uint32_t step = grn_hash_calculate_step(hash_value);
+ grn_id id, *index, *garbage_index = NULL;
+ grn_hash_entry *entry;
+
+ /* lock */
+ if ((*hash->n_entries + *hash->n_garbages) * 2 > *hash->max_offset) {
+ grn_hash_reset(ctx, hash, 0);
+ }
+
+ for (i = hash_value; ; i += step) {
+ index = grn_hash_idx_at(ctx, hash, i);
+ if (!index) {
+ return GRN_ID_NIL;
+ }
+ id = *index;
+ if (!id) {
+ break;
+ }
+ if (id == GARBAGE) {
+ if (!garbage_index) {
+ garbage_index = index;
+ }
+ continue;
+ }
+
+ entry = grn_hash_entry_at(ctx, hash, id, GRN_TABLE_ADD);
+ if (!entry) {
+ return GRN_ID_NIL;
+ }
+ if (grn_hash_entry_compare_key(ctx, hash, entry, hash_value,
+ key, key_size)) {
+ if (value) {
+ *value = grn_hash_entry_get_value(hash, entry);
+ }
+ if (added) {
+ *added = 0;
+ }
+ return id;
+ }
+ }
+
+ if (grn_hash_is_io_hash(hash)) {
+ id = grn_io_hash_add(ctx, hash, hash_value, key, key_size, value);
+ } else {
+ id = grn_tiny_hash_add(ctx, hash, hash_value, key, key_size, value);
+ }
+ if (!id) {
+ return GRN_ID_NIL;
+ }
+ if (garbage_index) {
+ (*hash->n_garbages)--;
+ index = garbage_index;
+ }
+ *index = id;
+ (*hash->n_entries)++;
+ /* unlock */
+
+ if (added) {
+ *added = 1;
+ }
+ return id;
+ }
+}
+
+grn_id
+grn_hash_get(grn_ctx *ctx, grn_hash *hash, const void *key,
+ unsigned int key_size, void **value)
+{
+ uint32_t hash_value;
+ if (hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ if (key_size > hash->key_size) {
+ return GRN_ID_NIL;
+ }
+ hash_value = grn_hash_calculate_hash_value(key, key_size);
+ } else {
+ if (key_size != hash->key_size) {
+ return GRN_ID_NIL;
+ }
+ if (key_size == sizeof(uint32_t)) {
+ hash_value = *((uint32_t *)key);
+ } else {
+ hash_value = grn_hash_calculate_hash_value(key, key_size);
+ }
+ }
+
+ {
+ uint32_t i;
+ const uint32_t step = grn_hash_calculate_step(hash_value);
+ for (i = hash_value; ; i += step) {
+ grn_id id;
+ grn_id * const index = grn_hash_idx_at(ctx, hash, i);
+ if (!index) {
+ return GRN_ID_NIL;
+ }
+ id = *index;
+ if (!id) {
+ return GRN_ID_NIL;
+ }
+ if (id != GARBAGE) {
+ grn_hash_entry * const entry = grn_hash_entry_at(ctx, hash, id, 0);
+ if (entry) {
+ if (grn_hash_entry_compare_key(ctx, hash, entry, hash_value,
+ key, key_size)) {
+ if (value) {
+ *value = grn_hash_entry_get_value(hash, entry);
+ }
+ return id;
+ }
+ }
+ }
+ }
+ }
+}
+
+inline static grn_hash_entry *
+grn_hash_get_entry(grn_ctx *ctx, grn_hash *hash, grn_id id)
+{
+ if (!grn_hash_bitmap_at(ctx, hash, id)) {
+ return NULL;
+ }
+ return grn_hash_entry_at(ctx, hash, id, 0);
+}
+
+const char *
+_grn_hash_key(grn_ctx *ctx, grn_hash *hash, grn_id id, uint32_t *key_size)
+{
+ grn_hash_entry * const entry = grn_hash_get_entry(ctx, hash, id);
+ if (!entry) {
+ *key_size = 0;
+ return NULL;
+ }
+ *key_size = grn_hash_entry_get_key_size(hash, entry);
+ return grn_hash_entry_get_key(ctx, hash, entry);
+}
+
+int
+grn_hash_get_key(grn_ctx *ctx, grn_hash *hash, grn_id id, void *keybuf, int bufsize)
+{
+ int key_size;
+ grn_hash_entry * const entry = grn_hash_get_entry(ctx, hash, id);
+ if (!entry) {
+ return 0;
+ }
+ key_size = grn_hash_entry_get_key_size(hash, entry);
+ if (bufsize >= key_size) {
+ memcpy(keybuf, grn_hash_entry_get_key(ctx, hash, entry), key_size);
+ }
+ return key_size;
+}
+
+int
+grn_hash_get_key2(grn_ctx *ctx, grn_hash *hash, grn_id id, grn_obj *bulk)
+{
+ int key_size;
+ char *key;
+ grn_hash_entry * const entry = grn_hash_get_entry(ctx, hash, id);
+ if (!entry) {
+ return 0;
+ }
+ key_size = grn_hash_entry_get_key_size(hash, entry);
+ key = grn_hash_entry_get_key(ctx, hash, entry);
+ if (bulk->header.impl_flags & GRN_OBJ_REFER) {
+ bulk->u.b.head = key;
+ bulk->u.b.curr = key + key_size;
+ } else {
+ grn_bulk_write(ctx, bulk, key, key_size);
+ }
+ return key_size;
+}
+
+int
+grn_hash_get_value(grn_ctx *ctx, grn_hash *hash, grn_id id, void *valuebuf)
+{
+ void *value;
+ grn_hash_entry * const entry = grn_hash_get_entry(ctx, hash, id);
+ if (!entry) {
+ return 0;
+ }
+ value = grn_hash_entry_get_value(hash, entry);
+ if (!value) {
+ return 0;
+ }
+ if (valuebuf) {
+ memcpy(valuebuf, value, hash->value_size);
+ }
+ return hash->value_size;
+}
+
+const char *
+grn_hash_get_value_(grn_ctx *ctx, grn_hash *hash, grn_id id, uint32_t *size)
+{
+ const void *value;
+ grn_hash_entry * const entry = grn_hash_get_entry(ctx, hash, id);
+ if (!entry) {
+ return NULL;
+ }
+ value = grn_hash_entry_get_value(hash, entry);
+ if (!value) {
+ return NULL;
+ }
+ *size = hash->value_size;
+ return (const char *)value;
+}
+
+int
+grn_hash_get_key_value(grn_ctx *ctx, grn_hash *hash, grn_id id,
+ void *keybuf, int bufsize, void *valuebuf)
+{
+ void *value;
+ int key_size;
+ grn_hash_entry * const entry = grn_hash_get_entry(ctx, hash, id);
+ if (!entry) {
+ return 0;
+ }
+ key_size = grn_hash_entry_get_key_size(hash, entry);
+ if (bufsize >= key_size) {
+ memcpy(keybuf, grn_hash_entry_get_key(ctx, hash, entry), key_size);
+ }
+ value = grn_hash_entry_get_value(hash, entry);
+ if (!value) {
+ return 0;
+ }
+ if (valuebuf) {
+ memcpy(valuebuf, value, hash->value_size);
+ }
+ return key_size;
+}
+
+int
+_grn_hash_get_key_value(grn_ctx *ctx, grn_hash *hash, grn_id id,
+ void **key, void **value)
+{
+ int key_size;
+ grn_hash_entry * const entry = grn_hash_get_entry(ctx, hash, id);
+ if (!entry) {
+ return 0;
+ }
+ key_size = grn_hash_entry_get_key_size(hash, entry);
+ *key = grn_hash_entry_get_key(ctx, hash, entry);
+ *value = grn_hash_entry_get_value(hash, entry);
+ return *value ? key_size : 0;
+}
+
+grn_rc
+grn_hash_set_value(grn_ctx *ctx, grn_hash *hash, grn_id id,
+ const void *value, int flags)
+{
+ void *entry_value;
+ grn_hash_entry *entry;
+ if (!value) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ entry = grn_hash_get_entry(ctx, hash, id);
+ if (!entry) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ entry_value = grn_hash_entry_get_value(hash, entry);
+ if (!entry_value) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+
+ switch (flags & GRN_OBJ_SET_MASK) {
+ case GRN_OBJ_SET :
+ memcpy(entry_value, value, hash->value_size);
+ return GRN_SUCCESS;
+ case GRN_OBJ_INCR :
+ switch (hash->value_size) {
+ case sizeof(int32_t) :
+ *((int32_t *)entry_value) += *((int32_t *)value);
+ return GRN_SUCCESS;
+ case sizeof(int64_t) :
+ *((int64_t *)entry_value) += *((int64_t *)value);
+ return GRN_SUCCESS;
+ default :
+ return GRN_INVALID_ARGUMENT;
+ }
+ break;
+ case GRN_OBJ_DECR :
+ switch (hash->value_size) {
+ case sizeof(int32_t) :
+ *((int32_t *)entry_value) -= *((int32_t *)value);
+ return GRN_SUCCESS;
+ case sizeof(int64_t) :
+ *((int64_t *)entry_value) -= *((int64_t *)value);
+ return GRN_SUCCESS;
+ default :
+ return GRN_INVALID_ARGUMENT;
+ }
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "flags = %d", flags);
+ return ctx->rc;
+ }
+}
+
+#define DELETE_IT do {\
+ *ep = GARBAGE;\
+ if (grn_hash_is_io_hash(hash)) {\
+ uint32_t size = key_size - 1;\
+ struct grn_hash_header *hh = hash->header;\
+ ee->key = hh->garbages[size];\
+ hh->garbages[size] = e;\
+ grn_io_array_bit_off(ctx, hash->io, GRN_HASH_BITMAP_SEGMENT, e);\
+ } else {\
+ ee->key = hash->garbages;\
+ hash->garbages = e;\
+ if ((hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) && !(ee->flag & HASH_IMMEDIATE)) {\
+ grn_ctx *ctx = hash->ctx;\
+ GRN_CTX_FREE(ctx, ((entry_astr *)ee)->str);\
+ }\
+ grn_tiny_bitmap_get_and_set(&hash->bitmap, e, 0);\
+ }\
+ (*hash->n_entries)--;\
+ (*hash->n_garbages)++;\
+ rc = GRN_SUCCESS;\
+} while (0)
+
+grn_rc
+grn_hash_delete_by_id(grn_ctx *ctx, grn_hash *hash, grn_id id,
+ grn_table_delete_optarg *optarg)
+{
+ entry_str *ee;
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ if (!hash || !id) { return rc; }
+ /* lock */
+ ee = grn_hash_entry_at(ctx, hash, id, 0);
+ if (ee) {
+ grn_id e, *ep;
+ uint32_t i, key_size, h = ee->key, s = grn_hash_calculate_step(h);
+ key_size = (hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) ? ee->size : hash->key_size;
+ for (i = h; ; i += s) {
+ if (!(ep = grn_hash_idx_at(ctx, hash, i))) { return GRN_NO_MEMORY_AVAILABLE; }
+ if (!(e = *ep)) { break; }
+ if (e == id) {
+ DELETE_IT;
+ break;
+ }
+ }
+ }
+ /* unlock */
+ return rc;
+}
+
+grn_rc
+grn_hash_delete(grn_ctx *ctx, grn_hash *hash, const void *key, uint32_t key_size,
+ grn_table_delete_optarg *optarg)
+{
+ uint32_t h, i, m, s;
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ if (hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ if (key_size > hash->key_size) { return GRN_INVALID_ARGUMENT; }
+ h = grn_hash_calculate_hash_value(key, key_size);
+ } else {
+ if (key_size != hash->key_size) { return GRN_INVALID_ARGUMENT; }
+ if (key_size == sizeof(uint32_t)) {
+ h = *((uint32_t *)key);
+ } else {
+ h = grn_hash_calculate_hash_value(key, key_size);
+ }
+ }
+ s = grn_hash_calculate_step(h);
+ {
+ grn_id e, *ep;
+ /* lock */
+ m = *hash->max_offset;
+ for (i = h; ; i += s) {
+ if (!(ep = grn_hash_idx_at(ctx, hash, i))) { return GRN_NO_MEMORY_AVAILABLE; }
+ if (!(e = *ep)) { break; }
+ if (e == GARBAGE) { continue; }
+ {
+ entry_str * const ee = grn_hash_entry_at(ctx, hash, e, 0);
+ if (ee && match_key(ctx, hash, ee, h, key, key_size)) {
+ DELETE_IT;
+ break;
+ }
+ }
+ }
+ /* unlock */
+ return rc;
+ }
+}
+
+/* only valid for hash tables, GRN_OBJ_KEY_VAR_SIZE && GRN_HASH_TINY */
+const char *
+_grn_hash_strkey_by_val(void *v, uint16_t *size)
+{
+ entry_astr *n = (entry_astr *)((uintptr_t)v -
+ (uintptr_t)&((entry_astr *)0)->dummy);
+ *size = n->size;
+ return (n->flag & HASH_IMMEDIATE) ? (char *)&n->str : n->str;
+}
+
+void
+grn_hash_cursor_close(grn_ctx *ctx, grn_hash_cursor *c)
+{
+ GRN_ASSERT(c->ctx == ctx);
+ GRN_FREE(c);
+}
+
+#define HASH_CURR_MAX(hash) \
+ ((grn_hash_is_io_hash(hash)) ? (hash)->header->curr_rec : (hash)->a.max)
+
+grn_hash_cursor *
+grn_hash_cursor_open(grn_ctx *ctx, grn_hash *hash,
+ const void *min, uint32_t min_size,
+ const void *max, uint32_t max_size,
+ int offset, int limit, int flags)
+{
+ grn_hash_cursor *c;
+ if (!hash || !ctx) { return NULL; }
+ if (!(c = GRN_MALLOCN(grn_hash_cursor, 1))) { return NULL; }
+ GRN_DB_OBJ_SET_TYPE(c, GRN_CURSOR_TABLE_HASH_KEY);
+ c->hash = hash;
+ c->ctx = ctx;
+ c->obj.header.flags = flags;
+ c->obj.header.domain = GRN_ID_NIL;
+ if (flags & GRN_CURSOR_DESCENDING) {
+ c->dir = -1;
+ if (max) {
+ if (!(c->curr_rec = grn_hash_get(ctx, hash, max, max_size, NULL))) {
+ c->tail = GRN_ID_NIL;
+ goto exit;
+ }
+ if (!(flags & GRN_CURSOR_LT)) { c->curr_rec++; }
+ } else {
+ c->curr_rec = HASH_CURR_MAX(hash) + 1;
+ }
+ if (min) {
+ if (!(c->tail = grn_hash_get(ctx, hash, min, min_size, NULL))) {
+ c->curr_rec = GRN_ID_NIL;
+ goto exit;
+ }
+ if ((flags & GRN_CURSOR_GT)) { c->tail++; }
+ } else {
+ c->tail = GRN_ID_NIL + 1;
+ }
+ if (c->curr_rec < c->tail) { c->tail = c->curr_rec; }
+ } else {
+ c->dir = 1;
+ if (min) {
+ if (!(c->curr_rec = grn_hash_get(ctx, hash, min, min_size, NULL))) {
+ c->tail = GRN_ID_NIL;
+ goto exit;
+ }
+ if (!(flags & GRN_CURSOR_GT)) { c->curr_rec--; }
+ } else {
+ c->curr_rec = GRN_ID_NIL;
+ }
+ if (max) {
+ if (!(c->tail = grn_hash_get(ctx, hash, max, max_size, NULL))) {
+ c->curr_rec = GRN_ID_NIL;
+ goto exit;
+ }
+ if ((flags & GRN_CURSOR_LT)) { c->tail--; }
+ } else {
+ c->tail = HASH_CURR_MAX(hash);
+ }
+ if (c->tail < c->curr_rec) { c->tail = c->curr_rec; }
+ }
+ if (*hash->n_entries != HASH_CURR_MAX(hash)) {
+ while (offset && c->curr_rec != c->tail) {
+ c->curr_rec += c->dir;
+ if (grn_hash_bitmap_at(ctx, c->hash, c->curr_rec)) { offset--; }
+ }
+ } else {
+ c->curr_rec += c->dir * offset;
+ }
+exit :
+ c->rest = (limit < 0) ? GRN_ARRAY_MAX : limit;
+ return c;
+}
+
+grn_id
+grn_hash_cursor_next(grn_ctx *ctx, grn_hash_cursor *c)
+{
+ if (c && c->rest) {
+ while (c->curr_rec != c->tail) {
+ c->curr_rec += c->dir;
+ if (*c->hash->n_entries != HASH_CURR_MAX(c->hash)) {
+ if (!grn_hash_bitmap_at(ctx, c->hash, c->curr_rec)) { continue; }
+ }
+ c->rest--;
+ return c->curr_rec;
+ }
+ }
+ return GRN_ID_NIL;
+}
+
+grn_id
+grn_hash_next(grn_ctx *ctx, grn_hash *hash, grn_id id)
+{
+ grn_id max = HASH_CURR_MAX(hash);
+ while (++id <= max) {
+ if (grn_hash_bitmap_at(ctx, hash, id)) { return id; }
+ }
+ return GRN_ID_NIL;
+}
+
+grn_id
+grn_hash_at(grn_ctx *ctx, grn_hash *hash, grn_id id)
+{
+ return grn_hash_bitmap_at(ctx, hash, id) ? id : GRN_ID_NIL;
+}
+
+int
+grn_hash_cursor_get_key(grn_ctx *ctx, grn_hash_cursor *c, void **key)
+{
+ int key_size;
+ entry_str *ee;
+ if (!c) { return 0; }
+ ee = grn_hash_entry_at(ctx, c->hash, c->curr_rec, 0);
+ if (!ee) { return 0; }
+ key_size = (c->hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) ? ee->size : c->hash->key_size;
+ *key = get_key(ctx, c->hash, ee);
+ return key_size;
+}
+
+int
+grn_hash_cursor_get_value(grn_ctx *ctx, grn_hash_cursor *c, void **value)
+{
+ void *v;
+ entry_str *ee;
+ if (!c) { return 0; }
+ ee = grn_hash_entry_at(ctx, c->hash, c->curr_rec, 0);
+ if (ee && (v = get_value(c->hash, ee))) {
+ *value = v;
+ return c->hash->value_size;
+ }
+ return 0;
+}
+
+int
+grn_hash_cursor_get_key_value(grn_ctx *ctx, grn_hash_cursor *c,
+ void **key, uint32_t *key_size, void **value)
+{
+ entry_str *ee;
+ if (!c) { return GRN_INVALID_ARGUMENT; }
+ ee = grn_hash_entry_at(ctx, c->hash, c->curr_rec, 0);
+ if (!ee) { return GRN_INVALID_ARGUMENT; }
+ if (key_size) {
+ *key_size = (c->hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) ? ee->size : c->hash->key_size;
+ }
+ if (key) { *key = get_key(ctx, c->hash, ee); }
+ if (value) { *value = get_value(c->hash, ee); }
+ return c->hash->value_size;
+}
+
+grn_rc
+grn_hash_cursor_set_value(grn_ctx *ctx, grn_hash_cursor *c,
+ const void *value, int flags)
+{
+ if (!c) { return GRN_INVALID_ARGUMENT; }
+ return grn_hash_set_value(ctx, c->hash, c->curr_rec, value, flags);
+}
+
+grn_rc
+grn_hash_cursor_delete(grn_ctx *ctx, grn_hash_cursor *c,
+ grn_table_delete_optarg *optarg)
+{
+ if (!c) { return GRN_INVALID_ARGUMENT; }
+ return grn_hash_delete_by_id(ctx, c->hash, c->curr_rec, optarg);
+}
+
+/* sort */
+
+#define PREPARE_VAL(e,ep,es) do {\
+ if ((arg->flags & GRN_TABLE_SORT_BY_VALUE)) {\
+ ep = ((const uint8_t *)(get_value(hash, (entry_str *)(e))));\
+ es = hash->value_size;\
+ } else {\
+ ep = ((const uint8_t *)(get_key(ctx, hash, (entry_str *)(e))));\
+ es = ((hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE)\
+ ? ((entry_str *)(e))->size : hash->key_size); \
+ }\
+ ep += arg->offset;\
+ es -= arg->offset;\
+} while (0)
+
+#define COMPARE_VAL_(ap,as,bp,bs)\
+ (arg->compar\
+ ? arg->compar(ctx,\
+ (grn_obj *)hash, (void *)(ap), as,\
+ (grn_obj *)hash, (void *)(bp), bs, arg->compar_arg)\
+ : ((arg->flags & GRN_TABLE_SORT_AS_NUMBER)\
+ ? ((arg->flags & GRN_TABLE_SORT_AS_UNSIGNED)\
+ ? ((arg->flags & GRN_TABLE_SORT_AS_INT64)\
+ ? *((uint64_t *)(ap)) > *((uint64_t *)(bp))\
+ : *((uint32_t *)(ap)) > *((uint32_t *)(bp)))\
+ : ((arg->flags & GRN_TABLE_SORT_AS_INT64)\
+ ? *((int64_t *)(ap)) > *((int64_t *)(bp))\
+ : *((int32_t *)(ap)) > *((int32_t *)(bp))))\
+ : grn_str_greater(ap, as, bp, bs)))
+
+#define COMPARE_VAL(ap,as,bp,bs)\
+ ((dir) ? COMPARE_VAL_((bp),(bs),(ap),(as)) : COMPARE_VAL_((ap),(as),(bp),(bs)))
+
+inline static entry **
+pack(grn_ctx *ctx, grn_hash *hash, entry **res, grn_table_sort_optarg *arg, int dir)
+{
+ uint32_t n;
+ uint32_t cs, es;
+ const uint8_t *cp, *ep;
+ entry **head, **tail, *e, *c;
+ grn_id id, m = HASH_CURR_MAX(hash);
+ for (id = m >> 1;;id = (id == m) ? 1 : id + 1) {
+ if (grn_hash_bitmap_at(ctx, hash, id)) { break; }
+ }
+ c = grn_hash_entry_at(ctx, hash, id, 0);
+ if (!c) { return NULL; }
+ PREPARE_VAL(c, cp, cs);
+ head = res;
+ n = *hash->n_entries - 1;
+ tail = res + n;
+ while (n--) {
+ do {
+ id = (id == m) ? 1 : id + 1;
+ } while (!grn_hash_bitmap_at(ctx, hash, id));
+ e = grn_hash_entry_at(ctx, hash, id, 0);
+ if (!e) { return NULL; }
+ PREPARE_VAL(e, ep, es);
+ if (COMPARE_VAL(cp, cs, ep, es)) {
+ *head++ = e;
+ } else {
+ *tail-- = e;
+ }
+ }
+ *head = c;
+ return *hash->n_entries > 2 ? head : NULL;
+}
+
+inline static void
+swap(entry **a, entry **b)
+{
+ entry *c_ = *a;
+ *a = *b;
+ *b = c_;
+}
+
+#define SWAP(a,ap,as,b,bp,bs) do {\
+ const uint8_t *cp_ = ap;\
+ uint32_t cs_ = as;\
+ ap = bp; bp = cp_;\
+ as = bs; bs = cs_;\
+ swap(a,b);\
+} while (0)
+
+inline static entry **
+part(grn_ctx *ctx, entry **b, entry **e, grn_table_sort_optarg *arg, grn_hash *hash, int dir)
+{
+ entry **c;
+ const uint8_t *bp, *cp, *ep;
+ uint32_t bs, cs, es;
+ intptr_t d = e - b;
+ PREPARE_VAL(*b, bp, bs);
+ PREPARE_VAL(*e, ep, es);
+ if (COMPARE_VAL(bp, bs, ep, es)) {
+ SWAP(b, bp, bs, e, ep, es);
+ }
+ if (d < 2) { return NULL; }
+ c = b + (d >> 1);
+ PREPARE_VAL(*c, cp, cs);
+ if (COMPARE_VAL(bp, bs, cp, cs)) {
+ SWAP(b, bp, bs, c, cp, cs);
+ } else {
+ if (COMPARE_VAL(cp, cs, ep, es)) {
+ SWAP(c, cp, cs, e, ep, es);
+ }
+ }
+ if (d < 3) { return NULL; }
+ b++;
+ swap(b, c);
+ c = b;
+ PREPARE_VAL(*c, cp, cs);
+ for (;;) {
+ do {
+ b++;
+ PREPARE_VAL(*b, bp, bs);
+ } while (COMPARE_VAL(cp, cs, bp, bs));
+ do {
+ e--;
+ PREPARE_VAL(*e, ep, es);
+ } while (COMPARE_VAL(ep, es, cp, cs));
+ if (b >= e) { break; }
+ SWAP(b, bp, bs, e, ep, es);
+ }
+ SWAP(c, cp, cs, e, ep, es);
+ return e;
+}
+
+static void
+_sort(grn_ctx *ctx, entry **head, entry **tail, int limit,
+ grn_table_sort_optarg *arg, grn_hash *hash, int dir)
+{
+ entry **c;
+ if (head < tail && (c = part(ctx, head, tail, arg, hash, dir))) {
+ intptr_t rest = limit - 1 - (c - head);
+ _sort(ctx, head, c - 1, limit, arg, hash, dir);
+ if (rest > 0) { _sort(ctx, c + 1, tail, (int)rest, arg, hash, dir); }
+ }
+}
+
+static void
+sort(grn_ctx *ctx,
+ grn_hash *hash, entry **res, int limit, grn_table_sort_optarg *arg, int dir)
+{
+ entry **c = pack(ctx, hash, res, arg, dir);
+ if (c) {
+ intptr_t rest = limit - 1 - (c - res);
+ _sort(ctx, res, c - 1, limit, arg, hash, dir);
+ if (rest > 0 ) {
+ _sort(ctx, c + 1, res + *hash->n_entries - 1, (int)rest, arg, hash, dir);
+ }
+ }
+}
+
+typedef struct {
+ grn_id id;
+ int32_t v;
+} val32;
+
+#define PREPARE_VAL32(id,e,ep) do {\
+ (ep)->id = id;\
+ (ep)->v = (arg->flags & GRN_TABLE_SORT_BY_ID)\
+ ? (int32_t) id\
+ : (*((int32_t *)((byte *)((arg->flags & GRN_TABLE_SORT_BY_VALUE)\
+ ? get_value(hash, (e))\
+ : get_key(ctx, hash, (e))) + arg->offset)));\
+} while (0)
+
+#define COMPARE_VAL32_(ap,bp) \
+ (arg->compar\
+ ? arg->compar(ctx,\
+ (grn_obj *)hash, (void *)&(ap)->v, sizeof(uint32_t),\
+ (grn_obj *)hash, (void *)&(bp)->v, sizeof(uint32_t),\
+ arg->compar_arg)\
+ : ((arg->flags & GRN_TABLE_SORT_AS_NUMBER)\
+ ? ((arg->flags & GRN_TABLE_SORT_AS_UNSIGNED)\
+ ? *((uint32_t *)&(ap)->v) > *((uint32_t *)&(bp)->v)\
+ : *((int32_t *)&(ap)->v) > *((int32_t *)&(bp)->v))\
+ : memcmp(&(ap)->v, &(bp)->v, sizeof(uint32_t)) > 0))
+
+#define COMPARE_VAL32(ap,bp)\
+ ((dir) ? COMPARE_VAL32_((bp),(ap)) : COMPARE_VAL32_((ap),(bp)))
+
+inline static val32 *
+pack_val32(grn_ctx *ctx, grn_hash *hash, val32 *res, grn_table_sort_optarg *arg, int dir)
+{
+ uint32_t n;
+ entry_str *e, *c;
+ val32 *head, *tail, cr, er;
+ grn_id id, m = HASH_CURR_MAX(hash);
+ for (id = m >> 1;;id = (id == m) ? 1 : id + 1) {
+ if (grn_hash_bitmap_at(ctx, hash, id)) { break; }
+ }
+ c = grn_hash_entry_at(ctx, hash, id, 0);
+ if (!c) { return NULL; }
+ PREPARE_VAL32(id, c, &cr);
+ head = res;
+ n = *hash->n_entries - 1;
+ tail = res + n;
+ while (n--) {
+ do {
+ id = (id == m) ? 1 : id + 1;
+ } while (!grn_hash_bitmap_at(ctx, hash, id));
+ e = grn_hash_entry_at(ctx, hash, id, 0);
+ if (!e) { return NULL; }
+ PREPARE_VAL32(id, e, &er);
+ if (COMPARE_VAL32(&cr, &er)) {
+ *head++ = er;
+ } else {
+ *tail-- = er;
+ }
+ }
+ *head = cr;
+ return *hash->n_entries > 2 ? head : NULL;
+}
+
+#define SWAP_VAL32(ap,bp) do {\
+ val32 cr_ = *ap;\
+ *ap = *bp;\
+ *bp = cr_;\
+} while (0)
+
+inline static val32 *
+part_val32(grn_ctx *ctx,
+ val32 *b, val32 *e, grn_table_sort_optarg *arg, grn_hash *hash, int dir)
+{
+ val32 *c;
+ intptr_t d = e - b;
+ if (COMPARE_VAL32(b, e)) { SWAP_VAL32(b, e); }
+ if (d < 2) { return NULL; }
+ c = b + (d >> 1);
+ if (COMPARE_VAL32(b, c)) {
+ SWAP_VAL32(b, c);
+ } else {
+ if (COMPARE_VAL32(c, e)) { SWAP_VAL32(c, e); }
+ }
+ if (d < 3) { return NULL; }
+ b++;
+ SWAP_VAL32(b, c);
+ c = b;
+ for (;;) {
+ do { b++; } while (COMPARE_VAL32(c, b));
+ do { e--; } while (COMPARE_VAL32(e, c));
+ if (b >= e) { break; }
+ SWAP_VAL32(b, e);
+ }
+ SWAP_VAL32(c, e);
+ return e;
+}
+
+static void
+_sort_val32(grn_ctx *ctx, val32 *head, val32 *tail, int limit,
+ grn_table_sort_optarg *arg, grn_hash *hash, int dir)
+{
+ val32 *c;
+ if (head < tail && (c = part_val32(ctx, head, tail, arg, hash, dir))) {
+ intptr_t rest = limit - 1 - (c - head);
+ _sort_val32(ctx, head, c - 1, limit, arg, hash, dir);
+ if (rest > 0) { _sort_val32(ctx, c + 1, tail, (int)rest, arg, hash, dir); }
+ }
+}
+
+static void
+sort_val32(grn_ctx *ctx,
+ grn_hash *hash, val32 *res, int limit, grn_table_sort_optarg *arg, int dir)
+{
+ val32 *c = pack_val32(ctx, hash, res, arg, dir);
+ if (c) {
+ intptr_t rest = limit - 1 - (c - res);
+ _sort_val32(ctx, res, c - 1, limit, arg, hash, dir);
+ if (rest > 0 ) {
+ _sort_val32(ctx, c + 1, res + *hash->n_entries - 1, (int)rest, arg, hash, dir);
+ }
+ }
+}
+
+inline static grn_id
+entry2id(grn_ctx *ctx, grn_hash *hash, entry *e)
+{
+ entry *e2;
+ grn_id id, *ep;
+ uint32_t i, h = e->key, s = grn_hash_calculate_step(h);
+ for (i = h; ; i += s) {
+ if (!(ep = grn_hash_idx_at(ctx, hash, i))) { return GRN_ID_NIL; }
+ if (!(id = *ep)) { break; }
+ if (id != GARBAGE) {
+ e2 = grn_hash_entry_at(ctx, hash, id, 0);
+ if (!e2) { return GRN_ID_NIL; }
+ if (e2 == e) { break; }
+ }
+ }
+ return id;
+}
+
+int
+grn_hash_sort(grn_ctx *ctx, grn_hash *hash,
+ int limit, grn_array *result, grn_table_sort_optarg *optarg)
+{
+ entry **res;
+ if (!result || !*hash->n_entries) { return 0; }
+ if (!(res = GRN_MALLOC(sizeof(entry *) * *hash->n_entries))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "allocation of entries failed on grn_hash_sort !");
+ return 0;
+ }
+ if (limit < 0) {
+ limit += *hash->n_entries + 1;
+ if (limit < 0) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "limit is too small in grn_hash_sort !");
+ return 0;
+ }
+ }
+ if (limit > *hash->n_entries) { limit = *hash->n_entries; }
+ /* hash->limit = limit; */
+ if (optarg) {
+ int dir = (optarg->flags & GRN_TABLE_SORT_DESC);
+ if ((optarg->flags & GRN_TABLE_SORT_BY_ID) ||
+ (optarg->flags & GRN_TABLE_SORT_BY_VALUE)
+ ? ((hash->value_size - optarg->offset) == sizeof(uint32_t))
+ : (!(hash->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE)
+ && hash->key_size == sizeof(uint32_t))) {
+ if (sizeof(entry *) != sizeof(val32)) {
+ GRN_FREE(res);
+ if (!(res = GRN_MALLOC(sizeof(val32) * *hash->n_entries))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "allocation of entries failed on grn_hash_sort !");
+ return 0;
+ }
+ }
+ sort_val32(ctx, hash, (val32 *)res, limit, optarg, dir);
+ {
+ int i;
+ grn_id *v;
+ val32 *rp = (val32 *)res;
+ for (i = 0; i < limit; i++, rp++) {
+ if (!grn_array_add(ctx, result, (void **)&v)) { break; }
+ if (!(*v = rp->id)) { break; }
+ }
+ GRN_FREE(res);
+ return i;
+ }
+ } else {
+ sort(ctx, hash, res, limit, optarg, dir);
+ }
+ } else {
+ grn_table_sort_optarg opt = {0, NULL, NULL, NULL, 0};
+ sort(ctx, hash, res, limit, &opt, 0);
+ }
+ {
+ int i;
+ grn_id *v;
+ entry **rp = res;
+ for (i = 0; i < limit; i++, rp++) {
+ if (!grn_array_add(ctx, result, (void **)&v)) { break; }
+ if (!(*v = entry2id(ctx, hash, *rp))) { break; }
+ }
+ GRN_FREE(res);
+ return i;
+ }
+}
+
+void
+grn_hash_check(grn_ctx *ctx, grn_hash *hash)
+{
+ char buf[8];
+ struct grn_hash_header *h = hash->header;
+ GRN_OUTPUT_ARRAY_OPEN("RESULT", 1);
+ GRN_OUTPUT_MAP_OPEN("SUMMARY", 25);
+ GRN_OUTPUT_CSTR("flags");
+ grn_itoh(h->flags, buf, 8);
+ GRN_OUTPUT_STR(buf, 8);
+ GRN_OUTPUT_CSTR("key_size");
+ GRN_OUTPUT_INT64(hash->key_size);
+ GRN_OUTPUT_CSTR("value_size");
+ GRN_OUTPUT_INT64(hash->value_size);
+ GRN_OUTPUT_CSTR("tokenizer");
+ GRN_OUTPUT_INT64(h->tokenizer);
+ GRN_OUTPUT_CSTR("normalizer");
+ GRN_OUTPUT_INT64(h->normalizer);
+ GRN_OUTPUT_CSTR("curr_rec");
+ GRN_OUTPUT_INT64(h->curr_rec);
+ GRN_OUTPUT_CSTR("curr_key");
+ GRN_OUTPUT_INT64(h->curr_key);
+ GRN_OUTPUT_CSTR("idx_offset");
+ GRN_OUTPUT_INT64(h->idx_offset);
+ GRN_OUTPUT_CSTR("entry_size");
+ GRN_OUTPUT_INT64(hash->entry_size);
+ GRN_OUTPUT_CSTR("max_offset");
+ GRN_OUTPUT_INT64(*hash->max_offset);
+ GRN_OUTPUT_CSTR("n_entries");
+ GRN_OUTPUT_INT64(*hash->n_entries);
+ GRN_OUTPUT_CSTR("n_garbages");
+ GRN_OUTPUT_INT64(*hash->n_garbages);
+ GRN_OUTPUT_CSTR("lock");
+ GRN_OUTPUT_INT64(h->lock);
+ GRN_OUTPUT_MAP_CLOSE();
+ GRN_OUTPUT_ARRAY_CLOSE();
+}
+
+/* rhash : grn_hash with subrecs */
+
+#ifdef USE_GRN_INDEX2
+
+static uint32_t default_flags = GRN_HASH_TINY;
+
+grn_rc
+grn_rhash_init(grn_ctx *ctx, grn_hash *hash, grn_rec_unit record_unit, int record_size,
+ grn_rec_unit subrec_unit, int subrec_size, unsigned int max_n_subrecs)
+{
+ grn_rc rc;
+ record_size = grn_rec_unit_size(record_unit, record_size);
+ subrec_size = grn_rec_unit_size(subrec_unit, subrec_size);
+ if (record_unit != grn_rec_userdef && subrec_unit != grn_rec_userdef) {
+ subrec_size -= record_size;
+ }
+ if (!hash) { return GRN_INVALID_ARGUMENT; }
+ if (record_size < 0) { return GRN_INVALID_ARGUMENT; }
+ if ((default_flags & GRN_HASH_TINY)) {
+ rc = grn_tiny_hash_init(ctx, hash, NULL, record_size,
+ max_n_subrecs * (GRN_RSET_SCORE_SIZE + subrec_size),
+ default_flags, GRN_ENC_NONE);
+ } else {
+ rc = grn_io_hash_init(ctx, hash, NULL, record_size,
+ max_n_subrecs * (GRN_RSET_SCORE_SIZE + subrec_size),
+ default_flags, GRN_ENC_NONE, 0);
+ }
+ if (rc) { return rc; }
+ hash->record_unit = record_unit;
+ hash->subrec_unit = subrec_unit;
+ hash->subrec_size = subrec_size;
+ hash->max_n_subrecs = max_n_subrecs;
+ return rc;
+}
+
+grn_rc
+grn_rhash_fin(grn_ctx *ctx, grn_hash *hash)
+{
+ grn_rc rc;
+ if (grn_hash_is_io_hash(hash)) {
+ rc = grn_io_close(ctx, hash->io);
+ } else {
+ GRN_ASSERT(ctx == hash->ctx);
+ rc = grn_tiny_hash_fin(ctx, hash);
+ }
+ return rc;
+}
+
+inline static void
+subrecs_push(byte *subrecs, int size, int n_subrecs, int score, void *body, int dir)
+{
+ byte *v;
+ int *c2;
+ int n = n_subrecs - 1, n2;
+ while (n) {
+ n2 = (n - 1) >> 1;
+ c2 = GRN_RSET_SUBRECS_NTH(subrecs,size,n2);
+ if (GRN_RSET_SUBRECS_CMP(score, *c2, dir) > 0) { break; }
+ GRN_RSET_SUBRECS_COPY(subrecs,size,n,c2);
+ n = n2;
+ }
+ v = subrecs + n * (size + GRN_RSET_SCORE_SIZE);
+ *((int *)v) = score;
+ memcpy(v + GRN_RSET_SCORE_SIZE, body, size);
+}
+
+inline static void
+subrecs_replace_min(byte *subrecs, int size, int n_subrecs, int score, void *body, int dir)
+{
+ byte *v;
+ int n = 0, n1, n2, *c1, *c2;
+ for (;;) {
+ n1 = n * 2 + 1;
+ n2 = n1 + 1;
+ c1 = n1 < n_subrecs ? GRN_RSET_SUBRECS_NTH(subrecs,size,n1) : NULL;
+ c2 = n2 < n_subrecs ? GRN_RSET_SUBRECS_NTH(subrecs,size,n2) : NULL;
+ if (c1 && GRN_RSET_SUBRECS_CMP(score, *c1, dir) > 0) {
+ if (c2 &&
+ GRN_RSET_SUBRECS_CMP(score, *c2, dir) > 0 &&
+ GRN_RSET_SUBRECS_CMP(*c1, *c2, dir) > 0) {
+ GRN_RSET_SUBRECS_COPY(subrecs,size,n,c2);
+ n = n2;
+ } else {
+ GRN_RSET_SUBRECS_COPY(subrecs,size,n,c1);
+ n = n1;
+ }
+ } else {
+ if (c2 && GRN_RSET_SUBRECS_CMP(score, *c2, dir) > 0) {
+ GRN_RSET_SUBRECS_COPY(subrecs,size,n,c2);
+ n = n2;
+ } else {
+ break;
+ }
+ }
+ }
+ v = subrecs + n * (size + GRN_RSET_SCORE_SIZE);
+ memcpy(v, &score, GRN_RSET_SCORE_SIZE);
+ memcpy(v + GRN_RSET_SCORE_SIZE, body, size);
+}
+
+void
+grn_rhash_add_subrec(grn_hash *s, grn_rset_recinfo *ri, int score, void *body, int dir)
+{
+ int limit = s->max_n_subrecs;
+ ri->score += score;
+ ri->n_subrecs += 1;
+ if (limit) {
+ int ssize = s->subrec_size;
+ int n_subrecs = GRN_RSET_N_SUBRECS(ri);
+ if (limit < n_subrecs) {
+ if (GRN_RSET_SUBRECS_CMP(score, *ri->subrecs, dir) > 0) {
+ subrecs_replace_min(ri->subrecs, ssize, limit, score, body, dir);
+ }
+ } else {
+ subrecs_push(ri->subrecs, ssize, n_subrecs, score, body, dir);
+ }
+ }
+}
+
+grn_hash *
+grn_rhash_group(grn_hash *s, int limit, grn_group_optarg *optarg)
+{
+ grn_ctx *ctx = s->ctx;
+ grn_hash *g, h;
+ grn_rset_recinfo *ri;
+ grn_rec_unit unit;
+ grn_hash_cursor *c;
+ grn_id rh;
+ byte *key, *ekey, *gkey = NULL;
+ int funcp, dir;
+ unsigned int rsize;
+ if (!s || !s->index) { return NULL; }
+ if (optarg) {
+ unit = grn_rec_userdef;
+ rsize = optarg->key_size;
+ funcp = optarg->func ? 1 : 0;
+ dir = (optarg->mode == grn_sort_ascending) ? -1 : 1;
+ } else {
+ unit = grn_rec_document;
+ rsize = grn_rec_unit_size(unit, sizeof(grn_id));
+ funcp = 0;
+ dir = 1;
+ }
+ if (funcp) {
+ gkey = GRN_MALLOC(rsize ? rsize : 8192);
+ if (!gkey) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "allocation for gkey failed !");
+ return NULL;
+ }
+ } else {
+ if (s->key_size <= rsize) { return NULL; }
+ }
+ if (!(c = grn_hash_cursor_open(s->ctx, s, NULL, 0, NULL, -1, 0))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_hash_cursor_open on grn_hash_group failed !");
+ if (gkey) { GRN_FREE(gkey); }
+ return NULL;
+ }
+ memcpy(&h, s, sizeof(grn_hash));
+ g = s;
+ s = &h;
+ if (grn_rhash_init(ctx, g, unit, rsize, s->record_unit, s->key_size, limit)) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_rhash_init in grn_hash_group failed !");
+ grn_hash_cursor_close(s->ctx, c);
+ if (gkey) { GRN_FREE(gkey); }
+ return NULL;
+ }
+ while ((rh = grn_hash_cursor_next(ctx, c))) {
+ grn_hash_cursor_get_key_value(ctx, c, (void **)&key, NULL, (void **)&ri);
+ if (funcp) {
+ if (optarg->func((grn_records *)s,
+ (grn_recordh *)(intptr_t)rh, gkey, optarg->func_arg)) { continue; }
+ ekey = key;
+ } else {
+ gkey = key;
+ ekey = key + rsize;
+ }
+ {
+ grn_rset_recinfo *gri;
+ if (grn_hash_add(ctx, g, gkey, rsize, (void **)&gri, NULL)) {
+ grn_rhash_add_subrec(g, gri, ri->score, ekey, dir);
+ }
+ }
+ }
+ grn_hash_cursor_close(s->ctx, c);
+ grn_rhash_fin(s->ctx, s);
+ if (funcp) { GRN_FREE(gkey); }
+ return g;
+}
+
+grn_rc
+grn_rhash_subrec_info(grn_hash *s, grn_id rh, int index,
+ grn_id *rid, int *section, int *pos, int *score, void **subrec)
+{
+ grn_rset_posinfo *pi;
+ grn_rset_recinfo *ri;
+ int *p, unit_size = GRN_RSET_SCORE_SIZE + s->subrec_size;
+ if (!s || !rh || index < 0) { return GRN_INVALID_ARGUMENT; }
+ if ((unsigned int)index >= s->max_n_subrecs) { return GRN_INVALID_ARGUMENT; }
+ {
+ entry_str *ee;
+ if (!grn_hash_bitmap_at(ctx, s, rh)) { return GRN_INVALID_ARGUMENT; }
+ ee = grn_hash_entry_at(ctx, s, rh, 0);
+ if (!ee) { return GRN_INVALID_ARGUMENT; }
+ pi = (grn_rset_posinfo *)get_key(ctx, s, ee);
+ ri = get_value(s, ee);
+ if (!pi || !ri) { return GRN_INVALID_ARGUMENT; }
+ }
+ if (index >= ri->n_subrecs) { return GRN_INVALID_ARGUMENT; }
+ p = (int *)(ri->subrecs + index * unit_size);
+ if (score) { *score = p[0]; }
+ if (subrec) { *subrec = &p[1]; }
+ switch (s->record_unit) {
+ case grn_rec_document :
+ if (rid) { *rid = pi->rid; }
+ if (section) { *section = (s->subrec_unit != grn_rec_userdef) ? p[1] : 0; }
+ if (pos) { *pos = (s->subrec_unit == grn_rec_position) ? p[2] : 0; }
+ break;
+ case grn_rec_section :
+ if (rid) { *rid = pi->rid; }
+ if (section) { *section = pi->sid; }
+ if (pos) { *pos = (s->subrec_unit == grn_rec_position) ? p[1] : 0; }
+ break;
+ default :
+ pi = (grn_rset_posinfo *)&p[1];
+ switch (s->subrec_unit) {
+ case grn_rec_document :
+ if (rid) { *rid = pi->rid; }
+ if (section) { *section = 0; }
+ if (pos) { *pos = 0; }
+ break;
+ case grn_rec_section :
+ if (rid) { *rid = pi->rid; }
+ if (section) { *section = pi->sid; }
+ if (pos) { *pos = 0; }
+ break;
+ case grn_rec_position :
+ if (rid) { *rid = pi->rid; }
+ if (section) { *section = pi->sid; }
+ if (pos) { *pos = pi->pos; }
+ break;
+ default :
+ if (rid) { *rid = 0; }
+ if (section) { *section = 0; }
+ if (pos) { *pos = 0; }
+ break;
+ }
+ break;
+ }
+ return GRN_SUCCESS;
+}
+#endif /* USE_GRN_INDEX2 */
diff --git a/storage/mroonga/vendor/groonga/lib/hash.h b/storage/mroonga/vendor/groonga/lib/hash.h
new file mode 100644
index 00000000000..59cdec1223b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/hash.h
@@ -0,0 +1,346 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_HASH_H
+#define GRN_HASH_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifndef GRN_CTX_H
+#include "ctx.h"
+#endif /* GRN_CTX_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**** grn_tiny_array ****/
+
+/*
+ * grn_tiny_array_init() accepts a logical OR of the following flags.
+ * Note that other flags, such as (1 << 30), will be ignored.
+ *
+ * - GRN_TINY_ARRAY_CLEAR specifies to initialize a new block with zeros.
+ * It is valid only iff specified with GRN_TINY_ARRAY_USE_MALLOC.
+ * - GRN_TINY_ARRAY_THREADSAFE specifies to create a critical section when
+ * allocating memory.
+ * - GRN_TINY_ARRAY_USE_MALLOC specifies to use GRN_MALLOC/CALLOC/FREE instead
+ * of GRN_CTX_ALLOC/FREE.
+ */
+#define GRN_TINY_ARRAY_CLEAR (1 << 0)
+#define GRN_TINY_ARRAY_THREADSAFE (1 << 1)
+#define GRN_TINY_ARRAY_USE_MALLOC (1 << 2)
+
+/*
+ * - GRN_TINY_ARRAY_FACTOR is the global parameter of grn_tiny_array.
+ * - GRN_TINY_ARRAY_GET_OFFSET() returns the offset of a specified block.
+ * - GRN_TINY_ARRAY_BASE_BLOCK_SIZE is the number of elements in the first
+ * block.
+ * - GRN_TINY_ARRAY_GET_BLOCK_SIZE() returns the number of elements in a
+ * specified block.
+ * - GRN_TINY_ARRAY_NUM_BLOCKS is the maximum number of blocks.
+ */
+#define GRN_TINY_ARRAY_FACTOR 0
+#define GRN_TINY_ARRAY_GET_OFFSET(block_id) \
+ (1 << ((block_id) << GRN_TINY_ARRAY_FACTOR))
+#define GRN_TINY_ARRAY_BASE_BLOCK_SIZE \
+ (GRN_TINY_ARRAY_GET_OFFSET(1) - GRN_TINY_ARRAY_GET_OFFSET(0))
+#define GRN_TINY_ARRAY_GET_BLOCK_SIZE(block_id) \
+ (GRN_TINY_ARRAY_BASE_BLOCK_SIZE * GRN_TINY_ARRAY_GET_OFFSET(block_id))
+#define GRN_TINY_ARRAY_NUM_BLOCKS (32 >> GRN_TINY_ARRAY_FACTOR)
+
+/*
+ * grn_tiny_array uses several blocks to emulate an array.
+ * The k-th block, blocks[k - 1], consists of 2^(k-1) elements.
+ */
+typedef struct _grn_tiny_array grn_tiny_array;
+
+struct _grn_tiny_array {
+ grn_ctx *ctx;
+ grn_id max;
+ uint16_t element_size;
+ uint16_t flags;
+ void *blocks[GRN_TINY_ARRAY_NUM_BLOCKS];
+ grn_critical_section lock;
+};
+
+#define GRN_TINY_ARRAY_EACH(array, head, tail, key, value, block) do { \
+ int _block_id; \
+ const grn_id _head = (head); \
+ const grn_id _tail = (tail); \
+ for (_block_id = 0, (key) = (_head); \
+ _block_id < GRN_TINY_ARRAY_NUM_BLOCKS && (key) <= (_tail); \
+ _block_id++) { \
+ int _id = GRN_TINY_ARRAY_GET_BLOCK_SIZE(_block_id); \
+ (value) = (array)->blocks[_block_id]; \
+ if (value) { \
+ while (_id-- && (key) <= (_tail)) { \
+ { \
+ block \
+ } \
+ (key)++; \
+ (value) = (void *)((byte *)(value) + (array)->element_size); \
+ } \
+ } else { \
+ (key) += _id; \
+ } \
+ } \
+} while (0)
+
+GRN_API void grn_tiny_array_init(grn_ctx *ctx, grn_tiny_array *array,
+ uint16_t element_size, uint16_t flags);
+GRN_API void grn_tiny_array_fin(grn_tiny_array *array);
+GRN_API void *grn_tiny_array_at(grn_tiny_array *array, grn_id id);
+GRN_API grn_id grn_tiny_array_id(grn_tiny_array *array,
+ const void *element_address);
+
+/**** grn_tiny_bitmap ****/
+
+typedef struct _grn_tiny_bitmap grn_tiny_bitmap;
+
+struct _grn_tiny_bitmap {
+ grn_ctx *ctx;
+ void *blocks[GRN_TINY_ARRAY_NUM_BLOCKS];
+};
+
+/**** grn_array ****/
+
+#define GRN_ARRAY_TINY (0x01<<6)
+
+/*
+ * grn_array uses grn_io or grn_tiny_array to represent an array.
+ *
+ * To create a grn_tiny_array-based grn_array, specify the GRN_ARRAY_TINY flag
+ * to grn_array_create(). Note that a grn_tiny_array-based grn_array is not
+ * backed by a file.
+ */
+struct _grn_array {
+ grn_db_obj obj;
+ grn_ctx *ctx;
+ uint32_t value_size;
+ int32_t n_keys;
+ grn_table_sort_key *keys;
+ uint32_t *n_garbages;
+ uint32_t *n_entries;
+
+ /* For grn_io_array. */
+ grn_io *io;
+ struct grn_array_header *header;
+ uint32_t *lock;
+
+ /* For grn_tiny_array. */
+ uint32_t n_garbages_buf;
+ uint32_t n_entries_buf;
+ grn_id garbages;
+ grn_tiny_array array;
+ grn_tiny_bitmap bitmap;
+};
+
+struct _grn_array_cursor {
+ grn_db_obj obj;
+ grn_array *array;
+ grn_ctx *ctx;
+ grn_id curr_rec;
+ grn_id tail;
+ unsigned int rest;
+ int dir;
+};
+
+#define GRN_ARRAY_SIZE(array) (*((array)->n_entries))
+
+grn_rc grn_array_truncate(grn_ctx *ctx, grn_array *array);
+grn_rc grn_array_copy_sort_key(grn_ctx *ctx, grn_array *array,
+ grn_table_sort_key *keys, int n_keys);
+
+/* grn_table_queue */
+
+typedef struct _grn_table_queue grn_table_queue;
+
+struct _grn_table_queue {
+ grn_mutex mutex;
+ grn_cond cond;
+ grn_id head;
+ grn_id tail;
+ grn_id cap;
+ grn_bool unblock_requested;
+};
+
+GRN_API void grn_array_queue_lock_clear(grn_ctx *ctx, grn_array *array);
+GRN_API void grn_array_clear_curr_rec(grn_ctx *ctx, grn_array *array);
+GRN_API grn_table_queue *grn_array_queue(grn_ctx *ctx, grn_array *array);
+GRN_API uint32_t grn_table_queue_size(grn_table_queue *queue);
+GRN_API void grn_table_queue_head_increment(grn_table_queue *queue);
+GRN_API void grn_table_queue_tail_increment(grn_table_queue *queue);
+GRN_API grn_id grn_table_queue_head(grn_table_queue *queue);
+GRN_API grn_id grn_table_queue_tail(grn_table_queue *queue);
+
+/**** grn_hash ****/
+
+#define GRN_HASH_TINY (0x01<<6)
+#define GRN_HASH_MAX_KEY_SIZE GRN_TABLE_MAX_KEY_SIZE
+
+struct _grn_hash {
+ grn_db_obj obj;
+ grn_ctx *ctx;
+ uint32_t key_size;
+ grn_encoding encoding;
+ uint32_t value_size;
+ uint32_t entry_size;
+ uint32_t *n_garbages;
+ uint32_t *n_entries;
+ uint32_t *max_offset;
+ grn_obj *tokenizer;
+ grn_obj *normalizer;
+
+ /* For grn_io_hash. */
+ grn_io *io;
+ struct grn_hash_header *header;
+ uint32_t *lock;
+ // uint32_t nref;
+ // unsigned int max_n_subrecs;
+ // unsigned int record_size;
+ // unsigned int subrec_size;
+ // grn_rec_unit record_unit;
+ // grn_rec_unit subrec_unit;
+ // uint8_t arrayp;
+ // grn_recordh *curr_rec;
+ // grn_set_cursor *cursor;
+ // int limit;
+ // void *userdata;
+ // grn_id subrec_id;
+
+ /* For grn_tiny_hash. */
+ uint32_t max_offset_;
+ uint32_t n_garbages_;
+ uint32_t n_entries_;
+ grn_id *index;
+ grn_id garbages;
+ grn_tiny_array a;
+ grn_tiny_bitmap bitmap;
+};
+
+/* Header of grn_io_hash. */
+struct grn_hash_header {
+ uint32_t flags;
+ grn_encoding encoding;
+ uint32_t key_size;
+ uint32_t value_size;
+ grn_id tokenizer;
+ uint32_t curr_rec;
+ int32_t curr_key;
+ uint32_t idx_offset;
+ uint32_t entry_size;
+ uint32_t max_offset;
+ uint32_t n_entries;
+ uint32_t n_garbages;
+ uint32_t lock;
+ grn_id normalizer;
+ uint32_t reserved[15];
+ grn_id garbages[GRN_HASH_MAX_KEY_SIZE];
+ grn_table_queue queue;
+};
+
+struct _grn_hash_cursor {
+ grn_db_obj obj;
+ grn_hash *hash;
+ grn_ctx *ctx;
+ grn_id curr_rec;
+ grn_id tail;
+ unsigned int rest;
+ int dir;
+};
+
+/* deprecated */
+
+#define GRN_TABLE_SORT_BY_KEY 0
+#define GRN_TABLE_SORT_BY_ID (1L<<1)
+#define GRN_TABLE_SORT_BY_VALUE (1L<<2)
+#define GRN_TABLE_SORT_RES_ID 0
+#define GRN_TABLE_SORT_RES_KEY (1L<<3)
+#define GRN_TABLE_SORT_AS_BIN 0
+#define GRN_TABLE_SORT_AS_NUMBER (1L<<4)
+#define GRN_TABLE_SORT_AS_SIGNED 0
+#define GRN_TABLE_SORT_AS_UNSIGNED (1L<<5)
+#define GRN_TABLE_SORT_AS_INT32 0
+#define GRN_TABLE_SORT_AS_INT64 (1L<<6)
+#define GRN_TABLE_SORT_NO_PROC 0
+#define GRN_TABLE_SORT_WITH_PROC (1L<<7)
+
+typedef struct _grn_table_sort_optarg grn_table_sort_optarg;
+
+struct _grn_table_sort_optarg {
+ grn_table_sort_flags flags;
+ int (*compar)(grn_ctx *ctx,
+ grn_obj *table1, void *target1, unsigned int target1_size,
+ grn_obj *table2, void *target2, unsigned int target2_size,
+ void *compare_arg);
+ void *compar_arg;
+ grn_obj *proc;
+ int offset;
+};
+
+GRN_API int grn_hash_sort(grn_ctx *ctx, grn_hash *hash, int limit,
+ grn_array *result, grn_table_sort_optarg *optarg);
+
+grn_rc grn_hash_lock(grn_ctx *ctx, grn_hash *hash, int timeout);
+grn_rc grn_hash_unlock(grn_ctx *ctx, grn_hash *hash);
+grn_rc grn_hash_clear_lock(grn_ctx *ctx, grn_hash *hash);
+
+#define GRN_HASH_SIZE(hash) (*((hash)->n_entries))
+
+/* private */
+typedef enum {
+ grn_rec_document = 0,
+ grn_rec_section,
+ grn_rec_position,
+ grn_rec_userdef,
+ grn_rec_none
+} grn_rec_unit;
+
+GRN_API grn_rc grn_hash_truncate(grn_ctx *ctx, grn_hash *hash);
+
+int grn_rec_unit_size(grn_rec_unit unit, int rec_size);
+
+const char * _grn_hash_key(grn_ctx *ctx, grn_hash *hash, grn_id id, uint32_t *key_size);
+
+int grn_hash_get_key_value(grn_ctx *ctx, grn_hash *hash, grn_id id,
+ void *keybuf, int bufsize, void *valuebuf);
+
+int _grn_hash_get_key_value(grn_ctx *ctx, grn_hash *hash, grn_id id,
+ void **key, void **value);
+
+grn_id grn_hash_next(grn_ctx *ctx, grn_hash *hash, grn_id id);
+
+/* only valid for hash tables, GRN_OBJ_KEY_VAR_SIZE && GRN_HASH_TINY */
+const char *_grn_hash_strkey_by_val(void *v, uint16_t *size);
+
+const char *grn_hash_get_value_(grn_ctx *ctx, grn_hash *hash, grn_id id, uint32_t *size);
+
+grn_rc grn_hash_remove(grn_ctx *ctx, const char *path);
+grn_rc grn_array_remove(grn_ctx *ctx, const char *path);
+
+grn_id grn_hash_at(grn_ctx *ctx, grn_hash *hash, grn_id id);
+grn_id grn_array_at(grn_ctx *ctx, grn_array *array, grn_id id);
+
+void grn_hash_check(grn_ctx *ctx, grn_hash *hash);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_HASH_H */
diff --git a/storage/mroonga/vendor/groonga/lib/icudump.c b/storage/mroonga/vendor/groonga/lib/icudump.c
new file mode 100644
index 00000000000..2cbc15c249c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/icudump.c
@@ -0,0 +1,294 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2010 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include <stdio.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <string.h>
+#include <unicode/utf.h>
+#include <unicode/uchar.h>
+#include <unicode/unorm.h>
+#include <unicode/ustring.h>
+
+#define MAX_UNICODE 0x110000
+#define BUF_SIZE 0x100
+
+static int
+ucs2utf(unsigned int i, unsigned char *buf)
+{
+ unsigned char *p = buf;
+ if (i < 0x80) {
+ *p++ = i;
+ } else {
+ if (i < 0x800) {
+ *p++ = (i >> 6) | 0xc0;
+ } else {
+ if (i < 0x00010000) {
+ *p++ = (i >> 12) | 0xe0;
+ } else {
+ if (i < 0x00200000) {
+ *p++ = (i >> 18) | 0xf0;
+ } else {
+ if (i < 0x04000000) {
+ *p++ = (i >> 24) | 0xf8;
+ } else if (i < 0x80000000) {
+ *p++ = (i >> 30) | 0xfc;
+ *p++ = ((i >> 24) & 0x3f) | 0x80;
+ }
+ *p++ = ((i >> 18) & 0x3f) | 0x80;
+ }
+ *p++ = ((i >> 12) & 0x3f) | 0x80;
+ }
+ *p++ = ((i >> 6) & 0x3f) | 0x80;
+ }
+ *p++ = (0x3f & i) | 0x80;
+ }
+ *p = '\0';
+ return (p - buf);
+}
+
+void
+blockcode(void)
+{
+ UChar32 ch;
+ unsigned char *p, src[7];
+ UBlockCode code, lc = -1;
+ for (ch = 1; ch < MAX_UNICODE; ch++) {
+ if (!U_IS_UNICODE_CHAR(ch)) { continue; }
+ code = ublock_getCode(ch);
+ if (code != lc) {
+ ucs2utf(ch, src);
+ for (p = src; *p; p++) {
+ printf("%x:", *p);
+ }
+ printf("\t%04x\t%d\n", ch, code);
+ }
+ lc = code;
+ }
+}
+
+int
+normalize(const char *str, char *res, UNormalizationMode mode)
+{
+ UErrorCode rc;
+ int32_t ulen, nlen;
+ UChar ubuf[BUF_SIZE], nbuf[BUF_SIZE];
+ rc = U_ZERO_ERROR;
+ u_strFromUTF8(ubuf, BUF_SIZE, &ulen, str, -1, &rc);
+ if (rc != U_ZERO_ERROR /*&& rc != U_STRING_NOT_TERMINATED_WARNING*/) {
+ return -1;
+ }
+ rc = U_ZERO_ERROR;
+ nlen = unorm_normalize(ubuf, ulen, mode, 0, nbuf, BUF_SIZE, &rc);
+ if (rc != U_ZERO_ERROR /*&& rc != U_STRING_NOT_TERMINATED_WARNING*/) {
+ return -1;
+ }
+ rc = U_ZERO_ERROR;
+ u_strToUTF8(res, BUF_SIZE, NULL, nbuf, nlen, &rc);
+ if (rc != U_ZERO_ERROR /*&& rc != U_BUFFER_OVERFLOW_ERROR*/) {
+ return -1;
+ }
+ return 0;
+}
+
+void
+dump(UNormalizationMode mode)
+{
+ UChar32 ch;
+ char str[7], norm[BUF_SIZE];
+ for (ch = 1; ch < MAX_UNICODE; ch++) {
+ if (!U_IS_UNICODE_CHAR(ch)) { continue; }
+ ucs2utf(ch, (unsigned char *)str);
+ if (normalize(str, norm, mode)) {
+ printf("ch=%04x error occure\n", ch);
+ continue;
+ }
+ if (strcmp(norm, str)) {
+ printf("%04x\t%s\t%s\n", ch, str, norm);
+ }
+ }
+}
+
+void
+ccdump(void)
+{
+ UChar32 ch;
+ char str[7], nfd[BUF_SIZE], nfc[BUF_SIZE];
+ for (ch = 1; ch < MAX_UNICODE; ch++) {
+ if (!U_IS_UNICODE_CHAR(ch)) { continue; }
+ ucs2utf(ch, (unsigned char *)str);
+ if (normalize(str, nfd, UNORM_NFD)) {
+ printf("ch=%04x error occure\n", ch);
+ continue;
+ }
+ if (normalize(str, nfc, UNORM_NFC)) {
+ printf("ch=%04x error occure\n", ch);
+ continue;
+ }
+ if (strcmp(nfd, nfc)) {
+ printf("%04x\t%s\t%s\n", ch, nfd, nfc);
+ }
+ }
+}
+
+enum {
+ ctype_null = 0,
+ ctype_alpha,
+ ctype_digit,
+ ctype_symbol,
+ ctype_hiragana,
+ ctype_katakana,
+ ctype_kanji,
+ ctype_others
+};
+
+static const char *ctypes[] = {
+ "grn_str_null",
+ "grn_str_alpha",
+ "grn_str_digit",
+ "grn_str_symbol",
+ "grn_str_hiragana",
+ "grn_str_katakana",
+ "grn_str_kanji",
+ "grn_str_others"
+};
+
+void
+gcdump(void)
+{
+ UChar32 ch;
+ unsigned char *p, src[7];
+ int ctype, lc = -1;
+ for (ch = 1; ch < MAX_UNICODE; ch++) {
+ UCharCategory cat;
+ UBlockCode code;
+ if (!U_IS_UNICODE_CHAR(ch)) { continue; }
+ code = ublock_getCode(ch);
+ switch (code) {
+ case UBLOCK_CJK_RADICALS_SUPPLEMENT: /* cjk radicals */
+ case UBLOCK_KANGXI_RADICALS: /* kanji radicals */
+ case UBLOCK_BOPOMOFO: /* bopomofo letter */
+ case UBLOCK_HANGUL_COMPATIBILITY_JAMO: /* hangul letter */
+ case UBLOCK_KANBUN: /* kaeri ten used in kanbun ex. re-ten */
+ case UBLOCK_BOPOMOFO_EXTENDED: /* bopomofo extended letter */
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A: /* cjk letter */
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS: /* cjk letter */
+ case UBLOCK_YI_SYLLABLES: /* Yi syllables */
+ case UBLOCK_YI_RADICALS: /* Yi radicals */
+ case UBLOCK_HANGUL_SYLLABLES: /* hangul syllables */
+ case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS: /* cjk letter */
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B: /* cjk letter */
+ case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT: /* cjk letter */
+ case UBLOCK_CJK_STROKES: /* kakijun*/
+ ctype = ctype_kanji;
+ break;
+ case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION: /* symbols ex. JIS mark */
+ case UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS: /* ex. (kabu) */
+ case UBLOCK_CJK_COMPATIBILITY: /* symbols ex. ton doll */
+ case UBLOCK_CJK_COMPATIBILITY_FORMS: /* symbols ex. tategaki kagi-kakko */
+ ctype = ctype_symbol;
+ break;
+ case UBLOCK_HIRAGANA:
+ ctype = ctype_hiragana;
+ break;
+ case UBLOCK_KATAKANA:
+ case UBLOCK_KATAKANA_PHONETIC_EXTENSIONS:
+ ctype = ctype_katakana;
+ break;
+ default:
+ cat = u_charType(ch);
+ switch (cat) {
+ case U_UPPERCASE_LETTER:
+ case U_LOWERCASE_LETTER:
+ case U_TITLECASE_LETTER:
+ case U_MODIFIER_LETTER:
+ case U_OTHER_LETTER:
+ ctype = ctype_alpha;
+ break;
+ case U_DECIMAL_DIGIT_NUMBER:
+ case U_LETTER_NUMBER:
+ case U_OTHER_NUMBER:
+ ctype = ctype_digit;
+ break;
+ case U_DASH_PUNCTUATION:
+ case U_START_PUNCTUATION:
+ case U_END_PUNCTUATION:
+ case U_CONNECTOR_PUNCTUATION:
+ case U_OTHER_PUNCTUATION:
+ case U_MATH_SYMBOL:
+ case U_CURRENCY_SYMBOL:
+ case U_MODIFIER_SYMBOL:
+ case U_OTHER_SYMBOL:
+ ctype = ctype_symbol;
+ break;
+ default:
+ ctype = ctype_others;
+ break;
+ }
+ break;
+ }
+ if (ctype != lc) {
+ ucs2utf(ch, src);
+ for (p = src; *p; p++) {
+ printf("%x:", *p);
+ }
+ printf("\t%04x\t%s\n", ch, ctypes[ctype]);
+ }
+ lc = ctype;
+ }
+}
+
+struct option options[] = {
+ {"bc", 0, NULL, 'b'},
+ {"nfd", 0, NULL, 'd'},
+ {"nfkd", 0, NULL, 'D'},
+ {"nfc", 0, NULL, 'c'},
+ {"nfkc", 0, NULL, 'C'},
+ {"cc", 0, NULL, 'o'},
+ {"gc", 0, NULL, 'g'},
+};
+
+int
+main(int argc, char **argv)
+{
+ switch (getopt_long(argc, argv, "bdDcCog", options, NULL)) {
+ case 'b' :
+ blockcode();
+ break;
+ case 'd' :
+ dump(UNORM_NFD);
+ break;
+ case 'D' :
+ dump(UNORM_NFKD);
+ break;
+ case 'c' :
+ dump(UNORM_NFC);
+ break;
+ case 'C' :
+ dump(UNORM_NFKC);
+ break;
+ case 'o' :
+ ccdump();
+ break;
+ case 'g' :
+ gcdump();
+ break;
+ default :
+ fputs("usage: icudump --[bc|nfd|nfkd|nfc|nfkc|cc|gc]\n", stderr);
+ break;
+ }
+ return 0;
+}
diff --git a/storage/mroonga/vendor/groonga/lib/ii.c b/storage/mroonga/vendor/groonga/lib/ii.c
new file mode 100644
index 00000000000..f3b67628818
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/ii.c
@@ -0,0 +1,7396 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "groonga_in.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "ii.h"
+#include "ctx_impl.h"
+#include "token.h"
+#include "pat.h"
+#include "db.h"
+#include "output.h"
+#include "util.h"
+
+#define MAX_PSEG 0x20000
+#define S_CHUNK (1 << GRN_II_W_CHUNK)
+#define W_SEGMENT 18
+#define S_SEGMENT (1 << W_SEGMENT)
+#define N_CHUNKS_PER_FILE (GRN_IO_FILE_SIZE >> W_SEGMENT)
+#define W_ARRAY_ELEMENT 3
+#define S_ARRAY_ELEMENT (1 << W_ARRAY_ELEMENT)
+#define W_ARRAY (W_SEGMENT - W_ARRAY_ELEMENT)
+#define ARRAY_MASK_IN_A_SEGMENT ((1 << W_ARRAY) - 1)
+#define NOT_ASSIGNED 0xffffffff
+
+#define S_GARBAGE (1<<12)
+
+#define CHUNK_SPLIT 0x80000000
+#define CHUNK_SPLIT_THRESHOLD 0x60000
+
+#define MAX_N_ELEMENTS 5
+
+#define LSEG(pos) ((pos) >> 16)
+#define LPOS(pos) (((pos) & 0xffff) << 2)
+#define SEG2POS(seg,pos) ((((uint32_t)(seg)) << 16) + (((uint32_t)(pos)) >> 2))
+
+#define NEXT_ADDR(p) (((byte *)(p)) + sizeof(*(p)))
+
+#ifndef S_IRUSR
+# define S_IRUSR 0400
+#endif /* S_IRUSR */
+#ifndef S_IWUSR
+# define S_IWUSR 0200
+#endif /* S_IWUSR */
+
+/* segment */
+
+inline static uint32_t
+segment_get(grn_ctx *ctx, grn_ii *ii)
+{
+ uint32_t pseg;
+ if (ii->header->bgqtail == ((ii->header->bgqhead + 1) & (GRN_II_BGQSIZE - 1))) {
+ pseg = ii->header->bgqbody[ii->header->bgqtail];
+ ii->header->bgqtail = (ii->header->bgqtail + 1) & (GRN_II_BGQSIZE - 1);
+ } else {
+ pseg = ii->header->pnext;
+#ifndef CUT_OFF_COMPATIBILITY
+ if (!pseg) {
+ int i;
+ uint32_t pmax = 0;
+ char *used = GRN_CALLOC(MAX_PSEG);
+ if (!used) { return MAX_PSEG; }
+ for (i = 0; i < GRN_II_MAX_LSEG; i++) {
+ if ((pseg = ii->header->ainfo[i]) != NOT_ASSIGNED) {
+ if (pseg > pmax) { pmax = pseg; }
+ used[pseg] = 1;
+ }
+ if ((pseg = ii->header->binfo[i]) != NOT_ASSIGNED) {
+ if (pseg > pmax) { pmax = pseg; }
+ used[pseg] = 1;
+ }
+ }
+ for (pseg = 0; pseg < MAX_PSEG && used[pseg]; pseg++) ;
+ GRN_FREE(used);
+ ii->header->pnext = pmax + 1;
+ } else
+#endif /* CUT_OFF_COMPATIBILITY */
+ if (ii->header->pnext < MAX_PSEG) { ii->header->pnext++; }
+ }
+ return pseg;
+}
+
+inline static grn_rc
+segment_get_clear(grn_ctx *ctx, grn_ii *ii, uint32_t *pseg)
+{
+ uint32_t seg = segment_get(ctx, ii);
+ if (seg < MAX_PSEG) {
+ void *p = NULL;
+ GRN_IO_SEG_REF(ii->seg, seg, p);
+ if (!p) { return GRN_NO_MEMORY_AVAILABLE; }
+ memset(p, 0, S_SEGMENT);
+ GRN_IO_SEG_UNREF(ii->seg, seg);
+ *pseg = seg;
+ return GRN_SUCCESS;
+ } else {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+}
+
+inline static grn_rc
+buffer_segment_new(grn_ctx *ctx, grn_ii *ii, uint32_t *segno)
+{
+ uint32_t lseg, pseg;
+ if (*segno < GRN_II_MAX_LSEG) {
+ if (ii->header->binfo[*segno] != NOT_ASSIGNED) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ lseg = *segno;
+ } else {
+ for (lseg = 0; lseg < GRN_II_MAX_LSEG; lseg++) {
+ if (ii->header->binfo[lseg] == NOT_ASSIGNED) { break; }
+ }
+ if (lseg == GRN_II_MAX_LSEG) { return GRN_NO_MEMORY_AVAILABLE; }
+ *segno = lseg;
+ }
+ pseg = segment_get(ctx, ii);
+ if (pseg < MAX_PSEG) {
+ ii->header->binfo[lseg] = pseg;
+ if (lseg >= ii->header->bmax) { ii->header->bmax = lseg + 1; }
+ return GRN_SUCCESS;
+ } else {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+}
+
+static grn_rc
+buffer_segment_reserve(grn_ctx *ctx, grn_ii *ii,
+ uint32_t *lseg0, uint32_t *pseg0,
+ uint32_t *lseg1, uint32_t *pseg1)
+{
+ uint32_t i = 0;
+ for (;; i++) {
+ if (i == GRN_II_MAX_LSEG) { return GRN_NO_MEMORY_AVAILABLE; }
+ if (ii->header->binfo[i] == NOT_ASSIGNED) { break; }
+ }
+ *lseg0 = i++;
+ for (;; i++) {
+ if (i == GRN_II_MAX_LSEG) { return GRN_NO_MEMORY_AVAILABLE; }
+ if (ii->header->binfo[i] == NOT_ASSIGNED) { break; }
+ }
+ *lseg1 = i;
+ if ((*pseg0 = segment_get(ctx, ii)) == MAX_PSEG) { return GRN_NO_MEMORY_AVAILABLE; }
+ if ((*pseg1 = segment_get(ctx, ii)) == MAX_PSEG) { return GRN_NO_MEMORY_AVAILABLE; }
+ /*
+ {
+ uint32_t pseg;
+ char *used = GRN_CALLOC(MAX_PSEG);
+ if (!used) { return GRN_NO_MEMORY_AVAILABLE; }
+ for (i = 0; i < GRN_II_MAX_LSEG; i++) {
+ if ((pseg = ii->header->ainfo[i]) != NOT_ASSIGNED) { used[pseg] = 1; }
+ if ((pseg = ii->header->binfo[i]) != NOT_ASSIGNED) { used[pseg] = 1; }
+ }
+ for (pseg = 0;; pseg++) {
+ if (pseg == MAX_PSEG) { GRN_FREE(used); return GRN_NO_MEMORY_AVAILABLE; }
+ if (!used[pseg]) { break; }
+ }
+ *pseg0 = pseg++;
+ for (;; pseg++) {
+ if (pseg == MAX_PSEG) { GRN_FREE(used); return GRN_NO_MEMORY_AVAILABLE; }
+ if (!used[pseg]) { break; }
+ }
+ *pseg1 = pseg;
+ GRN_FREE(used);
+ }
+ */
+ return GRN_SUCCESS;
+}
+
+#define BGQENQUE(lseg) do {\
+ if (ii->header->binfo[lseg] != NOT_ASSIGNED) {\
+ ii->header->bgqbody[ii->header->bgqhead] = ii->header->binfo[lseg];\
+ ii->header->bgqhead = (ii->header->bgqhead + 1) & (GRN_II_BGQSIZE - 1);\
+ GRN_ASSERT(ii->header->bgqhead != ii->header->bgqtail);\
+ }\
+} while (0)
+
+inline static void
+buffer_segment_update(grn_ii *ii, uint32_t lseg, uint32_t pseg)
+{
+ BGQENQUE(lseg);
+ // smb_wmb();
+ ii->header->binfo[lseg] = pseg;
+ if (lseg >= ii->header->bmax) { ii->header->bmax = lseg + 1; }
+}
+
+inline static void
+buffer_segment_clear(grn_ii *ii, uint32_t lseg)
+{
+ BGQENQUE(lseg);
+ // smb_wmb();
+ ii->header->binfo[lseg] = NOT_ASSIGNED;
+}
+
+/* chunk */
+
+#define HEADER_CHUNK_AT(ii,offset) \
+ ((((ii)->header->chunks[((offset) >> 3)]) >> ((offset) & 7)) & 1)
+
+#define HEADER_CHUNK_ON(ii,offset) \
+ (((ii)->header->chunks[((offset) >> 3)]) |= (1 << ((offset) & 7)))
+
+#define HEADER_CHUNK_OFF(ii,offset) \
+ (((ii)->header->chunks[((offset) >> 3)]) &= ~(1 << ((offset) & 7)))
+
+#define N_GARBAGES_TH 1
+
+#define N_GARBAGES ((S_GARBAGE - (sizeof(uint32_t) * 4))/(sizeof(uint32_t)))
+
+typedef struct {
+ uint32_t head;
+ uint32_t tail;
+ uint32_t nrecs;
+ uint32_t next;
+ uint32_t recs[N_GARBAGES];
+} grn_ii_ginfo;
+
+#define WIN_MAP2(chunk,ctx,iw,seg,pos,size,mode)\
+ grn_io_win_map2(chunk, ctx, iw,\
+ ((seg) >> GRN_II_N_CHUNK_VARIATION),\
+ (((seg) & ((1 << GRN_II_N_CHUNK_VARIATION) - 1)) << GRN_II_W_LEAST_CHUNK) + (pos),\
+ size,mode)
+/*
+static int new_histogram[32];
+static int free_histogram[32];
+*/
+static grn_rc
+chunk_new(grn_ctx *ctx, grn_ii *ii, uint32_t *res, uint32_t size)
+{
+ /*
+ if (size) {
+ int m, es = size - 1;
+ GRN_BIT_SCAN_REV(es, m);
+ m++;
+ new_histogram[m]++;
+ }
+ */
+ if (size > S_CHUNK) {
+ int i, j;
+ uint32_t n = (size + S_CHUNK - 1) >> GRN_II_W_CHUNK;
+ for (i = 0, j = -1; i < GRN_II_MAX_CHUNK; i++) {
+ if (HEADER_CHUNK_AT(ii, i)) {
+ j = i;
+ } else {
+ if (i == j + n) {
+ j++;
+ *res = j << GRN_II_N_CHUNK_VARIATION;
+ for (; j <= i; j++) { HEADER_CHUNK_ON(ii, j); }
+ return GRN_SUCCESS;
+ }
+ }
+ }
+ GRN_LOG(ctx, GRN_LOG_CRIT, "index full. requested chunk_size=%d.", size);
+ return GRN_NO_MEMORY_AVAILABLE;
+ } else {
+ uint32_t *vp;
+ int m, aligned_size;
+ if (size > (1 << GRN_II_W_LEAST_CHUNK)) {
+ int es = size - 1;
+ GRN_BIT_SCAN_REV(es, m);
+ m++;
+ } else {
+ m = GRN_II_W_LEAST_CHUNK;
+ }
+ aligned_size = 1 << (m - GRN_II_W_LEAST_CHUNK);
+ if (ii->header->ngarbages[m - GRN_II_W_LEAST_CHUNK] > N_GARBAGES_TH) {
+ grn_ii_ginfo *ginfo;
+ uint32_t *gseg;
+ grn_io_win iw, iw_;
+ iw_.addr = NULL;
+ gseg = &ii->header->garbages[m - GRN_II_W_LEAST_CHUNK];
+ while (*gseg != NOT_ASSIGNED) {
+ ginfo = WIN_MAP2(ii->chunk, ctx, &iw, *gseg, 0, S_GARBAGE, grn_io_rdwr);
+ //GRN_IO_SEG_MAP2(ii->chunk, *gseg, ginfo);
+ if (!ginfo) {
+ if (iw_.addr) { grn_io_win_unmap2(&iw_); }
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ if (ginfo->next != NOT_ASSIGNED || ginfo->nrecs > N_GARBAGES_TH) {
+ *res = ginfo->recs[ginfo->tail];
+ if (++ginfo->tail == N_GARBAGES) { ginfo->tail = 0; }
+ ginfo->nrecs--;
+ ii->header->ngarbages[m - GRN_II_W_LEAST_CHUNK]--;
+ if (!ginfo->nrecs) {
+ HEADER_CHUNK_OFF(ii, *gseg);
+ *gseg = ginfo->next;
+ }
+ if (iw_.addr) { grn_io_win_unmap2(&iw_); }
+ grn_io_win_unmap2(&iw);
+ return GRN_SUCCESS;
+ }
+ if (iw_.addr) { grn_io_win_unmap2(&iw_); }
+ iw_ = iw;
+ gseg = &ginfo->next;
+ }
+ if (iw_.addr) { grn_io_win_unmap2(&iw_); }
+ }
+ vp = &ii->header->free_chunks[m - GRN_II_W_LEAST_CHUNK];
+ if (*vp == NOT_ASSIGNED) {
+ int i = 0;
+ while (HEADER_CHUNK_AT(ii, i)) {
+ if (++i >= GRN_II_MAX_CHUNK) { return GRN_NO_MEMORY_AVAILABLE; }
+ }
+ HEADER_CHUNK_ON(ii, i);
+ *vp = i << GRN_II_N_CHUNK_VARIATION;
+ }
+ *res = *vp;
+ *vp += 1 << (m - GRN_II_W_LEAST_CHUNK);
+ if (!(*vp & ((1 << GRN_II_N_CHUNK_VARIATION) - 1))) {
+ *vp = NOT_ASSIGNED;
+ }
+ return GRN_SUCCESS;
+ }
+}
+
+static grn_rc
+chunk_free(grn_ctx *ctx, grn_ii *ii, uint32_t offset, uint32_t dummy, uint32_t size)
+{
+ /*
+ if (size) {
+ int m, es = size - 1;
+ GRN_BIT_SCAN_REV(es, m);
+ m++;
+ free_histogram[m]++;
+ }
+ */
+ grn_io_win iw, iw_;
+ grn_ii_ginfo *ginfo;
+ uint32_t seg, m, *gseg;
+ seg = offset >> GRN_II_N_CHUNK_VARIATION;
+ if (size > S_CHUNK) {
+ int n = (size + S_CHUNK - 1) >> GRN_II_W_CHUNK;
+ for (; n--; seg++) { HEADER_CHUNK_OFF(ii, seg); }
+ return GRN_SUCCESS;
+ }
+ if (size > (1 << GRN_II_W_LEAST_CHUNK)) {
+ int es = size - 1;
+ GRN_BIT_SCAN_REV(es, m);
+ m++;
+ } else {
+ m = GRN_II_W_LEAST_CHUNK;
+ }
+ gseg = &ii->header->garbages[m - GRN_II_W_LEAST_CHUNK];
+ iw_.addr = NULL;
+ while (*gseg != NOT_ASSIGNED) {
+ ginfo = WIN_MAP2(ii->chunk, ctx, &iw, *gseg, 0, S_GARBAGE, grn_io_rdwr);
+ // GRN_IO_SEG_MAP2(ii->chunk, *gseg, ginfo);
+ if (!ginfo) {
+ if (iw_.addr) { grn_io_win_unmap2(&iw_); }
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ if (ginfo->nrecs < N_GARBAGES) { break; }
+ if (iw_.addr) { grn_io_win_unmap2(&iw_); }
+ iw_ = iw;
+ gseg = &ginfo->next;
+ }
+ if (*gseg == NOT_ASSIGNED) {
+ grn_rc rc;
+ if ((rc = chunk_new(ctx, ii, gseg, S_GARBAGE))) {
+ if (iw_.addr) { grn_io_win_unmap2(&iw_); }
+ return rc;
+ }
+ ginfo = WIN_MAP2(ii->chunk, ctx, &iw, *gseg, 0, S_GARBAGE, grn_io_rdwr);
+ /*
+ uint32_t i = 0;
+ while (HEADER_CHUNK_AT(ii, i)) {
+ if (++i >= GRN_II_MAX_CHUNK) { return GRN_NO_MEMORY_AVAILABLE; }
+ }
+ HEADER_CHUNK_ON(ii, i);
+ *gseg = i;
+ GRN_IO_SEG_MAP2(ii->chunk, *gseg, ginfo);
+ */
+ if (!ginfo) {
+ if (iw_.addr) { grn_io_win_unmap2(&iw_); }
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ ginfo->head = 0;
+ ginfo->tail = 0;
+ ginfo->nrecs = 0;
+ ginfo->next = NOT_ASSIGNED;
+ }
+ if (iw_.addr) { grn_io_win_unmap2(&iw_); }
+ ginfo->recs[ginfo->head] = offset;
+ if (++ginfo->head == N_GARBAGES) { ginfo->head = 0; }
+ ginfo->nrecs++;
+ grn_io_win_unmap2(&iw);
+ ii->header->ngarbages[m - GRN_II_W_LEAST_CHUNK]++;
+ return GRN_SUCCESS;
+}
+
+/*
+inline static grn_rc
+chunk_new(grn_ii *ii, uint32_t *res, uint32_t size)
+{
+ int i, j;
+ uint32_t n = (size + S_CHUNK - 1) >> GRN_II_W_CHUNK;
+ uint32_t base_seg = grn_io_base_seg(ii->chunk);
+ for (i = 0, j = -1; i < GRN_II_MAX_CHUNK; i++) {
+ if (HEADER_CHUNK_AT(ii, i)) {
+ j = i;
+ } else {
+ if (i == j + n) {
+ j++;
+ if (res) { *res = j; }
+ for (; j <= i; j++) { HEADER_CHUNK_ON(ii, j); }
+ return GRN_SUCCESS;
+ }
+ // todo : cut off
+ if ((i + base_seg)/ N_CHUNKS_PER_FILE !=
+ (i + base_seg + 1) / N_CHUNKS_PER_FILE) { j = i; }
+ }
+ }
+ GRN_LOG(ctx, GRN_LOG_CRIT, "index full.");
+ return GRN_NO_MEMORY_AVAILABLE;
+}
+
+static void
+chunk_free(grn_ii *ii, int offset, uint32_t size1, uint32_t size2)
+{
+ uint32_t i = offset + ((size1 + S_CHUNK - 1) >> GRN_II_W_CHUNK);
+ uint32_t n = offset + ((size2 + S_CHUNK - 1) >> GRN_II_W_CHUNK);
+ for (; i < n; i++) { HEADER_CHUNK_OFF(ii, i); }
+}
+*/
+
+#define UNIT_SIZE 0x80
+#define UNIT_MASK (UNIT_SIZE - 1)
+
+/* <generated> */
+static uint8_t *
+pack_1(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ v = *p++ << 7;
+ v += *p++ << 6;
+ v += *p++ << 5;
+ v += *p++ << 4;
+ v += *p++ << 3;
+ v += *p++ << 2;
+ v += *p++ << 1;
+ *rp++ = v + *p++;
+ return rp;
+}
+static uint8_t *
+unpack_1(uint32_t *p, uint8_t *dp)
+{
+ *p++ = (*dp >> 7);
+ *p++ = ((*dp >> 6) & 0x1);
+ *p++ = ((*dp >> 5) & 0x1);
+ *p++ = ((*dp >> 4) & 0x1);
+ *p++ = ((*dp >> 3) & 0x1);
+ *p++ = ((*dp >> 2) & 0x1);
+ *p++ = ((*dp >> 1) & 0x1);
+ *p++ = (*dp++ & 0x1);
+ return dp;
+}
+static uint8_t *
+pack_2(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ v = *p++ << 6;
+ v += *p++ << 4;
+ v += *p++ << 2;
+ *rp++ = v + *p++;
+ v = *p++ << 6;
+ v += *p++ << 4;
+ v += *p++ << 2;
+ *rp++ = v + *p++;
+ return rp;
+}
+static uint8_t *
+unpack_2(uint32_t *p, uint8_t *dp)
+{
+ *p++ = (*dp >> 6);
+ *p++ = ((*dp >> 4) & 0x3);
+ *p++ = ((*dp >> 2) & 0x3);
+ *p++ = (*dp++ & 0x3);
+ *p++ = (*dp >> 6);
+ *p++ = ((*dp >> 4) & 0x3);
+ *p++ = ((*dp >> 2) & 0x3);
+ *p++ = (*dp++ & 0x3);
+ return dp;
+}
+static uint8_t *
+pack_3(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ v = *p++ << 5;
+ v += *p++ << 2;
+ *rp++ = v + (*p >> 1); v = *p++ << 7;
+ v += *p++ << 4;
+ v += *p++ << 1;
+ *rp++ = v + (*p >> 2); v = *p++ << 6;
+ v += *p++ << 3;
+ *rp++ = v + *p++;
+ return rp;
+}
+static uint8_t *
+unpack_3(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ *p++ = (*dp >> 5);
+ *p++ = ((*dp >> 2) & 0x7);
+ v = ((*dp++ << 1) & 0x7); *p++ = v + (*dp >> 7);
+ *p++ = ((*dp >> 4) & 0x7);
+ *p++ = ((*dp >> 1) & 0x7);
+ v = ((*dp++ << 2) & 0x7); *p++ = v + (*dp >> 6);
+ *p++ = ((*dp >> 3) & 0x7);
+ *p++ = (*dp++ & 0x7);
+ return dp;
+}
+static uint8_t *
+pack_4(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ v = *p++ << 4;
+ *rp++ = v + *p++;
+ v = *p++ << 4;
+ *rp++ = v + *p++;
+ v = *p++ << 4;
+ *rp++ = v + *p++;
+ v = *p++ << 4;
+ *rp++ = v + *p++;
+ return rp;
+}
+static uint8_t *
+unpack_4(uint32_t *p, uint8_t *dp)
+{
+ *p++ = (*dp >> 4);
+ *p++ = (*dp++ & 0xf);
+ *p++ = (*dp >> 4);
+ *p++ = (*dp++ & 0xf);
+ *p++ = (*dp >> 4);
+ *p++ = (*dp++ & 0xf);
+ *p++ = (*dp >> 4);
+ *p++ = (*dp++ & 0xf);
+ return dp;
+}
+static uint8_t *
+pack_5(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ v = *p++ << 3;
+ *rp++ = v + (*p >> 2); v = *p++ << 6;
+ v += *p++ << 1;
+ *rp++ = v + (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 1); v = *p++ << 7;
+ v += *p++ << 2;
+ *rp++ = v + (*p >> 3); v = *p++ << 5;
+ *rp++ = v + *p++;
+ return rp;
+}
+static uint8_t *
+unpack_5(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ *p++ = (*dp >> 3);
+ v = ((*dp++ << 2) & 0x1f); *p++ = v + (*dp >> 6);
+ *p++ = ((*dp >> 1) & 0x1f);
+ v = ((*dp++ << 4) & 0x1f); *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 1) & 0x1f); *p++ = v + (*dp >> 7);
+ *p++ = ((*dp >> 2) & 0x1f);
+ v = ((*dp++ << 3) & 0x1f); *p++ = v + (*dp >> 5);
+ *p++ = (*dp++ & 0x1f);
+ return dp;
+}
+static uint8_t *
+pack_6(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ v = *p++ << 2;
+ *rp++ = v + (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 2); v = *p++ << 6;
+ *rp++ = v + *p++;
+ v = *p++ << 2;
+ *rp++ = v + (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 2); v = *p++ << 6;
+ *rp++ = v + *p++;
+ return rp;
+}
+static uint8_t *
+unpack_6(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ *p++ = (*dp >> 2);
+ v = ((*dp++ << 4) & 0x3f); *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 2) & 0x3f); *p++ = v + (*dp >> 6);
+ *p++ = (*dp++ & 0x3f);
+ *p++ = (*dp >> 2);
+ v = ((*dp++ << 4) & 0x3f); *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 2) & 0x3f); *p++ = v + (*dp >> 6);
+ *p++ = (*dp++ & 0x3f);
+ return dp;
+}
+static uint8_t *
+pack_7(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ v = *p++ << 1;
+ *rp++ = v + (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 5); v = *p++ << 3;
+ *rp++ = v + (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 3); v = *p++ << 5;
+ *rp++ = v + (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 1); v = *p++ << 7;
+ *rp++ = v + *p++;
+ return rp;
+}
+static uint8_t *
+unpack_7(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ *p++ = (*dp >> 1);
+ v = ((*dp++ << 6) & 0x7f); *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 5) & 0x7f); *p++ = v + (*dp >> 3);
+ v = ((*dp++ << 4) & 0x7f); *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 3) & 0x7f); *p++ = v + (*dp >> 5);
+ v = ((*dp++ << 2) & 0x7f); *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 1) & 0x7f); *p++ = v + (*dp >> 7);
+ *p++ = (*dp++ & 0x7f);
+ return dp;
+}
+static uint8_t *
+pack_8(uint32_t *p, uint8_t *rp)
+{
+ *rp++ = *p++;
+ *rp++ = *p++;
+ *rp++ = *p++;
+ *rp++ = *p++;
+ *rp++ = *p++;
+ *rp++ = *p++;
+ *rp++ = *p++;
+ *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_8(uint32_t *p, uint8_t *dp)
+{
+ *p++ = *dp++;
+ *p++ = *dp++;
+ *p++ = *dp++;
+ *p++ = *dp++;
+ *p++ = *dp++;
+ *p++ = *dp++;
+ *p++ = *dp++;
+ *p++ = *dp++;
+ return dp;
+}
+static uint8_t *
+pack_9(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 1); v = *p++ << 7;
+ *rp++ = v + (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 3); v = *p++ << 5;
+ *rp++ = v + (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 5); v = *p++ << 3;
+ *rp++ = v + (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 7); v = *p++ << 1;
+ *rp++ = v + (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_9(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 1; *p++ = v + (*dp >> 7);
+ v = ((*dp++ << 2) & 0x1ff); *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 3) & 0x1ff); *p++ = v + (*dp >> 5);
+ v = ((*dp++ << 4) & 0x1ff); *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 5) & 0x1ff); *p++ = v + (*dp >> 3);
+ v = ((*dp++ << 6) & 0x1ff); *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 7) & 0x1ff); *p++ = v + (*dp >> 1);
+ v = ((*dp++ << 8) & 0x1ff); *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_10(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_10(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 4) & 0x3ff); *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 6) & 0x3ff); *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 8) & 0x3ff); *p++ = v + *dp++;
+ v = *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 4) & 0x3ff); *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 6) & 0x3ff); *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 8) & 0x3ff); *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_11(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 3); v = *p++ << 5;
+ *rp++ = v + (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 9); *rp++ = (*p >> 1); v = *p++ << 7;
+ *rp++ = v + (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 7); v = *p++ << 1;
+ *rp++ = v + (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 5); v = *p++ << 3;
+ *rp++ = v + (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_11(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 3; *p++ = v + (*dp >> 5);
+ v = ((*dp++ << 6) & 0x7ff); *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 9) & 0x7ff); v += *dp++ << 1; *p++ = v + (*dp >> 7);
+ v = ((*dp++ << 4) & 0x7ff); *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 7) & 0x7ff); *p++ = v + (*dp >> 1);
+ v = ((*dp++ << 10) & 0x7ff); v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 5) & 0x7ff); *p++ = v + (*dp >> 3);
+ v = ((*dp++ << 8) & 0x7ff); *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_12(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_12(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 8) & 0xfff); *p++ = v + *dp++;
+ v = *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 8) & 0xfff); *p++ = v + *dp++;
+ v = *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 8) & 0xfff); *p++ = v + *dp++;
+ v = *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 8) & 0xfff); *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_13(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 5); v = *p++ << 3;
+ *rp++ = v + (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 7); v = *p++ << 1;
+ *rp++ = v + (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 9); *rp++ = (*p >> 1); v = *p++ << 7;
+ *rp++ = v + (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 11); *rp++ = (*p >> 3); v = *p++ << 5;
+ *rp++ = v + (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_13(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 5; *p++ = v + (*dp >> 3);
+ v = ((*dp++ << 10) & 0x1fff); v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 7) & 0x1fff); *p++ = v + (*dp >> 1);
+ v = ((*dp++ << 12) & 0x1fff); v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 9) & 0x1fff); v += *dp++ << 1; *p++ = v + (*dp >> 7);
+ v = ((*dp++ << 6) & 0x1fff); *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 11) & 0x1fff); v += *dp++ << 3; *p++ = v + (*dp >> 5);
+ v = ((*dp++ << 8) & 0x1fff); *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_14(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_14(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 12) & 0x3fff); v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 10) & 0x3fff); v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 8) & 0x3fff); *p++ = v + *dp++;
+ v = *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 12) & 0x3fff); v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 10) & 0x3fff); v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 8) & 0x3fff); *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_15(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 7); v = *p++ << 1;
+ *rp++ = v + (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 13); *rp++ = (*p >> 5); v = *p++ << 3;
+ *rp++ = v + (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 11); *rp++ = (*p >> 3); v = *p++ << 5;
+ *rp++ = v + (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 9); *rp++ = (*p >> 1); v = *p++ << 7;
+ *rp++ = v + (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_15(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 7; *p++ = v + (*dp >> 1);
+ v = ((*dp++ << 14) & 0x7fff); v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 13) & 0x7fff); v += *dp++ << 5; *p++ = v + (*dp >> 3);
+ v = ((*dp++ << 12) & 0x7fff); v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 11) & 0x7fff); v += *dp++ << 3; *p++ = v + (*dp >> 5);
+ v = ((*dp++ << 10) & 0x7fff); v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 9) & 0x7fff); v += *dp++ << 1; *p++ = v + (*dp >> 7);
+ v = ((*dp++ << 8) & 0x7fff); *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_16(uint32_t *p, uint8_t *rp)
+{
+ *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_16(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_17(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 9); *rp++ = (*p >> 1); v = *p++ << 7;
+ *rp++ = v + (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 11); *rp++ = (*p >> 3); v = *p++ << 5;
+ *rp++ = v + (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 13); *rp++ = (*p >> 5); v = *p++ << 3;
+ *rp++ = v + (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 15); *rp++ = (*p >> 7); v = *p++ << 1;
+ *rp++ = v + (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_17(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 9; v += *dp++ << 1; *p++ = v + (*dp >> 7);
+ v = ((*dp++ << 10) & 0x1ffff); v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 11) & 0x1ffff); v += *dp++ << 3; *p++ = v + (*dp >> 5);
+ v = ((*dp++ << 12) & 0x1ffff); v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 13) & 0x1ffff); v += *dp++ << 5; *p++ = v + (*dp >> 3);
+ v = ((*dp++ << 14) & 0x1ffff); v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 15) & 0x1ffff); v += *dp++ << 7; *p++ = v + (*dp >> 1);
+ v = ((*dp++ << 16) & 0x1ffff); v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_18(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_18(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 12) & 0x3ffff); v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 14) & 0x3ffff); v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 16) & 0x3ffff); v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 12) & 0x3ffff); v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 14) & 0x3ffff); v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 16) & 0x3ffff); v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_19(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 11); *rp++ = (*p >> 3); v = *p++ << 5;
+ *rp++ = v + (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 17); *rp++ = (*p >> 9); *rp++ = (*p >> 1); v = *p++ << 7;
+ *rp++ = v + (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 15); *rp++ = (*p >> 7); v = *p++ << 1;
+ *rp++ = v + (*p >> 18); *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 13); *rp++ = (*p >> 5); v = *p++ << 3;
+ *rp++ = v + (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_19(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 11; v += *dp++ << 3; *p++ = v + (*dp >> 5);
+ v = ((*dp++ << 14) & 0x7ffff); v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 17) & 0x7ffff); v += *dp++ << 9; v += *dp++ << 1; *p++ = v + (*dp >> 7);
+ v = ((*dp++ << 12) & 0x7ffff); v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 15) & 0x7ffff); v += *dp++ << 7; *p++ = v + (*dp >> 1);
+ v = ((*dp++ << 18) & 0x7ffff); v += *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 13) & 0x7ffff); v += *dp++ << 5; *p++ = v + (*dp >> 3);
+ v = ((*dp++ << 16) & 0x7ffff); v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_20(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_20(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 16) & 0xfffff); v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 16) & 0xfffff); v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 16) & 0xfffff); v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 16) & 0xfffff); v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_21(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 13); *rp++ = (*p >> 5); v = *p++ << 3;
+ *rp++ = v + (*p >> 18); *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 15); *rp++ = (*p >> 7); v = *p++ << 1;
+ *rp++ = v + (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 17); *rp++ = (*p >> 9); *rp++ = (*p >> 1); v = *p++ << 7;
+ *rp++ = v + (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 19); *rp++ = (*p >> 11); *rp++ = (*p >> 3); v = *p++ << 5;
+ *rp++ = v + (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_21(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 13; v += *dp++ << 5; *p++ = v + (*dp >> 3);
+ v = ((*dp++ << 18) & 0x1fffff); v += *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 15) & 0x1fffff); v += *dp++ << 7; *p++ = v + (*dp >> 1);
+ v = ((*dp++ << 20) & 0x1fffff); v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 17) & 0x1fffff); v += *dp++ << 9; v += *dp++ << 1; *p++ = v + (*dp >> 7);
+ v = ((*dp++ << 14) & 0x1fffff); v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 19) & 0x1fffff); v += *dp++ << 11; v += *dp++ << 3; *p++ = v + (*dp >> 5);
+ v = ((*dp++ << 16) & 0x1fffff); v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_22(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 18); *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 18); *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_22(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 14; v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 20) & 0x3fffff); v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 18) & 0x3fffff); v += *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 16) & 0x3fffff); v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 14; v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 20) & 0x3fffff); v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 18) & 0x3fffff); v += *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 16) & 0x3fffff); v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_23(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 15); *rp++ = (*p >> 7); v = *p++ << 1;
+ *rp++ = v + (*p >> 22); *rp++ = (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 21); *rp++ = (*p >> 13); *rp++ = (*p >> 5); v = *p++ << 3;
+ *rp++ = v + (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 19); *rp++ = (*p >> 11); *rp++ = (*p >> 3); v = *p++ << 5;
+ *rp++ = v + (*p >> 18); *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 17); *rp++ = (*p >> 9); *rp++ = (*p >> 1); v = *p++ << 7;
+ *rp++ = v + (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_23(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 15; v += *dp++ << 7; *p++ = v + (*dp >> 1);
+ v = ((*dp++ << 22) & 0x7fffff); v += *dp++ << 14; v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 21) & 0x7fffff); v += *dp++ << 13; v += *dp++ << 5; *p++ = v + (*dp >> 3);
+ v = ((*dp++ << 20) & 0x7fffff); v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 19) & 0x7fffff); v += *dp++ << 11; v += *dp++ << 3; *p++ = v + (*dp >> 5);
+ v = ((*dp++ << 18) & 0x7fffff); v += *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 17) & 0x7fffff); v += *dp++ << 9; v += *dp++ << 1; *p++ = v + (*dp >> 7);
+ v = ((*dp++ << 16) & 0x7fffff); v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_24(uint32_t *p, uint8_t *rp)
+{
+ *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_24(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_25(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 17); *rp++ = (*p >> 9); *rp++ = (*p >> 1); v = *p++ << 7;
+ *rp++ = v + (*p >> 18); *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 19); *rp++ = (*p >> 11); *rp++ = (*p >> 3); v = *p++ << 5;
+ *rp++ = v + (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 21); *rp++ = (*p >> 13); *rp++ = (*p >> 5); v = *p++ << 3;
+ *rp++ = v + (*p >> 22); *rp++ = (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 23); *rp++ = (*p >> 15); *rp++ = (*p >> 7); v = *p++ << 1;
+ *rp++ = v + (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_25(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 17; v += *dp++ << 9; v += *dp++ << 1; *p++ = v + (*dp >> 7);
+ v = ((*dp++ << 18) & 0x1ffffff); v += *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 19) & 0x1ffffff); v += *dp++ << 11; v += *dp++ << 3; *p++ = v + (*dp >> 5);
+ v = ((*dp++ << 20) & 0x1ffffff); v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 21) & 0x1ffffff); v += *dp++ << 13; v += *dp++ << 5; *p++ = v + (*dp >> 3);
+ v = ((*dp++ << 22) & 0x1ffffff); v += *dp++ << 14; v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 23) & 0x1ffffff); v += *dp++ << 15; v += *dp++ << 7; *p++ = v + (*dp >> 1);
+ v = ((*dp++ << 24) & 0x1ffffff); v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_26(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 18); *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 22); *rp++ = (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 18); *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 22); *rp++ = (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_26(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 18; v += *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 20) & 0x3ffffff); v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 22) & 0x3ffffff); v += *dp++ << 14; v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 24) & 0x3ffffff); v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 18; v += *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 20) & 0x3ffffff); v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 22) & 0x3ffffff); v += *dp++ << 14; v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 24) & 0x3ffffff); v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_27(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 19); *rp++ = (*p >> 11); *rp++ = (*p >> 3); v = *p++ << 5;
+ *rp++ = v + (*p >> 22); *rp++ = (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 25); *rp++ = (*p >> 17); *rp++ = (*p >> 9); *rp++ = (*p >> 1); v = *p++ << 7;
+ *rp++ = v + (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 23); *rp++ = (*p >> 15); *rp++ = (*p >> 7); v = *p++ << 1;
+ *rp++ = v + (*p >> 26); *rp++ = (*p >> 18); *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 21); *rp++ = (*p >> 13); *rp++ = (*p >> 5); v = *p++ << 3;
+ *rp++ = v + (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_27(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 19; v += *dp++ << 11; v += *dp++ << 3; *p++ = v + (*dp >> 5);
+ v = ((*dp++ << 22) & 0x7ffffff); v += *dp++ << 14; v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 25) & 0x7ffffff); v += *dp++ << 17; v += *dp++ << 9; v += *dp++ << 1; *p++ = v + (*dp >> 7);
+ v = ((*dp++ << 20) & 0x7ffffff); v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 23) & 0x7ffffff); v += *dp++ << 15; v += *dp++ << 7; *p++ = v + (*dp >> 1);
+ v = ((*dp++ << 26) & 0x7ffffff); v += *dp++ << 18; v += *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 21) & 0x7ffffff); v += *dp++ << 13; v += *dp++ << 5; *p++ = v + (*dp >> 3);
+ v = ((*dp++ << 24) & 0x7ffffff); v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_28(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_28(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 20; v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 24) & 0xfffffff); v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 20; v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 24) & 0xfffffff); v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 20; v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 24) & 0xfffffff); v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 20; v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 24) & 0xfffffff); v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_29(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 21); *rp++ = (*p >> 13); *rp++ = (*p >> 5); v = *p++ << 3;
+ *rp++ = v + (*p >> 26); *rp++ = (*p >> 18); *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 23); *rp++ = (*p >> 15); *rp++ = (*p >> 7); v = *p++ << 1;
+ *rp++ = v + (*p >> 28); *rp++ = (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 25); *rp++ = (*p >> 17); *rp++ = (*p >> 9); *rp++ = (*p >> 1); v = *p++ << 7;
+ *rp++ = v + (*p >> 22); *rp++ = (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 27); *rp++ = (*p >> 19); *rp++ = (*p >> 11); *rp++ = (*p >> 3); v = *p++ << 5;
+ *rp++ = v + (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_29(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 21; v += *dp++ << 13; v += *dp++ << 5; *p++ = v + (*dp >> 3);
+ v = ((*dp++ << 26) & 0x1fffffff); v += *dp++ << 18; v += *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 23) & 0x1fffffff); v += *dp++ << 15; v += *dp++ << 7; *p++ = v + (*dp >> 1);
+ v = ((*dp++ << 28) & 0x1fffffff); v += *dp++ << 20; v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 25) & 0x1fffffff); v += *dp++ << 17; v += *dp++ << 9; v += *dp++ << 1; *p++ = v + (*dp >> 7);
+ v = ((*dp++ << 22) & 0x1fffffff); v += *dp++ << 14; v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 27) & 0x1fffffff); v += *dp++ << 19; v += *dp++ << 11; v += *dp++ << 3; *p++ = v + (*dp >> 5);
+ v = ((*dp++ << 24) & 0x1fffffff); v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_30(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 22); *rp++ = (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 28); *rp++ = (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 26); *rp++ = (*p >> 18); *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 22); *rp++ = (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 28); *rp++ = (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 26); *rp++ = (*p >> 18); *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_30(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 22; v += *dp++ << 14; v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 28) & 0x3fffffff); v += *dp++ << 20; v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 26) & 0x3fffffff); v += *dp++ << 18; v += *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 24) & 0x3fffffff); v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 22; v += *dp++ << 14; v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 28) & 0x3fffffff); v += *dp++ << 20; v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 26) & 0x3fffffff); v += *dp++ << 18; v += *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 24) & 0x3fffffff); v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_31(uint32_t *p, uint8_t *rp)
+{
+ uint8_t v;
+ *rp++ = (*p >> 23); *rp++ = (*p >> 15); *rp++ = (*p >> 7); v = *p++ << 1;
+ *rp++ = v + (*p >> 30); *rp++ = (*p >> 22); *rp++ = (*p >> 14); *rp++ = (*p >> 6); v = *p++ << 2;
+ *rp++ = v + (*p >> 29); *rp++ = (*p >> 21); *rp++ = (*p >> 13); *rp++ = (*p >> 5); v = *p++ << 3;
+ *rp++ = v + (*p >> 28); *rp++ = (*p >> 20); *rp++ = (*p >> 12); *rp++ = (*p >> 4); v = *p++ << 4;
+ *rp++ = v + (*p >> 27); *rp++ = (*p >> 19); *rp++ = (*p >> 11); *rp++ = (*p >> 3); v = *p++ << 5;
+ *rp++ = v + (*p >> 26); *rp++ = (*p >> 18); *rp++ = (*p >> 10); *rp++ = (*p >> 2); v = *p++ << 6;
+ *rp++ = v + (*p >> 25); *rp++ = (*p >> 17); *rp++ = (*p >> 9); *rp++ = (*p >> 1); v = *p++ << 7;
+ *rp++ = v + (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_31(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 23; v += *dp++ << 15; v += *dp++ << 7; *p++ = v + (*dp >> 1);
+ v = ((*dp++ << 30) & 0x7fffffff); v += *dp++ << 22; v += *dp++ << 14; v += *dp++ << 6; *p++ = v + (*dp >> 2);
+ v = ((*dp++ << 29) & 0x7fffffff); v += *dp++ << 21; v += *dp++ << 13; v += *dp++ << 5; *p++ = v + (*dp >> 3);
+ v = ((*dp++ << 28) & 0x7fffffff); v += *dp++ << 20; v += *dp++ << 12; v += *dp++ << 4; *p++ = v + (*dp >> 4);
+ v = ((*dp++ << 27) & 0x7fffffff); v += *dp++ << 19; v += *dp++ << 11; v += *dp++ << 3; *p++ = v + (*dp >> 5);
+ v = ((*dp++ << 26) & 0x7fffffff); v += *dp++ << 18; v += *dp++ << 10; v += *dp++ << 2; *p++ = v + (*dp >> 6);
+ v = ((*dp++ << 25) & 0x7fffffff); v += *dp++ << 17; v += *dp++ << 9; v += *dp++ << 1; *p++ = v + (*dp >> 7);
+ v = ((*dp++ << 24) & 0x7fffffff); v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+static uint8_t *
+pack_32(uint32_t *p, uint8_t *rp)
+{
+ *rp++ = (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ *rp++ = (*p >> 24); *rp++ = (*p >> 16); *rp++ = (*p >> 8); *rp++ = *p++;
+ return rp;
+}
+static uint8_t *
+unpack_32(uint32_t *p, uint8_t *dp)
+{
+ uint32_t v;
+ v = *dp++ << 24; v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 24; v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 24; v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 24; v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 24; v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 24; v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 24; v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ v = *dp++ << 24; v += *dp++ << 16; v += *dp++ << 8; *p++ = v + *dp++;
+ return dp;
+}
+/* </generated> */
+
+static uint8_t *
+pack_(uint32_t *p, uint32_t i, int w, uint8_t *rp)
+{
+ while (i >= 8) {
+ switch (w) {
+ case 0 : break;
+ case 1 : rp = pack_1(p, rp); break;
+ case 2 : rp = pack_2(p, rp); break;
+ case 3 : rp = pack_3(p, rp); break;
+ case 4 : rp = pack_4(p, rp); break;
+ case 5 : rp = pack_5(p, rp); break;
+ case 6 : rp = pack_6(p, rp); break;
+ case 7 : rp = pack_7(p, rp); break;
+ case 8 : rp = pack_8(p, rp); break;
+ case 9 : rp = pack_9(p, rp); break;
+ case 10 : rp = pack_10(p, rp); break;
+ case 11 : rp = pack_11(p, rp); break;
+ case 12 : rp = pack_12(p, rp); break;
+ case 13 : rp = pack_13(p, rp); break;
+ case 14 : rp = pack_14(p, rp); break;
+ case 15 : rp = pack_15(p, rp); break;
+ case 16 : rp = pack_16(p, rp); break;
+ case 17 : rp = pack_17(p, rp); break;
+ case 18 : rp = pack_18(p, rp); break;
+ case 19 : rp = pack_19(p, rp); break;
+ case 20 : rp = pack_20(p, rp); break;
+ case 21 : rp = pack_21(p, rp); break;
+ case 22 : rp = pack_22(p, rp); break;
+ case 23 : rp = pack_23(p, rp); break;
+ case 24 : rp = pack_24(p, rp); break;
+ case 25 : rp = pack_25(p, rp); break;
+ case 26 : rp = pack_26(p, rp); break;
+ case 27 : rp = pack_27(p, rp); break;
+ case 28 : rp = pack_28(p, rp); break;
+ case 29 : rp = pack_29(p, rp); break;
+ case 30 : rp = pack_30(p, rp); break;
+ case 31 : rp = pack_31(p, rp); break;
+ case 32 : rp = pack_32(p, rp); break;
+ }
+ p += 8;
+ i -= 8;
+ }
+ {
+ int b;
+ uint8_t v;
+ uint32_t *pe = p + i;
+ for (b = 8 - w, v = 0; p < pe;) {
+ if (b > 0) {
+ v += *p++ << b;
+ b -= w;
+ } else if (b < 0) {
+ *rp++ = v + (*p >> -b);
+ b += 8;
+ v = 0;
+ } else {
+ *rp++ = v + *p++;
+ b = 8 - w;
+ v = 0;
+ }
+ }
+ if (b + w != 8) { *rp++ = v; }
+ return rp;
+ }
+}
+
+static uint8_t *
+pack(uint32_t *p, uint32_t i, uint8_t *freq, uint8_t *rp)
+{
+ int32_t k, w;
+ uint8_t ebuf[UNIT_SIZE], *ep = ebuf;
+ uint32_t s, *pe = p + i, r, th = i - (i >> 3);
+ for (w = 0, s = 0; w <= 32; w++) {
+ if ((s += freq[w]) >= th) { break; }
+ }
+ if (i == s) {
+ *rp++ = w;
+ return pack_(p, i, w, rp);
+ }
+ r = 1 << w;
+ *rp++ = w + 0x80;
+ *rp++ = i - s;
+ if (r >= UNIT_SIZE) {
+ uint32_t first, *last = &first;
+ for (k = 0; p < pe; p++, k++) {
+ if (*p >= r) {
+ GRN_B_ENC(*p - r, ep);
+ *last = k;
+ last = p;
+ }
+ }
+ *last = 0;
+ *rp++ = (uint8_t) first;
+ } else {
+ for (k = 0; p < pe; p++, k++) {
+ if (*p >= r) {
+ *ep++ = k;
+ GRN_B_ENC(*p - r, ep);
+ *p = 0;
+ }
+ }
+ }
+ rp = pack_(p - i, i, w, rp);
+ memcpy(rp, ebuf, ep - ebuf);
+ return rp + (ep - ebuf);
+}
+
+int
+grn_p_enc(grn_ctx *ctx, uint32_t *data, uint32_t data_size, uint8_t **res)
+{
+ uint8_t *rp, freq[33];
+ uint32_t j, *dp, *dpe, d, w, buf[UNIT_SIZE];
+ *res = rp = GRN_MALLOC(data_size * sizeof(uint32_t) * 2);
+ GRN_B_ENC(data_size, rp);
+ memset(freq, 0, 33);
+ for (j = 0, dp = data, dpe = dp + data_size; dp < dpe; j++, dp++) {
+ if (j == UNIT_SIZE) {
+ rp = pack(buf, j, freq, rp);
+ memset(freq, 0, 33);
+ j = 0;
+ }
+ if ((d = buf[j] = *dp)) {
+ GRN_BIT_SCAN_REV(d, w);
+ freq[w + 1]++;
+ } else {
+ freq[0]++;
+ }
+ }
+ if (j) { rp = pack(buf, j, freq, rp); }
+ return rp - *res;
+}
+
+#define USE_P_ENC (1<<0)
+#define CUT_OFF (1<<1)
+#define ODD (1<<2)
+
+typedef struct {
+ uint32_t *data;
+ uint32_t data_size;
+ uint32_t flags;
+} datavec;
+
+static grn_rc
+datavec_reset(grn_ctx *ctx, datavec *dv, uint32_t dvlen,
+ size_t unitsize, size_t totalsize)
+{
+ int i;
+ if (!dv[0].data || dv[dvlen].data < dv[0].data + totalsize) {
+ if (dv[0].data) { GRN_FREE(dv[0].data); }
+ if (!(dv[0].data = GRN_MALLOC(totalsize * sizeof(uint32_t)))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ dv[dvlen].data = dv[0].data + totalsize;
+ }
+ for (i = 1; i < dvlen; i++) {
+ dv[i].data = dv[i - 1].data + unitsize;
+ }
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+datavec_init(grn_ctx *ctx, datavec *dv, uint32_t dvlen,
+ size_t unitsize, size_t totalsize)
+{
+ int i;
+ if (!totalsize) {
+ memset(dv, 0, sizeof(datavec) * (dvlen + 1));
+ return GRN_SUCCESS;
+ }
+ if (!(dv[0].data = GRN_MALLOC(totalsize * sizeof(uint32_t)))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ dv[dvlen].data = dv[0].data + totalsize;
+ for (i = 1; i < dvlen; i++) {
+ dv[i].data = dv[i - 1].data + unitsize;
+ }
+ return GRN_SUCCESS;
+}
+
+static void
+datavec_fin(grn_ctx *ctx, datavec *dv)
+{
+ if (dv[0].data) { GRN_FREE(dv[0].data); }
+}
+
+size_t
+grn_p_encv(grn_ctx *ctx, datavec *dv, uint32_t dvlen, uint8_t *res)
+{
+ uint8_t *rp = res, freq[33];
+ uint32_t pgap, usep, l, df, data_size, *dp, *dpe;
+ if (!dvlen || !(df = dv[0].data_size)) { return 0; }
+ for (usep = 0, data_size = 0, l = 0; l < dvlen; l++) {
+ uint32_t dl = dv[l].data_size;
+ if (dl < df || ((dl > df) && (l != dvlen - 1))) {
+ /* invalid argument */
+ return 0;
+ }
+ usep += (dv[l].flags & USE_P_ENC) << l;
+ data_size += dl;
+ }
+ pgap = data_size - df * dvlen;
+ if (!usep) {
+ GRN_B_ENC((df << 1) + 1, rp);
+ for (l = 0; l < dvlen; l++) {
+ for (dp = dv[l].data, dpe = dp + dv[l].data_size; dp < dpe; dp++) {
+ GRN_B_ENC(*dp, rp);
+ }
+ }
+ } else {
+ uint32_t buf[UNIT_SIZE];
+ GRN_B_ENC((usep << 1), rp);
+ GRN_B_ENC(df, rp);
+ if (dv[dvlen - 1].flags & ODD) {
+ GRN_B_ENC(pgap, rp);
+ } else {
+ GRN_ASSERT(!pgap);
+ }
+ for (l = 0; l < dvlen; l++) {
+ dp = dv[l].data;
+ dpe = dp + dv[l].data_size;
+ if ((dv[l].flags & USE_P_ENC)) {
+ uint32_t j = 0, d;
+ memset(freq, 0, 33);
+ while (dp < dpe) {
+ if (j == UNIT_SIZE) {
+ rp = pack(buf, j, freq, rp);
+ memset(freq, 0, 33);
+ j = 0;
+ }
+ if ((d = buf[j++] = *dp++)) {
+ uint32_t w;
+ GRN_BIT_SCAN_REV(d, w);
+ freq[w + 1]++;
+ } else {
+ freq[0]++;
+ }
+ }
+ if (j) { rp = pack(buf, j, freq, rp); }
+ } else {
+ while (dp < dpe) { GRN_B_ENC(*dp++, rp); }
+ }
+ }
+ }
+ return rp - res;
+}
+
+#define GRN_B_DEC_CHECK(v,p,pe) do { \
+ uint8_t *_p = (uint8_t *)p; \
+ uint32_t _v; \
+ if (_p >= pe) { return 0; } \
+ _v = *_p++; \
+ switch (_v >> 4) { \
+ case 0x08 : \
+ if (_v == 0x8f) { \
+ if (_p + sizeof(uint32_t) > pe) { return 0; } \
+ memcpy(&_v, _p, sizeof(uint32_t)); \
+ _p += sizeof(uint32_t); \
+ } \
+ break; \
+ case 0x09 : \
+ if (_p + 3 > pe) { return 0; } \
+ _v = (_v - 0x90) * 0x100 + *_p++; \
+ _v = _v * 0x100 + *_p++; \
+ _v = _v * 0x100 + *_p++ + 0x20408f; \
+ break; \
+ case 0x0a : \
+ case 0x0b : \
+ if (_p + 2 > pe) { return 0; } \
+ _v = (_v - 0xa0) * 0x100 + *_p++; \
+ _v = _v * 0x100 + *_p++ + 0x408f; \
+ break; \
+ case 0x0c : \
+ case 0x0d : \
+ case 0x0e : \
+ case 0x0f : \
+ if (_p + 1 > pe) { return 0; } \
+ _v = (_v - 0xc0) * 0x100 + *_p++ + 0x8f; \
+ break; \
+ } \
+ v = _v; \
+ p = _p; \
+} while (0)
+
+static uint8_t *
+unpack(uint8_t *dp, uint8_t *dpe, int i, uint32_t *rp)
+{
+ uint8_t ne = 0, k = 0, w = *dp++;
+ uint32_t m, *p = rp;
+ if (w & 0x80) {
+ ne = *dp++;
+ w -= 0x80;
+ m = (1 << w) - 1;
+ if (m >= UNIT_MASK) { k = *dp++; }
+ } else {
+ m = (1 << w) - 1;
+ }
+ if (w) {
+ while (i >= 8) {
+ if (dp + w > dpe) { return NULL; }
+ switch (w) {
+ case 1 : dp = unpack_1(p, dp); break;
+ case 2 : dp = unpack_2(p, dp); break;
+ case 3 : dp = unpack_3(p, dp); break;
+ case 4 : dp = unpack_4(p, dp); break;
+ case 5 : dp = unpack_5(p, dp); break;
+ case 6 : dp = unpack_6(p, dp); break;
+ case 7 : dp = unpack_7(p, dp); break;
+ case 8 : dp = unpack_8(p, dp); break;
+ case 9 : dp = unpack_9(p, dp); break;
+ case 10 : dp = unpack_10(p, dp); break;
+ case 11 : dp = unpack_11(p, dp); break;
+ case 12 : dp = unpack_12(p, dp); break;
+ case 13 : dp = unpack_13(p, dp); break;
+ case 14 : dp = unpack_14(p, dp); break;
+ case 15 : dp = unpack_15(p, dp); break;
+ case 16 : dp = unpack_16(p, dp); break;
+ case 17 : dp = unpack_17(p, dp); break;
+ case 18 : dp = unpack_18(p, dp); break;
+ case 19 : dp = unpack_19(p, dp); break;
+ case 20 : dp = unpack_20(p, dp); break;
+ case 21 : dp = unpack_21(p, dp); break;
+ case 22 : dp = unpack_22(p, dp); break;
+ case 23 : dp = unpack_23(p, dp); break;
+ case 24 : dp = unpack_24(p, dp); break;
+ case 25 : dp = unpack_25(p, dp); break;
+ case 26 : dp = unpack_26(p, dp); break;
+ case 27 : dp = unpack_27(p, dp); break;
+ case 28 : dp = unpack_28(p, dp); break;
+ case 29 : dp = unpack_29(p, dp); break;
+ case 30 : dp = unpack_30(p, dp); break;
+ case 31 : dp = unpack_31(p, dp); break;
+ case 32 : dp = unpack_32(p, dp); break;
+ }
+ i -= 8;
+ p += 8;
+ }
+ {
+ int b;
+ uint32_t v, *pe;
+ for (b = 8 - w, v = 0, pe = p + i; p < pe && dp < dpe;) {
+ if (b > 0) {
+ *p++ = v + ((*dp >> b) & m);
+ b -= w;
+ v = 0;
+ } else if (b < 0) {
+ v += (*dp++ << -b) & m;
+ b += 8;
+ } else {
+ *p++ = v + (*dp++ & m);
+ b = 8 - w;
+ v = 0;
+ }
+ }
+ if (b + w != 8) { dp++; }
+ }
+ } else {
+ memset(p, 0, sizeof(uint32_t) * i);
+ }
+ if (ne) {
+ if (m >= UNIT_MASK) {
+ uint32_t *pp;
+ while (ne--) {
+ pp = &rp[k];
+ k = *pp;
+ GRN_B_DEC_CHECK(*pp, dp, dpe);
+ *pp += (m + 1);
+ }
+ } else {
+ while (ne--) {
+ k = *dp++;
+ GRN_B_DEC_CHECK(rp[k], dp, dpe);
+ rp[k] += (m + 1);
+ }
+ }
+ }
+ return dp;
+}
+
+int
+grn_p_dec(grn_ctx *ctx, uint8_t *data, uint32_t data_size, uint32_t nreq, uint32_t **res)
+{
+ uint8_t *dp = data, *dpe = data + data_size;
+ uint32_t rest, orig_size, *rp, *rpe;
+ GRN_B_DEC(orig_size, dp);
+ if (!orig_size) {
+ if (!nreq || nreq > data_size) { nreq = data_size; }
+ if ((*res = rp = GRN_MALLOC(nreq * 4))) {
+ for (rpe = rp + nreq; dp < data + data_size && rp < rpe; rp++) {
+ GRN_B_DEC(*rp, dp);
+ }
+ }
+ return rp - *res;
+ } else {
+ if (!(*res = rp = GRN_MALLOC(orig_size * sizeof(uint32_t)))) {
+ return 0;
+ }
+ if (!nreq || nreq > orig_size) { nreq = orig_size; }
+ for (rest = nreq; rest >= UNIT_SIZE; rest -= UNIT_SIZE) {
+ if (!(dp = unpack(dp, dpe, UNIT_SIZE, rp))) { return 0; }
+ rp += UNIT_SIZE;
+ }
+ if (rest) { if (!(dp = unpack(dp, dpe, rest, rp))) { return 0; } }
+ GRN_ASSERT(data + data_size == dp);
+ return nreq;
+ }
+}
+
+int
+grn_p_decv(grn_ctx *ctx, uint8_t *data, uint32_t data_size, datavec *dv, uint32_t dvlen)
+{
+ size_t size;
+ uint32_t df, l, i, *rp, nreq;
+ uint8_t *dp = data, *dpe = data + data_size;
+ if (!data_size) {
+ dv[0].data_size = 0;
+ return 0;
+ }
+ for (nreq = 0; nreq < dvlen; nreq++) {
+ if (dv[nreq].flags & CUT_OFF) { break; }
+ }
+ if (!nreq) { return 0; }
+ GRN_B_DEC_CHECK(df, dp, dpe);
+ if ((df & 1)) {
+ df >>= 1;
+ size = nreq == dvlen ? data_size : df * nreq;
+ if (dv[dvlen].data < dv[0].data + size) {
+ if (dv[0].data) { GRN_FREE(dv[0].data); }
+ if (!(rp = GRN_MALLOC(size * sizeof(uint32_t)))) { return 0; }
+ dv[dvlen].data = rp + size;
+ } else {
+ rp = dv[0].data;
+ }
+ for (l = 0; l < dvlen; l++) {
+ if (dv[l].flags & CUT_OFF) { break; }
+ dv[l].data = rp;
+ if (l < dvlen - 1) {
+ for (i = 0; i < df; i++, rp++) { GRN_B_DEC_CHECK(*rp, dp, dpe); }
+ } else {
+ for (i = 0; dp < dpe; i++, rp++) { GRN_B_DEC_CHECK(*rp, dp, dpe); }
+ }
+ dv[l].data_size = i;
+ }
+ } else {
+ uint32_t n, rest, usep = df >> 1;
+ GRN_B_DEC_CHECK(df, dp, dpe);
+ if (dv[dvlen -1].flags & ODD) {
+ GRN_B_DEC_CHECK(rest, dp, dpe);
+ } else {
+ rest = 0;
+ }
+ size = df * nreq + (nreq == dvlen ? rest : 0);
+ if (dv[dvlen].data < dv[0].data + size) {
+ if (dv[0].data) { GRN_FREE(dv[0].data); }
+ if (!(rp = GRN_MALLOC(size * sizeof(uint32_t)))) { return 0; }
+ dv[dvlen].data = rp + size;
+ } else {
+ rp = dv[0].data;
+ }
+ for (l = 0; l < dvlen; l++) {
+ if (dv[l].flags & CUT_OFF) { break; }
+ dv[l].data = rp;
+ dv[l].data_size = n = (l < dvlen - 1) ? df : df + rest;
+ if (usep & (1 << l)) {
+ for (; n >= UNIT_SIZE; n -= UNIT_SIZE) {
+ if (!(dp = unpack(dp, dpe, UNIT_SIZE, rp))) { return 0; }
+ rp += UNIT_SIZE;
+ }
+ if (n) {
+ if (!(dp = unpack(dp, dpe, n, rp))) { return 0; }
+ rp += n;
+ }
+ dv[l].flags |= USE_P_ENC;
+ } else {
+ for (; n; n--, rp++) {
+ GRN_B_DEC_CHECK(*rp, dp, dpe);
+ }
+ }
+ }
+ GRN_ASSERT(dp == dpe);
+ if (dp != dpe) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "data_size=%d, %" GRN_FMT_LLD,
+ data_size, (long long int)(dpe - dp));
+ }
+ }
+ return rp - dv[0].data;
+}
+
+int
+grn_b_enc(grn_ctx *ctx, uint32_t *data, uint32_t data_size, uint8_t **res)
+{
+ uint8_t *rp;
+ uint32_t *dp, i;
+ *res = rp = GRN_MALLOC(data_size * sizeof(uint32_t) * 2);
+ GRN_B_ENC(data_size, rp);
+ for (i = data_size, dp = data; i; i--, dp++) {
+ GRN_B_ENC(*dp, rp);
+ }
+ return rp - *res;
+}
+
+int
+grn_b_dec(grn_ctx *ctx, uint8_t *data, uint32_t data_size, uint32_t **res)
+{
+ uint32_t i, *rp, orig_size;
+ uint8_t *dp = data;
+ GRN_B_DEC(orig_size, dp);
+ *res = rp = GRN_MALLOC(orig_size * sizeof(uint32_t));
+ for (i = orig_size; i; i--, rp++) {
+ GRN_B_DEC(*rp, dp);
+ }
+ return orig_size;
+}
+
+/* buffer */
+
+typedef struct {
+ uint32_t tid;
+ uint32_t size_in_chunk;
+ uint32_t pos_in_chunk;
+ uint16_t size_in_buffer;
+ uint16_t pos_in_buffer;
+} buffer_term;
+
+typedef struct {
+ uint16_t step;
+ uint16_t jump;
+} buffer_rec;
+
+typedef struct {
+ uint32_t chunk;
+ uint32_t chunk_size;
+ uint32_t buffer_free;
+ uint16_t nterms;
+ uint16_t nterms_void;
+} buffer_header;
+
+struct grn_ii_buffer {
+ buffer_header header;
+ buffer_term terms[(S_SEGMENT - sizeof(buffer_header))/sizeof(buffer_term)];
+};
+
+typedef struct grn_ii_buffer buffer;
+
+inline static uint32_t
+buffer_open(grn_ctx *ctx, grn_ii *ii, uint32_t pos, buffer_term **bt, buffer **b)
+{
+ byte *p = NULL;
+ uint16_t lseg = (uint16_t) (LSEG(pos));
+ uint32_t pseg = ii->header->binfo[lseg];
+ if (pseg != NOT_ASSIGNED) {
+ GRN_IO_SEG_REF(ii->seg, pseg, p);
+ if (!p) { return NOT_ASSIGNED; }
+ if (b) { *b = (buffer *)p; }
+ if (bt) { *bt = (buffer_term *)(p + LPOS(pos)); }
+ }
+ return pseg;
+}
+
+inline static grn_rc
+buffer_close(grn_ctx *ctx, grn_ii *ii, uint32_t pseg)
+{
+ if (pseg >= MAX_PSEG) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "invalid pseg buffer_close(%d)", pseg);
+ return GRN_INVALID_ARGUMENT;
+ }
+ GRN_IO_SEG_UNREF(ii->seg, pseg);
+ return GRN_SUCCESS;
+}
+
+inline static uint32_t
+buffer_open_if_capable(grn_ctx *ctx, grn_ii *ii, int32_t seg, int size, buffer **b)
+{
+ uint32_t pseg, pos = SEG2POS(seg, 0);
+ if ((pseg = buffer_open(ctx, ii, pos, NULL, b)) != NOT_ASSIGNED) {
+ uint16_t nterms = (*b)->header.nterms - (*b)->header.nterms_void;
+ if (!((nterms < 4096 ||
+ (ii->header->total_chunk_size >> ((nterms >> 8) - 6))
+ > (*b)->header.chunk_size) &&
+ ((*b)->header.buffer_free >= size + sizeof(buffer_term)))) {
+ buffer_close(ctx, ii, pseg);
+ return NOT_ASSIGNED;
+ }
+ }
+ return pseg;
+}
+
+typedef struct {
+ uint32_t rid;
+ uint32_t sid;
+} docid;
+
+#define BUFFER_REC_DEL(r) ((r)->jump = 1)
+#define BUFFER_REC_DELETED(r) ((r)->jump == 1)
+
+#define BUFFER_REC_AT(b,pos) ((buffer_rec *)(b) + (pos))
+#define BUFFER_REC_POS(b,rec) ((uint16_t)((rec) - (buffer_rec *)(b)))
+
+inline static void
+buffer_term_dump(grn_ctx *ctx, grn_ii *ii, buffer *b, buffer_term *bt)
+{
+ int pos, rid, sid;
+ uint8_t *p;
+ buffer_rec *r;
+ GRN_LOG(ctx, GRN_LOG_DEBUG,
+ "b=(%x %u %u %u)", b->header.chunk, b->header.chunk_size, b->header.buffer_free, b->header.nterms);
+ GRN_LOG(ctx, GRN_LOG_DEBUG,
+ "bt=(%u %u %u %u %u)", bt->tid, bt->size_in_chunk, bt->pos_in_chunk, bt->size_in_buffer, bt->pos_in_buffer);
+ for (pos = bt->pos_in_buffer; pos; pos = r->step) {
+ r = BUFFER_REC_AT(b, pos);
+ p = NEXT_ADDR(r);
+ GRN_B_DEC(rid, p);
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ GRN_B_DEC(sid, p);
+ } else {
+ sid = 1;
+ }
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "%d=(%d:%d),(%d:%d)", pos, r->jump, r->step, rid, sid);
+ }
+}
+
+inline static grn_rc
+check_jump(grn_ctx *ctx, grn_ii *ii, buffer *b, buffer_rec *r, int j)
+{
+ uint16_t i = BUFFER_REC_POS(b, r);
+ uint8_t *p;
+ buffer_rec *r2;
+ docid id, id2;
+ if (!j) { return GRN_SUCCESS; }
+ p = NEXT_ADDR(r);
+ GRN_B_DEC(id.rid, p);
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ GRN_B_DEC(id.sid, p);
+ } else {
+ id.sid = 1;
+ }
+ if (j == 1) {
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "deleting! %d(%d:%d)", i, id.rid, id.sid);
+ return GRN_SUCCESS;
+ }
+ r2 = BUFFER_REC_AT(b, j);
+ p = NEXT_ADDR(r2);
+ GRN_B_DEC(id2.rid, p);
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ GRN_B_DEC(id2.sid, p);
+ } else {
+ id2.sid = 1;
+ }
+ if (r2->step == i) {
+ GRN_LOG(ctx, GRN_LOG_EMERG, "cycle! %d(%d:%d)<->%d(%d:%d)", i, id.rid, id.sid, j, id2.rid, id2.sid);
+ return GRN_FILE_CORRUPT;
+ }
+ if (id2.rid < id.rid || (id2.rid == id.rid && id2.sid <= id.sid)) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "invalid jump! %d(%d:%d)(%d:%d)->%d(%d:%d)(%d:%d)", i, r->jump, r->step, id.rid, id.sid, j, r2->jump, r2->step, id2.rid, id2.sid);
+ return GRN_FILE_CORRUPT;
+ }
+ return GRN_SUCCESS;
+}
+
+inline static grn_rc
+set_jump_r(grn_ctx *ctx, grn_ii *ii, buffer *b, buffer_rec *from, int to)
+{
+ int i, j, max_jump = 100;
+ buffer_rec *r, *r2;
+ for (r = from, j = to; j > 1 && max_jump--; r = BUFFER_REC_AT(b, r->step)) {
+ r2 = BUFFER_REC_AT(b, j);
+ if (r == r2) { break; }
+ if (BUFFER_REC_DELETED(r2)) { break; }
+ if (j == (i = r->jump)) { break; }
+ if (j == r->step) { break; }
+ if (check_jump(ctx, ii, b, r, j)) {
+ ERR(GRN_FILE_CORRUPT, "check_jump failed");
+ return ctx->rc;
+ }
+ r->jump = j;
+ j = i;
+ if (!r->step) { return GRN_FILE_CORRUPT; }
+ }
+ return GRN_SUCCESS;
+}
+
+#define GET_NUM_BITS(x,n) do {\
+ n = x;\
+ n = (n & 0x55555555) + ((n >> 1) & 0x55555555);\
+ n = (n & 0x33333333) + ((n >> 2) & 0x33333333);\
+ n = (n & 0x0F0F0F0F) + ((n >> 4) & 0x0F0F0F0F);\
+ n = (n & 0x00FF00FF) + ((n >> 8) & 0x00FF00FF);\
+ n = (n & 0x0000FFFF) + ((n >>16) & 0x0000FFFF);\
+} while (0)
+
+inline static grn_rc
+buffer_put(grn_ctx *ctx, grn_ii *ii, buffer *b, buffer_term *bt,
+ buffer_rec *rnew, uint8_t *bs, grn_ii_updspec *u, int size)
+{
+ uint8_t *p;
+ grn_rc rc = GRN_SUCCESS;
+ docid id_curr = {0, 0}, id_start = {0, 0}, id_post = {0, 0};
+ buffer_rec *r_curr, *r_start = NULL;
+ uint16_t last = 0, *lastp = &bt->pos_in_buffer, pos = BUFFER_REC_POS(b, rnew);
+ int vdelta = 0, delta, delta0 = 0, vhops = 0, nhops = 0, reset = 1;
+ memcpy(NEXT_ADDR(rnew), bs, size - sizeof(buffer_rec));
+ for (;;) {
+ if (!*lastp) {
+ rnew->step = 0;
+ rnew->jump = 0;
+ // smb_wmb();
+ *lastp = pos;
+ if (bt->size_in_buffer++ > 1) {
+ buffer_rec *rhead = BUFFER_REC_AT(b, bt->pos_in_buffer);
+ rhead->jump = pos;
+ if (!(bt->size_in_buffer & 1)) {
+ int n;
+ buffer_rec *r = BUFFER_REC_AT(b, rhead->step), *r2;
+ GET_NUM_BITS(bt->size_in_buffer, n);
+ while (n-- && (r->jump > 1)) {
+ r2 = BUFFER_REC_AT(b, r->jump);
+ if (BUFFER_REC_DELETED(r2)) { break; }
+ r = r2;
+ }
+ if (r != rnew) { set_jump_r(ctx, ii, b, r, last); }
+ }
+ }
+ break;
+ }
+ r_curr = BUFFER_REC_AT(b, *lastp);
+ p = NEXT_ADDR(r_curr);
+ GRN_B_DEC(id_curr.rid, p);
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ GRN_B_DEC(id_curr.sid, p);
+ } else {
+ id_curr.sid = 1;
+ }
+ if (id_curr.rid < id_post.rid ||
+ (id_curr.rid == id_post.rid && id_curr.sid < id_post.sid)) {
+ rc = GRN_FILE_CORRUPT;
+ ERRSET(ctx, GRN_CRIT, rc, "loop found!!! (%d:%d)->(%d:%d)",
+ id_post.rid, id_post.sid, id_curr.rid, id_curr.sid);
+ buffer_term_dump(ctx, ii, b, bt);
+ bt->pos_in_buffer = 0;
+ bt->size_in_buffer = 0;
+ lastp = &bt->pos_in_buffer;
+ continue;
+ }
+ id_post.rid = id_curr.rid;
+ id_post.sid = id_curr.sid;
+ if (u->rid < id_curr.rid || (u->rid == id_curr.rid && u->sid <= id_curr.sid)) {
+ uint16_t step = *lastp, jump = r_curr->jump;
+ if (u->rid == id_curr.rid) {
+ if (u->sid == 0) {
+ while (id_curr.rid == u->rid) {
+ BUFFER_REC_DEL(r_curr);
+ if (!(step = r_curr->step)) { break; }
+ r_curr = BUFFER_REC_AT(b, step);
+ p = NEXT_ADDR(r_curr);
+ GRN_B_DEC(id_curr.rid, p);
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ GRN_B_DEC(id_curr.sid, p);
+ } else {
+ id_curr.sid = 1;
+ }
+ }
+ } else if (u->sid == id_curr.sid) {
+ BUFFER_REC_DEL(r_curr);
+ step = r_curr->step;
+ }
+ }
+ rnew->step = step;
+ rnew->jump = check_jump(ctx, ii, b, rnew, jump) ? 0 : jump;
+ // smb_wmb();
+ *lastp = pos;
+ break;
+ }
+
+ if (reset) {
+ r_start = r_curr;
+ id_start.rid = id_curr.rid;
+ id_start.sid = id_curr.sid;
+ if (!(delta0 = u->rid - id_start.rid)) { delta0 = u->sid - id_start.sid; }
+ nhops = 0;
+ vhops = 1;
+ vdelta = delta0 >> 1;
+ } else {
+ if (!(delta = id_curr.rid - id_start.rid)) { delta = id_curr.sid - id_start.sid; }
+ if (vdelta < delta) {
+ vdelta += (delta0 >> ++vhops);
+ r_start = r_curr;
+ }
+ if (nhops > vhops) {
+ set_jump_r(ctx, ii, b, r_start, *lastp);
+ } else {
+ nhops++;
+ }
+ }
+
+ last = *lastp;
+ lastp = &r_curr->step;
+ reset = 0;
+ {
+ uint16_t posj = r_curr->jump;
+ if (posj > 1) {
+ buffer_rec *rj = BUFFER_REC_AT(b, posj);
+ if (!BUFFER_REC_DELETED(rj)) {
+ docid idj;
+ p = NEXT_ADDR(rj);
+ GRN_B_DEC(idj.rid, p);
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ GRN_B_DEC(idj.sid, p);
+ } else {
+ idj.sid = 1;
+ }
+ if (idj.rid < u->rid || (idj.rid == u->rid && idj.sid < u->sid)) {
+ last = posj;
+ lastp = &rj->step;
+ } else {
+ reset = 1;
+ }
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+/* array */
+
+inline static uint32_t *
+array_at(grn_ctx *ctx, grn_ii *ii, uint32_t id)
+{
+ byte *p = NULL;
+ uint32_t seg, pseg;
+ if (id > GRN_ID_MAX) { return NULL; }
+ seg = id >> W_ARRAY;
+ if ((pseg = ii->header->ainfo[seg]) == NOT_ASSIGNED) { return NULL; }
+ GRN_IO_SEG_REF(ii->seg, pseg, p);
+ if (!p) { return NULL; }
+ return (uint32_t *)(p + (id & ARRAY_MASK_IN_A_SEGMENT) * S_ARRAY_ELEMENT);
+}
+
+inline static uint32_t *
+array_get(grn_ctx *ctx, grn_ii *ii, uint32_t id)
+{
+ byte *p = NULL;
+ uint16_t seg;
+ uint32_t pseg;
+ if (id > GRN_ID_MAX) { return NULL; }
+ seg = id >> W_ARRAY;
+ if ((pseg = ii->header->ainfo[seg]) == NOT_ASSIGNED) {
+ if (segment_get_clear(ctx, ii, &pseg)) { return NULL; }
+ ii->header->ainfo[seg] = pseg;
+ if (seg >= ii->header->amax) { ii->header->amax = seg + 1; }
+ }
+ GRN_IO_SEG_REF(ii->seg, pseg, p);
+ if (!p) { return NULL; }
+ return (uint32_t *)(p + (id & ARRAY_MASK_IN_A_SEGMENT) * S_ARRAY_ELEMENT);
+}
+
+inline static void
+array_unref(grn_ii *ii, uint32_t id)
+{
+ GRN_IO_SEG_UNREF(ii->seg, ii->header->ainfo[id >> W_ARRAY]);
+}
+
+/* updspec */
+
+grn_ii_updspec *
+grn_ii_updspec_open(grn_ctx *ctx, uint32_t rid, uint32_t sid)
+{
+ grn_ii_updspec *u;
+ if (!(u = GRN_MALLOC(sizeof(grn_ii_updspec)))) { return NULL; }
+ u->rid = rid;
+ u->sid = sid;
+ u->weight = 0;
+ u->tf = 0;
+ u->atf = 0;
+ u->pos = NULL;
+ u->tail = NULL;
+ // u->vnodes = NULL;
+ return u;
+}
+
+#define GRN_II_MAX_TF 0x1ffff
+
+grn_rc
+grn_ii_updspec_add(grn_ctx *ctx, grn_ii_updspec *u, int pos, int32_t weight)
+{
+ struct _grn_ii_pos *p;
+ u->atf++;
+ if (u->tf >= GRN_II_MAX_TF) { return GRN_SUCCESS; }
+ if (!(p = GRN_MALLOC(sizeof(struct _grn_ii_pos)))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ u->weight += weight;
+ p->pos = pos;
+ p->next = NULL;
+ if (u->tail) {
+ u->tail->next = p;
+ } else {
+ u->pos = p;
+ }
+ u->tail = p;
+ u->tf++;
+ return GRN_SUCCESS;
+}
+
+int
+grn_ii_updspec_cmp(grn_ii_updspec *a, grn_ii_updspec *b)
+{
+ struct _grn_ii_pos *pa, *pb;
+ if (a->rid != b->rid) { return a->rid - b->rid; }
+ if (a->sid != b->sid) { return a->sid - b->sid; }
+ if (a->weight != b->weight) { return a->weight - b->weight; }
+ if (a->tf != b->tf) { return a->tf - b->tf; }
+ for (pa = a->pos, pb = b->pos; pa && pb; pa = pa->next, pb = pb->next) {
+ if (pa->pos != pb->pos) { return pa->pos - pb->pos; }
+ }
+ if (pa) { return 1; }
+ if (pb) { return -1; }
+ return 0;
+}
+
+grn_rc
+grn_ii_updspec_close(grn_ctx *ctx, grn_ii_updspec *u)
+{
+ struct _grn_ii_pos *p = u->pos, *q;
+ while (p) {
+ q = p->next;
+ GRN_FREE(p);
+ p = q;
+ }
+ GRN_FREE(u);
+ return GRN_SUCCESS;
+}
+
+inline static uint8_t *
+encode_rec(grn_ctx *ctx, grn_ii *ii, grn_ii_updspec *u, unsigned int *size, int deletep)
+{
+ uint8_t *br, *p;
+ struct _grn_ii_pos *pp;
+ uint32_t lpos, tf, weight;
+ if (deletep) {
+ tf = 0;
+ weight = 0;
+ } else {
+ tf = u->tf;
+ weight = u->weight;
+ }
+ if (!(br = GRN_MALLOC((tf + 4) * 5))) {
+ return NULL;
+ }
+ p = br;
+ GRN_B_ENC(u->rid, p);
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ GRN_B_ENC(u->sid, p);
+ } else {
+ u->sid = 1;
+ }
+ GRN_B_ENC(tf, p);
+ if ((ii->header->flags & GRN_OBJ_WITH_WEIGHT)) { GRN_B_ENC(weight, p); }
+ if ((ii->header->flags & GRN_OBJ_WITH_POSITION)) {
+ for (lpos = 0, pp = u->pos; pp && tf--; lpos = pp->pos, pp = pp->next) {
+ GRN_B_ENC(pp->pos - lpos, p);
+ }
+ }
+ while (((intptr_t)p & 0x03)) { *p++ = 0; }
+ *size = (unsigned int) ((p - br) + sizeof(buffer_rec));
+ return br;
+}
+
+typedef struct {
+ grn_ii *ii;
+ grn_hash *h;
+} lexicon_deletable_arg;
+
+#ifdef CASCADE_DELETE_LEXICON
+static int
+lexicon_deletable(grn_ctx *ctx, grn_obj *lexicon, grn_id tid, void *arg)
+{
+ uint32_t *a;
+ grn_hash *h = ((lexicon_deletable_arg *)arg)->h;
+ grn_ii *ii = ((lexicon_deletable_arg *)arg)->ii;
+ if (!h) { return 0; }
+ if ((a = array_at(ctx, ii, tid))) {
+ if (a[0]) {
+ array_unref(ii, tid);
+ return 0;
+ }
+ array_unref(ii, tid);
+ }
+ {
+ grn_ii_updspec **u;
+ if (!grn_hash_get(ctx, h, &tid, sizeof(grn_id), (void **) &u)) {
+ return (ERRP(ctx, GRN_ERROR)) ? 0 : 1;
+ }
+ if (!(*u)->tf || !(*u)->sid) { return 1; }
+ return 0;
+ }
+}
+#endif /* CASCADE_DELETE_LEXICON */
+
+inline static void
+lexicon_delete(grn_ctx *ctx, grn_ii *ii, uint32_t tid, grn_hash *h)
+{
+#ifdef CASCADE_DELETE_LEXICON
+ lexicon_deletable_arg arg = {ii, h};
+ grn_table_delete_optarg optarg = {0, lexicon_deletable, &arg};
+ _grn_table_delete_by_id(ctx, ii->lexicon, tid, &optarg);
+#endif /* CASCADE_DELETE_LEXICON */
+}
+
+typedef struct {
+ grn_id rid;
+ uint32_t sid;
+ uint32_t tf;
+ uint32_t weight;
+ uint32_t flags;
+} docinfo;
+
+#define GETNEXTC() do {\
+ if (sdf) {\
+ uint32_t dgap = *srp++;\
+ cid.rid += dgap;\
+ if (dgap) { cid.sid = 0; }\
+ snp += cid.tf;\
+ cid.tf = 1 + *stp++;\
+ if ((ii->header->flags & GRN_OBJ_WITH_WEIGHT)) { cid.weight = *sop++; }\
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {\
+ cid.sid += 1 + *ssp++;\
+ } else {\
+ cid.sid = 1;\
+ }\
+ sdf--;\
+ } else {\
+ cid.rid = 0;\
+ }\
+} while (0)
+
+#define PUTNEXT_(id) do {\
+ uint32_t dgap = id.rid - lid.rid;\
+ uint32_t sgap = (dgap ? id.sid : id.sid - lid.sid) - 1;\
+ *ridp++ = dgap;\
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {\
+ *sidp++ = sgap;\
+ }\
+ *tfp++ = id.tf - 1;\
+ if ((ii->header->flags & GRN_OBJ_WITH_WEIGHT)) { *weightp++ = id.weight; }\
+ lid.rid = id.rid;\
+ lid.sid = id.sid;\
+} while (0)
+
+#define PUTNEXTC() do {\
+ if (cid.rid) {\
+ if (cid.tf) {\
+ if (lid.rid > cid.rid || (lid.rid == cid.rid && lid.sid >= cid.sid)) {\
+ GRN_LOG(ctx, GRN_LOG_CRIT, "brokenc!! (%d:%d) -> (%d:%d)", lid.rid, lid.sid, bid.rid, bid.sid);\
+ rc = GRN_FILE_CORRUPT;\
+ break;\
+ }\
+ PUTNEXT_(cid);\
+ if ((ii->header->flags & GRN_OBJ_WITH_POSITION)) {\
+ uint32_t i;\
+ for (i = 0; i < cid.tf; i++) {\
+ *posp++ = snp[i];\
+ spos += snp[i];\
+ }\
+ }\
+ } else {\
+ GRN_LOG(ctx, GRN_LOG_CRIT, "invalid chunk(%d,%d)", bt->tid, cid.rid);\
+ rc = GRN_FILE_CORRUPT;\
+ break;\
+ }\
+ }\
+ GETNEXTC();\
+} while (0)
+
+#define GETNEXTB() do {\
+ if (nextb) {\
+ uint32_t lrid = bid.rid, lsid = bid.sid;\
+ buffer_rec *br = BUFFER_REC_AT(sb, nextb);\
+ sbp = NEXT_ADDR(br);\
+ GRN_B_DEC(bid.rid, sbp);\
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {\
+ GRN_B_DEC(bid.sid, sbp);\
+ } else {\
+ bid.sid = 1;\
+ }\
+ if (lrid > bid.rid || (lrid == bid.rid && lsid >= bid.sid)) {\
+ GRN_LOG(ctx, GRN_LOG_CRIT, "brokeng!! (%d:%d) -> (%d:%d)", lrid, lsid, bid.rid, bid.sid);\
+ rc = GRN_FILE_CORRUPT;\
+ break;\
+ }\
+ nextb = br->step;\
+ } else {\
+ bid.rid = 0;\
+ }\
+} while (0)
+
+#define PUTNEXTB() do {\
+ if (bid.rid && bid.sid) {\
+ GRN_B_DEC(bid.tf, sbp);\
+ if (bid.tf > 0) {\
+ if (lid.rid > bid.rid || (lid.rid == bid.rid && lid.sid >= bid.sid)) {\
+ GRN_LOG(ctx, GRN_LOG_CRIT, "brokenb!! (%d:%d) -> (%d:%d)", lid.rid, lid.sid, bid.rid, bid.sid);\
+ rc = GRN_FILE_CORRUPT;\
+ break;\
+ }\
+ if ((ii->header->flags & GRN_OBJ_WITH_WEIGHT)) { GRN_B_DEC(bid.weight, sbp); }\
+ PUTNEXT_(bid);\
+ if ((ii->header->flags & GRN_OBJ_WITH_POSITION)) {\
+ while (bid.tf--) { GRN_B_DEC(*posp, sbp); spos += *posp++; }\
+ }\
+ }\
+ }\
+ GETNEXTB();\
+} while (0)
+
+#define MERGE_BC(cond) do {\
+ if (bid.rid) {\
+ if (cid.rid) {\
+ if (cid.rid < bid.rid) {\
+ PUTNEXTC();\
+ } else {\
+ if (bid.rid < cid.rid) {\
+ PUTNEXTB();\
+ } else {\
+ if (bid.sid) {\
+ if (cid.sid < bid.sid) {\
+ PUTNEXTC();\
+ } else {\
+ if (bid.sid == cid.sid) { GETNEXTC(); }\
+ PUTNEXTB();\
+ }\
+ } else {\
+ GETNEXTC();\
+ }\
+ }\
+ }\
+ } else {\
+ PUTNEXTB();\
+ }\
+ } else {\
+ if (cid.rid) {\
+ PUTNEXTC();\
+ } else {\
+ break;\
+ }\
+ }\
+} while (cond)
+
+typedef struct {
+ uint32_t segno;
+ uint32_t size;
+ uint32_t dgap;
+} chunk_info;
+
+static grn_rc
+chunk_flush(grn_ctx *ctx, grn_ii *ii, chunk_info *cinfo, uint8_t *enc, uint32_t encsize)
+{
+ grn_rc rc;
+ uint8_t *dc;
+ uint32_t dcn;
+ grn_io_win dw;
+ if (!(rc = chunk_new(ctx, ii, &dcn, encsize))) {
+ if ((dc = WIN_MAP2(ii->chunk, ctx, &dw, dcn, 0, encsize, grn_io_wronly))) {
+ memcpy(dc, enc, encsize);
+ grn_io_win_unmap2(&dw);
+ cinfo->segno = dcn;
+ cinfo->size = encsize;
+ rc = GRN_SUCCESS;
+ } else {
+ chunk_free(ctx, ii, dcn, 0, encsize);
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ return rc;
+}
+
+static grn_rc
+chunk_merge(grn_ctx *ctx, grn_ii *ii, buffer *sb, buffer_term *bt,
+ chunk_info *cinfo, grn_id rid, datavec *dv,
+ uint16_t *nextbp, uint8_t **sbpp, docinfo *bidp, int32_t *balance)
+{
+ grn_rc rc;
+ grn_io_win sw;
+ uint64_t spos = 0;
+ uint32_t segno = cinfo->segno, size = cinfo->size, sdf = 0, ndf = 0;
+ uint32_t *ridp = NULL, *sidp = NULL, *tfp, *weightp = NULL, *posp = NULL;
+ docinfo cid = {0, 0, 0, 0, 0}, lid = {0, 0, 0, 0, 0}, bid = *bidp;
+ uint8_t *scp = WIN_MAP2(ii->chunk, ctx, &sw, segno, 0, size, grn_io_rdonly);
+ if (scp) {
+ uint16_t nextb = *nextbp;
+ uint32_t snn = 0, *srp, *ssp = NULL, *stp, *sop = NULL, *snp;
+ uint8_t *sbp = *sbpp;
+ datavec rdv[MAX_N_ELEMENTS + 1];
+ size_t bufsize = S_SEGMENT * ii->n_elements;
+ datavec_init(ctx, rdv, ii->n_elements, 0, 0);
+ if ((ii->header->flags & GRN_OBJ_WITH_POSITION)) {
+ rdv[ii->n_elements - 1].flags = ODD;
+ }
+ bufsize += grn_p_decv(ctx, scp, cinfo->size, rdv, ii->n_elements);
+ // (df in chunk list) = a[1] - sdf;
+ {
+ int j = 0;
+ sdf = rdv[j].data_size;
+ srp = rdv[j++].data;
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) { ssp = rdv[j++].data; }
+ stp = rdv[j++].data;
+ if ((ii->header->flags & GRN_OBJ_WITH_WEIGHT)) { sop = rdv[j++].data; }
+ snn = rdv[j].data_size;
+ snp = rdv[j].data;
+ }
+ if (!(rc = datavec_reset(ctx, dv, ii->n_elements, sdf + S_SEGMENT, bufsize))) {
+ {
+ int j = 0;
+ ridp = dv[j++].data;
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) { sidp = dv[j++].data; }
+ tfp = dv[j++].data;
+ if ((ii->header->flags & GRN_OBJ_WITH_WEIGHT)) { weightp = dv[j++].data; }
+ posp = dv[j].data;
+ }
+ GETNEXTC();
+ MERGE_BC(bid.rid <= rid || cid.rid);
+ *sbpp = sbp;
+ *nextbp = nextb;
+ *bidp = bid;
+ GRN_ASSERT(posp < dv[ii->n_elements].data);
+ ndf = ridp - dv[0].data;
+ }
+ datavec_fin(ctx, rdv);
+ grn_io_win_unmap2(&sw);
+ } else {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ if (!rc) {
+ int j = 0;
+ uint8_t *enc;
+ uint32_t encsize;
+ uint32_t np = posp - dv[ii->n_elements - 1].data;
+ uint32_t f_s = (ndf < 3) ? 0 : USE_P_ENC;
+ uint32_t f_d = ((ndf < 16) || (ndf <= (lid.rid >> 8))) ? 0 : USE_P_ENC;
+ dv[j].data_size = ndf; dv[j++].flags = f_d;
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ dv[j].data_size = ndf; dv[j++].flags = f_s;
+ }
+ dv[j].data_size = ndf; dv[j++].flags = f_s;
+ if ((ii->header->flags & GRN_OBJ_WITH_WEIGHT)) {
+ dv[j].data_size = ndf; dv[j++].flags = f_s;
+ }
+ if ((ii->header->flags & GRN_OBJ_WITH_POSITION)) {
+ uint32_t f_p = ((np < 32) || (np <= (spos >> 13))) ? 0 : USE_P_ENC;
+ dv[j].data_size = np; dv[j].flags = f_p|ODD;
+ }
+ if ((enc = GRN_MALLOC((ndf * 4 + np) * 2))) {
+ encsize = grn_p_encv(ctx, dv, ii->n_elements, enc);
+ if (!(rc = chunk_flush(ctx, ii, cinfo, enc, encsize))) {
+ chunk_free(ctx, ii, segno, 0, size);
+ }
+ GRN_FREE(enc);
+ } else {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ *balance += (ndf - sdf);
+ return rc;
+}
+
+static grn_rc
+buffer_merge(grn_ctx *ctx, grn_ii *ii, uint32_t seg, grn_hash *h,
+ buffer *sb, uint8_t *sc, buffer *db, uint8_t *dc)
+{
+ buffer_term *bt;
+ grn_rc rc = GRN_SUCCESS;
+ uint8_t *sbp = NULL, *dcp = dc;
+ datavec dv[MAX_N_ELEMENTS + 1];
+ datavec rdv[MAX_N_ELEMENTS + 1];
+ uint16_t n = db->header.nterms, nterms_void = 0;
+ size_t unitsize = (S_SEGMENT + sb->header.chunk_size / sb->header.nterms) * 2;
+ // size_t unitsize = (S_SEGMENT + sb->header.chunk_size) * 2 + (1<<24);
+ size_t totalsize = unitsize * ii->n_elements;
+ //todo : realloc
+ if ((rc = datavec_init(ctx, dv, ii->n_elements, unitsize, totalsize))) {
+ return rc;
+ }
+ datavec_init(ctx, rdv, ii->n_elements, 0, 0);
+ if ((ii->header->flags & GRN_OBJ_WITH_POSITION)) {
+ rdv[ii->n_elements - 1].flags = ODD;
+ }
+ for (bt = db->terms; n; n--, bt++) {
+ uint16_t nextb;
+ uint64_t spos = 0;
+ int32_t balance = 0;
+ uint32_t *ridp, *sidp = NULL, *tfp, *weightp = NULL, *posp, nchunks = 0;
+ chunk_info *cinfo = NULL;
+ grn_id crid = GRN_ID_NIL;
+ docinfo cid = {0, 0, 0, 0, 0}, lid = {0, 0, 0, 0, 0}, bid = {0, 0};
+ uint32_t sdf = 0, snn = 0, ndf;
+ uint32_t *srp = NULL, *ssp = NULL, *stp = NULL, *sop = NULL, *snp = NULL;
+ if (!bt->tid) {
+ nterms_void++;
+ continue;
+ }
+ if (!bt->pos_in_buffer) {
+ GRN_ASSERT(!bt->size_in_buffer);
+ if (bt->size_in_chunk) {
+ memcpy(dcp, sc + bt->pos_in_chunk, bt->size_in_chunk);
+ bt->pos_in_chunk = (uint32_t)(dcp - dc);
+ dcp += bt->size_in_chunk;
+ }
+ continue;
+ }
+ nextb = bt->pos_in_buffer;
+ GETNEXTB();
+ if (sc && bt->size_in_chunk) {
+ uint8_t *scp = sc + bt->pos_in_chunk;
+ uint8_t *sce = scp + bt->size_in_chunk;
+ size_t size = S_SEGMENT * ii->n_elements;
+ if ((bt->tid & CHUNK_SPLIT)) {
+ int i;
+ GRN_B_DEC(nchunks, scp);
+ if (!(cinfo = GRN_MALLOCN(chunk_info, nchunks + 1))) {
+ datavec_fin(ctx, dv);
+ datavec_fin(ctx, rdv);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ for (i = 0; i < nchunks; i++) {
+ GRN_B_DEC(cinfo[i].segno, scp);
+ GRN_B_DEC(cinfo[i].size, scp);
+ GRN_B_DEC(cinfo[i].dgap, scp);
+ crid += cinfo[i].dgap;
+ if (bid.rid <= crid) {
+ rc = chunk_merge(ctx, ii, sb, bt, &cinfo[i], crid, dv,
+ &nextb, &sbp, &bid, &balance);
+ if (rc) {
+ datavec_fin(ctx, dv);
+ datavec_fin(ctx, rdv);
+ return rc;
+ }
+ }
+ }
+ }
+ if (sce > scp) {
+ size += grn_p_decv(ctx, scp, sce - scp, rdv, ii->n_elements);
+ {
+ int j = 0;
+ sdf = rdv[j].data_size;
+ srp = rdv[j++].data;
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) { ssp = rdv[j++].data; }
+ stp = rdv[j++].data;
+ if ((ii->header->flags & GRN_OBJ_WITH_WEIGHT)) { sop = rdv[j++].data; }
+ snn = rdv[j].data_size;
+ snp = rdv[j].data;
+ }
+ if ((rc = datavec_reset(ctx, dv, ii->n_elements, sdf + S_SEGMENT, size))) {
+ datavec_fin(ctx, dv);
+ datavec_fin(ctx, rdv);
+ return rc;
+ }
+ }
+ }
+ {
+ int j = 0;
+ ridp = dv[j++].data;
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) { sidp = dv[j++].data; }
+ tfp = dv[j++].data;
+ if ((ii->header->flags & GRN_OBJ_WITH_WEIGHT)) { weightp = dv[j++].data; }
+ posp = dv[j].data;
+ }
+ GETNEXTC();
+ MERGE_BC(1);
+ GRN_ASSERT(posp < dv[ii->n_elements].data);
+ ndf = ridp - dv[0].data;
+ /*
+ {
+ grn_obj buf;
+ uint32_t rid, sid, tf, i, pos, *pp;
+ GRN_TEXT_INIT(&buf, 0);
+ rid = 0;
+ pp = dv[3].data;
+ for (i = 0; i < ndf; i++) {
+ GRN_BULK_REWIND(&buf);
+ rid += dv[0].data[i];
+ if (dv[0].data[i]) { sid = 0; }
+ sid += dv[1].data[i] + 1;
+ tf = dv[2].data[i] + 1;
+ pos = 0;
+ grn_text_itoa(ctx, &buf, rid);
+ GRN_TEXT_PUTC(ctx, &buf, ':');
+ grn_text_itoa(ctx, &buf, sid);
+ GRN_TEXT_PUTC(ctx, &buf, ':');
+ grn_text_itoa(ctx, &buf, tf);
+ GRN_TEXT_PUTC(ctx, &buf, ':');
+ while (tf--) {
+ pos += *pp++;
+ grn_text_itoa(ctx, &buf, pos);
+ if (tf) { GRN_TEXT_PUTC(ctx, &buf, ','); }
+ }
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "Posting:%s", GRN_TEXT_VALUE(&buf));
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ */
+ {
+ grn_id tid = bt->tid & GRN_ID_MAX;
+ uint32_t *a = array_at(ctx, ii, tid);
+ if (!a) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "array_entry not found tid=%d", tid);
+ memset(bt, 0, sizeof(buffer_term));
+ nterms_void++;
+ } else {
+ if (!ndf && !nchunks) {
+ a[0] = 0;
+ a[1] = 0;
+ lexicon_delete(ctx, ii, tid, h);
+ memset(bt, 0, sizeof(buffer_term));
+ nterms_void++;
+ } else if ((ii->header->flags & GRN_OBJ_WITH_SECTION)
+ && !nchunks && ndf == 1 && lid.rid < 0x100000 &&
+ lid.sid < 0x800 && lid.tf == 1 && lid.weight == 0) {
+ a[0] = (lid.rid << 12) + (lid.sid << 1) + 1;
+ a[1] = (ii->header->flags & GRN_OBJ_WITH_POSITION) ? posp[-1] : 0;
+ memset(bt, 0, sizeof(buffer_term));
+ nterms_void++;
+ } else if (!(ii->header->flags & GRN_OBJ_WITH_SECTION)
+ && !nchunks && ndf == 1 && lid.tf == 1 && lid.weight == 0) {
+ a[0] = (lid.rid << 1) + 1;
+ a[1] = (ii->header->flags & GRN_OBJ_WITH_POSITION) ? posp[-1] : 0;
+ memset(bt, 0, sizeof(buffer_term));
+ nterms_void++;
+ } else {
+ int j = 0;
+ uint8_t *dcp0;
+ uint32_t encsize;
+ uint32_t f_s = (ndf < 3) ? 0 : USE_P_ENC;
+ uint32_t f_d = ((ndf < 16) || (ndf <= (lid.rid >> 8))) ? 0 : USE_P_ENC;
+ dv[j].data_size = ndf; dv[j++].flags = f_d;
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ dv[j].data_size = ndf; dv[j++].flags = f_s;
+ }
+ dv[j].data_size = ndf; dv[j++].flags = f_s;
+ if ((ii->header->flags & GRN_OBJ_WITH_WEIGHT)) {
+ dv[j].data_size = ndf; dv[j++].flags = f_s;
+ }
+ if ((ii->header->flags & GRN_OBJ_WITH_POSITION)) {
+ uint32_t np = posp - dv[ii->n_elements - 1].data;
+ uint32_t f_p = ((np < 32) || (np <= (spos >> 13))) ? 0 : USE_P_ENC;
+ dv[j].data_size = np; dv[j].flags = f_p|ODD;
+ }
+ dcp0 = dcp;
+ a[1] = (bt->size_in_chunk ? a[1] : 0) + (ndf - sdf) + balance;
+ if (nchunks) {
+ int i;
+ GRN_B_ENC(nchunks, dcp);
+ for (i = 0; i < nchunks; i++) {
+ GRN_B_ENC(cinfo[i].segno, dcp);
+ GRN_B_ENC(cinfo[i].size, dcp);
+ GRN_B_ENC(cinfo[i].dgap, dcp);
+ }
+ }
+ encsize = grn_p_encv(ctx, dv, ii->n_elements, dcp);
+
+ if (sb->header.chunk_size + S_SEGMENT <= (dcp - dc) + encsize) {
+ int i;
+ char buf[255], *bufp;
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "cs(%d)+(%d)=(%d)<=(%" GRN_FMT_LLD ")+(%d)=(%" GRN_FMT_LLD ")",
+ sb->header.chunk_size, S_SEGMENT, sb->header.chunk_size + S_SEGMENT,
+ (long long int)(dcp - dc), encsize, (long long int)((dcp - dc) + encsize));
+ for (j = 0; j < ii->n_elements; j++) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "rdv[%d] data_size=%d, flags=%d",
+ j, rdv[j].data_size, rdv[j].flags);
+ for (i = 0, bufp = buf; i < rdv[j].data_size;) {
+ bufp += sprintf(bufp, " %d", rdv[j].data[i]);
+ i++;
+ if (!(i % 32) || i == rdv[j].data_size) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "rdv[%d].data[%d]%s", j, i, buf);
+ bufp = buf;
+ }
+ }
+ }
+
+ for (j = 0; j < ii->n_elements; j++) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "dv[%d] data_size=%d, flags=%d",
+ j, dv[j].data_size, dv[j].flags);
+ for (i = 0, bufp = buf; i < dv[j].data_size;) {
+ bufp += sprintf(bufp, " %d", dv[j].data[i]);
+ i++;
+ if (!(i % 32) || i == dv[j].data_size) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "dv[%d].data[%d]%s", j, i, buf);
+ bufp = buf;
+ }
+ }
+ }
+
+ }
+
+ if (encsize > CHUNK_SPLIT_THRESHOLD &&
+ (cinfo || (cinfo = GRN_MALLOCN(chunk_info, nchunks + 1))) &&
+ !chunk_flush(ctx, ii, &cinfo[nchunks], dcp, encsize)) {
+ int i;
+ cinfo[nchunks].dgap = lid.rid - crid;
+ nchunks++;
+ dcp = dcp0;
+ GRN_B_ENC(nchunks, dcp);
+ for (i = 0; i < nchunks; i++) {
+ GRN_B_ENC(cinfo[i].segno, dcp);
+ GRN_B_ENC(cinfo[i].size, dcp);
+ GRN_B_ENC(cinfo[i].dgap, dcp);
+ }
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "split (%d) encsize=%d", tid, encsize);
+ bt->tid |= CHUNK_SPLIT;
+ } else {
+ dcp += encsize;
+ }
+ bt->pos_in_chunk = (uint32_t)(dcp0 - dc);
+ bt->size_in_chunk = (uint32_t)(dcp - dcp0);
+ bt->size_in_buffer = 0;
+ bt->pos_in_buffer = 0;
+ }
+ array_unref(ii, tid);
+ }
+ }
+ if (cinfo) { GRN_FREE(cinfo); }
+ }
+ datavec_fin(ctx, rdv);
+ datavec_fin(ctx, dv);
+ db->header.chunk_size = (uint32_t)(dcp - dc);
+ db->header.buffer_free =
+ S_SEGMENT - sizeof(buffer_header) - db->header.nterms * sizeof(buffer_term);
+ db->header.nterms_void = nterms_void;
+ return rc;
+}
+
+static void
+fake_map2(grn_ctx *ctx, grn_io *io, grn_io_win *iw, void *addr, uint32_t seg, uint32_t size)
+{
+ iw->ctx = ctx;
+ iw->diff = 0;
+ iw->io = io;
+ iw->mode = grn_io_wronly;
+ iw->segment = ((seg) >> GRN_II_N_CHUNK_VARIATION);
+ iw->offset = (((seg) & ((1 << GRN_II_N_CHUNK_VARIATION) - 1)) << GRN_II_W_LEAST_CHUNK);
+ iw->size = size;
+ iw->cached = 0;
+ iw->addr = addr;
+}
+
+static grn_rc
+buffer_flush(grn_ctx *ctx, grn_ii *ii, uint32_t seg, grn_hash *h)
+{
+ grn_rc rc;
+ grn_io_win sw, dw;
+ buffer *sb, *db = NULL;
+ uint8_t *dc, *sc = NULL;
+ uint32_t ds, pseg, scn, dcn = 0;
+ if (ii->header->binfo[seg] == NOT_ASSIGNED) { return GRN_FILE_CORRUPT; }
+ if ((ds = segment_get(ctx, ii)) == MAX_PSEG) { return GRN_NO_MEMORY_AVAILABLE; }
+ pseg = buffer_open(ctx, ii, SEG2POS(seg, 0), NULL, &sb);
+ if (pseg != NOT_ASSIGNED) {
+ GRN_IO_SEG_REF(ii->seg, ds, db);
+ if (db) {
+ uint32_t actual_chunk_size = 0;
+ uint32_t max_dest_chunk_size = sb->header.chunk_size + S_SEGMENT;
+ if ((dc = GRN_MALLOC(max_dest_chunk_size * 2))) {
+ if ((scn = sb->header.chunk) == NOT_ASSIGNED ||
+ (sc = WIN_MAP2(ii->chunk, ctx, &sw, scn, 0,
+ sb->header.chunk_size, grn_io_rdonly))) {
+ uint16_t n = sb->header.nterms;
+ memset(db, 0, S_SEGMENT);
+ memcpy(db->terms, sb->terms, n * sizeof(buffer_term));
+ db->header.nterms = n;
+ if (!(rc = buffer_merge(ctx, ii, seg, h, sb, sc, db, dc))) {
+ actual_chunk_size = db->header.chunk_size;
+ if (actual_chunk_size >= max_dest_chunk_size) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "actual_chunk_size(%d) >= max_dest_chunk_size(%d)",
+ actual_chunk_size, max_dest_chunk_size);
+ }
+ if (!actual_chunk_size || !(rc = chunk_new(ctx, ii, &dcn, actual_chunk_size))) {
+ db->header.chunk = actual_chunk_size ? dcn : NOT_ASSIGNED;
+ fake_map2(ctx, ii->chunk, &dw, dc, dcn, actual_chunk_size);
+ if (!(rc = grn_io_win_unmap2(&dw))) {
+ buffer_segment_update(ii, seg, ds);
+ ii->header->total_chunk_size += actual_chunk_size;
+ if (scn != NOT_ASSIGNED) {
+ grn_io_win_unmap2(&sw);
+ chunk_free(ctx, ii, scn, 0, sb->header.chunk_size);
+ ii->header->total_chunk_size -= sb->header.chunk_size;
+ }
+ } else {
+ GRN_FREE(dc);
+ if (actual_chunk_size) {
+ chunk_free(ctx, ii, dcn, 0, actual_chunk_size);
+ }
+ if (scn != NOT_ASSIGNED) { grn_io_win_unmap2(&sw); }
+ }
+ } else {
+ GRN_FREE(dc);
+ if (scn != NOT_ASSIGNED) { grn_io_win_unmap2(&sw); }
+ }
+ } else {
+ GRN_FREE(dc);
+ if (scn != NOT_ASSIGNED) { grn_io_win_unmap2(&sw); }
+ }
+ } else {
+ GRN_FREE(dc);
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ } else {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ GRN_IO_SEG_UNREF(ii->seg, ds);
+ } else {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ buffer_close(ctx, ii, pseg);
+ } else {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ return rc;
+}
+
+void
+grn_ii_buffer_check(grn_ctx *ctx, grn_ii *ii, uint32_t seg)
+{
+ grn_rc rc;
+ grn_io_win sw;
+ buffer *sb;
+ uint8_t *sc = NULL;
+ uint32_t pseg, scn, nterms_with_corrupt_chunk = 0, nterm_with_chunk = 0;
+ uint32_t ndeleted_terms_with_value = 0;
+ buffer_term *bt;
+ uint8_t *sbp = NULL;
+ datavec rdv[MAX_N_ELEMENTS + 1];
+ uint16_t n;
+ int nterms_void = 0;
+ int size_in_buffer = 0;
+ grn_obj buf;
+ size_t lower_bound;
+ int64_t nloops = 0, nviolations = 0;
+ if (ii->header->binfo[seg] == NOT_ASSIGNED) {
+ GRN_OUTPUT_BOOL(GRN_FALSE);
+ return;
+ }
+ pseg = buffer_open(ctx, ii, SEG2POS(seg, 0), NULL, &sb);
+ if (pseg == NOT_ASSIGNED) {
+ GRN_OUTPUT_BOOL(GRN_FALSE);
+ return;
+ }
+ lower_bound =
+ (sb->header.buffer_free + sizeof(buffer_term) * sb->header.nterms)
+ / sizeof(buffer_rec);
+ datavec_init(ctx, rdv, ii->n_elements, 0, 0);
+ if ((ii->header->flags & GRN_OBJ_WITH_POSITION)) {
+ rdv[ii->n_elements - 1].flags = ODD;
+ }
+ GRN_OUTPUT_MAP_OPEN("BUFFER", -1);
+ GRN_OUTPUT_CSTR("buffer id");
+ GRN_OUTPUT_INT64(seg);
+ if ((scn = sb->header.chunk) == NOT_ASSIGNED) {
+ GRN_OUTPUT_CSTR("void chunk size");
+ GRN_OUTPUT_INT64(sb->header.chunk_size);
+ } else {
+ if ((sc = WIN_MAP2(ii->chunk, ctx, &sw, scn, 0, sb->header.chunk_size, grn_io_rdonly))) {
+ GRN_OUTPUT_CSTR("chunk size");
+ GRN_OUTPUT_INT64(sb->header.chunk_size);
+ } else {
+ GRN_OUTPUT_CSTR("unmappable chunk size");
+ GRN_OUTPUT_INT64(sb->header.chunk_size);
+ }
+ }
+ GRN_OUTPUT_CSTR("buffer term");
+ GRN_OUTPUT_ARRAY_OPEN("TERMS", sb->header.nterms);
+
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, ii->lexicon->header.domain);
+ for (bt = sb->terms, n = sb->header.nterms; n; n--, bt++) {
+ grn_id tid, tid_;
+ char key[GRN_TABLE_MAX_KEY_SIZE];
+ int key_size;
+ uint16_t nextb;
+ uint32_t nchunks = 0;
+ chunk_info *cinfo = NULL;
+ grn_id crid = GRN_ID_NIL;
+ docinfo bid = {0, 0};
+ uint32_t sdf = 0, snn = 0;
+ uint32_t *srp = NULL, *ssp = NULL, *stp = NULL, *sop = NULL, *snp = NULL;
+ if (!bt->tid && !bt->pos_in_buffer && !bt->size_in_buffer) {
+ nterms_void++;
+ continue;
+ }
+ GRN_OUTPUT_ARRAY_OPEN("TERM", -1);
+ tid = (bt->tid & GRN_ID_MAX);
+ key_size = grn_table_get_key(ctx, ii->lexicon, tid, key, GRN_TABLE_MAX_KEY_SIZE);
+ tid_ = grn_table_get(ctx, ii->lexicon, key, key_size);
+ GRN_TEXT_SET(ctx, &buf, key, key_size);
+ GRN_OUTPUT_OBJ(&buf, NULL);
+ GRN_OUTPUT_INT64(bt->tid);
+ GRN_OUTPUT_INT64(tid_);
+ nextb = bt->pos_in_buffer;
+ size_in_buffer += bt->size_in_buffer;
+ if (tid != tid_ && (bt->size_in_buffer || bt->size_in_chunk)) {
+ ndeleted_terms_with_value++;
+ }
+ GETNEXTB();
+ GRN_OUTPUT_INT64(bt->size_in_buffer);
+ GRN_OUTPUT_INT64(bt->size_in_chunk);
+ if (sc && bt->size_in_chunk) {
+ uint8_t *scp = sc + bt->pos_in_chunk;
+ uint8_t *sce = scp + bt->size_in_chunk;
+ size_t size = S_SEGMENT * ii->n_elements;
+ if ((bt->tid & CHUNK_SPLIT)) {
+ int i;
+ GRN_B_DEC(nchunks, scp);
+ if (!(cinfo = GRN_MALLOCN(chunk_info, nchunks + 1))) {
+ datavec_fin(ctx, rdv);
+ GRN_OBJ_FIN(ctx, &buf);
+ return;
+ }
+ for (i = 0; i < nchunks; i++) {
+ GRN_B_DEC(cinfo[i].segno, scp);
+ GRN_B_DEC(cinfo[i].size, scp);
+ GRN_B_DEC(cinfo[i].dgap, scp);
+ crid += cinfo[i].dgap;
+ }
+ }
+ if (sce > scp) {
+ size += grn_p_decv(ctx, scp, sce - scp, rdv, ii->n_elements);
+ {
+ int j = 0;
+ sdf = rdv[j].data_size;
+ GRN_OUTPUT_INT64(sdf);
+ srp = rdv[j++].data;
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) { ssp = rdv[j++].data; }
+ if (sdf != rdv[j].data_size) {
+ nterms_with_corrupt_chunk++;
+ }
+ stp = rdv[j++].data;
+ if ((ii->header->flags & GRN_OBJ_WITH_WEIGHT)) { sop = rdv[j++].data; }
+ GRN_OUTPUT_INT64(rdv[j].data_size);
+ snn = rdv[j].data_size;
+ snp = rdv[j].data;
+ }
+ nterm_with_chunk++;
+ }
+ }
+ {
+ uint16_t pos;
+ grn_id rid, sid, rid_ = 0, sid_ = 0;
+ uint8_t *p;
+ buffer_rec *r;
+ for (pos = bt->pos_in_buffer; pos; pos = r->step) {
+ if (pos < lower_bound) {
+ nviolations++;
+ }
+ r = BUFFER_REC_AT(sb, pos);
+ p = NEXT_ADDR(r);
+ GRN_B_DEC(rid, p);
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ GRN_B_DEC(sid, p);
+ } else {
+ sid = 1;
+ }
+ if (rid < rid_ || (rid == rid_ && sid < sid_)) {
+ nloops++;
+ }
+ rid_ = rid;
+ sid_ = sid;
+ }
+ }
+ GRN_OUTPUT_ARRAY_CLOSE();
+ if (cinfo) { GRN_FREE(cinfo); }
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_CSTR("buffer free");
+ GRN_OUTPUT_INT64(sb->header.buffer_free);
+ GRN_OUTPUT_CSTR("size in buffer");
+ GRN_OUTPUT_INT64(size_in_buffer);
+ GRN_OUTPUT_CSTR("nterms");
+ GRN_OUTPUT_INT64(sb->header.nterms);
+ if (nterms_void != sb->header.nterms_void) {
+ GRN_OUTPUT_CSTR("nterms void gap");
+ GRN_OUTPUT_INT64(nterms_void - sb->header.nterms_void);
+ }
+ GRN_OUTPUT_CSTR("nterms with chunk");
+ GRN_OUTPUT_INT64(nterm_with_chunk);
+ if (nterms_with_corrupt_chunk) {
+ GRN_OUTPUT_CSTR("nterms with corrupt chunk");
+ GRN_OUTPUT_INT64(nterms_with_corrupt_chunk);
+ }
+ if (ndeleted_terms_with_value) {
+ GRN_OUTPUT_CSTR("number of deleted terms with value");
+ GRN_OUTPUT_INT64(ndeleted_terms_with_value);
+ }
+ if (nloops) {
+ GRN_OUTPUT_CSTR("number of loops");
+ GRN_OUTPUT_INT64(nloops);
+ }
+ if (nviolations) {
+ GRN_OUTPUT_CSTR("number of violations");
+ GRN_OUTPUT_INT64(nviolations);
+ }
+ GRN_OUTPUT_MAP_CLOSE();
+ datavec_fin(ctx, rdv);
+ if (sc) { grn_io_win_unmap2(&sw); }
+ buffer_close(ctx, ii, pseg);
+}
+
+typedef struct {
+ buffer_term *bt;
+ const char *key;
+ uint32_t key_size;
+} term_sort;
+
+static int
+term_compar(const void *t1, const void *t2)
+{
+ int r;
+ const term_sort *x = (term_sort *)t1, *y = (term_sort *)t2;
+ if (x->key_size > y->key_size) {
+ return (r = memcmp(x->key, y->key, y->key_size)) ? r : x->key_size - y->key_size;
+ } else {
+ return (r = memcmp(x->key, y->key, x->key_size)) ? r : x->key_size - y->key_size;
+ }
+}
+
+static grn_rc
+term_split(grn_ctx *ctx, grn_obj *lexicon, buffer *sb, buffer *db0, buffer *db1)
+{
+ uint16_t i, n, *nt;
+ buffer_term *bt;
+ uint32_t s, th = (sb->header.chunk_size + sb->header.nterms) >> 1;
+ term_sort *ts = GRN_MALLOC(sb->header.nterms * sizeof(term_sort));
+ if (!ts) { return GRN_NO_MEMORY_AVAILABLE; }
+ for (i = 0, n = sb->header.nterms, bt = sb->terms; n; bt++, n--) {
+ if (bt->tid) {
+ grn_id tid = bt->tid & GRN_ID_MAX;
+ ts[i].key = _grn_table_key(ctx, lexicon, tid, &ts[i].key_size);
+ ts[i].bt = bt;
+ i++;
+ }
+ }
+ qsort(ts, i, sizeof(term_sort), term_compar);
+ memset(db0, 0, S_SEGMENT);
+ bt = db0->terms;
+ nt = &db0->header.nterms;
+ for (s = 0; n + 1 < i && s <= th; n++, bt++) {
+ memcpy(bt, ts[n].bt, sizeof(buffer_term));
+ (*nt)++;
+ s += ts[n].bt->size_in_chunk + 1;
+ }
+ memset(db1, 0, S_SEGMENT);
+ bt = db1->terms;
+ nt = &db1->header.nterms;
+ for (; n < i; n++, bt++) {
+ memcpy(bt, ts[n].bt, sizeof(buffer_term));
+ (*nt)++;
+ }
+ GRN_FREE(ts);
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "d0=%d d1=%d", db0->header.nterms, db1->header.nterms);
+ return GRN_SUCCESS;
+}
+
+static void
+array_update(grn_ctx *ctx, grn_ii *ii, uint32_t dls, buffer *db)
+{
+ uint16_t n;
+ buffer_term *bt;
+ uint32_t *a, pos = SEG2POS(dls, sizeof(buffer_header));
+ for (n = db->header.nterms, bt = db->terms; n; n--, bt++) {
+ if (bt->tid) {
+ grn_id tid = bt->tid & GRN_ID_MAX;
+ if ((a = array_at(ctx, ii, tid))) {
+ a[0] = pos;
+ array_unref(ii, tid);
+ } else {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "array_at failed (%d)", tid);
+ }
+ }
+ pos += sizeof(buffer_term) >> 2;
+ }
+}
+
+static grn_rc
+buffer_split(grn_ctx *ctx, grn_ii *ii, uint32_t seg, grn_hash *h)
+{
+ grn_rc rc;
+ grn_io_win sw, dw0, dw1;
+ buffer *sb, *db0 = NULL, *db1 = NULL;
+ uint8_t *sc = NULL, *dc0, *dc1;
+ uint32_t dps0, dps1, dls0, dls1, sps, scn, dcn0 = 0, dcn1 = 0;
+ if (ii->header->binfo[seg] == NOT_ASSIGNED) { return GRN_FILE_CORRUPT; }
+ if ((rc = buffer_segment_reserve(ctx, ii, &dls0, &dps0, &dls1, &dps1))) {
+ return rc;
+ }
+ sps = buffer_open(ctx, ii, SEG2POS(seg, 0), NULL, &sb);
+ if (sps != NOT_ASSIGNED) {
+ GRN_IO_SEG_REF(ii->seg, dps0, db0);
+ if (db0) {
+ GRN_IO_SEG_REF(ii->seg, dps1, db1);
+ if (db1) {
+ uint32_t actual_db0_chunk_size = 0;
+ uint32_t actual_db1_chunk_size = 0;
+ uint32_t max_dest_chunk_size = sb->header.chunk_size + S_SEGMENT;
+ if ((dc0 = GRN_MALLOC(max_dest_chunk_size * 2))) {
+ if ((dc1 = GRN_MALLOC(max_dest_chunk_size * 2))) {
+ if ((scn = sb->header.chunk) == NOT_ASSIGNED ||
+ (sc = WIN_MAP2(ii->chunk, ctx, &sw, scn, 0,
+ sb->header.chunk_size, grn_io_rdonly))) {
+ term_split(ctx, ii->lexicon, sb, db0, db1);
+ if (!(rc = buffer_merge(ctx, ii, seg, h, sb, sc, db0, dc0))) {
+ actual_db0_chunk_size = db0->header.chunk_size;
+ if (actual_db0_chunk_size >= max_dest_chunk_size) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "actual_db0_chunk_size(%d) >= max_dest_chunk_size(%d)",
+ actual_db0_chunk_size, max_dest_chunk_size);
+ }
+ if (!actual_db0_chunk_size ||
+ !(rc = chunk_new(ctx, ii, &dcn0, actual_db0_chunk_size))) {
+ db0->header.chunk = actual_db0_chunk_size ? dcn0 : NOT_ASSIGNED;
+ fake_map2(ctx, ii->chunk, &dw0, dc0, dcn0, actual_db0_chunk_size);
+ if (!(rc = grn_io_win_unmap2(&dw0))) {
+ if (!(rc = buffer_merge(ctx, ii, seg, h, sb, sc, db1, dc1))) {
+ actual_db1_chunk_size = db1->header.chunk_size;
+ if (actual_db1_chunk_size >= max_dest_chunk_size) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "actual_db1_chunk_size(%d) >= max_dest_chunk_size(%d)",
+ actual_db1_chunk_size, max_dest_chunk_size);
+ }
+ if (!actual_db1_chunk_size ||
+ !(rc = chunk_new(ctx, ii, &dcn1, actual_db1_chunk_size))) {
+ fake_map2(ctx, ii->chunk, &dw1, dc1, dcn1, actual_db1_chunk_size);
+ if (!(rc = grn_io_win_unmap2(&dw1))) {
+ db1->header.chunk = actual_db1_chunk_size ? dcn1 : NOT_ASSIGNED;
+ buffer_segment_update(ii, dls0, dps0);
+ buffer_segment_update(ii, dls1, dps1);
+ array_update(ctx, ii, dls0, db0);
+ array_update(ctx, ii, dls1, db1);
+ buffer_segment_clear(ii, seg);
+ ii->header->total_chunk_size += actual_db0_chunk_size;
+ ii->header->total_chunk_size += actual_db1_chunk_size;
+ if (scn != NOT_ASSIGNED) {
+ grn_io_win_unmap2(&sw);
+ chunk_free(ctx, ii, scn, 0, sb->header.chunk_size);
+ ii->header->total_chunk_size -= sb->header.chunk_size;
+ }
+ } else {
+ if (actual_db1_chunk_size) {
+ chunk_free(ctx, ii, dcn1, 0, actual_db1_chunk_size);
+ }
+ if (actual_db0_chunk_size) {
+ chunk_free(ctx, ii, dcn0, 0, actual_db0_chunk_size);
+ }
+ GRN_FREE(dc1);
+ if (scn != NOT_ASSIGNED) { grn_io_win_unmap2(&sw); }
+ }
+ } else {
+ if (actual_db0_chunk_size) {
+ chunk_free(ctx, ii, dcn0, 0, actual_db0_chunk_size);
+ }
+ GRN_FREE(dc1);
+ if (scn != NOT_ASSIGNED) { grn_io_win_unmap2(&sw); }
+ }
+ } else {
+ if (actual_db0_chunk_size) {
+ chunk_free(ctx, ii, dcn0, 0, actual_db0_chunk_size);
+ }
+ GRN_FREE(dc1);
+ if (scn != NOT_ASSIGNED) { grn_io_win_unmap2(&sw); }
+ }
+ } else {
+ if (actual_db0_chunk_size) {
+ chunk_free(ctx, ii, dcn0, 0, actual_db0_chunk_size);
+ }
+ GRN_FREE(dc1);
+ GRN_FREE(dc0);
+ if (scn != NOT_ASSIGNED) { grn_io_win_unmap2(&sw); }
+ }
+ } else {
+ GRN_FREE(dc1);
+ GRN_FREE(dc0);
+ if (scn != NOT_ASSIGNED) { grn_io_win_unmap2(&sw); }
+ }
+ } else {
+ GRN_FREE(dc1);
+ GRN_FREE(dc0);
+ if (scn != NOT_ASSIGNED) { grn_io_win_unmap2(&sw); }
+ }
+ } else {
+ GRN_FREE(dc1);
+ GRN_FREE(dc0);
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ } else {
+ GRN_FREE(dc0);
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ } else {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ GRN_IO_SEG_UNREF(ii->seg, dps1);
+ } else {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ GRN_IO_SEG_UNREF(ii->seg, dps0);
+ } else {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ buffer_close(ctx, ii, sps);
+ } else {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ }
+ return rc;
+}
+
+#define SCALE_FACTOR 2048
+#define MAX_NTERMS 8192
+#define SPLIT_COND (b->header.nterms > 1024 ||\
+ (b->header.nterms > 1 &&\
+ b->header.chunk_size * 100 > ii->header->total_chunk_size))
+
+inline static uint32_t
+buffer_new(grn_ctx *ctx, grn_ii *ii, int size, uint32_t *pos,
+ buffer_term **bt, buffer_rec **br, buffer **bp, grn_id id, grn_hash *h)
+{
+ buffer *b = NULL;
+ grn_id tid;
+ uint16_t offset;
+ char key[GRN_TABLE_MAX_KEY_SIZE];
+ // unsigned int key_size;
+ // const char *key = _grn_table_key(ctx, ii->lexicon, id, &key_size);
+ int key_size = grn_table_get_key(ctx, ii->lexicon, id, key, GRN_TABLE_MAX_KEY_SIZE);
+ uint32_t *a, lseg = NOT_ASSIGNED, pseg = NOT_ASSIGNED;
+ grn_table_cursor *tc = NULL;
+ if (S_SEGMENT - sizeof(buffer_header) < size + sizeof(buffer_term)) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "requested size(%d) is too large", size);
+ return NOT_ASSIGNED;
+ }
+ if (ii->lexicon->header.type == GRN_TABLE_PAT_KEY) {
+ if (ii->lexicon->header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ tc = grn_table_cursor_open(ctx, ii->lexicon, key, key_size, NULL, 0, 0, -1,
+ GRN_CURSOR_ASCENDING|GRN_CURSOR_GT);
+ } else {
+ tc = grn_table_cursor_open(ctx, ii->lexicon, NULL, 0, key, key_size, 0, -1,
+ GRN_CURSOR_PREFIX);
+ }
+ } else {
+ tc = grn_table_cursor_open(ctx, ii->lexicon, NULL, 0, NULL, 0, 0, -1,
+ GRN_CURSOR_ASCENDING);
+ }
+ if (tc) {
+ while (lseg == NOT_ASSIGNED && (tid = grn_table_cursor_next(ctx, tc))) {
+ if ((a = array_at(ctx, ii, tid))) {
+ for (;;) {
+ uint32_t pos = a[0];
+ if (!pos || (pos & 1)) { break; }
+ if ((pseg = buffer_open(ctx, ii, pos, NULL, &b)) == NOT_ASSIGNED) { break; }
+ if (b->header.buffer_free >= size + sizeof(buffer_term)) {
+ lseg = LSEG(pos);
+ break;
+ }
+ buffer_close(ctx, ii, pseg);
+ if (SPLIT_COND)
+ /* ((S_SEGMENT - sizeof(buffer_header) + ii->header->bmax -
+ b->header.nterms * sizeof(buffer_term)) * 4 <
+ b->header.chunk_size) */
+ {
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "nterms=%d chunk=%d total=%" GRN_FMT_INT64U,
+ b->header.nterms,
+ b->header.chunk_size,
+ ii->header->total_chunk_size >> 10);
+ if (buffer_split(ctx, ii, LSEG(pos), h)) { break; }
+ } else {
+ if (S_SEGMENT - sizeof(buffer_header)
+ - b->header.nterms * sizeof(buffer_term)
+ < size + sizeof(buffer_term)) {
+ break;
+ }
+ if (buffer_flush(ctx, ii, LSEG(pos), h)) { break; }
+ }
+ }
+ array_unref(ii, tid);
+ }
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ if (lseg == NOT_ASSIGNED) {
+ if (buffer_segment_new(ctx, ii, &lseg) ||
+ (pseg = buffer_open(ctx, ii, SEG2POS(lseg, 0), NULL, &b)) == NOT_ASSIGNED) {
+ return NOT_ASSIGNED;
+ }
+ memset(b, 0, S_SEGMENT);
+ b->header.buffer_free = S_SEGMENT - sizeof(buffer_header);
+ b->header.chunk = NOT_ASSIGNED;
+ }
+ if (b->header.nterms_void) {
+ for (offset = 0; offset < b->header.nterms; offset++) {
+ if (!b->terms[offset].tid) { break; }
+ }
+ if (offset == b->header.nterms) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "inconsistent buffer(%d)", lseg);
+ b->header.nterms_void = 0;
+ b->header.nterms++;
+ b->header.buffer_free -= size + sizeof(buffer_term);
+ } else {
+ b->header.nterms_void--;
+ b->header.buffer_free -= size;
+ }
+ } else {
+ offset = b->header.nterms++;
+ b->header.buffer_free -= size + sizeof(buffer_term);
+ }
+ *pos = SEG2POS(lseg, (sizeof(buffer_header) + sizeof(buffer_term) * offset));
+ *bt = &b->terms[offset];
+ *br = (buffer_rec *)(((byte *)&b->terms[b->header.nterms]) + b->header.buffer_free);
+ *bp = b;
+ return pseg;
+}
+
+/* ii */
+
+static grn_ii *
+_grn_ii_create(grn_ctx *ctx, grn_ii *ii, const char *path, grn_obj *lexicon, uint32_t flags)
+{
+ int i;
+ grn_io *seg, *chunk;
+ char path2[PATH_MAX];
+ struct grn_ii_header *header;
+ grn_obj_flags lflags;
+ grn_encoding encoding;
+ grn_obj *tokenizer;
+ /*
+ for (i = 0; i < 32; i++) {
+ new_histogram[i] = 0;
+ free_histogram[i] = 0;
+ }
+ */
+ if (grn_table_get_info(ctx, lexicon, &lflags, &encoding, &tokenizer, NULL)) {
+ return NULL;
+ }
+ if (path && strlen(path) + 6 >= PATH_MAX) { return NULL; }
+ seg = grn_io_create(ctx, path, sizeof(struct grn_ii_header),
+ S_SEGMENT, MAX_PSEG, grn_io_auto, GRN_IO_EXPIRE_SEGMENT);
+ if (!seg) { return NULL; }
+ if (path) {
+ strcpy(path2, path);
+ strcat(path2, ".c");
+ chunk = grn_io_create(ctx, path2, 0, S_CHUNK, GRN_II_MAX_CHUNK, grn_io_auto,
+ GRN_IO_EXPIRE_SEGMENT);
+ } else {
+ chunk = grn_io_create(ctx, NULL, 0, S_CHUNK, GRN_II_MAX_CHUNK, grn_io_auto, 0);
+ }
+ if (!chunk) {
+ grn_io_close(ctx, seg);
+ return NULL;
+ }
+ header = grn_io_header(seg);
+ grn_io_set_type(seg, GRN_COLUMN_INDEX);
+ for (i = 0; i < GRN_II_MAX_LSEG; i++) {
+ header->ainfo[i] = NOT_ASSIGNED;
+ header->binfo[i] = NOT_ASSIGNED;
+ }
+ for (i = 0; i <= GRN_II_N_CHUNK_VARIATION; i++) {
+ header->free_chunks[i] = NOT_ASSIGNED;
+ header->garbages[i] = NOT_ASSIGNED;
+ }
+ header->flags = flags;
+ ii->seg = seg;
+ ii->chunk = chunk;
+ ii->lexicon = lexicon;
+ ii->lflags = lflags;
+ ii->encoding = encoding;
+ ii->header = header;
+ ii->n_elements = 2;
+ if ((flags & GRN_OBJ_WITH_SECTION)) { ii->n_elements++; }
+ if ((flags & GRN_OBJ_WITH_WEIGHT)) { ii->n_elements++; }
+ if ((flags & GRN_OBJ_WITH_POSITION)) { ii->n_elements++; }
+ return ii;
+}
+
+grn_ii *
+grn_ii_create(grn_ctx *ctx, const char *path, grn_obj *lexicon, uint32_t flags)
+{
+ grn_ii *ii = NULL;
+ if (!(ii = GRN_GMALLOC(sizeof(grn_ii)))) {
+ return NULL;
+ }
+ GRN_DB_OBJ_SET_TYPE(ii, GRN_COLUMN_INDEX);
+ if (!_grn_ii_create(ctx, ii, path, lexicon, flags)) {
+ GRN_FREE(ii);
+ return NULL;
+ }
+ return ii;
+}
+
+grn_rc
+grn_ii_remove(grn_ctx *ctx, const char *path)
+{
+ grn_rc rc;
+ char buffer[PATH_MAX];
+ if (!path || strlen(path) > PATH_MAX - 4) { return GRN_INVALID_ARGUMENT; }
+ if ((rc = grn_io_remove(ctx, path))) { goto exit; }
+ snprintf(buffer, PATH_MAX, "%s.c", path);
+ rc = grn_io_remove(ctx, buffer);
+exit :
+ return rc;
+}
+
+grn_rc
+grn_ii_truncate(grn_ctx *ctx, grn_ii *ii)
+{
+ grn_rc rc;
+ const char *io_segpath, *io_chunkpath;
+ char *segpath, *chunkpath = NULL;
+ grn_obj *lexicon;
+ uint32_t flags;
+ if ((io_segpath = grn_io_path(ii->seg)) && *io_segpath != '\0') {
+ if (!(segpath = GRN_STRDUP(io_segpath))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "cannot duplicate path: <%s>", io_segpath);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ if ((io_chunkpath = grn_io_path(ii->chunk)) && *io_chunkpath != '\0') {
+ if (!(chunkpath = GRN_STRDUP(io_chunkpath))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "cannot duplicate path: <%s>", io_chunkpath);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ } else {
+ chunkpath = NULL;
+ }
+ } else {
+ segpath = NULL;
+ }
+ lexicon = ii->lexicon;
+ flags = ii->header->flags;
+ if ((rc = grn_io_close(ctx, ii->seg))) { goto exit; }
+ if ((rc = grn_io_close(ctx, ii->chunk))) { goto exit; }
+ ii->seg = NULL;
+ ii->chunk = NULL;
+ if (segpath && (rc = grn_io_remove(ctx, segpath))) { goto exit; }
+ if (chunkpath && (rc = grn_io_remove(ctx, chunkpath))) { goto exit; }
+ if (!_grn_ii_create(ctx, ii, segpath, lexicon, flags)) {
+ rc = GRN_UNKNOWN_ERROR;
+ }
+exit:
+ if (segpath) { GRN_FREE(segpath); }
+ if (chunkpath) { GRN_FREE(chunkpath); }
+ return rc;
+}
+
+grn_ii *
+grn_ii_open(grn_ctx *ctx, const char *path, grn_obj *lexicon)
+{
+ grn_io *seg, *chunk;
+ grn_ii *ii;
+ char path2[PATH_MAX];
+ struct grn_ii_header *header;
+ grn_obj_flags lflags;
+ grn_encoding encoding;
+ grn_obj *tokenizer;
+ if (grn_table_get_info(ctx, lexicon, &lflags, &encoding, &tokenizer, NULL)) {
+ return NULL;
+ }
+ if (strlen(path) + 6 >= PATH_MAX) { return NULL; }
+ strcpy(path2, path);
+ strcat(path2, ".c");
+ seg = grn_io_open(ctx, path, grn_io_auto);
+ if (!seg) { return NULL; }
+ chunk = grn_io_open(ctx, path2, grn_io_auto);
+ if (!chunk) {
+ grn_io_close(ctx, seg);
+ return NULL;
+ }
+ header = grn_io_header(seg);
+ if (grn_io_get_type(seg) != GRN_COLUMN_INDEX) {
+ ERR(GRN_INVALID_FORMAT, "file type unmatch");
+ grn_io_close(ctx, seg);
+ grn_io_close(ctx, chunk);
+ return NULL;
+ }
+ if (!(ii = GRN_GMALLOC(sizeof(grn_ii)))) {
+ grn_io_close(ctx, seg);
+ grn_io_close(ctx, chunk);
+ return NULL;
+ }
+ GRN_DB_OBJ_SET_TYPE(ii, GRN_COLUMN_INDEX);
+ ii->seg = seg;
+ ii->chunk = chunk;
+ ii->lexicon = lexicon;
+ ii->lflags = lflags;
+ ii->encoding = encoding;
+ ii->header = header;
+ ii->n_elements = 2;
+ if ((header->flags & GRN_OBJ_WITH_SECTION)) { ii->n_elements++; }
+ if ((header->flags & GRN_OBJ_WITH_WEIGHT)) { ii->n_elements++; }
+ if ((header->flags & GRN_OBJ_WITH_POSITION)) { ii->n_elements++; }
+ return ii;
+}
+
+grn_rc
+grn_ii_close(grn_ctx *ctx, grn_ii *ii)
+{
+ grn_rc rc;
+ if (!ii) { return GRN_INVALID_ARGUMENT; }
+ if ((rc = grn_io_close(ctx, ii->seg))) { return rc; }
+ if ((rc = grn_io_close(ctx, ii->chunk))) { return rc; }
+ GRN_GFREE(ii);
+ /*
+ {
+ int i;
+ for (i = 0; i < 32; i++) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "new[%d]=%d free[%d]=%d",
+ i, new_histogram[i],
+ i, free_histogram[i]);
+ }
+ }
+ */
+ return rc;
+}
+
+grn_rc
+grn_ii_info(grn_ctx *ctx, grn_ii *ii, uint64_t *seg_size, uint64_t *chunk_size)
+{
+ grn_rc rc;
+
+ if (seg_size) {
+ if ((rc = grn_io_size(ctx, ii->seg, seg_size))) {
+ return rc;
+ }
+ }
+
+ if (chunk_size) {
+ if ((rc = grn_io_size(ctx, ii->chunk, chunk_size))) {
+ return rc;
+ }
+ }
+
+ return GRN_SUCCESS;
+}
+
+void
+grn_ii_expire(grn_ctx *ctx, grn_ii *ii)
+{
+ /*
+ grn_io_expire(ctx, ii->seg, 128, 1000000);
+ */
+ grn_io_expire(ctx, ii->chunk, 0, 1000000);
+}
+
+#define BIT11_01(x) ((x >> 1) & 0x7ff)
+#define BIT31_12(x) (x >> 12)
+
+grn_rc
+grn_ii_update_one(grn_ctx *ctx, grn_ii *ii, grn_id tid, grn_ii_updspec *u, grn_hash *h)
+{
+ grn_rc rc = GRN_SUCCESS;
+ buffer *b;
+ uint8_t *bs;
+ buffer_rec *br = NULL;
+ buffer_term *bt;
+ uint32_t pseg = 0, pos = 0, size, *a;
+ if (!tid) { return rc; }
+ if (!u->tf || !u->sid) { return grn_ii_delete_one(ctx, ii, tid, u, h); }
+ if (u->sid > ii->header->smax) { ii->header->smax = u->sid; }
+ if (!(a = array_get(ctx, ii, tid))) { return GRN_NO_MEMORY_AVAILABLE; }
+ if (!(bs = encode_rec(ctx, ii, u, &size, 0))) {
+ rc = GRN_NO_MEMORY_AVAILABLE; goto exit;
+ }
+ for (;;) {
+ if (a[0]) {
+ if (!(a[0] & 1)) {
+ pos = a[0];
+ if ((pseg = buffer_open(ctx, ii, pos, &bt, &b)) == NOT_ASSIGNED) {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ if (b->header.buffer_free < size) {
+ int bfb = b->header.buffer_free;
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "flushing a[0]=%d seg=%d(%p) free=%d",
+ a[0], LSEG(a[0]), b, b->header.buffer_free);
+ buffer_close(ctx, ii, pseg);
+ if (SPLIT_COND)
+ /*((S_SEGMENT - sizeof(buffer_header) + ii->header->bmax -
+ b->header.nterms * sizeof(buffer_term)) * 4 <
+ b->header.chunk_size)*/
+ {
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "nterms=%d chunk=%d total=%" GRN_FMT_INT64U,
+ b->header.nterms,
+ b->header.chunk_size,
+ ii->header->total_chunk_size >> 10);
+ if ((rc = buffer_split(ctx, ii, LSEG(pos), h))) { goto exit; }
+ continue;
+ }
+ if ((rc = buffer_flush(ctx, ii, LSEG(pos), h))) { goto exit; }
+ if (a[0] != pos) {
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "grn_ii_update_one: a[0] changed %d->%d", a[0], pos);
+ continue;
+ }
+ if ((pseg = buffer_open(ctx, ii, pos, &bt, &b)) == NOT_ASSIGNED) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "buffer not found a[0]=%d", a[0]);
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "flushed a[0]=%d seg=%d(%p) free=%d->%d nterms=%d v=%d",
+ a[0], LSEG(a[0]), b, bfb, b->header.buffer_free,
+ b->header.nterms, b->header.nterms_void);
+ if (b->header.buffer_free < size) {
+ buffer_close(ctx, ii, pseg);
+ GRN_LOG(ctx, GRN_LOG_CRIT, "buffer(%d) is full (%d < %d) in grn_ii_update_one",
+ a[0], b->header.buffer_free, size);
+ /* todo: direct merge */
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ }
+ b->header.buffer_free -= size;
+ br = (buffer_rec *)(((byte *)&b->terms[b->header.nterms])
+ + b->header.buffer_free);
+ } else {
+ grn_ii_updspec u2;
+ uint32_t size2 = 0, v = a[0];
+ struct _grn_ii_pos pos2;
+ pos2.pos = a[1];
+ pos2.next = NULL;
+ u2.pos = &pos2;
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ u2.rid = BIT31_12(v);
+ u2.sid = BIT11_01(v);
+ } else {
+ u2.rid = v >> 1;
+ u2.sid = 1;
+ }
+ u2.tf = 1;
+ u2.weight = 0;
+ if (u2.rid != u->rid || u2.sid != u->sid) {
+ uint8_t *bs2 = encode_rec(ctx, ii, &u2, &size2, 0);
+ if (!bs2) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "encode_rec on grn_ii_update_one failed !");
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ pseg = buffer_new(ctx, ii, size + size2, &pos, &bt, &br, &b, tid, h);
+ if (pseg == NOT_ASSIGNED) {
+ GRN_FREE(bs2);
+ goto exit;
+ }
+ bt->tid = tid;
+ bt->size_in_chunk = 0;
+ bt->pos_in_chunk = 0;
+ bt->size_in_buffer = 0;
+ bt->pos_in_buffer = 0;
+ if ((rc = buffer_put(ctx, ii, b, bt, br, bs2, &u2, size2))) {
+ GRN_FREE(bs2);
+ buffer_close(ctx, ii, pseg);
+ goto exit;
+ }
+ br = (buffer_rec *)(((byte *)br) + size2);
+ GRN_FREE(bs2);
+ }
+ }
+ }
+ break;
+ }
+ if (!br) {
+ if (u->tf == 1 && u->weight == 0) {
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ if (u->rid < 0x100000 && u->sid < 0x800) {
+ a[0] = (u->rid << 12) + (u->sid << 1) + 1;
+ a[1] = u->pos->pos;
+ goto exit;
+ }
+ } else {
+ a[0] = (u->rid << 1) + 1;
+ a[1] = u->pos->pos;
+ goto exit;
+ }
+ }
+ pseg = buffer_new(ctx, ii, size, &pos, &bt, &br, &b, tid, h);
+ if (pseg == NOT_ASSIGNED) { goto exit; }
+ bt->tid = tid;
+ bt->size_in_chunk = 0;
+ bt->pos_in_chunk = 0;
+ bt->size_in_buffer = 0;
+ bt->pos_in_buffer = 0;
+ }
+ rc = buffer_put(ctx, ii, b, bt, br, bs, u, size);
+ buffer_close(ctx, ii, pseg);
+ if (!a[0] || (a[0] & 1)) { a[0] = pos; }
+exit :
+ array_unref(ii, tid);
+ if (bs) { GRN_FREE(bs); }
+ if (u->tf != u->atf) {
+ char term[GRN_TABLE_MAX_KEY_SIZE];
+ int term_size;
+ term_size = grn_table_get_key(ctx, ii->lexicon, tid,
+ term, GRN_TABLE_MAX_KEY_SIZE);
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "too many postings(%d). %d postings are discarded. "
+ "term: <%d>(<%.*s>)",
+ u->atf, u->atf - u->tf,
+ tid, term_size, term);
+ }
+ grn_ii_expire(ctx, ii);
+ return rc;
+}
+
+grn_rc
+grn_ii_delete_one(grn_ctx *ctx, grn_ii *ii, grn_id tid, grn_ii_updspec *u, grn_hash *h)
+{
+ grn_rc rc = GRN_SUCCESS;
+ buffer *b;
+ uint8_t *bs = NULL;
+ buffer_rec *br;
+ buffer_term *bt;
+ uint32_t pseg, size, *a;
+ if (!tid) { return rc; }
+ if (!(a = array_at(ctx, ii, tid))) { return GRN_INVALID_ARGUMENT; }
+ for (;;) {
+ if (!a[0]) { goto exit; }
+ if (a[0] & 1) {
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ uint32_t rid = BIT31_12(a[0]);
+ uint32_t sid = BIT11_01(a[0]);
+ if (u->rid == rid && (!u->sid || u->sid == sid)) {
+ a[0] = 0;
+ lexicon_delete(ctx, ii, tid, h);
+ }
+ } else {
+ uint32_t rid = a[0] >> 1;
+ if (u->rid == rid) {
+ a[0] = 0;
+ lexicon_delete(ctx, ii, tid, h);
+ }
+ }
+ goto exit;
+ }
+ if (!(bs = encode_rec(ctx, ii, u, &size, 1))) {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ if ((pseg = buffer_open(ctx, ii, a[0], &bt, &b)) == NOT_ASSIGNED) {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ if (b->header.buffer_free < size) {
+ uint32_t _a = a[0];
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "flushing! b=%p free=%d, seg(%d)", b, b->header.buffer_free, LSEG(a[0]));
+ buffer_close(ctx, ii, pseg);
+ if ((rc = buffer_flush(ctx, ii, LSEG(a[0]), h))) { goto exit; }
+ if (a[0] != _a) {
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "grn_ii_delete_one: a[0] changed %d->%d)", a[0], _a);
+ continue;
+ }
+ if ((pseg = buffer_open(ctx, ii, a[0], &bt, &b)) == NOT_ASSIGNED) {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "flushed! b=%p free=%d, seg(%d)", b, b->header.buffer_free, LSEG(a[0]));
+ if (b->header.buffer_free < size) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "buffer(%d) is full (%d < %d) in grn_ii_delete_one",
+ a[0], b->header.buffer_free, size);
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ buffer_close(ctx, ii, pseg);
+ goto exit;
+ }
+ }
+
+ b->header.buffer_free -= size;
+ br = (buffer_rec *)(((byte *)&b->terms[b->header.nterms]) + b->header.buffer_free);
+ rc = buffer_put(ctx, ii, b, bt, br, bs, u, size);
+ buffer_close(ctx, ii, pseg);
+ break;
+ }
+exit :
+ array_unref(ii, tid);
+ if (bs) { GRN_FREE(bs); }
+ return rc;
+}
+
+#define CHUNK_USED 1
+#define BUFFER_USED 2
+#define SOLE_DOC_USED 4
+#define SOLE_POS_USED 8
+
+struct _grn_ii_cursor {
+ grn_db_obj obj;
+ grn_ctx *ctx;
+ grn_ii *ii;
+ grn_id id;
+ grn_ii_posting *post;
+
+ grn_id min;
+ grn_id max;
+ grn_ii_posting pc;
+ grn_ii_posting pb;
+
+ uint32_t cdf;
+ uint32_t *cdp;
+ uint32_t *crp;
+ uint32_t *csp;
+ uint32_t *ctp;
+ uint32_t *cwp;
+ uint32_t *cpp;
+
+ uint8_t *bp;
+
+ int nelements;
+ uint32_t nchunks;
+ uint32_t curr_chunk;
+ chunk_info *cinfo;
+ grn_io_win iw;
+ uint8_t *cp;
+ uint8_t *cpe;
+ datavec rdv[MAX_N_ELEMENTS + 1];
+
+ struct grn_ii_buffer *buf;
+ uint16_t stat;
+ uint16_t nextb;
+ uint32_t buffer_pseg;
+ int flags;
+ uint32_t *ppseg;
+};
+
+static int
+buffer_is_reused(grn_ctx *ctx, grn_ii *ii, grn_ii_cursor *c)
+{
+ if (*c->ppseg != c->buffer_pseg) {
+ uint32_t i;
+ for (i = ii->header->bgqtail; i != ii->header->bgqhead; i = (i + 1) & (GRN_II_BGQSIZE - 1)) {
+ if (ii->header->bgqbody[i] == c->buffer_pseg) { return 0; }
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static int
+chunk_is_reused(grn_ctx *ctx, grn_ii *ii, grn_ii_cursor *c, uint32_t offset, uint32_t size)
+{
+ if (*c->ppseg != c->buffer_pseg) {
+ uint32_t i, m, gseg;
+ if (size > S_CHUNK) { return 1; }
+ if (size > (1 << GRN_II_W_LEAST_CHUNK)) {
+ int es = size - 1;
+ GRN_BIT_SCAN_REV(es, m);
+ m++;
+ } else {
+ m = GRN_II_W_LEAST_CHUNK;
+ }
+ gseg = ii->header->garbages[m - GRN_II_W_LEAST_CHUNK];
+ while (gseg != NOT_ASSIGNED) {
+ grn_io_win iw;
+ grn_ii_ginfo *ginfo = WIN_MAP2(ii->chunk, ctx, &iw, gseg, 0, S_GARBAGE, grn_io_rdwr);
+ if (!ginfo) { break; }
+ for (i = 0; i < ginfo->nrecs; i++) {
+ if (ginfo->recs[i] == offset) {
+ grn_io_win_unmap2(&iw);
+ return 0;
+ }
+ }
+ gseg = ginfo->next;
+ grn_io_win_unmap2(&iw);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+#define GRN_II_CURSOR_CMP(c1,c2) \
+ (((c1)->post->rid > (c2)->post->rid) || \
+ (((c1)->post->rid == (c2)->post->rid) && \
+ (((c1)->post->sid > (c2)->post->sid) || \
+ (((c1)->post->sid == (c2)->post->sid) && \
+ ((c1)->post->pos > (c2)->post->pos)))))
+
+grn_ii_cursor *
+grn_ii_cursor_open(grn_ctx *ctx, grn_ii *ii, grn_id tid,
+ grn_id min, grn_id max, int nelements, int flags)
+{
+ grn_ii_cursor *c = NULL;
+ uint32_t pos, *a;
+ if (!(a = array_at(ctx, ii, tid))) { return NULL; }
+ for (;;) {
+ if (!(pos = a[0])) { goto exit; }
+ if (!(c = GRN_MALLOC(sizeof(grn_ii_cursor)))) { goto exit; }
+ memset(c, 0, sizeof(grn_ii_cursor));
+ c->ctx = ctx;
+ c->ii = ii;
+ c->id = tid;
+ c->min = min;
+ c->max = max;
+ c->nelements = nelements;
+ c->flags = flags;
+ if (pos & 1) {
+ c->stat = 0;
+ if ((ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ c->pb.rid = BIT31_12(pos);
+ c->pb.sid = BIT11_01(pos);
+ } else {
+ c->pb.rid = pos >> 1;
+ c->pb.sid = 1;
+ }
+ c->pb.tf = 1;
+ c->pb.weight = 0;
+ c->pb.pos = a[1];
+ } else {
+ uint32_t chunk;
+ buffer_term *bt;
+ if ((c->buffer_pseg = buffer_open(ctx, ii, pos, &bt, &c->buf)) == NOT_ASSIGNED) {
+ GRN_FREE(c);
+ c = NULL;
+ goto exit;
+ }
+ c->ppseg = &ii->header->binfo[LSEG(pos)];
+ if (bt->size_in_chunk && (chunk = c->buf->header.chunk) != NOT_ASSIGNED) {
+ if (!(c->cp = WIN_MAP2(ii->chunk, ctx, &c->iw, chunk, bt->pos_in_chunk,
+ bt->size_in_chunk, grn_io_rdonly))) {
+ buffer_close(ctx, ii, c->buffer_pseg);
+ GRN_FREE(c);
+ c = NULL;
+ goto exit;
+ }
+ if (buffer_is_reused(ctx, ii, c)) {
+ grn_ii_cursor_close(ctx, c);
+ continue;
+ }
+ c->cpe = c->cp + bt->size_in_chunk;
+ if ((bt->tid & CHUNK_SPLIT)) {
+ int i;
+ grn_id crid;
+ GRN_B_DEC(c->nchunks, c->cp);
+ if (chunk_is_reused(ctx, ii, c, chunk, c->buf->header.chunk_size)) {
+ grn_ii_cursor_close(ctx, c);
+ continue;
+ }
+ if (!(c->cinfo = GRN_MALLOCN(chunk_info, c->nchunks))) {
+ buffer_close(ctx, ii, c->buffer_pseg);
+ grn_io_win_unmap2(&c->iw);
+ GRN_FREE(c);
+ c = NULL;
+ goto exit;
+ }
+ for (i = 0, crid = GRN_ID_NIL; i < c->nchunks; i++) {
+ GRN_B_DEC(c->cinfo[i].segno, c->cp);
+ GRN_B_DEC(c->cinfo[i].size, c->cp);
+ GRN_B_DEC(c->cinfo[i].dgap, c->cp);
+ crid += c->cinfo[i].dgap;
+ if (crid < min) { c->curr_chunk = i + 1; }
+ }
+ if (chunk_is_reused(ctx, ii, c, chunk, c->buf->header.chunk_size)) {
+ grn_ii_cursor_close(ctx, c);
+ continue;
+ }
+ }
+ if ((ii->header->flags & GRN_OBJ_WITH_POSITION)) {
+ c->rdv[ii->n_elements - 1].flags = ODD;
+ }
+ }
+ c->nextb = bt->pos_in_buffer;
+ c->stat = CHUNK_USED|BUFFER_USED;
+ }
+ if (pos == a[0]) { break; }
+ grn_ii_cursor_close(ctx, c);
+ }
+exit :
+ array_unref(ii, tid);
+ return c;
+}
+
+grn_ii_posting *
+grn_ii_cursor_next(grn_ctx *ctx, grn_ii_cursor *c)
+{
+ if (c->buf) {
+ for (;;) {
+ if (c->stat & CHUNK_USED) {
+ for (;;) {
+ if (c->crp < c->cdp + c->cdf) {
+ uint32_t dgap = *c->crp++;
+ c->pc.rid += dgap;
+ if (dgap) { c->pc.sid = 0; }
+ if ((c->ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ c->pc.sid += 1 + *c->csp++;
+ } else {
+ c->pc.sid = 1;
+ }
+ c->cpp += c->pc.rest;
+ c->pc.rest = c->pc.tf = 1 + *c->ctp++;
+ if ((c->ii->header->flags & GRN_OBJ_WITH_WEIGHT)) {
+ c->pc.weight = *c->cwp++;
+ } else {
+ c->pc.weight = 0;
+ }
+ c->pc.pos = 0;
+ /*
+ {
+ static int count = 0;
+ int tf = c->pc.tf, pos = 0, *pp = (int *)c->cpp;
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ grn_text_itoa(ctx, &buf, c->pc.rid);
+ GRN_TEXT_PUTC(ctx, &buf, ':');
+ grn_text_itoa(ctx, &buf, c->pc.sid);
+ GRN_TEXT_PUTC(ctx, &buf, ':');
+ grn_text_itoa(ctx, &buf, c->pc.tf);
+ GRN_TEXT_PUTC(ctx, &buf, '(');
+ while (tf--) {
+ pos += *pp++;
+ count++;
+ grn_text_itoa(ctx, &buf, pos);
+ if (tf) { GRN_TEXT_PUTC(ctx, &buf, ':'); }
+ }
+ GRN_TEXT_PUTC(ctx, &buf, ')');
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "posting(%d):%s", count, GRN_TEXT_VALUE(&buf));
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ */
+ } else {
+ if (c->curr_chunk <= c->nchunks) {
+ if (c->curr_chunk == c->nchunks) {
+ if (c->cp < c->cpe) {
+ grn_p_decv(ctx, c->cp, c->cpe - c->cp, c->rdv, c->ii->n_elements);
+ } else {
+ c->pc.rid = 0;
+ break;
+ }
+ } else {
+ uint8_t *cp;
+ grn_io_win iw;
+ uint32_t size = c->cinfo[c->curr_chunk].size;
+ if (size && (cp = WIN_MAP2(c->ii->chunk, ctx, &iw,
+ c->cinfo[c->curr_chunk].segno, 0,
+ size, grn_io_rdonly))) {
+ grn_p_decv(ctx, cp, size, c->rdv, c->ii->n_elements);
+ grn_io_win_unmap2(&iw);
+ if (chunk_is_reused(ctx, c->ii, c,
+ c->cinfo[c->curr_chunk].segno, size)) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "chunk(%d) is reused by another thread",
+ c->cinfo[c->curr_chunk].segno);
+ c->pc.rid = 0;
+ break;
+ }
+ } else {
+ c->pc.rid = 0;
+ break;
+ }
+ }
+ {
+ int j = 0;
+ c->cdf = c->rdv[j].data_size;
+ c->crp = c->cdp = c->rdv[j++].data;
+ if ((c->ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ c->csp = c->rdv[j++].data;
+ }
+ c->ctp = c->rdv[j++].data;
+ if ((c->ii->header->flags & GRN_OBJ_WITH_WEIGHT)) {
+ c->cwp = c->rdv[j++].data;
+ }
+ c->cpp = c->rdv[j].data;
+ }
+ c->pc.rid = 0;
+ c->pc.sid = 0;
+ c->pc.rest = 0;
+ c->curr_chunk++;
+ continue;
+ } else {
+ c->pc.rid = 0;
+ }
+ }
+ break;
+ }
+ }
+ if (c->stat & BUFFER_USED) {
+ if (c->nextb) {
+ uint32_t lrid = c->pb.rid, lsid = c->pb.sid; /* for check */
+ buffer_rec *br = BUFFER_REC_AT(c->buf, c->nextb);
+ if (buffer_is_reused(ctx, c->ii, c)) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "buffer reused(%d,%d)", c->buffer_pseg, *c->ppseg);
+ // todo : rewind;
+ }
+ c->bp = NEXT_ADDR(br);
+ GRN_B_DEC(c->pb.rid, c->bp);
+ if ((c->ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ GRN_B_DEC(c->pb.sid, c->bp);
+ } else {
+ c->pb.sid = 1;
+ }
+ if (lrid > c->pb.rid || (lrid == c->pb.rid && lsid >= c->pb.sid)) {
+ ERR(GRN_FILE_CORRUPT, "brokend!! (%d:%d) -> (%d:%d) (%d->%d)", lrid, lsid, c->pb.rid, c->pb.sid, c->buffer_pseg, *c->ppseg);
+ }
+ c->nextb = br->step;
+ GRN_B_DEC(c->pb.tf, c->bp);
+ if ((c->ii->header->flags & GRN_OBJ_WITH_WEIGHT)) {
+ GRN_B_DEC(c->pb.weight, c->bp);
+ } else {
+ c->pb.weight = 0;
+ }
+ c->pb.rest = c->pb.tf;
+ c->pb.pos = 0;
+ } else {
+ c->pb.rid = 0;
+ }
+ }
+ if (c->pb.rid) {
+ if (c->pc.rid) {
+ if (c->pc.rid < c->pb.rid) {
+ c->stat = CHUNK_USED;
+ if (c->pc.tf && c->pc.sid) { c->post = &c->pc; break; }
+ } else {
+ if (c->pb.rid < c->pc.rid) {
+ c->stat = BUFFER_USED;
+ if (c->pb.tf && c->pb.sid) { c->post = &c->pb; break; }
+ } else {
+ if (c->pb.sid) {
+ if (c->pc.sid < c->pb.sid) {
+ c->stat = CHUNK_USED;
+ if (c->pc.tf && c->pc.sid) { c->post = &c->pc; break; }
+ } else {
+ c->stat = BUFFER_USED;
+ if (c->pb.sid == c->pc.sid) { c->stat |= CHUNK_USED; }
+ if (c->pb.tf) { c->post = &c->pb; break; }
+ }
+ } else {
+ c->stat = CHUNK_USED;
+ }
+ }
+ }
+ } else {
+ c->stat = BUFFER_USED;
+ if (c->pb.tf && c->pb.sid) { c->post = &c->pb; break; }
+ }
+ } else {
+ if (c->pc.rid) {
+ c->stat = CHUNK_USED;
+ if (c->pc.tf && c->pc.sid) { c->post = &c->pc; break; }
+ } else {
+ c->post = NULL;
+ return NULL;
+ }
+ }
+ }
+ } else {
+ if (c->stat & SOLE_DOC_USED) {
+ c->post = NULL;
+ return NULL;
+ } else {
+ c->post = &c->pb;
+ c->stat |= SOLE_DOC_USED;
+ }
+ }
+ return c->post;
+}
+
+grn_ii_posting *
+grn_ii_cursor_next_pos(grn_ctx *ctx, grn_ii_cursor *c)
+{
+ uint32_t gap;
+ if ((c->ii->header->flags & GRN_OBJ_WITH_POSITION)) {
+ if (c->nelements == c->ii->n_elements) {
+ if (c->buf) {
+ if (c->post == &c->pc) {
+ if (c->pc.rest) {
+ c->pc.rest--;
+ c->pc.pos += *c->cpp++;
+ } else {
+ return NULL;
+ }
+ } else if (c->post == &c->pb) {
+ if (buffer_is_reused(ctx, c->ii, c)) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "buffer reused(%d,%d)", c->buffer_pseg, *c->ppseg);
+ // todo : rewind;
+ }
+ if (c->pb.rest) {
+ c->pb.rest--;
+ GRN_B_DEC(gap, c->bp);
+ c->pb.pos += gap;
+ } else {
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+ } else {
+ if (c->stat & SOLE_POS_USED) {
+ return NULL;
+ } else {
+ c->stat |= SOLE_POS_USED;
+ }
+ }
+ }
+ } else {
+ if (c->stat & SOLE_POS_USED) {
+ return NULL;
+ } else {
+ c->stat |= SOLE_POS_USED;
+ }
+ }
+ return c->post;
+}
+
+grn_rc
+grn_ii_cursor_close(grn_ctx *ctx, grn_ii_cursor *c)
+{
+ if (!c) { return GRN_INVALID_ARGUMENT; }
+ datavec_fin(ctx, c->rdv);
+ if (c->cinfo) { GRN_FREE(c->cinfo); }
+ if (c->buf) { buffer_close(ctx, c->ii, c->buffer_pseg); }
+ if (c->cp) { grn_io_win_unmap2(&c->iw); }
+ GRN_FREE(c);
+ return GRN_SUCCESS;
+}
+
+uint32_t
+grn_ii_get_chunksize(grn_ctx *ctx, grn_ii *ii, grn_id tid)
+{
+ uint32_t res, pos, *a;
+ a = array_at(ctx, ii, tid);
+ if (!a) { return 0; }
+ if ((pos = a[0])) {
+ if (pos & 1) {
+ res = 0;
+ } else {
+ buffer *buf;
+ uint32_t pseg;
+ buffer_term *bt;
+ if ((pseg = buffer_open(ctx, ii, pos, &bt, &buf)) == NOT_ASSIGNED) {
+ res = 0;
+ } else {
+ res = bt->size_in_chunk;
+ buffer_close(ctx, ii, pseg);
+ }
+ }
+ } else {
+ res = 0;
+ }
+ array_unref(ii, tid);
+ return res;
+}
+
+uint32_t
+grn_ii_estimate_size(grn_ctx *ctx, grn_ii *ii, grn_id tid)
+{
+ uint32_t res, pos, *a;
+ a = array_at(ctx, ii, tid);
+ if (!a) { return 0; }
+ if ((pos = a[0])) {
+ if (pos & 1) {
+ res = 1;
+ } else {
+ buffer *buf;
+ uint32_t pseg;
+ buffer_term *bt;
+ if ((pseg = buffer_open(ctx, ii, pos, &bt, &buf)) == NOT_ASSIGNED) {
+ res = 0;
+ } else {
+ res = a[1] + bt->size_in_buffer + 2;
+ buffer_close(ctx, ii, pseg);
+ }
+ }
+ } else {
+ res = 0;
+ }
+ array_unref(ii, tid);
+ return res;
+}
+
+int
+grn_ii_entry_info(grn_ctx *ctx, grn_ii *ii, grn_id tid, unsigned int *a,
+ unsigned int *chunk, unsigned int *chunk_size, unsigned int *buffer_free,
+ unsigned int *nterms, unsigned int *nterms_void, unsigned int *bt_tid,
+ unsigned int *size_in_chunk, unsigned int *pos_in_chunk,
+ unsigned int *size_in_buffer, unsigned int *pos_in_buffer)
+{
+ buffer *b;
+ buffer_term *bt;
+ uint32_t pseg, *ap;
+ ERRCLR(NULL);
+ ap = array_at(ctx, ii, tid);
+ if (!ap) { return 0; }
+ a[0] = *ap;
+ array_unref(ii, tid);
+ if (!a[0]) { return 1; }
+ if (a[0] & 1) { return 2; }
+ if ((pseg = buffer_open(ctx, ii, a[0], &bt, &b)) == NOT_ASSIGNED) { return 3; }
+ *chunk = b->header.chunk;
+ *chunk_size = b->header.chunk_size;
+ *buffer_free = b->header.buffer_free;
+ *nterms = b->header.nterms;
+ *bt_tid = bt->tid;
+ *size_in_chunk = bt->size_in_chunk;
+ *pos_in_chunk = bt->pos_in_chunk;
+ *size_in_buffer = bt->size_in_buffer;
+ *pos_in_buffer = bt->pos_in_buffer;
+ buffer_close(ctx, ii, pseg);
+ return 4;
+}
+
+const char *
+grn_ii_path(grn_ii *ii)
+{
+ return grn_io_path(ii->seg);
+}
+
+uint32_t
+grn_ii_max_section(grn_ii *ii)
+{
+ return ii->header->smax;
+}
+
+grn_obj *
+grn_ii_lexicon(grn_ii *ii)
+{
+ return ii->lexicon;
+}
+
+/* private classes */
+
+/* b-heap */
+
+typedef struct {
+ int n_entries;
+ int n_bins;
+ grn_ii_cursor **bins;
+} cursor_heap;
+
+static inline cursor_heap *
+cursor_heap_open(grn_ctx *ctx, int max)
+{
+ cursor_heap *h = GRN_MALLOC(sizeof(cursor_heap));
+ if (!h) { return NULL; }
+ h->bins = GRN_MALLOC(sizeof(grn_ii_cursor *) * max);
+ if (!h->bins) {
+ GRN_FREE(h);
+ return NULL;
+ }
+ h->n_entries = 0;
+ h->n_bins = max;
+ return h;
+}
+
+static inline grn_rc
+cursor_heap_push(grn_ctx *ctx, cursor_heap *h, grn_ii *ii, grn_id tid, uint32_t offset2)
+{
+ int n, n2;
+ grn_ii_cursor *c, *c2;
+ if (h->n_entries >= h->n_bins) {
+ int max = h->n_bins * 2;
+ grn_ii_cursor **bins = GRN_REALLOC(h->bins, sizeof(grn_ii_cursor *) * max);
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "expanded cursor_heap to %d,%p", max, bins);
+ if (!bins) { return GRN_NO_MEMORY_AVAILABLE; }
+ h->n_bins = max;
+ h->bins = bins;
+ }
+ {
+ if (!(c = grn_ii_cursor_open(ctx, ii, tid, GRN_ID_NIL, GRN_ID_MAX,
+ ii->n_elements, 0))) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "cursor open failed");
+ return ctx->rc;
+ }
+ if (!grn_ii_cursor_next(ctx, c)) {
+ grn_ii_cursor_close(ctx, c);
+ return GRN_END_OF_DATA;
+ }
+ if (!grn_ii_cursor_next_pos(ctx, c)) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "invalid ii_cursor b");
+ grn_ii_cursor_close(ctx, c);
+ return GRN_END_OF_DATA;
+ }
+ n = h->n_entries++;
+ while (n) {
+ n2 = (n - 1) >> 1;
+ c2 = h->bins[n2];
+ if (GRN_II_CURSOR_CMP(c, c2)) { break; }
+ h->bins[n] = c2;
+ n = n2;
+ }
+ h->bins[n] = c;
+ }
+ return GRN_SUCCESS;
+}
+
+static inline grn_rc
+cursor_heap_push2(cursor_heap *h)
+{
+ grn_rc rc = GRN_SUCCESS;
+ return rc;
+}
+
+static inline grn_ii_cursor *
+cursor_heap_min(cursor_heap *h)
+{
+ return h->n_entries ? h->bins[0] : NULL;
+}
+
+static inline void
+cursor_heap_recalc_min(cursor_heap *h)
+{
+ int n = 0, n1, n2, m;
+ if ((m = h->n_entries) > 1) {
+ grn_ii_cursor *c = h->bins[0], *c1, *c2;
+ for (;;) {
+ n1 = n * 2 + 1;
+ n2 = n1 + 1;
+ c1 = n1 < m ? h->bins[n1] : NULL;
+ c2 = n2 < m ? h->bins[n2] : NULL;
+ if (c1 && GRN_II_CURSOR_CMP(c, c1)) {
+ if (c2 && GRN_II_CURSOR_CMP(c, c2) && GRN_II_CURSOR_CMP(c1, c2)) {
+ h->bins[n] = c2;
+ n = n2;
+ } else {
+ h->bins[n] = c1;
+ n = n1;
+ }
+ } else {
+ if (c2 && GRN_II_CURSOR_CMP(c, c2)) {
+ h->bins[n] = c2;
+ n = n2;
+ } else {
+ h->bins[n] = c;
+ break;
+ }
+ }
+ }
+ }
+}
+
+static inline void
+cursor_heap_pop(grn_ctx *ctx, cursor_heap *h)
+{
+ if (h->n_entries) {
+ grn_ii_cursor *c = h->bins[0];
+ if (!grn_ii_cursor_next(ctx, c)) {
+ grn_ii_cursor_close(ctx, c);
+ h->bins[0] = h->bins[--h->n_entries];
+ } else if (!grn_ii_cursor_next_pos(ctx, c)) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "invalid ii_cursor c");
+ grn_ii_cursor_close(ctx, c);
+ h->bins[0] = h->bins[--h->n_entries];
+ }
+ if (h->n_entries > 1) { cursor_heap_recalc_min(h); }
+ }
+}
+
+static inline void
+cursor_heap_pop_pos(grn_ctx *ctx, cursor_heap *h)
+{
+ if (h->n_entries) {
+ grn_ii_cursor *c = h->bins[0];
+ if (!grn_ii_cursor_next_pos(ctx, c)) {
+ if (!grn_ii_cursor_next(ctx, c)) {
+ grn_ii_cursor_close(ctx, c);
+ h->bins[0] = h->bins[--h->n_entries];
+ } else if (!grn_ii_cursor_next_pos(ctx, c)) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "invalid ii_cursor d");
+ grn_ii_cursor_close(ctx, c);
+ h->bins[0] = h->bins[--h->n_entries];
+ }
+ }
+ if (h->n_entries > 1) { cursor_heap_recalc_min(h); }
+ }
+}
+
+static inline void
+cursor_heap_close(grn_ctx *ctx, cursor_heap *h)
+{
+ int i;
+ if (!h) { return; }
+ for (i = h->n_entries; i--;) { grn_ii_cursor_close(ctx, h->bins[i]); }
+ GRN_FREE(h->bins);
+ GRN_FREE(h);
+}
+
+/* update */
+#ifdef USE_VGRAM
+
+inline static grn_rc
+index_add(grn_ctx *ctx, grn_id rid, grn_obj *lexicon, grn_ii *ii, grn_vgram *vgram,
+ const char *value, size_t value_len)
+{
+ grn_hash *h;
+ unsigned int token_flags = 0;
+ grn_token *token;
+ grn_ii_updspec **u;
+ grn_id tid, *tp;
+ grn_rc r, rc = GRN_SUCCESS;
+ grn_vgram_buf *sbuf = NULL;
+ if (!rid) { return GRN_INVALID_ARGUMENT; }
+ if (!(token = grn_token_open(ctx, lexicon, value, value_len,
+ GRN_TOKEN_ADD, token_flags))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ if (vgram) { sbuf = grn_vgram_buf_open(value_len); }
+ h = grn_hash_create(ctx, NULL, sizeof(grn_id), sizeof(grn_ii_updspec *), GRN_HASH_TINY);
+ if (!h) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_hash_create on index_add failed !");
+ grn_token_close(ctx, token);
+ if (sbuf) { grn_vgram_buf_close(sbuf); }
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ while (!token->status) {
+ (tid = grn_token_next(ctx, token));
+ if (tid) {
+ if (!grn_hash_add(ctx, h, &tid, sizeof(grn_id), (void **) &u, NULL)) { break; }
+ if (!*u) {
+ if (!(*u = grn_ii_updspec_open(ctx, rid, 1))) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "grn_ii_updspec_open on index_add failed!");
+ goto exit;
+ }
+ }
+ if (grn_ii_updspec_add(ctx, *u, token->pos, 0)) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "grn_ii_updspec_add on index_add failed!");
+ goto exit;
+ }
+ if (sbuf) { grn_vgram_buf_add(sbuf, tid); }
+ }
+ }
+ grn_token_close(ctx, token);
+ // todo : support vgram
+ // if (sbuf) { grn_vgram_update(vgram, rid, sbuf, (grn_set *)h); }
+ GRN_HASH_EACH(ctx, h, id, &tp, NULL, &u, {
+ if ((r = grn_ii_update_one(ctx, ii, *tp, *u, h))) { rc = r; }
+ grn_ii_updspec_close(ctx, *u);
+ });
+ grn_hash_close(ctx, h);
+ if (sbuf) { grn_vgram_buf_close(sbuf); }
+ return rc;
+exit:
+ grn_hash_close(ctx, h);
+ grn_token_close(ctx, token);
+ if (sbuf) { grn_vgram_buf_close(sbuf); }
+ return GRN_NO_MEMORY_AVAILABLE;
+}
+
+inline static grn_rc
+index_del(grn_ctx *ctx, grn_id rid, grn_obj *lexicon, grn_ii *ii, grn_vgram *vgram,
+ const char *value, size_t value_len)
+{
+ grn_hash *h;
+ unsigned int token_flags = 0;
+ grn_token *token;
+ grn_ii_updspec **u;
+ grn_id tid, *tp;
+ if (!rid) { return GRN_INVALID_ARGUMENT; }
+ if (!(token = grn_token_open(ctx, lexicon, value, value_len,
+ GRN_TOKEN_DEL, token_flags))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ h = grn_hash_create(ctx, NULL, sizeof(grn_id), sizeof(grn_ii_updspec *), GRN_HASH_TINY);
+ if (!h) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_hash_create on index_del failed !");
+ grn_token_close(ctx, token);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ while (!token->status) {
+ if ((tid = grn_token_next(ctx, token))) {
+ if (!grn_hash_add(ctx, h, &tid, sizeof(grn_id), (void **) &u, NULL)) { break; }
+ if (!*u) {
+ if (!(*u = grn_ii_updspec_open(ctx, rid, 0))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_ii_updspec_open on index_del failed !");
+ grn_hash_close(ctx, h);
+ grn_token_close(ctx, token);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ }
+ }
+ grn_token_close(ctx, token);
+ GRN_HASH_EACH(ctx, h, id, &tp, NULL, &u, {
+ if (*tp) {
+ grn_ii_delete_one(ctx, ii, *tp, *u, NULL);
+ }
+ grn_ii_updspec_close(ctx, *u);
+ });
+ grn_hash_close(ctx, h);
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ii_upd(grn_ctx *ctx, grn_ii *ii, grn_id rid, grn_vgram *vgram,
+ const char *oldvalue, unsigned int oldvalue_len,
+ const char *newvalue, unsigned int newvalue_len)
+{
+ grn_rc rc;
+ grn_obj *lexicon = ii->lexicon;
+ if (!rid) { return GRN_INVALID_ARGUMENT; }
+ if (oldvalue && *oldvalue) {
+ if ((rc = index_del(ctx, rid, lexicon, ii, vgram, oldvalue, oldvalue_len))) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "index_del on grn_ii_upd failed !");
+ goto exit;
+ }
+ }
+ if (newvalue && *newvalue) {
+ rc = index_add(ctx, rid, lexicon, ii, vgram, newvalue, newvalue_len);
+ }
+exit :
+ return rc;
+}
+
+grn_rc
+grn_ii_update(grn_ctx *ctx, grn_ii *ii, grn_id rid, grn_vgram *vgram, unsigned int section,
+ grn_values *oldvalues, grn_values *newvalues)
+{
+ int j;
+ grn_value *v;
+ unsigned int token_flags = 0;
+ grn_token *token;
+ grn_rc rc = GRN_SUCCESS;
+ grn_hash *old, *new;
+ grn_id tid, *tp;
+ grn_ii_updspec **u, **un;
+ grn_obj *lexicon = ii->lexicon;
+ if (!lexicon || !ii || !rid) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "grn_ii_update: invalid argument");
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (newvalues) {
+ new = grn_hash_create(ctx, NULL, sizeof(grn_id), sizeof(grn_ii_updspec *), GRN_HASH_TINY);
+ if (!new) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_hash_create on grn_ii_update failed !");
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ for (j = newvalues->n_values, v = newvalues->values; j; j--, v++) {
+ if ((token = grn_token_open(ctx, lexicon, v->str, v->str_len,
+ GRN_TOKEN_ADD, token_flags))) {
+ while (!token->status) {
+ if ((tid = grn_token_next(ctx, token))) {
+ if (!grn_hash_add(ctx, new, &tid, sizeof(grn_id), (void **) &u, NULL)) {
+ break;
+ }
+ if (!*u) {
+ if (!(*u = grn_ii_updspec_open(ctx, rid, section))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_ii_updspec_open on grn_ii_update failed!");
+ grn_token_close(ctx, token);
+ grn_hash_close(ctx, new);
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ }
+ if (grn_ii_updspec_add(ctx, *u, token->pos, v->weight)) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_ii_updspec_add on grn_ii_update failed!");
+ grn_token_close(ctx, token);
+ grn_hash_close(ctx, new);
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ }
+ }
+ grn_token_close(ctx, token);
+ }
+ }
+ if (!GRN_HASH_SIZE(new)) {
+ grn_hash_close(ctx, new);
+ new = NULL;
+ }
+ } else {
+ new = NULL;
+ }
+ if (oldvalues) {
+ old = grn_hash_create(ctx, NULL, sizeof(grn_id), sizeof(grn_ii_updspec *), GRN_HASH_TINY);
+ if (!old) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_hash_create(ctx, NULL, old) on grn_ii_update failed!");
+ if (new) { grn_hash_close(ctx, new); }
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ for (j = oldvalues->n_values, v = oldvalues->values; j; j--, v++) {
+ if ((token = grn_token_open(ctx, lexicon, v->str, v->str_len,
+ GRN_TOKEN_DEL, token_flags))) {
+ while (!token->status) {
+ if ((tid = grn_token_next(ctx, token))) {
+ if (!grn_hash_add(ctx, old, &tid, sizeof(grn_id), (void **) &u, NULL)) {
+ break;
+ }
+ if (!*u) {
+ if (!(*u = grn_ii_updspec_open(ctx, rid, section))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_ii_updspec_open on grn_ii_update failed!");
+ grn_token_close(ctx, token);
+ if (new) { grn_hash_close(ctx, new); };
+ grn_hash_close(ctx, old);
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ }
+ if (grn_ii_updspec_add(ctx, *u, token->pos, v->weight)) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_ii_updspec_add on grn_ii_update failed!");
+ grn_token_close(ctx, token);
+ if (new) { grn_hash_close(ctx, new); };
+ grn_hash_close(ctx, old);
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ }
+ }
+ grn_token_close(ctx, token);
+ }
+ }
+ } else {
+ old = NULL;
+ }
+ if (old) {
+ grn_id eid;
+ GRN_HASH_EACH(ctx, old, id, &tp, NULL, &u, {
+ if (new && (eid = grn_hash_get(ctx, new, tp, sizeof(grn_id), (void **) &un))) {
+ if (!grn_ii_updspec_cmp(*u, *un)) {
+ grn_ii_updspec_close(ctx, *un);
+ grn_hash_delete_by_id(ctx, new, eid, NULL);
+ }
+ } else {
+ grn_ii_delete_one(ctx, ii, *tp, *u, new);
+ }
+ grn_ii_updspec_close(ctx, *u);
+ });
+ grn_hash_close(ctx, old);
+ }
+ if (new) {
+ GRN_HASH_EACH(ctx, new, id, &tp, NULL, &u, {
+ grn_rc r;
+ if ((r = grn_ii_update_one(ctx, ii, *tp, *u, new))) { rc = r; }
+ grn_ii_updspec_close(ctx, *u);
+ });
+ grn_hash_close(ctx, new);
+ } else {
+ if (!section) {
+ /* todo: delete key when all sections deleted */
+ }
+ }
+exit :
+ return rc;
+}
+#endif /* USE_VGRAM */
+
+static grn_rc
+grn_vector2updspecs(grn_ctx *ctx, grn_ii *ii, grn_id rid, unsigned int section,
+ grn_obj *in, grn_obj *out, grn_token_mode mode, grn_obj *posting)
+{
+ int j;
+ grn_id tid;
+ grn_section *v;
+ grn_token *token;
+ grn_ii_updspec **u;
+ grn_hash *h = (grn_hash *)out;
+ grn_obj *lexicon = ii->lexicon;
+ if (in->u.v.body) {
+ const char *head = GRN_BULK_HEAD(in->u.v.body);
+ for (j = in->u.v.n_sections, v = in->u.v.sections; j; j--, v++) {
+ unsigned int token_flags = 0;
+ if (v->length &&
+ (token = grn_token_open(ctx, lexicon, head + v->offset, v->length,
+ mode, token_flags))) {
+ while (!token->status) {
+ if ((tid = grn_token_next(ctx, token))) {
+ if (posting) { GRN_RECORD_PUT(ctx, posting, tid); }
+ if (!grn_hash_add(ctx, h, &tid, sizeof(grn_id), (void **) &u, NULL)) {
+ break;
+ }
+ if (!*u) {
+ if (!(*u = grn_ii_updspec_open(ctx, rid, section))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_ii_updspec_open on grn_ii_update failed!");
+ grn_token_close(ctx, token);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ if (grn_ii_updspec_add(ctx, *u, token->pos, v->weight)) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_ii_updspec_add on grn_ii_update failed!");
+ grn_token_close(ctx, token);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ }
+ grn_token_close(ctx, token);
+ }
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_uvector2updspecs(grn_ctx *ctx, grn_ii *ii, grn_id rid, unsigned int section,
+ grn_obj *in, grn_obj *out)
+{
+ int i, n;
+ grn_ii_updspec **u;
+ grn_hash *h = (grn_hash *)out;
+
+ n = grn_vector_size(ctx, in);
+ for (i = 0; i < n; i++) {
+ grn_id id;
+ unsigned int weight;
+
+ id = grn_uvector_get_element(ctx, in, i, &weight);
+ if (!grn_hash_add(ctx, h, &id, sizeof(grn_id), (void **)&u, NULL)) {
+ break;
+ }
+ if (!*u) {
+ if (!(*u = grn_ii_updspec_open(ctx, rid, section))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_ii_updspec_open on grn_ii_update failed!");
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ if (grn_ii_updspec_add(ctx, *u, i, weight)) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_ii_updspec_add on grn_ii_update failed!");
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ii_column_update(grn_ctx *ctx, grn_ii *ii, grn_id rid, unsigned int section,
+ grn_obj *oldvalue, grn_obj *newvalue, grn_obj *posting)
+{
+ grn_id *tp;
+ grn_bool do_grn_ii_updspec_cmp = GRN_TRUE;
+ grn_rc rc = GRN_SUCCESS;
+ grn_ii_updspec **u, **un;
+ grn_obj *old_, *old = oldvalue, *new_, *new = newvalue, oldv, newv, buf, *post = NULL;
+ if (!ii || !ii->lexicon || !rid) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_ii_column_update: invalid argument");
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (posting) {
+ GRN_RECORD_INIT(&buf, GRN_OBJ_VECTOR, grn_obj_id(ctx, ii->lexicon));
+ post = &buf;
+ }
+ if (grn_io_lock(ctx, ii->seg, grn_lock_timeout)) { return ctx->rc; }
+ if (new) {
+ unsigned char type = (ii->obj.header.domain == new->header.domain)
+ ? GRN_UVECTOR
+ : new->header.type;
+ switch (type) {
+ case GRN_BULK :
+ {
+ if (grn_bulk_is_zero(ctx, new)) {
+ do_grn_ii_updspec_cmp = GRN_FALSE;
+ }
+ new_ = new;
+ GRN_OBJ_INIT(&newv, GRN_VECTOR, GRN_OBJ_DO_SHALLOW_COPY, GRN_DB_TEXT);
+ newv.u.v.body = new;
+ new = &newv;
+ grn_vector_delimit(ctx, new, 0, GRN_ID_NIL);
+ if (new_ != newvalue) { grn_obj_close(ctx, new_); }
+ }
+ /* fallthru */
+ case GRN_VECTOR :
+ new_ = new;
+ new = (grn_obj *)grn_hash_create(ctx, NULL, sizeof(grn_id),
+ sizeof(grn_ii_updspec *),
+ GRN_HASH_TINY);
+ if (!new) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_hash_create on grn_ii_update failed !");
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ } else {
+ rc = grn_vector2updspecs(ctx, ii, rid, section, new_, new, GRN_TOKEN_ADD, post);
+ }
+ if (new_ != newvalue) { grn_obj_close(ctx, new_); }
+ if (rc) { goto exit; }
+ break;
+ case GRN_UVECTOR :
+ new_ = new;
+ new = (grn_obj *)grn_hash_create(ctx, NULL, sizeof(grn_id),
+ sizeof(grn_ii_updspec *),
+ GRN_HASH_TINY);
+ if (!new) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_hash_create on grn_ii_update failed !");
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ } else {
+ if (new_->header.type == GRN_UVECTOR) {
+ rc = grn_uvector2updspecs(ctx, ii, rid, section, new_, new);
+ } else {
+ grn_obj uvector;
+ unsigned int weight = 0;
+ GRN_VALUE_FIX_SIZE_INIT(&uvector, GRN_OBJ_VECTOR, new_->header.domain);
+ if (new_->header.impl_flags & GRN_OBJ_WITH_WEIGHT) {
+ uvector.header.impl_flags |= GRN_OBJ_WITH_WEIGHT;
+ }
+ grn_uvector_add_element(ctx, &uvector, GRN_RECORD_VALUE(new_), weight);
+ rc = grn_uvector2updspecs(ctx, ii, rid, section, &uvector, new);
+ GRN_OBJ_FIN(ctx, &uvector);
+ }
+ }
+ if (new_ != newvalue) { grn_obj_close(ctx, new_); }
+ if (rc) { goto exit; }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "invalid object assigned as newvalue");
+ goto exit;
+ }
+ }
+ if (posting) {
+ grn_ii_updspec *u_;
+ uint32_t offset = 0;
+ grn_id tid_ = 0, gap, tid, *tpe;
+ grn_table_sort_optarg arg = {GRN_TABLE_SORT_ASC|
+ GRN_TABLE_SORT_AS_NUMBER|
+ GRN_TABLE_SORT_AS_UNSIGNED, NULL, NULL,0 };
+ grn_array *sorted = grn_array_create(ctx, NULL, sizeof(grn_id), 0);
+ grn_hash_sort(ctx, (grn_hash *)new, -1, sorted, &arg);
+ GRN_TEXT_PUT(ctx, posting, ((grn_hash *)new)->n_entries, sizeof(uint32_t));
+ GRN_ARRAY_EACH(ctx, sorted, 0, 0, id, &tp, {
+ grn_hash_get_key(ctx, (grn_hash *)new, *tp, &tid, sizeof(grn_id));
+ gap = tid - tid_;
+ GRN_TEXT_PUT(ctx, posting, &gap, sizeof(grn_id));
+ tid_ = tid;
+ });
+ GRN_ARRAY_EACH(ctx, sorted, 0, 0, id, &tp, {
+ grn_hash_get_value(ctx, (grn_hash *)new, *tp, &u_);
+ u_->offset = offset++;
+ GRN_TEXT_PUT(ctx, posting, &u_->tf, sizeof(int32_t));
+ });
+ tpe = (grn_id *)GRN_BULK_CURR(post);
+ for (tp = (grn_id *)GRN_BULK_HEAD(post); tp < tpe; tp++) {
+ grn_hash_get(ctx, (grn_hash *)new, (void *)tp, sizeof(grn_id), (void **)&u);
+ GRN_TEXT_PUT(ctx, posting, &(*u)->offset, sizeof(int32_t));
+ }
+ GRN_OBJ_FIN(ctx, post);
+ grn_array_close(ctx, sorted);
+ }
+
+ if (old) {
+ unsigned char type = (ii->obj.header.domain == old->header.domain)
+ ? GRN_UVECTOR
+ : old->header.type;
+ switch (type) {
+ case GRN_BULK :
+ {
+ // const char *str = GRN_BULK_HEAD(old);
+ // unsigned int str_len = GRN_BULK_VSIZE(old);
+ old_ = old;
+ GRN_OBJ_INIT(&oldv, GRN_VECTOR, GRN_OBJ_DO_SHALLOW_COPY, GRN_DB_TEXT);
+ oldv.u.v.body = old;
+ old = &oldv;
+ grn_vector_delimit(ctx, old, 0, GRN_ID_NIL);
+ if (old_ != oldvalue) { grn_obj_close(ctx, old_); }
+ }
+ /* fallthru */
+ case GRN_VECTOR :
+ old_ = old;
+ old = (grn_obj *)grn_hash_create(ctx, NULL, sizeof(grn_id),
+ sizeof(grn_ii_updspec *),
+ GRN_HASH_TINY);
+ if (!old) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_hash_create(ctx, NULL, old) on grn_ii_update failed!");
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ } else {
+ rc = grn_vector2updspecs(ctx, ii, rid, section, old_, old, GRN_TOKEN_DEL, NULL);
+ }
+ if (old_ != oldvalue) { grn_obj_close(ctx, old_); }
+ if (rc) { goto exit; }
+ break;
+ case GRN_UVECTOR :
+ old_ = old;
+ old = (grn_obj *)grn_hash_create(ctx, NULL, sizeof(grn_id),
+ sizeof(grn_ii_updspec *),
+ GRN_HASH_TINY);
+ if (!old) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_hash_create(ctx, NULL, old) on grn_ii_update failed!");
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ } else {
+ if (old_->header.type == GRN_UVECTOR) {
+ rc = grn_uvector2updspecs(ctx, ii, rid, section, old_, old);
+ } else {
+ grn_obj uvector;
+ unsigned int weight = 0;
+ GRN_VALUE_FIX_SIZE_INIT(&uvector, GRN_OBJ_VECTOR, old_->header.domain);
+ if (old_->header.impl_flags & GRN_OBJ_WITH_WEIGHT) {
+ uvector.header.impl_flags |= GRN_OBJ_WITH_WEIGHT;
+ }
+ grn_uvector_add_element(ctx, &uvector, GRN_RECORD_VALUE(old_), weight);
+ rc = grn_uvector2updspecs(ctx, ii, rid, section, &uvector, old);
+ GRN_OBJ_FIN(ctx, &uvector);
+ }
+ }
+ if (old_ != oldvalue) { grn_obj_close(ctx, old_); }
+ if (rc) { goto exit; }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "invalid object assigned as oldvalue");
+ goto exit;
+ }
+ }
+
+ if (old) {
+ grn_id eid;
+ grn_hash *o = (grn_hash *)old;
+ grn_hash *n = (grn_hash *)new;
+ GRN_HASH_EACH(ctx, o, id, &tp, NULL, &u, {
+ if (n && (eid = grn_hash_get(ctx, n, tp, sizeof(grn_id), (void **) &un))) {
+ if (do_grn_ii_updspec_cmp && !grn_ii_updspec_cmp(*u, *un)) {
+ grn_ii_updspec_close(ctx, *un);
+ grn_hash_delete_by_id(ctx, n, eid, NULL);
+ }
+ } else {
+ grn_ii_delete_one(ctx, ii, *tp, *u, n);
+ }
+ grn_ii_updspec_close(ctx, *u);
+ });
+ }
+ if (new) {
+ grn_hash *n = (grn_hash *)new;
+ GRN_HASH_EACH(ctx, n, id, &tp, NULL, &u, {
+ grn_rc r;
+ if ((r = grn_ii_update_one(ctx, ii, *tp, *u, n))) { rc = r; }
+ grn_ii_updspec_close(ctx, *u);
+ });
+ } else {
+ if (!section) {
+ /* todo: delete key when all sections deleted */
+ }
+ }
+exit :
+ grn_io_unlock(ii->seg);
+ if (old && old != oldvalue) { grn_obj_close(ctx, old); }
+ if (new && new != newvalue) { grn_obj_close(ctx, new); }
+ return ctx->rc;
+}
+
+/* token_info */
+
+typedef struct {
+ cursor_heap *cursors;
+ int offset;
+ int pos;
+ int size;
+ int ntoken;
+ grn_ii_posting *p;
+} token_info;
+
+#define EX_NONE 0
+#define EX_PREFIX 1
+#define EX_SUFFIX 2
+#define EX_BOTH 3
+
+inline static void
+token_info_expand_both(grn_ctx *ctx, grn_obj *lexicon, grn_ii *ii,
+ const char *key, unsigned int key_size, token_info *ti)
+{
+ int s = 0;
+ grn_hash *h, *g;
+ uint32_t *offset2;
+ grn_hash_cursor *c;
+ grn_id *tp, *tq;
+ if ((h = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, 0))) {
+ grn_table_search(ctx, lexicon, key, key_size,
+ GRN_OP_PREFIX, (grn_obj *)h, GRN_OP_OR);
+ if (GRN_HASH_SIZE(h)) {
+ if ((ti->cursors = cursor_heap_open(ctx, GRN_HASH_SIZE(h) + 256))) {
+ if ((c = grn_hash_cursor_open(ctx, h, NULL, 0, NULL, 0, 0, -1, 0))) {
+ uint32_t key2_size;
+ const char *key2;
+ while (grn_hash_cursor_next(ctx, c)) {
+ grn_hash_cursor_get_key(ctx, c, (void **) &tp);
+ key2 = _grn_table_key(ctx, lexicon, *tp, &key2_size);
+ if (!key2) { break; }
+ if ((lexicon->header.type != GRN_TABLE_PAT_KEY) ||
+ !(lexicon->header.flags & GRN_OBJ_KEY_WITH_SIS) ||
+ key2_size <= 2) { // todo: refine
+ if ((s = grn_ii_estimate_size(ctx, ii, *tp))) {
+ cursor_heap_push(ctx, ti->cursors, ii, *tp, 0);
+ ti->ntoken++;
+ ti->size += s;
+ }
+ } else {
+ if ((g = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, GRN_HASH_TINY))) {
+ grn_pat_suffix_search(ctx, (grn_pat *)lexicon, key2, key2_size, g);
+ GRN_HASH_EACH(ctx, g, id, &tq, NULL, &offset2, {
+ if ((s = grn_ii_estimate_size(ctx, ii, *tq))) {
+ cursor_heap_push(ctx, ti->cursors, ii, *tq, /* *offset2 */ 0);
+ ti->ntoken++;
+ ti->size += s;
+ }
+ });
+ grn_hash_close(ctx, g);
+ }
+ }
+ }
+ grn_hash_cursor_close(ctx, c);
+ }
+ }
+ }
+ grn_hash_close(ctx, h);
+ }
+}
+
+inline static grn_rc
+token_info_close(grn_ctx *ctx, token_info *ti)
+{
+ cursor_heap_close(ctx, ti->cursors);
+ GRN_FREE(ti);
+ return GRN_SUCCESS;
+}
+
+inline static token_info *
+token_info_open(grn_ctx *ctx, grn_obj *lexicon, grn_ii *ii,
+ const char *key, unsigned int key_size, uint32_t offset, int mode)
+{
+ int s = 0;
+ grn_hash *h;
+ token_info *ti;
+ grn_id tid;
+ grn_id *tp;
+ if (!key) { return NULL; }
+ if (!(ti = GRN_MALLOC(sizeof(token_info)))) { return NULL; }
+ ti->cursors = NULL;
+ ti->size = 0;
+ ti->ntoken = 0;
+ ti->offset = offset;
+ switch (mode) {
+ case EX_BOTH :
+ token_info_expand_both(ctx, lexicon, ii, key, key_size, ti);
+ break;
+ case EX_NONE :
+ if ((tid = grn_table_get(ctx, lexicon, key, key_size)) &&
+ (s = grn_ii_estimate_size(ctx, ii, tid)) &&
+ (ti->cursors = cursor_heap_open(ctx, 1))) {
+ cursor_heap_push(ctx, ti->cursors, ii, tid, 0);
+ ti->ntoken++;
+ ti->size = s;
+ }
+ break;
+ case EX_PREFIX :
+ if ((h = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, 0))) {
+ grn_table_search(ctx, lexicon, key, key_size,
+ GRN_OP_PREFIX, (grn_obj *)h, GRN_OP_OR);
+ if (GRN_HASH_SIZE(h)) {
+ if ((ti->cursors = cursor_heap_open(ctx, GRN_HASH_SIZE(h)))) {
+ GRN_HASH_EACH(ctx, h, id, &tp, NULL, NULL, {
+ if ((s = grn_ii_estimate_size(ctx, ii, *tp))) {
+ cursor_heap_push(ctx, ti->cursors, ii, *tp, 0);
+ ti->ntoken++;
+ ti->size += s;
+ }
+ });
+ }
+ }
+ grn_hash_close(ctx, h);
+ }
+ break;
+ case EX_SUFFIX :
+ if ((h = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, 0))) {
+ grn_table_search(ctx, lexicon, key, key_size,
+ GRN_OP_SUFFIX, (grn_obj *)h, GRN_OP_OR);
+ if (GRN_HASH_SIZE(h)) {
+ if ((ti->cursors = cursor_heap_open(ctx, GRN_HASH_SIZE(h)))) {
+ uint32_t *offset2;
+ GRN_HASH_EACH(ctx, h, id, &tp, NULL, &offset2, {
+ if ((s = grn_ii_estimate_size(ctx, ii, *tp))) {
+ cursor_heap_push(ctx, ti->cursors, ii, *tp, /* *offset2 */ 0);
+ ti->ntoken++;
+ ti->size += s;
+ }
+ });
+ }
+ }
+ grn_hash_close(ctx, h);
+ }
+ break;
+ }
+ if (cursor_heap_push2(ti->cursors)) {
+ token_info_close(ctx, ti);
+ return NULL;
+ }
+ {
+ grn_ii_cursor *ic;
+ if (ti->cursors && (ic = cursor_heap_min(ti->cursors))) {
+ grn_ii_posting *p = ic->post;
+ ti->pos = p->pos - ti->offset;
+ ti->p = p;
+ } else {
+ token_info_close(ctx, ti);
+ ti = NULL;
+ }
+ }
+ return ti;
+}
+
+static inline grn_rc
+token_info_skip(grn_ctx *ctx, token_info *ti, uint32_t rid, uint32_t sid)
+{
+ grn_ii_cursor *c;
+ grn_ii_posting *p;
+ for (;;) {
+ if (!(c = cursor_heap_min(ti->cursors))) { return GRN_END_OF_DATA; }
+ p = c->post;
+ if (p->rid > rid || (p->rid == rid && p->sid >= sid)) { break; }
+ cursor_heap_pop(ctx, ti->cursors);
+ }
+ ti->pos = p->pos - ti->offset;
+ ti->p = p;
+ return GRN_SUCCESS;
+}
+
+static inline grn_rc
+token_info_skip_pos(grn_ctx *ctx, token_info *ti, uint32_t rid, uint32_t sid, uint32_t pos)
+{
+ grn_ii_cursor *c;
+ grn_ii_posting *p;
+ pos += ti->offset;
+ for (;;) {
+ if (!(c = cursor_heap_min(ti->cursors))) { return GRN_END_OF_DATA; }
+ p = c->post;
+ if (p->rid != rid || p->sid != sid || p->pos >= pos) { break; }
+ cursor_heap_pop_pos(ctx, ti->cursors);
+ }
+ ti->pos = p->pos - ti->offset;
+ ti->p = p;
+ return GRN_SUCCESS;
+}
+
+inline static int
+token_compare(const void *a, const void *b)
+{
+ const token_info *t1 = *((token_info **)a), *t2 = *((token_info **)b);
+ return t1->size - t2->size;
+}
+
+inline static grn_rc
+token_info_build(grn_ctx *ctx, grn_obj *lexicon, grn_ii *ii, const char *string, unsigned int string_len,
+ token_info **tis, uint32_t *n, grn_operator mode)
+{
+ token_info *ti;
+ const char *key;
+ uint32_t size;
+ grn_rc rc = GRN_END_OF_DATA;
+ unsigned int token_flags = GRN_TOKEN_ENABLE_TOKENIZED_DELIMITER;
+ grn_token *token = grn_token_open(ctx, lexicon, string, string_len,
+ GRN_TOKEN_GET, token_flags);
+ if (!token) { return GRN_NO_MEMORY_AVAILABLE; }
+ if (mode == GRN_OP_UNSPLIT) {
+ if ((ti = token_info_open(ctx, lexicon, ii, (char *)token->orig, token->orig_blen, 0, EX_BOTH))) {
+ tis[(*n)++] = ti;
+ rc = GRN_SUCCESS;
+ }
+ } else {
+ grn_id tid;
+ int ef;
+ switch (mode) {
+ case GRN_OP_PREFIX :
+ ef = EX_PREFIX;
+ break;
+ case GRN_OP_SUFFIX :
+ ef = EX_SUFFIX;
+ break;
+ case GRN_OP_PARTIAL :
+ ef = EX_BOTH;
+ break;
+ default :
+ ef = EX_NONE;
+ break;
+ }
+ tid = grn_token_next(ctx, token);
+ if (token->force_prefix) { ef |= EX_PREFIX; }
+ switch (token->status) {
+ case GRN_TOKEN_DOING :
+ key = _grn_table_key(ctx, lexicon, tid, &size);
+ ti = token_info_open(ctx, lexicon, ii, key, size, token->pos, ef & EX_SUFFIX);
+ break;
+ case GRN_TOKEN_DONE :
+ ti = token_info_open(ctx, lexicon, ii, (const char *)token->curr,
+ token->curr_size, 0, ef);
+ /*
+ key = _grn_table_key(ctx, lexicon, tid, &size);
+ ti = token_info_open(ctx, lexicon, ii, token->curr, token->curr_size, token->pos, ef);
+ ti = token_info_open(ctx, lexicon, ii, (char *)token->orig,
+ token->orig_blen, token->pos, ef);
+ */
+ break;
+ case GRN_TOKEN_NOT_FOUND :
+ ti = token_info_open(ctx, lexicon, ii, (char *)token->orig,
+ token->orig_blen, 0, ef);
+ break;
+ default :
+ goto exit;
+ }
+ if (!ti) { goto exit ; }
+ tis[(*n)++] = ti;
+ while (token->status == GRN_TOKEN_DOING) {
+ tid = grn_token_next(ctx, token);
+ switch (token->status) {
+ case GRN_TOKEN_DOING :
+ key = _grn_table_key(ctx, lexicon, tid, &size);
+ ti = token_info_open(ctx, lexicon, ii, key, size, token->pos, EX_NONE);
+ break;
+ case GRN_TOKEN_DONE :
+ if (tid) {
+ key = _grn_table_key(ctx, lexicon, tid, &size);
+ ti = token_info_open(ctx, lexicon, ii, key, size, token->pos, ef & EX_PREFIX);
+ break;
+ } /* else fallthru */
+ default :
+ ti = token_info_open(ctx, lexicon, ii, (char *)token->curr,
+ token->curr_size, token->pos, ef & EX_PREFIX);
+ break;
+ }
+ if (!ti) { goto exit; }
+ tis[(*n)++] = ti;
+ }
+ rc = GRN_SUCCESS;
+ }
+exit :
+ grn_token_close(ctx, token);
+ return rc;
+}
+
+static void
+token_info_clear_offset(token_info **tis, uint32_t n)
+{
+ token_info **tie;
+ for (tie = tis + n; tis < tie; tis++) { (*tis)->offset = 0; }
+}
+
+/* select */
+
+inline static void
+res_add(grn_ctx *ctx, grn_hash *s, grn_rset_posinfo *pi, uint32_t score,
+ grn_operator op)
+{
+ grn_rset_recinfo *ri;
+ switch (op) {
+ case GRN_OP_OR :
+ if (grn_hash_add(ctx, s, pi, s->key_size, (void **)&ri, NULL)) {
+ if (s->obj.header.flags & GRN_OBJ_WITH_SUBREC) {
+ grn_table_add_subrec((grn_obj *)s, ri, score, pi, 1);
+ }
+ }
+ break;
+ case GRN_OP_AND :
+ if (grn_hash_get(ctx, s, pi, s->key_size, (void **)&ri)) {
+ if (s->obj.header.flags & GRN_OBJ_WITH_SUBREC) {
+ ri->n_subrecs |= GRN_RSET_UTIL_BIT;
+ grn_table_add_subrec((grn_obj *)s, ri, score, pi, 1);
+ }
+ }
+ break;
+ case GRN_OP_AND_NOT :
+ {
+ grn_id id;
+ if ((id = grn_hash_get(ctx, s, pi, s->key_size, (void **)&ri))) {
+ grn_hash_delete_by_id(ctx, s, id, NULL);
+ }
+ }
+ break;
+ case GRN_OP_ADJUST :
+ if (grn_hash_get(ctx, s, pi, s->key_size, (void **)&ri)) {
+ if (s->obj.header.flags & GRN_OBJ_WITH_SUBREC) {
+ ri->score += score;
+ }
+ }
+ break;
+ default :
+ break;
+ }
+}
+
+grn_rc
+grn_ii_posting_add(grn_ctx *ctx, grn_ii_posting *pos, grn_hash *s, grn_operator op)
+{
+ res_add(ctx, s, (grn_rset_posinfo *)(pos), (1 + pos->weight), op);
+ return ctx->rc;
+}
+
+#ifdef USE_BHEAP
+
+/* todo */
+
+#else /* USE_BHEAP */
+
+struct _btr_node {
+ struct _btr_node *car;
+ struct _btr_node *cdr;
+ token_info *ti;
+};
+
+typedef struct _btr_node btr_node;
+
+typedef struct {
+ int n;
+ token_info *min;
+ token_info *max;
+ btr_node *root;
+ btr_node *nodes;
+} btr;
+
+inline static void
+bt_zap(btr *bt)
+{
+ bt->n = 0;
+ bt->min = NULL;
+ bt->max = NULL;
+ bt->root = NULL;
+}
+
+inline static btr *
+bt_open(grn_ctx *ctx, int size)
+{
+ btr *bt = GRN_MALLOC(sizeof(btr));
+ if (bt) {
+ bt_zap(bt);
+ if (!(bt->nodes = GRN_MALLOC(sizeof(btr_node) * size))) {
+ GRN_FREE(bt);
+ bt = NULL;
+ }
+ }
+ return bt;
+}
+
+inline static void
+bt_close(grn_ctx *ctx, btr *bt)
+{
+ if (!bt) { return; }
+ GRN_FREE(bt->nodes);
+ GRN_FREE(bt);
+}
+
+inline static void
+bt_push(btr *bt, token_info *ti)
+{
+ int pos = ti->pos, minp = 1, maxp = 1;
+ btr_node *node, *new, **last;
+ new = bt->nodes + bt->n++;
+ new->ti = ti;
+ new->car = NULL;
+ new->cdr = NULL;
+ for (last = &bt->root; (node = *last);) {
+ if (pos < node->ti->pos) {
+ last = &node->car;
+ maxp = 0;
+ } else {
+ last = &node->cdr;
+ minp = 0;
+ }
+ }
+ *last = new;
+ if (minp) { bt->min = ti; }
+ if (maxp) { bt->max = ti; }
+}
+
+inline static void
+bt_pop(btr *bt)
+{
+ btr_node *node, *min, *newmin, **last;
+ for (last = &bt->root; (min = *last) && min->car; last = &min->car) ;
+ if (min) {
+ int pos = min->ti->pos, minp = 1, maxp = 1;
+ *last = min->cdr;
+ min->cdr = NULL;
+ for (last = &bt->root; (node = *last);) {
+ if (pos < node->ti->pos) {
+ last = &node->car;
+ maxp = 0;
+ } else {
+ last = &node->cdr;
+ minp = 0;
+ }
+ }
+ *last = min;
+ if (maxp) { bt->max = min->ti; }
+ if (!minp) {
+ for (newmin = bt->root; newmin->car; newmin = newmin->car) ;
+ bt->min = newmin->ti;
+ }
+ }
+}
+
+#endif /* USE_BHEAP */
+
+typedef enum {
+ grn_wv_none = 0,
+ grn_wv_static,
+ grn_wv_dynamic,
+ grn_wv_constant
+} grn_wv_mode;
+
+inline static int
+get_weight(grn_ctx *ctx, grn_hash *s, grn_id rid, int sid,
+ grn_wv_mode wvm, grn_select_optarg *optarg)
+{
+ switch (wvm) {
+ case grn_wv_none :
+ return 1;
+ case grn_wv_static :
+ return sid <= optarg->vector_size ? optarg->weight_vector[sid - 1] : 0;
+ case grn_wv_dynamic :
+ /* todo : support hash with keys
+ if (s->keys) {
+ uint32_t key_size;
+ const char *key = _grn_table_key(ctx, s->keys, rid, &key_size);
+ // todo : change grn_select_optarg
+ return key ? optarg->func(s, key, key_size, sid, optarg->func_arg) : 0;
+ }
+ */
+ /* todo : cast */
+ return optarg->func(ctx, (void *)s, (void *)(intptr_t)rid, sid, optarg->func_arg);
+ case grn_wv_constant :
+ return optarg->vector_size;
+ default :
+ return 1;
+ }
+}
+
+grn_rc
+grn_ii_similar_search(grn_ctx *ctx, grn_ii *ii,
+ const char *string, unsigned int string_len,
+ grn_hash *s, grn_operator op, grn_select_optarg *optarg)
+{
+ int *w1, limit;
+ grn_id tid, *tp, max_size;
+ grn_rc rc = GRN_SUCCESS;
+ grn_hash *h;
+ grn_token *token;
+ unsigned int token_flags = GRN_TOKEN_ENABLE_TOKENIZED_DELIMITER;
+ grn_obj *lexicon = ii->lexicon;
+ if (!lexicon || !ii || !string || !string_len || !s || !optarg) { return GRN_INVALID_ARGUMENT; }
+ if (!(h = grn_hash_create(ctx, NULL, sizeof(grn_id), sizeof(int), 0))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ if (!(token = grn_token_open(ctx, lexicon, string, string_len,
+ GRN_TOKEN_GET, token_flags))) {
+ grn_hash_close(ctx, h);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ if (!(max_size = optarg->max_size)) { max_size = 1048576; }
+ while (token->status != GRN_TOKEN_DONE) {
+ if ((tid = grn_token_next(ctx, token))) {
+ if (grn_hash_add(ctx, h, &tid, sizeof(grn_id), (void **)&w1, NULL)) { (*w1)++; }
+ }
+ if (tid && token->curr_size) {
+ if (optarg->max_interval == GRN_OP_UNSPLIT) {
+ grn_table_search(ctx, lexicon, token->curr, token->curr_size,
+ GRN_OP_PREFIX, (grn_obj *)h, GRN_OP_OR);
+ }
+ if (optarg->max_interval == GRN_OP_PARTIAL) {
+ grn_table_search(ctx, lexicon, token->curr, token->curr_size,
+ GRN_OP_SUFFIX, (grn_obj *)h, GRN_OP_OR);
+ }
+ }
+ }
+ grn_token_close(ctx, token);
+ {
+ grn_hash_cursor *c = grn_hash_cursor_open(ctx, h, NULL, 0, NULL, 0, 0, -1, 0);
+ if (!c) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_hash_cursor_open on grn_ii_similar_search failed !");
+ grn_hash_close(ctx, h);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ while (grn_hash_cursor_next(ctx, c)) {
+ uint32_t es;
+ grn_hash_cursor_get_key_value(ctx, c, (void **) &tp, NULL, (void **) &w1);
+ if ((es = grn_ii_estimate_size(ctx, ii, *tp))) {
+ *w1 += max_size / es;
+ } else {
+ grn_hash_cursor_delete(ctx, c, NULL);
+ }
+ }
+ grn_hash_cursor_close(ctx, c);
+ }
+ limit = optarg->similarity_threshold
+ ? (optarg->similarity_threshold > GRN_HASH_SIZE(h)
+ ? GRN_HASH_SIZE(h)
+ : optarg->similarity_threshold)
+ : (GRN_HASH_SIZE(h) >> 3) + 1;
+ if (GRN_HASH_SIZE(h)) {
+ grn_id j, id;
+ int w2, rep;
+ grn_ii_cursor *c;
+ grn_ii_posting *pos;
+ grn_wv_mode wvm = grn_wv_none;
+ grn_table_sort_optarg arg = {GRN_TABLE_SORT_DESC, NULL, (void *)sizeof(grn_id), 0};
+ grn_array *sorted = grn_array_create(ctx, NULL, sizeof(grn_id), 0);
+ if (!sorted) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_hash_sort on grn_ii_similar_search failed !");
+ grn_hash_close(ctx, h);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ grn_hash_sort(ctx, h, limit, sorted, &arg);
+ /* todo support subrec
+ rep = (s->record_unit == grn_rec_position || s->subrec_unit == grn_rec_position);
+ */
+ rep = 0;
+ if (optarg->func) {
+ wvm = grn_wv_dynamic;
+ } else if (optarg->vector_size) {
+ wvm = optarg->weight_vector ? grn_wv_static : grn_wv_constant;
+ }
+ for (j = 1; j <= limit; j++) {
+ grn_array_get_value(ctx, sorted, j, &id);
+ _grn_hash_get_key_value(ctx, h, id, (void **) &tp, (void **) &w1);
+ if (!*tp || !(c = grn_ii_cursor_open(ctx, ii, *tp, GRN_ID_NIL, GRN_ID_MAX,
+ rep
+ ? ii->n_elements
+ : ii->n_elements - 1, 0))) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "cursor open failed (%d)", *tp);
+ continue;
+ }
+ if (rep) {
+ while (grn_ii_cursor_next(ctx, c)) {
+ pos = c->post;
+ if ((w2 = get_weight(ctx, s, pos->rid, pos->sid, wvm, optarg))) {
+ while (grn_ii_cursor_next_pos(ctx, c)) {
+ res_add(ctx, s, (grn_rset_posinfo *) pos, *w1 * w2 * (1 + pos->weight), op);
+ }
+ }
+ }
+ } else {
+ while (grn_ii_cursor_next(ctx, c)) {
+ pos = c->post;
+ if ((w2 = get_weight(ctx, s, pos->rid, pos->sid, wvm, optarg))) {
+ res_add(ctx, s, (grn_rset_posinfo *) pos, *w1 * w2 * (pos->tf + pos->weight), op);
+ }
+ }
+ }
+ grn_ii_cursor_close(ctx, c);
+ }
+ grn_array_close(ctx, sorted);
+ }
+ grn_hash_close(ctx, h);
+ grn_ii_resolve_sel_and(ctx, s, op);
+ // grn_hash_cursor_clear(r);
+ return rc;
+}
+
+#define TERM_EXTRACT_EACH_POST 0
+#define TERM_EXTRACT_EACH_TERM 1
+
+grn_rc
+grn_ii_term_extract(grn_ctx *ctx, grn_ii *ii, const char *string,
+ unsigned int string_len, grn_hash *s,
+ grn_operator op, grn_select_optarg *optarg)
+{
+ grn_rset_posinfo pi;
+ grn_id tid;
+ const char *p, *pe;
+ grn_obj *nstr;
+ const char *normalized;
+ unsigned int normalized_length_in_bytes;
+ grn_ii_cursor *c;
+ grn_ii_posting *pos;
+ int skip, rep, policy;
+ grn_rc rc = GRN_SUCCESS;
+ grn_wv_mode wvm = grn_wv_none;
+ if (!ii || !string || !string_len || !s || !optarg) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (!(nstr = grn_string_open(ctx, string, string_len, NULL, 0))) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ policy = optarg->max_interval;
+ if (optarg->func) {
+ wvm = grn_wv_dynamic;
+ } else if (optarg->vector_size) {
+ wvm = optarg->weight_vector ? grn_wv_static : grn_wv_constant;
+ }
+ /* todo support subrec
+ if (policy == TERM_EXTRACT_EACH_POST) {
+ if ((rc = grn_records_reopen(s, grn_rec_section, grn_rec_none, 0))) { goto exit; }
+ }
+ rep = (s->record_unit == grn_rec_position || s->subrec_unit == grn_rec_position);
+ */
+ rep = 0;
+ grn_string_get_normalized(ctx, nstr, &normalized, &normalized_length_in_bytes,
+ NULL);
+ for (p = normalized, pe = p + normalized_length_in_bytes; p < pe; p += skip) {
+ if ((tid = grn_table_lcp_search(ctx, ii->lexicon, p, pe - p))) {
+ if (policy == TERM_EXTRACT_EACH_POST) {
+ if (!(skip = grn_table_get_key(ctx, ii->lexicon, tid, NULL, 0))) { break; }
+ } else {
+ if (!(skip = (int)grn_charlen(ctx, p, pe))) { break; }
+ }
+ if (!(c = grn_ii_cursor_open(ctx, ii, tid, GRN_ID_NIL, GRN_ID_MAX,
+ rep
+ ? ii->n_elements
+ : ii->n_elements - 1, 0))) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "cursor open failed (%d)", tid);
+ continue;
+ }
+ if (rep) {
+ while (grn_ii_cursor_next(ctx, c)) {
+ pos = c->post;
+ while (grn_ii_cursor_next_pos(ctx, c)) {
+ res_add(ctx, s, (grn_rset_posinfo *) pos,
+ get_weight(ctx, s, pos->rid, pos->sid, wvm, optarg), op);
+ }
+ }
+ } else {
+ while (grn_ii_cursor_next(ctx, c)) {
+ if (policy == TERM_EXTRACT_EACH_POST) {
+ pi.rid = c->post->rid;
+ pi.sid = p - normalized;
+ res_add(ctx, s, &pi, pi.sid + 1, op);
+ } else {
+ pos = c->post;
+ res_add(ctx, s, (grn_rset_posinfo *) pos,
+ get_weight(ctx, s, pos->rid, pos->sid, wvm, optarg), op);
+ }
+ }
+ }
+ grn_ii_cursor_close(ctx, c);
+ } else {
+ if (!(skip = (int)grn_charlen(ctx, p, pe))) {
+ break;
+ }
+ }
+ }
+ grn_obj_close(ctx, nstr);
+ return rc;
+}
+
+grn_rc
+grn_ii_select(grn_ctx *ctx, grn_ii *ii, const char *string, unsigned int string_len,
+ grn_hash *s, grn_operator op, grn_select_optarg *optarg)
+{
+ btr *bt = NULL;
+ grn_rc rc = GRN_SUCCESS;
+ int rep, orp, weight, max_interval = 0;
+ token_info *ti, **tis = NULL, **tip, **tie;
+ uint32_t n = 0, rid, sid, nrid, nsid;
+ grn_operator mode = GRN_OP_EXACT;
+ grn_wv_mode wvm = grn_wv_none;
+ grn_obj *lexicon = ii->lexicon;
+ if (!lexicon || !ii || !s) { return GRN_INVALID_ARGUMENT; }
+ if (optarg) {
+ mode = optarg->mode;
+ if (optarg->func) {
+ wvm = grn_wv_dynamic;
+ } else if (optarg->vector_size) {
+ wvm = optarg->weight_vector ? grn_wv_static : grn_wv_constant;
+ }
+ }
+ if (mode == GRN_OP_SIMILAR) {
+ return grn_ii_similar_search(ctx, ii, string, string_len, s, op, optarg);
+ }
+ if (mode == GRN_OP_TERM_EXTRACT) {
+ return grn_ii_term_extract(ctx, ii, string, string_len, s, op, optarg);
+ }
+ /* todo : support subrec
+ rep = (s->record_unit == grn_rec_position || s->subrec_unit == grn_rec_position);
+ orp = (s->record_unit == grn_rec_position || op == GRN_OP_OR);
+ */
+ rep = 0;
+ orp = op == GRN_OP_OR;
+ if (!string_len) { goto exit; }
+ if (!(tis = GRN_MALLOC(sizeof(token_info *) * string_len * 2))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ if (token_info_build(ctx, lexicon, ii, string, string_len, tis, &n, mode) || !n) { goto exit; }
+ switch (mode) {
+ case GRN_OP_NEAR2 :
+ token_info_clear_offset(tis, n);
+ mode = GRN_OP_NEAR;
+ /* fallthru */
+ case GRN_OP_NEAR :
+ if (!(bt = bt_open(ctx, n))) { rc = GRN_NO_MEMORY_AVAILABLE; goto exit; }
+ max_interval = optarg->max_interval;
+ break;
+ default :
+ break;
+ }
+ qsort(tis, n, sizeof(token_info *), token_compare);
+ tie = tis + n;
+ /*
+ for (tip = tis; tip < tie; tip++) {
+ ti = *tip;
+ grn_log("o=%d n=%d s=%d r=%d", ti->offset, ti->ntoken, ti->size, ti->rid);
+ }
+ */
+ GRN_LOG(ctx, GRN_LOG_INFO, "n=%d (%.*s)", n, string_len, string);
+ /* todo : array as result
+ if (n == 1 && (*tis)->cursors->n_entries == 1 && op == GRN_OP_OR
+ && !GRN_HASH_SIZE(s) && !s->garbages
+ && s->record_unit == grn_rec_document && !s->max_n_subrecs
+ && grn_ii_max_section(ii) == 1) {
+ grn_ii_cursor *c = (*tis)->cursors->bins[0];
+ if ((rc = grn_hash_array_init(s, (*tis)->size + 32768))) { goto exit; }
+ do {
+ grn_rset_recinfo *ri;
+ grn_ii_posting *p = c->post;
+ if ((weight = get_weight(ctx, s, p->rid, p->sid, wvm, optarg))) {
+ GRN_HASH_INT_ADD(s, p, ri);
+ ri->score = (p->tf + p->score) * weight;
+ ri->n_subrecs = 1;
+ }
+ } while (grn_ii_cursor_next(ctx, c));
+ goto exit;
+ }
+ */
+ for (;;) {
+ rid = (*tis)->p->rid;
+ sid = (*tis)->p->sid;
+ for (tip = tis + 1, nrid = rid, nsid = sid + 1; tip < tie; tip++) {
+ ti = *tip;
+ if (token_info_skip(ctx, ti, rid, sid)) { goto exit; }
+ if (ti->p->rid != rid || ti->p->sid != sid) {
+ nrid = ti->p->rid;
+ nsid = ti->p->sid;
+ break;
+ }
+ }
+ weight = get_weight(ctx, s, rid, sid, wvm, optarg);
+ if (tip == tie && weight) {
+ grn_rset_posinfo pi = {rid, sid, 0};
+ if (orp || grn_hash_get(ctx, s, &pi, s->key_size, NULL)) {
+ int count = 0, noccur = 0, pos = 0, score = 0, tscore = 0, min, max;
+
+#define SKIP_OR_BREAK(pos) {\
+ if (token_info_skip_pos(ctx, ti, rid, sid, pos)) { break; } \
+ if (ti->p->rid != rid || ti->p->sid != sid) { \
+ nrid = ti->p->rid; \
+ nsid = ti->p->sid; \
+ break; \
+ } \
+}
+ if (n == 1 && !rep) {
+ noccur = (*tis)->p->tf;
+ tscore = (*tis)->p->weight;
+ } else if (mode == GRN_OP_NEAR) {
+ bt_zap(bt);
+ for (tip = tis; tip < tie; tip++) {
+ ti = *tip;
+ SKIP_OR_BREAK(pos);
+ bt_push(bt, ti);
+ }
+ if (tip == tie) {
+ for (;;) {
+ ti = bt->min; min = ti->pos; max = bt->max->pos;
+ if (min > max) { exit(0); }
+ if (max - min <= max_interval) {
+ if (rep) { pi.pos = min; res_add(ctx, s, &pi, weight, op); }
+ noccur++;
+ if (ti->pos == max + 1) {
+ break;
+ }
+ SKIP_OR_BREAK(max + 1);
+ } else {
+ if (ti->pos == max - max_interval) {
+ break;
+ }
+ SKIP_OR_BREAK(max - max_interval);
+ }
+ bt_pop(bt);
+ }
+ }
+ } else {
+ for (tip = tis; ; tip++) {
+ if (tip == tie) { tip = tis; }
+ ti = *tip;
+ SKIP_OR_BREAK(pos);
+ if (ti->pos == pos) {
+ score += ti->p->weight; count++;
+ } else {
+ score = ti->p->weight; count = 1; pos = ti->pos;
+ }
+ if (count == n) {
+ if (rep) { pi.pos = pos; res_add(ctx, s, &pi, (score + 1) * weight, op); }
+ tscore += score;
+ score = 0; count = 0; pos++;
+ noccur++;
+ }
+ }
+ }
+ if (noccur && !rep) { res_add(ctx, s, &pi, (noccur + tscore) * weight, op); }
+#undef SKIP_OR_BREAK
+ }
+ }
+ if (token_info_skip(ctx, *tis, nrid, nsid)) { goto exit; }
+ }
+exit :
+ for (tip = tis; tip < tis + n; tip++) {
+ if (*tip) { token_info_close(ctx, *tip); }
+ }
+ if (tis) { GRN_FREE(tis); }
+ grn_ii_resolve_sel_and(ctx, s, op);
+ // grn_hash_cursor_clear(r);
+ bt_close(ctx, bt);
+#ifdef DEBUG
+ {
+ uint32_t segno = GRN_II_MAX_LSEG, nnref = 0;
+ grn_io_mapinfo *info = ii->seg->maps;
+ for (; segno; segno--, info++) { if (info->nref) { nnref++; } }
+ GRN_LOG(ctx, GRN_LOG_INFO, "nnref=%d", nnref);
+ }
+#endif /* DEBUG */
+ return rc;
+}
+
+grn_rc
+grn_ii_sel(grn_ctx *ctx, grn_ii *ii, const char *string, unsigned int string_len,
+ grn_hash *s, grn_operator op, grn_search_optarg *optarg)
+{
+ ERRCLR(ctx);
+ GRN_LOG(ctx, GRN_LOG_INFO, "grn_ii_sel > (%.*s)", string_len, string);
+ {
+ grn_select_optarg arg = {GRN_OP_EXACT, 0, 0, NULL, 0, NULL, NULL, 0};
+ if (!s) { return GRN_INVALID_ARGUMENT; }
+ if (optarg) {
+ switch (optarg->mode) {
+ case GRN_OP_NEAR :
+ case GRN_OP_NEAR2 :
+ arg.mode = optarg->mode;
+ arg.max_interval = optarg->max_interval;
+ break;
+ case GRN_OP_SIMILAR :
+ arg.mode = optarg->mode;
+ break;
+ default :
+ break;
+ }
+ if (optarg->vector_size > 0) {
+ arg.weight_vector = optarg->weight_vector;
+ arg.vector_size = optarg->vector_size;
+ }
+ }
+ /* todo : support subrec
+ grn_rset_init(ctx, s, grn_rec_document, 0, grn_rec_none, 0, 0);
+ */
+ if (grn_ii_select(ctx, ii, string, string_len, s, op, &arg)) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "grn_ii_select on grn_ii_sel(1) failed !");
+ return ctx->rc;
+ }
+ GRN_LOG(ctx, GRN_LOG_INFO, "exact: %d", GRN_HASH_SIZE(s));
+ if (op == GRN_OP_OR) {
+ if ((int64_t)GRN_HASH_SIZE(s) <= ctx->impl->match_escalation_threshold) {
+ arg.mode = GRN_OP_UNSPLIT;
+ if (grn_ii_select(ctx, ii, string, string_len, s, op, &arg)) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "grn_ii_select on grn_ii_sel(2) failed !");
+ return ctx->rc;
+ }
+ GRN_LOG(ctx, GRN_LOG_INFO, "unsplit: %d", GRN_HASH_SIZE(s));
+ }
+ if ((int64_t)GRN_HASH_SIZE(s) <= ctx->impl->match_escalation_threshold) {
+ arg.mode = GRN_OP_PARTIAL;
+ if (grn_ii_select(ctx, ii, string, string_len, s, op, &arg)) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "grn_ii_select on grn_ii_sel(3) failed !");
+ return ctx->rc;
+ }
+ GRN_LOG(ctx, GRN_LOG_INFO, "partial: %d", GRN_HASH_SIZE(s));
+ }
+ }
+ GRN_LOG(ctx, GRN_LOG_INFO, "hits=%d", GRN_HASH_SIZE(s));
+ return GRN_SUCCESS;
+ }
+}
+
+grn_rc
+grn_ii_at(grn_ctx *ctx, grn_ii *ii, grn_id id, grn_hash *s, grn_operator op)
+{
+ int rep = 0;
+ grn_ii_cursor *c;
+ grn_ii_posting *pos;
+ if ((c = grn_ii_cursor_open(ctx, ii, id, GRN_ID_NIL, GRN_ID_MAX,
+ rep ? ii->n_elements : ii->n_elements - 1, 0))) {
+ while ((pos = grn_ii_cursor_next(ctx, c))) {
+ res_add(ctx, s, (grn_rset_posinfo *) pos, (1 + pos->weight), op);
+ }
+ grn_ii_cursor_close(ctx, c);
+ }
+ return ctx->rc;
+}
+
+void
+grn_ii_resolve_sel_and(grn_ctx *ctx, grn_hash *s, grn_operator op)
+{
+ if (op == GRN_OP_AND
+ && !(ctx->flags & GRN_CTX_TEMPORARY_DISABLE_II_RESOLVE_SEL_AND)) {
+ grn_id eid;
+ grn_rset_recinfo *ri;
+ grn_hash_cursor *c = grn_hash_cursor_open(ctx, s, NULL, 0, NULL, 0, 0, -1, 0);
+ if (c) {
+ while ((eid = grn_hash_cursor_next(ctx, c))) {
+ grn_hash_cursor_get_value(ctx, c, (void **) &ri);
+ if ((ri->n_subrecs & GRN_RSET_UTIL_BIT)) {
+ ri->n_subrecs &= ~GRN_RSET_UTIL_BIT;
+ } else {
+ grn_hash_delete_by_id(ctx, s, eid, NULL);
+ }
+ }
+ grn_hash_cursor_close(ctx, c);
+ }
+ }
+}
+
+/* just for inspect */
+static grn_ii_posting *
+grn_ii_cursor_next_all(grn_ctx *ctx, grn_ii_cursor *c)
+{
+ if (c->buf) {
+ for (;;) {
+ if (c->stat & CHUNK_USED) {
+ for (;;) {
+ if (c->crp < c->cdp + c->cdf) {
+ uint32_t dgap = *c->crp++;
+ c->pc.rid += dgap;
+ if (dgap) { c->pc.sid = 0; }
+ if ((c->ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ c->pc.sid += 1 + *c->csp++;
+ } else {
+ c->pc.sid = 1;
+ }
+ c->cpp += c->pc.rest;
+ c->pc.rest = c->pc.tf = 1 + *c->ctp++;
+ if ((c->ii->header->flags & GRN_OBJ_WITH_WEIGHT)) {
+ c->pc.weight = *c->cwp++;
+ } else {
+ c->pc.weight = 0;
+ }
+ c->pc.pos = 0;
+ /*
+ {
+ static int count = 0;
+ int tf = c->pc.tf, pos = 0, *pp = (int *)c->cpp;
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ grn_text_itoa(ctx, &buf, c->pc.rid);
+ GRN_TEXT_PUTC(ctx, &buf, ':');
+ grn_text_itoa(ctx, &buf, c->pc.sid);
+ GRN_TEXT_PUTC(ctx, &buf, ':');
+ grn_text_itoa(ctx, &buf, c->pc.tf);
+ GRN_TEXT_PUTC(ctx, &buf, '(');
+ while (tf--) {
+ pos += *pp++;
+ count++;
+ grn_text_itoa(ctx, &buf, pos);
+ if (tf) { GRN_TEXT_PUTC(ctx, &buf, ':'); }
+ }
+ GRN_TEXT_PUTC(ctx, &buf, ')');
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "posting(%d):%s", count, GRN_TEXT_VALUE(&buf));
+ GRN_OBJ_FIN(ctx, &buf);
+ }
+ */
+ } else {
+ if (c->curr_chunk <= c->nchunks) {
+ if (c->curr_chunk == c->nchunks) {
+ if (c->cp < c->cpe) {
+ grn_p_decv(ctx, c->cp, c->cpe - c->cp, c->rdv, c->ii->n_elements);
+ } else {
+ c->pc.rid = 0;
+ break;
+ }
+ } else {
+ uint8_t *cp;
+ grn_io_win iw;
+ uint32_t size = c->cinfo[c->curr_chunk].size;
+ if (size && (cp = WIN_MAP2(c->ii->chunk, ctx, &iw,
+ c->cinfo[c->curr_chunk].segno, 0,
+ size, grn_io_rdonly))) {
+ grn_p_decv(ctx, cp, size, c->rdv, c->ii->n_elements);
+ grn_io_win_unmap2(&iw);
+ } else {
+ c->pc.rid = 0;
+ break;
+ }
+ }
+ {
+ int j = 0;
+ c->cdf = c->rdv[j].data_size;
+ c->crp = c->cdp = c->rdv[j++].data;
+ if ((c->ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ c->csp = c->rdv[j++].data;
+ }
+ c->ctp = c->rdv[j++].data;
+ if ((c->ii->header->flags & GRN_OBJ_WITH_WEIGHT)) {
+ c->cwp = c->rdv[j++].data;
+ }
+ c->cpp = c->rdv[j].data;
+ }
+ c->pc.rid = 0;
+ c->pc.sid = 0;
+ c->pc.rest = 0;
+ c->curr_chunk++;
+ continue;
+ } else {
+ c->pc.rid = 0;
+ }
+ }
+ break;
+ }
+ }
+ if (c->stat & BUFFER_USED) {
+ if (c->nextb) {
+ uint32_t lrid = c->pb.rid, lsid = c->pb.sid; /* for check */
+ buffer_rec *br = BUFFER_REC_AT(c->buf, c->nextb);
+ if (buffer_is_reused(ctx, c->ii, c)) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "buffer reused(%d,%d)", c->buffer_pseg, *c->ppseg);
+ // todo : rewind;
+ }
+ c->bp = NEXT_ADDR(br);
+ GRN_B_DEC(c->pb.rid, c->bp);
+ if ((c->ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ GRN_B_DEC(c->pb.sid, c->bp);
+ } else {
+ c->pb.sid = 1;
+ }
+ if (lrid > c->pb.rid || (lrid == c->pb.rid && lsid >= c->pb.sid)) {
+ ERR(GRN_FILE_CORRUPT, "brokend!! (%d:%d) -> (%d:%d) (%d->%d)", lrid, lsid, c->pb.rid, c->pb.sid, c->buffer_pseg, *c->ppseg);
+ }
+ c->nextb = br->step;
+ GRN_B_DEC(c->pb.tf, c->bp);
+ if ((c->ii->header->flags & GRN_OBJ_WITH_WEIGHT)) {
+ GRN_B_DEC(c->pb.weight, c->bp);
+ } else {
+ c->pb.weight = 0;
+ }
+ c->pb.rest = c->pb.tf;
+ c->pb.pos = 0;
+ } else {
+ c->pb.rid = 0;
+ }
+ }
+ if (c->pb.rid) {
+ if (c->pc.rid) {
+ if (c->pc.rid < c->pb.rid) {
+ c->stat = CHUNK_USED;
+ c->post = &c->pc;
+ break;
+ } else {
+ if (c->pb.rid < c->pc.rid) {
+ c->stat = BUFFER_USED;
+ c->post = &c->pb;
+ break;
+ } else {
+ if (c->pb.sid) {
+ if (c->pc.sid < c->pb.sid) {
+ c->stat = CHUNK_USED;
+ c->post = &c->pc;
+ break;
+ } else {
+ c->stat = BUFFER_USED;
+ if (c->pb.sid == c->pc.sid) { c->stat |= CHUNK_USED; }
+ c->post = &c->pb;
+ break;
+ }
+ } else {
+ c->stat = CHUNK_USED;
+ }
+ }
+ }
+ } else {
+ c->stat = BUFFER_USED;
+ c->post = &c->pb;
+ break;
+ }
+ } else {
+ if (c->pc.rid) {
+ c->stat = CHUNK_USED;
+ c->post = &c->pc;
+ break;
+ } else {
+ c->post = NULL;
+ return NULL;
+ }
+ }
+ }
+ } else {
+ if (c->stat & SOLE_DOC_USED) {
+ c->post = NULL;
+ return NULL;
+ } else {
+ c->post = &c->pb;
+ c->stat |= SOLE_DOC_USED;
+ }
+ }
+ return c->post;
+}
+
+void
+grn_ii_cursor_inspect(grn_ctx *ctx, grn_ii_cursor *c, grn_obj *buf)
+{
+ grn_obj key_buf;
+ char key[GRN_TABLE_MAX_KEY_SIZE];
+ int key_size;
+ int i = 0;
+
+ GRN_TEXT_PUTS(ctx, buf, " #<");
+ key_size = grn_table_get_key(ctx, c->ii->lexicon, c->id,
+ key, GRN_TABLE_MAX_KEY_SIZE);
+ GRN_OBJ_INIT(&key_buf, GRN_BULK, 0, c->ii->lexicon->header.domain);
+ GRN_TEXT_SET(ctx, &key_buf, key, key_size);
+ grn_inspect(ctx, buf, &key_buf);
+ GRN_OBJ_FIN(ctx, &key_buf);
+
+ GRN_TEXT_PUTS(ctx, buf, "\n elements:[\n ");
+ while (grn_ii_cursor_next_all(ctx, c)) {
+ grn_ii_posting *pos = c->post;
+ if (i > 0) {
+ GRN_TEXT_PUTS(ctx, buf, ",\n ");
+ }
+ i++;
+ GRN_TEXT_PUTS(ctx, buf, "{status:");
+ if (pos->tf && pos->sid) {
+ GRN_TEXT_PUTS(ctx, buf, "available");
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "garbage");
+ }
+ GRN_TEXT_PUTS(ctx, buf, ", rid:");
+ grn_text_lltoa(ctx, buf, pos->rid);
+ GRN_TEXT_PUTS(ctx, buf, ", sid:");
+ grn_text_lltoa(ctx, buf, pos->sid);
+ GRN_TEXT_PUTS(ctx, buf, ", pos:");
+ grn_text_lltoa(ctx, buf, pos->pos);
+ GRN_TEXT_PUTS(ctx, buf, ", tf:");
+ grn_text_lltoa(ctx, buf, pos->tf);
+ GRN_TEXT_PUTS(ctx, buf, ", weight:");
+ grn_text_lltoa(ctx, buf, pos->weight);
+ GRN_TEXT_PUTS(ctx, buf, ", rest:");
+ grn_text_lltoa(ctx, buf, pos->rest);
+ GRN_TEXT_PUTS(ctx, buf, "}");
+ }
+ GRN_TEXT_PUTS(ctx, buf, "\n ]\n >");
+}
+
+void
+grn_ii_inspect_elements(grn_ctx *ctx, grn_ii *ii, grn_obj *buf)
+{
+ grn_table_cursor *tc;
+ GRN_TEXT_PUTS(ctx, buf, "[");
+ if ((tc = grn_table_cursor_open(ctx, ii->lexicon, NULL, 0, NULL, 0, 0, -1,
+ GRN_CURSOR_ASCENDING))) {
+ int i = 0;
+ grn_id tid;
+ grn_ii_cursor *c;
+ while ((tid = grn_table_cursor_next(ctx, tc))) {
+ if (i > 0) {
+ GRN_TEXT_PUTS(ctx, buf, ",");
+ }
+ i++;
+ GRN_TEXT_PUTS(ctx, buf, "\n");
+ if ((c = grn_ii_cursor_open(ctx, ii, tid, GRN_ID_NIL, GRN_ID_MAX,
+ ii->n_elements,
+ GRN_OBJ_WITH_POSITION|GRN_OBJ_WITH_SECTION))) {
+ grn_ii_cursor_inspect(ctx, c, buf);
+ grn_ii_cursor_close(ctx, c);
+ }
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ GRN_TEXT_PUTS(ctx, buf, "]");
+}
+
+/********************** buffered index builder ***********************/
+
+const grn_id II_BUFFER_RID_FLAG = 0x80000000;
+const grn_id II_BUFFER_WEIGHT_FLAG = 0x40000000;
+#ifdef II_BUFFER_ORDER_BY_ID
+const int II_BUFFER_ORDER = GRN_CURSOR_BY_ID;
+#else /* II_BUFFER_ORDER_BY_ID */
+const int II_BUFFER_ORDER = GRN_CURSOR_BY_KEY;
+#endif /* II_BUFFER_ORDER_BY_ID */
+const uint16_t II_BUFFER_NTERMS_PER_BUFFER = 16380;
+const uint32_t II_BUFFER_PACKED_BUF_SIZE = 0x4000000;
+const char *TMPFILE_PATH = "grn_ii_buffer_tmp";
+const uint32_t II_BUFFER_NCOUNTERS_MARGIN = 0x100000;
+const size_t II_BUFFER_BLOCK_SIZE = 0x1000000;
+const uint32_t II_BUFFER_BLOCK_READ_UNIT_SIZE = 0x200000;
+
+typedef struct {
+ uint32_t nrecs;
+ uint32_t nposts;
+ grn_id last_rid;
+ uint32_t last_sid;
+ uint32_t last_tf;
+ uint32_t last_weight;
+ uint32_t last_pos;
+ uint32_t offset_rid;
+ uint32_t offset_sid;
+ uint32_t offset_tf;
+ uint32_t offset_weight;
+ uint32_t offset_pos;
+} ii_buffer_counter;
+
+typedef struct {
+ off_t head;
+ off_t tail;
+ uint32_t nextsize;
+ uint8_t *buffer;
+ uint32_t buffersize;
+ uint8_t *bufcur;
+ uint32_t rest;
+ grn_id tid;
+ uint32_t nrecs;
+ uint32_t nposts;
+ grn_id *recs;
+ uint32_t *tfs;
+ uint32_t *posts;
+} ii_buffer_block;
+
+struct _grn_ii_buffer {
+ grn_obj *lexicon;
+ grn_obj *tmp_lexicon;
+ ii_buffer_block *blocks;
+ uint32_t nblocks;
+ int tmpfd;
+ char tmpfpath[PATH_MAX];
+ uint64_t update_buffer_size;
+ // stuff for parsing
+ off_t filepos;
+ grn_id *block_buf;
+ size_t block_buf_size;
+ size_t block_pos;
+ ii_buffer_counter *counters;
+ uint32_t ncounters;
+ size_t total_size;
+ size_t curr_size;
+ // stuff for merging
+ grn_ii *ii;
+ uint32_t lseg;
+ uint32_t dseg;
+ buffer *term_buffer;
+ datavec data_vectors[MAX_N_ELEMENTS + 1];
+ uint8_t *packed_buf;
+ size_t packed_buf_size;
+ size_t packed_len;
+ size_t total_chunk_size;
+};
+
+static ii_buffer_block *
+block_new(grn_ctx *ctx, grn_ii_buffer *ii_buffer)
+{
+ ii_buffer_block *block;
+ if (!(ii_buffer->nblocks & 0x3ff)) {
+ ii_buffer_block *blocks;
+ if (!(blocks = GRN_REALLOC(ii_buffer->blocks,
+ (ii_buffer->nblocks + 0x400) *
+ sizeof(ii_buffer_block)))) {
+ return NULL;
+ }
+ ii_buffer->blocks = blocks;
+ }
+ block = &ii_buffer->blocks[ii_buffer->nblocks];
+ block->head = ii_buffer->filepos;
+ block->rest = 0;
+ block->buffer = NULL;
+ block->buffersize = 0;
+ return block;
+}
+
+static uint8_t *
+allocate_outbuf(grn_ctx *ctx, grn_ii_buffer *ii_buffer)
+{
+ size_t bufsize = 0, bufsize_ = 0;
+ uint32_t flags = ii_buffer->ii->header->flags;
+ ii_buffer_counter *counter = ii_buffer->counters;
+ grn_id tid, tid_max = grn_table_size(ctx, ii_buffer->tmp_lexicon);
+ for (tid = 1; tid <= tid_max; counter++, tid++) {
+ counter->offset_tf += GRN_B_ENC_SIZE(counter->last_tf - 1);
+ counter->last_rid = 0;
+ counter->last_tf = 0;
+ bufsize += 5;
+ bufsize += GRN_B_ENC_SIZE(counter->nrecs);
+ bufsize += GRN_B_ENC_SIZE(counter->nposts);
+ bufsize += counter->offset_rid;
+ if ((flags & GRN_OBJ_WITH_SECTION)) {
+ bufsize += counter->offset_sid;
+ }
+ bufsize += counter->offset_tf;
+ if ((flags & GRN_OBJ_WITH_WEIGHT)) {
+ bufsize += counter->offset_weight;
+ }
+ if ((flags & GRN_OBJ_WITH_POSITION)) {
+ bufsize += counter->offset_pos;
+ }
+ if (bufsize_ + II_BUFFER_BLOCK_READ_UNIT_SIZE < bufsize) {
+ bufsize += sizeof(uint32_t);
+ bufsize_ = bufsize;
+ }
+ }
+ GRN_LOG(ctx, GRN_LOG_INFO, "flushing:%d bufsize:%zu",
+ ii_buffer->nblocks, bufsize);
+ return (uint8_t *)GRN_MALLOC(bufsize);
+}
+
+static size_t
+encode_terms(grn_ctx *ctx, grn_ii_buffer *ii_buffer,
+ uint8_t *outbuf, ii_buffer_block *block)
+{
+ grn_id tid;
+ uint8_t *outbufp = outbuf;
+ uint8_t *outbufp_ = outbuf;
+ grn_table_cursor *tc;
+ uint8_t *pnext = (uint8_t *)&block->nextsize;
+ uint32_t flags = ii_buffer->ii->header->flags;
+ tc = grn_table_cursor_open(ctx, ii_buffer->tmp_lexicon,
+ NULL, 0, NULL, 0, 0, -1, II_BUFFER_ORDER);
+ while ((tid = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) {
+ char key[GRN_TABLE_MAX_KEY_SIZE];
+ int key_size = grn_table_get_key(ctx, ii_buffer->tmp_lexicon, tid,
+ key, GRN_TABLE_MAX_KEY_SIZE);
+ grn_id gtid = grn_table_add(ctx, ii_buffer->lexicon, key, key_size, NULL);
+ ii_buffer_counter *counter = &ii_buffer->counters[tid - 1];
+ if (counter->nrecs) {
+ uint32_t offset_rid = counter->offset_rid;
+ uint32_t offset_sid = counter->offset_sid;
+ uint32_t offset_tf = counter->offset_tf;
+ uint32_t offset_weight = counter->offset_weight;
+ uint32_t offset_pos = counter->offset_pos;
+ GRN_B_ENC(gtid, outbufp);
+ GRN_B_ENC(counter->nrecs, outbufp);
+ GRN_B_ENC(counter->nposts, outbufp);
+ ii_buffer->total_size += counter->nrecs + counter->nposts;
+ counter->offset_rid = outbufp - outbuf;
+ outbufp += offset_rid;
+ if ((flags & GRN_OBJ_WITH_SECTION)) {
+ counter->offset_sid = outbufp - outbuf;
+ outbufp += offset_sid;
+ }
+ counter->offset_tf = outbufp - outbuf;
+ outbufp += offset_tf;
+ if ((flags & GRN_OBJ_WITH_WEIGHT)) {
+ counter->offset_weight = outbufp - outbuf;
+ outbufp += offset_weight;
+ }
+ if ((flags & GRN_OBJ_WITH_POSITION)) {
+ counter->offset_pos = outbufp - outbuf;
+ outbufp += offset_pos;
+ }
+ }
+ if (outbufp_ + II_BUFFER_BLOCK_READ_UNIT_SIZE < outbufp) {
+ uint32_t size = outbufp - outbufp_ + sizeof(uint32_t);
+ memcpy(pnext, &size, sizeof(uint32_t));
+ pnext = outbufp;
+ outbufp += sizeof(uint32_t);
+ outbufp_ = outbufp;
+ }
+ }
+ grn_table_cursor_close(ctx, tc);
+ if (outbufp_ < outbufp) {
+ uint32_t size = outbufp - outbufp_;
+ memcpy(pnext, &size, sizeof(uint32_t));
+ }
+ return outbufp - outbuf;
+}
+
+static void
+encode_postings(grn_ctx *ctx, grn_ii_buffer *ii_buffer, uint8_t *outbuf)
+{
+ grn_id rid = 0;
+ unsigned int sid = 1;
+ unsigned int weight = 0;
+ uint32_t pos = 0;
+ uint32_t rest;
+ grn_id *bp = ii_buffer->block_buf;
+ uint32_t flags = ii_buffer->ii->header->flags;
+ for (rest = ii_buffer->block_pos; rest; bp++, rest--) {
+ grn_id id = *bp;
+ if (id & II_BUFFER_RID_FLAG) {
+ rid = id - II_BUFFER_RID_FLAG;
+ if ((flags & GRN_OBJ_WITH_SECTION) && rest) {
+ sid = *++bp;
+ rest--;
+ }
+ weight = 0;
+ pos = 0;
+ } else if (id & II_BUFFER_WEIGHT_FLAG) {
+ weight = id - II_BUFFER_WEIGHT_FLAG;
+ } else {
+ ii_buffer_counter *counter = &ii_buffer->counters[id - 1];
+ if (counter->last_rid == rid && counter->last_sid == sid) {
+ counter->last_tf++;
+ counter->last_weight += weight;
+ } else {
+ if (counter->last_tf) {
+ uint8_t *p = outbuf + counter->offset_tf;
+ GRN_B_ENC(counter->last_tf - 1, p);
+ counter->offset_tf = p - outbuf;
+ if (flags & GRN_OBJ_WITH_WEIGHT) {
+ p = outbuf + counter->offset_weight;
+ GRN_B_ENC(counter->last_weight, p);
+ counter->offset_weight = p - outbuf;
+ }
+ }
+ {
+ uint8_t *p = outbuf + counter->offset_rid;
+ GRN_B_ENC(rid - counter->last_rid, p);
+ counter->offset_rid = p - outbuf;
+ }
+ if (flags & GRN_OBJ_WITH_SECTION) {
+ uint8_t *p = outbuf + counter->offset_sid;
+ if (counter->last_rid != rid) {
+ GRN_B_ENC(sid - 1, p);
+ } else {
+ GRN_B_ENC(sid - counter->last_sid - 1, p);
+ }
+ counter->offset_sid = p - outbuf;
+ }
+ counter->last_rid = rid;
+ counter->last_sid = sid;
+ counter->last_tf = 1;
+ counter->last_weight = weight;
+ counter->last_pos = 0;
+ }
+ if (flags & GRN_OBJ_WITH_POSITION) {
+ uint8_t *p = outbuf + counter->offset_pos;
+ GRN_B_ENC(pos - counter->last_pos, p);
+ counter->offset_pos = p - outbuf;
+ counter->last_pos = pos;
+ }
+ pos++;
+ }
+ }
+}
+
+static void
+encode_last_tf(grn_ctx *ctx, grn_ii_buffer *ii_buffer, uint8_t *outbuf)
+{
+ ii_buffer_counter *counter = ii_buffer->counters;
+ grn_id tid, tid_max = grn_table_size(ctx, ii_buffer->tmp_lexicon);
+ for (tid = 1; tid <= tid_max; counter++, tid++) {
+ uint8_t *p = outbuf + counter->offset_tf;
+ GRN_B_ENC(counter->last_tf - 1, p);
+ }
+ if ((ii_buffer->ii->header->flags & GRN_OBJ_WITH_WEIGHT)) {
+ for (tid = 1; tid <= tid_max; counter++, tid++) {
+ uint8_t *p = outbuf + counter->offset_weight;
+ GRN_B_ENC(counter->last_weight, p);
+ }
+ }
+}
+
+static void
+grn_ii_buffer_flush(grn_ctx *ctx, grn_ii_buffer *ii_buffer)
+{
+ size_t encsize;
+ uint8_t *outbuf;
+ ii_buffer_block *block;
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "flushing:%d npostings:%zu",
+ ii_buffer->nblocks, ii_buffer->block_pos);
+ if (!(block = block_new(ctx, ii_buffer))) { return; }
+ if (!(outbuf = allocate_outbuf(ctx, ii_buffer))) { return; }
+ encsize = encode_terms(ctx, ii_buffer, outbuf, block);
+ encode_postings(ctx, ii_buffer, outbuf);
+ encode_last_tf(ctx, ii_buffer, outbuf);
+ {
+ ssize_t r = GRN_WRITE(ii_buffer->tmpfd, outbuf, encsize);
+ if (r != encsize) {
+ ERR(GRN_INPUT_OUTPUT_ERROR, "write returned %" GRN_FMT_LLD " != %" GRN_FMT_LLU,
+ (long long int)r, (unsigned long long int)encsize);
+ return;
+ }
+ ii_buffer->filepos += r;
+ block->tail = ii_buffer->filepos;
+ }
+ GRN_FREE(outbuf);
+ memset(ii_buffer->counters, 0,
+ grn_table_size(ctx, ii_buffer->tmp_lexicon) *
+ sizeof(ii_buffer_counter));
+ grn_obj_close(ctx, ii_buffer->tmp_lexicon);
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "flushed: %d encsize:%zu",
+ ii_buffer->nblocks, encsize);
+ ii_buffer->tmp_lexicon = NULL;
+ ii_buffer->nblocks++;
+ ii_buffer->block_pos = 0;
+}
+
+const uint32_t PAT_CACHE_SIZE = 1<<20;
+
+static grn_obj *
+get_tmp_lexicon(grn_ctx *ctx, grn_ii_buffer *ii_buffer)
+{
+ grn_obj *tmp_lexicon = ii_buffer->tmp_lexicon;
+ if (!tmp_lexicon) {
+ grn_obj *domain = grn_ctx_at(ctx, ii_buffer->lexicon->header.domain);
+ grn_obj *range = grn_ctx_at(ctx, DB_OBJ(ii_buffer->lexicon)->range);
+ grn_obj *tokenizer;
+ grn_obj *normalizer;
+ grn_obj_flags flags;
+ grn_table_get_info(ctx, ii_buffer->lexicon, &flags, NULL,
+ &tokenizer, &normalizer);
+ flags &= ~GRN_OBJ_PERSISTENT;
+ tmp_lexicon = grn_table_create(ctx, NULL, 0, NULL, flags, domain, range);
+ if (tmp_lexicon) {
+ ii_buffer->tmp_lexicon = tmp_lexicon;
+ grn_obj_set_info(ctx, tmp_lexicon,
+ GRN_INFO_DEFAULT_TOKENIZER, tokenizer);
+ grn_obj_set_info(ctx, tmp_lexicon,
+ GRN_INFO_NORMALIZER, normalizer);
+ if ((flags & GRN_OBJ_TABLE_TYPE_MASK) == GRN_OBJ_TABLE_PAT_KEY) {
+ grn_pat_cache_enable(ctx, (grn_pat *)tmp_lexicon, PAT_CACHE_SIZE);
+ }
+ }
+ }
+ return tmp_lexicon;
+}
+
+static ii_buffer_counter *
+get_buffer_counter(grn_ctx *ctx, grn_ii_buffer *ii_buffer,
+ grn_obj *tmp_lexicon, grn_id tid)
+{
+ if (tid > ii_buffer->ncounters) {
+ ii_buffer_counter *counters;
+ uint32_t ncounters =
+ grn_table_size(ctx, tmp_lexicon) + II_BUFFER_NCOUNTERS_MARGIN;
+ counters = GRN_REALLOC(ii_buffer->counters,
+ ncounters * sizeof(ii_buffer_counter));
+ if (!counters) { return NULL; }
+ memset(&counters[ii_buffer->ncounters], 0,
+ (ncounters - ii_buffer->ncounters) * sizeof(ii_buffer_counter));
+ ii_buffer->ncounters = ncounters;
+ ii_buffer->counters = counters;
+ }
+ return &ii_buffer->counters[tid - 1];
+}
+
+static void
+grn_ii_buffer_tokenize(grn_ctx *ctx, grn_ii_buffer *ii_buffer, grn_id rid,
+ unsigned int sid, unsigned int weight,
+ const char *value, uint32_t value_len)
+{
+ if (value_len) {
+ grn_obj *tmp_lexicon;
+ uint32_t est_len = value_len + 2;
+ if (ii_buffer->block_buf_size < ii_buffer->block_pos + est_len) {
+ grn_ii_buffer_flush(ctx, ii_buffer);
+ }
+ if (ii_buffer->block_buf_size < est_len) {
+ grn_id *block_buf = (grn_id *)GRN_REALLOC(ii_buffer->block_buf,
+ est_len * sizeof(grn_id));
+ if (!block_buf) { return; }
+ ii_buffer->block_buf = block_buf;
+ ii_buffer->block_buf_size = est_len;
+ }
+ if ((tmp_lexicon = get_tmp_lexicon(ctx, ii_buffer))) {
+ unsigned int token_flags = 0;
+ grn_token *token;
+ grn_id *buffer = ii_buffer->block_buf;
+ uint32_t block_pos = ii_buffer->block_pos;
+ buffer[block_pos++] = rid + II_BUFFER_RID_FLAG;
+ if ((ii_buffer->ii->header->flags & GRN_OBJ_WITH_SECTION)) {
+ buffer[block_pos++] = sid;
+ }
+ if (weight) {
+ buffer[block_pos++] = weight + II_BUFFER_WEIGHT_FLAG;
+ }
+ if ((token = grn_token_open(ctx, tmp_lexicon, value,
+ value_len, GRN_TOKEN_ADD, token_flags))) {
+ uint32_t pos;
+ for (pos = 0; !token->status; pos++) {
+ grn_id tid;
+ if ((tid = grn_token_next(ctx, token))) {
+ ii_buffer_counter *counter;
+ counter = get_buffer_counter(ctx, ii_buffer, tmp_lexicon, tid);
+ if (!counter) { return; }
+ buffer[block_pos++] = tid;
+ if (counter->last_rid != rid) {
+ counter->offset_rid += GRN_B_ENC_SIZE(rid - counter->last_rid);
+ counter->last_rid = rid;
+ counter->offset_sid += GRN_B_ENC_SIZE(sid - 1);
+ counter->last_sid = sid;
+ if (counter->last_tf) {
+ counter->offset_tf += GRN_B_ENC_SIZE(counter->last_tf - 1);
+ counter->last_tf = 0;
+ counter->offset_weight += GRN_B_ENC_SIZE(counter->last_weight);
+ counter->last_weight = 0;
+ }
+ counter->last_pos = 0;
+ counter->nrecs++;
+ } else if (counter->last_sid != sid) {
+ counter->offset_rid += GRN_B_ENC_SIZE(0);
+ counter->offset_sid +=
+ GRN_B_ENC_SIZE(sid - counter->last_sid - 1);
+ counter->last_sid = sid;
+ if (counter->last_tf) {
+ counter->offset_tf += GRN_B_ENC_SIZE(counter->last_tf - 1);
+ counter->last_tf = 0;
+ counter->offset_weight += GRN_B_ENC_SIZE(counter->last_weight);
+ counter->last_weight = 0;
+ }
+ counter->last_pos = 0;
+ counter->nrecs++;
+ }
+ counter->offset_pos += GRN_B_ENC_SIZE(pos - counter->last_pos);
+ counter->last_pos = pos;
+ counter->last_tf++;
+ counter->last_weight += weight;
+ counter->nposts++;
+ }
+ }
+ grn_token_close(ctx, token);
+ }
+ ii_buffer->block_pos = block_pos;
+ }
+ }
+}
+
+static void
+grn_ii_buffer_fetch(grn_ctx *ctx, grn_ii_buffer *ii_buffer,
+ ii_buffer_block *block)
+{
+ if (!block->rest) {
+ if (block->head < block->tail) {
+ size_t bytesize = block->nextsize;
+ if (block->buffersize < block->nextsize) {
+ void *r = GRN_REALLOC(block->buffer, bytesize);
+ if (r) {
+ block->buffer = (uint8_t *)r;
+ block->buffersize = block->nextsize;
+ } else {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "realloc: %" GRN_FMT_LLU,
+ (unsigned long long int)bytesize);
+ return;
+ }
+ }
+ if (lseek(ii_buffer->tmpfd, block->head, SEEK_SET) != block->head) {
+ SERR("lseek");
+ return;
+ }
+ if (read(ii_buffer->tmpfd, block->buffer, bytesize) != bytesize) {
+ SERR("read");
+ return;
+ }
+ block->head += bytesize;
+ block->bufcur = block->buffer;
+ if (block->head >= block->tail) {
+ if (block->head > block->tail) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "fetch error: %jd > %jd", block->head, block->tail);
+ }
+ block->rest = block->nextsize;
+ block->nextsize = 0;
+ } else {
+ block->rest = block->nextsize - sizeof(uint32_t);
+ memcpy(&block->nextsize,
+ &block->buffer[block->rest], sizeof(uint32_t));
+ }
+ }
+ }
+ if (block->rest) {
+ uint8_t *p = block->bufcur;
+ GRN_B_DEC(block->tid, p);
+ GRN_B_DEC(block->nrecs, p);
+ GRN_B_DEC(block->nposts, p);
+ block->rest -= (p - block->bufcur);
+ block->bufcur = p;
+ } else {
+ block->tid = 0;
+ }
+}
+
+static void
+grn_ii_buffer_chunk_flush(grn_ctx *ctx, grn_ii_buffer *ii_buffer)
+{
+ grn_io_win io_win;
+ uint32_t chunk_number;
+ chunk_new(ctx, ii_buffer->ii, &chunk_number, ii_buffer->packed_len);
+ GRN_LOG(ctx, GRN_LOG_INFO, "chunk:%d, packed_len:%zu",
+ chunk_number, ii_buffer->packed_len);
+ fake_map2(ctx, ii_buffer->ii->chunk, &io_win, ii_buffer->packed_buf,
+ chunk_number, ii_buffer->packed_len);
+ grn_io_win_unmap2(&io_win);
+ ii_buffer->term_buffer->header.chunk = chunk_number;
+ ii_buffer->term_buffer->header.chunk_size = ii_buffer->packed_len;
+ ii_buffer->term_buffer->header.buffer_free =
+ S_SEGMENT - sizeof(buffer_header) -
+ ii_buffer->term_buffer->header.nterms * sizeof(buffer_term);
+ ii_buffer->term_buffer->header.nterms_void = 0;
+ buffer_segment_update(ii_buffer->ii, ii_buffer->lseg, ii_buffer->dseg);
+ ii_buffer->ii->header->total_chunk_size += ii_buffer->packed_len;
+ ii_buffer->total_chunk_size += ii_buffer->packed_len;
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "nterms=%d chunk=%d total=%" GRN_FMT_INT64U "KB",
+ ii_buffer->term_buffer->header.nterms,
+ ii_buffer->term_buffer->header.chunk_size,
+ ii_buffer->ii->header->total_chunk_size >> 10);
+ ii_buffer->term_buffer = NULL;
+ ii_buffer->packed_buf = NULL;
+ ii_buffer->packed_len = 0;
+ ii_buffer->packed_buf_size = 0;
+ ii_buffer->curr_size = 0;
+}
+
+static size_t
+merge_hit_blocks(grn_ctx *ctx, grn_ii_buffer *ii_buffer,
+ ii_buffer_block *hits[], int nhits)
+{
+ uint64_t nrecs = 0;
+ uint64_t nposts = 0;
+ size_t max_size;
+ uint64_t flags = ii_buffer->ii->header->flags;
+ int i;
+ for (i = 0; i < nhits; i++) {
+ ii_buffer_block *block = hits[i];
+ nrecs += block->nrecs;
+ nposts += block->nposts;
+ }
+ ii_buffer->curr_size += nrecs + nposts;
+ max_size = nrecs * (ii_buffer->ii->n_elements);
+ if (flags & GRN_OBJ_WITH_POSITION) { max_size += nposts - nrecs; }
+ datavec_reset(ctx, ii_buffer->data_vectors,
+ ii_buffer->ii->n_elements, nrecs, max_size);
+ {
+ int i;
+ uint32_t lr = 0;
+ uint64_t spos = 0;
+ uint32_t *ridp, *sidp = NULL, *tfp, *weightp = NULL, *posp = NULL;
+ {
+ int j = 0;
+ ridp = ii_buffer->data_vectors[j++].data;
+ if (flags & GRN_OBJ_WITH_SECTION) {
+ sidp = ii_buffer->data_vectors[j++].data;
+ }
+ tfp = ii_buffer->data_vectors[j++].data;
+ if (flags & GRN_OBJ_WITH_WEIGHT) {
+ weightp = ii_buffer->data_vectors[j++].data;
+ }
+ if (flags & GRN_OBJ_WITH_POSITION) {
+ posp = ii_buffer->data_vectors[j++].data;
+ }
+ }
+ for (i = 0; i < nhits; i++) {
+ ii_buffer_block *block = hits[i];
+ uint8_t *p = block->bufcur;
+ uint32_t n = block->nrecs;
+ if (n) {
+ GRN_B_DEC(*ridp, p);
+ *ridp -= lr;
+ lr += *ridp++;
+ while (--n) {
+ GRN_B_DEC(*ridp, p);
+ lr += *ridp++;
+ }
+ }
+ if ((flags & GRN_OBJ_WITH_SECTION)) {
+ for (n = block->nrecs; n; n--) {
+ GRN_B_DEC(*sidp++, p);
+ }
+ }
+ for (n = block->nrecs; n; n--) {
+ GRN_B_DEC(*tfp++, p);
+ }
+ if ((flags & GRN_OBJ_WITH_WEIGHT)) {
+ for (n = block->nrecs; n; n--) {
+ GRN_B_DEC(*weightp++, p);
+ }
+ }
+ if ((flags & GRN_OBJ_WITH_POSITION)) {
+ for (n = block->nposts; n; n--) {
+ GRN_B_DEC(*posp, p);
+ spos += *posp++;
+ }
+ }
+ block->rest -= (p - block->bufcur);
+ block->bufcur = p;
+ grn_ii_buffer_fetch(ctx, ii_buffer, block);
+ }
+ {
+ int j = 0;
+ uint32_t f_s = (nrecs < 3) ? 0 : USE_P_ENC;
+ uint32_t f_d = ((nrecs < 16) || (nrecs <= (lr >> 8))) ? 0 : USE_P_ENC;
+ ii_buffer->data_vectors[j].data_size = nrecs;
+ ii_buffer->data_vectors[j++].flags = f_d;
+ if ((flags & GRN_OBJ_WITH_SECTION)) {
+ ii_buffer->data_vectors[j].data_size = nrecs;
+ ii_buffer->data_vectors[j++].flags = f_s;
+ }
+ ii_buffer->data_vectors[j].data_size = nrecs;
+ ii_buffer->data_vectors[j++].flags = f_s;
+ if ((flags & GRN_OBJ_WITH_WEIGHT)) {
+ ii_buffer->data_vectors[j].data_size = nrecs;
+ ii_buffer->data_vectors[j++].flags = f_s;
+ }
+ if ((flags & GRN_OBJ_WITH_POSITION)) {
+ uint32_t f_p = (((nposts < 32) ||
+ (nposts <= (spos >> 13))) ? 0 : USE_P_ENC);
+ ii_buffer->data_vectors[j].data_size = nposts;
+ ii_buffer->data_vectors[j++].flags = f_p|ODD;
+ }
+ }
+ }
+ return (max_size + ii_buffer->ii->n_elements) * 4;
+}
+
+static buffer *
+get_term_buffer(grn_ctx *ctx, grn_ii_buffer *ii_buffer)
+{
+ if (!ii_buffer->term_buffer) {
+ uint32_t lseg;
+ void *term_buffer;
+ for (lseg = 0; lseg < GRN_II_MAX_LSEG; lseg++) {
+ if (ii_buffer->ii->header->binfo[lseg] == NOT_ASSIGNED) { break; }
+ }
+ if (lseg == GRN_II_MAX_LSEG) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "segment allocate failed");
+ return NULL;
+ }
+ ii_buffer->lseg = lseg;
+ ii_buffer->dseg = segment_get(ctx, ii_buffer->ii);
+ GRN_IO_SEG_REF(ii_buffer->ii->seg, ii_buffer->dseg, term_buffer);
+ ii_buffer->term_buffer = (buffer *)term_buffer;
+ }
+ return ii_buffer->term_buffer;
+}
+
+static grn_bool
+try_in_place_packing(grn_ctx *ctx, grn_ii_buffer *ii_buffer,
+ grn_id tid, ii_buffer_block *hits[], int nhits)
+{
+ if (nhits == 1 && hits[0]->nrecs == 1 && hits[0]->nposts == 1) {
+ grn_id rid;
+ uint32_t sid = 1, tf, pos = 0, weight = 0;
+ ii_buffer_block *block = hits[0];
+ uint8_t *p = block->bufcur;
+ uint32_t flags = ii_buffer->ii->header->flags;
+ GRN_B_DEC(rid, p);
+ if (flags & GRN_OBJ_WITH_SECTION) {
+ GRN_B_DEC(sid, p);
+ sid++;
+ }
+ GRN_B_DEC(tf, p);
+ if (tf != 0) { GRN_LOG(ctx, GRN_LOG_WARNING, "tf=%d", tf); }
+ if (flags & GRN_OBJ_WITH_WEIGHT) { GRN_B_DEC(weight, p); }
+ if (flags & GRN_OBJ_WITH_POSITION) { GRN_B_DEC(pos, p); }
+ if (!weight) {
+ if (flags & GRN_OBJ_WITH_SECTION) {
+ if (rid < 0x100000 && sid < 0x800) {
+ uint32_t *a = array_get(ctx, ii_buffer->ii, tid);
+ a[0] = (rid << 12) + (sid << 1) + 1;
+ a[1] = pos;
+ } else {
+ return GRN_FALSE;
+ }
+ } else {
+ uint32_t *a = array_get(ctx, ii_buffer->ii, tid);
+ a[0] = (rid << 1) + 1;
+ a[1] = pos;
+ }
+ block->rest -= (p - block->bufcur);
+ block->bufcur = p;
+ grn_ii_buffer_fetch(ctx, ii_buffer, block);
+ return GRN_TRUE;
+ }
+ }
+ return GRN_FALSE;
+}
+
+static void
+grn_ii_buffer_merge(grn_ctx *ctx, grn_ii_buffer *ii_buffer,
+ grn_id tid, ii_buffer_block *hits[], int nhits)
+{
+ if (!try_in_place_packing(ctx, ii_buffer, tid, hits, nhits)) {
+ size_t max_size = merge_hit_blocks(ctx, ii_buffer, hits, nhits);
+ if (ii_buffer->packed_buf &&
+ ii_buffer->packed_buf_size < ii_buffer->packed_len + max_size) {
+ grn_ii_buffer_chunk_flush(ctx, ii_buffer);
+ }
+ if (!ii_buffer->packed_buf) {
+ size_t buf_size = (max_size > II_BUFFER_PACKED_BUF_SIZE)
+ ? max_size : II_BUFFER_PACKED_BUF_SIZE;
+ if ((ii_buffer->packed_buf = GRN_MALLOC(buf_size))) {
+ ii_buffer->packed_buf_size = buf_size;
+ }
+ }
+ {
+ uint16_t nterm;
+ size_t packed_len;
+ buffer_term *bt;
+ uint32_t *a = array_get(ctx, ii_buffer->ii, tid);
+ buffer *term_buffer = get_term_buffer(ctx, ii_buffer);
+ if (!term_buffer) { return; }
+ nterm = term_buffer->header.nterms++;
+ bt = &term_buffer->terms[nterm];
+ a[0] = SEG2POS(ii_buffer->lseg,
+ (sizeof(buffer_header) + sizeof(buffer_term) * nterm));
+ packed_len = grn_p_encv(ctx, ii_buffer->data_vectors,
+ ii_buffer->ii->n_elements,
+ ii_buffer->packed_buf +
+ ii_buffer->packed_len);
+ a[1] = ii_buffer->data_vectors[0].data_size;
+ bt->tid = tid;
+ bt->size_in_buffer = 0;
+ bt->pos_in_buffer = 0;
+ bt->size_in_chunk = packed_len;
+ bt->pos_in_chunk = ii_buffer->packed_len;
+ ii_buffer->packed_len += packed_len;
+ if (((ii_buffer->curr_size * ii_buffer->update_buffer_size) +
+ (ii_buffer->total_size * term_buffer->header.nterms * 16)) >=
+ (ii_buffer->total_size * II_BUFFER_NTERMS_PER_BUFFER * 16)) {
+ grn_ii_buffer_chunk_flush(ctx, ii_buffer);
+ }
+ }
+ }
+}
+
+grn_ii_buffer *
+grn_ii_buffer_open(grn_ctx *ctx, grn_ii *ii,
+ long long unsigned int update_buffer_size)
+{
+ if (ii && ii->lexicon) {
+ grn_ii_buffer *ii_buffer = GRN_MALLOCN(grn_ii_buffer, 1);
+ if (ii_buffer) {
+ ii_buffer->ii = ii;
+ ii_buffer->lexicon = ii->lexicon;
+ ii_buffer->tmp_lexicon = NULL;
+ ii_buffer->nblocks = 0;
+ ii_buffer->blocks = NULL;
+ ii_buffer->ncounters = II_BUFFER_NCOUNTERS_MARGIN;
+ ii_buffer->block_pos = 0;
+ ii_buffer->filepos = 0;
+ ii_buffer->curr_size = 0;
+ ii_buffer->total_size = 0;
+ ii_buffer->update_buffer_size = update_buffer_size;
+ ii_buffer->counters = GRN_CALLOC(ii_buffer->ncounters *
+ sizeof(ii_buffer_counter));
+ ii_buffer->term_buffer = NULL;
+ ii_buffer->packed_buf = NULL;
+ ii_buffer->packed_len = 0;
+ ii_buffer->packed_buf_size = 0;
+ ii_buffer->total_chunk_size = 0;
+ if (ii_buffer->counters) {
+ ii_buffer->block_buf = GRN_MALLOCN(grn_id, II_BUFFER_BLOCK_SIZE);
+ if (ii_buffer->block_buf) {
+ snprintf(ii_buffer->tmpfpath, PATH_MAX,
+ "%sXXXXXX", grn_io_path(ii->seg));
+ ii_buffer->block_buf_size = II_BUFFER_BLOCK_SIZE;
+ ii_buffer->tmpfd = GRN_MKOSTEMP(ii_buffer->tmpfpath,
+ O_WRONLY|O_CREAT|O_TRUNC,
+ S_IRUSR|S_IWUSR);
+ if (ii_buffer->tmpfd != -1) {
+ grn_obj_flags flags;
+ grn_table_get_info(ctx, ii->lexicon, &flags, NULL, NULL, NULL);
+ if ((flags & GRN_OBJ_TABLE_TYPE_MASK) == GRN_OBJ_TABLE_PAT_KEY) {
+ grn_pat_cache_enable(ctx, (grn_pat *)ii->lexicon,
+ PAT_CACHE_SIZE);
+ }
+ return ii_buffer;
+ } else {
+ SERR("mkostemp");
+ }
+ GRN_FREE(ii_buffer->block_buf);
+ }
+ GRN_FREE(ii_buffer->counters);
+ }
+ GRN_FREE(ii_buffer);
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "ii or ii->lexicon is NULL");
+ }
+ return NULL;
+}
+
+grn_rc
+grn_ii_buffer_append(grn_ctx *ctx, grn_ii_buffer *ii_buffer,
+ grn_id rid, unsigned int sid, grn_obj *value)
+{
+ grn_ii_buffer_tokenize(ctx, ii_buffer, rid, sid, 0,
+ GRN_TEXT_VALUE(value), GRN_TEXT_LEN(value));
+ return ctx->rc;
+}
+
+grn_rc
+grn_ii_buffer_commit(grn_ctx *ctx, grn_ii_buffer *ii_buffer)
+{
+ if (ii_buffer->block_pos) {
+ grn_ii_buffer_flush(ctx, ii_buffer);
+ }
+ if (ii_buffer->tmpfd != -1) {
+ GRN_CLOSE(ii_buffer->tmpfd);
+ }
+ if (ii_buffer->block_buf) {
+ GRN_FREE(ii_buffer->block_buf);
+ ii_buffer->block_buf = NULL;
+ }
+ if (ii_buffer->counters) {
+ GRN_FREE(ii_buffer->counters);
+ ii_buffer->counters = NULL;
+ }
+
+ if (ii_buffer->update_buffer_size &&
+ ii_buffer->update_buffer_size < 20) {
+ if (ii_buffer->update_buffer_size < 10) {
+ ii_buffer->update_buffer_size =
+ ii_buffer->total_size >> (10 - ii_buffer->update_buffer_size);
+ } else {
+ ii_buffer->update_buffer_size =
+ ii_buffer->total_size << (ii_buffer->update_buffer_size - 10);
+ }
+ }
+
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "nblocks=%d, update_buffer_size=%" GRN_FMT_INT64U,
+ ii_buffer->nblocks, ii_buffer->update_buffer_size);
+
+ datavec_init(ctx, ii_buffer->data_vectors, ii_buffer->ii->n_elements, 0, 0);
+#ifdef WIN32
+ ii_buffer->tmpfd = GRN_OPEN(ii_buffer->tmpfpath, O_RDONLY|O_BINARY);
+#else /* WIN32 */
+ ii_buffer->tmpfd = GRN_OPEN(ii_buffer->tmpfpath, O_RDONLY);
+#endif /* WIN32 */
+ if (ii_buffer->tmpfd == -1) {
+ SERR("oepn");
+ return ctx->rc;
+ }
+ {
+ uint32_t i;
+ for (i = 0; i < ii_buffer->nblocks; i++) {
+ grn_ii_buffer_fetch(ctx, ii_buffer, &ii_buffer->blocks[i]);
+ }
+ }
+ {
+ ii_buffer_block **hits;
+ if ((hits = GRN_MALLOCN(ii_buffer_block *, ii_buffer->nblocks))) {
+ grn_id tid;
+ grn_table_cursor *tc;
+ tc = grn_table_cursor_open(ctx, ii_buffer->lexicon,
+ NULL, 0, NULL, 0, 0, -1, II_BUFFER_ORDER);
+ if (tc) {
+ while ((tid = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) {
+ int nrests = 0;
+ int nhits = 0;
+ uint32_t i;
+ for (i = 0; i < ii_buffer->nblocks; i++) {
+ if (ii_buffer->blocks[i].tid == tid) {
+ hits[nhits++] = &ii_buffer->blocks[i];
+ }
+ if (ii_buffer->blocks[i].tid) { nrests++; }
+ }
+ if (nhits) { grn_ii_buffer_merge(ctx, ii_buffer, tid, hits, nhits); }
+ if (!nrests) { break; }
+ }
+ if (ii_buffer->packed_len) {
+ grn_ii_buffer_chunk_flush(ctx, ii_buffer);
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ GRN_FREE(hits);
+ }
+ }
+ datavec_fin(ctx, ii_buffer->data_vectors);
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "tmpfile_size:%jd > total_chunk_size:%" GRN_FMT_INT64U,
+ ii_buffer->filepos, ii_buffer->total_chunk_size);
+ GRN_CLOSE(ii_buffer->tmpfd);
+ unlink(ii_buffer->tmpfpath);
+ ii_buffer->tmpfd = -1;
+ return ctx->rc;
+}
+
+grn_rc
+grn_ii_buffer_close(grn_ctx *ctx, grn_ii_buffer *ii_buffer)
+{
+ uint32_t i;
+ grn_obj_flags flags;
+ grn_table_get_info(ctx, ii_buffer->ii->lexicon, &flags, NULL, NULL, NULL);
+ if ((flags & GRN_OBJ_TABLE_TYPE_MASK) == GRN_OBJ_TABLE_PAT_KEY) {
+ grn_pat_cache_disable(ctx, (grn_pat *)ii_buffer->ii->lexicon);
+ }
+ if (ii_buffer->tmp_lexicon) {
+ grn_obj_close(ctx, ii_buffer->tmp_lexicon);
+ }
+ if (ii_buffer->tmpfd != -1) {
+ GRN_CLOSE(ii_buffer->tmpfd);
+ unlink(ii_buffer->tmpfpath);
+ }
+ if (ii_buffer->block_buf) {
+ GRN_FREE(ii_buffer->block_buf);
+ }
+ if (ii_buffer->counters) {
+ GRN_FREE(ii_buffer->counters);
+ }
+ if (ii_buffer->blocks) {
+ for (i = 0; i < ii_buffer->nblocks; i++) {
+ if (ii_buffer->blocks[i].buffer) {
+ GRN_FREE(ii_buffer->blocks[i].buffer);
+ }
+ }
+ GRN_FREE(ii_buffer->blocks);
+ }
+ GRN_FREE(ii_buffer);
+ return ctx->rc;
+}
+
+static void
+grn_ii_buffer_parse(grn_ctx *ctx, grn_ii_buffer *ii_buffer,
+ grn_obj *target, int ncols, grn_obj **cols)
+{
+ grn_table_cursor *tc;
+ if ((tc = grn_table_cursor_open(ctx, target,
+ NULL, 0, NULL, 0, 0, -1,
+ GRN_CURSOR_BY_ID))) {
+ grn_id rid;
+ grn_obj rv;
+ GRN_TEXT_INIT(&rv, 0);
+ while ((rid = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) {
+ int sid;
+ grn_obj **col;
+ for (sid = 1, col = cols; sid <= ncols; sid++, col++) {
+ grn_obj_reinit_for(ctx, &rv, *col);
+ if (GRN_OBJ_TABLEP(*col)) {
+ grn_table_get_key2(ctx, *col, rid, &rv);
+ } else {
+ grn_obj_get_value(ctx, *col, rid, &rv);
+ }
+ switch (rv.header.type) {
+ case GRN_BULK :
+ grn_ii_buffer_tokenize(ctx, ii_buffer, rid, sid, 0,
+ GRN_TEXT_VALUE(&rv), GRN_TEXT_LEN(&rv));
+ break;
+ case GRN_VECTOR :
+ if (rv.u.v.body) {
+ int i;
+ int n_sections = rv.u.v.n_sections;
+ grn_section *sections = rv.u.v.sections;
+ const char *head = GRN_BULK_HEAD(rv.u.v.body);
+ for (i = 0; i < n_sections; i++) {
+ grn_section *section = sections + i;
+ if (section->length == 0) {
+ continue;
+ }
+ grn_ii_buffer_tokenize(ctx, ii_buffer, rid,
+ sid, section->weight,
+ head + section->offset, section->length);
+ }
+ }
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "[index] invalid object assigned as value");
+ break;
+ }
+ }
+ }
+ GRN_OBJ_FIN(ctx, &rv);
+ grn_table_cursor_close(ctx, tc);
+ }
+}
+
+grn_rc
+grn_ii_build(grn_ctx *ctx, grn_ii *ii, uint64_t sparsity)
+{
+ grn_ii_buffer *ii_buffer = grn_ii_buffer_open(ctx, ii, sparsity);
+ if (ii_buffer) {
+ grn_id *s = ii->obj.source;
+ if ((ii->obj.source_size) && s) {
+ int ncols = ii->obj.source_size / sizeof(grn_id);
+ grn_obj **cols = GRN_MALLOCN(grn_obj *, ncols);
+ if (cols) {
+ int i;
+ for (i = 0; i < ncols; i++) {
+ if (!(cols[i] = grn_ctx_at(ctx, s[i]))) { break; }
+ }
+ if (i == ncols) {
+ grn_obj *target = cols[0];
+ if (!GRN_OBJ_TABLEP(target)) {
+ target = grn_ctx_at(ctx, target->header.domain);
+ }
+ if (target) {
+ grn_ii_buffer_parse(ctx, ii_buffer, target, ncols, cols);
+ grn_ii_buffer_commit(ctx, ii_buffer);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "failed to resolve the target");
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "failed to resolve a column (%d)", i);
+ }
+ GRN_FREE(cols);
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "ii->obj.source is void");
+ }
+ grn_ii_buffer_close(ctx, ii_buffer);
+ }
+ return ctx->rc;
+}
diff --git a/storage/mroonga/vendor/groonga/lib/ii.h b/storage/mroonga/vendor/groonga/lib/ii.h
new file mode 100644
index 00000000000..877200fff7c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/ii.h
@@ -0,0 +1,196 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_II_H
+#define GRN_II_H
+
+/* "ii" is for inverted index */
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifndef GRN_HASH_H
+#include "hash.h"
+#endif /* GRN_HASH_H */
+
+#ifndef GRN_IO_H
+#include "io.h"
+#endif /* GRN_IO_H */
+
+#ifndef GRN_STORE_H
+#include "store.h"
+#endif /* GRN_STORE_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _grn_ii {
+ grn_db_obj obj;
+ grn_io *seg;
+ grn_io *chunk;
+ grn_obj *lexicon;
+ grn_obj_flags lflags;
+ grn_encoding encoding;
+ uint32_t n_elements;
+ struct grn_ii_header *header;
+};
+
+#define GRN_II_BGQSIZE 16
+#define GRN_II_MAX_LSEG 0x10000
+#define GRN_II_W_TOTAL_CHUNK 40
+#define GRN_II_W_CHUNK 22
+#define GRN_II_W_LEAST_CHUNK (GRN_II_W_TOTAL_CHUNK - 32)
+#define GRN_II_MAX_CHUNK (1 << (GRN_II_W_TOTAL_CHUNK - GRN_II_W_CHUNK))
+#define GRN_II_N_CHUNK_VARIATION (GRN_II_W_CHUNK - GRN_II_W_LEAST_CHUNK)
+
+struct grn_ii_header {
+ uint64_t total_chunk_size;
+ uint64_t bmax;
+ uint32_t flags;
+ uint32_t amax;
+ uint32_t smax;
+ uint32_t param1;
+ uint32_t param2;
+ uint32_t pnext;
+ uint32_t bgqhead;
+ uint32_t bgqtail;
+ uint32_t bgqbody[GRN_II_BGQSIZE];
+ uint32_t reserved[288];
+ uint32_t ainfo[GRN_II_MAX_LSEG];
+ uint32_t binfo[GRN_II_MAX_LSEG];
+ uint32_t free_chunks[GRN_II_N_CHUNK_VARIATION + 1];
+ uint32_t garbages[GRN_II_N_CHUNK_VARIATION + 1];
+ uint32_t ngarbages[GRN_II_N_CHUNK_VARIATION + 1];
+ uint8_t chunks[GRN_II_MAX_CHUNK >> 3];
+};
+
+struct _grn_ii_pos {
+ struct _grn_ii_pos *next;
+ uint32_t pos;
+};
+
+struct _grn_ii_updspec {
+ uint32_t rid;
+ uint32_t sid;
+ int32_t weight;
+ int32_t tf; /* number of postings successfully stored to index */
+ int32_t atf; /* actual number of postings */
+ int32_t offset;
+ struct _grn_ii_pos *pos;
+ struct _grn_ii_pos *tail;
+ /* grn_vgram_vnode *vnodes; */
+};
+
+typedef struct _grn_ii_updspec grn_ii_updspec;
+
+GRN_API grn_ii *grn_ii_create(grn_ctx *ctx, const char *path, grn_obj *lexicon,
+ uint32_t flags);
+GRN_API grn_ii *grn_ii_open(grn_ctx *ctx, const char *path, grn_obj *lexicon);
+GRN_API grn_rc grn_ii_close(grn_ctx *ctx, grn_ii *ii);
+GRN_API grn_rc grn_ii_remove(grn_ctx *ctx, const char *path);
+grn_rc grn_ii_info(grn_ctx *ctx, grn_ii *ii, uint64_t *seg_size, uint64_t *chunk_size);
+grn_rc grn_ii_update_one(grn_ctx *ctx, grn_ii *ii, uint32_t key, grn_ii_updspec *u,
+ grn_hash *h);
+grn_rc grn_ii_delete_one(grn_ctx *ctx, grn_ii *ii, uint32_t key, grn_ii_updspec *u,
+ grn_hash *h);
+grn_ii_updspec *grn_ii_updspec_open(grn_ctx *ctx, uint32_t rid, uint32_t sid);
+grn_rc grn_ii_updspec_close(grn_ctx *ctx, grn_ii_updspec *u);
+grn_rc grn_ii_updspec_add(grn_ctx *ctx, grn_ii_updspec *u, int pos, int32_t weight);
+int grn_ii_updspec_cmp(grn_ii_updspec *a, grn_ii_updspec *b);
+
+uint32_t grn_ii_estimate_size(grn_ctx *ctx, grn_ii *ii, uint32_t key);
+
+void grn_ii_expire(grn_ctx *ctx, grn_ii *ii);
+
+typedef struct {
+ grn_id rid;
+ uint32_t sid;
+ uint32_t pos;
+ uint32_t tf;
+ uint32_t weight;
+ uint32_t rest;
+} grn_ii_posting;
+
+typedef struct _grn_ii_cursor grn_ii_cursor;
+
+GRN_API grn_rc grn_ii_posting_add(grn_ctx *ctx, grn_ii_posting *pos,
+ grn_hash *s, grn_operator op);
+
+GRN_API grn_ii_cursor *grn_ii_cursor_open(grn_ctx *ctx, grn_ii *ii, grn_id tid,
+ grn_id min, grn_id max, int nelements, int flags);
+grn_ii_cursor *grn_ii_cursor_openv1(grn_ii *ii, uint32_t key);
+grn_rc grn_ii_cursor_openv2(grn_ii_cursor **cursors, int ncursors);
+GRN_API grn_ii_posting *grn_ii_cursor_next(grn_ctx *ctx, grn_ii_cursor *c);
+grn_ii_posting *grn_ii_cursor_next_pos(grn_ctx *ctx, grn_ii_cursor *c);
+GRN_API grn_rc grn_ii_cursor_close(grn_ctx *ctx, grn_ii_cursor *c);
+
+uint32_t grn_ii_max_section(grn_ii *ii);
+
+int grn_ii_check(grn_ii *ii);
+const char *grn_ii_path(grn_ii *ii);
+grn_obj *grn_ii_lexicon(grn_ii *ii);
+
+/*
+grn_rc grn_ii_upd(grn_ctx *ctx, grn_ii *ii, grn_id rid, grn_vgram *vgram,
+ const char *oldvalue, unsigned int oldvalue_len,
+ const char *newvalue, unsigned int newvalue_len);
+grn_rc grn_ii_update(grn_ctx *ctx, grn_ii *ii, grn_id rid, grn_vgram *vgram,
+ unsigned int section,
+ grn_values *oldvalues, grn_values *newvalues);
+*/
+
+typedef struct _grn_select_optarg grn_select_optarg;
+
+struct _grn_select_optarg {
+ grn_operator mode;
+ int similarity_threshold;
+ int max_interval;
+ int *weight_vector;
+ int vector_size;
+ int (*func)(grn_ctx *, grn_hash *, const void *, int, void *);
+ void *func_arg;
+ int max_size;
+};
+
+GRN_API grn_rc grn_ii_column_update(grn_ctx *ctx, grn_ii *ii, grn_id id,
+ unsigned int section, grn_obj *oldvalue,
+ grn_obj *newvalue, grn_obj *posting);
+grn_rc grn_ii_term_extract(grn_ctx *ctx, grn_ii *ii, const char *string,
+ unsigned int string_len, grn_hash *s,
+ grn_operator op, grn_select_optarg *optarg);
+grn_rc grn_ii_similar_search(grn_ctx *ctx, grn_ii *ii, const char *string, unsigned int string_len,
+ grn_hash *s, grn_operator op, grn_select_optarg *optarg);
+GRN_API grn_rc grn_ii_select(grn_ctx *ctx, grn_ii *ii, const char *string, unsigned int string_len,
+ grn_hash *s, grn_operator op, grn_select_optarg *optarg);
+grn_rc grn_ii_sel(grn_ctx *ctx, grn_ii *ii, const char *string, unsigned int string_len,
+ grn_hash *s, grn_operator op, grn_search_optarg *optarg);
+
+void grn_ii_resolve_sel_and(grn_ctx *ctx, grn_hash *s, grn_operator op);
+
+grn_rc grn_ii_at(grn_ctx *ctx, grn_ii *ii, grn_id id, grn_hash *s, grn_operator op);
+
+void grn_ii_inspect_elements(grn_ctx *ctx, grn_ii *ii, grn_obj *buf);
+void grn_ii_cursor_inspect(grn_ctx *ctx, grn_ii_cursor *c, grn_obj *buf);
+
+grn_rc grn_ii_build(grn_ctx *ctx, grn_ii *ii, uint64_t sparsity);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_II_H */
diff --git a/storage/mroonga/vendor/groonga/lib/io.c b/storage/mroonga/vendor/groonga/lib/io.c
new file mode 100644
index 00000000000..067832ee77b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/io.c
@@ -0,0 +1,1801 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "groonga_in.h"
+
+#include <stdio.h>
+
+#ifndef __USE_GNU
+#define __USE_GNU /* O_DIRECT */
+#endif /* __USE_GNU */
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "ctx.h"
+#include "io.h"
+#include "plugin_in.h"
+#include "hash.h"
+#include "ctx_impl.h"
+
+#define GRN_IO_IDSTR "GROONGA:IO:00001"
+
+#ifndef O_BINARY
+# ifdef _O_BINARY
+# define O_BINARY _O_BINARY
+# else
+# define O_BINARY 0
+# endif
+#endif
+
+typedef struct _grn_io_fileinfo {
+#ifdef WIN32
+ HANDLE fh;
+ HANDLE fmo;
+ grn_critical_section cs;
+#else /* WIN32 */
+ int fd;
+ dev_t dev;
+ ino_t inode;
+#endif /* WIN32 */
+} fileinfo;
+
+#define IO_HEADER_SIZE 64
+
+inline static grn_rc grn_open(grn_ctx *ctx, fileinfo *fi, const char *path, int flags, size_t maxsize);
+inline static void grn_fileinfo_init(fileinfo *fis, int nfis);
+inline static int grn_opened(fileinfo *fi);
+inline static grn_rc grn_close(grn_ctx *ctx, fileinfo *fi);
+#if defined(WIN32) && defined(WIN32_FMO_EACH)
+inline static void * grn_mmap(grn_ctx *ctx, HANDLE *fmo, fileinfo *fi,
+ off_t offset, size_t length);
+inline static int grn_munmap(grn_ctx *ctx, HANDLE *fmo, void *start, size_t length);
+#define GRN_MMAP(ctx,fmo,fi,offset,length)\
+ (grn_mmap((ctx), (fmo), (fi), (offset), (length)))
+#define GRN_MUNMAP(ctx,fmo,start,length) (grn_munmap((ctx), (fmo), (start), (length)))
+#else /* defined(WIN32) && defined(WIN32_FMO_EACH) */
+inline static void * grn_mmap(grn_ctx *ctx, fileinfo *fi, off_t offset, size_t length);
+inline static int grn_munmap(grn_ctx *ctx, void *start, size_t length);
+#define GRN_MUNMAP(ctx,fmo,start,length) (grn_munmap((ctx), (start), (length)))
+#ifdef USE_FAIL_MALLOC
+inline static void * grn_fail_mmap(grn_ctx *ctx, fileinfo *fi,
+ off_t offset, size_t length,
+ const char* file, int line, const char *func);
+#define GRN_MMAP(ctx,fmo,fi,offset,length) \
+ (grn_fail_mmap((ctx), (fi), (offset), (length), __FILE__, __LINE__, __FUNCTION__))
+#else /* USE_FAIL_MALLOC */
+#define GRN_MMAP(ctx,fmo,fi,offset,length) (grn_mmap((ctx), (fi), (offset), (length)))
+#endif /* USE_FAIL_MALLOC */
+#endif /* defined(WIN32) && defined(WIN32_FMO_EACH) */
+inline static int grn_msync(grn_ctx *ctx, void *start, size_t length);
+inline static grn_rc grn_pread(grn_ctx *ctx, fileinfo *fi, void *buf, size_t count, off_t offset);
+inline static grn_rc grn_pwrite(grn_ctx *ctx, fileinfo *fi, void *buf, size_t count, off_t offset);
+
+grn_rc
+grn_io_init(void)
+{
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_io_fin(void)
+{
+ return GRN_SUCCESS;
+}
+
+grn_io *
+grn_io_create_tmp(uint32_t header_size, uint32_t segment_size,
+ uint32_t max_segment, grn_io_mode mode, uint32_t flags)
+{
+ grn_io *io;
+ unsigned int b;
+ uint32_t total_header_size;
+ struct _grn_io_header *header;
+ total_header_size = IO_HEADER_SIZE + header_size;
+ b = (total_header_size + grn_pagesize - 1) & ~(grn_pagesize - 1);
+ if ((header = (struct _grn_io_header *)GRN_MMAP(&grn_gctx, NULL, NULL, 0, b))) {
+ header->header_size = header_size;
+ header->segment_size = segment_size;
+ header->max_segment = max_segment;
+ header->n_arrays = 0;
+ header->flags = flags;
+ header->lock = 0;
+ memcpy(header->idstr, GRN_IO_IDSTR, 16);
+ if ((io = GRN_GMALLOCN(grn_io, 1))) {
+ grn_io_mapinfo *maps = NULL;
+ if (((maps = GRN_GMALLOCN(grn_io_mapinfo, max_segment)) &&
+ memset(maps, 0, sizeof(grn_io_mapinfo) * max_segment))) {
+ io->header = header;
+ io->user_header = (((byte *) header) + IO_HEADER_SIZE);
+ io->maps = maps;
+ io->base = b;
+ io->base_seg = 0;
+ io->mode = mode;
+ io->header->curr_size = b;
+ io->fis = NULL;
+ io->ainfo = NULL;
+ io->max_map_seg = 0;
+ io->nmaps = 0;
+ io->count = 0;
+ io->flags = GRN_IO_TEMPORARY;
+ io->lock = &header->lock;
+ io->path[0] = '\0';
+ return io;
+ }
+ GRN_GFREE(io);
+ }
+ GRN_MUNMAP(&grn_gctx, NULL, header, b);
+ }
+ return NULL;
+}
+
+static void
+grn_io_register(grn_io *io)
+{
+ if (io->fis && (io->flags & (GRN_IO_EXPIRE_GTICK|GRN_IO_EXPIRE_SEGMENT))) {
+ grn_bool succeeded = GRN_FALSE;
+ CRITICAL_SECTION_ENTER(grn_glock);
+ if (grn_gctx.impl && grn_gctx.impl->ios &&
+ grn_hash_add(&grn_gctx, grn_gctx.impl->ios, io->path, strlen(io->path),
+ (void **)&io, NULL)) {
+ succeeded = GRN_TRUE;
+ }
+ CRITICAL_SECTION_LEAVE(grn_glock);
+ if (!succeeded) {
+ GRN_LOG(&grn_gctx, GRN_LOG_WARNING,
+ "grn_io_register(%s) failed", io->path);
+ }
+ }
+}
+
+static void
+grn_io_unregister(grn_io *io)
+{
+ if (io->fis && (io->flags & (GRN_IO_EXPIRE_GTICK|GRN_IO_EXPIRE_SEGMENT))) {
+ grn_bool succeeded = GRN_FALSE;
+ CRITICAL_SECTION_ENTER(grn_glock);
+ if (grn_gctx.impl && grn_gctx.impl->ios) {
+ grn_hash_delete(&grn_gctx, grn_gctx.impl->ios,
+ io->path, strlen(io->path), NULL);
+ succeeded = GRN_TRUE;
+ }
+ CRITICAL_SECTION_LEAVE(grn_glock);
+ if (!succeeded) {
+ GRN_LOG(&grn_gctx, GRN_LOG_WARNING,
+ "grn_io_unregister(%s) failed", io->path);
+ }
+ }
+}
+
+grn_io *
+grn_io_create(grn_ctx *ctx, const char *path, uint32_t header_size, uint32_t segment_size,
+ uint32_t max_segment, grn_io_mode mode, uint32_t flags)
+{
+ grn_io *io;
+ fileinfo *fis;
+ unsigned int b, max_nfiles;
+ uint32_t bs, total_header_size;
+ struct _grn_io_header *header;
+ if (!path) {
+ return grn_io_create_tmp(header_size, segment_size, max_segment, mode, flags);
+ }
+ if (!*path || (strlen(path) > PATH_MAX - 4)) { return NULL; }
+ total_header_size = IO_HEADER_SIZE + header_size;
+ b = (total_header_size + grn_pagesize - 1) & ~(grn_pagesize - 1);
+ bs = (b + segment_size - 1) / segment_size;
+ max_nfiles = (unsigned int)(
+ ((uint64_t)segment_size * (max_segment + bs) + GRN_IO_FILE_SIZE - 1)
+ / GRN_IO_FILE_SIZE);
+ if ((fis = GRN_GMALLOCN(fileinfo, max_nfiles))) {
+ grn_fileinfo_init(fis, max_nfiles);
+ if (!grn_open(ctx, fis, path, O_RDWR|O_CREAT|O_EXCL, GRN_IO_FILE_SIZE)) {
+ if ((header = (struct _grn_io_header *)GRN_MMAP(&grn_gctx, &fis->fmo, fis, 0, b))) {
+ header->header_size = header_size;
+ header->segment_size = segment_size;
+ header->max_segment = max_segment;
+ header->n_arrays = 0;
+ header->flags = flags;
+ header->lock = 0;
+ memcpy(header->idstr, GRN_IO_IDSTR, 16);
+ grn_msync(ctx, header, b);
+ if ((io = GRN_GMALLOCN(grn_io, 1))) {
+ grn_io_mapinfo *maps = NULL;
+ if (((maps = GRN_GMALLOCN(grn_io_mapinfo, max_segment)) &&
+ memset(maps, 0, sizeof(grn_io_mapinfo) * max_segment))) {
+ strncpy(io->path, path, PATH_MAX);
+ io->header = header;
+ io->user_header = (((byte *) header) + IO_HEADER_SIZE);
+ io->maps = maps;
+ io->base = b;
+ io->base_seg = bs;
+ io->mode = mode;
+ io->header->curr_size = b;
+ io->fis = fis;
+ io->ainfo = NULL;
+ io->max_map_seg = 0;
+ io->nmaps = 0;
+ io->count = 0;
+ io->flags = flags;
+ io->lock = &header->lock;
+ grn_io_register(io);
+ return io;
+ }
+ GRN_GFREE(io);
+ }
+ GRN_MUNMAP(&grn_gctx, &fis->fmo, header, b);
+ }
+ grn_close(ctx, fis);
+ }
+ GRN_GFREE(fis);
+ }
+ return NULL;
+}
+
+static grn_rc
+array_init_(grn_io *io, int n_arrays, size_t hsize, size_t msize)
+{
+ int i;
+ uint32_t ws;
+ byte *hp, *mp;
+ grn_io_array_spec *array_specs = (grn_io_array_spec *)io->user_header;
+ hp = io->user_header;
+ if (!(mp = GRN_GCALLOC(msize))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ io->ainfo = (grn_io_array_info *)mp;
+ hp += sizeof(grn_io_array_spec) * n_arrays;
+ mp += sizeof(grn_io_array_info) * n_arrays;
+ for (ws = 0; (1 << ws) < io->header->segment_size; ws++);
+ for (i = 0; i < n_arrays; i++) {
+ uint32_t we = ws - array_specs[i].w_of_element;
+ io->ainfo[i].w_of_elm_in_a_segment = we;
+ io->ainfo[i].elm_mask_in_a_segment = (1 << we) - 1;
+ io->ainfo[i].max_n_segments = array_specs[i].max_n_segments;
+ io->ainfo[i].element_size = 1 << array_specs[i].w_of_element;
+ io->ainfo[i].segments = (uint32_t *)hp;
+ io->ainfo[i].addrs = (void **)mp;
+ hp += sizeof(uint32_t) * array_specs[i].max_n_segments;
+ mp += sizeof(void *) * array_specs[i].max_n_segments;
+ }
+ io->user_header += hsize;
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+array_init(grn_io *io, int n_arrays)
+{
+ if (n_arrays) {
+ int i;
+ grn_io_array_spec *array_specs = (grn_io_array_spec *)io->user_header;
+ size_t hsize = sizeof(grn_io_array_spec) * n_arrays;
+ size_t msize = sizeof(grn_io_array_info) * n_arrays;
+ for (i = 0; i < n_arrays; i++) {
+ hsize += sizeof(uint32_t) * array_specs[i].max_n_segments;
+ msize += sizeof(void *) * array_specs[i].max_n_segments;
+ }
+ return array_init_(io, n_arrays, hsize, msize);
+ }
+ return GRN_SUCCESS;
+}
+
+grn_io *
+grn_io_create_with_array(grn_ctx *ctx, const char *path,
+ uint32_t header_size, uint32_t segment_size,
+ grn_io_mode mode, int n_arrays, grn_io_array_spec *array_specs)
+{
+ if (n_arrays) {
+ int i;
+ grn_io *io;
+ byte *hp;
+ uint32_t nsegs = 0;
+ size_t hsize = sizeof(grn_io_array_spec) * n_arrays;
+ size_t msize = sizeof(grn_io_array_info) * n_arrays;
+ for (i = 0; i < n_arrays; i++) {
+ nsegs += array_specs[i].max_n_segments;
+ hsize += sizeof(uint32_t) * array_specs[i].max_n_segments;
+ msize += sizeof(void *) * array_specs[i].max_n_segments;
+ }
+ if ((io = grn_io_create(ctx, path, header_size + hsize,
+ segment_size, nsegs, mode, GRN_IO_EXPIRE_GTICK))) {
+ hp = io->user_header;
+ memcpy(hp, array_specs, sizeof(grn_io_array_spec) * n_arrays);
+ io->header->n_arrays = n_arrays;
+ io->header->segment_tail = 1;
+ if (!array_init_(io, n_arrays, hsize, msize)) {
+ return io;
+ }
+ ERR(GRN_NO_MEMORY_AVAILABLE, "grn_io_create_with_array failed");
+ grn_io_close(ctx, io);
+ }
+ }
+ return NULL;
+}
+
+inline static uint32_t
+segment_alloc(grn_io *io)
+{
+ uint32_t n, s;
+ grn_io_array_info *ai;
+ if (io->header->segment_tail) {
+ if (io->header->segment_tail > io->header->max_segment) {
+ s = 0;
+ } else {
+ s = io->header->segment_tail++;
+ }
+ } else {
+ char *used = GRN_GCALLOC(io->header->max_segment + 1);
+ if (!used) { return 0; }
+ for (n = io->header->n_arrays, ai = io->ainfo; n; n--, ai++) {
+ for (s = 0; s < ai->max_n_segments; s++) {
+ used[ai->segments[s]] = 1;
+ }
+ }
+ for (s = 1; ; s++) {
+ if (s > io->header->max_segment) {
+ io->header->segment_tail = s;
+ s = 0;
+ break;
+ }
+ if (!used[s]) {
+ io->header->segment_tail = s + 1;
+ break;
+ }
+ }
+ GRN_GFREE(used);
+ }
+ return s;
+}
+
+void
+grn_io_segment_alloc(grn_ctx *ctx, grn_io *io, grn_io_array_info *ai, uint32_t lseg, int *flags, void **p)
+{
+ uint32_t *sp = &ai->segments[lseg];
+ if (!*sp) {
+ if ((*flags & GRN_TABLE_ADD)) {
+ if ((*sp = segment_alloc(io))) {
+ *flags |= GRN_TABLE_ADDED;
+ }
+ }
+ }
+ if (*sp) {
+ uint32_t pseg = *sp - 1;
+ GRN_IO_SEG_REF(io, pseg, *p);
+ if (*p) { GRN_IO_SEG_UNREF(io, pseg); };
+ }
+}
+
+void *
+grn_io_array_at(grn_ctx *ctx, grn_io *io, uint32_t array, off_t offset, int *flags)
+{
+ void *res;
+ GRN_IO_ARRAY_AT(io,array,offset,flags,res);
+ return res;
+}
+
+uint32_t
+grn_io_detect_type(grn_ctx *ctx, const char *path)
+{
+ struct _grn_io_header h;
+ uint32_t res = 0;
+ int fd = GRN_OPEN(path, O_RDWR | O_BINARY);
+ if (fd != -1) {
+ struct stat s;
+ if (fstat(fd, &s) != -1 && s.st_size >= sizeof(struct _grn_io_header)) {
+ if (read(fd, &h, sizeof(struct _grn_io_header)) == sizeof(struct _grn_io_header)) {
+ if (!memcmp(h.idstr, GRN_IO_IDSTR, 16)) {
+ res = h.type;
+ } else {
+ ERR(GRN_INCOMPATIBLE_FILE_FORMAT, "incompatible file format");
+ }
+ } else {
+ SERR(path);
+ }
+ } else {
+ ERR(GRN_INVALID_FORMAT, "grn_io_detect_type failed");
+ }
+ GRN_CLOSE(fd);
+ } else {
+ SERR(path);
+ }
+ return res;
+}
+
+grn_io *
+grn_io_open(grn_ctx *ctx, const char *path, grn_io_mode mode)
+{
+ grn_io *io;
+ struct stat s;
+ fileinfo *fis;
+ uint32_t flags = 0;
+ unsigned int b, max_nfiles;
+ uint32_t total_header_size;
+ uint32_t header_size = 0, segment_size = 0, max_segment = 0, bs;
+ if (!path || !*path || (strlen(path) > PATH_MAX - 4)) { return NULL; }
+ {
+ struct _grn_io_header h;
+ int fd = GRN_OPEN(path, O_RDWR | O_BINARY);
+ if (fd == -1) { SERR(path); return NULL; }
+ if (fstat(fd, &s) != -1 && s.st_size >= sizeof(struct _grn_io_header)) {
+ if (read(fd, &h, sizeof(struct _grn_io_header)) == sizeof(struct _grn_io_header)) {
+ if (!memcmp(h.idstr, GRN_IO_IDSTR, 16)) {
+ header_size = h.header_size;
+ segment_size = h.segment_size;
+ max_segment = h.max_segment;
+ flags = h.flags;
+ } else {
+ ERR(GRN_INCOMPATIBLE_FILE_FORMAT, "incompatible file format");
+ }
+ }
+ }
+ GRN_CLOSE(fd);
+ if (!segment_size) { return NULL; }
+ }
+ total_header_size = IO_HEADER_SIZE + header_size;
+ b = (total_header_size + grn_pagesize - 1) & ~(grn_pagesize - 1);
+ bs = (b + segment_size - 1) / segment_size;
+ max_nfiles = (unsigned int)(
+ ((uint64_t)segment_size * (max_segment + bs) + GRN_IO_FILE_SIZE - 1)
+ / GRN_IO_FILE_SIZE);
+ if (!(fis = GRN_GMALLOCN(fileinfo, max_nfiles))) { return NULL; }
+ grn_fileinfo_init(fis, max_nfiles);
+ if (!grn_open(ctx, fis, path, O_RDWR, GRN_IO_FILE_SIZE)) {
+ struct _grn_io_header *header;
+ if ((header = GRN_MMAP(&grn_gctx, &fis->fmo, fis, 0, b))) {
+ if ((io = GRN_GMALLOC(sizeof(grn_io)))) {
+ grn_io_mapinfo *maps = NULL;
+ if ((maps = GRN_GCALLOC(sizeof(grn_io_mapinfo) * max_segment))) {
+ strncpy(io->path, path, PATH_MAX);
+ io->header = header;
+ io->user_header = (((byte *) header) + IO_HEADER_SIZE);
+ {
+ io->maps = maps;
+ io->base = b;
+ io->base_seg = bs;
+ io->mode = mode;
+ io->fis = fis;
+ io->ainfo = NULL;
+ io->max_map_seg = 0;
+ io->nmaps = 0;
+ io->count = 0;
+ io->flags = header->flags;
+ io->lock = &header->lock;
+ if (!array_init(io, io->header->n_arrays)) {
+ grn_io_register(io);
+ return io;
+ }
+ }
+ if (io->maps) { GRN_GFREE(io->maps); }
+ }
+ GRN_GFREE(io);
+ }
+ GRN_MUNMAP(&grn_gctx, &fis->fmo, header, b);
+ }
+ grn_close(ctx, fis);
+ }
+ GRN_GFREE(fis);
+ return NULL;
+}
+
+grn_rc
+grn_io_close(grn_ctx *ctx, grn_io *io)
+{
+ int i;
+ grn_io_mapinfo *mi;
+ fileinfo *fi;
+ uint32_t bs = io->base_seg;
+ uint32_t max_segment = io->header->segment_tail
+ ? io->header->segment_tail : io->header->max_segment;
+ uint32_t segment_size = io->header->segment_size;
+ unsigned int max_nfiles = (unsigned int)(
+ ((uint64_t)segment_size * (max_segment + bs) + GRN_IO_FILE_SIZE - 1)
+ / GRN_IO_FILE_SIZE);
+ grn_io_unregister(io);
+ if (io->ainfo) { GRN_GFREE(io->ainfo); }
+ if (io->maps) {
+ for (mi = io->maps, i = max_segment; i; mi++, i--) {
+ if (mi->map) {
+ /* if (atomic_read(mi->nref)) { return STILL_IN_USE ; } */
+#ifdef WIN32
+ if ((io->flags & GRN_IO_TEMPORARY)) {
+ GRN_GFREE(mi->map);
+ } else
+#endif /* WIN32 */
+ GRN_MUNMAP(&grn_gctx, &mi->fmo, mi->map, segment_size);
+ }
+ }
+ GRN_GFREE(io->maps);
+ }
+#ifdef WIN32
+ if ((io->flags & GRN_IO_TEMPORARY)) {
+ GRN_GFREE(io->header);
+ } else
+#endif /* WIN32 */
+ GRN_MUNMAP(&grn_gctx, &io->fis->fmo, io->header, io->base);
+ if (io->fis) {
+ for (fi = io->fis, i = max_nfiles; i; fi++, i--) { grn_close(ctx, fi); }
+ GRN_GFREE(io->fis);
+ }
+ GRN_GFREE(io);
+ return GRN_SUCCESS;
+}
+
+uint32_t
+grn_io_base_seg(grn_io *io)
+{
+ return io->base_seg;
+}
+
+const char *
+grn_io_path(grn_io *io)
+{
+ return io->path;
+}
+
+void *
+grn_io_header(grn_io *io)
+{
+ return io->user_header;
+}
+
+grn_rc
+grn_io_set_type(grn_io *io, uint32_t type)
+{
+ if (!io || !io->header) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ io->header->type = type;
+ return GRN_SUCCESS;
+}
+
+uint32_t
+grn_io_get_type(grn_io *io)
+{
+ if (!io || !io->header) { return GRN_VOID; }
+ return io->header->type;
+}
+
+inline static void
+gen_pathname(const char *path, char *buffer, int fno)
+{
+ size_t len = strlen(path);
+ memcpy(buffer, path, len);
+ if (fno) {
+ buffer[len] = '.';
+ grn_itoh(fno, buffer + len + 1, 3);
+ buffer[len + 4] = '\0';
+ } else {
+ buffer[len] = '\0';
+ }
+}
+
+grn_rc
+grn_io_size(grn_ctx *ctx, grn_io *io, uint64_t *size)
+{
+ int fno;
+ struct stat s;
+ uint64_t tsize = 0;
+ char buffer[PATH_MAX];
+ uint32_t nfiles;
+ if (io->header->curr_size) {
+ nfiles = (uint32_t) ((io->header->curr_size + GRN_IO_FILE_SIZE - 1) / GRN_IO_FILE_SIZE);
+ } else {
+ uint32_t bs = io->base_seg;
+ uint32_t max_segment = io->header->max_segment;
+ uint32_t segment_size = io->header->segment_size;
+ nfiles = (uint32_t) (((uint64_t)segment_size * (max_segment + bs) + GRN_IO_FILE_SIZE - 1)
+ / GRN_IO_FILE_SIZE);
+ }
+ for (fno = 0; fno < nfiles; fno++) {
+ gen_pathname(io->path, buffer, fno);
+ if (stat(buffer, &s)) {
+ SERR(buffer);
+ } else {
+ tsize += s.st_size;
+ }
+ }
+ *size = tsize;
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_io_remove(grn_ctx *ctx, const char *path)
+{
+ struct stat s;
+ if (stat(path, &s)) {
+ SERR("stat");
+ return ctx->rc;
+ } else if (unlink(path)) {
+ SERR(path);
+ return ctx->rc;
+ } else {
+ int fno;
+ char buffer[PATH_MAX];
+ for (fno = 1; ; fno++) {
+ gen_pathname(path, buffer, fno);
+ if (!stat(buffer, &s)) {
+ if (unlink(buffer)) { SERR(buffer); }
+ } else {
+ break;
+ }
+ }
+ return GRN_SUCCESS;
+ }
+}
+
+grn_rc
+grn_io_rename(grn_ctx *ctx, const char *old_name, const char *new_name)
+{
+ struct stat s;
+ if (stat(old_name, &s)) {
+ SERR("stat");
+ return ctx->rc;
+ } else if (rename(old_name, new_name)) {
+ SERR(old_name);
+ return ctx->rc;
+ } else {
+ int fno;
+ char old_buffer[PATH_MAX];
+ char new_buffer[PATH_MAX];
+ for (fno = 1; ; fno++) {
+ gen_pathname(old_name, old_buffer, fno);
+ if (!stat(old_buffer, &s)) {
+ gen_pathname(new_name, new_buffer, fno);
+ if (rename(old_buffer, new_buffer)) { SERR(old_buffer); }
+ } else {
+ SERR("stat");
+ return ctx->rc;
+ }
+ }
+ return GRN_SUCCESS;
+ }
+}
+
+typedef struct {
+ grn_io_ja_ehead head;
+ char body[256];
+} ja_element;
+
+grn_rc
+grn_io_read_ja(grn_io *io, grn_ctx *ctx, grn_io_ja_einfo *einfo, uint32_t epos, uint32_t key,
+ uint32_t segment, uint32_t offset, void **value, uint32_t *value_len)
+{
+ uint32_t rest = 0, size = *value_len + sizeof(grn_io_ja_ehead);
+ uint32_t segment_size = io->header->segment_size;
+ uint32_t segments_per_file = GRN_IO_FILE_SIZE / segment_size;
+ uint32_t bseg = segment + io->base_seg;
+ int fno = bseg / segments_per_file;
+ fileinfo *fi = &io->fis[fno];
+ off_t base = fno ? 0 : io->base - (uint64_t)segment_size * io->base_seg;
+ off_t pos = (uint64_t)segment_size * (bseg % segments_per_file) + offset + base;
+ ja_element *v = GRN_MALLOC(size);
+ if (!v) {
+ *value = NULL;
+ *value_len = 0;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ if (pos + size > GRN_IO_FILE_SIZE) {
+ rest = pos + size - GRN_IO_FILE_SIZE;
+ size = GRN_IO_FILE_SIZE - pos;
+ }
+ if (!grn_opened(fi)) {
+ char path[PATH_MAX];
+ gen_pathname(io->path, path, fno);
+ if (grn_open(ctx, fi, path, O_RDWR|O_CREAT, GRN_IO_FILE_SIZE)) {
+ *value = NULL;
+ *value_len = 0;
+ GRN_FREE(v);
+ return ctx->rc;
+ }
+ }
+ if (grn_pread(ctx, fi, v, size, pos)) {
+ *value = NULL;
+ *value_len = 0;
+ GRN_FREE(v);
+ return ctx->rc;
+ }
+ if (einfo->pos != epos) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "einfo pos changed %x => %x", einfo->pos, epos);
+ *value = NULL;
+ *value_len = 0;
+ GRN_FREE(v);
+ return GRN_FILE_CORRUPT;
+ }
+ if (einfo->size != *value_len) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "einfo size changed %d => %d", einfo->size, *value_len);
+ *value = NULL;
+ *value_len = 0;
+ GRN_FREE(v);
+ return GRN_FILE_CORRUPT;
+ }
+ if (v->head.key != key) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "ehead key unmatch %x => %x", key, v->head.key);
+ *value = NULL;
+ *value_len = 0;
+ GRN_FREE(v);
+ return GRN_INVALID_FORMAT;
+ }
+ if (v->head.size != *value_len) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "ehead size unmatch %d => %d", *value_len, v->head.size);
+ *value = NULL;
+ *value_len = 0;
+ GRN_FREE(v);
+ return GRN_INVALID_FORMAT;
+ }
+ if (rest) {
+ byte *vr = (byte *)v + size;
+ do {
+ fi = &io->fis[++fno];
+ if (!grn_opened(fi)) {
+ char path[PATH_MAX];
+ gen_pathname(io->path, path, fno);
+ if (grn_open(ctx, fi, path, O_RDWR|O_CREAT, GRN_IO_FILE_SIZE)) {
+ *value = NULL;
+ *value_len = 0;
+ GRN_FREE(v);
+ return ctx->rc;
+ }
+ }
+ size = rest > GRN_IO_FILE_SIZE ? GRN_IO_FILE_SIZE : rest;
+ if (grn_pread(ctx, fi, vr, size, 0)) {
+ *value = NULL;
+ *value_len = 0;
+ GRN_FREE(v);
+ return ctx->rc;
+ }
+ vr += size;
+ rest -= size;
+ } while (rest);
+ }
+ *value = v->body;
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_io_write_ja(grn_io *io, grn_ctx *ctx, uint32_t key,
+ uint32_t segment, uint32_t offset, void *value, uint32_t value_len)
+{
+ grn_rc rc;
+ uint32_t rest = 0, size = value_len + sizeof(grn_io_ja_ehead);
+ uint32_t segment_size = io->header->segment_size;
+ uint32_t segments_per_file = GRN_IO_FILE_SIZE / segment_size;
+ uint32_t bseg = segment + io->base_seg;
+ int fno = bseg / segments_per_file;
+ fileinfo *fi = &io->fis[fno];
+ off_t base = fno ? 0 : io->base - (uint64_t)segment_size * io->base_seg;
+ off_t pos = (uint64_t)segment_size * (bseg % segments_per_file) + offset + base;
+ if (pos + size > GRN_IO_FILE_SIZE) {
+ rest = pos + size - GRN_IO_FILE_SIZE;
+ size = GRN_IO_FILE_SIZE - pos;
+ }
+ if (!grn_opened(fi)) {
+ char path[PATH_MAX];
+ gen_pathname(io->path, path, fno);
+ if ((rc = grn_open(ctx, fi, path, O_RDWR|O_CREAT, GRN_IO_FILE_SIZE))) { return rc; }
+ }
+ if (value_len <= 256) {
+ ja_element je;
+ je.head.size = value_len;
+ je.head.key = key;
+ memcpy(je.body, value, value_len);
+ rc = grn_pwrite(ctx, fi, &je, size, pos);
+ } else {
+ grn_io_ja_ehead eh;
+ eh.size = value_len;
+ eh.key = key;
+ if ((rc = grn_pwrite(ctx, fi, &eh, sizeof(grn_io_ja_ehead), pos))) { return rc; }
+ pos += sizeof(grn_io_ja_ehead);
+ rc = grn_pwrite(ctx, fi, value, size - sizeof(grn_io_ja_ehead), pos);
+ }
+ if (rc) { return rc; }
+ if (rest) {
+ byte *vr = (byte *)value + size - sizeof(grn_io_ja_ehead);
+ do {
+ fi = &io->fis[++fno];
+ if (!grn_opened(fi)) {
+ char path[PATH_MAX];
+ gen_pathname(io->path, path, fno);
+ if ((rc = grn_open(ctx, fi, path, O_RDWR|O_CREAT, GRN_IO_FILE_SIZE))) { return rc; }
+ }
+ size = rest > GRN_IO_FILE_SIZE ? GRN_IO_FILE_SIZE : rest;
+ if ((rc = grn_pwrite(ctx, fi, vr, size, 0))) { return rc; }
+ vr += size;
+ rest -= size;
+ } while (rest);
+ }
+ return rc;
+}
+
+grn_rc
+grn_io_write_ja_ehead(grn_io *io, grn_ctx *ctx, uint32_t key,
+ uint32_t segment, uint32_t offset, uint32_t value_len)
+{
+ grn_rc rc;
+ uint32_t segment_size = io->header->segment_size;
+ uint32_t segments_per_file = GRN_IO_FILE_SIZE / segment_size;
+ uint32_t bseg = segment + io->base_seg;
+ int fno = bseg / segments_per_file;
+ fileinfo *fi = &io->fis[fno];
+ off_t base = fno ? 0 : io->base - (uint64_t)segment_size + io->base_seg;
+ off_t pos = (uint64_t)segment_size * (bseg % segments_per_file) + offset + base;
+ if (!grn_opened(fi)) {
+ char path[PATH_MAX];
+ gen_pathname(io->path, path, fno);
+ if ((rc = grn_open(ctx, fi, path, O_RDWR|O_CREAT, GRN_IO_FILE_SIZE))) { return rc; }
+ }
+ {
+ grn_io_ja_ehead eh;
+ eh.size = value_len;
+ eh.key = key;
+ return grn_pwrite(ctx, fi, &eh, sizeof(grn_io_ja_ehead), pos);
+ }
+}
+
+void *
+grn_io_win_map(grn_io *io, grn_ctx *ctx, grn_io_win *iw, uint32_t segment,
+ uint32_t offset, uint32_t size, grn_io_rw_mode mode)
+{
+ byte *p;
+ off_t pos, base;
+ int fno;
+ uint32_t nseg, bseg;
+ uint32_t segment_size = io->header->segment_size;
+ uint32_t segments_per_file = GRN_IO_FILE_SIZE / segment_size;
+ iw->ctx = ctx;
+ iw->diff = 0;
+ if (offset >= segment_size) {
+ segment += offset / segment_size;
+ offset = offset % segment_size;
+ }
+ nseg = (offset + size + segment_size - 1) / segment_size;
+ bseg = segment + io->base_seg;
+ fno = bseg / segments_per_file;
+ base = fno ? 0 : io->base - (uint64_t)segment_size * io->base_seg;
+ pos = (uint64_t)segment_size * (bseg % segments_per_file) + offset + base;
+ if (!size || !io || segment + nseg > io->header->max_segment ||
+ fno != (bseg + nseg - 1) / segments_per_file) {
+ return NULL;
+ }
+ switch (mode) {
+ case grn_io_rdonly:
+ {
+ fileinfo *fi = &io->fis[fno];
+ if (!grn_opened(fi)) {
+ char path[PATH_MAX];
+ gen_pathname(io->path, path, fno);
+ if (grn_open(ctx, fi, path, O_RDWR|O_CREAT, GRN_IO_FILE_SIZE)) {
+ return NULL;
+ }
+ }
+ if (!(p = GRN_MALLOC(size))) { return NULL; }
+ if (grn_pread(ctx, fi, p, size, pos)) {
+ GRN_FREE(p);
+ return NULL;
+ }
+ iw->addr = p;
+ }
+ break;
+ case grn_io_rdwr:
+ // if (nseg > 1) { /* auto unmap is not implemented yet */
+ if (nseg > 0) {
+ fileinfo *fi = &io->fis[fno];
+ if (!grn_opened(fi)) {
+ char path[PATH_MAX];
+ gen_pathname(io->path, path, fno);
+ if (grn_open(ctx, fi, path, O_RDWR|O_CREAT, GRN_IO_FILE_SIZE)) {
+ return NULL;
+ }
+ }
+ if (!(p = GRN_MMAP(&grn_gctx, &iw->fmo, fi, pos, (uint64_t)segment_size * nseg))) {
+ return NULL;
+ }
+ {
+ uint64_t tail = io->base + (uint64_t)segment_size * segment + offset + size;
+ if (tail > io->header->curr_size) { io->header->curr_size = tail; }
+ }
+ } else {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "nseg == 0! in grn_io_win_map(%p, %u, %u, %u)", io, segment, offset, size);
+ // GRN_IO_SEG_REF(io, segment, p); if (!p) { return NULL; }
+ return NULL;
+ }
+ iw->addr = p + offset;
+ break;
+ case grn_io_wronly:
+ if (!(p = GRN_MALLOC(size))) { return NULL; }
+ memset(p, 0, size);
+ iw->cached = 0;
+ iw->addr = p;
+ break;
+ default :
+ return NULL;
+ }
+ iw->io = io;
+ iw->mode = mode;
+ iw->segment = segment;
+ iw->offset = offset;
+ iw->nseg = nseg;
+ iw->size = size;
+ iw->pos = pos;
+ return iw->addr;
+}
+
+grn_rc
+grn_io_win_unmap(grn_io_win *iw)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_io *io = iw->io;
+ grn_ctx *ctx = iw->ctx;
+ uint32_t segment_size = io->header->segment_size;
+ uint32_t segments_per_file = GRN_IO_FILE_SIZE / segment_size;
+ int nseg = iw->nseg;
+ switch (iw->mode) {
+ case grn_io_rdonly:
+ if (iw->addr) { GRN_FREE(iw->addr); }
+ iw->addr = NULL;
+ break;
+ case grn_io_rdwr:
+ // if (nseg > 1) { /* auto unmap is not implemented yet */
+ if (nseg > 0) {
+ GRN_MUNMAP(&grn_gctx, &iw->fmo, ((byte *)iw->addr) - iw->offset, (uint64_t)segment_size * nseg);
+ } else {
+ if (iw->segment >= io->header->max_segment) {
+ rc = GRN_INVALID_ARGUMENT;
+ } else {
+ //GRN_IO_SEG_UNREF(io, iw->segment);
+ }
+ }
+ iw->addr = NULL;
+ break;
+ case grn_io_wronly:
+ {
+ int fno = (iw->segment + io->base_seg) / segments_per_file;
+ fileinfo *fi = &io->fis[fno];
+ if (!grn_opened(fi)) {
+ char path[PATH_MAX];
+ gen_pathname(io->path, path, fno);
+ rc = grn_open(ctx, fi, path, O_RDWR|O_CREAT, GRN_IO_FILE_SIZE);
+ }
+ if (!rc) {
+ if (!(rc = grn_pwrite(ctx, fi, iw->addr, iw->size, iw->pos))) {
+ {
+ uint64_t tail = io->base + (uint64_t)segment_size * iw->segment + iw->offset + iw->size;
+ if (tail > io->header->curr_size) { io->header->curr_size = tail; }
+ }
+ if (!iw->cached) { GRN_FREE(iw->addr); }
+ iw->addr = NULL;
+ }
+ }
+ }
+ break;
+ default :
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ return rc;
+}
+
+void *
+grn_io_win_map2(grn_io *io, grn_ctx *ctx, grn_io_win *iw, uint32_t segment,
+ uint32_t offset, uint32_t size, grn_io_rw_mode mode)
+{
+ uint32_t nseg, segment_size = io->header->segment_size;
+ if (offset >= segment_size) {
+ segment += offset / segment_size;
+ offset = offset % segment_size;
+ }
+ nseg = (offset + size + segment_size - 1) / segment_size;
+ if (!size || !ctx || segment + nseg > io->header->max_segment) { return NULL; }
+ iw->ctx = ctx;
+ iw->diff = 0;
+ iw->io = io;
+ iw->mode = mode;
+ iw->tiny_p = 0;
+ iw->segment = segment;
+ iw->offset = offset;
+ iw->nseg = nseg;
+ iw->size = size;
+ if (nseg == 1) {
+ byte *addr = NULL;
+ GRN_IO_SEG_REF(io, segment, addr);
+ if (!addr) { return NULL; }
+ iw->cached = 1;
+ iw->addr = addr + offset;
+ } else {
+ if (!(iw->addr = GRN_MALLOC(size))) { return NULL; }
+ iw->cached = 0;
+ switch (mode) {
+ case grn_io_rdonly:
+ case grn_io_rdwr:
+ {
+ byte *p, *q = NULL;
+ uint32_t s, r;
+ for (p = iw->addr, r = size; r; p += s, r -= s, segment++, offset = 0) {
+ GRN_IO_SEG_REF(io, segment, q);
+ if (!q) {
+ GRN_FREE(iw->addr);
+ return NULL;
+ }
+ s = (offset + r > segment_size) ? segment_size - offset : r;
+ memcpy(p, q + offset, s);
+ GRN_IO_SEG_UNREF(io, segment);
+ }
+ }
+ break;
+ case grn_io_wronly:
+ break;
+ default :
+ return NULL;
+ }
+ }
+ return iw->addr;
+}
+
+grn_rc
+grn_io_win_unmap2(grn_io_win *iw)
+{
+ if (!iw || !iw->io ||!iw->ctx) { return GRN_INVALID_ARGUMENT; }
+ if (iw->cached) {
+ if (!iw->tiny_p) { GRN_IO_SEG_UNREF(iw->io, iw->segment); }
+ return GRN_SUCCESS;
+ }
+ {
+ grn_io *io = iw->io;
+ grn_ctx *ctx = iw->ctx;
+ switch (iw->mode) {
+ case grn_io_rdonly:
+ if (!iw->addr) { return GRN_INVALID_ARGUMENT; }
+ GRN_FREE(iw->addr);
+ return GRN_SUCCESS;
+ case grn_io_rdwr:
+ case grn_io_wronly:
+ {
+ byte *p, *q = NULL;
+ uint32_t segment_size = io->header->segment_size;
+ uint32_t s, r, offset = iw->offset, segment = iw->segment;
+ for (p = iw->addr, r = iw->size; r; p += s, r -= s, segment++, offset = 0) {
+ GRN_IO_SEG_REF(io, segment, q);
+ if (!q) { return GRN_NO_MEMORY_AVAILABLE; }
+ s = (offset + r > segment_size) ? segment_size - offset : r;
+ memcpy(q + offset, p, s);
+ GRN_IO_SEG_UNREF(io, segment);
+ }
+ }
+ GRN_FREE(iw->addr);
+ return GRN_SUCCESS;
+ default :
+ return GRN_INVALID_ARGUMENT;
+ }
+ }
+}
+
+#define DO_MAP(io,fmo,fi,pos,size,segno,res) do {\
+ if (((res) = GRN_MMAP(&grn_gctx, (fmo), (fi), (pos), (size)))) {\
+ uint32_t nmaps;\
+ if (io->max_map_seg < segno) { io->max_map_seg = segno; }\
+ GRN_ATOMIC_ADD_EX(&io->nmaps, 1, nmaps);\
+ {\
+ uint64_t tail = io->base + (uint64_t)(size) * ((segno) + 1);\
+ if (tail > io->header->curr_size) { io->header->curr_size = tail; }\
+ }\
+ }\
+} while (0)
+
+#define SEG_MAP(io,segno,info) do {\
+ uint32_t segment_size = io->header->segment_size;\
+ if ((io->flags & GRN_IO_TEMPORARY)) {\
+ DO_MAP(io, &info->fmo, NULL, 0, segment_size, segno, info->map);\
+ } else {\
+ uint32_t segments_per_file = GRN_IO_FILE_SIZE / segment_size;\
+ uint32_t bseg = segno + io->base_seg;\
+ uint32_t fno = bseg / segments_per_file;\
+ off_t base = fno ? 0 : io->base - (uint64_t)segment_size * io->base_seg;\
+ off_t pos = (uint64_t)segment_size * (bseg % segments_per_file) + base;\
+ fileinfo *fi = &io->fis[fno];\
+ if (!grn_opened(fi)) {\
+ char path[PATH_MAX];\
+ gen_pathname(io->path, path, fno);\
+ if (!grn_open(ctx, fi, path, O_RDWR|O_CREAT, GRN_IO_FILE_SIZE)) { \
+ DO_MAP(io, &info->fmo, fi, pos, segment_size, segno, info->map);\
+ }\
+ } else {\
+ DO_MAP(io, &info->fmo, fi, pos, segment_size, segno, info->map);\
+ }\
+ }\
+} while (0)
+
+void
+grn_io_seg_map_(grn_ctx *ctx, grn_io *io, uint32_t segno, grn_io_mapinfo *info)
+{
+ SEG_MAP(io, segno, info);
+}
+
+grn_rc
+grn_io_seg_expire(grn_ctx *ctx, grn_io *io, uint32_t segno, uint32_t nretry)
+{
+ uint32_t retry, *pnref;
+ grn_io_mapinfo *info;
+ if (!io->maps || segno >= io->header->max_segment) { return GRN_INVALID_ARGUMENT; }
+ info = &io->maps[segno];
+ if (!info->map) { return GRN_INVALID_ARGUMENT; }
+ pnref = &info->nref;
+ for (retry = 0;; retry++) {
+ uint32_t nref;
+ GRN_ATOMIC_ADD_EX(pnref, 1, nref);
+ if (nref) {
+ GRN_ATOMIC_ADD_EX(pnref, -1, nref);
+ if (retry >= GRN_IO_MAX_RETRY) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "deadlock detected! in grn_io_seg_expire(%p, %u, %u)", io, segno, nref);
+ return GRN_RESOURCE_DEADLOCK_AVOIDED;
+ }
+ } else {
+ GRN_ATOMIC_ADD_EX(pnref, GRN_IO_MAX_REF, nref);
+ if (nref > 1) {
+ GRN_ATOMIC_ADD_EX(pnref, -(GRN_IO_MAX_REF + 1), nref);
+ GRN_FUTEX_WAKE(pnref);
+ if (retry >= GRN_IO_MAX_RETRY) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "deadlock detected!! in grn_io_seg_expire(%p, %u, %u)", io,
+ segno, nref);
+ return GRN_RESOURCE_DEADLOCK_AVOIDED;
+ }
+ } else {
+ uint32_t nmaps;
+ GRN_MUNMAP(&grn_gctx, &info->fmo, info->map, io->header->segment_size);
+ info->map = NULL;
+ GRN_ATOMIC_ADD_EX(pnref, -(GRN_IO_MAX_REF + 1), nref);
+ GRN_ATOMIC_ADD_EX(&io->nmaps, -1, nmaps);
+ GRN_FUTEX_WAKE(pnref);
+ return GRN_SUCCESS;
+ }
+ }
+ if (retry >= nretry) { return GRN_RESOURCE_DEADLOCK_AVOIDED; }
+ GRN_FUTEX_WAIT(pnref);
+ }
+}
+
+uint32_t
+grn_io_expire(grn_ctx *ctx, grn_io *io, int count_thresh, uint32_t limit)
+{
+ uint32_t m, n = 0, ln = io->nmaps;
+ switch ((io->flags & (GRN_IO_EXPIRE_GTICK|GRN_IO_EXPIRE_SEGMENT))) {
+ case GRN_IO_EXPIRE_GTICK :
+ {
+ uint32_t nref, nmaps, *pnref = &io->nref;
+ GRN_ATOMIC_ADD_EX(pnref, 1, nref);
+ if (!nref && grn_gtick - io->count > count_thresh) {
+ uint32_t i = io->header->n_arrays;
+ grn_io_mapinfo *info = io->maps;
+ grn_io_array_spec *array_specs = (grn_io_array_spec *)io->user_header;
+ while (i--) {
+ memset(io->ainfo[i].addrs, 0, sizeof(void *) * array_specs[i].max_n_segments);
+ }
+ for (m = io->max_map_seg; m; info++, m--) {
+ if (info->map) {
+ GRN_MUNMAP(&grn_gctx, &info->fmo, info->map, io->header->segment_size);
+ info->map = NULL;
+ info->nref = 0;
+ info->count = grn_gtick;
+ GRN_ATOMIC_ADD_EX(&io->nmaps, -1, nmaps);
+ n++;
+ }
+ }
+ }
+ GRN_ATOMIC_ADD_EX(pnref, -1, nref);
+ }
+ break;
+ case GRN_IO_EXPIRE_SEGMENT :
+ for (m = io->max_map_seg; n < limit && m; m--) {
+ if (!grn_io_seg_expire(ctx, io, m, 0)) { n++; }
+ }
+ break;
+ case (GRN_IO_EXPIRE_GTICK|GRN_IO_EXPIRE_SEGMENT) :
+ {
+ grn_io_mapinfo *info = io->maps;
+ for (m = io->max_map_seg; n < limit && m; info++, m--) {
+ if (info->map && (grn_gtick - info->count) > count_thresh) {
+ uint32_t nmaps, nref, *pnref = &info->nref;
+ GRN_ATOMIC_ADD_EX(pnref, 1, nref);
+ if (!nref && info->map && (grn_gtick - info->count) > count_thresh) {
+ GRN_MUNMAP(&grn_gctx, &info->fmo, info->map, io->header->segment_size);
+ GRN_ATOMIC_ADD_EX(&io->nmaps, -1, nmaps);
+ info->map = NULL;
+ info->count = grn_gtick;
+ n++;
+ }
+ GRN_ATOMIC_ADD_EX(pnref, -1, nref);
+ }
+ }
+ }
+ break;
+ }
+ if (n) {
+ GRN_LOG(ctx, GRN_LOG_INFO, "<%p:%x> expired i=%p max=%d (%d/%d)",
+ ctx, grn_gtick, io, io->max_map_seg, n, ln);
+ }
+ return n;
+}
+
+static uint32_t
+grn_expire_(grn_ctx *ctx, int count_thresh, uint32_t limit)
+{
+ uint32_t n = 0;
+ grn_io *io;
+ GRN_HASH_EACH(ctx, grn_gctx.impl->ios, id, NULL, NULL, (void **)&io, {
+ grn_plugin_close(ctx, id);
+ n += grn_io_expire(ctx, io, count_thresh, limit);
+ if (n >= limit) { break; }
+ });
+ return n;
+}
+
+uint32_t
+grn_expire(grn_ctx *ctx, int count_thresh, uint32_t limit)
+{
+ grn_ctx *c;
+ uint32_t n = 0;
+ CRITICAL_SECTION_ENTER(grn_glock);
+ if (grn_gtick) {
+ for (c = grn_gctx.next;; c = ctx->next) {
+ if (c == &grn_gctx) {
+ CRITICAL_SECTION_LEAVE(grn_glock);
+ n = grn_expire_(ctx, count_thresh, limit);
+ CRITICAL_SECTION_ENTER(grn_glock);
+ break;
+ }
+ if ((c->seqno & 1) && (c->seqno == c->seqno2)) { break; }
+ }
+ }
+ grn_gtick++;
+ for (c = grn_gctx.next; c != &grn_gctx; c = ctx->next) { c->seqno2 = c->seqno; }
+ CRITICAL_SECTION_LEAVE(grn_glock);
+ return n;
+}
+
+void *
+grn_io_anon_map(grn_ctx *ctx, grn_io_mapinfo *mi, size_t length)
+{
+ return (mi->map = GRN_MMAP(ctx, &mi->fmo, NULL, 0, length));
+}
+
+void
+grn_io_anon_unmap(grn_ctx *ctx, grn_io_mapinfo *mi, size_t length)
+{
+ /* support WIN32 */
+#ifdef WIN32
+ return GRN_FREE(mi->map);
+#endif
+ GRN_MUNMAP(ctx, &mi->fmo, mi->map, length);
+}
+
+grn_rc
+grn_io_lock(grn_ctx *ctx, grn_io *io, int timeout)
+{
+ static int _ncalls = 0, _ncolls = 0;
+ uint32_t count, count_log_border = 1000;
+ _ncalls++;
+ if (!io) { return GRN_INVALID_ARGUMENT; }
+ for (count = 0;; count++) {
+ uint32_t lock;
+ GRN_ATOMIC_ADD_EX(io->lock, 1, lock);
+ if (lock) {
+ GRN_ATOMIC_ADD_EX(io->lock, -1, lock);
+ if (count == count_log_border) {
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "io(%s) collisions(%d/%d): lock failed %d times",
+ io->path, _ncolls, _ncalls, count_log_border);
+ }
+ if (!timeout || (timeout > 0 && timeout == count)) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "[DB Locked] time out(%d): io(%s) collisions(%d/%d)",
+ timeout, io->path, _ncolls, _ncalls);
+ break;
+ }
+ if (!(++_ncolls % 1000000) && (_ncolls > _ncalls)) {
+ if (_ncolls < 0 || _ncalls < 0) {
+ _ncolls = 0; _ncalls = 0;
+ } else {
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "io(%s) collisions(%d/%d)", io->path, _ncolls, _ncalls);
+ }
+ }
+ grn_nanosleep(GRN_LOCK_WAIT_TIME_NANOSECOND);
+ continue;
+ }
+ return GRN_SUCCESS;
+ }
+ ERR(GRN_RESOURCE_DEADLOCK_AVOIDED, "grn_io_lock failed");
+ return ctx->rc;
+}
+
+void
+grn_io_unlock(grn_io *io)
+{
+ if (io) {
+ uint32_t lock;
+ GRN_ATOMIC_ADD_EX(io->lock, -1, lock);
+ }
+}
+
+void
+grn_io_clear_lock(grn_io *io)
+{
+ if (io) { *io->lock = 0; }
+}
+
+uint32_t
+grn_io_is_locked(grn_io *io)
+{
+ return io ? *io->lock : 0;
+}
+
+/** mmap abstraction **/
+
+static size_t mmap_size = 0;
+
+#ifdef WIN32
+
+#ifdef WIN32_FMO_EACH
+
+inline static grn_rc
+grn_open(grn_ctx *ctx, fileinfo *fi, const char *path, int flags, size_t maxsize)
+{
+ if ((flags & O_CREAT)) {
+ DWORD dwCreationDisposition;
+ if (flags & O_EXCL) {
+ dwCreationDisposition = CREATE_NEW;
+ } else {
+ dwCreationDisposition = OPEN_ALWAYS;
+ }
+ fi->fh = CreateFile(path, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, 0);
+ if (fi->fh == INVALID_HANDLE_VALUE) {
+ SERR("CreateFile");
+ return ctx->rc;
+ }
+ goto exit;
+ }
+ if ((flags & O_TRUNC)) {
+ CloseHandle(fi->fh);
+ fi->fh = CreateFile(path, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if (fi->fh == INVALID_HANDLE_VALUE) {
+ SERR("CreateFile");
+ return ctx->rc;
+ }
+ goto exit;
+ }
+ /* O_RDWR only */
+ fi->fh = CreateFile(path, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if (fi->fh == INVALID_HANDLE_VALUE) {
+ SERR("CreateFile");
+ return ctx->rc;
+ }
+exit:
+ CRITICAL_SECTION_INIT(fi->cs);
+ return GRN_SUCCESS;
+}
+
+inline static void *
+grn_mmap(grn_ctx *ctx, HANDLE *fmo, fileinfo *fi, off_t offset, size_t length)
+{
+ void *res;
+ if (!fi) {
+ if(fmo) {
+ *fmo = (HANDLE)0;
+ }
+ return GRN_GCALLOC(length);
+ }
+ /* CRITICAL_SECTION_ENTER(fi->cs); */
+ /* try to create fmo */
+ *fmo = CreateFileMapping(fi->fh, NULL, PAGE_READWRITE, 0, offset + length, NULL);
+ if (!*fmo) { return NULL; }
+ res = MapViewOfFile(*fmo, FILE_MAP_WRITE, 0, (DWORD)offset, (SIZE_T)length);
+ if (!res) {
+ MERR("MapViewOfFile failed #%d <%zu>", GetLastError(), mmap_size);
+ return NULL;
+ }
+ /* CRITICAL_SECTION_LEAVE(fi->cs); */
+ mmap_size += length;
+ return res;
+}
+
+inline static int
+grn_munmap(grn_ctx *ctx, HANDLE *fmo, void *start, size_t length)
+{
+ int r = 0;
+ if (!fmo) {
+ GRN_FREE(start);
+ }
+ if (*fmo) {
+ if (UnmapViewOfFile(start)) {
+ mmap_size -= length;
+ } else {
+ SERR("UnmapViewOfFile");
+ GRN_LOG(ctx, GRN_LOG_ERROR, "UnmapViewOfFile(%p,%d) failed <%zu>", start, length, mmap_size);
+ r = -1;
+ }
+ if (!CloseHandle(*fmo)) {
+ SERR("CloseHandle");
+ GRN_LOG(ctx, GRN_LOG_ERROR, "CloseHandle(%p,%d) failed <%zu>", start, length, mmap_size);
+ }
+ *fmo = NULL;
+ } else {
+ GRN_FREE(start);
+ }
+ return r;
+}
+
+inline static grn_rc
+grn_close(grn_ctx *ctx, fileinfo *fi)
+{
+ if (fi->fmo != NULL) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "file mapping object exists");
+ }
+ if (fi->fh != INVALID_HANDLE_VALUE) {
+ CloseHandle(fi->fh);
+ CRITICAL_SECTION_FIN(fi->cs);
+ fi->fh = INVALID_HANDLE_VALUE;
+ }
+ return GRN_SUCCESS;
+}
+
+#else /* WIN32_FMO_EACH */
+inline static grn_rc
+grn_open(grn_ctx *ctx, fileinfo *fi, const char *path, int flags, size_t maxsize)
+{
+ /* may be wrong if flags is just only O_RDWR */
+ if ((flags & O_CREAT)) {
+ DWORD dwCreationDisposition;
+ if (flags & O_EXCL) {
+ dwCreationDisposition = CREATE_NEW;
+ } else {
+ dwCreationDisposition = OPEN_ALWAYS;
+ }
+ fi->fh = CreateFile(path, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, 0);
+ if (fi->fh == INVALID_HANDLE_VALUE) {
+ SERR("CreateFile");
+ return ctx->rc;
+ }
+ goto exit;
+ }
+ if ((flags & O_TRUNC)) {
+ CloseHandle(fi->fh);
+ /* unable to assign OPEN_ALWAYS and TRUNCATE_EXISTING at once */
+ fi->fh = CreateFile(path, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if (fi->fh == INVALID_HANDLE_VALUE) {
+ SERR("CreateFile");
+ return ctx->rc;
+ }
+ goto exit;
+ }
+ /* O_RDWR only */
+ fi->fh = CreateFile(path, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if (fi->fh == INVALID_HANDLE_VALUE) {
+ SERR("CreateFile");
+ return ctx->rc;
+ }
+exit:
+ /* signature may be wrong.. */
+ fi->fmo = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, NULL);
+ /* open failed */
+ if (fi->fmo == NULL) {
+ // flock
+ /* retry to open */
+ fi->fmo = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, NULL);
+ /* failed again */
+ if (fi->fmo == NULL) {
+ /* try to create fmo */
+ fi->fmo = CreateFileMapping(fi->fh, NULL, PAGE_READWRITE, 0, GRN_IO_FILE_SIZE, NULL);
+ }
+ // funlock
+ }
+ if (fi->fmo != NULL) {
+ if (GetLastError() != ERROR_ALREADY_EXISTS ) {
+ CRITICAL_SECTION_INIT(fi->cs);
+ return GRN_SUCCESS;
+ } else {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "fmo object already exists! handle=%d", fi->fh);
+ CloseHandle(fi->fmo);
+ }
+ } else {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "failed to get FileMappingObject #%d", GetLastError());
+ }
+ CloseHandle(fi->fh);
+ SERR("OpenFileMapping");
+ return ctx->rc;
+}
+
+inline static void *
+grn_mmap(grn_ctx *ctx, fileinfo *fi, off_t offset, size_t length)
+{
+ void *res;
+ if (!fi) { return GRN_GCALLOC(length); }
+ /* file must be exceeded to GRN_IO_FILE_SIZE when FileMappingObject created.
+ and, after fmo created, it's not allowed to expand the size of file.
+ DWORD tail = (DWORD)(offset + length);
+ DWORD filesize = GetFileSize(fi->fh, NULL);
+ if (filesize < tail) {
+ if (SetFilePointer(fi->fh, tail, NULL, FILE_BEGIN) != tail) {
+ grn_log("SetFilePointer failed");
+ return NULL;
+ }
+ if (!SetEndOfFile(fi->fh)) {
+ grn_log("SetEndOfFile failed");
+ return NULL;
+ }
+ filesize = tail;
+ }
+ */
+ res = MapViewOfFile(fi->fmo, FILE_MAP_WRITE, 0, (DWORD)offset, (SIZE_T)length);
+ if (!res) {
+ MERR("MapViewOfFile failed #%d <%zu>", GetLastError(), mmap_size);
+ return NULL;
+ }
+ mmap_size += length;
+ return res;
+}
+
+inline static int
+grn_munmap(grn_ctx *ctx, void *start, size_t length)
+{
+ if (UnmapViewOfFile(start)) {
+ mmap_size -= length;
+ return 0;
+ } else {
+ SERR("UnmapViewOfFile");
+ GRN_LOG(ctx, GRN_LOG_ERROR, "UnmapViewOfFile(%p,%d) failed <%zu>", start, length, mmap_size);
+ return -1;
+ }
+}
+
+inline static grn_rc
+grn_close(grn_ctx *ctx, fileinfo *fi)
+{
+ if (fi->fmo != NULL) {
+ CloseHandle(fi->fmo);
+ fi->fmo = NULL;
+ }
+ if (fi->fh != INVALID_HANDLE_VALUE) {
+ CloseHandle(fi->fh);
+ CRITICAL_SECTION_FIN(fi->cs);
+ fi->fh = INVALID_HANDLE_VALUE;
+ }
+ return GRN_SUCCESS;
+}
+#endif /* WIN32_FMO_EACH */
+
+inline static void
+grn_fileinfo_init(fileinfo *fis, int nfis)
+{
+ for (; nfis--; fis++) {
+ fis->fh = INVALID_HANDLE_VALUE;
+ fis->fmo = NULL;
+ }
+}
+
+inline static int
+grn_opened(fileinfo *fi)
+{
+ return fi->fh != INVALID_HANDLE_VALUE;
+}
+
+inline static int
+grn_msync(grn_ctx *ctx, void *start, size_t length)
+{
+ /* return value may be wrong... */
+ return FlushViewOfFile(start, length);
+}
+
+inline static grn_rc
+grn_pread(grn_ctx *ctx, fileinfo *fi, void *buf, size_t count, off_t offset)
+{
+ DWORD r, len;
+ CRITICAL_SECTION_ENTER(fi->cs);
+ r = SetFilePointer(fi->fh, offset, NULL, FILE_BEGIN);
+ if (r == INVALID_SET_FILE_POINTER) {
+ SERR("SetFilePointer");
+ } else {
+ if (!ReadFile(fi->fh, buf, (DWORD)count, &len, NULL)) {
+ SERR("ReadFile");
+ } else if (len != count) {
+ /* todo : should retry ? */
+ ERR(GRN_INPUT_OUTPUT_ERROR, "ReadFile %d != %d", count, len);
+ }
+ }
+ CRITICAL_SECTION_LEAVE(fi->cs);
+ return ctx->rc;
+}
+
+inline static grn_rc
+grn_pwrite(grn_ctx *ctx, fileinfo *fi, void *buf, size_t count, off_t offset)
+{
+ DWORD r, len;
+ CRITICAL_SECTION_ENTER(fi->cs);
+ r = SetFilePointer(fi->fh, offset, NULL, FILE_BEGIN);
+ if (r == INVALID_SET_FILE_POINTER) {
+ SERR("SetFilePointer");
+ } else {
+ if (!WriteFile(fi->fh, buf, (DWORD)count, &len, NULL)) {
+ SERR("WriteFile");
+ } else if (len != count) {
+ /* todo : should retry ? */
+ ERR(GRN_INPUT_OUTPUT_ERROR, "WriteFile %d != %d", count, len);
+ }
+ }
+ CRITICAL_SECTION_LEAVE(fi->cs);
+ return ctx->rc;
+}
+
+#else /* WIN32 */
+
+inline static grn_rc
+grn_open(grn_ctx *ctx, fileinfo *fi, const char *path, int flags, size_t maxsize)
+{
+ struct stat st;
+ if ((fi->fd = GRN_OPEN(path, flags, 0666)) == -1) {
+ SERR(path);
+ return ctx->rc;
+ }
+ if (fstat(fi->fd, &st) == -1) {
+ SERR(path);
+ return ctx->rc;
+ }
+ fi->dev = st.st_dev;
+ fi->inode = st.st_ino;
+ return GRN_SUCCESS;
+}
+
+inline static void
+grn_fileinfo_init(fileinfo *fis, int nfis)
+{
+ for (; nfis--; fis++) { fis->fd = -1; }
+}
+
+inline static int
+grn_opened(fileinfo *fi)
+{
+ return fi->fd != -1;
+}
+
+inline static grn_rc
+grn_close(grn_ctx *ctx, fileinfo *fi)
+{
+ if (fi->fd != -1) {
+ if (GRN_CLOSE(fi->fd) == -1) {
+ SERR("close");
+ return ctx->rc;
+ }
+ fi->fd = -1;
+ }
+ return GRN_SUCCESS;
+}
+
+#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#include <sys/mman.h>
+
+inline static void *
+grn_mmap(grn_ctx *ctx, fileinfo *fi, off_t offset, size_t length)
+{
+ void *res;
+ int fd, flags;
+ if (fi) {
+ struct stat s;
+ off_t tail = offset + length;
+ fd = fi->fd;
+ if ((fstat(fd, &s) == -1) || (s.st_size < tail && ftruncate(fd, tail) == -1)) {
+ SERR("fstat");
+ return NULL;
+ }
+ flags = MAP_SHARED;
+ } else {
+ fd = -1;
+ flags = MAP_PRIVATE|MAP_ANONYMOUS;
+ }
+ res = mmap(NULL, length, PROT_READ|PROT_WRITE, flags, fd, offset);
+ if (MAP_FAILED == res) {
+ MERR("mmap(%" GRN_FMT_LLU ",%d,%" GRN_FMT_LLD ")=%s <%" GRN_FMT_LLU ">",
+ (unsigned long long int)length, fd, (long long int)offset, strerror(errno),
+ (unsigned long long int)mmap_size);
+ return NULL;
+ }
+ mmap_size += length;
+ return res;
+}
+
+#ifdef USE_FAIL_MALLOC
+inline static void *
+grn_fail_mmap(grn_ctx *ctx, fileinfo *fi, off_t offset, size_t length,
+ const char* file, int line, const char *func)
+{
+ if (grn_fail_malloc_check(length, file, line, func)) {
+ return grn_mmap(ctx, fi, offset, length);
+ } else {
+ MERR("fail_mmap(%zu,%d,%" GRN_FMT_LLU ") (%s:%d@%s) <%zu>",
+ length, fi ? fi->fd : 0, offset, file, line, func, mmap_size);
+ return NULL;
+ }
+}
+#endif /* USE_FAIL_MALLOC */
+
+inline static int
+grn_msync(grn_ctx *ctx, void *start, size_t length)
+{
+ int r = msync(start, length, MS_SYNC);
+ if (r == -1) { SERR("msync"); }
+ return r;
+}
+
+inline static int
+grn_munmap(grn_ctx *ctx, void *start, size_t length)
+{
+ int res;
+ res = munmap(start, length);
+ if (res) {
+ SERR("munmap");
+ GRN_LOG(ctx, GRN_LOG_ERROR, "munmap(%p,%" GRN_FMT_LLU ") failed <%" GRN_FMT_LLU ">",
+ start, (unsigned long long int)length, (unsigned long long int)mmap_size);
+ } else {
+ mmap_size -= length;
+ }
+ return res;
+}
+
+inline static grn_rc
+grn_pread(grn_ctx *ctx, fileinfo *fi, void *buf, size_t count, off_t offset)
+{
+ ssize_t r = pread(fi->fd, buf, count, offset);
+ if (r != count) {
+ if (r == -1) {
+ SERR("pread");
+ } else {
+ /* todo : should retry ? */
+ ERR(GRN_INPUT_OUTPUT_ERROR, "pread returned %" GRN_FMT_LLD " != %" GRN_FMT_LLU,
+ (long long int)r, (unsigned long long int)count);
+ }
+ return ctx->rc;
+ }
+ return GRN_SUCCESS;
+}
+
+inline static grn_rc
+grn_pwrite(grn_ctx *ctx, fileinfo *fi, void *buf, size_t count, off_t offset)
+{
+ ssize_t r = pwrite(fi->fd, buf, count, offset);
+ if (r != count) {
+ if (r == -1) {
+ SERR("pwrite");
+ } else {
+ /* todo : should retry ? */
+ ERR(GRN_INPUT_OUTPUT_ERROR, "pwrite returned %" GRN_FMT_LLD " != %" GRN_FMT_LLU,
+ (long long int)r, (unsigned long long int)count);
+ }
+ return ctx->rc;
+ }
+ return GRN_SUCCESS;
+}
+
+#endif /* WIN32 */
diff --git a/storage/mroonga/vendor/groonga/lib/io.h b/storage/mroonga/vendor/groonga/lib/io.h
new file mode 100644
index 00000000000..27538b381e2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/io.h
@@ -0,0 +1,494 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_IO_H
+#define GRN_IO_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifndef GRN_ERROR_H
+#include "error.h"
+#endif /* GRN_ERROR_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef WIN32
+#ifdef WIN32_FMO_EACH
+#define GRN_IO_FILE_SIZE 1073741824UL
+#else /* FMO_EACH */
+#define GRN_IO_FILE_SIZE 134217728L
+#endif /* FMO_EACH */
+#define GRN_IO_COPY grn_io_rdonly
+#define GRN_IO_UPDATE grn_io_wronly
+#else /* WIN32 */
+#define GRN_IO_FILE_SIZE 1073741824UL
+#define GRN_IO_COPY grn_io_rdwr
+#define GRN_IO_UPDATE grn_io_rdwr
+#endif /* WIN32 */
+
+typedef enum {
+ grn_io_rdonly,
+ grn_io_wronly,
+ grn_io_rdwr
+} grn_io_rw_mode;
+
+typedef enum {
+ grn_io_auto,
+ grn_io_manual
+} grn_io_mode;
+
+/**** grn_io ****/
+
+typedef struct _grn_io grn_io;
+
+typedef struct {
+ grn_io *io;
+ grn_ctx *ctx;
+ uint8_t mode;
+ uint8_t tiny_p;
+ uint32_t pseg;
+ uint32_t segment;
+ uint32_t offset;
+ uint32_t size;
+ uint32_t nseg;
+ off_t pos;
+ void *addr;
+ uint32_t diff;
+ int32_t cached;
+#if defined(WIN32) && defined(WIN32_FMO_EACH)
+ HANDLE fmo;
+#endif /* defined(WIN32) && defined(WIN32_FMO_EACH) */
+} grn_io_win;
+
+typedef struct {
+ void *map;
+ uint32_t nref;
+ uint32_t count;
+#if defined(WIN32) && defined(WIN32_FMO_EACH)
+ HANDLE fmo;
+#endif /* defined(WIN32) && defined(WIN32_FMO_EACH) */
+} grn_io_mapinfo;
+
+typedef struct _grn_io_array_info grn_io_array_info;
+
+struct _grn_io_header {
+ char idstr[16];
+ uint32_t type;
+ uint32_t version;
+ uint32_t flags;
+ uint32_t header_size;
+ uint32_t segment_size;
+ uint32_t max_segment;
+ uint32_t n_arrays;
+ uint32_t lock;
+ uint64_t curr_size;
+ uint32_t segment_tail;
+ uint32_t lastmod;
+};
+
+struct _grn_io {
+ char path[PATH_MAX];
+ struct _grn_io_header *header;
+ byte *user_header;
+ grn_io_mapinfo *maps;
+ uint32_t base;
+ uint32_t base_seg;
+ grn_io_mode mode;
+ struct _grn_io_fileinfo *fis;
+ grn_io_array_info *ainfo;
+ uint32_t max_map_seg;
+ uint32_t nmaps;
+ uint32_t nref;
+ uint32_t count;
+ uint8_t flags;
+ uint32_t *lock;
+};
+
+GRN_API grn_io *grn_io_create(grn_ctx *ctx, const char *path,
+ uint32_t header_size, uint32_t segment_size,
+ uint32_t max_segment, grn_io_mode mode,
+ unsigned int flags);
+grn_io *grn_io_open(grn_ctx *ctx, const char *path, grn_io_mode mode);
+GRN_API grn_rc grn_io_close(grn_ctx *ctx, grn_io *io);
+grn_rc grn_io_remove(grn_ctx *ctx, const char *path);
+grn_rc grn_io_size(grn_ctx *ctx, grn_io *io, uint64_t *size);
+grn_rc grn_io_rename(grn_ctx *ctx, const char *old_name, const char *new_name);
+GRN_API void *grn_io_header(grn_io *io);
+
+void *grn_io_win_map(grn_io *io, grn_ctx *ctx, grn_io_win *iw, uint32_t segment,
+ uint32_t offset, uint32_t size, grn_io_rw_mode mode);
+grn_rc grn_io_win_mapv(grn_io_win **list, grn_ctx *ctx, int nent);
+grn_rc grn_io_win_unmap(grn_io_win *iw);
+
+void *grn_io_win_map2(grn_io *io, grn_ctx *ctx, grn_io_win *iw, uint32_t segment,
+ uint32_t offset, uint32_t size, grn_io_rw_mode mode);
+grn_rc grn_io_win_unmap2(grn_io_win *iw);
+
+typedef struct _grn_io_ja_einfo grn_io_ja_einfo;
+typedef struct _grn_io_ja_ehead grn_io_ja_ehead;
+
+struct _grn_io_ja_einfo {
+ uint32_t pos;
+ uint32_t size;
+};
+
+struct _grn_io_ja_ehead {
+ uint32_t size;
+ uint32_t key;
+};
+
+grn_rc grn_io_read_ja(grn_io *io, grn_ctx *ctx, grn_io_ja_einfo *einfo, uint32_t epos,
+ uint32_t key, uint32_t segment, uint32_t offset,
+ void **value, uint32_t *value_len);
+grn_rc grn_io_write_ja(grn_io *io, grn_ctx *ctx,
+ uint32_t key, uint32_t segment, uint32_t offset,
+ void *value, uint32_t value_len);
+
+grn_rc grn_io_write_ja_ehead(grn_io *io, grn_ctx *ctx, uint32_t key,
+ uint32_t segment, uint32_t offset, uint32_t value_len);
+
+#define GRN_TABLE_ADD (0x01<<6)
+#define GRN_TABLE_ADDED (0x01<<7)
+
+#define GRN_IO_MAX_RETRY (0x10000)
+#define GRN_IO_MAX_REF (0x80000000)
+
+#define GRN_IO_EXPIRE_GTICK (0x01)
+#define GRN_IO_EXPIRE_SEGMENT (0x02)
+#define GRN_IO_TEMPORARY (0x04)
+
+void grn_io_seg_map_(grn_ctx *ctx, grn_io *io, uint32_t segno, grn_io_mapinfo *info);
+
+/* arguments must be validated by caller;
+ * io mustn't be NULL;
+ * segno must be in valid range;
+ * addr must be set NULL;
+ */
+#define GRN_IO_SEG_REF(io,segno,addr) do {\
+ grn_io_mapinfo *info = &(io)->maps[segno];\
+ uint32_t nref, retry, *pnref = &info->nref;\
+ if (io->flags & GRN_IO_EXPIRE_SEGMENT) {\
+ if (io->flags & GRN_IO_EXPIRE_GTICK) {\
+ for (retry = 0; !info->map || info->count != grn_gtick; retry++) {\
+ GRN_ATOMIC_ADD_EX(pnref, 1, nref);\
+ if (nref) {\
+ GRN_ATOMIC_ADD_EX(pnref, -1, nref);\
+ if (retry >= GRN_IO_MAX_RETRY) {\
+ GRN_LOG(ctx, GRN_LOG_CRIT, "deadlock detected! in GRN_IO_SEG_REF(%p, %u)", io, segno);\
+ break;\
+ }\
+ GRN_FUTEX_WAIT(pnref);\
+ } else {\
+ info->count = grn_gtick;\
+ if (!info->map) {\
+ grn_io_seg_map_(ctx, io, segno, info);\
+ if (!info->map) {\
+ GRN_LOG(ctx, GRN_LOG_CRIT,\
+ "mmap failed! in GRN_IO_SEG_REF(%p, %u): %s",\
+ io, segno, grn_current_error_message());\
+ }\
+ }\
+ GRN_ATOMIC_ADD_EX(pnref, -1, nref);\
+ GRN_FUTEX_WAKE(pnref);\
+ break;\
+ }\
+ }\
+ } else {\
+ for (retry = 0;; retry++) {\
+ GRN_ATOMIC_ADD_EX(pnref, 1, nref);\
+ if (nref >= GRN_IO_MAX_REF) {\
+ GRN_ATOMIC_ADD_EX(pnref, -1, nref);\
+ if (retry >= GRN_IO_MAX_RETRY) {\
+ GRN_LOG(ctx, GRN_LOG_CRIT, "deadlock detected!! in GRN_IO_SEG_REF(%p, %u, %u)", io, segno, nref);\
+ *pnref = 0; /* force reset */ \
+ break;\
+ }\
+ GRN_FUTEX_WAIT(pnref);\
+ continue;\
+ }\
+ if (nref >= 0x40000000) {\
+ ALERT("strange nref value!! in GRN_IO_SEG_REF(%p, %u, %u)", io, segno, nref); \
+ }\
+ if (!info->map) {\
+ if (nref) {\
+ GRN_ATOMIC_ADD_EX(pnref, -1, nref);\
+ if (retry >= GRN_IO_MAX_RETRY) {\
+ GRN_LOG(ctx, GRN_LOG_CRIT, "deadlock detected!!! in GRN_IO_SEG_REF(%p, %u, %u)", io, segno, nref);\
+ break;\
+ }\
+ GRN_FUTEX_WAIT(pnref);\
+ continue;\
+ } else {\
+ grn_io_seg_map_(ctx, io, segno, info);\
+ if (!info->map) {\
+ GRN_ATOMIC_ADD_EX(pnref, -1, nref);\
+ GRN_LOG(ctx, GRN_LOG_CRIT,\
+ "mmap failed!!! in GRN_IO_SEG_REF(%p, %u, %u): %s",\
+ io, segno, nref, grn_current_error_message());\
+ }\
+ \
+ GRN_FUTEX_WAKE(pnref);\
+ }\
+ }\
+ break;\
+ }\
+ info->count = grn_gtick;\
+ }\
+ } else {\
+ for (retry = 0; !info->map; retry++) {\
+ GRN_ATOMIC_ADD_EX(pnref, 1, nref);\
+ if (nref) {\
+ GRN_ATOMIC_ADD_EX(pnref, -1, nref);\
+ if (retry >= GRN_IO_MAX_RETRY) {\
+ GRN_LOG(ctx, GRN_LOG_CRIT, "deadlock detected!!!! in GRN_IO_SEG_REF(%p, %u)", io, segno);\
+ break;\
+ }\
+ GRN_FUTEX_WAIT(pnref);\
+ } else {\
+ if (!info->map) {\
+ grn_io_seg_map_(ctx, io, segno, info);\
+ if (!info->map) {\
+ GRN_LOG(ctx, GRN_LOG_CRIT,\
+ "mmap failed!!!! in GRN_IO_SEG_REF(%p, %u): %s",\
+ io, segno, grn_current_error_message());\
+ }\
+ }\
+ GRN_ATOMIC_ADD_EX(pnref, -1, nref);\
+ GRN_FUTEX_WAKE(pnref);\
+ break;\
+ }\
+ }\
+ info->count = grn_gtick;\
+ }\
+ addr = info->map;\
+} while (0)
+
+#define GRN_IO_SEG_UNREF(io,segno) do {\
+ if (GRN_IO_EXPIRE_SEGMENT ==\
+ (io->flags & (GRN_IO_EXPIRE_GTICK|GRN_IO_EXPIRE_SEGMENT))) {\
+ uint32_t nref, *pnref = &(io)->maps[segno].nref;\
+ GRN_ATOMIC_ADD_EX(pnref, -1, nref);\
+ }\
+} while (0)
+
+uint32_t grn_io_base_seg(grn_io *io);
+const char *grn_io_path(grn_io *io);
+
+typedef struct _grn_io_array_spec grn_io_array_spec;
+
+struct _grn_io_array_spec {
+ uint32_t w_of_element;
+ uint32_t max_n_segments;
+};
+
+struct _grn_io_array_info {
+ uint32_t w_of_elm_in_a_segment;
+ uint32_t elm_mask_in_a_segment;
+ uint32_t max_n_segments;
+ uint32_t element_size;
+ uint32_t *segments;
+ void **addrs;
+};
+
+grn_io *grn_io_create_with_array(grn_ctx *ctx, const char *path, uint32_t header_size,
+ uint32_t segment_size, grn_io_mode mode,
+ int n_arrays, grn_io_array_spec *array_specs);
+
+void *grn_io_array_at(grn_ctx *ctx, grn_io *io, uint32_t array, off_t offset, int *flags);
+
+void grn_io_segment_alloc(grn_ctx *ctx, grn_io *io, grn_io_array_info *ai,
+ uint32_t lseg, int *flags, void **p);
+
+GRN_API grn_rc grn_io_lock(grn_ctx *ctx, grn_io *io, int timeout);
+GRN_API void grn_io_unlock(grn_io *io);
+void grn_io_clear_lock(grn_io *io);
+uint32_t grn_io_is_locked(grn_io *io);
+
+#define GRN_IO_ARRAY_AT(io,array,offset,flags,res) do {\
+ grn_io_array_info *ainfo = &(io)->ainfo[array];\
+ uint32_t lseg = (offset) >> ainfo->w_of_elm_in_a_segment;\
+ void **p_ = &ainfo->addrs[lseg];\
+ if (!*p_) {\
+ grn_io_segment_alloc(ctx, (io), ainfo, lseg, (flags), p_);\
+ if (!*p_) { (res) = NULL; break; }\
+ }\
+ *((byte **)(&(res))) = (((byte *)*p_) + \
+ (((offset) & ainfo->elm_mask_in_a_segment) * ainfo->element_size));\
+} while (0)
+
+#define GRN_IO_ARRAY_BIT_AT(io,array,offset,res) do {\
+ uint8_t *ptr_;\
+ int flags_ = 0;\
+ GRN_IO_ARRAY_AT((io), (array), ((offset) >> 3) + 1, &flags_, ptr_);\
+ res = ptr_ ? ((*ptr_ >> ((offset) & 7)) & 1) : 0;\
+} while (0)
+
+#define GRN_IO_ARRAY_BIT_ON(io,array,offset) do {\
+ uint8_t *ptr_;\
+ int flags_ = GRN_TABLE_ADD;\
+ GRN_IO_ARRAY_AT((io), (array), ((offset) >> 3) + 1, &flags_, ptr_);\
+ if (ptr_) { *ptr_ |= (1 << ((offset) & 7)); }\
+} while (0)
+
+#define GRN_IO_ARRAY_BIT_OFF(io,array,offset) do {\
+ uint8_t *ptr_;\
+ int flags_ = GRN_TABLE_ADD;\
+ GRN_IO_ARRAY_AT((io), (array), ((offset) >> 3) + 1, &flags_, ptr_);\
+ if (ptr_) { *ptr_ &= ~(1 << ((offset) & 7)); }\
+} while (0)
+
+#define GRN_IO_ARRAY_BIT_FLIP(io,array,offset) do {\
+ uint8_t *ptr_;\
+ int flags_ = GRN_TABLE_ADD;\
+ GRN_IO_ARRAY_AT((io), (array), ((offset) >> 3) + 1, &flags_, ptr_);\
+ if (ptr_) { *ptr_ ^= (1 << ((offset) & 7)); }\
+} while (0)
+
+void *grn_io_anon_map(grn_ctx *ctx, grn_io_mapinfo *mi, size_t length);
+void grn_io_anon_unmap(grn_ctx *ctx, grn_io_mapinfo *mi, size_t length);
+uint32_t grn_io_detect_type(grn_ctx *ctx, const char *path);
+grn_rc grn_io_set_type(grn_io *io, uint32_t type);
+uint32_t grn_io_get_type(grn_io *io);
+
+grn_rc grn_io_init(void);
+grn_rc grn_io_fin(void);
+
+uint32_t grn_io_expire(grn_ctx *ctx, grn_io *io, int count_thresh, uint32_t limit);
+uint32_t grn_expire(grn_ctx *ctx, int count_thresh, uint32_t limit);
+
+/* encode/decode */
+
+#define GRN_B_ENC(v,p) do {\
+ uint8_t *_p = (uint8_t *)p; \
+ uint32_t _v = v; \
+ if (_v < 0x8f) { \
+ *_p++ = _v; \
+ } else if (_v < 0x408f) { \
+ _v -= 0x8f; \
+ *_p++ = 0xc0 + (_v >> 8); \
+ *_p++ = _v & 0xff; \
+ } else if (_v < 0x20408f) { \
+ _v -= 0x408f; \
+ *_p++ = 0xa0 + (_v >> 16); \
+ *_p++ = (_v >> 8) & 0xff; \
+ *_p++ = _v & 0xff; \
+ } else if (_v < 0x1020408f) { \
+ _v -= 0x20408f; \
+ *_p++ = 0x90 + (_v >> 24); \
+ *_p++ = (_v >> 16) & 0xff; \
+ *_p++ = (_v >> 8) & 0xff; \
+ *_p++ = _v & 0xff; \
+ } else { \
+ *_p++ = 0x8f; \
+ memcpy(_p, &_v, sizeof(uint32_t));\
+ _p += sizeof(uint32_t); \
+ } \
+ p = _p; \
+} while (0)
+
+#define GRN_B_ENC_SIZE(v) \
+ ((v) < 0x8f ? 1 : ((v) < 0x408f ? 2 : ((v) < 0x20408f ? 3 : ((v) < 0x1020408f ? 4 : 5))))
+
+#define GRN_B_DEC(v,p) do { \
+ uint8_t *_p = (uint8_t *)p; \
+ uint32_t _v = *_p++; \
+ switch (_v >> 4) { \
+ case 0x08 : \
+ if (_v == 0x8f) { \
+ memcpy(&_v, _p, sizeof(uint32_t));\
+ _p += sizeof(uint32_t); \
+ } \
+ break; \
+ case 0x09 : \
+ _v = (_v - 0x90) * 0x100 + *_p++; \
+ _v = _v * 0x100 + *_p++; \
+ _v = _v * 0x100 + *_p++ + 0x20408f; \
+ break; \
+ case 0x0a : \
+ case 0x0b : \
+ _v = (_v - 0xa0) * 0x100 + *_p++; \
+ _v = _v * 0x100 + *_p++ + 0x408f; \
+ break; \
+ case 0x0c : \
+ case 0x0d : \
+ case 0x0e : \
+ case 0x0f : \
+ _v = (_v - 0xc0) * 0x100 + *_p++ + 0x8f; \
+ break; \
+ } \
+ v = _v; \
+ p = _p; \
+} while (0)
+
+#define GRN_B_SKIP(p) do { \
+ uint8_t *_p = (uint8_t *)p; \
+ uint32_t _v = *_p++; \
+ switch (_v >> 4) { \
+ case 0x08 : \
+ if (_v == 0x8f) { \
+ _p += sizeof(uint32_t); \
+ } \
+ break; \
+ case 0x09 : \
+ _p += 3; \
+ break; \
+ case 0x0a : \
+ case 0x0b : \
+ _p += 2; \
+ break; \
+ case 0x0c : \
+ case 0x0d : \
+ case 0x0e : \
+ case 0x0f : \
+ _p += 1; \
+ break; \
+ } \
+ p = _p; \
+} while (0)
+
+#define GRN_B_COPY(p2,p1) do { \
+ uint32_t size = 0, _v = *p1++; \
+ *p2++ = _v; \
+ switch (_v >> 4) { \
+ case 0x08 : \
+ size = (_v == 0x8f) ? 4 : 0; \
+ break; \
+ case 0x09 : \
+ size = 3; \
+ break; \
+ case 0x0a : \
+ case 0x0b : \
+ size = 2; \
+ break; \
+ case 0x0c : \
+ case 0x0d : \
+ case 0x0e : \
+ case 0x0f : \
+ size = 1; \
+ break; \
+ } \
+ while (size--) { *p2++ = *p1++; } \
+} while (0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_IO_H */
diff --git a/storage/mroonga/vendor/groonga/lib/libgroonga.c b/storage/mroonga/vendor/groonga/lib/libgroonga.c
new file mode 100644
index 00000000000..19e0941031d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/libgroonga.c
@@ -0,0 +1,8 @@
+#ifdef WIN32
+#include <windows.h>
+
+BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void * reserve)
+{
+ return TRUE;
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb.c b/storage/mroonga/vendor/groonga/lib/mrb.c
new file mode 100644
index 00000000000..f05949cef98
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb.c
@@ -0,0 +1,226 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "mrb.h"
+#include "ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+# include <mruby/proc.h>
+# include <mruby/compile.h>
+# include <mruby/string.h>
+#endif
+
+#define BUFFER_SIZE 2048
+
+#ifdef GRN_WITH_MRUBY
+#ifdef WIN32
+static char *win32_ruby_scripts_dir = NULL;
+static char win32_ruby_scripts_dir_buffer[PATH_MAX];
+static const char *
+grn_mrb_get_system_ruby_scripts_dir(void)
+{
+ if (!win32_ruby_scripts_dir) {
+ const char *base_dir;
+ const char *relative_path = GRN_RELATIVE_RUBY_SCRIPTS_DIR;
+ char *path;
+ size_t base_dir_length;
+
+ base_dir = grn_win32_base_dir();
+ base_dir_length = strlen(base_dir);
+ strcpy(win32_ruby_scripts_dir_buffer, base_dir);
+ strcat(win32_ruby_scripts_dir_buffer, "/");
+ strcat(win32_ruby_scripts_dir_buffer, relative_path);
+ win32_ruby_scripts_dir = win32_ruby_scripts_dir_buffer;
+ }
+ return win32_ruby_scripts_dir;
+}
+
+#else /* WIN32 */
+static const char *
+grn_mrb_get_system_ruby_scripts_dir(void)
+{
+ return GRN_RUBY_SCRIPTS_DIR;
+}
+#endif /* WIN32 */
+
+static grn_bool
+grn_mrb_expand_script_path(grn_ctx *ctx, const char *path, char *expanded_path)
+{
+ const char *ruby_scripts_dir;
+ char dir_last_char;
+ int path_length, max_path_length;
+
+ if (path[0] == '/') {
+ expanded_path[0] = '\0';
+ } else {
+ ruby_scripts_dir = getenv("GRN_RUBY_SCRIPTS_DIR");
+ if (!ruby_scripts_dir) {
+ ruby_scripts_dir = grn_mrb_get_system_ruby_scripts_dir();
+ }
+ strcpy(expanded_path, ruby_scripts_dir);
+
+ dir_last_char = ruby_scripts_dir[strlen(expanded_path) - 1];
+ if (dir_last_char != '/') {
+ strcat(expanded_path, "/");
+ }
+ }
+
+ path_length = strlen(path);
+ max_path_length = PATH_MAX - strlen(expanded_path) - 1;
+ if (path_length > max_path_length) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "script path is too long: %d (max: %d) <%s%s>",
+ path_length, max_path_length,
+ expanded_path, path);
+ return GRN_FALSE;
+ }
+
+ strcat(expanded_path, path);
+
+ return GRN_TRUE;
+}
+
+mrb_value
+grn_mrb_load(grn_ctx *ctx, const char *path)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ char expanded_path[PATH_MAX];
+ FILE *file;
+ mrb_value result;
+ struct mrb_parser_state *parser;
+
+ if (!mrb) {
+ return mrb_nil_value();
+ }
+
+ if (!grn_mrb_expand_script_path(ctx, path, expanded_path)) {
+ return mrb_nil_value();
+ }
+
+ file = fopen(expanded_path, "r");
+ if (!file) {
+ char message[BUFFER_SIZE];
+ mrb_value exception;
+ snprintf(message, BUFFER_SIZE - 1,
+ "fopen: failed to open mruby script file: <%s>", expanded_path);
+ SERR(message);
+ exception = mrb_exc_new(mrb, E_ARGUMENT_ERROR,
+ ctx->errbuf, strlen(ctx->errbuf));
+ mrb->exc = mrb_obj_ptr(exception);
+ return mrb_nil_value();
+ }
+
+ parser = mrb_parser_new(mrb);
+ mrb_parser_set_filename(parser, expanded_path);
+ parser->s = parser->send = NULL;
+ parser->f = file;
+ mrb_parser_parse(parser, NULL);
+ fclose(file);
+
+ {
+ struct RProc *proc;
+ proc = mrb_generate_code(mrb, parser);
+ result = mrb_toplevel_run(mrb, proc);
+ }
+ mrb_parser_free(parser);
+
+ return result;
+}
+
+mrb_value
+grn_mrb_eval(grn_ctx *ctx, const char *script, int script_length)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ mrb_value result;
+ struct mrb_parser_state *parser;
+
+ if (!mrb) {
+ return mrb_nil_value();
+ }
+
+ if (script_length < 0) {
+ script_length = strlen(script);
+ }
+ parser = mrb_parse_nstring(mrb, script, script_length, NULL);
+ {
+ struct RProc *proc;
+ struct RClass *eval_context_class;
+ mrb_value eval_context;
+
+ proc = mrb_generate_code(mrb, parser);
+ eval_context_class = mrb_class_get_under(mrb, data->module, "EvalContext");
+ eval_context = mrb_obj_new(mrb, eval_context_class, 0, NULL);
+ result = mrb_context_run(mrb, proc, eval_context, 0);
+ }
+ mrb_parser_free(parser);
+
+ return result;
+}
+
+grn_rc
+grn_mrb_to_grn(grn_ctx *ctx, mrb_value mrb_object, grn_obj *grn_object)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+
+ switch (mrb_type(mrb_object)) {
+ case MRB_TT_FALSE :
+ if (mrb_nil_p(mrb_object)) {
+ grn_obj_reinit(ctx, grn_object, GRN_DB_VOID, 0);
+ } else {
+ grn_obj_reinit(ctx, grn_object, GRN_DB_BOOL, 0);
+ GRN_BOOL_SET(ctx, grn_object, GRN_FALSE);
+ }
+ break;
+ case MRB_TT_TRUE :
+ grn_obj_reinit(ctx, grn_object, GRN_DB_BOOL, 0);
+ GRN_BOOL_SET(ctx, grn_object, GRN_TRUE);
+ break;
+ case MRB_TT_FIXNUM :
+ grn_obj_reinit(ctx, grn_object, GRN_DB_INT32, 0);
+ GRN_INT32_SET(ctx, grn_object, mrb_fixnum(mrb_object));
+ break;
+ case MRB_TT_STRING :
+ grn_obj_reinit(ctx, grn_object, GRN_DB_TEXT, 0);
+ GRN_TEXT_SET(ctx, grn_object,
+ RSTRING_PTR(mrb_object),
+ RSTRING_LEN(mrb_object));
+ break;
+ case MRB_TT_SYMBOL :
+ {
+ const char *name;
+ int name_length;
+
+ grn_obj_reinit(ctx, grn_object, GRN_DB_TEXT, 0);
+ GRN_BULK_REWIND(grn_object);
+ GRN_TEXT_PUTC(ctx, grn_object, ':');
+ name = mrb_sym2name_len(mrb, mrb_symbol(mrb_object), &name_length);
+ GRN_TEXT_PUT(ctx, grn_object, name, name_length);
+ }
+ break;
+ default :
+ rc = GRN_INVALID_ARGUMENT;
+ break;
+ }
+
+ return rc;
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb.h b/storage/mroonga/vendor/groonga/lib/mrb.h
new file mode 100644
index 00000000000..4cff30ef1e9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb.h
@@ -0,0 +1,43 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_H
+#define GRN_MRB_H
+
+#include "groonga_in.h"
+#include "ctx.h"
+
+#ifdef GRN_WITH_MRUBY
+# include <mruby.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef GRN_WITH_MRUBY
+mrb_value grn_mrb_eval(grn_ctx *ctx, const char *script, int script_length);
+mrb_value grn_mrb_load(grn_ctx *ctx, const char *path);
+grn_rc grn_mrb_to_grn(grn_ctx *ctx, mrb_value mrb_object, grn_obj *grn_object);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/Makefile.am b/storage/mroonga/vendor/groonga/lib/mrb/Makefile.am
new file mode 100644
index 00000000000..13b4aeb57d3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/Makefile.am
@@ -0,0 +1,20 @@
+SUBDIRS = \
+ scripts
+
+AM_CPPFLAGS = \
+ -I$(top_builddir) \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib
+
+AM_CFLAGS = \
+ $(NO_STRICT_ALIASING_CFLAGS) \
+ $(COVERAGE_CFLAGS) \
+ $(GRN_CFLAGS) \
+ $(MESSAGE_PACK_CFLAGS) \
+ $(MRUBY_CFLAGS)
+
+noinst_LTLIBRARIES = libgrnmrb.la
+
+include sources.am
+
+CLEANFILES = *.gcno *.gcda
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_accessor.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_accessor.c
new file mode 100644
index 00000000000..a350c6b04f3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_accessor.c
@@ -0,0 +1,71 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/variable.h>
+#include <mruby/data.h>
+
+#include "../db.h"
+#include "mrb_accessor.h"
+
+static struct mrb_data_type mrb_grn_accessor_type = {
+ "Groonga::Accessor",
+ NULL
+};
+
+static mrb_value
+mrb_grn_accessor_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mrb_accessor_ptr;
+
+ mrb_get_args(mrb, "o", &mrb_accessor_ptr);
+ DATA_TYPE(self) = &mrb_grn_accessor_type;
+ DATA_PTR(self) = mrb_cptr(mrb_accessor_ptr);
+ return self;
+}
+
+static mrb_value
+mrb_grn_accessor_next(mrb_state *mrb, mrb_value self)
+{
+ grn_accessor *accessor;
+
+ accessor = DATA_PTR(self);
+ if (!accessor->next) { return mrb_nil_value(); }
+ return mrb_cptr_value(mrb, accessor->next);
+}
+
+void
+grn_mrb_accessor_init(grn_ctx *ctx)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ struct RClass *module = data->module;
+ struct RClass *klass;
+
+ klass = mrb_define_class_under(mrb, module, "Accessor", data->object_class);
+ MRB_SET_INSTANCE_TT(klass, MRB_TT_DATA);
+ mrb_define_method(mrb, klass, "initialize",
+ mrb_grn_accessor_initialize, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "next",
+ mrb_grn_accessor_next, MRB_ARGS_NONE());
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_accessor.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_accessor.h
new file mode 100644
index 00000000000..2aaf32f48f9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_accessor.h
@@ -0,0 +1,35 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_ACCESSOR_H
+#define GRN_MRB_ACCESSOR_H
+
+#include "../ctx.h"
+#include "../db.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_accessor_init(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_ACCESSOR_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_bulk.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_bulk.c
new file mode 100644
index 00000000000..ad428ea5bde
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_bulk.c
@@ -0,0 +1,149 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/variable.h>
+#include <mruby/data.h>
+#include <mruby/numeric.h>
+
+#include "../db.h"
+#include "mrb_bulk.h"
+
+static struct mrb_data_type mrb_grn_bulk_type = {
+ "Groonga::Bulk",
+ NULL
+};
+
+static mrb_value
+mrb_grn_bulk_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mrb_bulk_ptr;
+
+ mrb_get_args(mrb, "o", &mrb_bulk_ptr);
+ DATA_TYPE(self) = &mrb_grn_bulk_type;
+ DATA_PTR(self) = mrb_cptr(mrb_bulk_ptr);
+ return self;
+}
+
+static mrb_value
+mrb_grn_bulk_get_domain(mrb_state *mrb, mrb_value self)
+{
+ grn_obj *bulk;
+
+ bulk = DATA_PTR(self);
+ return mrb_fixnum_value(bulk->header.domain);
+}
+
+static mrb_value
+mrb_grn_bulk_get_value(mrb_state *mrb, mrb_value self)
+{
+ grn_obj *bulk;
+ mrb_value mrb_value_;
+
+ bulk = DATA_PTR(self);
+ switch (bulk->header.domain) {
+ case GRN_DB_INT32 :
+ {
+ int32_t value;
+ value = GRN_INT32_VALUE(bulk);
+ mrb_value_ = mrb_fixnum_value(value);
+ }
+ break;
+ case GRN_DB_UINT32 :
+ {
+ int64_t value;
+ value = GRN_UINT32_VALUE(bulk);
+ if (!FIXABLE(value)) {
+ mrb_raisef(mrb, E_RANGE_ERROR,
+ "can't handle large number: <%S>: max: <%S>",
+ mrb_fixnum_value(value), /* TODO: This will cause overflow */
+ mrb_fixnum_value(MRB_INT_MAX));
+ }
+ mrb_value_ = mrb_fixnum_value(value);
+ }
+ break;
+ default :
+ {
+#define MESSAGE_SIZE 4096
+ char message[MESSAGE_SIZE];
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ grn_obj *domain;
+ char domain_name[GRN_TABLE_MAX_KEY_SIZE];
+ int domain_name_size;
+
+ domain = grn_ctx_at(ctx, bulk->header.domain);
+ if (domain) {
+ domain_name_size = grn_obj_name(ctx, domain,
+ domain_name, GRN_TABLE_MAX_KEY_SIZE);
+ grn_obj_unlink(ctx, domain);
+ } else {
+ strcpy(domain_name, "unknown");
+ domain_name_size = strlen(domain_name);
+ }
+ snprintf(message, MESSAGE_SIZE,
+ "unsupported bulk value type: <%d>(%.*s)",
+ bulk->header.domain,
+ domain_name_size,
+ domain_name);
+ mrb_raise(mrb, E_RANGE_ERROR, message);
+#undef MESSAGE_SIZE
+ }
+ break;
+ }
+
+ return mrb_value_;
+}
+
+static mrb_value
+mrb_grn_bulk_equal(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mrb_other;
+
+ mrb_get_args(mrb, "o", &mrb_other);
+
+ if (!mrb_obj_is_kind_of(mrb, mrb_other, mrb_class(mrb, self))) {
+ return mrb_false_value();
+ }
+
+ return mrb_bool_value(DATA_PTR(self) == DATA_PTR(mrb_other));
+}
+
+void
+grn_mrb_bulk_init(grn_ctx *ctx)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ struct RClass *module = data->module;
+ struct RClass *klass;
+
+ klass = mrb_define_class_under(mrb, module, "Bulk", mrb->object_class);
+ MRB_SET_INSTANCE_TT(klass, MRB_TT_DATA);
+ mrb_define_method(mrb, klass, "initialize",
+ mrb_grn_bulk_initialize, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "domain",
+ mrb_grn_bulk_get_domain, MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "value",
+ mrb_grn_bulk_get_value, MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "==",
+ mrb_grn_bulk_equal, MRB_ARGS_REQ(1));
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_bulk.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_bulk.h
new file mode 100644
index 00000000000..3be86584dc6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_bulk.h
@@ -0,0 +1,35 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_BULK_H
+#define GRN_MRB_BULK_H
+
+#include "../ctx.h"
+#include "../db.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_bulk_init(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_BULK_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_column.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_column.c
new file mode 100644
index 00000000000..545f070d91c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_column.c
@@ -0,0 +1,39 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+#include <mruby/class.h>
+
+#include "mrb_column.h"
+
+void
+grn_mrb_column_init(grn_ctx *ctx)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ struct RClass *module = data->module;
+ struct RClass *object_class = data->object_class;
+ struct RClass *klass;
+
+ klass = mrb_define_class_under(mrb, module, "Column", object_class);
+ MRB_SET_INSTANCE_TT(klass, MRB_TT_DATA);
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_column.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_column.h
new file mode 100644
index 00000000000..77e0dab82fd
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_column.h
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_COLUMN_H
+#define GRN_MRB_COLUMN_H
+
+#include "../ctx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_column_init(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_COLUMN_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_converter.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_converter.c
new file mode 100644
index 00000000000..2e7c41edbdf
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_converter.c
@@ -0,0 +1,93 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+
+#include "mrb_converter.h"
+
+struct RClass *
+grn_mrb_class_from_grn_obj(mrb_state *mrb, grn_obj *object)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ grn_mrb_data *data;
+ struct RClass *klass = NULL;
+
+ data = &(ctx->impl->mrb);
+ switch (object->header.type) {
+ case GRN_ACCESSOR :
+ klass = mrb_class_get_under(mrb, data->module, "Accessor");
+ break;
+ case GRN_BULK :
+ klass = mrb_class_get_under(mrb, data->module, "Bulk");
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ klass = mrb_class_get_under(mrb, data->module, "FixedSizeColumn");
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ klass = mrb_class_get_under(mrb, data->module, "VariableSizeColumn");
+ break;
+ case GRN_COLUMN_INDEX :
+ klass = mrb_class_get_under(mrb, data->module, "IndexColumn");
+ break;
+ case GRN_EXPR :
+ klass = mrb_class_get_under(mrb, data->module, "Expression");
+ break;
+ case GRN_PROC :
+ klass = mrb_class_get_under(mrb, data->module, "Procedure");
+ break;
+ case GRN_VOID :
+ klass = mrb_class_get_under(mrb, data->module, "Void");
+ break;
+ default :
+ break;
+ }
+
+ if (!klass) {
+#define BUFFER_SIZE 1024
+ char buffer[BUFFER_SIZE];
+ snprintf(buffer, BUFFER_SIZE - 1,
+ "can't find class for object type: %#x", object->header.type);
+ mrb_raise(mrb, E_ARGUMENT_ERROR, buffer);
+#undef BUFFER_SIZE
+ }
+
+ return klass;
+}
+
+mrb_value
+grn_mrb_value_from_grn_obj(mrb_state *mrb, grn_obj *object)
+{
+ struct RClass *mrb_class;
+ mrb_value mrb_new_arguments[1];
+ mrb_value mrb_object;
+
+ if (!object) {
+ return mrb_nil_value();
+ }
+
+ mrb_class = grn_mrb_class_from_grn_obj(mrb, object);
+ mrb_new_arguments[0] = mrb_cptr_value(mrb, object);
+ mrb_object = mrb_obj_new(mrb, mrb_class, 1, mrb_new_arguments);
+ return mrb_object;
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_converter.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_converter.h
new file mode 100644
index 00000000000..27223a8c0bf
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_converter.h
@@ -0,0 +1,37 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_CONVERTER_H
+#define GRN_MRB_CONVERTER_H
+
+#include "../ctx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef GRN_WITH_MRUBY
+struct RClass *grn_mrb_class_from_grn_obj(mrb_state *mrb, grn_obj *object);
+mrb_value grn_mrb_value_from_grn_obj(mrb_state *mrb, grn_obj *object);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_CONVERTER_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_ctx.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_ctx.c
new file mode 100644
index 00000000000..81a584d0f95
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_ctx.c
@@ -0,0 +1,224 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+#include <mruby/variable.h>
+#include <mruby/string.h>
+
+#include "../mrb.h"
+#include "mrb_ctx.h"
+#include "mrb_converter.h"
+
+static mrb_value
+ctx_class_instance(mrb_state *mrb, mrb_value klass)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ mrb_value mrb_ctx;
+
+ mrb_ctx = mrb_obj_value(mrb_obj_alloc(mrb, MRB_TT_DATA, mrb_class_ptr(klass)));
+ DATA_PTR(mrb_ctx) = ctx;
+
+ return mrb_ctx;
+}
+
+static mrb_value
+ctx_array_reference(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ grn_obj *object;
+ char *name;
+ int name_length;
+
+ mrb_get_args(mrb, "s", &name, &name_length);
+ object = grn_ctx_get(ctx, name, name_length);
+
+ return grn_mrb_value_from_grn_obj(mrb, object);
+}
+
+static mrb_value
+ctx_get_rc(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+
+ return mrb_fixnum_value(ctx->rc);
+}
+
+static mrb_value
+ctx_set_rc(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ mrb_int rc;
+
+ mrb_get_args(mrb, "i", &rc);
+ ctx->rc = rc;
+
+ return mrb_fixnum_value(ctx->rc);
+}
+
+static mrb_value
+ctx_get_error_level(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+
+ return mrb_fixnum_value(ctx->errlvl);
+}
+
+static mrb_value
+ctx_set_error_level(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ mrb_int error_level;
+
+ mrb_get_args(mrb, "i", &error_level);
+ ctx->errlvl = error_level;
+
+ return mrb_fixnum_value(ctx->errlvl);
+}
+
+static mrb_value
+ctx_get_error_file(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+
+ return mrb_str_new_cstr(mrb, ctx->errfile);
+}
+
+static mrb_value
+ctx_set_error_file(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ mrb_value error_file;
+
+ mrb_get_args(mrb, "S", &error_file);
+ mrb_iv_set(mrb, self, mrb_intern_lit(mrb, "@error_file"), error_file);
+ ctx->errfile = mrb_string_value_cstr(mrb, &error_file);
+
+ return error_file;
+}
+
+static mrb_value
+ctx_get_error_line(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+
+ return mrb_fixnum_value(ctx->errline);
+}
+
+static mrb_value
+ctx_set_error_line(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ mrb_int error_line;
+
+ mrb_get_args(mrb, "i", &error_line);
+ ctx->errline = error_line;
+
+ return mrb_fixnum_value(ctx->errline);
+}
+
+static mrb_value
+ctx_get_error_method(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+
+ return mrb_str_new_cstr(mrb, ctx->errfunc);
+}
+
+static mrb_value
+ctx_set_error_method(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ mrb_value error_method;
+
+ mrb_get_args(mrb, "S", &error_method);
+ mrb_iv_set(mrb, self, mrb_intern_lit(mrb, "@error_method"), error_method);
+ ctx->errfunc = mrb_string_value_cstr(mrb, &error_method);
+
+ return error_method;
+}
+
+static mrb_value
+ctx_get_error_message(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+
+ return mrb_str_new_cstr(mrb, ctx->errbuf);
+}
+
+static mrb_value
+ctx_set_error_message(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ mrb_value error_message;
+
+ mrb_get_args(mrb, "S", &error_message);
+ grn_ctx_log(ctx, "%.*s",
+ RSTRING_LEN(error_message),
+ RSTRING_PTR(error_message));
+
+ return error_message;
+}
+
+void
+grn_mrb_ctx_init(grn_ctx *ctx)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ struct RClass *module = data->module;
+ struct RClass *klass;
+
+ klass = mrb_define_class_under(mrb, module, "Context", mrb->object_class);
+ MRB_SET_INSTANCE_TT(klass, MRB_TT_DATA);
+
+ mrb_define_class_method(mrb, klass, "instance",
+ ctx_class_instance, MRB_ARGS_NONE());
+
+ mrb_define_method(mrb, klass, "[]", ctx_array_reference, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "rc", ctx_get_rc, MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "rc=", ctx_set_rc, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "error_level", ctx_get_error_level,
+ MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "error_level=", ctx_set_error_level,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "error_file", ctx_get_error_file,
+ MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "error_file=", ctx_set_error_file,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "error_line", ctx_get_error_line,
+ MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "error_line=", ctx_set_error_line,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "error_method", ctx_get_error_method,
+ MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "error_method=", ctx_set_error_method,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "error_message", ctx_get_error_message,
+ MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "error_message=", ctx_set_error_message,
+ MRB_ARGS_REQ(1));
+
+ grn_mrb_load(ctx, "context/error_level.rb");
+ grn_mrb_load(ctx, "context/rc.rb");
+ grn_mrb_load(ctx, "context.rb");
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_ctx.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_ctx.h
new file mode 100644
index 00000000000..f620f941c93
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_ctx.h
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_CTX_H
+#define GRN_MRB_CTX_H
+
+#include "../ctx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_ctx_init(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_CTX_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_expr.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_expr.c
new file mode 100644
index 00000000000..b9fb35020cb
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_expr.c
@@ -0,0 +1,460 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/variable.h>
+#include <mruby/data.h>
+#include <mruby/array.h>
+
+#include "../expr.h"
+#include "../util.h"
+#include "../mrb.h"
+#include "mrb_accessor.h"
+#include "mrb_expr.h"
+#include "mrb_converter.h"
+
+static struct mrb_data_type mrb_grn_scan_info_type = {
+ "Groonga::ScanInfo",
+ NULL
+};
+static struct mrb_data_type mrb_grn_expr_code_type = {
+ "Groonga::ExpressionCode",
+ NULL
+};
+static struct mrb_data_type mrb_grn_expression_type = {
+ "Groonga::Expression",
+ NULL
+};
+
+static mrb_value
+mrb_grn_scan_info_new(mrb_state *mrb, scan_info *scan_info)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ struct RClass *module = ctx->impl->mrb.module;
+ struct RClass *klass;
+ mrb_value mrb_scan_info;
+
+ mrb_scan_info = mrb_cptr_value(mrb, scan_info);
+ klass = mrb_class_get_under(mrb, module, "ScanInfo");
+ return mrb_obj_new(mrb, klass, 1, &mrb_scan_info);
+}
+
+static mrb_value
+mrb_grn_expr_code_new(mrb_state *mrb, grn_expr_code *code)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ struct RClass *module = ctx->impl->mrb.module;
+ struct RClass *klass;
+ mrb_value mrb_code;
+
+ mrb_code = mrb_cptr_value(mrb, code);
+ klass = mrb_class_get_under(mrb, module, "ExpressionCode");
+ return mrb_obj_new(mrb, klass, 1, &mrb_code);
+}
+
+static mrb_value
+mrb_grn_scan_info_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mrb_ptr;
+
+ mrb_get_args(mrb, "o", &mrb_ptr);
+ DATA_TYPE(self) = &mrb_grn_scan_info_type;
+ DATA_PTR(self) = mrb_cptr(mrb_ptr);
+ return self;
+}
+
+static mrb_value
+mrb_grn_expr_code_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mrb_code;
+
+ mrb_get_args(mrb, "o", &mrb_code);
+ DATA_TYPE(self) = &mrb_grn_expr_code_type;
+ DATA_PTR(self) = mrb_cptr(mrb_code);
+ return self;
+}
+
+static mrb_value
+mrb_grn_scan_info_put_index(mrb_state *mrb, mrb_value self)
+{
+ int sid;
+ int32_t weight;
+ scan_info *si;
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ grn_obj *index;
+ mrb_value mrb_index;
+
+ mrb_get_args(mrb, "oii", &mrb_index, &sid, &weight);
+ si = DATA_PTR(self);
+ index = DATA_PTR(mrb_index);
+ grn_scan_info_put_index(ctx, si, index, sid, weight);
+ return self;
+}
+
+static mrb_value
+mrb_grn_scan_info_get_op(mrb_state *mrb, mrb_value self)
+{
+ scan_info *si;
+ grn_operator op;
+
+ si = DATA_PTR(self);
+ op = grn_scan_info_get_op(si);
+ return mrb_fixnum_value(op);
+}
+
+static mrb_value
+mrb_grn_scan_info_set_op(mrb_state *mrb, mrb_value self)
+{
+ scan_info *si;
+ grn_operator op;
+
+ mrb_get_args(mrb, "i", &op);
+ si = DATA_PTR(self);
+ grn_scan_info_set_op(si, op);
+ return self;
+}
+
+static mrb_value
+mrb_grn_scan_info_set_end(mrb_state *mrb, mrb_value self)
+{
+ scan_info *si;
+ int end;
+
+ mrb_get_args(mrb, "i", &end);
+ si = DATA_PTR(self);
+ grn_scan_info_set_end(si, end);
+ return self;
+}
+
+static mrb_value
+mrb_grn_scan_info_set_query(mrb_state *mrb, mrb_value self)
+{
+ scan_info *si;
+ mrb_value mrb_query;
+
+ mrb_get_args(mrb, "o", &mrb_query);
+ si = DATA_PTR(self);
+ if (mrb_nil_p(mrb_query)) {
+ grn_scan_info_set_query(si, NULL);
+ } else {
+ grn_scan_info_set_query(si, DATA_PTR(mrb_query));
+ }
+ return self;
+}
+
+static mrb_value
+mrb_grn_scan_info_set_flags(mrb_state *mrb, mrb_value self)
+{
+ scan_info *si;
+ int flags;
+
+ mrb_get_args(mrb, "i", &flags);
+ si = DATA_PTR(self);
+ grn_scan_info_set_flags(si, flags);
+ return self;
+}
+
+static mrb_value
+mrb_grn_scan_info_get_flags(mrb_state *mrb, mrb_value self)
+{
+ scan_info *si;
+ int flags;
+
+ si = DATA_PTR(self);
+ flags = grn_scan_info_get_flags(si);
+ return mrb_fixnum_value(flags);
+}
+
+static mrb_value
+mrb_grn_scan_info_set_logical_op(mrb_state *mrb, mrb_value self)
+{
+ scan_info *si;
+ grn_operator logical_op;
+
+ mrb_get_args(mrb, "i", &logical_op);
+ si = DATA_PTR(self);
+ grn_scan_info_set_logical_op(si, logical_op);
+ return self;
+}
+
+static mrb_value
+mrb_grn_scan_info_get_logical_op(mrb_state *mrb, mrb_value self)
+{
+ scan_info *si;
+ grn_operator logical_op;
+
+ si = DATA_PTR(self);
+ logical_op = grn_scan_info_get_logical_op(si);
+ return mrb_fixnum_value(logical_op);
+}
+
+static mrb_value
+mrb_grn_scan_info_set_max_interval(mrb_state *mrb, mrb_value self)
+{
+ scan_info *si;
+ int max_interval;
+
+ mrb_get_args(mrb, "i", &max_interval);
+ si = DATA_PTR(self);
+ grn_scan_info_set_max_interval(si, max_interval);
+ return self;
+}
+
+static mrb_value
+mrb_grn_scan_info_get_max_interval(mrb_state *mrb, mrb_value self)
+{
+ scan_info *si;
+ int max_interval;
+
+ si = DATA_PTR(self);
+ max_interval = grn_scan_info_get_max_interval(si);
+ return mrb_fixnum_value(max_interval);
+}
+
+static mrb_value
+mrb_grn_scan_info_get_arg(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ scan_info *si;
+ int index;
+ grn_obj *arg;
+
+ mrb_get_args(mrb, "i", &index);
+
+ si = DATA_PTR(self);
+ arg = grn_scan_info_get_arg(ctx, si, index);
+
+ return grn_mrb_value_from_grn_obj(mrb, arg);
+}
+
+static mrb_value
+mrb_grn_scan_info_push_arg(mrb_state *mrb, mrb_value self)
+{
+ scan_info *si;
+ mrb_value mrb_arg;
+ grn_bool success;
+
+ mrb_get_args(mrb, "o", &mrb_arg);
+
+ si = DATA_PTR(self);
+ success = grn_scan_info_push_arg(si, DATA_PTR(mrb_arg));
+
+ return mrb_bool_value(success);
+}
+
+static mrb_value
+mrb_grn_expr_code_get_weight(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+
+ return mrb_fixnum_value(grn_expr_code_get_weight(ctx, DATA_PTR(self)));
+}
+
+static mrb_value
+mrb_grn_expr_code_get_value(mrb_state *mrb, mrb_value self)
+{
+ grn_expr_code *expr_code;
+
+ expr_code = DATA_PTR(self);
+ return grn_mrb_value_from_grn_obj(mrb, expr_code->value);
+}
+
+static mrb_value
+mrb_grn_expr_code_get_op(mrb_state *mrb, mrb_value self)
+{
+ grn_expr_code *expr_code;
+
+ expr_code = DATA_PTR(self);
+ return mrb_fixnum_value(expr_code->op);
+}
+
+static mrb_value
+mrb_grn_expr_code_get_flags(mrb_state *mrb, mrb_value self)
+{
+ grn_expr_code *expr_code;
+
+ expr_code = DATA_PTR(self);
+ return mrb_fixnum_value(expr_code->flags);
+}
+
+static mrb_value
+mrb_grn_expression_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mrb_expression_ptr;
+
+ mrb_get_args(mrb, "o", &mrb_expression_ptr);
+ DATA_TYPE(self) = &mrb_grn_expression_type;
+ DATA_PTR(self) = mrb_cptr(mrb_expression_ptr);
+ return self;
+}
+
+static mrb_value
+mrb_grn_expression_codes(mrb_state *mrb, mrb_value self)
+{
+ grn_expr *expr;
+ mrb_value mrb_codes;
+ int i;
+
+ expr = DATA_PTR(self);
+ mrb_codes = mrb_ary_new_capa(mrb, expr->codes_curr);
+ for (i = 0; i < expr->codes_curr; i++) {
+ grn_expr_code *code = expr->codes + i;
+ mrb_ary_push(mrb, mrb_codes, mrb_grn_expr_code_new(mrb, code));
+ }
+
+ return mrb_codes;
+}
+
+static mrb_value
+mrb_grn_expression_get_var_by_offset(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ grn_obj *expr;
+ mrb_int offset;
+ grn_obj *var;
+
+ mrb_get_args(mrb, "i", &offset);
+
+ expr = DATA_PTR(self);
+ var = grn_expr_get_var_by_offset(ctx, expr, offset);
+ return grn_mrb_value_from_grn_obj(mrb, var);
+}
+
+void
+grn_mrb_expr_init(grn_ctx *ctx)
+{
+ mrb_state *mrb = ctx->impl->mrb.state;
+ struct RClass *module = ctx->impl->mrb.module;
+ struct RClass *object_class = ctx->impl->mrb.object_class;
+ struct RClass *klass;
+
+ klass = mrb_define_class_under(mrb, module, "ScanInfo", mrb->object_class);
+ MRB_SET_INSTANCE_TT(klass, MRB_TT_DATA);
+ mrb_define_method(mrb, klass, "initialize",
+ mrb_grn_scan_info_initialize, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "put_index",
+ mrb_grn_scan_info_put_index, MRB_ARGS_REQ(3));
+ mrb_define_method(mrb, klass, "op",
+ mrb_grn_scan_info_get_op, MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "op=",
+ mrb_grn_scan_info_set_op, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "end=",
+ mrb_grn_scan_info_set_end, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "query=",
+ mrb_grn_scan_info_set_query, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "flags",
+ mrb_grn_scan_info_get_flags, MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "flags=",
+ mrb_grn_scan_info_set_flags, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "logical_op",
+ mrb_grn_scan_info_get_logical_op, MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "logical_op=",
+ mrb_grn_scan_info_set_logical_op, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "max_interval",
+ mrb_grn_scan_info_get_max_interval, MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "max_interval=",
+ mrb_grn_scan_info_set_max_interval, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "get_arg",
+ mrb_grn_scan_info_get_arg, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "push_arg",
+ mrb_grn_scan_info_push_arg, MRB_ARGS_REQ(1));
+
+ klass = mrb_define_class_under(mrb, module,
+ "ExpressionCode", mrb->object_class);
+ MRB_SET_INSTANCE_TT(klass, MRB_TT_DATA);
+ mrb_define_method(mrb, klass, "initialize",
+ mrb_grn_expr_code_initialize, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "weight",
+ mrb_grn_expr_code_get_weight, MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "value",
+ mrb_grn_expr_code_get_value, MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "op",
+ mrb_grn_expr_code_get_op, MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "flags",
+ mrb_grn_expr_code_get_flags, MRB_ARGS_NONE());
+
+ klass = mrb_define_class_under(mrb, module, "Expression", object_class);
+ MRB_SET_INSTANCE_TT(klass, MRB_TT_DATA);
+ mrb_define_method(mrb, klass, "initialize",
+ mrb_grn_expression_initialize, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "codes",
+ mrb_grn_expression_codes, MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "get_var_by_offset",
+ mrb_grn_expression_get_var_by_offset, MRB_ARGS_REQ(1));
+
+ grn_mrb_load(ctx, "expression.rb");
+ grn_mrb_load(ctx, "scan_info.rb");
+ grn_mrb_load(ctx, "scan_info_data.rb");
+ grn_mrb_load(ctx, "scan_info_builder.rb");
+}
+
+scan_info **
+grn_mrb_scan_info_build(grn_ctx *ctx, grn_obj *expr, int *n,
+ grn_operator op, uint32_t size)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ mrb_value mrb_expression;
+ mrb_value mrb_sis;
+ scan_info **sis = NULL;
+ int i;
+ int arena_index;
+
+ arena_index = mrb_gc_arena_save(mrb);
+
+ mrb_expression = grn_mrb_value_from_grn_obj(mrb, expr);
+ mrb_sis = mrb_funcall(mrb, mrb_expression, "build_scan_info", 2,
+ mrb_fixnum_value(op),
+ mrb_fixnum_value(size));
+
+ if (mrb_nil_p(mrb_sis)) {
+ goto exit;
+ }
+
+ if (mrb_type(mrb_sis) == MRB_TT_EXCEPTION) {
+ mrb->exc = mrb_obj_ptr(mrb_sis);
+ mrb_print_error(mrb);
+ goto exit;
+ }
+
+ *n = RARRAY_LEN(mrb_sis);
+ sis = GRN_MALLOCN(scan_info *, *n);
+ for (i = 0; i < *n; i++) {
+ mrb_value mrb_si;
+ mrb_value mrb_si_data;
+ scan_info *si;
+ int start;
+
+ mrb_si_data = RARRAY_PTR(mrb_sis)[i];
+ start = mrb_fixnum(mrb_funcall(mrb, mrb_si_data, "start", 0));
+ si = grn_scan_info_open(ctx, start);
+ mrb_si = mrb_grn_scan_info_new(mrb, si);
+ mrb_funcall(mrb, mrb_si, "apply", 1, mrb_si_data);
+ sis[i] = si;
+ }
+
+exit:
+ mrb_gc_arena_restore(mrb, arena_index);
+
+ return sis;
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_expr.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_expr.h
new file mode 100644
index 00000000000..7fd5c66e9c5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_expr.h
@@ -0,0 +1,36 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_EXPR_H
+#define GRN_MRB_EXPR_H
+
+#include "../ctx.h"
+#include "../expr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_expr_init(grn_ctx *ctx);
+scan_info **grn_mrb_scan_info_build(grn_ctx *ctx, grn_obj *expr, int *n, grn_operator op, uint32_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_EXPR_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_fixed_size_column.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_fixed_size_column.c
new file mode 100644
index 00000000000..d8df913f77d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_fixed_size_column.c
@@ -0,0 +1,59 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+
+#include "mrb_fixed_size_column.h"
+
+static struct mrb_data_type mrb_grn_fixed_size_column_type = {
+ "Groonga::FixedSizeColumn",
+ NULL
+};
+
+static mrb_value
+mrb_grn_fixed_size_column_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mrb_fixed_size_column_ptr;
+
+ mrb_get_args(mrb, "o", &mrb_fixed_size_column_ptr);
+ DATA_TYPE(self) = &mrb_grn_fixed_size_column_type;
+ DATA_PTR(self) = mrb_cptr(mrb_fixed_size_column_ptr);
+ return self;
+}
+
+void
+grn_mrb_fixed_size_column_init(grn_ctx *ctx)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ struct RClass *module = data->module;
+ struct RClass *column_class;
+ struct RClass *klass;
+
+ column_class = mrb_class_get_under(mrb, module, "Column");
+ klass = mrb_define_class_under(mrb, module, "FixedSizeColumn", column_class);
+ MRB_SET_INSTANCE_TT(klass, MRB_TT_DATA);
+ mrb_define_method(mrb, klass, "initialize",
+ mrb_grn_fixed_size_column_initialize, MRB_ARGS_REQ(1));
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_fixed_size_column.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_fixed_size_column.h
new file mode 100644
index 00000000000..938dcd76c7a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_fixed_size_column.h
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_FIXED_SIZE_COLUMN_H
+#define GRN_MRB_FIXED_SIZE_COLUMN_H
+
+#include "../ctx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_fixed_size_column_init(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_FIXED_SIZE_COLUMN_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_id.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_id.c
new file mode 100644
index 00000000000..aa907b84a43
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_id.c
@@ -0,0 +1,74 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+
+#include "mrb_id.h"
+
+void
+grn_mrb_id_init(grn_ctx *ctx)
+{
+ mrb_state *mrb = ctx->impl->mrb.state;
+ struct RClass *module = ctx->impl->mrb.module;
+ struct RClass *id_module;
+
+ id_module = mrb_define_module_under(mrb, module, "ID");
+
+ mrb_define_const(mrb, id_module, "VOID",
+ mrb_fixnum_value(GRN_DB_VOID));
+ mrb_define_const(mrb, id_module, "DB",
+ mrb_fixnum_value(GRN_DB_DB));
+ mrb_define_const(mrb, id_module, "OBJECT",
+ mrb_fixnum_value(GRN_DB_OBJECT));
+ mrb_define_const(mrb, id_module, "BOOL",
+ mrb_fixnum_value(GRN_DB_BOOL));
+ mrb_define_const(mrb, id_module, "INT8",
+ mrb_fixnum_value(GRN_DB_INT8));
+ mrb_define_const(mrb, id_module, "UINT8",
+ mrb_fixnum_value(GRN_DB_UINT8));
+ mrb_define_const(mrb, id_module, "INT16",
+ mrb_fixnum_value(GRN_DB_INT16));
+ mrb_define_const(mrb, id_module, "UINT16",
+ mrb_fixnum_value(GRN_DB_UINT16));
+ mrb_define_const(mrb, id_module, "INT32",
+ mrb_fixnum_value(GRN_DB_INT32));
+ mrb_define_const(mrb, id_module, "UINT32",
+ mrb_fixnum_value(GRN_DB_UINT32));
+ mrb_define_const(mrb, id_module, "INT64",
+ mrb_fixnum_value(GRN_DB_INT64));
+ mrb_define_const(mrb, id_module, "UINT64",
+ mrb_fixnum_value(GRN_DB_UINT64));
+ mrb_define_const(mrb, id_module, "FLOAT",
+ mrb_fixnum_value(GRN_DB_FLOAT));
+ mrb_define_const(mrb, id_module, "TIME",
+ mrb_fixnum_value(GRN_DB_TIME));
+ mrb_define_const(mrb, id_module, "SHORT_TEXT",
+ mrb_fixnum_value(GRN_DB_SHORT_TEXT));
+ mrb_define_const(mrb, id_module, "TEXT",
+ mrb_fixnum_value(GRN_DB_TEXT));
+ mrb_define_const(mrb, id_module, "LONG_TEXT",
+ mrb_fixnum_value(GRN_DB_LONG_TEXT));
+ mrb_define_const(mrb, id_module, "TOKYO_GEO_POINT",
+ mrb_fixnum_value(GRN_DB_TOKYO_GEO_POINT));
+ mrb_define_const(mrb, id_module, "WGS84_GEO_POINT",
+ mrb_fixnum_value(GRN_DB_WGS84_GEO_POINT));
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_id.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_id.h
new file mode 100644
index 00000000000..f5b44358143
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_id.h
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_ID_H
+#define GRN_MRB_ID_H
+
+#include "../ctx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_id_init(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_ID_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_index_column.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_index_column.c
new file mode 100644
index 00000000000..7bdf7528fc7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_index_column.c
@@ -0,0 +1,59 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+
+#include "mrb_index_column.h"
+
+static struct mrb_data_type mrb_grn_index_column_type = {
+ "Groonga::IndexColumn",
+ NULL
+};
+
+static mrb_value
+mrb_grn_index_column_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mrb_index_column_ptr;
+
+ mrb_get_args(mrb, "o", &mrb_index_column_ptr);
+ DATA_TYPE(self) = &mrb_grn_index_column_type;
+ DATA_PTR(self) = mrb_cptr(mrb_index_column_ptr);
+ return self;
+}
+
+void
+grn_mrb_index_column_init(grn_ctx *ctx)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ struct RClass *module = data->module;
+ struct RClass *column_class;
+ struct RClass *klass;
+
+ column_class = mrb_class_get_under(mrb, module, "Column");
+ klass = mrb_define_class_under(mrb, module, "IndexColumn", column_class);
+ MRB_SET_INSTANCE_TT(klass, MRB_TT_DATA);
+ mrb_define_method(mrb, klass, "initialize",
+ mrb_grn_index_column_initialize, MRB_ARGS_REQ(1));
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_index_column.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_index_column.h
new file mode 100644
index 00000000000..10e0589bf93
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_index_column.h
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_INDEX_COLUMN_H
+#define GRN_MRB_INDEX_COLUMN_H
+
+#include "../ctx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_index_column_init(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_INDEX_COLUMN_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_logger.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_logger.c
new file mode 100644
index 00000000000..4917694fa8a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_logger.c
@@ -0,0 +1,76 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+#include <mruby/variable.h>
+#include <mruby/string.h>
+
+#include "../mrb.h"
+#include "mrb_logger.h"
+
+static mrb_value
+logger_need_log_p(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ mrb_int level;
+
+ mrb_get_args(mrb, "i", &level);
+
+ return mrb_bool_value(grn_logger_pass(ctx, level));
+}
+
+static mrb_value
+logger_log(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ mrb_int level;
+ char *file;
+ mrb_int line;
+ char *method;
+ char *message;
+ mrb_int message_size;
+
+ mrb_get_args(mrb, "izizs",
+ &level, &file, &line, &method, &message, &message_size);
+ grn_logger_put(ctx, level, file, line, method, "%.*s", message_size, message);
+
+ return self;
+}
+
+void
+grn_mrb_logger_init(grn_ctx *ctx)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ struct RClass *module = data->module;
+ struct RClass *klass;
+
+ klass = mrb_define_class_under(mrb, module, "Logger", mrb->object_class);
+
+ mrb_define_method(mrb, klass, "need_log?", logger_need_log_p, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "log", logger_log, MRB_ARGS_REQ(5));
+
+ grn_mrb_load(ctx, "logger/level.rb");
+ grn_mrb_load(ctx, "logger.rb");
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_logger.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_logger.h
new file mode 100644
index 00000000000..5bfb62e98a2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_logger.h
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_LOGGER_H
+#define GRN_MRB_LOGGER_H
+
+#include "../ctx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_logger_init(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_LOGGER_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_obj.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_obj.c
new file mode 100644
index 00000000000..de3bf678c00
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_obj.c
@@ -0,0 +1,114 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+#include "../util.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+#include <mruby/string.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+
+#include "../mrb.h"
+#include "mrb_obj.h"
+#include "mrb_converter.h"
+
+static mrb_value
+object_get_name(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ grn_obj *object;
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+ int name_length;
+
+ object = DATA_PTR(self);
+ name_length = grn_obj_name(ctx, object, name, GRN_TABLE_MAX_KEY_SIZE);
+
+ return mrb_str_new(mrb, name, name_length);
+}
+
+static mrb_value
+object_find_index(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ grn_obj *object;
+ mrb_value mrb_operator;
+ grn_obj *index;
+ int n_indexes;
+ int section_id;
+
+ mrb_get_args(mrb, "o", &mrb_operator);
+ object = DATA_PTR(self);
+ n_indexes = grn_column_index(ctx,
+ object,
+ mrb_fixnum(mrb_operator),
+ &index,
+ 1,
+ &section_id);
+ if (n_indexes == 0) {
+ return mrb_nil_value();
+ } else {
+ grn_mrb_data *data;
+ struct RClass *klass;
+ mrb_value args[2];
+
+ data = &(ctx->impl->mrb);
+ klass = mrb_class_get_under(mrb, data->module, "IndexInfo");
+ args[0] = grn_mrb_value_from_grn_obj(mrb, index);
+ args[1] = mrb_fixnum_value(section_id);
+ return mrb_obj_new(mrb, klass, 2, args);
+ }
+}
+
+static mrb_value
+object_grn_inspect(mrb_state *mrb, mrb_value self)
+{
+ grn_ctx *ctx = (grn_ctx *)mrb->ud;
+ grn_obj buffer;
+ mrb_value inspected;
+
+ GRN_TEXT_INIT(&buffer, 0);
+ grn_inspect(ctx, &buffer, DATA_PTR(self));
+ inspected = mrb_str_new(mrb, GRN_TEXT_VALUE(&buffer), GRN_TEXT_LEN(&buffer));
+ GRN_OBJ_FIN(ctx, &buffer);
+
+ return inspected;
+}
+
+void
+grn_mrb_obj_init(grn_ctx *ctx)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ struct RClass *module = data->module;
+ struct RClass *klass;
+
+ klass = mrb_define_class_under(mrb, module, "Object", mrb->object_class);
+ MRB_SET_INSTANCE_TT(klass, MRB_TT_DATA);
+ data->object_class = klass;
+
+ mrb_define_method(mrb, klass, "name", object_get_name, MRB_ARGS_NONE());
+ mrb_define_method(mrb, klass, "find_index",
+ object_find_index, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, klass, "grn_inspect",
+ object_grn_inspect, MRB_ARGS_NONE());
+
+ grn_mrb_load(ctx, "index_info.rb");
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_obj.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_obj.h
new file mode 100644
index 00000000000..31d53240f9e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_obj.h
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_OBJ_H
+#define GRN_MRB_OBJ_H
+
+#include "../ctx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_obj_init(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_OBJ_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_operator.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_operator.c
new file mode 100644
index 00000000000..5a18d2e7023
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_operator.c
@@ -0,0 +1,190 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+
+#include "mrb_operator.h"
+
+void
+grn_mrb_operator_init(grn_ctx *ctx)
+{
+ mrb_state *mrb = ctx->impl->mrb.state;
+ struct RClass *module = ctx->impl->mrb.module;
+ struct RClass *operator_module;
+
+ operator_module = mrb_define_module_under(mrb, module, "Operator");
+
+ mrb_define_const(mrb, operator_module, "PUSH",
+ mrb_fixnum_value(GRN_OP_PUSH));
+ mrb_define_const(mrb, operator_module, "POP",
+ mrb_fixnum_value(GRN_OP_POP));
+ mrb_define_const(mrb, operator_module, "NOP",
+ mrb_fixnum_value(GRN_OP_NOP));
+ mrb_define_const(mrb, operator_module, "CALL",
+ mrb_fixnum_value(GRN_OP_CALL));
+ mrb_define_const(mrb, operator_module, "INTERN",
+ mrb_fixnum_value(GRN_OP_INTERN));
+ mrb_define_const(mrb, operator_module, "GET_REF",
+ mrb_fixnum_value(GRN_OP_GET_REF));
+ mrb_define_const(mrb, operator_module, "GET_VALUE",
+ mrb_fixnum_value(GRN_OP_GET_VALUE));
+ mrb_define_const(mrb, operator_module, "AND",
+ mrb_fixnum_value(GRN_OP_AND));
+ mrb_define_const(mrb, operator_module, "AND_NOT",
+ mrb_fixnum_value(GRN_OP_AND_NOT));
+ mrb_define_const(mrb, operator_module, "OR",
+ mrb_fixnum_value(GRN_OP_OR));
+ mrb_define_const(mrb, operator_module, "ASSIGN",
+ mrb_fixnum_value(GRN_OP_ASSIGN));
+ mrb_define_const(mrb, operator_module, "STAR_ASSIGN",
+ mrb_fixnum_value(GRN_OP_STAR_ASSIGN));
+ mrb_define_const(mrb, operator_module, "SLASH_ASSIGN",
+ mrb_fixnum_value(GRN_OP_SLASH_ASSIGN));
+ mrb_define_const(mrb, operator_module, "MOD_ASSIGN",
+ mrb_fixnum_value(GRN_OP_MOD_ASSIGN));
+ mrb_define_const(mrb, operator_module, "PLUS_ASSIGN",
+ mrb_fixnum_value(GRN_OP_PLUS_ASSIGN));
+ mrb_define_const(mrb, operator_module, "MINUS_ASSIGN",
+ mrb_fixnum_value(GRN_OP_MINUS_ASSIGN));
+ mrb_define_const(mrb, operator_module, "SHIFTL_ASSIGN",
+ mrb_fixnum_value(GRN_OP_SHIFTL_ASSIGN));
+ mrb_define_const(mrb, operator_module, "SHIFTR_ASSIGN",
+ mrb_fixnum_value(GRN_OP_SHIFTR_ASSIGN));
+ mrb_define_const(mrb, operator_module, "SHIFTRR_ASSIGN",
+ mrb_fixnum_value(GRN_OP_SHIFTRR_ASSIGN));
+ mrb_define_const(mrb, operator_module, "AND_ASSIGN",
+ mrb_fixnum_value(GRN_OP_AND_ASSIGN));
+ mrb_define_const(mrb, operator_module, "XOR_ASSIGN",
+ mrb_fixnum_value(GRN_OP_XOR_ASSIGN));
+ mrb_define_const(mrb, operator_module, "OR_ASSIGN",
+ mrb_fixnum_value(GRN_OP_OR_ASSIGN));
+ mrb_define_const(mrb, operator_module, "JUMP",
+ mrb_fixnum_value(GRN_OP_JUMP));
+ mrb_define_const(mrb, operator_module, "CJUMP",
+ mrb_fixnum_value(GRN_OP_CJUMP));
+ mrb_define_const(mrb, operator_module, "COMMA",
+ mrb_fixnum_value(GRN_OP_COMMA));
+ mrb_define_const(mrb, operator_module, "BITWISE_OR",
+ mrb_fixnum_value(GRN_OP_BITWISE_OR));
+ mrb_define_const(mrb, operator_module, "BITWISE_XOR",
+ mrb_fixnum_value(GRN_OP_BITWISE_XOR));
+ mrb_define_const(mrb, operator_module, "BITWISE_AND",
+ mrb_fixnum_value(GRN_OP_BITWISE_AND));
+ mrb_define_const(mrb, operator_module, "BITWISE_NOT",
+ mrb_fixnum_value(GRN_OP_BITWISE_NOT));
+ mrb_define_const(mrb, operator_module, "EQUAL",
+ mrb_fixnum_value(GRN_OP_EQUAL));
+ mrb_define_const(mrb, operator_module, "NOT_EQUAL",
+ mrb_fixnum_value(GRN_OP_NOT_EQUAL));
+ mrb_define_const(mrb, operator_module, "LESS",
+ mrb_fixnum_value(GRN_OP_LESS));
+ mrb_define_const(mrb, operator_module, "GREATER",
+ mrb_fixnum_value(GRN_OP_GREATER));
+ mrb_define_const(mrb, operator_module, "LESS_EQUAL",
+ mrb_fixnum_value(GRN_OP_LESS_EQUAL));
+ mrb_define_const(mrb, operator_module, "GREATER_EQUAL",
+ mrb_fixnum_value(GRN_OP_GREATER_EQUAL));
+ mrb_define_const(mrb, operator_module, "IN",
+ mrb_fixnum_value(GRN_OP_IN));
+ mrb_define_const(mrb, operator_module, "MATCH",
+ mrb_fixnum_value(GRN_OP_MATCH));
+ mrb_define_const(mrb, operator_module, "NEAR",
+ mrb_fixnum_value(GRN_OP_NEAR));
+ mrb_define_const(mrb, operator_module, "NEAR2",
+ mrb_fixnum_value(GRN_OP_NEAR2));
+ mrb_define_const(mrb, operator_module, "SIMILAR",
+ mrb_fixnum_value(GRN_OP_SIMILAR));
+ mrb_define_const(mrb, operator_module, "TERM_EXTRACT",
+ mrb_fixnum_value(GRN_OP_TERM_EXTRACT));
+ mrb_define_const(mrb, operator_module, "SHIFTL",
+ mrb_fixnum_value(GRN_OP_SHIFTL));
+ mrb_define_const(mrb, operator_module, "SHIFTR",
+ mrb_fixnum_value(GRN_OP_SHIFTR));
+ mrb_define_const(mrb, operator_module, "SHIFTRR",
+ mrb_fixnum_value(GRN_OP_SHIFTRR));
+ mrb_define_const(mrb, operator_module, "PLUS",
+ mrb_fixnum_value(GRN_OP_PLUS));
+ mrb_define_const(mrb, operator_module, "MINUS",
+ mrb_fixnum_value(GRN_OP_MINUS));
+ mrb_define_const(mrb, operator_module, "STAR",
+ mrb_fixnum_value(GRN_OP_STAR));
+ mrb_define_const(mrb, operator_module, "SLASH",
+ mrb_fixnum_value(GRN_OP_SLASH));
+ mrb_define_const(mrb, operator_module, "MOD",
+ mrb_fixnum_value(GRN_OP_MOD));
+ mrb_define_const(mrb, operator_module, "DELETE",
+ mrb_fixnum_value(GRN_OP_DELETE));
+ mrb_define_const(mrb, operator_module, "INCR",
+ mrb_fixnum_value(GRN_OP_INCR));
+ mrb_define_const(mrb, operator_module, "DECR",
+ mrb_fixnum_value(GRN_OP_DECR));
+ mrb_define_const(mrb, operator_module, "INCR_POST",
+ mrb_fixnum_value(GRN_OP_INCR_POST));
+ mrb_define_const(mrb, operator_module, "DECR_POST",
+ mrb_fixnum_value(GRN_OP_DECR_POST));
+ mrb_define_const(mrb, operator_module, "NOT",
+ mrb_fixnum_value(GRN_OP_NOT));
+ mrb_define_const(mrb, operator_module, "ADJUST",
+ mrb_fixnum_value(GRN_OP_ADJUST));
+ mrb_define_const(mrb, operator_module, "EXACT",
+ mrb_fixnum_value(GRN_OP_EXACT));
+ mrb_define_const(mrb, operator_module, "LCP",
+ mrb_fixnum_value(GRN_OP_LCP));
+ mrb_define_const(mrb, operator_module, "PARTIAL",
+ mrb_fixnum_value(GRN_OP_PARTIAL));
+ mrb_define_const(mrb, operator_module, "UNSPLIT",
+ mrb_fixnum_value(GRN_OP_UNSPLIT));
+ mrb_define_const(mrb, operator_module, "PREFIX",
+ mrb_fixnum_value(GRN_OP_PREFIX));
+ mrb_define_const(mrb, operator_module, "SUFFIX",
+ mrb_fixnum_value(GRN_OP_SUFFIX));
+ mrb_define_const(mrb, operator_module, "GEO_DISTANCE1",
+ mrb_fixnum_value(GRN_OP_GEO_DISTANCE1));
+ mrb_define_const(mrb, operator_module, "GEO_DISTANCE2",
+ mrb_fixnum_value(GRN_OP_GEO_DISTANCE2));
+ mrb_define_const(mrb, operator_module, "GEO_DISTANCE3",
+ mrb_fixnum_value(GRN_OP_GEO_DISTANCE3));
+ mrb_define_const(mrb, operator_module, "GEO_DISTANCE4",
+ mrb_fixnum_value(GRN_OP_GEO_DISTANCE4));
+ mrb_define_const(mrb, operator_module, "GEO_WITHINP5",
+ mrb_fixnum_value(GRN_OP_GEO_WITHINP5));
+ mrb_define_const(mrb, operator_module, "GEO_WITHINP6",
+ mrb_fixnum_value(GRN_OP_GEO_WITHINP6));
+ mrb_define_const(mrb, operator_module, "GEO_WITHINP8",
+ mrb_fixnum_value(GRN_OP_GEO_WITHINP8));
+ mrb_define_const(mrb, operator_module, "OBJ_SEARCH",
+ mrb_fixnum_value(GRN_OP_OBJ_SEARCH));
+ mrb_define_const(mrb, operator_module, "EXPR_GET_VAR",
+ mrb_fixnum_value(GRN_OP_EXPR_GET_VAR));
+ mrb_define_const(mrb, operator_module, "TABLE_CREATE",
+ mrb_fixnum_value(GRN_OP_TABLE_CREATE));
+ mrb_define_const(mrb, operator_module, "TABLE_SELECT",
+ mrb_fixnum_value(GRN_OP_TABLE_SELECT));
+ mrb_define_const(mrb, operator_module, "TABLE_SORT",
+ mrb_fixnum_value(GRN_OP_TABLE_SORT));
+ mrb_define_const(mrb, operator_module, "TABLE_GROUP",
+ mrb_fixnum_value(GRN_OP_TABLE_GROUP));
+ mrb_define_const(mrb, operator_module, "JSON_PUT",
+ mrb_fixnum_value(GRN_OP_JSON_PUT));
+ mrb_define_const(mrb, operator_module, "GET_MEMBER",
+ mrb_fixnum_value(GRN_OP_GET_MEMBER));
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_operator.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_operator.h
new file mode 100644
index 00000000000..2650cc9421d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_operator.h
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_OPERATOR_H
+#define GRN_MRB_OPERATOR_H
+
+#include "../ctx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_operator_init(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_OPERATOR_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_procedure.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_procedure.c
new file mode 100644
index 00000000000..fa9c677f2e6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_procedure.c
@@ -0,0 +1,58 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+
+#include "mrb_procedure.h"
+
+static struct mrb_data_type mrb_grn_procedure_type = {
+ "Groonga::Procedure",
+ NULL
+};
+
+static mrb_value
+mrb_grn_procedure_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mrb_procedure_ptr;
+
+ mrb_get_args(mrb, "o", &mrb_procedure_ptr);
+ DATA_TYPE(self) = &mrb_grn_procedure_type;
+ DATA_PTR(self) = mrb_cptr(mrb_procedure_ptr);
+ return self;
+}
+
+void
+grn_mrb_procedure_init(grn_ctx *ctx)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ struct RClass *module = data->module;
+ struct RClass *object_class = data->object_class;
+ struct RClass *klass;
+
+ klass = mrb_define_class_under(mrb, module, "Procedure", object_class);
+ MRB_SET_INSTANCE_TT(klass, MRB_TT_DATA);
+ mrb_define_method(mrb, klass, "initialize",
+ mrb_grn_procedure_initialize, MRB_ARGS_REQ(1));
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_procedure.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_procedure.h
new file mode 100644
index 00000000000..16a4b32895c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_procedure.h
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_PROCEDURE_H
+#define GRN_MRB_PROCEDURE_H
+
+#include "../ctx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_procedure_init(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_PROCEDURE_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_variable_size_column.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_variable_size_column.c
new file mode 100644
index 00000000000..9073b326f85
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_variable_size_column.c
@@ -0,0 +1,60 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+
+#include "mrb_variable_size_column.h"
+
+static struct mrb_data_type mrb_grn_variable_size_column_type = {
+ "Groonga::VariableSizeColumn",
+ NULL
+};
+
+static mrb_value
+mrb_grn_variable_size_column_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mrb_variable_size_column_ptr;
+
+ mrb_get_args(mrb, "o", &mrb_variable_size_column_ptr);
+ DATA_TYPE(self) = &mrb_grn_variable_size_column_type;
+ DATA_PTR(self) = mrb_cptr(mrb_variable_size_column_ptr);
+ return self;
+}
+
+void
+grn_mrb_variable_size_column_init(grn_ctx *ctx)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ struct RClass *module = data->module;
+ struct RClass *column_class;
+ struct RClass *klass;
+
+ column_class = mrb_class_get_under(mrb, module, "Column");
+ klass = mrb_define_class_under(mrb, module,
+ "VariableSizeColumn", column_class);
+ MRB_SET_INSTANCE_TT(klass, MRB_TT_DATA);
+ mrb_define_method(mrb, klass, "initialize",
+ mrb_grn_variable_size_column_initialize, MRB_ARGS_REQ(1));
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_variable_size_column.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_variable_size_column.h
new file mode 100644
index 00000000000..d120f737671
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_variable_size_column.h
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_VARIABLE_SIZE_COLUMN_H
+#define GRN_MRB_VARIABLE_SIZE_COLUMN_H
+
+#include "../ctx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_variable_size_column_init(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_VARIABLE_SIZE_COLUMN_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_void.c b/storage/mroonga/vendor/groonga/lib/mrb/mrb_void.c
new file mode 100644
index 00000000000..976c472be92
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_void.c
@@ -0,0 +1,59 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "../ctx_impl.h"
+
+#ifdef GRN_WITH_MRUBY
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/variable.h>
+#include <mruby/data.h>
+
+#include "../db.h"
+#include "mrb_void.h"
+
+static struct mrb_data_type mrb_grn_void_type = {
+ "Groonga::Void",
+ NULL
+};
+
+static mrb_value
+mrb_grn_void_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_value mrb_void_ptr;
+
+ mrb_get_args(mrb, "o", &mrb_void_ptr);
+ DATA_TYPE(self) = &mrb_grn_void_type;
+ DATA_PTR(self) = mrb_cptr(mrb_void_ptr);
+ return self;
+}
+
+void
+grn_mrb_void_init(grn_ctx *ctx)
+{
+ grn_mrb_data *data = &(ctx->impl->mrb);
+ mrb_state *mrb = data->state;
+ struct RClass *module = data->module;
+ struct RClass *klass;
+
+ klass = mrb_define_class_under(mrb, module, "Void", mrb->object_class);
+ MRB_SET_INSTANCE_TT(klass, MRB_TT_DATA);
+ mrb_define_method(mrb, klass, "initialize",
+ mrb_grn_void_initialize, MRB_ARGS_REQ(1));
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/mrb_void.h b/storage/mroonga/vendor/groonga/lib/mrb/mrb_void.h
new file mode 100644
index 00000000000..a766cecb0b7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/mrb_void.h
@@ -0,0 +1,34 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_MRB_VOID_H
+#define GRN_MRB_VOID_H
+
+#include "../ctx.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void grn_mrb_void_init(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_MRB_VOID_H */
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/Makefile.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/Makefile.am
new file mode 100644
index 00000000000..631923e2e06
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/Makefile.am
@@ -0,0 +1,9 @@
+include sources.am
+
+EXTRA_DIST = \
+ test/empty.rb \
+ $(RUBY_SCRIPT_FILES)
+
+if WITH_MRUBY
+nobase_ruby_scripts_DATA = $(RUBY_SCRIPT_FILES)
+endif
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/backtrace_entry.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/backtrace_entry.rb
new file mode 100644
index 00000000000..68ea9e4b6f2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/backtrace_entry.rb
@@ -0,0 +1,20 @@
+module Groonga
+ class BacktraceEntry
+ class << self
+ def parse(entry)
+ match_data = /:(\d+):?/.match(entry)
+ file = match_data.pre_match
+ line = match_data[1].to_i
+ method = match_data.post_match.gsub(/\Ain /, "")
+ new(file, line, method)
+ end
+ end
+
+ attr_reader :file, :line, :method
+ def initialize(file, line, method)
+ @file = file
+ @line = line
+ @method = method
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/context.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context.rb
new file mode 100644
index 00000000000..8ebe2c913b4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context.rb
@@ -0,0 +1,31 @@
+module Groonga
+ class Context
+ def guard(fallback=nil)
+ begin
+ yield
+ rescue => error
+ logger.log_error(error)
+ fallback
+ end
+ end
+
+ def logger
+ @logger ||= Logger.new
+ end
+
+ def record_error(rc, error)
+ rc = RC.find(rc) if rc.is_a?(Symbol)
+ self.rc = rc.to_i
+ self.error_level = ErrorLevel.find(:error).to_i
+
+ backtrace = error.backtrace
+ entry = BacktraceEntry.parse(backtrace.first)
+ self.error_file = entry.file
+ self.error_line = entry.line
+ self.error_method = entry.method
+ self.error_message = error.message
+
+ logger.log_error(error)
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/error_level.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/error_level.rb
new file mode 100644
index 00000000000..c0685f16eef
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/error_level.rb
@@ -0,0 +1,30 @@
+module Groonga
+ class Context
+ class ErrorLevel
+ @@names = {}
+
+ class << self
+ def find(name)
+ @@names[name]
+ end
+ end
+
+ attr_reader :name
+ def initialize(name, level)
+ @@names[name] = self
+ @name = name
+ @level = level
+ end
+
+ def to_i
+ @level
+ end
+
+ EMERGENCY = new(:emergency, 1)
+ ALERT = new(:alert, 2)
+ CRITICAL = new(:critical, 3)
+ ERROR = new(:error, 4)
+ WARNING = new(:warning, 5)
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/rc.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/rc.rb
new file mode 100644
index 00000000000..2863c7f6195
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/context/rc.rb
@@ -0,0 +1,99 @@
+module Groonga
+ class Context
+ class RC
+ @@names = {}
+
+ class << self
+ def find(name)
+ @@names[name]
+ end
+ end
+
+ attr_reader :name
+ def initialize(name, code)
+ @@names[name] = self
+ @name = name
+ @code = code
+ end
+
+ def to_i
+ @code
+ end
+
+ SUCCESS = new(:success, 0)
+ END_OF_DATA = new(:end_of_data, 1)
+ UNKNOWN_ERROR = new(:unknown_error, -1)
+ OPERATION_NOT_PERMITTED = new(:operation_not_permitted, -2)
+ NO_SUCH_FILE_OR_DIRECTORY = new(:no_such_file_or_directory, -3)
+ NO_SUCH_PROCESS = new(:no_such_process, -4)
+ INTERRUPTED_FUNCTION_CALL = new(:interrupted_function_call, -5)
+ INPUT_OUTPUT_ERROR = new(:input_output_error, -6)
+ NO_SUCH_DEVICE_OR_ADDRESS = new(:no_such_device_or_address, -7)
+ ARG_LIST_TOO_LONG = new(:arg_list_too_long, -8)
+ EXEC_FORMAT_ERROR = new(:exec_format_error, -9)
+ BAD_FILE_DESCRIPTOR = new(:bad_file_descriptor, -10)
+ NO_CHILD_PROCESSES = new(:no_child_processes, -11)
+ RESOURCE_TEMPORARILY_UNAVAILABLE = new(:resource_temporarily_unavailable, -12)
+ NOT_ENOUGH_SPACE = new(:not_enough_space, -13)
+ PERMISSION_DENIED = new(:permission_denied, -14)
+ BAD_ADDRESS = new(:bad_address, -15)
+ RESOURCE_BUSY = new(:resource_busy, -16)
+ FILE_EXISTS = new(:file_exists, -17)
+ IMPROPER_LINK = new(:improper_link, -18)
+ NO_SUCH_DEVICE = new(:no_such_device, -19)
+ NOT_A_DIRECTORY = new(:not_a_directory, -20)
+ IS_A_DIRECTORY = new(:is_a_directory, -21)
+ INVALID_ARGUMENT = new(:invalid_argument, -22)
+ TOO_MANY_OPEN_FILES_IN_SYSTEM = new(:too_many_open_files_in_system, -23)
+ TOO_MANY_OPEN_FILES = new(:too_many_open_files, -24)
+ INAPPROPRIATE_IO_CONTROL_OPERATION = new(:inappropriate_io_control_operation, -25)
+ FILE_TOO_LARGE = new(:file_too_large, -26)
+ NO_SPACE_LEFT_ON_DEVICE = new(:no_space_left_on_device, -27)
+ INVALID_SEEK = new(:invalid_seek, -28)
+ READ_ONLY_FILE_SYSTEM = new(:read_only_file_system, -29)
+ TOO_MANY_LINKS = new(:too_many_links, -30)
+ BROKEN_PIPE = new(:broken_pipe, -31)
+ DOMAIN_ERROR = new(:domain_error, -32)
+ RESULT_TOO_LARGE = new(:result_too_large, -33)
+ RESOURCE_DEADLOCK_AVOIDED = new(:resource_deadlock_avoided, -34)
+ NO_MEMORY_AVAILABLE = new(:no_memory_available, -35)
+ FILENAME_TOO_LONG = new(:filename_too_long, -36)
+ NO_LOCKS_AVAILABLE = new(:no_locks_available, -37)
+ FUNCTION_NOT_IMPLEMENTED = new(:function_not_implemented, -38)
+ DIRECTORY_NOT_EMPTY = new(:directory_not_empty, -39)
+ ILLEGAL_BYTE_SEQUENCE = new(:illegal_byte_sequence, -40)
+ SOCKET_NOT_INITIALIZED = new(:socket_not_initialized, -41)
+ OPERATION_WOULD_BLOCK = new(:operation_would_block, -42)
+ ADDRESS_IS_NOT_AVAILABLE = new(:address_is_not_available, -43)
+ NETWORK_IS_DOWN = new(:network_is_down, -44)
+ NO_BUFFER = new(:no_buffer, -45)
+ SOCKET_IS_ALREADY_CONNECTED = new(:socket_is_already_connected, -46)
+ SOCKET_IS_NOT_CONNECTED = new(:socket_is_not_connected, -47)
+ SOCKET_IS_ALREADY_SHUTDOWNED = new(:socket_is_already_shutdowned, -48)
+ OPERATION_TIMEOUT = new(:operation_timeout, -49)
+ CONNECTION_REFUSED = new(:connection_refused, -50)
+ RANGE_ERROR = new(:range_error, -51)
+ TOKENIZER_ERROR = new(:tokenizer_error, -52)
+ FILE_CORRUPT = new(:file_corrupt, -53)
+ INVALID_FORMAT = new(:invalid_format, -54)
+ OBJECT_CORRUPT = new(:object_corrupt, -55)
+ TOO_MANY_SYMBOLIC_LINKS = new(:too_many_symbolic_links, -56)
+ NOT_SOCKET = new(:not_socket, -57)
+ OPERATION_NOT_SUPPORTED = new(:operation_not_supported, -58)
+ ADDRESS_IS_IN_USE = new(:address_is_in_use, -59)
+ ZLIB_ERROR = new(:zlib_error, -60)
+ LZO_ERROR = new(:lzo_error, -61)
+ STACK_OVER_FLOW = new(:stack_over_flow, -62)
+ SYNTAX_ERROR = new(:syntax_error, -63)
+ RETRY_MAX = new(:retry_max, -64)
+ INCOMPATIBLE_FILE_FORMAT = new(:incompatible_file_format, -65)
+ UPDATE_NOT_ALLOWED = new(:update_not_allowed, -66)
+ TOO_SMALL_OFFSET = new(:too_small_offset, -67)
+ TOO_LARGE_OFFSET = new(:too_large_offset, -68)
+ TOO_SMALL_LIMIT = new(:too_small_limit, -69)
+ CAS_ERROR = new(:cas_error, -70)
+ UNSUPPORTED_COMMAND_VERSION = new(:unsupported_command_version, -71)
+ NORMALIZER_ERROR = new(:normalizer_error, -72)
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/eval_context.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/eval_context.rb
new file mode 100644
index 00000000000..546c130d9b5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/eval_context.rb
@@ -0,0 +1,13 @@
+module Groonga
+ class EvalContext
+ def method_missing(id, *args, &block)
+ return super unless args.empty?
+ return super if block_given?
+
+ object = Context.instance[id.to_s]
+ return super if object.nil?
+
+ object
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression.rb
new file mode 100644
index 00000000000..29290e207f2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/expression.rb
@@ -0,0 +1,13 @@
+module Groonga
+ class Expression
+ def build_scan_info(op, size)
+ begin
+ builder = ScanInfoBuilder.new(self, op, size)
+ builder.build
+ rescue => error
+ Context.instance.record_error(:invalid_argument, error)
+ nil
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/index_info.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/index_info.rb
new file mode 100644
index 00000000000..cf8336e50c7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/index_info.rb
@@ -0,0 +1,10 @@
+module Groonga
+ class IndexInfo
+ attr_reader :index
+ attr_reader :section_id
+ def initialize(index, section_id)
+ @index = index
+ @section_id = section_id
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger.rb
new file mode 100644
index 00000000000..06f99c14fc3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger.rb
@@ -0,0 +1,27 @@
+module Groonga
+ class Logger
+ def log_error(error)
+ log_level = Level::ERROR.to_i
+
+ message = "#{error.class}: #{error.message}"
+ backtrace = error.backtrace
+ first_raw_entry = backtrace.first
+ if first_raw_entry
+ first_entry = BacktraceEntry.parse(first_raw_entry)
+ file = first_entry.file
+ line = first_entry.line
+ method = first_entry.method
+ else
+ file = ""
+ line = 0
+ method = ""
+ end
+ log(log_level, file, line, method, message)
+
+ backtrace.each do |raw_entry|
+ entry = BacktraceEntry.parse(raw_entry)
+ log(log_level, entry.file, entry.line, entry.method, raw_entry)
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/level.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/level.rb
new file mode 100644
index 00000000000..b7660993824
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/logger/level.rb
@@ -0,0 +1,32 @@
+module Groonga
+ class Logger
+ class Level
+ @@names = {}
+ def self.find(name)
+ @@names[name]
+ end
+
+ attr_reader :name
+ def initialize(name, level)
+ @@names[name] = self
+ @name = name
+ @level = level
+ end
+
+ def to_i
+ @level
+ end
+
+ NONE = new(:none, 0)
+ EMERG = new(:emerg, 1)
+ ALERT = new(:alert, 2)
+ CRIT = new(:crit, 3)
+ ERROR = new(:error, 4)
+ WARNING = new(:warning, 5)
+ NOTICE = new(:notice, 6)
+ INFO = new(:info, 7)
+ DEBUG = new(:debug, 8)
+ DUMP = new(:dump, 9)
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/object.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/object.rb
new file mode 100644
index 00000000000..f1cc1a6aa79
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/object.rb
@@ -0,0 +1,7 @@
+module Groonga
+ class Object
+ def inspect
+ super[0..-2] + ": #{grn_inspect}>"
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info.rb
new file mode 100644
index 00000000000..9977d9bd8d7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info.rb
@@ -0,0 +1,27 @@
+module Groonga
+ class ScanInfo
+ module Flags
+ ACCESSOR = 0x01
+ PUSH = 0x02
+ POP = 0x04
+ PRE_CONST = 0x08
+ end
+
+ def apply(data)
+ self.op = data.op
+ self.logical_op = data.logical_op
+ self.end = data.end
+ self.query = data.query
+ self.flags = data.flags
+ if data.max_interval
+ self.max_interval = data.max_interval
+ end
+ data.args.each do |arg|
+ push_arg(arg)
+ end
+ data.indexes.each do |index, section_id, weight|
+ put_index(index, section_id, weight)
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_builder.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_builder.rb
new file mode 100644
index 00000000000..0d88f4027aa
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_builder.rb
@@ -0,0 +1,243 @@
+module Groonga
+ # TODO: Move me
+ class ExpressionCode
+ module Flags
+ RELATIONAL_EXPRESSION = 0x01
+ end
+ end
+
+ class ScanInfoBuilder
+ module Status
+ START = 0
+ VAR = 1
+ COL1 = 2
+ COL2 = 3
+ CONST = 4
+ end
+
+ def initialize(expression, operator, size)
+ @data_list = []
+ @expression = expression
+ @operator = operator
+ @size = size
+ end
+
+ RELATION_OPERATORS = [
+ Operator::MATCH,
+ Operator::NEAR,
+ Operator::NEAR2,
+ Operator::SIMILAR,
+ Operator::PREFIX,
+ Operator::SUFFIX,
+ Operator::EQUAL,
+ Operator::NOT_EQUAL,
+ Operator::LESS,
+ Operator::GREATER,
+ Operator::LESS_EQUAL,
+ Operator::GREATER_EQUAL,
+ Operator::GEO_WITHINP5,
+ Operator::GEO_WITHINP6,
+ Operator::GEO_WITHINP8,
+ Operator::TERM_EXTRACT,
+ ]
+
+ LOGICAL_OPERATORS = [
+ Operator::AND,
+ Operator::OR,
+ Operator::AND_NOT,
+ Operator::ADJUST,
+ ]
+ def build
+ return nil unless valid?
+
+ status = Status::START
+ variable = @expression.get_var_by_offset(0)
+ data = nil
+ codes = @expression.codes
+ n_codes = codes.size
+ codes.each_with_index do |code, i|
+ case code.op
+ when *RELATION_OPERATORS
+ status = Status::START
+ data.op = code.op
+ data.end = i
+ data.match_resolve_index
+ @data_list << data
+ data = nil
+ when *LOGICAL_OPERATORS
+ put_logical_op(code.op, i)
+ # TODO: rescue and return nil
+ status = Status::START
+ when Operator::PUSH
+ data ||= ScanInfoData.new(i)
+ if code.value == variable
+ status = Status::VAR
+ else
+ data.args << code.value
+ if status == Status::START
+ data.flags |= ScanInfo::Flags::PRE_CONST
+ end
+ status = Status::CONST
+ end
+ when Operator::GET_VALUE
+ case status
+ when Status::START
+ data ||= ScanInfoData.new(i)
+ status = Status::COL1
+ data.args << code.value
+ when Status::CONST, Status::VAR
+ status = Status::COL1
+ data.args << code.value
+ when Status::COL1
+ raise "invalid expression: can't use column as a value: <#{code.value.name}>: <#{@expression.grn_inspect}>"
+ status = Status::COL2
+ when Status::COL2
+ # Do nothing
+ end
+ when Operator::CALL
+ data ||= ScanInfoData.new(i)
+ if (code.flags & ExpressionCode::Flags::RELATIONAL_EXPRESSION) != 0 or
+ (i + 1) == n_codes
+ status = Status::START
+ data.op = code.op
+ data.end = i
+ data.call_relational_resolve_indexes
+ @data_list << data
+ data = nil
+ else
+ status = Status::COL2
+ end
+ end
+ end
+
+ if @operator == Operator::OR and @size == 0
+ first_data = @data_list.first
+ if (first_data.flags & ScanInfo::Flags::PUSH) == 0 or
+ first_data.logical_op != @operator
+ raise "invalid expr"
+ else
+ first_data.flags &= ~ScanInfo::Flags::PUSH
+ first_data.logical_op = @operator
+ end
+ else
+ put_logical_op(@operator, n_codes)
+ end
+
+ @data_list
+ end
+
+ private
+ def valid?
+ n_relation_expressions = 0
+ n_logical_expressions = 0
+ status = Status::START
+ variable = @expression.get_var_by_offset(0)
+ codes = @expression.codes
+ codes.each do |code|
+ case code.op
+ when *RELATION_OPERATORS
+ return false if status < Status::COL1
+ return false if status > Status::CONST
+ status = Status::START
+ n_relation_expressions += 1
+ when *LOGICAL_OPERATORS
+ return false if status != Status::START
+ n_logical_expressions += 1
+ return false if n_logical_expressions >= n_relation_expressions
+ when Operator::PUSH
+ if code.value == variable
+ status = Status::VAR
+ else
+ status = Status::CONST
+ end
+ when Operator::GET_VALUE
+ case status
+ when Status::START, Status::CONST, Status::VAR
+ status = Status::COL1
+ when Status::COL1
+ status = Status::COL2
+ when Status::COL2
+ # Do nothing
+ else
+ return false
+ end
+ when Operator::CALL
+ if (code.flags & ExpressionCode::Flags::RELATIONAL_EXPRESSION) != 0 or
+ code == codes.last
+ status = Status::START
+ n_relation_expressions += 1
+ else
+ status = Status::COL2
+ end
+ else
+ return false
+ end
+ end
+
+ return false if status != Status::START
+ return false if n_relation_expressions != (n_logical_expressions + 1)
+
+ true
+ end
+
+ def put_logical_op(operator, start)
+ n_parens = 1
+ n_dif_ops = 0
+ r = 0
+ j = @data_list.size
+ while j > 0
+ j -= 1
+ data = @data_list[j]
+ if (data.flags & ScanInfo::Flags::POP) != 0
+ n_dif_ops += 1
+ n_parens += 1
+ else
+ if (data.flags & ScanInfo::Flags::PUSH) != 0
+ n_parens -= 1
+ if n_parens == 0
+ if r == 0
+ if n_dif_ops > 0
+ if j > 0 and operator != Operator::AND_NOT
+ n_parens = 1
+ n_dif_ops = 0
+ r = j
+ else
+ new_data = ScanInfoData.new(start)
+ new_data.flags = ScanInfo::Flags::POP
+ new_data.logical_op = operator
+ @data_list << new_data
+ end
+ else
+ data.flags &= ~ScanInfo::Flags::PUSH
+ data.logical_op = operator
+ end
+ else
+ if n_dif_ops > 0
+ new_data = ScanInfoData.new(start)
+ new_data.flags = ScanInfo::Flags::POP
+ new_data.logical_op = operator
+ @data_list << new_data
+ else
+ data.flags &= ~ScanInfo::Flags::PUSH
+ data.logical_op = operator
+ @data_list =
+ @data_list[0...j] +
+ @data_list[r..-1] +
+ @data_list[j...r]
+ end
+ end
+ end
+ else
+ if operator == Operator::AND_NOT or operator != data.logical_op
+ n_dif_ops += 1
+ end
+ end
+ end
+
+ if j < 0
+ raise GRN_INVALID_ARGUMENT.new("unmatched nesting level")
+ end
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_data.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_data.rb
new file mode 100644
index 00000000000..87eb9ed4937
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/scan_info_data.rb
@@ -0,0 +1,171 @@
+module Groonga
+ class ScanInfoData
+ attr_accessor :start
+ attr_accessor :end
+ attr_accessor :op
+ attr_accessor :logical_op
+ attr_accessor :query
+ attr_accessor :args
+ attr_accessor :indexes
+ attr_accessor :flags
+ attr_accessor :max_interval
+ def initialize(start)
+ @start = start
+ @end = 0
+ @op = 0
+ @logical_op = Operator::OR
+ @query = nil
+ @args = []
+ @indexes = []
+ @flags = ScanInfo::Flags::PUSH
+ @max_interval = nil
+ end
+
+ def match_resolve_index
+ if near_search?
+ match_near_resolve_index
+ else
+ match_generic_resolve_index
+ end
+ end
+
+ def call_relational_resolve_indexes
+ # better index resolving framework for functions should be implemented
+ @args.each do |arg|
+ call_relational_resolve_index(arg)
+ end
+ end
+
+ private
+ def near_search?
+ (@op == Operator::NEAR or @op == Operator::NEAR2) and @args.size == 3
+ end
+
+ def match_near_resolve_index
+ arg = @args[0]
+ case arg
+ when Expression
+ match_resolve_index_expression(arg)
+ when Accessor
+ match_resolve_index_accessor(arg)
+ when Object
+ match_resolve_index_db_obj(arg)
+ else
+ message =
+ "The first argument of NEAR/NEAR2 must be Expression, Accessor or Object: #{arg.class}"
+ raise message
+ end
+
+ self.query = @args[1]
+ self.max_interval = @args[2].value
+ end
+
+ def match_generic_resolve_index
+ @args.each do |arg|
+ case arg
+ when Expression
+ match_resolve_index_expression(arg)
+ when Accessor
+ match_resolve_index_accessor(arg)
+ when Object
+ match_resolve_index_db_obj(arg)
+ else
+ self.query = arg
+ end
+ end
+ end
+
+ def match_resolve_index_expression(expression)
+ codes = expression.codes
+ n_codes = codes.size
+ i = 0
+ while i < n_codes
+ code = codes[i]
+ value = code.value
+ case value
+ when Accessor
+ match_resolve_index_expression_accessor(code)
+ when FixedSizeColumn, VariableSizeColumn
+ match_resolve_index_expression_data_column(code)
+ when IndexColumn
+ section_id = 0
+ rest_n_codes = n_codes - i
+ if rest_n_codes >= 2 and
+ codes[i + 1].value.is_a?(Bulk) and
+ codes[i + 1].value.domain == ID::UINT32 and
+ codes[i + 2].op == Operator::GET_MEMBER
+ section_id = codes[i + 1].value.value + 1
+ code = codes[i + 2]
+ i += 2
+ end
+ put_index(value, section_id, code.weight)
+ end
+ i += 1
+ end
+ end
+
+ def match_resolve_index_expression_accessor(expr_code)
+ accessor = expr_code.value
+ self.flags |= ScanInfo::Flags::ACCESSOR
+ index_info = accessor.find_index(op)
+ return if index_info.nil?
+ if accessor.next
+ put_index(accessor, index_info.section_id, expr_code.weight)
+ else
+ put_index(index_info.index, index_info.section_id, expr_code.weight)
+ end
+ end
+
+ def match_resolve_index_expression_data_column(expr_code)
+ column = expr_code.value
+ index_info = column.find_index(op)
+ return if index_info.nil?
+ put_index(index_info.index, index_info.section_id, expr_code.weight)
+ end
+
+ def match_resolve_index_db_obj(db_obj)
+ index_info = db_obj.find_index(op)
+ return if index_info.nil?
+ put_index(index_info.index, index_info.section_id, 1)
+ end
+
+ def match_resolve_index_accessor(accessor)
+ self.flags |= ScanInfo::Flags::ACCESSOR
+ index_info = accessor.find_index(op)
+ return if index_info.nil?
+ if accessor.next
+ put_index(accessor, index_info.section_id, 1)
+ else
+ put_index(index_info.index, index_info.section_id, 1)
+ end
+ end
+
+ def call_relational_resolve_index(object)
+ case object
+ when Accessor
+ call_relational_resolve_index_accessor(object)
+ when Bulk
+ self.query = object
+ else
+ call_relational_resolve_index_db_obj(object)
+ end
+ end
+
+ def call_relational_resolve_index_db_obj(db_obj)
+ index_info = db_obj.find_index(op)
+ return if index_info.nil?
+ put_index(index_info.index, index_info.section_id, 1)
+ end
+
+ def call_relational_resolve_index_accessor(accessor)
+ self.flags |= ScanInfo::Flags::ACCESSOR
+ index_info = accessor.find_index(op)
+ return if index_info.nil?
+ put_index(index_info.index, index_info.section_id, 1)
+ end
+
+ def put_index(index, section_id, weight)
+ @indexes << [index, section_id, weight]
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/sources.am b/storage/mroonga/vendor/groonga/lib/mrb/scripts/sources.am
new file mode 100644
index 00000000000..93ade1627e6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/sources.am
@@ -0,0 +1,13 @@
+RUBY_SCRIPT_FILES = \
+ backtrace_entry.rb \
+ context.rb \
+ context/error_level.rb \
+ context/rc.rb \
+ eval_context.rb \
+ expression.rb \
+ index_info.rb \
+ logger.rb \
+ logger/level.rb \
+ scan_info.rb \
+ scan_info_builder.rb \
+ scan_info_data.rb
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/scripts/test/empty.rb b/storage/mroonga/vendor/groonga/lib/mrb/scripts/test/empty.rb
new file mode 100644
index 00000000000..123002d4b2b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/scripts/test/empty.rb
@@ -0,0 +1 @@
+# This file is just for test.
diff --git a/storage/mroonga/vendor/groonga/lib/mrb/sources.am b/storage/mroonga/vendor/groonga/lib/mrb/sources.am
new file mode 100644
index 00000000000..858b2205d4f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/mrb/sources.am
@@ -0,0 +1,31 @@
+libgrnmrb_la_SOURCES = \
+ mrb_accessor.c \
+ mrb_accessor.h \
+ mrb_bulk.c \
+ mrb_bulk.h \
+ mrb_column.c \
+ mrb_column.h \
+ mrb_converter.c \
+ mrb_converter.h \
+ mrb_ctx.c \
+ mrb_ctx.h \
+ mrb_expr.c \
+ mrb_expr.h \
+ mrb_fixed_size_column.c \
+ mrb_fixed_size_column.h \
+ mrb_id.c \
+ mrb_id.h \
+ mrb_index_column.c \
+ mrb_index_column.h \
+ mrb_logger.c \
+ mrb_logger.h \
+ mrb_obj.c \
+ mrb_obj.h \
+ mrb_operator.c \
+ mrb_operator.h \
+ mrb_procedure.c \
+ mrb_procedure.h \
+ mrb_variable_size_column.c \
+ mrb_variable_size_column.h \
+ mrb_void.c \
+ mrb_void.h
diff --git a/storage/mroonga/vendor/groonga/lib/nfkc-custom-rules.txt b/storage/mroonga/vendor/groonga/lib/nfkc-custom-rules.txt
new file mode 100644
index 00000000000..496751a5581
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/nfkc-custom-rules.txt
@@ -0,0 +1 @@
+〜 ~
diff --git a/storage/mroonga/vendor/groonga/lib/nfkc.c b/storage/mroonga/vendor/groonga/lib/nfkc.c
new file mode 100644
index 00000000000..252fb210e6e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/nfkc.c
@@ -0,0 +1,80249 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2010 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+don't edit this file by hand. it generated automatically by nfkc.rb
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <groonga/nfkc.h>
+
+#ifdef GRN_WITH_NFKC
+
+grn_char_type
+grn_nfkc_char_type(const unsigned char *str)
+{
+switch (str[0]) {
+case 0x01 :
+case 0x02 :
+case 0x03 :
+case 0x04 :
+case 0x05 :
+case 0x06 :
+case 0x07 :
+case 0x08 :
+case 0x09 :
+case 0x0A :
+case 0x0B :
+case 0x0C :
+case 0x0D :
+case 0x0E :
+case 0x0F :
+case 0x10 :
+case 0x11 :
+case 0x12 :
+case 0x13 :
+case 0x14 :
+case 0x15 :
+case 0x16 :
+case 0x17 :
+case 0x18 :
+case 0x19 :
+case 0x1A :
+case 0x1B :
+case 0x1C :
+case 0x1D :
+case 0x1E :
+case 0x1F :
+case 0x20 :
+ return GRN_CHAR_OTHERS;
+ break;
+case 0x21 :
+case 0x22 :
+case 0x23 :
+case 0x24 :
+case 0x25 :
+case 0x26 :
+case 0x27 :
+case 0x28 :
+case 0x29 :
+case 0x2A :
+case 0x2B :
+case 0x2C :
+case 0x2D :
+case 0x2E :
+case 0x2F :
+ return GRN_CHAR_SYMBOL;
+ break;
+case 0x30 :
+case 0x31 :
+case 0x32 :
+case 0x33 :
+case 0x34 :
+case 0x35 :
+case 0x36 :
+case 0x37 :
+case 0x38 :
+case 0x39 :
+ return GRN_CHAR_DIGIT;
+ break;
+case 0x3A :
+case 0x3B :
+case 0x3C :
+case 0x3D :
+case 0x3E :
+case 0x3F :
+case 0x40 :
+ return GRN_CHAR_SYMBOL;
+ break;
+case 0x41 :
+case 0x42 :
+case 0x43 :
+case 0x44 :
+case 0x45 :
+case 0x46 :
+case 0x47 :
+case 0x48 :
+case 0x49 :
+case 0x4A :
+case 0x4B :
+case 0x4C :
+case 0x4D :
+case 0x4E :
+case 0x4F :
+case 0x50 :
+case 0x51 :
+case 0x52 :
+case 0x53 :
+case 0x54 :
+case 0x55 :
+case 0x56 :
+case 0x57 :
+case 0x58 :
+case 0x59 :
+case 0x5A :
+ return GRN_CHAR_ALPHA;
+ break;
+case 0x5B :
+case 0x5C :
+case 0x5D :
+case 0x5E :
+case 0x5F :
+case 0x60 :
+ return GRN_CHAR_SYMBOL;
+ break;
+case 0x61 :
+case 0x62 :
+case 0x63 :
+case 0x64 :
+case 0x65 :
+case 0x66 :
+case 0x67 :
+case 0x68 :
+case 0x69 :
+case 0x6A :
+case 0x6B :
+case 0x6C :
+case 0x6D :
+case 0x6E :
+case 0x6F :
+case 0x70 :
+case 0x71 :
+case 0x72 :
+case 0x73 :
+case 0x74 :
+case 0x75 :
+case 0x76 :
+case 0x77 :
+case 0x78 :
+case 0x79 :
+case 0x7A :
+ return GRN_CHAR_ALPHA;
+ break;
+case 0x7B :
+case 0x7C :
+case 0x7D :
+case 0x7E :
+ return GRN_CHAR_SYMBOL;
+ break;
+case 0x7F :
+case 0x80 :
+case 0x81 :
+case 0x82 :
+case 0x83 :
+case 0x84 :
+case 0x85 :
+case 0x86 :
+case 0x87 :
+case 0x88 :
+case 0x89 :
+case 0x8A :
+case 0x8B :
+case 0x8C :
+case 0x8D :
+case 0x8E :
+case 0x8F :
+case 0x90 :
+case 0x91 :
+case 0x92 :
+case 0x93 :
+case 0x94 :
+case 0x95 :
+case 0x96 :
+case 0x97 :
+case 0x98 :
+case 0x99 :
+case 0x9A :
+case 0x9B :
+case 0x9C :
+case 0x9D :
+case 0x9E :
+case 0x9F :
+case 0xA0 :
+case 0xA1 :
+case 0xA2 :
+case 0xA3 :
+case 0xA4 :
+case 0xA5 :
+case 0xA6 :
+case 0xA7 :
+case 0xA8 :
+case 0xA9 :
+case 0xAA :
+case 0xAB :
+case 0xAC :
+case 0xAD :
+case 0xAE :
+case 0xAF :
+case 0xB0 :
+case 0xB1 :
+case 0xB2 :
+case 0xB3 :
+case 0xB4 :
+case 0xB5 :
+case 0xB6 :
+case 0xB7 :
+case 0xB8 :
+case 0xB9 :
+case 0xBA :
+case 0xBB :
+case 0xBC :
+case 0xBD :
+case 0xBE :
+case 0xBF :
+case 0xC0 :
+case 0xC1 :
+ return GRN_CHAR_OTHERS;
+ break;
+case 0xC2 :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAB :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAC :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAD :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB4 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB5 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB9 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xBA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBB :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBC :
+ case 0xBD :
+ case 0xBE :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+case 0xC3 :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x97 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB7 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB8 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+case 0xC4 :
+case 0xC5 :
+case 0xC6 :
+case 0xC7 :
+case 0xC8 :
+case 0xC9 :
+case 0xCA :
+ return GRN_CHAR_ALPHA;
+ break;
+case 0xCB :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAE :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAF :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+case 0xCC :
+ return GRN_CHAR_OTHERS;
+ break;
+case 0xCD :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB4 :
+ case 0xB5 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ case 0xBD :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBE :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+case 0xCE :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x84 :
+ case 0x85 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x86 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x87 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8B :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA2 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA3 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+case 0xCF :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB6 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB7 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+case 0xD0 :
+case 0xD1 :
+ return GRN_CHAR_ALPHA;
+ break;
+case 0xD2 :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x82 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8A :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+case 0xD3 :
+ return GRN_CHAR_ALPHA;
+ break;
+case 0xD4 :
+ if (str[1] < 0x94) { return GRN_CHAR_ALPHA; }
+ if (str[1] < 0xB1) { return GRN_CHAR_OTHERS; }
+ return GRN_CHAR_ALPHA;
+ break;
+case 0xD5 :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x97 :
+ case 0x98 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x99 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA0 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA1 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+case 0xD6 :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x88 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x89 :
+ case 0x8A :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ case 0xBD :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBE :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+case 0xD7 :
+ switch (str[1]) {
+ case 0x80 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x81 :
+ case 0x82 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x83 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x84 :
+ case 0x85 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x86 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB3 :
+ case 0xB4 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB5 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+case 0xD8 :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9B :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA0 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBB :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+case 0xD9 :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB0 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB1 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+case 0xDA :
+ return GRN_CHAR_ALPHA;
+ break;
+case 0xDB :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x94 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x95 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA5 :
+ case 0xA6 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA7 :
+ case 0xA8 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA9 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBD :
+ case 0xBE :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+case 0xDC :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB0 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+case 0xDD :
+ if (str[1] < 0x8D) { return GRN_CHAR_OTHERS; }
+ if (str[1] < 0xAE) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+case 0xDE :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB2 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+case 0xDF :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB4 :
+ case 0xB5 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBB :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+case 0xE0 :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA4 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBD :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBE :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA2 :
+ case 0xA3 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA4 :
+ case 0xA5 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB0 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBB :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8D :
+ case 0x8E :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8F :
+ case 0x90 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ case 0x92 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA9 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB1 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB2 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBD :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBE :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8E :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9E :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xBA :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBB :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8F :
+ case 0x90 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ case 0x92 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA9 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB1 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB4 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB5 :
+ case 0xB6 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB7 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBA :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9E :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB5 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8E :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x92 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA9 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB1 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB4 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBD :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBE :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB0 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB1 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB2 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8D :
+ case 0x8E :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8F :
+ case 0x90 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ case 0x92 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA9 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB1 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB4 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBD :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBE :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9E :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB0 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB2 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x83 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x84 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x99 :
+ case 0x9A :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9B :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA3 :
+ case 0xA4 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBA :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBB :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA9 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB4 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBA :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB0 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA9 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB4 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBD :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBE :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9E :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB0 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB1 :
+ case 0xB2 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB3 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA9 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBA :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB0 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB2 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBC :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBD :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBE :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB4 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB5 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (str[2]) {
+ case 0x80 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB1 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ case 0xBD :
+ case 0xBE :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x9A :
+ case 0x9B :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9C :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (str[2]) {
+ case 0x80 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x81 :
+ case 0x82 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x83 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x84 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x85 :
+ case 0x86 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x87 :
+ case 0x88 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x89 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8A :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8B :
+ case 0x8C :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x98 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA0 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA4 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA5 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA6 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA7 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA8 :
+ case 0xA9 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAA :
+ case 0xAB :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAC :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB1 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBD :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBE :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x85 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x86 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x9A :
+ case 0x9B :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9E :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (str[2]) {
+ case 0x80 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB4 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB5 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB6 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB7 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB8 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB9 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ case 0xBD :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBE :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x88 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAB :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x85 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x86 :
+ case 0x87 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ case 0xBD :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBE :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x86 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8D :
+ case 0x8E :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x92 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+case 0xE1 :
+ switch (str[1]) {
+ case 0x80 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA2 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA8 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA9 :
+ case 0xAA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAB :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x96 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x82 :
+ if (str[2] < 0xA0) { return GRN_CHAR_OTHERS; }
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x83 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBB :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBC :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBD :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x84 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x85 :
+ if (str[2] < 0x9A) { return GRN_CHAR_ALPHA; }
+ if (str[2] < 0x9F) { return GRN_CHAR_OTHERS; }
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x86 :
+ if (str[2] < 0xA3) { return GRN_CHAR_ALPHA; }
+ if (str[2] < 0xA8) { return GRN_CHAR_OTHERS; }
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x87 :
+ if (str[2] < 0xBA) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x88 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x89 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x89 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x97 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x98 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x99 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x89 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB1 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB6 :
+ case 0xB7 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ case 0xBD :
+ case 0xBE :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (str[2]) {
+ case 0x80 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x81 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x86 :
+ case 0x87 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x97 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x98 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x96 :
+ case 0x97 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x98 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xBD :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x8F :
+ if (str[2] < 0xB5) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ if (str[2] < 0x81) { return GRN_CHAR_OTHERS; }
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x99 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAD :
+ case 0xAE :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB7 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (str[2]) {
+ case 0x80 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9B :
+ case 0x9C :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB1 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB5 :
+ case 0xB6 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB7 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAD :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB1 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x9E :
+ if (str[2] < 0xB4) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9F :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x97 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xBA :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0xA1 :
+ if (str[2] < 0xB8) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA2 :
+ if (str[2] < 0xA9) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA3 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA4 :
+ if (str[2] < 0x9D) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA5 :
+ switch (str[2]) {
+ case 0x80 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x84 :
+ case 0x85 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB5 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xA6 :
+ if (str[2] < 0xAA) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA7 :
+ switch (str[2]) {
+ case 0x80 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9E :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA0 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAC :
+ if (str[2] < 0x85) { return GRN_CHAR_OTHERS; }
+ if (str[2] < 0xB4) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAD :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBD :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB7 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBA :
+ if (str[2] < 0x9C) { return GRN_CHAR_ALPHA; }
+ if (str[2] < 0xA0) { return GRN_CHAR_OTHERS; }
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBB :
+ if (str[2] < 0xBA) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBC :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x96 :
+ case 0x97 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x86 :
+ case 0x87 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x98 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x99 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9A :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9B :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9C :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9E :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ case 0xBD :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBE :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB5 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBD :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBE :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x85 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x94 :
+ case 0x95 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9C :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB5 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBD :
+ case 0xBE :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+case 0xE2 :
+ switch (str[1]) {
+ case 0x80 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9A :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9E :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB9 :
+ case 0xBA :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBB :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB1 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ case 0xBD :
+ case 0xBE :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB6 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x83 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x84 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x82 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x87 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x88 :
+ case 0x89 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x94 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x95 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA4 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA5 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA6 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA7 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA8 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA9 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAE :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBA :
+ case 0xBB :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBC :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8E :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x93 :
+ default :
+ return GRN_CHAR_DIGIT;
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8F :
+ if (str[2] < 0xA8) { return GRN_CHAR_SYMBOL; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ if (str[2] < 0xA7) { return GRN_CHAR_SYMBOL; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x91 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ default :
+ return GRN_CHAR_DIGIT;
+ break;
+ }
+ break;
+ case 0x92 :
+ if (str[2] < 0x9C) { return GRN_CHAR_DIGIT; }
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x93 :
+ if (str[2] < 0xAA) { return GRN_CHAR_SYMBOL; }
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9A :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB3 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x9B :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9C :
+ switch (str[2]) {
+ case 0x80 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x85 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8A :
+ case 0x8B :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA8 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA9 :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8C :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8D :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8E :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x96 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x97 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9F :
+ case 0xA0 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB6 :
+ default :
+ return GRN_CHAR_DIGIT;
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x94 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB0 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ case 0xBD :
+ case 0xBE :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAC :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA4 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB8 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB2 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB3 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBD :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xBE :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAF :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB0 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA7 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB7 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ case 0xBD :
+ case 0xBE :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x87 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x97 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9F :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x89 :
+ case 0x8A :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8B :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8C :
+ case 0x8D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x98 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB9 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ case 0xBD :
+ case 0xBE :
+ return GRN_CHAR_KANJI;
+ break;
+ case 0xBF :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_KANJI;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBC :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+case 0xE3 :
+ switch (str[1]) {
+ case 0x80 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x81 :
+ return GRN_CHAR_HIRAGANA;
+ break;
+ case 0x82 :
+ if (str[2] < 0xA0) { return GRN_CHAR_HIRAGANA; }
+ return GRN_CHAR_KATAKANA;
+ break;
+ case 0x83 :
+ return GRN_CHAR_KATAKANA;
+ break;
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ return GRN_CHAR_KANJI;
+ break;
+ case 0x87 :
+ if (str[2] < 0xB0) { return GRN_CHAR_KANJI; }
+ return GRN_CHAR_KATAKANA;
+ break;
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x90 :
+ default :
+ return GRN_CHAR_KANJI;
+ break;
+ }
+ break;
+case 0xE4 :
+ if (str[1] < 0xB7) { return GRN_CHAR_KANJI; }
+ if (str[1] < 0xB8) { return GRN_CHAR_SYMBOL; }
+ return GRN_CHAR_KANJI;
+ break;
+case 0xE5 :
+case 0xE6 :
+case 0xE7 :
+case 0xE8 :
+case 0xE9 :
+ return GRN_CHAR_KANJI;
+ break;
+case 0xEA :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ return GRN_CHAR_KANJI;
+ break;
+ case 0x93 :
+ if (str[2] < 0x90) { return GRN_CHAR_KANJI; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9C :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA2 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x82 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x86 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8B :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAC :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB8 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ default :
+ return GRN_CHAR_KANJI;
+ break;
+ }
+ break;
+case 0xEB :
+case 0xEC :
+ return GRN_CHAR_KANJI;
+ break;
+case 0xED :
+ if (str[1] < 0x9E) { return GRN_CHAR_KANJI; }
+ if (str[1] == 0x9E) {
+ if (str[2] < 0xB0) { return GRN_CHAR_KANJI; }
+ return GRN_CHAR_OTHERS;
+ }
+ return GRN_CHAR_OTHERS;
+ break;
+case 0xEE :
+ return GRN_CHAR_OTHERS;
+ break;
+case 0xEF :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ return GRN_CHAR_KANJI;
+ break;
+ case 0xAC :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9E :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA9 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB7 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ case 0xBC :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBD :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBE :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x82 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x85 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x86 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0xAE :
+ if (str[2] < 0xB2) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAF :
+ if (str[2] < 0x93) { return GRN_CHAR_OTHERS; }
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB4 :
+ if (str[2] < 0xBE) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB5 :
+ if (str[2] < 0x90) { return GRN_CHAR_OTHERS; }
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB6 :
+ if (str[2] < 0x90) { return GRN_CHAR_ALPHA; }
+ if (str[2] < 0x92) { return GRN_CHAR_OTHERS; }
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB7 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBC :
+ case 0xBD :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBE :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x93 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA7 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB5 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB6 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0xBA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBB :
+ if (str[2] < 0xBD) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBC :
+ switch (str[2]) {
+ case 0x80 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBB :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (str[2]) {
+ case 0x80 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA6 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0xBE :
+ if (str[2] < 0xBF) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBF :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x88 :
+ case 0x89 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x90 :
+ case 0x91 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA7 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBC :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+case 0xF0 :
+ switch (str[1]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x90 :
+ switch (str[2]) {
+ case 0x80 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8C :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA7 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBB :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBC :
+ case 0xBD :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBE :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9E :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x82 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x83 :
+ if (str[3] < 0xBB) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x84 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB7 :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0x85 :
+ if (str[3] < 0xB9) { return GRN_CHAR_DIGIT; }
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x86 :
+ if (str[3] < 0x8A) { return GRN_CHAR_SYMBOL; }
+ if (str[3] < 0x8B) { return GRN_CHAR_DIGIT; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8C :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (str[3]) {
+ case 0x80 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x81 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8A :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x8B :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9E :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA0 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x90 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x96 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x90 :
+ case 0x91 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x92 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xAA :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x86 :
+ case 0x87 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x88 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x89 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB6 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB7 :
+ case 0xB8 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB9 :
+ case 0xBA :
+ case 0xBB :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBC :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBD :
+ case 0xBE :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA4 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA0 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA8 :
+ switch (str[3]) {
+ case 0x80 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x94 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x98 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB4 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x99 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x91 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x92 :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8D :
+ if (str[3] < 0xAF) { return GRN_CHAR_ALPHA; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0x91 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB4 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9D :
+ switch (str[2]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x83 :
+ if (str[3] < 0xB6) { return GRN_CHAR_SYMBOL; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x84 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAA :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAD :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAE :
+ default :
+ return GRN_CHAR_SYMBOL;
+ break;
+ }
+ break;
+ case 0x87 :
+ if (str[3] < 0x9E) { return GRN_CHAR_SYMBOL; }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x88 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x89 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x85 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x86 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x8A :
+ case 0x8B :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8C :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8D :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ return GRN_CHAR_DIGIT;
+ break;
+ case 0xB2 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x8E :
+ case 0x8F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x90 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ if (str[3] < 0x95) { return GRN_CHAR_ALPHA; }
+ if (str[3] < 0x96) { return GRN_CHAR_OTHERS; }
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x92 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA2 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA3 :
+ case 0xA4 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA5 :
+ case 0xA6 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA7 :
+ case 0xA8 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAD :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBA :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBB :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBC :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBD :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x93 :
+ if (str[3] < 0x84) { return GRN_CHAR_ALPHA; }
+ if (str[3] < 0x85) { return GRN_CHAR_OTHERS; }
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x94 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x86 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8B :
+ case 0x8C :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x95 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBA :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xBB :
+ case 0xBC :
+ case 0xBD :
+ case 0xBE :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBF :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x85 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x86 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x91 :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x92 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9A :
+ if (str[3] < 0xA6) { return GRN_CHAR_ALPHA; }
+ if (str[3] < 0xA8) { return GRN_CHAR_OTHERS; }
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9B :
+ switch (str[3]) {
+ case 0x80 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x81 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x9B :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ case 0xB5 :
+ case 0xB6 :
+ case 0xB7 :
+ case 0xB8 :
+ case 0xB9 :
+ case 0xBA :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xBB :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xBC :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x95 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ case 0xAF :
+ case 0xB0 :
+ case 0xB1 :
+ case 0xB2 :
+ case 0xB3 :
+ case 0xB4 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xB5 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB6 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8F :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ case 0xAA :
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xAF :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xB0 :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ case 0x83 :
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x89 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x8A :
+ case 0x8B :
+ case 0x8C :
+ case 0x8D :
+ case 0x8E :
+ case 0x8F :
+ case 0x90 :
+ case 0x91 :
+ case 0x92 :
+ case 0x93 :
+ case 0x94 :
+ case 0x95 :
+ case 0x96 :
+ case 0x97 :
+ case 0x98 :
+ case 0x99 :
+ case 0x9A :
+ case 0x9B :
+ case 0x9C :
+ case 0x9D :
+ case 0x9E :
+ case 0x9F :
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0xA9 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0xAA :
+ default :
+ return GRN_CHAR_ALPHA;
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (str[3]) {
+ case 0x80 :
+ case 0x81 :
+ case 0x82 :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x83 :
+ return GRN_CHAR_SYMBOL;
+ break;
+ case 0x84 :
+ case 0x85 :
+ case 0x86 :
+ case 0x87 :
+ case 0x88 :
+ case 0x89 :
+ case 0x8A :
+ case 0x8B :
+ return GRN_CHAR_ALPHA;
+ break;
+ case 0x8C :
+ case 0x8D :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0x8E :
+ default :
+ return GRN_CHAR_DIGIT;
+ break;
+ }
+ break;
+ case 0xA0 :
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ case 0x9E :
+ case 0x9F :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xA0 :
+ case 0xA1 :
+ case 0xA2 :
+ case 0xA3 :
+ case 0xA4 :
+ case 0xA5 :
+ case 0xA6 :
+ case 0xA7 :
+ case 0xA8 :
+ case 0xA9 :
+ return GRN_CHAR_KANJI;
+ break;
+ case 0xAA :
+ if (str[2] < 0x9B) { return GRN_CHAR_KANJI; }
+ if (str[2] == 0x9B) {
+ if (str[3] < 0xA0) { return GRN_CHAR_KANJI; }
+ return GRN_CHAR_OTHERS;
+ }
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAB :
+ case 0xAC :
+ case 0xAD :
+ case 0xAE :
+ return GRN_CHAR_OTHERS;
+ break;
+ case 0xAF :
+ if (str[2] < 0xA0) { return GRN_CHAR_OTHERS; }
+ if (str[2] < 0xA8) { return GRN_CHAR_KANJI; }
+ if (str[2] == 0xA8) {
+ if (str[3] < 0xA0) { return GRN_CHAR_KANJI; }
+ return GRN_CHAR_OTHERS;
+ }
+ return GRN_CHAR_OTHERS;
+ break;
+ default :
+ return GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+default :
+ return GRN_CHAR_OTHERS;
+ break;
+}
+ return -1;
+}
+
+const char *
+grn_nfkc_map1(const unsigned char *str)
+{
+switch (str[0]) {
+case 0x41 :
+ return "\x61";
+ break;
+case 0x42 :
+ return "\x62";
+ break;
+case 0x43 :
+ return "\x63";
+ break;
+case 0x44 :
+ return "\x64";
+ break;
+case 0x45 :
+ return "\x65";
+ break;
+case 0x46 :
+ return "\x66";
+ break;
+case 0x47 :
+ return "\x67";
+ break;
+case 0x48 :
+ return "\x68";
+ break;
+case 0x49 :
+ return "\x69";
+ break;
+case 0x4A :
+ return "\x6A";
+ break;
+case 0x4B :
+ return "\x6B";
+ break;
+case 0x4C :
+ return "\x6C";
+ break;
+case 0x4D :
+ return "\x6D";
+ break;
+case 0x4E :
+ return "\x6E";
+ break;
+case 0x4F :
+ return "\x6F";
+ break;
+case 0x50 :
+ return "\x70";
+ break;
+case 0x51 :
+ return "\x71";
+ break;
+case 0x52 :
+ return "\x72";
+ break;
+case 0x53 :
+ return "\x73";
+ break;
+case 0x54 :
+ return "\x74";
+ break;
+case 0x55 :
+ return "\x75";
+ break;
+case 0x56 :
+ return "\x76";
+ break;
+case 0x57 :
+ return "\x77";
+ break;
+case 0x58 :
+ return "\x78";
+ break;
+case 0x59 :
+ return "\x79";
+ break;
+case 0x5A :
+ return "\x7A";
+ break;
+case 0xC2 :
+ switch (str[1]) {
+ case 0xA0 :
+ return "\x20";
+ break;
+ case 0xA8 :
+ return "\xCC\x88";
+ break;
+ case 0xAA :
+ return "\x61";
+ break;
+ case 0xAF :
+ return "\xCC\x84";
+ break;
+ case 0xB2 :
+ return "\x32";
+ break;
+ case 0xB3 :
+ return "\x33";
+ break;
+ case 0xB4 :
+ return "\xCC\x81";
+ break;
+ case 0xB5 :
+ return "\xCE\xBC";
+ break;
+ case 0xB8 :
+ return "\xCC\xA7";
+ break;
+ case 0xB9 :
+ return "\x31";
+ break;
+ case 0xBA :
+ return "\x6F";
+ break;
+ case 0xBC :
+ return "\x31\xE2\x81\x84\x34";
+ break;
+ case 0xBD :
+ return "\x31\xE2\x81\x84\x32";
+ break;
+ case 0xBE :
+ return "\x33\xE2\x81\x84\x34";
+ break;
+ }
+ break;
+case 0xC3 :
+ switch (str[1]) {
+ case 0x80 :
+ return "\xC3\xA0";
+ break;
+ case 0x81 :
+ return "\xC3\xA1";
+ break;
+ case 0x82 :
+ return "\xC3\xA2";
+ break;
+ case 0x83 :
+ return "\xC3\xA3";
+ break;
+ case 0x84 :
+ return "\xC3\xA4";
+ break;
+ case 0x85 :
+ return "\xC3\xA5";
+ break;
+ case 0x87 :
+ return "\xC3\xA7";
+ break;
+ case 0x88 :
+ return "\xC3\xA8";
+ break;
+ case 0x89 :
+ return "\xC3\xA9";
+ break;
+ case 0x8A :
+ return "\xC3\xAA";
+ break;
+ case 0x8B :
+ return "\xC3\xAB";
+ break;
+ case 0x8C :
+ return "\xC3\xAC";
+ break;
+ case 0x8D :
+ return "\xC3\xAD";
+ break;
+ case 0x8E :
+ return "\xC3\xAE";
+ break;
+ case 0x8F :
+ return "\xC3\xAF";
+ break;
+ case 0x91 :
+ return "\xC3\xB1";
+ break;
+ case 0x92 :
+ return "\xC3\xB2";
+ break;
+ case 0x93 :
+ return "\xC3\xB3";
+ break;
+ case 0x94 :
+ return "\xC3\xB4";
+ break;
+ case 0x95 :
+ return "\xC3\xB5";
+ break;
+ case 0x96 :
+ return "\xC3\xB6";
+ break;
+ case 0x99 :
+ return "\xC3\xB9";
+ break;
+ case 0x9A :
+ return "\xC3\xBA";
+ break;
+ case 0x9B :
+ return "\xC3\xBB";
+ break;
+ case 0x9C :
+ return "\xC3\xBC";
+ break;
+ case 0x9D :
+ return "\xC3\xBD";
+ break;
+ }
+ break;
+case 0xC4 :
+ switch (str[1]) {
+ case 0x80 :
+ return "\xC4\x81";
+ break;
+ case 0x82 :
+ return "\xC4\x83";
+ break;
+ case 0x84 :
+ return "\xC4\x85";
+ break;
+ case 0x86 :
+ return "\xC4\x87";
+ break;
+ case 0x88 :
+ return "\xC4\x89";
+ break;
+ case 0x8A :
+ return "\xC4\x8B";
+ break;
+ case 0x8C :
+ return "\xC4\x8D";
+ break;
+ case 0x8E :
+ return "\xC4\x8F";
+ break;
+ case 0x92 :
+ return "\xC4\x93";
+ break;
+ case 0x94 :
+ return "\xC4\x95";
+ break;
+ case 0x96 :
+ return "\xC4\x97";
+ break;
+ case 0x98 :
+ return "\xC4\x99";
+ break;
+ case 0x9A :
+ return "\xC4\x9B";
+ break;
+ case 0x9C :
+ return "\xC4\x9D";
+ break;
+ case 0x9E :
+ return "\xC4\x9F";
+ break;
+ case 0xA0 :
+ return "\xC4\xA1";
+ break;
+ case 0xA2 :
+ return "\xC4\xA3";
+ break;
+ case 0xA4 :
+ return "\xC4\xA5";
+ break;
+ case 0xA8 :
+ return "\xC4\xA9";
+ break;
+ case 0xAA :
+ return "\xC4\xAB";
+ break;
+ case 0xAC :
+ return "\xC4\xAD";
+ break;
+ case 0xAE :
+ return "\xC4\xAF";
+ break;
+ case 0xB0 :
+ return "\x69\xCC\x87";
+ break;
+ case 0xB2 :
+ return "\x69\x6A";
+ break;
+ case 0xB3 :
+ return "\x69\x6A";
+ break;
+ case 0xB4 :
+ return "\xC4\xB5";
+ break;
+ case 0xB6 :
+ return "\xC4\xB7";
+ break;
+ case 0xB9 :
+ return "\xC4\xBA";
+ break;
+ case 0xBB :
+ return "\xC4\xBC";
+ break;
+ case 0xBD :
+ return "\xC4\xBE";
+ break;
+ case 0xBF :
+ return "\x6C\xC2\xB7";
+ break;
+ }
+ break;
+case 0xC5 :
+ switch (str[1]) {
+ case 0x80 :
+ return "\x6C\xC2\xB7";
+ break;
+ case 0x83 :
+ return "\xC5\x84";
+ break;
+ case 0x85 :
+ return "\xC5\x86";
+ break;
+ case 0x87 :
+ return "\xC5\x88";
+ break;
+ case 0x89 :
+ return "\xCA\xBC\x6E";
+ break;
+ case 0x8C :
+ return "\xC5\x8D";
+ break;
+ case 0x8E :
+ return "\xC5\x8F";
+ break;
+ case 0x90 :
+ return "\xC5\x91";
+ break;
+ case 0x94 :
+ return "\xC5\x95";
+ break;
+ case 0x96 :
+ return "\xC5\x97";
+ break;
+ case 0x98 :
+ return "\xC5\x99";
+ break;
+ case 0x9A :
+ return "\xC5\x9B";
+ break;
+ case 0x9C :
+ return "\xC5\x9D";
+ break;
+ case 0x9E :
+ return "\xC5\x9F";
+ break;
+ case 0xA0 :
+ return "\xC5\xA1";
+ break;
+ case 0xA2 :
+ return "\xC5\xA3";
+ break;
+ case 0xA4 :
+ return "\xC5\xA5";
+ break;
+ case 0xA8 :
+ return "\xC5\xA9";
+ break;
+ case 0xAA :
+ return "\xC5\xAB";
+ break;
+ case 0xAC :
+ return "\xC5\xAD";
+ break;
+ case 0xAE :
+ return "\xC5\xAF";
+ break;
+ case 0xB0 :
+ return "\xC5\xB1";
+ break;
+ case 0xB2 :
+ return "\xC5\xB3";
+ break;
+ case 0xB4 :
+ return "\xC5\xB5";
+ break;
+ case 0xB6 :
+ return "\xC5\xB7";
+ break;
+ case 0xB8 :
+ return "\xC3\xBF";
+ break;
+ case 0xB9 :
+ return "\xC5\xBA";
+ break;
+ case 0xBB :
+ return "\xC5\xBC";
+ break;
+ case 0xBD :
+ return "\xC5\xBE";
+ break;
+ case 0xBF :
+ return "\x73";
+ break;
+ }
+ break;
+case 0xC6 :
+ switch (str[1]) {
+ case 0xA0 :
+ return "\xC6\xA1";
+ break;
+ case 0xAF :
+ return "\xC6\xB0";
+ break;
+ }
+ break;
+case 0xC7 :
+ switch (str[1]) {
+ case 0x84 :
+ return "\x64\xC5\xBE";
+ break;
+ case 0x85 :
+ return "\x64\xC5\xBE";
+ break;
+ case 0x86 :
+ return "\x64\xC5\xBE";
+ break;
+ case 0x87 :
+ return "\x6C\x6A";
+ break;
+ case 0x88 :
+ return "\x6C\x6A";
+ break;
+ case 0x89 :
+ return "\x6C\x6A";
+ break;
+ case 0x8A :
+ return "\x6E\x6A";
+ break;
+ case 0x8B :
+ return "\x6E\x6A";
+ break;
+ case 0x8C :
+ return "\x6E\x6A";
+ break;
+ case 0x8D :
+ return "\xC7\x8E";
+ break;
+ case 0x8F :
+ return "\xC7\x90";
+ break;
+ case 0x91 :
+ return "\xC7\x92";
+ break;
+ case 0x93 :
+ return "\xC7\x94";
+ break;
+ case 0x95 :
+ return "\xC7\x96";
+ break;
+ case 0x97 :
+ return "\xC7\x98";
+ break;
+ case 0x99 :
+ return "\xC7\x9A";
+ break;
+ case 0x9B :
+ return "\xC7\x9C";
+ break;
+ case 0x9E :
+ return "\xC7\x9F";
+ break;
+ case 0xA0 :
+ return "\xC7\xA1";
+ break;
+ case 0xA6 :
+ return "\xC7\xA7";
+ break;
+ case 0xA8 :
+ return "\xC7\xA9";
+ break;
+ case 0xAA :
+ return "\xC7\xAB";
+ break;
+ case 0xAC :
+ return "\xC7\xAD";
+ break;
+ case 0xB1 :
+ return "\x64\x7A";
+ break;
+ case 0xB2 :
+ return "\x64\x7A";
+ break;
+ case 0xB3 :
+ return "\x64\x7A";
+ break;
+ case 0xB4 :
+ return "\xC7\xB5";
+ break;
+ case 0xB8 :
+ return "\xC7\xB9";
+ break;
+ case 0xBA :
+ return "\xC7\xBB";
+ break;
+ }
+ break;
+case 0xC8 :
+ switch (str[1]) {
+ case 0x80 :
+ return "\xC8\x81";
+ break;
+ case 0x82 :
+ return "\xC8\x83";
+ break;
+ case 0x84 :
+ return "\xC8\x85";
+ break;
+ case 0x86 :
+ return "\xC8\x87";
+ break;
+ case 0x88 :
+ return "\xC8\x89";
+ break;
+ case 0x8A :
+ return "\xC8\x8B";
+ break;
+ case 0x8C :
+ return "\xC8\x8D";
+ break;
+ case 0x8E :
+ return "\xC8\x8F";
+ break;
+ case 0x90 :
+ return "\xC8\x91";
+ break;
+ case 0x92 :
+ return "\xC8\x93";
+ break;
+ case 0x94 :
+ return "\xC8\x95";
+ break;
+ case 0x96 :
+ return "\xC8\x97";
+ break;
+ case 0x98 :
+ return "\xC8\x99";
+ break;
+ case 0x9A :
+ return "\xC8\x9B";
+ break;
+ case 0x9E :
+ return "\xC8\x9F";
+ break;
+ case 0xA6 :
+ return "\xC8\xA7";
+ break;
+ case 0xA8 :
+ return "\xC8\xA9";
+ break;
+ case 0xAA :
+ return "\xC8\xAB";
+ break;
+ case 0xAC :
+ return "\xC8\xAD";
+ break;
+ case 0xAE :
+ return "\xC8\xAF";
+ break;
+ case 0xB0 :
+ return "\xC8\xB1";
+ break;
+ case 0xB2 :
+ return "\xC8\xB3";
+ break;
+ }
+ break;
+case 0xCA :
+ switch (str[1]) {
+ case 0xB0 :
+ return "\x68";
+ break;
+ case 0xB1 :
+ return "\xC9\xA6";
+ break;
+ case 0xB2 :
+ return "\x6A";
+ break;
+ case 0xB3 :
+ return "\x72";
+ break;
+ case 0xB4 :
+ return "\xC9\xB9";
+ break;
+ case 0xB5 :
+ return "\xC9\xBB";
+ break;
+ case 0xB6 :
+ return "\xCA\x81";
+ break;
+ case 0xB7 :
+ return "\x77";
+ break;
+ case 0xB8 :
+ return "\x79";
+ break;
+ }
+ break;
+case 0xCB :
+ switch (str[1]) {
+ case 0x98 :
+ return "\xCC\x86";
+ break;
+ case 0x99 :
+ return "\xCC\x87";
+ break;
+ case 0x9A :
+ return "\xCC\x8A";
+ break;
+ case 0x9B :
+ return "\xCC\xA8";
+ break;
+ case 0x9C :
+ return "\xCC\x83";
+ break;
+ case 0x9D :
+ return "\xCC\x8B";
+ break;
+ case 0xA0 :
+ return "\xC9\xA3";
+ break;
+ case 0xA1 :
+ return "\x6C";
+ break;
+ case 0xA2 :
+ return "\x73";
+ break;
+ case 0xA3 :
+ return "\x78";
+ break;
+ case 0xA4 :
+ return "\xCA\x95";
+ break;
+ }
+ break;
+case 0xCD :
+ switch (str[1]) {
+ case 0x80 :
+ return "\xCC\x80";
+ break;
+ case 0x81 :
+ return "\xCC\x81";
+ break;
+ case 0x83 :
+ return "\xCC\x93";
+ break;
+ case 0x84 :
+ return "\xCC\x88\xCC\x81";
+ break;
+ case 0xB4 :
+ return "\xCA\xB9";
+ break;
+ case 0xBA :
+ return "\xCD\x85";
+ break;
+ case 0xBE :
+ return "\x3B";
+ break;
+ }
+ break;
+case 0xCE :
+ switch (str[1]) {
+ case 0x84 :
+ return "\xCC\x81";
+ break;
+ case 0x85 :
+ return "\xCC\x88\xCC\x81";
+ break;
+ case 0x87 :
+ return "\xC2\xB7";
+ break;
+ }
+ break;
+case 0xCF :
+ switch (str[1]) {
+ case 0x90 :
+ return "\xCE\xB2";
+ break;
+ case 0x91 :
+ return "\xCE\xB8";
+ break;
+ case 0x92 :
+ return "\xCE\xA5";
+ break;
+ case 0x93 :
+ return "\xCE\x8E";
+ break;
+ case 0x94 :
+ return "\xCE\xAB";
+ break;
+ case 0x95 :
+ return "\xCF\x86";
+ break;
+ case 0x96 :
+ return "\xCF\x80";
+ break;
+ case 0xB0 :
+ return "\xCE\xBA";
+ break;
+ case 0xB1 :
+ return "\xCF\x81";
+ break;
+ case 0xB2 :
+ return "\xCF\x82";
+ break;
+ case 0xB4 :
+ return "\xCE\x98";
+ break;
+ case 0xB5 :
+ return "\xCE\xB5";
+ break;
+ case 0xB9 :
+ return "\xCE\xA3";
+ break;
+ }
+ break;
+case 0xD6 :
+ if (str[1] == 0x87) {
+ return "\xD5\xA5\xD6\x82";
+ }
+ break;
+case 0xD9 :
+ switch (str[1]) {
+ case 0xB5 :
+ return "\xD8\xA7\xD9\xB4";
+ break;
+ case 0xB6 :
+ return "\xD9\x88\xD9\xB4";
+ break;
+ case 0xB7 :
+ return "\xDB\x87\xD9\xB4";
+ break;
+ case 0xB8 :
+ return "\xD9\x8A\xD9\xB4";
+ break;
+ }
+ break;
+case 0xE0 :
+ switch (str[1]) {
+ case 0xA5 :
+ switch (str[2]) {
+ case 0x98 :
+ return "\xE0\xA4\x95\xE0\xA4\xBC";
+ break;
+ case 0x99 :
+ return "\xE0\xA4\x96\xE0\xA4\xBC";
+ break;
+ case 0x9A :
+ return "\xE0\xA4\x97\xE0\xA4\xBC";
+ break;
+ case 0x9B :
+ return "\xE0\xA4\x9C\xE0\xA4\xBC";
+ break;
+ case 0x9C :
+ return "\xE0\xA4\xA1\xE0\xA4\xBC";
+ break;
+ case 0x9D :
+ return "\xE0\xA4\xA2\xE0\xA4\xBC";
+ break;
+ case 0x9E :
+ return "\xE0\xA4\xAB\xE0\xA4\xBC";
+ break;
+ case 0x9F :
+ return "\xE0\xA4\xAF\xE0\xA4\xBC";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (str[2]) {
+ case 0x9C :
+ return "\xE0\xA6\xA1\xE0\xA6\xBC";
+ break;
+ case 0x9D :
+ return "\xE0\xA6\xA2\xE0\xA6\xBC";
+ break;
+ case 0x9F :
+ return "\xE0\xA6\xAF\xE0\xA6\xBC";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (str[2]) {
+ case 0xB3 :
+ return "\xE0\xA8\xB2\xE0\xA8\xBC";
+ break;
+ case 0xB6 :
+ return "\xE0\xA8\xB8\xE0\xA8\xBC";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (str[2]) {
+ case 0x99 :
+ return "\xE0\xA8\x96\xE0\xA8\xBC";
+ break;
+ case 0x9A :
+ return "\xE0\xA8\x97\xE0\xA8\xBC";
+ break;
+ case 0x9B :
+ return "\xE0\xA8\x9C\xE0\xA8\xBC";
+ break;
+ case 0x9E :
+ return "\xE0\xA8\xAB\xE0\xA8\xBC";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (str[2]) {
+ case 0x9C :
+ return "\xE0\xAC\xA1\xE0\xAC\xBC";
+ break;
+ case 0x9D :
+ return "\xE0\xAC\xA2\xE0\xAC\xBC";
+ break;
+ }
+ break;
+ case 0xB8 :
+ if (str[2] == 0xB3) {
+ return "\xE0\xB9\x8D\xE0\xB8\xB2";
+ }
+ break;
+ case 0xBA :
+ if (str[2] == 0xB3) {
+ return "\xE0\xBB\x8D\xE0\xBA\xB2";
+ }
+ break;
+ case 0xBB :
+ switch (str[2]) {
+ case 0x9C :
+ return "\xE0\xBA\xAB\xE0\xBA\x99";
+ break;
+ case 0x9D :
+ return "\xE0\xBA\xAB\xE0\xBA\xA1";
+ break;
+ }
+ break;
+ case 0xBC :
+ if (str[2] == 0x8C) {
+ return "\xE0\xBC\x8B";
+ }
+ break;
+ case 0xBD :
+ switch (str[2]) {
+ case 0x83 :
+ return "\xE0\xBD\x82\xE0\xBE\xB7";
+ break;
+ case 0x8D :
+ return "\xE0\xBD\x8C\xE0\xBE\xB7";
+ break;
+ case 0x92 :
+ return "\xE0\xBD\x91\xE0\xBE\xB7";
+ break;
+ case 0x97 :
+ return "\xE0\xBD\x96\xE0\xBE\xB7";
+ break;
+ case 0x9C :
+ return "\xE0\xBD\x9B\xE0\xBE\xB7";
+ break;
+ case 0xA9 :
+ return "\xE0\xBD\x80\xE0\xBE\xB5";
+ break;
+ case 0xB3 :
+ return "\xE0\xBD\xB1\xE0\xBD\xB2";
+ break;
+ case 0xB5 :
+ return "\xE0\xBD\xB1\xE0\xBD\xB4";
+ break;
+ case 0xB6 :
+ return "\xE0\xBE\xB2\xE0\xBE\x80";
+ break;
+ case 0xB7 :
+ return "\xE0\xBE\xB2\xE0\xBD\xB1\xE0\xBE\x80";
+ break;
+ case 0xB8 :
+ return "\xE0\xBE\xB3\xE0\xBE\x80";
+ break;
+ case 0xB9 :
+ return "\xE0\xBE\xB3\xE0\xBD\xB1\xE0\xBE\x80";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (str[2]) {
+ case 0x81 :
+ return "\xE0\xBD\xB1\xE0\xBE\x80";
+ break;
+ case 0x93 :
+ return "\xE0\xBE\x92\xE0\xBE\xB7";
+ break;
+ case 0x9D :
+ return "\xE0\xBE\x9C\xE0\xBE\xB7";
+ break;
+ case 0xA2 :
+ return "\xE0\xBE\xA1\xE0\xBE\xB7";
+ break;
+ case 0xA7 :
+ return "\xE0\xBE\xA6\xE0\xBE\xB7";
+ break;
+ case 0xAC :
+ return "\xE0\xBE\xAB\xE0\xBE\xB7";
+ break;
+ case 0xB9 :
+ return "\xE0\xBE\x90\xE0\xBE\xB5";
+ break;
+ }
+ break;
+ }
+ break;
+case 0xE1 :
+ switch (str[1]) {
+ case 0x83 :
+ if (str[2] == 0xBC) {
+ return "\xE1\x83\x9C";
+ }
+ break;
+ case 0xB4 :
+ switch (str[2]) {
+ case 0xAC :
+ return "\x61";
+ break;
+ case 0xAD :
+ return "\xC3\x86";
+ break;
+ case 0xAE :
+ return "\x62";
+ break;
+ case 0xB0 :
+ return "\x64";
+ break;
+ case 0xB1 :
+ return "\x65";
+ break;
+ case 0xB2 :
+ return "\xC6\x8E";
+ break;
+ case 0xB3 :
+ return "\x67";
+ break;
+ case 0xB4 :
+ return "\x68";
+ break;
+ case 0xB5 :
+ return "\x69";
+ break;
+ case 0xB6 :
+ return "\x6A";
+ break;
+ case 0xB7 :
+ return "\x6B";
+ break;
+ case 0xB8 :
+ return "\x6C";
+ break;
+ case 0xB9 :
+ return "\x6D";
+ break;
+ case 0xBA :
+ return "\x6E";
+ break;
+ case 0xBC :
+ return "\x6F";
+ break;
+ case 0xBD :
+ return "\xC8\xA2";
+ break;
+ case 0xBE :
+ return "\x70";
+ break;
+ case 0xBF :
+ return "\x72";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x74";
+ break;
+ case 0x81 :
+ return "\x75";
+ break;
+ case 0x82 :
+ return "\x77";
+ break;
+ case 0x83 :
+ return "\x61";
+ break;
+ case 0x84 :
+ return "\xC9\x90";
+ break;
+ case 0x85 :
+ return "\xC9\x91";
+ break;
+ case 0x86 :
+ return "\xE1\xB4\x82";
+ break;
+ case 0x87 :
+ return "\x62";
+ break;
+ case 0x88 :
+ return "\x64";
+ break;
+ case 0x89 :
+ return "\x65";
+ break;
+ case 0x8A :
+ return "\xC9\x99";
+ break;
+ case 0x8B :
+ return "\xC9\x9B";
+ break;
+ case 0x8C :
+ return "\xC9\x9C";
+ break;
+ case 0x8D :
+ return "\x67";
+ break;
+ case 0x8F :
+ return "\x6B";
+ break;
+ case 0x90 :
+ return "\x6D";
+ break;
+ case 0x91 :
+ return "\xC5\x8B";
+ break;
+ case 0x92 :
+ return "\x6F";
+ break;
+ case 0x93 :
+ return "\xC9\x94";
+ break;
+ case 0x94 :
+ return "\xE1\xB4\x96";
+ break;
+ case 0x95 :
+ return "\xE1\xB4\x97";
+ break;
+ case 0x96 :
+ return "\x70";
+ break;
+ case 0x97 :
+ return "\x74";
+ break;
+ case 0x98 :
+ return "\x75";
+ break;
+ case 0x99 :
+ return "\xE1\xB4\x9D";
+ break;
+ case 0x9A :
+ return "\xC9\xAF";
+ break;
+ case 0x9B :
+ return "\x76";
+ break;
+ case 0x9C :
+ return "\xE1\xB4\xA5";
+ break;
+ case 0x9D :
+ return "\xCE\xB2";
+ break;
+ case 0x9E :
+ return "\xCE\xB3";
+ break;
+ case 0x9F :
+ return "\xCE\xB4";
+ break;
+ case 0xA0 :
+ return "\xCF\x86";
+ break;
+ case 0xA1 :
+ return "\xCF\x87";
+ break;
+ case 0xA2 :
+ return "\x69";
+ break;
+ case 0xA3 :
+ return "\x72";
+ break;
+ case 0xA4 :
+ return "\x75";
+ break;
+ case 0xA5 :
+ return "\x76";
+ break;
+ case 0xA6 :
+ return "\xCE\xB2";
+ break;
+ case 0xA7 :
+ return "\xCE\xB3";
+ break;
+ case 0xA8 :
+ return "\xCF\x81";
+ break;
+ case 0xA9 :
+ return "\xCF\x86";
+ break;
+ case 0xAA :
+ return "\xCF\x87";
+ break;
+ case 0xB8 :
+ return "\xD0\xBD";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (str[2]) {
+ case 0x9B :
+ return "\xC9\x92";
+ break;
+ case 0x9C :
+ return "\x63";
+ break;
+ case 0x9D :
+ return "\xC9\x95";
+ break;
+ case 0x9E :
+ return "\xC3\xB0";
+ break;
+ case 0x9F :
+ return "\xC9\x9C";
+ break;
+ case 0xA0 :
+ return "\x66";
+ break;
+ case 0xA1 :
+ return "\xC9\x9F";
+ break;
+ case 0xA2 :
+ return "\xC9\xA1";
+ break;
+ case 0xA3 :
+ return "\xC9\xA5";
+ break;
+ case 0xA4 :
+ return "\xC9\xA8";
+ break;
+ case 0xA5 :
+ return "\xC9\xA9";
+ break;
+ case 0xA6 :
+ return "\xC9\xAA";
+ break;
+ case 0xA7 :
+ return "\xE1\xB5\xBB";
+ break;
+ case 0xA8 :
+ return "\xCA\x9D";
+ break;
+ case 0xA9 :
+ return "\xC9\xAD";
+ break;
+ case 0xAA :
+ return "\xE1\xB6\x85";
+ break;
+ case 0xAB :
+ return "\xCA\x9F";
+ break;
+ case 0xAC :
+ return "\xC9\xB1";
+ break;
+ case 0xAD :
+ return "\xC9\xB0";
+ break;
+ case 0xAE :
+ return "\xC9\xB2";
+ break;
+ case 0xAF :
+ return "\xC9\xB3";
+ break;
+ case 0xB0 :
+ return "\xC9\xB4";
+ break;
+ case 0xB1 :
+ return "\xC9\xB5";
+ break;
+ case 0xB2 :
+ return "\xC9\xB8";
+ break;
+ case 0xB3 :
+ return "\xCA\x82";
+ break;
+ case 0xB4 :
+ return "\xCA\x83";
+ break;
+ case 0xB5 :
+ return "\xC6\xAB";
+ break;
+ case 0xB6 :
+ return "\xCA\x89";
+ break;
+ case 0xB7 :
+ return "\xCA\x8A";
+ break;
+ case 0xB8 :
+ return "\xE1\xB4\x9C";
+ break;
+ case 0xB9 :
+ return "\xCA\x8B";
+ break;
+ case 0xBA :
+ return "\xCA\x8C";
+ break;
+ case 0xBB :
+ return "\x7A";
+ break;
+ case 0xBC :
+ return "\xCA\x90";
+ break;
+ case 0xBD :
+ return "\xCA\x91";
+ break;
+ case 0xBE :
+ return "\xCA\x92";
+ break;
+ case 0xBF :
+ return "\xCE\xB8";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE1\xB8\x81";
+ break;
+ case 0x82 :
+ return "\xE1\xB8\x83";
+ break;
+ case 0x84 :
+ return "\xE1\xB8\x85";
+ break;
+ case 0x86 :
+ return "\xE1\xB8\x87";
+ break;
+ case 0x88 :
+ return "\xE1\xB8\x89";
+ break;
+ case 0x8A :
+ return "\xE1\xB8\x8B";
+ break;
+ case 0x8C :
+ return "\xE1\xB8\x8D";
+ break;
+ case 0x8E :
+ return "\xE1\xB8\x8F";
+ break;
+ case 0x90 :
+ return "\xE1\xB8\x91";
+ break;
+ case 0x92 :
+ return "\xE1\xB8\x93";
+ break;
+ case 0x94 :
+ return "\xE1\xB8\x95";
+ break;
+ case 0x96 :
+ return "\xE1\xB8\x97";
+ break;
+ case 0x98 :
+ return "\xE1\xB8\x99";
+ break;
+ case 0x9A :
+ return "\xE1\xB8\x9B";
+ break;
+ case 0x9C :
+ return "\xE1\xB8\x9D";
+ break;
+ case 0x9E :
+ return "\xE1\xB8\x9F";
+ break;
+ case 0xA0 :
+ return "\xE1\xB8\xA1";
+ break;
+ case 0xA2 :
+ return "\xE1\xB8\xA3";
+ break;
+ case 0xA4 :
+ return "\xE1\xB8\xA5";
+ break;
+ case 0xA6 :
+ return "\xE1\xB8\xA7";
+ break;
+ case 0xA8 :
+ return "\xE1\xB8\xA9";
+ break;
+ case 0xAA :
+ return "\xE1\xB8\xAB";
+ break;
+ case 0xAC :
+ return "\xE1\xB8\xAD";
+ break;
+ case 0xAE :
+ return "\xE1\xB8\xAF";
+ break;
+ case 0xB0 :
+ return "\xE1\xB8\xB1";
+ break;
+ case 0xB2 :
+ return "\xE1\xB8\xB3";
+ break;
+ case 0xB4 :
+ return "\xE1\xB8\xB5";
+ break;
+ case 0xB6 :
+ return "\xE1\xB8\xB7";
+ break;
+ case 0xB8 :
+ return "\xE1\xB8\xB9";
+ break;
+ case 0xBA :
+ return "\xE1\xB8\xBB";
+ break;
+ case 0xBC :
+ return "\xE1\xB8\xBD";
+ break;
+ case 0xBE :
+ return "\xE1\xB8\xBF";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE1\xB9\x81";
+ break;
+ case 0x82 :
+ return "\xE1\xB9\x83";
+ break;
+ case 0x84 :
+ return "\xE1\xB9\x85";
+ break;
+ case 0x86 :
+ return "\xE1\xB9\x87";
+ break;
+ case 0x88 :
+ return "\xE1\xB9\x89";
+ break;
+ case 0x8A :
+ return "\xE1\xB9\x8B";
+ break;
+ case 0x8C :
+ return "\xE1\xB9\x8D";
+ break;
+ case 0x8E :
+ return "\xE1\xB9\x8F";
+ break;
+ case 0x90 :
+ return "\xE1\xB9\x91";
+ break;
+ case 0x92 :
+ return "\xE1\xB9\x93";
+ break;
+ case 0x94 :
+ return "\xE1\xB9\x95";
+ break;
+ case 0x96 :
+ return "\xE1\xB9\x97";
+ break;
+ case 0x98 :
+ return "\xE1\xB9\x99";
+ break;
+ case 0x9A :
+ return "\xE1\xB9\x9B";
+ break;
+ case 0x9C :
+ return "\xE1\xB9\x9D";
+ break;
+ case 0x9E :
+ return "\xE1\xB9\x9F";
+ break;
+ case 0xA0 :
+ return "\xE1\xB9\xA1";
+ break;
+ case 0xA2 :
+ return "\xE1\xB9\xA3";
+ break;
+ case 0xA4 :
+ return "\xE1\xB9\xA5";
+ break;
+ case 0xA6 :
+ return "\xE1\xB9\xA7";
+ break;
+ case 0xA8 :
+ return "\xE1\xB9\xA9";
+ break;
+ case 0xAA :
+ return "\xE1\xB9\xAB";
+ break;
+ case 0xAC :
+ return "\xE1\xB9\xAD";
+ break;
+ case 0xAE :
+ return "\xE1\xB9\xAF";
+ break;
+ case 0xB0 :
+ return "\xE1\xB9\xB1";
+ break;
+ case 0xB2 :
+ return "\xE1\xB9\xB3";
+ break;
+ case 0xB4 :
+ return "\xE1\xB9\xB5";
+ break;
+ case 0xB6 :
+ return "\xE1\xB9\xB7";
+ break;
+ case 0xB8 :
+ return "\xE1\xB9\xB9";
+ break;
+ case 0xBA :
+ return "\xE1\xB9\xBB";
+ break;
+ case 0xBC :
+ return "\xE1\xB9\xBD";
+ break;
+ case 0xBE :
+ return "\xE1\xB9\xBF";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE1\xBA\x81";
+ break;
+ case 0x82 :
+ return "\xE1\xBA\x83";
+ break;
+ case 0x84 :
+ return "\xE1\xBA\x85";
+ break;
+ case 0x86 :
+ return "\xE1\xBA\x87";
+ break;
+ case 0x88 :
+ return "\xE1\xBA\x89";
+ break;
+ case 0x8A :
+ return "\xE1\xBA\x8B";
+ break;
+ case 0x8C :
+ return "\xE1\xBA\x8D";
+ break;
+ case 0x8E :
+ return "\xE1\xBA\x8F";
+ break;
+ case 0x90 :
+ return "\xE1\xBA\x91";
+ break;
+ case 0x92 :
+ return "\xE1\xBA\x93";
+ break;
+ case 0x94 :
+ return "\xE1\xBA\x95";
+ break;
+ case 0x9A :
+ return "\x61\xCA\xBE";
+ break;
+ case 0x9B :
+ return "\xE1\xB9\xA1";
+ break;
+ case 0xA0 :
+ return "\xE1\xBA\xA1";
+ break;
+ case 0xA2 :
+ return "\xE1\xBA\xA3";
+ break;
+ case 0xA4 :
+ return "\xE1\xBA\xA5";
+ break;
+ case 0xA6 :
+ return "\xE1\xBA\xA7";
+ break;
+ case 0xA8 :
+ return "\xE1\xBA\xA9";
+ break;
+ case 0xAA :
+ return "\xE1\xBA\xAB";
+ break;
+ case 0xAC :
+ return "\xE1\xBA\xAD";
+ break;
+ case 0xAE :
+ return "\xE1\xBA\xAF";
+ break;
+ case 0xB0 :
+ return "\xE1\xBA\xB1";
+ break;
+ case 0xB2 :
+ return "\xE1\xBA\xB3";
+ break;
+ case 0xB4 :
+ return "\xE1\xBA\xB5";
+ break;
+ case 0xB6 :
+ return "\xE1\xBA\xB7";
+ break;
+ case 0xB8 :
+ return "\xE1\xBA\xB9";
+ break;
+ case 0xBA :
+ return "\xE1\xBA\xBB";
+ break;
+ case 0xBC :
+ return "\xE1\xBA\xBD";
+ break;
+ case 0xBE :
+ return "\xE1\xBA\xBF";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE1\xBB\x81";
+ break;
+ case 0x82 :
+ return "\xE1\xBB\x83";
+ break;
+ case 0x84 :
+ return "\xE1\xBB\x85";
+ break;
+ case 0x86 :
+ return "\xE1\xBB\x87";
+ break;
+ case 0x88 :
+ return "\xE1\xBB\x89";
+ break;
+ case 0x8A :
+ return "\xE1\xBB\x8B";
+ break;
+ case 0x8C :
+ return "\xE1\xBB\x8D";
+ break;
+ case 0x8E :
+ return "\xE1\xBB\x8F";
+ break;
+ case 0x90 :
+ return "\xE1\xBB\x91";
+ break;
+ case 0x92 :
+ return "\xE1\xBB\x93";
+ break;
+ case 0x94 :
+ return "\xE1\xBB\x95";
+ break;
+ case 0x96 :
+ return "\xE1\xBB\x97";
+ break;
+ case 0x98 :
+ return "\xE1\xBB\x99";
+ break;
+ case 0x9A :
+ return "\xE1\xBB\x9B";
+ break;
+ case 0x9C :
+ return "\xE1\xBB\x9D";
+ break;
+ case 0x9E :
+ return "\xE1\xBB\x9F";
+ break;
+ case 0xA0 :
+ return "\xE1\xBB\xA1";
+ break;
+ case 0xA2 :
+ return "\xE1\xBB\xA3";
+ break;
+ case 0xA4 :
+ return "\xE1\xBB\xA5";
+ break;
+ case 0xA6 :
+ return "\xE1\xBB\xA7";
+ break;
+ case 0xA8 :
+ return "\xE1\xBB\xA9";
+ break;
+ case 0xAA :
+ return "\xE1\xBB\xAB";
+ break;
+ case 0xAC :
+ return "\xE1\xBB\xAD";
+ break;
+ case 0xAE :
+ return "\xE1\xBB\xAF";
+ break;
+ case 0xB0 :
+ return "\xE1\xBB\xB1";
+ break;
+ case 0xB2 :
+ return "\xE1\xBB\xB3";
+ break;
+ case 0xB4 :
+ return "\xE1\xBB\xB5";
+ break;
+ case 0xB6 :
+ return "\xE1\xBB\xB7";
+ break;
+ case 0xB8 :
+ return "\xE1\xBB\xB9";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (str[2]) {
+ case 0xB1 :
+ return "\xCE\xAC";
+ break;
+ case 0xB3 :
+ return "\xCE\xAD";
+ break;
+ case 0xB5 :
+ return "\xCE\xAE";
+ break;
+ case 0xB7 :
+ return "\xCE\xAF";
+ break;
+ case 0xB9 :
+ return "\xCF\x8C";
+ break;
+ case 0xBB :
+ return "\xCF\x8D";
+ break;
+ case 0xBD :
+ return "\xCF\x8E";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (str[2]) {
+ case 0xBB :
+ return "\xCE\x86";
+ break;
+ case 0xBD :
+ return "\xCC\x93";
+ break;
+ case 0xBE :
+ return "\xCE\xB9";
+ break;
+ case 0xBF :
+ return "\xCC\x93";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xCD\x82";
+ break;
+ case 0x81 :
+ return "\xCC\x88\xCD\x82";
+ break;
+ case 0x89 :
+ return "\xCE\x88";
+ break;
+ case 0x8B :
+ return "\xCE\x89";
+ break;
+ case 0x8D :
+ return "\xCC\x93\xCC\x80";
+ break;
+ case 0x8E :
+ return "\xCC\x93\xCC\x81";
+ break;
+ case 0x8F :
+ return "\xCC\x93\xCD\x82";
+ break;
+ case 0x93 :
+ return "\xCE\x90";
+ break;
+ case 0x9B :
+ return "\xCE\x8A";
+ break;
+ case 0x9D :
+ return "\xCC\x94\xCC\x80";
+ break;
+ case 0x9E :
+ return "\xCC\x94\xCC\x81";
+ break;
+ case 0x9F :
+ return "\xCC\x94\xCD\x82";
+ break;
+ case 0xA3 :
+ return "\xCE\xB0";
+ break;
+ case 0xAB :
+ return "\xCE\x8E";
+ break;
+ case 0xAD :
+ return "\xCC\x88\xCC\x80";
+ break;
+ case 0xAE :
+ return "\xCC\x88\xCC\x81";
+ break;
+ case 0xAF :
+ return "\x60";
+ break;
+ case 0xB9 :
+ return "\xCE\x8C";
+ break;
+ case 0xBB :
+ return "\xCE\x8F";
+ break;
+ case 0xBD :
+ return "\xCC\x81";
+ break;
+ case 0xBE :
+ return "\xCC\x94";
+ break;
+ }
+ break;
+ }
+ break;
+case 0xE2 :
+ switch (str[1]) {
+ case 0x80 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x20";
+ break;
+ case 0x81 :
+ return "\x20";
+ break;
+ case 0x82 :
+ return "\x20";
+ break;
+ case 0x83 :
+ return "\x20";
+ break;
+ case 0x84 :
+ return "\x20";
+ break;
+ case 0x85 :
+ return "\x20";
+ break;
+ case 0x86 :
+ return "\x20";
+ break;
+ case 0x87 :
+ return "\x20";
+ break;
+ case 0x88 :
+ return "\x20";
+ break;
+ case 0x89 :
+ return "\x20";
+ break;
+ case 0x8A :
+ return "\x20";
+ break;
+ case 0x91 :
+ return "\xE2\x80\x90";
+ break;
+ case 0x97 :
+ return "\xCC\xB3";
+ break;
+ case 0xA4 :
+ return "\x2E";
+ break;
+ case 0xA5 :
+ return "\x2E\x2E";
+ break;
+ case 0xA6 :
+ return "\x2E\x2E\x2E";
+ break;
+ case 0xAF :
+ return "\x20";
+ break;
+ case 0xB3 :
+ return "\xE2\x80\xB2\xE2\x80\xB2";
+ break;
+ case 0xB4 :
+ return "\xE2\x80\xB2\xE2\x80\xB2\xE2\x80\xB2";
+ break;
+ case 0xB6 :
+ return "\xE2\x80\xB5\xE2\x80\xB5";
+ break;
+ case 0xB7 :
+ return "\xE2\x80\xB5\xE2\x80\xB5\xE2\x80\xB5";
+ break;
+ case 0xBC :
+ return "\x21\x21";
+ break;
+ case 0xBE :
+ return "\xCC\x85";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (str[2]) {
+ case 0x87 :
+ return "\x3F\x3F";
+ break;
+ case 0x88 :
+ return "\x3F\x21";
+ break;
+ case 0x89 :
+ return "\x21\x3F";
+ break;
+ case 0x97 :
+ return "\xE2\x80\xB2\xE2\x80\xB2\xE2\x80\xB2\xE2\x80\xB2";
+ break;
+ case 0x9F :
+ return "\x20";
+ break;
+ case 0xB0 :
+ return "\x30";
+ break;
+ case 0xB1 :
+ return "\x69";
+ break;
+ case 0xB4 :
+ return "\x34";
+ break;
+ case 0xB5 :
+ return "\x35";
+ break;
+ case 0xB6 :
+ return "\x36";
+ break;
+ case 0xB7 :
+ return "\x37";
+ break;
+ case 0xB8 :
+ return "\x38";
+ break;
+ case 0xB9 :
+ return "\x39";
+ break;
+ case 0xBA :
+ return "\x2B";
+ break;
+ case 0xBB :
+ return "\xE2\x88\x92";
+ break;
+ case 0xBC :
+ return "\x3D";
+ break;
+ case 0xBD :
+ return "\x28";
+ break;
+ case 0xBE :
+ return "\x29";
+ break;
+ case 0xBF :
+ return "\x6E";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x30";
+ break;
+ case 0x81 :
+ return "\x31";
+ break;
+ case 0x82 :
+ return "\x32";
+ break;
+ case 0x83 :
+ return "\x33";
+ break;
+ case 0x84 :
+ return "\x34";
+ break;
+ case 0x85 :
+ return "\x35";
+ break;
+ case 0x86 :
+ return "\x36";
+ break;
+ case 0x87 :
+ return "\x37";
+ break;
+ case 0x88 :
+ return "\x38";
+ break;
+ case 0x89 :
+ return "\x39";
+ break;
+ case 0x8A :
+ return "\x2B";
+ break;
+ case 0x8B :
+ return "\xE2\x88\x92";
+ break;
+ case 0x8C :
+ return "\x3D";
+ break;
+ case 0x8D :
+ return "\x28";
+ break;
+ case 0x8E :
+ return "\x29";
+ break;
+ case 0x90 :
+ return "\x61";
+ break;
+ case 0x91 :
+ return "\x65";
+ break;
+ case 0x92 :
+ return "\x6F";
+ break;
+ case 0x93 :
+ return "\x78";
+ break;
+ case 0x94 :
+ return "\xC9\x99";
+ break;
+ case 0xA8 :
+ return "\x72\x73";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x61\x2F\x63";
+ break;
+ case 0x81 :
+ return "\x61\x2F\x73";
+ break;
+ case 0x82 :
+ return "\x63";
+ break;
+ case 0x83 :
+ return "\xC2\xB0\x63";
+ break;
+ case 0x85 :
+ return "\x63\x2F\x6F";
+ break;
+ case 0x86 :
+ return "\x63\x2F\x75";
+ break;
+ case 0x87 :
+ return "\xC6\x90";
+ break;
+ case 0x89 :
+ return "\xC2\xB0\x66";
+ break;
+ case 0x8A :
+ return "\x67";
+ break;
+ case 0x8B :
+ return "\x68";
+ break;
+ case 0x8C :
+ return "\x68";
+ break;
+ case 0x8D :
+ return "\x68";
+ break;
+ case 0x8E :
+ return "\x68";
+ break;
+ case 0x8F :
+ return "\xC4\xA7";
+ break;
+ case 0x90 :
+ return "\x69";
+ break;
+ case 0x91 :
+ return "\x69";
+ break;
+ case 0x92 :
+ return "\x6C";
+ break;
+ case 0x93 :
+ return "\x6C";
+ break;
+ case 0x95 :
+ return "\x6E";
+ break;
+ case 0x96 :
+ return "\x6E\x6F";
+ break;
+ case 0x99 :
+ return "\x70";
+ break;
+ case 0x9A :
+ return "\x71";
+ break;
+ case 0x9B :
+ return "\x72";
+ break;
+ case 0x9C :
+ return "\x72";
+ break;
+ case 0x9D :
+ return "\x72";
+ break;
+ case 0xA0 :
+ return "\x73\x6D";
+ break;
+ case 0xA1 :
+ return "\x74\x65\x6C";
+ break;
+ case 0xA2 :
+ return "\x74\x6D";
+ break;
+ case 0xA4 :
+ return "\x7A";
+ break;
+ case 0xA6 :
+ return "\xCE\xA9";
+ break;
+ case 0xA8 :
+ return "\x7A";
+ break;
+ case 0xAA :
+ return "\x6B";
+ break;
+ case 0xAB :
+ return "\xC3\xA5";
+ break;
+ case 0xAC :
+ return "\x62";
+ break;
+ case 0xAD :
+ return "\x63";
+ break;
+ case 0xAF :
+ return "\x65";
+ break;
+ case 0xB0 :
+ return "\x65";
+ break;
+ case 0xB1 :
+ return "\x66";
+ break;
+ case 0xB3 :
+ return "\x6D";
+ break;
+ case 0xB4 :
+ return "\x6F";
+ break;
+ case 0xB5 :
+ return "\xD7\x90";
+ break;
+ case 0xB6 :
+ return "\xD7\x91";
+ break;
+ case 0xB7 :
+ return "\xD7\x92";
+ break;
+ case 0xB8 :
+ return "\xD7\x93";
+ break;
+ case 0xB9 :
+ return "\x69";
+ break;
+ case 0xBB :
+ return "\x66\x61\x78";
+ break;
+ case 0xBC :
+ return "\xCF\x80";
+ break;
+ case 0xBD :
+ return "\xCE\xB3";
+ break;
+ case 0xBE :
+ return "\xCE\x93";
+ break;
+ case 0xBF :
+ return "\xCE\xA0";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE2\x88\x91";
+ break;
+ case 0x85 :
+ return "\x64";
+ break;
+ case 0x86 :
+ return "\x64";
+ break;
+ case 0x87 :
+ return "\x65";
+ break;
+ case 0x88 :
+ return "\x69";
+ break;
+ case 0x89 :
+ return "\x6A";
+ break;
+ case 0x93 :
+ return "\x31\xE2\x81\x84\x33";
+ break;
+ case 0x94 :
+ return "\x32\xE2\x81\x84\x33";
+ break;
+ case 0x95 :
+ return "\x31\xE2\x81\x84\x35";
+ break;
+ case 0x96 :
+ return "\x32\xE2\x81\x84\x35";
+ break;
+ case 0x97 :
+ return "\x33\xE2\x81\x84\x35";
+ break;
+ case 0x98 :
+ return "\x34\xE2\x81\x84\x35";
+ break;
+ case 0x99 :
+ return "\x31\xE2\x81\x84\x36";
+ break;
+ case 0x9A :
+ return "\x35\xE2\x81\x84\x36";
+ break;
+ case 0x9B :
+ return "\x31\xE2\x81\x84\x38";
+ break;
+ case 0x9C :
+ return "\x33\xE2\x81\x84\x38";
+ break;
+ case 0x9D :
+ return "\x35\xE2\x81\x84\x38";
+ break;
+ case 0x9E :
+ return "\x37\xE2\x81\x84\x38";
+ break;
+ case 0x9F :
+ return "\x31\xE2\x81\x84";
+ break;
+ case 0xA0 :
+ return "\x69";
+ break;
+ case 0xA1 :
+ return "\x69\x69";
+ break;
+ case 0xA2 :
+ return "\x69\x69\x69";
+ break;
+ case 0xA3 :
+ return "\x69\x76";
+ break;
+ case 0xA4 :
+ return "\x76";
+ break;
+ case 0xA5 :
+ return "\x76\x69";
+ break;
+ case 0xA6 :
+ return "\x76\x69\x69";
+ break;
+ case 0xA7 :
+ return "\x76\x69\x69\x69";
+ break;
+ case 0xA8 :
+ return "\x69\x78";
+ break;
+ case 0xA9 :
+ return "\x78";
+ break;
+ case 0xAA :
+ return "\x78\x69";
+ break;
+ case 0xAB :
+ return "\x78\x69\x69";
+ break;
+ case 0xAC :
+ return "\x6C";
+ break;
+ case 0xAD :
+ return "\x63";
+ break;
+ case 0xAE :
+ return "\x64";
+ break;
+ case 0xAF :
+ return "\x6D";
+ break;
+ case 0xB0 :
+ return "\x69";
+ break;
+ case 0xB1 :
+ return "\x69\x69";
+ break;
+ case 0xB2 :
+ return "\x69\x69\x69";
+ break;
+ case 0xB3 :
+ return "\x69\x76";
+ break;
+ case 0xB4 :
+ return "\x76";
+ break;
+ case 0xB5 :
+ return "\x76\x69";
+ break;
+ case 0xB6 :
+ return "\x76\x69\x69";
+ break;
+ case 0xB7 :
+ return "\x76\x69\x69\x69";
+ break;
+ case 0xB8 :
+ return "\x69\x78";
+ break;
+ case 0xB9 :
+ return "\x78";
+ break;
+ case 0xBA :
+ return "\x78\x69";
+ break;
+ case 0xBB :
+ return "\x78\x69\x69";
+ break;
+ case 0xBC :
+ return "\x6C";
+ break;
+ case 0xBD :
+ return "\x63";
+ break;
+ case 0xBE :
+ return "\x64";
+ break;
+ case 0xBF :
+ return "\x6D";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (str[2]) {
+ case 0xAC :
+ return "\xE2\x88\xAB\xE2\x88\xAB";
+ break;
+ case 0xAD :
+ return "\xE2\x88\xAB\xE2\x88\xAB\xE2\x88\xAB";
+ break;
+ case 0xAF :
+ return "\xE2\x88\xAE\xE2\x88\xAE";
+ break;
+ case 0xB0 :
+ return "\xE2\x88\xAE\xE2\x88\xAE\xE2\x88\xAE";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (str[2]) {
+ case 0xA9 :
+ return "\xE3\x80\x88";
+ break;
+ case 0xAA :
+ return "\xE3\x80\x89";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (str[2]) {
+ case 0xA0 :
+ return "\x31";
+ break;
+ case 0xA1 :
+ return "\x32";
+ break;
+ case 0xA2 :
+ return "\x33";
+ break;
+ case 0xA3 :
+ return "\x34";
+ break;
+ case 0xA4 :
+ return "\x35";
+ break;
+ case 0xA5 :
+ return "\x36";
+ break;
+ case 0xA6 :
+ return "\x37";
+ break;
+ case 0xA7 :
+ return "\x38";
+ break;
+ case 0xA8 :
+ return "\x39";
+ break;
+ case 0xA9 :
+ return "\x31\x30";
+ break;
+ case 0xAA :
+ return "\x31\x31";
+ break;
+ case 0xAB :
+ return "\x31\x32";
+ break;
+ case 0xAC :
+ return "\x31\x33";
+ break;
+ case 0xAD :
+ return "\x31\x34";
+ break;
+ case 0xAE :
+ return "\x31\x35";
+ break;
+ case 0xAF :
+ return "\x31\x36";
+ break;
+ case 0xB0 :
+ return "\x31\x37";
+ break;
+ case 0xB1 :
+ return "\x31\x38";
+ break;
+ case 0xB2 :
+ return "\x31\x39";
+ break;
+ case 0xB3 :
+ return "\x32\x30";
+ break;
+ case 0xB4 :
+ return "\x28\x31\x29";
+ break;
+ case 0xB5 :
+ return "\x28\x32\x29";
+ break;
+ case 0xB6 :
+ return "\x28\x33\x29";
+ break;
+ case 0xB7 :
+ return "\x28\x34\x29";
+ break;
+ case 0xB8 :
+ return "\x28\x35\x29";
+ break;
+ case 0xB9 :
+ return "\x28\x36\x29";
+ break;
+ case 0xBA :
+ return "\x28\x37\x29";
+ break;
+ case 0xBB :
+ return "\x28\x38\x29";
+ break;
+ case 0xBC :
+ return "\x28\x39\x29";
+ break;
+ case 0xBD :
+ return "\x28\x31\x30\x29";
+ break;
+ case 0xBE :
+ return "\x28\x31\x31\x29";
+ break;
+ case 0xBF :
+ return "\x28\x31\x32\x29";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x28\x31\x33\x29";
+ break;
+ case 0x81 :
+ return "\x28\x31\x34\x29";
+ break;
+ case 0x82 :
+ return "\x28\x31\x35\x29";
+ break;
+ case 0x83 :
+ return "\x28\x31\x36\x29";
+ break;
+ case 0x84 :
+ return "\x28\x31\x37\x29";
+ break;
+ case 0x85 :
+ return "\x28\x31\x38\x29";
+ break;
+ case 0x86 :
+ return "\x28\x31\x39\x29";
+ break;
+ case 0x87 :
+ return "\x28\x32\x30\x29";
+ break;
+ case 0x88 :
+ return "\x31\x2E";
+ break;
+ case 0x89 :
+ return "\x32\x2E";
+ break;
+ case 0x8A :
+ return "\x33\x2E";
+ break;
+ case 0x8B :
+ return "\x34\x2E";
+ break;
+ case 0x8C :
+ return "\x35\x2E";
+ break;
+ case 0x8D :
+ return "\x36\x2E";
+ break;
+ case 0x8E :
+ return "\x37\x2E";
+ break;
+ case 0x8F :
+ return "\x38\x2E";
+ break;
+ case 0x90 :
+ return "\x39\x2E";
+ break;
+ case 0x91 :
+ return "\x31\x30\x2E";
+ break;
+ case 0x92 :
+ return "\x31\x31\x2E";
+ break;
+ case 0x93 :
+ return "\x31\x32\x2E";
+ break;
+ case 0x94 :
+ return "\x31\x33\x2E";
+ break;
+ case 0x95 :
+ return "\x31\x34\x2E";
+ break;
+ case 0x96 :
+ return "\x31\x35\x2E";
+ break;
+ case 0x97 :
+ return "\x31\x36\x2E";
+ break;
+ case 0x98 :
+ return "\x31\x37\x2E";
+ break;
+ case 0x99 :
+ return "\x31\x38\x2E";
+ break;
+ case 0x9A :
+ return "\x31\x39\x2E";
+ break;
+ case 0x9B :
+ return "\x32\x30\x2E";
+ break;
+ case 0x9C :
+ return "\x28\x61\x29";
+ break;
+ case 0x9D :
+ return "\x28\x62\x29";
+ break;
+ case 0x9E :
+ return "\x28\x63\x29";
+ break;
+ case 0x9F :
+ return "\x28\x64\x29";
+ break;
+ case 0xA0 :
+ return "\x28\x65\x29";
+ break;
+ case 0xA1 :
+ return "\x28\x66\x29";
+ break;
+ case 0xA2 :
+ return "\x28\x67\x29";
+ break;
+ case 0xA3 :
+ return "\x28\x68\x29";
+ break;
+ case 0xA4 :
+ return "\x28\x69\x29";
+ break;
+ case 0xA5 :
+ return "\x28\x6A\x29";
+ break;
+ case 0xA6 :
+ return "\x28\x6B\x29";
+ break;
+ case 0xA7 :
+ return "\x28\x6C\x29";
+ break;
+ case 0xA8 :
+ return "\x28\x6D\x29";
+ break;
+ case 0xA9 :
+ return "\x28\x6E\x29";
+ break;
+ case 0xAA :
+ return "\x28\x6F\x29";
+ break;
+ case 0xAB :
+ return "\x28\x70\x29";
+ break;
+ case 0xAC :
+ return "\x28\x71\x29";
+ break;
+ case 0xAD :
+ return "\x28\x72\x29";
+ break;
+ case 0xAE :
+ return "\x28\x73\x29";
+ break;
+ case 0xAF :
+ return "\x28\x74\x29";
+ break;
+ case 0xB0 :
+ return "\x28\x75\x29";
+ break;
+ case 0xB1 :
+ return "\x28\x76\x29";
+ break;
+ case 0xB2 :
+ return "\x28\x77\x29";
+ break;
+ case 0xB3 :
+ return "\x28\x78\x29";
+ break;
+ case 0xB4 :
+ return "\x28\x79\x29";
+ break;
+ case 0xB5 :
+ return "\x28\x7A\x29";
+ break;
+ case 0xB6 :
+ return "\x61";
+ break;
+ case 0xB7 :
+ return "\x62";
+ break;
+ case 0xB8 :
+ return "\x63";
+ break;
+ case 0xB9 :
+ return "\x64";
+ break;
+ case 0xBA :
+ return "\x65";
+ break;
+ case 0xBB :
+ return "\x66";
+ break;
+ case 0xBC :
+ return "\x67";
+ break;
+ case 0xBD :
+ return "\x68";
+ break;
+ case 0xBE :
+ return "\x69";
+ break;
+ case 0xBF :
+ return "\x6A";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x6B";
+ break;
+ case 0x81 :
+ return "\x6C";
+ break;
+ case 0x82 :
+ return "\x6D";
+ break;
+ case 0x83 :
+ return "\x6E";
+ break;
+ case 0x84 :
+ return "\x6F";
+ break;
+ case 0x85 :
+ return "\x70";
+ break;
+ case 0x86 :
+ return "\x71";
+ break;
+ case 0x87 :
+ return "\x72";
+ break;
+ case 0x88 :
+ return "\x73";
+ break;
+ case 0x89 :
+ return "\x74";
+ break;
+ case 0x8A :
+ return "\x75";
+ break;
+ case 0x8B :
+ return "\x76";
+ break;
+ case 0x8C :
+ return "\x77";
+ break;
+ case 0x8D :
+ return "\x78";
+ break;
+ case 0x8E :
+ return "\x79";
+ break;
+ case 0x8F :
+ return "\x7A";
+ break;
+ case 0x90 :
+ return "\x61";
+ break;
+ case 0x91 :
+ return "\x62";
+ break;
+ case 0x92 :
+ return "\x63";
+ break;
+ case 0x93 :
+ return "\x64";
+ break;
+ case 0x94 :
+ return "\x65";
+ break;
+ case 0x95 :
+ return "\x66";
+ break;
+ case 0x96 :
+ return "\x67";
+ break;
+ case 0x97 :
+ return "\x68";
+ break;
+ case 0x98 :
+ return "\x69";
+ break;
+ case 0x99 :
+ return "\x6A";
+ break;
+ case 0x9A :
+ return "\x6B";
+ break;
+ case 0x9B :
+ return "\x6C";
+ break;
+ case 0x9C :
+ return "\x6D";
+ break;
+ case 0x9D :
+ return "\x6E";
+ break;
+ case 0x9E :
+ return "\x6F";
+ break;
+ case 0x9F :
+ return "\x70";
+ break;
+ case 0xA0 :
+ return "\x71";
+ break;
+ case 0xA1 :
+ return "\x72";
+ break;
+ case 0xA2 :
+ return "\x73";
+ break;
+ case 0xA3 :
+ return "\x74";
+ break;
+ case 0xA4 :
+ return "\x75";
+ break;
+ case 0xA5 :
+ return "\x76";
+ break;
+ case 0xA6 :
+ return "\x77";
+ break;
+ case 0xA7 :
+ return "\x78";
+ break;
+ case 0xA8 :
+ return "\x79";
+ break;
+ case 0xA9 :
+ return "\x7A";
+ break;
+ case 0xAA :
+ return "\x30";
+ break;
+ }
+ break;
+ case 0xA8 :
+ if (str[2] == 0x8C) {
+ return "\xE2\x88\xAB\xE2\x88\xAB\xE2\x88\xAB\xE2\x88\xAB";
+ }
+ break;
+ case 0xA9 :
+ switch (str[2]) {
+ case 0xB4 :
+ return "\x3A\x3A\x3D";
+ break;
+ case 0xB5 :
+ return "\x3D\x3D";
+ break;
+ case 0xB6 :
+ return "\x3D\x3D\x3D";
+ break;
+ }
+ break;
+ case 0xAB :
+ if (str[2] == 0x9C) {
+ return "\xE2\xAB\x9D\xCC\xB8";
+ }
+ break;
+ case 0xB5 :
+ if (str[2] == 0xAF) {
+ return "\xE2\xB5\xA1";
+ }
+ break;
+ case 0xBA :
+ if (str[2] == 0x9F) {
+ return "\xE6\xAF\x8D";
+ }
+ break;
+ case 0xBB :
+ if (str[2] == 0xB3) {
+ return "\xE9\xBE\x9F";
+ }
+ break;
+ case 0xBC :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE4\xB8\x80";
+ break;
+ case 0x81 :
+ return "\xE4\xB8\xA8";
+ break;
+ case 0x82 :
+ return "\xE4\xB8\xB6";
+ break;
+ case 0x83 :
+ return "\xE4\xB8\xBF";
+ break;
+ case 0x84 :
+ return "\xE4\xB9\x99";
+ break;
+ case 0x85 :
+ return "\xE4\xBA\x85";
+ break;
+ case 0x86 :
+ return "\xE4\xBA\x8C";
+ break;
+ case 0x87 :
+ return "\xE4\xBA\xA0";
+ break;
+ case 0x88 :
+ return "\xE4\xBA\xBA";
+ break;
+ case 0x89 :
+ return "\xE5\x84\xBF";
+ break;
+ case 0x8A :
+ return "\xE5\x85\xA5";
+ break;
+ case 0x8B :
+ return "\xE5\x85\xAB";
+ break;
+ case 0x8C :
+ return "\xE5\x86\x82";
+ break;
+ case 0x8D :
+ return "\xE5\x86\x96";
+ break;
+ case 0x8E :
+ return "\xE5\x86\xAB";
+ break;
+ case 0x8F :
+ return "\xE5\x87\xA0";
+ break;
+ case 0x90 :
+ return "\xE5\x87\xB5";
+ break;
+ case 0x91 :
+ return "\xE5\x88\x80";
+ break;
+ case 0x92 :
+ return "\xE5\x8A\x9B";
+ break;
+ case 0x93 :
+ return "\xE5\x8B\xB9";
+ break;
+ case 0x94 :
+ return "\xE5\x8C\x95";
+ break;
+ case 0x95 :
+ return "\xE5\x8C\x9A";
+ break;
+ case 0x96 :
+ return "\xE5\x8C\xB8";
+ break;
+ case 0x97 :
+ return "\xE5\x8D\x81";
+ break;
+ case 0x98 :
+ return "\xE5\x8D\x9C";
+ break;
+ case 0x99 :
+ return "\xE5\x8D\xA9";
+ break;
+ case 0x9A :
+ return "\xE5\x8E\x82";
+ break;
+ case 0x9B :
+ return "\xE5\x8E\xB6";
+ break;
+ case 0x9C :
+ return "\xE5\x8F\x88";
+ break;
+ case 0x9D :
+ return "\xE5\x8F\xA3";
+ break;
+ case 0x9E :
+ return "\xE5\x9B\x97";
+ break;
+ case 0x9F :
+ return "\xE5\x9C\x9F";
+ break;
+ case 0xA0 :
+ return "\xE5\xA3\xAB";
+ break;
+ case 0xA1 :
+ return "\xE5\xA4\x82";
+ break;
+ case 0xA2 :
+ return "\xE5\xA4\x8A";
+ break;
+ case 0xA3 :
+ return "\xE5\xA4\x95";
+ break;
+ case 0xA4 :
+ return "\xE5\xA4\xA7";
+ break;
+ case 0xA5 :
+ return "\xE5\xA5\xB3";
+ break;
+ case 0xA6 :
+ return "\xE5\xAD\x90";
+ break;
+ case 0xA7 :
+ return "\xE5\xAE\x80";
+ break;
+ case 0xA8 :
+ return "\xE5\xAF\xB8";
+ break;
+ case 0xA9 :
+ return "\xE5\xB0\x8F";
+ break;
+ case 0xAA :
+ return "\xE5\xB0\xA2";
+ break;
+ case 0xAB :
+ return "\xE5\xB0\xB8";
+ break;
+ case 0xAC :
+ return "\xE5\xB1\xAE";
+ break;
+ case 0xAD :
+ return "\xE5\xB1\xB1";
+ break;
+ case 0xAE :
+ return "\xE5\xB7\x9B";
+ break;
+ case 0xAF :
+ return "\xE5\xB7\xA5";
+ break;
+ case 0xB0 :
+ return "\xE5\xB7\xB1";
+ break;
+ case 0xB1 :
+ return "\xE5\xB7\xBE";
+ break;
+ case 0xB2 :
+ return "\xE5\xB9\xB2";
+ break;
+ case 0xB3 :
+ return "\xE5\xB9\xBA";
+ break;
+ case 0xB4 :
+ return "\xE5\xB9\xBF";
+ break;
+ case 0xB5 :
+ return "\xE5\xBB\xB4";
+ break;
+ case 0xB6 :
+ return "\xE5\xBB\xBE";
+ break;
+ case 0xB7 :
+ return "\xE5\xBC\x8B";
+ break;
+ case 0xB8 :
+ return "\xE5\xBC\x93";
+ break;
+ case 0xB9 :
+ return "\xE5\xBD\x90";
+ break;
+ case 0xBA :
+ return "\xE5\xBD\xA1";
+ break;
+ case 0xBB :
+ return "\xE5\xBD\xB3";
+ break;
+ case 0xBC :
+ return "\xE5\xBF\x83";
+ break;
+ case 0xBD :
+ return "\xE6\x88\x88";
+ break;
+ case 0xBE :
+ return "\xE6\x88\xB6";
+ break;
+ case 0xBF :
+ return "\xE6\x89\x8B";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE6\x94\xAF";
+ break;
+ case 0x81 :
+ return "\xE6\x94\xB4";
+ break;
+ case 0x82 :
+ return "\xE6\x96\x87";
+ break;
+ case 0x83 :
+ return "\xE6\x96\x97";
+ break;
+ case 0x84 :
+ return "\xE6\x96\xA4";
+ break;
+ case 0x85 :
+ return "\xE6\x96\xB9";
+ break;
+ case 0x86 :
+ return "\xE6\x97\xA0";
+ break;
+ case 0x87 :
+ return "\xE6\x97\xA5";
+ break;
+ case 0x88 :
+ return "\xE6\x9B\xB0";
+ break;
+ case 0x89 :
+ return "\xE6\x9C\x88";
+ break;
+ case 0x8A :
+ return "\xE6\x9C\xA8";
+ break;
+ case 0x8B :
+ return "\xE6\xAC\xA0";
+ break;
+ case 0x8C :
+ return "\xE6\xAD\xA2";
+ break;
+ case 0x8D :
+ return "\xE6\xAD\xB9";
+ break;
+ case 0x8E :
+ return "\xE6\xAE\xB3";
+ break;
+ case 0x8F :
+ return "\xE6\xAF\x8B";
+ break;
+ case 0x90 :
+ return "\xE6\xAF\x94";
+ break;
+ case 0x91 :
+ return "\xE6\xAF\x9B";
+ break;
+ case 0x92 :
+ return "\xE6\xB0\x8F";
+ break;
+ case 0x93 :
+ return "\xE6\xB0\x94";
+ break;
+ case 0x94 :
+ return "\xE6\xB0\xB4";
+ break;
+ case 0x95 :
+ return "\xE7\x81\xAB";
+ break;
+ case 0x96 :
+ return "\xE7\x88\xAA";
+ break;
+ case 0x97 :
+ return "\xE7\x88\xB6";
+ break;
+ case 0x98 :
+ return "\xE7\x88\xBB";
+ break;
+ case 0x99 :
+ return "\xE7\x88\xBF";
+ break;
+ case 0x9A :
+ return "\xE7\x89\x87";
+ break;
+ case 0x9B :
+ return "\xE7\x89\x99";
+ break;
+ case 0x9C :
+ return "\xE7\x89\x9B";
+ break;
+ case 0x9D :
+ return "\xE7\x8A\xAC";
+ break;
+ case 0x9E :
+ return "\xE7\x8E\x84";
+ break;
+ case 0x9F :
+ return "\xE7\x8E\x89";
+ break;
+ case 0xA0 :
+ return "\xE7\x93\x9C";
+ break;
+ case 0xA1 :
+ return "\xE7\x93\xA6";
+ break;
+ case 0xA2 :
+ return "\xE7\x94\x98";
+ break;
+ case 0xA3 :
+ return "\xE7\x94\x9F";
+ break;
+ case 0xA4 :
+ return "\xE7\x94\xA8";
+ break;
+ case 0xA5 :
+ return "\xE7\x94\xB0";
+ break;
+ case 0xA6 :
+ return "\xE7\x96\x8B";
+ break;
+ case 0xA7 :
+ return "\xE7\x96\x92";
+ break;
+ case 0xA8 :
+ return "\xE7\x99\xB6";
+ break;
+ case 0xA9 :
+ return "\xE7\x99\xBD";
+ break;
+ case 0xAA :
+ return "\xE7\x9A\xAE";
+ break;
+ case 0xAB :
+ return "\xE7\x9A\xBF";
+ break;
+ case 0xAC :
+ return "\xE7\x9B\xAE";
+ break;
+ case 0xAD :
+ return "\xE7\x9F\x9B";
+ break;
+ case 0xAE :
+ return "\xE7\x9F\xA2";
+ break;
+ case 0xAF :
+ return "\xE7\x9F\xB3";
+ break;
+ case 0xB0 :
+ return "\xE7\xA4\xBA";
+ break;
+ case 0xB1 :
+ return "\xE7\xA6\xB8";
+ break;
+ case 0xB2 :
+ return "\xE7\xA6\xBE";
+ break;
+ case 0xB3 :
+ return "\xE7\xA9\xB4";
+ break;
+ case 0xB4 :
+ return "\xE7\xAB\x8B";
+ break;
+ case 0xB5 :
+ return "\xE7\xAB\xB9";
+ break;
+ case 0xB6 :
+ return "\xE7\xB1\xB3";
+ break;
+ case 0xB7 :
+ return "\xE7\xB3\xB8";
+ break;
+ case 0xB8 :
+ return "\xE7\xBC\xB6";
+ break;
+ case 0xB9 :
+ return "\xE7\xBD\x91";
+ break;
+ case 0xBA :
+ return "\xE7\xBE\x8A";
+ break;
+ case 0xBB :
+ return "\xE7\xBE\xBD";
+ break;
+ case 0xBC :
+ return "\xE8\x80\x81";
+ break;
+ case 0xBD :
+ return "\xE8\x80\x8C";
+ break;
+ case 0xBE :
+ return "\xE8\x80\x92";
+ break;
+ case 0xBF :
+ return "\xE8\x80\xB3";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE8\x81\xBF";
+ break;
+ case 0x81 :
+ return "\xE8\x82\x89";
+ break;
+ case 0x82 :
+ return "\xE8\x87\xA3";
+ break;
+ case 0x83 :
+ return "\xE8\x87\xAA";
+ break;
+ case 0x84 :
+ return "\xE8\x87\xB3";
+ break;
+ case 0x85 :
+ return "\xE8\x87\xBC";
+ break;
+ case 0x86 :
+ return "\xE8\x88\x8C";
+ break;
+ case 0x87 :
+ return "\xE8\x88\x9B";
+ break;
+ case 0x88 :
+ return "\xE8\x88\x9F";
+ break;
+ case 0x89 :
+ return "\xE8\x89\xAE";
+ break;
+ case 0x8A :
+ return "\xE8\x89\xB2";
+ break;
+ case 0x8B :
+ return "\xE8\x89\xB8";
+ break;
+ case 0x8C :
+ return "\xE8\x99\x8D";
+ break;
+ case 0x8D :
+ return "\xE8\x99\xAB";
+ break;
+ case 0x8E :
+ return "\xE8\xA1\x80";
+ break;
+ case 0x8F :
+ return "\xE8\xA1\x8C";
+ break;
+ case 0x90 :
+ return "\xE8\xA1\xA3";
+ break;
+ case 0x91 :
+ return "\xE8\xA5\xBE";
+ break;
+ case 0x92 :
+ return "\xE8\xA6\x8B";
+ break;
+ case 0x93 :
+ return "\xE8\xA7\x92";
+ break;
+ case 0x94 :
+ return "\xE8\xA8\x80";
+ break;
+ case 0x95 :
+ return "\xE8\xB0\xB7";
+ break;
+ case 0x96 :
+ return "\xE8\xB1\x86";
+ break;
+ case 0x97 :
+ return "\xE8\xB1\x95";
+ break;
+ case 0x98 :
+ return "\xE8\xB1\xB8";
+ break;
+ case 0x99 :
+ return "\xE8\xB2\x9D";
+ break;
+ case 0x9A :
+ return "\xE8\xB5\xA4";
+ break;
+ case 0x9B :
+ return "\xE8\xB5\xB0";
+ break;
+ case 0x9C :
+ return "\xE8\xB6\xB3";
+ break;
+ case 0x9D :
+ return "\xE8\xBA\xAB";
+ break;
+ case 0x9E :
+ return "\xE8\xBB\x8A";
+ break;
+ case 0x9F :
+ return "\xE8\xBE\x9B";
+ break;
+ case 0xA0 :
+ return "\xE8\xBE\xB0";
+ break;
+ case 0xA1 :
+ return "\xE8\xBE\xB5";
+ break;
+ case 0xA2 :
+ return "\xE9\x82\x91";
+ break;
+ case 0xA3 :
+ return "\xE9\x85\x89";
+ break;
+ case 0xA4 :
+ return "\xE9\x87\x86";
+ break;
+ case 0xA5 :
+ return "\xE9\x87\x8C";
+ break;
+ case 0xA6 :
+ return "\xE9\x87\x91";
+ break;
+ case 0xA7 :
+ return "\xE9\x95\xB7";
+ break;
+ case 0xA8 :
+ return "\xE9\x96\x80";
+ break;
+ case 0xA9 :
+ return "\xE9\x98\x9C";
+ break;
+ case 0xAA :
+ return "\xE9\x9A\xB6";
+ break;
+ case 0xAB :
+ return "\xE9\x9A\xB9";
+ break;
+ case 0xAC :
+ return "\xE9\x9B\xA8";
+ break;
+ case 0xAD :
+ return "\xE9\x9D\x91";
+ break;
+ case 0xAE :
+ return "\xE9\x9D\x9E";
+ break;
+ case 0xAF :
+ return "\xE9\x9D\xA2";
+ break;
+ case 0xB0 :
+ return "\xE9\x9D\xA9";
+ break;
+ case 0xB1 :
+ return "\xE9\x9F\x8B";
+ break;
+ case 0xB2 :
+ return "\xE9\x9F\xAD";
+ break;
+ case 0xB3 :
+ return "\xE9\x9F\xB3";
+ break;
+ case 0xB4 :
+ return "\xE9\xA0\x81";
+ break;
+ case 0xB5 :
+ return "\xE9\xA2\xA8";
+ break;
+ case 0xB6 :
+ return "\xE9\xA3\x9B";
+ break;
+ case 0xB7 :
+ return "\xE9\xA3\x9F";
+ break;
+ case 0xB8 :
+ return "\xE9\xA6\x96";
+ break;
+ case 0xB9 :
+ return "\xE9\xA6\x99";
+ break;
+ case 0xBA :
+ return "\xE9\xA6\xAC";
+ break;
+ case 0xBB :
+ return "\xE9\xAA\xA8";
+ break;
+ case 0xBC :
+ return "\xE9\xAB\x98";
+ break;
+ case 0xBD :
+ return "\xE9\xAB\x9F";
+ break;
+ case 0xBE :
+ return "\xE9\xAC\xA5";
+ break;
+ case 0xBF :
+ return "\xE9\xAC\xAF";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE9\xAC\xB2";
+ break;
+ case 0x81 :
+ return "\xE9\xAC\xBC";
+ break;
+ case 0x82 :
+ return "\xE9\xAD\x9A";
+ break;
+ case 0x83 :
+ return "\xE9\xB3\xA5";
+ break;
+ case 0x84 :
+ return "\xE9\xB9\xB5";
+ break;
+ case 0x85 :
+ return "\xE9\xB9\xBF";
+ break;
+ case 0x86 :
+ return "\xE9\xBA\xA5";
+ break;
+ case 0x87 :
+ return "\xE9\xBA\xBB";
+ break;
+ case 0x88 :
+ return "\xE9\xBB\x83";
+ break;
+ case 0x89 :
+ return "\xE9\xBB\x8D";
+ break;
+ case 0x8A :
+ return "\xE9\xBB\x91";
+ break;
+ case 0x8B :
+ return "\xE9\xBB\xB9";
+ break;
+ case 0x8C :
+ return "\xE9\xBB\xBD";
+ break;
+ case 0x8D :
+ return "\xE9\xBC\x8E";
+ break;
+ case 0x8E :
+ return "\xE9\xBC\x93";
+ break;
+ case 0x8F :
+ return "\xE9\xBC\xA0";
+ break;
+ case 0x90 :
+ return "\xE9\xBC\xBB";
+ break;
+ case 0x91 :
+ return "\xE9\xBD\x8A";
+ break;
+ case 0x92 :
+ return "\xE9\xBD\x92";
+ break;
+ case 0x93 :
+ return "\xE9\xBE\x8D";
+ break;
+ case 0x94 :
+ return "\xE9\xBE\x9C";
+ break;
+ case 0x95 :
+ return "\xE9\xBE\xA0";
+ break;
+ }
+ break;
+ }
+ break;
+case 0xE3 :
+ switch (str[1]) {
+ case 0x80 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x20";
+ break;
+ case 0x9C :
+ return "\x7E";
+ break;
+ case 0xB6 :
+ return "\xE3\x80\x92";
+ break;
+ case 0xB8 :
+ return "\xE5\x8D\x81";
+ break;
+ case 0xB9 :
+ return "\xE5\x8D\x84";
+ break;
+ case 0xBA :
+ return "\xE5\x8D\x85";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (str[2]) {
+ case 0x9B :
+ return "\xE3\x82\x99";
+ break;
+ case 0x9C :
+ return "\xE3\x82\x9A";
+ break;
+ case 0x9F :
+ return "\xE3\x82\x88\xE3\x82\x8A";
+ break;
+ }
+ break;
+ case 0x83 :
+ if (str[2] == 0xBF) {
+ return "\xE3\x82\xB3\xE3\x83\x88";
+ }
+ break;
+ case 0x84 :
+ switch (str[2]) {
+ case 0xB1 :
+ return "\xE1\x84\x80";
+ break;
+ case 0xB2 :
+ return "\xE1\x84\x81";
+ break;
+ case 0xB3 :
+ return "\xE1\x86\xAA";
+ break;
+ case 0xB4 :
+ return "\xE1\x84\x82";
+ break;
+ case 0xB5 :
+ return "\xE1\x86\xAC";
+ break;
+ case 0xB6 :
+ return "\xE1\x86\xAD";
+ break;
+ case 0xB7 :
+ return "\xE1\x84\x83";
+ break;
+ case 0xB8 :
+ return "\xE1\x84\x84";
+ break;
+ case 0xB9 :
+ return "\xE1\x84\x85";
+ break;
+ case 0xBA :
+ return "\xE1\x86\xB0";
+ break;
+ case 0xBB :
+ return "\xE1\x86\xB1";
+ break;
+ case 0xBC :
+ return "\xE1\x86\xB2";
+ break;
+ case 0xBD :
+ return "\xE1\x86\xB3";
+ break;
+ case 0xBE :
+ return "\xE1\x86\xB4";
+ break;
+ case 0xBF :
+ return "\xE1\x86\xB5";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE1\x84\x9A";
+ break;
+ case 0x81 :
+ return "\xE1\x84\x86";
+ break;
+ case 0x82 :
+ return "\xE1\x84\x87";
+ break;
+ case 0x83 :
+ return "\xE1\x84\x88";
+ break;
+ case 0x84 :
+ return "\xE1\x84\xA1";
+ break;
+ case 0x85 :
+ return "\xE1\x84\x89";
+ break;
+ case 0x86 :
+ return "\xE1\x84\x8A";
+ break;
+ case 0x87 :
+ return "\xE1\x84\x8B";
+ break;
+ case 0x88 :
+ return "\xE1\x84\x8C";
+ break;
+ case 0x89 :
+ return "\xE1\x84\x8D";
+ break;
+ case 0x8A :
+ return "\xE1\x84\x8E";
+ break;
+ case 0x8B :
+ return "\xE1\x84\x8F";
+ break;
+ case 0x8C :
+ return "\xE1\x84\x90";
+ break;
+ case 0x8D :
+ return "\xE1\x84\x91";
+ break;
+ case 0x8E :
+ return "\xE1\x84\x92";
+ break;
+ case 0x8F :
+ return "\xE1\x85\xA1";
+ break;
+ case 0x90 :
+ return "\xE1\x85\xA2";
+ break;
+ case 0x91 :
+ return "\xE1\x85\xA3";
+ break;
+ case 0x92 :
+ return "\xE1\x85\xA4";
+ break;
+ case 0x93 :
+ return "\xE1\x85\xA5";
+ break;
+ case 0x94 :
+ return "\xE1\x85\xA6";
+ break;
+ case 0x95 :
+ return "\xE1\x85\xA7";
+ break;
+ case 0x96 :
+ return "\xE1\x85\xA8";
+ break;
+ case 0x97 :
+ return "\xE1\x85\xA9";
+ break;
+ case 0x98 :
+ return "\xE1\x85\xAA";
+ break;
+ case 0x99 :
+ return "\xE1\x85\xAB";
+ break;
+ case 0x9A :
+ return "\xE1\x85\xAC";
+ break;
+ case 0x9B :
+ return "\xE1\x85\xAD";
+ break;
+ case 0x9C :
+ return "\xE1\x85\xAE";
+ break;
+ case 0x9D :
+ return "\xE1\x85\xAF";
+ break;
+ case 0x9E :
+ return "\xE1\x85\xB0";
+ break;
+ case 0x9F :
+ return "\xE1\x85\xB1";
+ break;
+ case 0xA0 :
+ return "\xE1\x85\xB2";
+ break;
+ case 0xA1 :
+ return "\xE1\x85\xB3";
+ break;
+ case 0xA2 :
+ return "\xE1\x85\xB4";
+ break;
+ case 0xA3 :
+ return "\xE1\x85\xB5";
+ break;
+ case 0xA4 :
+ return "\xE1\x85\xA0";
+ break;
+ case 0xA5 :
+ return "\xE1\x84\x94";
+ break;
+ case 0xA6 :
+ return "\xE1\x84\x95";
+ break;
+ case 0xA7 :
+ return "\xE1\x87\x87";
+ break;
+ case 0xA8 :
+ return "\xE1\x87\x88";
+ break;
+ case 0xA9 :
+ return "\xE1\x87\x8C";
+ break;
+ case 0xAA :
+ return "\xE1\x87\x8E";
+ break;
+ case 0xAB :
+ return "\xE1\x87\x93";
+ break;
+ case 0xAC :
+ return "\xE1\x87\x97";
+ break;
+ case 0xAD :
+ return "\xE1\x87\x99";
+ break;
+ case 0xAE :
+ return "\xE1\x84\x9C";
+ break;
+ case 0xAF :
+ return "\xE1\x87\x9D";
+ break;
+ case 0xB0 :
+ return "\xE1\x87\x9F";
+ break;
+ case 0xB1 :
+ return "\xE1\x84\x9D";
+ break;
+ case 0xB2 :
+ return "\xE1\x84\x9E";
+ break;
+ case 0xB3 :
+ return "\xE1\x84\xA0";
+ break;
+ case 0xB4 :
+ return "\xE1\x84\xA2";
+ break;
+ case 0xB5 :
+ return "\xE1\x84\xA3";
+ break;
+ case 0xB6 :
+ return "\xE1\x84\xA7";
+ break;
+ case 0xB7 :
+ return "\xE1\x84\xA9";
+ break;
+ case 0xB8 :
+ return "\xE1\x84\xAB";
+ break;
+ case 0xB9 :
+ return "\xE1\x84\xAC";
+ break;
+ case 0xBA :
+ return "\xE1\x84\xAD";
+ break;
+ case 0xBB :
+ return "\xE1\x84\xAE";
+ break;
+ case 0xBC :
+ return "\xE1\x84\xAF";
+ break;
+ case 0xBD :
+ return "\xE1\x84\xB2";
+ break;
+ case 0xBE :
+ return "\xE1\x84\xB6";
+ break;
+ case 0xBF :
+ return "\xE1\x85\x80";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE1\x85\x87";
+ break;
+ case 0x81 :
+ return "\xE1\x85\x8C";
+ break;
+ case 0x82 :
+ return "\xE1\x87\xB1";
+ break;
+ case 0x83 :
+ return "\xE1\x87\xB2";
+ break;
+ case 0x84 :
+ return "\xE1\x85\x97";
+ break;
+ case 0x85 :
+ return "\xE1\x85\x98";
+ break;
+ case 0x86 :
+ return "\xE1\x85\x99";
+ break;
+ case 0x87 :
+ return "\xE1\x86\x84";
+ break;
+ case 0x88 :
+ return "\xE1\x86\x85";
+ break;
+ case 0x89 :
+ return "\xE1\x86\x88";
+ break;
+ case 0x8A :
+ return "\xE1\x86\x91";
+ break;
+ case 0x8B :
+ return "\xE1\x86\x92";
+ break;
+ case 0x8C :
+ return "\xE1\x86\x94";
+ break;
+ case 0x8D :
+ return "\xE1\x86\x9E";
+ break;
+ case 0x8E :
+ return "\xE1\x86\xA1";
+ break;
+ case 0x92 :
+ return "\xE4\xB8\x80";
+ break;
+ case 0x93 :
+ return "\xE4\xBA\x8C";
+ break;
+ case 0x94 :
+ return "\xE4\xB8\x89";
+ break;
+ case 0x95 :
+ return "\xE5\x9B\x9B";
+ break;
+ case 0x96 :
+ return "\xE4\xB8\x8A";
+ break;
+ case 0x97 :
+ return "\xE4\xB8\xAD";
+ break;
+ case 0x98 :
+ return "\xE4\xB8\x8B";
+ break;
+ case 0x99 :
+ return "\xE7\x94\xB2";
+ break;
+ case 0x9A :
+ return "\xE4\xB9\x99";
+ break;
+ case 0x9B :
+ return "\xE4\xB8\x99";
+ break;
+ case 0x9C :
+ return "\xE4\xB8\x81";
+ break;
+ case 0x9D :
+ return "\xE5\xA4\xA9";
+ break;
+ case 0x9E :
+ return "\xE5\x9C\xB0";
+ break;
+ case 0x9F :
+ return "\xE4\xBA\xBA";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x28\xE1\x84\x80\x29";
+ break;
+ case 0x81 :
+ return "\x28\xE1\x84\x82\x29";
+ break;
+ case 0x82 :
+ return "\x28\xE1\x84\x83\x29";
+ break;
+ case 0x83 :
+ return "\x28\xE1\x84\x85\x29";
+ break;
+ case 0x84 :
+ return "\x28\xE1\x84\x86\x29";
+ break;
+ case 0x85 :
+ return "\x28\xE1\x84\x87\x29";
+ break;
+ case 0x86 :
+ return "\x28\xE1\x84\x89\x29";
+ break;
+ case 0x87 :
+ return "\x28\xE1\x84\x8B\x29";
+ break;
+ case 0x88 :
+ return "\x28\xE1\x84\x8C\x29";
+ break;
+ case 0x89 :
+ return "\x28\xE1\x84\x8E\x29";
+ break;
+ case 0x8A :
+ return "\x28\xE1\x84\x8F\x29";
+ break;
+ case 0x8B :
+ return "\x28\xE1\x84\x90\x29";
+ break;
+ case 0x8C :
+ return "\x28\xE1\x84\x91\x29";
+ break;
+ case 0x8D :
+ return "\x28\xE1\x84\x92\x29";
+ break;
+ case 0x8E :
+ return "\x28\xEA\xB0\x80\x29";
+ break;
+ case 0x8F :
+ return "\x28\xEB\x82\x98\x29";
+ break;
+ case 0x90 :
+ return "\x28\xEB\x8B\xA4\x29";
+ break;
+ case 0x91 :
+ return "\x28\xEB\x9D\xBC\x29";
+ break;
+ case 0x92 :
+ return "\x28\xEB\xA7\x88\x29";
+ break;
+ case 0x93 :
+ return "\x28\xEB\xB0\x94\x29";
+ break;
+ case 0x94 :
+ return "\x28\xEC\x82\xAC\x29";
+ break;
+ case 0x95 :
+ return "\x28\xEC\x95\x84\x29";
+ break;
+ case 0x96 :
+ return "\x28\xEC\x9E\x90\x29";
+ break;
+ case 0x97 :
+ return "\x28\xEC\xB0\xA8\x29";
+ break;
+ case 0x98 :
+ return "\x28\xEC\xB9\xB4\x29";
+ break;
+ case 0x99 :
+ return "\x28\xED\x83\x80\x29";
+ break;
+ case 0x9A :
+ return "\x28\xED\x8C\x8C\x29";
+ break;
+ case 0x9B :
+ return "\x28\xED\x95\x98\x29";
+ break;
+ case 0x9C :
+ return "\x28\xEC\xA3\xBC\x29";
+ break;
+ case 0x9D :
+ return "\x28\xEC\x98\xA4\xEC\xA0\x84\x29";
+ break;
+ case 0x9E :
+ return "\x28\xEC\x98\xA4\xED\x9B\x84\x29";
+ break;
+ case 0xA0 :
+ return "\x28\xE4\xB8\x80\x29";
+ break;
+ case 0xA1 :
+ return "\x28\xE4\xBA\x8C\x29";
+ break;
+ case 0xA2 :
+ return "\x28\xE4\xB8\x89\x29";
+ break;
+ case 0xA3 :
+ return "\x28\xE5\x9B\x9B\x29";
+ break;
+ case 0xA4 :
+ return "\x28\xE4\xBA\x94\x29";
+ break;
+ case 0xA5 :
+ return "\x28\xE5\x85\xAD\x29";
+ break;
+ case 0xA6 :
+ return "\x28\xE4\xB8\x83\x29";
+ break;
+ case 0xA7 :
+ return "\x28\xE5\x85\xAB\x29";
+ break;
+ case 0xA8 :
+ return "\x28\xE4\xB9\x9D\x29";
+ break;
+ case 0xA9 :
+ return "\x28\xE5\x8D\x81\x29";
+ break;
+ case 0xAA :
+ return "\x28\xE6\x9C\x88\x29";
+ break;
+ case 0xAB :
+ return "\x28\xE7\x81\xAB\x29";
+ break;
+ case 0xAC :
+ return "\x28\xE6\xB0\xB4\x29";
+ break;
+ case 0xAD :
+ return "\x28\xE6\x9C\xA8\x29";
+ break;
+ case 0xAE :
+ return "\x28\xE9\x87\x91\x29";
+ break;
+ case 0xAF :
+ return "\x28\xE5\x9C\x9F\x29";
+ break;
+ case 0xB0 :
+ return "\x28\xE6\x97\xA5\x29";
+ break;
+ case 0xB1 :
+ return "\x28\xE6\xA0\xAA\x29";
+ break;
+ case 0xB2 :
+ return "\x28\xE6\x9C\x89\x29";
+ break;
+ case 0xB3 :
+ return "\x28\xE7\xA4\xBE\x29";
+ break;
+ case 0xB4 :
+ return "\x28\xE5\x90\x8D\x29";
+ break;
+ case 0xB5 :
+ return "\x28\xE7\x89\xB9\x29";
+ break;
+ case 0xB6 :
+ return "\x28\xE8\xB2\xA1\x29";
+ break;
+ case 0xB7 :
+ return "\x28\xE7\xA5\x9D\x29";
+ break;
+ case 0xB8 :
+ return "\x28\xE5\x8A\xB4\x29";
+ break;
+ case 0xB9 :
+ return "\x28\xE4\xBB\xA3\x29";
+ break;
+ case 0xBA :
+ return "\x28\xE5\x91\xBC\x29";
+ break;
+ case 0xBB :
+ return "\x28\xE5\xAD\xA6\x29";
+ break;
+ case 0xBC :
+ return "\x28\xE7\x9B\xA3\x29";
+ break;
+ case 0xBD :
+ return "\x28\xE4\xBC\x81\x29";
+ break;
+ case 0xBE :
+ return "\x28\xE8\xB3\x87\x29";
+ break;
+ case 0xBF :
+ return "\x28\xE5\x8D\x94\x29";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x28\xE7\xA5\xAD\x29";
+ break;
+ case 0x81 :
+ return "\x28\xE4\xBC\x91\x29";
+ break;
+ case 0x82 :
+ return "\x28\xE8\x87\xAA\x29";
+ break;
+ case 0x83 :
+ return "\x28\xE8\x87\xB3\x29";
+ break;
+ case 0x90 :
+ return "\x70\x74\x65";
+ break;
+ case 0x91 :
+ return "\x32\x31";
+ break;
+ case 0x92 :
+ return "\x32\x32";
+ break;
+ case 0x93 :
+ return "\x32\x33";
+ break;
+ case 0x94 :
+ return "\x32\x34";
+ break;
+ case 0x95 :
+ return "\x32\x35";
+ break;
+ case 0x96 :
+ return "\x32\x36";
+ break;
+ case 0x97 :
+ return "\x32\x37";
+ break;
+ case 0x98 :
+ return "\x32\x38";
+ break;
+ case 0x99 :
+ return "\x32\x39";
+ break;
+ case 0x9A :
+ return "\x33\x30";
+ break;
+ case 0x9B :
+ return "\x33\x31";
+ break;
+ case 0x9C :
+ return "\x33\x32";
+ break;
+ case 0x9D :
+ return "\x33\x33";
+ break;
+ case 0x9E :
+ return "\x33\x34";
+ break;
+ case 0x9F :
+ return "\x33\x35";
+ break;
+ case 0xA0 :
+ return "\xE1\x84\x80";
+ break;
+ case 0xA1 :
+ return "\xE1\x84\x82";
+ break;
+ case 0xA2 :
+ return "\xE1\x84\x83";
+ break;
+ case 0xA3 :
+ return "\xE1\x84\x85";
+ break;
+ case 0xA4 :
+ return "\xE1\x84\x86";
+ break;
+ case 0xA5 :
+ return "\xE1\x84\x87";
+ break;
+ case 0xA6 :
+ return "\xE1\x84\x89";
+ break;
+ case 0xA7 :
+ return "\xE1\x84\x8B";
+ break;
+ case 0xA8 :
+ return "\xE1\x84\x8C";
+ break;
+ case 0xA9 :
+ return "\xE1\x84\x8E";
+ break;
+ case 0xAA :
+ return "\xE1\x84\x8F";
+ break;
+ case 0xAB :
+ return "\xE1\x84\x90";
+ break;
+ case 0xAC :
+ return "\xE1\x84\x91";
+ break;
+ case 0xAD :
+ return "\xE1\x84\x92";
+ break;
+ case 0xAE :
+ return "\xEA\xB0\x80";
+ break;
+ case 0xAF :
+ return "\xEB\x82\x98";
+ break;
+ case 0xB0 :
+ return "\xEB\x8B\xA4";
+ break;
+ case 0xB1 :
+ return "\xEB\x9D\xBC";
+ break;
+ case 0xB2 :
+ return "\xEB\xA7\x88";
+ break;
+ case 0xB3 :
+ return "\xEB\xB0\x94";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\xAC";
+ break;
+ case 0xB5 :
+ return "\xEC\x95\x84";
+ break;
+ case 0xB6 :
+ return "\xEC\x9E\x90";
+ break;
+ case 0xB7 :
+ return "\xEC\xB0\xA8";
+ break;
+ case 0xB8 :
+ return "\xEC\xB9\xB4";
+ break;
+ case 0xB9 :
+ return "\xED\x83\x80";
+ break;
+ case 0xBA :
+ return "\xED\x8C\x8C";
+ break;
+ case 0xBB :
+ return "\xED\x95\x98";
+ break;
+ case 0xBC :
+ return "\xEC\xB0\xB8\xEA\xB3\xA0";
+ break;
+ case 0xBD :
+ return "\xEC\xA3\xBC\xEC\x9D\x98";
+ break;
+ case 0xBE :
+ return "\xEC\x9A\xB0";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE4\xB8\x80";
+ break;
+ case 0x81 :
+ return "\xE4\xBA\x8C";
+ break;
+ case 0x82 :
+ return "\xE4\xB8\x89";
+ break;
+ case 0x83 :
+ return "\xE5\x9B\x9B";
+ break;
+ case 0x84 :
+ return "\xE4\xBA\x94";
+ break;
+ case 0x85 :
+ return "\xE5\x85\xAD";
+ break;
+ case 0x86 :
+ return "\xE4\xB8\x83";
+ break;
+ case 0x87 :
+ return "\xE5\x85\xAB";
+ break;
+ case 0x88 :
+ return "\xE4\xB9\x9D";
+ break;
+ case 0x89 :
+ return "\xE5\x8D\x81";
+ break;
+ case 0x8A :
+ return "\xE6\x9C\x88";
+ break;
+ case 0x8B :
+ return "\xE7\x81\xAB";
+ break;
+ case 0x8C :
+ return "\xE6\xB0\xB4";
+ break;
+ case 0x8D :
+ return "\xE6\x9C\xA8";
+ break;
+ case 0x8E :
+ return "\xE9\x87\x91";
+ break;
+ case 0x8F :
+ return "\xE5\x9C\x9F";
+ break;
+ case 0x90 :
+ return "\xE6\x97\xA5";
+ break;
+ case 0x91 :
+ return "\xE6\xA0\xAA";
+ break;
+ case 0x92 :
+ return "\xE6\x9C\x89";
+ break;
+ case 0x93 :
+ return "\xE7\xA4\xBE";
+ break;
+ case 0x94 :
+ return "\xE5\x90\x8D";
+ break;
+ case 0x95 :
+ return "\xE7\x89\xB9";
+ break;
+ case 0x96 :
+ return "\xE8\xB2\xA1";
+ break;
+ case 0x97 :
+ return "\xE7\xA5\x9D";
+ break;
+ case 0x98 :
+ return "\xE5\x8A\xB4";
+ break;
+ case 0x99 :
+ return "\xE7\xA7\x98";
+ break;
+ case 0x9A :
+ return "\xE7\x94\xB7";
+ break;
+ case 0x9B :
+ return "\xE5\xA5\xB3";
+ break;
+ case 0x9C :
+ return "\xE9\x81\xA9";
+ break;
+ case 0x9D :
+ return "\xE5\x84\xAA";
+ break;
+ case 0x9E :
+ return "\xE5\x8D\xB0";
+ break;
+ case 0x9F :
+ return "\xE6\xB3\xA8";
+ break;
+ case 0xA0 :
+ return "\xE9\xA0\x85";
+ break;
+ case 0xA1 :
+ return "\xE4\xBC\x91";
+ break;
+ case 0xA2 :
+ return "\xE5\x86\x99";
+ break;
+ case 0xA3 :
+ return "\xE6\xAD\xA3";
+ break;
+ case 0xA4 :
+ return "\xE4\xB8\x8A";
+ break;
+ case 0xA5 :
+ return "\xE4\xB8\xAD";
+ break;
+ case 0xA6 :
+ return "\xE4\xB8\x8B";
+ break;
+ case 0xA7 :
+ return "\xE5\xB7\xA6";
+ break;
+ case 0xA8 :
+ return "\xE5\x8F\xB3";
+ break;
+ case 0xA9 :
+ return "\xE5\x8C\xBB";
+ break;
+ case 0xAA :
+ return "\xE5\xAE\x97";
+ break;
+ case 0xAB :
+ return "\xE5\xAD\xA6";
+ break;
+ case 0xAC :
+ return "\xE7\x9B\xA3";
+ break;
+ case 0xAD :
+ return "\xE4\xBC\x81";
+ break;
+ case 0xAE :
+ return "\xE8\xB3\x87";
+ break;
+ case 0xAF :
+ return "\xE5\x8D\x94";
+ break;
+ case 0xB0 :
+ return "\xE5\xA4\x9C";
+ break;
+ case 0xB1 :
+ return "\x33\x36";
+ break;
+ case 0xB2 :
+ return "\x33\x37";
+ break;
+ case 0xB3 :
+ return "\x33\x38";
+ break;
+ case 0xB4 :
+ return "\x33\x39";
+ break;
+ case 0xB5 :
+ return "\x34\x30";
+ break;
+ case 0xB6 :
+ return "\x34\x31";
+ break;
+ case 0xB7 :
+ return "\x34\x32";
+ break;
+ case 0xB8 :
+ return "\x34\x33";
+ break;
+ case 0xB9 :
+ return "\x34\x34";
+ break;
+ case 0xBA :
+ return "\x34\x35";
+ break;
+ case 0xBB :
+ return "\x34\x36";
+ break;
+ case 0xBC :
+ return "\x34\x37";
+ break;
+ case 0xBD :
+ return "\x34\x38";
+ break;
+ case 0xBE :
+ return "\x34\x39";
+ break;
+ case 0xBF :
+ return "\x35\x30";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x31\xE6\x9C\x88";
+ break;
+ case 0x81 :
+ return "\x32\xE6\x9C\x88";
+ break;
+ case 0x82 :
+ return "\x33\xE6\x9C\x88";
+ break;
+ case 0x83 :
+ return "\x34\xE6\x9C\x88";
+ break;
+ case 0x84 :
+ return "\x35\xE6\x9C\x88";
+ break;
+ case 0x85 :
+ return "\x36\xE6\x9C\x88";
+ break;
+ case 0x86 :
+ return "\x37\xE6\x9C\x88";
+ break;
+ case 0x87 :
+ return "\x38\xE6\x9C\x88";
+ break;
+ case 0x88 :
+ return "\x39\xE6\x9C\x88";
+ break;
+ case 0x89 :
+ return "\x31\x30\xE6\x9C\x88";
+ break;
+ case 0x8A :
+ return "\x31\x31\xE6\x9C\x88";
+ break;
+ case 0x8B :
+ return "\x31\x32\xE6\x9C\x88";
+ break;
+ case 0x8C :
+ return "\x68\x67";
+ break;
+ case 0x8D :
+ return "\x65\x72\x67";
+ break;
+ case 0x8E :
+ return "\x65\x76";
+ break;
+ case 0x8F :
+ return "\x6C\x74\x64";
+ break;
+ case 0x90 :
+ return "\xE3\x82\xA2";
+ break;
+ case 0x91 :
+ return "\xE3\x82\xA4";
+ break;
+ case 0x92 :
+ return "\xE3\x82\xA6";
+ break;
+ case 0x93 :
+ return "\xE3\x82\xA8";
+ break;
+ case 0x94 :
+ return "\xE3\x82\xAA";
+ break;
+ case 0x95 :
+ return "\xE3\x82\xAB";
+ break;
+ case 0x96 :
+ return "\xE3\x82\xAD";
+ break;
+ case 0x97 :
+ return "\xE3\x82\xAF";
+ break;
+ case 0x98 :
+ return "\xE3\x82\xB1";
+ break;
+ case 0x99 :
+ return "\xE3\x82\xB3";
+ break;
+ case 0x9A :
+ return "\xE3\x82\xB5";
+ break;
+ case 0x9B :
+ return "\xE3\x82\xB7";
+ break;
+ case 0x9C :
+ return "\xE3\x82\xB9";
+ break;
+ case 0x9D :
+ return "\xE3\x82\xBB";
+ break;
+ case 0x9E :
+ return "\xE3\x82\xBD";
+ break;
+ case 0x9F :
+ return "\xE3\x82\xBF";
+ break;
+ case 0xA0 :
+ return "\xE3\x83\x81";
+ break;
+ case 0xA1 :
+ return "\xE3\x83\x84";
+ break;
+ case 0xA2 :
+ return "\xE3\x83\x86";
+ break;
+ case 0xA3 :
+ return "\xE3\x83\x88";
+ break;
+ case 0xA4 :
+ return "\xE3\x83\x8A";
+ break;
+ case 0xA5 :
+ return "\xE3\x83\x8B";
+ break;
+ case 0xA6 :
+ return "\xE3\x83\x8C";
+ break;
+ case 0xA7 :
+ return "\xE3\x83\x8D";
+ break;
+ case 0xA8 :
+ return "\xE3\x83\x8E";
+ break;
+ case 0xA9 :
+ return "\xE3\x83\x8F";
+ break;
+ case 0xAA :
+ return "\xE3\x83\x92";
+ break;
+ case 0xAB :
+ return "\xE3\x83\x95";
+ break;
+ case 0xAC :
+ return "\xE3\x83\x98";
+ break;
+ case 0xAD :
+ return "\xE3\x83\x9B";
+ break;
+ case 0xAE :
+ return "\xE3\x83\x9E";
+ break;
+ case 0xAF :
+ return "\xE3\x83\x9F";
+ break;
+ case 0xB0 :
+ return "\xE3\x83\xA0";
+ break;
+ case 0xB1 :
+ return "\xE3\x83\xA1";
+ break;
+ case 0xB2 :
+ return "\xE3\x83\xA2";
+ break;
+ case 0xB3 :
+ return "\xE3\x83\xA4";
+ break;
+ case 0xB4 :
+ return "\xE3\x83\xA6";
+ break;
+ case 0xB5 :
+ return "\xE3\x83\xA8";
+ break;
+ case 0xB6 :
+ return "\xE3\x83\xA9";
+ break;
+ case 0xB7 :
+ return "\xE3\x83\xAA";
+ break;
+ case 0xB8 :
+ return "\xE3\x83\xAB";
+ break;
+ case 0xB9 :
+ return "\xE3\x83\xAC";
+ break;
+ case 0xBA :
+ return "\xE3\x83\xAD";
+ break;
+ case 0xBB :
+ return "\xE3\x83\xAF";
+ break;
+ case 0xBC :
+ return "\xE3\x83\xB0";
+ break;
+ case 0xBD :
+ return "\xE3\x83\xB1";
+ break;
+ case 0xBE :
+ return "\xE3\x83\xB2";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE3\x82\xA2\xE3\x83\x91\xE3\x83\xBC\xE3\x83\x88";
+ break;
+ case 0x81 :
+ return "\xE3\x82\xA2\xE3\x83\xAB\xE3\x83\x95\xE3\x82\xA1";
+ break;
+ case 0x82 :
+ return "\xE3\x82\xA2\xE3\x83\xB3\xE3\x83\x9A\xE3\x82\xA2";
+ break;
+ case 0x83 :
+ return "\xE3\x82\xA2\xE3\x83\xBC\xE3\x83\xAB";
+ break;
+ case 0x84 :
+ return "\xE3\x82\xA4\xE3\x83\x8B\xE3\x83\xB3\xE3\x82\xB0";
+ break;
+ case 0x85 :
+ return "\xE3\x82\xA4\xE3\x83\xB3\xE3\x83\x81";
+ break;
+ case 0x86 :
+ return "\xE3\x82\xA6\xE3\x82\xA9\xE3\x83\xB3";
+ break;
+ case 0x87 :
+ return "\xE3\x82\xA8\xE3\x82\xB9\xE3\x82\xAF\xE3\x83\xBC\xE3\x83\x89";
+ break;
+ case 0x88 :
+ return "\xE3\x82\xA8\xE3\x83\xBC\xE3\x82\xAB\xE3\x83\xBC";
+ break;
+ case 0x89 :
+ return "\xE3\x82\xAA\xE3\x83\xB3\xE3\x82\xB9";
+ break;
+ case 0x8A :
+ return "\xE3\x82\xAA\xE3\x83\xBC\xE3\x83\xA0";
+ break;
+ case 0x8B :
+ return "\xE3\x82\xAB\xE3\x82\xA4\xE3\x83\xAA";
+ break;
+ case 0x8C :
+ return "\xE3\x82\xAB\xE3\x83\xA9\xE3\x83\x83\xE3\x83\x88";
+ break;
+ case 0x8D :
+ return "\xE3\x82\xAB\xE3\x83\xAD\xE3\x83\xAA\xE3\x83\xBC";
+ break;
+ case 0x8E :
+ return "\xE3\x82\xAC\xE3\x83\xAD\xE3\x83\xB3";
+ break;
+ case 0x8F :
+ return "\xE3\x82\xAC\xE3\x83\xB3\xE3\x83\x9E";
+ break;
+ case 0x90 :
+ return "\xE3\x82\xAE\xE3\x82\xAC";
+ break;
+ case 0x91 :
+ return "\xE3\x82\xAE\xE3\x83\x8B\xE3\x83\xBC";
+ break;
+ case 0x92 :
+ return "\xE3\x82\xAD\xE3\x83\xA5\xE3\x83\xAA\xE3\x83\xBC";
+ break;
+ case 0x93 :
+ return "\xE3\x82\xAE\xE3\x83\xAB\xE3\x83\x80\xE3\x83\xBC";
+ break;
+ case 0x94 :
+ return "\xE3\x82\xAD\xE3\x83\xAD";
+ break;
+ case 0x95 :
+ return "\xE3\x82\xAD\xE3\x83\xAD\xE3\x82\xB0\xE3\x83\xA9\xE3\x83\xA0";
+ break;
+ case 0x96 :
+ return "\xE3\x82\xAD\xE3\x83\xAD\xE3\x83\xA1\xE3\x83\xBC\xE3\x83\x88\xE3\x83\xAB";
+ break;
+ case 0x97 :
+ return "\xE3\x82\xAD\xE3\x83\xAD\xE3\x83\xAF\xE3\x83\x83\xE3\x83\x88";
+ break;
+ case 0x98 :
+ return "\xE3\x82\xB0\xE3\x83\xA9\xE3\x83\xA0";
+ break;
+ case 0x99 :
+ return "\xE3\x82\xB0\xE3\x83\xA9\xE3\x83\xA0\xE3\x83\x88\xE3\x83\xB3";
+ break;
+ case 0x9A :
+ return "\xE3\x82\xAF\xE3\x83\xAB\xE3\x82\xBC\xE3\x82\xA4\xE3\x83\xAD";
+ break;
+ case 0x9B :
+ return "\xE3\x82\xAF\xE3\x83\xAD\xE3\x83\xBC\xE3\x83\x8D";
+ break;
+ case 0x9C :
+ return "\xE3\x82\xB1\xE3\x83\xBC\xE3\x82\xB9";
+ break;
+ case 0x9D :
+ return "\xE3\x82\xB3\xE3\x83\xAB\xE3\x83\x8A";
+ break;
+ case 0x9E :
+ return "\xE3\x82\xB3\xE3\x83\xBC\xE3\x83\x9D";
+ break;
+ case 0x9F :
+ return "\xE3\x82\xB5\xE3\x82\xA4\xE3\x82\xAF\xE3\x83\xAB";
+ break;
+ case 0xA0 :
+ return "\xE3\x82\xB5\xE3\x83\xB3\xE3\x83\x81\xE3\x83\xBC\xE3\x83\xA0";
+ break;
+ case 0xA1 :
+ return "\xE3\x82\xB7\xE3\x83\xAA\xE3\x83\xB3\xE3\x82\xB0";
+ break;
+ case 0xA2 :
+ return "\xE3\x82\xBB\xE3\x83\xB3\xE3\x83\x81";
+ break;
+ case 0xA3 :
+ return "\xE3\x82\xBB\xE3\x83\xB3\xE3\x83\x88";
+ break;
+ case 0xA4 :
+ return "\xE3\x83\x80\xE3\x83\xBC\xE3\x82\xB9";
+ break;
+ case 0xA5 :
+ return "\xE3\x83\x87\xE3\x82\xB7";
+ break;
+ case 0xA6 :
+ return "\xE3\x83\x89\xE3\x83\xAB";
+ break;
+ case 0xA7 :
+ return "\xE3\x83\x88\xE3\x83\xB3";
+ break;
+ case 0xA8 :
+ return "\xE3\x83\x8A\xE3\x83\x8E";
+ break;
+ case 0xA9 :
+ return "\xE3\x83\x8E\xE3\x83\x83\xE3\x83\x88";
+ break;
+ case 0xAA :
+ return "\xE3\x83\x8F\xE3\x82\xA4\xE3\x83\x84";
+ break;
+ case 0xAB :
+ return "\xE3\x83\x91\xE3\x83\xBC\xE3\x82\xBB\xE3\x83\xB3\xE3\x83\x88";
+ break;
+ case 0xAC :
+ return "\xE3\x83\x91\xE3\x83\xBC\xE3\x83\x84";
+ break;
+ case 0xAD :
+ return "\xE3\x83\x90\xE3\x83\xBC\xE3\x83\xAC\xE3\x83\xAB";
+ break;
+ case 0xAE :
+ return "\xE3\x83\x94\xE3\x82\xA2\xE3\x82\xB9\xE3\x83\x88\xE3\x83\xAB";
+ break;
+ case 0xAF :
+ return "\xE3\x83\x94\xE3\x82\xAF\xE3\x83\xAB";
+ break;
+ case 0xB0 :
+ return "\xE3\x83\x94\xE3\x82\xB3";
+ break;
+ case 0xB1 :
+ return "\xE3\x83\x93\xE3\x83\xAB";
+ break;
+ case 0xB2 :
+ return "\xE3\x83\x95\xE3\x82\xA1\xE3\x83\xA9\xE3\x83\x83\xE3\x83\x89";
+ break;
+ case 0xB3 :
+ return "\xE3\x83\x95\xE3\x82\xA3\xE3\x83\xBC\xE3\x83\x88";
+ break;
+ case 0xB4 :
+ return "\xE3\x83\x96\xE3\x83\x83\xE3\x82\xB7\xE3\x82\xA7\xE3\x83\xAB";
+ break;
+ case 0xB5 :
+ return "\xE3\x83\x95\xE3\x83\xA9\xE3\x83\xB3";
+ break;
+ case 0xB6 :
+ return "\xE3\x83\x98\xE3\x82\xAF\xE3\x82\xBF\xE3\x83\xBC\xE3\x83\xAB";
+ break;
+ case 0xB7 :
+ return "\xE3\x83\x9A\xE3\x82\xBD";
+ break;
+ case 0xB8 :
+ return "\xE3\x83\x9A\xE3\x83\x8B\xE3\x83\x92";
+ break;
+ case 0xB9 :
+ return "\xE3\x83\x98\xE3\x83\xAB\xE3\x83\x84";
+ break;
+ case 0xBA :
+ return "\xE3\x83\x9A\xE3\x83\xB3\xE3\x82\xB9";
+ break;
+ case 0xBB :
+ return "\xE3\x83\x9A\xE3\x83\xBC\xE3\x82\xB8";
+ break;
+ case 0xBC :
+ return "\xE3\x83\x99\xE3\x83\xBC\xE3\x82\xBF";
+ break;
+ case 0xBD :
+ return "\xE3\x83\x9D\xE3\x82\xA4\xE3\x83\xB3\xE3\x83\x88";
+ break;
+ case 0xBE :
+ return "\xE3\x83\x9C\xE3\x83\xAB\xE3\x83\x88";
+ break;
+ case 0xBF :
+ return "\xE3\x83\x9B\xE3\x83\xB3";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE3\x83\x9D\xE3\x83\xB3\xE3\x83\x89";
+ break;
+ case 0x81 :
+ return "\xE3\x83\x9B\xE3\x83\xBC\xE3\x83\xAB";
+ break;
+ case 0x82 :
+ return "\xE3\x83\x9B\xE3\x83\xBC\xE3\x83\xB3";
+ break;
+ case 0x83 :
+ return "\xE3\x83\x9E\xE3\x82\xA4\xE3\x82\xAF\xE3\x83\xAD";
+ break;
+ case 0x84 :
+ return "\xE3\x83\x9E\xE3\x82\xA4\xE3\x83\xAB";
+ break;
+ case 0x85 :
+ return "\xE3\x83\x9E\xE3\x83\x83\xE3\x83\x8F";
+ break;
+ case 0x86 :
+ return "\xE3\x83\x9E\xE3\x83\xAB\xE3\x82\xAF";
+ break;
+ case 0x87 :
+ return "\xE3\x83\x9E\xE3\x83\xB3\xE3\x82\xB7\xE3\x83\xA7\xE3\x83\xB3";
+ break;
+ case 0x88 :
+ return "\xE3\x83\x9F\xE3\x82\xAF\xE3\x83\xAD\xE3\x83\xB3";
+ break;
+ case 0x89 :
+ return "\xE3\x83\x9F\xE3\x83\xAA";
+ break;
+ case 0x8A :
+ return "\xE3\x83\x9F\xE3\x83\xAA\xE3\x83\x90\xE3\x83\xBC\xE3\x83\xAB";
+ break;
+ case 0x8B :
+ return "\xE3\x83\xA1\xE3\x82\xAC";
+ break;
+ case 0x8C :
+ return "\xE3\x83\xA1\xE3\x82\xAC\xE3\x83\x88\xE3\x83\xB3";
+ break;
+ case 0x8D :
+ return "\xE3\x83\xA1\xE3\x83\xBC\xE3\x83\x88\xE3\x83\xAB";
+ break;
+ case 0x8E :
+ return "\xE3\x83\xA4\xE3\x83\xBC\xE3\x83\x89";
+ break;
+ case 0x8F :
+ return "\xE3\x83\xA4\xE3\x83\xBC\xE3\x83\xAB";
+ break;
+ case 0x90 :
+ return "\xE3\x83\xA6\xE3\x82\xA2\xE3\x83\xB3";
+ break;
+ case 0x91 :
+ return "\xE3\x83\xAA\xE3\x83\x83\xE3\x83\x88\xE3\x83\xAB";
+ break;
+ case 0x92 :
+ return "\xE3\x83\xAA\xE3\x83\xA9";
+ break;
+ case 0x93 :
+ return "\xE3\x83\xAB\xE3\x83\x94\xE3\x83\xBC";
+ break;
+ case 0x94 :
+ return "\xE3\x83\xAB\xE3\x83\xBC\xE3\x83\x96\xE3\x83\xAB";
+ break;
+ case 0x95 :
+ return "\xE3\x83\xAC\xE3\x83\xA0";
+ break;
+ case 0x96 :
+ return "\xE3\x83\xAC\xE3\x83\xB3\xE3\x83\x88\xE3\x82\xB2\xE3\x83\xB3";
+ break;
+ case 0x97 :
+ return "\xE3\x83\xAF\xE3\x83\x83\xE3\x83\x88";
+ break;
+ case 0x98 :
+ return "\x30\xE7\x82\xB9";
+ break;
+ case 0x99 :
+ return "\x31\xE7\x82\xB9";
+ break;
+ case 0x9A :
+ return "\x32\xE7\x82\xB9";
+ break;
+ case 0x9B :
+ return "\x33\xE7\x82\xB9";
+ break;
+ case 0x9C :
+ return "\x34\xE7\x82\xB9";
+ break;
+ case 0x9D :
+ return "\x35\xE7\x82\xB9";
+ break;
+ case 0x9E :
+ return "\x36\xE7\x82\xB9";
+ break;
+ case 0x9F :
+ return "\x37\xE7\x82\xB9";
+ break;
+ case 0xA0 :
+ return "\x38\xE7\x82\xB9";
+ break;
+ case 0xA1 :
+ return "\x39\xE7\x82\xB9";
+ break;
+ case 0xA2 :
+ return "\x31\x30\xE7\x82\xB9";
+ break;
+ case 0xA3 :
+ return "\x31\x31\xE7\x82\xB9";
+ break;
+ case 0xA4 :
+ return "\x31\x32\xE7\x82\xB9";
+ break;
+ case 0xA5 :
+ return "\x31\x33\xE7\x82\xB9";
+ break;
+ case 0xA6 :
+ return "\x31\x34\xE7\x82\xB9";
+ break;
+ case 0xA7 :
+ return "\x31\x35\xE7\x82\xB9";
+ break;
+ case 0xA8 :
+ return "\x31\x36\xE7\x82\xB9";
+ break;
+ case 0xA9 :
+ return "\x31\x37\xE7\x82\xB9";
+ break;
+ case 0xAA :
+ return "\x31\x38\xE7\x82\xB9";
+ break;
+ case 0xAB :
+ return "\x31\x39\xE7\x82\xB9";
+ break;
+ case 0xAC :
+ return "\x32\x30\xE7\x82\xB9";
+ break;
+ case 0xAD :
+ return "\x32\x31\xE7\x82\xB9";
+ break;
+ case 0xAE :
+ return "\x32\x32\xE7\x82\xB9";
+ break;
+ case 0xAF :
+ return "\x32\x33\xE7\x82\xB9";
+ break;
+ case 0xB0 :
+ return "\x32\x34\xE7\x82\xB9";
+ break;
+ case 0xB1 :
+ return "\x68\x70\x61";
+ break;
+ case 0xB2 :
+ return "\x64\x61";
+ break;
+ case 0xB3 :
+ return "\x61\x75";
+ break;
+ case 0xB4 :
+ return "\x62\x61\x72";
+ break;
+ case 0xB5 :
+ return "\x6F\x76";
+ break;
+ case 0xB6 :
+ return "\x70\x63";
+ break;
+ case 0xB7 :
+ return "\x64\x6D";
+ break;
+ case 0xB8 :
+ return "\x64\x6D\x32";
+ break;
+ case 0xB9 :
+ return "\x64\x6D\x33";
+ break;
+ case 0xBA :
+ return "\x69\x75";
+ break;
+ case 0xBB :
+ return "\xE5\xB9\xB3\xE6\x88\x90";
+ break;
+ case 0xBC :
+ return "\xE6\x98\xAD\xE5\x92\x8C";
+ break;
+ case 0xBD :
+ return "\xE5\xA4\xA7\xE6\xAD\xA3";
+ break;
+ case 0xBE :
+ return "\xE6\x98\x8E\xE6\xB2\xBB";
+ break;
+ case 0xBF :
+ return "\xE6\xA0\xAA\xE5\xBC\x8F\xE4\xBC\x9A\xE7\xA4\xBE";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x70\x61";
+ break;
+ case 0x81 :
+ return "\x6E\x61";
+ break;
+ case 0x82 :
+ return "\xCE\xBC\x61";
+ break;
+ case 0x83 :
+ return "\x6D\x61";
+ break;
+ case 0x84 :
+ return "\x6B\x61";
+ break;
+ case 0x85 :
+ return "\x6B\x62";
+ break;
+ case 0x86 :
+ return "\x6D\x62";
+ break;
+ case 0x87 :
+ return "\x67\x62";
+ break;
+ case 0x88 :
+ return "\x63\x61\x6C";
+ break;
+ case 0x89 :
+ return "\x6B\x63\x61\x6C";
+ break;
+ case 0x8A :
+ return "\x70\x66";
+ break;
+ case 0x8B :
+ return "\x6E\x66";
+ break;
+ case 0x8C :
+ return "\xCE\xBC\x66";
+ break;
+ case 0x8D :
+ return "\xCE\xBC\x67";
+ break;
+ case 0x8E :
+ return "\x6D\x67";
+ break;
+ case 0x8F :
+ return "\x6B\x67";
+ break;
+ case 0x90 :
+ return "\x68\x7A";
+ break;
+ case 0x91 :
+ return "\x6B\x68\x7A";
+ break;
+ case 0x92 :
+ return "\x6D\x68\x7A";
+ break;
+ case 0x93 :
+ return "\x67\x68\x7A";
+ break;
+ case 0x94 :
+ return "\x74\x68\x7A";
+ break;
+ case 0x95 :
+ return "\xCE\xBC\x6C";
+ break;
+ case 0x96 :
+ return "\x6D\x6C";
+ break;
+ case 0x97 :
+ return "\x64\x6C";
+ break;
+ case 0x98 :
+ return "\x6B\x6C";
+ break;
+ case 0x99 :
+ return "\x66\x6D";
+ break;
+ case 0x9A :
+ return "\x6E\x6D";
+ break;
+ case 0x9B :
+ return "\xCE\xBC\x6D";
+ break;
+ case 0x9C :
+ return "\x6D\x6D";
+ break;
+ case 0x9D :
+ return "\x63\x6D";
+ break;
+ case 0x9E :
+ return "\x6B\x6D";
+ break;
+ case 0x9F :
+ return "\x6D\x6D\x32";
+ break;
+ case 0xA0 :
+ return "\x63\x6D\x32";
+ break;
+ case 0xA1 :
+ return "\x6D\x32";
+ break;
+ case 0xA2 :
+ return "\x6B\x6D\x32";
+ break;
+ case 0xA3 :
+ return "\x6D\x6D\x33";
+ break;
+ case 0xA4 :
+ return "\x63\x6D\x33";
+ break;
+ case 0xA5 :
+ return "\x6D\x33";
+ break;
+ case 0xA6 :
+ return "\x6B\x6D\x33";
+ break;
+ case 0xA7 :
+ return "\x6D\xE2\x88\x95\x73";
+ break;
+ case 0xA8 :
+ return "\x6D\xE2\x88\x95\x73\x32";
+ break;
+ case 0xA9 :
+ return "\x70\x61";
+ break;
+ case 0xAA :
+ return "\x6B\x70\x61";
+ break;
+ case 0xAB :
+ return "\x6D\x70\x61";
+ break;
+ case 0xAC :
+ return "\x67\x70\x61";
+ break;
+ case 0xAD :
+ return "\x72\x61\x64";
+ break;
+ case 0xAE :
+ return "\x72\x61\x64\xE2\x88\x95\x73";
+ break;
+ case 0xAF :
+ return "\x72\x61\x64\xE2\x88\x95\x73\x32";
+ break;
+ case 0xB0 :
+ return "\x70\x73";
+ break;
+ case 0xB1 :
+ return "\x6E\x73";
+ break;
+ case 0xB2 :
+ return "\xCE\xBC\x73";
+ break;
+ case 0xB3 :
+ return "\x6D\x73";
+ break;
+ case 0xB4 :
+ return "\x70\x76";
+ break;
+ case 0xB5 :
+ return "\x6E\x76";
+ break;
+ case 0xB6 :
+ return "\xCE\xBC\x76";
+ break;
+ case 0xB7 :
+ return "\x6D\x76";
+ break;
+ case 0xB8 :
+ return "\x6B\x76";
+ break;
+ case 0xB9 :
+ return "\x6D\x76";
+ break;
+ case 0xBA :
+ return "\x70\x77";
+ break;
+ case 0xBB :
+ return "\x6E\x77";
+ break;
+ case 0xBC :
+ return "\xCE\xBC\x77";
+ break;
+ case 0xBD :
+ return "\x6D\x77";
+ break;
+ case 0xBE :
+ return "\x6B\x77";
+ break;
+ case 0xBF :
+ return "\x6D\x77";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x6B\xCE\xA9";
+ break;
+ case 0x81 :
+ return "\x6D\xCE\xA9";
+ break;
+ case 0x82 :
+ return "\x61\x2E\x6D\x2E";
+ break;
+ case 0x83 :
+ return "\x62\x71";
+ break;
+ case 0x84 :
+ return "\x63\x63";
+ break;
+ case 0x85 :
+ return "\x63\x64";
+ break;
+ case 0x86 :
+ return "\x63\xE2\x88\x95\x6B\x67";
+ break;
+ case 0x87 :
+ return "\x63\x6F\x2E";
+ break;
+ case 0x88 :
+ return "\x64\x62";
+ break;
+ case 0x89 :
+ return "\x67\x79";
+ break;
+ case 0x8A :
+ return "\x68\x61";
+ break;
+ case 0x8B :
+ return "\x68\x70";
+ break;
+ case 0x8C :
+ return "\x69\x6E";
+ break;
+ case 0x8D :
+ return "\x6B\x6B";
+ break;
+ case 0x8E :
+ return "\x6B\x6D";
+ break;
+ case 0x8F :
+ return "\x6B\x74";
+ break;
+ case 0x90 :
+ return "\x6C\x6D";
+ break;
+ case 0x91 :
+ return "\x6C\x6E";
+ break;
+ case 0x92 :
+ return "\x6C\x6F\x67";
+ break;
+ case 0x93 :
+ return "\x6C\x78";
+ break;
+ case 0x94 :
+ return "\x6D\x62";
+ break;
+ case 0x95 :
+ return "\x6D\x69\x6C";
+ break;
+ case 0x96 :
+ return "\x6D\x6F\x6C";
+ break;
+ case 0x97 :
+ return "\x70\x68";
+ break;
+ case 0x98 :
+ return "\x70\x2E\x6D\x2E";
+ break;
+ case 0x99 :
+ return "\x70\x70\x6D";
+ break;
+ case 0x9A :
+ return "\x70\x72";
+ break;
+ case 0x9B :
+ return "\x73\x72";
+ break;
+ case 0x9C :
+ return "\x73\x76";
+ break;
+ case 0x9D :
+ return "\x77\x62";
+ break;
+ case 0x9E :
+ return "\x76\xE2\x88\x95\x6D";
+ break;
+ case 0x9F :
+ return "\x61\xE2\x88\x95\x6D";
+ break;
+ case 0xA0 :
+ return "\x31\xE6\x97\xA5";
+ break;
+ case 0xA1 :
+ return "\x32\xE6\x97\xA5";
+ break;
+ case 0xA2 :
+ return "\x33\xE6\x97\xA5";
+ break;
+ case 0xA3 :
+ return "\x34\xE6\x97\xA5";
+ break;
+ case 0xA4 :
+ return "\x35\xE6\x97\xA5";
+ break;
+ case 0xA5 :
+ return "\x36\xE6\x97\xA5";
+ break;
+ case 0xA6 :
+ return "\x37\xE6\x97\xA5";
+ break;
+ case 0xA7 :
+ return "\x38\xE6\x97\xA5";
+ break;
+ case 0xA8 :
+ return "\x39\xE6\x97\xA5";
+ break;
+ case 0xA9 :
+ return "\x31\x30\xE6\x97\xA5";
+ break;
+ case 0xAA :
+ return "\x31\x31\xE6\x97\xA5";
+ break;
+ case 0xAB :
+ return "\x31\x32\xE6\x97\xA5";
+ break;
+ case 0xAC :
+ return "\x31\x33\xE6\x97\xA5";
+ break;
+ case 0xAD :
+ return "\x31\x34\xE6\x97\xA5";
+ break;
+ case 0xAE :
+ return "\x31\x35\xE6\x97\xA5";
+ break;
+ case 0xAF :
+ return "\x31\x36\xE6\x97\xA5";
+ break;
+ case 0xB0 :
+ return "\x31\x37\xE6\x97\xA5";
+ break;
+ case 0xB1 :
+ return "\x31\x38\xE6\x97\xA5";
+ break;
+ case 0xB2 :
+ return "\x31\x39\xE6\x97\xA5";
+ break;
+ case 0xB3 :
+ return "\x32\x30\xE6\x97\xA5";
+ break;
+ case 0xB4 :
+ return "\x32\x31\xE6\x97\xA5";
+ break;
+ case 0xB5 :
+ return "\x32\x32\xE6\x97\xA5";
+ break;
+ case 0xB6 :
+ return "\x32\x33\xE6\x97\xA5";
+ break;
+ case 0xB7 :
+ return "\x32\x34\xE6\x97\xA5";
+ break;
+ case 0xB8 :
+ return "\x32\x35\xE6\x97\xA5";
+ break;
+ case 0xB9 :
+ return "\x32\x36\xE6\x97\xA5";
+ break;
+ case 0xBA :
+ return "\x32\x37\xE6\x97\xA5";
+ break;
+ case 0xBB :
+ return "\x32\x38\xE6\x97\xA5";
+ break;
+ case 0xBC :
+ return "\x32\x39\xE6\x97\xA5";
+ break;
+ case 0xBD :
+ return "\x33\x30\xE6\x97\xA5";
+ break;
+ case 0xBE :
+ return "\x33\x31\xE6\x97\xA5";
+ break;
+ case 0xBF :
+ return "\x67\x61\x6C";
+ break;
+ }
+ break;
+ }
+ break;
+case 0xEF :
+ switch (str[1]) {
+ case 0xA4 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE8\xB1\x88";
+ break;
+ case 0x81 :
+ return "\xE6\x9B\xB4";
+ break;
+ case 0x82 :
+ return "\xE8\xBB\x8A";
+ break;
+ case 0x83 :
+ return "\xE8\xB3\x88";
+ break;
+ case 0x84 :
+ return "\xE6\xBB\x91";
+ break;
+ case 0x85 :
+ return "\xE4\xB8\xB2";
+ break;
+ case 0x86 :
+ return "\xE5\x8F\xA5";
+ break;
+ case 0x87 :
+ return "\xE9\xBE\x9C";
+ break;
+ case 0x88 :
+ return "\xE9\xBE\x9C";
+ break;
+ case 0x89 :
+ return "\xE5\xA5\x91";
+ break;
+ case 0x8A :
+ return "\xE9\x87\x91";
+ break;
+ case 0x8B :
+ return "\xE5\x96\x87";
+ break;
+ case 0x8C :
+ return "\xE5\xA5\x88";
+ break;
+ case 0x8D :
+ return "\xE6\x87\xB6";
+ break;
+ case 0x8E :
+ return "\xE7\x99\xA9";
+ break;
+ case 0x8F :
+ return "\xE7\xBE\x85";
+ break;
+ case 0x90 :
+ return "\xE8\x98\xBF";
+ break;
+ case 0x91 :
+ return "\xE8\x9E\xBA";
+ break;
+ case 0x92 :
+ return "\xE8\xA3\xB8";
+ break;
+ case 0x93 :
+ return "\xE9\x82\x8F";
+ break;
+ case 0x94 :
+ return "\xE6\xA8\x82";
+ break;
+ case 0x95 :
+ return "\xE6\xB4\x9B";
+ break;
+ case 0x96 :
+ return "\xE7\x83\x99";
+ break;
+ case 0x97 :
+ return "\xE7\x8F\x9E";
+ break;
+ case 0x98 :
+ return "\xE8\x90\xBD";
+ break;
+ case 0x99 :
+ return "\xE9\x85\xAA";
+ break;
+ case 0x9A :
+ return "\xE9\xA7\xB1";
+ break;
+ case 0x9B :
+ return "\xE4\xBA\x82";
+ break;
+ case 0x9C :
+ return "\xE5\x8D\xB5";
+ break;
+ case 0x9D :
+ return "\xE6\xAC\x84";
+ break;
+ case 0x9E :
+ return "\xE7\x88\x9B";
+ break;
+ case 0x9F :
+ return "\xE8\x98\xAD";
+ break;
+ case 0xA0 :
+ return "\xE9\xB8\x9E";
+ break;
+ case 0xA1 :
+ return "\xE5\xB5\x90";
+ break;
+ case 0xA2 :
+ return "\xE6\xBF\xAB";
+ break;
+ case 0xA3 :
+ return "\xE8\x97\x8D";
+ break;
+ case 0xA4 :
+ return "\xE8\xA5\xA4";
+ break;
+ case 0xA5 :
+ return "\xE6\x8B\x89";
+ break;
+ case 0xA6 :
+ return "\xE8\x87\x98";
+ break;
+ case 0xA7 :
+ return "\xE8\xA0\x9F";
+ break;
+ case 0xA8 :
+ return "\xE5\xBB\x8A";
+ break;
+ case 0xA9 :
+ return "\xE6\x9C\x97";
+ break;
+ case 0xAA :
+ return "\xE6\xB5\xAA";
+ break;
+ case 0xAB :
+ return "\xE7\x8B\xBC";
+ break;
+ case 0xAC :
+ return "\xE9\x83\x8E";
+ break;
+ case 0xAD :
+ return "\xE4\xBE\x86";
+ break;
+ case 0xAE :
+ return "\xE5\x86\xB7";
+ break;
+ case 0xAF :
+ return "\xE5\x8B\x9E";
+ break;
+ case 0xB0 :
+ return "\xE6\x93\x84";
+ break;
+ case 0xB1 :
+ return "\xE6\xAB\x93";
+ break;
+ case 0xB2 :
+ return "\xE7\x88\x90";
+ break;
+ case 0xB3 :
+ return "\xE7\x9B\xA7";
+ break;
+ case 0xB4 :
+ return "\xE8\x80\x81";
+ break;
+ case 0xB5 :
+ return "\xE8\x98\x86";
+ break;
+ case 0xB6 :
+ return "\xE8\x99\x9C";
+ break;
+ case 0xB7 :
+ return "\xE8\xB7\xAF";
+ break;
+ case 0xB8 :
+ return "\xE9\x9C\xB2";
+ break;
+ case 0xB9 :
+ return "\xE9\xAD\xAF";
+ break;
+ case 0xBA :
+ return "\xE9\xB7\xBA";
+ break;
+ case 0xBB :
+ return "\xE7\xA2\x8C";
+ break;
+ case 0xBC :
+ return "\xE7\xA5\xBF";
+ break;
+ case 0xBD :
+ return "\xE7\xB6\xA0";
+ break;
+ case 0xBE :
+ return "\xE8\x8F\x89";
+ break;
+ case 0xBF :
+ return "\xE9\x8C\x84";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE9\xB9\xBF";
+ break;
+ case 0x81 :
+ return "\xE8\xAB\x96";
+ break;
+ case 0x82 :
+ return "\xE5\xA3\x9F";
+ break;
+ case 0x83 :
+ return "\xE5\xBC\x84";
+ break;
+ case 0x84 :
+ return "\xE7\xB1\xA0";
+ break;
+ case 0x85 :
+ return "\xE8\x81\xBE";
+ break;
+ case 0x86 :
+ return "\xE7\x89\xA2";
+ break;
+ case 0x87 :
+ return "\xE7\xA3\x8A";
+ break;
+ case 0x88 :
+ return "\xE8\xB3\x82";
+ break;
+ case 0x89 :
+ return "\xE9\x9B\xB7";
+ break;
+ case 0x8A :
+ return "\xE5\xA3\x98";
+ break;
+ case 0x8B :
+ return "\xE5\xB1\xA2";
+ break;
+ case 0x8C :
+ return "\xE6\xA8\x93";
+ break;
+ case 0x8D :
+ return "\xE6\xB7\x9A";
+ break;
+ case 0x8E :
+ return "\xE6\xBC\x8F";
+ break;
+ case 0x8F :
+ return "\xE7\xB4\xAF";
+ break;
+ case 0x90 :
+ return "\xE7\xB8\xB7";
+ break;
+ case 0x91 :
+ return "\xE9\x99\x8B";
+ break;
+ case 0x92 :
+ return "\xE5\x8B\x92";
+ break;
+ case 0x93 :
+ return "\xE8\x82\x8B";
+ break;
+ case 0x94 :
+ return "\xE5\x87\x9C";
+ break;
+ case 0x95 :
+ return "\xE5\x87\x8C";
+ break;
+ case 0x96 :
+ return "\xE7\xA8\x9C";
+ break;
+ case 0x97 :
+ return "\xE7\xB6\xBE";
+ break;
+ case 0x98 :
+ return "\xE8\x8F\xB1";
+ break;
+ case 0x99 :
+ return "\xE9\x99\xB5";
+ break;
+ case 0x9A :
+ return "\xE8\xAE\x80";
+ break;
+ case 0x9B :
+ return "\xE6\x8B\x8F";
+ break;
+ case 0x9C :
+ return "\xE6\xA8\x82";
+ break;
+ case 0x9D :
+ return "\xE8\xAB\xBE";
+ break;
+ case 0x9E :
+ return "\xE4\xB8\xB9";
+ break;
+ case 0x9F :
+ return "\xE5\xAF\xA7";
+ break;
+ case 0xA0 :
+ return "\xE6\x80\x92";
+ break;
+ case 0xA1 :
+ return "\xE7\x8E\x87";
+ break;
+ case 0xA2 :
+ return "\xE7\x95\xB0";
+ break;
+ case 0xA3 :
+ return "\xE5\x8C\x97";
+ break;
+ case 0xA4 :
+ return "\xE7\xA3\xBB";
+ break;
+ case 0xA5 :
+ return "\xE4\xBE\xBF";
+ break;
+ case 0xA6 :
+ return "\xE5\xBE\xA9";
+ break;
+ case 0xA7 :
+ return "\xE4\xB8\x8D";
+ break;
+ case 0xA8 :
+ return "\xE6\xB3\x8C";
+ break;
+ case 0xA9 :
+ return "\xE6\x95\xB8";
+ break;
+ case 0xAA :
+ return "\xE7\xB4\xA2";
+ break;
+ case 0xAB :
+ return "\xE5\x8F\x83";
+ break;
+ case 0xAC :
+ return "\xE5\xA1\x9E";
+ break;
+ case 0xAD :
+ return "\xE7\x9C\x81";
+ break;
+ case 0xAE :
+ return "\xE8\x91\x89";
+ break;
+ case 0xAF :
+ return "\xE8\xAA\xAA";
+ break;
+ case 0xB0 :
+ return "\xE6\xAE\xBA";
+ break;
+ case 0xB1 :
+ return "\xE8\xBE\xB0";
+ break;
+ case 0xB2 :
+ return "\xE6\xB2\x88";
+ break;
+ case 0xB3 :
+ return "\xE6\x8B\xBE";
+ break;
+ case 0xB4 :
+ return "\xE8\x8B\xA5";
+ break;
+ case 0xB5 :
+ return "\xE6\x8E\xA0";
+ break;
+ case 0xB6 :
+ return "\xE7\x95\xA5";
+ break;
+ case 0xB7 :
+ return "\xE4\xBA\xAE";
+ break;
+ case 0xB8 :
+ return "\xE5\x85\xA9";
+ break;
+ case 0xB9 :
+ return "\xE5\x87\x89";
+ break;
+ case 0xBA :
+ return "\xE6\xA2\x81";
+ break;
+ case 0xBB :
+ return "\xE7\xB3\xA7";
+ break;
+ case 0xBC :
+ return "\xE8\x89\xAF";
+ break;
+ case 0xBD :
+ return "\xE8\xAB\x92";
+ break;
+ case 0xBE :
+ return "\xE9\x87\x8F";
+ break;
+ case 0xBF :
+ return "\xE5\x8B\xB5";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE5\x91\x82";
+ break;
+ case 0x81 :
+ return "\xE5\xA5\xB3";
+ break;
+ case 0x82 :
+ return "\xE5\xBB\xAC";
+ break;
+ case 0x83 :
+ return "\xE6\x97\x85";
+ break;
+ case 0x84 :
+ return "\xE6\xBF\xBE";
+ break;
+ case 0x85 :
+ return "\xE7\xA4\xAA";
+ break;
+ case 0x86 :
+ return "\xE9\x96\xAD";
+ break;
+ case 0x87 :
+ return "\xE9\xA9\xAA";
+ break;
+ case 0x88 :
+ return "\xE9\xBA\x97";
+ break;
+ case 0x89 :
+ return "\xE9\xBB\x8E";
+ break;
+ case 0x8A :
+ return "\xE5\x8A\x9B";
+ break;
+ case 0x8B :
+ return "\xE6\x9B\x86";
+ break;
+ case 0x8C :
+ return "\xE6\xAD\xB7";
+ break;
+ case 0x8D :
+ return "\xE8\xBD\xA2";
+ break;
+ case 0x8E :
+ return "\xE5\xB9\xB4";
+ break;
+ case 0x8F :
+ return "\xE6\x86\x90";
+ break;
+ case 0x90 :
+ return "\xE6\x88\x80";
+ break;
+ case 0x91 :
+ return "\xE6\x92\x9A";
+ break;
+ case 0x92 :
+ return "\xE6\xBC\xA3";
+ break;
+ case 0x93 :
+ return "\xE7\x85\x89";
+ break;
+ case 0x94 :
+ return "\xE7\x92\x89";
+ break;
+ case 0x95 :
+ return "\xE7\xA7\x8A";
+ break;
+ case 0x96 :
+ return "\xE7\xB7\xB4";
+ break;
+ case 0x97 :
+ return "\xE8\x81\xAF";
+ break;
+ case 0x98 :
+ return "\xE8\xBC\xA6";
+ break;
+ case 0x99 :
+ return "\xE8\x93\xAE";
+ break;
+ case 0x9A :
+ return "\xE9\x80\xA3";
+ break;
+ case 0x9B :
+ return "\xE9\x8D\x8A";
+ break;
+ case 0x9C :
+ return "\xE5\x88\x97";
+ break;
+ case 0x9D :
+ return "\xE5\x8A\xA3";
+ break;
+ case 0x9E :
+ return "\xE5\x92\xBD";
+ break;
+ case 0x9F :
+ return "\xE7\x83\x88";
+ break;
+ case 0xA0 :
+ return "\xE8\xA3\x82";
+ break;
+ case 0xA1 :
+ return "\xE8\xAA\xAA";
+ break;
+ case 0xA2 :
+ return "\xE5\xBB\x89";
+ break;
+ case 0xA3 :
+ return "\xE5\xBF\xB5";
+ break;
+ case 0xA4 :
+ return "\xE6\x8D\xBB";
+ break;
+ case 0xA5 :
+ return "\xE6\xAE\xAE";
+ break;
+ case 0xA6 :
+ return "\xE7\xB0\xBE";
+ break;
+ case 0xA7 :
+ return "\xE7\x8D\xB5";
+ break;
+ case 0xA8 :
+ return "\xE4\xBB\xA4";
+ break;
+ case 0xA9 :
+ return "\xE5\x9B\xB9";
+ break;
+ case 0xAA :
+ return "\xE5\xAF\xA7";
+ break;
+ case 0xAB :
+ return "\xE5\xB6\xBA";
+ break;
+ case 0xAC :
+ return "\xE6\x80\x9C";
+ break;
+ case 0xAD :
+ return "\xE7\x8E\xB2";
+ break;
+ case 0xAE :
+ return "\xE7\x91\xA9";
+ break;
+ case 0xAF :
+ return "\xE7\xBE\x9A";
+ break;
+ case 0xB0 :
+ return "\xE8\x81\x86";
+ break;
+ case 0xB1 :
+ return "\xE9\x88\xB4";
+ break;
+ case 0xB2 :
+ return "\xE9\x9B\xB6";
+ break;
+ case 0xB3 :
+ return "\xE9\x9D\x88";
+ break;
+ case 0xB4 :
+ return "\xE9\xA0\x98";
+ break;
+ case 0xB5 :
+ return "\xE4\xBE\x8B";
+ break;
+ case 0xB6 :
+ return "\xE7\xA6\xAE";
+ break;
+ case 0xB7 :
+ return "\xE9\x86\xB4";
+ break;
+ case 0xB8 :
+ return "\xE9\x9A\xB8";
+ break;
+ case 0xB9 :
+ return "\xE6\x83\xA1";
+ break;
+ case 0xBA :
+ return "\xE4\xBA\x86";
+ break;
+ case 0xBB :
+ return "\xE5\x83\x9A";
+ break;
+ case 0xBC :
+ return "\xE5\xAF\xAE";
+ break;
+ case 0xBD :
+ return "\xE5\xB0\xBF";
+ break;
+ case 0xBE :
+ return "\xE6\x96\x99";
+ break;
+ case 0xBF :
+ return "\xE6\xA8\x82";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE7\x87\x8E";
+ break;
+ case 0x81 :
+ return "\xE7\x99\x82";
+ break;
+ case 0x82 :
+ return "\xE8\x93\xBC";
+ break;
+ case 0x83 :
+ return "\xE9\x81\xBC";
+ break;
+ case 0x84 :
+ return "\xE9\xBE\x8D";
+ break;
+ case 0x85 :
+ return "\xE6\x9A\x88";
+ break;
+ case 0x86 :
+ return "\xE9\x98\xAE";
+ break;
+ case 0x87 :
+ return "\xE5\x8A\x89";
+ break;
+ case 0x88 :
+ return "\xE6\x9D\xBB";
+ break;
+ case 0x89 :
+ return "\xE6\x9F\xB3";
+ break;
+ case 0x8A :
+ return "\xE6\xB5\x81";
+ break;
+ case 0x8B :
+ return "\xE6\xBA\x9C";
+ break;
+ case 0x8C :
+ return "\xE7\x90\x89";
+ break;
+ case 0x8D :
+ return "\xE7\x95\x99";
+ break;
+ case 0x8E :
+ return "\xE7\xA1\xAB";
+ break;
+ case 0x8F :
+ return "\xE7\xB4\x90";
+ break;
+ case 0x90 :
+ return "\xE9\xA1\x9E";
+ break;
+ case 0x91 :
+ return "\xE5\x85\xAD";
+ break;
+ case 0x92 :
+ return "\xE6\x88\xAE";
+ break;
+ case 0x93 :
+ return "\xE9\x99\xB8";
+ break;
+ case 0x94 :
+ return "\xE5\x80\xAB";
+ break;
+ case 0x95 :
+ return "\xE5\xB4\x99";
+ break;
+ case 0x96 :
+ return "\xE6\xB7\xAA";
+ break;
+ case 0x97 :
+ return "\xE8\xBC\xAA";
+ break;
+ case 0x98 :
+ return "\xE5\xBE\x8B";
+ break;
+ case 0x99 :
+ return "\xE6\x85\x84";
+ break;
+ case 0x9A :
+ return "\xE6\xA0\x97";
+ break;
+ case 0x9B :
+ return "\xE7\x8E\x87";
+ break;
+ case 0x9C :
+ return "\xE9\x9A\x86";
+ break;
+ case 0x9D :
+ return "\xE5\x88\xA9";
+ break;
+ case 0x9E :
+ return "\xE5\x90\x8F";
+ break;
+ case 0x9F :
+ return "\xE5\xB1\xA5";
+ break;
+ case 0xA0 :
+ return "\xE6\x98\x93";
+ break;
+ case 0xA1 :
+ return "\xE6\x9D\x8E";
+ break;
+ case 0xA2 :
+ return "\xE6\xA2\xA8";
+ break;
+ case 0xA3 :
+ return "\xE6\xB3\xA5";
+ break;
+ case 0xA4 :
+ return "\xE7\x90\x86";
+ break;
+ case 0xA5 :
+ return "\xE7\x97\xA2";
+ break;
+ case 0xA6 :
+ return "\xE7\xBD\xB9";
+ break;
+ case 0xA7 :
+ return "\xE8\xA3\x8F";
+ break;
+ case 0xA8 :
+ return "\xE8\xA3\xA1";
+ break;
+ case 0xA9 :
+ return "\xE9\x87\x8C";
+ break;
+ case 0xAA :
+ return "\xE9\x9B\xA2";
+ break;
+ case 0xAB :
+ return "\xE5\x8C\xBF";
+ break;
+ case 0xAC :
+ return "\xE6\xBA\xBA";
+ break;
+ case 0xAD :
+ return "\xE5\x90\x9D";
+ break;
+ case 0xAE :
+ return "\xE7\x87\x90";
+ break;
+ case 0xAF :
+ return "\xE7\x92\x98";
+ break;
+ case 0xB0 :
+ return "\xE8\x97\xBA";
+ break;
+ case 0xB1 :
+ return "\xE9\x9A\xA3";
+ break;
+ case 0xB2 :
+ return "\xE9\xB1\x97";
+ break;
+ case 0xB3 :
+ return "\xE9\xBA\x9F";
+ break;
+ case 0xB4 :
+ return "\xE6\x9E\x97";
+ break;
+ case 0xB5 :
+ return "\xE6\xB7\x8B";
+ break;
+ case 0xB6 :
+ return "\xE8\x87\xA8";
+ break;
+ case 0xB7 :
+ return "\xE7\xAB\x8B";
+ break;
+ case 0xB8 :
+ return "\xE7\xAC\xA0";
+ break;
+ case 0xB9 :
+ return "\xE7\xB2\x92";
+ break;
+ case 0xBA :
+ return "\xE7\x8B\x80";
+ break;
+ case 0xBB :
+ return "\xE7\x82\x99";
+ break;
+ case 0xBC :
+ return "\xE8\xAD\x98";
+ break;
+ case 0xBD :
+ return "\xE4\xBB\x80";
+ break;
+ case 0xBE :
+ return "\xE8\x8C\xB6";
+ break;
+ case 0xBF :
+ return "\xE5\x88\xBA";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE5\x88\x87";
+ break;
+ case 0x81 :
+ return "\xE5\xBA\xA6";
+ break;
+ case 0x82 :
+ return "\xE6\x8B\x93";
+ break;
+ case 0x83 :
+ return "\xE7\xB3\x96";
+ break;
+ case 0x84 :
+ return "\xE5\xAE\x85";
+ break;
+ case 0x85 :
+ return "\xE6\xB4\x9E";
+ break;
+ case 0x86 :
+ return "\xE6\x9A\xB4";
+ break;
+ case 0x87 :
+ return "\xE8\xBC\xBB";
+ break;
+ case 0x88 :
+ return "\xE8\xA1\x8C";
+ break;
+ case 0x89 :
+ return "\xE9\x99\x8D";
+ break;
+ case 0x8A :
+ return "\xE8\xA6\x8B";
+ break;
+ case 0x8B :
+ return "\xE5\xBB\x93";
+ break;
+ case 0x8C :
+ return "\xE5\x85\x80";
+ break;
+ case 0x8D :
+ return "\xE5\x97\x80";
+ break;
+ case 0x90 :
+ return "\xE5\xA1\x9A";
+ break;
+ case 0x92 :
+ return "\xE6\x99\xB4";
+ break;
+ case 0x95 :
+ return "\xE5\x87\x9E";
+ break;
+ case 0x96 :
+ return "\xE7\x8C\xAA";
+ break;
+ case 0x97 :
+ return "\xE7\x9B\x8A";
+ break;
+ case 0x98 :
+ return "\xE7\xA4\xBC";
+ break;
+ case 0x99 :
+ return "\xE7\xA5\x9E";
+ break;
+ case 0x9A :
+ return "\xE7\xA5\xA5";
+ break;
+ case 0x9B :
+ return "\xE7\xA6\x8F";
+ break;
+ case 0x9C :
+ return "\xE9\x9D\x96";
+ break;
+ case 0x9D :
+ return "\xE7\xB2\xBE";
+ break;
+ case 0x9E :
+ return "\xE7\xBE\xBD";
+ break;
+ case 0xA0 :
+ return "\xE8\x98\x92";
+ break;
+ case 0xA2 :
+ return "\xE8\xAB\xB8";
+ break;
+ case 0xA5 :
+ return "\xE9\x80\xB8";
+ break;
+ case 0xA6 :
+ return "\xE9\x83\xBD";
+ break;
+ case 0xAA :
+ return "\xE9\xA3\xAF";
+ break;
+ case 0xAB :
+ return "\xE9\xA3\xBC";
+ break;
+ case 0xAC :
+ return "\xE9\xA4\xA8";
+ break;
+ case 0xAD :
+ return "\xE9\xB6\xB4";
+ break;
+ case 0xB0 :
+ return "\xE4\xBE\xAE";
+ break;
+ case 0xB1 :
+ return "\xE5\x83\xA7";
+ break;
+ case 0xB2 :
+ return "\xE5\x85\x8D";
+ break;
+ case 0xB3 :
+ return "\xE5\x8B\x89";
+ break;
+ case 0xB4 :
+ return "\xE5\x8B\xA4";
+ break;
+ case 0xB5 :
+ return "\xE5\x8D\x91";
+ break;
+ case 0xB6 :
+ return "\xE5\x96\x9D";
+ break;
+ case 0xB7 :
+ return "\xE5\x98\x86";
+ break;
+ case 0xB8 :
+ return "\xE5\x99\xA8";
+ break;
+ case 0xB9 :
+ return "\xE5\xA1\x80";
+ break;
+ case 0xBA :
+ return "\xE5\xA2\xA8";
+ break;
+ case 0xBB :
+ return "\xE5\xB1\xA4";
+ break;
+ case 0xBC :
+ return "\xE5\xB1\xAE";
+ break;
+ case 0xBD :
+ return "\xE6\x82\x94";
+ break;
+ case 0xBE :
+ return "\xE6\x85\xA8";
+ break;
+ case 0xBF :
+ return "\xE6\x86\x8E";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE6\x87\xB2";
+ break;
+ case 0x81 :
+ return "\xE6\x95\x8F";
+ break;
+ case 0x82 :
+ return "\xE6\x97\xA2";
+ break;
+ case 0x83 :
+ return "\xE6\x9A\x91";
+ break;
+ case 0x84 :
+ return "\xE6\xA2\x85";
+ break;
+ case 0x85 :
+ return "\xE6\xB5\xB7";
+ break;
+ case 0x86 :
+ return "\xE6\xB8\x9A";
+ break;
+ case 0x87 :
+ return "\xE6\xBC\xA2";
+ break;
+ case 0x88 :
+ return "\xE7\x85\xAE";
+ break;
+ case 0x89 :
+ return "\xE7\x88\xAB";
+ break;
+ case 0x8A :
+ return "\xE7\x90\xA2";
+ break;
+ case 0x8B :
+ return "\xE7\xA2\x91";
+ break;
+ case 0x8C :
+ return "\xE7\xA4\xBE";
+ break;
+ case 0x8D :
+ return "\xE7\xA5\x89";
+ break;
+ case 0x8E :
+ return "\xE7\xA5\x88";
+ break;
+ case 0x8F :
+ return "\xE7\xA5\x90";
+ break;
+ case 0x90 :
+ return "\xE7\xA5\x96";
+ break;
+ case 0x91 :
+ return "\xE7\xA5\x9D";
+ break;
+ case 0x92 :
+ return "\xE7\xA6\x8D";
+ break;
+ case 0x93 :
+ return "\xE7\xA6\x8E";
+ break;
+ case 0x94 :
+ return "\xE7\xA9\x80";
+ break;
+ case 0x95 :
+ return "\xE7\xAA\x81";
+ break;
+ case 0x96 :
+ return "\xE7\xAF\x80";
+ break;
+ case 0x97 :
+ return "\xE7\xB7\xB4";
+ break;
+ case 0x98 :
+ return "\xE7\xB8\x89";
+ break;
+ case 0x99 :
+ return "\xE7\xB9\x81";
+ break;
+ case 0x9A :
+ return "\xE7\xBD\xB2";
+ break;
+ case 0x9B :
+ return "\xE8\x80\x85";
+ break;
+ case 0x9C :
+ return "\xE8\x87\xAD";
+ break;
+ case 0x9D :
+ return "\xE8\x89\xB9";
+ break;
+ case 0x9E :
+ return "\xE8\x89\xB9";
+ break;
+ case 0x9F :
+ return "\xE8\x91\x97";
+ break;
+ case 0xA0 :
+ return "\xE8\xA4\x90";
+ break;
+ case 0xA1 :
+ return "\xE8\xA6\x96";
+ break;
+ case 0xA2 :
+ return "\xE8\xAC\x81";
+ break;
+ case 0xA3 :
+ return "\xE8\xAC\xB9";
+ break;
+ case 0xA4 :
+ return "\xE8\xB3\x93";
+ break;
+ case 0xA5 :
+ return "\xE8\xB4\x88";
+ break;
+ case 0xA6 :
+ return "\xE8\xBE\xB6";
+ break;
+ case 0xA7 :
+ return "\xE9\x80\xB8";
+ break;
+ case 0xA8 :
+ return "\xE9\x9B\xA3";
+ break;
+ case 0xA9 :
+ return "\xE9\x9F\xBF";
+ break;
+ case 0xAA :
+ return "\xE9\xA0\xBB";
+ break;
+ case 0xB0 :
+ return "\xE4\xB8\xA6";
+ break;
+ case 0xB1 :
+ return "\xE5\x86\xB5";
+ break;
+ case 0xB2 :
+ return "\xE5\x85\xA8";
+ break;
+ case 0xB3 :
+ return "\xE4\xBE\x80";
+ break;
+ case 0xB4 :
+ return "\xE5\x85\x85";
+ break;
+ case 0xB5 :
+ return "\xE5\x86\x80";
+ break;
+ case 0xB6 :
+ return "\xE5\x8B\x87";
+ break;
+ case 0xB7 :
+ return "\xE5\x8B\xBA";
+ break;
+ case 0xB8 :
+ return "\xE5\x96\x9D";
+ break;
+ case 0xB9 :
+ return "\xE5\x95\x95";
+ break;
+ case 0xBA :
+ return "\xE5\x96\x99";
+ break;
+ case 0xBB :
+ return "\xE5\x97\xA2";
+ break;
+ case 0xBC :
+ return "\xE5\xA1\x9A";
+ break;
+ case 0xBD :
+ return "\xE5\xA2\xB3";
+ break;
+ case 0xBE :
+ return "\xE5\xA5\x84";
+ break;
+ case 0xBF :
+ return "\xE5\xA5\x94";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE5\xA9\xA2";
+ break;
+ case 0x81 :
+ return "\xE5\xAC\xA8";
+ break;
+ case 0x82 :
+ return "\xE5\xBB\x92";
+ break;
+ case 0x83 :
+ return "\xE5\xBB\x99";
+ break;
+ case 0x84 :
+ return "\xE5\xBD\xA9";
+ break;
+ case 0x85 :
+ return "\xE5\xBE\xAD";
+ break;
+ case 0x86 :
+ return "\xE6\x83\x98";
+ break;
+ case 0x87 :
+ return "\xE6\x85\x8E";
+ break;
+ case 0x88 :
+ return "\xE6\x84\x88";
+ break;
+ case 0x89 :
+ return "\xE6\x86\x8E";
+ break;
+ case 0x8A :
+ return "\xE6\x85\xA0";
+ break;
+ case 0x8B :
+ return "\xE6\x87\xB2";
+ break;
+ case 0x8C :
+ return "\xE6\x88\xB4";
+ break;
+ case 0x8D :
+ return "\xE6\x8F\x84";
+ break;
+ case 0x8E :
+ return "\xE6\x90\x9C";
+ break;
+ case 0x8F :
+ return "\xE6\x91\x92";
+ break;
+ case 0x90 :
+ return "\xE6\x95\x96";
+ break;
+ case 0x91 :
+ return "\xE6\x99\xB4";
+ break;
+ case 0x92 :
+ return "\xE6\x9C\x97";
+ break;
+ case 0x93 :
+ return "\xE6\x9C\x9B";
+ break;
+ case 0x94 :
+ return "\xE6\x9D\x96";
+ break;
+ case 0x95 :
+ return "\xE6\xAD\xB9";
+ break;
+ case 0x96 :
+ return "\xE6\xAE\xBA";
+ break;
+ case 0x97 :
+ return "\xE6\xB5\x81";
+ break;
+ case 0x98 :
+ return "\xE6\xBB\x9B";
+ break;
+ case 0x99 :
+ return "\xE6\xBB\x8B";
+ break;
+ case 0x9A :
+ return "\xE6\xBC\xA2";
+ break;
+ case 0x9B :
+ return "\xE7\x80\x9E";
+ break;
+ case 0x9C :
+ return "\xE7\x85\xAE";
+ break;
+ case 0x9D :
+ return "\xE7\x9E\xA7";
+ break;
+ case 0x9E :
+ return "\xE7\x88\xB5";
+ break;
+ case 0x9F :
+ return "\xE7\x8A\xAF";
+ break;
+ case 0xA0 :
+ return "\xE7\x8C\xAA";
+ break;
+ case 0xA1 :
+ return "\xE7\x91\xB1";
+ break;
+ case 0xA2 :
+ return "\xE7\x94\x86";
+ break;
+ case 0xA3 :
+ return "\xE7\x94\xBB";
+ break;
+ case 0xA4 :
+ return "\xE7\x98\x9D";
+ break;
+ case 0xA5 :
+ return "\xE7\x98\x9F";
+ break;
+ case 0xA6 :
+ return "\xE7\x9B\x8A";
+ break;
+ case 0xA7 :
+ return "\xE7\x9B\x9B";
+ break;
+ case 0xA8 :
+ return "\xE7\x9B\xB4";
+ break;
+ case 0xA9 :
+ return "\xE7\x9D\x8A";
+ break;
+ case 0xAA :
+ return "\xE7\x9D\x80";
+ break;
+ case 0xAB :
+ return "\xE7\xA3\x8C";
+ break;
+ case 0xAC :
+ return "\xE7\xAA\xB1";
+ break;
+ case 0xAD :
+ return "\xE7\xAF\x80";
+ break;
+ case 0xAE :
+ return "\xE7\xB1\xBB";
+ break;
+ case 0xAF :
+ return "\xE7\xB5\x9B";
+ break;
+ case 0xB0 :
+ return "\xE7\xB7\xB4";
+ break;
+ case 0xB1 :
+ return "\xE7\xBC\xBE";
+ break;
+ case 0xB2 :
+ return "\xE8\x80\x85";
+ break;
+ case 0xB3 :
+ return "\xE8\x8D\x92";
+ break;
+ case 0xB4 :
+ return "\xE8\x8F\xAF";
+ break;
+ case 0xB5 :
+ return "\xE8\x9D\xB9";
+ break;
+ case 0xB6 :
+ return "\xE8\xA5\x81";
+ break;
+ case 0xB7 :
+ return "\xE8\xA6\x86";
+ break;
+ case 0xB8 :
+ return "\xE8\xA6\x96";
+ break;
+ case 0xB9 :
+ return "\xE8\xAA\xBF";
+ break;
+ case 0xBA :
+ return "\xE8\xAB\xB8";
+ break;
+ case 0xBB :
+ return "\xE8\xAB\x8B";
+ break;
+ case 0xBC :
+ return "\xE8\xAC\x81";
+ break;
+ case 0xBD :
+ return "\xE8\xAB\xBE";
+ break;
+ case 0xBE :
+ return "\xE8\xAB\xAD";
+ break;
+ case 0xBF :
+ return "\xE8\xAC\xB9";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE8\xAE\x8A";
+ break;
+ case 0x81 :
+ return "\xE8\xB4\x88";
+ break;
+ case 0x82 :
+ return "\xE8\xBC\xB8";
+ break;
+ case 0x83 :
+ return "\xE9\x81\xB2";
+ break;
+ case 0x84 :
+ return "\xE9\x86\x99";
+ break;
+ case 0x85 :
+ return "\xE9\x89\xB6";
+ break;
+ case 0x86 :
+ return "\xE9\x99\xBC";
+ break;
+ case 0x87 :
+ return "\xE9\x9B\xA3";
+ break;
+ case 0x88 :
+ return "\xE9\x9D\x96";
+ break;
+ case 0x89 :
+ return "\xE9\x9F\x9B";
+ break;
+ case 0x8A :
+ return "\xE9\x9F\xBF";
+ break;
+ case 0x8B :
+ return "\xE9\xA0\x8B";
+ break;
+ case 0x8C :
+ return "\xE9\xA0\xBB";
+ break;
+ case 0x8D :
+ return "\xE9\xAC\x92";
+ break;
+ case 0x8E :
+ return "\xE9\xBE\x9C";
+ break;
+ case 0x8F :
+ return "\xF0\xA2\xA1\x8A";
+ break;
+ case 0x90 :
+ return "\xF0\xA2\xA1\x84";
+ break;
+ case 0x91 :
+ return "\xF0\xA3\x8F\x95";
+ break;
+ case 0x92 :
+ return "\xE3\xAE\x9D";
+ break;
+ case 0x93 :
+ return "\xE4\x80\x98";
+ break;
+ case 0x94 :
+ return "\xE4\x80\xB9";
+ break;
+ case 0x95 :
+ return "\xF0\xA5\x89\x89";
+ break;
+ case 0x96 :
+ return "\xF0\xA5\xB3\x90";
+ break;
+ case 0x97 :
+ return "\xF0\xA7\xBB\x93";
+ break;
+ case 0x98 :
+ return "\xE9\xBD\x83";
+ break;
+ case 0x99 :
+ return "\xE9\xBE\x8E";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x66\x66";
+ break;
+ case 0x81 :
+ return "\x66\x69";
+ break;
+ case 0x82 :
+ return "\x66\x6C";
+ break;
+ case 0x83 :
+ return "\x66\x66\x69";
+ break;
+ case 0x84 :
+ return "\x66\x66\x6C";
+ break;
+ case 0x85 :
+ return "\x73\x74";
+ break;
+ case 0x86 :
+ return "\x73\x74";
+ break;
+ case 0x93 :
+ return "\xD5\xB4\xD5\xB6";
+ break;
+ case 0x94 :
+ return "\xD5\xB4\xD5\xA5";
+ break;
+ case 0x95 :
+ return "\xD5\xB4\xD5\xAB";
+ break;
+ case 0x96 :
+ return "\xD5\xBE\xD5\xB6";
+ break;
+ case 0x97 :
+ return "\xD5\xB4\xD5\xAD";
+ break;
+ case 0x9D :
+ return "\xD7\x99\xD6\xB4";
+ break;
+ case 0x9F :
+ return "\xD7\xB2\xD6\xB7";
+ break;
+ case 0xA0 :
+ return "\xD7\xA2";
+ break;
+ case 0xA1 :
+ return "\xD7\x90";
+ break;
+ case 0xA2 :
+ return "\xD7\x93";
+ break;
+ case 0xA3 :
+ return "\xD7\x94";
+ break;
+ case 0xA4 :
+ return "\xD7\x9B";
+ break;
+ case 0xA5 :
+ return "\xD7\x9C";
+ break;
+ case 0xA6 :
+ return "\xD7\x9D";
+ break;
+ case 0xA7 :
+ return "\xD7\xA8";
+ break;
+ case 0xA8 :
+ return "\xD7\xAA";
+ break;
+ case 0xA9 :
+ return "\x2B";
+ break;
+ case 0xAA :
+ return "\xD7\xA9\xD7\x81";
+ break;
+ case 0xAB :
+ return "\xD7\xA9\xD7\x82";
+ break;
+ case 0xAC :
+ return "\xD7\xA9\xD6\xBC\xD7\x81";
+ break;
+ case 0xAD :
+ return "\xD7\xA9\xD6\xBC\xD7\x82";
+ break;
+ case 0xAE :
+ return "\xD7\x90\xD6\xB7";
+ break;
+ case 0xAF :
+ return "\xD7\x90\xD6\xB8";
+ break;
+ case 0xB0 :
+ return "\xD7\x90\xD6\xBC";
+ break;
+ case 0xB1 :
+ return "\xD7\x91\xD6\xBC";
+ break;
+ case 0xB2 :
+ return "\xD7\x92\xD6\xBC";
+ break;
+ case 0xB3 :
+ return "\xD7\x93\xD6\xBC";
+ break;
+ case 0xB4 :
+ return "\xD7\x94\xD6\xBC";
+ break;
+ case 0xB5 :
+ return "\xD7\x95\xD6\xBC";
+ break;
+ case 0xB6 :
+ return "\xD7\x96\xD6\xBC";
+ break;
+ case 0xB8 :
+ return "\xD7\x98\xD6\xBC";
+ break;
+ case 0xB9 :
+ return "\xD7\x99\xD6\xBC";
+ break;
+ case 0xBA :
+ return "\xD7\x9A\xD6\xBC";
+ break;
+ case 0xBB :
+ return "\xD7\x9B\xD6\xBC";
+ break;
+ case 0xBC :
+ return "\xD7\x9C\xD6\xBC";
+ break;
+ case 0xBE :
+ return "\xD7\x9E\xD6\xBC";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xD7\xA0\xD6\xBC";
+ break;
+ case 0x81 :
+ return "\xD7\xA1\xD6\xBC";
+ break;
+ case 0x83 :
+ return "\xD7\xA3\xD6\xBC";
+ break;
+ case 0x84 :
+ return "\xD7\xA4\xD6\xBC";
+ break;
+ case 0x86 :
+ return "\xD7\xA6\xD6\xBC";
+ break;
+ case 0x87 :
+ return "\xD7\xA7\xD6\xBC";
+ break;
+ case 0x88 :
+ return "\xD7\xA8\xD6\xBC";
+ break;
+ case 0x89 :
+ return "\xD7\xA9\xD6\xBC";
+ break;
+ case 0x8A :
+ return "\xD7\xAA\xD6\xBC";
+ break;
+ case 0x8B :
+ return "\xD7\x95\xD6\xB9";
+ break;
+ case 0x8C :
+ return "\xD7\x91\xD6\xBF";
+ break;
+ case 0x8D :
+ return "\xD7\x9B\xD6\xBF";
+ break;
+ case 0x8E :
+ return "\xD7\xA4\xD6\xBF";
+ break;
+ case 0x8F :
+ return "\xD7\x90\xD7\x9C";
+ break;
+ case 0x90 :
+ return "\xD9\xB1";
+ break;
+ case 0x91 :
+ return "\xD9\xB1";
+ break;
+ case 0x92 :
+ return "\xD9\xBB";
+ break;
+ case 0x93 :
+ return "\xD9\xBB";
+ break;
+ case 0x94 :
+ return "\xD9\xBB";
+ break;
+ case 0x95 :
+ return "\xD9\xBB";
+ break;
+ case 0x96 :
+ return "\xD9\xBE";
+ break;
+ case 0x97 :
+ return "\xD9\xBE";
+ break;
+ case 0x98 :
+ return "\xD9\xBE";
+ break;
+ case 0x99 :
+ return "\xD9\xBE";
+ break;
+ case 0x9A :
+ return "\xDA\x80";
+ break;
+ case 0x9B :
+ return "\xDA\x80";
+ break;
+ case 0x9C :
+ return "\xDA\x80";
+ break;
+ case 0x9D :
+ return "\xDA\x80";
+ break;
+ case 0x9E :
+ return "\xD9\xBA";
+ break;
+ case 0x9F :
+ return "\xD9\xBA";
+ break;
+ case 0xA0 :
+ return "\xD9\xBA";
+ break;
+ case 0xA1 :
+ return "\xD9\xBA";
+ break;
+ case 0xA2 :
+ return "\xD9\xBF";
+ break;
+ case 0xA3 :
+ return "\xD9\xBF";
+ break;
+ case 0xA4 :
+ return "\xD9\xBF";
+ break;
+ case 0xA5 :
+ return "\xD9\xBF";
+ break;
+ case 0xA6 :
+ return "\xD9\xB9";
+ break;
+ case 0xA7 :
+ return "\xD9\xB9";
+ break;
+ case 0xA8 :
+ return "\xD9\xB9";
+ break;
+ case 0xA9 :
+ return "\xD9\xB9";
+ break;
+ case 0xAA :
+ return "\xDA\xA4";
+ break;
+ case 0xAB :
+ return "\xDA\xA4";
+ break;
+ case 0xAC :
+ return "\xDA\xA4";
+ break;
+ case 0xAD :
+ return "\xDA\xA4";
+ break;
+ case 0xAE :
+ return "\xDA\xA6";
+ break;
+ case 0xAF :
+ return "\xDA\xA6";
+ break;
+ case 0xB0 :
+ return "\xDA\xA6";
+ break;
+ case 0xB1 :
+ return "\xDA\xA6";
+ break;
+ case 0xB2 :
+ return "\xDA\x84";
+ break;
+ case 0xB3 :
+ return "\xDA\x84";
+ break;
+ case 0xB4 :
+ return "\xDA\x84";
+ break;
+ case 0xB5 :
+ return "\xDA\x84";
+ break;
+ case 0xB6 :
+ return "\xDA\x83";
+ break;
+ case 0xB7 :
+ return "\xDA\x83";
+ break;
+ case 0xB8 :
+ return "\xDA\x83";
+ break;
+ case 0xB9 :
+ return "\xDA\x83";
+ break;
+ case 0xBA :
+ return "\xDA\x86";
+ break;
+ case 0xBB :
+ return "\xDA\x86";
+ break;
+ case 0xBC :
+ return "\xDA\x86";
+ break;
+ case 0xBD :
+ return "\xDA\x86";
+ break;
+ case 0xBE :
+ return "\xDA\x87";
+ break;
+ case 0xBF :
+ return "\xDA\x87";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xDA\x87";
+ break;
+ case 0x81 :
+ return "\xDA\x87";
+ break;
+ case 0x82 :
+ return "\xDA\x8D";
+ break;
+ case 0x83 :
+ return "\xDA\x8D";
+ break;
+ case 0x84 :
+ return "\xDA\x8C";
+ break;
+ case 0x85 :
+ return "\xDA\x8C";
+ break;
+ case 0x86 :
+ return "\xDA\x8E";
+ break;
+ case 0x87 :
+ return "\xDA\x8E";
+ break;
+ case 0x88 :
+ return "\xDA\x88";
+ break;
+ case 0x89 :
+ return "\xDA\x88";
+ break;
+ case 0x8A :
+ return "\xDA\x98";
+ break;
+ case 0x8B :
+ return "\xDA\x98";
+ break;
+ case 0x8C :
+ return "\xDA\x91";
+ break;
+ case 0x8D :
+ return "\xDA\x91";
+ break;
+ case 0x8E :
+ return "\xDA\xA9";
+ break;
+ case 0x8F :
+ return "\xDA\xA9";
+ break;
+ case 0x90 :
+ return "\xDA\xA9";
+ break;
+ case 0x91 :
+ return "\xDA\xA9";
+ break;
+ case 0x92 :
+ return "\xDA\xAF";
+ break;
+ case 0x93 :
+ return "\xDA\xAF";
+ break;
+ case 0x94 :
+ return "\xDA\xAF";
+ break;
+ case 0x95 :
+ return "\xDA\xAF";
+ break;
+ case 0x96 :
+ return "\xDA\xB3";
+ break;
+ case 0x97 :
+ return "\xDA\xB3";
+ break;
+ case 0x98 :
+ return "\xDA\xB3";
+ break;
+ case 0x99 :
+ return "\xDA\xB3";
+ break;
+ case 0x9A :
+ return "\xDA\xB1";
+ break;
+ case 0x9B :
+ return "\xDA\xB1";
+ break;
+ case 0x9C :
+ return "\xDA\xB1";
+ break;
+ case 0x9D :
+ return "\xDA\xB1";
+ break;
+ case 0x9E :
+ return "\xDA\xBA";
+ break;
+ case 0x9F :
+ return "\xDA\xBA";
+ break;
+ case 0xA0 :
+ return "\xDA\xBB";
+ break;
+ case 0xA1 :
+ return "\xDA\xBB";
+ break;
+ case 0xA2 :
+ return "\xDA\xBB";
+ break;
+ case 0xA3 :
+ return "\xDA\xBB";
+ break;
+ case 0xA4 :
+ return "\xDB\x80";
+ break;
+ case 0xA5 :
+ return "\xDB\x80";
+ break;
+ case 0xA6 :
+ return "\xDB\x81";
+ break;
+ case 0xA7 :
+ return "\xDB\x81";
+ break;
+ case 0xA8 :
+ return "\xDB\x81";
+ break;
+ case 0xA9 :
+ return "\xDB\x81";
+ break;
+ case 0xAA :
+ return "\xDA\xBE";
+ break;
+ case 0xAB :
+ return "\xDA\xBE";
+ break;
+ case 0xAC :
+ return "\xDA\xBE";
+ break;
+ case 0xAD :
+ return "\xDA\xBE";
+ break;
+ case 0xAE :
+ return "\xDB\x92";
+ break;
+ case 0xAF :
+ return "\xDB\x92";
+ break;
+ case 0xB0 :
+ return "\xDB\x93";
+ break;
+ case 0xB1 :
+ return "\xDB\x93";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (str[2]) {
+ case 0x93 :
+ return "\xDA\xAD";
+ break;
+ case 0x94 :
+ return "\xDA\xAD";
+ break;
+ case 0x95 :
+ return "\xDA\xAD";
+ break;
+ case 0x96 :
+ return "\xDA\xAD";
+ break;
+ case 0x97 :
+ return "\xDB\x87";
+ break;
+ case 0x98 :
+ return "\xDB\x87";
+ break;
+ case 0x99 :
+ return "\xDB\x86";
+ break;
+ case 0x9A :
+ return "\xDB\x86";
+ break;
+ case 0x9B :
+ return "\xDB\x88";
+ break;
+ case 0x9C :
+ return "\xDB\x88";
+ break;
+ case 0x9D :
+ return "\xDB\x87\xD9\xB4";
+ break;
+ case 0x9E :
+ return "\xDB\x8B";
+ break;
+ case 0x9F :
+ return "\xDB\x8B";
+ break;
+ case 0xA0 :
+ return "\xDB\x85";
+ break;
+ case 0xA1 :
+ return "\xDB\x85";
+ break;
+ case 0xA2 :
+ return "\xDB\x89";
+ break;
+ case 0xA3 :
+ return "\xDB\x89";
+ break;
+ case 0xA4 :
+ return "\xDB\x90";
+ break;
+ case 0xA5 :
+ return "\xDB\x90";
+ break;
+ case 0xA6 :
+ return "\xDB\x90";
+ break;
+ case 0xA7 :
+ return "\xDB\x90";
+ break;
+ case 0xA8 :
+ return "\xD9\x89";
+ break;
+ case 0xA9 :
+ return "\xD9\x89";
+ break;
+ case 0xAA :
+ return "\xD8\xA6\xD8\xA7";
+ break;
+ case 0xAB :
+ return "\xD8\xA6\xD8\xA7";
+ break;
+ case 0xAC :
+ return "\xD8\xA6\xDB\x95";
+ break;
+ case 0xAD :
+ return "\xD8\xA6\xDB\x95";
+ break;
+ case 0xAE :
+ return "\xD8\xA6\xD9\x88";
+ break;
+ case 0xAF :
+ return "\xD8\xA6\xD9\x88";
+ break;
+ case 0xB0 :
+ return "\xD8\xA6\xDB\x87";
+ break;
+ case 0xB1 :
+ return "\xD8\xA6\xDB\x87";
+ break;
+ case 0xB2 :
+ return "\xD8\xA6\xDB\x86";
+ break;
+ case 0xB3 :
+ return "\xD8\xA6\xDB\x86";
+ break;
+ case 0xB4 :
+ return "\xD8\xA6\xDB\x88";
+ break;
+ case 0xB5 :
+ return "\xD8\xA6\xDB\x88";
+ break;
+ case 0xB6 :
+ return "\xD8\xA6\xDB\x90";
+ break;
+ case 0xB7 :
+ return "\xD8\xA6\xDB\x90";
+ break;
+ case 0xB8 :
+ return "\xD8\xA6\xDB\x90";
+ break;
+ case 0xB9 :
+ return "\xD8\xA6\xD9\x89";
+ break;
+ case 0xBA :
+ return "\xD8\xA6\xD9\x89";
+ break;
+ case 0xBB :
+ return "\xD8\xA6\xD9\x89";
+ break;
+ case 0xBC :
+ return "\xDB\x8C";
+ break;
+ case 0xBD :
+ return "\xDB\x8C";
+ break;
+ case 0xBE :
+ return "\xDB\x8C";
+ break;
+ case 0xBF :
+ return "\xDB\x8C";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xD8\xA6\xD8\xAC";
+ break;
+ case 0x81 :
+ return "\xD8\xA6\xD8\xAD";
+ break;
+ case 0x82 :
+ return "\xD8\xA6\xD9\x85";
+ break;
+ case 0x83 :
+ return "\xD8\xA6\xD9\x89";
+ break;
+ case 0x84 :
+ return "\xD8\xA6\xD9\x8A";
+ break;
+ case 0x85 :
+ return "\xD8\xA8\xD8\xAC";
+ break;
+ case 0x86 :
+ return "\xD8\xA8\xD8\xAD";
+ break;
+ case 0x87 :
+ return "\xD8\xA8\xD8\xAE";
+ break;
+ case 0x88 :
+ return "\xD8\xA8\xD9\x85";
+ break;
+ case 0x89 :
+ return "\xD8\xA8\xD9\x89";
+ break;
+ case 0x8A :
+ return "\xD8\xA8\xD9\x8A";
+ break;
+ case 0x8B :
+ return "\xD8\xAA\xD8\xAC";
+ break;
+ case 0x8C :
+ return "\xD8\xAA\xD8\xAD";
+ break;
+ case 0x8D :
+ return "\xD8\xAA\xD8\xAE";
+ break;
+ case 0x8E :
+ return "\xD8\xAA\xD9\x85";
+ break;
+ case 0x8F :
+ return "\xD8\xAA\xD9\x89";
+ break;
+ case 0x90 :
+ return "\xD8\xAA\xD9\x8A";
+ break;
+ case 0x91 :
+ return "\xD8\xAB\xD8\xAC";
+ break;
+ case 0x92 :
+ return "\xD8\xAB\xD9\x85";
+ break;
+ case 0x93 :
+ return "\xD8\xAB\xD9\x89";
+ break;
+ case 0x94 :
+ return "\xD8\xAB\xD9\x8A";
+ break;
+ case 0x95 :
+ return "\xD8\xAC\xD8\xAD";
+ break;
+ case 0x96 :
+ return "\xD8\xAC\xD9\x85";
+ break;
+ case 0x97 :
+ return "\xD8\xAD\xD8\xAC";
+ break;
+ case 0x98 :
+ return "\xD8\xAD\xD9\x85";
+ break;
+ case 0x99 :
+ return "\xD8\xAE\xD8\xAC";
+ break;
+ case 0x9A :
+ return "\xD8\xAE\xD8\xAD";
+ break;
+ case 0x9B :
+ return "\xD8\xAE\xD9\x85";
+ break;
+ case 0x9C :
+ return "\xD8\xB3\xD8\xAC";
+ break;
+ case 0x9D :
+ return "\xD8\xB3\xD8\xAD";
+ break;
+ case 0x9E :
+ return "\xD8\xB3\xD8\xAE";
+ break;
+ case 0x9F :
+ return "\xD8\xB3\xD9\x85";
+ break;
+ case 0xA0 :
+ return "\xD8\xB5\xD8\xAD";
+ break;
+ case 0xA1 :
+ return "\xD8\xB5\xD9\x85";
+ break;
+ case 0xA2 :
+ return "\xD8\xB6\xD8\xAC";
+ break;
+ case 0xA3 :
+ return "\xD8\xB6\xD8\xAD";
+ break;
+ case 0xA4 :
+ return "\xD8\xB6\xD8\xAE";
+ break;
+ case 0xA5 :
+ return "\xD8\xB6\xD9\x85";
+ break;
+ case 0xA6 :
+ return "\xD8\xB7\xD8\xAD";
+ break;
+ case 0xA7 :
+ return "\xD8\xB7\xD9\x85";
+ break;
+ case 0xA8 :
+ return "\xD8\xB8\xD9\x85";
+ break;
+ case 0xA9 :
+ return "\xD8\xB9\xD8\xAC";
+ break;
+ case 0xAA :
+ return "\xD8\xB9\xD9\x85";
+ break;
+ case 0xAB :
+ return "\xD8\xBA\xD8\xAC";
+ break;
+ case 0xAC :
+ return "\xD8\xBA\xD9\x85";
+ break;
+ case 0xAD :
+ return "\xD9\x81\xD8\xAC";
+ break;
+ case 0xAE :
+ return "\xD9\x81\xD8\xAD";
+ break;
+ case 0xAF :
+ return "\xD9\x81\xD8\xAE";
+ break;
+ case 0xB0 :
+ return "\xD9\x81\xD9\x85";
+ break;
+ case 0xB1 :
+ return "\xD9\x81\xD9\x89";
+ break;
+ case 0xB2 :
+ return "\xD9\x81\xD9\x8A";
+ break;
+ case 0xB3 :
+ return "\xD9\x82\xD8\xAD";
+ break;
+ case 0xB4 :
+ return "\xD9\x82\xD9\x85";
+ break;
+ case 0xB5 :
+ return "\xD9\x82\xD9\x89";
+ break;
+ case 0xB6 :
+ return "\xD9\x82\xD9\x8A";
+ break;
+ case 0xB7 :
+ return "\xD9\x83\xD8\xA7";
+ break;
+ case 0xB8 :
+ return "\xD9\x83\xD8\xAC";
+ break;
+ case 0xB9 :
+ return "\xD9\x83\xD8\xAD";
+ break;
+ case 0xBA :
+ return "\xD9\x83\xD8\xAE";
+ break;
+ case 0xBB :
+ return "\xD9\x83\xD9\x84";
+ break;
+ case 0xBC :
+ return "\xD9\x83\xD9\x85";
+ break;
+ case 0xBD :
+ return "\xD9\x83\xD9\x89";
+ break;
+ case 0xBE :
+ return "\xD9\x83\xD9\x8A";
+ break;
+ case 0xBF :
+ return "\xD9\x84\xD8\xAC";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xD9\x84\xD8\xAD";
+ break;
+ case 0x81 :
+ return "\xD9\x84\xD8\xAE";
+ break;
+ case 0x82 :
+ return "\xD9\x84\xD9\x85";
+ break;
+ case 0x83 :
+ return "\xD9\x84\xD9\x89";
+ break;
+ case 0x84 :
+ return "\xD9\x84\xD9\x8A";
+ break;
+ case 0x85 :
+ return "\xD9\x85\xD8\xAC";
+ break;
+ case 0x86 :
+ return "\xD9\x85\xD8\xAD";
+ break;
+ case 0x87 :
+ return "\xD9\x85\xD8\xAE";
+ break;
+ case 0x88 :
+ return "\xD9\x85\xD9\x85";
+ break;
+ case 0x89 :
+ return "\xD9\x85\xD9\x89";
+ break;
+ case 0x8A :
+ return "\xD9\x85\xD9\x8A";
+ break;
+ case 0x8B :
+ return "\xD9\x86\xD8\xAC";
+ break;
+ case 0x8C :
+ return "\xD9\x86\xD8\xAD";
+ break;
+ case 0x8D :
+ return "\xD9\x86\xD8\xAE";
+ break;
+ case 0x8E :
+ return "\xD9\x86\xD9\x85";
+ break;
+ case 0x8F :
+ return "\xD9\x86\xD9\x89";
+ break;
+ case 0x90 :
+ return "\xD9\x86\xD9\x8A";
+ break;
+ case 0x91 :
+ return "\xD9\x87\xD8\xAC";
+ break;
+ case 0x92 :
+ return "\xD9\x87\xD9\x85";
+ break;
+ case 0x93 :
+ return "\xD9\x87\xD9\x89";
+ break;
+ case 0x94 :
+ return "\xD9\x87\xD9\x8A";
+ break;
+ case 0x95 :
+ return "\xD9\x8A\xD8\xAC";
+ break;
+ case 0x96 :
+ return "\xD9\x8A\xD8\xAD";
+ break;
+ case 0x97 :
+ return "\xD9\x8A\xD8\xAE";
+ break;
+ case 0x98 :
+ return "\xD9\x8A\xD9\x85";
+ break;
+ case 0x99 :
+ return "\xD9\x8A\xD9\x89";
+ break;
+ case 0x9A :
+ return "\xD9\x8A\xD9\x8A";
+ break;
+ case 0x9B :
+ return "\xD8\xB0\xD9\xB0";
+ break;
+ case 0x9C :
+ return "\xD8\xB1\xD9\xB0";
+ break;
+ case 0x9D :
+ return "\xD9\x89\xD9\xB0";
+ break;
+ case 0x9E :
+ return "\xD9\x8C\xD9\x91";
+ break;
+ case 0x9F :
+ return "\xD9\x8D\xD9\x91";
+ break;
+ case 0xA0 :
+ return "\xD9\x8E\xD9\x91";
+ break;
+ case 0xA1 :
+ return "\xD9\x8F\xD9\x91";
+ break;
+ case 0xA2 :
+ return "\xD9\x90\xD9\x91";
+ break;
+ case 0xA3 :
+ return "\xD9\x91\xD9\xB0";
+ break;
+ case 0xA4 :
+ return "\xD8\xA6\xD8\xB1";
+ break;
+ case 0xA5 :
+ return "\xD8\xA6\xD8\xB2";
+ break;
+ case 0xA6 :
+ return "\xD8\xA6\xD9\x85";
+ break;
+ case 0xA7 :
+ return "\xD8\xA6\xD9\x86";
+ break;
+ case 0xA8 :
+ return "\xD8\xA6\xD9\x89";
+ break;
+ case 0xA9 :
+ return "\xD8\xA6\xD9\x8A";
+ break;
+ case 0xAA :
+ return "\xD8\xA8\xD8\xB1";
+ break;
+ case 0xAB :
+ return "\xD8\xA8\xD8\xB2";
+ break;
+ case 0xAC :
+ return "\xD8\xA8\xD9\x85";
+ break;
+ case 0xAD :
+ return "\xD8\xA8\xD9\x86";
+ break;
+ case 0xAE :
+ return "\xD8\xA8\xD9\x89";
+ break;
+ case 0xAF :
+ return "\xD8\xA8\xD9\x8A";
+ break;
+ case 0xB0 :
+ return "\xD8\xAA\xD8\xB1";
+ break;
+ case 0xB1 :
+ return "\xD8\xAA\xD8\xB2";
+ break;
+ case 0xB2 :
+ return "\xD8\xAA\xD9\x85";
+ break;
+ case 0xB3 :
+ return "\xD8\xAA\xD9\x86";
+ break;
+ case 0xB4 :
+ return "\xD8\xAA\xD9\x89";
+ break;
+ case 0xB5 :
+ return "\xD8\xAA\xD9\x8A";
+ break;
+ case 0xB6 :
+ return "\xD8\xAB\xD8\xB1";
+ break;
+ case 0xB7 :
+ return "\xD8\xAB\xD8\xB2";
+ break;
+ case 0xB8 :
+ return "\xD8\xAB\xD9\x85";
+ break;
+ case 0xB9 :
+ return "\xD8\xAB\xD9\x86";
+ break;
+ case 0xBA :
+ return "\xD8\xAB\xD9\x89";
+ break;
+ case 0xBB :
+ return "\xD8\xAB\xD9\x8A";
+ break;
+ case 0xBC :
+ return "\xD9\x81\xD9\x89";
+ break;
+ case 0xBD :
+ return "\xD9\x81\xD9\x8A";
+ break;
+ case 0xBE :
+ return "\xD9\x82\xD9\x89";
+ break;
+ case 0xBF :
+ return "\xD9\x82\xD9\x8A";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xD9\x83\xD8\xA7";
+ break;
+ case 0x81 :
+ return "\xD9\x83\xD9\x84";
+ break;
+ case 0x82 :
+ return "\xD9\x83\xD9\x85";
+ break;
+ case 0x83 :
+ return "\xD9\x83\xD9\x89";
+ break;
+ case 0x84 :
+ return "\xD9\x83\xD9\x8A";
+ break;
+ case 0x85 :
+ return "\xD9\x84\xD9\x85";
+ break;
+ case 0x86 :
+ return "\xD9\x84\xD9\x89";
+ break;
+ case 0x87 :
+ return "\xD9\x84\xD9\x8A";
+ break;
+ case 0x88 :
+ return "\xD9\x85\xD8\xA7";
+ break;
+ case 0x89 :
+ return "\xD9\x85\xD9\x85";
+ break;
+ case 0x8A :
+ return "\xD9\x86\xD8\xB1";
+ break;
+ case 0x8B :
+ return "\xD9\x86\xD8\xB2";
+ break;
+ case 0x8C :
+ return "\xD9\x86\xD9\x85";
+ break;
+ case 0x8D :
+ return "\xD9\x86\xD9\x86";
+ break;
+ case 0x8E :
+ return "\xD9\x86\xD9\x89";
+ break;
+ case 0x8F :
+ return "\xD9\x86\xD9\x8A";
+ break;
+ case 0x90 :
+ return "\xD9\x89\xD9\xB0";
+ break;
+ case 0x91 :
+ return "\xD9\x8A\xD8\xB1";
+ break;
+ case 0x92 :
+ return "\xD9\x8A\xD8\xB2";
+ break;
+ case 0x93 :
+ return "\xD9\x8A\xD9\x85";
+ break;
+ case 0x94 :
+ return "\xD9\x8A\xD9\x86";
+ break;
+ case 0x95 :
+ return "\xD9\x8A\xD9\x89";
+ break;
+ case 0x96 :
+ return "\xD9\x8A\xD9\x8A";
+ break;
+ case 0x97 :
+ return "\xD8\xA6\xD8\xAC";
+ break;
+ case 0x98 :
+ return "\xD8\xA6\xD8\xAD";
+ break;
+ case 0x99 :
+ return "\xD8\xA6\xD8\xAE";
+ break;
+ case 0x9A :
+ return "\xD8\xA6\xD9\x85";
+ break;
+ case 0x9B :
+ return "\xD8\xA6\xD9\x87";
+ break;
+ case 0x9C :
+ return "\xD8\xA8\xD8\xAC";
+ break;
+ case 0x9D :
+ return "\xD8\xA8\xD8\xAD";
+ break;
+ case 0x9E :
+ return "\xD8\xA8\xD8\xAE";
+ break;
+ case 0x9F :
+ return "\xD8\xA8\xD9\x85";
+ break;
+ case 0xA0 :
+ return "\xD8\xA8\xD9\x87";
+ break;
+ case 0xA1 :
+ return "\xD8\xAA\xD8\xAC";
+ break;
+ case 0xA2 :
+ return "\xD8\xAA\xD8\xAD";
+ break;
+ case 0xA3 :
+ return "\xD8\xAA\xD8\xAE";
+ break;
+ case 0xA4 :
+ return "\xD8\xAA\xD9\x85";
+ break;
+ case 0xA5 :
+ return "\xD8\xAA\xD9\x87";
+ break;
+ case 0xA6 :
+ return "\xD8\xAB\xD9\x85";
+ break;
+ case 0xA7 :
+ return "\xD8\xAC\xD8\xAD";
+ break;
+ case 0xA8 :
+ return "\xD8\xAC\xD9\x85";
+ break;
+ case 0xA9 :
+ return "\xD8\xAD\xD8\xAC";
+ break;
+ case 0xAA :
+ return "\xD8\xAD\xD9\x85";
+ break;
+ case 0xAB :
+ return "\xD8\xAE\xD8\xAC";
+ break;
+ case 0xAC :
+ return "\xD8\xAE\xD9\x85";
+ break;
+ case 0xAD :
+ return "\xD8\xB3\xD8\xAC";
+ break;
+ case 0xAE :
+ return "\xD8\xB3\xD8\xAD";
+ break;
+ case 0xAF :
+ return "\xD8\xB3\xD8\xAE";
+ break;
+ case 0xB0 :
+ return "\xD8\xB3\xD9\x85";
+ break;
+ case 0xB1 :
+ return "\xD8\xB5\xD8\xAD";
+ break;
+ case 0xB2 :
+ return "\xD8\xB5\xD8\xAE";
+ break;
+ case 0xB3 :
+ return "\xD8\xB5\xD9\x85";
+ break;
+ case 0xB4 :
+ return "\xD8\xB6\xD8\xAC";
+ break;
+ case 0xB5 :
+ return "\xD8\xB6\xD8\xAD";
+ break;
+ case 0xB6 :
+ return "\xD8\xB6\xD8\xAE";
+ break;
+ case 0xB7 :
+ return "\xD8\xB6\xD9\x85";
+ break;
+ case 0xB8 :
+ return "\xD8\xB7\xD8\xAD";
+ break;
+ case 0xB9 :
+ return "\xD8\xB8\xD9\x85";
+ break;
+ case 0xBA :
+ return "\xD8\xB9\xD8\xAC";
+ break;
+ case 0xBB :
+ return "\xD8\xB9\xD9\x85";
+ break;
+ case 0xBC :
+ return "\xD8\xBA\xD8\xAC";
+ break;
+ case 0xBD :
+ return "\xD8\xBA\xD9\x85";
+ break;
+ case 0xBE :
+ return "\xD9\x81\xD8\xAC";
+ break;
+ case 0xBF :
+ return "\xD9\x81\xD8\xAD";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xD9\x81\xD8\xAE";
+ break;
+ case 0x81 :
+ return "\xD9\x81\xD9\x85";
+ break;
+ case 0x82 :
+ return "\xD9\x82\xD8\xAD";
+ break;
+ case 0x83 :
+ return "\xD9\x82\xD9\x85";
+ break;
+ case 0x84 :
+ return "\xD9\x83\xD8\xAC";
+ break;
+ case 0x85 :
+ return "\xD9\x83\xD8\xAD";
+ break;
+ case 0x86 :
+ return "\xD9\x83\xD8\xAE";
+ break;
+ case 0x87 :
+ return "\xD9\x83\xD9\x84";
+ break;
+ case 0x88 :
+ return "\xD9\x83\xD9\x85";
+ break;
+ case 0x89 :
+ return "\xD9\x84\xD8\xAC";
+ break;
+ case 0x8A :
+ return "\xD9\x84\xD8\xAD";
+ break;
+ case 0x8B :
+ return "\xD9\x84\xD8\xAE";
+ break;
+ case 0x8C :
+ return "\xD9\x84\xD9\x85";
+ break;
+ case 0x8D :
+ return "\xD9\x84\xD9\x87";
+ break;
+ case 0x8E :
+ return "\xD9\x85\xD8\xAC";
+ break;
+ case 0x8F :
+ return "\xD9\x85\xD8\xAD";
+ break;
+ case 0x90 :
+ return "\xD9\x85\xD8\xAE";
+ break;
+ case 0x91 :
+ return "\xD9\x85\xD9\x85";
+ break;
+ case 0x92 :
+ return "\xD9\x86\xD8\xAC";
+ break;
+ case 0x93 :
+ return "\xD9\x86\xD8\xAD";
+ break;
+ case 0x94 :
+ return "\xD9\x86\xD8\xAE";
+ break;
+ case 0x95 :
+ return "\xD9\x86\xD9\x85";
+ break;
+ case 0x96 :
+ return "\xD9\x86\xD9\x87";
+ break;
+ case 0x97 :
+ return "\xD9\x87\xD8\xAC";
+ break;
+ case 0x98 :
+ return "\xD9\x87\xD9\x85";
+ break;
+ case 0x99 :
+ return "\xD9\x87\xD9\xB0";
+ break;
+ case 0x9A :
+ return "\xD9\x8A\xD8\xAC";
+ break;
+ case 0x9B :
+ return "\xD9\x8A\xD8\xAD";
+ break;
+ case 0x9C :
+ return "\xD9\x8A\xD8\xAE";
+ break;
+ case 0x9D :
+ return "\xD9\x8A\xD9\x85";
+ break;
+ case 0x9E :
+ return "\xD9\x8A\xD9\x87";
+ break;
+ case 0x9F :
+ return "\xD8\xA6\xD9\x85";
+ break;
+ case 0xA0 :
+ return "\xD8\xA6\xD9\x87";
+ break;
+ case 0xA1 :
+ return "\xD8\xA8\xD9\x85";
+ break;
+ case 0xA2 :
+ return "\xD8\xA8\xD9\x87";
+ break;
+ case 0xA3 :
+ return "\xD8\xAA\xD9\x85";
+ break;
+ case 0xA4 :
+ return "\xD8\xAA\xD9\x87";
+ break;
+ case 0xA5 :
+ return "\xD8\xAB\xD9\x85";
+ break;
+ case 0xA6 :
+ return "\xD8\xAB\xD9\x87";
+ break;
+ case 0xA7 :
+ return "\xD8\xB3\xD9\x85";
+ break;
+ case 0xA8 :
+ return "\xD8\xB3\xD9\x87";
+ break;
+ case 0xA9 :
+ return "\xD8\xB4\xD9\x85";
+ break;
+ case 0xAA :
+ return "\xD8\xB4\xD9\x87";
+ break;
+ case 0xAB :
+ return "\xD9\x83\xD9\x84";
+ break;
+ case 0xAC :
+ return "\xD9\x83\xD9\x85";
+ break;
+ case 0xAD :
+ return "\xD9\x84\xD9\x85";
+ break;
+ case 0xAE :
+ return "\xD9\x86\xD9\x85";
+ break;
+ case 0xAF :
+ return "\xD9\x86\xD9\x87";
+ break;
+ case 0xB0 :
+ return "\xD9\x8A\xD9\x85";
+ break;
+ case 0xB1 :
+ return "\xD9\x8A\xD9\x87";
+ break;
+ case 0xB2 :
+ return "\xD9\x80\xD9\x8E\xD9\x91";
+ break;
+ case 0xB3 :
+ return "\xD9\x80\xD9\x8F\xD9\x91";
+ break;
+ case 0xB4 :
+ return "\xD9\x80\xD9\x90\xD9\x91";
+ break;
+ case 0xB5 :
+ return "\xD8\xB7\xD9\x89";
+ break;
+ case 0xB6 :
+ return "\xD8\xB7\xD9\x8A";
+ break;
+ case 0xB7 :
+ return "\xD8\xB9\xD9\x89";
+ break;
+ case 0xB8 :
+ return "\xD8\xB9\xD9\x8A";
+ break;
+ case 0xB9 :
+ return "\xD8\xBA\xD9\x89";
+ break;
+ case 0xBA :
+ return "\xD8\xBA\xD9\x8A";
+ break;
+ case 0xBB :
+ return "\xD8\xB3\xD9\x89";
+ break;
+ case 0xBC :
+ return "\xD8\xB3\xD9\x8A";
+ break;
+ case 0xBD :
+ return "\xD8\xB4\xD9\x89";
+ break;
+ case 0xBE :
+ return "\xD8\xB4\xD9\x8A";
+ break;
+ case 0xBF :
+ return "\xD8\xAD\xD9\x89";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xD8\xAD\xD9\x8A";
+ break;
+ case 0x81 :
+ return "\xD8\xAC\xD9\x89";
+ break;
+ case 0x82 :
+ return "\xD8\xAC\xD9\x8A";
+ break;
+ case 0x83 :
+ return "\xD8\xAE\xD9\x89";
+ break;
+ case 0x84 :
+ return "\xD8\xAE\xD9\x8A";
+ break;
+ case 0x85 :
+ return "\xD8\xB5\xD9\x89";
+ break;
+ case 0x86 :
+ return "\xD8\xB5\xD9\x8A";
+ break;
+ case 0x87 :
+ return "\xD8\xB6\xD9\x89";
+ break;
+ case 0x88 :
+ return "\xD8\xB6\xD9\x8A";
+ break;
+ case 0x89 :
+ return "\xD8\xB4\xD8\xAC";
+ break;
+ case 0x8A :
+ return "\xD8\xB4\xD8\xAD";
+ break;
+ case 0x8B :
+ return "\xD8\xB4\xD8\xAE";
+ break;
+ case 0x8C :
+ return "\xD8\xB4\xD9\x85";
+ break;
+ case 0x8D :
+ return "\xD8\xB4\xD8\xB1";
+ break;
+ case 0x8E :
+ return "\xD8\xB3\xD8\xB1";
+ break;
+ case 0x8F :
+ return "\xD8\xB5\xD8\xB1";
+ break;
+ case 0x90 :
+ return "\xD8\xB6\xD8\xB1";
+ break;
+ case 0x91 :
+ return "\xD8\xB7\xD9\x89";
+ break;
+ case 0x92 :
+ return "\xD8\xB7\xD9\x8A";
+ break;
+ case 0x93 :
+ return "\xD8\xB9\xD9\x89";
+ break;
+ case 0x94 :
+ return "\xD8\xB9\xD9\x8A";
+ break;
+ case 0x95 :
+ return "\xD8\xBA\xD9\x89";
+ break;
+ case 0x96 :
+ return "\xD8\xBA\xD9\x8A";
+ break;
+ case 0x97 :
+ return "\xD8\xB3\xD9\x89";
+ break;
+ case 0x98 :
+ return "\xD8\xB3\xD9\x8A";
+ break;
+ case 0x99 :
+ return "\xD8\xB4\xD9\x89";
+ break;
+ case 0x9A :
+ return "\xD8\xB4\xD9\x8A";
+ break;
+ case 0x9B :
+ return "\xD8\xAD\xD9\x89";
+ break;
+ case 0x9C :
+ return "\xD8\xAD\xD9\x8A";
+ break;
+ case 0x9D :
+ return "\xD8\xAC\xD9\x89";
+ break;
+ case 0x9E :
+ return "\xD8\xAC\xD9\x8A";
+ break;
+ case 0x9F :
+ return "\xD8\xAE\xD9\x89";
+ break;
+ case 0xA0 :
+ return "\xD8\xAE\xD9\x8A";
+ break;
+ case 0xA1 :
+ return "\xD8\xB5\xD9\x89";
+ break;
+ case 0xA2 :
+ return "\xD8\xB5\xD9\x8A";
+ break;
+ case 0xA3 :
+ return "\xD8\xB6\xD9\x89";
+ break;
+ case 0xA4 :
+ return "\xD8\xB6\xD9\x8A";
+ break;
+ case 0xA5 :
+ return "\xD8\xB4\xD8\xAC";
+ break;
+ case 0xA6 :
+ return "\xD8\xB4\xD8\xAD";
+ break;
+ case 0xA7 :
+ return "\xD8\xB4\xD8\xAE";
+ break;
+ case 0xA8 :
+ return "\xD8\xB4\xD9\x85";
+ break;
+ case 0xA9 :
+ return "\xD8\xB4\xD8\xB1";
+ break;
+ case 0xAA :
+ return "\xD8\xB3\xD8\xB1";
+ break;
+ case 0xAB :
+ return "\xD8\xB5\xD8\xB1";
+ break;
+ case 0xAC :
+ return "\xD8\xB6\xD8\xB1";
+ break;
+ case 0xAD :
+ return "\xD8\xB4\xD8\xAC";
+ break;
+ case 0xAE :
+ return "\xD8\xB4\xD8\xAD";
+ break;
+ case 0xAF :
+ return "\xD8\xB4\xD8\xAE";
+ break;
+ case 0xB0 :
+ return "\xD8\xB4\xD9\x85";
+ break;
+ case 0xB1 :
+ return "\xD8\xB3\xD9\x87";
+ break;
+ case 0xB2 :
+ return "\xD8\xB4\xD9\x87";
+ break;
+ case 0xB3 :
+ return "\xD8\xB7\xD9\x85";
+ break;
+ case 0xB4 :
+ return "\xD8\xB3\xD8\xAC";
+ break;
+ case 0xB5 :
+ return "\xD8\xB3\xD8\xAD";
+ break;
+ case 0xB6 :
+ return "\xD8\xB3\xD8\xAE";
+ break;
+ case 0xB7 :
+ return "\xD8\xB4\xD8\xAC";
+ break;
+ case 0xB8 :
+ return "\xD8\xB4\xD8\xAD";
+ break;
+ case 0xB9 :
+ return "\xD8\xB4\xD8\xAE";
+ break;
+ case 0xBA :
+ return "\xD8\xB7\xD9\x85";
+ break;
+ case 0xBB :
+ return "\xD8\xB8\xD9\x85";
+ break;
+ case 0xBC :
+ return "\xD8\xA7\xD9\x8B";
+ break;
+ case 0xBD :
+ return "\xD8\xA7\xD9\x8B";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (str[2]) {
+ case 0x90 :
+ return "\xD8\xAA\xD8\xAC\xD9\x85";
+ break;
+ case 0x91 :
+ return "\xD8\xAA\xD8\xAD\xD8\xAC";
+ break;
+ case 0x92 :
+ return "\xD8\xAA\xD8\xAD\xD8\xAC";
+ break;
+ case 0x93 :
+ return "\xD8\xAA\xD8\xAD\xD9\x85";
+ break;
+ case 0x94 :
+ return "\xD8\xAA\xD8\xAE\xD9\x85";
+ break;
+ case 0x95 :
+ return "\xD8\xAA\xD9\x85\xD8\xAC";
+ break;
+ case 0x96 :
+ return "\xD8\xAA\xD9\x85\xD8\xAD";
+ break;
+ case 0x97 :
+ return "\xD8\xAA\xD9\x85\xD8\xAE";
+ break;
+ case 0x98 :
+ return "\xD8\xAC\xD9\x85\xD8\xAD";
+ break;
+ case 0x99 :
+ return "\xD8\xAC\xD9\x85\xD8\xAD";
+ break;
+ case 0x9A :
+ return "\xD8\xAD\xD9\x85\xD9\x8A";
+ break;
+ case 0x9B :
+ return "\xD8\xAD\xD9\x85\xD9\x89";
+ break;
+ case 0x9C :
+ return "\xD8\xB3\xD8\xAD\xD8\xAC";
+ break;
+ case 0x9D :
+ return "\xD8\xB3\xD8\xAC\xD8\xAD";
+ break;
+ case 0x9E :
+ return "\xD8\xB3\xD8\xAC\xD9\x89";
+ break;
+ case 0x9F :
+ return "\xD8\xB3\xD9\x85\xD8\xAD";
+ break;
+ case 0xA0 :
+ return "\xD8\xB3\xD9\x85\xD8\xAD";
+ break;
+ case 0xA1 :
+ return "\xD8\xB3\xD9\x85\xD8\xAC";
+ break;
+ case 0xA2 :
+ return "\xD8\xB3\xD9\x85\xD9\x85";
+ break;
+ case 0xA3 :
+ return "\xD8\xB3\xD9\x85\xD9\x85";
+ break;
+ case 0xA4 :
+ return "\xD8\xB5\xD8\xAD\xD8\xAD";
+ break;
+ case 0xA5 :
+ return "\xD8\xB5\xD8\xAD\xD8\xAD";
+ break;
+ case 0xA6 :
+ return "\xD8\xB5\xD9\x85\xD9\x85";
+ break;
+ case 0xA7 :
+ return "\xD8\xB4\xD8\xAD\xD9\x85";
+ break;
+ case 0xA8 :
+ return "\xD8\xB4\xD8\xAD\xD9\x85";
+ break;
+ case 0xA9 :
+ return "\xD8\xB4\xD8\xAC\xD9\x8A";
+ break;
+ case 0xAA :
+ return "\xD8\xB4\xD9\x85\xD8\xAE";
+ break;
+ case 0xAB :
+ return "\xD8\xB4\xD9\x85\xD8\xAE";
+ break;
+ case 0xAC :
+ return "\xD8\xB4\xD9\x85\xD9\x85";
+ break;
+ case 0xAD :
+ return "\xD8\xB4\xD9\x85\xD9\x85";
+ break;
+ case 0xAE :
+ return "\xD8\xB6\xD8\xAD\xD9\x89";
+ break;
+ case 0xAF :
+ return "\xD8\xB6\xD8\xAE\xD9\x85";
+ break;
+ case 0xB0 :
+ return "\xD8\xB6\xD8\xAE\xD9\x85";
+ break;
+ case 0xB1 :
+ return "\xD8\xB7\xD9\x85\xD8\xAD";
+ break;
+ case 0xB2 :
+ return "\xD8\xB7\xD9\x85\xD8\xAD";
+ break;
+ case 0xB3 :
+ return "\xD8\xB7\xD9\x85\xD9\x85";
+ break;
+ case 0xB4 :
+ return "\xD8\xB7\xD9\x85\xD9\x8A";
+ break;
+ case 0xB5 :
+ return "\xD8\xB9\xD8\xAC\xD9\x85";
+ break;
+ case 0xB6 :
+ return "\xD8\xB9\xD9\x85\xD9\x85";
+ break;
+ case 0xB7 :
+ return "\xD8\xB9\xD9\x85\xD9\x85";
+ break;
+ case 0xB8 :
+ return "\xD8\xB9\xD9\x85\xD9\x89";
+ break;
+ case 0xB9 :
+ return "\xD8\xBA\xD9\x85\xD9\x85";
+ break;
+ case 0xBA :
+ return "\xD8\xBA\xD9\x85\xD9\x8A";
+ break;
+ case 0xBB :
+ return "\xD8\xBA\xD9\x85\xD9\x89";
+ break;
+ case 0xBC :
+ return "\xD9\x81\xD8\xAE\xD9\x85";
+ break;
+ case 0xBD :
+ return "\xD9\x81\xD8\xAE\xD9\x85";
+ break;
+ case 0xBE :
+ return "\xD9\x82\xD9\x85\xD8\xAD";
+ break;
+ case 0xBF :
+ return "\xD9\x82\xD9\x85\xD9\x85";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xD9\x84\xD8\xAD\xD9\x85";
+ break;
+ case 0x81 :
+ return "\xD9\x84\xD8\xAD\xD9\x8A";
+ break;
+ case 0x82 :
+ return "\xD9\x84\xD8\xAD\xD9\x89";
+ break;
+ case 0x83 :
+ return "\xD9\x84\xD8\xAC\xD8\xAC";
+ break;
+ case 0x84 :
+ return "\xD9\x84\xD8\xAC\xD8\xAC";
+ break;
+ case 0x85 :
+ return "\xD9\x84\xD8\xAE\xD9\x85";
+ break;
+ case 0x86 :
+ return "\xD9\x84\xD8\xAE\xD9\x85";
+ break;
+ case 0x87 :
+ return "\xD9\x84\xD9\x85\xD8\xAD";
+ break;
+ case 0x88 :
+ return "\xD9\x84\xD9\x85\xD8\xAD";
+ break;
+ case 0x89 :
+ return "\xD9\x85\xD8\xAD\xD8\xAC";
+ break;
+ case 0x8A :
+ return "\xD9\x85\xD8\xAD\xD9\x85";
+ break;
+ case 0x8B :
+ return "\xD9\x85\xD8\xAD\xD9\x8A";
+ break;
+ case 0x8C :
+ return "\xD9\x85\xD8\xAC\xD8\xAD";
+ break;
+ case 0x8D :
+ return "\xD9\x85\xD8\xAC\xD9\x85";
+ break;
+ case 0x8E :
+ return "\xD9\x85\xD8\xAE\xD8\xAC";
+ break;
+ case 0x8F :
+ return "\xD9\x85\xD8\xAE\xD9\x85";
+ break;
+ case 0x92 :
+ return "\xD9\x85\xD8\xAC\xD8\xAE";
+ break;
+ case 0x93 :
+ return "\xD9\x87\xD9\x85\xD8\xAC";
+ break;
+ case 0x94 :
+ return "\xD9\x87\xD9\x85\xD9\x85";
+ break;
+ case 0x95 :
+ return "\xD9\x86\xD8\xAD\xD9\x85";
+ break;
+ case 0x96 :
+ return "\xD9\x86\xD8\xAD\xD9\x89";
+ break;
+ case 0x97 :
+ return "\xD9\x86\xD8\xAC\xD9\x85";
+ break;
+ case 0x98 :
+ return "\xD9\x86\xD8\xAC\xD9\x85";
+ break;
+ case 0x99 :
+ return "\xD9\x86\xD8\xAC\xD9\x89";
+ break;
+ case 0x9A :
+ return "\xD9\x86\xD9\x85\xD9\x8A";
+ break;
+ case 0x9B :
+ return "\xD9\x86\xD9\x85\xD9\x89";
+ break;
+ case 0x9C :
+ return "\xD9\x8A\xD9\x85\xD9\x85";
+ break;
+ case 0x9D :
+ return "\xD9\x8A\xD9\x85\xD9\x85";
+ break;
+ case 0x9E :
+ return "\xD8\xA8\xD8\xAE\xD9\x8A";
+ break;
+ case 0x9F :
+ return "\xD8\xAA\xD8\xAC\xD9\x8A";
+ break;
+ case 0xA0 :
+ return "\xD8\xAA\xD8\xAC\xD9\x89";
+ break;
+ case 0xA1 :
+ return "\xD8\xAA\xD8\xAE\xD9\x8A";
+ break;
+ case 0xA2 :
+ return "\xD8\xAA\xD8\xAE\xD9\x89";
+ break;
+ case 0xA3 :
+ return "\xD8\xAA\xD9\x85\xD9\x8A";
+ break;
+ case 0xA4 :
+ return "\xD8\xAA\xD9\x85\xD9\x89";
+ break;
+ case 0xA5 :
+ return "\xD8\xAC\xD9\x85\xD9\x8A";
+ break;
+ case 0xA6 :
+ return "\xD8\xAC\xD8\xAD\xD9\x89";
+ break;
+ case 0xA7 :
+ return "\xD8\xAC\xD9\x85\xD9\x89";
+ break;
+ case 0xA8 :
+ return "\xD8\xB3\xD8\xAE\xD9\x89";
+ break;
+ case 0xA9 :
+ return "\xD8\xB5\xD8\xAD\xD9\x8A";
+ break;
+ case 0xAA :
+ return "\xD8\xB4\xD8\xAD\xD9\x8A";
+ break;
+ case 0xAB :
+ return "\xD8\xB6\xD8\xAD\xD9\x8A";
+ break;
+ case 0xAC :
+ return "\xD9\x84\xD8\xAC\xD9\x8A";
+ break;
+ case 0xAD :
+ return "\xD9\x84\xD9\x85\xD9\x8A";
+ break;
+ case 0xAE :
+ return "\xD9\x8A\xD8\xAD\xD9\x8A";
+ break;
+ case 0xAF :
+ return "\xD9\x8A\xD8\xAC\xD9\x8A";
+ break;
+ case 0xB0 :
+ return "\xD9\x8A\xD9\x85\xD9\x8A";
+ break;
+ case 0xB1 :
+ return "\xD9\x85\xD9\x85\xD9\x8A";
+ break;
+ case 0xB2 :
+ return "\xD9\x82\xD9\x85\xD9\x8A";
+ break;
+ case 0xB3 :
+ return "\xD9\x86\xD8\xAD\xD9\x8A";
+ break;
+ case 0xB4 :
+ return "\xD9\x82\xD9\x85\xD8\xAD";
+ break;
+ case 0xB5 :
+ return "\xD9\x84\xD8\xAD\xD9\x85";
+ break;
+ case 0xB6 :
+ return "\xD8\xB9\xD9\x85\xD9\x8A";
+ break;
+ case 0xB7 :
+ return "\xD9\x83\xD9\x85\xD9\x8A";
+ break;
+ case 0xB8 :
+ return "\xD9\x86\xD8\xAC\xD8\xAD";
+ break;
+ case 0xB9 :
+ return "\xD9\x85\xD8\xAE\xD9\x8A";
+ break;
+ case 0xBA :
+ return "\xD9\x84\xD8\xAC\xD9\x85";
+ break;
+ case 0xBB :
+ return "\xD9\x83\xD9\x85\xD9\x85";
+ break;
+ case 0xBC :
+ return "\xD9\x84\xD8\xAC\xD9\x85";
+ break;
+ case 0xBD :
+ return "\xD9\x86\xD8\xAC\xD8\xAD";
+ break;
+ case 0xBE :
+ return "\xD8\xAC\xD8\xAD\xD9\x8A";
+ break;
+ case 0xBF :
+ return "\xD8\xAD\xD8\xAC\xD9\x8A";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xD9\x85\xD8\xAC\xD9\x8A";
+ break;
+ case 0x81 :
+ return "\xD9\x81\xD9\x85\xD9\x8A";
+ break;
+ case 0x82 :
+ return "\xD8\xA8\xD8\xAD\xD9\x8A";
+ break;
+ case 0x83 :
+ return "\xD9\x83\xD9\x85\xD9\x85";
+ break;
+ case 0x84 :
+ return "\xD8\xB9\xD8\xAC\xD9\x85";
+ break;
+ case 0x85 :
+ return "\xD8\xB5\xD9\x85\xD9\x85";
+ break;
+ case 0x86 :
+ return "\xD8\xB3\xD8\xAE\xD9\x8A";
+ break;
+ case 0x87 :
+ return "\xD9\x86\xD8\xAC\xD9\x8A";
+ break;
+ case 0xB0 :
+ return "\xD8\xB5\xD9\x84\xDB\x92";
+ break;
+ case 0xB1 :
+ return "\xD9\x82\xD9\x84\xDB\x92";
+ break;
+ case 0xB2 :
+ return "\xD8\xA7\xD9\x84\xD9\x84\xD9\x87";
+ break;
+ case 0xB3 :
+ return "\xD8\xA7\xD9\x83\xD8\xA8\xD8\xB1";
+ break;
+ case 0xB4 :
+ return "\xD9\x85\xD8\xAD\xD9\x85\xD8\xAF";
+ break;
+ case 0xB5 :
+ return "\xD8\xB5\xD9\x84\xD8\xB9\xD9\x85";
+ break;
+ case 0xB6 :
+ return "\xD8\xB1\xD8\xB3\xD9\x88\xD9\x84";
+ break;
+ case 0xB7 :
+ return "\xD8\xB9\xD9\x84\xD9\x8A\xD9\x87";
+ break;
+ case 0xB8 :
+ return "\xD9\x88\xD8\xB3\xD9\x84\xD9\x85";
+ break;
+ case 0xB9 :
+ return "\xD8\xB5\xD9\x84\xD9\x89";
+ break;
+ case 0xBA :
+ return "\xD8\xB5\xD9\x84\xD9\x89\x20\xD8\xA7\xD9\x84\xD9\x84\xD9\x87\x20\xD8\xB9\xD9\x84\xD9\x8A\xD9\x87\x20\xD9\x88\xD8\xB3\xD9\x84\xD9\x85";
+ break;
+ case 0xBB :
+ return "\xD8\xAC\xD9\x84\x20\xD8\xAC\xD9\x84\xD8\xA7\xD9\x84\xD9\x87";
+ break;
+ case 0xBC :
+ return "\xD8\xB1\xDB\x8C\xD8\xA7\xD9\x84";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (str[2]) {
+ case 0x90 :
+ return "\x2C";
+ break;
+ case 0x91 :
+ return "\xE3\x80\x81";
+ break;
+ case 0x92 :
+ return "\xE3\x80\x82";
+ break;
+ case 0x93 :
+ return "\x3A";
+ break;
+ case 0x94 :
+ return "\x3B";
+ break;
+ case 0x95 :
+ return "\x21";
+ break;
+ case 0x96 :
+ return "\x3F";
+ break;
+ case 0x97 :
+ return "\xE3\x80\x96";
+ break;
+ case 0x98 :
+ return "\xE3\x80\x97";
+ break;
+ case 0x99 :
+ return "\x2E\x2E\x2E";
+ break;
+ case 0xB0 :
+ return "\x2E\x2E";
+ break;
+ case 0xB1 :
+ return "\xE2\x80\x94";
+ break;
+ case 0xB2 :
+ return "\xE2\x80\x93";
+ break;
+ case 0xB3 :
+ return "\x5F";
+ break;
+ case 0xB4 :
+ return "\x5F";
+ break;
+ case 0xB5 :
+ return "\x28";
+ break;
+ case 0xB6 :
+ return "\x29";
+ break;
+ case 0xB7 :
+ return "\x7B";
+ break;
+ case 0xB8 :
+ return "\x7D";
+ break;
+ case 0xB9 :
+ return "\xE3\x80\x94";
+ break;
+ case 0xBA :
+ return "\xE3\x80\x95";
+ break;
+ case 0xBB :
+ return "\xE3\x80\x90";
+ break;
+ case 0xBC :
+ return "\xE3\x80\x91";
+ break;
+ case 0xBD :
+ return "\xE3\x80\x8A";
+ break;
+ case 0xBE :
+ return "\xE3\x80\x8B";
+ break;
+ case 0xBF :
+ return "\xE3\x80\x88";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE3\x80\x89";
+ break;
+ case 0x81 :
+ return "\xE3\x80\x8C";
+ break;
+ case 0x82 :
+ return "\xE3\x80\x8D";
+ break;
+ case 0x83 :
+ return "\xE3\x80\x8E";
+ break;
+ case 0x84 :
+ return "\xE3\x80\x8F";
+ break;
+ case 0x87 :
+ return "\x5B";
+ break;
+ case 0x88 :
+ return "\x5D";
+ break;
+ case 0x89 :
+ return "\xCC\x85";
+ break;
+ case 0x8A :
+ return "\xCC\x85";
+ break;
+ case 0x8B :
+ return "\xCC\x85";
+ break;
+ case 0x8C :
+ return "\xCC\x85";
+ break;
+ case 0x8D :
+ return "\x5F";
+ break;
+ case 0x8E :
+ return "\x5F";
+ break;
+ case 0x8F :
+ return "\x5F";
+ break;
+ case 0x90 :
+ return "\x2C";
+ break;
+ case 0x91 :
+ return "\xE3\x80\x81";
+ break;
+ case 0x92 :
+ return "\x2E";
+ break;
+ case 0x94 :
+ return "\x3B";
+ break;
+ case 0x95 :
+ return "\x3A";
+ break;
+ case 0x96 :
+ return "\x3F";
+ break;
+ case 0x97 :
+ return "\x21";
+ break;
+ case 0x98 :
+ return "\xE2\x80\x94";
+ break;
+ case 0x99 :
+ return "\x28";
+ break;
+ case 0x9A :
+ return "\x29";
+ break;
+ case 0x9B :
+ return "\x7B";
+ break;
+ case 0x9C :
+ return "\x7D";
+ break;
+ case 0x9D :
+ return "\xE3\x80\x94";
+ break;
+ case 0x9E :
+ return "\xE3\x80\x95";
+ break;
+ case 0x9F :
+ return "\x23";
+ break;
+ case 0xA0 :
+ return "\x26";
+ break;
+ case 0xA1 :
+ return "\x2A";
+ break;
+ case 0xA2 :
+ return "\x2B";
+ break;
+ case 0xA3 :
+ return "\x2D";
+ break;
+ case 0xA4 :
+ return "\x3C";
+ break;
+ case 0xA5 :
+ return "\x3E";
+ break;
+ case 0xA6 :
+ return "\x3D";
+ break;
+ case 0xA8 :
+ return "\x5C";
+ break;
+ case 0xA9 :
+ return "\x24";
+ break;
+ case 0xAA :
+ return "\x25";
+ break;
+ case 0xAB :
+ return "\x40";
+ break;
+ case 0xB0 :
+ return "\xD9\x8B";
+ break;
+ case 0xB1 :
+ return "\xD9\x80\xD9\x8B";
+ break;
+ case 0xB2 :
+ return "\xD9\x8C";
+ break;
+ case 0xB4 :
+ return "\xD9\x8D";
+ break;
+ case 0xB6 :
+ return "\xD9\x8E";
+ break;
+ case 0xB7 :
+ return "\xD9\x80\xD9\x8E";
+ break;
+ case 0xB8 :
+ return "\xD9\x8F";
+ break;
+ case 0xB9 :
+ return "\xD9\x80\xD9\x8F";
+ break;
+ case 0xBA :
+ return "\xD9\x90";
+ break;
+ case 0xBB :
+ return "\xD9\x80\xD9\x90";
+ break;
+ case 0xBC :
+ return "\xD9\x91";
+ break;
+ case 0xBD :
+ return "\xD9\x80\xD9\x91";
+ break;
+ case 0xBE :
+ return "\xD9\x92";
+ break;
+ case 0xBF :
+ return "\xD9\x80\xD9\x92";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xD8\xA1";
+ break;
+ case 0x81 :
+ return "\xD8\xA2";
+ break;
+ case 0x82 :
+ return "\xD8\xA2";
+ break;
+ case 0x83 :
+ return "\xD8\xA3";
+ break;
+ case 0x84 :
+ return "\xD8\xA3";
+ break;
+ case 0x85 :
+ return "\xD8\xA4";
+ break;
+ case 0x86 :
+ return "\xD8\xA4";
+ break;
+ case 0x87 :
+ return "\xD8\xA5";
+ break;
+ case 0x88 :
+ return "\xD8\xA5";
+ break;
+ case 0x89 :
+ return "\xD8\xA6";
+ break;
+ case 0x8A :
+ return "\xD8\xA6";
+ break;
+ case 0x8B :
+ return "\xD8\xA6";
+ break;
+ case 0x8C :
+ return "\xD8\xA6";
+ break;
+ case 0x8D :
+ return "\xD8\xA7";
+ break;
+ case 0x8E :
+ return "\xD8\xA7";
+ break;
+ case 0x8F :
+ return "\xD8\xA8";
+ break;
+ case 0x90 :
+ return "\xD8\xA8";
+ break;
+ case 0x91 :
+ return "\xD8\xA8";
+ break;
+ case 0x92 :
+ return "\xD8\xA8";
+ break;
+ case 0x93 :
+ return "\xD8\xA9";
+ break;
+ case 0x94 :
+ return "\xD8\xA9";
+ break;
+ case 0x95 :
+ return "\xD8\xAA";
+ break;
+ case 0x96 :
+ return "\xD8\xAA";
+ break;
+ case 0x97 :
+ return "\xD8\xAA";
+ break;
+ case 0x98 :
+ return "\xD8\xAA";
+ break;
+ case 0x99 :
+ return "\xD8\xAB";
+ break;
+ case 0x9A :
+ return "\xD8\xAB";
+ break;
+ case 0x9B :
+ return "\xD8\xAB";
+ break;
+ case 0x9C :
+ return "\xD8\xAB";
+ break;
+ case 0x9D :
+ return "\xD8\xAC";
+ break;
+ case 0x9E :
+ return "\xD8\xAC";
+ break;
+ case 0x9F :
+ return "\xD8\xAC";
+ break;
+ case 0xA0 :
+ return "\xD8\xAC";
+ break;
+ case 0xA1 :
+ return "\xD8\xAD";
+ break;
+ case 0xA2 :
+ return "\xD8\xAD";
+ break;
+ case 0xA3 :
+ return "\xD8\xAD";
+ break;
+ case 0xA4 :
+ return "\xD8\xAD";
+ break;
+ case 0xA5 :
+ return "\xD8\xAE";
+ break;
+ case 0xA6 :
+ return "\xD8\xAE";
+ break;
+ case 0xA7 :
+ return "\xD8\xAE";
+ break;
+ case 0xA8 :
+ return "\xD8\xAE";
+ break;
+ case 0xA9 :
+ return "\xD8\xAF";
+ break;
+ case 0xAA :
+ return "\xD8\xAF";
+ break;
+ case 0xAB :
+ return "\xD8\xB0";
+ break;
+ case 0xAC :
+ return "\xD8\xB0";
+ break;
+ case 0xAD :
+ return "\xD8\xB1";
+ break;
+ case 0xAE :
+ return "\xD8\xB1";
+ break;
+ case 0xAF :
+ return "\xD8\xB2";
+ break;
+ case 0xB0 :
+ return "\xD8\xB2";
+ break;
+ case 0xB1 :
+ return "\xD8\xB3";
+ break;
+ case 0xB2 :
+ return "\xD8\xB3";
+ break;
+ case 0xB3 :
+ return "\xD8\xB3";
+ break;
+ case 0xB4 :
+ return "\xD8\xB3";
+ break;
+ case 0xB5 :
+ return "\xD8\xB4";
+ break;
+ case 0xB6 :
+ return "\xD8\xB4";
+ break;
+ case 0xB7 :
+ return "\xD8\xB4";
+ break;
+ case 0xB8 :
+ return "\xD8\xB4";
+ break;
+ case 0xB9 :
+ return "\xD8\xB5";
+ break;
+ case 0xBA :
+ return "\xD8\xB5";
+ break;
+ case 0xBB :
+ return "\xD8\xB5";
+ break;
+ case 0xBC :
+ return "\xD8\xB5";
+ break;
+ case 0xBD :
+ return "\xD8\xB6";
+ break;
+ case 0xBE :
+ return "\xD8\xB6";
+ break;
+ case 0xBF :
+ return "\xD8\xB6";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xD8\xB6";
+ break;
+ case 0x81 :
+ return "\xD8\xB7";
+ break;
+ case 0x82 :
+ return "\xD8\xB7";
+ break;
+ case 0x83 :
+ return "\xD8\xB7";
+ break;
+ case 0x84 :
+ return "\xD8\xB7";
+ break;
+ case 0x85 :
+ return "\xD8\xB8";
+ break;
+ case 0x86 :
+ return "\xD8\xB8";
+ break;
+ case 0x87 :
+ return "\xD8\xB8";
+ break;
+ case 0x88 :
+ return "\xD8\xB8";
+ break;
+ case 0x89 :
+ return "\xD8\xB9";
+ break;
+ case 0x8A :
+ return "\xD8\xB9";
+ break;
+ case 0x8B :
+ return "\xD8\xB9";
+ break;
+ case 0x8C :
+ return "\xD8\xB9";
+ break;
+ case 0x8D :
+ return "\xD8\xBA";
+ break;
+ case 0x8E :
+ return "\xD8\xBA";
+ break;
+ case 0x8F :
+ return "\xD8\xBA";
+ break;
+ case 0x90 :
+ return "\xD8\xBA";
+ break;
+ case 0x91 :
+ return "\xD9\x81";
+ break;
+ case 0x92 :
+ return "\xD9\x81";
+ break;
+ case 0x93 :
+ return "\xD9\x81";
+ break;
+ case 0x94 :
+ return "\xD9\x81";
+ break;
+ case 0x95 :
+ return "\xD9\x82";
+ break;
+ case 0x96 :
+ return "\xD9\x82";
+ break;
+ case 0x97 :
+ return "\xD9\x82";
+ break;
+ case 0x98 :
+ return "\xD9\x82";
+ break;
+ case 0x99 :
+ return "\xD9\x83";
+ break;
+ case 0x9A :
+ return "\xD9\x83";
+ break;
+ case 0x9B :
+ return "\xD9\x83";
+ break;
+ case 0x9C :
+ return "\xD9\x83";
+ break;
+ case 0x9D :
+ return "\xD9\x84";
+ break;
+ case 0x9E :
+ return "\xD9\x84";
+ break;
+ case 0x9F :
+ return "\xD9\x84";
+ break;
+ case 0xA0 :
+ return "\xD9\x84";
+ break;
+ case 0xA1 :
+ return "\xD9\x85";
+ break;
+ case 0xA2 :
+ return "\xD9\x85";
+ break;
+ case 0xA3 :
+ return "\xD9\x85";
+ break;
+ case 0xA4 :
+ return "\xD9\x85";
+ break;
+ case 0xA5 :
+ return "\xD9\x86";
+ break;
+ case 0xA6 :
+ return "\xD9\x86";
+ break;
+ case 0xA7 :
+ return "\xD9\x86";
+ break;
+ case 0xA8 :
+ return "\xD9\x86";
+ break;
+ case 0xA9 :
+ return "\xD9\x87";
+ break;
+ case 0xAA :
+ return "\xD9\x87";
+ break;
+ case 0xAB :
+ return "\xD9\x87";
+ break;
+ case 0xAC :
+ return "\xD9\x87";
+ break;
+ case 0xAD :
+ return "\xD9\x88";
+ break;
+ case 0xAE :
+ return "\xD9\x88";
+ break;
+ case 0xAF :
+ return "\xD9\x89";
+ break;
+ case 0xB0 :
+ return "\xD9\x89";
+ break;
+ case 0xB1 :
+ return "\xD9\x8A";
+ break;
+ case 0xB2 :
+ return "\xD9\x8A";
+ break;
+ case 0xB3 :
+ return "\xD9\x8A";
+ break;
+ case 0xB4 :
+ return "\xD9\x8A";
+ break;
+ case 0xB5 :
+ return "\xD9\x84\xD8\xA2";
+ break;
+ case 0xB6 :
+ return "\xD9\x84\xD8\xA2";
+ break;
+ case 0xB7 :
+ return "\xD9\x84\xD8\xA3";
+ break;
+ case 0xB8 :
+ return "\xD9\x84\xD8\xA3";
+ break;
+ case 0xB9 :
+ return "\xD9\x84\xD8\xA5";
+ break;
+ case 0xBA :
+ return "\xD9\x84\xD8\xA5";
+ break;
+ case 0xBB :
+ return "\xD9\x84\xD8\xA7";
+ break;
+ case 0xBC :
+ return "\xD9\x84\xD8\xA7";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (str[2]) {
+ case 0x81 :
+ return "\x21";
+ break;
+ case 0x82 :
+ return "\x22";
+ break;
+ case 0x83 :
+ return "\x23";
+ break;
+ case 0x84 :
+ return "\x24";
+ break;
+ case 0x85 :
+ return "\x25";
+ break;
+ case 0x86 :
+ return "\x26";
+ break;
+ case 0x87 :
+ return "\x27";
+ break;
+ case 0x88 :
+ return "\x28";
+ break;
+ case 0x89 :
+ return "\x29";
+ break;
+ case 0x8A :
+ return "\x2A";
+ break;
+ case 0x8B :
+ return "\x2B";
+ break;
+ case 0x8C :
+ return "\x2C";
+ break;
+ case 0x8D :
+ return "\x2D";
+ break;
+ case 0x8E :
+ return "\x2E";
+ break;
+ case 0x8F :
+ return "\x2F";
+ break;
+ case 0x90 :
+ return "\x30";
+ break;
+ case 0x91 :
+ return "\x31";
+ break;
+ case 0x92 :
+ return "\x32";
+ break;
+ case 0x93 :
+ return "\x33";
+ break;
+ case 0x94 :
+ return "\x34";
+ break;
+ case 0x95 :
+ return "\x35";
+ break;
+ case 0x96 :
+ return "\x36";
+ break;
+ case 0x97 :
+ return "\x37";
+ break;
+ case 0x98 :
+ return "\x38";
+ break;
+ case 0x99 :
+ return "\x39";
+ break;
+ case 0x9A :
+ return "\x3A";
+ break;
+ case 0x9B :
+ return "\x3B";
+ break;
+ case 0x9C :
+ return "\x3C";
+ break;
+ case 0x9D :
+ return "\x3D";
+ break;
+ case 0x9E :
+ return "\x3E";
+ break;
+ case 0x9F :
+ return "\x3F";
+ break;
+ case 0xA0 :
+ return "\x40";
+ break;
+ case 0xA1 :
+ return "\x61";
+ break;
+ case 0xA2 :
+ return "\x62";
+ break;
+ case 0xA3 :
+ return "\x63";
+ break;
+ case 0xA4 :
+ return "\x64";
+ break;
+ case 0xA5 :
+ return "\x65";
+ break;
+ case 0xA6 :
+ return "\x66";
+ break;
+ case 0xA7 :
+ return "\x67";
+ break;
+ case 0xA8 :
+ return "\x68";
+ break;
+ case 0xA9 :
+ return "\x69";
+ break;
+ case 0xAA :
+ return "\x6A";
+ break;
+ case 0xAB :
+ return "\x6B";
+ break;
+ case 0xAC :
+ return "\x6C";
+ break;
+ case 0xAD :
+ return "\x6D";
+ break;
+ case 0xAE :
+ return "\x6E";
+ break;
+ case 0xAF :
+ return "\x6F";
+ break;
+ case 0xB0 :
+ return "\x70";
+ break;
+ case 0xB1 :
+ return "\x71";
+ break;
+ case 0xB2 :
+ return "\x72";
+ break;
+ case 0xB3 :
+ return "\x73";
+ break;
+ case 0xB4 :
+ return "\x74";
+ break;
+ case 0xB5 :
+ return "\x75";
+ break;
+ case 0xB6 :
+ return "\x76";
+ break;
+ case 0xB7 :
+ return "\x77";
+ break;
+ case 0xB8 :
+ return "\x78";
+ break;
+ case 0xB9 :
+ return "\x79";
+ break;
+ case 0xBA :
+ return "\x7A";
+ break;
+ case 0xBB :
+ return "\x5B";
+ break;
+ case 0xBC :
+ return "\x5C";
+ break;
+ case 0xBD :
+ return "\x5D";
+ break;
+ case 0xBE :
+ return "\x5E";
+ break;
+ case 0xBF :
+ return "\x5F";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (str[2]) {
+ case 0x80 :
+ return "\x60";
+ break;
+ case 0x81 :
+ return "\x61";
+ break;
+ case 0x82 :
+ return "\x62";
+ break;
+ case 0x83 :
+ return "\x63";
+ break;
+ case 0x84 :
+ return "\x64";
+ break;
+ case 0x85 :
+ return "\x65";
+ break;
+ case 0x86 :
+ return "\x66";
+ break;
+ case 0x87 :
+ return "\x67";
+ break;
+ case 0x88 :
+ return "\x68";
+ break;
+ case 0x89 :
+ return "\x69";
+ break;
+ case 0x8A :
+ return "\x6A";
+ break;
+ case 0x8B :
+ return "\x6B";
+ break;
+ case 0x8C :
+ return "\x6C";
+ break;
+ case 0x8D :
+ return "\x6D";
+ break;
+ case 0x8E :
+ return "\x6E";
+ break;
+ case 0x8F :
+ return "\x6F";
+ break;
+ case 0x90 :
+ return "\x70";
+ break;
+ case 0x91 :
+ return "\x71";
+ break;
+ case 0x92 :
+ return "\x72";
+ break;
+ case 0x93 :
+ return "\x73";
+ break;
+ case 0x94 :
+ return "\x74";
+ break;
+ case 0x95 :
+ return "\x75";
+ break;
+ case 0x96 :
+ return "\x76";
+ break;
+ case 0x97 :
+ return "\x77";
+ break;
+ case 0x98 :
+ return "\x78";
+ break;
+ case 0x99 :
+ return "\x79";
+ break;
+ case 0x9A :
+ return "\x7A";
+ break;
+ case 0x9B :
+ return "\x7B";
+ break;
+ case 0x9C :
+ return "\x7C";
+ break;
+ case 0x9D :
+ return "\x7D";
+ break;
+ case 0x9E :
+ return "\x7E";
+ break;
+ case 0x9F :
+ return "\xE2\xA6\x85";
+ break;
+ case 0xA0 :
+ return "\xE2\xA6\x86";
+ break;
+ case 0xA1 :
+ return "\xE3\x80\x82";
+ break;
+ case 0xA2 :
+ return "\xE3\x80\x8C";
+ break;
+ case 0xA3 :
+ return "\xE3\x80\x8D";
+ break;
+ case 0xA4 :
+ return "\xE3\x80\x81";
+ break;
+ case 0xA5 :
+ return "\xE3\x83\xBB";
+ break;
+ case 0xA6 :
+ return "\xE3\x83\xB2";
+ break;
+ case 0xA7 :
+ return "\xE3\x82\xA1";
+ break;
+ case 0xA8 :
+ return "\xE3\x82\xA3";
+ break;
+ case 0xA9 :
+ return "\xE3\x82\xA5";
+ break;
+ case 0xAA :
+ return "\xE3\x82\xA7";
+ break;
+ case 0xAB :
+ return "\xE3\x82\xA9";
+ break;
+ case 0xAC :
+ return "\xE3\x83\xA3";
+ break;
+ case 0xAD :
+ return "\xE3\x83\xA5";
+ break;
+ case 0xAE :
+ return "\xE3\x83\xA7";
+ break;
+ case 0xAF :
+ return "\xE3\x83\x83";
+ break;
+ case 0xB0 :
+ return "\xE3\x83\xBC";
+ break;
+ case 0xB1 :
+ return "\xE3\x82\xA2";
+ break;
+ case 0xB2 :
+ return "\xE3\x82\xA4";
+ break;
+ case 0xB3 :
+ return "\xE3\x82\xA6";
+ break;
+ case 0xB4 :
+ return "\xE3\x82\xA8";
+ break;
+ case 0xB5 :
+ return "\xE3\x82\xAA";
+ break;
+ case 0xB6 :
+ return "\xE3\x82\xAB";
+ break;
+ case 0xB7 :
+ return "\xE3\x82\xAD";
+ break;
+ case 0xB8 :
+ return "\xE3\x82\xAF";
+ break;
+ case 0xB9 :
+ return "\xE3\x82\xB1";
+ break;
+ case 0xBA :
+ return "\xE3\x82\xB3";
+ break;
+ case 0xBB :
+ return "\xE3\x82\xB5";
+ break;
+ case 0xBC :
+ return "\xE3\x82\xB7";
+ break;
+ case 0xBD :
+ return "\xE3\x82\xB9";
+ break;
+ case 0xBE :
+ return "\xE3\x82\xBB";
+ break;
+ case 0xBF :
+ return "\xE3\x82\xBD";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (str[2]) {
+ case 0x80 :
+ return "\xE3\x82\xBF";
+ break;
+ case 0x81 :
+ return "\xE3\x83\x81";
+ break;
+ case 0x82 :
+ return "\xE3\x83\x84";
+ break;
+ case 0x83 :
+ return "\xE3\x83\x86";
+ break;
+ case 0x84 :
+ return "\xE3\x83\x88";
+ break;
+ case 0x85 :
+ return "\xE3\x83\x8A";
+ break;
+ case 0x86 :
+ return "\xE3\x83\x8B";
+ break;
+ case 0x87 :
+ return "\xE3\x83\x8C";
+ break;
+ case 0x88 :
+ return "\xE3\x83\x8D";
+ break;
+ case 0x89 :
+ return "\xE3\x83\x8E";
+ break;
+ case 0x8A :
+ return "\xE3\x83\x8F";
+ break;
+ case 0x8B :
+ return "\xE3\x83\x92";
+ break;
+ case 0x8C :
+ return "\xE3\x83\x95";
+ break;
+ case 0x8D :
+ return "\xE3\x83\x98";
+ break;
+ case 0x8E :
+ return "\xE3\x83\x9B";
+ break;
+ case 0x8F :
+ return "\xE3\x83\x9E";
+ break;
+ case 0x90 :
+ return "\xE3\x83\x9F";
+ break;
+ case 0x91 :
+ return "\xE3\x83\xA0";
+ break;
+ case 0x92 :
+ return "\xE3\x83\xA1";
+ break;
+ case 0x93 :
+ return "\xE3\x83\xA2";
+ break;
+ case 0x94 :
+ return "\xE3\x83\xA4";
+ break;
+ case 0x95 :
+ return "\xE3\x83\xA6";
+ break;
+ case 0x96 :
+ return "\xE3\x83\xA8";
+ break;
+ case 0x97 :
+ return "\xE3\x83\xA9";
+ break;
+ case 0x98 :
+ return "\xE3\x83\xAA";
+ break;
+ case 0x99 :
+ return "\xE3\x83\xAB";
+ break;
+ case 0x9A :
+ return "\xE3\x83\xAC";
+ break;
+ case 0x9B :
+ return "\xE3\x83\xAD";
+ break;
+ case 0x9C :
+ return "\xE3\x83\xAF";
+ break;
+ case 0x9D :
+ return "\xE3\x83\xB3";
+ break;
+ case 0x9E :
+ return "\xE3\x82\x99";
+ break;
+ case 0x9F :
+ return "\xE3\x82\x9A";
+ break;
+ case 0xA0 :
+ return "\xE1\x85\xA0";
+ break;
+ case 0xA1 :
+ return "\xE1\x84\x80";
+ break;
+ case 0xA2 :
+ return "\xE1\x84\x81";
+ break;
+ case 0xA3 :
+ return "\xE1\x86\xAA";
+ break;
+ case 0xA4 :
+ return "\xE1\x84\x82";
+ break;
+ case 0xA5 :
+ return "\xE1\x86\xAC";
+ break;
+ case 0xA6 :
+ return "\xE1\x86\xAD";
+ break;
+ case 0xA7 :
+ return "\xE1\x84\x83";
+ break;
+ case 0xA8 :
+ return "\xE1\x84\x84";
+ break;
+ case 0xA9 :
+ return "\xE1\x84\x85";
+ break;
+ case 0xAA :
+ return "\xE1\x86\xB0";
+ break;
+ case 0xAB :
+ return "\xE1\x86\xB1";
+ break;
+ case 0xAC :
+ return "\xE1\x86\xB2";
+ break;
+ case 0xAD :
+ return "\xE1\x86\xB3";
+ break;
+ case 0xAE :
+ return "\xE1\x86\xB4";
+ break;
+ case 0xAF :
+ return "\xE1\x86\xB5";
+ break;
+ case 0xB0 :
+ return "\xE1\x84\x9A";
+ break;
+ case 0xB1 :
+ return "\xE1\x84\x86";
+ break;
+ case 0xB2 :
+ return "\xE1\x84\x87";
+ break;
+ case 0xB3 :
+ return "\xE1\x84\x88";
+ break;
+ case 0xB4 :
+ return "\xE1\x84\xA1";
+ break;
+ case 0xB5 :
+ return "\xE1\x84\x89";
+ break;
+ case 0xB6 :
+ return "\xE1\x84\x8A";
+ break;
+ case 0xB7 :
+ return "\xE1\x84\x8B";
+ break;
+ case 0xB8 :
+ return "\xE1\x84\x8C";
+ break;
+ case 0xB9 :
+ return "\xE1\x84\x8D";
+ break;
+ case 0xBA :
+ return "\xE1\x84\x8E";
+ break;
+ case 0xBB :
+ return "\xE1\x84\x8F";
+ break;
+ case 0xBC :
+ return "\xE1\x84\x90";
+ break;
+ case 0xBD :
+ return "\xE1\x84\x91";
+ break;
+ case 0xBE :
+ return "\xE1\x84\x92";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (str[2]) {
+ case 0x82 :
+ return "\xE1\x85\xA1";
+ break;
+ case 0x83 :
+ return "\xE1\x85\xA2";
+ break;
+ case 0x84 :
+ return "\xE1\x85\xA3";
+ break;
+ case 0x85 :
+ return "\xE1\x85\xA4";
+ break;
+ case 0x86 :
+ return "\xE1\x85\xA5";
+ break;
+ case 0x87 :
+ return "\xE1\x85\xA6";
+ break;
+ case 0x8A :
+ return "\xE1\x85\xA7";
+ break;
+ case 0x8B :
+ return "\xE1\x85\xA8";
+ break;
+ case 0x8C :
+ return "\xE1\x85\xA9";
+ break;
+ case 0x8D :
+ return "\xE1\x85\xAA";
+ break;
+ case 0x8E :
+ return "\xE1\x85\xAB";
+ break;
+ case 0x8F :
+ return "\xE1\x85\xAC";
+ break;
+ case 0x92 :
+ return "\xE1\x85\xAD";
+ break;
+ case 0x93 :
+ return "\xE1\x85\xAE";
+ break;
+ case 0x94 :
+ return "\xE1\x85\xAF";
+ break;
+ case 0x95 :
+ return "\xE1\x85\xB0";
+ break;
+ case 0x96 :
+ return "\xE1\x85\xB1";
+ break;
+ case 0x97 :
+ return "\xE1\x85\xB2";
+ break;
+ case 0x9A :
+ return "\xE1\x85\xB3";
+ break;
+ case 0x9B :
+ return "\xE1\x85\xB4";
+ break;
+ case 0x9C :
+ return "\xE1\x85\xB5";
+ break;
+ case 0xA0 :
+ return "\xC2\xA2";
+ break;
+ case 0xA1 :
+ return "\xC2\xA3";
+ break;
+ case 0xA2 :
+ return "\xC2\xAC";
+ break;
+ case 0xA3 :
+ return "\xCC\x84";
+ break;
+ case 0xA4 :
+ return "\xC2\xA6";
+ break;
+ case 0xA5 :
+ return "\xC2\xA5";
+ break;
+ case 0xA6 :
+ return "\xE2\x82\xA9";
+ break;
+ case 0xA8 :
+ return "\xE2\x94\x82";
+ break;
+ case 0xA9 :
+ return "\xE2\x86\x90";
+ break;
+ case 0xAA :
+ return "\xE2\x86\x91";
+ break;
+ case 0xAB :
+ return "\xE2\x86\x92";
+ break;
+ case 0xAC :
+ return "\xE2\x86\x93";
+ break;
+ case 0xAD :
+ return "\xE2\x96\xA0";
+ break;
+ case 0xAE :
+ return "\xE2\x97\x8B";
+ break;
+ }
+ break;
+ }
+ break;
+case 0xF0 :
+ switch (str[1]) {
+ case 0x9D :
+ switch (str[2]) {
+ case 0x85 :
+ switch (str[3]) {
+ case 0x9E :
+ return "\xF0\x9D\x85\x97\xF0\x9D\x85\xA5";
+ break;
+ case 0x9F :
+ return "\xF0\x9D\x85\x98\xF0\x9D\x85\xA5";
+ break;
+ case 0xA0 :
+ return "\xF0\x9D\x85\x98\xF0\x9D\x85\xA5\xF0\x9D\x85\xAE";
+ break;
+ case 0xA1 :
+ return "\xF0\x9D\x85\x98\xF0\x9D\x85\xA5\xF0\x9D\x85\xAF";
+ break;
+ case 0xA2 :
+ return "\xF0\x9D\x85\x98\xF0\x9D\x85\xA5\xF0\x9D\x85\xB0";
+ break;
+ case 0xA3 :
+ return "\xF0\x9D\x85\x98\xF0\x9D\x85\xA5\xF0\x9D\x85\xB1";
+ break;
+ case 0xA4 :
+ return "\xF0\x9D\x85\x98\xF0\x9D\x85\xA5\xF0\x9D\x85\xB2";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (str[3]) {
+ case 0xBB :
+ return "\xF0\x9D\x86\xB9\xF0\x9D\x85\xA5";
+ break;
+ case 0xBC :
+ return "\xF0\x9D\x86\xBA\xF0\x9D\x85\xA5";
+ break;
+ case 0xBD :
+ return "\xF0\x9D\x86\xB9\xF0\x9D\x85\xA5\xF0\x9D\x85\xAE";
+ break;
+ case 0xBE :
+ return "\xF0\x9D\x86\xBA\xF0\x9D\x85\xA5\xF0\x9D\x85\xAE";
+ break;
+ case 0xBF :
+ return "\xF0\x9D\x86\xB9\xF0\x9D\x85\xA5\xF0\x9D\x85\xAF";
+ break;
+ }
+ break;
+ case 0x87 :
+ if (str[3] == 0x80) {
+ return "\xF0\x9D\x86\xBA\xF0\x9D\x85\xA5\xF0\x9D\x85\xAF";
+ }
+ break;
+ case 0x90 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\x61";
+ break;
+ case 0x81 :
+ return "\x62";
+ break;
+ case 0x82 :
+ return "\x63";
+ break;
+ case 0x83 :
+ return "\x64";
+ break;
+ case 0x84 :
+ return "\x65";
+ break;
+ case 0x85 :
+ return "\x66";
+ break;
+ case 0x86 :
+ return "\x67";
+ break;
+ case 0x87 :
+ return "\x68";
+ break;
+ case 0x88 :
+ return "\x69";
+ break;
+ case 0x89 :
+ return "\x6A";
+ break;
+ case 0x8A :
+ return "\x6B";
+ break;
+ case 0x8B :
+ return "\x6C";
+ break;
+ case 0x8C :
+ return "\x6D";
+ break;
+ case 0x8D :
+ return "\x6E";
+ break;
+ case 0x8E :
+ return "\x6F";
+ break;
+ case 0x8F :
+ return "\x70";
+ break;
+ case 0x90 :
+ return "\x71";
+ break;
+ case 0x91 :
+ return "\x72";
+ break;
+ case 0x92 :
+ return "\x73";
+ break;
+ case 0x93 :
+ return "\x74";
+ break;
+ case 0x94 :
+ return "\x75";
+ break;
+ case 0x95 :
+ return "\x76";
+ break;
+ case 0x96 :
+ return "\x77";
+ break;
+ case 0x97 :
+ return "\x78";
+ break;
+ case 0x98 :
+ return "\x79";
+ break;
+ case 0x99 :
+ return "\x7A";
+ break;
+ case 0x9A :
+ return "\x61";
+ break;
+ case 0x9B :
+ return "\x62";
+ break;
+ case 0x9C :
+ return "\x63";
+ break;
+ case 0x9D :
+ return "\x64";
+ break;
+ case 0x9E :
+ return "\x65";
+ break;
+ case 0x9F :
+ return "\x66";
+ break;
+ case 0xA0 :
+ return "\x67";
+ break;
+ case 0xA1 :
+ return "\x68";
+ break;
+ case 0xA2 :
+ return "\x69";
+ break;
+ case 0xA3 :
+ return "\x6A";
+ break;
+ case 0xA4 :
+ return "\x6B";
+ break;
+ case 0xA5 :
+ return "\x6C";
+ break;
+ case 0xA6 :
+ return "\x6D";
+ break;
+ case 0xA7 :
+ return "\x6E";
+ break;
+ case 0xA8 :
+ return "\x6F";
+ break;
+ case 0xA9 :
+ return "\x70";
+ break;
+ case 0xAA :
+ return "\x71";
+ break;
+ case 0xAB :
+ return "\x72";
+ break;
+ case 0xAC :
+ return "\x73";
+ break;
+ case 0xAD :
+ return "\x74";
+ break;
+ case 0xAE :
+ return "\x75";
+ break;
+ case 0xAF :
+ return "\x76";
+ break;
+ case 0xB0 :
+ return "\x77";
+ break;
+ case 0xB1 :
+ return "\x78";
+ break;
+ case 0xB2 :
+ return "\x79";
+ break;
+ case 0xB3 :
+ return "\x7A";
+ break;
+ case 0xB4 :
+ return "\x61";
+ break;
+ case 0xB5 :
+ return "\x62";
+ break;
+ case 0xB6 :
+ return "\x63";
+ break;
+ case 0xB7 :
+ return "\x64";
+ break;
+ case 0xB8 :
+ return "\x65";
+ break;
+ case 0xB9 :
+ return "\x66";
+ break;
+ case 0xBA :
+ return "\x67";
+ break;
+ case 0xBB :
+ return "\x68";
+ break;
+ case 0xBC :
+ return "\x69";
+ break;
+ case 0xBD :
+ return "\x6A";
+ break;
+ case 0xBE :
+ return "\x6B";
+ break;
+ case 0xBF :
+ return "\x6C";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\x6D";
+ break;
+ case 0x81 :
+ return "\x6E";
+ break;
+ case 0x82 :
+ return "\x6F";
+ break;
+ case 0x83 :
+ return "\x70";
+ break;
+ case 0x84 :
+ return "\x71";
+ break;
+ case 0x85 :
+ return "\x72";
+ break;
+ case 0x86 :
+ return "\x73";
+ break;
+ case 0x87 :
+ return "\x74";
+ break;
+ case 0x88 :
+ return "\x75";
+ break;
+ case 0x89 :
+ return "\x76";
+ break;
+ case 0x8A :
+ return "\x77";
+ break;
+ case 0x8B :
+ return "\x78";
+ break;
+ case 0x8C :
+ return "\x79";
+ break;
+ case 0x8D :
+ return "\x7A";
+ break;
+ case 0x8E :
+ return "\x61";
+ break;
+ case 0x8F :
+ return "\x62";
+ break;
+ case 0x90 :
+ return "\x63";
+ break;
+ case 0x91 :
+ return "\x64";
+ break;
+ case 0x92 :
+ return "\x65";
+ break;
+ case 0x93 :
+ return "\x66";
+ break;
+ case 0x94 :
+ return "\x67";
+ break;
+ case 0x96 :
+ return "\x69";
+ break;
+ case 0x97 :
+ return "\x6A";
+ break;
+ case 0x98 :
+ return "\x6B";
+ break;
+ case 0x99 :
+ return "\x6C";
+ break;
+ case 0x9A :
+ return "\x6D";
+ break;
+ case 0x9B :
+ return "\x6E";
+ break;
+ case 0x9C :
+ return "\x6F";
+ break;
+ case 0x9D :
+ return "\x70";
+ break;
+ case 0x9E :
+ return "\x71";
+ break;
+ case 0x9F :
+ return "\x72";
+ break;
+ case 0xA0 :
+ return "\x73";
+ break;
+ case 0xA1 :
+ return "\x74";
+ break;
+ case 0xA2 :
+ return "\x75";
+ break;
+ case 0xA3 :
+ return "\x76";
+ break;
+ case 0xA4 :
+ return "\x77";
+ break;
+ case 0xA5 :
+ return "\x78";
+ break;
+ case 0xA6 :
+ return "\x79";
+ break;
+ case 0xA7 :
+ return "\x7A";
+ break;
+ case 0xA8 :
+ return "\x61";
+ break;
+ case 0xA9 :
+ return "\x62";
+ break;
+ case 0xAA :
+ return "\x63";
+ break;
+ case 0xAB :
+ return "\x64";
+ break;
+ case 0xAC :
+ return "\x65";
+ break;
+ case 0xAD :
+ return "\x66";
+ break;
+ case 0xAE :
+ return "\x67";
+ break;
+ case 0xAF :
+ return "\x68";
+ break;
+ case 0xB0 :
+ return "\x69";
+ break;
+ case 0xB1 :
+ return "\x6A";
+ break;
+ case 0xB2 :
+ return "\x6B";
+ break;
+ case 0xB3 :
+ return "\x6C";
+ break;
+ case 0xB4 :
+ return "\x6D";
+ break;
+ case 0xB5 :
+ return "\x6E";
+ break;
+ case 0xB6 :
+ return "\x6F";
+ break;
+ case 0xB7 :
+ return "\x70";
+ break;
+ case 0xB8 :
+ return "\x71";
+ break;
+ case 0xB9 :
+ return "\x72";
+ break;
+ case 0xBA :
+ return "\x73";
+ break;
+ case 0xBB :
+ return "\x74";
+ break;
+ case 0xBC :
+ return "\x75";
+ break;
+ case 0xBD :
+ return "\x76";
+ break;
+ case 0xBE :
+ return "\x77";
+ break;
+ case 0xBF :
+ return "\x78";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\x79";
+ break;
+ case 0x81 :
+ return "\x7A";
+ break;
+ case 0x82 :
+ return "\x61";
+ break;
+ case 0x83 :
+ return "\x62";
+ break;
+ case 0x84 :
+ return "\x63";
+ break;
+ case 0x85 :
+ return "\x64";
+ break;
+ case 0x86 :
+ return "\x65";
+ break;
+ case 0x87 :
+ return "\x66";
+ break;
+ case 0x88 :
+ return "\x67";
+ break;
+ case 0x89 :
+ return "\x68";
+ break;
+ case 0x8A :
+ return "\x69";
+ break;
+ case 0x8B :
+ return "\x6A";
+ break;
+ case 0x8C :
+ return "\x6B";
+ break;
+ case 0x8D :
+ return "\x6C";
+ break;
+ case 0x8E :
+ return "\x6D";
+ break;
+ case 0x8F :
+ return "\x6E";
+ break;
+ case 0x90 :
+ return "\x6F";
+ break;
+ case 0x91 :
+ return "\x70";
+ break;
+ case 0x92 :
+ return "\x71";
+ break;
+ case 0x93 :
+ return "\x72";
+ break;
+ case 0x94 :
+ return "\x73";
+ break;
+ case 0x95 :
+ return "\x74";
+ break;
+ case 0x96 :
+ return "\x75";
+ break;
+ case 0x97 :
+ return "\x76";
+ break;
+ case 0x98 :
+ return "\x77";
+ break;
+ case 0x99 :
+ return "\x78";
+ break;
+ case 0x9A :
+ return "\x79";
+ break;
+ case 0x9B :
+ return "\x7A";
+ break;
+ case 0x9C :
+ return "\x61";
+ break;
+ case 0x9E :
+ return "\x63";
+ break;
+ case 0x9F :
+ return "\x64";
+ break;
+ case 0xA2 :
+ return "\x67";
+ break;
+ case 0xA5 :
+ return "\x6A";
+ break;
+ case 0xA6 :
+ return "\x6B";
+ break;
+ case 0xA9 :
+ return "\x6E";
+ break;
+ case 0xAA :
+ return "\x6F";
+ break;
+ case 0xAB :
+ return "\x70";
+ break;
+ case 0xAC :
+ return "\x71";
+ break;
+ case 0xAE :
+ return "\x73";
+ break;
+ case 0xAF :
+ return "\x74";
+ break;
+ case 0xB0 :
+ return "\x75";
+ break;
+ case 0xB1 :
+ return "\x76";
+ break;
+ case 0xB2 :
+ return "\x77";
+ break;
+ case 0xB3 :
+ return "\x78";
+ break;
+ case 0xB4 :
+ return "\x79";
+ break;
+ case 0xB5 :
+ return "\x7A";
+ break;
+ case 0xB6 :
+ return "\x61";
+ break;
+ case 0xB7 :
+ return "\x62";
+ break;
+ case 0xB8 :
+ return "\x63";
+ break;
+ case 0xB9 :
+ return "\x64";
+ break;
+ case 0xBB :
+ return "\x66";
+ break;
+ case 0xBD :
+ return "\x68";
+ break;
+ case 0xBE :
+ return "\x69";
+ break;
+ case 0xBF :
+ return "\x6A";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\x6B";
+ break;
+ case 0x81 :
+ return "\x6C";
+ break;
+ case 0x82 :
+ return "\x6D";
+ break;
+ case 0x83 :
+ return "\x6E";
+ break;
+ case 0x85 :
+ return "\x70";
+ break;
+ case 0x86 :
+ return "\x71";
+ break;
+ case 0x87 :
+ return "\x72";
+ break;
+ case 0x88 :
+ return "\x73";
+ break;
+ case 0x89 :
+ return "\x74";
+ break;
+ case 0x8A :
+ return "\x75";
+ break;
+ case 0x8B :
+ return "\x76";
+ break;
+ case 0x8C :
+ return "\x77";
+ break;
+ case 0x8D :
+ return "\x78";
+ break;
+ case 0x8E :
+ return "\x79";
+ break;
+ case 0x8F :
+ return "\x7A";
+ break;
+ case 0x90 :
+ return "\x61";
+ break;
+ case 0x91 :
+ return "\x62";
+ break;
+ case 0x92 :
+ return "\x63";
+ break;
+ case 0x93 :
+ return "\x64";
+ break;
+ case 0x94 :
+ return "\x65";
+ break;
+ case 0x95 :
+ return "\x66";
+ break;
+ case 0x96 :
+ return "\x67";
+ break;
+ case 0x97 :
+ return "\x68";
+ break;
+ case 0x98 :
+ return "\x69";
+ break;
+ case 0x99 :
+ return "\x6A";
+ break;
+ case 0x9A :
+ return "\x6B";
+ break;
+ case 0x9B :
+ return "\x6C";
+ break;
+ case 0x9C :
+ return "\x6D";
+ break;
+ case 0x9D :
+ return "\x6E";
+ break;
+ case 0x9E :
+ return "\x6F";
+ break;
+ case 0x9F :
+ return "\x70";
+ break;
+ case 0xA0 :
+ return "\x71";
+ break;
+ case 0xA1 :
+ return "\x72";
+ break;
+ case 0xA2 :
+ return "\x73";
+ break;
+ case 0xA3 :
+ return "\x74";
+ break;
+ case 0xA4 :
+ return "\x75";
+ break;
+ case 0xA5 :
+ return "\x76";
+ break;
+ case 0xA6 :
+ return "\x77";
+ break;
+ case 0xA7 :
+ return "\x78";
+ break;
+ case 0xA8 :
+ return "\x79";
+ break;
+ case 0xA9 :
+ return "\x7A";
+ break;
+ case 0xAA :
+ return "\x61";
+ break;
+ case 0xAB :
+ return "\x62";
+ break;
+ case 0xAC :
+ return "\x63";
+ break;
+ case 0xAD :
+ return "\x64";
+ break;
+ case 0xAE :
+ return "\x65";
+ break;
+ case 0xAF :
+ return "\x66";
+ break;
+ case 0xB0 :
+ return "\x67";
+ break;
+ case 0xB1 :
+ return "\x68";
+ break;
+ case 0xB2 :
+ return "\x69";
+ break;
+ case 0xB3 :
+ return "\x6A";
+ break;
+ case 0xB4 :
+ return "\x6B";
+ break;
+ case 0xB5 :
+ return "\x6C";
+ break;
+ case 0xB6 :
+ return "\x6D";
+ break;
+ case 0xB7 :
+ return "\x6E";
+ break;
+ case 0xB8 :
+ return "\x6F";
+ break;
+ case 0xB9 :
+ return "\x70";
+ break;
+ case 0xBA :
+ return "\x71";
+ break;
+ case 0xBB :
+ return "\x72";
+ break;
+ case 0xBC :
+ return "\x73";
+ break;
+ case 0xBD :
+ return "\x74";
+ break;
+ case 0xBE :
+ return "\x75";
+ break;
+ case 0xBF :
+ return "\x76";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\x77";
+ break;
+ case 0x81 :
+ return "\x78";
+ break;
+ case 0x82 :
+ return "\x79";
+ break;
+ case 0x83 :
+ return "\x7A";
+ break;
+ case 0x84 :
+ return "\x61";
+ break;
+ case 0x85 :
+ return "\x62";
+ break;
+ case 0x87 :
+ return "\x64";
+ break;
+ case 0x88 :
+ return "\x65";
+ break;
+ case 0x89 :
+ return "\x66";
+ break;
+ case 0x8A :
+ return "\x67";
+ break;
+ case 0x8D :
+ return "\x6A";
+ break;
+ case 0x8E :
+ return "\x6B";
+ break;
+ case 0x8F :
+ return "\x6C";
+ break;
+ case 0x90 :
+ return "\x6D";
+ break;
+ case 0x91 :
+ return "\x6E";
+ break;
+ case 0x92 :
+ return "\x6F";
+ break;
+ case 0x93 :
+ return "\x70";
+ break;
+ case 0x94 :
+ return "\x71";
+ break;
+ case 0x96 :
+ return "\x73";
+ break;
+ case 0x97 :
+ return "\x74";
+ break;
+ case 0x98 :
+ return "\x75";
+ break;
+ case 0x99 :
+ return "\x76";
+ break;
+ case 0x9A :
+ return "\x77";
+ break;
+ case 0x9B :
+ return "\x78";
+ break;
+ case 0x9C :
+ return "\x79";
+ break;
+ case 0x9E :
+ return "\x61";
+ break;
+ case 0x9F :
+ return "\x62";
+ break;
+ case 0xA0 :
+ return "\x63";
+ break;
+ case 0xA1 :
+ return "\x64";
+ break;
+ case 0xA2 :
+ return "\x65";
+ break;
+ case 0xA3 :
+ return "\x66";
+ break;
+ case 0xA4 :
+ return "\x67";
+ break;
+ case 0xA5 :
+ return "\x68";
+ break;
+ case 0xA6 :
+ return "\x69";
+ break;
+ case 0xA7 :
+ return "\x6A";
+ break;
+ case 0xA8 :
+ return "\x6B";
+ break;
+ case 0xA9 :
+ return "\x6C";
+ break;
+ case 0xAA :
+ return "\x6D";
+ break;
+ case 0xAB :
+ return "\x6E";
+ break;
+ case 0xAC :
+ return "\x6F";
+ break;
+ case 0xAD :
+ return "\x70";
+ break;
+ case 0xAE :
+ return "\x71";
+ break;
+ case 0xAF :
+ return "\x72";
+ break;
+ case 0xB0 :
+ return "\x73";
+ break;
+ case 0xB1 :
+ return "\x74";
+ break;
+ case 0xB2 :
+ return "\x75";
+ break;
+ case 0xB3 :
+ return "\x76";
+ break;
+ case 0xB4 :
+ return "\x77";
+ break;
+ case 0xB5 :
+ return "\x78";
+ break;
+ case 0xB6 :
+ return "\x79";
+ break;
+ case 0xB7 :
+ return "\x7A";
+ break;
+ case 0xB8 :
+ return "\x61";
+ break;
+ case 0xB9 :
+ return "\x62";
+ break;
+ case 0xBB :
+ return "\x64";
+ break;
+ case 0xBC :
+ return "\x65";
+ break;
+ case 0xBD :
+ return "\x66";
+ break;
+ case 0xBE :
+ return "\x67";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\x69";
+ break;
+ case 0x81 :
+ return "\x6A";
+ break;
+ case 0x82 :
+ return "\x6B";
+ break;
+ case 0x83 :
+ return "\x6C";
+ break;
+ case 0x84 :
+ return "\x6D";
+ break;
+ case 0x86 :
+ return "\x6F";
+ break;
+ case 0x8A :
+ return "\x73";
+ break;
+ case 0x8B :
+ return "\x74";
+ break;
+ case 0x8C :
+ return "\x75";
+ break;
+ case 0x8D :
+ return "\x76";
+ break;
+ case 0x8E :
+ return "\x77";
+ break;
+ case 0x8F :
+ return "\x78";
+ break;
+ case 0x90 :
+ return "\x79";
+ break;
+ case 0x92 :
+ return "\x61";
+ break;
+ case 0x93 :
+ return "\x62";
+ break;
+ case 0x94 :
+ return "\x63";
+ break;
+ case 0x95 :
+ return "\x64";
+ break;
+ case 0x96 :
+ return "\x65";
+ break;
+ case 0x97 :
+ return "\x66";
+ break;
+ case 0x98 :
+ return "\x67";
+ break;
+ case 0x99 :
+ return "\x68";
+ break;
+ case 0x9A :
+ return "\x69";
+ break;
+ case 0x9B :
+ return "\x6A";
+ break;
+ case 0x9C :
+ return "\x6B";
+ break;
+ case 0x9D :
+ return "\x6C";
+ break;
+ case 0x9E :
+ return "\x6D";
+ break;
+ case 0x9F :
+ return "\x6E";
+ break;
+ case 0xA0 :
+ return "\x6F";
+ break;
+ case 0xA1 :
+ return "\x70";
+ break;
+ case 0xA2 :
+ return "\x71";
+ break;
+ case 0xA3 :
+ return "\x72";
+ break;
+ case 0xA4 :
+ return "\x73";
+ break;
+ case 0xA5 :
+ return "\x74";
+ break;
+ case 0xA6 :
+ return "\x75";
+ break;
+ case 0xA7 :
+ return "\x76";
+ break;
+ case 0xA8 :
+ return "\x77";
+ break;
+ case 0xA9 :
+ return "\x78";
+ break;
+ case 0xAA :
+ return "\x79";
+ break;
+ case 0xAB :
+ return "\x7A";
+ break;
+ case 0xAC :
+ return "\x61";
+ break;
+ case 0xAD :
+ return "\x62";
+ break;
+ case 0xAE :
+ return "\x63";
+ break;
+ case 0xAF :
+ return "\x64";
+ break;
+ case 0xB0 :
+ return "\x65";
+ break;
+ case 0xB1 :
+ return "\x66";
+ break;
+ case 0xB2 :
+ return "\x67";
+ break;
+ case 0xB3 :
+ return "\x68";
+ break;
+ case 0xB4 :
+ return "\x69";
+ break;
+ case 0xB5 :
+ return "\x6A";
+ break;
+ case 0xB6 :
+ return "\x6B";
+ break;
+ case 0xB7 :
+ return "\x6C";
+ break;
+ case 0xB8 :
+ return "\x6D";
+ break;
+ case 0xB9 :
+ return "\x6E";
+ break;
+ case 0xBA :
+ return "\x6F";
+ break;
+ case 0xBB :
+ return "\x70";
+ break;
+ case 0xBC :
+ return "\x71";
+ break;
+ case 0xBD :
+ return "\x72";
+ break;
+ case 0xBE :
+ return "\x73";
+ break;
+ case 0xBF :
+ return "\x74";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\x75";
+ break;
+ case 0x81 :
+ return "\x76";
+ break;
+ case 0x82 :
+ return "\x77";
+ break;
+ case 0x83 :
+ return "\x78";
+ break;
+ case 0x84 :
+ return "\x79";
+ break;
+ case 0x85 :
+ return "\x7A";
+ break;
+ case 0x86 :
+ return "\x61";
+ break;
+ case 0x87 :
+ return "\x62";
+ break;
+ case 0x88 :
+ return "\x63";
+ break;
+ case 0x89 :
+ return "\x64";
+ break;
+ case 0x8A :
+ return "\x65";
+ break;
+ case 0x8B :
+ return "\x66";
+ break;
+ case 0x8C :
+ return "\x67";
+ break;
+ case 0x8D :
+ return "\x68";
+ break;
+ case 0x8E :
+ return "\x69";
+ break;
+ case 0x8F :
+ return "\x6A";
+ break;
+ case 0x90 :
+ return "\x6B";
+ break;
+ case 0x91 :
+ return "\x6C";
+ break;
+ case 0x92 :
+ return "\x6D";
+ break;
+ case 0x93 :
+ return "\x6E";
+ break;
+ case 0x94 :
+ return "\x6F";
+ break;
+ case 0x95 :
+ return "\x70";
+ break;
+ case 0x96 :
+ return "\x71";
+ break;
+ case 0x97 :
+ return "\x72";
+ break;
+ case 0x98 :
+ return "\x73";
+ break;
+ case 0x99 :
+ return "\x74";
+ break;
+ case 0x9A :
+ return "\x75";
+ break;
+ case 0x9B :
+ return "\x76";
+ break;
+ case 0x9C :
+ return "\x77";
+ break;
+ case 0x9D :
+ return "\x78";
+ break;
+ case 0x9E :
+ return "\x79";
+ break;
+ case 0x9F :
+ return "\x7A";
+ break;
+ case 0xA0 :
+ return "\x61";
+ break;
+ case 0xA1 :
+ return "\x62";
+ break;
+ case 0xA2 :
+ return "\x63";
+ break;
+ case 0xA3 :
+ return "\x64";
+ break;
+ case 0xA4 :
+ return "\x65";
+ break;
+ case 0xA5 :
+ return "\x66";
+ break;
+ case 0xA6 :
+ return "\x67";
+ break;
+ case 0xA7 :
+ return "\x68";
+ break;
+ case 0xA8 :
+ return "\x69";
+ break;
+ case 0xA9 :
+ return "\x6A";
+ break;
+ case 0xAA :
+ return "\x6B";
+ break;
+ case 0xAB :
+ return "\x6C";
+ break;
+ case 0xAC :
+ return "\x6D";
+ break;
+ case 0xAD :
+ return "\x6E";
+ break;
+ case 0xAE :
+ return "\x6F";
+ break;
+ case 0xAF :
+ return "\x70";
+ break;
+ case 0xB0 :
+ return "\x71";
+ break;
+ case 0xB1 :
+ return "\x72";
+ break;
+ case 0xB2 :
+ return "\x73";
+ break;
+ case 0xB3 :
+ return "\x74";
+ break;
+ case 0xB4 :
+ return "\x75";
+ break;
+ case 0xB5 :
+ return "\x76";
+ break;
+ case 0xB6 :
+ return "\x77";
+ break;
+ case 0xB7 :
+ return "\x78";
+ break;
+ case 0xB8 :
+ return "\x79";
+ break;
+ case 0xB9 :
+ return "\x7A";
+ break;
+ case 0xBA :
+ return "\x61";
+ break;
+ case 0xBB :
+ return "\x62";
+ break;
+ case 0xBC :
+ return "\x63";
+ break;
+ case 0xBD :
+ return "\x64";
+ break;
+ case 0xBE :
+ return "\x65";
+ break;
+ case 0xBF :
+ return "\x66";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\x67";
+ break;
+ case 0x81 :
+ return "\x68";
+ break;
+ case 0x82 :
+ return "\x69";
+ break;
+ case 0x83 :
+ return "\x6A";
+ break;
+ case 0x84 :
+ return "\x6B";
+ break;
+ case 0x85 :
+ return "\x6C";
+ break;
+ case 0x86 :
+ return "\x6D";
+ break;
+ case 0x87 :
+ return "\x6E";
+ break;
+ case 0x88 :
+ return "\x6F";
+ break;
+ case 0x89 :
+ return "\x70";
+ break;
+ case 0x8A :
+ return "\x71";
+ break;
+ case 0x8B :
+ return "\x72";
+ break;
+ case 0x8C :
+ return "\x73";
+ break;
+ case 0x8D :
+ return "\x74";
+ break;
+ case 0x8E :
+ return "\x75";
+ break;
+ case 0x8F :
+ return "\x76";
+ break;
+ case 0x90 :
+ return "\x77";
+ break;
+ case 0x91 :
+ return "\x78";
+ break;
+ case 0x92 :
+ return "\x79";
+ break;
+ case 0x93 :
+ return "\x7A";
+ break;
+ case 0x94 :
+ return "\x61";
+ break;
+ case 0x95 :
+ return "\x62";
+ break;
+ case 0x96 :
+ return "\x63";
+ break;
+ case 0x97 :
+ return "\x64";
+ break;
+ case 0x98 :
+ return "\x65";
+ break;
+ case 0x99 :
+ return "\x66";
+ break;
+ case 0x9A :
+ return "\x67";
+ break;
+ case 0x9B :
+ return "\x68";
+ break;
+ case 0x9C :
+ return "\x69";
+ break;
+ case 0x9D :
+ return "\x6A";
+ break;
+ case 0x9E :
+ return "\x6B";
+ break;
+ case 0x9F :
+ return "\x6C";
+ break;
+ case 0xA0 :
+ return "\x6D";
+ break;
+ case 0xA1 :
+ return "\x6E";
+ break;
+ case 0xA2 :
+ return "\x6F";
+ break;
+ case 0xA3 :
+ return "\x70";
+ break;
+ case 0xA4 :
+ return "\x71";
+ break;
+ case 0xA5 :
+ return "\x72";
+ break;
+ case 0xA6 :
+ return "\x73";
+ break;
+ case 0xA7 :
+ return "\x74";
+ break;
+ case 0xA8 :
+ return "\x75";
+ break;
+ case 0xA9 :
+ return "\x76";
+ break;
+ case 0xAA :
+ return "\x77";
+ break;
+ case 0xAB :
+ return "\x78";
+ break;
+ case 0xAC :
+ return "\x79";
+ break;
+ case 0xAD :
+ return "\x7A";
+ break;
+ case 0xAE :
+ return "\x61";
+ break;
+ case 0xAF :
+ return "\x62";
+ break;
+ case 0xB0 :
+ return "\x63";
+ break;
+ case 0xB1 :
+ return "\x64";
+ break;
+ case 0xB2 :
+ return "\x65";
+ break;
+ case 0xB3 :
+ return "\x66";
+ break;
+ case 0xB4 :
+ return "\x67";
+ break;
+ case 0xB5 :
+ return "\x68";
+ break;
+ case 0xB6 :
+ return "\x69";
+ break;
+ case 0xB7 :
+ return "\x6A";
+ break;
+ case 0xB8 :
+ return "\x6B";
+ break;
+ case 0xB9 :
+ return "\x6C";
+ break;
+ case 0xBA :
+ return "\x6D";
+ break;
+ case 0xBB :
+ return "\x6E";
+ break;
+ case 0xBC :
+ return "\x6F";
+ break;
+ case 0xBD :
+ return "\x70";
+ break;
+ case 0xBE :
+ return "\x71";
+ break;
+ case 0xBF :
+ return "\x72";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\x73";
+ break;
+ case 0x81 :
+ return "\x74";
+ break;
+ case 0x82 :
+ return "\x75";
+ break;
+ case 0x83 :
+ return "\x76";
+ break;
+ case 0x84 :
+ return "\x77";
+ break;
+ case 0x85 :
+ return "\x78";
+ break;
+ case 0x86 :
+ return "\x79";
+ break;
+ case 0x87 :
+ return "\x7A";
+ break;
+ case 0x88 :
+ return "\x61";
+ break;
+ case 0x89 :
+ return "\x62";
+ break;
+ case 0x8A :
+ return "\x63";
+ break;
+ case 0x8B :
+ return "\x64";
+ break;
+ case 0x8C :
+ return "\x65";
+ break;
+ case 0x8D :
+ return "\x66";
+ break;
+ case 0x8E :
+ return "\x67";
+ break;
+ case 0x8F :
+ return "\x68";
+ break;
+ case 0x90 :
+ return "\x69";
+ break;
+ case 0x91 :
+ return "\x6A";
+ break;
+ case 0x92 :
+ return "\x6B";
+ break;
+ case 0x93 :
+ return "\x6C";
+ break;
+ case 0x94 :
+ return "\x6D";
+ break;
+ case 0x95 :
+ return "\x6E";
+ break;
+ case 0x96 :
+ return "\x6F";
+ break;
+ case 0x97 :
+ return "\x70";
+ break;
+ case 0x98 :
+ return "\x71";
+ break;
+ case 0x99 :
+ return "\x72";
+ break;
+ case 0x9A :
+ return "\x73";
+ break;
+ case 0x9B :
+ return "\x74";
+ break;
+ case 0x9C :
+ return "\x75";
+ break;
+ case 0x9D :
+ return "\x76";
+ break;
+ case 0x9E :
+ return "\x77";
+ break;
+ case 0x9F :
+ return "\x78";
+ break;
+ case 0xA0 :
+ return "\x79";
+ break;
+ case 0xA1 :
+ return "\x7A";
+ break;
+ case 0xA2 :
+ return "\x61";
+ break;
+ case 0xA3 :
+ return "\x62";
+ break;
+ case 0xA4 :
+ return "\x63";
+ break;
+ case 0xA5 :
+ return "\x64";
+ break;
+ case 0xA6 :
+ return "\x65";
+ break;
+ case 0xA7 :
+ return "\x66";
+ break;
+ case 0xA8 :
+ return "\x67";
+ break;
+ case 0xA9 :
+ return "\x68";
+ break;
+ case 0xAA :
+ return "\x69";
+ break;
+ case 0xAB :
+ return "\x6A";
+ break;
+ case 0xAC :
+ return "\x6B";
+ break;
+ case 0xAD :
+ return "\x6C";
+ break;
+ case 0xAE :
+ return "\x6D";
+ break;
+ case 0xAF :
+ return "\x6E";
+ break;
+ case 0xB0 :
+ return "\x6F";
+ break;
+ case 0xB1 :
+ return "\x70";
+ break;
+ case 0xB2 :
+ return "\x71";
+ break;
+ case 0xB3 :
+ return "\x72";
+ break;
+ case 0xB4 :
+ return "\x73";
+ break;
+ case 0xB5 :
+ return "\x74";
+ break;
+ case 0xB6 :
+ return "\x75";
+ break;
+ case 0xB7 :
+ return "\x76";
+ break;
+ case 0xB8 :
+ return "\x77";
+ break;
+ case 0xB9 :
+ return "\x78";
+ break;
+ case 0xBA :
+ return "\x79";
+ break;
+ case 0xBB :
+ return "\x7A";
+ break;
+ case 0xBC :
+ return "\x61";
+ break;
+ case 0xBD :
+ return "\x62";
+ break;
+ case 0xBE :
+ return "\x63";
+ break;
+ case 0xBF :
+ return "\x64";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\x65";
+ break;
+ case 0x81 :
+ return "\x66";
+ break;
+ case 0x82 :
+ return "\x67";
+ break;
+ case 0x83 :
+ return "\x68";
+ break;
+ case 0x84 :
+ return "\x69";
+ break;
+ case 0x85 :
+ return "\x6A";
+ break;
+ case 0x86 :
+ return "\x6B";
+ break;
+ case 0x87 :
+ return "\x6C";
+ break;
+ case 0x88 :
+ return "\x6D";
+ break;
+ case 0x89 :
+ return "\x6E";
+ break;
+ case 0x8A :
+ return "\x6F";
+ break;
+ case 0x8B :
+ return "\x70";
+ break;
+ case 0x8C :
+ return "\x71";
+ break;
+ case 0x8D :
+ return "\x72";
+ break;
+ case 0x8E :
+ return "\x73";
+ break;
+ case 0x8F :
+ return "\x74";
+ break;
+ case 0x90 :
+ return "\x75";
+ break;
+ case 0x91 :
+ return "\x76";
+ break;
+ case 0x92 :
+ return "\x77";
+ break;
+ case 0x93 :
+ return "\x78";
+ break;
+ case 0x94 :
+ return "\x79";
+ break;
+ case 0x95 :
+ return "\x7A";
+ break;
+ case 0x96 :
+ return "\x61";
+ break;
+ case 0x97 :
+ return "\x62";
+ break;
+ case 0x98 :
+ return "\x63";
+ break;
+ case 0x99 :
+ return "\x64";
+ break;
+ case 0x9A :
+ return "\x65";
+ break;
+ case 0x9B :
+ return "\x66";
+ break;
+ case 0x9C :
+ return "\x67";
+ break;
+ case 0x9D :
+ return "\x68";
+ break;
+ case 0x9E :
+ return "\x69";
+ break;
+ case 0x9F :
+ return "\x6A";
+ break;
+ case 0xA0 :
+ return "\x6B";
+ break;
+ case 0xA1 :
+ return "\x6C";
+ break;
+ case 0xA2 :
+ return "\x6D";
+ break;
+ case 0xA3 :
+ return "\x6E";
+ break;
+ case 0xA4 :
+ return "\x6F";
+ break;
+ case 0xA5 :
+ return "\x70";
+ break;
+ case 0xA6 :
+ return "\x71";
+ break;
+ case 0xA7 :
+ return "\x72";
+ break;
+ case 0xA8 :
+ return "\x73";
+ break;
+ case 0xA9 :
+ return "\x74";
+ break;
+ case 0xAA :
+ return "\x75";
+ break;
+ case 0xAB :
+ return "\x76";
+ break;
+ case 0xAC :
+ return "\x77";
+ break;
+ case 0xAD :
+ return "\x78";
+ break;
+ case 0xAE :
+ return "\x79";
+ break;
+ case 0xAF :
+ return "\x7A";
+ break;
+ case 0xB0 :
+ return "\x61";
+ break;
+ case 0xB1 :
+ return "\x62";
+ break;
+ case 0xB2 :
+ return "\x63";
+ break;
+ case 0xB3 :
+ return "\x64";
+ break;
+ case 0xB4 :
+ return "\x65";
+ break;
+ case 0xB5 :
+ return "\x66";
+ break;
+ case 0xB6 :
+ return "\x67";
+ break;
+ case 0xB7 :
+ return "\x68";
+ break;
+ case 0xB8 :
+ return "\x69";
+ break;
+ case 0xB9 :
+ return "\x6A";
+ break;
+ case 0xBA :
+ return "\x6B";
+ break;
+ case 0xBB :
+ return "\x6C";
+ break;
+ case 0xBC :
+ return "\x6D";
+ break;
+ case 0xBD :
+ return "\x6E";
+ break;
+ case 0xBE :
+ return "\x6F";
+ break;
+ case 0xBF :
+ return "\x70";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (str[3]) {
+ case 0x80 :
+ return "\x71";
+ break;
+ case 0x81 :
+ return "\x72";
+ break;
+ case 0x82 :
+ return "\x73";
+ break;
+ case 0x83 :
+ return "\x74";
+ break;
+ case 0x84 :
+ return "\x75";
+ break;
+ case 0x85 :
+ return "\x76";
+ break;
+ case 0x86 :
+ return "\x77";
+ break;
+ case 0x87 :
+ return "\x78";
+ break;
+ case 0x88 :
+ return "\x79";
+ break;
+ case 0x89 :
+ return "\x7A";
+ break;
+ case 0x8A :
+ return "\x61";
+ break;
+ case 0x8B :
+ return "\x62";
+ break;
+ case 0x8C :
+ return "\x63";
+ break;
+ case 0x8D :
+ return "\x64";
+ break;
+ case 0x8E :
+ return "\x65";
+ break;
+ case 0x8F :
+ return "\x66";
+ break;
+ case 0x90 :
+ return "\x67";
+ break;
+ case 0x91 :
+ return "\x68";
+ break;
+ case 0x92 :
+ return "\x69";
+ break;
+ case 0x93 :
+ return "\x6A";
+ break;
+ case 0x94 :
+ return "\x6B";
+ break;
+ case 0x95 :
+ return "\x6C";
+ break;
+ case 0x96 :
+ return "\x6D";
+ break;
+ case 0x97 :
+ return "\x6E";
+ break;
+ case 0x98 :
+ return "\x6F";
+ break;
+ case 0x99 :
+ return "\x70";
+ break;
+ case 0x9A :
+ return "\x71";
+ break;
+ case 0x9B :
+ return "\x72";
+ break;
+ case 0x9C :
+ return "\x73";
+ break;
+ case 0x9D :
+ return "\x74";
+ break;
+ case 0x9E :
+ return "\x75";
+ break;
+ case 0x9F :
+ return "\x76";
+ break;
+ case 0xA0 :
+ return "\x77";
+ break;
+ case 0xA1 :
+ return "\x78";
+ break;
+ case 0xA2 :
+ return "\x79";
+ break;
+ case 0xA3 :
+ return "\x7A";
+ break;
+ case 0xA4 :
+ return "\xC4\xB1";
+ break;
+ case 0xA5 :
+ return "\xC8\xB7";
+ break;
+ case 0xA8 :
+ return "\xCE\x91";
+ break;
+ case 0xA9 :
+ return "\xCE\x92";
+ break;
+ case 0xAA :
+ return "\xCE\x93";
+ break;
+ case 0xAB :
+ return "\xCE\x94";
+ break;
+ case 0xAC :
+ return "\xCE\x95";
+ break;
+ case 0xAD :
+ return "\xCE\x96";
+ break;
+ case 0xAE :
+ return "\xCE\x97";
+ break;
+ case 0xAF :
+ return "\xCE\x98";
+ break;
+ case 0xB0 :
+ return "\xCE\x99";
+ break;
+ case 0xB1 :
+ return "\xCE\x9A";
+ break;
+ case 0xB2 :
+ return "\xCE\x9B";
+ break;
+ case 0xB3 :
+ return "\xCE\x9C";
+ break;
+ case 0xB4 :
+ return "\xCE\x9D";
+ break;
+ case 0xB5 :
+ return "\xCE\x9E";
+ break;
+ case 0xB6 :
+ return "\xCE\x9F";
+ break;
+ case 0xB7 :
+ return "\xCE\xA0";
+ break;
+ case 0xB8 :
+ return "\xCE\xA1";
+ break;
+ case 0xB9 :
+ return "\xCE\x98";
+ break;
+ case 0xBA :
+ return "\xCE\xA3";
+ break;
+ case 0xBB :
+ return "\xCE\xA4";
+ break;
+ case 0xBC :
+ return "\xCE\xA5";
+ break;
+ case 0xBD :
+ return "\xCE\xA6";
+ break;
+ case 0xBE :
+ return "\xCE\xA7";
+ break;
+ case 0xBF :
+ return "\xCE\xA8";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xCE\xA9";
+ break;
+ case 0x81 :
+ return "\xE2\x88\x87";
+ break;
+ case 0x82 :
+ return "\xCE\xB1";
+ break;
+ case 0x83 :
+ return "\xCE\xB2";
+ break;
+ case 0x84 :
+ return "\xCE\xB3";
+ break;
+ case 0x85 :
+ return "\xCE\xB4";
+ break;
+ case 0x86 :
+ return "\xCE\xB5";
+ break;
+ case 0x87 :
+ return "\xCE\xB6";
+ break;
+ case 0x88 :
+ return "\xCE\xB7";
+ break;
+ case 0x89 :
+ return "\xCE\xB8";
+ break;
+ case 0x8A :
+ return "\xCE\xB9";
+ break;
+ case 0x8B :
+ return "\xCE\xBA";
+ break;
+ case 0x8C :
+ return "\xCE\xBB";
+ break;
+ case 0x8D :
+ return "\xCE\xBC";
+ break;
+ case 0x8E :
+ return "\xCE\xBD";
+ break;
+ case 0x8F :
+ return "\xCE\xBE";
+ break;
+ case 0x90 :
+ return "\xCE\xBF";
+ break;
+ case 0x91 :
+ return "\xCF\x80";
+ break;
+ case 0x92 :
+ return "\xCF\x81";
+ break;
+ case 0x93 :
+ return "\xCF\x82";
+ break;
+ case 0x94 :
+ return "\xCF\x83";
+ break;
+ case 0x95 :
+ return "\xCF\x84";
+ break;
+ case 0x96 :
+ return "\xCF\x85";
+ break;
+ case 0x97 :
+ return "\xCF\x86";
+ break;
+ case 0x98 :
+ return "\xCF\x87";
+ break;
+ case 0x99 :
+ return "\xCF\x88";
+ break;
+ case 0x9A :
+ return "\xCF\x89";
+ break;
+ case 0x9B :
+ return "\xE2\x88\x82";
+ break;
+ case 0x9C :
+ return "\xCE\xB5";
+ break;
+ case 0x9D :
+ return "\xCE\xB8";
+ break;
+ case 0x9E :
+ return "\xCE\xBA";
+ break;
+ case 0x9F :
+ return "\xCF\x86";
+ break;
+ case 0xA0 :
+ return "\xCF\x81";
+ break;
+ case 0xA1 :
+ return "\xCF\x80";
+ break;
+ case 0xA2 :
+ return "\xCE\x91";
+ break;
+ case 0xA3 :
+ return "\xCE\x92";
+ break;
+ case 0xA4 :
+ return "\xCE\x93";
+ break;
+ case 0xA5 :
+ return "\xCE\x94";
+ break;
+ case 0xA6 :
+ return "\xCE\x95";
+ break;
+ case 0xA7 :
+ return "\xCE\x96";
+ break;
+ case 0xA8 :
+ return "\xCE\x97";
+ break;
+ case 0xA9 :
+ return "\xCE\x98";
+ break;
+ case 0xAA :
+ return "\xCE\x99";
+ break;
+ case 0xAB :
+ return "\xCE\x9A";
+ break;
+ case 0xAC :
+ return "\xCE\x9B";
+ break;
+ case 0xAD :
+ return "\xCE\x9C";
+ break;
+ case 0xAE :
+ return "\xCE\x9D";
+ break;
+ case 0xAF :
+ return "\xCE\x9E";
+ break;
+ case 0xB0 :
+ return "\xCE\x9F";
+ break;
+ case 0xB1 :
+ return "\xCE\xA0";
+ break;
+ case 0xB2 :
+ return "\xCE\xA1";
+ break;
+ case 0xB3 :
+ return "\xCE\x98";
+ break;
+ case 0xB4 :
+ return "\xCE\xA3";
+ break;
+ case 0xB5 :
+ return "\xCE\xA4";
+ break;
+ case 0xB6 :
+ return "\xCE\xA5";
+ break;
+ case 0xB7 :
+ return "\xCE\xA6";
+ break;
+ case 0xB8 :
+ return "\xCE\xA7";
+ break;
+ case 0xB9 :
+ return "\xCE\xA8";
+ break;
+ case 0xBA :
+ return "\xCE\xA9";
+ break;
+ case 0xBB :
+ return "\xE2\x88\x87";
+ break;
+ case 0xBC :
+ return "\xCE\xB1";
+ break;
+ case 0xBD :
+ return "\xCE\xB2";
+ break;
+ case 0xBE :
+ return "\xCE\xB3";
+ break;
+ case 0xBF :
+ return "\xCE\xB4";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xCE\xB5";
+ break;
+ case 0x81 :
+ return "\xCE\xB6";
+ break;
+ case 0x82 :
+ return "\xCE\xB7";
+ break;
+ case 0x83 :
+ return "\xCE\xB8";
+ break;
+ case 0x84 :
+ return "\xCE\xB9";
+ break;
+ case 0x85 :
+ return "\xCE\xBA";
+ break;
+ case 0x86 :
+ return "\xCE\xBB";
+ break;
+ case 0x87 :
+ return "\xCE\xBC";
+ break;
+ case 0x88 :
+ return "\xCE\xBD";
+ break;
+ case 0x89 :
+ return "\xCE\xBE";
+ break;
+ case 0x8A :
+ return "\xCE\xBF";
+ break;
+ case 0x8B :
+ return "\xCF\x80";
+ break;
+ case 0x8C :
+ return "\xCF\x81";
+ break;
+ case 0x8D :
+ return "\xCF\x82";
+ break;
+ case 0x8E :
+ return "\xCF\x83";
+ break;
+ case 0x8F :
+ return "\xCF\x84";
+ break;
+ case 0x90 :
+ return "\xCF\x85";
+ break;
+ case 0x91 :
+ return "\xCF\x86";
+ break;
+ case 0x92 :
+ return "\xCF\x87";
+ break;
+ case 0x93 :
+ return "\xCF\x88";
+ break;
+ case 0x94 :
+ return "\xCF\x89";
+ break;
+ case 0x95 :
+ return "\xE2\x88\x82";
+ break;
+ case 0x96 :
+ return "\xCE\xB5";
+ break;
+ case 0x97 :
+ return "\xCE\xB8";
+ break;
+ case 0x98 :
+ return "\xCE\xBA";
+ break;
+ case 0x99 :
+ return "\xCF\x86";
+ break;
+ case 0x9A :
+ return "\xCF\x81";
+ break;
+ case 0x9B :
+ return "\xCF\x80";
+ break;
+ case 0x9C :
+ return "\xCE\x91";
+ break;
+ case 0x9D :
+ return "\xCE\x92";
+ break;
+ case 0x9E :
+ return "\xCE\x93";
+ break;
+ case 0x9F :
+ return "\xCE\x94";
+ break;
+ case 0xA0 :
+ return "\xCE\x95";
+ break;
+ case 0xA1 :
+ return "\xCE\x96";
+ break;
+ case 0xA2 :
+ return "\xCE\x97";
+ break;
+ case 0xA3 :
+ return "\xCE\x98";
+ break;
+ case 0xA4 :
+ return "\xCE\x99";
+ break;
+ case 0xA5 :
+ return "\xCE\x9A";
+ break;
+ case 0xA6 :
+ return "\xCE\x9B";
+ break;
+ case 0xA7 :
+ return "\xCE\x9C";
+ break;
+ case 0xA8 :
+ return "\xCE\x9D";
+ break;
+ case 0xA9 :
+ return "\xCE\x9E";
+ break;
+ case 0xAA :
+ return "\xCE\x9F";
+ break;
+ case 0xAB :
+ return "\xCE\xA0";
+ break;
+ case 0xAC :
+ return "\xCE\xA1";
+ break;
+ case 0xAD :
+ return "\xCE\x98";
+ break;
+ case 0xAE :
+ return "\xCE\xA3";
+ break;
+ case 0xAF :
+ return "\xCE\xA4";
+ break;
+ case 0xB0 :
+ return "\xCE\xA5";
+ break;
+ case 0xB1 :
+ return "\xCE\xA6";
+ break;
+ case 0xB2 :
+ return "\xCE\xA7";
+ break;
+ case 0xB3 :
+ return "\xCE\xA8";
+ break;
+ case 0xB4 :
+ return "\xCE\xA9";
+ break;
+ case 0xB5 :
+ return "\xE2\x88\x87";
+ break;
+ case 0xB6 :
+ return "\xCE\xB1";
+ break;
+ case 0xB7 :
+ return "\xCE\xB2";
+ break;
+ case 0xB8 :
+ return "\xCE\xB3";
+ break;
+ case 0xB9 :
+ return "\xCE\xB4";
+ break;
+ case 0xBA :
+ return "\xCE\xB5";
+ break;
+ case 0xBB :
+ return "\xCE\xB6";
+ break;
+ case 0xBC :
+ return "\xCE\xB7";
+ break;
+ case 0xBD :
+ return "\xCE\xB8";
+ break;
+ case 0xBE :
+ return "\xCE\xB9";
+ break;
+ case 0xBF :
+ return "\xCE\xBA";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xCE\xBB";
+ break;
+ case 0x81 :
+ return "\xCE\xBC";
+ break;
+ case 0x82 :
+ return "\xCE\xBD";
+ break;
+ case 0x83 :
+ return "\xCE\xBE";
+ break;
+ case 0x84 :
+ return "\xCE\xBF";
+ break;
+ case 0x85 :
+ return "\xCF\x80";
+ break;
+ case 0x86 :
+ return "\xCF\x81";
+ break;
+ case 0x87 :
+ return "\xCF\x82";
+ break;
+ case 0x88 :
+ return "\xCF\x83";
+ break;
+ case 0x89 :
+ return "\xCF\x84";
+ break;
+ case 0x8A :
+ return "\xCF\x85";
+ break;
+ case 0x8B :
+ return "\xCF\x86";
+ break;
+ case 0x8C :
+ return "\xCF\x87";
+ break;
+ case 0x8D :
+ return "\xCF\x88";
+ break;
+ case 0x8E :
+ return "\xCF\x89";
+ break;
+ case 0x8F :
+ return "\xE2\x88\x82";
+ break;
+ case 0x90 :
+ return "\xCE\xB5";
+ break;
+ case 0x91 :
+ return "\xCE\xB8";
+ break;
+ case 0x92 :
+ return "\xCE\xBA";
+ break;
+ case 0x93 :
+ return "\xCF\x86";
+ break;
+ case 0x94 :
+ return "\xCF\x81";
+ break;
+ case 0x95 :
+ return "\xCF\x80";
+ break;
+ case 0x96 :
+ return "\xCE\x91";
+ break;
+ case 0x97 :
+ return "\xCE\x92";
+ break;
+ case 0x98 :
+ return "\xCE\x93";
+ break;
+ case 0x99 :
+ return "\xCE\x94";
+ break;
+ case 0x9A :
+ return "\xCE\x95";
+ break;
+ case 0x9B :
+ return "\xCE\x96";
+ break;
+ case 0x9C :
+ return "\xCE\x97";
+ break;
+ case 0x9D :
+ return "\xCE\x98";
+ break;
+ case 0x9E :
+ return "\xCE\x99";
+ break;
+ case 0x9F :
+ return "\xCE\x9A";
+ break;
+ case 0xA0 :
+ return "\xCE\x9B";
+ break;
+ case 0xA1 :
+ return "\xCE\x9C";
+ break;
+ case 0xA2 :
+ return "\xCE\x9D";
+ break;
+ case 0xA3 :
+ return "\xCE\x9E";
+ break;
+ case 0xA4 :
+ return "\xCE\x9F";
+ break;
+ case 0xA5 :
+ return "\xCE\xA0";
+ break;
+ case 0xA6 :
+ return "\xCE\xA1";
+ break;
+ case 0xA7 :
+ return "\xCE\x98";
+ break;
+ case 0xA8 :
+ return "\xCE\xA3";
+ break;
+ case 0xA9 :
+ return "\xCE\xA4";
+ break;
+ case 0xAA :
+ return "\xCE\xA5";
+ break;
+ case 0xAB :
+ return "\xCE\xA6";
+ break;
+ case 0xAC :
+ return "\xCE\xA7";
+ break;
+ case 0xAD :
+ return "\xCE\xA8";
+ break;
+ case 0xAE :
+ return "\xCE\xA9";
+ break;
+ case 0xAF :
+ return "\xE2\x88\x87";
+ break;
+ case 0xB0 :
+ return "\xCE\xB1";
+ break;
+ case 0xB1 :
+ return "\xCE\xB2";
+ break;
+ case 0xB2 :
+ return "\xCE\xB3";
+ break;
+ case 0xB3 :
+ return "\xCE\xB4";
+ break;
+ case 0xB4 :
+ return "\xCE\xB5";
+ break;
+ case 0xB5 :
+ return "\xCE\xB6";
+ break;
+ case 0xB6 :
+ return "\xCE\xB7";
+ break;
+ case 0xB7 :
+ return "\xCE\xB8";
+ break;
+ case 0xB8 :
+ return "\xCE\xB9";
+ break;
+ case 0xB9 :
+ return "\xCE\xBA";
+ break;
+ case 0xBA :
+ return "\xCE\xBB";
+ break;
+ case 0xBB :
+ return "\xCE\xBC";
+ break;
+ case 0xBC :
+ return "\xCE\xBD";
+ break;
+ case 0xBD :
+ return "\xCE\xBE";
+ break;
+ case 0xBE :
+ return "\xCE\xBF";
+ break;
+ case 0xBF :
+ return "\xCF\x80";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xCF\x81";
+ break;
+ case 0x81 :
+ return "\xCF\x82";
+ break;
+ case 0x82 :
+ return "\xCF\x83";
+ break;
+ case 0x83 :
+ return "\xCF\x84";
+ break;
+ case 0x84 :
+ return "\xCF\x85";
+ break;
+ case 0x85 :
+ return "\xCF\x86";
+ break;
+ case 0x86 :
+ return "\xCF\x87";
+ break;
+ case 0x87 :
+ return "\xCF\x88";
+ break;
+ case 0x88 :
+ return "\xCF\x89";
+ break;
+ case 0x89 :
+ return "\xE2\x88\x82";
+ break;
+ case 0x8A :
+ return "\xCE\xB5";
+ break;
+ case 0x8B :
+ return "\xCE\xB8";
+ break;
+ case 0x8C :
+ return "\xCE\xBA";
+ break;
+ case 0x8D :
+ return "\xCF\x86";
+ break;
+ case 0x8E :
+ return "\xCF\x81";
+ break;
+ case 0x8F :
+ return "\xCF\x80";
+ break;
+ case 0x90 :
+ return "\xCE\x91";
+ break;
+ case 0x91 :
+ return "\xCE\x92";
+ break;
+ case 0x92 :
+ return "\xCE\x93";
+ break;
+ case 0x93 :
+ return "\xCE\x94";
+ break;
+ case 0x94 :
+ return "\xCE\x95";
+ break;
+ case 0x95 :
+ return "\xCE\x96";
+ break;
+ case 0x96 :
+ return "\xCE\x97";
+ break;
+ case 0x97 :
+ return "\xCE\x98";
+ break;
+ case 0x98 :
+ return "\xCE\x99";
+ break;
+ case 0x99 :
+ return "\xCE\x9A";
+ break;
+ case 0x9A :
+ return "\xCE\x9B";
+ break;
+ case 0x9B :
+ return "\xCE\x9C";
+ break;
+ case 0x9C :
+ return "\xCE\x9D";
+ break;
+ case 0x9D :
+ return "\xCE\x9E";
+ break;
+ case 0x9E :
+ return "\xCE\x9F";
+ break;
+ case 0x9F :
+ return "\xCE\xA0";
+ break;
+ case 0xA0 :
+ return "\xCE\xA1";
+ break;
+ case 0xA1 :
+ return "\xCE\x98";
+ break;
+ case 0xA2 :
+ return "\xCE\xA3";
+ break;
+ case 0xA3 :
+ return "\xCE\xA4";
+ break;
+ case 0xA4 :
+ return "\xCE\xA5";
+ break;
+ case 0xA5 :
+ return "\xCE\xA6";
+ break;
+ case 0xA6 :
+ return "\xCE\xA7";
+ break;
+ case 0xA7 :
+ return "\xCE\xA8";
+ break;
+ case 0xA8 :
+ return "\xCE\xA9";
+ break;
+ case 0xA9 :
+ return "\xE2\x88\x87";
+ break;
+ case 0xAA :
+ return "\xCE\xB1";
+ break;
+ case 0xAB :
+ return "\xCE\xB2";
+ break;
+ case 0xAC :
+ return "\xCE\xB3";
+ break;
+ case 0xAD :
+ return "\xCE\xB4";
+ break;
+ case 0xAE :
+ return "\xCE\xB5";
+ break;
+ case 0xAF :
+ return "\xCE\xB6";
+ break;
+ case 0xB0 :
+ return "\xCE\xB7";
+ break;
+ case 0xB1 :
+ return "\xCE\xB8";
+ break;
+ case 0xB2 :
+ return "\xCE\xB9";
+ break;
+ case 0xB3 :
+ return "\xCE\xBA";
+ break;
+ case 0xB4 :
+ return "\xCE\xBB";
+ break;
+ case 0xB5 :
+ return "\xCE\xBC";
+ break;
+ case 0xB6 :
+ return "\xCE\xBD";
+ break;
+ case 0xB7 :
+ return "\xCE\xBE";
+ break;
+ case 0xB8 :
+ return "\xCE\xBF";
+ break;
+ case 0xB9 :
+ return "\xCF\x80";
+ break;
+ case 0xBA :
+ return "\xCF\x81";
+ break;
+ case 0xBB :
+ return "\xCF\x82";
+ break;
+ case 0xBC :
+ return "\xCF\x83";
+ break;
+ case 0xBD :
+ return "\xCF\x84";
+ break;
+ case 0xBE :
+ return "\xCF\x85";
+ break;
+ case 0xBF :
+ return "\xCF\x86";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xCF\x87";
+ break;
+ case 0x81 :
+ return "\xCF\x88";
+ break;
+ case 0x82 :
+ return "\xCF\x89";
+ break;
+ case 0x83 :
+ return "\xE2\x88\x82";
+ break;
+ case 0x84 :
+ return "\xCE\xB5";
+ break;
+ case 0x85 :
+ return "\xCE\xB8";
+ break;
+ case 0x86 :
+ return "\xCE\xBA";
+ break;
+ case 0x87 :
+ return "\xCF\x86";
+ break;
+ case 0x88 :
+ return "\xCF\x81";
+ break;
+ case 0x89 :
+ return "\xCF\x80";
+ break;
+ case 0x8A :
+ return "\xCF\x9C";
+ break;
+ case 0x8B :
+ return "\xCF\x9D";
+ break;
+ case 0x8E :
+ return "\x30";
+ break;
+ case 0x8F :
+ return "\x31";
+ break;
+ case 0x90 :
+ return "\x32";
+ break;
+ case 0x91 :
+ return "\x33";
+ break;
+ case 0x92 :
+ return "\x34";
+ break;
+ case 0x93 :
+ return "\x35";
+ break;
+ case 0x94 :
+ return "\x36";
+ break;
+ case 0x95 :
+ return "\x37";
+ break;
+ case 0x96 :
+ return "\x38";
+ break;
+ case 0x97 :
+ return "\x39";
+ break;
+ case 0x98 :
+ return "\x30";
+ break;
+ case 0x99 :
+ return "\x31";
+ break;
+ case 0x9A :
+ return "\x32";
+ break;
+ case 0x9B :
+ return "\x33";
+ break;
+ case 0x9C :
+ return "\x34";
+ break;
+ case 0x9D :
+ return "\x35";
+ break;
+ case 0x9E :
+ return "\x36";
+ break;
+ case 0x9F :
+ return "\x37";
+ break;
+ case 0xA0 :
+ return "\x38";
+ break;
+ case 0xA1 :
+ return "\x39";
+ break;
+ case 0xA2 :
+ return "\x30";
+ break;
+ case 0xA3 :
+ return "\x31";
+ break;
+ case 0xA4 :
+ return "\x32";
+ break;
+ case 0xA5 :
+ return "\x33";
+ break;
+ case 0xA6 :
+ return "\x34";
+ break;
+ case 0xA7 :
+ return "\x35";
+ break;
+ case 0xA8 :
+ return "\x36";
+ break;
+ case 0xA9 :
+ return "\x37";
+ break;
+ case 0xAA :
+ return "\x38";
+ break;
+ case 0xAB :
+ return "\x39";
+ break;
+ case 0xAC :
+ return "\x30";
+ break;
+ case 0xAD :
+ return "\x31";
+ break;
+ case 0xAE :
+ return "\x32";
+ break;
+ case 0xAF :
+ return "\x33";
+ break;
+ case 0xB0 :
+ return "\x34";
+ break;
+ case 0xB1 :
+ return "\x35";
+ break;
+ case 0xB2 :
+ return "\x36";
+ break;
+ case 0xB3 :
+ return "\x37";
+ break;
+ case 0xB4 :
+ return "\x38";
+ break;
+ case 0xB5 :
+ return "\x39";
+ break;
+ case 0xB6 :
+ return "\x30";
+ break;
+ case 0xB7 :
+ return "\x31";
+ break;
+ case 0xB8 :
+ return "\x32";
+ break;
+ case 0xB9 :
+ return "\x33";
+ break;
+ case 0xBA :
+ return "\x34";
+ break;
+ case 0xBB :
+ return "\x35";
+ break;
+ case 0xBC :
+ return "\x36";
+ break;
+ case 0xBD :
+ return "\x37";
+ break;
+ case 0xBE :
+ return "\x38";
+ break;
+ case 0xBF :
+ return "\x39";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (str[2]) {
+ case 0xA0 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xE4\xB8\xBD";
+ break;
+ case 0x81 :
+ return "\xE4\xB8\xB8";
+ break;
+ case 0x82 :
+ return "\xE4\xB9\x81";
+ break;
+ case 0x83 :
+ return "\xF0\xA0\x84\xA2";
+ break;
+ case 0x84 :
+ return "\xE4\xBD\xA0";
+ break;
+ case 0x85 :
+ return "\xE4\xBE\xAE";
+ break;
+ case 0x86 :
+ return "\xE4\xBE\xBB";
+ break;
+ case 0x87 :
+ return "\xE5\x80\x82";
+ break;
+ case 0x88 :
+ return "\xE5\x81\xBA";
+ break;
+ case 0x89 :
+ return "\xE5\x82\x99";
+ break;
+ case 0x8A :
+ return "\xE5\x83\xA7";
+ break;
+ case 0x8B :
+ return "\xE5\x83\x8F";
+ break;
+ case 0x8C :
+ return "\xE3\x92\x9E";
+ break;
+ case 0x8D :
+ return "\xF0\xA0\x98\xBA";
+ break;
+ case 0x8E :
+ return "\xE5\x85\x8D";
+ break;
+ case 0x8F :
+ return "\xE5\x85\x94";
+ break;
+ case 0x90 :
+ return "\xE5\x85\xA4";
+ break;
+ case 0x91 :
+ return "\xE5\x85\xB7";
+ break;
+ case 0x92 :
+ return "\xF0\xA0\x94\x9C";
+ break;
+ case 0x93 :
+ return "\xE3\x92\xB9";
+ break;
+ case 0x94 :
+ return "\xE5\x85\xA7";
+ break;
+ case 0x95 :
+ return "\xE5\x86\x8D";
+ break;
+ case 0x96 :
+ return "\xF0\xA0\x95\x8B";
+ break;
+ case 0x97 :
+ return "\xE5\x86\x97";
+ break;
+ case 0x98 :
+ return "\xE5\x86\xA4";
+ break;
+ case 0x99 :
+ return "\xE4\xBB\x8C";
+ break;
+ case 0x9A :
+ return "\xE5\x86\xAC";
+ break;
+ case 0x9B :
+ return "\xE5\x86\xB5";
+ break;
+ case 0x9C :
+ return "\xF0\xA9\x87\x9F";
+ break;
+ case 0x9D :
+ return "\xE5\x87\xB5";
+ break;
+ case 0x9E :
+ return "\xE5\x88\x83";
+ break;
+ case 0x9F :
+ return "\xE3\x93\x9F";
+ break;
+ case 0xA0 :
+ return "\xE5\x88\xBB";
+ break;
+ case 0xA1 :
+ return "\xE5\x89\x86";
+ break;
+ case 0xA2 :
+ return "\xE5\x89\xB2";
+ break;
+ case 0xA3 :
+ return "\xE5\x89\xB7";
+ break;
+ case 0xA4 :
+ return "\xE3\x94\x95";
+ break;
+ case 0xA5 :
+ return "\xE5\x8B\x87";
+ break;
+ case 0xA6 :
+ return "\xE5\x8B\x89";
+ break;
+ case 0xA7 :
+ return "\xE5\x8B\xA4";
+ break;
+ case 0xA8 :
+ return "\xE5\x8B\xBA";
+ break;
+ case 0xA9 :
+ return "\xE5\x8C\x85";
+ break;
+ case 0xAA :
+ return "\xE5\x8C\x86";
+ break;
+ case 0xAB :
+ return "\xE5\x8C\x97";
+ break;
+ case 0xAC :
+ return "\xE5\x8D\x89";
+ break;
+ case 0xAD :
+ return "\xE5\x8D\x91";
+ break;
+ case 0xAE :
+ return "\xE5\x8D\x9A";
+ break;
+ case 0xAF :
+ return "\xE5\x8D\xB3";
+ break;
+ case 0xB0 :
+ return "\xE5\x8D\xBD";
+ break;
+ case 0xB1 :
+ return "\xE5\x8D\xBF";
+ break;
+ case 0xB2 :
+ return "\xE5\x8D\xBF";
+ break;
+ case 0xB3 :
+ return "\xE5\x8D\xBF";
+ break;
+ case 0xB4 :
+ return "\xF0\xA0\xA8\xAC";
+ break;
+ case 0xB5 :
+ return "\xE7\x81\xB0";
+ break;
+ case 0xB6 :
+ return "\xE5\x8F\x8A";
+ break;
+ case 0xB7 :
+ return "\xE5\x8F\x9F";
+ break;
+ case 0xB8 :
+ return "\xF0\xA0\xAD\xA3";
+ break;
+ case 0xB9 :
+ return "\xE5\x8F\xAB";
+ break;
+ case 0xBA :
+ return "\xE5\x8F\xB1";
+ break;
+ case 0xBB :
+ return "\xE5\x90\x86";
+ break;
+ case 0xBC :
+ return "\xE5\x92\x9E";
+ break;
+ case 0xBD :
+ return "\xE5\x90\xB8";
+ break;
+ case 0xBE :
+ return "\xE5\x91\x88";
+ break;
+ case 0xBF :
+ return "\xE5\x91\xA8";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xE5\x92\xA2";
+ break;
+ case 0x81 :
+ return "\xE5\x93\xB6";
+ break;
+ case 0x82 :
+ return "\xE5\x94\x90";
+ break;
+ case 0x83 :
+ return "\xE5\x95\x93";
+ break;
+ case 0x84 :
+ return "\xE5\x95\xA3";
+ break;
+ case 0x85 :
+ return "\xE5\x96\x84";
+ break;
+ case 0x86 :
+ return "\xE5\x96\x84";
+ break;
+ case 0x87 :
+ return "\xE5\x96\x99";
+ break;
+ case 0x88 :
+ return "\xE5\x96\xAB";
+ break;
+ case 0x89 :
+ return "\xE5\x96\xB3";
+ break;
+ case 0x8A :
+ return "\xE5\x97\x82";
+ break;
+ case 0x8B :
+ return "\xE5\x9C\x96";
+ break;
+ case 0x8C :
+ return "\xE5\x98\x86";
+ break;
+ case 0x8D :
+ return "\xE5\x9C\x97";
+ break;
+ case 0x8E :
+ return "\xE5\x99\x91";
+ break;
+ case 0x8F :
+ return "\xE5\x99\xB4";
+ break;
+ case 0x90 :
+ return "\xE5\x88\x87";
+ break;
+ case 0x91 :
+ return "\xE5\xA3\xAE";
+ break;
+ case 0x92 :
+ return "\xE5\x9F\x8E";
+ break;
+ case 0x93 :
+ return "\xE5\x9F\xB4";
+ break;
+ case 0x94 :
+ return "\xE5\xA0\x8D";
+ break;
+ case 0x95 :
+ return "\xE5\x9E\x8B";
+ break;
+ case 0x96 :
+ return "\xE5\xA0\xB2";
+ break;
+ case 0x97 :
+ return "\xE5\xA0\xB1";
+ break;
+ case 0x98 :
+ return "\xE5\xA2\xAC";
+ break;
+ case 0x99 :
+ return "\xF0\xA1\x93\xA4";
+ break;
+ case 0x9A :
+ return "\xE5\xA3\xB2";
+ break;
+ case 0x9B :
+ return "\xE5\xA3\xB7";
+ break;
+ case 0x9C :
+ return "\xE5\xA4\x86";
+ break;
+ case 0x9D :
+ return "\xE5\xA4\x9A";
+ break;
+ case 0x9E :
+ return "\xE5\xA4\xA2";
+ break;
+ case 0x9F :
+ return "\xE5\xA5\xA2";
+ break;
+ case 0xA0 :
+ return "\xF0\xA1\x9A\xA8";
+ break;
+ case 0xA1 :
+ return "\xF0\xA1\x9B\xAA";
+ break;
+ case 0xA2 :
+ return "\xE5\xA7\xAC";
+ break;
+ case 0xA3 :
+ return "\xE5\xA8\x9B";
+ break;
+ case 0xA4 :
+ return "\xE5\xA8\xA7";
+ break;
+ case 0xA5 :
+ return "\xE5\xA7\x98";
+ break;
+ case 0xA6 :
+ return "\xE5\xA9\xA6";
+ break;
+ case 0xA7 :
+ return "\xE3\x9B\xAE";
+ break;
+ case 0xA8 :
+ return "\xE3\x9B\xBC";
+ break;
+ case 0xA9 :
+ return "\xE5\xAC\x88";
+ break;
+ case 0xAA :
+ return "\xE5\xAC\xBE";
+ break;
+ case 0xAB :
+ return "\xE5\xAC\xBE";
+ break;
+ case 0xAC :
+ return "\xF0\xA1\xA7\x88";
+ break;
+ case 0xAD :
+ return "\xE5\xAF\x83";
+ break;
+ case 0xAE :
+ return "\xE5\xAF\x98";
+ break;
+ case 0xAF :
+ return "\xE5\xAF\xA7";
+ break;
+ case 0xB0 :
+ return "\xE5\xAF\xB3";
+ break;
+ case 0xB1 :
+ return "\xF0\xA1\xAC\x98";
+ break;
+ case 0xB2 :
+ return "\xE5\xAF\xBF";
+ break;
+ case 0xB3 :
+ return "\xE5\xB0\x86";
+ break;
+ case 0xB4 :
+ return "\xE5\xBD\x93";
+ break;
+ case 0xB5 :
+ return "\xE5\xB0\xA2";
+ break;
+ case 0xB6 :
+ return "\xE3\x9E\x81";
+ break;
+ case 0xB7 :
+ return "\xE5\xB1\xA0";
+ break;
+ case 0xB8 :
+ return "\xE5\xB1\xAE";
+ break;
+ case 0xB9 :
+ return "\xE5\xB3\x80";
+ break;
+ case 0xBA :
+ return "\xE5\xB2\x8D";
+ break;
+ case 0xBB :
+ return "\xF0\xA1\xB7\xA4";
+ break;
+ case 0xBC :
+ return "\xE5\xB5\x83";
+ break;
+ case 0xBD :
+ return "\xF0\xA1\xB7\xA6";
+ break;
+ case 0xBE :
+ return "\xE5\xB5\xAE";
+ break;
+ case 0xBF :
+ return "\xE5\xB5\xAB";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xE5\xB5\xBC";
+ break;
+ case 0x81 :
+ return "\xE5\xB7\xA1";
+ break;
+ case 0x82 :
+ return "\xE5\xB7\xA2";
+ break;
+ case 0x83 :
+ return "\xE3\xA0\xAF";
+ break;
+ case 0x84 :
+ return "\xE5\xB7\xBD";
+ break;
+ case 0x85 :
+ return "\xE5\xB8\xA8";
+ break;
+ case 0x86 :
+ return "\xE5\xB8\xBD";
+ break;
+ case 0x87 :
+ return "\xE5\xB9\xA9";
+ break;
+ case 0x88 :
+ return "\xE3\xA1\xA2";
+ break;
+ case 0x89 :
+ return "\xF0\xA2\x86\x83";
+ break;
+ case 0x8A :
+ return "\xE3\xA1\xBC";
+ break;
+ case 0x8B :
+ return "\xE5\xBA\xB0";
+ break;
+ case 0x8C :
+ return "\xE5\xBA\xB3";
+ break;
+ case 0x8D :
+ return "\xE5\xBA\xB6";
+ break;
+ case 0x8E :
+ return "\xE5\xBB\x8A";
+ break;
+ case 0x8F :
+ return "\xF0\xAA\x8E\x92";
+ break;
+ case 0x90 :
+ return "\xE5\xBB\xBE";
+ break;
+ case 0x91 :
+ return "\xF0\xA2\x8C\xB1";
+ break;
+ case 0x92 :
+ return "\xF0\xA2\x8C\xB1";
+ break;
+ case 0x93 :
+ return "\xE8\x88\x81";
+ break;
+ case 0x94 :
+ return "\xE5\xBC\xA2";
+ break;
+ case 0x95 :
+ return "\xE5\xBC\xA2";
+ break;
+ case 0x96 :
+ return "\xE3\xA3\x87";
+ break;
+ case 0x97 :
+ return "\xF0\xA3\x8A\xB8";
+ break;
+ case 0x98 :
+ return "\xF0\xA6\x87\x9A";
+ break;
+ case 0x99 :
+ return "\xE5\xBD\xA2";
+ break;
+ case 0x9A :
+ return "\xE5\xBD\xAB";
+ break;
+ case 0x9B :
+ return "\xE3\xA3\xA3";
+ break;
+ case 0x9C :
+ return "\xE5\xBE\x9A";
+ break;
+ case 0x9D :
+ return "\xE5\xBF\x8D";
+ break;
+ case 0x9E :
+ return "\xE5\xBF\x97";
+ break;
+ case 0x9F :
+ return "\xE5\xBF\xB9";
+ break;
+ case 0xA0 :
+ return "\xE6\x82\x81";
+ break;
+ case 0xA1 :
+ return "\xE3\xA4\xBA";
+ break;
+ case 0xA2 :
+ return "\xE3\xA4\x9C";
+ break;
+ case 0xA3 :
+ return "\xE6\x82\x94";
+ break;
+ case 0xA4 :
+ return "\xF0\xA2\x9B\x94";
+ break;
+ case 0xA5 :
+ return "\xE6\x83\x87";
+ break;
+ case 0xA6 :
+ return "\xE6\x85\x88";
+ break;
+ case 0xA7 :
+ return "\xE6\x85\x8C";
+ break;
+ case 0xA8 :
+ return "\xE6\x85\x8E";
+ break;
+ case 0xA9 :
+ return "\xE6\x85\x8C";
+ break;
+ case 0xAA :
+ return "\xE6\x85\xBA";
+ break;
+ case 0xAB :
+ return "\xE6\x86\x8E";
+ break;
+ case 0xAC :
+ return "\xE6\x86\xB2";
+ break;
+ case 0xAD :
+ return "\xE6\x86\xA4";
+ break;
+ case 0xAE :
+ return "\xE6\x86\xAF";
+ break;
+ case 0xAF :
+ return "\xE6\x87\x9E";
+ break;
+ case 0xB0 :
+ return "\xE6\x87\xB2";
+ break;
+ case 0xB1 :
+ return "\xE6\x87\xB6";
+ break;
+ case 0xB2 :
+ return "\xE6\x88\x90";
+ break;
+ case 0xB3 :
+ return "\xE6\x88\x9B";
+ break;
+ case 0xB4 :
+ return "\xE6\x89\x9D";
+ break;
+ case 0xB5 :
+ return "\xE6\x8A\xB1";
+ break;
+ case 0xB6 :
+ return "\xE6\x8B\x94";
+ break;
+ case 0xB7 :
+ return "\xE6\x8D\x90";
+ break;
+ case 0xB8 :
+ return "\xF0\xA2\xAC\x8C";
+ break;
+ case 0xB9 :
+ return "\xE6\x8C\xBD";
+ break;
+ case 0xBA :
+ return "\xE6\x8B\xBC";
+ break;
+ case 0xBB :
+ return "\xE6\x8D\xA8";
+ break;
+ case 0xBC :
+ return "\xE6\x8E\x83";
+ break;
+ case 0xBD :
+ return "\xE6\x8F\xA4";
+ break;
+ case 0xBE :
+ return "\xF0\xA2\xAF\xB1";
+ break;
+ case 0xBF :
+ return "\xE6\x90\xA2";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xE6\x8F\x85";
+ break;
+ case 0x81 :
+ return "\xE6\x8E\xA9";
+ break;
+ case 0x82 :
+ return "\xE3\xA8\xAE";
+ break;
+ case 0x83 :
+ return "\xE6\x91\xA9";
+ break;
+ case 0x84 :
+ return "\xE6\x91\xBE";
+ break;
+ case 0x85 :
+ return "\xE6\x92\x9D";
+ break;
+ case 0x86 :
+ return "\xE6\x91\xB7";
+ break;
+ case 0x87 :
+ return "\xE3\xA9\xAC";
+ break;
+ case 0x88 :
+ return "\xE6\x95\x8F";
+ break;
+ case 0x89 :
+ return "\xE6\x95\xAC";
+ break;
+ case 0x8A :
+ return "\xF0\xA3\x80\x8A";
+ break;
+ case 0x8B :
+ return "\xE6\x97\xA3";
+ break;
+ case 0x8C :
+ return "\xE6\x9B\xB8";
+ break;
+ case 0x8D :
+ return "\xE6\x99\x89";
+ break;
+ case 0x8E :
+ return "\xE3\xAC\x99";
+ break;
+ case 0x8F :
+ return "\xE6\x9A\x91";
+ break;
+ case 0x90 :
+ return "\xE3\xAC\x88";
+ break;
+ case 0x91 :
+ return "\xE3\xAB\xA4";
+ break;
+ case 0x92 :
+ return "\xE5\x86\x92";
+ break;
+ case 0x93 :
+ return "\xE5\x86\x95";
+ break;
+ case 0x94 :
+ return "\xE6\x9C\x80";
+ break;
+ case 0x95 :
+ return "\xE6\x9A\x9C";
+ break;
+ case 0x96 :
+ return "\xE8\x82\xAD";
+ break;
+ case 0x97 :
+ return "\xE4\x8F\x99";
+ break;
+ case 0x98 :
+ return "\xE6\x9C\x97";
+ break;
+ case 0x99 :
+ return "\xE6\x9C\x9B";
+ break;
+ case 0x9A :
+ return "\xE6\x9C\xA1";
+ break;
+ case 0x9B :
+ return "\xE6\x9D\x9E";
+ break;
+ case 0x9C :
+ return "\xE6\x9D\x93";
+ break;
+ case 0x9D :
+ return "\xF0\xA3\x8F\x83";
+ break;
+ case 0x9E :
+ return "\xE3\xAD\x89";
+ break;
+ case 0x9F :
+ return "\xE6\x9F\xBA";
+ break;
+ case 0xA0 :
+ return "\xE6\x9E\x85";
+ break;
+ case 0xA1 :
+ return "\xE6\xA1\x92";
+ break;
+ case 0xA2 :
+ return "\xE6\xA2\x85";
+ break;
+ case 0xA3 :
+ return "\xF0\xA3\x91\xAD";
+ break;
+ case 0xA4 :
+ return "\xE6\xA2\x8E";
+ break;
+ case 0xA5 :
+ return "\xE6\xA0\x9F";
+ break;
+ case 0xA6 :
+ return "\xE6\xA4\x94";
+ break;
+ case 0xA7 :
+ return "\xE3\xAE\x9D";
+ break;
+ case 0xA8 :
+ return "\xE6\xA5\x82";
+ break;
+ case 0xA9 :
+ return "\xE6\xA6\xA3";
+ break;
+ case 0xAA :
+ return "\xE6\xA7\xAA";
+ break;
+ case 0xAB :
+ return "\xE6\xAA\xA8";
+ break;
+ case 0xAC :
+ return "\xF0\xA3\x9A\xA3";
+ break;
+ case 0xAD :
+ return "\xE6\xAB\x9B";
+ break;
+ case 0xAE :
+ return "\xE3\xB0\x98";
+ break;
+ case 0xAF :
+ return "\xE6\xAC\xA1";
+ break;
+ case 0xB0 :
+ return "\xF0\xA3\xA2\xA7";
+ break;
+ case 0xB1 :
+ return "\xE6\xAD\x94";
+ break;
+ case 0xB2 :
+ return "\xE3\xB1\x8E";
+ break;
+ case 0xB3 :
+ return "\xE6\xAD\xB2";
+ break;
+ case 0xB4 :
+ return "\xE6\xAE\x9F";
+ break;
+ case 0xB5 :
+ return "\xE6\xAE\xBA";
+ break;
+ case 0xB6 :
+ return "\xE6\xAE\xBB";
+ break;
+ case 0xB7 :
+ return "\xF0\xA3\xAA\x8D";
+ break;
+ case 0xB8 :
+ return "\xF0\xA1\xB4\x8B";
+ break;
+ case 0xB9 :
+ return "\xF0\xA3\xAB\xBA";
+ break;
+ case 0xBA :
+ return "\xE6\xB1\x8E";
+ break;
+ case 0xBB :
+ return "\xF0\xA3\xB2\xBC";
+ break;
+ case 0xBC :
+ return "\xE6\xB2\xBF";
+ break;
+ case 0xBD :
+ return "\xE6\xB3\x8D";
+ break;
+ case 0xBE :
+ return "\xE6\xB1\xA7";
+ break;
+ case 0xBF :
+ return "\xE6\xB4\x96";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xE6\xB4\xBE";
+ break;
+ case 0x81 :
+ return "\xE6\xB5\xB7";
+ break;
+ case 0x82 :
+ return "\xE6\xB5\x81";
+ break;
+ case 0x83 :
+ return "\xE6\xB5\xA9";
+ break;
+ case 0x84 :
+ return "\xE6\xB5\xB8";
+ break;
+ case 0x85 :
+ return "\xE6\xB6\x85";
+ break;
+ case 0x86 :
+ return "\xF0\xA3\xB4\x9E";
+ break;
+ case 0x87 :
+ return "\xE6\xB4\xB4";
+ break;
+ case 0x88 :
+ return "\xE6\xB8\xAF";
+ break;
+ case 0x89 :
+ return "\xE6\xB9\xAE";
+ break;
+ case 0x8A :
+ return "\xE3\xB4\xB3";
+ break;
+ case 0x8B :
+ return "\xE6\xBB\x8B";
+ break;
+ case 0x8C :
+ return "\xE6\xBB\x87";
+ break;
+ case 0x8D :
+ return "\xF0\xA3\xBB\x91";
+ break;
+ case 0x8E :
+ return "\xE6\xB7\xB9";
+ break;
+ case 0x8F :
+ return "\xE6\xBD\xAE";
+ break;
+ case 0x90 :
+ return "\xF0\xA3\xBD\x9E";
+ break;
+ case 0x91 :
+ return "\xF0\xA3\xBE\x8E";
+ break;
+ case 0x92 :
+ return "\xE6\xBF\x86";
+ break;
+ case 0x93 :
+ return "\xE7\x80\xB9";
+ break;
+ case 0x94 :
+ return "\xE7\x80\x9E";
+ break;
+ case 0x95 :
+ return "\xE7\x80\x9B";
+ break;
+ case 0x96 :
+ return "\xE3\xB6\x96";
+ break;
+ case 0x97 :
+ return "\xE7\x81\x8A";
+ break;
+ case 0x98 :
+ return "\xE7\x81\xBD";
+ break;
+ case 0x99 :
+ return "\xE7\x81\xB7";
+ break;
+ case 0x9A :
+ return "\xE7\x82\xAD";
+ break;
+ case 0x9B :
+ return "\xF0\xA0\x94\xA5";
+ break;
+ case 0x9C :
+ return "\xE7\x85\x85";
+ break;
+ case 0x9D :
+ return "\xF0\xA4\x89\xA3";
+ break;
+ case 0x9E :
+ return "\xE7\x86\x9C";
+ break;
+ case 0x9F :
+ return "\xF0\xA4\x8E\xAB";
+ break;
+ case 0xA0 :
+ return "\xE7\x88\xA8";
+ break;
+ case 0xA1 :
+ return "\xE7\x88\xB5";
+ break;
+ case 0xA2 :
+ return "\xE7\x89\x90";
+ break;
+ case 0xA3 :
+ return "\xF0\xA4\x98\x88";
+ break;
+ case 0xA4 :
+ return "\xE7\x8A\x80";
+ break;
+ case 0xA5 :
+ return "\xE7\x8A\x95";
+ break;
+ case 0xA6 :
+ return "\xF0\xA4\x9C\xB5";
+ break;
+ case 0xA7 :
+ return "\xF0\xA4\xA0\x94";
+ break;
+ case 0xA8 :
+ return "\xE7\x8D\xBA";
+ break;
+ case 0xA9 :
+ return "\xE7\x8E\x8B";
+ break;
+ case 0xAA :
+ return "\xE3\xBA\xAC";
+ break;
+ case 0xAB :
+ return "\xE7\x8E\xA5";
+ break;
+ case 0xAC :
+ return "\xE3\xBA\xB8";
+ break;
+ case 0xAD :
+ return "\xE3\xBA\xB8";
+ break;
+ case 0xAE :
+ return "\xE7\x91\x87";
+ break;
+ case 0xAF :
+ return "\xE7\x91\x9C";
+ break;
+ case 0xB0 :
+ return "\xE7\x91\xB1";
+ break;
+ case 0xB1 :
+ return "\xE7\x92\x85";
+ break;
+ case 0xB2 :
+ return "\xE7\x93\x8A";
+ break;
+ case 0xB3 :
+ return "\xE3\xBC\x9B";
+ break;
+ case 0xB4 :
+ return "\xE7\x94\xA4";
+ break;
+ case 0xB5 :
+ return "\xF0\xA4\xB0\xB6";
+ break;
+ case 0xB6 :
+ return "\xE7\x94\xBE";
+ break;
+ case 0xB7 :
+ return "\xF0\xA4\xB2\x92";
+ break;
+ case 0xB8 :
+ return "\xE7\x95\xB0";
+ break;
+ case 0xB9 :
+ return "\xF0\xA2\x86\x9F";
+ break;
+ case 0xBA :
+ return "\xE7\x98\x90";
+ break;
+ case 0xBB :
+ return "\xF0\xA4\xBE\xA1";
+ break;
+ case 0xBC :
+ return "\xF0\xA4\xBE\xB8";
+ break;
+ case 0xBD :
+ return "\xF0\xA5\x81\x84";
+ break;
+ case 0xBE :
+ return "\xE3\xBF\xBC";
+ break;
+ case 0xBF :
+ return "\xE4\x80\x88";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xE7\x9B\xB4";
+ break;
+ case 0x81 :
+ return "\xF0\xA5\x83\xB3";
+ break;
+ case 0x82 :
+ return "\xF0\xA5\x83\xB2";
+ break;
+ case 0x83 :
+ return "\xF0\xA5\x84\x99";
+ break;
+ case 0x84 :
+ return "\xF0\xA5\x84\xB3";
+ break;
+ case 0x85 :
+ return "\xE7\x9C\x9E";
+ break;
+ case 0x86 :
+ return "\xE7\x9C\x9F";
+ break;
+ case 0x87 :
+ return "\xE7\x9C\x9F";
+ break;
+ case 0x88 :
+ return "\xE7\x9D\x8A";
+ break;
+ case 0x89 :
+ return "\xE4\x80\xB9";
+ break;
+ case 0x8A :
+ return "\xE7\x9E\x8B";
+ break;
+ case 0x8B :
+ return "\xE4\x81\x86";
+ break;
+ case 0x8C :
+ return "\xE4\x82\x96";
+ break;
+ case 0x8D :
+ return "\xF0\xA5\x90\x9D";
+ break;
+ case 0x8E :
+ return "\xE7\xA1\x8E";
+ break;
+ case 0x8F :
+ return "\xE7\xA2\x8C";
+ break;
+ case 0x90 :
+ return "\xE7\xA3\x8C";
+ break;
+ case 0x91 :
+ return "\xE4\x83\xA3";
+ break;
+ case 0x92 :
+ return "\xF0\xA5\x98\xA6";
+ break;
+ case 0x93 :
+ return "\xE7\xA5\x96";
+ break;
+ case 0x94 :
+ return "\xF0\xA5\x9A\x9A";
+ break;
+ case 0x95 :
+ return "\xF0\xA5\x9B\x85";
+ break;
+ case 0x96 :
+ return "\xE7\xA6\x8F";
+ break;
+ case 0x97 :
+ return "\xE7\xA7\xAB";
+ break;
+ case 0x98 :
+ return "\xE4\x84\xAF";
+ break;
+ case 0x99 :
+ return "\xE7\xA9\x80";
+ break;
+ case 0x9A :
+ return "\xE7\xA9\x8A";
+ break;
+ case 0x9B :
+ return "\xE7\xA9\x8F";
+ break;
+ case 0x9C :
+ return "\xF0\xA5\xA5\xBC";
+ break;
+ case 0x9D :
+ return "\xF0\xA5\xAA\xA7";
+ break;
+ case 0x9E :
+ return "\xF0\xA5\xAA\xA7";
+ break;
+ case 0x9F :
+ return "\xE7\xAB\xAE";
+ break;
+ case 0xA0 :
+ return "\xE4\x88\x82";
+ break;
+ case 0xA1 :
+ return "\xF0\xA5\xAE\xAB";
+ break;
+ case 0xA2 :
+ return "\xE7\xAF\x86";
+ break;
+ case 0xA3 :
+ return "\xE7\xAF\x89";
+ break;
+ case 0xA4 :
+ return "\xE4\x88\xA7";
+ break;
+ case 0xA5 :
+ return "\xF0\xA5\xB2\x80";
+ break;
+ case 0xA6 :
+ return "\xE7\xB3\x92";
+ break;
+ case 0xA7 :
+ return "\xE4\x8A\xA0";
+ break;
+ case 0xA8 :
+ return "\xE7\xB3\xA8";
+ break;
+ case 0xA9 :
+ return "\xE7\xB3\xA3";
+ break;
+ case 0xAA :
+ return "\xE7\xB4\x80";
+ break;
+ case 0xAB :
+ return "\xF0\xA5\xBE\x86";
+ break;
+ case 0xAC :
+ return "\xE7\xB5\xA3";
+ break;
+ case 0xAD :
+ return "\xE4\x8C\x81";
+ break;
+ case 0xAE :
+ return "\xE7\xB7\x87";
+ break;
+ case 0xAF :
+ return "\xE7\xB8\x82";
+ break;
+ case 0xB0 :
+ return "\xE7\xB9\x85";
+ break;
+ case 0xB1 :
+ return "\xE4\x8C\xB4";
+ break;
+ case 0xB2 :
+ return "\xF0\xA6\x88\xA8";
+ break;
+ case 0xB3 :
+ return "\xF0\xA6\x89\x87";
+ break;
+ case 0xB4 :
+ return "\xE4\x8D\x99";
+ break;
+ case 0xB5 :
+ return "\xF0\xA6\x8B\x99";
+ break;
+ case 0xB6 :
+ return "\xE7\xBD\xBA";
+ break;
+ case 0xB7 :
+ return "\xF0\xA6\x8C\xBE";
+ break;
+ case 0xB8 :
+ return "\xE7\xBE\x95";
+ break;
+ case 0xB9 :
+ return "\xE7\xBF\xBA";
+ break;
+ case 0xBA :
+ return "\xE8\x80\x85";
+ break;
+ case 0xBB :
+ return "\xF0\xA6\x93\x9A";
+ break;
+ case 0xBC :
+ return "\xF0\xA6\x94\xA3";
+ break;
+ case 0xBD :
+ return "\xE8\x81\xA0";
+ break;
+ case 0xBE :
+ return "\xF0\xA6\x96\xA8";
+ break;
+ case 0xBF :
+ return "\xE8\x81\xB0";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xF0\xA3\x8D\x9F";
+ break;
+ case 0x81 :
+ return "\xE4\x8F\x95";
+ break;
+ case 0x82 :
+ return "\xE8\x82\xB2";
+ break;
+ case 0x83 :
+ return "\xE8\x84\x83";
+ break;
+ case 0x84 :
+ return "\xE4\x90\x8B";
+ break;
+ case 0x85 :
+ return "\xE8\x84\xBE";
+ break;
+ case 0x86 :
+ return "\xE5\xAA\xB5";
+ break;
+ case 0x87 :
+ return "\xF0\xA6\x9E\xA7";
+ break;
+ case 0x88 :
+ return "\xF0\xA6\x9E\xB5";
+ break;
+ case 0x89 :
+ return "\xF0\xA3\x8E\x93";
+ break;
+ case 0x8A :
+ return "\xF0\xA3\x8E\x9C";
+ break;
+ case 0x8B :
+ return "\xE8\x88\x81";
+ break;
+ case 0x8C :
+ return "\xE8\x88\x84";
+ break;
+ case 0x8D :
+ return "\xE8\xBE\x9E";
+ break;
+ case 0x8E :
+ return "\xE4\x91\xAB";
+ break;
+ case 0x8F :
+ return "\xE8\x8A\x91";
+ break;
+ case 0x90 :
+ return "\xE8\x8A\x8B";
+ break;
+ case 0x91 :
+ return "\xE8\x8A\x9D";
+ break;
+ case 0x92 :
+ return "\xE5\x8A\xB3";
+ break;
+ case 0x93 :
+ return "\xE8\x8A\xB1";
+ break;
+ case 0x94 :
+ return "\xE8\x8A\xB3";
+ break;
+ case 0x95 :
+ return "\xE8\x8A\xBD";
+ break;
+ case 0x96 :
+ return "\xE8\x8B\xA6";
+ break;
+ case 0x97 :
+ return "\xF0\xA6\xAC\xBC";
+ break;
+ case 0x98 :
+ return "\xE8\x8B\xA5";
+ break;
+ case 0x99 :
+ return "\xE8\x8C\x9D";
+ break;
+ case 0x9A :
+ return "\xE8\x8D\xA3";
+ break;
+ case 0x9B :
+ return "\xE8\x8E\xAD";
+ break;
+ case 0x9C :
+ return "\xE8\x8C\xA3";
+ break;
+ case 0x9D :
+ return "\xE8\x8E\xBD";
+ break;
+ case 0x9E :
+ return "\xE8\x8F\xA7";
+ break;
+ case 0x9F :
+ return "\xE8\x91\x97";
+ break;
+ case 0xA0 :
+ return "\xE8\x8D\x93";
+ break;
+ case 0xA1 :
+ return "\xE8\x8F\x8A";
+ break;
+ case 0xA2 :
+ return "\xE8\x8F\x8C";
+ break;
+ case 0xA3 :
+ return "\xE8\x8F\x9C";
+ break;
+ case 0xA4 :
+ return "\xF0\xA6\xB0\xB6";
+ break;
+ case 0xA5 :
+ return "\xF0\xA6\xB5\xAB";
+ break;
+ case 0xA6 :
+ return "\xF0\xA6\xB3\x95";
+ break;
+ case 0xA7 :
+ return "\xE4\x94\xAB";
+ break;
+ case 0xA8 :
+ return "\xE8\x93\xB1";
+ break;
+ case 0xA9 :
+ return "\xE8\x93\xB3";
+ break;
+ case 0xAA :
+ return "\xE8\x94\x96";
+ break;
+ case 0xAB :
+ return "\xF0\xA7\x8F\x8A";
+ break;
+ case 0xAC :
+ return "\xE8\x95\xA4";
+ break;
+ case 0xAD :
+ return "\xF0\xA6\xBC\xAC";
+ break;
+ case 0xAE :
+ return "\xE4\x95\x9D";
+ break;
+ case 0xAF :
+ return "\xE4\x95\xA1";
+ break;
+ case 0xB0 :
+ return "\xF0\xA6\xBE\xB1";
+ break;
+ case 0xB1 :
+ return "\xF0\xA7\x83\x92";
+ break;
+ case 0xB2 :
+ return "\xE4\x95\xAB";
+ break;
+ case 0xB3 :
+ return "\xE8\x99\x90";
+ break;
+ case 0xB4 :
+ return "\xE8\x99\x9C";
+ break;
+ case 0xB5 :
+ return "\xE8\x99\xA7";
+ break;
+ case 0xB6 :
+ return "\xE8\x99\xA9";
+ break;
+ case 0xB7 :
+ return "\xE8\x9A\xA9";
+ break;
+ case 0xB8 :
+ return "\xE8\x9A\x88";
+ break;
+ case 0xB9 :
+ return "\xE8\x9C\x8E";
+ break;
+ case 0xBA :
+ return "\xE8\x9B\xA2";
+ break;
+ case 0xBB :
+ return "\xE8\x9D\xB9";
+ break;
+ case 0xBC :
+ return "\xE8\x9C\xA8";
+ break;
+ case 0xBD :
+ return "\xE8\x9D\xAB";
+ break;
+ case 0xBE :
+ return "\xE8\x9E\x86";
+ break;
+ case 0xBF :
+ return "\xE4\x97\x97";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xE8\x9F\xA1";
+ break;
+ case 0x81 :
+ return "\xE8\xA0\x81";
+ break;
+ case 0x82 :
+ return "\xE4\x97\xB9";
+ break;
+ case 0x83 :
+ return "\xE8\xA1\xA0";
+ break;
+ case 0x84 :
+ return "\xE8\xA1\xA3";
+ break;
+ case 0x85 :
+ return "\xF0\xA7\x99\xA7";
+ break;
+ case 0x86 :
+ return "\xE8\xA3\x97";
+ break;
+ case 0x87 :
+ return "\xE8\xA3\x9E";
+ break;
+ case 0x88 :
+ return "\xE4\x98\xB5";
+ break;
+ case 0x89 :
+ return "\xE8\xA3\xBA";
+ break;
+ case 0x8A :
+ return "\xE3\x92\xBB";
+ break;
+ case 0x8B :
+ return "\xF0\xA7\xA2\xAE";
+ break;
+ case 0x8C :
+ return "\xF0\xA7\xA5\xA6";
+ break;
+ case 0x8D :
+ return "\xE4\x9A\xBE";
+ break;
+ case 0x8E :
+ return "\xE4\x9B\x87";
+ break;
+ case 0x8F :
+ return "\xE8\xAA\xA0";
+ break;
+ case 0x90 :
+ return "\xE8\xAB\xAD";
+ break;
+ case 0x91 :
+ return "\xE8\xAE\x8A";
+ break;
+ case 0x92 :
+ return "\xE8\xB1\x95";
+ break;
+ case 0x93 :
+ return "\xF0\xA7\xB2\xA8";
+ break;
+ case 0x94 :
+ return "\xE8\xB2\xAB";
+ break;
+ case 0x95 :
+ return "\xE8\xB3\x81";
+ break;
+ case 0x96 :
+ return "\xE8\xB4\x9B";
+ break;
+ case 0x97 :
+ return "\xE8\xB5\xB7";
+ break;
+ case 0x98 :
+ return "\xF0\xA7\xBC\xAF";
+ break;
+ case 0x99 :
+ return "\xF0\xA0\xA0\x84";
+ break;
+ case 0x9A :
+ return "\xE8\xB7\x8B";
+ break;
+ case 0x9B :
+ return "\xE8\xB6\xBC";
+ break;
+ case 0x9C :
+ return "\xE8\xB7\xB0";
+ break;
+ case 0x9D :
+ return "\xF0\xA0\xA3\x9E";
+ break;
+ case 0x9E :
+ return "\xE8\xBB\x94";
+ break;
+ case 0x9F :
+ return "\xE8\xBC\xB8";
+ break;
+ case 0xA0 :
+ return "\xF0\xA8\x97\x92";
+ break;
+ case 0xA1 :
+ return "\xF0\xA8\x97\xAD";
+ break;
+ case 0xA2 :
+ return "\xE9\x82\x94";
+ break;
+ case 0xA3 :
+ return "\xE9\x83\xB1";
+ break;
+ case 0xA4 :
+ return "\xE9\x84\x91";
+ break;
+ case 0xA5 :
+ return "\xF0\xA8\x9C\xAE";
+ break;
+ case 0xA6 :
+ return "\xE9\x84\x9B";
+ break;
+ case 0xA7 :
+ return "\xE9\x88\xB8";
+ break;
+ case 0xA8 :
+ return "\xE9\x8B\x97";
+ break;
+ case 0xA9 :
+ return "\xE9\x8B\x98";
+ break;
+ case 0xAA :
+ return "\xE9\x89\xBC";
+ break;
+ case 0xAB :
+ return "\xE9\x8F\xB9";
+ break;
+ case 0xAC :
+ return "\xE9\x90\x95";
+ break;
+ case 0xAD :
+ return "\xF0\xA8\xAF\xBA";
+ break;
+ case 0xAE :
+ return "\xE9\x96\x8B";
+ break;
+ case 0xAF :
+ return "\xE4\xA6\x95";
+ break;
+ case 0xB0 :
+ return "\xE9\x96\xB7";
+ break;
+ case 0xB1 :
+ return "\xF0\xA8\xB5\xB7";
+ break;
+ case 0xB2 :
+ return "\xE4\xA7\xA6";
+ break;
+ case 0xB3 :
+ return "\xE9\x9B\x83";
+ break;
+ case 0xB4 :
+ return "\xE5\xB6\xB2";
+ break;
+ case 0xB5 :
+ return "\xE9\x9C\xA3";
+ break;
+ case 0xB6 :
+ return "\xF0\xA9\x85\x85";
+ break;
+ case 0xB7 :
+ return "\xF0\xA9\x88\x9A";
+ break;
+ case 0xB8 :
+ return "\xE4\xA9\xAE";
+ break;
+ case 0xB9 :
+ return "\xE4\xA9\xB6";
+ break;
+ case 0xBA :
+ return "\xE9\x9F\xA0";
+ break;
+ case 0xBB :
+ return "\xF0\xA9\x90\x8A";
+ break;
+ case 0xBC :
+ return "\xE4\xAA\xB2";
+ break;
+ case 0xBD :
+ return "\xF0\xA9\x92\x96";
+ break;
+ case 0xBE :
+ return "\xE9\xA0\x8B";
+ break;
+ case 0xBF :
+ return "\xE9\xA0\x8B";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (str[3]) {
+ case 0x80 :
+ return "\xE9\xA0\xA9";
+ break;
+ case 0x81 :
+ return "\xF0\xA9\x96\xB6";
+ break;
+ case 0x82 :
+ return "\xE9\xA3\xA2";
+ break;
+ case 0x83 :
+ return "\xE4\xAC\xB3";
+ break;
+ case 0x84 :
+ return "\xE9\xA4\xA9";
+ break;
+ case 0x85 :
+ return "\xE9\xA6\xA7";
+ break;
+ case 0x86 :
+ return "\xE9\xA7\x82";
+ break;
+ case 0x87 :
+ return "\xE9\xA7\xBE";
+ break;
+ case 0x88 :
+ return "\xE4\xAF\x8E";
+ break;
+ case 0x89 :
+ return "\xF0\xA9\xAC\xB0";
+ break;
+ case 0x8A :
+ return "\xE9\xAC\x92";
+ break;
+ case 0x8B :
+ return "\xE9\xB1\x80";
+ break;
+ case 0x8C :
+ return "\xE9\xB3\xBD";
+ break;
+ case 0x8D :
+ return "\xE4\xB3\x8E";
+ break;
+ case 0x8E :
+ return "\xE4\xB3\xAD";
+ break;
+ case 0x8F :
+ return "\xE9\xB5\xA7";
+ break;
+ case 0x90 :
+ return "\xF0\xAA\x83\x8E";
+ break;
+ case 0x91 :
+ return "\xE4\xB3\xB8";
+ break;
+ case 0x92 :
+ return "\xF0\xAA\x84\x85";
+ break;
+ case 0x93 :
+ return "\xF0\xAA\x88\x8E";
+ break;
+ case 0x94 :
+ return "\xF0\xAA\x8A\x91";
+ break;
+ case 0x95 :
+ return "\xE9\xBA\xBB";
+ break;
+ case 0x96 :
+ return "\xE4\xB5\x96";
+ break;
+ case 0x97 :
+ return "\xE9\xBB\xB9";
+ break;
+ case 0x98 :
+ return "\xE9\xBB\xBE";
+ break;
+ case 0x99 :
+ return "\xE9\xBC\x85";
+ break;
+ case 0x9A :
+ return "\xE9\xBC\x8F";
+ break;
+ case 0x9B :
+ return "\xE9\xBC\x96";
+ break;
+ case 0x9C :
+ return "\xE9\xBC\xBB";
+ break;
+ case 0x9D :
+ return "\xF0\xAA\x98\x80";
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+}
+ return 0;
+}
+
+const char *
+grn_nfkc_map2(const unsigned char *prefix, const unsigned char *suffix)
+{
+switch (suffix[0]) {
+case 0xCC :
+ switch (suffix[1]) {
+ case 0x80 :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xC3\xA0";
+ break;
+ case 0x65 :
+ return "\xC3\xA8";
+ break;
+ case 0x69 :
+ return "\xC3\xAC";
+ break;
+ case 0x6E :
+ return "\xC7\xB9";
+ break;
+ case 0x6F :
+ return "\xC3\xB2";
+ break;
+ case 0x75 :
+ return "\xC3\xB9";
+ break;
+ case 0x77 :
+ return "\xE1\xBA\x81";
+ break;
+ case 0x79 :
+ return "\xE1\xBB\xB3";
+ break;
+ case 0xC3 :
+ switch (prefix[1]) {
+ case 0xA2 :
+ return "\xE1\xBA\xA7";
+ break;
+ case 0xAA :
+ return "\xE1\xBB\x81";
+ break;
+ case 0xB4 :
+ return "\xE1\xBB\x93";
+ break;
+ case 0xBC :
+ return "\xC7\x9C";
+ break;
+ }
+ break;
+ case 0xC4 :
+ switch (prefix[1]) {
+ case 0x83 :
+ return "\xE1\xBA\xB1";
+ break;
+ case 0x93 :
+ return "\xE1\xB8\x95";
+ break;
+ }
+ break;
+ case 0xC5 :
+ if (prefix[1] == 0x8D) {
+ return "\xE1\xB9\x91";
+ }
+ break;
+ case 0xC6 :
+ switch (prefix[1]) {
+ case 0xA1 :
+ return "\xE1\xBB\x9D";
+ break;
+ case 0xB0 :
+ return "\xE1\xBB\xAB";
+ break;
+ }
+ break;
+ case 0xCE :
+ switch (prefix[1]) {
+ case 0x91 :
+ return "\xE1\xBE\xBA";
+ break;
+ case 0x95 :
+ return "\xE1\xBF\x88";
+ break;
+ case 0x97 :
+ return "\xE1\xBF\x8A";
+ break;
+ case 0x99 :
+ return "\xE1\xBF\x9A";
+ break;
+ case 0x9F :
+ return "\xE1\xBF\xB8";
+ break;
+ case 0xA5 :
+ return "\xE1\xBF\xAA";
+ break;
+ case 0xA9 :
+ return "\xE1\xBF\xBA";
+ break;
+ case 0xB1 :
+ return "\xE1\xBD\xB0";
+ break;
+ case 0xB5 :
+ return "\xE1\xBD\xB2";
+ break;
+ case 0xB7 :
+ return "\xE1\xBD\xB4";
+ break;
+ case 0xB9 :
+ return "\xE1\xBD\xB6";
+ break;
+ case 0xBF :
+ return "\xE1\xBD\xB8";
+ break;
+ }
+ break;
+ case 0xCF :
+ switch (prefix[1]) {
+ case 0x85 :
+ return "\xE1\xBD\xBA";
+ break;
+ case 0x89 :
+ return "\xE1\xBD\xBC";
+ break;
+ case 0x8A :
+ return "\xE1\xBF\x92";
+ break;
+ case 0x8B :
+ return "\xE1\xBF\xA2";
+ break;
+ }
+ break;
+ case 0xD0 :
+ switch (prefix[1]) {
+ case 0x95 :
+ return "\xD0\x80";
+ break;
+ case 0x98 :
+ return "\xD0\x8D";
+ break;
+ case 0xB5 :
+ return "\xD1\x90";
+ break;
+ case 0xB8 :
+ return "\xD1\x9D";
+ break;
+ }
+ break;
+ case 0xE1 :
+ switch (prefix[1]) {
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xE1\xBC\x82";
+ break;
+ case 0x81 :
+ return "\xE1\xBC\x83";
+ break;
+ case 0x88 :
+ return "\xE1\xBC\x8A";
+ break;
+ case 0x89 :
+ return "\xE1\xBC\x8B";
+ break;
+ case 0x90 :
+ return "\xE1\xBC\x92";
+ break;
+ case 0x91 :
+ return "\xE1\xBC\x93";
+ break;
+ case 0x98 :
+ return "\xE1\xBC\x9A";
+ break;
+ case 0x99 :
+ return "\xE1\xBC\x9B";
+ break;
+ case 0xA0 :
+ return "\xE1\xBC\xA2";
+ break;
+ case 0xA1 :
+ return "\xE1\xBC\xA3";
+ break;
+ case 0xA8 :
+ return "\xE1\xBC\xAA";
+ break;
+ case 0xA9 :
+ return "\xE1\xBC\xAB";
+ break;
+ case 0xB0 :
+ return "\xE1\xBC\xB2";
+ break;
+ case 0xB1 :
+ return "\xE1\xBC\xB3";
+ break;
+ case 0xB8 :
+ return "\xE1\xBC\xBA";
+ break;
+ case 0xB9 :
+ return "\xE1\xBC\xBB";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xE1\xBD\x82";
+ break;
+ case 0x81 :
+ return "\xE1\xBD\x83";
+ break;
+ case 0x88 :
+ return "\xE1\xBD\x8A";
+ break;
+ case 0x89 :
+ return "\xE1\xBD\x8B";
+ break;
+ case 0x90 :
+ return "\xE1\xBD\x92";
+ break;
+ case 0x91 :
+ return "\xE1\xBD\x93";
+ break;
+ case 0x99 :
+ return "\xE1\xBD\x9B";
+ break;
+ case 0xA0 :
+ return "\xE1\xBD\xA2";
+ break;
+ case 0xA1 :
+ return "\xE1\xBD\xA3";
+ break;
+ case 0xA8 :
+ return "\xE1\xBD\xAA";
+ break;
+ case 0xA9 :
+ return "\xE1\xBD\xAB";
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xC3\xA1";
+ break;
+ case 0x63 :
+ return "\xC4\x87";
+ break;
+ case 0x65 :
+ return "\xC3\xA9";
+ break;
+ case 0x67 :
+ return "\xC7\xB5";
+ break;
+ case 0x69 :
+ return "\xC3\xAD";
+ break;
+ case 0x6B :
+ return "\xE1\xB8\xB1";
+ break;
+ case 0x6C :
+ return "\xC4\xBA";
+ break;
+ case 0x6D :
+ return "\xE1\xB8\xBF";
+ break;
+ case 0x6E :
+ return "\xC5\x84";
+ break;
+ case 0x6F :
+ return "\xC3\xB3";
+ break;
+ case 0x70 :
+ return "\xE1\xB9\x95";
+ break;
+ case 0x72 :
+ return "\xC5\x95";
+ break;
+ case 0x73 :
+ return "\xC5\x9B";
+ break;
+ case 0x75 :
+ return "\xC3\xBA";
+ break;
+ case 0x77 :
+ return "\xE1\xBA\x83";
+ break;
+ case 0x79 :
+ return "\xC3\xBD";
+ break;
+ case 0x7A :
+ return "\xC5\xBA";
+ break;
+ case 0xC3 :
+ switch (prefix[1]) {
+ case 0x86 :
+ return "\xC7\xBC";
+ break;
+ case 0x98 :
+ return "\xC7\xBE";
+ break;
+ case 0xA2 :
+ return "\xE1\xBA\xA5";
+ break;
+ case 0xA5 :
+ return "\xC7\xBB";
+ break;
+ case 0xA6 :
+ return "\xC7\xBD";
+ break;
+ case 0xA7 :
+ return "\xE1\xB8\x89";
+ break;
+ case 0xAA :
+ return "\xE1\xBA\xBF";
+ break;
+ case 0xAF :
+ return "\xE1\xB8\xAF";
+ break;
+ case 0xB4 :
+ return "\xE1\xBB\x91";
+ break;
+ case 0xB5 :
+ return "\xE1\xB9\x8D";
+ break;
+ case 0xB8 :
+ return "\xC7\xBF";
+ break;
+ case 0xBC :
+ return "\xC7\x98";
+ break;
+ }
+ break;
+ case 0xC4 :
+ switch (prefix[1]) {
+ case 0x83 :
+ return "\xE1\xBA\xAF";
+ break;
+ case 0x93 :
+ return "\xE1\xB8\x97";
+ break;
+ }
+ break;
+ case 0xC5 :
+ switch (prefix[1]) {
+ case 0x8D :
+ return "\xE1\xB9\x93";
+ break;
+ case 0xA9 :
+ return "\xE1\xB9\xB9";
+ break;
+ }
+ break;
+ case 0xC6 :
+ switch (prefix[1]) {
+ case 0xA1 :
+ return "\xE1\xBB\x9B";
+ break;
+ case 0xB0 :
+ return "\xE1\xBB\xA9";
+ break;
+ }
+ break;
+ case 0xCE :
+ switch (prefix[1]) {
+ case 0x91 :
+ return "\xCE\x86";
+ break;
+ case 0x95 :
+ return "\xCE\x88";
+ break;
+ case 0x97 :
+ return "\xCE\x89";
+ break;
+ case 0x99 :
+ return "\xCE\x8A";
+ break;
+ case 0x9F :
+ return "\xCE\x8C";
+ break;
+ case 0xA5 :
+ return "\xCE\x8E";
+ break;
+ case 0xA9 :
+ return "\xCE\x8F";
+ break;
+ case 0xB1 :
+ return "\xCE\xAC";
+ break;
+ case 0xB5 :
+ return "\xCE\xAD";
+ break;
+ case 0xB7 :
+ return "\xCE\xAE";
+ break;
+ case 0xB9 :
+ return "\xCE\xAF";
+ break;
+ case 0xBF :
+ return "\xCF\x8C";
+ break;
+ }
+ break;
+ case 0xCF :
+ switch (prefix[1]) {
+ case 0x85 :
+ return "\xCF\x8D";
+ break;
+ case 0x89 :
+ return "\xCF\x8E";
+ break;
+ case 0x8A :
+ return "\xCE\x90";
+ break;
+ case 0x8B :
+ return "\xCE\xB0";
+ break;
+ }
+ break;
+ case 0xD0 :
+ switch (prefix[1]) {
+ case 0x93 :
+ return "\xD0\x83";
+ break;
+ case 0x9A :
+ return "\xD0\x8C";
+ break;
+ case 0xB3 :
+ return "\xD1\x93";
+ break;
+ case 0xBA :
+ return "\xD1\x9C";
+ break;
+ }
+ break;
+ case 0xE1 :
+ switch (prefix[1]) {
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xE1\xBC\x84";
+ break;
+ case 0x81 :
+ return "\xE1\xBC\x85";
+ break;
+ case 0x88 :
+ return "\xE1\xBC\x8C";
+ break;
+ case 0x89 :
+ return "\xE1\xBC\x8D";
+ break;
+ case 0x90 :
+ return "\xE1\xBC\x94";
+ break;
+ case 0x91 :
+ return "\xE1\xBC\x95";
+ break;
+ case 0x98 :
+ return "\xE1\xBC\x9C";
+ break;
+ case 0x99 :
+ return "\xE1\xBC\x9D";
+ break;
+ case 0xA0 :
+ return "\xE1\xBC\xA4";
+ break;
+ case 0xA1 :
+ return "\xE1\xBC\xA5";
+ break;
+ case 0xA8 :
+ return "\xE1\xBC\xAC";
+ break;
+ case 0xA9 :
+ return "\xE1\xBC\xAD";
+ break;
+ case 0xB0 :
+ return "\xE1\xBC\xB4";
+ break;
+ case 0xB1 :
+ return "\xE1\xBC\xB5";
+ break;
+ case 0xB8 :
+ return "\xE1\xBC\xBC";
+ break;
+ case 0xB9 :
+ return "\xE1\xBC\xBD";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xE1\xBD\x84";
+ break;
+ case 0x81 :
+ return "\xE1\xBD\x85";
+ break;
+ case 0x88 :
+ return "\xE1\xBD\x8C";
+ break;
+ case 0x89 :
+ return "\xE1\xBD\x8D";
+ break;
+ case 0x90 :
+ return "\xE1\xBD\x94";
+ break;
+ case 0x91 :
+ return "\xE1\xBD\x95";
+ break;
+ case 0x99 :
+ return "\xE1\xBD\x9D";
+ break;
+ case 0xA0 :
+ return "\xE1\xBD\xA4";
+ break;
+ case 0xA1 :
+ return "\xE1\xBD\xA5";
+ break;
+ case 0xA8 :
+ return "\xE1\xBD\xAC";
+ break;
+ case 0xA9 :
+ return "\xE1\xBD\xAD";
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xC3\xA2";
+ break;
+ case 0x63 :
+ return "\xC4\x89";
+ break;
+ case 0x65 :
+ return "\xC3\xAA";
+ break;
+ case 0x67 :
+ return "\xC4\x9D";
+ break;
+ case 0x68 :
+ return "\xC4\xA5";
+ break;
+ case 0x69 :
+ return "\xC3\xAE";
+ break;
+ case 0x6A :
+ return "\xC4\xB5";
+ break;
+ case 0x6F :
+ return "\xC3\xB4";
+ break;
+ case 0x73 :
+ return "\xC5\x9D";
+ break;
+ case 0x75 :
+ return "\xC3\xBB";
+ break;
+ case 0x77 :
+ return "\xC5\xB5";
+ break;
+ case 0x79 :
+ return "\xC5\xB7";
+ break;
+ case 0x7A :
+ return "\xE1\xBA\x91";
+ break;
+ case 0xE1 :
+ switch (prefix[1]) {
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0xA1 :
+ return "\xE1\xBA\xAD";
+ break;
+ case 0xB9 :
+ return "\xE1\xBB\x87";
+ break;
+ }
+ break;
+ case 0xBB :
+ if (prefix[2] == 0x8D) {
+ return "\xE1\xBB\x99";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xC3\xA3";
+ break;
+ case 0x65 :
+ return "\xE1\xBA\xBD";
+ break;
+ case 0x69 :
+ return "\xC4\xA9";
+ break;
+ case 0x6E :
+ return "\xC3\xB1";
+ break;
+ case 0x6F :
+ return "\xC3\xB5";
+ break;
+ case 0x75 :
+ return "\xC5\xA9";
+ break;
+ case 0x76 :
+ return "\xE1\xB9\xBD";
+ break;
+ case 0x79 :
+ return "\xE1\xBB\xB9";
+ break;
+ case 0xC3 :
+ switch (prefix[1]) {
+ case 0xA2 :
+ return "\xE1\xBA\xAB";
+ break;
+ case 0xAA :
+ return "\xE1\xBB\x85";
+ break;
+ case 0xB4 :
+ return "\xE1\xBB\x97";
+ break;
+ }
+ break;
+ case 0xC4 :
+ if (prefix[1] == 0x83) {
+ return "\xE1\xBA\xB5";
+ }
+ break;
+ case 0xC6 :
+ switch (prefix[1]) {
+ case 0xA1 :
+ return "\xE1\xBB\xA1";
+ break;
+ case 0xB0 :
+ return "\xE1\xBB\xAF";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xC4\x81";
+ break;
+ case 0x65 :
+ return "\xC4\x93";
+ break;
+ case 0x67 :
+ return "\xE1\xB8\xA1";
+ break;
+ case 0x69 :
+ return "\xC4\xAB";
+ break;
+ case 0x6F :
+ return "\xC5\x8D";
+ break;
+ case 0x75 :
+ return "\xC5\xAB";
+ break;
+ case 0x79 :
+ return "\xC8\xB3";
+ break;
+ case 0xC3 :
+ switch (prefix[1]) {
+ case 0x86 :
+ return "\xC7\xA2";
+ break;
+ case 0xA4 :
+ return "\xC7\x9F";
+ break;
+ case 0xA6 :
+ return "\xC7\xA3";
+ break;
+ case 0xB5 :
+ return "\xC8\xAD";
+ break;
+ case 0xB6 :
+ return "\xC8\xAB";
+ break;
+ case 0xBC :
+ return "\xC7\x96";
+ break;
+ }
+ break;
+ case 0xC7 :
+ if (prefix[1] == 0xAB) {
+ return "\xC7\xAD";
+ }
+ break;
+ case 0xC8 :
+ switch (prefix[1]) {
+ case 0xA7 :
+ return "\xC7\xA1";
+ break;
+ case 0xAF :
+ return "\xC8\xB1";
+ break;
+ }
+ break;
+ case 0xCE :
+ switch (prefix[1]) {
+ case 0x91 :
+ return "\xE1\xBE\xB9";
+ break;
+ case 0x99 :
+ return "\xE1\xBF\x99";
+ break;
+ case 0xA5 :
+ return "\xE1\xBF\xA9";
+ break;
+ case 0xB1 :
+ return "\xE1\xBE\xB1";
+ break;
+ case 0xB9 :
+ return "\xE1\xBF\x91";
+ break;
+ }
+ break;
+ case 0xCF :
+ if (prefix[1] == 0x85) {
+ return "\xE1\xBF\xA1";
+ }
+ break;
+ case 0xD0 :
+ switch (prefix[1]) {
+ case 0x98 :
+ return "\xD3\xA2";
+ break;
+ case 0xA3 :
+ return "\xD3\xAE";
+ break;
+ case 0xB8 :
+ return "\xD3\xA3";
+ break;
+ }
+ break;
+ case 0xD1 :
+ if (prefix[1] == 0x83) {
+ return "\xD3\xAF";
+ }
+ break;
+ case 0xE1 :
+ switch (prefix[1]) {
+ case 0xB8 :
+ if (prefix[2] == 0xB7) {
+ return "\xE1\xB8\xB9";
+ }
+ break;
+ case 0xB9 :
+ if (prefix[2] == 0x9B) {
+ return "\xE1\xB9\x9D";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xC4\x83";
+ break;
+ case 0x65 :
+ return "\xC4\x95";
+ break;
+ case 0x67 :
+ return "\xC4\x9F";
+ break;
+ case 0x69 :
+ return "\xC4\xAD";
+ break;
+ case 0x6F :
+ return "\xC5\x8F";
+ break;
+ case 0x75 :
+ return "\xC5\xAD";
+ break;
+ case 0xC8 :
+ if (prefix[1] == 0xA9) {
+ return "\xE1\xB8\x9D";
+ }
+ break;
+ case 0xCE :
+ switch (prefix[1]) {
+ case 0x91 :
+ return "\xE1\xBE\xB8";
+ break;
+ case 0x99 :
+ return "\xE1\xBF\x98";
+ break;
+ case 0xA5 :
+ return "\xE1\xBF\xA8";
+ break;
+ case 0xB1 :
+ return "\xE1\xBE\xB0";
+ break;
+ case 0xB9 :
+ return "\xE1\xBF\x90";
+ break;
+ }
+ break;
+ case 0xCF :
+ if (prefix[1] == 0x85) {
+ return "\xE1\xBF\xA0";
+ }
+ break;
+ case 0xD0 :
+ switch (prefix[1]) {
+ case 0x90 :
+ return "\xD3\x90";
+ break;
+ case 0x95 :
+ return "\xD3\x96";
+ break;
+ case 0x96 :
+ return "\xD3\x81";
+ break;
+ case 0x98 :
+ return "\xD0\x99";
+ break;
+ case 0xA3 :
+ return "\xD0\x8E";
+ break;
+ case 0xB0 :
+ return "\xD3\x91";
+ break;
+ case 0xB5 :
+ return "\xD3\x97";
+ break;
+ case 0xB6 :
+ return "\xD3\x82";
+ break;
+ case 0xB8 :
+ return "\xD0\xB9";
+ break;
+ }
+ break;
+ case 0xD1 :
+ if (prefix[1] == 0x83) {
+ return "\xD1\x9E";
+ }
+ break;
+ case 0xE1 :
+ if (prefix[1] == 0xBA) {
+ if (prefix[2] == 0xA1) {
+ return "\xE1\xBA\xB7";
+ }
+ }
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xC8\xA7";
+ break;
+ case 0x62 :
+ return "\xE1\xB8\x83";
+ break;
+ case 0x63 :
+ return "\xC4\x8B";
+ break;
+ case 0x64 :
+ return "\xE1\xB8\x8B";
+ break;
+ case 0x65 :
+ return "\xC4\x97";
+ break;
+ case 0x66 :
+ return "\xE1\xB8\x9F";
+ break;
+ case 0x67 :
+ return "\xC4\xA1";
+ break;
+ case 0x68 :
+ return "\xE1\xB8\xA3";
+ break;
+ case 0x6D :
+ return "\xE1\xB9\x81";
+ break;
+ case 0x6E :
+ return "\xE1\xB9\x85";
+ break;
+ case 0x6F :
+ return "\xC8\xAF";
+ break;
+ case 0x70 :
+ return "\xE1\xB9\x97";
+ break;
+ case 0x72 :
+ return "\xE1\xB9\x99";
+ break;
+ case 0x73 :
+ return "\xE1\xB9\xA1";
+ break;
+ case 0x74 :
+ return "\xE1\xB9\xAB";
+ break;
+ case 0x77 :
+ return "\xE1\xBA\x87";
+ break;
+ case 0x78 :
+ return "\xE1\xBA\x8B";
+ break;
+ case 0x79 :
+ return "\xE1\xBA\x8F";
+ break;
+ case 0x7A :
+ return "\xC5\xBC";
+ break;
+ case 0xC5 :
+ switch (prefix[1]) {
+ case 0x9B :
+ return "\xE1\xB9\xA5";
+ break;
+ case 0xA1 :
+ return "\xE1\xB9\xA7";
+ break;
+ }
+ break;
+ case 0xE1 :
+ if (prefix[1] == 0xB9) {
+ if (prefix[2] == 0xA3) {
+ return "\xE1\xB9\xA9";
+ }
+ }
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xC3\xA4";
+ break;
+ case 0x65 :
+ return "\xC3\xAB";
+ break;
+ case 0x68 :
+ return "\xE1\xB8\xA7";
+ break;
+ case 0x69 :
+ return "\xC3\xAF";
+ break;
+ case 0x6F :
+ return "\xC3\xB6";
+ break;
+ case 0x74 :
+ return "\xE1\xBA\x97";
+ break;
+ case 0x75 :
+ return "\xC3\xBC";
+ break;
+ case 0x77 :
+ return "\xE1\xBA\x85";
+ break;
+ case 0x78 :
+ return "\xE1\xBA\x8D";
+ break;
+ case 0x79 :
+ return "\xC3\xBF";
+ break;
+ case 0xC3 :
+ if (prefix[1] == 0xB5) {
+ return "\xE1\xB9\x8F";
+ }
+ break;
+ case 0xC5 :
+ if (prefix[1] == 0xAB) {
+ return "\xE1\xB9\xBB";
+ }
+ break;
+ case 0xCE :
+ switch (prefix[1]) {
+ case 0x99 :
+ return "\xCE\xAA";
+ break;
+ case 0xA5 :
+ return "\xCE\xAB";
+ break;
+ case 0xB9 :
+ return "\xCF\x8A";
+ break;
+ }
+ break;
+ case 0xCF :
+ if (prefix[1] == 0x85) {
+ return "\xCF\x8B";
+ }
+ break;
+ case 0xD0 :
+ switch (prefix[1]) {
+ case 0x86 :
+ return "\xD0\x87";
+ break;
+ case 0x90 :
+ return "\xD3\x92";
+ break;
+ case 0x95 :
+ return "\xD0\x81";
+ break;
+ case 0x96 :
+ return "\xD3\x9C";
+ break;
+ case 0x97 :
+ return "\xD3\x9E";
+ break;
+ case 0x98 :
+ return "\xD3\xA4";
+ break;
+ case 0x9E :
+ return "\xD3\xA6";
+ break;
+ case 0xA3 :
+ return "\xD3\xB0";
+ break;
+ case 0xA7 :
+ return "\xD3\xB4";
+ break;
+ case 0xAB :
+ return "\xD3\xB8";
+ break;
+ case 0xAD :
+ return "\xD3\xAC";
+ break;
+ case 0xB0 :
+ return "\xD3\x93";
+ break;
+ case 0xB5 :
+ return "\xD1\x91";
+ break;
+ case 0xB6 :
+ return "\xD3\x9D";
+ break;
+ case 0xB7 :
+ return "\xD3\x9F";
+ break;
+ case 0xB8 :
+ return "\xD3\xA5";
+ break;
+ case 0xBE :
+ return "\xD3\xA7";
+ break;
+ }
+ break;
+ case 0xD1 :
+ switch (prefix[1]) {
+ case 0x83 :
+ return "\xD3\xB1";
+ break;
+ case 0x87 :
+ return "\xD3\xB5";
+ break;
+ case 0x8B :
+ return "\xD3\xB9";
+ break;
+ case 0x8D :
+ return "\xD3\xAD";
+ break;
+ case 0x96 :
+ return "\xD1\x97";
+ break;
+ }
+ break;
+ case 0xD3 :
+ switch (prefix[1]) {
+ case 0x98 :
+ return "\xD3\x9A";
+ break;
+ case 0x99 :
+ return "\xD3\x9B";
+ break;
+ case 0xA8 :
+ return "\xD3\xAA";
+ break;
+ case 0xA9 :
+ return "\xD3\xAB";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xE1\xBA\xA3";
+ break;
+ case 0x65 :
+ return "\xE1\xBA\xBB";
+ break;
+ case 0x69 :
+ return "\xE1\xBB\x89";
+ break;
+ case 0x6F :
+ return "\xE1\xBB\x8F";
+ break;
+ case 0x75 :
+ return "\xE1\xBB\xA7";
+ break;
+ case 0x79 :
+ return "\xE1\xBB\xB7";
+ break;
+ case 0xC3 :
+ switch (prefix[1]) {
+ case 0xA2 :
+ return "\xE1\xBA\xA9";
+ break;
+ case 0xAA :
+ return "\xE1\xBB\x83";
+ break;
+ case 0xB4 :
+ return "\xE1\xBB\x95";
+ break;
+ }
+ break;
+ case 0xC4 :
+ if (prefix[1] == 0x83) {
+ return "\xE1\xBA\xB3";
+ }
+ break;
+ case 0xC6 :
+ switch (prefix[1]) {
+ case 0xA1 :
+ return "\xE1\xBB\x9F";
+ break;
+ case 0xB0 :
+ return "\xE1\xBB\xAD";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xC3\xA5";
+ break;
+ case 0x75 :
+ return "\xC5\xAF";
+ break;
+ case 0x77 :
+ return "\xE1\xBA\x98";
+ break;
+ case 0x79 :
+ return "\xE1\xBA\x99";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[0]) {
+ case 0x6F :
+ return "\xC5\x91";
+ break;
+ case 0x75 :
+ return "\xC5\xB1";
+ break;
+ case 0xD0 :
+ if (prefix[1] == 0xA3) {
+ return "\xD3\xB2";
+ }
+ break;
+ case 0xD1 :
+ if (prefix[1] == 0x83) {
+ return "\xD3\xB3";
+ }
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xC7\x8E";
+ break;
+ case 0x63 :
+ return "\xC4\x8D";
+ break;
+ case 0x64 :
+ return "\xC4\x8F";
+ break;
+ case 0x65 :
+ return "\xC4\x9B";
+ break;
+ case 0x67 :
+ return "\xC7\xA7";
+ break;
+ case 0x68 :
+ return "\xC8\x9F";
+ break;
+ case 0x69 :
+ return "\xC7\x90";
+ break;
+ case 0x6A :
+ return "\xC7\xB0";
+ break;
+ case 0x6B :
+ return "\xC7\xA9";
+ break;
+ case 0x6C :
+ return "\xC4\xBE";
+ break;
+ case 0x6E :
+ return "\xC5\x88";
+ break;
+ case 0x6F :
+ return "\xC7\x92";
+ break;
+ case 0x72 :
+ return "\xC5\x99";
+ break;
+ case 0x73 :
+ return "\xC5\xA1";
+ break;
+ case 0x74 :
+ return "\xC5\xA5";
+ break;
+ case 0x75 :
+ return "\xC7\x94";
+ break;
+ case 0x7A :
+ return "\xC5\xBE";
+ break;
+ case 0xC3 :
+ if (prefix[1] == 0xBC) {
+ return "\xC7\x9A";
+ }
+ break;
+ case 0xC6 :
+ if (prefix[1] == 0xB7) {
+ return "\xC7\xAE";
+ }
+ break;
+ case 0xCA :
+ if (prefix[1] == 0x92) {
+ return "\xC7\xAF";
+ }
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xC8\x81";
+ break;
+ case 0x65 :
+ return "\xC8\x85";
+ break;
+ case 0x69 :
+ return "\xC8\x89";
+ break;
+ case 0x6F :
+ return "\xC8\x8D";
+ break;
+ case 0x72 :
+ return "\xC8\x91";
+ break;
+ case 0x75 :
+ return "\xC8\x95";
+ break;
+ case 0xD1 :
+ switch (prefix[1]) {
+ case 0xB4 :
+ return "\xD1\xB6";
+ break;
+ case 0xB5 :
+ return "\xD1\xB7";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xC8\x83";
+ break;
+ case 0x65 :
+ return "\xC8\x87";
+ break;
+ case 0x69 :
+ return "\xC8\x8B";
+ break;
+ case 0x6F :
+ return "\xC8\x8F";
+ break;
+ case 0x72 :
+ return "\xC8\x93";
+ break;
+ case 0x75 :
+ return "\xC8\x97";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[0]) {
+ case 0xCE :
+ switch (prefix[1]) {
+ case 0x91 :
+ return "\xE1\xBC\x88";
+ break;
+ case 0x95 :
+ return "\xE1\xBC\x98";
+ break;
+ case 0x97 :
+ return "\xE1\xBC\xA8";
+ break;
+ case 0x99 :
+ return "\xE1\xBC\xB8";
+ break;
+ case 0x9F :
+ return "\xE1\xBD\x88";
+ break;
+ case 0xA9 :
+ return "\xE1\xBD\xA8";
+ break;
+ case 0xB1 :
+ return "\xE1\xBC\x80";
+ break;
+ case 0xB5 :
+ return "\xE1\xBC\x90";
+ break;
+ case 0xB7 :
+ return "\xE1\xBC\xA0";
+ break;
+ case 0xB9 :
+ return "\xE1\xBC\xB0";
+ break;
+ case 0xBF :
+ return "\xE1\xBD\x80";
+ break;
+ }
+ break;
+ case 0xCF :
+ switch (prefix[1]) {
+ case 0x81 :
+ return "\xE1\xBF\xA4";
+ break;
+ case 0x85 :
+ return "\xE1\xBD\x90";
+ break;
+ case 0x89 :
+ return "\xE1\xBD\xA0";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[0]) {
+ case 0xCE :
+ switch (prefix[1]) {
+ case 0x91 :
+ return "\xE1\xBC\x89";
+ break;
+ case 0x95 :
+ return "\xE1\xBC\x99";
+ break;
+ case 0x97 :
+ return "\xE1\xBC\xA9";
+ break;
+ case 0x99 :
+ return "\xE1\xBC\xB9";
+ break;
+ case 0x9F :
+ return "\xE1\xBD\x89";
+ break;
+ case 0xA1 :
+ return "\xE1\xBF\xAC";
+ break;
+ case 0xA5 :
+ return "\xE1\xBD\x99";
+ break;
+ case 0xA9 :
+ return "\xE1\xBD\xA9";
+ break;
+ case 0xB1 :
+ return "\xE1\xBC\x81";
+ break;
+ case 0xB5 :
+ return "\xE1\xBC\x91";
+ break;
+ case 0xB7 :
+ return "\xE1\xBC\xA1";
+ break;
+ case 0xB9 :
+ return "\xE1\xBC\xB1";
+ break;
+ case 0xBF :
+ return "\xE1\xBD\x81";
+ break;
+ }
+ break;
+ case 0xCF :
+ switch (prefix[1]) {
+ case 0x81 :
+ return "\xE1\xBF\xA5";
+ break;
+ case 0x85 :
+ return "\xE1\xBD\x91";
+ break;
+ case 0x89 :
+ return "\xE1\xBD\xA1";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[0]) {
+ case 0x6F :
+ return "\xC6\xA1";
+ break;
+ case 0x75 :
+ return "\xC6\xB0";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xE1\xBA\xA1";
+ break;
+ case 0x62 :
+ return "\xE1\xB8\x85";
+ break;
+ case 0x64 :
+ return "\xE1\xB8\x8D";
+ break;
+ case 0x65 :
+ return "\xE1\xBA\xB9";
+ break;
+ case 0x68 :
+ return "\xE1\xB8\xA5";
+ break;
+ case 0x69 :
+ return "\xE1\xBB\x8B";
+ break;
+ case 0x6B :
+ return "\xE1\xB8\xB3";
+ break;
+ case 0x6C :
+ return "\xE1\xB8\xB7";
+ break;
+ case 0x6D :
+ return "\xE1\xB9\x83";
+ break;
+ case 0x6E :
+ return "\xE1\xB9\x87";
+ break;
+ case 0x6F :
+ return "\xE1\xBB\x8D";
+ break;
+ case 0x72 :
+ return "\xE1\xB9\x9B";
+ break;
+ case 0x73 :
+ return "\xE1\xB9\xA3";
+ break;
+ case 0x74 :
+ return "\xE1\xB9\xAD";
+ break;
+ case 0x75 :
+ return "\xE1\xBB\xA5";
+ break;
+ case 0x76 :
+ return "\xE1\xB9\xBF";
+ break;
+ case 0x77 :
+ return "\xE1\xBA\x89";
+ break;
+ case 0x79 :
+ return "\xE1\xBB\xB5";
+ break;
+ case 0x7A :
+ return "\xE1\xBA\x93";
+ break;
+ case 0xC6 :
+ switch (prefix[1]) {
+ case 0xA1 :
+ return "\xE1\xBB\xA3";
+ break;
+ case 0xB0 :
+ return "\xE1\xBB\xB1";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xA4 :
+ if (prefix[0] == 0x75) {
+ return "\xE1\xB9\xB3";
+ }
+ break;
+ case 0xA5 :
+ if (prefix[0] == 0x61) {
+ return "\xE1\xB8\x81";
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[0]) {
+ case 0x73 :
+ return "\xC8\x99";
+ break;
+ case 0x74 :
+ return "\xC8\x9B";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[0]) {
+ case 0x63 :
+ return "\xC3\xA7";
+ break;
+ case 0x64 :
+ return "\xE1\xB8\x91";
+ break;
+ case 0x65 :
+ return "\xC8\xA9";
+ break;
+ case 0x67 :
+ return "\xC4\xA3";
+ break;
+ case 0x68 :
+ return "\xE1\xB8\xA9";
+ break;
+ case 0x6B :
+ return "\xC4\xB7";
+ break;
+ case 0x6C :
+ return "\xC4\xBC";
+ break;
+ case 0x6E :
+ return "\xC5\x86";
+ break;
+ case 0x72 :
+ return "\xC5\x97";
+ break;
+ case 0x73 :
+ return "\xC5\x9F";
+ break;
+ case 0x74 :
+ return "\xC5\xA3";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[0]) {
+ case 0x61 :
+ return "\xC4\x85";
+ break;
+ case 0x65 :
+ return "\xC4\x99";
+ break;
+ case 0x69 :
+ return "\xC4\xAF";
+ break;
+ case 0x6F :
+ return "\xC7\xAB";
+ break;
+ case 0x75 :
+ return "\xC5\xB3";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[0]) {
+ case 0x64 :
+ return "\xE1\xB8\x93";
+ break;
+ case 0x65 :
+ return "\xE1\xB8\x99";
+ break;
+ case 0x6C :
+ return "\xE1\xB8\xBD";
+ break;
+ case 0x6E :
+ return "\xE1\xB9\x8B";
+ break;
+ case 0x74 :
+ return "\xE1\xB9\xB1";
+ break;
+ case 0x75 :
+ return "\xE1\xB9\xB7";
+ break;
+ }
+ break;
+ case 0xAE :
+ if (prefix[0] == 0x68) {
+ return "\xE1\xB8\xAB";
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[0]) {
+ case 0x65 :
+ return "\xE1\xB8\x9B";
+ break;
+ case 0x69 :
+ return "\xE1\xB8\xAD";
+ break;
+ case 0x75 :
+ return "\xE1\xB9\xB5";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[0]) {
+ case 0x62 :
+ return "\xE1\xB8\x87";
+ break;
+ case 0x64 :
+ return "\xE1\xB8\x8F";
+ break;
+ case 0x68 :
+ return "\xE1\xBA\x96";
+ break;
+ case 0x6B :
+ return "\xE1\xB8\xB5";
+ break;
+ case 0x6C :
+ return "\xE1\xB8\xBB";
+ break;
+ case 0x6E :
+ return "\xE1\xB9\x89";
+ break;
+ case 0x72 :
+ return "\xE1\xB9\x9F";
+ break;
+ case 0x74 :
+ return "\xE1\xB9\xAF";
+ break;
+ case 0x7A :
+ return "\xE1\xBA\x95";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[0]) {
+ case 0x3C :
+ return "\xE2\x89\xAE";
+ break;
+ case 0x3D :
+ return "\xE2\x89\xA0";
+ break;
+ case 0x3E :
+ return "\xE2\x89\xAF";
+ break;
+ case 0xE2 :
+ switch (prefix[1]) {
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xE2\x86\x9A";
+ break;
+ case 0x92 :
+ return "\xE2\x86\x9B";
+ break;
+ case 0x94 :
+ return "\xE2\x86\xAE";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xE2\x87\x8D";
+ break;
+ case 0x92 :
+ return "\xE2\x87\x8F";
+ break;
+ case 0x94 :
+ return "\xE2\x87\x8E";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x83 :
+ return "\xE2\x88\x84";
+ break;
+ case 0x88 :
+ return "\xE2\x88\x89";
+ break;
+ case 0x8B :
+ return "\xE2\x88\x8C";
+ break;
+ case 0xA3 :
+ return "\xE2\x88\xA4";
+ break;
+ case 0xA5 :
+ return "\xE2\x88\xA6";
+ break;
+ case 0xBC :
+ return "\xE2\x89\x81";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x83 :
+ return "\xE2\x89\x84";
+ break;
+ case 0x85 :
+ return "\xE2\x89\x87";
+ break;
+ case 0x88 :
+ return "\xE2\x89\x89";
+ break;
+ case 0x8D :
+ return "\xE2\x89\xAD";
+ break;
+ case 0xA1 :
+ return "\xE2\x89\xA2";
+ break;
+ case 0xA4 :
+ return "\xE2\x89\xB0";
+ break;
+ case 0xA5 :
+ return "\xE2\x89\xB1";
+ break;
+ case 0xB2 :
+ return "\xE2\x89\xB4";
+ break;
+ case 0xB3 :
+ return "\xE2\x89\xB5";
+ break;
+ case 0xB6 :
+ return "\xE2\x89\xB8";
+ break;
+ case 0xB7 :
+ return "\xE2\x89\xB9";
+ break;
+ case 0xBA :
+ return "\xE2\x8A\x80";
+ break;
+ case 0xBB :
+ return "\xE2\x8A\x81";
+ break;
+ case 0xBC :
+ return "\xE2\x8B\xA0";
+ break;
+ case 0xBD :
+ return "\xE2\x8B\xA1";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x82 :
+ return "\xE2\x8A\x84";
+ break;
+ case 0x83 :
+ return "\xE2\x8A\x85";
+ break;
+ case 0x86 :
+ return "\xE2\x8A\x88";
+ break;
+ case 0x87 :
+ return "\xE2\x8A\x89";
+ break;
+ case 0x91 :
+ return "\xE2\x8B\xA2";
+ break;
+ case 0x92 :
+ return "\xE2\x8B\xA3";
+ break;
+ case 0xA2 :
+ return "\xE2\x8A\xAC";
+ break;
+ case 0xA8 :
+ return "\xE2\x8A\xAD";
+ break;
+ case 0xA9 :
+ return "\xE2\x8A\xAE";
+ break;
+ case 0xAB :
+ return "\xE2\x8A\xAF";
+ break;
+ case 0xB2 :
+ return "\xE2\x8B\xAA";
+ break;
+ case 0xB3 :
+ return "\xE2\x8B\xAB";
+ break;
+ case 0xB4 :
+ return "\xE2\x8B\xAC";
+ break;
+ case 0xB5 :
+ return "\xE2\x8B\xAD";
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+case 0xCD :
+ switch (suffix[1]) {
+ case 0x82 :
+ switch (prefix[0]) {
+ case 0xCE :
+ switch (prefix[1]) {
+ case 0xB1 :
+ return "\xE1\xBE\xB6";
+ break;
+ case 0xB7 :
+ return "\xE1\xBF\x86";
+ break;
+ case 0xB9 :
+ return "\xE1\xBF\x96";
+ break;
+ }
+ break;
+ case 0xCF :
+ switch (prefix[1]) {
+ case 0x85 :
+ return "\xE1\xBF\xA6";
+ break;
+ case 0x89 :
+ return "\xE1\xBF\xB6";
+ break;
+ case 0x8A :
+ return "\xE1\xBF\x97";
+ break;
+ case 0x8B :
+ return "\xE1\xBF\xA7";
+ break;
+ }
+ break;
+ case 0xE1 :
+ switch (prefix[1]) {
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xE1\xBC\x86";
+ break;
+ case 0x81 :
+ return "\xE1\xBC\x87";
+ break;
+ case 0x88 :
+ return "\xE1\xBC\x8E";
+ break;
+ case 0x89 :
+ return "\xE1\xBC\x8F";
+ break;
+ case 0xA0 :
+ return "\xE1\xBC\xA6";
+ break;
+ case 0xA1 :
+ return "\xE1\xBC\xA7";
+ break;
+ case 0xA8 :
+ return "\xE1\xBC\xAE";
+ break;
+ case 0xA9 :
+ return "\xE1\xBC\xAF";
+ break;
+ case 0xB0 :
+ return "\xE1\xBC\xB6";
+ break;
+ case 0xB1 :
+ return "\xE1\xBC\xB7";
+ break;
+ case 0xB8 :
+ return "\xE1\xBC\xBE";
+ break;
+ case 0xB9 :
+ return "\xE1\xBC\xBF";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xE1\xBD\x96";
+ break;
+ case 0x91 :
+ return "\xE1\xBD\x97";
+ break;
+ case 0x99 :
+ return "\xE1\xBD\x9F";
+ break;
+ case 0xA0 :
+ return "\xE1\xBD\xA6";
+ break;
+ case 0xA1 :
+ return "\xE1\xBD\xA7";
+ break;
+ case 0xA8 :
+ return "\xE1\xBD\xAE";
+ break;
+ case 0xA9 :
+ return "\xE1\xBD\xAF";
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[0]) {
+ case 0xCE :
+ switch (prefix[1]) {
+ case 0x91 :
+ return "\xE1\xBE\xBC";
+ break;
+ case 0x97 :
+ return "\xE1\xBF\x8C";
+ break;
+ case 0xA9 :
+ return "\xE1\xBF\xBC";
+ break;
+ case 0xAC :
+ return "\xE1\xBE\xB4";
+ break;
+ case 0xAE :
+ return "\xE1\xBF\x84";
+ break;
+ case 0xB1 :
+ return "\xE1\xBE\xB3";
+ break;
+ case 0xB7 :
+ return "\xE1\xBF\x83";
+ break;
+ }
+ break;
+ case 0xCF :
+ switch (prefix[1]) {
+ case 0x89 :
+ return "\xE1\xBF\xB3";
+ break;
+ case 0x8E :
+ return "\xE1\xBF\xB4";
+ break;
+ }
+ break;
+ case 0xE1 :
+ switch (prefix[1]) {
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xE1\xBE\x80";
+ break;
+ case 0x81 :
+ return "\xE1\xBE\x81";
+ break;
+ case 0x82 :
+ return "\xE1\xBE\x82";
+ break;
+ case 0x83 :
+ return "\xE1\xBE\x83";
+ break;
+ case 0x84 :
+ return "\xE1\xBE\x84";
+ break;
+ case 0x85 :
+ return "\xE1\xBE\x85";
+ break;
+ case 0x86 :
+ return "\xE1\xBE\x86";
+ break;
+ case 0x87 :
+ return "\xE1\xBE\x87";
+ break;
+ case 0x88 :
+ return "\xE1\xBE\x88";
+ break;
+ case 0x89 :
+ return "\xE1\xBE\x89";
+ break;
+ case 0x8A :
+ return "\xE1\xBE\x8A";
+ break;
+ case 0x8B :
+ return "\xE1\xBE\x8B";
+ break;
+ case 0x8C :
+ return "\xE1\xBE\x8C";
+ break;
+ case 0x8D :
+ return "\xE1\xBE\x8D";
+ break;
+ case 0x8E :
+ return "\xE1\xBE\x8E";
+ break;
+ case 0x8F :
+ return "\xE1\xBE\x8F";
+ break;
+ case 0xA0 :
+ return "\xE1\xBE\x90";
+ break;
+ case 0xA1 :
+ return "\xE1\xBE\x91";
+ break;
+ case 0xA2 :
+ return "\xE1\xBE\x92";
+ break;
+ case 0xA3 :
+ return "\xE1\xBE\x93";
+ break;
+ case 0xA4 :
+ return "\xE1\xBE\x94";
+ break;
+ case 0xA5 :
+ return "\xE1\xBE\x95";
+ break;
+ case 0xA6 :
+ return "\xE1\xBE\x96";
+ break;
+ case 0xA7 :
+ return "\xE1\xBE\x97";
+ break;
+ case 0xA8 :
+ return "\xE1\xBE\x98";
+ break;
+ case 0xA9 :
+ return "\xE1\xBE\x99";
+ break;
+ case 0xAA :
+ return "\xE1\xBE\x9A";
+ break;
+ case 0xAB :
+ return "\xE1\xBE\x9B";
+ break;
+ case 0xAC :
+ return "\xE1\xBE\x9C";
+ break;
+ case 0xAD :
+ return "\xE1\xBE\x9D";
+ break;
+ case 0xAE :
+ return "\xE1\xBE\x9E";
+ break;
+ case 0xAF :
+ return "\xE1\xBE\x9F";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0xA0 :
+ return "\xE1\xBE\xA0";
+ break;
+ case 0xA1 :
+ return "\xE1\xBE\xA1";
+ break;
+ case 0xA2 :
+ return "\xE1\xBE\xA2";
+ break;
+ case 0xA3 :
+ return "\xE1\xBE\xA3";
+ break;
+ case 0xA4 :
+ return "\xE1\xBE\xA4";
+ break;
+ case 0xA5 :
+ return "\xE1\xBE\xA5";
+ break;
+ case 0xA6 :
+ return "\xE1\xBE\xA6";
+ break;
+ case 0xA7 :
+ return "\xE1\xBE\xA7";
+ break;
+ case 0xA8 :
+ return "\xE1\xBE\xA8";
+ break;
+ case 0xA9 :
+ return "\xE1\xBE\xA9";
+ break;
+ case 0xAA :
+ return "\xE1\xBE\xAA";
+ break;
+ case 0xAB :
+ return "\xE1\xBE\xAB";
+ break;
+ case 0xAC :
+ return "\xE1\xBE\xAC";
+ break;
+ case 0xAD :
+ return "\xE1\xBE\xAD";
+ break;
+ case 0xAE :
+ return "\xE1\xBE\xAE";
+ break;
+ case 0xAF :
+ return "\xE1\xBE\xAF";
+ break;
+ case 0xB0 :
+ return "\xE1\xBE\xB2";
+ break;
+ case 0xB4 :
+ return "\xE1\xBF\x82";
+ break;
+ case 0xBC :
+ return "\xE1\xBF\xB2";
+ break;
+ }
+ break;
+ case 0xBE :
+ if (prefix[2] == 0xB6) {
+ return "\xE1\xBE\xB7";
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x86 :
+ return "\xE1\xBF\x87";
+ break;
+ case 0xB6 :
+ return "\xE1\xBF\xB7";
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+case 0xD9 :
+ switch (suffix[1]) {
+ case 0x93 :
+ if (prefix[0] == 0xD8) {
+ if (prefix[1] == 0xA7) {
+ return "\xD8\xA2";
+ }
+ }
+ break;
+ case 0x94 :
+ switch (prefix[0]) {
+ case 0xD8 :
+ if (prefix[1] == 0xA7) {
+ return "\xD8\xA3";
+ }
+ break;
+ case 0xD9 :
+ switch (prefix[1]) {
+ case 0x88 :
+ return "\xD8\xA4";
+ break;
+ case 0x8A :
+ return "\xD8\xA6";
+ break;
+ }
+ break;
+ case 0xDB :
+ switch (prefix[1]) {
+ case 0x81 :
+ return "\xDB\x82";
+ break;
+ case 0x92 :
+ return "\xDB\x93";
+ break;
+ case 0x95 :
+ return "\xDB\x80";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x95 :
+ if (prefix[0] == 0xD8) {
+ if (prefix[1] == 0xA7) {
+ return "\xD8\xA5";
+ }
+ }
+ break;
+ }
+ break;
+case 0xE0 :
+ switch (suffix[1]) {
+ case 0xA4 :
+ if (suffix[2] == 0xBC) {
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xA4) {
+ switch (prefix[2]) {
+ case 0xA8 :
+ return "\xE0\xA4\xA9";
+ break;
+ case 0xB0 :
+ return "\xE0\xA4\xB1";
+ break;
+ case 0xB3 :
+ return "\xE0\xA4\xB4";
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case 0xA6 :
+ if (suffix[2] == 0xBE) {
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xA7) {
+ if (prefix[2] == 0x87) {
+ return "\xE0\xA7\x8B";
+ }
+ }
+ }
+ }
+ break;
+ case 0xA7 :
+ if (suffix[2] == 0x97) {
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xA7) {
+ if (prefix[2] == 0x87) {
+ return "\xE0\xA7\x8C";
+ }
+ }
+ }
+ }
+ break;
+ case 0xAC :
+ if (suffix[2] == 0xBE) {
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xAD) {
+ if (prefix[2] == 0x87) {
+ return "\xE0\xAD\x8B";
+ }
+ }
+ }
+ }
+ break;
+ case 0xAD :
+ switch (suffix[2]) {
+ case 0x96 :
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xAD) {
+ if (prefix[2] == 0x87) {
+ return "\xE0\xAD\x88";
+ }
+ }
+ }
+ break;
+ case 0x97 :
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xAD) {
+ if (prefix[2] == 0x87) {
+ return "\xE0\xAD\x8C";
+ }
+ }
+ }
+ break;
+ }
+ break;
+ case 0xAE :
+ if (suffix[2] == 0xBE) {
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xAF) {
+ switch (prefix[2]) {
+ case 0x86 :
+ return "\xE0\xAF\x8A";
+ break;
+ case 0x87 :
+ return "\xE0\xAF\x8B";
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case 0xAF :
+ if (suffix[2] == 0x97) {
+ if (prefix[0] == 0xE0) {
+ switch (prefix[1]) {
+ case 0xAE :
+ if (prefix[2] == 0x92) {
+ return "\xE0\xAE\x94";
+ }
+ break;
+ case 0xAF :
+ if (prefix[2] == 0x86) {
+ return "\xE0\xAF\x8C";
+ }
+ break;
+ }
+ }
+ }
+ break;
+ case 0xB1 :
+ if (suffix[2] == 0x96) {
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xB1) {
+ if (prefix[2] == 0x86) {
+ return "\xE0\xB1\x88";
+ }
+ }
+ }
+ }
+ break;
+ case 0xB3 :
+ switch (suffix[2]) {
+ case 0x82 :
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xB3) {
+ if (prefix[2] == 0x86) {
+ return "\xE0\xB3\x8A";
+ }
+ }
+ }
+ break;
+ case 0x95 :
+ if (prefix[0] == 0xE0) {
+ switch (prefix[1]) {
+ case 0xB2 :
+ if (prefix[2] == 0xBF) {
+ return "\xE0\xB3\x80";
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x86 :
+ return "\xE0\xB3\x87";
+ break;
+ case 0x8A :
+ return "\xE0\xB3\x8B";
+ break;
+ }
+ break;
+ }
+ }
+ break;
+ case 0x96 :
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xB3) {
+ if (prefix[2] == 0x86) {
+ return "\xE0\xB3\x88";
+ }
+ }
+ }
+ break;
+ }
+ break;
+ case 0xB4 :
+ if (suffix[2] == 0xBE) {
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xB5) {
+ switch (prefix[2]) {
+ case 0x86 :
+ return "\xE0\xB5\x8A";
+ break;
+ case 0x87 :
+ return "\xE0\xB5\x8B";
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case 0xB5 :
+ if (suffix[2] == 0x97) {
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xB5) {
+ if (prefix[2] == 0x86) {
+ return "\xE0\xB5\x8C";
+ }
+ }
+ }
+ }
+ break;
+ case 0xB7 :
+ switch (suffix[2]) {
+ case 0x8A :
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xB7) {
+ switch (prefix[2]) {
+ case 0x99 :
+ return "\xE0\xB7\x9A";
+ break;
+ case 0x9C :
+ return "\xE0\xB7\x9D";
+ break;
+ }
+ }
+ }
+ break;
+ case 0x8F :
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xB7) {
+ if (prefix[2] == 0x99) {
+ return "\xE0\xB7\x9C";
+ }
+ }
+ }
+ break;
+ case 0x9F :
+ if (prefix[0] == 0xE0) {
+ if (prefix[1] == 0xB7) {
+ if (prefix[2] == 0x99) {
+ return "\xE0\xB7\x9E";
+ }
+ }
+ }
+ break;
+ }
+ break;
+ }
+ break;
+case 0xE1 :
+ switch (suffix[1]) {
+ case 0x80 :
+ if (suffix[2] == 0xAE) {
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x80) {
+ if (prefix[2] == 0xA5) {
+ return "\xE1\x80\xA6";
+ }
+ }
+ }
+ }
+ break;
+ case 0x85 :
+ switch (suffix[2]) {
+ case 0xA1 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x80";
+ break;
+ case 0x81 :
+ return "\xEA\xB9\x8C";
+ break;
+ case 0x82 :
+ return "\xEB\x82\x98";
+ break;
+ case 0x83 :
+ return "\xEB\x8B\xA4";
+ break;
+ case 0x84 :
+ return "\xEB\x94\xB0";
+ break;
+ case 0x85 :
+ return "\xEB\x9D\xBC";
+ break;
+ case 0x86 :
+ return "\xEB\xA7\x88";
+ break;
+ case 0x87 :
+ return "\xEB\xB0\x94";
+ break;
+ case 0x88 :
+ return "\xEB\xB9\xA0";
+ break;
+ case 0x89 :
+ return "\xEC\x82\xAC";
+ break;
+ case 0x8A :
+ return "\xEC\x8B\xB8";
+ break;
+ case 0x8B :
+ return "\xEC\x95\x84";
+ break;
+ case 0x8C :
+ return "\xEC\x9E\x90";
+ break;
+ case 0x8D :
+ return "\xEC\xA7\x9C";
+ break;
+ case 0x8E :
+ return "\xEC\xB0\xA8";
+ break;
+ case 0x8F :
+ return "\xEC\xB9\xB4";
+ break;
+ case 0x90 :
+ return "\xED\x83\x80";
+ break;
+ case 0x91 :
+ return "\xED\x8C\x8C";
+ break;
+ case 0x92 :
+ return "\xED\x95\x98";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xA2 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x9C";
+ break;
+ case 0x81 :
+ return "\xEA\xB9\xA8";
+ break;
+ case 0x82 :
+ return "\xEB\x82\xB4";
+ break;
+ case 0x83 :
+ return "\xEB\x8C\x80";
+ break;
+ case 0x84 :
+ return "\xEB\x95\x8C";
+ break;
+ case 0x85 :
+ return "\xEB\x9E\x98";
+ break;
+ case 0x86 :
+ return "\xEB\xA7\xA4";
+ break;
+ case 0x87 :
+ return "\xEB\xB0\xB0";
+ break;
+ case 0x88 :
+ return "\xEB\xB9\xBC";
+ break;
+ case 0x89 :
+ return "\xEC\x83\x88";
+ break;
+ case 0x8A :
+ return "\xEC\x8C\x94";
+ break;
+ case 0x8B :
+ return "\xEC\x95\xA0";
+ break;
+ case 0x8C :
+ return "\xEC\x9E\xAC";
+ break;
+ case 0x8D :
+ return "\xEC\xA7\xB8";
+ break;
+ case 0x8E :
+ return "\xEC\xB1\x84";
+ break;
+ case 0x8F :
+ return "\xEC\xBA\x90";
+ break;
+ case 0x90 :
+ return "\xED\x83\x9C";
+ break;
+ case 0x91 :
+ return "\xED\x8C\xA8";
+ break;
+ case 0x92 :
+ return "\xED\x95\xB4";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xA3 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\xB8";
+ break;
+ case 0x81 :
+ return "\xEA\xBA\x84";
+ break;
+ case 0x82 :
+ return "\xEB\x83\x90";
+ break;
+ case 0x83 :
+ return "\xEB\x8C\x9C";
+ break;
+ case 0x84 :
+ return "\xEB\x95\xA8";
+ break;
+ case 0x85 :
+ return "\xEB\x9E\xB4";
+ break;
+ case 0x86 :
+ return "\xEB\xA8\x80";
+ break;
+ case 0x87 :
+ return "\xEB\xB1\x8C";
+ break;
+ case 0x88 :
+ return "\xEB\xBA\x98";
+ break;
+ case 0x89 :
+ return "\xEC\x83\xA4";
+ break;
+ case 0x8A :
+ return "\xEC\x8C\xB0";
+ break;
+ case 0x8B :
+ return "\xEC\x95\xBC";
+ break;
+ case 0x8C :
+ return "\xEC\x9F\x88";
+ break;
+ case 0x8D :
+ return "\xEC\xA8\x94";
+ break;
+ case 0x8E :
+ return "\xEC\xB1\xA0";
+ break;
+ case 0x8F :
+ return "\xEC\xBA\xAC";
+ break;
+ case 0x90 :
+ return "\xED\x83\xB8";
+ break;
+ case 0x91 :
+ return "\xED\x8D\x84";
+ break;
+ case 0x92 :
+ return "\xED\x96\x90";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xA4 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB1\x94";
+ break;
+ case 0x81 :
+ return "\xEA\xBA\xA0";
+ break;
+ case 0x82 :
+ return "\xEB\x83\xAC";
+ break;
+ case 0x83 :
+ return "\xEB\x8C\xB8";
+ break;
+ case 0x84 :
+ return "\xEB\x96\x84";
+ break;
+ case 0x85 :
+ return "\xEB\x9F\x90";
+ break;
+ case 0x86 :
+ return "\xEB\xA8\x9C";
+ break;
+ case 0x87 :
+ return "\xEB\xB1\xA8";
+ break;
+ case 0x88 :
+ return "\xEB\xBA\xB4";
+ break;
+ case 0x89 :
+ return "\xEC\x84\x80";
+ break;
+ case 0x8A :
+ return "\xEC\x8D\x8C";
+ break;
+ case 0x8B :
+ return "\xEC\x96\x98";
+ break;
+ case 0x8C :
+ return "\xEC\x9F\xA4";
+ break;
+ case 0x8D :
+ return "\xEC\xA8\xB0";
+ break;
+ case 0x8E :
+ return "\xEC\xB1\xBC";
+ break;
+ case 0x8F :
+ return "\xEC\xBB\x88";
+ break;
+ case 0x90 :
+ return "\xED\x84\x94";
+ break;
+ case 0x91 :
+ return "\xED\x8D\xA0";
+ break;
+ case 0x92 :
+ return "\xED\x96\xAC";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xA5 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB1\xB0";
+ break;
+ case 0x81 :
+ return "\xEA\xBA\xBC";
+ break;
+ case 0x82 :
+ return "\xEB\x84\x88";
+ break;
+ case 0x83 :
+ return "\xEB\x8D\x94";
+ break;
+ case 0x84 :
+ return "\xEB\x96\xA0";
+ break;
+ case 0x85 :
+ return "\xEB\x9F\xAC";
+ break;
+ case 0x86 :
+ return "\xEB\xA8\xB8";
+ break;
+ case 0x87 :
+ return "\xEB\xB2\x84";
+ break;
+ case 0x88 :
+ return "\xEB\xBB\x90";
+ break;
+ case 0x89 :
+ return "\xEC\x84\x9C";
+ break;
+ case 0x8A :
+ return "\xEC\x8D\xA8";
+ break;
+ case 0x8B :
+ return "\xEC\x96\xB4";
+ break;
+ case 0x8C :
+ return "\xEC\xA0\x80";
+ break;
+ case 0x8D :
+ return "\xEC\xA9\x8C";
+ break;
+ case 0x8E :
+ return "\xEC\xB2\x98";
+ break;
+ case 0x8F :
+ return "\xEC\xBB\xA4";
+ break;
+ case 0x90 :
+ return "\xED\x84\xB0";
+ break;
+ case 0x91 :
+ return "\xED\x8D\xBC";
+ break;
+ case 0x92 :
+ return "\xED\x97\x88";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xA6 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB2\x8C";
+ break;
+ case 0x81 :
+ return "\xEA\xBB\x98";
+ break;
+ case 0x82 :
+ return "\xEB\x84\xA4";
+ break;
+ case 0x83 :
+ return "\xEB\x8D\xB0";
+ break;
+ case 0x84 :
+ return "\xEB\x96\xBC";
+ break;
+ case 0x85 :
+ return "\xEB\xA0\x88";
+ break;
+ case 0x86 :
+ return "\xEB\xA9\x94";
+ break;
+ case 0x87 :
+ return "\xEB\xB2\xA0";
+ break;
+ case 0x88 :
+ return "\xEB\xBB\xAC";
+ break;
+ case 0x89 :
+ return "\xEC\x84\xB8";
+ break;
+ case 0x8A :
+ return "\xEC\x8E\x84";
+ break;
+ case 0x8B :
+ return "\xEC\x97\x90";
+ break;
+ case 0x8C :
+ return "\xEC\xA0\x9C";
+ break;
+ case 0x8D :
+ return "\xEC\xA9\xA8";
+ break;
+ case 0x8E :
+ return "\xEC\xB2\xB4";
+ break;
+ case 0x8F :
+ return "\xEC\xBC\x80";
+ break;
+ case 0x90 :
+ return "\xED\x85\x8C";
+ break;
+ case 0x91 :
+ return "\xED\x8E\x98";
+ break;
+ case 0x92 :
+ return "\xED\x97\xA4";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xA7 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB2\xA8";
+ break;
+ case 0x81 :
+ return "\xEA\xBB\xB4";
+ break;
+ case 0x82 :
+ return "\xEB\x85\x80";
+ break;
+ case 0x83 :
+ return "\xEB\x8E\x8C";
+ break;
+ case 0x84 :
+ return "\xEB\x97\x98";
+ break;
+ case 0x85 :
+ return "\xEB\xA0\xA4";
+ break;
+ case 0x86 :
+ return "\xEB\xA9\xB0";
+ break;
+ case 0x87 :
+ return "\xEB\xB2\xBC";
+ break;
+ case 0x88 :
+ return "\xEB\xBC\x88";
+ break;
+ case 0x89 :
+ return "\xEC\x85\x94";
+ break;
+ case 0x8A :
+ return "\xEC\x8E\xA0";
+ break;
+ case 0x8B :
+ return "\xEC\x97\xAC";
+ break;
+ case 0x8C :
+ return "\xEC\xA0\xB8";
+ break;
+ case 0x8D :
+ return "\xEC\xAA\x84";
+ break;
+ case 0x8E :
+ return "\xEC\xB3\x90";
+ break;
+ case 0x8F :
+ return "\xEC\xBC\x9C";
+ break;
+ case 0x90 :
+ return "\xED\x85\xA8";
+ break;
+ case 0x91 :
+ return "\xED\x8E\xB4";
+ break;
+ case 0x92 :
+ return "\xED\x98\x80";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xA8 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB3\x84";
+ break;
+ case 0x81 :
+ return "\xEA\xBC\x90";
+ break;
+ case 0x82 :
+ return "\xEB\x85\x9C";
+ break;
+ case 0x83 :
+ return "\xEB\x8E\xA8";
+ break;
+ case 0x84 :
+ return "\xEB\x97\xB4";
+ break;
+ case 0x85 :
+ return "\xEB\xA1\x80";
+ break;
+ case 0x86 :
+ return "\xEB\xAA\x8C";
+ break;
+ case 0x87 :
+ return "\xEB\xB3\x98";
+ break;
+ case 0x88 :
+ return "\xEB\xBC\xA4";
+ break;
+ case 0x89 :
+ return "\xEC\x85\xB0";
+ break;
+ case 0x8A :
+ return "\xEC\x8E\xBC";
+ break;
+ case 0x8B :
+ return "\xEC\x98\x88";
+ break;
+ case 0x8C :
+ return "\xEC\xA1\x94";
+ break;
+ case 0x8D :
+ return "\xEC\xAA\xA0";
+ break;
+ case 0x8E :
+ return "\xEC\xB3\xAC";
+ break;
+ case 0x8F :
+ return "\xEC\xBC\xB8";
+ break;
+ case 0x90 :
+ return "\xED\x86\x84";
+ break;
+ case 0x91 :
+ return "\xED\x8F\x90";
+ break;
+ case 0x92 :
+ return "\xED\x98\x9C";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xA9 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB3\xA0";
+ break;
+ case 0x81 :
+ return "\xEA\xBC\xAC";
+ break;
+ case 0x82 :
+ return "\xEB\x85\xB8";
+ break;
+ case 0x83 :
+ return "\xEB\x8F\x84";
+ break;
+ case 0x84 :
+ return "\xEB\x98\x90";
+ break;
+ case 0x85 :
+ return "\xEB\xA1\x9C";
+ break;
+ case 0x86 :
+ return "\xEB\xAA\xA8";
+ break;
+ case 0x87 :
+ return "\xEB\xB3\xB4";
+ break;
+ case 0x88 :
+ return "\xEB\xBD\x80";
+ break;
+ case 0x89 :
+ return "\xEC\x86\x8C";
+ break;
+ case 0x8A :
+ return "\xEC\x8F\x98";
+ break;
+ case 0x8B :
+ return "\xEC\x98\xA4";
+ break;
+ case 0x8C :
+ return "\xEC\xA1\xB0";
+ break;
+ case 0x8D :
+ return "\xEC\xAA\xBC";
+ break;
+ case 0x8E :
+ return "\xEC\xB4\x88";
+ break;
+ case 0x8F :
+ return "\xEC\xBD\x94";
+ break;
+ case 0x90 :
+ return "\xED\x86\xA0";
+ break;
+ case 0x91 :
+ return "\xED\x8F\xAC";
+ break;
+ case 0x92 :
+ return "\xED\x98\xB8";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xAA :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB3\xBC";
+ break;
+ case 0x81 :
+ return "\xEA\xBD\x88";
+ break;
+ case 0x82 :
+ return "\xEB\x86\x94";
+ break;
+ case 0x83 :
+ return "\xEB\x8F\xA0";
+ break;
+ case 0x84 :
+ return "\xEB\x98\xAC";
+ break;
+ case 0x85 :
+ return "\xEB\xA1\xB8";
+ break;
+ case 0x86 :
+ return "\xEB\xAB\x84";
+ break;
+ case 0x87 :
+ return "\xEB\xB4\x90";
+ break;
+ case 0x88 :
+ return "\xEB\xBD\x9C";
+ break;
+ case 0x89 :
+ return "\xEC\x86\xA8";
+ break;
+ case 0x8A :
+ return "\xEC\x8F\xB4";
+ break;
+ case 0x8B :
+ return "\xEC\x99\x80";
+ break;
+ case 0x8C :
+ return "\xEC\xA2\x8C";
+ break;
+ case 0x8D :
+ return "\xEC\xAB\x98";
+ break;
+ case 0x8E :
+ return "\xEC\xB4\xA4";
+ break;
+ case 0x8F :
+ return "\xEC\xBD\xB0";
+ break;
+ case 0x90 :
+ return "\xED\x86\xBC";
+ break;
+ case 0x91 :
+ return "\xED\x90\x88";
+ break;
+ case 0x92 :
+ return "\xED\x99\x94";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xAB :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB4\x98";
+ break;
+ case 0x81 :
+ return "\xEA\xBD\xA4";
+ break;
+ case 0x82 :
+ return "\xEB\x86\xB0";
+ break;
+ case 0x83 :
+ return "\xEB\x8F\xBC";
+ break;
+ case 0x84 :
+ return "\xEB\x99\x88";
+ break;
+ case 0x85 :
+ return "\xEB\xA2\x94";
+ break;
+ case 0x86 :
+ return "\xEB\xAB\xA0";
+ break;
+ case 0x87 :
+ return "\xEB\xB4\xAC";
+ break;
+ case 0x88 :
+ return "\xEB\xBD\xB8";
+ break;
+ case 0x89 :
+ return "\xEC\x87\x84";
+ break;
+ case 0x8A :
+ return "\xEC\x90\x90";
+ break;
+ case 0x8B :
+ return "\xEC\x99\x9C";
+ break;
+ case 0x8C :
+ return "\xEC\xA2\xA8";
+ break;
+ case 0x8D :
+ return "\xEC\xAB\xB4";
+ break;
+ case 0x8E :
+ return "\xEC\xB5\x80";
+ break;
+ case 0x8F :
+ return "\xEC\xBE\x8C";
+ break;
+ case 0x90 :
+ return "\xED\x87\x98";
+ break;
+ case 0x91 :
+ return "\xED\x90\xA4";
+ break;
+ case 0x92 :
+ return "\xED\x99\xB0";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xAC :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB4\xB4";
+ break;
+ case 0x81 :
+ return "\xEA\xBE\x80";
+ break;
+ case 0x82 :
+ return "\xEB\x87\x8C";
+ break;
+ case 0x83 :
+ return "\xEB\x90\x98";
+ break;
+ case 0x84 :
+ return "\xEB\x99\xA4";
+ break;
+ case 0x85 :
+ return "\xEB\xA2\xB0";
+ break;
+ case 0x86 :
+ return "\xEB\xAB\xBC";
+ break;
+ case 0x87 :
+ return "\xEB\xB5\x88";
+ break;
+ case 0x88 :
+ return "\xEB\xBE\x94";
+ break;
+ case 0x89 :
+ return "\xEC\x87\xA0";
+ break;
+ case 0x8A :
+ return "\xEC\x90\xAC";
+ break;
+ case 0x8B :
+ return "\xEC\x99\xB8";
+ break;
+ case 0x8C :
+ return "\xEC\xA3\x84";
+ break;
+ case 0x8D :
+ return "\xEC\xAC\x90";
+ break;
+ case 0x8E :
+ return "\xEC\xB5\x9C";
+ break;
+ case 0x8F :
+ return "\xEC\xBE\xA8";
+ break;
+ case 0x90 :
+ return "\xED\x87\xB4";
+ break;
+ case 0x91 :
+ return "\xED\x91\x80";
+ break;
+ case 0x92 :
+ return "\xED\x9A\x8C";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xAD :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB5\x90";
+ break;
+ case 0x81 :
+ return "\xEA\xBE\x9C";
+ break;
+ case 0x82 :
+ return "\xEB\x87\xA8";
+ break;
+ case 0x83 :
+ return "\xEB\x90\xB4";
+ break;
+ case 0x84 :
+ return "\xEB\x9A\x80";
+ break;
+ case 0x85 :
+ return "\xEB\xA3\x8C";
+ break;
+ case 0x86 :
+ return "\xEB\xAC\x98";
+ break;
+ case 0x87 :
+ return "\xEB\xB5\xA4";
+ break;
+ case 0x88 :
+ return "\xEB\xBE\xB0";
+ break;
+ case 0x89 :
+ return "\xEC\x87\xBC";
+ break;
+ case 0x8A :
+ return "\xEC\x91\x88";
+ break;
+ case 0x8B :
+ return "\xEC\x9A\x94";
+ break;
+ case 0x8C :
+ return "\xEC\xA3\xA0";
+ break;
+ case 0x8D :
+ return "\xEC\xAC\xAC";
+ break;
+ case 0x8E :
+ return "\xEC\xB5\xB8";
+ break;
+ case 0x8F :
+ return "\xEC\xBF\x84";
+ break;
+ case 0x90 :
+ return "\xED\x88\x90";
+ break;
+ case 0x91 :
+ return "\xED\x91\x9C";
+ break;
+ case 0x92 :
+ return "\xED\x9A\xA8";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xAE :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB5\xAC";
+ break;
+ case 0x81 :
+ return "\xEA\xBE\xB8";
+ break;
+ case 0x82 :
+ return "\xEB\x88\x84";
+ break;
+ case 0x83 :
+ return "\xEB\x91\x90";
+ break;
+ case 0x84 :
+ return "\xEB\x9A\x9C";
+ break;
+ case 0x85 :
+ return "\xEB\xA3\xA8";
+ break;
+ case 0x86 :
+ return "\xEB\xAC\xB4";
+ break;
+ case 0x87 :
+ return "\xEB\xB6\x80";
+ break;
+ case 0x88 :
+ return "\xEB\xBF\x8C";
+ break;
+ case 0x89 :
+ return "\xEC\x88\x98";
+ break;
+ case 0x8A :
+ return "\xEC\x91\xA4";
+ break;
+ case 0x8B :
+ return "\xEC\x9A\xB0";
+ break;
+ case 0x8C :
+ return "\xEC\xA3\xBC";
+ break;
+ case 0x8D :
+ return "\xEC\xAD\x88";
+ break;
+ case 0x8E :
+ return "\xEC\xB6\x94";
+ break;
+ case 0x8F :
+ return "\xEC\xBF\xA0";
+ break;
+ case 0x90 :
+ return "\xED\x88\xAC";
+ break;
+ case 0x91 :
+ return "\xED\x91\xB8";
+ break;
+ case 0x92 :
+ return "\xED\x9B\x84";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xAF :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB6\x88";
+ break;
+ case 0x81 :
+ return "\xEA\xBF\x94";
+ break;
+ case 0x82 :
+ return "\xEB\x88\xA0";
+ break;
+ case 0x83 :
+ return "\xEB\x91\xAC";
+ break;
+ case 0x84 :
+ return "\xEB\x9A\xB8";
+ break;
+ case 0x85 :
+ return "\xEB\xA4\x84";
+ break;
+ case 0x86 :
+ return "\xEB\xAD\x90";
+ break;
+ case 0x87 :
+ return "\xEB\xB6\x9C";
+ break;
+ case 0x88 :
+ return "\xEB\xBF\xA8";
+ break;
+ case 0x89 :
+ return "\xEC\x88\xB4";
+ break;
+ case 0x8A :
+ return "\xEC\x92\x80";
+ break;
+ case 0x8B :
+ return "\xEC\x9B\x8C";
+ break;
+ case 0x8C :
+ return "\xEC\xA4\x98";
+ break;
+ case 0x8D :
+ return "\xEC\xAD\xA4";
+ break;
+ case 0x8E :
+ return "\xEC\xB6\xB0";
+ break;
+ case 0x8F :
+ return "\xEC\xBF\xBC";
+ break;
+ case 0x90 :
+ return "\xED\x89\x88";
+ break;
+ case 0x91 :
+ return "\xED\x92\x94";
+ break;
+ case 0x92 :
+ return "\xED\x9B\xA0";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xB0 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB6\xA4";
+ break;
+ case 0x81 :
+ return "\xEA\xBF\xB0";
+ break;
+ case 0x82 :
+ return "\xEB\x88\xBC";
+ break;
+ case 0x83 :
+ return "\xEB\x92\x88";
+ break;
+ case 0x84 :
+ return "\xEB\x9B\x94";
+ break;
+ case 0x85 :
+ return "\xEB\xA4\xA0";
+ break;
+ case 0x86 :
+ return "\xEB\xAD\xAC";
+ break;
+ case 0x87 :
+ return "\xEB\xB6\xB8";
+ break;
+ case 0x88 :
+ return "\xEC\x80\x84";
+ break;
+ case 0x89 :
+ return "\xEC\x89\x90";
+ break;
+ case 0x8A :
+ return "\xEC\x92\x9C";
+ break;
+ case 0x8B :
+ return "\xEC\x9B\xA8";
+ break;
+ case 0x8C :
+ return "\xEC\xA4\xB4";
+ break;
+ case 0x8D :
+ return "\xEC\xAE\x80";
+ break;
+ case 0x8E :
+ return "\xEC\xB7\x8C";
+ break;
+ case 0x8F :
+ return "\xED\x80\x98";
+ break;
+ case 0x90 :
+ return "\xED\x89\xA4";
+ break;
+ case 0x91 :
+ return "\xED\x92\xB0";
+ break;
+ case 0x92 :
+ return "\xED\x9B\xBC";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xB1 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x80";
+ break;
+ case 0x81 :
+ return "\xEB\x80\x8C";
+ break;
+ case 0x82 :
+ return "\xEB\x89\x98";
+ break;
+ case 0x83 :
+ return "\xEB\x92\xA4";
+ break;
+ case 0x84 :
+ return "\xEB\x9B\xB0";
+ break;
+ case 0x85 :
+ return "\xEB\xA4\xBC";
+ break;
+ case 0x86 :
+ return "\xEB\xAE\x88";
+ break;
+ case 0x87 :
+ return "\xEB\xB7\x94";
+ break;
+ case 0x88 :
+ return "\xEC\x80\xA0";
+ break;
+ case 0x89 :
+ return "\xEC\x89\xAC";
+ break;
+ case 0x8A :
+ return "\xEC\x92\xB8";
+ break;
+ case 0x8B :
+ return "\xEC\x9C\x84";
+ break;
+ case 0x8C :
+ return "\xEC\xA5\x90";
+ break;
+ case 0x8D :
+ return "\xEC\xAE\x9C";
+ break;
+ case 0x8E :
+ return "\xEC\xB7\xA8";
+ break;
+ case 0x8F :
+ return "\xED\x80\xB4";
+ break;
+ case 0x90 :
+ return "\xED\x8A\x80";
+ break;
+ case 0x91 :
+ return "\xED\x93\x8C";
+ break;
+ case 0x92 :
+ return "\xED\x9C\x98";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xB2 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x9C";
+ break;
+ case 0x81 :
+ return "\xEB\x80\xA8";
+ break;
+ case 0x82 :
+ return "\xEB\x89\xB4";
+ break;
+ case 0x83 :
+ return "\xEB\x93\x80";
+ break;
+ case 0x84 :
+ return "\xEB\x9C\x8C";
+ break;
+ case 0x85 :
+ return "\xEB\xA5\x98";
+ break;
+ case 0x86 :
+ return "\xEB\xAE\xA4";
+ break;
+ case 0x87 :
+ return "\xEB\xB7\xB0";
+ break;
+ case 0x88 :
+ return "\xEC\x80\xBC";
+ break;
+ case 0x89 :
+ return "\xEC\x8A\x88";
+ break;
+ case 0x8A :
+ return "\xEC\x93\x94";
+ break;
+ case 0x8B :
+ return "\xEC\x9C\xA0";
+ break;
+ case 0x8C :
+ return "\xEC\xA5\xAC";
+ break;
+ case 0x8D :
+ return "\xEC\xAE\xB8";
+ break;
+ case 0x8E :
+ return "\xEC\xB8\x84";
+ break;
+ case 0x8F :
+ return "\xED\x81\x90";
+ break;
+ case 0x90 :
+ return "\xED\x8A\x9C";
+ break;
+ case 0x91 :
+ return "\xED\x93\xA8";
+ break;
+ case 0x92 :
+ return "\xED\x9C\xB4";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xB3 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\xB8";
+ break;
+ case 0x81 :
+ return "\xEB\x81\x84";
+ break;
+ case 0x82 :
+ return "\xEB\x8A\x90";
+ break;
+ case 0x83 :
+ return "\xEB\x93\x9C";
+ break;
+ case 0x84 :
+ return "\xEB\x9C\xA8";
+ break;
+ case 0x85 :
+ return "\xEB\xA5\xB4";
+ break;
+ case 0x86 :
+ return "\xEB\xAF\x80";
+ break;
+ case 0x87 :
+ return "\xEB\xB8\x8C";
+ break;
+ case 0x88 :
+ return "\xEC\x81\x98";
+ break;
+ case 0x89 :
+ return "\xEC\x8A\xA4";
+ break;
+ case 0x8A :
+ return "\xEC\x93\xB0";
+ break;
+ case 0x8B :
+ return "\xEC\x9C\xBC";
+ break;
+ case 0x8C :
+ return "\xEC\xA6\x88";
+ break;
+ case 0x8D :
+ return "\xEC\xAF\x94";
+ break;
+ case 0x8E :
+ return "\xEC\xB8\xA0";
+ break;
+ case 0x8F :
+ return "\xED\x81\xAC";
+ break;
+ case 0x90 :
+ return "\xED\x8A\xB8";
+ break;
+ case 0x91 :
+ return "\xED\x94\x84";
+ break;
+ case 0x92 :
+ return "\xED\x9D\x90";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xB4 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB8\x94";
+ break;
+ case 0x81 :
+ return "\xEB\x81\xA0";
+ break;
+ case 0x82 :
+ return "\xEB\x8A\xAC";
+ break;
+ case 0x83 :
+ return "\xEB\x93\xB8";
+ break;
+ case 0x84 :
+ return "\xEB\x9D\x84";
+ break;
+ case 0x85 :
+ return "\xEB\xA6\x90";
+ break;
+ case 0x86 :
+ return "\xEB\xAF\x9C";
+ break;
+ case 0x87 :
+ return "\xEB\xB8\xA8";
+ break;
+ case 0x88 :
+ return "\xEC\x81\xB4";
+ break;
+ case 0x89 :
+ return "\xEC\x8B\x80";
+ break;
+ case 0x8A :
+ return "\xEC\x94\x8C";
+ break;
+ case 0x8B :
+ return "\xEC\x9D\x98";
+ break;
+ case 0x8C :
+ return "\xEC\xA6\xA4";
+ break;
+ case 0x8D :
+ return "\xEC\xAF\xB0";
+ break;
+ case 0x8E :
+ return "\xEC\xB8\xBC";
+ break;
+ case 0x8F :
+ return "\xED\x82\x88";
+ break;
+ case 0x90 :
+ return "\xED\x8B\x94";
+ break;
+ case 0x91 :
+ return "\xED\x94\xA0";
+ break;
+ case 0x92 :
+ return "\xED\x9D\xAC";
+ break;
+ }
+ }
+ }
+ break;
+ case 0xB5 :
+ if (prefix[0] == 0xE1) {
+ if (prefix[1] == 0x84) {
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB8\xB0";
+ break;
+ case 0x81 :
+ return "\xEB\x81\xBC";
+ break;
+ case 0x82 :
+ return "\xEB\x8B\x88";
+ break;
+ case 0x83 :
+ return "\xEB\x94\x94";
+ break;
+ case 0x84 :
+ return "\xEB\x9D\xA0";
+ break;
+ case 0x85 :
+ return "\xEB\xA6\xAC";
+ break;
+ case 0x86 :
+ return "\xEB\xAF\xB8";
+ break;
+ case 0x87 :
+ return "\xEB\xB9\x84";
+ break;
+ case 0x88 :
+ return "\xEC\x82\x90";
+ break;
+ case 0x89 :
+ return "\xEC\x8B\x9C";
+ break;
+ case 0x8A :
+ return "\xEC\x94\xA8";
+ break;
+ case 0x8B :
+ return "\xEC\x9D\xB4";
+ break;
+ case 0x8C :
+ return "\xEC\xA7\x80";
+ break;
+ case 0x8D :
+ return "\xEC\xB0\x8C";
+ break;
+ case 0x8E :
+ return "\xEC\xB9\x98";
+ break;
+ case 0x8F :
+ return "\xED\x82\xA4";
+ break;
+ case 0x90 :
+ return "\xED\x8B\xB0";
+ break;
+ case 0x91 :
+ return "\xED\x94\xBC";
+ break;
+ case 0x92 :
+ return "\xED\x9E\x88";
+ break;
+ }
+ }
+ }
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (suffix[2]) {
+ case 0xA8 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x81";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\x9D";
+ break;
+ case 0xB8 :
+ return "\xEA\xB0\xB9";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\x95";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xB1";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x8D";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xA9";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x85";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xA1";
+ break;
+ case 0xBC :
+ return "\xEA\xB3\xBD";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\x99";
+ break;
+ case 0xB4 :
+ return "\xEA\xB4\xB5";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x91";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xAD";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x89";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xA5";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x81";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\x9D";
+ break;
+ case 0xB8 :
+ return "\xEA\xB7\xB9";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\x95";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xB1";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x8D";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xA9";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x85";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xA1";
+ break;
+ case 0xBC :
+ return "\xEA\xBA\xBD";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\x99";
+ break;
+ case 0xB4 :
+ return "\xEA\xBB\xB5";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x91";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xAD";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x89";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xA5";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x81";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\x9D";
+ break;
+ case 0xB8 :
+ return "\xEA\xBE\xB9";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\x95";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xB1";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x8D";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xA9";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x85";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xA1";
+ break;
+ case 0xBC :
+ return "\xEB\x81\xBD";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\x99";
+ break;
+ case 0xB4 :
+ return "\xEB\x82\xB5";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x91";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xAD";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x89";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xA5";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x81";
+ break;
+ case 0x9C :
+ return "\xEB\x85\x9D";
+ break;
+ case 0xB8 :
+ return "\xEB\x85\xB9";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\x95";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xB1";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x8D";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xA9";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x85";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xA1";
+ break;
+ case 0xBC :
+ return "\xEB\x88\xBD";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\x99";
+ break;
+ case 0xB4 :
+ return "\xEB\x89\xB5";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x91";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xAD";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x89";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xA5";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x81";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\x9D";
+ break;
+ case 0xB8 :
+ return "\xEB\x8C\xB9";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\x95";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xB1";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x8D";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xA9";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x85";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xA1";
+ break;
+ case 0xBC :
+ return "\xEB\x8F\xBD";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\x99";
+ break;
+ case 0xB4 :
+ return "\xEB\x90\xB5";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x91";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xAD";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x89";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xA5";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x81";
+ break;
+ case 0x9C :
+ return "\xEB\x93\x9D";
+ break;
+ case 0xB8 :
+ return "\xEB\x93\xB9";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\x95";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xB1";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x8D";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xA9";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x85";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xA1";
+ break;
+ case 0xBC :
+ return "\xEB\x96\xBD";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\x99";
+ break;
+ case 0xB4 :
+ return "\xEB\x97\xB5";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x91";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xAD";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x89";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xA5";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x81";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\x9D";
+ break;
+ case 0xB8 :
+ return "\xEB\x9A\xB9";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\x95";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xB1";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x8D";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xA9";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x85";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xA1";
+ break;
+ case 0xBC :
+ return "\xEB\x9D\xBD";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\x99";
+ break;
+ case 0xB4 :
+ return "\xEB\x9E\xB5";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x91";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xAD";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x89";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xA5";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x81";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\x9D";
+ break;
+ case 0xB8 :
+ return "\xEB\xA1\xB9";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\x95";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xB1";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x8D";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xA9";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x85";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xA1";
+ break;
+ case 0xBC :
+ return "\xEB\xA4\xBD";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\x99";
+ break;
+ case 0xB4 :
+ return "\xEB\xA5\xB5";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x91";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xAD";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x89";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xA5";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x81";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\x9D";
+ break;
+ case 0xB8 :
+ return "\xEB\xA8\xB9";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\x95";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xB1";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x8D";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xA9";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x85";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xA1";
+ break;
+ case 0xBC :
+ return "\xEB\xAB\xBD";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\x99";
+ break;
+ case 0xB4 :
+ return "\xEB\xAC\xB5";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x91";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xAD";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x89";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xA5";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x81";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\x9D";
+ break;
+ case 0xB8 :
+ return "\xEB\xAF\xB9";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\x95";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xB1";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x8D";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xA9";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x85";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xA1";
+ break;
+ case 0xBC :
+ return "\xEB\xB2\xBD";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\x99";
+ break;
+ case 0xB4 :
+ return "\xEB\xB3\xB5";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x91";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xAD";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x89";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xA5";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x81";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\x9D";
+ break;
+ case 0xB8 :
+ return "\xEB\xB6\xB9";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\x95";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xB1";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x8D";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xA9";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x85";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xA1";
+ break;
+ case 0xBC :
+ return "\xEB\xB9\xBD";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\x99";
+ break;
+ case 0xB4 :
+ return "\xEB\xBA\xB5";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x91";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xAD";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x89";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xA5";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x81";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\x9D";
+ break;
+ case 0xB8 :
+ return "\xEB\xBD\xB9";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\x95";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xB1";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x8D";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xA9";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x85";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xA1";
+ break;
+ case 0xBC :
+ return "\xEC\x80\xBD";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\x99";
+ break;
+ case 0xB4 :
+ return "\xEC\x81\xB5";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x91";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xAD";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x89";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xA5";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x81";
+ break;
+ case 0x9C :
+ return "\xEC\x84\x9D";
+ break;
+ case 0xB8 :
+ return "\xEC\x84\xB9";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\x95";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xB1";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x8D";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xA9";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x85";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xA1";
+ break;
+ case 0xBC :
+ return "\xEC\x87\xBD";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\x99";
+ break;
+ case 0xB4 :
+ return "\xEC\x88\xB5";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x91";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xAD";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x89";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xA5";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x81";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\x9D";
+ break;
+ case 0xB8 :
+ return "\xEC\x8B\xB9";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\x95";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xB1";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x8D";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xA9";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x85";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xA1";
+ break;
+ case 0xBC :
+ return "\xEC\x8E\xBD";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\x99";
+ break;
+ case 0xB4 :
+ return "\xEC\x8F\xB5";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x91";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xAD";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x89";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xA5";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x81";
+ break;
+ case 0x9C :
+ return "\xEC\x92\x9D";
+ break;
+ case 0xB8 :
+ return "\xEC\x92\xB9";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\x95";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xB1";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x8D";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xA9";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x85";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xA1";
+ break;
+ case 0xBC :
+ return "\xEC\x95\xBD";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\x99";
+ break;
+ case 0xB4 :
+ return "\xEC\x96\xB5";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x91";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xAD";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x89";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xA5";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x81";
+ break;
+ case 0x9C :
+ return "\xEC\x99\x9D";
+ break;
+ case 0xB8 :
+ return "\xEC\x99\xB9";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\x95";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xB1";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x8D";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xA9";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x85";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xA1";
+ break;
+ case 0xBC :
+ return "\xEC\x9C\xBD";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\x99";
+ break;
+ case 0xB4 :
+ return "\xEC\x9D\xB5";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x91";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xAD";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x89";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xA5";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x81";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\x9D";
+ break;
+ case 0xB8 :
+ return "\xEC\xA0\xB9";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\x95";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xB1";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x8D";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xA9";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x85";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xA1";
+ break;
+ case 0xBC :
+ return "\xEC\xA3\xBD";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\x99";
+ break;
+ case 0xB4 :
+ return "\xEC\xA4\xB5";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x91";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xAD";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x89";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xA5";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x81";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\x9D";
+ break;
+ case 0xB8 :
+ return "\xEC\xA7\xB9";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\x95";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xB1";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x8D";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xA9";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x85";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xA1";
+ break;
+ case 0xBC :
+ return "\xEC\xAA\xBD";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\x99";
+ break;
+ case 0xB4 :
+ return "\xEC\xAB\xB5";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x91";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xAD";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x89";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xA5";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x81";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\x9D";
+ break;
+ case 0xB8 :
+ return "\xEC\xAE\xB9";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\x95";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xB1";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x8D";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xA9";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x85";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xA1";
+ break;
+ case 0xBC :
+ return "\xEC\xB1\xBD";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\x99";
+ break;
+ case 0xB4 :
+ return "\xEC\xB2\xB5";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x91";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xAD";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x89";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xA5";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x81";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\x9D";
+ break;
+ case 0xB8 :
+ return "\xEC\xB5\xB9";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\x95";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xB1";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x8D";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xA9";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x85";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xA1";
+ break;
+ case 0xBC :
+ return "\xEC\xB8\xBD";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\x99";
+ break;
+ case 0xB4 :
+ return "\xEC\xB9\xB5";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x91";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xAD";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x89";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xA5";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x81";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\x9D";
+ break;
+ case 0xB8 :
+ return "\xEC\xBC\xB9";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\x95";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xB1";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x8D";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xA9";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x85";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xA1";
+ break;
+ case 0xBC :
+ return "\xEC\xBF\xBD";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\x99";
+ break;
+ case 0xB4 :
+ return "\xED\x80\xB5";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x91";
+ break;
+ case 0xAC :
+ return "\xED\x81\xAD";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x89";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xA5";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x81";
+ break;
+ case 0x9C :
+ return "\xED\x83\x9D";
+ break;
+ case 0xB8 :
+ return "\xED\x83\xB9";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\x95";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xB1";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x8D";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xA9";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x85";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xA1";
+ break;
+ case 0xBC :
+ return "\xED\x86\xBD";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\x99";
+ break;
+ case 0xB4 :
+ return "\xED\x87\xB5";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x91";
+ break;
+ case 0xAC :
+ return "\xED\x88\xAD";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x89";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xA5";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x81";
+ break;
+ case 0x9C :
+ return "\xED\x8A\x9D";
+ break;
+ case 0xB8 :
+ return "\xED\x8A\xB9";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\x95";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xB1";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x8D";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xA9";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x85";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xA1";
+ break;
+ case 0xBC :
+ return "\xED\x8D\xBD";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\x99";
+ break;
+ case 0xB4 :
+ return "\xED\x8E\xB5";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x91";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xAD";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x89";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xA5";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x81";
+ break;
+ case 0x9C :
+ return "\xED\x91\x9D";
+ break;
+ case 0xB8 :
+ return "\xED\x91\xB9";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\x95";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xB1";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x8D";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xA9";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x85";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xA1";
+ break;
+ case 0xBC :
+ return "\xED\x94\xBD";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\x99";
+ break;
+ case 0xB4 :
+ return "\xED\x95\xB5";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x91";
+ break;
+ case 0xAC :
+ return "\xED\x96\xAD";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x89";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xA5";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x81";
+ break;
+ case 0x9C :
+ return "\xED\x98\x9D";
+ break;
+ case 0xB8 :
+ return "\xED\x98\xB9";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\x95";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xB1";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x8D";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xA9";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x85";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xA1";
+ break;
+ case 0xBC :
+ return "\xED\x9B\xBD";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\x99";
+ break;
+ case 0xB4 :
+ return "\xED\x9C\xB5";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x91";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xAD";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x89";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x82";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\x9E";
+ break;
+ case 0xB8 :
+ return "\xEA\xB0\xBA";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\x96";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xB2";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x8E";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xAA";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x86";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xA2";
+ break;
+ case 0xBC :
+ return "\xEA\xB3\xBE";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\x9A";
+ break;
+ case 0xB4 :
+ return "\xEA\xB4\xB6";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x92";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xAE";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x8A";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xA6";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x82";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\x9E";
+ break;
+ case 0xB8 :
+ return "\xEA\xB7\xBA";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\x96";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xB2";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x8E";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xAA";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x86";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xA2";
+ break;
+ case 0xBC :
+ return "\xEA\xBA\xBE";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\x9A";
+ break;
+ case 0xB4 :
+ return "\xEA\xBB\xB6";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x92";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xAE";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x8A";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xA6";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x82";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\x9E";
+ break;
+ case 0xB8 :
+ return "\xEA\xBE\xBA";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\x96";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xB2";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x8E";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xAA";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x86";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xA2";
+ break;
+ case 0xBC :
+ return "\xEB\x81\xBE";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\x9A";
+ break;
+ case 0xB4 :
+ return "\xEB\x82\xB6";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x92";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xAE";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x8A";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xA6";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x82";
+ break;
+ case 0x9C :
+ return "\xEB\x85\x9E";
+ break;
+ case 0xB8 :
+ return "\xEB\x85\xBA";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\x96";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xB2";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x8E";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xAA";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x86";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xA2";
+ break;
+ case 0xBC :
+ return "\xEB\x88\xBE";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\x9A";
+ break;
+ case 0xB4 :
+ return "\xEB\x89\xB6";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x92";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xAE";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x8A";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xA6";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x82";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\x9E";
+ break;
+ case 0xB8 :
+ return "\xEB\x8C\xBA";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\x96";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xB2";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x8E";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xAA";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x86";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xA2";
+ break;
+ case 0xBC :
+ return "\xEB\x8F\xBE";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\x9A";
+ break;
+ case 0xB4 :
+ return "\xEB\x90\xB6";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x92";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xAE";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x8A";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xA6";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x82";
+ break;
+ case 0x9C :
+ return "\xEB\x93\x9E";
+ break;
+ case 0xB8 :
+ return "\xEB\x93\xBA";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\x96";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xB2";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x8E";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xAA";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x86";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xA2";
+ break;
+ case 0xBC :
+ return "\xEB\x96\xBE";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\x9A";
+ break;
+ case 0xB4 :
+ return "\xEB\x97\xB6";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x92";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xAE";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x8A";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xA6";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x82";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\x9E";
+ break;
+ case 0xB8 :
+ return "\xEB\x9A\xBA";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\x96";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xB2";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x8E";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xAA";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x86";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xA2";
+ break;
+ case 0xBC :
+ return "\xEB\x9D\xBE";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\x9A";
+ break;
+ case 0xB4 :
+ return "\xEB\x9E\xB6";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x92";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xAE";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x8A";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xA6";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x82";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\x9E";
+ break;
+ case 0xB8 :
+ return "\xEB\xA1\xBA";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\x96";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xB2";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x8E";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xAA";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x86";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xA2";
+ break;
+ case 0xBC :
+ return "\xEB\xA4\xBE";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\x9A";
+ break;
+ case 0xB4 :
+ return "\xEB\xA5\xB6";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x92";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xAE";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x8A";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xA6";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x82";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\x9E";
+ break;
+ case 0xB8 :
+ return "\xEB\xA8\xBA";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\x96";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xB2";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x8E";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xAA";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x86";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xA2";
+ break;
+ case 0xBC :
+ return "\xEB\xAB\xBE";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\x9A";
+ break;
+ case 0xB4 :
+ return "\xEB\xAC\xB6";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x92";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xAE";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x8A";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xA6";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x82";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\x9E";
+ break;
+ case 0xB8 :
+ return "\xEB\xAF\xBA";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\x96";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xB2";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x8E";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xAA";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x86";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xA2";
+ break;
+ case 0xBC :
+ return "\xEB\xB2\xBE";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\x9A";
+ break;
+ case 0xB4 :
+ return "\xEB\xB3\xB6";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x92";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xAE";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x8A";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xA6";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x82";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\x9E";
+ break;
+ case 0xB8 :
+ return "\xEB\xB6\xBA";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\x96";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xB2";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x8E";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xAA";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x86";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xA2";
+ break;
+ case 0xBC :
+ return "\xEB\xB9\xBE";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\x9A";
+ break;
+ case 0xB4 :
+ return "\xEB\xBA\xB6";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x92";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xAE";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x8A";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xA6";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x82";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\x9E";
+ break;
+ case 0xB8 :
+ return "\xEB\xBD\xBA";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\x96";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xB2";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x8E";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xAA";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x86";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xA2";
+ break;
+ case 0xBC :
+ return "\xEC\x80\xBE";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\x9A";
+ break;
+ case 0xB4 :
+ return "\xEC\x81\xB6";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x92";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xAE";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x8A";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xA6";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x82";
+ break;
+ case 0x9C :
+ return "\xEC\x84\x9E";
+ break;
+ case 0xB8 :
+ return "\xEC\x84\xBA";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\x96";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xB2";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x8E";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xAA";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x86";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xA2";
+ break;
+ case 0xBC :
+ return "\xEC\x87\xBE";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\x9A";
+ break;
+ case 0xB4 :
+ return "\xEC\x88\xB6";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x92";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xAE";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x8A";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xA6";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x82";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\x9E";
+ break;
+ case 0xB8 :
+ return "\xEC\x8B\xBA";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\x96";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xB2";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x8E";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xAA";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x86";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xA2";
+ break;
+ case 0xBC :
+ return "\xEC\x8E\xBE";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\x9A";
+ break;
+ case 0xB4 :
+ return "\xEC\x8F\xB6";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x92";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xAE";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x8A";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xA6";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x82";
+ break;
+ case 0x9C :
+ return "\xEC\x92\x9E";
+ break;
+ case 0xB8 :
+ return "\xEC\x92\xBA";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\x96";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xB2";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x8E";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xAA";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x86";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xA2";
+ break;
+ case 0xBC :
+ return "\xEC\x95\xBE";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\x9A";
+ break;
+ case 0xB4 :
+ return "\xEC\x96\xB6";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x92";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xAE";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x8A";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xA6";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x82";
+ break;
+ case 0x9C :
+ return "\xEC\x99\x9E";
+ break;
+ case 0xB8 :
+ return "\xEC\x99\xBA";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\x96";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xB2";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x8E";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xAA";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x86";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xA2";
+ break;
+ case 0xBC :
+ return "\xEC\x9C\xBE";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\x9A";
+ break;
+ case 0xB4 :
+ return "\xEC\x9D\xB6";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x92";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xAE";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x8A";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xA6";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x82";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\x9E";
+ break;
+ case 0xB8 :
+ return "\xEC\xA0\xBA";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\x96";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xB2";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x8E";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xAA";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x86";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xA2";
+ break;
+ case 0xBC :
+ return "\xEC\xA3\xBE";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\x9A";
+ break;
+ case 0xB4 :
+ return "\xEC\xA4\xB6";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x92";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xAE";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x8A";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xA6";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x82";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\x9E";
+ break;
+ case 0xB8 :
+ return "\xEC\xA7\xBA";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\x96";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xB2";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x8E";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xAA";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x86";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xA2";
+ break;
+ case 0xBC :
+ return "\xEC\xAA\xBE";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\x9A";
+ break;
+ case 0xB4 :
+ return "\xEC\xAB\xB6";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x92";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xAE";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x8A";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xA6";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x82";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\x9E";
+ break;
+ case 0xB8 :
+ return "\xEC\xAE\xBA";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\x96";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xB2";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x8E";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xAA";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x86";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xA2";
+ break;
+ case 0xBC :
+ return "\xEC\xB1\xBE";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\x9A";
+ break;
+ case 0xB4 :
+ return "\xEC\xB2\xB6";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x92";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xAE";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x8A";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xA6";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x82";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\x9E";
+ break;
+ case 0xB8 :
+ return "\xEC\xB5\xBA";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\x96";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xB2";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x8E";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xAA";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x86";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xA2";
+ break;
+ case 0xBC :
+ return "\xEC\xB8\xBE";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\x9A";
+ break;
+ case 0xB4 :
+ return "\xEC\xB9\xB6";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x92";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xAE";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x8A";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xA6";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x82";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\x9E";
+ break;
+ case 0xB8 :
+ return "\xEC\xBC\xBA";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\x96";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xB2";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x8E";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xAA";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x86";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xA2";
+ break;
+ case 0xBC :
+ return "\xEC\xBF\xBE";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\x9A";
+ break;
+ case 0xB4 :
+ return "\xED\x80\xB6";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x92";
+ break;
+ case 0xAC :
+ return "\xED\x81\xAE";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x8A";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xA6";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x82";
+ break;
+ case 0x9C :
+ return "\xED\x83\x9E";
+ break;
+ case 0xB8 :
+ return "\xED\x83\xBA";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\x96";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xB2";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x8E";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xAA";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x86";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xA2";
+ break;
+ case 0xBC :
+ return "\xED\x86\xBE";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\x9A";
+ break;
+ case 0xB4 :
+ return "\xED\x87\xB6";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x92";
+ break;
+ case 0xAC :
+ return "\xED\x88\xAE";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x8A";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xA6";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x82";
+ break;
+ case 0x9C :
+ return "\xED\x8A\x9E";
+ break;
+ case 0xB8 :
+ return "\xED\x8A\xBA";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\x96";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xB2";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x8E";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xAA";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x86";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xA2";
+ break;
+ case 0xBC :
+ return "\xED\x8D\xBE";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\x9A";
+ break;
+ case 0xB4 :
+ return "\xED\x8E\xB6";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x92";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xAE";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x8A";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xA6";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x82";
+ break;
+ case 0x9C :
+ return "\xED\x91\x9E";
+ break;
+ case 0xB8 :
+ return "\xED\x91\xBA";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\x96";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xB2";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x8E";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xAA";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x86";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xA2";
+ break;
+ case 0xBC :
+ return "\xED\x94\xBE";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\x9A";
+ break;
+ case 0xB4 :
+ return "\xED\x95\xB6";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x92";
+ break;
+ case 0xAC :
+ return "\xED\x96\xAE";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x8A";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xA6";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x82";
+ break;
+ case 0x9C :
+ return "\xED\x98\x9E";
+ break;
+ case 0xB8 :
+ return "\xED\x98\xBA";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\x96";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xB2";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x8E";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xAA";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x86";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xA2";
+ break;
+ case 0xBC :
+ return "\xED\x9B\xBE";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\x9A";
+ break;
+ case 0xB4 :
+ return "\xED\x9C\xB6";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x92";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xAE";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x8A";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x83";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\x9F";
+ break;
+ case 0xB8 :
+ return "\xEA\xB0\xBB";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\x97";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xB3";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x8F";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xAB";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x87";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xA3";
+ break;
+ case 0xBC :
+ return "\xEA\xB3\xBF";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\x9B";
+ break;
+ case 0xB4 :
+ return "\xEA\xB4\xB7";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x93";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xAF";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x8B";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xA7";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x83";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\x9F";
+ break;
+ case 0xB8 :
+ return "\xEA\xB7\xBB";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\x97";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xB3";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x8F";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xAB";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x87";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xA3";
+ break;
+ case 0xBC :
+ return "\xEA\xBA\xBF";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\x9B";
+ break;
+ case 0xB4 :
+ return "\xEA\xBB\xB7";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x93";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xAF";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x8B";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xA7";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x83";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\x9F";
+ break;
+ case 0xB8 :
+ return "\xEA\xBE\xBB";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\x97";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xB3";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x8F";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xAB";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x87";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xA3";
+ break;
+ case 0xBC :
+ return "\xEB\x81\xBF";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\x9B";
+ break;
+ case 0xB4 :
+ return "\xEB\x82\xB7";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x93";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xAF";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x8B";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xA7";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x83";
+ break;
+ case 0x9C :
+ return "\xEB\x85\x9F";
+ break;
+ case 0xB8 :
+ return "\xEB\x85\xBB";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\x97";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xB3";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x8F";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xAB";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x87";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xA3";
+ break;
+ case 0xBC :
+ return "\xEB\x88\xBF";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\x9B";
+ break;
+ case 0xB4 :
+ return "\xEB\x89\xB7";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x93";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xAF";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x8B";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xA7";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x83";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\x9F";
+ break;
+ case 0xB8 :
+ return "\xEB\x8C\xBB";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\x97";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xB3";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x8F";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xAB";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x87";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xA3";
+ break;
+ case 0xBC :
+ return "\xEB\x8F\xBF";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\x9B";
+ break;
+ case 0xB4 :
+ return "\xEB\x90\xB7";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x93";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xAF";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x8B";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xA7";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x83";
+ break;
+ case 0x9C :
+ return "\xEB\x93\x9F";
+ break;
+ case 0xB8 :
+ return "\xEB\x93\xBB";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\x97";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xB3";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x8F";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xAB";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x87";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xA3";
+ break;
+ case 0xBC :
+ return "\xEB\x96\xBF";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\x9B";
+ break;
+ case 0xB4 :
+ return "\xEB\x97\xB7";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x93";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xAF";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x8B";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xA7";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x83";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\x9F";
+ break;
+ case 0xB8 :
+ return "\xEB\x9A\xBB";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\x97";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xB3";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x8F";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xAB";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x87";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xA3";
+ break;
+ case 0xBC :
+ return "\xEB\x9D\xBF";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\x9B";
+ break;
+ case 0xB4 :
+ return "\xEB\x9E\xB7";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x93";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xAF";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x8B";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xA7";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x83";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\x9F";
+ break;
+ case 0xB8 :
+ return "\xEB\xA1\xBB";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\x97";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xB3";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x8F";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xAB";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x87";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xA3";
+ break;
+ case 0xBC :
+ return "\xEB\xA4\xBF";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\x9B";
+ break;
+ case 0xB4 :
+ return "\xEB\xA5\xB7";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x93";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xAF";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x8B";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xA7";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x83";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\x9F";
+ break;
+ case 0xB8 :
+ return "\xEB\xA8\xBB";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\x97";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xB3";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x8F";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xAB";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x87";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xA3";
+ break;
+ case 0xBC :
+ return "\xEB\xAB\xBF";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\x9B";
+ break;
+ case 0xB4 :
+ return "\xEB\xAC\xB7";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x93";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xAF";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x8B";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xA7";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x83";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\x9F";
+ break;
+ case 0xB8 :
+ return "\xEB\xAF\xBB";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\x97";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xB3";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x8F";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xAB";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x87";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xA3";
+ break;
+ case 0xBC :
+ return "\xEB\xB2\xBF";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\x9B";
+ break;
+ case 0xB4 :
+ return "\xEB\xB3\xB7";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x93";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xAF";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x8B";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xA7";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x83";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\x9F";
+ break;
+ case 0xB8 :
+ return "\xEB\xB6\xBB";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\x97";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xB3";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x8F";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xAB";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x87";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xA3";
+ break;
+ case 0xBC :
+ return "\xEB\xB9\xBF";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\x9B";
+ break;
+ case 0xB4 :
+ return "\xEB\xBA\xB7";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x93";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xAF";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x8B";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xA7";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x83";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\x9F";
+ break;
+ case 0xB8 :
+ return "\xEB\xBD\xBB";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\x97";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xB3";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x8F";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xAB";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x87";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xA3";
+ break;
+ case 0xBC :
+ return "\xEC\x80\xBF";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\x9B";
+ break;
+ case 0xB4 :
+ return "\xEC\x81\xB7";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x93";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xAF";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x8B";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xA7";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x83";
+ break;
+ case 0x9C :
+ return "\xEC\x84\x9F";
+ break;
+ case 0xB8 :
+ return "\xEC\x84\xBB";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\x97";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xB3";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x8F";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xAB";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x87";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xA3";
+ break;
+ case 0xBC :
+ return "\xEC\x87\xBF";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\x9B";
+ break;
+ case 0xB4 :
+ return "\xEC\x88\xB7";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x93";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xAF";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x8B";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xA7";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x83";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\x9F";
+ break;
+ case 0xB8 :
+ return "\xEC\x8B\xBB";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\x97";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xB3";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x8F";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xAB";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x87";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xA3";
+ break;
+ case 0xBC :
+ return "\xEC\x8E\xBF";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\x9B";
+ break;
+ case 0xB4 :
+ return "\xEC\x8F\xB7";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x93";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xAF";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x8B";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xA7";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x83";
+ break;
+ case 0x9C :
+ return "\xEC\x92\x9F";
+ break;
+ case 0xB8 :
+ return "\xEC\x92\xBB";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\x97";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xB3";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x8F";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xAB";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x87";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xA3";
+ break;
+ case 0xBC :
+ return "\xEC\x95\xBF";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\x9B";
+ break;
+ case 0xB4 :
+ return "\xEC\x96\xB7";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x93";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xAF";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x8B";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xA7";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x83";
+ break;
+ case 0x9C :
+ return "\xEC\x99\x9F";
+ break;
+ case 0xB8 :
+ return "\xEC\x99\xBB";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\x97";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xB3";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x8F";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xAB";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x87";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xA3";
+ break;
+ case 0xBC :
+ return "\xEC\x9C\xBF";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\x9B";
+ break;
+ case 0xB4 :
+ return "\xEC\x9D\xB7";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x93";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xAF";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x8B";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xA7";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x83";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\x9F";
+ break;
+ case 0xB8 :
+ return "\xEC\xA0\xBB";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\x97";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xB3";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x8F";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xAB";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x87";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xA3";
+ break;
+ case 0xBC :
+ return "\xEC\xA3\xBF";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\x9B";
+ break;
+ case 0xB4 :
+ return "\xEC\xA4\xB7";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x93";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xAF";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x8B";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xA7";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x83";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\x9F";
+ break;
+ case 0xB8 :
+ return "\xEC\xA7\xBB";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\x97";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xB3";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x8F";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xAB";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x87";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xA3";
+ break;
+ case 0xBC :
+ return "\xEC\xAA\xBF";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\x9B";
+ break;
+ case 0xB4 :
+ return "\xEC\xAB\xB7";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x93";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xAF";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x8B";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xA7";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x83";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\x9F";
+ break;
+ case 0xB8 :
+ return "\xEC\xAE\xBB";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\x97";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xB3";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x8F";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xAB";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x87";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xA3";
+ break;
+ case 0xBC :
+ return "\xEC\xB1\xBF";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\x9B";
+ break;
+ case 0xB4 :
+ return "\xEC\xB2\xB7";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x93";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xAF";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x8B";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xA7";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x83";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\x9F";
+ break;
+ case 0xB8 :
+ return "\xEC\xB5\xBB";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\x97";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xB3";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x8F";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xAB";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x87";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xA3";
+ break;
+ case 0xBC :
+ return "\xEC\xB8\xBF";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\x9B";
+ break;
+ case 0xB4 :
+ return "\xEC\xB9\xB7";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x93";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xAF";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x8B";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xA7";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x83";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\x9F";
+ break;
+ case 0xB8 :
+ return "\xEC\xBC\xBB";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\x97";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xB3";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x8F";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xAB";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x87";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xA3";
+ break;
+ case 0xBC :
+ return "\xEC\xBF\xBF";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\x9B";
+ break;
+ case 0xB4 :
+ return "\xED\x80\xB7";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x93";
+ break;
+ case 0xAC :
+ return "\xED\x81\xAF";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x8B";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xA7";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x83";
+ break;
+ case 0x9C :
+ return "\xED\x83\x9F";
+ break;
+ case 0xB8 :
+ return "\xED\x83\xBB";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\x97";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xB3";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x8F";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xAB";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x87";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xA3";
+ break;
+ case 0xBC :
+ return "\xED\x86\xBF";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\x9B";
+ break;
+ case 0xB4 :
+ return "\xED\x87\xB7";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x93";
+ break;
+ case 0xAC :
+ return "\xED\x88\xAF";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x8B";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xA7";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x83";
+ break;
+ case 0x9C :
+ return "\xED\x8A\x9F";
+ break;
+ case 0xB8 :
+ return "\xED\x8A\xBB";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\x97";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xB3";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x8F";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xAB";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x87";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xA3";
+ break;
+ case 0xBC :
+ return "\xED\x8D\xBF";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\x9B";
+ break;
+ case 0xB4 :
+ return "\xED\x8E\xB7";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x93";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xAF";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x8B";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xA7";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x83";
+ break;
+ case 0x9C :
+ return "\xED\x91\x9F";
+ break;
+ case 0xB8 :
+ return "\xED\x91\xBB";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\x97";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xB3";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x8F";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xAB";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x87";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xA3";
+ break;
+ case 0xBC :
+ return "\xED\x94\xBF";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\x9B";
+ break;
+ case 0xB4 :
+ return "\xED\x95\xB7";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x93";
+ break;
+ case 0xAC :
+ return "\xED\x96\xAF";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x8B";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xA7";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x83";
+ break;
+ case 0x9C :
+ return "\xED\x98\x9F";
+ break;
+ case 0xB8 :
+ return "\xED\x98\xBB";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\x97";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xB3";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x8F";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xAB";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x87";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xA3";
+ break;
+ case 0xBC :
+ return "\xED\x9B\xBF";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\x9B";
+ break;
+ case 0xB4 :
+ return "\xED\x9C\xB7";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x93";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xAF";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x8B";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x84";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xA0";
+ break;
+ case 0xB8 :
+ return "\xEA\xB0\xBC";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\x98";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xB4";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x90";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xAC";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x88";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xA4";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x80";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\x9C";
+ break;
+ case 0xB4 :
+ return "\xEA\xB4\xB8";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x94";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xB0";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x8C";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xA8";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x84";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xA0";
+ break;
+ case 0xB8 :
+ return "\xEA\xB7\xBC";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\x98";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xB4";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x90";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xAC";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x88";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xA4";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x80";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\x9C";
+ break;
+ case 0xB4 :
+ return "\xEA\xBB\xB8";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x94";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xB0";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x8C";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xA8";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x84";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xA0";
+ break;
+ case 0xB8 :
+ return "\xEA\xBE\xBC";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\x98";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xB4";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x90";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xAC";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x88";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xA4";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x80";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\x9C";
+ break;
+ case 0xB4 :
+ return "\xEB\x82\xB8";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x94";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xB0";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x8C";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xA8";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x84";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xA0";
+ break;
+ case 0xB8 :
+ return "\xEB\x85\xBC";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\x98";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xB4";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x90";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xAC";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x88";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xA4";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x80";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\x9C";
+ break;
+ case 0xB4 :
+ return "\xEB\x89\xB8";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x94";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xB0";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x8C";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xA8";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x84";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xA0";
+ break;
+ case 0xB8 :
+ return "\xEB\x8C\xBC";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\x98";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xB4";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x90";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xAC";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x88";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xA4";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x80";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\x9C";
+ break;
+ case 0xB4 :
+ return "\xEB\x90\xB8";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x94";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xB0";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x8C";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xA8";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x84";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xA0";
+ break;
+ case 0xB8 :
+ return "\xEB\x93\xBC";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\x98";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xB4";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x90";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xAC";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x88";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xA4";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x80";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\x9C";
+ break;
+ case 0xB4 :
+ return "\xEB\x97\xB8";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x94";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xB0";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x8C";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xA8";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x84";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xA0";
+ break;
+ case 0xB8 :
+ return "\xEB\x9A\xBC";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\x98";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xB4";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x90";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xAC";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x88";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xA4";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x80";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\x9C";
+ break;
+ case 0xB4 :
+ return "\xEB\x9E\xB8";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x94";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xB0";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x8C";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xA8";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x84";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xA0";
+ break;
+ case 0xB8 :
+ return "\xEB\xA1\xBC";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\x98";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xB4";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x90";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xAC";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x88";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xA4";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x80";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\x9C";
+ break;
+ case 0xB4 :
+ return "\xEB\xA5\xB8";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x94";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xB0";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x8C";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xA8";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x84";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xA0";
+ break;
+ case 0xB8 :
+ return "\xEB\xA8\xBC";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\x98";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xB4";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x90";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xAC";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x88";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xA4";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x80";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\x9C";
+ break;
+ case 0xB4 :
+ return "\xEB\xAC\xB8";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x94";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xB0";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x8C";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xA8";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x84";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xA0";
+ break;
+ case 0xB8 :
+ return "\xEB\xAF\xBC";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\x98";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xB4";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x90";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xAC";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x88";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xA4";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x80";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\x9C";
+ break;
+ case 0xB4 :
+ return "\xEB\xB3\xB8";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x94";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xB0";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x8C";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xA8";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x84";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xA0";
+ break;
+ case 0xB8 :
+ return "\xEB\xB6\xBC";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\x98";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xB4";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x90";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xAC";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x88";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xA4";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x80";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\x9C";
+ break;
+ case 0xB4 :
+ return "\xEB\xBA\xB8";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x94";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xB0";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x8C";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xA8";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x84";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xA0";
+ break;
+ case 0xB8 :
+ return "\xEB\xBD\xBC";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\x98";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xB4";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x90";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xAC";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x88";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xA4";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x80";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\x9C";
+ break;
+ case 0xB4 :
+ return "\xEC\x81\xB8";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x94";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xB0";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x8C";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xA8";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x84";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xA0";
+ break;
+ case 0xB8 :
+ return "\xEC\x84\xBC";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\x98";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xB4";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x90";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xAC";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x88";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xA4";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x80";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\x9C";
+ break;
+ case 0xB4 :
+ return "\xEC\x88\xB8";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x94";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xB0";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x8C";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xA8";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x84";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xA0";
+ break;
+ case 0xB8 :
+ return "\xEC\x8B\xBC";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\x98";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xB4";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x90";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xAC";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x88";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xA4";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x80";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\x9C";
+ break;
+ case 0xB4 :
+ return "\xEC\x8F\xB8";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x94";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xB0";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x8C";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xA8";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x84";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xA0";
+ break;
+ case 0xB8 :
+ return "\xEC\x92\xBC";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\x98";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xB4";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x90";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xAC";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x88";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xA4";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x80";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\x9C";
+ break;
+ case 0xB4 :
+ return "\xEC\x96\xB8";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x94";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xB0";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x8C";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xA8";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x84";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xA0";
+ break;
+ case 0xB8 :
+ return "\xEC\x99\xBC";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\x98";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xB4";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x90";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xAC";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x88";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xA4";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x80";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\x9C";
+ break;
+ case 0xB4 :
+ return "\xEC\x9D\xB8";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x94";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xB0";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x8C";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xA8";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x84";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xA0";
+ break;
+ case 0xB8 :
+ return "\xEC\xA0\xBC";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\x98";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xB4";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x90";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xAC";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x88";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xA4";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x80";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\x9C";
+ break;
+ case 0xB4 :
+ return "\xEC\xA4\xB8";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x94";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xB0";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x8C";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xA8";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x84";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xA0";
+ break;
+ case 0xB8 :
+ return "\xEC\xA7\xBC";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\x98";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xB4";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x90";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xAC";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x88";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xA4";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x80";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\x9C";
+ break;
+ case 0xB4 :
+ return "\xEC\xAB\xB8";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x94";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xB0";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x8C";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xA8";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x84";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xA0";
+ break;
+ case 0xB8 :
+ return "\xEC\xAE\xBC";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\x98";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xB4";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x90";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xAC";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x88";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xA4";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x80";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\x9C";
+ break;
+ case 0xB4 :
+ return "\xEC\xB2\xB8";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x94";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xB0";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x8C";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xA8";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x84";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xA0";
+ break;
+ case 0xB8 :
+ return "\xEC\xB5\xBC";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\x98";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xB4";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x90";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xAC";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x88";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xA4";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x80";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\x9C";
+ break;
+ case 0xB4 :
+ return "\xEC\xB9\xB8";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x94";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xB0";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x8C";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xA8";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x84";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xA0";
+ break;
+ case 0xB8 :
+ return "\xEC\xBC\xBC";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\x98";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xB4";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x90";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xAC";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x88";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xA4";
+ break;
+ case 0xBC :
+ return "\xED\x80\x80";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\x9C";
+ break;
+ case 0xB4 :
+ return "\xED\x80\xB8";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x94";
+ break;
+ case 0xAC :
+ return "\xED\x81\xB0";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x8C";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xA8";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x84";
+ break;
+ case 0x9C :
+ return "\xED\x83\xA0";
+ break;
+ case 0xB8 :
+ return "\xED\x83\xBC";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\x98";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xB4";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x90";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xAC";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x88";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xA4";
+ break;
+ case 0xBC :
+ return "\xED\x87\x80";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\x9C";
+ break;
+ case 0xB4 :
+ return "\xED\x87\xB8";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x94";
+ break;
+ case 0xAC :
+ return "\xED\x88\xB0";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x8C";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xA8";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x84";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xA0";
+ break;
+ case 0xB8 :
+ return "\xED\x8A\xBC";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\x98";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xB4";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x90";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xAC";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x88";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xA4";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x80";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\x9C";
+ break;
+ case 0xB4 :
+ return "\xED\x8E\xB8";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x94";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xB0";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x8C";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xA8";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x84";
+ break;
+ case 0x9C :
+ return "\xED\x91\xA0";
+ break;
+ case 0xB8 :
+ return "\xED\x91\xBC";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\x98";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xB4";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x90";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xAC";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x88";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xA4";
+ break;
+ case 0xBC :
+ return "\xED\x95\x80";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\x9C";
+ break;
+ case 0xB4 :
+ return "\xED\x95\xB8";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x94";
+ break;
+ case 0xAC :
+ return "\xED\x96\xB0";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x8C";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xA8";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x84";
+ break;
+ case 0x9C :
+ return "\xED\x98\xA0";
+ break;
+ case 0xB8 :
+ return "\xED\x98\xBC";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\x98";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xB4";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x90";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xAC";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x88";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xA4";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x80";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\x9C";
+ break;
+ case 0xB4 :
+ return "\xED\x9C\xB8";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x94";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xB0";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x8C";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x85";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xA1";
+ break;
+ case 0xB8 :
+ return "\xEA\xB0\xBD";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\x99";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xB5";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x91";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xAD";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x89";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xA5";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x81";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\x9D";
+ break;
+ case 0xB4 :
+ return "\xEA\xB4\xB9";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x95";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xB1";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x8D";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xA9";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x85";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xA1";
+ break;
+ case 0xB8 :
+ return "\xEA\xB7\xBD";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\x99";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xB5";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x91";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xAD";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x89";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xA5";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x81";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\x9D";
+ break;
+ case 0xB4 :
+ return "\xEA\xBB\xB9";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x95";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xB1";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x8D";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xA9";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x85";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xA1";
+ break;
+ case 0xB8 :
+ return "\xEA\xBE\xBD";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\x99";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xB5";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x91";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xAD";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x89";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xA5";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x81";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\x9D";
+ break;
+ case 0xB4 :
+ return "\xEB\x82\xB9";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x95";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xB1";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x8D";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xA9";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x85";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xA1";
+ break;
+ case 0xB8 :
+ return "\xEB\x85\xBD";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\x99";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xB5";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x91";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xAD";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x89";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xA5";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x81";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\x9D";
+ break;
+ case 0xB4 :
+ return "\xEB\x89\xB9";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x95";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xB1";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x8D";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xA9";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x85";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xA1";
+ break;
+ case 0xB8 :
+ return "\xEB\x8C\xBD";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\x99";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xB5";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x91";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xAD";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x89";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xA5";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x81";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\x9D";
+ break;
+ case 0xB4 :
+ return "\xEB\x90\xB9";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x95";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xB1";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x8D";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xA9";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x85";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xA1";
+ break;
+ case 0xB8 :
+ return "\xEB\x93\xBD";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\x99";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xB5";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x91";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xAD";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x89";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xA5";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x81";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\x9D";
+ break;
+ case 0xB4 :
+ return "\xEB\x97\xB9";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x95";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xB1";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x8D";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xA9";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x85";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xA1";
+ break;
+ case 0xB8 :
+ return "\xEB\x9A\xBD";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\x99";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xB5";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x91";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xAD";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x89";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xA5";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x81";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\x9D";
+ break;
+ case 0xB4 :
+ return "\xEB\x9E\xB9";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x95";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xB1";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x8D";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xA9";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x85";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xA1";
+ break;
+ case 0xB8 :
+ return "\xEB\xA1\xBD";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\x99";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xB5";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x91";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xAD";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x89";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xA5";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x81";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\x9D";
+ break;
+ case 0xB4 :
+ return "\xEB\xA5\xB9";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x95";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xB1";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x8D";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xA9";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x85";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xA1";
+ break;
+ case 0xB8 :
+ return "\xEB\xA8\xBD";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\x99";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xB5";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x91";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xAD";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x89";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xA5";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x81";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\x9D";
+ break;
+ case 0xB4 :
+ return "\xEB\xAC\xB9";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x95";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xB1";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x8D";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xA9";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x85";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xA1";
+ break;
+ case 0xB8 :
+ return "\xEB\xAF\xBD";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\x99";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xB5";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x91";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xAD";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x89";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xA5";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x81";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\x9D";
+ break;
+ case 0xB4 :
+ return "\xEB\xB3\xB9";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x95";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xB1";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x8D";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xA9";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x85";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xA1";
+ break;
+ case 0xB8 :
+ return "\xEB\xB6\xBD";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\x99";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xB5";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x91";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xAD";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x89";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xA5";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x81";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\x9D";
+ break;
+ case 0xB4 :
+ return "\xEB\xBA\xB9";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x95";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xB1";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x8D";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xA9";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x85";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xA1";
+ break;
+ case 0xB8 :
+ return "\xEB\xBD\xBD";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\x99";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xB5";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x91";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xAD";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x89";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xA5";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x81";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\x9D";
+ break;
+ case 0xB4 :
+ return "\xEC\x81\xB9";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x95";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xB1";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x8D";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xA9";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x85";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xA1";
+ break;
+ case 0xB8 :
+ return "\xEC\x84\xBD";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\x99";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xB5";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x91";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xAD";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x89";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xA5";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x81";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\x9D";
+ break;
+ case 0xB4 :
+ return "\xEC\x88\xB9";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x95";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xB1";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x8D";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xA9";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x85";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xA1";
+ break;
+ case 0xB8 :
+ return "\xEC\x8B\xBD";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\x99";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xB5";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x91";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xAD";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x89";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xA5";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x81";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\x9D";
+ break;
+ case 0xB4 :
+ return "\xEC\x8F\xB9";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x95";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xB1";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x8D";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xA9";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x85";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xA1";
+ break;
+ case 0xB8 :
+ return "\xEC\x92\xBD";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\x99";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xB5";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x91";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xAD";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x89";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xA5";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x81";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\x9D";
+ break;
+ case 0xB4 :
+ return "\xEC\x96\xB9";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x95";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xB1";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x8D";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xA9";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x85";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xA1";
+ break;
+ case 0xB8 :
+ return "\xEC\x99\xBD";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\x99";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xB5";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x91";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xAD";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x89";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xA5";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x81";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\x9D";
+ break;
+ case 0xB4 :
+ return "\xEC\x9D\xB9";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x95";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xB1";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x8D";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xA9";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x85";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xA1";
+ break;
+ case 0xB8 :
+ return "\xEC\xA0\xBD";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\x99";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xB5";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x91";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xAD";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x89";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xA5";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x81";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\x9D";
+ break;
+ case 0xB4 :
+ return "\xEC\xA4\xB9";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x95";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xB1";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x8D";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xA9";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x85";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xA1";
+ break;
+ case 0xB8 :
+ return "\xEC\xA7\xBD";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\x99";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xB5";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x91";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xAD";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x89";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xA5";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x81";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\x9D";
+ break;
+ case 0xB4 :
+ return "\xEC\xAB\xB9";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x95";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xB1";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x8D";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xA9";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x85";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xA1";
+ break;
+ case 0xB8 :
+ return "\xEC\xAE\xBD";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\x99";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xB5";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x91";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xAD";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x89";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xA5";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x81";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\x9D";
+ break;
+ case 0xB4 :
+ return "\xEC\xB2\xB9";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x95";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xB1";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x8D";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xA9";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x85";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xA1";
+ break;
+ case 0xB8 :
+ return "\xEC\xB5\xBD";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\x99";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xB5";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x91";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xAD";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x89";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xA5";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x81";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\x9D";
+ break;
+ case 0xB4 :
+ return "\xEC\xB9\xB9";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x95";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xB1";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x8D";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xA9";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x85";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xA1";
+ break;
+ case 0xB8 :
+ return "\xEC\xBC\xBD";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\x99";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xB5";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x91";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xAD";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x89";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xA5";
+ break;
+ case 0xBC :
+ return "\xED\x80\x81";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\x9D";
+ break;
+ case 0xB4 :
+ return "\xED\x80\xB9";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x95";
+ break;
+ case 0xAC :
+ return "\xED\x81\xB1";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x8D";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xA9";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x85";
+ break;
+ case 0x9C :
+ return "\xED\x83\xA1";
+ break;
+ case 0xB8 :
+ return "\xED\x83\xBD";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\x99";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xB5";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x91";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xAD";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x89";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xA5";
+ break;
+ case 0xBC :
+ return "\xED\x87\x81";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\x9D";
+ break;
+ case 0xB4 :
+ return "\xED\x87\xB9";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x95";
+ break;
+ case 0xAC :
+ return "\xED\x88\xB1";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x8D";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xA9";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x85";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xA1";
+ break;
+ case 0xB8 :
+ return "\xED\x8A\xBD";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\x99";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xB5";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x91";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xAD";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x89";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xA5";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x81";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\x9D";
+ break;
+ case 0xB4 :
+ return "\xED\x8E\xB9";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x95";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xB1";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x8D";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xA9";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x85";
+ break;
+ case 0x9C :
+ return "\xED\x91\xA1";
+ break;
+ case 0xB8 :
+ return "\xED\x91\xBD";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\x99";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xB5";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x91";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xAD";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x89";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xA5";
+ break;
+ case 0xBC :
+ return "\xED\x95\x81";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\x9D";
+ break;
+ case 0xB4 :
+ return "\xED\x95\xB9";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x95";
+ break;
+ case 0xAC :
+ return "\xED\x96\xB1";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x8D";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xA9";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x85";
+ break;
+ case 0x9C :
+ return "\xED\x98\xA1";
+ break;
+ case 0xB8 :
+ return "\xED\x98\xBD";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\x99";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xB5";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x91";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xAD";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x89";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xA5";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x81";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\x9D";
+ break;
+ case 0xB4 :
+ return "\xED\x9C\xB9";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x95";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xB1";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x8D";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x86";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xA2";
+ break;
+ case 0xB8 :
+ return "\xEA\xB0\xBE";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\x9A";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xB6";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x92";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xAE";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x8A";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xA6";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x82";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\x9E";
+ break;
+ case 0xB4 :
+ return "\xEA\xB4\xBA";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x96";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xB2";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x8E";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xAA";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x86";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xA2";
+ break;
+ case 0xB8 :
+ return "\xEA\xB7\xBE";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\x9A";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xB6";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x92";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xAE";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x8A";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xA6";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x82";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\x9E";
+ break;
+ case 0xB4 :
+ return "\xEA\xBB\xBA";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x96";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xB2";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x8E";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xAA";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x86";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xA2";
+ break;
+ case 0xB8 :
+ return "\xEA\xBE\xBE";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\x9A";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xB6";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x92";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xAE";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x8A";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xA6";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x82";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\x9E";
+ break;
+ case 0xB4 :
+ return "\xEB\x82\xBA";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x96";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xB2";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x8E";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xAA";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x86";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xA2";
+ break;
+ case 0xB8 :
+ return "\xEB\x85\xBE";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\x9A";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xB6";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x92";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xAE";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x8A";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xA6";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x82";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\x9E";
+ break;
+ case 0xB4 :
+ return "\xEB\x89\xBA";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x96";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xB2";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x8E";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xAA";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x86";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xA2";
+ break;
+ case 0xB8 :
+ return "\xEB\x8C\xBE";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\x9A";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xB6";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x92";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xAE";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x8A";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xA6";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x82";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\x9E";
+ break;
+ case 0xB4 :
+ return "\xEB\x90\xBA";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x96";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xB2";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x8E";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xAA";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x86";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xA2";
+ break;
+ case 0xB8 :
+ return "\xEB\x93\xBE";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\x9A";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xB6";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x92";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xAE";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x8A";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xA6";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x82";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\x9E";
+ break;
+ case 0xB4 :
+ return "\xEB\x97\xBA";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x96";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xB2";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x8E";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xAA";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x86";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xA2";
+ break;
+ case 0xB8 :
+ return "\xEB\x9A\xBE";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\x9A";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xB6";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x92";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xAE";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x8A";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xA6";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x82";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\x9E";
+ break;
+ case 0xB4 :
+ return "\xEB\x9E\xBA";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x96";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xB2";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x8E";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xAA";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x86";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xA2";
+ break;
+ case 0xB8 :
+ return "\xEB\xA1\xBE";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\x9A";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xB6";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x92";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xAE";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x8A";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xA6";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x82";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\x9E";
+ break;
+ case 0xB4 :
+ return "\xEB\xA5\xBA";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x96";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xB2";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x8E";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xAA";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x86";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xA2";
+ break;
+ case 0xB8 :
+ return "\xEB\xA8\xBE";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\x9A";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xB6";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x92";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xAE";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x8A";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xA6";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x82";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\x9E";
+ break;
+ case 0xB4 :
+ return "\xEB\xAC\xBA";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x96";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xB2";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x8E";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xAA";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x86";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xA2";
+ break;
+ case 0xB8 :
+ return "\xEB\xAF\xBE";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\x9A";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xB6";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x92";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xAE";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x8A";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xA6";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x82";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\x9E";
+ break;
+ case 0xB4 :
+ return "\xEB\xB3\xBA";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x96";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xB2";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x8E";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xAA";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x86";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xA2";
+ break;
+ case 0xB8 :
+ return "\xEB\xB6\xBE";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\x9A";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xB6";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x92";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xAE";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x8A";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xA6";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x82";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\x9E";
+ break;
+ case 0xB4 :
+ return "\xEB\xBA\xBA";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x96";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xB2";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x8E";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xAA";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x86";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xA2";
+ break;
+ case 0xB8 :
+ return "\xEB\xBD\xBE";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\x9A";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xB6";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x92";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xAE";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x8A";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xA6";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x82";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\x9E";
+ break;
+ case 0xB4 :
+ return "\xEC\x81\xBA";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x96";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xB2";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x8E";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xAA";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x86";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xA2";
+ break;
+ case 0xB8 :
+ return "\xEC\x84\xBE";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\x9A";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xB6";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x92";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xAE";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x8A";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xA6";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x82";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\x9E";
+ break;
+ case 0xB4 :
+ return "\xEC\x88\xBA";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x96";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xB2";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x8E";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xAA";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x86";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xA2";
+ break;
+ case 0xB8 :
+ return "\xEC\x8B\xBE";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\x9A";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xB6";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x92";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xAE";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x8A";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xA6";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x82";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\x9E";
+ break;
+ case 0xB4 :
+ return "\xEC\x8F\xBA";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x96";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xB2";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x8E";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xAA";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x86";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xA2";
+ break;
+ case 0xB8 :
+ return "\xEC\x92\xBE";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\x9A";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xB6";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x92";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xAE";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x8A";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xA6";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x82";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\x9E";
+ break;
+ case 0xB4 :
+ return "\xEC\x96\xBA";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x96";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xB2";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x8E";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xAA";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x86";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xA2";
+ break;
+ case 0xB8 :
+ return "\xEC\x99\xBE";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\x9A";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xB6";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x92";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xAE";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x8A";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xA6";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x82";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\x9E";
+ break;
+ case 0xB4 :
+ return "\xEC\x9D\xBA";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x96";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xB2";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x8E";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xAA";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x86";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xA2";
+ break;
+ case 0xB8 :
+ return "\xEC\xA0\xBE";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\x9A";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xB6";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x92";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xAE";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x8A";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xA6";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x82";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\x9E";
+ break;
+ case 0xB4 :
+ return "\xEC\xA4\xBA";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x96";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xB2";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x8E";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xAA";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x86";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xA2";
+ break;
+ case 0xB8 :
+ return "\xEC\xA7\xBE";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\x9A";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xB6";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x92";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xAE";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x8A";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xA6";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x82";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\x9E";
+ break;
+ case 0xB4 :
+ return "\xEC\xAB\xBA";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x96";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xB2";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x8E";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xAA";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x86";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xA2";
+ break;
+ case 0xB8 :
+ return "\xEC\xAE\xBE";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\x9A";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xB6";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x92";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xAE";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x8A";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xA6";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x82";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\x9E";
+ break;
+ case 0xB4 :
+ return "\xEC\xB2\xBA";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x96";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xB2";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x8E";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xAA";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x86";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xA2";
+ break;
+ case 0xB8 :
+ return "\xEC\xB5\xBE";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\x9A";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xB6";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x92";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xAE";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x8A";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xA6";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x82";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\x9E";
+ break;
+ case 0xB4 :
+ return "\xEC\xB9\xBA";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x96";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xB2";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x8E";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xAA";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x86";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xA2";
+ break;
+ case 0xB8 :
+ return "\xEC\xBC\xBE";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\x9A";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xB6";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x92";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xAE";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x8A";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xA6";
+ break;
+ case 0xBC :
+ return "\xED\x80\x82";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\x9E";
+ break;
+ case 0xB4 :
+ return "\xED\x80\xBA";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x96";
+ break;
+ case 0xAC :
+ return "\xED\x81\xB2";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x8E";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xAA";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x86";
+ break;
+ case 0x9C :
+ return "\xED\x83\xA2";
+ break;
+ case 0xB8 :
+ return "\xED\x83\xBE";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\x9A";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xB6";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x92";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xAE";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x8A";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xA6";
+ break;
+ case 0xBC :
+ return "\xED\x87\x82";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\x9E";
+ break;
+ case 0xB4 :
+ return "\xED\x87\xBA";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x96";
+ break;
+ case 0xAC :
+ return "\xED\x88\xB2";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x8E";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xAA";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x86";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xA2";
+ break;
+ case 0xB8 :
+ return "\xED\x8A\xBE";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\x9A";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xB6";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x92";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xAE";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x8A";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xA6";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x82";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\x9E";
+ break;
+ case 0xB4 :
+ return "\xED\x8E\xBA";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x96";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xB2";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x8E";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xAA";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x86";
+ break;
+ case 0x9C :
+ return "\xED\x91\xA2";
+ break;
+ case 0xB8 :
+ return "\xED\x91\xBE";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\x9A";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xB6";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x92";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xAE";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x8A";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xA6";
+ break;
+ case 0xBC :
+ return "\xED\x95\x82";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\x9E";
+ break;
+ case 0xB4 :
+ return "\xED\x95\xBA";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x96";
+ break;
+ case 0xAC :
+ return "\xED\x96\xB2";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x8E";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xAA";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x86";
+ break;
+ case 0x9C :
+ return "\xED\x98\xA2";
+ break;
+ case 0xB8 :
+ return "\xED\x98\xBE";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\x9A";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xB6";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x92";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xAE";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x8A";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xA6";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x82";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\x9E";
+ break;
+ case 0xB4 :
+ return "\xED\x9C\xBA";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x96";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xB2";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x8E";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x87";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xA3";
+ break;
+ case 0xB8 :
+ return "\xEA\xB0\xBF";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\x9B";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xB7";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x93";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xAF";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x8B";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xA7";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x83";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\x9F";
+ break;
+ case 0xB4 :
+ return "\xEA\xB4\xBB";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x97";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xB3";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x8F";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xAB";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x87";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xA3";
+ break;
+ case 0xB8 :
+ return "\xEA\xB7\xBF";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\x9B";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xB7";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x93";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xAF";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x8B";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xA7";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x83";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\x9F";
+ break;
+ case 0xB4 :
+ return "\xEA\xBB\xBB";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x97";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xB3";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x8F";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xAB";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x87";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xA3";
+ break;
+ case 0xB8 :
+ return "\xEA\xBE\xBF";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\x9B";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xB7";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x93";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xAF";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x8B";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xA7";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x83";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\x9F";
+ break;
+ case 0xB4 :
+ return "\xEB\x82\xBB";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x97";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xB3";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x8F";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xAB";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x87";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xA3";
+ break;
+ case 0xB8 :
+ return "\xEB\x85\xBF";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\x9B";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xB7";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x93";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xAF";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x8B";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xA7";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x83";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\x9F";
+ break;
+ case 0xB4 :
+ return "\xEB\x89\xBB";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x97";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xB3";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x8F";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xAB";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x87";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xA3";
+ break;
+ case 0xB8 :
+ return "\xEB\x8C\xBF";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\x9B";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xB7";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x93";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xAF";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x8B";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xA7";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x83";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\x9F";
+ break;
+ case 0xB4 :
+ return "\xEB\x90\xBB";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x97";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xB3";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x8F";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xAB";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x87";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xA3";
+ break;
+ case 0xB8 :
+ return "\xEB\x93\xBF";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\x9B";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xB7";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x93";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xAF";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x8B";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xA7";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x83";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\x9F";
+ break;
+ case 0xB4 :
+ return "\xEB\x97\xBB";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x97";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xB3";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x8F";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xAB";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x87";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xA3";
+ break;
+ case 0xB8 :
+ return "\xEB\x9A\xBF";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\x9B";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xB7";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x93";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xAF";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x8B";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xA7";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x83";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\x9F";
+ break;
+ case 0xB4 :
+ return "\xEB\x9E\xBB";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x97";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xB3";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x8F";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xAB";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x87";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xA3";
+ break;
+ case 0xB8 :
+ return "\xEB\xA1\xBF";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\x9B";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xB7";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x93";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xAF";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x8B";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xA7";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x83";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\x9F";
+ break;
+ case 0xB4 :
+ return "\xEB\xA5\xBB";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x97";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xB3";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x8F";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xAB";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x87";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xA3";
+ break;
+ case 0xB8 :
+ return "\xEB\xA8\xBF";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\x9B";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xB7";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x93";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xAF";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x8B";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xA7";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x83";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\x9F";
+ break;
+ case 0xB4 :
+ return "\xEB\xAC\xBB";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x97";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xB3";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x8F";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xAB";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x87";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xA3";
+ break;
+ case 0xB8 :
+ return "\xEB\xAF\xBF";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\x9B";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xB7";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x93";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xAF";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x8B";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xA7";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x83";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\x9F";
+ break;
+ case 0xB4 :
+ return "\xEB\xB3\xBB";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x97";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xB3";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x8F";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xAB";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x87";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xA3";
+ break;
+ case 0xB8 :
+ return "\xEB\xB6\xBF";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\x9B";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xB7";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x93";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xAF";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x8B";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xA7";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x83";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\x9F";
+ break;
+ case 0xB4 :
+ return "\xEB\xBA\xBB";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x97";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xB3";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x8F";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xAB";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x87";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xA3";
+ break;
+ case 0xB8 :
+ return "\xEB\xBD\xBF";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\x9B";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xB7";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x93";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xAF";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x8B";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xA7";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x83";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\x9F";
+ break;
+ case 0xB4 :
+ return "\xEC\x81\xBB";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x97";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xB3";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x8F";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xAB";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x87";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xA3";
+ break;
+ case 0xB8 :
+ return "\xEC\x84\xBF";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\x9B";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xB7";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x93";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xAF";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x8B";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xA7";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x83";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\x9F";
+ break;
+ case 0xB4 :
+ return "\xEC\x88\xBB";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x97";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xB3";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x8F";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xAB";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x87";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xA3";
+ break;
+ case 0xB8 :
+ return "\xEC\x8B\xBF";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\x9B";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xB7";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x93";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xAF";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x8B";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xA7";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x83";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\x9F";
+ break;
+ case 0xB4 :
+ return "\xEC\x8F\xBB";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x97";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xB3";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x8F";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xAB";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x87";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xA3";
+ break;
+ case 0xB8 :
+ return "\xEC\x92\xBF";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\x9B";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xB7";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x93";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xAF";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x8B";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xA7";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x83";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\x9F";
+ break;
+ case 0xB4 :
+ return "\xEC\x96\xBB";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x97";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xB3";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x8F";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xAB";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x87";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xA3";
+ break;
+ case 0xB8 :
+ return "\xEC\x99\xBF";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\x9B";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xB7";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x93";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xAF";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x8B";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xA7";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x83";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\x9F";
+ break;
+ case 0xB4 :
+ return "\xEC\x9D\xBB";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x97";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xB3";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x8F";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xAB";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x87";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xA3";
+ break;
+ case 0xB8 :
+ return "\xEC\xA0\xBF";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\x9B";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xB7";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x93";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xAF";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x8B";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xA7";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x83";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\x9F";
+ break;
+ case 0xB4 :
+ return "\xEC\xA4\xBB";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x97";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xB3";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x8F";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xAB";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x87";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xA3";
+ break;
+ case 0xB8 :
+ return "\xEC\xA7\xBF";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\x9B";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xB7";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x93";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xAF";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x8B";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xA7";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x83";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\x9F";
+ break;
+ case 0xB4 :
+ return "\xEC\xAB\xBB";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x97";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xB3";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x8F";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xAB";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x87";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xA3";
+ break;
+ case 0xB8 :
+ return "\xEC\xAE\xBF";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\x9B";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xB7";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x93";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xAF";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x8B";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xA7";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x83";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\x9F";
+ break;
+ case 0xB4 :
+ return "\xEC\xB2\xBB";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x97";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xB3";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x8F";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xAB";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x87";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xA3";
+ break;
+ case 0xB8 :
+ return "\xEC\xB5\xBF";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\x9B";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xB7";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x93";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xAF";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x8B";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xA7";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x83";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\x9F";
+ break;
+ case 0xB4 :
+ return "\xEC\xB9\xBB";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x97";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xB3";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x8F";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xAB";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x87";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xA3";
+ break;
+ case 0xB8 :
+ return "\xEC\xBC\xBF";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\x9B";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xB7";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x93";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xAF";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x8B";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xA7";
+ break;
+ case 0xBC :
+ return "\xED\x80\x83";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\x9F";
+ break;
+ case 0xB4 :
+ return "\xED\x80\xBB";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x97";
+ break;
+ case 0xAC :
+ return "\xED\x81\xB3";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x8F";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xAB";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x87";
+ break;
+ case 0x9C :
+ return "\xED\x83\xA3";
+ break;
+ case 0xB8 :
+ return "\xED\x83\xBF";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\x9B";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xB7";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x93";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xAF";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x8B";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xA7";
+ break;
+ case 0xBC :
+ return "\xED\x87\x83";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\x9F";
+ break;
+ case 0xB4 :
+ return "\xED\x87\xBB";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x97";
+ break;
+ case 0xAC :
+ return "\xED\x88\xB3";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x8F";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xAB";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x87";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xA3";
+ break;
+ case 0xB8 :
+ return "\xED\x8A\xBF";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\x9B";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xB7";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x93";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xAF";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x8B";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xA7";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x83";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\x9F";
+ break;
+ case 0xB4 :
+ return "\xED\x8E\xBB";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x97";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xB3";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x8F";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xAB";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x87";
+ break;
+ case 0x9C :
+ return "\xED\x91\xA3";
+ break;
+ case 0xB8 :
+ return "\xED\x91\xBF";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\x9B";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xB7";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x93";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xAF";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x8B";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xA7";
+ break;
+ case 0xBC :
+ return "\xED\x95\x83";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\x9F";
+ break;
+ case 0xB4 :
+ return "\xED\x95\xBB";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x97";
+ break;
+ case 0xAC :
+ return "\xED\x96\xB3";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x8F";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xAB";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x87";
+ break;
+ case 0x9C :
+ return "\xED\x98\xA3";
+ break;
+ case 0xB8 :
+ return "\xED\x98\xBF";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\x9B";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xB7";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x93";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xAF";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x8B";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xA7";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x83";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\x9F";
+ break;
+ case 0xB4 :
+ return "\xED\x9C\xBB";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x97";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xB3";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x8F";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x88";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xA4";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x80";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\x9C";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xB8";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x94";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xB0";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x8C";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xA8";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x84";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xA0";
+ break;
+ case 0xB4 :
+ return "\xEA\xB4\xBC";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x98";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xB4";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x90";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xAC";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x88";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xA4";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x80";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\x9C";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xB8";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x94";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xB0";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x8C";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xA8";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x84";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xA0";
+ break;
+ case 0xB4 :
+ return "\xEA\xBB\xBC";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x98";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xB4";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x90";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xAC";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x88";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xA4";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x80";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\x9C";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xB8";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x94";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xB0";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x8C";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xA8";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x84";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xA0";
+ break;
+ case 0xB4 :
+ return "\xEB\x82\xBC";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x98";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xB4";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x90";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xAC";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x88";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xA4";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x80";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\x9C";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xB8";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x94";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xB0";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x8C";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xA8";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x84";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xA0";
+ break;
+ case 0xB4 :
+ return "\xEB\x89\xBC";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x98";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xB4";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x90";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xAC";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x88";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xA4";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x80";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\x9C";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xB8";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x94";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xB0";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x8C";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xA8";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x84";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xA0";
+ break;
+ case 0xB4 :
+ return "\xEB\x90\xBC";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x98";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xB4";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x90";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xAC";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x88";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xA4";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x80";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\x9C";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xB8";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x94";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xB0";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x8C";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xA8";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x84";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xA0";
+ break;
+ case 0xB4 :
+ return "\xEB\x97\xBC";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x98";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xB4";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x90";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xAC";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x88";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xA4";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x80";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\x9C";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xB8";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x94";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xB0";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x8C";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xA8";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x84";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xA0";
+ break;
+ case 0xB4 :
+ return "\xEB\x9E\xBC";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x98";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xB4";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x90";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xAC";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x88";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xA4";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x80";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\x9C";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xB8";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x94";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xB0";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x8C";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xA8";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x84";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xA0";
+ break;
+ case 0xB4 :
+ return "\xEB\xA5\xBC";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x98";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xB4";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x90";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xAC";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x88";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xA4";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x80";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\x9C";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xB8";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x94";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xB0";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x8C";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xA8";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x84";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xA0";
+ break;
+ case 0xB4 :
+ return "\xEB\xAC\xBC";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x98";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xB4";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x90";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xAC";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x88";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xA4";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x80";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\x9C";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xB8";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x94";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xB0";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x8C";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xA8";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x84";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xA0";
+ break;
+ case 0xB4 :
+ return "\xEB\xB3\xBC";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x98";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xB4";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x90";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xAC";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x88";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xA4";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x80";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\x9C";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xB8";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x94";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xB0";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x8C";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xA8";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x84";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xA0";
+ break;
+ case 0xB4 :
+ return "\xEB\xBA\xBC";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x98";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xB4";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x90";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xAC";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x88";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xA4";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x80";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\x9C";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xB8";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x94";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xB0";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x8C";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xA8";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x84";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xA0";
+ break;
+ case 0xB4 :
+ return "\xEC\x81\xBC";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x98";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xB4";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x90";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xAC";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x88";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xA4";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x80";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\x9C";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xB8";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x94";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xB0";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x8C";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xA8";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x84";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xA0";
+ break;
+ case 0xB4 :
+ return "\xEC\x88\xBC";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x98";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xB4";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x90";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xAC";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x88";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xA4";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x80";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\x9C";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xB8";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x94";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xB0";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x8C";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xA8";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x84";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xA0";
+ break;
+ case 0xB4 :
+ return "\xEC\x8F\xBC";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x98";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xB4";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x90";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xAC";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x88";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xA4";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x80";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\x9C";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xB8";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x94";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xB0";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x8C";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xA8";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x84";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xA0";
+ break;
+ case 0xB4 :
+ return "\xEC\x96\xBC";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x98";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xB4";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x90";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xAC";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x88";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xA4";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x80";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\x9C";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xB8";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x94";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xB0";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x8C";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xA8";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x84";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xA0";
+ break;
+ case 0xB4 :
+ return "\xEC\x9D\xBC";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x98";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xB4";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x90";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xAC";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x88";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xA4";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x80";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\x9C";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xB8";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x94";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xB0";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x8C";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xA8";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x84";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xA0";
+ break;
+ case 0xB4 :
+ return "\xEC\xA4\xBC";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x98";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xB4";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x90";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xAC";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x88";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xA4";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x80";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\x9C";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xB8";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x94";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xB0";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x8C";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xA8";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x84";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xA0";
+ break;
+ case 0xB4 :
+ return "\xEC\xAB\xBC";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x98";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xB4";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x90";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xAC";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x88";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xA4";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x80";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\x9C";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xB8";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x94";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xB0";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x8C";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xA8";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x84";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xA0";
+ break;
+ case 0xB4 :
+ return "\xEC\xB2\xBC";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x98";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xB4";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x90";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xAC";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x88";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xA4";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x80";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\x9C";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xB8";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x94";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xB0";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x8C";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xA8";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x84";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xA0";
+ break;
+ case 0xB4 :
+ return "\xEC\xB9\xBC";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x98";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xB4";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x90";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xAC";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x88";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xA4";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x80";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\x9C";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xB8";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x94";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xB0";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x8C";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xA8";
+ break;
+ case 0xBC :
+ return "\xED\x80\x84";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xA0";
+ break;
+ case 0xB4 :
+ return "\xED\x80\xBC";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x98";
+ break;
+ case 0xAC :
+ return "\xED\x81\xB4";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x90";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xAC";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x88";
+ break;
+ case 0x9C :
+ return "\xED\x83\xA4";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x80";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\x9C";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xB8";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x94";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xB0";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x8C";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xA8";
+ break;
+ case 0xBC :
+ return "\xED\x87\x84";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xA0";
+ break;
+ case 0xB4 :
+ return "\xED\x87\xBC";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x98";
+ break;
+ case 0xAC :
+ return "\xED\x88\xB4";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x90";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xAC";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x88";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xA4";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x80";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\x9C";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xB8";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x94";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xB0";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x8C";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xA8";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x84";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xA0";
+ break;
+ case 0xB4 :
+ return "\xED\x8E\xBC";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x98";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xB4";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x90";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xAC";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x88";
+ break;
+ case 0x9C :
+ return "\xED\x91\xA4";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x80";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\x9C";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xB8";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x94";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xB0";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x8C";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xA8";
+ break;
+ case 0xBC :
+ return "\xED\x95\x84";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xA0";
+ break;
+ case 0xB4 :
+ return "\xED\x95\xBC";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x98";
+ break;
+ case 0xAC :
+ return "\xED\x96\xB4";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x90";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xAC";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x88";
+ break;
+ case 0x9C :
+ return "\xED\x98\xA4";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x80";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\x9C";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xB8";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x94";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xB0";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x8C";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xA8";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x84";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xA0";
+ break;
+ case 0xB4 :
+ return "\xED\x9C\xBC";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x98";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xB4";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x90";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x89";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xA5";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x81";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\x9D";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xB9";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x95";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xB1";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x8D";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xA9";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x85";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xA1";
+ break;
+ case 0xB4 :
+ return "\xEA\xB4\xBD";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x99";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xB5";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x91";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xAD";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x89";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xA5";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x81";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\x9D";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xB9";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x95";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xB1";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x8D";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xA9";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x85";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xA1";
+ break;
+ case 0xB4 :
+ return "\xEA\xBB\xBD";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x99";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xB5";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x91";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xAD";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x89";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xA5";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x81";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\x9D";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xB9";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x95";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xB1";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x8D";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xA9";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x85";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xA1";
+ break;
+ case 0xB4 :
+ return "\xEB\x82\xBD";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x99";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xB5";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x91";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xAD";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x89";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xA5";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x81";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\x9D";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xB9";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x95";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xB1";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x8D";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xA9";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x85";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xA1";
+ break;
+ case 0xB4 :
+ return "\xEB\x89\xBD";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x99";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xB5";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x91";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xAD";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x89";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xA5";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x81";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\x9D";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xB9";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x95";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xB1";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x8D";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xA9";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x85";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xA1";
+ break;
+ case 0xB4 :
+ return "\xEB\x90\xBD";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x99";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xB5";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x91";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xAD";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x89";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xA5";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x81";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\x9D";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xB9";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x95";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xB1";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x8D";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xA9";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x85";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xA1";
+ break;
+ case 0xB4 :
+ return "\xEB\x97\xBD";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x99";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xB5";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x91";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xAD";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x89";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xA5";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x81";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\x9D";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xB9";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x95";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xB1";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x8D";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xA9";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x85";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xA1";
+ break;
+ case 0xB4 :
+ return "\xEB\x9E\xBD";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x99";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xB5";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x91";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xAD";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x89";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xA5";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x81";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\x9D";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xB9";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x95";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xB1";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x8D";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xA9";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x85";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xA1";
+ break;
+ case 0xB4 :
+ return "\xEB\xA5\xBD";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x99";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xB5";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x91";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xAD";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x89";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xA5";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x81";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\x9D";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xB9";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x95";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xB1";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x8D";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xA9";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x85";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xA1";
+ break;
+ case 0xB4 :
+ return "\xEB\xAC\xBD";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x99";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xB5";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x91";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xAD";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x89";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xA5";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x81";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\x9D";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xB9";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x95";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xB1";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x8D";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xA9";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x85";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xA1";
+ break;
+ case 0xB4 :
+ return "\xEB\xB3\xBD";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x99";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xB5";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x91";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xAD";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x89";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xA5";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x81";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\x9D";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xB9";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x95";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xB1";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x8D";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xA9";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x85";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xA1";
+ break;
+ case 0xB4 :
+ return "\xEB\xBA\xBD";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x99";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xB5";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x91";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xAD";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x89";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xA5";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x81";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\x9D";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xB9";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x95";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xB1";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x8D";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xA9";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x85";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xA1";
+ break;
+ case 0xB4 :
+ return "\xEC\x81\xBD";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x99";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xB5";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x91";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xAD";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x89";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xA5";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x81";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\x9D";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xB9";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x95";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xB1";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x8D";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xA9";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x85";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xA1";
+ break;
+ case 0xB4 :
+ return "\xEC\x88\xBD";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x99";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xB5";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x91";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xAD";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x89";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xA5";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x81";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\x9D";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xB9";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x95";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xB1";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x8D";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xA9";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x85";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xA1";
+ break;
+ case 0xB4 :
+ return "\xEC\x8F\xBD";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x99";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xB5";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x91";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xAD";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x89";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xA5";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x81";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\x9D";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xB9";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x95";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xB1";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x8D";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xA9";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x85";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xA1";
+ break;
+ case 0xB4 :
+ return "\xEC\x96\xBD";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x99";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xB5";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x91";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xAD";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x89";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xA5";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x81";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\x9D";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xB9";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x95";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xB1";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x8D";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xA9";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x85";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xA1";
+ break;
+ case 0xB4 :
+ return "\xEC\x9D\xBD";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x99";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xB5";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x91";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xAD";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x89";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xA5";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x81";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\x9D";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xB9";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x95";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xB1";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x8D";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xA9";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x85";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xA1";
+ break;
+ case 0xB4 :
+ return "\xEC\xA4\xBD";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x99";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xB5";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x91";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xAD";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x89";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xA5";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x81";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\x9D";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xB9";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x95";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xB1";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x8D";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xA9";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x85";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xA1";
+ break;
+ case 0xB4 :
+ return "\xEC\xAB\xBD";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x99";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xB5";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x91";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xAD";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x89";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xA5";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x81";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\x9D";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xB9";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x95";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xB1";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x8D";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xA9";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x85";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xA1";
+ break;
+ case 0xB4 :
+ return "\xEC\xB2\xBD";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x99";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xB5";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x91";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xAD";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x89";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xA5";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x81";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\x9D";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xB9";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x95";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xB1";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x8D";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xA9";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x85";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xA1";
+ break;
+ case 0xB4 :
+ return "\xEC\xB9\xBD";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x99";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xB5";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x91";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xAD";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x89";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xA5";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x81";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\x9D";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xB9";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x95";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xB1";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x8D";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xA9";
+ break;
+ case 0xBC :
+ return "\xED\x80\x85";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xA1";
+ break;
+ case 0xB4 :
+ return "\xED\x80\xBD";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x99";
+ break;
+ case 0xAC :
+ return "\xED\x81\xB5";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x91";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xAD";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x89";
+ break;
+ case 0x9C :
+ return "\xED\x83\xA5";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x81";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\x9D";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xB9";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x95";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xB1";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x8D";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xA9";
+ break;
+ case 0xBC :
+ return "\xED\x87\x85";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xA1";
+ break;
+ case 0xB4 :
+ return "\xED\x87\xBD";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x99";
+ break;
+ case 0xAC :
+ return "\xED\x88\xB5";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x91";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xAD";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x89";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xA5";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x81";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\x9D";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xB9";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x95";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xB1";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x8D";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xA9";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x85";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xA1";
+ break;
+ case 0xB4 :
+ return "\xED\x8E\xBD";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x99";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xB5";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x91";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xAD";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x89";
+ break;
+ case 0x9C :
+ return "\xED\x91\xA5";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x81";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\x9D";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xB9";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x95";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xB1";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x8D";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xA9";
+ break;
+ case 0xBC :
+ return "\xED\x95\x85";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xA1";
+ break;
+ case 0xB4 :
+ return "\xED\x95\xBD";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x99";
+ break;
+ case 0xAC :
+ return "\xED\x96\xB5";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x91";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xAD";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x89";
+ break;
+ case 0x9C :
+ return "\xED\x98\xA5";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x81";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\x9D";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xB9";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x95";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xB1";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x8D";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xA9";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x85";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xA1";
+ break;
+ case 0xB4 :
+ return "\xED\x9C\xBD";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x99";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xB5";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x91";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x8A";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xA6";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x82";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\x9E";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xBA";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x96";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xB2";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x8E";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xAA";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x86";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xA2";
+ break;
+ case 0xB4 :
+ return "\xEA\xB4\xBE";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x9A";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xB6";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x92";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xAE";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x8A";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xA6";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x82";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\x9E";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xBA";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x96";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xB2";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x8E";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xAA";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x86";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xA2";
+ break;
+ case 0xB4 :
+ return "\xEA\xBB\xBE";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x9A";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xB6";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x92";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xAE";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x8A";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xA6";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x82";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\x9E";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xBA";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x96";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xB2";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x8E";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xAA";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x86";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xA2";
+ break;
+ case 0xB4 :
+ return "\xEB\x82\xBE";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x9A";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xB6";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x92";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xAE";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x8A";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xA6";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x82";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\x9E";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xBA";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x96";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xB2";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x8E";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xAA";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x86";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xA2";
+ break;
+ case 0xB4 :
+ return "\xEB\x89\xBE";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x9A";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xB6";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x92";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xAE";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x8A";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xA6";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x82";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\x9E";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xBA";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x96";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xB2";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x8E";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xAA";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x86";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xA2";
+ break;
+ case 0xB4 :
+ return "\xEB\x90\xBE";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x9A";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xB6";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x92";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xAE";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x8A";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xA6";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x82";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\x9E";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xBA";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x96";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xB2";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x8E";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xAA";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x86";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xA2";
+ break;
+ case 0xB4 :
+ return "\xEB\x97\xBE";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x9A";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xB6";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x92";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xAE";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x8A";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xA6";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x82";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\x9E";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xBA";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x96";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xB2";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x8E";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xAA";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x86";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xA2";
+ break;
+ case 0xB4 :
+ return "\xEB\x9E\xBE";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x9A";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xB6";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x92";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xAE";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x8A";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xA6";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x82";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\x9E";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xBA";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x96";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xB2";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x8E";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xAA";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x86";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xA2";
+ break;
+ case 0xB4 :
+ return "\xEB\xA5\xBE";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x9A";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xB6";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x92";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xAE";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x8A";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xA6";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x82";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\x9E";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xBA";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x96";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xB2";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x8E";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xAA";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x86";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xA2";
+ break;
+ case 0xB4 :
+ return "\xEB\xAC\xBE";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x9A";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xB6";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x92";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xAE";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x8A";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xA6";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x82";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\x9E";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xBA";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x96";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xB2";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x8E";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xAA";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x86";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xA2";
+ break;
+ case 0xB4 :
+ return "\xEB\xB3\xBE";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x9A";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xB6";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x92";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xAE";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x8A";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xA6";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x82";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\x9E";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xBA";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x96";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xB2";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x8E";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xAA";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x86";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xA2";
+ break;
+ case 0xB4 :
+ return "\xEB\xBA\xBE";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x9A";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xB6";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x92";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xAE";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x8A";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xA6";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x82";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\x9E";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xBA";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x96";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xB2";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x8E";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xAA";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x86";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xA2";
+ break;
+ case 0xB4 :
+ return "\xEC\x81\xBE";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x9A";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xB6";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x92";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xAE";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x8A";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xA6";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x82";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\x9E";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xBA";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x96";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xB2";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x8E";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xAA";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x86";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xA2";
+ break;
+ case 0xB4 :
+ return "\xEC\x88\xBE";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x9A";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xB6";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x92";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xAE";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x8A";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xA6";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x82";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\x9E";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xBA";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x96";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xB2";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x8E";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xAA";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x86";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xA2";
+ break;
+ case 0xB4 :
+ return "\xEC\x8F\xBE";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x9A";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xB6";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x92";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xAE";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x8A";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xA6";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x82";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\x9E";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xBA";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x96";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xB2";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x8E";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xAA";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x86";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xA2";
+ break;
+ case 0xB4 :
+ return "\xEC\x96\xBE";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x9A";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xB6";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x92";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xAE";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x8A";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xA6";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x82";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\x9E";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xBA";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x96";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xB2";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x8E";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xAA";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x86";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xA2";
+ break;
+ case 0xB4 :
+ return "\xEC\x9D\xBE";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x9A";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xB6";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x92";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xAE";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x8A";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xA6";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x82";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\x9E";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xBA";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x96";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xB2";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x8E";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xAA";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x86";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xA2";
+ break;
+ case 0xB4 :
+ return "\xEC\xA4\xBE";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x9A";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xB6";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x92";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xAE";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x8A";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xA6";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x82";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\x9E";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xBA";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x96";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xB2";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x8E";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xAA";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x86";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xA2";
+ break;
+ case 0xB4 :
+ return "\xEC\xAB\xBE";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x9A";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xB6";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x92";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xAE";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x8A";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xA6";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x82";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\x9E";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xBA";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x96";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xB2";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x8E";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xAA";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x86";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xA2";
+ break;
+ case 0xB4 :
+ return "\xEC\xB2\xBE";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x9A";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xB6";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x92";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xAE";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x8A";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xA6";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x82";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\x9E";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xBA";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x96";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xB2";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x8E";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xAA";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x86";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xA2";
+ break;
+ case 0xB4 :
+ return "\xEC\xB9\xBE";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x9A";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xB6";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x92";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xAE";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x8A";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xA6";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x82";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\x9E";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xBA";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x96";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xB2";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x8E";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xAA";
+ break;
+ case 0xBC :
+ return "\xED\x80\x86";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xA2";
+ break;
+ case 0xB4 :
+ return "\xED\x80\xBE";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x9A";
+ break;
+ case 0xAC :
+ return "\xED\x81\xB6";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x92";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xAE";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x8A";
+ break;
+ case 0x9C :
+ return "\xED\x83\xA6";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x82";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\x9E";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xBA";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x96";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xB2";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x8E";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xAA";
+ break;
+ case 0xBC :
+ return "\xED\x87\x86";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xA2";
+ break;
+ case 0xB4 :
+ return "\xED\x87\xBE";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x9A";
+ break;
+ case 0xAC :
+ return "\xED\x88\xB6";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x92";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xAE";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x8A";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xA6";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x82";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\x9E";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xBA";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x96";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xB2";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x8E";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xAA";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x86";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xA2";
+ break;
+ case 0xB4 :
+ return "\xED\x8E\xBE";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x9A";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xB6";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x92";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xAE";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x8A";
+ break;
+ case 0x9C :
+ return "\xED\x91\xA6";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x82";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\x9E";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xBA";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x96";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xB2";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x8E";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xAA";
+ break;
+ case 0xBC :
+ return "\xED\x95\x86";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xA2";
+ break;
+ case 0xB4 :
+ return "\xED\x95\xBE";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x9A";
+ break;
+ case 0xAC :
+ return "\xED\x96\xB6";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x92";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xAE";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x8A";
+ break;
+ case 0x9C :
+ return "\xED\x98\xA6";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x82";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\x9E";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xBA";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x96";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xB2";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x8E";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xAA";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x86";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xA2";
+ break;
+ case 0xB4 :
+ return "\xED\x9C\xBE";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x9A";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xB6";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x92";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x8B";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xA7";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x83";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\x9F";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xBB";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x97";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xB3";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x8F";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xAB";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x87";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xA3";
+ break;
+ case 0xB4 :
+ return "\xEA\xB4\xBF";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x9B";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xB7";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x93";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xAF";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x8B";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xA7";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x83";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\x9F";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xBB";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x97";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xB3";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x8F";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xAB";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x87";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xA3";
+ break;
+ case 0xB4 :
+ return "\xEA\xBB\xBF";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x9B";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xB7";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x93";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xAF";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x8B";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xA7";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x83";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\x9F";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xBB";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x97";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xB3";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x8F";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xAB";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x87";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xA3";
+ break;
+ case 0xB4 :
+ return "\xEB\x82\xBF";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x9B";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xB7";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x93";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xAF";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x8B";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xA7";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x83";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\x9F";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xBB";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x97";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xB3";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x8F";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xAB";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x87";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xA3";
+ break;
+ case 0xB4 :
+ return "\xEB\x89\xBF";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x9B";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xB7";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x93";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xAF";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x8B";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xA7";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x83";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\x9F";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xBB";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x97";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xB3";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x8F";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xAB";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x87";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xA3";
+ break;
+ case 0xB4 :
+ return "\xEB\x90\xBF";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x9B";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xB7";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x93";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xAF";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x8B";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xA7";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x83";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\x9F";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xBB";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x97";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xB3";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x8F";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xAB";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x87";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xA3";
+ break;
+ case 0xB4 :
+ return "\xEB\x97\xBF";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x9B";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xB7";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x93";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xAF";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x8B";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xA7";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x83";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\x9F";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xBB";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x97";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xB3";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x8F";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xAB";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x87";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xA3";
+ break;
+ case 0xB4 :
+ return "\xEB\x9E\xBF";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x9B";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xB7";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x93";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xAF";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x8B";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xA7";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x83";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\x9F";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xBB";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x97";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xB3";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x8F";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xAB";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x87";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xA3";
+ break;
+ case 0xB4 :
+ return "\xEB\xA5\xBF";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x9B";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xB7";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x93";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xAF";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x8B";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xA7";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x83";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\x9F";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xBB";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x97";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xB3";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x8F";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xAB";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x87";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xA3";
+ break;
+ case 0xB4 :
+ return "\xEB\xAC\xBF";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x9B";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xB7";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x93";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xAF";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x8B";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xA7";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x83";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\x9F";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xBB";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x97";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xB3";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x8F";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xAB";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x87";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xA3";
+ break;
+ case 0xB4 :
+ return "\xEB\xB3\xBF";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x9B";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xB7";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x93";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xAF";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x8B";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xA7";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x83";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\x9F";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xBB";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x97";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xB3";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x8F";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xAB";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x87";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xA3";
+ break;
+ case 0xB4 :
+ return "\xEB\xBA\xBF";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x9B";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xB7";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x93";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xAF";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x8B";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xA7";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x83";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\x9F";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xBB";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x97";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xB3";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x8F";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xAB";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x87";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xA3";
+ break;
+ case 0xB4 :
+ return "\xEC\x81\xBF";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x9B";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xB7";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x93";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xAF";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x8B";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xA7";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x83";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\x9F";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xBB";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x97";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xB3";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x8F";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xAB";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x87";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xA3";
+ break;
+ case 0xB4 :
+ return "\xEC\x88\xBF";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x9B";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xB7";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x93";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xAF";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x8B";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xA7";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x83";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\x9F";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xBB";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x97";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xB3";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x8F";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xAB";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x87";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xA3";
+ break;
+ case 0xB4 :
+ return "\xEC\x8F\xBF";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x9B";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xB7";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x93";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xAF";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x8B";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xA7";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x83";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\x9F";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xBB";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x97";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xB3";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x8F";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xAB";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x87";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xA3";
+ break;
+ case 0xB4 :
+ return "\xEC\x96\xBF";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x9B";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xB7";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x93";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xAF";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x8B";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xA7";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x83";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\x9F";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xBB";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x97";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xB3";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x8F";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xAB";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x87";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xA3";
+ break;
+ case 0xB4 :
+ return "\xEC\x9D\xBF";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x9B";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xB7";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x93";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xAF";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x8B";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xA7";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x83";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\x9F";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xBB";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x97";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xB3";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x8F";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xAB";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x87";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xA3";
+ break;
+ case 0xB4 :
+ return "\xEC\xA4\xBF";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x9B";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xB7";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x93";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xAF";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x8B";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xA7";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x83";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\x9F";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xBB";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x97";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xB3";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x8F";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xAB";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x87";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xA3";
+ break;
+ case 0xB4 :
+ return "\xEC\xAB\xBF";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x9B";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xB7";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x93";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xAF";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x8B";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xA7";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x83";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\x9F";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xBB";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x97";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xB3";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x8F";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xAB";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x87";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xA3";
+ break;
+ case 0xB4 :
+ return "\xEC\xB2\xBF";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x9B";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xB7";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x93";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xAF";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x8B";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xA7";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x83";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\x9F";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xBB";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x97";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xB3";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x8F";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xAB";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x87";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xA3";
+ break;
+ case 0xB4 :
+ return "\xEC\xB9\xBF";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x9B";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xB7";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x93";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xAF";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x8B";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xA7";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x83";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\x9F";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xBB";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x97";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xB3";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x8F";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xAB";
+ break;
+ case 0xBC :
+ return "\xED\x80\x87";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xA3";
+ break;
+ case 0xB4 :
+ return "\xED\x80\xBF";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x9B";
+ break;
+ case 0xAC :
+ return "\xED\x81\xB7";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x93";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xAF";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x8B";
+ break;
+ case 0x9C :
+ return "\xED\x83\xA7";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x83";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\x9F";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xBB";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x97";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xB3";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x8F";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xAB";
+ break;
+ case 0xBC :
+ return "\xED\x87\x87";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xA3";
+ break;
+ case 0xB4 :
+ return "\xED\x87\xBF";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x9B";
+ break;
+ case 0xAC :
+ return "\xED\x88\xB7";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x93";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xAF";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x8B";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xA7";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x83";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\x9F";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xBB";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x97";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xB3";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x8F";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xAB";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x87";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xA3";
+ break;
+ case 0xB4 :
+ return "\xED\x8E\xBF";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x9B";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xB7";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x93";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xAF";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x8B";
+ break;
+ case 0x9C :
+ return "\xED\x91\xA7";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x83";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\x9F";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xBB";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x97";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xB3";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x8F";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xAB";
+ break;
+ case 0xBC :
+ return "\xED\x95\x87";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xA3";
+ break;
+ case 0xB4 :
+ return "\xED\x95\xBF";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x9B";
+ break;
+ case 0xAC :
+ return "\xED\x96\xB7";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x93";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xAF";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x8B";
+ break;
+ case 0x9C :
+ return "\xED\x98\xA7";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x83";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\x9F";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xBB";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x97";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xB3";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x8F";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xAB";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x87";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xA3";
+ break;
+ case 0xB4 :
+ return "\xED\x9C\xBF";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x9B";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xB7";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x93";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x8C";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xA8";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x84";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xA0";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xBC";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x98";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xB4";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x90";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xAC";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x88";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xA4";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x80";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x9C";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xB8";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x94";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xB0";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x8C";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xA8";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x84";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xA0";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xBC";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x98";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xB4";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x90";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xAC";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x88";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xA4";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x80";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x9C";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xB8";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x94";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xB0";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x8C";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xA8";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x84";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xA0";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xBC";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x98";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xB4";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x90";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xAC";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x88";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xA4";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x80";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x9C";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xB8";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x94";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xB0";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x8C";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xA8";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x84";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xA0";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xBC";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x98";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xB4";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x90";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xAC";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x88";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xA4";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x80";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x9C";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xB8";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x94";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xB0";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x8C";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xA8";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x84";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xA0";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xBC";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x98";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xB4";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x90";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xAC";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x88";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xA4";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x80";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x9C";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xB8";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x94";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xB0";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x8C";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xA8";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x84";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xA0";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xBC";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x98";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xB4";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x90";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xAC";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x88";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xA4";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x80";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x9C";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xB8";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x94";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xB0";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x8C";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xA8";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x84";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xA0";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xBC";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x98";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xB4";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x90";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xAC";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x88";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xA4";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x80";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x9C";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xB8";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x94";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xB0";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x8C";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xA8";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x84";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xA0";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xBC";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x98";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xB4";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x90";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xAC";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x88";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xA4";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x80";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x9C";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xB8";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x94";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xB0";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x8C";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xA8";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x84";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xA0";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xBC";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x98";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xB4";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x90";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xAC";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x88";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xA4";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x80";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x9C";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xB8";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x94";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xB0";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x8C";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xA8";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x84";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xA0";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xBC";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x98";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xB4";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x90";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xAC";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x88";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xA4";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x80";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x9C";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xB8";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x94";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xB0";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x8C";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xA8";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x84";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xA0";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xBC";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x98";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xB4";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x90";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xAC";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x88";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xA4";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x80";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x9C";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xB8";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x94";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xB0";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x8C";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xA8";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x84";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xA0";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xBC";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x98";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xB4";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x90";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xAC";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x88";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xA4";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x80";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x9C";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xB8";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x94";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xB0";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x8C";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xA8";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x84";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xA0";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xBC";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x98";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xB4";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x90";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xAC";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x88";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xA4";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x80";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x9C";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xB8";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x94";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xB0";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x8C";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xA8";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x84";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xA0";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xBC";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x98";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xB4";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x90";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xAC";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x88";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xA4";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x80";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x9C";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xB8";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x94";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xB0";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x8C";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xA8";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x84";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xA0";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xBC";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x98";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xB4";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x90";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xAC";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x88";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xA4";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x80";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x9C";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xB8";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x94";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xB0";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x8C";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xA8";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x84";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xA0";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xBC";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x98";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xB4";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x90";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xAC";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x88";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xA4";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x80";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x9C";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xB8";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x94";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xB0";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x8C";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xA8";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x84";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xA0";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xBC";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x98";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xB4";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x90";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xAC";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x88";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xA4";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x80";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x9C";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xB8";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x94";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xB0";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x8C";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xA8";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x84";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xA0";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xBC";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x98";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xB4";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x90";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xAC";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x88";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xA4";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x80";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x9C";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xB8";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x94";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xB0";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x8C";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xA8";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x84";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xA0";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xBC";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x98";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xB4";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x90";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xAC";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x88";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xA4";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x80";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x9C";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xB8";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x94";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xB0";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x8C";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xA8";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x84";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xA0";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xBC";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x98";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xB4";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x90";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xAC";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x88";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xA4";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x80";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x9C";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xB8";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x94";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xB0";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x8C";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xA8";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x84";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xA0";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xBC";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x98";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xB4";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x90";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xAC";
+ break;
+ case 0xBC :
+ return "\xED\x80\x88";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xA4";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x80";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x9C";
+ break;
+ case 0xAC :
+ return "\xED\x81\xB8";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x94";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xB0";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x8C";
+ break;
+ case 0x9C :
+ return "\xED\x83\xA8";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x84";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xA0";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xBC";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x98";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xB4";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x90";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xAC";
+ break;
+ case 0xBC :
+ return "\xED\x87\x88";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xA4";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x80";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x9C";
+ break;
+ case 0xAC :
+ return "\xED\x88\xB8";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x94";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xB0";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x8C";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xA8";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x84";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xA0";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xBC";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x98";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xB4";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x90";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xAC";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x88";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xA4";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x80";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x9C";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xB8";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x94";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xB0";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x8C";
+ break;
+ case 0x9C :
+ return "\xED\x91\xA8";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x84";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xA0";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xBC";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x98";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xB4";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x90";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xAC";
+ break;
+ case 0xBC :
+ return "\xED\x95\x88";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xA4";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x80";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x9C";
+ break;
+ case 0xAC :
+ return "\xED\x96\xB8";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x94";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xB0";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x8C";
+ break;
+ case 0x9C :
+ return "\xED\x98\xA8";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x84";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xA0";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xBC";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x98";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xB4";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x90";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xAC";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x88";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xA4";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x80";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x9C";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xB8";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x94";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x8D";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xA9";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x85";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xA1";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xBD";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x99";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xB5";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x91";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xAD";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x89";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xA5";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x81";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x9D";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xB9";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x95";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xB1";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x8D";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xA9";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x85";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xA1";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xBD";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x99";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xB5";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x91";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xAD";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x89";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xA5";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x81";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x9D";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xB9";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x95";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xB1";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x8D";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xA9";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x85";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xA1";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xBD";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x99";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xB5";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x91";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xAD";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x89";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xA5";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x81";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x9D";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xB9";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x95";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xB1";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x8D";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xA9";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x85";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xA1";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xBD";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x99";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xB5";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x91";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xAD";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x89";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xA5";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x81";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x9D";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xB9";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x95";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xB1";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x8D";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xA9";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x85";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xA1";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xBD";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x99";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xB5";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x91";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xAD";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x89";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xA5";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x81";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x9D";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xB9";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x95";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xB1";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x8D";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xA9";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x85";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xA1";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xBD";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x99";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xB5";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x91";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xAD";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x89";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xA5";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x81";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x9D";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xB9";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x95";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xB1";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x8D";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xA9";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x85";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xA1";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xBD";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x99";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xB5";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x91";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xAD";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x89";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xA5";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x81";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x9D";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xB9";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x95";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xB1";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x8D";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xA9";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x85";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xA1";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xBD";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x99";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xB5";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x91";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xAD";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x89";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xA5";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x81";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x9D";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xB9";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x95";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xB1";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x8D";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xA9";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x85";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xA1";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xBD";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x99";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xB5";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x91";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xAD";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x89";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xA5";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x81";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x9D";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xB9";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x95";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xB1";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x8D";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xA9";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x85";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xA1";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xBD";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x99";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xB5";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x91";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xAD";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x89";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xA5";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x81";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x9D";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xB9";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x95";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xB1";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x8D";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xA9";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x85";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xA1";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xBD";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x99";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xB5";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x91";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xAD";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x89";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xA5";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x81";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x9D";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xB9";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x95";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xB1";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x8D";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xA9";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x85";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xA1";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xBD";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x99";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xB5";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x91";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xAD";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x89";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xA5";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x81";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x9D";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xB9";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x95";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xB1";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x8D";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xA9";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x85";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xA1";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xBD";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x99";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xB5";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x91";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xAD";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x89";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xA5";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x81";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x9D";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xB9";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x95";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xB1";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x8D";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xA9";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x85";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xA1";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xBD";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x99";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xB5";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x91";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xAD";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x89";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xA5";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x81";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x9D";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xB9";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x95";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xB1";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x8D";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xA9";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x85";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xA1";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xBD";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x99";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xB5";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x91";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xAD";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x89";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xA5";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x81";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x9D";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xB9";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x95";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xB1";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x8D";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xA9";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x85";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xA1";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xBD";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x99";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xB5";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x91";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xAD";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x89";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xA5";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x81";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x9D";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xB9";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x95";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xB1";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x8D";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xA9";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x85";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xA1";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xBD";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x99";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xB5";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x91";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xAD";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x89";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xA5";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x81";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x9D";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xB9";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x95";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xB1";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x8D";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xA9";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x85";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xA1";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xBD";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x99";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xB5";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x91";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xAD";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x89";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xA5";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x81";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x9D";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xB9";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x95";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xB1";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x8D";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xA9";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x85";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xA1";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xBD";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x99";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xB5";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x91";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xAD";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x89";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xA5";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x81";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x9D";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xB9";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x95";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xB1";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x8D";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xA9";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x85";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xA1";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xBD";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x99";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xB5";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x91";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xAD";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x89";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xA5";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x81";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x9D";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xB9";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x95";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xB1";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x8D";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xA9";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x85";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xA1";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xBD";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x99";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xB5";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x91";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xAD";
+ break;
+ case 0xBC :
+ return "\xED\x80\x89";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xA5";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x81";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x9D";
+ break;
+ case 0xAC :
+ return "\xED\x81\xB9";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x95";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xB1";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x8D";
+ break;
+ case 0x9C :
+ return "\xED\x83\xA9";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x85";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xA1";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xBD";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x99";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xB5";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x91";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xAD";
+ break;
+ case 0xBC :
+ return "\xED\x87\x89";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xA5";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x81";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x9D";
+ break;
+ case 0xAC :
+ return "\xED\x88\xB9";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x95";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xB1";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x8D";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xA9";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x85";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xA1";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xBD";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x99";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xB5";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x91";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xAD";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x89";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xA5";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x81";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x9D";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xB9";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x95";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xB1";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x8D";
+ break;
+ case 0x9C :
+ return "\xED\x91\xA9";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x85";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xA1";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xBD";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x99";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xB5";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x91";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xAD";
+ break;
+ case 0xBC :
+ return "\xED\x95\x89";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xA5";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x81";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x9D";
+ break;
+ case 0xAC :
+ return "\xED\x96\xB9";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x95";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xB1";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x8D";
+ break;
+ case 0x9C :
+ return "\xED\x98\xA9";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x85";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xA1";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xBD";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x99";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xB5";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x91";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xAD";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x89";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xA5";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x81";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x9D";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xB9";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x95";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x8E";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xAA";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x86";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xA2";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xBE";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x9A";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xB6";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x92";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xAE";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x8A";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xA6";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x82";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x9E";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xBA";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x96";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xB2";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x8E";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xAA";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x86";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xA2";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xBE";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x9A";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xB6";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x92";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xAE";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x8A";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xA6";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x82";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x9E";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xBA";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x96";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xB2";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x8E";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xAA";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x86";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xA2";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xBE";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x9A";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xB6";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x92";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xAE";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x8A";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xA6";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x82";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x9E";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xBA";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x96";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xB2";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x8E";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xAA";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x86";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xA2";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xBE";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x9A";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xB6";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x92";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xAE";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x8A";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xA6";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x82";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x9E";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xBA";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x96";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xB2";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x8E";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xAA";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x86";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xA2";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xBE";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x9A";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xB6";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x92";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xAE";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x8A";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xA6";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x82";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x9E";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xBA";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x96";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xB2";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x8E";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xAA";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x86";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xA2";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xBE";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x9A";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xB6";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x92";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xAE";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x8A";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xA6";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x82";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x9E";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xBA";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x96";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xB2";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x8E";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xAA";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x86";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xA2";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xBE";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x9A";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xB6";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x92";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xAE";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x8A";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xA6";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x82";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x9E";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xBA";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x96";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xB2";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x8E";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xAA";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x86";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xA2";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xBE";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x9A";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xB6";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x92";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xAE";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x8A";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xA6";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x82";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x9E";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xBA";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x96";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xB2";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x8E";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xAA";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x86";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xA2";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xBE";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x9A";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xB6";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x92";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xAE";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x8A";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xA6";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x82";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x9E";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xBA";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x96";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xB2";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x8E";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xAA";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x86";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xA2";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xBE";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x9A";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xB6";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x92";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xAE";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x8A";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xA6";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x82";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x9E";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xBA";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x96";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xB2";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x8E";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xAA";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x86";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xA2";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xBE";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x9A";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xB6";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x92";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xAE";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x8A";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xA6";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x82";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x9E";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xBA";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x96";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xB2";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x8E";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xAA";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x86";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xA2";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xBE";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x9A";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xB6";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x92";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xAE";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x8A";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xA6";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x82";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x9E";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xBA";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x96";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xB2";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x8E";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xAA";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x86";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xA2";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xBE";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x9A";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xB6";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x92";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xAE";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x8A";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xA6";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x82";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x9E";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xBA";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x96";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xB2";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x8E";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xAA";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x86";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xA2";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xBE";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x9A";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xB6";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x92";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xAE";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x8A";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xA6";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x82";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x9E";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xBA";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x96";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xB2";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x8E";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xAA";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x86";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xA2";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xBE";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x9A";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xB6";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x92";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xAE";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x8A";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xA6";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x82";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x9E";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xBA";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x96";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xB2";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x8E";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xAA";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x86";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xA2";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xBE";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x9A";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xB6";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x92";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xAE";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x8A";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xA6";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x82";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x9E";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xBA";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x96";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xB2";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x8E";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xAA";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x86";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xA2";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xBE";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x9A";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xB6";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x92";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xAE";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x8A";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xA6";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x82";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x9E";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xBA";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x96";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xB2";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x8E";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xAA";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x86";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xA2";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xBE";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x9A";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xB6";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x92";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xAE";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x8A";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xA6";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x82";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x9E";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xBA";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x96";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xB2";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x8E";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xAA";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x86";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xA2";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xBE";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x9A";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xB6";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x92";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xAE";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x8A";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xA6";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x82";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x9E";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xBA";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x96";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xB2";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x8E";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xAA";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x86";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xA2";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xBE";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x9A";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xB6";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x92";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xAE";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x8A";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xA6";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x82";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x9E";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xBA";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x96";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xB2";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x8E";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xAA";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x86";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xA2";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xBE";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x9A";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xB6";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x92";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xAE";
+ break;
+ case 0xBC :
+ return "\xED\x80\x8A";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xA6";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x82";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x9E";
+ break;
+ case 0xAC :
+ return "\xED\x81\xBA";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x96";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xB2";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x8E";
+ break;
+ case 0x9C :
+ return "\xED\x83\xAA";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x86";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xA2";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xBE";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x9A";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xB6";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x92";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xAE";
+ break;
+ case 0xBC :
+ return "\xED\x87\x8A";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xA6";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x82";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x9E";
+ break;
+ case 0xAC :
+ return "\xED\x88\xBA";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x96";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xB2";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x8E";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xAA";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x86";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xA2";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xBE";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x9A";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xB6";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x92";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xAE";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x8A";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xA6";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x82";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x9E";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xBA";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x96";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xB2";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x8E";
+ break;
+ case 0x9C :
+ return "\xED\x91\xAA";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x86";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xA2";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xBE";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x9A";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xB6";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x92";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xAE";
+ break;
+ case 0xBC :
+ return "\xED\x95\x8A";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xA6";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x82";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x9E";
+ break;
+ case 0xAC :
+ return "\xED\x96\xBA";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x96";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xB2";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x8E";
+ break;
+ case 0x9C :
+ return "\xED\x98\xAA";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x86";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xA2";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xBE";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x9A";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xB6";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x92";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xAE";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x8A";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xA6";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x82";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x9E";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xBA";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x96";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x8F";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xAB";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x87";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xA3";
+ break;
+ case 0xB0 :
+ return "\xEA\xB1\xBF";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x9B";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xB7";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x93";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xAF";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x8B";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xA7";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x83";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\x9F";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xBB";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x97";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xB3";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x8F";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xAB";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x87";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xA3";
+ break;
+ case 0xB0 :
+ return "\xEA\xB8\xBF";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x9B";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xB7";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x93";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xAF";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x8B";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xA7";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x83";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\x9F";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xBB";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x97";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xB3";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x8F";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xAB";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x87";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xA3";
+ break;
+ case 0xB0 :
+ return "\xEA\xBF\xBF";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x9B";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xB7";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x93";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xAF";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x8B";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xA7";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x83";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\x9F";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xBB";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x97";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xB3";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x8F";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xAB";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x87";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xA3";
+ break;
+ case 0xB0 :
+ return "\xEB\x86\xBF";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x9B";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xB7";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x93";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xAF";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x8B";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xA7";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x83";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\x9F";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xBB";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x97";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xB3";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x8F";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xAB";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x87";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xA3";
+ break;
+ case 0xB0 :
+ return "\xEB\x8D\xBF";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x9B";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xB7";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x93";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xAF";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x8B";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xA7";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x83";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\x9F";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xBB";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x97";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xB3";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x8F";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xAB";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x87";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xA3";
+ break;
+ case 0xB0 :
+ return "\xEB\x94\xBF";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x9B";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xB7";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x93";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xAF";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x8B";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xA7";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x83";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\x9F";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xBB";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x97";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xB3";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x8F";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xAB";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x87";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xA3";
+ break;
+ case 0xB0 :
+ return "\xEB\x9B\xBF";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x9B";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xB7";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x93";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xAF";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x8B";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xA7";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x83";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\x9F";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xBB";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x97";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xB3";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x8F";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xAB";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x87";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xA3";
+ break;
+ case 0xB0 :
+ return "\xEB\xA2\xBF";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x9B";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xB7";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x93";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xAF";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x8B";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xA7";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x83";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\x9F";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xBB";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x97";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xB3";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x8F";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xAB";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x87";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xA3";
+ break;
+ case 0xB0 :
+ return "\xEB\xA9\xBF";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x9B";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xB7";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x93";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xAF";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x8B";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xA7";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x83";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\x9F";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xBB";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x97";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xB3";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x8F";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xAB";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x87";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xA3";
+ break;
+ case 0xB0 :
+ return "\xEB\xB0\xBF";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x9B";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xB7";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x93";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xAF";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x8B";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xA7";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x83";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\x9F";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xBB";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x97";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xB3";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x8F";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xAB";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x87";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xA3";
+ break;
+ case 0xB0 :
+ return "\xEB\xB7\xBF";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x9B";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xB7";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x93";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xAF";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x8B";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xA7";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x83";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\x9F";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xBB";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x97";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xB3";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x8F";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xAB";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x87";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xA3";
+ break;
+ case 0xB0 :
+ return "\xEB\xBE\xBF";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x9B";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xB7";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x93";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xAF";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x8B";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xA7";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x83";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\x9F";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xBB";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x97";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xB3";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x8F";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xAB";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x87";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xA3";
+ break;
+ case 0xB0 :
+ return "\xEC\x85\xBF";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x9B";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xB7";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x93";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xAF";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x8B";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xA7";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x83";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\x9F";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xBB";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x97";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xB3";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x8F";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xAB";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x87";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xA3";
+ break;
+ case 0xB0 :
+ return "\xEC\x8C\xBF";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x9B";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xB7";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x93";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xAF";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x8B";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xA7";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x83";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\x9F";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xBB";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x97";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xB3";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x8F";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xAB";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x87";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xA3";
+ break;
+ case 0xB0 :
+ return "\xEC\x93\xBF";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x9B";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xB7";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x93";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xAF";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x8B";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xA7";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x83";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\x9F";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xBB";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x97";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xB3";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x8F";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xAB";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x87";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xA3";
+ break;
+ case 0xB0 :
+ return "\xEC\x9A\xBF";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x9B";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xB7";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x93";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xAF";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x8B";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xA7";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x83";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\x9F";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xBB";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x97";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xB3";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x8F";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xAB";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x87";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xA3";
+ break;
+ case 0xB0 :
+ return "\xEC\xA1\xBF";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x9B";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xB7";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x93";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xAF";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x8B";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xA7";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x83";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\x9F";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xBB";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x97";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xB3";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x8F";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xAB";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x87";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xA3";
+ break;
+ case 0xB0 :
+ return "\xEC\xA8\xBF";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x9B";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xB7";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x93";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xAF";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x8B";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xA7";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x83";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\x9F";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xBB";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x97";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xB3";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x8F";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xAB";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x87";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xA3";
+ break;
+ case 0xB0 :
+ return "\xEC\xAF\xBF";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x9B";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xB7";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x93";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xAF";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x8B";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xA7";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x83";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\x9F";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xBB";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x97";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xB3";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x8F";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xAB";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x87";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xA3";
+ break;
+ case 0xB0 :
+ return "\xEC\xB6\xBF";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x9B";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xB7";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x93";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xAF";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x8B";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xA7";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x83";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\x9F";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xBB";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x97";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xB3";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x8F";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xAB";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x87";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xA3";
+ break;
+ case 0xB0 :
+ return "\xEC\xBD\xBF";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x9B";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xB7";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x93";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xAF";
+ break;
+ case 0xBC :
+ return "\xED\x80\x8B";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xA7";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x83";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\x9F";
+ break;
+ case 0xAC :
+ return "\xED\x81\xBB";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x97";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xB3";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x8F";
+ break;
+ case 0x9C :
+ return "\xED\x83\xAB";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x87";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xA3";
+ break;
+ case 0xB0 :
+ return "\xED\x84\xBF";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x9B";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xB7";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x93";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xAF";
+ break;
+ case 0xBC :
+ return "\xED\x87\x8B";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xA7";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x83";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\x9F";
+ break;
+ case 0xAC :
+ return "\xED\x88\xBB";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x97";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xB3";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x8F";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xAB";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x87";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xA3";
+ break;
+ case 0xB0 :
+ return "\xED\x8B\xBF";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x9B";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xB7";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x93";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xAF";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x8B";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xA7";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x83";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\x9F";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xBB";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x97";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xB3";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x8F";
+ break;
+ case 0x9C :
+ return "\xED\x91\xAB";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x87";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xA3";
+ break;
+ case 0xB0 :
+ return "\xED\x92\xBF";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x9B";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xB7";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x93";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xAF";
+ break;
+ case 0xBC :
+ return "\xED\x95\x8B";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xA7";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x83";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\x9F";
+ break;
+ case 0xAC :
+ return "\xED\x96\xBB";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x97";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xB3";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x8F";
+ break;
+ case 0x9C :
+ return "\xED\x98\xAB";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x87";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xA3";
+ break;
+ case 0xB0 :
+ return "\xED\x99\xBF";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x9B";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xB7";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x93";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xAF";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x8B";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xA7";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x83";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\x9F";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xBB";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x97";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x90";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xAC";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x88";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xA4";
+ break;
+ case 0xB0 :
+ return "\xEA\xB2\x80";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x9C";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xB8";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x94";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xB0";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x8C";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xA8";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x84";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\xA0";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xBC";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x98";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xB4";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x90";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xAC";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x88";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xA4";
+ break;
+ case 0xB0 :
+ return "\xEA\xB9\x80";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x9C";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xB8";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x94";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xB0";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x8C";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xA8";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x84";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\xA0";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xBC";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x98";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xB4";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x90";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xAC";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x88";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xA4";
+ break;
+ case 0xB0 :
+ return "\xEB\x80\x80";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x9C";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xB8";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x94";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xB0";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x8C";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xA8";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x84";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\xA0";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xBC";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x98";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xB4";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x90";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xAC";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x88";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xA4";
+ break;
+ case 0xB0 :
+ return "\xEB\x87\x80";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x9C";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xB8";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x94";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xB0";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x8C";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xA8";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x84";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\xA0";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xBC";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x98";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xB4";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x90";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xAC";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x88";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xA4";
+ break;
+ case 0xB0 :
+ return "\xEB\x8E\x80";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x9C";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xB8";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x94";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xB0";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x8C";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xA8";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x84";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\xA0";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xBC";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x98";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xB4";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x90";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xAC";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x88";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xA4";
+ break;
+ case 0xB0 :
+ return "\xEB\x95\x80";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x9C";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xB8";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x94";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xB0";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x8C";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xA8";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x84";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\xA0";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xBC";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x98";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xB4";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x90";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xAC";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x88";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xA4";
+ break;
+ case 0xB0 :
+ return "\xEB\x9C\x80";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x9C";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xB8";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x94";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xB0";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x8C";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xA8";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x84";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\xA0";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xBC";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x98";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xB4";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x90";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xAC";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x88";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xA4";
+ break;
+ case 0xB0 :
+ return "\xEB\xA3\x80";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x9C";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xB8";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x94";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xB0";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x8C";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xA8";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x84";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\xA0";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xBC";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x98";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xB4";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x90";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xAC";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x88";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xA4";
+ break;
+ case 0xB0 :
+ return "\xEB\xAA\x80";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x9C";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xB8";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x94";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xB0";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x8C";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xA8";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x84";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\xA0";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xBC";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x98";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xB4";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x90";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xAC";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x88";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xA4";
+ break;
+ case 0xB0 :
+ return "\xEB\xB1\x80";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x9C";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xB8";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x94";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xB0";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x8C";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xA8";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x84";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\xA0";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xBC";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x98";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xB4";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x90";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xAC";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x88";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xA4";
+ break;
+ case 0xB0 :
+ return "\xEB\xB8\x80";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x9C";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xB8";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x94";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xB0";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x8C";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xA8";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x84";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\xA0";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xBC";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x98";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xB4";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x90";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xAC";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x88";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xA4";
+ break;
+ case 0xB0 :
+ return "\xEB\xBF\x80";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x9C";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xB8";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x94";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xB0";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x8C";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xA8";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x84";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\xA0";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xBC";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x98";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xB4";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x90";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xAC";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x88";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xA4";
+ break;
+ case 0xB0 :
+ return "\xEC\x86\x80";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x9C";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xB8";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x94";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xB0";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x8C";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xA8";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x84";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\xA0";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xBC";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x98";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xB4";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x90";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xAC";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x88";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xA4";
+ break;
+ case 0xB0 :
+ return "\xEC\x8D\x80";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x9C";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xB8";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x94";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xB0";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x8C";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xA8";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x84";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\xA0";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xBC";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x98";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xB4";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x90";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xAC";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x88";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xA4";
+ break;
+ case 0xB0 :
+ return "\xEC\x94\x80";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x9C";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xB8";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x94";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xB0";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x8C";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xA8";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x84";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\xA0";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xBC";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x98";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xB4";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x90";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xAC";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x88";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xA4";
+ break;
+ case 0xB0 :
+ return "\xEC\x9B\x80";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x9C";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xB8";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x94";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xB0";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x8C";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xA8";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x84";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\xA0";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xBC";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x98";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xB4";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x90";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xAC";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x88";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xA4";
+ break;
+ case 0xB0 :
+ return "\xEC\xA2\x80";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x9C";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xB8";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x94";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xB0";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x8C";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xA8";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x84";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\xA0";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xBC";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x98";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xB4";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x90";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xAC";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x88";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xA4";
+ break;
+ case 0xB0 :
+ return "\xEC\xA9\x80";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x9C";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xB8";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x94";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xB0";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x8C";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xA8";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x84";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\xA0";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xBC";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x98";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xB4";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x90";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xAC";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x88";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xA4";
+ break;
+ case 0xB0 :
+ return "\xEC\xB0\x80";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x9C";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xB8";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x94";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xB0";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x8C";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xA8";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x84";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\xA0";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xBC";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x98";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xB4";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x90";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xAC";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x88";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xA4";
+ break;
+ case 0xB0 :
+ return "\xEC\xB7\x80";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x9C";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xB8";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x94";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xB0";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x8C";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xA8";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x84";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\xA0";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xBC";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x98";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xB4";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x90";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xAC";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x88";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xA4";
+ break;
+ case 0xB0 :
+ return "\xEC\xBE\x80";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x9C";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xB8";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x94";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xB0";
+ break;
+ case 0xBC :
+ return "\xED\x80\x8C";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xA8";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x84";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\xA0";
+ break;
+ case 0xAC :
+ return "\xED\x81\xBC";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x98";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xB4";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x90";
+ break;
+ case 0x9C :
+ return "\xED\x83\xAC";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x88";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xA4";
+ break;
+ case 0xB0 :
+ return "\xED\x85\x80";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x9C";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xB8";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x94";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xB0";
+ break;
+ case 0xBC :
+ return "\xED\x87\x8C";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xA8";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x84";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\xA0";
+ break;
+ case 0xAC :
+ return "\xED\x88\xBC";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x98";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xB4";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x90";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xAC";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x88";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xA4";
+ break;
+ case 0xB0 :
+ return "\xED\x8C\x80";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x9C";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xB8";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x94";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xB0";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x8C";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xA8";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x84";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\xA0";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xBC";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x98";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xB4";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x90";
+ break;
+ case 0x9C :
+ return "\xED\x91\xAC";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x88";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xA4";
+ break;
+ case 0xB0 :
+ return "\xED\x93\x80";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x9C";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xB8";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x94";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xB0";
+ break;
+ case 0xBC :
+ return "\xED\x95\x8C";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xA8";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x84";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\xA0";
+ break;
+ case 0xAC :
+ return "\xED\x96\xBC";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x98";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xB4";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x90";
+ break;
+ case 0x9C :
+ return "\xED\x98\xAC";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x88";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xA4";
+ break;
+ case 0xB0 :
+ return "\xED\x9A\x80";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x9C";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xB8";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x94";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xB0";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x8C";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xA8";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x84";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\xA0";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xBC";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x98";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x91";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xAD";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x89";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xA5";
+ break;
+ case 0xB0 :
+ return "\xEA\xB2\x81";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x9D";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xB9";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x95";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xB1";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x8D";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xA9";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x85";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\xA1";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xBD";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x99";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xB5";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x91";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xAD";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x89";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xA5";
+ break;
+ case 0xB0 :
+ return "\xEA\xB9\x81";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x9D";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xB9";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x95";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xB1";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x8D";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xA9";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x85";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\xA1";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xBD";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x99";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xB5";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x91";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xAD";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x89";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xA5";
+ break;
+ case 0xB0 :
+ return "\xEB\x80\x81";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x9D";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xB9";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x95";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xB1";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x8D";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xA9";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x85";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\xA1";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xBD";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x99";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xB5";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x91";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xAD";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x89";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xA5";
+ break;
+ case 0xB0 :
+ return "\xEB\x87\x81";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x9D";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xB9";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x95";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xB1";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x8D";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xA9";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x85";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\xA1";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xBD";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x99";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xB5";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x91";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xAD";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x89";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xA5";
+ break;
+ case 0xB0 :
+ return "\xEB\x8E\x81";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x9D";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xB9";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x95";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xB1";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x8D";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xA9";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x85";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\xA1";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xBD";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x99";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xB5";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x91";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xAD";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x89";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xA5";
+ break;
+ case 0xB0 :
+ return "\xEB\x95\x81";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x9D";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xB9";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x95";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xB1";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x8D";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xA9";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x85";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\xA1";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xBD";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x99";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xB5";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x91";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xAD";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x89";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xA5";
+ break;
+ case 0xB0 :
+ return "\xEB\x9C\x81";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x9D";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xB9";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x95";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xB1";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x8D";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xA9";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x85";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\xA1";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xBD";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x99";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xB5";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x91";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xAD";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x89";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xA5";
+ break;
+ case 0xB0 :
+ return "\xEB\xA3\x81";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x9D";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xB9";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x95";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xB1";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x8D";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xA9";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x85";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\xA1";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xBD";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x99";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xB5";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x91";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xAD";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x89";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xA5";
+ break;
+ case 0xB0 :
+ return "\xEB\xAA\x81";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x9D";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xB9";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x95";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xB1";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x8D";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xA9";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x85";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\xA1";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xBD";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x99";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xB5";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x91";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xAD";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x89";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xA5";
+ break;
+ case 0xB0 :
+ return "\xEB\xB1\x81";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x9D";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xB9";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x95";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xB1";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x8D";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xA9";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x85";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\xA1";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xBD";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x99";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xB5";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x91";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xAD";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x89";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xA5";
+ break;
+ case 0xB0 :
+ return "\xEB\xB8\x81";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x9D";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xB9";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x95";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xB1";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x8D";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xA9";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x85";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\xA1";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xBD";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x99";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xB5";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x91";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xAD";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x89";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xA5";
+ break;
+ case 0xB0 :
+ return "\xEB\xBF\x81";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x9D";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xB9";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x95";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xB1";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x8D";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xA9";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x85";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\xA1";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xBD";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x99";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xB5";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x91";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xAD";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x89";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xA5";
+ break;
+ case 0xB0 :
+ return "\xEC\x86\x81";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x9D";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xB9";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x95";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xB1";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x8D";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xA9";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x85";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\xA1";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xBD";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x99";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xB5";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x91";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xAD";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x89";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xA5";
+ break;
+ case 0xB0 :
+ return "\xEC\x8D\x81";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x9D";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xB9";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x95";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xB1";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x8D";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xA9";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x85";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\xA1";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xBD";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x99";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xB5";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x91";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xAD";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x89";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xA5";
+ break;
+ case 0xB0 :
+ return "\xEC\x94\x81";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x9D";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xB9";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x95";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xB1";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x8D";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xA9";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x85";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\xA1";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xBD";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x99";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xB5";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x91";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xAD";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x89";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xA5";
+ break;
+ case 0xB0 :
+ return "\xEC\x9B\x81";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x9D";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xB9";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x95";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xB1";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x8D";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xA9";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x85";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\xA1";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xBD";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x99";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xB5";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x91";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xAD";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x89";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xA5";
+ break;
+ case 0xB0 :
+ return "\xEC\xA2\x81";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x9D";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xB9";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x95";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xB1";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x8D";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xA9";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x85";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\xA1";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xBD";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x99";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xB5";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x91";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xAD";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x89";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xA5";
+ break;
+ case 0xB0 :
+ return "\xEC\xA9\x81";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x9D";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xB9";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x95";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xB1";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x8D";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xA9";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x85";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\xA1";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xBD";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x99";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xB5";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x91";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xAD";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x89";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xA5";
+ break;
+ case 0xB0 :
+ return "\xEC\xB0\x81";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x9D";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xB9";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x95";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xB1";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x8D";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xA9";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x85";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\xA1";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xBD";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x99";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xB5";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x91";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xAD";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x89";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xA5";
+ break;
+ case 0xB0 :
+ return "\xEC\xB7\x81";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x9D";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xB9";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x95";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xB1";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x8D";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xA9";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x85";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\xA1";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xBD";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x99";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xB5";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x91";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xAD";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x89";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xA5";
+ break;
+ case 0xB0 :
+ return "\xEC\xBE\x81";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x9D";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xB9";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x95";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xB1";
+ break;
+ case 0xBC :
+ return "\xED\x80\x8D";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xA9";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x85";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\xA1";
+ break;
+ case 0xAC :
+ return "\xED\x81\xBD";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x99";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xB5";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x91";
+ break;
+ case 0x9C :
+ return "\xED\x83\xAD";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x89";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xA5";
+ break;
+ case 0xB0 :
+ return "\xED\x85\x81";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x9D";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xB9";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x95";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xB1";
+ break;
+ case 0xBC :
+ return "\xED\x87\x8D";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xA9";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x85";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\xA1";
+ break;
+ case 0xAC :
+ return "\xED\x88\xBD";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x99";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xB5";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x91";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xAD";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x89";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xA5";
+ break;
+ case 0xB0 :
+ return "\xED\x8C\x81";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x9D";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xB9";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x95";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xB1";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x8D";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xA9";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x85";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\xA1";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xBD";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x99";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xB5";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x91";
+ break;
+ case 0x9C :
+ return "\xED\x91\xAD";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x89";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xA5";
+ break;
+ case 0xB0 :
+ return "\xED\x93\x81";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x9D";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xB9";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x95";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xB1";
+ break;
+ case 0xBC :
+ return "\xED\x95\x8D";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xA9";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x85";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\xA1";
+ break;
+ case 0xAC :
+ return "\xED\x96\xBD";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x99";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xB5";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x91";
+ break;
+ case 0x9C :
+ return "\xED\x98\xAD";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x89";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xA5";
+ break;
+ case 0xB0 :
+ return "\xED\x9A\x81";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x9D";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xB9";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x95";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xB1";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x8D";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xA9";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x85";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\xA1";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xBD";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x99";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x92";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xAE";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x8A";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xA6";
+ break;
+ case 0xB0 :
+ return "\xEA\xB2\x82";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x9E";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xBA";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x96";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xB2";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x8E";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xAA";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x86";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\xA2";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xBE";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x9A";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xB6";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x92";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xAE";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x8A";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xA6";
+ break;
+ case 0xB0 :
+ return "\xEA\xB9\x82";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x9E";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xBA";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x96";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xB2";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x8E";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xAA";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x86";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\xA2";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xBE";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x9A";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xB6";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x92";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xAE";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x8A";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xA6";
+ break;
+ case 0xB0 :
+ return "\xEB\x80\x82";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x9E";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xBA";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x96";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xB2";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x8E";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xAA";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x86";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\xA2";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xBE";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x9A";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xB6";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x92";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xAE";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x8A";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xA6";
+ break;
+ case 0xB0 :
+ return "\xEB\x87\x82";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x9E";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xBA";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x96";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xB2";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x8E";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xAA";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x86";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\xA2";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xBE";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x9A";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xB6";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x92";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xAE";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x8A";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xA6";
+ break;
+ case 0xB0 :
+ return "\xEB\x8E\x82";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x9E";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xBA";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x96";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xB2";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x8E";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xAA";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x86";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\xA2";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xBE";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x9A";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xB6";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x92";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xAE";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x8A";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xA6";
+ break;
+ case 0xB0 :
+ return "\xEB\x95\x82";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x9E";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xBA";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x96";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xB2";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x8E";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xAA";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x86";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\xA2";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xBE";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x9A";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xB6";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x92";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xAE";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x8A";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xA6";
+ break;
+ case 0xB0 :
+ return "\xEB\x9C\x82";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x9E";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xBA";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x96";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xB2";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x8E";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xAA";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x86";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\xA2";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xBE";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x9A";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xB6";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x92";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xAE";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x8A";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xA6";
+ break;
+ case 0xB0 :
+ return "\xEB\xA3\x82";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x9E";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xBA";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x96";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xB2";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x8E";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xAA";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x86";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\xA2";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xBE";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x9A";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xB6";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x92";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xAE";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x8A";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xA6";
+ break;
+ case 0xB0 :
+ return "\xEB\xAA\x82";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x9E";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xBA";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x96";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xB2";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x8E";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xAA";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x86";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\xA2";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xBE";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x9A";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xB6";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x92";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xAE";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x8A";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xA6";
+ break;
+ case 0xB0 :
+ return "\xEB\xB1\x82";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x9E";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xBA";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x96";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xB2";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x8E";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xAA";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x86";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\xA2";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xBE";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x9A";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xB6";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x92";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xAE";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x8A";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xA6";
+ break;
+ case 0xB0 :
+ return "\xEB\xB8\x82";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x9E";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xBA";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x96";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xB2";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x8E";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xAA";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x86";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\xA2";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xBE";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x9A";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xB6";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x92";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xAE";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x8A";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xA6";
+ break;
+ case 0xB0 :
+ return "\xEB\xBF\x82";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x9E";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xBA";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x96";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xB2";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x8E";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xAA";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x86";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\xA2";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xBE";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x9A";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xB6";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x92";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xAE";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x8A";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xA6";
+ break;
+ case 0xB0 :
+ return "\xEC\x86\x82";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x9E";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xBA";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x96";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xB2";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x8E";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xAA";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x86";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\xA2";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xBE";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x9A";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xB6";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x92";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xAE";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x8A";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xA6";
+ break;
+ case 0xB0 :
+ return "\xEC\x8D\x82";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x9E";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xBA";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x96";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xB2";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x8E";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xAA";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x86";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\xA2";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xBE";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x9A";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xB6";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x92";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xAE";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x8A";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xA6";
+ break;
+ case 0xB0 :
+ return "\xEC\x94\x82";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x9E";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xBA";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x96";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xB2";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x8E";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xAA";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x86";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\xA2";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xBE";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x9A";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xB6";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x92";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xAE";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x8A";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xA6";
+ break;
+ case 0xB0 :
+ return "\xEC\x9B\x82";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x9E";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xBA";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x96";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xB2";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x8E";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xAA";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x86";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\xA2";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xBE";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x9A";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xB6";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x92";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xAE";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x8A";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xA6";
+ break;
+ case 0xB0 :
+ return "\xEC\xA2\x82";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x9E";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xBA";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x96";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xB2";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x8E";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xAA";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x86";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\xA2";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xBE";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x9A";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xB6";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x92";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xAE";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x8A";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xA6";
+ break;
+ case 0xB0 :
+ return "\xEC\xA9\x82";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x9E";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xBA";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x96";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xB2";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x8E";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xAA";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x86";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\xA2";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xBE";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x9A";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xB6";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x92";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xAE";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x8A";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xA6";
+ break;
+ case 0xB0 :
+ return "\xEC\xB0\x82";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x9E";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xBA";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x96";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xB2";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x8E";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xAA";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x86";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\xA2";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xBE";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x9A";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xB6";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x92";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xAE";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x8A";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xA6";
+ break;
+ case 0xB0 :
+ return "\xEC\xB7\x82";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x9E";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xBA";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x96";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xB2";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x8E";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xAA";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x86";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\xA2";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xBE";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x9A";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xB6";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x92";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xAE";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x8A";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xA6";
+ break;
+ case 0xB0 :
+ return "\xEC\xBE\x82";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x9E";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xBA";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x96";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xB2";
+ break;
+ case 0xBC :
+ return "\xED\x80\x8E";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xAA";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x86";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\xA2";
+ break;
+ case 0xAC :
+ return "\xED\x81\xBE";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x9A";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xB6";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x92";
+ break;
+ case 0x9C :
+ return "\xED\x83\xAE";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x8A";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xA6";
+ break;
+ case 0xB0 :
+ return "\xED\x85\x82";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x9E";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xBA";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x96";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xB2";
+ break;
+ case 0xBC :
+ return "\xED\x87\x8E";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xAA";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x86";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\xA2";
+ break;
+ case 0xAC :
+ return "\xED\x88\xBE";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x9A";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xB6";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x92";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xAE";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x8A";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xA6";
+ break;
+ case 0xB0 :
+ return "\xED\x8C\x82";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x9E";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xBA";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x96";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xB2";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x8E";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xAA";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x86";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\xA2";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xBE";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x9A";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xB6";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x92";
+ break;
+ case 0x9C :
+ return "\xED\x91\xAE";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x8A";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xA6";
+ break;
+ case 0xB0 :
+ return "\xED\x93\x82";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x9E";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xBA";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x96";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xB2";
+ break;
+ case 0xBC :
+ return "\xED\x95\x8E";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xAA";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x86";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\xA2";
+ break;
+ case 0xAC :
+ return "\xED\x96\xBE";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x9A";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xB6";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x92";
+ break;
+ case 0x9C :
+ return "\xED\x98\xAE";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x8A";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xA6";
+ break;
+ case 0xB0 :
+ return "\xED\x9A\x82";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x9E";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xBA";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x96";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xB2";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x8E";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xAA";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x86";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\xA2";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xBE";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x9A";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x93";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xAF";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x8B";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xA7";
+ break;
+ case 0xB0 :
+ return "\xEA\xB2\x83";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\x9F";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xBB";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x97";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xB3";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x8F";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xAB";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x87";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\xA3";
+ break;
+ case 0xAC :
+ return "\xEA\xB5\xBF";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x9B";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xB7";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x93";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xAF";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x8B";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xA7";
+ break;
+ case 0xB0 :
+ return "\xEA\xB9\x83";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\x9F";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xBB";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x97";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xB3";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x8F";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xAB";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x87";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\xA3";
+ break;
+ case 0xAC :
+ return "\xEA\xBC\xBF";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x9B";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xB7";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x93";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xAF";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x8B";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xA7";
+ break;
+ case 0xB0 :
+ return "\xEB\x80\x83";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\x9F";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xBB";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x97";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xB3";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x8F";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xAB";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x87";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\xA3";
+ break;
+ case 0xAC :
+ return "\xEB\x83\xBF";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x9B";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xB7";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x93";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xAF";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x8B";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xA7";
+ break;
+ case 0xB0 :
+ return "\xEB\x87\x83";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\x9F";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xBB";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x97";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xB3";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x8F";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xAB";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x87";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\xA3";
+ break;
+ case 0xAC :
+ return "\xEB\x8A\xBF";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x9B";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xB7";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x93";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xAF";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x8B";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xA7";
+ break;
+ case 0xB0 :
+ return "\xEB\x8E\x83";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\x9F";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xBB";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x97";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xB3";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x8F";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xAB";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x87";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\xA3";
+ break;
+ case 0xAC :
+ return "\xEB\x91\xBF";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x9B";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xB7";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x93";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xAF";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x8B";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xA7";
+ break;
+ case 0xB0 :
+ return "\xEB\x95\x83";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\x9F";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xBB";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x97";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xB3";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x8F";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xAB";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x87";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\xA3";
+ break;
+ case 0xAC :
+ return "\xEB\x98\xBF";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x9B";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xB7";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x93";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xAF";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x8B";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xA7";
+ break;
+ case 0xB0 :
+ return "\xEB\x9C\x83";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\x9F";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xBB";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x97";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xB3";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x8F";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xAB";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x87";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\xA3";
+ break;
+ case 0xAC :
+ return "\xEB\x9F\xBF";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x9B";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xB7";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x93";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xAF";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x8B";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xA7";
+ break;
+ case 0xB0 :
+ return "\xEB\xA3\x83";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\x9F";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xBB";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x97";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xB3";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x8F";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xAB";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x87";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\xA3";
+ break;
+ case 0xAC :
+ return "\xEB\xA6\xBF";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x9B";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xB7";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x93";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xAF";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x8B";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xA7";
+ break;
+ case 0xB0 :
+ return "\xEB\xAA\x83";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\x9F";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xBB";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x97";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xB3";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x8F";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xAB";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x87";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\xA3";
+ break;
+ case 0xAC :
+ return "\xEB\xAD\xBF";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x9B";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xB7";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x93";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xAF";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x8B";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xA7";
+ break;
+ case 0xB0 :
+ return "\xEB\xB1\x83";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\x9F";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xBB";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x97";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xB3";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x8F";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xAB";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x87";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\xA3";
+ break;
+ case 0xAC :
+ return "\xEB\xB4\xBF";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x9B";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xB7";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x93";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xAF";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x8B";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xA7";
+ break;
+ case 0xB0 :
+ return "\xEB\xB8\x83";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\x9F";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xBB";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x97";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xB3";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x8F";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xAB";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x87";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\xA3";
+ break;
+ case 0xAC :
+ return "\xEB\xBB\xBF";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x9B";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xB7";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x93";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xAF";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x8B";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xA7";
+ break;
+ case 0xB0 :
+ return "\xEB\xBF\x83";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\x9F";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xBB";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x97";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xB3";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x8F";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xAB";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x87";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\xA3";
+ break;
+ case 0xAC :
+ return "\xEC\x82\xBF";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x9B";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xB7";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x93";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xAF";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x8B";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xA7";
+ break;
+ case 0xB0 :
+ return "\xEC\x86\x83";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\x9F";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xBB";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x97";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xB3";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x8F";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xAB";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x87";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\xA3";
+ break;
+ case 0xAC :
+ return "\xEC\x89\xBF";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x9B";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xB7";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x93";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xAF";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x8B";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xA7";
+ break;
+ case 0xB0 :
+ return "\xEC\x8D\x83";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\x9F";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xBB";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x97";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xB3";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x8F";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xAB";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x87";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\xA3";
+ break;
+ case 0xAC :
+ return "\xEC\x90\xBF";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x9B";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xB7";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x93";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xAF";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x8B";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xA7";
+ break;
+ case 0xB0 :
+ return "\xEC\x94\x83";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\x9F";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xBB";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x97";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xB3";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x8F";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xAB";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x87";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\xA3";
+ break;
+ case 0xAC :
+ return "\xEC\x97\xBF";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x9B";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xB7";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x93";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xAF";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x8B";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xA7";
+ break;
+ case 0xB0 :
+ return "\xEC\x9B\x83";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\x9F";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xBB";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x97";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xB3";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x8F";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xAB";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x87";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\xA3";
+ break;
+ case 0xAC :
+ return "\xEC\x9E\xBF";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x9B";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xB7";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x93";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xAF";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x8B";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xA7";
+ break;
+ case 0xB0 :
+ return "\xEC\xA2\x83";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\x9F";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xBB";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x97";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xB3";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x8F";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xAB";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x87";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\xA3";
+ break;
+ case 0xAC :
+ return "\xEC\xA5\xBF";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x9B";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xB7";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x93";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xAF";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x8B";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xA7";
+ break;
+ case 0xB0 :
+ return "\xEC\xA9\x83";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\x9F";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xBB";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x97";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xB3";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x8F";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xAB";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x87";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\xA3";
+ break;
+ case 0xAC :
+ return "\xEC\xAC\xBF";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x9B";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xB7";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x93";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xAF";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x8B";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xA7";
+ break;
+ case 0xB0 :
+ return "\xEC\xB0\x83";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\x9F";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xBB";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x97";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xB3";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x8F";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xAB";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x87";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\xA3";
+ break;
+ case 0xAC :
+ return "\xEC\xB3\xBF";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x9B";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xB7";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x93";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xAF";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x8B";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xA7";
+ break;
+ case 0xB0 :
+ return "\xEC\xB7\x83";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\x9F";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xBB";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x97";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xB3";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x8F";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xAB";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x87";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\xA3";
+ break;
+ case 0xAC :
+ return "\xEC\xBA\xBF";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x9B";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xB7";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x93";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xAF";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x8B";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xA7";
+ break;
+ case 0xB0 :
+ return "\xEC\xBE\x83";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\x9F";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xBB";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x97";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xB3";
+ break;
+ case 0xBC :
+ return "\xED\x80\x8F";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xAB";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x87";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\xA3";
+ break;
+ case 0xAC :
+ return "\xED\x81\xBF";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x9B";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xB7";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x93";
+ break;
+ case 0x9C :
+ return "\xED\x83\xAF";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x8B";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xA7";
+ break;
+ case 0xB0 :
+ return "\xED\x85\x83";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\x9F";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xBB";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x97";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xB3";
+ break;
+ case 0xBC :
+ return "\xED\x87\x8F";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xAB";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x87";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\xA3";
+ break;
+ case 0xAC :
+ return "\xED\x88\xBF";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x9B";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xB7";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x93";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xAF";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x8B";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xA7";
+ break;
+ case 0xB0 :
+ return "\xED\x8C\x83";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\x9F";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xBB";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x97";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xB3";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x8F";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xAB";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x87";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\xA3";
+ break;
+ case 0xAC :
+ return "\xED\x8F\xBF";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x9B";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xB7";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x93";
+ break;
+ case 0x9C :
+ return "\xED\x91\xAF";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x8B";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xA7";
+ break;
+ case 0xB0 :
+ return "\xED\x93\x83";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\x9F";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xBB";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x97";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xB3";
+ break;
+ case 0xBC :
+ return "\xED\x95\x8F";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xAB";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x87";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\xA3";
+ break;
+ case 0xAC :
+ return "\xED\x96\xBF";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x9B";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xB7";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x93";
+ break;
+ case 0x9C :
+ return "\xED\x98\xAF";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x8B";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xA7";
+ break;
+ case 0xB0 :
+ return "\xED\x9A\x83";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\x9F";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xBB";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x97";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xB3";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x8F";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xAB";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x87";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\xA3";
+ break;
+ case 0xAC :
+ return "\xED\x9D\xBF";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x9B";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x94";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xB0";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x8C";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xA8";
+ break;
+ case 0xB0 :
+ return "\xEA\xB2\x84";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\xA0";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xBC";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x98";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xB4";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x90";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xAC";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x88";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\xA4";
+ break;
+ case 0xAC :
+ return "\xEA\xB6\x80";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x9C";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xB8";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x94";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xB0";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x8C";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xA8";
+ break;
+ case 0xB0 :
+ return "\xEA\xB9\x84";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\xA0";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xBC";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x98";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xB4";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x90";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xAC";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x88";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\xA4";
+ break;
+ case 0xAC :
+ return "\xEA\xBD\x80";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x9C";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xB8";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x94";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xB0";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x8C";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xA8";
+ break;
+ case 0xB0 :
+ return "\xEB\x80\x84";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\xA0";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xBC";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x98";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xB4";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x90";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xAC";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x88";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\xA4";
+ break;
+ case 0xAC :
+ return "\xEB\x84\x80";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x9C";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xB8";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x94";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xB0";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x8C";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xA8";
+ break;
+ case 0xB0 :
+ return "\xEB\x87\x84";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\xA0";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xBC";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x98";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xB4";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x90";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xAC";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x88";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\xA4";
+ break;
+ case 0xAC :
+ return "\xEB\x8B\x80";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x9C";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xB8";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x94";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xB0";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x8C";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xA8";
+ break;
+ case 0xB0 :
+ return "\xEB\x8E\x84";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\xA0";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xBC";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x98";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xB4";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x90";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xAC";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x88";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\xA4";
+ break;
+ case 0xAC :
+ return "\xEB\x92\x80";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x9C";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xB8";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x94";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xB0";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x8C";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xA8";
+ break;
+ case 0xB0 :
+ return "\xEB\x95\x84";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\xA0";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xBC";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x98";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xB4";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x90";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xAC";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x88";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\xA4";
+ break;
+ case 0xAC :
+ return "\xEB\x99\x80";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x9C";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xB8";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x94";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xB0";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x8C";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xA8";
+ break;
+ case 0xB0 :
+ return "\xEB\x9C\x84";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\xA0";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xBC";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x98";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xB4";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x90";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xAC";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x88";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\xA4";
+ break;
+ case 0xAC :
+ return "\xEB\xA0\x80";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x9C";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xB8";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x94";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xB0";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x8C";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xA8";
+ break;
+ case 0xB0 :
+ return "\xEB\xA3\x84";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\xA0";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xBC";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x98";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xB4";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x90";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xAC";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x88";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\xA4";
+ break;
+ case 0xAC :
+ return "\xEB\xA7\x80";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x9C";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xB8";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x94";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xB0";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x8C";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xA8";
+ break;
+ case 0xB0 :
+ return "\xEB\xAA\x84";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\xA0";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xBC";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x98";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xB4";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x90";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xAC";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x88";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\xA4";
+ break;
+ case 0xAC :
+ return "\xEB\xAE\x80";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x9C";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xB8";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x94";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xB0";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x8C";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xA8";
+ break;
+ case 0xB0 :
+ return "\xEB\xB1\x84";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\xA0";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xBC";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x98";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xB4";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x90";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xAC";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x88";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\xA4";
+ break;
+ case 0xAC :
+ return "\xEB\xB5\x80";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x9C";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xB8";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x94";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xB0";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x8C";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xA8";
+ break;
+ case 0xB0 :
+ return "\xEB\xB8\x84";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\xA0";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xBC";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x98";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xB4";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x90";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xAC";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x88";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\xA4";
+ break;
+ case 0xAC :
+ return "\xEB\xBC\x80";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x9C";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xB8";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x94";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xB0";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x8C";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xA8";
+ break;
+ case 0xB0 :
+ return "\xEB\xBF\x84";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\xA0";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xBC";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x98";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xB4";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x90";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xAC";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x88";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\xA4";
+ break;
+ case 0xAC :
+ return "\xEC\x83\x80";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x9C";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xB8";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x94";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xB0";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x8C";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xA8";
+ break;
+ case 0xB0 :
+ return "\xEC\x86\x84";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\xA0";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xBC";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x98";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xB4";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x90";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xAC";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x88";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\xA4";
+ break;
+ case 0xAC :
+ return "\xEC\x8A\x80";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x9C";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xB8";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x94";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xB0";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x8C";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xA8";
+ break;
+ case 0xB0 :
+ return "\xEC\x8D\x84";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\xA0";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xBC";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x98";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xB4";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x90";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xAC";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x88";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\xA4";
+ break;
+ case 0xAC :
+ return "\xEC\x91\x80";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x9C";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xB8";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x94";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xB0";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x8C";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xA8";
+ break;
+ case 0xB0 :
+ return "\xEC\x94\x84";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\xA0";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xBC";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x98";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xB4";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x90";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xAC";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x88";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\xA4";
+ break;
+ case 0xAC :
+ return "\xEC\x98\x80";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x9C";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xB8";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x94";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xB0";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x8C";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xA8";
+ break;
+ case 0xB0 :
+ return "\xEC\x9B\x84";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\xA0";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xBC";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x98";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xB4";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x90";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xAC";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x88";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\xA4";
+ break;
+ case 0xAC :
+ return "\xEC\x9F\x80";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x9C";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xB8";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x94";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xB0";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x8C";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xA8";
+ break;
+ case 0xB0 :
+ return "\xEC\xA2\x84";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\xA0";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xBC";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x98";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xB4";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x90";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xAC";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x88";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\xA4";
+ break;
+ case 0xAC :
+ return "\xEC\xA6\x80";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x9C";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xB8";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x94";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xB0";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x8C";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xA8";
+ break;
+ case 0xB0 :
+ return "\xEC\xA9\x84";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\xA0";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xBC";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x98";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xB4";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x90";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xAC";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x88";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\xA4";
+ break;
+ case 0xAC :
+ return "\xEC\xAD\x80";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x9C";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xB8";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x94";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xB0";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x8C";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xA8";
+ break;
+ case 0xB0 :
+ return "\xEC\xB0\x84";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\xA0";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xBC";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x98";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xB4";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x90";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xAC";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x88";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\xA4";
+ break;
+ case 0xAC :
+ return "\xEC\xB4\x80";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x9C";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xB8";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x94";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xB0";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x8C";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xA8";
+ break;
+ case 0xB0 :
+ return "\xEC\xB7\x84";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\xA0";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xBC";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x98";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xB4";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x90";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xAC";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x88";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\xA4";
+ break;
+ case 0xAC :
+ return "\xEC\xBB\x80";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x9C";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xB8";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x94";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xB0";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x8C";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xA8";
+ break;
+ case 0xB0 :
+ return "\xEC\xBE\x84";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\xA0";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xBC";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x98";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xB4";
+ break;
+ case 0xBC :
+ return "\xED\x80\x90";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xAC";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x88";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\xA4";
+ break;
+ case 0xAC :
+ return "\xED\x82\x80";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x9C";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xB8";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x94";
+ break;
+ case 0x9C :
+ return "\xED\x83\xB0";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x8C";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xA8";
+ break;
+ case 0xB0 :
+ return "\xED\x85\x84";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\xA0";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xBC";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x98";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xB4";
+ break;
+ case 0xBC :
+ return "\xED\x87\x90";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xAC";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x88";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\xA4";
+ break;
+ case 0xAC :
+ return "\xED\x89\x80";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x9C";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xB8";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x94";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xB0";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x8C";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xA8";
+ break;
+ case 0xB0 :
+ return "\xED\x8C\x84";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\xA0";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xBC";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x98";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xB4";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x90";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xAC";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x88";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\xA4";
+ break;
+ case 0xAC :
+ return "\xED\x90\x80";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x9C";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xB8";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x94";
+ break;
+ case 0x9C :
+ return "\xED\x91\xB0";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x8C";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xA8";
+ break;
+ case 0xB0 :
+ return "\xED\x93\x84";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\xA0";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xBC";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x98";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xB4";
+ break;
+ case 0xBC :
+ return "\xED\x95\x90";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xAC";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x88";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\xA4";
+ break;
+ case 0xAC :
+ return "\xED\x97\x80";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x9C";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xB8";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x94";
+ break;
+ case 0x9C :
+ return "\xED\x98\xB0";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x8C";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xA8";
+ break;
+ case 0xB0 :
+ return "\xED\x9A\x84";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\xA0";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xBC";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x98";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xB4";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x90";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xAC";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x88";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\xA4";
+ break;
+ case 0xAC :
+ return "\xED\x9E\x80";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x9C";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x95";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xB1";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x8D";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xA9";
+ break;
+ case 0xB0 :
+ return "\xEA\xB2\x85";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\xA1";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xBD";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x99";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xB5";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x91";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xAD";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x89";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\xA5";
+ break;
+ case 0xAC :
+ return "\xEA\xB6\x81";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x9D";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xB9";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x95";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xB1";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x8D";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xA9";
+ break;
+ case 0xB0 :
+ return "\xEA\xB9\x85";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\xA1";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xBD";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x99";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xB5";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x91";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xAD";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x89";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\xA5";
+ break;
+ case 0xAC :
+ return "\xEA\xBD\x81";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x9D";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xB9";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x95";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xB1";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x8D";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xA9";
+ break;
+ case 0xB0 :
+ return "\xEB\x80\x85";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\xA1";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xBD";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x99";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xB5";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x91";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xAD";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x89";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\xA5";
+ break;
+ case 0xAC :
+ return "\xEB\x84\x81";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x9D";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xB9";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x95";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xB1";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x8D";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xA9";
+ break;
+ case 0xB0 :
+ return "\xEB\x87\x85";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\xA1";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xBD";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x99";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xB5";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x91";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xAD";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x89";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\xA5";
+ break;
+ case 0xAC :
+ return "\xEB\x8B\x81";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x9D";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xB9";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x95";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xB1";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x8D";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xA9";
+ break;
+ case 0xB0 :
+ return "\xEB\x8E\x85";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\xA1";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xBD";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x99";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xB5";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x91";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xAD";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x89";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\xA5";
+ break;
+ case 0xAC :
+ return "\xEB\x92\x81";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x9D";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xB9";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x95";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xB1";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x8D";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xA9";
+ break;
+ case 0xB0 :
+ return "\xEB\x95\x85";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\xA1";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xBD";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x99";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xB5";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x91";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xAD";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x89";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\xA5";
+ break;
+ case 0xAC :
+ return "\xEB\x99\x81";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x9D";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xB9";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x95";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xB1";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x8D";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xA9";
+ break;
+ case 0xB0 :
+ return "\xEB\x9C\x85";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\xA1";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xBD";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x99";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xB5";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x91";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xAD";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x89";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\xA5";
+ break;
+ case 0xAC :
+ return "\xEB\xA0\x81";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x9D";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xB9";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x95";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xB1";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x8D";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xA9";
+ break;
+ case 0xB0 :
+ return "\xEB\xA3\x85";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\xA1";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xBD";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x99";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xB5";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x91";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xAD";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x89";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\xA5";
+ break;
+ case 0xAC :
+ return "\xEB\xA7\x81";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x9D";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xB9";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x95";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xB1";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x8D";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xA9";
+ break;
+ case 0xB0 :
+ return "\xEB\xAA\x85";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\xA1";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xBD";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x99";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xB5";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x91";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xAD";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x89";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\xA5";
+ break;
+ case 0xAC :
+ return "\xEB\xAE\x81";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x9D";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xB9";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x95";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xB1";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x8D";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xA9";
+ break;
+ case 0xB0 :
+ return "\xEB\xB1\x85";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\xA1";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xBD";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x99";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xB5";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x91";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xAD";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x89";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\xA5";
+ break;
+ case 0xAC :
+ return "\xEB\xB5\x81";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x9D";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xB9";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x95";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xB1";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x8D";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xA9";
+ break;
+ case 0xB0 :
+ return "\xEB\xB8\x85";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\xA1";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xBD";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x99";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xB5";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x91";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xAD";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x89";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\xA5";
+ break;
+ case 0xAC :
+ return "\xEB\xBC\x81";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x9D";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xB9";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x95";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xB1";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x8D";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xA9";
+ break;
+ case 0xB0 :
+ return "\xEB\xBF\x85";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\xA1";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xBD";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x99";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xB5";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x91";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xAD";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x89";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\xA5";
+ break;
+ case 0xAC :
+ return "\xEC\x83\x81";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x9D";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xB9";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x95";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xB1";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x8D";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xA9";
+ break;
+ case 0xB0 :
+ return "\xEC\x86\x85";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\xA1";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xBD";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x99";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xB5";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x91";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xAD";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x89";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\xA5";
+ break;
+ case 0xAC :
+ return "\xEC\x8A\x81";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x9D";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xB9";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x95";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xB1";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x8D";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xA9";
+ break;
+ case 0xB0 :
+ return "\xEC\x8D\x85";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\xA1";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xBD";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x99";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xB5";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x91";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xAD";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x89";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\xA5";
+ break;
+ case 0xAC :
+ return "\xEC\x91\x81";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x9D";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xB9";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x95";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xB1";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x8D";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xA9";
+ break;
+ case 0xB0 :
+ return "\xEC\x94\x85";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\xA1";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xBD";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x99";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xB5";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x91";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xAD";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x89";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\xA5";
+ break;
+ case 0xAC :
+ return "\xEC\x98\x81";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x9D";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xB9";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x95";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xB1";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x8D";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xA9";
+ break;
+ case 0xB0 :
+ return "\xEC\x9B\x85";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\xA1";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xBD";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x99";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xB5";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x91";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xAD";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x89";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\xA5";
+ break;
+ case 0xAC :
+ return "\xEC\x9F\x81";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x9D";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xB9";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x95";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xB1";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x8D";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xA9";
+ break;
+ case 0xB0 :
+ return "\xEC\xA2\x85";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\xA1";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xBD";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x99";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xB5";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x91";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xAD";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x89";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\xA5";
+ break;
+ case 0xAC :
+ return "\xEC\xA6\x81";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x9D";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xB9";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x95";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xB1";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x8D";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xA9";
+ break;
+ case 0xB0 :
+ return "\xEC\xA9\x85";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\xA1";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xBD";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x99";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xB5";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x91";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xAD";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x89";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\xA5";
+ break;
+ case 0xAC :
+ return "\xEC\xAD\x81";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x9D";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xB9";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x95";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xB1";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x8D";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xA9";
+ break;
+ case 0xB0 :
+ return "\xEC\xB0\x85";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\xA1";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xBD";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x99";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xB5";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x91";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xAD";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x89";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\xA5";
+ break;
+ case 0xAC :
+ return "\xEC\xB4\x81";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x9D";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xB9";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x95";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xB1";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x8D";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xA9";
+ break;
+ case 0xB0 :
+ return "\xEC\xB7\x85";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\xA1";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xBD";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x99";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xB5";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x91";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xAD";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x89";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\xA5";
+ break;
+ case 0xAC :
+ return "\xEC\xBB\x81";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x9D";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xB9";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x95";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xB1";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x8D";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xA9";
+ break;
+ case 0xB0 :
+ return "\xEC\xBE\x85";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\xA1";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xBD";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x99";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xB5";
+ break;
+ case 0xBC :
+ return "\xED\x80\x91";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xAD";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x89";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\xA5";
+ break;
+ case 0xAC :
+ return "\xED\x82\x81";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x9D";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xB9";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x95";
+ break;
+ case 0x9C :
+ return "\xED\x83\xB1";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x8D";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xA9";
+ break;
+ case 0xB0 :
+ return "\xED\x85\x85";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\xA1";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xBD";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x99";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xB5";
+ break;
+ case 0xBC :
+ return "\xED\x87\x91";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xAD";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x89";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\xA5";
+ break;
+ case 0xAC :
+ return "\xED\x89\x81";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x9D";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xB9";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x95";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xB1";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x8D";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xA9";
+ break;
+ case 0xB0 :
+ return "\xED\x8C\x85";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\xA1";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xBD";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x99";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xB5";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x91";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xAD";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x89";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\xA5";
+ break;
+ case 0xAC :
+ return "\xED\x90\x81";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x9D";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xB9";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x95";
+ break;
+ case 0x9C :
+ return "\xED\x91\xB1";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x8D";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xA9";
+ break;
+ case 0xB0 :
+ return "\xED\x93\x85";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\xA1";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xBD";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x99";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xB5";
+ break;
+ case 0xBC :
+ return "\xED\x95\x91";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xAD";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x89";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\xA5";
+ break;
+ case 0xAC :
+ return "\xED\x97\x81";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x9D";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xB9";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x95";
+ break;
+ case 0x9C :
+ return "\xED\x98\xB1";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x8D";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xA9";
+ break;
+ case 0xB0 :
+ return "\xED\x9A\x85";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\xA1";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xBD";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x99";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xB5";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x91";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xAD";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x89";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\xA5";
+ break;
+ case 0xAC :
+ return "\xED\x9E\x81";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x9D";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x96";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xB2";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x8E";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xAA";
+ break;
+ case 0xB0 :
+ return "\xEA\xB2\x86";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\xA2";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xBE";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x9A";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xB6";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x92";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xAE";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x8A";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\xA6";
+ break;
+ case 0xAC :
+ return "\xEA\xB6\x82";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x9E";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xBA";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x96";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xB2";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x8E";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xAA";
+ break;
+ case 0xB0 :
+ return "\xEA\xB9\x86";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\xA2";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xBE";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x9A";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xB6";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x92";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xAE";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x8A";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\xA6";
+ break;
+ case 0xAC :
+ return "\xEA\xBD\x82";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x9E";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xBA";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x96";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xB2";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x8E";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xAA";
+ break;
+ case 0xB0 :
+ return "\xEB\x80\x86";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\xA2";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xBE";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x9A";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xB6";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x92";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xAE";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x8A";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\xA6";
+ break;
+ case 0xAC :
+ return "\xEB\x84\x82";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x9E";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xBA";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x96";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xB2";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x8E";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xAA";
+ break;
+ case 0xB0 :
+ return "\xEB\x87\x86";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\xA2";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xBE";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x9A";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xB6";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x92";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xAE";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x8A";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\xA6";
+ break;
+ case 0xAC :
+ return "\xEB\x8B\x82";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x9E";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xBA";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x96";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xB2";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x8E";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xAA";
+ break;
+ case 0xB0 :
+ return "\xEB\x8E\x86";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\xA2";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xBE";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x9A";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xB6";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x92";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xAE";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x8A";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\xA6";
+ break;
+ case 0xAC :
+ return "\xEB\x92\x82";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x9E";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xBA";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x96";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xB2";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x8E";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xAA";
+ break;
+ case 0xB0 :
+ return "\xEB\x95\x86";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\xA2";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xBE";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x9A";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xB6";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x92";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xAE";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x8A";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\xA6";
+ break;
+ case 0xAC :
+ return "\xEB\x99\x82";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x9E";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xBA";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x96";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xB2";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x8E";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xAA";
+ break;
+ case 0xB0 :
+ return "\xEB\x9C\x86";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\xA2";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xBE";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x9A";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xB6";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x92";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xAE";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x8A";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\xA6";
+ break;
+ case 0xAC :
+ return "\xEB\xA0\x82";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x9E";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xBA";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x96";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xB2";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x8E";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xAA";
+ break;
+ case 0xB0 :
+ return "\xEB\xA3\x86";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\xA2";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xBE";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x9A";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xB6";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x92";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xAE";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x8A";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\xA6";
+ break;
+ case 0xAC :
+ return "\xEB\xA7\x82";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x9E";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xBA";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x96";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xB2";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x8E";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xAA";
+ break;
+ case 0xB0 :
+ return "\xEB\xAA\x86";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\xA2";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xBE";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x9A";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xB6";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x92";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xAE";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x8A";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\xA6";
+ break;
+ case 0xAC :
+ return "\xEB\xAE\x82";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x9E";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xBA";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x96";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xB2";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x8E";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xAA";
+ break;
+ case 0xB0 :
+ return "\xEB\xB1\x86";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\xA2";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xBE";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x9A";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xB6";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x92";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xAE";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x8A";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\xA6";
+ break;
+ case 0xAC :
+ return "\xEB\xB5\x82";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x9E";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xBA";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x96";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xB2";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x8E";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xAA";
+ break;
+ case 0xB0 :
+ return "\xEB\xB8\x86";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\xA2";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xBE";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x9A";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xB6";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x92";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xAE";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x8A";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\xA6";
+ break;
+ case 0xAC :
+ return "\xEB\xBC\x82";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x9E";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xBA";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x96";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xB2";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x8E";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xAA";
+ break;
+ case 0xB0 :
+ return "\xEB\xBF\x86";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\xA2";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xBE";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x9A";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xB6";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x92";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xAE";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x8A";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\xA6";
+ break;
+ case 0xAC :
+ return "\xEC\x83\x82";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x9E";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xBA";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x96";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xB2";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x8E";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xAA";
+ break;
+ case 0xB0 :
+ return "\xEC\x86\x86";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\xA2";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xBE";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x9A";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xB6";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x92";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xAE";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x8A";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\xA6";
+ break;
+ case 0xAC :
+ return "\xEC\x8A\x82";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x9E";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xBA";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x96";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xB2";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x8E";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xAA";
+ break;
+ case 0xB0 :
+ return "\xEC\x8D\x86";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\xA2";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xBE";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x9A";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xB6";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x92";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xAE";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x8A";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\xA6";
+ break;
+ case 0xAC :
+ return "\xEC\x91\x82";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x9E";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xBA";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x96";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xB2";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x8E";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xAA";
+ break;
+ case 0xB0 :
+ return "\xEC\x94\x86";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\xA2";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xBE";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x9A";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xB6";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x92";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xAE";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x8A";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\xA6";
+ break;
+ case 0xAC :
+ return "\xEC\x98\x82";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x9E";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xBA";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x96";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xB2";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x8E";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xAA";
+ break;
+ case 0xB0 :
+ return "\xEC\x9B\x86";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\xA2";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xBE";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x9A";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xB6";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x92";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xAE";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x8A";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\xA6";
+ break;
+ case 0xAC :
+ return "\xEC\x9F\x82";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x9E";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xBA";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x96";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xB2";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x8E";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xAA";
+ break;
+ case 0xB0 :
+ return "\xEC\xA2\x86";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\xA2";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xBE";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x9A";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xB6";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x92";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xAE";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x8A";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\xA6";
+ break;
+ case 0xAC :
+ return "\xEC\xA6\x82";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x9E";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xBA";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x96";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xB2";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x8E";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xAA";
+ break;
+ case 0xB0 :
+ return "\xEC\xA9\x86";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\xA2";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xBE";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x9A";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xB6";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x92";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xAE";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x8A";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\xA6";
+ break;
+ case 0xAC :
+ return "\xEC\xAD\x82";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x9E";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xBA";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x96";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xB2";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x8E";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xAA";
+ break;
+ case 0xB0 :
+ return "\xEC\xB0\x86";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\xA2";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xBE";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x9A";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xB6";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x92";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xAE";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x8A";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\xA6";
+ break;
+ case 0xAC :
+ return "\xEC\xB4\x82";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x9E";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xBA";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x96";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xB2";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x8E";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xAA";
+ break;
+ case 0xB0 :
+ return "\xEC\xB7\x86";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\xA2";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xBE";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x9A";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xB6";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x92";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xAE";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x8A";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\xA6";
+ break;
+ case 0xAC :
+ return "\xEC\xBB\x82";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x9E";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xBA";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x96";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xB2";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x8E";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xAA";
+ break;
+ case 0xB0 :
+ return "\xEC\xBE\x86";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\xA2";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xBE";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x9A";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xB6";
+ break;
+ case 0xBC :
+ return "\xED\x80\x92";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xAE";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x8A";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\xA6";
+ break;
+ case 0xAC :
+ return "\xED\x82\x82";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x9E";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xBA";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x96";
+ break;
+ case 0x9C :
+ return "\xED\x83\xB2";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x8E";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xAA";
+ break;
+ case 0xB0 :
+ return "\xED\x85\x86";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\xA2";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xBE";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x9A";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xB6";
+ break;
+ case 0xBC :
+ return "\xED\x87\x92";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xAE";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x8A";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\xA6";
+ break;
+ case 0xAC :
+ return "\xED\x89\x82";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x9E";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xBA";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x96";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xB2";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x8E";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xAA";
+ break;
+ case 0xB0 :
+ return "\xED\x8C\x86";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\xA2";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xBE";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x9A";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xB6";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x92";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xAE";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x8A";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\xA6";
+ break;
+ case 0xAC :
+ return "\xED\x90\x82";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x9E";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xBA";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x96";
+ break;
+ case 0x9C :
+ return "\xED\x91\xB2";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x8E";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xAA";
+ break;
+ case 0xB0 :
+ return "\xED\x93\x86";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\xA2";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xBE";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x9A";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xB6";
+ break;
+ case 0xBC :
+ return "\xED\x95\x92";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xAE";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x8A";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\xA6";
+ break;
+ case 0xAC :
+ return "\xED\x97\x82";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x9E";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xBA";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x96";
+ break;
+ case 0x9C :
+ return "\xED\x98\xB2";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x8E";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xAA";
+ break;
+ case 0xB0 :
+ return "\xED\x9A\x86";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\xA2";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xBE";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x9A";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xB6";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x92";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xAE";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x8A";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\xA6";
+ break;
+ case 0xAC :
+ return "\xED\x9E\x82";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x9E";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x97";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xB3";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x8F";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xAB";
+ break;
+ case 0xB0 :
+ return "\xEA\xB2\x87";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\xA3";
+ break;
+ case 0xA8 :
+ return "\xEA\xB2\xBF";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x9B";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xB7";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x93";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xAF";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x8B";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\xA7";
+ break;
+ case 0xAC :
+ return "\xEA\xB6\x83";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\x9F";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xBB";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x97";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xB3";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x8F";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xAB";
+ break;
+ case 0xB0 :
+ return "\xEA\xB9\x87";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\xA3";
+ break;
+ case 0xA8 :
+ return "\xEA\xB9\xBF";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x9B";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xB7";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x93";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xAF";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x8B";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\xA7";
+ break;
+ case 0xAC :
+ return "\xEA\xBD\x83";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\x9F";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xBB";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x97";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xB3";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x8F";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xAB";
+ break;
+ case 0xB0 :
+ return "\xEB\x80\x87";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\xA3";
+ break;
+ case 0xA8 :
+ return "\xEB\x80\xBF";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x9B";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xB7";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x93";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xAF";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x8B";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\xA7";
+ break;
+ case 0xAC :
+ return "\xEB\x84\x83";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\x9F";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xBB";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x97";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xB3";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x8F";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xAB";
+ break;
+ case 0xB0 :
+ return "\xEB\x87\x87";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\xA3";
+ break;
+ case 0xA8 :
+ return "\xEB\x87\xBF";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x9B";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xB7";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x93";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xAF";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x8B";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\xA7";
+ break;
+ case 0xAC :
+ return "\xEB\x8B\x83";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\x9F";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xBB";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x97";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xB3";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x8F";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xAB";
+ break;
+ case 0xB0 :
+ return "\xEB\x8E\x87";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\xA3";
+ break;
+ case 0xA8 :
+ return "\xEB\x8E\xBF";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x9B";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xB7";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x93";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xAF";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x8B";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\xA7";
+ break;
+ case 0xAC :
+ return "\xEB\x92\x83";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\x9F";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xBB";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x97";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xB3";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x8F";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xAB";
+ break;
+ case 0xB0 :
+ return "\xEB\x95\x87";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\xA3";
+ break;
+ case 0xA8 :
+ return "\xEB\x95\xBF";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x9B";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xB7";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x93";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xAF";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x8B";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\xA7";
+ break;
+ case 0xAC :
+ return "\xEB\x99\x83";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\x9F";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xBB";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x97";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xB3";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x8F";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xAB";
+ break;
+ case 0xB0 :
+ return "\xEB\x9C\x87";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\xA3";
+ break;
+ case 0xA8 :
+ return "\xEB\x9C\xBF";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x9B";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xB7";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x93";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xAF";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x8B";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\xA7";
+ break;
+ case 0xAC :
+ return "\xEB\xA0\x83";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\x9F";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xBB";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x97";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xB3";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x8F";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xAB";
+ break;
+ case 0xB0 :
+ return "\xEB\xA3\x87";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\xA3";
+ break;
+ case 0xA8 :
+ return "\xEB\xA3\xBF";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x9B";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xB7";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x93";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xAF";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x8B";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\xA7";
+ break;
+ case 0xAC :
+ return "\xEB\xA7\x83";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\x9F";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xBB";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x97";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xB3";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x8F";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xAB";
+ break;
+ case 0xB0 :
+ return "\xEB\xAA\x87";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\xA3";
+ break;
+ case 0xA8 :
+ return "\xEB\xAA\xBF";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x9B";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xB7";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x93";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xAF";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x8B";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\xA7";
+ break;
+ case 0xAC :
+ return "\xEB\xAE\x83";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\x9F";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xBB";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x97";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xB3";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x8F";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xAB";
+ break;
+ case 0xB0 :
+ return "\xEB\xB1\x87";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\xA3";
+ break;
+ case 0xA8 :
+ return "\xEB\xB1\xBF";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x9B";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xB7";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x93";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xAF";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x8B";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\xA7";
+ break;
+ case 0xAC :
+ return "\xEB\xB5\x83";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\x9F";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xBB";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x97";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xB3";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x8F";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xAB";
+ break;
+ case 0xB0 :
+ return "\xEB\xB8\x87";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\xA3";
+ break;
+ case 0xA8 :
+ return "\xEB\xB8\xBF";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x9B";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xB7";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x93";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xAF";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x8B";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\xA7";
+ break;
+ case 0xAC :
+ return "\xEB\xBC\x83";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\x9F";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xBB";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x97";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xB3";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x8F";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xAB";
+ break;
+ case 0xB0 :
+ return "\xEB\xBF\x87";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\xA3";
+ break;
+ case 0xA8 :
+ return "\xEB\xBF\xBF";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x9B";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xB7";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x93";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xAF";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x8B";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\xA7";
+ break;
+ case 0xAC :
+ return "\xEC\x83\x83";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\x9F";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xBB";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x97";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xB3";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x8F";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xAB";
+ break;
+ case 0xB0 :
+ return "\xEC\x86\x87";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\xA3";
+ break;
+ case 0xA8 :
+ return "\xEC\x86\xBF";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x9B";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xB7";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x93";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xAF";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x8B";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\xA7";
+ break;
+ case 0xAC :
+ return "\xEC\x8A\x83";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\x9F";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xBB";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x97";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xB3";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x8F";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xAB";
+ break;
+ case 0xB0 :
+ return "\xEC\x8D\x87";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\xA3";
+ break;
+ case 0xA8 :
+ return "\xEC\x8D\xBF";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x9B";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xB7";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x93";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xAF";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x8B";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\xA7";
+ break;
+ case 0xAC :
+ return "\xEC\x91\x83";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\x9F";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xBB";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x97";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xB3";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x8F";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xAB";
+ break;
+ case 0xB0 :
+ return "\xEC\x94\x87";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\xA3";
+ break;
+ case 0xA8 :
+ return "\xEC\x94\xBF";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x9B";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xB7";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x93";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xAF";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x8B";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\xA7";
+ break;
+ case 0xAC :
+ return "\xEC\x98\x83";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\x9F";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xBB";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x97";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xB3";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x8F";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xAB";
+ break;
+ case 0xB0 :
+ return "\xEC\x9B\x87";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\xA3";
+ break;
+ case 0xA8 :
+ return "\xEC\x9B\xBF";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x9B";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xB7";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x93";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xAF";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x8B";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\xA7";
+ break;
+ case 0xAC :
+ return "\xEC\x9F\x83";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\x9F";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xBB";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x97";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xB3";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x8F";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xAB";
+ break;
+ case 0xB0 :
+ return "\xEC\xA2\x87";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\xA3";
+ break;
+ case 0xA8 :
+ return "\xEC\xA2\xBF";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x9B";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xB7";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x93";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xAF";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x8B";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\xA7";
+ break;
+ case 0xAC :
+ return "\xEC\xA6\x83";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\x9F";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xBB";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x97";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xB3";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x8F";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xAB";
+ break;
+ case 0xB0 :
+ return "\xEC\xA9\x87";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\xA3";
+ break;
+ case 0xA8 :
+ return "\xEC\xA9\xBF";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x9B";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xB7";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x93";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xAF";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x8B";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\xA7";
+ break;
+ case 0xAC :
+ return "\xEC\xAD\x83";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\x9F";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xBB";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x97";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xB3";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x8F";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xAB";
+ break;
+ case 0xB0 :
+ return "\xEC\xB0\x87";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\xA3";
+ break;
+ case 0xA8 :
+ return "\xEC\xB0\xBF";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x9B";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xB7";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x93";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xAF";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x8B";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\xA7";
+ break;
+ case 0xAC :
+ return "\xEC\xB4\x83";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\x9F";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xBB";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x97";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xB3";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x8F";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xAB";
+ break;
+ case 0xB0 :
+ return "\xEC\xB7\x87";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\xA3";
+ break;
+ case 0xA8 :
+ return "\xEC\xB7\xBF";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x9B";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xB7";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x93";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xAF";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x8B";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\xA7";
+ break;
+ case 0xAC :
+ return "\xEC\xBB\x83";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\x9F";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xBB";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x97";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xB3";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x8F";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xAB";
+ break;
+ case 0xB0 :
+ return "\xEC\xBE\x87";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\xA3";
+ break;
+ case 0xA8 :
+ return "\xEC\xBE\xBF";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x9B";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xB7";
+ break;
+ case 0xBC :
+ return "\xED\x80\x93";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xAF";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x8B";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\xA7";
+ break;
+ case 0xAC :
+ return "\xED\x82\x83";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\x9F";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xBB";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x97";
+ break;
+ case 0x9C :
+ return "\xED\x83\xB3";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x8F";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xAB";
+ break;
+ case 0xB0 :
+ return "\xED\x85\x87";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\xA3";
+ break;
+ case 0xA8 :
+ return "\xED\x85\xBF";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x9B";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xB7";
+ break;
+ case 0xBC :
+ return "\xED\x87\x93";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xAF";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x8B";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\xA7";
+ break;
+ case 0xAC :
+ return "\xED\x89\x83";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\x9F";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xBB";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x97";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xB3";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x8F";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xAB";
+ break;
+ case 0xB0 :
+ return "\xED\x8C\x87";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\xA3";
+ break;
+ case 0xA8 :
+ return "\xED\x8C\xBF";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x9B";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xB7";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x93";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xAF";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x8B";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\xA7";
+ break;
+ case 0xAC :
+ return "\xED\x90\x83";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\x9F";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xBB";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x97";
+ break;
+ case 0x9C :
+ return "\xED\x91\xB3";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x8F";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xAB";
+ break;
+ case 0xB0 :
+ return "\xED\x93\x87";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\xA3";
+ break;
+ case 0xA8 :
+ return "\xED\x93\xBF";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x9B";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xB7";
+ break;
+ case 0xBC :
+ return "\xED\x95\x93";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xAF";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x8B";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\xA7";
+ break;
+ case 0xAC :
+ return "\xED\x97\x83";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\x9F";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xBB";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x97";
+ break;
+ case 0x9C :
+ return "\xED\x98\xB3";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x8F";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xAB";
+ break;
+ case 0xB0 :
+ return "\xED\x9A\x87";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\xA3";
+ break;
+ case 0xA8 :
+ return "\xED\x9A\xBF";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x9B";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xB7";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x93";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xAF";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x8B";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\xA7";
+ break;
+ case 0xAC :
+ return "\xED\x9E\x83";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\x9F";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x98";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xB4";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x90";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xAC";
+ break;
+ case 0xB0 :
+ return "\xEA\xB2\x88";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\xA4";
+ break;
+ case 0xA8 :
+ return "\xEA\xB3\x80";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x9C";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xB8";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x94";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xB0";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x8C";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\xA8";
+ break;
+ case 0xAC :
+ return "\xEA\xB6\x84";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\xA0";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xBC";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x98";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xB4";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x90";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xAC";
+ break;
+ case 0xB0 :
+ return "\xEA\xB9\x88";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\xA4";
+ break;
+ case 0xA8 :
+ return "\xEA\xBA\x80";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x9C";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xB8";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x94";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xB0";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x8C";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\xA8";
+ break;
+ case 0xAC :
+ return "\xEA\xBD\x84";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\xA0";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xBC";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x98";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xB4";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x90";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xAC";
+ break;
+ case 0xB0 :
+ return "\xEB\x80\x88";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\xA4";
+ break;
+ case 0xA8 :
+ return "\xEB\x81\x80";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x9C";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xB8";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x94";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xB0";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x8C";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\xA8";
+ break;
+ case 0xAC :
+ return "\xEB\x84\x84";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\xA0";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xBC";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x98";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xB4";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x90";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xAC";
+ break;
+ case 0xB0 :
+ return "\xEB\x87\x88";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\xA4";
+ break;
+ case 0xA8 :
+ return "\xEB\x88\x80";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x9C";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xB8";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x94";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xB0";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x8C";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\xA8";
+ break;
+ case 0xAC :
+ return "\xEB\x8B\x84";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\xA0";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xBC";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x98";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xB4";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x90";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xAC";
+ break;
+ case 0xB0 :
+ return "\xEB\x8E\x88";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\xA4";
+ break;
+ case 0xA8 :
+ return "\xEB\x8F\x80";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x9C";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xB8";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x94";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xB0";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x8C";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\xA8";
+ break;
+ case 0xAC :
+ return "\xEB\x92\x84";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\xA0";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xBC";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x98";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xB4";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x90";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xAC";
+ break;
+ case 0xB0 :
+ return "\xEB\x95\x88";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\xA4";
+ break;
+ case 0xA8 :
+ return "\xEB\x96\x80";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x9C";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xB8";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x94";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xB0";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x8C";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\xA8";
+ break;
+ case 0xAC :
+ return "\xEB\x99\x84";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\xA0";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xBC";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x98";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xB4";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x90";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xAC";
+ break;
+ case 0xB0 :
+ return "\xEB\x9C\x88";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\xA4";
+ break;
+ case 0xA8 :
+ return "\xEB\x9D\x80";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x9C";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xB8";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x94";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xB0";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x8C";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\xA8";
+ break;
+ case 0xAC :
+ return "\xEB\xA0\x84";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\xA0";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xBC";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x98";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xB4";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x90";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xAC";
+ break;
+ case 0xB0 :
+ return "\xEB\xA3\x88";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\xA4";
+ break;
+ case 0xA8 :
+ return "\xEB\xA4\x80";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x9C";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xB8";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x94";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xB0";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x8C";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\xA8";
+ break;
+ case 0xAC :
+ return "\xEB\xA7\x84";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\xA0";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xBC";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x98";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xB4";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x90";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xAC";
+ break;
+ case 0xB0 :
+ return "\xEB\xAA\x88";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\xA4";
+ break;
+ case 0xA8 :
+ return "\xEB\xAB\x80";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x9C";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xB8";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x94";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xB0";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x8C";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\xA8";
+ break;
+ case 0xAC :
+ return "\xEB\xAE\x84";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\xA0";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xBC";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x98";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xB4";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x90";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xAC";
+ break;
+ case 0xB0 :
+ return "\xEB\xB1\x88";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\xA4";
+ break;
+ case 0xA8 :
+ return "\xEB\xB2\x80";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x9C";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xB8";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x94";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xB0";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x8C";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\xA8";
+ break;
+ case 0xAC :
+ return "\xEB\xB5\x84";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\xA0";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xBC";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x98";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xB4";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x90";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xAC";
+ break;
+ case 0xB0 :
+ return "\xEB\xB8\x88";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\xA4";
+ break;
+ case 0xA8 :
+ return "\xEB\xB9\x80";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x9C";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xB8";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x94";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xB0";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x8C";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\xA8";
+ break;
+ case 0xAC :
+ return "\xEB\xBC\x84";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\xA0";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xBC";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x98";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xB4";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x90";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xAC";
+ break;
+ case 0xB0 :
+ return "\xEB\xBF\x88";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\xA4";
+ break;
+ case 0xA8 :
+ return "\xEC\x80\x80";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x9C";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xB8";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x94";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xB0";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x8C";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\xA8";
+ break;
+ case 0xAC :
+ return "\xEC\x83\x84";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\xA0";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xBC";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x98";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xB4";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x90";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xAC";
+ break;
+ case 0xB0 :
+ return "\xEC\x86\x88";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\xA4";
+ break;
+ case 0xA8 :
+ return "\xEC\x87\x80";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x9C";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xB8";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x94";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xB0";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x8C";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\xA8";
+ break;
+ case 0xAC :
+ return "\xEC\x8A\x84";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\xA0";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xBC";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x98";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xB4";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x90";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xAC";
+ break;
+ case 0xB0 :
+ return "\xEC\x8D\x88";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\xA4";
+ break;
+ case 0xA8 :
+ return "\xEC\x8E\x80";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x9C";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xB8";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x94";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xB0";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x8C";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\xA8";
+ break;
+ case 0xAC :
+ return "\xEC\x91\x84";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\xA0";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xBC";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x98";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xB4";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x90";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xAC";
+ break;
+ case 0xB0 :
+ return "\xEC\x94\x88";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\xA4";
+ break;
+ case 0xA8 :
+ return "\xEC\x95\x80";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x9C";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xB8";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x94";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xB0";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x8C";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\xA8";
+ break;
+ case 0xAC :
+ return "\xEC\x98\x84";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\xA0";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xBC";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x98";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xB4";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x90";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xAC";
+ break;
+ case 0xB0 :
+ return "\xEC\x9B\x88";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\xA4";
+ break;
+ case 0xA8 :
+ return "\xEC\x9C\x80";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x9C";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xB8";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x94";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xB0";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x8C";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\xA8";
+ break;
+ case 0xAC :
+ return "\xEC\x9F\x84";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\xA0";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xBC";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x98";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xB4";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x90";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xAC";
+ break;
+ case 0xB0 :
+ return "\xEC\xA2\x88";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\xA4";
+ break;
+ case 0xA8 :
+ return "\xEC\xA3\x80";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x9C";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xB8";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x94";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xB0";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x8C";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\xA8";
+ break;
+ case 0xAC :
+ return "\xEC\xA6\x84";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\xA0";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xBC";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x98";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xB4";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x90";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xAC";
+ break;
+ case 0xB0 :
+ return "\xEC\xA9\x88";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\xA4";
+ break;
+ case 0xA8 :
+ return "\xEC\xAA\x80";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x9C";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xB8";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x94";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xB0";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x8C";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\xA8";
+ break;
+ case 0xAC :
+ return "\xEC\xAD\x84";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\xA0";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xBC";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x98";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xB4";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x90";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xAC";
+ break;
+ case 0xB0 :
+ return "\xEC\xB0\x88";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\xA4";
+ break;
+ case 0xA8 :
+ return "\xEC\xB1\x80";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x9C";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xB8";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x94";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xB0";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x8C";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\xA8";
+ break;
+ case 0xAC :
+ return "\xEC\xB4\x84";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\xA0";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xBC";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x98";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xB4";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x90";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xAC";
+ break;
+ case 0xB0 :
+ return "\xEC\xB7\x88";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\xA4";
+ break;
+ case 0xA8 :
+ return "\xEC\xB8\x80";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x9C";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xB8";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x94";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xB0";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x8C";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\xA8";
+ break;
+ case 0xAC :
+ return "\xEC\xBB\x84";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\xA0";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xBC";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x98";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xB4";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x90";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xAC";
+ break;
+ case 0xB0 :
+ return "\xEC\xBE\x88";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\xA4";
+ break;
+ case 0xA8 :
+ return "\xEC\xBF\x80";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x9C";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xB8";
+ break;
+ case 0xBC :
+ return "\xED\x80\x94";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xB0";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x8C";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\xA8";
+ break;
+ case 0xAC :
+ return "\xED\x82\x84";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\xA0";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xBC";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x98";
+ break;
+ case 0x9C :
+ return "\xED\x83\xB4";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x90";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xAC";
+ break;
+ case 0xB0 :
+ return "\xED\x85\x88";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\xA4";
+ break;
+ case 0xA8 :
+ return "\xED\x86\x80";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x9C";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xB8";
+ break;
+ case 0xBC :
+ return "\xED\x87\x94";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xB0";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x8C";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\xA8";
+ break;
+ case 0xAC :
+ return "\xED\x89\x84";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\xA0";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xBC";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x98";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xB4";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x90";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xAC";
+ break;
+ case 0xB0 :
+ return "\xED\x8C\x88";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\xA4";
+ break;
+ case 0xA8 :
+ return "\xED\x8D\x80";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x9C";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xB8";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x94";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xB0";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x8C";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\xA8";
+ break;
+ case 0xAC :
+ return "\xED\x90\x84";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\xA0";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xBC";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x98";
+ break;
+ case 0x9C :
+ return "\xED\x91\xB4";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x90";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xAC";
+ break;
+ case 0xB0 :
+ return "\xED\x93\x88";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\xA4";
+ break;
+ case 0xA8 :
+ return "\xED\x94\x80";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x9C";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xB8";
+ break;
+ case 0xBC :
+ return "\xED\x95\x94";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xB0";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x8C";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\xA8";
+ break;
+ case 0xAC :
+ return "\xED\x97\x84";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\xA0";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xBC";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x98";
+ break;
+ case 0x9C :
+ return "\xED\x98\xB4";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x90";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xAC";
+ break;
+ case 0xB0 :
+ return "\xED\x9A\x88";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\xA4";
+ break;
+ case 0xA8 :
+ return "\xED\x9B\x80";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x9C";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xB8";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x94";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xB0";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x8C";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\xA8";
+ break;
+ case 0xAC :
+ return "\xED\x9E\x84";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\xA0";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (suffix[2]) {
+ case 0x80 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x99";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xB5";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x91";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xAD";
+ break;
+ case 0xB0 :
+ return "\xEA\xB2\x89";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\xA5";
+ break;
+ case 0xA8 :
+ return "\xEA\xB3\x81";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x9D";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xB9";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x95";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xB1";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x8D";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\xA9";
+ break;
+ case 0xAC :
+ return "\xEA\xB6\x85";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\xA1";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xBD";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x99";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xB5";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x91";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xAD";
+ break;
+ case 0xB0 :
+ return "\xEA\xB9\x89";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\xA5";
+ break;
+ case 0xA8 :
+ return "\xEA\xBA\x81";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x9D";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xB9";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x95";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xB1";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x8D";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\xA9";
+ break;
+ case 0xAC :
+ return "\xEA\xBD\x85";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\xA1";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xBD";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x99";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xB5";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x91";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xAD";
+ break;
+ case 0xB0 :
+ return "\xEB\x80\x89";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\xA5";
+ break;
+ case 0xA8 :
+ return "\xEB\x81\x81";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x9D";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xB9";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x95";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xB1";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x8D";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\xA9";
+ break;
+ case 0xAC :
+ return "\xEB\x84\x85";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\xA1";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xBD";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x99";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xB5";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x91";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xAD";
+ break;
+ case 0xB0 :
+ return "\xEB\x87\x89";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\xA5";
+ break;
+ case 0xA8 :
+ return "\xEB\x88\x81";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x9D";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xB9";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x95";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xB1";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x8D";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\xA9";
+ break;
+ case 0xAC :
+ return "\xEB\x8B\x85";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\xA1";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xBD";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x99";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xB5";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x91";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xAD";
+ break;
+ case 0xB0 :
+ return "\xEB\x8E\x89";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\xA5";
+ break;
+ case 0xA8 :
+ return "\xEB\x8F\x81";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x9D";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xB9";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x95";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xB1";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x8D";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\xA9";
+ break;
+ case 0xAC :
+ return "\xEB\x92\x85";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\xA1";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xBD";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x99";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xB5";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x91";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xAD";
+ break;
+ case 0xB0 :
+ return "\xEB\x95\x89";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\xA5";
+ break;
+ case 0xA8 :
+ return "\xEB\x96\x81";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x9D";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xB9";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x95";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xB1";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x8D";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\xA9";
+ break;
+ case 0xAC :
+ return "\xEB\x99\x85";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\xA1";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xBD";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x99";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xB5";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x91";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xAD";
+ break;
+ case 0xB0 :
+ return "\xEB\x9C\x89";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\xA5";
+ break;
+ case 0xA8 :
+ return "\xEB\x9D\x81";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x9D";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xB9";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x95";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xB1";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x8D";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\xA9";
+ break;
+ case 0xAC :
+ return "\xEB\xA0\x85";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\xA1";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xBD";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x99";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xB5";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x91";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xAD";
+ break;
+ case 0xB0 :
+ return "\xEB\xA3\x89";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\xA5";
+ break;
+ case 0xA8 :
+ return "\xEB\xA4\x81";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x9D";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xB9";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x95";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xB1";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x8D";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\xA9";
+ break;
+ case 0xAC :
+ return "\xEB\xA7\x85";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\xA1";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xBD";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x99";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xB5";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x91";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xAD";
+ break;
+ case 0xB0 :
+ return "\xEB\xAA\x89";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\xA5";
+ break;
+ case 0xA8 :
+ return "\xEB\xAB\x81";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x9D";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xB9";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x95";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xB1";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x8D";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\xA9";
+ break;
+ case 0xAC :
+ return "\xEB\xAE\x85";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\xA1";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xBD";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x99";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xB5";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x91";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xAD";
+ break;
+ case 0xB0 :
+ return "\xEB\xB1\x89";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\xA5";
+ break;
+ case 0xA8 :
+ return "\xEB\xB2\x81";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x9D";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xB9";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x95";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xB1";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x8D";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\xA9";
+ break;
+ case 0xAC :
+ return "\xEB\xB5\x85";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\xA1";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xBD";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x99";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xB5";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x91";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xAD";
+ break;
+ case 0xB0 :
+ return "\xEB\xB8\x89";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\xA5";
+ break;
+ case 0xA8 :
+ return "\xEB\xB9\x81";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x9D";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xB9";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x95";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xB1";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x8D";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\xA9";
+ break;
+ case 0xAC :
+ return "\xEB\xBC\x85";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\xA1";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xBD";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x99";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xB5";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x91";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xAD";
+ break;
+ case 0xB0 :
+ return "\xEB\xBF\x89";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\xA5";
+ break;
+ case 0xA8 :
+ return "\xEC\x80\x81";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x9D";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xB9";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x95";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xB1";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x8D";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\xA9";
+ break;
+ case 0xAC :
+ return "\xEC\x83\x85";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\xA1";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xBD";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x99";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xB5";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x91";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xAD";
+ break;
+ case 0xB0 :
+ return "\xEC\x86\x89";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\xA5";
+ break;
+ case 0xA8 :
+ return "\xEC\x87\x81";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x9D";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xB9";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x95";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xB1";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x8D";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\xA9";
+ break;
+ case 0xAC :
+ return "\xEC\x8A\x85";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\xA1";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xBD";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x99";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xB5";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x91";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xAD";
+ break;
+ case 0xB0 :
+ return "\xEC\x8D\x89";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\xA5";
+ break;
+ case 0xA8 :
+ return "\xEC\x8E\x81";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x9D";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xB9";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x95";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xB1";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x8D";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\xA9";
+ break;
+ case 0xAC :
+ return "\xEC\x91\x85";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\xA1";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xBD";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x99";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xB5";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x91";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xAD";
+ break;
+ case 0xB0 :
+ return "\xEC\x94\x89";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\xA5";
+ break;
+ case 0xA8 :
+ return "\xEC\x95\x81";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x9D";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xB9";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x95";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xB1";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x8D";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\xA9";
+ break;
+ case 0xAC :
+ return "\xEC\x98\x85";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\xA1";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xBD";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x99";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xB5";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x91";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xAD";
+ break;
+ case 0xB0 :
+ return "\xEC\x9B\x89";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\xA5";
+ break;
+ case 0xA8 :
+ return "\xEC\x9C\x81";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x9D";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xB9";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x95";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xB1";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x8D";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\xA9";
+ break;
+ case 0xAC :
+ return "\xEC\x9F\x85";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\xA1";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xBD";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x99";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xB5";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x91";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xAD";
+ break;
+ case 0xB0 :
+ return "\xEC\xA2\x89";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\xA5";
+ break;
+ case 0xA8 :
+ return "\xEC\xA3\x81";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x9D";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xB9";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x95";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xB1";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x8D";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\xA9";
+ break;
+ case 0xAC :
+ return "\xEC\xA6\x85";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\xA1";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xBD";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x99";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xB5";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x91";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xAD";
+ break;
+ case 0xB0 :
+ return "\xEC\xA9\x89";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\xA5";
+ break;
+ case 0xA8 :
+ return "\xEC\xAA\x81";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x9D";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xB9";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x95";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xB1";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x8D";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\xA9";
+ break;
+ case 0xAC :
+ return "\xEC\xAD\x85";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\xA1";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xBD";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x99";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xB5";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x91";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xAD";
+ break;
+ case 0xB0 :
+ return "\xEC\xB0\x89";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\xA5";
+ break;
+ case 0xA8 :
+ return "\xEC\xB1\x81";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x9D";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xB9";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x95";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xB1";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x8D";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\xA9";
+ break;
+ case 0xAC :
+ return "\xEC\xB4\x85";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\xA1";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xBD";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x99";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xB5";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x91";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xAD";
+ break;
+ case 0xB0 :
+ return "\xEC\xB7\x89";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\xA5";
+ break;
+ case 0xA8 :
+ return "\xEC\xB8\x81";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x9D";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xB9";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x95";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xB1";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x8D";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\xA9";
+ break;
+ case 0xAC :
+ return "\xEC\xBB\x85";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\xA1";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xBD";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x99";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xB5";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x91";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xAD";
+ break;
+ case 0xB0 :
+ return "\xEC\xBE\x89";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\xA5";
+ break;
+ case 0xA8 :
+ return "\xEC\xBF\x81";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x9D";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xB9";
+ break;
+ case 0xBC :
+ return "\xED\x80\x95";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xB1";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x8D";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\xA9";
+ break;
+ case 0xAC :
+ return "\xED\x82\x85";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\xA1";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xBD";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x99";
+ break;
+ case 0x9C :
+ return "\xED\x83\xB5";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x91";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xAD";
+ break;
+ case 0xB0 :
+ return "\xED\x85\x89";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\xA5";
+ break;
+ case 0xA8 :
+ return "\xED\x86\x81";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x9D";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xB9";
+ break;
+ case 0xBC :
+ return "\xED\x87\x95";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xB1";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x8D";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\xA9";
+ break;
+ case 0xAC :
+ return "\xED\x89\x85";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\xA1";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xBD";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x99";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xB5";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x91";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xAD";
+ break;
+ case 0xB0 :
+ return "\xED\x8C\x89";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\xA5";
+ break;
+ case 0xA8 :
+ return "\xED\x8D\x81";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x9D";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xB9";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x95";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xB1";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x8D";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\xA9";
+ break;
+ case 0xAC :
+ return "\xED\x90\x85";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\xA1";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xBD";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x99";
+ break;
+ case 0x9C :
+ return "\xED\x91\xB5";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x91";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xAD";
+ break;
+ case 0xB0 :
+ return "\xED\x93\x89";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\xA5";
+ break;
+ case 0xA8 :
+ return "\xED\x94\x81";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x9D";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xB9";
+ break;
+ case 0xBC :
+ return "\xED\x95\x95";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xB1";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x8D";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\xA9";
+ break;
+ case 0xAC :
+ return "\xED\x97\x85";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\xA1";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xBD";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x99";
+ break;
+ case 0x9C :
+ return "\xED\x98\xB5";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x91";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xAD";
+ break;
+ case 0xB0 :
+ return "\xED\x9A\x89";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\xA5";
+ break;
+ case 0xA8 :
+ return "\xED\x9B\x81";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x9D";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xB9";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x95";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xB1";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x8D";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\xA9";
+ break;
+ case 0xAC :
+ return "\xED\x9E\x85";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\xA1";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x9A";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xB6";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x92";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xAE";
+ break;
+ case 0xB0 :
+ return "\xEA\xB2\x8A";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\xA6";
+ break;
+ case 0xA8 :
+ return "\xEA\xB3\x82";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x9E";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xBA";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x96";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xB2";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x8E";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\xAA";
+ break;
+ case 0xAC :
+ return "\xEA\xB6\x86";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\xA2";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xBE";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x9A";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xB6";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x92";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xAE";
+ break;
+ case 0xB0 :
+ return "\xEA\xB9\x8A";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\xA6";
+ break;
+ case 0xA8 :
+ return "\xEA\xBA\x82";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x9E";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xBA";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x96";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xB2";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x8E";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\xAA";
+ break;
+ case 0xAC :
+ return "\xEA\xBD\x86";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\xA2";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xBE";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x9A";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xB6";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x92";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xAE";
+ break;
+ case 0xB0 :
+ return "\xEB\x80\x8A";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\xA6";
+ break;
+ case 0xA8 :
+ return "\xEB\x81\x82";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x9E";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xBA";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x96";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xB2";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x8E";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\xAA";
+ break;
+ case 0xAC :
+ return "\xEB\x84\x86";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\xA2";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xBE";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x9A";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xB6";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x92";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xAE";
+ break;
+ case 0xB0 :
+ return "\xEB\x87\x8A";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\xA6";
+ break;
+ case 0xA8 :
+ return "\xEB\x88\x82";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x9E";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xBA";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x96";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xB2";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x8E";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\xAA";
+ break;
+ case 0xAC :
+ return "\xEB\x8B\x86";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\xA2";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xBE";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x9A";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xB6";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x92";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xAE";
+ break;
+ case 0xB0 :
+ return "\xEB\x8E\x8A";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\xA6";
+ break;
+ case 0xA8 :
+ return "\xEB\x8F\x82";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x9E";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xBA";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x96";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xB2";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x8E";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\xAA";
+ break;
+ case 0xAC :
+ return "\xEB\x92\x86";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\xA2";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xBE";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x9A";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xB6";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x92";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xAE";
+ break;
+ case 0xB0 :
+ return "\xEB\x95\x8A";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\xA6";
+ break;
+ case 0xA8 :
+ return "\xEB\x96\x82";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x9E";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xBA";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x96";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xB2";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x8E";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\xAA";
+ break;
+ case 0xAC :
+ return "\xEB\x99\x86";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\xA2";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xBE";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x9A";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xB6";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x92";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xAE";
+ break;
+ case 0xB0 :
+ return "\xEB\x9C\x8A";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\xA6";
+ break;
+ case 0xA8 :
+ return "\xEB\x9D\x82";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x9E";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xBA";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x96";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xB2";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x8E";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\xAA";
+ break;
+ case 0xAC :
+ return "\xEB\xA0\x86";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\xA2";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xBE";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x9A";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xB6";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x92";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xAE";
+ break;
+ case 0xB0 :
+ return "\xEB\xA3\x8A";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\xA6";
+ break;
+ case 0xA8 :
+ return "\xEB\xA4\x82";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x9E";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xBA";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x96";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xB2";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x8E";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\xAA";
+ break;
+ case 0xAC :
+ return "\xEB\xA7\x86";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\xA2";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xBE";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x9A";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xB6";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x92";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xAE";
+ break;
+ case 0xB0 :
+ return "\xEB\xAA\x8A";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\xA6";
+ break;
+ case 0xA8 :
+ return "\xEB\xAB\x82";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x9E";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xBA";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x96";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xB2";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x8E";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\xAA";
+ break;
+ case 0xAC :
+ return "\xEB\xAE\x86";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\xA2";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xBE";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x9A";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xB6";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x92";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xAE";
+ break;
+ case 0xB0 :
+ return "\xEB\xB1\x8A";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\xA6";
+ break;
+ case 0xA8 :
+ return "\xEB\xB2\x82";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x9E";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xBA";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x96";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xB2";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x8E";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\xAA";
+ break;
+ case 0xAC :
+ return "\xEB\xB5\x86";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\xA2";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xBE";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x9A";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xB6";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x92";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xAE";
+ break;
+ case 0xB0 :
+ return "\xEB\xB8\x8A";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\xA6";
+ break;
+ case 0xA8 :
+ return "\xEB\xB9\x82";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x9E";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xBA";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x96";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xB2";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x8E";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\xAA";
+ break;
+ case 0xAC :
+ return "\xEB\xBC\x86";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\xA2";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xBE";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x9A";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xB6";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x92";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xAE";
+ break;
+ case 0xB0 :
+ return "\xEB\xBF\x8A";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\xA6";
+ break;
+ case 0xA8 :
+ return "\xEC\x80\x82";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x9E";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xBA";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x96";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xB2";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x8E";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\xAA";
+ break;
+ case 0xAC :
+ return "\xEC\x83\x86";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\xA2";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xBE";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x9A";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xB6";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x92";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xAE";
+ break;
+ case 0xB0 :
+ return "\xEC\x86\x8A";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\xA6";
+ break;
+ case 0xA8 :
+ return "\xEC\x87\x82";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x9E";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xBA";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x96";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xB2";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x8E";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\xAA";
+ break;
+ case 0xAC :
+ return "\xEC\x8A\x86";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\xA2";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xBE";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x9A";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xB6";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x92";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xAE";
+ break;
+ case 0xB0 :
+ return "\xEC\x8D\x8A";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\xA6";
+ break;
+ case 0xA8 :
+ return "\xEC\x8E\x82";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x9E";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xBA";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x96";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xB2";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x8E";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\xAA";
+ break;
+ case 0xAC :
+ return "\xEC\x91\x86";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\xA2";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xBE";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x9A";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xB6";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x92";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xAE";
+ break;
+ case 0xB0 :
+ return "\xEC\x94\x8A";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\xA6";
+ break;
+ case 0xA8 :
+ return "\xEC\x95\x82";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x9E";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xBA";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x96";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xB2";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x8E";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\xAA";
+ break;
+ case 0xAC :
+ return "\xEC\x98\x86";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\xA2";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xBE";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x9A";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xB6";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x92";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xAE";
+ break;
+ case 0xB0 :
+ return "\xEC\x9B\x8A";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\xA6";
+ break;
+ case 0xA8 :
+ return "\xEC\x9C\x82";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x9E";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xBA";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x96";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xB2";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x8E";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\xAA";
+ break;
+ case 0xAC :
+ return "\xEC\x9F\x86";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\xA2";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xBE";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x9A";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xB6";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x92";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xAE";
+ break;
+ case 0xB0 :
+ return "\xEC\xA2\x8A";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\xA6";
+ break;
+ case 0xA8 :
+ return "\xEC\xA3\x82";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x9E";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xBA";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x96";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xB2";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x8E";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\xAA";
+ break;
+ case 0xAC :
+ return "\xEC\xA6\x86";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\xA2";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xBE";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x9A";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xB6";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x92";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xAE";
+ break;
+ case 0xB0 :
+ return "\xEC\xA9\x8A";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\xA6";
+ break;
+ case 0xA8 :
+ return "\xEC\xAA\x82";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x9E";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xBA";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x96";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xB2";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x8E";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\xAA";
+ break;
+ case 0xAC :
+ return "\xEC\xAD\x86";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\xA2";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xBE";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x9A";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xB6";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x92";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xAE";
+ break;
+ case 0xB0 :
+ return "\xEC\xB0\x8A";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\xA6";
+ break;
+ case 0xA8 :
+ return "\xEC\xB1\x82";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x9E";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xBA";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x96";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xB2";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x8E";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\xAA";
+ break;
+ case 0xAC :
+ return "\xEC\xB4\x86";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\xA2";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xBE";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x9A";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xB6";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x92";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xAE";
+ break;
+ case 0xB0 :
+ return "\xEC\xB7\x8A";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\xA6";
+ break;
+ case 0xA8 :
+ return "\xEC\xB8\x82";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x9E";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xBA";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x96";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xB2";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x8E";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\xAA";
+ break;
+ case 0xAC :
+ return "\xEC\xBB\x86";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\xA2";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xBE";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x9A";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xB6";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x92";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xAE";
+ break;
+ case 0xB0 :
+ return "\xEC\xBE\x8A";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\xA6";
+ break;
+ case 0xA8 :
+ return "\xEC\xBF\x82";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x9E";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xBA";
+ break;
+ case 0xBC :
+ return "\xED\x80\x96";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xB2";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x8E";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\xAA";
+ break;
+ case 0xAC :
+ return "\xED\x82\x86";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\xA2";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xBE";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x9A";
+ break;
+ case 0x9C :
+ return "\xED\x83\xB6";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x92";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xAE";
+ break;
+ case 0xB0 :
+ return "\xED\x85\x8A";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\xA6";
+ break;
+ case 0xA8 :
+ return "\xED\x86\x82";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x9E";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xBA";
+ break;
+ case 0xBC :
+ return "\xED\x87\x96";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xB2";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x8E";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\xAA";
+ break;
+ case 0xAC :
+ return "\xED\x89\x86";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\xA2";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xBE";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x9A";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xB6";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x92";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xAE";
+ break;
+ case 0xB0 :
+ return "\xED\x8C\x8A";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\xA6";
+ break;
+ case 0xA8 :
+ return "\xED\x8D\x82";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x9E";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xBA";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x96";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xB2";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x8E";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\xAA";
+ break;
+ case 0xAC :
+ return "\xED\x90\x86";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\xA2";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xBE";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x9A";
+ break;
+ case 0x9C :
+ return "\xED\x91\xB6";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x92";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xAE";
+ break;
+ case 0xB0 :
+ return "\xED\x93\x8A";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\xA6";
+ break;
+ case 0xA8 :
+ return "\xED\x94\x82";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x9E";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xBA";
+ break;
+ case 0xBC :
+ return "\xED\x95\x96";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xB2";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x8E";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\xAA";
+ break;
+ case 0xAC :
+ return "\xED\x97\x86";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\xA2";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xBE";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x9A";
+ break;
+ case 0x9C :
+ return "\xED\x98\xB6";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x92";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xAE";
+ break;
+ case 0xB0 :
+ return "\xED\x9A\x8A";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\xA6";
+ break;
+ case 0xA8 :
+ return "\xED\x9B\x82";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x9E";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xBA";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x96";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xB2";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x8E";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\xAA";
+ break;
+ case 0xAC :
+ return "\xED\x9E\x86";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\xA2";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[0]) {
+ case 0xEA :
+ switch (prefix[1]) {
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB0\x9B";
+ break;
+ case 0x9C :
+ return "\xEA\xB0\xB7";
+ break;
+ case 0xB8 :
+ return "\xEA\xB1\x93";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB1\xAF";
+ break;
+ case 0xB0 :
+ return "\xEA\xB2\x8B";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB2\xA7";
+ break;
+ case 0xA8 :
+ return "\xEA\xB3\x83";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xB3\x9F";
+ break;
+ case 0xA0 :
+ return "\xEA\xB3\xBB";
+ break;
+ case 0xBC :
+ return "\xEA\xB4\x97";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xB4\xB3";
+ break;
+ case 0xB4 :
+ return "\xEA\xB5\x8F";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xB5\xAB";
+ break;
+ case 0xAC :
+ return "\xEA\xB6\x87";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xB6\xA3";
+ break;
+ case 0xA4 :
+ return "\xEA\xB6\xBF";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xB7\x9B";
+ break;
+ case 0x9C :
+ return "\xEA\xB7\xB7";
+ break;
+ case 0xB8 :
+ return "\xEA\xB8\x93";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xB8\xAF";
+ break;
+ case 0xB0 :
+ return "\xEA\xB9\x8B";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEA\xB9\xA7";
+ break;
+ case 0xA8 :
+ return "\xEA\xBA\x83";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEA\xBA\x9F";
+ break;
+ case 0xA0 :
+ return "\xEA\xBA\xBB";
+ break;
+ case 0xBC :
+ return "\xEA\xBB\x97";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEA\xBB\xB3";
+ break;
+ case 0xB4 :
+ return "\xEA\xBC\x8F";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEA\xBC\xAB";
+ break;
+ case 0xAC :
+ return "\xEA\xBD\x87";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEA\xBD\xA3";
+ break;
+ case 0xA4 :
+ return "\xEA\xBD\xBF";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEA\xBE\x9B";
+ break;
+ case 0x9C :
+ return "\xEA\xBE\xB7";
+ break;
+ case 0xB8 :
+ return "\xEA\xBF\x93";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEA\xBF\xAF";
+ break;
+ case 0xB0 :
+ return "\xEB\x80\x8B";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEB :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x80\xA7";
+ break;
+ case 0xA8 :
+ return "\xEB\x81\x83";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x81\x9F";
+ break;
+ case 0xA0 :
+ return "\xEB\x81\xBB";
+ break;
+ case 0xBC :
+ return "\xEB\x82\x97";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x82\xB3";
+ break;
+ case 0xB4 :
+ return "\xEB\x83\x8F";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x83\xAB";
+ break;
+ case 0xAC :
+ return "\xEB\x84\x87";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x84\xA3";
+ break;
+ case 0xA4 :
+ return "\xEB\x84\xBF";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x85\x9B";
+ break;
+ case 0x9C :
+ return "\xEB\x85\xB7";
+ break;
+ case 0xB8 :
+ return "\xEB\x86\x93";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x86\xAF";
+ break;
+ case 0xB0 :
+ return "\xEB\x87\x8B";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x87\xA7";
+ break;
+ case 0xA8 :
+ return "\xEB\x88\x83";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x88\x9F";
+ break;
+ case 0xA0 :
+ return "\xEB\x88\xBB";
+ break;
+ case 0xBC :
+ return "\xEB\x89\x97";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x89\xB3";
+ break;
+ case 0xB4 :
+ return "\xEB\x8A\x8F";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x8A\xAB";
+ break;
+ case 0xAC :
+ return "\xEB\x8B\x87";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x8B\xA3";
+ break;
+ case 0xA4 :
+ return "\xEB\x8B\xBF";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x8C\x9B";
+ break;
+ case 0x9C :
+ return "\xEB\x8C\xB7";
+ break;
+ case 0xB8 :
+ return "\xEB\x8D\x93";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x8D\xAF";
+ break;
+ case 0xB0 :
+ return "\xEB\x8E\x8B";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x8E\xA7";
+ break;
+ case 0xA8 :
+ return "\xEB\x8F\x83";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x8F\x9F";
+ break;
+ case 0xA0 :
+ return "\xEB\x8F\xBB";
+ break;
+ case 0xBC :
+ return "\xEB\x90\x97";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x90\xB3";
+ break;
+ case 0xB4 :
+ return "\xEB\x91\x8F";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x91\xAB";
+ break;
+ case 0xAC :
+ return "\xEB\x92\x87";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x92\xA3";
+ break;
+ case 0xA4 :
+ return "\xEB\x92\xBF";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x93\x9B";
+ break;
+ case 0x9C :
+ return "\xEB\x93\xB7";
+ break;
+ case 0xB8 :
+ return "\xEB\x94\x93";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x94\xAF";
+ break;
+ case 0xB0 :
+ return "\xEB\x95\x8B";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x95\xA7";
+ break;
+ case 0xA8 :
+ return "\xEB\x96\x83";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x96\x9F";
+ break;
+ case 0xA0 :
+ return "\xEB\x96\xBB";
+ break;
+ case 0xBC :
+ return "\xEB\x97\x97";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x97\xB3";
+ break;
+ case 0xB4 :
+ return "\xEB\x98\x8F";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x98\xAB";
+ break;
+ case 0xAC :
+ return "\xEB\x99\x87";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\x99\xA3";
+ break;
+ case 0xA4 :
+ return "\xEB\x99\xBF";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\x9A\x9B";
+ break;
+ case 0x9C :
+ return "\xEB\x9A\xB7";
+ break;
+ case 0xB8 :
+ return "\xEB\x9B\x93";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\x9B\xAF";
+ break;
+ case 0xB0 :
+ return "\xEB\x9C\x8B";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\x9C\xA7";
+ break;
+ case 0xA8 :
+ return "\xEB\x9D\x83";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\x9D\x9F";
+ break;
+ case 0xA0 :
+ return "\xEB\x9D\xBB";
+ break;
+ case 0xBC :
+ return "\xEB\x9E\x97";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\x9E\xB3";
+ break;
+ case 0xB4 :
+ return "\xEB\x9F\x8F";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\x9F\xAB";
+ break;
+ case 0xAC :
+ return "\xEB\xA0\x87";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA0\xA3";
+ break;
+ case 0xA4 :
+ return "\xEB\xA0\xBF";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA1\x9B";
+ break;
+ case 0x9C :
+ return "\xEB\xA1\xB7";
+ break;
+ case 0xB8 :
+ return "\xEB\xA2\x93";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA2\xAF";
+ break;
+ case 0xB0 :
+ return "\xEB\xA3\x8B";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xA3\xA7";
+ break;
+ case 0xA8 :
+ return "\xEB\xA4\x83";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xA4\x9F";
+ break;
+ case 0xA0 :
+ return "\xEB\xA4\xBB";
+ break;
+ case 0xBC :
+ return "\xEB\xA5\x97";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xA5\xB3";
+ break;
+ case 0xB4 :
+ return "\xEB\xA6\x8F";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xA6\xAB";
+ break;
+ case 0xAC :
+ return "\xEB\xA7\x87";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xA7\xA3";
+ break;
+ case 0xA4 :
+ return "\xEB\xA7\xBF";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xA8\x9B";
+ break;
+ case 0x9C :
+ return "\xEB\xA8\xB7";
+ break;
+ case 0xB8 :
+ return "\xEB\xA9\x93";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xA9\xAF";
+ break;
+ case 0xB0 :
+ return "\xEB\xAA\x8B";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xAA\xA7";
+ break;
+ case 0xA8 :
+ return "\xEB\xAB\x83";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xAB\x9F";
+ break;
+ case 0xA0 :
+ return "\xEB\xAB\xBB";
+ break;
+ case 0xBC :
+ return "\xEB\xAC\x97";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xAC\xB3";
+ break;
+ case 0xB4 :
+ return "\xEB\xAD\x8F";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xAD\xAB";
+ break;
+ case 0xAC :
+ return "\xEB\xAE\x87";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xAE\xA3";
+ break;
+ case 0xA4 :
+ return "\xEB\xAE\xBF";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xAF\x9B";
+ break;
+ case 0x9C :
+ return "\xEB\xAF\xB7";
+ break;
+ case 0xB8 :
+ return "\xEB\xB0\x93";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB0\xAF";
+ break;
+ case 0xB0 :
+ return "\xEB\xB1\x8B";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB1\xA7";
+ break;
+ case 0xA8 :
+ return "\xEB\xB2\x83";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB2\x9F";
+ break;
+ case 0xA0 :
+ return "\xEB\xB2\xBB";
+ break;
+ case 0xBC :
+ return "\xEB\xB3\x97";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xB3\xB3";
+ break;
+ case 0xB4 :
+ return "\xEB\xB4\x8F";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xB4\xAB";
+ break;
+ case 0xAC :
+ return "\xEB\xB5\x87";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xB5\xA3";
+ break;
+ case 0xA4 :
+ return "\xEB\xB5\xBF";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xB6\x9B";
+ break;
+ case 0x9C :
+ return "\xEB\xB6\xB7";
+ break;
+ case 0xB8 :
+ return "\xEB\xB7\x93";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xB7\xAF";
+ break;
+ case 0xB0 :
+ return "\xEB\xB8\x8B";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xB8\xA7";
+ break;
+ case 0xA8 :
+ return "\xEB\xB9\x83";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEB\xB9\x9F";
+ break;
+ case 0xA0 :
+ return "\xEB\xB9\xBB";
+ break;
+ case 0xBC :
+ return "\xEB\xBA\x97";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEB\xBA\xB3";
+ break;
+ case 0xB4 :
+ return "\xEB\xBB\x8F";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEB\xBB\xAB";
+ break;
+ case 0xAC :
+ return "\xEB\xBC\x87";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEB\xBC\xA3";
+ break;
+ case 0xA4 :
+ return "\xEB\xBC\xBF";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEB\xBD\x9B";
+ break;
+ case 0x9C :
+ return "\xEB\xBD\xB7";
+ break;
+ case 0xB8 :
+ return "\xEB\xBE\x93";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEB\xBE\xAF";
+ break;
+ case 0xB0 :
+ return "\xEB\xBF\x8B";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEB\xBF\xA7";
+ break;
+ case 0xA8 :
+ return "\xEC\x80\x83";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xEC :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x80\x9F";
+ break;
+ case 0xA0 :
+ return "\xEC\x80\xBB";
+ break;
+ case 0xBC :
+ return "\xEC\x81\x97";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x81\xB3";
+ break;
+ case 0xB4 :
+ return "\xEC\x82\x8F";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x82\xAB";
+ break;
+ case 0xAC :
+ return "\xEC\x83\x87";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x83\xA3";
+ break;
+ case 0xA4 :
+ return "\xEC\x83\xBF";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x84\x9B";
+ break;
+ case 0x9C :
+ return "\xEC\x84\xB7";
+ break;
+ case 0xB8 :
+ return "\xEC\x85\x93";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x85\xAF";
+ break;
+ case 0xB0 :
+ return "\xEC\x86\x8B";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x86\xA7";
+ break;
+ case 0xA8 :
+ return "\xEC\x87\x83";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x87\x9F";
+ break;
+ case 0xA0 :
+ return "\xEC\x87\xBB";
+ break;
+ case 0xBC :
+ return "\xEC\x88\x97";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x88\xB3";
+ break;
+ case 0xB4 :
+ return "\xEC\x89\x8F";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x89\xAB";
+ break;
+ case 0xAC :
+ return "\xEC\x8A\x87";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x8A\xA3";
+ break;
+ case 0xA4 :
+ return "\xEC\x8A\xBF";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x8B\x9B";
+ break;
+ case 0x9C :
+ return "\xEC\x8B\xB7";
+ break;
+ case 0xB8 :
+ return "\xEC\x8C\x93";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x8C\xAF";
+ break;
+ case 0xB0 :
+ return "\xEC\x8D\x8B";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x8D\xA7";
+ break;
+ case 0xA8 :
+ return "\xEC\x8E\x83";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x8E\x9F";
+ break;
+ case 0xA0 :
+ return "\xEC\x8E\xBB";
+ break;
+ case 0xBC :
+ return "\xEC\x8F\x97";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x8F\xB3";
+ break;
+ case 0xB4 :
+ return "\xEC\x90\x8F";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x90\xAB";
+ break;
+ case 0xAC :
+ return "\xEC\x91\x87";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x91\xA3";
+ break;
+ case 0xA4 :
+ return "\xEC\x91\xBF";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x92\x9B";
+ break;
+ case 0x9C :
+ return "\xEC\x92\xB7";
+ break;
+ case 0xB8 :
+ return "\xEC\x93\x93";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x93\xAF";
+ break;
+ case 0xB0 :
+ return "\xEC\x94\x8B";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x94\xA7";
+ break;
+ case 0xA8 :
+ return "\xEC\x95\x83";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x95\x9F";
+ break;
+ case 0xA0 :
+ return "\xEC\x95\xBB";
+ break;
+ case 0xBC :
+ return "\xEC\x96\x97";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x96\xB3";
+ break;
+ case 0xB4 :
+ return "\xEC\x97\x8F";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x97\xAB";
+ break;
+ case 0xAC :
+ return "\xEC\x98\x87";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x98\xA3";
+ break;
+ case 0xA4 :
+ return "\xEC\x98\xBF";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\x99\x9B";
+ break;
+ case 0x9C :
+ return "\xEC\x99\xB7";
+ break;
+ case 0xB8 :
+ return "\xEC\x9A\x93";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\x9A\xAF";
+ break;
+ case 0xB0 :
+ return "\xEC\x9B\x8B";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\x9B\xA7";
+ break;
+ case 0xA8 :
+ return "\xEC\x9C\x83";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\x9C\x9F";
+ break;
+ case 0xA0 :
+ return "\xEC\x9C\xBB";
+ break;
+ case 0xBC :
+ return "\xEC\x9D\x97";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\x9D\xB3";
+ break;
+ case 0xB4 :
+ return "\xEC\x9E\x8F";
+ break;
+ }
+ break;
+ case 0x9E :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\x9E\xAB";
+ break;
+ case 0xAC :
+ return "\xEC\x9F\x87";
+ break;
+ }
+ break;
+ case 0x9F :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\x9F\xA3";
+ break;
+ case 0xA4 :
+ return "\xEC\x9F\xBF";
+ break;
+ }
+ break;
+ case 0xA0 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA0\x9B";
+ break;
+ case 0x9C :
+ return "\xEC\xA0\xB7";
+ break;
+ case 0xB8 :
+ return "\xEC\xA1\x93";
+ break;
+ }
+ break;
+ case 0xA1 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA1\xAF";
+ break;
+ case 0xB0 :
+ return "\xEC\xA2\x8B";
+ break;
+ }
+ break;
+ case 0xA2 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA2\xA7";
+ break;
+ case 0xA8 :
+ return "\xEC\xA3\x83";
+ break;
+ }
+ break;
+ case 0xA3 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xA3\x9F";
+ break;
+ case 0xA0 :
+ return "\xEC\xA3\xBB";
+ break;
+ case 0xBC :
+ return "\xEC\xA4\x97";
+ break;
+ }
+ break;
+ case 0xA4 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xA4\xB3";
+ break;
+ case 0xB4 :
+ return "\xEC\xA5\x8F";
+ break;
+ }
+ break;
+ case 0xA5 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xA5\xAB";
+ break;
+ case 0xAC :
+ return "\xEC\xA6\x87";
+ break;
+ }
+ break;
+ case 0xA6 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xA6\xA3";
+ break;
+ case 0xA4 :
+ return "\xEC\xA6\xBF";
+ break;
+ }
+ break;
+ case 0xA7 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xA7\x9B";
+ break;
+ case 0x9C :
+ return "\xEC\xA7\xB7";
+ break;
+ case 0xB8 :
+ return "\xEC\xA8\x93";
+ break;
+ }
+ break;
+ case 0xA8 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xA8\xAF";
+ break;
+ case 0xB0 :
+ return "\xEC\xA9\x8B";
+ break;
+ }
+ break;
+ case 0xA9 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xA9\xA7";
+ break;
+ case 0xA8 :
+ return "\xEC\xAA\x83";
+ break;
+ }
+ break;
+ case 0xAA :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xAA\x9F";
+ break;
+ case 0xA0 :
+ return "\xEC\xAA\xBB";
+ break;
+ case 0xBC :
+ return "\xEC\xAB\x97";
+ break;
+ }
+ break;
+ case 0xAB :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xAB\xB3";
+ break;
+ case 0xB4 :
+ return "\xEC\xAC\x8F";
+ break;
+ }
+ break;
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xAC\xAB";
+ break;
+ case 0xAC :
+ return "\xEC\xAD\x87";
+ break;
+ }
+ break;
+ case 0xAD :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xAD\xA3";
+ break;
+ case 0xA4 :
+ return "\xEC\xAD\xBF";
+ break;
+ }
+ break;
+ case 0xAE :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xAE\x9B";
+ break;
+ case 0x9C :
+ return "\xEC\xAE\xB7";
+ break;
+ case 0xB8 :
+ return "\xEC\xAF\x93";
+ break;
+ }
+ break;
+ case 0xAF :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xAF\xAF";
+ break;
+ case 0xB0 :
+ return "\xEC\xB0\x8B";
+ break;
+ }
+ break;
+ case 0xB0 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB0\xA7";
+ break;
+ case 0xA8 :
+ return "\xEC\xB1\x83";
+ break;
+ }
+ break;
+ case 0xB1 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB1\x9F";
+ break;
+ case 0xA0 :
+ return "\xEC\xB1\xBB";
+ break;
+ case 0xBC :
+ return "\xEC\xB2\x97";
+ break;
+ }
+ break;
+ case 0xB2 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB2\xB3";
+ break;
+ case 0xB4 :
+ return "\xEC\xB3\x8F";
+ break;
+ }
+ break;
+ case 0xB3 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xB3\xAB";
+ break;
+ case 0xAC :
+ return "\xEC\xB4\x87";
+ break;
+ }
+ break;
+ case 0xB4 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xB4\xA3";
+ break;
+ case 0xA4 :
+ return "\xEC\xB4\xBF";
+ break;
+ }
+ break;
+ case 0xB5 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xB5\x9B";
+ break;
+ case 0x9C :
+ return "\xEC\xB5\xB7";
+ break;
+ case 0xB8 :
+ return "\xEC\xB6\x93";
+ break;
+ }
+ break;
+ case 0xB6 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xB6\xAF";
+ break;
+ case 0xB0 :
+ return "\xEC\xB7\x8B";
+ break;
+ }
+ break;
+ case 0xB7 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xB7\xA7";
+ break;
+ case 0xA8 :
+ return "\xEC\xB8\x83";
+ break;
+ }
+ break;
+ case 0xB8 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xB8\x9F";
+ break;
+ case 0xA0 :
+ return "\xEC\xB8\xBB";
+ break;
+ case 0xBC :
+ return "\xEC\xB9\x97";
+ break;
+ }
+ break;
+ case 0xB9 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xEC\xB9\xB3";
+ break;
+ case 0xB4 :
+ return "\xEC\xBA\x8F";
+ break;
+ }
+ break;
+ case 0xBA :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xEC\xBA\xAB";
+ break;
+ case 0xAC :
+ return "\xEC\xBB\x87";
+ break;
+ }
+ break;
+ case 0xBB :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xEC\xBB\xA3";
+ break;
+ case 0xA4 :
+ return "\xEC\xBB\xBF";
+ break;
+ }
+ break;
+ case 0xBC :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xEC\xBC\x9B";
+ break;
+ case 0x9C :
+ return "\xEC\xBC\xB7";
+ break;
+ case 0xB8 :
+ return "\xEC\xBD\x93";
+ break;
+ }
+ break;
+ case 0xBD :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xEC\xBD\xAF";
+ break;
+ case 0xB0 :
+ return "\xEC\xBE\x8B";
+ break;
+ }
+ break;
+ case 0xBE :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xEC\xBE\xA7";
+ break;
+ case 0xA8 :
+ return "\xEC\xBF\x83";
+ break;
+ }
+ break;
+ case 0xBF :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xEC\xBF\x9F";
+ break;
+ case 0xA0 :
+ return "\xEC\xBF\xBB";
+ break;
+ case 0xBC :
+ return "\xED\x80\x97";
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xED :
+ switch (prefix[1]) {
+ case 0x80 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x80\xB3";
+ break;
+ case 0xB4 :
+ return "\xED\x81\x8F";
+ break;
+ }
+ break;
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x81\xAB";
+ break;
+ case 0xAC :
+ return "\xED\x82\x87";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x82\xA3";
+ break;
+ case 0xA4 :
+ return "\xED\x82\xBF";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x83\x9B";
+ break;
+ case 0x9C :
+ return "\xED\x83\xB7";
+ break;
+ case 0xB8 :
+ return "\xED\x84\x93";
+ break;
+ }
+ break;
+ case 0x84 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x84\xAF";
+ break;
+ case 0xB0 :
+ return "\xED\x85\x8B";
+ break;
+ }
+ break;
+ case 0x85 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x85\xA7";
+ break;
+ case 0xA8 :
+ return "\xED\x86\x83";
+ break;
+ }
+ break;
+ case 0x86 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x86\x9F";
+ break;
+ case 0xA0 :
+ return "\xED\x86\xBB";
+ break;
+ case 0xBC :
+ return "\xED\x87\x97";
+ break;
+ }
+ break;
+ case 0x87 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x87\xB3";
+ break;
+ case 0xB4 :
+ return "\xED\x88\x8F";
+ break;
+ }
+ break;
+ case 0x88 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x88\xAB";
+ break;
+ case 0xAC :
+ return "\xED\x89\x87";
+ break;
+ }
+ break;
+ case 0x89 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x89\xA3";
+ break;
+ case 0xA4 :
+ return "\xED\x89\xBF";
+ break;
+ }
+ break;
+ case 0x8A :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x8A\x9B";
+ break;
+ case 0x9C :
+ return "\xED\x8A\xB7";
+ break;
+ case 0xB8 :
+ return "\xED\x8B\x93";
+ break;
+ }
+ break;
+ case 0x8B :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x8B\xAF";
+ break;
+ case 0xB0 :
+ return "\xED\x8C\x8B";
+ break;
+ }
+ break;
+ case 0x8C :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x8C\xA7";
+ break;
+ case 0xA8 :
+ return "\xED\x8D\x83";
+ break;
+ }
+ break;
+ case 0x8D :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x8D\x9F";
+ break;
+ case 0xA0 :
+ return "\xED\x8D\xBB";
+ break;
+ case 0xBC :
+ return "\xED\x8E\x97";
+ break;
+ }
+ break;
+ case 0x8E :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x8E\xB3";
+ break;
+ case 0xB4 :
+ return "\xED\x8F\x8F";
+ break;
+ }
+ break;
+ case 0x8F :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x8F\xAB";
+ break;
+ case 0xAC :
+ return "\xED\x90\x87";
+ break;
+ }
+ break;
+ case 0x90 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x90\xA3";
+ break;
+ case 0xA4 :
+ return "\xED\x90\xBF";
+ break;
+ }
+ break;
+ case 0x91 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x91\x9B";
+ break;
+ case 0x9C :
+ return "\xED\x91\xB7";
+ break;
+ case 0xB8 :
+ return "\xED\x92\x93";
+ break;
+ }
+ break;
+ case 0x92 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x92\xAF";
+ break;
+ case 0xB0 :
+ return "\xED\x93\x8B";
+ break;
+ }
+ break;
+ case 0x93 :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x93\xA7";
+ break;
+ case 0xA8 :
+ return "\xED\x94\x83";
+ break;
+ }
+ break;
+ case 0x94 :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x94\x9F";
+ break;
+ case 0xA0 :
+ return "\xED\x94\xBB";
+ break;
+ case 0xBC :
+ return "\xED\x95\x97";
+ break;
+ }
+ break;
+ case 0x95 :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x95\xB3";
+ break;
+ case 0xB4 :
+ return "\xED\x96\x8F";
+ break;
+ }
+ break;
+ case 0x96 :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x96\xAB";
+ break;
+ case 0xAC :
+ return "\xED\x97\x87";
+ break;
+ }
+ break;
+ case 0x97 :
+ switch (prefix[2]) {
+ case 0x88 :
+ return "\xED\x97\xA3";
+ break;
+ case 0xA4 :
+ return "\xED\x97\xBF";
+ break;
+ }
+ break;
+ case 0x98 :
+ switch (prefix[2]) {
+ case 0x80 :
+ return "\xED\x98\x9B";
+ break;
+ case 0x9C :
+ return "\xED\x98\xB7";
+ break;
+ case 0xB8 :
+ return "\xED\x99\x93";
+ break;
+ }
+ break;
+ case 0x99 :
+ switch (prefix[2]) {
+ case 0x94 :
+ return "\xED\x99\xAF";
+ break;
+ case 0xB0 :
+ return "\xED\x9A\x8B";
+ break;
+ }
+ break;
+ case 0x9A :
+ switch (prefix[2]) {
+ case 0x8C :
+ return "\xED\x9A\xA7";
+ break;
+ case 0xA8 :
+ return "\xED\x9B\x83";
+ break;
+ }
+ break;
+ case 0x9B :
+ switch (prefix[2]) {
+ case 0x84 :
+ return "\xED\x9B\x9F";
+ break;
+ case 0xA0 :
+ return "\xED\x9B\xBB";
+ break;
+ case 0xBC :
+ return "\xED\x9C\x97";
+ break;
+ }
+ break;
+ case 0x9C :
+ switch (prefix[2]) {
+ case 0x98 :
+ return "\xED\x9C\xB3";
+ break;
+ case 0xB4 :
+ return "\xED\x9D\x8F";
+ break;
+ }
+ break;
+ case 0x9D :
+ switch (prefix[2]) {
+ case 0x90 :
+ return "\xED\x9D\xAB";
+ break;
+ case 0xAC :
+ return "\xED\x9E\x87";
+ break;
+ }
+ break;
+ case 0x9E :
+ if (prefix[2] == 0x88) {
+ return "\xED\x9E\xA3";
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ break;
+ case 0xAC :
+ if (suffix[2] == 0xB5) {
+ if (prefix[0] == 0xE1) {
+ switch (prefix[1]) {
+ case 0xAC :
+ switch (prefix[2]) {
+ case 0x85 :
+ return "\xE1\xAC\x86";
+ break;
+ case 0x87 :
+ return "\xE1\xAC\x88";
+ break;
+ case 0x89 :
+ return "\xE1\xAC\x8A";
+ break;
+ case 0x8B :
+ return "\xE1\xAC\x8C";
+ break;
+ case 0x8D :
+ return "\xE1\xAC\x8E";
+ break;
+ case 0x91 :
+ return "\xE1\xAC\x92";
+ break;
+ case 0xBA :
+ return "\xE1\xAC\xBB";
+ break;
+ case 0xBC :
+ return "\xE1\xAC\xBD";
+ break;
+ case 0xBE :
+ return "\xE1\xAD\x80";
+ break;
+ case 0xBF :
+ return "\xE1\xAD\x81";
+ break;
+ }
+ break;
+ case 0xAD :
+ if (prefix[2] == 0x82) {
+ return "\xE1\xAD\x83";
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ break;
+case 0xE3 :
+ if (suffix[1] == 0x82) {
+ switch (suffix[2]) {
+ case 0x99 :
+ if (prefix[0] == 0xE3) {
+ switch (prefix[1]) {
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0x86 :
+ return "\xE3\x82\x94";
+ break;
+ case 0x8B :
+ return "\xE3\x81\x8C";
+ break;
+ case 0x8D :
+ return "\xE3\x81\x8E";
+ break;
+ case 0x8F :
+ return "\xE3\x81\x90";
+ break;
+ case 0x91 :
+ return "\xE3\x81\x92";
+ break;
+ case 0x93 :
+ return "\xE3\x81\x94";
+ break;
+ case 0x95 :
+ return "\xE3\x81\x96";
+ break;
+ case 0x97 :
+ return "\xE3\x81\x98";
+ break;
+ case 0x99 :
+ return "\xE3\x81\x9A";
+ break;
+ case 0x9B :
+ return "\xE3\x81\x9C";
+ break;
+ case 0x9D :
+ return "\xE3\x81\x9E";
+ break;
+ case 0x9F :
+ return "\xE3\x81\xA0";
+ break;
+ case 0xA1 :
+ return "\xE3\x81\xA2";
+ break;
+ case 0xA4 :
+ return "\xE3\x81\xA5";
+ break;
+ case 0xA6 :
+ return "\xE3\x81\xA7";
+ break;
+ case 0xA8 :
+ return "\xE3\x81\xA9";
+ break;
+ case 0xAF :
+ return "\xE3\x81\xB0";
+ break;
+ case 0xB2 :
+ return "\xE3\x81\xB3";
+ break;
+ case 0xB5 :
+ return "\xE3\x81\xB6";
+ break;
+ case 0xB8 :
+ return "\xE3\x81\xB9";
+ break;
+ case 0xBB :
+ return "\xE3\x81\xBC";
+ break;
+ }
+ break;
+ case 0x82 :
+ switch (prefix[2]) {
+ case 0x9D :
+ return "\xE3\x82\x9E";
+ break;
+ case 0xA6 :
+ return "\xE3\x83\xB4";
+ break;
+ case 0xAB :
+ return "\xE3\x82\xAC";
+ break;
+ case 0xAD :
+ return "\xE3\x82\xAE";
+ break;
+ case 0xAF :
+ return "\xE3\x82\xB0";
+ break;
+ case 0xB1 :
+ return "\xE3\x82\xB2";
+ break;
+ case 0xB3 :
+ return "\xE3\x82\xB4";
+ break;
+ case 0xB5 :
+ return "\xE3\x82\xB6";
+ break;
+ case 0xB7 :
+ return "\xE3\x82\xB8";
+ break;
+ case 0xB9 :
+ return "\xE3\x82\xBA";
+ break;
+ case 0xBB :
+ return "\xE3\x82\xBC";
+ break;
+ case 0xBD :
+ return "\xE3\x82\xBE";
+ break;
+ case 0xBF :
+ return "\xE3\x83\x80";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x81 :
+ return "\xE3\x83\x82";
+ break;
+ case 0x84 :
+ return "\xE3\x83\x85";
+ break;
+ case 0x86 :
+ return "\xE3\x83\x87";
+ break;
+ case 0x88 :
+ return "\xE3\x83\x89";
+ break;
+ case 0x8F :
+ return "\xE3\x83\x90";
+ break;
+ case 0x92 :
+ return "\xE3\x83\x93";
+ break;
+ case 0x95 :
+ return "\xE3\x83\x96";
+ break;
+ case 0x98 :
+ return "\xE3\x83\x99";
+ break;
+ case 0x9B :
+ return "\xE3\x83\x9C";
+ break;
+ case 0xAF :
+ return "\xE3\x83\xB7";
+ break;
+ case 0xB0 :
+ return "\xE3\x83\xB8";
+ break;
+ case 0xB1 :
+ return "\xE3\x83\xB9";
+ break;
+ case 0xB2 :
+ return "\xE3\x83\xBA";
+ break;
+ case 0xBD :
+ return "\xE3\x83\xBE";
+ break;
+ }
+ break;
+ }
+ }
+ break;
+ case 0x9A :
+ if (prefix[0] == 0xE3) {
+ switch (prefix[1]) {
+ case 0x81 :
+ switch (prefix[2]) {
+ case 0xAF :
+ return "\xE3\x81\xB1";
+ break;
+ case 0xB2 :
+ return "\xE3\x81\xB4";
+ break;
+ case 0xB5 :
+ return "\xE3\x81\xB7";
+ break;
+ case 0xB8 :
+ return "\xE3\x81\xBA";
+ break;
+ case 0xBB :
+ return "\xE3\x81\xBD";
+ break;
+ }
+ break;
+ case 0x83 :
+ switch (prefix[2]) {
+ case 0x8F :
+ return "\xE3\x83\x91";
+ break;
+ case 0x92 :
+ return "\xE3\x83\x94";
+ break;
+ case 0x95 :
+ return "\xE3\x83\x97";
+ break;
+ case 0x98 :
+ return "\xE3\x83\x9A";
+ break;
+ case 0x9B :
+ return "\xE3\x83\x9D";
+ break;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ break;
+}
+ return 0;
+}
+
+#endif /* GRN_WITH_NFKC */
+
diff --git a/storage/mroonga/vendor/groonga/lib/nfkc.rb b/storage/mroonga/vendor/groonga/lib/nfkc.rb
new file mode 100755
index 00000000000..1a134384c80
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/nfkc.rb
@@ -0,0 +1,418 @@
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
+#
+# Copyright(C) 2010 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+$KCODE = 'u'
+
+CUSTOM_RULE_PATH = 'nfkc-custom-rules.txt'
+
+def gen_bc(file, hash, level)
+ bl = ' ' * (level * 2)
+ h2 = {}
+ hash.each{|key,val|
+ head = key[0]
+ rest = key[1..-1]
+ if h2[head]
+ h2[head][rest] = val
+ else
+ h2[head] = {rest => val}
+ end
+ }
+ if h2.size < 3
+ h2.keys.sort.each{|k|
+ if (0x80 < k)
+ file.printf("#{bl}if (str[#{level}] < 0x%02X) { return #{$lv}; }\n", k)
+ end
+ h = h2[k]
+ if h.keys.join =~ /^\x80*$/
+ $lv, = h.values
+ else
+ file.printf("#{bl}if (str[#{level}] == 0x%02X) {\n", k)
+ gen_bc(file, h, level + 1)
+ file.puts bl + '}'
+ end
+ }
+ file.puts bl + "return #{$lv};"
+ else
+ file.puts bl + "switch (str[#{level}]) {"
+ lk = 0x80
+ br = true
+ h2.keys.sort.each{|k|
+ if (lk < k)
+ for j in lk..k-1
+ file.printf("#{bl}case 0x%02X :\n", j)
+ end
+ br = false
+ end
+ unless br
+ file.puts bl + " return #{$lv};"
+ file.puts bl + ' break;'
+ end
+ h = h2[k]
+ file.printf("#{bl}case 0x%02X :\n", k)
+ if h.keys.join =~ /^\x80*$/
+ $lv, = h.values
+ br = false
+ else
+ gen_bc(file, h, level + 1)
+ file.puts bl + ' break;'
+ br = true
+ end
+ lk = k + 1
+ }
+ file.puts bl + 'default :'
+ file.puts bl + " return #{$lv};"
+ file.puts bl + ' break;'
+ file.puts bl + '}'
+ end
+end
+
+def generate_blockcode_char_type(file, option)
+ bc = {}
+ open("|./icudump --#{option}").each{|l|
+ src,_,code = l.chomp.split("\t")
+ str = src.split(':').collect{|c| format("%c", c.hex)}.join
+ bc[str] = code
+ }
+ $lv = 0
+ gen_bc(file, bc, 0)
+end
+
+def ccpush(hash, src, dst)
+ head = src.shift
+ hash[head] = {} unless hash[head]
+ if head
+ ccpush(hash[head], src, dst)
+ else
+ hash[head] = dst
+ end
+end
+
+def subst(hash, str)
+ cand = nil
+ src = str.split(//)
+ for i in 0..src.size-1
+ h = hash
+ for j in i..src.size-1
+ head = src[j]
+ h = h[head]
+ break unless h
+ if h[nil]
+ cand = src[0,i].to_s + h[nil] + src[j + 1..-1].to_s
+ end
+ end
+ return cand if cand
+ end
+ return str
+end
+
+def map_entry(map1, cc, src, dst)
+ dst.downcase! unless $case_sensitive
+ loop {
+ dst2 = subst(cc, dst)
+ break if dst2 == dst
+ dst = dst2
+ }
+ unless $keep_space
+ dst = $1 if dst =~ /^ +([^ ].*)$/
+ end
+ map1[src] = dst if src != dst
+end
+
+def create_map1()
+ cc = {}
+ open('|./icudump --cc').each{|l|
+ _,src,dst = l.chomp.split("\t")
+ if cc[src]
+ STDERR.puts "caution: ambiguous mapping #{src}|#{cc[src]}|#{dst}" if cc[src] != dst
+ end
+ ccpush(cc, src.split(//), dst)
+ }
+ map1 = {}
+ open('|./icudump --nfkd').each{|l|
+ n,src,dst = l.chomp.split("\t")
+ map_entry(map1, cc, src, dst)
+ }
+ if File.readable?(CUSTOM_RULE_PATH)
+ open(CUSTOM_RULE_PATH).each{|l|
+ src,dst = l.chomp.split("\t")
+ map_entry(map1, cc, src, dst)
+ }
+ end
+ unless $case_sensitive
+ for c in 'A'..'Z'
+ map1[c] = c.downcase
+ end
+ end
+ return map1
+end
+
+def create_map2(map1)
+ cc = {}
+ open('|./icudump --cc').each{|l|
+ _,src,dst = l.chomp.split("\t")
+ src = src.split(//).collect{|c| map1[c] || c}.join
+ dst = map1[dst] || dst
+ if cc[src] && cc[src] != dst
+ STDERR.puts("caution: inconsitent mapping '#{src}' => '#{cc[src]}'|'#{dst}'")
+ end
+ cc[src] = dst if src != dst
+ }
+ loop {
+ noccur = 0
+ cc2 = {}
+ cc.each {|src,dst|
+ src2 = src
+ chars = src.split(//)
+ l = chars.size - 1
+ for i in 0..l
+ for j in i..l
+ next if i == 0 && j == l
+ str = chars[i..j].join
+ if map1[str]
+ STDERR.printf("caution: recursive mapping '%s'=>'%s'\n", str, map1[str])
+ end
+ if cc[str]
+ src2 = (i > 0 ? chars[0..i-1].join : '') + cc[str] + (j < l ? chars[j+1..l].join : '')
+ noccur += 1
+ end
+ end
+ end
+ cc2[src2] = dst if src2 != dst
+ }
+ cc = cc2
+ STDERR.puts("substituted #{noccur} patterns.")
+ break if noccur == 0
+ STDERR.puts('try again..')
+ }
+ return cc
+end
+
+def generate_map1(file, hash, level)
+ bl = ' ' * ((level + 0) * 2)
+ if hash['']
+ dst = ''
+ hash[''].each_byte{|b| dst << format('\x%02X', b)}
+ file.puts "#{bl}return \"#{dst}\";"
+ hash.delete('')
+ end
+ return if hash.empty?
+ h2 = {}
+ hash.each{|key,val|
+ head = key[0]
+ rest = key[1..-1]
+ if h2[head]
+ h2[head][rest] = val
+ else
+ h2[head] = {rest => val}
+ end
+ }
+ if h2.size == 1
+ h2.each{|key,val|
+ file.printf("#{bl}if (str[#{level}] == 0x%02X) {\n", key)
+ generate_map1(file, val, level + 1)
+ file.puts bl + '}'
+ }
+ else
+ file.puts "#{bl}switch (str[#{level}]) {"
+ h2.keys.sort.each{|k|
+ file.printf("#{bl}case 0x%02X :\n", k)
+ generate_map1(file, h2[k], level + 1)
+ file.puts("#{bl} break;")
+ }
+ file.puts bl + '}'
+ end
+end
+
+def gen_map2_sub2(file, hash, level, indent)
+ bl = ' ' * ((level + indent + 0) * 2)
+ if hash['']
+ file.print "#{bl}return \""
+ hash[''].each_byte{|b| file.printf('\x%02X', b)}
+ file.puts "\";"
+ hash.delete('')
+ end
+ return if hash.empty?
+
+ h2 = {}
+ hash.each{|key,val|
+ head = key[0]
+ rest = key[1..-1]
+ if h2[head]
+ h2[head][rest] = val
+ else
+ h2[head] = {rest => val}
+ end
+ }
+
+ if h2.size == 1
+ h2.each{|key,val|
+ file.printf("#{bl}if (prefix[#{level}] == 0x%02X) {\n", key)
+ gen_map2_sub2(file, val, level + 1, indent)
+ file.puts bl + '}'
+ }
+ else
+ file.puts "#{bl}switch (prefix[#{level}]) {"
+ h2.keys.sort.each{|k|
+ file.printf("#{bl}case 0x%02X :\n", k)
+ gen_map2_sub2(file, h2[k], level + 1, indent)
+ file.puts("#{bl} break;")
+ }
+ file.puts bl + '}'
+ end
+end
+
+def gen_map2_sub(file, hash, level)
+ bl = ' ' * ((level + 0) * 2)
+ if hash['']
+ gen_map2_sub2(file, hash[''], 0, level)
+ hash.delete('')
+ end
+ return if hash.empty?
+ h2 = {}
+ hash.each{|key,val|
+ head = key[0]
+ rest = key[1..-1]
+ if h2[head]
+ h2[head][rest] = val
+ else
+ h2[head] = {rest => val}
+ end
+ }
+ if h2.size == 1
+ h2.each{|key,val|
+ file.printf("#{bl}if (suffix[#{level}] == 0x%02X) {\n", key)
+ gen_map2_sub(file, val, level + 1)
+ file.puts bl + '}'
+ }
+ else
+ file.puts "#{bl}switch (suffix[#{level}]) {"
+ h2.keys.sort.each{|k|
+ file.printf("#{bl}case 0x%02X :\n", k)
+ gen_map2_sub(file, h2[k], level + 1)
+ file.puts("#{bl} break;")
+ }
+ file.puts bl + '}'
+ end
+end
+
+def generate_map2(file, map2)
+ suffix = {}
+ map2.each{|src,dst|
+ chars = src.split(//)
+ if chars.size != 2
+ STDERR.puts "caution: more than two chars in pattern #{chars.join('|')}"
+ end
+ s = chars.pop
+ if suffix[s]
+ suffix[s][chars.join] = dst
+ else
+ suffix[s] = {chars.join=>dst}
+ end
+ }
+ gen_map2_sub(file, suffix, 0)
+end
+
+template = <<END
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2010 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+don't edit this file by hand. it generated automatically by nfkc.rb
+*/
+
+#include "nfkc.h"
+
+#ifdef GRN_WITH_NFKC
+
+unsigned char
+grn_nfkc_char_type(const unsigned char *str)
+{
+% return -1;
+}
+
+const char *
+grn_nfkc_map1(const unsigned char *str)
+{
+% return 0;
+}
+
+const char *
+grn_nfkc_map2(const unsigned char *prefix, const unsigned char *suffix)
+{
+% return 0;
+}
+
+#endif /* GRN_WITH_NFKC */
+
+END
+
+######## main #######
+
+ARGV.each{|arg|
+ case arg
+ when /-*c/i
+ $case_sensitive = true
+ when /-*s/i
+ $keep_space = true
+ end
+}
+
+STDERR.puts('compiling icudump')
+system('cc -Wall -O3 -o icudump icudump.c -licui18n')
+
+STDERR.puts('creating map1..')
+map1 = create_map1()
+
+STDERR.puts('creating map2..')
+map2 = create_map2(map1)
+
+outf = open('nfkc.c', 'w')
+
+tmps = template.split(/%/)
+
+#STDERR.puts('generating block code..')
+#outf.print(tmps.shift)
+#generate_blockcode_char_type(outf, 'bc')
+
+STDERR.puts('generating char type code..')
+outf.print(tmps.shift)
+generate_blockcode_char_type(outf, 'gc')
+
+STDERR.puts('generating map1 code..')
+outf.print(tmps.shift)
+generate_map1(outf, map1, 0)
+
+STDERR.puts('generating map2 code..')
+outf.print(tmps.shift)
+generate_map2(outf, map2)
+
+outf.print(tmps.shift)
+outf.close
+STDERR.puts('done.')
diff --git a/storage/mroonga/vendor/groonga/lib/normalizer.c b/storage/mroonga/vendor/groonga/lib/normalizer.c
new file mode 100644
index 00000000000..04719bdd463
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/normalizer.c
@@ -0,0 +1,1195 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <string.h>
+
+#include "normalizer_in.h"
+#include "string_in.h"
+#include <groonga/normalizer.h>
+#include <groonga/tokenizer.h>
+
+grn_rc
+grn_normalizer_register(grn_ctx *ctx,
+ const char *name_ptr,
+ int name_length,
+ grn_proc_func *init,
+ grn_proc_func *next,
+ grn_proc_func *fin)
+{
+ grn_expr_var vars[] = {
+ { NULL, 0 }
+ };
+ GRN_PTR_INIT(&vars[0].value, 0, GRN_ID_NIL);
+
+ if (name_length < 0) {
+ name_length = strlen(name_ptr);
+ }
+
+ {
+ grn_obj * const normalizer = grn_proc_create(ctx,
+ name_ptr, name_length,
+ GRN_PROC_NORMALIZER,
+ init, next, fin,
+ sizeof(*vars) / sizeof(vars),
+ vars);
+ if (!normalizer) {
+ GRN_PLUGIN_ERROR(ctx, GRN_NORMALIZER_ERROR,
+ "[normalizer] failed to register normalizer: <%.*s>",
+ name_length, name_ptr);
+ return ctx->rc;
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_normalizer_init(void)
+{
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_normalizer_fin(void)
+{
+ return GRN_SUCCESS;
+}
+
+static unsigned char symbol[] = {
+ ',', '.', 0, ':', ';', '?', '!', 0, 0, 0, '`', 0, '^', '~', '_', 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, '-', '-', '/', '\\', 0, 0, '|', 0, 0, 0, '\'', 0,
+ '"', '(', ')', 0, 0, '[', ']', '{', '}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ '+', '-', 0, 0, 0, '=', 0, '<', '>', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ '$', 0, 0, '%', '#', '&', '*', '@', 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+inline static grn_obj *
+eucjp_normalize(grn_ctx *ctx, grn_string *nstr)
+{
+ static uint16_t hankana[] = {
+ 0xa1a1, 0xa1a3, 0xa1d6, 0xa1d7, 0xa1a2, 0xa1a6, 0xa5f2, 0xa5a1, 0xa5a3,
+ 0xa5a5, 0xa5a7, 0xa5a9, 0xa5e3, 0xa5e5, 0xa5e7, 0xa5c3, 0xa1bc, 0xa5a2,
+ 0xa5a4, 0xa5a6, 0xa5a8, 0xa5aa, 0xa5ab, 0xa5ad, 0xa5af, 0xa5b1, 0xa5b3,
+ 0xa5b5, 0xa5b7, 0xa5b9, 0xa5bb, 0xa5bd, 0xa5bf, 0xa5c1, 0xa5c4, 0xa5c6,
+ 0xa5c8, 0xa5ca, 0xa5cb, 0xa5cc, 0xa5cd, 0xa5ce, 0xa5cf, 0xa5d2, 0xa5d5,
+ 0xa5d8, 0xa5db, 0xa5de, 0xa5df, 0xa5e0, 0xa5e1, 0xa5e2, 0xa5e4, 0xa5e6,
+ 0xa5e8, 0xa5e9, 0xa5ea, 0xa5eb, 0xa5ec, 0xa5ed, 0xa5ef, 0xa5f3, 0xa1ab,
+ 0xa1eb
+ };
+ static unsigned char dakuten[] = {
+ 0xf4, 0, 0, 0, 0, 0xac, 0, 0xae, 0, 0xb0, 0, 0xb2, 0, 0xb4, 0, 0xb6, 0,
+ 0xb8, 0, 0xba, 0, 0xbc, 0, 0xbe, 0, 0xc0, 0, 0xc2, 0, 0, 0xc5, 0, 0xc7,
+ 0, 0xc9, 0, 0, 0, 0, 0, 0, 0xd0, 0, 0, 0xd3, 0, 0, 0xd6, 0, 0, 0xd9, 0,
+ 0, 0xdc
+ };
+ static unsigned char handaku[] = {
+ 0xd1, 0, 0, 0xd4, 0, 0, 0xd7, 0, 0, 0xda, 0, 0, 0xdd
+ };
+ int16_t *ch;
+ const unsigned char *s, *s_, *e;
+ unsigned char *d, *d0, *d_, b;
+ uint_least8_t *cp, *ctypes, ctype;
+ size_t size = nstr->original_length_in_bytes, length = 0;
+ int removeblankp = nstr->flags & GRN_STRING_REMOVE_BLANK;
+ if (!(nstr->normalized = GRN_MALLOC(size * 2 + 1))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][eucjp] failed to allocate normalized text space");
+ return NULL;
+ }
+ d0 = (unsigned char *) nstr->normalized;
+ if (nstr->flags & GRN_STRING_WITH_CHECKS) {
+ if (!(nstr->checks = GRN_MALLOC(size * 2 * sizeof(int16_t) + 1))) {
+ GRN_FREE(nstr->normalized);
+ nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][eucjp] failed to allocate checks space");
+ return NULL;
+ }
+ }
+ ch = nstr->checks;
+ if (nstr->flags & GRN_STRING_WITH_TYPES) {
+ if (!(nstr->ctypes = GRN_MALLOC(size + 1))) {
+ GRN_FREE(nstr->checks);
+ GRN_FREE(nstr->normalized);
+ nstr->checks = NULL;
+ nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][eucjp] failed to allocate character types space");
+ return NULL;
+ }
+ }
+ cp = ctypes = nstr->ctypes;
+ e = (unsigned char *)nstr->original + size;
+ for (s = s_ = (unsigned char *) nstr->original, d = d_ = d0; s < e; s++) {
+ if ((*s & 0x80)) {
+ if (((s + 1) < e) && (*(s + 1) & 0x80)) {
+ unsigned char c1 = *s++, c2 = *s, c3 = 0;
+ switch (c1 >> 4) {
+ case 0x08 :
+ if (c1 == 0x8e && 0xa0 <= c2 && c2 <= 0xdf) {
+ uint16_t c = hankana[c2 - 0xa0];
+ switch (c) {
+ case 0xa1ab :
+ if (d > d0 + 1 && d[-2] == 0xa5
+ && 0xa6 <= d[-1] && d[-1] <= 0xdb && (b = dakuten[d[-1] - 0xa6])) {
+ *(d - 1) = b;
+ if (ch) { ch[-1] += 2; s_ += 2; }
+ continue;
+ } else {
+ *d++ = c >> 8; *d = c & 0xff;
+ }
+ break;
+ case 0xa1eb :
+ if (d > d0 + 1 && d[-2] == 0xa5
+ && 0xcf <= d[-1] && d[-1] <= 0xdb && (b = handaku[d[-1] - 0xcf])) {
+ *(d - 1) = b;
+ if (ch) { ch[-1] += 2; s_ += 2; }
+ continue;
+ } else {
+ *d++ = c >> 8; *d = c & 0xff;
+ }
+ break;
+ default :
+ *d++ = c >> 8; *d = c & 0xff;
+ break;
+ }
+ ctype = GRN_CHAR_KATAKANA;
+ } else {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ }
+ break;
+ case 0x09 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ case 0x0a :
+ switch (c1 & 0x0f) {
+ case 1 :
+ switch (c2) {
+ case 0xbc :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KATAKANA;
+ break;
+ case 0xb9 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KANJI;
+ break;
+ case 0xa1 :
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_CHAR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_CHAR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ break;
+ default :
+ if (c2 >= 0xa4 && (c3 = symbol[c2 - 0xa4])) {
+ *d = c3;
+ ctype = GRN_CHAR_SYMBOL;
+ } else {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ }
+ break;
+ }
+ break;
+ case 2 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_SYMBOL;
+ break;
+ case 3 :
+ c3 = c2 - 0x80;
+ if ('a' <= c3 && c3 <= 'z') {
+ ctype = GRN_CHAR_ALPHA;
+ *d = c3;
+ } else if ('A' <= c3 && c3 <= 'Z') {
+ ctype = GRN_CHAR_ALPHA;
+ *d = c3 + 0x20;
+ } else if ('0' <= c3 && c3 <= '9') {
+ ctype = GRN_CHAR_DIGIT;
+ *d = c3;
+ } else {
+ ctype = GRN_CHAR_OTHERS;
+ *d++ = c1; *d = c2;
+ }
+ break;
+ case 4 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_HIRAGANA;
+ break;
+ case 5 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KATAKANA;
+ break;
+ case 6 :
+ case 7 :
+ case 8 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_SYMBOL;
+ break;
+ default :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ default :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KANJI;
+ break;
+ }
+ } else {
+ /* skip invalid character */
+ continue;
+ }
+ } else {
+ unsigned char c = *s;
+ switch (c >> 4) {
+ case 0 :
+ case 1 :
+ /* skip unprintable ascii */
+ if (cp > ctypes) { *(cp - 1) |= GRN_CHAR_BLANK; }
+ continue;
+ case 2 :
+ if (c == 0x20) {
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_CHAR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_CHAR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 3 :
+ *d = c;
+ ctype = (c <= 0x39) ? GRN_CHAR_DIGIT : GRN_CHAR_SYMBOL;
+ break;
+ case 4 :
+ *d = ('A' <= c) ? c + 0x20 : c;
+ ctype = (c == 0x40) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 5 :
+ *d = (c <= 'Z') ? c + 0x20 : c;
+ ctype = (c <= 0x5a) ? GRN_CHAR_ALPHA : GRN_CHAR_SYMBOL;
+ break;
+ case 6 :
+ *d = c;
+ ctype = (c == 0x60) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 7 :
+ *d = c;
+ ctype = (c <= 0x7a) ? GRN_CHAR_ALPHA : (c == 0x7f ? GRN_CHAR_OTHERS : GRN_CHAR_SYMBOL);
+ break;
+ default :
+ *d = c;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ }
+ d++;
+ length++;
+ if (cp) { *cp++ = ctype; }
+ if (ch) {
+ *ch++ = (int16_t)(s + 1 - s_);
+ s_ = s + 1;
+ while (++d_ < d) { *ch++ = 0; }
+ }
+ }
+ if (cp) { *cp = GRN_CHAR_NULL; }
+ *d = '\0';
+ nstr->n_characters = length;
+ nstr->normalized_length_in_bytes = (size_t)(d - (unsigned char *)nstr->normalized);
+ return NULL;
+}
+
+inline static grn_obj *
+sjis_normalize(grn_ctx *ctx, grn_string *nstr)
+{
+ static uint16_t hankana[] = {
+ 0x8140, 0x8142, 0x8175, 0x8176, 0x8141, 0x8145, 0x8392, 0x8340, 0x8342,
+ 0x8344, 0x8346, 0x8348, 0x8383, 0x8385, 0x8387, 0x8362, 0x815b, 0x8341,
+ 0x8343, 0x8345, 0x8347, 0x8349, 0x834a, 0x834c, 0x834e, 0x8350, 0x8352,
+ 0x8354, 0x8356, 0x8358, 0x835a, 0x835c, 0x835e, 0x8360, 0x8363, 0x8365,
+ 0x8367, 0x8369, 0x836a, 0x836b, 0x836c, 0x836d, 0x836e, 0x8371, 0x8374,
+ 0x8377, 0x837a, 0x837d, 0x837e, 0x8380, 0x8381, 0x8382, 0x8384, 0x8386,
+ 0x8388, 0x8389, 0x838a, 0x838b, 0x838c, 0x838d, 0x838f, 0x8393, 0x814a,
+ 0x814b
+ };
+ static unsigned char dakuten[] = {
+ 0x94, 0, 0, 0, 0, 0x4b, 0, 0x4d, 0, 0x4f, 0, 0x51, 0, 0x53, 0, 0x55, 0,
+ 0x57, 0, 0x59, 0, 0x5b, 0, 0x5d, 0, 0x5f, 0, 0x61, 0, 0, 0x64, 0, 0x66,
+ 0, 0x68, 0, 0, 0, 0, 0, 0, 0x6f, 0, 0, 0x72, 0, 0, 0x75, 0, 0, 0x78, 0,
+ 0, 0x7b
+ };
+ static unsigned char handaku[] = {
+ 0x70, 0, 0, 0x73, 0, 0, 0x76, 0, 0, 0x79, 0, 0, 0x7c
+ };
+ int16_t *ch;
+ const unsigned char *s, *s_;
+ unsigned char *d, *d0, *d_, b, *e;
+ uint_least8_t *cp, *ctypes, ctype;
+ size_t size = nstr->original_length_in_bytes, length = 0;
+ int removeblankp = nstr->flags & GRN_STRING_REMOVE_BLANK;
+ if (!(nstr->normalized = GRN_MALLOC(size * 2 + 1))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][sjis] failed to allocate normalized text space");
+ return NULL;
+ }
+ d0 = (unsigned char *) nstr->normalized;
+ if (nstr->flags & GRN_STRING_WITH_CHECKS) {
+ if (!(nstr->checks = GRN_MALLOC(size * 2 * sizeof(int16_t) + 1))) {
+ GRN_FREE(nstr->normalized);
+ nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][sjis] failed to allocate checks space");
+ return NULL;
+ }
+ }
+ ch = nstr->checks;
+ if (nstr->flags & GRN_STRING_WITH_TYPES) {
+ if (!(nstr->ctypes = GRN_MALLOC(size + 1))) {
+ GRN_FREE(nstr->checks);
+ GRN_FREE(nstr->normalized);
+ nstr->checks = NULL;
+ nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][sjis] failed to allocate character types space");
+ return NULL;
+ }
+ }
+ cp = ctypes = nstr->ctypes;
+ e = (unsigned char *)nstr->original + size;
+ for (s = s_ = (unsigned char *) nstr->original, d = d_ = d0; s < e; s++) {
+ if ((*s & 0x80)) {
+ if (0xa0 <= *s && *s <= 0xdf) {
+ uint16_t c = hankana[*s - 0xa0];
+ switch (c) {
+ case 0x814a :
+ if (d > d0 + 1 && d[-2] == 0x83
+ && 0x45 <= d[-1] && d[-1] <= 0x7a && (b = dakuten[d[-1] - 0x45])) {
+ *(d - 1) = b;
+ if (ch) { ch[-1]++; s_++; }
+ continue;
+ } else {
+ *d++ = c >> 8; *d = c & 0xff;
+ }
+ break;
+ case 0x814b :
+ if (d > d0 + 1 && d[-2] == 0x83
+ && 0x6e <= d[-1] && d[-1] <= 0x7a && (b = handaku[d[-1] - 0x6e])) {
+ *(d - 1) = b;
+ if (ch) { ch[-1]++; s_++; }
+ continue;
+ } else {
+ *d++ = c >> 8; *d = c & 0xff;
+ }
+ break;
+ default :
+ *d++ = c >> 8; *d = c & 0xff;
+ break;
+ }
+ ctype = GRN_CHAR_KATAKANA;
+ } else {
+ if ((s + 1) < e && 0x40 <= *(s + 1) && *(s + 1) <= 0xfc) {
+ unsigned char c1 = *s++, c2 = *s, c3 = 0;
+ if (0x81 <= c1 && c1 <= 0x87) {
+ switch (c1 & 0x0f) {
+ case 1 :
+ switch (c2) {
+ case 0x5b :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KATAKANA;
+ break;
+ case 0x58 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KANJI;
+ break;
+ case 0x40 :
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_CHAR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_CHAR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ break;
+ default :
+ if (0x43 <= c2 && c2 <= 0x7e && (c3 = symbol[c2 - 0x43])) {
+ *d = c3;
+ ctype = GRN_CHAR_SYMBOL;
+ } else if (0x7f <= c2 && c2 <= 0x97 && (c3 = symbol[c2 - 0x44])) {
+ *d = c3;
+ ctype = GRN_CHAR_SYMBOL;
+ } else {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ }
+ break;
+ }
+ break;
+ case 2 :
+ c3 = c2 - 0x1f;
+ if (0x4f <= c2 && c2 <= 0x58) {
+ ctype = GRN_CHAR_DIGIT;
+ *d = c2 - 0x1f;
+ } else if (0x60 <= c2 && c2 <= 0x79) {
+ ctype = GRN_CHAR_ALPHA;
+ *d = c2 + 0x01;
+ } else if (0x81 <= c2 && c2 <= 0x9a) {
+ ctype = GRN_CHAR_ALPHA;
+ *d = c2 - 0x20;
+ } else if (0x9f <= c2 && c2 <= 0xf1) {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_HIRAGANA;
+ } else {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ }
+ break;
+ case 3 :
+ if (0x40 <= c2 && c2 <= 0x96) {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KATAKANA;
+ } else {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 4 :
+ case 7 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_SYMBOL;
+ break;
+ default :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ } else {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KANJI;
+ }
+ } else {
+ /* skip invalid character */
+ continue;
+ }
+ }
+ } else {
+ unsigned char c = *s;
+ switch (c >> 4) {
+ case 0 :
+ case 1 :
+ /* skip unprintable ascii */
+ if (cp > ctypes) { *(cp - 1) |= GRN_CHAR_BLANK; }
+ continue;
+ case 2 :
+ if (c == 0x20) {
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_CHAR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_CHAR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 3 :
+ *d = c;
+ ctype = (c <= 0x39) ? GRN_CHAR_DIGIT : GRN_CHAR_SYMBOL;
+ break;
+ case 4 :
+ *d = ('A' <= c) ? c + 0x20 : c;
+ ctype = (c == 0x40) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 5 :
+ *d = (c <= 'Z') ? c + 0x20 : c;
+ ctype = (c <= 0x5a) ? GRN_CHAR_ALPHA : GRN_CHAR_SYMBOL;
+ break;
+ case 6 :
+ *d = c;
+ ctype = (c == 0x60) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 7 :
+ *d = c;
+ ctype = (c <= 0x7a) ? GRN_CHAR_ALPHA : (c == 0x7f ? GRN_CHAR_OTHERS : GRN_CHAR_SYMBOL);
+ break;
+ default :
+ *d = c;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ }
+ d++;
+ length++;
+ if (cp) { *cp++ = ctype; }
+ if (ch) {
+ *ch++ = (int16_t)(s + 1 - s_);
+ s_ = s + 1;
+ while (++d_ < d) { *ch++ = 0; }
+ }
+ }
+ if (cp) { *cp = GRN_CHAR_NULL; }
+ *d = '\0';
+ nstr->n_characters = length;
+ nstr->normalized_length_in_bytes = (size_t)(d - (unsigned char *)nstr->normalized);
+ return NULL;
+}
+
+#ifdef GRN_WITH_NFKC
+const char *grn_nfkc_map1(const unsigned char *str);
+const char *grn_nfkc_map2(const unsigned char *prefix, const unsigned char *suffix);
+
+static inline int
+grn_str_charlen_utf8(grn_ctx *ctx, const unsigned char *str, const unsigned char *end)
+{
+ /* MEMO: This function allows non-null-terminated string as str. */
+ /* But requires the end of string. */
+ const unsigned char *p = str;
+ if (end <= p || !*p) { return 0; }
+ if (*p & 0x80) {
+ int b, w;
+ int size;
+ int i;
+ for (b = 0x40, w = 0; b && (*p & b); b >>= 1, w++);
+ if (!w) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "invalid utf8 string: the first bit is 0x80: <%.*s>: <%.*s>",
+ (int)(end - p), p,
+ (int)(end - str), str);
+ return 0;
+ }
+ size = w + 1;
+ for (i = 1; i < size; i++) {
+ if (++p >= end) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "invalid utf8 string: too short: "
+ "%d byte is required but %d byte is given: <%.*s>",
+ size, i,
+ (int)(end - str), str);
+ return 0;
+ }
+ if (!*p) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "invalid utf8 string: NULL character is found: <%.*s>",
+ (int)(end - str), str);
+ return 0;
+ }
+ if ((*p & 0xc0) != 0x80) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "invalid utf8 string: 0x80 is not allowed: <%.*s>: <%.*s>",
+ (int)(end - p), p,
+ (int)(end - str), str);
+ return 0;
+ }
+ }
+ return size;
+ } else {
+ return 1;
+ }
+ return 0;
+}
+
+inline static grn_obj *
+utf8_normalize(grn_ctx *ctx, grn_string *nstr)
+{
+ int16_t *ch;
+ const unsigned char *s, *s_, *s__ = NULL, *p, *p2, *pe, *e;
+ unsigned char *d, *d_, *de;
+ uint_least8_t *cp;
+ size_t length = 0, ls, lp, size = nstr->original_length_in_bytes, ds = size * 3;
+ int removeblankp = nstr->flags & GRN_STRING_REMOVE_BLANK;
+ grn_bool remove_tokenized_delimiter_p =
+ nstr->flags & GRN_STRING_REMOVE_TOKENIZED_DELIMITER;
+ if (!(nstr->normalized = GRN_MALLOC(ds + 1))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][utf8] failed to allocate normalized text space");
+ return NULL;
+ }
+ if (nstr->flags & GRN_STRING_WITH_CHECKS) {
+ if (!(nstr->checks = GRN_MALLOC(ds * sizeof(int16_t) + 1))) {
+ GRN_FREE(nstr->normalized);
+ nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][utf8] failed to allocate checks space");
+ return NULL;
+ }
+ }
+ ch = nstr->checks;
+ if (nstr->flags & GRN_STRING_WITH_TYPES) {
+ if (!(nstr->ctypes = GRN_MALLOC(ds + 1))) {
+ if (nstr->checks) { GRN_FREE(nstr->checks); nstr->checks = NULL; }
+ GRN_FREE(nstr->normalized); nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][utf8] failed to allocate character types space");
+ return NULL;
+ }
+ }
+ cp = nstr->ctypes;
+ d = (unsigned char *)nstr->normalized;
+ de = d + ds;
+ d_ = NULL;
+ e = (unsigned char *)nstr->original + size;
+ for (s = s_ = (unsigned char *)nstr->original; ; s += ls) {
+ if (!(ls = grn_str_charlen_utf8(ctx, s, e))) {
+ break;
+ }
+ if (remove_tokenized_delimiter_p &&
+ grn_tokenizer_is_tokenized_delimiter(ctx, (const char *)s, ls,
+ GRN_ENC_UTF8)) {
+ continue;
+ }
+ if ((p = (unsigned char *)grn_nfkc_map1(s))) {
+ pe = p + strlen((char *)p);
+ } else {
+ p = s;
+ pe = p + ls;
+ }
+ if (d_ && (p2 = (unsigned char *)grn_nfkc_map2(d_, p))) {
+ p = p2;
+ pe = p + strlen((char *)p);
+ if (cp) { cp--; }
+ if (ch) {
+ ch -= (d - d_);
+ if (ch[0] >= 0) {
+ s_ = s__;
+ }
+ }
+ d = d_;
+ length--;
+ }
+ for (; ; p += lp) {
+ if (!(lp = grn_str_charlen_utf8(ctx, p, pe))) {
+ break;
+ }
+ if ((*p == ' ' && removeblankp) || *p < 0x20 /* skip unprintable ascii */ ) {
+ if (cp > nstr->ctypes) { *(cp - 1) |= GRN_CHAR_BLANK; }
+ } else {
+ if (de <= d + lp) {
+ unsigned char *normalized;
+ ds += (ds >> 1) + lp;
+ if (!(normalized = GRN_REALLOC(nstr->normalized, ds + 1))) {
+ if (nstr->ctypes) { GRN_FREE(nstr->ctypes); nstr->ctypes = NULL; }
+ if (nstr->checks) { GRN_FREE(nstr->checks); nstr->checks = NULL; }
+ GRN_FREE(nstr->normalized); nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][utf8] failed to expand normalized text space");
+ return NULL;
+ }
+ de = normalized + ds;
+ d = normalized + (d - (unsigned char *)nstr->normalized);
+ nstr->normalized = (char *)normalized;
+ if (ch) {
+ int16_t *checks;
+ if (!(checks = GRN_REALLOC(nstr->checks, ds * sizeof(int16_t) + 1))) {
+ if (nstr->ctypes) { GRN_FREE(nstr->ctypes); nstr->ctypes = NULL; }
+ GRN_FREE(nstr->checks); nstr->checks = NULL;
+ GRN_FREE(nstr->normalized); nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][utf8] failed to expand checks space");
+ return NULL;
+ }
+ ch = checks + (ch - nstr->checks);
+ nstr->checks = checks;
+ }
+ if (cp) {
+ uint_least8_t *ctypes;
+ if (!(ctypes = GRN_REALLOC(nstr->ctypes, ds + 1))) {
+ GRN_FREE(nstr->ctypes); nstr->ctypes = NULL;
+ if (nstr->checks) { GRN_FREE(nstr->checks); nstr->checks = NULL; }
+ GRN_FREE(nstr->normalized); nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][utf8] failed to expand character types space");
+ return NULL;
+ }
+ cp = ctypes + (cp - nstr->ctypes);
+ nstr->ctypes = ctypes;
+ }
+ }
+ memcpy(d, p, lp);
+ d_ = d;
+ d += lp;
+ length++;
+ if (cp) { *cp++ = grn_nfkc_char_type(p); }
+ if (ch) {
+ size_t i;
+ if (s_ == s + ls) {
+ *ch++ = -1;
+ } else {
+ *ch++ = (int16_t)(s + ls - s_);
+ s__ = s_;
+ s_ = s + ls;
+ }
+ for (i = lp; i > 1; i--) { *ch++ = 0; }
+ }
+ }
+ }
+ }
+ if (cp) { *cp = GRN_CHAR_NULL; }
+ *d = '\0';
+ nstr->n_characters = length;
+ nstr->normalized_length_in_bytes = (size_t)(d - (unsigned char *)nstr->normalized);
+ return NULL;
+}
+#endif /* GRN_WITH_NFKC */
+
+inline static grn_obj *
+ascii_normalize(grn_ctx *ctx, grn_string *nstr)
+{
+ int16_t *ch;
+ const unsigned char *s, *s_, *e;
+ unsigned char *d, *d0, *d_;
+ uint_least8_t *cp, *ctypes, ctype;
+ size_t size = nstr->original_length_in_bytes, length = 0;
+ int removeblankp = nstr->flags & GRN_STRING_REMOVE_BLANK;
+ if (!(nstr->normalized = GRN_MALLOC(size + 1))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][ascii] failed to allocate normalized text space");
+ return NULL;
+ }
+ d0 = (unsigned char *) nstr->normalized;
+ if (nstr->flags & GRN_STRING_WITH_CHECKS) {
+ if (!(nstr->checks = GRN_MALLOC(size * sizeof(int16_t) + 1))) {
+ GRN_FREE(nstr->normalized);
+ nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][ascii] failed to allocate checks space");
+ return NULL;
+ }
+ }
+ ch = nstr->checks;
+ if (nstr->flags & GRN_STRING_WITH_TYPES) {
+ if (!(nstr->ctypes = GRN_MALLOC(size + 1))) {
+ GRN_FREE(nstr->checks);
+ GRN_FREE(nstr->normalized);
+ nstr->checks = NULL;
+ nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][ascii] failed to allocate character types space");
+ return NULL;
+ }
+ }
+ cp = ctypes = nstr->ctypes;
+ e = (unsigned char *)nstr->original + size;
+ for (s = s_ = (unsigned char *) nstr->original, d = d_ = d0; s < e; s++) {
+ unsigned char c = *s;
+ switch (c >> 4) {
+ case 0 :
+ case 1 :
+ /* skip unprintable ascii */
+ if (cp > ctypes) { *(cp - 1) |= GRN_CHAR_BLANK; }
+ continue;
+ case 2 :
+ if (c == 0x20) {
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_CHAR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_CHAR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 3 :
+ *d = c;
+ ctype = (c <= 0x39) ? GRN_CHAR_DIGIT : GRN_CHAR_SYMBOL;
+ break;
+ case 4 :
+ *d = ('A' <= c) ? c + 0x20 : c;
+ ctype = (c == 0x40) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 5 :
+ *d = (c <= 'Z') ? c + 0x20 : c;
+ ctype = (c <= 0x5a) ? GRN_CHAR_ALPHA : GRN_CHAR_SYMBOL;
+ break;
+ case 6 :
+ *d = c;
+ ctype = (c == 0x60) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 7 :
+ *d = c;
+ ctype = (c <= 0x7a) ? GRN_CHAR_ALPHA : (c == 0x7f ? GRN_CHAR_OTHERS : GRN_CHAR_SYMBOL);
+ break;
+ default :
+ *d = c;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ d++;
+ length++;
+ if (cp) { *cp++ = ctype; }
+ if (ch) {
+ *ch++ = (int16_t)(s + 1 - s_);
+ s_ = s + 1;
+ while (++d_ < d) { *ch++ = 0; }
+ }
+ }
+ if (cp) { *cp = GRN_CHAR_NULL; }
+ *d = '\0';
+ nstr->n_characters = length;
+ nstr->normalized_length_in_bytes = (size_t)(d - (unsigned char *)nstr->normalized);
+ return NULL;
+}
+
+/* use cp1252 as latin1 */
+inline static grn_obj *
+latin1_normalize(grn_ctx *ctx, grn_string *nstr)
+{
+ int16_t *ch;
+ const unsigned char *s, *s_, *e;
+ unsigned char *d, *d0, *d_;
+ uint_least8_t *cp, *ctypes, ctype;
+ size_t size = nstr->original_length_in_bytes, length = 0;
+ int removeblankp = nstr->flags & GRN_STRING_REMOVE_BLANK;
+ if (!(nstr->normalized = GRN_MALLOC(size + 1))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][latin1] failed to allocate normalized text space");
+ return NULL;
+ }
+ d0 = (unsigned char *) nstr->normalized;
+ if (nstr->flags & GRN_STRING_WITH_CHECKS) {
+ if (!(nstr->checks = GRN_MALLOC(size * sizeof(int16_t) + 1))) {
+ GRN_FREE(nstr->normalized);
+ nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][latin1] failed to allocate checks space");
+ return NULL;
+ }
+ }
+ ch = nstr->checks;
+ if (nstr->flags & GRN_STRING_WITH_TYPES) {
+ if (!(nstr->ctypes = GRN_MALLOC(size + 1))) {
+ GRN_FREE(nstr->checks);
+ GRN_FREE(nstr->normalized);
+ nstr->checks = NULL;
+ nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[normalizer][latin1] failed to allocate character types space");
+ return NULL;
+ }
+ }
+ cp = ctypes = nstr->ctypes;
+ e = (unsigned char *)nstr->original + size;
+ for (s = s_ = (unsigned char *) nstr->original, d = d_ = d0; s < e; s++) {
+ unsigned char c = *s;
+ switch (c >> 4) {
+ case 0 :
+ case 1 :
+ /* skip unprintable ascii */
+ if (cp > ctypes) { *(cp - 1) |= GRN_CHAR_BLANK; }
+ continue;
+ case 2 :
+ if (c == 0x20) {
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_CHAR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_CHAR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 3 :
+ *d = c;
+ ctype = (c <= 0x39) ? GRN_CHAR_DIGIT : GRN_CHAR_SYMBOL;
+ break;
+ case 4 :
+ *d = ('A' <= c) ? c + 0x20 : c;
+ ctype = (c == 0x40) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 5 :
+ *d = (c <= 'Z') ? c + 0x20 : c;
+ ctype = (c <= 0x5a) ? GRN_CHAR_ALPHA : GRN_CHAR_SYMBOL;
+ break;
+ case 6 :
+ *d = c;
+ ctype = (c == 0x60) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 7 :
+ *d = c;
+ ctype = (c <= 0x7a) ? GRN_CHAR_ALPHA : (c == 0x7f ? GRN_CHAR_OTHERS : GRN_CHAR_SYMBOL);
+ break;
+ case 8 :
+ if (c == 0x8a || c == 0x8c || c == 0x8e) {
+ *d = c + 0x10;
+ ctype = GRN_CHAR_ALPHA;
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 9 :
+ if (c == 0x9a || c == 0x9c || c == 0x9e || c == 0x9f) {
+ *d = (c == 0x9f) ? c + 0x60 : c;
+ ctype = GRN_CHAR_ALPHA;
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 0x0c :
+ *d = c + 0x20;
+ ctype = GRN_CHAR_ALPHA;
+ break;
+ case 0x0d :
+ *d = (c == 0xd7 || c == 0xdf) ? c : c + 0x20;
+ ctype = (c == 0xd7) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 0x0e :
+ *d = c;
+ ctype = GRN_CHAR_ALPHA;
+ break;
+ case 0x0f :
+ *d = c;
+ ctype = (c == 0xf7) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ default :
+ *d = c;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ d++;
+ length++;
+ if (cp) { *cp++ = ctype; }
+ if (ch) {
+ *ch++ = (int16_t)(s + 1 - s_);
+ s_ = s + 1;
+ while (++d_ < d) { *ch++ = 0; }
+ }
+ }
+ if (cp) { *cp = GRN_CHAR_NULL; }
+ *d = '\0';
+ nstr->n_characters = length;
+ nstr->normalized_length_in_bytes = (size_t)(d - (unsigned char *)nstr->normalized);
+ return NULL;
+}
+
+inline static grn_obj *
+koi8r_normalize(grn_ctx *ctx, grn_string *nstr)
+{
+ int16_t *ch;
+ const unsigned char *s, *s_, *e;
+ unsigned char *d, *d0, *d_;
+ uint_least8_t *cp, *ctypes, ctype;
+ size_t size = nstr->original_length_in_bytes, length = 0;
+ int removeblankp = nstr->flags & GRN_STRING_REMOVE_BLANK;
+ if (!(nstr->normalized = GRN_MALLOC(size + 1))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][koi8r] failed to allocate normalized text space");
+ return NULL;
+ }
+ d0 = (unsigned char *) nstr->normalized;
+ if (nstr->flags & GRN_STRING_WITH_CHECKS) {
+ if (!(nstr->checks = GRN_MALLOC(size * sizeof(int16_t) + 1))) {
+ GRN_FREE(nstr->normalized);
+ nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][koi8r] failed to allocate checks space");
+ return NULL;
+ }
+ }
+ ch = nstr->checks;
+ if (nstr->flags & GRN_STRING_WITH_TYPES) {
+ if (!(nstr->ctypes = GRN_MALLOC(size + 1))) {
+ GRN_FREE(nstr->checks);
+ GRN_FREE(nstr->normalized);
+ nstr->checks = NULL;
+ nstr->normalized = NULL;
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[string][koi8r] failed to allocate character types space");
+ return NULL;
+ }
+ }
+ cp = ctypes = nstr->ctypes;
+ e = (unsigned char *)nstr->original + size;
+ for (s = s_ = (unsigned char *) nstr->original, d = d_ = d0; s < e; s++) {
+ unsigned char c = *s;
+ switch (c >> 4) {
+ case 0 :
+ case 1 :
+ /* skip unprintable ascii */
+ if (cp > ctypes) { *(cp - 1) |= GRN_CHAR_BLANK; }
+ continue;
+ case 2 :
+ if (c == 0x20) {
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_CHAR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_CHAR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 3 :
+ *d = c;
+ ctype = (c <= 0x39) ? GRN_CHAR_DIGIT : GRN_CHAR_SYMBOL;
+ break;
+ case 4 :
+ *d = ('A' <= c) ? c + 0x20 : c;
+ ctype = (c == 0x40) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 5 :
+ *d = (c <= 'Z') ? c + 0x20 : c;
+ ctype = (c <= 0x5a) ? GRN_CHAR_ALPHA : GRN_CHAR_SYMBOL;
+ break;
+ case 6 :
+ *d = c;
+ ctype = (c == 0x60) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 7 :
+ *d = c;
+ ctype = (c <= 0x7a) ? GRN_CHAR_ALPHA : (c == 0x7f ? GRN_CHAR_OTHERS : GRN_CHAR_SYMBOL);
+ break;
+ case 0x0a :
+ *d = c;
+ ctype = (c == 0xa3) ? GRN_CHAR_ALPHA : GRN_CHAR_OTHERS;
+ break;
+ case 0x0b :
+ if (c == 0xb3) {
+ *d = c - 0x10;
+ ctype = GRN_CHAR_ALPHA;
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_OTHERS;
+ }
+ break;
+ case 0x0c :
+ case 0x0d :
+ *d = c;
+ ctype = GRN_CHAR_ALPHA;
+ break;
+ case 0x0e :
+ case 0x0f :
+ *d = c - 0x20;
+ ctype = GRN_CHAR_ALPHA;
+ break;
+ default :
+ *d = c;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ d++;
+ length++;
+ if (cp) { *cp++ = ctype; }
+ if (ch) {
+ *ch++ = (int16_t)(s + 1 - s_);
+ s_ = s + 1;
+ while (++d_ < d) { *ch++ = 0; }
+ }
+ }
+ if (cp) { *cp = GRN_CHAR_NULL; }
+ *d = '\0';
+ nstr->n_characters = length;
+ nstr->normalized_length_in_bytes = (size_t)(d - (unsigned char *)nstr->normalized);
+ return NULL;
+}
+
+static grn_obj *
+auto_next(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_string *string = (grn_string *)(args[0]);
+ switch (string->encoding) {
+ case GRN_ENC_EUC_JP :
+ eucjp_normalize(ctx, string);
+ break;
+ case GRN_ENC_UTF8 :
+#ifdef GRN_WITH_NFKC
+ utf8_normalize(ctx, string);
+#else /* GRN_WITH_NFKC */
+ ascii_normalize(ctx, string);
+#endif /* GRN_WITH_NFKC */
+ break;
+ case GRN_ENC_SJIS :
+ sjis_normalize(ctx, string);
+ break;
+ case GRN_ENC_LATIN1 :
+ latin1_normalize(ctx, string);
+ break;
+ case GRN_ENC_KOI8R :
+ koi8r_normalize(ctx, string);
+ break;
+ default :
+ ascii_normalize(ctx, string);
+ break;
+ }
+ return NULL;
+}
+
+#ifdef GRN_WITH_NFKC
+static grn_obj *
+nfkc51_next(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_string *string = (grn_string *)(args[0]);
+ utf8_normalize(ctx, string);
+ return NULL;
+}
+#endif /* GRN_WITH_NFKC */
+
+grn_rc
+grn_normalizer_normalize(grn_ctx *ctx, grn_obj *normalizer, grn_obj *string)
+{
+ grn_rc rc;
+ int nargs = 0;
+
+ grn_ctx_push(ctx, string);
+ nargs++;
+ rc = grn_proc_call(ctx, normalizer, nargs, NULL);
+ grn_ctx_pop(ctx);
+
+ return rc;
+}
+
+grn_rc
+grn_db_init_builtin_normalizers(grn_ctx *ctx)
+{
+ const char *normalizer_nfkc51_name = "NormalizerNFKC51";
+
+ grn_normalizer_register(ctx, GRN_NORMALIZER_AUTO_NAME, -1,
+ NULL, auto_next, NULL);
+
+#ifdef GRN_WITH_NFKC
+ grn_normalizer_register(ctx, normalizer_nfkc51_name, -1,
+ NULL, nfkc51_next, NULL);
+#else /* GRN_WITH_NFKC */
+ grn_normalizer_register(ctx, normalizer_nfkc51_name, -1,
+ NULL, NULL, NULL);
+#endif /* GRN_WITH_NFKC */
+/*
+ grn_normalizer_register(ctx, "NormalizerUCA", -1,
+ NULL, uca_next, NULL);
+*/
+
+ return GRN_SUCCESS;
+}
diff --git a/storage/mroonga/vendor/groonga/lib/normalizer_in.h b/storage/mroonga/vendor/groonga/lib/normalizer_in.h
new file mode 100644
index 00000000000..201c789f626
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/normalizer_in.h
@@ -0,0 +1,52 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_NORMALIZER_IN_H
+#define GRN_NORMALIZER_IN_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifndef GRN_CTX_H
+#include "ctx.h"
+#endif /* GRN_CTX_H */
+
+#ifndef GRN_DB_H
+#include "db.h"
+#endif /* GRN_DB_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GRN_NORMALIZER_AUTO_NAME "NormalizerAuto"
+
+grn_rc grn_normalizer_init(void);
+grn_rc grn_normalizer_fin(void);
+
+grn_rc grn_normalizer_normalize(grn_ctx *ctx,
+ grn_obj *normalizer,
+ grn_obj *string);
+
+grn_rc grn_db_init_builtin_normalizers(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_NORMALIZER_IN_H */
diff --git a/storage/mroonga/vendor/groonga/lib/output.c b/storage/mroonga/vendor/groonga/lib/output.c
new file mode 100644
index 00000000000..19bfd73819d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/output.c
@@ -0,0 +1,1926 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#include <string.h>
+#include "str.h"
+#include "db.h"
+#include "util.h"
+#include "output.h"
+
+#define LEVELS (&ctx->impl->levels)
+#define DEPTH (GRN_BULK_VSIZE(LEVELS)>>2)
+#define CURR_LEVEL (DEPTH ? (GRN_UINT32_VALUE_AT(LEVELS, (DEPTH - 1))) : 0)
+#define INCR_DEPTH(i) GRN_UINT32_PUT(ctx, LEVELS, i)
+#define DECR_DEPTH (DEPTH ? grn_bulk_truncate(ctx, LEVELS, GRN_BULK_VSIZE(LEVELS) - sizeof(uint32_t)) : 0)
+#define INCR_LENGTH (DEPTH ? (GRN_UINT32_VALUE_AT(LEVELS, (DEPTH - 1)) += 2) : 0)
+
+static void
+put_delimiter(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
+{
+ uint32_t level = CURR_LEVEL;
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ if (level < 2) { return; }
+ GRN_TEXT_PUTC(ctx, outbuf, ((level & 3) == 3) ? ':' : ',');
+ // if (DEPTH == 1 && ((level & 3) != 3)) { GRN_TEXT_PUTC(ctx, outbuf, '\n'); }
+ break;
+ case GRN_CONTENT_XML:
+ if (!DEPTH) { return; }
+ GRN_TEXT_PUTC(ctx, outbuf, '\n');
+ break;
+ case GRN_CONTENT_TSV:
+ if (level < 2) { return; }
+ if (DEPTH <= 2) {
+ GRN_TEXT_PUTC(ctx, outbuf, ((level & 3) == 3) ? '\t' : '\n');
+ } else {
+ GRN_TEXT_PUTC(ctx, outbuf, '\t');
+ }
+ case GRN_CONTENT_MSGPACK :
+ // do nothing
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+}
+
+void
+grn_output_array_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ const char *name, int nelements)
+{
+ put_delimiter(ctx, outbuf, output_type);
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ GRN_TEXT_PUTC(ctx, outbuf, '[');
+ break;
+ case GRN_CONTENT_XML:
+ GRN_TEXT_PUTC(ctx, outbuf, '<');
+ GRN_TEXT_PUTS(ctx, outbuf, name);
+ GRN_TEXT_PUTC(ctx, outbuf, '>');
+ grn_vector_add_element(ctx, &ctx->impl->names, name, strlen(name), 0, GRN_DB_SHORT_TEXT);
+ break;
+ case GRN_CONTENT_TSV:
+ if (DEPTH > 2) { GRN_TEXT_PUTS(ctx, outbuf, "[\t"); }
+ break;
+ case GRN_CONTENT_MSGPACK :
+#ifdef GRN_WITH_MESSAGE_PACK
+ if (nelements < 0) {
+ GRN_LOG(ctx, GRN_LOG_DEBUG,
+ "grn_output_array_open nelements (%d) for <%s>",
+ nelements,
+ name);
+ }
+ msgpack_pack_array(&ctx->impl->msgpacker, nelements);
+#endif
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+ INCR_DEPTH(0);
+}
+
+void
+grn_output_array_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
+{
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ GRN_TEXT_PUTC(ctx, outbuf, ']');
+ break;
+ case GRN_CONTENT_TSV:
+ if (DEPTH > 3) {
+ if (CURR_LEVEL >= 2) { GRN_TEXT_PUTC(ctx, outbuf, '\t'); }
+ GRN_TEXT_PUTC(ctx, outbuf, ']');
+ }
+ break;
+ case GRN_CONTENT_XML:
+ {
+ const char *name;
+ unsigned int name_len = grn_vector_pop_element(ctx, &ctx->impl->names, &name, NULL, NULL);
+ GRN_TEXT_PUTS(ctx, outbuf, "</");
+ GRN_TEXT_PUT(ctx, outbuf, name, name_len);
+ GRN_TEXT_PUTC(ctx, outbuf, '>');
+ }
+ break;
+ case GRN_CONTENT_MSGPACK :
+ // do nothing
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+ DECR_DEPTH;
+ INCR_LENGTH;
+}
+
+void
+grn_output_map_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ const char *name, int nelements)
+{
+ put_delimiter(ctx, outbuf, output_type);
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ GRN_TEXT_PUTS(ctx, outbuf, "{");
+ break;
+ case GRN_CONTENT_XML:
+ GRN_TEXT_PUTC(ctx, outbuf, '<');
+ GRN_TEXT_PUTS(ctx, outbuf, name);
+ GRN_TEXT_PUTC(ctx, outbuf, '>');
+ grn_vector_add_element(ctx, &ctx->impl->names, name, strlen(name), 0, GRN_DB_SHORT_TEXT);
+ break;
+ case GRN_CONTENT_TSV:
+ if (DEPTH > 2) { GRN_TEXT_PUTS(ctx, outbuf, "{\t"); }
+ break;
+ case GRN_CONTENT_MSGPACK :
+#ifdef GRN_WITH_MESSAGE_PACK
+ if (nelements < 0) {
+ GRN_LOG(ctx, GRN_LOG_DEBUG,
+ "grn_output_map_open nelements (%d) for <%s>",
+ nelements,
+ name);
+ }
+ msgpack_pack_map(&ctx->impl->msgpacker, nelements);
+#endif
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+ INCR_DEPTH(1);
+}
+
+void
+grn_output_map_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
+{
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ GRN_TEXT_PUTS(ctx, outbuf, "}");
+ break;
+ case GRN_CONTENT_TSV:
+ if (DEPTH > 3) {
+ if (CURR_LEVEL >= 2) { GRN_TEXT_PUTC(ctx, outbuf, '\t'); }
+ GRN_TEXT_PUTC(ctx, outbuf, '}');
+ }
+ break;
+ case GRN_CONTENT_XML:
+ {
+ const char *name;
+ unsigned int name_len = grn_vector_pop_element(ctx, &ctx->impl->names, &name, NULL, NULL);
+ GRN_TEXT_PUTS(ctx, outbuf, "</");
+ GRN_TEXT_PUT(ctx, outbuf, name, name_len);
+ GRN_TEXT_PUTC(ctx, outbuf, '>');
+ }
+ break;
+ case GRN_CONTENT_MSGPACK :
+ // do nothing
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+ DECR_DEPTH;
+ INCR_LENGTH;
+}
+
+void
+grn_output_int32(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int value)
+{
+ put_delimiter(ctx, outbuf, output_type);
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ grn_text_itoa(ctx, outbuf, value);
+ break;
+ case GRN_CONTENT_TSV:
+ grn_text_itoa(ctx, outbuf, value);
+ break;
+ case GRN_CONTENT_XML:
+ GRN_TEXT_PUTS(ctx, outbuf, "<INT>");
+ grn_text_itoa(ctx, outbuf, value);
+ GRN_TEXT_PUTS(ctx, outbuf, "</INT>");
+ break;
+ case GRN_CONTENT_MSGPACK :
+#ifdef GRN_WITH_MESSAGE_PACK
+ msgpack_pack_int32(&ctx->impl->msgpacker, value);
+#endif
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+ INCR_LENGTH;
+}
+
+void
+grn_output_int64(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int64_t value)
+{
+ put_delimiter(ctx, outbuf, output_type);
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ grn_text_lltoa(ctx, outbuf, value);
+ break;
+ case GRN_CONTENT_TSV:
+ grn_text_lltoa(ctx, outbuf, value);
+ break;
+ case GRN_CONTENT_XML:
+ GRN_TEXT_PUTS(ctx, outbuf, "<INT>");
+ grn_text_lltoa(ctx, outbuf, value);
+ GRN_TEXT_PUTS(ctx, outbuf, "</INT>");
+ break;
+ case GRN_CONTENT_MSGPACK :
+#ifdef GRN_WITH_MESSAGE_PACK
+ msgpack_pack_int64(&ctx->impl->msgpacker, value);
+#endif
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+ INCR_LENGTH;
+}
+
+void
+grn_output_uint64(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int64_t value)
+{
+ put_delimiter(ctx, outbuf, output_type);
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ grn_text_ulltoa(ctx, outbuf, value);
+ break;
+ case GRN_CONTENT_TSV:
+ grn_text_ulltoa(ctx, outbuf, value);
+ break;
+ case GRN_CONTENT_XML:
+ GRN_TEXT_PUTS(ctx, outbuf, "<INT>");
+ grn_text_ulltoa(ctx, outbuf, value);
+ GRN_TEXT_PUTS(ctx, outbuf, "</INT>");
+ break;
+ case GRN_CONTENT_MSGPACK :
+#ifdef GRN_WITH_MESSAGE_PACK
+ msgpack_pack_uint64(&ctx->impl->msgpacker, value);
+#endif
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+ INCR_LENGTH;
+}
+
+void
+grn_output_float(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, double value)
+{
+ put_delimiter(ctx, outbuf, output_type);
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ grn_text_ftoa(ctx, outbuf, value);
+ break;
+ case GRN_CONTENT_TSV:
+ grn_text_ftoa(ctx, outbuf, value);
+ break;
+ case GRN_CONTENT_XML:
+ GRN_TEXT_PUTS(ctx, outbuf, "<FLOAT>");
+ grn_text_ftoa(ctx, outbuf, value);
+ GRN_TEXT_PUTS(ctx, outbuf, "</FLOAT>");
+ break;
+ case GRN_CONTENT_MSGPACK :
+#ifdef GRN_WITH_MESSAGE_PACK
+ msgpack_pack_double(&ctx->impl->msgpacker, value);
+#endif
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+ INCR_LENGTH;
+}
+
+void
+grn_output_str(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ const char *value, size_t value_len)
+{
+ put_delimiter(ctx, outbuf, output_type);
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ grn_text_esc(ctx, outbuf, value, value_len);
+ break;
+ case GRN_CONTENT_TSV:
+ grn_text_esc(ctx, outbuf, value, value_len);
+ break;
+ case GRN_CONTENT_XML:
+ GRN_TEXT_PUTS(ctx, outbuf, "<TEXT>");
+ grn_text_escape_xml(ctx, outbuf, value, value_len);
+ GRN_TEXT_PUTS(ctx, outbuf, "</TEXT>");
+ break;
+ case GRN_CONTENT_MSGPACK :
+#ifdef GRN_WITH_MESSAGE_PACK
+ msgpack_pack_raw(&ctx->impl->msgpacker, value_len);
+ msgpack_pack_raw_body(&ctx->impl->msgpacker, value, value_len);
+#endif
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+ INCR_LENGTH;
+}
+
+void
+grn_output_cstr(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ const char *value)
+{
+ grn_output_str(ctx, outbuf, output_type, value, strlen(value));
+}
+
+void
+grn_output_bool(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, grn_bool value)
+{
+ put_delimiter(ctx, outbuf, output_type);
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false");
+ break;
+ case GRN_CONTENT_TSV:
+ GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false");
+ break;
+ case GRN_CONTENT_XML:
+ GRN_TEXT_PUTS(ctx, outbuf, "<BOOL>");
+ GRN_TEXT_PUTS(ctx, outbuf, value ? "true" : "false");
+ GRN_TEXT_PUTS(ctx, outbuf, "</BOOL>");
+ break;
+ case GRN_CONTENT_MSGPACK :
+#ifdef GRN_WITH_MESSAGE_PACK
+ if (value) {
+ msgpack_pack_true(&ctx->impl->msgpacker);
+ } else {
+ msgpack_pack_false(&ctx->impl->msgpacker);
+ }
+#endif
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+ INCR_LENGTH;
+}
+
+static inline void
+grn_output_null(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type)
+{
+ put_delimiter(ctx, outbuf, output_type);
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ GRN_TEXT_PUTS(ctx, outbuf, "null");
+ break;
+ case GRN_CONTENT_TSV:
+ break;
+ case GRN_CONTENT_XML:
+ GRN_TEXT_PUTS(ctx, outbuf, "<NULL/>");
+ break;
+ case GRN_CONTENT_MSGPACK :
+#ifdef GRN_WITH_MESSAGE_PACK
+ msgpack_pack_nil(&ctx->impl->msgpacker);
+#endif
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+ INCR_LENGTH;
+}
+
+static inline void
+grn_output_bulk_void(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ const char *value, size_t value_len)
+{
+ if (value_len == sizeof(grn_id) && *(grn_id *)value == GRN_ID_NIL) {
+ grn_output_null(ctx, outbuf, output_type);
+ } else {
+ grn_output_str(ctx, outbuf, output_type, value, value_len);
+ }
+}
+
+void
+grn_output_time(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type, int64_t value)
+{
+ double dv = value;
+ dv /= 1000000.0;
+ put_delimiter(ctx, outbuf, output_type);
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ grn_text_ftoa(ctx, outbuf, dv);
+ break;
+ case GRN_CONTENT_TSV:
+ grn_text_ftoa(ctx, outbuf, dv);
+ break;
+ case GRN_CONTENT_XML:
+ GRN_TEXT_PUTS(ctx, outbuf, "<DATE>");
+ grn_text_ftoa(ctx, outbuf, dv);
+ GRN_TEXT_PUTS(ctx, outbuf, "</DATE>");
+ break;
+ case GRN_CONTENT_MSGPACK :
+#ifdef GRN_WITH_MESSAGE_PACK
+ msgpack_pack_double(&ctx->impl->msgpacker, dv);
+#endif
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+ INCR_LENGTH;
+}
+
+void
+grn_output_geo_point(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ grn_geo_point *value)
+{
+ put_delimiter(ctx, outbuf, output_type);
+ switch (output_type) {
+ case GRN_CONTENT_JSON:
+ if (value) {
+ GRN_TEXT_PUTC(ctx, outbuf, '"');
+ grn_text_itoa(ctx, outbuf, value->latitude);
+ GRN_TEXT_PUTC(ctx, outbuf, 'x');
+ grn_text_itoa(ctx, outbuf, value->longitude);
+ GRN_TEXT_PUTC(ctx, outbuf, '"');
+ } else {
+ GRN_TEXT_PUTS(ctx, outbuf, "null");
+ }
+ break;
+ case GRN_CONTENT_TSV:
+ if (value) {
+ GRN_TEXT_PUTC(ctx, outbuf, '"');
+ grn_text_itoa(ctx, outbuf, value->latitude);
+ GRN_TEXT_PUTC(ctx, outbuf, 'x');
+ grn_text_itoa(ctx, outbuf, value->longitude);
+ GRN_TEXT_PUTC(ctx, outbuf, '"');
+ } else {
+ GRN_TEXT_PUTS(ctx, outbuf, "\"\"");
+ }
+ break;
+ case GRN_CONTENT_XML:
+ GRN_TEXT_PUTS(ctx, outbuf, "<GEO_POINT>");
+ if (value) {
+ grn_text_itoa(ctx, outbuf, value->latitude);
+ GRN_TEXT_PUTC(ctx, outbuf, 'x');
+ grn_text_itoa(ctx, outbuf, value->longitude);
+ }
+ GRN_TEXT_PUTS(ctx, outbuf, "</GEO_POINT>");
+ break;
+ case GRN_CONTENT_MSGPACK :
+#ifdef GRN_WITH_MESSAGE_PACK
+ if (value) {
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ grn_text_itoa(ctx, &buf, value->latitude);
+ GRN_TEXT_PUTC(ctx, &buf, 'x');
+ grn_text_itoa(ctx, &buf, value->longitude);
+ msgpack_pack_raw(&ctx->impl->msgpacker, GRN_TEXT_LEN(&buf));
+ msgpack_pack_raw_body(&ctx->impl->msgpacker,
+ GRN_TEXT_VALUE(&buf),
+ GRN_TEXT_LEN(&buf));
+ grn_obj_close(ctx, &buf);
+ } else {
+ msgpack_pack_nil(&ctx->impl->msgpacker);
+ }
+#endif
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+ INCR_LENGTH;
+}
+
+static void
+grn_text_atoj(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ grn_obj *obj, grn_id id)
+{
+ uint32_t vs;
+ grn_obj buf;
+ if (obj->header.type == GRN_ACCESSOR) {
+ grn_accessor *a = (grn_accessor *)obj;
+ GRN_TEXT_INIT(&buf, 0);
+ for (;;) {
+ buf.header.domain = grn_obj_get_range(ctx, obj);
+ GRN_BULK_REWIND(&buf);
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ GRN_UINT32_PUT(ctx, &buf, id);
+ buf.header.domain = GRN_DB_UINT32;
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ grn_table_get_key2(ctx, a->obj, id, &buf);
+ buf.header.domain = DB_OBJ(a->obj)->header.domain;
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ buf.header.domain = DB_OBJ(a->obj)->range;
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
+ GRN_INT32_PUT(ctx, &buf, ri->score);
+ }
+ buf.header.domain = GRN_DB_INT32;
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
+ GRN_INT32_PUT(ctx, &buf, ri->n_subrecs);
+ }
+ buf.header.domain = GRN_DB_INT32;
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ if ((a->obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR) {
+ if (a->next) {
+ grn_id *idp;
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ idp = (grn_id *)GRN_BULK_HEAD(&buf);
+ vs = GRN_BULK_VSIZE(&buf) / sizeof(grn_id);
+ grn_output_array_open(ctx, outbuf, output_type, "VECTOR", vs);
+ for (; vs--; idp++) {
+ grn_text_atoj(ctx, outbuf, output_type, (grn_obj *)a->next, *idp);
+ }
+ grn_output_array_close(ctx, outbuf, output_type);
+ } else {
+ grn_text_atoj(ctx, outbuf, output_type, a->obj, id);
+ }
+ goto exit;
+ } else {
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ }
+ break;
+ case GRN_ACCESSOR_GET_DB_OBJ :
+ /* todo */
+ break;
+ case GRN_ACCESSOR_LOOKUP :
+ /* todo */
+ break;
+ case GRN_ACCESSOR_FUNCALL :
+ /* todo */
+ break;
+ }
+ if (a->next) {
+ a = a->next;
+ if (GRN_BULK_VSIZE(&buf) >= sizeof(grn_id)) {
+ id = *((grn_id *)GRN_BULK_HEAD(&buf));
+ } else {
+ id = GRN_ID_NIL;
+ }
+ } else {
+ break;
+ }
+ }
+ grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
+ } else {
+ grn_obj_format *format_argument = NULL;
+ grn_obj_format format;
+ GRN_OBJ_FORMAT_INIT(&format, 0, 0, 0, 0);
+ switch (obj->header.type) {
+ case GRN_COLUMN_FIX_SIZE :
+ GRN_VALUE_FIX_SIZE_INIT(&buf, 0, DB_OBJ(obj)->range);
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ if ((obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR) {
+ grn_obj *range = grn_ctx_at(ctx, DB_OBJ(obj)->range);
+ if (GRN_OBJ_TABLEP(range) ||
+ (range->header.flags & GRN_OBJ_KEY_VAR_SIZE) == 0) {
+ GRN_VALUE_FIX_SIZE_INIT(&buf, GRN_OBJ_VECTOR, DB_OBJ(obj)->range);
+ } else {
+ GRN_VALUE_VAR_SIZE_INIT(&buf, GRN_OBJ_VECTOR, DB_OBJ(obj)->range);
+ }
+ if (obj->header.flags & GRN_OBJ_WITH_WEIGHT) {
+ format.flags |= GRN_OBJ_FORMAT_WITH_WEIGHT;
+ format_argument = &format;
+ }
+ } else {
+ GRN_VALUE_VAR_SIZE_INIT(&buf, 0, DB_OBJ(obj)->range);
+ }
+ break;
+ case GRN_COLUMN_INDEX :
+ GRN_UINT32_INIT(&buf, 0);
+ break;
+ default:
+ GRN_TEXT_INIT(&buf, 0);
+ break;
+ }
+ grn_obj_get_value(ctx, obj, id, &buf);
+ grn_output_obj(ctx, outbuf, output_type, &buf, format_argument);
+ }
+exit :
+ grn_obj_close(ctx, &buf);
+}
+
+static inline void
+grn_output_void(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ grn_obj *bulk, grn_obj_format *format)
+{
+ grn_output_null(ctx, outbuf, output_type);
+}
+
+static inline void
+grn_output_bulk(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ grn_obj *bulk, grn_obj_format *format)
+{
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ switch (bulk->header.domain) {
+ case GRN_DB_VOID :
+ grn_output_bulk_void(ctx, outbuf, output_type, GRN_BULK_HEAD(bulk), GRN_BULK_VSIZE(bulk));
+ break;
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ grn_output_str(ctx, outbuf, output_type, GRN_BULK_HEAD(bulk), GRN_BULK_VSIZE(bulk));
+ break;
+ case GRN_DB_BOOL :
+ grn_output_bool(ctx, outbuf, output_type,
+ GRN_BULK_VSIZE(bulk) ? GRN_UINT8_VALUE(bulk) : 0);
+ break;
+ case GRN_DB_INT8 :
+ grn_output_int32(ctx, outbuf, output_type,
+ GRN_BULK_VSIZE(bulk) ? GRN_INT8_VALUE(bulk) : 0);
+ break;
+ case GRN_DB_UINT8 :
+ grn_output_int32(ctx, outbuf, output_type,
+ GRN_BULK_VSIZE(bulk) ? GRN_UINT8_VALUE(bulk) : 0);
+ break;
+ case GRN_DB_INT16 :
+ grn_output_int32(ctx, outbuf, output_type,
+ GRN_BULK_VSIZE(bulk) ? GRN_INT16_VALUE(bulk) : 0);
+ break;
+ case GRN_DB_UINT16 :
+ grn_output_int32(ctx, outbuf, output_type,
+ GRN_BULK_VSIZE(bulk) ? GRN_UINT16_VALUE(bulk) : 0);
+ break;
+ case GRN_DB_INT32 :
+ grn_output_int32(ctx, outbuf, output_type,
+ GRN_BULK_VSIZE(bulk) ? GRN_INT32_VALUE(bulk) : 0);
+ break;
+ case GRN_DB_UINT32 :
+ grn_output_int64(ctx, outbuf, output_type,
+ GRN_BULK_VSIZE(bulk) ? GRN_UINT32_VALUE(bulk) : 0);
+ break;
+ case GRN_DB_INT64 :
+ grn_output_int64(ctx, outbuf, output_type,
+ GRN_BULK_VSIZE(bulk) ? GRN_INT64_VALUE(bulk) : 0);
+ break;
+ case GRN_DB_UINT64 :
+ grn_output_uint64(ctx, outbuf, output_type,
+ GRN_BULK_VSIZE(bulk) ? GRN_UINT64_VALUE(bulk) : 0);
+ break;
+ case GRN_DB_FLOAT :
+ grn_output_float(ctx, outbuf, output_type,
+ GRN_BULK_VSIZE(bulk) ? GRN_FLOAT_VALUE(bulk) : 0);
+ break;
+ case GRN_DB_TIME :
+ grn_output_time(ctx, outbuf, output_type,
+ GRN_BULK_VSIZE(bulk) ? GRN_INT64_VALUE(bulk) : 0);
+ break;
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ grn_output_geo_point(ctx, outbuf, output_type,
+ GRN_BULK_VSIZE(bulk) ? (grn_geo_point *)GRN_BULK_HEAD(bulk) : NULL);
+ break;
+ default :
+ if (format) {
+ int j;
+ int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
+ grn_id id = GRN_RECORD_VALUE(bulk);
+ grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
+ if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
+ grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", ncolumns);
+ for (j = 0; j < ncolumns; j++) {
+ grn_id range_id;
+ grn_output_array_open(ctx, outbuf, output_type, "COLUMN", 2);
+ GRN_BULK_REWIND(&buf);
+ grn_column_name_(ctx, columns[j], &buf);
+ grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
+ /* column range */
+ range_id = grn_obj_get_range(ctx, columns[j]);
+ if (range_id == GRN_ID_NIL) {
+ GRN_TEXT_PUTS(ctx, outbuf, "null");
+ } else {
+ int name_len;
+ grn_obj *range_obj;
+ char name_buf[GRN_TABLE_MAX_KEY_SIZE];
+
+ range_obj = grn_ctx_at(ctx, range_id);
+ name_len = grn_obj_name(ctx, range_obj, name_buf,
+ GRN_TABLE_MAX_KEY_SIZE);
+ GRN_BULK_REWIND(&buf);
+ GRN_TEXT_PUT(ctx, &buf, name_buf, name_len);
+ grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
+ }
+ grn_output_array_close(ctx, outbuf, output_type);
+ }
+ grn_output_array_close(ctx, outbuf, output_type);
+ }
+ grn_output_array_open(ctx, outbuf, output_type, "HIT", ncolumns);
+ for (j = 0; j < ncolumns; j++) {
+ grn_text_atoj(ctx, outbuf, output_type, columns[j], id);
+ }
+ grn_output_array_close(ctx, outbuf, output_type);
+ } else {
+ grn_obj *table = grn_ctx_at(ctx, bulk->header.domain);
+ grn_id id = GRN_RECORD_VALUE(bulk);
+ if (table && table->header.type != GRN_TABLE_NO_KEY) {
+ grn_obj *accessor = grn_obj_column(ctx, table,
+ GRN_COLUMN_NAME_KEY,
+ GRN_COLUMN_NAME_KEY_LEN);
+ if (accessor) {
+ if (id == GRN_ID_NIL) {
+ grn_obj_reinit_for(ctx, &buf, accessor);
+ } else {
+ grn_obj_get_value(ctx, accessor, id, &buf);
+ }
+ grn_obj_unlink(ctx, accessor);
+ }
+ grn_output_obj(ctx, outbuf, output_type, &buf, format);
+ } else {
+ grn_output_int64(ctx, outbuf, output_type, id);
+ }
+ }
+ break;
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+}
+
+static void
+grn_output_uvector_result_set(grn_ctx *ctx,
+ grn_obj *outbuf,
+ grn_content_type output_type,
+ grn_obj *uvector,
+ grn_obj_format *format)
+{
+ unsigned int i_hit, n_hits;
+ unsigned int i_column, n_columns;
+ unsigned int n_elements;
+ grn_obj **columns;
+ grn_obj buf;
+ grn_bool with_column_names = GRN_FALSE;
+
+ n_hits = grn_vector_size(ctx, uvector);
+
+ n_columns = GRN_BULK_VSIZE(&format->columns) / sizeof(grn_obj *);
+ columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
+
+ GRN_TEXT_INIT(&buf, 0);
+
+ if (n_hits > 0 && format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
+ with_column_names = GRN_TRUE;
+ }
+
+ n_elements = 1; /* for NHITS */
+ if (with_column_names) {
+ n_elements += 1; /* for COLUMNS */
+ }
+ n_elements += n_hits; /* for HITS */
+ grn_output_array_open(ctx, outbuf, output_type, "RESULTSET", n_elements);
+
+ grn_output_array_open(ctx, outbuf, output_type, "NHITS", 1);
+ grn_text_itoa(ctx, outbuf, n_hits);
+ grn_output_array_close(ctx, outbuf, output_type);
+
+ if (with_column_names) {
+ grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", n_columns);
+ for (i_column = 0; i_column < n_columns; i_column++) {
+ grn_id range_id;
+ grn_output_array_open(ctx, outbuf, output_type, "COLUMN", 2);
+
+ /* name */
+ GRN_BULK_REWIND(&buf);
+ grn_column_name_(ctx, columns[i_column], &buf);
+ grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
+
+ /* type */
+ range_id = grn_obj_get_range(ctx, columns[i_column]);
+ if (range_id == GRN_ID_NIL) {
+ GRN_TEXT_PUTS(ctx, outbuf, "null");
+ } else {
+ int name_len;
+ grn_obj *range_obj;
+ char name_buf[GRN_TABLE_MAX_KEY_SIZE];
+
+ range_obj = grn_ctx_at(ctx, range_id);
+ name_len = grn_obj_name(ctx, range_obj, name_buf,
+ GRN_TABLE_MAX_KEY_SIZE);
+ GRN_BULK_REWIND(&buf);
+ GRN_TEXT_PUT(ctx, &buf, name_buf, name_len);
+ grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
+ }
+
+ grn_output_array_close(ctx, outbuf, output_type);
+ }
+ grn_output_array_close(ctx, outbuf, output_type);
+ }
+
+ for (i_hit = 0; i_hit < n_hits++; i_hit++) {
+ grn_id id;
+
+ id = grn_uvector_get_element(ctx, uvector, i_hit, NULL);
+ grn_output_array_open(ctx, outbuf, output_type, "HITS", n_columns);
+ for (i_column = 0; i_column < n_columns; i_column++) {
+ GRN_BULK_REWIND(&buf);
+ grn_obj_get_value(ctx, columns[i_column], id, &buf);
+ grn_output_obj(ctx, outbuf, output_type, &buf, NULL);
+ }
+ grn_output_array_close(ctx, outbuf, output_type);
+ }
+
+ grn_output_array_close(ctx, outbuf, output_type);
+
+ GRN_OBJ_FIN(ctx, &buf);
+}
+
+static inline void
+grn_output_uvector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ grn_obj *uvector, grn_obj_format *format)
+{
+ grn_bool output_result_set = GRN_FALSE;
+ grn_bool with_weight = GRN_FALSE;
+ grn_obj *range;
+ grn_bool range_is_type;
+
+ if (format) {
+ if (GRN_BULK_VSIZE(&(format->columns)) > 0) {
+ output_result_set = GRN_TRUE;
+ }
+ if (format->flags & GRN_OBJ_FORMAT_WITH_WEIGHT) {
+ with_weight = GRN_TRUE;
+ }
+ }
+
+ if (output_result_set) {
+ grn_output_uvector_result_set(ctx, outbuf, output_type, uvector, format);
+ return;
+ }
+
+ range = grn_ctx_at(ctx, uvector->header.domain);
+ range_is_type = (range->header.type == GRN_TYPE);
+ if (range_is_type) {
+ unsigned int i, n;
+ char *raw_elements;
+ unsigned int element_size;
+ grn_obj element;
+
+ raw_elements = GRN_BULK_HEAD(uvector);
+ element_size = GRN_TYPE_SIZE(DB_OBJ(range));
+ n = GRN_BULK_VSIZE(uvector) / element_size;
+
+ grn_output_array_open(ctx, outbuf, output_type, "VECTOR", n);
+ GRN_OBJ_INIT(&element, GRN_BULK, 0, uvector->header.domain);
+ for (i = 0; i < n; i++) {
+ GRN_BULK_REWIND(&element);
+ grn_bulk_write_from(ctx, &element, raw_elements + (element_size * i),
+ 0, element_size);
+ grn_output_obj(ctx, outbuf, output_type, &element, NULL);
+ }
+ GRN_OBJ_FIN(ctx, &element);
+ grn_output_array_close(ctx, outbuf, output_type);
+ } else {
+ unsigned int i, n;
+ grn_obj id_value;
+ grn_obj key_value;
+
+ GRN_UINT32_INIT(&id_value, 0);
+ GRN_OBJ_INIT(&key_value, GRN_BULK, 0, range->header.domain);
+
+ n = grn_vector_size(ctx, uvector);
+ if (with_weight) {
+ grn_output_map_open(ctx, outbuf, output_type, "WEIGHT_VECTOR", n);
+ } else {
+ grn_output_array_open(ctx, outbuf, output_type, "VECTOR", n);
+ }
+
+ for (i = 0; i < n; i++) {
+ grn_id id;
+ unsigned int weight;
+
+ id = grn_uvector_get_element(ctx, uvector, i, &weight);
+ if (range->header.type == GRN_TABLE_NO_KEY) {
+ GRN_UINT32_SET(ctx, &id_value, id);
+ grn_output_obj(ctx, outbuf, output_type, &id_value, NULL);
+ } else {
+ GRN_BULK_REWIND(&key_value);
+ grn_table_get_key2(ctx, range, id, &key_value);
+ grn_output_obj(ctx, outbuf, output_type, &key_value, NULL);
+ }
+
+ if (with_weight) {
+ grn_output_uint64(ctx, outbuf, output_type, weight);
+ }
+ }
+
+ if (with_weight) {
+ grn_output_map_close(ctx, outbuf, output_type);
+ } else {
+ grn_output_array_close(ctx, outbuf, output_type);
+ }
+
+ GRN_OBJ_FIN(ctx, &id_value);
+ GRN_OBJ_FIN(ctx, &key_value);
+ }
+ grn_obj_unlink(ctx, range);
+}
+
+static inline void
+grn_output_vector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ grn_obj *vector, grn_obj_format *format)
+{
+ grn_bool with_weight = GRN_FALSE;
+
+ if (vector->header.domain == GRN_DB_VOID) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid obj->header.domain");
+ return;
+ }
+
+ if (format) {
+ if (format->flags & GRN_OBJ_FORMAT_WITH_WEIGHT) {
+ with_weight = GRN_TRUE;
+ }
+ }
+
+ if (with_weight) {
+ unsigned int i, n;
+ grn_obj value;
+
+ GRN_VOID_INIT(&value);
+ n = grn_vector_size(ctx, vector);
+ grn_output_map_open(ctx, outbuf, output_type, "WEIGHT_VECTOR", n);
+ for (i = 0; i < n; i++) {
+ const char *_value;
+ unsigned int weight, length;
+ grn_id domain;
+
+ length = grn_vector_get_element(ctx, vector, i,
+ &_value, &weight, &domain);
+ if (domain != GRN_DB_VOID) {
+ grn_obj_reinit(ctx, &value, domain, 0);
+ } else {
+ grn_obj_reinit(ctx, &value, vector->header.domain, 0);
+ }
+ grn_bulk_write(ctx, &value, _value, length);
+ grn_output_obj(ctx, outbuf, output_type, &value, NULL);
+ grn_output_uint64(ctx, outbuf, output_type, weight);
+ }
+ grn_output_map_close(ctx, outbuf, output_type);
+ GRN_OBJ_FIN(ctx, &value);
+ } else {
+ unsigned int i, n;
+ grn_obj value;
+ GRN_VOID_INIT(&value);
+ n = grn_vector_size(ctx, vector);
+ grn_output_array_open(ctx, outbuf, output_type, "VECTOR", n);
+ for (i = 0; i < n; i++) {
+ const char *_value;
+ unsigned int weight, length;
+ grn_id domain;
+
+ length = grn_vector_get_element(ctx, vector, i,
+ &_value, &weight, &domain);
+ if (domain != GRN_DB_VOID) {
+ grn_obj_reinit(ctx, &value, domain, 0);
+ } else {
+ grn_obj_reinit(ctx, &value, vector->header.domain, 0);
+ }
+ grn_bulk_write(ctx, &value, _value, length);
+ grn_output_obj(ctx, outbuf, output_type, &value, NULL);
+ }
+ grn_output_array_close(ctx, outbuf, output_type);
+ GRN_OBJ_FIN(ctx, &value);
+ }
+}
+
+static inline void
+grn_output_pvector(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ grn_obj *pvector, grn_obj_format *format)
+{
+ if (format) {
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED,
+ "cannot print GRN_PVECTOR using grn_obj_format");
+ } else {
+ unsigned int i, n;
+ grn_output_array_open(ctx, outbuf, output_type, "VECTOR", -1);
+ n = GRN_BULK_VSIZE(pvector) / sizeof(grn_obj *);
+ for (i = 0; i < n; i++) {
+ grn_obj *value;
+
+ value = GRN_PTR_VALUE_AT(pvector, i);
+ grn_output_obj(ctx, outbuf, output_type, value, NULL);
+ }
+ grn_output_array_close(ctx, outbuf, output_type);
+ }
+}
+
+static inline void
+grn_output_table_header(grn_ctx *ctx, grn_obj *outbuf,
+ grn_content_type output_type,
+ grn_obj *table, grn_obj_format *format)
+{
+ grn_output_array_open(ctx, outbuf, output_type, "NHITS", 1);
+ if (output_type == GRN_CONTENT_XML) {
+ grn_text_itoa(ctx, outbuf, format->nhits);
+ } else {
+ grn_output_int32(ctx, outbuf, output_type, format->nhits);
+ }
+ grn_output_array_close(ctx, outbuf, output_type);
+}
+
+static inline int
+count_n_elements_in_expression(grn_ctx *ctx, grn_obj *expression)
+{
+ int n_elements = 0;
+ grn_bool is_first_comma = GRN_TRUE;
+ grn_expr *expr = (grn_expr *)expression;
+ grn_expr_code *code;
+ grn_expr_code *code_end = expr->codes + expr->codes_curr;
+
+ for (code = expr->codes; code < code_end; code++) {
+ if (code->op == GRN_OP_COMMA) {
+ n_elements++;
+ if (is_first_comma) {
+ n_elements++;
+ is_first_comma = GRN_FALSE;
+ }
+ }
+ }
+
+ return n_elements;
+}
+
+static inline int
+count_used_n_codes(grn_ctx *ctx, grn_expr_code *start, grn_expr_code *target)
+{
+ int n_codes;
+ int i, n_args;
+ grn_bool have_proc_push_code = GRN_FALSE;
+ grn_expr_code *sub_code;
+
+ if (start == target) {
+ return 0;
+ }
+
+ n_args = target->nargs;
+ if (target->op == GRN_OP_CALL) {
+ if (!target->value) {
+ have_proc_push_code = GRN_TRUE;
+ }
+ } else {
+ if (target->value) {
+ n_args--;
+ if (n_args == 0) {
+ return 1;
+ }
+ }
+ }
+
+ n_codes = 1;
+ sub_code = target - 1;
+ for (i = 0; i < n_args; i++) {
+ int sub_n_codes;
+ sub_n_codes = count_used_n_codes(ctx, start, sub_code);
+ n_codes += sub_n_codes;
+ sub_code -= sub_n_codes;
+ if (sub_code < start) {
+ /* TODO: report error */
+ return 0;
+ }
+ }
+
+ if (have_proc_push_code) {
+ n_codes++;
+ sub_code--;
+ if (sub_code < start) {
+ /* TODO: report error */
+ return 0;
+ }
+ }
+
+ return n_codes;
+}
+
+static inline void
+grn_output_table_column(grn_ctx *ctx, grn_obj *outbuf,
+ grn_content_type output_type,
+ grn_obj *column, grn_obj *buf)
+{
+ grn_output_array_open(ctx, outbuf, output_type, "COLUMN", 2);
+ if (column) {
+ grn_id range_id;
+ GRN_BULK_REWIND(buf);
+ grn_column_name_(ctx, column, buf);
+ grn_output_obj(ctx, outbuf, output_type, buf, NULL);
+ range_id = grn_obj_get_range(ctx, column);
+ if (range_id == GRN_ID_NIL) {
+ grn_output_cstr(ctx, outbuf, output_type, "null");
+ } else {
+ int name_len;
+ grn_obj *range_obj;
+ char name_buf[GRN_TABLE_MAX_KEY_SIZE];
+
+ range_obj = grn_ctx_at(ctx, range_id);
+ name_len = grn_obj_name(ctx, range_obj, name_buf,
+ GRN_TABLE_MAX_KEY_SIZE);
+ GRN_BULK_REWIND(buf);
+ GRN_TEXT_PUT(ctx, buf, name_buf, name_len);
+ grn_output_obj(ctx, outbuf, output_type, buf, NULL);
+ }
+ } else {
+ grn_output_cstr(ctx, outbuf, output_type, "");
+ grn_output_cstr(ctx, outbuf, output_type, "");
+ }
+ grn_output_array_close(ctx, outbuf, output_type);
+}
+
+static inline void
+grn_output_table_columns_by_expression(grn_ctx *ctx, grn_obj *outbuf,
+ grn_content_type output_type,
+ grn_obj *table, grn_obj_format *format,
+ grn_obj *buf)
+{
+ int n_elements;
+ int previous_comma_offset = -1;
+ grn_bool is_first_comma = GRN_TRUE;
+ grn_bool have_comma = GRN_FALSE;
+ grn_expr *expr = (grn_expr *)format->expression;
+ grn_expr_code *code;
+ grn_expr_code *code_end = expr->codes + expr->codes_curr;
+
+ n_elements = count_n_elements_in_expression(ctx, format->expression);
+
+ grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", n_elements);
+ for (code = expr->codes; code < code_end; code++) {
+ int code_start_offset;
+
+ if (code->op != GRN_OP_COMMA) {
+ continue;
+ }
+
+ have_comma = GRN_TRUE;
+ code_start_offset = previous_comma_offset + 1;
+ if (is_first_comma) {
+ int code_end_offset;
+ int n_used_code;
+
+ grn_output_table_column(ctx, outbuf, output_type,
+ expr->codes[0].value, buf);
+
+ code_end_offset = code - expr->codes - code_start_offset - 1;
+ n_used_code = count_used_n_codes(ctx,
+ expr->codes,
+ expr->codes + code_end_offset);
+ code_start_offset = code_end_offset - n_used_code + 1;
+ is_first_comma = GRN_FALSE;
+ }
+
+ grn_output_table_column(ctx, outbuf, output_type,
+ expr->codes[code_start_offset].value, buf);
+ previous_comma_offset = code - expr->codes;
+ }
+
+ if (!have_comma && expr->codes_curr > 0) {
+ grn_output_table_column(ctx, outbuf, output_type,
+ expr->codes[0].value, buf);
+ }
+
+ grn_output_array_close(ctx, outbuf, output_type);
+}
+
+static inline void
+grn_output_table_columns_by_columns(grn_ctx *ctx, grn_obj *outbuf,
+ grn_content_type output_type,
+ grn_obj *table, grn_obj_format *format,
+ grn_obj *buf)
+{
+ int i;
+ int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
+ grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
+
+ grn_output_array_open(ctx, outbuf, output_type, "COLUMNS", ncolumns);
+ for (i = 0; i < ncolumns; i++) {
+ grn_output_table_column(ctx, outbuf, output_type, columns[i], buf);
+ }
+ grn_output_array_close(ctx, outbuf, output_type);
+}
+
+static inline void
+grn_output_table_columns(grn_ctx *ctx, grn_obj *outbuf,
+ grn_content_type output_type,
+ grn_obj *table, grn_obj_format *format)
+{
+ grn_obj buf;
+
+ GRN_TEXT_INIT(&buf, 0);
+ if (format->expression) {
+ grn_output_table_columns_by_expression(ctx, outbuf, output_type,
+ table, format, &buf);
+ } else {
+ grn_output_table_columns_by_columns(ctx, outbuf, output_type,
+ table, format, &buf);
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+}
+
+static inline void
+grn_output_table_record_by_expression(grn_ctx *ctx, grn_obj *outbuf,
+ grn_content_type output_type,
+ grn_obj *expression)
+{
+ grn_obj *result;
+ result = grn_expr_exec(ctx, expression, 0);
+ if (result) {
+ grn_output_obj(ctx, outbuf, output_type, result, NULL);
+ } else {
+ grn_output_cstr(ctx, outbuf, output_type, ctx->errbuf);
+ }
+}
+
+static inline void
+grn_output_table_records_by_expression(grn_ctx *ctx, grn_obj *outbuf,
+ grn_content_type output_type,
+ grn_table_cursor *tc,
+ grn_obj_format *format)
+{
+ int n_elements = 0;
+ grn_id id;
+ grn_obj *record;
+ grn_expr *expr = (grn_expr *)format->expression;
+ grn_expr_code *code;
+ grn_expr_code *code_end = expr->codes + expr->codes_curr;
+
+ n_elements = count_n_elements_in_expression(ctx, format->expression);
+ record = grn_expr_get_var_by_offset(ctx, format->expression, 0);
+ while ((id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) {
+ int previous_comma_offset = -1;
+ grn_bool is_first_comma = GRN_TRUE;
+ grn_bool have_comma = GRN_FALSE;
+ GRN_RECORD_SET(ctx, record, id);
+ grn_output_array_open(ctx, outbuf, output_type, "HIT", n_elements);
+ for (code = expr->codes; code < code_end; code++) {
+ if (code->op == GRN_OP_COMMA) {
+ int code_start_offset = previous_comma_offset + 1;
+ int code_end_offset;
+ int original_codes_curr = expr->codes_curr;
+
+ have_comma = GRN_TRUE;
+ if (is_first_comma) {
+ int second_code_offset;
+ int second_code_n_used_code;
+ second_code_offset = code - expr->codes - 1;
+ second_code_n_used_code =
+ count_used_n_codes(ctx,
+ expr->codes,
+ expr->codes + second_code_offset);
+ expr->codes_curr = second_code_offset - second_code_n_used_code + 1;
+ grn_output_table_record_by_expression(ctx, outbuf, output_type,
+ format->expression);
+ code_start_offset = expr->codes_curr;
+ is_first_comma = GRN_FALSE;
+ }
+ code_end_offset = code - expr->codes - code_start_offset;
+ expr->codes += code_start_offset;
+ expr->codes_curr = code_end_offset;
+ grn_output_table_record_by_expression(ctx, outbuf, output_type,
+ format->expression);
+ expr->codes -= code_start_offset;
+ expr->codes_curr = original_codes_curr;
+ previous_comma_offset = code - expr->codes;
+ }
+ }
+
+ if (!have_comma && expr->codes_curr > 0) {
+ grn_output_table_record_by_expression(ctx, outbuf, output_type,
+ format->expression);
+ }
+
+ grn_output_array_close(ctx, outbuf, output_type);
+ }
+}
+
+static inline void
+grn_output_table_records_by_columns(grn_ctx *ctx, grn_obj *outbuf,
+ grn_content_type output_type,
+ grn_table_cursor *tc,
+ grn_obj_format *format)
+{
+ int i;
+ grn_id id;
+ int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
+ grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
+ while ((id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) {
+ grn_output_array_open(ctx, outbuf, output_type, "HIT", ncolumns);
+ for (i = 0; i < ncolumns; i++) {
+ grn_text_atoj(ctx, outbuf, output_type, columns[i], id);
+ }
+ grn_output_array_close(ctx, outbuf, output_type);
+ }
+}
+
+static inline void
+grn_output_table_records(grn_ctx *ctx, grn_obj *outbuf,
+ grn_content_type output_type,
+ grn_obj *table, grn_obj_format *format)
+{
+ grn_table_cursor *tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0,
+ format->offset, format->limit,
+ GRN_CURSOR_ASCENDING);
+ if (tc) {
+ if (format->expression) {
+ grn_output_table_records_by_expression(ctx, outbuf, output_type,
+ tc, format);
+ } else {
+ grn_output_table_records_by_columns(ctx, outbuf, output_type,
+ tc, format);
+ }
+ grn_table_cursor_close(ctx, tc);
+ } else {
+ ERRCLR(ctx);
+ }
+}
+
+static inline void
+grn_output_table(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ grn_obj *table, grn_obj_format *format)
+{
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ if (format) {
+ int resultset_size = 1;
+ /* resultset: [NHITS, (COLUMNS), (HITS)] */
+ if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
+ resultset_size++;
+ }
+ resultset_size += format->limit;
+ grn_output_array_open(ctx, outbuf, output_type, "RESULTSET", resultset_size);
+ grn_output_table_header(ctx, outbuf, output_type, table, format);
+ if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
+ grn_output_table_columns(ctx, outbuf, output_type, table, format);
+ }
+ grn_output_table_records(ctx, outbuf, output_type, table, format);
+ grn_output_array_close(ctx, outbuf, output_type);
+ } else {
+ int i;
+ grn_obj *column = grn_obj_column(ctx, table,
+ GRN_COLUMN_NAME_KEY,
+ GRN_COLUMN_NAME_KEY_LEN);
+ grn_table_cursor *tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0,
+ 0, -1, GRN_CURSOR_ASCENDING);
+ grn_output_array_open(ctx, outbuf, output_type, "HIT", -1);
+ if (tc) {
+ grn_id id;
+ for (i = 0; (id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL; i++) {
+ GRN_BULK_REWIND(&buf);
+ grn_obj_get_value(ctx, column, id, &buf);
+ grn_text_esc(ctx, outbuf, GRN_BULK_HEAD(&buf), GRN_BULK_VSIZE(&buf));
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ grn_output_array_close(ctx, outbuf, output_type);
+ grn_obj_unlink(ctx, column);
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+}
+
+void
+grn_output_obj(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ grn_obj *obj, grn_obj_format *format)
+{
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ switch (obj->header.type) {
+ case GRN_VOID :
+ grn_output_void(ctx, outbuf, output_type, obj, format);
+ break;
+ case GRN_BULK :
+ grn_output_bulk(ctx, outbuf, output_type, obj, format);
+ break;
+ case GRN_UVECTOR :
+ grn_output_uvector(ctx, outbuf, output_type, obj, format);
+ break;
+ case GRN_VECTOR :
+ grn_output_vector(ctx, outbuf, output_type, obj, format);
+ break;
+ case GRN_PVECTOR :
+ grn_output_pvector(ctx, outbuf, output_type, obj, format);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ grn_output_table(ctx, outbuf, output_type, obj, format);
+ break;
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+}
+
+typedef enum {
+ XML_START,
+ XML_START_ELEMENT,
+ XML_END_ELEMENT,
+ XML_TEXT
+} xml_status;
+
+typedef enum {
+ XML_PLACE_NONE,
+ XML_PLACE_COLUMN,
+ XML_PLACE_HIT
+} xml_place;
+
+static char *
+transform_xml_next_column(grn_obj *columns, int n)
+{
+ char *column = GRN_TEXT_VALUE(columns);
+ while (n--) {
+ while (*column) {
+ column++;
+ }
+ column++;
+ }
+ return column;
+}
+
+static void
+transform_xml(grn_ctx *ctx, grn_obj *output, grn_obj *transformed)
+{
+ char *s, *e;
+ xml_status status = XML_START;
+ xml_place place = XML_PLACE_NONE;
+ grn_obj buf, name, columns, *expr;
+ unsigned int len;
+ int offset = 0, limit = 0, record_n = 0;
+ int column_n = 0, column_text_n = 0, result_set_n = -1;
+ grn_bool in_vector = GRN_FALSE;
+ unsigned int vector_element_n = 0;
+ grn_bool in_weight_vector = GRN_FALSE;
+ unsigned int weight_vector_item_n = 0;
+
+ s = GRN_TEXT_VALUE(output);
+ e = GRN_BULK_CURR(output);
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_INIT(&name, 0);
+ GRN_TEXT_INIT(&columns, 0);
+
+ expr = ctx->impl->curr_expr;
+
+#define EQUAL_NAME_P(_name) \
+ (GRN_TEXT_LEN(&name) == strlen(_name) && \
+ !memcmp(GRN_TEXT_VALUE(&name), _name, strlen(_name)))
+
+ while (s < e) {
+ switch (*s) {
+ case '<' :
+ s++;
+ switch (*s) {
+ case '/' :
+ status = XML_END_ELEMENT;
+ s++;
+ break;
+ default :
+ status = XML_START_ELEMENT;
+ break;
+ }
+ GRN_BULK_REWIND(&name);
+ break;
+ case '>' :
+ switch (status) {
+ case XML_START_ELEMENT :
+ if (EQUAL_NAME_P("COLUMN")) {
+ place = XML_PLACE_COLUMN;
+ column_text_n = 0;
+ } else if (EQUAL_NAME_P("HIT")) {
+ place = XML_PLACE_HIT;
+ column_n = 0;
+ if (result_set_n == 0) {
+ GRN_TEXT_PUTS(ctx, transformed, "<HIT NO=\"");
+ grn_text_itoa(ctx, transformed, record_n++);
+ GRN_TEXT_PUTS(ctx, transformed, "\">\n");
+ } else {
+ GRN_TEXT_PUTS(ctx, transformed, "<NAVIGATIONELEMENT ");
+ }
+ } else if (EQUAL_NAME_P("RESULTSET")) {
+ GRN_BULK_REWIND(&columns);
+ result_set_n++;
+ if (result_set_n == 0) {
+ } else {
+ GRN_TEXT_PUTS(ctx, transformed, "<NAVIGATIONENTRY>\n");
+ }
+ } else if (EQUAL_NAME_P("VECTOR")) {
+ char *c = transform_xml_next_column(&columns, column_n++);
+ in_vector = GRN_TRUE;
+ vector_element_n = 0;
+ GRN_TEXT_PUTS(ctx, transformed, "<FIELD NAME=\"");
+ GRN_TEXT_PUTS(ctx, transformed, c);
+ GRN_TEXT_PUTS(ctx, transformed, "\">");
+ } else if (EQUAL_NAME_P("WEIGHT_VECTOR")) {
+ char *c = transform_xml_next_column(&columns, column_n++);
+ in_weight_vector = GRN_TRUE;
+ weight_vector_item_n = 0;
+ GRN_TEXT_PUTS(ctx, transformed, "<FIELD NAME=\"");
+ GRN_TEXT_PUTS(ctx, transformed, c);
+ GRN_TEXT_PUTS(ctx, transformed, "\">");
+ }
+ break;
+ case XML_END_ELEMENT :
+ if (EQUAL_NAME_P("HIT")) {
+ place = XML_PLACE_NONE;
+ if (result_set_n == 0) {
+ GRN_TEXT_PUTS(ctx, transformed, "</HIT>\n");
+ } else {
+ GRN_TEXT_PUTS(ctx, transformed, "/>\n");
+ }
+ } else if (EQUAL_NAME_P("RESULTSET")) {
+ place = XML_PLACE_NONE;
+ if (result_set_n == 0) {
+ GRN_TEXT_PUTS(ctx, transformed, "</RESULTSET>\n");
+ } else {
+ GRN_TEXT_PUTS(ctx, transformed,
+ "</NAVIGATIONELEMENTS>\n"
+ "</NAVIGATIONENTRY>\n");
+ }
+ } else if (EQUAL_NAME_P("RESULT")) {
+ GRN_TEXT_PUTS(ctx, transformed,
+ "</RESULTPAGE>\n"
+ "</SEGMENT>\n"
+ "</SEGMENTS>\n");
+ } else if (EQUAL_NAME_P("VECTOR")) {
+ in_vector = GRN_FALSE;
+ GRN_TEXT_PUTS(ctx, transformed, "</FIELD>\n");
+ } else if (EQUAL_NAME_P("WEIGHT_VECTOR")) {
+ in_weight_vector = GRN_FALSE;
+ GRN_TEXT_PUTS(ctx, transformed, "</FIELD>\n");
+ } else {
+ switch (place) {
+ case XML_PLACE_HIT :
+ if (result_set_n == 0) {
+ if (in_vector) {
+ if (vector_element_n > 0) {
+ GRN_TEXT_PUTS(ctx, transformed, ", ");
+ }
+ GRN_TEXT_PUT(ctx, transformed,
+ GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ vector_element_n++;
+ } else if (in_weight_vector) {
+ grn_bool is_key;
+ is_key = ((weight_vector_item_n % 2) == 0);
+ if (is_key) {
+ unsigned int weight_vector_key_n;
+ weight_vector_key_n = weight_vector_item_n / 2;
+ if (weight_vector_key_n > 0) {
+ GRN_TEXT_PUTS(ctx, transformed, ", ");
+ }
+ } else {
+ GRN_TEXT_PUTS(ctx, transformed, ":");
+ }
+ GRN_TEXT_PUT(ctx, transformed,
+ GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ weight_vector_item_n++;
+ } else {
+ char *c = transform_xml_next_column(&columns, column_n++);
+ GRN_TEXT_PUTS(ctx, transformed, "<FIELD NAME=\"");
+ GRN_TEXT_PUTS(ctx, transformed, c);
+ GRN_TEXT_PUTS(ctx, transformed, "\">");
+ GRN_TEXT_PUT(ctx, transformed,
+ GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ GRN_TEXT_PUTS(ctx, transformed, "</FIELD>\n");
+ }
+ } else {
+ char *c = transform_xml_next_column(&columns, column_n++);
+ GRN_TEXT_PUTS(ctx, transformed, c);
+ GRN_TEXT_PUTS(ctx, transformed, "=\"");
+ GRN_TEXT_PUT(ctx, transformed,
+ GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ GRN_TEXT_PUTS(ctx, transformed, "\" ");
+ }
+ break;
+ default :
+ if (EQUAL_NAME_P("NHITS")) {
+ if (result_set_n == 0) {
+ uint32_t nhits;
+ grn_obj *offset_value, *limit_value;
+
+ nhits = grn_atoui(GRN_TEXT_VALUE(&buf), GRN_BULK_CURR(&buf),
+ NULL);
+ offset_value = grn_expr_get_var(ctx, expr,
+ "offset", strlen("offset"));
+ limit_value = grn_expr_get_var(ctx, expr,
+ "limit", strlen("limit"));
+ if (GRN_TEXT_LEN(offset_value)) {
+ offset = grn_atoi(GRN_TEXT_VALUE(offset_value),
+ GRN_BULK_CURR(offset_value),
+ NULL);
+ } else {
+ offset = 0;
+ }
+ if (GRN_TEXT_LEN(limit_value)) {
+ limit = grn_atoi(GRN_TEXT_VALUE(limit_value),
+ GRN_BULK_CURR(limit_value),
+ NULL);
+ } else {
+#define DEFAULT_LIMIT 10
+ limit = DEFAULT_LIMIT;
+#undef DEFAULT_LIMIT
+ }
+ grn_normalize_offset_and_limit(ctx, nhits, &offset, &limit);
+ record_n = offset + 1;
+ GRN_TEXT_PUTS(ctx, transformed,
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<SEGMENTS>\n"
+ "<SEGMENT>\n"
+ "<RESULTPAGE>\n"
+ "<RESULTSET OFFSET=\"");
+ grn_text_lltoa(ctx, transformed, offset);
+ GRN_TEXT_PUTS(ctx, transformed, "\" LIMIT=\"");
+ grn_text_lltoa(ctx, transformed, limit);
+ GRN_TEXT_PUTS(ctx, transformed, "\" NHITS=\"");
+ grn_text_lltoa(ctx, transformed, nhits);
+ GRN_TEXT_PUTS(ctx, transformed, "\">\n");
+ } else {
+ GRN_TEXT_PUTS(ctx, transformed,
+ "<NAVIGATIONELEMENTS COUNT=\"");
+ GRN_TEXT_PUT(ctx, transformed,
+ GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ GRN_TEXT_PUTS(ctx, transformed,
+ "\">\n");
+ }
+ } else if (EQUAL_NAME_P("TEXT")) {
+ switch (place) {
+ case XML_PLACE_COLUMN :
+ if (column_text_n == 0) {
+ GRN_TEXT_PUT(ctx, &columns,
+ GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ GRN_TEXT_PUTC(ctx, &columns, '\0');
+ }
+ column_text_n++;
+ break;
+ default :
+ break;
+ }
+ }
+ }
+ }
+ default :
+ break;
+ }
+ s++;
+ GRN_BULK_REWIND(&buf);
+ status = XML_TEXT;
+ break;
+ default :
+ len = grn_charlen(ctx, s, e);
+ switch (status) {
+ case XML_START_ELEMENT :
+ case XML_END_ELEMENT :
+ GRN_TEXT_PUT(ctx, &name, s, len);
+ break;
+ default :
+ GRN_TEXT_PUT(ctx, &buf, s, len);
+ break;
+ }
+ s += len;
+ break;
+ }
+ }
+#undef EQUAL_NAME_P
+
+ GRN_OBJ_FIN(ctx, &buf);
+ GRN_OBJ_FIN(ctx, &name);
+ GRN_OBJ_FIN(ctx, &columns);
+}
+
+#ifdef GRN_WITH_MESSAGE_PACK
+typedef struct {
+ grn_ctx *ctx;
+ grn_obj *buffer;
+} msgpack_writer_ctx;
+
+static inline int
+msgpack_buffer_writer(void* data, const char* buf, unsigned int len)
+{
+ msgpack_writer_ctx *writer_ctx = (msgpack_writer_ctx *)data;
+ return grn_bulk_write(writer_ctx->ctx, writer_ctx->buffer, buf, len);
+}
+#endif
+
+#define JSON_CALLBACK_PARAM "callback"
+
+void
+grn_output_envelope(grn_ctx *ctx,
+ grn_rc rc,
+ grn_obj *head,
+ grn_obj *body,
+ grn_obj *foot,
+ const char *file,
+ int line)
+{
+ double started, finished, elapsed;
+ grn_obj *expr = NULL;
+ grn_obj *jsonp_func = NULL;
+
+ grn_timeval tv_now;
+ grn_timeval_now(ctx, &tv_now);
+ started = ctx->impl->tv.tv_sec;
+ started += ctx->impl->tv.tv_nsec / GRN_TIME_NSEC_PER_SEC_F;
+ finished = tv_now.tv_sec;
+ finished += tv_now.tv_nsec / GRN_TIME_NSEC_PER_SEC_F;
+ elapsed = finished - started;
+
+ switch (ctx->impl->output_type) {
+ case GRN_CONTENT_JSON:
+ expr = ctx->impl->curr_expr;
+ if (expr) {
+ jsonp_func = grn_expr_get_var(ctx, expr, JSON_CALLBACK_PARAM,
+ strlen(JSON_CALLBACK_PARAM));
+ }
+ if (jsonp_func && GRN_TEXT_LEN(jsonp_func)) {
+ GRN_TEXT_PUT(ctx, head, GRN_TEXT_VALUE(jsonp_func), GRN_TEXT_LEN(jsonp_func));
+ GRN_TEXT_PUTC(ctx, head, '(');
+ }
+ GRN_TEXT_PUTS(ctx, head, "[[");
+ grn_text_itoa(ctx, head, rc);
+ GRN_TEXT_PUTC(ctx, head, ',');
+ grn_text_ftoa(ctx, head, started);
+ GRN_TEXT_PUTC(ctx, head, ',');
+ grn_text_ftoa(ctx, head, elapsed);
+ if (rc != GRN_SUCCESS) {
+ GRN_TEXT_PUTC(ctx, head, ',');
+ grn_text_esc(ctx, head, ctx->errbuf, strlen(ctx->errbuf));
+ if (ctx->errfunc && ctx->errfile) {
+ grn_obj *command;
+ /* TODO: output backtrace */
+ GRN_TEXT_PUTS(ctx, head, ",[[");
+ grn_text_esc(ctx, head, ctx->errfunc, strlen(ctx->errfunc));
+ GRN_TEXT_PUTC(ctx, head, ',');
+ grn_text_esc(ctx, head, ctx->errfile, strlen(ctx->errfile));
+ GRN_TEXT_PUTC(ctx, head, ',');
+ grn_text_itoa(ctx, head, ctx->errline);
+ GRN_TEXT_PUTS(ctx, head, "]");
+ if (file && (command = GRN_CTX_USER_DATA(ctx)->ptr)) {
+ GRN_TEXT_PUTS(ctx, head, ",[");
+ grn_text_esc(ctx, head, file, strlen(file));
+ GRN_TEXT_PUTC(ctx, head, ',');
+ grn_text_itoa(ctx, head, line);
+ GRN_TEXT_PUTC(ctx, head, ',');
+ grn_text_esc(ctx, head, GRN_TEXT_VALUE(command), GRN_TEXT_LEN(command));
+ GRN_TEXT_PUTS(ctx, head, "]");
+ }
+ GRN_TEXT_PUTS(ctx, head, "]");
+ }
+ }
+ GRN_TEXT_PUTC(ctx, head, ']');
+ if (GRN_TEXT_LEN(body)) { GRN_TEXT_PUTC(ctx, head, ','); }
+ GRN_TEXT_PUTC(ctx, foot, ']');
+ if (jsonp_func && GRN_TEXT_LEN(jsonp_func)) {
+ GRN_TEXT_PUTS(ctx, foot, ");");
+ }
+ break;
+ case GRN_CONTENT_TSV:
+ grn_text_itoa(ctx, head, rc);
+ GRN_TEXT_PUTC(ctx, head, '\t');
+ grn_text_ftoa(ctx, head, started);
+ GRN_TEXT_PUTC(ctx, head, '\t');
+ grn_text_ftoa(ctx, head, elapsed);
+ if (rc != GRN_SUCCESS) {
+ GRN_TEXT_PUTC(ctx, head, '\t');
+ grn_text_esc(ctx, head, ctx->errbuf, strlen(ctx->errbuf));
+ if (ctx->errfunc && ctx->errfile) {
+ /* TODO: output backtrace */
+ GRN_TEXT_PUTC(ctx, head, '\t');
+ grn_text_esc(ctx, head, ctx->errfunc, strlen(ctx->errfunc));
+ GRN_TEXT_PUTC(ctx, head, '\t');
+ grn_text_esc(ctx, head, ctx->errfile, strlen(ctx->errfile));
+ GRN_TEXT_PUTC(ctx, head, '\t');
+ grn_text_itoa(ctx, head, ctx->errline);
+ }
+ }
+ GRN_TEXT_PUTS(ctx, head, "\n");
+ GRN_TEXT_PUTS(ctx, foot, "\nEND");
+ break;
+ case GRN_CONTENT_XML:
+ {
+ char buf[GRN_TABLE_MAX_KEY_SIZE];
+ int is_select = 0;
+ if (!rc && ctx->impl->curr_expr) {
+ int len = grn_obj_name(ctx, ctx->impl->curr_expr,
+ buf, GRN_TABLE_MAX_KEY_SIZE);
+ buf[len] = '\0';
+ is_select = strcmp(buf, "select") == 0;
+ }
+ if (is_select) {
+ grn_obj transformed;
+ GRN_TEXT_INIT(&transformed, 0);
+ transform_xml(ctx, body, &transformed);
+ GRN_TEXT_SET(ctx, body,
+ GRN_TEXT_VALUE(&transformed), GRN_TEXT_LEN(&transformed));
+ GRN_OBJ_FIN(ctx, &transformed);
+ } else {
+ GRN_TEXT_PUTS(ctx, head, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RESULT CODE=\"");
+ grn_text_itoa(ctx, head, rc);
+ GRN_TEXT_PUTS(ctx, head, "\" UP=\"");
+ grn_text_ftoa(ctx, head, started);
+ GRN_TEXT_PUTS(ctx, head, "\" ELAPSED=\"");
+ grn_text_ftoa(ctx, head, elapsed);
+ GRN_TEXT_PUTS(ctx, head, "\">\n");
+ if (rc != GRN_SUCCESS) {
+ GRN_TEXT_PUTS(ctx, head, "<ERROR>");
+ grn_text_escape_xml(ctx, head, ctx->errbuf, strlen(ctx->errbuf));
+ if (ctx->errfunc && ctx->errfile) {
+ /* TODO: output backtrace */
+ GRN_TEXT_PUTS(ctx, head, "<INFO FUNC=\"");
+ grn_text_escape_xml(ctx, head, ctx->errfunc, strlen(ctx->errfunc));
+ GRN_TEXT_PUTS(ctx, head, "\" FILE=\"");
+ grn_text_escape_xml(ctx, head, ctx->errfile, strlen(ctx->errfile));
+ GRN_TEXT_PUTS(ctx, head, "\" LINE=\"");
+ grn_text_itoa(ctx, head, ctx->errline);
+ GRN_TEXT_PUTS(ctx, head, "\"/>");
+ }
+ GRN_TEXT_PUTS(ctx, head, "</ERROR>");
+ }
+ GRN_TEXT_PUTS(ctx, foot, "\n</RESULT>");
+ }
+ }
+ break;
+ case GRN_CONTENT_MSGPACK:
+#ifdef GRN_WITH_MESSAGE_PACK
+ {
+ msgpack_writer_ctx head_writer_ctx;
+ msgpack_packer header_packer;
+ int header_size;
+
+ head_writer_ctx.ctx = ctx;
+ head_writer_ctx.buffer = head;
+ msgpack_packer_init(&header_packer, &head_writer_ctx, msgpack_buffer_writer);
+
+ /* [HEAD, (BODY)] */
+ if (GRN_TEXT_LEN(body) > 0) {
+ msgpack_pack_array(&header_packer, 2);
+ } else {
+ msgpack_pack_array(&header_packer, 1);
+ }
+
+ /* HEAD := [rc, started, elapsed, (error, (ERROR DETAIL))] */
+ header_size = 3;
+ if (rc != GRN_SUCCESS) {
+ header_size++;
+ if (ctx->errfunc && ctx->errfile) {
+ header_size++;
+ }
+ }
+ msgpack_pack_array(&header_packer, header_size);
+ msgpack_pack_int(&header_packer, rc);
+
+ msgpack_pack_double(&header_packer, started);
+ msgpack_pack_double(&header_packer, elapsed);
+
+ if (rc != GRN_SUCCESS) {
+ msgpack_pack_raw(&header_packer, strlen(ctx->errbuf));
+ msgpack_pack_raw_body(&header_packer, ctx->errbuf, strlen(ctx->errbuf));
+ if (ctx->errfunc && ctx->errfile) {
+ grn_obj *command = GRN_CTX_USER_DATA(ctx)->ptr;
+ int error_detail_size;
+
+ /* ERROR DETAIL := [[errfunc, errfile, errline,
+ (file, line, command)]] */
+ /* TODO: output backtrace */
+ msgpack_pack_array(&header_packer, 1);
+ error_detail_size = 3;
+ if (command) {
+ error_detail_size += 3;
+ }
+ msgpack_pack_array(&header_packer, error_detail_size);
+
+ msgpack_pack_raw(&header_packer, strlen(ctx->errfunc));
+ msgpack_pack_raw_body(&header_packer, ctx->errfunc, strlen(ctx->errfunc));
+
+ msgpack_pack_raw(&header_packer, strlen(ctx->errfile));
+ msgpack_pack_raw_body(&header_packer, ctx->errfile, strlen(ctx->errfile));
+
+ msgpack_pack_int(&header_packer, ctx->errline);
+
+ if (command) {
+ if (file) {
+ msgpack_pack_raw(&header_packer, strlen(file));
+ msgpack_pack_raw_body(&header_packer, file, strlen(file));
+ } else {
+ msgpack_pack_raw(&header_packer, 7);
+ msgpack_pack_raw_body(&header_packer, "(stdin)", 7);
+ }
+
+ msgpack_pack_int(&header_packer, line);
+
+ msgpack_pack_raw(&header_packer, GRN_TEXT_LEN(command));
+ msgpack_pack_raw_body(&header_packer, GRN_TEXT_VALUE(command), GRN_TEXT_LEN(command));
+ }
+ }
+ }
+ }
+#endif
+ break;
+ case GRN_CONTENT_NONE:
+ break;
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/lib/output.h b/storage/mroonga/vendor/groonga/lib/output.h
new file mode 100644
index 00000000000..7c8a3f7a769
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/output.h
@@ -0,0 +1,89 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2010-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_OUTPUT_H
+#define GRN_OUTPUT_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifndef GRN_CTX_H
+#include "ctx.h"
+#endif /* GRN_CTX_H */
+
+#ifndef GRN_STORE_H
+#include "store.h"
+#endif /* GRN_STORE_H */
+
+#ifndef GRN_CTX_IMPL_H
+#include "ctx_impl.h"
+#endif /* GRN_CTX_IMPL_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+GRN_API void grn_output_array_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ const char *name, int nelements);
+GRN_API void grn_output_array_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type);
+GRN_API void grn_output_map_open(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ const char *name, int nelements);
+GRN_API void grn_output_map_close(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type);
+void grn_output_int32(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ int32_t value);
+GRN_API void grn_output_int64(grn_ctx *ctx, grn_obj *outbuf,
+ grn_content_type output_type,
+ int64_t value);
+void grn_output_float(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ double value);
+GRN_API void grn_output_cstr(grn_ctx *ctx, grn_obj *outbuf, grn_content_type output_type,
+ const char *value);
+GRN_API void grn_output_str(grn_ctx *ctx, grn_obj *outbuf,
+ grn_content_type output_type,
+ const char *value, size_t value_len);
+GRN_API void grn_output_bool(grn_ctx *ctx, grn_obj *outbuf,
+ grn_content_type output_type,
+ grn_bool value);
+
+#define GRN_OUTPUT_ARRAY_OPEN(name,nelements) \
+ (grn_ctx_output_array_open(ctx, name, nelements))
+#define GRN_OUTPUT_ARRAY_CLOSE() \
+ (grn_ctx_output_array_close(ctx))
+#define GRN_OUTPUT_MAP_OPEN(name,nelements) \
+ (grn_ctx_output_map_open(ctx, name, nelements))
+#define GRN_OUTPUT_MAP_CLOSE() \
+ (grn_ctx_output_map_close(ctx))
+#define GRN_OUTPUT_INT32(value) \
+ (grn_ctx_output_int32(ctx, value))
+#define GRN_OUTPUT_INT64(value) \
+ (grn_ctx_output_int64(ctx, value))
+#define GRN_OUTPUT_FLOAT(value) \
+ (grn_ctx_output_float(ctx, value))
+#define GRN_OUTPUT_CSTR(value)\
+ (grn_ctx_output_cstr(ctx, value))
+#define GRN_OUTPUT_STR(value,value_len)\
+ (grn_ctx_output_str(ctx, value, value_len))
+#define GRN_OUTPUT_BOOL(value)\
+ (grn_ctx_output_bool(ctx, value))
+#define GRN_OUTPUT_OBJ(obj,format)\
+ (grn_ctx_output_obj(ctx, obj, format))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_OUTPUT_H */
diff --git a/storage/mroonga/vendor/groonga/lib/pat.c b/storage/mroonga/vendor/groonga/lib/pat.c
new file mode 100644
index 00000000000..4ca2ffc42e0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/pat.c
@@ -0,0 +1,2913 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "groonga_in.h"
+#include <string.h>
+#include <limits.h>
+#include "pat.h"
+#include "output.h"
+#include "util.h"
+#include "normalizer_in.h"
+
+#define GRN_PAT_DELETED (GRN_ID_MAX + 1)
+
+#define GRN_PAT_SEGMENT_SIZE 0x400000
+#define W_OF_KEY_IN_A_SEGMENT 22
+#define W_OF_PAT_IN_A_SEGMENT 18
+#define W_OF_SIS_IN_A_SEGMENT 19
+#define KEY_MASK_IN_A_SEGMENT 0x3fffff
+#define PAT_MASK_IN_A_SEGMENT 0x3ffff
+#define SIS_MASK_IN_A_SEGMENT 0x7ffff
+#define SEG_NOT_ASSIGNED 0xffff
+#define GRN_PAT_MAX_SEGMENT 0x1000
+#define GRN_PAT_MDELINFOS (GRN_PAT_NDELINFOS - 1)
+
+#define GRN_PAT_BIN_KEY 0x70000
+
+typedef struct {
+ grn_id lr[2];
+ /*
+ lr[0]: the left node.
+ lr[1]: the right node.
+
+ The left node has 0 at the nth bit at the nth byte.
+ The right node has 1 at the nth bit at the nth byte.
+ 'check' value indicate 'at the nth bit at the nth byte'.
+
+ The both available nodes has larger check value rather
+ than the current node.
+
+ The first node (PAT_AT(pat, GRN_ID_NIL, node)) has only
+ the right node and the node is the start point.
+ */
+ uint32_t key;
+ /*
+ PAT_IMD(node) == 0: key bytes offset in memory map.
+ PAT_IMD(node) == 1: the key bytes.
+ */
+ uint16_t check;
+ /*
+ nth byte: 12, nth bit: 3, terminated: 1
+
+ nth byte is different in key bytes: (check >> 4): max == 4095
+ the left most byte is the 0th byte and the right most byte is the 11th byte.
+
+ nth bit is different in nth byte: ((check >> 1) & 0b111)
+ the left most bit is the 0th bit and the right most bit is the 7th bit.
+
+ terminated: (check & 0b1)
+ terminated == 1: key is terminated.
+ */
+ uint16_t bits;
+ /* length : 13, deleting : 1, immediate : 1 */
+} pat_node;
+
+#define PAT_DELETING (1<<1)
+#define PAT_IMMEDIATE (1<<2)
+
+#define PAT_DEL(x) ((x)->bits & PAT_DELETING)
+#define PAT_IMD(x) ((x)->bits & PAT_IMMEDIATE)
+#define PAT_LEN(x) (((x)->bits >> 3) + 1)
+#define PAT_CHK(x) ((x)->check)
+#define PAT_DEL_ON(x) ((x)->bits |= PAT_DELETING)
+#define PAT_IMD_ON(x) ((x)->bits |= PAT_IMMEDIATE)
+#define PAT_DEL_OFF(x) ((x)->bits &= ~PAT_DELETING)
+#define PAT_IMD_OFF(x) ((x)->bits &= ~PAT_IMMEDIATE)
+#define PAT_LEN_SET(x,v) ((x)->bits = ((x)->bits & ((1<<3) - 1))|(((v) - 1) << 3))
+#define PAT_CHK_SET(x,v) ((x)->check = (v))
+
+typedef struct {
+ grn_id children;
+ grn_id sibling;
+} sis_node;
+
+enum {
+ segment_key = 0,
+ segment_pat = 1,
+ segment_sis = 2
+};
+
+/* bit operation */
+
+#define nth_bit(key,n,l) ((((key)[(n)>>4]) >> (7 - (((n)>>1) & 7))) & 1)
+
+/* segment operation */
+
+/* patricia array operation */
+
+#define PAT_AT(pat,id,n) do {\
+ int flags = 0;\
+ GRN_IO_ARRAY_AT(pat->io, segment_pat, id, &flags, n);\
+} while (0)
+
+inline static pat_node *
+pat_get(grn_ctx *ctx, grn_pat *pat, grn_id id)
+{
+ pat_node *res;
+ int flags = GRN_TABLE_ADD;
+ if (id > GRN_ID_MAX) { return NULL; }
+ GRN_IO_ARRAY_AT(pat->io, segment_pat, id, &flags, res);
+ return res;
+}
+
+inline static pat_node *
+pat_node_new(grn_ctx *ctx, grn_pat *pat, grn_id *id)
+{
+ uint32_t n = pat->header->curr_rec + 1;
+ pat_node *res;
+ if (n > GRN_ID_MAX) { return NULL; }
+ if ((res = pat_get(ctx, pat, n))) {
+ pat->header->curr_rec = n;
+ pat->header->n_entries++;
+ }
+ if (id) { *id = n; }
+ return res;
+}
+
+/* sis operation */
+
+inline static sis_node *
+sis_at(grn_ctx *ctx, grn_pat *pat, grn_id id)
+{
+ sis_node *res;
+ int flags = 0;
+ if (id > GRN_ID_MAX) { return NULL; }
+ GRN_IO_ARRAY_AT(pat->io, segment_sis, id, &flags, res);
+ return res;
+}
+
+inline static sis_node *
+sis_get(grn_ctx *ctx, grn_pat *pat, grn_id id)
+{
+ sis_node *res;
+ int flags = GRN_TABLE_ADD;
+ if (id > GRN_ID_MAX) { return NULL; }
+ GRN_IO_ARRAY_AT(pat->io, segment_sis, id, &flags, res);
+ return res;
+}
+
+#define MAX_LEVEL 16
+
+static void
+sis_collect(grn_ctx *ctx, grn_pat *pat, grn_hash *h, grn_id id, uint32_t level)
+{
+ uint32_t *offset;
+ sis_node *sl = sis_at(ctx, pat, id);
+ if (sl) {
+ grn_id sid = sl->children;
+ while (sid && sid != id) {
+ if (grn_hash_add(ctx, h, &sid, sizeof(grn_id), (void **) &offset, NULL)) {
+ *offset = level;
+ if (level < MAX_LEVEL) { sis_collect(ctx, pat, h, sid, level + 1); }
+ if (!(sl = sis_at(ctx, pat, sid))) { break; }
+ sid = sl->sibling;
+ } else {
+ /* todo : must be handled */
+ }
+ }
+ }
+}
+
+/* key operation */
+
+#define KEY_AT(pat,pos,ptr,addp) do {\
+ int flags = addp;\
+ GRN_IO_ARRAY_AT(pat->io, segment_key, pos, &flags, ptr);\
+} while (0)
+
+inline static uint32_t
+key_put(grn_ctx *ctx, grn_pat *pat, const uint8_t *key, int len)
+{
+ uint32_t res, ts;
+// if (len >= GRN_PAT_SEGMENT_SIZE) { return 0; /* error */ }
+ res = pat->header->curr_key;
+ ts = (res + len) >> W_OF_KEY_IN_A_SEGMENT;
+ if (res >> W_OF_KEY_IN_A_SEGMENT != ts) {
+ res = pat->header->curr_key = ts << W_OF_KEY_IN_A_SEGMENT;
+ }
+ {
+ uint8_t *dest;
+ KEY_AT(pat, res, dest, GRN_TABLE_ADD);
+ if (!dest) { return 0; }
+ memcpy(dest, key, len);
+ }
+ pat->header->curr_key += len;
+ return res;
+}
+
+inline static uint8_t *
+pat_node_get_key(grn_ctx *ctx, grn_pat *pat, pat_node *n)
+{
+ if (PAT_IMD(n)) {
+ return (uint8_t *) &n->key;
+ } else {
+ uint8_t *res;
+ KEY_AT(pat, n->key, res, 0);
+ return res;
+ }
+}
+
+inline static grn_rc
+pat_node_set_key(grn_ctx *ctx, grn_pat *pat, pat_node *n, const uint8_t *key, unsigned int len)
+{
+ if (!key || !len) { return GRN_INVALID_ARGUMENT; }
+ PAT_LEN_SET(n, len);
+ if (len <= sizeof(uint32_t)) {
+ PAT_IMD_ON(n);
+ memcpy(&n->key, key, len);
+ } else {
+ PAT_IMD_OFF(n);
+ n->key = key_put(ctx, pat, key, len);
+ }
+ return GRN_SUCCESS;
+}
+
+/* delinfo operation */
+
+enum {
+ DL_EMPTY = 0,
+ DL_PHASE1,
+ DL_PHASE2
+};
+
+inline static grn_pat_delinfo *
+delinfo_search(grn_pat *pat, grn_id id)
+{
+ int i;
+ grn_pat_delinfo *di;
+ for (i = (pat->header->curr_del2) & GRN_PAT_MDELINFOS;
+ i != pat->header->curr_del;
+ i = (i + 1) & GRN_PAT_MDELINFOS) {
+ di = &pat->header->delinfos[i];
+ if (di->stat != DL_PHASE1) { continue; }
+ if (di->ld == id) { return di; }
+ if (di->d == id) { return di; }
+ }
+ return NULL;
+}
+
+inline static grn_rc
+delinfo_turn_2(grn_ctx *ctx, grn_pat *pat, grn_pat_delinfo *di)
+{
+ grn_id d, *p = NULL;
+ pat_node *ln, *dn;
+ // grn_log("delinfo_turn_2> di->d=%d di->ld=%d stat=%d", di->d, di->ld, di->stat);
+ if (di->stat != DL_PHASE1) { return GRN_SUCCESS; }
+ PAT_AT(pat, di->ld, ln);
+ if (!ln) { return GRN_INVALID_ARGUMENT; }
+ if (!(d = di->d)) { return GRN_INVALID_ARGUMENT; }
+ PAT_AT(pat, d, dn);
+ if (!dn) { return GRN_INVALID_ARGUMENT; }
+ PAT_DEL_OFF(ln);
+ PAT_DEL_OFF(dn);
+ {
+ grn_id r, *p0;
+ pat_node *rn;
+ int c0 = -1, c;
+ uint32_t len = PAT_LEN(dn) * 16;
+ const uint8_t *key = pat_node_get_key(ctx, pat, dn);
+ if (!key) { return GRN_INVALID_ARGUMENT; }
+ PAT_AT(pat, 0, rn);
+ p0 = &rn->lr[1];
+ while ((r = *p0)) {
+ if (r == d) {
+ p = p0;
+ break;
+ }
+ PAT_AT(pat, r, rn);
+ if (!rn) { return GRN_FILE_CORRUPT; }
+ c = PAT_CHK(rn);
+ if (c <= c0 || len <= c) { break; }
+ if (c & 1) {
+ p0 = (c + 1 < len) ? &rn->lr[1] : &rn->lr[0];
+ } else {
+ p0 = &rn->lr[nth_bit((uint8_t *)key, c, len)];
+ }
+ c0 = c;
+ }
+ }
+ if (p) {
+ PAT_CHK_SET(ln, PAT_CHK(dn));
+ ln->lr[1] = dn->lr[1];
+ ln->lr[0] = dn->lr[0];
+ *p = di->ld;
+ } else {
+ /* debug */
+ int j;
+ grn_id dd;
+ grn_pat_delinfo *ddi;
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "failed to find d=%d", d);
+ for (j = (pat->header->curr_del2 + 1) & GRN_PAT_MDELINFOS;
+ j != pat->header->curr_del;
+ j = (j + 1) & GRN_PAT_MDELINFOS) {
+ ddi = &pat->header->delinfos[j];
+ if (ddi->stat != DL_PHASE1) { continue; }
+ PAT_AT(pat, ddi->ld, ln);
+ if (!ln) { continue; }
+ if (!(dd = ddi->d)) { continue; }
+ if (d == ddi->ld) {
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "found!!!, d(%d) become ld of (%d)", d, dd);
+ }
+ }
+ /* debug */
+ }
+ di->stat = DL_PHASE2;
+ di->d = d;
+ // grn_log("delinfo_turn_2< di->d=%d di->ld=%d", di->d, di->ld);
+ return GRN_SUCCESS;
+}
+
+inline static grn_rc
+delinfo_turn_3(grn_ctx *ctx, grn_pat *pat, grn_pat_delinfo *di)
+{
+ pat_node *dn;
+ uint32_t size;
+ if (di->stat != DL_PHASE2) { return GRN_SUCCESS; }
+ PAT_AT(pat, di->d, dn);
+ if (!dn) { return GRN_INVALID_ARGUMENT; }
+ if (di->shared) {
+ PAT_IMD_ON(dn);
+ size = 0;
+ } else {
+ if (PAT_IMD(dn)) {
+ size = 0;
+ } else {
+ size = PAT_LEN(dn);
+ }
+ }
+ di->stat = DL_EMPTY;
+ // dn->lr[1] = GRN_PAT_DELETED;
+ dn->lr[0] = pat->header->garbages[size];
+ pat->header->garbages[size] = di->d;
+ return GRN_SUCCESS;
+}
+
+inline static grn_pat_delinfo *
+delinfo_new(grn_ctx *ctx, grn_pat *pat)
+{
+ grn_pat_delinfo *res = &pat->header->delinfos[pat->header->curr_del];
+ uint32_t n = (pat->header->curr_del + 1) & GRN_PAT_MDELINFOS;
+ int gap = ((n + GRN_PAT_NDELINFOS - pat->header->curr_del2) & GRN_PAT_MDELINFOS)
+ - (GRN_PAT_NDELINFOS / 2);
+ while (gap-- > 0) {
+ if (delinfo_turn_2(ctx, pat, &pat->header->delinfos[pat->header->curr_del2])) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "d2 failed: %d", pat->header->delinfos[pat->header->curr_del2].ld);
+ }
+ pat->header->curr_del2 = (pat->header->curr_del2 + 1) & GRN_PAT_MDELINFOS;
+ }
+ if (n == pat->header->curr_del3) {
+ if (delinfo_turn_3(ctx, pat, &pat->header->delinfos[pat->header->curr_del3])) {
+ GRN_LOG(ctx, GRN_LOG_CRIT, "d3 failed: %d", pat->header->delinfos[pat->header->curr_del3].ld);
+ }
+ pat->header->curr_del3 = (pat->header->curr_del3 + 1) & GRN_PAT_MDELINFOS;
+ }
+ pat->header->curr_del = n;
+ return res;
+}
+
+/* pat operation */
+
+inline static grn_pat *
+_grn_pat_create(grn_ctx *ctx, grn_pat *pat,
+ const char *path, uint32_t key_size,
+ uint32_t value_size, uint32_t flags) {
+ grn_io *io;
+ pat_node *node0;
+ struct grn_pat_header *header;
+ uint32_t entry_size, w_of_element;
+ grn_encoding encoding = ctx->encoding;
+ if (flags & GRN_OBJ_KEY_WITH_SIS) {
+ entry_size = sizeof(sis_node) + value_size;
+ } else {
+ entry_size = value_size;
+ }
+ for (w_of_element = 0; (1 << w_of_element) < entry_size; w_of_element++) {
+ /* nop */
+ }
+ {
+ grn_io_array_spec array_spec[3];
+ array_spec[segment_key].w_of_element = 0;
+ array_spec[segment_key].max_n_segments = 0x400;
+ array_spec[segment_pat].w_of_element = 4;
+ array_spec[segment_pat].max_n_segments = 1 << (30 - (22 - 4));
+ array_spec[segment_sis].w_of_element = w_of_element;
+ array_spec[segment_sis].max_n_segments = 1 << (30 - (22 - w_of_element));
+ io = grn_io_create_with_array(ctx, path, sizeof(struct grn_pat_header),
+ GRN_PAT_SEGMENT_SIZE, grn_io_auto, 3, array_spec);
+ }
+ if (!io) { return NULL; }
+ if (encoding == GRN_ENC_DEFAULT) { encoding = grn_gctx.encoding; }
+ header = grn_io_header(io);
+ grn_io_set_type(io, GRN_TABLE_PAT_KEY);
+ header->flags = flags;
+ header->encoding = encoding;
+ header->key_size = key_size;
+ header->value_size = value_size;
+ header->n_entries = 0;
+ header->curr_rec = 0;
+ header->curr_key = -1;
+ header->curr_del = 0;
+ header->curr_del2 = 0;
+ header->curr_del3 = 0;
+ header->n_garbages = 0;
+ header->tokenizer = GRN_ID_NIL;
+ if (header->flags & GRN_OBJ_KEY_NORMALIZE) {
+ header->flags &= ~GRN_OBJ_KEY_NORMALIZE;
+ pat->normalizer = grn_ctx_get(ctx, GRN_NORMALIZER_AUTO_NAME, -1);
+ header->normalizer = grn_obj_id(ctx, pat->normalizer);
+ } else {
+ pat->normalizer = NULL;
+ header->normalizer = GRN_ID_NIL;
+ }
+ pat->io = io;
+ pat->header = header;
+ pat->key_size = key_size;
+ pat->value_size = value_size;
+ pat->tokenizer = NULL;
+ pat->encoding = encoding;
+ pat->obj.header.flags = header->flags;
+ if (!(node0 = pat_get(ctx, pat, 0))) {
+ grn_io_close(ctx, io);
+ return NULL;
+ }
+ node0->lr[1] = 0;
+ node0->lr[0] = 0;
+ node0->key = 0;
+ return pat;
+}
+
+grn_pat *
+grn_pat_create(grn_ctx *ctx, const char *path, uint32_t key_size,
+ uint32_t value_size, uint32_t flags)
+{
+ grn_pat *pat;
+ if (!(pat = GRN_MALLOC(sizeof(grn_pat)))) {
+ return NULL;
+ }
+ GRN_DB_OBJ_SET_TYPE(pat, GRN_TABLE_PAT_KEY);
+ if (!_grn_pat_create(ctx, pat, path, key_size, value_size, flags)) {
+ GRN_FREE(pat);
+ return NULL;
+ }
+ pat->cache = NULL;
+ pat->cache_size = 0;
+ return pat;
+}
+
+/*
+ grn_pat_cache_enable() and grn_pat_cache_disable() are not thread-safe.
+ So far, they can be used only from single threaded programs.
+ */
+
+grn_rc
+grn_pat_cache_enable(grn_ctx *ctx, grn_pat *pat, uint32_t cache_size)
+{
+ if (pat->cache || pat->cache_size) {
+ ERR(GRN_INVALID_ARGUMENT, "cache is already enabled");
+ return ctx->rc;
+ }
+ if (cache_size & (cache_size - 1)) {
+ ERR(GRN_INVALID_ARGUMENT, "cache_size(%u) must be a power of two", cache_size);
+ return ctx->rc;
+ }
+ if (!(pat->cache = GRN_CALLOC(cache_size * sizeof(grn_id)))) {
+ return ctx->rc;
+ }
+ pat->cache_size = cache_size;
+ return GRN_SUCCESS;
+}
+
+void
+grn_pat_cache_disable(grn_ctx *ctx, grn_pat *pat)
+{
+ if (pat->cache) {
+ GRN_FREE(pat->cache);
+ pat->cache_size = 0;
+ pat->cache = NULL;
+ }
+}
+
+grn_pat *
+grn_pat_open(grn_ctx *ctx, const char *path)
+{
+ grn_io *io;
+ grn_pat *pat;
+ pat_node *node0;
+ struct grn_pat_header *header;
+ io = grn_io_open(ctx, path, grn_io_auto);
+ if (!io) { return NULL; }
+ header = grn_io_header(io);
+ if (grn_io_get_type(io) != GRN_TABLE_PAT_KEY) {
+ ERR(GRN_INVALID_FORMAT, "file type unmatch");
+ grn_io_close(ctx, io);
+ return NULL;
+ }
+ if (!(pat = GRN_MALLOC(sizeof(grn_pat)))) {
+ grn_io_close(ctx, io);
+ return NULL;
+ }
+ GRN_DB_OBJ_SET_TYPE(pat, GRN_TABLE_PAT_KEY);
+ pat->io = io;
+ pat->header = header;
+ pat->key_size = header->key_size;
+ pat->value_size = header->value_size;
+ pat->encoding = header->encoding;
+ pat->tokenizer = grn_ctx_at(ctx, header->tokenizer);
+ if (header->flags & GRN_OBJ_KEY_NORMALIZE) {
+ header->flags &= ~GRN_OBJ_KEY_NORMALIZE;
+ pat->normalizer = grn_ctx_get(ctx, GRN_NORMALIZER_AUTO_NAME, -1);
+ header->normalizer = grn_obj_id(ctx, pat->normalizer);
+ } else {
+ pat->normalizer = grn_ctx_at(ctx, header->normalizer);
+ }
+ pat->obj.header.flags = header->flags;
+ PAT_AT(pat, 0, node0);
+ if (!node0) {
+ grn_io_close(ctx, io);
+ GRN_GFREE(pat);
+ return NULL;
+ }
+ pat->cache = NULL;
+ pat->cache_size = 0;
+ return pat;
+}
+
+grn_rc
+grn_pat_close(grn_ctx *ctx, grn_pat *pat)
+{
+ grn_rc rc;
+ if ((rc = grn_io_close(ctx, pat->io))) {
+ ERR(rc, "grn_io_close failed");
+ } else {
+ if (pat->cache) { grn_pat_cache_disable(ctx, pat); }
+ GRN_FREE(pat);
+ }
+ return rc;
+}
+
+grn_rc
+grn_pat_remove(grn_ctx *ctx, const char *path)
+{
+ if (!path) {
+ ERR(GRN_INVALID_ARGUMENT, "path is null");
+ return GRN_INVALID_ARGUMENT;
+ }
+ return grn_io_remove(ctx, path);
+}
+
+grn_rc
+grn_pat_truncate(grn_ctx *ctx, grn_pat *pat)
+{
+ grn_rc rc;
+ const char *io_path;
+ char *path;
+ uint32_t key_size, value_size, flags;
+
+ if ((io_path = grn_io_path(pat->io)) && *io_path != '\0') {
+ if (!(path = GRN_STRDUP(io_path))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "cannot duplicate path: <%s>", io_path);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ } else {
+ path = NULL;
+ }
+ key_size = pat->key_size;
+ value_size = pat->value_size;
+ flags = pat->obj.header.flags;
+ if ((rc = grn_io_close(ctx, pat->io))) { goto exit; }
+ pat->io = NULL;
+ if (path && (rc = grn_io_remove(ctx, path))) { goto exit; }
+ if (!_grn_pat_create(ctx, pat, path, key_size, value_size, flags)) {
+ rc = GRN_UNKNOWN_ERROR;
+ }
+ if (pat->cache && pat->cache_size) {
+ memset(pat->cache, 0, pat->cache_size * sizeof(grn_id));
+ }
+exit:
+ if (path) { GRN_FREE(path); }
+ return rc;
+}
+
+inline static grn_id
+_grn_pat_add(grn_ctx *ctx, grn_pat *pat, const uint8_t *key, uint32_t size, uint32_t *new, uint32_t *lkey)
+{
+ grn_id r, r0, *p0, *p1 = NULL;
+ pat_node *rn, *rn0;
+ int c, c0 = -1, c1 = -1, len;
+
+ uint32_t cache_id = 0;
+ if (pat->cache) {
+ const uint8_t *p = key;
+ uint32_t length = size;
+ for (cache_id = 0; length--; p++) { cache_id = (cache_id * 37) + *p; }
+ cache_id &= (pat->cache_size - 1);
+ if (pat->cache[cache_id]) {
+ PAT_AT(pat, pat->cache[cache_id], rn);
+ if (rn) {
+ const uint8_t *k = pat_node_get_key(ctx, pat, rn);
+ if (k && size == PAT_LEN(rn) && !memcmp(k, key, size)) {
+ return pat->cache[cache_id];
+ }
+ }
+ }
+ }
+
+ *new = 0;
+ len = (int)size * 16;
+ PAT_AT(pat, 0, rn0);
+ p0 = &rn0->lr[1];
+ if (*p0) {
+ uint32_t size2;
+ int xor, mask;
+ const uint8_t *s, *d;
+ for (;;) {
+ if (!(r0 = *p0)) {
+ if (!(s = pat_node_get_key(ctx, pat, rn0))) { return 0; }
+ size2 = PAT_LEN(rn0);
+ break;
+ }
+ PAT_AT(pat, r0, rn0);
+ if (!rn0) { return GRN_ID_NIL; }
+ if (c0 < rn0->check && rn0->check < len) {
+ c1 = c0; c0 = rn0->check;
+ p1 = p0;
+ if (c0 & 1) {
+ p0 = (c0 + 1 < len) ? &rn0->lr[1] : &rn0->lr[0];
+ } else {
+ p0 = &rn0->lr[nth_bit(key, c0, len)];
+ }
+ } else {
+ if (!(s = pat_node_get_key(ctx, pat, rn0))) { return 0; }
+ size2 = PAT_LEN(rn0);
+ if (size == size2 && !memcmp(s, key, size)) {
+ if (pat->cache) { pat->cache[cache_id] = r0; }
+ return r0;
+ }
+ break;
+ }
+ }
+ {
+ uint32_t min = size > size2 ? size2 : size;
+ for (c = 0, d = key; min && *s == *d; c += 16, s++, d++, min--);
+ if (min) {
+ for (xor = *s ^ *d, mask = 0x80; !(xor & mask); mask >>= 1, c += 2);
+ } else {
+ c--;
+ }
+ }
+ if (c == c0 && !*p0) {
+ if (c < len - 2) { c += 2; }
+ } else {
+ if (c < c0) {
+ if (c > c1) {
+ p0 = p1;
+ } else {
+ PAT_AT(pat, 0, rn0);
+ p0 = &rn0->lr[1];
+ while ((r0 = *p0)) {
+ PAT_AT(pat, r0, rn0);
+ if (!rn0) { return 0; }
+ c0 = PAT_CHK(rn0);
+ if (c < c0) { break; }
+ if (c0 & 1) {
+ p0 = (c0 + 1 < len) ? &rn0->lr[1] : &rn0->lr[0];
+ } else {
+ p0 = &rn0->lr[nth_bit(key, c0, len)];
+ }
+ }
+ }
+ }
+ }
+ if (c >= len) { return 0; }
+ } else {
+ c = len - 2;
+ }
+ {
+ uint32_t size2 = size > sizeof(uint32_t) ? size : 0;
+ if (*lkey && size2) {
+ if (pat->header->garbages[0]) {
+ r = pat->header->garbages[0];
+ pat->header->n_entries++;
+ pat->header->n_garbages--;
+ PAT_AT(pat, r, rn);
+ if (!rn) { return 0; }
+ pat->header->garbages[0] = rn->lr[0];
+ } else {
+ if (!(rn = pat_node_new(ctx, pat, &r))) { return 0; }
+ }
+ PAT_IMD_OFF(rn);
+ PAT_LEN_SET(rn, size);
+ rn->key = *lkey;
+ } else {
+ if (pat->header->garbages[size2]) {
+ uint8_t *keybuf;
+ r = pat->header->garbages[size2];
+ pat->header->n_entries++;
+ pat->header->n_garbages--;
+ PAT_AT(pat, r, rn);
+ if (!rn) { return 0; }
+ pat->header->garbages[size2] = rn->lr[0];
+ if (!(keybuf = pat_node_get_key(ctx, pat, rn))) { return 0; }
+ PAT_LEN_SET(rn, size);
+ memcpy(keybuf, key, size);
+ } else {
+ if (!(rn = pat_node_new(ctx, pat, &r))) { return 0; }
+ pat_node_set_key(ctx, pat, rn, key, size);
+ }
+ *lkey = rn->key;
+ }
+ }
+ PAT_CHK_SET(rn, c);
+ PAT_DEL_OFF(rn);
+ if ((c & 1) ? (c + 1 < len) : nth_bit(key, c, len)) {
+ rn->lr[1] = r;
+ rn->lr[0] = *p0;
+ } else {
+ rn->lr[1] = *p0;
+ rn->lr[0] = r;
+ }
+ // smp_wmb();
+ *p0 = r;
+ *new = 1;
+ if (pat->cache) { pat->cache[cache_id] = r; }
+ return r;
+}
+
+inline static int
+chop(grn_ctx *ctx, grn_pat *pat, const char **key, const char *end, uint32_t *lkey)
+{
+ size_t len = grn_charlen(ctx, *key, end);
+ if (len) {
+ *lkey += len;
+ *key += len;
+ return **key;
+ } else {
+ return 0;
+ }
+}
+
+#define MAX_FIXED_KEY_SIZE (sizeof(int64_t))
+
+#define KEY_NEEDS_CONVERT(pat,size) \
+ (!((pat)->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE) && (size) <= MAX_FIXED_KEY_SIZE)
+
+#define KEY_ENC(pat,keybuf,key,size) do {\
+ switch ((pat)->obj.header.flags & GRN_OBJ_KEY_MASK) {\
+ case GRN_OBJ_KEY_UINT :\
+ if (((pat)->obj.header.domain != GRN_DB_TOKYO_GEO_POINT) &&\
+ ((pat)->obj.header.domain != GRN_DB_WGS84_GEO_POINT)) {\
+ grn_hton((keybuf), (key), (size));\
+ break;\
+ }\
+ case GRN_OBJ_KEY_GEO_POINT :\
+ grn_gton((keybuf), (key), (size));\
+ break;\
+ case GRN_OBJ_KEY_INT :\
+ grn_hton((keybuf), (key), (size));\
+ *((uint8_t *)(keybuf)) ^= 0x80;\
+ break;\
+ case GRN_OBJ_KEY_FLOAT :\
+ if ((size) == sizeof(int64_t)) {\
+ int64_t v = *(int64_t *)(key);\
+ v ^= ((v >> 63)|(1LL << 63));\
+ grn_hton((keybuf), &v, (size));\
+ }\
+ break;\
+ }\
+} while (0)
+
+#define KEY_DEC(pat,keybuf,key,size) do {\
+ switch ((pat)->obj.header.flags & GRN_OBJ_KEY_MASK) {\
+ case GRN_OBJ_KEY_UINT :\
+ if (((pat)->obj.header.domain != GRN_DB_TOKYO_GEO_POINT) &&\
+ ((pat)->obj.header.domain != GRN_DB_WGS84_GEO_POINT)) {\
+ grn_ntoh((keybuf), (key), (size));\
+ break;\
+ }\
+ case GRN_OBJ_KEY_GEO_POINT :\
+ grn_ntog((keybuf), (key), (size));\
+ break;\
+ case GRN_OBJ_KEY_INT :\
+ grn_ntohi((keybuf), (key), (size));\
+ break;\
+ case GRN_OBJ_KEY_FLOAT :\
+ if ((size) == sizeof(int64_t)) {\
+ int64_t v;\
+ grn_hton(&v, (key), (size));\
+ *((int64_t *)(keybuf)) = v ^ (((v^(1LL<<63))>> 63)|(1LL<<63)); \
+ }\
+ break;\
+ }\
+} while (0)
+
+#define KEY_ENCODE(pat,keybuf,key,size) do {\
+ if (KEY_NEEDS_CONVERT(pat,size)) {\
+ KEY_ENC((pat), (keybuf), (key), (size));\
+ (key) = (keybuf);\
+ }\
+} while (0)
+
+grn_id
+grn_pat_add(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size,
+ void **value, int *added)
+{
+ uint32_t new, lkey = 0;
+ grn_id r0;
+ uint8_t keybuf[MAX_FIXED_KEY_SIZE];
+ if (!key || !key_size) { return GRN_ID_NIL; }
+ if (key_size > GRN_TABLE_MAX_KEY_SIZE) {
+ ERR(GRN_INVALID_ARGUMENT, "too long key: (%u)", key_size);
+ return GRN_ID_NIL;
+ }
+ KEY_ENCODE(pat, keybuf, key, key_size);
+ r0 = _grn_pat_add(ctx, pat, (uint8_t *)key, key_size, &new, &lkey);
+ if (added) { *added = new; }
+ if (r0 && (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) &&
+ (*((uint8_t *)key) & 0x80)) { // todo: refine!!
+ sis_node *sl, *sr;
+ grn_id l = r0, r;
+ if (new && (sl = sis_get(ctx, pat, l))) {
+ const char *sis = key, *end = sis + key_size;
+ sl->children = l;
+ sl->sibling = 0;
+ while (chop(ctx, pat, &sis, end, &lkey)) {
+ if (!(*sis & 0x80)) { break; }
+ if (!(r = _grn_pat_add(ctx, pat, (uint8_t *)sis, end - sis, &new, &lkey))) {
+ break;
+ }
+ if (!(sr = sis_get(ctx, pat, r))) { break; }
+ if (new) {
+ sl->sibling = r;
+ sr->children = l;
+ sr->sibling = 0;
+ } else {
+ sl->sibling = sr->children;
+ sr->children = l;
+ break;
+ }
+ l = r;
+ sl = sr;
+ }
+ }
+ }
+ if (r0 && value) {
+ byte *v = (byte *)sis_get(ctx, pat, r0);
+ if (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) {
+ *value = v + sizeof(sis_node);
+ } else {
+ *value = v;
+ }
+ }
+ return r0;
+}
+
+inline static grn_id
+_grn_pat_get(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size, void **value)
+{
+ grn_id r;
+ pat_node *rn;
+ int c0 = -1, c;
+ uint32_t len = key_size * 16;
+ PAT_AT(pat, 0, rn);
+ for (r = rn->lr[1]; r;) {
+ PAT_AT(pat, r, rn);
+ if (!rn) { break; /* corrupt? */ }
+ c = PAT_CHK(rn);
+ if (len <= c) { break; }
+ if (c <= c0) {
+ const uint8_t *k = pat_node_get_key(ctx, pat, rn);
+ if (k && key_size == PAT_LEN(rn) && !memcmp(k, key, key_size)) {
+ if (value) {
+ byte *v = (byte *)sis_get(ctx, pat, r);
+ if (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) {
+ *value = v + sizeof(sis_node);
+ } else {
+ *value = v;
+ }
+ }
+ return r;
+ }
+ break;
+ }
+ if (c & 1) {
+ r = (c + 1 < len) ? rn->lr[1] : rn->lr[0];
+ } else {
+ r = rn->lr[nth_bit((uint8_t *)key, c, len)];
+ }
+ c0 = c;
+ }
+ return GRN_ID_NIL;
+}
+
+grn_id
+grn_pat_get(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size, void **value)
+{
+ uint8_t keybuf[MAX_FIXED_KEY_SIZE];
+ KEY_ENCODE(pat, keybuf, key, key_size);
+ return _grn_pat_get(ctx, pat, key, key_size, value);
+}
+
+grn_id
+grn_pat_nextid(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size)
+{
+ grn_id r = GRN_ID_NIL;
+ if (pat && key) {
+ if (!(r = pat->header->garbages[key_size > sizeof(uint32_t) ? key_size : 0])) {
+ r = pat->header->curr_rec + 1;
+ }
+ }
+ return r;
+}
+
+static void
+get_tc(grn_ctx *ctx, grn_pat *pat, grn_hash *h, pat_node *rn)
+{
+ grn_id id;
+ pat_node *node;
+ id = rn->lr[1];
+ if (id) {
+ PAT_AT(pat, id, node);
+ if (node) {
+ if (PAT_CHK(node) > PAT_CHK(rn)) {
+ get_tc(ctx, pat, h, node);
+ } else {
+ grn_hash_add(ctx, h, &id, sizeof(grn_id), NULL, NULL);
+ }
+ }
+ }
+ id = rn->lr[0];
+ if (id) {
+ PAT_AT(pat, id, node);
+ if (node) {
+ if (PAT_CHK(node) > PAT_CHK(rn)) {
+ get_tc(ctx, pat, h, node);
+ } else {
+ grn_hash_add(ctx, h, &id, sizeof(grn_id), NULL, NULL);
+ }
+ }
+ }
+}
+
+grn_rc
+grn_pat_prefix_search(grn_ctx *ctx, grn_pat *pat,
+ const void *key, uint32_t key_size, grn_hash *h)
+{
+ int c0 = -1, c;
+ const uint8_t *k;
+ uint32_t len = key_size * 16;
+ grn_id r;
+ pat_node *rn;
+ uint8_t keybuf[MAX_FIXED_KEY_SIZE];
+ KEY_ENCODE(pat, keybuf, key, key_size);
+ PAT_AT(pat, 0, rn);
+ r = rn->lr[1];
+ while (r) {
+ PAT_AT(pat, r, rn);
+ if (!rn) { return GRN_FILE_CORRUPT; }
+ c = PAT_CHK(rn);
+ if (c0 < c && c < len - 1) {
+ if (c & 1) {
+ r = (c + 1 < len) ? rn->lr[1] : rn->lr[0];
+ } else {
+ r = rn->lr[nth_bit((uint8_t *)key, c, len)];
+ }
+ c0 = c;
+ continue;
+ }
+ if (!(k = pat_node_get_key(ctx, pat, rn))) { break; }
+ if (PAT_LEN(rn) < key_size) { break; }
+ if (!memcmp(k, key, key_size)) {
+ if (c >= len - 1) {
+ get_tc(ctx, pat, h, rn);
+ } else {
+ grn_hash_add(ctx, h, &r, sizeof(grn_id), NULL, NULL);
+ }
+ return GRN_SUCCESS;
+ }
+ break;
+ }
+ return GRN_END_OF_DATA;
+}
+
+grn_hash *
+grn_pat_prefix_search2(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size)
+{
+ grn_hash *h;
+ if (!pat || !key) { return NULL; }
+ if ((h = grn_hash_create(ctx, NULL, sizeof(grn_id), 0, 0))) {
+ if (grn_pat_prefix_search(ctx, pat, key, key_size, h)) {
+ grn_hash_close(ctx, h);
+ h = NULL;
+ }
+ }
+ return h;
+}
+
+grn_rc
+grn_pat_suffix_search(grn_ctx *ctx, grn_pat *pat,
+ const void *key, uint32_t key_size, grn_hash *h)
+{
+ grn_id r;
+ if ((r = grn_pat_get(ctx, pat, key, key_size, NULL))) {
+ uint32_t *offset;
+ if (grn_hash_add(ctx, h, &r, sizeof(grn_id), (void **) &offset, NULL)) {
+ *offset = 0;
+ if (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) { sis_collect(ctx, pat, h, r, 1); }
+ return GRN_SUCCESS;
+ }
+ }
+ return GRN_END_OF_DATA;
+}
+
+grn_hash *
+grn_pat_suffix_search2(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size)
+{
+ grn_hash *h;
+ if (!pat || !key) { return NULL; }
+ if ((h = grn_hash_create(ctx, NULL, sizeof(grn_id), sizeof(uint32_t), 0))) {
+ if (grn_pat_suffix_search(ctx, pat, key, key_size, h)) {
+ grn_hash_close(ctx, h);
+ h = NULL;
+ }
+ }
+ return h;
+}
+
+grn_id
+grn_pat_lcp_search(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size)
+{
+ pat_node *rn;
+ grn_id r, r2 = GRN_ID_NIL;
+ uint32_t len = key_size * 16;
+ int c0 = -1, c;
+ if (!pat || !key || !(pat->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE)) { return GRN_ID_NIL; }
+ PAT_AT(pat, 0, rn);
+ for (r = rn->lr[1]; r;) {
+ PAT_AT(pat, r, rn);
+ if (!rn) { break; /* corrupt? */ }
+ c = PAT_CHK(rn);
+ if (c <= c0) {
+ if (PAT_LEN(rn) <= key_size) {
+ uint8_t *p = pat_node_get_key(ctx, pat, rn);
+ if (!p) { break; }
+ if (!memcmp(p, key, PAT_LEN(rn))) { return r; }
+ }
+ break;
+ }
+ if (len <= c) { break; }
+ if (c & 1) {
+ uint8_t *p;
+ pat_node *rn0;
+ grn_id r0 = rn->lr[0];
+ PAT_AT(pat, r0, rn0);
+ if (!rn0) { break; /* corrupt? */ }
+ p = pat_node_get_key(ctx, pat, rn0);
+ if (!p) { break; }
+ if (PAT_LEN(rn0) <= key_size && !memcmp(p, key, PAT_LEN(rn0))) { r2 = r0; }
+ r = (c + 1 < len) ? rn->lr[1] : rn->lr[0];
+ } else {
+ r = rn->lr[nth_bit((uint8_t *)key, c, len)];
+ }
+ c0 = c;
+ }
+ return r2;
+}
+
+inline static grn_rc
+_grn_pat_del(grn_ctx *ctx, grn_pat *pat, const char *key, uint32_t key_size, int shared,
+ grn_table_delete_optarg *optarg)
+{
+ grn_pat_delinfo *di;
+ uint8_t direction;
+ pat_node *rn, *rn0 = NULL, *rno;
+ int c, c0 = -1, ch;
+ uint32_t len = key_size * 16;
+ grn_id r, otherside, *proot, *p, *p0 = NULL;
+
+ di = delinfo_new(ctx, pat); /* must be called before find rn */
+ di->shared = shared;
+ PAT_AT(pat, 0, rn);
+ c = -1;
+ proot = p = &rn->lr[1];
+ for (;;) {
+ if (!(r = *p)) { return GRN_INVALID_ARGUMENT; }
+ PAT_AT(pat, r, rn);
+ if (!rn) { return GRN_FILE_CORRUPT; }
+ ch = PAT_CHK(rn);
+ if (len <= ch) { return GRN_INVALID_ARGUMENT; }
+ if (c >= ch) {
+ const uint8_t *k = pat_node_get_key(ctx, pat, rn);
+ if (!k) { return GRN_INVALID_ARGUMENT; }
+ if (key_size == PAT_LEN(rn) && !memcmp(k, key, key_size)) {
+ break; /* found */
+ } else { return GRN_INVALID_ARGUMENT; }
+ }
+ c0 = c;
+ p0 = p;
+ if ((c = ch) & 1) {
+ p = (c + 1 < len) ? &rn->lr[1] : &rn->lr[0];
+ } else {
+ p = &rn->lr[nth_bit((uint8_t *)key, c, len)];
+ }
+ rn0 = rn;
+ }
+ if (optarg && optarg->func &&
+ !optarg->func(ctx, (grn_obj *)pat, r, optarg->func_arg)) {
+ return GRN_SUCCESS;
+ }
+ direction = (rn0->lr[1] == r);
+ otherside = direction ? rn0->lr[0] : rn0->lr[1];
+ if (rn == rn0) {
+ di->stat = DL_PHASE2;
+ di->d = r;
+ if (otherside) {
+ PAT_AT(pat, otherside, rno);
+ if (rno && c0 < PAT_CHK(rno) && PAT_CHK(rno) <= c) {
+ if (!delinfo_search(pat, otherside)) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "no delinfo found %d", otherside);
+ }
+ PAT_CHK_SET(rno, 0);
+ }
+ if (proot == p0 && !rno->check) { rno->lr[0] = rno->lr[1] = otherside; }
+ }
+ *p0 = otherside;
+ } else {
+ grn_pat_delinfo *ldi = NULL, *ddi = NULL;
+ if (PAT_DEL(rn)) { ldi = delinfo_search(pat, r); }
+ if (PAT_DEL(rn0)) { ddi = delinfo_search(pat, *p0); }
+ if (ldi) {
+ PAT_DEL_OFF(rn);
+ di->stat = DL_PHASE2;
+ if (ddi) {
+ PAT_DEL_OFF(rn0);
+ ddi->stat = DL_PHASE2;
+ if (ddi == ldi) {
+ if (r != ddi->ld) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "r(%d) != ddi->ld(%d)", r, ddi->ld);
+ }
+ di->d = r;
+ } else {
+ ldi->ld = ddi->ld;
+ di->d = r;
+ }
+ } else {
+ PAT_DEL_ON(rn0);
+ ldi->ld = *p0;
+ di->d = r;
+ }
+ } else {
+ PAT_DEL_ON(rn);
+ if (ddi) {
+ if (ddi->d != *p0) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "ddi->d(%d) != *p0(%d)", ddi->d, *p0);
+ }
+ PAT_DEL_OFF(rn0);
+ ddi->stat = DL_PHASE2;
+ di->stat = DL_PHASE1;
+ di->ld = ddi->ld;
+ di->d = r;
+ /*
+ PAT_DEL_OFF(rn0);
+ ddi->d = r;
+ di->stat = DL_PHASE2;
+ di->d = *p0;
+ */
+ } else {
+ PAT_DEL_ON(rn0);
+ di->stat = DL_PHASE1;
+ di->ld = *p0;
+ di->d = r;
+ // grn_log("pat_del d=%d ld=%d stat=%d", r, *p0, DL_PHASE1);
+ }
+ }
+ if (*p0 == otherside) {
+ PAT_CHK_SET(rn0, 0);
+ if (proot == p0 && !rn0->check) { rn0->lr[0] = rn0->lr[1] = otherside; }
+ } else {
+ if (otherside) {
+ PAT_AT(pat, otherside, rno);
+ if (rno && c0 < PAT_CHK(rno) && PAT_CHK(rno) <= c) {
+ if (!delinfo_search(pat, otherside)) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "no delinfo found %d", otherside);
+ }
+ PAT_CHK_SET(rno, 0);
+ }
+ if (proot == p0 && !rno->check) { rno->lr[0] = rno->lr[1] = otherside; }
+ }
+ *p0 = otherside;
+ }
+ }
+ pat->header->n_entries--;
+ pat->header->n_garbages++;
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+_grn_pat_delete(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size,
+ grn_table_delete_optarg *optarg)
+{
+ if (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) {
+ grn_id id = grn_pat_get(ctx, pat, key, key_size, NULL);
+ if (id && grn_pat_delete_with_sis(ctx, pat, id, optarg)) {
+ return GRN_SUCCESS;
+ }
+ return GRN_INVALID_ARGUMENT;
+ }
+ return _grn_pat_del(ctx, pat, key, key_size, 0, optarg);
+}
+
+grn_rc
+grn_pat_delete(grn_ctx *ctx, grn_pat *pat, const void *key, uint32_t key_size,
+ grn_table_delete_optarg *optarg)
+{
+ uint8_t keybuf[MAX_FIXED_KEY_SIZE];
+ if (!pat || !key || !key_size) { return GRN_INVALID_ARGUMENT; }
+ KEY_ENCODE(pat, keybuf, key, key_size);
+ return _grn_pat_delete(ctx, pat, key, key_size, optarg);
+}
+
+uint32_t
+grn_pat_size(grn_ctx *ctx, grn_pat *pat)
+{
+ if (!pat) { return GRN_INVALID_ARGUMENT; }
+ return pat->header->n_entries;
+}
+
+const char *
+_grn_pat_key(grn_ctx *ctx, grn_pat *pat, grn_id id, uint32_t *key_size)
+{
+ pat_node *node;
+ uint8_t *key;
+ PAT_AT(pat, id, node);
+ if (!node) {
+ *key_size = 0;
+ return NULL;
+ }
+ key = pat_node_get_key(ctx, pat, node);
+ if (key) {
+ *key_size = PAT_LEN(node);
+ } else {
+ *key_size = 0;
+ }
+ return (const char *)key;
+}
+
+grn_rc
+grn_pat_delete_by_id(grn_ctx *ctx, grn_pat *pat, grn_id id,
+ grn_table_delete_optarg *optarg)
+{
+ if (!pat || !id) { return GRN_INVALID_ARGUMENT; }
+ {
+ uint32_t key_size;
+ const char *key = _grn_pat_key(ctx, pat, id, &key_size);
+ return _grn_pat_delete(ctx, pat, key, key_size, optarg);
+ }
+}
+
+int
+grn_pat_get_key(grn_ctx *ctx, grn_pat *pat, grn_id id, void *keybuf, int bufsize)
+{
+ int len;
+ uint8_t *key;
+ pat_node *node;
+ if (!pat) { return GRN_INVALID_ARGUMENT; }
+ PAT_AT(pat, id, node);
+ if (!node) { return 0; }
+ if (!(key = pat_node_get_key(ctx, pat, node))) { return 0; }
+ len = PAT_LEN(node);
+ if (keybuf && bufsize >= len) {
+ if (KEY_NEEDS_CONVERT(pat, len)) {
+ KEY_DEC(pat, keybuf, key, len);
+ } else {
+ memcpy(keybuf, key, len);
+ }
+ }
+ return len;
+}
+
+int
+grn_pat_get_key2(grn_ctx *ctx, grn_pat *pat, grn_id id, grn_obj *bulk)
+{
+ uint32_t len;
+ uint8_t *key;
+ pat_node *node;
+ if (!pat) { return GRN_INVALID_ARGUMENT; }
+ PAT_AT(pat, id, node);
+ if (!node) { return 0; }
+ if (!(key = pat_node_get_key(ctx, pat, node))) { return 0; }
+ len = PAT_LEN(node);
+ if (KEY_NEEDS_CONVERT(pat, len)) {
+ if (bulk->header.impl_flags & GRN_OBJ_REFER) {
+ GRN_TEXT_INIT(bulk, 0);
+ }
+ if (!grn_bulk_reserve(ctx, bulk, len)) {
+ char *curr = GRN_BULK_CURR(bulk);
+ KEY_DEC(pat, curr, key, len);
+ grn_bulk_truncate(ctx, bulk, GRN_BULK_VSIZE(bulk) + len);
+ }
+ } else {
+ if (bulk->header.impl_flags & GRN_OBJ_REFER) {
+ bulk->u.b.head = (char *)key;
+ bulk->u.b.curr = (char *)key + len;
+ } else {
+ grn_bulk_write(ctx, bulk, (char *)key, len);
+ }
+ }
+ return len;
+}
+
+int
+grn_pat_get_value(grn_ctx *ctx, grn_pat *pat, grn_id id, void *valuebuf)
+{
+ int value_size = (int)pat->value_size;
+ if (value_size) {
+ byte *v = (byte *)sis_at(ctx, pat, id);
+ if (v) {
+ if (valuebuf) {
+ if (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) {
+ memcpy(valuebuf, v + sizeof(sis_node), value_size);
+ } else {
+ memcpy(valuebuf, v, value_size);
+ }
+ }
+ return value_size;
+ }
+ }
+ return 0;
+}
+
+const char *
+grn_pat_get_value_(grn_ctx *ctx, grn_pat *pat, grn_id id, uint32_t *size)
+{
+ const char *value = NULL;
+ if ((*size = pat->value_size)) {
+ if ((value = (const char *)sis_at(ctx, pat, id))
+ && (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS)) {
+ value += sizeof(sis_node);
+ }
+ }
+ return value;
+}
+
+grn_rc
+grn_pat_set_value(grn_ctx *ctx, grn_pat *pat, grn_id id,
+ const void *value, int flags)
+{
+ if (value) {
+ uint32_t value_size = pat->value_size;
+ if (value_size) {
+ byte *v = (byte *)sis_get(ctx, pat, id);
+ if (v) {
+ if (pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) { v += sizeof(sis_node); }
+ switch ((flags & GRN_OBJ_SET_MASK)) {
+ case GRN_OBJ_SET :
+ memcpy(v, value, value_size);
+ return GRN_SUCCESS;
+ case GRN_OBJ_INCR :
+ switch (value_size) {
+ case sizeof(int32_t) :
+ *((int32_t *)v) += *((int32_t *)value);
+ return GRN_SUCCESS;
+ case sizeof(int64_t) :
+ *((int64_t *)v) += *((int64_t *)value);
+ return GRN_SUCCESS;
+ default :
+ return GRN_INVALID_ARGUMENT;
+ }
+ break;
+ case GRN_OBJ_DECR :
+ switch (value_size) {
+ case sizeof(int32_t) :
+ *((int32_t *)v) -= *((int32_t *)value);
+ return GRN_SUCCESS;
+ case sizeof(int64_t) :
+ *((int64_t *)v) -= *((int64_t *)value);
+ return GRN_SUCCESS;
+ default :
+ return GRN_INVALID_ARGUMENT;
+ }
+ break;
+ default :
+ // todo : support other types.
+ return GRN_INVALID_ARGUMENT;
+ }
+ } else {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ }
+ return GRN_INVALID_ARGUMENT;
+}
+
+grn_rc
+grn_pat_info(grn_ctx *ctx, grn_pat *pat, int *key_size, unsigned int *flags,
+ grn_encoding *encoding, unsigned int *n_entries, unsigned int *file_size)
+{
+ ERRCLR(NULL);
+ if (!pat) { return GRN_INVALID_ARGUMENT; }
+ if (key_size) { *key_size = pat->key_size; }
+ if (flags) { *flags = pat->obj.header.flags; }
+ if (encoding) { *encoding = pat->encoding; }
+ if (n_entries) { *n_entries = pat->header->n_entries; }
+ if (file_size) {
+ grn_rc rc;
+ uint64_t tmp = 0;
+ if ((rc = grn_io_size(ctx, pat->io, &tmp))) {
+ return rc;
+ }
+ *file_size = (unsigned int) tmp; /* FIXME: inappropriate cast */
+ }
+ return GRN_SUCCESS;
+}
+
+int
+grn_pat_delete_with_sis(grn_ctx *ctx, grn_pat *pat, grn_id id,
+ grn_table_delete_optarg *optarg)
+{
+ int level = 0, shared;
+ const char *key = NULL, *_key;
+ sis_node *sp, *ss = NULL, *si = sis_at(ctx, pat, id);
+ while (id) {
+ pat_node *rn;
+ uint32_t key_size;
+ if ((si && si->children && si->children != id) ||
+ (optarg && optarg->func &&
+ !optarg->func(ctx, (grn_obj *)pat, id, optarg->func_arg))) {
+ break;
+ }
+ PAT_AT(pat, id, rn);
+ if (!(_key = (char *)pat_node_get_key(ctx, pat, rn))) { return 0; }
+ if (_key == key) {
+ shared = 1;
+ } else {
+ key = _key;
+ shared = 0;
+ }
+ key_size = PAT_LEN(rn);
+ if (key && key_size) { _grn_pat_del(ctx, pat, key, key_size, shared, NULL); }
+ if (si) {
+ grn_id *p, sid;
+ uint32_t lkey = 0;
+ if ((*key & 0x80) && chop(ctx, pat, &key, key + key_size, &lkey)) {
+ if ((sid = grn_pat_get(ctx, pat, key, key_size - lkey, NULL)) &&
+ (ss = sis_at(ctx, pat, sid))) {
+ for (p = &ss->children; *p && *p != sid; p = &sp->sibling) {
+ if (*p == id) {
+ *p = si->sibling;
+ break;
+ }
+ if (!(sp = sis_at(ctx, pat, *p))) { break; }
+ }
+ }
+ } else {
+ sid = GRN_ID_NIL;
+ }
+ si->sibling = 0;
+ si->children = 0;
+ id = sid;
+ si = ss;
+ } else {
+ id = GRN_ID_NIL;
+ }
+ level++;
+ }
+ if (level) {
+ uint32_t lkey = 0;
+ while (id && key) {
+ uint32_t key_size;
+ if (_grn_pat_key(ctx, pat, id, &key_size) != key) { break; }
+ {
+ pat_node *rn;
+ PAT_AT(pat, id, rn);
+ if (!rn) { break; }
+ if (lkey) {
+ rn->key = lkey;
+ } else {
+ pat_node_set_key(ctx, pat, rn, (uint8_t *)key, key_size);
+ lkey = rn->key;
+ }
+ }
+ {
+ const char *end = key + key_size;
+ if (!((*key & 0x80) && chop(ctx, pat, &key, end, &lkey))) { break; }
+ id = grn_pat_get(ctx, pat, key, end - key, NULL);
+ }
+ }
+ }
+ return level;
+}
+
+grn_id
+grn_pat_next(grn_ctx *ctx, grn_pat *pat, grn_id id)
+{
+ while (++id <= pat->header->curr_rec) {
+ uint32_t key_size;
+ const char *key = _grn_pat_key(ctx, pat, id, &key_size);
+ if (id == grn_pat_get(ctx, pat, key, key_size, NULL)) {
+ return id;
+ }
+ }
+ return GRN_ID_NIL;
+}
+
+grn_id
+grn_pat_at(grn_ctx *ctx, grn_pat *pat, grn_id id)
+{
+ uint32_t key_size;
+ const char *key = _grn_pat_key(ctx, pat, id, &key_size);
+ if (key && (id == _grn_pat_get(ctx, pat, key, key_size, NULL))) { return id; }
+ return GRN_ID_NIL;
+}
+
+grn_id
+grn_pat_curr_id(grn_ctx *ctx, grn_pat *pat)
+{
+ return pat->header->curr_rec;
+}
+
+int
+grn_pat_scan(grn_ctx *ctx, grn_pat *pat, const char *str, unsigned int str_len,
+ grn_pat_scan_hit *sh, unsigned int sh_size, const char **rest)
+{
+ int n = 0;
+ grn_id tid;
+ if (pat->normalizer) {
+ grn_obj *nstr = grn_string_open(ctx, str, str_len,
+ pat->normalizer, GRN_STRING_WITH_CHECKS);
+ if (nstr) {
+ const short *cp = grn_string_get_checks(ctx, nstr);
+ unsigned int offset = 0, offset0 = 0;
+ unsigned int normalized_length_in_bytes;
+ const char *sp, *se;
+ grn_string_get_normalized(ctx, nstr, &sp, &normalized_length_in_bytes,
+ NULL);
+ se = sp + normalized_length_in_bytes;
+ while (n < sh_size) {
+ if ((tid = grn_pat_lcp_search(ctx, pat, sp, se - sp))) {
+ uint32_t len;
+ _grn_pat_key(ctx, pat, tid, &len);
+ sh[n].id = tid;
+ sh[n].offset = (*cp > 0) ? offset : offset0;
+ while (len--) {
+ if (*cp > 0) { offset0 = offset; offset += *cp; }
+ sp++; cp++;
+ }
+ sh[n].length = offset - sh[n].offset;
+ n++;
+ } else {
+ if (*cp > 0) { offset0 = offset; offset += *cp; }
+ do {
+ sp++; cp++;
+ } while (sp < se && !*cp);
+ }
+ if (se <= sp) { offset = str_len; break; }
+ }
+ if (rest) {
+ grn_string_get_original(ctx, nstr, rest, NULL);
+ *rest += offset;
+ }
+ grn_obj_close(ctx, nstr);
+ } else {
+ n = -1;
+ if (rest) { *rest = str; }
+ }
+ } else {
+ uint32_t len;
+ const char *sp, *se = str + str_len;
+ for (sp = str; sp < se && n < sh_size; sp += len) {
+ if ((tid = grn_pat_lcp_search(ctx, pat, sp, se - sp))) {
+ _grn_pat_key(ctx, pat, tid, &len);
+ sh[n].id = tid;
+ sh[n].offset = sp - str;
+ sh[n].length = len;
+ n++;
+ } else {
+ len = grn_charlen(ctx, sp, se);
+ }
+ if (!len) { break; }
+ }
+ if (rest) { *rest = sp; }
+ }
+ return n;
+}
+
+#define INITIAL_SIZE 512
+
+inline static void
+push(grn_pat_cursor *c, grn_id id, uint16_t check)
+{
+ grn_ctx *ctx = c->ctx;
+ grn_pat_cursor_entry *se;
+ if (c->size <= c->sp) {
+ if (c->ss) {
+ uint32_t size = c->size * 4;
+ grn_pat_cursor_entry *ss = GRN_REALLOC(c->ss, size);
+ if (!ss) { return; /* give up */ }
+ c->ss = ss;
+ c->size = size;
+ } else {
+ if (!(c->ss = GRN_MALLOC(sizeof(grn_pat_cursor_entry) * INITIAL_SIZE))) {
+ return; /* give up */
+ }
+ c->size = INITIAL_SIZE;
+ }
+ }
+ se = &c->ss[c->sp++];
+ se->id = id;
+ se->check = check;
+}
+
+inline static grn_pat_cursor_entry *
+pop(grn_pat_cursor *c)
+{
+ return c->sp ? &c->ss[--c->sp] : NULL;
+}
+
+static grn_id
+grn_pat_cursor_next_by_id(grn_ctx *ctx, grn_pat_cursor *c)
+{
+ grn_pat *pat = c->pat;
+ int dir = (c->obj.header.flags & GRN_CURSOR_DESCENDING) ? -1 : 1;
+ while (c->curr_rec != c->tail) {
+ c->curr_rec += dir;
+ if (pat->header->n_garbages) {
+ uint32_t key_size;
+ const void *key = _grn_pat_key(ctx, pat, c->curr_rec, &key_size);
+ if (_grn_pat_get(ctx, pat, key, key_size, NULL) != c->curr_rec) {
+ continue;
+ }
+ }
+ c->rest--;
+ return c->curr_rec;
+ }
+ return GRN_ID_NIL;
+}
+
+grn_id
+grn_pat_cursor_next(grn_ctx *ctx, grn_pat_cursor *c)
+{
+ pat_node *node;
+ grn_pat_cursor_entry *se;
+ if (!c->rest) { return GRN_ID_NIL; }
+ if ((c->obj.header.flags & GRN_CURSOR_BY_ID)) {
+ return grn_pat_cursor_next_by_id(ctx, c);
+ }
+ while ((se = pop(c))) {
+ grn_id id = se->id;
+ int check = se->check, ch;
+ for (;;) {
+ if (id) {
+ PAT_AT(c->pat, id, node);
+ if (node) {
+ ch = PAT_CHK(node);
+ if (ch > check) {
+ if (c->obj.header.flags & GRN_CURSOR_DESCENDING) {
+ push(c, node->lr[0], ch);
+ id = node->lr[1];
+ } else {
+ push(c, node->lr[1], ch);
+ id = node->lr[0];
+ }
+ check = ch;
+ continue;
+ } else {
+ if (id == c->tail) {
+ c->sp = 0;
+ } else {
+ if (!c->curr_rec && c->tail) {
+ uint32_t lmin, lmax;
+ pat_node *nmin, *nmax;
+ const uint8_t *kmin, *kmax;
+ if (c->obj.header.flags & GRN_CURSOR_DESCENDING) {
+ PAT_AT(c->pat, c->tail, nmin);
+ PAT_AT(c->pat, id, nmax);
+ } else {
+ PAT_AT(c->pat, id, nmin);
+ PAT_AT(c->pat, c->tail, nmax);
+ }
+ lmin = PAT_LEN(nmin);
+ lmax = PAT_LEN(nmax);
+ kmin = pat_node_get_key(ctx, c->pat, nmin);
+ kmax = pat_node_get_key(ctx, c->pat, nmax);
+ if ((lmin < lmax) ?
+ (memcmp(kmin, kmax, lmin) > 0) :
+ (memcmp(kmin, kmax, lmax) >= 0)) {
+ c->sp = 0;
+ break;
+ }
+ }
+ }
+ c->curr_rec = id;
+ c->rest--;
+ return id;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ return GRN_ID_NIL;
+}
+
+void
+grn_pat_cursor_close(grn_ctx *ctx, grn_pat_cursor *c)
+{
+ GRN_ASSERT(c->ctx == ctx);
+ if (c->ss) { GRN_FREE(c->ss); }
+ GRN_FREE(c);
+}
+
+inline static int
+bitcmp(const void *s1, const void *s2, int offset, int length)
+{
+ int r, rest = length + (offset & 7) - 8, bl = offset >> 3, mask = 0xff >> (offset & 7);
+ unsigned char *a = (unsigned char *)s1 + bl, *b = (unsigned char *)s2 + bl;
+ if (rest <= 0) {
+ mask &= 0xff << -rest;
+ return (*a & mask) - (*b & mask);
+ }
+ if ((r = (*a & mask) - (*b & mask))) { return r; }
+ a++; b++;
+ if ((bl = rest >> 3)) {
+ if ((r = memcmp(a, b, bl))) { return r; }
+ a += bl; b += bl;
+ }
+ mask = 0xff << (8 - (rest & 7));
+ return (*a & mask) - (*b & mask);
+}
+
+inline static grn_rc
+set_cursor_prefix(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
+ const void *key, uint32_t key_size, int flags)
+{
+ int c0 = -1, ch;
+ const uint8_t *k;
+ uint32_t len, byte_len;
+ grn_id id;
+ pat_node *node;
+ uint8_t keybuf[MAX_FIXED_KEY_SIZE];
+ if (flags & GRN_CURSOR_SIZE_BY_BIT) {
+ len = key_size * 2;
+ byte_len = key_size >> 3;
+ } else {
+ len = key_size * 16;
+ byte_len = key_size;
+ }
+ KEY_ENCODE(pat, keybuf, key, byte_len);
+ PAT_AT(pat, 0, node);
+ id = node->lr[1];
+ while (id) {
+ PAT_AT(pat, id, node);
+ if (!node) { return GRN_FILE_CORRUPT; }
+ ch = PAT_CHK(node);
+ if (c0 < ch && ch < len - 1) {
+ if (ch & 1) {
+ id = (ch + 1 < len) ? node->lr[1] : node->lr[0];
+ } else {
+ id = node->lr[nth_bit((uint8_t *)key, ch, len)];
+ }
+ c0 = ch;
+ continue;
+ }
+ if (!(k = pat_node_get_key(ctx, pat, node))) { break; }
+ if (PAT_LEN(node) < byte_len) { break; }
+ if ((flags & GRN_CURSOR_SIZE_BY_BIT)
+ ? !bitcmp(k, key, 0, key_size)
+ : !memcmp(k, key, key_size)) {
+ if (c0 < ch) {
+ if (flags & GRN_CURSOR_DESCENDING) {
+ if ((ch > len - 1) || !(flags & GRN_CURSOR_GT)) {
+ push(c, node->lr[0], ch);
+ }
+ push(c, node->lr[1], ch);
+ } else {
+ push(c, node->lr[1], ch);
+ if ((ch > len - 1) || !(flags & GRN_CURSOR_GT)) {
+ push(c, node->lr[0], ch);
+ }
+ }
+ } else {
+ if (PAT_LEN(node) * 16 > len || !(flags & GRN_CURSOR_GT)) {
+ push(c, id, ch);
+ }
+ }
+ }
+ break;
+ }
+ return GRN_SUCCESS;
+}
+
+inline static grn_rc
+set_cursor_near(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
+ uint32_t min_size, const void *key, int flags)
+{
+ grn_id id;
+ pat_node *node;
+ const uint8_t *k;
+ int r, check = -1, ch;
+ uint32_t min = min_size * 16;
+ uint8_t keybuf[MAX_FIXED_KEY_SIZE];
+ KEY_ENCODE(pat, keybuf, key, pat->key_size);
+ PAT_AT(pat, 0, node);
+ for (id = node->lr[1]; id;) {
+ PAT_AT(pat, id, node);
+ if (!node) { return GRN_FILE_CORRUPT; }
+ ch = PAT_CHK(node);
+ if (ch <= check) {
+ if (check >= min) { push(c, id, check); }
+ break;
+ }
+ if ((check += 2) < ch) {
+ if (!(k = pat_node_get_key(ctx, pat, node))) { return GRN_FILE_CORRUPT; }
+ if ((r = bitcmp(key, k, check >> 1, (ch - check) >> 1))) {
+ if (ch >= min) {
+ push(c, node->lr[1], ch);
+ push(c, node->lr[0], ch);
+ }
+ break;
+ }
+ }
+ check = ch;
+ if (nth_bit((uint8_t *)key, check, pat->key_size)) {
+ if (check >= min) { push(c, node->lr[0], check); }
+ id = node->lr[1];
+ } else {
+ if (check >= min) { push(c, node->lr[1], check); }
+ id = node->lr[0];
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+inline static grn_rc
+set_cursor_common_prefix(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
+ uint32_t min_size, const void *key, uint32_t key_size, int flags)
+{
+ grn_id id;
+ pat_node *node;
+ const uint8_t *k;
+ int check = -1, ch;
+ uint32_t len = key_size * 16;
+ uint8_t keybuf[MAX_FIXED_KEY_SIZE];
+ KEY_ENCODE(pat, keybuf, key, key_size);
+ PAT_AT(pat, 0, node);
+ for (id = node->lr[1]; id;) {
+ PAT_AT(pat, id, node);
+ if (!node) { return GRN_FILE_CORRUPT; }
+ ch = PAT_CHK(node);
+ if (ch <= check) {
+ if (!(k = pat_node_get_key(ctx, pat, node))) { return GRN_FILE_CORRUPT; }
+ {
+ uint32_t l = PAT_LEN(node);
+ if (min_size <= l && l <= key_size) {
+ if (!memcmp(key, k, l)) { push(c, id, check); }
+ }
+ }
+ break;
+ }
+ check = ch;
+ if (len <= check) { break; }
+ if (check & 1) {
+ grn_id id0 = node->lr[0];
+ pat_node *node0;
+ PAT_AT(pat, id0, node0);
+ if (!node0) { return GRN_FILE_CORRUPT; }
+ if (!(k = pat_node_get_key(ctx, pat, node0))) { return GRN_FILE_CORRUPT; }
+ {
+ uint32_t l = PAT_LEN(node0);
+ if (memcmp(key, k, l)) { break; }
+ if (min_size <= l) {
+ push(c, id0, check);
+ }
+ }
+ id = node->lr[1];
+ } else {
+ id = node->lr[nth_bit((uint8_t *)key, check, len)];
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+inline static grn_rc
+set_cursor_ascend(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
+ const void *key, uint32_t key_size, int flags)
+{
+ grn_id id;
+ pat_node *node;
+ const uint8_t *k;
+ int r, check = -1, ch, c2;
+ uint32_t len = key_size * 16;
+ uint8_t keybuf[MAX_FIXED_KEY_SIZE];
+ KEY_ENCODE(pat, keybuf, key, key_size);
+ PAT_AT(pat, 0, node);
+ for (id = node->lr[1]; id;) {
+ PAT_AT(pat, id, node);
+ if (!node) { return GRN_FILE_CORRUPT; }
+ ch = PAT_CHK(node);
+ if (ch <= check) {
+ if (!(k = pat_node_get_key(ctx, pat, node))) { return GRN_FILE_CORRUPT; }
+ {
+ uint32_t l = PAT_LEN(node);
+ if (l == key_size) {
+ if (flags & GRN_CURSOR_GT) {
+ if (memcmp(key, k, l) < 0) { push(c, id, check); }
+ } else {
+ if (memcmp(key, k, l) <= 0) { push(c, id, check); }
+ }
+ } else if (l < key_size) {
+ if (memcmp(key, k, l) < 0) { push(c, id, check); }
+ } else {
+ if (memcmp(key, k, key_size) <= 0) { push(c, id, check); }
+ }
+ }
+ break;
+ }
+ c2 = len < ch ? len : ch;
+ if ((check += 2) < c2) {
+ if (!(k = pat_node_get_key(ctx, pat, node))) { return GRN_FILE_CORRUPT; }
+ if ((r = bitcmp(key, k, check >> 1, ((c2 + 1) >> 1) - (check >> 1)))) {
+ if (r < 0) {
+ push(c, node->lr[1], ch);
+ push(c, node->lr[0], ch);
+ }
+ break;
+ }
+ }
+ check = ch;
+ if (len <= check) {
+ push(c, node->lr[1], ch);
+ push(c, node->lr[0], ch);
+ break;
+ }
+ if (check & 1) {
+ if (check + 1 < len) {
+ id = node->lr[1];
+ } else {
+ push(c, node->lr[1], check);
+ id = node->lr[0];
+ }
+ } else {
+ if (nth_bit((uint8_t *)key, check, len)) {
+ id = node->lr[1];
+ } else {
+ push(c, node->lr[1], check);
+ id = node->lr[0];
+ }
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+inline static grn_rc
+set_cursor_descend(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
+ const void *key, uint32_t key_size, int flags)
+{
+ grn_id id;
+ pat_node *node;
+ const uint8_t *k;
+ int r, check = -1, ch, c2;
+ uint32_t len = key_size * 16;
+ uint8_t keybuf[MAX_FIXED_KEY_SIZE];
+ KEY_ENCODE(pat, keybuf, key, key_size);
+ PAT_AT(pat, 0, node);
+ for (id = node->lr[1]; id;) {
+ PAT_AT(pat, id, node);
+ if (!node) { return GRN_FILE_CORRUPT; }
+ ch = PAT_CHK(node);
+ if (ch <= check) {
+ if (!(k = pat_node_get_key(ctx, pat, node))) { return GRN_FILE_CORRUPT; }
+ {
+ uint32_t l = PAT_LEN(node);
+ if (l <= key_size) {
+ if ((flags & GRN_CURSOR_LT) && l == key_size) {
+ if (memcmp(key, k, l) > 0) { push(c, id, check); }
+ } else {
+ if (memcmp(key, k, l) >= 0) { push(c, id, check); }
+ }
+ } else {
+ if (memcmp(key, k, key_size) > 0) { push(c, id, check); }
+ }
+ }
+ break;
+ }
+ c2 = len < ch ? len : ch;
+ if ((check += 2) < c2) {
+ if (!(k = pat_node_get_key(ctx, pat, node))) { return GRN_FILE_CORRUPT; }
+ if ((r = bitcmp(key, k, check >> 1, ((c2 + 1) >> 1) - (check >> 1)))) {
+ if (r >= 0) {
+ push(c, node->lr[0], ch);
+ push(c, node->lr[1], ch);
+ }
+ break;
+ }
+ }
+ check = ch;
+ if (len <= check) { break; }
+ if (check & 1) {
+ if (check + 1 < len) {
+ push(c, node->lr[0], check);
+ id = node->lr[1];
+ } else {
+ id = node->lr[0];
+ }
+ } else {
+ if (nth_bit((uint8_t *)key, check, len)) {
+ push(c, node->lr[0], check);
+ id = node->lr[1];
+ } else {
+ id = node->lr[0];
+ }
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+static grn_pat_cursor *
+grn_pat_cursor_open_by_id(grn_ctx *ctx, grn_pat *pat,
+ const void *min, uint32_t min_size,
+ const void *max, uint32_t max_size,
+ int offset, int limit, int flags)
+{
+ int dir;
+ grn_pat_cursor *c;
+ if (!pat || !ctx) { return NULL; }
+ if (!(c = GRN_MALLOCN(grn_pat_cursor, 1))) { return NULL; }
+ GRN_DB_OBJ_SET_TYPE(c, GRN_CURSOR_TABLE_PAT_KEY);
+ c->pat = pat;
+ c->ctx = ctx;
+ c->obj.header.flags = flags;
+ c->obj.header.domain = GRN_ID_NIL;
+ c->size = 0;
+ c->sp = 0;
+ c->ss = NULL;
+ c->tail = 0;
+ if (flags & GRN_CURSOR_DESCENDING) {
+ dir = -1;
+ if (max) {
+ if (!(c->curr_rec = grn_pat_get(ctx, pat, max, max_size, NULL))) {
+ c->tail = GRN_ID_NIL;
+ goto exit;
+ }
+ if (!(flags & GRN_CURSOR_LT)) { c->curr_rec++; }
+ } else {
+ c->curr_rec = pat->header->curr_rec + 1;
+ }
+ if (min) {
+ if (!(c->tail = grn_pat_get(ctx, pat, min, min_size, NULL))) {
+ c->curr_rec = GRN_ID_NIL;
+ goto exit;
+ }
+ if ((flags & GRN_CURSOR_GT)) { c->tail++; }
+ } else {
+ c->tail = GRN_ID_NIL + 1;
+ }
+ if (c->curr_rec < c->tail) { c->tail = c->curr_rec; }
+ } else {
+ dir = 1;
+ if (min) {
+ if (!(c->curr_rec = grn_pat_get(ctx, pat, min, min_size, NULL))) {
+ c->tail = GRN_ID_NIL;
+ goto exit;
+ }
+ if (!(flags & GRN_CURSOR_GT)) { c->curr_rec--; }
+ } else {
+ c->curr_rec = GRN_ID_NIL;
+ }
+ if (max) {
+ if (!(c->tail = grn_pat_get(ctx, pat, max, max_size, NULL))) {
+ c->curr_rec = GRN_ID_NIL;
+ goto exit;
+ }
+ if ((flags & GRN_CURSOR_LT)) { c->tail--; }
+ } else {
+ c->tail = pat->header->curr_rec;
+ }
+ if (c->tail < c->curr_rec) { c->tail = c->curr_rec; }
+ }
+ if (pat->header->n_garbages) {
+ while (offset && c->curr_rec != c->tail) {
+ uint32_t key_size;
+ const void *key;
+ c->curr_rec += dir;
+ key = _grn_pat_key(ctx, pat, c->curr_rec, &key_size);
+ if (_grn_pat_get(ctx, pat, key, key_size, NULL) == c->curr_rec) {
+ offset--;
+ }
+ }
+ } else {
+ if ((dir * (c->tail - c->curr_rec)) < offset) {
+ c->curr_rec = c->tail;
+ } else {
+ c->curr_rec += dir * offset;
+ }
+ }
+ c->rest = (limit < 0) ? GRN_ID_MAX : limit;
+exit :
+ return c;
+}
+
+static grn_rc set_cursor_rk(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
+ const void *key, uint32_t key_size, int flags);
+
+grn_pat_cursor *
+grn_pat_cursor_open(grn_ctx *ctx, grn_pat *pat,
+ const void *min, uint32_t min_size,
+ const void *max, uint32_t max_size,
+ int offset, int limit, int flags)
+{
+ grn_id id;
+ pat_node *node;
+ grn_pat_cursor *c;
+ if (!pat || !ctx) { return NULL; }
+ if ((flags & GRN_CURSOR_BY_ID)) {
+ return grn_pat_cursor_open_by_id(ctx, pat, min, min_size, max, max_size,
+ offset, limit, flags);
+ }
+ if (!(c = GRN_MALLOCN(grn_pat_cursor, 1))) { return NULL; }
+ GRN_DB_OBJ_SET_TYPE(c, GRN_CURSOR_TABLE_PAT_KEY);
+ c->pat = pat;
+ c->ctx = ctx;
+ c->size = 0;
+ c->sp = 0;
+ c->ss = NULL;
+ c->tail = 0;
+ c->rest = GRN_ID_MAX;
+ c->curr_rec = GRN_ID_NIL;
+ c->obj.header.domain = GRN_ID_NIL;
+ if (flags & GRN_CURSOR_PREFIX) {
+ if (max && max_size) {
+ if ((pat->obj.header.flags & GRN_OBJ_KEY_VAR_SIZE)) {
+ set_cursor_common_prefix(ctx, pat, c, min_size, max, max_size, flags);
+ } else {
+ set_cursor_near(ctx, pat, c, min_size, max, flags);
+ }
+ goto exit;
+ } else {
+ if (min && min_size) {
+ if (flags & GRN_CURSOR_RK) {
+ set_cursor_rk(ctx, pat, c, min, min_size, flags);
+ } else {
+ set_cursor_prefix(ctx, pat, c, min, min_size, flags);
+ }
+ goto exit;
+ }
+ }
+ }
+ if (flags & GRN_CURSOR_DESCENDING) {
+ if (min && min_size) {
+ set_cursor_ascend(ctx, pat, c, min, min_size, flags);
+ c->obj.header.flags = GRN_CURSOR_ASCENDING;
+ c->tail = grn_pat_cursor_next(ctx, c);
+ c->sp = 0;
+ if (!c->tail) { goto exit; }
+ }
+ if (max && max_size) {
+ set_cursor_descend(ctx, pat, c, max, max_size, flags);
+ } else {
+ PAT_AT(pat, 0, node);
+ if (!node) {
+ grn_pat_cursor_close(ctx, c);
+ return NULL;
+ }
+ if ((id = node->lr[1])) {
+ PAT_AT(pat, id, node);
+ if (node) {
+ int ch = PAT_CHK(node);
+ push(c, node->lr[0], ch);
+ push(c, node->lr[1], ch);
+ }
+ }
+ }
+ } else {
+ if (max && max_size) {
+ set_cursor_descend(ctx, pat, c, max, max_size, flags);
+ c->obj.header.flags = GRN_CURSOR_DESCENDING;
+ c->tail = grn_pat_cursor_next(ctx, c);
+ c->sp = 0;
+ if (!c->tail) { goto exit; }
+ }
+ if (min && min_size) {
+ set_cursor_ascend(ctx, pat, c, min, min_size, flags);
+ } else {
+ PAT_AT(pat, 0, node);
+ if (!node) {
+ grn_pat_cursor_close(ctx, c);
+ return NULL;
+ }
+ if ((id = node->lr[1])) {
+ PAT_AT(pat, id, node);
+ if (node) {
+ int ch = PAT_CHK(node);
+ push(c, node->lr[1], ch);
+ push(c, node->lr[0], ch);
+ }
+ }
+ }
+ }
+exit :
+ c->obj.header.flags = flags;
+ c->curr_rec = GRN_ID_NIL;
+ while (offset--) { grn_pat_cursor_next(ctx, c); }
+ c->rest = (limit < 0) ? GRN_ID_MAX : limit;
+ return c;
+}
+
+int
+grn_pat_cursor_get_key(grn_ctx *ctx, grn_pat_cursor *c, void **key)
+{
+ *key = c->curr_key;
+ return grn_pat_get_key(ctx, c->pat, c->curr_rec, *key, GRN_TABLE_MAX_KEY_SIZE);
+}
+
+int
+grn_pat_cursor_get_value(grn_ctx *ctx, grn_pat_cursor *c, void **value)
+{
+ int value_size = (int)c->pat->value_size;
+ if (value_size) {
+ byte *v = (byte *)sis_at(ctx, c->pat, c->curr_rec);
+ if (v) {
+ if (c->pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) {
+ *value = v + sizeof(sis_node);
+ } else {
+ *value = v;
+ }
+ } else {
+ *value = NULL;
+ }
+ }
+ return value_size;
+}
+
+int
+grn_pat_cursor_get_key_value(grn_ctx *ctx, grn_pat_cursor *c,
+ void **key, uint32_t *key_size, void **value)
+{
+ int value_size = (int)c->pat->value_size;
+ if (key_size) {
+ *key_size = (uint32_t) grn_pat_get_key(ctx, c->pat, c->curr_rec, c->curr_key,
+ GRN_TABLE_MAX_KEY_SIZE);
+ if (key) { *key = c->curr_key; }
+ }
+ if (value && value_size) {
+ byte *v = (byte *)sis_at(ctx, c->pat, c->curr_rec);
+ if (v) {
+ if (c->pat->obj.header.flags & GRN_OBJ_KEY_WITH_SIS) {
+ *value = v + sizeof(sis_node);
+ } else {
+ *value = v;
+ }
+ } else {
+ *value = NULL;
+ }
+ }
+ return value_size;
+}
+
+grn_rc
+grn_pat_cursor_set_value(grn_ctx *ctx, grn_pat_cursor *c,
+ const void *value, int flags)
+{
+ return grn_pat_set_value(ctx, c->pat, c->curr_rec, value, flags);
+}
+
+grn_rc
+grn_pat_cursor_delete(grn_ctx *ctx, grn_pat_cursor *c,
+ grn_table_delete_optarg *optarg)
+{
+ return grn_pat_delete_by_id(ctx, c->pat, c->curr_rec, optarg);
+}
+
+void
+grn_pat_check(grn_ctx *ctx, grn_pat *pat)
+{
+ char buf[8];
+ struct grn_pat_header *h = pat->header;
+ GRN_OUTPUT_ARRAY_OPEN("RESULT", 1);
+ GRN_OUTPUT_MAP_OPEN("SUMMARY", 23);
+ GRN_OUTPUT_CSTR("flags");
+ grn_itoh(h->flags, buf, 8);
+ GRN_OUTPUT_STR(buf, 8);
+ GRN_OUTPUT_CSTR("key size");
+ GRN_OUTPUT_INT64(h->key_size);
+ GRN_OUTPUT_CSTR("value_size");
+ GRN_OUTPUT_INT64(h->value_size);
+ GRN_OUTPUT_CSTR("tokenizer");
+ GRN_OUTPUT_INT64(h->tokenizer);
+ GRN_OUTPUT_CSTR("normalizer");
+ GRN_OUTPUT_INT64(h->normalizer);
+ GRN_OUTPUT_CSTR("n_entries");
+ GRN_OUTPUT_INT64(h->n_entries);
+ GRN_OUTPUT_CSTR("curr_rec");
+ GRN_OUTPUT_INT64(h->curr_rec);
+ GRN_OUTPUT_CSTR("curr_key");
+ GRN_OUTPUT_INT64(h->curr_key);
+ GRN_OUTPUT_CSTR("curr_del");
+ GRN_OUTPUT_INT64(h->curr_del);
+ GRN_OUTPUT_CSTR("curr_del2");
+ GRN_OUTPUT_INT64(h->curr_del2);
+ GRN_OUTPUT_CSTR("curr_del3");
+ GRN_OUTPUT_INT64(h->curr_del3);
+ GRN_OUTPUT_CSTR("n_garbages");
+ GRN_OUTPUT_INT64(h->n_garbages);
+ GRN_OUTPUT_MAP_CLOSE();
+ GRN_OUTPUT_ARRAY_CLOSE();
+}
+
+static void
+grn_pat_inspect_check(grn_ctx *ctx, grn_obj *buf, int check)
+{
+ GRN_TEXT_PUTS(ctx, buf, "{");
+ grn_text_lltoa(ctx, buf, check >> 4);
+ GRN_TEXT_PUTS(ctx, buf, ",");
+ grn_text_lltoa(ctx, buf, (check >> 1) & 7);
+ GRN_TEXT_PUTS(ctx, buf, ",");
+ grn_text_lltoa(ctx, buf, check & 1);
+ GRN_TEXT_PUTS(ctx, buf, "}");
+}
+
+static void
+grn_pat_inspect_node(grn_ctx *ctx, grn_pat *pat, grn_id id, int check,
+ grn_obj *key_buf, int indent, const char *prefix,
+ grn_obj *buf)
+{
+ pat_node *node = NULL;
+ int i, c;
+
+ PAT_AT(pat, id, node);
+ c = PAT_CHK(node);
+
+ for (i = 0; i < indent; i++) {
+ GRN_TEXT_PUTC(ctx, buf, ' ');
+ }
+ GRN_TEXT_PUTS(ctx, buf, prefix);
+ grn_text_lltoa(ctx, buf, id);
+
+ if (c > check) {
+ int key_size;
+ uint8_t *key;
+
+ key_size = PAT_LEN(node);
+ GRN_BULK_REWIND(key_buf);
+ grn_bulk_space(ctx, key_buf, key_size);
+ grn_pat_get_key(ctx, pat, id, GRN_BULK_HEAD(key_buf), key_size);
+ GRN_TEXT_PUTS(ctx, buf, "(");
+ grn_inspect(ctx, buf, key_buf);
+ GRN_TEXT_PUTS(ctx, buf, ")");
+
+ grn_pat_inspect_check(ctx, buf, c);
+
+ GRN_TEXT_PUTS(ctx, buf, "[");
+ key = pat_node_get_key(ctx, pat, node);
+ for (i = 0; i < key_size; i++) {
+ int j;
+ uint8_t byte = key[i];
+ if (i != 0) {
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ }
+ for (j = 0; j < 8; j++) {
+ grn_text_lltoa(ctx, buf, (byte >> (7 - j)) & 1);
+ }
+ }
+ GRN_TEXT_PUTS(ctx, buf, "]");
+ }
+
+ if (c > check) {
+ GRN_TEXT_PUTS(ctx, buf, "\n");
+ grn_pat_inspect_node(ctx, pat, node->lr[0], c, key_buf,
+ indent + 2, "L:", buf);
+ GRN_TEXT_PUTS(ctx, buf, "\n");
+ grn_pat_inspect_node(ctx, pat, node->lr[1], c, key_buf,
+ indent + 2, "R:", buf);
+ }
+}
+
+void
+grn_pat_inspect_nodes(grn_ctx *ctx, grn_pat *pat, grn_obj *buf)
+{
+ pat_node *node;
+ grn_obj key_buf;
+
+ GRN_TEXT_PUTS(ctx, buf, "{");
+ PAT_AT(pat, GRN_ID_NIL, node);
+ if (node->lr[1]) {
+ GRN_TEXT_PUTS(ctx, buf, "\n");
+ GRN_OBJ_INIT(&key_buf, GRN_BULK, 0, pat->obj.header.domain);
+ grn_pat_inspect_node(ctx, pat, node->lr[1], -1, &key_buf, 0, "", buf);
+ GRN_OBJ_FIN(ctx, &key_buf);
+ GRN_TEXT_PUTS(ctx, buf, "\n");
+ }
+ GRN_TEXT_PUTS(ctx, buf, "}");
+}
+
+static void
+grn_pat_cursor_inspect_entries(grn_ctx *ctx, grn_pat_cursor *c, grn_obj *buf)
+{
+ int i;
+ GRN_TEXT_PUTS(ctx, buf, "[");
+ for (i = 0; i < c->sp; i++) {
+ grn_pat_cursor_entry *e = c->ss + i;
+ if (i != 0) {
+ GRN_TEXT_PUTS(ctx, buf, ", ");
+ }
+ GRN_TEXT_PUTS(ctx, buf, "[");
+ grn_text_lltoa(ctx, buf, e->id);
+ GRN_TEXT_PUTS(ctx, buf, ",");
+ grn_pat_inspect_check(ctx, buf, e->check);
+ GRN_TEXT_PUTS(ctx, buf, "]");
+ }
+ GRN_TEXT_PUTS(ctx, buf, "]");
+}
+
+void
+grn_pat_cursor_inspect(grn_ctx *ctx, grn_pat_cursor *c, grn_obj *buf)
+{
+ GRN_TEXT_PUTS(ctx, buf, "#<cursor:pat:");
+ grn_inspect_name(ctx, buf, (grn_obj *)(c->pat));
+
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ GRN_TEXT_PUTS(ctx, buf, "current:");
+ grn_text_lltoa(ctx, buf, c->curr_rec);
+
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ GRN_TEXT_PUTS(ctx, buf, "tail:");
+ grn_text_lltoa(ctx, buf, c->tail);
+
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ GRN_TEXT_PUTS(ctx, buf, "flags:");
+ if (c->obj.header.flags & GRN_CURSOR_PREFIX) {
+ GRN_TEXT_PUTS(ctx, buf, "prefix");
+ } else {
+ if (c->obj.header.flags & GRN_CURSOR_DESCENDING) {
+ GRN_TEXT_PUTS(ctx, buf, "descending");
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "ascending");
+ }
+ GRN_TEXT_PUTS(ctx, buf, "|");
+ if (c->obj.header.flags & GRN_CURSOR_GT) {
+ GRN_TEXT_PUTS(ctx, buf, "greater-than");
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "greater");
+ }
+ GRN_TEXT_PUTS(ctx, buf, "|");
+ if (c->obj.header.flags & GRN_CURSOR_LT) {
+ GRN_TEXT_PUTS(ctx, buf, "less-than");
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "less");
+ }
+ if (c->obj.header.flags & GRN_CURSOR_BY_ID) {
+ GRN_TEXT_PUTS(ctx, buf, "|by-id");
+ }
+ if (c->obj.header.flags & GRN_CURSOR_BY_KEY) {
+ GRN_TEXT_PUTS(ctx, buf, "|by-key");
+ }
+ }
+
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ GRN_TEXT_PUTS(ctx, buf, "rest:");
+ grn_text_lltoa(ctx, buf, c->rest);
+
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ GRN_TEXT_PUTS(ctx, buf, "entries:");
+ grn_pat_cursor_inspect_entries(ctx, c, buf);
+
+ GRN_TEXT_PUTS(ctx, buf, ">");
+}
+
+typedef struct {
+ uint8_t code;
+ uint8_t next;
+ uint8_t emit;
+ uint8_t attr;
+} rk_tree_node;
+
+static uint16_t rk_str_idx[] = {
+ 0x0003, 0x0006, 0x0009, 0x000c, 0x0012, 0x0015, 0x0018, 0x001e, 0x0024, 0x002a,
+ 0x0030, 0x0036, 0x003c, 0x0042, 0x0048, 0x004e, 0x0054, 0x005a, 0x0060, 0x0066,
+ 0x006c, 0x0072, 0x0078, 0x007e, 0x0084, 0x008a, 0x0090, 0x0096, 0x009c, 0x00a2,
+ 0x00a8, 0x00ae, 0x00b4, 0x00ba, 0x00c0, 0x00c3, 0x00c6, 0x00c9, 0x00cc, 0x00cf,
+ 0x00d2, 0x00d5, 0x00db, 0x00e1, 0x00e7, 0x00ea, 0x00f0, 0x00f6, 0x00fc, 0x00ff,
+ 0x0105, 0x0108, 0x010e, 0x0111, 0x0114, 0x0117, 0x011a, 0x011d, 0x0120, 0x0123,
+ 0x0129, 0x012f, 0x0135, 0x013b, 0x013e, 0x0144, 0x014a, 0x0150, 0x0156, 0x0159,
+ 0x015c, 0x015f, 0x0162, 0x0165, 0x0168, 0x016b, 0x016e, 0x0171, 0x0177, 0x017d,
+ 0x0183, 0x0189, 0x018c, 0x0192, 0x0198, 0x019e, 0x01a1, 0x01a4, 0x01aa, 0x01b0,
+ 0x01b6, 0x01bc, 0x01bf, 0x01c2, 0x01c8, 0x01ce, 0x01d1, 0x01d7, 0x01dd, 0x01e0,
+ 0x01e6, 0x01e9, 0x01ef, 0x01f2, 0x01f5, 0x01fb, 0x0201, 0x0207, 0x020d, 0x0213,
+ 0x0216, 0x0219, 0x021c, 0x021f, 0x0222, 0x0225, 0x0228, 0x022e, 0x0234, 0x023a,
+ 0x023d, 0x0243, 0x0249, 0x024f, 0x0252, 0x0258, 0x025e, 0x0264, 0x0267, 0x026d,
+ 0x0273, 0x0279, 0x027f, 0x0285, 0x0288, 0x028b, 0x028e, 0x0291, 0x0294, 0x0297,
+ 0x029a, 0x029d, 0x02a0, 0x02a3, 0x02a9, 0x02af, 0x02b5, 0x02b8, 0x02bb, 0x02be,
+ 0x02c1, 0x02c4, 0x02c7, 0x02ca, 0x02cd, 0x02d0, 0x02d3, 0x02d6, 0x02dc, 0x02e2,
+ 0x02e8, 0x02eb, 0x02ee, 0x02f1, 0x02f4, 0x02f7, 0x02fa, 0x02fd, 0x0300, 0x0303,
+ 0x0309, 0x030c, 0x0312, 0x0318, 0x031e, 0x0324, 0x0327, 0x032a, 0x032d
+};
+static char rk_str[] = {
+ 0xe3, 0x82, 0xa1, 0xe3, 0x82, 0xa2, 0xe3, 0x82, 0xa3, 0xe3, 0x82, 0xa4, 0xe3,
+ 0x82, 0xa4, 0xe3, 0x82, 0xa7, 0xe3, 0x82, 0xa5, 0xe3, 0x82, 0xa6, 0xe3, 0x82,
+ 0xa6, 0xe3, 0x82, 0xa2, 0xe3, 0x82, 0xa6, 0xe3, 0x82, 0xa3, 0xe3, 0x82, 0xa6,
+ 0xe3, 0x82, 0xa4, 0xe3, 0x82, 0xa6, 0xe3, 0x82, 0xa6, 0xe3, 0x82, 0xa6, 0xe3,
+ 0x82, 0xa7, 0xe3, 0x82, 0xa6, 0xe3, 0x82, 0xa8, 0xe3, 0x82, 0xa6, 0xe3, 0x82,
+ 0xaa, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa0, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa1,
+ 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa2, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa3, 0xe3,
+ 0x82, 0xa6, 0xe3, 0x83, 0xa4, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa5, 0xe3, 0x82,
+ 0xa6, 0xe3, 0x83, 0xa6, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa7, 0xe3, 0x82, 0xa6,
+ 0xe3, 0x83, 0xa8, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xa9, 0xe3, 0x82, 0xa6, 0xe3,
+ 0x83, 0xaa, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xab, 0xe3, 0x82, 0xa6, 0xe3, 0x83,
+ 0xac, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xad, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xae,
+ 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xaf, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xb0, 0xe3,
+ 0x82, 0xa6, 0xe3, 0x83, 0xb1, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xb2, 0xe3, 0x82,
+ 0xa6, 0xe3, 0x83, 0xb3, 0xe3, 0x82, 0xa6, 0xe3, 0x83, 0xbc, 0xe3, 0x82, 0xa7,
+ 0xe3, 0x82, 0xa8, 0xe3, 0x82, 0xa9, 0xe3, 0x82, 0xaa, 0xe3, 0x82, 0xab, 0xe3,
+ 0x82, 0xac, 0xe3, 0x82, 0xad, 0xe3, 0x82, 0xad, 0xe3, 0x83, 0xa3, 0xe3, 0x82,
+ 0xad, 0xe3, 0x83, 0xa5, 0xe3, 0x82, 0xad, 0xe3, 0x83, 0xa7, 0xe3, 0x82, 0xae,
+ 0xe3, 0x82, 0xae, 0xe3, 0x83, 0xa3, 0xe3, 0x82, 0xae, 0xe3, 0x83, 0xa5, 0xe3,
+ 0x82, 0xae, 0xe3, 0x83, 0xa7, 0xe3, 0x82, 0xaf, 0xe3, 0x82, 0xaf, 0xe3, 0x82,
+ 0xa1, 0xe3, 0x82, 0xb0, 0xe3, 0x82, 0xb0, 0xe3, 0x82, 0xa1, 0xe3, 0x82, 0xb1,
+ 0xe3, 0x82, 0xb2, 0xe3, 0x82, 0xb3, 0xe3, 0x82, 0xb4, 0xe3, 0x82, 0xb5, 0xe3,
+ 0x82, 0xb6, 0xe3, 0x82, 0xb7, 0xe3, 0x82, 0xb7, 0xe3, 0x82, 0xa7, 0xe3, 0x82,
+ 0xb7, 0xe3, 0x83, 0xa3, 0xe3, 0x82, 0xb7, 0xe3, 0x83, 0xa5, 0xe3, 0x82, 0xb7,
+ 0xe3, 0x83, 0xa7, 0xe3, 0x82, 0xb8, 0xe3, 0x82, 0xb8, 0xe3, 0x82, 0xa7, 0xe3,
+ 0x82, 0xb8, 0xe3, 0x83, 0xa3, 0xe3, 0x82, 0xb8, 0xe3, 0x83, 0xa5, 0xe3, 0x82,
+ 0xb8, 0xe3, 0x83, 0xa7, 0xe3, 0x82, 0xb9, 0xe3, 0x82, 0xba, 0xe3, 0x82, 0xbb,
+ 0xe3, 0x82, 0xbc, 0xe3, 0x82, 0xbd, 0xe3, 0x82, 0xbe, 0xe3, 0x82, 0xbf, 0xe3,
+ 0x83, 0x80, 0xe3, 0x83, 0x81, 0xe3, 0x83, 0x81, 0xe3, 0x82, 0xa7, 0xe3, 0x83,
+ 0x81, 0xe3, 0x83, 0xa3, 0xe3, 0x83, 0x81, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x81,
+ 0xe3, 0x83, 0xa7, 0xe3, 0x83, 0x82, 0xe3, 0x83, 0x82, 0xe3, 0x83, 0xa3, 0xe3,
+ 0x83, 0x82, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x82, 0xe3, 0x83, 0xa7, 0xe3, 0x83,
+ 0x83, 0xe3, 0x83, 0x84, 0xe3, 0x83, 0x84, 0xe3, 0x82, 0xa1, 0xe3, 0x83, 0x84,
+ 0xe3, 0x82, 0xa3, 0xe3, 0x83, 0x84, 0xe3, 0x82, 0xa7, 0xe3, 0x83, 0x84, 0xe3,
+ 0x82, 0xa9, 0xe3, 0x83, 0x85, 0xe3, 0x83, 0x86, 0xe3, 0x83, 0x86, 0xe3, 0x82,
+ 0xa3, 0xe3, 0x83, 0x86, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x87, 0xe3, 0x83, 0x87,
+ 0xe3, 0x82, 0xa3, 0xe3, 0x83, 0x87, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x88, 0xe3,
+ 0x83, 0x88, 0xe3, 0x82, 0xa5, 0xe3, 0x83, 0x89, 0xe3, 0x83, 0x89, 0xe3, 0x82,
+ 0xa5, 0xe3, 0x83, 0x8a, 0xe3, 0x83, 0x8b, 0xe3, 0x83, 0x8b, 0xe3, 0x82, 0xa3,
+ 0xe3, 0x83, 0x8b, 0xe3, 0x82, 0xa7, 0xe3, 0x83, 0x8b, 0xe3, 0x83, 0xa3, 0xe3,
+ 0x83, 0x8b, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x8b, 0xe3, 0x83, 0xa7, 0xe3, 0x83,
+ 0x8c, 0xe3, 0x83, 0x8d, 0xe3, 0x83, 0x8e, 0xe3, 0x83, 0x8f, 0xe3, 0x83, 0x90,
+ 0xe3, 0x83, 0x91, 0xe3, 0x83, 0x92, 0xe3, 0x83, 0x92, 0xe3, 0x83, 0xa3, 0xe3,
+ 0x83, 0x92, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x92, 0xe3, 0x83, 0xa7, 0xe3, 0x83,
+ 0x93, 0xe3, 0x83, 0x93, 0xe3, 0x83, 0xa3, 0xe3, 0x83, 0x93, 0xe3, 0x83, 0xa5,
+ 0xe3, 0x83, 0x93, 0xe3, 0x83, 0xa7, 0xe3, 0x83, 0x94, 0xe3, 0x83, 0x94, 0xe3,
+ 0x83, 0xa3, 0xe3, 0x83, 0x94, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x94, 0xe3, 0x83,
+ 0xa7, 0xe3, 0x83, 0x95, 0xe3, 0x83, 0x95, 0xe3, 0x82, 0xa1, 0xe3, 0x83, 0x95,
+ 0xe3, 0x82, 0xa3, 0xe3, 0x83, 0x95, 0xe3, 0x82, 0xa7, 0xe3, 0x83, 0x95, 0xe3,
+ 0x82, 0xa9, 0xe3, 0x83, 0x95, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0x96, 0xe3, 0x83,
+ 0x97, 0xe3, 0x83, 0x98, 0xe3, 0x83, 0x99, 0xe3, 0x83, 0x9a, 0xe3, 0x83, 0x9b,
+ 0xe3, 0x83, 0x9c, 0xe3, 0x83, 0x9d, 0xe3, 0x83, 0x9e, 0xe3, 0x83, 0x9f, 0xe3,
+ 0x83, 0x9f, 0xe3, 0x83, 0xa3, 0xe3, 0x83, 0x9f, 0xe3, 0x83, 0xa5, 0xe3, 0x83,
+ 0x9f, 0xe3, 0x83, 0xa7, 0xe3, 0x83, 0xa0, 0xe3, 0x83, 0xa1, 0xe3, 0x83, 0xa2,
+ 0xe3, 0x83, 0xa3, 0xe3, 0x83, 0xa4, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0xa6, 0xe3,
+ 0x83, 0xa7, 0xe3, 0x83, 0xa8, 0xe3, 0x83, 0xa9, 0xe3, 0x83, 0xaa, 0xe3, 0x83,
+ 0xaa, 0xe3, 0x83, 0xa3, 0xe3, 0x83, 0xaa, 0xe3, 0x83, 0xa5, 0xe3, 0x83, 0xaa,
+ 0xe3, 0x83, 0xa7, 0xe3, 0x83, 0xab, 0xe3, 0x83, 0xac, 0xe3, 0x83, 0xad, 0xe3,
+ 0x83, 0xae, 0xe3, 0x83, 0xaf, 0xe3, 0x83, 0xb0, 0xe3, 0x83, 0xb1, 0xe3, 0x83,
+ 0xb2, 0xe3, 0x83, 0xb3, 0xe3, 0x83, 0xb3, 0xe3, 0x83, 0xbc, 0xe3, 0x83, 0xb4,
+ 0xe3, 0x83, 0xb4, 0xe3, 0x82, 0xa1, 0xe3, 0x83, 0xb4, 0xe3, 0x82, 0xa3, 0xe3,
+ 0x83, 0xb4, 0xe3, 0x82, 0xa7, 0xe3, 0x83, 0xb4, 0xe3, 0x82, 0xa9, 0xe3, 0x83,
+ 0xb5, 0xe3, 0x83, 0xb6, 0xe3, 0x83, 0xbc
+};
+static uint16_t rk_tree_idx[] = {
+ 0x001b, 0x0022, 0x0025, 0x0028, 0x002d, 0x0030, 0x0039, 0x003b, 0x003c, 0x003f,
+ 0x0046, 0x0047, 0x004f, 0x0050, 0x0053, 0x005a, 0x005d, 0x0064, 0x0067, 0x006f,
+ 0x0070, 0x0073, 0x007d, 0x007f, 0x0081, 0x0082, 0x0083, 0x0088, 0x008f, 0x0092,
+ 0x00af, 0x00b5, 0x00bc, 0x00bf, 0x00c6, 0x00c9, 0x00d1, 0x00d6, 0x00da, 0x00e4,
+ 0x00e6, 0x00eb, 0x00ec, 0x00f0, 0x00f6, 0x00fc, 0x00fe, 0x0108, 0x010a, 0x010c,
+ 0x010d, 0x010e, 0x0113, 0x0118, 0x011f, 0x0123, 0x0125, 0x0164, 0x0180, 0x0183,
+ 0x0199, 0x01ad
+};
+static rk_tree_node rk_tree[] = {
+ {0x2d, 0x00, 0xb2, 0x01}, {0x61, 0x00, 0x01, 0x01}, {0x62, 0x01, 0xff, 0x01},
+ {0x63, 0x03, 0xff, 0x01}, {0x64, 0x06, 0xff, 0x01}, {0x65, 0x00, 0x24, 0x01},
+ {0x66, 0x0a, 0xff, 0x01}, {0x67, 0x0c, 0xff, 0x01}, {0x68, 0x0f, 0xff, 0x01},
+ {0x69, 0x00, 0x03, 0x01}, {0x6a, 0x11, 0xff, 0x01}, {0x6b, 0x13, 0xff, 0x01},
+ {0x6c, 0x16, 0xff, 0x01}, {0x6d, 0x1c, 0xff, 0x01}, {0x6e, 0x1e, 0xff, 0x01},
+ {0x6f, 0x00, 0x26, 0x01}, {0x70, 0x20, 0xff, 0x01}, {0x72, 0x22, 0xff, 0x01},
+ {0x73, 0x24, 0xff, 0x01}, {0x74, 0x27, 0xff, 0x01}, {0x75, 0x00, 0x06, 0x01},
+ {0x76, 0x2c, 0xff, 0x01}, {0x77, 0x2d, 0xff, 0x01}, {0x78, 0x2f, 0xff, 0x01},
+ {0x79, 0x35, 0xff, 0x01}, {0x7a, 0x36, 0xff, 0x01}, {0xe3, 0x38, 0xff, 0x01},
+ {0x61, 0x00, 0x72, 0x01}, {0x62, 0x01, 0x56, 0x01}, {0x65, 0x00, 0x89, 0x01},
+ {0x69, 0x00, 0x78, 0x01}, {0x6f, 0x00, 0x8c, 0x01}, {0x75, 0x00, 0x86, 0x01},
+ {0x79, 0x02, 0xff, 0x00}, {0x61, 0x00, 0x79, 0x01}, {0x6f, 0x00, 0x7b, 0x01},
+ {0x75, 0x00, 0x7a, 0x01}, {0x63, 0x03, 0x56, 0x01}, {0x68, 0x04, 0xff, 0x01},
+ {0x79, 0x05, 0xff, 0x01}, {0x61, 0x00, 0x4f, 0x00}, {0x65, 0x00, 0x4e, 0x00},
+ {0x69, 0x00, 0x4d, 0x01}, {0x6f, 0x00, 0x51, 0x00}, {0x75, 0x00, 0x50, 0x00},
+ {0x61, 0x00, 0x4f, 0x01}, {0x6f, 0x00, 0x51, 0x01}, {0x75, 0x00, 0x50, 0x01},
+ {0x61, 0x00, 0x4c, 0x01}, {0x64, 0x06, 0x56, 0x01}, {0x65, 0x00, 0x60, 0x01},
+ {0x68, 0x07, 0xff, 0x00}, {0x69, 0x00, 0x61, 0x00}, {0x6f, 0x00, 0x65, 0x01},
+ {0x75, 0x00, 0x5c, 0x01}, {0x77, 0x08, 0xff, 0x00}, {0x79, 0x09, 0xff, 0x01},
+ {0x69, 0x00, 0x61, 0x01}, {0x75, 0x00, 0x62, 0x01}, {0x75, 0x00, 0x66, 0x01},
+ {0x61, 0x00, 0x53, 0x01}, {0x6f, 0x00, 0x55, 0x01}, {0x75, 0x00, 0x54, 0x01},
+ {0x61, 0x00, 0x81, 0x00}, {0x65, 0x00, 0x83, 0x00}, {0x66, 0x0a, 0x56, 0x01},
+ {0x69, 0x00, 0x82, 0x00}, {0x6f, 0x00, 0x84, 0x00}, {0x75, 0x00, 0x80, 0x01},
+ {0x79, 0x0b, 0xff, 0x00}, {0x75, 0x00, 0x85, 0x01}, {0x61, 0x00, 0x28, 0x01},
+ {0x65, 0x00, 0x36, 0x01}, {0x67, 0x0c, 0x56, 0x01}, {0x69, 0x00, 0x2d, 0x01},
+ {0x6f, 0x00, 0x38, 0x01}, {0x75, 0x00, 0x33, 0x01}, {0x77, 0x0d, 0xff, 0x00},
+ {0x79, 0x0e, 0xff, 0x00}, {0x61, 0x00, 0x34, 0x01}, {0x61, 0x00, 0x2e, 0x01},
+ {0x6f, 0x00, 0x30, 0x01}, {0x75, 0x00, 0x2f, 0x01}, {0x61, 0x00, 0x71, 0x01},
+ {0x65, 0x00, 0x88, 0x01}, {0x68, 0x0f, 0x56, 0x01}, {0x69, 0x00, 0x74, 0x01},
+ {0x6f, 0x00, 0x8b, 0x01}, {0x75, 0x00, 0x80, 0x01}, {0x79, 0x10, 0xff, 0x00},
+ {0x61, 0x00, 0x75, 0x01}, {0x6f, 0x00, 0x77, 0x01}, {0x75, 0x00, 0x76, 0x01},
+ {0x61, 0x00, 0x42, 0x00}, {0x65, 0x00, 0x41, 0x00}, {0x69, 0x00, 0x40, 0x01},
+ {0x6a, 0x11, 0x56, 0x01}, {0x6f, 0x00, 0x44, 0x00}, {0x75, 0x00, 0x43, 0x00},
+ {0x79, 0x12, 0xff, 0x00}, {0x61, 0x00, 0x42, 0x01}, {0x6f, 0x00, 0x44, 0x01},
+ {0x75, 0x00, 0x43, 0x01}, {0x61, 0x00, 0x27, 0x01}, {0x65, 0x00, 0x35, 0x01},
+ {0x69, 0x00, 0x29, 0x01}, {0x6b, 0x13, 0x56, 0x01}, {0x6f, 0x00, 0x37, 0x01},
+ {0x75, 0x00, 0x31, 0x01}, {0x77, 0x14, 0xff, 0x00}, {0x79, 0x15, 0xff, 0x00},
+ {0x61, 0x00, 0x32, 0x01}, {0x61, 0x00, 0x2a, 0x01}, {0x6f, 0x00, 0x2c, 0x01},
+ {0x75, 0x00, 0x2b, 0x01}, {0x61, 0x00, 0x00, 0x01}, {0x65, 0x00, 0x23, 0x01},
+ {0x69, 0x00, 0x02, 0x01}, {0x6b, 0x17, 0xff, 0x01}, {0x6c, 0x16, 0x56, 0x01},
+ {0x6f, 0x00, 0x25, 0x01}, {0x74, 0x18, 0xff, 0x01}, {0x75, 0x00, 0x05, 0x01},
+ {0x77, 0x1a, 0xff, 0x01}, {0x79, 0x1b, 0xff, 0x01}, {0x61, 0x00, 0xb0, 0x01},
+ {0x65, 0x00, 0xb1, 0x01}, {0x73, 0x19, 0xff, 0x00}, {0x75, 0x00, 0x56, 0x01},
+ {0x75, 0x00, 0x56, 0x01}, {0x61, 0x00, 0xa4, 0x01}, {0x61, 0x00, 0x96, 0x01},
+ {0x65, 0x00, 0x23, 0x01}, {0x69, 0x00, 0x02, 0x01}, {0x6f, 0x00, 0x9a, 0x01},
+ {0x75, 0x00, 0x98, 0x01}, {0x61, 0x00, 0x8e, 0x01}, {0x65, 0x00, 0x94, 0x01},
+ {0x69, 0x00, 0x8f, 0x01}, {0x6d, 0x1c, 0x56, 0x01}, {0x6f, 0x00, 0x95, 0x01},
+ {0x75, 0x00, 0x93, 0x01}, {0x79, 0x1d, 0xff, 0x00}, {0x61, 0x00, 0x90, 0x01},
+ {0x6f, 0x00, 0x92, 0x01}, {0x75, 0x00, 0x91, 0x01}, {0x00, 0x00, 0xa9, 0x01},
+ {0x27, 0x00, 0xa9, 0x00}, {0x2d, 0x00, 0xaa, 0x00}, {0x61, 0x00, 0x67, 0x01},
+ {0x62, 0x01, 0xa9, 0x00}, {0x63, 0x03, 0xa9, 0x00}, {0x64, 0x06, 0xa9, 0x00},
+ {0x65, 0x00, 0x6f, 0x01}, {0x66, 0x0a, 0xa9, 0x00}, {0x67, 0x0c, 0xa9, 0x00},
+ {0x68, 0x0f, 0xa9, 0x00}, {0x69, 0x00, 0x68, 0x01}, {0x6a, 0x11, 0xa9, 0x00},
+ {0x6b, 0x13, 0xa9, 0x00}, {0x6c, 0x16, 0xa9, 0x00}, {0x6d, 0x1c, 0xa9, 0x00},
+ {0x6e, 0x00, 0xa9, 0x00}, {0x6f, 0x00, 0x70, 0x01}, {0x70, 0x20, 0xa9, 0x00},
+ {0x72, 0x22, 0xa9, 0x00}, {0x73, 0x24, 0xa9, 0x00}, {0x74, 0x27, 0xa9, 0x00},
+ {0x75, 0x00, 0x6e, 0x01}, {0x76, 0x2c, 0xa9, 0x00}, {0x77, 0x2d, 0xa9, 0x00},
+ {0x78, 0x2f, 0xa9, 0x00}, {0x79, 0x1f, 0xff, 0x00}, {0x7a, 0x36, 0xa9, 0x00},
+ {0xe3, 0x38, 0xa9, 0x00}, {0x00, 0x00, 0xa9, 0x01}, {0x61, 0x00, 0x6b, 0x01},
+ {0x65, 0x00, 0x6a, 0x01}, {0x69, 0x00, 0x69, 0x01}, {0x6f, 0x00, 0x6d, 0x01},
+ {0x75, 0x00, 0x6c, 0x01}, {0x61, 0x00, 0x73, 0x01}, {0x65, 0x00, 0x8a, 0x01},
+ {0x69, 0x00, 0x7c, 0x01}, {0x6f, 0x00, 0x8d, 0x01}, {0x70, 0x20, 0x56, 0x01},
+ {0x75, 0x00, 0x87, 0x01}, {0x79, 0x21, 0xff, 0x00}, {0x61, 0x00, 0x7d, 0x01},
+ {0x6f, 0x00, 0x7f, 0x01}, {0x75, 0x00, 0x7e, 0x01}, {0x61, 0x00, 0x9c, 0x01},
+ {0x65, 0x00, 0xa2, 0x01}, {0x69, 0x00, 0x9d, 0x01}, {0x6f, 0x00, 0xa3, 0x01},
+ {0x72, 0x22, 0x56, 0x01}, {0x75, 0x00, 0xa1, 0x01}, {0x79, 0x23, 0xff, 0x00},
+ {0x61, 0x00, 0x9e, 0x01}, {0x6f, 0x00, 0xa0, 0x01}, {0x75, 0x00, 0x9f, 0x01},
+ {0x61, 0x00, 0x39, 0x01}, {0x65, 0x00, 0x47, 0x01}, {0x68, 0x25, 0xff, 0x00},
+ {0x69, 0x00, 0x3b, 0x01}, {0x6f, 0x00, 0x49, 0x01}, {0x73, 0x24, 0x56, 0x01},
+ {0x75, 0x00, 0x45, 0x01}, {0x79, 0x26, 0xff, 0x00}, {0x61, 0x00, 0x3d, 0x00},
+ {0x65, 0x00, 0x3c, 0x00}, {0x69, 0x00, 0x3b, 0x01}, {0x6f, 0x00, 0x3f, 0x00},
+ {0x75, 0x00, 0x3e, 0x00}, {0x61, 0x00, 0x3d, 0x01}, {0x65, 0x00, 0x3c, 0x01},
+ {0x6f, 0x00, 0x3f, 0x01}, {0x75, 0x00, 0x3e, 0x01}, {0x61, 0x00, 0x4b, 0x01},
+ {0x65, 0x00, 0x5d, 0x01}, {0x68, 0x28, 0xff, 0x00}, {0x69, 0x00, 0x4d, 0x01},
+ {0x6f, 0x00, 0x63, 0x01}, {0x73, 0x29, 0xff, 0x00}, {0x74, 0x27, 0x56, 0x01},
+ {0x75, 0x00, 0x57, 0x01}, {0x77, 0x2a, 0xff, 0x00}, {0x79, 0x2b, 0xff, 0x00},
+ {0x69, 0x00, 0x5e, 0x01}, {0x75, 0x00, 0x5f, 0x01}, {0x61, 0x00, 0x58, 0x00},
+ {0x65, 0x00, 0x5a, 0x00}, {0x69, 0x00, 0x59, 0x00}, {0x6f, 0x00, 0x5b, 0x00},
+ {0x75, 0x00, 0x57, 0x01}, {0x75, 0x00, 0x64, 0x01}, {0x61, 0x00, 0x4f, 0x01},
+ {0x65, 0x00, 0x4e, 0x01}, {0x6f, 0x00, 0x51, 0x01}, {0x75, 0x00, 0x50, 0x01},
+ {0x61, 0x00, 0xac, 0x00}, {0x65, 0x00, 0xae, 0x00}, {0x69, 0x00, 0xad, 0x00},
+ {0x6f, 0x00, 0xaf, 0x00}, {0x75, 0x00, 0xab, 0x01}, {0x76, 0x2c, 0x56, 0x01},
+ {0x61, 0x00, 0xa5, 0x01}, {0x65, 0x00, 0x0b, 0x01}, {0x69, 0x00, 0x08, 0x01},
+ {0x6f, 0x00, 0xa8, 0x01}, {0x77, 0x2d, 0x56, 0x01}, {0x79, 0x2e, 0xff, 0x01},
+ {0x65, 0x00, 0xa7, 0x01}, {0x69, 0x00, 0xa6, 0x01}, {0x61, 0x00, 0x00, 0x01},
+ {0x65, 0x00, 0x23, 0x01}, {0x69, 0x00, 0x02, 0x01}, {0x6b, 0x30, 0xff, 0x01},
+ {0x6f, 0x00, 0x25, 0x01}, {0x74, 0x31, 0xff, 0x01}, {0x75, 0x00, 0x05, 0x01},
+ {0x77, 0x33, 0xff, 0x01}, {0x78, 0x2f, 0x56, 0x01}, {0x79, 0x34, 0xff, 0x01},
+ {0x61, 0x00, 0xb0, 0x01}, {0x65, 0x00, 0xb1, 0x01}, {0x73, 0x32, 0xff, 0x00},
+ {0x75, 0x00, 0x56, 0x01}, {0x75, 0x00, 0x56, 0x01}, {0x61, 0x00, 0xa4, 0x01},
+ {0x61, 0x00, 0x96, 0x01}, {0x65, 0x00, 0x23, 0x01}, {0x69, 0x00, 0x02, 0x01},
+ {0x6f, 0x00, 0x9a, 0x01}, {0x75, 0x00, 0x98, 0x01}, {0x61, 0x00, 0x97, 0x01},
+ {0x65, 0x00, 0x04, 0x01}, {0x6f, 0x00, 0x9b, 0x01}, {0x75, 0x00, 0x99, 0x01},
+ {0x79, 0x35, 0x56, 0x01}, {0x61, 0x00, 0x3a, 0x01}, {0x65, 0x00, 0x48, 0x01},
+ {0x69, 0x00, 0x40, 0x01}, {0x6f, 0x00, 0x4a, 0x01}, {0x75, 0x00, 0x46, 0x01},
+ {0x79, 0x37, 0xff, 0x00}, {0x7a, 0x36, 0x56, 0x01}, {0x61, 0x00, 0x42, 0x01},
+ {0x65, 0x00, 0x41, 0x01}, {0x6f, 0x00, 0x44, 0x01}, {0x75, 0x00, 0x43, 0x01},
+ {0x81, 0x39, 0xff, 0x01}, {0x82, 0x3d, 0xff, 0x01}, {0x81, 0x00, 0x00, 0x01},
+ {0x82, 0x00, 0x01, 0x01}, {0x83, 0x00, 0x02, 0x01}, {0x84, 0x00, 0x03, 0x01},
+ {0x85, 0x00, 0x05, 0x01}, {0x86, 0x3a, 0xff, 0x01}, {0x87, 0x00, 0x23, 0x01},
+ {0x88, 0x00, 0x24, 0x01}, {0x89, 0x00, 0x25, 0x01}, {0x8a, 0x00, 0x26, 0x01},
+ {0x8b, 0x00, 0x27, 0x01}, {0x8c, 0x00, 0x28, 0x01}, {0x8d, 0x00, 0x29, 0x01},
+ {0x8e, 0x00, 0x2d, 0x01}, {0x8f, 0x00, 0x31, 0x01}, {0x90, 0x00, 0x33, 0x01},
+ {0x91, 0x00, 0x35, 0x01}, {0x92, 0x00, 0x36, 0x01}, {0x93, 0x00, 0x37, 0x01},
+ {0x94, 0x00, 0x38, 0x01}, {0x95, 0x00, 0x39, 0x01}, {0x96, 0x00, 0x3a, 0x01},
+ {0x97, 0x00, 0x3b, 0x01}, {0x98, 0x00, 0x40, 0x01}, {0x99, 0x00, 0x45, 0x01},
+ {0x9a, 0x00, 0x46, 0x01}, {0x9b, 0x00, 0x47, 0x01}, {0x9c, 0x00, 0x48, 0x01},
+ {0x9d, 0x00, 0x49, 0x01}, {0x9e, 0x00, 0x4a, 0x01}, {0x9f, 0x00, 0x4b, 0x01},
+ {0xa0, 0x00, 0x4c, 0x01}, {0xa1, 0x00, 0x4d, 0x01}, {0xa2, 0x00, 0x52, 0x01},
+ {0xa3, 0x00, 0x56, 0x01}, {0xa4, 0x00, 0x57, 0x01}, {0xa5, 0x00, 0x5c, 0x01},
+ {0xa6, 0x00, 0x5d, 0x01}, {0xa7, 0x00, 0x60, 0x01}, {0xa8, 0x00, 0x63, 0x01},
+ {0xa9, 0x00, 0x65, 0x01}, {0xaa, 0x00, 0x67, 0x01}, {0xab, 0x00, 0x68, 0x01},
+ {0xac, 0x00, 0x6e, 0x01}, {0xad, 0x00, 0x6f, 0x01}, {0xae, 0x00, 0x70, 0x01},
+ {0xaf, 0x00, 0x71, 0x01}, {0xb0, 0x00, 0x72, 0x01}, {0xb1, 0x00, 0x73, 0x01},
+ {0xb2, 0x00, 0x74, 0x01}, {0xb3, 0x00, 0x78, 0x01}, {0xb4, 0x00, 0x7c, 0x01},
+ {0xb5, 0x00, 0x80, 0x01}, {0xb6, 0x00, 0x86, 0x01}, {0xb7, 0x00, 0x87, 0x01},
+ {0xb8, 0x00, 0x88, 0x01}, {0xb9, 0x00, 0x89, 0x01}, {0xba, 0x00, 0x8a, 0x01},
+ {0xbb, 0x00, 0x8b, 0x01}, {0xbc, 0x00, 0x8c, 0x01}, {0xbd, 0x00, 0x8d, 0x01},
+ {0xbe, 0x00, 0x8e, 0x01}, {0xbf, 0x00, 0x8f, 0x01}, {0x00, 0x00, 0x06, 0x00},
+ {0x2d, 0x00, 0x22, 0x00}, {0x61, 0x00, 0x07, 0x00}, {0x62, 0x01, 0x06, 0x00},
+ {0x63, 0x03, 0x06, 0x00}, {0x64, 0x06, 0x06, 0x00}, {0x65, 0x00, 0x0c, 0x00},
+ {0x66, 0x0a, 0x06, 0x00}, {0x67, 0x0c, 0x06, 0x00}, {0x68, 0x0f, 0x06, 0x00},
+ {0x69, 0x00, 0x09, 0x00}, {0x6a, 0x11, 0x06, 0x00}, {0x6b, 0x13, 0x06, 0x00},
+ {0x6c, 0x16, 0x06, 0x00}, {0x6d, 0x1c, 0x06, 0x00}, {0x6e, 0x1e, 0x06, 0x00},
+ {0x6f, 0x00, 0x0d, 0x00}, {0x70, 0x20, 0x06, 0x00}, {0x72, 0x22, 0x06, 0x00},
+ {0x73, 0x24, 0x06, 0x00}, {0x74, 0x27, 0x06, 0x00}, {0x75, 0x00, 0x0a, 0x00},
+ {0x76, 0x2c, 0x06, 0x00}, {0x77, 0x2d, 0x06, 0x00}, {0x78, 0x2f, 0x06, 0x00},
+ {0x79, 0x35, 0x06, 0x00}, {0x7a, 0x36, 0x06, 0x00}, {0xe3, 0x3b, 0xff, 0x01},
+ {0x00, 0x00, 0x06, 0x00}, {0x81, 0x39, 0x06, 0x00}, {0x82, 0x3c, 0xff, 0x01},
+ {0x00, 0x00, 0x06, 0x01}, {0x80, 0x00, 0x0e, 0x00}, {0x81, 0x00, 0x0f, 0x00},
+ {0x82, 0x00, 0x10, 0x00}, {0x83, 0x00, 0x11, 0x00}, {0x84, 0x00, 0x12, 0x00},
+ {0x85, 0x00, 0x13, 0x00}, {0x86, 0x00, 0x14, 0x00}, {0x87, 0x00, 0x15, 0x00},
+ {0x88, 0x00, 0x16, 0x00}, {0x89, 0x00, 0x17, 0x00}, {0x8a, 0x00, 0x18, 0x00},
+ {0x8b, 0x00, 0x19, 0x00}, {0x8c, 0x00, 0x1a, 0x00}, {0x8d, 0x00, 0x1b, 0x00},
+ {0x8e, 0x00, 0x1c, 0x00}, {0x8f, 0x00, 0x1d, 0x00}, {0x90, 0x00, 0x1e, 0x00},
+ {0x91, 0x00, 0x1f, 0x00}, {0x92, 0x00, 0x20, 0x00}, {0x93, 0x00, 0x21, 0x00},
+ {0x9b, 0x00, 0xab, 0x01}, {0x80, 0x00, 0x93, 0x01}, {0x81, 0x00, 0x94, 0x01},
+ {0x82, 0x00, 0x95, 0x01}, {0x83, 0x00, 0x96, 0x01}, {0x84, 0x00, 0x97, 0x01},
+ {0x85, 0x00, 0x98, 0x01}, {0x86, 0x00, 0x99, 0x01}, {0x87, 0x00, 0x9a, 0x01},
+ {0x88, 0x00, 0x9b, 0x01}, {0x89, 0x00, 0x9c, 0x01}, {0x8a, 0x00, 0x9d, 0x01},
+ {0x8b, 0x00, 0xa1, 0x01}, {0x8c, 0x00, 0xa2, 0x01}, {0x8d, 0x00, 0xa3, 0x01},
+ {0x8e, 0x00, 0xa4, 0x01}, {0x8f, 0x00, 0xa5, 0x01}, {0x90, 0x00, 0xa6, 0x01},
+ {0x91, 0x00, 0xa7, 0x01}, {0x92, 0x00, 0xa8, 0x01}, {0x93, 0x00, 0xa9, 0x01}
+};
+
+static rk_tree_node *
+rk_lookup(uint8_t state, uint8_t code)
+{
+ if (state < sizeof(rk_tree_idx)/sizeof(uint16_t)) {
+ uint16_t ns = state ? rk_tree_idx[state - 1] : 0;
+ uint16_t ne = rk_tree_idx[state];
+ while (ns < ne) {
+ uint16_t m = (ns + ne)>>1;
+ rk_tree_node *rn = &rk_tree[m];
+ if (rn->code == code) { return rn; }
+ if (rn->code < code) {
+ ns = m + 1;
+ } else {
+ ne = m;
+ }
+ }
+ }
+ return NULL;
+}
+
+static uint32_t
+rk_emit(rk_tree_node *rn, char **str)
+{
+ if (rn && rn->emit != 0xff) {
+ uint16_t pos = rn->emit ? rk_str_idx[rn->emit - 1] : 0;
+ *str = &rk_str[pos];
+ return (uint32_t)(rk_str_idx[rn->emit] - pos);
+ } else {
+ *str = NULL;
+ return 0;
+ }
+}
+
+#define RK_OUTPUT(e,l) do {\
+ if (oc < oe) {\
+ uint32_t l_ = (oc + (l) < oe) ? (l) : (oe - oc);\
+ memcpy(oc, (e), l_);\
+ oc += l_;\
+ ic_ = ic;\
+ }\
+} while (0)
+
+static uint32_t
+rk_conv(const char *str, uint32_t str_len, uint8_t *buf, uint32_t buf_size, uint8_t *statep)
+{
+ uint32_t l;
+ uint8_t state = 0;
+ rk_tree_node *rn;
+ char *e;
+ uint8_t *oc = buf, *oe = oc + buf_size;
+ const uint8_t *ic = (uint8_t *)str, *ic_ = ic, *ie = ic + str_len;
+ while (ic < ie) {
+ if ((rn = rk_lookup(state, *ic))) {
+ ic++;
+ if ((l = rk_emit(rn, &e))) { RK_OUTPUT(e, l); }
+ state = rn->next;
+ } else {
+ if (!state) { ic++; }
+ if (ic_ < ic) { RK_OUTPUT(ic_, ic - ic_); }
+ state = 0;
+ }
+ }
+#ifdef FLUSH_UNRESOLVED_INPUT
+ if ((rn = rk_lookup(state, 0))) {
+ if ((l = rk_emit(rn, &e))) { RK_OUTPUT(e, l); }
+ state = rn->next;
+ } else {
+ if (ic_ < ic) { RK_OUTPUT(ic_, ic - ic_); }
+ }
+#endif /* FLUSH_UNRESOLVED_INPUT */
+ *statep = state;
+ return oc - buf;
+}
+
+static grn_id
+sub_search(grn_ctx *ctx, grn_pat *pat, grn_id id,
+ int *c0, uint8_t *key, uint32_t key_len)
+{
+ pat_node *pn;
+ uint32_t len = key_len * 16;
+ if (!key_len) { return id; }
+ PAT_AT(pat, id, pn);
+ while (pn) {
+ int ch;
+ ch = PAT_CHK(pn);
+ if (*c0 < ch && ch < len - 1) {
+ if (ch & 1) {
+ id = (ch + 1 < len) ? pn->lr[1] : pn->lr[0];
+ } else {
+ id = pn->lr[nth_bit(key, ch, len)];
+ }
+ *c0 = ch;
+ PAT_AT(pat, id, pn);
+ } else {
+ const uint8_t *k = pat_node_get_key(ctx, pat, pn);
+ return (k && key_len <= PAT_LEN(pn) && !memcmp(k, key, key_len)) ? id : GRN_ID_NIL;
+ }
+ }
+ return GRN_ID_NIL;
+}
+
+static void
+search_push(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
+ uint8_t *key, uint32_t key_len, uint8_t state, grn_id id, int c0, int flags)
+{
+ if (state) {
+ int step;
+ uint16_t ns, ne;
+ if (flags & GRN_CURSOR_DESCENDING) {
+ ns = rk_tree_idx[state - 1];
+ ne = rk_tree_idx[state];
+ step = 1;
+ } else {
+ ns = rk_tree_idx[state] - 1;
+ ne = rk_tree_idx[state - 1] - 1;
+ step = -1;
+ }
+ for (; ns != ne; ns += step) {
+ rk_tree_node *rn = &rk_tree[ns];
+ if (rn->attr) {
+ char *e;
+ uint32_t l = rk_emit(rn, &e);
+ if (l) {
+ if (l + key_len <= GRN_TABLE_MAX_KEY_SIZE) {
+ int ch = c0;
+ grn_id i;
+ memcpy(key + key_len, e, l);
+ if ((i = sub_search(ctx, pat, id, &ch, key, key_len + l))) {
+ search_push(ctx, pat, c, key, key_len + l, rn->next, i, ch, flags);
+ }
+ }
+ } else {
+ search_push(ctx, pat, c, key, key_len, rn->next, id, c0, flags);
+ }
+ }
+ }
+ } else {
+ pat_node *pn;
+ PAT_AT(pat, id, pn);
+ if (pn) {
+ int ch = PAT_CHK(pn);
+ uint32_t len = key_len * 16;
+ if (c0 < ch) {
+ if (flags & GRN_CURSOR_DESCENDING) {
+ if ((ch > len - 1) || !(flags & GRN_CURSOR_GT)) { push(c, pn->lr[0], ch); }
+ push(c, pn->lr[1], ch);
+ } else {
+ push(c, pn->lr[1], ch);
+ if ((ch > len - 1) || !(flags & GRN_CURSOR_GT)) { push(c, pn->lr[0], ch); }
+ }
+ } else {
+ if (PAT_LEN(pn) * 16 > len || !(flags & GRN_CURSOR_GT)) { push(c, id, ch); }
+ }
+ }
+ }
+}
+
+static grn_rc
+set_cursor_rk(grn_ctx *ctx, grn_pat *pat, grn_pat_cursor *c,
+ const void *key, uint32_t key_len, int flags)
+{
+ grn_id id;
+ uint8_t state;
+ pat_node *pn;
+ int c0 = -1;
+ uint32_t len, byte_len;
+ uint8_t keybuf[GRN_TABLE_MAX_KEY_SIZE];
+ if (flags & GRN_CURSOR_SIZE_BY_BIT) { return GRN_OPERATION_NOT_SUPPORTED; }
+ byte_len = rk_conv(key, key_len, keybuf, GRN_TABLE_MAX_KEY_SIZE, &state);
+ len = byte_len * 16;
+ PAT_AT(pat, 0, pn);
+ id = pn->lr[1];
+ if ((id = sub_search(ctx, pat, id, &c0, keybuf, byte_len))) {
+ search_push(ctx, pat, c, keybuf, byte_len, state, id, c0, flags);
+ }
+ return ctx->rc;
+}
diff --git a/storage/mroonga/vendor/groonga/lib/pat.h b/storage/mroonga/vendor/groonga/lib/pat.h
new file mode 100644
index 00000000000..09e1fa87992
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/pat.h
@@ -0,0 +1,113 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_PAT_H
+#define GRN_PAT_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#include "db.h"
+#include "hash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GRN_PAT_MAX_KEY_SIZE GRN_TABLE_MAX_KEY_SIZE
+
+struct _grn_pat {
+ grn_db_obj obj;
+ grn_io *io;
+ struct grn_pat_header *header;
+ grn_encoding encoding;
+ uint32_t key_size;
+ uint32_t value_size;
+ grn_obj *tokenizer;
+ grn_obj *normalizer;
+ grn_id *cache;
+ uint32_t cache_size;
+};
+
+#define GRN_PAT_NDELINFOS 0x100
+
+typedef struct {
+ grn_id d;
+ grn_id ld;
+ uint32_t stat;
+ uint32_t shared;
+} grn_pat_delinfo;
+
+struct grn_pat_header {
+ uint32_t flags;
+ grn_encoding encoding;
+ uint32_t key_size;
+ uint32_t value_size;
+ grn_id tokenizer;
+ uint32_t n_entries;
+ uint32_t curr_rec;
+ int32_t curr_key;
+ int32_t curr_del;
+ int32_t curr_del2;
+ int32_t curr_del3;
+ uint32_t n_garbages;
+ grn_id normalizer;
+ uint32_t reserved[1004];
+ grn_pat_delinfo delinfos[GRN_PAT_NDELINFOS];
+ grn_id garbages[GRN_PAT_MAX_KEY_SIZE + 1];
+};
+
+struct _grn_pat_cursor_entry {
+ grn_id id;
+ uint16_t check;
+};
+
+typedef struct _grn_pat_cursor_entry grn_pat_cursor_entry;
+
+struct _grn_pat_cursor {
+ grn_db_obj obj;
+ grn_id curr_rec;
+ grn_pat *pat;
+ grn_ctx *ctx;
+ unsigned int size;
+ unsigned int sp;
+ grn_id tail;
+ unsigned int rest;
+ grn_pat_cursor_entry *ss;
+ uint8_t curr_key[GRN_TABLE_MAX_KEY_SIZE];
+};
+
+GRN_API grn_id grn_pat_curr_id(grn_ctx *ctx, grn_pat *pat);
+
+/* private */
+GRN_API grn_rc grn_pat_truncate(grn_ctx *ctx, grn_pat *pat);
+const char *_grn_pat_key(grn_ctx *ctx, grn_pat *pat, grn_id id, uint32_t *key_size);
+grn_id grn_pat_next(grn_ctx *ctx, grn_pat *pat, grn_id id);
+const char *grn_pat_get_value_(grn_ctx *ctx, grn_pat *pat, grn_id id, uint32_t *size);
+GRN_API grn_id grn_pat_at(grn_ctx *ctx, grn_pat *pat, grn_id id);
+void grn_pat_check(grn_ctx *ctx, grn_pat *pat);
+void grn_pat_inspect_nodes(grn_ctx *ctx, grn_pat *pat, grn_obj *buf);
+void grn_pat_cursor_inspect(grn_ctx *ctx, grn_pat_cursor *c, grn_obj *buf);
+
+grn_rc grn_pat_cache_enable(grn_ctx *ctx, grn_pat *pat, uint32_t cache_size);
+void grn_pat_cache_disable(grn_ctx *ctx, grn_pat *pat);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_PAT_H */
diff --git a/storage/mroonga/vendor/groonga/lib/plugin.c b/storage/mroonga/vendor/groonga/lib/plugin.c
new file mode 100644
index 00000000000..26e4a3d5165
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/plugin.c
@@ -0,0 +1,753 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012-2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "groonga_in.h"
+#include "groonga/plugin.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "db.h"
+#include "plugin_in.h"
+#include "ctx_impl.h"
+#include "util.h"
+
+static grn_hash *grn_plugins = NULL;
+static grn_critical_section grn_plugins_lock;
+
+#define PATHLEN(filename) (strlen(filename) + 1)
+
+#ifdef HAVE_DLFCN_H
+# include <dlfcn.h>
+# define grn_dl_open(filename) dlopen(filename, RTLD_LAZY | RTLD_LOCAL)
+# define grn_dl_open_error_label() dlerror()
+# define grn_dl_close(dl) (dlclose(dl) == 0)
+# define grn_dl_close_error_label() dlerror()
+# define grn_dl_sym(dl, symbol) dlsym(dl, symbol)
+# define grn_dl_sym_error_label() dlerror()
+# define grn_dl_clear_error() dlerror()
+#else
+# define grn_dl_open(filename) LoadLibrary(filename)
+# define grn_dl_open_error_label() "LoadLibrary"
+# define grn_dl_close(dl) (FreeLibrary(dl) != 0)
+# define grn_dl_close_error_label() "FreeLibrary"
+# define grn_dl_sym(dl, symbol) ((void *)GetProcAddress(dl, symbol))
+# define grn_dl_sym_error_label() "GetProcAddress"
+# define grn_dl_clear_error()
+#endif
+
+static int
+compute_name_size(const char *name, int name_size)
+{
+ if (name_size < 0) {
+ if (name) {
+ name_size = strlen(name);
+ } else {
+ name_size = 0;
+ }
+ }
+ return name_size;
+}
+
+grn_id
+grn_plugin_reference(grn_ctx *ctx, const char *filename)
+{
+ grn_id id;
+ grn_plugin **plugin = NULL;
+
+ CRITICAL_SECTION_ENTER(grn_plugins_lock);
+ id = grn_hash_get(&grn_gctx, grn_plugins, filename, PATHLEN(filename),
+ (void **)&plugin);
+ if (plugin) {
+ (*plugin)->refcount++;
+ }
+ CRITICAL_SECTION_LEAVE(grn_plugins_lock);
+
+ return id;
+}
+
+const char *
+grn_plugin_path(grn_ctx *ctx, grn_id id)
+{
+ const char *path;
+ uint32_t key_size;
+ const char *system_plugins_dir;
+ size_t system_plugins_dir_size;
+
+ CRITICAL_SECTION_ENTER(grn_plugins_lock);
+ path = _grn_hash_key(&grn_gctx, grn_plugins, id, &key_size);
+ CRITICAL_SECTION_LEAVE(grn_plugins_lock);
+
+ if (!path) {
+ return NULL;
+ }
+
+ system_plugins_dir = grn_plugin_get_system_plugins_dir();
+ system_plugins_dir_size = strlen(system_plugins_dir);
+ if (strncmp(system_plugins_dir, path, system_plugins_dir_size) == 0) {
+ const char *plugin_name = path + system_plugins_dir_size;
+ while (plugin_name[0] == '/') {
+ plugin_name++;
+ }
+ /* TODO: remove suffix too? */
+ return plugin_name;
+ } else {
+ return path;
+ }
+}
+
+#define GRN_PLUGIN_FUNC_PREFIX "grn_plugin_impl_"
+
+static grn_rc
+grn_plugin_call_init (grn_ctx *ctx, grn_id id)
+{
+ grn_plugin *plugin;
+ if (!grn_hash_get_value(&grn_gctx, grn_plugins, id, &plugin)) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (plugin->init_func) {
+ return plugin->init_func(ctx);
+ }
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_plugin_call_register(grn_ctx *ctx, grn_id id)
+{
+ grn_plugin *plugin;
+ if (!grn_hash_get_value(&grn_gctx, grn_plugins, id, &plugin)) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (plugin->register_func) {
+ return plugin->register_func(ctx);
+ }
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_plugin_call_fin(grn_ctx *ctx, grn_id id)
+{
+ grn_plugin *plugin;
+ if (!grn_hash_get_value(&grn_gctx, grn_plugins, id, &plugin)) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (plugin->fin_func) {
+ return plugin->fin_func(ctx);
+ }
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_plugin_initialize(grn_ctx *ctx, grn_plugin *plugin,
+ grn_dl dl, grn_id id, const char *path)
+{
+ plugin->dl = dl;
+
+#define GET_SYMBOL(type) do { \
+ grn_dl_clear_error(); \
+ plugin->type ## _func = grn_dl_sym(dl, GRN_PLUGIN_FUNC_PREFIX #type); \
+ if (!plugin->type ## _func) { \
+ const char *label; \
+ label = grn_dl_sym_error_label(); \
+ SERR(label); \
+ } \
+} while (0)
+
+ GET_SYMBOL(init);
+ GET_SYMBOL(register);
+ GET_SYMBOL(fin);
+
+#undef GET_SYMBOL
+
+ if (!plugin->init_func || !plugin->register_func || !plugin->fin_func) {
+ ERR(GRN_INVALID_FORMAT,
+ "init func (%s) %sfound, "
+ "register func (%s) %sfound and "
+ "fin func (%s) %sfound",
+ GRN_PLUGIN_FUNC_PREFIX "init", plugin->init_func ? "" : "not ",
+ GRN_PLUGIN_FUNC_PREFIX "register", plugin->register_func ? "" : "not ",
+ GRN_PLUGIN_FUNC_PREFIX "fin", plugin->fin_func ? "" : "not ");
+ }
+
+ if (!ctx->rc) {
+ ctx->impl->plugin_path = path;
+ grn_plugin_call_init(ctx, id);
+ ctx->impl->plugin_path = NULL;
+ }
+
+ return ctx->rc;
+}
+
+grn_id
+grn_plugin_open(grn_ctx *ctx, const char *filename)
+{
+ grn_id id;
+ grn_dl dl;
+ grn_plugin **plugin = NULL;
+
+ CRITICAL_SECTION_ENTER(grn_plugins_lock);
+ if ((id = grn_hash_get(&grn_gctx, grn_plugins, filename, PATHLEN(filename),
+ (void **)&plugin))) {
+ (*plugin)->refcount++;
+ goto exit;
+ }
+
+ if ((dl = grn_dl_open(filename))) {
+ if ((id = grn_hash_add(&grn_gctx, grn_plugins, filename, PATHLEN(filename),
+ (void **)&plugin, NULL))) {
+ *plugin = GRN_GMALLOCN(grn_plugin, 1);
+ if (*plugin) {
+ if (grn_plugin_initialize(ctx, *plugin, dl, id, filename)) {
+ GRN_GFREE(*plugin);
+ *plugin = NULL;
+ }
+ }
+ if (!*plugin) {
+ grn_hash_delete_by_id(&grn_gctx, grn_plugins, id, NULL);
+ if (grn_dl_close(dl)) {
+ /* Now, __FILE__ set in plugin is invalid. */
+ ctx->errline = 0;
+ ctx->errfile = NULL;
+ } else {
+ const char *label;
+ label = grn_dl_close_error_label();
+ SERR(label);
+ }
+ id = GRN_ID_NIL;
+ } else {
+ (*plugin)->refcount = 1;
+ }
+ } else {
+ if (!grn_dl_close(dl)) {
+ const char *label;
+ label = grn_dl_close_error_label();
+ SERR(label);
+ }
+ }
+ } else {
+ const char *label;
+ label = grn_dl_open_error_label();
+ SERR(label);
+ }
+
+exit:
+ CRITICAL_SECTION_LEAVE(grn_plugins_lock);
+
+ return id;
+}
+
+grn_rc
+grn_plugin_close(grn_ctx *ctx, grn_id id)
+{
+ grn_rc rc;
+ grn_plugin *plugin;
+
+ if (id == GRN_ID_NIL) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ CRITICAL_SECTION_ENTER(grn_plugins_lock);
+ if (!grn_hash_get_value(&grn_gctx, grn_plugins, id, &plugin)) {
+ rc = GRN_INVALID_ARGUMENT;
+ goto exit;
+ }
+ if (--plugin->refcount) {
+ rc = GRN_SUCCESS;
+ goto exit;
+ }
+ grn_plugin_call_fin(ctx, id);
+ if (!grn_dl_close(plugin->dl)) {
+ const char *label;
+ label = grn_dl_close_error_label();
+ SERR(label);
+ }
+ GRN_GFREE(plugin);
+ rc = grn_hash_delete_by_id(&grn_gctx, grn_plugins, id, NULL);
+
+exit:
+ CRITICAL_SECTION_LEAVE(grn_plugins_lock);
+
+ return rc;
+}
+
+void *
+grn_plugin_sym(grn_ctx *ctx, grn_id id, const char *symbol)
+{
+ grn_plugin *plugin;
+ grn_dl_symbol func;
+
+ if (id == GRN_ID_NIL) {
+ return NULL;
+ }
+
+ CRITICAL_SECTION_ENTER(grn_plugins_lock);
+ if (!grn_hash_get_value(&grn_gctx, grn_plugins, id, &plugin)) {
+ func = NULL;
+ goto exit;
+ }
+ grn_dl_clear_error();
+ if (!(func = grn_dl_sym(plugin->dl, symbol))) {
+ const char *label;
+ label = grn_dl_sym_error_label();
+ SERR(label);
+ }
+
+exit:
+ CRITICAL_SECTION_LEAVE(grn_plugins_lock);
+
+ return func;
+}
+
+grn_rc
+grn_plugins_init(void)
+{
+ CRITICAL_SECTION_INIT(grn_plugins_lock);
+ grn_plugins = grn_hash_create(&grn_gctx, NULL, PATH_MAX, sizeof(grn_plugin *),
+ GRN_OBJ_KEY_VAR_SIZE);
+ if (!grn_plugins) { return GRN_NO_MEMORY_AVAILABLE; }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_plugins_fin(void)
+{
+ grn_rc rc;
+ if (!grn_plugins) { return GRN_INVALID_ARGUMENT; }
+ GRN_HASH_EACH(&grn_gctx, grn_plugins, id, NULL, NULL, NULL, {
+ grn_plugin_close(&grn_gctx, id);
+ });
+ rc = grn_hash_close(&grn_gctx, grn_plugins);
+ CRITICAL_SECTION_FIN(grn_plugins_lock);
+ return rc;
+}
+
+const char *
+grn_plugin_get_suffix(void)
+{
+ return GRN_PLUGIN_SUFFIX;
+}
+
+grn_rc
+grn_plugin_register_by_path(grn_ctx *ctx, const char *path)
+{
+ grn_obj *db;
+ if (!ctx || !ctx->impl || !(db = ctx->impl->db)) {
+ ERR(GRN_INVALID_ARGUMENT, "db not initialized");
+ return ctx->rc;
+ }
+ GRN_API_ENTER;
+ if (GRN_DB_P(db)) {
+ grn_id id;
+ id = grn_plugin_open(ctx, path);
+ if (id) {
+ ctx->impl->plugin_path = path;
+ ctx->rc = grn_plugin_call_register(ctx, id);
+ ctx->impl->plugin_path = NULL;
+ grn_plugin_close(ctx, id);
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "invalid db assigned");
+ }
+ GRN_API_RETURN(ctx->rc);
+}
+
+#ifdef WIN32
+static char *win32_plugins_dir = NULL;
+static char win32_plugins_dir_buffer[PATH_MAX];
+const char *
+grn_plugin_get_system_plugins_dir(void)
+{
+ if (!win32_plugins_dir) {
+ const char *base_dir;
+ const char *relative_path = GRN_RELATIVE_PLUGINS_DIR;
+ char *path;
+ size_t base_dir_length;
+
+ base_dir = grn_win32_base_dir();
+ base_dir_length = strlen(base_dir);
+ strcpy(win32_plugins_dir_buffer, base_dir);
+ strcat(win32_plugins_dir_buffer, "/");
+ strcat(win32_plugins_dir_buffer, relative_path);
+ win32_plugins_dir = win32_plugins_dir_buffer;
+ }
+ return win32_plugins_dir;
+}
+
+#else /* WIN32 */
+const char *
+grn_plugin_get_system_plugins_dir(void)
+{
+ return GRN_PLUGINS_DIR;
+}
+#endif /* WIN32 */
+
+char *
+grn_plugin_find_path(grn_ctx *ctx, const char *name)
+{
+ const char *plugins_dir;
+ char dir_last_char;
+ char path[PATH_MAX];
+ int name_length, max_name_length;
+ FILE *plugin_file;
+ char complemented_path[PATH_MAX], complemented_libs_path[PATH_MAX];
+ char *found_path = NULL;
+ size_t path_len;
+
+ GRN_API_ENTER;
+ if (name[0] == '/') {
+ path[0] = '\0';
+ } else {
+ plugins_dir = getenv("GRN_PLUGINS_DIR");
+ if (!plugins_dir) {
+ plugins_dir = grn_plugin_get_system_plugins_dir();
+ }
+ strcpy(path, plugins_dir);
+
+ dir_last_char = plugins_dir[strlen(path) - 1];
+ if (dir_last_char != '/') {
+ strcat(path, "/");
+ }
+ }
+
+ name_length = strlen(name);
+ max_name_length = PATH_MAX - strlen(path) - 1;
+ if (name_length > max_name_length) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "plugin name is too long: %d (max: %d) <%s%s>",
+ name_length, max_name_length,
+ path, name);
+ goto exit;
+ }
+ strcat(path, name);
+
+ plugin_file = fopen(path, "r");
+ if (plugin_file) {
+ fclose(plugin_file);
+ found_path = GRN_STRDUP(path);
+ } else {
+ path_len = strlen(path);
+ path_len += strlen(grn_plugin_get_suffix());
+ if (path_len >= PATH_MAX) {
+ ERR(GRN_FILENAME_TOO_LONG,
+ "too long plugin path: <%s%s>",
+ path, grn_plugin_get_suffix());
+ goto exit;
+ }
+ strcpy(complemented_path, path);
+ strcat(complemented_path, grn_plugin_get_suffix());
+ plugin_file = fopen(complemented_path, "r");
+ if (plugin_file) {
+ fclose(plugin_file);
+ found_path = GRN_STRDUP(complemented_path);
+ } else {
+ const char *base_name;
+
+ base_name = strrchr(path, '/');
+ if (base_name) {
+ path_len = base_name - path + strlen("/.libs") + strlen(base_name);
+ path_len += strlen(grn_plugin_get_suffix());
+ if (path_len >= PATH_MAX) {
+ ERR(GRN_FILENAME_TOO_LONG,
+ "too long plugin path: <%.*s/.libs%s%s>",
+ (int)(base_name - path), path, base_name, grn_plugin_get_suffix());
+ goto exit;
+ }
+ complemented_libs_path[0] = '\0';
+ strncat(complemented_libs_path, path, base_name - path);
+ strcat(complemented_libs_path, "/.libs");
+ strcat(complemented_libs_path, base_name);
+ strcat(complemented_libs_path, grn_plugin_get_suffix());
+ plugin_file = fopen(complemented_libs_path, "r");
+ if (plugin_file) {
+ fclose(plugin_file);
+ found_path = GRN_STRDUP(complemented_libs_path);
+ }
+ }
+ }
+ }
+
+exit :
+ GRN_API_RETURN(found_path);
+}
+
+grn_rc
+grn_plugin_register(grn_ctx *ctx, const char *name)
+{
+ grn_rc rc;
+ char *path;
+
+ GRN_API_ENTER;
+ path = grn_plugin_find_path(ctx, name);
+ if (path) {
+ rc = grn_plugin_register_by_path(ctx, path);
+ GRN_FREE(path);
+ } else {
+ if (ctx->rc == GRN_SUCCESS) {
+ const char *prefix, *prefix_separator, *suffix;
+ if (name[0] == '/') {
+ prefix = "";
+ prefix_separator = "";
+ suffix = "";
+ } else {
+ prefix = getenv("GRN_PLUGINS_DIR");
+ if (!prefix) {
+ prefix = grn_plugin_get_system_plugins_dir();
+ }
+ if (prefix[strlen(prefix) - 1] != '/') {
+ prefix_separator = "/";
+ } else {
+ prefix_separator = "";
+ }
+ suffix = grn_plugin_get_suffix();
+ }
+ ERR(GRN_NO_SUCH_FILE_OR_DIRECTORY,
+ "cannot find plugin file: <%s%s%s%s>",
+ prefix, prefix_separator, name, suffix);
+ }
+ rc = ctx->rc;
+ }
+ GRN_API_RETURN(rc);
+}
+
+void *
+grn_plugin_malloc(grn_ctx *ctx, size_t size, const char *file, int line,
+ const char *func)
+{
+ return grn_malloc(ctx, size, file, line, func);
+}
+
+void *
+grn_plugin_realloc(grn_ctx *ctx, void *ptr, size_t size,
+ const char *file, int line, const char *func)
+{
+ return grn_realloc(ctx, ptr, size, file, line, func);
+}
+
+void
+grn_plugin_free(grn_ctx *ctx, void *ptr, const char *file, int line,
+ const char *func)
+{
+ grn_free(ctx, ptr, file, line, func);
+}
+
+/*
+ grn_plugin_ctx_log() is a clone of grn_ctx_log() in ctx.c. The only
+ difference is that grn_plugin_ctx_log() uses va_list instead of `...'.
+ */
+static void
+grn_plugin_ctx_log(grn_ctx *ctx, const char *format, va_list ap)
+{
+ vsnprintf(ctx->errbuf, GRN_CTX_MSGSIZE, format, ap);
+}
+
+void
+grn_plugin_set_error(grn_ctx *ctx, grn_log_level level, grn_rc error_code,
+ const char *file, int line, const char *func,
+ const char *format, ...)
+{
+ ctx->errlvl = level;
+ ctx->rc = error_code;
+ ctx->errfile = file;
+ ctx->errline = line;
+ ctx->errfunc = func;
+
+ {
+ va_list ap;
+ va_start(ap, format);
+ grn_plugin_ctx_log(ctx, format, ap);
+ va_end(ap);
+ }
+}
+
+void
+grn_plugin_backtrace(grn_ctx *ctx)
+{
+ BACKTRACE(ctx);
+}
+
+void
+grn_plugin_logtrace(grn_ctx *ctx, grn_log_level level)
+{
+ if (level <= GRN_LOG_ERROR) {
+ LOGTRACE(ctx, level);
+ }
+}
+
+struct _grn_plugin_mutex {
+ grn_critical_section critical_section;
+};
+
+grn_plugin_mutex *
+grn_plugin_mutex_open(grn_ctx *ctx)
+{
+ grn_plugin_mutex * const mutex =
+ GRN_PLUGIN_MALLOC(ctx, sizeof(grn_plugin_mutex));
+ if (mutex != NULL) {
+ CRITICAL_SECTION_INIT(mutex->critical_section);
+ }
+ return mutex;
+}
+
+grn_plugin_mutex *
+grn_plugin_mutex_create(grn_ctx *ctx)
+{
+ return grn_plugin_mutex_open(ctx);
+}
+
+void
+grn_plugin_mutex_close(grn_ctx *ctx, grn_plugin_mutex *mutex)
+{
+ if (mutex != NULL) {
+ CRITICAL_SECTION_FIN(mutex->critical_section);
+ GRN_PLUGIN_FREE(ctx, mutex);
+ }
+}
+
+void
+grn_plugin_mutex_destroy(grn_ctx *ctx, grn_plugin_mutex *mutex)
+{
+ grn_plugin_mutex_close(ctx, mutex);
+}
+
+void
+grn_plugin_mutex_lock(grn_ctx *ctx, grn_plugin_mutex *mutex)
+{
+ if (mutex != NULL) {
+ CRITICAL_SECTION_ENTER(mutex->critical_section);
+ }
+}
+
+void
+grn_plugin_mutex_unlock(grn_ctx *ctx, grn_plugin_mutex *mutex)
+{
+ if (mutex != NULL) {
+ CRITICAL_SECTION_LEAVE(mutex->critical_section);
+ }
+}
+
+grn_obj *
+grn_plugin_proc_alloc(grn_ctx *ctx, grn_user_data *user_data,
+ grn_id domain, grn_obj_flags flags)
+{
+ return grn_proc_alloc(ctx, user_data, domain, flags);
+}
+
+grn_obj *
+grn_plugin_proc_get_var(grn_ctx *ctx, grn_user_data *user_data,
+ const char *name, int name_size)
+{
+ name_size = compute_name_size(name, name_size);
+ return grn_proc_get_var(ctx, user_data, name, name_size);
+}
+
+grn_obj *
+grn_plugin_proc_get_var_by_offset(grn_ctx *ctx, grn_user_data *user_data,
+ unsigned int offset)
+{
+ return grn_proc_get_var_by_offset(ctx, user_data, offset);
+}
+
+const char *
+grn_plugin_win32_base_dir(void)
+{
+#ifdef WIN32
+ return grn_win32_base_dir();
+#else /* WIN32 */
+ return NULL;
+#endif /* WIN32 */
+}
+
+/*
+ grn_plugin_charlen() takes the length of a string, unlike grn_charlen_().
+ */
+int
+grn_plugin_charlen(grn_ctx *ctx, const char *str_ptr,
+ unsigned int str_length, grn_encoding encoding)
+{
+ return grn_charlen_(ctx, str_ptr, str_ptr + str_length, encoding);
+}
+
+/*
+ grn_plugin_isspace() takes the length of a string, unlike grn_isspace().
+ */
+int
+grn_plugin_isspace(grn_ctx *ctx, const char *str_ptr,
+ unsigned int str_length, grn_encoding encoding)
+{
+ if ((str_ptr == NULL) || (str_length == 0)) {
+ return 0;
+ }
+ switch ((unsigned char)str_ptr[0]) {
+ case ' ' :
+ case '\f' :
+ case '\n' :
+ case '\r' :
+ case '\t' :
+ case '\v' :
+ return 1;
+ case 0x81 :
+ if ((encoding == GRN_ENC_SJIS) && (str_length >= 2) &&
+ ((unsigned char)str_ptr[1] == 0x40)) {
+ return 2;
+ }
+ break;
+ case 0xA1 :
+ if ((encoding == GRN_ENC_EUC_JP) && (str_length >= 2) &&
+ ((unsigned char)str_ptr[1] == 0xA1)) {
+ return 2;
+ }
+ break;
+ case 0xE3 :
+ if ((encoding == GRN_ENC_UTF8) && (str_length >= 3) &&
+ ((unsigned char)str_ptr[1] == 0x80) &&
+ ((unsigned char)str_ptr[2] == 0x80)) {
+ return 3;
+ }
+ break;
+ default :
+ break;
+ }
+ return 0;
+}
+
+grn_rc
+grn_plugin_expr_var_init(grn_ctx *ctx,
+ grn_expr_var *var,
+ const char *name,
+ int name_size)
+{
+ var->name = name;
+ var->name_size = compute_name_size(name, name_size);
+ GRN_TEXT_INIT(&var->value, 0);
+ return GRN_SUCCESS;
+}
+
+grn_obj *
+grn_plugin_command_create(grn_ctx *ctx,
+ const char *name,
+ int name_size,
+ grn_proc_func func,
+ unsigned int n_vars,
+ grn_expr_var *vars)
+{
+ grn_obj *proc;
+ name_size = compute_name_size(name, name_size);
+ proc = grn_proc_create(ctx, name, name_size, GRN_PROC_COMMAND,
+ func, NULL, NULL, n_vars, vars);
+ return proc;
+}
diff --git a/storage/mroonga/vendor/groonga/lib/plugin_in.h b/storage/mroonga/vendor/groonga/lib/plugin_in.h
new file mode 100644
index 00000000000..6f983089dc9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/plugin_in.h
@@ -0,0 +1,68 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2010-2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_PLUGIN_IN_H
+#define GRN_PLUGIN_IN_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifndef GRN_CTX_H
+#include "ctx.h"
+#endif /* GRN_CTX_H */
+
+#ifndef GRN_STORE_H
+#include "store.h"
+#endif /* GRN_STORE_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef WIN32
+typedef HINSTANCE grn_dl;
+typedef FARPROC grn_dl_symbol;
+
+#else
+typedef void * grn_dl;
+typedef void * grn_dl_symbol;
+#endif
+
+typedef struct _grn_plugin grn_plugin;
+
+struct _grn_plugin {
+ grn_dl dl;
+ grn_plugin_func init_func;
+ grn_plugin_func register_func;
+ grn_plugin_func unregister_func;
+ grn_plugin_func fin_func;
+ int refcount;
+};
+
+grn_rc grn_plugins_init(void);
+grn_rc grn_plugins_fin(void);
+grn_id grn_plugin_open(grn_ctx *ctx, const char *filename);
+grn_rc grn_plugin_close(grn_ctx *ctx, grn_id id);
+grn_id grn_plugin_reference(grn_ctx *ctx, const char *filename);
+const char *grn_plugin_path(grn_ctx *ctx, grn_id id);
+char *grn_plugin_find_path(grn_ctx *ctx, const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_PLUGIN_IN_H */
diff --git a/storage/mroonga/vendor/groonga/lib/proc.c b/storage/mroonga/vendor/groonga/lib/proc.c
new file mode 100644
index 00000000000..e1473472f55
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/proc.c
@@ -0,0 +1,5247 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2009-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "proc.h"
+#include "ii.h"
+#include "db.h"
+#include "util.h"
+#include "output.h"
+#include "pat.h"
+#include "geo.h"
+#include "token.h"
+#include "expr.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+typedef grn_rc (*grn_substitute_term_func) (grn_ctx *ctx,
+ const char *term,
+ unsigned int term_len,
+ grn_obj *substituted_term,
+ grn_user_data *user_data);
+typedef struct {
+ grn_obj *table;
+ grn_obj *column;
+} grn_substitute_term_by_column_data;
+
+/**** globals for procs ****/
+const char *grn_document_root = NULL;
+
+#define VAR GRN_PROC_GET_VAR_BY_OFFSET
+
+#define GRN_SELECT_INTERNAL_VAR_CONDITION "$condition"
+#define GRN_SELECT_INTERNAL_VAR_MATCH_COLUMNS "$match_columns"
+
+/* bulk must be initialized grn_bulk or grn_msg */
+static int
+grn_bulk_put_from_file(grn_ctx *ctx, grn_obj *bulk, const char *path)
+{
+ /* FIXME: implement more smartly with grn_bulk */
+ int fd, ret = 0;
+ struct stat stat;
+ if ((fd = GRN_OPEN(path, O_RDONLY|O_NOFOLLOW|O_BINARY)) == -1) {
+ switch (errno) {
+ case EACCES :
+ ERR(GRN_OPERATION_NOT_PERMITTED, "request is not allowed: <%s>", path);
+ break;
+ case ENOENT :
+ ERR(GRN_NO_SUCH_FILE_OR_DIRECTORY, "no such file: <%s>", path);
+ break;
+#ifndef WIN32
+ case ELOOP :
+ ERR(GRN_NO_SUCH_FILE_OR_DIRECTORY,
+ "symbolic link is not allowed: <%s>", path);
+ break;
+#endif /* WIN32 */
+ default :
+ ERR(GRN_UNKNOWN_ERROR, "GRN_OPEN() failed(errno: %d): <%s>", errno, path);
+ break;
+ }
+ return 0;
+ }
+ if (fstat(fd, &stat) != -1) {
+ char *buf, *bp;
+ off_t rest = stat.st_size;
+ if ((buf = GRN_MALLOC(rest))) {
+ ssize_t ss;
+ for (bp = buf; rest; rest -= ss, bp += ss) {
+ if ((ss = GRN_READ(fd, bp, rest)) == -1) { goto exit; }
+ }
+ GRN_TEXT_PUT(ctx, bulk, buf, stat.st_size);
+ ret = 1;
+ }
+ GRN_FREE(buf);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "cannot stat file: <%s>", path);
+ }
+exit :
+ GRN_CLOSE(fd);
+ return ret;
+}
+
+#ifdef stat
+# undef stat
+#endif /* stat */
+
+/**** query expander ****/
+
+static grn_rc
+substitute_term_by_func(grn_ctx *ctx, const char *term, unsigned int term_len,
+ grn_obj *expanded_term, grn_user_data *user_data)
+{
+ grn_rc rc;
+ grn_obj *expander = user_data->ptr;
+ grn_obj grn_term;
+ grn_obj *caller;
+ grn_obj *rc_object;
+ int nargs = 0;
+
+ GRN_TEXT_INIT(&grn_term, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET(ctx, &grn_term, term, term_len);
+ grn_ctx_push(ctx, &grn_term);
+ nargs++;
+ grn_ctx_push(ctx, expanded_term);
+ nargs++;
+
+ caller = grn_expr_create(ctx, NULL, 0);
+ rc = grn_proc_call(ctx, expander, nargs, caller);
+ GRN_OBJ_FIN(ctx, &grn_term);
+ rc_object = grn_ctx_pop(ctx);
+ rc = GRN_INT32_VALUE(rc_object);
+ grn_obj_unlink(ctx, caller);
+
+ return rc;
+}
+
+static grn_rc
+substitute_term_by_column(grn_ctx *ctx, const char *term, unsigned int term_len,
+ grn_obj *expanded_term, grn_user_data *user_data)
+{
+ grn_rc rc = GRN_END_OF_DATA;
+ grn_id id;
+ grn_substitute_term_by_column_data *data = user_data->ptr;
+ grn_obj *table, *column;
+
+ table = data->table;
+ column = data->column;
+ if ((id = grn_table_get(ctx, table, term, term_len))) {
+ if ((column->header.type == GRN_COLUMN_VAR_SIZE) &&
+ ((column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR)) {
+ unsigned int i, n;
+ grn_obj values;
+ GRN_TEXT_INIT(&values, GRN_OBJ_VECTOR);
+ grn_obj_get_value(ctx, column, id, &values);
+ n = grn_vector_size(ctx, &values);
+ if (n > 1) { GRN_TEXT_PUTC(ctx, expanded_term, '('); }
+ for (i = 0; i < n; i++) {
+ const char *value;
+ unsigned int length;
+ if (i > 0) {
+ GRN_TEXT_PUTS(ctx, expanded_term, " OR ");
+ }
+ if (n > 1) { GRN_TEXT_PUTC(ctx, expanded_term, '('); }
+ length = grn_vector_get_element(ctx, &values, i, &value, NULL, NULL);
+ GRN_TEXT_PUT(ctx, expanded_term, value, length);
+ if (n > 1) { GRN_TEXT_PUTC(ctx, expanded_term, ')'); }
+ }
+ if (n > 1) { GRN_TEXT_PUTC(ctx, expanded_term, ')'); }
+ GRN_OBJ_FIN(ctx, &values);
+ } else {
+ grn_obj_get_value(ctx, column, id, expanded_term);
+ }
+ rc = GRN_SUCCESS;
+ }
+ return rc;
+}
+
+static grn_rc
+substitute_terms(grn_ctx *ctx, const char *query, unsigned int query_len,
+ grn_expr_flags flags,
+ grn_obj *expanded_query,
+ grn_substitute_term_func substitute_term_func,
+ grn_user_data *user_data)
+{
+ grn_obj buf;
+ unsigned int len;
+ const char *start, *cur = query, *query_end = query + (size_t)query_len;
+ GRN_TEXT_INIT(&buf, 0);
+ for (;;) {
+ while (cur < query_end && grn_isspace(cur, ctx->encoding)) {
+ if (!(len = grn_charlen(ctx, cur, query_end))) { goto exit; }
+ GRN_TEXT_PUT(ctx, expanded_query, cur, len);
+ cur += len;
+ }
+ if (query_end <= cur) { break; }
+ switch (*cur) {
+ case '\0' :
+ goto exit;
+ break;
+ case GRN_QUERY_AND :
+ case GRN_QUERY_ADJ_INC :
+ case GRN_QUERY_ADJ_DEC :
+ case GRN_QUERY_ADJ_NEG :
+ case GRN_QUERY_AND_NOT :
+ case GRN_QUERY_PARENL :
+ case GRN_QUERY_PARENR :
+ case GRN_QUERY_PREFIX :
+ GRN_TEXT_PUTC(ctx, expanded_query, *cur);
+ cur++;
+ break;
+ case GRN_QUERY_QUOTEL :
+ GRN_BULK_REWIND(&buf);
+ for (start = cur++; cur < query_end; cur += len) {
+ if (!(len = grn_charlen(ctx, cur, query_end))) {
+ goto exit;
+ } else if (len == 1) {
+ if (*cur == GRN_QUERY_QUOTER) {
+ cur++;
+ break;
+ } else if (cur + 1 < query_end && *cur == GRN_QUERY_ESCAPE) {
+ cur++;
+ len = grn_charlen(ctx, cur, query_end);
+ }
+ }
+ GRN_TEXT_PUT(ctx, &buf, cur, len);
+ }
+ if (substitute_term_func(ctx, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf),
+ expanded_query, user_data)) {
+ GRN_TEXT_PUT(ctx, expanded_query, start, cur - start);
+ }
+ break;
+ case 'O' :
+ if (cur + 2 <= query_end && cur[1] == 'R' &&
+ (cur + 2 == query_end || grn_isspace(cur + 2, ctx->encoding))) {
+ GRN_TEXT_PUT(ctx, expanded_query, cur, 2);
+ cur += 2;
+ break;
+ }
+ /* fallthru */
+ default :
+ for (start = cur; cur < query_end; cur += len) {
+ if (!(len = grn_charlen(ctx, cur, query_end))) {
+ goto exit;
+ } else if (grn_isspace(cur, ctx->encoding)) {
+ break;
+ } else if (len == 1) {
+ if (*cur == GRN_QUERY_PARENL ||
+ *cur == GRN_QUERY_PARENR ||
+ *cur == GRN_QUERY_PREFIX) {
+ break;
+ } else if (flags & GRN_EXPR_ALLOW_COLUMN && *cur == GRN_QUERY_COLUMN) {
+ if (cur + 1 < query_end) {
+ switch (cur[1]) {
+ case '!' :
+ case '@' :
+ case '^' :
+ case '$' :
+ cur += 2;
+ break;
+ case '=' :
+ cur += (flags & GRN_EXPR_ALLOW_UPDATE) ? 2 : 1;
+ break;
+ case '<' :
+ case '>' :
+ cur += (cur + 2 < query_end && cur[2] == '=') ? 3 : 2;
+ break;
+ default :
+ cur += 1;
+ break;
+ }
+ } else {
+ cur += 1;
+ }
+ GRN_TEXT_PUT(ctx, expanded_query, start, cur - start);
+ start = cur;
+ break;
+ }
+ }
+ }
+ if (start < cur) {
+ if (substitute_term_func(ctx, start, cur - start,
+ expanded_query, user_data)) {
+ GRN_TEXT_PUT(ctx, expanded_query, start, cur - start);
+ }
+ }
+ break;
+ }
+ }
+exit :
+ GRN_OBJ_FIN(ctx, &buf);
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+expand_query(grn_ctx *ctx, const char *query, unsigned int query_len,
+ grn_expr_flags flags,
+ const char *query_expander_name,
+ unsigned int query_expander_name_len,
+ grn_obj *expanded_query)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_obj *query_expander;
+
+ query_expander = grn_ctx_get(ctx,
+ query_expander_name, query_expander_name_len);
+ if (!query_expander) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "nonexistent query expansion column: <%.*s>",
+ query_expander_name_len, query_expander_name);
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ switch (query_expander->header.type) {
+ case GRN_PROC :
+ if (((grn_proc *)query_expander)->type == GRN_PROC_FUNCTION) {
+ grn_user_data user_data;
+ user_data.ptr = query_expander;
+ substitute_terms(ctx, query, query_len, flags, expanded_query,
+ substitute_term_by_func, &user_data);
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[expand-query] must be function proc: <%.*s>",
+ query_expander_name_len, query_expander_name);
+ }
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ {
+ grn_obj *query_expansion_table;
+ query_expansion_table = grn_column_table(ctx, query_expander);
+ if (query_expansion_table) {
+ grn_user_data user_data;
+ grn_substitute_term_by_column_data data;
+ user_data.ptr = &data;
+ data.table = query_expansion_table;
+ data.column = query_expander;
+ substitute_terms(ctx, query, query_len, flags, expanded_query,
+ substitute_term_by_column, &user_data);
+ grn_obj_unlink(ctx, query_expansion_table);
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[expand-query] failed to get table of column: <%.*s>",
+ query_expander_name_len, query_expander_name);
+ }
+ }
+ break;
+ default :
+ rc = GRN_INVALID_ARGUMENT;
+ {
+ grn_obj type_name;
+ GRN_TEXT_INIT(&type_name, 0);
+ grn_inspect_type(ctx, &type_name, query_expander->header.type);
+ ERR(rc,
+ "[expand-query] must be a column or function proc: <%.*s>(%.*s)",
+ query_expander_name_len, query_expander_name,
+ (int)GRN_TEXT_LEN(&type_name), GRN_TEXT_VALUE(&type_name));
+ GRN_OBJ_FIN(ctx, &type_name);
+ }
+ break;
+ }
+ grn_obj_unlink(ctx, query_expander);
+
+ return rc;
+}
+
+
+/**** procs ****/
+
+#define DEFAULT_LIMIT 10
+#define DEFAULT_OUTPUT_COLUMNS "_id, _key, *"
+#define DEFAULT_DRILLDOWN_LIMIT 10
+#define DEFAULT_DRILLDOWN_OUTPUT_COLUMNS "_key, _nsubrecs"
+#define DUMP_COLUMNS "_id, _key, _value, *"
+
+static grn_expr_flags
+grn_parse_query_flags(grn_ctx *ctx, const char *query_flags,
+ unsigned int query_flags_len)
+{
+ grn_expr_flags flags = 0;
+ const char *query_flags_end = query_flags + query_flags_len;
+
+ while (query_flags < query_flags_end) {
+ if (*query_flags == '|' || *query_flags == ' ') {
+ query_flags += 1;
+ continue;
+ }
+
+#define CHECK_EXPR_FLAG(name)\
+ if (((query_flags_end - query_flags) >= (sizeof(#name) - 1)) &&\
+ (!memcmp(query_flags, #name, sizeof(#name) - 1))) {\
+ flags |= GRN_EXPR_ ## name;\
+ query_flags += sizeof(#name);\
+ continue;\
+ }
+
+ CHECK_EXPR_FLAG(ALLOW_PRAGMA);
+ CHECK_EXPR_FLAG(ALLOW_COLUMN);
+ CHECK_EXPR_FLAG(ALLOW_UPDATE);
+ CHECK_EXPR_FLAG(ALLOW_LEADING_NOT);
+
+#define GRN_EXPR_NONE 0
+ CHECK_EXPR_FLAG(NONE);
+#undef GNR_EXPR_NONE
+
+ ERR(GRN_INVALID_ARGUMENT, "invalid query flag: <%.*s>",
+ (int)(query_flags_end - query_flags), query_flags);
+ return 0;
+#undef CHECK_EXPR_FLAG
+ }
+
+ return flags;
+}
+
+static inline grn_bool
+is_output_columns_format_v1(grn_ctx *ctx,
+ const char *output_columns,
+ unsigned int output_columns_len)
+{
+ unsigned int i;
+
+ /* TODO: REMOVE ME. If new output_columns handler is marked as stable,
+ this check is removed. We need more error checks. */
+ if (grn_ctx_get_command_version(ctx) == GRN_COMMAND_VERSION_1) {
+ return GRN_TRUE;
+ }
+
+ for (i = 0; i < output_columns_len; i++) {
+ switch (output_columns[i]) {
+ case ',' :
+ case '(' :
+ return GRN_FALSE;
+ default :
+ break;
+ }
+ }
+
+ return GRN_TRUE;
+}
+
+static int
+grn_select_apply_adjuster_ensure_factor(grn_ctx *ctx, grn_obj *factor_object)
+{
+ if (!factor_object) {
+ return 1;
+ } else if (factor_object->header.domain == GRN_DB_INT32) {
+ return GRN_INT32_VALUE(factor_object);
+ } else {
+ grn_rc rc;
+ grn_obj int32_object;
+ int factor;
+ GRN_INT32_INIT(&int32_object, 0);
+ rc = grn_obj_cast(ctx, factor_object, &int32_object, GRN_FALSE);
+ if (rc == GRN_SUCCESS) {
+ factor = GRN_INT32_VALUE(&int32_object);
+ } else {
+ /* TODO: Log or return error? */
+ factor = 1;
+ }
+ GRN_OBJ_FIN(ctx, &int32_object);
+ return factor;
+ }
+}
+
+static void
+grn_select_apply_adjuster_adjust(grn_ctx *ctx, grn_obj *table, grn_obj *res,
+ grn_obj *column, grn_obj *value,
+ grn_obj *factor)
+{
+ grn_obj *index;
+ unsigned int n_indexes;
+ int factor_value;
+
+ n_indexes = grn_column_index(ctx, column, GRN_OP_MATCH, &index, 1, NULL);
+ if (n_indexes == 0) {
+ char column_name[GRN_TABLE_MAX_KEY_SIZE];
+ int column_name_size;
+ column_name_size = grn_obj_name(ctx, column,
+ column_name, GRN_TABLE_MAX_KEY_SIZE);
+ ERR(GRN_INVALID_ARGUMENT,
+ "adjuster requires index column for the target column: <%.*s>",
+ column_name_size, column_name);
+ return;
+ }
+
+ factor_value = grn_select_apply_adjuster_ensure_factor(ctx, factor);
+
+ {
+ grn_search_optarg options;
+
+ options.mode = GRN_OP_EXACT;
+ options.similarity_threshold = 0;
+ options.max_interval = 0;
+ options.weight_vector = NULL;
+ options.vector_size = factor_value;
+ options.proc = NULL;
+ options.max_size = 0;
+
+ grn_obj_search(ctx, index, value, res, GRN_OP_ADJUST, &options);
+ }
+}
+
+static void
+grn_select_apply_adjuster(grn_ctx *ctx, grn_obj *table, grn_obj *res,
+ grn_obj *adjuster)
+{
+ grn_expr *expr = (grn_expr *)adjuster;
+ grn_expr_code *code, *code_end;
+
+ code = expr->codes;
+ code_end = expr->codes + expr->codes_curr;
+ while (code < code_end) {
+ grn_obj *column, *value, *factor;
+
+ if (code->op == GRN_OP_PLUS) {
+ code++;
+ continue;
+ }
+
+ column = code->value;
+ code++;
+ value = code->value;
+ code++;
+ code++; /* op == GRN_OP_MATCH */
+ if ((code_end - code) >= 2 && code[1].op == GRN_OP_STAR) {
+ factor = code->value;
+ code++;
+ code++; /* op == GRN_OP_STAR */
+ } else {
+ factor = NULL;
+ }
+ grn_select_apply_adjuster_adjust(ctx, table, res, column, value, factor);
+ }
+}
+
+grn_rc
+grn_select(grn_ctx *ctx, const char *table, unsigned int table_len,
+ const char *match_columns, unsigned int match_columns_len,
+ const char *query, unsigned int query_len,
+ const char *filter, unsigned int filter_len,
+ const char *scorer, unsigned int scorer_len,
+ const char *sortby, unsigned int sortby_len,
+ const char *output_columns, unsigned int output_columns_len,
+ int offset, int limit,
+ const char *drilldown, unsigned int drilldown_len,
+ const char *drilldown_sortby, unsigned int drilldown_sortby_len,
+ const char *drilldown_output_columns, unsigned int drilldown_output_columns_len,
+ int drilldown_offset, int drilldown_limit,
+ const char *cache, unsigned int cache_len,
+ const char *match_escalation_threshold, unsigned int match_escalation_threshold_len,
+ const char *query_expander, unsigned int query_expander_len,
+ const char *query_flags, unsigned int query_flags_len,
+ const char *adjuster, unsigned int adjuster_len)
+{
+ uint32_t nkeys, nhits;
+ uint16_t cacheable = 1, taintable = 0;
+ grn_obj_format format;
+ grn_table_sort_key *keys;
+ grn_obj *outbuf = ctx->impl->outbuf;
+ grn_content_type output_type = ctx->impl->output_type;
+ grn_obj *table_, *match_columns_ = NULL, *cond = NULL, *scorer_, *res = NULL, *sorted;
+ char cache_key[GRN_TABLE_MAX_KEY_SIZE];
+ uint32_t cache_key_size = table_len + 1 + match_columns_len + 1 + query_len + 1 +
+ filter_len + 1 + scorer_len + 1 + sortby_len + 1 + output_columns_len + 1 +
+ drilldown_len + 1 + drilldown_sortby_len + 1 +
+ drilldown_output_columns_len + 1 + match_escalation_threshold_len + 1 +
+ query_expander_len + 1 + query_flags_len + 1 + adjuster_len + 1 +
+ sizeof(grn_content_type) + sizeof(int) * 4;
+ long long int threshold, original_threshold = 0;
+ grn_cache *cache_obj = grn_cache_current_get(ctx);
+ if (cache_key_size <= GRN_TABLE_MAX_KEY_SIZE) {
+ grn_obj *cache_value;
+ char *cp = cache_key;
+ memcpy(cp, table, table_len);
+ cp += table_len; *cp++ = '\0';
+ memcpy(cp, match_columns, match_columns_len);
+ cp += match_columns_len; *cp++ = '\0';
+ memcpy(cp, query, query_len);
+ cp += query_len; *cp++ = '\0';
+ memcpy(cp, filter, filter_len);
+ cp += filter_len; *cp++ = '\0';
+ memcpy(cp, scorer, scorer_len);
+ cp += scorer_len; *cp++ = '\0';
+ memcpy(cp, sortby, sortby_len);
+ cp += sortby_len; *cp++ = '\0';
+ memcpy(cp, output_columns, output_columns_len);
+ cp += output_columns_len; *cp++ = '\0';
+ memcpy(cp, drilldown, drilldown_len);
+ cp += drilldown_len; *cp++ = '\0';
+ memcpy(cp, drilldown_sortby, drilldown_sortby_len);
+ cp += drilldown_sortby_len; *cp++ = '\0';
+ memcpy(cp, drilldown_output_columns, drilldown_output_columns_len);
+ cp += drilldown_output_columns_len; *cp++ = '\0';
+ memcpy(cp, match_escalation_threshold, match_escalation_threshold_len);
+ cp += match_escalation_threshold_len; *cp++ = '\0';
+ memcpy(cp, query_expander, query_expander_len);
+ cp += query_expander_len; *cp++ = '\0';
+ memcpy(cp, query_flags, query_flags_len);
+ cp += query_flags_len; *cp++ = '\0';
+ memcpy(cp, adjuster, adjuster_len);
+ cp += adjuster_len; *cp++ = '\0';
+ memcpy(cp, &output_type, sizeof(grn_content_type)); cp += sizeof(grn_content_type);
+ memcpy(cp, &offset, sizeof(int)); cp += sizeof(int);
+ memcpy(cp, &limit, sizeof(int)); cp += sizeof(int);
+ memcpy(cp, &drilldown_offset, sizeof(int)); cp += sizeof(int);
+ memcpy(cp, &drilldown_limit, sizeof(int)); cp += sizeof(int);
+ cache_value = grn_cache_fetch(ctx, cache_obj, cache_key, cache_key_size);
+ if (cache_value) {
+ GRN_TEXT_PUT(ctx, outbuf,
+ GRN_TEXT_VALUE(cache_value),
+ GRN_TEXT_LEN(cache_value));
+ grn_cache_unref(ctx, cache_obj, cache_key, cache_key_size);
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_CACHE,
+ ":", "cache(%" GRN_FMT_LLD ")",
+ (long long int)GRN_TEXT_LEN(cache_value));
+ return ctx->rc;
+ }
+ }
+ if (match_escalation_threshold_len) {
+ const char *end, *rest;
+ original_threshold = grn_ctx_get_match_escalation_threshold(ctx);
+ end = match_escalation_threshold + match_escalation_threshold_len;
+ threshold = grn_atoll(match_escalation_threshold, end, &rest);
+ if (end == rest) {
+ grn_ctx_set_match_escalation_threshold(ctx, threshold);
+ }
+ }
+ if ((table_ = grn_ctx_get(ctx, table, table_len))) {
+ // match_columns_ = grn_obj_column(ctx, table_, match_columns, match_columns_len);
+ if (query_len || filter_len) {
+ grn_obj *v;
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, table_, cond, v);
+ if (cond) {
+ if (match_columns_len) {
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, table_, match_columns_, v);
+ if (match_columns_) {
+ grn_expr_parse(ctx, match_columns_, match_columns, match_columns_len,
+ NULL, GRN_OP_MATCH, GRN_OP_AND,
+ GRN_EXPR_SYNTAX_SCRIPT);
+ } else {
+ /* todo */
+ }
+ }
+ if (query_len) {
+ grn_expr_flags flags;
+ grn_obj query_expander_buf;
+ GRN_TEXT_INIT(&query_expander_buf, 0);
+ flags = GRN_EXPR_SYNTAX_QUERY;
+ if (query_flags_len) {
+ flags |= grn_parse_query_flags(ctx, query_flags, query_flags_len);
+ } else {
+ flags |= GRN_EXPR_ALLOW_PRAGMA|GRN_EXPR_ALLOW_COLUMN;
+ if (ctx->rc) {
+ goto exit;
+ }
+ }
+ if (query_expander_len) {
+ if (expand_query(ctx, query, query_len, flags,
+ query_expander, query_expander_len,
+ &query_expander_buf) == GRN_SUCCESS) {
+ query = GRN_TEXT_VALUE(&query_expander_buf);
+ query_len = GRN_TEXT_LEN(&query_expander_buf);
+ } else {
+ GRN_OBJ_FIN(ctx, &query_expander_buf);
+ goto exit;
+ }
+ }
+ grn_expr_parse(ctx, cond, query, query_len,
+ match_columns_, GRN_OP_MATCH, GRN_OP_AND, flags);
+ GRN_OBJ_FIN(ctx, &query_expander_buf);
+ if (!ctx->rc && filter_len) {
+ grn_expr_parse(ctx, cond, filter, filter_len,
+ match_columns_, GRN_OP_MATCH, GRN_OP_AND,
+ GRN_EXPR_SYNTAX_SCRIPT);
+ if (!ctx->rc) { grn_expr_append_op(ctx, cond, GRN_OP_AND, 2); }
+ }
+ } else {
+ grn_expr_parse(ctx, cond, filter, filter_len,
+ match_columns_, GRN_OP_MATCH, GRN_OP_AND,
+ GRN_EXPR_SYNTAX_SCRIPT);
+ }
+ cacheable *= ((grn_expr *)cond)->cacheable;
+ taintable += ((grn_expr *)cond)->taintable;
+ /*
+ grn_obj strbuf;
+ GRN_TEXT_INIT(&strbuf, 0);
+ grn_expr_inspect(ctx, &strbuf, cond);
+ GRN_TEXT_PUTC(ctx, &strbuf, '\0');
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "query=(%s)", GRN_TEXT_VALUE(&strbuf));
+ GRN_OBJ_FIN(ctx, &strbuf);
+ */
+ if (!ctx->rc) { res = grn_table_select(ctx, table_, cond, NULL, GRN_OP_OR); }
+ } else {
+ /* todo */
+ ERRCLR(ctx);
+ }
+ } else {
+ res = table_;
+ }
+ nhits = res ? grn_table_size(ctx, res) : 0;
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
+ ":", "select(%d)", nhits);
+
+ if (res) {
+ uint32_t ngkeys;
+ grn_table_sort_key *gkeys = NULL;
+ int result_size = 1;
+ if (!ctx->rc && drilldown_len) {
+ gkeys = grn_table_sort_key_from_str(ctx,
+ drilldown, drilldown_len,
+ res, &ngkeys);
+ if (gkeys) {
+ result_size += ngkeys;
+ }
+ }
+
+ if (adjuster && adjuster_len) {
+ grn_obj *adjuster_;
+ grn_obj *v;
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, table_, adjuster_, v);
+ if (adjuster_ && v) {
+ grn_rc rc;
+ rc = grn_expr_parse(ctx, adjuster_, adjuster, adjuster_len, NULL,
+ GRN_OP_MATCH, GRN_OP_ADJUST,
+ GRN_EXPR_SYNTAX_ADJUSTER);
+ if (rc) {
+ grn_obj_unlink(ctx, adjuster_);
+ goto exit;
+ }
+ cacheable *= ((grn_expr *)adjuster_)->cacheable;
+ taintable += ((grn_expr *)adjuster_)->taintable;
+ grn_select_apply_adjuster(ctx, table_, res, adjuster_);
+ grn_obj_unlink(ctx, adjuster_);
+ }
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
+ ":", "adjust(%d)", nhits);
+ }
+
+ if (scorer && scorer_len) {
+ grn_obj *v;
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, res, scorer_, v);
+ if (scorer_ && v) {
+ grn_table_cursor *tc;
+ grn_expr_parse(ctx, scorer_, scorer, scorer_len, NULL, GRN_OP_MATCH, GRN_OP_AND,
+ GRN_EXPR_SYNTAX_SCRIPT|GRN_EXPR_ALLOW_UPDATE);
+ cacheable *= ((grn_expr *)scorer_)->cacheable;
+ taintable += ((grn_expr *)scorer_)->taintable;
+ if ((tc = grn_table_cursor_open(ctx, res, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) {
+ GRN_RECORD_SET(ctx, v, id);
+ grn_expr_exec(ctx, scorer_, 0);
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ grn_obj_unlink(ctx, scorer_);
+ }
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
+ ":", "score(%d)", nhits);
+ }
+
+ GRN_OUTPUT_ARRAY_OPEN("RESULT", result_size);
+
+ grn_normalize_offset_and_limit(ctx, nhits, &offset, &limit);
+
+ if (sortby_len &&
+ (keys = grn_table_sort_key_from_str(ctx, sortby, sortby_len, res, &nkeys))) {
+ if ((sorted = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_OBJ_TABLE_NO_KEY, NULL, res))) {
+ grn_table_sort(ctx, res, offset, limit, sorted, keys, nkeys);
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
+ ":", "sort(%d)", limit);
+ GRN_OBJ_FORMAT_INIT(&format, nhits, 0, limit, offset);
+ format.flags =
+ GRN_OBJ_FORMAT_WITH_COLUMN_NAMES|
+ GRN_OBJ_FORMAT_XML_ELEMENT_RESULTSET;
+ if (is_output_columns_format_v1(ctx, output_columns, output_columns_len)) {
+ grn_obj_columns(ctx, sorted, output_columns, output_columns_len,
+ &format.columns);
+ } else {
+ grn_obj *v;
+ grn_obj *condition_ptr;
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, sorted, format.expression, v);
+ grn_expr_parse(ctx, format.expression,
+ output_columns, output_columns_len, NULL,
+ GRN_OP_MATCH, GRN_OP_AND,
+ GRN_EXPR_SYNTAX_OUTPUT_COLUMNS);
+ condition_ptr =
+ grn_expr_get_or_add_var(ctx, format.expression,
+ GRN_SELECT_INTERNAL_VAR_CONDITION,
+ strlen(GRN_SELECT_INTERNAL_VAR_CONDITION));
+ GRN_PTR_INIT(condition_ptr, 0, GRN_DB_OBJECT);
+ GRN_PTR_SET(ctx, condition_ptr, cond);
+ }
+ GRN_OUTPUT_OBJ(sorted, &format);
+ GRN_OBJ_FORMAT_FIN(ctx, &format);
+ grn_obj_unlink(ctx, sorted);
+ }
+ grn_table_sort_key_close(ctx, keys, nkeys);
+ } else {
+ if (!ctx->rc) {
+ GRN_OBJ_FORMAT_INIT(&format, nhits, offset, limit, offset);
+ format.flags =
+ GRN_OBJ_FORMAT_WITH_COLUMN_NAMES|
+ GRN_OBJ_FORMAT_XML_ELEMENT_RESULTSET;
+ if (is_output_columns_format_v1(ctx, output_columns, output_columns_len)) {
+ grn_obj_columns(ctx, res, output_columns, output_columns_len,
+ &format.columns);
+ } else {
+ grn_obj *v;
+ grn_obj *condition_ptr;
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, res, format.expression, v);
+ grn_expr_parse(ctx, format.expression,
+ output_columns, output_columns_len, NULL,
+ GRN_OP_MATCH, GRN_OP_AND,
+ GRN_EXPR_SYNTAX_OUTPUT_COLUMNS);
+ condition_ptr =
+ grn_expr_get_or_add_var(ctx, format.expression,
+ GRN_SELECT_INTERNAL_VAR_CONDITION,
+ strlen(GRN_SELECT_INTERNAL_VAR_CONDITION));
+ GRN_PTR_INIT(condition_ptr, 0, GRN_DB_OBJECT);
+ GRN_PTR_SET(ctx, condition_ptr, cond);
+ }
+ GRN_OUTPUT_OBJ(res, &format);
+ GRN_OBJ_FORMAT_FIN(ctx, &format);
+ }
+ }
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
+ ":", "output(%d)", limit);
+ if (!ctx->rc && drilldown_len) {
+ uint32_t i;
+ grn_table_group_result g = {NULL, 0, 0, 1, GRN_TABLE_GROUP_CALC_COUNT, 0};
+ if (gkeys) {
+ for (i = 0; i < ngkeys; i++) {
+ if ((g.table = grn_table_create_for_group(ctx, NULL, 0, NULL,
+ gkeys[i].key, res, 0))) {
+ int n_drilldown_offset = drilldown_offset,
+ n_drilldown_limit = drilldown_limit;
+
+ grn_table_group(ctx, res, &gkeys[i], 1, &g, 1);
+ nhits = grn_table_size(ctx, g.table);
+
+ grn_normalize_offset_and_limit(ctx, nhits,
+ &n_drilldown_offset, &n_drilldown_limit);
+
+ if (drilldown_sortby_len) {
+ if ((keys = grn_table_sort_key_from_str(ctx,
+ drilldown_sortby, drilldown_sortby_len,
+ g.table, &nkeys))) {
+ if ((sorted = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY,
+ NULL, g.table))) {
+ grn_table_sort(ctx, g.table, n_drilldown_offset, n_drilldown_limit,
+ sorted, keys, nkeys);
+ GRN_OBJ_FORMAT_INIT(&format, nhits, 0,
+ n_drilldown_limit, n_drilldown_offset);
+ format.flags =
+ GRN_OBJ_FORMAT_WITH_COLUMN_NAMES|
+ GRN_OBJ_FORMAT_XML_ELEMENT_NAVIGATIONENTRY;
+ grn_obj_columns(ctx, sorted,
+ drilldown_output_columns, drilldown_output_columns_len,
+ &format.columns);
+ GRN_OUTPUT_OBJ(sorted, &format);
+ GRN_OBJ_FORMAT_FIN(ctx, &format);
+ grn_obj_unlink(ctx, sorted);
+ }
+ grn_table_sort_key_close(ctx, keys, nkeys);
+ }
+ } else {
+ GRN_OBJ_FORMAT_INIT(&format, nhits, n_drilldown_offset,
+ n_drilldown_limit, n_drilldown_offset);
+ format.flags =
+ GRN_OBJ_FORMAT_WITH_COLUMN_NAMES|
+ GRN_OBJ_FORMAT_XML_ELEMENT_NAVIGATIONENTRY;
+ grn_obj_columns(ctx, g.table, drilldown_output_columns,
+ drilldown_output_columns_len, &format.columns);
+ GRN_OUTPUT_OBJ(g.table, &format);
+ GRN_OBJ_FORMAT_FIN(ctx, &format);
+ }
+ grn_obj_unlink(ctx, g.table);
+ }
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
+ ":", "drilldown(%d)", nhits);
+ }
+ grn_table_sort_key_close(ctx, gkeys, ngkeys);
+ }
+ }
+ if (res != table_) { grn_obj_unlink(ctx, res); }
+ } else {
+ GRN_OUTPUT_ARRAY_OPEN("RESULT", 0);
+ }
+ GRN_OUTPUT_ARRAY_CLOSE();
+ if (!ctx->rc && cacheable && cache_key_size <= GRN_TABLE_MAX_KEY_SIZE
+ && (!cache || cache_len != 2 || *cache != 'n' || *(cache + 1) != 'o')) {
+ grn_cache_update(ctx, cache_obj, cache_key, cache_key_size, outbuf);
+ }
+ if (taintable) { grn_db_touch(ctx, DB_OBJ(table_)->db); }
+ grn_obj_unlink(ctx, table_);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "invalid table name: <%.*s>", table_len, table);
+ }
+exit:
+ if (match_escalation_threshold_len) {
+ grn_ctx_set_match_escalation_threshold(ctx, original_threshold);
+ }
+ if (match_columns_) {
+ grn_obj_unlink(ctx, match_columns_);
+ }
+ if (cond) {
+ grn_obj_unlink(ctx, cond);
+ }
+ /* GRN_LOG(ctx, GRN_LOG_NONE, "%d", ctx->seqno); */
+ return ctx->rc;
+}
+
+static grn_obj *
+proc_select(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ int offset = GRN_TEXT_LEN(VAR(7))
+ ? grn_atoi(GRN_TEXT_VALUE(VAR(7)), GRN_BULK_CURR(VAR(7)), NULL)
+ : 0;
+ int limit = GRN_TEXT_LEN(VAR(8))
+ ? grn_atoi(GRN_TEXT_VALUE(VAR(8)), GRN_BULK_CURR(VAR(8)), NULL)
+ : DEFAULT_LIMIT;
+ const char *output_columns = GRN_TEXT_VALUE(VAR(6));
+ uint32_t output_columns_len = GRN_TEXT_LEN(VAR(6));
+ const char *drilldown_output_columns = GRN_TEXT_VALUE(VAR(11));
+ uint32_t drilldown_output_columns_len = GRN_TEXT_LEN(VAR(11));
+ int drilldown_offset = GRN_TEXT_LEN(VAR(12))
+ ? grn_atoi(GRN_TEXT_VALUE(VAR(12)), GRN_BULK_CURR(VAR(12)), NULL)
+ : 0;
+ int drilldown_limit = GRN_TEXT_LEN(VAR(13))
+ ? grn_atoi(GRN_TEXT_VALUE(VAR(13)), GRN_BULK_CURR(VAR(13)), NULL)
+ : DEFAULT_DRILLDOWN_LIMIT;
+ grn_obj *query_expansion = VAR(16);
+ grn_obj *query_expander = VAR(18);
+ grn_obj *adjuster = VAR(19);
+ if (GRN_TEXT_LEN(query_expander) == 0 && GRN_TEXT_LEN(query_expansion) > 0) {
+ query_expander = query_expansion;
+ }
+ if (!output_columns_len) {
+ output_columns = DEFAULT_OUTPUT_COLUMNS;
+ output_columns_len = strlen(DEFAULT_OUTPUT_COLUMNS);
+ }
+ if (!drilldown_output_columns_len) {
+ drilldown_output_columns = DEFAULT_DRILLDOWN_OUTPUT_COLUMNS;
+ drilldown_output_columns_len = strlen(DEFAULT_DRILLDOWN_OUTPUT_COLUMNS);
+ }
+ if (grn_select(ctx, GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)),
+ GRN_TEXT_VALUE(VAR(1)), GRN_TEXT_LEN(VAR(1)),
+ GRN_TEXT_VALUE(VAR(2)), GRN_TEXT_LEN(VAR(2)),
+ GRN_TEXT_VALUE(VAR(3)), GRN_TEXT_LEN(VAR(3)),
+ GRN_TEXT_VALUE(VAR(4)), GRN_TEXT_LEN(VAR(4)),
+ GRN_TEXT_VALUE(VAR(5)), GRN_TEXT_LEN(VAR(5)),
+ output_columns, output_columns_len,
+ offset, limit,
+ GRN_TEXT_VALUE(VAR(9)), GRN_TEXT_LEN(VAR(9)),
+ GRN_TEXT_VALUE(VAR(10)), GRN_TEXT_LEN(VAR(10)),
+ drilldown_output_columns, drilldown_output_columns_len,
+ drilldown_offset, drilldown_limit,
+ GRN_TEXT_VALUE(VAR(14)), GRN_TEXT_LEN(VAR(14)),
+ GRN_TEXT_VALUE(VAR(15)), GRN_TEXT_LEN(VAR(15)),
+ GRN_TEXT_VALUE(query_expander), GRN_TEXT_LEN(query_expander),
+ GRN_TEXT_VALUE(VAR(17)), GRN_TEXT_LEN(VAR(17)),
+ GRN_TEXT_VALUE(adjuster), GRN_TEXT_LEN(adjuster))) {
+ }
+ return NULL;
+}
+
+static grn_obj *
+proc_define_selector(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ uint32_t i, nvars;
+ grn_expr_var *vars;
+ grn_proc_get_info(ctx, user_data, &vars, &nvars, NULL);
+ for (i = 1; i < nvars; i++) {
+ GRN_TEXT_SET(ctx, &((vars + i)->value),
+ GRN_TEXT_VALUE(VAR(i)), GRN_TEXT_LEN(VAR(i)));
+ }
+ grn_proc_create(ctx,
+ GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)),
+ GRN_PROC_COMMAND, proc_select, NULL, NULL, nvars - 1, vars + 1);
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static grn_obj *
+proc_load(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_load(ctx, grn_get_ctype(VAR(4)),
+ GRN_TEXT_VALUE(VAR(1)), GRN_TEXT_LEN(VAR(1)),
+ GRN_TEXT_VALUE(VAR(2)), GRN_TEXT_LEN(VAR(2)),
+ GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)),
+ GRN_TEXT_VALUE(VAR(3)), GRN_TEXT_LEN(VAR(3)),
+ GRN_TEXT_VALUE(VAR(5)), GRN_TEXT_LEN(VAR(5)));
+ if (ctx->impl->loader.stat != GRN_LOADER_END) {
+ grn_ctx_set_next_expr(ctx, grn_proc_get_info(ctx, user_data, NULL, NULL, NULL));
+ } else {
+ GRN_OUTPUT_INT64(ctx->impl->loader.nrecords);
+ if (ctx->impl->loader.table) {
+ grn_db_touch(ctx, DB_OBJ(ctx->impl->loader.table)->db);
+ }
+ /* maybe necessary : grn_ctx_loader_clear(ctx); */
+ }
+ return NULL;
+}
+
+static grn_obj *
+proc_status(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_timeval now;
+ grn_cache *cache;
+ grn_cache_statistics statistics;
+
+ grn_timeval_now(ctx, &now);
+ cache = grn_cache_current_get(ctx);
+ grn_cache_get_statistics(ctx, cache, &statistics);
+ GRN_OUTPUT_MAP_OPEN("RESULT", 9);
+ GRN_OUTPUT_CSTR("alloc_count");
+ GRN_OUTPUT_INT32(grn_alloc_count());
+ GRN_OUTPUT_CSTR("starttime");
+ GRN_OUTPUT_INT32(grn_starttime.tv_sec);
+ GRN_OUTPUT_CSTR("uptime");
+ GRN_OUTPUT_INT32(now.tv_sec - grn_starttime.tv_sec);
+ GRN_OUTPUT_CSTR("version");
+ GRN_OUTPUT_CSTR(grn_get_version());
+ GRN_OUTPUT_CSTR("n_queries");
+ GRN_OUTPUT_INT64(statistics.nfetches);
+ GRN_OUTPUT_CSTR("cache_hit_rate");
+ if (statistics.nfetches == 0) {
+ GRN_OUTPUT_FLOAT(0.0);
+ } else {
+ double cache_hit_rate;
+ cache_hit_rate = (double)statistics.nhits / (double)statistics.nfetches;
+ GRN_OUTPUT_FLOAT(cache_hit_rate * 100.0);
+ }
+ GRN_OUTPUT_CSTR("command_version");
+ GRN_OUTPUT_INT32(grn_ctx_get_command_version(ctx));
+ GRN_OUTPUT_CSTR("default_command_version");
+ GRN_OUTPUT_INT32(grn_get_default_command_version());
+ GRN_OUTPUT_CSTR("max_command_version");
+ GRN_OUTPUT_INT32(GRN_COMMAND_VERSION_MAX);
+ GRN_OUTPUT_MAP_CLOSE();
+ return NULL;
+}
+
+static grn_obj_flags
+grn_parse_table_create_flags(grn_ctx *ctx, const char *nptr, const char *end)
+{
+ grn_obj_flags flags = 0;
+ while (nptr < end) {
+ if (*nptr == '|' || *nptr == ' ') {
+ nptr += 1;
+ continue;
+ }
+ if (!memcmp(nptr, "TABLE_HASH_KEY", 14)) {
+ flags |= GRN_OBJ_TABLE_HASH_KEY;
+ nptr += 14;
+ } else if (!memcmp(nptr, "TABLE_PAT_KEY", 13)) {
+ flags |= GRN_OBJ_TABLE_PAT_KEY;
+ nptr += 13;
+ } else if (!memcmp(nptr, "TABLE_DAT_KEY", 13)) {
+ flags |= GRN_OBJ_TABLE_DAT_KEY;
+ nptr += 13;
+ } else if (!memcmp(nptr, "TABLE_NO_KEY", 12)) {
+ flags |= GRN_OBJ_TABLE_NO_KEY;
+ nptr += 12;
+ } else if (!memcmp(nptr, "KEY_NORMALIZE", 13)) {
+ flags |= GRN_OBJ_KEY_NORMALIZE;
+ nptr += 13;
+ } else if (!memcmp(nptr, "KEY_WITH_SIS", 12)) {
+ flags |= GRN_OBJ_KEY_WITH_SIS;
+ nptr += 12;
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "invalid flags option: %.*s",
+ (int)(end - nptr), nptr);
+ return 0;
+ }
+ }
+ return flags;
+}
+
+static grn_obj_flags
+grn_parse_column_create_flags(grn_ctx *ctx, const char *nptr, const char *end)
+{
+ grn_obj_flags flags = 0;
+ while (nptr < end) {
+ if (*nptr == '|' || *nptr == ' ') {
+ nptr += 1;
+ continue;
+ }
+ if (!memcmp(nptr, "COLUMN_SCALAR", 13)) {
+ flags |= GRN_OBJ_COLUMN_SCALAR;
+ nptr += 13;
+ } else if (!memcmp(nptr, "COLUMN_VECTOR", 13)) {
+ flags |= GRN_OBJ_COLUMN_VECTOR;
+ nptr += 13;
+ } else if (!memcmp(nptr, "COLUMN_INDEX", 12)) {
+ flags |= GRN_OBJ_COLUMN_INDEX;
+ nptr += 12;
+ } else if (!memcmp(nptr, "WITH_SECTION", 12)) {
+ flags |= GRN_OBJ_WITH_SECTION;
+ nptr += 12;
+ } else if (!memcmp(nptr, "WITH_WEIGHT", 11)) {
+ flags |= GRN_OBJ_WITH_WEIGHT;
+ nptr += 11;
+ } else if (!memcmp(nptr, "WITH_POSITION", 13)) {
+ flags |= GRN_OBJ_WITH_POSITION;
+ nptr += 13;
+ } else if (!memcmp(nptr, "RING_BUFFER", 11)) {
+ flags |= GRN_OBJ_RING_BUFFER;
+ nptr += 11;
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "invalid flags option: %.*s",
+ (int)(end - nptr), nptr);
+ return 0;
+ }
+ }
+ return flags;
+}
+
+static void
+grn_table_create_flags_to_text(grn_ctx *ctx, grn_obj *buf, grn_obj_flags flags)
+{
+ GRN_BULK_REWIND(buf);
+ switch (flags & GRN_OBJ_TABLE_TYPE_MASK) {
+ case GRN_OBJ_TABLE_HASH_KEY:
+ GRN_TEXT_PUTS(ctx, buf, "TABLE_HASH_KEY");
+ break;
+ case GRN_OBJ_TABLE_PAT_KEY:
+ GRN_TEXT_PUTS(ctx, buf, "TABLE_PAT_KEY");
+ break;
+ case GRN_OBJ_TABLE_DAT_KEY:
+ GRN_TEXT_PUTS(ctx, buf, "TABLE_DAT_KEY");
+ break;
+ case GRN_OBJ_TABLE_NO_KEY:
+ GRN_TEXT_PUTS(ctx, buf, "TABLE_NO_KEY");
+ break;
+ }
+ if (flags & GRN_OBJ_KEY_WITH_SIS) {
+ GRN_TEXT_PUTS(ctx, buf, "|KEY_WITH_SIS");
+ }
+ if (flags & GRN_OBJ_KEY_NORMALIZE) {
+ GRN_TEXT_PUTS(ctx, buf, "|KEY_NORMALIZE");
+ }
+ if (flags & GRN_OBJ_PERSISTENT) {
+ GRN_TEXT_PUTS(ctx, buf, "|PERSISTENT");
+ }
+}
+
+static void
+grn_column_create_flags_to_text(grn_ctx *ctx, grn_obj *buf, grn_obj_flags flags)
+{
+ GRN_BULK_REWIND(buf);
+ switch (flags & GRN_OBJ_COLUMN_TYPE_MASK) {
+ case GRN_OBJ_COLUMN_SCALAR:
+ GRN_TEXT_PUTS(ctx, buf, "COLUMN_SCALAR");
+ break;
+ case GRN_OBJ_COLUMN_VECTOR:
+ GRN_TEXT_PUTS(ctx, buf, "COLUMN_VECTOR");
+ if (flags & GRN_OBJ_WITH_WEIGHT) {
+ GRN_TEXT_PUTS(ctx, buf, "|WITH_WEIGHT");
+ }
+ break;
+ case GRN_OBJ_COLUMN_INDEX:
+ GRN_TEXT_PUTS(ctx, buf, "COLUMN_INDEX");
+ if (flags & GRN_OBJ_WITH_SECTION) {
+ GRN_TEXT_PUTS(ctx, buf, "|WITH_SECTION");
+ }
+ if (flags & GRN_OBJ_WITH_WEIGHT) {
+ GRN_TEXT_PUTS(ctx, buf, "|WITH_WEIGHT");
+ }
+ if (flags & GRN_OBJ_WITH_POSITION) {
+ GRN_TEXT_PUTS(ctx, buf, "|WITH_POSITION");
+ }
+ break;
+ }
+ switch (flags & GRN_OBJ_COMPRESS_MASK) {
+ case GRN_OBJ_COMPRESS_NONE:
+ break;
+ case GRN_OBJ_COMPRESS_ZLIB:
+ GRN_TEXT_PUTS(ctx, buf, "|COMPRESS_ZLIB");
+ break;
+ case GRN_OBJ_COMPRESS_LZO:
+ GRN_TEXT_PUTS(ctx, buf, "|COMPRESS_LZO");
+ break;
+ }
+ if (flags & GRN_OBJ_PERSISTENT) {
+ GRN_TEXT_PUTS(ctx, buf, "|PERSISTENT");
+ }
+}
+
+static grn_obj *
+proc_table_create(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *table;
+ const char *rest;
+ grn_obj_flags flags = grn_atoi(GRN_TEXT_VALUE(VAR(1)),
+ GRN_BULK_CURR(VAR(1)), &rest);
+ if (GRN_TEXT_VALUE(VAR(1)) == rest) {
+ flags = grn_parse_table_create_flags(ctx, GRN_TEXT_VALUE(VAR(1)),
+ GRN_BULK_CURR(VAR(1)));
+ if (ctx->rc) { goto exit; }
+ }
+ if (GRN_TEXT_LEN(VAR(0))) {
+ grn_obj *key_type = NULL, *value_type = NULL;
+ if (GRN_TEXT_LEN(VAR(2)) > 0) {
+ key_type = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(2)),
+ GRN_TEXT_LEN(VAR(2)));
+ if (!key_type) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] key type doesn't exist: <%.*s> (%.*s)",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)),
+ (int)GRN_TEXT_LEN(VAR(2)), GRN_TEXT_VALUE(VAR(2)));
+ return NULL;
+ }
+ }
+ if (GRN_TEXT_LEN(VAR(3)) > 0) {
+ value_type = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(3)),
+ GRN_TEXT_LEN(VAR(3)));
+ if (!value_type) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] value type doesn't exist: <%.*s> (%.*s)",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)),
+ (int)GRN_TEXT_LEN(VAR(3)), GRN_TEXT_VALUE(VAR(3)));
+ return NULL;
+ }
+ }
+ flags |= GRN_OBJ_PERSISTENT;
+ table = grn_table_create(ctx,
+ GRN_TEXT_VALUE(VAR(0)),
+ GRN_TEXT_LEN(VAR(0)),
+ NULL, flags,
+ key_type,
+ value_type);
+ if (table) {
+ grn_obj *normalizer_name;
+ grn_obj_set_info(ctx, table,
+ GRN_INFO_DEFAULT_TOKENIZER,
+ grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(4)),
+ GRN_TEXT_LEN(VAR(4))));
+ normalizer_name = VAR(5);
+ if (GRN_TEXT_LEN(normalizer_name) > 0) {
+ grn_obj_set_info(ctx, table,
+ GRN_INFO_NORMALIZER,
+ grn_ctx_get(ctx,
+ GRN_TEXT_VALUE(normalizer_name),
+ GRN_TEXT_LEN(normalizer_name)));
+ }
+ grn_obj_unlink(ctx, table);
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][create] should not create anonymous table");
+ }
+exit:
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static grn_obj *
+proc_table_remove(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *table;
+ table = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(0)),
+ GRN_TEXT_LEN(VAR(0)));
+ if (table) {
+ grn_obj_remove(ctx,table);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "table not found.");
+ }
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static grn_obj *
+proc_table_rename(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_obj *table = NULL;
+ if (GRN_TEXT_LEN(VAR(0)) == 0) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc, "[table][rename] table name isn't specified");
+ goto exit;
+ }
+ table = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)));
+ if (!table) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[table][rename] table isn't found: <%.*s>",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)));
+ goto exit;
+ }
+ if (GRN_TEXT_LEN(VAR(1)) == 0) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[table][rename] new table name isn't specified: <%.*s>",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)));
+ goto exit;
+ }
+ rc = grn_table_rename(ctx, table,
+ GRN_TEXT_VALUE(VAR(1)), GRN_TEXT_LEN(VAR(1)));
+ if (rc != GRN_SUCCESS && ctx->rc == GRN_SUCCESS) {
+ ERR(rc,
+ "[table][rename] failed to rename: <%.*s> -> <%.*s>",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)),
+ (int)GRN_TEXT_LEN(VAR(1)), GRN_TEXT_VALUE(VAR(1)));
+ }
+exit:
+ GRN_OUTPUT_BOOL(!rc);
+ if (table) { grn_obj_unlink(ctx, table); }
+ return NULL;
+}
+
+static grn_obj *
+proc_column_create(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *column, *table = NULL, *type = NULL;
+ const char *rest;
+ grn_obj_flags flags = grn_atoi(GRN_TEXT_VALUE(VAR(2)),
+ GRN_BULK_CURR(VAR(2)), &rest);
+ if (GRN_TEXT_VALUE(VAR(2)) == rest) {
+ flags = grn_parse_column_create_flags(ctx, GRN_TEXT_VALUE(VAR(2)),
+ GRN_BULK_CURR(VAR(2)));
+ if (ctx->rc) { goto exit; }
+ }
+ table = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)));
+ if (!table) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][create] table doesn't exist: <%.*s>",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)));
+ goto exit;
+ }
+ type = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(3)),
+ GRN_TEXT_LEN(VAR(3)));
+ if (!type) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[column][create] type doesn't exist: <%.*s>",
+ (int)GRN_TEXT_LEN(VAR(3)), GRN_TEXT_VALUE(VAR(3))) ;
+ goto exit;
+ }
+ if (GRN_TEXT_LEN(VAR(1))) {
+ flags |= GRN_OBJ_PERSISTENT;
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "[column][create] name is missing");
+ goto exit;
+ }
+ column = grn_column_create(ctx, table,
+ GRN_TEXT_VALUE(VAR(1)),
+ GRN_TEXT_LEN(VAR(1)),
+ NULL, flags, type);
+ if (column) {
+ if (GRN_TEXT_LEN(VAR(4))) {
+ grn_obj sources, source_ids, **p, **pe;
+ GRN_PTR_INIT(&sources, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ GRN_UINT32_INIT(&source_ids, GRN_OBJ_VECTOR);
+ grn_obj_columns(ctx, type,
+ GRN_TEXT_VALUE(VAR(4)),
+ GRN_TEXT_LEN(VAR(4)),
+ &sources);
+ p = (grn_obj **)GRN_BULK_HEAD(&sources);
+ pe = (grn_obj **)GRN_BULK_CURR(&sources);
+ for (; p < pe; p++) {
+ grn_id source_id = grn_obj_id(ctx, *p);
+ if ((*p)->header.type == GRN_ACCESSOR) {
+ /* todo : if "_key" assigned */
+ source_id = grn_obj_id(ctx, type);
+ }
+ if (source_id) {
+ GRN_UINT32_PUT(ctx, &source_ids, source_id);
+ }
+ grn_obj_unlink(ctx, *p);
+ }
+ if (GRN_BULK_VSIZE(&source_ids)) {
+ grn_obj_set_info(ctx, column, GRN_INFO_SOURCE, &source_ids);
+ }
+ GRN_OBJ_FIN(ctx, &source_ids);
+ GRN_OBJ_FIN(ctx, &sources);
+ }
+ grn_obj_unlink(ctx, column);
+ }
+exit:
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ if (table) { grn_obj_unlink(ctx, table); }
+ if (type) { grn_obj_unlink(ctx, type); }
+ return NULL;
+}
+
+static grn_obj *
+proc_column_remove(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *table, *col;
+ char *colname,fullname[GRN_TABLE_MAX_KEY_SIZE];
+ unsigned int colname_len,fullname_len;
+
+ table = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(0)),
+ GRN_TEXT_LEN(VAR(0)));
+
+ colname = GRN_TEXT_VALUE(VAR(1));
+ colname_len = GRN_TEXT_LEN(VAR(1));
+
+ if ((fullname_len = grn_obj_name(ctx, table, fullname, GRN_TABLE_MAX_KEY_SIZE))) {
+ fullname[fullname_len] = GRN_DB_DELIMITER;
+ memcpy((fullname + fullname_len + 1), colname, colname_len);
+ fullname_len += colname_len + 1;
+ //TODO:check fullname_len < GRN_TABLE_MAX_KEY_SIZE
+ col = grn_ctx_get(ctx, fullname, fullname_len);
+ if (col) {
+ grn_obj_remove(ctx, col);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "column not found.");
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "table not found.");
+ }
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static grn_obj *
+proc_column_rename(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_obj *table = NULL;
+ grn_obj *column = NULL;
+ if (GRN_TEXT_LEN(VAR(0)) == 0) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc, "[column][rename] table name isn't specified");
+ goto exit;
+ }
+ table = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)));
+ if (!table) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[column][rename] table isn't found: <%.*s>",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)));
+ goto exit;
+ }
+ if (GRN_TEXT_LEN(VAR(1)) == 0) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[column][rename] column name isn't specified: <%.*s>",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)));
+ goto exit;
+ }
+ column = grn_obj_column(ctx, table,
+ GRN_TEXT_VALUE(VAR(1)), GRN_TEXT_LEN(VAR(1)));
+ if (!column) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[column][rename] column isn't found: <%.*s.%.*s>",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)),
+ (int)GRN_TEXT_LEN(VAR(1)), GRN_TEXT_VALUE(VAR(1)));
+ goto exit;
+ }
+ if (GRN_TEXT_LEN(VAR(2)) == 0) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[column][rename] new column name isn't specified: <%.*s.%.*s>",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)),
+ (int)GRN_TEXT_LEN(VAR(1)), GRN_TEXT_VALUE(VAR(1)));
+ goto exit;
+ }
+ rc = grn_column_rename(ctx, column,
+ GRN_TEXT_VALUE(VAR(2)), GRN_TEXT_LEN(VAR(2)));
+ if (rc != GRN_SUCCESS && ctx->rc == GRN_SUCCESS) {
+ ERR(rc,
+ "[column][rename] failed to rename: <%.*s.%.*s> -> <%.*s.%.*s>",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)),
+ (int)GRN_TEXT_LEN(VAR(1)), GRN_TEXT_VALUE(VAR(1)),
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)),
+ (int)GRN_TEXT_LEN(VAR(2)), GRN_TEXT_VALUE(VAR(2)));
+ }
+exit:
+ GRN_OUTPUT_BOOL(!rc);
+ if (column) { grn_obj_unlink(ctx, column); }
+ if (table) { grn_obj_unlink(ctx, table); }
+ return NULL;
+}
+
+#define GRN_STRLEN(s) ((s) ? strlen(s) : 0)
+
+static void
+output_column_name(grn_ctx *ctx, grn_obj *column)
+{
+ grn_obj bulk;
+ int name_len;
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+
+ GRN_TEXT_INIT(&bulk, GRN_OBJ_DO_SHALLOW_COPY);
+ name_len = grn_column_name(ctx, column, name, GRN_TABLE_MAX_KEY_SIZE);
+ GRN_TEXT_SET(ctx, &bulk, name, name_len);
+
+ GRN_OUTPUT_OBJ(&bulk, NULL);
+ GRN_OBJ_FIN(ctx, &bulk);
+}
+
+static void
+output_object_name(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_obj bulk;
+ int name_len;
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+
+ if (obj) {
+ GRN_TEXT_INIT(&bulk, GRN_OBJ_DO_SHALLOW_COPY);
+ name_len = grn_obj_name(ctx, obj, name, GRN_TABLE_MAX_KEY_SIZE);
+ GRN_TEXT_SET(ctx, &bulk, name, name_len);
+ } else {
+ GRN_VOID_INIT(&bulk);
+ }
+
+ GRN_OUTPUT_OBJ(&bulk, NULL);
+ GRN_OBJ_FIN(ctx, &bulk);
+}
+
+static void
+output_object_id_name(grn_ctx *ctx, grn_id id)
+{
+ grn_obj *obj = NULL;
+
+ if (id != GRN_ID_NIL) {
+ obj = grn_ctx_at(ctx, id);
+ }
+
+ output_object_name(ctx, obj);
+}
+
+static int
+output_column_info(grn_ctx *ctx, grn_obj *column)
+{
+ grn_obj o;
+ grn_id id;
+ const char *type;
+ const char *path;
+
+ switch (column->header.type) {
+ case GRN_COLUMN_FIX_SIZE:
+ type = "fix";
+ break;
+ case GRN_COLUMN_VAR_SIZE:
+ type = "var";
+ break;
+ case GRN_COLUMN_INDEX:
+ type = "index";
+ break;
+ default:
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "invalid header type %d\n", column->header.type);
+ return 0;
+ }
+ id = grn_obj_id(ctx, column);
+ path = grn_obj_path(ctx, column);
+ GRN_TEXT_INIT(&o, 0);
+ GRN_OUTPUT_ARRAY_OPEN("COLUMN", 8);
+ GRN_OUTPUT_INT64(id);
+ output_column_name(ctx, column);
+ GRN_OUTPUT_CSTR(path);
+ GRN_OUTPUT_CSTR(type);
+ grn_column_create_flags_to_text(ctx, &o, column->header.flags);
+ GRN_OUTPUT_OBJ(&o, NULL);
+ output_object_id_name(ctx, column->header.domain);
+ output_object_id_name(ctx, grn_obj_get_range(ctx, column));
+ {
+ grn_db_obj *obj = (grn_db_obj *)column;
+ grn_id *s = obj->source;
+ int i = 0, n = obj->source_size / sizeof(grn_id);
+ GRN_OUTPUT_ARRAY_OPEN("SOURCES", n);
+ for (i = 0; i < n; i++, s++) {
+ output_object_id_name(ctx, *s);
+ }
+ GRN_OUTPUT_ARRAY_CLOSE();
+
+ }
+ // output_obj_source(ctx, (grn_db_obj *)column);
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OBJ_FIN(ctx, &o);
+ return 1;
+}
+
+static grn_obj *
+proc_column_list(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *table;
+ if ((table = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(0)),
+ GRN_TEXT_LEN(VAR(0))))) {
+ grn_hash *cols;
+ grn_obj *col;
+ int column_list_size = -1;
+#ifdef GRN_WITH_MESSAGE_PACK
+ column_list_size = 1; /* [header, (key), (COLUMNS)] */
+ if ((col = grn_obj_column(ctx, table,
+ GRN_COLUMN_NAME_KEY,
+ GRN_COLUMN_NAME_KEY_LEN))) {
+ column_list_size++;
+ grn_obj_unlink(ctx, col);
+ }
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ column_list_size += grn_table_columns(ctx, table, NULL, 0,
+ (grn_obj *)cols);
+ grn_hash_close(ctx, cols);
+ }
+#endif
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ GRN_OUTPUT_ARRAY_OPEN("COLUMN_LIST", column_list_size);
+ GRN_OUTPUT_ARRAY_OPEN("HEADER", 8);
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("id");
+ GRN_OUTPUT_CSTR("UInt32");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("name");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("path");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("type");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("flags");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("domain");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("range");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("source");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_CLOSE();
+ if ((col = grn_obj_column(ctx, table,
+ GRN_COLUMN_NAME_KEY,
+ GRN_COLUMN_NAME_KEY_LEN))) {
+ int name_len;
+ char name_buf[GRN_TABLE_MAX_KEY_SIZE];
+ grn_id id;
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_OUTPUT_ARRAY_OPEN("COLUMN", 8);
+ id = grn_obj_id(ctx, table);
+ GRN_OUTPUT_INT64(id);
+ GRN_OUTPUT_CSTR(GRN_COLUMN_NAME_KEY);
+ GRN_OUTPUT_CSTR("");
+ GRN_OUTPUT_CSTR("");
+ grn_column_create_flags_to_text(ctx, &buf, 0);
+ GRN_OUTPUT_OBJ(&buf, NULL);
+ name_len = grn_obj_name(ctx, table, name_buf, GRN_TABLE_MAX_KEY_SIZE);
+ GRN_OUTPUT_STR(name_buf, name_len);
+ output_object_id_name(ctx, table->header.domain);
+ GRN_OUTPUT_ARRAY_OPEN("SOURCES", 0);
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OBJ_FIN(ctx, &buf);
+ grn_obj_unlink(ctx, col);
+ }
+ if (grn_table_columns(ctx, table, NULL, 0, (grn_obj *)cols) >= 0) {
+ grn_id *key;
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ if ((col = grn_ctx_at(ctx, *key))) {
+ output_column_info(ctx, col);
+ grn_obj_unlink(ctx, col);
+ }
+ });
+ }
+ GRN_OUTPUT_ARRAY_CLOSE();
+ grn_hash_close(ctx, cols);
+ }
+ grn_obj_unlink(ctx, table);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "table '%.*s' does not exist.",
+ (int)GRN_TEXT_LEN(VAR(0)),
+ GRN_TEXT_VALUE(VAR(0)));
+ }
+ return NULL;
+}
+
+static grn_bool
+is_table(grn_obj *obj)
+{
+ switch (obj->header.type) {
+ case GRN_TABLE_HASH_KEY:
+ case GRN_TABLE_PAT_KEY:
+ case GRN_TABLE_DAT_KEY:
+ case GRN_TABLE_NO_KEY:
+ return GRN_TRUE;
+ default:
+ return GRN_FALSE;
+ }
+}
+
+static int
+output_table_info(grn_ctx *ctx, grn_obj *table)
+{
+ grn_id id;
+ grn_obj o;
+ const char *path;
+ grn_obj *default_tokenizer;
+ grn_obj *normalizer;
+
+ id = grn_obj_id(ctx, table);
+ path = grn_obj_path(ctx, table);
+ GRN_TEXT_INIT(&o, 0);
+ GRN_OUTPUT_ARRAY_OPEN("TABLE", 8);
+ GRN_OUTPUT_INT64(id);
+ output_object_id_name(ctx, id);
+ GRN_OUTPUT_CSTR(path);
+ grn_table_create_flags_to_text(ctx, &o, table->header.flags);
+ GRN_OUTPUT_OBJ(&o, NULL);
+ output_object_id_name(ctx, table->header.domain);
+ output_object_id_name(ctx, grn_obj_get_range(ctx, table));
+ default_tokenizer = grn_obj_get_info(ctx, table, GRN_INFO_DEFAULT_TOKENIZER,
+ NULL);
+ output_object_name(ctx, default_tokenizer);
+ normalizer = grn_obj_get_info(ctx, table, GRN_INFO_NORMALIZER, NULL);
+ output_object_name(ctx, normalizer);
+ grn_obj_unlink(ctx, normalizer);
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OBJ_FIN(ctx, &o);
+ return 1;
+}
+
+static grn_obj *
+proc_table_list(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_table_cursor *cur;
+ grn_obj tables;
+ int n_top_level_elements;
+ int n_elements_for_header = 1;
+ int n_tables;
+ int i;
+
+ GRN_PTR_INIT(&tables, GRN_OBJ_VECTOR, GRN_ID_NIL);
+
+ if ((cur = grn_table_cursor_open(ctx, ctx->impl->db, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *object;
+ if ((object = grn_ctx_at(ctx, id))) {
+ if (is_table(object)) {
+ GRN_PTR_PUT(ctx, &tables, object);
+ } else {
+ grn_obj_unlink(ctx, object);
+ }
+ } else {
+ if (ctx->rc != GRN_SUCCESS) {
+ ERRCLR(ctx);
+ }
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+
+ n_tables = GRN_BULK_VSIZE(&tables) / sizeof(grn_obj *);
+ n_top_level_elements = n_elements_for_header + n_tables;
+ GRN_OUTPUT_ARRAY_OPEN("TABLE_LIST", n_top_level_elements);
+
+ GRN_OUTPUT_ARRAY_OPEN("HEADER", 8);
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("id");
+ GRN_OUTPUT_CSTR("UInt32");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("name");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("path");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("flags");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("domain");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("range");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("default_tokenizer");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("PROPERTY", 2);
+ GRN_OUTPUT_CSTR("normalizer");
+ GRN_OUTPUT_CSTR("ShortText");
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_CLOSE();
+
+ for (i = 0; i < n_tables; i++) {
+ grn_obj *table = ((grn_obj **)GRN_BULK_HEAD(&tables))[i];
+ output_table_info(ctx, table);
+ grn_obj_unlink(ctx, table);
+ }
+ GRN_OBJ_FIN(ctx, &tables);
+
+ GRN_OUTPUT_ARRAY_CLOSE();
+
+ return NULL;
+}
+
+static grn_obj *
+proc_missing(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ uint32_t plen;
+ grn_obj *outbuf = ctx->impl->outbuf;
+ static int grn_document_root_len = -1;
+ if (!grn_document_root) { return NULL; }
+ if (grn_document_root_len < 0) {
+ size_t l;
+ if ((l = strlen(grn_document_root)) > PATH_MAX) {
+ return NULL;
+ }
+ grn_document_root_len = (int)l;
+ if (l > 0 && grn_document_root[l - 1] == '/') { grn_document_root_len--; }
+ }
+ if ((plen = GRN_TEXT_LEN(VAR(0))) + grn_document_root_len < PATH_MAX) {
+ char path[PATH_MAX];
+ memcpy(path, grn_document_root, grn_document_root_len);
+ path[grn_document_root_len] = '/';
+ grn_str_url_path_normalize(ctx,
+ GRN_TEXT_VALUE(VAR(0)),
+ GRN_TEXT_LEN(VAR(0)),
+ path + grn_document_root_len + 1,
+ PATH_MAX - grn_document_root_len - 1);
+ grn_bulk_put_from_file(ctx, outbuf, path);
+ } else {
+ uint32_t abbrlen = 32;
+ ERR(GRN_INVALID_ARGUMENT,
+ "too long path name: <%s/%.*s...> %u(%u)",
+ grn_document_root,
+ abbrlen < plen ? abbrlen : plen, GRN_TEXT_VALUE(VAR(0)),
+ plen + grn_document_root_len, PATH_MAX);
+ }
+ return NULL;
+}
+
+static grn_obj *
+proc_quit(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ ctx->stat = GRN_CTX_QUITTING;
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static grn_obj *
+proc_shutdown(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_gctx.stat = GRN_CTX_QUIT;
+ ctx->stat = GRN_CTX_QUITTING;
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static grn_obj *
+proc_clearlock(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ int target_name_len;
+ grn_obj *target_name;
+ grn_obj *obj;
+
+ target_name = VAR(0);
+ target_name_len = GRN_TEXT_LEN(target_name);
+
+ if (target_name_len) {
+ obj = grn_ctx_get(ctx, GRN_TEXT_VALUE(target_name), target_name_len);
+ } else {
+ obj = ctx->impl->db;
+ }
+
+ if (obj) {
+ grn_obj_clear_lock(ctx, obj);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "[clearlock] target object not found: <%.*s>",
+ target_name_len, GRN_TEXT_VALUE(target_name));
+ }
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static grn_obj *
+proc_defrag(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *obj;
+ int olen, threshold;
+ olen = GRN_TEXT_LEN(VAR(0));
+
+ if (olen) {
+ obj = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(0)), olen);
+ } else {
+ obj = ctx->impl->db;
+ }
+
+ threshold = GRN_TEXT_LEN(VAR(1))
+ ? grn_atoi(GRN_TEXT_VALUE(VAR(1)), GRN_BULK_CURR(VAR(1)), NULL)
+ : 0;
+
+ if (obj) {
+ grn_obj_defrag(ctx, obj, threshold);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "defrag object not found");
+ }
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static char slev[] = " EACewnid-";
+
+static grn_obj *
+proc_log_level(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ char *p;
+ if (GRN_TEXT_LEN(VAR(0)) &&
+ (p = strchr(slev, GRN_TEXT_VALUE(VAR(0))[0]))) {
+ grn_log_level max_level = (grn_log_level)(p - slev);
+ grn_logger_set_max_level(ctx, max_level);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "invalid log level.");
+ }
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static grn_obj *
+proc_log_put(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ char *p;
+ if (GRN_TEXT_LEN(VAR(0)) &&
+ (p = strchr(slev, GRN_TEXT_VALUE(VAR(0))[0]))) {
+ GRN_TEXT_PUTC(ctx, VAR(1), '\0');
+ GRN_LOG(ctx, (int)(p - slev), "%s", GRN_TEXT_VALUE(VAR(1)));
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "invalid log level.");
+ }
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static grn_obj *
+proc_log_reopen(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_log_reopen(ctx);
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static grn_rc
+proc_delete_validate_selector(grn_ctx *ctx, grn_obj *table, grn_obj *table_name,
+ grn_obj *key, grn_obj *id, grn_obj *filter)
+{
+ grn_rc rc = GRN_SUCCESS;
+
+ if (!table) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[table][record][delete] table doesn't exist: <%.*s>",
+ (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name));
+ return rc;
+ }
+
+ if (GRN_TEXT_LEN(key) == 0 &&
+ GRN_TEXT_LEN(id) == 0 &&
+ GRN_TEXT_LEN(filter) == 0) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[table][record][delete] either key, id or filter must be specified: "
+ "table: <%.*s>",
+ (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name));
+ return rc;
+ }
+
+ if (GRN_TEXT_LEN(key) && GRN_TEXT_LEN(id) && GRN_TEXT_LEN(filter)) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[table][record][delete] "
+ "record selector must be one of key, id and filter: "
+ "table: <%.*s>, key: <%.*s>, id: <%.*s>, filter: <%.*s>",
+ (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name),
+ (int)GRN_TEXT_LEN(key), GRN_TEXT_VALUE(key),
+ (int)GRN_TEXT_LEN(id), GRN_TEXT_VALUE(id),
+ (int)GRN_TEXT_LEN(filter), GRN_TEXT_VALUE(filter));
+ return rc;
+ }
+
+ if (GRN_TEXT_LEN(key) && GRN_TEXT_LEN(id) && GRN_TEXT_LEN(filter) == 0) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[table][record][delete] "
+ "can't use both key and id: table: <%.*s>, key: <%.*s>, id: <%.*s>",
+ (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name),
+ (int)GRN_TEXT_LEN(key), GRN_TEXT_VALUE(key),
+ (int)GRN_TEXT_LEN(id), GRN_TEXT_VALUE(id));
+ return rc;
+ }
+
+ if (GRN_TEXT_LEN(key) && GRN_TEXT_LEN(id) == 0 && GRN_TEXT_LEN(filter)) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[table][record][delete] "
+ "can't use both key and filter: "
+ "table: <%.*s>, key: <%.*s>, filter: <%.*s>",
+ (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name),
+ (int)GRN_TEXT_LEN(key), GRN_TEXT_VALUE(key),
+ (int)GRN_TEXT_LEN(filter), GRN_TEXT_VALUE(filter));
+ return rc;
+ }
+
+ if (GRN_TEXT_LEN(key) == 0 && GRN_TEXT_LEN(id) && GRN_TEXT_LEN(filter)) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[table][record][delete] "
+ "can't use both id and filter: "
+ "table: <%.*s>, id: <%.*s>, filter: <%.*s>",
+ (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name),
+ (int)GRN_TEXT_LEN(id), GRN_TEXT_VALUE(id),
+ (int)GRN_TEXT_LEN(filter), GRN_TEXT_VALUE(filter));
+ return rc;
+ }
+
+ return rc;
+}
+
+static grn_obj *
+proc_delete(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ grn_obj *table_name = VAR(0);
+ grn_obj *key = VAR(1);
+ grn_obj *id = VAR(2);
+ grn_obj *filter = VAR(3);
+ grn_obj *table = NULL;
+
+ if (GRN_TEXT_LEN(table_name) == 0) {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc, "[table][record][delete] table name isn't specified");
+ goto exit;
+ }
+
+ table = grn_ctx_get(ctx,
+ GRN_TEXT_VALUE(table_name),
+ GRN_TEXT_LEN(table_name));
+ rc = proc_delete_validate_selector(ctx, table, table_name, key, id, filter);
+ if (rc != GRN_SUCCESS) { goto exit; }
+
+ if (GRN_TEXT_LEN(key)) {
+ grn_obj casted_key;
+ if (key->header.domain != table->header.domain) {
+ GRN_OBJ_INIT(&casted_key, GRN_BULK, 0, table->header.domain);
+ grn_obj_cast(ctx, key, &casted_key, GRN_FALSE);
+ key = &casted_key;
+ }
+ if (ctx->rc) {
+ rc = ctx->rc;
+ } else {
+ rc = grn_table_delete(ctx, table, GRN_BULK_HEAD(key), GRN_BULK_VSIZE(key));
+ if (key == &casted_key) {
+ GRN_OBJ_FIN(ctx, &casted_key);
+ }
+ }
+ } else if (GRN_TEXT_LEN(id)) {
+ const char *end;
+ grn_id parsed_id = grn_atoui(GRN_TEXT_VALUE(id), GRN_BULK_CURR(id), &end);
+ if (end == GRN_BULK_CURR(id)) {
+ rc = grn_table_delete_by_id(ctx, table, parsed_id);
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ ERR(rc,
+ "[table][record][delete] id should be number: "
+ "table: <%.*s>, id: <%.*s>, detail: <%.*s|%c|%.*s>",
+ (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name),
+ (int)GRN_TEXT_LEN(id), GRN_TEXT_VALUE(id),
+ (int)(end - GRN_TEXT_VALUE(id)), GRN_TEXT_VALUE(id),
+ end[0],
+ (int)(GRN_TEXT_VALUE(id) - end - 1), end + 1);
+ }
+ } else if (GRN_TEXT_LEN(filter)) {
+ grn_obj *cond, *v;
+
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, table, cond, v);
+ grn_expr_parse(ctx, cond,
+ GRN_TEXT_VALUE(filter),
+ GRN_TEXT_LEN(filter),
+ NULL, GRN_OP_MATCH, GRN_OP_AND,
+ GRN_EXPR_SYNTAX_SCRIPT);
+ if (ctx->rc) {
+ char original_error_message[GRN_CTX_MSGSIZE];
+ strcpy(original_error_message, ctx->errbuf);
+ rc = ctx->rc;
+ ERR(rc,
+ "[table][record][delete] failed to parse filter: "
+ "table: <%.*s>, filter: <%.*s>, detail: <%s>",
+ (int)GRN_TEXT_LEN(table_name), GRN_TEXT_VALUE(table_name),
+ (int)GRN_TEXT_LEN(filter), GRN_TEXT_VALUE(filter),
+ original_error_message);
+ } else {
+ grn_obj *records;
+
+ records = grn_table_select(ctx, table, cond, NULL, GRN_OP_OR);
+ if (records) {
+ void *key = NULL;
+ GRN_TABLE_EACH(ctx, records, GRN_ID_NIL, GRN_ID_NIL,
+ result_id, &key, NULL, NULL, {
+ grn_id id = *(grn_id *)key;
+ grn_table_delete_by_id(ctx, table, id);
+ if (ctx->rc == GRN_OPERATION_NOT_PERMITTED) {
+ ERRCLR(ctx);
+ }
+ });
+ grn_obj_unlink(ctx, records);
+ }
+ }
+ grn_obj_unlink(ctx, cond);
+ }
+
+exit :
+ if (table) {
+ grn_obj_unlink(ctx, table);
+ }
+ GRN_OUTPUT_BOOL(!rc);
+ return NULL;
+}
+
+static void
+dump_name(grn_ctx *ctx, grn_obj *outbuf, const char *name, int name_len)
+{
+ grn_obj escaped_name;
+ GRN_TEXT_INIT(&escaped_name, 0);
+ grn_text_esc(ctx, &escaped_name, name, name_len);
+ /* is no character escaped? */
+ /* TODO false positive with spaces inside names */
+ if (GRN_TEXT_LEN(&escaped_name) == name_len + 2) {
+ GRN_TEXT_PUT(ctx, outbuf, name, name_len);
+ } else {
+ GRN_TEXT_PUT(ctx, outbuf,
+ GRN_TEXT_VALUE(&escaped_name), GRN_TEXT_LEN(&escaped_name));
+ }
+ grn_obj_close(ctx, &escaped_name);
+}
+
+static void
+dump_obj_name(grn_ctx *ctx, grn_obj *outbuf, grn_obj *obj)
+{
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+ int name_len;
+ name_len = grn_obj_name(ctx, obj, name, GRN_TABLE_MAX_KEY_SIZE);
+ dump_name(ctx, outbuf, name, name_len);
+}
+
+static void
+dump_column_name(grn_ctx *ctx, grn_obj *outbuf, grn_obj *column)
+{
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+ int name_len;
+ name_len = grn_column_name(ctx, column, name, GRN_TABLE_MAX_KEY_SIZE);
+ dump_name(ctx, outbuf, name, name_len);
+}
+
+static void
+dump_index_column_sources(grn_ctx *ctx, grn_obj *outbuf, grn_obj *column)
+{
+ grn_obj sources;
+ grn_id *source_ids;
+ int i, n;
+
+ GRN_OBJ_INIT(&sources, GRN_BULK, 0, GRN_ID_NIL);
+ grn_obj_get_info(ctx, column, GRN_INFO_SOURCE, &sources);
+
+ n = GRN_BULK_VSIZE(&sources) / sizeof(grn_id);
+ source_ids = (grn_id *)GRN_BULK_HEAD(&sources);
+ if (n > 0) {
+ GRN_TEXT_PUTC(ctx, outbuf, ' ');
+ }
+ for (i = 0; i < n; i++) {
+ grn_obj *source;
+ if ((source = grn_ctx_at(ctx, *source_ids))) {
+ if (i) { GRN_TEXT_PUTC(ctx, outbuf, ','); }
+ switch (source->header.type) {
+ case GRN_TABLE_PAT_KEY:
+ case GRN_TABLE_DAT_KEY:
+ case GRN_TABLE_HASH_KEY:
+ GRN_TEXT_PUT(ctx, outbuf, GRN_COLUMN_NAME_KEY, GRN_COLUMN_NAME_KEY_LEN);
+ break;
+ default:
+ dump_column_name(ctx, outbuf, source);
+ break;
+ }
+ }
+ source_ids++;
+ }
+ grn_obj_close(ctx, &sources);
+}
+
+static void
+dump_column(grn_ctx *ctx, grn_obj *outbuf , grn_obj *table, grn_obj *column)
+{
+ grn_obj *type;
+ grn_obj_flags default_flags = GRN_OBJ_PERSISTENT;
+ grn_obj buf;
+
+ type = grn_ctx_at(ctx, ((grn_db_obj *)column)->range);
+ if (!type) {
+ // ERR(GRN_RANGE_ERROR, "couldn't get column's type object");
+ return;
+ }
+
+ GRN_TEXT_PUTS(ctx, outbuf, "column_create ");
+ dump_obj_name(ctx, outbuf, table);
+ GRN_TEXT_PUTC(ctx, outbuf, ' ');
+ dump_column_name(ctx, outbuf, column);
+ GRN_TEXT_PUTC(ctx, outbuf, ' ');
+ if (type->header.type == GRN_TYPE) {
+ default_flags |= type->header.flags;
+ }
+ GRN_TEXT_INIT(&buf, 0);
+ grn_column_create_flags_to_text(ctx, &buf, column->header.flags & ~default_flags);
+ GRN_TEXT_PUT(ctx, outbuf, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ GRN_OBJ_FIN(ctx, &buf);
+ GRN_TEXT_PUTC(ctx, outbuf, ' ');
+ dump_obj_name(ctx, outbuf, type);
+ if (column->header.flags & GRN_OBJ_COLUMN_INDEX) {
+ dump_index_column_sources(ctx, outbuf, column);
+ }
+ GRN_TEXT_PUTC(ctx, outbuf, '\n');
+
+ grn_obj_unlink(ctx, type);
+}
+
+static int
+reference_column_p(grn_ctx *ctx, grn_obj *column)
+{
+ grn_obj *range;
+
+ range = grn_ctx_at(ctx, grn_obj_get_range(ctx, column));
+ if (!range) {
+ return GRN_FALSE;
+ }
+
+ switch (range->header.type) {
+ case GRN_TABLE_HASH_KEY:
+ case GRN_TABLE_PAT_KEY:
+ case GRN_TABLE_DAT_KEY:
+ case GRN_TABLE_NO_KEY:
+ return GRN_TRUE;
+ default:
+ return GRN_FALSE;
+ }
+}
+
+static void
+dump_columns(grn_ctx *ctx, grn_obj *outbuf, grn_obj *table,
+ grn_obj *pending_columns)
+{
+ grn_hash *columns;
+ columns = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY);
+ if (!columns) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "couldn't create a hash to hold columns");
+ return;
+ }
+
+ if (grn_table_columns(ctx, table, NULL, 0, (grn_obj *)columns) >= 0) {
+ grn_id *key;
+
+ GRN_HASH_EACH(ctx, columns, id, &key, NULL, NULL, {
+ grn_obj *column;
+ if ((column = grn_ctx_at(ctx, *key))) {
+ if (reference_column_p(ctx, column)) {
+ GRN_PTR_PUT(ctx, pending_columns, column);
+ } else {
+ dump_column(ctx, outbuf, table, column);
+ grn_obj_unlink(ctx, column);
+ }
+ }
+ });
+ }
+ grn_hash_close(ctx, columns);
+}
+
+static void
+dump_record_column_vector(grn_ctx *ctx, grn_obj *outbuf, grn_id id,
+ grn_obj *column, grn_id range_id, grn_obj *buf)
+{
+ grn_obj *range;
+
+ range = grn_ctx_at(ctx, range_id);
+ if (GRN_OBJ_TABLEP(range) ||
+ (range->header.flags & GRN_OBJ_KEY_VAR_SIZE) == 0) {
+ GRN_OBJ_INIT(buf, GRN_UVECTOR, 0, range_id);
+ grn_obj_get_value(ctx, column, id, buf);
+ grn_text_otoj(ctx, outbuf, buf, NULL);
+ } else {
+ grn_obj_format *format_argument = NULL;
+ grn_obj_format format;
+ if (column->header.flags & GRN_OBJ_WITH_WEIGHT) {
+ format.flags = GRN_OBJ_FORMAT_WITH_WEIGHT;
+ format_argument = &format;
+ }
+ GRN_OBJ_INIT(buf, GRN_VECTOR, 0, range_id);
+ grn_obj_get_value(ctx, column, id, buf);
+ grn_text_otoj(ctx, outbuf, buf, format_argument);
+ }
+ grn_obj_unlink(ctx, range);
+ grn_obj_unlink(ctx, buf);
+}
+
+static void
+dump_records(grn_ctx *ctx, grn_obj *outbuf, grn_obj *table)
+{
+ grn_obj **columns;
+ grn_id old_id = 0, id;
+ grn_table_cursor *cursor;
+ int i, ncolumns, n_use_columns;
+ grn_obj columnbuf, delete_commands, use_columns, column_name;
+
+ switch (table->header.type) {
+ case GRN_TABLE_HASH_KEY:
+ case GRN_TABLE_PAT_KEY:
+ case GRN_TABLE_DAT_KEY:
+ case GRN_TABLE_NO_KEY:
+ break;
+ default:
+ return;
+ }
+
+ if (grn_table_size(ctx, table) == 0) {
+ return;
+ }
+
+ GRN_TEXT_INIT(&delete_commands, 0);
+
+ GRN_TEXT_PUTS(ctx, outbuf, "load --table ");
+ dump_obj_name(ctx, outbuf, table);
+ GRN_TEXT_PUTS(ctx, outbuf, "\n[\n");
+
+ GRN_PTR_INIT(&columnbuf, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ grn_obj_columns(ctx, table, DUMP_COLUMNS, strlen(DUMP_COLUMNS), &columnbuf);
+ columns = (grn_obj **)GRN_BULK_HEAD(&columnbuf);
+ ncolumns = GRN_BULK_VSIZE(&columnbuf)/sizeof(grn_obj *);
+
+ GRN_PTR_INIT(&use_columns, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ GRN_TEXT_INIT(&column_name, 0);
+ for (i = 0; i < ncolumns; i++) {
+ if (GRN_OBJ_INDEX_COLUMNP(columns[i])) {
+ continue;
+ }
+ GRN_BULK_REWIND(&column_name);
+ grn_column_name_(ctx, columns[i], &column_name);
+ if (((table->header.type == GRN_TABLE_HASH_KEY ||
+ table->header.type == GRN_TABLE_PAT_KEY ||
+ table->header.type == GRN_TABLE_DAT_KEY) &&
+ GRN_TEXT_LEN(&column_name) == GRN_COLUMN_NAME_ID_LEN &&
+ !memcmp(GRN_TEXT_VALUE(&column_name),
+ GRN_COLUMN_NAME_ID,
+ GRN_COLUMN_NAME_ID_LEN)) ||
+ (table->header.type == GRN_TABLE_NO_KEY &&
+ GRN_TEXT_LEN(&column_name) == GRN_COLUMN_NAME_KEY_LEN &&
+ !memcmp(GRN_TEXT_VALUE(&column_name),
+ GRN_COLUMN_NAME_KEY,
+ GRN_COLUMN_NAME_KEY_LEN))) {
+ continue;
+ }
+ GRN_PTR_PUT(ctx, &use_columns, columns[i]);
+ }
+
+ n_use_columns = GRN_BULK_VSIZE(&use_columns) / sizeof(grn_obj *);
+ GRN_TEXT_PUTC(ctx, outbuf, '[');
+ for (i = 0; i < n_use_columns; i++) {
+ grn_obj *column;
+ column = *((grn_obj **)GRN_BULK_HEAD(&use_columns) + i);
+ if (i) { GRN_TEXT_PUTC(ctx, outbuf, ','); }
+ GRN_BULK_REWIND(&column_name);
+ grn_column_name_(ctx, column, &column_name);
+ grn_text_otoj(ctx, outbuf, &column_name, NULL);
+ }
+ GRN_TEXT_PUTS(ctx, outbuf, "],\n");
+
+ cursor = grn_table_cursor_open(ctx, table, NULL, 0, NULL, 0, 0, -1,
+ GRN_CURSOR_BY_KEY);
+ for (i = 0; (id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL;
+ ++i, old_id = id) {
+ int is_value_column;
+ int j;
+ grn_obj buf;
+ if (i) { GRN_TEXT_PUTS(ctx, outbuf, ",\n"); }
+ if (table->header.type == GRN_TABLE_NO_KEY && old_id + 1 < id) {
+ grn_id current_id;
+ for (current_id = old_id + 1; current_id < id; current_id++) {
+ GRN_TEXT_PUTS(ctx, outbuf, "[],\n");
+ GRN_TEXT_PUTS(ctx, &delete_commands, "delete --table ");
+ dump_obj_name(ctx, &delete_commands, table);
+ GRN_TEXT_PUTS(ctx, &delete_commands, " --id ");
+ grn_text_lltoa(ctx, &delete_commands, current_id);
+ GRN_TEXT_PUTC(ctx, &delete_commands, '\n');
+ }
+ }
+ GRN_TEXT_PUTC(ctx, outbuf, '[');
+ for (j = 0; j < n_use_columns; j++) {
+ grn_id range;
+ grn_obj *column;
+ column = *((grn_obj **)GRN_BULK_HEAD(&use_columns) + j);
+ GRN_BULK_REWIND(&column_name);
+ grn_column_name_(ctx, column, &column_name);
+ if (GRN_TEXT_LEN(&column_name) == GRN_COLUMN_NAME_VALUE_LEN &&
+ !memcmp(GRN_TEXT_VALUE(&column_name),
+ GRN_COLUMN_NAME_VALUE,
+ GRN_COLUMN_NAME_VALUE_LEN)) {
+ is_value_column = 1;
+ } else {
+ is_value_column = 0;
+ }
+ range = grn_obj_get_range(ctx, column);
+
+ if (j) { GRN_TEXT_PUTC(ctx, outbuf, ','); }
+ switch (column->header.type) {
+ case GRN_COLUMN_VAR_SIZE:
+ case GRN_COLUMN_FIX_SIZE:
+ switch (column->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) {
+ case GRN_OBJ_COLUMN_VECTOR:
+ dump_record_column_vector(ctx, outbuf, id, column, range, &buf);
+ break;
+ case GRN_OBJ_COLUMN_SCALAR:
+ {
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, range);
+ grn_obj_get_value(ctx, column, id, &buf);
+ grn_text_otoj(ctx, outbuf, &buf, NULL);
+ grn_obj_unlink(ctx, &buf);
+ }
+ break;
+ default:
+ ERR(GRN_OPERATION_NOT_SUPPORTED,
+ "unsupported column type: %#x",
+ column->header.type);
+ break;
+ }
+ break;
+ case GRN_COLUMN_INDEX:
+ break;
+ case GRN_ACCESSOR:
+ {
+ GRN_OBJ_INIT(&buf, GRN_BULK, 0, range);
+ grn_obj_get_value(ctx, column, id, &buf);
+ /* XXX maybe, grn_obj_get_range() should not unconditionally return
+ GRN_DB_INT32 when column is GRN_ACCESSOR and
+ GRN_ACCESSOR_GET_VALUE */
+ if (is_value_column) {
+ buf.header.domain = ((grn_db_obj *)table)->range;
+ }
+ grn_text_otoj(ctx, outbuf, &buf, NULL);
+ grn_obj_unlink(ctx, &buf);
+ }
+ break;
+ default:
+ ERR(GRN_OPERATION_NOT_SUPPORTED,
+ "unsupported header type %#x",
+ column->header.type);
+ break;
+ }
+ }
+ GRN_TEXT_PUTC(ctx, outbuf, ']');
+ }
+ GRN_TEXT_PUTS(ctx, outbuf, "\n]\n");
+ GRN_TEXT_PUT(ctx, outbuf, GRN_TEXT_VALUE(&delete_commands),
+ GRN_TEXT_LEN(&delete_commands));
+ grn_obj_unlink(ctx, &delete_commands);
+ grn_obj_unlink(ctx, &column_name);
+ grn_obj_unlink(ctx, &use_columns);
+
+ grn_table_cursor_close(ctx, cursor);
+ for (i = 0; i < ncolumns; i++) {
+ grn_obj_unlink(ctx, columns[i]);
+ }
+ grn_obj_unlink(ctx, &columnbuf);
+}
+
+static void
+dump_table(grn_ctx *ctx, grn_obj *outbuf, grn_obj *table,
+ grn_obj *pending_columns)
+{
+ grn_obj *domain = NULL, *range = NULL;
+ grn_obj_flags default_flags = GRN_OBJ_PERSISTENT;
+ grn_obj *default_tokenizer;
+ grn_obj *normalizer;
+ grn_obj buf;
+
+ switch (table->header.type) {
+ case GRN_TABLE_HASH_KEY:
+ case GRN_TABLE_PAT_KEY:
+ case GRN_TABLE_DAT_KEY:
+ domain = grn_ctx_at(ctx, table->header.domain);
+ break;
+ default:
+ break;
+ }
+
+ GRN_TEXT_PUTS(ctx, outbuf, "table_create ");
+ dump_obj_name(ctx, outbuf, table);
+ GRN_TEXT_PUTC(ctx, outbuf, ' ');
+ GRN_TEXT_INIT(&buf, 0);
+ grn_table_create_flags_to_text(ctx, &buf, table->header.flags & ~default_flags);
+ GRN_TEXT_PUT(ctx, outbuf, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ GRN_OBJ_FIN(ctx, &buf);
+ if (domain) {
+ GRN_TEXT_PUTC(ctx, outbuf, ' ');
+ dump_obj_name(ctx, outbuf, domain);
+ }
+ if (((grn_db_obj *)table)->range != GRN_ID_NIL) {
+ range = grn_ctx_at(ctx, ((grn_db_obj *)table)->range);
+ if (!range) {
+ // ERR(GRN_RANGE_ERROR, "couldn't get table's value_type object");
+ return;
+ }
+ if (table->header.type != GRN_TABLE_NO_KEY) {
+ GRN_TEXT_PUTC(ctx, outbuf, ' ');
+ } else {
+ GRN_TEXT_PUTS(ctx, outbuf, " --value_type ");
+ }
+ dump_obj_name(ctx, outbuf, range);
+ grn_obj_unlink(ctx, range);
+ }
+ default_tokenizer = grn_obj_get_info(ctx, table, GRN_INFO_DEFAULT_TOKENIZER,
+ NULL);
+ if (default_tokenizer) {
+ GRN_TEXT_PUTS(ctx, outbuf, " --default_tokenizer ");
+ dump_obj_name(ctx, outbuf, default_tokenizer);
+ }
+ normalizer = grn_obj_get_info(ctx, table, GRN_INFO_NORMALIZER, NULL);
+ if (normalizer) {
+ GRN_TEXT_PUTS(ctx, outbuf, " --normalizer ");
+ dump_obj_name(ctx, outbuf, normalizer);
+ }
+
+ GRN_TEXT_PUTC(ctx, outbuf, '\n');
+
+ if (domain) {
+ grn_obj_unlink(ctx, domain);
+ }
+
+ dump_columns(ctx, outbuf, table, pending_columns);
+}
+
+/* can we move this to groonga.h? */
+#define GRN_PTR_POP(obj,value) do {\
+ if (GRN_BULK_VSIZE(obj) >= sizeof(grn_obj *)) {\
+ GRN_BULK_INCR_LEN((obj), -(sizeof(grn_obj *)));\
+ value = *(grn_obj **)(GRN_BULK_CURR(obj));\
+ } else {\
+ value = NULL;\
+ }\
+} while (0)
+
+static void
+dump_schema(grn_ctx *ctx, grn_obj *outbuf)
+{
+ grn_obj *db = ctx->impl->db;
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1,
+ GRN_CURSOR_BY_ID))) {
+ grn_id id;
+ grn_obj pending_columns;
+
+ GRN_PTR_INIT(&pending_columns, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ while ((id = grn_table_cursor_next(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *object;
+
+ if ((object = grn_ctx_at(ctx, id))) {
+ switch (object->header.type) {
+ case GRN_TABLE_HASH_KEY:
+ case GRN_TABLE_PAT_KEY:
+ case GRN_TABLE_DAT_KEY:
+ case GRN_TABLE_NO_KEY:
+ dump_table(ctx, outbuf, object, &pending_columns);
+ break;
+ default:
+ break;
+ }
+ grn_obj_unlink(ctx, object);
+ } else {
+ /* XXX: this clause is executed when MeCab tokenizer is enabled in
+ database but the groonga isn't supported MeCab.
+ We should return error mesage about it and error exit status
+ but it's too difficult for this architecture. :< */
+ ERRCLR(ctx);
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+
+ while (GRN_TRUE) {
+ grn_obj *table, *column;
+ GRN_PTR_POP(&pending_columns, column);
+ if (!column) {
+ break;
+ }
+ table = grn_ctx_at(ctx, column->header.domain);
+ dump_column(ctx, outbuf, table, column);
+ grn_obj_unlink(ctx, column);
+ grn_obj_unlink(ctx, table);
+ }
+ grn_obj_close(ctx, &pending_columns);
+ }
+}
+
+static void
+dump_selected_tables_records(grn_ctx *ctx, grn_obj *outbuf, grn_obj *tables)
+{
+ const char *p, *e;
+
+ p = GRN_TEXT_VALUE(tables);
+ e = p + GRN_TEXT_LEN(tables);
+ while (p < e) {
+ int len;
+ grn_obj *table;
+ const char *token, *token_e;
+
+ if ((len = grn_isspace(p, ctx->encoding))) {
+ p += len;
+ continue;
+ }
+
+ token = p;
+ if (!(('a' <= *p && *p <= 'z') ||
+ ('A' <= *p && *p <= 'Z') ||
+ (*p == '_'))) {
+ while (p < e && !grn_isspace(p, ctx->encoding)) {
+ p++;
+ }
+ GRN_LOG(ctx, GRN_LOG_WARNING, "invalid table name is ignored: <%.*s>\n",
+ (int)(p - token), token);
+ continue;
+ }
+ while (p < e &&
+ (('a' <= *p && *p <= 'z') ||
+ ('A' <= *p && *p <= 'Z') ||
+ ('0' <= *p && *p <= '9') ||
+ (*p == '_'))) {
+ p++;
+ }
+ token_e = p;
+ while (p < e && (len = grn_isspace(p, ctx->encoding))) {
+ p += len;
+ continue;
+ }
+ if (p < e && *p == ',') {
+ p++;
+ }
+
+ if ((table = grn_ctx_get(ctx, token, token_e - token))) {
+ dump_records(ctx, outbuf, table);
+ grn_obj_unlink(ctx, table);
+ } else {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "nonexistent table name is ignored: <%.*s>\n",
+ (int)(token_e - token), token);
+ }
+ }
+}
+
+static void
+dump_all_records(grn_ctx *ctx, grn_obj *outbuf)
+{
+ grn_obj *db = ctx->impl->db;
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1,
+ GRN_CURSOR_BY_ID))) {
+ grn_id id;
+
+ while ((id = grn_table_cursor_next(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *table;
+
+ if ((table = grn_ctx_at(ctx, id))) {
+ dump_records(ctx, outbuf, table);
+ grn_obj_unlink(ctx, table);
+ } else {
+ /* XXX: this clause is executed when MeCab tokenizer is enabled in
+ database but the groonga isn't supported MeCab.
+ We should return error mesage about it and error exit status
+ but it's too difficult for this architecture. :< */
+ ERRCLR(ctx);
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+}
+
+static grn_obj *
+proc_dump(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *outbuf = ctx->impl->outbuf;
+ ctx->impl->output_type = GRN_CONTENT_NONE;
+ ctx->impl->mime_type = "text/x-groonga-command-list";
+ dump_schema(ctx, outbuf);
+ /* To update index columns correctly, we first create the whole schema, then
+ load non-derivative records, while skipping records of index columns. That
+ way, groonga will silently do the job of updating index columns for us. */
+ if (GRN_TEXT_LEN(VAR(0)) > 0) {
+ dump_selected_tables_records(ctx, outbuf, VAR(0));
+ } else {
+ dump_all_records(ctx, outbuf);
+ }
+
+ /* remove the last newline because another one will be added by the calller.
+ maybe, the caller of proc functions currently doesn't consider the
+ possibility of multiple-line output from proc functions. */
+ if (GRN_BULK_VSIZE(outbuf) > 0) {
+ grn_bulk_truncate(ctx, outbuf, GRN_BULK_VSIZE(outbuf) - 1);
+ }
+ return NULL;
+}
+
+static grn_obj *
+proc_cache_limit(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_cache *cache;
+ unsigned int current_max_n_entries;
+
+ cache = grn_cache_current_get(ctx);
+ current_max_n_entries = grn_cache_get_max_n_entries(ctx, cache);
+ if (GRN_TEXT_LEN(VAR(0))) {
+ const char *rest;
+ uint32_t max = grn_atoui(GRN_TEXT_VALUE(VAR(0)),
+ GRN_BULK_CURR(VAR(0)), &rest);
+ if (GRN_BULK_CURR(VAR(0)) == rest) {
+ grn_cache_set_max_n_entries(ctx, cache, max);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT,
+ "max value is invalid unsigned integer format: <%.*s>",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)));
+ }
+ }
+ if (ctx->rc == GRN_SUCCESS) {
+ GRN_OUTPUT_INT64(current_max_n_entries);
+ }
+ return NULL;
+}
+
+static grn_obj *
+proc_register(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ if (GRN_TEXT_LEN(VAR(0))) {
+ const char *name;
+ GRN_TEXT_PUTC(ctx, VAR(0), '\0');
+ name = GRN_TEXT_VALUE(VAR(0));
+ grn_plugin_register(ctx, name);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "path is required");
+ }
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+void grn_ii_buffer_check(grn_ctx *ctx, grn_ii *ii, uint32_t seg);
+
+static grn_obj *
+proc_check(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *obj = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)));
+ if (!obj) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "no such object: <%.*s>", (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)));
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ } else {
+ switch (obj->header.type) {
+ case GRN_DB :
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ break;
+ case GRN_TABLE_PAT_KEY :
+ grn_pat_check(ctx, (grn_pat *)obj);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ grn_hash_check(ctx, (grn_hash *)obj);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ case GRN_COLUMN_FIX_SIZE :
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ grn_ja_check(ctx, (grn_ja *)obj);
+ break;
+ case GRN_COLUMN_INDEX :
+ {
+ grn_ii *ii = (grn_ii *)obj;
+ struct grn_ii_header *h = ii->header;
+ char buf[8];
+ GRN_OUTPUT_ARRAY_OPEN("RESULT", 8);
+ {
+ uint32_t i, j, g =0, a = 0, b = 0;
+ uint32_t max = 0;
+ for (i = h->bgqtail; i != h->bgqhead; i = ((i + 1) & (GRN_II_BGQSIZE - 1))) {
+ j = h->bgqbody[i];
+ g++;
+ if (j > max) { max = j; }
+ }
+ for (i = 0; i < GRN_II_MAX_LSEG; i++) {
+ j = h->binfo[i];
+ if (j < 0x20000) {
+ if (j > max) { max = j; }
+ b++;
+ }
+ }
+ for (i = 0; i < GRN_II_MAX_LSEG; i++) {
+ j = h->ainfo[i];
+ if (j < 0x20000) {
+ if (j > max) { max = j; }
+ a++;
+ }
+ }
+ GRN_OUTPUT_MAP_OPEN("SUMMARY", 12);
+ GRN_OUTPUT_CSTR("flags");
+ grn_itoh(h->flags, buf, 8);
+ GRN_OUTPUT_STR(buf, 8);
+ GRN_OUTPUT_CSTR("max sid");
+ GRN_OUTPUT_INT64(h->smax);
+ GRN_OUTPUT_CSTR("number of garbage segments");
+ GRN_OUTPUT_INT64(g);
+ GRN_OUTPUT_CSTR("number of array segments");
+ GRN_OUTPUT_INT64(a);
+ GRN_OUTPUT_CSTR("max id of array segment");
+ GRN_OUTPUT_INT64(h->amax);
+ GRN_OUTPUT_CSTR("number of buffer segments");
+ GRN_OUTPUT_INT64(b);
+ GRN_OUTPUT_CSTR("max id of buffer segment");
+ GRN_OUTPUT_INT64(h->bmax);
+ GRN_OUTPUT_CSTR("max id of physical segment in use");
+ GRN_OUTPUT_INT64(max);
+ GRN_OUTPUT_CSTR("number of unmanaged segments");
+ GRN_OUTPUT_INT64(h->pnext - a - b - g);
+ GRN_OUTPUT_CSTR("total chunk size");
+ GRN_OUTPUT_INT64(h->total_chunk_size);
+ for (max = 0, i = 0; i < (GRN_II_MAX_CHUNK >> 3); i++) {
+ if ((j = h->chunks[i])) {
+ int k;
+ for (k = 0; k < 8; k++) {
+ if ((j & (1 << k))) { max = (i << 3) + j; }
+ }
+ }
+ }
+ GRN_OUTPUT_CSTR("max id of chunk segments in use");
+ GRN_OUTPUT_INT64(max);
+ GRN_OUTPUT_CSTR("number of garbage chunk");
+ GRN_OUTPUT_ARRAY_OPEN("NGARBAGES", GRN_II_N_CHUNK_VARIATION);
+ for (i = 0; i <= GRN_II_N_CHUNK_VARIATION; i++) {
+ GRN_OUTPUT_INT64(h->ngarbages[i]);
+ }
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_MAP_CLOSE();
+ for (i = 0; i < GRN_II_MAX_LSEG; i++) {
+ if (h->binfo[i] < 0x20000) { grn_ii_buffer_check(ctx, ii, i); }
+ }
+ }
+ GRN_OUTPUT_ARRAY_CLOSE();
+ }
+ break;
+ }
+ }
+ return NULL;
+}
+
+static grn_obj *
+proc_truncate(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ int table_name_len = GRN_TEXT_LEN(VAR(0));
+ if (table_name_len == 0) {
+ ERR(GRN_INVALID_ARGUMENT, "table name is missing");
+ } else {
+ const char *table_name = GRN_TEXT_VALUE(VAR(0));
+ grn_obj *table = grn_ctx_get(ctx, table_name, table_name_len);
+ if (!table) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "no such table: <%.*s>", table_name_len, table_name);
+ } else {
+ switch (table->header.type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ grn_table_truncate(ctx, table);
+ break;
+ default:
+ {
+ grn_obj buffer;
+ GRN_TEXT_INIT(&buffer, 0);
+ grn_inspect(ctx, &buffer, table);
+ ERR(GRN_INVALID_ARGUMENT,
+ "not a table object: %.*s",
+ (int)GRN_TEXT_LEN(&buffer), GRN_TEXT_VALUE(&buffer));
+ GRN_OBJ_FIN(ctx, &buffer);
+ }
+ break;
+ }
+ }
+ }
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static int
+parse_normalize_flags(grn_ctx *ctx, grn_obj *flag_names)
+{
+ int flags = 0;
+ const char *names, *names_end;
+ int length;
+
+ names = GRN_TEXT_VALUE(flag_names);
+ length = GRN_TEXT_LEN(flag_names);
+ names_end = names + length;
+ while (names < names_end) {
+ if (*names == '|' || *names == ' ') {
+ names += 1;
+ continue;
+ }
+
+#define CHECK_FLAG(name)\
+ if (((names_end - names) >= (sizeof(#name) - 1)) &&\
+ (!memcmp(names, #name, sizeof(#name) - 1))) {\
+ flags |= GRN_STRING_ ## name;\
+ names += sizeof(#name);\
+ continue;\
+ }
+
+ CHECK_FLAG(REMOVE_BLANK);
+ CHECK_FLAG(WITH_TYPES);
+ CHECK_FLAG(WITH_CHECKS);
+ CHECK_FLAG(REMOVE_TOKENIZED_DELIMITER);
+
+#define GRN_STRING_NONE 0
+ CHECK_FLAG(NONE);
+#undef GRN_STRING_NONE
+
+ ERR(GRN_INVALID_ARGUMENT, "[normalize] invalid flag: <%.*s>",
+ (int)(names_end - names), names);
+ return 0;
+#undef CHECK_FLAG
+ }
+
+ return flags;
+}
+
+static grn_bool
+is_normalizer(grn_ctx *ctx, grn_obj *object)
+{
+ if (object->header.type != GRN_PROC) {
+ return GRN_FALSE;
+ }
+
+ if (grn_proc_get_type(ctx, object) != GRN_PROC_NORMALIZER) {
+ return GRN_FALSE;
+ }
+
+ return GRN_TRUE;
+}
+
+static grn_bool
+is_tokenizer(grn_ctx *ctx, grn_obj *object)
+{
+ if (object->header.type != GRN_PROC) {
+ return GRN_FALSE;
+ }
+
+ if (grn_proc_get_type(ctx, object) != GRN_PROC_TOKENIZER) {
+ return GRN_FALSE;
+ }
+
+ return GRN_TRUE;
+}
+
+static const char *
+char_type_name(grn_char_type type)
+{
+ const char *name = "unknown";
+
+ switch (type) {
+ case GRN_CHAR_NULL :
+ name = "null";
+ break;
+ case GRN_CHAR_ALPHA :
+ name = "alpha";
+ break;
+ case GRN_CHAR_DIGIT :
+ name = "digit";
+ break;
+ case GRN_CHAR_SYMBOL :
+ name = "symbol";
+ break;
+ case GRN_CHAR_HIRAGANA :
+ name = "hiragana";
+ break;
+ case GRN_CHAR_KATAKANA :
+ name = "katakana";
+ break;
+ case GRN_CHAR_KANJI :
+ name = "kanji";
+ break;
+ case GRN_CHAR_OTHERS :
+ name = "others";
+ break;
+ }
+
+ return name;
+}
+
+static grn_obj *
+proc_normalize(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *normalizer_name;
+ grn_obj *string;
+ grn_obj *flag_names;
+
+ normalizer_name = VAR(0);
+ string = VAR(1);
+ flag_names = VAR(2);
+ if (GRN_TEXT_LEN(normalizer_name) == 0) {
+ ERR(GRN_INVALID_ARGUMENT, "normalizer name is missing");
+ GRN_OUTPUT_CSTR("");
+ return NULL;
+ }
+
+ {
+ grn_obj *normalizer;
+ grn_obj *grn_string;
+ int flags;
+ unsigned int normalized_length_in_bytes;
+ unsigned int normalized_n_characters;
+
+ flags = parse_normalize_flags(ctx, flag_names);
+ normalizer = grn_ctx_get(ctx,
+ GRN_TEXT_VALUE(normalizer_name),
+ GRN_TEXT_LEN(normalizer_name));
+ if (!normalizer) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[normalize] nonexistent normalizer: <%.*s>",
+ (int)GRN_TEXT_LEN(normalizer_name),
+ GRN_TEXT_VALUE(normalizer_name));
+ GRN_OUTPUT_CSTR("");
+ return NULL;
+ }
+
+ if (!is_normalizer(ctx, normalizer)) {
+ grn_obj inspected;
+ GRN_TEXT_INIT(&inspected, 0);
+ grn_inspect(ctx, &inspected, normalizer);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[normalize] not normalizer: %.*s",
+ (int)GRN_TEXT_LEN(&inspected),
+ GRN_TEXT_VALUE(&inspected));
+ GRN_OBJ_FIN(ctx, &inspected);
+ GRN_OUTPUT_CSTR("");
+ grn_obj_unlink(ctx, normalizer);
+ return NULL;
+ }
+
+ grn_string = grn_string_open(ctx,
+ GRN_TEXT_VALUE(string), GRN_TEXT_LEN(string),
+ normalizer, flags);
+ grn_obj_unlink(ctx, normalizer);
+
+ GRN_OUTPUT_MAP_OPEN("RESULT", 3);
+ {
+ const char *normalized;
+
+ grn_string_get_normalized(ctx, grn_string,
+ &normalized,
+ &normalized_length_in_bytes,
+ &normalized_n_characters);
+ GRN_OUTPUT_CSTR("normalized");
+ GRN_OUTPUT_STR(normalized, normalized_length_in_bytes);
+ }
+ {
+ const unsigned char *types;
+
+ types = grn_string_get_types(ctx, grn_string);
+ GRN_OUTPUT_CSTR("types");
+ if (types) {
+ unsigned int i;
+ GRN_OUTPUT_ARRAY_OPEN("types", normalized_n_characters);
+ for (i = 0; i < normalized_n_characters; i++) {
+ GRN_OUTPUT_CSTR(char_type_name(types[i]));
+ }
+ GRN_OUTPUT_ARRAY_CLOSE();
+ } else {
+ GRN_OUTPUT_ARRAY_OPEN("types", 0);
+ GRN_OUTPUT_ARRAY_CLOSE();
+ }
+ }
+ {
+ const short *checks;
+
+ checks = grn_string_get_checks(ctx, grn_string);
+ GRN_OUTPUT_CSTR("checks");
+ if (checks) {
+ unsigned int i;
+ GRN_OUTPUT_ARRAY_OPEN("checks", normalized_length_in_bytes);
+ for (i = 0; i < normalized_length_in_bytes; i++) {
+ GRN_OUTPUT_INT32(checks[i]);
+ }
+ GRN_OUTPUT_ARRAY_CLOSE();
+ } else {
+ GRN_OUTPUT_ARRAY_OPEN("checks", 0);
+ GRN_OUTPUT_ARRAY_CLOSE();
+ }
+ }
+ GRN_OUTPUT_MAP_CLOSE();
+
+ grn_obj_unlink(ctx, grn_string);
+ }
+
+ return NULL;
+}
+
+static unsigned int
+parse_tokenize_flags(grn_ctx *ctx, grn_obj *flag_names)
+{
+ unsigned int flags = 0;
+ const char *names, *names_end;
+ int length;
+
+ names = GRN_TEXT_VALUE(flag_names);
+ length = GRN_TEXT_LEN(flag_names);
+ names_end = names + length;
+ while (names < names_end) {
+ if (*names == '|' || *names == ' ') {
+ names += 1;
+ continue;
+ }
+
+#define CHECK_FLAG(name)\
+ if (((names_end - names) >= (sizeof(#name) - 1)) &&\
+ (!memcmp(names, #name, sizeof(#name) - 1))) {\
+ flags |= GRN_TOKEN_ ## name;\
+ names += sizeof(#name);\
+ continue;\
+ }
+
+ CHECK_FLAG(ENABLE_TOKENIZED_DELIMITER);
+
+#define GRN_TOKEN_NONE 0
+ CHECK_FLAG(NONE);
+#undef GRN_TOKEN_NONE
+
+ ERR(GRN_INVALID_ARGUMENT, "[tokenize] invalid flag: <%.*s>",
+ (int)(names_end - names), names);
+ return 0;
+#undef CHECK_FLAG
+ }
+
+ return flags;
+}
+
+typedef struct {
+ grn_id id;
+ int32_t position;
+} tokenize_token;
+
+static void
+output_tokens(grn_ctx *ctx, grn_obj *tokens, grn_hash *lexicon)
+{
+ int i, n_tokens;
+
+ n_tokens = GRN_BULK_VSIZE(tokens) / sizeof(tokenize_token);
+ GRN_OUTPUT_ARRAY_OPEN("TOKENS", n_tokens);
+ for (i = 0; i < n_tokens; i++) {
+ tokenize_token *token;
+ char value[GRN_TABLE_MAX_KEY_SIZE];
+ unsigned int value_size;
+
+ token = ((tokenize_token *)(GRN_BULK_HEAD(tokens))) + i;
+
+ GRN_OUTPUT_MAP_OPEN("TOKEN", 2);
+
+ GRN_OUTPUT_CSTR("value");
+ value_size = grn_hash_get_key(ctx, lexicon, token->id,
+ value, GRN_TABLE_MAX_KEY_SIZE);
+ GRN_OUTPUT_STR(value, value_size);
+
+ GRN_OUTPUT_CSTR("position");
+ GRN_OUTPUT_INT32(token->position);
+
+ GRN_OUTPUT_MAP_CLOSE();
+ }
+ GRN_OUTPUT_ARRAY_CLOSE();
+}
+
+static grn_hash *
+create_lexicon_for_tokenize(grn_ctx *ctx,
+ grn_obj *tokenizer_name,
+ grn_obj *normalizer_name)
+{
+ grn_hash *lexicon;
+ grn_obj *tokenizer;
+ grn_obj *normalizer = NULL;
+
+ tokenizer = grn_ctx_get(ctx,
+ GRN_TEXT_VALUE(tokenizer_name),
+ GRN_TEXT_LEN(tokenizer_name));
+ if (!tokenizer) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[tokenize] nonexistent tokenizer: <%.*s>",
+ (int)GRN_TEXT_LEN(tokenizer_name),
+ GRN_TEXT_VALUE(tokenizer_name));
+ return NULL;
+ }
+
+ if (!is_tokenizer(ctx, tokenizer)) {
+ grn_obj inspected;
+ GRN_TEXT_INIT(&inspected, 0);
+ grn_inspect(ctx, &inspected, tokenizer);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[tokenize] not tokenizer: %.*s",
+ (int)GRN_TEXT_LEN(&inspected),
+ GRN_TEXT_VALUE(&inspected));
+ GRN_OBJ_FIN(ctx, &inspected);
+ grn_obj_unlink(ctx, tokenizer);
+ return NULL;
+ }
+
+ if (GRN_TEXT_LEN(normalizer_name) > 0) {
+ normalizer = grn_ctx_get(ctx,
+ GRN_TEXT_VALUE(normalizer_name),
+ GRN_TEXT_LEN(normalizer_name));
+ if (!normalizer) {
+ grn_obj_unlink(ctx, tokenizer);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[tokenize] nonexistent normalizer: <%.*s>",
+ (int)GRN_TEXT_LEN(normalizer_name),
+ GRN_TEXT_VALUE(normalizer_name));
+ return NULL;
+ }
+
+ if (!is_normalizer(ctx, normalizer)) {
+ grn_obj inspected;
+ grn_obj_unlink(ctx, tokenizer);
+ GRN_TEXT_INIT(&inspected, 0);
+ grn_inspect(ctx, &inspected, normalizer);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[tokenize] not normalizer: %.*s",
+ (int)GRN_TEXT_LEN(&inspected),
+ GRN_TEXT_VALUE(&inspected));
+ GRN_OBJ_FIN(ctx, &inspected);
+ grn_obj_unlink(ctx, normalizer);
+ return NULL;
+ }
+ }
+
+ lexicon = grn_hash_create(ctx, NULL, GRN_TABLE_MAX_KEY_SIZE, 0,
+ GRN_OBJ_TABLE_HASH_KEY | GRN_OBJ_KEY_VAR_SIZE);
+ grn_obj_set_info(ctx, (grn_obj *)lexicon,
+ GRN_INFO_DEFAULT_TOKENIZER, tokenizer);
+ grn_obj_unlink(ctx, tokenizer);
+ if (normalizer) {
+ grn_obj_set_info(ctx, (grn_obj *)lexicon,
+ GRN_INFO_NORMALIZER, normalizer);
+ grn_obj_unlink(ctx, normalizer);
+ }
+
+ return lexicon;
+}
+
+static void
+tokenize(grn_ctx *ctx, grn_hash *lexicon, grn_obj *string, grn_token_mode mode,
+ unsigned int flags, grn_obj *tokens)
+{
+ grn_token *token;
+
+ token = grn_token_open(ctx, (grn_obj *)lexicon,
+ GRN_TEXT_VALUE(string), GRN_TEXT_LEN(string),
+ mode, flags);
+ if (!token) {
+ return;
+ }
+
+ while (token->status == GRN_TOKEN_DOING) {
+ grn_id token_id = grn_token_next(ctx, token);
+ tokenize_token *current_token;
+ if (token_id == GRN_ID_NIL) {
+ continue;
+ }
+ grn_bulk_space(ctx, tokens, sizeof(tokenize_token));
+ current_token = ((tokenize_token *)(GRN_BULK_CURR(tokens))) - 1;
+ current_token->id = token_id;
+ current_token->position = token->pos;
+ }
+ grn_token_close(ctx, token);
+}
+
+static grn_obj *
+proc_tokenize(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *tokenizer_name;
+ grn_obj *string;
+ grn_obj *normalizer_name;
+ grn_obj *flag_names;
+ grn_obj *mode_name;
+
+ tokenizer_name = VAR(0);
+ string = VAR(1);
+ normalizer_name = VAR(2);
+ flag_names = VAR(3);
+ mode_name = VAR(4);
+
+ if (GRN_TEXT_LEN(tokenizer_name) == 0) {
+ ERR(GRN_INVALID_ARGUMENT, "[tokenize] tokenizer name is missing");
+ return NULL;
+ }
+
+ if (GRN_TEXT_LEN(string) == 0) {
+ ERR(GRN_INVALID_ARGUMENT, "[tokenize] string is missing");
+ return NULL;
+ }
+
+ {
+ unsigned int flags;
+ grn_hash *lexicon;
+
+ flags = parse_tokenize_flags(ctx, flag_names);
+ if (ctx->rc != GRN_SUCCESS) {
+ return NULL;
+ }
+
+ lexicon = create_lexicon_for_tokenize(ctx, tokenizer_name, normalizer_name);
+ if (!lexicon) {
+ return NULL;
+ }
+
+#define MODE_NAME_EQUAL(name)\
+ (GRN_TEXT_LEN(mode_name) == strlen(name) &&\
+ memcmp(GRN_TEXT_VALUE(mode_name), name, strlen(name)) == 0)
+
+ if (GRN_TEXT_LEN(mode_name) == 0 || MODE_NAME_EQUAL("ADD")) {
+ grn_obj tokens;
+ GRN_VALUE_FIX_SIZE_INIT(&tokens, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ tokenize(ctx, lexicon, string, GRN_TOKEN_ADD, flags, &tokens);
+ output_tokens(ctx, &tokens, lexicon);
+ GRN_OBJ_FIN(ctx, &tokens);
+ } else if (MODE_NAME_EQUAL("GET")) {
+ {
+ grn_token *token;
+ token = grn_token_open(ctx, (grn_obj *)lexicon,
+ GRN_TEXT_VALUE(string), GRN_TEXT_LEN(string),
+ GRN_TOKEN_ADD, flags);
+ if (token) {
+ while (token->status == GRN_TOKEN_DOING) {
+ grn_token_next(ctx, token);
+ }
+ grn_token_close(ctx, token);
+ }
+ }
+
+ {
+ grn_obj tokens;
+ GRN_VALUE_FIX_SIZE_INIT(&tokens, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ tokenize(ctx, lexicon, string, GRN_TOKEN_GET, flags, &tokens);
+ output_tokens(ctx, &tokens, lexicon);
+ GRN_OBJ_FIN(ctx, &tokens);
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "[tokenize] invalid mode: <%.*s>",
+ (int)GRN_TEXT_LEN(mode_name), GRN_TEXT_VALUE(mode_name));
+ }
+#undef MODE_NAME_EQUAL
+
+ grn_hash_close(ctx, lexicon);
+ }
+
+ return NULL;
+}
+
+static void
+list_proc(grn_ctx *ctx, grn_proc_type target_proc_type,
+ const char *name, const char *plural_name)
+{
+ grn_obj *db;
+ grn_table_cursor *cursor;
+ grn_obj target_procs;
+
+ db = grn_ctx_db(ctx);
+ cursor = grn_table_cursor_open(ctx, db, NULL, 0, NULL, 0, 0, -1,
+ GRN_CURSOR_BY_ID);
+ if (!cursor) {
+ return;
+ }
+
+ GRN_PTR_INIT(&target_procs, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ {
+ grn_id id;
+
+ while ((id = grn_table_cursor_next(ctx, cursor)) != GRN_ID_NIL) {
+ grn_obj *obj;
+ grn_proc_type proc_type;
+
+ obj = grn_ctx_at(ctx, id);
+ if (!obj) {
+ continue;
+ }
+
+ if (obj->header.type != GRN_PROC) {
+ grn_obj_unlink(ctx, obj);
+ continue;
+ }
+
+ proc_type = grn_proc_get_type(ctx, obj);
+ if (proc_type != target_proc_type) {
+ grn_obj_unlink(ctx, obj);
+ continue;
+ }
+
+ GRN_PTR_PUT(ctx, &target_procs, obj);
+ }
+ grn_table_cursor_close(ctx, cursor);
+
+ {
+ int i, n_procs;
+
+ n_procs = GRN_BULK_VSIZE(&target_procs) / sizeof(grn_obj *);
+ GRN_OUTPUT_ARRAY_OPEN(plural_name, n_procs);
+ for (i = 0; i < n_procs; i++) {
+ grn_obj *proc;
+ char name[GRN_TABLE_MAX_KEY_SIZE];
+ int name_size;
+
+ proc = GRN_PTR_VALUE_AT(&target_procs, i);
+ name_size = grn_obj_name(ctx, proc, name, GRN_TABLE_MAX_KEY_SIZE);
+ GRN_OUTPUT_MAP_OPEN(name, 1);
+ GRN_OUTPUT_CSTR("name");
+ GRN_OUTPUT_STR(name, name_size);
+ GRN_OUTPUT_MAP_CLOSE();
+
+ grn_obj_unlink(ctx, proc);
+ }
+ GRN_OUTPUT_ARRAY_CLOSE();
+ }
+
+ grn_obj_unlink(ctx, &target_procs);
+ }
+}
+
+static grn_obj *
+proc_tokenizer_list(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ list_proc(ctx, GRN_PROC_TOKENIZER, "tokenizer", "tokenizers");
+ return NULL;
+}
+
+static grn_obj *
+proc_normalizer_list(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ list_proc(ctx, GRN_PROC_NORMALIZER, "normalizer", "normalizers");
+ return NULL;
+}
+
+static grn_obj *
+func_rand(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ int val;
+ grn_obj *obj;
+ if (nargs > 0) {
+ int max = GRN_INT32_VALUE(args[0]);
+ val = (int) (1.0 * max * rand() / (RAND_MAX + 1.0));
+ } else {
+ val = rand();
+ }
+ if ((obj = GRN_PROC_ALLOC(GRN_DB_INT32, 0))) {
+ GRN_INT32_SET(ctx, obj, val);
+ }
+ return obj;
+}
+
+static grn_obj *
+func_now(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *obj;
+ if ((obj = GRN_PROC_ALLOC(GRN_DB_TIME, 0))) {
+ GRN_TIME_NOW(ctx, obj);
+ }
+ return obj;
+}
+
+static inline grn_bool
+is_comparable_number_type(grn_id type)
+{
+ return GRN_DB_INT8 <= type && type <= GRN_DB_TIME;
+}
+
+static inline grn_id
+larger_number_type(grn_id type1, grn_id type2)
+{
+ if (type1 == type2) {
+ return type1;
+ }
+
+ switch (type1) {
+ case GRN_DB_FLOAT :
+ return type1;
+ case GRN_DB_TIME :
+ if (type2 == GRN_DB_FLOAT) {
+ return type2;
+ } else {
+ return type1;
+ }
+ default :
+ if (type2 > type1) {
+ return type2;
+ } else {
+ return type1;
+ }
+ }
+}
+
+static inline grn_id
+smaller_number_type(grn_id type1, grn_id type2)
+{
+ if (type1 == type2) {
+ return type1;
+ }
+
+ switch (type1) {
+ case GRN_DB_FLOAT :
+ return type1;
+ case GRN_DB_TIME :
+ if (type2 == GRN_DB_FLOAT) {
+ return type2;
+ } else {
+ return type1;
+ }
+ default :
+ {
+ grn_id smaller_number_type;
+ if (type2 > type1) {
+ smaller_number_type = type2;
+ } else {
+ smaller_number_type = type1;
+ }
+ switch (smaller_number_type) {
+ case GRN_DB_UINT8 :
+ return GRN_DB_INT8;
+ case GRN_DB_UINT16 :
+ return GRN_DB_INT16;
+ case GRN_DB_UINT32 :
+ return GRN_DB_INT32;
+ case GRN_DB_UINT64 :
+ return GRN_DB_INT64;
+ default :
+ return smaller_number_type;
+ }
+ }
+ }
+}
+
+static inline grn_bool
+is_negative_value(grn_obj *number)
+{
+ switch (number->header.domain) {
+ case GRN_DB_INT8 :
+ return GRN_INT8_VALUE(number) < 0;
+ case GRN_DB_INT16 :
+ return GRN_INT16_VALUE(number) < 0;
+ case GRN_DB_INT32 :
+ return GRN_INT32_VALUE(number) < 0;
+ case GRN_DB_INT64 :
+ return GRN_INT64_VALUE(number) < 0;
+ case GRN_DB_TIME :
+ return GRN_TIME_VALUE(number) < 0;
+ case GRN_DB_FLOAT :
+ return GRN_FLOAT_VALUE(number) < 0;
+ default :
+ return GRN_FALSE;
+ }
+}
+
+static inline grn_bool
+number_safe_cast(grn_ctx *ctx, grn_obj *src, grn_obj *dest, grn_id type)
+{
+ grn_obj_reinit(ctx, dest, type, 0);
+ if (src->header.domain == type) {
+ GRN_TEXT_SET(ctx, dest, GRN_TEXT_VALUE(src), GRN_TEXT_LEN(src));
+ return GRN_TRUE;
+ }
+
+ switch (type) {
+ case GRN_DB_UINT8 :
+ if (is_negative_value(src)) {
+ GRN_UINT8_SET(ctx, dest, 0);
+ return GRN_TRUE;
+ }
+ case GRN_DB_UINT16 :
+ if (is_negative_value(src)) {
+ GRN_UINT16_SET(ctx, dest, 0);
+ return GRN_TRUE;
+ }
+ case GRN_DB_UINT32 :
+ if (is_negative_value(src)) {
+ GRN_UINT32_SET(ctx, dest, 0);
+ return GRN_TRUE;
+ }
+ case GRN_DB_UINT64 :
+ if (is_negative_value(src)) {
+ GRN_UINT64_SET(ctx, dest, 0);
+ return GRN_TRUE;
+ }
+ default :
+ return grn_obj_cast(ctx, src, dest, GRN_FALSE) == GRN_SUCCESS;
+ }
+}
+
+static inline int
+compare_number(grn_ctx *ctx, grn_obj *number1, grn_obj *number2, grn_id type)
+{
+#define COMPARE_AND_RETURN(type, value1, value2)\
+ {\
+ type computed_value1 = value1;\
+ type computed_value2 = value2;\
+ if (computed_value1 > computed_value2) {\
+ return 1;\
+ } else if (computed_value1 < computed_value2) {\
+ return -1;\
+ } else {\
+ return 0;\
+ }\
+ }
+
+ switch (type) {
+ case GRN_DB_INT8 :
+ COMPARE_AND_RETURN(int8_t,
+ GRN_INT8_VALUE(number1),
+ GRN_INT8_VALUE(number2));
+ case GRN_DB_UINT8 :
+ COMPARE_AND_RETURN(uint8_t,
+ GRN_UINT8_VALUE(number1),
+ GRN_UINT8_VALUE(number2));
+ case GRN_DB_INT16 :
+ COMPARE_AND_RETURN(int16_t,
+ GRN_INT16_VALUE(number1),
+ GRN_INT16_VALUE(number2));
+ case GRN_DB_UINT16 :
+ COMPARE_AND_RETURN(uint16_t,
+ GRN_UINT16_VALUE(number1),
+ GRN_UINT16_VALUE(number2));
+ case GRN_DB_INT32 :
+ COMPARE_AND_RETURN(int32_t,
+ GRN_INT32_VALUE(number1),
+ GRN_INT32_VALUE(number2));
+ case GRN_DB_UINT32 :
+ COMPARE_AND_RETURN(uint32_t,
+ GRN_UINT32_VALUE(number1),
+ GRN_UINT32_VALUE(number2));
+ case GRN_DB_INT64 :
+ COMPARE_AND_RETURN(int64_t,
+ GRN_INT64_VALUE(number1),
+ GRN_INT64_VALUE(number2));
+ case GRN_DB_UINT64 :
+ COMPARE_AND_RETURN(uint64_t,
+ GRN_UINT64_VALUE(number1),
+ GRN_UINT64_VALUE(number2));
+ case GRN_DB_FLOAT :
+ COMPARE_AND_RETURN(double,
+ GRN_FLOAT_VALUE(number1),
+ GRN_FLOAT_VALUE(number2));
+ case GRN_DB_TIME :
+ COMPARE_AND_RETURN(int64_t,
+ GRN_TIME_VALUE(number1),
+ GRN_TIME_VALUE(number2));
+ default :
+ return 0;
+ }
+
+#undef COMPARE_AND_RETURN
+}
+
+static grn_obj *
+func_max(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *max;
+ grn_id cast_type = GRN_DB_INT8;
+ grn_obj casted_max, casted_number;
+ int i;
+
+ max = GRN_PROC_ALLOC(GRN_DB_VOID, 0);
+ if (!max) {
+ return max;
+ }
+
+ GRN_VOID_INIT(&casted_max);
+ GRN_VOID_INIT(&casted_number);
+ for (i = 0; i < nargs; i++) {
+ grn_obj *number = args[i];
+ grn_id domain = number->header.domain;
+ if (!is_comparable_number_type(domain)) {
+ continue;
+ }
+ cast_type = larger_number_type(cast_type, domain);
+ if (!number_safe_cast(ctx, number, &casted_number, cast_type)) {
+ continue;
+ }
+ if (max->header.domain == GRN_DB_VOID) {
+ grn_obj_reinit(ctx, max, cast_type, 0);
+ GRN_TEXT_SET(ctx, max,
+ GRN_TEXT_VALUE(&casted_number),
+ GRN_TEXT_LEN(&casted_number));
+ continue;
+ }
+
+ if (max->header.domain != cast_type) {
+ if (!number_safe_cast(ctx, max, &casted_max, cast_type)) {
+ continue;
+ }
+ grn_obj_reinit(ctx, max, cast_type, 0);
+ GRN_TEXT_SET(ctx, max,
+ GRN_TEXT_VALUE(&casted_max),
+ GRN_TEXT_LEN(&casted_max));
+ }
+ if (compare_number(ctx, &casted_number, max, cast_type) > 0) {
+ grn_obj_reinit(ctx, max, cast_type, 0);
+ GRN_TEXT_SET(ctx, max,
+ GRN_TEXT_VALUE(&casted_number),
+ GRN_TEXT_LEN(&casted_number));
+ }
+ }
+ GRN_OBJ_FIN(ctx, &casted_max);
+ GRN_OBJ_FIN(ctx, &casted_number);
+
+ return max;
+}
+
+static grn_obj *
+func_min(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *min;
+ grn_id cast_type = GRN_DB_INT8;
+ grn_obj casted_min, casted_number;
+ int i;
+
+ min = GRN_PROC_ALLOC(GRN_DB_VOID, 0);
+ if (!min) {
+ return min;
+ }
+
+ GRN_VOID_INIT(&casted_min);
+ GRN_VOID_INIT(&casted_number);
+ for (i = 0; i < nargs; i++) {
+ grn_obj *number = args[i];
+ grn_id domain = number->header.domain;
+ if (!is_comparable_number_type(domain)) {
+ continue;
+ }
+ cast_type = smaller_number_type(cast_type, domain);
+ if (!number_safe_cast(ctx, number, &casted_number, cast_type)) {
+ continue;
+ }
+ if (min->header.domain == GRN_DB_VOID) {
+ grn_obj_reinit(ctx, min, cast_type, 0);
+ GRN_TEXT_SET(ctx, min,
+ GRN_TEXT_VALUE(&casted_number),
+ GRN_TEXT_LEN(&casted_number));
+ continue;
+ }
+
+ if (min->header.domain != cast_type) {
+ if (!number_safe_cast(ctx, min, &casted_min, cast_type)) {
+ continue;
+ }
+ grn_obj_reinit(ctx, min, cast_type, 0);
+ GRN_TEXT_SET(ctx, min,
+ GRN_TEXT_VALUE(&casted_min),
+ GRN_TEXT_LEN(&casted_min));
+ }
+ if (compare_number(ctx, &casted_number, min, cast_type) < 0) {
+ grn_obj_reinit(ctx, min, cast_type, 0);
+ GRN_TEXT_SET(ctx, min,
+ GRN_TEXT_VALUE(&casted_number),
+ GRN_TEXT_LEN(&casted_number));
+ }
+ }
+ GRN_OBJ_FIN(ctx, &casted_min);
+ GRN_OBJ_FIN(ctx, &casted_number);
+
+ return min;
+}
+
+static grn_obj *
+func_geo_in_circle(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *obj;
+ unsigned char r = GRN_FALSE;
+ grn_geo_approximate_type type = GRN_GEO_APPROXIMATE_RECTANGLE;
+ switch (nargs) {
+ case 4 :
+ if (grn_geo_resolve_approximate_type(ctx, args[3], &type) != GRN_SUCCESS) {
+ break;
+ }
+ /* fallthru */
+ case 3 :
+ r = grn_geo_in_circle(ctx, args[0], args[1], args[2], type);
+ break;
+ default :
+ break;
+ }
+ if ((obj = GRN_PROC_ALLOC(GRN_DB_UINT32, 0))) {
+ GRN_UINT32_SET(ctx, obj, r);
+ }
+ return obj;
+}
+
+static grn_obj *
+func_geo_in_rectangle(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *obj;
+ unsigned char r = GRN_FALSE;
+ if (nargs == 3) {
+ r = grn_geo_in_rectangle(ctx, args[0], args[1], args[2]);
+ }
+ if ((obj = GRN_PROC_ALLOC(GRN_DB_UINT32, 0))) {
+ GRN_UINT32_SET(ctx, obj, r);
+ }
+ return obj;
+}
+
+static grn_obj *
+func_geo_distance(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *obj;
+ double d = 0.0;
+ grn_geo_approximate_type type = GRN_GEO_APPROXIMATE_RECTANGLE;
+ switch (nargs) {
+ case 3 :
+ if (grn_geo_resolve_approximate_type(ctx, args[2], &type) != GRN_SUCCESS) {
+ break;
+ }
+ /* fallthru */
+ case 2 :
+ d = grn_geo_distance(ctx, args[0], args[1], type);
+ break;
+ default:
+ break;
+ }
+ if ((obj = GRN_PROC_ALLOC(GRN_DB_FLOAT, 0))) {
+ GRN_FLOAT_SET(ctx, obj, d);
+ }
+ return obj;
+}
+
+/* deprecated. */
+static grn_obj *
+func_geo_distance2(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *obj;
+ double d = 0;
+ if (nargs == 2) {
+ d = grn_geo_distance_sphere(ctx, args[0], args[1]);
+ }
+ if ((obj = GRN_PROC_ALLOC(GRN_DB_FLOAT, 0))) {
+ GRN_FLOAT_SET(ctx, obj, d);
+ }
+ return obj;
+}
+
+/* deprecated. */
+static grn_obj *
+func_geo_distance3(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *obj;
+ double d = 0;
+ if (nargs == 2) {
+ d = grn_geo_distance_ellipsoid(ctx, args[0], args[1]);
+ }
+ if ((obj = GRN_PROC_ALLOC(GRN_DB_FLOAT, 0))) {
+ GRN_FLOAT_SET(ctx, obj, d);
+ }
+ return obj;
+}
+
+#define DIST(ox,oy) (dists[((lx + 1) * (oy)) + (ox)])
+
+static grn_obj *
+func_edit_distance(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ int d = 0;
+ grn_obj *obj;
+ if (nargs == 2) {
+ uint32_t cx, lx, cy, ly, *dists;
+ char *px, *sx = GRN_TEXT_VALUE(args[0]), *ex = GRN_BULK_CURR(args[0]);
+ char *py, *sy = GRN_TEXT_VALUE(args[1]), *ey = GRN_BULK_CURR(args[1]);
+ for (px = sx, lx = 0; px < ex && (cx = grn_charlen(ctx, px, ex)); px += cx, lx++);
+ for (py = sy, ly = 0; py < ey && (cy = grn_charlen(ctx, py, ey)); py += cy, ly++);
+ if ((dists = GRN_MALLOC((lx + 1) * (ly + 1) * sizeof(uint32_t)))) {
+ uint32_t x, y;
+ for (x = 0; x <= lx; x++) { DIST(x, 0) = x; }
+ for (y = 0; y <= ly; y++) { DIST(0, y) = y; }
+ for (x = 1, px = sx; x <= lx; x++, px += cx) {
+ cx = grn_charlen(ctx, px, ex);
+ for (y = 1, py = sy; y <= ly; y++, py += cy) {
+ cy = grn_charlen(ctx, py, ey);
+ if (cx == cy && !memcmp(px, py, cx)) {
+ DIST(x, y) = DIST(x - 1, y - 1);
+ } else {
+ uint32_t a = DIST(x - 1, y) + 1;
+ uint32_t b = DIST(x, y - 1) + 1;
+ uint32_t c = DIST(x - 1, y - 1) + 1;
+ DIST(x, y) = ((a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c));
+ }
+ }
+ }
+ d = DIST(lx, ly);
+ GRN_FREE(dists);
+ }
+ }
+ if ((obj = GRN_PROC_ALLOC(GRN_DB_UINT32, 0))) {
+ GRN_UINT32_SET(ctx, obj, d);
+ }
+ return obj;
+}
+
+static grn_obj *
+func_all_records(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data)
+{
+ grn_obj *true_value;
+ if ((true_value = GRN_PROC_ALLOC(GRN_DB_BOOL, 0))) {
+ GRN_BOOL_SET(ctx, true_value, GRN_TRUE);
+ }
+ return true_value;
+}
+
+static grn_rc
+selector_all_records(grn_ctx *ctx, grn_obj *table, grn_obj *index,
+ int nargs, grn_obj **args,
+ grn_obj *res, grn_operator op)
+{
+ grn_obj score;
+
+ GRN_UINT32_INIT(&score, 0);
+ GRN_UINT32_SET(ctx, &score, 1);
+
+ GRN_TABLE_EACH(ctx, table, 0, 0, id, NULL, NULL, NULL, {
+ grn_id result_id;
+ result_id = grn_table_add(ctx, res, &id, sizeof(grn_id), NULL);
+ grn_obj_set_value(ctx, res, result_id, &score, GRN_OBJ_SET);
+ });
+
+ GRN_OBJ_FIN(ctx, &score);
+
+ return ctx->rc;
+}
+
+static grn_obj *
+snippet_exec(grn_ctx *ctx, grn_obj *snip, grn_obj *text,
+ grn_user_data *user_data)
+{
+ grn_rc rc;
+ unsigned int i, n_results, max_tagged_length;
+ grn_obj snippet_buffer;
+ grn_obj *snippets;
+
+ if (GRN_TEXT_LEN(text) == 0) {
+ return NULL;
+ }
+
+ rc = grn_snip_exec(ctx, snip,
+ GRN_TEXT_VALUE(text), GRN_TEXT_LEN(text),
+ &n_results, &max_tagged_length);
+ if (rc != GRN_SUCCESS) {
+ return NULL;
+ }
+
+ if (n_results == 0) {
+ return GRN_PROC_ALLOC(GRN_DB_VOID, 0);
+ }
+
+ snippets = GRN_PROC_ALLOC(GRN_DB_SHORT_TEXT, GRN_OBJ_VECTOR);
+ if (!snippets) {
+ return NULL;
+ }
+
+ GRN_TEXT_INIT(&snippet_buffer, 0);
+ grn_bulk_space(ctx, &snippet_buffer, max_tagged_length);
+ for (i = 0; i < n_results; i++) {
+ unsigned int snippet_length;
+
+ GRN_BULK_REWIND(&snippet_buffer);
+ rc = grn_snip_get_result(ctx, snip, i,
+ GRN_TEXT_VALUE(&snippet_buffer),
+ &snippet_length);
+ if (rc == GRN_SUCCESS) {
+ grn_vector_add_element(ctx, snippets,
+ GRN_TEXT_VALUE(&snippet_buffer), snippet_length,
+ 0, GRN_DB_SHORT_TEXT);
+ }
+ }
+ GRN_OBJ_FIN(ctx, &snippet_buffer);
+
+ return snippets;
+}
+
+static grn_obj *
+func_snippet_html(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data)
+{
+ grn_obj *snippets = NULL;
+
+ /* TODO: support parameters */
+ if (nargs == 1) {
+ grn_obj *text = args[0];
+ grn_obj *expression = NULL;
+ grn_obj *condition_ptr = NULL;
+ grn_obj *condition = NULL;
+ grn_obj *snip = NULL;
+ int flags = GRN_SNIP_SKIP_LEADING_SPACES;
+ unsigned int width = 200;
+ unsigned int max_n_results = 3;
+ const char *open_tag = "<span class=\"keyword\">";
+ const char *close_tag = "</span>";
+ grn_snip_mapping *mapping = GRN_SNIP_MAPPING_HTML_ESCAPE;
+
+ grn_proc_get_info(ctx, user_data, NULL, NULL, &expression);
+ condition_ptr = grn_expr_get_var(ctx, expression,
+ GRN_SELECT_INTERNAL_VAR_CONDITION,
+ strlen(GRN_SELECT_INTERNAL_VAR_CONDITION));
+ if (condition_ptr) {
+ condition = GRN_PTR_VALUE(condition_ptr);
+ }
+
+ if (condition) {
+ snip = grn_snip_open(ctx, flags, width, max_n_results,
+ open_tag, strlen(open_tag),
+ close_tag, strlen(close_tag),
+ mapping);
+ if (snip) {
+ grn_snip_set_normalizer(ctx, snip, GRN_NORMALIZER_AUTO);
+ grn_expr_snip_add_conditions(ctx, condition, snip,
+ 0, NULL, NULL, NULL, NULL);
+ }
+ }
+
+ if (snip) {
+ snippets = snippet_exec(ctx, snip, text, user_data);
+ grn_obj_close(ctx, snip);
+ }
+ }
+
+ if (!snippets) {
+ snippets = GRN_PROC_ALLOC(GRN_DB_VOID, 0);
+ }
+
+ return snippets;
+}
+
+typedef struct {
+ grn_obj *found;
+ grn_obj *table;
+ grn_obj *records;
+} selector_to_function_data;
+
+static grn_bool
+selector_to_function_data_init(grn_ctx *ctx,
+ selector_to_function_data *data,
+ grn_user_data *user_data)
+{
+ grn_obj *condition = NULL;
+ grn_obj *variable;
+
+ data->table = NULL;
+ data->records = NULL;
+
+ data->found = GRN_PROC_ALLOC(GRN_DB_BOOL, 0);
+ if (!data->found) {
+ return GRN_FALSE;
+ }
+ GRN_BOOL_SET(ctx, data->found, GRN_FALSE);
+
+ grn_proc_get_info(ctx, user_data, NULL, NULL, &condition);
+ if (!condition) {
+ return GRN_FALSE;
+ }
+
+ variable = grn_expr_get_var_by_offset(ctx, condition, 0);
+ if (!variable) {
+ return GRN_FALSE;
+ }
+
+ data->table = grn_ctx_at(ctx, variable->header.domain);
+ if (!data->table) {
+ return GRN_FALSE;
+ }
+
+ data->records = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC,
+ data->table, NULL);
+ if (!data->records) {
+ return GRN_FALSE;
+ }
+
+ {
+ grn_rset_posinfo pi;
+ unsigned int key_size;
+ memset(&pi, 0, sizeof(grn_rset_posinfo));
+ pi.rid = GRN_RECORD_VALUE(variable);
+ key_size = ((grn_hash *)(data->records))->key_size;
+ if (grn_table_add(ctx, data->records, &pi, key_size, NULL) == GRN_ID_NIL) {
+ return GRN_FALSE;
+ }
+ }
+
+ return GRN_TRUE;
+}
+
+static void
+selector_to_function_data_selected(grn_ctx *ctx,
+ selector_to_function_data *data)
+{
+ GRN_BOOL_SET(ctx, data->found, grn_table_size(ctx, data->records) > 0);
+}
+
+static void
+selector_to_function_data_fin(grn_ctx *ctx,
+ selector_to_function_data *data)
+{
+ if (data->records) {
+ grn_obj_unlink(ctx, data->records);
+ }
+
+ if (data->table) {
+ grn_obj_unlink(ctx, data->table);
+ }
+}
+
+static grn_rc
+run_query(grn_ctx *ctx, grn_obj *table,
+ int nargs, grn_obj **args,
+ grn_obj *res, grn_operator op)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_obj *match_columns_string;
+ grn_obj *query;
+ grn_obj *query_expander_name = NULL;
+ grn_obj *match_columns = NULL;
+ grn_obj *condition = NULL;
+ grn_obj *dummy_variable;
+
+ /* TODO: support flags by parameters */
+ if (!(2 <= nargs && nargs <= 3)) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "wrong number of arguments (%d for 2..3)", nargs);
+ rc = ctx->rc;
+ goto exit;
+ }
+
+ match_columns_string = args[0];
+ query = args[1];
+ if (nargs > 2) {
+ query_expander_name = args[2];
+ }
+
+ if (match_columns_string->header.domain == GRN_DB_TEXT &&
+ GRN_TEXT_LEN(match_columns_string) > 0) {
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, table, match_columns, dummy_variable);
+ if (!match_columns) {
+ rc = ctx->rc;
+ goto exit;
+ }
+
+ grn_expr_parse(ctx, match_columns,
+ GRN_TEXT_VALUE(match_columns_string),
+ GRN_TEXT_LEN(match_columns_string),
+ NULL, GRN_OP_MATCH, GRN_OP_AND,
+ GRN_EXPR_SYNTAX_SCRIPT);
+ if (ctx->rc != GRN_SUCCESS) {
+ rc = ctx->rc;
+ goto exit;
+ }
+ }
+
+ if (query->header.domain == GRN_DB_TEXT && GRN_TEXT_LEN(query) > 0) {
+ const char *query_string;
+ unsigned int query_string_len;
+ grn_obj expanded_query;
+ grn_expr_flags flags =
+ GRN_EXPR_SYNTAX_QUERY|GRN_EXPR_ALLOW_PRAGMA|GRN_EXPR_ALLOW_COLUMN;
+
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, table, condition, dummy_variable);
+ if (!condition) {
+ rc = ctx->rc;
+ goto exit;
+ }
+
+ query_string = GRN_TEXT_VALUE(query);
+ query_string_len = GRN_TEXT_LEN(query);
+
+ GRN_TEXT_INIT(&expanded_query, 0);
+ if (query_expander_name &&
+ query_expander_name->header.domain == GRN_DB_TEXT &&
+ GRN_TEXT_LEN(query_expander_name) > 0) {
+ rc = expand_query(ctx, query_string, query_string_len, flags,
+ GRN_TEXT_VALUE(query_expander_name),
+ GRN_TEXT_LEN(query_expander_name),
+ &expanded_query);
+ if (rc != GRN_SUCCESS) {
+ GRN_OBJ_FIN(ctx, &expanded_query);
+ goto exit;
+ }
+ query_string = GRN_TEXT_VALUE(&expanded_query);
+ query_string_len = GRN_TEXT_LEN(&expanded_query);
+ }
+ grn_expr_parse(ctx, condition,
+ query_string,
+ query_string_len,
+ match_columns, GRN_OP_MATCH, GRN_OP_AND, flags);
+ rc = ctx->rc;
+ GRN_OBJ_FIN(ctx, &expanded_query);
+ if (rc != GRN_SUCCESS) {
+ goto exit;
+ }
+ grn_table_select(ctx, table, condition, res, op);
+ rc = ctx->rc;
+ }
+
+exit:
+ if (match_columns) {
+ grn_obj_unlink(ctx, match_columns);
+ }
+ if (condition) {
+ grn_obj_unlink(ctx, condition);
+ }
+
+ return rc;
+}
+
+static grn_obj *
+func_query(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ selector_to_function_data data;
+
+ if (selector_to_function_data_init(ctx, &data, user_data)) {
+ grn_rc rc;
+ rc = run_query(ctx, data.table, nargs, args, data.records, GRN_OP_AND);
+ if (rc == GRN_SUCCESS) {
+ selector_to_function_data_selected(ctx, &data);
+ }
+ }
+ selector_to_function_data_fin(ctx, &data);
+
+ return data.found;
+}
+
+static grn_rc
+selector_query(grn_ctx *ctx, grn_obj *table, grn_obj *index,
+ int nargs, grn_obj **args,
+ grn_obj *res, grn_operator op)
+{
+ return run_query(ctx, table, nargs - 1, args + 1, res, op);
+}
+
+static grn_rc
+run_sub_filter(grn_ctx *ctx, grn_obj *table,
+ int nargs, grn_obj **args,
+ grn_obj *res, grn_operator op)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_obj *scope;
+ grn_obj *sub_filter_string;
+ grn_obj *scope_domain = NULL;
+ grn_obj *sub_filter = NULL;
+ grn_obj *dummy_variable = NULL;
+
+ if (nargs != 2) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "sub_filter(): wrong number of arguments (%d for 2)", nargs);
+ rc = ctx->rc;
+ goto exit;
+ }
+
+ scope = args[0];
+ sub_filter_string = args[1];
+
+ switch (scope->header.type) {
+ case GRN_ACCESSOR :
+ case GRN_COLUMN_FIX_SIZE :
+ case GRN_COLUMN_VAR_SIZE :
+ break;
+ default :
+ /* TODO: put inspected the 1nd argument to message */
+ ERR(GRN_INVALID_ARGUMENT,
+ "sub_filter(): the 1nd argument must be column or accessor");
+ rc = ctx->rc;
+ goto exit;
+ break;
+ }
+
+ scope_domain = grn_ctx_at(ctx, grn_obj_get_range(ctx, scope));
+
+ if (sub_filter_string->header.domain != GRN_DB_TEXT) {
+ /* TODO: put inspected the 2nd argument to message */
+ ERR(GRN_INVALID_ARGUMENT,
+ "sub_filter(): the 2nd argument must be String");
+ rc = ctx->rc;
+ goto exit;
+ }
+ if (GRN_TEXT_LEN(sub_filter_string) == 0) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "sub_filter(): the 2nd argument must not be empty String");
+ rc = ctx->rc;
+ goto exit;
+ }
+
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, scope_domain, sub_filter, dummy_variable);
+ if (!sub_filter) {
+ rc = ctx->rc;
+ goto exit;
+ }
+
+ grn_expr_parse(ctx, sub_filter,
+ GRN_TEXT_VALUE(sub_filter_string),
+ GRN_TEXT_LEN(sub_filter_string),
+ NULL, GRN_OP_MATCH, GRN_OP_AND,
+ GRN_EXPR_SYNTAX_SCRIPT);
+ if (ctx->rc != GRN_SUCCESS) {
+ rc = ctx->rc;
+ goto exit;
+ }
+
+ {
+ grn_obj *base_res = NULL;
+ grn_obj *resolve_res = NULL;
+
+ base_res = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC,
+ scope_domain, NULL);
+ grn_table_select(ctx, scope_domain, sub_filter, base_res, GRN_OP_OR);
+ if (scope->header.type == GRN_ACCESSOR) {
+ rc = grn_accessor_resolve(ctx, scope, -1, base_res, &resolve_res, NULL);
+ } else {
+ grn_accessor accessor;
+ accessor.header.type = GRN_ACCESSOR;
+ accessor.obj = scope;
+ accessor.action = GRN_ACCESSOR_GET_COLUMN_VALUE;
+ accessor.next = NULL;
+ rc = grn_accessor_resolve(ctx, (grn_obj *)&accessor, -1, base_res,
+ &resolve_res, NULL);
+ }
+ if (resolve_res) {
+ rc = grn_table_setoperation(ctx, res, resolve_res, res, op);
+ grn_obj_unlink(ctx, resolve_res);
+ }
+ grn_obj_unlink(ctx, base_res);
+ }
+
+exit:
+ if (scope_domain) {
+ grn_obj_unlink(ctx, scope_domain);
+ }
+ if (sub_filter) {
+ grn_obj_unlink(ctx, sub_filter);
+ }
+
+ return rc;
+}
+
+static grn_obj *
+func_sub_filter(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ selector_to_function_data data;
+
+ if (selector_to_function_data_init(ctx, &data, user_data)) {
+ grn_rc rc;
+ rc = run_sub_filter(ctx, data.table, nargs, args, data.records, GRN_OP_AND);
+ if (rc == GRN_SUCCESS) {
+ selector_to_function_data_selected(ctx, &data);
+ }
+ }
+ selector_to_function_data_fin(ctx, &data);
+
+ return data.found;
+}
+
+static grn_rc
+selector_sub_filter(grn_ctx *ctx, grn_obj *table, grn_obj *index,
+ int nargs, grn_obj **args,
+ grn_obj *res, grn_operator op)
+{
+ return run_sub_filter(ctx, table, nargs - 1, args + 1, res, op);
+}
+
+static grn_obj *
+func_html_untag(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data)
+{
+ grn_obj *html_arg;
+ int html_arg_domain;
+ grn_obj html;
+ grn_obj *text;
+ const char *html_raw;
+ int i, length;
+ grn_bool in_tag = GRN_FALSE;
+
+ if (nargs != 1) {
+ ERR(GRN_INVALID_ARGUMENT, "HTML is missing");
+ return NULL;
+ }
+
+ html_arg = args[0];
+ html_arg_domain = html_arg->header.domain;
+ switch (html_arg_domain) {
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ GRN_VALUE_VAR_SIZE_INIT(&html, GRN_OBJ_DO_SHALLOW_COPY, html_arg_domain);
+ GRN_TEXT_SET(ctx, &html, GRN_TEXT_VALUE(html_arg), GRN_TEXT_LEN(html_arg));
+ break;
+ default :
+ GRN_TEXT_INIT(&html, 0);
+ if (grn_obj_cast(ctx, html_arg, &html, GRN_FALSE)) {
+ grn_obj inspected;
+ GRN_TEXT_INIT(&inspected, 0);
+ grn_inspect(ctx, &inspected, html_arg);
+ ERR(GRN_INVALID_ARGUMENT, "failed to cast to text: <%.*s>",
+ (int)GRN_TEXT_LEN(&inspected), GRN_TEXT_VALUE(&inspected));
+ GRN_OBJ_FIN(ctx, &inspected);
+ GRN_OBJ_FIN(ctx, &html);
+ return NULL;
+ }
+ break;
+ }
+
+ text = GRN_PROC_ALLOC(html.header.domain, 0);
+ if (!text) {
+ GRN_OBJ_FIN(ctx, &html);
+ return NULL;
+ }
+
+ html_raw = GRN_TEXT_VALUE(&html);
+ length = GRN_TEXT_LEN(&html);
+ for (i = 0; i < length; i++) {
+ switch (html_raw[i]) {
+ case '<' :
+ in_tag = GRN_TRUE;
+ break;
+ case '>' :
+ if (in_tag) {
+ in_tag = GRN_FALSE;
+ } else {
+ GRN_TEXT_PUTC(ctx, text, html_raw[i]);
+ }
+ break;
+ default :
+ if (!in_tag) {
+ GRN_TEXT_PUTC(ctx, text, html_raw[i]);
+ }
+ break;
+ }
+ }
+
+ GRN_OBJ_FIN(ctx, &html);
+
+ return text;
+}
+
+static grn_bool
+grn_text_equal_cstr(grn_ctx *ctx, grn_obj *text, const char *cstr)
+{
+ int cstr_len;
+
+ cstr_len = strlen(cstr);
+ return (GRN_TEXT_LEN(text) == cstr_len &&
+ strncmp(GRN_TEXT_VALUE(text), cstr, cstr_len) == 0);
+}
+
+typedef enum {
+ BETWEEN_BORDER_INVALID,
+ BETWEEN_BORDER_INCLUDE,
+ BETWEEN_BORDER_EXCLUDE
+} between_border_type;
+
+typedef struct {
+ grn_obj *value;
+ grn_obj *min;
+ between_border_type min_border_type;
+ grn_obj *max;
+ between_border_type max_border_type;
+} between_data;
+
+static between_border_type
+between_parse_border(grn_ctx *ctx, grn_obj *border,
+ const char *argument_description)
+{
+ grn_obj inspected;
+
+ /* TODO: support other text types */
+ if (border->header.domain == GRN_DB_TEXT) {
+ if (grn_text_equal_cstr(ctx, border, "include")) {
+ return BETWEEN_BORDER_INCLUDE;
+ } else if (grn_text_equal_cstr(ctx, border, "exclude")) {
+ return BETWEEN_BORDER_EXCLUDE;
+ }
+ }
+
+ GRN_TEXT_INIT(&inspected, 0);
+ grn_inspect(ctx, &inspected, border);
+ ERR(GRN_INVALID_ARGUMENT,
+ "between(): %s must be \"include\" or \"exclude\": <%.*s>",
+ argument_description,
+ (int)GRN_TEXT_LEN(&inspected),
+ GRN_TEXT_VALUE(&inspected));
+ grn_obj_unlink(ctx, &inspected);
+
+ return BETWEEN_BORDER_INVALID;
+}
+
+static grn_rc
+between_parse_args(grn_ctx *ctx, int nargs, grn_obj **args, between_data *data)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_obj *min_border;
+ grn_obj *max_border;
+
+ if (nargs != 5) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "between(): wrong number of arguments (%d for 5)", nargs);
+ rc = ctx->rc;
+ goto exit;
+ }
+
+ data->value = args[0];
+ data->min = args[1];
+ min_border = args[2];
+ data->max = args[3];
+ max_border = args[4];
+
+ data->min_border_type =
+ between_parse_border(ctx, min_border, "the 3rd argument (min_border)");
+ if (data->min_border_type == BETWEEN_BORDER_INVALID) {
+ rc = ctx->rc;
+ goto exit;
+ }
+
+ data->max_border_type =
+ between_parse_border(ctx, max_border, "the 5th argument (max_border)");
+ if (data->max_border_type == BETWEEN_BORDER_INVALID) {
+ rc = ctx->rc;
+ goto exit;
+ }
+
+exit :
+ return rc;
+}
+
+static grn_rc
+between_cast(grn_ctx *ctx, grn_obj *source, grn_obj *destination, grn_id domain,
+ const char *target_argument_name)
+{
+ grn_rc rc;
+
+ GRN_OBJ_INIT(destination, GRN_BULK, 0, domain);
+ rc = grn_obj_cast(ctx, source, destination, GRN_FALSE);
+ if (rc != GRN_SUCCESS) {
+ grn_obj inspected_source;
+ grn_obj *domain_object;
+ char domain_name[GRN_TABLE_MAX_KEY_SIZE];
+ int domain_name_length;
+
+ GRN_TEXT_INIT(&inspected_source, 0);
+ grn_inspect(ctx, &inspected_source, source);
+
+ domain_object = grn_ctx_at(ctx, domain);
+ domain_name_length =
+ grn_obj_name(ctx, domain_object, domain_name, GRN_TABLE_MAX_KEY_SIZE);
+
+ ERR(rc, "between(): failed to cast %s: <%.*s> -> <%.*s>",
+ target_argument_name,
+ (int)GRN_TEXT_LEN(&inspected_source),
+ GRN_TEXT_VALUE(&inspected_source),
+ domain_name_length,
+ domain_name);
+
+ grn_obj_unlink(ctx, &inspected_source);
+ grn_obj_unlink(ctx, domain_object);
+ }
+
+ return rc;
+}
+
+static grn_obj *
+func_between(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_obj *found;
+ between_data data;
+ grn_obj *condition = NULL;
+ grn_obj *variable;
+ grn_obj *table;
+ grn_obj *between_expr;
+ grn_obj *between_variable;
+ grn_obj *result;
+
+ found = GRN_PROC_ALLOC(GRN_DB_BOOL, 0);
+ if (!found) {
+ return NULL;
+ }
+ GRN_BOOL_SET(ctx, found, GRN_FALSE);
+
+ grn_proc_get_info(ctx, user_data, NULL, NULL, &condition);
+ if (!condition) {
+ return found;
+ }
+
+ variable = grn_expr_get_var_by_offset(ctx, condition, 0);
+ if (!variable) {
+ return found;
+ }
+
+ rc = between_parse_args(ctx, nargs, args, &data);
+ if (rc != GRN_SUCCESS) {
+ return found;
+ }
+
+ table = grn_ctx_at(ctx, variable->header.domain);
+ if (!table) {
+ return found;
+ }
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, table, between_expr, between_variable);
+ if (!between_expr) {
+ grn_obj_unlink(ctx, table);
+ return found;
+ }
+
+ grn_expr_append_obj(ctx, between_expr, data.value, GRN_OP_PUSH, 1);
+ grn_expr_append_obj(ctx, between_expr, data.min, GRN_OP_PUSH, 1);
+ if (data.min_border_type == BETWEEN_BORDER_INCLUDE) {
+ grn_expr_append_op(ctx, between_expr, GRN_OP_GREATER_EQUAL, 2);
+ } else {
+ grn_expr_append_op(ctx, between_expr, GRN_OP_GREATER, 2);
+ }
+
+ grn_expr_append_obj(ctx, between_expr, data.value, GRN_OP_PUSH, 1);
+ grn_expr_append_obj(ctx, between_expr, data.max, GRN_OP_PUSH, 1);
+ if (data.max_border_type == BETWEEN_BORDER_INCLUDE) {
+ grn_expr_append_op(ctx, between_expr, GRN_OP_LESS_EQUAL, 2);
+ } else {
+ grn_expr_append_op(ctx, between_expr, GRN_OP_LESS, 2);
+ }
+
+ grn_expr_append_op(ctx, between_expr, GRN_OP_AND, 2);
+
+ GRN_RECORD_SET(ctx, between_variable, GRN_RECORD_VALUE(variable));
+ result = grn_expr_exec(ctx, between_expr, 0);
+ if (result && GRN_UINT32_VALUE(result) > 0) {
+ GRN_BOOL_SET(ctx, found, GRN_TRUE);
+ }
+
+ grn_obj_unlink(ctx, between_expr);
+ grn_obj_unlink(ctx, table);
+
+ return found;
+}
+
+static grn_rc
+selector_between(grn_ctx *ctx, grn_obj *table, grn_obj *index,
+ int nargs, grn_obj **args,
+ grn_obj *res, grn_operator op)
+{
+ grn_rc rc = GRN_SUCCESS;
+ int offset = 0;
+ int limit = -1;
+ int flags = GRN_CURSOR_ASCENDING | GRN_CURSOR_BY_KEY;
+ between_data data;
+ grn_obj casted_min, casted_max;
+ grn_obj *used_min = NULL;
+ grn_obj *used_max = NULL;
+ grn_obj *index_table = NULL;
+ grn_table_cursor *cursor;
+ grn_id id;
+
+ if (!index) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ rc = between_parse_args(ctx, nargs - 1, args + 1, &data);
+ if (rc != GRN_SUCCESS) {
+ return rc;
+ }
+
+ if (data.min_border_type == BETWEEN_BORDER_EXCLUDE) {
+ flags |= GRN_CURSOR_GT;
+ }
+ if (data.max_border_type == BETWEEN_BORDER_EXCLUDE) {
+ flags |= GRN_CURSOR_LT;
+ }
+
+ index_table = grn_ctx_at(ctx, index->header.domain);
+ if (data.min->header.domain == index_table->header.domain) {
+ used_min = data.min;
+ } else {
+ used_min = &casted_min;
+ rc = between_cast(ctx, data.min, &casted_min, index_table->header.domain,
+ "min");
+ if (rc != GRN_SUCCESS) {
+ goto exit;
+ }
+ }
+ if (data.max->header.domain == index_table->header.domain) {
+ used_max = data.max;
+ } else {
+ used_max = &casted_max;
+ rc = between_cast(ctx, data.max, &casted_max, index_table->header.domain,
+ "max");
+ if (rc != GRN_SUCCESS) {
+ goto exit;
+ }
+ }
+ cursor = grn_table_cursor_open(ctx, index_table,
+ GRN_BULK_HEAD(used_min),
+ GRN_BULK_VSIZE(used_min),
+ GRN_BULK_HEAD(used_max),
+ GRN_BULK_VSIZE(used_max),
+ offset, limit, flags);
+ if (!cursor) {
+ rc = ctx->rc;
+ goto exit;
+ }
+
+ while ((id = grn_table_cursor_next(ctx, cursor))) {
+ grn_ii_at(ctx, (grn_ii *)index, id, (grn_hash *)res, op);
+ }
+ grn_ii_resolve_sel_and(ctx, (grn_hash *)res, op);
+ grn_table_cursor_close(ctx, cursor);
+
+exit :
+ if (used_min == &casted_min) {
+ grn_obj_unlink(ctx, &casted_min);
+ }
+ if (used_max == &casted_max) {
+ grn_obj_unlink(ctx, &casted_max);
+ }
+ if (index_table) {
+ grn_obj_unlink(ctx, index_table);
+ }
+
+ return rc;
+}
+
+static void
+grn_pat_tag_keys_put_original_text(grn_ctx *ctx, grn_obj *output,
+ const char *text, unsigned int length,
+ grn_bool use_html_escape)
+{
+ if (use_html_escape) {
+ grn_text_escape_xml(ctx, output, text, length);
+ } else {
+ GRN_TEXT_PUT(ctx, output, text, length);
+ }
+}
+
+static grn_rc
+grn_pat_tag_keys(grn_ctx *ctx, grn_obj *keywords,
+ const char *string, unsigned int string_length,
+ const char **open_tags, unsigned int *open_tag_lengths,
+ const char **close_tags, unsigned int *close_tag_lengths,
+ unsigned int n_tags,
+ grn_obj *highlighted,
+ grn_bool use_html_escape)
+{
+ while (string_length > 0) {
+#define MAX_N_HITS 1024
+ grn_pat_scan_hit hits[MAX_N_HITS];
+ const char *rest;
+ unsigned int i, n_hits;
+ unsigned int previous = 0;
+
+ n_hits = grn_pat_scan(ctx, (grn_pat *)keywords,
+ string, string_length,
+ hits, MAX_N_HITS, &rest);
+ for (i = 0; i < n_hits; i++) {
+ unsigned int nth_tag;
+ if (hits[i].offset - previous > 0) {
+ grn_pat_tag_keys_put_original_text(ctx,
+ highlighted,
+ string + previous,
+ hits[i].offset - previous,
+ use_html_escape);
+ }
+ nth_tag = ((hits[i].id - 1) % n_tags);
+ GRN_TEXT_PUT(ctx, highlighted,
+ open_tags[nth_tag], open_tag_lengths[nth_tag]);
+ grn_pat_tag_keys_put_original_text(ctx,
+ highlighted,
+ string + hits[i].offset,
+ hits[i].length,
+ use_html_escape);
+ GRN_TEXT_PUT(ctx, highlighted,
+ close_tags[nth_tag], close_tag_lengths[nth_tag]);
+ previous = hits[i].offset + hits[i].length;
+ }
+ if (string_length - previous > 0) {
+ grn_pat_tag_keys_put_original_text(ctx,
+ highlighted,
+ string + previous,
+ string_length - previous,
+ use_html_escape);
+ }
+ string_length -= rest - string;
+ string = rest;
+#undef MAX_N_HITS
+ }
+
+ return GRN_SUCCESS;
+}
+
+static grn_obj *
+func_highlight_html(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data)
+{
+ grn_obj *highlighted = NULL;
+
+#define N_REQUIRED_ARGS 1
+ if (nargs == N_REQUIRED_ARGS) {
+ grn_obj *string = args[0];
+ grn_obj *expression = NULL;
+ grn_obj *condition_ptr = NULL;
+ grn_obj *condition = NULL;
+ grn_bool use_html_escape = GRN_TRUE;
+ unsigned int n_keyword_sets = 1;
+ const char *open_tags[1];
+ unsigned int open_tag_lengths[1];
+ const char *close_tags[1];
+ unsigned int close_tag_lengths[1];
+ grn_obj *keywords;
+
+ open_tags[0] = "<span class=\"keyword\">";
+ open_tag_lengths[0] = strlen("<span class=\"keyword\">");
+ close_tags[0] = "</span>";
+ close_tag_lengths[0] = strlen("</span>");
+
+ keywords = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_OBJ_TABLE_PAT_KEY,
+ grn_ctx_at(ctx, GRN_DB_SHORT_TEXT),
+ NULL);
+ {
+ grn_obj *normalizer;
+ normalizer = grn_ctx_get(ctx, "NormalizerAuto", -1);
+ grn_obj_set_info(ctx, keywords, GRN_INFO_NORMALIZER, normalizer);
+ grn_obj_unlink(ctx, normalizer);
+ }
+
+ grn_proc_get_info(ctx, user_data, NULL, NULL, &expression);
+ condition_ptr = grn_expr_get_var(ctx, expression,
+ GRN_SELECT_INTERNAL_VAR_CONDITION,
+ strlen(GRN_SELECT_INTERNAL_VAR_CONDITION));
+ if (condition_ptr) {
+ condition = GRN_PTR_VALUE(condition_ptr);
+ }
+
+ if (condition) {
+ grn_obj current_keywords;
+ GRN_PTR_INIT(&current_keywords, GRN_OBJ_VECTOR, GRN_ID_NIL);
+ grn_expr_get_keywords(ctx, condition, &current_keywords);
+
+ for (;;) {
+ grn_obj *keyword;
+ GRN_PTR_POP(&current_keywords, keyword);
+ if (!keyword) { break; }
+ grn_table_add(ctx, keywords,
+ GRN_TEXT_VALUE(keyword),
+ GRN_TEXT_LEN(keyword),
+ NULL);
+ }
+ grn_obj_unlink(ctx, &current_keywords);
+ }
+
+ highlighted = GRN_PROC_ALLOC(GRN_DB_TEXT, 0);
+ grn_pat_tag_keys(ctx, keywords,
+ GRN_TEXT_VALUE(string), GRN_TEXT_LEN(string),
+ open_tags,
+ open_tag_lengths,
+ close_tags,
+ close_tag_lengths,
+ n_keyword_sets,
+ highlighted,
+ use_html_escape);
+
+ grn_obj_unlink(ctx, keywords);
+ }
+#undef N_REQUIRED_ARGS
+
+ if (!highlighted) {
+ highlighted = GRN_PROC_ALLOC(GRN_DB_VOID, 0);
+ }
+
+ return highlighted;
+}
+
+static grn_obj *
+func_highlight_full(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data)
+{
+ grn_obj *highlighted = NULL;
+
+#define N_REQUIRED_ARGS 3
+#define KEYWORD_SET_SIZE 3
+ if (nargs >= (N_REQUIRED_ARGS + KEYWORD_SET_SIZE) &&
+ (nargs - N_REQUIRED_ARGS) % KEYWORD_SET_SIZE == 0) {
+ grn_obj *string = args[0];
+ grn_obj *normalizer_name = args[1];
+ grn_obj *use_html_escape = args[2];
+ grn_obj **keyword_set_args = args + N_REQUIRED_ARGS;
+ unsigned int n_keyword_sets = (nargs - N_REQUIRED_ARGS) / KEYWORD_SET_SIZE;
+ unsigned int i;
+ grn_obj open_tags;
+ grn_obj open_tag_lengths;
+ grn_obj close_tags;
+ grn_obj close_tag_lengths;
+ grn_obj *keywords;
+
+ keywords = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_OBJ_TABLE_PAT_KEY,
+ grn_ctx_at(ctx, GRN_DB_SHORT_TEXT),
+ NULL);
+
+ if (GRN_TEXT_LEN(normalizer_name)) {
+ grn_obj *normalizer;
+ normalizer = grn_ctx_get(ctx,
+ GRN_TEXT_VALUE(normalizer_name),
+ GRN_TEXT_LEN(normalizer_name));
+ if (!is_normalizer(ctx, normalizer)) {
+ grn_obj inspected;
+ GRN_TEXT_INIT(&inspected, 0);
+ grn_inspect(ctx, &inspected, normalizer);
+ ERR(GRN_INVALID_ARGUMENT,
+ "[highlight_full] not normalizer: %.*s",
+ (int)GRN_TEXT_LEN(&inspected),
+ GRN_TEXT_VALUE(&inspected));
+ GRN_OBJ_FIN(ctx, &inspected);
+ grn_obj_unlink(ctx, normalizer);
+ grn_obj_unlink(ctx, keywords);
+ return NULL;
+ }
+ grn_obj_set_info(ctx, keywords, GRN_INFO_NORMALIZER, normalizer);
+ grn_obj_unlink(ctx, normalizer);
+ }
+
+ GRN_OBJ_INIT(&open_tags, GRN_BULK, 0, GRN_DB_VOID);
+ GRN_OBJ_INIT(&open_tag_lengths, GRN_BULK, 0, GRN_DB_VOID);
+ GRN_OBJ_INIT(&close_tags, GRN_BULK, 0, GRN_DB_VOID);
+ GRN_OBJ_INIT(&close_tag_lengths, GRN_BULK, 0, GRN_DB_VOID);
+ for (i = 0; i < n_keyword_sets; i++) {
+ grn_obj *keyword = keyword_set_args[i * KEYWORD_SET_SIZE + 0];
+ grn_obj *open_tag = keyword_set_args[i * KEYWORD_SET_SIZE + 1];
+ grn_obj *close_tag = keyword_set_args[i * KEYWORD_SET_SIZE + 2];
+
+ grn_table_add(ctx, keywords,
+ GRN_TEXT_VALUE(keyword),
+ GRN_TEXT_LEN(keyword),
+ NULL);
+
+ {
+ const char *open_tag_content = GRN_TEXT_VALUE(open_tag);
+ grn_bulk_write(ctx, &open_tags,
+ (const char *)(&open_tag_content),
+ sizeof(char *));
+ }
+ {
+ unsigned int open_tag_length = GRN_TEXT_LEN(open_tag);
+ grn_bulk_write(ctx, &open_tag_lengths,
+ (const char *)(&open_tag_length),
+ sizeof(unsigned int));
+ }
+ {
+ const char *close_tag_content = GRN_TEXT_VALUE(close_tag);
+ grn_bulk_write(ctx, &close_tags,
+ (const char *)(&close_tag_content),
+ sizeof(char *));
+ }
+ {
+ unsigned int close_tag_length = GRN_TEXT_LEN(close_tag);
+ grn_bulk_write(ctx, &close_tag_lengths,
+ (const char *)(&close_tag_length),
+ sizeof(unsigned int));
+ }
+ }
+
+ highlighted = GRN_PROC_ALLOC(GRN_DB_TEXT, 0);
+ grn_pat_tag_keys(ctx, keywords,
+ GRN_TEXT_VALUE(string), GRN_TEXT_LEN(string),
+ (const char **)GRN_BULK_HEAD(&open_tags),
+ (unsigned int *)GRN_BULK_HEAD(&open_tag_lengths),
+ (const char **)GRN_BULK_HEAD(&close_tags),
+ (unsigned int *)GRN_BULK_HEAD(&close_tag_lengths),
+ n_keyword_sets,
+ highlighted,
+ GRN_BOOL_VALUE(use_html_escape));
+
+ grn_obj_unlink(ctx, keywords);
+ grn_obj_unlink(ctx, &open_tags);
+ grn_obj_unlink(ctx, &open_tag_lengths);
+ grn_obj_unlink(ctx, &close_tags);
+ grn_obj_unlink(ctx, &close_tag_lengths);
+ }
+#undef N_REQUIRED_ARGS
+#undef KEYWORD_SET_SIZE
+
+ if (!highlighted) {
+ highlighted = GRN_PROC_ALLOC(GRN_DB_VOID, 0);
+ }
+
+ return highlighted;
+}
+
+#define DEF_VAR(v,name_str) do {\
+ (v).name = (name_str);\
+ (v).name_size = GRN_STRLEN(name_str);\
+ GRN_TEXT_INIT(&(v).value, 0);\
+} while (0)
+
+#define DEF_COMMAND(name, func, nvars, vars)\
+ (grn_proc_create(ctx, (name), (sizeof(name) - 1),\
+ GRN_PROC_COMMAND, (func), NULL, NULL, (nvars), (vars)))
+
+void
+grn_db_init_builtin_query(grn_ctx *ctx)
+{
+ grn_expr_var vars[21];
+
+ DEF_VAR(vars[0], "name");
+ DEF_VAR(vars[1], "table");
+ DEF_VAR(vars[2], "match_columns");
+ DEF_VAR(vars[3], "query");
+ DEF_VAR(vars[4], "filter");
+ DEF_VAR(vars[5], "scorer");
+ DEF_VAR(vars[6], "sortby");
+ DEF_VAR(vars[7], "output_columns");
+ DEF_VAR(vars[8], "offset");
+ DEF_VAR(vars[9], "limit");
+ DEF_VAR(vars[10], "drilldown");
+ DEF_VAR(vars[11], "drilldown_sortby");
+ DEF_VAR(vars[12], "drilldown_output_columns");
+ DEF_VAR(vars[13], "drilldown_offset");
+ DEF_VAR(vars[14], "drilldown_limit");
+ DEF_VAR(vars[15], "cache");
+ DEF_VAR(vars[16], "match_escalation_threshold");
+ /* Deprecated. Use query_expander instead. */
+ DEF_VAR(vars[17], "query_expansion");
+ DEF_VAR(vars[18], "query_flags");
+ DEF_VAR(vars[19], "query_expander");
+ DEF_VAR(vars[20], "adjuster");
+ DEF_COMMAND("define_selector", proc_define_selector, 21, vars);
+ DEF_COMMAND("select", proc_select, 20, vars + 1);
+
+ DEF_VAR(vars[0], "values");
+ DEF_VAR(vars[1], "table");
+ DEF_VAR(vars[2], "columns");
+ DEF_VAR(vars[3], "ifexists");
+ DEF_VAR(vars[4], "input_type");
+ DEF_VAR(vars[5], "each");
+ DEF_COMMAND("load", proc_load, 6, vars);
+
+ DEF_COMMAND("status", proc_status, 0, vars);
+
+ DEF_COMMAND("table_list", proc_table_list, 0, vars);
+
+ DEF_VAR(vars[0], "table");
+ DEF_COMMAND("column_list", proc_column_list, 1, vars);
+
+ DEF_VAR(vars[0], "name");
+ DEF_VAR(vars[1], "flags");
+ DEF_VAR(vars[2], "key_type");
+ DEF_VAR(vars[3], "value_type");
+ DEF_VAR(vars[4], "default_tokenizer");
+ DEF_VAR(vars[5], "normalizer");
+ DEF_COMMAND("table_create", proc_table_create, 6, vars);
+
+ DEF_VAR(vars[0], "name");
+ DEF_COMMAND("table_remove", proc_table_remove, 1, vars);
+
+ DEF_VAR(vars[0], "name");
+ DEF_VAR(vars[1], "new_name");
+ DEF_COMMAND("table_rename", proc_table_rename, 2, vars);
+
+ DEF_VAR(vars[0], "table");
+ DEF_VAR(vars[1], "name");
+ DEF_VAR(vars[2], "flags");
+ DEF_VAR(vars[3], "type");
+ DEF_VAR(vars[4], "source");
+ DEF_COMMAND("column_create", proc_column_create, 5, vars);
+
+ DEF_VAR(vars[0], "table");
+ DEF_VAR(vars[1], "name");
+ DEF_COMMAND("column_remove", proc_column_remove, 2, vars);
+
+ DEF_VAR(vars[0], "table");
+ DEF_VAR(vars[1], "name");
+ DEF_VAR(vars[2], "new_name");
+ DEF_COMMAND("column_rename", proc_column_rename, 3, vars);
+
+ DEF_VAR(vars[0], "path");
+ DEF_COMMAND(GRN_EXPR_MISSING_NAME, proc_missing, 1, vars);
+
+ DEF_COMMAND("quit", proc_quit, 0, vars);
+
+ DEF_COMMAND("shutdown", proc_shutdown, 0, vars);
+
+ DEF_VAR(vars[0], "target_name");
+ DEF_COMMAND("clearlock", proc_clearlock, 1, vars);
+
+ DEF_VAR(vars[0], "target_name");
+ DEF_VAR(vars[1], "threshold");
+ DEF_COMMAND("defrag", proc_defrag, 2, vars);
+
+ DEF_VAR(vars[0], "level");
+ DEF_COMMAND("log_level", proc_log_level, 1, vars);
+
+ DEF_VAR(vars[0], "level");
+ DEF_VAR(vars[1], "message");
+ DEF_COMMAND("log_put", proc_log_put, 2, vars);
+
+ DEF_COMMAND("log_reopen", proc_log_reopen, 0, vars);
+
+ DEF_VAR(vars[0], "table");
+ DEF_VAR(vars[1], "key");
+ DEF_VAR(vars[2], "id");
+ DEF_VAR(vars[3], "filter");
+ DEF_COMMAND("delete", proc_delete, 4, vars);
+
+ DEF_VAR(vars[0], "max");
+ DEF_COMMAND("cache_limit", proc_cache_limit, 1, vars);
+
+ DEF_VAR(vars[0], "tables");
+ DEF_COMMAND("dump", proc_dump, 1, vars);
+
+ DEF_VAR(vars[0], "path");
+ DEF_COMMAND("register", proc_register, 1, vars);
+
+ DEF_VAR(vars[0], "obj");
+ DEF_COMMAND("check", proc_check, 1, vars);
+
+ DEF_VAR(vars[0], "table");
+ DEF_COMMAND("truncate", proc_truncate, 1, vars);
+
+ DEF_VAR(vars[0], "normalizer");
+ DEF_VAR(vars[1], "string");
+ DEF_VAR(vars[2], "flags");
+ DEF_COMMAND("normalize", proc_normalize, 3, vars);
+
+ DEF_VAR(vars[0], "tokenizer");
+ DEF_VAR(vars[1], "string");
+ DEF_VAR(vars[2], "normalizer");
+ DEF_VAR(vars[3], "flags");
+ DEF_VAR(vars[4], "mode");
+ DEF_COMMAND("tokenize", proc_tokenize, 5, vars);
+
+ DEF_COMMAND("tokenizer_list", proc_tokenizer_list, 0, vars);
+
+ DEF_COMMAND("normalizer_list", proc_normalizer_list, 0, vars);
+
+ DEF_VAR(vars[0], "seed");
+ grn_proc_create(ctx, "rand", -1, GRN_PROC_FUNCTION, func_rand,
+ NULL, NULL, 0, vars);
+
+ grn_proc_create(ctx, "now", -1, GRN_PROC_FUNCTION, func_now,
+ NULL, NULL, 0, vars);
+
+ grn_proc_create(ctx, "max", -1, GRN_PROC_FUNCTION, func_max,
+ NULL, NULL, 0, vars);
+ grn_proc_create(ctx, "min", -1, GRN_PROC_FUNCTION, func_min,
+ NULL, NULL, 0, vars);
+
+ {
+ grn_obj *selector_proc;
+
+ selector_proc = grn_proc_create(ctx, "geo_in_circle", -1, GRN_PROC_FUNCTION,
+ func_geo_in_circle, NULL, NULL, 0, NULL);
+ grn_proc_set_selector(ctx, selector_proc, grn_selector_geo_in_circle);
+
+ selector_proc = grn_proc_create(ctx, "geo_in_rectangle", -1,
+ GRN_PROC_FUNCTION,
+ func_geo_in_rectangle, NULL, NULL, 0, NULL);
+ grn_proc_set_selector(ctx, selector_proc, grn_selector_geo_in_rectangle);
+ }
+
+ grn_proc_create(ctx, "geo_distance", -1, GRN_PROC_FUNCTION,
+ func_geo_distance, NULL, NULL, 0, NULL);
+
+ /* deprecated. */
+ grn_proc_create(ctx, "geo_distance2", -1, GRN_PROC_FUNCTION,
+ func_geo_distance2, NULL, NULL, 0, NULL);
+
+ /* deprecated. */
+ grn_proc_create(ctx, "geo_distance3", -1, GRN_PROC_FUNCTION,
+ func_geo_distance3, NULL, NULL, 0, NULL);
+
+ grn_proc_create(ctx, "edit_distance", -1, GRN_PROC_FUNCTION,
+ func_edit_distance, NULL, NULL, 0, NULL);
+
+ {
+ grn_obj *selector_proc;
+
+ selector_proc = grn_proc_create(ctx, "all_records", -1, GRN_PROC_FUNCTION,
+ func_all_records, NULL, NULL, 0, NULL);
+ grn_proc_set_selector(ctx, selector_proc, selector_all_records);
+ }
+
+ /* experimental */
+ grn_proc_create(ctx, "snippet_html", -1, GRN_PROC_FUNCTION,
+ func_snippet_html, NULL, NULL, 0, NULL);
+
+ {
+ grn_obj *selector_proc;
+
+ selector_proc = grn_proc_create(ctx, "query", -1, GRN_PROC_FUNCTION,
+ func_query, NULL, NULL, 0, NULL);
+ grn_proc_set_selector(ctx, selector_proc, selector_query);
+ }
+
+ {
+ grn_obj *selector_proc;
+
+ selector_proc = grn_proc_create(ctx, "sub_filter", -1, GRN_PROC_FUNCTION,
+ func_sub_filter, NULL, NULL, 0, NULL);
+ grn_proc_set_selector(ctx, selector_proc, selector_sub_filter);
+ }
+
+ grn_proc_create(ctx, "html_untag", -1, GRN_PROC_FUNCTION,
+ func_html_untag, NULL, NULL, 0, NULL);
+
+ {
+ grn_obj *selector_proc;
+
+ selector_proc = grn_proc_create(ctx, "between", -1, GRN_PROC_FUNCTION,
+ func_between, NULL, NULL, 0, NULL);
+ grn_proc_set_selector(ctx, selector_proc, selector_between);
+ }
+
+ grn_proc_create(ctx, "highlight_html", -1, GRN_PROC_FUNCTION,
+ func_highlight_html, NULL, NULL, 0, NULL);
+
+ grn_proc_create(ctx, "highlight_full", -1, GRN_PROC_FUNCTION,
+ func_highlight_full, NULL, NULL, 0, NULL);
+}
diff --git a/storage/mroonga/vendor/groonga/lib/proc.h b/storage/mroonga/vendor/groonga/lib/proc.h
new file mode 100644
index 00000000000..1f7f81d5d97
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/proc.h
@@ -0,0 +1,35 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_PROC_H
+#define GRN_PROC_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+GRN_VAR const char *grn_document_root;
+void grn_db_init_builtin_query(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_PROC_H */
diff --git a/storage/mroonga/vendor/groonga/lib/snip.c b/storage/mroonga/vendor/groonga/lib/snip.c
new file mode 100644
index 00000000000..f5ed677b091
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/snip.c
@@ -0,0 +1,838 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "groonga_in.h"
+#include <string.h>
+#include <stddef.h>
+#include "snip.h"
+#include "ctx.h"
+
+#if !defined MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#if !defined MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+static int
+grn_bm_check_euc(const unsigned char *x, const size_t y)
+{
+ const unsigned char *p;
+ for (p = x + y - 1; p >= x && *p >= 0x80U; p--);
+ return (int) ((x + y - p) & 1);
+}
+
+static int
+grn_bm_check_sjis(const unsigned char *x, const size_t y)
+{
+ const unsigned char *p;
+ for (p = x + y - 1; p >= x; p--)
+ if ((*p < 0x81U) || (*p > 0x9fU && *p < 0xe0U) || (*p > 0xfcU))
+ break;
+ return (int) ((x + y - p) & 1);
+}
+
+/*
+static void
+grn_bm_suffixes(const unsigned char *x, size_t m, size_t *suff)
+{
+ size_t f, g;
+ intptr_t i;
+ f = 0;
+ suff[m - 1] = m;
+ g = m - 1;
+ for (i = m - 2; i >= 0; --i) {
+ if (i > (intptr_t) g && suff[i + m - 1 - f] < i - g)
+ suff[i] = suff[i + m - 1 - f];
+ else {
+ if (i < (intptr_t) g)
+ g = i;
+ f = i;
+ while (g > 0 && x[g] == x[g + m - 1 - f])
+ --g;
+ suff[i] = f - g;
+ }
+ }
+}
+*/
+
+static void
+grn_bm_preBmBc(const unsigned char *x, size_t m, size_t *bmBc)
+{
+ size_t i;
+ for (i = 0; i < ASIZE; ++i) {
+ bmBc[i] = m;
+ }
+ for (i = 0; i < m - 1; ++i) {
+ bmBc[(unsigned int) x[i]] = m - (i + 1);
+ }
+}
+
+#define GRN_BM_COMPARE do { \
+ if (string_checks[found]) { \
+ size_t offset = cond->last_offset, found_alpha_head = cond->found_alpha_head; \
+ /* calc real offset */\
+ for (i = cond->last_found; i < found; i++) { \
+ if (string_checks[i] > 0) { \
+ found_alpha_head = i; \
+ offset += string_checks[i]; \
+ } \
+ } \
+ /* if real offset is in a character, move it the head of the character */ \
+ if (string_checks[found] < 0) { \
+ offset -= string_checks[found_alpha_head]; \
+ cond->last_found = found_alpha_head; \
+ } else { \
+ cond->last_found = found; \
+ } \
+ cond->start_offset = cond->last_offset = offset; \
+ if (flags & GRN_SNIP_SKIP_LEADING_SPACES) { \
+ while (cond->start_offset < string_original_length_in_bytes && \
+ (i = grn_isspace(string_original + cond->start_offset, \
+ string_encoding))) { cond->start_offset += i; } \
+ } \
+ for (i = cond->last_found; i < found + m; i++) { \
+ if (string_checks[i] > 0) { \
+ offset += string_checks[i]; \
+ } \
+ } \
+ cond->end_offset = offset; \
+ cond->found = found + shift; \
+ cond->found_alpha_head = found_alpha_head; \
+ /* printf("bm: cond:%p found:%zd last_found:%zd st_off:%zd ed_off:%zd\n", cond, cond->found,cond->last_found,cond->start_offset,cond->end_offset); */ \
+ return; \
+ } \
+} while (0)
+
+#define GRN_BM_BM_COMPARE do { \
+ if (p[-2] == ck) { \
+ for (i = 3; i <= m && p[-(intptr_t)i] == cp[-(intptr_t)i]; ++i) { \
+ } \
+ if (i > m) { \
+ found = p - y - m; \
+ GRN_BM_COMPARE; \
+ } \
+ } \
+} while (0)
+
+void
+grn_bm_tunedbm(grn_ctx *ctx, snip_cond *cond, grn_obj *string, int flags)
+{
+ register unsigned char *limit, ck;
+ register const unsigned char *p, *cp;
+ register size_t *bmBc, delta1, i;
+
+ const unsigned char *x;
+ unsigned char *y;
+ size_t shift, found;
+
+ const char *string_original;
+ unsigned int string_original_length_in_bytes;
+ const short *string_checks;
+ grn_encoding string_encoding;
+ const char *string_norm, *keyword_norm;
+ unsigned int n, m;
+
+ grn_string_get_original(ctx, string,
+ &string_original, &string_original_length_in_bytes);
+ string_checks = grn_string_get_checks(ctx, string);
+ string_encoding = grn_string_get_encoding(ctx, string);
+ grn_string_get_normalized(ctx, string, &string_norm, &n, NULL);
+ grn_string_get_normalized(ctx, cond->keyword, &keyword_norm, &m, NULL);
+
+ y = (unsigned char *)string_norm;
+ if (m == 1) {
+ if (n > cond->found) {
+ shift = 1;
+ p = memchr(y + cond->found, keyword_norm[0], n - cond->found);
+ if (p != NULL) {
+ found = p - y;
+ GRN_BM_COMPARE;
+ }
+ }
+ cond->stopflag = SNIPCOND_STOP;
+ return;
+ }
+
+ x = (unsigned char *)keyword_norm;
+ bmBc = cond->bmBc;
+ shift = cond->shift;
+
+ /* Restart */
+ p = y + m + cond->found;
+ cp = x + m;
+ ck = cp[-2];
+
+ /* 12 means 1(initial offset) + 10 (in loop) + 1 (shift) */
+ if (n - cond->found > 12 * m) {
+ limit = y + n - 11 * m;
+ while (p <= limit) {
+ p += bmBc[p[-1]];
+ if(!(delta1 = bmBc[p[-1]])) {
+ goto check;
+ }
+ p += delta1;
+ p += bmBc[p[-1]];
+ p += bmBc[p[-1]];
+ if(!(delta1 = bmBc[p[-1]])) {
+ goto check;
+ }
+ p += delta1;
+ p += bmBc[p[-1]];
+ p += bmBc[p[-1]];
+ if(!(delta1 = bmBc[p[-1]])) {
+ goto check;
+ }
+ p += delta1;
+ p += bmBc[p[-1]];
+ p += bmBc[p[-1]];
+ continue;
+ check:
+ GRN_BM_BM_COMPARE;
+ p += shift;
+ }
+ }
+ /* limit check + search */
+ limit = y + n;
+ while(p <= limit) {
+ if (!(delta1 = bmBc[p[-1]])) {
+ GRN_BM_BM_COMPARE;
+ p += shift;
+ }
+ p += delta1;
+ }
+ cond->stopflag = SNIPCOND_STOP;
+}
+
+static size_t
+count_mapped_chars(const char *str, const char *end)
+{
+ const char *p;
+ size_t dl;
+
+ dl = 0;
+ for (p = str; p != end; p++) {
+ switch (*p) {
+ case '<':
+ case '>':
+ dl += 4; /* &lt; or &gt; */
+ break;
+ case '&':
+ dl += 5; /* &amp; */
+ break;
+ case '"':
+ dl += 6; /* &quot; */
+ break;
+ default:
+ dl++;
+ break;
+ }
+ }
+ return dl;
+}
+
+grn_rc
+grn_snip_cond_close(grn_ctx *ctx, snip_cond *cond)
+{
+ if (!cond) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (cond->keyword) {
+ grn_obj_close(ctx, cond->keyword);
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_snip_cond_init(grn_ctx *ctx, snip_cond *sc, const char *keyword, unsigned int keyword_len,
+ grn_encoding enc, grn_obj *normalizer, int flags)
+{
+ const char *norm;
+ unsigned int norm_blen;
+ int f = GRN_STR_REMOVEBLANK;
+ memset(sc, 0, sizeof(snip_cond));
+ if (!(sc->keyword = grn_string_open(ctx, keyword, keyword_len,
+ normalizer, f))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT,
+ "grn_string_open on snip_cond_init failed!");
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ grn_string_get_normalized(ctx, sc->keyword, &norm, &norm_blen, NULL);
+ if (!norm_blen) {
+ grn_snip_cond_close(ctx, sc);
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (norm_blen != 1) {
+ grn_bm_preBmBc((unsigned char *)norm, norm_blen, sc->bmBc);
+ sc->shift = sc->bmBc[(unsigned char)norm[norm_blen - 1]];
+ sc->bmBc[(unsigned char)norm[norm_blen - 1]] = 0;
+ }
+ return GRN_SUCCESS;
+}
+
+void
+grn_snip_cond_reinit(snip_cond *cond)
+{
+ cond->found = 0;
+ cond->last_found = 0;
+ cond->last_offset = 0;
+ cond->start_offset = 0;
+ cond->end_offset = 0;
+
+ cond->count = 0;
+ cond->stopflag = SNIPCOND_NONSTOP;
+}
+
+inline static char *
+grn_snip_strndup(grn_ctx *ctx, const char *string, unsigned int string_len)
+{
+ char *copied_string;
+
+ copied_string = GRN_MALLOC(string_len + 1);
+ if (!copied_string) {
+ return NULL;
+ }
+ memcpy(copied_string, string, string_len);
+ copied_string[string_len]= '\0'; /* not required, but for ql use */
+ return copied_string;
+}
+
+inline static grn_rc
+grn_snip_cond_set_tag(grn_ctx *ctx,
+ const char **dest_tag, size_t *dest_tag_len,
+ const char *tag, unsigned int tag_len,
+ const char *default_tag, unsigned int default_tag_len,
+ int copy_tag)
+{
+ if (tag) {
+ if (copy_tag) {
+ char *copied_tag;
+ copied_tag = grn_snip_strndup(ctx, tag, tag_len);
+ if (!copied_tag) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ *dest_tag = copied_tag;
+ } else {
+ *dest_tag = tag;
+ }
+ *dest_tag_len = tag_len;
+ } else {
+ *dest_tag = default_tag;
+ *dest_tag_len = default_tag_len;
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_snip_set_normalizer(grn_ctx *ctx, grn_obj *snip,
+ grn_obj *normalizer)
+{
+ grn_snip *snip_;
+ if (!snip) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ snip_ = (grn_snip *)snip;
+ snip_->normalizer = normalizer;
+ return GRN_SUCCESS;
+}
+
+grn_obj *
+grn_snip_get_normalizer(grn_ctx *ctx, grn_obj *snip)
+{
+ grn_snip *snip_;
+
+ if (!snip) {
+ return NULL;
+ }
+
+ snip_ = (grn_snip *)snip;
+ return snip_->normalizer;
+}
+
+grn_rc
+grn_snip_add_cond(grn_ctx *ctx, grn_obj *snip,
+ const char *keyword, unsigned int keyword_len,
+ const char *opentag, unsigned int opentag_len,
+ const char *closetag, unsigned int closetag_len)
+{
+ grn_rc rc;
+ int copy_tag;
+ snip_cond *cond;
+ unsigned int norm_blen;
+ grn_snip *snip_;
+
+ snip_ = (grn_snip *)snip;
+ if (!snip_ || !keyword || !keyword_len || snip_->cond_len >= MAX_SNIP_COND_COUNT) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ cond = snip_->cond + snip_->cond_len;
+ if ((rc = grn_snip_cond_init(ctx, cond, keyword, keyword_len,
+ snip_->encoding, snip_->normalizer, snip_->flags))) {
+ return rc;
+ }
+ grn_string_get_normalized(ctx, cond->keyword, NULL, &norm_blen, NULL);
+ if (norm_blen > snip_->width) {
+ grn_snip_cond_close(ctx, cond);
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ copy_tag = snip_->flags & GRN_SNIP_COPY_TAG;
+ rc = grn_snip_cond_set_tag(ctx,
+ &(cond->opentag), &(cond->opentag_len),
+ opentag, opentag_len,
+ snip_->defaultopentag, snip_->defaultopentag_len,
+ copy_tag);
+ if (rc) {
+ grn_snip_cond_close(ctx, cond);
+ return rc;
+ }
+
+ rc = grn_snip_cond_set_tag(ctx,
+ &(cond->closetag), &(cond->closetag_len),
+ closetag, closetag_len,
+ snip_->defaultclosetag, snip_->defaultclosetag_len,
+ copy_tag);
+ if (rc) {
+ if (opentag && copy_tag) {
+ GRN_FREE((void *)cond->opentag);
+ }
+ grn_snip_cond_close(ctx, cond);
+ return rc;
+ }
+
+ snip_->cond_len++;
+ return GRN_SUCCESS;
+}
+
+static size_t
+grn_snip_find_firstbyte(const char *string, grn_encoding encoding, size_t offset,
+ size_t doffset)
+{
+ switch (encoding) {
+ case GRN_ENC_EUC_JP:
+ while (!(grn_bm_check_euc((unsigned char *) string, offset)))
+ offset += doffset;
+ break;
+ case GRN_ENC_SJIS:
+ if (!(grn_bm_check_sjis((unsigned char *) string, offset)))
+ offset += doffset;
+ break;
+ case GRN_ENC_UTF8:
+ while (string[offset] <= (char)0xc0)
+ offset += doffset;
+ break;
+ default:
+ break;
+ }
+ return offset;
+}
+
+inline static grn_rc
+grn_snip_set_default_tag(grn_ctx *ctx,
+ const char **dest_tag, size_t *dest_tag_len,
+ const char *tag, unsigned int tag_len,
+ int copy_tag)
+{
+ if (copy_tag && tag) {
+ char *copied_tag;
+ copied_tag = grn_snip_strndup(ctx, tag, tag_len);
+ if (!copied_tag) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ *dest_tag = copied_tag;
+ } else {
+ *dest_tag = tag;
+ }
+ *dest_tag_len = tag_len;
+ return GRN_SUCCESS;
+}
+
+grn_obj *
+grn_snip_open(grn_ctx *ctx, int flags, unsigned int width,
+ unsigned int max_results,
+ const char *defaultopentag, unsigned int defaultopentag_len,
+ const char *defaultclosetag, unsigned int defaultclosetag_len,
+ grn_snip_mapping *mapping)
+{
+ int copy_tag;
+ grn_snip *ret = NULL;
+ if (!(ret = GRN_MALLOC(sizeof(grn_snip)))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_snip allocation failed on grn_snip_open");
+ return NULL;
+ }
+ if (max_results > MAX_SNIP_RESULT_COUNT || max_results == 0) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "max_results is invalid on grn_snip_open");
+ GRN_FREE(ret);
+ return NULL;
+ }
+ GRN_API_ENTER;
+ ret->encoding = ctx->encoding;
+ ret->flags = flags;
+ ret->width = width;
+ ret->max_results = max_results;
+ ret->defaultopentag = NULL;
+ ret->defaultclosetag = NULL;
+
+ copy_tag = flags & GRN_SNIP_COPY_TAG;
+ if (grn_snip_set_default_tag(ctx,
+ &(ret->defaultopentag),
+ &(ret->defaultopentag_len),
+ defaultopentag, defaultopentag_len,
+ copy_tag)) {
+ GRN_FREE(ret);
+ GRN_API_RETURN(NULL);
+ }
+
+ if (grn_snip_set_default_tag(ctx,
+ &(ret->defaultclosetag),
+ &(ret->defaultclosetag_len),
+ defaultclosetag, defaultclosetag_len,
+ copy_tag)) {
+ if (copy_tag && ret->defaultopentag) {
+ GRN_FREE((void *)ret->defaultopentag);
+ }
+ GRN_FREE(ret);
+ GRN_API_RETURN(NULL);
+ }
+
+ ret->cond_len = 0;
+ ret->mapping = mapping;
+ ret->nstr = NULL;
+ ret->tag_count = 0;
+ ret->snip_count = 0;
+ if (ret->flags & GRN_SNIP_NORMALIZE) {
+ ret->normalizer = GRN_NORMALIZER_AUTO;
+ } else {
+ ret->normalizer = NULL;
+ }
+
+ GRN_DB_OBJ_SET_TYPE(ret, GRN_SNIP);
+ {
+ grn_obj *db;
+ grn_id id;
+ db = grn_ctx_db(ctx);
+ id = grn_obj_register(ctx, db, NULL, 0);
+ DB_OBJ(ret)->header.domain = GRN_ID_NIL;
+ DB_OBJ(ret)->range = GRN_ID_NIL;
+ grn_db_obj_init(ctx, db, id, DB_OBJ(ret));
+ }
+
+ GRN_API_RETURN((grn_obj *)ret);
+}
+
+static grn_rc
+exec_clean(grn_ctx *ctx, grn_snip *snip)
+{
+ snip_cond *cond, *cond_end;
+ if (snip->nstr) {
+ grn_obj_close(ctx, snip->nstr);
+ snip->nstr = NULL;
+ }
+ snip->tag_count = 0;
+ snip->snip_count = 0;
+ for (cond = snip->cond, cond_end = cond + snip->cond_len;
+ cond < cond_end; cond++) {
+ grn_snip_cond_reinit(cond);
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_snip_close(grn_ctx *ctx, grn_snip *snip)
+{
+ snip_cond *cond, *cond_end;
+ if (!snip) { return GRN_INVALID_ARGUMENT; }
+ GRN_API_ENTER;
+ if (snip->flags & GRN_SNIP_COPY_TAG) {
+ int i;
+ snip_cond *sc;
+ const char *dot = snip->defaultopentag, *dct = snip->defaultclosetag;
+ for (i = snip->cond_len, sc = snip->cond; i; i--, sc++) {
+ if (sc->opentag != dot) { GRN_FREE((void *)sc->opentag); }
+ if (sc->closetag != dct) { GRN_FREE((void *)sc->closetag); }
+ }
+ if (dot) { GRN_FREE((void *)dot); }
+ if (dct) { GRN_FREE((void *)dct); }
+ }
+ if (snip->nstr) {
+ grn_obj_close(ctx, snip->nstr);
+ }
+ for (cond = snip->cond, cond_end = cond + snip->cond_len;
+ cond < cond_end; cond++) {
+ grn_snip_cond_close(ctx, cond);
+ }
+ GRN_FREE(snip);
+ GRN_API_RETURN(GRN_SUCCESS);
+}
+
+grn_rc
+grn_snip_exec(grn_ctx *ctx, grn_obj *snip, const char *string, unsigned int string_len,
+ unsigned int *nresults, unsigned int *max_tagged_len)
+{
+ size_t i;
+ grn_snip *snip_;
+ int f = GRN_STR_WITH_CHECKS|GRN_STR_REMOVEBLANK;
+ if (!snip || !string || !nresults || !max_tagged_len) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ GRN_API_ENTER;
+ snip_ = (grn_snip *)snip;
+ exec_clean(ctx, snip_);
+ *nresults = 0;
+ snip_->nstr = grn_string_open(ctx, string, string_len, snip_->normalizer, f);
+ if (!snip_->nstr) {
+ exec_clean(ctx, snip_);
+ GRN_LOG(ctx, GRN_LOG_ALERT, "grn_string_open on grn_snip_exec failed !");
+ GRN_API_RETURN(ctx->rc);
+ }
+ for (i = 0; i < snip_->cond_len; i++) {
+ grn_bm_tunedbm(ctx, snip_->cond + i, snip_->nstr, snip_->flags);
+ }
+
+ {
+ _snip_tag_result *tag_result = snip_->tag_result;
+ _snip_result *snip_result = snip_->snip_result;
+ size_t last_end_offset = 0, last_last_end_offset = 0;
+ unsigned int unfound_cond_count = snip_->cond_len;
+
+ *max_tagged_len = 0;
+ while (1) {
+ size_t tagged_len = 0, last_tag_end = 0;
+ int_least8_t all_stop = 1, found_cond = 0;
+ snip_result->tag_count = 0;
+
+ while (1) {
+ size_t min_start_offset = (size_t) -1;
+ size_t max_end_offset = 0;
+ snip_cond *cond = NULL;
+
+ /* get condition which have minimum offset and is not stopped */
+ for (i = 0; i < snip_->cond_len; i++) {
+ if (snip_->cond[i].stopflag == SNIPCOND_NONSTOP &&
+ (min_start_offset > snip_->cond[i].start_offset ||
+ (min_start_offset == snip_->cond[i].start_offset &&
+ max_end_offset < snip_->cond[i].end_offset))) {
+ min_start_offset = snip_->cond[i].start_offset;
+ max_end_offset = snip_->cond[i].end_offset;
+ cond = &snip_->cond[i];
+ }
+ }
+ if (!cond) {
+ break;
+ }
+ /* check whether condtion is the first condition in snippet */
+ if (snip_result->tag_count == 0) {
+ /* skip condition if the number of rest snippet field is smaller than */
+ /* the number of unfound keywords. */
+ if (snip_->max_results - *nresults <= unfound_cond_count && cond->count > 0) {
+ int_least8_t exclude_other_cond = 1;
+ for (i = 0; i < snip_->cond_len; i++) {
+ if ((snip_->cond + i) != cond
+ && snip_->cond[i].end_offset <= cond->start_offset + snip_->width
+ && snip_->cond[i].count == 0) {
+ exclude_other_cond = 0;
+ }
+ }
+ if (exclude_other_cond) {
+ grn_bm_tunedbm(ctx, cond, snip_->nstr, snip_->flags);
+ continue;
+ }
+ }
+ snip_result->start_offset = cond->start_offset;
+ snip_result->first_tag_result_idx = snip_->tag_count;
+ } else {
+ if (cond->start_offset >= snip_result->start_offset + snip_->width) {
+ break;
+ }
+ /* check nesting to make valid HTML */
+ /* ToDo: allow <test><te>te</te><st>st</st></test> */
+ if (cond->start_offset < last_tag_end) {
+ grn_bm_tunedbm(ctx, cond, snip_->nstr, snip_->flags);
+ continue;
+ }
+ }
+ if (cond->end_offset > snip_result->start_offset + snip_->width) {
+ /* If a keyword gets across a snippet, */
+ /* it was skipped and never to be tagged. */
+ cond->stopflag = SNIPCOND_ACROSS;
+ grn_bm_tunedbm(ctx, cond, snip_->nstr, snip_->flags);
+ } else {
+ found_cond = 1;
+ if (cond->count == 0) {
+ unfound_cond_count--;
+ }
+ cond->count++;
+ last_end_offset = cond->end_offset;
+
+ tag_result->cond = cond;
+ tag_result->start_offset = cond->start_offset;
+ tag_result->end_offset = last_tag_end = cond->end_offset;
+
+ snip_result->tag_count++;
+ tag_result++;
+ tagged_len += cond->opentag_len + cond->closetag_len;
+ if (++snip_->tag_count >= MAX_SNIP_TAG_COUNT) {
+ break;
+ }
+ grn_bm_tunedbm(ctx, cond, snip_->nstr, snip_->flags);
+ }
+ }
+ if (!found_cond) {
+ break;
+ }
+ if (snip_result->start_offset + last_end_offset < snip_->width) {
+ snip_result->start_offset = 0;
+ } else {
+ snip_result->start_offset =
+ MAX(MIN
+ ((snip_result->start_offset + last_end_offset - snip_->width) / 2,
+ string_len - snip_->width), last_last_end_offset);
+ }
+ snip_result->start_offset =
+ grn_snip_find_firstbyte(string, snip_->encoding, snip_result->start_offset, 1);
+
+ snip_result->end_offset = snip_result->start_offset + snip_->width;
+ if (snip_result->end_offset < string_len) {
+ snip_result->end_offset =
+ grn_snip_find_firstbyte(string, snip_->encoding, snip_result->end_offset, -1);
+ } else {
+ snip_result->end_offset = string_len;
+ }
+ last_last_end_offset = snip_result->end_offset;
+
+ if (snip_->mapping == (grn_snip_mapping *) -1) {
+ tagged_len +=
+ count_mapped_chars(&string[snip_result->start_offset],
+ &string[snip_result->end_offset]) + 1;
+ } else {
+ tagged_len += snip_result->end_offset - snip_result->start_offset + 1;
+ }
+
+ *max_tagged_len = MAX(*max_tagged_len, tagged_len);
+
+ snip_result->last_tag_result_idx = snip_->tag_count - 1;
+ (*nresults)++;
+ snip_result++;
+
+ if (*nresults == snip_->max_results || snip_->tag_count == MAX_SNIP_TAG_COUNT) {
+ break;
+ }
+ for (i = 0; i < snip_->cond_len; i++) {
+ if (snip_->cond[i].stopflag != SNIPCOND_STOP) {
+ all_stop = 0;
+ snip_->cond[i].stopflag = SNIPCOND_NONSTOP;
+ }
+ }
+ if (all_stop) {
+ break;
+ }
+ }
+ }
+ snip_->snip_count = *nresults;
+ snip_->string = string;
+
+ snip_->max_tagged_len = *max_tagged_len;
+
+ GRN_API_RETURN(ctx->rc);
+}
+
+grn_rc
+grn_snip_get_result(grn_ctx *ctx, grn_obj *snip, const unsigned int index, char *result, unsigned int *result_len)
+{
+ char *p;
+ size_t i, j, k;
+ _snip_result *sres;
+ grn_snip *snip_;
+
+ snip_ = (grn_snip *)snip;
+ if (snip_->snip_count <= index || !snip_->nstr) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ GRN_ASSERT(snip_->snip_count != 0 && snip_->tag_count != 0);
+
+ GRN_API_ENTER;
+ sres = &snip_->snip_result[index];
+ j = sres->first_tag_result_idx;
+ for (p = result, i = sres->start_offset; i < sres->end_offset; i++) {
+ for (; j <= sres->last_tag_result_idx && snip_->tag_result[j].start_offset == i; j++) {
+ if (snip_->tag_result[j].end_offset > sres->end_offset) {
+ continue;
+ }
+ memcpy(p, snip_->tag_result[j].cond->opentag, snip_->tag_result[j].cond->opentag_len);
+ p += snip_->tag_result[j].cond->opentag_len;
+ }
+
+ if (snip_->mapping == GRN_SNIP_MAPPING_HTML_ESCAPE) {
+ switch (snip_->string[i]) {
+ case '<':
+ *p++ = '&';
+ *p++ = 'l';
+ *p++ = 't';
+ *p++ = ';';
+ break;
+ case '>':
+ *p++ = '&';
+ *p++ = 'g';
+ *p++ = 't';
+ *p++ = ';';
+ break;
+ case '&':
+ *p++ = '&';
+ *p++ = 'a';
+ *p++ = 'm';
+ *p++ = 'p';
+ *p++ = ';';
+ break;
+ case '"':
+ *p++ = '&';
+ *p++ = 'q';
+ *p++ = 'u';
+ *p++ = 'o';
+ *p++ = 't';
+ *p++ = ';';
+ break;
+ default:
+ *p++ = snip_->string[i];
+ break;
+ }
+ } else {
+ *p++ = snip_->string[i];
+ }
+
+ for (k = sres->last_tag_result_idx;
+ snip_->tag_result[k].end_offset <= sres->end_offset; k--) {
+ /* TODO: avoid all loop */
+ if (snip_->tag_result[k].end_offset == i + 1) {
+ memcpy(p, snip_->tag_result[k].cond->closetag,
+ snip_->tag_result[k].cond->closetag_len);
+ p += snip_->tag_result[k].cond->closetag_len;
+ }
+ if (k <= sres->first_tag_result_idx) {
+ break;
+ }
+ };
+ }
+ *p = '\0';
+
+ if(result_len) { *result_len = (unsigned int)(p - result); }
+ GRN_ASSERT((unsigned int)(p - result) <= snip_->max_tagged_len);
+
+ GRN_API_RETURN(ctx->rc);
+}
diff --git a/storage/mroonga/vendor/groonga/lib/snip.h b/storage/mroonga/vendor/groonga/lib/snip.h
new file mode 100644
index 00000000000..7a252921b3a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/snip.h
@@ -0,0 +1,132 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_SNIP_H
+#define GRN_SNIP_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifndef GRN_STR_H
+#include "str.h"
+#endif /* GRN_STR_H */
+
+#include "db.h"
+
+#define ASIZE 256U
+#define MAX_SNIP_TAG_COUNT 512U
+#define MAX_SNIP_COND_COUNT 32U
+#define MAX_SNIP_RESULT_COUNT 16U
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define SNIPCOND_NONSTOP 0
+#define SNIPCOND_STOP 1
+#define SNIPCOND_ACROSS 2
+
+#define GRN_QUERY_SCAN_ALLOCCONDS 0x0002
+
+typedef struct _snip_cond
+{
+ /* initial parameters */
+ const char *opentag;
+ const char *closetag;
+ size_t opentag_len;
+ size_t closetag_len;
+ grn_obj *keyword;
+
+ /* Tuned BM pre */
+ size_t bmBc[ASIZE];
+ size_t shift;
+
+ /* Tuned BM temporal result */
+ size_t found;
+ size_t last_found;
+ size_t last_offset;
+ size_t start_offset;
+ size_t end_offset;
+ size_t found_alpha_head;
+
+ /* search result */
+ int count;
+
+ /* stop flag */
+ int_least8_t stopflag;
+} snip_cond;
+
+typedef struct
+{
+ size_t start_offset;
+ size_t end_offset;
+ snip_cond *cond;
+} _snip_tag_result;
+
+typedef struct
+{
+ size_t start_offset;
+ size_t end_offset;
+ unsigned int first_tag_result_idx;
+ unsigned int last_tag_result_idx;
+ unsigned int tag_count;
+} _snip_result;
+
+typedef struct _grn_snip
+{
+ grn_db_obj obj;
+ grn_encoding encoding;
+ int flags;
+ size_t width;
+ unsigned int max_results;
+ const char *defaultopentag;
+ const char *defaultclosetag;
+ size_t defaultopentag_len;
+ size_t defaultclosetag_len;
+
+ grn_snip_mapping *mapping;
+
+ snip_cond cond[MAX_SNIP_COND_COUNT];
+ unsigned int cond_len;
+
+ unsigned int tag_count;
+ unsigned int snip_count;
+
+ const char *string;
+ grn_obj *nstr;
+
+ _snip_result snip_result[MAX_SNIP_RESULT_COUNT];
+ _snip_tag_result tag_result[MAX_SNIP_TAG_COUNT];
+
+ size_t max_tagged_len;
+
+ grn_obj *normalizer;
+} grn_snip;
+
+grn_rc grn_snip_close(grn_ctx *ctx, grn_snip *snip);
+grn_rc grn_snip_cond_init(grn_ctx *ctx, snip_cond *sc, const char *keyword, unsigned int keyword_len,
+ grn_encoding enc, grn_obj *normalizer, int flags);
+void grn_snip_cond_reinit(snip_cond *cond);
+grn_rc grn_snip_cond_close(grn_ctx *ctx, snip_cond *cond);
+void grn_bm_tunedbm(grn_ctx *ctx, snip_cond *cond, grn_obj *string, int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_SNIP_H */
diff --git a/storage/mroonga/vendor/groonga/lib/sources.am b/storage/mroonga/vendor/groonga/lib/sources.am
new file mode 100644
index 00000000000..9e726b1607b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/sources.am
@@ -0,0 +1,51 @@
+libgroonga_la_SOURCES = \
+ com.c \
+ com.h \
+ ctx.c \
+ ctx.h \
+ ctx_impl.h \
+ ctx_impl_mrb.c \
+ ctx_impl_mrb.h \
+ dat.cpp \
+ dat.h \
+ db.c \
+ db.h \
+ error.c \
+ error.h \
+ expr.c \
+ expr.h \
+ geo.c \
+ geo.h \
+ groonga_in.h \
+ hash.c \
+ hash.h \
+ ii.c \
+ ii.h \
+ io.c \
+ io.h \
+ mrb.c \
+ mrb.h \
+ nfkc.c \
+ normalizer.c \
+ normalizer_in.h \
+ output.c \
+ output.h \
+ pat.c \
+ pat.h \
+ plugin.c \
+ plugin_in.h \
+ proc.c \
+ proc.h \
+ snip.c \
+ snip.h \
+ store.c \
+ store.h \
+ str.c \
+ str.h \
+ string.c \
+ string_in.h \
+ token.c \
+ token.h \
+ tokenizer.c \
+ util.c \
+ util.h
diff --git a/storage/mroonga/vendor/groonga/lib/store.c b/storage/mroonga/vendor/groonga/lib/store.c
new file mode 100644
index 00000000000..041c01ec3f8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/store.c
@@ -0,0 +1,1779 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "groonga_in.h"
+#include "str.h"
+#include "store.h"
+#include "ctx_impl.h"
+#include "output.h"
+#include <string.h>
+
+/* rectangular arrays */
+
+#define GRN_RA_SEGMENT_SIZE (1 << 22)
+
+static grn_ra *
+_grn_ra_create(grn_ctx *ctx, grn_ra *ra, const char *path, unsigned int element_size)
+{
+ grn_io *io;
+ int max_segments, n_elm, w_elm;
+ struct grn_ra_header *header;
+ unsigned int actual_size;
+ if (element_size > GRN_RA_SEGMENT_SIZE) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "element_size too large (%d)", element_size);
+ return NULL;
+ }
+ for (actual_size = 1; actual_size < element_size; actual_size *= 2) ;
+ max_segments = ((GRN_ID_MAX + 1) / GRN_RA_SEGMENT_SIZE) * actual_size;
+ io = grn_io_create(ctx, path, sizeof(struct grn_ra_header),
+ GRN_RA_SEGMENT_SIZE, max_segments, grn_io_auto,
+ GRN_IO_EXPIRE_SEGMENT);
+ if (!io) { return NULL; }
+ header = grn_io_header(io);
+ grn_io_set_type(io, GRN_COLUMN_FIX_SIZE);
+ header->element_size = actual_size;
+ n_elm = GRN_RA_SEGMENT_SIZE / header->element_size;
+ for (w_elm = 22; (1 << w_elm) > n_elm; w_elm--);
+ ra->io = io;
+ ra->header = header;
+ ra->element_mask = n_elm - 1;
+ ra->element_width = w_elm;
+ return ra;
+}
+
+grn_ra *
+grn_ra_create(grn_ctx *ctx, const char *path, unsigned int element_size)
+{
+ grn_ra *ra = NULL;
+ if (!(ra = GRN_GMALLOC(sizeof(grn_ra)))) {
+ return NULL;
+ }
+ GRN_DB_OBJ_SET_TYPE(ra, GRN_COLUMN_FIX_SIZE);
+ if (!_grn_ra_create(ctx, ra, path, element_size)) {
+ GRN_FREE(ra);
+ return NULL;
+ }
+ return ra;
+}
+
+grn_ra *
+grn_ra_open(grn_ctx *ctx, const char *path)
+{
+ grn_io *io;
+ int n_elm, w_elm;
+ grn_ra *ra = NULL;
+ struct grn_ra_header *header;
+ io = grn_io_open(ctx, path, grn_io_auto);
+ if (!io) { return NULL; }
+ header = grn_io_header(io);
+ if (grn_io_get_type(io) != GRN_COLUMN_FIX_SIZE) {
+ ERR(GRN_INVALID_FORMAT, "file type unmatch");
+ grn_io_close(ctx, io);
+ return NULL;
+ }
+ if (!(ra = GRN_GMALLOC(sizeof(grn_ra)))) {
+ grn_io_close(ctx, io);
+ return NULL;
+ }
+ n_elm = GRN_RA_SEGMENT_SIZE / header->element_size;
+ for (w_elm = 22; (1 << w_elm) > n_elm; w_elm--);
+ GRN_DB_OBJ_SET_TYPE(ra, GRN_COLUMN_FIX_SIZE);
+ ra->io = io;
+ ra->header = header;
+ ra->element_mask = n_elm - 1;
+ ra->element_width = w_elm;
+ return ra;
+}
+
+grn_rc
+grn_ra_info(grn_ctx *ctx, grn_ra *ra, unsigned int *element_size)
+{
+ if (!ra) { return GRN_INVALID_ARGUMENT; }
+ if (element_size) { *element_size = ra->header->element_size; }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ra_close(grn_ctx *ctx, grn_ra *ra)
+{
+ grn_rc rc;
+ if (!ra) { return GRN_INVALID_ARGUMENT; }
+ rc = grn_io_close(ctx, ra->io);
+ GRN_GFREE(ra);
+ return rc;
+}
+
+grn_rc
+grn_ra_remove(grn_ctx *ctx, const char *path)
+{
+ if (!path) { return GRN_INVALID_ARGUMENT; }
+ return grn_io_remove(ctx, path);
+}
+
+grn_rc
+grn_ra_truncate(grn_ctx *ctx, grn_ra *ra)
+{
+ grn_rc rc;
+ const char *io_path;
+ char *path;
+ unsigned int element_size;
+ if ((io_path = grn_io_path(ra->io)) && *io_path != '\0') {
+ if (!(path = GRN_STRDUP(io_path))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "cannot duplicate path: <%s>", io_path);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ } else {
+ path = NULL;
+ }
+ element_size = ra->header->element_size;
+ if ((rc = grn_io_close(ctx, ra->io))) { goto exit; }
+ ra->io = NULL;
+ if (path && (rc = grn_io_remove(ctx, path))) { goto exit; }
+ if (!_grn_ra_create(ctx, ra, path, element_size)) {
+ rc = GRN_UNKNOWN_ERROR;
+ }
+exit:
+ if (path) { GRN_FREE(path); }
+ return rc;
+}
+
+void *
+grn_ra_ref(grn_ctx *ctx, grn_ra *ra, grn_id id)
+{
+ void *p = NULL;
+ uint16_t seg;
+ if (id > GRN_ID_MAX) { return NULL; }
+ seg = id >> ra->element_width;
+ GRN_IO_SEG_REF(ra->io, seg, p);
+ if (!p) { return NULL; }
+ return (void *)(((byte *)p) + ((id & ra->element_mask) * ra->header->element_size));
+}
+
+grn_rc
+grn_ra_unref(grn_ctx *ctx, grn_ra *ra, grn_id id)
+{
+ uint16_t seg;
+ if (id > GRN_ID_MAX) { return GRN_INVALID_ARGUMENT; }
+ seg = id >> ra->element_width;
+ GRN_IO_SEG_UNREF(ra->io, seg);
+ return GRN_SUCCESS;
+}
+
+void *
+grn_ra_ref_cache(grn_ctx *ctx, grn_ra *ra, grn_id id, grn_ra_cache *cache)
+{
+ void *p = NULL;
+ uint16_t seg;
+ if (id > GRN_ID_MAX) { return NULL; }
+ seg = id >> ra->element_width;
+ if (seg == cache->seg) {
+ p = cache->p;
+ } else {
+ if (cache->seg != -1) { GRN_IO_SEG_UNREF(ra->io, cache->seg); }
+ GRN_IO_SEG_REF(ra->io, seg, p);
+ cache->seg = seg;
+ cache->p = p;
+ }
+ if (!p) { return NULL; }
+ return (void *)(((byte *)p) + ((id & ra->element_mask) * ra->header->element_size));
+}
+
+grn_rc
+grn_ra_cache_fin(grn_ctx *ctx, grn_ra *ra, grn_id id)
+{
+ uint16_t seg;
+ if (id > GRN_ID_MAX) { return GRN_INVALID_ARGUMENT; }
+ seg = id >> ra->element_width;
+ GRN_IO_SEG_UNREF(ra->io, seg);
+ return GRN_SUCCESS;
+}
+
+/**** jagged arrays ****/
+
+#define GRN_JA_W_SEGREGATE_THRESH_V1 7
+#define GRN_JA_W_SEGREGATE_THRESH_V2 16
+#define GRN_JA_W_CAPACITY 38
+#define GRN_JA_W_SEGMENT 22
+
+#define JA_ESEG_VOID (0xffffffffU)
+#define JA_SEGMENT_SIZE (1U << GRN_JA_W_SEGMENT)
+#define JA_W_EINFO 3
+#define JA_W_SEGMENTS_MAX (GRN_JA_W_CAPACITY - GRN_JA_W_SEGMENT)
+#define JA_W_EINFO_IN_A_SEGMENT (GRN_JA_W_SEGMENT - JA_W_EINFO)
+#define JA_N_EINFO_IN_A_SEGMENT (1U << JA_W_EINFO_IN_A_SEGMENT)
+#define JA_M_EINFO_IN_A_SEGMENT (JA_N_EINFO_IN_A_SEGMENT - 1)
+#define JA_N_GARBAGES_IN_A_SEGMENT ((1U << (GRN_JA_W_SEGMENT - 3)) - 2)
+#define JA_N_ELEMENT_VARIATION_V1 (GRN_JA_W_SEGREGATE_THRESH_V1 - JA_W_EINFO + 1)
+#define JA_N_ELEMENT_VARIATION_V2 (GRN_JA_W_SEGREGATE_THRESH_V2 - JA_W_EINFO + 1)
+#define JA_N_DSEGMENTS (1U << JA_W_SEGMENTS_MAX)
+#define JA_N_ESEGMENTS (1U << (GRN_ID_WIDTH - JA_W_EINFO_IN_A_SEGMENT))
+
+typedef struct _grn_ja_einfo grn_ja_einfo;
+
+struct _grn_ja_einfo {
+ union {
+ struct {
+ uint16_t seg;
+ uint16_t pos;
+ uint16_t size;
+ uint8_t c1;
+ uint8_t c2;
+ } n;
+ struct {
+ uint32_t size;
+ uint16_t seg;
+ uint8_t c1;
+ uint8_t c2;
+ } h;
+ uint8_t c[8];
+ } u;
+};
+
+#define ETINY (0x80)
+#define EHUGE (0x40)
+#define ETINY_P(e) ((e)->u.c[7] & ETINY)
+#define ETINY_ENC(e,_size) ((e)->u.c[7] = (_size) + ETINY)
+#define ETINY_DEC(e,_size) ((_size) = (e)->u.c[7] & ~(ETINY|EHUGE))
+#define EHUGE_P(e) ((e)->u.c[7] & EHUGE)
+#define EHUGE_ENC(e,_seg,_size) do {\
+ (e)->u.h.c1 = 0;\
+ (e)->u.h.c2 = EHUGE;\
+ (e)->u.h.seg = (_seg);\
+ (e)->u.h.size = (_size);\
+} while (0)
+#define EHUGE_DEC(e,_seg,_size) do {\
+ (_seg) = (e)->u.h.seg;\
+ (_size) = (e)->u.h.size;\
+} while (0)
+#define EINFO_ENC(e,_seg,_pos,_size) do {\
+ (e)->u.n.c1 = (_pos) >> 16;\
+ (e)->u.n.c2 = ((_size) >> 16);\
+ (e)->u.n.seg = (_seg);\
+ (e)->u.n.pos = (_pos);\
+ (e)->u.n.size = (_size);\
+} while (0)
+#define EINFO_DEC(e,_seg,_pos,_size) do {\
+ (_seg) = (e)->u.n.seg;\
+ (_pos) = ((e)->u.n.c1 << 16) + (e)->u.n.pos;\
+ (_size) = ((e)->u.n.c2 << 16) + (e)->u.n.size;\
+} while (0)
+
+typedef struct {
+ uint32_t seg;
+ uint32_t pos;
+} ja_pos;
+
+typedef struct {
+ uint32_t head;
+ uint32_t tail;
+ uint32_t nrecs;
+ uint32_t next;
+ ja_pos recs[JA_N_GARBAGES_IN_A_SEGMENT];
+} grn_ja_ginfo;
+
+struct grn_ja_header_v1 {
+ uint32_t flags;
+ uint32_t curr_seg;
+ uint32_t curr_pos;
+ uint32_t max_element_size;
+ ja_pos free_elements[JA_N_ELEMENT_VARIATION_V1];
+ uint32_t garbages[JA_N_ELEMENT_VARIATION_V1];
+ uint32_t ngarbages[JA_N_ELEMENT_VARIATION_V1];
+ uint32_t dsegs[JA_N_DSEGMENTS];
+ uint32_t esegs[JA_N_ESEGMENTS];
+};
+
+struct grn_ja_header_v2 {
+ uint32_t flags;
+ uint32_t curr_seg;
+ uint32_t curr_pos;
+ uint32_t max_element_size;
+ ja_pos free_elements[JA_N_ELEMENT_VARIATION_V2];
+ uint32_t garbages[JA_N_ELEMENT_VARIATION_V2];
+ uint32_t ngarbages[JA_N_ELEMENT_VARIATION_V2];
+ uint32_t dsegs[JA_N_DSEGMENTS];
+ uint32_t esegs[JA_N_ESEGMENTS];
+ uint8_t segregate_threshold;
+ uint8_t n_element_variation;
+};
+
+struct grn_ja_header {
+ uint32_t flags;
+ uint32_t *curr_seg;
+ uint32_t *curr_pos;
+ uint32_t max_element_size;
+ ja_pos *free_elements;
+ uint32_t *garbages;
+ uint32_t *ngarbages;
+ uint32_t *dsegs;
+ uint32_t *esegs;
+ uint8_t segregate_threshold;
+ uint8_t n_element_variation;
+};
+
+#define SEG_SEQ (0x10000000U)
+#define SEG_HUGE (0x20000000U)
+#define SEG_EINFO (0x30000000U)
+#define SEG_GINFO (0x40000000U)
+#define SEG_MASK (0xf0000000U)
+
+#define SEGMENTS_AT(ja,seg) ((ja)->header->dsegs[seg])
+#define SEGMENTS_SEGRE_ON(ja,seg,width) (SEGMENTS_AT(ja,seg) = width)
+#define SEGMENTS_SEQ_ON(ja,seg) (SEGMENTS_AT(ja,seg) = SEG_SEQ)
+#define SEGMENTS_HUGE_ON(ja,seg) (SEGMENTS_AT(ja,seg) = SEG_HUGE)
+#define SEGMENTS_EINFO_ON(ja,seg,lseg) (SEGMENTS_AT(ja,seg) = SEG_EINFO|(lseg))
+#define SEGMENTS_GINFO_ON(ja,seg,width) (SEGMENTS_AT(ja,seg) = SEG_GINFO|(width))
+#define SEGMENTS_OFF(ja,seg) (SEGMENTS_AT(ja,seg) = 0)
+
+grn_bool grn_ja_skip_same_value_put = GRN_TRUE;
+
+static grn_ja *
+_grn_ja_create(grn_ctx *ctx, grn_ja *ja, const char *path,
+ unsigned int max_element_size, uint32_t flags)
+{
+ int i;
+ grn_io *io;
+ struct grn_ja_header *header;
+ struct grn_ja_header_v2 *header_v2;
+ io = grn_io_create(ctx, path, sizeof(struct grn_ja_header_v2),
+ JA_SEGMENT_SIZE, JA_N_DSEGMENTS, grn_io_auto,
+ GRN_IO_EXPIRE_SEGMENT);
+ if (!io) { return NULL; }
+ grn_io_set_type(io, GRN_COLUMN_VAR_SIZE);
+
+ header_v2 = grn_io_header(io);
+ header_v2->flags = flags;
+ header_v2->curr_seg = 0;
+ header_v2->curr_pos = JA_SEGMENT_SIZE;
+ header_v2->max_element_size = max_element_size;
+ for (i = 0; i < JA_N_ESEGMENTS; i++) { header_v2->esegs[i] = JA_ESEG_VOID; }
+ header_v2->segregate_threshold = GRN_JA_W_SEGREGATE_THRESH_V2;
+ header_v2->n_element_variation = JA_N_ELEMENT_VARIATION_V2;
+
+ header = GRN_GMALLOC(sizeof(struct grn_ja_header));
+ if (!header) {
+ grn_io_close(ctx, io);
+ return NULL;
+ }
+ header->flags = header_v2->flags;
+ header->curr_seg = &(header_v2->curr_seg);
+ header->curr_pos = &(header_v2->curr_pos);
+ header->max_element_size = header_v2->max_element_size;
+ header->free_elements = header_v2->free_elements;
+ header->garbages = header_v2->garbages;
+ header->ngarbages = header_v2->ngarbages;
+ header->dsegs = header_v2->dsegs;
+ header->esegs = header_v2->esegs;
+ header->segregate_threshold = header_v2->segregate_threshold;
+ header->n_element_variation = header_v2->n_element_variation;
+
+ ja->io = io;
+ ja->header = header;
+ SEGMENTS_EINFO_ON(ja, 0, 0);
+ header->esegs[0] = 0;
+ return ja;
+}
+
+grn_ja *
+grn_ja_create(grn_ctx *ctx, const char *path, unsigned int max_element_size, uint32_t flags)
+{
+ grn_ja *ja = NULL;
+ if (!(ja = GRN_GMALLOC(sizeof(grn_ja)))) {
+ return NULL;
+ }
+ GRN_DB_OBJ_SET_TYPE(ja, GRN_COLUMN_VAR_SIZE);
+ if (!_grn_ja_create(ctx, ja, path, max_element_size, flags)) {
+ GRN_FREE(ja);
+ return NULL;
+ }
+ return ja;
+}
+
+grn_ja *
+grn_ja_open(grn_ctx *ctx, const char *path)
+{
+ grn_io *io;
+ grn_ja *ja = NULL;
+ struct grn_ja_header *header;
+ struct grn_ja_header_v2 *header_v2;
+ io = grn_io_open(ctx, path, grn_io_auto);
+ if (!io) { return NULL; }
+ header_v2 = grn_io_header(io);
+ if (grn_io_get_type(io) != GRN_COLUMN_VAR_SIZE) {
+ ERR(GRN_INVALID_FORMAT, "file type unmatch");
+ grn_io_close(ctx, io);
+ return NULL;
+ }
+ if (header_v2->segregate_threshold == 0) {
+ header_v2->segregate_threshold = GRN_JA_W_SEGREGATE_THRESH_V1;
+ }
+ if (header_v2->n_element_variation == 0) {
+ header_v2->n_element_variation = JA_N_ELEMENT_VARIATION_V1;
+ }
+ if (!(ja = GRN_GMALLOC(sizeof(grn_ja)))) {
+ grn_io_close(ctx, io);
+ return NULL;
+ }
+ GRN_DB_OBJ_SET_TYPE(ja, GRN_COLUMN_VAR_SIZE);
+ if (!(header = GRN_GMALLOC(sizeof(struct grn_ja_header)))) {
+ grn_io_close(ctx, io);
+ GRN_GFREE(ja);
+ return NULL;
+ }
+
+ header->flags = header_v2->flags;
+ header->curr_seg = &(header_v2->curr_seg);
+ header->curr_pos = &(header_v2->curr_pos);
+ header->max_element_size = header_v2->max_element_size;
+ header->segregate_threshold = header_v2->segregate_threshold;
+ header->n_element_variation = header_v2->n_element_variation;
+ if (header->segregate_threshold == GRN_JA_W_SEGREGATE_THRESH_V1) {
+ struct grn_ja_header_v1 *header_v1 = (struct grn_ja_header_v1 *)header_v2;
+ header->free_elements = header_v1->free_elements;
+ header->garbages = header_v1->garbages;
+ header->ngarbages = header_v1->ngarbages;
+ header->dsegs = header_v1->dsegs;
+ header->esegs = header_v1->esegs;
+ } else {
+ header->free_elements = header_v2->free_elements;
+ header->garbages = header_v2->garbages;
+ header->ngarbages = header_v2->ngarbages;
+ header->dsegs = header_v2->dsegs;
+ header->esegs = header_v2->esegs;
+ }
+
+ ja->io = io;
+ ja->header = header;
+
+ return ja;
+}
+
+grn_rc
+grn_ja_info(grn_ctx *ctx, grn_ja *ja, unsigned int *max_element_size)
+{
+ if (!ja) { return GRN_INVALID_ARGUMENT; }
+ if (max_element_size) { *max_element_size = ja->header->max_element_size; }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ja_close(grn_ctx *ctx, grn_ja *ja)
+{
+ grn_rc rc;
+ if (!ja) { return GRN_INVALID_ARGUMENT; }
+ rc = grn_io_close(ctx, ja->io);
+ GRN_GFREE(ja->header);
+ GRN_GFREE(ja);
+ return rc;
+}
+
+grn_rc
+grn_ja_remove(grn_ctx *ctx, const char *path)
+{
+ if (!path) { return GRN_INVALID_ARGUMENT; }
+ return grn_io_remove(ctx, path);
+}
+
+grn_rc
+grn_ja_truncate(grn_ctx *ctx, grn_ja *ja)
+{
+ grn_rc rc;
+ const char *io_path;
+ char *path;
+ unsigned int max_element_size;
+ uint32_t flags;
+ if ((io_path = grn_io_path(ja->io)) && *io_path != '\0') {
+ if (!(path = GRN_STRDUP(io_path))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE, "cannot duplicate path: <%s>", io_path);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ } else {
+ path = NULL;
+ }
+ max_element_size = ja->header->max_element_size;
+ flags = ja->header->flags;
+ if ((rc = grn_io_close(ctx, ja->io))) { goto exit; }
+ ja->io = NULL;
+ if (path && (rc = grn_io_remove(ctx, path))) { goto exit; }
+ GRN_GFREE(ja->header);
+ if (!_grn_ja_create(ctx, ja, path, max_element_size, flags)) {
+ rc = GRN_UNKNOWN_ERROR;
+ }
+exit:
+ if (path) { GRN_FREE(path); }
+ return rc;
+}
+
+static void *
+grn_ja_ref_raw(grn_ctx *ctx, grn_ja *ja, grn_id id, grn_io_win *iw, uint32_t *value_len)
+{
+ uint32_t pseg = ja->header->esegs[id >> JA_W_EINFO_IN_A_SEGMENT];
+ iw->size = 0;
+ iw->addr = NULL;
+ iw->pseg = pseg;
+ if (pseg != JA_ESEG_VOID) {
+ grn_ja_einfo *einfo = NULL;
+ GRN_IO_SEG_REF(ja->io, pseg, einfo);
+ if (einfo) {
+ grn_ja_einfo *ei = &einfo[id & JA_M_EINFO_IN_A_SEGMENT];
+ if (ETINY_P(ei)) {
+ iw->tiny_p = 1;
+ ETINY_DEC(ei, iw->size);
+ iw->io = ja->io;
+ iw->ctx = ctx;
+ iw->addr = (void *)ei;
+ } else {
+ uint32_t jag, vpos, vsize;
+ iw->tiny_p = 0;
+ if (EHUGE_P(ei)) {
+ EHUGE_DEC(ei, jag, vsize);
+ vpos = 0;
+ } else {
+ EINFO_DEC(ei, jag, vpos, vsize);
+ }
+ grn_io_win_map2(ja->io, ctx, iw, jag, vpos, vsize, grn_io_rdonly);
+ }
+ if (!iw->addr) { GRN_IO_SEG_UNREF(ja->io, pseg); }
+ }
+ }
+ *value_len = iw->size;
+ return iw->addr;
+}
+
+grn_rc
+grn_ja_unref(grn_ctx *ctx, grn_io_win *iw)
+{
+ if (!iw->addr) { return GRN_INVALID_ARGUMENT; }
+ GRN_IO_SEG_UNREF(iw->io, iw->pseg);
+ if (!iw->tiny_p) { grn_io_win_unmap2(iw); }
+ return GRN_SUCCESS;
+}
+
+#define DELETED 0x80000000
+
+static grn_rc
+grn_ja_free(grn_ctx *ctx, grn_ja *ja, grn_ja_einfo *einfo)
+{
+ grn_ja_ginfo *ginfo = NULL;
+ uint32_t seg, pos, element_size, aligned_size, m, *gseg;
+ if (ETINY_P(einfo)) { return GRN_SUCCESS; }
+ if (EHUGE_P(einfo)) {
+ uint32_t n;
+ EHUGE_DEC(einfo, seg, element_size);
+ n = ((element_size + JA_SEGMENT_SIZE - 1) >> GRN_JA_W_SEGMENT);
+ for (; n--; seg++) { SEGMENTS_OFF(ja, seg); }
+ return GRN_SUCCESS;
+ }
+ EINFO_DEC(einfo, seg, pos, element_size);
+ if (!element_size) { return GRN_SUCCESS; }
+ {
+ int es = element_size - 1;
+ GRN_BIT_SCAN_REV(es, m);
+ m++;
+ }
+ if (m > ja->header->segregate_threshold) {
+ byte *addr = NULL;
+ GRN_IO_SEG_REF(ja->io, seg, addr);
+ if (!addr) { return GRN_NO_MEMORY_AVAILABLE; }
+ aligned_size = (element_size + sizeof(grn_id) - 1) & ~(sizeof(grn_id) - 1);
+ *(uint32_t *)(addr + pos - sizeof(grn_id)) = DELETED|aligned_size;
+ if (SEGMENTS_AT(ja, seg) < (aligned_size + sizeof(grn_id)) + SEG_SEQ) {
+ GRN_LOG(ctx, GRN_WARN, "inconsistent ja entry detected (%d > %d)",
+ element_size, SEGMENTS_AT(ja, seg) - SEG_SEQ);
+ }
+ SEGMENTS_AT(ja, seg) -= (aligned_size + sizeof(grn_id));
+ if (SEGMENTS_AT(ja, seg) == SEG_SEQ) {
+ /* reuse the segment */
+ SEGMENTS_OFF(ja, seg);
+ if (seg == *(ja->header->curr_seg)) {
+ *(ja->header->curr_pos) = JA_SEGMENT_SIZE;
+ }
+ }
+ GRN_IO_SEG_UNREF(ja->io, seg);
+ } else {
+ uint32_t lseg = 0, lseg_;
+ gseg = &ja->header->garbages[m - JA_W_EINFO];
+ while ((lseg_ = *gseg)) {
+ if (lseg) { GRN_IO_SEG_UNREF(ja->io, lseg); }
+ GRN_IO_SEG_REF(ja->io, lseg_, ginfo);
+ if (!ginfo) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ lseg = lseg_;
+ if (ginfo->nrecs < JA_N_GARBAGES_IN_A_SEGMENT) { break; }
+ gseg = &ginfo->next;
+ }
+ if (!lseg_) {
+ uint32_t i = 0;
+ while (SEGMENTS_AT(ja, i)) {
+ if (++i >= JA_N_DSEGMENTS) {
+ if (lseg) { GRN_IO_SEG_UNREF(ja->io, lseg); }
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ SEGMENTS_GINFO_ON(ja, i, m - JA_W_EINFO);
+ *gseg = i;
+ lseg_ = *gseg;
+ if (lseg) { GRN_IO_SEG_UNREF(ja->io, lseg); }
+ GRN_IO_SEG_REF(ja->io, lseg_, ginfo);
+ lseg = lseg_;
+ if (!ginfo) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ ginfo->head = 0;
+ ginfo->tail = 0;
+ ginfo->nrecs = 0;
+ ginfo->next = 0;
+ }
+ ginfo->recs[ginfo->head].seg = seg;
+ ginfo->recs[ginfo->head].pos = pos;
+ if (++ginfo->head == JA_N_GARBAGES_IN_A_SEGMENT) { ginfo->head = 0; }
+ ginfo->nrecs++;
+ ja->header->ngarbages[m - JA_W_EINFO]++;
+ if (lseg) { GRN_IO_SEG_UNREF(ja->io, lseg); }
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ja_replace(grn_ctx *ctx, grn_ja *ja, grn_id id,
+ grn_ja_einfo *ei, uint64_t *cas)
+{
+ grn_rc rc = GRN_SUCCESS;
+ uint32_t lseg, *pseg, pos;
+ grn_ja_einfo *einfo = NULL, eback;
+ lseg = id >> JA_W_EINFO_IN_A_SEGMENT;
+ pos = id & JA_M_EINFO_IN_A_SEGMENT;
+ pseg = &ja->header->esegs[lseg];
+ if (grn_io_lock(ctx, ja->io, grn_lock_timeout)) {
+ return ctx->rc;
+ }
+ if (*pseg == JA_ESEG_VOID) {
+ int i = 0;
+ while (SEGMENTS_AT(ja, i)) {
+ if (++i >= JA_N_DSEGMENTS) {
+ ERR(GRN_NOT_ENOUGH_SPACE, "grn_ja file (%s) is full", ja->io->path);
+ rc = GRN_NOT_ENOUGH_SPACE;
+ goto exit;
+ }
+ }
+ SEGMENTS_EINFO_ON(ja, i, lseg);
+ GRN_IO_SEG_REF(ja->io, i, einfo);
+ if (einfo) {
+ *pseg = i;
+ memset(einfo, 0, JA_SEGMENT_SIZE);
+ }
+ } else {
+ GRN_IO_SEG_REF(ja->io, *pseg, einfo);
+ }
+ if (!einfo) {
+ rc = GRN_NO_MEMORY_AVAILABLE;
+ goto exit;
+ }
+ eback = einfo[pos];
+ if (cas && *cas != *((uint64_t *)&eback)) {
+ ERR(GRN_CAS_ERROR, "cas failed (%d)", id);
+ GRN_IO_SEG_UNREF(ja->io, *pseg);
+ rc = GRN_CAS_ERROR;
+ goto exit;
+ }
+ // smb_wmb();
+ {
+ uint64_t *location = (uint64_t *)(einfo + pos);
+ uint64_t value = *((uint64_t *)ei);
+ GRN_SET_64BIT(location, value);
+ }
+ GRN_IO_SEG_UNREF(ja->io, *pseg);
+ grn_ja_free(ctx, ja, &eback);
+exit :
+ grn_io_unlock(ja->io);
+ return rc;
+}
+
+#define JA_N_GARBAGES_TH 10
+
+// todo : grn_io_win_map2 cause verbose copy when nseg > 1, it should be copied directly.
+static grn_rc
+grn_ja_alloc(grn_ctx *ctx, grn_ja *ja, grn_id id,
+ uint32_t element_size, grn_ja_einfo *einfo, grn_io_win *iw)
+{
+ byte *addr = NULL;
+ iw->io = ja->io;
+ iw->ctx = ctx;
+ iw->cached = 1;
+ if (element_size < 8) {
+ ETINY_ENC(einfo, element_size);
+ iw->tiny_p = 1;
+ iw->addr = (void *)einfo;
+ return GRN_SUCCESS;
+ }
+ iw->tiny_p = 0;
+ if (grn_io_lock(ctx, ja->io, grn_lock_timeout)) { return ctx->rc; }
+ if (element_size + sizeof(grn_id) > JA_SEGMENT_SIZE) {
+ int i, j, n = (element_size + JA_SEGMENT_SIZE - 1) >> GRN_JA_W_SEGMENT;
+ for (i = 0, j = -1; i < JA_N_DSEGMENTS; i++) {
+ if (SEGMENTS_AT(ja, i)) {
+ j = i;
+ } else {
+ if (i == j + n) {
+ j++;
+ addr = grn_io_win_map2(ja->io, ctx, iw, j, 0, element_size, grn_io_wronly);
+ if (!addr) {
+ grn_io_unlock(ja->io);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ EHUGE_ENC(einfo, j, element_size);
+ for (; j <= i; j++) { SEGMENTS_HUGE_ON(ja, j); }
+ grn_io_unlock(ja->io);
+ return GRN_SUCCESS;
+ }
+ }
+ }
+ GRN_LOG(ctx, GRN_LOG_CRIT, "ja full. requested element_size=%d.", element_size);
+ grn_io_unlock(ja->io);
+ return GRN_NO_MEMORY_AVAILABLE;
+ } else {
+ ja_pos *vp;
+ int m, aligned_size, es = element_size - 1;
+ GRN_BIT_SCAN_REV(es, m);
+ m++;
+ if (m > ja->header->segregate_threshold) {
+ uint32_t seg = *(ja->header->curr_seg);
+ uint32_t pos = *(ja->header->curr_pos);
+ if (pos + element_size + sizeof(grn_id) > JA_SEGMENT_SIZE) {
+ seg = 0;
+ while (SEGMENTS_AT(ja, seg)) {
+ if (++seg >= JA_N_DSEGMENTS) {
+ grn_io_unlock(ja->io);
+ GRN_LOG(ctx, GRN_LOG_CRIT, "ja full. seg=%d.", seg);
+ return GRN_NOT_ENOUGH_SPACE;
+ }
+ }
+ SEGMENTS_SEQ_ON(ja, seg);
+ *(ja->header->curr_seg) = seg;
+ pos = 0;
+ }
+ GRN_IO_SEG_REF(ja->io, seg, addr);
+ if (!addr) {
+ grn_io_unlock(ja->io);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ *(grn_id *)(addr + pos) = id;
+ aligned_size = (element_size + sizeof(grn_id) - 1) & ~(sizeof(grn_id) - 1);
+ if (pos + aligned_size < JA_SEGMENT_SIZE) {
+ *(grn_id *)(addr + pos + aligned_size) = GRN_ID_NIL;
+ }
+ SEGMENTS_AT(ja, seg) += aligned_size + sizeof(grn_id);
+ pos += sizeof(grn_id);
+ EINFO_ENC(einfo, seg, pos, element_size);
+ iw->segment = seg;
+ iw->addr = addr + pos;
+ *(ja->header->curr_pos) = pos + aligned_size;
+ grn_io_unlock(ja->io);
+ return GRN_SUCCESS;
+ } else {
+ uint32_t lseg = 0, lseg_;
+ aligned_size = 1 << m;
+ if (ja->header->ngarbages[m - JA_W_EINFO] > JA_N_GARBAGES_TH) {
+ grn_ja_ginfo *ginfo = NULL;
+ uint32_t seg, pos, *gseg;
+ gseg = &ja->header->garbages[m - JA_W_EINFO];
+ while ((lseg_ = *gseg)) {
+ GRN_IO_SEG_REF(ja->io, lseg_, ginfo);
+ if (!ginfo) {
+ if (lseg) { GRN_IO_SEG_UNREF(ja->io, lseg); }
+ grn_io_unlock(ja->io);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ if (ginfo->next || ginfo->nrecs > JA_N_GARBAGES_TH) {
+ seg = ginfo->recs[ginfo->tail].seg;
+ pos = ginfo->recs[ginfo->tail].pos;
+ GRN_IO_SEG_REF(ja->io, seg, addr);
+ if (!addr) {
+ if (lseg) { GRN_IO_SEG_UNREF(ja->io, lseg); }
+ GRN_IO_SEG_UNREF(ja->io, lseg_);
+ grn_io_unlock(ja->io);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ EINFO_ENC(einfo, seg, pos, element_size);
+ iw->segment = seg;
+ iw->addr = addr + pos;
+ if (++ginfo->tail == JA_N_GARBAGES_IN_A_SEGMENT) { ginfo->tail = 0; }
+ ginfo->nrecs--;
+ ja->header->ngarbages[m - JA_W_EINFO]--;
+ if (!ginfo->nrecs) {
+ SEGMENTS_OFF(ja, *gseg);
+ *gseg = ginfo->next;
+ }
+ if (lseg) { GRN_IO_SEG_UNREF(ja->io, lseg); }
+ GRN_IO_SEG_UNREF(ja->io, lseg_);
+ grn_io_unlock(ja->io);
+ return GRN_SUCCESS;
+ }
+ if (lseg) { GRN_IO_SEG_UNREF(ja->io, lseg); }
+ if (!ginfo->next) {
+ GRN_IO_SEG_UNREF(ja->io, lseg_);
+ break;
+ }
+ lseg = lseg_;
+ gseg = &ginfo->next;
+ }
+ }
+ vp = &ja->header->free_elements[m - JA_W_EINFO];
+ if (!vp->seg) {
+ int i = 0;
+ while (SEGMENTS_AT(ja, i)) {
+ if (++i >= JA_N_DSEGMENTS) {
+ grn_io_unlock(ja->io);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ SEGMENTS_SEGRE_ON(ja, i, m);
+ vp->seg = i;
+ vp->pos = 0;
+ }
+ }
+ EINFO_ENC(einfo, vp->seg, vp->pos, element_size);
+ GRN_IO_SEG_REF(ja->io, vp->seg, addr);
+ if (!addr) {
+ grn_io_unlock(ja->io);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ iw->segment = vp->seg;
+ iw->addr = addr + vp->pos;
+ if ((vp->pos += aligned_size) == JA_SEGMENT_SIZE) {
+ vp->seg = 0;
+ vp->pos = 0;
+ }
+ grn_io_unlock(ja->io);
+ return GRN_SUCCESS;
+ }
+}
+
+static grn_rc
+set_value(grn_ctx *ctx, grn_ja *ja, grn_id id, void *value, uint32_t value_len,
+ grn_ja_einfo *einfo)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_io_win iw;
+ if ((ja->header->flags & GRN_OBJ_RING_BUFFER) &&
+ value_len >= ja->header->max_element_size) {
+ if ((rc = grn_ja_alloc(ctx, ja, id, value_len + sizeof(uint32_t), einfo, &iw))) {
+ return rc;
+ }
+ memcpy(iw.addr, value, value_len);
+ memset((byte *)iw.addr + value_len, 0, sizeof(uint32_t));
+ grn_io_win_unmap2(&iw);
+ } else {
+ if ((rc = grn_ja_alloc(ctx, ja, id, value_len, einfo, &iw))) { return rc; }
+ memcpy(iw.addr, value, value_len);
+ grn_io_win_unmap2(&iw);
+ }
+ return rc;
+}
+
+grn_rc
+grn_ja_put_raw(grn_ctx *ctx, grn_ja *ja, grn_id id,
+ void *value, uint32_t value_len, int flags, uint64_t *cas)
+{
+ int rc;
+ int64_t buf;
+ grn_io_win iw;
+ grn_ja_einfo einfo;
+
+ if (grn_ja_skip_same_value_put &&
+ (flags & GRN_OBJ_SET_MASK) == GRN_OBJ_SET &&
+ value_len > 0) {
+ grn_io_win jw;
+ uint32_t old_len;
+ void *old_value;
+ grn_bool same_value = GRN_FALSE;
+
+ old_value = grn_ja_ref(ctx, ja, id, &jw, &old_len);
+ if (value_len == old_len && memcmp(value, old_value, value_len) == 0) {
+ same_value = GRN_TRUE;
+ }
+ grn_ja_unref(ctx, &jw);
+ if (same_value) {
+ return GRN_SUCCESS;
+ }
+ }
+
+ switch (flags & GRN_OBJ_SET_MASK) {
+ case GRN_OBJ_APPEND :
+ if (value_len) {
+ grn_io_win jw;
+ uint32_t old_len;
+ void *oldvalue = grn_ja_ref(ctx, ja, id, &jw, &old_len);
+ if (oldvalue) {
+ if ((ja->header->flags & GRN_OBJ_RING_BUFFER) &&
+ old_len + value_len >= ja->header->max_element_size) {
+ if (old_len >= ja->header->max_element_size) {
+ byte *b = oldvalue;
+ uint32_t el = old_len - sizeof(uint32_t);
+ uint32_t pos = *((uint32_t *)(b + el));
+ GRN_ASSERT(pos < el);
+ if (el <= pos + value_len) {
+ uint32_t rest = el - pos;
+ memcpy(b + pos, value, rest);
+ memcpy(b, (byte *)value + rest, value_len - rest);
+ *((uint32_t *)(b + el)) = value_len - rest;
+ } else {
+ memcpy(b + pos, value, value_len);
+ *((uint32_t *)(b + el)) = pos + value_len;
+ }
+ return GRN_SUCCESS;
+ } else {
+ if ((rc = grn_ja_alloc(ctx, ja, id,
+ value_len + old_len + sizeof(uint32_t),
+ &einfo, &iw))) {
+ grn_ja_unref(ctx, &jw);
+ return rc;
+ }
+ memcpy(iw.addr, oldvalue, old_len);
+ memcpy((byte *)iw.addr + old_len, value, value_len);
+ memset((byte *)iw.addr + old_len + value_len, 0, sizeof(uint32_t));
+ grn_io_win_unmap2(&iw);
+ }
+ } else {
+ if ((rc = grn_ja_alloc(ctx, ja, id, value_len + old_len, &einfo, &iw))) {
+ grn_ja_unref(ctx, &jw);
+ return rc;
+ }
+ memcpy(iw.addr, oldvalue, old_len);
+ memcpy((byte *)iw.addr + old_len, value, value_len);
+ grn_io_win_unmap2(&iw);
+ }
+ grn_ja_unref(ctx, &jw);
+ } else {
+ set_value(ctx, ja, id, value, value_len, &einfo);
+ }
+ }
+ break;
+ case GRN_OBJ_PREPEND :
+ if (value_len) {
+ grn_io_win jw;
+ uint32_t old_len;
+ void *oldvalue = grn_ja_ref(ctx, ja, id, &jw, &old_len);
+ if (oldvalue) {
+ if ((ja->header->flags & GRN_OBJ_RING_BUFFER) &&
+ old_len + value_len >= ja->header->max_element_size) {
+ if (old_len >= ja->header->max_element_size) {
+ byte *b = oldvalue;
+ uint32_t el = old_len - sizeof(uint32_t);
+ uint32_t pos = *((uint32_t *)(b + el));
+ GRN_ASSERT(pos < el);
+ if (pos < value_len) {
+ uint32_t rest = value_len - pos;
+ memcpy(b, (byte *)value + rest, pos);
+ memcpy(b + el - rest, value, rest);
+ *((uint32_t *)(b + el)) = el - rest;
+ } else {
+ memcpy(b + pos - value_len, value, value_len);
+ *((uint32_t *)(b + el)) = pos - value_len;
+ }
+ return GRN_SUCCESS;
+ } else {
+ if ((rc = grn_ja_alloc(ctx, ja, id,
+ value_len + old_len + sizeof(uint32_t),
+ &einfo, &iw))) {
+ grn_ja_unref(ctx, &jw);
+ return rc;
+ }
+ memcpy(iw.addr, value, value_len);
+ memcpy((byte *)iw.addr + value_len, oldvalue, old_len);
+ memset((byte *)iw.addr + value_len + old_len, 0, sizeof(uint32_t));
+ grn_io_win_unmap2(&iw);
+ }
+ } else {
+ if ((rc = grn_ja_alloc(ctx, ja, id, value_len + old_len, &einfo, &iw))) {
+ grn_ja_unref(ctx, &jw);
+ return rc;
+ }
+ memcpy(iw.addr, value, value_len);
+ memcpy((byte *)iw.addr + value_len, oldvalue, old_len);
+ grn_io_win_unmap2(&iw);
+ }
+ grn_ja_unref(ctx, &jw);
+ } else {
+ set_value(ctx, ja, id, value, value_len, &einfo);
+ }
+ }
+ break;
+ case GRN_OBJ_DECR :
+ if (value_len == sizeof(int64_t)) {
+ int64_t *v = (int64_t *)&buf;
+ *v = -(*(int64_t *)value);
+ value = v;
+ } else if (value_len == sizeof(int32_t)) {
+ int32_t *v = (int32_t *)&buf;
+ *v = -(*(int32_t *)value);
+ value = v;
+ } else {
+ return GRN_INVALID_ARGUMENT;
+ }
+ /* fallthru */
+ case GRN_OBJ_INCR :
+ {
+ grn_io_win jw;
+ uint32_t old_len;
+ void *oldvalue = grn_ja_ref(ctx, ja, id, &jw, &old_len);
+ if (oldvalue && old_len) {
+ grn_rc rc = GRN_INVALID_ARGUMENT;
+ if (old_len == sizeof(int64_t) && value_len == sizeof(int64_t)) {
+ (*(int64_t *)oldvalue) += (*(int64_t *)value);
+ rc = GRN_SUCCESS;
+ } else if (old_len == sizeof(int32_t) && value_len == sizeof(int32_t)) {
+ (*(int32_t *)oldvalue) += (*(int32_t *)value);
+ rc = GRN_SUCCESS;
+ }
+ grn_ja_unref(ctx, &jw);
+ return rc;
+ }
+ }
+ /* fallthru */
+ case GRN_OBJ_SET :
+ if (value_len) {
+ set_value(ctx, ja, id, value, value_len, &einfo);
+ } else {
+ memset(&einfo, 0, sizeof(grn_ja_einfo));
+ }
+ break;
+ default :
+ ERR(GRN_INVALID_ARGUMENT, "grn_ja_put_raw called with illegal flags value");
+ return GRN_INVALID_ARGUMENT;
+ }
+ if ((rc = grn_ja_replace(ctx, ja, id, &einfo, cas))) {
+ if (!grn_io_lock(ctx, ja->io, grn_lock_timeout)) {
+ grn_ja_free(ctx, ja, &einfo);
+ grn_io_unlock(ja->io);
+ }
+ }
+ return rc;
+}
+
+grn_rc
+grn_ja_putv(grn_ctx *ctx, grn_ja *ja, grn_id id, grn_obj *vector, int flags)
+{
+ grn_obj header, footer;
+ grn_rc rc = GRN_SUCCESS;
+ grn_section *vp;
+ int i, f = 0, n = grn_vector_size(ctx, vector);
+ GRN_TEXT_INIT(&header, 0);
+ GRN_TEXT_INIT(&footer, 0);
+ grn_text_benc(ctx, &header, n);
+ for (i = 0, vp = vector->u.v.sections; i < n; i++, vp++) {
+ grn_text_benc(ctx, &header, vp->length);
+ if (vp->weight || vp->domain) { f = 1; }
+ }
+ if (f) {
+ for (i = 0, vp = vector->u.v.sections; i < n; i++, vp++) {
+ grn_text_benc(ctx, &footer, vp->weight);
+ grn_text_benc(ctx, &footer, vp->domain);
+ }
+ }
+ {
+ grn_io_win iw;
+ grn_ja_einfo einfo;
+ grn_obj *body = vector->u.v.body;
+ size_t sizeh = GRN_BULK_VSIZE(&header);
+ size_t sizev = body ? GRN_BULK_VSIZE(body) : 0;
+ size_t sizef = GRN_BULK_VSIZE(&footer);
+ if ((rc = grn_ja_alloc(ctx, ja, id, sizeh + sizev + sizef, &einfo, &iw))) { goto exit; }
+ memcpy(iw.addr, GRN_BULK_HEAD(&header), sizeh);
+ if (body) { memcpy((char *)iw.addr + sizeh, GRN_BULK_HEAD(body), sizev); }
+ if (f) { memcpy((char *)iw.addr + sizeh + sizev, GRN_BULK_HEAD(&footer), sizef); }
+ grn_io_win_unmap2(&iw);
+ rc = grn_ja_replace(ctx, ja, id, &einfo, NULL);
+ }
+exit :
+ GRN_OBJ_FIN(ctx, &footer);
+ GRN_OBJ_FIN(ctx, &header);
+ return rc;
+}
+
+uint32_t
+grn_ja_size(grn_ctx *ctx, grn_ja *ja, grn_id id)
+{
+ grn_ja_einfo *einfo = NULL, *ei;
+ uint32_t lseg, *pseg, pos, size;
+ lseg = id >> JA_W_EINFO_IN_A_SEGMENT;
+ pos = id & JA_M_EINFO_IN_A_SEGMENT;
+ pseg = &ja->header->esegs[lseg];
+ if (*pseg == JA_ESEG_VOID) {
+ ctx->rc = GRN_INVALID_ARGUMENT;
+ return 0;
+ }
+ GRN_IO_SEG_REF(ja->io, *pseg, einfo);
+ if (!einfo) {
+ ctx->rc = GRN_NO_MEMORY_AVAILABLE;
+ return 0;
+ }
+ ei = &einfo[pos];
+ if (ETINY_P(ei)) {
+ ETINY_DEC(ei, size);
+ } else {
+ if (EHUGE_P(ei)) {
+ size = ei->u.h.size;
+ } else {
+ size = (ei->u.n.c2 << 16) + ei->u.n.size;
+ }
+ }
+ GRN_IO_SEG_UNREF(ja->io, *pseg);
+ return size;
+}
+
+grn_rc
+grn_ja_element_info(grn_ctx *ctx, grn_ja *ja, grn_id id,
+ uint64_t *cas, uint32_t *pos, uint32_t *size)
+{
+ uint32_t pseg = ja->header->esegs[id >> JA_W_EINFO_IN_A_SEGMENT];
+ if (pseg == JA_ESEG_VOID) {
+ return GRN_INVALID_ARGUMENT;
+ } else {
+ grn_ja_einfo *einfo = NULL;
+ GRN_IO_SEG_REF(ja->io, pseg, einfo);
+ if (einfo) {
+ grn_ja_einfo *ei;
+ *cas = *((uint64_t *)&einfo[id & JA_M_EINFO_IN_A_SEGMENT]);
+ ei = (grn_ja_einfo *)cas;
+ if (ETINY_P(ei)) {
+ ETINY_DEC(ei, *size);
+ *pos = 0;
+ } else {
+ uint32_t jag;
+ if (EHUGE_P(ei)) {
+ EHUGE_DEC(ei, jag, *size);
+ *pos = 0;
+ } else {
+ EINFO_DEC(ei, jag, *pos, *size);
+ }
+ }
+ GRN_IO_SEG_UNREF(ja->io, pseg);
+ } else {
+ return GRN_INVALID_ARGUMENT;
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+#ifdef GRN_WITH_ZLIB
+#include <zlib.h>
+
+static void *
+grn_ja_ref_zlib(grn_ctx *ctx, grn_ja *ja, grn_id id, grn_io_win *iw, uint32_t *value_len)
+{
+ /* TODO: This function leaks a memory. The return value
+ * must be freed. */
+ z_stream zstream;
+ void *value, *zvalue;
+ uint32_t zvalue_len;
+ if (!(zvalue = grn_ja_ref_raw(ctx, ja, id, iw, &zvalue_len))) {
+ *value_len = 0;
+ return NULL;
+ }
+ zstream.next_in = (Bytef *)(((uint64_t *)zvalue) + 1);
+ zstream.avail_in = zvalue_len + sizeof(uint64_t);
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ if (inflateInit2(&zstream, 15 /* windowBits */) != Z_OK) {
+ *value_len = 0;
+ return NULL;
+ }
+ if (!(value = GRN_MALLOC(*((uint64_t *)zvalue)))) {
+ inflateEnd(&zstream);
+ *value_len = 0;
+ return NULL;
+ }
+ zstream.next_out = (Bytef *)value;
+ zstream.avail_out = *(uint64_t *)zvalue;
+ if (inflate(&zstream, Z_FINISH) != Z_STREAM_END) {
+ inflateEnd(&zstream);
+ GRN_FREE(value);
+ *value_len = 0;
+ return NULL;
+ }
+ *value_len = zstream.total_out;
+ if (inflateEnd(&zstream) != Z_OK) {
+ GRN_FREE(value);
+ *value_len = 0;
+ return NULL;
+ }
+ return value;
+}
+#endif /* GRN_WITH_ZLIB */
+
+#ifdef GRN_WITH_LZO
+#include <lzo/lzo1x.h>
+
+static void *
+grn_ja_ref_lzo(grn_ctx *ctx, grn_ja *ja, grn_id id, grn_io_win *iw, uint32_t *value_len)
+{
+ /* TODO: This function leaks a memory. The return value
+ * must be freed. */
+ void *value, *lvalue;
+ uint32_t lvalue_len;
+ lzo_uint lout_len;
+ if (!(lvalue = grn_ja_ref_raw(ctx, ja, id, iw, &lvalue_len))) {
+ *value_len = 0;
+ return NULL;
+ }
+ if (!(value = GRN_MALLOC(*((uint64_t *)lvalue)))) {
+ *value_len = 0;
+ return NULL;
+ }
+ lout_len = *((uint64_t *)lvalue);
+ switch (lzo1x_decompress((lzo_bytep)(((uint64_t *)lvalue) + 1),
+ lvalue_len,
+ (lzo_bytep)(value),
+ &lout_len,
+ NULL)) {
+ case LZO_E_OK :
+ case LZO_E_INPUT_NOT_CONSUMED :
+ break;
+ default :
+ GRN_FREE(value);
+ *value_len = 0;
+ return NULL;
+ }
+ *value_len = lout_len;
+ return value;
+}
+#endif /* GRN_WITH_LZO */
+
+void *
+grn_ja_ref(grn_ctx *ctx, grn_ja *ja, grn_id id, grn_io_win *iw, uint32_t *value_len)
+{
+#ifdef GRN_WITH_ZLIB
+ if (ja->header->flags & GRN_OBJ_COMPRESS_ZLIB) {
+ return grn_ja_ref_zlib(ctx, ja, id, iw, value_len);
+ }
+#endif /* GRN_WITH_ZLIB */
+#ifdef GRN_WITH_LZO
+ if (ja->header->flags & GRN_OBJ_COMPRESS_LZO) {
+ return grn_ja_ref_lzo(ctx, ja, id, iw, value_len);
+ }
+#endif /* GRN_WITH_LZO */
+ return grn_ja_ref_raw(ctx, ja, id, iw, value_len);
+}
+
+grn_obj *
+grn_ja_get_value(grn_ctx *ctx, grn_ja *ja, grn_id id, grn_obj *value)
+{
+ void *v;
+ uint32_t len;
+ grn_io_win iw;
+ if (!value) {
+ if (!(value = grn_obj_open(ctx, GRN_BULK, 0, 0))) {
+ ERR(GRN_INVALID_ARGUMENT, "grn_obj_get_value failed");
+ goto exit;
+ }
+ }
+ if ((v = grn_ja_ref(ctx, ja, id, &iw, &len))) {
+ if ((ja->header->flags & GRN_OBJ_RING_BUFFER) &&
+ len > ja->header->max_element_size) {
+ byte *b = v;
+ uint32_t el = len - sizeof(uint32_t);
+ uint32_t pos = *((uint32_t *)(b + el));
+ GRN_ASSERT(pos < el);
+ grn_bulk_write(ctx, value, (char *)(b + pos), el - pos);
+ grn_bulk_write(ctx, value, (char *)(b), pos);
+ } else {
+ grn_bulk_write(ctx, value, v, len);
+ }
+ grn_ja_unref(ctx, &iw);
+ }
+exit :
+ return value;
+}
+
+#ifdef GRN_WITH_ZLIB
+inline static grn_rc
+grn_ja_put_zlib(grn_ctx *ctx, grn_ja *ja, grn_id id,
+ void *value, uint32_t value_len, int flags, uint64_t *cas)
+{
+ grn_rc rc;
+ z_stream zstream;
+ void *zvalue;
+ int zvalue_len;
+
+ if (value_len == 0) {
+ return grn_ja_put_raw(ctx, ja, id, value, value_len, flags, cas);
+ }
+
+ zstream.next_in = value;
+ zstream.avail_in = value_len;
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ if (deflateInit2(&zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
+ 15 /* windowBits */,
+ 8 /* memLevel */,
+ Z_DEFAULT_STRATEGY) != Z_OK) {
+ ERR(GRN_ZLIB_ERROR, "deflateInit2 failed");
+ return ctx->rc;
+ }
+ zvalue_len = deflateBound(&zstream, value_len);
+ if (!(zvalue = GRN_MALLOC(zvalue_len + sizeof(uint64_t)))) { deflateEnd(&zstream); return GRN_NO_MEMORY_AVAILABLE; }
+ zstream.next_out = (Bytef *)(((uint64_t *)zvalue) + 1);
+ zstream.avail_out = zvalue_len;
+ if (deflate(&zstream, Z_FINISH) != Z_STREAM_END) {
+ deflateEnd(&zstream);
+ GRN_FREE(zvalue);
+ ERR(GRN_ZLIB_ERROR, "deflate failed");
+ return ctx->rc;
+ }
+ zvalue_len = zstream.total_out;
+ if (deflateEnd(&zstream) != Z_OK) {
+ GRN_FREE(zvalue);
+ ERR(GRN_ZLIB_ERROR, "deflateEnd failed");
+ return ctx->rc;
+ }
+ *(uint64_t *)zvalue = value_len;
+ rc = grn_ja_put_raw(ctx, ja, id, zvalue, zvalue_len + sizeof(uint64_t), flags, cas);
+ GRN_FREE(zvalue);
+ return rc;
+}
+#endif /* GRN_WITH_ZLIB */
+
+#ifdef GRN_WITH_LZO
+inline static grn_rc
+grn_ja_put_lzo(grn_ctx *ctx, grn_ja *ja, grn_id id,
+ void *value, uint32_t value_len, int flags, uint64_t *cas)
+{
+ grn_rc rc;
+ void *lvalue, *lwork;
+ lzo_uint lvalue_len = value_len + value_len / 16 + 64 + 3;
+
+ if (value_len == 0) {
+ return grn_ja_put_raw(ctx, ja, id, value, value_len, flags, cas);
+ }
+
+ if (!(lvalue = GRN_MALLOC(lvalue_len + sizeof(uint64_t)))) { return GRN_NO_MEMORY_AVAILABLE; }
+ if (!(lwork = GRN_MALLOC(LZO1X_1_MEM_COMPRESS))) { GRN_FREE(lvalue); return GRN_NO_MEMORY_AVAILABLE; }
+ if (lzo1x_1_compress(value, value_len, (lzo_bytep)((uint64_t *)lvalue + 1), &lvalue_len, lwork) != LZO_E_OK) {
+ GRN_FREE(lwork);
+ GRN_FREE(lvalue);
+ ERR(GRN_LZO_ERROR, "lzo1x_1_compress");
+ return ctx->rc;
+ }
+ GRN_FREE(lwork);
+ *(uint64_t *)lvalue = value_len;
+ rc = grn_ja_put_raw(ctx, ja, id, lvalue, lvalue_len + sizeof(uint64_t), flags, cas);
+ GRN_FREE(lvalue);
+ return rc;
+}
+#endif /* GRN_WITH_LZO */
+
+grn_rc
+grn_ja_put(grn_ctx *ctx, grn_ja *ja, grn_id id, void *value, uint32_t value_len,
+ int flags, uint64_t *cas)
+{
+#ifdef GRN_WITH_ZLIB
+ if (ja->header->flags & GRN_OBJ_COMPRESS_ZLIB) {
+ return grn_ja_put_zlib(ctx, ja, id, value, value_len, flags, cas);
+ }
+#endif /* GRN_WITH_ZLIB */
+#ifdef GRN_WITH_LZO
+ if (ja->header->flags & GRN_OBJ_COMPRESS_LZO) {
+ return grn_ja_put_lzo(ctx, ja, id, value, value_len, flags, cas);
+ }
+#endif /* GRN_WITH_LZO */
+ return grn_ja_put_raw(ctx, ja, id, value, value_len, flags, cas);
+}
+
+static grn_rc
+grn_ja_defrag_seg(grn_ctx *ctx, grn_ja *ja, uint32_t seg)
+{
+ byte *v = NULL, *ve;
+ uint32_t element_size, cum = 0, *seginfo = &SEGMENTS_AT(ja,seg), sum;
+ sum = (*seginfo & ~SEG_MASK);
+ GRN_IO_SEG_REF(ja->io, seg, v);
+ if (!v) { return GRN_NO_MEMORY_AVAILABLE; }
+ ve = v + JA_SEGMENT_SIZE;
+ while (v < ve && cum < sum) {
+ grn_id id = *((grn_id *)v);
+ if (!id) { break; }
+ if (id & DELETED) {
+ element_size = (id & ~DELETED);
+ } else {
+ uint64_t cas;
+ uint32_t pos;
+ if (grn_ja_element_info(ctx, ja, id, &cas, &pos, &element_size)) { break; }
+ if (v + sizeof(uint32_t) != ve - JA_SEGMENT_SIZE + pos) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "dseges[%d] = pos unmatch (%d != %" GRN_FMT_LLD ")",
+ seg, pos, (long long int)(v + sizeof(uint32_t) + JA_SEGMENT_SIZE - ve));
+ break;
+ }
+ if (grn_ja_put(ctx, ja, id, v + sizeof(uint32_t), element_size, GRN_OBJ_SET, &cas)) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "dseges[%d] = put failed (%d)", seg, id);
+ break;
+ }
+ element_size = (element_size + sizeof(grn_id) - 1) & ~(sizeof(grn_id) - 1);
+ cum += sizeof(uint32_t) + element_size;
+ }
+ v += sizeof(uint32_t) + element_size;
+ }
+ if (*seginfo) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "dseges[%d] = %d after defrag", seg, (*seginfo & ~SEG_MASK));
+ }
+ GRN_IO_SEG_UNREF(ja->io, seg);
+ return GRN_SUCCESS;
+}
+
+int
+grn_ja_defrag(grn_ctx *ctx, grn_ja *ja, int threshold)
+{
+ int nsegs = 0;
+ uint32_t seg, ts = 1U << (GRN_JA_W_SEGMENT - threshold);
+ for (seg = 0; seg < JA_N_DSEGMENTS; seg++) {
+ if (seg == *(ja->header->curr_seg)) { continue; }
+ if (((SEGMENTS_AT(ja, seg) & SEG_MASK) == SEG_SEQ) &&
+ ((SEGMENTS_AT(ja, seg) & ~SEG_MASK) < ts)) {
+ if (!grn_ja_defrag_seg(ctx, ja, seg)) { nsegs++; }
+ }
+ }
+ return nsegs;
+}
+
+void
+grn_ja_check(grn_ctx *ctx, grn_ja *ja)
+{
+ char buf[8];
+ uint32_t seg;
+ struct grn_ja_header *h = ja->header;
+ GRN_OUTPUT_ARRAY_OPEN("RESULT", 8);
+ GRN_OUTPUT_MAP_OPEN("SUMMARY", 8);
+ GRN_OUTPUT_CSTR("flags");
+ grn_itoh(h->flags, buf, 8);
+ GRN_OUTPUT_STR(buf, 8);
+ GRN_OUTPUT_CSTR("curr seg");
+ GRN_OUTPUT_INT64(*(h->curr_seg));
+ GRN_OUTPUT_CSTR("curr pos");
+ GRN_OUTPUT_INT64(*(h->curr_pos));
+ GRN_OUTPUT_CSTR("max_element_size");
+ GRN_OUTPUT_INT64(h->max_element_size);
+ GRN_OUTPUT_CSTR("segregate_threshold");
+ GRN_OUTPUT_INT64(h->segregate_threshold);
+ GRN_OUTPUT_CSTR("n_element_variation");
+ GRN_OUTPUT_INT64(h->n_element_variation);
+ GRN_OUTPUT_MAP_CLOSE();
+ GRN_OUTPUT_ARRAY_OPEN("DETAIL", -1);
+ for (seg = 0; seg < JA_N_DSEGMENTS; seg++) {
+ int dseg = SEGMENTS_AT(ja, seg);
+ if (dseg) {
+ GRN_OUTPUT_MAP_OPEN("SEG", -1);
+ GRN_OUTPUT_CSTR("seg id");
+ GRN_OUTPUT_INT64(seg);
+ GRN_OUTPUT_CSTR("seg type");
+ GRN_OUTPUT_INT64((dseg & SEG_MASK)>>28);
+ GRN_OUTPUT_CSTR("seg value");
+ GRN_OUTPUT_INT64(dseg & ~SEG_MASK);
+ if ((dseg & SEG_MASK) == SEG_SEQ) {
+ byte *v = NULL, *ve;
+ uint32_t element_size, cum = 0, sum = dseg & ~SEG_MASK;
+ uint32_t n_del_elements = 0, n_elements = 0, s_del_elements = 0, s_elements = 0;
+ GRN_IO_SEG_REF(ja->io, seg, v);
+ if (v) {
+ /*
+ GRN_OUTPUT_CSTR("seg seq");
+ GRN_OUTPUT_ARRAY_OPEN("SEQ", -1);
+ */
+ ve = v + JA_SEGMENT_SIZE;
+ while (v < ve && cum < sum) {
+ grn_id id = *((grn_id *)v);
+ /*
+ GRN_OUTPUT_MAP_OPEN("ENTRY", -1);
+ GRN_OUTPUT_CSTR("id");
+ GRN_OUTPUT_INT64(id);
+ */
+ if (!id) { break; }
+ if (id & DELETED) {
+ element_size = (id & ~DELETED);
+ n_del_elements++;
+ s_del_elements += element_size;
+ } else {
+ element_size = grn_ja_size(ctx, ja, id);
+ element_size = (element_size + sizeof(grn_id) - 1) & ~(sizeof(grn_id) - 1);
+ cum += sizeof(uint32_t) + element_size;
+ n_elements++;
+ s_elements += sizeof(uint32_t) + element_size;
+ }
+ v += sizeof(uint32_t) + element_size;
+ /*
+ GRN_OUTPUT_CSTR("size");
+ GRN_OUTPUT_INT64(element_size);
+ GRN_OUTPUT_CSTR("cum");
+ GRN_OUTPUT_INT64(cum);
+ GRN_OUTPUT_MAP_CLOSE();
+ */
+ }
+ GRN_IO_SEG_UNREF(ja->io, seg);
+ /*
+ GRN_OUTPUT_ARRAY_CLOSE();
+ */
+ GRN_OUTPUT_CSTR("n_elements");
+ GRN_OUTPUT_INT64(n_elements);
+ GRN_OUTPUT_CSTR("s_elements");
+ GRN_OUTPUT_INT64(s_elements);
+ GRN_OUTPUT_CSTR("n_del_elements");
+ GRN_OUTPUT_INT64(n_del_elements);
+ GRN_OUTPUT_CSTR("s_del_elements");
+ GRN_OUTPUT_INT64(s_del_elements);
+ if (cum != sum) {
+ GRN_OUTPUT_CSTR("cum gap");
+ GRN_OUTPUT_INT64(cum - sum);
+ }
+ }
+ }
+ GRN_OUTPUT_MAP_CLOSE();
+ }
+ }
+ GRN_OUTPUT_ARRAY_CLOSE();
+ GRN_OUTPUT_ARRAY_CLOSE();
+}
+
+/**** vgram ****/
+
+/*
+
+static int len_sum = 0;
+static int img_sum = 0;
+static int simple_sum = 0;
+static int skip_sum = 0;
+
+grn_vgram *
+grn_vgram_create(const char *path)
+{
+ grn_vgram *s;
+ if (!(s = GRN_GMALLOC(sizeof(grn_vgram)))) { return NULL; }
+ s->vgram = grn_sym_create(path, sizeof(grn_id) * 2, 0, GRN_ENC_NONE);
+ if (!s->vgram) {
+ GRN_GFREE(s);
+ return NULL;
+ }
+ return s;
+}
+
+grn_vgram *
+grn_vgram_open(const char *path)
+{
+ grn_vgram *s;
+ if (!(s = GRN_GMALLOC(sizeof(grn_vgram)))) { return NULL; }
+ s->vgram = grn_sym_open(path);
+ if (!s->vgram) {
+ GRN_GFREE(s);
+ return NULL;
+ }
+ return s;
+}
+
+grn_vgram_buf *
+grn_vgram_buf_open(size_t len)
+{
+ grn_vgram_buf *b;
+ if (!(b = GRN_GMALLOC(sizeof(grn_vgram_buf)))) { return NULL; }
+ b->len = len;
+ b->tvs = b->tvp = GRN_GMALLOC(sizeof(grn_id) * len);
+ if (!b->tvp) { GRN_GFREE(b); return NULL; }
+ b->tve = b->tvs + len;
+ b->vps = b->vpp = GRN_GMALLOC(sizeof(grn_vgram_vnode) * len * 2);
+ if (!b->vpp) { GRN_GFREE(b->tvp); GRN_GFREE(b); return NULL; }
+ b->vpe = b->vps + len;
+ return b;
+}
+
+grn_rc
+grn_vgram_buf_add(grn_vgram_buf *b, grn_id tid)
+{
+ uint8_t dummybuf[8], *dummyp;
+ if (b->tvp < b->tve) { *b->tvp++ = tid; }
+ dummyp = dummybuf;
+ GRN_B_ENC(tid, dummyp);
+ simple_sum += dummyp - dummybuf;
+ return GRN_SUCCESS;
+}
+
+typedef struct {
+ grn_id vid;
+ grn_id tid;
+} vgram_key;
+
+grn_rc
+grn_vgram_update(grn_vgram *vgram, grn_id rid, grn_vgram_buf *b, grn_hash *terms)
+{
+ grn_inv_updspec **u;
+ if (b && b->tvs < b->tvp) {
+ grn_id *t0, *tn;
+ for (t0 = b->tvs; t0 < b->tvp - 1; t0++) {
+ grn_vgram_vnode *v, **vp;
+ if (grn_set_at(terms, t0, (void **) &u)) {
+ vp = &(*u)->vnodes;
+ for (tn = t0 + 1; tn < b->tvp; tn++) {
+ for (v = *vp; v && v->tid != *tn; v = v->cdr) ;
+ if (!v) {
+ if (b->vpp < b->vpe) {
+ v = b->vpp++;
+ } else {
+ // todo;
+ break;
+ }
+ v->car = NULL;
+ v->cdr = *vp;
+ *vp = v;
+ v->tid = *tn;
+ v->vid = 0;
+ v->freq = 0;
+ v->len = tn - t0;
+ }
+ v->freq++;
+ if (v->vid) {
+ vp = &v->car;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ {
+ grn_set *th = grn_set_open(sizeof(grn_id), sizeof(int), 0);
+ if (!th) { return GRN_NO_MEMORY_AVAILABLE; }
+ if (t0 == b->tvp) { GRN_LOG(ctx, GRN_LOG_DEBUG, "t0 == tvp"); }
+ for (t0 = b->tvs; t0 < b->tvp; t0++) {
+ grn_id vid, vid0 = *t0, vid1 = 0;
+ grn_vgram_vnode *v, *v2 = NULL, **vp;
+ if (grn_set_at(terms, t0, (void **) &u)) {
+ vp = &(*u)->vnodes;
+ for (tn = t0 + 1; tn < b->tvp; tn++) {
+ for (v = *vp; v; v = v->cdr) {
+ if (!v->vid && (v->freq < 2 || v->freq * v->len < 4)) {
+ *vp = v->cdr;
+ v->freq = 0;
+ }
+ if (v->tid == *tn) { break; }
+ vp = &v->cdr;
+ }
+ if (v) {
+ if (v->freq) {
+ v2 = v;
+ vid1 = vid0;
+ vid0 = v->vid;
+ }
+ if (v->vid) {
+ vp = &v->car;
+ continue;
+ }
+ }
+ break;
+ }
+ }
+ if (v2) {
+ if (!v2->vid) {
+ vgram_key key;
+ key.vid = vid1;
+ key.tid = v2->tid;
+ if (!(v2->vid = grn_sym_get(vgram->vgram, (char *)&key))) {
+ grn_set_close(th);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ vid = *t0 = v2->vid * 2 + 1;
+ memset(t0 + 1, 0, sizeof(grn_id) * v2->len);
+ t0 += v2->len;
+ } else {
+ vid = *t0 *= 2;
+ }
+ {
+ int *tf;
+ if (!grn_set_get(th, &vid, (void **) &tf)) {
+ grn_set_close(th);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ (*tf)++;
+ }
+ }
+ if (!th->n_entries) { GRN_LOG(ctx, GRN_LOG_DEBUG, "th->n_entries == 0"); }
+ {
+ int j = 0;
+ int skip = 0;
+ grn_set_eh *ehs, *ehp, *ehe;
+ grn_set_sort_optarg arg;
+ uint8_t *ps = GRN_GMALLOC(b->len * 2), *pp, *pe;
+ if (!ps) {
+ grn_set_close(th);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ pp = ps;
+ pe = ps + b->len * 2;
+ arg.mode = grn_sort_descending;
+ arg.compar = NULL;
+ arg.compar_arg = (void *)(intptr_t)sizeof(grn_id);
+ ehs = grn_set_sort(th, 0, &arg);
+ if (!ehs) {
+ GRN_GFREE(ps);
+ grn_set_close(th);
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ GRN_B_ENC(th->n_entries, pp);
+ for (ehp = ehs, ehe = ehs + th->n_entries; ehp < ehe; ehp++, j++) {
+ int *id = (int *)GRN_SET_INTVAL(*ehp);
+ GRN_B_ENC(*GRN_SET_INTKEY(*ehp), pp);
+ *id = j;
+ }
+ for (t0 = b->tvs; t0 < b->tvp; t0++) {
+ if (*t0) {
+ int *id;
+ if (!grn_set_at(th, t0, (void **) &id)) {
+ GRN_LOG(ctx, GRN_LOG_ERROR, "lookup error (%d)", *t0);
+ }
+ GRN_B_ENC(*id, pp);
+ } else {
+ skip++;
+ }
+ }
+ len_sum += b->len;
+ img_sum += pp - ps;
+ skip_sum += skip;
+ GRN_GFREE(ehs);
+ GRN_GFREE(ps);
+ }
+ grn_set_close(th);
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_vgram_buf_close(grn_vgram_buf *b)
+{
+ if (!b) { return GRN_INVALID_ARGUMENT; }
+ if (b->tvs) { GRN_GFREE(b->tvs); }
+ if (b->vps) { GRN_GFREE(b->vps); }
+ GRN_GFREE(b);
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_vgram_close(grn_vgram *vgram)
+{
+ if (!vgram) { return GRN_INVALID_ARGUMENT; }
+ GRN_LOG(ctx, GRN_LOG_DEBUG, "len=%d img=%d skip=%d simple=%d", len_sum, img_sum, skip_sum, simple_sum);
+ grn_sym_close(vgram->vgram);
+ GRN_GFREE(vgram);
+ return GRN_SUCCESS;
+}
+*/
diff --git a/storage/mroonga/vendor/groonga/lib/store.h b/storage/mroonga/vendor/groonga/lib/store.h
new file mode 100644
index 00000000000..719fe775bd9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/store.h
@@ -0,0 +1,160 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_STORE_H
+#define GRN_STORE_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifndef GRN_CTX_H
+#include "ctx.h"
+#endif /* GRN_CTX_H */
+
+#ifndef GRN_HASH_H
+#include "hash.h"
+#endif /* GRN_HASH_H */
+
+#ifndef GRN_IO_H
+#include "io.h"
+#endif /* GRN_IO_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**** fixed sized elements ****/
+
+typedef struct _grn_ra grn_ra;
+
+struct _grn_ra {
+ grn_db_obj obj;
+ grn_io *io;
+ int element_width;
+ int element_mask;
+ struct grn_ra_header *header;
+};
+
+struct grn_ra_header {
+ uint32_t element_size;
+ uint32_t nrecords; /* nrecords is not maintained by default */
+ uint32_t reserved[10];
+};
+
+grn_ra *grn_ra_create(grn_ctx *ctx, const char *path, unsigned int element_size);
+grn_ra *grn_ra_open(grn_ctx *ctx, const char *path);
+grn_rc grn_ra_info(grn_ctx *ctx, grn_ra *ra, unsigned int *element_size);
+grn_rc grn_ra_close(grn_ctx *ctx, grn_ra *ra);
+grn_rc grn_ra_remove(grn_ctx *ctx, const char *path);
+void *grn_ra_ref(grn_ctx *ctx, grn_ra *ra, grn_id id);
+grn_rc grn_ra_unref(grn_ctx *ctx, grn_ra *ra, grn_id id);
+
+typedef struct _grn_ra_cache grn_ra_cache;
+
+struct _grn_ra_cache {
+ void *p;
+ int32_t seg;
+};
+
+#define GRN_RA_CACHE_INIT(ra,c) do {\
+ (c)->p = NULL; (c)->seg = -1;\
+} while (0)
+
+#define GRN_RA_CACHE_FIN(ra,c) do {\
+ if ((c)->seg != -1) { GRN_IO_SEG_UNREF((ra)->io, (c)->seg); }\
+} while (0);
+
+void *grn_ra_ref_cache(grn_ctx *ctx, grn_ra *ra, grn_id id, grn_ra_cache *cache);
+
+/**** variable sized elements ****/
+
+extern grn_bool grn_ja_skip_same_value_put;
+
+typedef struct _grn_ja grn_ja;
+
+struct _grn_ja {
+ grn_db_obj obj;
+ grn_io *io;
+ struct grn_ja_header *header;
+};
+
+GRN_API grn_ja *grn_ja_create(grn_ctx *ctx, const char *path,
+ uint32_t max_element_size, uint32_t flags);
+grn_ja *grn_ja_open(grn_ctx *ctx, const char *path);
+grn_rc grn_ja_info(grn_ctx *ctx, grn_ja *ja, unsigned int *max_element_size);
+GRN_API grn_rc grn_ja_close(grn_ctx *ctx, grn_ja *ja);
+grn_rc grn_ja_remove(grn_ctx *ctx, const char *path);
+grn_rc grn_ja_put(grn_ctx *ctx, grn_ja *ja, grn_id id,
+ void *value, uint32_t value_len, int flags, uint64_t *cas);
+int grn_ja_at(grn_ctx *ctx, grn_ja *ja, grn_id id, void *valbuf, int buf_size);
+
+GRN_API void *grn_ja_ref(grn_ctx *ctx, grn_ja *ja, grn_id id, grn_io_win *iw,
+ uint32_t *value_len);
+grn_obj *grn_ja_get_value(grn_ctx *ctx, grn_ja *ja, grn_id id, grn_obj *value);
+
+GRN_API grn_rc grn_ja_unref(grn_ctx *ctx, grn_io_win *iw);
+int grn_ja_defrag(grn_ctx *ctx, grn_ja *ja, int threshold);
+
+GRN_API grn_rc grn_ja_putv(grn_ctx *ctx, grn_ja *ja, grn_id id,
+ grn_obj *vector, int flags);
+GRN_API uint32_t grn_ja_size(grn_ctx *ctx, grn_ja *ja, grn_id id);
+
+void grn_ja_check(grn_ctx *ctx, grn_ja *ja);
+
+/*
+
+typedef struct _grn_vgram_vnode
+{
+ struct _grn_vgram_vnode *car;
+ struct _grn_vgram_vnode *cdr;
+ grn_id tid;
+ grn_id vid;
+ int freq;
+ int len;
+} grn_vgram_vnode;
+
+typedef struct _grn_vgram grn_vgram;
+struct _grn_vgram {
+ void *vgram;
+};
+
+struct _grn_vgram_buf {
+ size_t len;
+ grn_id *tvs;
+ grn_id *tvp;
+ grn_id *tve;
+ grn_vgram_vnode *vps;
+ grn_vgram_vnode *vpp;
+ grn_vgram_vnode *vpe;
+};
+
+grn_vgram *grn_vgram_create(const char *path);
+grn_vgram *grn_vgram_open(const char *path);
+grn_rc grn_vgram_close(grn_vgram *vgram);
+grn_rc grn_vgram_update(grn_vgram *vgram, grn_id rid, grn_vgram_buf *b, grn_hash *terms);
+
+grn_vgram_buf *grn_vgram_buf_open(size_t len);
+grn_rc grn_vgram_buf_add(grn_vgram_buf *b, grn_id tid);
+grn_rc grn_vgram_buf_close(grn_vgram_buf *b);
+
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_STORE_H */
diff --git a/storage/mroonga/vendor/groonga/lib/str.c b/storage/mroonga/vendor/groonga/lib/str.c
new file mode 100644
index 00000000000..255f8020a7b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/str.c
@@ -0,0 +1,3181 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "groonga_in.h"
+#include <limits.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include "db.h"
+#include "str.h"
+
+#ifndef _ISOC99_SOURCE
+#define _ISOC99_SOURCE
+#endif /* _ISOC99_SOURCE */
+#include <math.h>
+
+/* For Visual C++ 2010. Drop the code when we drop Visual C++ 2010 support. */
+#if defined(_MSC_VER) && _MSC_VER < 1800
+# define va_copy(destination, source) destination = source
+#endif
+
+inline static int
+grn_str_charlen_utf8(grn_ctx *ctx, const unsigned char *str, const unsigned char *end)
+{
+ /* MEMO: This function allows non-null-terminated string as str. */
+ /* But requires the end of string. */
+ if (end <= str || !*str) {
+ return 0;
+ }
+ if (*str & 0x80) {
+ int i;
+ int len;
+ GRN_BIT_SCAN_REV(~(*str << 24), len);
+ len = 31 - len;
+ if ((unsigned int)(len - 2) >= 3) { /* (len == 1 || len >= 5) */
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "grn_str_charlen_utf8(): first byte is invalid");
+ return 0;
+ }
+ if (str + len > end) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "grn_str_charlen_utf8(): incomplete character");
+ return 0;
+ }
+ for (i = 1; i < len; ++i) {
+ if ((str[i] & 0xc0) != 0x80) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "grn_str_charlen_utf8(): <%d>th byte is invalid",
+ i + 1);
+ return 0;
+ }
+ }
+ return len;
+ } else {
+ return 1;
+ }
+}
+
+unsigned int
+grn_str_charlen(grn_ctx *ctx, const char *str, grn_encoding encoding)
+{
+ /* MEMO: This function requires null-terminated string as str.*/
+ unsigned char *p = (unsigned char *) str;
+ if (!*p) { return 0; }
+ switch (encoding) {
+ case GRN_ENC_EUC_JP :
+ if (*p & 0x80) {
+ if (*(p + 1)) {
+ return 2;
+ } else {
+ /* This is invalid character */
+ GRN_LOG(ctx, GRN_LOG_WARNING, "invalid euc-jp string end on grn_str_charlen");
+ return 0;
+ }
+ }
+ return 1;
+ break;
+ case GRN_ENC_UTF8 :
+ if (*p & 0x80) {
+ int b, w;
+ size_t size;
+ for (b = 0x40, w = 0; b && (*p & b); b >>= 1, w++);
+ if (!w) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "invalid utf8 string(1) on grn_str_charlen");
+ return 0;
+ }
+ for (size = 1; w--; size++) {
+ if (!*++p || (*p & 0xc0) != 0x80) {
+ GRN_LOG(ctx, GRN_LOG_WARNING, "invalid utf8 string(2) on grn_str_charlen");
+ return 0;
+ }
+ }
+ return size;
+ } else {
+ return 1;
+ }
+ break;
+ case GRN_ENC_SJIS :
+ if (*p & 0x80) {
+ /* we regard 0xa0 as JIS X 0201 KANA. adjusted to other tools. */
+ if (0xa0 <= *p && *p <= 0xdf) {
+ /* hankaku-kana */
+ return 1;
+ } else if (!(*(p + 1))) {
+ /* This is invalid character */
+ GRN_LOG(ctx, GRN_LOG_WARNING, "invalid sjis string end on grn_str_charlen");
+ return 0;
+ } else {
+ return 2;
+ }
+ } else {
+ return 1;
+ }
+ break;
+ default :
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+int
+grn_charlen_(grn_ctx *ctx, const char *str, const char *end, grn_encoding encoding)
+{
+ /* MEMO: This function allows non-null-terminated string as str. */
+ /* But requires the end of string. */
+ unsigned char *p = (unsigned char *) str;
+ if (p >= (unsigned char *)end) { return 0; }
+ switch (encoding) {
+ case GRN_ENC_EUC_JP :
+ if (*p & 0x80) {
+ if ((p + 1) < (unsigned char *)end) {
+ return 2;
+ } else {
+ /* This is invalid character */
+ GRN_LOG(ctx, GRN_LOG_WARNING, "invalid euc-jp string end on grn_charlen");
+ return 0;
+ }
+ }
+ return 1;
+ break;
+ case GRN_ENC_UTF8 :
+ return grn_str_charlen_utf8(ctx, p, (unsigned char *)end);
+ break;
+ case GRN_ENC_SJIS :
+ if (*p & 0x80) {
+ /* we regard 0xa0 as JIS X 0201 KANA. adjusted to other tools. */
+ if (0xa0 <= *p && *p <= 0xdf) {
+ /* hankaku-kana */
+ return 1;
+ } else if (++p >= (unsigned char *)end) {
+ /* This is invalid character */
+ GRN_LOG(ctx, GRN_LOG_WARNING, "invalid sjis string end on grn_charlen");
+ return 0;
+ } else {
+ return 2;
+ }
+ } else {
+ return 1;
+ }
+ break;
+ default :
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+int
+grn_charlen(grn_ctx *ctx, const char *str, const char *end)
+{
+ return grn_charlen_(ctx, str, end, ctx->encoding);
+}
+
+static unsigned char symbol[] = {
+ ',', '.', 0, ':', ';', '?', '!', 0, 0, 0, '`', 0, '^', '~', '_', 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, '-', '-', '/', '\\', 0, 0, '|', 0, 0, 0, '\'', 0,
+ '"', '(', ')', 0, 0, '[', ']', '{', '}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ '+', '-', 0, 0, 0, '=', 0, '<', '>', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ '$', 0, 0, '%', '#', '&', '*', '@', 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+inline static grn_rc
+normalize_euc(grn_ctx *ctx, grn_str *nstr)
+{
+ static uint16_t hankana[] = {
+ 0xa1a1, 0xa1a3, 0xa1d6, 0xa1d7, 0xa1a2, 0xa1a6, 0xa5f2, 0xa5a1, 0xa5a3,
+ 0xa5a5, 0xa5a7, 0xa5a9, 0xa5e3, 0xa5e5, 0xa5e7, 0xa5c3, 0xa1bc, 0xa5a2,
+ 0xa5a4, 0xa5a6, 0xa5a8, 0xa5aa, 0xa5ab, 0xa5ad, 0xa5af, 0xa5b1, 0xa5b3,
+ 0xa5b5, 0xa5b7, 0xa5b9, 0xa5bb, 0xa5bd, 0xa5bf, 0xa5c1, 0xa5c4, 0xa5c6,
+ 0xa5c8, 0xa5ca, 0xa5cb, 0xa5cc, 0xa5cd, 0xa5ce, 0xa5cf, 0xa5d2, 0xa5d5,
+ 0xa5d8, 0xa5db, 0xa5de, 0xa5df, 0xa5e0, 0xa5e1, 0xa5e2, 0xa5e4, 0xa5e6,
+ 0xa5e8, 0xa5e9, 0xa5ea, 0xa5eb, 0xa5ec, 0xa5ed, 0xa5ef, 0xa5f3, 0xa1ab,
+ 0xa1eb
+ };
+ static unsigned char dakuten[] = {
+ 0xf4, 0, 0, 0, 0, 0xac, 0, 0xae, 0, 0xb0, 0, 0xb2, 0, 0xb4, 0, 0xb6, 0,
+ 0xb8, 0, 0xba, 0, 0xbc, 0, 0xbe, 0, 0xc0, 0, 0xc2, 0, 0, 0xc5, 0, 0xc7,
+ 0, 0xc9, 0, 0, 0, 0, 0, 0, 0xd0, 0, 0, 0xd3, 0, 0, 0xd6, 0, 0, 0xd9, 0,
+ 0, 0xdc
+ };
+ static unsigned char handaku[] = {
+ 0xd1, 0, 0, 0xd4, 0, 0, 0xd7, 0, 0, 0xda, 0, 0, 0xdd
+ };
+ int16_t *ch;
+ const unsigned char *s, *s_, *e;
+ unsigned char *d, *d0, *d_, b;
+ uint_least8_t *cp, *ctypes, ctype;
+ size_t size = nstr->orig_blen, length = 0;
+ int removeblankp = nstr->flags & GRN_STR_REMOVEBLANK;
+ if (!(nstr->norm = GRN_MALLOC(size * 2 + 1))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ d0 = (unsigned char *) nstr->norm;
+ if (nstr->flags & GRN_STR_WITH_CHECKS) {
+ if (!(nstr->checks = GRN_MALLOC(size * 2 * sizeof(int16_t) + 1))) {
+ GRN_FREE(nstr->norm);
+ nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ ch = nstr->checks;
+ if (nstr->flags & GRN_STR_WITH_CTYPES) {
+ if (!(nstr->ctypes = GRN_MALLOC(size + 1))) {
+ GRN_FREE(nstr->checks);
+ GRN_FREE(nstr->norm);
+ nstr->checks = NULL;
+ nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ cp = ctypes = nstr->ctypes;
+ e = (unsigned char *)nstr->orig + size;
+ for (s = s_ = (unsigned char *) nstr->orig, d = d_ = d0; s < e; s++) {
+ if ((*s & 0x80)) {
+ if (((s + 1) < e) && (*(s + 1) & 0x80)) {
+ unsigned char c1 = *s++, c2 = *s, c3 = 0;
+ switch (c1 >> 4) {
+ case 0x08 :
+ if (c1 == 0x8e && 0xa0 <= c2 && c2 <= 0xdf) {
+ uint16_t c = hankana[c2 - 0xa0];
+ switch (c) {
+ case 0xa1ab :
+ if (d > d0 + 1 && d[-2] == 0xa5
+ && 0xa6 <= d[-1] && d[-1] <= 0xdb && (b = dakuten[d[-1] - 0xa6])) {
+ *(d - 1) = b;
+ if (ch) { ch[-1] += 2; s_ += 2; }
+ continue;
+ } else {
+ *d++ = c >> 8; *d = c & 0xff;
+ }
+ break;
+ case 0xa1eb :
+ if (d > d0 + 1 && d[-2] == 0xa5
+ && 0xcf <= d[-1] && d[-1] <= 0xdb && (b = handaku[d[-1] - 0xcf])) {
+ *(d - 1) = b;
+ if (ch) { ch[-1] += 2; s_ += 2; }
+ continue;
+ } else {
+ *d++ = c >> 8; *d = c & 0xff;
+ }
+ break;
+ default :
+ *d++ = c >> 8; *d = c & 0xff;
+ break;
+ }
+ ctype = GRN_CHAR_KATAKANA;
+ } else {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ }
+ break;
+ case 0x09 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ case 0x0a :
+ switch (c1 & 0x0f) {
+ case 1 :
+ switch (c2) {
+ case 0xbc :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KATAKANA;
+ break;
+ case 0xb9 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KANJI;
+ break;
+ case 0xa1 :
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_STR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_STR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ break;
+ default :
+ if (c2 >= 0xa4 && (c3 = symbol[c2 - 0xa4])) {
+ *d = c3;
+ ctype = GRN_CHAR_SYMBOL;
+ } else {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ }
+ break;
+ }
+ break;
+ case 2 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_SYMBOL;
+ break;
+ case 3 :
+ c3 = c2 - 0x80;
+ if ('a' <= c3 && c3 <= 'z') {
+ ctype = GRN_CHAR_ALPHA;
+ *d = c3;
+ } else if ('A' <= c3 && c3 <= 'Z') {
+ ctype = GRN_CHAR_ALPHA;
+ *d = c3 + 0x20;
+ } else if ('0' <= c3 && c3 <= '9') {
+ ctype = GRN_CHAR_DIGIT;
+ *d = c3;
+ } else {
+ ctype = GRN_CHAR_OTHERS;
+ *d++ = c1; *d = c2;
+ }
+ break;
+ case 4 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_HIRAGANA;
+ break;
+ case 5 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KATAKANA;
+ break;
+ case 6 :
+ case 7 :
+ case 8 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_SYMBOL;
+ break;
+ default :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ break;
+ default :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KANJI;
+ break;
+ }
+ } else {
+ /* skip invalid character */
+ continue;
+ }
+ } else {
+ unsigned char c = *s;
+ switch (c >> 4) {
+ case 0 :
+ case 1 :
+ /* skip unprintable ascii */
+ if (cp > ctypes) { *(cp - 1) |= GRN_STR_BLANK; }
+ continue;
+ case 2 :
+ if (c == 0x20) {
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_STR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_STR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 3 :
+ *d = c;
+ ctype = (c <= 0x39) ? GRN_CHAR_DIGIT : GRN_CHAR_SYMBOL;
+ break;
+ case 4 :
+ *d = ('A' <= c) ? c + 0x20 : c;
+ ctype = (c == 0x40) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 5 :
+ *d = (c <= 'Z') ? c + 0x20 : c;
+ ctype = (c <= 0x5a) ? GRN_CHAR_ALPHA : GRN_CHAR_SYMBOL;
+ break;
+ case 6 :
+ *d = c;
+ ctype = (c == 0x60) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 7 :
+ *d = c;
+ ctype = (c <= 0x7a) ? GRN_CHAR_ALPHA : (c == 0x7f ? GRN_CHAR_OTHERS : GRN_CHAR_SYMBOL);
+ break;
+ default :
+ *d = c;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ }
+ d++;
+ length++;
+ if (cp) { *cp++ = ctype; }
+ if (ch) {
+ *ch++ = (int16_t)(s + 1 - s_);
+ s_ = s + 1;
+ while (++d_ < d) { *ch++ = 0; }
+ }
+ }
+ if (cp) { *cp = GRN_CHAR_NULL; }
+ *d = '\0';
+ nstr->length = length;
+ nstr->norm_blen = (size_t)(d - (unsigned char *)nstr->norm);
+ return GRN_SUCCESS;
+}
+
+#ifdef GRN_WITH_NFKC
+const char *grn_nfkc_map1(const unsigned char *str);
+const char *grn_nfkc_map2(const unsigned char *prefix, const unsigned char *suffix);
+
+inline static grn_rc
+normalize_utf8(grn_ctx *ctx, grn_str *nstr)
+{
+ int16_t *ch;
+ const unsigned char *s, *s_, *s__ = NULL, *p, *p2, *pe, *e;
+ unsigned char *d, *d_, *de;
+ uint_least8_t *cp;
+ size_t length = 0, ls, lp, size = nstr->orig_blen, ds = size * 3;
+ int removeblankp = nstr->flags & GRN_STR_REMOVEBLANK;
+ if (!(nstr->norm = GRN_MALLOC(ds + 1))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ if (nstr->flags & GRN_STR_WITH_CHECKS) {
+ if (!(nstr->checks = GRN_MALLOC(ds * sizeof(int16_t) + 1))) {
+ GRN_FREE(nstr->norm); nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ ch = nstr->checks;
+ if (nstr->flags & GRN_STR_WITH_CTYPES) {
+ if (!(nstr->ctypes = GRN_MALLOC(ds + 1))) {
+ if (nstr->checks) { GRN_FREE(nstr->checks); nstr->checks = NULL; }
+ GRN_FREE(nstr->norm); nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ cp = nstr->ctypes;
+ d = (unsigned char *)nstr->norm;
+ de = d + ds;
+ d_ = NULL;
+ e = (unsigned char *)nstr->orig + size;
+ for (s = s_ = (unsigned char *)nstr->orig; ; s += ls) {
+ if (!(ls = grn_str_charlen_utf8(ctx, s, e))) {
+ break;
+ }
+ if ((p = (unsigned char *)grn_nfkc_map1(s))) {
+ pe = p + strlen((char *)p);
+ } else {
+ p = s;
+ pe = p + ls;
+ }
+ if (d_ && (p2 = (unsigned char *)grn_nfkc_map2(d_, p))) {
+ p = p2;
+ pe = p + strlen((char *)p);
+ if (cp) { cp--; }
+ if (ch) {
+ ch -= (d - d_);
+ s_ = s__;
+ }
+ d = d_;
+ length--;
+ }
+ for (; ; p += lp) {
+ if (!(lp = grn_str_charlen_utf8(ctx, p, pe))) {
+ break;
+ }
+ if ((*p == ' ' && removeblankp) || *p < 0x20 /* skip unprintable ascii */ ) {
+ if (cp > nstr->ctypes) { *(cp - 1) |= GRN_STR_BLANK; }
+ } else {
+ if (de <= d + lp) {
+ unsigned char *norm;
+ ds += (ds >> 1) + lp;
+ if (!(norm = GRN_REALLOC(nstr->norm, ds + 1))) {
+ if (nstr->ctypes) { GRN_FREE(nstr->ctypes); nstr->ctypes = NULL; }
+ if (nstr->checks) { GRN_FREE(nstr->checks); nstr->checks = NULL; }
+ GRN_FREE(nstr->norm); nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ de = norm + ds;
+ d = norm + (d - (unsigned char *)nstr->norm);
+ nstr->norm = (char *)norm;
+ if (ch) {
+ int16_t *checks;
+ if (!(checks = GRN_REALLOC(nstr->checks, ds * sizeof(int16_t)+ 1))) {
+ if (nstr->ctypes) { GRN_FREE(nstr->ctypes); nstr->ctypes = NULL; }
+ GRN_FREE(nstr->checks); nstr->checks = NULL;
+ GRN_FREE(nstr->norm); nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ ch = checks + (ch - nstr->checks);
+ nstr->checks = checks;
+ }
+ if (cp) {
+ uint_least8_t *ctypes;
+ if (!(ctypes = GRN_REALLOC(nstr->ctypes, ds + 1))) {
+ GRN_FREE(nstr->ctypes); nstr->ctypes = NULL;
+ if (nstr->checks) { GRN_FREE(nstr->checks); nstr->checks = NULL; }
+ GRN_FREE(nstr->norm); nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ cp = ctypes + (cp - nstr->ctypes);
+ nstr->ctypes = ctypes;
+ }
+ }
+ memcpy(d, p, lp);
+ d_ = d;
+ d += lp;
+ length++;
+ if (cp) { *cp++ = grn_nfkc_char_type(p); }
+ if (ch) {
+ size_t i;
+ if (s_ == s + ls) {
+ *ch++ = -1;
+ } else {
+ *ch++ = (int16_t)(s + ls - s_);
+ s__ = s_;
+ s_ = s + ls;
+ }
+ for (i = lp; i > 1; i--) { *ch++ = 0; }
+ }
+ }
+ }
+ }
+ if (cp) { *cp = GRN_CHAR_NULL; }
+ *d = '\0';
+ nstr->length = length;
+ nstr->norm_blen = (size_t)(d - (unsigned char *)nstr->norm);
+ return GRN_SUCCESS;
+}
+#endif /* GRN_WITH_NFKC */
+
+inline static grn_rc
+normalize_sjis(grn_ctx *ctx, grn_str *nstr)
+{
+ static uint16_t hankana[] = {
+ 0x8140, 0x8142, 0x8175, 0x8176, 0x8141, 0x8145, 0x8392, 0x8340, 0x8342,
+ 0x8344, 0x8346, 0x8348, 0x8383, 0x8385, 0x8387, 0x8362, 0x815b, 0x8341,
+ 0x8343, 0x8345, 0x8347, 0x8349, 0x834a, 0x834c, 0x834e, 0x8350, 0x8352,
+ 0x8354, 0x8356, 0x8358, 0x835a, 0x835c, 0x835e, 0x8360, 0x8363, 0x8365,
+ 0x8367, 0x8369, 0x836a, 0x836b, 0x836c, 0x836d, 0x836e, 0x8371, 0x8374,
+ 0x8377, 0x837a, 0x837d, 0x837e, 0x8380, 0x8381, 0x8382, 0x8384, 0x8386,
+ 0x8388, 0x8389, 0x838a, 0x838b, 0x838c, 0x838d, 0x838f, 0x8393, 0x814a,
+ 0x814b
+ };
+ static unsigned char dakuten[] = {
+ 0x94, 0, 0, 0, 0, 0x4b, 0, 0x4d, 0, 0x4f, 0, 0x51, 0, 0x53, 0, 0x55, 0,
+ 0x57, 0, 0x59, 0, 0x5b, 0, 0x5d, 0, 0x5f, 0, 0x61, 0, 0, 0x64, 0, 0x66,
+ 0, 0x68, 0, 0, 0, 0, 0, 0, 0x6f, 0, 0, 0x72, 0, 0, 0x75, 0, 0, 0x78, 0,
+ 0, 0x7b
+ };
+ static unsigned char handaku[] = {
+ 0x70, 0, 0, 0x73, 0, 0, 0x76, 0, 0, 0x79, 0, 0, 0x7c
+ };
+ int16_t *ch;
+ const unsigned char *s, *s_;
+ unsigned char *d, *d0, *d_, b, *e;
+ uint_least8_t *cp, *ctypes, ctype;
+ size_t size = nstr->orig_blen, length = 0;
+ int removeblankp = nstr->flags & GRN_STR_REMOVEBLANK;
+ if (!(nstr->norm = GRN_MALLOC(size * 2 + 1))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ d0 = (unsigned char *) nstr->norm;
+ if (nstr->flags & GRN_STR_WITH_CHECKS) {
+ if (!(nstr->checks = GRN_MALLOC(size * 2 * sizeof(int16_t) + 1))) {
+ GRN_FREE(nstr->norm);
+ nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ ch = nstr->checks;
+ if (nstr->flags & GRN_STR_WITH_CTYPES) {
+ if (!(nstr->ctypes = GRN_MALLOC(size + 1))) {
+ GRN_FREE(nstr->checks);
+ GRN_FREE(nstr->norm);
+ nstr->checks = NULL;
+ nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ cp = ctypes = nstr->ctypes;
+ e = (unsigned char *)nstr->orig + size;
+ for (s = s_ = (unsigned char *) nstr->orig, d = d_ = d0; s < e; s++) {
+ if ((*s & 0x80)) {
+ if (0xa0 <= *s && *s <= 0xdf) {
+ uint16_t c = hankana[*s - 0xa0];
+ switch (c) {
+ case 0x814a :
+ if (d > d0 + 1 && d[-2] == 0x83
+ && 0x45 <= d[-1] && d[-1] <= 0x7a && (b = dakuten[d[-1] - 0x45])) {
+ *(d - 1) = b;
+ if (ch) { ch[-1]++; s_++; }
+ continue;
+ } else {
+ *d++ = c >> 8; *d = c & 0xff;
+ }
+ break;
+ case 0x814b :
+ if (d > d0 + 1 && d[-2] == 0x83
+ && 0x6e <= d[-1] && d[-1] <= 0x7a && (b = handaku[d[-1] - 0x6e])) {
+ *(d - 1) = b;
+ if (ch) { ch[-1]++; s_++; }
+ continue;
+ } else {
+ *d++ = c >> 8; *d = c & 0xff;
+ }
+ break;
+ default :
+ *d++ = c >> 8; *d = c & 0xff;
+ break;
+ }
+ ctype = GRN_CHAR_KATAKANA;
+ } else {
+ if ((s + 1) < e && 0x40 <= *(s + 1) && *(s + 1) <= 0xfc) {
+ unsigned char c1 = *s++, c2 = *s, c3 = 0;
+ if (0x81 <= c1 && c1 <= 0x87) {
+ switch (c1 & 0x0f) {
+ case 1 :
+ switch (c2) {
+ case 0x5b :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KATAKANA;
+ break;
+ case 0x58 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KANJI;
+ break;
+ case 0x40 :
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_STR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_STR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ break;
+ default :
+ if (0x43 <= c2 && c2 <= 0x7e && (c3 = symbol[c2 - 0x43])) {
+ *d = c3;
+ ctype = GRN_CHAR_SYMBOL;
+ } else if (0x7f <= c2 && c2 <= 0x97 && (c3 = symbol[c2 - 0x44])) {
+ *d = c3;
+ ctype = GRN_CHAR_SYMBOL;
+ } else {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ }
+ break;
+ }
+ break;
+ case 2 :
+ c3 = c2 - 0x1f;
+ if (0x4f <= c2 && c2 <= 0x58) {
+ ctype = GRN_CHAR_DIGIT;
+ *d = c2 - 0x1f;
+ } else if (0x60 <= c2 && c2 <= 0x79) {
+ ctype = GRN_CHAR_ALPHA;
+ *d = c2 + 0x01;
+ } else if (0x81 <= c2 && c2 <= 0x9a) {
+ ctype = GRN_CHAR_ALPHA;
+ *d = c2 - 0x20;
+ } else if (0x9f <= c2 && c2 <= 0xf1) {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_HIRAGANA;
+ } else {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ }
+ break;
+ case 3 :
+ if (0x40 <= c2 && c2 <= 0x96) {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KATAKANA;
+ } else {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 4 :
+ case 7 :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_SYMBOL;
+ break;
+ default :
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ } else {
+ *d++ = c1; *d = c2;
+ ctype = GRN_CHAR_KANJI;
+ }
+ } else {
+ /* skip invalid character */
+ continue;
+ }
+ }
+ } else {
+ unsigned char c = *s;
+ switch (c >> 4) {
+ case 0 :
+ case 1 :
+ /* skip unprintable ascii */
+ if (cp > ctypes) { *(cp - 1) |= GRN_STR_BLANK; }
+ continue;
+ case 2 :
+ if (c == 0x20) {
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_STR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_STR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 3 :
+ *d = c;
+ ctype = (c <= 0x39) ? GRN_CHAR_DIGIT : GRN_CHAR_SYMBOL;
+ break;
+ case 4 :
+ *d = ('A' <= c) ? c + 0x20 : c;
+ ctype = (c == 0x40) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 5 :
+ *d = (c <= 'Z') ? c + 0x20 : c;
+ ctype = (c <= 0x5a) ? GRN_CHAR_ALPHA : GRN_CHAR_SYMBOL;
+ break;
+ case 6 :
+ *d = c;
+ ctype = (c == 0x60) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 7 :
+ *d = c;
+ ctype = (c <= 0x7a) ? GRN_CHAR_ALPHA : (c == 0x7f ? GRN_CHAR_OTHERS : GRN_CHAR_SYMBOL);
+ break;
+ default :
+ *d = c;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ }
+ d++;
+ length++;
+ if (cp) { *cp++ = ctype; }
+ if (ch) {
+ *ch++ = (int16_t)(s + 1 - s_);
+ s_ = s + 1;
+ while (++d_ < d) { *ch++ = 0; }
+ }
+ }
+ if (cp) { *cp = GRN_CHAR_NULL; }
+ *d = '\0';
+ nstr->length = length;
+ nstr->norm_blen = (size_t)(d - (unsigned char *)nstr->norm);
+ return GRN_SUCCESS;
+}
+
+inline static grn_rc
+normalize_none(grn_ctx *ctx, grn_str *nstr)
+{
+ int16_t *ch;
+ const unsigned char *s, *s_, *e;
+ unsigned char *d, *d0, *d_;
+ uint_least8_t *cp, *ctypes, ctype;
+ size_t size = nstr->orig_blen, length = 0;
+ int removeblankp = nstr->flags & GRN_STR_REMOVEBLANK;
+ if (!(nstr->norm = GRN_MALLOC(size + 1))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ d0 = (unsigned char *) nstr->norm;
+ if (nstr->flags & GRN_STR_WITH_CHECKS) {
+ if (!(nstr->checks = GRN_MALLOC(size * sizeof(int16_t) + 1))) {
+ GRN_FREE(nstr->norm);
+ nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ ch = nstr->checks;
+ if (nstr->flags & GRN_STR_WITH_CTYPES) {
+ if (!(nstr->ctypes = GRN_MALLOC(size + 1))) {
+ GRN_FREE(nstr->checks);
+ GRN_FREE(nstr->norm);
+ nstr->checks = NULL;
+ nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ cp = ctypes = nstr->ctypes;
+ e = (unsigned char *)nstr->orig + size;
+ for (s = s_ = (unsigned char *) nstr->orig, d = d_ = d0; s < e; s++) {
+ unsigned char c = *s;
+ switch (c >> 4) {
+ case 0 :
+ case 1 :
+ /* skip unprintable ascii */
+ if (cp > ctypes) { *(cp - 1) |= GRN_STR_BLANK; }
+ continue;
+ case 2 :
+ if (c == 0x20) {
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_STR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_STR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 3 :
+ *d = c;
+ ctype = (c <= 0x39) ? GRN_CHAR_DIGIT : GRN_CHAR_SYMBOL;
+ break;
+ case 4 :
+ *d = ('A' <= c) ? c + 0x20 : c;
+ ctype = (c == 0x40) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 5 :
+ *d = (c <= 'Z') ? c + 0x20 : c;
+ ctype = (c <= 0x5a) ? GRN_CHAR_ALPHA : GRN_CHAR_SYMBOL;
+ break;
+ case 6 :
+ *d = c;
+ ctype = (c == 0x60) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 7 :
+ *d = c;
+ ctype = (c <= 0x7a) ? GRN_CHAR_ALPHA : (c == 0x7f ? GRN_CHAR_OTHERS : GRN_CHAR_SYMBOL);
+ break;
+ default :
+ *d = c;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ d++;
+ length++;
+ if (cp) { *cp++ = ctype; }
+ if (ch) {
+ *ch++ = (int16_t)(s + 1 - s_);
+ s_ = s + 1;
+ while (++d_ < d) { *ch++ = 0; }
+ }
+ }
+ if (cp) { *cp = GRN_CHAR_NULL; }
+ *d = '\0';
+ nstr->length = length;
+ nstr->norm_blen = (size_t)(d - (unsigned char *)nstr->norm);
+ return GRN_SUCCESS;
+}
+
+/* use cp1252 as latin1 */
+inline static grn_rc
+normalize_latin1(grn_ctx *ctx, grn_str *nstr)
+{
+ int16_t *ch;
+ const unsigned char *s, *s_, *e;
+ unsigned char *d, *d0, *d_;
+ uint_least8_t *cp, *ctypes, ctype;
+ size_t size = nstr->orig_blen, length = 0;
+ int removeblankp = nstr->flags & GRN_STR_REMOVEBLANK;
+ if (!(nstr->norm = GRN_MALLOC(size + 1))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ d0 = (unsigned char *) nstr->norm;
+ if (nstr->flags & GRN_STR_WITH_CHECKS) {
+ if (!(nstr->checks = GRN_MALLOC(size * sizeof(int16_t) + 1))) {
+ GRN_FREE(nstr->norm);
+ nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ ch = nstr->checks;
+ if (nstr->flags & GRN_STR_WITH_CTYPES) {
+ if (!(nstr->ctypes = GRN_MALLOC(size + 1))) {
+ GRN_FREE(nstr->checks);
+ GRN_FREE(nstr->norm);
+ nstr->checks = NULL;
+ nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ cp = ctypes = nstr->ctypes;
+ e = (unsigned char *)nstr->orig + size;
+ for (s = s_ = (unsigned char *) nstr->orig, d = d_ = d0; s < e; s++) {
+ unsigned char c = *s;
+ switch (c >> 4) {
+ case 0 :
+ case 1 :
+ /* skip unprintable ascii */
+ if (cp > ctypes) { *(cp - 1) |= GRN_STR_BLANK; }
+ continue;
+ case 2 :
+ if (c == 0x20) {
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_STR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_STR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 3 :
+ *d = c;
+ ctype = (c <= 0x39) ? GRN_CHAR_DIGIT : GRN_CHAR_SYMBOL;
+ break;
+ case 4 :
+ *d = ('A' <= c) ? c + 0x20 : c;
+ ctype = (c == 0x40) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 5 :
+ *d = (c <= 'Z') ? c + 0x20 : c;
+ ctype = (c <= 0x5a) ? GRN_CHAR_ALPHA : GRN_CHAR_SYMBOL;
+ break;
+ case 6 :
+ *d = c;
+ ctype = (c == 0x60) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 7 :
+ *d = c;
+ ctype = (c <= 0x7a) ? GRN_CHAR_ALPHA : (c == 0x7f ? GRN_CHAR_OTHERS : GRN_CHAR_SYMBOL);
+ break;
+ case 8 :
+ if (c == 0x8a || c == 0x8c || c == 0x8e) {
+ *d = c + 0x10;
+ ctype = GRN_CHAR_ALPHA;
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 9 :
+ if (c == 0x9a || c == 0x9c || c == 0x9e || c == 0x9f) {
+ *d = (c == 0x9f) ? c + 0x60 : c;
+ ctype = GRN_CHAR_ALPHA;
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 0x0c :
+ *d = c + 0x20;
+ ctype = GRN_CHAR_ALPHA;
+ break;
+ case 0x0d :
+ *d = (c == 0xd7 || c == 0xdf) ? c : c + 0x20;
+ ctype = (c == 0xd7) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 0x0e :
+ *d = c;
+ ctype = GRN_CHAR_ALPHA;
+ break;
+ case 0x0f :
+ *d = c;
+ ctype = (c == 0xf7) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ default :
+ *d = c;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ d++;
+ length++;
+ if (cp) { *cp++ = ctype; }
+ if (ch) {
+ *ch++ = (int16_t)(s + 1 - s_);
+ s_ = s + 1;
+ while (++d_ < d) { *ch++ = 0; }
+ }
+ }
+ if (cp) { *cp = GRN_CHAR_NULL; }
+ *d = '\0';
+ nstr->length = length;
+ nstr->norm_blen = (size_t)(d - (unsigned char *)nstr->norm);
+ return GRN_SUCCESS;
+}
+
+inline static grn_rc
+normalize_koi8r(grn_ctx *ctx, grn_str *nstr)
+{
+ int16_t *ch;
+ const unsigned char *s, *s_, *e;
+ unsigned char *d, *d0, *d_;
+ uint_least8_t *cp, *ctypes, ctype;
+ size_t size = strlen(nstr->orig), length = 0;
+ int removeblankp = nstr->flags & GRN_STR_REMOVEBLANK;
+ if (!(nstr->norm = GRN_MALLOC(size + 1))) {
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ d0 = (unsigned char *) nstr->norm;
+ if (nstr->flags & GRN_STR_WITH_CHECKS) {
+ if (!(nstr->checks = GRN_MALLOC(size * sizeof(int16_t) + 1))) {
+ GRN_FREE(nstr->norm);
+ nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ ch = nstr->checks;
+ if (nstr->flags & GRN_STR_WITH_CTYPES) {
+ if (!(nstr->ctypes = GRN_MALLOC(size + 1))) {
+ GRN_FREE(nstr->checks);
+ GRN_FREE(nstr->norm);
+ nstr->checks = NULL;
+ nstr->norm = NULL;
+ return GRN_NO_MEMORY_AVAILABLE;
+ }
+ }
+ cp = ctypes = nstr->ctypes;
+ e = (unsigned char *)nstr->orig + size;
+ for (s = s_ = (unsigned char *) nstr->orig, d = d_ = d0; s < e; s++) {
+ unsigned char c = *s;
+ switch (c >> 4) {
+ case 0 :
+ case 1 :
+ /* skip unprintable ascii */
+ if (cp > ctypes) { *(cp - 1) |= GRN_STR_BLANK; }
+ continue;
+ case 2 :
+ if (c == 0x20) {
+ if (removeblankp) {
+ if (cp > ctypes) { *(cp - 1) |= GRN_STR_BLANK; }
+ continue;
+ } else {
+ *d = ' ';
+ ctype = GRN_STR_BLANK|GRN_CHAR_SYMBOL;
+ }
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_SYMBOL;
+ }
+ break;
+ case 3 :
+ *d = c;
+ ctype = (c <= 0x39) ? GRN_CHAR_DIGIT : GRN_CHAR_SYMBOL;
+ break;
+ case 4 :
+ *d = ('A' <= c) ? c + 0x20 : c;
+ ctype = (c == 0x40) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 5 :
+ *d = (c <= 'Z') ? c + 0x20 : c;
+ ctype = (c <= 0x5a) ? GRN_CHAR_ALPHA : GRN_CHAR_SYMBOL;
+ break;
+ case 6 :
+ *d = c;
+ ctype = (c == 0x60) ? GRN_CHAR_SYMBOL : GRN_CHAR_ALPHA;
+ break;
+ case 7 :
+ *d = c;
+ ctype = (c <= 0x7a) ? GRN_CHAR_ALPHA : (c == 0x7f ? GRN_CHAR_OTHERS : GRN_CHAR_SYMBOL);
+ break;
+ case 0x0a :
+ *d = c;
+ ctype = (c == 0xa3) ? GRN_CHAR_ALPHA : GRN_CHAR_OTHERS;
+ break;
+ case 0x0b :
+ if (c == 0xb3) {
+ *d = c - 0x10;
+ ctype = GRN_CHAR_ALPHA;
+ } else {
+ *d = c;
+ ctype = GRN_CHAR_OTHERS;
+ }
+ break;
+ case 0x0c :
+ case 0x0d :
+ *d = c;
+ ctype = GRN_CHAR_ALPHA;
+ break;
+ case 0x0e :
+ case 0x0f :
+ *d = c - 0x20;
+ ctype = GRN_CHAR_ALPHA;
+ break;
+ default :
+ *d = c;
+ ctype = GRN_CHAR_OTHERS;
+ break;
+ }
+ d++;
+ length++;
+ if (cp) { *cp++ = ctype; }
+ if (ch) {
+ *ch++ = (int16_t)(s + 1 - s_);
+ s_ = s + 1;
+ while (++d_ < d) { *ch++ = 0; }
+ }
+ }
+ if (cp) { *cp = GRN_CHAR_NULL; }
+ *d = '\0';
+ nstr->length = length;
+ nstr->norm_blen = (size_t)(d - (unsigned char *)nstr->norm);
+ return GRN_SUCCESS;
+}
+
+static grn_str *
+grn_fakenstr_open(grn_ctx *ctx, const char *str, size_t str_len, grn_encoding encoding, int flags)
+{
+ /* TODO: support GRN_STR_REMOVEBLANK flag and ctypes */
+ grn_str *nstr;
+ if (!(nstr = GRN_MALLOC(sizeof(grn_str)))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "memory allocation on grn_fakenstr_open failed !");
+ return NULL;
+ }
+ if (!(nstr->norm = GRN_MALLOC(str_len + 1))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "memory allocation for keyword on grn_snip_add_cond failed !");
+ GRN_FREE(nstr);
+ return NULL;
+ }
+ nstr->orig = str;
+ nstr->orig_blen = str_len;
+ memcpy(nstr->norm, str, str_len);
+ nstr->norm[str_len] = '\0';
+ nstr->norm_blen = str_len;
+ nstr->ctypes = NULL;
+ nstr->flags = flags;
+
+ if (flags & GRN_STR_WITH_CHECKS) {
+ int16_t f = 0;
+ unsigned char c;
+ size_t i;
+ if (!(nstr->checks = (int16_t *) GRN_MALLOC(sizeof(int16_t) * str_len))) {
+ GRN_FREE(nstr->norm);
+ GRN_FREE(nstr);
+ return NULL;
+ }
+ switch (encoding) {
+ case GRN_ENC_EUC_JP:
+ for (i = 0; i < str_len; i++) {
+ if (!f) {
+ c = (unsigned char) str[i];
+ f = ((c >= 0xa1U && c <= 0xfeU) || c == 0x8eU ? 2 : (c == 0x8fU ? 3 : 1)
+ );
+ nstr->checks[i] = f;
+ } else {
+ nstr->checks[i] = 0;
+ }
+ f--;
+ }
+ break;
+ case GRN_ENC_SJIS:
+ for (i = 0; i < str_len; i++) {
+ if (!f) {
+ c = (unsigned char) str[i];
+ f = (c >= 0x81U && ((c <= 0x9fU) || (c >= 0xe0U && c <= 0xfcU)) ? 2 : 1);
+ nstr->checks[i] = f;
+ } else {
+ nstr->checks[i] = 0;
+ }
+ f--;
+ }
+ break;
+ case GRN_ENC_UTF8:
+ for (i = 0; i < str_len; i++) {
+ if (!f) {
+ c = (unsigned char) str[i];
+ f = (c & 0x80U ? (c & 0x20U ? (c & 0x10U ? 4 : 3)
+ : 2)
+ : 1);
+ nstr->checks[i] = f;
+ } else {
+ nstr->checks[i] = 0;
+ }
+ f--;
+ }
+ break;
+ default:
+ for (i = 0; i < str_len; i++) {
+ nstr->checks[i] = 1;
+ }
+ break;
+ }
+ } else {
+ nstr->checks = NULL;
+ }
+ return nstr;
+}
+
+grn_str *
+grn_str_open_(grn_ctx *ctx, const char *str, unsigned int str_len, int flags, grn_encoding encoding)
+{
+ grn_rc rc;
+ grn_str *nstr;
+ if (!str || !str_len) { return NULL; }
+
+ if (!(flags & GRN_STR_NORMALIZE)) {
+ return grn_fakenstr_open(ctx, str, str_len, encoding, flags);
+ }
+
+ if (!(nstr = GRN_MALLOC(sizeof(grn_str)))) {
+ GRN_LOG(ctx, GRN_LOG_ALERT, "memory allocation on grn_str_open failed !");
+ return NULL;
+ }
+ nstr->orig = str;
+ nstr->orig_blen = str_len;
+ nstr->norm = NULL;
+ nstr->norm_blen = 0;
+ nstr->checks = NULL;
+ nstr->ctypes = NULL;
+ nstr->encoding = encoding;
+ nstr->flags = flags;
+ switch (encoding) {
+ case GRN_ENC_EUC_JP :
+ rc = normalize_euc(ctx, nstr);
+ break;
+ case GRN_ENC_UTF8 :
+#ifdef GRN_WITH_NFKC
+ rc = normalize_utf8(ctx, nstr);
+#else /* GRN_WITH_NFKC */
+ rc = normalize_none(ctx, nstr);
+#endif /* GRN_WITH_NFKC */
+ break;
+ case GRN_ENC_SJIS :
+ rc = normalize_sjis(ctx, nstr);
+ break;
+ case GRN_ENC_LATIN1 :
+ rc = normalize_latin1(ctx, nstr);
+ break;
+ case GRN_ENC_KOI8R :
+ rc = normalize_koi8r(ctx, nstr);
+ break;
+ default :
+ rc = normalize_none(ctx, nstr);
+ break;
+ }
+ if (rc) {
+ grn_str_close(ctx, nstr);
+ return NULL;
+ }
+ return nstr;
+}
+
+grn_str *
+grn_str_open(grn_ctx *ctx, const char *str, unsigned int str_len, int flags)
+{
+ return grn_str_open_(ctx, str, str_len, flags, ctx->encoding);
+}
+
+grn_rc
+grn_str_close(grn_ctx *ctx, grn_str *nstr)
+{
+ if (nstr) {
+ if (nstr->norm) { GRN_FREE(nstr->norm); }
+ if (nstr->ctypes) { GRN_FREE(nstr->ctypes); }
+ if (nstr->checks) { GRN_FREE(nstr->checks); }
+ GRN_FREE(nstr);
+ return GRN_SUCCESS;
+ } else {
+ return GRN_INVALID_ARGUMENT;
+ }
+}
+
+static const char *grn_enc_string[] = {
+ "default",
+ "none",
+ "euc_jp",
+ "utf8",
+ "sjis",
+ "latin1",
+ "koi8r"
+};
+
+const char *
+grn_encoding_to_string(grn_encoding enc)
+{
+ if (enc < (sizeof(grn_enc_string) / sizeof(char *))) {
+ return grn_enc_string[enc];
+ } else {
+ return "unknown";
+ }
+}
+
+grn_encoding
+grn_encoding_parse(const char *str)
+{
+ grn_encoding e = GRN_ENC_UTF8;
+ int i = sizeof(grn_enc_string) / sizeof(grn_enc_string[0]);
+ while (i--) {
+ if (!strcmp(str, grn_enc_string[i])) {
+ e = (grn_encoding)i;
+ }
+ }
+ return e;
+}
+
+size_t
+grn_str_len(grn_ctx *ctx, const char *str, grn_encoding encoding, const char **last)
+{
+ size_t len, tlen;
+ const char *p = NULL;
+ for (len = 0; ; len++) {
+ p = str;
+ if (!(tlen = grn_str_charlen(ctx, str, encoding))) {
+ break;
+ }
+ str += tlen;
+ }
+ if (last) { *last = p; }
+ return len;
+}
+
+int
+grn_isspace(const char *str, grn_encoding encoding)
+{
+ const unsigned char *s = (const unsigned char *) str;
+ if (!s) { return 0; }
+ switch (s[0]) {
+ case ' ' :
+ case '\f' :
+ case '\n' :
+ case '\r' :
+ case '\t' :
+ case '\v' :
+ return 1;
+ case 0x81 :
+ if (encoding == GRN_ENC_SJIS && s[1] == 0x40) { return 2; }
+ break;
+ case 0xA1 :
+ if (encoding == GRN_ENC_EUC_JP && s[1] == 0xA1) { return 2; }
+ break;
+ case 0xE3 :
+ if (encoding == GRN_ENC_UTF8 && s[1] == 0x80 && s[2] == 0x80) { return 3; }
+ break;
+ default :
+ break;
+ }
+ return 0;
+}
+
+int8_t
+grn_atoi8(const char *nptr, const char *end, const char **rest)
+{
+ const char *p = nptr;
+ int8_t v = 0, t, n = 0, o = 0;
+ if (p < end && *p == '-') {
+ p++;
+ n = 1;
+ o = 1;
+ }
+ while (p < end && *p >= '0' && *p <= '9') {
+ t = v * 10 - (*p - '0');
+ if (t > v || (!n && t == INT8_MIN)) { v = 0; break; }
+ v = t;
+ o = 0;
+ p++;
+ }
+ if (rest) { *rest = o ? nptr : p; }
+ return n ? v : -v;
+}
+
+uint8_t
+grn_atoui8(const char *nptr, const char *end, const char **rest)
+{
+ uint8_t v = 0, t;
+ while (nptr < end && *nptr >= '0' && *nptr <= '9') {
+ t = v * 10 + (*nptr - '0');
+ if (t < v) { v = 0; break; }
+ v = t;
+ nptr++;
+ }
+ if (rest) { *rest = nptr; }
+ return v;
+}
+
+int16_t
+grn_atoi16(const char *nptr, const char *end, const char **rest)
+{
+ const char *p = nptr;
+ int16_t v = 0, t, n = 0, o = 0;
+ if (p < end && *p == '-') {
+ p++;
+ n = 1;
+ o = 1;
+ }
+ while (p < end && *p >= '0' && *p <= '9') {
+ t = v * 10 - (*p - '0');
+ if (t > v || (!n && t == INT16_MIN)) { v = 0; break; }
+ v = t;
+ o = 0;
+ p++;
+ }
+ if (rest) { *rest = o ? nptr : p; }
+ return n ? v : -v;
+}
+
+uint16_t
+grn_atoui16(const char *nptr, const char *end, const char **rest)
+{
+ uint16_t v = 0, t;
+ while (nptr < end && *nptr >= '0' && *nptr <= '9') {
+ t = v * 10 + (*nptr - '0');
+ if (t < v) { v = 0; break; }
+ v = t;
+ nptr++;
+ }
+ if (rest) { *rest = nptr; }
+ return v;
+}
+
+int
+grn_atoi(const char *nptr, const char *end, const char **rest)
+{
+ const char *p = nptr;
+ int v = 0, t, n = 0, o = 0;
+ if (p < end && *p == '-') {
+ p++;
+ n = 1;
+ o = 1;
+ }
+ while (p < end && *p >= '0' && *p <= '9') {
+ t = v * 10 - (*p - '0');
+ if (t > v || (!n && t == INT32_MIN)) { v = 0; break; }
+ v = t;
+ o = 0;
+ p++;
+ }
+ if (rest) { *rest = o ? nptr : p; }
+ return n ? v : -v;
+}
+
+unsigned int
+grn_atoui(const char *nptr, const char *end, const char **rest)
+{
+ unsigned int v = 0, t;
+ while (nptr < end && *nptr >= '0' && *nptr <= '9') {
+ t = v * 10 + (*nptr - '0');
+ if (t < v) { v = 0; break; }
+ v = t;
+ nptr++;
+ }
+ if (rest) { *rest = nptr; }
+ return v;
+}
+
+int64_t
+grn_atoll(const char *nptr, const char *end, const char **rest)
+{
+ /* FIXME: INT_MIN is not supported */
+ const char *p = nptr;
+ int n = 0, o = 0;
+ int64_t v = 0, t;
+ if (p < end && *p == '-') {
+ p++;
+ n = 1;
+ o = 1;
+ }
+ while (p < end && *p >= '0' && *p <= '9') {
+ t = v * 10 + (*p - '0');
+ if (t < v) { v = 0; break; }
+ v = t;
+ o = 0;
+ p++;
+ }
+ if (rest) { *rest = o ? nptr : p; }
+ return n ? -v : v;
+}
+
+unsigned int
+grn_htoui(const char *nptr, const char *end, const char **rest)
+{
+ unsigned int v = 0, t;
+ while (nptr < end) {
+ switch (*nptr) {
+ case '0' :
+ case '1' :
+ case '2' :
+ case '3' :
+ case '4' :
+ case '5' :
+ case '6' :
+ case '7' :
+ case '8' :
+ case '9' :
+ t = v * 16 + (*nptr++ - '0');
+ break;
+ case 'a' :
+ case 'b' :
+ case 'c' :
+ case 'd' :
+ case 'e' :
+ case 'f' :
+ t = v * 16 + (*nptr++ - 'a') + 10;
+ break;
+ case 'A' :
+ case 'B' :
+ case 'C' :
+ case 'D' :
+ case 'E' :
+ case 'F' :
+ t = v * 16 + (*nptr++ - 'A') + 10;
+ break;
+ default :
+ v = 0; goto exit;
+ }
+ if (t < v) { v = 0; goto exit; }
+ v = t;
+ }
+exit :
+ if (rest) { *rest = nptr; }
+ return v;
+}
+
+void
+grn_itoh(unsigned int i, char *p, unsigned int len)
+{
+ static const char *hex = "0123456789ABCDEF";
+ p += len - 1;
+ while (len--) {
+ *p-- = hex[i & 0xf];
+ i >>= 4;
+ }
+}
+
+grn_rc
+grn_itoa(int i, char *p, char *end, char **rest)
+{
+ char *q;
+ if (p >= end) { return GRN_INVALID_ARGUMENT; }
+ q = p;
+ if (i < 0) {
+ *p++ = '-';
+ q = p;
+ if (i == INT_MIN) {
+ if (p >= end) { return GRN_INVALID_ARGUMENT; }
+ *p++ = (-(i % 10)) + '0';
+ i /= 10;
+ }
+ i = -i;
+ }
+ do {
+ if (p >= end) { return GRN_INVALID_ARGUMENT; }
+ *p++ = i % 10 + '0';
+ } while ((i /= 10) > 0);
+ if (rest) { *rest = p; }
+ for (p--; q < p; q++, p--) {
+ char t = *q;
+ *q = *p;
+ *p = t;
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_itoa_padded(int i, char *p, char *end, char ch)
+{
+ char *q;
+ if (p >= end) { return GRN_INVALID_ARGUMENT; }
+ if (i < 0) {
+ *p++ = '-';
+ if (i == INT_MIN) {
+ if (p >= end) { return GRN_INVALID_ARGUMENT; }
+ *p++ = (-(i % 10)) + '0';
+ i /= 10;
+ }
+ i = -i;
+ }
+ q = end - 1;
+ do {
+ if (q < p) { return GRN_INVALID_ARGUMENT; }
+ *q-- = i % 10 + '0';
+ } while ((i /= 10) > 0);
+ while (q >= p) {
+ *q-- = ch;
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_lltoa(int64_t i, char *p, char *end, char **rest)
+{
+ char *q;
+ if (p >= end) { return GRN_INVALID_ARGUMENT; }
+ q = p;
+ if (i < 0) {
+ *p++ = '-';
+ q = p;
+ if (i == INT64_MIN) {
+ *p++ = (-(i % 10)) + '0';
+ i /= 10;
+ }
+ i = -i;
+ }
+ do {
+ if (p >= end) { return GRN_INVALID_ARGUMENT; }
+ *p++ = i % 10 + '0';
+ } while ((i /= 10) > 0);
+ if (rest) { *rest = p; }
+ for (p--; q < p; q++, p--) {
+ char t = *q;
+ *q = *p;
+ *p = t;
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_ulltoa(uint64_t i, char *p, char *end, char **rest)
+{
+ char *q;
+ if (p >= end) { return GRN_INVALID_ARGUMENT; }
+ q = p;
+ do {
+ if (p >= end) { return GRN_INVALID_ARGUMENT; }
+ *p++ = i % 10 + '0';
+ } while ((i /= 10) > 0);
+ if (rest) { *rest = p; }
+ for (p--; q < p; q++, p--) {
+ char t = *q;
+ *q = *p;
+ *p = t;
+ }
+ return GRN_SUCCESS;
+}
+
+#define I2B(i) \
+ ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(i) & 0x3f])
+
+#define B2I(b) \
+ (((b) < '+' || 'z' < (b)) ? 0xff : "\x3e\xff\xff\xff\x3f\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\xff\xff\xff\xff\xff\xff\xff\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\xff\xff\xff\xff\xff\xff\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"[(b) - '+'])
+
+#define MASK 0x34d34d34
+
+char *
+grn_itob(grn_id id, char *p)
+{
+ id ^= MASK;
+ *p++ = I2B(id >> 24);
+ *p++ = I2B(id >> 18);
+ *p++ = I2B(id >> 12);
+ *p++ = I2B(id >> 6);
+ *p++ = I2B(id);
+ return p;
+}
+
+grn_id
+grn_btoi(char *b)
+{
+ uint8_t i;
+ grn_id id = 0;
+ int len = 5;
+ while (len--) {
+ char c = *b++;
+ if ((i = B2I(c)) == 0xff) { return 0; }
+ id = (id << 6) + i;
+ }
+ return id ^ MASK;
+}
+
+#define I2B32H(i) ("0123456789ABCDEFGHIJKLMNOPQRSTUV"[(i) & 0x1f])
+
+char *
+grn_lltob32h(int64_t i, char *p)
+{
+ uint64_t u = (uint64_t)i + 0x8000000000000000ULL;
+ *p++ = I2B32H(u >> 60);
+ *p++ = I2B32H(u >> 55);
+ *p++ = I2B32H(u >> 50);
+ *p++ = I2B32H(u >> 45);
+ *p++ = I2B32H(u >> 40);
+ *p++ = I2B32H(u >> 35);
+ *p++ = I2B32H(u >> 30);
+ *p++ = I2B32H(u >> 25);
+ *p++ = I2B32H(u >> 20);
+ *p++ = I2B32H(u >> 15);
+ *p++ = I2B32H(u >> 10);
+ *p++ = I2B32H(u >> 5);
+ *p++ = I2B32H(u);
+ return p;
+}
+
+char *
+grn_ulltob32h(uint64_t i, char *p)
+{
+ char lb = (i >> 59) & 0x10;
+ i += 0x8000000000000000ULL;
+ *p++ = lb + I2B32H(i >> 60);
+ *p++ = I2B32H(i >> 55);
+ *p++ = I2B32H(i >> 50);
+ *p++ = I2B32H(i >> 45);
+ *p++ = I2B32H(i >> 40);
+ *p++ = I2B32H(i >> 35);
+ *p++ = I2B32H(i >> 30);
+ *p++ = I2B32H(i >> 25);
+ *p++ = I2B32H(i >> 20);
+ *p++ = I2B32H(i >> 15);
+ *p++ = I2B32H(i >> 10);
+ *p++ = I2B32H(i >> 5);
+ *p++ = I2B32H(i);
+ return p;
+}
+
+grn_rc
+grn_aton(grn_ctx *ctx, const char *p, const char *end, const char **rest,
+ grn_obj *res)
+{
+ if (*p == '+') {
+ p++;
+ }
+
+ switch (*p) {
+ case '-' :
+ case '0' : case '1' : case '2' : case '3' : case '4' :
+ case '5' : case '6' : case '7' : case '8' : case '9' :
+ {
+ int64_t int64;
+ char rest_char;
+ int64 = grn_atoll(p, end, rest);
+ rest_char = **rest;
+ if (end == *rest) {
+ if ((int64_t)INT32_MIN <= int64 && int64 <= (int64_t)INT32_MAX) {
+ grn_obj_reinit(ctx, res, GRN_DB_INT32, 0);
+ GRN_INT32_SET(ctx, res, int64);
+ } else if ((int64_t)INT32_MAX < int64 && int64 <= (int64_t)UINT32_MAX) {
+ grn_obj_reinit(ctx, res, GRN_DB_UINT32, 0);
+ GRN_UINT32_SET(ctx, res, int64);
+ } else {
+ grn_obj_reinit(ctx, res, GRN_DB_INT64, 0);
+ GRN_INT64_SET(ctx, res, int64);
+ }
+ } else if (rest_char == '.' || rest_char == 'e' || rest_char == 'E' ||
+ (rest_char >= '0' && rest_char <= '9')) {
+ char *rest_float;
+ double d;
+ errno = 0;
+ d = strtod(p, &rest_float);
+ if (!errno && rest_float == end) {
+ grn_obj_reinit(ctx, res, GRN_DB_FLOAT, 0);
+ GRN_FLOAT_SET(ctx, res, d);
+ *rest = rest_float;
+ } else {
+ return GRN_INVALID_ARGUMENT;
+ }
+ }
+ }
+ break;
+ default :
+ return GRN_INVALID_ARGUMENT;
+ break;
+ }
+
+ return GRN_SUCCESS;
+}
+
+int
+grn_str_tok(const char *str, size_t str_len, char delim, const char **tokbuf, int buf_size, const char **rest)
+{
+ const char **tok = tokbuf, **tok_end = tokbuf + buf_size;
+ if (buf_size > 0) {
+ const char *str_end = str + str_len;
+ for (;;str++) {
+ if (str == str_end) {
+ *tok++ = str;
+ break;
+ }
+ if (delim == *str) {
+ // *str = '\0';
+ *tok++ = str;
+ if (tok == tok_end) { break; }
+ }
+ }
+ }
+ if (rest) { *rest = str; }
+ return tok - tokbuf;
+}
+
+inline static int
+op_getopt_flag(int *flags, const grn_str_getopt_opt *o,
+ int argc, char * const argv[], int i, const char *optvalue)
+{
+ switch (o->op) {
+ case GETOPT_OP_NONE:
+ break;
+ case GETOPT_OP_ON:
+ *flags |= o->flag;
+ break;
+ case GETOPT_OP_OFF:
+ *flags &= ~o->flag;
+ break;
+ case GETOPT_OP_UPDATE:
+ *flags = o->flag;
+ break;
+ default:
+ return i;
+ }
+ if (o->arg) {
+ if (optvalue) {
+ *o->arg = (char *)optvalue;
+ } else if (++i < argc) {
+ *o->arg = argv[i];
+ } else {
+ return -1;
+ }
+ }
+ return i;
+}
+
+int
+grn_str_getopt(int argc, char * const argv[], const grn_str_getopt_opt *opts,
+ int *flags)
+{
+ int i;
+ for (i = 1; i < argc; i++) {
+ const char * v = argv[i];
+ if (*v == '-') {
+ const grn_str_getopt_opt *o;
+ int found;
+ if (*++v == '-') {
+ const char *eq;
+ size_t len;
+ found = 0;
+ v++;
+ for (eq = v; *eq != '\0' && *eq != '='; eq++) {}
+ len = eq - v;
+ for (o = opts; o->opt != '\0' || o->longopt != NULL; o++) {
+ if (o->longopt && strlen(o->longopt) == len &&
+ !memcmp(v, o->longopt, len)) {
+ i = op_getopt_flag(flags, o, argc, argv, i,
+ (*eq == '\0' ? NULL : eq + 1));
+ if (i < 0) {
+ fprintf(stderr, "%s: option '--%s' needs argument.\n", argv[0], o->longopt);
+ return -1;
+ }
+ found = 1;
+ break;
+ }
+ }
+ if (!found) { goto exit; }
+ } else {
+ const char *p;
+ for (p = v; *p; p++) {
+ found = 0;
+ for (o = opts; o->opt != '\0' || o->longopt != NULL; o++) {
+ if (o->opt && *p == o->opt) {
+ i = op_getopt_flag(flags, o, argc, argv, i, NULL);
+ if (i < 0) {
+ fprintf(stderr, "%s: option '-%c' needs argument.\n", argv[0], *p);
+ return -1;
+ }
+ found = 1;
+ break;
+ }
+ }
+ if (!found) { goto exit; }
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ return i;
+exit:
+ fprintf(stderr, "%s: cannot recognize option '%s'.\n", argv[0], argv[i]);
+ return -1;
+}
+
+#define UNIT_SIZE (1 << 12)
+#define UNIT_MASK (UNIT_SIZE - 1)
+
+int grn_bulk_margin_size = 0;
+
+grn_rc
+grn_bulk_resize(grn_ctx *ctx, grn_obj *buf, unsigned int newsize)
+{
+ char *head;
+ unsigned int rounded_newsize;
+ newsize += grn_bulk_margin_size + 1;
+ if (GRN_BULK_OUTP(buf)) {
+ rounded_newsize = (newsize + (UNIT_MASK)) & ~UNIT_MASK;
+ if (rounded_newsize < newsize) { return GRN_NOT_ENOUGH_SPACE; }
+ newsize = rounded_newsize;
+ head = buf->u.b.head - (buf->u.b.head ? grn_bulk_margin_size : 0);
+ if (!(head = GRN_REALLOC(head, newsize))) { return GRN_NO_MEMORY_AVAILABLE; }
+ buf->u.b.curr = head + grn_bulk_margin_size + GRN_BULK_VSIZE(buf);
+ buf->u.b.head = head + grn_bulk_margin_size;
+ buf->u.b.tail = head + newsize;
+ } else {
+ if (newsize > GRN_BULK_BUFSIZE) {
+ rounded_newsize = (newsize + (UNIT_MASK)) & ~UNIT_MASK;
+ if (rounded_newsize < newsize) { return GRN_NOT_ENOUGH_SPACE; }
+ newsize = rounded_newsize;
+ if (!(head = GRN_MALLOC(newsize))) { return GRN_NO_MEMORY_AVAILABLE; }
+ memcpy(head, GRN_BULK_HEAD(buf), GRN_BULK_VSIZE(buf));
+ buf->u.b.curr = head + grn_bulk_margin_size + GRN_BULK_VSIZE(buf);
+ buf->u.b.head = head + grn_bulk_margin_size;
+ buf->u.b.tail = head + newsize;
+ buf->header.impl_flags |= GRN_OBJ_OUTPLACE;
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_bulk_reinit(grn_ctx *ctx, grn_obj *buf, unsigned int size)
+{
+ GRN_BULK_REWIND(buf);
+ return grn_bulk_resize(ctx, buf, size);
+}
+
+grn_rc
+grn_bulk_write(grn_ctx *ctx, grn_obj *buf, const char *str, unsigned int len)
+{
+ grn_rc rc = GRN_SUCCESS;
+ char *curr;
+ if (GRN_BULK_REST(buf) < len) {
+ if ((rc = grn_bulk_resize(ctx, buf, GRN_BULK_VSIZE(buf) + len))) { return rc; }
+ }
+ curr = GRN_BULK_CURR(buf);
+ memcpy(curr, str, len);
+ GRN_BULK_INCR_LEN(buf, len);
+ return rc;
+}
+
+grn_rc
+grn_bulk_write_from(grn_ctx *ctx, grn_obj *bulk,
+ const char *str, unsigned int from, unsigned int len)
+{
+ grn_rc rc = grn_bulk_truncate(ctx, bulk, from);
+ if (!rc) { rc = grn_bulk_write(ctx, bulk, str, len); }
+ return rc;
+}
+
+grn_rc
+grn_bulk_reserve(grn_ctx *ctx, grn_obj *buf, unsigned int len)
+{
+ grn_rc rc = GRN_SUCCESS;
+ if (GRN_BULK_REST(buf) < len) {
+ if ((rc = grn_bulk_resize(ctx, buf, GRN_BULK_VSIZE(buf) + len))) { return rc; }
+ }
+ return rc;
+}
+
+grn_rc
+grn_bulk_space(grn_ctx *ctx, grn_obj *buf, unsigned int len)
+{
+ grn_rc rc = grn_bulk_reserve(ctx, buf, len);
+ if (!rc) {
+ GRN_BULK_INCR_LEN(buf, len);
+ }
+ return rc;
+}
+
+static grn_rc
+grn_bulk_space_clear(grn_ctx *ctx, grn_obj *buf, unsigned int len)
+{
+ grn_rc rc = grn_bulk_reserve(ctx, buf, len);
+ if (!rc) {
+ memset(GRN_BULK_CURR(buf), 0, len);
+ GRN_BULK_INCR_LEN(buf, len);
+ }
+ return rc;
+}
+
+grn_rc
+grn_bulk_truncate(grn_ctx *ctx, grn_obj *bulk, unsigned int len)
+{
+ if (GRN_BULK_OUTP(bulk)) {
+ if ((bulk->u.b.tail - bulk->u.b.head) < len) {
+ return grn_bulk_space_clear(ctx, bulk, len);
+ } else {
+ bulk->u.b.curr = bulk->u.b.head + len;
+ }
+ } else {
+ if (GRN_BULK_BUFSIZE < len) {
+ return grn_bulk_space_clear(ctx, bulk, len);
+ } else {
+ bulk->header.flags &= ~GRN_BULK_BUFSIZE_MAX;
+ bulk->header.flags += len;
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_text_itoa(grn_ctx *ctx, grn_obj *buf, int i)
+{
+ grn_rc rc = GRN_SUCCESS;
+ for (;;) {
+ char *curr = GRN_BULK_CURR(buf);
+ char *tail = GRN_BULK_TAIL(buf);
+ if (grn_itoa(i, curr, tail, &curr)) {
+ if ((rc = grn_bulk_resize(ctx, buf, GRN_BULK_WSIZE(buf) + UNIT_SIZE))) { return rc; }
+ } else {
+ GRN_BULK_SET_CURR(buf, curr);
+ break;
+ }
+ }
+ return rc;
+}
+
+grn_rc
+grn_text_itoa_padded(grn_ctx *ctx, grn_obj *buf, int i, char ch, unsigned int len)
+{
+ grn_rc rc = GRN_SUCCESS;
+ char *curr;
+ if ((rc = grn_bulk_reserve(ctx, buf, len))) { return rc; }
+ curr = GRN_BULK_CURR(buf);
+ if (!grn_itoa_padded(i, curr, curr + len, ch)) {
+ GRN_BULK_SET_CURR(buf, curr + len);
+ }
+ return rc;
+}
+
+grn_rc
+grn_text_lltoa(grn_ctx *ctx, grn_obj *buf, long long int i)
+{
+ grn_rc rc = GRN_SUCCESS;
+ for (;;) {
+ char *curr = GRN_BULK_CURR(buf);
+ char *tail = GRN_BULK_TAIL(buf);
+ if (grn_lltoa(i, curr, tail, &curr)) {
+ if ((rc = grn_bulk_resize(ctx, buf, GRN_BULK_WSIZE(buf) + UNIT_SIZE))) { return rc; }
+ } else {
+ GRN_BULK_SET_CURR(buf, curr);
+ break;
+ }
+ }
+ return rc;
+}
+
+grn_rc
+grn_text_ulltoa(grn_ctx *ctx, grn_obj *buf, unsigned long long int i)
+{
+ grn_rc rc = GRN_SUCCESS;
+ for (;;) {
+ char *curr = GRN_BULK_CURR(buf);
+ char *tail = GRN_BULK_TAIL(buf);
+ if (grn_ulltoa(i, curr, tail, &curr)) {
+ if ((rc = grn_bulk_resize(ctx, buf, GRN_BULK_WSIZE(buf) + UNIT_SIZE))) { return rc; }
+ } else {
+ GRN_BULK_SET_CURR(buf, curr);
+ break;
+ }
+ }
+ return rc;
+}
+
+inline static void
+ftoa_(grn_ctx *ctx, grn_obj *buf, double d)
+{
+ char *curr;
+ size_t len;
+#define DIGIT_NUMBER 15
+ grn_bulk_reserve(ctx, buf, DIGIT_NUMBER + 1);
+ curr = GRN_BULK_CURR(buf);
+ len = sprintf(curr, "%#.*g", DIGIT_NUMBER, d);
+#undef DIGIT_NUMBER
+ if (curr[len - 1] == '.') {
+ GRN_BULK_INCR_LEN(buf, len);
+ GRN_TEXT_PUTC(ctx, buf, '0');
+ } else {
+ char *p, *q;
+ curr[len] = '\0';
+ if ((p = strchr(curr, 'e'))) {
+ for (q = p; *(q - 2) != '.' && *(q - 1) == '0'; q--) { len--; }
+ memmove(q, p, curr + len - q);
+ } else {
+ for (q = curr + len; *(q - 2) != '.' && *(q - 1) == '0'; q--) { len--; }
+ }
+ GRN_BULK_INCR_LEN(buf, len);
+ }
+}
+
+grn_rc
+grn_text_ftoa(grn_ctx *ctx, grn_obj *buf, double d)
+{
+ grn_rc rc = GRN_SUCCESS;
+ if (GRN_BULK_REST(buf) < 32) {
+ if ((rc = grn_bulk_resize(ctx, buf, GRN_BULK_VSIZE(buf) + 32))) { return rc; }
+ }
+#ifdef HAVE_FPCLASSIFY
+ switch (fpclassify(d)) {
+ case FP_NAN :
+ GRN_TEXT_PUTS(ctx, buf, "#<nan>");
+ break;
+ case FP_INFINITE :
+ GRN_TEXT_PUTS(ctx, buf, d > 0 ? "#i1/0" : "#i-1/0");
+ break;
+ default :
+ ftoa_(ctx, buf, d);
+ break;
+ }
+#else /* HAVE_FPCLASSIFY */
+ if (d == d) {
+ if (d != 0 && ((d / 2.0) == d)) {
+ GRN_TEXT_PUTS(ctx, buf, d > 0 ? "#i1/0" : "#i-1/0");
+ } else {
+ ftoa_(ctx, buf, d);
+ }
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "#<nan>");
+ }
+#endif /* HAVE_FPCLASSIFY */
+ return rc;
+}
+
+grn_rc
+grn_text_itoh(grn_ctx *ctx, grn_obj *buf, int i, unsigned int len)
+{
+ grn_rc rc = GRN_SUCCESS;
+ if (GRN_BULK_REST(buf) < len) {
+ if ((rc = grn_bulk_resize(ctx, buf, GRN_BULK_VSIZE(buf) + len))) { return rc; }
+ }
+ grn_itoh(i, GRN_BULK_CURR(buf), len);
+ GRN_BULK_INCR_LEN(buf, len);
+ return rc;
+}
+
+grn_rc
+grn_text_itob(grn_ctx *ctx, grn_obj *buf, grn_id id)
+{
+ size_t len = 5;
+ grn_rc rc = GRN_SUCCESS;
+ if (GRN_BULK_REST(buf) < len) {
+ if ((rc = grn_bulk_resize(ctx, buf, GRN_BULK_VSIZE(buf) + len))) { return rc; }
+ }
+ grn_itob(id, GRN_BULK_CURR(buf));
+ GRN_BULK_INCR_LEN(buf, len);
+ return rc;
+}
+
+grn_rc
+grn_text_lltob32h(grn_ctx *ctx, grn_obj *buf, long long int i)
+{
+ size_t len = 13;
+ grn_rc rc = GRN_SUCCESS;
+ if (GRN_BULK_REST(buf) < len) {
+ if ((rc = grn_bulk_resize(ctx, buf, GRN_BULK_VSIZE(buf) + len))) { return rc; }
+ }
+ grn_lltob32h(i, GRN_BULK_CURR(buf));
+ GRN_BULK_INCR_LEN(buf, len);
+ return rc;
+}
+
+grn_rc
+grn_text_esc(grn_ctx *ctx, grn_obj *buf, const char *s, unsigned int len)
+{
+ const char *e;
+ unsigned int l;
+ grn_rc rc = GRN_SUCCESS;
+
+ GRN_TEXT_PUTC(ctx, buf, '"');
+ for (e = s + len; s < e; s += l) {
+ if (!(l = grn_charlen(ctx, s, e))) { break; }
+ if (l == 1) {
+ switch (*s) {
+ case '"' :
+ grn_bulk_write(ctx, buf, "\\\"", 2);
+ break;
+ case '\\' :
+ grn_bulk_write(ctx, buf, "\\\\", 2);
+ break;
+ case '\b' :
+ grn_bulk_write(ctx, buf, "\\b", 2);
+ break;
+ case '\f' :
+ grn_bulk_write(ctx, buf, "\\f", 2);
+ break;
+ case '\n' :
+ grn_bulk_write(ctx, buf, "\\n", 2);
+ break;
+ case '\r' :
+ grn_bulk_write(ctx, buf, "\\r", 2);
+ break;
+ case '\t' :
+ grn_bulk_write(ctx, buf, "\\t", 2);
+ break;
+ case '\x00': case '\x01': case '\x02': case '\x03': case '\x04': case '\x05':
+ case '\x06': case '\x07': case '\x0b': case '\x0e': case '\x0f': case '\x10':
+ case '\x11': case '\x12': case '\x13': case '\x14': case '\x15': case '\x16':
+ case '\x17': case '\x18': case '\x19': case '\x1a': case '\x1b': case '\x1c':
+ case '\x1d': case '\x1e': case '\x1f': case '\x7f':
+ if (!(rc = grn_bulk_write(ctx, buf, "\\u", 2))) {
+ if ((rc = grn_text_itoh(ctx, buf, *s, 4))) {
+ GRN_BULK_INCR_LEN(buf, -2);
+ return rc;
+ }
+ } else {
+ return rc;
+ }
+ break;
+ default :
+ GRN_TEXT_PUTC(ctx, buf, *s);
+ }
+ } else if (l == 3) {
+ if (*s == '\xe2' && *(s + 1) == '\x80') {
+ switch (*(s + 2)) {
+ case '\xa8': /* \u2028 */
+ grn_bulk_write(ctx, buf, "\\u2028", 6);
+ break;
+ case '\xa9': /* \u2029 */
+ grn_bulk_write(ctx, buf, "\\u2029", 6);
+ break;
+ default:
+ grn_bulk_write(ctx, buf, s, l);
+ }
+ } else {
+ grn_bulk_write(ctx, buf, s, l);
+ }
+ } else {
+ grn_bulk_write(ctx, buf, s, l);
+ }
+ }
+ GRN_TEXT_PUTC(ctx, buf, '"');
+ return rc;
+}
+
+grn_rc
+grn_text_escape_xml(grn_ctx *ctx, grn_obj *buf, const char *s, unsigned int len)
+{
+ const char *e;
+ unsigned int l;
+ grn_rc rc = GRN_SUCCESS;
+
+ for (e = s + len; s < e; s += l) {
+ if (!(l = grn_charlen(ctx, s, e))) { break; }
+ if (l == 1) {
+ switch (*s) {
+ case '"' :
+ grn_bulk_write(ctx, buf, "&quot;", 6);
+ break;
+ case '<' :
+ grn_bulk_write(ctx, buf, "&lt;", 4);
+ break;
+ case '>' :
+ grn_bulk_write(ctx, buf, "&gt;", 4);
+ break;
+ case '&' :
+ grn_bulk_write(ctx, buf, "&amp;", 5);
+ break;
+ default :
+ GRN_TEXT_PUTC(ctx, buf, *s);
+ }
+ } else {
+ grn_bulk_write(ctx, buf, s, l);
+ }
+ }
+ return rc;
+}
+
+#define TOK_ESC (0x80)
+
+const char *
+grn_text_unesc_tok(grn_ctx *ctx, grn_obj *buf, const char *s, const char *e, char *tok_type)
+{
+ const char *p;
+ unsigned int len;
+ uint8_t stat = GRN_TOK_VOID;
+ for (p = s; p < e; p += len) {
+ if (!(len = grn_charlen(ctx, p, e))) {
+ p = e;
+ stat &= ~TOK_ESC;
+ goto exit;
+ }
+ switch (stat) {
+ case GRN_TOK_VOID :
+ if (*p == ' ') { continue; }
+ switch (*p) {
+ case '"' :
+ stat = GRN_TOK_STRING;
+ break;
+ case '\'' :
+ stat = GRN_TOK_QUOTE;
+ break;
+ case ')' :
+ case '(' :
+ GRN_TEXT_PUT(ctx, buf, p, len);
+ p += len;
+ stat = GRN_TOK_SYMBOL;
+ goto exit;
+ case '\\' :
+ stat = GRN_TOK_SYMBOL|TOK_ESC;
+ break;
+ default :
+ stat = GRN_TOK_SYMBOL;
+ GRN_TEXT_PUT(ctx, buf, p, len);
+ break;
+ }
+ break;
+ case GRN_TOK_SYMBOL :
+ if (*p == ' ') { goto exit; }
+ switch (*p) {
+ case '\'' :
+ case '"' :
+ case ')' :
+ case '(' :
+ goto exit;
+ case '\\' :
+ stat |= TOK_ESC;
+ break;
+ default :
+ GRN_TEXT_PUT(ctx, buf, p, len);
+ break;
+ }
+ break;
+ case GRN_TOK_STRING :
+ switch (*p) {
+ case '"' :
+ p += len;
+ goto exit;
+ case '\\' :
+ stat |= TOK_ESC;
+ break;
+ default :
+ GRN_TEXT_PUT(ctx, buf, p, len);
+ break;
+ }
+ break;
+ case GRN_TOK_QUOTE :
+ switch (*p) {
+ case '\'' :
+ p += len;
+ goto exit;
+ case '\\' :
+ stat |= TOK_ESC;
+ break;
+ default :
+ GRN_TEXT_PUT(ctx, buf, p, len);
+ break;
+ }
+ break;
+ case GRN_TOK_SYMBOL|TOK_ESC :
+ case GRN_TOK_STRING|TOK_ESC :
+ case GRN_TOK_QUOTE|TOK_ESC :
+ switch (*p) {
+ case 'b' :
+ GRN_TEXT_PUTC(ctx, buf, '\b');
+ break;
+ case 'f' :
+ GRN_TEXT_PUTC(ctx, buf, '\f');
+ break;
+ case 'n' :
+ GRN_TEXT_PUTC(ctx, buf, '\n');
+ break;
+ case 'r' :
+ GRN_TEXT_PUTC(ctx, buf, '\r');
+ break;
+ case 't' :
+ GRN_TEXT_PUTC(ctx, buf, '\t');
+ break;
+ default :
+ GRN_TEXT_PUT(ctx, buf, p, len);
+ break;
+ }
+ stat &= ~TOK_ESC;
+ break;
+ }
+ }
+exit :
+ *tok_type = stat;
+ return p;
+}
+
+grn_rc
+grn_text_benc(grn_ctx *ctx, grn_obj *buf, unsigned int v)
+{
+ grn_rc rc = GRN_SUCCESS;
+ uint8_t *p;
+ if (GRN_BULK_REST(buf) < 5) {
+ if ((rc = grn_bulk_resize(ctx, buf, GRN_BULK_VSIZE(buf) + 5))) { return rc; }
+ }
+ p = (uint8_t *)GRN_BULK_CURR(buf);
+ GRN_B_ENC(v, p);
+ GRN_BULK_SET_CURR(buf, (char *)p);
+ return rc;
+}
+
+/* 0x00 - 0x7f */
+static const int_least8_t urlenc_tbl[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 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
+};
+
+grn_rc
+grn_text_urlenc(grn_ctx *ctx, grn_obj *buf, const char *s, unsigned int len)
+{
+ const char *e, c = '%';
+ for (e = s + len; s < e; s++) {
+ if (*s < 0 || urlenc_tbl[(int)*s]) {
+ if (!grn_bulk_write(ctx, buf, &c, 1)) {
+ if (grn_text_itoh(ctx, buf, *s, 2)) {
+ GRN_BULK_INCR_LEN(buf, -1);
+ }
+ }
+ } else {
+ GRN_TEXT_PUTC(ctx, buf, *s);
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+static const char *weekdays[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+static const char *months[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+grn_rc
+grn_text_time2rfc1123(grn_ctx *ctx, grn_obj *bulk, int sec)
+{
+ time_t tsec;
+ struct tm *t;
+#ifdef HAVE_GMTIME_R
+ struct tm tm;
+ tsec = (time_t)sec;
+ t = gmtime_r(&tsec, &tm);
+#else /* HAVE_GMTIME_R */
+ tsec = (time_t)sec;
+ t = gmtime(&tsec);
+#endif /* HAVE_GMTIME_R */
+ if (t) {
+ GRN_TEXT_SET(ctx, bulk, weekdays[t->tm_wday], 3);
+ GRN_TEXT_PUTS(ctx, bulk, ", ");
+ grn_text_itoa_padded(ctx, bulk, t->tm_mday, '0', 2);
+ GRN_TEXT_PUTS(ctx, bulk, " ");
+ GRN_TEXT_PUT(ctx, bulk, months[t->tm_mon], 3);
+ GRN_TEXT_PUTS(ctx, bulk, " ");
+ grn_text_itoa(ctx, bulk, t->tm_year + 1900);
+ GRN_TEXT_PUTS(ctx, bulk, " ");
+ grn_text_itoa_padded(ctx, bulk, t->tm_hour, '0', 2);
+ GRN_TEXT_PUTS(ctx, bulk, ":");
+ grn_text_itoa_padded(ctx, bulk, t->tm_min, '0', 2);
+ GRN_TEXT_PUTS(ctx, bulk, ":");
+ grn_text_itoa_padded(ctx, bulk, t->tm_sec, '0', 2);
+ GRN_TEXT_PUTS(ctx, bulk, " GMT");
+ } else {
+ GRN_TEXT_SETS(ctx, bulk, "Mon, 16 Mar 1980 20:40:00 GMT");
+ }
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_text_printf(grn_ctx *ctx, grn_obj *bulk, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ grn_text_vprintf(ctx, bulk, format, args);
+ va_end(args);
+
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_text_vprintf(grn_ctx *ctx, grn_obj *bulk, const char *format, va_list args)
+{
+ int rest_size, written_size;
+
+ {
+ va_list copied_args;
+
+ rest_size = GRN_BULK_REST(bulk);
+ va_copy(copied_args, args);
+ written_size = vsnprintf(GRN_BULK_CURR(bulk), rest_size,
+ format, copied_args);
+ va_end(copied_args);
+ }
+
+ if (written_size >= rest_size) {
+ grn_rc rc;
+ int required_size = written_size + 1; /* "+ 1" for terminate '\0'. */
+
+ rc = grn_bulk_reserve(ctx, bulk, GRN_BULK_VSIZE(bulk) + required_size);
+ if (rc) {
+ return rc;
+ }
+ written_size = vsnprintf(GRN_BULK_CURR(bulk), required_size,
+ format, args);
+ }
+
+ if (written_size < 0) {
+ return GRN_INVALID_ARGUMENT;
+ }
+
+ GRN_BULK_INCR_LEN(bulk, written_size);
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_bulk_fin(grn_ctx *ctx, grn_obj *buf)
+{
+ if (!(buf->header.impl_flags & GRN_OBJ_REFER)) {
+ if (GRN_BULK_OUTP(buf) && buf->u.b.head) {
+ GRN_REALLOC(buf->u.b.head - grn_bulk_margin_size, 0);
+ }
+ }
+ buf->header.flags = 0;
+ buf->header.impl_flags &= ~GRN_OBJ_DO_SHALLOW_COPY;
+ buf->u.b.head = NULL;
+ buf->u.b.curr = NULL;
+ buf->u.b.tail = NULL;
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_substring(grn_ctx *ctx, char **str, char **str_end, int start, int end, grn_encoding encoding)
+{
+ int i;
+ size_t l;
+ char *s = *str, *e = *str_end;
+ for (i = 0; s < e; i++, s += l) {
+ if (i == start) { *str = s; }
+ if (!(l = grn_charlen(ctx, s, e))) {
+ return GRN_INVALID_ARGUMENT;
+ }
+ if (i == end) {
+ *str_end = s;
+ break;
+ }
+ }
+ return GRN_SUCCESS;
+}
+
+static void
+grn_text_atoj(grn_ctx *ctx, grn_obj *bulk, grn_obj *obj, grn_id id)
+{
+ uint32_t vs;
+ grn_obj buf;
+ if (obj->header.type == GRN_ACCESSOR) {
+ grn_accessor *a = (grn_accessor *)obj;
+ GRN_TEXT_INIT(&buf, 0);
+ for (;;) {
+ GRN_BULK_REWIND(&buf);
+ switch (a->action) {
+ case GRN_ACCESSOR_GET_ID :
+ GRN_UINT32_PUT(ctx, &buf, id);
+ buf.header.domain = GRN_DB_UINT32;
+ break;
+ case GRN_ACCESSOR_GET_KEY :
+ grn_table_get_key2(ctx, a->obj, id, &buf);
+ buf.header.domain = DB_OBJ(a->obj)->header.domain;
+ break;
+ case GRN_ACCESSOR_GET_VALUE :
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ buf.header.domain = GRN_DB_INT32; /* fix me */
+ break;
+ case GRN_ACCESSOR_GET_SCORE :
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
+ GRN_INT32_PUT(ctx, &buf, ri->score);
+ }
+ buf.header.domain = GRN_DB_INT32;
+ break;
+ case GRN_ACCESSOR_GET_NSUBRECS :
+ {
+ grn_rset_recinfo *ri = (grn_rset_recinfo *)grn_obj_get_value_(ctx, a->obj, id, &vs);
+ GRN_INT32_PUT(ctx, &buf, ri->n_subrecs);
+ }
+ buf.header.domain = GRN_DB_INT32;
+ break;
+ case GRN_ACCESSOR_GET_COLUMN_VALUE :
+ if ((a->obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR) {
+ if (a->next) {
+ grn_id *idp;
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ idp = (grn_id *)GRN_BULK_HEAD(&buf);
+ GRN_TEXT_PUTC(ctx, bulk, '[');
+ for (vs = GRN_BULK_VSIZE(&buf) / sizeof(grn_id); vs--; idp++) {
+ grn_text_atoj(ctx, bulk, (grn_obj *)a->next, *idp);
+ if (vs) { GRN_TEXT_PUTC(ctx, bulk, ','); }
+ }
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ } else {
+ grn_text_atoj(ctx, bulk, a->obj, id);
+ }
+ goto exit;
+ } else {
+ grn_obj_get_value(ctx, a->obj, id, &buf);
+ }
+ break;
+ case GRN_ACCESSOR_GET_DB_OBJ :
+ /* todo */
+ break;
+ case GRN_ACCESSOR_LOOKUP :
+ /* todo */
+ break;
+ case GRN_ACCESSOR_FUNCALL :
+ /* todo */
+ break;
+ }
+ if (a->next) {
+ a = a->next;
+ id = *((grn_id *)GRN_BULK_HEAD(&buf));
+ } else {
+ break;
+ }
+ }
+ } else {
+ switch (obj->header.type) {
+ case GRN_COLUMN_FIX_SIZE :
+ GRN_VALUE_FIX_SIZE_INIT(&buf, 0, DB_OBJ(obj)->range);
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ if ((obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR) {
+ grn_obj *range = grn_ctx_at(ctx, DB_OBJ(obj)->range);
+ if (range->header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ GRN_VALUE_VAR_SIZE_INIT(&buf, GRN_OBJ_VECTOR, DB_OBJ(obj)->range);
+ } else {
+ GRN_VALUE_FIX_SIZE_INIT(&buf, GRN_OBJ_VECTOR, DB_OBJ(obj)->range);
+ }
+ } else {
+ GRN_VALUE_VAR_SIZE_INIT(&buf, 0, DB_OBJ(obj)->range);
+ }
+ break;
+ case GRN_COLUMN_INDEX :
+ GRN_UINT32_INIT(&buf, 0);
+ break;
+ default:
+ GRN_TEXT_INIT(&buf, 0);
+ break;
+ }
+ grn_obj_get_value(ctx, obj, id, &buf);
+ }
+ grn_text_otoj(ctx, bulk, &buf, NULL);
+exit :
+ grn_obj_close(ctx, &buf);
+}
+
+grn_rc
+grn_text_otoj(grn_ctx *ctx, grn_obj *bulk, grn_obj *obj, grn_obj_format *format)
+{
+ grn_obj buf;
+ GRN_TEXT_INIT(&buf, 0);
+ switch (obj->header.type) {
+ case GRN_BULK :
+ switch (obj->header.domain) {
+ case GRN_DB_VOID :
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ grn_text_esc(ctx, bulk, GRN_BULK_HEAD(obj), GRN_BULK_VSIZE(obj));
+ break;
+ case GRN_DB_BOOL :
+ if (*((unsigned char *)GRN_BULK_HEAD(obj))) {
+ GRN_TEXT_PUTS(ctx, bulk, "true");
+ } else {
+ GRN_TEXT_PUTS(ctx, bulk, "false");
+ }
+ break;
+ case GRN_DB_INT8 :
+ grn_text_itoa(ctx, bulk, GRN_BULK_VSIZE(obj) ? GRN_INT8_VALUE(obj) : 0);
+ break;
+ case GRN_DB_UINT8 :
+ grn_text_lltoa(ctx, bulk, GRN_BULK_VSIZE(obj) ? GRN_UINT8_VALUE(obj) : 0);
+ break;
+ case GRN_DB_INT16 :
+ grn_text_itoa(ctx, bulk, GRN_BULK_VSIZE(obj) ? GRN_INT16_VALUE(obj) : 0);
+ break;
+ case GRN_DB_UINT16 :
+ grn_text_lltoa(ctx, bulk, GRN_BULK_VSIZE(obj) ? GRN_UINT16_VALUE(obj) : 0);
+ break;
+ case GRN_DB_INT32 :
+ grn_text_itoa(ctx, bulk, GRN_BULK_VSIZE(obj) ? GRN_INT32_VALUE(obj) : 0);
+ break;
+ case GRN_DB_UINT32 :
+ grn_text_lltoa(ctx, bulk, GRN_BULK_VSIZE(obj) ? GRN_UINT32_VALUE(obj) : 0);
+ break;
+ case GRN_DB_INT64 :
+ grn_text_lltoa(ctx, bulk, GRN_BULK_VSIZE(obj) ? GRN_INT64_VALUE(obj) : 0);
+ break;
+ case GRN_DB_UINT64 :
+ grn_text_ulltoa(ctx, bulk, GRN_BULK_VSIZE(obj) ? GRN_UINT64_VALUE(obj) : 0);
+ break;
+ case GRN_DB_FLOAT :
+ grn_text_ftoa(ctx, bulk, GRN_BULK_VSIZE(obj) ? GRN_FLOAT_VALUE(obj) : 0);
+ break;
+ case GRN_DB_TIME :
+ {
+ double dv = *((int64_t *)GRN_BULK_HEAD(obj));
+ dv /= 1000000.0;
+ grn_text_ftoa(ctx, bulk, dv);
+ }
+ break;
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ if (GRN_BULK_VSIZE(obj) == sizeof(grn_geo_point)) {
+ grn_geo_point *gp = (grn_geo_point *)GRN_BULK_HEAD(obj);
+ GRN_TEXT_PUTC(ctx, bulk, '"');
+ grn_text_itoa(ctx, bulk, gp->latitude);
+ GRN_TEXT_PUTC(ctx, bulk, 'x');
+ grn_text_itoa(ctx, bulk, gp->longitude);
+ GRN_TEXT_PUTC(ctx, bulk, '"');
+ } else {
+ GRN_TEXT_PUTS(ctx, bulk, "\"\"");
+ }
+ break;
+ default :
+ if (format) {
+ int j;
+ int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
+ grn_id id = GRN_RECORD_VALUE(obj);
+ grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
+ if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
+ GRN_TEXT_PUTS(ctx, bulk, "[");
+ for (j = 0; j < ncolumns; j++) {
+ grn_id range_id;
+ if (j) { GRN_TEXT_PUTC(ctx, bulk, ','); }
+ GRN_TEXT_PUTS(ctx, bulk, "[");
+ GRN_BULK_REWIND(&buf);
+ grn_column_name_(ctx, columns[j], &buf);
+ grn_text_otoj(ctx, bulk, &buf, NULL);
+ GRN_TEXT_PUTC(ctx, bulk, ',');
+ /* column range */
+ range_id = grn_obj_get_range(ctx, columns[j]);
+ if (range_id == GRN_ID_NIL) {
+ GRN_TEXT_PUTS(ctx, bulk, "null");
+ } else {
+ int name_len;
+ grn_obj *range_obj;
+ char name_buf[GRN_TABLE_MAX_KEY_SIZE];
+
+ range_obj = grn_ctx_at(ctx, range_id);
+ name_len = grn_obj_name(ctx, range_obj, name_buf,
+ GRN_TABLE_MAX_KEY_SIZE);
+ GRN_BULK_REWIND(&buf);
+ GRN_TEXT_PUT(ctx, &buf, name_buf, name_len);
+ grn_text_otoj(ctx, bulk, &buf, NULL);
+ }
+ GRN_TEXT_PUTS(ctx, bulk, "]");
+ }
+ GRN_TEXT_PUTS(ctx, bulk, "],");
+ }
+ GRN_TEXT_PUTC(ctx, bulk, '[');
+ for (j = 0; j < ncolumns; j++) {
+ if (j) { GRN_TEXT_PUTC(ctx, bulk, ','); }
+ grn_text_atoj(ctx, bulk, columns[j], id);
+ }
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ } else {
+ if (GRN_BULK_VSIZE(obj) == 0) {
+ GRN_TEXT_PUTS(ctx, bulk, "null");
+ } else {
+ grn_obj *table = grn_ctx_at(ctx, obj->header.domain);
+ grn_id id = GRN_RECORD_VALUE(obj);
+ if (table && table->header.type != GRN_TABLE_NO_KEY) {
+ /* todo : temporal patch. grn_table_at() is kinda costful... */
+ if (grn_table_at(ctx, table, id)) {
+ grn_obj *accessor = grn_obj_column(ctx, table,
+ GRN_COLUMN_NAME_KEY,
+ GRN_COLUMN_NAME_KEY_LEN);
+ if (accessor) {
+ grn_obj_get_value(ctx, accessor, id, &buf);
+ grn_obj_unlink(ctx, accessor);
+ }
+ }
+ grn_text_otoj(ctx, bulk, &buf, format);
+ } else {
+ grn_text_lltoa(ctx, bulk, id);
+ }
+ }
+ }
+ }
+ break;
+ case GRN_UVECTOR :
+ if (format) {
+ int i, j;
+ grn_id *v = (grn_id *)GRN_BULK_HEAD(obj), *ve = (grn_id *)GRN_BULK_CURR(obj);
+ int ncolumns = GRN_BULK_VSIZE(&format->columns) / sizeof(grn_obj *);
+ grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
+ GRN_TEXT_PUTS(ctx, bulk, "[[");
+ grn_text_itoa(ctx, bulk, ve - v);
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ if (v < ve) {
+ if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
+ GRN_TEXT_PUTS(ctx, bulk, ",[");
+ for (j = 0; j < ncolumns; j++) {
+ grn_id range_id;
+ if (j) { GRN_TEXT_PUTC(ctx, bulk, ','); }
+ GRN_TEXT_PUTS(ctx, bulk, "[");
+ GRN_BULK_REWIND(&buf);
+ grn_column_name_(ctx, columns[j], &buf);
+ grn_text_otoj(ctx, bulk, &buf, NULL);
+ GRN_TEXT_PUTC(ctx, bulk, ',');
+ /* column range */
+ range_id = grn_obj_get_range(ctx, columns[j]);
+ if (range_id == GRN_ID_NIL) {
+ GRN_TEXT_PUTS(ctx, bulk, "null");
+ } else {
+ int name_len;
+ grn_obj *range_obj;
+ char name_buf[GRN_TABLE_MAX_KEY_SIZE];
+
+ range_obj = grn_ctx_at(ctx, range_id);
+ name_len = grn_obj_name(ctx, range_obj, name_buf,
+ GRN_TABLE_MAX_KEY_SIZE);
+ GRN_BULK_REWIND(&buf);
+ GRN_TEXT_PUT(ctx, &buf, name_buf, name_len);
+ grn_text_otoj(ctx, bulk, &buf, NULL);
+ }
+ GRN_TEXT_PUTS(ctx, bulk, "]");
+ }
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ }
+ for (i = 0;; i++) {
+ GRN_TEXT_PUTS(ctx, bulk, ",[");
+ for (j = 0; j < ncolumns; j++) {
+ if (j) { GRN_TEXT_PUTC(ctx, bulk, ','); }
+ GRN_BULK_REWIND(&buf);
+ grn_obj_get_value(ctx, columns[j], *v, &buf);
+ grn_text_otoj(ctx, bulk, &buf, NULL);
+ }
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ v++;
+ if (v < ve) {
+ GRN_TEXT_PUTC(ctx, bulk, ',');
+ } else {
+ break;
+ }
+ }
+ }
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ } else {
+ grn_obj *range = grn_ctx_at(ctx, obj->header.domain);
+ if (range && range->header.type == GRN_TYPE) {
+ grn_id value_size = ((struct _grn_type *)range)->obj.range;
+ char *v = (char *)GRN_BULK_HEAD(obj),
+ *ve = (char *)GRN_BULK_CURR(obj);
+ GRN_TEXT_PUTC(ctx, bulk, '[');
+ if (v < ve) {
+ for (;;) {
+ grn_obj value;
+ GRN_OBJ_INIT(&value, GRN_BULK, 0, obj->header.domain);
+ grn_bulk_write_from(ctx, &value, v, 0, value_size);
+ grn_text_otoj(ctx, bulk, &value, NULL);
+
+ v += value_size;
+ if (v < ve) {
+ GRN_TEXT_PUTC(ctx, bulk, ',');
+ } else {
+ break;
+ }
+ }
+ }
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ } else {
+ grn_id *v = (grn_id *)GRN_BULK_HEAD(obj),
+ *ve = (grn_id *)GRN_BULK_CURR(obj);
+ GRN_TEXT_PUTC(ctx, bulk, '[');
+ if (v < ve) {
+ for (;;) {
+ if (range->header.type != GRN_TABLE_NO_KEY) {
+ grn_obj key;
+ GRN_OBJ_INIT(&key, GRN_BULK, 0, range->header.domain);
+ grn_table_get_key2(ctx, range, *v, &key);
+ grn_text_otoj(ctx, bulk, &key, NULL);
+ GRN_OBJ_FIN(ctx, &key);
+ } else {
+ grn_text_lltoa(ctx, bulk, *v);
+ }
+ v++;
+ if (v < ve) {
+ GRN_TEXT_PUTC(ctx, bulk, ',');
+ } else {
+ break;
+ }
+ }
+ }
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ }
+ }
+ break;
+ case GRN_VECTOR :
+ if (obj->header.domain == GRN_DB_VOID) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid obj->header.domain");
+ } else {
+ unsigned int i, n;
+ grn_obj value;
+ grn_obj weight;
+ grn_bool with_weight;
+
+ GRN_VOID_INIT(&value);
+ GRN_UINT32_INIT(&weight, 0);
+ with_weight = (format && format->flags & GRN_OBJ_FORMAT_WITH_WEIGHT);
+ n = grn_vector_size(ctx, obj);
+ GRN_TEXT_PUTC(ctx, bulk, '[');
+ for (i = 0; i < n; i++) {
+ const char *_value;
+ unsigned int _weight, length;
+ grn_id domain;
+ if (i) { GRN_TEXT_PUTC(ctx, bulk, ','); }
+
+ length = grn_vector_get_element(ctx, obj, i,
+ &_value, &_weight, &domain);
+ if (domain != GRN_DB_VOID) {
+ grn_obj_reinit(ctx, &value, domain, 0);
+ } else {
+ grn_obj_reinit(ctx, &value, obj->header.domain, 0);
+ }
+ if (with_weight) {
+ GRN_TEXT_PUTC(ctx, bulk, '{');
+ }
+ grn_bulk_write(ctx, &value, _value, length);
+ grn_text_otoj(ctx, bulk, &value, NULL);
+ if (with_weight) {
+ GRN_TEXT_PUTC(ctx, bulk, ':');
+ GRN_UINT32_SET(ctx, &weight, _weight);
+ grn_text_otoj(ctx, bulk, &weight, NULL);
+ GRN_TEXT_PUTC(ctx, bulk, '}');
+ }
+ }
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ }
+ break;
+ case GRN_PVECTOR :
+ if (format) {
+ ERR(GRN_FUNCTION_NOT_IMPLEMENTED,
+ "cannot print GRN_PVECTOR using grn_obj_format");
+ } else {
+ unsigned int i, n;
+ GRN_TEXT_PUTC(ctx, bulk, '[');
+ n = GRN_BULK_VSIZE(obj) / sizeof(grn_obj *);
+ for (i = 0; i < n; i++) {
+ grn_obj *value;
+
+ if (i) { GRN_TEXT_PUTC(ctx, bulk, ','); }
+ value = GRN_PTR_VALUE_AT(obj, i);
+ grn_text_otoj(ctx, bulk, value, NULL);
+ }
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ if (format) {
+ int i, j;
+ int ncolumns = GRN_BULK_VSIZE(&format->columns)/sizeof(grn_obj *);
+ grn_obj **columns = (grn_obj **)GRN_BULK_HEAD(&format->columns);
+ grn_table_cursor *tc = grn_table_cursor_open(ctx, obj, NULL, 0, NULL, 0,
+ format->offset, format->limit,
+ GRN_CURSOR_ASCENDING);
+ if (!tc) { ERRCLR(ctx); }
+ GRN_TEXT_PUTS(ctx, bulk, "[[");
+ grn_text_itoa(ctx, bulk, format->nhits);
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ if (format->flags & GRN_OBJ_FORMAT_WITH_COLUMN_NAMES) {
+ GRN_TEXT_PUTS(ctx, bulk, ",[");
+ for (j = 0; j < ncolumns; j++) {
+ grn_id range_id;
+ if (j) { GRN_TEXT_PUTC(ctx, bulk, ','); }
+ GRN_TEXT_PUTS(ctx, bulk, "[");
+ GRN_BULK_REWIND(&buf);
+ grn_column_name_(ctx, columns[j], &buf);
+ grn_text_otoj(ctx, bulk, &buf, NULL);
+ GRN_TEXT_PUTC(ctx, bulk, ',');
+ /* column range */
+ range_id = grn_obj_get_range(ctx, columns[j]);
+ if (range_id == GRN_ID_NIL) {
+ GRN_TEXT_PUTS(ctx, bulk, "null");
+ } else {
+ int name_len;
+ grn_obj *range_obj;
+ char name_buf[GRN_TABLE_MAX_KEY_SIZE];
+
+ range_obj = grn_ctx_at(ctx, range_id);
+ name_len = grn_obj_name(ctx, range_obj, name_buf,
+ GRN_TABLE_MAX_KEY_SIZE);
+ GRN_BULK_REWIND(&buf);
+ GRN_TEXT_PUT(ctx, &buf, name_buf, name_len);
+ grn_text_otoj(ctx, bulk, &buf, NULL);
+ }
+ GRN_TEXT_PUTS(ctx, bulk, "]");
+ }
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ }
+ if (tc) {
+ grn_id id;
+ for (i = 0; (id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL; i++) {
+ GRN_TEXT_PUTS(ctx, bulk, ",[");
+ for (j = 0; j < ncolumns; j++) {
+ if (j) { GRN_TEXT_PUTC(ctx, bulk, ','); }
+ grn_text_atoj(ctx, bulk, columns[j], id);
+ }
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ } else {
+ int i;
+ grn_id id;
+ grn_obj *column = grn_obj_column(ctx, obj,
+ GRN_COLUMN_NAME_KEY,
+ GRN_COLUMN_NAME_KEY_LEN);
+ grn_table_cursor *tc = grn_table_cursor_open(ctx, obj, NULL, 0, NULL, 0,
+ 0, -1, GRN_CURSOR_ASCENDING);
+ GRN_TEXT_PUTC(ctx, bulk, '[');
+ if (tc) {
+ for (i = 0; (id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL; i++) {
+ if (i) { GRN_TEXT_PUTC(ctx, bulk, ','); }
+ GRN_BULK_REWIND(&buf);
+ grn_obj_get_value(ctx, column, id, &buf);
+ grn_text_esc(ctx, bulk, GRN_BULK_HEAD(&buf), GRN_BULK_VSIZE(&buf));
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ GRN_TEXT_PUTC(ctx, bulk, ']');
+ grn_obj_unlink(ctx, column);
+ }
+ break;
+ }
+ grn_obj_close(ctx, &buf);
+ return GRN_SUCCESS;
+}
+
+const char *
+grn_text_urldec(grn_ctx *ctx, grn_obj *buf, const char *p, const char *e, char d)
+{
+ while (p < e) {
+ if (*p == d) {
+ p++; break;
+ } else if (*p == '%' && p + 3 <= e) {
+ const char *r;
+ unsigned int c = grn_htoui(p + 1, p + 3, &r);
+ if (p + 3 == r) {
+ GRN_TEXT_PUTC(ctx, buf, c);
+ p += 3;
+ } else {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "invalid %% sequence (%c%c)", p[1], p[2]);
+ GRN_TEXT_PUTC(ctx, buf, '%');
+ p += 1;
+ }
+ } else {
+ GRN_TEXT_PUTC(ctx, buf, *p);
+ p++;
+ }
+ }
+ return p;
+}
+
+const char *
+grn_text_cgidec(grn_ctx *ctx, grn_obj *buf, const char *p, const char *e,
+ const char *delimiters)
+{
+ while (p < e) {
+ grn_bool found_delimiter = GRN_FALSE;
+ const char *delimiter;
+ for (delimiter = delimiters; *delimiter; delimiter++) {
+ if (*p == *delimiter) {
+ found_delimiter = GRN_TRUE;
+ break;
+ }
+ }
+ if (found_delimiter) {
+ p++;
+ break;
+ }
+
+ if (*p == '+') {
+ GRN_TEXT_PUTC(ctx, buf, ' ');
+ p++;
+ } else if (*p == '%' && p + 3 <= e) {
+ const char *r;
+ unsigned int c = grn_htoui(p + 1, p + 3, &r);
+ if (p + 3 == r) {
+ GRN_TEXT_PUTC(ctx, buf, c);
+ p += 3;
+ } else {
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "invalid %% sequence (%c%c)", p[1], p[2]);
+ GRN_TEXT_PUTC(ctx, buf, '%');
+ p += 1;
+ }
+ } else {
+ GRN_TEXT_PUTC(ctx, buf, *p);
+ p++;
+ }
+ }
+ return p;
+}
+
+void
+grn_str_url_path_normalize(grn_ctx *ctx, const char *path, size_t path_len,
+ char *buf, size_t buf_len)
+{
+ char *b = buf, *be = buf + buf_len - 1;
+ const char *p = path, *pe = path + path_len, *pc;
+
+ if (buf_len < 2) { return; }
+
+ while (p < pe) {
+ for (pc = p; pc < pe && *pc != '/'; pc++) {}
+ if (*p == '.') {
+ if (pc == p + 2 && *(p + 1) == '.') {
+ /* '..' */
+ if (b - buf >= 2) {
+ for (b -= 2; *b != '/' && b >= buf; b--) {}
+ }
+ if (*b == '/') {
+ b++;
+ ERR(GRN_INVALID_ARGUMENT, "parent path doesn't exist.");
+ }
+ p = pc + 1;
+ continue;
+ } else if (pc == p + 1) {
+ /* '.' */
+ p = pc + 1;
+ continue;
+ }
+ }
+ if (be - b >= pc - p) {
+ memcpy(b, p, (pc - p));
+ b += pc - p;
+ p = pc;
+ if (p < pe && *pc == '/' && be > b) {
+ *b++ = '/';
+ p++;
+ }
+ }
+ }
+ *b = '\0';
+}
+
+grn_rc
+grn_text_fgets(grn_ctx *ctx, grn_obj *buf, FILE *fp)
+{
+ size_t len;
+ grn_rc rc = GRN_END_OF_DATA;
+ for (;;) {
+ grn_bulk_reserve(ctx, buf, BUFSIZ);
+ if (!fgets(GRN_BULK_CURR(buf), BUFSIZ, fp)) { break; }
+ if (!(len = strlen(GRN_BULK_CURR(buf)))) { break; }
+ GRN_BULK_INCR_LEN(buf, len);
+ rc = GRN_SUCCESS;
+ if (GRN_BULK_CURR(buf)[-1] == '\n') { break; }
+ }
+ return rc;
+}
+
+grn_bool
+grn_bulk_is_zero(grn_ctx *ctx, grn_obj *obj)
+{
+ const char *v = GRN_BULK_HEAD(obj);
+ unsigned int s = GRN_BULK_VSIZE(obj);
+ for (; s; s--, v++) {
+ if (*v) { return GRN_FALSE; }
+ }
+ return GRN_TRUE;
+}
+
diff --git a/storage/mroonga/vendor/groonga/lib/str.h b/storage/mroonga/vendor/groonga/lib/str.h
new file mode 100644
index 00000000000..069250a70d8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/str.h
@@ -0,0 +1,129 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_STR_H
+#define GRN_STR_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifndef GRN_NFKC_H
+#include <groonga/nfkc.h>
+#endif /* GRN_NFKC_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ GETOPT_OP_NONE = 0,
+ GETOPT_OP_ON,
+ GETOPT_OP_OFF,
+ GETOPT_OP_UPDATE
+} grn_str_getopt_op;
+
+typedef struct {
+ const char opt; /* ends opt == 0 && longopt == NULL */
+ const char *longopt;
+ const char **arg; /* if NULL, no arg are required */
+ int flag;
+ grn_str_getopt_op op;
+} grn_str_getopt_opt;
+
+GRN_API size_t grn_str_len(grn_ctx *ctx, const char *str, grn_encoding encoding, const char **last);
+
+#define GRN_STR_BLANK 0x80
+#define GRN_STR_ISBLANK(c) (c & 0x80)
+#define GRN_STR_CTYPE(c) (c & 0x7f)
+
+GRN_API int grn_isspace(const char *s, grn_encoding encoding);
+int8_t grn_atoi8(const char *nptr, const char *end, const char **rest);
+uint8_t grn_atoui8(const char *nptr, const char *end, const char **rest);
+int16_t grn_atoi16(const char *nptr, const char *end, const char **rest);
+uint16_t grn_atoui16(const char *nptr, const char *end, const char **rest);
+GRN_API int grn_atoi(const char *nptr, const char *end, const char **rest);
+GRN_API unsigned int grn_atoui(const char *nptr, const char *end, const char **rest);
+unsigned int grn_htoui(const char *nptr, const char *end, const char **rest);
+GRN_API int64_t grn_atoll(const char *nptr, const char *end, const char **rest);
+grn_rc grn_itoa(int i, char *p, char *end, char **rest);
+grn_rc grn_lltoa(int64_t i, char *p, char *end, char **rest);
+grn_rc grn_ulltoa(uint64_t i, char *p, char *end, char **rest);
+GRN_API grn_rc grn_aton(grn_ctx *ctx, const char *p, const char *end, const char **rest, grn_obj *res);
+
+GRN_API void grn_itoh(unsigned int i, char *p, unsigned int len);
+int grn_str_tok(const char *str, size_t str_len, char delim, const char **tokbuf, int buf_size, const char **rest);
+GRN_API int grn_str_getopt(int argc, char * const argv[], const grn_str_getopt_opt *opts, int *flags);
+
+extern int grn_str_margin_size;
+
+char *grn_itob(grn_id id, char *p);
+grn_id grn_btoi(char *b);
+
+grn_rc grn_substring(grn_ctx *ctx, char **str, char **str_end, int start, int end, grn_encoding encoding);
+
+GRN_API int grn_charlen_(grn_ctx *ctx, const char *str, const char *end, grn_encoding encoding);
+GRN_API grn_str *grn_str_open_(grn_ctx *ctx, const char *str, unsigned int str_len, int flags, grn_encoding encoding);
+
+#define GRN_BULK_INCR_LEN(buf,len) do {\
+ if (GRN_BULK_OUTP(buf)) {\
+ (buf)->u.b.curr += (len);\
+ } else {\
+ (buf)->header.flags += (len);\
+ }\
+} while (0)
+
+#define GRN_BULK_SET_CURR(buf,p) do {\
+ if (GRN_BULK_OUTP(buf)) {\
+ (buf)->u.b.curr = (char *)(p);\
+ } else {\
+ (buf)->header.flags = (char *)(p) - GRN_BULK_HEAD(buf);\
+ }\
+} while (0)
+
+grn_rc grn_text_ulltoa(grn_ctx *ctx, grn_obj *buf, unsigned long long int i);
+
+GRN_API const char *grn_text_cgidec(grn_ctx *ctx, grn_obj *buf,
+ const char *p, const char *e,
+ const char *delimiters);
+
+#define GRN_TOK_VOID (0x00)
+#define GRN_TOK_SYMBOL (0x01)
+#define GRN_TOK_STRING (0x02)
+#define GRN_TOK_QUOTE (0x03)
+
+GRN_API const char *grn_text_unesc_tok(grn_ctx *ctx, grn_obj *buf,
+ const char *p, const char *e,
+ char *tok_type);
+
+GRN_API void grn_str_url_path_normalize(grn_ctx *ctx,
+ const char *path, size_t path_len,
+ char *buf, size_t buf_len);
+
+#define GRN_OBJ_FORMAT_XML_ELEMENT_MASK (0x01<<1)
+#define GRN_OBJ_FORMAT_XML_ELEMENT_RESULTSET (0x00<<1)
+#define GRN_OBJ_FORMAT_XML_ELEMENT_NAVIGATIONENTRY (0x01<<1)
+
+#include <stdio.h>
+GRN_API grn_rc grn_text_fgets(grn_ctx *ctx, grn_obj *buf, FILE *fp);
+
+grn_bool grn_bulk_is_zero(grn_ctx *ctx, grn_obj *obj);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_STR_H */
diff --git a/storage/mroonga/vendor/groonga/lib/string.c b/storage/mroonga/vendor/groonga/lib/string.c
new file mode 100644
index 00000000000..f0fcf561b5e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/string.c
@@ -0,0 +1,406 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "groonga_in.h"
+#include <string.h>
+#include "string_in.h"
+#include "normalizer_in.h"
+#include "str.h"
+#include "util.h"
+
+#include <groonga/tokenizer.h>
+
+static grn_string *
+grn_fake_string_open(grn_ctx *ctx, grn_string *string)
+{
+ /* TODO: support GRN_STRING_REMOVE_BLANK flag and ctypes */
+ grn_string *nstr = string;
+ const char *str;
+ unsigned int str_len;
+
+ str = nstr->original;
+ str_len = nstr->original_length_in_bytes;
+
+ if (!(nstr->normalized = GRN_MALLOC(str_len + 1))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[strinig][fake] failed to allocate normalized text space");
+ grn_string_close(ctx, (grn_obj *)nstr);
+ return NULL;
+ }
+
+ if (nstr->flags & GRN_STRING_REMOVE_TOKENIZED_DELIMITER &&
+ ctx->encoding == GRN_ENC_UTF8) {
+ int char_length;
+ const char *source_current = str;
+ const char *source_end = str + str_len;
+ char *destination = nstr->normalized;
+ unsigned int destination_length = 0;
+ while ((char_length = grn_charlen(ctx, source_current, source_end)) > 0) {
+ if (!grn_tokenizer_is_tokenized_delimiter(ctx,
+ source_current, char_length,
+ ctx->encoding)) {
+ memcpy(destination, source_current, char_length);
+ destination += char_length;
+ destination_length += char_length;
+ }
+ source_current += char_length;
+ }
+ nstr->normalized[destination_length] = '\0';
+ nstr->normalized_length_in_bytes = destination_length;
+ } else {
+ memcpy(nstr->normalized, str, str_len);
+ nstr->normalized[str_len] = '\0';
+ nstr->normalized_length_in_bytes = str_len;
+ }
+
+ if (nstr->flags & GRN_STRING_WITH_CHECKS) {
+ int16_t f = 0;
+ unsigned char c;
+ size_t i;
+ if (!(nstr->checks = (int16_t *) GRN_MALLOC(sizeof(int16_t) * str_len))) {
+ grn_string_close(ctx, (grn_obj *)nstr);
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[strinig][fake] failed to allocate checks space");
+ return NULL;
+ }
+ switch (nstr->encoding) {
+ case GRN_ENC_EUC_JP:
+ for (i = 0; i < str_len; i++) {
+ if (!f) {
+ c = (unsigned char) str[i];
+ f = ((c >= 0xa1U && c <= 0xfeU) || c == 0x8eU ? 2 : (c == 0x8fU ? 3 : 1)
+ );
+ nstr->checks[i] = f;
+ } else {
+ nstr->checks[i] = 0;
+ }
+ f--;
+ }
+ break;
+ case GRN_ENC_SJIS:
+ for (i = 0; i < str_len; i++) {
+ if (!f) {
+ c = (unsigned char) str[i];
+ f = (c >= 0x81U && ((c <= 0x9fU) || (c >= 0xe0U && c <= 0xfcU)) ? 2 : 1);
+ nstr->checks[i] = f;
+ } else {
+ nstr->checks[i] = 0;
+ }
+ f--;
+ }
+ break;
+ case GRN_ENC_UTF8:
+ for (i = 0; i < str_len; i++) {
+ if (!f) {
+ c = (unsigned char) str[i];
+ f = (c & 0x80U ? (c & 0x20U ? (c & 0x10U ? 4 : 3)
+ : 2)
+ : 1);
+ nstr->checks[i] = f;
+ } else {
+ nstr->checks[i] = 0;
+ }
+ f--;
+ }
+ break;
+ default:
+ for (i = 0; i < str_len; i++) {
+ nstr->checks[i] = 1;
+ }
+ break;
+ }
+ }
+ return nstr;
+}
+
+grn_obj *
+grn_string_open_(grn_ctx *ctx, const char *str, unsigned int str_len,
+ grn_obj *normalizer, int flags, grn_encoding encoding)
+{
+ grn_string *string;
+ grn_obj *obj;
+ grn_bool is_normalizer_auto;
+
+ if (!str || !str_len) {
+ return NULL;
+ }
+
+ string = GRN_MALLOCN(grn_string, 1);
+ if (!string) {
+ GRN_LOG(ctx, GRN_LOG_ALERT,
+ "[string][open] failed to allocate memory");
+ return NULL;
+ }
+
+ obj = (grn_obj *)string;
+ GRN_OBJ_INIT(obj, GRN_STRING, GRN_OBJ_ALLOCATED, GRN_ID_NIL);
+ string->original = str;
+ string->original_length_in_bytes = str_len;
+ string->normalized = NULL;
+ string->normalized_length_in_bytes = 0;
+ string->n_characters = 0;
+ string->checks = NULL;
+ string->ctypes = NULL;
+ string->encoding = encoding;
+ string->flags = flags;
+
+ if (!normalizer) {
+ return (grn_obj *)grn_fake_string_open(ctx, string);
+ }
+
+ is_normalizer_auto = (normalizer == GRN_NORMALIZER_AUTO);
+ if (is_normalizer_auto) {
+ normalizer = grn_ctx_get(ctx, GRN_NORMALIZER_AUTO_NAME, -1);
+ }
+
+ /* TODO: check rc */
+ grn_normalizer_normalize(ctx, normalizer, (grn_obj *)string);
+ if (ctx->rc) {
+ grn_obj_close(ctx, obj);
+ obj = NULL;
+ }
+
+ if (is_normalizer_auto) {
+ grn_obj_unlink(ctx, normalizer);
+ }
+
+ return obj;
+}
+
+grn_obj *
+grn_string_open(grn_ctx *ctx, const char *str, unsigned int str_len,
+ grn_obj *normalizer, int flags)
+{
+ return grn_string_open_(ctx, str, str_len, normalizer, flags, ctx->encoding);
+}
+
+grn_rc
+grn_string_get_original(grn_ctx *ctx, grn_obj *string,
+ const char **original,
+ unsigned int *length_in_bytes)
+{
+ grn_rc rc;
+ grn_string *string_ = (grn_string *)string;
+ GRN_API_ENTER;
+ if (string_) {
+ if (original) { *original = string_->original; }
+ if (length_in_bytes) {
+ *length_in_bytes = string_->original_length_in_bytes;
+ }
+ rc = GRN_SUCCESS;
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ GRN_API_RETURN(rc);
+}
+
+int
+grn_string_get_flags(grn_ctx *ctx, grn_obj *string)
+{
+ int flags = 0;
+ grn_string *string_ = (grn_string *)string;
+ GRN_API_ENTER;
+ if (string_) {
+ flags = string_->flags;
+ }
+ GRN_API_RETURN(flags);
+}
+
+grn_rc
+grn_string_get_normalized(grn_ctx *ctx, grn_obj *string,
+ const char **normalized,
+ unsigned int *length_in_bytes,
+ unsigned int *n_characters)
+{
+ grn_rc rc;
+ grn_string *string_ = (grn_string *)string;
+ GRN_API_ENTER;
+ if (string_) {
+ if (normalized) { *normalized = string_->normalized; }
+ if (length_in_bytes) {
+ *length_in_bytes = string_->normalized_length_in_bytes;
+ }
+ if (n_characters) { *n_characters = string_->n_characters; }
+ rc = GRN_SUCCESS;
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_rc
+grn_string_set_normalized(grn_ctx *ctx, grn_obj *string,
+ char *normalized, unsigned int length_in_bytes,
+ unsigned int n_characters)
+{
+ grn_rc rc;
+ grn_string *string_ = (grn_string *)string;
+ GRN_API_ENTER;
+ if (string_) {
+ if (string_->normalized) { GRN_FREE(string_->normalized); }
+ string_->normalized = normalized;
+ string_->normalized_length_in_bytes = length_in_bytes;
+ string_->n_characters = n_characters;
+ rc = GRN_SUCCESS;
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ GRN_API_RETURN(rc);
+}
+
+const short *
+grn_string_get_checks(grn_ctx *ctx, grn_obj *string)
+{
+ int16_t *checks = NULL;
+ grn_string *string_ = (grn_string *)string;
+ GRN_API_ENTER;
+ if (string_) {
+ checks = string_->checks;
+ } else {
+ checks = NULL;
+ }
+ GRN_API_RETURN(checks);
+}
+
+grn_rc
+grn_string_set_checks(grn_ctx *ctx, grn_obj *string, short *checks)
+{
+ grn_rc rc;
+ grn_string *string_ = (grn_string *)string;
+ GRN_API_ENTER;
+ if (string_) {
+ if (string_->checks) { GRN_FREE(string_->checks); }
+ string_->checks = checks;
+ rc = GRN_SUCCESS;
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ GRN_API_RETURN(rc);
+}
+
+const unsigned char *
+grn_string_get_types(grn_ctx *ctx, grn_obj *string)
+{
+ unsigned char *types = NULL;
+ grn_string *string_ = (grn_string *)string;
+ GRN_API_ENTER;
+ if (string_) {
+ types = string_->ctypes;
+ } else {
+ types = NULL;
+ }
+ GRN_API_RETURN(types);
+}
+
+grn_rc
+grn_string_set_types(grn_ctx *ctx, grn_obj *string, unsigned char *types)
+{
+ grn_rc rc;
+ grn_string *string_ = (grn_string *)string;
+ GRN_API_ENTER;
+ if (string_) {
+ if (string_->ctypes) { GRN_FREE(string_->ctypes); }
+ string_->ctypes = types;
+ rc = GRN_SUCCESS;
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ GRN_API_RETURN(rc);
+}
+
+grn_encoding
+grn_string_get_encoding(grn_ctx *ctx, grn_obj *string)
+{
+ grn_encoding encoding = GRN_ENC_NONE;
+ grn_string *string_ = (grn_string *)string;
+ GRN_API_ENTER;
+ if (string_) {
+ encoding = string_->encoding;
+ }
+ GRN_API_RETURN(encoding);
+}
+
+grn_rc
+grn_string_inspect(grn_ctx *ctx, grn_obj *buffer, grn_obj *string)
+{
+ grn_string *string_ = (grn_string *)string;
+
+ GRN_TEXT_PUTS(ctx, buffer, "#<string:");
+
+ GRN_TEXT_PUTS(ctx, buffer, " original:<");
+ GRN_TEXT_PUT(ctx, buffer,
+ string_->original,
+ string_->original_length_in_bytes);
+ GRN_TEXT_PUTS(ctx, buffer, ">");
+ GRN_TEXT_PUTS(ctx, buffer, "(");
+ grn_text_itoa(ctx, buffer, string_->original_length_in_bytes);
+ GRN_TEXT_PUTS(ctx, buffer, ")");
+
+ GRN_TEXT_PUTS(ctx, buffer, " normalized:<");
+ GRN_TEXT_PUT(ctx, buffer,
+ string_->normalized,
+ string_->normalized_length_in_bytes);
+ GRN_TEXT_PUTS(ctx, buffer, ">");
+ GRN_TEXT_PUTS(ctx, buffer, "(");
+ grn_text_itoa(ctx, buffer, string_->normalized_length_in_bytes);
+ GRN_TEXT_PUTS(ctx, buffer, ")");
+
+ GRN_TEXT_PUTS(ctx, buffer, " n_characters:");
+ grn_text_itoa(ctx, buffer, string_->n_characters);
+
+ GRN_TEXT_PUTS(ctx, buffer, " encoding:");
+ grn_inspect_encoding(ctx, buffer, string_->encoding);
+
+ GRN_TEXT_PUTS(ctx, buffer, " flags:");
+ if (string_->flags & GRN_STRING_REMOVE_BLANK) {
+ GRN_TEXT_PUTS(ctx, buffer, "REMOVE_BLANK|");
+ }
+ if (string_->flags & GRN_STRING_WITH_TYPES) {
+ GRN_TEXT_PUTS(ctx, buffer, "WITH_TYPES|");
+ }
+ if (string_->flags & GRN_STRING_WITH_CHECKS) {
+ GRN_TEXT_PUTS(ctx, buffer, "WITH_CHECKS|");
+ }
+ if (string_->flags & GRN_STRING_REMOVE_TOKENIZED_DELIMITER) {
+ GRN_TEXT_PUTS(ctx, buffer, "REMOVE_TOKENIZED_DELIMITER|");
+ }
+ if (GRN_TEXT_VALUE(buffer)[GRN_TEXT_LEN(buffer) - 1] == '|') {
+ grn_bulk_truncate(ctx, buffer, GRN_TEXT_LEN(buffer) - 1);
+ }
+
+ GRN_TEXT_PUTS(ctx, buffer, ">");
+
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_string_close(grn_ctx *ctx, grn_obj *string)
+{
+ grn_rc rc;
+ grn_string *string_ = (grn_string *)string;
+ if (string_) {
+ if (string_->normalized) { GRN_FREE(string_->normalized); }
+ if (string_->ctypes) { GRN_FREE(string_->ctypes); }
+ if (string_->checks) { GRN_FREE(string_->checks); }
+ GRN_FREE(string);
+ rc = GRN_SUCCESS;
+ } else {
+ rc = GRN_INVALID_ARGUMENT;
+ }
+ return rc;
+}
diff --git a/storage/mroonga/vendor/groonga/lib/string_in.h b/storage/mroonga/vendor/groonga/lib/string_in.h
new file mode 100644
index 00000000000..55e2cb2cbcb
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/string_in.h
@@ -0,0 +1,65 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef GRN_STRING_H
+#define GRN_STRING_H
+
+#ifndef GROONGA_IN_H
+# include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifndef GRN_CTX_H
+# include "ctx.h"
+#endif /* GRN_CTX_H */
+
+#ifndef GRN_DB_H
+# include "db.h"
+#endif /* GRN_DB_H */
+
+#ifndef GRN_STR_H
+# include "str.h"
+#endif /* GRN_STR_IN_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ grn_obj_header header;
+ const char *original;
+ unsigned int original_length_in_bytes;
+ char *normalized;
+ unsigned int normalized_length_in_bytes;
+ unsigned int n_characters;
+ short *checks;
+ unsigned char *ctypes;
+ grn_encoding encoding;
+ int flags;
+} grn_string;
+
+grn_obj *grn_string_open_(grn_ctx *ctx, const char *str, unsigned int str_len,
+ grn_obj *normalizer, int flags, grn_encoding encoding);
+grn_rc grn_string_close(grn_ctx *ctx, grn_obj *string);
+grn_rc grn_string_inspect(grn_ctx *ctx, grn_obj *buffer, grn_obj *string);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_STRING_IN_H */
diff --git a/storage/mroonga/vendor/groonga/lib/token.c b/storage/mroonga/vendor/groonga/lib/token.c
new file mode 100644
index 00000000000..edce2edcb82
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/token.c
@@ -0,0 +1,778 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "groonga_in.h"
+#include <string.h>
+#include <ctype.h>
+#include "ctx_impl.h"
+#include "token.h"
+#include "pat.h"
+#include "dat.h"
+#include "hash.h"
+#include "string_in.h"
+#include "plugin_in.h"
+#include <groonga/tokenizer.h>
+
+grn_obj *grn_token_uvector = NULL;
+
+typedef struct {
+ grn_tokenizer_token token;
+ byte *curr;
+ byte *tail;
+ uint32_t unit;
+} grn_uvector_tokenizer;
+
+static grn_obj *
+uvector_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *str, *flags, *mode;
+ grn_uvector_tokenizer *tokenizer;
+ if (!(flags = grn_ctx_pop(ctx))) {
+ ERR(GRN_INVALID_ARGUMENT, "[tokenizer][uvector] missing argument: flags");
+ return NULL;
+ }
+ if (!(str = grn_ctx_pop(ctx))) {
+ ERR(GRN_INVALID_ARGUMENT, "[tokenizer][uvector] missing argument: string");
+ return NULL;
+ }
+ if (!(mode = grn_ctx_pop(ctx))) {
+ ERR(GRN_INVALID_ARGUMENT, "[tokenizer][uvector] missing argument: mode");
+ return NULL;
+ }
+ if (!(tokenizer = GRN_MALLOC(sizeof(grn_uvector_tokenizer)))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[tokenizer][uvector] "
+ "memory allocation to grn_uvector_tokenizer failed");
+ return NULL;
+ }
+ user_data->ptr = tokenizer;
+
+ grn_tokenizer_token_init(ctx, &(tokenizer->token));
+ tokenizer->curr = (byte *)GRN_TEXT_VALUE(str);
+ tokenizer->tail = tokenizer->curr + GRN_TEXT_LEN(str);
+ tokenizer->unit = sizeof(grn_id);
+ return NULL;
+}
+
+static grn_obj *
+uvector_next(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_uvector_tokenizer *tokenizer = user_data->ptr;
+ byte *p = tokenizer->curr + tokenizer->unit;
+ if (tokenizer->tail < p) {
+ grn_tokenizer_token_push(ctx, &(tokenizer->token),
+ (const char *)tokenizer->curr, 0,
+ GRN_TOKENIZER_TOKEN_LAST);
+ } else {
+ grn_tokenizer_status status;
+ if (tokenizer->tail == p) {
+ status = GRN_TOKENIZER_TOKEN_LAST;
+ } else {
+ status = GRN_TOKENIZER_TOKEN_CONTINUE;
+ }
+ grn_tokenizer_token_push(ctx, &(tokenizer->token),
+ (const char *)tokenizer->curr, tokenizer->unit,
+ status);
+ tokenizer->curr = p;
+ }
+ return NULL;
+}
+
+static grn_obj *
+uvector_fin(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_uvector_tokenizer *tokenizer = user_data->ptr;
+ if (!tokenizer) {
+ return NULL;
+ }
+ grn_tokenizer_token_fin(ctx, &(tokenizer->token));
+ GRN_FREE(tokenizer);
+ return NULL;
+}
+
+typedef struct {
+ const uint8_t *delimiter;
+ uint32_t delimiter_len;
+ const unsigned char *next;
+ const unsigned char *end;
+ grn_tokenizer_token token;
+ grn_tokenizer_query *query;
+ grn_bool have_tokenized_delimiter;
+} grn_delimited_tokenizer;
+
+static grn_obj *
+delimited_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data,
+ const uint8_t *delimiter, uint32_t delimiter_len)
+{
+ grn_tokenizer_query *query;
+ unsigned int normalize_flags = 0;
+ const char *normalized;
+ unsigned int normalized_length_in_bytes;
+ grn_delimited_tokenizer *tokenizer;
+
+ query = grn_tokenizer_query_open(ctx, nargs, args, normalize_flags);
+ if (!query) {
+ return NULL;
+ }
+
+ if (!(tokenizer = GRN_MALLOC(sizeof(grn_delimited_tokenizer)))) {
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[tokenizer][delimit] "
+ "memory allocation to grn_delimited_tokenizer failed");
+ grn_tokenizer_query_close(ctx, query);
+ return NULL;
+ }
+ user_data->ptr = tokenizer;
+
+ tokenizer->query = query;
+
+ tokenizer->have_tokenized_delimiter =
+ grn_tokenizer_have_tokenized_delimiter(ctx,
+ tokenizer->query->ptr,
+ tokenizer->query->length,
+ tokenizer->query->encoding);
+ tokenizer->delimiter = delimiter;
+ tokenizer->delimiter_len = delimiter_len;
+ grn_string_get_normalized(ctx, tokenizer->query->normalized_query,
+ &normalized, &normalized_length_in_bytes,
+ NULL);
+ tokenizer->next = (const unsigned char *)normalized;
+ tokenizer->end = tokenizer->next + normalized_length_in_bytes;
+
+ grn_tokenizer_token_init(ctx, &(tokenizer->token));
+
+ return NULL;
+}
+
+static grn_obj *
+delimited_next(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_delimited_tokenizer *tokenizer = user_data->ptr;
+
+ if (tokenizer->have_tokenized_delimiter) {
+ unsigned int rest_length;
+ rest_length = tokenizer->end - tokenizer->next;
+ tokenizer->next =
+ (unsigned char *)grn_tokenizer_tokenized_delimiter_next(
+ ctx,
+ &(tokenizer->token),
+ (const char *)tokenizer->next,
+ rest_length,
+ tokenizer->query->encoding);
+ } else {
+ size_t cl;
+ const unsigned char *p = tokenizer->next, *r;
+ const unsigned char *e = tokenizer->end;
+ grn_tokenizer_status status;
+ for (r = p; r < e; r += cl) {
+ if (!(cl = grn_charlen_(ctx, (char *)r, (char *)e,
+ tokenizer->query->encoding))) {
+ tokenizer->next = (unsigned char *)e;
+ break;
+ }
+ {
+ grn_bool found_delimiter = GRN_FALSE;
+ const unsigned char *current_end = r;
+ while (current_end + tokenizer->delimiter_len <= e &&
+ !memcmp(current_end,
+ tokenizer->delimiter, tokenizer->delimiter_len)) {
+ current_end += tokenizer->delimiter_len;
+ tokenizer->next = current_end;
+ found_delimiter = GRN_TRUE;
+ }
+ if (found_delimiter) {
+ break;
+ }
+ }
+ }
+ if (r == e) {
+ status = GRN_TOKENIZER_LAST;
+ } else {
+ status = GRN_TOKENIZER_CONTINUE;
+ }
+ grn_tokenizer_token_push(ctx,
+ &(tokenizer->token),
+ (const char *)p,
+ r - p,
+ status);
+ }
+
+ return NULL;
+}
+
+static grn_obj *
+delimited_fin(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_delimited_tokenizer *tokenizer = user_data->ptr;
+ if (!tokenizer) {
+ return NULL;
+ }
+ grn_tokenizer_query_close(ctx, tokenizer->query);
+ grn_tokenizer_token_fin(ctx, &(tokenizer->token));
+ GRN_FREE(tokenizer);
+ return NULL;
+}
+
+static grn_obj *
+delimit_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ static const uint8_t delimiter[1] = {' '};
+ return delimited_init(ctx, nargs, args, user_data, delimiter, 1);
+}
+
+static grn_obj *
+delimit_null_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ static const uint8_t delimiter[1] = {'\0'};
+ return delimited_init(ctx, nargs, args, user_data, delimiter, 1);
+}
+
+/* ngram tokenizer */
+
+typedef struct {
+ grn_tokenizer_token token;
+ grn_tokenizer_query *query;
+ uint8_t uni_alpha;
+ uint8_t uni_digit;
+ uint8_t uni_symbol;
+ uint8_t ngram_unit;
+ uint8_t ignore_blank;
+ uint8_t overlap;
+ int32_t pos;
+ uint32_t skip;
+ const unsigned char *next;
+ const unsigned char *end;
+ const uint_least8_t *ctypes;
+ uint32_t len;
+ uint32_t tail;
+} grn_ngram_tokenizer;
+
+static grn_obj *
+ngram_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data, uint8_t ngram_unit,
+ uint8_t uni_alpha, uint8_t uni_digit, uint8_t uni_symbol, uint8_t ignore_blank)
+{
+ unsigned int normalize_flags =
+ GRN_STRING_REMOVE_BLANK |
+ GRN_STRING_WITH_TYPES |
+ GRN_STRING_REMOVE_TOKENIZED_DELIMITER;
+ grn_tokenizer_query *query;
+ const char *normalized;
+ unsigned int normalized_length_in_bytes;
+ grn_ngram_tokenizer *tokenizer;
+
+ query = grn_tokenizer_query_open(ctx, nargs, args, normalize_flags);
+ if (!query) {
+ return NULL;
+ }
+
+ if (!(tokenizer = GRN_MALLOC(sizeof(grn_ngram_tokenizer)))) {
+ grn_tokenizer_query_close(ctx, query);
+ ERR(GRN_NO_MEMORY_AVAILABLE,
+ "[tokenizer][ngram] "
+ "memory allocation to grn_ngram_tokenizer failed");
+ return NULL;
+ }
+ user_data->ptr = tokenizer;
+
+ grn_tokenizer_token_init(ctx, &(tokenizer->token));
+ tokenizer->query = query;
+
+ tokenizer->uni_alpha = uni_alpha;
+ tokenizer->uni_digit = uni_digit;
+ tokenizer->uni_symbol = uni_symbol;
+ tokenizer->ngram_unit = ngram_unit;
+ tokenizer->ignore_blank = ignore_blank;
+ tokenizer->overlap = 0;
+ tokenizer->pos = 0;
+ tokenizer->skip = 0;
+
+ grn_string_get_normalized(ctx, tokenizer->query->normalized_query,
+ &normalized, &normalized_length_in_bytes,
+ &(tokenizer->len));
+ tokenizer->next = (const unsigned char *)normalized;
+ tokenizer->end = tokenizer->next + normalized_length_in_bytes;
+ tokenizer->ctypes =
+ grn_string_get_types(ctx, tokenizer->query->normalized_query);
+ return NULL;
+}
+
+static grn_obj *
+unigram_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{ return ngram_init(ctx, nargs, args, user_data, 1, 1, 1, 1, 0); }
+
+static grn_obj *
+bigram_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{ return ngram_init(ctx, nargs, args, user_data, 2, 1, 1, 1, 0); }
+
+static grn_obj *
+trigram_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{ return ngram_init(ctx, nargs, args, user_data, 3, 1, 1, 1, 0); }
+
+static grn_obj *
+bigrams_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{ return ngram_init(ctx, nargs, args, user_data, 2, 1, 1, 0, 0); }
+
+static grn_obj *
+bigramsa_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{ return ngram_init(ctx, nargs, args, user_data, 2, 0, 1, 0, 0); }
+
+static grn_obj *
+bigramsad_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{ return ngram_init(ctx, nargs, args, user_data, 2, 0, 0, 0, 0); }
+
+static grn_obj *
+bigrami_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{ return ngram_init(ctx, nargs, args, user_data, 2, 1, 1, 1, 1); }
+
+static grn_obj *
+bigramis_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{ return ngram_init(ctx, nargs, args, user_data, 2, 1, 1, 0, 1); }
+
+static grn_obj *
+bigramisa_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{ return ngram_init(ctx, nargs, args, user_data, 2, 0, 1, 0, 1); }
+
+static grn_obj *
+bigramisad_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{ return ngram_init(ctx, nargs, args, user_data, 2, 0, 0, 0, 1); }
+
+static grn_obj *
+ngram_next(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ size_t cl;
+ grn_ngram_tokenizer *tokenizer = user_data->ptr;
+ const unsigned char *p = tokenizer->next, *r = p, *e = tokenizer->end;
+ int32_t len = 0, pos = tokenizer->pos + tokenizer->skip, status = 0;
+ const uint_least8_t *cp = tokenizer->ctypes ? tokenizer->ctypes + pos : NULL;
+ if (cp && tokenizer->uni_alpha && GRN_STR_CTYPE(*cp) == GRN_CHAR_ALPHA) {
+ while ((cl = grn_charlen_(ctx, (char *)r, (char *)e,
+ tokenizer->query->encoding))) {
+ len++;
+ r += cl;
+ if (/* !tokenizer->ignore_blank && */ GRN_STR_ISBLANK(*cp)) { break; }
+ if (GRN_STR_CTYPE(*++cp) != GRN_CHAR_ALPHA) { break; }
+ }
+ tokenizer->next = r;
+ tokenizer->overlap = 0;
+ } else if (cp &&
+ tokenizer->uni_digit &&
+ GRN_STR_CTYPE(*cp) == GRN_CHAR_DIGIT) {
+ while ((cl = grn_charlen_(ctx, (char *)r, (char *)e,
+ tokenizer->query->encoding))) {
+ len++;
+ r += cl;
+ if (/* !tokenizer->ignore_blank && */ GRN_STR_ISBLANK(*cp)) { break; }
+ if (GRN_STR_CTYPE(*++cp) != GRN_CHAR_DIGIT) { break; }
+ }
+ tokenizer->next = r;
+ tokenizer->overlap = 0;
+ } else if (cp &&
+ tokenizer->uni_symbol &&
+ GRN_STR_CTYPE(*cp) == GRN_CHAR_SYMBOL) {
+ while ((cl = grn_charlen_(ctx, (char *)r, (char *)e,
+ tokenizer->query->encoding))) {
+ len++;
+ r += cl;
+ if (!tokenizer->ignore_blank && GRN_STR_ISBLANK(*cp)) { break; }
+ if (GRN_STR_CTYPE(*++cp) != GRN_CHAR_SYMBOL) { break; }
+ }
+ tokenizer->next = r;
+ tokenizer->overlap = 0;
+ } else {
+#ifdef PRE_DEFINED_UNSPLIT_WORDS
+ const unsigned char *key = NULL;
+ // todo : grn_pat_lcp_search
+ if ((tid = grn_sym_common_prefix_search(sym, p))) {
+ if (!(key = _grn_sym_key(sym, tid))) {
+ tokenizer->status = GRN_TOKEN_NOT_FOUND;
+ return NULL;
+ }
+ len = grn_str_len(key, tokenizer->query->encoding, NULL);
+ }
+ r = p + grn_charlen_(ctx, p, e, tokenizer->query->encoding);
+ if (tid && (len > 1 || r == p)) {
+ if (r != p && pos + len - 1 <= tokenizer->tail) { continue; }
+ p += strlen(key);
+ if (!*p && tokenizer->mode == GRN_TOKEN_GET) {
+ tokenizer->status = GRN_TOKEN_DONE;
+ }
+ }
+#endif /* PRE_DEFINED_UNSPLIT_WORDS */
+ if ((cl = grn_charlen_(ctx, (char *)r, (char *)e,
+ tokenizer->query->encoding))) {
+ len++;
+ r += cl;
+ tokenizer->next = r;
+ while (len < tokenizer->ngram_unit &&
+ (cl = grn_charlen_(ctx, (char *)r, (char *)e,
+ tokenizer->query->encoding))) {
+ if (cp) {
+ if (!tokenizer->ignore_blank && GRN_STR_ISBLANK(*cp)) { break; }
+ cp++;
+ if ((tokenizer->uni_alpha && GRN_STR_CTYPE(*cp) == GRN_CHAR_ALPHA) ||
+ (tokenizer->uni_digit && GRN_STR_CTYPE(*cp) == GRN_CHAR_DIGIT) ||
+ (tokenizer->uni_symbol && GRN_STR_CTYPE(*cp) == GRN_CHAR_SYMBOL)) {
+ break;
+ }
+ }
+ len++;
+ r += cl;
+ }
+ if (tokenizer->overlap) {
+ status |= GRN_TOKENIZER_TOKEN_OVERLAP;
+ }
+ if (len < tokenizer->ngram_unit) {
+ status |= GRN_TOKENIZER_TOKEN_UNMATURED;
+ }
+ tokenizer->overlap = (len > 1) ? 1 : 0;
+ }
+ }
+ tokenizer->pos = pos;
+ tokenizer->len = len;
+ tokenizer->tail = pos + len - 1;
+ if (p == r || tokenizer->next == e) {
+ tokenizer->skip = 0;
+ status |= GRN_TOKENIZER_TOKEN_LAST;
+ } else {
+ tokenizer->skip = tokenizer->overlap ? 1 : len;
+ }
+ if (r == e) { status |= GRN_TOKENIZER_TOKEN_REACH_END; }
+ grn_tokenizer_token_push(ctx,
+ &(tokenizer->token),
+ (const char *)p,
+ r - p,
+ status);
+ return NULL;
+}
+
+static grn_obj *
+ngram_fin(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_ngram_tokenizer *tokenizer = user_data->ptr;
+ if (!tokenizer) {
+ return NULL;
+ }
+ grn_tokenizer_token_fin(ctx, &(tokenizer->token));
+ grn_tokenizer_query_close(ctx, tokenizer->query);
+ GRN_FREE(tokenizer);
+ return NULL;
+}
+
+/* external */
+
+grn_rc
+grn_token_init(void)
+{
+ static grn_proc _grn_token_uvector;
+ _grn_token_uvector.obj.db = NULL;
+ _grn_token_uvector.obj.id = GRN_ID_NIL;
+ _grn_token_uvector.obj.header.domain = GRN_ID_NIL;
+ _grn_token_uvector.obj.range = GRN_ID_NIL;
+ _grn_token_uvector.funcs[PROC_INIT] = uvector_init;
+ _grn_token_uvector.funcs[PROC_NEXT] = uvector_next;
+ _grn_token_uvector.funcs[PROC_FIN] = uvector_fin;
+ grn_token_uvector = (grn_obj *)&_grn_token_uvector;
+ return GRN_SUCCESS;
+}
+
+grn_rc
+grn_token_fin(void)
+{
+ return GRN_SUCCESS;
+}
+
+grn_token *
+grn_token_open(grn_ctx *ctx, grn_obj *table, const char *str, size_t str_len,
+ grn_token_mode mode, unsigned int flags)
+{
+ grn_token *token;
+ grn_encoding encoding;
+ grn_obj *tokenizer;
+ grn_obj *normalizer;
+ grn_obj_flags table_flags;
+ if (grn_table_get_info(ctx, table, &table_flags, &encoding, &tokenizer,
+ &normalizer)) {
+ return NULL;
+ }
+ if (!(token = GRN_MALLOC(sizeof(grn_token)))) { return NULL; }
+ token->table = table;
+ token->mode = mode;
+ token->encoding = encoding;
+ token->tokenizer = tokenizer;
+ token->orig = (const unsigned char *)str;
+ token->orig_blen = str_len;
+ token->curr = NULL;
+ token->nstr = NULL;
+ token->curr_size = 0;
+ token->pos = -1;
+ token->status = GRN_TOKEN_DOING;
+ token->force_prefix = 0;
+ if (tokenizer) {
+ grn_obj str_, flags_, mode_;
+ GRN_TEXT_INIT(&str_, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET_REF(&str_, str, str_len);
+ GRN_UINT32_INIT(&flags_, 0);
+ GRN_UINT32_SET(ctx, &flags_, flags);
+ GRN_UINT32_INIT(&mode_, 0);
+ GRN_UINT32_SET(ctx, &mode_, mode);
+ token->pctx.caller = NULL;
+ token->pctx.user_data.ptr = NULL;
+ token->pctx.proc = (grn_proc *)tokenizer;
+ token->pctx.hooks = NULL;
+ token->pctx.currh = NULL;
+ token->pctx.phase = PROC_INIT;
+ grn_ctx_push(ctx, &mode_);
+ grn_ctx_push(ctx, &str_);
+ grn_ctx_push(ctx, &flags_);
+ ((grn_proc *)tokenizer)->funcs[PROC_INIT](ctx, 1, &table, &token->pctx.user_data);
+ grn_obj_close(ctx, &flags_);
+ grn_obj_close(ctx, &str_);
+ grn_obj_close(ctx, &mode_);
+ } else {
+ int nflags = 0;
+ token->nstr = grn_string_open_(ctx, str, str_len,
+ normalizer, nflags, token->encoding);
+ if (token->nstr) {
+ const char *normalized;
+ grn_string_get_normalized(ctx, token->nstr,
+ &normalized, &(token->curr_size), NULL);
+ token->curr = (const unsigned char *)normalized;
+ } else {
+ ERR(GRN_TOKENIZER_ERROR, "grn_string_open failed at grn_token_open");
+ }
+ }
+ if (ctx->rc) {
+ grn_token_close(ctx, token);
+ token = NULL;
+ }
+ return token;
+}
+
+grn_id
+grn_token_next(grn_ctx *ctx, grn_token *token)
+{
+ int status;
+ grn_id tid = GRN_ID_NIL;
+ grn_obj *table = token->table;
+ grn_obj *tokenizer = token->tokenizer;
+ while (token->status != GRN_TOKEN_DONE) {
+ if (tokenizer) {
+ grn_obj *curr_, *stat_;
+ ((grn_proc *)tokenizer)->funcs[PROC_NEXT](ctx, 1, &table, &token->pctx.user_data);
+ stat_ = grn_ctx_pop(ctx);
+ curr_ = grn_ctx_pop(ctx);
+ token->curr = (const unsigned char *)GRN_TEXT_VALUE(curr_);
+ token->curr_size = GRN_TEXT_LEN(curr_);
+ status = GRN_UINT32_VALUE(stat_);
+ token->status = ((status & GRN_TOKENIZER_TOKEN_LAST) ||
+ (token->mode == GRN_TOKEN_GET &&
+ (status & GRN_TOKENIZER_TOKEN_REACH_END)))
+ ? GRN_TOKEN_DONE : GRN_TOKEN_DOING;
+ token->force_prefix = 0;
+ if (status & GRN_TOKENIZER_TOKEN_SKIP) {
+ token->pos++;
+ continue;
+ } else if (status & GRN_TOKENIZER_TOKEN_SKIP_WITH_POSITION) {
+ continue;
+ }
+ if (token->curr_size == 0) {
+ char tokenizer_name[GRN_TABLE_MAX_KEY_SIZE];
+ int tokenizer_name_length;
+ tokenizer_name_length =
+ grn_obj_name(ctx, token->tokenizer,
+ tokenizer_name, GRN_TABLE_MAX_KEY_SIZE);
+ GRN_LOG(ctx, GRN_WARN,
+ "[token_next] ignore an empty token: <%.*s>: <%.*s>",
+ tokenizer_name_length, tokenizer_name,
+ token->orig_blen, token->orig);
+ continue;
+ }
+ if (token->curr_size > GRN_TABLE_MAX_KEY_SIZE) {
+ GRN_LOG(ctx, GRN_WARN,
+ "[token_next] ignore too long token. "
+ "Token must be less than or equal to %d: <%d>(<%.*s>)",
+ GRN_TABLE_MAX_KEY_SIZE,
+ token->curr_size,
+ token->curr_size, token->curr);
+ continue;
+ }
+ if (status & GRN_TOKENIZER_TOKEN_UNMATURED) {
+ if (status & GRN_TOKENIZER_TOKEN_OVERLAP) {
+ if (token->mode == GRN_TOKEN_GET) { token->pos++; continue; }
+ } else {
+ if (status & GRN_TOKENIZER_TOKEN_LAST) { token->force_prefix = 1; }
+ }
+ }
+ } else {
+ token->status = GRN_TOKEN_DONE;
+ }
+ if (token->mode == GRN_TOKEN_ADD) {
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ if (grn_io_lock(ctx, ((grn_pat *)table)->io, grn_lock_timeout)) {
+ tid = GRN_ID_NIL;
+ } else {
+ tid = grn_pat_add(ctx, (grn_pat *)table, token->curr, token->curr_size,
+ NULL, NULL);
+ grn_io_unlock(((grn_pat *)table)->io);
+ }
+ break;
+ case GRN_TABLE_DAT_KEY :
+ if (grn_io_lock(ctx, ((grn_dat *)table)->io, grn_lock_timeout)) {
+ tid = GRN_ID_NIL;
+ } else {
+ tid = grn_dat_add(ctx, (grn_dat *)table, token->curr, token->curr_size,
+ NULL, NULL);
+ grn_io_unlock(((grn_dat *)table)->io);
+ }
+ break;
+ case GRN_TABLE_HASH_KEY :
+ if (grn_io_lock(ctx, ((grn_hash *)table)->io, grn_lock_timeout)) {
+ tid = GRN_ID_NIL;
+ } else {
+ tid = grn_hash_add(ctx, (grn_hash *)table, token->curr, token->curr_size,
+ NULL, NULL);
+ grn_io_unlock(((grn_hash *)table)->io);
+ }
+ break;
+ case GRN_TABLE_NO_KEY :
+ if (token->curr_size == sizeof(grn_id)) {
+ tid = *((grn_id *)token->curr);
+ } else {
+ tid = GRN_ID_NIL;
+ }
+ break;
+ }
+ } else {
+ switch (table->header.type) {
+ case GRN_TABLE_PAT_KEY :
+ tid = grn_pat_get(ctx, (grn_pat *)table, token->curr, token->curr_size, NULL);
+ break;
+ case GRN_TABLE_DAT_KEY :
+ tid = grn_dat_get(ctx, (grn_dat *)table, token->curr, token->curr_size, NULL);
+ break;
+ case GRN_TABLE_HASH_KEY :
+ tid = grn_hash_get(ctx, (grn_hash *)table, token->curr, token->curr_size, NULL);
+ break;
+ case GRN_TABLE_NO_KEY :
+ if (token->curr_size == sizeof(grn_id)) {
+ tid = *((grn_id *)token->curr);
+ } else {
+ tid = GRN_ID_NIL;
+ }
+ break;
+ }
+ }
+ if (tid == GRN_ID_NIL && token->status != GRN_TOKEN_DONE) {
+ token->status = GRN_TOKEN_NOT_FOUND;
+ }
+ token->pos++;
+ break;
+ }
+ return tid;
+}
+
+grn_rc
+grn_token_close(grn_ctx *ctx, grn_token *token)
+{
+ if (token) {
+ if (token->tokenizer) {
+ ((grn_proc *)token->tokenizer)->funcs[PROC_FIN](ctx, 1, &token->table,
+ &token->pctx.user_data);
+ }
+ if (token->nstr) {
+ grn_obj_close(ctx, token->nstr);
+ }
+ GRN_FREE(token);
+ return GRN_SUCCESS;
+ } else {
+ return GRN_INVALID_ARGUMENT;
+ }
+}
+
+grn_rc
+grn_db_init_mecab_tokenizer(grn_ctx *ctx)
+{
+ switch (GRN_CTX_GET_ENCODING(ctx)) {
+ case GRN_ENC_EUC_JP :
+ case GRN_ENC_UTF8 :
+ case GRN_ENC_SJIS :
+ {
+ const char *mecab_plugin_name = "tokenizers/mecab";
+ char *path;
+ path = grn_plugin_find_path(ctx, mecab_plugin_name);
+ if (path) {
+ GRN_FREE(path);
+ return grn_plugin_register(ctx, mecab_plugin_name);
+ } else {
+ return GRN_NO_SUCH_FILE_OR_DIRECTORY;
+ }
+ }
+ break;
+ default :
+ return GRN_OPERATION_NOT_SUPPORTED;
+ }
+}
+
+#define DEF_TOKENIZER(name, init, next, fin, vars)\
+ (grn_proc_create(ctx, (name), (sizeof(name) - 1),\
+ GRN_PROC_TOKENIZER, (init), (next), (fin), 3, (vars)))
+
+grn_rc
+grn_db_init_builtin_tokenizers(grn_ctx *ctx)
+{
+ grn_obj *obj;
+ grn_expr_var vars[] = {
+ {NULL, 0},
+ {NULL, 0},
+ {NULL, 0}
+ };
+ GRN_TEXT_INIT(&vars[0].value, 0);
+ GRN_TEXT_INIT(&vars[1].value, 0);
+ GRN_UINT32_INIT(&vars[2].value, 0);
+
+ obj = DEF_TOKENIZER("TokenDelimit",
+ delimit_init, delimited_next, delimited_fin, vars);
+ if (!obj || ((grn_db_obj *)obj)->id != GRN_DB_DELIMIT) { return GRN_FILE_CORRUPT; }
+ obj = DEF_TOKENIZER("TokenUnigram",
+ unigram_init, ngram_next, ngram_fin, vars);
+ if (!obj || ((grn_db_obj *)obj)->id != GRN_DB_UNIGRAM) { return GRN_FILE_CORRUPT; }
+ obj = DEF_TOKENIZER("TokenBigram",
+ bigram_init, ngram_next, ngram_fin, vars);
+ if (!obj || ((grn_db_obj *)obj)->id != GRN_DB_BIGRAM) { return GRN_FILE_CORRUPT; }
+ obj = DEF_TOKENIZER("TokenTrigram",
+ trigram_init, ngram_next, ngram_fin, vars);
+ if (!obj || ((grn_db_obj *)obj)->id != GRN_DB_TRIGRAM) { return GRN_FILE_CORRUPT; }
+
+ DEF_TOKENIZER("TokenBigramSplitSymbol",
+ bigrams_init, ngram_next, ngram_fin, vars);
+ DEF_TOKENIZER("TokenBigramSplitSymbolAlpha",
+ bigramsa_init, ngram_next, ngram_fin, vars);
+ DEF_TOKENIZER("TokenBigramSplitSymbolAlphaDigit",
+ bigramsad_init, ngram_next, ngram_fin, vars);
+ DEF_TOKENIZER("TokenBigramIgnoreBlank",
+ bigrami_init, ngram_next, ngram_fin, vars);
+ DEF_TOKENIZER("TokenBigramIgnoreBlankSplitSymbol",
+ bigramis_init, ngram_next, ngram_fin, vars);
+ DEF_TOKENIZER("TokenBigramIgnoreBlankSplitSymbolAlpha",
+ bigramisa_init, ngram_next, ngram_fin, vars);
+ DEF_TOKENIZER("TokenBigramIgnoreBlankSplitSymbolAlphaDigit",
+ bigramisad_init, ngram_next, ngram_fin, vars);
+ DEF_TOKENIZER("TokenDelimitNull",
+ delimit_null_init, delimited_next, delimited_fin, vars);
+ return GRN_SUCCESS;
+}
diff --git a/storage/mroonga/vendor/groonga/lib/token.h b/storage/mroonga/vendor/groonga/lib/token.h
new file mode 100644
index 00000000000..f69e30d3aaf
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/token.h
@@ -0,0 +1,91 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_TOKEN_H
+#define GRN_TOKEN_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifndef GRN_CTX_H
+#include "ctx.h"
+#endif /* GRN_CTX_H */
+
+#ifndef GRN_DB_H
+#include "db.h"
+#endif /* GRN_DB_H */
+
+#ifndef GRN_STR_H
+#include "str.h"
+#endif /* GRN_STR_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ GRN_TOKEN_GET = 0,
+ GRN_TOKEN_ADD,
+ GRN_TOKEN_DEL
+} grn_token_mode;
+
+typedef enum {
+ GRN_TOKEN_DOING = 0,
+ GRN_TOKEN_DONE,
+ GRN_TOKEN_NOT_FOUND
+} grn_token_status;
+
+typedef struct {
+ grn_obj *table;
+ const unsigned char *orig;
+ const unsigned char *curr;
+ uint32_t orig_blen;
+ uint32_t curr_size;
+ int32_t pos;
+ grn_token_mode mode;
+ grn_token_status status;
+ uint8_t force_prefix;
+ grn_obj_flags table_flags;
+ grn_encoding encoding;
+ grn_obj *tokenizer;
+ grn_proc_ctx pctx;
+ uint32_t variant;
+ grn_obj *nstr;
+} grn_token;
+
+extern grn_obj *grn_token_uvector;
+
+grn_rc grn_token_init(void);
+grn_rc grn_token_fin(void);
+
+#define GRN_TOKEN_ENABLE_TOKENIZED_DELIMITER (0x01L<<0)
+
+GRN_API grn_token *grn_token_open(grn_ctx *ctx, grn_obj *table, const char *str,
+ size_t str_len, grn_token_mode mode,
+ unsigned int flags);
+
+GRN_API grn_id grn_token_next(grn_ctx *ctx, grn_token *ng);
+GRN_API grn_rc grn_token_close(grn_ctx *ctx, grn_token *ng);
+
+grn_rc grn_db_init_mecab_tokenizer(grn_ctx *ctx);
+grn_rc grn_db_init_builtin_tokenizers(grn_ctx *ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_TOKEN_H */
diff --git a/storage/mroonga/vendor/groonga/lib/tokenizer.c b/storage/mroonga/vendor/groonga/lib/tokenizer.c
new file mode 100644
index 00000000000..37d22789663
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/tokenizer.c
@@ -0,0 +1,320 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "groonga_in.h"
+#include "groonga/tokenizer.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "ctx.h"
+#include "db.h"
+#include "str.h"
+#include "string_in.h"
+#include "token.h"
+
+/*
+ Just for backward compatibility. See grn_plugin_charlen() instead.
+ */
+int
+grn_tokenizer_charlen(grn_ctx *ctx, const char *str_ptr,
+ unsigned int str_length, grn_encoding encoding)
+{
+ return grn_plugin_charlen(ctx, str_ptr, str_length, encoding);
+}
+
+/*
+ Just for backward compatibility. See grn_plugin_isspace() instead.
+ */
+int
+grn_tokenizer_isspace(grn_ctx *ctx, const char *str_ptr,
+ unsigned int str_length, grn_encoding encoding)
+{
+ return grn_plugin_isspace(ctx, str_ptr, str_length, encoding);
+}
+
+grn_bool
+grn_tokenizer_is_tokenized_delimiter(grn_ctx *ctx,
+ const char *str_ptr,
+ unsigned int str_length,
+ grn_encoding encoding)
+{
+ if (encoding != GRN_ENC_UTF8) {
+ return GRN_FALSE;
+ }
+
+ if (str_length != GRN_TOKENIZER_TOKENIZED_DELIMITER_UTF8_LEN) {
+ return GRN_FALSE;
+ }
+
+ return memcmp(str_ptr,
+ GRN_TOKENIZER_TOKENIZED_DELIMITER_UTF8,
+ GRN_TOKENIZER_TOKENIZED_DELIMITER_UTF8_LEN) == 0;
+}
+
+grn_bool
+grn_tokenizer_have_tokenized_delimiter(grn_ctx *ctx,
+ const char *str_ptr,
+ unsigned int str_length,
+ grn_encoding encoding)
+{
+ int char_length;
+ const char *current = str_ptr;
+ const char *end = str_ptr + str_length;
+
+ if (encoding != GRN_ENC_UTF8) {
+ return GRN_FALSE;
+ }
+
+ if (str_length == 0) {
+ return GRN_FALSE;
+ }
+
+ while ((char_length = grn_charlen_(ctx, current, end, encoding)) > 0) {
+ if (grn_tokenizer_is_tokenized_delimiter(ctx,
+ current, char_length,
+ encoding)) {
+ return GRN_TRUE;
+ }
+ current += char_length;
+ }
+ return GRN_FALSE;
+}
+
+grn_tokenizer_query *
+grn_tokenizer_query_open(grn_ctx *ctx, int num_args, grn_obj **args,
+ unsigned int normalize_flags)
+{
+ grn_obj *flags = grn_ctx_pop(ctx);
+ grn_obj *query_str = grn_ctx_pop(ctx);
+ grn_obj *token_mode = grn_ctx_pop(ctx);
+
+ if (query_str == NULL) {
+ GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, "missing argument");
+ return NULL;
+ }
+
+ if ((num_args < 1) || (args == NULL) || (args[0] == NULL)) {
+ GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, "invalid NULL pointer");
+ return NULL;
+ }
+
+ {
+ grn_tokenizer_query * const query =
+ GRN_PLUGIN_MALLOC(ctx, sizeof(grn_tokenizer_query));
+ if (query == NULL) {
+ return NULL;
+ }
+ query->normalized_query = NULL;
+ query->query_buf = NULL;
+ if (flags) {
+ query->flags = GRN_UINT32_VALUE(flags);
+ } else {
+ query->flags = 0;
+ }
+ if (token_mode) {
+ query->token_mode = GRN_UINT32_VALUE(token_mode);
+ } else {
+ query->token_mode = GRN_TOKEN_ADD;
+ }
+
+ {
+ grn_obj * const table = args[0];
+ grn_obj_flags table_flags;
+ grn_encoding table_encoding;
+ unsigned int query_length = GRN_TEXT_LEN(query_str);
+ char *query_buf = (char *)GRN_PLUGIN_MALLOC(ctx, query_length + 1);
+ grn_obj *normalizer = NULL;
+
+ if (query_buf == NULL) {
+ GRN_PLUGIN_FREE(ctx, query);
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer] failed to duplicate query");
+ return NULL;
+ }
+ grn_table_get_info(ctx, table, &table_flags, &table_encoding, NULL,
+ &normalizer);
+ {
+ grn_obj *normalized_query;
+ if (table_flags & GRN_OBJ_KEY_NORMALIZE) {
+ normalizer = GRN_NORMALIZER_AUTO;
+ }
+ normalized_query = grn_string_open_(ctx,
+ GRN_TEXT_VALUE(query_str),
+ GRN_TEXT_LEN(query_str),
+ normalizer,
+ normalize_flags,
+ table_encoding);
+ if (!normalized_query) {
+ GRN_PLUGIN_FREE(ctx, query_buf);
+ GRN_PLUGIN_FREE(ctx, query);
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer] failed to open normalized string");
+ return NULL;
+ }
+ query->normalized_query = normalized_query;
+ memcpy(query_buf, GRN_TEXT_VALUE(query_str), query_length);
+ query_buf[query_length] = '\0';
+ query->query_buf = query_buf;
+ query->ptr = query_buf;
+ query->length = query_length;
+ }
+ query->encoding = table_encoding;
+
+ if (query->flags & GRN_TOKEN_ENABLE_TOKENIZED_DELIMITER) {
+ const char *normalized_string;
+ unsigned int normalized_string_length;
+
+ grn_string_get_normalized(ctx,
+ query->normalized_query,
+ &normalized_string,
+ &normalized_string_length,
+ NULL);
+ query->have_tokenized_delimiter =
+ grn_tokenizer_have_tokenized_delimiter(ctx,
+ normalized_string,
+ normalized_string_length,
+ query->encoding);
+ } else {
+ query->have_tokenized_delimiter = GRN_FALSE;
+ }
+ }
+ return query;
+ }
+}
+
+grn_tokenizer_query *
+grn_tokenizer_query_create(grn_ctx *ctx, int num_args, grn_obj **args)
+{
+ return grn_tokenizer_query_open(ctx, num_args, args, 0);
+}
+
+void
+grn_tokenizer_query_close(grn_ctx *ctx, grn_tokenizer_query *query)
+{
+ if (query != NULL) {
+ if (query->normalized_query != NULL) {
+ grn_obj_unlink(ctx, query->normalized_query);
+ }
+ if (query->query_buf != NULL) {
+ GRN_PLUGIN_FREE(ctx, query->query_buf);
+ }
+ GRN_PLUGIN_FREE(ctx, query);
+ }
+}
+
+void
+grn_tokenizer_query_destroy(grn_ctx *ctx, grn_tokenizer_query *query)
+{
+ grn_tokenizer_query_close(ctx, query);
+}
+
+void
+grn_tokenizer_token_init(grn_ctx *ctx, grn_tokenizer_token *token)
+{
+ GRN_TEXT_INIT(&token->str, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_UINT32_INIT(&token->status, 0);
+}
+
+void
+grn_tokenizer_token_fin(grn_ctx *ctx, grn_tokenizer_token *token)
+{
+ GRN_OBJ_FIN(ctx, &(token->str));
+ GRN_OBJ_FIN(ctx, &(token->status));
+}
+
+void
+grn_tokenizer_token_push(grn_ctx *ctx, grn_tokenizer_token *token,
+ const char *str_ptr, unsigned int str_length,
+ grn_tokenizer_status status)
+{
+ GRN_TEXT_SET_REF(&token->str, str_ptr, str_length);
+ GRN_UINT32_SET(ctx, &token->status, status);
+ grn_ctx_push(ctx, &token->str);
+ grn_ctx_push(ctx, &token->status);
+}
+
+const char *
+grn_tokenizer_tokenized_delimiter_next(grn_ctx *ctx,
+ grn_tokenizer_token *token,
+ const char *str_ptr,
+ unsigned int str_length,
+ grn_encoding encoding)
+{
+ size_t char_length = 0;
+ const char *start = str_ptr;
+ const char *current;
+ const char *end = str_ptr + str_length;
+ const char *next_start = NULL;
+ unsigned int token_length;
+ grn_tokenizer_status status;
+
+ for (current = start; current < end; current += char_length) {
+ char_length = grn_charlen_(ctx, current, end, encoding);
+ if (char_length == 0) {
+ break;
+ }
+ if (grn_tokenizer_is_tokenized_delimiter(ctx, current, char_length,
+ encoding)) {
+ next_start = str_ptr + (current - start + char_length);
+ break;
+ }
+ }
+
+ token_length = current - start;
+ if (current == end) {
+ status = GRN_TOKENIZER_LAST;
+ } else {
+ status = GRN_TOKENIZER_CONTINUE;
+ }
+ grn_tokenizer_token_push(ctx, token, start, token_length, status);
+
+ return next_start;
+}
+
+grn_rc
+grn_tokenizer_register(grn_ctx *ctx, const char *plugin_name_ptr,
+ unsigned int plugin_name_length,
+ grn_proc_func *init, grn_proc_func *next,
+ grn_proc_func *fin)
+{
+ grn_expr_var vars[] = {
+ { NULL, 0 },
+ { NULL, 0 },
+ { NULL, 0 }
+ };
+ GRN_TEXT_INIT(&vars[0].value, 0);
+ GRN_TEXT_INIT(&vars[1].value, 0);
+ GRN_UINT32_INIT(&vars[2].value, 0);
+
+ {
+ /*
+ grn_proc_create() registers a plugin to the database which is associated
+ with `ctx'. A returned object must not be finalized here.
+ */
+ grn_obj * const obj = grn_proc_create(ctx, plugin_name_ptr,
+ plugin_name_length,
+ GRN_PROC_TOKENIZER,
+ init, next, fin, 3, vars);
+ if (obj == NULL) {
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR, "grn_proc_create() failed");
+ return ctx->rc;
+ }
+ }
+ return GRN_SUCCESS;
+}
diff --git a/storage/mroonga/vendor/groonga/lib/util.c b/storage/mroonga/vendor/groonga/lib/util.c
new file mode 100644
index 00000000000..62794abb856
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/util.c
@@ -0,0 +1,1148 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2010-2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "db.h"
+#include "pat.h"
+#include "ii.h"
+#include "util.h"
+#include "string_in.h"
+
+#include <string.h>
+#include <stdio.h>
+
+grn_rc
+grn_normalize_offset_and_limit(grn_ctx *ctx, int size, int *p_offset, int *p_limit)
+{
+ int end;
+ int offset = *p_offset;
+ int limit = *p_limit;
+
+ if (offset < 0) {
+ offset += size;
+ if (offset < 0) {
+ *p_offset = 0;
+ *p_limit = 0;
+ return GRN_TOO_SMALL_OFFSET;
+ }
+ } else if (offset != 0 && offset >= size) {
+ *p_offset = 0;
+ *p_limit = 0;
+ return GRN_TOO_LARGE_OFFSET;
+ }
+
+ if (limit < 0) {
+ limit += size + 1;
+ if (limit < 0) {
+ *p_offset = 0;
+ *p_limit = 0;
+ return GRN_TOO_SMALL_LIMIT;
+ }
+ } else if (limit > size) {
+ limit = size;
+ }
+
+ /* At this point, offset and limit must be zero or positive. */
+ end = offset + limit;
+ if (end > size) {
+ limit -= end - size;
+ }
+ *p_offset = offset;
+ *p_limit = limit;
+ return GRN_SUCCESS;
+}
+
+grn_obj *
+grn_inspect_name(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ int name_size;
+
+ name_size = grn_obj_name(ctx, obj, NULL, 0);
+ if (name_size) {
+ grn_bulk_space(ctx, buf, name_size);
+ grn_obj_name(ctx, obj, GRN_BULK_CURR(buf) - name_size, name_size);
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "(nil)");
+ }
+
+ return buf;
+}
+
+grn_obj *
+grn_inspect_encoding(grn_ctx *ctx, grn_obj *buf, grn_encoding encoding)
+{
+ switch (encoding) {
+ case GRN_ENC_DEFAULT :
+ GRN_TEXT_PUTS(ctx, buf, "default(");
+ grn_inspect_encoding(ctx, buf, grn_get_default_encoding());
+ GRN_TEXT_PUTS(ctx, buf, ")");
+ break;
+ case GRN_ENC_NONE :
+ GRN_TEXT_PUTS(ctx, buf, "none");
+ break;
+ case GRN_ENC_EUC_JP :
+ GRN_TEXT_PUTS(ctx, buf, "EUC-JP");
+ break;
+ case GRN_ENC_UTF8 :
+ GRN_TEXT_PUTS(ctx, buf, "UTF-8");
+ break;
+ case GRN_ENC_SJIS :
+ GRN_TEXT_PUTS(ctx, buf, "Shift_JIS");
+ break;
+ case GRN_ENC_LATIN1 :
+ GRN_TEXT_PUTS(ctx, buf, "Latin-1");
+ break;
+ case GRN_ENC_KOI8R :
+ GRN_TEXT_PUTS(ctx, buf, "KOI8-R");
+ break;
+ default :
+ GRN_TEXT_PUTS(ctx, buf, "unknown(");
+ grn_text_itoa(ctx, buf, encoding);
+ GRN_TEXT_PUTS(ctx, buf, ")");
+ break;
+ }
+
+ return buf;
+}
+
+grn_obj *
+grn_inspect_type(grn_ctx *ctx, grn_obj *buf, unsigned char type)
+{
+ switch (type) {
+ case GRN_VOID :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_VOID");
+ break;
+ case GRN_BULK :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_BULK");
+ break;
+ case GRN_PTR :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_PTR");
+ break;
+ case GRN_UVECTOR :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_UVECTOR");
+ break;
+ case GRN_PVECTOR :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_PVECTOR");
+ break;
+ case GRN_VECTOR :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_VECTOR");
+ break;
+ case GRN_MSG :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_MSG");
+ break;
+ case GRN_QUERY :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_QUERY");
+ break;
+ case GRN_ACCESSOR :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_ACCESSOR");
+ break;
+ case GRN_SNIP :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_SNIP");
+ break;
+ case GRN_PATSNIP :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_PATSNIP");
+ break;
+ case GRN_STRING :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_STRING");
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_CURSOR_TABLE_HASH_KEY");
+ break;
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_CURSOR_TABLE_PAT_KEY");
+ break;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_CURSOR_TABLE_DAT_KEY");
+ break;
+ case GRN_CURSOR_TABLE_NO_KEY :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_CURSOR_TABLE_NO_KEY");
+ break;
+ case GRN_CURSOR_COLUMN_INDEX :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_CURSOR_COLUMN_INDEX");
+ break;
+ case GRN_CURSOR_COLUMN_GEO_INDEX :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_CURSOR_COLUMN_GEO_INDEX");
+ break;
+ case GRN_TYPE :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_TYPE");
+ break;
+ case GRN_PROC :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_PROC");
+ break;
+ case GRN_EXPR :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_EXPR");
+ break;
+ case GRN_TABLE_HASH_KEY :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_TABLE_HASH_KEY");
+ break;
+ case GRN_TABLE_PAT_KEY :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_TABLE_PAT_KEY");
+ break;
+ case GRN_TABLE_DAT_KEY :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_TABLE_DAT_KEY");
+ break;
+ case GRN_TABLE_NO_KEY :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_TABLE_NO_KEY");
+ break;
+ case GRN_DB :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_DB");
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_COLUMN_FIX_SIZE");
+ break;
+ case GRN_COLUMN_VAR_SIZE :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_COLUMN_VAR_SIZE");
+ break;
+ case GRN_COLUMN_INDEX :
+ GRN_TEXT_PUTS(ctx, buf, "GRN_COLUMN_INDEX");
+ break;
+ default:
+ {
+ char type_in_hex[5]; /* "0xXX" */
+ sprintf(type_in_hex, "%#02x", type);
+ GRN_TEXT_PUTS(ctx, buf, "(unknown: ");
+ GRN_TEXT_PUTS(ctx, buf, type_in_hex);
+ GRN_TEXT_PUTS(ctx, buf, ")");
+ }
+ break;
+ }
+
+ return buf;
+}
+
+static grn_rc
+grn_proc_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_proc *proc = (grn_proc *)obj;
+ uint32_t i;
+
+ GRN_TEXT_PUTS(ctx, buf, "#<proc:");
+ switch (proc->type) {
+ case GRN_PROC_INVALID :
+ GRN_TEXT_PUTS(ctx, buf, "invalid");
+ GRN_TEXT_PUTS(ctx, buf, ">");
+ return GRN_SUCCESS;
+ break;
+ case GRN_PROC_TOKENIZER :
+ GRN_TEXT_PUTS(ctx, buf, "tokenizer");
+ break;
+ case GRN_PROC_COMMAND :
+ GRN_TEXT_PUTS(ctx, buf, "command");
+ break;
+ case GRN_PROC_FUNCTION :
+ GRN_TEXT_PUTS(ctx, buf, "function");
+ break;
+ case GRN_PROC_HOOK :
+ GRN_TEXT_PUTS(ctx, buf, "hook");
+ break;
+ case GRN_PROC_NORMALIZER :
+ GRN_TEXT_PUTS(ctx, buf, "normalizer");
+ break;
+ }
+ GRN_TEXT_PUTS(ctx, buf, " ");
+
+ grn_inspect_name(ctx, buf, obj);
+ GRN_TEXT_PUTS(ctx, buf, " ");
+
+ GRN_TEXT_PUTS(ctx, buf, "arguments:[");
+ for (i = 0; i < proc->nvars; i++) {
+ grn_expr_var *var = proc->vars + i;
+ if (i != 0) {
+ GRN_TEXT_PUTS(ctx, buf, ", ");
+ }
+ GRN_TEXT_PUT(ctx, buf, var->name, var->name_size);
+ }
+ GRN_TEXT_PUTS(ctx, buf, "]");
+
+ GRN_TEXT_PUTS(ctx, buf, ">");
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_vector_inspect(grn_ctx *ctx, grn_obj *buffer, grn_obj *vector)
+{
+ int i;
+ grn_obj *body = vector->u.v.body;
+
+ GRN_TEXT_PUTS(ctx, buffer, "[");
+ for (i = 0; i < vector->u.v.n_sections; i++) {
+ grn_section *section = &(vector->u.v.sections[i]);
+ const char *value_raw;
+
+ if (i > 0) {
+ GRN_TEXT_PUTS(ctx, buffer, ", ");
+ }
+
+ value_raw = GRN_BULK_HEAD(body) + section->offset;
+ GRN_TEXT_PUTS(ctx, buffer, "{");
+ GRN_TEXT_PUTS(ctx, buffer, "\"value\":");
+ {
+ grn_obj value_object;
+ GRN_OBJ_INIT(&value_object, GRN_BULK, GRN_OBJ_DO_SHALLOW_COPY,
+ section->domain);
+ grn_bulk_write(ctx, &value_object, value_raw, section->length);
+ grn_inspect(ctx, buffer, &value_object);
+ GRN_OBJ_FIN(ctx, &value_object);
+ }
+ GRN_TEXT_PUTS(ctx, buffer, ", \"weight\":");
+ grn_text_itoa(ctx, buffer, section->weight);
+ GRN_TEXT_PUTS(ctx, buffer, "}");
+ }
+ GRN_TEXT_PUTS(ctx, buffer, "]");
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_accessor_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ return grn_column_name_(ctx, obj, buf);
+}
+
+static grn_rc
+grn_type_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_id range_id;
+
+ GRN_TEXT_PUTS(ctx, buf, "#<type ");
+ grn_inspect_name(ctx, buf, obj);
+
+ range_id = grn_obj_get_range(ctx, obj);
+ GRN_TEXT_PUTS(ctx, buf, " size:");
+ grn_text_lltoa(ctx, buf, range_id);
+
+ GRN_TEXT_PUTS(ctx, buf, " type:");
+ if (obj->header.flags & GRN_OBJ_KEY_VAR_SIZE) {
+ GRN_TEXT_PUTS(ctx, buf, "var_size");
+ } else {
+ switch (obj->header.flags & GRN_OBJ_KEY_MASK) {
+ case GRN_OBJ_KEY_UINT :
+ GRN_TEXT_PUTS(ctx, buf, "uint");
+ break;
+ case GRN_OBJ_KEY_INT :
+ GRN_TEXT_PUTS(ctx, buf, "int");
+ break;
+ case GRN_OBJ_KEY_FLOAT :
+ GRN_TEXT_PUTS(ctx, buf, "float");
+ break;
+ case GRN_OBJ_KEY_GEO_POINT :
+ GRN_TEXT_PUTS(ctx, buf, "geo_point");
+ break;
+ default :
+ break;
+ }
+ }
+
+ GRN_TEXT_PUTS(ctx, buf, ">");
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_column_inspect_common(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_id range_id;
+
+ grn_inspect_name(ctx, buf, obj);
+
+ range_id = grn_obj_get_range(ctx, obj);
+ if (range_id) {
+ grn_obj *range = grn_ctx_at(ctx, range_id);
+ GRN_TEXT_PUTS(ctx, buf, " range:");
+ if (range) {
+ grn_inspect_name(ctx, buf, range);
+ grn_obj_unlink(ctx, range);
+ } else {
+ grn_text_lltoa(ctx, buf, range_id);
+ }
+ }
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_store_inspect_body(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_column_inspect_common(ctx, buf, obj);
+ GRN_TEXT_PUTS(ctx, buf, " type:");
+ switch (obj->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) {
+ case GRN_OBJ_COLUMN_VECTOR :
+ GRN_TEXT_PUTS(ctx, buf, "vector");
+ break;
+ case GRN_OBJ_COLUMN_SCALAR :
+ GRN_TEXT_PUTS(ctx, buf, "scalar");
+ break;
+ default:
+ break;
+ }
+
+ GRN_TEXT_PUTS(ctx, buf, " compress:");
+ switch (obj->header.flags & GRN_OBJ_COMPRESS_MASK) {
+ case GRN_OBJ_COMPRESS_NONE :
+ GRN_TEXT_PUTS(ctx, buf, "none");
+ break;
+ case GRN_OBJ_COMPRESS_ZLIB :
+ GRN_TEXT_PUTS(ctx, buf, "zlib");
+ break;
+ case GRN_OBJ_COMPRESS_LZO :
+ GRN_TEXT_PUTS(ctx, buf, "lzo");
+ break;
+ default:
+ break;
+ }
+
+ if (obj->header.flags & GRN_OBJ_RING_BUFFER) {
+ GRN_TEXT_PUTS(ctx, buf, " ring_buffer:true");
+ }
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_ra_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ GRN_TEXT_PUTS(ctx, buf, "#<column:fix_size ");
+ grn_store_inspect_body(ctx, buf, obj);
+ GRN_TEXT_PUTS(ctx, buf, ">");
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_ja_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ GRN_TEXT_PUTS(ctx, buf, "#<column:var_size ");
+ grn_store_inspect_body(ctx, buf, obj);
+ GRN_TEXT_PUTS(ctx, buf, ">");
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_ii_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_obj sources;
+ int i, n, have_flags = 0;
+ grn_id *source_ids;
+
+ GRN_TEXT_PUTS(ctx, buf, "#<column:index ");
+ grn_column_inspect_common(ctx, buf, obj);
+
+ GRN_TEXT_INIT(&sources, 0);
+ grn_obj_get_info(ctx, obj, GRN_INFO_SOURCE, &sources);
+ source_ids = (grn_id *)GRN_BULK_HEAD(&sources);
+ n = GRN_BULK_VSIZE(&sources) / sizeof(grn_id);
+ GRN_TEXT_PUTS(ctx, buf, " sources:[");
+ for (i = 0; i < n; i++) {
+ grn_id source_id;
+ grn_obj *source;
+ if (i) { GRN_TEXT_PUTS(ctx, buf, ", "); }
+ source_id = source_ids[i];
+ source = grn_ctx_at(ctx, source_id);
+ if (source) {
+ grn_inspect_name(ctx, buf, source);
+ } else {
+ grn_text_lltoa(ctx, buf, source_id);
+ }
+ }
+ GRN_TEXT_PUTS(ctx, buf, "]");
+ GRN_OBJ_FIN(ctx, &sources);
+
+ GRN_TEXT_PUTS(ctx, buf, " flags:");
+ if (obj->header.flags & GRN_OBJ_WITH_SECTION) {
+ GRN_TEXT_PUTS(ctx, buf, "SECTION");
+ have_flags = 1;
+ }
+ if (obj->header.flags & GRN_OBJ_WITH_WEIGHT) {
+ if (have_flags) { GRN_TEXT_PUTS(ctx, buf, "|"); }
+ GRN_TEXT_PUTS(ctx, buf, "WEIGHT");
+ have_flags = 1;
+ }
+ if (obj->header.flags & GRN_OBJ_WITH_POSITION) {
+ if (have_flags) { GRN_TEXT_PUTS(ctx, buf, "|"); }
+ GRN_TEXT_PUTS(ctx, buf, "POSITION");
+ have_flags = 1;
+ }
+ if (!have_flags) {
+ GRN_TEXT_PUTS(ctx, buf, "NONE");
+ }
+
+ GRN_TEXT_PUTS(ctx, buf, " elements:");
+ grn_ii_inspect_elements(ctx, (grn_ii *)obj, buf);
+
+ GRN_TEXT_PUTS(ctx, buf, ">");
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_table_type_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ switch (obj->header.type) {
+ case GRN_TABLE_HASH_KEY:
+ GRN_TEXT_PUTS(ctx, buf, "hash");
+ break;
+ case GRN_TABLE_PAT_KEY:
+ GRN_TEXT_PUTS(ctx, buf, "pat");
+ break;
+ case GRN_TABLE_NO_KEY:
+ GRN_TEXT_PUTS(ctx, buf, "no_key");
+ break;
+ }
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_table_key_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_obj *domain;
+ grn_id domain_id;
+
+ GRN_TEXT_PUTS(ctx, buf, "key:");
+ domain_id = obj->header.domain;
+ domain = grn_ctx_at(ctx, domain_id);
+ if (domain) {
+ grn_inspect_name(ctx, buf, domain);
+ grn_obj_unlink(ctx, domain);
+ } else if (domain_id) {
+ grn_text_lltoa(ctx, buf, domain_id);
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "(nil)");
+ }
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_table_columns_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_hash *cols;
+
+ GRN_TEXT_PUTS(ctx, buf, "columns:[");
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ if (grn_table_columns(ctx, obj, "", 0, (grn_obj *)cols)) {
+ int i = 0;
+ grn_id *key;
+ GRN_HASH_EACH(ctx, cols, id, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (col) {
+ if (i++ > 0) { GRN_TEXT_PUTS(ctx, buf, ", "); }
+ grn_column_name_(ctx, col, buf);
+ grn_obj_unlink(ctx, col);
+ }
+ });
+ }
+ grn_hash_close(ctx, cols);
+ }
+ GRN_TEXT_PUTS(ctx, buf, "]");
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_table_ids_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_table_cursor *tc;
+
+ GRN_TEXT_PUTS(ctx, buf, "ids:[");
+ tc = grn_table_cursor_open(ctx, obj, NULL, 0, NULL, 0,
+ 0, -1, GRN_CURSOR_ASCENDING);
+ if (tc) {
+ int i = 0;
+ grn_id id;
+ while ((id = grn_table_cursor_next(ctx, tc))) {
+ if (i++ > 0) { GRN_TEXT_PUTS(ctx, buf, ", "); }
+ grn_text_lltoa(ctx, buf, id);
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ GRN_TEXT_PUTS(ctx, buf, "]");
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_table_default_tokenizer_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_obj *default_tokenizer;
+
+ GRN_TEXT_PUTS(ctx, buf, "default_tokenizer:");
+ default_tokenizer = grn_obj_get_info(ctx, obj,
+ GRN_INFO_DEFAULT_TOKENIZER, NULL);
+ if (default_tokenizer) {
+ grn_inspect_name(ctx, buf, default_tokenizer);
+ grn_obj_unlink(ctx, default_tokenizer);
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "(nil)");
+ }
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_table_normalizer_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_obj *normalizer;
+
+ GRN_TEXT_PUTS(ctx, buf, "normalizer:");
+ normalizer = grn_obj_get_info(ctx, obj, GRN_INFO_NORMALIZER, NULL);
+ if (normalizer) {
+ grn_inspect_name(ctx, buf, normalizer);
+ grn_obj_unlink(ctx, normalizer);
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "(nil)");
+ }
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_table_keys_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_table_cursor *tc;
+
+ GRN_TEXT_PUTS(ctx, buf, "keys:[");
+ tc = grn_table_cursor_open(ctx, obj, NULL, 0, NULL, 0,
+ 0, -1, GRN_CURSOR_ASCENDING);
+ if (tc) {
+ int i = 0;
+ grn_id id;
+ grn_obj key;
+ GRN_OBJ_INIT(&key, GRN_BULK, 0, obj->header.domain);
+ while ((id = grn_table_cursor_next(ctx, tc))) {
+ if (i++ > 0) { GRN_TEXT_PUTS(ctx, buf, ", "); }
+ grn_table_get_key2(ctx, obj, id, &key);
+ grn_inspect(ctx, buf, &key);
+ GRN_BULK_REWIND(&key);
+ }
+ GRN_OBJ_FIN(ctx, &key);
+ grn_table_cursor_close(ctx, tc);
+ }
+ GRN_TEXT_PUTS(ctx, buf, "]");
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_table_subrec_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ GRN_TEXT_PUTS(ctx, buf, "subrec:");
+ if (obj->header.flags & GRN_OBJ_WITH_SUBREC) {
+ switch (obj->header.flags & GRN_OBJ_UNIT_MASK) {
+ case GRN_OBJ_UNIT_DOCUMENT_NONE :
+ GRN_TEXT_PUTS(ctx, buf, "document:none");
+ break;
+ case GRN_OBJ_UNIT_DOCUMENT_SECTION :
+ GRN_TEXT_PUTS(ctx, buf, "document:section");
+ break;
+ case GRN_OBJ_UNIT_DOCUMENT_POSITION :
+ GRN_TEXT_PUTS(ctx, buf, "document:position");
+ break;
+ case GRN_OBJ_UNIT_SECTION_NONE :
+ GRN_TEXT_PUTS(ctx, buf, "section:none");
+ break;
+ case GRN_OBJ_UNIT_SECTION_POSITION :
+ GRN_TEXT_PUTS(ctx, buf, "section:popsition");
+ break;
+ case GRN_OBJ_UNIT_POSITION_NONE :
+ GRN_TEXT_PUTS(ctx, buf, "section:none");
+ break;
+ case GRN_OBJ_UNIT_USERDEF_DOCUMENT :
+ GRN_TEXT_PUTS(ctx, buf, "userdef:document");
+ break;
+ case GRN_OBJ_UNIT_USERDEF_SECTION :
+ GRN_TEXT_PUTS(ctx, buf, "userdef:section");
+ break;
+ case GRN_OBJ_UNIT_USERDEF_POSITION :
+ GRN_TEXT_PUTS(ctx, buf, "userdef:position");
+ break;
+ }
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "none");
+ }
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_table_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_id range_id;
+ grn_obj *range;
+
+ GRN_TEXT_PUTS(ctx, buf, "#<table:");
+ grn_table_type_inspect(ctx, buf, obj);
+ GRN_TEXT_PUTS(ctx, buf, " ");
+
+ grn_inspect_name(ctx, buf, obj);
+
+ if (obj->header.type != GRN_TABLE_NO_KEY) {
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ grn_table_key_inspect(ctx, buf, obj);
+ }
+
+ GRN_TEXT_PUTS(ctx, buf, " value:");
+ range_id = grn_obj_get_range(ctx, obj);
+ range = grn_ctx_at(ctx, range_id);
+ if (range) {
+ grn_inspect_name(ctx, buf, range);
+ } else if (range_id) {
+ grn_text_lltoa(ctx, buf, range_id);
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "(nil)");
+ }
+
+ GRN_TEXT_PUTS(ctx, buf, " size:");
+ grn_text_lltoa(ctx, buf, grn_table_size(ctx, obj));
+
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ grn_table_columns_inspect(ctx, buf, obj);
+
+ if (obj->header.type == GRN_TABLE_NO_KEY) {
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ grn_table_ids_inspect(ctx, buf, obj);
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ grn_table_default_tokenizer_inspect(ctx, buf, obj);
+
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ grn_table_normalizer_inspect(ctx, buf, obj);
+
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ grn_table_keys_inspect(ctx, buf, obj);
+ }
+
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ grn_table_subrec_inspect(ctx, buf, obj);
+
+ if (obj->header.type == GRN_TABLE_PAT_KEY) {
+ GRN_TEXT_PUTS(ctx, buf, " nodes:");
+ grn_pat_inspect_nodes(ctx, (grn_pat *)obj, buf);
+ }
+
+ GRN_TEXT_PUTS(ctx, buf, ">");
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_geo_point_inspect_point(grn_ctx *ctx, grn_obj *buf, int point)
+{
+ GRN_TEXT_PUTS(ctx, buf, "(");
+ grn_text_itoa(ctx, buf, point / 1000 / 3600 % 3600);
+ GRN_TEXT_PUTS(ctx, buf, ", ");
+ grn_text_itoa(ctx, buf, point / 1000 / 60 % 60);
+ GRN_TEXT_PUTS(ctx, buf, ", ");
+ grn_text_itoa(ctx, buf, point / 1000 % 60);
+ GRN_TEXT_PUTS(ctx, buf, ", ");
+ grn_text_itoa(ctx, buf, point % 1000);
+ GRN_TEXT_PUTS(ctx, buf, ")");
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_geo_point_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ int latitude, longitude;
+
+ GRN_GEO_POINT_VALUE(obj, latitude, longitude);
+
+ GRN_TEXT_PUTS(ctx, buf, "[");
+ GRN_TEXT_PUTS(ctx, buf, "(");
+ grn_text_itoa(ctx, buf, latitude);
+ GRN_TEXT_PUTS(ctx, buf, ",");
+ grn_text_itoa(ctx, buf, longitude);
+ GRN_TEXT_PUTS(ctx, buf, ")");
+
+ GRN_TEXT_PUTS(ctx, buf, " (");
+ grn_geo_point_inspect_point(ctx, buf, latitude);
+ GRN_TEXT_PUTS(ctx, buf, ",");
+ grn_geo_point_inspect_point(ctx, buf, longitude);
+ GRN_TEXT_PUTS(ctx, buf, ")");
+
+ {
+ int i, j;
+ grn_geo_point point;
+ uint8_t encoded[sizeof(grn_geo_point)];
+
+ GRN_TEXT_PUTS(ctx, buf, " [");
+ point.latitude = latitude;
+ point.longitude = longitude;
+ grn_gton(encoded, &point, sizeof(grn_geo_point));
+ for (i = 0; i < sizeof(grn_geo_point); i++) {
+ if (i != 0) {
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ }
+ for (j = 0; j < 8; j++) {
+ grn_text_itoa(ctx, buf, (encoded[i] >> (7 - j)) & 1);
+ }
+ }
+ GRN_TEXT_PUTS(ctx, buf, "]");
+ }
+
+ GRN_TEXT_PUTS(ctx, buf, "]");
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_json_load_open_bracket_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ uint32_t i, n;
+
+ n = GRN_UINT32_VALUE(obj);
+
+ GRN_TEXT_PUTS(ctx, buf, "[");
+ for (i = 0; i < n; i++) {
+ grn_obj *value;
+ value = obj + 1 + i;
+ if (i > 0) {
+ GRN_TEXT_PUTS(ctx, buf, ", ");
+ }
+ grn_inspect(ctx, buf, value);
+ }
+ GRN_TEXT_PUTS(ctx, buf, "]");
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_json_load_open_brace_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ uint32_t i, n;
+
+ n = GRN_UINT32_VALUE(obj);
+
+ GRN_TEXT_PUTS(ctx, buf, "{");
+ for (i = 0; i < n; i += 2) {
+ grn_obj *key, *value;
+ key = obj + 1 + i;
+ value = key + 1;
+ if (i > 0) {
+ GRN_TEXT_PUTS(ctx, buf, ", ");
+ }
+ grn_inspect(ctx, buf, key);
+ GRN_TEXT_PUTS(ctx, buf, ": ");
+ grn_inspect(ctx, buf, value);
+ }
+ GRN_TEXT_PUTS(ctx, buf, "}");
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_record_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ grn_id id;
+ grn_obj *table;
+ grn_hash *cols;
+
+ table = grn_ctx_at(ctx, obj->header.domain);
+ GRN_TEXT_PUTS(ctx, buf, "#<record:");
+ if (table) {
+ grn_table_type_inspect(ctx, buf, table);
+ GRN_TEXT_PUTS(ctx, buf, ":");
+ grn_inspect_name(ctx, buf, table);
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "(anonymous table:");
+ grn_text_lltoa(ctx, buf, obj->header.domain);
+ GRN_TEXT_PUTS(ctx, buf, ")");
+ }
+
+ GRN_TEXT_PUTS(ctx, buf, " id:");
+ id = GRN_RECORD_VALUE(obj);
+ grn_text_lltoa(ctx, buf, id);
+
+ if (table && grn_table_at(ctx, table, id)) {
+ if (table->header.type != GRN_TABLE_NO_KEY) {
+ grn_obj key;
+ GRN_TEXT_PUTS(ctx, buf, " key:");
+ GRN_OBJ_INIT(&key, GRN_BULK, 0, table->header.domain);
+ grn_table_get_key2(ctx, table, id, &key);
+ grn_inspect(ctx, buf, &key);
+ GRN_OBJ_FIN(ctx, &key);
+ }
+ if ((cols = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY))) {
+ if (grn_table_columns(ctx, table, "", 0, (grn_obj *)cols)) {
+ grn_id *key;
+ GRN_HASH_EACH(ctx, cols, column_id, &key, NULL, NULL, {
+ grn_obj *col = grn_ctx_at(ctx, *key);
+ if (col) {
+ grn_obj value;
+ GRN_TEXT_INIT(&value, 0);
+ GRN_TEXT_PUTS(ctx, buf, " ");
+ grn_column_name_(ctx, col, buf);
+ GRN_TEXT_PUTS(ctx, buf, ":");
+ grn_obj_get_value(ctx, col, id, &value);
+ grn_inspect(ctx, buf, &value);
+ GRN_OBJ_FIN(ctx, &value);
+ grn_obj_unlink(ctx, col);
+ }
+ });
+ }
+ grn_hash_close(ctx, cols);
+ }
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "(nonexistent)");
+ }
+ GRN_TEXT_PUTS(ctx, buf, ">");
+
+ if (table) {
+ grn_obj_unlink(ctx, table);
+ }
+
+ return GRN_SUCCESS;
+}
+
+static grn_rc
+grn_uvector_record_inspect(grn_ctx *ctx, grn_obj *buf, grn_obj *obj)
+{
+ unsigned int i, n = 0;
+ grn_obj record;
+
+ GRN_RECORD_INIT(&record, 0, obj->header.domain);
+ GRN_TEXT_PUTS(ctx, buf, "[");
+ n = grn_vector_size(ctx, obj);
+ for (i = 0; i < n; i++) {
+ grn_id id;
+ unsigned int weight;
+
+ if (i > 0) {
+ GRN_TEXT_PUTS(ctx, buf, ", ");
+ }
+
+ id = grn_uvector_get_element(ctx, obj, i, &weight);
+ GRN_TEXT_PUTS(ctx, buf, "#<element record:");
+ GRN_RECORD_SET(ctx, &record, id);
+ grn_inspect(ctx, buf, &record);
+ grn_text_printf(ctx, buf, ", weight:%u>", weight);
+ }
+ GRN_TEXT_PUTS(ctx, buf, "]");
+ GRN_OBJ_FIN(ctx, &record);
+
+ return GRN_SUCCESS;
+}
+
+grn_obj *
+grn_inspect(grn_ctx *ctx, grn_obj *buffer, grn_obj *obj)
+{
+ grn_obj *domain;
+
+ if (!buffer) {
+ buffer = grn_obj_open(ctx, GRN_BULK, 0, GRN_DB_TEXT);
+ }
+
+ if (!obj) {
+ GRN_TEXT_PUTS(ctx, buffer, "(NULL)");
+ return buffer;
+ }
+
+ switch (obj->header.type) {
+ case GRN_VOID :
+ /* TODO */
+ break;
+ case GRN_BULK :
+ switch (obj->header.domain) {
+ case GRN_DB_TOKYO_GEO_POINT :
+ case GRN_DB_WGS84_GEO_POINT :
+ grn_geo_point_inspect(ctx, buffer, obj);
+ return buffer;
+ case GRN_JSON_LOAD_OPEN_BRACKET :
+ grn_json_load_open_bracket_inspect(ctx, buffer, obj);
+ return buffer;
+ case GRN_JSON_LOAD_OPEN_BRACE :
+ grn_json_load_open_brace_inspect(ctx, buffer, obj);
+ return buffer;
+ default :
+ domain = grn_ctx_at(ctx, obj->header.domain);
+ if (domain) {
+ grn_id type = domain->header.type;
+ grn_obj_unlink(ctx, domain);
+ switch (type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ grn_record_inspect(ctx, buffer, obj);
+ return buffer;
+ default :
+ break;
+ }
+ }
+ }
+ break;
+ case GRN_PTR :
+ /* TODO */
+ break;
+ case GRN_UVECTOR :
+ domain = grn_ctx_at(ctx, obj->header.domain);
+ if (domain) {
+ grn_id type = domain->header.type;
+ grn_obj_unlink(ctx, domain);
+ switch (type) {
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ grn_uvector_record_inspect(ctx, buffer, obj);
+ return buffer;
+ default :
+ break;
+ }
+ }
+ break;
+ case GRN_PVECTOR :
+ /* TODO */
+ break;
+ case GRN_VECTOR :
+ grn_vector_inspect(ctx, buffer, obj);
+ return buffer;
+ case GRN_MSG :
+ /* TODO */
+ break;
+ case GRN_ACCESSOR :
+ grn_accessor_inspect(ctx, buffer, obj);
+ return buffer;
+ case GRN_SNIP :
+ case GRN_PATSNIP :
+ /* TODO */
+ break;
+ case GRN_STRING :
+ grn_string_inspect(ctx, buffer, obj);
+ break;
+ case GRN_CURSOR_TABLE_HASH_KEY :
+ /* TODO */
+ break;
+ case GRN_CURSOR_TABLE_PAT_KEY :
+ grn_pat_cursor_inspect(ctx, (grn_pat_cursor *)obj, buffer);
+ return buffer;
+ case GRN_CURSOR_TABLE_DAT_KEY :
+ case GRN_CURSOR_TABLE_NO_KEY :
+ case GRN_CURSOR_COLUMN_INDEX :
+ case GRN_CURSOR_COLUMN_GEO_INDEX :
+ /* TODO */
+ break;
+ case GRN_TYPE :
+ grn_type_inspect(ctx, buffer, obj);
+ return buffer;
+ case GRN_PROC :
+ grn_proc_inspect(ctx, buffer, obj);
+ return buffer;
+ case GRN_EXPR :
+ grn_expr_inspect(ctx, buffer, obj);
+ return buffer;
+ case GRN_TABLE_HASH_KEY :
+ case GRN_TABLE_PAT_KEY :
+ case GRN_TABLE_DAT_KEY :
+ case GRN_TABLE_NO_KEY :
+ grn_table_inspect(ctx, buffer, obj);
+ return buffer;
+ case GRN_DB :
+ /* TODO */
+ break;
+ case GRN_COLUMN_FIX_SIZE :
+ grn_ra_inspect(ctx, buffer, obj);
+ return buffer;
+ case GRN_COLUMN_VAR_SIZE :
+ grn_ja_inspect(ctx, buffer, obj);
+ return buffer;
+ case GRN_COLUMN_INDEX :
+ grn_ii_inspect(ctx, buffer, obj);
+ return buffer;
+ default :
+ break;
+ }
+
+ grn_text_otoj(ctx, buffer, obj, NULL);
+ return buffer;
+}
+
+void
+grn_p(grn_ctx *ctx, grn_obj *obj)
+{
+ grn_obj buffer;
+
+ GRN_TEXT_INIT(&buffer, 0);
+ grn_inspect(ctx, &buffer, obj);
+ printf("%.*s\n", (int)GRN_TEXT_LEN(&buffer), GRN_TEXT_VALUE(&buffer));
+ grn_obj_unlink(ctx, &buffer);
+}
+
+void
+grn_p_geo_point(grn_ctx *ctx, grn_geo_point *point)
+{
+ grn_obj obj;
+
+ GRN_WGS84_GEO_POINT_INIT(&obj, 0);
+ GRN_GEO_POINT_SET(ctx, &obj, point->latitude, point->longitude);
+ grn_p(ctx, &obj);
+ grn_obj_unlink(ctx, &obj);
+}
+
+#ifdef WIN32
+static char *win32_base_dir = NULL;
+const char *
+grn_win32_base_dir(void)
+{
+ if (!win32_base_dir) {
+ HMODULE dll;
+ const wchar_t *dll_filename = GRN_DLL_FILENAME;
+ wchar_t absolute_dll_filename[MAX_PATH];
+ DWORD absolute_dll_filename_size;
+ dll = GetModuleHandleW(dll_filename);
+ absolute_dll_filename_size = GetModuleFileNameW(dll,
+ absolute_dll_filename,
+ MAX_PATH);
+ if (absolute_dll_filename_size == 0) {
+ win32_base_dir = strdup(".");
+ } else {
+ DWORD ansi_dll_filename_size;
+ ansi_dll_filename_size =
+ WideCharToMultiByte(CP_ACP, 0,
+ absolute_dll_filename, absolute_dll_filename_size,
+ NULL, 0, NULL, NULL);
+ if (ansi_dll_filename_size == 0) {
+ win32_base_dir = strdup(".");
+ } else {
+ char *path;
+ win32_base_dir = malloc(ansi_dll_filename_size + 1);
+ WideCharToMultiByte(CP_ACP, 0,
+ absolute_dll_filename, absolute_dll_filename_size,
+ win32_base_dir, ansi_dll_filename_size,
+ NULL, NULL);
+ win32_base_dir[ansi_dll_filename_size] = '\0';
+ if ((path = strrchr(win32_base_dir, '\\'))) {
+ *path = '\0';
+ }
+ path = strrchr(win32_base_dir, '\\');
+ if (path && (strcasecmp(path + 1, "bin") == 0 ||
+ strcasecmp(path + 1, "lib") == 0)) {
+ *path = '\0';
+ } else {
+ path = win32_base_dir + strlen(win32_base_dir);
+ *path = '\0';
+ }
+ for (path = win32_base_dir; *path; path++) {
+ if (*path == '\\') {
+ *path = '/';
+ }
+ }
+ }
+ }
+ }
+ return win32_base_dir;
+}
+#endif
diff --git a/storage/mroonga/vendor/groonga/lib/util.h b/storage/mroonga/vendor/groonga/lib/util.h
new file mode 100644
index 00000000000..b4dbb000aed
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/lib/util.h
@@ -0,0 +1,48 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2010-2011 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_UTIL_H
+#define GRN_UTIL_H
+
+#ifndef GROONGA_IN_H
+#include "groonga_in.h"
+#endif /* GROONGA_IN_H */
+
+#ifndef GRN_CTX_H
+#include "ctx.h"
+#endif /* GRN_CTX_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+GRN_API grn_rc grn_normalize_offset_and_limit(grn_ctx *ctx, int size, int *offset, int *limit);
+
+GRN_API grn_obj *grn_inspect(grn_ctx *ctx, grn_obj *buffer, grn_obj *obj);
+GRN_API grn_obj *grn_inspect_name(grn_ctx *ctx, grn_obj *buffer, grn_obj *obj);
+GRN_API grn_obj *grn_inspect_encoding(grn_ctx *ctx, grn_obj *buffer, grn_encoding encoding);
+GRN_API grn_obj *grn_inspect_type(grn_ctx *ctx, grn_obj *buffer, unsigned char type);
+void grn_p(grn_ctx *ctx, grn_obj *obj);
+void grn_p_geo_point(grn_ctx *ctx, grn_geo_point *point);
+
+GRN_API const char *grn_win32_base_dir(void);
+GRN_API char *grn_path_separator_to_system(char *dest, char *groonga_path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRN_UTIL_H */
diff --git a/storage/mroonga/vendor/groonga/nginx_version b/storage/mroonga/vendor/groonga/nginx_version
new file mode 100644
index 00000000000..10c088013f8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/nginx_version
@@ -0,0 +1 @@
+1.7.4
diff --git a/storage/mroonga/vendor/groonga/packages/Makefile.am b/storage/mroonga/vendor/groonga/packages/Makefile.am
new file mode 100644
index 00000000000..5ba46fdfd66
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/Makefile.am
@@ -0,0 +1,7 @@
+SUBDIRS = \
+ apt \
+ ubuntu \
+ rpm \
+ yum \
+ source \
+ windows
diff --git a/storage/mroonga/vendor/groonga/packages/apt/Makefile.am b/storage/mroonga/vendor/groonga/packages/apt/Makefile.am
new file mode 100644
index 00000000000..3a6a00a4628
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/Makefile.am
@@ -0,0 +1,79 @@
+REPOSITORIES_PATH = repositories
+DISTRIBUTIONS = debian
+CHROOT_BASE = /var/lib/chroot
+ARCHITECTURES = i386 amd64
+CODES = wheezy jessie unstable
+KEYRING_PACKAGE = groonga-keyring
+KEYRING_VERSION = 2012.05.29
+KEYRING_BASE_NAME = $(KEYRING_PACKAGE)-$(KEYRING_VERSION)
+
+all:
+
+release: build sign-packages update-repository sign-repository upload
+
+remove-existing-packages:
+ for distribution in $(DISTRIBUTIONS); do \
+ find $(REPOSITORIES_PATH)/$${distribution}/pool \
+ -type f -delete; \
+ done
+
+ensure-rsync-path:
+ @if test -z "$(RSYNC_PATH)"; then \
+ echo "--with-rsync-path configure option must be specified."; \
+ false; \
+ fi
+
+download: ensure-rsync-path
+ for distribution in $(DISTRIBUTIONS); do \
+ rsync -avz --progress --delete \
+ $(RSYNC_PATH)/$${distribution} \
+ ${REPOSITORIES_PATH}/; \
+ done
+
+sign-packages:
+ ./sign-packages.sh '$(GPG_UID)' '$(REPOSITORIES_PATH)/' '$(CODES)'
+
+update-repository:
+ ./update-repository.sh '$(PACKAGE_NAME)' '$(REPOSITORIES_PATH)/' \
+ '$(ARCHITECTURES)' '$(CODES)'
+
+sign-repository:
+ ./sign-repository.sh '$(GPG_UID)' '$(REPOSITORIES_PATH)/' '$(CODES)'
+
+upload: ensure-rsync-path
+ for distribution in $(DISTRIBUTIONS); do \
+ (cd $(REPOSITORIES_PATH)/$${distribution}; \
+ rsync -avz --progress --delete \
+ dists pool $(RSYNC_PATH)/$${distribution}); \
+ done
+
+build: build-package-deb build-keyring-deb
+
+build-package-deb: source
+ ./build-in-chroot.sh \
+ $(PACKAGE) $(VERSION) $(srcdir)/.. $(REPOSITORIES_PATH)/ \
+ $(CHROOT_BASE) '$(ARCHITECTURES)' '$(CODES)'
+
+build-keyring-deb: keyring-source
+ ./build-in-chroot.sh \
+ $(KEYRING_PACKAGE) $(KEYRING_VERSION) $(srcdir) \
+ $(REPOSITORIES_PATH)/ $(CHROOT_BASE) \
+ '$(ARCHITECTURES)' '$(CODES)'
+
+source: ../$(PACKAGE)-$(VERSION).tar.gz
+
+../$(PACKAGE)-$(VERSION).tar.gz:
+ ln -s $(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz ../
+
+keyring-source: $(KEYRING_BASE_NAME).tar.gz
+
+ensure-public-key:
+ gpg --list-keys '$(GPG_UID)' > /dev/null || \
+ gpg --keyserver keyserver.ubuntu.com --recv-key '$(GPG_UID)'
+
+$(KEYRING_BASE_NAME).tar.gz: ensure-public-key
+ rm -rf $(KEYRING_BASE_NAME)
+ mkdir -p $(KEYRING_BASE_NAME)
+ gpg --armor --export '$(GPG_UID)' > \
+ $(KEYRING_BASE_NAME)/groonga-keyring.gpg
+ tar cvzf $(KEYRING_BASE_NAME).tar.gz $(KEYRING_BASE_NAME)
diff --git a/storage/mroonga/vendor/groonga/packages/apt/build-deb.sh b/storage/mroonga/vendor/groonga/packages/apt/build-deb.sh
new file mode 100755
index 00000000000..1f5a5b03985
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/build-deb.sh
@@ -0,0 +1,115 @@
+#!/bin/sh
+
+LANG=C
+
+PACKAGE=$(cat /tmp/build-package)
+USER_NAME=$(cat /tmp/build-user)
+VERSION=$(cat /tmp/build-version)
+DEPENDED_PACKAGES=$(cat /tmp/depended-packages)
+BUILD_SCRIPT=/tmp/build-deb-in-chroot.sh
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+if [ ! -x /usr/bin/lsb_release ]; then
+ run apt-get update
+ run apt-get install -y lsb-release
+fi
+
+distribution=$(lsb_release --id --short)
+code_name=$(lsb_release --codename --short)
+
+security_list=/etc/apt/sources.list.d/security.list
+if [ ! -f "${security_list}" ]; then
+ case ${distribution} in
+ Debian)
+ if [ "${code_name}" = "sid" ]; then
+ touch "${security_list}"
+ else
+ cat <<EOF > "${security_list}"
+deb http://security.debian.org/ ${code_name}/updates main
+deb-src http://security.debian.org/ ${code_name}/updates main
+EOF
+ fi
+ ;;
+ Ubuntu)
+ cat <<EOF > "${security_list}"
+deb http://security.ubuntu.com/ubuntu ${code_name}-security main restricted
+deb-src http://security.ubuntu.com/ubuntu ${code_name}-security main restricted
+EOF
+ ;;
+ esac
+fi
+
+sources_list=/etc/apt/sources.list
+if [ "$distribution" = "Ubuntu" ] && \
+ ! (grep '^deb' $sources_list | grep -q universe); then
+ run sed -i'' -e 's/main$/main universe/g' $sources_list
+fi
+
+if [ ! -x /usr/bin/aptitude ]; then
+ run apt-get update
+ run apt-get install -y aptitude
+fi
+run aptitude update -V -D
+run aptitude safe-upgrade -V -D -y
+
+run aptitude install -V -D -y ruby
+
+if aptitude show libmsgpack-dev > /dev/null 2>&1; then
+ DEPENDED_PACKAGES="${DEPENDED_PACKAGES} libmsgpack-dev"
+else
+ ruby -i'' -ne 'print $_ unless /libmsgpack/' /tmp/${PACKAGE}-debian/control
+fi
+
+case $(lsb_release -s -c) in
+ jessie|sid)
+ DEPENDED_PACKAGES="${DEPENDED_PACKAGES} libzmq3-dev"
+ ;;
+ *)
+ DEPENDED_PACKAGES="${DEPENDED_PACKAGES} libzmq-dev"
+ ;;
+esac
+
+if aptitude show libevent-dev > /dev/null 2>&1; then
+ DEPENDED_PACKAGES="${DEPENDED_PACKAGES} libevent-dev"
+else
+ ruby -i'' -ne 'print $_ unless /libevent/' /tmp/${PACKAGE}-debian/control
+fi
+
+if aptitude show liblzo2-dev > /dev/null 2>&1; then
+ DEPENDED_PACKAGES="${DEPENDED_PACKAGES} liblzo2-dev"
+else
+ ruby -i'' -ne 'print $_ unless /liblzo2-dev/' /tmp/${PACKAGE}-debian/control
+fi
+
+run aptitude install -V -D -y devscripts ${DEPENDED_PACKAGES}
+run aptitude clean
+
+if ! id $USER_NAME >/dev/null 2>&1; then
+ run useradd -m $USER_NAME
+fi
+
+cat <<EOF > $BUILD_SCRIPT
+#!/bin/sh
+
+rm -rf build
+mkdir -p build
+
+cp /tmp/${PACKAGE}-${VERSION}.tar.gz build/${PACKAGE}_${VERSION}.orig.tar.gz
+cd build
+tar xfz ${PACKAGE}_${VERSION}.orig.tar.gz
+cd ${PACKAGE}-${VERSION}/
+cp -rp /tmp/${PACKAGE}-debian debian
+# export DEB_BUILD_OPTIONS=noopt
+debuild -us -uc
+EOF
+
+run chmod +x $BUILD_SCRIPT
+run su - $USER_NAME $BUILD_SCRIPT
diff --git a/storage/mroonga/vendor/groonga/packages/apt/build-in-chroot.sh b/storage/mroonga/vendor/groonga/packages/apt/build-in-chroot.sh
new file mode 100755
index 00000000000..9d97d42c077
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/build-in-chroot.sh
@@ -0,0 +1,134 @@
+#!/bin/sh
+
+if [ $# != 7 ]; then
+ echo "Usage: $0 PACKAGE VERSION SOURCE_DIR DESTINATION CHROOT_BASE ARCHITECTURES CODES"
+ echo " e.g.: $0 groonga 0.1.9 SOURCE_DIR repositories/ /var/lib/chroot 'i386 amd64' 'lenny unstable hardy karmic'"
+ exit 1
+fi
+
+PACKAGE=$1
+VERSION=$2
+SOURCE_DIR=$3
+DESTINATION=$4
+CHROOT_BASE=$5
+ARCHITECTURES=$6
+CODES=$7
+
+PATH=/usr/local/sbin:/usr/sbin:$PATH
+
+script_base_dir=`dirname $0`
+
+if test "$PARALLEL" = "yes"; then
+ parallel="yes"
+else
+ parallel="no"
+fi
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+run_sudo()
+{
+ run sudo "$@"
+}
+
+build_chroot()
+{
+ architecture=$1
+ code_name=$2
+
+ run_sudo debootstrap --arch $architecture $code_name $base_dir
+
+ case $code_name in
+ wheezy|jessie|unstable)
+ run_sudo sed -i'' -e 's/us/jp/' $base_dir/etc/apt/sources.list
+ ;;
+ *)
+ run_sudo sed -i'' \
+ -e 's,http://archive,http://jp.archive,' \
+ -e 's/main$/main universe/' \
+ $base_dir/etc/apt/sources.list
+ ;;
+ esac
+
+ run_sudo sh -c "echo >> /etc/fstab"
+ run_sudo sh -c "echo /sys ${base_dir}/sys none bind 0 0 >> /etc/fstab"
+ run_sudo sh -c "echo /dev ${base_dir}/dev none bind 0 0 >> /etc/fstab"
+ run_sudo sh -c "echo devpts-chroot ${base_dir}/dev/pts devpts defaults 0 0 >> /etc/fstab"
+ run_sudo sh -c "echo proc-chroot ${base_dir}/proc proc defaults 0 0 >> /etc/fstab"
+ run_sudo mount ${base_dir}/sys
+ run_sudo mount ${base_dir}/dev
+ run_sudo mount ${base_dir}/dev/pts
+ run_sudo mount ${base_dir}/proc
+}
+
+build()
+{
+ architecture=$1
+ code_name=$2
+
+ target=${code_name}-${architecture}
+ base_dir=${CHROOT_BASE}/${target}
+ if [ ! -d $base_dir ]; then
+ run build_chroot $architecture $code_name
+ fi
+
+ case ${code_name} in
+ wheezy|jessie|unstable)
+ distribution=debian
+ component=main
+ ;;
+ *)
+ distribution=ubuntu
+ component=universe
+ ;;
+ esac
+
+ source_dir=${SOURCE_DIR}
+ build_user=${PACKAGE}-build
+ build_user_dir=${base_dir}/home/$build_user
+ build_dir=${build_user_dir}/build
+ pool_base_dir=${DESTINATION}${distribution}/pool/${code_name}/${component}
+ package_initial=$(echo ${PACKAGE} | sed -e 's/\(.\).*/\1/')
+ pool_dir=${pool_base_dir}/${package_initial}/${PACKAGE}
+ run cp $source_dir/${PACKAGE}-${VERSION}.tar.gz \
+ ${CHROOT_BASE}/$target/tmp/
+ run rm -rf ${CHROOT_BASE}/$target/tmp/${PACKAGE}-debian
+ run cp -rp $source_dir/debian/ \
+ ${CHROOT_BASE}/$target/tmp/${PACKAGE}-debian
+ run echo $PACKAGE > ${CHROOT_BASE}/$target/tmp/build-package
+ run echo $VERSION > ${CHROOT_BASE}/$target/tmp/build-version
+ run echo $build_user > ${CHROOT_BASE}/$target/tmp/build-user
+ run cp ${script_base_dir}/${PACKAGE}-depended-packages \
+ ${CHROOT_BASE}/$target/tmp/depended-packages
+ run cp ${script_base_dir}/build-deb.sh \
+ ${CHROOT_BASE}/$target/tmp/
+ run_sudo rm -rf $build_dir
+ run_sudo su -c "/usr/sbin/chroot ${CHROOT_BASE}/$target /tmp/build-deb.sh"
+ run mkdir -p $pool_dir
+ for path in $build_dir/*; do
+ [ -f $path ] && run cp -p $path $pool_dir/
+ done
+}
+
+for architecture in $ARCHITECTURES; do
+ for code_name in $CODES; do
+ if test "$parallel" = "yes"; then
+ build $architecture $code_name &
+ else
+ mkdir -p tmp
+ build_log=tmp/build-$code_name-$architecture.log
+ build $architecture $code_name 2>&1 | tee $build_log
+ fi;
+ done;
+done
+
+if test "$parallel" = "yes"; then
+ wait
+fi
diff --git a/storage/mroonga/vendor/groonga/packages/apt/debian/changelog b/storage/mroonga/vendor/groonga/packages/apt/debian/changelog
new file mode 100644
index 00000000000..d1e8835e2a5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/debian/changelog
@@ -0,0 +1,11 @@
+groonga-keyring (2012.05.29-1) unstable; urgency=low
+
+ * Replace old key id to new one.
+
+ -- groonga Key (groonga Official Signing Key) <packages@groonga.org> Tue, 29 May 2012 00:00:00 +0900
+
+groonga-keyring (2012.04.29-1) unstable; urgency=low
+
+ * Initial release.
+
+ -- groonga Key (groonga Official Signing Key) <packages@groonga.org> Sun, 29 Apr 2012 00:00:00 +0900
diff --git a/storage/mroonga/vendor/groonga/packages/apt/debian/compat b/storage/mroonga/vendor/groonga/packages/apt/debian/compat
new file mode 100644
index 00000000000..7f8f011eb73
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/storage/mroonga/vendor/groonga/packages/apt/debian/control b/storage/mroonga/vendor/groonga/packages/apt/debian/control
new file mode 100644
index 00000000000..949ce13370a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/debian/control
@@ -0,0 +1,18 @@
+Source: groonga-keyring
+Section: misc
+Priority: important
+Maintainer: groonga project <packages@groonga.org>
+Build-Depends:
+ debhelper (>= 7.0.50)
+Standards-Version: 3.9.1
+Homepage: http://groonga.org/
+
+Package: groonga-keyring
+Section: misc
+Architecture: all
+Depends:
+ ${misc:Depends},
+ gnupg
+Description: GnuPG archive key of the groonga repository
+ The groonga repository digitally signs its Release files. This
+ package contains the repository key used for that.
diff --git a/storage/mroonga/vendor/groonga/packages/apt/debian/copyright b/storage/mroonga/vendor/groonga/packages/apt/debian/copyright
new file mode 100644
index 00000000000..bcf01d23f98
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/debian/copyright
@@ -0,0 +1,34 @@
+This package was debianized by Kouhei Sutou <kou@clear-code.com> on
+Sun, 29 Apr 2012 00:00:00 +0000.
+
+It was downloaded from <http://groonga.org/>
+
+Upstream Author(s):
+
+ Daijiro MORI <morita at razil. jp>
+ Tasuku SUENAGA <a at razil. jp>
+ Yutaro Shimamura <yu at razil. jp>
+ Kouhei Sutou <kou at cozmixng. org>
+ Kazuho Oku <kazuhooku at gmail. com>
+ Moriyoshi Koizumi <moriyoshi at gmail. com>
+
+Copyright:
+
+ Copyright(C) 2009-2012 Brazil
+
+License:
+
+ This library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 2.1 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+The Debian packaging is (C) 2012, Kouhei Sutou <kou@clear-code.com> and
+is licensed under the LGPL-2.1, see `/usr/share/common-licenses/LGPL-2.1'.
diff --git a/storage/mroonga/vendor/groonga/packages/apt/debian/groonga-keyring.install b/storage/mroonga/vendor/groonga/packages/apt/debian/groonga-keyring.install
new file mode 100644
index 00000000000..a068a88bf27
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/debian/groonga-keyring.install
@@ -0,0 +1 @@
+usr/share/keyrings/*
diff --git a/storage/mroonga/vendor/groonga/packages/apt/debian/groonga-keyring.postinst b/storage/mroonga/vendor/groonga/packages/apt/debian/groonga-keyring.postinst
new file mode 100755
index 00000000000..bf268a63e62
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/debian/groonga-keyring.postinst
@@ -0,0 +1,22 @@
+#! /bin/sh
+
+set -e
+
+prevver="$2"
+
+case "$1" in
+ configure)
+ if [ -x /usr/bin/apt-key ]; then
+ apt-key add /usr/share/keyrings/groonga-keyring.gpg
+ fi
+ ;;
+ abort-upgrade|abort-deconfigure|abort-remove)
+ :
+ ;;
+ *)
+ echo "Called with unknown argument $1, bailing out."
+ exit 1
+ ;;
+esac
+
+#DEBHELPER#
diff --git a/storage/mroonga/vendor/groonga/packages/apt/debian/groonga-keyring.postrm.in b/storage/mroonga/vendor/groonga/packages/apt/debian/groonga-keyring.postrm.in
new file mode 100644
index 00000000000..b2e8ad6b6e6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/debian/groonga-keyring.postrm.in
@@ -0,0 +1,26 @@
+#! /bin/sh
+
+set -e
+
+case "$1" in
+ remove)
+ if [ -x /usr/bin/apt-key ]; then
+ apt-key del @GPG_UID@
+ fi
+ ;;
+ purge)
+ :
+ ;;
+ upgrade|deconfigure)
+ :
+ ;;
+ failed-upgrade)
+ :
+ ;;
+ *)
+ echo "prerm called with unknown argument \`$1'" >&2
+ exit 1
+ ;;
+esac
+
+#DEBHELPER#
diff --git a/storage/mroonga/vendor/groonga/packages/apt/debian/rules b/storage/mroonga/vendor/groonga/packages/apt/debian/rules
new file mode 100755
index 00000000000..759f2fd5ed8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/debian/rules
@@ -0,0 +1,17 @@
+#!/usr/bin/make -f
+# -*- makefile-gmake -*-
+#
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+# This has to be exported to make some magic below work.
+export DH_OPTIONS
+
+%:
+ dh $@
+
+override_dh_install:
+ install -d debian/tmp/usr/share/keyrings/
+ install -m 0644 groonga-keyring.gpg \
+ debian/tmp/usr/share/keyrings/
+
+ dh_install
diff --git a/storage/mroonga/vendor/groonga/packages/apt/debian/source/format b/storage/mroonga/vendor/groonga/packages/apt/debian/source/format
new file mode 100644
index 00000000000..163aaf8d82b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/storage/mroonga/vendor/groonga/packages/apt/groonga-depended-packages b/storage/mroonga/vendor/groonga/packages/apt/groonga-depended-packages
new file mode 100644
index 00000000000..35d5f1e7cd8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/groonga-depended-packages
@@ -0,0 +1,7 @@
+debhelper
+autotools-dev
+pkg-config
+libmecab-dev
+ruby
+zlib1g-dev
+libpcre3-dev
diff --git a/storage/mroonga/vendor/groonga/packages/apt/groonga-keyring-depended-packages b/storage/mroonga/vendor/groonga/packages/apt/groonga-keyring-depended-packages
new file mode 100644
index 00000000000..a8ef4049b5d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/groonga-keyring-depended-packages
@@ -0,0 +1 @@
+gnupg
diff --git a/storage/mroonga/vendor/groonga/packages/apt/sign-packages.sh b/storage/mroonga/vendor/groonga/packages/apt/sign-packages.sh
new file mode 100755
index 00000000000..6e74f1ceff2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/sign-packages.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 3 ]; then
+ echo "Usage: $0 GPG_UID DESITINATION CODES"
+ echo " e.g.: $0 'F10399C0' repositories/ 'lenny unstable hardy karmic'"
+ exit 1
+fi
+
+GPG_UID=$1
+DESTINATION=$2
+CODES=$3
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+for code_name in ${CODES}; do
+ case ${code_name} in
+ wheezy|jessie|unstable)
+ distribution=debian
+ ;;
+ *)
+ distribution=ubuntu
+ ;;
+ esac
+
+ base_directory=${DESTINATION}${distribution}
+ debsign -pgpg2 --re-sign -k${GPG_UID} \
+ $(find ${base_directory} -name '*.dsc' -or -name '*.changes') &
+ if [ "${PARALLEL}" != "yes" ]; then
+ wait
+ fi
+done
+
+wait
diff --git a/storage/mroonga/vendor/groonga/packages/apt/sign-repository.sh b/storage/mroonga/vendor/groonga/packages/apt/sign-repository.sh
new file mode 100755
index 00000000000..312528805a4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/sign-repository.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 3 ]; then
+ echo "Usage: $0 GPG_UID DESTINATION CODES"
+ echo " e.g.: $0 'F10399C0' repositories/ 'lenny unstable hardy karmic'"
+ exit 1
+fi
+
+GPG_UID=$1
+DESTINATION=$2
+CODES=$3
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+for code_name in ${CODES}; do
+ case ${code_name} in
+ wheezy|jessie|unstable)
+ distribution=debian
+ ;;
+ *)
+ distribution=ubuntu
+ ;;
+ esac
+
+ release=${DESTINATION}${distribution}/dists/${code_name}/Release
+ rm -f ${release}.gpg
+ gpg2 --sign --detach-sign --armor \
+ --local-user ${GPG_UID} \
+ --output ${release}.gpg \
+ ${release} &
+
+ if [ "${PARALLEL}" != "yes" ]; then
+ wait
+ fi
+done
+
+wait
diff --git a/storage/mroonga/vendor/groonga/packages/apt/update-repository.sh b/storage/mroonga/vendor/groonga/packages/apt/update-repository.sh
new file mode 100755
index 00000000000..a87805ae729
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/apt/update-repository.sh
@@ -0,0 +1,130 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 4 ]; then
+ echo "Usage: $0 PROJECT_NAME DESTINATION ARCHITECTURES CODES"
+ echo " e.g.: $0 mroonga repositories/ 'i386 amd64' 'lenny unstable hardy karmic'"
+ exit 1
+fi
+
+PROJECT_NAME=$1
+DESTINATION=$2
+ARCHITECTURES=$3
+CODES=$4
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+update_repository()
+{
+ distribution=$1
+ code_name=$2
+ component=$3
+
+ rm -rf dists/${code_name}
+ mkdir -p dists/${code_name}/${component}/binary-i386/
+ mkdir -p dists/${code_name}/${component}/binary-amd64/
+ mkdir -p dists/${code_name}/${component}/source/
+
+ cat <<EOF > dists/.htaccess
+Options +Indexes
+EOF
+
+ cat <<EOF > dists/${code_name}/${component}/binary-i386/Release
+Archive: ${code_name}
+Component: ${component}
+Origin: The ${PROJECT_NAME} project
+Label: The ${PROJECT_NAME} project
+Architecture: i386
+EOF
+
+ cat <<EOF > dists/${code_name}/${component}/binary-amd64/Release
+Archive: ${code_name}
+Component: ${component}
+Origin: The ${PROJECT_NAME} project
+Label: The ${PROJECT_NAME} project
+Architecture: amd64
+EOF
+
+ cat <<EOF > dists/${code_name}/${component}/source/Release
+Archive: ${code_name}
+Component: ${component}
+Origin: The ${PROJECT_NAME} project
+Label: The ${PROJECT_NAME} project
+Architecture: source
+EOF
+
+ cat <<EOF > generate-${code_name}.conf
+Dir::ArchiveDir ".";
+Dir::CacheDir ".";
+TreeDefault::Directory "pool/${code_name}/${component}";
+TreeDefault::SrcDirectory "pool/${code_name}/${component}";
+Default::Packages::Extensions ".deb";
+Default::Packages::Compress ". gzip bzip2";
+Default::Sources::Compress ". gzip bzip2";
+Default::Contents::Compress "gzip bzip2";
+
+BinDirectory "dists/${code_name}/${component}/binary-i386" {
+ Packages "dists/${code_name}/${component}/binary-i386/Packages";
+ Contents "dists/${code_name}/Contents-i386";
+ SrcPackages "dists/${code_name}/${component}/source/Sources";
+};
+
+BinDirectory "dists/${code_name}/${component}/binary-amd64" {
+ Packages "dists/${code_name}/${component}/binary-amd64/Packages";
+ Contents "dists/${code_name}/Contents-amd64";
+ SrcPackages "dists/${code_name}/${component}/source/Sources";
+};
+
+Tree "dists/${code_name}" {
+ Sections "${component}";
+ Architectures "i386 amd64 source";
+};
+EOF
+ apt-ftparchive generate generate-${code_name}.conf
+ chmod 644 dists/${code_name}/Contents-*
+
+ rm -f dists/${code_name}/Release*
+ rm -f *.db
+ cat <<EOF > release-${code_name}.conf
+APT::FTPArchive::Release::Origin "The ${PROJECT_NAME} project";
+APT::FTPArchive::Release::Label "The ${PROJECT_NAME} project";
+APT::FTPArchive::Release::Architectures "i386 amd64";
+APT::FTPArchive::Release::Codename "${code_name}";
+APT::FTPArchive::Release::Suite "${code_name}";
+APT::FTPArchive::Release::Components "${component}";
+APT::FTPArchive::Release::Description "${PACKAGE_NAME} packages";
+EOF
+ apt-ftparchive -c release-${code_name}.conf \
+ release dists/${code_name} > /tmp/Release
+ mv /tmp/Release dists/${code_name}
+}
+
+for code_name in ${CODES}; do
+ case ${code_name} in
+ wheezy|jessie|unstable)
+ distribution=debian
+ component=main
+ ;;
+ *)
+ distribution=ubuntu
+ component=universe
+ ;;
+ esac
+
+ mkdir -p ${DESTINATION}${distribution}
+ (cd ${DESTINATION}${distribution}
+ update_repository $distribution $code_name $component) &
+ if [ "${PARALLEL}" != "yes" ]; then
+ wait
+ fi
+done
+
+wait
diff --git a/storage/mroonga/vendor/groonga/packages/check-utility.sh b/storage/mroonga/vendor/groonga/packages/check-utility.sh
new file mode 100755
index 00000000000..8c06067f9bd
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/check-utility.sh
@@ -0,0 +1,538 @@
+#!/bin/sh
+
+# Usage: check-utility.sh [--install-groonga]
+# [--check-install]
+# [--check-address]
+# [--enable-repository]
+#
+# CODES="squeeze wheezy unstable lucid natty oneiric precise"
+# DISTRIBUTIONS="centos fedora"
+
+CHROOT_ROOT=/var/lib/chroot
+CHECK_ADDRESS=0
+CHECK_INSTALL=0
+CHECK_INSTALL_PACKAGE=groonga
+CHECK_BUILD=0
+ENABLE_REPOSITORY=0
+DISABLE_REPOSITORY=0
+INSTALL_SCRIPT=0
+INSTALL_GROONGA=0
+UNINSTALL_GROONGA=0
+
+common_deb_procedure ()
+{
+ for code in $CODES; do
+ for arch in $DEB_ARCHITECTURES; do
+ root_dir=$CHROOT_ROOT/$code-$arch
+ eval $1 $code $arch $root_dir
+ done
+ done
+}
+
+common_rpm_procedure ()
+{
+ for dist in $DISTRIBUTIONS; do
+ case $dist in
+ "fedora")
+ DISTRIBUTIONS_VERSION="19"
+ ;;
+ "centos")
+ DISTRIBUTIONS_VERSION="5 6"
+ ;;
+ esac
+ for ver in $DISTRIBUTIONS_VERSION; do
+ for arch in $RPM_ARCHITECTURES; do
+ root_dir=$CHROOT_ROOT/$dist-$ver-$arch
+ eval $1 $dist $arch $ver $root_dir
+ done
+ done
+ done
+}
+
+copy_and_exec_script ()
+{
+ chroot_host=$1
+ root_dir=$2
+ script=$3
+ if [ -d $root_dir ]; then
+ echo "copy script $script to $root_dir/tmp"
+ sudo rm -f $root_dir/tmp/$script
+ cp tmp/$script $root_dir/tmp
+ sudo chmod 755 $root_dir/tmp/$script
+ sudo chname $chroot_host chroot $root_dir /tmp/$script
+ fi
+}
+
+echo_packages_repository_address ()
+{
+ root_dir=$1
+ code=$2
+ arch=$3
+ address=`grep "packages.groonga.org" $root_dir/etc/hosts | grep -v "#"`
+ nameserver=`grep "nameserver" $root_dir/etc/resolv.conf | grep -v "#" | cut -d ' ' -f 2`
+ if [ -z "$address" ]; then
+ echo "$code-$arch: default nameserver:$nameserver"
+ else
+ echo "$code-$arch: $address nameserver:$nameserver"
+ fi
+}
+
+setup_distributions ()
+{
+ if [ -z "$DISTRIBUTIONS" ]; then
+ DISTRIBUTIONS="centos fedora"
+ fi
+}
+
+setup_rpm_architectures ()
+{
+ if [ -z "$RPM_ARCHITECTURES" ]; then
+ RPM_ARCHITECTURES="i386 x86_64"
+ fi
+}
+
+setup_codes ()
+{
+ if [ -z "$CODES" ]; then
+ CODES="squeeze wheezy jessie unstable lucid precise quantal raring"
+ fi
+}
+setup_deb_architectures ()
+{
+ if [ -z "$DEB_ARCHITECTURES" ]; then
+ DEB_ARCHITECTURES="i386 amd64"
+ fi
+}
+
+check_packages_repository_address ()
+{
+ common_deb_procedure "check_packages_deb_repository_address"
+ common_rpm_procedure "check_packages_rpm_repository_address"
+}
+
+check_packages_deb_repository_address ()
+{
+ code=$1
+ arch=$2
+ root_dir=$3
+ if [ -d "$root_dir" ]; then
+ echo_packages_repository_address "$root_dir" "$code" "$arch"
+ fi
+}
+
+check_packages_rpm_repository_address ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ root_dir=$4
+ if [ -d "$root_dir" ]; then
+ echo_packages_repository_address "$root_dir" "$dist-$ver" "$arch"
+ fi
+}
+
+
+host_address ()
+{
+ ifconfig_result=`LANG=C /sbin/ifconfig wlan0`
+ inet_addr=`echo "$ifconfig_result" | grep "inet addr:192"`
+ address=`echo $inet_addr | ruby -ne '/inet addr:(.+?)\s/ =~ $_ && puts($1)'`
+ HOST_ADDRESS=$address
+}
+
+check_build_packages ()
+{
+ common_deb_procedure "check_build_deb_packages"
+ common_rpm_procedure "check_build_rpm_packages"
+}
+
+check_build_deb_packages ()
+{
+ code=$1
+ arch=$2
+ BASE_VERSION=`cat ../base_version`
+ RESULT_SET=`find apt/repositories -name "*$BASE_VERSION*" | grep $code | grep $arch`
+ if [ -z "$RESULT_SET" ]; then
+ printf "%8s %5s %s => 0 deb\n" $code $arch $BASE_VERSION
+ else
+ PACKAGE_COUNT=`find apt/repositories -name "*$BASE_VERSION*" | grep $code | grep $arch | wc | awk '{print \$1}'`
+ printf "%8s %5s %s => %2d debs\n" $code $arch $BASE_VERSION $PACKAGE_COUNT
+ fi
+}
+
+check_build_rpm_packages ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ BASE_VERSION=`cat ../base_version`
+ FIND_PATH=yum/repositories/$dist/$ver/$arch
+ RESULT_SET=`find $FIND_PATH -name "*$BASE_VERSION*"`
+ if [ -z "$RESULT_SET" ]; then
+ printf "%8s %6s %s => 0 rpm\n" $dist$ver $arch $BASE_VERSION
+ else
+ PACKAGE_COUNT=`find $FIND_PATH -name "*$BASE_VERSION*" | wc -l`
+ printf "%8s %6s %s => %2d rpms\n" $dist$ver $arch $BASE_VERSION $PACKAGE_COUNT
+ fi
+}
+
+check_installed_groonga_packages ()
+{
+ common_deb_procedure "check_installed_groonga_deb_packages"
+ common_rpm_procedure "check_installed_groonga_rpm_packages"
+}
+
+check_installed_groonga_deb_packages ()
+{
+ code=$1
+ arch=$2
+ root_dir=$3
+ CHECK_SCRIPT=check-deb-groonga.sh
+ cat > tmp/$CHECK_SCRIPT <<EOF
+#!/bin/sh
+dpkg -l | grep $CHECK_INSTALL_PACKAGE
+EOF
+ copy_and_exec_script $code-$arch $root_dir $CHECK_SCRIPT
+}
+
+
+check_installed_groonga_rpm_packages ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ root_dir=$4
+ CHECK_SCRIPT=check-rpm-groonga.sh
+ cat > tmp/$CHECK_SCRIPT <<EOF
+#!/bin/sh
+rpm -qa | grep $CHECK_INSTALL_PACKAGE
+EOF
+ copy_and_exec_script $code-$ver-$arch $root_dir $CHECK_SCRIPT
+}
+
+install_groonga_packages ()
+{
+ common_deb_procedure "install_groonga_deb_packages"
+ common_rpm_procedure "install_groonga_rpm_packages"
+}
+
+install_groonga_deb_packages ()
+{
+ code=$1
+ arch=$2
+ root_dir=$3
+ cat > tmp/install-aptitude-groonga.sh <<EOF
+#!/bin/sh
+sudo aptitude clean
+rm -f /var/lib/apt/lists/packages.groonga.org_*
+rm -f /var/lib/apt/lists/partial/packages.groonga.org_*
+sudo aptitude update
+sudo aptitude -V -D -y --allow-untrusted install groonga-keyring
+sudo aptitude update
+sudo aptitude -V -D -y install groonga
+sudo aptitude -V -D -y install groonga-tokenizer-mecab
+sudo apt-get -y install libgroonga-dev
+#sudo aptitude -V -D -y install groonga-munin-plugins
+EOF
+ cat > tmp/install-aptget-groonga.sh <<EOF
+#!/bin/sh
+sudo apt-get clean
+rm -f /var/lib/apt/lists/packages.groonga.org_*
+rm -f /var/lib/apt/lists/partial/packages.groonga.org_*
+sudo apt-get update
+sudo apt-get -y --allow-unauthenticated install groonga-keyring
+sudo apt-get update
+sudo apt-get -y install groonga
+sudo apt-get -y install groonga-tokenizer-mecab
+sudo apt-get -y install libgroonga-dev
+#sudo apt-get -y install groonga-munin-plugins
+EOF
+ INSTALL_SCRIPT=""
+ case $code in
+ squeeze|unstable)
+ INSTALL_SCRIPT=install-aptitude-groonga.sh
+ ;;
+ *)
+ INSTALL_SCRIPT=install-aptget-groonga.sh
+ ;;
+ esac
+ copy_and_exec_script $code-$arch $root_dir $INSTALL_SCRIPT
+}
+
+install_groonga_rpm_packages ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ root_dir=$4
+ cat > tmp/install-centos5-groonga.sh <<EOF
+sudo rpm -ivh http://packages.groonga.org/centos/groonga-release-1.1.0-1.noarch.rpm
+sudo yum makecache
+sudo yum install -y groonga
+sudo yum install -y groonga-tokenizer-mecab
+rm -f epel-release-*.rpm
+wget http://download.fedoraproject.org/pub/epel/5/i386/epel-release-5-4.noarch.rpm
+sudo rpm -ivh epel-release-5-4.noarch.rpm
+sudo yum install -y groonga-devel
+#sudo yum install -y groonga-munin-plugins
+EOF
+ cat > tmp/install-centos6-groonga.sh <<EOF
+sudo rpm -ivh http://packages.groonga.org/centos/groonga-release-1.1.0-1.noarch.rpm
+sudo yum makecache
+sudo yum install -y groonga
+sudo yum install -y groonga-tokenizer-mecab
+sudo rpm -ivh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
+sudo yum install -y groonga-devel
+#sudo yum install -y groonga-munin-plugins
+EOF
+ cat > tmp/install-fedora-groonga.sh <<EOF
+sudo rpm -ivh http://packages.groonga.org/fedora/groonga-release-1.1.0-1.noarch.rpm
+sudo yum makecache
+sudo yum install -y groonga
+sudo yum install -y groonga-tokenizer-mecab
+sudo yum install -y groonga-devel
+#sudo yum install -y groonga-munin-plugins
+EOF
+ case "$dist-$ver" in
+ centos-5)
+ INSTALL_SCRIPT=install-centos5-groonga.sh
+ ;;
+ centos-6)
+ INSTALL_SCRIPT=install-centos6-groonga.sh
+ ;;
+ fedora-19)
+ INSTALL_SCRIPT=install-fedora-groonga.sh
+ ;;
+ *)
+ ;;
+ esac
+ copy_and_exec_script $dist-$ver-$arch $root_dir $INSTALL_SCRIPT
+}
+
+uninstall_groonga_packages ()
+{
+ UNINSTALL_SCRIPT=uninstall-deb-groonga.sh
+ cat > $UNINSTALL_SCRIPT <<EOF
+#!/bin/sh
+sudo apt-get purge -y groonga-* mysql-*
+EOF
+ for code in $CODES; do
+ for arch in $DEB_ARCHITECTURES; do
+ root_dir=$CHROOT_ROOT/$code-$arch
+ if [ -d $root_dir ]; then
+ echo "copy uninstall script $UNINSTALL_SCRIPT to $root_dir/tmp"
+ sudo rm -f $root_dir/tmp/$UNINSTALL_SCRIPT
+ cp $UNINSTALL_SCRIPT $root_dir/tmp
+ chmod 755 $root_dir/tmp/$UNINSTALL_SCRIPT
+ sudo chname $code-$arch chroot $root_dir /tmp/$UNINSTALL_SCRIPT
+ fi
+ done
+ done
+ UNINSTALL_SCRIPT=uninstall-rpm-groonga.sh
+ cat > $UNINSTALL_SCRIPT <<EOF
+#!/bin/sh
+sudo yum remove -y groonga-* mysql-*
+EOF
+ for dist in $DISTRIBUTIONS; do
+ case $dist in
+ "fedora")
+ DISTRIBUTIONS_VERSION="19"
+ ;;
+ "centos")
+ DISTRIBUTIONS_VERSION="5 6"
+ ;;
+ esac
+ for ver in $DISTRIBUTIONS_VERSION; do
+ for arch in $RPM_ARCHITECTURES; do
+ root_dir=$CHROOT_ROOT/$dist-$ver-$arch
+ if [ -d $root_dir ]; then
+ echo "copy install script $UNINSTALL_SCRIPT to $root_dir/tmp"
+ sudo rm -f $root_dir/tmp/$UNINSTALL_SCRIPT
+ cp $UNINSTALL_SCRIPT $root_dir/tmp
+ chmod 755 $root_dir/tmp/$UNINSTALL_SCRIPT
+ sudo chname $code-$ver-$arch chroot $root_dir /tmp/$UNINSTALL_SCRIPT
+ fi
+ done
+ done
+ done
+}
+
+
+
+enable_temporary_groonga_repository ()
+{
+ SCRIPT=enable-repository.sh
+ cat > tmp/$SCRIPT <<EOF
+#!/bin/sh
+
+grep -v "packages.groonga.org" /etc/hosts > /tmp/hosts
+echo "$HOST_ADDRESS packages.groonga.org" >> /tmp/hosts
+cp -f /tmp/hosts /etc/hosts
+EOF
+ common_deb_procedure "enable_temporary_groonga_deb_repository"
+ common_rpm_procedure "enable_temporary_groonga_rpm_repository"
+ check_packages_repository_address
+}
+
+enable_temporary_groonga_deb_repository ()
+{
+ code=$1
+ arch=$2
+ root_dir=$3
+ SCRIPT=enable-repository.sh
+ today=`date '+%Y%m%d.%s'`
+ copy_and_exec_script $code-$arch $root_dir $SCRIPT
+}
+
+enable_temporary_groonga_rpm_repository ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ root_dir=$4
+ SCRIPT=enable-repository.sh
+ today=`date '+%Y%m%d.%s'`
+ copy_and_exec_script $dist-$ver-$arch $root_dir $SCRIPT
+}
+
+disable_temporary_groonga_repository ()
+{
+ SCRIPT=disable-repository.sh
+ cat > tmp/$SCRIPT <<EOF
+#!/bin/sh
+
+grep -v "packages.groonga.org" /etc/hosts > /tmp/hosts
+cp -f /tmp/hosts /etc/hosts
+EOF
+ common_deb_procedure "disable_temporary_groonga_deb_repository"
+ common_rpm_procedure "disable_temporary_groonga_rpm_repository"
+ check_packages_repository_address
+}
+
+disable_temporary_groonga_deb_repository ()
+{
+ code=$1
+ arch=$2
+ root_dir=$3
+ copy_and_exec_script $code-$arch $root_dir $SCRIPT
+}
+
+disable_temporary_groonga_rpm_repository ()
+{
+ dist=$1
+ arch=$2
+ ver=$3
+ root_dir=$4
+ copy_and_exec_script $code-$arch $root_dir $SCRIPT
+}
+
+host_address
+echo $HOST_ADDRESS
+
+while [ $# -ne 0 ]; do
+ case $1 in
+ --check-install)
+ CHECK_INSTALL=1
+ shift
+ if [ ! -z "$1" ]; then
+ case $1 in
+ groonga|mroonga|roonga|mecab)
+ CHECK_INSTALL_PACKAGE=$1
+ ;;
+ *)
+ ;;
+ esac
+ fi
+ ;;
+ --check-address)
+ CHECK_ADDRESS=1
+ shift
+ ;;
+ --check-build)
+ CHECK_BUILD=1
+ shift
+ ;;
+ --enable-repository)
+ ENABLE_REPOSITORY=1
+ shift
+ ;;
+ --disable-repository)
+ DISABLE_REPOSITORY=1
+ shift
+ ;;
+ --install-groonga)
+ INSTALL_GROONGA=1
+ shift
+ ;;
+ --uninstall-groonga)
+ UNINSTALL_GROONGA=1
+ shift
+ ;;
+ --code)
+ shift
+ if [ "$1" = "all" ]; then
+ setup_codes
+ else
+ CODES=$1
+ fi
+ shift
+ ;;
+ --code-arch)
+ shift
+ if [ "$1" = "all" ]; then
+ setup_deb_architectures
+ else
+ DEB_ARCHITECTURES=$1
+ fi
+ shift
+ ;;
+ --dist)
+ shift
+ if [ "$1" = "all" ]; then
+ setup_distributions
+ else
+ DISTRIBUTIONS=$1
+ fi
+ shift
+ ;;
+ --dist-arch)
+ shift
+ if [ "$1" = "all" ]; then
+ setup_rpm_architectures
+ else
+ RPM_ARCHITECTURES=$1
+ fi
+ shift
+ ;;
+ *)
+ shift
+ ;;
+ esac
+done
+
+mkdir -p tmp
+setup_deb_architectures
+setup_rpm_architectures
+
+if [ $CHECK_INSTALL -ne 0 ]; then
+ check_installed_groonga_packages
+fi
+if [ $CHECK_ADDRESS -ne 0 ]; then
+ check_packages_repository_address
+fi
+if [ $CHECK_BUILD -ne 0 ]; then
+ check_build_packages
+fi
+if [ $ENABLE_REPOSITORY -ne 0 ]; then
+ enable_temporary_groonga_repository
+fi
+if [ $DISABLE_REPOSITORY -ne 0 ]; then
+ disable_temporary_groonga_repository
+fi
+if [ $INSTALL_GROONGA -ne 0 ]; then
+ install_groonga_packages
+fi
+if [ $UNINSTALL_GROONGA -ne 0 ]; then
+ uninstall_groonga_packages
+fi
+
diff --git a/storage/mroonga/vendor/groonga/packages/debian/changelog b/storage/mroonga/vendor/groonga/packages/debian/changelog
new file mode 100644
index 00000000000..c4eccba77a4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/changelog
@@ -0,0 +1,414 @@
+groonga (4.0.5-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Fri, 29 Aug 2014 00:00:00 +0900
+
+groonga (4.0.4-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Tue, 29 Jul 2014 00:00:00 +0900
+
+groonga (4.0.3-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Sun, 29 Jun 2014 00:00:00 +0900
+
+groonga (4.0.2-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 29 May 2014 00:00:00 +0900
+
+groonga (4.0.1-2) unstable; urgency=low
+
+ * debian/control
+ - Add a missing pkg-config dependency to libgroonga-dev.
+ - Depend on mecab-naist-jdic instead of mecab-ipadic-utf8 because
+ mecab-naist-jdic is a replacement of mecab-ipadic-utf8.
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 29 May 2014 00:00:00 +0900
+
+groonga (4.0.1-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sat, 29 Mar 2014 00:00:00 +0900
+
+groonga (4.0.0-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sun, 09 Feb 2014 00:00:00 +0900
+
+groonga (3.1.2-1) unstable; urgency=low
+
+ * New upstream release.
+ * debian/control
+ - Add proper description to fix "duplicate-long-description" lintian warning
+ for groonga-server-gqtp and groonga-server-http packages.
+ - Remove needless period to fix I:description-synopsis-might-not-be-phrased-properly lintian warning.
+ - Use "all" in Architecture field to fix I: package-contains-no-arch-dependent-files
+ lintian warning for groonga-example package.
+ * debian/rules
+ - Add DEB_BUILD_HARDENING flag to fix lintian warning "hardening-no-relro" and
+ "hardening-no-fortify-functions"
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Wed, 29 Jan 2014 00:00:00 +0900
+
+groonga (3.1.1-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sun, 29 Dec 2013 00:00:00 +0900
+
+groonga (3.1.0-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Fri, 29 Nov 2013 00:00:00 +0900
+
+groonga (3.0.9-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Tue, 29 Oct 2013 00:00:00 +0900
+
+groonga (3.0.8-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sun, 29 Sep 2013 00:00:00 +0900
+
+groonga (3.0.7-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Thu, 29 Aug 2013 00:00:00 +0900
+
+groonga (3.0.6-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Mon, 29 Jul 2013 00:00:00 +0900
+
+groonga (3.0.5-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sat, 29 Jun 2013 00:00:00 +0900
+
+groonga (3.0.4-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Wed, 29 May 2013 00:00:00 +0900
+
+groonga (3.0.3-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Mon, 29 Apr 2013 00:00:00 +0900
+
+groonga (3.0.2-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Fri, 29 Mar 2013 00:00:00 +0900
+
+groonga (3.0.1-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Thu, 28 Feb 2013 00:00:00 +0900
+
+groonga (3.0.0-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sat, 09 Feb 2013 00:00:00 +0900
+
+groonga (2.1.2-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Tue, 29 Jan 2013 00:00:00 +0900
+
+groonga (2.1.1-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Sat, 29 Dec 2012 15:04:41 +0900
+
+groonga (2.1.0-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sat, 29 Dec 2012 00:00:00 +0900
+
+groonga (2.0.9-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Thu, 29 Nov 2012 00:00:00 +0900
+
+groonga (2.0.8-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Mon, 29 Oct 2012 00:00:00 +0900
+
+groonga (2.0.7-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sat, 29 Sep 2012 00:00:00 +0900
+
+groonga (2.0.6-1) unstable; urgency=low
+
+ * New upstream release.
+ * Split common tasks for server use into groonga-server-common package.
+ * groonga-server and groonga-httpd require groonga-server-common package.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Wed, 29 Aug 2012 00:00:00 +0900
+
+groonga (2.0.5-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Sun, 29 Jul 2012 00:00:00 +0900
+
+groonga (2.0.4-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Fri, 29 Jun 2012 00:00:00 +0900
+
+groonga (2.0.3-1) unstable; urgency=low
+
+ * New upstream release.
+ * Fix a bug that log_reopen command in logrotate uses wrong protocol.
+
+ -- Kouhei Sutou <kou@clear-code.com> Tue, 29 May 2012 00:00:00 +0900
+
+groonga (2.0.2-1) unstable; urgency=low
+
+ * New upstream release.
+ * Set missing default values.
+
+ -- Kouhei Sutou <kou@clear-code.com> Sun, 29 Apr 2012 00:00:00 +0900
+
+groonga (2.0.1-3) unstable; urgency=low
+
+ * Fix default file path: /etc/default/groonga -> /etc/default/groonga-server
+ * Use shutdown command for stop.
+
+ -- Kouhei Sutou <kou@clear-code.com> Fri, 30 Mar 2012 00:00:00 +0900
+
+groonga (2.0.1-2) unstable; urgency=low
+
+ * Fix bind address argument parameter.
+ Patch by Masaharu IWAI. Thanks!!!
+
+ -- Kouhei Sutou <kou@clear-code.com> Fri, 30 Mar 2012 00:00:00 +0900
+
+groonga (2.0.1-1) unstable; urgency=low
+
+ * New upstream release.
+ * grntest -> groonga-benchmark.
+ * Remove groogna-tools package.
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 29 Mar 2012 00:00:00 +0900
+
+groonga (2.0.0-1) unstable; urgency=low
+
+ * New upstream release.
+ * Use HTTP as the default protocol.
+
+ -- Kouhei Sutou <kou@clear-code.com> Wed, 29 Feb 2012 00:00:00 +0900
+
+groonga (1.3.0-1) unstable; urgency=low
+
+ * New upstream release.
+ * groonga-server doesn't require groonga-munin-plugins.
+ Suggested by Masaharu IWAI. Thanks!!!
+
+ -- Kouhei Sutou <kou@clear-code.com> Sun, 29 Jan 2012 00:00:00 +0900
+
+groonga (1.2.9-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 29 Dec 2011 00:00:00 +0900
+
+groonga (1.2.8-1) unstable; urgency=low
+
+ * New upstream release.
+ * Enable zlib support.
+ * Enable lzo support.
+
+ -- Kouhei Sutou <kou@clear-code.com> Tue, 29 Nov 2011 00:00:00 +0900
+
+groonga (1.2.7-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Sat, 29 Oct 2011 00:00:00 +0900
+
+groonga (1.2.6-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 29 Sep 2011 00:00:00 +0900
+
+groonga (1.2.5-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Mon, 29 Aug 2011 00:00:00 +0900
+
+groonga (1.2.4-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Fri, 29 Jul 2011 00:00:00 +0900
+
+groonga (1.2.3-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Wed, 29 Jun 2011 00:00:00 +0900
+
+groonga (1.2.2-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Sun, 29 May 2011 00:00:00 +0900
+
+groonga (1.2.1-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Fri, 29 Apr 2011 00:00:00 +0900
+
+groonga (1.2.0-3) unstable; urgency=low
+
+ * libgronga-dev: add missing libgroonga.so.
+
+ -- Kouhei Sutou <kou@clear-code.com> Wed, 06 Apr 2011 21:00:00 +0900
+
+groonga (1.2.0-2) unstable; urgency=low
+
+ * libgronga-dev: add missing libgroonga.so.
+
+ -- Kouhei Sutou <kou@clear-code.com> Tue, 29 Mar 2011 21:00:00 +0900
+
+groonga (1.2.0-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Tue, 29 Mar 2011 00:00:00 +0900
+
+groonga (1.1.0-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Wed, 09 Feb 2011 00:00:00 +0900
+
+groonga (1.0.8-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Wed, 02 Feb 2011 00:00:00 +0900
+
+groonga (1.0.7-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Sat, 29 Jan 2011 00:00:00 +0900
+
+groonga (1.0.6-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Fri, 31 Dec 2010 00:00:00 +0900
+
+groonga (1.0.5-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Wed, 29 Dec 2010 00:00:00 +0900
+
+groonga (1.0.4-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Mon, 29 Nov 2010 00:00:00 +0900
+
+groonga (1.0.3-1) lucid; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Fri, 29 Oct 2010 16:00:22 +0900
+
+groonga (1.0.2-1) lucid; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 09 Sep 2010 15:19:24 +0900
+
+groonga (1.0.1-1) lucid; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Mon, 06 Sep 2010 16:21:35 +0900
+
+groonga (1.0.0-1) lucid; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Sun, 29 Aug 2010 00:07:53 +0900
+
+groonga (0.7.7-1) lucid; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Wed, 25 Aug 2010 17:05:58 +0900
+
+groonga (0.7.6-2) unstable; urgency=low
+
+ * Split packages.
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 25 Aug 2010 22:11:14 +0900
+
+groonga (0.7.6) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 19 Aug 2010 22:11:14 +0900
+
+groonga (0.7.4) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Thu, 29 Jul 2010 14:24:00 +0900
+
+groonga (0.1.9) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Tue, 20 Apr 2010 15:10:04 +0900
+
+groonga (0.1.6) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Wed, 10 Feb 2010 13:00:55 +0900
+
+groonga (0.1.5) unstable; urgency=low
+
+ * Initial release.
+
+ -- Kouhei Sutou <kou@clear-code.com> Fri, 15 Jan 2010 16:41:04 +0900
diff --git a/storage/mroonga/vendor/groonga/packages/debian/compat b/storage/mroonga/vendor/groonga/packages/debian/compat
new file mode 100644
index 00000000000..ec635144f60
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/storage/mroonga/vendor/groonga/packages/debian/control b/storage/mroonga/vendor/groonga/packages/debian/control
new file mode 100644
index 00000000000..1a1ba8c96cb
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/control
@@ -0,0 +1,210 @@
+Source: groonga
+Section: database
+Priority: optional
+Maintainer: Groonga Project <packages@groonga.org>
+Uploaders: HAYASHI Kentaro <hayashi@clear-code.com>
+Build-Depends:
+ debhelper (>= 9),
+ autotools-dev,
+ zlib1g-dev,
+ liblzo2-dev,
+ libmsgpack-dev,
+ libzmq3-dev | libzmq-dev,
+ libevent-dev,
+ libmecab-dev,
+ libpcre3-dev
+Standards-Version: 3.9.5
+Homepage: http://groonga.org/
+
+Package: groonga
+Architecture: any
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ groonga-bin (= ${binary:Version}),
+ groonga-plugin-suggest (= ${binary:Version}),
+ groonga-doc (= ${source:Version})
+Description: Fulltext search engine (meta-package for library use)
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package depends all Groonga related package for library use.
+
+Package: groonga-server-common
+Architecture: any
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ adduser,
+ groonga (= ${binary:Version})
+Description: Fulltext search engine (meta-package for server use)
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package provides common settings for server use.
+
+Package: groonga-server-http
+Architecture: any
+Replaces: groonga-server (<< 2.0.7-1)
+Breaks: groonga-server (<< 2.0.7-1)
+Conflicts: groonga-httpd
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ adduser,
+ curl,
+ groonga-server-common (= ${binary:Version})
+Description: Fulltext search engine (meta-package for HTTP server use)
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package depends all Groonga related package for HTTP server use.
+
+Package: groonga-server-gqtp
+Architecture: any
+Replaces: groonga-server (<< 2.0.7-1)
+Breaks: groonga-server (<< 2.0.7-1)
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ adduser,
+ groonga-server-common (= ${binary:Version})
+Description: Fulltext search engine (meta-package for GQTP server use)
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package depends all Groonga related package for GQTP server use.
+
+Package: libgroonga-dev
+Section: libdevel
+Architecture: any
+Multi-Arch: same
+Replaces: libgroonga (<< 1.2.0-1)
+Breaks: libgroonga (<< 1.2.0-1)
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ pkg-config,
+ libgroonga0 (= ${binary:Version})
+Description: Development files to use Groonga as a library
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package provides header files to use Groonga as a library.
+
+Package: libgroonga0
+Section: libs
+Architecture: any
+Multi-Arch: same
+Replaces: libgroonga (<< 1.2.0-1)
+Breaks: libgroonga (<< 1.2.0-1)
+Pre-Depends: ${misc:Pre-Depends}
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends}
+Description: Library files for Groonga
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package provides library files.
+
+Package: groonga-tokenizer-mecab
+Section: libs
+Architecture: any
+Replaces: libgroonga-tokenizer-mecab (<< 1.2.0-1)
+Breaks: libgroonga-tokenizer-mecab (<< 1.2.0-1)
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ libgroonga0 (= ${binary:Version}),
+ mecab-naist-jdic | mecab-jumandic-utf8
+Description: MeCab tokenizer for Groonga
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package provides MeCab tokenizer.
+
+Package: groonga-plugin-suggest
+Section: libs
+Architecture: any
+Replaces: libgroonga-plugin-suggest (<< 1.2.0-1)
+Breaks: libgroonga-plugin-suggest (<< 1.2.0-1)
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ libgroonga0 (= ${binary:Version})
+Description: Suggest plugin for Groonga
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package provides suggest plugin.
+
+Package: groonga-bin
+Architecture: any
+Replaces: groonga (<< 1.2.0-1)
+Breaks: groonga (<< 1.2.0-1)
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ libgroonga0 (= ${binary:Version})
+Description: Commands for Groonga
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package provides 'groonga' command.
+
+Package: groonga-httpd
+Architecture: any
+Replaces: groonga (<< 1.2.0-1)
+Breaks: groonga (<< 1.2.0-1)
+Conflicts: groonga-server-http
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ curl,
+ groonga-server-common (= ${binary:Version})
+Description: Groonga HTTP server
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package provides 'groonga-httpd' command.
+
+Package: groonga-doc
+Section: doc
+Architecture: all
+Depends:
+ ${misc:Depends},
+ libjs-underscore,
+ libjs-jquery
+Description: Documentation of Groonga
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package provides documentation of Groonga.
+
+Package: groonga-examples
+Architecture: all
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ groonga-bin (>= ${source:Version}),
+ ruby
+Description: Examples of Groonga
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package provides examples of Groonga.
+
+Package: groonga-munin-plugins
+Section: net
+Architecture: any
+Depends:
+ ${misc:Depends},
+ groonga-bin (= ${binary:Version}),
+ munin-node,
+ ruby
+Description: munin-node plugins for Groonga
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package provides munin-node plugins to monitor Groonga.
diff --git a/storage/mroonga/vendor/groonga/packages/debian/copyright b/storage/mroonga/vendor/groonga/packages/debian/copyright
new file mode 100644
index 00000000000..e13db6c1670
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/copyright
@@ -0,0 +1,109 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: Groonga Project
+Upstream-Contact: Groonga Project <packages@groonga.org>
+Source: http://packages.groonga.org/source/groonga/
+
+Files: *
+Copyright: Copyright 2009-2013 Brazil, Inc.
+License: LGPL-2.1
+
+Files: debian/*
+Copyright: Copyright 2011 Kouhei Sutou <kou@clear-code.com>
+License: LGPL-2.1
+
+Files: data/html/admin/css/redmond/*
+Copyright: Copyright 2011, Brandon Aaron
+ Paul Bakaus (paulbakaus.com)
+ David Bolter
+ Rich Caloggero
+ Chi Cheng (cloudream@gmail.com)
+ Colin Clark (http://colin.atrc.utoronto.ca/)
+ Michelle D'Souza
+ Aaron Eisenberger (aaronchi@gmail.com)
+ Ariel Flesler
+ Bohdan Ganicky
+ Scott González
+ Marc Grabanski (m@marcgrabanski.com)
+ Klaus Hartl (stilbuero.de)
+ Scott Jehl
+ Cody Lindley
+ Eduardo Lundgren (eduardolundgren@gmail.com)
+ Todd Parker
+ John Resig
+ Patty Toland
+ Ca-Phun Ung (yelotofu.com)
+ Keith Wood (kbwood@virginbroadband.com.au)
+ Maggie Costello Wachs
+ Richard D. Worth (rdworth.org)
+ Jörn Zaefferer (bassistance.de)
+License: MIT or GPL-2
+
+Files: data/html/admin/js/jquery.flot*
+Copyright: Copyright (c) 2007-2009 IOLA and Ole Laursen
+License: MIT
+
+Files: data/html/admin/js/jquery-1.7.2.min.js
+Copyright: Copyright 2011, John Resig
+License: MIT or GPL-2
+
+Files: doc/locale/*/jquery.js
+Copyright: Copyright 2012 jQuery Foundation
+License: MIT
+
+Files: doc/locale/*/underscore.js
+Copyright: Copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
+License: MIT
+
+License: LGPL-2.1
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+ .
+ You should have received a copy of the GNU Lesser General Public
+ License along with this package; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ .
+ On Debian systems, the complete text of the GNU Lesser General
+ Public License can be found in `/usr/share/common-licenses/LGPL-2.1'.
+
+License: MIT
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+ .
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+
+License: GPL-2
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2.
+ .
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-bin.install b/storage/mroonga/vendor/groonga/packages/debian/groonga-bin.install
new file mode 100644
index 00000000000..c3e8e2839c3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-bin.install
@@ -0,0 +1,6 @@
+usr/bin/groonga
+usr/bin/groonga-benchmark
+usr/share/groonga/html/*
+usr/share/man/man1/*
+usr/share/man/*/man1/*
+etc/groonga/groonga.conf
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-doc.install b/storage/mroonga/vendor/groonga/packages/debian/groonga-doc.install
new file mode 100644
index 00000000000..b4d750909f1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-doc.install
@@ -0,0 +1 @@
+usr/share/doc/groonga-doc/
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-doc.links b/storage/mroonga/vendor/groonga/packages/debian/groonga-doc.links
new file mode 100644
index 00000000000..e93c5423d67
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-doc.links
@@ -0,0 +1,4 @@
+usr/share/javascript/jquery/jquery.js usr/share/doc/groonga-doc/en/html/_static/jquery.js
+usr/share/javascript/jquery/jquery.js usr/share/doc/groonga-doc/ja/html/_static/jquery.js
+usr/share/javascript/underscore/underscore.js usr/share/doc/groonga-doc/ja/html/_static/underscore.js
+usr/share/javascript/underscore/underscore.js usr/share/doc/groonga-doc/en/html/_static/underscore.js
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-examples.install b/storage/mroonga/vendor/groonga/packages/debian/groonga-examples.install
new file mode 100644
index 00000000000..a107e52d2b1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-examples.install
@@ -0,0 +1 @@
+usr/share/groonga/examples/*
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.default b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.default
new file mode 100644
index 00000000000..b95fd7ee174
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.default
@@ -0,0 +1,15 @@
+# Default
+#USER=groonga
+#GROUP=groonga
+#ADDRESS=127.0.0.1
+#PORT=10041
+#DATABASE=/var/lib/groonga/db/db
+#LOG_PATH=/var/log/groonga/groonga.log
+#QUERY_LOG_PATH=/var/log/groonga/query.log
+#PROTOCOL=http
+#GRN_QUERY_EXPANDER_TSV_SYNONYMS_FILE=/usr/share/groonga/synonyms.tsv
+TIMEOUT=3
+# Comment out this to disable groonga-httpd daemon.
+ENABLE=yes
+# Set "no" to disable to skip updating column for same value.
+GRN_JA_SKIP_SAME_VALUE_PUT=yes
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.dirs b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.dirs
new file mode 100644
index 00000000000..32c730751f2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.dirs
@@ -0,0 +1,3 @@
+var/log/groonga/httpd
+etc/groonga/httpd
+etc/groonga/httpd/logs
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.init b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.init
new file mode 100755
index 00000000000..ad8eb269afe
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.init
@@ -0,0 +1,193 @@
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides: groonga-httpd
+# Required-Start: $remote_fs $network
+# Required-Stop: $remote_fs $network
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: groonga-httpd's init script
+# Description: groonga-httpd is a HTTP server for groonga.
+### END INIT INFO
+
+# Author: Kouhei Sutou <kou@clear-code.com>
+
+# Do NOT "set -e"
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC="a HTTP server for groonga which provides full-text search engine and column store"
+NAME=groonga-httpd
+DAEMON=/usr/sbin/$NAME
+DAEMON_RESTART=/usr/sbin/$NAME-restart
+CURL=/usr/bin/curl
+USER=groonga
+GROUP=groonga
+ADDRESS=127.0.0.1
+PORT=10041
+PIDFILE=/var/run/groonga/$NAME.pid
+SCRIPTNAME=/etc/init.d/$NAME
+OPTION_ARGS=""
+START_STOP_DAEMON_ARGS=""
+ENABLE="no"
+TIMEOUT=5
+
+DEFAULT_FILE=/etc/default/groonga-httpd
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+# Read configuration variable file if it is present
+[ -r $DEFAULT_FILE ] && . $DEFAULT_FILE
+
+[ "$ENABLE" = "yes" ] || exit 0
+
+export GRN_JA_SKIP_SAME_VALUE_PUT="$GRN_JA_SKIP_SAME_VALUE_PUT"
+
+mkdir -p $(dirname ${PIDFILE})
+
+if [ -n "${USER}" ]; then
+ if ! getent passwd | grep -q "^${USER}:"; then
+ echo "$0: user for running groonga-httpd doesn't exist: ${USER}" >&2
+ exit 1
+ fi
+fi
+if [ -n "${GROUP}" ]; then
+ if ! getent group | grep -q "^${GROUP}:"; then
+ echo "$0: group for running groonga-httpd doesn't exist: ${GROUP}" >&2
+ exit 1
+ fi
+fi
+DAEMON_ARGS="${DAEMON_ARGS} ${OPTION_ARGS}"
+
+
+# Load the VERBOSE setting and other rcS variables
+. /lib/init/vars.sh
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
+. /lib/lsb/init-functions
+
+send_command()
+{
+ command=$1
+ if [ "$command" = "status" ]; then
+ $CURL --max-time $TIMEOUT "http://${ADDRESS}:${PORT}/d/${command}"
+ else
+ $CURL "http://${ADDRESS}:${PORT}/d/${command}"
+ fi
+}
+
+#
+# Function that starts the daemon/service
+#
+do_start()
+{
+ # Return
+ # 0 if daemon has been started
+ # 1 if daemon was already running
+ # 2 if daemon could not be started
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
+ ${START_STOP_DAEMON_ARGS} --test > /dev/null || return 1
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
+ ${START_STOP_DAEMON_ARGS} -- $DAEMON_ARGS || return 2
+ # Add code here, if necessary, that waits for the process to be ready
+ # to handle requests from services started subsequently which depend
+ # on this one. As a last resort, sleep for some time.
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+ # Return
+ # 0 if daemon has been stopped
+ # 1 if daemon was already stopped
+ # 2 if daemon could not be stopped
+ # other if a failure occurred
+
+ $DAEMON -s stop
+
+ start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 \
+ --pidfile $PIDFILE --name $NAME
+ RETVAL="$?"
+ [ "$RETVAL" = 2 ] && return 2
+ # Wait for children to finish too if this is a daemon that forks
+ # and if the daemon is only ever run from this initscript.
+ # If the above conditions are not satisfied then add some other code
+ # that waits for the process to drop all resources that could be
+ # needed by services started subsequently. A last resort is to
+ # sleep for some time.
+ start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
+ [ "$?" = 2 ] && return 2
+ # Many daemons don't delete their pidfiles when they exit.
+ rm -f $PIDFILE
+ return "$RETVAL"
+}
+
+#
+# Function that sends a SIGHUP to the daemon/service
+#
+do_reload() {
+ #
+ # If the daemon can reload its configuration without
+ # restarting (for example, when it is sent a SIGHUP),
+ # then implement that here.
+ #
+ start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
+ return 0
+}
+
+do_status() {
+ send_command status
+ return $?
+}
+
+case "$1" in
+ start)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
+ do_start
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ stop)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ reload|force-reload)
+ log_daemon_msg "Reloading $DESC" "$NAME"
+ do_reload
+ log_end_msg $?
+ ;;
+ restart)
+ log_daemon_msg "Restarting $DESC" "$NAME"
+ if [ -f ${PIDFILE} ]; then
+ $DAEMON_RESTART
+ else
+ do_start
+ fi
+ case "$?" in
+ 0) log_end_msg 0 ;;
+ *) log_end_msg 1 ;;
+ esac
+ ;;
+ status)
+ do_status
+ case "$?" in
+ 0) ;; # service is OK
+ *) exit 3 ;; # program is not running
+ esac
+ ;;
+ *)
+ echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload|status}" >&2
+ exit 3
+ ;;
+esac
+
+:
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.install b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.install
new file mode 100644
index 00000000000..7aea2953021
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.install
@@ -0,0 +1,3 @@
+usr/sbin/groonga-httpd
+usr/sbin/groonga-httpd-restart
+etc/groonga/httpd/*
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.logrotate b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.logrotate
new file mode 100644
index 00000000000..e1ed8532700
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.logrotate
@@ -0,0 +1,17 @@
+/var/log/groonga/httpd/*.log {
+ daily
+ missingok
+ rotate 30
+ compress
+ delaycompress
+ notifempty
+ create 640 groonga groonga
+ sharedscripts
+ postrotate
+ . /etc/default/groonga-httpd
+ if x"$ENABLE" = x"yes"; then
+ /usr/bin/curl --silent --output /dev/null \
+ "http://127.0.0.1:10041/d/log_reopen"
+ fi
+ endscript
+}
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.postinst b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.postinst
new file mode 100755
index 00000000000..e7a663b4275
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.postinst
@@ -0,0 +1,26 @@
+#! /bin/sh
+
+set -e
+
+fixperms() {
+ for target in /etc/groonga/httpd /etc/groonga/httpd/logs /var/log/groonga/httpd
+ do
+ dpkg-statoverride --list $target >/dev/null || \
+ dpkg-statoverride --update --add groonga groonga 0755 $target
+ done
+}
+
+case "$1" in
+ configure)
+ fixperms
+ ;;
+ abort-upgrade|abort-deconfigure|abort-remove)
+ :
+ ;;
+ *)
+ echo "Called with unknown argument $1, bailing out."
+ exit 1
+ ;;
+esac
+
+#DEBHELPER#
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.postrm b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.postrm
new file mode 100755
index 00000000000..816a1e6f7da
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-httpd.postrm
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+set -e
+
+if [ "$1" = "purge" ]; then
+ for target in /var/log/groonga/httpd /etc/groonga/httpd/logs /etc/groonga/httpd
+ do
+ dpkg-statoverride --remove $target; rm -rf $target
+ done
+fi
+
+#DEBHELPER#
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.conf b/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.conf
new file mode 100644
index 00000000000..4bb7a2aa68f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.conf
@@ -0,0 +1,24 @@
+[groonga_*]
+ user groonga
+ group groonga
+ env.PATH /usr/bin:/bin:/usr/local/bin
+ env.database_path /var/lib/groonga/db/db
+ env.host 127.0.0.1
+
+ env.http_host 127.0.0.1
+ env.http_port 10041
+ env.http_database_path /var/lib/groonga/db/db
+ env.http_pid_path /var/run/groonga/groonga-http.pid
+ env.http_query_log_path /var/log/groonga/query-http.log
+
+ env.httpd_host 127.0.0.1
+ env.httpd_port 10041
+ env.httpd_database_path /var/lib/groonga/db/db
+ env.httpd_pid_path /var/run/groonga/groonga-httpd.pid
+ env.httpd_query_log_path /var/log/groonga/httpd/groonga-query.log
+
+ env.gqtp_host 127.0.0.1
+ env.gqtp_port 10043
+ env.gqtp_database_path /var/lib/groonga/db/db
+ env.gqtp_pid_path /var/run/groonga/groonga-gqtp.pid
+ env.gqtp_query_log_path /var/log/groonga/query-gqtp.log
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.install b/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.install
new file mode 100644
index 00000000000..ef12a7ab323
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.install
@@ -0,0 +1,2 @@
+etc/munin/plugin-conf.d/*
+usr/share/groonga/munin/plugins/*
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.links b/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.links
new file mode 100644
index 00000000000..26248a4de70
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.links
@@ -0,0 +1,8 @@
+usr/share/groonga/munin/plugins/groonga_cpu_load_ /usr/share/munin/plugins/groonga_cpu_load_
+usr/share/groonga/munin/plugins/groonga_cpu_time_ /usr/share/munin/plugins/groonga_cpu_time_
+usr/share/groonga/munin/plugins/groonga_disk_ /usr/share/munin/plugins/groonga_disk_
+usr/share/groonga/munin/plugins/groonga_memory_ /usr/share/munin/plugins/groonga_memory_
+usr/share/groonga/munin/plugins/groonga_n_records_ /usr/share/munin/plugins/groonga_n_records_
+usr/share/groonga/munin/plugins/groonga_query_performance_ /usr/share/munin/plugins/groonga_query_performance_
+usr/share/groonga/munin/plugins/groonga_status_ /usr/share/munin/plugins/groonga_status_
+usr/share/groonga/munin/plugins/groonga_throughput_ /usr/share/munin/plugins/groonga_throughput_
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.postinst b/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.postinst
new file mode 100755
index 00000000000..67ccb90bf59
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.postinst
@@ -0,0 +1,27 @@
+#! /bin/sh
+
+set -e
+
+prevver="$2"
+
+init_plugins() {
+ munin-node-configure --shell --remove-also | \
+ grep -e 'groonga_' | \
+ sh
+ invoke-rc.d munin-node restart
+}
+
+case "$1" in
+ configure)
+ init_plugins
+ ;;
+ abort-upgrade|abort-deconfigure|abort-remove)
+ :
+ ;;
+ *)
+ echo "Called with unknown argument $1, bailing out."
+ exit 1
+ ;;
+esac
+
+#DEBHELPER#
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.postrm b/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.postrm
new file mode 100755
index 00000000000..f800a19c232
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-munin-plugins.postrm
@@ -0,0 +1,9 @@
+#! /bin/sh
+
+set -e
+
+if [ "$1" = "purge" ]; then
+ rm -f /etc/munin/plugins/groonga_* > /dev/null 2>&1
+fi
+
+#DEBHELPER#
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-plugin-suggest.install b/storage/mroonga/vendor/groonga/packages/debian/groonga-plugin-suggest.install
new file mode 100644
index 00000000000..83952d69f01
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-plugin-suggest.install
@@ -0,0 +1,2 @@
+usr/bin/groonga-suggest-*
+usr/lib/*/groonga/plugins/suggest/*
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-server-common.postinst b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-common.postinst
new file mode 100755
index 00000000000..7aa3ba8ae88
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-common.postinst
@@ -0,0 +1,42 @@
+#! /bin/sh
+
+set -e
+
+add_system_user() {
+ if ! getent passwd groonga >/dev/null; then
+ adduser --group --system --home /var/lib/groonga groonga
+ fi
+}
+
+fixperms() {
+ for target in /var/run/groonga /var/lib/groonga /etc/groonga /var/log/groonga
+ do
+ dpkg-statoverride --list $target >/dev/null || \
+ dpkg-statoverride --update --add groonga groonga 0755 $target
+ done
+}
+
+create_database() {
+ if [ ! -d /var/lib/groonga/db ]; then
+ mkdir -p /var/lib/groonga/db
+ groonga -n /var/lib/groonga/db/db shutdown > /dev/null
+ chown -R groonga:groonga /var/lib/groonga
+ fi
+}
+
+case "$1" in
+ configure)
+ add_system_user
+ create_database
+ fixperms
+ ;;
+ abort-upgrade|abort-deconfigure|abort-remove)
+ :
+ ;;
+ *)
+ echo "Called with unknown argument $1, bailing out."
+ exit 1
+ ;;
+esac
+
+#DEBHELPER#
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-server-common.postrm b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-common.postrm
new file mode 100755
index 00000000000..c0868f60df5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-common.postrm
@@ -0,0 +1,13 @@
+#! /bin/sh
+
+set -e
+
+if [ "$1" = "purge" ]; then
+ for target in /var/run/groonga /var/lib/groonga /var/log/groonga /etc/groonga
+ do
+ dpkg-statoverride --remove $target
+ rm -rf $target
+ done
+fi
+
+#DEBHELPER#
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.default b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.default
new file mode 100644
index 00000000000..89ee510b2ec
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.default
@@ -0,0 +1,15 @@
+# Default
+#USER=groonga
+#GROUP=groonga
+#ADDRESS=127.0.0.1
+#PORT=10043
+#DATABASE=/var/lib/groonga/db/db
+#LOG_PATH=/var/log/groonga/groonga-gqtp.log
+#QUERY_LOG_PATH=/var/log/groonga/query-gqtp.log
+#PROTOCOL=gqtp
+#GRN_QUERY_EXPANDER_TSV_SYNONYMS_FILE=/usr/share/groonga/synonyms.tsv
+
+# Comment out this to disable groonga daemon.
+ENABLE=yes
+# Set "no" to disable to skip updating column for same value.
+GRN_JA_SKIP_SAME_VALUE_PUT=yes
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.dirs b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.dirs
new file mode 100644
index 00000000000..52486379889
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.dirs
@@ -0,0 +1,2 @@
+var/log/groonga
+var/lib/groonga
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.init b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.init
new file mode 100755
index 00000000000..279b5dbdc41
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.init
@@ -0,0 +1,235 @@
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides: groonga-server-gqtp
+# Required-Start: $remote_fs $network
+# Required-Stop: $remote_fs $network
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: groonga's init script
+# Description: groonga is a full-text search engine and column store.
+### END INIT INFO
+
+# Author: Kouhei Sutou <kou@clear-code.com>
+
+# Do NOT "set -e"
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC="a full-text search engine and column store"
+NAME=groonga
+DAEMON=/usr/bin/$NAME
+USER=groonga
+GROUP=groonga
+DATABASE=/var/lib/groonga/db/db
+ADDRESS=127.0.0.1
+PORT=10043
+PROTOCOL=gqtp
+LOG_PATH=/var/log/groonga/groonga-$PROTOCOL.log
+QUERY_LOG_PATH=/var/log/groonga/query-$PROTOCOL.log
+PIDFILE=/var/run/groonga/$NAME-$PROTOCOL.pid
+SCRIPTNAME=/etc/init.d/$NAME-server-$PROTOCOL
+OPTION_ARGS=""
+START_STOP_DAEMON_ARGS=""
+ENABLE="no"
+
+DEFAULT_FILE=/etc/default/groonga-server-gqtp
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+# Read configuration variable file if it is present
+[ -r $DEFAULT_FILE ] && . $DEFAULT_FILE
+
+[ "$ENABLE" = "yes" ] || exit 0
+
+export GRN_JA_SKIP_SAME_VALUE_PUT="$GRN_JA_SKIP_SAME_VALUE_PUT"
+
+DAEMON_ARGS="-d --pid-path ${PIDFILE}"
+if [ -n "${ADDRESS}" ]; then
+ DAEMON_ARGS="${DAEMON_ARGS} --bind-address ${ADDRESS}"
+fi
+if [ -n "${PORT}" ]; then
+ DAEMON_ARGS="${DAEMON_ARGS} --port ${PORT}"
+fi
+if [ -n "${LOG_PATH}" ]; then
+ DAEMON_ARGS="${DAEMON_ARGS} --log-path ${LOG_PATH}"
+fi
+if [ -n "${QUERY_LOG_PATH}" ]; then
+ DAEMON_ARGS="${DAEMON_ARGS} --query-log-path ${QUERY_LOG_PATH}"
+fi
+if [ -n "${PROTOCOL}" ]; then
+ DAEMON_ARGS="${DAEMON_ARGS} --protocol ${PROTOCOL}"
+fi
+mkdir -p $(dirname ${PIDFILE})
+if [ -n "${USER}" ]; then
+ if ! getent passwd | grep -q "^${USER}:"; then
+ echo "$0: user for running groonga doesn't exist: ${USER}" >&2
+ exit 1
+ fi
+ chown -R ${USER} $(dirname ${PIDFILE})
+ START_STOP_DAEMON_ARGS="${START_STOP_DAEMON_ARGS} --chuid ${USER}"
+fi
+if [ -n "${GROUP}" ]; then
+ if ! getent group | grep -q "^${GROUP}:"; then
+ echo "$0: group for running groonga doesn't exist: ${GROUP}" >&2
+ exit 1
+ fi
+ START_STOP_DAEMON_ARGS="${START_STOP_DAEMON_ARGS} --group ${GROUP}"
+fi
+DAEMON_ARGS="${DAEMON_ARGS} ${OPTION_ARGS}"
+
+if [ -z "${DATABASE}" ]; then
+ echo "$0: DATABASE should not be empty" >&2
+ exit 1
+fi
+
+if [ -f "${DATABASE}" ]; then
+ DAEMON_ARGS="${DAEMON_ARGS} ${DATABASE}"
+else
+ mkdir -p $(dirname ${DATABASE})
+ if [ -n "${USER}" ]; then
+ chown -R ${USER} $(dirname ${DATABASE})
+ fi
+ if [ -n "${GROUP}" ]; then
+ chgrp -R ${GROUP} $(dirname ${DATABASE})
+ fi
+ DAEMON_ARGS="${DAEMON_ARGS} -n ${DATABASE}"
+fi
+
+# Load the VERBOSE setting and other rcS variables
+. /lib/init/vars.sh
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
+. /lib/lsb/init-functions
+
+send_command()
+{
+ command=$1
+ $DAEMON --port ${PORT} -c ${ADDRESS} ${command}
+}
+
+#
+# Function that starts the daemon/service
+#
+do_start()
+{
+ # Return
+ # 0 if daemon has been started
+ # 1 if daemon was already running
+ # 2 if daemon could not be started
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
+ ${START_STOP_DAEMON_ARGS} --test > /dev/null || return 1
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
+ ${START_STOP_DAEMON_ARGS} -- $DAEMON_ARGS || return 2
+ # Add code here, if necessary, that waits for the process to be ready
+ # to handle requests from services started subsequently which depend
+ # on this one. As a last resort, sleep for some time.
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+ # Return
+ # 0 if daemon has been stopped
+ # 1 if daemon was already stopped
+ # 2 if daemon could not be stopped
+ # other if a failure occurred
+
+ send_command shutdown
+
+ start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 \
+ --pidfile $PIDFILE --name $NAME
+ RETVAL="$?"
+ [ "$RETVAL" = 2 ] && return 2
+ # Wait for children to finish too if this is a daemon that forks
+ # and if the daemon is only ever run from this initscript.
+ # If the above conditions are not satisfied then add some other code
+ # that waits for the process to drop all resources that could be
+ # needed by services started subsequently. A last resort is to
+ # sleep for some time.
+ start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 \
+ --pidfile $PIDFILE --exec $DAEMON
+ [ "$?" = 2 ] && return 2
+ # Many daemons don't delete their pidfiles when they exit.
+ rm -f $PIDFILE
+ return "$RETVAL"
+}
+
+#
+# Function that sends a SIGHUP to the daemon/service
+#
+do_reload() {
+ #
+ # If the daemon can reload its configuration without
+ # restarting (for example, when it is sent a SIGHUP),
+ # then implement that here.
+ #
+ start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
+ return 0
+}
+
+do_status() {
+ if [ -z "${ADDRESS}" ]; then
+ ADDRESS="localhost"
+ fi
+ send_command status
+ return $?
+}
+
+case "$1" in
+ start)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
+ do_start
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ stop)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ reload|force-reload)
+ log_daemon_msg "Reloading $DESC" "$NAME"
+ do_reload
+ log_end_msg $?
+ ;;
+ restart)
+ log_daemon_msg "Restarting $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1)
+ do_start
+ case "$?" in
+ 0) log_end_msg 0 ;;
+ 1) log_end_msg 1 ;; # Old process is still running
+ *) log_end_msg 1 ;; # Failed to start
+ esac
+ ;;
+ *)
+ # Failed to stop
+ log_end_msg 1
+ ;;
+ esac
+ ;;
+ status)
+ do_status
+ case "$?" in
+ 0) ;; # service is OK
+ *) exit 3 ;; # program is not running
+ esac
+ ;;
+ *)
+ echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload|status}" >&2
+ exit 3
+ ;;
+esac
+
+:
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.logrotate b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.logrotate
new file mode 100644
index 00000000000..ba0c32c14f9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-gqtp.logrotate
@@ -0,0 +1,17 @@
+/var/log/groonga/*-gqtp.log {
+ daily
+ missingok
+ rotate 30
+ compress
+ delaycompress
+ notifempty
+ create 640 groonga groonga
+ sharedscripts
+ postrotate
+ . /etc/default/groonga-server-gqtp
+ if x"$ENABLE" = x"yes"; then
+ /usr/bin/groonga --port "${PORT:-10043}" -c 127.0.0.1 \
+ log_reopen > /dev/null
+ fi
+ endscript
+}
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.default b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.default
new file mode 100644
index 00000000000..4ccbc719c2e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.default
@@ -0,0 +1,16 @@
+# Default
+#USER=groonga
+#GROUP=groonga
+#ADDRESS=127.0.0.1
+#PORT=10041
+#DATABASE=/var/lib/groonga/db/db
+#LOG_PATH=/var/log/groonga/groonga-http.log
+#QUERY_LOG_PATH=/var/log/groonga/query-http.log
+#PROTOCOL=http
+#GRN_QUERY_EXPANDER_TSV_SYNONYMS_FILE=/usr/share/groonga/synonyms.tsv
+#TIMEOUT=5
+
+# Comment out this to disable groonga daemon.
+ENABLE=yes
+# Set "no" to disable to skip updating column for same value.
+GRN_JA_SKIP_SAME_VALUE_PUT=yes
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.dirs b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.dirs
new file mode 100644
index 00000000000..52486379889
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.dirs
@@ -0,0 +1,2 @@
+var/log/groonga
+var/lib/groonga
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.init b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.init
new file mode 100755
index 00000000000..2e9c96d9016
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.init
@@ -0,0 +1,250 @@
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides: groonga-server-http
+# Required-Start: $remote_fs $network
+# Required-Stop: $remote_fs $network
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: groonga's init script
+# Description: groonga is a full-text search engine and column store.
+### END INIT INFO
+
+# Author: Kouhei Sutou <kou@clear-code.com>
+
+# Do NOT "set -e"
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC="a full-text search engine and column store"
+NAME=groonga
+DAEMON=/usr/bin/$NAME
+CURL=/usr/bin/curl
+USER=groonga
+GROUP=groonga
+DATABASE=/var/lib/groonga/db/db
+ADDRESS=127.0.0.1
+PORT=10041
+PROTOCOL=http
+LOG_PATH=/var/log/groonga/groonga-$PROTOCOL.log
+QUERY_LOG_PATH=/var/log/groonga/query-$PROTOCOL.log
+PIDFILE=/var/run/groonga/$NAME-$PROTOCOL.pid
+SCRIPTNAME=/etc/init.d/$NAME-server-$PROTOCOL
+OPTION_ARGS=""
+START_STOP_DAEMON_ARGS=""
+TIMEOUT=5
+ENABLE="no"
+
+DEFAULT_FILE=/etc/default/groonga-server-http
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+# Read configuration variable file if it is present
+[ -r $DEFAULT_FILE ] && . $DEFAULT_FILE
+
+[ "$ENABLE" = "yes" ] || exit 0
+
+export GRN_JA_SKIP_SAME_VALUE_PUT="$GRN_JA_SKIP_SAME_VALUE_PUT"
+
+DAEMON_ARGS="-d --pid-path ${PIDFILE}"
+if [ -n "${ADDRESS}" ]; then
+ DAEMON_ARGS="${DAEMON_ARGS} --bind-address ${ADDRESS}"
+fi
+if [ -n "${PORT}" ]; then
+ DAEMON_ARGS="${DAEMON_ARGS} --port ${PORT}"
+fi
+if [ -n "${LOG_PATH}" ]; then
+ DAEMON_ARGS="${DAEMON_ARGS} --log-path ${LOG_PATH}"
+fi
+if [ -n "${QUERY_LOG_PATH}" ]; then
+ DAEMON_ARGS="${DAEMON_ARGS} --query-log-path ${QUERY_LOG_PATH}"
+fi
+if [ -n "${PROTOCOL}" ]; then
+ DAEMON_ARGS="${DAEMON_ARGS} --protocol ${PROTOCOL}"
+fi
+mkdir -p $(dirname ${PIDFILE})
+if [ -n "${USER}" ]; then
+ if ! getent passwd | grep -q "^${USER}:"; then
+ echo "$0: user for running groonga doesn't exist: ${USER}" >&2
+ exit 1
+ fi
+ chown -R ${USER} $(dirname ${PIDFILE})
+ START_STOP_DAEMON_ARGS="${START_STOP_DAEMON_ARGS} --chuid ${USER}"
+fi
+if [ -n "${GROUP}" ]; then
+ if ! getent group | grep -q "^${GROUP}:"; then
+ echo "$0: group for running groonga doesn't exist: ${GROUP}" >&2
+ exit 1
+ fi
+ START_STOP_DAEMON_ARGS="${START_STOP_DAEMON_ARGS} --group ${GROUP}"
+fi
+DAEMON_ARGS="${DAEMON_ARGS} ${OPTION_ARGS}"
+
+if [ -z "${DATABASE}" ]; then
+ echo "$0: DATABASE should not be empty" >&2
+ exit 1
+fi
+
+if [ -f "${DATABASE}" ]; then
+ DAEMON_ARGS="${DAEMON_ARGS} ${DATABASE}"
+else
+ mkdir -p $(dirname ${DATABASE})
+ if [ -n "${USER}" ]; then
+ chown -R ${USER} $(dirname ${DATABASE})
+ fi
+ if [ -n "${GROUP}" ]; then
+ chgrp -R ${GROUP} $(dirname ${DATABASE})
+ fi
+ DAEMON_ARGS="${DAEMON_ARGS} -n ${DATABASE}"
+fi
+
+# Load the VERBOSE setting and other rcS variables
+. /lib/init/vars.sh
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
+. /lib/lsb/init-functions
+
+send_command()
+{
+ command=$1
+ if [ "${PROTOCOL}" = "http" ]; then
+ $CURL --max-time $TIMEOUT "http://${ADDRESS}:${PORT}/d/${command}"
+ else
+ $DAEMON --port ${PORT} -c ${ADDRESS} ${command}
+ fi
+}
+
+#
+# Function that starts the daemon/service
+#
+do_start()
+{
+ # Return
+ # 0 if daemon has been started
+ # 1 if daemon was already running
+ # 2 if daemon could not be started
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
+ ${START_STOP_DAEMON_ARGS} --test > /dev/null || return 1
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
+ ${START_STOP_DAEMON_ARGS} -- $DAEMON_ARGS || return 2
+ # Add code here, if necessary, that waits for the process to be ready
+ # to handle requests from services started subsequently which depend
+ # on this one. As a last resort, sleep for some time.
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+ # Return
+ # 0 if daemon has been stopped
+ # 1 if daemon was already stopped
+ # 2 if daemon could not be stopped
+ # other if a failure occurred
+
+ send_command shutdown
+
+ start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 \
+ --pidfile $PIDFILE --name $NAME
+ RETVAL="$?"
+ [ "$RETVAL" = 2 ] && return 2
+ # Wait for children to finish too if this is a daemon that forks
+ # and if the daemon is only ever run from this initscript.
+ # If the above conditions are not satisfied then add some other code
+ # that waits for the process to drop all resources that could be
+ # needed by services started subsequently. A last resort is to
+ # sleep for some time.
+ start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 \
+ --pidfile $PIDFILE --exec $DAEMON
+ [ "$?" = 2 ] && return 2
+ # Many daemons don't delete their pidfiles when they exit.
+ rm -f $PIDFILE
+ return "$RETVAL"
+}
+
+#
+# Function that sends a SIGHUP to the daemon/service
+#
+do_reload() {
+ #
+ # If the daemon can reload its configuration without
+ # restarting (for example, when it is sent a SIGHUP),
+ # then implement that here.
+ #
+ start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
+ return 0
+}
+
+do_status() {
+ if [ -z "${ADDRESS}" ]; then
+ ADDRESS="localhost"
+ fi
+ send_command status
+ return $?
+}
+
+case "$1" in
+ start)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
+ do_start
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ stop)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ reload|force-reload)
+ log_daemon_msg "Reloading $DESC" "$NAME"
+ do_reload
+ log_end_msg $?
+ ;;
+ restart)
+ log_daemon_msg "Restarting $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1)
+ do_start
+ case "$?" in
+ 0) log_end_msg 0 ;;
+ 1) log_end_msg 1 ;; # Old process is still running
+ *) log_end_msg 1 ;; # Failed to start
+ esac
+ ;;
+ *)
+ # Failed to stop
+ log_end_msg 1
+ ;;
+ esac
+ ;;
+ status)
+ do_status
+ case "$?" in
+ 0)
+ # service is OK
+ ;;
+ 7)
+ # curl can't connect to host, program is not running
+ exit 3
+ ;;
+ *)
+ # service status is unknown
+ exit 4
+ ;;
+ esac
+ ;;
+ *)
+ echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload|status}" >&2
+ exit 3
+ ;;
+esac
+
+:
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.logrotate b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.logrotate
new file mode 100644
index 00000000000..8138715ef3d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-server-http.logrotate
@@ -0,0 +1,17 @@
+/var/log/groonga/*-http.log {
+ daily
+ missingok
+ rotate 30
+ compress
+ delaycompress
+ notifempty
+ create 640 groonga groonga
+ sharedscripts
+ postrotate
+ . /etc/default/groonga-server-http
+ if x"$ENABLE" = x"yes"; then
+ /usr/bin/curl --silent --output /dev/null \
+ "http://127.0.0.1:${PORT:-10041}/d/log_reopen"
+ fi
+ endscript
+}
diff --git a/storage/mroonga/vendor/groonga/packages/debian/groonga-tokenizer-mecab.install b/storage/mroonga/vendor/groonga/packages/debian/groonga-tokenizer-mecab.install
new file mode 100644
index 00000000000..b15a2153804
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/groonga-tokenizer-mecab.install
@@ -0,0 +1 @@
+usr/lib/*/groonga/plugins/tokenizers/mecab*
diff --git a/storage/mroonga/vendor/groonga/packages/debian/libgroonga-dev.install b/storage/mroonga/vendor/groonga/packages/debian/libgroonga-dev.install
new file mode 100644
index 00000000000..352d7b13ad9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/libgroonga-dev.install
@@ -0,0 +1,3 @@
+usr/include/groonga/*
+usr/lib/*/pkgconfig/*
+usr/lib/*/libgroonga.so
diff --git a/storage/mroonga/vendor/groonga/packages/debian/libgroonga0.install b/storage/mroonga/vendor/groonga/packages/debian/libgroonga0.install
new file mode 100644
index 00000000000..880c9a196b3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/libgroonga0.install
@@ -0,0 +1,4 @@
+usr/lib/*/libgroonga*.so.*
+usr/lib/*/groonga/plugins/table/*
+usr/lib/*/groonga/plugins/query_expanders/*
+etc/groonga/synonyms.tsv
diff --git a/storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery-1.7.2.js b/storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery-1.7.2.js
new file mode 100644
index 00000000000..3774ff98613
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery-1.7.2.js
@@ -0,0 +1,9404 @@
+/*!
+ * jQuery JavaScript Library v1.7.2
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Wed Mar 21 12:46:34 2012 -0700
+ */
+(function( window, undefined ) {
+
+// Use the correct document accordingly with window argument (sandbox)
+var document = window.document,
+ navigator = window.navigator,
+ location = window.location;
+var jQuery = (function() {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // A simple way to check for HTML strings or ID strings
+ // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+ quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
+
+ // Check if a string has a non-whitespace character in it
+ rnotwhite = /\S/,
+
+ // Used for trimming whitespace
+ trimLeft = /^\s+/,
+ trimRight = /\s+$/,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+ // JSON RegExp
+ rvalidchars = /^[\],:{}\s]*$/,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+ rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+
+ // Useragent RegExp
+ rwebkit = /(webkit)[ \/]([\w.]+)/,
+ ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
+ rmsie = /(msie) ([\w.]+)/,
+ rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
+
+ // Matches dashed string for camelizing
+ rdashAlpha = /-([a-z]|[0-9])/ig,
+ rmsPrefix = /^-ms-/,
+
+ // Used by jQuery.camelCase as callback to replace()
+ fcamelCase = function( all, letter ) {
+ return ( letter + "" ).toUpperCase();
+ },
+
+ // Keep a UserAgent string for use with jQuery.browser
+ userAgent = navigator.userAgent,
+
+ // For matching the engine and version of the browser
+ browserMatch,
+
+ // The deferred used on DOM ready
+ readyList,
+
+ // The ready event handler
+ DOMContentLoaded,
+
+ // Save a reference to some core methods
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ push = Array.prototype.push,
+ slice = Array.prototype.slice,
+ trim = String.prototype.trim,
+ indexOf = Array.prototype.indexOf,
+
+ // [[Class]] -> type pairs
+ class2type = {};
+
+jQuery.fn = jQuery.prototype = {
+ constructor: jQuery,
+ init: function( selector, context, rootjQuery ) {
+ var match, elem, ret, doc;
+
+ // Handle $(""), $(null), or $(undefined)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // The body element only exists once, optimize finding it
+ if ( selector === "body" && !context && document.body ) {
+ this.context = document;
+ this[0] = document.body;
+ this.selector = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+ // Assume that strings that start and end with <> are HTML and skip the regex check
+ match = [ null, selector, null ];
+
+ } else {
+ match = quickExpr.exec( selector );
+ }
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
+ doc = ( context ? context.ownerDocument || context : document );
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ ret = rsingleTag.exec( selector );
+
+ if ( ret ) {
+ if ( jQuery.isPlainObject( context ) ) {
+ selector = [ document.createElement( ret[1] ) ];
+ jQuery.fn.attr.call( selector, context, true );
+
+ } else {
+ selector = [ doc.createElement( ret[1] ) ];
+ }
+
+ } else {
+ ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
+ selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
+ }
+
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $("#id")
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return ( context || rootjQuery ).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if ( selector.selector !== undefined ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.7.2",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return slice.call( this, 0 );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = this.constructor();
+
+ if ( jQuery.isArray( elems ) ) {
+ push.apply( ret, elems );
+
+ } else {
+ jQuery.merge( ret, elems );
+ }
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" ) {
+ ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
+ } else if ( name ) {
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+ }
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Attach the listeners
+ jQuery.bindReady();
+
+ // Add the callback
+ readyList.add( fn );
+
+ return this;
+ },
+
+ eq: function( i ) {
+ i = +i;
+ return i === -1 ?
+ this.slice( i ) :
+ this.slice( i, i + 1 );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ),
+ "slice", slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+ // Either a released hold or an DOMready/load event and not yet ready
+ if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.fireWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger( "ready" ).off( "ready" );
+ }
+ }
+ },
+
+ bindReady: function() {
+ if ( readyList ) {
+ return;
+ }
+
+ readyList = jQuery.Callbacks( "once memory" );
+
+ // Catch cases where $(document).ready() is called after the
+ // browser event has already occurred.
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Mozilla, Opera and webkit nightlies currently support this event
+ if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
+
+ // If IE event model is used
+ } else if ( document.attachEvent ) {
+ // ensure firing before onload,
+ // maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", DOMContentLoaded );
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var toplevel = false;
+
+ try {
+ toplevel = window.frameElement == null;
+ } catch(e) {}
+
+ if ( document.documentElement.doScroll && toplevel ) {
+ doScrollCheck();
+ }
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type(obj) === "array";
+ },
+
+ isWindow: function( obj ) {
+ return obj != null && obj == obj.window;
+ },
+
+ isNumeric: function( obj ) {
+ return !isNaN( parseFloat(obj) ) && isFinite( obj );
+ },
+
+ type: function( obj ) {
+ return obj == null ?
+ String( obj ) :
+ class2type[ toString.call(obj) ] || "object";
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ try {
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !hasOwn.call(obj, "constructor") &&
+ !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+ } catch ( e ) {
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwn.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ for ( var name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw new Error( msg );
+ },
+
+ parseJSON: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ // Attempt to parse using the native JSON parser first
+ if ( window.JSON && window.JSON.parse ) {
+ return window.JSON.parse( data );
+ }
+
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+ .replace( rvalidtokens, "]" )
+ .replace( rvalidbraces, "")) ) {
+
+ return ( new Function( "return " + data ) )();
+
+ }
+ jQuery.error( "Invalid JSON: " + data );
+ },
+
+ // Cross-browser xml parsing
+ parseXML: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+ var xml, tmp;
+ try {
+ if ( window.DOMParser ) { // Standard
+ tmp = new DOMParser();
+ xml = tmp.parseFromString( data , "text/xml" );
+ } else { // IE
+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
+ xml.async = "false";
+ xml.loadXML( data );
+ }
+ } catch( e ) {
+ xml = undefined;
+ }
+ if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
+ jQuery.error( "Invalid XML: " + data );
+ }
+ return xml;
+ },
+
+ noop: function() {},
+
+ // Evaluates a script in a global context
+ // Workarounds based on findings by Jim Driscoll
+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+ globalEval: function( data ) {
+ if ( data && rnotwhite.test( data ) ) {
+ // We use execScript on Internet Explorer
+ // We use an anonymous function so that context is window
+ // rather than jQuery in Firefox
+ ( window.execScript || function( data ) {
+ window[ "eval" ].call( window, data );
+ } )( data );
+ }
+ },
+
+ // Convert dashed to camelCase; used by the css and data modules
+ // Microsoft forgot to hump their vendor prefix (#9572)
+ camelCase: function( string ) {
+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0,
+ length = object.length,
+ isObj = length === undefined || jQuery.isFunction( object );
+
+ if ( args ) {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.apply( object[ name ], args ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.apply( object[ i++ ], args ) === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return object;
+ },
+
+ // Use native String.trim function wherever possible
+ trim: trim ?
+ function( text ) {
+ return text == null ?
+ "" :
+ trim.call( text );
+ } :
+
+ // Otherwise use our own trimming functionality
+ function( text ) {
+ return text == null ?
+ "" :
+ text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( array, results ) {
+ var ret = results || [];
+
+ if ( array != null ) {
+ // The window, strings (and functions) also have 'length'
+ // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
+ var type = jQuery.type( array );
+
+ if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
+ push.call( ret, array );
+ } else {
+ jQuery.merge( ret, array );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array, i ) {
+ var len;
+
+ if ( array ) {
+ if ( indexOf ) {
+ return indexOf.call( array, elem, i );
+ }
+
+ len = array.length;
+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+ for ( ; i < len; i++ ) {
+ // Skip accessing in sparse arrays
+ if ( i in array && array[ i ] === elem ) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var i = first.length,
+ j = 0;
+
+ if ( typeof second.length === "number" ) {
+ for ( var l = second.length; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [], retVal;
+ inv = !!inv;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ retVal = !!callback( elems[ i ], i );
+ if ( inv !== retVal ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var value, key, ret = [],
+ i = 0,
+ length = elems.length,
+ // jquery objects are treated as arrays
+ isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
+
+ // Go through the array, translating each of the items to their
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( key in elems ) {
+ value = callback( elems[ key ], key, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return ret.concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ if ( typeof context === "string" ) {
+ var tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ var args = slice.call( arguments, 2 ),
+ proxy = function() {
+ return fn.apply( context, args.concat( slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ // Mutifunctional method to get and set values to a collection
+ // The value/s can optionally be executed if it's a function
+ access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
+ var exec,
+ bulk = key == null,
+ i = 0,
+ length = elems.length;
+
+ // Sets many values
+ if ( key && typeof key === "object" ) {
+ for ( i in key ) {
+ jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
+ }
+ chainable = 1;
+
+ // Sets one value
+ } else if ( value !== undefined ) {
+ // Optionally, function values get executed if exec is true
+ exec = pass === undefined && jQuery.isFunction( value );
+
+ if ( bulk ) {
+ // Bulk operations only iterate when executing function values
+ if ( exec ) {
+ exec = fn;
+ fn = function( elem, key, value ) {
+ return exec.call( jQuery( elem ), value );
+ };
+
+ // Otherwise they run against the entire set
+ } else {
+ fn.call( elems, value );
+ fn = null;
+ }
+ }
+
+ if ( fn ) {
+ for (; i < length; i++ ) {
+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+ }
+ }
+
+ chainable = 1;
+ }
+
+ return chainable ?
+ elems :
+
+ // Gets
+ bulk ?
+ fn.call( elems ) :
+ length ? fn( elems[0], key ) : emptyGet;
+ },
+
+ now: function() {
+ return ( new Date() ).getTime();
+ },
+
+ // Use of jQuery.browser is frowned upon.
+ // More details: http://docs.jquery.com/Utilities/jQuery.browser
+ uaMatch: function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = rwebkit.exec( ua ) ||
+ ropera.exec( ua ) ||
+ rmsie.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
+ [];
+
+ return { browser: match[1] || "", version: match[2] || "0" };
+ },
+
+ sub: function() {
+ function jQuerySub( selector, context ) {
+ return new jQuerySub.fn.init( selector, context );
+ }
+ jQuery.extend( true, jQuerySub, this );
+ jQuerySub.superclass = this;
+ jQuerySub.fn = jQuerySub.prototype = this();
+ jQuerySub.fn.constructor = jQuerySub;
+ jQuerySub.sub = this.sub;
+ jQuerySub.fn.init = function init( selector, context ) {
+ if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
+ context = jQuerySub( context );
+ }
+
+ return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
+ };
+ jQuerySub.fn.init.prototype = jQuerySub.fn;
+ var rootjQuerySub = jQuerySub(document);
+ return jQuerySub;
+ },
+
+ browser: {}
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+ jQuery.browser[ browserMatch.browser ] = true;
+ jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+ jQuery.browser.safari = true;
+}
+
+// IE doesn't match non-breaking spaces with \s
+if ( rnotwhite.test( "\xA0" ) ) {
+ trimLeft = /^[\s\xA0]+/;
+ trimRight = /[\s\xA0]+$/;
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+ DOMContentLoaded = function() {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ };
+
+} else if ( document.attachEvent ) {
+ DOMContentLoaded = function() {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( document.readyState === "complete" ) {
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+ if ( jQuery.isReady ) {
+ return;
+ }
+
+ try {
+ // If IE is used, use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch(e) {
+ setTimeout( doScrollCheck, 1 );
+ return;
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+}
+
+return jQuery;
+
+})();
+
+
+// String to Object flags format cache
+var flagsCache = {};
+
+// Convert String-formatted flags into Object-formatted ones and store in cache
+function createFlags( flags ) {
+ var object = flagsCache[ flags ] = {},
+ i, length;
+ flags = flags.split( /\s+/ );
+ for ( i = 0, length = flags.length; i < length; i++ ) {
+ object[ flags[i] ] = true;
+ }
+ return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ * flags: an optional list of space-separated flags that will change how
+ * the callback list behaves
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible flags:
+ *
+ * once: will ensure the callback list can only be fired once (like a Deferred)
+ *
+ * memory: will keep track of previous values and will call any callback added
+ * after the list has been fired right away with the latest "memorized"
+ * values (like a Deferred)
+ *
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
+ *
+ * stopOnFalse: interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( flags ) {
+
+ // Convert flags from String-formatted to Object-formatted
+ // (we check in cache first)
+ flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
+
+ var // Actual callback list
+ list = [],
+ // Stack of fire calls for repeatable lists
+ stack = [],
+ // Last fire value (for non-forgettable lists)
+ memory,
+ // Flag to know if list was already fired
+ fired,
+ // Flag to know if list is currently firing
+ firing,
+ // First callback to fire (used internally by add and fireWith)
+ firingStart,
+ // End of the loop when firing
+ firingLength,
+ // Index of currently firing callback (modified by remove if needed)
+ firingIndex,
+ // Add one or several callbacks to the list
+ add = function( args ) {
+ var i,
+ length,
+ elem,
+ type,
+ actual;
+ for ( i = 0, length = args.length; i < length; i++ ) {
+ elem = args[ i ];
+ type = jQuery.type( elem );
+ if ( type === "array" ) {
+ // Inspect recursively
+ add( elem );
+ } else if ( type === "function" ) {
+ // Add if not in unique mode and callback is not in
+ if ( !flags.unique || !self.has( elem ) ) {
+ list.push( elem );
+ }
+ }
+ }
+ },
+ // Fire callbacks
+ fire = function( context, args ) {
+ args = args || [];
+ memory = !flags.memory || [ context, args ];
+ fired = true;
+ firing = true;
+ firingIndex = firingStart || 0;
+ firingStart = 0;
+ firingLength = list.length;
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+ if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
+ memory = true; // Mark as halted
+ break;
+ }
+ }
+ firing = false;
+ if ( list ) {
+ if ( !flags.once ) {
+ if ( stack && stack.length ) {
+ memory = stack.shift();
+ self.fireWith( memory[ 0 ], memory[ 1 ] );
+ }
+ } else if ( memory === true ) {
+ self.disable();
+ } else {
+ list = [];
+ }
+ }
+ },
+ // Actual Callbacks object
+ self = {
+ // Add a callback or a collection of callbacks to the list
+ add: function() {
+ if ( list ) {
+ var length = list.length;
+ add( arguments );
+ // Do we need to add the callbacks to the
+ // current firing batch?
+ if ( firing ) {
+ firingLength = list.length;
+ // With memory, if we're not firing then
+ // we should call right away, unless previous
+ // firing was halted (stopOnFalse)
+ } else if ( memory && memory !== true ) {
+ firingStart = length;
+ fire( memory[ 0 ], memory[ 1 ] );
+ }
+ }
+ return this;
+ },
+ // Remove a callback from the list
+ remove: function() {
+ if ( list ) {
+ var args = arguments,
+ argIndex = 0,
+ argLength = args.length;
+ for ( ; argIndex < argLength ; argIndex++ ) {
+ for ( var i = 0; i < list.length; i++ ) {
+ if ( args[ argIndex ] === list[ i ] ) {
+ // Handle firingIndex and firingLength
+ if ( firing ) {
+ if ( i <= firingLength ) {
+ firingLength--;
+ if ( i <= firingIndex ) {
+ firingIndex--;
+ }
+ }
+ }
+ // Remove the element
+ list.splice( i--, 1 );
+ // If we have some unicity property then
+ // we only need to do this once
+ if ( flags.unique ) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ return this;
+ },
+ // Control if a given callback is in the list
+ has: function( fn ) {
+ if ( list ) {
+ var i = 0,
+ length = list.length;
+ for ( ; i < length; i++ ) {
+ if ( fn === list[ i ] ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+ // Remove all callbacks from the list
+ empty: function() {
+ list = [];
+ return this;
+ },
+ // Have the list do nothing anymore
+ disable: function() {
+ list = stack = memory = undefined;
+ return this;
+ },
+ // Is it disabled?
+ disabled: function() {
+ return !list;
+ },
+ // Lock the list in its current state
+ lock: function() {
+ stack = undefined;
+ if ( !memory || memory === true ) {
+ self.disable();
+ }
+ return this;
+ },
+ // Is it locked?
+ locked: function() {
+ return !stack;
+ },
+ // Call all callbacks with the given context and arguments
+ fireWith: function( context, args ) {
+ if ( stack ) {
+ if ( firing ) {
+ if ( !flags.once ) {
+ stack.push( [ context, args ] );
+ }
+ } else if ( !( flags.once && memory ) ) {
+ fire( context, args );
+ }
+ }
+ return this;
+ },
+ // Call all the callbacks with the given arguments
+ fire: function() {
+ self.fireWith( this, arguments );
+ return this;
+ },
+ // To know if the callbacks have already been called at least once
+ fired: function() {
+ return !!fired;
+ }
+ };
+
+ return self;
+};
+
+
+
+
+var // Static reference to slice
+ sliceDeferred = [].slice;
+
+jQuery.extend({
+
+ Deferred: function( func ) {
+ var doneList = jQuery.Callbacks( "once memory" ),
+ failList = jQuery.Callbacks( "once memory" ),
+ progressList = jQuery.Callbacks( "memory" ),
+ state = "pending",
+ lists = {
+ resolve: doneList,
+ reject: failList,
+ notify: progressList
+ },
+ promise = {
+ done: doneList.add,
+ fail: failList.add,
+ progress: progressList.add,
+
+ state: function() {
+ return state;
+ },
+
+ // Deprecated
+ isResolved: doneList.fired,
+ isRejected: failList.fired,
+
+ then: function( doneCallbacks, failCallbacks, progressCallbacks ) {
+ deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
+ return this;
+ },
+ always: function() {
+ deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
+ return this;
+ },
+ pipe: function( fnDone, fnFail, fnProgress ) {
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( {
+ done: [ fnDone, "resolve" ],
+ fail: [ fnFail, "reject" ],
+ progress: [ fnProgress, "notify" ]
+ }, function( handler, data ) {
+ var fn = data[ 0 ],
+ action = data[ 1 ],
+ returned;
+ if ( jQuery.isFunction( fn ) ) {
+ deferred[ handler ](function() {
+ returned = fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
+ } else {
+ newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
+ }
+ });
+ } else {
+ deferred[ handler ]( newDefer[ action ] );
+ }
+ });
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ if ( obj == null ) {
+ obj = promise;
+ } else {
+ for ( var key in promise ) {
+ obj[ key ] = promise[ key ];
+ }
+ }
+ return obj;
+ }
+ },
+ deferred = promise.promise({}),
+ key;
+
+ for ( key in lists ) {
+ deferred[ key ] = lists[ key ].fire;
+ deferred[ key + "With" ] = lists[ key ].fireWith;
+ }
+
+ // Handle state
+ deferred.done( function() {
+ state = "resolved";
+ }, failList.disable, progressList.lock ).fail( function() {
+ state = "rejected";
+ }, doneList.disable, progressList.lock );
+
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+
+ // All done!
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( firstParam ) {
+ var args = sliceDeferred.call( arguments, 0 ),
+ i = 0,
+ length = args.length,
+ pValues = new Array( length ),
+ count = length,
+ pCount = length,
+ deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
+ firstParam :
+ jQuery.Deferred(),
+ promise = deferred.promise();
+ function resolveFunc( i ) {
+ return function( value ) {
+ args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+ if ( !( --count ) ) {
+ deferred.resolveWith( deferred, args );
+ }
+ };
+ }
+ function progressFunc( i ) {
+ return function( value ) {
+ pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+ deferred.notifyWith( promise, pValues );
+ };
+ }
+ if ( length > 1 ) {
+ for ( ; i < length; i++ ) {
+ if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {
+ args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) );
+ } else {
+ --count;
+ }
+ }
+ if ( !count ) {
+ deferred.resolveWith( deferred, args );
+ }
+ } else if ( deferred !== firstParam ) {
+ deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
+ }
+ return promise;
+ }
+});
+
+
+
+
+jQuery.support = (function() {
+
+ var support,
+ all,
+ a,
+ select,
+ opt,
+ input,
+ fragment,
+ tds,
+ events,
+ eventName,
+ i,
+ isSupported,
+ div = document.createElement( "div" ),
+ documentElement = document.documentElement;
+
+ // Preliminary tests
+ div.setAttribute("className", "t");
+ div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+
+ all = div.getElementsByTagName( "*" );
+ a = div.getElementsByTagName( "a" )[ 0 ];
+
+ // Can't get basic test support
+ if ( !all || !all.length || !a ) {
+ return {};
+ }
+
+ // First batch of supports tests
+ select = document.createElement( "select" );
+ opt = select.appendChild( document.createElement("option") );
+ input = div.getElementsByTagName( "input" )[ 0 ];
+
+ support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: ( div.firstChild.nodeType === 3 ),
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName("tbody").length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName("link").length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText instead)
+ style: /top/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: ( a.getAttribute("href") === "/a" ),
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ // Use a regex to work around a WebKit issue. See #5145
+ opacity: /^0.55/.test( a.style.opacity ),
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Make sure that if no value is specified for a checkbox
+ // that it defaults to "on".
+ // (WebKit defaults to "" instead)
+ checkOn: ( input.value === "on" ),
+
+ // Make sure that a selected-by-default option has a working selected property.
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+ optSelected: opt.selected,
+
+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+ getSetAttribute: div.className !== "t",
+
+ // Tests for enctype support on a form(#6743)
+ enctype: !!document.createElement("form").enctype,
+
+ // Makes sure cloning an html5 element does not cause problems
+ // Where outerHTML is undefined, this still works
+ html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
+
+ // Will be defined later
+ submitBubbles: true,
+ changeBubbles: true,
+ focusinBubbles: false,
+ deleteExpando: true,
+ noCloneEvent: true,
+ inlineBlockNeedsLayout: false,
+ shrinkWrapBlocks: false,
+ reliableMarginRight: true,
+ pixelMargin: true
+ };
+
+ // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead
+ jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat");
+
+ // Make sure checked status is properly cloned
+ input.checked = true;
+ support.noCloneChecked = input.cloneNode( true ).checked;
+
+ // Make sure that the options inside disabled selects aren't marked as disabled
+ // (WebKit marks them as disabled)
+ select.disabled = true;
+ support.optDisabled = !opt.disabled;
+
+ // Test to see if it's possible to delete an expando from an element
+ // Fails in Internet Explorer
+ try {
+ delete div.test;
+ } catch( e ) {
+ support.deleteExpando = false;
+ }
+
+ if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
+ div.attachEvent( "onclick", function() {
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ support.noCloneEvent = false;
+ });
+ div.cloneNode( true ).fireEvent( "onclick" );
+ }
+
+ // Check if a radio maintains its value
+ // after being appended to the DOM
+ input = document.createElement("input");
+ input.value = "t";
+ input.setAttribute("type", "radio");
+ support.radioValue = input.value === "t";
+
+ input.setAttribute("checked", "checked");
+
+ // #11217 - WebKit loses check when the name is after the checked attribute
+ input.setAttribute( "name", "t" );
+
+ div.appendChild( input );
+ fragment = document.createDocumentFragment();
+ fragment.appendChild( div.lastChild );
+
+ // WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ // Check if a disconnected checkbox will retain its checked
+ // value of true after appended to the DOM (IE6/7)
+ support.appendChecked = input.checked;
+
+ fragment.removeChild( input );
+ fragment.appendChild( div );
+
+ // Technique from Juriy Zaytsev
+ // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
+ // We only care about the case where non-standard event systems
+ // are used, namely in IE. Short-circuiting here helps us to
+ // avoid an eval call (in setAttribute) which can cause CSP
+ // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
+ if ( div.attachEvent ) {
+ for ( i in {
+ submit: 1,
+ change: 1,
+ focusin: 1
+ }) {
+ eventName = "on" + i;
+ isSupported = ( eventName in div );
+ if ( !isSupported ) {
+ div.setAttribute( eventName, "return;" );
+ isSupported = ( typeof div[ eventName ] === "function" );
+ }
+ support[ i + "Bubbles" ] = isSupported;
+ }
+ }
+
+ fragment.removeChild( div );
+
+ // Null elements to avoid leaks in IE
+ fragment = select = opt = div = input = null;
+
+ // Run tests that need a body at doc ready
+ jQuery(function() {
+ var container, outer, inner, table, td, offsetSupport,
+ marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight,
+ paddingMarginBorderVisibility, paddingMarginBorder,
+ body = document.getElementsByTagName("body")[0];
+
+ if ( !body ) {
+ // Return for frameset docs that don't have a body
+ return;
+ }
+
+ conMarginTop = 1;
+ paddingMarginBorder = "padding:0;margin:0;border:";
+ positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;";
+ paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;";
+ style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;";
+ html = "<div " + style + "display:block;'><div style='" + paddingMarginBorder + "0;display:block;overflow:hidden;'></div></div>" +
+ "<table " + style + "' cellpadding='0' cellspacing='0'>" +
+ "<tr><td></td></tr></table>";
+
+ container = document.createElement("div");
+ container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px";
+ body.insertBefore( container, body.firstChild );
+
+ // Construct the test element
+ div = document.createElement("div");
+ container.appendChild( div );
+
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ // (only IE 8 fails this test)
+ div.innerHTML = "<table><tr><td style='" + paddingMarginBorder + "0;display:none'></td><td>t</td></tr></table>";
+ tds = div.getElementsByTagName( "td" );
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+ tds[ 0 ].style.display = "";
+ tds[ 1 ].style.display = "none";
+
+ // Check if empty table cells still have offsetWidth/Height
+ // (IE <= 8 fail this test)
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. For more
+ // info see bug #3333
+ // Fails in WebKit before Feb 2011 nightlies
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ if ( window.getComputedStyle ) {
+ div.innerHTML = "";
+ marginDiv = document.createElement( "div" );
+ marginDiv.style.width = "0";
+ marginDiv.style.marginRight = "0";
+ div.style.width = "2px";
+ div.appendChild( marginDiv );
+ support.reliableMarginRight =
+ ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
+ }
+
+ if ( typeof div.style.zoom !== "undefined" ) {
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ // (IE < 8 does this)
+ div.innerHTML = "";
+ div.style.width = div.style.padding = "1px";
+ div.style.border = 0;
+ div.style.overflow = "hidden";
+ div.style.display = "inline";
+ div.style.zoom = 1;
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
+
+ // Check if elements with layout shrink-wrap their children
+ // (IE 6 does this)
+ div.style.display = "block";
+ div.style.overflow = "visible";
+ div.innerHTML = "<div style='width:5px;'></div>";
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
+ }
+
+ div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility;
+ div.innerHTML = html;
+
+ outer = div.firstChild;
+ inner = outer.firstChild;
+ td = outer.nextSibling.firstChild.firstChild;
+
+ offsetSupport = {
+ doesNotAddBorder: ( inner.offsetTop !== 5 ),
+ doesAddBorderForTableAndCells: ( td.offsetTop === 5 )
+ };
+
+ inner.style.position = "fixed";
+ inner.style.top = "20px";
+
+ // safari subtracts parent border width here which is 5px
+ offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 );
+ inner.style.position = inner.style.top = "";
+
+ outer.style.overflow = "hidden";
+ outer.style.position = "relative";
+
+ offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 );
+ offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );
+
+ if ( window.getComputedStyle ) {
+ div.style.marginTop = "1%";
+ support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%";
+ }
+
+ if ( typeof container.style.zoom !== "undefined" ) {
+ container.style.zoom = 1;
+ }
+
+ body.removeChild( container );
+ marginDiv = div = container = null;
+
+ jQuery.extend( support, offsetSupport );
+ });
+
+ return support;
+})();
+
+
+
+
+var rbrace = /^(?:\{.*\}|\[.*\])$/,
+ rmultiDash = /([A-Z])/g;
+
+jQuery.extend({
+ cache: {},
+
+ // Please use with caution
+ uuid: 0,
+
+ // Unique for each copy of jQuery on the page
+ // Non-digits removed to match rinlinejQuery
+ expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
+
+ // The following elements throw uncatchable exceptions if you
+ // attempt to add expando properties to them.
+ noData: {
+ "embed": true,
+ // Ban all objects except for Flash (which handle expandos)
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+ "applet": true
+ },
+
+ hasData: function( elem ) {
+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+ return !!elem && !isEmptyDataObject( elem );
+ },
+
+ data: function( elem, name, data, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var privateCache, thisCache, ret,
+ internalKey = jQuery.expando,
+ getByName = typeof name === "string",
+
+ // We have to handle DOM nodes and JS objects differently because IE6-7
+ // can't GC object references properly across the DOM-JS boundary
+ isNode = elem.nodeType,
+
+ // Only DOM nodes need the global jQuery cache; JS object data is
+ // attached directly to the object so GC can occur automatically
+ cache = isNode ? jQuery.cache : elem,
+
+ // Only defining an ID for JS objects if its cache already exists allows
+ // the code to shortcut on the same path as a DOM node with no cache
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
+ isEvents = name === "events";
+
+ // Avoid doing any more work than we need to when trying to get data on an
+ // object that has no data at all
+ if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
+ return;
+ }
+
+ if ( !id ) {
+ // Only DOM nodes need a new unique ID for each element since their data
+ // ends up in the global cache
+ if ( isNode ) {
+ elem[ internalKey ] = id = ++jQuery.uuid;
+ } else {
+ id = internalKey;
+ }
+ }
+
+ if ( !cache[ id ] ) {
+ cache[ id ] = {};
+
+ // Avoids exposing jQuery metadata on plain JS objects when the object
+ // is serialized using JSON.stringify
+ if ( !isNode ) {
+ cache[ id ].toJSON = jQuery.noop;
+ }
+ }
+
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
+ // shallow copied over onto the existing cache
+ if ( typeof name === "object" || typeof name === "function" ) {
+ if ( pvt ) {
+ cache[ id ] = jQuery.extend( cache[ id ], name );
+ } else {
+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+ }
+ }
+
+ privateCache = thisCache = cache[ id ];
+
+ // jQuery data() is stored in a separate object inside the object's internal data
+ // cache in order to avoid key collisions between internal data and user-defined
+ // data.
+ if ( !pvt ) {
+ if ( !thisCache.data ) {
+ thisCache.data = {};
+ }
+
+ thisCache = thisCache.data;
+ }
+
+ if ( data !== undefined ) {
+ thisCache[ jQuery.camelCase( name ) ] = data;
+ }
+
+ // Users should not attempt to inspect the internal events object using jQuery.data,
+ // it is undocumented and subject to change. But does anyone listen? No.
+ if ( isEvents && !thisCache[ name ] ) {
+ return privateCache.events;
+ }
+
+ // Check for both converted-to-camel and non-converted data property names
+ // If a data property was specified
+ if ( getByName ) {
+
+ // First Try to find as-is property data
+ ret = thisCache[ name ];
+
+ // Test for null|undefined property data
+ if ( ret == null ) {
+
+ // Try to find the camelCased property
+ ret = thisCache[ jQuery.camelCase( name ) ];
+ }
+ } else {
+ ret = thisCache;
+ }
+
+ return ret;
+ },
+
+ removeData: function( elem, name, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var thisCache, i, l,
+
+ // Reference to internal data cache key
+ internalKey = jQuery.expando,
+
+ isNode = elem.nodeType,
+
+ // See jQuery.data for more information
+ cache = isNode ? jQuery.cache : elem,
+
+ // See jQuery.data for more information
+ id = isNode ? elem[ internalKey ] : internalKey;
+
+ // If there is already no cache entry for this object, there is no
+ // purpose in continuing
+ if ( !cache[ id ] ) {
+ return;
+ }
+
+ if ( name ) {
+
+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+ if ( thisCache ) {
+
+ // Support array or space separated string names for data keys
+ if ( !jQuery.isArray( name ) ) {
+
+ // try the string as a key before any manipulation
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+
+ // split the camel cased version by spaces unless a key with the spaces exists
+ name = jQuery.camelCase( name );
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+ name = name.split( " " );
+ }
+ }
+ }
+
+ for ( i = 0, l = name.length; i < l; i++ ) {
+ delete thisCache[ name[i] ];
+ }
+
+ // If there is no data left in the cache, we want to continue
+ // and let the cache object itself get destroyed
+ if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
+ return;
+ }
+ }
+ }
+
+ // See jQuery.data for more information
+ if ( !pvt ) {
+ delete cache[ id ].data;
+
+ // Don't destroy the parent cache unless the internal data object
+ // had been the only thing left in it
+ if ( !isEmptyDataObject(cache[ id ]) ) {
+ return;
+ }
+ }
+
+ // Browsers that fail expando deletion also refuse to delete expandos on
+ // the window, but it will allow it on all other JS objects; other browsers
+ // don't care
+ // Ensure that `cache` is not a window object #10080
+ if ( jQuery.support.deleteExpando || !cache.setInterval ) {
+ delete cache[ id ];
+ } else {
+ cache[ id ] = null;
+ }
+
+ // We destroyed the cache and need to eliminate the expando on the node to avoid
+ // false lookups in the cache for entries that no longer exist
+ if ( isNode ) {
+ // IE does not allow us to delete expando properties from nodes,
+ // nor does it have a removeAttribute function on Document nodes;
+ // we must handle all of these cases
+ if ( jQuery.support.deleteExpando ) {
+ delete elem[ internalKey ];
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( internalKey );
+ } else {
+ elem[ internalKey ] = null;
+ }
+ }
+ },
+
+ // For internal use only.
+ _data: function( elem, name, data ) {
+ return jQuery.data( elem, name, data, true );
+ },
+
+ // A method for determining if a DOM node can handle the data expando
+ acceptData: function( elem ) {
+ if ( elem.nodeName ) {
+ var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+ if ( match ) {
+ return !(match === true || elem.getAttribute("classid") !== match);
+ }
+ }
+
+ return true;
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ var parts, part, attr, name, l,
+ elem = this[0],
+ i = 0,
+ data = null;
+
+ // Gets all values
+ if ( key === undefined ) {
+ if ( this.length ) {
+ data = jQuery.data( elem );
+
+ if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+ attr = elem.attributes;
+ for ( l = attr.length; i < l; i++ ) {
+ name = attr[i].name;
+
+ if ( name.indexOf( "data-" ) === 0 ) {
+ name = jQuery.camelCase( name.substring(5) );
+
+ dataAttr( elem, name, data[ name ] );
+ }
+ }
+ jQuery._data( elem, "parsedAttrs", true );
+ }
+ }
+
+ return data;
+ }
+
+ // Sets multiple values
+ if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ parts = key.split( ".", 2 );
+ parts[1] = parts[1] ? "." + parts[1] : "";
+ part = parts[1] + "!";
+
+ return jQuery.access( this, function( value ) {
+
+ if ( value === undefined ) {
+ data = this.triggerHandler( "getData" + part, [ parts[0] ] );
+
+ // Try to fetch any internally stored data first
+ if ( data === undefined && elem ) {
+ data = jQuery.data( elem, key );
+ data = dataAttr( elem, key, data );
+ }
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+ }
+
+ parts[1] = value;
+ this.each(function() {
+ var self = jQuery( this );
+
+ self.triggerHandler( "setData" + part, parts );
+ jQuery.data( this, key, value );
+ self.triggerHandler( "changeData" + part, parts );
+ });
+ }, null, value, arguments.length > 1, null, false );
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+
+function dataAttr( elem, key, data ) {
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+
+ var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+
+ data = elem.getAttribute( name );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ jQuery.isNumeric( data ) ? +data :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ jQuery.data( elem, key, data );
+
+ } else {
+ data = undefined;
+ }
+ }
+
+ return data;
+}
+
+// checks a cache object for emptiness
+function isEmptyDataObject( obj ) {
+ for ( var name in obj ) {
+
+ // if the public data object is empty, the private is still empty
+ if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+ continue;
+ }
+ if ( name !== "toJSON" ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+
+function handleQueueMarkDefer( elem, type, src ) {
+ var deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ defer = jQuery._data( elem, deferDataKey );
+ if ( defer &&
+ ( src === "queue" || !jQuery._data(elem, queueDataKey) ) &&
+ ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
+ // Give room for hard-coded callbacks to fire first
+ // and eventually mark/queue something else on the element
+ setTimeout( function() {
+ if ( !jQuery._data( elem, queueDataKey ) &&
+ !jQuery._data( elem, markDataKey ) ) {
+ jQuery.removeData( elem, deferDataKey, true );
+ defer.fire();
+ }
+ }, 0 );
+ }
+}
+
+jQuery.extend({
+
+ _mark: function( elem, type ) {
+ if ( elem ) {
+ type = ( type || "fx" ) + "mark";
+ jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 );
+ }
+ },
+
+ _unmark: function( force, elem, type ) {
+ if ( force !== true ) {
+ type = elem;
+ elem = force;
+ force = false;
+ }
+ if ( elem ) {
+ type = type || "fx";
+ var key = type + "mark",
+ count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 );
+ if ( count ) {
+ jQuery._data( elem, key, count );
+ } else {
+ jQuery.removeData( elem, key, true );
+ handleQueueMarkDefer( elem, type, "mark" );
+ }
+ }
+ },
+
+ queue: function( elem, type, data ) {
+ var q;
+ if ( elem ) {
+ type = ( type || "fx" ) + "queue";
+ q = jQuery._data( elem, type );
+
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !q || jQuery.isArray(data) ) {
+ q = jQuery._data( elem, type, jQuery.makeArray(data) );
+ } else {
+ q.push( data );
+ }
+ }
+ return q || [];
+ }
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ fn = queue.shift(),
+ hooks = {};
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ }
+
+ if ( fn ) {
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift( "inprogress" );
+ }
+
+ jQuery._data( elem, type + ".run", hooks );
+ fn.call( elem, function() {
+ jQuery.dequeue( elem, type );
+ }, hooks );
+ }
+
+ if ( !queue.length ) {
+ jQuery.removeData( elem, type + "queue " + type + ".run", true );
+ handleQueueMarkDefer( elem, type, "queue" );
+ }
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ var setter = 2;
+
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ setter--;
+ }
+
+ if ( arguments.length < setter ) {
+ return jQuery.queue( this[0], type );
+ }
+
+ return data === undefined ?
+ this :
+ this.each(function() {
+ var queue = jQuery.queue( this, type, data );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+ // Based off of the plugin by Clint Helfers, with permission.
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function( next, hooks ) {
+ var timeout = setTimeout( next, time );
+ hooks.stop = function() {
+ clearTimeout( timeout );
+ };
+ });
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, object ) {
+ if ( typeof type !== "string" ) {
+ object = type;
+ type = undefined;
+ }
+ type = type || "fx";
+ var defer = jQuery.Deferred(),
+ elements = this,
+ i = elements.length,
+ count = 1,
+ deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ tmp;
+ function resolve() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ }
+ while( i-- ) {
+ if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
+ ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
+ jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
+ jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) {
+ count++;
+ tmp.add( resolve );
+ }
+ }
+ resolve();
+ return defer.promise( object );
+ }
+});
+
+
+
+
+var rclass = /[\n\t\r]/g,
+ rspace = /\s+/,
+ rreturn = /\r/g,
+ rtype = /^(?:button|input)$/i,
+ rfocusable = /^(?:button|input|object|select|textarea)$/i,
+ rclickable = /^a(?:rea)?$/i,
+ rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
+ getSetAttribute = jQuery.support.getSetAttribute,
+ nodeHook, boolHook, fixSpecified;
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
+ },
+
+ removeAttr: function( name ) {
+ return this.each(function() {
+ jQuery.removeAttr( this, name );
+ });
+ },
+
+ prop: function( name, value ) {
+ return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+ },
+
+ removeProp: function( name ) {
+ name = jQuery.propFix[ name ] || name;
+ return this.each(function() {
+ // try/catch handles cases where IE balks (such as removing a property on window)
+ try {
+ this[ name ] = undefined;
+ delete this[ name ];
+ } catch( e ) {}
+ });
+ },
+
+ addClass: function( value ) {
+ var classNames, i, l, elem,
+ setClass, c, cl;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).addClass( value.call(this, j, this.className) );
+ });
+ }
+
+ if ( value && typeof value === "string" ) {
+ classNames = value.split( rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.nodeType === 1 ) {
+ if ( !elem.className && classNames.length === 1 ) {
+ elem.className = value;
+
+ } else {
+ setClass = " " + elem.className + " ";
+
+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+ if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
+ setClass += classNames[ c ] + " ";
+ }
+ }
+ elem.className = jQuery.trim( setClass );
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ var classNames, i, l, elem, className, c, cl;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).removeClass( value.call(this, j, this.className) );
+ });
+ }
+
+ if ( (value && typeof value === "string") || value === undefined ) {
+ classNames = ( value || "" ).split( rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.nodeType === 1 && elem.className ) {
+ if ( value ) {
+ className = (" " + elem.className + " ").replace( rclass, " " );
+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+ className = className.replace(" " + classNames[ c ] + " ", " ");
+ }
+ elem.className = jQuery.trim( className );
+
+ } else {
+ elem.className = "";
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value,
+ isBool = typeof stateVal === "boolean";
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( i ) {
+ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className,
+ i = 0,
+ self = jQuery( this ),
+ state = stateVal,
+ classNames = value.split( rspace );
+
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space seperated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
+
+ } else if ( type === "undefined" || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ jQuery._data( this, "__className__", this.className );
+ }
+
+ // toggle whole className
+ this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ",
+ i = 0,
+ l = this.length;
+ for ( ; i < l; i++ ) {
+ if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ val: function( value ) {
+ var hooks, ret, isFunction,
+ elem = this[0];
+
+ if ( !arguments.length ) {
+ if ( elem ) {
+ hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+ return ret;
+ }
+
+ ret = elem.value;
+
+ return typeof ret === "string" ?
+ // handle most common string cases
+ ret.replace(rreturn, "") :
+ // handle cases where value is null/undef or number
+ ret == null ? "" : ret;
+ }
+
+ return;
+ }
+
+ isFunction = jQuery.isFunction( value );
+
+ return this.each(function( i ) {
+ var self = jQuery(this), val;
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call( this, i, self.val() );
+ } else {
+ val = value;
+ }
+
+ // Treat null/undefined as ""; convert numbers to string
+ if ( val == null ) {
+ val = "";
+ } else if ( typeof val === "number" ) {
+ val += "";
+ } else if ( jQuery.isArray( val ) ) {
+ val = jQuery.map(val, function ( value ) {
+ return value == null ? "" : value + "";
+ });
+ }
+
+ hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+ // If set returns undefined, fall back to normal setting
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ valHooks: {
+ option: {
+ get: function( elem ) {
+ // attributes.value is undefined in Blackberry 4.7 but
+ // uses .value. See #6932
+ var val = elem.attributes.value;
+ return !val || val.specified ? elem.value : elem.text;
+ }
+ },
+ select: {
+ get: function( elem ) {
+ var value, i, max, option,
+ index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type === "select-one";
+
+ // Nothing was selected
+ if ( index < 0 ) {
+ return null;
+ }
+
+ // Loop through all the selected options
+ i = one ? index : 0;
+ max = one ? index + 1 : options.length;
+ for ( ; i < max; i++ ) {
+ option = options[ i ];
+
+ // Don't return options that are disabled or in a disabled optgroup
+ if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
+ (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
+
+ // Get the specific value for the option
+ value = jQuery( option ).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
+ if ( one && !values.length && options.length ) {
+ return jQuery( options[ index ] ).val();
+ }
+
+ return values;
+ },
+
+ set: function( elem, value ) {
+ var values = jQuery.makeArray( value );
+
+ jQuery(elem).find("option").each(function() {
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+ });
+
+ if ( !values.length ) {
+ elem.selectedIndex = -1;
+ }
+ return values;
+ }
+ }
+ },
+
+ attrFn: {
+ val: true,
+ css: true,
+ html: true,
+ text: true,
+ data: true,
+ width: true,
+ height: true,
+ offset: true
+ },
+
+ attr: function( elem, name, value, pass ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // don't get/set attributes on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ if ( pass && name in jQuery.attrFn ) {
+ return jQuery( elem )[ name ]( value );
+ }
+
+ // Fallback to prop when attributes are not supported
+ if ( typeof elem.getAttribute === "undefined" ) {
+ return jQuery.prop( elem, name, value );
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ // All attributes are lowercase
+ // Grab necessary hook if one is defined
+ if ( notxml ) {
+ name = name.toLowerCase();
+ hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
+ }
+
+ if ( value !== undefined ) {
+
+ if ( value === null ) {
+ jQuery.removeAttr( elem, name );
+ return;
+
+ } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ elem.setAttribute( name, "" + value );
+ return value;
+ }
+
+ } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+
+ ret = elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return ret === null ?
+ undefined :
+ ret;
+ }
+ },
+
+ removeAttr: function( elem, value ) {
+ var propName, attrNames, name, l, isBool,
+ i = 0;
+
+ if ( value && elem.nodeType === 1 ) {
+ attrNames = value.toLowerCase().split( rspace );
+ l = attrNames.length;
+
+ for ( ; i < l; i++ ) {
+ name = attrNames[ i ];
+
+ if ( name ) {
+ propName = jQuery.propFix[ name ] || name;
+ isBool = rboolean.test( name );
+
+ // See #9699 for explanation of this approach (setting first, then removal)
+ // Do not do this for boolean attributes (see #10870)
+ if ( !isBool ) {
+ jQuery.attr( elem, name, "" );
+ }
+ elem.removeAttribute( getSetAttribute ? name : propName );
+
+ // Set corresponding property to false for boolean attributes
+ if ( isBool && propName in elem ) {
+ elem[ propName ] = false;
+ }
+ }
+ }
+ }
+ },
+
+ attrHooks: {
+ type: {
+ set: function( elem, value ) {
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
+ jQuery.error( "type property can't be changed" );
+ } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+ // Setting the type on a radio button after the value resets the value in IE6-9
+ // Reset value to it's default in case type is set after value
+ // This is for element creation
+ var val = elem.value;
+ elem.setAttribute( "type", value );
+ if ( val ) {
+ elem.value = val;
+ }
+ return value;
+ }
+ }
+ },
+ // Use the value property for back compat
+ // Use the nodeHook for button elements in IE6/7 (#1954)
+ value: {
+ get: function( elem, name ) {
+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+ return nodeHook.get( elem, name );
+ }
+ return name in elem ?
+ elem.value :
+ null;
+ },
+ set: function( elem, value, name ) {
+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+ return nodeHook.set( elem, value, name );
+ }
+ // Does not return so that setAttribute is also used
+ elem.value = value;
+ }
+ }
+ },
+
+ propFix: {
+ tabindex: "tabIndex",
+ readonly: "readOnly",
+ "for": "htmlFor",
+ "class": "className",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ cellpadding: "cellPadding",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ usemap: "useMap",
+ frameborder: "frameBorder",
+ contenteditable: "contentEditable"
+ },
+
+ prop: function( elem, name, value ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // don't get/set properties on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ if ( notxml ) {
+ // Fix name and attach hooks
+ name = jQuery.propFix[ name ] || name;
+ hooks = jQuery.propHooks[ name ];
+ }
+
+ if ( value !== undefined ) {
+ if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ return ( elem[ name ] = value );
+ }
+
+ } else {
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+ return elem[ name ];
+ }
+ }
+ },
+
+ propHooks: {
+ tabIndex: {
+ get: function( elem ) {
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+ var attributeNode = elem.getAttributeNode("tabindex");
+
+ return attributeNode && attributeNode.specified ?
+ parseInt( attributeNode.value, 10 ) :
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+ 0 :
+ undefined;
+ }
+ }
+ }
+});
+
+// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional)
+jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex;
+
+// Hook for boolean attributes
+boolHook = {
+ get: function( elem, name ) {
+ // Align boolean attributes with corresponding properties
+ // Fall back to attribute presence where some booleans are not supported
+ var attrNode,
+ property = jQuery.prop( elem, name );
+ return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
+ name.toLowerCase() :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ var propName;
+ if ( value === false ) {
+ // Remove boolean attributes when set to false
+ jQuery.removeAttr( elem, name );
+ } else {
+ // value is true since we know at this point it's type boolean and not false
+ // Set boolean attributes to the same name and set the DOM property
+ propName = jQuery.propFix[ name ] || name;
+ if ( propName in elem ) {
+ // Only set the IDL specifically if it already exists on the element
+ elem[ propName ] = true;
+ }
+
+ elem.setAttribute( name, name.toLowerCase() );
+ }
+ return name;
+ }
+};
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !getSetAttribute ) {
+
+ fixSpecified = {
+ name: true,
+ id: true,
+ coords: true
+ };
+
+ // Use this for any attribute in IE6/7
+ // This fixes almost every IE6/7 issue
+ nodeHook = jQuery.valHooks.button = {
+ get: function( elem, name ) {
+ var ret;
+ ret = elem.getAttributeNode( name );
+ return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ?
+ ret.nodeValue :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ // Set the existing or create a new attribute node
+ var ret = elem.getAttributeNode( name );
+ if ( !ret ) {
+ ret = document.createAttribute( name );
+ elem.setAttributeNode( ret );
+ }
+ return ( ret.nodeValue = value + "" );
+ }
+ };
+
+ // Apply the nodeHook to tabindex
+ jQuery.attrHooks.tabindex.set = nodeHook.set;
+
+ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
+ // This is for removals
+ jQuery.each([ "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ set: function( elem, value ) {
+ if ( value === "" ) {
+ elem.setAttribute( name, "auto" );
+ return value;
+ }
+ }
+ });
+ });
+
+ // Set contenteditable to false on removals(#10429)
+ // Setting to empty string throws an error as an invalid value
+ jQuery.attrHooks.contenteditable = {
+ get: nodeHook.get,
+ set: function( elem, value, name ) {
+ if ( value === "" ) {
+ value = "false";
+ }
+ nodeHook.set( elem, value, name );
+ }
+ };
+}
+
+
+// Some attributes require a special call on IE
+if ( !jQuery.support.hrefNormalized ) {
+ jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ get: function( elem ) {
+ var ret = elem.getAttribute( name, 2 );
+ return ret === null ? undefined : ret;
+ }
+ });
+ });
+}
+
+if ( !jQuery.support.style ) {
+ jQuery.attrHooks.style = {
+ get: function( elem ) {
+ // Return undefined in the case of empty string
+ // Normalize to lowercase since IE uppercases css property names
+ return elem.style.cssText.toLowerCase() || undefined;
+ },
+ set: function( elem, value ) {
+ return ( elem.style.cssText = "" + value );
+ }
+ };
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+ jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+ get: function( elem ) {
+ var parent = elem.parentNode;
+
+ if ( parent ) {
+ parent.selectedIndex;
+
+ // Make sure that it also works with optgroups, see #5701
+ if ( parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ }
+ return null;
+ }
+ });
+}
+
+// IE6/7 call enctype encoding
+if ( !jQuery.support.enctype ) {
+ jQuery.propFix.enctype = "encoding";
+}
+
+// Radios and checkboxes getter/setter
+if ( !jQuery.support.checkOn ) {
+ jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = {
+ get: function( elem ) {
+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ }
+ };
+ });
+}
+jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+ set: function( elem, value ) {
+ if ( jQuery.isArray( value ) ) {
+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+ }
+ }
+ });
+});
+
+
+
+
+var rformElems = /^(?:textarea|input|select)$/i,
+ rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/,
+ rhoverHack = /(?:^|\s)hover(\.\S+)?\b/,
+ rkeyEvent = /^key/,
+ rmouseEvent = /^(?:mouse|contextmenu)|click/,
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+ rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,
+ quickParse = function( selector ) {
+ var quick = rquickIs.exec( selector );
+ if ( quick ) {
+ // 0 1 2 3
+ // [ _, tag, id, class ]
+ quick[1] = ( quick[1] || "" ).toLowerCase();
+ quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" );
+ }
+ return quick;
+ },
+ quickIs = function( elem, m ) {
+ var attrs = elem.attributes || {};
+ return (
+ (!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
+ (!m[2] || (attrs.id || {}).value === m[2]) &&
+ (!m[3] || m[3].test( (attrs[ "class" ] || {}).value ))
+ );
+ },
+ hoverHack = function( events ) {
+ return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
+ };
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+ add: function( elem, types, handler, data, selector ) {
+
+ var elemData, eventHandle, events,
+ t, tns, type, namespaces, handleObj,
+ handleObjIn, quick, handlers, special;
+
+ // Don't attach events to noData or text/comment nodes (allow plain objects tho)
+ if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
+ return;
+ }
+
+ // Caller can pass in an object of custom data in lieu of the handler
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ selector = handleObjIn.selector;
+ }
+
+ // Make sure that the handler has a unique ID, used to find/remove it later
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure and main handler, if this is the first
+ events = elemData.events;
+ if ( !events ) {
+ elemData.events = events = {};
+ }
+ eventHandle = elemData.handle;
+ if ( !eventHandle ) {
+ elemData.handle = eventHandle = function( e ) {
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
+ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+ eventHandle.elem = elem;
+ }
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ types = jQuery.trim( hoverHack(types) ).split( " " );
+ for ( t = 0; t < types.length; t++ ) {
+
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = tns[1];
+ namespaces = ( tns[2] || "" ).split( "." ).sort();
+
+ // If event changes its type, use the special event handlers for the changed type
+ special = jQuery.event.special[ type ] || {};
+
+ // If selector defined, determine special event api type, otherwise given type
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+
+ // Update special based on newly reset type
+ special = jQuery.event.special[ type ] || {};
+
+ // handleObj is passed to all event handlers
+ handleObj = jQuery.extend({
+ type: type,
+ origType: tns[1],
+ data: data,
+ handler: handler,
+ guid: handler.guid,
+ selector: selector,
+ quick: selector && quickParse( selector ),
+ namespace: namespaces.join(".")
+ }, handleObjIn );
+
+ // Init the event handler queue if we're the first
+ handlers = events[ type ];
+ if ( !handlers ) {
+ handlers = events[ type ] = [];
+ handlers.delegateCount = 0;
+
+ // Only use addEventListener/attachEvent if the special events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add to the element's handler list, delegates in front
+ if ( selector ) {
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
+ } else {
+ handlers.push( handleObj );
+ }
+
+ // Keep track of which events have ever been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, selector, mappedTypes ) {
+
+ var elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
+ t, tns, type, origType, namespaces, origCount,
+ j, events, special, handle, eventType, handleObj;
+
+ if ( !elemData || !(events = elemData.events) ) {
+ return;
+ }
+
+ // Once for each type.namespace in types; type may be omitted
+ types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
+ for ( t = 0; t < types.length; t++ ) {
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tns[1];
+ namespaces = tns[2];
+
+ // Unbind all events (on this namespace, if provided) for the element
+ if ( !type ) {
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+ }
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+ type = ( selector? special.delegateType : special.bindType ) || type;
+ eventType = events[ type ] || [];
+ origCount = eventType.length;
+ namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
+
+ // Remove matching events
+ for ( j = 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
+ ( !handler || handler.guid === handleObj.guid ) &&
+ ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
+ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+ eventType.splice( j--, 1 );
+
+ if ( handleObj.selector ) {
+ eventType.delegateCount--;
+ }
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+ }
+
+ // Remove generic event handler if we removed something and no more handlers exist
+ // (avoids potential for endless recursion during removal of special event handlers)
+ if ( eventType.length === 0 && origCount !== eventType.length ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ handle = elemData.handle;
+ if ( handle ) {
+ handle.elem = null;
+ }
+
+ // removeData also checks for emptiness and clears the expando if empty
+ // so use it instead of delete
+ jQuery.removeData( elem, [ "events", "handle" ], true );
+ }
+ },
+
+ // Events that are safe to short-circuit if no handlers are attached.
+ // Native DOM events should not be added, they may have inline handlers.
+ customEvent: {
+ "getData": true,
+ "setData": true,
+ "changeData": true
+ },
+
+ trigger: function( event, data, elem, onlyHandlers ) {
+ // Don't do events on text and comment nodes
+ if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
+ return;
+ }
+
+ // Event object or event type
+ var type = event.type || event,
+ namespaces = [],
+ cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;
+
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+ return;
+ }
+
+ if ( type.indexOf( "!" ) >= 0 ) {
+ // Exclusive events trigger only for the exact event (no namespaces)
+ type = type.slice(0, -1);
+ exclusive = true;
+ }
+
+ if ( type.indexOf( "." ) >= 0 ) {
+ // Namespaced trigger; create a regexp to match event type in handle()
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespaces.sort();
+ }
+
+ if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
+ // No jQuery handlers for this event type, and it can't have inline handlers
+ return;
+ }
+
+ // Caller can pass in an Event, Object, or just an event type string
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[ jQuery.expando ] ? event :
+ // Object literal
+ new jQuery.Event( type, event ) :
+ // Just the event type (string)
+ new jQuery.Event( type );
+
+ event.type = type;
+ event.isTrigger = true;
+ event.exclusive = exclusive;
+ event.namespace = namespaces.join( "." );
+ event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
+ ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
+
+ // Handle a global trigger
+ if ( !elem ) {
+
+ // TODO: Stop taunting the data cache; remove global events and always attach to document
+ cache = jQuery.cache;
+ for ( i in cache ) {
+ if ( cache[ i ].events && cache[ i ].events[ type ] ) {
+ jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
+ }
+ }
+ return;
+ }
+
+ // Clean up the event in case it is being reused
+ event.result = undefined;
+ if ( !event.target ) {
+ event.target = elem;
+ }
+
+ // Clone any incoming data and prepend the event, creating the handler arg list
+ data = data != null ? jQuery.makeArray( data ) : [];
+ data.unshift( event );
+
+ // Allow special events to draw outside the lines
+ special = jQuery.event.special[ type ] || {};
+ if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
+ return;
+ }
+
+ // Determine event propagation path in advance, per W3C events spec (#9951)
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+ eventPath = [[ elem, special.bindType || type ]];
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+ bubbleType = special.delegateType || type;
+ cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
+ old = null;
+ for ( ; cur; cur = cur.parentNode ) {
+ eventPath.push([ cur, bubbleType ]);
+ old = cur;
+ }
+
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
+ if ( old && old === elem.ownerDocument ) {
+ eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
+ }
+ }
+
+ // Fire handlers on the event path
+ for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
+
+ cur = eventPath[i][0];
+ event.type = eventPath[i][1];
+
+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
+ if ( handle ) {
+ handle.apply( cur, data );
+ }
+ // Note that this is a bare JS function and not a jQuery handler
+ handle = ontype && cur[ ontype ];
+ if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {
+ event.preventDefault();
+ }
+ }
+ event.type = type;
+
+ // If nobody prevented the default action, do it now
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+ if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
+ !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
+
+ // Call a native DOM method on the target with the same name name as the event.
+ // Can't use an .isFunction() check here because IE6/7 fails that test.
+ // Don't do default actions on window, that's where global variables be (#6170)
+ // IE<9 dies on focus/blur to hidden element (#1486)
+ if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
+
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ old = elem[ ontype ];
+
+ if ( old ) {
+ elem[ ontype ] = null;
+ }
+
+ // Prevent re-triggering of the same event, since we already bubbled it above
+ jQuery.event.triggered = type;
+ elem[ type ]();
+ jQuery.event.triggered = undefined;
+
+ if ( old ) {
+ elem[ ontype ] = old;
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ dispatch: function( event ) {
+
+ // Make a writable jQuery.Event from the native event object
+ event = jQuery.event.fix( event || window.event );
+
+ var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
+ delegateCount = handlers.delegateCount,
+ args = [].slice.call( arguments, 0 ),
+ run_all = !event.exclusive && !event.namespace,
+ special = jQuery.event.special[ event.type ] || {},
+ handlerQueue = [],
+ i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related;
+
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
+ args[0] = event;
+ event.delegateTarget = this;
+
+ // Call the preDispatch hook for the mapped type, and let it bail if desired
+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+ return;
+ }
+
+ // Determine handlers that should run if there are delegated events
+ // Avoid non-left-click bubbling in Firefox (#3861)
+ if ( delegateCount && !(event.button && event.type === "click") ) {
+
+ // Pregenerate a single jQuery object for reuse with .is()
+ jqcur = jQuery(this);
+ jqcur.context = this.ownerDocument || this;
+
+ for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
+
+ // Don't process events on disabled elements (#6911, #8165)
+ if ( cur.disabled !== true ) {
+ selMatch = {};
+ matches = [];
+ jqcur[0] = cur;
+ for ( i = 0; i < delegateCount; i++ ) {
+ handleObj = handlers[ i ];
+ sel = handleObj.selector;
+
+ if ( selMatch[ sel ] === undefined ) {
+ selMatch[ sel ] = (
+ handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )
+ );
+ }
+ if ( selMatch[ sel ] ) {
+ matches.push( handleObj );
+ }
+ }
+ if ( matches.length ) {
+ handlerQueue.push({ elem: cur, matches: matches });
+ }
+ }
+ }
+ }
+
+ // Add the remaining (directly-bound) handlers
+ if ( handlers.length > delegateCount ) {
+ handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
+ }
+
+ // Run delegates first; they may want to stop propagation beneath us
+ for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
+ matched = handlerQueue[ i ];
+ event.currentTarget = matched.elem;
+
+ for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
+ handleObj = matched.matches[ j ];
+
+ // Triggered event must either 1) be non-exclusive and have no namespace, or
+ // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+ if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
+
+ event.data = handleObj.data;
+ event.handleObj = handleObj;
+
+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+ .apply( matched.elem, args );
+
+ if ( ret !== undefined ) {
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+ }
+ }
+
+ // Call the postDispatch hook for the mapped type
+ if ( special.postDispatch ) {
+ special.postDispatch.call( this, event );
+ }
+
+ return event.result;
+ },
+
+ // Includes some event props shared by KeyEvent and MouseEvent
+ // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
+ props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+ fixHooks: {},
+
+ keyHooks: {
+ props: "char charCode key keyCode".split(" "),
+ filter: function( event, original ) {
+
+ // Add which for key events
+ if ( event.which == null ) {
+ event.which = original.charCode != null ? original.charCode : original.keyCode;
+ }
+
+ return event;
+ }
+ },
+
+ mouseHooks: {
+ props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+ filter: function( event, original ) {
+ var eventDoc, doc, body,
+ button = original.button,
+ fromElement = original.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && original.clientX != null ) {
+ eventDoc = event.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+
+ event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && fromElement ) {
+ event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && button !== undefined ) {
+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+ }
+
+ return event;
+ }
+ },
+
+ fix: function( event ) {
+ if ( event[ jQuery.expando ] ) {
+ return event;
+ }
+
+ // Create a writable copy of the event object and normalize some properties
+ var i, prop,
+ originalEvent = event,
+ fixHook = jQuery.event.fixHooks[ event.type ] || {},
+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+ event = jQuery.Event( originalEvent );
+
+ for ( i = copy.length; i; ) {
+ prop = copy[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
+ if ( !event.target ) {
+ event.target = originalEvent.srcElement || document;
+ }
+
+ // Target should not be a text node (#504, Safari)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)
+ if ( event.metaKey === undefined ) {
+ event.metaKey = event.ctrlKey;
+ }
+
+ return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
+ },
+
+ special: {
+ ready: {
+ // Make sure the ready event is setup
+ setup: jQuery.bindReady
+ },
+
+ load: {
+ // Prevent triggered image.load events from bubbling to window.load
+ noBubble: true
+ },
+
+ focus: {
+ delegateType: "focusin"
+ },
+ blur: {
+ delegateType: "focusout"
+ },
+
+ beforeunload: {
+ setup: function( data, namespaces, eventHandle ) {
+ // We only want to do this special case on windows
+ if ( jQuery.isWindow( this ) ) {
+ this.onbeforeunload = eventHandle;
+ }
+ },
+
+ teardown: function( namespaces, eventHandle ) {
+ if ( this.onbeforeunload === eventHandle ) {
+ this.onbeforeunload = null;
+ }
+ }
+ }
+ },
+
+ simulate: function( type, elem, event, bubble ) {
+ // Piggyback on a donor event to simulate a different one.
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
+ // simulated event prevents default then we do the same on the donor.
+ var e = jQuery.extend(
+ new jQuery.Event(),
+ event,
+ { type: type,
+ isSimulated: true,
+ originalEvent: {}
+ }
+ );
+ if ( bubble ) {
+ jQuery.event.trigger( e, null, elem );
+ } else {
+ jQuery.event.dispatch.call( elem, e );
+ }
+ if ( e.isDefaultPrevented() ) {
+ event.preventDefault();
+ }
+ }
+};
+
+// Some plugins are using, but it's undocumented/deprecated and will be removed.
+// The 1.7 special event interface should provide all the hooks needed now.
+jQuery.event.handle = jQuery.event.dispatch;
+
+jQuery.removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+ if ( elem.removeEventListener ) {
+ elem.removeEventListener( type, handle, false );
+ }
+ } :
+ function( elem, type, handle ) {
+ if ( elem.detachEvent ) {
+ elem.detachEvent( "on" + type, handle );
+ }
+ };
+
+jQuery.Event = function( src, props ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !(this instanceof jQuery.Event) ) {
+ return new jQuery.Event( src, props );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+
+ // Events bubbling up the document may have been marked as prevented
+ // by a handler lower down the tree; reflect the correct value.
+ this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+ src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
+
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // Put explicitly provided properties onto the event object
+ if ( props ) {
+ jQuery.extend( this, props );
+ }
+
+ // Create a timestamp if incoming event doesn't have one
+ this.timeStamp = src && src.timeStamp || jQuery.now();
+
+ // Mark it as fixed
+ this[ jQuery.expando ] = true;
+};
+
+function returnFalse() {
+ return false;
+}
+function returnTrue() {
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+
+ // otherwise set the returnValue property of the original event to false (IE)
+ } else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ delegateType: fix,
+ bindType: fix,
+
+ handle: function( event ) {
+ var target = this,
+ related = event.relatedTarget,
+ handleObj = event.handleObj,
+ selector = handleObj.selector,
+ ret;
+
+ // For mousenter/leave call the handler if related is outside the target.
+ // NB: No relatedTarget if the mouse left/entered the browser window
+ if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+ event.type = handleObj.origType;
+ ret = handleObj.handler.apply( this, arguments );
+ event.type = fix;
+ }
+ return ret;
+ }
+ };
+});
+
+// IE submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+ jQuery.event.special.submit = {
+ setup: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Lazy-add a submit handler when a descendant form may potentially be submitted
+ jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+ // Node name check avoids a VML-related crash in IE (#9807)
+ var elem = e.target,
+ form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+ if ( form && !form._submit_attached ) {
+ jQuery.event.add( form, "submit._submit", function( event ) {
+ event._submit_bubble = true;
+ });
+ form._submit_attached = true;
+ }
+ });
+ // return undefined since we don't need an event listener
+ },
+
+ postDispatch: function( event ) {
+ // If form was submitted by the user, bubble the event up the tree
+ if ( event._submit_bubble ) {
+ delete event._submit_bubble;
+ if ( this.parentNode && !event.isTrigger ) {
+ jQuery.event.simulate( "submit", this.parentNode, event, true );
+ }
+ }
+ },
+
+ teardown: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+ jQuery.event.remove( this, "._submit" );
+ }
+ };
+}
+
+// IE change delegation and checkbox/radio fix
+if ( !jQuery.support.changeBubbles ) {
+
+ jQuery.event.special.change = {
+
+ setup: function() {
+
+ if ( rformElems.test( this.nodeName ) ) {
+ // IE doesn't fire change on a check/radio until blur; trigger it on click
+ // after a propertychange. Eat the blur-change in special.change.handle.
+ // This still fires onchange a second time for check/radio after blur.
+ if ( this.type === "checkbox" || this.type === "radio" ) {
+ jQuery.event.add( this, "propertychange._change", function( event ) {
+ if ( event.originalEvent.propertyName === "checked" ) {
+ this._just_changed = true;
+ }
+ });
+ jQuery.event.add( this, "click._change", function( event ) {
+ if ( this._just_changed && !event.isTrigger ) {
+ this._just_changed = false;
+ jQuery.event.simulate( "change", this, event, true );
+ }
+ });
+ }
+ return false;
+ }
+ // Delegated event; lazy-add a change handler on descendant inputs
+ jQuery.event.add( this, "beforeactivate._change", function( e ) {
+ var elem = e.target;
+
+ if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) {
+ jQuery.event.add( elem, "change._change", function( event ) {
+ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+ jQuery.event.simulate( "change", this.parentNode, event, true );
+ }
+ });
+ elem._change_attached = true;
+ }
+ });
+ },
+
+ handle: function( event ) {
+ var elem = event.target;
+
+ // Swallow native change events from checkbox/radio, we already triggered them above
+ if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+ return event.handleObj.handler.apply( this, arguments );
+ }
+ },
+
+ teardown: function() {
+ jQuery.event.remove( this, "._change" );
+
+ return rformElems.test( this.nodeName );
+ }
+ };
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+ // Attach a single capturing handler while someone wants focusin/focusout
+ var attaches = 0,
+ handler = function( event ) {
+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+ };
+
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ if ( attaches++ === 0 ) {
+ document.addEventListener( orig, handler, true );
+ }
+ },
+ teardown: function() {
+ if ( --attaches === 0 ) {
+ document.removeEventListener( orig, handler, true );
+ }
+ }
+ };
+ });
+}
+
+jQuery.fn.extend({
+
+ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+ var origFn, type;
+
+ // Types can be a map of types/handlers
+ if ( typeof types === "object" ) {
+ // ( types-Object, selector, data )
+ if ( typeof selector !== "string" ) { // && selector != null
+ // ( types-Object, data )
+ data = data || selector;
+ selector = undefined;
+ }
+ for ( type in types ) {
+ this.on( type, selector, data, types[ type ], one );
+ }
+ return this;
+ }
+
+ if ( data == null && fn == null ) {
+ // ( types, fn )
+ fn = selector;
+ data = selector = undefined;
+ } else if ( fn == null ) {
+ if ( typeof selector === "string" ) {
+ // ( types, selector, fn )
+ fn = data;
+ data = undefined;
+ } else {
+ // ( types, data, fn )
+ fn = data;
+ data = selector;
+ selector = undefined;
+ }
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ } else if ( !fn ) {
+ return this;
+ }
+
+ if ( one === 1 ) {
+ origFn = fn;
+ fn = function( event ) {
+ // Can use an empty set, since event contains the info
+ jQuery().off( event );
+ return origFn.apply( this, arguments );
+ };
+ // Use same guid so caller can remove using origFn
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+ }
+ return this.each( function() {
+ jQuery.event.add( this, types, fn, data, selector );
+ });
+ },
+ one: function( types, selector, data, fn ) {
+ return this.on( types, selector, data, fn, 1 );
+ },
+ off: function( types, selector, fn ) {
+ if ( types && types.preventDefault && types.handleObj ) {
+ // ( event ) dispatched jQuery.Event
+ var handleObj = types.handleObj;
+ jQuery( types.delegateTarget ).off(
+ handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+ handleObj.selector,
+ handleObj.handler
+ );
+ return this;
+ }
+ if ( typeof types === "object" ) {
+ // ( types-object [, selector] )
+ for ( var type in types ) {
+ this.off( type, selector, types[ type ] );
+ }
+ return this;
+ }
+ if ( selector === false || typeof selector === "function" ) {
+ // ( types [, fn] )
+ fn = selector;
+ selector = undefined;
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ }
+ return this.each(function() {
+ jQuery.event.remove( this, types, fn, selector );
+ });
+ },
+
+ bind: function( types, data, fn ) {
+ return this.on( types, null, data, fn );
+ },
+ unbind: function( types, fn ) {
+ return this.off( types, null, fn );
+ },
+
+ live: function( types, data, fn ) {
+ jQuery( this.context ).on( types, this.selector, data, fn );
+ return this;
+ },
+ die: function( types, fn ) {
+ jQuery( this.context ).off( types, this.selector || "**", fn );
+ return this;
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.on( types, selector, data, fn );
+ },
+ undelegate: function( selector, types, fn ) {
+ // ( namespace ) or ( selector, types [, fn] )
+ return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn );
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+ triggerHandler: function( type, data ) {
+ if ( this[0] ) {
+ return jQuery.event.trigger( type, data, this[0], true );
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments,
+ guid = fn.guid || jQuery.guid++,
+ i = 0,
+ toggler = function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ };
+
+ // link all the functions, so any of them can unbind this click handler
+ toggler.guid = guid;
+ while ( i < args.length ) {
+ args[ i++ ].guid = guid;
+ }
+
+ return this.click( toggler );
+ },
+
+ hover: function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ }
+});
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( data, fn ) {
+ if ( fn == null ) {
+ fn = data;
+ data = null;
+ }
+
+ return arguments.length > 0 ?
+ this.on( name, null, data, fn ) :
+ this.trigger( name );
+ };
+
+ if ( jQuery.attrFn ) {
+ jQuery.attrFn[ name ] = true;
+ }
+
+ if ( rkeyEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
+ }
+
+ if ( rmouseEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
+ }
+});
+
+
+
+/*!
+ * Sizzle CSS Selector Engine
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+ expando = "sizcache" + (Math.random() + '').replace('.', ''),
+ done = 0,
+ toString = Object.prototype.toString,
+ hasDuplicate = false,
+ baseHasDuplicate = true,
+ rBackslash = /\\/g,
+ rReturn = /\r\n/g,
+ rNonWord = /\W/;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+// Thus far that includes Google Chrome.
+[0, 0].sort(function() {
+ baseHasDuplicate = false;
+ return 0;
+});
+
+var Sizzle = function( selector, context, results, seed ) {
+ results = results || [];
+ context = context || document;
+
+ var origContext = context;
+
+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+ return [];
+ }
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ var m, set, checkSet, extra, ret, cur, pop, i,
+ prune = true,
+ contextXML = Sizzle.isXML( context ),
+ parts = [],
+ soFar = selector;
+
+ // Reset the position of the chunker regexp (start from head)
+ do {
+ chunker.exec( "" );
+ m = chunker.exec( soFar );
+
+ if ( m ) {
+ soFar = m[3];
+
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = m[3];
+ break;
+ }
+ }
+ } while ( m );
+
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
+
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+ set = posProcess( parts[0] + parts[1], context, seed );
+
+ } else {
+ set = Expr.relative[ parts[0] ] ?
+ [ context ] :
+ Sizzle( parts.shift(), context );
+
+ while ( parts.length ) {
+ selector = parts.shift();
+
+ if ( Expr.relative[ selector ] ) {
+ selector += parts.shift();
+ }
+
+ set = posProcess( selector, set, seed );
+ }
+ }
+
+ } else {
+ // Take a shortcut and set the context if the root selector is an ID
+ // (but not if it'll be faster if the inner selector is an ID)
+ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+
+ ret = Sizzle.find( parts.shift(), context, contextXML );
+ context = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set )[0] :
+ ret.set[0];
+ }
+
+ if ( context ) {
+ ret = seed ?
+ { expr: parts.pop(), set: makeArray(seed) } :
+ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
+
+ set = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set ) :
+ ret.set;
+
+ if ( parts.length > 0 ) {
+ checkSet = makeArray( set );
+
+ } else {
+ prune = false;
+ }
+
+ while ( parts.length ) {
+ cur = parts.pop();
+ pop = cur;
+
+ if ( !Expr.relative[ cur ] ) {
+ cur = "";
+ } else {
+ pop = parts.pop();
+ }
+
+ if ( pop == null ) {
+ pop = context;
+ }
+
+ Expr.relative[ cur ]( checkSet, pop, contextXML );
+ }
+
+ } else {
+ checkSet = parts = [];
+ }
+ }
+
+ if ( !checkSet ) {
+ checkSet = set;
+ }
+
+ if ( !checkSet ) {
+ Sizzle.error( cur || selector );
+ }
+
+ if ( toString.call(checkSet) === "[object Array]" ) {
+ if ( !prune ) {
+ results.push.apply( results, checkSet );
+
+ } else if ( context && context.nodeType === 1 ) {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
+ results.push( set[i] );
+ }
+ }
+
+ } else {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+ results.push( set[i] );
+ }
+ }
+ }
+
+ } else {
+ makeArray( checkSet, results );
+ }
+
+ if ( extra ) {
+ Sizzle( extra, origContext, results, seed );
+ Sizzle.uniqueSort( results );
+ }
+
+ return results;
+};
+
+Sizzle.uniqueSort = function( results ) {
+ if ( sortOrder ) {
+ hasDuplicate = baseHasDuplicate;
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ for ( var i = 1; i < results.length; i++ ) {
+ if ( results[i] === results[ i - 1 ] ) {
+ results.splice( i--, 1 );
+ }
+ }
+ }
+ }
+
+ return results;
+};
+
+Sizzle.matches = function( expr, set ) {
+ return Sizzle( expr, null, null, set );
+};
+
+Sizzle.matchesSelector = function( node, expr ) {
+ return Sizzle( expr, null, null, [node] ).length > 0;
+};
+
+Sizzle.find = function( expr, context, isXML ) {
+ var set, i, len, match, type, left;
+
+ if ( !expr ) {
+ return [];
+ }
+
+ for ( i = 0, len = Expr.order.length; i < len; i++ ) {
+ type = Expr.order[i];
+
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+ left = match[1];
+ match.splice( 1, 1 );
+
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
+ match[1] = (match[1] || "").replace( rBackslash, "" );
+ set = Expr.find[ type ]( match, context, isXML );
+
+ if ( set != null ) {
+ expr = expr.replace( Expr.match[ type ], "" );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !set ) {
+ set = typeof context.getElementsByTagName !== "undefined" ?
+ context.getElementsByTagName( "*" ) :
+ [];
+ }
+
+ return { set: set, expr: expr };
+};
+
+Sizzle.filter = function( expr, set, inplace, not ) {
+ var match, anyFound,
+ type, found, item, filter, left,
+ i, pass,
+ old = expr,
+ result = [],
+ curLoop = set,
+ isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
+
+ while ( expr && set.length ) {
+ for ( type in Expr.filter ) {
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+ filter = Expr.filter[ type ];
+ left = match[1];
+
+ anyFound = false;
+
+ match.splice(1,1);
+
+ if ( left.substr( left.length - 1 ) === "\\" ) {
+ continue;
+ }
+
+ if ( curLoop === result ) {
+ result = [];
+ }
+
+ if ( Expr.preFilter[ type ] ) {
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
+
+ if ( !match ) {
+ anyFound = found = true;
+
+ } else if ( match === true ) {
+ continue;
+ }
+ }
+
+ if ( match ) {
+ for ( i = 0; (item = curLoop[i]) != null; i++ ) {
+ if ( item ) {
+ found = filter( item, match, i, curLoop );
+ pass = not ^ found;
+
+ if ( inplace && found != null ) {
+ if ( pass ) {
+ anyFound = true;
+
+ } else {
+ curLoop[i] = false;
+ }
+
+ } else if ( pass ) {
+ result.push( item );
+ anyFound = true;
+ }
+ }
+ }
+ }
+
+ if ( found !== undefined ) {
+ if ( !inplace ) {
+ curLoop = result;
+ }
+
+ expr = expr.replace( Expr.match[ type ], "" );
+
+ if ( !anyFound ) {
+ return [];
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Improper expression
+ if ( expr === old ) {
+ if ( anyFound == null ) {
+ Sizzle.error( expr );
+
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Utility function for retreiving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+var getText = Sizzle.getText = function( elem ) {
+ var i, node,
+ nodeType = elem.nodeType,
+ ret = "";
+
+ if ( nodeType ) {
+ if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ // Use textContent || innerText for elements
+ if ( typeof elem.textContent === 'string' ) {
+ return elem.textContent;
+ } else if ( typeof elem.innerText === 'string' ) {
+ // Replace IE's carriage returns
+ return elem.innerText.replace( rReturn, '' );
+ } else {
+ // Traverse it's children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ } else {
+
+ // If no nodeType, this is expected to be an array
+ for ( i = 0; (node = elem[i]); i++ ) {
+ // Do not traverse comment nodes
+ if ( node.nodeType !== 8 ) {
+ ret += getText( node );
+ }
+ }
+ }
+ return ret;
+};
+
+var Expr = Sizzle.selectors = {
+ order: [ "ID", "NAME", "TAG" ],
+
+ match: {
+ ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
+ CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+ },
+
+ leftMatch: {},
+
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+
+ attrHandle: {
+ href: function( elem ) {
+ return elem.getAttribute( "href" );
+ },
+ type: function( elem ) {
+ return elem.getAttribute( "type" );
+ }
+ },
+
+ relative: {
+ "+": function(checkSet, part){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !rNonWord.test( part ),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag ) {
+ part = part.toLowerCase();
+ }
+
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+ if ( (elem = checkSet[i]) ) {
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+
+ ">": function( checkSet, part ) {
+ var elem,
+ isPartStr = typeof part === "string",
+ i = 0,
+ l = checkSet.length;
+
+ if ( isPartStr && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+ }
+ }
+
+ } else {
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ checkSet[i] = isPartStr ?
+ elem.parentNode :
+ elem.parentNode === part;
+ }
+ }
+
+ if ( isPartStr ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ }
+ },
+
+ "": function(checkSet, part, isXML){
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
+ },
+
+ "~": function( checkSet, part, isXML ) {
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
+ }
+ },
+
+ find: {
+ ID: function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ },
+
+ NAME: function( match, context ) {
+ if ( typeof context.getElementsByName !== "undefined" ) {
+ var ret = [],
+ results = context.getElementsByName( match[1] );
+
+ for ( var i = 0, l = results.length; i < l; i++ ) {
+ if ( results[i].getAttribute("name") === match[1] ) {
+ ret.push( results[i] );
+ }
+ }
+
+ return ret.length === 0 ? null : ret;
+ }
+ },
+
+ TAG: function( match, context ) {
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
+ return context.getElementsByTagName( match[1] );
+ }
+ }
+ },
+ preFilter: {
+ CLASS: function( match, curLoop, inplace, result, not, isXML ) {
+ match = " " + match[1].replace( rBackslash, "" ) + " ";
+
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
+ if ( !inplace ) {
+ result.push( elem );
+ }
+
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
+ }
+ }
+
+ return false;
+ },
+
+ ID: function( match ) {
+ return match[1].replace( rBackslash, "" );
+ },
+
+ TAG: function( match, curLoop ) {
+ return match[1].replace( rBackslash, "" ).toLowerCase();
+ },
+
+ CHILD: function( match ) {
+ if ( match[1] === "nth" ) {
+ if ( !match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ match[2] = match[2].replace(/^\+|\s*/g, '');
+
+ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+ var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
+ match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ // calculate the numbers (first)n+(last) including if they are negative
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+ else if ( match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // TODO: Move to normal caching system
+ match[0] = done++;
+
+ return match;
+ },
+
+ ATTR: function( match, curLoop, inplace, result, not, isXML ) {
+ var name = match[1] = match[1].replace( rBackslash, "" );
+
+ if ( !isXML && Expr.attrMap[name] ) {
+ match[1] = Expr.attrMap[name];
+ }
+
+ // Handle if an un-quoted value was used
+ match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
+
+ if ( match[2] === "~=" ) {
+ match[4] = " " + match[4] + " ";
+ }
+
+ return match;
+ },
+
+ PSEUDO: function( match, curLoop, inplace, result, not ) {
+ if ( match[1] === "not" ) {
+ // If we're dealing with a complex expression, or a simple one
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
+ match[3] = Sizzle(match[3], null, null, curLoop);
+
+ } else {
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+
+ if ( !inplace ) {
+ result.push.apply( result, ret );
+ }
+
+ return false;
+ }
+
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+ return true;
+ }
+
+ return match;
+ },
+
+ POS: function( match ) {
+ match.unshift( true );
+
+ return match;
+ }
+ },
+
+ filters: {
+ enabled: function( elem ) {
+ return elem.disabled === false && elem.type !== "hidden";
+ },
+
+ disabled: function( elem ) {
+ return elem.disabled === true;
+ },
+
+ checked: function( elem ) {
+ return elem.checked === true;
+ },
+
+ selected: function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ parent: function( elem ) {
+ return !!elem.firstChild;
+ },
+
+ empty: function( elem ) {
+ return !elem.firstChild;
+ },
+
+ has: function( elem, i, match ) {
+ return !!Sizzle( match[3], elem ).length;
+ },
+
+ header: function( elem ) {
+ return (/h\d/i).test( elem.nodeName );
+ },
+
+ text: function( elem ) {
+ var attr = elem.getAttribute( "type" ), type = elem.type;
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
+ },
+
+ radio: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
+ },
+
+ checkbox: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
+ },
+
+ file: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
+ },
+
+ password: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
+ },
+
+ submit: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && "submit" === elem.type;
+ },
+
+ image: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
+ },
+
+ reset: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && "reset" === elem.type;
+ },
+
+ button: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && "button" === elem.type || name === "button";
+ },
+
+ input: function( elem ) {
+ return (/input|select|textarea|button/i).test( elem.nodeName );
+ },
+
+ focus: function( elem ) {
+ return elem === elem.ownerDocument.activeElement;
+ }
+ },
+ setFilters: {
+ first: function( elem, i ) {
+ return i === 0;
+ },
+
+ last: function( elem, i, match, array ) {
+ return i === array.length - 1;
+ },
+
+ even: function( elem, i ) {
+ return i % 2 === 0;
+ },
+
+ odd: function( elem, i ) {
+ return i % 2 === 1;
+ },
+
+ lt: function( elem, i, match ) {
+ return i < match[3] - 0;
+ },
+
+ gt: function( elem, i, match ) {
+ return i > match[3] - 0;
+ },
+
+ nth: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ },
+
+ eq: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ }
+ },
+ filter: {
+ PSEUDO: function( elem, match, i, array ) {
+ var name = match[1],
+ filter = Expr.filters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+
+ } else if ( name === "contains" ) {
+ return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
+
+ } else if ( name === "not" ) {
+ var not = match[3];
+
+ for ( var j = 0, l = not.length; j < l; j++ ) {
+ if ( not[j] === elem ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ } else {
+ Sizzle.error( name );
+ }
+ },
+
+ CHILD: function( elem, match ) {
+ var first, last,
+ doneName, parent, cache,
+ count, diff,
+ type = match[1],
+ node = elem;
+
+ switch ( type ) {
+ case "only":
+ case "first":
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ if ( type === "first" ) {
+ return true;
+ }
+
+ node = elem;
+
+ /* falls through */
+ case "last":
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ case "nth":
+ first = match[2];
+ last = match[3];
+
+ if ( first === 1 && last === 0 ) {
+ return true;
+ }
+
+ doneName = match[0];
+ parent = elem.parentNode;
+
+ if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
+ count = 0;
+
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+
+ parent[ expando ] = doneName;
+ }
+
+ diff = elem.nodeIndex - last;
+
+ if ( first === 0 ) {
+ return diff === 0;
+
+ } else {
+ return ( diff % first === 0 && diff / first >= 0 );
+ }
+ }
+ },
+
+ ID: function( elem, match ) {
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
+ },
+
+ TAG: function( elem, match ) {
+ return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;
+ },
+
+ CLASS: function( elem, match ) {
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
+ .indexOf( match ) > -1;
+ },
+
+ ATTR: function( elem, match ) {
+ var name = match[1],
+ result = Sizzle.attr ?
+ Sizzle.attr( elem, name ) :
+ Expr.attrHandle[ name ] ?
+ Expr.attrHandle[ name ]( elem ) :
+ elem[ name ] != null ?
+ elem[ name ] :
+ elem.getAttribute( name ),
+ value = result + "",
+ type = match[2],
+ check = match[4];
+
+ return result == null ?
+ type === "!=" :
+ !type && Sizzle.attr ?
+ result != null :
+ type === "=" ?
+ value === check :
+ type === "*=" ?
+ value.indexOf(check) >= 0 :
+ type === "~=" ?
+ (" " + value + " ").indexOf(check) >= 0 :
+ !check ?
+ value && result !== false :
+ type === "!=" ?
+ value !== check :
+ type === "^=" ?
+ value.indexOf(check) === 0 :
+ type === "$=" ?
+ value.substr(value.length - check.length) === check :
+ type === "|=" ?
+ value === check || value.substr(0, check.length + 1) === check + "-" :
+ false;
+ },
+
+ POS: function( elem, match, i, array ) {
+ var name = match[2],
+ filter = Expr.setFilters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ }
+ }
+ }
+};
+
+var origPOS = Expr.match.POS,
+ fescape = function(all, num){
+ return "\\" + (num - 0 + 1);
+ };
+
+for ( var type in Expr.match ) {
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
+}
+// Expose origPOS
+// "global" as in regardless of relation to brackets/parens
+Expr.match.globalPOS = origPOS;
+
+var makeArray = function( array, results ) {
+ array = Array.prototype.slice.call( array, 0 );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch( e ) {
+ makeArray = function( array, results ) {
+ var i = 0,
+ ret = results || [];
+
+ if ( toString.call(array) === "[object Array]" ) {
+ Array.prototype.push.apply( ret, array );
+
+ } else {
+ if ( typeof array.length === "number" ) {
+ for ( var l = array.length; i < l; i++ ) {
+ ret.push( array[i] );
+ }
+
+ } else {
+ for ( ; array[i]; i++ ) {
+ ret.push( array[i] );
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var sortOrder, siblingCheck;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+ return a.compareDocumentPosition ? -1 : 1;
+ }
+
+ return a.compareDocumentPosition(b) & 4 ? -1 : 1;
+ };
+
+} else {
+ sortOrder = function( a, b ) {
+ // The nodes are identical, we can exit early
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // Fallback to using sourceIndex (in IE) if it's available on both nodes
+ } else if ( a.sourceIndex && b.sourceIndex ) {
+ return a.sourceIndex - b.sourceIndex;
+ }
+
+ var al, bl,
+ ap = [],
+ bp = [],
+ aup = a.parentNode,
+ bup = b.parentNode,
+ cur = aup;
+
+ // If the nodes are siblings (or identical) we can do a quick check
+ if ( aup === bup ) {
+ return siblingCheck( a, b );
+
+ // If no parents were found then the nodes are disconnected
+ } else if ( !aup ) {
+ return -1;
+
+ } else if ( !bup ) {
+ return 1;
+ }
+
+ // Otherwise they're somewhere else in the tree so we need
+ // to build up a full list of the parentNodes for comparison
+ while ( cur ) {
+ ap.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ cur = bup;
+
+ while ( cur ) {
+ bp.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ al = ap.length;
+ bl = bp.length;
+
+ // Start walking down the tree looking for a discrepancy
+ for ( var i = 0; i < al && i < bl; i++ ) {
+ if ( ap[i] !== bp[i] ) {
+ return siblingCheck( ap[i], bp[i] );
+ }
+ }
+
+ // We ended someplace up the tree so do a sibling check
+ return i === al ?
+ siblingCheck( a, bp[i], -1 ) :
+ siblingCheck( ap[i], b, 1 );
+ };
+
+ siblingCheck = function( a, b, ret ) {
+ if ( a === b ) {
+ return ret;
+ }
+
+ var cur = a.nextSibling;
+
+ while ( cur ) {
+ if ( cur === b ) {
+ return -1;
+ }
+
+ cur = cur.nextSibling;
+ }
+
+ return 1;
+ };
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+ // We're going to inject a fake input element with a specified name
+ var form = document.createElement("div"),
+ id = "script" + (new Date()).getTime(),
+ root = document.documentElement;
+
+ form.innerHTML = "<a name='" + id + "'/>";
+
+ // Inject it into the root element, check its status, and remove it quickly
+ root.insertBefore( form, root.firstChild );
+
+ // The workaround has to do additional checks after a getElementById
+ // Which slows things down for other browsers (hence the branching)
+ if ( document.getElementById( id ) ) {
+ Expr.find.ID = function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+
+ return m ?
+ m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
+ [m] :
+ undefined :
+ [];
+ }
+ };
+
+ Expr.filter.ID = function( elem, match ) {
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+
+ return elem.nodeType === 1 && node && node.nodeValue === match;
+ };
+ }
+
+ root.removeChild( form );
+
+ // release memory in IE
+ root = form = null;
+})();
+
+(function(){
+ // Check to see if the browser returns only elements
+ // when doing getElementsByTagName("*")
+
+ // Create a fake element
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ // Make sure no comments are found
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function( match, context ) {
+ var results = context.getElementsByTagName( match[1] );
+
+ // Filter out possible comments
+ if ( match[1] === "*" ) {
+ var tmp = [];
+
+ for ( var i = 0; results[i]; i++ ) {
+ if ( results[i].nodeType === 1 ) {
+ tmp.push( results[i] );
+ }
+ }
+
+ results = tmp;
+ }
+
+ return results;
+ };
+ }
+
+ // Check to see if an attribute returns normalized href attributes
+ div.innerHTML = "<a href='#'></a>";
+
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
+
+ Expr.attrHandle.href = function( elem ) {
+ return elem.getAttribute( "href", 2 );
+ };
+ }
+
+ // release memory in IE
+ div = null;
+})();
+
+if ( document.querySelectorAll ) {
+ (function(){
+ var oldSizzle = Sizzle,
+ div = document.createElement("div"),
+ id = "__sizzle__";
+
+ div.innerHTML = "<p class='TEST'></p>";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
+
+ Sizzle = function( query, context, extra, seed ) {
+ context = context || document;
+
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && !Sizzle.isXML(context) ) {
+ // See if we find a selector to speed up
+ var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
+
+ if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
+ // Speed-up: Sizzle("TAG")
+ if ( match[1] ) {
+ return makeArray( context.getElementsByTagName( query ), extra );
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
+ return makeArray( context.getElementsByClassName( match[2] ), extra );
+ }
+ }
+
+ if ( context.nodeType === 9 ) {
+ // Speed-up: Sizzle("body")
+ // The body element only exists once, optimize finding it
+ if ( query === "body" && context.body ) {
+ return makeArray( [ context.body ], extra );
+
+ // Speed-up: Sizzle("#ID")
+ } else if ( match && match[3] ) {
+ var elem = context.getElementById( match[3] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id === match[3] ) {
+ return makeArray( [ elem ], extra );
+ }
+
+ } else {
+ return makeArray( [], extra );
+ }
+ }
+
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(qsaError) {}
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ var oldContext = context,
+ old = context.getAttribute( "id" ),
+ nid = old || id,
+ hasParent = context.parentNode,
+ relativeHierarchySelector = /^\s*[+~]/.test( query );
+
+ if ( !old ) {
+ context.setAttribute( "id", nid );
+ } else {
+ nid = nid.replace( /'/g, "\\$&" );
+ }
+ if ( relativeHierarchySelector && hasParent ) {
+ context = context.parentNode;
+ }
+
+ try {
+ if ( !relativeHierarchySelector || hasParent ) {
+ return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
+ }
+
+ } catch(pseudoError) {
+ } finally {
+ if ( !old ) {
+ oldContext.removeAttribute( "id" );
+ }
+ }
+ }
+ }
+
+ return oldSizzle(query, context, extra, seed);
+ };
+
+ for ( var prop in oldSizzle ) {
+ Sizzle[ prop ] = oldSizzle[ prop ];
+ }
+
+ // release memory in IE
+ div = null;
+ })();
+}
+
+(function(){
+ var html = document.documentElement,
+ matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
+
+ if ( matches ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9 fails this)
+ var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
+ pseudoWorks = false;
+
+ try {
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( document.documentElement, "[test!='']:sizzle" );
+
+ } catch( pseudoError ) {
+ pseudoWorks = true;
+ }
+
+ Sizzle.matchesSelector = function( node, expr ) {
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
+
+ if ( !Sizzle.isXML( node ) ) {
+ try {
+ if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
+ var ret = matches.call( node, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || !disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9, so check for that
+ node.document && node.document.nodeType !== 11 ) {
+ return ret;
+ }
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle(expr, null, null, [node]).length > 0;
+ };
+ }
+})();
+
+(function(){
+ var div = document.createElement("div");
+
+ div.innerHTML = "<div class='test e'></div><div class='test'></div>";
+
+ // Opera can't find a second classname (in 9.6)
+ // Also, make sure that getElementsByClassName actually exists
+ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+ return;
+ }
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ div.lastChild.className = "e";
+
+ if ( div.getElementsByClassName("e").length === 1 ) {
+ return;
+ }
+
+ Expr.order.splice(1, 0, "CLASS");
+ Expr.find.CLASS = function( match, context, isXML ) {
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
+ return context.getElementsByClassName(match[1]);
+ }
+ };
+
+ // release memory in IE
+ div = null;
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem[ expando ] === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 && !isXML ){
+ elem[ expando ] = doneName;
+ elem.sizset = i;
+ }
+
+ if ( elem.nodeName.toLowerCase() === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem[ expando ] === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 ) {
+ if ( !isXML ) {
+ elem[ expando ] = doneName;
+ elem.sizset = i;
+ }
+
+ if ( typeof cur !== "string" ) {
+ if ( elem === cur ) {
+ match = true;
+ break;
+ }
+
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+ match = elem;
+ break;
+ }
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+if ( document.documentElement.contains ) {
+ Sizzle.contains = function( a, b ) {
+ return a !== b && (a.contains ? a.contains(b) : true);
+ };
+
+} else if ( document.documentElement.compareDocumentPosition ) {
+ Sizzle.contains = function( a, b ) {
+ return !!(a.compareDocumentPosition(b) & 16);
+ };
+
+} else {
+ Sizzle.contains = function() {
+ return false;
+ };
+}
+
+Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function( selector, context, seed ) {
+ var match,
+ tmpSet = [],
+ later = "",
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+ later += match[0];
+ selector = selector.replace( Expr.match.PSEUDO, "" );
+ }
+
+ selector = Expr.relative[selector] ? selector + "*" : selector;
+
+ for ( var i = 0, l = root.length; i < l; i++ ) {
+ Sizzle( selector, root[i], tmpSet, seed );
+ }
+
+ return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+// Override sizzle attribute retrieval
+Sizzle.attr = jQuery.attr;
+Sizzle.selectors.attrMap = {};
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})();
+
+
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+ // Note: This RegExp should be improved, or likely pulled from Sizzle
+ rmultiselector = /,/,
+ isSimple = /^.[^:#\[\.,]*$/,
+ slice = Array.prototype.slice,
+ POS = jQuery.expr.match.globalPOS,
+ // methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var self = this,
+ i, l;
+
+ if ( typeof selector !== "string" ) {
+ return jQuery( selector ).filter(function() {
+ for ( i = 0, l = self.length; i < l; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ });
+ }
+
+ var ret = this.pushStack( "", "find", selector ),
+ length, n, r;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ length = ret.length;
+ jQuery.find( selector, this[i], ret );
+
+ if ( i > 0 ) {
+ // Make sure that the results are unique
+ for ( n = length; n < ret.length; n++ ) {
+ for ( r = 0; r < length; r++ ) {
+ if ( ret[r] === ret[n] ) {
+ ret.splice(n--, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ has: function( target ) {
+ var targets = jQuery( target );
+ return this.filter(function() {
+ for ( var i = 0, l = targets.length; i < l; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false), "not", selector);
+ },
+
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true), "filter", selector );
+ },
+
+ is: function( selector ) {
+ return !!selector && (
+ typeof selector === "string" ?
+ // If this is a positional selector, check membership in the returned set
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
+ POS.test( selector ) ?
+ jQuery( selector, this.context ).index( this[0] ) >= 0 :
+ jQuery.filter( selector, this ).length > 0 :
+ this.filter( selector ).length > 0 );
+ },
+
+ closest: function( selectors, context ) {
+ var ret = [], i, l, cur = this[0];
+
+ // Array (deprecated as of jQuery 1.7)
+ if ( jQuery.isArray( selectors ) ) {
+ var level = 1;
+
+ while ( cur && cur.ownerDocument && cur !== context ) {
+ for ( i = 0; i < selectors.length; i++ ) {
+
+ if ( jQuery( cur ).is( selectors[ i ] ) ) {
+ ret.push({ selector: selectors[ i ], elem: cur, level: level });
+ }
+ }
+
+ cur = cur.parentNode;
+ level++;
+ }
+
+ return ret;
+ }
+
+ // String
+ var pos = POS.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ cur = this[i];
+
+ while ( cur ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+ ret.push( cur );
+ break;
+
+ } else {
+ cur = cur.parentNode;
+ if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
+ break;
+ }
+ }
+ }
+ }
+
+ ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
+
+ return this.pushStack( ret, "closest", selectors );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+
+ // No argument, return index in parent
+ if ( !elem ) {
+ return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
+ }
+
+ // index in selector
+ if ( typeof elem === "string" ) {
+ return jQuery.inArray( this[0], jQuery( elem ) );
+ }
+
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ var set = typeof selector === "string" ?
+ jQuery( selector, context ) :
+ jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+ all = jQuery.merge( this.get(), set );
+
+ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+ all :
+ jQuery.unique( all ) );
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+ return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return jQuery.nth( elem, 2, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return jQuery.nth( elem, 2, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.makeArray( elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until );
+
+ if ( !runtil.test( name ) ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
+
+ if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+
+ return this.pushStack( ret, name, slice.call( arguments ).join(",") );
+ };
+});
+
+jQuery.extend({
+ filter: function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 ?
+ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+ jQuery.find.matches(expr, elems);
+ },
+
+ dir: function( elem, dir, until ) {
+ var matched = [],
+ cur = elem[ dir ];
+
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ nth: function( cur, result, dir, elem ) {
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] ) {
+ if ( cur.nodeType === 1 && ++num === result ) {
+ break;
+ }
+ }
+
+ return cur;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+
+ // Can't pass null or undefined to indexOf in Firefox 4
+ // Set to 0 to skip string check
+ qualifier = qualifier || 0;
+
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ var retVal = !!qualifier.call( elem, i, elem );
+ return retVal === keep;
+ });
+
+ } else if ( qualifier.nodeType ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return ( elem === qualifier ) === keep;
+ });
+
+ } else if ( typeof qualifier === "string" ) {
+ var filtered = jQuery.grep(elements, function( elem ) {
+ return elem.nodeType === 1;
+ });
+
+ if ( isSimple.test( qualifier ) ) {
+ return jQuery.filter(qualifier, filtered, !keep);
+ } else {
+ qualifier = jQuery.filter( qualifier, filtered );
+ }
+ }
+
+ return jQuery.grep(elements, function( elem, i ) {
+ return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
+ });
+}
+
+
+
+
+function createSafeFragment( document ) {
+ var list = nodeNames.split( "|" ),
+ safeFrag = document.createDocumentFragment();
+
+ if ( safeFrag.createElement ) {
+ while ( list.length ) {
+ safeFrag.createElement(
+ list.pop()
+ );
+ }
+ }
+ return safeFrag;
+}
+
+var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
+ "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+ rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+ rleadingWhitespace = /^\s+/,
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
+ rtagName = /<([\w:]+)/,
+ rtbody = /<tbody/i,
+ rhtml = /<|&#?\w+;/,
+ rnoInnerhtml = /<(?:script|style)/i,
+ rnocache = /<(?:script|object|embed|option|style)/i,
+ rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
+ // checked="checked" or checked
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+ rscriptType = /\/(java|ecma)script/i,
+ rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
+ wrapMap = {
+ option: [ 1, "<select multiple='multiple'>", "</select>" ],
+ legend: [ 1, "<fieldset>", "</fieldset>" ],
+ thead: [ 1, "<table>", "</table>" ],
+ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+ col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+ area: [ 1, "<map>", "</map>" ],
+ _default: [ 0, "", "" ]
+ },
+ safeFragment = createSafeFragment( document );
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize <link> and <script> tags normally
+if ( !jQuery.support.htmlSerialize ) {
+ wrapMap._default = [ 1, "div<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+ text: function( value ) {
+ return jQuery.access( this, function( value ) {
+ return value === undefined ?
+ jQuery.text( this ) :
+ this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
+ }, null, value, arguments.length );
+ },
+
+ wrapAll: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapAll( html.call(this, i) );
+ });
+ }
+
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+ if ( this[0].parentNode ) {
+ wrap.insertBefore( this[0] );
+ }
+
+ wrap.map(function() {
+ var elem = this;
+
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+ elem = elem.firstChild;
+ }
+
+ return elem;
+ }).append( this );
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapInner( html.call(this, i) );
+ });
+ }
+
+ return this.each(function() {
+ var self = jQuery( this ),
+ contents = self.contents();
+
+ if ( contents.length ) {
+ contents.wrapAll( html );
+
+ } else {
+ self.append( html );
+ }
+ });
+ },
+
+ wrap: function( html ) {
+ var isFunction = jQuery.isFunction( html );
+
+ return this.each(function(i) {
+ jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+ });
+ },
+
+ unwrap: function() {
+ return this.parent().each(function() {
+ if ( !jQuery.nodeName( this, "body" ) ) {
+ jQuery( this ).replaceWith( this.childNodes );
+ }
+ }).end();
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.appendChild( elem );
+ }
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.insertBefore( elem, this.firstChild );
+ }
+ });
+ },
+
+ before: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this );
+ });
+ } else if ( arguments.length ) {
+ var set = jQuery.clean( arguments );
+ set.push.apply( set, this.toArray() );
+ return this.pushStack( set, "before", arguments );
+ }
+ },
+
+ after: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ } else if ( arguments.length ) {
+ var set = this.pushStack( this, "after", arguments );
+ set.push.apply( set, jQuery.clean(arguments) );
+ return set;
+ }
+ },
+
+ // keepData is for internal use only--do not document
+ remove: function( selector, keepData ) {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+ if ( !keepData && elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ jQuery.cleanData( [ elem ] );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ }
+ }
+
+ return this;
+ },
+
+ empty: function() {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ }
+
+ // Remove any remaining nodes
+ while ( elem.firstChild ) {
+ elem.removeChild( elem.firstChild );
+ }
+ }
+
+ return this;
+ },
+
+ clone: function( dataAndEvents, deepDataAndEvents ) {
+ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+ return this.map( function () {
+ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+ });
+ },
+
+ html: function( value ) {
+ return jQuery.access( this, function( value ) {
+ var elem = this[0] || {},
+ i = 0,
+ l = this.length;
+
+ if ( value === undefined ) {
+ return elem.nodeType === 1 ?
+ elem.innerHTML.replace( rinlinejQuery, "" ) :
+ null;
+ }
+
+
+ if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+ ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
+ !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
+
+ value = value.replace( rxhtmlTag, "<$1></$2>" );
+
+ try {
+ for (; i < l; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ elem = this[i] || {};
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName( "*" ) );
+ elem.innerHTML = value;
+ }
+ }
+
+ elem = 0;
+
+ // If using innerHTML throws an exception, use the fallback method
+ } catch(e) {}
+ }
+
+ if ( elem ) {
+ this.empty().append( value );
+ }
+ }, null, value, arguments.length );
+ },
+
+ replaceWith: function( value ) {
+ if ( this[0] && this[0].parentNode ) {
+ // Make sure that the elements are removed from the DOM before they are inserted
+ // this can help fix replacing a parent with child elements
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this), old = self.html();
+ self.replaceWith( value.call( this, i, old ) );
+ });
+ }
+
+ if ( typeof value !== "string" ) {
+ value = jQuery( value ).detach();
+ }
+
+ return this.each(function() {
+ var next = this.nextSibling,
+ parent = this.parentNode;
+
+ jQuery( this ).remove();
+
+ if ( next ) {
+ jQuery(next).before( value );
+ } else {
+ jQuery(parent).append( value );
+ }
+ });
+ } else {
+ return this.length ?
+ this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
+ this;
+ }
+ },
+
+ detach: function( selector ) {
+ return this.remove( selector, true );
+ },
+
+ domManip: function( args, table, callback ) {
+ var results, first, fragment, parent,
+ value = args[0],
+ scripts = [];
+
+ // We can't cloneNode fragments that contain checked, in WebKit
+ if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
+ return this.each(function() {
+ jQuery(this).domManip( args, table, callback, true );
+ });
+ }
+
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ args[0] = value.call(this, i, table ? self.html() : undefined);
+ self.domManip( args, table, callback );
+ });
+ }
+
+ if ( this[0] ) {
+ parent = value && value.parentNode;
+
+ // If we're in a fragment, just use that instead of building a new one
+ if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
+ results = { fragment: parent };
+
+ } else {
+ results = jQuery.buildFragment( args, this, scripts );
+ }
+
+ fragment = results.fragment;
+
+ if ( fragment.childNodes.length === 1 ) {
+ first = fragment = fragment.firstChild;
+ } else {
+ first = fragment.firstChild;
+ }
+
+ if ( first ) {
+ table = table && jQuery.nodeName( first, "tr" );
+
+ for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
+ callback.call(
+ table ?
+ root(this[i], first) :
+ this[i],
+ // Make sure that we do not leak memory by inadvertently discarding
+ // the original fragment (which might have attached data) instead of
+ // using it; in addition, use the original fragment object for the last
+ // item instead of first because it can end up being emptied incorrectly
+ // in certain situations (Bug #8070).
+ // Fragments from the fragment cache must always be cloned and never used
+ // in place.
+ results.cacheable || ( l > 1 && i < lastIndex ) ?
+ jQuery.clone( fragment, true, true ) :
+ fragment
+ );
+ }
+ }
+
+ if ( scripts.length ) {
+ jQuery.each( scripts, function( i, elem ) {
+ if ( elem.src ) {
+ jQuery.ajax({
+ type: "GET",
+ global: false,
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+ } else {
+ jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ });
+ }
+ }
+
+ return this;
+ }
+});
+
+function root( elem, cur ) {
+ return jQuery.nodeName(elem, "table") ?
+ (elem.getElementsByTagName("tbody")[0] ||
+ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+ elem;
+}
+
+function cloneCopyEvent( src, dest ) {
+
+ if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+ return;
+ }
+
+ var type, i, l,
+ oldData = jQuery._data( src ),
+ curData = jQuery._data( dest, oldData ),
+ events = oldData.events;
+
+ if ( events ) {
+ delete curData.handle;
+ curData.events = {};
+
+ for ( type in events ) {
+ for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+ jQuery.event.add( dest, type, events[ type ][ i ] );
+ }
+ }
+ }
+
+ // make the cloned public data object a copy from the original
+ if ( curData.data ) {
+ curData.data = jQuery.extend( {}, curData.data );
+ }
+}
+
+function cloneFixAttributes( src, dest ) {
+ var nodeName;
+
+ // We do not need to do anything for non-Elements
+ if ( dest.nodeType !== 1 ) {
+ return;
+ }
+
+ // clearAttributes removes the attributes, which we don't want,
+ // but also removes the attachEvent events, which we *do* want
+ if ( dest.clearAttributes ) {
+ dest.clearAttributes();
+ }
+
+ // mergeAttributes, in contrast, only merges back on the
+ // original attributes, not the events
+ if ( dest.mergeAttributes ) {
+ dest.mergeAttributes( src );
+ }
+
+ nodeName = dest.nodeName.toLowerCase();
+
+ // IE6-8 fail to clone children inside object elements that use
+ // the proprietary classid attribute value (rather than the type
+ // attribute) to identify the type of content to display
+ if ( nodeName === "object" ) {
+ dest.outerHTML = src.outerHTML;
+
+ } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
+ // IE6-8 fails to persist the checked state of a cloned checkbox
+ // or radio button. Worse, IE6-7 fail to give the cloned element
+ // a checked appearance if the defaultChecked value isn't also set
+ if ( src.checked ) {
+ dest.defaultChecked = dest.checked = src.checked;
+ }
+
+ // IE6-7 get confused and end up setting the value of a cloned
+ // checkbox/radio button to an empty string instead of "on"
+ if ( dest.value !== src.value ) {
+ dest.value = src.value;
+ }
+
+ // IE6-8 fails to return the selected option to the default selected
+ // state when cloning options
+ } else if ( nodeName === "option" ) {
+ dest.selected = src.defaultSelected;
+
+ // IE6-8 fails to set the defaultValue to the correct value when
+ // cloning other types of input fields
+ } else if ( nodeName === "input" || nodeName === "textarea" ) {
+ dest.defaultValue = src.defaultValue;
+
+ // IE blanks contents when cloning scripts
+ } else if ( nodeName === "script" && dest.text !== src.text ) {
+ dest.text = src.text;
+ }
+
+ // Event data gets referenced instead of copied if the expando
+ // gets copied too
+ dest.removeAttribute( jQuery.expando );
+
+ // Clear flags for bubbling special change/submit events, they must
+ // be reattached when the newly cloned events are first activated
+ dest.removeAttribute( "_submit_attached" );
+ dest.removeAttribute( "_change_attached" );
+}
+
+jQuery.buildFragment = function( args, nodes, scripts ) {
+ var fragment, cacheable, cacheresults, doc,
+ first = args[ 0 ];
+
+ // nodes may contain either an explicit document object,
+ // a jQuery collection or context object.
+ // If nodes[0] contains a valid object to assign to doc
+ if ( nodes && nodes[0] ) {
+ doc = nodes[0].ownerDocument || nodes[0];
+ }
+
+ // Ensure that an attr object doesn't incorrectly stand in as a document object
+ // Chrome and Firefox seem to allow this to occur and will throw exception
+ // Fixes #8950
+ if ( !doc.createDocumentFragment ) {
+ doc = document;
+ }
+
+ // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
+ // Cloning options loses the selected state, so don't cache them
+ // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+ // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+ // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
+ if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document &&
+ first.charAt(0) === "<" && !rnocache.test( first ) &&
+ (jQuery.support.checkClone || !rchecked.test( first )) &&
+ (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
+
+ cacheable = true;
+
+ cacheresults = jQuery.fragments[ first ];
+ if ( cacheresults && cacheresults !== 1 ) {
+ fragment = cacheresults;
+ }
+ }
+
+ if ( !fragment ) {
+ fragment = doc.createDocumentFragment();
+ jQuery.clean( args, doc, fragment, scripts );
+ }
+
+ if ( cacheable ) {
+ jQuery.fragments[ first ] = cacheresults ? fragment : 1;
+ }
+
+ return { fragment: fragment, cacheable: cacheable };
+};
+
+jQuery.fragments = {};
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [],
+ insert = jQuery( selector ),
+ parent = this.length === 1 && this[0].parentNode;
+
+ if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
+ insert[ original ]( this[0] );
+ return this;
+
+ } else {
+ for ( var i = 0, l = insert.length; i < l; i++ ) {
+ var elems = ( i > 0 ? this.clone(true) : this ).get();
+ jQuery( insert[i] )[ original ]( elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, insert.selector );
+ }
+ };
+});
+
+function getAll( elem ) {
+ if ( typeof elem.getElementsByTagName !== "undefined" ) {
+ return elem.getElementsByTagName( "*" );
+
+ } else if ( typeof elem.querySelectorAll !== "undefined" ) {
+ return elem.querySelectorAll( "*" );
+
+ } else {
+ return [];
+ }
+}
+
+// Used in clean, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+ if ( elem.type === "checkbox" || elem.type === "radio" ) {
+ elem.defaultChecked = elem.checked;
+ }
+}
+// Finds all inputs and passes them to fixDefaultChecked
+function findInputs( elem ) {
+ var nodeName = ( elem.nodeName || "" ).toLowerCase();
+ if ( nodeName === "input" ) {
+ fixDefaultChecked( elem );
+ // Skip scripts, get other children
+ } else if ( nodeName !== "script" && typeof elem.getElementsByTagName !== "undefined" ) {
+ jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
+ }
+}
+
+// Derived From: http://www.iecss.com/shimprove/javascript/shimprove.1-0-1.js
+function shimCloneNode( elem ) {
+ var div = document.createElement( "div" );
+ safeFragment.appendChild( div );
+
+ div.innerHTML = elem.outerHTML;
+ return div.firstChild;
+}
+
+jQuery.extend({
+ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+ var srcElements,
+ destElements,
+ i,
+ // IE<=8 does not properly clone detached, unknown element nodes
+ clone = jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ?
+ elem.cloneNode( true ) :
+ shimCloneNode( elem );
+
+ if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
+ (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
+ // IE copies events bound via attachEvent when using cloneNode.
+ // Calling detachEvent on the clone will also remove the events
+ // from the original. In order to get around this, we use some
+ // proprietary methods to clear the events. Thanks to MooTools
+ // guys for this hotness.
+
+ cloneFixAttributes( elem, clone );
+
+ // Using Sizzle here is crazy slow, so we use getElementsByTagName instead
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ // Weird iteration because IE will replace the length property
+ // with an element if you are cloning the body and one of the
+ // elements on the page has a name or id of "length"
+ for ( i = 0; srcElements[i]; ++i ) {
+ // Ensure that the destination node is not null; Fixes #9587
+ if ( destElements[i] ) {
+ cloneFixAttributes( srcElements[i], destElements[i] );
+ }
+ }
+ }
+
+ // Copy the events from the original to the clone
+ if ( dataAndEvents ) {
+ cloneCopyEvent( elem, clone );
+
+ if ( deepDataAndEvents ) {
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ for ( i = 0; srcElements[i]; ++i ) {
+ cloneCopyEvent( srcElements[i], destElements[i] );
+ }
+ }
+ }
+
+ srcElements = destElements = null;
+
+ // Return the cloned set
+ return clone;
+ },
+
+ clean: function( elems, context, fragment, scripts ) {
+ var checkScriptType, script, j,
+ ret = [];
+
+ context = context || document;
+
+ // !context.createElement fails in IE with an error but returns typeof 'object'
+ if ( typeof context.createElement === "undefined" ) {
+ context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+ }
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( typeof elem === "number" ) {
+ elem += "";
+ }
+
+ if ( !elem ) {
+ continue;
+ }
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" ) {
+ if ( !rhtml.test( elem ) ) {
+ elem = context.createTextNode( elem );
+ } else {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(rxhtmlTag, "<$1></$2>");
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(),
+ wrap = wrapMap[ tag ] || wrapMap._default,
+ depth = wrap[0],
+ div = context.createElement("div"),
+ safeChildNodes = safeFragment.childNodes,
+ remove;
+
+ // Append wrapper element to unknown element safe doc fragment
+ if ( context === document ) {
+ // Use the fragment we've already created for this document
+ safeFragment.appendChild( div );
+ } else {
+ // Use a fragment created with the owner document
+ createSafeFragment( context ).appendChild( div );
+ }
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( depth-- ) {
+ div = div.lastChild;
+ }
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ var hasBody = rtbody.test(elem),
+ tbody = tag === "table" && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] === "<table>" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( j = tbody.length - 1; j >= 0 ; --j ) {
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+ }
+ }
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+ div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+ }
+
+ elem = div.childNodes;
+
+ // Clear elements from DocumentFragment (safeFragment or otherwise)
+ // to avoid hoarding elements. Fixes #11356
+ if ( div ) {
+ div.parentNode.removeChild( div );
+
+ // Guard against -1 index exceptions in FF3.6
+ if ( safeChildNodes.length > 0 ) {
+ remove = safeChildNodes[ safeChildNodes.length - 1 ];
+
+ if ( remove && remove.parentNode ) {
+ remove.parentNode.removeChild( remove );
+ }
+ }
+ }
+ }
+ }
+
+ // Resets defaultChecked for any radios and checkboxes
+ // about to be appended to the DOM in IE 6/7 (#8060)
+ var len;
+ if ( !jQuery.support.appendChecked ) {
+ if ( elem[0] && typeof (len = elem.length) === "number" ) {
+ for ( j = 0; j < len; j++ ) {
+ findInputs( elem[j] );
+ }
+ } else {
+ findInputs( elem );
+ }
+ }
+
+ if ( elem.nodeType ) {
+ ret.push( elem );
+ } else {
+ ret = jQuery.merge( ret, elem );
+ }
+ }
+
+ if ( fragment ) {
+ checkScriptType = function( elem ) {
+ return !elem.type || rscriptType.test( elem.type );
+ };
+ for ( i = 0; ret[i]; i++ ) {
+ script = ret[i];
+ if ( scripts && jQuery.nodeName( script, "script" ) && (!script.type || rscriptType.test( script.type )) ) {
+ scripts.push( script.parentNode ? script.parentNode.removeChild( script ) : script );
+
+ } else {
+ if ( script.nodeType === 1 ) {
+ var jsTags = jQuery.grep( script.getElementsByTagName( "script" ), checkScriptType );
+
+ ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
+ }
+ fragment.appendChild( script );
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ cleanData: function( elems ) {
+ var data, id,
+ cache = jQuery.cache,
+ special = jQuery.event.special,
+ deleteExpando = jQuery.support.deleteExpando;
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+ continue;
+ }
+
+ id = elem[ jQuery.expando ];
+
+ if ( id ) {
+ data = cache[ id ];
+
+ if ( data && data.events ) {
+ for ( var type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
+
+ // This is a shortcut to avoid jQuery.event.remove's overhead
+ } else {
+ jQuery.removeEvent( elem, type, data.handle );
+ }
+ }
+
+ // Null the DOM reference to avoid IE6/7/8 leak (#7054)
+ if ( data.handle ) {
+ data.handle.elem = null;
+ }
+ }
+
+ if ( deleteExpando ) {
+ delete elem[ jQuery.expando ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ }
+
+ delete cache[ id ];
+ }
+ }
+ }
+});
+
+
+
+
+var ralpha = /alpha\([^)]*\)/i,
+ ropacity = /opacity=([^)]*)/,
+ // fixed for IE9, see #8346
+ rupper = /([A-Z]|^ms)/g,
+ rnum = /^[\-+]?(?:\d*\.)?\d+$/i,
+ rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,
+ rrelNum = /^([\-+])=([\-+.\de]+)/,
+ rmargin = /^margin/,
+
+ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+
+ // order is important!
+ cssExpand = [ "Top", "Right", "Bottom", "Left" ],
+
+ curCSS,
+
+ getComputedStyle,
+ currentStyle;
+
+jQuery.fn.css = function( name, value ) {
+ return jQuery.access( this, function( elem, name, value ) {
+ return value !== undefined ?
+ jQuery.style( elem, name, value ) :
+ jQuery.css( elem, name );
+ }, name, value, arguments.length > 1 );
+};
+
+jQuery.extend({
+ // Add in style property hooks for overriding the default
+ // behavior of getting and setting a style property
+ cssHooks: {
+ opacity: {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ // We should always get a number back from opacity
+ var ret = curCSS( elem, "opacity" );
+ return ret === "" ? "1" : ret;
+
+ } else {
+ return elem.style.opacity;
+ }
+ }
+ }
+ },
+
+ // Exclude the following css properties to add px
+ cssNumber: {
+ "fillOpacity": true,
+ "fontWeight": true,
+ "lineHeight": true,
+ "opacity": true,
+ "orphans": true,
+ "widows": true,
+ "zIndex": true,
+ "zoom": true
+ },
+
+ // Add in properties whose names you wish to fix before
+ // setting or getting the value
+ cssProps: {
+ // normalize float css property
+ "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
+ },
+
+ // Get and set the style property on a DOM Node
+ style: function( elem, name, value, extra ) {
+ // Don't set styles on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+ return;
+ }
+
+ // Make sure that we're working with the right name
+ var ret, type, origName = jQuery.camelCase( name ),
+ style = elem.style, hooks = jQuery.cssHooks[ origName ];
+
+ name = jQuery.cssProps[ origName ] || origName;
+
+ // Check if we're setting a value
+ if ( value !== undefined ) {
+ type = typeof value;
+
+ // convert relative number strings (+= or -=) to relative numbers. #7345
+ if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+ value = ( +( ret[1] + 1) * +ret[2] ) + parseFloat( jQuery.css( elem, name ) );
+ // Fixes bug #9237
+ type = "number";
+ }
+
+ // Make sure that NaN and null values aren't set. See: #7116
+ if ( value == null || type === "number" && isNaN( value ) ) {
+ return;
+ }
+
+ // If a number was passed in, add 'px' to the (except for certain CSS properties)
+ if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+ value += "px";
+ }
+
+ // If a hook was provided, use that value, otherwise just set the specified value
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {
+ // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+ // Fixes bug #5509
+ try {
+ style[ name ] = value;
+ } catch(e) {}
+ }
+
+ } else {
+ // If a hook was provided get the non-computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+ return ret;
+ }
+
+ // Otherwise just get the value from the style object
+ return style[ name ];
+ }
+ },
+
+ css: function( elem, name, extra ) {
+ var ret, hooks;
+
+ // Make sure that we're working with the right name
+ name = jQuery.camelCase( name );
+ hooks = jQuery.cssHooks[ name ];
+ name = jQuery.cssProps[ name ] || name;
+
+ // cssFloat needs a special treatment
+ if ( name === "cssFloat" ) {
+ name = "float";
+ }
+
+ // If a hook was provided get the computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {
+ return ret;
+
+ // Otherwise, if a way to get the computed value exists, use that
+ } else if ( curCSS ) {
+ return curCSS( elem, name );
+ }
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {},
+ ret, name;
+
+ // Remember the old values, and insert the new ones
+ for ( name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ ret = callback.call( elem );
+
+ // Revert the old values
+ for ( name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+
+ return ret;
+ }
+});
+
+// DEPRECATED in 1.3, Use jQuery.css() instead
+jQuery.curCSS = jQuery.css;
+
+if ( document.defaultView && document.defaultView.getComputedStyle ) {
+ getComputedStyle = function( elem, name ) {
+ var ret, defaultView, computedStyle, width,
+ style = elem.style;
+
+ name = name.replace( rupper, "-$1" ).toLowerCase();
+
+ if ( (defaultView = elem.ownerDocument.defaultView) &&
+ (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
+
+ ret = computedStyle.getPropertyValue( name );
+ if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
+ ret = jQuery.style( elem, name );
+ }
+ }
+
+ // A tribute to the "awesome hack by Dean Edwards"
+ // WebKit uses "computed value (percentage if specified)" instead of "used value" for margins
+ // which is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+ if ( !jQuery.support.pixelMargin && computedStyle && rmargin.test( name ) && rnumnonpx.test( ret ) ) {
+ width = style.width;
+ style.width = ret;
+ ret = computedStyle.width;
+ style.width = width;
+ }
+
+ return ret;
+ };
+}
+
+if ( document.documentElement.currentStyle ) {
+ currentStyle = function( elem, name ) {
+ var left, rsLeft, uncomputed,
+ ret = elem.currentStyle && elem.currentStyle[ name ],
+ style = elem.style;
+
+ // Avoid setting ret to empty string here
+ // so we don't default to auto
+ if ( ret == null && style && (uncomputed = style[ name ]) ) {
+ ret = uncomputed;
+ }
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( rnumnonpx.test( ret ) ) {
+
+ // Remember the original values
+ left = style.left;
+ rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ }
+ style.left = name === "fontSize" ? "1em" : ret;
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret === "" ? "auto" : ret;
+ };
+}
+
+curCSS = getComputedStyle || currentStyle;
+
+function getWidthOrHeight( elem, name, extra ) {
+
+ // Start with offset property
+ var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+ i = name === "width" ? 1 : 0,
+ len = 4;
+
+ if ( val > 0 ) {
+ if ( extra !== "border" ) {
+ for ( ; i < len; i += 2 ) {
+ if ( !extra ) {
+ val -= parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0;
+ }
+ if ( extra === "margin" ) {
+ val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ] ) ) || 0;
+ } else {
+ val -= parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
+ }
+ }
+ }
+
+ return val + "px";
+ }
+
+ // Fall back to computed then uncomputed css if necessary
+ val = curCSS( elem, name );
+ if ( val < 0 || val == null ) {
+ val = elem.style[ name ];
+ }
+
+ // Computed unit is not pixels. Stop here and return.
+ if ( rnumnonpx.test(val) ) {
+ return val;
+ }
+
+ // Normalize "", auto, and prepare for extra
+ val = parseFloat( val ) || 0;
+
+ // Add padding, border, margin
+ if ( extra ) {
+ for ( ; i < len; i += 2 ) {
+ val += parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0;
+ if ( extra !== "padding" ) {
+ val += parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
+ }
+ if ( extra === "margin" ) {
+ val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ]) ) || 0;
+ }
+ }
+ }
+
+ return val + "px";
+}
+
+jQuery.each([ "height", "width" ], function( i, name ) {
+ jQuery.cssHooks[ name ] = {
+ get: function( elem, computed, extra ) {
+ if ( computed ) {
+ if ( elem.offsetWidth !== 0 ) {
+ return getWidthOrHeight( elem, name, extra );
+ } else {
+ return jQuery.swap( elem, cssShow, function() {
+ return getWidthOrHeight( elem, name, extra );
+ });
+ }
+ }
+ },
+
+ set: function( elem, value ) {
+ return rnum.test( value ) ?
+ value + "px" :
+ value;
+ }
+ };
+});
+
+if ( !jQuery.support.opacity ) {
+ jQuery.cssHooks.opacity = {
+ get: function( elem, computed ) {
+ // IE uses filters for opacity
+ return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+ ( parseFloat( RegExp.$1 ) / 100 ) + "" :
+ computed ? "1" : "";
+ },
+
+ set: function( elem, value ) {
+ var style = elem.style,
+ currentStyle = elem.currentStyle,
+ opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
+ filter = currentStyle && currentStyle.filter || style.filter || "";
+
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ style.zoom = 1;
+
+ // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
+ if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" ) {
+
+ // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
+ // if "filter:" is present at all, clearType is disabled, we want to avoid this
+ // style.removeAttribute is IE Only, but so apparently is this code path...
+ style.removeAttribute( "filter" );
+
+ // if there there is no filter style applied in a css rule, we are done
+ if ( currentStyle && !currentStyle.filter ) {
+ return;
+ }
+ }
+
+ // otherwise, set new filter values
+ style.filter = ralpha.test( filter ) ?
+ filter.replace( ralpha, opacity ) :
+ filter + " " + opacity;
+ }
+ };
+}
+
+jQuery(function() {
+ // This hook cannot be added until DOM ready because the support test
+ // for it is not run until after DOM ready
+ if ( !jQuery.support.reliableMarginRight ) {
+ jQuery.cssHooks.marginRight = {
+ get: function( elem, computed ) {
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ // Work around by temporarily setting element display to inline-block
+ return jQuery.swap( elem, { "display": "inline-block" }, function() {
+ if ( computed ) {
+ return curCSS( elem, "margin-right" );
+ } else {
+ return elem.style.marginRight;
+ }
+ });
+ }
+ };
+ }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.hidden = function( elem ) {
+ var width = elem.offsetWidth,
+ height = elem.offsetHeight;
+
+ return ( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
+ };
+
+ jQuery.expr.filters.visible = function( elem ) {
+ return !jQuery.expr.filters.hidden( elem );
+ };
+}
+
+// These hooks are used by animate to expand properties
+jQuery.each({
+ margin: "",
+ padding: "",
+ border: "Width"
+}, function( prefix, suffix ) {
+
+ jQuery.cssHooks[ prefix + suffix ] = {
+ expand: function( value ) {
+ var i,
+
+ // assumes a single number if not a string
+ parts = typeof value === "string" ? value.split(" ") : [ value ],
+ expanded = {};
+
+ for ( i = 0; i < 4; i++ ) {
+ expanded[ prefix + cssExpand[ i ] + suffix ] =
+ parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+ }
+
+ return expanded;
+ }
+ };
+});
+
+
+
+
+var r20 = /%20/g,
+ rbracket = /\[\]$/,
+ rCRLF = /\r?\n/g,
+ rhash = /#.*$/,
+ rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+ rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+ // #7653, #8125, #8152: local protocol detection
+ rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,
+ rnoContent = /^(?:GET|HEAD)$/,
+ rprotocol = /^\/\//,
+ rquery = /\?/,
+ rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+ rselectTextarea = /^(?:select|textarea)/i,
+ rspacesAjax = /\s+/,
+ rts = /([?&])_=[^&]*/,
+ rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
+
+ // Keep a copy of the old load method
+ _load = jQuery.fn.load,
+
+ /* Prefilters
+ * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+ * 2) These are called:
+ * - BEFORE asking for a transport
+ * - AFTER param serialization (s.data is a string if s.processData is true)
+ * 3) key is the dataType
+ * 4) the catchall symbol "*" can be used
+ * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+ */
+ prefilters = {},
+
+ /* Transports bindings
+ * 1) key is the dataType
+ * 2) the catchall symbol "*" can be used
+ * 3) selection will start with transport dataType and THEN go to "*" if needed
+ */
+ transports = {},
+
+ // Document location
+ ajaxLocation,
+
+ // Document location segments
+ ajaxLocParts,
+
+ // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+ allTypes = ["*/"] + ["*"];
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+ ajaxLocation = location.href;
+} catch( e ) {
+ // Use the href attribute of an A element
+ // since IE will modify it given document.location
+ ajaxLocation = document.createElement( "a" );
+ ajaxLocation.href = "";
+ ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+ // dataTypeExpression is optional and defaults to "*"
+ return function( dataTypeExpression, func ) {
+
+ if ( typeof dataTypeExpression !== "string" ) {
+ func = dataTypeExpression;
+ dataTypeExpression = "*";
+ }
+
+ if ( jQuery.isFunction( func ) ) {
+ var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
+ i = 0,
+ length = dataTypes.length,
+ dataType,
+ list,
+ placeBefore;
+
+ // For each dataType in the dataTypeExpression
+ for ( ; i < length; i++ ) {
+ dataType = dataTypes[ i ];
+ // We control if we're asked to add before
+ // any existing element
+ placeBefore = /^\+/.test( dataType );
+ if ( placeBefore ) {
+ dataType = dataType.substr( 1 ) || "*";
+ }
+ list = structure[ dataType ] = structure[ dataType ] || [];
+ // then we add to the structure accordingly
+ list[ placeBefore ? "unshift" : "push" ]( func );
+ }
+ }
+ };
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
+ dataType /* internal */, inspected /* internal */ ) {
+
+ dataType = dataType || options.dataTypes[ 0 ];
+ inspected = inspected || {};
+
+ inspected[ dataType ] = true;
+
+ var list = structure[ dataType ],
+ i = 0,
+ length = list ? list.length : 0,
+ executeOnly = ( structure === prefilters ),
+ selection;
+
+ for ( ; i < length && ( executeOnly || !selection ); i++ ) {
+ selection = list[ i ]( options, originalOptions, jqXHR );
+ // If we got redirected to another dataType
+ // we try there if executing only and not done already
+ if ( typeof selection === "string" ) {
+ if ( !executeOnly || inspected[ selection ] ) {
+ selection = undefined;
+ } else {
+ options.dataTypes.unshift( selection );
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, selection, inspected );
+ }
+ }
+ }
+ // If we're only executing or nothing was selected
+ // we try the catchall dataType if not done already
+ if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, "*", inspected );
+ }
+ // unnecessary when only executing (prefilters)
+ // but it'll be ignored by the caller in that case
+ return selection;
+}
+
+// A special extend for ajax options
+// that takes "flat" options (not to be deep extended)
+// Fixes #9887
+function ajaxExtend( target, src ) {
+ var key, deep,
+ flatOptions = jQuery.ajaxSettings.flatOptions || {};
+ for ( key in src ) {
+ if ( src[ key ] !== undefined ) {
+ ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
+ }
+ }
+ if ( deep ) {
+ jQuery.extend( true, target, deep );
+ }
+}
+
+jQuery.fn.extend({
+ load: function( url, params, callback ) {
+ if ( typeof url !== "string" && _load ) {
+ return _load.apply( this, arguments );
+
+ // Don't do a request if no elements are being requested
+ } else if ( !this.length ) {
+ return this;
+ }
+
+ var off = url.indexOf( " " );
+ if ( off >= 0 ) {
+ var selector = url.slice( off, url.length );
+ url = url.slice( 0, off );
+ }
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params ) {
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+ // We assume that it's the callback
+ callback = params;
+ params = undefined;
+
+ // Otherwise, build a param string
+ } else if ( typeof params === "object" ) {
+ params = jQuery.param( params, jQuery.ajaxSettings.traditional );
+ type = "POST";
+ }
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+ type: type,
+ dataType: "html",
+ data: params,
+ // Complete callback (responseText is used internally)
+ complete: function( jqXHR, status, responseText ) {
+ // Store the response as specified by the jqXHR object
+ responseText = jqXHR.responseText;
+ // If successful, inject the HTML into all the matched elements
+ if ( jqXHR.isResolved() ) {
+ // #4825: Get the actual response in case
+ // a dataFilter is present in ajaxSettings
+ jqXHR.done(function( r ) {
+ responseText = r;
+ });
+ // See if a selector was specified
+ self.html( selector ?
+ // Create a dummy div to hold the results
+ jQuery("<div>")
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append(responseText.replace(rscript, ""))
+
+ // Locate the specified elements
+ .find(selector) :
+
+ // If not, just inject the full result
+ responseText );
+ }
+
+ if ( callback ) {
+ self.each( callback, [ responseText, status, jqXHR ] );
+ }
+ }
+ });
+
+ return this;
+ },
+
+ serialize: function() {
+ return jQuery.param( this.serializeArray() );
+ },
+
+ serializeArray: function() {
+ return this.map(function(){
+ return this.elements ? jQuery.makeArray( this.elements ) : this;
+ })
+ .filter(function(){
+ return this.name && !this.disabled &&
+ ( this.checked || rselectTextarea.test( this.nodeName ) ||
+ rinput.test( this.type ) );
+ })
+ .map(function( i, elem ){
+ var val = jQuery( this ).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray( val ) ?
+ jQuery.map( val, function( val, i ){
+ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }) :
+ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }).get();
+ }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
+ jQuery.fn[ o ] = function( f ){
+ return this.on( o, f );
+ };
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+ jQuery[ method ] = function( url, data, callback, type ) {
+ // shift arguments if data argument was omitted
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = undefined;
+ }
+
+ return jQuery.ajax({
+ type: method,
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ };
+});
+
+jQuery.extend({
+
+ getScript: function( url, callback ) {
+ return jQuery.get( url, undefined, callback, "script" );
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get( url, data, callback, "json" );
+ },
+
+ // Creates a full fledged settings object into target
+ // with both ajaxSettings and settings fields.
+ // If target is omitted, writes into ajaxSettings.
+ ajaxSetup: function( target, settings ) {
+ if ( settings ) {
+ // Building a settings object
+ ajaxExtend( target, jQuery.ajaxSettings );
+ } else {
+ // Extending ajaxSettings
+ settings = target;
+ target = jQuery.ajaxSettings;
+ }
+ ajaxExtend( target, settings );
+ return target;
+ },
+
+ ajaxSettings: {
+ url: ajaxLocation,
+ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+ global: true,
+ type: "GET",
+ contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+ processData: true,
+ async: true,
+ /*
+ timeout: 0,
+ data: null,
+ dataType: null,
+ username: null,
+ password: null,
+ cache: null,
+ traditional: false,
+ headers: {},
+ */
+
+ accepts: {
+ xml: "application/xml, text/xml",
+ html: "text/html",
+ text: "text/plain",
+ json: "application/json, text/javascript",
+ "*": allTypes
+ },
+
+ contents: {
+ xml: /xml/,
+ html: /html/,
+ json: /json/
+ },
+
+ responseFields: {
+ xml: "responseXML",
+ text: "responseText"
+ },
+
+ // List of data converters
+ // 1) key format is "source_type destination_type" (a single space in-between)
+ // 2) the catchall symbol "*" can be used for source_type
+ converters: {
+
+ // Convert anything to text
+ "* text": window.String,
+
+ // Text to html (true = no transformation)
+ "text html": true,
+
+ // Evaluate text as a json expression
+ "text json": jQuery.parseJSON,
+
+ // Parse text as xml
+ "text xml": jQuery.parseXML
+ },
+
+ // For options that shouldn't be deep extended:
+ // you can add your own custom options here if
+ // and when you create one that shouldn't be
+ // deep extended (see ajaxExtend)
+ flatOptions: {
+ context: true,
+ url: true
+ }
+ },
+
+ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+ ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+ // Main method
+ ajax: function( url, options ) {
+
+ // If url is an object, simulate pre-1.5 signature
+ if ( typeof url === "object" ) {
+ options = url;
+ url = undefined;
+ }
+
+ // Force options to be an object
+ options = options || {};
+
+ var // Create the final options object
+ s = jQuery.ajaxSetup( {}, options ),
+ // Callbacks context
+ callbackContext = s.context || s,
+ // Context for global events
+ // It's the callbackContext if one was provided in the options
+ // and if it's a DOM node or a jQuery collection
+ globalEventContext = callbackContext !== s &&
+ ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
+ jQuery( callbackContext ) : jQuery.event,
+ // Deferreds
+ deferred = jQuery.Deferred(),
+ completeDeferred = jQuery.Callbacks( "once memory" ),
+ // Status-dependent callbacks
+ statusCode = s.statusCode || {},
+ // ifModified key
+ ifModifiedKey,
+ // Headers (they are sent all at once)
+ requestHeaders = {},
+ requestHeadersNames = {},
+ // Response headers
+ responseHeadersString,
+ responseHeaders,
+ // transport
+ transport,
+ // timeout handle
+ timeoutTimer,
+ // Cross-domain detection vars
+ parts,
+ // The jqXHR state
+ state = 0,
+ // To know if global events are to be dispatched
+ fireGlobals,
+ // Loop variable
+ i,
+ // Fake xhr
+ jqXHR = {
+
+ readyState: 0,
+
+ // Caches the header
+ setRequestHeader: function( name, value ) {
+ if ( !state ) {
+ var lname = name.toLowerCase();
+ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+ requestHeaders[ name ] = value;
+ }
+ return this;
+ },
+
+ // Raw string
+ getAllResponseHeaders: function() {
+ return state === 2 ? responseHeadersString : null;
+ },
+
+ // Builds headers hashtable if needed
+ getResponseHeader: function( key ) {
+ var match;
+ if ( state === 2 ) {
+ if ( !responseHeaders ) {
+ responseHeaders = {};
+ while( ( match = rheaders.exec( responseHeadersString ) ) ) {
+ responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+ }
+ }
+ match = responseHeaders[ key.toLowerCase() ];
+ }
+ return match === undefined ? null : match;
+ },
+
+ // Overrides response content-type header
+ overrideMimeType: function( type ) {
+ if ( !state ) {
+ s.mimeType = type;
+ }
+ return this;
+ },
+
+ // Cancel the request
+ abort: function( statusText ) {
+ statusText = statusText || "abort";
+ if ( transport ) {
+ transport.abort( statusText );
+ }
+ done( 0, statusText );
+ return this;
+ }
+ };
+
+ // Callback for when everything is done
+ // It is defined here because jslint complains if it is declared
+ // at the end of the function (which would be more logical and readable)
+ function done( status, nativeStatusText, responses, headers ) {
+
+ // Called once
+ if ( state === 2 ) {
+ return;
+ }
+
+ // State is "done" now
+ state = 2;
+
+ // Clear timeout if it exists
+ if ( timeoutTimer ) {
+ clearTimeout( timeoutTimer );
+ }
+
+ // Dereference transport for early garbage collection
+ // (no matter how long the jqXHR object will be used)
+ transport = undefined;
+
+ // Cache response headers
+ responseHeadersString = headers || "";
+
+ // Set readyState
+ jqXHR.readyState = status > 0 ? 4 : 0;
+
+ var isSuccess,
+ success,
+ error,
+ statusText = nativeStatusText,
+ response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
+ lastModified,
+ etag;
+
+ // If successful, handle type chaining
+ if ( status >= 200 && status < 300 || status === 304 ) {
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+
+ if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
+ jQuery.lastModified[ ifModifiedKey ] = lastModified;
+ }
+ if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
+ jQuery.etag[ ifModifiedKey ] = etag;
+ }
+ }
+
+ // If not modified
+ if ( status === 304 ) {
+
+ statusText = "notmodified";
+ isSuccess = true;
+
+ // If we have data
+ } else {
+
+ try {
+ success = ajaxConvert( s, response );
+ statusText = "success";
+ isSuccess = true;
+ } catch(e) {
+ // We have a parsererror
+ statusText = "parsererror";
+ error = e;
+ }
+ }
+ } else {
+ // We extract error from statusText
+ // then normalize statusText and status for non-aborts
+ error = statusText;
+ if ( !statusText || status ) {
+ statusText = "error";
+ if ( status < 0 ) {
+ status = 0;
+ }
+ }
+ }
+
+ // Set data for the fake xhr object
+ jqXHR.status = status;
+ jqXHR.statusText = "" + ( nativeStatusText || statusText );
+
+ // Success/Error
+ if ( isSuccess ) {
+ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+ } else {
+ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+ }
+
+ // Status-dependent callbacks
+ jqXHR.statusCode( statusCode );
+ statusCode = undefined;
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
+ [ jqXHR, s, isSuccess ? success : error ] );
+ }
+
+ // Complete
+ completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+ // Handle the global AJAX counter
+ if ( !( --jQuery.active ) ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ }
+ }
+
+ // Attach deferreds
+ deferred.promise( jqXHR );
+ jqXHR.success = jqXHR.done;
+ jqXHR.error = jqXHR.fail;
+ jqXHR.complete = completeDeferred.add;
+
+ // Status-dependent callbacks
+ jqXHR.statusCode = function( map ) {
+ if ( map ) {
+ var tmp;
+ if ( state < 2 ) {
+ for ( tmp in map ) {
+ statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
+ }
+ } else {
+ tmp = map[ jqXHR.status ];
+ jqXHR.then( tmp, tmp );
+ }
+ }
+ return this;
+ };
+
+ // Remove hash character (#7531: and string promotion)
+ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+ // We also use the url parameter if available
+ s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+ // Extract dataTypes list
+ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax );
+
+ // Determine if a cross-domain request is in order
+ if ( s.crossDomain == null ) {
+ parts = rurl.exec( s.url.toLowerCase() );
+ s.crossDomain = !!( parts &&
+ ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
+ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
+ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
+ );
+ }
+
+ // Convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
+ s.data = jQuery.param( s.data, s.traditional );
+ }
+
+ // Apply prefilters
+ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+ // If request was aborted inside a prefilter, stop there
+ if ( state === 2 ) {
+ return false;
+ }
+
+ // We can fire global events as of now if asked to
+ fireGlobals = s.global;
+
+ // Uppercase the type
+ s.type = s.type.toUpperCase();
+
+ // Determine if request has content
+ s.hasContent = !rnoContent.test( s.type );
+
+ // Watch for a new set of requests
+ if ( fireGlobals && jQuery.active++ === 0 ) {
+ jQuery.event.trigger( "ajaxStart" );
+ }
+
+ // More options handling for requests with no content
+ if ( !s.hasContent ) {
+
+ // If data is available, append data to url
+ if ( s.data ) {
+ s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
+ // #9682: remove data so that it's not used in an eventual retry
+ delete s.data;
+ }
+
+ // Get ifModifiedKey before adding the anti-cache parameter
+ ifModifiedKey = s.url;
+
+ // Add anti-cache in url if needed
+ if ( s.cache === false ) {
+
+ var ts = jQuery.now(),
+ // try replacing _= if it is there
+ ret = s.url.replace( rts, "$1_=" + ts );
+
+ // if nothing was replaced, add timestamp to the end
+ s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
+ }
+ }
+
+ // Set the correct header, if data is being sent
+ if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+ jqXHR.setRequestHeader( "Content-Type", s.contentType );
+ }
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ ifModifiedKey = ifModifiedKey || s.url;
+ if ( jQuery.lastModified[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
+ }
+ if ( jQuery.etag[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
+ }
+ }
+
+ // Set the Accepts header for the server, depending on the dataType
+ jqXHR.setRequestHeader(
+ "Accept",
+ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+ s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+ s.accepts[ "*" ]
+ );
+
+ // Check for headers option
+ for ( i in s.headers ) {
+ jqXHR.setRequestHeader( i, s.headers[ i ] );
+ }
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+ // Abort if not done already
+ jqXHR.abort();
+ return false;
+
+ }
+
+ // Install callbacks on deferreds
+ for ( i in { success: 1, error: 1, complete: 1 } ) {
+ jqXHR[ i ]( s[ i ] );
+ }
+
+ // Get transport
+ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+ // If no transport, we auto-abort
+ if ( !transport ) {
+ done( -1, "No Transport" );
+ } else {
+ jqXHR.readyState = 1;
+ // Send global event
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+ }
+ // Timeout
+ if ( s.async && s.timeout > 0 ) {
+ timeoutTimer = setTimeout( function(){
+ jqXHR.abort( "timeout" );
+ }, s.timeout );
+ }
+
+ try {
+ state = 1;
+ transport.send( requestHeaders, done );
+ } catch (e) {
+ // Propagate exception as error if not done
+ if ( state < 2 ) {
+ done( -1, e );
+ // Simply rethrow otherwise
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ return jqXHR;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function( a, traditional ) {
+ var s = [],
+ add = function( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction( value ) ? value() : value;
+ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+ };
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( var prefix in a ) {
+ buildParams( prefix, a[ prefix ], traditional, add );
+ }
+ }
+
+ // Return the resulting serialization
+ return s.join( "&" ).replace( r20, "+" );
+ }
+});
+
+function buildParams( prefix, obj, traditional, add ) {
+ if ( jQuery.isArray( obj ) ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || rbracket.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+
+ } else {
+ // If array item is non-scalar (array or object), encode its
+ // numeric index to resolve deserialization ambiguity issues.
+ // Note that rack (as of 1.0.0) can't currently deserialize
+ // nested arrays properly, and attempting to do so may cause
+ // a server error. Possible fixes are to modify rack's
+ // deserialization algorithm or to provide an option or flag
+ // to force array serialization to be shallow.
+ buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+ }
+ });
+
+ } else if ( !traditional && jQuery.type( obj ) === "object" ) {
+ // Serialize object item.
+ for ( var name in obj ) {
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+ }
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+}
+
+// This is still on the jQuery object... for now
+// Want to move this to jQuery.ajax some day
+jQuery.extend({
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ etag: {}
+
+});
+
+/* Handles responses to an ajax request:
+ * - sets all responseXXX fields accordingly
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+
+ var contents = s.contents,
+ dataTypes = s.dataTypes,
+ responseFields = s.responseFields,
+ ct,
+ type,
+ finalDataType,
+ firstDataType;
+
+ // Fill responseXXX fields
+ for ( type in responseFields ) {
+ if ( type in responses ) {
+ jqXHR[ responseFields[type] ] = responses[ type ];
+ }
+ }
+
+ // Remove auto dataType and get content-type in the process
+ while( dataTypes[ 0 ] === "*" ) {
+ dataTypes.shift();
+ if ( ct === undefined ) {
+ ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
+ }
+ }
+
+ // Check if we're dealing with a known content-type
+ if ( ct ) {
+ for ( type in contents ) {
+ if ( contents[ type ] && contents[ type ].test( ct ) ) {
+ dataTypes.unshift( type );
+ break;
+ }
+ }
+ }
+
+ // Check to see if we have a response for the expected dataType
+ if ( dataTypes[ 0 ] in responses ) {
+ finalDataType = dataTypes[ 0 ];
+ } else {
+ // Try convertible dataTypes
+ for ( type in responses ) {
+ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+ finalDataType = type;
+ break;
+ }
+ if ( !firstDataType ) {
+ firstDataType = type;
+ }
+ }
+ // Or just use first one
+ finalDataType = finalDataType || firstDataType;
+ }
+
+ // If we found a dataType
+ // We add the dataType to the list if needed
+ // and return the corresponding response
+ if ( finalDataType ) {
+ if ( finalDataType !== dataTypes[ 0 ] ) {
+ dataTypes.unshift( finalDataType );
+ }
+ return responses[ finalDataType ];
+ }
+}
+
+// Chain conversions given the request and the original response
+function ajaxConvert( s, response ) {
+
+ // Apply the dataFilter if provided
+ if ( s.dataFilter ) {
+ response = s.dataFilter( response, s.dataType );
+ }
+
+ var dataTypes = s.dataTypes,
+ converters = {},
+ i,
+ key,
+ length = dataTypes.length,
+ tmp,
+ // Current and previous dataTypes
+ current = dataTypes[ 0 ],
+ prev,
+ // Conversion expression
+ conversion,
+ // Conversion function
+ conv,
+ // Conversion functions (transitive conversion)
+ conv1,
+ conv2;
+
+ // For each dataType in the chain
+ for ( i = 1; i < length; i++ ) {
+
+ // Create converters map
+ // with lowercased keys
+ if ( i === 1 ) {
+ for ( key in s.converters ) {
+ if ( typeof key === "string" ) {
+ converters[ key.toLowerCase() ] = s.converters[ key ];
+ }
+ }
+ }
+
+ // Get the dataTypes
+ prev = current;
+ current = dataTypes[ i ];
+
+ // If current is auto dataType, update it to prev
+ if ( current === "*" ) {
+ current = prev;
+ // If no auto and dataTypes are actually different
+ } else if ( prev !== "*" && prev !== current ) {
+
+ // Get the converter
+ conversion = prev + " " + current;
+ conv = converters[ conversion ] || converters[ "* " + current ];
+
+ // If there is no direct converter, search transitively
+ if ( !conv ) {
+ conv2 = undefined;
+ for ( conv1 in converters ) {
+ tmp = conv1.split( " " );
+ if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
+ conv2 = converters[ tmp[1] + " " + current ];
+ if ( conv2 ) {
+ conv1 = converters[ conv1 ];
+ if ( conv1 === true ) {
+ conv = conv2;
+ } else if ( conv2 === true ) {
+ conv = conv1;
+ }
+ break;
+ }
+ }
+ }
+ }
+ // If we found no converter, dispatch an error
+ if ( !( conv || conv2 ) ) {
+ jQuery.error( "No conversion from " + conversion.replace(" "," to ") );
+ }
+ // If found converter is not an equivalence
+ if ( conv !== true ) {
+ // Convert with 1 or 2 converters accordingly
+ response = conv ? conv( response ) : conv2( conv1(response) );
+ }
+ }
+ }
+ return response;
+}
+
+
+
+
+var jsc = jQuery.now(),
+ jsre = /(\=)\?(&|$)|\?\?/i;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+ jsonp: "callback",
+ jsonpCallback: function() {
+ return jQuery.expando + "_" + ( jsc++ );
+ }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+ var inspectData = ( typeof s.data === "string" ) && /^application\/x\-www\-form\-urlencoded/.test( s.contentType );
+
+ if ( s.dataTypes[ 0 ] === "jsonp" ||
+ s.jsonp !== false && ( jsre.test( s.url ) ||
+ inspectData && jsre.test( s.data ) ) ) {
+
+ var responseContainer,
+ jsonpCallback = s.jsonpCallback =
+ jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
+ previous = window[ jsonpCallback ],
+ url = s.url,
+ data = s.data,
+ replace = "$1" + jsonpCallback + "$2";
+
+ if ( s.jsonp !== false ) {
+ url = url.replace( jsre, replace );
+ if ( s.url === url ) {
+ if ( inspectData ) {
+ data = data.replace( jsre, replace );
+ }
+ if ( s.data === data ) {
+ // Add callback manually
+ url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
+ }
+ }
+ }
+
+ s.url = url;
+ s.data = data;
+
+ // Install callback
+ window[ jsonpCallback ] = function( response ) {
+ responseContainer = [ response ];
+ };
+
+ // Clean-up function
+ jqXHR.always(function() {
+ // Set callback back to previous value
+ window[ jsonpCallback ] = previous;
+ // Call if it was a function and we have a response
+ if ( responseContainer && jQuery.isFunction( previous ) ) {
+ window[ jsonpCallback ]( responseContainer[ 0 ] );
+ }
+ });
+
+ // Use data converter to retrieve json after script execution
+ s.converters["script json"] = function() {
+ if ( !responseContainer ) {
+ jQuery.error( jsonpCallback + " was not called" );
+ }
+ return responseContainer[ 0 ];
+ };
+
+ // force json dataType
+ s.dataTypes[ 0 ] = "json";
+
+ // Delegate to script
+ return "script";
+ }
+});
+
+
+
+
+// Install script dataType
+jQuery.ajaxSetup({
+ accepts: {
+ script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+ },
+ contents: {
+ script: /javascript|ecmascript/
+ },
+ converters: {
+ "text script": function( text ) {
+ jQuery.globalEval( text );
+ return text;
+ }
+ }
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter( "script", function( s ) {
+ if ( s.cache === undefined ) {
+ s.cache = false;
+ }
+ if ( s.crossDomain ) {
+ s.type = "GET";
+ s.global = false;
+ }
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+ // This transport only deals with cross domain requests
+ if ( s.crossDomain ) {
+
+ var script,
+ head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
+
+ return {
+
+ send: function( _, callback ) {
+
+ script = document.createElement( "script" );
+
+ script.async = "async";
+
+ if ( s.scriptCharset ) {
+ script.charset = s.scriptCharset;
+ }
+
+ script.src = s.url;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+ if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+
+ // Remove the script
+ if ( head && script.parentNode ) {
+ head.removeChild( script );
+ }
+
+ // Dereference the script
+ script = undefined;
+
+ // Callback if not abort
+ if ( !isAbort ) {
+ callback( 200, "success" );
+ }
+ }
+ };
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709 and #4378).
+ head.insertBefore( script, head.firstChild );
+ },
+
+ abort: function() {
+ if ( script ) {
+ script.onload( 0, 1 );
+ }
+ }
+ };
+ }
+});
+
+
+
+
+var // #5280: Internet Explorer will keep connections alive if we don't abort on unload
+ xhrOnUnloadAbort = window.ActiveXObject ? function() {
+ // Abort all pending requests
+ for ( var key in xhrCallbacks ) {
+ xhrCallbacks[ key ]( 0, 1 );
+ }
+ } : false,
+ xhrId = 0,
+ xhrCallbacks;
+
+// Functions to create xhrs
+function createStandardXHR() {
+ try {
+ return new window.XMLHttpRequest();
+ } catch( e ) {}
+}
+
+function createActiveXHR() {
+ try {
+ return new window.ActiveXObject( "Microsoft.XMLHTTP" );
+ } catch( e ) {}
+}
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+ /* Microsoft failed to properly
+ * implement the XMLHttpRequest in IE7 (can't request local files),
+ * so we use the ActiveXObject when it is available
+ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+ * we need a fallback.
+ */
+ function() {
+ return !this.isLocal && createStandardXHR() || createActiveXHR();
+ } :
+ // For all other browsers, use the standard XMLHttpRequest object
+ createStandardXHR;
+
+// Determine support properties
+(function( xhr ) {
+ jQuery.extend( jQuery.support, {
+ ajax: !!xhr,
+ cors: !!xhr && ( "withCredentials" in xhr )
+ });
+})( jQuery.ajaxSettings.xhr() );
+
+// Create transport if the browser can provide an xhr
+if ( jQuery.support.ajax ) {
+
+ jQuery.ajaxTransport(function( s ) {
+ // Cross domain only allowed if supported through XMLHttpRequest
+ if ( !s.crossDomain || jQuery.support.cors ) {
+
+ var callback;
+
+ return {
+ send: function( headers, complete ) {
+
+ // Get a new xhr
+ var xhr = s.xhr(),
+ handle,
+ i;
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if ( s.username ) {
+ xhr.open( s.type, s.url, s.async, s.username, s.password );
+ } else {
+ xhr.open( s.type, s.url, s.async );
+ }
+
+ // Apply custom fields if provided
+ if ( s.xhrFields ) {
+ for ( i in s.xhrFields ) {
+ xhr[ i ] = s.xhrFields[ i ];
+ }
+ }
+
+ // Override mime type if needed
+ if ( s.mimeType && xhr.overrideMimeType ) {
+ xhr.overrideMimeType( s.mimeType );
+ }
+
+ // X-Requested-With header
+ // For cross-domain requests, seeing as conditions for a preflight are
+ // akin to a jigsaw puzzle, we simply never set it to be sure.
+ // (it can always be set on a per-request basis or even using ajaxSetup)
+ // For same-domain requests, won't change header if already provided.
+ if ( !s.crossDomain && !headers["X-Requested-With"] ) {
+ headers[ "X-Requested-With" ] = "XMLHttpRequest";
+ }
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ for ( i in headers ) {
+ xhr.setRequestHeader( i, headers[ i ] );
+ }
+ } catch( _ ) {}
+
+ // Do send the request
+ // This may raise an exception which is actually
+ // handled in jQuery.ajax (so no try/catch here)
+ xhr.send( ( s.hasContent && s.data ) || null );
+
+ // Listener
+ callback = function( _, isAbort ) {
+
+ var status,
+ statusText,
+ responseHeaders,
+ responses,
+ xml;
+
+ // Firefox throws exceptions when accessing properties
+ // of an xhr when a network error occured
+ // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
+ try {
+
+ // Was never called and is aborted or complete
+ if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+ // Only called once
+ callback = undefined;
+
+ // Do not keep as active anymore
+ if ( handle ) {
+ xhr.onreadystatechange = jQuery.noop;
+ if ( xhrOnUnloadAbort ) {
+ delete xhrCallbacks[ handle ];
+ }
+ }
+
+ // If it's an abort
+ if ( isAbort ) {
+ // Abort it manually if needed
+ if ( xhr.readyState !== 4 ) {
+ xhr.abort();
+ }
+ } else {
+ status = xhr.status;
+ responseHeaders = xhr.getAllResponseHeaders();
+ responses = {};
+ xml = xhr.responseXML;
+
+ // Construct response list
+ if ( xml && xml.documentElement /* #4958 */ ) {
+ responses.xml = xml;
+ }
+
+ // When requesting binary data, IE6-9 will throw an exception
+ // on any attempt to access responseText (#11426)
+ try {
+ responses.text = xhr.responseText;
+ } catch( _ ) {
+ }
+
+ // Firefox throws an exception when accessing
+ // statusText for faulty cross-domain requests
+ try {
+ statusText = xhr.statusText;
+ } catch( e ) {
+ // We normalize with Webkit giving an empty statusText
+ statusText = "";
+ }
+
+ // Filter status for non standard behaviors
+
+ // If the request is local and we have data: assume a success
+ // (success with no data won't get notified, that's the best we
+ // can do given current implementations)
+ if ( !status && s.isLocal && !s.crossDomain ) {
+ status = responses.text ? 200 : 404;
+ // IE - #1450: sometimes returns 1223 when it should be 204
+ } else if ( status === 1223 ) {
+ status = 204;
+ }
+ }
+ }
+ } catch( firefoxAccessException ) {
+ if ( !isAbort ) {
+ complete( -1, firefoxAccessException );
+ }
+ }
+
+ // Call complete if needed
+ if ( responses ) {
+ complete( status, statusText, responses, responseHeaders );
+ }
+ };
+
+ // if we're in sync mode or it's in cache
+ // and has been retrieved directly (IE6 & IE7)
+ // we need to manually fire the callback
+ if ( !s.async || xhr.readyState === 4 ) {
+ callback();
+ } else {
+ handle = ++xhrId;
+ if ( xhrOnUnloadAbort ) {
+ // Create the active xhrs callbacks list if needed
+ // and attach the unload handler
+ if ( !xhrCallbacks ) {
+ xhrCallbacks = {};
+ jQuery( window ).unload( xhrOnUnloadAbort );
+ }
+ // Add to list of active xhrs callbacks
+ xhrCallbacks[ handle ] = callback;
+ }
+ xhr.onreadystatechange = callback;
+ }
+ },
+
+ abort: function() {
+ if ( callback ) {
+ callback(0,1);
+ }
+ }
+ };
+ }
+ });
+}
+
+
+
+
+var elemdisplay = {},
+ iframe, iframeDoc,
+ rfxtypes = /^(?:toggle|show|hide)$/,
+ rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
+ timerId,
+ fxAttrs = [
+ // height animations
+ [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
+ // width animations
+ [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
+ // opacity animations
+ [ "opacity" ]
+ ],
+ fxNow;
+
+jQuery.fn.extend({
+ show: function( speed, easing, callback ) {
+ var elem, display;
+
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("show", 3), speed, easing, callback );
+
+ } else {
+ for ( var i = 0, j = this.length; i < j; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.style ) {
+ display = elem.style.display;
+
+ // Reset the inline display of this element to learn if it is
+ // being hidden by cascaded rules or not
+ if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
+ display = elem.style.display = "";
+ }
+
+ // Set elements which have been overridden with display: none
+ // in a stylesheet to whatever the default browser style is
+ // for such an element
+ if ( (display === "" && jQuery.css(elem, "display") === "none") ||
+ !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
+ jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
+ }
+ }
+ }
+
+ // Set the display of most of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.style ) {
+ display = elem.style.display;
+
+ if ( display === "" || display === "none" ) {
+ elem.style.display = jQuery._data( elem, "olddisplay" ) || "";
+ }
+ }
+ }
+
+ return this;
+ }
+ },
+
+ hide: function( speed, easing, callback ) {
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("hide", 3), speed, easing, callback);
+
+ } else {
+ var elem, display,
+ i = 0,
+ j = this.length;
+
+ for ( ; i < j; i++ ) {
+ elem = this[i];
+ if ( elem.style ) {
+ display = jQuery.css( elem, "display" );
+
+ if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) {
+ jQuery._data( elem, "olddisplay", display );
+ }
+ }
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ if ( this[i].style ) {
+ this[i].style.display = "none";
+ }
+ }
+
+ return this;
+ }
+ },
+
+ // Save the old toggle function
+ _toggle: jQuery.fn.toggle,
+
+ toggle: function( fn, fn2, callback ) {
+ var bool = typeof fn === "boolean";
+
+ if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
+ this._toggle.apply( this, arguments );
+
+ } else if ( fn == null || bool ) {
+ this.each(function() {
+ var state = bool ? fn : jQuery(this).is(":hidden");
+ jQuery(this)[ state ? "show" : "hide" ]();
+ });
+
+ } else {
+ this.animate(genFx("toggle", 3), fn, fn2, callback);
+ }
+
+ return this;
+ },
+
+ fadeTo: function( speed, to, easing, callback ) {
+ return this.filter(":hidden").css("opacity", 0).show().end()
+ .animate({opacity: to}, speed, easing, callback);
+ },
+
+ animate: function( prop, speed, easing, callback ) {
+ var optall = jQuery.speed( speed, easing, callback );
+
+ if ( jQuery.isEmptyObject( prop ) ) {
+ return this.each( optall.complete, [ false ] );
+ }
+
+ // Do not change referenced properties as per-property easing will be lost
+ prop = jQuery.extend( {}, prop );
+
+ function doAnimation() {
+ // XXX 'this' does not always have a nodeName when running the
+ // test suite
+
+ if ( optall.queue === false ) {
+ jQuery._mark( this );
+ }
+
+ var opt = jQuery.extend( {}, optall ),
+ isElement = this.nodeType === 1,
+ hidden = isElement && jQuery(this).is(":hidden"),
+ name, val, p, e, hooks, replace,
+ parts, start, end, unit,
+ method;
+
+ // will store per property easing and be used to determine when an animation is complete
+ opt.animatedProperties = {};
+
+ // first pass over propertys to expand / normalize
+ for ( p in prop ) {
+ name = jQuery.camelCase( p );
+ if ( p !== name ) {
+ prop[ name ] = prop[ p ];
+ delete prop[ p ];
+ }
+
+ if ( ( hooks = jQuery.cssHooks[ name ] ) && "expand" in hooks ) {
+ replace = hooks.expand( prop[ name ] );
+ delete prop[ name ];
+
+ // not quite $.extend, this wont overwrite keys already present.
+ // also - reusing 'p' from above because we have the correct "name"
+ for ( p in replace ) {
+ if ( ! ( p in prop ) ) {
+ prop[ p ] = replace[ p ];
+ }
+ }
+ }
+ }
+
+ for ( name in prop ) {
+ val = prop[ name ];
+ // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
+ if ( jQuery.isArray( val ) ) {
+ opt.animatedProperties[ name ] = val[ 1 ];
+ val = prop[ name ] = val[ 0 ];
+ } else {
+ opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
+ }
+
+ if ( val === "hide" && hidden || val === "show" && !hidden ) {
+ return opt.complete.call( this );
+ }
+
+ if ( isElement && ( name === "height" || name === "width" ) ) {
+ // Make sure that nothing sneaks out
+ // Record all 3 overflow attributes because IE does not
+ // change the overflow attribute when overflowX and
+ // overflowY are set to the same value
+ opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
+
+ // Set display property to inline-block for height/width
+ // animations on inline elements that are having width/height animated
+ if ( jQuery.css( this, "display" ) === "inline" &&
+ jQuery.css( this, "float" ) === "none" ) {
+
+ // inline-level elements accept inline-block;
+ // block-level elements need to be inline with layout
+ if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) {
+ this.style.display = "inline-block";
+
+ } else {
+ this.style.zoom = 1;
+ }
+ }
+ }
+ }
+
+ if ( opt.overflow != null ) {
+ this.style.overflow = "hidden";
+ }
+
+ for ( p in prop ) {
+ e = new jQuery.fx( this, opt, p );
+ val = prop[ p ];
+
+ if ( rfxtypes.test( val ) ) {
+
+ // Tracks whether to show or hide based on private
+ // data attached to the element
+ method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 );
+ if ( method ) {
+ jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" );
+ e[ method ]();
+ } else {
+ e[ val ]();
+ }
+
+ } else {
+ parts = rfxnum.exec( val );
+ start = e.cur();
+
+ if ( parts ) {
+ end = parseFloat( parts[2] );
+ unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
+
+ // We need to compute starting value
+ if ( unit !== "px" ) {
+ jQuery.style( this, p, (end || 1) + unit);
+ start = ( (end || 1) / e.cur() ) * start;
+ jQuery.style( this, p, start + unit);
+ }
+
+ // If a +=/-= token was provided, we're doing a relative animation
+ if ( parts[1] ) {
+ end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
+ }
+
+ e.custom( start, end, unit );
+
+ } else {
+ e.custom( start, val, "" );
+ }
+ }
+ }
+
+ // For JS strict compliance
+ return true;
+ }
+
+ return optall.queue === false ?
+ this.each( doAnimation ) :
+ this.queue( optall.queue, doAnimation );
+ },
+
+ stop: function( type, clearQueue, gotoEnd ) {
+ if ( typeof type !== "string" ) {
+ gotoEnd = clearQueue;
+ clearQueue = type;
+ type = undefined;
+ }
+ if ( clearQueue && type !== false ) {
+ this.queue( type || "fx", [] );
+ }
+
+ return this.each(function() {
+ var index,
+ hadTimers = false,
+ timers = jQuery.timers,
+ data = jQuery._data( this );
+
+ // clear marker counters if we know they won't be
+ if ( !gotoEnd ) {
+ jQuery._unmark( true, this );
+ }
+
+ function stopQueue( elem, data, index ) {
+ var hooks = data[ index ];
+ jQuery.removeData( elem, index, true );
+ hooks.stop( gotoEnd );
+ }
+
+ if ( type == null ) {
+ for ( index in data ) {
+ if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) {
+ stopQueue( this, data, index );
+ }
+ }
+ } else if ( data[ index = type + ".run" ] && data[ index ].stop ){
+ stopQueue( this, data, index );
+ }
+
+ for ( index = timers.length; index--; ) {
+ if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+ if ( gotoEnd ) {
+
+ // force the next step to be the last
+ timers[ index ]( true );
+ } else {
+ timers[ index ].saveState();
+ }
+ hadTimers = true;
+ timers.splice( index, 1 );
+ }
+ }
+
+ // start the next in the queue if the last step wasn't forced
+ // timers currently will call their complete callbacks, which will dequeue
+ // but only if they were gotoEnd
+ if ( !( gotoEnd && hadTimers ) ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ }
+
+});
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+ setTimeout( clearFxNow, 0 );
+ return ( fxNow = jQuery.now() );
+}
+
+function clearFxNow() {
+ fxNow = undefined;
+}
+
+// Generate parameters to create a standard animation
+function genFx( type, num ) {
+ var obj = {};
+
+ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {
+ obj[ this ] = type;
+ });
+
+ return obj;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx( "show", 1 ),
+ slideUp: genFx( "hide", 1 ),
+ slideToggle: genFx( "toggle", 1 ),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" },
+ fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return this.animate( props, speed, easing, callback );
+ };
+});
+
+jQuery.extend({
+ speed: function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+ // normalize opt.queue - true/undefined/null -> "fx"
+ if ( opt.queue == null || opt.queue === true ) {
+ opt.queue = "fx";
+ }
+
+ // Queueing
+ opt.old = opt.complete;
+
+ opt.complete = function( noUnmark ) {
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
+ }
+
+ if ( opt.queue ) {
+ jQuery.dequeue( this, opt.queue );
+ } else if ( noUnmark !== false ) {
+ jQuery._unmark( this );
+ }
+ };
+
+ return opt;
+ },
+
+ easing: {
+ linear: function( p ) {
+ return p;
+ },
+ swing: function( p ) {
+ return ( -Math.cos( p*Math.PI ) / 2 ) + 0.5;
+ }
+ },
+
+ timers: [],
+
+ fx: function( elem, options, prop ) {
+ this.options = options;
+ this.elem = elem;
+ this.prop = prop;
+
+ options.orig = options.orig || {};
+ }
+
+});
+
+jQuery.fx.prototype = {
+ // Simple function for setting a style value
+ update: function() {
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
+ }
+
+ ( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );
+ },
+
+ // Get the current size
+ cur: function() {
+ if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) {
+ return this.elem[ this.prop ];
+ }
+
+ var parsed,
+ r = jQuery.css( this.elem, this.prop );
+ // Empty strings, null, undefined and "auto" are converted to 0,
+ // complex values such as "rotate(1rad)" are returned as is,
+ // simple values such as "10px" are parsed to Float.
+ return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
+ },
+
+ // Start an animation from one number to another
+ custom: function( from, to, unit ) {
+ var self = this,
+ fx = jQuery.fx;
+
+ this.startTime = fxNow || createFxNow();
+ this.end = to;
+ this.now = this.start = from;
+ this.pos = this.state = 0;
+ this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
+
+ function t( gotoEnd ) {
+ return self.step( gotoEnd );
+ }
+
+ t.queue = this.options.queue;
+ t.elem = this.elem;
+ t.saveState = function() {
+ if ( jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) {
+ if ( self.options.hide ) {
+ jQuery._data( self.elem, "fxshow" + self.prop, self.start );
+ } else if ( self.options.show ) {
+ jQuery._data( self.elem, "fxshow" + self.prop, self.end );
+ }
+ }
+ };
+
+ if ( t() && jQuery.timers.push(t) && !timerId ) {
+ timerId = setInterval( fx.tick, fx.interval );
+ }
+ },
+
+ // Simple 'show' function
+ show: function() {
+ var dataShow = jQuery._data( this.elem, "fxshow" + this.prop );
+
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );
+ this.options.show = true;
+
+ // Begin the animation
+ // Make sure that we start at a small width/height to avoid any flash of content
+ if ( dataShow !== undefined ) {
+ // This show is picking up where a previous hide or show left off
+ this.custom( this.cur(), dataShow );
+ } else {
+ this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() );
+ }
+
+ // Start by showing the element
+ jQuery( this.elem ).show();
+ },
+
+ // Simple 'hide' function
+ hide: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop );
+ this.options.hide = true;
+
+ // Begin the animation
+ this.custom( this.cur(), 0 );
+ },
+
+ // Each step of an animation
+ step: function( gotoEnd ) {
+ var p, n, complete,
+ t = fxNow || createFxNow(),
+ done = true,
+ elem = this.elem,
+ options = this.options;
+
+ if ( gotoEnd || t >= options.duration + this.startTime ) {
+ this.now = this.end;
+ this.pos = this.state = 1;
+ this.update();
+
+ options.animatedProperties[ this.prop ] = true;
+
+ for ( p in options.animatedProperties ) {
+ if ( options.animatedProperties[ p ] !== true ) {
+ done = false;
+ }
+ }
+
+ if ( done ) {
+ // Reset the overflow
+ if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
+
+ jQuery.each( [ "", "X", "Y" ], function( index, value ) {
+ elem.style[ "overflow" + value ] = options.overflow[ index ];
+ });
+ }
+
+ // Hide the element if the "hide" operation was done
+ if ( options.hide ) {
+ jQuery( elem ).hide();
+ }
+
+ // Reset the properties, if the item has been hidden or shown
+ if ( options.hide || options.show ) {
+ for ( p in options.animatedProperties ) {
+ jQuery.style( elem, p, options.orig[ p ] );
+ jQuery.removeData( elem, "fxshow" + p, true );
+ // Toggle data is no longer needed
+ jQuery.removeData( elem, "toggle" + p, true );
+ }
+ }
+
+ // Execute the complete function
+ // in the event that the complete function throws an exception
+ // we must ensure it won't be called twice. #5684
+
+ complete = options.complete;
+ if ( complete ) {
+
+ options.complete = false;
+ complete.call( elem );
+ }
+ }
+
+ return false;
+
+ } else {
+ // classical easing cannot be used with an Infinity duration
+ if ( options.duration == Infinity ) {
+ this.now = t;
+ } else {
+ n = t - this.startTime;
+ this.state = n / options.duration;
+
+ // Perform the easing function, defaults to swing
+ this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration );
+ this.now = this.start + ( (this.end - this.start) * this.pos );
+ }
+ // Perform the next step of the animation
+ this.update();
+ }
+
+ return true;
+ }
+};
+
+jQuery.extend( jQuery.fx, {
+ tick: function() {
+ var timer,
+ timers = jQuery.timers,
+ i = 0;
+
+ for ( ; i < timers.length; i++ ) {
+ timer = timers[ i ];
+ // Checks the timer has not already been removed
+ if ( !timer() && timers[ i ] === timer ) {
+ timers.splice( i--, 1 );
+ }
+ }
+
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+ },
+
+ interval: 13,
+
+ stop: function() {
+ clearInterval( timerId );
+ timerId = null;
+ },
+
+ speeds: {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+ },
+
+ step: {
+ opacity: function( fx ) {
+ jQuery.style( fx.elem, "opacity", fx.now );
+ },
+
+ _default: function( fx ) {
+ if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
+ fx.elem.style[ fx.prop ] = fx.now + fx.unit;
+ } else {
+ fx.elem[ fx.prop ] = fx.now;
+ }
+ }
+ }
+});
+
+// Ensure props that can't be negative don't go there on undershoot easing
+jQuery.each( fxAttrs.concat.apply( [], fxAttrs ), function( i, prop ) {
+ // exclude marginTop, marginLeft, marginBottom and marginRight from this list
+ if ( prop.indexOf( "margin" ) ) {
+ jQuery.fx.step[ prop ] = function( fx ) {
+ jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit );
+ };
+ }
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+ };
+}
+
+// Try to restore the default display value of an element
+function defaultDisplay( nodeName ) {
+
+ if ( !elemdisplay[ nodeName ] ) {
+
+ var body = document.body,
+ elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
+ display = elem.css( "display" );
+ elem.remove();
+
+ // If the simple way fails,
+ // get element's real default display by attaching it to a temp iframe
+ if ( display === "none" || display === "" ) {
+ // No iframe to use yet, so create it
+ if ( !iframe ) {
+ iframe = document.createElement( "iframe" );
+ iframe.frameBorder = iframe.width = iframe.height = 0;
+ }
+
+ body.appendChild( iframe );
+
+ // Create a cacheable copy of the iframe document on first call.
+ // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
+ // document to it; WebKit & Firefox won't allow reusing the iframe document.
+ if ( !iframeDoc || !iframe.createElement ) {
+ iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
+ iframeDoc.write( ( jQuery.support.boxModel ? "<!doctype html>" : "" ) + "<html><body>" );
+ iframeDoc.close();
+ }
+
+ elem = iframeDoc.createElement( nodeName );
+
+ iframeDoc.body.appendChild( elem );
+
+ display = jQuery.css( elem, "display" );
+ body.removeChild( iframe );
+ }
+
+ // Store the correct default display
+ elemdisplay[ nodeName ] = display;
+ }
+
+ return elemdisplay[ nodeName ];
+}
+
+
+
+
+var getOffset,
+ rtable = /^t(?:able|d|h)$/i,
+ rroot = /^(?:body|html)$/i;
+
+if ( "getBoundingClientRect" in document.documentElement ) {
+ getOffset = function( elem, doc, docElem, box ) {
+ try {
+ box = elem.getBoundingClientRect();
+ } catch(e) {}
+
+ // Make sure we're not dealing with a disconnected DOM node
+ if ( !box || !jQuery.contains( docElem, elem ) ) {
+ return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
+ }
+
+ var body = doc.body,
+ win = getWindow( doc ),
+ clientTop = docElem.clientTop || body.clientTop || 0,
+ clientLeft = docElem.clientLeft || body.clientLeft || 0,
+ scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop,
+ scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft,
+ top = box.top + scrollTop - clientTop,
+ left = box.left + scrollLeft - clientLeft;
+
+ return { top: top, left: left };
+ };
+
+} else {
+ getOffset = function( elem, doc, docElem ) {
+ var computedStyle,
+ offsetParent = elem.offsetParent,
+ prevOffsetParent = elem,
+ body = doc.body,
+ defaultView = doc.defaultView,
+ prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
+ top = elem.offsetTop,
+ left = elem.offsetLeft;
+
+ while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
+ if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
+ break;
+ }
+
+ computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
+ top -= elem.scrollTop;
+ left -= elem.scrollLeft;
+
+ if ( elem === offsetParent ) {
+ top += elem.offsetTop;
+ left += elem.offsetLeft;
+
+ if ( jQuery.support.doesNotAddBorder && !(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevOffsetParent = offsetParent;
+ offsetParent = elem.offsetParent;
+ }
+
+ if ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevComputedStyle = computedStyle;
+ }
+
+ if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
+ top += body.offsetTop;
+ left += body.offsetLeft;
+ }
+
+ if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) {
+ top += Math.max( docElem.scrollTop, body.scrollTop );
+ left += Math.max( docElem.scrollLeft, body.scrollLeft );
+ }
+
+ return { top: top, left: left };
+ };
+}
+
+jQuery.fn.offset = function( options ) {
+ if ( arguments.length ) {
+ return options === undefined ?
+ this :
+ this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ var elem = this[0],
+ doc = elem && elem.ownerDocument;
+
+ if ( !doc ) {
+ return null;
+ }
+
+ if ( elem === doc.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ return getOffset( elem, doc, doc.documentElement );
+};
+
+jQuery.offset = {
+
+ bodyOffset: function( body ) {
+ var top = body.offsetTop,
+ left = body.offsetLeft;
+
+ if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) {
+ top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
+ left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
+ }
+
+ return { top: top, left: left };
+ },
+
+ setOffset: function( elem, options, i ) {
+ var position = jQuery.css( elem, "position" );
+
+ // set position first, in-case top/left are set even on static elem
+ if ( position === "static" ) {
+ elem.style.position = "relative";
+ }
+
+ var curElem = jQuery( elem ),
+ curOffset = curElem.offset(),
+ curCSSTop = jQuery.css( elem, "top" ),
+ curCSSLeft = jQuery.css( elem, "left" ),
+ calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+ props = {}, curPosition = {}, curTop, curLeft;
+
+ // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+ if ( calculatePosition ) {
+ curPosition = curElem.position();
+ curTop = curPosition.top;
+ curLeft = curPosition.left;
+ } else {
+ curTop = parseFloat( curCSSTop ) || 0;
+ curLeft = parseFloat( curCSSLeft ) || 0;
+ }
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ if ( options.top != null ) {
+ props.top = ( options.top - curOffset.top ) + curTop;
+ }
+ if ( options.left != null ) {
+ props.left = ( options.left - curOffset.left ) + curLeft;
+ }
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+
+jQuery.fn.extend({
+
+ position: function() {
+ if ( !this[0] ) {
+ return null;
+ }
+
+ var elem = this[0],
+
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent(),
+
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
+ offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
+
+ // Add offsetParent borders
+ parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
+ parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
+
+ // Subtract the two offsets
+ return {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || document.body;
+ while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent;
+ });
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
+ var top = /Y/.test( prop );
+
+ jQuery.fn[ method ] = function( val ) {
+ return jQuery.access( this, function( elem, method, val ) {
+ var win = getWindow( elem );
+
+ if ( val === undefined ) {
+ return win ? (prop in win) ? win[ prop ] :
+ jQuery.support.boxModel && win.document.documentElement[ method ] ||
+ win.document.body[ method ] :
+ elem[ method ];
+ }
+
+ if ( win ) {
+ win.scrollTo(
+ !top ? val : jQuery( win ).scrollLeft(),
+ top ? val : jQuery( win ).scrollTop()
+ );
+
+ } else {
+ elem[ method ] = val;
+ }
+ }, method, val, arguments.length, null );
+ };
+});
+
+function getWindow( elem ) {
+ return jQuery.isWindow( elem ) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+
+
+
+
+// Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+ var clientProp = "client" + name,
+ scrollProp = "scroll" + name,
+ offsetProp = "offset" + name;
+
+ // innerHeight and innerWidth
+ jQuery.fn[ "inner" + name ] = function() {
+ var elem = this[0];
+ return elem ?
+ elem.style ?
+ parseFloat( jQuery.css( elem, type, "padding" ) ) :
+ this[ type ]() :
+ null;
+ };
+
+ // outerHeight and outerWidth
+ jQuery.fn[ "outer" + name ] = function( margin ) {
+ var elem = this[0];
+ return elem ?
+ elem.style ?
+ parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) :
+ this[ type ]() :
+ null;
+ };
+
+ jQuery.fn[ type ] = function( value ) {
+ return jQuery.access( this, function( elem, type, value ) {
+ var doc, docElemProp, orig, ret;
+
+ if ( jQuery.isWindow( elem ) ) {
+ // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
+ doc = elem.document;
+ docElemProp = doc.documentElement[ clientProp ];
+ return jQuery.support.boxModel && docElemProp ||
+ doc.body && doc.body[ clientProp ] || docElemProp;
+ }
+
+ // Get document width or height
+ if ( elem.nodeType === 9 ) {
+ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+ doc = elem.documentElement;
+
+ // when a window > document, IE6 reports a offset[Width/Height] > client[Width/Height]
+ // so we can't use max, as it'll choose the incorrect offset[Width/Height]
+ // instead we use the correct client[Width/Height]
+ // support:IE6
+ if ( doc[ clientProp ] >= doc[ scrollProp ] ) {
+ return doc[ clientProp ];
+ }
+
+ return Math.max(
+ elem.body[ scrollProp ], doc[ scrollProp ],
+ elem.body[ offsetProp ], doc[ offsetProp ]
+ );
+ }
+
+ // Get width or height on the element
+ if ( value === undefined ) {
+ orig = jQuery.css( elem, type );
+ ret = parseFloat( orig );
+ return jQuery.isNumeric( ret ) ? ret : orig;
+ }
+
+ // Set the width or height on the element
+ jQuery( elem ).css( type, value );
+ }, type, value, arguments.length, null );
+ };
+});
+
+
+
+
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+// Expose jQuery as an AMD module, but only for AMD loaders that
+// understand the issues with loading multiple versions of jQuery
+// in a page that all might call define(). The loader will indicate
+// they have special allowances for multiple jQuery versions by
+// specifying define.amd.jQuery = true. Register as a named module,
+// since jQuery can be concatenated with other files that may use define,
+// but not use a proper concatenation script that understands anonymous
+// AMD modules. A named AMD is safest and most robust way to register.
+// Lowercase jquery is used because AMD module names are derived from
+// file names, and jQuery is normally delivered in a lowercase file name.
+// Do this after creating the global so that if an AMD module wants to call
+// noConflict to hide this version of jQuery, it will work.
+if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
+ define( "jquery", [], function () { return jQuery; } );
+}
+
+
+
+})( window );
diff --git a/storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery-ui-1.8.18.custom.js b/storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery-ui-1.8.18.custom.js
new file mode 100644
index 00000000000..d4444b2bd11
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery-ui-1.8.18.custom.js
@@ -0,0 +1,11802 @@
+/*!
+ * jQuery UI 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function( $, undefined ) {
+
+// prevent duplicate loading
+// this is only a problem because we proxy existing functions
+// and we don't want to double proxy them
+$.ui = $.ui || {};
+if ( $.ui.version ) {
+ return;
+}
+
+$.extend( $.ui, {
+ version: "1.8.18",
+
+ keyCode: {
+ ALT: 18,
+ BACKSPACE: 8,
+ CAPS_LOCK: 20,
+ COMMA: 188,
+ COMMAND: 91,
+ COMMAND_LEFT: 91, // COMMAND
+ COMMAND_RIGHT: 93,
+ CONTROL: 17,
+ DELETE: 46,
+ DOWN: 40,
+ END: 35,
+ ENTER: 13,
+ ESCAPE: 27,
+ HOME: 36,
+ INSERT: 45,
+ LEFT: 37,
+ MENU: 93, // COMMAND_RIGHT
+ NUMPAD_ADD: 107,
+ NUMPAD_DECIMAL: 110,
+ NUMPAD_DIVIDE: 111,
+ NUMPAD_ENTER: 108,
+ NUMPAD_MULTIPLY: 106,
+ NUMPAD_SUBTRACT: 109,
+ PAGE_DOWN: 34,
+ PAGE_UP: 33,
+ PERIOD: 190,
+ RIGHT: 39,
+ SHIFT: 16,
+ SPACE: 32,
+ TAB: 9,
+ UP: 38,
+ WINDOWS: 91 // COMMAND
+ }
+});
+
+// plugins
+$.fn.extend({
+ propAttr: $.fn.prop || $.fn.attr,
+
+ _focus: $.fn.focus,
+ focus: function( delay, fn ) {
+ return typeof delay === "number" ?
+ this.each(function() {
+ var elem = this;
+ setTimeout(function() {
+ $( elem ).focus();
+ if ( fn ) {
+ fn.call( elem );
+ }
+ }, delay );
+ }) :
+ this._focus.apply( this, arguments );
+ },
+
+ scrollParent: function() {
+ var scrollParent;
+ if (($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
+ scrollParent = this.parents().filter(function() {
+ return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+ }).eq(0);
+ } else {
+ scrollParent = this.parents().filter(function() {
+ return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
+ }).eq(0);
+ }
+
+ return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
+ },
+
+ zIndex: function( zIndex ) {
+ if ( zIndex !== undefined ) {
+ return this.css( "zIndex", zIndex );
+ }
+
+ if ( this.length ) {
+ var elem = $( this[ 0 ] ), position, value;
+ while ( elem.length && elem[ 0 ] !== document ) {
+ // Ignore z-index if position is set to a value where z-index is ignored by the browser
+ // This makes behavior of this function consistent across browsers
+ // WebKit always returns auto if the element is positioned
+ position = elem.css( "position" );
+ if ( position === "absolute" || position === "relative" || position === "fixed" ) {
+ // IE returns 0 when zIndex is not specified
+ // other browsers return a string
+ // we ignore the case of nested elements with an explicit value of 0
+ // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
+ value = parseInt( elem.css( "zIndex" ), 10 );
+ if ( !isNaN( value ) && value !== 0 ) {
+ return value;
+ }
+ }
+ elem = elem.parent();
+ }
+ }
+
+ return 0;
+ },
+
+ disableSelection: function() {
+ return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
+ ".ui-disableSelection", function( event ) {
+ event.preventDefault();
+ });
+ },
+
+ enableSelection: function() {
+ return this.unbind( ".ui-disableSelection" );
+ }
+});
+
+$.each( [ "Width", "Height" ], function( i, name ) {
+ var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
+ type = name.toLowerCase(),
+ orig = {
+ innerWidth: $.fn.innerWidth,
+ innerHeight: $.fn.innerHeight,
+ outerWidth: $.fn.outerWidth,
+ outerHeight: $.fn.outerHeight
+ };
+
+ function reduce( elem, size, border, margin ) {
+ $.each( side, function() {
+ size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0;
+ if ( border ) {
+ size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0;
+ }
+ if ( margin ) {
+ size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0;
+ }
+ });
+ return size;
+ }
+
+ $.fn[ "inner" + name ] = function( size ) {
+ if ( size === undefined ) {
+ return orig[ "inner" + name ].call( this );
+ }
+
+ return this.each(function() {
+ $( this ).css( type, reduce( this, size ) + "px" );
+ });
+ };
+
+ $.fn[ "outer" + name] = function( size, margin ) {
+ if ( typeof size !== "number" ) {
+ return orig[ "outer" + name ].call( this, size );
+ }
+
+ return this.each(function() {
+ $( this).css( type, reduce( this, size, true, margin ) + "px" );
+ });
+ };
+});
+
+// selectors
+function focusable( element, isTabIndexNotNaN ) {
+ var nodeName = element.nodeName.toLowerCase();
+ if ( "area" === nodeName ) {
+ var map = element.parentNode,
+ mapName = map.name,
+ img;
+ if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
+ return false;
+ }
+ img = $( "img[usemap=#" + mapName + "]" )[0];
+ return !!img && visible( img );
+ }
+ return ( /input|select|textarea|button|object/.test( nodeName )
+ ? !element.disabled
+ : "a" == nodeName
+ ? element.href || isTabIndexNotNaN
+ : isTabIndexNotNaN)
+ // the element and all of its ancestors must be visible
+ && visible( element );
+}
+
+function visible( element ) {
+ return !$( element ).parents().andSelf().filter(function() {
+ return $.curCSS( this, "visibility" ) === "hidden" ||
+ $.expr.filters.hidden( this );
+ }).length;
+}
+
+$.extend( $.expr[ ":" ], {
+ data: function( elem, i, match ) {
+ return !!$.data( elem, match[ 3 ] );
+ },
+
+ focusable: function( element ) {
+ return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
+ },
+
+ tabbable: function( element ) {
+ var tabIndex = $.attr( element, "tabindex" ),
+ isTabIndexNaN = isNaN( tabIndex );
+ return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
+ }
+});
+
+// support
+$(function() {
+ var body = document.body,
+ div = body.appendChild( div = document.createElement( "div" ) );
+
+ // access offsetHeight before setting the style to prevent a layout bug
+ // in IE 9 which causes the elemnt to continue to take up space even
+ // after it is removed from the DOM (#8026)
+ div.offsetHeight;
+
+ $.extend( div.style, {
+ minHeight: "100px",
+ height: "auto",
+ padding: 0,
+ borderWidth: 0
+ });
+
+ $.support.minHeight = div.offsetHeight === 100;
+ $.support.selectstart = "onselectstart" in div;
+
+ // set display to none to avoid a layout bug in IE
+ // http://dev.jquery.com/ticket/4014
+ body.removeChild( div ).style.display = "none";
+});
+
+
+
+
+
+// deprecated
+$.extend( $.ui, {
+ // $.ui.plugin is deprecated. Use the proxy pattern instead.
+ plugin: {
+ add: function( module, option, set ) {
+ var proto = $.ui[ module ].prototype;
+ for ( var i in set ) {
+ proto.plugins[ i ] = proto.plugins[ i ] || [];
+ proto.plugins[ i ].push( [ option, set[ i ] ] );
+ }
+ },
+ call: function( instance, name, args ) {
+ var set = instance.plugins[ name ];
+ if ( !set || !instance.element[ 0 ].parentNode ) {
+ return;
+ }
+
+ for ( var i = 0; i < set.length; i++ ) {
+ if ( instance.options[ set[ i ][ 0 ] ] ) {
+ set[ i ][ 1 ].apply( instance.element, args );
+ }
+ }
+ }
+ },
+
+ // will be deprecated when we switch to jQuery 1.4 - use jQuery.contains()
+ contains: function( a, b ) {
+ return document.compareDocumentPosition ?
+ a.compareDocumentPosition( b ) & 16 :
+ a !== b && a.contains( b );
+ },
+
+ // only used by resizable
+ hasScroll: function( el, a ) {
+
+ //If overflow is hidden, the element might have extra content, but the user wants to hide it
+ if ( $( el ).css( "overflow" ) === "hidden") {
+ return false;
+ }
+
+ var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
+ has = false;
+
+ if ( el[ scroll ] > 0 ) {
+ return true;
+ }
+
+ // TODO: determine which cases actually cause this to happen
+ // if the element doesn't have the scroll set, see if it's possible to
+ // set the scroll
+ el[ scroll ] = 1;
+ has = ( el[ scroll ] > 0 );
+ el[ scroll ] = 0;
+ return has;
+ },
+
+ // these are odd functions, fix the API or move into individual plugins
+ isOverAxis: function( x, reference, size ) {
+ //Determines when x coordinate is over "b" element axis
+ return ( x > reference ) && ( x < ( reference + size ) );
+ },
+ isOver: function( y, x, top, left, height, width ) {
+ //Determines when x, y coordinates is over "b" element
+ return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
+ }
+});
+
+})( jQuery );
+/*!
+ * jQuery UI Widget 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function( $, undefined ) {
+
+// jQuery 1.4+
+if ( $.cleanData ) {
+ var _cleanData = $.cleanData;
+ $.cleanData = function( elems ) {
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ try {
+ $( elem ).triggerHandler( "remove" );
+ // http://bugs.jquery.com/ticket/8235
+ } catch( e ) {}
+ }
+ _cleanData( elems );
+ };
+} else {
+ var _remove = $.fn.remove;
+ $.fn.remove = function( selector, keepData ) {
+ return this.each(function() {
+ if ( !keepData ) {
+ if ( !selector || $.filter( selector, [ this ] ).length ) {
+ $( "*", this ).add( [ this ] ).each(function() {
+ try {
+ $( this ).triggerHandler( "remove" );
+ // http://bugs.jquery.com/ticket/8235
+ } catch( e ) {}
+ });
+ }
+ }
+ return _remove.call( $(this), selector, keepData );
+ });
+ };
+}
+
+$.widget = function( name, base, prototype ) {
+ var namespace = name.split( "." )[ 0 ],
+ fullName;
+ name = name.split( "." )[ 1 ];
+ fullName = namespace + "-" + name;
+
+ if ( !prototype ) {
+ prototype = base;
+ base = $.Widget;
+ }
+
+ // create selector for plugin
+ $.expr[ ":" ][ fullName ] = function( elem ) {
+ return !!$.data( elem, name );
+ };
+
+ $[ namespace ] = $[ namespace ] || {};
+ $[ namespace ][ name ] = function( options, element ) {
+ // allow instantiation without initializing for simple inheritance
+ if ( arguments.length ) {
+ this._createWidget( options, element );
+ }
+ };
+
+ var basePrototype = new base();
+ // we need to make the options hash a property directly on the new instance
+ // otherwise we'll modify the options hash on the prototype that we're
+ // inheriting from
+// $.each( basePrototype, function( key, val ) {
+// if ( $.isPlainObject(val) ) {
+// basePrototype[ key ] = $.extend( {}, val );
+// }
+// });
+ basePrototype.options = $.extend( true, {}, basePrototype.options );
+ $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
+ namespace: namespace,
+ widgetName: name,
+ widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
+ widgetBaseClass: fullName
+ }, prototype );
+
+ $.widget.bridge( name, $[ namespace ][ name ] );
+};
+
+$.widget.bridge = function( name, object ) {
+ $.fn[ name ] = function( options ) {
+ var isMethodCall = typeof options === "string",
+ args = Array.prototype.slice.call( arguments, 1 ),
+ returnValue = this;
+
+ // allow multiple hashes to be passed on init
+ options = !isMethodCall && args.length ?
+ $.extend.apply( null, [ true, options ].concat(args) ) :
+ options;
+
+ // prevent calls to internal methods
+ if ( isMethodCall && options.charAt( 0 ) === "_" ) {
+ return returnValue;
+ }
+
+ if ( isMethodCall ) {
+ this.each(function() {
+ var instance = $.data( this, name ),
+ methodValue = instance && $.isFunction( instance[options] ) ?
+ instance[ options ].apply( instance, args ) :
+ instance;
+ // TODO: add this back in 1.9 and use $.error() (see #5972)
+// if ( !instance ) {
+// throw "cannot call methods on " + name + " prior to initialization; " +
+// "attempted to call method '" + options + "'";
+// }
+// if ( !$.isFunction( instance[options] ) ) {
+// throw "no such method '" + options + "' for " + name + " widget instance";
+// }
+// var methodValue = instance[ options ].apply( instance, args );
+ if ( methodValue !== instance && methodValue !== undefined ) {
+ returnValue = methodValue;
+ return false;
+ }
+ });
+ } else {
+ this.each(function() {
+ var instance = $.data( this, name );
+ if ( instance ) {
+ instance.option( options || {} )._init();
+ } else {
+ $.data( this, name, new object( options, this ) );
+ }
+ });
+ }
+
+ return returnValue;
+ };
+};
+
+$.Widget = function( options, element ) {
+ // allow instantiation without initializing for simple inheritance
+ if ( arguments.length ) {
+ this._createWidget( options, element );
+ }
+};
+
+$.Widget.prototype = {
+ widgetName: "widget",
+ widgetEventPrefix: "",
+ options: {
+ disabled: false
+ },
+ _createWidget: function( options, element ) {
+ // $.widget.bridge stores the plugin instance, but we do it anyway
+ // so that it's stored even before the _create function runs
+ $.data( element, this.widgetName, this );
+ this.element = $( element );
+ this.options = $.extend( true, {},
+ this.options,
+ this._getCreateOptions(),
+ options );
+
+ var self = this;
+ this.element.bind( "remove." + this.widgetName, function() {
+ self.destroy();
+ });
+
+ this._create();
+ this._trigger( "create" );
+ this._init();
+ },
+ _getCreateOptions: function() {
+ return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
+ },
+ _create: function() {},
+ _init: function() {},
+
+ destroy: function() {
+ this.element
+ .unbind( "." + this.widgetName )
+ .removeData( this.widgetName );
+ this.widget()
+ .unbind( "." + this.widgetName )
+ .removeAttr( "aria-disabled" )
+ .removeClass(
+ this.widgetBaseClass + "-disabled " +
+ "ui-state-disabled" );
+ },
+
+ widget: function() {
+ return this.element;
+ },
+
+ option: function( key, value ) {
+ var options = key;
+
+ if ( arguments.length === 0 ) {
+ // don't return a reference to the internal hash
+ return $.extend( {}, this.options );
+ }
+
+ if (typeof key === "string" ) {
+ if ( value === undefined ) {
+ return this.options[ key ];
+ }
+ options = {};
+ options[ key ] = value;
+ }
+
+ this._setOptions( options );
+
+ return this;
+ },
+ _setOptions: function( options ) {
+ var self = this;
+ $.each( options, function( key, value ) {
+ self._setOption( key, value );
+ });
+
+ return this;
+ },
+ _setOption: function( key, value ) {
+ this.options[ key ] = value;
+
+ if ( key === "disabled" ) {
+ this.widget()
+ [ value ? "addClass" : "removeClass"](
+ this.widgetBaseClass + "-disabled" + " " +
+ "ui-state-disabled" )
+ .attr( "aria-disabled", value );
+ }
+
+ return this;
+ },
+
+ enable: function() {
+ return this._setOption( "disabled", false );
+ },
+ disable: function() {
+ return this._setOption( "disabled", true );
+ },
+
+ _trigger: function( type, event, data ) {
+ var prop, orig,
+ callback = this.options[ type ];
+
+ data = data || {};
+ event = $.Event( event );
+ event.type = ( type === this.widgetEventPrefix ?
+ type :
+ this.widgetEventPrefix + type ).toLowerCase();
+ // the original event may come from any element
+ // so we need to reset the target on the new event
+ event.target = this.element[ 0 ];
+
+ // copy original event properties over to the new event
+ orig = event.originalEvent;
+ if ( orig ) {
+ for ( prop in orig ) {
+ if ( !( prop in event ) ) {
+ event[ prop ] = orig[ prop ];
+ }
+ }
+ }
+
+ this.element.trigger( event, data );
+
+ return !( $.isFunction(callback) &&
+ callback.call( this.element[0], event, data ) === false ||
+ event.isDefaultPrevented() );
+ }
+};
+
+})( jQuery );
+/*!
+ * jQuery UI Mouse 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+var mouseHandled = false;
+$( document ).mouseup( function( e ) {
+ mouseHandled = false;
+});
+
+$.widget("ui.mouse", {
+ options: {
+ cancel: ':input,option',
+ distance: 1,
+ delay: 0
+ },
+ _mouseInit: function() {
+ var self = this;
+
+ this.element
+ .bind('mousedown.'+this.widgetName, function(event) {
+ return self._mouseDown(event);
+ })
+ .bind('click.'+this.widgetName, function(event) {
+ if (true === $.data(event.target, self.widgetName + '.preventClickEvent')) {
+ $.removeData(event.target, self.widgetName + '.preventClickEvent');
+ event.stopImmediatePropagation();
+ return false;
+ }
+ });
+
+ this.started = false;
+ },
+
+ // TODO: make sure destroying one instance of mouse doesn't mess with
+ // other instances of mouse
+ _mouseDestroy: function() {
+ this.element.unbind('.'+this.widgetName);
+ },
+
+ _mouseDown: function(event) {
+ // don't let more than one widget handle mouseStart
+ if( mouseHandled ) { return };
+
+ // we may have missed mouseup (out of window)
+ (this._mouseStarted && this._mouseUp(event));
+
+ this._mouseDownEvent = event;
+
+ var self = this,
+ btnIsLeft = (event.which == 1),
+ // event.target.nodeName works around a bug in IE 8 with
+ // disabled inputs (#7620)
+ elIsCancel = (typeof this.options.cancel == "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
+ if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
+ return true;
+ }
+
+ this.mouseDelayMet = !this.options.delay;
+ if (!this.mouseDelayMet) {
+ this._mouseDelayTimer = setTimeout(function() {
+ self.mouseDelayMet = true;
+ }, this.options.delay);
+ }
+
+ if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+ this._mouseStarted = (this._mouseStart(event) !== false);
+ if (!this._mouseStarted) {
+ event.preventDefault();
+ return true;
+ }
+ }
+
+ // Click event may never have fired (Gecko & Opera)
+ if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) {
+ $.removeData(event.target, this.widgetName + '.preventClickEvent');
+ }
+
+ // these delegates are required to keep context
+ this._mouseMoveDelegate = function(event) {
+ return self._mouseMove(event);
+ };
+ this._mouseUpDelegate = function(event) {
+ return self._mouseUp(event);
+ };
+ $(document)
+ .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+ .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+ event.preventDefault();
+
+ mouseHandled = true;
+ return true;
+ },
+
+ _mouseMove: function(event) {
+ // IE mouseup check - mouseup happened when mouse was out of window
+ if ($.browser.msie && !(document.documentMode >= 9) && !event.button) {
+ return this._mouseUp(event);
+ }
+
+ if (this._mouseStarted) {
+ this._mouseDrag(event);
+ return event.preventDefault();
+ }
+
+ if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
+ this._mouseStarted =
+ (this._mouseStart(this._mouseDownEvent, event) !== false);
+ (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
+ }
+
+ return !this._mouseStarted;
+ },
+
+ _mouseUp: function(event) {
+ $(document)
+ .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
+ .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
+
+ if (this._mouseStarted) {
+ this._mouseStarted = false;
+
+ if (event.target == this._mouseDownEvent.target) {
+ $.data(event.target, this.widgetName + '.preventClickEvent', true);
+ }
+
+ this._mouseStop(event);
+ }
+
+ return false;
+ },
+
+ _mouseDistanceMet: function(event) {
+ return (Math.max(
+ Math.abs(this._mouseDownEvent.pageX - event.pageX),
+ Math.abs(this._mouseDownEvent.pageY - event.pageY)
+ ) >= this.options.distance
+ );
+ },
+
+ _mouseDelayMet: function(event) {
+ return this.mouseDelayMet;
+ },
+
+ // These are placeholder methods, to be overriden by extending plugin
+ _mouseStart: function(event) {},
+ _mouseDrag: function(event) {},
+ _mouseStop: function(event) {},
+ _mouseCapture: function(event) { return true; }
+});
+
+})(jQuery);
+/*
+ * jQuery UI Draggable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+$.widget("ui.draggable", $.ui.mouse, {
+ widgetEventPrefix: "drag",
+ options: {
+ addClasses: true,
+ appendTo: "parent",
+ axis: false,
+ connectToSortable: false,
+ containment: false,
+ cursor: "auto",
+ cursorAt: false,
+ grid: false,
+ handle: false,
+ helper: "original",
+ iframeFix: false,
+ opacity: false,
+ refreshPositions: false,
+ revert: false,
+ revertDuration: 500,
+ scope: "default",
+ scroll: true,
+ scrollSensitivity: 20,
+ scrollSpeed: 20,
+ snap: false,
+ snapMode: "both",
+ snapTolerance: 20,
+ stack: false,
+ zIndex: false
+ },
+ _create: function() {
+
+ if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position")))
+ this.element[0].style.position = 'relative';
+
+ (this.options.addClasses && this.element.addClass("ui-draggable"));
+ (this.options.disabled && this.element.addClass("ui-draggable-disabled"));
+
+ this._mouseInit();
+
+ },
+
+ destroy: function() {
+ if(!this.element.data('draggable')) return;
+ this.element
+ .removeData("draggable")
+ .unbind(".draggable")
+ .removeClass("ui-draggable"
+ + " ui-draggable-dragging"
+ + " ui-draggable-disabled");
+ this._mouseDestroy();
+
+ return this;
+ },
+
+ _mouseCapture: function(event) {
+
+ var o = this.options;
+
+ // among others, prevent a drag on a resizable-handle
+ if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle'))
+ return false;
+
+ //Quit if we're not on a valid handle
+ this.handle = this._getHandle(event);
+ if (!this.handle)
+ return false;
+
+ if ( o.iframeFix ) {
+ $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
+ $('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>')
+ .css({
+ width: this.offsetWidth+"px", height: this.offsetHeight+"px",
+ position: "absolute", opacity: "0.001", zIndex: 1000
+ })
+ .css($(this).offset())
+ .appendTo("body");
+ });
+ }
+
+ return true;
+
+ },
+
+ _mouseStart: function(event) {
+
+ var o = this.options;
+
+ //Create and append the visible helper
+ this.helper = this._createHelper(event);
+
+ //Cache the helper size
+ this._cacheHelperProportions();
+
+ //If ddmanager is used for droppables, set the global draggable
+ if($.ui.ddmanager)
+ $.ui.ddmanager.current = this;
+
+ /*
+ * - Position generation -
+ * This block generates everything position related - it's the core of draggables.
+ */
+
+ //Cache the margins of the original element
+ this._cacheMargins();
+
+ //Store the helper's css position
+ this.cssPosition = this.helper.css("position");
+ this.scrollParent = this.helper.scrollParent();
+
+ //The element's absolute position on the page minus margins
+ this.offset = this.positionAbs = this.element.offset();
+ this.offset = {
+ top: this.offset.top - this.margins.top,
+ left: this.offset.left - this.margins.left
+ };
+
+ $.extend(this.offset, {
+ click: { //Where the click happened, relative to the element
+ left: event.pageX - this.offset.left,
+ top: event.pageY - this.offset.top
+ },
+ parent: this._getParentOffset(),
+ relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
+ });
+
+ //Generate the original position
+ this.originalPosition = this.position = this._generatePosition(event);
+ this.originalPageX = event.pageX;
+ this.originalPageY = event.pageY;
+
+ //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
+ (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
+
+ //Set a containment if given in the options
+ if(o.containment)
+ this._setContainment();
+
+ //Trigger event + callbacks
+ if(this._trigger("start", event) === false) {
+ this._clear();
+ return false;
+ }
+
+ //Recache the helper size
+ this._cacheHelperProportions();
+
+ //Prepare the droppable offsets
+ if ($.ui.ddmanager && !o.dropBehaviour)
+ $.ui.ddmanager.prepareOffsets(this, event);
+
+ this.helper.addClass("ui-draggable-dragging");
+ this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
+
+ //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
+ if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);
+
+ return true;
+ },
+
+ _mouseDrag: function(event, noPropagation) {
+
+ //Compute the helpers position
+ this.position = this._generatePosition(event);
+ this.positionAbs = this._convertPositionTo("absolute");
+
+ //Call plugins and callbacks and use the resulting position if something is returned
+ if (!noPropagation) {
+ var ui = this._uiHash();
+ if(this._trigger('drag', event, ui) === false) {
+ this._mouseUp({});
+ return false;
+ }
+ this.position = ui.position;
+ }
+
+ if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
+ if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
+ if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
+
+ return false;
+ },
+
+ _mouseStop: function(event) {
+
+ //If we are using droppables, inform the manager about the drop
+ var dropped = false;
+ if ($.ui.ddmanager && !this.options.dropBehaviour)
+ dropped = $.ui.ddmanager.drop(this, event);
+
+ //if a drop comes from outside (a sortable)
+ if(this.dropped) {
+ dropped = this.dropped;
+ this.dropped = false;
+ }
+
+ //if the original element is removed, don't bother to continue if helper is set to "original"
+ if((!this.element[0] || !this.element[0].parentNode) && this.options.helper == "original")
+ return false;
+
+ if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
+ var self = this;
+ $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
+ if(self._trigger("stop", event) !== false) {
+ self._clear();
+ }
+ });
+ } else {
+ if(this._trigger("stop", event) !== false) {
+ this._clear();
+ }
+ }
+
+ return false;
+ },
+
+ _mouseUp: function(event) {
+ if (this.options.iframeFix === true) {
+ $("div.ui-draggable-iframeFix").each(function() {
+ this.parentNode.removeChild(this);
+ }); //Remove frame helpers
+ }
+
+ //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
+ if( $.ui.ddmanager ) $.ui.ddmanager.dragStop(this, event);
+
+ return $.ui.mouse.prototype._mouseUp.call(this, event);
+ },
+
+ cancel: function() {
+
+ if(this.helper.is(".ui-draggable-dragging")) {
+ this._mouseUp({});
+ } else {
+ this._clear();
+ }
+
+ return this;
+
+ },
+
+ _getHandle: function(event) {
+
+ var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
+ $(this.options.handle, this.element)
+ .find("*")
+ .andSelf()
+ .each(function() {
+ if(this == event.target) handle = true;
+ });
+
+ return handle;
+
+ },
+
+ _createHelper: function(event) {
+
+ var o = this.options;
+ var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone().removeAttr('id') : this.element);
+
+ if(!helper.parents('body').length)
+ helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo));
+
+ if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position")))
+ helper.css("position", "absolute");
+
+ return helper;
+
+ },
+
+ _adjustOffsetFromHelper: function(obj) {
+ if (typeof obj == 'string') {
+ obj = obj.split(' ');
+ }
+ if ($.isArray(obj)) {
+ obj = {left: +obj[0], top: +obj[1] || 0};
+ }
+ if ('left' in obj) {
+ this.offset.click.left = obj.left + this.margins.left;
+ }
+ if ('right' in obj) {
+ this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
+ }
+ if ('top' in obj) {
+ this.offset.click.top = obj.top + this.margins.top;
+ }
+ if ('bottom' in obj) {
+ this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
+ }
+ },
+
+ _getParentOffset: function() {
+
+ //Get the offsetParent and cache its position
+ this.offsetParent = this.helper.offsetParent();
+ var po = this.offsetParent.offset();
+
+ // This is a special case where we need to modify a offset calculated on start, since the following happened:
+ // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
+ // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
+ // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
+ if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
+ po.left += this.scrollParent.scrollLeft();
+ po.top += this.scrollParent.scrollTop();
+ }
+
+ if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
+ || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
+ po = { top: 0, left: 0 };
+
+ return {
+ top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
+ left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
+ };
+
+ },
+
+ _getRelativeOffset: function() {
+
+ if(this.cssPosition == "relative") {
+ var p = this.element.position();
+ return {
+ top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
+ left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
+ };
+ } else {
+ return { top: 0, left: 0 };
+ }
+
+ },
+
+ _cacheMargins: function() {
+ this.margins = {
+ left: (parseInt(this.element.css("marginLeft"),10) || 0),
+ top: (parseInt(this.element.css("marginTop"),10) || 0),
+ right: (parseInt(this.element.css("marginRight"),10) || 0),
+ bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
+ };
+ },
+
+ _cacheHelperProportions: function() {
+ this.helperProportions = {
+ width: this.helper.outerWidth(),
+ height: this.helper.outerHeight()
+ };
+ },
+
+ _setContainment: function() {
+
+ var o = this.options;
+ if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
+ if(o.containment == 'document' || o.containment == 'window') this.containment = [
+ o.containment == 'document' ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
+ o.containment == 'document' ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
+ (o.containment == 'document' ? 0 : $(window).scrollLeft()) + $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
+ (o.containment == 'document' ? 0 : $(window).scrollTop()) + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
+ ];
+
+ if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor != Array) {
+ var c = $(o.containment);
+ var ce = c[0]; if(!ce) return;
+ var co = c.offset();
+ var over = ($(ce).css("overflow") != 'hidden');
+
+ this.containment = [
+ (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
+ (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
+ (over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right,
+ (over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - this.margins.bottom
+ ];
+ this.relative_container = c;
+
+ } else if(o.containment.constructor == Array) {
+ this.containment = o.containment;
+ }
+
+ },
+
+ _convertPositionTo: function(d, pos) {
+
+ if(!pos) pos = this.position;
+ var mod = d == "absolute" ? 1 : -1;
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+
+ return {
+ top: (
+ pos.top // The absolute mouse position
+ + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
+ + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
+ - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
+ ),
+ left: (
+ pos.left // The absolute mouse position
+ + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
+ + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
+ - ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
+ )
+ };
+
+ },
+
+ _generatePosition: function(event) {
+
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+ var pageX = event.pageX;
+ var pageY = event.pageY;
+
+ /*
+ * - Position constraining -
+ * Constrain the position to a mix of grid, containment.
+ */
+
+ if(this.originalPosition) { //If we are not dragging yet, we won't check for options
+ var containment;
+ if(this.containment) {
+ if (this.relative_container){
+ var co = this.relative_container.offset();
+ containment = [ this.containment[0] + co.left,
+ this.containment[1] + co.top,
+ this.containment[2] + co.left,
+ this.containment[3] + co.top ];
+ }
+ else {
+ containment = this.containment;
+ }
+
+ if(event.pageX - this.offset.click.left < containment[0]) pageX = containment[0] + this.offset.click.left;
+ if(event.pageY - this.offset.click.top < containment[1]) pageY = containment[1] + this.offset.click.top;
+ if(event.pageX - this.offset.click.left > containment[2]) pageX = containment[2] + this.offset.click.left;
+ if(event.pageY - this.offset.click.top > containment[3]) pageY = containment[3] + this.offset.click.top;
+ }
+
+ if(o.grid) {
+ //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
+ var top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
+ pageY = containment ? (!(top - this.offset.click.top < containment[1] || top - this.offset.click.top > containment[3]) ? top : (!(top - this.offset.click.top < containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
+
+ var left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
+ pageX = containment ? (!(left - this.offset.click.left < containment[0] || left - this.offset.click.left > containment[2]) ? left : (!(left - this.offset.click.left < containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
+ }
+
+ }
+
+ return {
+ top: (
+ pageY // The absolute mouse position
+ - this.offset.click.top // Click offset (relative to the element)
+ - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
+ - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
+ + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
+ ),
+ left: (
+ pageX // The absolute mouse position
+ - this.offset.click.left // Click offset (relative to the element)
+ - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
+ - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
+ + ($.browser.safari && $.browser.version < 526 && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
+ )
+ };
+
+ },
+
+ _clear: function() {
+ this.helper.removeClass("ui-draggable-dragging");
+ if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove();
+ //if($.ui.ddmanager) $.ui.ddmanager.current = null;
+ this.helper = null;
+ this.cancelHelperRemoval = false;
+ },
+
+ // From now on bulk stuff - mainly helpers
+
+ _trigger: function(type, event, ui) {
+ ui = ui || this._uiHash();
+ $.ui.plugin.call(this, type, [event, ui]);
+ if(type == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins
+ return $.Widget.prototype._trigger.call(this, type, event, ui);
+ },
+
+ plugins: {},
+
+ _uiHash: function(event) {
+ return {
+ helper: this.helper,
+ position: this.position,
+ originalPosition: this.originalPosition,
+ offset: this.positionAbs
+ };
+ }
+
+});
+
+$.extend($.ui.draggable, {
+ version: "1.8.18"
+});
+
+$.ui.plugin.add("draggable", "connectToSortable", {
+ start: function(event, ui) {
+
+ var inst = $(this).data("draggable"), o = inst.options,
+ uiSortable = $.extend({}, ui, { item: inst.element });
+ inst.sortables = [];
+ $(o.connectToSortable).each(function() {
+ var sortable = $.data(this, 'sortable');
+ if (sortable && !sortable.options.disabled) {
+ inst.sortables.push({
+ instance: sortable,
+ shouldRevert: sortable.options.revert
+ });
+ sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
+ sortable._trigger("activate", event, uiSortable);
+ }
+ });
+
+ },
+ stop: function(event, ui) {
+
+ //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
+ var inst = $(this).data("draggable"),
+ uiSortable = $.extend({}, ui, { item: inst.element });
+
+ $.each(inst.sortables, function() {
+ if(this.instance.isOver) {
+
+ this.instance.isOver = 0;
+
+ inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
+ this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
+
+ //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: 'valid/invalid'
+ if(this.shouldRevert) this.instance.options.revert = true;
+
+ //Trigger the stop of the sortable
+ this.instance._mouseStop(event);
+
+ this.instance.options.helper = this.instance.options._helper;
+
+ //If the helper has been the original item, restore properties in the sortable
+ if(inst.options.helper == 'original')
+ this.instance.currentItem.css({ top: 'auto', left: 'auto' });
+
+ } else {
+ this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
+ this.instance._trigger("deactivate", event, uiSortable);
+ }
+
+ });
+
+ },
+ drag: function(event, ui) {
+
+ var inst = $(this).data("draggable"), self = this;
+
+ var checkPos = function(o) {
+ var dyClick = this.offset.click.top, dxClick = this.offset.click.left;
+ var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left;
+ var itemHeight = o.height, itemWidth = o.width;
+ var itemTop = o.top, itemLeft = o.left;
+
+ return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth);
+ };
+
+ $.each(inst.sortables, function(i) {
+
+ //Copy over some variables to allow calling the sortable's native _intersectsWith
+ this.instance.positionAbs = inst.positionAbs;
+ this.instance.helperProportions = inst.helperProportions;
+ this.instance.offset.click = inst.offset.click;
+
+ if(this.instance._intersectsWith(this.instance.containerCache)) {
+
+ //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
+ if(!this.instance.isOver) {
+
+ this.instance.isOver = 1;
+ //Now we fake the start of dragging for the sortable instance,
+ //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
+ //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
+ this.instance.currentItem = $(self).clone().removeAttr('id').appendTo(this.instance.element).data("sortable-item", true);
+ this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
+ this.instance.options.helper = function() { return ui.helper[0]; };
+
+ event.target = this.instance.currentItem[0];
+ this.instance._mouseCapture(event, true);
+ this.instance._mouseStart(event, true, true);
+
+ //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
+ this.instance.offset.click.top = inst.offset.click.top;
+ this.instance.offset.click.left = inst.offset.click.left;
+ this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
+ this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
+
+ inst._trigger("toSortable", event);
+ inst.dropped = this.instance.element; //draggable revert needs that
+ //hack so receive/update callbacks work (mostly)
+ inst.currentItem = inst.element;
+ this.instance.fromOutside = inst;
+
+ }
+
+ //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
+ if(this.instance.currentItem) this.instance._mouseDrag(event);
+
+ } else {
+
+ //If it doesn't intersect with the sortable, and it intersected before,
+ //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
+ if(this.instance.isOver) {
+
+ this.instance.isOver = 0;
+ this.instance.cancelHelperRemoval = true;
+
+ //Prevent reverting on this forced stop
+ this.instance.options.revert = false;
+
+ // The out event needs to be triggered independently
+ this.instance._trigger('out', event, this.instance._uiHash(this.instance));
+
+ this.instance._mouseStop(event, true);
+ this.instance.options.helper = this.instance.options._helper;
+
+ //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
+ this.instance.currentItem.remove();
+ if(this.instance.placeholder) this.instance.placeholder.remove();
+
+ inst._trigger("fromSortable", event);
+ inst.dropped = false; //draggable revert needs that
+ }
+
+ };
+
+ });
+
+ }
+});
+
+$.ui.plugin.add("draggable", "cursor", {
+ start: function(event, ui) {
+ var t = $('body'), o = $(this).data('draggable').options;
+ if (t.css("cursor")) o._cursor = t.css("cursor");
+ t.css("cursor", o.cursor);
+ },
+ stop: function(event, ui) {
+ var o = $(this).data('draggable').options;
+ if (o._cursor) $('body').css("cursor", o._cursor);
+ }
+});
+
+$.ui.plugin.add("draggable", "opacity", {
+ start: function(event, ui) {
+ var t = $(ui.helper), o = $(this).data('draggable').options;
+ if(t.css("opacity")) o._opacity = t.css("opacity");
+ t.css('opacity', o.opacity);
+ },
+ stop: function(event, ui) {
+ var o = $(this).data('draggable').options;
+ if(o._opacity) $(ui.helper).css('opacity', o._opacity);
+ }
+});
+
+$.ui.plugin.add("draggable", "scroll", {
+ start: function(event, ui) {
+ var i = $(this).data("draggable");
+ if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset();
+ },
+ drag: function(event, ui) {
+
+ var i = $(this).data("draggable"), o = i.options, scrolled = false;
+
+ if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') {
+
+ if(!o.axis || o.axis != 'x') {
+ if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
+ i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
+ else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity)
+ i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
+ }
+
+ if(!o.axis || o.axis != 'y') {
+ if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
+ i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
+ else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity)
+ i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
+ }
+
+ } else {
+
+ if(!o.axis || o.axis != 'x') {
+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
+ scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
+ else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
+ scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
+ }
+
+ if(!o.axis || o.axis != 'y') {
+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
+ scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
+ else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
+ scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
+ }
+
+ }
+
+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
+ $.ui.ddmanager.prepareOffsets(i, event);
+
+ }
+});
+
+$.ui.plugin.add("draggable", "snap", {
+ start: function(event, ui) {
+
+ var i = $(this).data("draggable"), o = i.options;
+ i.snapElements = [];
+
+ $(o.snap.constructor != String ? ( o.snap.items || ':data(draggable)' ) : o.snap).each(function() {
+ var $t = $(this); var $o = $t.offset();
+ if(this != i.element[0]) i.snapElements.push({
+ item: this,
+ width: $t.outerWidth(), height: $t.outerHeight(),
+ top: $o.top, left: $o.left
+ });
+ });
+
+ },
+ drag: function(event, ui) {
+
+ var inst = $(this).data("draggable"), o = inst.options;
+ var d = o.snapTolerance;
+
+ var x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
+ y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
+
+ for (var i = inst.snapElements.length - 1; i >= 0; i--){
+
+ var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width,
+ t = inst.snapElements[i].top, b = t + inst.snapElements[i].height;
+
+ //Yes, I know, this is insane ;)
+ if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
+ if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
+ inst.snapElements[i].snapping = false;
+ continue;
+ }
+
+ if(o.snapMode != 'inner') {
+ var ts = Math.abs(t - y2) <= d;
+ var bs = Math.abs(b - y1) <= d;
+ var ls = Math.abs(l - x2) <= d;
+ var rs = Math.abs(r - x1) <= d;
+ if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
+ if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
+ if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
+ if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
+ }
+
+ var first = (ts || bs || ls || rs);
+
+ if(o.snapMode != 'outer') {
+ var ts = Math.abs(t - y1) <= d;
+ var bs = Math.abs(b - y2) <= d;
+ var ls = Math.abs(l - x1) <= d;
+ var rs = Math.abs(r - x2) <= d;
+ if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
+ if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
+ if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
+ if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
+ }
+
+ if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first))
+ (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
+ inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
+
+ };
+
+ }
+});
+
+$.ui.plugin.add("draggable", "stack", {
+ start: function(event, ui) {
+
+ var o = $(this).data("draggable").options;
+
+ var group = $.makeArray($(o.stack)).sort(function(a,b) {
+ return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
+ });
+ if (!group.length) { return; }
+
+ var min = parseInt(group[0].style.zIndex) || 0;
+ $(group).each(function(i) {
+ this.style.zIndex = min + i;
+ });
+
+ this[0].style.zIndex = min + group.length;
+
+ }
+});
+
+$.ui.plugin.add("draggable", "zIndex", {
+ start: function(event, ui) {
+ var t = $(ui.helper), o = $(this).data("draggable").options;
+ if(t.css("zIndex")) o._zIndex = t.css("zIndex");
+ t.css('zIndex', o.zIndex);
+ },
+ stop: function(event, ui) {
+ var o = $(this).data("draggable").options;
+ if(o._zIndex) $(ui.helper).css('zIndex', o._zIndex);
+ }
+});
+
+})(jQuery);
+/*
+ * jQuery UI Droppable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Droppables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.mouse.js
+ * jquery.ui.draggable.js
+ */
+(function( $, undefined ) {
+
+$.widget("ui.droppable", {
+ widgetEventPrefix: "drop",
+ options: {
+ accept: '*',
+ activeClass: false,
+ addClasses: true,
+ greedy: false,
+ hoverClass: false,
+ scope: 'default',
+ tolerance: 'intersect'
+ },
+ _create: function() {
+
+ var o = this.options, accept = o.accept;
+ this.isover = 0; this.isout = 1;
+
+ this.accept = $.isFunction(accept) ? accept : function(d) {
+ return d.is(accept);
+ };
+
+ //Store the droppable's proportions
+ this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
+
+ // Add the reference and positions to the manager
+ $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
+ $.ui.ddmanager.droppables[o.scope].push(this);
+
+ (o.addClasses && this.element.addClass("ui-droppable"));
+
+ },
+
+ destroy: function() {
+ var drop = $.ui.ddmanager.droppables[this.options.scope];
+ for ( var i = 0; i < drop.length; i++ )
+ if ( drop[i] == this )
+ drop.splice(i, 1);
+
+ this.element
+ .removeClass("ui-droppable ui-droppable-disabled")
+ .removeData("droppable")
+ .unbind(".droppable");
+
+ return this;
+ },
+
+ _setOption: function(key, value) {
+
+ if(key == 'accept') {
+ this.accept = $.isFunction(value) ? value : function(d) {
+ return d.is(value);
+ };
+ }
+ $.Widget.prototype._setOption.apply(this, arguments);
+ },
+
+ _activate: function(event) {
+ var draggable = $.ui.ddmanager.current;
+ if(this.options.activeClass) this.element.addClass(this.options.activeClass);
+ (draggable && this._trigger('activate', event, this.ui(draggable)));
+ },
+
+ _deactivate: function(event) {
+ var draggable = $.ui.ddmanager.current;
+ if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
+ (draggable && this._trigger('deactivate', event, this.ui(draggable)));
+ },
+
+ _over: function(event) {
+
+ var draggable = $.ui.ddmanager.current;
+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
+
+ if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
+ if(this.options.hoverClass) this.element.addClass(this.options.hoverClass);
+ this._trigger('over', event, this.ui(draggable));
+ }
+
+ },
+
+ _out: function(event) {
+
+ var draggable = $.ui.ddmanager.current;
+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return; // Bail if draggable and droppable are same element
+
+ if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
+ if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
+ this._trigger('out', event, this.ui(draggable));
+ }
+
+ },
+
+ _drop: function(event,custom) {
+
+ var draggable = custom || $.ui.ddmanager.current;
+ if (!draggable || (draggable.currentItem || draggable.element)[0] == this.element[0]) return false; // Bail if draggable and droppable are same element
+
+ var childrenIntersection = false;
+ this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function() {
+ var inst = $.data(this, 'droppable');
+ if(
+ inst.options.greedy
+ && !inst.options.disabled
+ && inst.options.scope == draggable.options.scope
+ && inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element))
+ && $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
+ ) { childrenIntersection = true; return false; }
+ });
+ if(childrenIntersection) return false;
+
+ if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
+ if(this.options.activeClass) this.element.removeClass(this.options.activeClass);
+ if(this.options.hoverClass) this.element.removeClass(this.options.hoverClass);
+ this._trigger('drop', event, this.ui(draggable));
+ return this.element;
+ }
+
+ return false;
+
+ },
+
+ ui: function(c) {
+ return {
+ draggable: (c.currentItem || c.element),
+ helper: c.helper,
+ position: c.position,
+ offset: c.positionAbs
+ };
+ }
+
+});
+
+$.extend($.ui.droppable, {
+ version: "1.8.18"
+});
+
+$.ui.intersect = function(draggable, droppable, toleranceMode) {
+
+ if (!droppable.offset) return false;
+
+ var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
+ y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
+ var l = droppable.offset.left, r = l + droppable.proportions.width,
+ t = droppable.offset.top, b = t + droppable.proportions.height;
+
+ switch (toleranceMode) {
+ case 'fit':
+ return (l <= x1 && x2 <= r
+ && t <= y1 && y2 <= b);
+ break;
+ case 'intersect':
+ return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
+ && x2 - (draggable.helperProportions.width / 2) < r // Left Half
+ && t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
+ && y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
+ break;
+ case 'pointer':
+ var draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left),
+ draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top),
+ isOver = $.ui.isOver(draggableTop, draggableLeft, t, l, droppable.proportions.height, droppable.proportions.width);
+ return isOver;
+ break;
+ case 'touch':
+ return (
+ (y1 >= t && y1 <= b) || // Top edge touching
+ (y2 >= t && y2 <= b) || // Bottom edge touching
+ (y1 < t && y2 > b) // Surrounded vertically
+ ) && (
+ (x1 >= l && x1 <= r) || // Left edge touching
+ (x2 >= l && x2 <= r) || // Right edge touching
+ (x1 < l && x2 > r) // Surrounded horizontally
+ );
+ break;
+ default:
+ return false;
+ break;
+ }
+
+};
+
+/*
+ This manager tracks offsets of draggables and droppables
+*/
+$.ui.ddmanager = {
+ current: null,
+ droppables: { 'default': [] },
+ prepareOffsets: function(t, event) {
+
+ var m = $.ui.ddmanager.droppables[t.options.scope] || [];
+ var type = event ? event.type : null; // workaround for #2317
+ var list = (t.currentItem || t.element).find(":data(droppable)").andSelf();
+
+ droppablesLoop: for (var i = 0; i < m.length; i++) {
+
+ if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) continue; //No disabled and non-accepted
+ for (var j=0; j < list.length; j++) { if(list[j] == m[i].element[0]) { m[i].proportions.height = 0; continue droppablesLoop; } }; //Filter out elements in the current dragged item
+ m[i].visible = m[i].element.css("display") != "none"; if(!m[i].visible) continue; //If the element is not visible, continue
+
+ if(type == "mousedown") m[i]._activate.call(m[i], event); //Activate the droppable if used directly from draggables
+
+ m[i].offset = m[i].element.offset();
+ m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
+
+ }
+
+ },
+ drop: function(draggable, event) {
+
+ var dropped = false;
+ $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
+
+ if(!this.options) return;
+ if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
+ dropped = this._drop.call(this, event) || dropped;
+
+ if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
+ this.isout = 1; this.isover = 0;
+ this._deactivate.call(this, event);
+ }
+
+ });
+ return dropped;
+
+ },
+ dragStart: function( draggable, event ) {
+ //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
+ draggable.element.parents( ":not(body,html)" ).bind( "scroll.droppable", function() {
+ if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
+ });
+ },
+ drag: function(draggable, event) {
+
+ //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
+ if(draggable.options.refreshPositions) $.ui.ddmanager.prepareOffsets(draggable, event);
+
+ //Run through all droppables and check their positions based on specific tolerance options
+ $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
+
+ if(this.options.disabled || this.greedyChild || !this.visible) return;
+ var intersects = $.ui.intersect(draggable, this, this.options.tolerance);
+
+ var c = !intersects && this.isover == 1 ? 'isout' : (intersects && this.isover == 0 ? 'isover' : null);
+ if(!c) return;
+
+ var parentInstance;
+ if (this.options.greedy) {
+ var parent = this.element.parents(':data(droppable):eq(0)');
+ if (parent.length) {
+ parentInstance = $.data(parent[0], 'droppable');
+ parentInstance.greedyChild = (c == 'isover' ? 1 : 0);
+ }
+ }
+
+ // we just moved into a greedy child
+ if (parentInstance && c == 'isover') {
+ parentInstance['isover'] = 0;
+ parentInstance['isout'] = 1;
+ parentInstance._out.call(parentInstance, event);
+ }
+
+ this[c] = 1; this[c == 'isout' ? 'isover' : 'isout'] = 0;
+ this[c == "isover" ? "_over" : "_out"].call(this, event);
+
+ // we just moved out of a greedy child
+ if (parentInstance && c == 'isout') {
+ parentInstance['isout'] = 0;
+ parentInstance['isover'] = 1;
+ parentInstance._over.call(parentInstance, event);
+ }
+ });
+
+ },
+ dragStop: function( draggable, event ) {
+ draggable.element.parents( ":not(body,html)" ).unbind( "scroll.droppable" );
+ //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
+ if( !draggable.options.refreshPositions ) $.ui.ddmanager.prepareOffsets( draggable, event );
+ }
+};
+
+})(jQuery);
+/*
+ * jQuery UI Resizable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+$.widget("ui.resizable", $.ui.mouse, {
+ widgetEventPrefix: "resize",
+ options: {
+ alsoResize: false,
+ animate: false,
+ animateDuration: "slow",
+ animateEasing: "swing",
+ aspectRatio: false,
+ autoHide: false,
+ containment: false,
+ ghost: false,
+ grid: false,
+ handles: "e,s,se",
+ helper: false,
+ maxHeight: null,
+ maxWidth: null,
+ minHeight: 10,
+ minWidth: 10,
+ zIndex: 1000
+ },
+ _create: function() {
+
+ var self = this, o = this.options;
+ this.element.addClass("ui-resizable");
+
+ $.extend(this, {
+ _aspectRatio: !!(o.aspectRatio),
+ aspectRatio: o.aspectRatio,
+ originalElement: this.element,
+ _proportionallyResizeElements: [],
+ _helper: o.helper || o.ghost || o.animate ? o.helper || 'ui-resizable-helper' : null
+ });
+
+ //Wrap the element if it cannot hold child nodes
+ if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
+
+ //Create a wrapper element and set the wrapper to the new current internal element
+ this.element.wrap(
+ $('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({
+ position: this.element.css('position'),
+ width: this.element.outerWidth(),
+ height: this.element.outerHeight(),
+ top: this.element.css('top'),
+ left: this.element.css('left')
+ })
+ );
+
+ //Overwrite the original this.element
+ this.element = this.element.parent().data(
+ "resizable", this.element.data('resizable')
+ );
+
+ this.elementIsWrapper = true;
+
+ //Move margins to the wrapper
+ this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
+ this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
+
+ //Prevent Safari textarea resize
+ this.originalResizeStyle = this.originalElement.css('resize');
+ this.originalElement.css('resize', 'none');
+
+ //Push the actual element to our proportionallyResize internal array
+ this._proportionallyResizeElements.push(this.originalElement.css({ position: 'static', zoom: 1, display: 'block' }));
+
+ // avoid IE jump (hard set the margin)
+ this.originalElement.css({ margin: this.originalElement.css('margin') });
+
+ // fix handlers offset
+ this._proportionallyResize();
+
+ }
+
+ this.handles = o.handles || (!$('.ui-resizable-handle', this.element).length ? "e,s,se" : { n: '.ui-resizable-n', e: '.ui-resizable-e', s: '.ui-resizable-s', w: '.ui-resizable-w', se: '.ui-resizable-se', sw: '.ui-resizable-sw', ne: '.ui-resizable-ne', nw: '.ui-resizable-nw' });
+ if(this.handles.constructor == String) {
+
+ if(this.handles == 'all') this.handles = 'n,e,s,w,se,sw,ne,nw';
+ var n = this.handles.split(","); this.handles = {};
+
+ for(var i = 0; i < n.length; i++) {
+
+ var handle = $.trim(n[i]), hname = 'ui-resizable-'+handle;
+ var axis = $('<div class="ui-resizable-handle ' + hname + '"></div>');
+
+ // increase zIndex of sw, se, ne, nw axis
+ //TODO : this modifies original option
+ if(/sw|se|ne|nw/.test(handle)) axis.css({ zIndex: ++o.zIndex });
+
+ //TODO : What's going on here?
+ if ('se' == handle) {
+ axis.addClass('ui-icon ui-icon-gripsmall-diagonal-se');
+ };
+
+ //Insert into internal handles object and append to element
+ this.handles[handle] = '.ui-resizable-'+handle;
+ this.element.append(axis);
+ }
+
+ }
+
+ this._renderAxis = function(target) {
+
+ target = target || this.element;
+
+ for(var i in this.handles) {
+
+ if(this.handles[i].constructor == String)
+ this.handles[i] = $(this.handles[i], this.element).show();
+
+ //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
+ if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
+
+ var axis = $(this.handles[i], this.element), padWrapper = 0;
+
+ //Checking the correct pad and border
+ padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
+
+ //The padding type i have to apply...
+ var padPos = [ 'padding',
+ /ne|nw|n/.test(i) ? 'Top' :
+ /se|sw|s/.test(i) ? 'Bottom' :
+ /^e$/.test(i) ? 'Right' : 'Left' ].join("");
+
+ target.css(padPos, padWrapper);
+
+ this._proportionallyResize();
+
+ }
+
+ //TODO: What's that good for? There's not anything to be executed left
+ if(!$(this.handles[i]).length)
+ continue;
+
+ }
+ };
+
+ //TODO: make renderAxis a prototype function
+ this._renderAxis(this.element);
+
+ this._handles = $('.ui-resizable-handle', this.element)
+ .disableSelection();
+
+ //Matching axis name
+ this._handles.mouseover(function() {
+ if (!self.resizing) {
+ if (this.className)
+ var axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
+ //Axis, default = se
+ self.axis = axis && axis[1] ? axis[1] : 'se';
+ }
+ });
+
+ //If we want to auto hide the elements
+ if (o.autoHide) {
+ this._handles.hide();
+ $(this.element)
+ .addClass("ui-resizable-autohide")
+ .hover(function() {
+ if (o.disabled) return;
+ $(this).removeClass("ui-resizable-autohide");
+ self._handles.show();
+ },
+ function(){
+ if (o.disabled) return;
+ if (!self.resizing) {
+ $(this).addClass("ui-resizable-autohide");
+ self._handles.hide();
+ }
+ });
+ }
+
+ //Initialize the mouse interaction
+ this._mouseInit();
+
+ },
+
+ destroy: function() {
+
+ this._mouseDestroy();
+
+ var _destroy = function(exp) {
+ $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
+ .removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();
+ };
+
+ //TODO: Unwrap at same DOM position
+ if (this.elementIsWrapper) {
+ _destroy(this.element);
+ var wrapper = this.element;
+ wrapper.after(
+ this.originalElement.css({
+ position: wrapper.css('position'),
+ width: wrapper.outerWidth(),
+ height: wrapper.outerHeight(),
+ top: wrapper.css('top'),
+ left: wrapper.css('left')
+ })
+ ).remove();
+ }
+
+ this.originalElement.css('resize', this.originalResizeStyle);
+ _destroy(this.originalElement);
+
+ return this;
+ },
+
+ _mouseCapture: function(event) {
+ var handle = false;
+ for (var i in this.handles) {
+ if ($(this.handles[i])[0] == event.target) {
+ handle = true;
+ }
+ }
+
+ return !this.options.disabled && handle;
+ },
+
+ _mouseStart: function(event) {
+
+ var o = this.options, iniPos = this.element.position(), el = this.element;
+
+ this.resizing = true;
+ this.documentScroll = { top: $(document).scrollTop(), left: $(document).scrollLeft() };
+
+ // bugfix for http://dev.jquery.com/ticket/1749
+ if (el.is('.ui-draggable') || (/absolute/).test(el.css('position'))) {
+ el.css({ position: 'absolute', top: iniPos.top, left: iniPos.left });
+ }
+
+ this._renderProxy();
+
+ var curleft = num(this.helper.css('left')), curtop = num(this.helper.css('top'));
+
+ if (o.containment) {
+ curleft += $(o.containment).scrollLeft() || 0;
+ curtop += $(o.containment).scrollTop() || 0;
+ }
+
+ //Store needed variables
+ this.offset = this.helper.offset();
+ this.position = { left: curleft, top: curtop };
+ this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
+ this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
+ this.originalPosition = { left: curleft, top: curtop };
+ this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
+ this.originalMousePosition = { left: event.pageX, top: event.pageY };
+
+ //Aspect Ratio
+ this.aspectRatio = (typeof o.aspectRatio == 'number') ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
+
+ var cursor = $('.ui-resizable-' + this.axis).css('cursor');
+ $('body').css('cursor', cursor == 'auto' ? this.axis + '-resize' : cursor);
+
+ el.addClass("ui-resizable-resizing");
+ this._propagate("start", event);
+ return true;
+ },
+
+ _mouseDrag: function(event) {
+
+ //Increase performance, avoid regex
+ var el = this.helper, o = this.options, props = {},
+ self = this, smp = this.originalMousePosition, a = this.axis;
+
+ var dx = (event.pageX-smp.left)||0, dy = (event.pageY-smp.top)||0;
+ var trigger = this._change[a];
+ if (!trigger) return false;
+
+ // Calculate the attrs that will be change
+ var data = trigger.apply(this, [event, dx, dy]), ie6 = $.browser.msie && $.browser.version < 7, csdif = this.sizeDiff;
+
+ // Put this in the mouseDrag handler since the user can start pressing shift while resizing
+ this._updateVirtualBoundaries(event.shiftKey);
+ if (this._aspectRatio || event.shiftKey)
+ data = this._updateRatio(data, event);
+
+ data = this._respectSize(data, event);
+
+ // plugins callbacks need to be called first
+ this._propagate("resize", event);
+
+ el.css({
+ top: this.position.top + "px", left: this.position.left + "px",
+ width: this.size.width + "px", height: this.size.height + "px"
+ });
+
+ if (!this._helper && this._proportionallyResizeElements.length)
+ this._proportionallyResize();
+
+ this._updateCache(data);
+
+ // calling the user callback at the end
+ this._trigger('resize', event, this.ui());
+
+ return false;
+ },
+
+ _mouseStop: function(event) {
+
+ this.resizing = false;
+ var o = this.options, self = this;
+
+ if(this._helper) {
+ var pr = this._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
+ soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
+ soffsetw = ista ? 0 : self.sizeDiff.width;
+
+ var s = { width: (self.helper.width() - soffsetw), height: (self.helper.height() - soffseth) },
+ left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
+ top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
+
+ if (!o.animate)
+ this.element.css($.extend(s, { top: top, left: left }));
+
+ self.helper.height(self.size.height);
+ self.helper.width(self.size.width);
+
+ if (this._helper && !o.animate) this._proportionallyResize();
+ }
+
+ $('body').css('cursor', 'auto');
+
+ this.element.removeClass("ui-resizable-resizing");
+
+ this._propagate("stop", event);
+
+ if (this._helper) this.helper.remove();
+ return false;
+
+ },
+
+ _updateVirtualBoundaries: function(forceAspectRatio) {
+ var o = this.options, pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b;
+
+ b = {
+ minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
+ maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
+ minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
+ maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
+ };
+
+ if(this._aspectRatio || forceAspectRatio) {
+ // We want to create an enclosing box whose aspect ration is the requested one
+ // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
+ pMinWidth = b.minHeight * this.aspectRatio;
+ pMinHeight = b.minWidth / this.aspectRatio;
+ pMaxWidth = b.maxHeight * this.aspectRatio;
+ pMaxHeight = b.maxWidth / this.aspectRatio;
+
+ if(pMinWidth > b.minWidth) b.minWidth = pMinWidth;
+ if(pMinHeight > b.minHeight) b.minHeight = pMinHeight;
+ if(pMaxWidth < b.maxWidth) b.maxWidth = pMaxWidth;
+ if(pMaxHeight < b.maxHeight) b.maxHeight = pMaxHeight;
+ }
+ this._vBoundaries = b;
+ },
+
+ _updateCache: function(data) {
+ var o = this.options;
+ this.offset = this.helper.offset();
+ if (isNumber(data.left)) this.position.left = data.left;
+ if (isNumber(data.top)) this.position.top = data.top;
+ if (isNumber(data.height)) this.size.height = data.height;
+ if (isNumber(data.width)) this.size.width = data.width;
+ },
+
+ _updateRatio: function(data, event) {
+
+ var o = this.options, cpos = this.position, csize = this.size, a = this.axis;
+
+ if (isNumber(data.height)) data.width = (data.height * this.aspectRatio);
+ else if (isNumber(data.width)) data.height = (data.width / this.aspectRatio);
+
+ if (a == 'sw') {
+ data.left = cpos.left + (csize.width - data.width);
+ data.top = null;
+ }
+ if (a == 'nw') {
+ data.top = cpos.top + (csize.height - data.height);
+ data.left = cpos.left + (csize.width - data.width);
+ }
+
+ return data;
+ },
+
+ _respectSize: function(data, event) {
+
+ var el = this.helper, o = this._vBoundaries, pRatio = this._aspectRatio || event.shiftKey, a = this.axis,
+ ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
+ isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height);
+
+ if (isminw) data.width = o.minWidth;
+ if (isminh) data.height = o.minHeight;
+ if (ismaxw) data.width = o.maxWidth;
+ if (ismaxh) data.height = o.maxHeight;
+
+ var dw = this.originalPosition.left + this.originalSize.width, dh = this.position.top + this.size.height;
+ var cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
+
+ if (isminw && cw) data.left = dw - o.minWidth;
+ if (ismaxw && cw) data.left = dw - o.maxWidth;
+ if (isminh && ch) data.top = dh - o.minHeight;
+ if (ismaxh && ch) data.top = dh - o.maxHeight;
+
+ // fixing jump error on top/left - bug #2330
+ var isNotwh = !data.width && !data.height;
+ if (isNotwh && !data.left && data.top) data.top = null;
+ else if (isNotwh && !data.top && data.left) data.left = null;
+
+ return data;
+ },
+
+ _proportionallyResize: function() {
+
+ var o = this.options;
+ if (!this._proportionallyResizeElements.length) return;
+ var element = this.helper || this.element;
+
+ for (var i=0; i < this._proportionallyResizeElements.length; i++) {
+
+ var prel = this._proportionallyResizeElements[i];
+
+ if (!this.borderDif) {
+ var b = [prel.css('borderTopWidth'), prel.css('borderRightWidth'), prel.css('borderBottomWidth'), prel.css('borderLeftWidth')],
+ p = [prel.css('paddingTop'), prel.css('paddingRight'), prel.css('paddingBottom'), prel.css('paddingLeft')];
+
+ this.borderDif = $.map(b, function(v, i) {
+ var border = parseInt(v,10)||0, padding = parseInt(p[i],10)||0;
+ return border + padding;
+ });
+ }
+
+ if ($.browser.msie && !(!($(element).is(':hidden') || $(element).parents(':hidden').length)))
+ continue;
+
+ prel.css({
+ height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
+ width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
+ });
+
+ };
+
+ },
+
+ _renderProxy: function() {
+
+ var el = this.element, o = this.options;
+ this.elementOffset = el.offset();
+
+ if(this._helper) {
+
+ this.helper = this.helper || $('<div style="overflow:hidden;"></div>');
+
+ // fix ie6 offset TODO: This seems broken
+ var ie6 = $.browser.msie && $.browser.version < 7, ie6offset = (ie6 ? 1 : 0),
+ pxyoffset = ( ie6 ? 2 : -1 );
+
+ this.helper.addClass(this._helper).css({
+ width: this.element.outerWidth() + pxyoffset,
+ height: this.element.outerHeight() + pxyoffset,
+ position: 'absolute',
+ left: this.elementOffset.left - ie6offset +'px',
+ top: this.elementOffset.top - ie6offset +'px',
+ zIndex: ++o.zIndex //TODO: Don't modify option
+ });
+
+ this.helper
+ .appendTo("body")
+ .disableSelection();
+
+ } else {
+ this.helper = this.element;
+ }
+
+ },
+
+ _change: {
+ e: function(event, dx, dy) {
+ return { width: this.originalSize.width + dx };
+ },
+ w: function(event, dx, dy) {
+ var o = this.options, cs = this.originalSize, sp = this.originalPosition;
+ return { left: sp.left + dx, width: cs.width - dx };
+ },
+ n: function(event, dx, dy) {
+ var o = this.options, cs = this.originalSize, sp = this.originalPosition;
+ return { top: sp.top + dy, height: cs.height - dy };
+ },
+ s: function(event, dx, dy) {
+ return { height: this.originalSize.height + dy };
+ },
+ se: function(event, dx, dy) {
+ return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
+ },
+ sw: function(event, dx, dy) {
+ return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
+ },
+ ne: function(event, dx, dy) {
+ return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
+ },
+ nw: function(event, dx, dy) {
+ return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
+ }
+ },
+
+ _propagate: function(n, event) {
+ $.ui.plugin.call(this, n, [event, this.ui()]);
+ (n != "resize" && this._trigger(n, event, this.ui()));
+ },
+
+ plugins: {},
+
+ ui: function() {
+ return {
+ originalElement: this.originalElement,
+ element: this.element,
+ helper: this.helper,
+ position: this.position,
+ size: this.size,
+ originalSize: this.originalSize,
+ originalPosition: this.originalPosition
+ };
+ }
+
+});
+
+$.extend($.ui.resizable, {
+ version: "1.8.18"
+});
+
+/*
+ * Resizable Extensions
+ */
+
+$.ui.plugin.add("resizable", "alsoResize", {
+
+ start: function (event, ui) {
+ var self = $(this).data("resizable"), o = self.options;
+
+ var _store = function (exp) {
+ $(exp).each(function() {
+ var el = $(this);
+ el.data("resizable-alsoresize", {
+ width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
+ left: parseInt(el.css('left'), 10), top: parseInt(el.css('top'), 10)
+ });
+ });
+ };
+
+ if (typeof(o.alsoResize) == 'object' && !o.alsoResize.parentNode) {
+ if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
+ else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
+ }else{
+ _store(o.alsoResize);
+ }
+ },
+
+ resize: function (event, ui) {
+ var self = $(this).data("resizable"), o = self.options, os = self.originalSize, op = self.originalPosition;
+
+ var delta = {
+ height: (self.size.height - os.height) || 0, width: (self.size.width - os.width) || 0,
+ top: (self.position.top - op.top) || 0, left: (self.position.left - op.left) || 0
+ },
+
+ _alsoResize = function (exp, c) {
+ $(exp).each(function() {
+ var el = $(this), start = $(this).data("resizable-alsoresize"), style = {},
+ css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ['width', 'height'] : ['width', 'height', 'top', 'left'];
+
+ $.each(css, function (i, prop) {
+ var sum = (start[prop]||0) + (delta[prop]||0);
+ if (sum && sum >= 0)
+ style[prop] = sum || null;
+ });
+
+ el.css(style);
+ });
+ };
+
+ if (typeof(o.alsoResize) == 'object' && !o.alsoResize.nodeType) {
+ $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
+ }else{
+ _alsoResize(o.alsoResize);
+ }
+ },
+
+ stop: function (event, ui) {
+ $(this).removeData("resizable-alsoresize");
+ }
+});
+
+$.ui.plugin.add("resizable", "animate", {
+
+ stop: function(event, ui) {
+ var self = $(this).data("resizable"), o = self.options;
+
+ var pr = self._proportionallyResizeElements, ista = pr.length && (/textarea/i).test(pr[0].nodeName),
+ soffseth = ista && $.ui.hasScroll(pr[0], 'left') /* TODO - jump height */ ? 0 : self.sizeDiff.height,
+ soffsetw = ista ? 0 : self.sizeDiff.width;
+
+ var style = { width: (self.size.width - soffsetw), height: (self.size.height - soffseth) },
+ left = (parseInt(self.element.css('left'), 10) + (self.position.left - self.originalPosition.left)) || null,
+ top = (parseInt(self.element.css('top'), 10) + (self.position.top - self.originalPosition.top)) || null;
+
+ self.element.animate(
+ $.extend(style, top && left ? { top: top, left: left } : {}), {
+ duration: o.animateDuration,
+ easing: o.animateEasing,
+ step: function() {
+
+ var data = {
+ width: parseInt(self.element.css('width'), 10),
+ height: parseInt(self.element.css('height'), 10),
+ top: parseInt(self.element.css('top'), 10),
+ left: parseInt(self.element.css('left'), 10)
+ };
+
+ if (pr && pr.length) $(pr[0]).css({ width: data.width, height: data.height });
+
+ // propagating resize, and updating values for each animation step
+ self._updateCache(data);
+ self._propagate("resize", event);
+
+ }
+ }
+ );
+ }
+
+});
+
+$.ui.plugin.add("resizable", "containment", {
+
+ start: function(event, ui) {
+ var self = $(this).data("resizable"), o = self.options, el = self.element;
+ var oc = o.containment, ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
+ if (!ce) return;
+
+ self.containerElement = $(ce);
+
+ if (/document/.test(oc) || oc == document) {
+ self.containerOffset = { left: 0, top: 0 };
+ self.containerPosition = { left: 0, top: 0 };
+
+ self.parentData = {
+ element: $(document), left: 0, top: 0,
+ width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
+ };
+ }
+
+ // i'm a node, so compute top, left, right, bottom
+ else {
+ var element = $(ce), p = [];
+ $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
+
+ self.containerOffset = element.offset();
+ self.containerPosition = element.position();
+ self.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
+
+ var co = self.containerOffset, ch = self.containerSize.height, cw = self.containerSize.width,
+ width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw ), height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
+
+ self.parentData = {
+ element: ce, left: co.left, top: co.top, width: width, height: height
+ };
+ }
+ },
+
+ resize: function(event, ui) {
+ var self = $(this).data("resizable"), o = self.options,
+ ps = self.containerSize, co = self.containerOffset, cs = self.size, cp = self.position,
+ pRatio = self._aspectRatio || event.shiftKey, cop = { top:0, left:0 }, ce = self.containerElement;
+
+ if (ce[0] != document && (/static/).test(ce.css('position'))) cop = co;
+
+ if (cp.left < (self._helper ? co.left : 0)) {
+ self.size.width = self.size.width + (self._helper ? (self.position.left - co.left) : (self.position.left - cop.left));
+ if (pRatio) self.size.height = self.size.width / o.aspectRatio;
+ self.position.left = o.helper ? co.left : 0;
+ }
+
+ if (cp.top < (self._helper ? co.top : 0)) {
+ self.size.height = self.size.height + (self._helper ? (self.position.top - co.top) : self.position.top);
+ if (pRatio) self.size.width = self.size.height * o.aspectRatio;
+ self.position.top = self._helper ? co.top : 0;
+ }
+
+ self.offset.left = self.parentData.left+self.position.left;
+ self.offset.top = self.parentData.top+self.position.top;
+
+ var woset = Math.abs( (self._helper ? self.offset.left - cop.left : (self.offset.left - cop.left)) + self.sizeDiff.width ),
+ hoset = Math.abs( (self._helper ? self.offset.top - cop.top : (self.offset.top - co.top)) + self.sizeDiff.height );
+
+ var isParent = self.containerElement.get(0) == self.element.parent().get(0),
+ isOffsetRelative = /relative|absolute/.test(self.containerElement.css('position'));
+
+ if(isParent && isOffsetRelative) woset -= self.parentData.left;
+
+ if (woset + self.size.width >= self.parentData.width) {
+ self.size.width = self.parentData.width - woset;
+ if (pRatio) self.size.height = self.size.width / self.aspectRatio;
+ }
+
+ if (hoset + self.size.height >= self.parentData.height) {
+ self.size.height = self.parentData.height - hoset;
+ if (pRatio) self.size.width = self.size.height * self.aspectRatio;
+ }
+ },
+
+ stop: function(event, ui){
+ var self = $(this).data("resizable"), o = self.options, cp = self.position,
+ co = self.containerOffset, cop = self.containerPosition, ce = self.containerElement;
+
+ var helper = $(self.helper), ho = helper.offset(), w = helper.outerWidth() - self.sizeDiff.width, h = helper.outerHeight() - self.sizeDiff.height;
+
+ if (self._helper && !o.animate && (/relative/).test(ce.css('position')))
+ $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
+
+ if (self._helper && !o.animate && (/static/).test(ce.css('position')))
+ $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
+
+ }
+});
+
+$.ui.plugin.add("resizable", "ghost", {
+
+ start: function(event, ui) {
+
+ var self = $(this).data("resizable"), o = self.options, cs = self.size;
+
+ self.ghost = self.originalElement.clone();
+ self.ghost
+ .css({ opacity: .25, display: 'block', position: 'relative', height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
+ .addClass('ui-resizable-ghost')
+ .addClass(typeof o.ghost == 'string' ? o.ghost : '');
+
+ self.ghost.appendTo(self.helper);
+
+ },
+
+ resize: function(event, ui){
+ var self = $(this).data("resizable"), o = self.options;
+ if (self.ghost) self.ghost.css({ position: 'relative', height: self.size.height, width: self.size.width });
+ },
+
+ stop: function(event, ui){
+ var self = $(this).data("resizable"), o = self.options;
+ if (self.ghost && self.helper) self.helper.get(0).removeChild(self.ghost.get(0));
+ }
+
+});
+
+$.ui.plugin.add("resizable", "grid", {
+
+ resize: function(event, ui) {
+ var self = $(this).data("resizable"), o = self.options, cs = self.size, os = self.originalSize, op = self.originalPosition, a = self.axis, ratio = o._aspectRatio || event.shiftKey;
+ o.grid = typeof o.grid == "number" ? [o.grid, o.grid] : o.grid;
+ var ox = Math.round((cs.width - os.width) / (o.grid[0]||1)) * (o.grid[0]||1), oy = Math.round((cs.height - os.height) / (o.grid[1]||1)) * (o.grid[1]||1);
+
+ if (/^(se|s|e)$/.test(a)) {
+ self.size.width = os.width + ox;
+ self.size.height = os.height + oy;
+ }
+ else if (/^(ne)$/.test(a)) {
+ self.size.width = os.width + ox;
+ self.size.height = os.height + oy;
+ self.position.top = op.top - oy;
+ }
+ else if (/^(sw)$/.test(a)) {
+ self.size.width = os.width + ox;
+ self.size.height = os.height + oy;
+ self.position.left = op.left - ox;
+ }
+ else {
+ self.size.width = os.width + ox;
+ self.size.height = os.height + oy;
+ self.position.top = op.top - oy;
+ self.position.left = op.left - ox;
+ }
+ }
+
+});
+
+var num = function(v) {
+ return parseInt(v, 10) || 0;
+};
+
+var isNumber = function(value) {
+ return !isNaN(parseInt(value, 10));
+};
+
+})(jQuery);
+/*
+ * jQuery UI Selectable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+$.widget("ui.selectable", $.ui.mouse, {
+ options: {
+ appendTo: 'body',
+ autoRefresh: true,
+ distance: 0,
+ filter: '*',
+ tolerance: 'touch'
+ },
+ _create: function() {
+ var self = this;
+
+ this.element.addClass("ui-selectable");
+
+ this.dragged = false;
+
+ // cache selectee children based on filter
+ var selectees;
+ this.refresh = function() {
+ selectees = $(self.options.filter, self.element[0]);
+ selectees.addClass("ui-selectee");
+ selectees.each(function() {
+ var $this = $(this);
+ var pos = $this.offset();
+ $.data(this, "selectable-item", {
+ element: this,
+ $element: $this,
+ left: pos.left,
+ top: pos.top,
+ right: pos.left + $this.outerWidth(),
+ bottom: pos.top + $this.outerHeight(),
+ startselected: false,
+ selected: $this.hasClass('ui-selected'),
+ selecting: $this.hasClass('ui-selecting'),
+ unselecting: $this.hasClass('ui-unselecting')
+ });
+ });
+ };
+ this.refresh();
+
+ this.selectees = selectees.addClass("ui-selectee");
+
+ this._mouseInit();
+
+ this.helper = $("<div class='ui-selectable-helper'></div>");
+ },
+
+ destroy: function() {
+ this.selectees
+ .removeClass("ui-selectee")
+ .removeData("selectable-item");
+ this.element
+ .removeClass("ui-selectable ui-selectable-disabled")
+ .removeData("selectable")
+ .unbind(".selectable");
+ this._mouseDestroy();
+
+ return this;
+ },
+
+ _mouseStart: function(event) {
+ var self = this;
+
+ this.opos = [event.pageX, event.pageY];
+
+ if (this.options.disabled)
+ return;
+
+ var options = this.options;
+
+ this.selectees = $(options.filter, this.element[0]);
+
+ this._trigger("start", event);
+
+ $(options.appendTo).append(this.helper);
+ // position helper (lasso)
+ this.helper.css({
+ "left": event.clientX,
+ "top": event.clientY,
+ "width": 0,
+ "height": 0
+ });
+
+ if (options.autoRefresh) {
+ this.refresh();
+ }
+
+ this.selectees.filter('.ui-selected').each(function() {
+ var selectee = $.data(this, "selectable-item");
+ selectee.startselected = true;
+ if (!event.metaKey && !event.ctrlKey) {
+ selectee.$element.removeClass('ui-selected');
+ selectee.selected = false;
+ selectee.$element.addClass('ui-unselecting');
+ selectee.unselecting = true;
+ // selectable UNSELECTING callback
+ self._trigger("unselecting", event, {
+ unselecting: selectee.element
+ });
+ }
+ });
+
+ $(event.target).parents().andSelf().each(function() {
+ var selectee = $.data(this, "selectable-item");
+ if (selectee) {
+ var doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass('ui-selected');
+ selectee.$element
+ .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
+ .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
+ selectee.unselecting = !doSelect;
+ selectee.selecting = doSelect;
+ selectee.selected = doSelect;
+ // selectable (UN)SELECTING callback
+ if (doSelect) {
+ self._trigger("selecting", event, {
+ selecting: selectee.element
+ });
+ } else {
+ self._trigger("unselecting", event, {
+ unselecting: selectee.element
+ });
+ }
+ return false;
+ }
+ });
+
+ },
+
+ _mouseDrag: function(event) {
+ var self = this;
+ this.dragged = true;
+
+ if (this.options.disabled)
+ return;
+
+ var options = this.options;
+
+ var x1 = this.opos[0], y1 = this.opos[1], x2 = event.pageX, y2 = event.pageY;
+ if (x1 > x2) { var tmp = x2; x2 = x1; x1 = tmp; }
+ if (y1 > y2) { var tmp = y2; y2 = y1; y1 = tmp; }
+ this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
+
+ this.selectees.each(function() {
+ var selectee = $.data(this, "selectable-item");
+ //prevent helper from being selected if appendTo: selectable
+ if (!selectee || selectee.element == self.element[0])
+ return;
+ var hit = false;
+ if (options.tolerance == 'touch') {
+ hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
+ } else if (options.tolerance == 'fit') {
+ hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
+ }
+
+ if (hit) {
+ // SELECT
+ if (selectee.selected) {
+ selectee.$element.removeClass('ui-selected');
+ selectee.selected = false;
+ }
+ if (selectee.unselecting) {
+ selectee.$element.removeClass('ui-unselecting');
+ selectee.unselecting = false;
+ }
+ if (!selectee.selecting) {
+ selectee.$element.addClass('ui-selecting');
+ selectee.selecting = true;
+ // selectable SELECTING callback
+ self._trigger("selecting", event, {
+ selecting: selectee.element
+ });
+ }
+ } else {
+ // UNSELECT
+ if (selectee.selecting) {
+ if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
+ selectee.$element.removeClass('ui-selecting');
+ selectee.selecting = false;
+ selectee.$element.addClass('ui-selected');
+ selectee.selected = true;
+ } else {
+ selectee.$element.removeClass('ui-selecting');
+ selectee.selecting = false;
+ if (selectee.startselected) {
+ selectee.$element.addClass('ui-unselecting');
+ selectee.unselecting = true;
+ }
+ // selectable UNSELECTING callback
+ self._trigger("unselecting", event, {
+ unselecting: selectee.element
+ });
+ }
+ }
+ if (selectee.selected) {
+ if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
+ selectee.$element.removeClass('ui-selected');
+ selectee.selected = false;
+
+ selectee.$element.addClass('ui-unselecting');
+ selectee.unselecting = true;
+ // selectable UNSELECTING callback
+ self._trigger("unselecting", event, {
+ unselecting: selectee.element
+ });
+ }
+ }
+ }
+ });
+
+ return false;
+ },
+
+ _mouseStop: function(event) {
+ var self = this;
+
+ this.dragged = false;
+
+ var options = this.options;
+
+ $('.ui-unselecting', this.element[0]).each(function() {
+ var selectee = $.data(this, "selectable-item");
+ selectee.$element.removeClass('ui-unselecting');
+ selectee.unselecting = false;
+ selectee.startselected = false;
+ self._trigger("unselected", event, {
+ unselected: selectee.element
+ });
+ });
+ $('.ui-selecting', this.element[0]).each(function() {
+ var selectee = $.data(this, "selectable-item");
+ selectee.$element.removeClass('ui-selecting').addClass('ui-selected');
+ selectee.selecting = false;
+ selectee.selected = true;
+ selectee.startselected = true;
+ self._trigger("selected", event, {
+ selected: selectee.element
+ });
+ });
+ this._trigger("stop", event);
+
+ this.helper.remove();
+
+ return false;
+ }
+
+});
+
+$.extend($.ui.selectable, {
+ version: "1.8.18"
+});
+
+})(jQuery);
+/*
+ * jQuery UI Sortable 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Sortables
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+$.widget("ui.sortable", $.ui.mouse, {
+ widgetEventPrefix: "sort",
+ ready: false,
+ options: {
+ appendTo: "parent",
+ axis: false,
+ connectWith: false,
+ containment: false,
+ cursor: 'auto',
+ cursorAt: false,
+ dropOnEmpty: true,
+ forcePlaceholderSize: false,
+ forceHelperSize: false,
+ grid: false,
+ handle: false,
+ helper: "original",
+ items: '> *',
+ opacity: false,
+ placeholder: false,
+ revert: false,
+ scroll: true,
+ scrollSensitivity: 20,
+ scrollSpeed: 20,
+ scope: "default",
+ tolerance: "intersect",
+ zIndex: 1000
+ },
+ _create: function() {
+
+ var o = this.options;
+ this.containerCache = {};
+ this.element.addClass("ui-sortable");
+
+ //Get the items
+ this.refresh();
+
+ //Let's determine if the items are being displayed horizontally
+ this.floating = this.items.length ? o.axis === 'x' || (/left|right/).test(this.items[0].item.css('float')) || (/inline|table-cell/).test(this.items[0].item.css('display')) : false;
+
+ //Let's determine the parent's offset
+ this.offset = this.element.offset();
+
+ //Initialize mouse events for interaction
+ this._mouseInit();
+
+ //We're ready to go
+ this.ready = true
+
+ },
+
+ destroy: function() {
+ $.Widget.prototype.destroy.call( this );
+ this.element
+ .removeClass("ui-sortable ui-sortable-disabled");
+ this._mouseDestroy();
+
+ for ( var i = this.items.length - 1; i >= 0; i-- )
+ this.items[i].item.removeData(this.widgetName + "-item");
+
+ return this;
+ },
+
+ _setOption: function(key, value){
+ if ( key === "disabled" ) {
+ this.options[ key ] = value;
+
+ this.widget()
+ [ value ? "addClass" : "removeClass"]( "ui-sortable-disabled" );
+ } else {
+ // Don't call widget base _setOption for disable as it adds ui-state-disabled class
+ $.Widget.prototype._setOption.apply(this, arguments);
+ }
+ },
+
+ _mouseCapture: function(event, overrideHandle) {
+ var that = this;
+
+ if (this.reverting) {
+ return false;
+ }
+
+ if(this.options.disabled || this.options.type == 'static') return false;
+
+ //We have to refresh the items data once first
+ this._refreshItems(event);
+
+ //Find out if the clicked node (or one of its parents) is a actual item in this.items
+ var currentItem = null, self = this, nodes = $(event.target).parents().each(function() {
+ if($.data(this, that.widgetName + '-item') == self) {
+ currentItem = $(this);
+ return false;
+ }
+ });
+ if($.data(event.target, that.widgetName + '-item') == self) currentItem = $(event.target);
+
+ if(!currentItem) return false;
+ if(this.options.handle && !overrideHandle) {
+ var validHandle = false;
+
+ $(this.options.handle, currentItem).find("*").andSelf().each(function() { if(this == event.target) validHandle = true; });
+ if(!validHandle) return false;
+ }
+
+ this.currentItem = currentItem;
+ this._removeCurrentsFromItems();
+ return true;
+
+ },
+
+ _mouseStart: function(event, overrideHandle, noActivation) {
+
+ var o = this.options, self = this;
+ this.currentContainer = this;
+
+ //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
+ this.refreshPositions();
+
+ //Create and append the visible helper
+ this.helper = this._createHelper(event);
+
+ //Cache the helper size
+ this._cacheHelperProportions();
+
+ /*
+ * - Position generation -
+ * This block generates everything position related - it's the core of draggables.
+ */
+
+ //Cache the margins of the original element
+ this._cacheMargins();
+
+ //Get the next scrolling parent
+ this.scrollParent = this.helper.scrollParent();
+
+ //The element's absolute position on the page minus margins
+ this.offset = this.currentItem.offset();
+ this.offset = {
+ top: this.offset.top - this.margins.top,
+ left: this.offset.left - this.margins.left
+ };
+
+ // Only after we got the offset, we can change the helper's position to absolute
+ // TODO: Still need to figure out a way to make relative sorting possible
+ this.helper.css("position", "absolute");
+ this.cssPosition = this.helper.css("position");
+
+ $.extend(this.offset, {
+ click: { //Where the click happened, relative to the element
+ left: event.pageX - this.offset.left,
+ top: event.pageY - this.offset.top
+ },
+ parent: this._getParentOffset(),
+ relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
+ });
+
+ //Generate the original position
+ this.originalPosition = this._generatePosition(event);
+ this.originalPageX = event.pageX;
+ this.originalPageY = event.pageY;
+
+ //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
+ (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
+
+ //Cache the former DOM position
+ this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
+
+ //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
+ if(this.helper[0] != this.currentItem[0]) {
+ this.currentItem.hide();
+ }
+
+ //Create the placeholder
+ this._createPlaceholder();
+
+ //Set a containment if given in the options
+ if(o.containment)
+ this._setContainment();
+
+ if(o.cursor) { // cursor option
+ if ($('body').css("cursor")) this._storedCursor = $('body').css("cursor");
+ $('body').css("cursor", o.cursor);
+ }
+
+ if(o.opacity) { // opacity option
+ if (this.helper.css("opacity")) this._storedOpacity = this.helper.css("opacity");
+ this.helper.css("opacity", o.opacity);
+ }
+
+ if(o.zIndex) { // zIndex option
+ if (this.helper.css("zIndex")) this._storedZIndex = this.helper.css("zIndex");
+ this.helper.css("zIndex", o.zIndex);
+ }
+
+ //Prepare scrolling
+ if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML')
+ this.overflowOffset = this.scrollParent.offset();
+
+ //Call callbacks
+ this._trigger("start", event, this._uiHash());
+
+ //Recache the helper size
+ if(!this._preserveHelperProportions)
+ this._cacheHelperProportions();
+
+
+ //Post 'activate' events to possible containers
+ if(!noActivation) {
+ for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i]._trigger("activate", event, self._uiHash(this)); }
+ }
+
+ //Prepare possible droppables
+ if($.ui.ddmanager)
+ $.ui.ddmanager.current = this;
+
+ if ($.ui.ddmanager && !o.dropBehaviour)
+ $.ui.ddmanager.prepareOffsets(this, event);
+
+ this.dragging = true;
+
+ this.helper.addClass("ui-sortable-helper");
+ this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
+ return true;
+
+ },
+
+ _mouseDrag: function(event) {
+
+ //Compute the helpers position
+ this.position = this._generatePosition(event);
+ this.positionAbs = this._convertPositionTo("absolute");
+
+ if (!this.lastPositionAbs) {
+ this.lastPositionAbs = this.positionAbs;
+ }
+
+ //Do scrolling
+ if(this.options.scroll) {
+ var o = this.options, scrolled = false;
+ if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
+
+ if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
+ else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
+
+ if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
+ else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
+
+ } else {
+
+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
+ scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
+ else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
+ scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
+
+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
+ scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
+ else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
+ scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
+
+ }
+
+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
+ $.ui.ddmanager.prepareOffsets(this, event);
+ }
+
+ //Regenerate the absolute position used for position checks
+ this.positionAbs = this._convertPositionTo("absolute");
+
+ //Set the helper position
+ if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
+ if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
+
+ //Rearrange
+ for (var i = this.items.length - 1; i >= 0; i--) {
+
+ //Cache variables and intersection, continue if no intersection
+ var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
+ if (!intersection) continue;
+
+ if(itemElement != this.currentItem[0] //cannot intersect with itself
+ && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
+ && !$.ui.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
+ && (this.options.type == 'semi-dynamic' ? !$.ui.contains(this.element[0], itemElement) : true)
+ //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
+ ) {
+
+ this.direction = intersection == 1 ? "down" : "up";
+
+ if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
+ this._rearrange(event, item);
+ } else {
+ break;
+ }
+
+ this._trigger("change", event, this._uiHash());
+ break;
+ }
+ }
+
+ //Post events to containers
+ this._contactContainers(event);
+
+ //Interconnect with droppables
+ if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
+
+ //Call callbacks
+ this._trigger('sort', event, this._uiHash());
+
+ this.lastPositionAbs = this.positionAbs;
+ return false;
+
+ },
+
+ _mouseStop: function(event, noPropagation) {
+
+ if(!event) return;
+
+ //If we are using droppables, inform the manager about the drop
+ if ($.ui.ddmanager && !this.options.dropBehaviour)
+ $.ui.ddmanager.drop(this, event);
+
+ if(this.options.revert) {
+ var self = this;
+ var cur = self.placeholder.offset();
+
+ self.reverting = true;
+
+ $(this.helper).animate({
+ left: cur.left - this.offset.parent.left - self.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
+ top: cur.top - this.offset.parent.top - self.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
+ }, parseInt(this.options.revert, 10) || 500, function() {
+ self._clear(event);
+ });
+ } else {
+ this._clear(event, noPropagation);
+ }
+
+ return false;
+
+ },
+
+ cancel: function() {
+
+ var self = this;
+
+ if(this.dragging) {
+
+ this._mouseUp({ target: null });
+
+ if(this.options.helper == "original")
+ this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
+ else
+ this.currentItem.show();
+
+ //Post deactivating events to containers
+ for (var i = this.containers.length - 1; i >= 0; i--){
+ this.containers[i]._trigger("deactivate", null, self._uiHash(this));
+ if(this.containers[i].containerCache.over) {
+ this.containers[i]._trigger("out", null, self._uiHash(this));
+ this.containers[i].containerCache.over = 0;
+ }
+ }
+
+ }
+
+ if (this.placeholder) {
+ //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
+ if(this.placeholder[0].parentNode) this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
+ if(this.options.helper != "original" && this.helper && this.helper[0].parentNode) this.helper.remove();
+
+ $.extend(this, {
+ helper: null,
+ dragging: false,
+ reverting: false,
+ _noFinalSort: null
+ });
+
+ if(this.domPosition.prev) {
+ $(this.domPosition.prev).after(this.currentItem);
+ } else {
+ $(this.domPosition.parent).prepend(this.currentItem);
+ }
+ }
+
+ return this;
+
+ },
+
+ serialize: function(o) {
+
+ var items = this._getItemsAsjQuery(o && o.connected);
+ var str = []; o = o || {};
+
+ $(items).each(function() {
+ var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
+ if(res) str.push((o.key || res[1]+'[]')+'='+(o.key && o.expression ? res[1] : res[2]));
+ });
+
+ if(!str.length && o.key) {
+ str.push(o.key + '=');
+ }
+
+ return str.join('&');
+
+ },
+
+ toArray: function(o) {
+
+ var items = this._getItemsAsjQuery(o && o.connected);
+ var ret = []; o = o || {};
+
+ items.each(function() { ret.push($(o.item || this).attr(o.attribute || 'id') || ''); });
+ return ret;
+
+ },
+
+ /* Be careful with the following core functions */
+ _intersectsWith: function(item) {
+
+ var x1 = this.positionAbs.left,
+ x2 = x1 + this.helperProportions.width,
+ y1 = this.positionAbs.top,
+ y2 = y1 + this.helperProportions.height;
+
+ var l = item.left,
+ r = l + item.width,
+ t = item.top,
+ b = t + item.height;
+
+ var dyClick = this.offset.click.top,
+ dxClick = this.offset.click.left;
+
+ var isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
+
+ if( this.options.tolerance == "pointer"
+ || this.options.forcePointerForContainers
+ || (this.options.tolerance != "pointer" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height'])
+ ) {
+ return isOverElement;
+ } else {
+
+ return (l < x1 + (this.helperProportions.width / 2) // Right Half
+ && x2 - (this.helperProportions.width / 2) < r // Left Half
+ && t < y1 + (this.helperProportions.height / 2) // Bottom Half
+ && y2 - (this.helperProportions.height / 2) < b ); // Top Half
+
+ }
+ },
+
+ _intersectsWithPointer: function(item) {
+
+ var isOverElementHeight = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
+ isOverElementWidth = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
+ isOverElement = isOverElementHeight && isOverElementWidth,
+ verticalDirection = this._getDragVerticalDirection(),
+ horizontalDirection = this._getDragHorizontalDirection();
+
+ if (!isOverElement)
+ return false;
+
+ return this.floating ?
+ ( ((horizontalDirection && horizontalDirection == "right") || verticalDirection == "down") ? 2 : 1 )
+ : ( verticalDirection && (verticalDirection == "down" ? 2 : 1) );
+
+ },
+
+ _intersectsWithSides: function(item) {
+
+ var isOverBottomHalf = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
+ isOverRightHalf = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
+ verticalDirection = this._getDragVerticalDirection(),
+ horizontalDirection = this._getDragHorizontalDirection();
+
+ if (this.floating && horizontalDirection) {
+ return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
+ } else {
+ return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && !isOverBottomHalf));
+ }
+
+ },
+
+ _getDragVerticalDirection: function() {
+ var delta = this.positionAbs.top - this.lastPositionAbs.top;
+ return delta != 0 && (delta > 0 ? "down" : "up");
+ },
+
+ _getDragHorizontalDirection: function() {
+ var delta = this.positionAbs.left - this.lastPositionAbs.left;
+ return delta != 0 && (delta > 0 ? "right" : "left");
+ },
+
+ refresh: function(event) {
+ this._refreshItems(event);
+ this.refreshPositions();
+ return this;
+ },
+
+ _connectWith: function() {
+ var options = this.options;
+ return options.connectWith.constructor == String
+ ? [options.connectWith]
+ : options.connectWith;
+ },
+
+ _getItemsAsjQuery: function(connected) {
+
+ var self = this;
+ var items = [];
+ var queries = [];
+ var connectWith = this._connectWith();
+
+ if(connectWith && connected) {
+ for (var i = connectWith.length - 1; i >= 0; i--){
+ var cur = $(connectWith[i]);
+ for (var j = cur.length - 1; j >= 0; j--){
+ var inst = $.data(cur[j], this.widgetName);
+ if(inst && inst != this && !inst.options.disabled) {
+ queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), inst]);
+ }
+ };
+ };
+ }
+
+ queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), this]);
+
+ for (var i = queries.length - 1; i >= 0; i--){
+ queries[i][0].each(function() {
+ items.push(this);
+ });
+ };
+
+ return $(items);
+
+ },
+
+ _removeCurrentsFromItems: function() {
+
+ var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
+
+ for (var i=0; i < this.items.length; i++) {
+
+ for (var j=0; j < list.length; j++) {
+ if(list[j] == this.items[i].item[0])
+ this.items.splice(i,1);
+ };
+
+ };
+
+ },
+
+ _refreshItems: function(event) {
+
+ this.items = [];
+ this.containers = [this];
+ var items = this.items;
+ var self = this;
+ var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]];
+ var connectWith = this._connectWith();
+
+ if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
+ for (var i = connectWith.length - 1; i >= 0; i--){
+ var cur = $(connectWith[i]);
+ for (var j = cur.length - 1; j >= 0; j--){
+ var inst = $.data(cur[j], this.widgetName);
+ if(inst && inst != this && !inst.options.disabled) {
+ queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
+ this.containers.push(inst);
+ }
+ };
+ };
+ }
+
+ for (var i = queries.length - 1; i >= 0; i--) {
+ var targetData = queries[i][1];
+ var _queries = queries[i][0];
+
+ for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
+ var item = $(_queries[j]);
+
+ item.data(this.widgetName + '-item', targetData); // Data for target checking (mouse manager)
+
+ items.push({
+ item: item,
+ instance: targetData,
+ width: 0, height: 0,
+ left: 0, top: 0
+ });
+ };
+ };
+
+ },
+
+ refreshPositions: function(fast) {
+
+ //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
+ if(this.offsetParent && this.helper) {
+ this.offset.parent = this._getParentOffset();
+ }
+
+ for (var i = this.items.length - 1; i >= 0; i--){
+ var item = this.items[i];
+
+ //We ignore calculating positions of all connected containers when we're not over them
+ if(item.instance != this.currentContainer && this.currentContainer && item.item[0] != this.currentItem[0])
+ continue;
+
+ var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
+
+ if (!fast) {
+ item.width = t.outerWidth();
+ item.height = t.outerHeight();
+ }
+
+ var p = t.offset();
+ item.left = p.left;
+ item.top = p.top;
+ };
+
+ if(this.options.custom && this.options.custom.refreshContainers) {
+ this.options.custom.refreshContainers.call(this);
+ } else {
+ for (var i = this.containers.length - 1; i >= 0; i--){
+ var p = this.containers[i].element.offset();
+ this.containers[i].containerCache.left = p.left;
+ this.containers[i].containerCache.top = p.top;
+ this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
+ this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
+ };
+ }
+
+ return this;
+ },
+
+ _createPlaceholder: function(that) {
+
+ var self = that || this, o = self.options;
+
+ if(!o.placeholder || o.placeholder.constructor == String) {
+ var className = o.placeholder;
+ o.placeholder = {
+ element: function() {
+
+ var el = $(document.createElement(self.currentItem[0].nodeName))
+ .addClass(className || self.currentItem[0].className+" ui-sortable-placeholder")
+ .removeClass("ui-sortable-helper")[0];
+
+ if(!className)
+ el.style.visibility = "hidden";
+
+ return el;
+ },
+ update: function(container, p) {
+
+ // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
+ // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
+ if(className && !o.forcePlaceholderSize) return;
+
+ //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
+ if(!p.height()) { p.height(self.currentItem.innerHeight() - parseInt(self.currentItem.css('paddingTop')||0, 10) - parseInt(self.currentItem.css('paddingBottom')||0, 10)); };
+ if(!p.width()) { p.width(self.currentItem.innerWidth() - parseInt(self.currentItem.css('paddingLeft')||0, 10) - parseInt(self.currentItem.css('paddingRight')||0, 10)); };
+ }
+ };
+ }
+
+ //Create the placeholder
+ self.placeholder = $(o.placeholder.element.call(self.element, self.currentItem));
+
+ //Append it after the actual current item
+ self.currentItem.after(self.placeholder);
+
+ //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
+ o.placeholder.update(self, self.placeholder);
+
+ },
+
+ _contactContainers: function(event) {
+
+ // get innermost container that intersects with item
+ var innermostContainer = null, innermostIndex = null;
+
+
+ for (var i = this.containers.length - 1; i >= 0; i--){
+
+ // never consider a container that's located within the item itself
+ if($.ui.contains(this.currentItem[0], this.containers[i].element[0]))
+ continue;
+
+ if(this._intersectsWith(this.containers[i].containerCache)) {
+
+ // if we've already found a container and it's more "inner" than this, then continue
+ if(innermostContainer && $.ui.contains(this.containers[i].element[0], innermostContainer.element[0]))
+ continue;
+
+ innermostContainer = this.containers[i];
+ innermostIndex = i;
+
+ } else {
+ // container doesn't intersect. trigger "out" event if necessary
+ if(this.containers[i].containerCache.over) {
+ this.containers[i]._trigger("out", event, this._uiHash(this));
+ this.containers[i].containerCache.over = 0;
+ }
+ }
+
+ }
+
+ // if no intersecting containers found, return
+ if(!innermostContainer) return;
+
+ // move the item into the container if it's not there already
+ if(this.containers.length === 1) {
+ this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
+ this.containers[innermostIndex].containerCache.over = 1;
+ } else if(this.currentContainer != this.containers[innermostIndex]) {
+
+ //When entering a new container, we will find the item with the least distance and append our item near it
+ var dist = 10000; var itemWithLeastDistance = null; var base = this.positionAbs[this.containers[innermostIndex].floating ? 'left' : 'top'];
+ for (var j = this.items.length - 1; j >= 0; j--) {
+ if(!$.ui.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) continue;
+ var cur = this.items[j][this.containers[innermostIndex].floating ? 'left' : 'top'];
+ if(Math.abs(cur - base) < dist) {
+ dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
+ }
+ }
+
+ if(!itemWithLeastDistance && !this.options.dropOnEmpty) //Check if dropOnEmpty is enabled
+ return;
+
+ this.currentContainer = this.containers[innermostIndex];
+ itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
+ this._trigger("change", event, this._uiHash());
+ this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
+
+ //Update the placeholder
+ this.options.placeholder.update(this.currentContainer, this.placeholder);
+
+ this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
+ this.containers[innermostIndex].containerCache.over = 1;
+ }
+
+
+ },
+
+ _createHelper: function(event) {
+
+ var o = this.options;
+ var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper == 'clone' ? this.currentItem.clone() : this.currentItem);
+
+ if(!helper.parents('body').length) //Add the helper to the DOM if that didn't happen already
+ $(o.appendTo != 'parent' ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
+
+ if(helper[0] == this.currentItem[0])
+ this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") };
+
+ if(helper[0].style.width == '' || o.forceHelperSize) helper.width(this.currentItem.width());
+ if(helper[0].style.height == '' || o.forceHelperSize) helper.height(this.currentItem.height());
+
+ return helper;
+
+ },
+
+ _adjustOffsetFromHelper: function(obj) {
+ if (typeof obj == 'string') {
+ obj = obj.split(' ');
+ }
+ if ($.isArray(obj)) {
+ obj = {left: +obj[0], top: +obj[1] || 0};
+ }
+ if ('left' in obj) {
+ this.offset.click.left = obj.left + this.margins.left;
+ }
+ if ('right' in obj) {
+ this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
+ }
+ if ('top' in obj) {
+ this.offset.click.top = obj.top + this.margins.top;
+ }
+ if ('bottom' in obj) {
+ this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
+ }
+ },
+
+ _getParentOffset: function() {
+
+
+ //Get the offsetParent and cache its position
+ this.offsetParent = this.helper.offsetParent();
+ var po = this.offsetParent.offset();
+
+ // This is a special case where we need to modify a offset calculated on start, since the following happened:
+ // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
+ // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
+ // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
+ if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
+ po.left += this.scrollParent.scrollLeft();
+ po.top += this.scrollParent.scrollTop();
+ }
+
+ if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
+ || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
+ po = { top: 0, left: 0 };
+
+ return {
+ top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
+ left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
+ };
+
+ },
+
+ _getRelativeOffset: function() {
+
+ if(this.cssPosition == "relative") {
+ var p = this.currentItem.position();
+ return {
+ top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
+ left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
+ };
+ } else {
+ return { top: 0, left: 0 };
+ }
+
+ },
+
+ _cacheMargins: function() {
+ this.margins = {
+ left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
+ top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
+ };
+ },
+
+ _cacheHelperProportions: function() {
+ this.helperProportions = {
+ width: this.helper.outerWidth(),
+ height: this.helper.outerHeight()
+ };
+ },
+
+ _setContainment: function() {
+
+ var o = this.options;
+ if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
+ if(o.containment == 'document' || o.containment == 'window') this.containment = [
+ 0 - this.offset.relative.left - this.offset.parent.left,
+ 0 - this.offset.relative.top - this.offset.parent.top,
+ $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
+ ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
+ ];
+
+ if(!(/^(document|window|parent)$/).test(o.containment)) {
+ var ce = $(o.containment)[0];
+ var co = $(o.containment).offset();
+ var over = ($(ce).css("overflow") != 'hidden');
+
+ this.containment = [
+ co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
+ co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
+ co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
+ co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
+ ];
+ }
+
+ },
+
+ _convertPositionTo: function(d, pos) {
+
+ if(!pos) pos = this.position;
+ var mod = d == "absolute" ? 1 : -1;
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+
+ return {
+ top: (
+ pos.top // The absolute mouse position
+ + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
+ + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
+ - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
+ ),
+ left: (
+ pos.left // The absolute mouse position
+ + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
+ + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
+ - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
+ )
+ };
+
+ },
+
+ _generatePosition: function(event) {
+
+ var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+
+ // This is another very weird special case that only happens for relative elements:
+ // 1. If the css position is relative
+ // 2. and the scroll parent is the document or similar to the offset parent
+ // we have to refresh the relative offset during the scroll so there are no jumps
+ if(this.cssPosition == 'relative' && !(this.scrollParent[0] != document && this.scrollParent[0] != this.offsetParent[0])) {
+ this.offset.relative = this._getRelativeOffset();
+ }
+
+ var pageX = event.pageX;
+ var pageY = event.pageY;
+
+ /*
+ * - Position constraining -
+ * Constrain the position to a mix of grid, containment.
+ */
+
+ if(this.originalPosition) { //If we are not dragging yet, we won't check for options
+
+ if(this.containment) {
+ if(event.pageX - this.offset.click.left < this.containment[0]) pageX = this.containment[0] + this.offset.click.left;
+ if(event.pageY - this.offset.click.top < this.containment[1]) pageY = this.containment[1] + this.offset.click.top;
+ if(event.pageX - this.offset.click.left > this.containment[2]) pageX = this.containment[2] + this.offset.click.left;
+ if(event.pageY - this.offset.click.top > this.containment[3]) pageY = this.containment[3] + this.offset.click.top;
+ }
+
+ if(o.grid) {
+ var top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
+ pageY = this.containment ? (!(top - this.offset.click.top < this.containment[1] || top - this.offset.click.top > this.containment[3]) ? top : (!(top - this.offset.click.top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
+
+ var left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
+ pageX = this.containment ? (!(left - this.offset.click.left < this.containment[0] || left - this.offset.click.left > this.containment[2]) ? left : (!(left - this.offset.click.left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
+ }
+
+ }
+
+ return {
+ top: (
+ pageY // The absolute mouse position
+ - this.offset.click.top // Click offset (relative to the element)
+ - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent
+ - this.offset.parent.top // The offsetParent's offset without borders (offset + border)
+ + ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
+ ),
+ left: (
+ pageX // The absolute mouse position
+ - this.offset.click.left // Click offset (relative to the element)
+ - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent
+ - this.offset.parent.left // The offsetParent's offset without borders (offset + border)
+ + ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
+ )
+ };
+
+ },
+
+ _rearrange: function(event, i, a, hardRefresh) {
+
+ a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction == 'down' ? i.item[0] : i.item[0].nextSibling));
+
+ //Various things done here to improve the performance:
+ // 1. we create a setTimeout, that calls refreshPositions
+ // 2. on the instance, we have a counter variable, that get's higher after every append
+ // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
+ // 4. this lets only the last addition to the timeout stack through
+ this.counter = this.counter ? ++this.counter : 1;
+ var self = this, counter = this.counter;
+
+ window.setTimeout(function() {
+ if(counter == self.counter) self.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
+ },0);
+
+ },
+
+ _clear: function(event, noPropagation) {
+
+ this.reverting = false;
+ // We delay all events that have to be triggered to after the point where the placeholder has been removed and
+ // everything else normalized again
+ var delayedTriggers = [], self = this;
+
+ // We first have to update the dom position of the actual currentItem
+ // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
+ if(!this._noFinalSort && this.currentItem.parent().length) this.placeholder.before(this.currentItem);
+ this._noFinalSort = null;
+
+ if(this.helper[0] == this.currentItem[0]) {
+ for(var i in this._storedCSS) {
+ if(this._storedCSS[i] == 'auto' || this._storedCSS[i] == 'static') this._storedCSS[i] = '';
+ }
+ this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
+ } else {
+ this.currentItem.show();
+ }
+
+ if(this.fromOutside && !noPropagation) delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
+ if((this.fromOutside || this.domPosition.prev != this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent != this.currentItem.parent()[0]) && !noPropagation) delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
+ if(!$.ui.contains(this.element[0], this.currentItem[0])) { //Node was moved out of the current element
+ if(!noPropagation) delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
+ for (var i = this.containers.length - 1; i >= 0; i--){
+ if($.ui.contains(this.containers[i].element[0], this.currentItem[0]) && !noPropagation) {
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
+ }
+ };
+ };
+
+ //Post events to containers
+ for (var i = this.containers.length - 1; i >= 0; i--){
+ if(!noPropagation) delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
+ if(this.containers[i].containerCache.over) {
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
+ this.containers[i].containerCache.over = 0;
+ }
+ }
+
+ //Do what was originally in plugins
+ if(this._storedCursor) $('body').css("cursor", this._storedCursor); //Reset cursor
+ if(this._storedOpacity) this.helper.css("opacity", this._storedOpacity); //Reset opacity
+ if(this._storedZIndex) this.helper.css("zIndex", this._storedZIndex == 'auto' ? '' : this._storedZIndex); //Reset z-index
+
+ this.dragging = false;
+ if(this.cancelHelperRemoval) {
+ if(!noPropagation) {
+ this._trigger("beforeStop", event, this._uiHash());
+ for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
+ this._trigger("stop", event, this._uiHash());
+ }
+ return false;
+ }
+
+ if(!noPropagation) this._trigger("beforeStop", event, this._uiHash());
+
+ //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
+ this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
+
+ if(this.helper[0] != this.currentItem[0]) this.helper.remove(); this.helper = null;
+
+ if(!noPropagation) {
+ for (var i=0; i < delayedTriggers.length; i++) { delayedTriggers[i].call(this, event); }; //Trigger all delayed events
+ this._trigger("stop", event, this._uiHash());
+ }
+
+ this.fromOutside = false;
+ return true;
+
+ },
+
+ _trigger: function() {
+ if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
+ this.cancel();
+ }
+ },
+
+ _uiHash: function(inst) {
+ var self = inst || this;
+ return {
+ helper: self.helper,
+ placeholder: self.placeholder || $([]),
+ position: self.position,
+ originalPosition: self.originalPosition,
+ offset: self.positionAbs,
+ item: self.currentItem,
+ sender: inst ? inst.element : null
+ };
+ }
+
+});
+
+$.extend($.ui.sortable, {
+ version: "1.8.18"
+});
+
+})(jQuery);
+/*
+ * jQuery UI Effects 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/
+ */
+;jQuery.effects || (function($, undefined) {
+
+$.effects = {};
+
+
+
+/******************************************************************************/
+/****************************** COLOR ANIMATIONS ******************************/
+/******************************************************************************/
+
+// override the animation for color styles
+$.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor',
+ 'borderRightColor', 'borderTopColor', 'borderColor', 'color', 'outlineColor'],
+function(i, attr) {
+ $.fx.step[attr] = function(fx) {
+ if (!fx.colorInit) {
+ fx.start = getColor(fx.elem, attr);
+ fx.end = getRGB(fx.end);
+ fx.colorInit = true;
+ }
+
+ fx.elem.style[attr] = 'rgb(' +
+ Math.max(Math.min(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10), 255), 0) + ',' +
+ Math.max(Math.min(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10), 255), 0) + ',' +
+ Math.max(Math.min(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10), 255), 0) + ')';
+ };
+});
+
+// Color Conversion functions from highlightFade
+// By Blair Mitchelmore
+// http://jquery.offput.ca/highlightFade/
+
+// Parse strings looking for color tuples [255,255,255]
+function getRGB(color) {
+ var result;
+
+ // Check if we're already dealing with an array of colors
+ if ( color && color.constructor == Array && color.length == 3 )
+ return color;
+
+ // Look for rgb(num,num,num)
+ if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
+ return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)];
+
+ // Look for rgb(num%,num%,num%)
+ if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
+ return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
+
+ // Look for #a0b1c2
+ if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
+ return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
+
+ // Look for #fff
+ if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
+ return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
+
+ // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
+ if (result = /rgba\(0, 0, 0, 0\)/.exec(color))
+ return colors['transparent'];
+
+ // Otherwise, we're most likely dealing with a named color
+ return colors[$.trim(color).toLowerCase()];
+}
+
+function getColor(elem, attr) {
+ var color;
+
+ do {
+ color = $.curCSS(elem, attr);
+
+ // Keep going until we find an element that has color, or we hit the body
+ if ( color != '' && color != 'transparent' || $.nodeName(elem, "body") )
+ break;
+
+ attr = "backgroundColor";
+ } while ( elem = elem.parentNode );
+
+ return getRGB(color);
+};
+
+// Some named colors to work with
+// From Interface by Stefan Petre
+// http://interface.eyecon.ro/
+
+var colors = {
+ aqua:[0,255,255],
+ azure:[240,255,255],
+ beige:[245,245,220],
+ black:[0,0,0],
+ blue:[0,0,255],
+ brown:[165,42,42],
+ cyan:[0,255,255],
+ darkblue:[0,0,139],
+ darkcyan:[0,139,139],
+ darkgrey:[169,169,169],
+ darkgreen:[0,100,0],
+ darkkhaki:[189,183,107],
+ darkmagenta:[139,0,139],
+ darkolivegreen:[85,107,47],
+ darkorange:[255,140,0],
+ darkorchid:[153,50,204],
+ darkred:[139,0,0],
+ darksalmon:[233,150,122],
+ darkviolet:[148,0,211],
+ fuchsia:[255,0,255],
+ gold:[255,215,0],
+ green:[0,128,0],
+ indigo:[75,0,130],
+ khaki:[240,230,140],
+ lightblue:[173,216,230],
+ lightcyan:[224,255,255],
+ lightgreen:[144,238,144],
+ lightgrey:[211,211,211],
+ lightpink:[255,182,193],
+ lightyellow:[255,255,224],
+ lime:[0,255,0],
+ magenta:[255,0,255],
+ maroon:[128,0,0],
+ navy:[0,0,128],
+ olive:[128,128,0],
+ orange:[255,165,0],
+ pink:[255,192,203],
+ purple:[128,0,128],
+ violet:[128,0,128],
+ red:[255,0,0],
+ silver:[192,192,192],
+ white:[255,255,255],
+ yellow:[255,255,0],
+ transparent: [255,255,255]
+};
+
+
+
+/******************************************************************************/
+/****************************** CLASS ANIMATIONS ******************************/
+/******************************************************************************/
+
+var classAnimationActions = ['add', 'remove', 'toggle'],
+ shorthandStyles = {
+ border: 1,
+ borderBottom: 1,
+ borderColor: 1,
+ borderLeft: 1,
+ borderRight: 1,
+ borderTop: 1,
+ borderWidth: 1,
+ margin: 1,
+ padding: 1
+ };
+
+function getElementStyles() {
+ var style = document.defaultView
+ ? document.defaultView.getComputedStyle(this, null)
+ : this.currentStyle,
+ newStyle = {},
+ key,
+ camelCase;
+
+ // webkit enumerates style porperties
+ if (style && style.length && style[0] && style[style[0]]) {
+ var len = style.length;
+ while (len--) {
+ key = style[len];
+ if (typeof style[key] == 'string') {
+ camelCase = key.replace(/\-(\w)/g, function(all, letter){
+ return letter.toUpperCase();
+ });
+ newStyle[camelCase] = style[key];
+ }
+ }
+ } else {
+ for (key in style) {
+ if (typeof style[key] === 'string') {
+ newStyle[key] = style[key];
+ }
+ }
+ }
+
+ return newStyle;
+}
+
+function filterStyles(styles) {
+ var name, value;
+ for (name in styles) {
+ value = styles[name];
+ if (
+ // ignore null and undefined values
+ value == null ||
+ // ignore functions (when does this occur?)
+ $.isFunction(value) ||
+ // shorthand styles that need to be expanded
+ name in shorthandStyles ||
+ // ignore scrollbars (break in IE)
+ (/scrollbar/).test(name) ||
+
+ // only colors or values that can be converted to numbers
+ (!(/color/i).test(name) && isNaN(parseFloat(value)))
+ ) {
+ delete styles[name];
+ }
+ }
+
+ return styles;
+}
+
+function styleDifference(oldStyle, newStyle) {
+ var diff = { _: 0 }, // http://dev.jquery.com/ticket/5459
+ name;
+
+ for (name in newStyle) {
+ if (oldStyle[name] != newStyle[name]) {
+ diff[name] = newStyle[name];
+ }
+ }
+
+ return diff;
+}
+
+$.effects.animateClass = function(value, duration, easing, callback) {
+ if ($.isFunction(easing)) {
+ callback = easing;
+ easing = null;
+ }
+
+ return this.queue(function() {
+ var that = $(this),
+ originalStyleAttr = that.attr('style') || ' ',
+ originalStyle = filterStyles(getElementStyles.call(this)),
+ newStyle,
+ className = that.attr('class');
+
+ $.each(classAnimationActions, function(i, action) {
+ if (value[action]) {
+ that[action + 'Class'](value[action]);
+ }
+ });
+ newStyle = filterStyles(getElementStyles.call(this));
+ that.attr('class', className);
+
+ that.animate(styleDifference(originalStyle, newStyle), {
+ queue: false,
+ duration: duration,
+ easing: easing,
+ complete: function() {
+ $.each(classAnimationActions, function(i, action) {
+ if (value[action]) { that[action + 'Class'](value[action]); }
+ });
+ // work around bug in IE by clearing the cssText before setting it
+ if (typeof that.attr('style') == 'object') {
+ that.attr('style').cssText = '';
+ that.attr('style').cssText = originalStyleAttr;
+ } else {
+ that.attr('style', originalStyleAttr);
+ }
+ if (callback) { callback.apply(this, arguments); }
+ $.dequeue( this );
+ }
+ });
+ });
+};
+
+$.fn.extend({
+ _addClass: $.fn.addClass,
+ addClass: function(classNames, speed, easing, callback) {
+ return speed ? $.effects.animateClass.apply(this, [{ add: classNames },speed,easing,callback]) : this._addClass(classNames);
+ },
+
+ _removeClass: $.fn.removeClass,
+ removeClass: function(classNames,speed,easing,callback) {
+ return speed ? $.effects.animateClass.apply(this, [{ remove: classNames },speed,easing,callback]) : this._removeClass(classNames);
+ },
+
+ _toggleClass: $.fn.toggleClass,
+ toggleClass: function(classNames, force, speed, easing, callback) {
+ if ( typeof force == "boolean" || force === undefined ) {
+ if ( !speed ) {
+ // without speed parameter;
+ return this._toggleClass(classNames, force);
+ } else {
+ return $.effects.animateClass.apply(this, [(force?{add:classNames}:{remove:classNames}),speed,easing,callback]);
+ }
+ } else {
+ // without switch parameter;
+ return $.effects.animateClass.apply(this, [{ toggle: classNames },force,speed,easing]);
+ }
+ },
+
+ switchClass: function(remove,add,speed,easing,callback) {
+ return $.effects.animateClass.apply(this, [{ add: add, remove: remove },speed,easing,callback]);
+ }
+});
+
+
+
+/******************************************************************************/
+/*********************************** EFFECTS **********************************/
+/******************************************************************************/
+
+$.extend($.effects, {
+ version: "1.8.18",
+
+ // Saves a set of properties in a data storage
+ save: function(element, set) {
+ for(var i=0; i < set.length; i++) {
+ if(set[i] !== null) element.data("ec.storage."+set[i], element[0].style[set[i]]);
+ }
+ },
+
+ // Restores a set of previously saved properties from a data storage
+ restore: function(element, set) {
+ for(var i=0; i < set.length; i++) {
+ if(set[i] !== null) element.css(set[i], element.data("ec.storage."+set[i]));
+ }
+ },
+
+ setMode: function(el, mode) {
+ if (mode == 'toggle') mode = el.is(':hidden') ? 'show' : 'hide'; // Set for toggle
+ return mode;
+ },
+
+ getBaseline: function(origin, original) { // Translates a [top,left] array into a baseline value
+ // this should be a little more flexible in the future to handle a string & hash
+ var y, x;
+ switch (origin[0]) {
+ case 'top': y = 0; break;
+ case 'middle': y = 0.5; break;
+ case 'bottom': y = 1; break;
+ default: y = origin[0] / original.height;
+ };
+ switch (origin[1]) {
+ case 'left': x = 0; break;
+ case 'center': x = 0.5; break;
+ case 'right': x = 1; break;
+ default: x = origin[1] / original.width;
+ };
+ return {x: x, y: y};
+ },
+
+ // Wraps the element around a wrapper that copies position properties
+ createWrapper: function(element) {
+
+ // if the element is already wrapped, return it
+ if (element.parent().is('.ui-effects-wrapper')) {
+ return element.parent();
+ }
+
+ // wrap the element
+ var props = {
+ width: element.outerWidth(true),
+ height: element.outerHeight(true),
+ 'float': element.css('float')
+ },
+ wrapper = $('<div></div>')
+ .addClass('ui-effects-wrapper')
+ .css({
+ fontSize: '100%',
+ background: 'transparent',
+ border: 'none',
+ margin: 0,
+ padding: 0
+ }),
+ active = document.activeElement;
+
+ element.wrap(wrapper);
+
+ // Fixes #7595 - Elements lose focus when wrapped.
+ if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
+ $( active ).focus();
+ }
+
+ wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually loose the reference to the wrapped element
+
+ // transfer positioning properties to the wrapper
+ if (element.css('position') == 'static') {
+ wrapper.css({ position: 'relative' });
+ element.css({ position: 'relative' });
+ } else {
+ $.extend(props, {
+ position: element.css('position'),
+ zIndex: element.css('z-index')
+ });
+ $.each(['top', 'left', 'bottom', 'right'], function(i, pos) {
+ props[pos] = element.css(pos);
+ if (isNaN(parseInt(props[pos], 10))) {
+ props[pos] = 'auto';
+ }
+ });
+ element.css({position: 'relative', top: 0, left: 0, right: 'auto', bottom: 'auto' });
+ }
+
+ return wrapper.css(props).show();
+ },
+
+ removeWrapper: function(element) {
+ var parent,
+ active = document.activeElement;
+
+ if (element.parent().is('.ui-effects-wrapper')) {
+ parent = element.parent().replaceWith(element);
+ // Fixes #7595 - Elements lose focus when wrapped.
+ if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
+ $( active ).focus();
+ }
+ return parent;
+ }
+
+ return element;
+ },
+
+ setTransition: function(element, list, factor, value) {
+ value = value || {};
+ $.each(list, function(i, x){
+ unit = element.cssUnit(x);
+ if (unit[0] > 0) value[x] = unit[0] * factor + unit[1];
+ });
+ return value;
+ }
+});
+
+
+function _normalizeArguments(effect, options, speed, callback) {
+ // shift params for method overloading
+ if (typeof effect == 'object') {
+ callback = options;
+ speed = null;
+ options = effect;
+ effect = options.effect;
+ }
+ if ($.isFunction(options)) {
+ callback = options;
+ speed = null;
+ options = {};
+ }
+ if (typeof options == 'number' || $.fx.speeds[options]) {
+ callback = speed;
+ speed = options;
+ options = {};
+ }
+ if ($.isFunction(speed)) {
+ callback = speed;
+ speed = null;
+ }
+
+ options = options || {};
+
+ speed = speed || options.duration;
+ speed = $.fx.off ? 0 : typeof speed == 'number'
+ ? speed : speed in $.fx.speeds ? $.fx.speeds[speed] : $.fx.speeds._default;
+
+ callback = callback || options.complete;
+
+ return [effect, options, speed, callback];
+}
+
+function standardSpeed( speed ) {
+ // valid standard speeds
+ if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) {
+ return true;
+ }
+
+ // invalid strings - treat as "normal" speed
+ if ( typeof speed === "string" && !$.effects[ speed ] ) {
+ return true;
+ }
+
+ return false;
+}
+
+$.fn.extend({
+ effect: function(effect, options, speed, callback) {
+ var args = _normalizeArguments.apply(this, arguments),
+ // TODO: make effects take actual parameters instead of a hash
+ args2 = {
+ options: args[1],
+ duration: args[2],
+ callback: args[3]
+ },
+ mode = args2.options.mode,
+ effectMethod = $.effects[effect];
+
+ if ( $.fx.off || !effectMethod ) {
+ // delegate to the original method (e.g., .show()) if possible
+ if ( mode ) {
+ return this[ mode ]( args2.duration, args2.callback );
+ } else {
+ return this.each(function() {
+ if ( args2.callback ) {
+ args2.callback.call( this );
+ }
+ });
+ }
+ }
+
+ return effectMethod.call(this, args2);
+ },
+
+ _show: $.fn.show,
+ show: function(speed) {
+ if ( standardSpeed( speed ) ) {
+ return this._show.apply(this, arguments);
+ } else {
+ var args = _normalizeArguments.apply(this, arguments);
+ args[1].mode = 'show';
+ return this.effect.apply(this, args);
+ }
+ },
+
+ _hide: $.fn.hide,
+ hide: function(speed) {
+ if ( standardSpeed( speed ) ) {
+ return this._hide.apply(this, arguments);
+ } else {
+ var args = _normalizeArguments.apply(this, arguments);
+ args[1].mode = 'hide';
+ return this.effect.apply(this, args);
+ }
+ },
+
+ // jQuery core overloads toggle and creates _toggle
+ __toggle: $.fn.toggle,
+ toggle: function(speed) {
+ if ( standardSpeed( speed ) || typeof speed === "boolean" || $.isFunction( speed ) ) {
+ return this.__toggle.apply(this, arguments);
+ } else {
+ var args = _normalizeArguments.apply(this, arguments);
+ args[1].mode = 'toggle';
+ return this.effect.apply(this, args);
+ }
+ },
+
+ // helper functions
+ cssUnit: function(key) {
+ var style = this.css(key), val = [];
+ $.each( ['em','px','%','pt'], function(i, unit){
+ if(style.indexOf(unit) > 0)
+ val = [parseFloat(style), unit];
+ });
+ return val;
+ }
+});
+
+
+
+/******************************************************************************/
+/*********************************** EASING ***********************************/
+/******************************************************************************/
+
+/*
+ * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
+ *
+ * Uses the built in easing capabilities added In jQuery 1.1
+ * to offer multiple easing options
+ *
+ * TERMS OF USE - jQuery Easing
+ *
+ * Open source under the BSD License.
+ *
+ * Copyright 2008 George McGinley Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the author nor the names of contributors may be used to endorse
+ * or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+*/
+
+// t: current time, b: begInnIng value, c: change In value, d: duration
+$.easing.jswing = $.easing.swing;
+
+$.extend($.easing,
+{
+ def: 'easeOutQuad',
+ swing: function (x, t, b, c, d) {
+ //alert($.easing.default);
+ return $.easing[$.easing.def](x, t, b, c, d);
+ },
+ easeInQuad: function (x, t, b, c, d) {
+ return c*(t/=d)*t + b;
+ },
+ easeOutQuad: function (x, t, b, c, d) {
+ return -c *(t/=d)*(t-2) + b;
+ },
+ easeInOutQuad: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t + b;
+ return -c/2 * ((--t)*(t-2) - 1) + b;
+ },
+ easeInCubic: function (x, t, b, c, d) {
+ return c*(t/=d)*t*t + b;
+ },
+ easeOutCubic: function (x, t, b, c, d) {
+ return c*((t=t/d-1)*t*t + 1) + b;
+ },
+ easeInOutCubic: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t*t + b;
+ return c/2*((t-=2)*t*t + 2) + b;
+ },
+ easeInQuart: function (x, t, b, c, d) {
+ return c*(t/=d)*t*t*t + b;
+ },
+ easeOutQuart: function (x, t, b, c, d) {
+ return -c * ((t=t/d-1)*t*t*t - 1) + b;
+ },
+ easeInOutQuart: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
+ return -c/2 * ((t-=2)*t*t*t - 2) + b;
+ },
+ easeInQuint: function (x, t, b, c, d) {
+ return c*(t/=d)*t*t*t*t + b;
+ },
+ easeOutQuint: function (x, t, b, c, d) {
+ return c*((t=t/d-1)*t*t*t*t + 1) + b;
+ },
+ easeInOutQuint: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
+ return c/2*((t-=2)*t*t*t*t + 2) + b;
+ },
+ easeInSine: function (x, t, b, c, d) {
+ return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
+ },
+ easeOutSine: function (x, t, b, c, d) {
+ return c * Math.sin(t/d * (Math.PI/2)) + b;
+ },
+ easeInOutSine: function (x, t, b, c, d) {
+ return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
+ },
+ easeInExpo: function (x, t, b, c, d) {
+ return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
+ },
+ easeOutExpo: function (x, t, b, c, d) {
+ return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
+ },
+ easeInOutExpo: function (x, t, b, c, d) {
+ if (t==0) return b;
+ if (t==d) return b+c;
+ if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
+ return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
+ },
+ easeInCirc: function (x, t, b, c, d) {
+ return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
+ },
+ easeOutCirc: function (x, t, b, c, d) {
+ return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
+ },
+ easeInOutCirc: function (x, t, b, c, d) {
+ if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
+ return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
+ },
+ easeInElastic: function (x, t, b, c, d) {
+ var s=1.70158;var p=0;var a=c;
+ if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ },
+ easeOutElastic: function (x, t, b, c, d) {
+ var s=1.70158;var p=0;var a=c;
+ if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
+ },
+ easeInOutElastic: function (x, t, b, c, d) {
+ var s=1.70158;var p=0;var a=c;
+ if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
+ if (a < Math.abs(c)) { a=c; var s=p/4; }
+ else var s = p/(2*Math.PI) * Math.asin (c/a);
+ if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
+ },
+ easeInBack: function (x, t, b, c, d, s) {
+ if (s == undefined) s = 1.70158;
+ return c*(t/=d)*t*((s+1)*t - s) + b;
+ },
+ easeOutBack: function (x, t, b, c, d, s) {
+ if (s == undefined) s = 1.70158;
+ return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+ },
+ easeInOutBack: function (x, t, b, c, d, s) {
+ if (s == undefined) s = 1.70158;
+ if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
+ return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+ },
+ easeInBounce: function (x, t, b, c, d) {
+ return c - $.easing.easeOutBounce (x, d-t, 0, c, d) + b;
+ },
+ easeOutBounce: function (x, t, b, c, d) {
+ if ((t/=d) < (1/2.75)) {
+ return c*(7.5625*t*t) + b;
+ } else if (t < (2/2.75)) {
+ return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
+ } else if (t < (2.5/2.75)) {
+ return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
+ } else {
+ return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
+ }
+ },
+ easeInOutBounce: function (x, t, b, c, d) {
+ if (t < d/2) return $.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
+ return $.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
+ }
+});
+
+/*
+ *
+ * TERMS OF USE - EASING EQUATIONS
+ *
+ * Open source under the BSD License.
+ *
+ * Copyright 2001 Robert Penner
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * Neither the name of the author nor the names of contributors may be used to endorse
+ * or promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+})(jQuery);
+/*
+ * jQuery UI Effects Blind 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Blind
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.blind = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
+ var direction = o.options.direction || 'vertical'; // Default direction
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
+ var ref = (direction == 'vertical') ? 'height' : 'width';
+ var distance = (direction == 'vertical') ? wrapper.height() : wrapper.width();
+ if(mode == 'show') wrapper.css(ref, 0); // Shift
+
+ // Animation
+ var animation = {};
+ animation[ref] = mode == 'show' ? distance : 0;
+
+ // Animate
+ wrapper.animate(animation, o.duration, o.options.easing, function() {
+ if(mode == 'hide') el.hide(); // Hide
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(el[0], arguments); // Callback
+ el.dequeue();
+ });
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Bounce 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Bounce
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.bounce = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
+ var direction = o.options.direction || 'up'; // Default direction
+ var distance = o.options.distance || 20; // Default distance
+ var times = o.options.times || 5; // Default # of times
+ var speed = o.duration || 250; // Default speed per bounce
+ if (/show|hide/.test(mode)) props.push('opacity'); // Avoid touching opacity to prevent clearType and PNG issues in IE
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ $.effects.createWrapper(el); // Create Wrapper
+ var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
+ var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
+ var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) / 3 : el.outerWidth({margin:true}) / 3);
+ if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift
+ if (mode == 'hide') distance = distance / (times * 2);
+ if (mode != 'hide') times--;
+
+ // Animate
+ if (mode == 'show') { // Show Bounce
+ var animation = {opacity: 1};
+ animation[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
+ el.animate(animation, speed / 2, o.options.easing);
+ distance = distance / 2;
+ times--;
+ };
+ for (var i = 0; i < times; i++) { // Bounces
+ var animation1 = {}, animation2 = {};
+ animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
+ animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
+ el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing);
+ distance = (mode == 'hide') ? distance * 2 : distance / 2;
+ };
+ if (mode == 'hide') { // Last Bounce
+ var animation = {opacity: 0};
+ animation[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
+ el.animate(animation, speed / 2, o.options.easing, function(){
+ el.hide(); // Hide
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(this, arguments); // Callback
+ });
+ } else {
+ var animation1 = {}, animation2 = {};
+ animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
+ animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
+ el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing, function(){
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(this, arguments); // Callback
+ });
+ };
+ el.queue('fx', function() { el.dequeue(); });
+ el.dequeue();
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Clip 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Clip
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.clip = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right','height','width'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
+ var direction = o.options.direction || 'vertical'; // Default direction
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
+ var animate = el[0].tagName == 'IMG' ? wrapper : el;
+ var ref = {
+ size: (direction == 'vertical') ? 'height' : 'width',
+ position: (direction == 'vertical') ? 'top' : 'left'
+ };
+ var distance = (direction == 'vertical') ? animate.height() : animate.width();
+ if(mode == 'show') { animate.css(ref.size, 0); animate.css(ref.position, distance / 2); } // Shift
+
+ // Animation
+ var animation = {};
+ animation[ref.size] = mode == 'show' ? distance : 0;
+ animation[ref.position] = mode == 'show' ? 0 : distance / 2;
+
+ // Animate
+ animate.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
+ if(mode == 'hide') el.hide(); // Hide
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(el[0], arguments); // Callback
+ el.dequeue();
+ }});
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Drop 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Drop
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.drop = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right','opacity'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
+ var direction = o.options.direction || 'left'; // Default Direction
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ $.effects.createWrapper(el); // Create Wrapper
+ var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
+ var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
+ var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) / 2 : el.outerWidth({margin:true}) / 2);
+ if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift
+
+ // Animation
+ var animation = {opacity: mode == 'show' ? 1 : 0};
+ animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance;
+
+ // Animate
+ el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
+ if(mode == 'hide') el.hide(); // Hide
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(this, arguments); // Callback
+ el.dequeue();
+ }});
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Explode 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Explode
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.explode = function(o) {
+
+ return this.queue(function() {
+
+ var rows = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3;
+ var cells = o.options.pieces ? Math.round(Math.sqrt(o.options.pieces)) : 3;
+
+ o.options.mode = o.options.mode == 'toggle' ? ($(this).is(':visible') ? 'hide' : 'show') : o.options.mode;
+ var el = $(this).show().css('visibility', 'hidden');
+ var offset = el.offset();
+
+ //Substract the margins - not fixing the problem yet.
+ offset.top -= parseInt(el.css("marginTop"),10) || 0;
+ offset.left -= parseInt(el.css("marginLeft"),10) || 0;
+
+ var width = el.outerWidth(true);
+ var height = el.outerHeight(true);
+
+ for(var i=0;i<rows;i++) { // =
+ for(var j=0;j<cells;j++) { // ||
+ el
+ .clone()
+ .appendTo('body')
+ .wrap('<div></div>')
+ .css({
+ position: 'absolute',
+ visibility: 'visible',
+ left: -j*(width/cells),
+ top: -i*(height/rows)
+ })
+ .parent()
+ .addClass('ui-effects-explode')
+ .css({
+ position: 'absolute',
+ overflow: 'hidden',
+ width: width/cells,
+ height: height/rows,
+ left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? (j-Math.floor(cells/2))*(width/cells) : 0),
+ top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? (i-Math.floor(rows/2))*(height/rows) : 0),
+ opacity: o.options.mode == 'show' ? 0 : 1
+ }).animate({
+ left: offset.left + j*(width/cells) + (o.options.mode == 'show' ? 0 : (j-Math.floor(cells/2))*(width/cells)),
+ top: offset.top + i*(height/rows) + (o.options.mode == 'show' ? 0 : (i-Math.floor(rows/2))*(height/rows)),
+ opacity: o.options.mode == 'show' ? 1 : 0
+ }, o.duration || 500);
+ }
+ }
+
+ // Set a timeout, to call the callback approx. when the other animations have finished
+ setTimeout(function() {
+
+ o.options.mode == 'show' ? el.css({ visibility: 'visible' }) : el.css({ visibility: 'visible' }).hide();
+ if(o.callback) o.callback.apply(el[0]); // Callback
+ el.dequeue();
+
+ $('div.ui-effects-explode').remove();
+
+ }, o.duration || 500);
+
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Fade 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fade
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.fade = function(o) {
+ return this.queue(function() {
+ var elem = $(this),
+ mode = $.effects.setMode(elem, o.options.mode || 'hide');
+
+ elem.animate({ opacity: mode }, {
+ queue: false,
+ duration: o.duration,
+ easing: o.options.easing,
+ complete: function() {
+ (o.callback && o.callback.apply(this, arguments));
+ elem.dequeue();
+ }
+ });
+ });
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Fold 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fold
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.fold = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'hide'); // Set Mode
+ var size = o.options.size || 15; // Default fold size
+ var horizFirst = !(!o.options.horizFirst); // Ensure a boolean value
+ var duration = o.duration ? o.duration / 2 : $.fx.speeds._default / 2;
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ var wrapper = $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
+ var widthFirst = ((mode == 'show') != horizFirst);
+ var ref = widthFirst ? ['width', 'height'] : ['height', 'width'];
+ var distance = widthFirst ? [wrapper.width(), wrapper.height()] : [wrapper.height(), wrapper.width()];
+ var percent = /([0-9]+)%/.exec(size);
+ if(percent) size = parseInt(percent[1],10) / 100 * distance[mode == 'hide' ? 0 : 1];
+ if(mode == 'show') wrapper.css(horizFirst ? {height: 0, width: size} : {height: size, width: 0}); // Shift
+
+ // Animation
+ var animation1 = {}, animation2 = {};
+ animation1[ref[0]] = mode == 'show' ? distance[0] : size;
+ animation2[ref[1]] = mode == 'show' ? distance[1] : 0;
+
+ // Animate
+ wrapper.animate(animation1, duration, o.options.easing)
+ .animate(animation2, duration, o.options.easing, function() {
+ if(mode == 'hide') el.hide(); // Hide
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(el[0], arguments); // Callback
+ el.dequeue();
+ });
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Highlight 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Highlight
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.highlight = function(o) {
+ return this.queue(function() {
+ var elem = $(this),
+ props = ['backgroundImage', 'backgroundColor', 'opacity'],
+ mode = $.effects.setMode(elem, o.options.mode || 'show'),
+ animation = {
+ backgroundColor: elem.css('backgroundColor')
+ };
+
+ if (mode == 'hide') {
+ animation.opacity = 0;
+ }
+
+ $.effects.save(elem, props);
+ elem
+ .show()
+ .css({
+ backgroundImage: 'none',
+ backgroundColor: o.options.color || '#ffff99'
+ })
+ .animate(animation, {
+ queue: false,
+ duration: o.duration,
+ easing: o.options.easing,
+ complete: function() {
+ (mode == 'hide' && elem.hide());
+ $.effects.restore(elem, props);
+ (mode == 'show' && !$.support.opacity && this.style.removeAttribute('filter'));
+ (o.callback && o.callback.apply(this, arguments));
+ elem.dequeue();
+ }
+ });
+ });
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Pulsate 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Pulsate
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.pulsate = function(o) {
+ return this.queue(function() {
+ var elem = $(this),
+ mode = $.effects.setMode(elem, o.options.mode || 'show');
+ times = ((o.options.times || 5) * 2) - 1;
+ duration = o.duration ? o.duration / 2 : $.fx.speeds._default / 2,
+ isVisible = elem.is(':visible'),
+ animateTo = 0;
+
+ if (!isVisible) {
+ elem.css('opacity', 0).show();
+ animateTo = 1;
+ }
+
+ if ((mode == 'hide' && isVisible) || (mode == 'show' && !isVisible)) {
+ times--;
+ }
+
+ for (var i = 0; i < times; i++) {
+ elem.animate({ opacity: animateTo }, duration, o.options.easing);
+ animateTo = (animateTo + 1) % 2;
+ }
+
+ elem.animate({ opacity: animateTo }, duration, o.options.easing, function() {
+ if (animateTo == 0) {
+ elem.hide();
+ }
+ (o.callback && o.callback.apply(this, arguments));
+ });
+
+ elem
+ .queue('fx', function() { elem.dequeue(); })
+ .dequeue();
+ });
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Scale 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Scale
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.puff = function(o) {
+ return this.queue(function() {
+ var elem = $(this),
+ mode = $.effects.setMode(elem, o.options.mode || 'hide'),
+ percent = parseInt(o.options.percent, 10) || 150,
+ factor = percent / 100,
+ original = { height: elem.height(), width: elem.width() };
+
+ $.extend(o.options, {
+ fade: true,
+ mode: mode,
+ percent: mode == 'hide' ? percent : 100,
+ from: mode == 'hide'
+ ? original
+ : {
+ height: original.height * factor,
+ width: original.width * factor
+ }
+ });
+
+ elem.effect('scale', o.options, o.duration, o.callback);
+ elem.dequeue();
+ });
+};
+
+$.effects.scale = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this);
+
+ // Set options
+ var options = $.extend(true, {}, o.options);
+ var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
+ var percent = parseInt(o.options.percent,10) || (parseInt(o.options.percent,10) == 0 ? 0 : (mode == 'hide' ? 0 : 100)); // Set default scaling percent
+ var direction = o.options.direction || 'both'; // Set default axis
+ var origin = o.options.origin; // The origin of the scaling
+ if (mode != 'effect') { // Set default origin and restore for show/hide
+ options.origin = origin || ['middle','center'];
+ options.restore = true;
+ }
+ var original = {height: el.height(), width: el.width()}; // Save original
+ el.from = o.options.from || (mode == 'show' ? {height: 0, width: 0} : original); // Default from state
+
+ // Adjust
+ var factor = { // Set scaling factor
+ y: direction != 'horizontal' ? (percent / 100) : 1,
+ x: direction != 'vertical' ? (percent / 100) : 1
+ };
+ el.to = {height: original.height * factor.y, width: original.width * factor.x}; // Set to state
+
+ if (o.options.fade) { // Fade option to support puff
+ if (mode == 'show') {el.from.opacity = 0; el.to.opacity = 1;};
+ if (mode == 'hide') {el.from.opacity = 1; el.to.opacity = 0;};
+ };
+
+ // Animation
+ options.from = el.from; options.to = el.to; options.mode = mode;
+
+ // Animate
+ el.effect('size', options, o.duration, o.callback);
+ el.dequeue();
+ });
+
+};
+
+$.effects.size = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right','width','height','overflow','opacity'];
+ var props1 = ['position','top','bottom','left','right','overflow','opacity']; // Always restore
+ var props2 = ['width','height','overflow']; // Copy for children
+ var cProps = ['fontSize'];
+ var vProps = ['borderTopWidth', 'borderBottomWidth', 'paddingTop', 'paddingBottom'];
+ var hProps = ['borderLeftWidth', 'borderRightWidth', 'paddingLeft', 'paddingRight'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
+ var restore = o.options.restore || false; // Default restore
+ var scale = o.options.scale || 'both'; // Default scale mode
+ var origin = o.options.origin; // The origin of the sizing
+ var original = {height: el.height(), width: el.width()}; // Save original
+ el.from = o.options.from || original; // Default from state
+ el.to = o.options.to || original; // Default to state
+ // Adjust
+ if (origin) { // Calculate baseline shifts
+ var baseline = $.effects.getBaseline(origin, original);
+ el.from.top = (original.height - el.from.height) * baseline.y;
+ el.from.left = (original.width - el.from.width) * baseline.x;
+ el.to.top = (original.height - el.to.height) * baseline.y;
+ el.to.left = (original.width - el.to.width) * baseline.x;
+ };
+ var factor = { // Set scaling factor
+ from: {y: el.from.height / original.height, x: el.from.width / original.width},
+ to: {y: el.to.height / original.height, x: el.to.width / original.width}
+ };
+ if (scale == 'box' || scale == 'both') { // Scale the css box
+ if (factor.from.y != factor.to.y) { // Vertical props scaling
+ props = props.concat(vProps);
+ el.from = $.effects.setTransition(el, vProps, factor.from.y, el.from);
+ el.to = $.effects.setTransition(el, vProps, factor.to.y, el.to);
+ };
+ if (factor.from.x != factor.to.x) { // Horizontal props scaling
+ props = props.concat(hProps);
+ el.from = $.effects.setTransition(el, hProps, factor.from.x, el.from);
+ el.to = $.effects.setTransition(el, hProps, factor.to.x, el.to);
+ };
+ };
+ if (scale == 'content' || scale == 'both') { // Scale the content
+ if (factor.from.y != factor.to.y) { // Vertical props scaling
+ props = props.concat(cProps);
+ el.from = $.effects.setTransition(el, cProps, factor.from.y, el.from);
+ el.to = $.effects.setTransition(el, cProps, factor.to.y, el.to);
+ };
+ };
+ $.effects.save(el, restore ? props : props1); el.show(); // Save & Show
+ $.effects.createWrapper(el); // Create Wrapper
+ el.css('overflow','hidden').css(el.from); // Shift
+
+ // Animate
+ if (scale == 'content' || scale == 'both') { // Scale the children
+ vProps = vProps.concat(['marginTop','marginBottom']).concat(cProps); // Add margins/font-size
+ hProps = hProps.concat(['marginLeft','marginRight']); // Add margins
+ props2 = props.concat(vProps).concat(hProps); // Concat
+ el.find("*[width]").each(function(){
+ child = $(this);
+ if (restore) $.effects.save(child, props2);
+ var c_original = {height: child.height(), width: child.width()}; // Save original
+ child.from = {height: c_original.height * factor.from.y, width: c_original.width * factor.from.x};
+ child.to = {height: c_original.height * factor.to.y, width: c_original.width * factor.to.x};
+ if (factor.from.y != factor.to.y) { // Vertical props scaling
+ child.from = $.effects.setTransition(child, vProps, factor.from.y, child.from);
+ child.to = $.effects.setTransition(child, vProps, factor.to.y, child.to);
+ };
+ if (factor.from.x != factor.to.x) { // Horizontal props scaling
+ child.from = $.effects.setTransition(child, hProps, factor.from.x, child.from);
+ child.to = $.effects.setTransition(child, hProps, factor.to.x, child.to);
+ };
+ child.css(child.from); // Shift children
+ child.animate(child.to, o.duration, o.options.easing, function(){
+ if (restore) $.effects.restore(child, props2); // Restore children
+ }); // Animate children
+ });
+ };
+
+ // Animate
+ el.animate(el.to, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
+ if (el.to.opacity === 0) {
+ el.css('opacity', el.from.opacity);
+ }
+ if(mode == 'hide') el.hide(); // Hide
+ $.effects.restore(el, restore ? props : props1); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(this, arguments); // Callback
+ el.dequeue();
+ }});
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Shake 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Shake
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.shake = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
+ var direction = o.options.direction || 'left'; // Default direction
+ var distance = o.options.distance || 20; // Default distance
+ var times = o.options.times || 3; // Default # of times
+ var speed = o.duration || o.options.duration || 140; // Default speed per shake
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ $.effects.createWrapper(el); // Create Wrapper
+ var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
+ var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
+
+ // Animation
+ var animation = {}, animation1 = {}, animation2 = {};
+ animation[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
+ animation1[ref] = (motion == 'pos' ? '+=' : '-=') + distance * 2;
+ animation2[ref] = (motion == 'pos' ? '-=' : '+=') + distance * 2;
+
+ // Animate
+ el.animate(animation, speed, o.options.easing);
+ for (var i = 1; i < times; i++) { // Shakes
+ el.animate(animation1, speed, o.options.easing).animate(animation2, speed, o.options.easing);
+ };
+ el.animate(animation1, speed, o.options.easing).
+ animate(animation, speed / 2, o.options.easing, function(){ // Last shake
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(this, arguments); // Callback
+ });
+ el.queue('fx', function() { el.dequeue(); });
+ el.dequeue();
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Slide 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Slide
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.slide = function(o) {
+
+ return this.queue(function() {
+
+ // Create element
+ var el = $(this), props = ['position','top','bottom','left','right'];
+
+ // Set options
+ var mode = $.effects.setMode(el, o.options.mode || 'show'); // Set Mode
+ var direction = o.options.direction || 'left'; // Default Direction
+
+ // Adjust
+ $.effects.save(el, props); el.show(); // Save & Show
+ $.effects.createWrapper(el).css({overflow:'hidden'}); // Create Wrapper
+ var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
+ var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
+ var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) : el.outerWidth({margin:true}));
+ if (mode == 'show') el.css(ref, motion == 'pos' ? (isNaN(distance) ? "-" + distance : -distance) : distance); // Shift
+
+ // Animation
+ var animation = {};
+ animation[ref] = (mode == 'show' ? (motion == 'pos' ? '+=' : '-=') : (motion == 'pos' ? '-=' : '+=')) + distance;
+
+ // Animate
+ el.animate(animation, { queue: false, duration: o.duration, easing: o.options.easing, complete: function() {
+ if(mode == 'hide') el.hide(); // Hide
+ $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
+ if(o.callback) o.callback.apply(this, arguments); // Callback
+ el.dequeue();
+ }});
+
+ });
+
+};
+
+})(jQuery);
+/*
+ * jQuery UI Effects Transfer 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Transfer
+ *
+ * Depends:
+ * jquery.effects.core.js
+ */
+(function( $, undefined ) {
+
+$.effects.transfer = function(o) {
+ return this.queue(function() {
+ var elem = $(this),
+ target = $(o.options.to),
+ endPosition = target.offset(),
+ animation = {
+ top: endPosition.top,
+ left: endPosition.left,
+ height: target.innerHeight(),
+ width: target.innerWidth()
+ },
+ startPosition = elem.offset(),
+ transfer = $('<div class="ui-effects-transfer"></div>')
+ .appendTo(document.body)
+ .addClass(o.options.className)
+ .css({
+ top: startPosition.top,
+ left: startPosition.left,
+ height: elem.innerHeight(),
+ width: elem.innerWidth(),
+ position: 'absolute'
+ })
+ .animate(animation, o.duration, o.options.easing, function() {
+ transfer.remove();
+ (o.callback && o.callback.apply(elem[0], arguments));
+ elem.dequeue();
+ });
+ });
+};
+
+})(jQuery);
+/*
+ * jQuery UI Accordion 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+$.widget( "ui.accordion", {
+ options: {
+ active: 0,
+ animated: "slide",
+ autoHeight: true,
+ clearStyle: false,
+ collapsible: false,
+ event: "click",
+ fillSpace: false,
+ header: "> li > :first-child,> :not(li):even",
+ icons: {
+ header: "ui-icon-triangle-1-e",
+ headerSelected: "ui-icon-triangle-1-s"
+ },
+ navigation: false,
+ navigationFilter: function() {
+ return this.href.toLowerCase() === location.href.toLowerCase();
+ }
+ },
+
+ _create: function() {
+ var self = this,
+ options = self.options;
+
+ self.running = 0;
+
+ self.element
+ .addClass( "ui-accordion ui-widget ui-helper-reset" )
+ // in lack of child-selectors in CSS
+ // we need to mark top-LIs in a UL-accordion for some IE-fix
+ .children( "li" )
+ .addClass( "ui-accordion-li-fix" );
+
+ self.headers = self.element.find( options.header )
+ .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" )
+ .bind( "mouseenter.accordion", function() {
+ if ( options.disabled ) {
+ return;
+ }
+ $( this ).addClass( "ui-state-hover" );
+ })
+ .bind( "mouseleave.accordion", function() {
+ if ( options.disabled ) {
+ return;
+ }
+ $( this ).removeClass( "ui-state-hover" );
+ })
+ .bind( "focus.accordion", function() {
+ if ( options.disabled ) {
+ return;
+ }
+ $( this ).addClass( "ui-state-focus" );
+ })
+ .bind( "blur.accordion", function() {
+ if ( options.disabled ) {
+ return;
+ }
+ $( this ).removeClass( "ui-state-focus" );
+ });
+
+ self.headers.next()
+ .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" );
+
+ if ( options.navigation ) {
+ var current = self.element.find( "a" ).filter( options.navigationFilter ).eq( 0 );
+ if ( current.length ) {
+ var header = current.closest( ".ui-accordion-header" );
+ if ( header.length ) {
+ // anchor within header
+ self.active = header;
+ } else {
+ // anchor within content
+ self.active = current.closest( ".ui-accordion-content" ).prev();
+ }
+ }
+ }
+
+ self.active = self._findActive( self.active || options.active )
+ .addClass( "ui-state-default ui-state-active" )
+ .toggleClass( "ui-corner-all" )
+ .toggleClass( "ui-corner-top" );
+ self.active.next().addClass( "ui-accordion-content-active" );
+
+ self._createIcons();
+ self.resize();
+
+ // ARIA
+ self.element.attr( "role", "tablist" );
+
+ self.headers
+ .attr( "role", "tab" )
+ .bind( "keydown.accordion", function( event ) {
+ return self._keydown( event );
+ })
+ .next()
+ .attr( "role", "tabpanel" );
+
+ self.headers
+ .not( self.active || "" )
+ .attr({
+ "aria-expanded": "false",
+ "aria-selected": "false",
+ tabIndex: -1
+ })
+ .next()
+ .hide();
+
+ // make sure at least one header is in the tab order
+ if ( !self.active.length ) {
+ self.headers.eq( 0 ).attr( "tabIndex", 0 );
+ } else {
+ self.active
+ .attr({
+ "aria-expanded": "true",
+ "aria-selected": "true",
+ tabIndex: 0
+ });
+ }
+
+ // only need links in tab order for Safari
+ if ( !$.browser.safari ) {
+ self.headers.find( "a" ).attr( "tabIndex", -1 );
+ }
+
+ if ( options.event ) {
+ self.headers.bind( options.event.split(" ").join(".accordion ") + ".accordion", function(event) {
+ self._clickHandler.call( self, event, this );
+ event.preventDefault();
+ });
+ }
+ },
+
+ _createIcons: function() {
+ var options = this.options;
+ if ( options.icons ) {
+ $( "<span></span>" )
+ .addClass( "ui-icon " + options.icons.header )
+ .prependTo( this.headers );
+ this.active.children( ".ui-icon" )
+ .toggleClass(options.icons.header)
+ .toggleClass(options.icons.headerSelected);
+ this.element.addClass( "ui-accordion-icons" );
+ }
+ },
+
+ _destroyIcons: function() {
+ this.headers.children( ".ui-icon" ).remove();
+ this.element.removeClass( "ui-accordion-icons" );
+ },
+
+ destroy: function() {
+ var options = this.options;
+
+ this.element
+ .removeClass( "ui-accordion ui-widget ui-helper-reset" )
+ .removeAttr( "role" );
+
+ this.headers
+ .unbind( ".accordion" )
+ .removeClass( "ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" )
+ .removeAttr( "role" )
+ .removeAttr( "aria-expanded" )
+ .removeAttr( "aria-selected" )
+ .removeAttr( "tabIndex" );
+
+ this.headers.find( "a" ).removeAttr( "tabIndex" );
+ this._destroyIcons();
+ var contents = this.headers.next()
+ .css( "display", "" )
+ .removeAttr( "role" )
+ .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled" );
+ if ( options.autoHeight || options.fillHeight ) {
+ contents.css( "height", "" );
+ }
+
+ return $.Widget.prototype.destroy.call( this );
+ },
+
+ _setOption: function( key, value ) {
+ $.Widget.prototype._setOption.apply( this, arguments );
+
+ if ( key == "active" ) {
+ this.activate( value );
+ }
+ if ( key == "icons" ) {
+ this._destroyIcons();
+ if ( value ) {
+ this._createIcons();
+ }
+ }
+ // #5332 - opacity doesn't cascade to positioned elements in IE
+ // so we need to add the disabled class to the headers and panels
+ if ( key == "disabled" ) {
+ this.headers.add(this.headers.next())
+ [ value ? "addClass" : "removeClass" ](
+ "ui-accordion-disabled ui-state-disabled" );
+ }
+ },
+
+ _keydown: function( event ) {
+ if ( this.options.disabled || event.altKey || event.ctrlKey ) {
+ return;
+ }
+
+ var keyCode = $.ui.keyCode,
+ length = this.headers.length,
+ currentIndex = this.headers.index( event.target ),
+ toFocus = false;
+
+ switch ( event.keyCode ) {
+ case keyCode.RIGHT:
+ case keyCode.DOWN:
+ toFocus = this.headers[ ( currentIndex + 1 ) % length ];
+ break;
+ case keyCode.LEFT:
+ case keyCode.UP:
+ toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
+ break;
+ case keyCode.SPACE:
+ case keyCode.ENTER:
+ this._clickHandler( { target: event.target }, event.target );
+ event.preventDefault();
+ }
+
+ if ( toFocus ) {
+ $( event.target ).attr( "tabIndex", -1 );
+ $( toFocus ).attr( "tabIndex", 0 );
+ toFocus.focus();
+ return false;
+ }
+
+ return true;
+ },
+
+ resize: function() {
+ var options = this.options,
+ maxHeight;
+
+ if ( options.fillSpace ) {
+ if ( $.browser.msie ) {
+ var defOverflow = this.element.parent().css( "overflow" );
+ this.element.parent().css( "overflow", "hidden");
+ }
+ maxHeight = this.element.parent().height();
+ if ($.browser.msie) {
+ this.element.parent().css( "overflow", defOverflow );
+ }
+
+ this.headers.each(function() {
+ maxHeight -= $( this ).outerHeight( true );
+ });
+
+ this.headers.next()
+ .each(function() {
+ $( this ).height( Math.max( 0, maxHeight -
+ $( this ).innerHeight() + $( this ).height() ) );
+ })
+ .css( "overflow", "auto" );
+ } else if ( options.autoHeight ) {
+ maxHeight = 0;
+ this.headers.next()
+ .each(function() {
+ maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
+ })
+ .height( maxHeight );
+ }
+
+ return this;
+ },
+
+ activate: function( index ) {
+ // TODO this gets called on init, changing the option without an explicit call for that
+ this.options.active = index;
+ // call clickHandler with custom event
+ var active = this._findActive( index )[ 0 ];
+ this._clickHandler( { target: active }, active );
+
+ return this;
+ },
+
+ _findActive: function( selector ) {
+ return selector
+ ? typeof selector === "number"
+ ? this.headers.filter( ":eq(" + selector + ")" )
+ : this.headers.not( this.headers.not( selector ) )
+ : selector === false
+ ? $( [] )
+ : this.headers.filter( ":eq(0)" );
+ },
+
+ // TODO isn't event.target enough? why the separate target argument?
+ _clickHandler: function( event, target ) {
+ var options = this.options;
+ if ( options.disabled ) {
+ return;
+ }
+
+ // called only when using activate(false) to close all parts programmatically
+ if ( !event.target ) {
+ if ( !options.collapsible ) {
+ return;
+ }
+ this.active
+ .removeClass( "ui-state-active ui-corner-top" )
+ .addClass( "ui-state-default ui-corner-all" )
+ .children( ".ui-icon" )
+ .removeClass( options.icons.headerSelected )
+ .addClass( options.icons.header );
+ this.active.next().addClass( "ui-accordion-content-active" );
+ var toHide = this.active.next(),
+ data = {
+ options: options,
+ newHeader: $( [] ),
+ oldHeader: options.active,
+ newContent: $( [] ),
+ oldContent: toHide
+ },
+ toShow = ( this.active = $( [] ) );
+ this._toggle( toShow, toHide, data );
+ return;
+ }
+
+ // get the click target
+ var clicked = $( event.currentTarget || target ),
+ clickedIsActive = clicked[0] === this.active[0];
+
+ // TODO the option is changed, is that correct?
+ // TODO if it is correct, shouldn't that happen after determining that the click is valid?
+ options.active = options.collapsible && clickedIsActive ?
+ false :
+ this.headers.index( clicked );
+
+ // if animations are still active, or the active header is the target, ignore click
+ if ( this.running || ( !options.collapsible && clickedIsActive ) ) {
+ return;
+ }
+
+ // find elements to show and hide
+ var active = this.active,
+ toShow = clicked.next(),
+ toHide = this.active.next(),
+ data = {
+ options: options,
+ newHeader: clickedIsActive && options.collapsible ? $([]) : clicked,
+ oldHeader: this.active,
+ newContent: clickedIsActive && options.collapsible ? $([]) : toShow,
+ oldContent: toHide
+ },
+ down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );
+
+ // when the call to ._toggle() comes after the class changes
+ // it causes a very odd bug in IE 8 (see #6720)
+ this.active = clickedIsActive ? $([]) : clicked;
+ this._toggle( toShow, toHide, data, clickedIsActive, down );
+
+ // switch classes
+ active
+ .removeClass( "ui-state-active ui-corner-top" )
+ .addClass( "ui-state-default ui-corner-all" )
+ .children( ".ui-icon" )
+ .removeClass( options.icons.headerSelected )
+ .addClass( options.icons.header );
+ if ( !clickedIsActive ) {
+ clicked
+ .removeClass( "ui-state-default ui-corner-all" )
+ .addClass( "ui-state-active ui-corner-top" )
+ .children( ".ui-icon" )
+ .removeClass( options.icons.header )
+ .addClass( options.icons.headerSelected );
+ clicked
+ .next()
+ .addClass( "ui-accordion-content-active" );
+ }
+
+ return;
+ },
+
+ _toggle: function( toShow, toHide, data, clickedIsActive, down ) {
+ var self = this,
+ options = self.options;
+
+ self.toShow = toShow;
+ self.toHide = toHide;
+ self.data = data;
+
+ var complete = function() {
+ if ( !self ) {
+ return;
+ }
+ return self._completed.apply( self, arguments );
+ };
+
+ // trigger changestart event
+ self._trigger( "changestart", null, self.data );
+
+ // count elements to animate
+ self.running = toHide.size() === 0 ? toShow.size() : toHide.size();
+
+ if ( options.animated ) {
+ var animOptions = {};
+
+ if ( options.collapsible && clickedIsActive ) {
+ animOptions = {
+ toShow: $( [] ),
+ toHide: toHide,
+ complete: complete,
+ down: down,
+ autoHeight: options.autoHeight || options.fillSpace
+ };
+ } else {
+ animOptions = {
+ toShow: toShow,
+ toHide: toHide,
+ complete: complete,
+ down: down,
+ autoHeight: options.autoHeight || options.fillSpace
+ };
+ }
+
+ if ( !options.proxied ) {
+ options.proxied = options.animated;
+ }
+
+ if ( !options.proxiedDuration ) {
+ options.proxiedDuration = options.duration;
+ }
+
+ options.animated = $.isFunction( options.proxied ) ?
+ options.proxied( animOptions ) :
+ options.proxied;
+
+ options.duration = $.isFunction( options.proxiedDuration ) ?
+ options.proxiedDuration( animOptions ) :
+ options.proxiedDuration;
+
+ var animations = $.ui.accordion.animations,
+ duration = options.duration,
+ easing = options.animated;
+
+ if ( easing && !animations[ easing ] && !$.easing[ easing ] ) {
+ easing = "slide";
+ }
+ if ( !animations[ easing ] ) {
+ animations[ easing ] = function( options ) {
+ this.slide( options, {
+ easing: easing,
+ duration: duration || 700
+ });
+ };
+ }
+
+ animations[ easing ]( animOptions );
+ } else {
+ if ( options.collapsible && clickedIsActive ) {
+ toShow.toggle();
+ } else {
+ toHide.hide();
+ toShow.show();
+ }
+
+ complete( true );
+ }
+
+ // TODO assert that the blur and focus triggers are really necessary, remove otherwise
+ toHide.prev()
+ .attr({
+ "aria-expanded": "false",
+ "aria-selected": "false",
+ tabIndex: -1
+ })
+ .blur();
+ toShow.prev()
+ .attr({
+ "aria-expanded": "true",
+ "aria-selected": "true",
+ tabIndex: 0
+ })
+ .focus();
+ },
+
+ _completed: function( cancel ) {
+ this.running = cancel ? 0 : --this.running;
+ if ( this.running ) {
+ return;
+ }
+
+ if ( this.options.clearStyle ) {
+ this.toShow.add( this.toHide ).css({
+ height: "",
+ overflow: ""
+ });
+ }
+
+ // other classes are removed before the animation; this one needs to stay until completed
+ this.toHide.removeClass( "ui-accordion-content-active" );
+ // Work around for rendering bug in IE (#5421)
+ if ( this.toHide.length ) {
+ this.toHide.parent()[0].className = this.toHide.parent()[0].className;
+ }
+
+ this._trigger( "change", null, this.data );
+ }
+});
+
+$.extend( $.ui.accordion, {
+ version: "1.8.18",
+ animations: {
+ slide: function( options, additions ) {
+ options = $.extend({
+ easing: "swing",
+ duration: 300
+ }, options, additions );
+ if ( !options.toHide.size() ) {
+ options.toShow.animate({
+ height: "show",
+ paddingTop: "show",
+ paddingBottom: "show"
+ }, options );
+ return;
+ }
+ if ( !options.toShow.size() ) {
+ options.toHide.animate({
+ height: "hide",
+ paddingTop: "hide",
+ paddingBottom: "hide"
+ }, options );
+ return;
+ }
+ var overflow = options.toShow.css( "overflow" ),
+ percentDone = 0,
+ showProps = {},
+ hideProps = {},
+ fxAttrs = [ "height", "paddingTop", "paddingBottom" ],
+ originalWidth;
+ // fix width before calculating height of hidden element
+ var s = options.toShow;
+ originalWidth = s[0].style.width;
+ s.width( s.parent().width()
+ - parseFloat( s.css( "paddingLeft" ) )
+ - parseFloat( s.css( "paddingRight" ) )
+ - ( parseFloat( s.css( "borderLeftWidth" ) ) || 0 )
+ - ( parseFloat( s.css( "borderRightWidth" ) ) || 0 ) );
+
+ $.each( fxAttrs, function( i, prop ) {
+ hideProps[ prop ] = "hide";
+
+ var parts = ( "" + $.css( options.toShow[0], prop ) ).match( /^([\d+-.]+)(.*)$/ );
+ showProps[ prop ] = {
+ value: parts[ 1 ],
+ unit: parts[ 2 ] || "px"
+ };
+ });
+ options.toShow.css({ height: 0, overflow: "hidden" }).show();
+ options.toHide
+ .filter( ":hidden" )
+ .each( options.complete )
+ .end()
+ .filter( ":visible" )
+ .animate( hideProps, {
+ step: function( now, settings ) {
+ // only calculate the percent when animating height
+ // IE gets very inconsistent results when animating elements
+ // with small values, which is common for padding
+ if ( settings.prop == "height" ) {
+ percentDone = ( settings.end - settings.start === 0 ) ? 0 :
+ ( settings.now - settings.start ) / ( settings.end - settings.start );
+ }
+
+ options.toShow[ 0 ].style[ settings.prop ] =
+ ( percentDone * showProps[ settings.prop ].value )
+ + showProps[ settings.prop ].unit;
+ },
+ duration: options.duration,
+ easing: options.easing,
+ complete: function() {
+ if ( !options.autoHeight ) {
+ options.toShow.css( "height", "" );
+ }
+ options.toShow.css({
+ width: originalWidth,
+ overflow: overflow
+ });
+ options.complete();
+ }
+ });
+ },
+ bounceslide: function( options ) {
+ this.slide( options, {
+ easing: options.down ? "easeOutBounce" : "swing",
+ duration: options.down ? 1000 : 200
+ });
+ }
+ }
+});
+
+})( jQuery );
+/*
+ * jQuery UI Autocomplete 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.position.js
+ */
+(function( $, undefined ) {
+
+// used to prevent race conditions with remote data sources
+var requestIndex = 0;
+
+$.widget( "ui.autocomplete", {
+ options: {
+ appendTo: "body",
+ autoFocus: false,
+ delay: 300,
+ minLength: 1,
+ position: {
+ my: "left top",
+ at: "left bottom",
+ collision: "none"
+ },
+ source: null
+ },
+
+ pending: 0,
+
+ _create: function() {
+ var self = this,
+ doc = this.element[ 0 ].ownerDocument,
+ suppressKeyPress;
+
+ this.element
+ .addClass( "ui-autocomplete-input" )
+ .attr( "autocomplete", "off" )
+ // TODO verify these actually work as intended
+ .attr({
+ role: "textbox",
+ "aria-autocomplete": "list",
+ "aria-haspopup": "true"
+ })
+ .bind( "keydown.autocomplete", function( event ) {
+ if ( self.options.disabled || self.element.propAttr( "readOnly" ) ) {
+ return;
+ }
+
+ suppressKeyPress = false;
+ var keyCode = $.ui.keyCode;
+ switch( event.keyCode ) {
+ case keyCode.PAGE_UP:
+ self._move( "previousPage", event );
+ break;
+ case keyCode.PAGE_DOWN:
+ self._move( "nextPage", event );
+ break;
+ case keyCode.UP:
+ self._move( "previous", event );
+ // prevent moving cursor to beginning of text field in some browsers
+ event.preventDefault();
+ break;
+ case keyCode.DOWN:
+ self._move( "next", event );
+ // prevent moving cursor to end of text field in some browsers
+ event.preventDefault();
+ break;
+ case keyCode.ENTER:
+ case keyCode.NUMPAD_ENTER:
+ // when menu is open and has focus
+ if ( self.menu.active ) {
+ // #6055 - Opera still allows the keypress to occur
+ // which causes forms to submit
+ suppressKeyPress = true;
+ event.preventDefault();
+ }
+ //passthrough - ENTER and TAB both select the current element
+ case keyCode.TAB:
+ if ( !self.menu.active ) {
+ return;
+ }
+ self.menu.select( event );
+ break;
+ case keyCode.ESCAPE:
+ self.element.val( self.term );
+ self.close( event );
+ break;
+ default:
+ // keypress is triggered before the input value is changed
+ clearTimeout( self.searching );
+ self.searching = setTimeout(function() {
+ // only search if the value has changed
+ if ( self.term != self.element.val() ) {
+ self.selectedItem = null;
+ self.search( null, event );
+ }
+ }, self.options.delay );
+ break;
+ }
+ })
+ .bind( "keypress.autocomplete", function( event ) {
+ if ( suppressKeyPress ) {
+ suppressKeyPress = false;
+ event.preventDefault();
+ }
+ })
+ .bind( "focus.autocomplete", function() {
+ if ( self.options.disabled ) {
+ return;
+ }
+
+ self.selectedItem = null;
+ self.previous = self.element.val();
+ })
+ .bind( "blur.autocomplete", function( event ) {
+ if ( self.options.disabled ) {
+ return;
+ }
+
+ clearTimeout( self.searching );
+ // clicks on the menu (or a button to trigger a search) will cause a blur event
+ self.closing = setTimeout(function() {
+ self.close( event );
+ self._change( event );
+ }, 150 );
+ });
+ this._initSource();
+ this.response = function() {
+ return self._response.apply( self, arguments );
+ };
+ this.menu = $( "<ul></ul>" )
+ .addClass( "ui-autocomplete" )
+ .appendTo( $( this.options.appendTo || "body", doc )[0] )
+ // prevent the close-on-blur in case of a "slow" click on the menu (long mousedown)
+ .mousedown(function( event ) {
+ // clicking on the scrollbar causes focus to shift to the body
+ // but we can't detect a mouseup or a click immediately afterward
+ // so we have to track the next mousedown and close the menu if
+ // the user clicks somewhere outside of the autocomplete
+ var menuElement = self.menu.element[ 0 ];
+ if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
+ setTimeout(function() {
+ $( document ).one( 'mousedown', function( event ) {
+ if ( event.target !== self.element[ 0 ] &&
+ event.target !== menuElement &&
+ !$.ui.contains( menuElement, event.target ) ) {
+ self.close();
+ }
+ });
+ }, 1 );
+ }
+
+ // use another timeout to make sure the blur-event-handler on the input was already triggered
+ setTimeout(function() {
+ clearTimeout( self.closing );
+ }, 13);
+ })
+ .menu({
+ focus: function( event, ui ) {
+ var item = ui.item.data( "item.autocomplete" );
+ if ( false !== self._trigger( "focus", event, { item: item } ) ) {
+ // use value to match what will end up in the input, if it was a key event
+ if ( /^key/.test(event.originalEvent.type) ) {
+ self.element.val( item.value );
+ }
+ }
+ },
+ selected: function( event, ui ) {
+ var item = ui.item.data( "item.autocomplete" ),
+ previous = self.previous;
+
+ // only trigger when focus was lost (click on menu)
+ if ( self.element[0] !== doc.activeElement ) {
+ self.element.focus();
+ self.previous = previous;
+ // #6109 - IE triggers two focus events and the second
+ // is asynchronous, so we need to reset the previous
+ // term synchronously and asynchronously :-(
+ setTimeout(function() {
+ self.previous = previous;
+ self.selectedItem = item;
+ }, 1);
+ }
+
+ if ( false !== self._trigger( "select", event, { item: item } ) ) {
+ self.element.val( item.value );
+ }
+ // reset the term after the select event
+ // this allows custom select handling to work properly
+ self.term = self.element.val();
+
+ self.close( event );
+ self.selectedItem = item;
+ },
+ blur: function( event, ui ) {
+ // don't set the value of the text field if it's already correct
+ // this prevents moving the cursor unnecessarily
+ if ( self.menu.element.is(":visible") &&
+ ( self.element.val() !== self.term ) ) {
+ self.element.val( self.term );
+ }
+ }
+ })
+ .zIndex( this.element.zIndex() + 1 )
+ // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
+ .css({ top: 0, left: 0 })
+ .hide()
+ .data( "menu" );
+ if ( $.fn.bgiframe ) {
+ this.menu.element.bgiframe();
+ }
+ // turning off autocomplete prevents the browser from remembering the
+ // value when navigating through history, so we re-enable autocomplete
+ // if the page is unloaded before the widget is destroyed. #7790
+ self.beforeunloadHandler = function() {
+ self.element.removeAttr( "autocomplete" );
+ };
+ $( window ).bind( "beforeunload", self.beforeunloadHandler );
+ },
+
+ destroy: function() {
+ this.element
+ .removeClass( "ui-autocomplete-input" )
+ .removeAttr( "autocomplete" )
+ .removeAttr( "role" )
+ .removeAttr( "aria-autocomplete" )
+ .removeAttr( "aria-haspopup" );
+ this.menu.element.remove();
+ $( window ).unbind( "beforeunload", this.beforeunloadHandler );
+ $.Widget.prototype.destroy.call( this );
+ },
+
+ _setOption: function( key, value ) {
+ $.Widget.prototype._setOption.apply( this, arguments );
+ if ( key === "source" ) {
+ this._initSource();
+ }
+ if ( key === "appendTo" ) {
+ this.menu.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] )
+ }
+ if ( key === "disabled" && value && this.xhr ) {
+ this.xhr.abort();
+ }
+ },
+
+ _initSource: function() {
+ var self = this,
+ array,
+ url;
+ if ( $.isArray(this.options.source) ) {
+ array = this.options.source;
+ this.source = function( request, response ) {
+ response( $.ui.autocomplete.filter(array, request.term) );
+ };
+ } else if ( typeof this.options.source === "string" ) {
+ url = this.options.source;
+ this.source = function( request, response ) {
+ if ( self.xhr ) {
+ self.xhr.abort();
+ }
+ self.xhr = $.ajax({
+ url: url,
+ data: request,
+ dataType: "json",
+ context: {
+ autocompleteRequest: ++requestIndex
+ },
+ success: function( data, status ) {
+ if ( this.autocompleteRequest === requestIndex ) {
+ response( data );
+ }
+ },
+ error: function() {
+ if ( this.autocompleteRequest === requestIndex ) {
+ response( [] );
+ }
+ }
+ });
+ };
+ } else {
+ this.source = this.options.source;
+ }
+ },
+
+ search: function( value, event ) {
+ value = value != null ? value : this.element.val();
+
+ // always save the actual value, not the one passed as an argument
+ this.term = this.element.val();
+
+ if ( value.length < this.options.minLength ) {
+ return this.close( event );
+ }
+
+ clearTimeout( this.closing );
+ if ( this._trigger( "search", event ) === false ) {
+ return;
+ }
+
+ return this._search( value );
+ },
+
+ _search: function( value ) {
+ this.pending++;
+ this.element.addClass( "ui-autocomplete-loading" );
+
+ this.source( { term: value }, this.response );
+ },
+
+ _response: function( content ) {
+ if ( !this.options.disabled && content && content.length ) {
+ content = this._normalize( content );
+ this._suggest( content );
+ this._trigger( "open" );
+ } else {
+ this.close();
+ }
+ this.pending--;
+ if ( !this.pending ) {
+ this.element.removeClass( "ui-autocomplete-loading" );
+ }
+ },
+
+ close: function( event ) {
+ clearTimeout( this.closing );
+ if ( this.menu.element.is(":visible") ) {
+ this.menu.element.hide();
+ this.menu.deactivate();
+ this._trigger( "close", event );
+ }
+ },
+
+ _change: function( event ) {
+ if ( this.previous !== this.element.val() ) {
+ this._trigger( "change", event, { item: this.selectedItem } );
+ }
+ },
+
+ _normalize: function( items ) {
+ // assume all items have the right format when the first item is complete
+ if ( items.length && items[0].label && items[0].value ) {
+ return items;
+ }
+ return $.map( items, function(item) {
+ if ( typeof item === "string" ) {
+ return {
+ label: item,
+ value: item
+ };
+ }
+ return $.extend({
+ label: item.label || item.value,
+ value: item.value || item.label
+ }, item );
+ });
+ },
+
+ _suggest: function( items ) {
+ var ul = this.menu.element
+ .empty()
+ .zIndex( this.element.zIndex() + 1 );
+ this._renderMenu( ul, items );
+ // TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate
+ this.menu.deactivate();
+ this.menu.refresh();
+
+ // size and position menu
+ ul.show();
+ this._resizeMenu();
+ ul.position( $.extend({
+ of: this.element
+ }, this.options.position ));
+
+ if ( this.options.autoFocus ) {
+ this.menu.next( new $.Event("mouseover") );
+ }
+ },
+
+ _resizeMenu: function() {
+ var ul = this.menu.element;
+ ul.outerWidth( Math.max(
+ // Firefox wraps long text (possibly a rounding bug)
+ // so we add 1px to avoid the wrapping (#7513)
+ ul.width( "" ).outerWidth() + 1,
+ this.element.outerWidth()
+ ) );
+ },
+
+ _renderMenu: function( ul, items ) {
+ var self = this;
+ $.each( items, function( index, item ) {
+ self._renderItem( ul, item );
+ });
+ },
+
+ _renderItem: function( ul, item) {
+ return $( "<li></li>" )
+ .data( "item.autocomplete", item )
+ .append( $( "<a></a>" ).text( item.label ) )
+ .appendTo( ul );
+ },
+
+ _move: function( direction, event ) {
+ if ( !this.menu.element.is(":visible") ) {
+ this.search( null, event );
+ return;
+ }
+ if ( this.menu.first() && /^previous/.test(direction) ||
+ this.menu.last() && /^next/.test(direction) ) {
+ this.element.val( this.term );
+ this.menu.deactivate();
+ return;
+ }
+ this.menu[ direction ]( event );
+ },
+
+ widget: function() {
+ return this.menu.element;
+ }
+});
+
+$.extend( $.ui.autocomplete, {
+ escapeRegex: function( value ) {
+ return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
+ },
+ filter: function(array, term) {
+ var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
+ return $.grep( array, function(value) {
+ return matcher.test( value.label || value.value || value );
+ });
+ }
+});
+
+}( jQuery ));
+
+/*
+ * jQuery UI Menu (not officially released)
+ *
+ * This widget isn't yet finished and the API is subject to change. We plan to finish
+ * it for the next release. You're welcome to give it a try anyway and give us feedback,
+ * as long as you're okay with migrating your code later on. We can help with that, too.
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Menu
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function($) {
+
+$.widget("ui.menu", {
+ _create: function() {
+ var self = this;
+ this.element
+ .addClass("ui-menu ui-widget ui-widget-content ui-corner-all")
+ .attr({
+ role: "listbox",
+ "aria-activedescendant": "ui-active-menuitem"
+ })
+ .click(function( event ) {
+ if ( !$( event.target ).closest( ".ui-menu-item a" ).length ) {
+ return;
+ }
+ // temporary
+ event.preventDefault();
+ self.select( event );
+ });
+ this.refresh();
+ },
+
+ refresh: function() {
+ var self = this;
+
+ // don't refresh list items that are already adapted
+ var items = this.element.children("li:not(.ui-menu-item):has(a)")
+ .addClass("ui-menu-item")
+ .attr("role", "menuitem");
+
+ items.children("a")
+ .addClass("ui-corner-all")
+ .attr("tabindex", -1)
+ // mouseenter doesn't work with event delegation
+ .mouseenter(function( event ) {
+ self.activate( event, $(this).parent() );
+ })
+ .mouseleave(function() {
+ self.deactivate();
+ });
+ },
+
+ activate: function( event, item ) {
+ this.deactivate();
+ if (this.hasScroll()) {
+ var offset = item.offset().top - this.element.offset().top,
+ scroll = this.element.scrollTop(),
+ elementHeight = this.element.height();
+ if (offset < 0) {
+ this.element.scrollTop( scroll + offset);
+ } else if (offset >= elementHeight) {
+ this.element.scrollTop( scroll + offset - elementHeight + item.height());
+ }
+ }
+ this.active = item.eq(0)
+ .children("a")
+ .addClass("ui-state-hover")
+ .attr("id", "ui-active-menuitem")
+ .end();
+ this._trigger("focus", event, { item: item });
+ },
+
+ deactivate: function() {
+ if (!this.active) { return; }
+
+ this.active.children("a")
+ .removeClass("ui-state-hover")
+ .removeAttr("id");
+ this._trigger("blur");
+ this.active = null;
+ },
+
+ next: function(event) {
+ this.move("next", ".ui-menu-item:first", event);
+ },
+
+ previous: function(event) {
+ this.move("prev", ".ui-menu-item:last", event);
+ },
+
+ first: function() {
+ return this.active && !this.active.prevAll(".ui-menu-item").length;
+ },
+
+ last: function() {
+ return this.active && !this.active.nextAll(".ui-menu-item").length;
+ },
+
+ move: function(direction, edge, event) {
+ if (!this.active) {
+ this.activate(event, this.element.children(edge));
+ return;
+ }
+ var next = this.active[direction + "All"](".ui-menu-item").eq(0);
+ if (next.length) {
+ this.activate(event, next);
+ } else {
+ this.activate(event, this.element.children(edge));
+ }
+ },
+
+ // TODO merge with previousPage
+ nextPage: function(event) {
+ if (this.hasScroll()) {
+ // TODO merge with no-scroll-else
+ if (!this.active || this.last()) {
+ this.activate(event, this.element.children(".ui-menu-item:first"));
+ return;
+ }
+ var base = this.active.offset().top,
+ height = this.element.height(),
+ result = this.element.children(".ui-menu-item").filter(function() {
+ var close = $(this).offset().top - base - height + $(this).height();
+ // TODO improve approximation
+ return close < 10 && close > -10;
+ });
+
+ // TODO try to catch this earlier when scrollTop indicates the last page anyway
+ if (!result.length) {
+ result = this.element.children(".ui-menu-item:last");
+ }
+ this.activate(event, result);
+ } else {
+ this.activate(event, this.element.children(".ui-menu-item")
+ .filter(!this.active || this.last() ? ":first" : ":last"));
+ }
+ },
+
+ // TODO merge with nextPage
+ previousPage: function(event) {
+ if (this.hasScroll()) {
+ // TODO merge with no-scroll-else
+ if (!this.active || this.first()) {
+ this.activate(event, this.element.children(".ui-menu-item:last"));
+ return;
+ }
+
+ var base = this.active.offset().top,
+ height = this.element.height();
+ result = this.element.children(".ui-menu-item").filter(function() {
+ var close = $(this).offset().top - base + height - $(this).height();
+ // TODO improve approximation
+ return close < 10 && close > -10;
+ });
+
+ // TODO try to catch this earlier when scrollTop indicates the last page anyway
+ if (!result.length) {
+ result = this.element.children(".ui-menu-item:first");
+ }
+ this.activate(event, result);
+ } else {
+ this.activate(event, this.element.children(".ui-menu-item")
+ .filter(!this.active || this.first() ? ":last" : ":first"));
+ }
+ },
+
+ hasScroll: function() {
+ return this.element.height() < this.element[ $.fn.prop ? "prop" : "attr" ]("scrollHeight");
+ },
+
+ select: function( event ) {
+ this._trigger("selected", event, { item: this.active });
+ }
+});
+
+}(jQuery));
+/*
+ * jQuery UI Button 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+var lastActive, startXPos, startYPos, clickDragged,
+ baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
+ stateClasses = "ui-state-hover ui-state-active ",
+ typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",
+ formResetHandler = function() {
+ var buttons = $( this ).find( ":ui-button" );
+ setTimeout(function() {
+ buttons.button( "refresh" );
+ }, 1 );
+ },
+ radioGroup = function( radio ) {
+ var name = radio.name,
+ form = radio.form,
+ radios = $( [] );
+ if ( name ) {
+ if ( form ) {
+ radios = $( form ).find( "[name='" + name + "']" );
+ } else {
+ radios = $( "[name='" + name + "']", radio.ownerDocument )
+ .filter(function() {
+ return !this.form;
+ });
+ }
+ }
+ return radios;
+ };
+
+$.widget( "ui.button", {
+ options: {
+ disabled: null,
+ text: true,
+ label: null,
+ icons: {
+ primary: null,
+ secondary: null
+ }
+ },
+ _create: function() {
+ this.element.closest( "form" )
+ .unbind( "reset.button" )
+ .bind( "reset.button", formResetHandler );
+
+ if ( typeof this.options.disabled !== "boolean" ) {
+ this.options.disabled = !!this.element.propAttr( "disabled" );
+ } else {
+ this.element.propAttr( "disabled", this.options.disabled );
+ }
+
+ this._determineButtonType();
+ this.hasTitle = !!this.buttonElement.attr( "title" );
+
+ var self = this,
+ options = this.options,
+ toggleButton = this.type === "checkbox" || this.type === "radio",
+ hoverClass = "ui-state-hover" + ( !toggleButton ? " ui-state-active" : "" ),
+ focusClass = "ui-state-focus";
+
+ if ( options.label === null ) {
+ options.label = this.buttonElement.html();
+ }
+
+ this.buttonElement
+ .addClass( baseClasses )
+ .attr( "role", "button" )
+ .bind( "mouseenter.button", function() {
+ if ( options.disabled ) {
+ return;
+ }
+ $( this ).addClass( "ui-state-hover" );
+ if ( this === lastActive ) {
+ $( this ).addClass( "ui-state-active" );
+ }
+ })
+ .bind( "mouseleave.button", function() {
+ if ( options.disabled ) {
+ return;
+ }
+ $( this ).removeClass( hoverClass );
+ })
+ .bind( "click.button", function( event ) {
+ if ( options.disabled ) {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ }
+ });
+
+ this.element
+ .bind( "focus.button", function() {
+ // no need to check disabled, focus won't be triggered anyway
+ self.buttonElement.addClass( focusClass );
+ })
+ .bind( "blur.button", function() {
+ self.buttonElement.removeClass( focusClass );
+ });
+
+ if ( toggleButton ) {
+ this.element.bind( "change.button", function() {
+ if ( clickDragged ) {
+ return;
+ }
+ self.refresh();
+ });
+ // if mouse moves between mousedown and mouseup (drag) set clickDragged flag
+ // prevents issue where button state changes but checkbox/radio checked state
+ // does not in Firefox (see ticket #6970)
+ this.buttonElement
+ .bind( "mousedown.button", function( event ) {
+ if ( options.disabled ) {
+ return;
+ }
+ clickDragged = false;
+ startXPos = event.pageX;
+ startYPos = event.pageY;
+ })
+ .bind( "mouseup.button", function( event ) {
+ if ( options.disabled ) {
+ return;
+ }
+ if ( startXPos !== event.pageX || startYPos !== event.pageY ) {
+ clickDragged = true;
+ }
+ });
+ }
+
+ if ( this.type === "checkbox" ) {
+ this.buttonElement.bind( "click.button", function() {
+ if ( options.disabled || clickDragged ) {
+ return false;
+ }
+ $( this ).toggleClass( "ui-state-active" );
+ self.buttonElement.attr( "aria-pressed", self.element[0].checked );
+ });
+ } else if ( this.type === "radio" ) {
+ this.buttonElement.bind( "click.button", function() {
+ if ( options.disabled || clickDragged ) {
+ return false;
+ }
+ $( this ).addClass( "ui-state-active" );
+ self.buttonElement.attr( "aria-pressed", "true" );
+
+ var radio = self.element[ 0 ];
+ radioGroup( radio )
+ .not( radio )
+ .map(function() {
+ return $( this ).button( "widget" )[ 0 ];
+ })
+ .removeClass( "ui-state-active" )
+ .attr( "aria-pressed", "false" );
+ });
+ } else {
+ this.buttonElement
+ .bind( "mousedown.button", function() {
+ if ( options.disabled ) {
+ return false;
+ }
+ $( this ).addClass( "ui-state-active" );
+ lastActive = this;
+ $( document ).one( "mouseup", function() {
+ lastActive = null;
+ });
+ })
+ .bind( "mouseup.button", function() {
+ if ( options.disabled ) {
+ return false;
+ }
+ $( this ).removeClass( "ui-state-active" );
+ })
+ .bind( "keydown.button", function(event) {
+ if ( options.disabled ) {
+ return false;
+ }
+ if ( event.keyCode == $.ui.keyCode.SPACE || event.keyCode == $.ui.keyCode.ENTER ) {
+ $( this ).addClass( "ui-state-active" );
+ }
+ })
+ .bind( "keyup.button", function() {
+ $( this ).removeClass( "ui-state-active" );
+ });
+
+ if ( this.buttonElement.is("a") ) {
+ this.buttonElement.keyup(function(event) {
+ if ( event.keyCode === $.ui.keyCode.SPACE ) {
+ // TODO pass through original event correctly (just as 2nd argument doesn't work)
+ $( this ).click();
+ }
+ });
+ }
+ }
+
+ // TODO: pull out $.Widget's handling for the disabled option into
+ // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can
+ // be overridden by individual plugins
+ this._setOption( "disabled", options.disabled );
+ this._resetButton();
+ },
+
+ _determineButtonType: function() {
+
+ if ( this.element.is(":checkbox") ) {
+ this.type = "checkbox";
+ } else if ( this.element.is(":radio") ) {
+ this.type = "radio";
+ } else if ( this.element.is("input") ) {
+ this.type = "input";
+ } else {
+ this.type = "button";
+ }
+
+ if ( this.type === "checkbox" || this.type === "radio" ) {
+ // we don't search against the document in case the element
+ // is disconnected from the DOM
+ var ancestor = this.element.parents().filter(":last"),
+ labelSelector = "label[for='" + this.element.attr("id") + "']";
+ this.buttonElement = ancestor.find( labelSelector );
+ if ( !this.buttonElement.length ) {
+ ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
+ this.buttonElement = ancestor.filter( labelSelector );
+ if ( !this.buttonElement.length ) {
+ this.buttonElement = ancestor.find( labelSelector );
+ }
+ }
+ this.element.addClass( "ui-helper-hidden-accessible" );
+
+ var checked = this.element.is( ":checked" );
+ if ( checked ) {
+ this.buttonElement.addClass( "ui-state-active" );
+ }
+ this.buttonElement.attr( "aria-pressed", checked );
+ } else {
+ this.buttonElement = this.element;
+ }
+ },
+
+ widget: function() {
+ return this.buttonElement;
+ },
+
+ destroy: function() {
+ this.element
+ .removeClass( "ui-helper-hidden-accessible" );
+ this.buttonElement
+ .removeClass( baseClasses + " " + stateClasses + " " + typeClasses )
+ .removeAttr( "role" )
+ .removeAttr( "aria-pressed" )
+ .html( this.buttonElement.find(".ui-button-text").html() );
+
+ if ( !this.hasTitle ) {
+ this.buttonElement.removeAttr( "title" );
+ }
+
+ $.Widget.prototype.destroy.call( this );
+ },
+
+ _setOption: function( key, value ) {
+ $.Widget.prototype._setOption.apply( this, arguments );
+ if ( key === "disabled" ) {
+ if ( value ) {
+ this.element.propAttr( "disabled", true );
+ } else {
+ this.element.propAttr( "disabled", false );
+ }
+ return;
+ }
+ this._resetButton();
+ },
+
+ refresh: function() {
+ var isDisabled = this.element.is( ":disabled" );
+ if ( isDisabled !== this.options.disabled ) {
+ this._setOption( "disabled", isDisabled );
+ }
+ if ( this.type === "radio" ) {
+ radioGroup( this.element[0] ).each(function() {
+ if ( $( this ).is( ":checked" ) ) {
+ $( this ).button( "widget" )
+ .addClass( "ui-state-active" )
+ .attr( "aria-pressed", "true" );
+ } else {
+ $( this ).button( "widget" )
+ .removeClass( "ui-state-active" )
+ .attr( "aria-pressed", "false" );
+ }
+ });
+ } else if ( this.type === "checkbox" ) {
+ if ( this.element.is( ":checked" ) ) {
+ this.buttonElement
+ .addClass( "ui-state-active" )
+ .attr( "aria-pressed", "true" );
+ } else {
+ this.buttonElement
+ .removeClass( "ui-state-active" )
+ .attr( "aria-pressed", "false" );
+ }
+ }
+ },
+
+ _resetButton: function() {
+ if ( this.type === "input" ) {
+ if ( this.options.label ) {
+ this.element.val( this.options.label );
+ }
+ return;
+ }
+ var buttonElement = this.buttonElement.removeClass( typeClasses ),
+ buttonText = $( "<span></span>", this.element[0].ownerDocument )
+ .addClass( "ui-button-text" )
+ .html( this.options.label )
+ .appendTo( buttonElement.empty() )
+ .text(),
+ icons = this.options.icons,
+ multipleIcons = icons.primary && icons.secondary,
+ buttonClasses = [];
+
+ if ( icons.primary || icons.secondary ) {
+ if ( this.options.text ) {
+ buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
+ }
+
+ if ( icons.primary ) {
+ buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
+ }
+
+ if ( icons.secondary ) {
+ buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
+ }
+
+ if ( !this.options.text ) {
+ buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
+
+ if ( !this.hasTitle ) {
+ buttonElement.attr( "title", buttonText );
+ }
+ }
+ } else {
+ buttonClasses.push( "ui-button-text-only" );
+ }
+ buttonElement.addClass( buttonClasses.join( " " ) );
+ }
+});
+
+$.widget( "ui.buttonset", {
+ options: {
+ items: ":button, :submit, :reset, :checkbox, :radio, a, :data(button)"
+ },
+
+ _create: function() {
+ this.element.addClass( "ui-buttonset" );
+ },
+
+ _init: function() {
+ this.refresh();
+ },
+
+ _setOption: function( key, value ) {
+ if ( key === "disabled" ) {
+ this.buttons.button( "option", key, value );
+ }
+
+ $.Widget.prototype._setOption.apply( this, arguments );
+ },
+
+ refresh: function() {
+ var rtl = this.element.css( "direction" ) === "rtl";
+
+ this.buttons = this.element.find( this.options.items )
+ .filter( ":ui-button" )
+ .button( "refresh" )
+ .end()
+ .not( ":ui-button" )
+ .button()
+ .end()
+ .map(function() {
+ return $( this ).button( "widget" )[ 0 ];
+ })
+ .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
+ .filter( ":first" )
+ .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
+ .end()
+ .filter( ":last" )
+ .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
+ .end()
+ .end();
+ },
+
+ destroy: function() {
+ this.element.removeClass( "ui-buttonset" );
+ this.buttons
+ .map(function() {
+ return $( this ).button( "widget" )[ 0 ];
+ })
+ .removeClass( "ui-corner-left ui-corner-right" )
+ .end()
+ .button( "destroy" );
+
+ $.Widget.prototype.destroy.call( this );
+ }
+});
+
+}( jQuery ) );
+/*
+ * jQuery UI Datepicker 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker
+ *
+ * Depends:
+ * jquery.ui.core.js
+ */
+(function( $, undefined ) {
+
+$.extend($.ui, { datepicker: { version: "1.8.18" } });
+
+var PROP_NAME = 'datepicker';
+var dpuuid = new Date().getTime();
+var instActive;
+
+/* Date picker manager.
+ Use the singleton instance of this class, $.datepicker, to interact with the date picker.
+ Settings for (groups of) date pickers are maintained in an instance object,
+ allowing multiple different settings on the same page. */
+
+function Datepicker() {
+ this.debug = false; // Change this to true to start debugging
+ this._curInst = null; // The current instance in use
+ this._keyEvent = false; // If the last event was a key event
+ this._disabledInputs = []; // List of date picker inputs that have been disabled
+ this._datepickerShowing = false; // True if the popup picker is showing , false if not
+ this._inDialog = false; // True if showing within a "dialog", false if not
+ this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division
+ this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class
+ this._appendClass = 'ui-datepicker-append'; // The name of the append marker class
+ this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class
+ this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class
+ this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class
+ this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class
+ this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class
+ this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class
+ this.regional = []; // Available regional settings, indexed by language code
+ this.regional[''] = { // Default regional settings
+ closeText: 'Done', // Display text for close link
+ prevText: 'Prev', // Display text for previous month link
+ nextText: 'Next', // Display text for next month link
+ currentText: 'Today', // Display text for current month link
+ monthNames: ['January','February','March','April','May','June',
+ 'July','August','September','October','November','December'], // Names of months for drop-down and formatting
+ monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
+ dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
+ dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
+ dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
+ weekHeader: 'Wk', // Column header for week of the year
+ dateFormat: 'mm/dd/yy', // See format options on parseDate
+ firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
+ isRTL: false, // True if right-to-left language, false if left-to-right
+ showMonthAfterYear: false, // True if the year select precedes month, false for month then year
+ yearSuffix: '' // Additional text to append to the year in the month headers
+ };
+ this._defaults = { // Global defaults for all the date picker instances
+ showOn: 'focus', // 'focus' for popup on focus,
+ // 'button' for trigger button, or 'both' for either
+ showAnim: 'fadeIn', // Name of jQuery animation for popup
+ showOptions: {}, // Options for enhanced animations
+ defaultDate: null, // Used when field is blank: actual date,
+ // +/-number for offset from today, null for today
+ appendText: '', // Display text following the input box, e.g. showing the format
+ buttonText: '...', // Text for trigger button
+ buttonImage: '', // URL for trigger button image
+ buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
+ hideIfNoPrevNext: false, // True to hide next/previous month links
+ // if not applicable, false to just disable them
+ navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
+ gotoCurrent: false, // True if today link goes back to current selection instead
+ changeMonth: false, // True if month can be selected directly, false if only prev/next
+ changeYear: false, // True if year can be selected directly, false if only prev/next
+ yearRange: 'c-10:c+10', // Range of years to display in drop-down,
+ // either relative to today's year (-nn:+nn), relative to currently displayed year
+ // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
+ showOtherMonths: false, // True to show dates in other months, false to leave blank
+ selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
+ showWeek: false, // True to show week of the year, false to not show it
+ calculateWeek: this.iso8601Week, // How to calculate the week of the year,
+ // takes a Date and returns the number of the week for it
+ shortYearCutoff: '+10', // Short year values < this are in the current century,
+ // > this are in the previous century,
+ // string value starting with '+' for current year + value
+ minDate: null, // The earliest selectable date, or null for no limit
+ maxDate: null, // The latest selectable date, or null for no limit
+ duration: 'fast', // Duration of display/closure
+ beforeShowDay: null, // Function that takes a date and returns an array with
+ // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '',
+ // [2] = cell title (optional), e.g. $.datepicker.noWeekends
+ beforeShow: null, // Function that takes an input field and
+ // returns a set of custom settings for the date picker
+ onSelect: null, // Define a callback function when a date is selected
+ onChangeMonthYear: null, // Define a callback function when the month or year is changed
+ onClose: null, // Define a callback function when the datepicker is closed
+ numberOfMonths: 1, // Number of months to show at a time
+ showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
+ stepMonths: 1, // Number of months to step back/forward
+ stepBigMonths: 12, // Number of months to step back/forward for the big links
+ altField: '', // Selector for an alternate field to store selected dates into
+ altFormat: '', // The date format to use for the alternate field
+ constrainInput: true, // The input is constrained by the current date format
+ showButtonPanel: false, // True to show button panel, false to not show it
+ autoSize: false, // True to size the input for the date format, false to leave as is
+ disabled: false // The initial disabled state
+ };
+ $.extend(this._defaults, this.regional['']);
+ this.dpDiv = bindHover($('<div id="' + this._mainDivId + '" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'));
+}
+
+$.extend(Datepicker.prototype, {
+ /* Class name added to elements to indicate already configured with a date picker. */
+ markerClassName: 'hasDatepicker',
+
+ //Keep track of the maximum number of rows displayed (see #7043)
+ maxRows: 4,
+
+ /* Debug logging (if enabled). */
+ log: function () {
+ if (this.debug)
+ console.log.apply('', arguments);
+ },
+
+ // TODO rename to "widget" when switching to widget factory
+ _widgetDatepicker: function() {
+ return this.dpDiv;
+ },
+
+ /* Override the default settings for all instances of the date picker.
+ @param settings object - the new settings to use as defaults (anonymous object)
+ @return the manager object */
+ setDefaults: function(settings) {
+ extendRemove(this._defaults, settings || {});
+ return this;
+ },
+
+ /* Attach the date picker to a jQuery selection.
+ @param target element - the target input field or division or span
+ @param settings object - the new settings to use for this date picker instance (anonymous) */
+ _attachDatepicker: function(target, settings) {
+ // check for settings on the control itself - in namespace 'date:'
+ var inlineSettings = null;
+ for (var attrName in this._defaults) {
+ var attrValue = target.getAttribute('date:' + attrName);
+ if (attrValue) {
+ inlineSettings = inlineSettings || {};
+ try {
+ inlineSettings[attrName] = eval(attrValue);
+ } catch (err) {
+ inlineSettings[attrName] = attrValue;
+ }
+ }
+ }
+ var nodeName = target.nodeName.toLowerCase();
+ var inline = (nodeName == 'div' || nodeName == 'span');
+ if (!target.id) {
+ this.uuid += 1;
+ target.id = 'dp' + this.uuid;
+ }
+ var inst = this._newInst($(target), inline);
+ inst.settings = $.extend({}, settings || {}, inlineSettings || {});
+ if (nodeName == 'input') {
+ this._connectDatepicker(target, inst);
+ } else if (inline) {
+ this._inlineDatepicker(target, inst);
+ }
+ },
+
+ /* Create a new instance object. */
+ _newInst: function(target, inline) {
+ var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
+ return {id: id, input: target, // associated target
+ selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
+ drawMonth: 0, drawYear: 0, // month being drawn
+ inline: inline, // is datepicker inline or not
+ dpDiv: (!inline ? this.dpDiv : // presentation div
+ bindHover($('<div class="' + this._inlineClass + ' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>')))};
+ },
+
+ /* Attach the date picker to an input field. */
+ _connectDatepicker: function(target, inst) {
+ var input = $(target);
+ inst.append = $([]);
+ inst.trigger = $([]);
+ if (input.hasClass(this.markerClassName))
+ return;
+ this._attachments(input, inst);
+ input.addClass(this.markerClassName).keydown(this._doKeyDown).
+ keypress(this._doKeyPress).keyup(this._doKeyUp).
+ bind("setData.datepicker", function(event, key, value) {
+ inst.settings[key] = value;
+ }).bind("getData.datepicker", function(event, key) {
+ return this._get(inst, key);
+ });
+ this._autoSize(inst);
+ $.data(target, PROP_NAME, inst);
+ //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
+ if( inst.settings.disabled ) {
+ this._disableDatepicker( target );
+ }
+ },
+
+ /* Make attachments based on settings. */
+ _attachments: function(input, inst) {
+ var appendText = this._get(inst, 'appendText');
+ var isRTL = this._get(inst, 'isRTL');
+ if (inst.append)
+ inst.append.remove();
+ if (appendText) {
+ inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
+ input[isRTL ? 'before' : 'after'](inst.append);
+ }
+ input.unbind('focus', this._showDatepicker);
+ if (inst.trigger)
+ inst.trigger.remove();
+ var showOn = this._get(inst, 'showOn');
+ if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
+ input.focus(this._showDatepicker);
+ if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
+ var buttonText = this._get(inst, 'buttonText');
+ var buttonImage = this._get(inst, 'buttonImage');
+ inst.trigger = $(this._get(inst, 'buttonImageOnly') ?
+ $('<img/>').addClass(this._triggerClass).
+ attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
+ $('<button type="button"></button>').addClass(this._triggerClass).
+ html(buttonImage == '' ? buttonText : $('<img/>').attr(
+ { src:buttonImage, alt:buttonText, title:buttonText })));
+ input[isRTL ? 'before' : 'after'](inst.trigger);
+ inst.trigger.click(function() {
+ if ($.datepicker._datepickerShowing && $.datepicker._lastInput == input[0])
+ $.datepicker._hideDatepicker();
+ else if ($.datepicker._datepickerShowing && $.datepicker._lastInput != input[0]) {
+ $.datepicker._hideDatepicker();
+ $.datepicker._showDatepicker(input[0]);
+ } else
+ $.datepicker._showDatepicker(input[0]);
+ return false;
+ });
+ }
+ },
+
+ /* Apply the maximum length for the date format. */
+ _autoSize: function(inst) {
+ if (this._get(inst, 'autoSize') && !inst.inline) {
+ var date = new Date(2009, 12 - 1, 20); // Ensure double digits
+ var dateFormat = this._get(inst, 'dateFormat');
+ if (dateFormat.match(/[DM]/)) {
+ var findMax = function(names) {
+ var max = 0;
+ var maxI = 0;
+ for (var i = 0; i < names.length; i++) {
+ if (names[i].length > max) {
+ max = names[i].length;
+ maxI = i;
+ }
+ }
+ return maxI;
+ };
+ date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
+ 'monthNames' : 'monthNamesShort'))));
+ date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
+ 'dayNames' : 'dayNamesShort'))) + 20 - date.getDay());
+ }
+ inst.input.attr('size', this._formatDate(inst, date).length);
+ }
+ },
+
+ /* Attach an inline date picker to a div. */
+ _inlineDatepicker: function(target, inst) {
+ var divSpan = $(target);
+ if (divSpan.hasClass(this.markerClassName))
+ return;
+ divSpan.addClass(this.markerClassName).append(inst.dpDiv).
+ bind("setData.datepicker", function(event, key, value){
+ inst.settings[key] = value;
+ }).bind("getData.datepicker", function(event, key){
+ return this._get(inst, key);
+ });
+ $.data(target, PROP_NAME, inst);
+ this._setDate(inst, this._getDefaultDate(inst), true);
+ this._updateDatepicker(inst);
+ this._updateAlternate(inst);
+ //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
+ if( inst.settings.disabled ) {
+ this._disableDatepicker( target );
+ }
+ // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
+ // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
+ inst.dpDiv.css( "display", "block" );
+ },
+
+ /* Pop-up the date picker in a "dialog" box.
+ @param input element - ignored
+ @param date string or Date - the initial date to display
+ @param onSelect function - the function to call when a date is selected
+ @param settings object - update the dialog date picker instance's settings (anonymous object)
+ @param pos int[2] - coordinates for the dialog's position within the screen or
+ event - with x/y coordinates or
+ leave empty for default (screen centre)
+ @return the manager object */
+ _dialogDatepicker: function(input, date, onSelect, settings, pos) {
+ var inst = this._dialogInst; // internal instance
+ if (!inst) {
+ this.uuid += 1;
+ var id = 'dp' + this.uuid;
+ this._dialogInput = $('<input type="text" id="' + id +
+ '" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');
+ this._dialogInput.keydown(this._doKeyDown);
+ $('body').append(this._dialogInput);
+ inst = this._dialogInst = this._newInst(this._dialogInput, false);
+ inst.settings = {};
+ $.data(this._dialogInput[0], PROP_NAME, inst);
+ }
+ extendRemove(inst.settings, settings || {});
+ date = (date && date.constructor == Date ? this._formatDate(inst, date) : date);
+ this._dialogInput.val(date);
+
+ this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
+ if (!this._pos) {
+ var browserWidth = document.documentElement.clientWidth;
+ var browserHeight = document.documentElement.clientHeight;
+ var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
+ var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
+ this._pos = // should use actual width/height below
+ [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
+ }
+
+ // move input on screen for focus, but hidden behind dialog
+ this._dialogInput.css('left', (this._pos[0] + 20) + 'px').css('top', this._pos[1] + 'px');
+ inst.settings.onSelect = onSelect;
+ this._inDialog = true;
+ this.dpDiv.addClass(this._dialogClass);
+ this._showDatepicker(this._dialogInput[0]);
+ if ($.blockUI)
+ $.blockUI(this.dpDiv);
+ $.data(this._dialogInput[0], PROP_NAME, inst);
+ return this;
+ },
+
+ /* Detach a datepicker from its control.
+ @param target element - the target input field or division or span */
+ _destroyDatepicker: function(target) {
+ var $target = $(target);
+ var inst = $.data(target, PROP_NAME);
+ if (!$target.hasClass(this.markerClassName)) {
+ return;
+ }
+ var nodeName = target.nodeName.toLowerCase();
+ $.removeData(target, PROP_NAME);
+ if (nodeName == 'input') {
+ inst.append.remove();
+ inst.trigger.remove();
+ $target.removeClass(this.markerClassName).
+ unbind('focus', this._showDatepicker).
+ unbind('keydown', this._doKeyDown).
+ unbind('keypress', this._doKeyPress).
+ unbind('keyup', this._doKeyUp);
+ } else if (nodeName == 'div' || nodeName == 'span')
+ $target.removeClass(this.markerClassName).empty();
+ },
+
+ /* Enable the date picker to a jQuery selection.
+ @param target element - the target input field or division or span */
+ _enableDatepicker: function(target) {
+ var $target = $(target);
+ var inst = $.data(target, PROP_NAME);
+ if (!$target.hasClass(this.markerClassName)) {
+ return;
+ }
+ var nodeName = target.nodeName.toLowerCase();
+ if (nodeName == 'input') {
+ target.disabled = false;
+ inst.trigger.filter('button').
+ each(function() { this.disabled = false; }).end().
+ filter('img').css({opacity: '1.0', cursor: ''});
+ }
+ else if (nodeName == 'div' || nodeName == 'span') {
+ var inline = $target.children('.' + this._inlineClass);
+ inline.children().removeClass('ui-state-disabled');
+ inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
+ removeAttr("disabled");
+ }
+ this._disabledInputs = $.map(this._disabledInputs,
+ function(value) { return (value == target ? null : value); }); // delete entry
+ },
+
+ /* Disable the date picker to a jQuery selection.
+ @param target element - the target input field or division or span */
+ _disableDatepicker: function(target) {
+ var $target = $(target);
+ var inst = $.data(target, PROP_NAME);
+ if (!$target.hasClass(this.markerClassName)) {
+ return;
+ }
+ var nodeName = target.nodeName.toLowerCase();
+ if (nodeName == 'input') {
+ target.disabled = true;
+ inst.trigger.filter('button').
+ each(function() { this.disabled = true; }).end().
+ filter('img').css({opacity: '0.5', cursor: 'default'});
+ }
+ else if (nodeName == 'div' || nodeName == 'span') {
+ var inline = $target.children('.' + this._inlineClass);
+ inline.children().addClass('ui-state-disabled');
+ inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
+ attr("disabled", "disabled");
+ }
+ this._disabledInputs = $.map(this._disabledInputs,
+ function(value) { return (value == target ? null : value); }); // delete entry
+ this._disabledInputs[this._disabledInputs.length] = target;
+ },
+
+ /* Is the first field in a jQuery collection disabled as a datepicker?
+ @param target element - the target input field or division or span
+ @return boolean - true if disabled, false if enabled */
+ _isDisabledDatepicker: function(target) {
+ if (!target) {
+ return false;
+ }
+ for (var i = 0; i < this._disabledInputs.length; i++) {
+ if (this._disabledInputs[i] == target)
+ return true;
+ }
+ return false;
+ },
+
+ /* Retrieve the instance data for the target control.
+ @param target element - the target input field or division or span
+ @return object - the associated instance data
+ @throws error if a jQuery problem getting data */
+ _getInst: function(target) {
+ try {
+ return $.data(target, PROP_NAME);
+ }
+ catch (err) {
+ throw 'Missing instance data for this datepicker';
+ }
+ },
+
+ /* Update or retrieve the settings for a date picker attached to an input field or division.
+ @param target element - the target input field or division or span
+ @param name object - the new settings to update or
+ string - the name of the setting to change or retrieve,
+ when retrieving also 'all' for all instance settings or
+ 'defaults' for all global defaults
+ @param value any - the new value for the setting
+ (omit if above is an object or to retrieve a value) */
+ _optionDatepicker: function(target, name, value) {
+ var inst = this._getInst(target);
+ if (arguments.length == 2 && typeof name == 'string') {
+ return (name == 'defaults' ? $.extend({}, $.datepicker._defaults) :
+ (inst ? (name == 'all' ? $.extend({}, inst.settings) :
+ this._get(inst, name)) : null));
+ }
+ var settings = name || {};
+ if (typeof name == 'string') {
+ settings = {};
+ settings[name] = value;
+ }
+ if (inst) {
+ if (this._curInst == inst) {
+ this._hideDatepicker();
+ }
+ var date = this._getDateDatepicker(target, true);
+ var minDate = this._getMinMaxDate(inst, 'min');
+ var maxDate = this._getMinMaxDate(inst, 'max');
+ extendRemove(inst.settings, settings);
+ // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
+ if (minDate !== null && settings['dateFormat'] !== undefined && settings['minDate'] === undefined)
+ inst.settings.minDate = this._formatDate(inst, minDate);
+ if (maxDate !== null && settings['dateFormat'] !== undefined && settings['maxDate'] === undefined)
+ inst.settings.maxDate = this._formatDate(inst, maxDate);
+ this._attachments($(target), inst);
+ this._autoSize(inst);
+ this._setDate(inst, date);
+ this._updateAlternate(inst);
+ this._updateDatepicker(inst);
+ }
+ },
+
+ // change method deprecated
+ _changeDatepicker: function(target, name, value) {
+ this._optionDatepicker(target, name, value);
+ },
+
+ /* Redraw the date picker attached to an input field or division.
+ @param target element - the target input field or division or span */
+ _refreshDatepicker: function(target) {
+ var inst = this._getInst(target);
+ if (inst) {
+ this._updateDatepicker(inst);
+ }
+ },
+
+ /* Set the dates for a jQuery selection.
+ @param target element - the target input field or division or span
+ @param date Date - the new date */
+ _setDateDatepicker: function(target, date) {
+ var inst = this._getInst(target);
+ if (inst) {
+ this._setDate(inst, date);
+ this._updateDatepicker(inst);
+ this._updateAlternate(inst);
+ }
+ },
+
+ /* Get the date(s) for the first entry in a jQuery selection.
+ @param target element - the target input field or division or span
+ @param noDefault boolean - true if no default date is to be used
+ @return Date - the current date */
+ _getDateDatepicker: function(target, noDefault) {
+ var inst = this._getInst(target);
+ if (inst && !inst.inline)
+ this._setDateFromField(inst, noDefault);
+ return (inst ? this._getDate(inst) : null);
+ },
+
+ /* Handle keystrokes. */
+ _doKeyDown: function(event) {
+ var inst = $.datepicker._getInst(event.target);
+ var handled = true;
+ var isRTL = inst.dpDiv.is('.ui-datepicker-rtl');
+ inst._keyEvent = true;
+ if ($.datepicker._datepickerShowing)
+ switch (event.keyCode) {
+ case 9: $.datepicker._hideDatepicker();
+ handled = false;
+ break; // hide on tab out
+ case 13: var sel = $('td.' + $.datepicker._dayOverClass + ':not(.' +
+ $.datepicker._currentClass + ')', inst.dpDiv);
+ if (sel[0])
+ $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
+ var onSelect = $.datepicker._get(inst, 'onSelect');
+ if (onSelect) {
+ var dateStr = $.datepicker._formatDate(inst);
+
+ // trigger custom callback
+ onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
+ }
+ else
+ $.datepicker._hideDatepicker();
+ return false; // don't submit the form
+ break; // select the value on enter
+ case 27: $.datepicker._hideDatepicker();
+ break; // hide on escape
+ case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
+ -$.datepicker._get(inst, 'stepBigMonths') :
+ -$.datepicker._get(inst, 'stepMonths')), 'M');
+ break; // previous month/year on page up/+ ctrl
+ case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
+ +$.datepicker._get(inst, 'stepBigMonths') :
+ +$.datepicker._get(inst, 'stepMonths')), 'M');
+ break; // next month/year on page down/+ ctrl
+ case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target);
+ handled = event.ctrlKey || event.metaKey;
+ break; // clear on ctrl or command +end
+ case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target);
+ handled = event.ctrlKey || event.metaKey;
+ break; // current on ctrl or command +home
+ case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), 'D');
+ handled = event.ctrlKey || event.metaKey;
+ // -1 day on ctrl or command +left
+ if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
+ -$.datepicker._get(inst, 'stepBigMonths') :
+ -$.datepicker._get(inst, 'stepMonths')), 'M');
+ // next month/year on alt +left on Mac
+ break;
+ case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D');
+ handled = event.ctrlKey || event.metaKey;
+ break; // -1 week on ctrl or command +up
+ case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), 'D');
+ handled = event.ctrlKey || event.metaKey;
+ // +1 day on ctrl or command +right
+ if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
+ +$.datepicker._get(inst, 'stepBigMonths') :
+ +$.datepicker._get(inst, 'stepMonths')), 'M');
+ // next month/year on alt +right
+ break;
+ case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D');
+ handled = event.ctrlKey || event.metaKey;
+ break; // +1 week on ctrl or command +down
+ default: handled = false;
+ }
+ else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home
+ $.datepicker._showDatepicker(this);
+ else {
+ handled = false;
+ }
+ if (handled) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ },
+
+ /* Filter entered characters - based on date format. */
+ _doKeyPress: function(event) {
+ var inst = $.datepicker._getInst(event.target);
+ if ($.datepicker._get(inst, 'constrainInput')) {
+ var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat'));
+ var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
+ return event.ctrlKey || event.metaKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
+ }
+ },
+
+ /* Synchronise manual entry and field/alternate field. */
+ _doKeyUp: function(event) {
+ var inst = $.datepicker._getInst(event.target);
+ if (inst.input.val() != inst.lastVal) {
+ try {
+ var date = $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
+ (inst.input ? inst.input.val() : null),
+ $.datepicker._getFormatConfig(inst));
+ if (date) { // only if valid
+ $.datepicker._setDateFromField(inst);
+ $.datepicker._updateAlternate(inst);
+ $.datepicker._updateDatepicker(inst);
+ }
+ }
+ catch (event) {
+ $.datepicker.log(event);
+ }
+ }
+ return true;
+ },
+
+ /* Pop-up the date picker for a given input field.
+ If false returned from beforeShow event handler do not show.
+ @param input element - the input field attached to the date picker or
+ event - if triggered by focus */
+ _showDatepicker: function(input) {
+ input = input.target || input;
+ if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
+ input = $('input', input.parentNode)[0];
+ if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here
+ return;
+ var inst = $.datepicker._getInst(input);
+ if ($.datepicker._curInst && $.datepicker._curInst != inst) {
+ $.datepicker._curInst.dpDiv.stop(true, true);
+ if ( inst && $.datepicker._datepickerShowing ) {
+ $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
+ }
+ }
+ var beforeShow = $.datepicker._get(inst, 'beforeShow');
+ var beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
+ if(beforeShowSettings === false){
+ //false
+ return;
+ }
+ extendRemove(inst.settings, beforeShowSettings);
+ inst.lastVal = null;
+ $.datepicker._lastInput = input;
+ $.datepicker._setDateFromField(inst);
+ if ($.datepicker._inDialog) // hide cursor
+ input.value = '';
+ if (!$.datepicker._pos) { // position below input
+ $.datepicker._pos = $.datepicker._findPos(input);
+ $.datepicker._pos[1] += input.offsetHeight; // add the height
+ }
+ var isFixed = false;
+ $(input).parents().each(function() {
+ isFixed |= $(this).css('position') == 'fixed';
+ return !isFixed;
+ });
+ if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
+ $.datepicker._pos[0] -= document.documentElement.scrollLeft;
+ $.datepicker._pos[1] -= document.documentElement.scrollTop;
+ }
+ var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
+ $.datepicker._pos = null;
+ //to avoid flashes on Firefox
+ inst.dpDiv.empty();
+ // determine sizing offscreen
+ inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
+ $.datepicker._updateDatepicker(inst);
+ // fix width for dynamic number of date pickers
+ // and adjust position before showing
+ offset = $.datepicker._checkOffset(inst, offset, isFixed);
+ inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
+ 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
+ left: offset.left + 'px', top: offset.top + 'px'});
+ if (!inst.inline) {
+ var showAnim = $.datepicker._get(inst, 'showAnim');
+ var duration = $.datepicker._get(inst, 'duration');
+ var postProcess = function() {
+ var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
+ if( !! cover.length ){
+ var borders = $.datepicker._getBorders(inst.dpDiv);
+ cover.css({left: -borders[0], top: -borders[1],
+ width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()});
+ }
+ };
+ inst.dpDiv.zIndex($(input).zIndex()+1);
+ $.datepicker._datepickerShowing = true;
+ if ($.effects && $.effects[showAnim])
+ inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
+ else
+ inst.dpDiv[showAnim || 'show']((showAnim ? duration : null), postProcess);
+ if (!showAnim || !duration)
+ postProcess();
+ if (inst.input.is(':visible') && !inst.input.is(':disabled'))
+ inst.input.focus();
+ $.datepicker._curInst = inst;
+ }
+ },
+
+ /* Generate the date picker content. */
+ _updateDatepicker: function(inst) {
+ var self = this;
+ self.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
+ var borders = $.datepicker._getBorders(inst.dpDiv);
+ instActive = inst; // for delegate hover events
+ inst.dpDiv.empty().append(this._generateHTML(inst));
+ var cover = inst.dpDiv.find('iframe.ui-datepicker-cover'); // IE6- only
+ if( !!cover.length ){ //avoid call to outerXXXX() when not in IE6
+ cover.css({left: -borders[0], top: -borders[1], width: inst.dpDiv.outerWidth(), height: inst.dpDiv.outerHeight()})
+ }
+ inst.dpDiv.find('.' + this._dayOverClass + ' a').mouseover();
+ var numMonths = this._getNumberOfMonths(inst);
+ var cols = numMonths[1];
+ var width = 17;
+ inst.dpDiv.removeClass('ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4').width('');
+ if (cols > 1)
+ inst.dpDiv.addClass('ui-datepicker-multi-' + cols).css('width', (width * cols) + 'em');
+ inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
+ 'Class']('ui-datepicker-multi');
+ inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
+ 'Class']('ui-datepicker-rtl');
+ if (inst == $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input &&
+ // #6694 - don't focus the input if it's already focused
+ // this breaks the change event in IE
+ inst.input.is(':visible') && !inst.input.is(':disabled') && inst.input[0] != document.activeElement)
+ inst.input.focus();
+ // deffered render of the years select (to avoid flashes on Firefox)
+ if( inst.yearshtml ){
+ var origyearshtml = inst.yearshtml;
+ setTimeout(function(){
+ //assure that inst.yearshtml didn't change.
+ if( origyearshtml === inst.yearshtml && inst.yearshtml ){
+ inst.dpDiv.find('select.ui-datepicker-year:first').replaceWith(inst.yearshtml);
+ }
+ origyearshtml = inst.yearshtml = null;
+ }, 0);
+ }
+ },
+
+ /* Retrieve the size of left and top borders for an element.
+ @param elem (jQuery object) the element of interest
+ @return (number[2]) the left and top borders */
+ _getBorders: function(elem) {
+ var convert = function(value) {
+ return {thin: 1, medium: 2, thick: 3}[value] || value;
+ };
+ return [parseFloat(convert(elem.css('border-left-width'))),
+ parseFloat(convert(elem.css('border-top-width')))];
+ },
+
+ /* Check positioning to remain on screen. */
+ _checkOffset: function(inst, offset, isFixed) {
+ var dpWidth = inst.dpDiv.outerWidth();
+ var dpHeight = inst.dpDiv.outerHeight();
+ var inputWidth = inst.input ? inst.input.outerWidth() : 0;
+ var inputHeight = inst.input ? inst.input.outerHeight() : 0;
+ var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
+ var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
+
+ offset.left -= (this._get(inst, 'isRTL') ? (dpWidth - inputWidth) : 0);
+ offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
+ offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
+
+ // now check if datepicker is showing outside window viewport - move to a better place if so.
+ offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
+ Math.abs(offset.left + dpWidth - viewWidth) : 0);
+ offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
+ Math.abs(dpHeight + inputHeight) : 0);
+
+ return offset;
+ },
+
+ /* Find an object's position on the screen. */
+ _findPos: function(obj) {
+ var inst = this._getInst(obj);
+ var isRTL = this._get(inst, 'isRTL');
+ while (obj && (obj.type == 'hidden' || obj.nodeType != 1 || $.expr.filters.hidden(obj))) {
+ obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
+ }
+ var position = $(obj).offset();
+ return [position.left, position.top];
+ },
+
+ /* Hide the date picker from view.
+ @param input element - the input field attached to the date picker */
+ _hideDatepicker: function(input) {
+ var inst = this._curInst;
+ if (!inst || (input && inst != $.data(input, PROP_NAME)))
+ return;
+ if (this._datepickerShowing) {
+ var showAnim = this._get(inst, 'showAnim');
+ var duration = this._get(inst, 'duration');
+ var self = this;
+ var postProcess = function() {
+ $.datepicker._tidyDialog(inst);
+ self._curInst = null;
+ };
+ if ($.effects && $.effects[showAnim])
+ inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
+ else
+ inst.dpDiv[(showAnim == 'slideDown' ? 'slideUp' :
+ (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
+ if (!showAnim)
+ postProcess();
+ this._datepickerShowing = false;
+ var onClose = this._get(inst, 'onClose');
+ if (onClose)
+ onClose.apply((inst.input ? inst.input[0] : null),
+ [(inst.input ? inst.input.val() : ''), inst]);
+ this._lastInput = null;
+ if (this._inDialog) {
+ this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
+ if ($.blockUI) {
+ $.unblockUI();
+ $('body').append(this.dpDiv);
+ }
+ }
+ this._inDialog = false;
+ }
+ },
+
+ /* Tidy up after a dialog display. */
+ _tidyDialog: function(inst) {
+ inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker-calendar');
+ },
+
+ /* Close date picker if clicked elsewhere. */
+ _checkExternalClick: function(event) {
+ if (!$.datepicker._curInst)
+ return;
+
+ var $target = $(event.target),
+ inst = $.datepicker._getInst($target[0]);
+
+ if ( ( ( $target[0].id != $.datepicker._mainDivId &&
+ $target.parents('#' + $.datepicker._mainDivId).length == 0 &&
+ !$target.hasClass($.datepicker.markerClassName) &&
+ !$target.closest("." + $.datepicker._triggerClass).length &&
+ $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
+ ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst != inst ) )
+ $.datepicker._hideDatepicker();
+ },
+
+ /* Adjust one of the date sub-fields. */
+ _adjustDate: function(id, offset, period) {
+ var target = $(id);
+ var inst = this._getInst(target[0]);
+ if (this._isDisabledDatepicker(target[0])) {
+ return;
+ }
+ this._adjustInstDate(inst, offset +
+ (period == 'M' ? this._get(inst, 'showCurrentAtPos') : 0), // undo positioning
+ period);
+ this._updateDatepicker(inst);
+ },
+
+ /* Action for current link. */
+ _gotoToday: function(id) {
+ var target = $(id);
+ var inst = this._getInst(target[0]);
+ if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
+ inst.selectedDay = inst.currentDay;
+ inst.drawMonth = inst.selectedMonth = inst.currentMonth;
+ inst.drawYear = inst.selectedYear = inst.currentYear;
+ }
+ else {
+ var date = new Date();
+ inst.selectedDay = date.getDate();
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
+ inst.drawYear = inst.selectedYear = date.getFullYear();
+ }
+ this._notifyChange(inst);
+ this._adjustDate(target);
+ },
+
+ /* Action for selecting a new month/year. */
+ _selectMonthYear: function(id, select, period) {
+ var target = $(id);
+ var inst = this._getInst(target[0]);
+ inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
+ inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
+ parseInt(select.options[select.selectedIndex].value,10);
+ this._notifyChange(inst);
+ this._adjustDate(target);
+ },
+
+ /* Action for selecting a day. */
+ _selectDay: function(id, month, year, td) {
+ var target = $(id);
+ if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
+ return;
+ }
+ var inst = this._getInst(target[0]);
+ inst.selectedDay = inst.currentDay = $('a', td).html();
+ inst.selectedMonth = inst.currentMonth = month;
+ inst.selectedYear = inst.currentYear = year;
+ this._selectDate(id, this._formatDate(inst,
+ inst.currentDay, inst.currentMonth, inst.currentYear));
+ },
+
+ /* Erase the input field and hide the date picker. */
+ _clearDate: function(id) {
+ var target = $(id);
+ var inst = this._getInst(target[0]);
+ this._selectDate(target, '');
+ },
+
+ /* Update the input field with the selected date. */
+ _selectDate: function(id, dateStr) {
+ var target = $(id);
+ var inst = this._getInst(target[0]);
+ dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
+ if (inst.input)
+ inst.input.val(dateStr);
+ this._updateAlternate(inst);
+ var onSelect = this._get(inst, 'onSelect');
+ if (onSelect)
+ onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
+ else if (inst.input)
+ inst.input.trigger('change'); // fire the change event
+ if (inst.inline)
+ this._updateDatepicker(inst);
+ else {
+ this._hideDatepicker();
+ this._lastInput = inst.input[0];
+ if (typeof(inst.input[0]) != 'object')
+ inst.input.focus(); // restore focus
+ this._lastInput = null;
+ }
+ },
+
+ /* Update any alternate field to synchronise with the main field. */
+ _updateAlternate: function(inst) {
+ var altField = this._get(inst, 'altField');
+ if (altField) { // update alternate field too
+ var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
+ var date = this._getDate(inst);
+ var dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
+ $(altField).each(function() { $(this).val(dateStr); });
+ }
+ },
+
+ /* Set as beforeShowDay function to prevent selection of weekends.
+ @param date Date - the date to customise
+ @return [boolean, string] - is this date selectable?, what is its CSS class? */
+ noWeekends: function(date) {
+ var day = date.getDay();
+ return [(day > 0 && day < 6), ''];
+ },
+
+ /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
+ @param date Date - the date to get the week for
+ @return number - the number of the week within the year that contains this date */
+ iso8601Week: function(date) {
+ var checkDate = new Date(date.getTime());
+ // Find Thursday of this week starting on Monday
+ checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
+ var time = checkDate.getTime();
+ checkDate.setMonth(0); // Compare with Jan 1
+ checkDate.setDate(1);
+ return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
+ },
+
+ /* Parse a string value into a date object.
+ See formatDate below for the possible formats.
+
+ @param format string - the expected format of the date
+ @param value string - the date in the above format
+ @param settings Object - attributes include:
+ shortYearCutoff number - the cutoff year for determining the century (optional)
+ dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
+ dayNames string[7] - names of the days from Sunday (optional)
+ monthNamesShort string[12] - abbreviated names of the months (optional)
+ monthNames string[12] - names of the months (optional)
+ @return Date - the extracted date value or null if value is blank */
+ parseDate: function (format, value, settings) {
+ if (format == null || value == null)
+ throw 'Invalid arguments';
+ value = (typeof value == 'object' ? value.toString() : value + '');
+ if (value == '')
+ return null;
+ var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
+ shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
+ new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
+ var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
+ var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
+ var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
+ var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
+ var year = -1;
+ var month = -1;
+ var day = -1;
+ var doy = -1;
+ var literal = false;
+ // Check whether a format character is doubled
+ var lookAhead = function(match) {
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
+ if (matches)
+ iFormat++;
+ return matches;
+ };
+ // Extract a number from the string value
+ var getNumber = function(match) {
+ var isDoubled = lookAhead(match);
+ var size = (match == '@' ? 14 : (match == '!' ? 20 :
+ (match == 'y' && isDoubled ? 4 : (match == 'o' ? 3 : 2))));
+ var digits = new RegExp('^\\d{1,' + size + '}');
+ var num = value.substring(iValue).match(digits);
+ if (!num)
+ throw 'Missing number at position ' + iValue;
+ iValue += num[0].length;
+ return parseInt(num[0], 10);
+ };
+ // Extract a name from the string value and convert to an index
+ var getName = function(match, shortNames, longNames) {
+ var names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
+ return [ [k, v] ];
+ }).sort(function (a, b) {
+ return -(a[1].length - b[1].length);
+ });
+ var index = -1;
+ $.each(names, function (i, pair) {
+ var name = pair[1];
+ if (value.substr(iValue, name.length).toLowerCase() == name.toLowerCase()) {
+ index = pair[0];
+ iValue += name.length;
+ return false;
+ }
+ });
+ if (index != -1)
+ return index + 1;
+ else
+ throw 'Unknown name at position ' + iValue;
+ };
+ // Confirm that a literal character matches the string value
+ var checkLiteral = function() {
+ if (value.charAt(iValue) != format.charAt(iFormat))
+ throw 'Unexpected literal at position ' + iValue;
+ iValue++;
+ };
+ var iValue = 0;
+ for (var iFormat = 0; iFormat < format.length; iFormat++) {
+ if (literal)
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
+ literal = false;
+ else
+ checkLiteral();
+ else
+ switch (format.charAt(iFormat)) {
+ case 'd':
+ day = getNumber('d');
+ break;
+ case 'D':
+ getName('D', dayNamesShort, dayNames);
+ break;
+ case 'o':
+ doy = getNumber('o');
+ break;
+ case 'm':
+ month = getNumber('m');
+ break;
+ case 'M':
+ month = getName('M', monthNamesShort, monthNames);
+ break;
+ case 'y':
+ year = getNumber('y');
+ break;
+ case '@':
+ var date = new Date(getNumber('@'));
+ year = date.getFullYear();
+ month = date.getMonth() + 1;
+ day = date.getDate();
+ break;
+ case '!':
+ var date = new Date((getNumber('!') - this._ticksTo1970) / 10000);
+ year = date.getFullYear();
+ month = date.getMonth() + 1;
+ day = date.getDate();
+ break;
+ case "'":
+ if (lookAhead("'"))
+ checkLiteral();
+ else
+ literal = true;
+ break;
+ default:
+ checkLiteral();
+ }
+ }
+ if (iValue < value.length){
+ throw "Extra/unparsed characters found in date: " + value.substring(iValue);
+ }
+ if (year == -1)
+ year = new Date().getFullYear();
+ else if (year < 100)
+ year += new Date().getFullYear() - new Date().getFullYear() % 100 +
+ (year <= shortYearCutoff ? 0 : -100);
+ if (doy > -1) {
+ month = 1;
+ day = doy;
+ do {
+ var dim = this._getDaysInMonth(year, month - 1);
+ if (day <= dim)
+ break;
+ month++;
+ day -= dim;
+ } while (true);
+ }
+ var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
+ if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
+ throw 'Invalid date'; // E.g. 31/02/00
+ return date;
+ },
+
+ /* Standard date formats. */
+ ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
+ COOKIE: 'D, dd M yy',
+ ISO_8601: 'yy-mm-dd',
+ RFC_822: 'D, d M y',
+ RFC_850: 'DD, dd-M-y',
+ RFC_1036: 'D, d M y',
+ RFC_1123: 'D, d M yy',
+ RFC_2822: 'D, d M yy',
+ RSS: 'D, d M y', // RFC 822
+ TICKS: '!',
+ TIMESTAMP: '@',
+ W3C: 'yy-mm-dd', // ISO 8601
+
+ _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
+ Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
+
+ /* Format a date object into a string value.
+ The format can be combinations of the following:
+ d - day of month (no leading zero)
+ dd - day of month (two digit)
+ o - day of year (no leading zeros)
+ oo - day of year (three digit)
+ D - day name short
+ DD - day name long
+ m - month of year (no leading zero)
+ mm - month of year (two digit)
+ M - month name short
+ MM - month name long
+ y - year (two digit)
+ yy - year (four digit)
+ @ - Unix timestamp (ms since 01/01/1970)
+ ! - Windows ticks (100ns since 01/01/0001)
+ '...' - literal text
+ '' - single quote
+
+ @param format string - the desired format of the date
+ @param date Date - the date value to format
+ @param settings Object - attributes include:
+ dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
+ dayNames string[7] - names of the days from Sunday (optional)
+ monthNamesShort string[12] - abbreviated names of the months (optional)
+ monthNames string[12] - names of the months (optional)
+ @return string - the date in the above format */
+ formatDate: function (format, date, settings) {
+ if (!date)
+ return '';
+ var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
+ var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
+ var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
+ var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
+ // Check whether a format character is doubled
+ var lookAhead = function(match) {
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
+ if (matches)
+ iFormat++;
+ return matches;
+ };
+ // Format a number, with leading zero if necessary
+ var formatNumber = function(match, value, len) {
+ var num = '' + value;
+ if (lookAhead(match))
+ while (num.length < len)
+ num = '0' + num;
+ return num;
+ };
+ // Format a name, short or long as requested
+ var formatName = function(match, value, shortNames, longNames) {
+ return (lookAhead(match) ? longNames[value] : shortNames[value]);
+ };
+ var output = '';
+ var literal = false;
+ if (date)
+ for (var iFormat = 0; iFormat < format.length; iFormat++) {
+ if (literal)
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
+ literal = false;
+ else
+ output += format.charAt(iFormat);
+ else
+ switch (format.charAt(iFormat)) {
+ case 'd':
+ output += formatNumber('d', date.getDate(), 2);
+ break;
+ case 'D':
+ output += formatName('D', date.getDay(), dayNamesShort, dayNames);
+ break;
+ case 'o':
+ output += formatNumber('o',
+ Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
+ break;
+ case 'm':
+ output += formatNumber('m', date.getMonth() + 1, 2);
+ break;
+ case 'M':
+ output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
+ break;
+ case 'y':
+ output += (lookAhead('y') ? date.getFullYear() :
+ (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
+ break;
+ case '@':
+ output += date.getTime();
+ break;
+ case '!':
+ output += date.getTime() * 10000 + this._ticksTo1970;
+ break;
+ case "'":
+ if (lookAhead("'"))
+ output += "'";
+ else
+ literal = true;
+ break;
+ default:
+ output += format.charAt(iFormat);
+ }
+ }
+ return output;
+ },
+
+ /* Extract all possible characters from the date format. */
+ _possibleChars: function (format) {
+ var chars = '';
+ var literal = false;
+ // Check whether a format character is doubled
+ var lookAhead = function(match) {
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
+ if (matches)
+ iFormat++;
+ return matches;
+ };
+ for (var iFormat = 0; iFormat < format.length; iFormat++)
+ if (literal)
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
+ literal = false;
+ else
+ chars += format.charAt(iFormat);
+ else
+ switch (format.charAt(iFormat)) {
+ case 'd': case 'm': case 'y': case '@':
+ chars += '0123456789';
+ break;
+ case 'D': case 'M':
+ return null; // Accept anything
+ case "'":
+ if (lookAhead("'"))
+ chars += "'";
+ else
+ literal = true;
+ break;
+ default:
+ chars += format.charAt(iFormat);
+ }
+ return chars;
+ },
+
+ /* Get a setting value, defaulting if necessary. */
+ _get: function(inst, name) {
+ return inst.settings[name] !== undefined ?
+ inst.settings[name] : this._defaults[name];
+ },
+
+ /* Parse existing date and initialise date picker. */
+ _setDateFromField: function(inst, noDefault) {
+ if (inst.input.val() == inst.lastVal) {
+ return;
+ }
+ var dateFormat = this._get(inst, 'dateFormat');
+ var dates = inst.lastVal = inst.input ? inst.input.val() : null;
+ var date, defaultDate;
+ date = defaultDate = this._getDefaultDate(inst);
+ var settings = this._getFormatConfig(inst);
+ try {
+ date = this.parseDate(dateFormat, dates, settings) || defaultDate;
+ } catch (event) {
+ this.log(event);
+ dates = (noDefault ? '' : dates);
+ }
+ inst.selectedDay = date.getDate();
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
+ inst.drawYear = inst.selectedYear = date.getFullYear();
+ inst.currentDay = (dates ? date.getDate() : 0);
+ inst.currentMonth = (dates ? date.getMonth() : 0);
+ inst.currentYear = (dates ? date.getFullYear() : 0);
+ this._adjustInstDate(inst);
+ },
+
+ /* Retrieve the default date shown on opening. */
+ _getDefaultDate: function(inst) {
+ return this._restrictMinMax(inst,
+ this._determineDate(inst, this._get(inst, 'defaultDate'), new Date()));
+ },
+
+ /* A date may be specified as an exact value or a relative one. */
+ _determineDate: function(inst, date, defaultDate) {
+ var offsetNumeric = function(offset) {
+ var date = new Date();
+ date.setDate(date.getDate() + offset);
+ return date;
+ };
+ var offsetString = function(offset) {
+ try {
+ return $.datepicker.parseDate($.datepicker._get(inst, 'dateFormat'),
+ offset, $.datepicker._getFormatConfig(inst));
+ }
+ catch (e) {
+ // Ignore
+ }
+ var date = (offset.toLowerCase().match(/^c/) ?
+ $.datepicker._getDate(inst) : null) || new Date();
+ var year = date.getFullYear();
+ var month = date.getMonth();
+ var day = date.getDate();
+ var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
+ var matches = pattern.exec(offset);
+ while (matches) {
+ switch (matches[2] || 'd') {
+ case 'd' : case 'D' :
+ day += parseInt(matches[1],10); break;
+ case 'w' : case 'W' :
+ day += parseInt(matches[1],10) * 7; break;
+ case 'm' : case 'M' :
+ month += parseInt(matches[1],10);
+ day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
+ break;
+ case 'y': case 'Y' :
+ year += parseInt(matches[1],10);
+ day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
+ break;
+ }
+ matches = pattern.exec(offset);
+ }
+ return new Date(year, month, day);
+ };
+ var newDate = (date == null || date === '' ? defaultDate : (typeof date == 'string' ? offsetString(date) :
+ (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
+ newDate = (newDate && newDate.toString() == 'Invalid Date' ? defaultDate : newDate);
+ if (newDate) {
+ newDate.setHours(0);
+ newDate.setMinutes(0);
+ newDate.setSeconds(0);
+ newDate.setMilliseconds(0);
+ }
+ return this._daylightSavingAdjust(newDate);
+ },
+
+ /* Handle switch to/from daylight saving.
+ Hours may be non-zero on daylight saving cut-over:
+ > 12 when midnight changeover, but then cannot generate
+ midnight datetime, so jump to 1AM, otherwise reset.
+ @param date (Date) the date to check
+ @return (Date) the corrected date */
+ _daylightSavingAdjust: function(date) {
+ if (!date) return null;
+ date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
+ return date;
+ },
+
+ /* Set the date(s) directly. */
+ _setDate: function(inst, date, noChange) {
+ var clear = !date;
+ var origMonth = inst.selectedMonth;
+ var origYear = inst.selectedYear;
+ var newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
+ inst.selectedDay = inst.currentDay = newDate.getDate();
+ inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
+ inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
+ if ((origMonth != inst.selectedMonth || origYear != inst.selectedYear) && !noChange)
+ this._notifyChange(inst);
+ this._adjustInstDate(inst);
+ if (inst.input) {
+ inst.input.val(clear ? '' : this._formatDate(inst));
+ }
+ },
+
+ /* Retrieve the date(s) directly. */
+ _getDate: function(inst) {
+ var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null :
+ this._daylightSavingAdjust(new Date(
+ inst.currentYear, inst.currentMonth, inst.currentDay)));
+ return startDate;
+ },
+
+ /* Generate the HTML for the current state of the date picker. */
+ _generateHTML: function(inst) {
+ var today = new Date();
+ today = this._daylightSavingAdjust(
+ new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time
+ var isRTL = this._get(inst, 'isRTL');
+ var showButtonPanel = this._get(inst, 'showButtonPanel');
+ var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
+ var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
+ var numMonths = this._getNumberOfMonths(inst);
+ var showCurrentAtPos = this._get(inst, 'showCurrentAtPos');
+ var stepMonths = this._get(inst, 'stepMonths');
+ var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
+ var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
+ new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
+ var minDate = this._getMinMaxDate(inst, 'min');
+ var maxDate = this._getMinMaxDate(inst, 'max');
+ var drawMonth = inst.drawMonth - showCurrentAtPos;
+ var drawYear = inst.drawYear;
+ if (drawMonth < 0) {
+ drawMonth += 12;
+ drawYear--;
+ }
+ if (maxDate) {
+ var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
+ maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
+ maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
+ while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
+ drawMonth--;
+ if (drawMonth < 0) {
+ drawMonth = 11;
+ drawYear--;
+ }
+ }
+ }
+ inst.drawMonth = drawMonth;
+ inst.drawYear = drawYear;
+ var prevText = this._get(inst, 'prevText');
+ prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
+ this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
+ this._getFormatConfig(inst)));
+ var prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
+ '<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_' + dpuuid +
+ '.datepicker._adjustDate(\'#' + inst.id + '\', -' + stepMonths + ', \'M\');"' +
+ ' title="' + prevText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>' :
+ (hideIfNoPrevNext ? '' : '<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+ prevText +'"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'e' : 'w') + '">' + prevText + '</span></a>'));
+ var nextText = this._get(inst, 'nextText');
+ nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
+ this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
+ this._getFormatConfig(inst)));
+ var next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
+ '<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_' + dpuuid +
+ '.datepicker._adjustDate(\'#' + inst.id + '\', +' + stepMonths + ', \'M\');"' +
+ ' title="' + nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>' :
+ (hideIfNoPrevNext ? '' : '<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+ nextText + '"><span class="ui-icon ui-icon-circle-triangle-' + ( isRTL ? 'w' : 'e') + '">' + nextText + '</span></a>'));
+ var currentText = this._get(inst, 'currentText');
+ var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today);
+ currentText = (!navigationAsDateFormat ? currentText :
+ this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
+ var controls = (!inst.inline ? '<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_' + dpuuid +
+ '.datepicker._hideDatepicker();">' + this._get(inst, 'closeText') + '</button>' : '');
+ var buttonPanel = (showButtonPanel) ? '<div class="ui-datepicker-buttonpane ui-widget-content">' + (isRTL ? controls : '') +
+ (this._isInRange(inst, gotoDate) ? '<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_' + dpuuid +
+ '.datepicker._gotoToday(\'#' + inst.id + '\');"' +
+ '>' + currentText + '</button>' : '') + (isRTL ? '' : controls) + '</div>' : '';
+ var firstDay = parseInt(this._get(inst, 'firstDay'),10);
+ firstDay = (isNaN(firstDay) ? 0 : firstDay);
+ var showWeek = this._get(inst, 'showWeek');
+ var dayNames = this._get(inst, 'dayNames');
+ var dayNamesShort = this._get(inst, 'dayNamesShort');
+ var dayNamesMin = this._get(inst, 'dayNamesMin');
+ var monthNames = this._get(inst, 'monthNames');
+ var monthNamesShort = this._get(inst, 'monthNamesShort');
+ var beforeShowDay = this._get(inst, 'beforeShowDay');
+ var showOtherMonths = this._get(inst, 'showOtherMonths');
+ var selectOtherMonths = this._get(inst, 'selectOtherMonths');
+ var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
+ var defaultDate = this._getDefaultDate(inst);
+ var html = '';
+ for (var row = 0; row < numMonths[0]; row++) {
+ var group = '';
+ this.maxRows = 4;
+ for (var col = 0; col < numMonths[1]; col++) {
+ var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
+ var cornerClass = ' ui-corner-all';
+ var calender = '';
+ if (isMultiMonth) {
+ calender += '<div class="ui-datepicker-group';
+ if (numMonths[1] > 1)
+ switch (col) {
+ case 0: calender += ' ui-datepicker-group-first';
+ cornerClass = ' ui-corner-' + (isRTL ? 'right' : 'left'); break;
+ case numMonths[1]-1: calender += ' ui-datepicker-group-last';
+ cornerClass = ' ui-corner-' + (isRTL ? 'left' : 'right'); break;
+ default: calender += ' ui-datepicker-group-middle'; cornerClass = ''; break;
+ }
+ calender += '">';
+ }
+ calender += '<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix' + cornerClass + '">' +
+ (/all|left/.test(cornerClass) && row == 0 ? (isRTL ? next : prev) : '') +
+ (/all|right/.test(cornerClass) && row == 0 ? (isRTL ? prev : next) : '') +
+ this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
+ row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
+ '</div><table class="ui-datepicker-calendar"><thead>' +
+ '<tr>';
+ var thead = (showWeek ? '<th class="ui-datepicker-week-col">' + this._get(inst, 'weekHeader') + '</th>' : '');
+ for (var dow = 0; dow < 7; dow++) { // days of the week
+ var day = (dow + firstDay) % 7;
+ thead += '<th' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end"' : '') + '>' +
+ '<span title="' + dayNames[day] + '">' + dayNamesMin[day] + '</span></th>';
+ }
+ calender += thead + '</tr></thead><tbody>';
+ var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
+ if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
+ inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
+ var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
+ var curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
+ var numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
+ this.maxRows = numRows;
+ var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
+ for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
+ calender += '<tr>';
+ var tbody = (!showWeek ? '' : '<td class="ui-datepicker-week-col">' +
+ this._get(inst, 'calculateWeek')(printDate) + '</td>');
+ for (var dow = 0; dow < 7; dow++) { // create date picker days
+ var daySettings = (beforeShowDay ?
+ beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
+ var otherMonth = (printDate.getMonth() != drawMonth);
+ var unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
+ (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
+ tbody += '<td class="' +
+ ((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end' : '') + // highlight weekends
+ (otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months
+ ((printDate.getTime() == selectedDate.getTime() && drawMonth == inst.selectedMonth && inst._keyEvent) || // user pressed key
+ (defaultDate.getTime() == printDate.getTime() && defaultDate.getTime() == selectedDate.getTime()) ?
+ // or defaultDate is current printedDate and defaultDate is selectedDate
+ ' ' + this._dayOverClass : '') + // highlight selected day
+ (unselectable ? ' ' + this._unselectableClass + ' ui-state-disabled': '') + // highlight unselectable days
+ (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
+ (printDate.getTime() == currentDate.getTime() ? ' ' + this._currentClass : '') + // highlight selected day
+ (printDate.getTime() == today.getTime() ? ' ui-datepicker-today' : '')) + '"' + // highlight today (if different)
+ ((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
+ (unselectable ? '' : ' onclick="DP_jQuery_' + dpuuid + '.datepicker._selectDay(\'#' +
+ inst.id + '\',' + printDate.getMonth() + ',' + printDate.getFullYear() + ', this);return false;"') + '>' + // actions
+ (otherMonth && !showOtherMonths ? '&#xa0;' : // display for other months
+ (unselectable ? '<span class="ui-state-default">' + printDate.getDate() + '</span>' : '<a class="ui-state-default' +
+ (printDate.getTime() == today.getTime() ? ' ui-state-highlight' : '') +
+ (printDate.getTime() == currentDate.getTime() ? ' ui-state-active' : '') + // highlight selected day
+ (otherMonth ? ' ui-priority-secondary' : '') + // distinguish dates from other months
+ '" href="#">' + printDate.getDate() + '</a>')) + '</td>'; // display selectable date
+ printDate.setDate(printDate.getDate() + 1);
+ printDate = this._daylightSavingAdjust(printDate);
+ }
+ calender += tbody + '</tr>';
+ }
+ drawMonth++;
+ if (drawMonth > 11) {
+ drawMonth = 0;
+ drawYear++;
+ }
+ calender += '</tbody></table>' + (isMultiMonth ? '</div>' +
+ ((numMonths[0] > 0 && col == numMonths[1]-1) ? '<div class="ui-datepicker-row-break"></div>' : '') : '');
+ group += calender;
+ }
+ html += group;
+ }
+ html += buttonPanel + ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ?
+ '<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>' : '');
+ inst._keyEvent = false;
+ return html;
+ },
+
+ /* Generate the month and year header. */
+ _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
+ secondary, monthNames, monthNamesShort) {
+ var changeMonth = this._get(inst, 'changeMonth');
+ var changeYear = this._get(inst, 'changeYear');
+ var showMonthAfterYear = this._get(inst, 'showMonthAfterYear');
+ var html = '<div class="ui-datepicker-title">';
+ var monthHtml = '';
+ // month selection
+ if (secondary || !changeMonth)
+ monthHtml += '<span class="ui-datepicker-month">' + monthNames[drawMonth] + '</span>';
+ else {
+ var inMinYear = (minDate && minDate.getFullYear() == drawYear);
+ var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
+ monthHtml += '<select class="ui-datepicker-month" ' +
+ 'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'M\');" ' +
+ '>';
+ for (var month = 0; month < 12; month++) {
+ if ((!inMinYear || month >= minDate.getMonth()) &&
+ (!inMaxYear || month <= maxDate.getMonth()))
+ monthHtml += '<option value="' + month + '"' +
+ (month == drawMonth ? ' selected="selected"' : '') +
+ '>' + monthNamesShort[month] + '</option>';
+ }
+ monthHtml += '</select>';
+ }
+ if (!showMonthAfterYear)
+ html += monthHtml + (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '');
+ // year selection
+ if ( !inst.yearshtml ) {
+ inst.yearshtml = '';
+ if (secondary || !changeYear)
+ html += '<span class="ui-datepicker-year">' + drawYear + '</span>';
+ else {
+ // determine range of years to display
+ var years = this._get(inst, 'yearRange').split(':');
+ var thisYear = new Date().getFullYear();
+ var determineYear = function(value) {
+ var year = (value.match(/c[+-].*/) ? drawYear + parseInt(value.substring(1), 10) :
+ (value.match(/[+-].*/) ? thisYear + parseInt(value, 10) :
+ parseInt(value, 10)));
+ return (isNaN(year) ? thisYear : year);
+ };
+ var year = determineYear(years[0]);
+ var endYear = Math.max(year, determineYear(years[1] || ''));
+ year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
+ endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
+ inst.yearshtml += '<select class="ui-datepicker-year" ' +
+ 'onchange="DP_jQuery_' + dpuuid + '.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'Y\');" ' +
+ '>';
+ for (; year <= endYear; year++) {
+ inst.yearshtml += '<option value="' + year + '"' +
+ (year == drawYear ? ' selected="selected"' : '') +
+ '>' + year + '</option>';
+ }
+ inst.yearshtml += '</select>';
+
+ html += inst.yearshtml;
+ inst.yearshtml = null;
+ }
+ }
+ html += this._get(inst, 'yearSuffix');
+ if (showMonthAfterYear)
+ html += (secondary || !(changeMonth && changeYear) ? '&#xa0;' : '') + monthHtml;
+ html += '</div>'; // Close datepicker_header
+ return html;
+ },
+
+ /* Adjust one of the date sub-fields. */
+ _adjustInstDate: function(inst, offset, period) {
+ var year = inst.drawYear + (period == 'Y' ? offset : 0);
+ var month = inst.drawMonth + (period == 'M' ? offset : 0);
+ var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) +
+ (period == 'D' ? offset : 0);
+ var date = this._restrictMinMax(inst,
+ this._daylightSavingAdjust(new Date(year, month, day)));
+ inst.selectedDay = date.getDate();
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
+ inst.drawYear = inst.selectedYear = date.getFullYear();
+ if (period == 'M' || period == 'Y')
+ this._notifyChange(inst);
+ },
+
+ /* Ensure a date is within any min/max bounds. */
+ _restrictMinMax: function(inst, date) {
+ var minDate = this._getMinMaxDate(inst, 'min');
+ var maxDate = this._getMinMaxDate(inst, 'max');
+ var newDate = (minDate && date < minDate ? minDate : date);
+ newDate = (maxDate && newDate > maxDate ? maxDate : newDate);
+ return newDate;
+ },
+
+ /* Notify change of month/year. */
+ _notifyChange: function(inst) {
+ var onChange = this._get(inst, 'onChangeMonthYear');
+ if (onChange)
+ onChange.apply((inst.input ? inst.input[0] : null),
+ [inst.selectedYear, inst.selectedMonth + 1, inst]);
+ },
+
+ /* Determine the number of months to show. */
+ _getNumberOfMonths: function(inst) {
+ var numMonths = this._get(inst, 'numberOfMonths');
+ return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
+ },
+
+ /* Determine the current maximum date - ensure no time components are set. */
+ _getMinMaxDate: function(inst, minMax) {
+ return this._determineDate(inst, this._get(inst, minMax + 'Date'), null);
+ },
+
+ /* Find the number of days in a given month. */
+ _getDaysInMonth: function(year, month) {
+ return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
+ },
+
+ /* Find the day of the week of the first of a month. */
+ _getFirstDayOfMonth: function(year, month) {
+ return new Date(year, month, 1).getDay();
+ },
+
+ /* Determines if we should allow a "next/prev" month display change. */
+ _canAdjustMonth: function(inst, offset, curYear, curMonth) {
+ var numMonths = this._getNumberOfMonths(inst);
+ var date = this._daylightSavingAdjust(new Date(curYear,
+ curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
+ if (offset < 0)
+ date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
+ return this._isInRange(inst, date);
+ },
+
+ /* Is the given date in the accepted range? */
+ _isInRange: function(inst, date) {
+ var minDate = this._getMinMaxDate(inst, 'min');
+ var maxDate = this._getMinMaxDate(inst, 'max');
+ return ((!minDate || date.getTime() >= minDate.getTime()) &&
+ (!maxDate || date.getTime() <= maxDate.getTime()));
+ },
+
+ /* Provide the configuration settings for formatting/parsing. */
+ _getFormatConfig: function(inst) {
+ var shortYearCutoff = this._get(inst, 'shortYearCutoff');
+ shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
+ new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
+ return {shortYearCutoff: shortYearCutoff,
+ dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
+ monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
+ },
+
+ /* Format the given date for display. */
+ _formatDate: function(inst, day, month, year) {
+ if (!day) {
+ inst.currentDay = inst.selectedDay;
+ inst.currentMonth = inst.selectedMonth;
+ inst.currentYear = inst.selectedYear;
+ }
+ var date = (day ? (typeof day == 'object' ? day :
+ this._daylightSavingAdjust(new Date(year, month, day))) :
+ this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
+ return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
+ }
+});
+
+/*
+ * Bind hover events for datepicker elements.
+ * Done via delegate so the binding only occurs once in the lifetime of the parent div.
+ * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
+ */
+function bindHover(dpDiv) {
+ var selector = 'button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a';
+ return dpDiv.bind('mouseout', function(event) {
+ var elem = $( event.target ).closest( selector );
+ if ( !elem.length ) {
+ return;
+ }
+ elem.removeClass( "ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover" );
+ })
+ .bind('mouseover', function(event) {
+ var elem = $( event.target ).closest( selector );
+ if ($.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0]) ||
+ !elem.length ) {
+ return;
+ }
+ elem.parents('.ui-datepicker-calendar').find('a').removeClass('ui-state-hover');
+ elem.addClass('ui-state-hover');
+ if (elem.hasClass('ui-datepicker-prev')) elem.addClass('ui-datepicker-prev-hover');
+ if (elem.hasClass('ui-datepicker-next')) elem.addClass('ui-datepicker-next-hover');
+ });
+}
+
+/* jQuery extend now ignores nulls! */
+function extendRemove(target, props) {
+ $.extend(target, props);
+ for (var name in props)
+ if (props[name] == null || props[name] == undefined)
+ target[name] = props[name];
+ return target;
+};
+
+/* Determine whether an object is an array. */
+function isArray(a) {
+ return (a && (($.browser.safari && typeof a == 'object' && a.length) ||
+ (a.constructor && a.constructor.toString().match(/\Array\(\)/))));
+};
+
+/* Invoke the datepicker functionality.
+ @param options string - a command, optionally followed by additional parameters or
+ Object - settings for attaching new datepicker functionality
+ @return jQuery object */
+$.fn.datepicker = function(options){
+
+ /* Verify an empty collection wasn't passed - Fixes #6976 */
+ if ( !this.length ) {
+ return this;
+ }
+
+ /* Initialise the date picker. */
+ if (!$.datepicker.initialized) {
+ $(document).mousedown($.datepicker._checkExternalClick).
+ find('body').append($.datepicker.dpDiv);
+ $.datepicker.initialized = true;
+ }
+
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
+ if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate' || options == 'widget'))
+ return $.datepicker['_' + options + 'Datepicker'].
+ apply($.datepicker, [this[0]].concat(otherArgs));
+ if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
+ return $.datepicker['_' + options + 'Datepicker'].
+ apply($.datepicker, [this[0]].concat(otherArgs));
+ return this.each(function() {
+ typeof options == 'string' ?
+ $.datepicker['_' + options + 'Datepicker'].
+ apply($.datepicker, [this].concat(otherArgs)) :
+ $.datepicker._attachDatepicker(this, options);
+ });
+};
+
+$.datepicker = new Datepicker(); // singleton instance
+$.datepicker.initialized = false;
+$.datepicker.uuid = new Date().getTime();
+$.datepicker.version = "1.8.18";
+
+// Workaround for #4055
+// Add another global to avoid noConflict issues with inline event handlers
+window['DP_jQuery_' + dpuuid] = $;
+
+})(jQuery);
+/*
+ * jQuery UI Dialog 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ * jquery.ui.button.js
+ * jquery.ui.draggable.js
+ * jquery.ui.mouse.js
+ * jquery.ui.position.js
+ * jquery.ui.resizable.js
+ */
+(function( $, undefined ) {
+
+var uiDialogClasses =
+ 'ui-dialog ' +
+ 'ui-widget ' +
+ 'ui-widget-content ' +
+ 'ui-corner-all ',
+ sizeRelatedOptions = {
+ buttons: true,
+ height: true,
+ maxHeight: true,
+ maxWidth: true,
+ minHeight: true,
+ minWidth: true,
+ width: true
+ },
+ resizableRelatedOptions = {
+ maxHeight: true,
+ maxWidth: true,
+ minHeight: true,
+ minWidth: true
+ },
+ // support for jQuery 1.3.2 - handle common attrFn methods for dialog
+ attrFn = $.attrFn || {
+ val: true,
+ css: true,
+ html: true,
+ text: true,
+ data: true,
+ width: true,
+ height: true,
+ offset: true,
+ click: true
+ };
+
+$.widget("ui.dialog", {
+ options: {
+ autoOpen: true,
+ buttons: {},
+ closeOnEscape: true,
+ closeText: 'close',
+ dialogClass: '',
+ draggable: true,
+ hide: null,
+ height: 'auto',
+ maxHeight: false,
+ maxWidth: false,
+ minHeight: 150,
+ minWidth: 150,
+ modal: false,
+ position: {
+ my: 'center',
+ at: 'center',
+ collision: 'fit',
+ // ensure that the titlebar is never outside the document
+ using: function(pos) {
+ var topOffset = $(this).css(pos).offset().top;
+ if (topOffset < 0) {
+ $(this).css('top', pos.top - topOffset);
+ }
+ }
+ },
+ resizable: true,
+ show: null,
+ stack: true,
+ title: '',
+ width: 300,
+ zIndex: 1000
+ },
+
+ _create: function() {
+ this.originalTitle = this.element.attr('title');
+ // #5742 - .attr() might return a DOMElement
+ if ( typeof this.originalTitle !== "string" ) {
+ this.originalTitle = "";
+ }
+
+ this.options.title = this.options.title || this.originalTitle;
+ var self = this,
+ options = self.options,
+
+ title = options.title || '&#160;',
+ titleId = $.ui.dialog.getTitleId(self.element),
+
+ uiDialog = (self.uiDialog = $('<div></div>'))
+ .appendTo(document.body)
+ .hide()
+ .addClass(uiDialogClasses + options.dialogClass)
+ .css({
+ zIndex: options.zIndex
+ })
+ // setting tabIndex makes the div focusable
+ // setting outline to 0 prevents a border on focus in Mozilla
+ .attr('tabIndex', -1).css('outline', 0).keydown(function(event) {
+ if (options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
+ event.keyCode === $.ui.keyCode.ESCAPE) {
+
+ self.close(event);
+ event.preventDefault();
+ }
+ })
+ .attr({
+ role: 'dialog',
+ 'aria-labelledby': titleId
+ })
+ .mousedown(function(event) {
+ self.moveToTop(false, event);
+ }),
+
+ uiDialogContent = self.element
+ .show()
+ .removeAttr('title')
+ .addClass(
+ 'ui-dialog-content ' +
+ 'ui-widget-content')
+ .appendTo(uiDialog),
+
+ uiDialogTitlebar = (self.uiDialogTitlebar = $('<div></div>'))
+ .addClass(
+ 'ui-dialog-titlebar ' +
+ 'ui-widget-header ' +
+ 'ui-corner-all ' +
+ 'ui-helper-clearfix'
+ )
+ .prependTo(uiDialog),
+
+ uiDialogTitlebarClose = $('<a href="#"></a>')
+ .addClass(
+ 'ui-dialog-titlebar-close ' +
+ 'ui-corner-all'
+ )
+ .attr('role', 'button')
+ .hover(
+ function() {
+ uiDialogTitlebarClose.addClass('ui-state-hover');
+ },
+ function() {
+ uiDialogTitlebarClose.removeClass('ui-state-hover');
+ }
+ )
+ .focus(function() {
+ uiDialogTitlebarClose.addClass('ui-state-focus');
+ })
+ .blur(function() {
+ uiDialogTitlebarClose.removeClass('ui-state-focus');
+ })
+ .click(function(event) {
+ self.close(event);
+ return false;
+ })
+ .appendTo(uiDialogTitlebar),
+
+ uiDialogTitlebarCloseText = (self.uiDialogTitlebarCloseText = $('<span></span>'))
+ .addClass(
+ 'ui-icon ' +
+ 'ui-icon-closethick'
+ )
+ .text(options.closeText)
+ .appendTo(uiDialogTitlebarClose),
+
+ uiDialogTitle = $('<span></span>')
+ .addClass('ui-dialog-title')
+ .attr('id', titleId)
+ .html(title)
+ .prependTo(uiDialogTitlebar);
+
+ //handling of deprecated beforeclose (vs beforeClose) option
+ //Ticket #4669 http://dev.jqueryui.com/ticket/4669
+ //TODO: remove in 1.9pre
+ if ($.isFunction(options.beforeclose) && !$.isFunction(options.beforeClose)) {
+ options.beforeClose = options.beforeclose;
+ }
+
+ uiDialogTitlebar.find("*").add(uiDialogTitlebar).disableSelection();
+
+ if (options.draggable && $.fn.draggable) {
+ self._makeDraggable();
+ }
+ if (options.resizable && $.fn.resizable) {
+ self._makeResizable();
+ }
+
+ self._createButtons(options.buttons);
+ self._isOpen = false;
+
+ if ($.fn.bgiframe) {
+ uiDialog.bgiframe();
+ }
+ },
+
+ _init: function() {
+ if ( this.options.autoOpen ) {
+ this.open();
+ }
+ },
+
+ destroy: function() {
+ var self = this;
+
+ if (self.overlay) {
+ self.overlay.destroy();
+ }
+ self.uiDialog.hide();
+ self.element
+ .unbind('.dialog')
+ .removeData('dialog')
+ .removeClass('ui-dialog-content ui-widget-content')
+ .hide().appendTo('body');
+ self.uiDialog.remove();
+
+ if (self.originalTitle) {
+ self.element.attr('title', self.originalTitle);
+ }
+
+ return self;
+ },
+
+ widget: function() {
+ return this.uiDialog;
+ },
+
+ close: function(event) {
+ var self = this,
+ maxZ, thisZ;
+
+ if (false === self._trigger('beforeClose', event)) {
+ return;
+ }
+
+ if (self.overlay) {
+ self.overlay.destroy();
+ }
+ self.uiDialog.unbind('keypress.ui-dialog');
+
+ self._isOpen = false;
+
+ if (self.options.hide) {
+ self.uiDialog.hide(self.options.hide, function() {
+ self._trigger('close', event);
+ });
+ } else {
+ self.uiDialog.hide();
+ self._trigger('close', event);
+ }
+
+ $.ui.dialog.overlay.resize();
+
+ // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
+ if (self.options.modal) {
+ maxZ = 0;
+ $('.ui-dialog').each(function() {
+ if (this !== self.uiDialog[0]) {
+ thisZ = $(this).css('z-index');
+ if(!isNaN(thisZ)) {
+ maxZ = Math.max(maxZ, thisZ);
+ }
+ }
+ });
+ $.ui.dialog.maxZ = maxZ;
+ }
+
+ return self;
+ },
+
+ isOpen: function() {
+ return this._isOpen;
+ },
+
+ // the force parameter allows us to move modal dialogs to their correct
+ // position on open
+ moveToTop: function(force, event) {
+ var self = this,
+ options = self.options,
+ saveScroll;
+
+ if ((options.modal && !force) ||
+ (!options.stack && !options.modal)) {
+ return self._trigger('focus', event);
+ }
+
+ if (options.zIndex > $.ui.dialog.maxZ) {
+ $.ui.dialog.maxZ = options.zIndex;
+ }
+ if (self.overlay) {
+ $.ui.dialog.maxZ += 1;
+ self.overlay.$el.css('z-index', $.ui.dialog.overlay.maxZ = $.ui.dialog.maxZ);
+ }
+
+ //Save and then restore scroll since Opera 9.5+ resets when parent z-Index is changed.
+ // http://ui.jquery.com/bugs/ticket/3193
+ saveScroll = { scrollTop: self.element.scrollTop(), scrollLeft: self.element.scrollLeft() };
+ $.ui.dialog.maxZ += 1;
+ self.uiDialog.css('z-index', $.ui.dialog.maxZ);
+ self.element.attr(saveScroll);
+ self._trigger('focus', event);
+
+ return self;
+ },
+
+ open: function() {
+ if (this._isOpen) { return; }
+
+ var self = this,
+ options = self.options,
+ uiDialog = self.uiDialog;
+
+ self.overlay = options.modal ? new $.ui.dialog.overlay(self) : null;
+ self._size();
+ self._position(options.position);
+ uiDialog.show(options.show);
+ self.moveToTop(true);
+
+ // prevent tabbing out of modal dialogs
+ if ( options.modal ) {
+ uiDialog.bind( "keydown.ui-dialog", function( event ) {
+ if ( event.keyCode !== $.ui.keyCode.TAB ) {
+ return;
+ }
+
+ var tabbables = $(':tabbable', this),
+ first = tabbables.filter(':first'),
+ last = tabbables.filter(':last');
+
+ if (event.target === last[0] && !event.shiftKey) {
+ first.focus(1);
+ return false;
+ } else if (event.target === first[0] && event.shiftKey) {
+ last.focus(1);
+ return false;
+ }
+ });
+ }
+
+ // set focus to the first tabbable element in the content area or the first button
+ // if there are no tabbable elements, set focus on the dialog itself
+ $(self.element.find(':tabbable').get().concat(
+ uiDialog.find('.ui-dialog-buttonpane :tabbable').get().concat(
+ uiDialog.get()))).eq(0).focus();
+
+ self._isOpen = true;
+ self._trigger('open');
+
+ return self;
+ },
+
+ _createButtons: function(buttons) {
+ var self = this,
+ hasButtons = false,
+ uiDialogButtonPane = $('<div></div>')
+ .addClass(
+ 'ui-dialog-buttonpane ' +
+ 'ui-widget-content ' +
+ 'ui-helper-clearfix'
+ ),
+ uiButtonSet = $( "<div></div>" )
+ .addClass( "ui-dialog-buttonset" )
+ .appendTo( uiDialogButtonPane );
+
+ // if we already have a button pane, remove it
+ self.uiDialog.find('.ui-dialog-buttonpane').remove();
+
+ if (typeof buttons === 'object' && buttons !== null) {
+ $.each(buttons, function() {
+ return !(hasButtons = true);
+ });
+ }
+ if (hasButtons) {
+ $.each(buttons, function(name, props) {
+ props = $.isFunction( props ) ?
+ { click: props, text: name } :
+ props;
+ var button = $('<button type="button"></button>')
+ .click(function() {
+ props.click.apply(self.element[0], arguments);
+ })
+ .appendTo(uiButtonSet);
+ // can't use .attr( props, true ) with jQuery 1.3.2.
+ $.each( props, function( key, value ) {
+ if ( key === "click" ) {
+ return;
+ }
+ if ( key in attrFn ) {
+ button[ key ]( value );
+ } else {
+ button.attr( key, value );
+ }
+ });
+ if ($.fn.button) {
+ button.button();
+ }
+ });
+ uiDialogButtonPane.appendTo(self.uiDialog);
+ }
+ },
+
+ _makeDraggable: function() {
+ var self = this,
+ options = self.options,
+ doc = $(document),
+ heightBeforeDrag;
+
+ function filteredUi(ui) {
+ return {
+ position: ui.position,
+ offset: ui.offset
+ };
+ }
+
+ self.uiDialog.draggable({
+ cancel: '.ui-dialog-content, .ui-dialog-titlebar-close',
+ handle: '.ui-dialog-titlebar',
+ containment: 'document',
+ start: function(event, ui) {
+ heightBeforeDrag = options.height === "auto" ? "auto" : $(this).height();
+ $(this).height($(this).height()).addClass("ui-dialog-dragging");
+ self._trigger('dragStart', event, filteredUi(ui));
+ },
+ drag: function(event, ui) {
+ self._trigger('drag', event, filteredUi(ui));
+ },
+ stop: function(event, ui) {
+ options.position = [ui.position.left - doc.scrollLeft(),
+ ui.position.top - doc.scrollTop()];
+ $(this).removeClass("ui-dialog-dragging").height(heightBeforeDrag);
+ self._trigger('dragStop', event, filteredUi(ui));
+ $.ui.dialog.overlay.resize();
+ }
+ });
+ },
+
+ _makeResizable: function(handles) {
+ handles = (handles === undefined ? this.options.resizable : handles);
+ var self = this,
+ options = self.options,
+ // .ui-resizable has position: relative defined in the stylesheet
+ // but dialogs have to use absolute or fixed positioning
+ position = self.uiDialog.css('position'),
+ resizeHandles = (typeof handles === 'string' ?
+ handles :
+ 'n,e,s,w,se,sw,ne,nw'
+ );
+
+ function filteredUi(ui) {
+ return {
+ originalPosition: ui.originalPosition,
+ originalSize: ui.originalSize,
+ position: ui.position,
+ size: ui.size
+ };
+ }
+
+ self.uiDialog.resizable({
+ cancel: '.ui-dialog-content',
+ containment: 'document',
+ alsoResize: self.element,
+ maxWidth: options.maxWidth,
+ maxHeight: options.maxHeight,
+ minWidth: options.minWidth,
+ minHeight: self._minHeight(),
+ handles: resizeHandles,
+ start: function(event, ui) {
+ $(this).addClass("ui-dialog-resizing");
+ self._trigger('resizeStart', event, filteredUi(ui));
+ },
+ resize: function(event, ui) {
+ self._trigger('resize', event, filteredUi(ui));
+ },
+ stop: function(event, ui) {
+ $(this).removeClass("ui-dialog-resizing");
+ options.height = $(this).height();
+ options.width = $(this).width();
+ self._trigger('resizeStop', event, filteredUi(ui));
+ $.ui.dialog.overlay.resize();
+ }
+ })
+ .css('position', position)
+ .find('.ui-resizable-se').addClass('ui-icon ui-icon-grip-diagonal-se');
+ },
+
+ _minHeight: function() {
+ var options = this.options;
+
+ if (options.height === 'auto') {
+ return options.minHeight;
+ } else {
+ return Math.min(options.minHeight, options.height);
+ }
+ },
+
+ _position: function(position) {
+ var myAt = [],
+ offset = [0, 0],
+ isVisible;
+
+ if (position) {
+ // deep extending converts arrays to objects in jQuery <= 1.3.2 :-(
+ // if (typeof position == 'string' || $.isArray(position)) {
+ // myAt = $.isArray(position) ? position : position.split(' ');
+
+ if (typeof position === 'string' || (typeof position === 'object' && '0' in position)) {
+ myAt = position.split ? position.split(' ') : [position[0], position[1]];
+ if (myAt.length === 1) {
+ myAt[1] = myAt[0];
+ }
+
+ $.each(['left', 'top'], function(i, offsetPosition) {
+ if (+myAt[i] === myAt[i]) {
+ offset[i] = myAt[i];
+ myAt[i] = offsetPosition;
+ }
+ });
+
+ position = {
+ my: myAt.join(" "),
+ at: myAt.join(" "),
+ offset: offset.join(" ")
+ };
+ }
+
+ position = $.extend({}, $.ui.dialog.prototype.options.position, position);
+ } else {
+ position = $.ui.dialog.prototype.options.position;
+ }
+
+ // need to show the dialog to get the actual offset in the position plugin
+ isVisible = this.uiDialog.is(':visible');
+ if (!isVisible) {
+ this.uiDialog.show();
+ }
+ this.uiDialog
+ // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
+ .css({ top: 0, left: 0 })
+ .position($.extend({ of: window }, position));
+ if (!isVisible) {
+ this.uiDialog.hide();
+ }
+ },
+
+ _setOptions: function( options ) {
+ var self = this,
+ resizableOptions = {},
+ resize = false;
+
+ $.each( options, function( key, value ) {
+ self._setOption( key, value );
+
+ if ( key in sizeRelatedOptions ) {
+ resize = true;
+ }
+ if ( key in resizableRelatedOptions ) {
+ resizableOptions[ key ] = value;
+ }
+ });
+
+ if ( resize ) {
+ this._size();
+ }
+ if ( this.uiDialog.is( ":data(resizable)" ) ) {
+ this.uiDialog.resizable( "option", resizableOptions );
+ }
+ },
+
+ _setOption: function(key, value){
+ var self = this,
+ uiDialog = self.uiDialog;
+
+ switch (key) {
+ //handling of deprecated beforeclose (vs beforeClose) option
+ //Ticket #4669 http://dev.jqueryui.com/ticket/4669
+ //TODO: remove in 1.9pre
+ case "beforeclose":
+ key = "beforeClose";
+ break;
+ case "buttons":
+ self._createButtons(value);
+ break;
+ case "closeText":
+ // ensure that we always pass a string
+ self.uiDialogTitlebarCloseText.text("" + value);
+ break;
+ case "dialogClass":
+ uiDialog
+ .removeClass(self.options.dialogClass)
+ .addClass(uiDialogClasses + value);
+ break;
+ case "disabled":
+ if (value) {
+ uiDialog.addClass('ui-dialog-disabled');
+ } else {
+ uiDialog.removeClass('ui-dialog-disabled');
+ }
+ break;
+ case "draggable":
+ var isDraggable = uiDialog.is( ":data(draggable)" );
+ if ( isDraggable && !value ) {
+ uiDialog.draggable( "destroy" );
+ }
+
+ if ( !isDraggable && value ) {
+ self._makeDraggable();
+ }
+ break;
+ case "position":
+ self._position(value);
+ break;
+ case "resizable":
+ // currently resizable, becoming non-resizable
+ var isResizable = uiDialog.is( ":data(resizable)" );
+ if (isResizable && !value) {
+ uiDialog.resizable('destroy');
+ }
+
+ // currently resizable, changing handles
+ if (isResizable && typeof value === 'string') {
+ uiDialog.resizable('option', 'handles', value);
+ }
+
+ // currently non-resizable, becoming resizable
+ if (!isResizable && value !== false) {
+ self._makeResizable(value);
+ }
+ break;
+ case "title":
+ // convert whatever was passed in o a string, for html() to not throw up
+ $(".ui-dialog-title", self.uiDialogTitlebar).html("" + (value || '&#160;'));
+ break;
+ }
+
+ $.Widget.prototype._setOption.apply(self, arguments);
+ },
+
+ _size: function() {
+ /* If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
+ * divs will both have width and height set, so we need to reset them
+ */
+ var options = this.options,
+ nonContentHeight,
+ minContentHeight,
+ isVisible = this.uiDialog.is( ":visible" );
+
+ // reset content sizing
+ this.element.show().css({
+ width: 'auto',
+ minHeight: 0,
+ height: 0
+ });
+
+ if (options.minWidth > options.width) {
+ options.width = options.minWidth;
+ }
+
+ // reset wrapper sizing
+ // determine the height of all the non-content elements
+ nonContentHeight = this.uiDialog.css({
+ height: 'auto',
+ width: options.width
+ })
+ .height();
+ minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
+
+ if ( options.height === "auto" ) {
+ // only needed for IE6 support
+ if ( $.support.minHeight ) {
+ this.element.css({
+ minHeight: minContentHeight,
+ height: "auto"
+ });
+ } else {
+ this.uiDialog.show();
+ var autoHeight = this.element.css( "height", "auto" ).height();
+ if ( !isVisible ) {
+ this.uiDialog.hide();
+ }
+ this.element.height( Math.max( autoHeight, minContentHeight ) );
+ }
+ } else {
+ this.element.height( Math.max( options.height - nonContentHeight, 0 ) );
+ }
+
+ if (this.uiDialog.is(':data(resizable)')) {
+ this.uiDialog.resizable('option', 'minHeight', this._minHeight());
+ }
+ }
+});
+
+$.extend($.ui.dialog, {
+ version: "1.8.18",
+
+ uuid: 0,
+ maxZ: 0,
+
+ getTitleId: function($el) {
+ var id = $el.attr('id');
+ if (!id) {
+ this.uuid += 1;
+ id = this.uuid;
+ }
+ return 'ui-dialog-title-' + id;
+ },
+
+ overlay: function(dialog) {
+ this.$el = $.ui.dialog.overlay.create(dialog);
+ }
+});
+
+$.extend($.ui.dialog.overlay, {
+ instances: [],
+ // reuse old instances due to IE memory leak with alpha transparency (see #5185)
+ oldInstances: [],
+ maxZ: 0,
+ events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','),
+ function(event) { return event + '.dialog-overlay'; }).join(' '),
+ create: function(dialog) {
+ if (this.instances.length === 0) {
+ // prevent use of anchors and inputs
+ // we use a setTimeout in case the overlay is created from an
+ // event that we're going to be cancelling (see #2804)
+ setTimeout(function() {
+ // handle $(el).dialog().dialog('close') (see #4065)
+ if ($.ui.dialog.overlay.instances.length) {
+ $(document).bind($.ui.dialog.overlay.events, function(event) {
+ // stop events if the z-index of the target is < the z-index of the overlay
+ // we cannot return true when we don't want to cancel the event (#3523)
+ if ($(event.target).zIndex() < $.ui.dialog.overlay.maxZ) {
+ return false;
+ }
+ });
+ }
+ }, 1);
+
+ // allow closing by pressing the escape key
+ $(document).bind('keydown.dialog-overlay', function(event) {
+ if (dialog.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
+ event.keyCode === $.ui.keyCode.ESCAPE) {
+
+ dialog.close(event);
+ event.preventDefault();
+ }
+ });
+
+ // handle window resize
+ $(window).bind('resize.dialog-overlay', $.ui.dialog.overlay.resize);
+ }
+
+ var $el = (this.oldInstances.pop() || $('<div></div>').addClass('ui-widget-overlay'))
+ .appendTo(document.body)
+ .css({
+ width: this.width(),
+ height: this.height()
+ });
+
+ if ($.fn.bgiframe) {
+ $el.bgiframe();
+ }
+
+ this.instances.push($el);
+ return $el;
+ },
+
+ destroy: function($el) {
+ var indexOf = $.inArray($el, this.instances);
+ if (indexOf != -1){
+ this.oldInstances.push(this.instances.splice(indexOf, 1)[0]);
+ }
+
+ if (this.instances.length === 0) {
+ $([document, window]).unbind('.dialog-overlay');
+ }
+
+ $el.remove();
+
+ // adjust the maxZ to allow other modal dialogs to continue to work (see #4309)
+ var maxZ = 0;
+ $.each(this.instances, function() {
+ maxZ = Math.max(maxZ, this.css('z-index'));
+ });
+ this.maxZ = maxZ;
+ },
+
+ height: function() {
+ var scrollHeight,
+ offsetHeight;
+ // handle IE 6
+ if ($.browser.msie && $.browser.version < 7) {
+ scrollHeight = Math.max(
+ document.documentElement.scrollHeight,
+ document.body.scrollHeight
+ );
+ offsetHeight = Math.max(
+ document.documentElement.offsetHeight,
+ document.body.offsetHeight
+ );
+
+ if (scrollHeight < offsetHeight) {
+ return $(window).height() + 'px';
+ } else {
+ return scrollHeight + 'px';
+ }
+ // handle "good" browsers
+ } else {
+ return $(document).height() + 'px';
+ }
+ },
+
+ width: function() {
+ var scrollWidth,
+ offsetWidth;
+ // handle IE
+ if ( $.browser.msie ) {
+ scrollWidth = Math.max(
+ document.documentElement.scrollWidth,
+ document.body.scrollWidth
+ );
+ offsetWidth = Math.max(
+ document.documentElement.offsetWidth,
+ document.body.offsetWidth
+ );
+
+ if (scrollWidth < offsetWidth) {
+ return $(window).width() + 'px';
+ } else {
+ return scrollWidth + 'px';
+ }
+ // handle "good" browsers
+ } else {
+ return $(document).width() + 'px';
+ }
+ },
+
+ resize: function() {
+ /* If the dialog is draggable and the user drags it past the
+ * right edge of the window, the document becomes wider so we
+ * need to stretch the overlay. If the user then drags the
+ * dialog back to the left, the document will become narrower,
+ * so we need to shrink the overlay to the appropriate size.
+ * This is handled by shrinking the overlay before setting it
+ * to the full document size.
+ */
+ var $overlays = $([]);
+ $.each($.ui.dialog.overlay.instances, function() {
+ $overlays = $overlays.add(this);
+ });
+
+ $overlays.css({
+ width: 0,
+ height: 0
+ }).css({
+ width: $.ui.dialog.overlay.width(),
+ height: $.ui.dialog.overlay.height()
+ });
+ }
+});
+
+$.extend($.ui.dialog.overlay.prototype, {
+ destroy: function() {
+ $.ui.dialog.overlay.destroy(this.$el);
+ }
+});
+
+}(jQuery));
+/*
+ * jQuery UI Position 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Position
+ */
+(function( $, undefined ) {
+
+$.ui = $.ui || {};
+
+var horizontalPositions = /left|center|right/,
+ verticalPositions = /top|center|bottom/,
+ center = "center",
+ support = {},
+ _position = $.fn.position,
+ _offset = $.fn.offset;
+
+$.fn.position = function( options ) {
+ if ( !options || !options.of ) {
+ return _position.apply( this, arguments );
+ }
+
+ // make a copy, we don't want to modify arguments
+ options = $.extend( {}, options );
+
+ var target = $( options.of ),
+ targetElem = target[0],
+ collision = ( options.collision || "flip" ).split( " " ),
+ offset = options.offset ? options.offset.split( " " ) : [ 0, 0 ],
+ targetWidth,
+ targetHeight,
+ basePosition;
+
+ if ( targetElem.nodeType === 9 ) {
+ targetWidth = target.width();
+ targetHeight = target.height();
+ basePosition = { top: 0, left: 0 };
+ // TODO: use $.isWindow() in 1.9
+ } else if ( targetElem.setTimeout ) {
+ targetWidth = target.width();
+ targetHeight = target.height();
+ basePosition = { top: target.scrollTop(), left: target.scrollLeft() };
+ } else if ( targetElem.preventDefault ) {
+ // force left top to allow flipping
+ options.at = "left top";
+ targetWidth = targetHeight = 0;
+ basePosition = { top: options.of.pageY, left: options.of.pageX };
+ } else {
+ targetWidth = target.outerWidth();
+ targetHeight = target.outerHeight();
+ basePosition = target.offset();
+ }
+
+ // force my and at to have valid horizontal and veritcal positions
+ // if a value is missing or invalid, it will be converted to center
+ $.each( [ "my", "at" ], function() {
+ var pos = ( options[this] || "" ).split( " " );
+ if ( pos.length === 1) {
+ pos = horizontalPositions.test( pos[0] ) ?
+ pos.concat( [center] ) :
+ verticalPositions.test( pos[0] ) ?
+ [ center ].concat( pos ) :
+ [ center, center ];
+ }
+ pos[ 0 ] = horizontalPositions.test( pos[0] ) ? pos[ 0 ] : center;
+ pos[ 1 ] = verticalPositions.test( pos[1] ) ? pos[ 1 ] : center;
+ options[ this ] = pos;
+ });
+
+ // normalize collision option
+ if ( collision.length === 1 ) {
+ collision[ 1 ] = collision[ 0 ];
+ }
+
+ // normalize offset option
+ offset[ 0 ] = parseInt( offset[0], 10 ) || 0;
+ if ( offset.length === 1 ) {
+ offset[ 1 ] = offset[ 0 ];
+ }
+ offset[ 1 ] = parseInt( offset[1], 10 ) || 0;
+
+ if ( options.at[0] === "right" ) {
+ basePosition.left += targetWidth;
+ } else if ( options.at[0] === center ) {
+ basePosition.left += targetWidth / 2;
+ }
+
+ if ( options.at[1] === "bottom" ) {
+ basePosition.top += targetHeight;
+ } else if ( options.at[1] === center ) {
+ basePosition.top += targetHeight / 2;
+ }
+
+ basePosition.left += offset[ 0 ];
+ basePosition.top += offset[ 1 ];
+
+ return this.each(function() {
+ var elem = $( this ),
+ elemWidth = elem.outerWidth(),
+ elemHeight = elem.outerHeight(),
+ marginLeft = parseInt( $.curCSS( this, "marginLeft", true ) ) || 0,
+ marginTop = parseInt( $.curCSS( this, "marginTop", true ) ) || 0,
+ collisionWidth = elemWidth + marginLeft +
+ ( parseInt( $.curCSS( this, "marginRight", true ) ) || 0 ),
+ collisionHeight = elemHeight + marginTop +
+ ( parseInt( $.curCSS( this, "marginBottom", true ) ) || 0 ),
+ position = $.extend( {}, basePosition ),
+ collisionPosition;
+
+ if ( options.my[0] === "right" ) {
+ position.left -= elemWidth;
+ } else if ( options.my[0] === center ) {
+ position.left -= elemWidth / 2;
+ }
+
+ if ( options.my[1] === "bottom" ) {
+ position.top -= elemHeight;
+ } else if ( options.my[1] === center ) {
+ position.top -= elemHeight / 2;
+ }
+
+ // prevent fractions if jQuery version doesn't support them (see #5280)
+ if ( !support.fractions ) {
+ position.left = Math.round( position.left );
+ position.top = Math.round( position.top );
+ }
+
+ collisionPosition = {
+ left: position.left - marginLeft,
+ top: position.top - marginTop
+ };
+
+ $.each( [ "left", "top" ], function( i, dir ) {
+ if ( $.ui.position[ collision[i] ] ) {
+ $.ui.position[ collision[i] ][ dir ]( position, {
+ targetWidth: targetWidth,
+ targetHeight: targetHeight,
+ elemWidth: elemWidth,
+ elemHeight: elemHeight,
+ collisionPosition: collisionPosition,
+ collisionWidth: collisionWidth,
+ collisionHeight: collisionHeight,
+ offset: offset,
+ my: options.my,
+ at: options.at
+ });
+ }
+ });
+
+ if ( $.fn.bgiframe ) {
+ elem.bgiframe();
+ }
+ elem.offset( $.extend( position, { using: options.using } ) );
+ });
+};
+
+$.ui.position = {
+ fit: {
+ left: function( position, data ) {
+ var win = $( window ),
+ over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft();
+ position.left = over > 0 ? position.left - over : Math.max( position.left - data.collisionPosition.left, position.left );
+ },
+ top: function( position, data ) {
+ var win = $( window ),
+ over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop();
+ position.top = over > 0 ? position.top - over : Math.max( position.top - data.collisionPosition.top, position.top );
+ }
+ },
+
+ flip: {
+ left: function( position, data ) {
+ if ( data.at[0] === center ) {
+ return;
+ }
+ var win = $( window ),
+ over = data.collisionPosition.left + data.collisionWidth - win.width() - win.scrollLeft(),
+ myOffset = data.my[ 0 ] === "left" ?
+ -data.elemWidth :
+ data.my[ 0 ] === "right" ?
+ data.elemWidth :
+ 0,
+ atOffset = data.at[ 0 ] === "left" ?
+ data.targetWidth :
+ -data.targetWidth,
+ offset = -2 * data.offset[ 0 ];
+ position.left += data.collisionPosition.left < 0 ?
+ myOffset + atOffset + offset :
+ over > 0 ?
+ myOffset + atOffset + offset :
+ 0;
+ },
+ top: function( position, data ) {
+ if ( data.at[1] === center ) {
+ return;
+ }
+ var win = $( window ),
+ over = data.collisionPosition.top + data.collisionHeight - win.height() - win.scrollTop(),
+ myOffset = data.my[ 1 ] === "top" ?
+ -data.elemHeight :
+ data.my[ 1 ] === "bottom" ?
+ data.elemHeight :
+ 0,
+ atOffset = data.at[ 1 ] === "top" ?
+ data.targetHeight :
+ -data.targetHeight,
+ offset = -2 * data.offset[ 1 ];
+ position.top += data.collisionPosition.top < 0 ?
+ myOffset + atOffset + offset :
+ over > 0 ?
+ myOffset + atOffset + offset :
+ 0;
+ }
+ }
+};
+
+// offset setter from jQuery 1.4
+if ( !$.offset.setOffset ) {
+ $.offset.setOffset = function( elem, options ) {
+ // set position first, in-case top/left are set even on static elem
+ if ( /static/.test( $.curCSS( elem, "position" ) ) ) {
+ elem.style.position = "relative";
+ }
+ var curElem = $( elem ),
+ curOffset = curElem.offset(),
+ curTop = parseInt( $.curCSS( elem, "top", true ), 10 ) || 0,
+ curLeft = parseInt( $.curCSS( elem, "left", true ), 10) || 0,
+ props = {
+ top: (options.top - curOffset.top) + curTop,
+ left: (options.left - curOffset.left) + curLeft
+ };
+
+ if ( 'using' in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ };
+
+ $.fn.offset = function( options ) {
+ var elem = this[ 0 ];
+ if ( !elem || !elem.ownerDocument ) { return null; }
+ if ( options ) {
+ return this.each(function() {
+ $.offset.setOffset( this, options );
+ });
+ }
+ return _offset.call( this );
+ };
+}
+
+// fraction support test (older versions of jQuery don't support fractions)
+(function () {
+ var body = document.getElementsByTagName( "body" )[ 0 ],
+ div = document.createElement( "div" ),
+ testElement, testElementParent, testElementStyle, offset, offsetTotal;
+
+ //Create a "fake body" for testing based on method used in jQuery.support
+ testElement = document.createElement( body ? "div" : "body" );
+ testElementStyle = {
+ visibility: "hidden",
+ width: 0,
+ height: 0,
+ border: 0,
+ margin: 0,
+ background: "none"
+ };
+ if ( body ) {
+ $.extend( testElementStyle, {
+ position: "absolute",
+ left: "-1000px",
+ top: "-1000px"
+ });
+ }
+ for ( var i in testElementStyle ) {
+ testElement.style[ i ] = testElementStyle[ i ];
+ }
+ testElement.appendChild( div );
+ testElementParent = body || document.documentElement;
+ testElementParent.insertBefore( testElement, testElementParent.firstChild );
+
+ div.style.cssText = "position: absolute; left: 10.7432222px; top: 10.432325px; height: 30px; width: 201px;";
+
+ offset = $( div ).offset( function( _, offset ) {
+ return offset;
+ }).offset();
+
+ testElement.innerHTML = "";
+ testElementParent.removeChild( testElement );
+
+ offsetTotal = offset.top + offset.left + ( body ? 2000 : 0 );
+ support.fractions = offsetTotal > 21 && offsetTotal < 22;
+})();
+
+}( jQuery ));
+/*
+ * jQuery UI Progressbar 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+$.widget( "ui.progressbar", {
+ options: {
+ value: 0,
+ max: 100
+ },
+
+ min: 0,
+
+ _create: function() {
+ this.element
+ .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
+ .attr({
+ role: "progressbar",
+ "aria-valuemin": this.min,
+ "aria-valuemax": this.options.max,
+ "aria-valuenow": this._value()
+ });
+
+ this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" )
+ .appendTo( this.element );
+
+ this.oldValue = this._value();
+ this._refreshValue();
+ },
+
+ destroy: function() {
+ this.element
+ .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
+ .removeAttr( "role" )
+ .removeAttr( "aria-valuemin" )
+ .removeAttr( "aria-valuemax" )
+ .removeAttr( "aria-valuenow" );
+
+ this.valueDiv.remove();
+
+ $.Widget.prototype.destroy.apply( this, arguments );
+ },
+
+ value: function( newValue ) {
+ if ( newValue === undefined ) {
+ return this._value();
+ }
+
+ this._setOption( "value", newValue );
+ return this;
+ },
+
+ _setOption: function( key, value ) {
+ if ( key === "value" ) {
+ this.options.value = value;
+ this._refreshValue();
+ if ( this._value() === this.options.max ) {
+ this._trigger( "complete" );
+ }
+ }
+
+ $.Widget.prototype._setOption.apply( this, arguments );
+ },
+
+ _value: function() {
+ var val = this.options.value;
+ // normalize invalid value
+ if ( typeof val !== "number" ) {
+ val = 0;
+ }
+ return Math.min( this.options.max, Math.max( this.min, val ) );
+ },
+
+ _percentage: function() {
+ return 100 * this._value() / this.options.max;
+ },
+
+ _refreshValue: function() {
+ var value = this.value();
+ var percentage = this._percentage();
+
+ if ( this.oldValue !== value ) {
+ this.oldValue = value;
+ this._trigger( "change" );
+ }
+
+ this.valueDiv
+ .toggle( value > this.min )
+ .toggleClass( "ui-corner-right", value === this.options.max )
+ .width( percentage.toFixed(0) + "%" );
+ this.element.attr( "aria-valuenow", value );
+ }
+});
+
+$.extend( $.ui.progressbar, {
+ version: "1.8.18"
+});
+
+})( jQuery );
+/*
+ * jQuery UI Slider 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.mouse.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+// number of pages in a slider
+// (how many times can you page up/down to go through the whole range)
+var numPages = 5;
+
+$.widget( "ui.slider", $.ui.mouse, {
+
+ widgetEventPrefix: "slide",
+
+ options: {
+ animate: false,
+ distance: 0,
+ max: 100,
+ min: 0,
+ orientation: "horizontal",
+ range: false,
+ step: 1,
+ value: 0,
+ values: null
+ },
+
+ _create: function() {
+ var self = this,
+ o = this.options,
+ existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
+ handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
+ handleCount = ( o.values && o.values.length ) || 1,
+ handles = [];
+
+ this._keySliding = false;
+ this._mouseSliding = false;
+ this._animateOff = true;
+ this._handleIndex = null;
+ this._detectOrientation();
+ this._mouseInit();
+
+ this.element
+ .addClass( "ui-slider" +
+ " ui-slider-" + this.orientation +
+ " ui-widget" +
+ " ui-widget-content" +
+ " ui-corner-all" +
+ ( o.disabled ? " ui-slider-disabled ui-disabled" : "" ) );
+
+ this.range = $([]);
+
+ if ( o.range ) {
+ if ( o.range === true ) {
+ if ( !o.values ) {
+ o.values = [ this._valueMin(), this._valueMin() ];
+ }
+ if ( o.values.length && o.values.length !== 2 ) {
+ o.values = [ o.values[0], o.values[0] ];
+ }
+ }
+
+ this.range = $( "<div></div>" )
+ .appendTo( this.element )
+ .addClass( "ui-slider-range" +
+ // note: this isn't the most fittingly semantic framework class for this element,
+ // but worked best visually with a variety of themes
+ " ui-widget-header" +
+ ( ( o.range === "min" || o.range === "max" ) ? " ui-slider-range-" + o.range : "" ) );
+ }
+
+ for ( var i = existingHandles.length; i < handleCount; i += 1 ) {
+ handles.push( handle );
+ }
+
+ this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( self.element ) );
+
+ this.handle = this.handles.eq( 0 );
+
+ this.handles.add( this.range ).filter( "a" )
+ .click(function( event ) {
+ event.preventDefault();
+ })
+ .hover(function() {
+ if ( !o.disabled ) {
+ $( this ).addClass( "ui-state-hover" );
+ }
+ }, function() {
+ $( this ).removeClass( "ui-state-hover" );
+ })
+ .focus(function() {
+ if ( !o.disabled ) {
+ $( ".ui-slider .ui-state-focus" ).removeClass( "ui-state-focus" );
+ $( this ).addClass( "ui-state-focus" );
+ } else {
+ $( this ).blur();
+ }
+ })
+ .blur(function() {
+ $( this ).removeClass( "ui-state-focus" );
+ });
+
+ this.handles.each(function( i ) {
+ $( this ).data( "index.ui-slider-handle", i );
+ });
+
+ this.handles
+ .keydown(function( event ) {
+ var index = $( this ).data( "index.ui-slider-handle" ),
+ allowed,
+ curVal,
+ newVal,
+ step;
+
+ if ( self.options.disabled ) {
+ return;
+ }
+
+ switch ( event.keyCode ) {
+ case $.ui.keyCode.HOME:
+ case $.ui.keyCode.END:
+ case $.ui.keyCode.PAGE_UP:
+ case $.ui.keyCode.PAGE_DOWN:
+ case $.ui.keyCode.UP:
+ case $.ui.keyCode.RIGHT:
+ case $.ui.keyCode.DOWN:
+ case $.ui.keyCode.LEFT:
+ event.preventDefault();
+ if ( !self._keySliding ) {
+ self._keySliding = true;
+ $( this ).addClass( "ui-state-active" );
+ allowed = self._start( event, index );
+ if ( allowed === false ) {
+ return;
+ }
+ }
+ break;
+ }
+
+ step = self.options.step;
+ if ( self.options.values && self.options.values.length ) {
+ curVal = newVal = self.values( index );
+ } else {
+ curVal = newVal = self.value();
+ }
+
+ switch ( event.keyCode ) {
+ case $.ui.keyCode.HOME:
+ newVal = self._valueMin();
+ break;
+ case $.ui.keyCode.END:
+ newVal = self._valueMax();
+ break;
+ case $.ui.keyCode.PAGE_UP:
+ newVal = self._trimAlignValue( curVal + ( (self._valueMax() - self._valueMin()) / numPages ) );
+ break;
+ case $.ui.keyCode.PAGE_DOWN:
+ newVal = self._trimAlignValue( curVal - ( (self._valueMax() - self._valueMin()) / numPages ) );
+ break;
+ case $.ui.keyCode.UP:
+ case $.ui.keyCode.RIGHT:
+ if ( curVal === self._valueMax() ) {
+ return;
+ }
+ newVal = self._trimAlignValue( curVal + step );
+ break;
+ case $.ui.keyCode.DOWN:
+ case $.ui.keyCode.LEFT:
+ if ( curVal === self._valueMin() ) {
+ return;
+ }
+ newVal = self._trimAlignValue( curVal - step );
+ break;
+ }
+
+ self._slide( event, index, newVal );
+ })
+ .keyup(function( event ) {
+ var index = $( this ).data( "index.ui-slider-handle" );
+
+ if ( self._keySliding ) {
+ self._keySliding = false;
+ self._stop( event, index );
+ self._change( event, index );
+ $( this ).removeClass( "ui-state-active" );
+ }
+
+ });
+
+ this._refreshValue();
+
+ this._animateOff = false;
+ },
+
+ destroy: function() {
+ this.handles.remove();
+ this.range.remove();
+
+ this.element
+ .removeClass( "ui-slider" +
+ " ui-slider-horizontal" +
+ " ui-slider-vertical" +
+ " ui-slider-disabled" +
+ " ui-widget" +
+ " ui-widget-content" +
+ " ui-corner-all" )
+ .removeData( "slider" )
+ .unbind( ".slider" );
+
+ this._mouseDestroy();
+
+ return this;
+ },
+
+ _mouseCapture: function( event ) {
+ var o = this.options,
+ position,
+ normValue,
+ distance,
+ closestHandle,
+ self,
+ index,
+ allowed,
+ offset,
+ mouseOverHandle;
+
+ if ( o.disabled ) {
+ return false;
+ }
+
+ this.elementSize = {
+ width: this.element.outerWidth(),
+ height: this.element.outerHeight()
+ };
+ this.elementOffset = this.element.offset();
+
+ position = { x: event.pageX, y: event.pageY };
+ normValue = this._normValueFromMouse( position );
+ distance = this._valueMax() - this._valueMin() + 1;
+ self = this;
+ this.handles.each(function( i ) {
+ var thisDistance = Math.abs( normValue - self.values(i) );
+ if ( distance > thisDistance ) {
+ distance = thisDistance;
+ closestHandle = $( this );
+ index = i;
+ }
+ });
+
+ // workaround for bug #3736 (if both handles of a range are at 0,
+ // the first is always used as the one with least distance,
+ // and moving it is obviously prevented by preventing negative ranges)
+ if( o.range === true && this.values(1) === o.min ) {
+ index += 1;
+ closestHandle = $( this.handles[index] );
+ }
+
+ allowed = this._start( event, index );
+ if ( allowed === false ) {
+ return false;
+ }
+ this._mouseSliding = true;
+
+ self._handleIndex = index;
+
+ closestHandle
+ .addClass( "ui-state-active" )
+ .focus();
+
+ offset = closestHandle.offset();
+ mouseOverHandle = !$( event.target ).parents().andSelf().is( ".ui-slider-handle" );
+ this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
+ left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
+ top: event.pageY - offset.top -
+ ( closestHandle.height() / 2 ) -
+ ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
+ ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
+ ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
+ };
+
+ if ( !this.handles.hasClass( "ui-state-hover" ) ) {
+ this._slide( event, index, normValue );
+ }
+ this._animateOff = true;
+ return true;
+ },
+
+ _mouseStart: function( event ) {
+ return true;
+ },
+
+ _mouseDrag: function( event ) {
+ var position = { x: event.pageX, y: event.pageY },
+ normValue = this._normValueFromMouse( position );
+
+ this._slide( event, this._handleIndex, normValue );
+
+ return false;
+ },
+
+ _mouseStop: function( event ) {
+ this.handles.removeClass( "ui-state-active" );
+ this._mouseSliding = false;
+
+ this._stop( event, this._handleIndex );
+ this._change( event, this._handleIndex );
+
+ this._handleIndex = null;
+ this._clickOffset = null;
+ this._animateOff = false;
+
+ return false;
+ },
+
+ _detectOrientation: function() {
+ this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
+ },
+
+ _normValueFromMouse: function( position ) {
+ var pixelTotal,
+ pixelMouse,
+ percentMouse,
+ valueTotal,
+ valueMouse;
+
+ if ( this.orientation === "horizontal" ) {
+ pixelTotal = this.elementSize.width;
+ pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
+ } else {
+ pixelTotal = this.elementSize.height;
+ pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
+ }
+
+ percentMouse = ( pixelMouse / pixelTotal );
+ if ( percentMouse > 1 ) {
+ percentMouse = 1;
+ }
+ if ( percentMouse < 0 ) {
+ percentMouse = 0;
+ }
+ if ( this.orientation === "vertical" ) {
+ percentMouse = 1 - percentMouse;
+ }
+
+ valueTotal = this._valueMax() - this._valueMin();
+ valueMouse = this._valueMin() + percentMouse * valueTotal;
+
+ return this._trimAlignValue( valueMouse );
+ },
+
+ _start: function( event, index ) {
+ var uiHash = {
+ handle: this.handles[ index ],
+ value: this.value()
+ };
+ if ( this.options.values && this.options.values.length ) {
+ uiHash.value = this.values( index );
+ uiHash.values = this.values();
+ }
+ return this._trigger( "start", event, uiHash );
+ },
+
+ _slide: function( event, index, newVal ) {
+ var otherVal,
+ newValues,
+ allowed;
+
+ if ( this.options.values && this.options.values.length ) {
+ otherVal = this.values( index ? 0 : 1 );
+
+ if ( ( this.options.values.length === 2 && this.options.range === true ) &&
+ ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
+ ) {
+ newVal = otherVal;
+ }
+
+ if ( newVal !== this.values( index ) ) {
+ newValues = this.values();
+ newValues[ index ] = newVal;
+ // A slide can be canceled by returning false from the slide callback
+ allowed = this._trigger( "slide", event, {
+ handle: this.handles[ index ],
+ value: newVal,
+ values: newValues
+ } );
+ otherVal = this.values( index ? 0 : 1 );
+ if ( allowed !== false ) {
+ this.values( index, newVal, true );
+ }
+ }
+ } else {
+ if ( newVal !== this.value() ) {
+ // A slide can be canceled by returning false from the slide callback
+ allowed = this._trigger( "slide", event, {
+ handle: this.handles[ index ],
+ value: newVal
+ } );
+ if ( allowed !== false ) {
+ this.value( newVal );
+ }
+ }
+ }
+ },
+
+ _stop: function( event, index ) {
+ var uiHash = {
+ handle: this.handles[ index ],
+ value: this.value()
+ };
+ if ( this.options.values && this.options.values.length ) {
+ uiHash.value = this.values( index );
+ uiHash.values = this.values();
+ }
+
+ this._trigger( "stop", event, uiHash );
+ },
+
+ _change: function( event, index ) {
+ if ( !this._keySliding && !this._mouseSliding ) {
+ var uiHash = {
+ handle: this.handles[ index ],
+ value: this.value()
+ };
+ if ( this.options.values && this.options.values.length ) {
+ uiHash.value = this.values( index );
+ uiHash.values = this.values();
+ }
+
+ this._trigger( "change", event, uiHash );
+ }
+ },
+
+ value: function( newValue ) {
+ if ( arguments.length ) {
+ this.options.value = this._trimAlignValue( newValue );
+ this._refreshValue();
+ this._change( null, 0 );
+ return;
+ }
+
+ return this._value();
+ },
+
+ values: function( index, newValue ) {
+ var vals,
+ newValues,
+ i;
+
+ if ( arguments.length > 1 ) {
+ this.options.values[ index ] = this._trimAlignValue( newValue );
+ this._refreshValue();
+ this._change( null, index );
+ return;
+ }
+
+ if ( arguments.length ) {
+ if ( $.isArray( arguments[ 0 ] ) ) {
+ vals = this.options.values;
+ newValues = arguments[ 0 ];
+ for ( i = 0; i < vals.length; i += 1 ) {
+ vals[ i ] = this._trimAlignValue( newValues[ i ] );
+ this._change( null, i );
+ }
+ this._refreshValue();
+ } else {
+ if ( this.options.values && this.options.values.length ) {
+ return this._values( index );
+ } else {
+ return this.value();
+ }
+ }
+ } else {
+ return this._values();
+ }
+ },
+
+ _setOption: function( key, value ) {
+ var i,
+ valsLength = 0;
+
+ if ( $.isArray( this.options.values ) ) {
+ valsLength = this.options.values.length;
+ }
+
+ $.Widget.prototype._setOption.apply( this, arguments );
+
+ switch ( key ) {
+ case "disabled":
+ if ( value ) {
+ this.handles.filter( ".ui-state-focus" ).blur();
+ this.handles.removeClass( "ui-state-hover" );
+ this.handles.propAttr( "disabled", true );
+ this.element.addClass( "ui-disabled" );
+ } else {
+ this.handles.propAttr( "disabled", false );
+ this.element.removeClass( "ui-disabled" );
+ }
+ break;
+ case "orientation":
+ this._detectOrientation();
+ this.element
+ .removeClass( "ui-slider-horizontal ui-slider-vertical" )
+ .addClass( "ui-slider-" + this.orientation );
+ this._refreshValue();
+ break;
+ case "value":
+ this._animateOff = true;
+ this._refreshValue();
+ this._change( null, 0 );
+ this._animateOff = false;
+ break;
+ case "values":
+ this._animateOff = true;
+ this._refreshValue();
+ for ( i = 0; i < valsLength; i += 1 ) {
+ this._change( null, i );
+ }
+ this._animateOff = false;
+ break;
+ }
+ },
+
+ //internal value getter
+ // _value() returns value trimmed by min and max, aligned by step
+ _value: function() {
+ var val = this.options.value;
+ val = this._trimAlignValue( val );
+
+ return val;
+ },
+
+ //internal values getter
+ // _values() returns array of values trimmed by min and max, aligned by step
+ // _values( index ) returns single value trimmed by min and max, aligned by step
+ _values: function( index ) {
+ var val,
+ vals,
+ i;
+
+ if ( arguments.length ) {
+ val = this.options.values[ index ];
+ val = this._trimAlignValue( val );
+
+ return val;
+ } else {
+ // .slice() creates a copy of the array
+ // this copy gets trimmed by min and max and then returned
+ vals = this.options.values.slice();
+ for ( i = 0; i < vals.length; i+= 1) {
+ vals[ i ] = this._trimAlignValue( vals[ i ] );
+ }
+
+ return vals;
+ }
+ },
+
+ // returns the step-aligned value that val is closest to, between (inclusive) min and max
+ _trimAlignValue: function( val ) {
+ if ( val <= this._valueMin() ) {
+ return this._valueMin();
+ }
+ if ( val >= this._valueMax() ) {
+ return this._valueMax();
+ }
+ var step = ( this.options.step > 0 ) ? this.options.step : 1,
+ valModStep = (val - this._valueMin()) % step,
+ alignValue = val - valModStep;
+
+ if ( Math.abs(valModStep) * 2 >= step ) {
+ alignValue += ( valModStep > 0 ) ? step : ( -step );
+ }
+
+ // Since JavaScript has problems with large floats, round
+ // the final value to 5 digits after the decimal point (see #4124)
+ return parseFloat( alignValue.toFixed(5) );
+ },
+
+ _valueMin: function() {
+ return this.options.min;
+ },
+
+ _valueMax: function() {
+ return this.options.max;
+ },
+
+ _refreshValue: function() {
+ var oRange = this.options.range,
+ o = this.options,
+ self = this,
+ animate = ( !this._animateOff ) ? o.animate : false,
+ valPercent,
+ _set = {},
+ lastValPercent,
+ value,
+ valueMin,
+ valueMax;
+
+ if ( this.options.values && this.options.values.length ) {
+ this.handles.each(function( i, j ) {
+ valPercent = ( self.values(i) - self._valueMin() ) / ( self._valueMax() - self._valueMin() ) * 100;
+ _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
+ $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
+ if ( self.options.range === true ) {
+ if ( self.orientation === "horizontal" ) {
+ if ( i === 0 ) {
+ self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
+ }
+ if ( i === 1 ) {
+ self.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
+ }
+ } else {
+ if ( i === 0 ) {
+ self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
+ }
+ if ( i === 1 ) {
+ self.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
+ }
+ }
+ }
+ lastValPercent = valPercent;
+ });
+ } else {
+ value = this.value();
+ valueMin = this._valueMin();
+ valueMax = this._valueMax();
+ valPercent = ( valueMax !== valueMin ) ?
+ ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
+ 0;
+ _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
+ this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
+
+ if ( oRange === "min" && this.orientation === "horizontal" ) {
+ this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
+ }
+ if ( oRange === "max" && this.orientation === "horizontal" ) {
+ this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
+ }
+ if ( oRange === "min" && this.orientation === "vertical" ) {
+ this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
+ }
+ if ( oRange === "max" && this.orientation === "vertical" ) {
+ this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
+ }
+ }
+ }
+
+});
+
+$.extend( $.ui.slider, {
+ version: "1.8.18"
+});
+
+}(jQuery));
+/*
+ * jQuery UI Tabs 1.8.18
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs
+ *
+ * Depends:
+ * jquery.ui.core.js
+ * jquery.ui.widget.js
+ */
+(function( $, undefined ) {
+
+var tabId = 0,
+ listId = 0;
+
+function getNextTabId() {
+ return ++tabId;
+}
+
+function getNextListId() {
+ return ++listId;
+}
+
+$.widget( "ui.tabs", {
+ options: {
+ add: null,
+ ajaxOptions: null,
+ cache: false,
+ cookie: null, // e.g. { expires: 7, path: '/', domain: 'jquery.com', secure: true }
+ collapsible: false,
+ disable: null,
+ disabled: [],
+ enable: null,
+ event: "click",
+ fx: null, // e.g. { height: 'toggle', opacity: 'toggle', duration: 200 }
+ idPrefix: "ui-tabs-",
+ load: null,
+ panelTemplate: "<div></div>",
+ remove: null,
+ select: null,
+ show: null,
+ spinner: "<em>Loading&#8230;</em>",
+ tabTemplate: "<li><a href='#{href}'><span>#{label}</span></a></li>"
+ },
+
+ _create: function() {
+ this._tabify( true );
+ },
+
+ _setOption: function( key, value ) {
+ if ( key == "selected" ) {
+ if (this.options.collapsible && value == this.options.selected ) {
+ return;
+ }
+ this.select( value );
+ } else {
+ this.options[ key ] = value;
+ this._tabify();
+ }
+ },
+
+ _tabId: function( a ) {
+ return a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF-]/g, "" ) ||
+ this.options.idPrefix + getNextTabId();
+ },
+
+ _sanitizeSelector: function( hash ) {
+ // we need this because an id may contain a ":"
+ return hash.replace( /:/g, "\\:" );
+ },
+
+ _cookie: function() {
+ var cookie = this.cookie ||
+ ( this.cookie = this.options.cookie.name || "ui-tabs-" + getNextListId() );
+ return $.cookie.apply( null, [ cookie ].concat( $.makeArray( arguments ) ) );
+ },
+
+ _ui: function( tab, panel ) {
+ return {
+ tab: tab,
+ panel: panel,
+ index: this.anchors.index( tab )
+ };
+ },
+
+ _cleanup: function() {
+ // restore all former loading tabs labels
+ this.lis.filter( ".ui-state-processing" )
+ .removeClass( "ui-state-processing" )
+ .find( "span:data(label.tabs)" )
+ .each(function() {
+ var el = $( this );
+ el.html( el.data( "label.tabs" ) ).removeData( "label.tabs" );
+ });
+ },
+
+ _tabify: function( init ) {
+ var self = this,
+ o = this.options,
+ fragmentId = /^#.+/; // Safari 2 reports '#' for an empty hash
+
+ this.list = this.element.find( "ol,ul" ).eq( 0 );
+ this.lis = $( " > li:has(a[href])", this.list );
+ this.anchors = this.lis.map(function() {
+ return $( "a", this )[ 0 ];
+ });
+ this.panels = $( [] );
+
+ this.anchors.each(function( i, a ) {
+ var href = $( a ).attr( "href" );
+ // For dynamically created HTML that contains a hash as href IE < 8 expands
+ // such href to the full page url with hash and then misinterprets tab as ajax.
+ // Same consideration applies for an added tab with a fragment identifier
+ // since a[href=#fragment-identifier] does unexpectedly not match.
+ // Thus normalize href attribute...
+ var hrefBase = href.split( "#" )[ 0 ],
+ baseEl;
+ if ( hrefBase && ( hrefBase === location.toString().split( "#" )[ 0 ] ||
+ ( baseEl = $( "base" )[ 0 ]) && hrefBase === baseEl.href ) ) {
+ href = a.hash;
+ a.href = href;
+ }
+
+ // inline tab
+ if ( fragmentId.test( href ) ) {
+ self.panels = self.panels.add( self.element.find( self._sanitizeSelector( href ) ) );
+ // remote tab
+ // prevent loading the page itself if href is just "#"
+ } else if ( href && href !== "#" ) {
+ // required for restore on destroy
+ $.data( a, "href.tabs", href );
+
+ // TODO until #3808 is fixed strip fragment identifier from url
+ // (IE fails to load from such url)
+ $.data( a, "load.tabs", href.replace( /#.*$/, "" ) );
+
+ var id = self._tabId( a );
+ a.href = "#" + id;
+ var $panel = self.element.find( "#" + id );
+ if ( !$panel.length ) {
+ $panel = $( o.panelTemplate )
+ .attr( "id", id )
+ .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
+ .insertAfter( self.panels[ i - 1 ] || self.list );
+ $panel.data( "destroy.tabs", true );
+ }
+ self.panels = self.panels.add( $panel );
+ // invalid tab href
+ } else {
+ o.disabled.push( i );
+ }
+ });
+
+ // initialization from scratch
+ if ( init ) {
+ // attach necessary classes for styling
+ this.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" );
+ this.list.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
+ this.lis.addClass( "ui-state-default ui-corner-top" );
+ this.panels.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" );
+
+ // Selected tab
+ // use "selected" option or try to retrieve:
+ // 1. from fragment identifier in url
+ // 2. from cookie
+ // 3. from selected class attribute on <li>
+ if ( o.selected === undefined ) {
+ if ( location.hash ) {
+ this.anchors.each(function( i, a ) {
+ if ( a.hash == location.hash ) {
+ o.selected = i;
+ return false;
+ }
+ });
+ }
+ if ( typeof o.selected !== "number" && o.cookie ) {
+ o.selected = parseInt( self._cookie(), 10 );
+ }
+ if ( typeof o.selected !== "number" && this.lis.filter( ".ui-tabs-selected" ).length ) {
+ o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) );
+ }
+ o.selected = o.selected || ( this.lis.length ? 0 : -1 );
+ } else if ( o.selected === null ) { // usage of null is deprecated, TODO remove in next release
+ o.selected = -1;
+ }
+
+ // sanity check - default to first tab...
+ o.selected = ( ( o.selected >= 0 && this.anchors[ o.selected ] ) || o.selected < 0 )
+ ? o.selected
+ : 0;
+
+ // Take disabling tabs via class attribute from HTML
+ // into account and update option properly.
+ // A selected tab cannot become disabled.
+ o.disabled = $.unique( o.disabled.concat(
+ $.map( this.lis.filter( ".ui-state-disabled" ), function( n, i ) {
+ return self.lis.index( n );
+ })
+ ) ).sort();
+
+ if ( $.inArray( o.selected, o.disabled ) != -1 ) {
+ o.disabled.splice( $.inArray( o.selected, o.disabled ), 1 );
+ }
+
+ // highlight selected tab
+ this.panels.addClass( "ui-tabs-hide" );
+ this.lis.removeClass( "ui-tabs-selected ui-state-active" );
+ // check for length avoids error when initializing empty list
+ if ( o.selected >= 0 && this.anchors.length ) {
+ self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) ).removeClass( "ui-tabs-hide" );
+ this.lis.eq( o.selected ).addClass( "ui-tabs-selected ui-state-active" );
+
+ // seems to be expected behavior that the show callback is fired
+ self.element.queue( "tabs", function() {
+ self._trigger( "show", null,
+ self._ui( self.anchors[ o.selected ], self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )[ 0 ] ) );
+ });
+
+ this.load( o.selected );
+ }
+
+ // clean up to avoid memory leaks in certain versions of IE 6
+ // TODO: namespace this event
+ $( window ).bind( "unload", function() {
+ self.lis.add( self.anchors ).unbind( ".tabs" );
+ self.lis = self.anchors = self.panels = null;
+ });
+ // update selected after add/remove
+ } else {
+ o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) );
+ }
+
+ // update collapsible
+ // TODO: use .toggleClass()
+ this.element[ o.collapsible ? "addClass" : "removeClass" ]( "ui-tabs-collapsible" );
+
+ // set or update cookie after init and add/remove respectively
+ if ( o.cookie ) {
+ this._cookie( o.selected, o.cookie );
+ }
+
+ // disable tabs
+ for ( var i = 0, li; ( li = this.lis[ i ] ); i++ ) {
+ $( li )[ $.inArray( i, o.disabled ) != -1 &&
+ // TODO: use .toggleClass()
+ !$( li ).hasClass( "ui-tabs-selected" ) ? "addClass" : "removeClass" ]( "ui-state-disabled" );
+ }
+
+ // reset cache if switching from cached to not cached
+ if ( o.cache === false ) {
+ this.anchors.removeData( "cache.tabs" );
+ }
+
+ // remove all handlers before, tabify may run on existing tabs after add or option change
+ this.lis.add( this.anchors ).unbind( ".tabs" );
+
+ if ( o.event !== "mouseover" ) {
+ var addState = function( state, el ) {
+ if ( el.is( ":not(.ui-state-disabled)" ) ) {
+ el.addClass( "ui-state-" + state );
+ }
+ };
+ var removeState = function( state, el ) {
+ el.removeClass( "ui-state-" + state );
+ };
+ this.lis.bind( "mouseover.tabs" , function() {
+ addState( "hover", $( this ) );
+ });
+ this.lis.bind( "mouseout.tabs", function() {
+ removeState( "hover", $( this ) );
+ });
+ this.anchors.bind( "focus.tabs", function() {
+ addState( "focus", $( this ).closest( "li" ) );
+ });
+ this.anchors.bind( "blur.tabs", function() {
+ removeState( "focus", $( this ).closest( "li" ) );
+ });
+ }
+
+ // set up animations
+ var hideFx, showFx;
+ if ( o.fx ) {
+ if ( $.isArray( o.fx ) ) {
+ hideFx = o.fx[ 0 ];
+ showFx = o.fx[ 1 ];
+ } else {
+ hideFx = showFx = o.fx;
+ }
+ }
+
+ // Reset certain styles left over from animation
+ // and prevent IE's ClearType bug...
+ function resetStyle( $el, fx ) {
+ $el.css( "display", "" );
+ if ( !$.support.opacity && fx.opacity ) {
+ $el[ 0 ].style.removeAttribute( "filter" );
+ }
+ }
+
+ // Show a tab...
+ var showTab = showFx
+ ? function( clicked, $show ) {
+ $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" );
+ $show.hide().removeClass( "ui-tabs-hide" ) // avoid flicker that way
+ .animate( showFx, showFx.duration || "normal", function() {
+ resetStyle( $show, showFx );
+ self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) );
+ });
+ }
+ : function( clicked, $show ) {
+ $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" );
+ $show.removeClass( "ui-tabs-hide" );
+ self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) );
+ };
+
+ // Hide a tab, $show is optional...
+ var hideTab = hideFx
+ ? function( clicked, $hide ) {
+ $hide.animate( hideFx, hideFx.duration || "normal", function() {
+ self.lis.removeClass( "ui-tabs-selected ui-state-active" );
+ $hide.addClass( "ui-tabs-hide" );
+ resetStyle( $hide, hideFx );
+ self.element.dequeue( "tabs" );
+ });
+ }
+ : function( clicked, $hide, $show ) {
+ self.lis.removeClass( "ui-tabs-selected ui-state-active" );
+ $hide.addClass( "ui-tabs-hide" );
+ self.element.dequeue( "tabs" );
+ };
+
+ // attach tab event handler, unbind to avoid duplicates from former tabifying...
+ this.anchors.bind( o.event + ".tabs", function() {
+ var el = this,
+ $li = $(el).closest( "li" ),
+ $hide = self.panels.filter( ":not(.ui-tabs-hide)" ),
+ $show = self.element.find( self._sanitizeSelector( el.hash ) );
+
+ // If tab is already selected and not collapsible or tab disabled or
+ // or is already loading or click callback returns false stop here.
+ // Check if click handler returns false last so that it is not executed
+ // for a disabled or loading tab!
+ if ( ( $li.hasClass( "ui-tabs-selected" ) && !o.collapsible) ||
+ $li.hasClass( "ui-state-disabled" ) ||
+ $li.hasClass( "ui-state-processing" ) ||
+ self.panels.filter( ":animated" ).length ||
+ self._trigger( "select", null, self._ui( this, $show[ 0 ] ) ) === false ) {
+ this.blur();
+ return false;
+ }
+
+ o.selected = self.anchors.index( this );
+
+ self.abort();
+
+ // if tab may be closed
+ if ( o.collapsible ) {
+ if ( $li.hasClass( "ui-tabs-selected" ) ) {
+ o.selected = -1;
+
+ if ( o.cookie ) {
+ self._cookie( o.selected, o.cookie );
+ }
+
+ self.element.queue( "tabs", function() {
+ hideTab( el, $hide );
+ }).dequeue( "tabs" );
+
+ this.blur();
+ return false;
+ } else if ( !$hide.length ) {
+ if ( o.cookie ) {
+ self._cookie( o.selected, o.cookie );
+ }
+
+ self.element.queue( "tabs", function() {
+ showTab( el, $show );
+ });
+
+ // TODO make passing in node possible, see also http://dev.jqueryui.com/ticket/3171
+ self.load( self.anchors.index( this ) );
+
+ this.blur();
+ return false;
+ }
+ }
+
+ if ( o.cookie ) {
+ self._cookie( o.selected, o.cookie );
+ }
+
+ // show new tab
+ if ( $show.length ) {
+ if ( $hide.length ) {
+ self.element.queue( "tabs", function() {
+ hideTab( el, $hide );
+ });
+ }
+ self.element.queue( "tabs", function() {
+ showTab( el, $show );
+ });
+
+ self.load( self.anchors.index( this ) );
+ } else {
+ throw "jQuery UI Tabs: Mismatching fragment identifier.";
+ }
+
+ // Prevent IE from keeping other link focussed when using the back button
+ // and remove dotted border from clicked link. This is controlled via CSS
+ // in modern browsers; blur() removes focus from address bar in Firefox
+ // which can become a usability and annoying problem with tabs('rotate').
+ if ( $.browser.msie ) {
+ this.blur();
+ }
+ });
+
+ // disable click in any case
+ this.anchors.bind( "click.tabs", function(){
+ return false;
+ });
+ },
+
+ _getIndex: function( index ) {
+ // meta-function to give users option to provide a href string instead of a numerical index.
+ // also sanitizes numerical indexes to valid values.
+ if ( typeof index == "string" ) {
+ index = this.anchors.index( this.anchors.filter( "[href$=" + index + "]" ) );
+ }
+
+ return index;
+ },
+
+ destroy: function() {
+ var o = this.options;
+
+ this.abort();
+
+ this.element
+ .unbind( ".tabs" )
+ .removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" )
+ .removeData( "tabs" );
+
+ this.list.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
+
+ this.anchors.each(function() {
+ var href = $.data( this, "href.tabs" );
+ if ( href ) {
+ this.href = href;
+ }
+ var $this = $( this ).unbind( ".tabs" );
+ $.each( [ "href", "load", "cache" ], function( i, prefix ) {
+ $this.removeData( prefix + ".tabs" );
+ });
+ });
+
+ this.lis.unbind( ".tabs" ).add( this.panels ).each(function() {
+ if ( $.data( this, "destroy.tabs" ) ) {
+ $( this ).remove();
+ } else {
+ $( this ).removeClass([
+ "ui-state-default",
+ "ui-corner-top",
+ "ui-tabs-selected",
+ "ui-state-active",
+ "ui-state-hover",
+ "ui-state-focus",
+ "ui-state-disabled",
+ "ui-tabs-panel",
+ "ui-widget-content",
+ "ui-corner-bottom",
+ "ui-tabs-hide"
+ ].join( " " ) );
+ }
+ });
+
+ if ( o.cookie ) {
+ this._cookie( null, o.cookie );
+ }
+
+ return this;
+ },
+
+ add: function( url, label, index ) {
+ if ( index === undefined ) {
+ index = this.anchors.length;
+ }
+
+ var self = this,
+ o = this.options,
+ $li = $( o.tabTemplate.replace( /#\{href\}/g, url ).replace( /#\{label\}/g, label ) ),
+ id = !url.indexOf( "#" ) ? url.replace( "#", "" ) : this._tabId( $( "a", $li )[ 0 ] );
+
+ $li.addClass( "ui-state-default ui-corner-top" ).data( "destroy.tabs", true );
+
+ // try to find an existing element before creating a new one
+ var $panel = self.element.find( "#" + id );
+ if ( !$panel.length ) {
+ $panel = $( o.panelTemplate )
+ .attr( "id", id )
+ .data( "destroy.tabs", true );
+ }
+ $panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide" );
+
+ if ( index >= this.lis.length ) {
+ $li.appendTo( this.list );
+ $panel.appendTo( this.list[ 0 ].parentNode );
+ } else {
+ $li.insertBefore( this.lis[ index ] );
+ $panel.insertBefore( this.panels[ index ] );
+ }
+
+ o.disabled = $.map( o.disabled, function( n, i ) {
+ return n >= index ? ++n : n;
+ });
+
+ this._tabify();
+
+ if ( this.anchors.length == 1 ) {
+ o.selected = 0;
+ $li.addClass( "ui-tabs-selected ui-state-active" );
+ $panel.removeClass( "ui-tabs-hide" );
+ this.element.queue( "tabs", function() {
+ self._trigger( "show", null, self._ui( self.anchors[ 0 ], self.panels[ 0 ] ) );
+ });
+
+ this.load( 0 );
+ }
+
+ this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
+ return this;
+ },
+
+ remove: function( index ) {
+ index = this._getIndex( index );
+ var o = this.options,
+ $li = this.lis.eq( index ).remove(),
+ $panel = this.panels.eq( index ).remove();
+
+ // If selected tab was removed focus tab to the right or
+ // in case the last tab was removed the tab to the left.
+ if ( $li.hasClass( "ui-tabs-selected" ) && this.anchors.length > 1) {
+ this.select( index + ( index + 1 < this.anchors.length ? 1 : -1 ) );
+ }
+
+ o.disabled = $.map(
+ $.grep( o.disabled, function(n, i) {
+ return n != index;
+ }),
+ function( n, i ) {
+ return n >= index ? --n : n;
+ });
+
+ this._tabify();
+
+ this._trigger( "remove", null, this._ui( $li.find( "a" )[ 0 ], $panel[ 0 ] ) );
+ return this;
+ },
+
+ enable: function( index ) {
+ index = this._getIndex( index );
+ var o = this.options;
+ if ( $.inArray( index, o.disabled ) == -1 ) {
+ return;
+ }
+
+ this.lis.eq( index ).removeClass( "ui-state-disabled" );
+ o.disabled = $.grep( o.disabled, function( n, i ) {
+ return n != index;
+ });
+
+ this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
+ return this;
+ },
+
+ disable: function( index ) {
+ index = this._getIndex( index );
+ var self = this, o = this.options;
+ // cannot disable already selected tab
+ if ( index != o.selected ) {
+ this.lis.eq( index ).addClass( "ui-state-disabled" );
+
+ o.disabled.push( index );
+ o.disabled.sort();
+
+ this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
+ }
+
+ return this;
+ },
+
+ select: function( index ) {
+ index = this._getIndex( index );
+ if ( index == -1 ) {
+ if ( this.options.collapsible && this.options.selected != -1 ) {
+ index = this.options.selected;
+ } else {
+ return this;
+ }
+ }
+ this.anchors.eq( index ).trigger( this.options.event + ".tabs" );
+ return this;
+ },
+
+ load: function( index ) {
+ index = this._getIndex( index );
+ var self = this,
+ o = this.options,
+ a = this.anchors.eq( index )[ 0 ],
+ url = $.data( a, "load.tabs" );
+
+ this.abort();
+
+ // not remote or from cache
+ if ( !url || this.element.queue( "tabs" ).length !== 0 && $.data( a, "cache.tabs" ) ) {
+ this.element.dequeue( "tabs" );
+ return;
+ }
+
+ // load remote from here on
+ this.lis.eq( index ).addClass( "ui-state-processing" );
+
+ if ( o.spinner ) {
+ var span = $( "span", a );
+ span.data( "label.tabs", span.html() ).html( o.spinner );
+ }
+
+ this.xhr = $.ajax( $.extend( {}, o.ajaxOptions, {
+ url: url,
+ success: function( r, s ) {
+ self.element.find( self._sanitizeSelector( a.hash ) ).html( r );
+
+ // take care of tab labels
+ self._cleanup();
+
+ if ( o.cache ) {
+ $.data( a, "cache.tabs", true );
+ }
+
+ self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) );
+ try {
+ o.ajaxOptions.success( r, s );
+ }
+ catch ( e ) {}
+ },
+ error: function( xhr, s, e ) {
+ // take care of tab labels
+ self._cleanup();
+
+ self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) );
+ try {
+ // Passing index avoid a race condition when this method is
+ // called after the user has selected another tab.
+ // Pass the anchor that initiated this request allows
+ // loadError to manipulate the tab content panel via $(a.hash)
+ o.ajaxOptions.error( xhr, s, index, a );
+ }
+ catch ( e ) {}
+ }
+ } ) );
+
+ // last, so that load event is fired before show...
+ self.element.dequeue( "tabs" );
+
+ return this;
+ },
+
+ abort: function() {
+ // stop possibly running animations
+ this.element.queue( [] );
+ this.panels.stop( false, true );
+
+ // "tabs" queue must not contain more than two elements,
+ // which are the callbacks for the latest clicked tab...
+ this.element.queue( "tabs", this.element.queue( "tabs" ).splice( -2, 2 ) );
+
+ // terminate pending requests from other tabs
+ if ( this.xhr ) {
+ this.xhr.abort();
+ delete this.xhr;
+ }
+
+ // take care of tab labels
+ this._cleanup();
+ return this;
+ },
+
+ url: function( index, url ) {
+ this.anchors.eq( index ).removeData( "cache.tabs" ).data( "load.tabs", url );
+ return this;
+ },
+
+ length: function() {
+ return this.anchors.length;
+ }
+});
+
+$.extend( $.ui.tabs, {
+ version: "1.8.18"
+});
+
+/*
+ * Tabs Extensions
+ */
+
+/*
+ * Rotate
+ */
+$.extend( $.ui.tabs.prototype, {
+ rotation: null,
+ rotate: function( ms, continuing ) {
+ var self = this,
+ o = this.options;
+
+ var rotate = self._rotate || ( self._rotate = function( e ) {
+ clearTimeout( self.rotation );
+ self.rotation = setTimeout(function() {
+ var t = o.selected;
+ self.select( ++t < self.anchors.length ? t : 0 );
+ }, ms );
+
+ if ( e ) {
+ e.stopPropagation();
+ }
+ });
+
+ var stop = self._unrotate || ( self._unrotate = !continuing
+ ? function(e) {
+ if (e.clientX) { // in case of a true click
+ self.rotate(null);
+ }
+ }
+ : function( e ) {
+ t = o.selected;
+ rotate();
+ });
+
+ // start rotation
+ if ( ms ) {
+ this.element.bind( "tabsshow", rotate );
+ this.anchors.bind( o.event + ".tabs", stop );
+ rotate();
+ // stop rotation
+ } else {
+ clearTimeout( self.rotation );
+ this.element.unbind( "tabsshow", rotate );
+ this.anchors.unbind( o.event + ".tabs", stop );
+ delete this._rotate;
+ delete this._unrotate;
+ }
+
+ return this;
+ }
+});
+
+})( jQuery );
diff --git a/storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery.flot-0.7.js b/storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery.flot-0.7.js
new file mode 100644
index 00000000000..aabc544e9a9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery.flot-0.7.js
@@ -0,0 +1,2599 @@
+/*! Javascript plotting library for jQuery, v. 0.7.
+ *
+ * Released under the MIT license by IOLA, December 2007.
+ *
+ */
+
+// first an inline dependency, jquery.colorhelpers.js, we inline it here
+// for convenience
+
+/* Plugin for jQuery for working with colors.
+ *
+ * Version 1.1.
+ *
+ * Inspiration from jQuery color animation plugin by John Resig.
+ *
+ * Released under the MIT license by Ole Laursen, October 2009.
+ *
+ * Examples:
+ *
+ * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
+ * var c = $.color.extract($("#mydiv"), 'background-color');
+ * console.log(c.r, c.g, c.b, c.a);
+ * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
+ *
+ * Note that .scale() and .add() return the same modified object
+ * instead of making a new one.
+ *
+ * V. 1.1: Fix error handling so e.g. parsing an empty string does
+ * produce a color rather than just crashing.
+ */
+(function(B){B.color={};B.color.make=function(F,E,C,D){var G={};G.r=F||0;G.g=E||0;G.b=C||0;G.a=D!=null?D:1;G.add=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]+=I}return G.normalize()};G.scale=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]*=I}return G.normalize()};G.toString=function(){if(G.a>=1){return"rgb("+[G.r,G.g,G.b].join(",")+")"}else{return"rgba("+[G.r,G.g,G.b,G.a].join(",")+")"}};G.normalize=function(){function H(J,K,I){return K<J?J:(K>I?I:K)}G.r=H(0,parseInt(G.r),255);G.g=H(0,parseInt(G.g),255);G.b=H(0,parseInt(G.b),255);G.a=H(0,G.a,1);return G};G.clone=function(){return B.color.make(G.r,G.b,G.g,G.a)};return G.normalize()};B.color.extract=function(D,C){var E;do{E=D.css(C).toLowerCase();if(E!=""&&E!="transparent"){break}D=D.parent()}while(!B.nodeName(D.get(0),"body"));if(E=="rgba(0, 0, 0, 0)"){E="transparent"}return B.color.parse(E)};B.color.parse=function(F){var E,C=B.color.make;if(E=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10))}if(E=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10),parseFloat(E[4]))}if(E=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55)}if(E=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55,parseFloat(E[4]))}if(E=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(F)){return C(parseInt(E[1],16),parseInt(E[2],16),parseInt(E[3],16))}if(E=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(F)){return C(parseInt(E[1]+E[1],16),parseInt(E[2]+E[2],16),parseInt(E[3]+E[3],16))}var D=B.trim(F).toLowerCase();if(D=="transparent"){return C(255,255,255,0)}else{E=A[D]||[0,0,0];return C(E[0],E[1],E[2])}};var A={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);
+
+// the actual Flot code
+(function($) {
+ function Plot(placeholder, data_, options_, plugins) {
+ // data is on the form:
+ // [ series1, series2 ... ]
+ // where series is either just the data as [ [x1, y1], [x2, y2], ... ]
+ // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... }
+
+ var series = [],
+ options = {
+ // the color theme used for graphs
+ colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],
+ legend: {
+ show: true,
+ noColumns: 1, // number of colums in legend table
+ labelFormatter: null, // fn: string -> string
+ labelBoxBorderColor: "#ccc", // border color for the little label boxes
+ container: null, // container (as jQuery object) to put legend in, null means default on top of graph
+ position: "ne", // position of default legend container within plot
+ margin: 5, // distance from grid edge to default legend container within plot
+ backgroundColor: null, // null means auto-detect
+ backgroundOpacity: 0.85 // set to 0 to avoid background
+ },
+ xaxis: {
+ show: null, // null = auto-detect, true = always, false = never
+ position: "bottom", // or "top"
+ mode: null, // null or "time"
+ color: null, // base color, labels, ticks
+ tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)"
+ transform: null, // null or f: number -> number to transform axis
+ inverseTransform: null, // if transform is set, this should be the inverse function
+ min: null, // min. value to show, null means set automatically
+ max: null, // max. value to show, null means set automatically
+ autoscaleMargin: null, // margin in % to add if auto-setting min/max
+ ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
+ tickFormatter: null, // fn: number -> string
+ labelWidth: null, // size of tick labels in pixels
+ labelHeight: null,
+ reserveSpace: null, // whether to reserve space even if axis isn't shown
+ tickLength: null, // size in pixels of ticks, or "full" for whole line
+ alignTicksWithAxis: null, // axis number or null for no sync
+
+ // mode specific options
+ tickDecimals: null, // no. of decimals, null means auto
+ tickSize: null, // number or [number, "unit"]
+ minTickSize: null, // number or [number, "unit"]
+ monthNames: null, // list of names of months
+ timeformat: null, // format string to use
+ twelveHourClock: false // 12 or 24 time in time mode
+ },
+ yaxis: {
+ autoscaleMargin: 0.02,
+ position: "left" // or "right"
+ },
+ xaxes: [],
+ yaxes: [],
+ series: {
+ points: {
+ show: false,
+ radius: 3,
+ lineWidth: 2, // in pixels
+ fill: true,
+ fillColor: "#ffffff",
+ symbol: "circle" // or callback
+ },
+ lines: {
+ // we don't put in show: false so we can see
+ // whether lines were actively disabled
+ lineWidth: 2, // in pixels
+ fill: false,
+ fillColor: null,
+ steps: false
+ },
+ bars: {
+ show: false,
+ lineWidth: 2, // in pixels
+ barWidth: 1, // in units of the x axis
+ fill: true,
+ fillColor: null,
+ align: "left", // or "center"
+ horizontal: false
+ },
+ shadowSize: 3
+ },
+ grid: {
+ show: true,
+ aboveData: false,
+ color: "#545454", // primary color used for outline and labels
+ backgroundColor: null, // null for transparent, else color
+ borderColor: null, // set if different from the grid color
+ tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)"
+ labelMargin: 5, // in pixels
+ axisMargin: 8, // in pixels
+ borderWidth: 2, // in pixels
+ minBorderMargin: null, // in pixels, null means taken from points radius
+ markings: null, // array of ranges or fn: axes -> array of ranges
+ markingsColor: "#f4f4f4",
+ markingsLineWidth: 2,
+ // interactive stuff
+ clickable: false,
+ hoverable: false,
+ autoHighlight: true, // highlight in case mouse is near
+ mouseActiveRadius: 10 // how far the mouse can be away to activate an item
+ },
+ hooks: {}
+ },
+ canvas = null, // the canvas for the plot itself
+ overlay = null, // canvas for interactive stuff on top of plot
+ eventHolder = null, // jQuery object that events should be bound to
+ ctx = null, octx = null,
+ xaxes = [], yaxes = [],
+ plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
+ canvasWidth = 0, canvasHeight = 0,
+ plotWidth = 0, plotHeight = 0,
+ hooks = {
+ processOptions: [],
+ processRawData: [],
+ processDatapoints: [],
+ drawSeries: [],
+ draw: [],
+ bindEvents: [],
+ drawOverlay: [],
+ shutdown: []
+ },
+ plot = this;
+
+ // public functions
+ plot.setData = setData;
+ plot.setupGrid = setupGrid;
+ plot.draw = draw;
+ plot.getPlaceholder = function() { return placeholder; };
+ plot.getCanvas = function() { return canvas; };
+ plot.getPlotOffset = function() { return plotOffset; };
+ plot.width = function () { return plotWidth; };
+ plot.height = function () { return plotHeight; };
+ plot.offset = function () {
+ var o = eventHolder.offset();
+ o.left += plotOffset.left;
+ o.top += plotOffset.top;
+ return o;
+ };
+ plot.getData = function () { return series; };
+ plot.getAxes = function () {
+ var res = {}, i;
+ $.each(xaxes.concat(yaxes), function (_, axis) {
+ if (axis)
+ res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis;
+ });
+ return res;
+ };
+ plot.getXAxes = function () { return xaxes; };
+ plot.getYAxes = function () { return yaxes; };
+ plot.c2p = canvasToAxisCoords;
+ plot.p2c = axisToCanvasCoords;
+ plot.getOptions = function () { return options; };
+ plot.highlight = highlight;
+ plot.unhighlight = unhighlight;
+ plot.triggerRedrawOverlay = triggerRedrawOverlay;
+ plot.pointOffset = function(point) {
+ return {
+ left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left),
+ top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top)
+ };
+ };
+ plot.shutdown = shutdown;
+ plot.resize = function () {
+ getCanvasDimensions();
+ resizeCanvas(canvas);
+ resizeCanvas(overlay);
+ };
+
+ // public attributes
+ plot.hooks = hooks;
+
+ // initialize
+ initPlugins(plot);
+ parseOptions(options_);
+ setupCanvases();
+ setData(data_);
+ setupGrid();
+ draw();
+ bindEvents();
+
+
+ function executeHooks(hook, args) {
+ args = [plot].concat(args);
+ for (var i = 0; i < hook.length; ++i)
+ hook[i].apply(this, args);
+ }
+
+ function initPlugins() {
+ for (var i = 0; i < plugins.length; ++i) {
+ var p = plugins[i];
+ p.init(plot);
+ if (p.options)
+ $.extend(true, options, p.options);
+ }
+ }
+
+ function parseOptions(opts) {
+ var i;
+
+ $.extend(true, options, opts);
+
+ if (options.xaxis.color == null)
+ options.xaxis.color = options.grid.color;
+ if (options.yaxis.color == null)
+ options.yaxis.color = options.grid.color;
+
+ if (options.xaxis.tickColor == null) // backwards-compatibility
+ options.xaxis.tickColor = options.grid.tickColor;
+ if (options.yaxis.tickColor == null) // backwards-compatibility
+ options.yaxis.tickColor = options.grid.tickColor;
+
+ if (options.grid.borderColor == null)
+ options.grid.borderColor = options.grid.color;
+ if (options.grid.tickColor == null)
+ options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString();
+
+ // fill in defaults in axes, copy at least always the
+ // first as the rest of the code assumes it'll be there
+ for (i = 0; i < Math.max(1, options.xaxes.length); ++i)
+ options.xaxes[i] = $.extend(true, {}, options.xaxis, options.xaxes[i]);
+ for (i = 0; i < Math.max(1, options.yaxes.length); ++i)
+ options.yaxes[i] = $.extend(true, {}, options.yaxis, options.yaxes[i]);
+
+ // backwards compatibility, to be removed in future
+ if (options.xaxis.noTicks && options.xaxis.ticks == null)
+ options.xaxis.ticks = options.xaxis.noTicks;
+ if (options.yaxis.noTicks && options.yaxis.ticks == null)
+ options.yaxis.ticks = options.yaxis.noTicks;
+ if (options.x2axis) {
+ options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis);
+ options.xaxes[1].position = "top";
+ }
+ if (options.y2axis) {
+ options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis);
+ options.yaxes[1].position = "right";
+ }
+ if (options.grid.coloredAreas)
+ options.grid.markings = options.grid.coloredAreas;
+ if (options.grid.coloredAreasColor)
+ options.grid.markingsColor = options.grid.coloredAreasColor;
+ if (options.lines)
+ $.extend(true, options.series.lines, options.lines);
+ if (options.points)
+ $.extend(true, options.series.points, options.points);
+ if (options.bars)
+ $.extend(true, options.series.bars, options.bars);
+ if (options.shadowSize != null)
+ options.series.shadowSize = options.shadowSize;
+
+ // save options on axes for future reference
+ for (i = 0; i < options.xaxes.length; ++i)
+ getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i];
+ for (i = 0; i < options.yaxes.length; ++i)
+ getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i];
+
+ // add hooks from options
+ for (var n in hooks)
+ if (options.hooks[n] && options.hooks[n].length)
+ hooks[n] = hooks[n].concat(options.hooks[n]);
+
+ executeHooks(hooks.processOptions, [options]);
+ }
+
+ function setData(d) {
+ series = parseData(d);
+ fillInSeriesOptions();
+ processData();
+ }
+
+ function parseData(d) {
+ var res = [];
+ for (var i = 0; i < d.length; ++i) {
+ var s = $.extend(true, {}, options.series);
+
+ if (d[i].data != null) {
+ s.data = d[i].data; // move the data instead of deep-copy
+ delete d[i].data;
+
+ $.extend(true, s, d[i]);
+
+ d[i].data = s.data;
+ }
+ else
+ s.data = d[i];
+ res.push(s);
+ }
+
+ return res;
+ }
+
+ function axisNumber(obj, coord) {
+ var a = obj[coord + "axis"];
+ if (typeof a == "object") // if we got a real axis, extract number
+ a = a.n;
+ if (typeof a != "number")
+ a = 1; // default to first axis
+ return a;
+ }
+
+ function allAxes() {
+ // return flat array without annoying null entries
+ return $.grep(xaxes.concat(yaxes), function (a) { return a; });
+ }
+
+ function canvasToAxisCoords(pos) {
+ // return an object with x/y corresponding to all used axes
+ var res = {}, i, axis;
+ for (i = 0; i < xaxes.length; ++i) {
+ axis = xaxes[i];
+ if (axis && axis.used)
+ res["x" + axis.n] = axis.c2p(pos.left);
+ }
+
+ for (i = 0; i < yaxes.length; ++i) {
+ axis = yaxes[i];
+ if (axis && axis.used)
+ res["y" + axis.n] = axis.c2p(pos.top);
+ }
+
+ if (res.x1 !== undefined)
+ res.x = res.x1;
+ if (res.y1 !== undefined)
+ res.y = res.y1;
+
+ return res;
+ }
+
+ function axisToCanvasCoords(pos) {
+ // get canvas coords from the first pair of x/y found in pos
+ var res = {}, i, axis, key;
+
+ for (i = 0; i < xaxes.length; ++i) {
+ axis = xaxes[i];
+ if (axis && axis.used) {
+ key = "x" + axis.n;
+ if (pos[key] == null && axis.n == 1)
+ key = "x";
+
+ if (pos[key] != null) {
+ res.left = axis.p2c(pos[key]);
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < yaxes.length; ++i) {
+ axis = yaxes[i];
+ if (axis && axis.used) {
+ key = "y" + axis.n;
+ if (pos[key] == null && axis.n == 1)
+ key = "y";
+
+ if (pos[key] != null) {
+ res.top = axis.p2c(pos[key]);
+ break;
+ }
+ }
+ }
+
+ return res;
+ }
+
+ function getOrCreateAxis(axes, number) {
+ if (!axes[number - 1])
+ axes[number - 1] = {
+ n: number, // save the number for future reference
+ direction: axes == xaxes ? "x" : "y",
+ options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis)
+ };
+
+ return axes[number - 1];
+ }
+
+ function fillInSeriesOptions() {
+ var i;
+
+ // collect what we already got of colors
+ var neededColors = series.length,
+ usedColors = [],
+ assignedColors = [];
+ for (i = 0; i < series.length; ++i) {
+ var sc = series[i].color;
+ if (sc != null) {
+ --neededColors;
+ if (typeof sc == "number")
+ assignedColors.push(sc);
+ else
+ usedColors.push($.color.parse(series[i].color));
+ }
+ }
+
+ // we might need to generate more colors if higher indices
+ // are assigned
+ for (i = 0; i < assignedColors.length; ++i) {
+ neededColors = Math.max(neededColors, assignedColors[i] + 1);
+ }
+
+ // produce colors as needed
+ var colors = [], variation = 0;
+ i = 0;
+ while (colors.length < neededColors) {
+ var c;
+ if (options.colors.length == i) // check degenerate case
+ c = $.color.make(100, 100, 100);
+ else
+ c = $.color.parse(options.colors[i]);
+
+ // vary color if needed
+ var sign = variation % 2 == 1 ? -1 : 1;
+ c.scale('rgb', 1 + sign * Math.ceil(variation / 2) * 0.2)
+
+ // FIXME: if we're getting to close to something else,
+ // we should probably skip this one
+ colors.push(c);
+
+ ++i;
+ if (i >= options.colors.length) {
+ i = 0;
+ ++variation;
+ }
+ }
+
+ // fill in the options
+ var colori = 0, s;
+ for (i = 0; i < series.length; ++i) {
+ s = series[i];
+
+ // assign colors
+ if (s.color == null) {
+ s.color = colors[colori].toString();
+ ++colori;
+ }
+ else if (typeof s.color == "number")
+ s.color = colors[s.color].toString();
+
+ // turn on lines automatically in case nothing is set
+ if (s.lines.show == null) {
+ var v, show = true;
+ for (v in s)
+ if (s[v] && s[v].show) {
+ show = false;
+ break;
+ }
+ if (show)
+ s.lines.show = true;
+ }
+
+ // setup axes
+ s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x"));
+ s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y"));
+ }
+ }
+
+ function processData() {
+ var topSentry = Number.POSITIVE_INFINITY,
+ bottomSentry = Number.NEGATIVE_INFINITY,
+ fakeInfinity = Number.MAX_VALUE,
+ i, j, k, m, length,
+ s, points, ps, x, y, axis, val, f, p;
+
+ function updateAxis(axis, min, max) {
+ if (min < axis.datamin && min != -fakeInfinity)
+ axis.datamin = min;
+ if (max > axis.datamax && max != fakeInfinity)
+ axis.datamax = max;
+ }
+
+ $.each(allAxes(), function (_, axis) {
+ // init axis
+ axis.datamin = topSentry;
+ axis.datamax = bottomSentry;
+ axis.used = false;
+ });
+
+ for (i = 0; i < series.length; ++i) {
+ s = series[i];
+ s.datapoints = { points: [] };
+
+ executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]);
+ }
+
+ // first pass: clean and copy data
+ for (i = 0; i < series.length; ++i) {
+ s = series[i];
+
+ var data = s.data, format = s.datapoints.format;
+
+ if (!format) {
+ format = [];
+ // find out how to copy
+ format.push({ x: true, number: true, required: true });
+ format.push({ y: true, number: true, required: true });
+
+ if (s.bars.show || (s.lines.show && s.lines.fill)) {
+ format.push({ y: true, number: true, required: false, defaultValue: 0 });
+ if (s.bars.horizontal) {
+ delete format[format.length - 1].y;
+ format[format.length - 1].x = true;
+ }
+ }
+
+ s.datapoints.format = format;
+ }
+
+ if (s.datapoints.pointsize != null)
+ continue; // already filled in
+
+ s.datapoints.pointsize = format.length;
+
+ ps = s.datapoints.pointsize;
+ points = s.datapoints.points;
+
+ insertSteps = s.lines.show && s.lines.steps;
+ s.xaxis.used = s.yaxis.used = true;
+
+ for (j = k = 0; j < data.length; ++j, k += ps) {
+ p = data[j];
+
+ var nullify = p == null;
+ if (!nullify) {
+ for (m = 0; m < ps; ++m) {
+ val = p[m];
+ f = format[m];
+
+ if (f) {
+ if (f.number && val != null) {
+ val = +val; // convert to number
+ if (isNaN(val))
+ val = null;
+ else if (val == Infinity)
+ val = fakeInfinity;
+ else if (val == -Infinity)
+ val = -fakeInfinity;
+ }
+
+ if (val == null) {
+ if (f.required)
+ nullify = true;
+
+ if (f.defaultValue != null)
+ val = f.defaultValue;
+ }
+ }
+
+ points[k + m] = val;
+ }
+ }
+
+ if (nullify) {
+ for (m = 0; m < ps; ++m) {
+ val = points[k + m];
+ if (val != null) {
+ f = format[m];
+ // extract min/max info
+ if (f.x)
+ updateAxis(s.xaxis, val, val);
+ if (f.y)
+ updateAxis(s.yaxis, val, val);
+ }
+ points[k + m] = null;
+ }
+ }
+ else {
+ // a little bit of line specific stuff that
+ // perhaps shouldn't be here, but lacking
+ // better means...
+ if (insertSteps && k > 0
+ && points[k - ps] != null
+ && points[k - ps] != points[k]
+ && points[k - ps + 1] != points[k + 1]) {
+ // copy the point to make room for a middle point
+ for (m = 0; m < ps; ++m)
+ points[k + ps + m] = points[k + m];
+
+ // middle point has same y
+ points[k + 1] = points[k - ps + 1];
+
+ // we've added a point, better reflect that
+ k += ps;
+ }
+ }
+ }
+ }
+
+ // give the hooks a chance to run
+ for (i = 0; i < series.length; ++i) {
+ s = series[i];
+
+ executeHooks(hooks.processDatapoints, [ s, s.datapoints]);
+ }
+
+ // second pass: find datamax/datamin for auto-scaling
+ for (i = 0; i < series.length; ++i) {
+ s = series[i];
+ points = s.datapoints.points,
+ ps = s.datapoints.pointsize;
+
+ var xmin = topSentry, ymin = topSentry,
+ xmax = bottomSentry, ymax = bottomSentry;
+
+ for (j = 0; j < points.length; j += ps) {
+ if (points[j] == null)
+ continue;
+
+ for (m = 0; m < ps; ++m) {
+ val = points[j + m];
+ f = format[m];
+ if (!f || val == fakeInfinity || val == -fakeInfinity)
+ continue;
+
+ if (f.x) {
+ if (val < xmin)
+ xmin = val;
+ if (val > xmax)
+ xmax = val;
+ }
+ if (f.y) {
+ if (val < ymin)
+ ymin = val;
+ if (val > ymax)
+ ymax = val;
+ }
+ }
+ }
+
+ if (s.bars.show) {
+ // make sure we got room for the bar on the dancing floor
+ var delta = s.bars.align == "left" ? 0 : -s.bars.barWidth/2;
+ if (s.bars.horizontal) {
+ ymin += delta;
+ ymax += delta + s.bars.barWidth;
+ }
+ else {
+ xmin += delta;
+ xmax += delta + s.bars.barWidth;
+ }
+ }
+
+ updateAxis(s.xaxis, xmin, xmax);
+ updateAxis(s.yaxis, ymin, ymax);
+ }
+
+ $.each(allAxes(), function (_, axis) {
+ if (axis.datamin == topSentry)
+ axis.datamin = null;
+ if (axis.datamax == bottomSentry)
+ axis.datamax = null;
+ });
+ }
+
+ function makeCanvas(skipPositioning, cls) {
+ var c = document.createElement('canvas');
+ c.className = cls;
+ c.width = canvasWidth;
+ c.height = canvasHeight;
+
+ if (!skipPositioning)
+ $(c).css({ position: 'absolute', left: 0, top: 0 });
+
+ $(c).appendTo(placeholder);
+
+ if (!c.getContext) // excanvas hack
+ c = window.G_vmlCanvasManager.initElement(c);
+
+ // used for resetting in case we get replotted
+ c.getContext("2d").save();
+
+ return c;
+ }
+
+ function getCanvasDimensions() {
+ canvasWidth = placeholder.width();
+ canvasHeight = placeholder.height();
+
+ if (canvasWidth <= 0 || canvasHeight <= 0)
+ throw "Invalid dimensions for plot, width = " + canvasWidth + ", height = " + canvasHeight;
+ }
+
+ function resizeCanvas(c) {
+ // resizing should reset the state (excanvas seems to be
+ // buggy though)
+ if (c.width != canvasWidth)
+ c.width = canvasWidth;
+
+ if (c.height != canvasHeight)
+ c.height = canvasHeight;
+
+ // so try to get back to the initial state (even if it's
+ // gone now, this should be safe according to the spec)
+ var cctx = c.getContext("2d");
+ cctx.restore();
+
+ // and save again
+ cctx.save();
+ }
+
+ function setupCanvases() {
+ var reused,
+ existingCanvas = placeholder.children("canvas.base"),
+ existingOverlay = placeholder.children("canvas.overlay");
+
+ if (existingCanvas.length == 0 || existingOverlay == 0) {
+ // init everything
+
+ placeholder.html(""); // make sure placeholder is clear
+
+ placeholder.css({ padding: 0 }); // padding messes up the positioning
+
+ if (placeholder.css("position") == 'static')
+ placeholder.css("position", "relative"); // for positioning labels and overlay
+
+ getCanvasDimensions();
+
+ canvas = makeCanvas(true, "base");
+ overlay = makeCanvas(false, "overlay"); // overlay canvas for interactive features
+
+ reused = false;
+ }
+ else {
+ // reuse existing elements
+
+ canvas = existingCanvas.get(0);
+ overlay = existingOverlay.get(0);
+
+ reused = true;
+ }
+
+ ctx = canvas.getContext("2d");
+ octx = overlay.getContext("2d");
+
+ // we include the canvas in the event holder too, because IE 7
+ // sometimes has trouble with the stacking order
+ eventHolder = $([overlay, canvas]);
+
+ if (reused) {
+ // run shutdown in the old plot object
+ placeholder.data("plot").shutdown();
+
+ // reset reused canvases
+ plot.resize();
+
+ // make sure overlay pixels are cleared (canvas is cleared when we redraw)
+ octx.clearRect(0, 0, canvasWidth, canvasHeight);
+
+ // then whack any remaining obvious garbage left
+ eventHolder.unbind();
+ placeholder.children().not([canvas, overlay]).remove();
+ }
+
+ // save in case we get replotted
+ placeholder.data("plot", plot);
+ }
+
+ function bindEvents() {
+ // bind events
+ if (options.grid.hoverable) {
+ eventHolder.mousemove(onMouseMove);
+ eventHolder.mouseleave(onMouseLeave);
+ }
+
+ if (options.grid.clickable)
+ eventHolder.click(onClick);
+
+ executeHooks(hooks.bindEvents, [eventHolder]);
+ }
+
+ function shutdown() {
+ if (redrawTimeout)
+ clearTimeout(redrawTimeout);
+
+ eventHolder.unbind("mousemove", onMouseMove);
+ eventHolder.unbind("mouseleave", onMouseLeave);
+ eventHolder.unbind("click", onClick);
+
+ executeHooks(hooks.shutdown, [eventHolder]);
+ }
+
+ function setTransformationHelpers(axis) {
+ // set helper functions on the axis, assumes plot area
+ // has been computed already
+
+ function identity(x) { return x; }
+
+ var s, m, t = axis.options.transform || identity,
+ it = axis.options.inverseTransform;
+
+ // precompute how much the axis is scaling a point
+ // in canvas space
+ if (axis.direction == "x") {
+ s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min));
+ m = Math.min(t(axis.max), t(axis.min));
+ }
+ else {
+ s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min));
+ s = -s;
+ m = Math.max(t(axis.max), t(axis.min));
+ }
+
+ // data point to canvas coordinate
+ if (t == identity) // slight optimization
+ axis.p2c = function (p) { return (p - m) * s; };
+ else
+ axis.p2c = function (p) { return (t(p) - m) * s; };
+ // canvas coordinate to data point
+ if (!it)
+ axis.c2p = function (c) { return m + c / s; };
+ else
+ axis.c2p = function (c) { return it(m + c / s); };
+ }
+
+ function measureTickLabels(axis) {
+ var opts = axis.options, i, ticks = axis.ticks || [], labels = [],
+ l, w = opts.labelWidth, h = opts.labelHeight, dummyDiv;
+
+ function makeDummyDiv(labels, width) {
+ return $('<div style="position:absolute;top:-10000px;' + width + 'font-size:smaller">' +
+ '<div class="' + axis.direction + 'Axis ' + axis.direction + axis.n + 'Axis">'
+ + labels.join("") + '</div></div>')
+ .appendTo(placeholder);
+ }
+
+ if (axis.direction == "x") {
+ // to avoid measuring the widths of the labels (it's slow), we
+ // construct fixed-size boxes and put the labels inside
+ // them, we don't need the exact figures and the
+ // fixed-size box content is easy to center
+ if (w == null)
+ w = Math.floor(canvasWidth / (ticks.length > 0 ? ticks.length : 1));
+
+ // measure x label heights
+ if (h == null) {
+ labels = [];
+ for (i = 0; i < ticks.length; ++i) {
+ l = ticks[i].label;
+ if (l)
+ labels.push('<div class="tickLabel" style="float:left;width:' + w + 'px">' + l + '</div>');
+ }
+
+ if (labels.length > 0) {
+ // stick them all in the same div and measure
+ // collective height
+ labels.push('<div style="clear:left"></div>');
+ dummyDiv = makeDummyDiv(labels, "width:10000px;");
+ h = dummyDiv.height();
+ dummyDiv.remove();
+ }
+ }
+ }
+ else if (w == null || h == null) {
+ // calculate y label dimensions
+ for (i = 0; i < ticks.length; ++i) {
+ l = ticks[i].label;
+ if (l)
+ labels.push('<div class="tickLabel">' + l + '</div>');
+ }
+
+ if (labels.length > 0) {
+ dummyDiv = makeDummyDiv(labels, "");
+ if (w == null)
+ w = dummyDiv.children().width();
+ if (h == null)
+ h = dummyDiv.find("div.tickLabel").height();
+ dummyDiv.remove();
+ }
+ }
+
+ if (w == null)
+ w = 0;
+ if (h == null)
+ h = 0;
+
+ axis.labelWidth = w;
+ axis.labelHeight = h;
+ }
+
+ function allocateAxisBoxFirstPhase(axis) {
+ // find the bounding box of the axis by looking at label
+ // widths/heights and ticks, make room by diminishing the
+ // plotOffset
+
+ var lw = axis.labelWidth,
+ lh = axis.labelHeight,
+ pos = axis.options.position,
+ tickLength = axis.options.tickLength,
+ axismargin = options.grid.axisMargin,
+ padding = options.grid.labelMargin,
+ all = axis.direction == "x" ? xaxes : yaxes,
+ index;
+
+ // determine axis margin
+ var samePosition = $.grep(all, function (a) {
+ return a && a.options.position == pos && a.reserveSpace;
+ });
+ if ($.inArray(axis, samePosition) == samePosition.length - 1)
+ axismargin = 0; // outermost
+
+ // determine tick length - if we're innermost, we can use "full"
+ if (tickLength == null)
+ tickLength = "full";
+
+ var sameDirection = $.grep(all, function (a) {
+ return a && a.reserveSpace;
+ });
+
+ var innermost = $.inArray(axis, sameDirection) == 0;
+ if (!innermost && tickLength == "full")
+ tickLength = 5;
+
+ if (!isNaN(+tickLength))
+ padding += +tickLength;
+
+ // compute box
+ if (axis.direction == "x") {
+ lh += padding;
+
+ if (pos == "bottom") {
+ plotOffset.bottom += lh + axismargin;
+ axis.box = { top: canvasHeight - plotOffset.bottom, height: lh };
+ }
+ else {
+ axis.box = { top: plotOffset.top + axismargin, height: lh };
+ plotOffset.top += lh + axismargin;
+ }
+ }
+ else {
+ lw += padding;
+
+ if (pos == "left") {
+ axis.box = { left: plotOffset.left + axismargin, width: lw };
+ plotOffset.left += lw + axismargin;
+ }
+ else {
+ plotOffset.right += lw + axismargin;
+ axis.box = { left: canvasWidth - plotOffset.right, width: lw };
+ }
+ }
+
+ // save for future reference
+ axis.position = pos;
+ axis.tickLength = tickLength;
+ axis.box.padding = padding;
+ axis.innermost = innermost;
+ }
+
+ function allocateAxisBoxSecondPhase(axis) {
+ // set remaining bounding box coordinates
+ if (axis.direction == "x") {
+ axis.box.left = plotOffset.left;
+ axis.box.width = plotWidth;
+ }
+ else {
+ axis.box.top = plotOffset.top;
+ axis.box.height = plotHeight;
+ }
+ }
+
+ function setupGrid() {
+ var i, axes = allAxes();
+
+ // first calculate the plot and axis box dimensions
+
+ $.each(axes, function (_, axis) {
+ axis.show = axis.options.show;
+ if (axis.show == null)
+ axis.show = axis.used; // by default an axis is visible if it's got data
+
+ axis.reserveSpace = axis.show || axis.options.reserveSpace;
+
+ setRange(axis);
+ });
+
+ allocatedAxes = $.grep(axes, function (axis) { return axis.reserveSpace; });
+
+ plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = 0;
+ if (options.grid.show) {
+ $.each(allocatedAxes, function (_, axis) {
+ // make the ticks
+ setupTickGeneration(axis);
+ setTicks(axis);
+ snapRangeToTicks(axis, axis.ticks);
+
+ // find labelWidth/Height for axis
+ measureTickLabels(axis);
+ });
+
+ // with all dimensions in house, we can compute the
+ // axis boxes, start from the outside (reverse order)
+ for (i = allocatedAxes.length - 1; i >= 0; --i)
+ allocateAxisBoxFirstPhase(allocatedAxes[i]);
+
+ // make sure we've got enough space for things that
+ // might stick out
+ var minMargin = options.grid.minBorderMargin;
+ if (minMargin == null) {
+ minMargin = 0;
+ for (i = 0; i < series.length; ++i)
+ minMargin = Math.max(minMargin, series[i].points.radius + series[i].points.lineWidth/2);
+ }
+
+ for (var a in plotOffset) {
+ plotOffset[a] += options.grid.borderWidth;
+ plotOffset[a] = Math.max(minMargin, plotOffset[a]);
+ }
+ }
+
+ plotWidth = canvasWidth - plotOffset.left - plotOffset.right;
+ plotHeight = canvasHeight - plotOffset.bottom - plotOffset.top;
+
+ // now we got the proper plotWidth/Height, we can compute the scaling
+ $.each(axes, function (_, axis) {
+ setTransformationHelpers(axis);
+ });
+
+ if (options.grid.show) {
+ $.each(allocatedAxes, function (_, axis) {
+ allocateAxisBoxSecondPhase(axis);
+ });
+
+ insertAxisLabels();
+ }
+
+ insertLegend();
+ }
+
+ function setRange(axis) {
+ var opts = axis.options,
+ min = +(opts.min != null ? opts.min : axis.datamin),
+ max = +(opts.max != null ? opts.max : axis.datamax),
+ delta = max - min;
+
+ if (delta == 0.0) {
+ // degenerate case
+ var widen = max == 0 ? 1 : 0.01;
+
+ if (opts.min == null)
+ min -= widen;
+ // always widen max if we couldn't widen min to ensure we
+ // don't fall into min == max which doesn't work
+ if (opts.max == null || opts.min != null)
+ max += widen;
+ }
+ else {
+ // consider autoscaling
+ var margin = opts.autoscaleMargin;
+ if (margin != null) {
+ if (opts.min == null) {
+ min -= delta * margin;
+ // make sure we don't go below zero if all values
+ // are positive
+ if (min < 0 && axis.datamin != null && axis.datamin >= 0)
+ min = 0;
+ }
+ if (opts.max == null) {
+ max += delta * margin;
+ if (max > 0 && axis.datamax != null && axis.datamax <= 0)
+ max = 0;
+ }
+ }
+ }
+ axis.min = min;
+ axis.max = max;
+ }
+
+ function setupTickGeneration(axis) {
+ var opts = axis.options;
+
+ // estimate number of ticks
+ var noTicks;
+ if (typeof opts.ticks == "number" && opts.ticks > 0)
+ noTicks = opts.ticks;
+ else
+ // heuristic based on the model a*sqrt(x) fitted to
+ // some data points that seemed reasonable
+ noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? canvasWidth : canvasHeight);
+
+ var delta = (axis.max - axis.min) / noTicks,
+ size, generator, unit, formatter, i, magn, norm;
+
+ if (opts.mode == "time") {
+ // pretty handling of time
+
+ // map of app. size of time units in milliseconds
+ var timeUnitSize = {
+ "second": 1000,
+ "minute": 60 * 1000,
+ "hour": 60 * 60 * 1000,
+ "day": 24 * 60 * 60 * 1000,
+ "month": 30 * 24 * 60 * 60 * 1000,
+ "year": 365.2425 * 24 * 60 * 60 * 1000
+ };
+
+
+ // the allowed tick sizes, after 1 year we use
+ // an integer algorithm
+ var spec = [
+ [1, "second"], [2, "second"], [5, "second"], [10, "second"],
+ [30, "second"],
+ [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
+ [30, "minute"],
+ [1, "hour"], [2, "hour"], [4, "hour"],
+ [8, "hour"], [12, "hour"],
+ [1, "day"], [2, "day"], [3, "day"],
+ [0.25, "month"], [0.5, "month"], [1, "month"],
+ [2, "month"], [3, "month"], [6, "month"],
+ [1, "year"]
+ ];
+
+ var minSize = 0;
+ if (opts.minTickSize != null) {
+ if (typeof opts.tickSize == "number")
+ minSize = opts.tickSize;
+ else
+ minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
+ }
+
+ for (var i = 0; i < spec.length - 1; ++i)
+ if (delta < (spec[i][0] * timeUnitSize[spec[i][1]]
+ + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
+ && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize)
+ break;
+ size = spec[i][0];
+ unit = spec[i][1];
+
+ // special-case the possibility of several years
+ if (unit == "year") {
+ magn = Math.pow(10, Math.floor(Math.log(delta / timeUnitSize.year) / Math.LN10));
+ norm = (delta / timeUnitSize.year) / magn;
+ if (norm < 1.5)
+ size = 1;
+ else if (norm < 3)
+ size = 2;
+ else if (norm < 7.5)
+ size = 5;
+ else
+ size = 10;
+
+ size *= magn;
+ }
+
+ axis.tickSize = opts.tickSize || [size, unit];
+
+ generator = function(axis) {
+ var ticks = [],
+ tickSize = axis.tickSize[0], unit = axis.tickSize[1],
+ d = new Date(axis.min);
+
+ var step = tickSize * timeUnitSize[unit];
+
+ if (unit == "second")
+ d.setUTCSeconds(floorInBase(d.getUTCSeconds(), tickSize));
+ if (unit == "minute")
+ d.setUTCMinutes(floorInBase(d.getUTCMinutes(), tickSize));
+ if (unit == "hour")
+ d.setUTCHours(floorInBase(d.getUTCHours(), tickSize));
+ if (unit == "month")
+ d.setUTCMonth(floorInBase(d.getUTCMonth(), tickSize));
+ if (unit == "year")
+ d.setUTCFullYear(floorInBase(d.getUTCFullYear(), tickSize));
+
+ // reset smaller components
+ d.setUTCMilliseconds(0);
+ if (step >= timeUnitSize.minute)
+ d.setUTCSeconds(0);
+ if (step >= timeUnitSize.hour)
+ d.setUTCMinutes(0);
+ if (step >= timeUnitSize.day)
+ d.setUTCHours(0);
+ if (step >= timeUnitSize.day * 4)
+ d.setUTCDate(1);
+ if (step >= timeUnitSize.year)
+ d.setUTCMonth(0);
+
+
+ var carry = 0, v = Number.NaN, prev;
+ do {
+ prev = v;
+ v = d.getTime();
+ ticks.push(v);
+ if (unit == "month") {
+ if (tickSize < 1) {
+ // a bit complicated - we'll divide the month
+ // up but we need to take care of fractions
+ // so we don't end up in the middle of a day
+ d.setUTCDate(1);
+ var start = d.getTime();
+ d.setUTCMonth(d.getUTCMonth() + 1);
+ var end = d.getTime();
+ d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
+ carry = d.getUTCHours();
+ d.setUTCHours(0);
+ }
+ else
+ d.setUTCMonth(d.getUTCMonth() + tickSize);
+ }
+ else if (unit == "year") {
+ d.setUTCFullYear(d.getUTCFullYear() + tickSize);
+ }
+ else
+ d.setTime(v + step);
+ } while (v < axis.max && v != prev);
+
+ return ticks;
+ };
+
+ formatter = function (v, axis) {
+ var d = new Date(v);
+
+ // first check global format
+ if (opts.timeformat != null)
+ return $.plot.formatDate(d, opts.timeformat, opts.monthNames);
+
+ var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
+ var span = axis.max - axis.min;
+ var suffix = (opts.twelveHourClock) ? " %p" : "";
+
+ if (t < timeUnitSize.minute)
+ fmt = "%h:%M:%S" + suffix;
+ else if (t < timeUnitSize.day) {
+ if (span < 2 * timeUnitSize.day)
+ fmt = "%h:%M" + suffix;
+ else
+ fmt = "%b %d %h:%M" + suffix;
+ }
+ else if (t < timeUnitSize.month)
+ fmt = "%b %d";
+ else if (t < timeUnitSize.year) {
+ if (span < timeUnitSize.year)
+ fmt = "%b";
+ else
+ fmt = "%b %y";
+ }
+ else
+ fmt = "%y";
+
+ return $.plot.formatDate(d, fmt, opts.monthNames);
+ };
+ }
+ else {
+ // pretty rounding of base-10 numbers
+ var maxDec = opts.tickDecimals;
+ var dec = -Math.floor(Math.log(delta) / Math.LN10);
+ if (maxDec != null && dec > maxDec)
+ dec = maxDec;
+
+ magn = Math.pow(10, -dec);
+ norm = delta / magn; // norm is between 1.0 and 10.0
+
+ if (norm < 1.5)
+ size = 1;
+ else if (norm < 3) {
+ size = 2;
+ // special case for 2.5, requires an extra decimal
+ if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) {
+ size = 2.5;
+ ++dec;
+ }
+ }
+ else if (norm < 7.5)
+ size = 5;
+ else
+ size = 10;
+
+ size *= magn;
+
+ if (opts.minTickSize != null && size < opts.minTickSize)
+ size = opts.minTickSize;
+
+ axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);
+ axis.tickSize = opts.tickSize || size;
+
+ generator = function (axis) {
+ var ticks = [];
+
+ // spew out all possible ticks
+ var start = floorInBase(axis.min, axis.tickSize),
+ i = 0, v = Number.NaN, prev;
+ do {
+ prev = v;
+ v = start + i * axis.tickSize;
+ ticks.push(v);
+ ++i;
+ } while (v < axis.max && v != prev);
+ return ticks;
+ };
+
+ formatter = function (v, axis) {
+ return v.toFixed(axis.tickDecimals);
+ };
+ }
+
+ if (opts.alignTicksWithAxis != null) {
+ var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1];
+ if (otherAxis && otherAxis.used && otherAxis != axis) {
+ // consider snapping min/max to outermost nice ticks
+ var niceTicks = generator(axis);
+ if (niceTicks.length > 0) {
+ if (opts.min == null)
+ axis.min = Math.min(axis.min, niceTicks[0]);
+ if (opts.max == null && niceTicks.length > 1)
+ axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]);
+ }
+
+ generator = function (axis) {
+ // copy ticks, scaled to this axis
+ var ticks = [], v, i;
+ for (i = 0; i < otherAxis.ticks.length; ++i) {
+ v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min);
+ v = axis.min + v * (axis.max - axis.min);
+ ticks.push(v);
+ }
+ return ticks;
+ };
+
+ // we might need an extra decimal since forced
+ // ticks don't necessarily fit naturally
+ if (axis.mode != "time" && opts.tickDecimals == null) {
+ var extraDec = Math.max(0, -Math.floor(Math.log(delta) / Math.LN10) + 1),
+ ts = generator(axis);
+
+ // only proceed if the tick interval rounded
+ // with an extra decimal doesn't give us a
+ // zero at end
+ if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec))))
+ axis.tickDecimals = extraDec;
+ }
+ }
+ }
+
+ axis.tickGenerator = generator;
+ if ($.isFunction(opts.tickFormatter))
+ axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); };
+ else
+ axis.tickFormatter = formatter;
+ }
+
+ function setTicks(axis) {
+ var oticks = axis.options.ticks, ticks = [];
+ if (oticks == null || (typeof oticks == "number" && oticks > 0))
+ ticks = axis.tickGenerator(axis);
+ else if (oticks) {
+ if ($.isFunction(oticks))
+ // generate the ticks
+ ticks = oticks({ min: axis.min, max: axis.max });
+ else
+ ticks = oticks;
+ }
+
+ // clean up/labelify the supplied ticks, copy them over
+ var i, v;
+ axis.ticks = [];
+ for (i = 0; i < ticks.length; ++i) {
+ var label = null;
+ var t = ticks[i];
+ if (typeof t == "object") {
+ v = +t[0];
+ if (t.length > 1)
+ label = t[1];
+ }
+ else
+ v = +t;
+ if (label == null)
+ label = axis.tickFormatter(v, axis);
+ if (!isNaN(v))
+ axis.ticks.push({ v: v, label: label });
+ }
+ }
+
+ function snapRangeToTicks(axis, ticks) {
+ if (axis.options.autoscaleMargin && ticks.length > 0) {
+ // snap to ticks
+ if (axis.options.min == null)
+ axis.min = Math.min(axis.min, ticks[0].v);
+ if (axis.options.max == null && ticks.length > 1)
+ axis.max = Math.max(axis.max, ticks[ticks.length - 1].v);
+ }
+ }
+
+ function draw() {
+ ctx.clearRect(0, 0, canvasWidth, canvasHeight);
+
+ var grid = options.grid;
+
+ // draw background, if any
+ if (grid.show && grid.backgroundColor)
+ drawBackground();
+
+ if (grid.show && !grid.aboveData)
+ drawGrid();
+
+ for (var i = 0; i < series.length; ++i) {
+ executeHooks(hooks.drawSeries, [ctx, series[i]]);
+ drawSeries(series[i]);
+ }
+
+ executeHooks(hooks.draw, [ctx]);
+
+ if (grid.show && grid.aboveData)
+ drawGrid();
+ }
+
+ function extractRange(ranges, coord) {
+ var axis, from, to, key, axes = allAxes();
+
+ for (i = 0; i < axes.length; ++i) {
+ axis = axes[i];
+ if (axis.direction == coord) {
+ key = coord + axis.n + "axis";
+ if (!ranges[key] && axis.n == 1)
+ key = coord + "axis"; // support x1axis as xaxis
+ if (ranges[key]) {
+ from = ranges[key].from;
+ to = ranges[key].to;
+ break;
+ }
+ }
+ }
+
+ // backwards-compat stuff - to be removed in future
+ if (!ranges[key]) {
+ axis = coord == "x" ? xaxes[0] : yaxes[0];
+ from = ranges[coord + "1"];
+ to = ranges[coord + "2"];
+ }
+
+ // auto-reverse as an added bonus
+ if (from != null && to != null && from > to) {
+ var tmp = from;
+ from = to;
+ to = tmp;
+ }
+
+ return { from: from, to: to, axis: axis };
+ }
+
+ function drawBackground() {
+ ctx.save();
+ ctx.translate(plotOffset.left, plotOffset.top);
+
+ ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)");
+ ctx.fillRect(0, 0, plotWidth, plotHeight);
+ ctx.restore();
+ }
+
+ function drawGrid() {
+ var i;
+
+ ctx.save();
+ ctx.translate(plotOffset.left, plotOffset.top);
+
+ // draw markings
+ var markings = options.grid.markings;
+ if (markings) {
+ if ($.isFunction(markings)) {
+ var axes = plot.getAxes();
+ // xmin etc. is backwards compatibility, to be
+ // removed in the future
+ axes.xmin = axes.xaxis.min;
+ axes.xmax = axes.xaxis.max;
+ axes.ymin = axes.yaxis.min;
+ axes.ymax = axes.yaxis.max;
+
+ markings = markings(axes);
+ }
+
+ for (i = 0; i < markings.length; ++i) {
+ var m = markings[i],
+ xrange = extractRange(m, "x"),
+ yrange = extractRange(m, "y");
+
+ // fill in missing
+ if (xrange.from == null)
+ xrange.from = xrange.axis.min;
+ if (xrange.to == null)
+ xrange.to = xrange.axis.max;
+ if (yrange.from == null)
+ yrange.from = yrange.axis.min;
+ if (yrange.to == null)
+ yrange.to = yrange.axis.max;
+
+ // clip
+ if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||
+ yrange.to < yrange.axis.min || yrange.from > yrange.axis.max)
+ continue;
+
+ xrange.from = Math.max(xrange.from, xrange.axis.min);
+ xrange.to = Math.min(xrange.to, xrange.axis.max);
+ yrange.from = Math.max(yrange.from, yrange.axis.min);
+ yrange.to = Math.min(yrange.to, yrange.axis.max);
+
+ if (xrange.from == xrange.to && yrange.from == yrange.to)
+ continue;
+
+ // then draw
+ xrange.from = xrange.axis.p2c(xrange.from);
+ xrange.to = xrange.axis.p2c(xrange.to);
+ yrange.from = yrange.axis.p2c(yrange.from);
+ yrange.to = yrange.axis.p2c(yrange.to);
+
+ if (xrange.from == xrange.to || yrange.from == yrange.to) {
+ // draw line
+ ctx.beginPath();
+ ctx.strokeStyle = m.color || options.grid.markingsColor;
+ ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;
+ ctx.moveTo(xrange.from, yrange.from);
+ ctx.lineTo(xrange.to, yrange.to);
+ ctx.stroke();
+ }
+ else {
+ // fill area
+ ctx.fillStyle = m.color || options.grid.markingsColor;
+ ctx.fillRect(xrange.from, yrange.to,
+ xrange.to - xrange.from,
+ yrange.from - yrange.to);
+ }
+ }
+ }
+
+ // draw the ticks
+ var axes = allAxes(), bw = options.grid.borderWidth;
+
+ for (var j = 0; j < axes.length; ++j) {
+ var axis = axes[j], box = axis.box,
+ t = axis.tickLength, x, y, xoff, yoff;
+ if (!axis.show || axis.ticks.length == 0)
+ continue
+
+ ctx.strokeStyle = axis.options.tickColor || $.color.parse(axis.options.color).scale('a', 0.22).toString();
+ ctx.lineWidth = 1;
+
+ // find the edges
+ if (axis.direction == "x") {
+ x = 0;
+ if (t == "full")
+ y = (axis.position == "top" ? 0 : plotHeight);
+ else
+ y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0);
+ }
+ else {
+ y = 0;
+ if (t == "full")
+ x = (axis.position == "left" ? 0 : plotWidth);
+ else
+ x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0);
+ }
+
+ // draw tick bar
+ if (!axis.innermost) {
+ ctx.beginPath();
+ xoff = yoff = 0;
+ if (axis.direction == "x")
+ xoff = plotWidth;
+ else
+ yoff = plotHeight;
+
+ if (ctx.lineWidth == 1) {
+ x = Math.floor(x) + 0.5;
+ y = Math.floor(y) + 0.5;
+ }
+
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + xoff, y + yoff);
+ ctx.stroke();
+ }
+
+ // draw ticks
+ ctx.beginPath();
+ for (i = 0; i < axis.ticks.length; ++i) {
+ var v = axis.ticks[i].v;
+
+ xoff = yoff = 0;
+
+ if (v < axis.min || v > axis.max
+ // skip those lying on the axes if we got a border
+ || (t == "full" && bw > 0
+ && (v == axis.min || v == axis.max)))
+ continue;
+
+ if (axis.direction == "x") {
+ x = axis.p2c(v);
+ yoff = t == "full" ? -plotHeight : t;
+
+ if (axis.position == "top")
+ yoff = -yoff;
+ }
+ else {
+ y = axis.p2c(v);
+ xoff = t == "full" ? -plotWidth : t;
+
+ if (axis.position == "left")
+ xoff = -xoff;
+ }
+
+ if (ctx.lineWidth == 1) {
+ if (axis.direction == "x")
+ x = Math.floor(x) + 0.5;
+ else
+ y = Math.floor(y) + 0.5;
+ }
+
+ ctx.moveTo(x, y);
+ ctx.lineTo(x + xoff, y + yoff);
+ }
+
+ ctx.stroke();
+ }
+
+
+ // draw border
+ if (bw) {
+ ctx.lineWidth = bw;
+ ctx.strokeStyle = options.grid.borderColor;
+ ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw);
+ }
+
+ ctx.restore();
+ }
+
+ function insertAxisLabels() {
+ placeholder.find(".tickLabels").remove();
+
+ var html = ['<div class="tickLabels" style="font-size:smaller">'];
+
+ var axes = allAxes();
+ for (var j = 0; j < axes.length; ++j) {
+ var axis = axes[j], box = axis.box;
+ if (!axis.show)
+ continue;
+ //debug: html.push('<div style="position:absolute;opacity:0.10;background-color:red;left:' + box.left + 'px;top:' + box.top + 'px;width:' + box.width + 'px;height:' + box.height + 'px"></div>')
+ html.push('<div class="' + axis.direction + 'Axis ' + axis.direction + axis.n + 'Axis" style="color:' + axis.options.color + '">');
+ for (var i = 0; i < axis.ticks.length; ++i) {
+ var tick = axis.ticks[i];
+ if (!tick.label || tick.v < axis.min || tick.v > axis.max)
+ continue;
+
+ var pos = {}, align;
+
+ if (axis.direction == "x") {
+ align = "center";
+ pos.left = Math.round(plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2);
+ if (axis.position == "bottom")
+ pos.top = box.top + box.padding;
+ else
+ pos.bottom = canvasHeight - (box.top + box.height - box.padding);
+ }
+ else {
+ pos.top = Math.round(plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2);
+ if (axis.position == "left") {
+ pos.right = canvasWidth - (box.left + box.width - box.padding)
+ align = "right";
+ }
+ else {
+ pos.left = box.left + box.padding;
+ align = "left";
+ }
+ }
+
+ pos.width = axis.labelWidth;
+
+ var style = ["position:absolute", "text-align:" + align ];
+ for (var a in pos)
+ style.push(a + ":" + pos[a] + "px")
+
+ html.push('<div class="tickLabel" style="' + style.join(';') + '">' + tick.label + '</div>');
+ }
+ html.push('</div>');
+ }
+
+ html.push('</div>');
+
+ placeholder.append(html.join(""));
+ }
+
+ function drawSeries(series) {
+ if (series.lines.show)
+ drawSeriesLines(series);
+ if (series.bars.show)
+ drawSeriesBars(series);
+ if (series.points.show)
+ drawSeriesPoints(series);
+ }
+
+ function drawSeriesLines(series) {
+ function plotLine(datapoints, xoffset, yoffset, axisx, axisy) {
+ var points = datapoints.points,
+ ps = datapoints.pointsize,
+ prevx = null, prevy = null;
+
+ ctx.beginPath();
+ for (var i = ps; i < points.length; i += ps) {
+ var x1 = points[i - ps], y1 = points[i - ps + 1],
+ x2 = points[i], y2 = points[i + 1];
+
+ if (x1 == null || x2 == null)
+ continue;
+
+ // clip with ymin
+ if (y1 <= y2 && y1 < axisy.min) {
+ if (y2 < axisy.min)
+ continue; // line segment is outside
+ // compute new intersection point
+ x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
+ y1 = axisy.min;
+ }
+ else if (y2 <= y1 && y2 < axisy.min) {
+ if (y1 < axisy.min)
+ continue;
+ x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
+ y2 = axisy.min;
+ }
+
+ // clip with ymax
+ if (y1 >= y2 && y1 > axisy.max) {
+ if (y2 > axisy.max)
+ continue;
+ x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
+ y1 = axisy.max;
+ }
+ else if (y2 >= y1 && y2 > axisy.max) {
+ if (y1 > axisy.max)
+ continue;
+ x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
+ y2 = axisy.max;
+ }
+
+ // clip with xmin
+ if (x1 <= x2 && x1 < axisx.min) {
+ if (x2 < axisx.min)
+ continue;
+ y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
+ x1 = axisx.min;
+ }
+ else if (x2 <= x1 && x2 < axisx.min) {
+ if (x1 < axisx.min)
+ continue;
+ y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
+ x2 = axisx.min;
+ }
+
+ // clip with xmax
+ if (x1 >= x2 && x1 > axisx.max) {
+ if (x2 > axisx.max)
+ continue;
+ y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
+ x1 = axisx.max;
+ }
+ else if (x2 >= x1 && x2 > axisx.max) {
+ if (x1 > axisx.max)
+ continue;
+ y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
+ x2 = axisx.max;
+ }
+
+ if (x1 != prevx || y1 != prevy)
+ ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset);
+
+ prevx = x2;
+ prevy = y2;
+ ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset);
+ }
+ ctx.stroke();
+ }
+
+ function plotLineArea(datapoints, axisx, axisy) {
+ var points = datapoints.points,
+ ps = datapoints.pointsize,
+ bottom = Math.min(Math.max(0, axisy.min), axisy.max),
+ i = 0, top, areaOpen = false,
+ ypos = 1, segmentStart = 0, segmentEnd = 0;
+
+ // we process each segment in two turns, first forward
+ // direction to sketch out top, then once we hit the
+ // end we go backwards to sketch the bottom
+ while (true) {
+ if (ps > 0 && i > points.length + ps)
+ break;
+
+ i += ps; // ps is negative if going backwards
+
+ var x1 = points[i - ps],
+ y1 = points[i - ps + ypos],
+ x2 = points[i], y2 = points[i + ypos];
+
+ if (areaOpen) {
+ if (ps > 0 && x1 != null && x2 == null) {
+ // at turning point
+ segmentEnd = i;
+ ps = -ps;
+ ypos = 2;
+ continue;
+ }
+
+ if (ps < 0 && i == segmentStart + ps) {
+ // done with the reverse sweep
+ ctx.fill();
+ areaOpen = false;
+ ps = -ps;
+ ypos = 1;
+ i = segmentStart = segmentEnd + ps;
+ continue;
+ }
+ }
+
+ if (x1 == null || x2 == null)
+ continue;
+
+ // clip x values
+
+ // clip with xmin
+ if (x1 <= x2 && x1 < axisx.min) {
+ if (x2 < axisx.min)
+ continue;
+ y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
+ x1 = axisx.min;
+ }
+ else if (x2 <= x1 && x2 < axisx.min) {
+ if (x1 < axisx.min)
+ continue;
+ y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
+ x2 = axisx.min;
+ }
+
+ // clip with xmax
+ if (x1 >= x2 && x1 > axisx.max) {
+ if (x2 > axisx.max)
+ continue;
+ y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
+ x1 = axisx.max;
+ }
+ else if (x2 >= x1 && x2 > axisx.max) {
+ if (x1 > axisx.max)
+ continue;
+ y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
+ x2 = axisx.max;
+ }
+
+ if (!areaOpen) {
+ // open area
+ ctx.beginPath();
+ ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom));
+ areaOpen = true;
+ }
+
+ // now first check the case where both is outside
+ if (y1 >= axisy.max && y2 >= axisy.max) {
+ ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max));
+ ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max));
+ continue;
+ }
+ else if (y1 <= axisy.min && y2 <= axisy.min) {
+ ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min));
+ ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min));
+ continue;
+ }
+
+ // else it's a bit more complicated, there might
+ // be a flat maxed out rectangle first, then a
+ // triangular cutout or reverse; to find these
+ // keep track of the current x values
+ var x1old = x1, x2old = x2;
+
+ // clip the y values, without shortcutting, we
+ // go through all cases in turn
+
+ // clip with ymin
+ if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) {
+ x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
+ y1 = axisy.min;
+ }
+ else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) {
+ x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
+ y2 = axisy.min;
+ }
+
+ // clip with ymax
+ if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) {
+ x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
+ y1 = axisy.max;
+ }
+ else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) {
+ x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
+ y2 = axisy.max;
+ }
+
+ // if the x value was changed we got a rectangle
+ // to fill
+ if (x1 != x1old) {
+ ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1));
+ // it goes to (x1, y1), but we fill that below
+ }
+
+ // fill triangular section, this sometimes result
+ // in redundant points if (x1, y1) hasn't changed
+ // from previous line to, but we just ignore that
+ ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1));
+ ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
+
+ // fill the other rectangle if it's there
+ if (x2 != x2old) {
+ ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
+ ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2));
+ }
+ }
+ }
+
+ ctx.save();
+ ctx.translate(plotOffset.left, plotOffset.top);
+ ctx.lineJoin = "round";
+
+ var lw = series.lines.lineWidth,
+ sw = series.shadowSize;
+ // FIXME: consider another form of shadow when filling is turned on
+ if (lw > 0 && sw > 0) {
+ // draw shadow as a thick and thin line with transparency
+ ctx.lineWidth = sw;
+ ctx.strokeStyle = "rgba(0,0,0,0.1)";
+ // position shadow at angle from the mid of line
+ var angle = Math.PI/18;
+ plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis);
+ ctx.lineWidth = sw/2;
+ plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis);
+ }
+
+ ctx.lineWidth = lw;
+ ctx.strokeStyle = series.color;
+ var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight);
+ if (fillStyle) {
+ ctx.fillStyle = fillStyle;
+ plotLineArea(series.datapoints, series.xaxis, series.yaxis);
+ }
+
+ if (lw > 0)
+ plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis);
+ ctx.restore();
+ }
+
+ function drawSeriesPoints(series) {
+ function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) {
+ var points = datapoints.points, ps = datapoints.pointsize;
+
+ for (var i = 0; i < points.length; i += ps) {
+ var x = points[i], y = points[i + 1];
+ if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
+ continue;
+
+ ctx.beginPath();
+ x = axisx.p2c(x);
+ y = axisy.p2c(y) + offset;
+ if (symbol == "circle")
+ ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);
+ else
+ symbol(ctx, x, y, radius, shadow);
+ ctx.closePath();
+
+ if (fillStyle) {
+ ctx.fillStyle = fillStyle;
+ ctx.fill();
+ }
+ ctx.stroke();
+ }
+ }
+
+ ctx.save();
+ ctx.translate(plotOffset.left, plotOffset.top);
+
+ var lw = series.points.lineWidth,
+ sw = series.shadowSize,
+ radius = series.points.radius,
+ symbol = series.points.symbol;
+ if (lw > 0 && sw > 0) {
+ // draw shadow in two steps
+ var w = sw / 2;
+ ctx.lineWidth = w;
+ ctx.strokeStyle = "rgba(0,0,0,0.1)";
+ plotPoints(series.datapoints, radius, null, w + w/2, true,
+ series.xaxis, series.yaxis, symbol);
+
+ ctx.strokeStyle = "rgba(0,0,0,0.2)";
+ plotPoints(series.datapoints, radius, null, w/2, true,
+ series.xaxis, series.yaxis, symbol);
+ }
+
+ ctx.lineWidth = lw;
+ ctx.strokeStyle = series.color;
+ plotPoints(series.datapoints, radius,
+ getFillStyle(series.points, series.color), 0, false,
+ series.xaxis, series.yaxis, symbol);
+ ctx.restore();
+ }
+
+ function drawBar(x, y, b, barLeft, barRight, offset, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) {
+ var left, right, bottom, top,
+ drawLeft, drawRight, drawTop, drawBottom,
+ tmp;
+
+ // in horizontal mode, we start the bar from the left
+ // instead of from the bottom so it appears to be
+ // horizontal rather than vertical
+ if (horizontal) {
+ drawBottom = drawRight = drawTop = true;
+ drawLeft = false;
+ left = b;
+ right = x;
+ top = y + barLeft;
+ bottom = y + barRight;
+
+ // account for negative bars
+ if (right < left) {
+ tmp = right;
+ right = left;
+ left = tmp;
+ drawLeft = true;
+ drawRight = false;
+ }
+ }
+ else {
+ drawLeft = drawRight = drawTop = true;
+ drawBottom = false;
+ left = x + barLeft;
+ right = x + barRight;
+ bottom = b;
+ top = y;
+
+ // account for negative bars
+ if (top < bottom) {
+ tmp = top;
+ top = bottom;
+ bottom = tmp;
+ drawBottom = true;
+ drawTop = false;
+ }
+ }
+
+ // clip
+ if (right < axisx.min || left > axisx.max ||
+ top < axisy.min || bottom > axisy.max)
+ return;
+
+ if (left < axisx.min) {
+ left = axisx.min;
+ drawLeft = false;
+ }
+
+ if (right > axisx.max) {
+ right = axisx.max;
+ drawRight = false;
+ }
+
+ if (bottom < axisy.min) {
+ bottom = axisy.min;
+ drawBottom = false;
+ }
+
+ if (top > axisy.max) {
+ top = axisy.max;
+ drawTop = false;
+ }
+
+ left = axisx.p2c(left);
+ bottom = axisy.p2c(bottom);
+ right = axisx.p2c(right);
+ top = axisy.p2c(top);
+
+ // fill the bar
+ if (fillStyleCallback) {
+ c.beginPath();
+ c.moveTo(left, bottom);
+ c.lineTo(left, top);
+ c.lineTo(right, top);
+ c.lineTo(right, bottom);
+ c.fillStyle = fillStyleCallback(bottom, top);
+ c.fill();
+ }
+
+ // draw outline
+ if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) {
+ c.beginPath();
+
+ // FIXME: inline moveTo is buggy with excanvas
+ c.moveTo(left, bottom + offset);
+ if (drawLeft)
+ c.lineTo(left, top + offset);
+ else
+ c.moveTo(left, top + offset);
+ if (drawTop)
+ c.lineTo(right, top + offset);
+ else
+ c.moveTo(right, top + offset);
+ if (drawRight)
+ c.lineTo(right, bottom + offset);
+ else
+ c.moveTo(right, bottom + offset);
+ if (drawBottom)
+ c.lineTo(left, bottom + offset);
+ else
+ c.moveTo(left, bottom + offset);
+ c.stroke();
+ }
+ }
+
+ function drawSeriesBars(series) {
+ function plotBars(datapoints, barLeft, barRight, offset, fillStyleCallback, axisx, axisy) {
+ var points = datapoints.points, ps = datapoints.pointsize;
+
+ for (var i = 0; i < points.length; i += ps) {
+ if (points[i] == null)
+ continue;
+ drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, offset, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth);
+ }
+ }
+
+ ctx.save();
+ ctx.translate(plotOffset.left, plotOffset.top);
+
+ // FIXME: figure out a way to add shadows (for instance along the right edge)
+ ctx.lineWidth = series.bars.lineWidth;
+ ctx.strokeStyle = series.color;
+ var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2;
+ var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null;
+ plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, 0, fillStyleCallback, series.xaxis, series.yaxis);
+ ctx.restore();
+ }
+
+ function getFillStyle(filloptions, seriesColor, bottom, top) {
+ var fill = filloptions.fill;
+ if (!fill)
+ return null;
+
+ if (filloptions.fillColor)
+ return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor);
+
+ var c = $.color.parse(seriesColor);
+ c.a = typeof fill == "number" ? fill : 0.4;
+ c.normalize();
+ return c.toString();
+ }
+
+ function insertLegend() {
+ placeholder.find(".legend").remove();
+
+ if (!options.legend.show)
+ return;
+
+ var fragments = [], rowStarted = false,
+ lf = options.legend.labelFormatter, s, label;
+ for (var i = 0; i < series.length; ++i) {
+ s = series[i];
+ label = s.label;
+ if (!label)
+ continue;
+
+ if (i % options.legend.noColumns == 0) {
+ if (rowStarted)
+ fragments.push('</tr>');
+ fragments.push('<tr>');
+ rowStarted = true;
+ }
+
+ if (lf)
+ label = lf(label, s);
+
+ fragments.push(
+ '<td class="legendColorBox"><div style="border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px"><div style="width:4px;height:0;border:5px solid ' + s.color + ';overflow:hidden"></div></div></td>' +
+ '<td class="legendLabel">' + label + '</td>');
+ }
+ if (rowStarted)
+ fragments.push('</tr>');
+
+ if (fragments.length == 0)
+ return;
+
+ var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join("") + '</table>';
+ if (options.legend.container != null)
+ $(options.legend.container).html(table);
+ else {
+ var pos = "",
+ p = options.legend.position,
+ m = options.legend.margin;
+ if (m[0] == null)
+ m = [m, m];
+ if (p.charAt(0) == "n")
+ pos += 'top:' + (m[1] + plotOffset.top) + 'px;';
+ else if (p.charAt(0) == "s")
+ pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;';
+ if (p.charAt(1) == "e")
+ pos += 'right:' + (m[0] + plotOffset.right) + 'px;';
+ else if (p.charAt(1) == "w")
+ pos += 'left:' + (m[0] + plotOffset.left) + 'px;';
+ var legend = $('<div class="legend">' + table.replace('style="', 'style="position:absolute;' + pos +';') + '</div>').appendTo(placeholder);
+ if (options.legend.backgroundOpacity != 0.0) {
+ // put in the transparent background
+ // separately to avoid blended labels and
+ // label boxes
+ var c = options.legend.backgroundColor;
+ if (c == null) {
+ c = options.grid.backgroundColor;
+ if (c && typeof c == "string")
+ c = $.color.parse(c);
+ else
+ c = $.color.extract(legend, 'background-color');
+ c.a = 1;
+ c = c.toString();
+ }
+ var div = legend.children();
+ $('<div style="position:absolute;width:' + div.width() + 'px;height:' + div.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').prependTo(legend).css('opacity', options.legend.backgroundOpacity);
+ }
+ }
+ }
+
+
+ // interactive features
+
+ var highlights = [],
+ redrawTimeout = null;
+
+ // returns the data item the mouse is over, or null if none is found
+ function findNearbyItem(mouseX, mouseY, seriesFilter) {
+ var maxDistance = options.grid.mouseActiveRadius,
+ smallestDistance = maxDistance * maxDistance + 1,
+ item = null, foundPoint = false, i, j;
+
+ for (i = series.length - 1; i >= 0; --i) {
+ if (!seriesFilter(series[i]))
+ continue;
+
+ var s = series[i],
+ axisx = s.xaxis,
+ axisy = s.yaxis,
+ points = s.datapoints.points,
+ ps = s.datapoints.pointsize,
+ mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster
+ my = axisy.c2p(mouseY),
+ maxx = maxDistance / axisx.scale,
+ maxy = maxDistance / axisy.scale;
+
+ // with inverse transforms, we can't use the maxx/maxy
+ // optimization, sadly
+ if (axisx.options.inverseTransform)
+ maxx = Number.MAX_VALUE;
+ if (axisy.options.inverseTransform)
+ maxy = Number.MAX_VALUE;
+
+ if (s.lines.show || s.points.show) {
+ for (j = 0; j < points.length; j += ps) {
+ var x = points[j], y = points[j + 1];
+ if (x == null)
+ continue;
+
+ // For points and lines, the cursor must be within a
+ // certain distance to the data point
+ if (x - mx > maxx || x - mx < -maxx ||
+ y - my > maxy || y - my < -maxy)
+ continue;
+
+ // We have to calculate distances in pixels, not in
+ // data units, because the scales of the axes may be different
+ var dx = Math.abs(axisx.p2c(x) - mouseX),
+ dy = Math.abs(axisy.p2c(y) - mouseY),
+ dist = dx * dx + dy * dy; // we save the sqrt
+
+ // use <= to ensure last point takes precedence
+ // (last generally means on top of)
+ if (dist < smallestDistance) {
+ smallestDistance = dist;
+ item = [i, j / ps];
+ }
+ }
+ }
+
+ if (s.bars.show && !item) { // no other point can be nearby
+ var barLeft = s.bars.align == "left" ? 0 : -s.bars.barWidth/2,
+ barRight = barLeft + s.bars.barWidth;
+
+ for (j = 0; j < points.length; j += ps) {
+ var x = points[j], y = points[j + 1], b = points[j + 2];
+ if (x == null)
+ continue;
+
+ // for a bar graph, the cursor must be inside the bar
+ if (series[i].bars.horizontal ?
+ (mx <= Math.max(b, x) && mx >= Math.min(b, x) &&
+ my >= y + barLeft && my <= y + barRight) :
+ (mx >= x + barLeft && mx <= x + barRight &&
+ my >= Math.min(b, y) && my <= Math.max(b, y)))
+ item = [i, j / ps];
+ }
+ }
+ }
+
+ if (item) {
+ i = item[0];
+ j = item[1];
+ ps = series[i].datapoints.pointsize;
+
+ return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps),
+ dataIndex: j,
+ series: series[i],
+ seriesIndex: i };
+ }
+
+ return null;
+ }
+
+ function onMouseMove(e) {
+ if (options.grid.hoverable)
+ triggerClickHoverEvent("plothover", e,
+ function (s) { return s["hoverable"] != false; });
+ }
+
+ function onMouseLeave(e) {
+ if (options.grid.hoverable)
+ triggerClickHoverEvent("plothover", e,
+ function (s) { return false; });
+ }
+
+ function onClick(e) {
+ triggerClickHoverEvent("plotclick", e,
+ function (s) { return s["clickable"] != false; });
+ }
+
+ // trigger click or hover event (they send the same parameters
+ // so we share their code)
+ function triggerClickHoverEvent(eventname, event, seriesFilter) {
+ var offset = eventHolder.offset(),
+ canvasX = event.pageX - offset.left - plotOffset.left,
+ canvasY = event.pageY - offset.top - plotOffset.top,
+ pos = canvasToAxisCoords({ left: canvasX, top: canvasY });
+
+ pos.pageX = event.pageX;
+ pos.pageY = event.pageY;
+
+ var item = findNearbyItem(canvasX, canvasY, seriesFilter);
+
+ if (item) {
+ // fill in mouse pos for any listeners out there
+ item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left);
+ item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top);
+ }
+
+ if (options.grid.autoHighlight) {
+ // clear auto-highlights
+ for (var i = 0; i < highlights.length; ++i) {
+ var h = highlights[i];
+ if (h.auto == eventname &&
+ !(item && h.series == item.series &&
+ h.point[0] == item.datapoint[0] &&
+ h.point[1] == item.datapoint[1]))
+ unhighlight(h.series, h.point);
+ }
+
+ if (item)
+ highlight(item.series, item.datapoint, eventname);
+ }
+
+ placeholder.trigger(eventname, [ pos, item ]);
+ }
+
+ function triggerRedrawOverlay() {
+ if (!redrawTimeout)
+ redrawTimeout = setTimeout(drawOverlay, 30);
+ }
+
+ function drawOverlay() {
+ redrawTimeout = null;
+
+ // draw highlights
+ octx.save();
+ octx.clearRect(0, 0, canvasWidth, canvasHeight);
+ octx.translate(plotOffset.left, plotOffset.top);
+
+ var i, hi;
+ for (i = 0; i < highlights.length; ++i) {
+ hi = highlights[i];
+
+ if (hi.series.bars.show)
+ drawBarHighlight(hi.series, hi.point);
+ else
+ drawPointHighlight(hi.series, hi.point);
+ }
+ octx.restore();
+
+ executeHooks(hooks.drawOverlay, [octx]);
+ }
+
+ function highlight(s, point, auto) {
+ if (typeof s == "number")
+ s = series[s];
+
+ if (typeof point == "number") {
+ var ps = s.datapoints.pointsize;
+ point = s.datapoints.points.slice(ps * point, ps * (point + 1));
+ }
+
+ var i = indexOfHighlight(s, point);
+ if (i == -1) {
+ highlights.push({ series: s, point: point, auto: auto });
+
+ triggerRedrawOverlay();
+ }
+ else if (!auto)
+ highlights[i].auto = false;
+ }
+
+ function unhighlight(s, point) {
+ if (s == null && point == null) {
+ highlights = [];
+ triggerRedrawOverlay();
+ }
+
+ if (typeof s == "number")
+ s = series[s];
+
+ if (typeof point == "number")
+ point = s.data[point];
+
+ var i = indexOfHighlight(s, point);
+ if (i != -1) {
+ highlights.splice(i, 1);
+
+ triggerRedrawOverlay();
+ }
+ }
+
+ function indexOfHighlight(s, p) {
+ for (var i = 0; i < highlights.length; ++i) {
+ var h = highlights[i];
+ if (h.series == s && h.point[0] == p[0]
+ && h.point[1] == p[1])
+ return i;
+ }
+ return -1;
+ }
+
+ function drawPointHighlight(series, point) {
+ var x = point[0], y = point[1],
+ axisx = series.xaxis, axisy = series.yaxis;
+
+ if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
+ return;
+
+ var pointRadius = series.points.radius + series.points.lineWidth / 2;
+ octx.lineWidth = pointRadius;
+ octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString();
+ var radius = 1.5 * pointRadius,
+ x = axisx.p2c(x),
+ y = axisy.p2c(y);
+
+ octx.beginPath();
+ if (series.points.symbol == "circle")
+ octx.arc(x, y, radius, 0, 2 * Math.PI, false);
+ else
+ series.points.symbol(octx, x, y, radius, false);
+ octx.closePath();
+ octx.stroke();
+ }
+
+ function drawBarHighlight(series, point) {
+ octx.lineWidth = series.bars.lineWidth;
+ octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString();
+ var fillStyle = $.color.parse(series.color).scale('a', 0.5).toString();
+ var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2;
+ drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth,
+ 0, function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth);
+ }
+
+ function getColorOrGradient(spec, bottom, top, defaultColor) {
+ if (typeof spec == "string")
+ return spec;
+ else {
+ // assume this is a gradient spec; IE currently only
+ // supports a simple vertical gradient properly, so that's
+ // what we support too
+ var gradient = ctx.createLinearGradient(0, top, 0, bottom);
+
+ for (var i = 0, l = spec.colors.length; i < l; ++i) {
+ var c = spec.colors[i];
+ if (typeof c != "string") {
+ var co = $.color.parse(defaultColor);
+ if (c.brightness != null)
+ co = co.scale('rgb', c.brightness)
+ if (c.opacity != null)
+ co.a *= c.opacity;
+ c = co.toString();
+ }
+ gradient.addColorStop(i / (l - 1), c);
+ }
+
+ return gradient;
+ }
+ }
+ }
+
+ $.plot = function(placeholder, data, options) {
+ //var t0 = new Date();
+ var plot = new Plot($(placeholder), data, options, $.plot.plugins);
+ //(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime()));
+ return plot;
+ };
+
+ $.plot.version = "0.7";
+
+ $.plot.plugins = [];
+
+ // returns a string with the date d formatted according to fmt
+ $.plot.formatDate = function(d, fmt, monthNames) {
+ var leftPad = function(n) {
+ n = "" + n;
+ return n.length == 1 ? "0" + n : n;
+ };
+
+ var r = [];
+ var escape = false, padNext = false;
+ var hours = d.getUTCHours();
+ var isAM = hours < 12;
+ if (monthNames == null)
+ monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
+
+ if (fmt.search(/%p|%P/) != -1) {
+ if (hours > 12) {
+ hours = hours - 12;
+ } else if (hours == 0) {
+ hours = 12;
+ }
+ }
+ for (var i = 0; i < fmt.length; ++i) {
+ var c = fmt.charAt(i);
+
+ if (escape) {
+ switch (c) {
+ case 'h': c = "" + hours; break;
+ case 'H': c = leftPad(hours); break;
+ case 'M': c = leftPad(d.getUTCMinutes()); break;
+ case 'S': c = leftPad(d.getUTCSeconds()); break;
+ case 'd': c = "" + d.getUTCDate(); break;
+ case 'm': c = "" + (d.getUTCMonth() + 1); break;
+ case 'y': c = "" + d.getUTCFullYear(); break;
+ case 'b': c = "" + monthNames[d.getUTCMonth()]; break;
+ case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
+ case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
+ case '0': c = ""; padNext = true; break;
+ }
+ if (c && padNext) {
+ c = leftPad(c);
+ padNext = false;
+ }
+ r.push(c);
+ if (!padNext)
+ escape = false;
+ }
+ else {
+ if (c == "%")
+ escape = true;
+ else
+ r.push(c);
+ }
+ }
+ return r.join("");
+ };
+
+ // round to nearby lower multiple of base
+ function floorInBase(n, base) {
+ return base * Math.floor(n / base);
+ }
+
+})(jQuery);
diff --git a/storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery.js b/storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery.js
new file mode 100644
index 00000000000..8c24ffc610d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/missing-sources/jquery.js
@@ -0,0 +1,9472 @@
+/*!
+ * jQuery JavaScript Library v1.8.3
+ * http://jquery.com/
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ *
+ * Copyright 2012 jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time)
+ */
+(function( window, undefined ) {
+var
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // The deferred used on DOM ready
+ readyList,
+
+ // Use the correct document accordingly with window argument (sandbox)
+ document = window.document,
+ location = window.location,
+ navigator = window.navigator,
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // Save a reference to some core methods
+ core_push = Array.prototype.push,
+ core_slice = Array.prototype.slice,
+ core_indexOf = Array.prototype.indexOf,
+ core_toString = Object.prototype.toString,
+ core_hasOwn = Object.prototype.hasOwnProperty,
+ core_trim = String.prototype.trim,
+
+ // Define a local copy of jQuery
+ jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
+
+ // Used for matching numbers
+ core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,
+
+ // Used for detecting and trimming whitespace
+ core_rnotwhite = /\S/,
+ core_rspace = /\s+/,
+
+ // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
+ rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
+
+ // A simple way to check for HTML strings
+ // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+ rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
+
+ // JSON RegExp
+ rvalidchars = /^[\],:{}\s]*$/,
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
+ rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,
+
+ // Matches dashed string for camelizing
+ rmsPrefix = /^-ms-/,
+ rdashAlpha = /-([\da-z])/gi,
+
+ // Used by jQuery.camelCase as callback to replace()
+ fcamelCase = function( all, letter ) {
+ return ( letter + "" ).toUpperCase();
+ },
+
+ // The ready event handler and self cleanup method
+ DOMContentLoaded = function() {
+ if ( document.addEventListener ) {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ } else if ( document.readyState === "complete" ) {
+ // we're here because readyState === "complete" in oldIE
+ // which is good enough for us to call the dom ready!
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ },
+
+ // [[Class]] -> type pairs
+ class2type = {};
+
+jQuery.fn = jQuery.prototype = {
+ constructor: jQuery,
+ init: function( selector, context, rootjQuery ) {
+ var match, elem, ret, doc;
+
+ // Handle $(""), $(null), $(undefined), $(false)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+ // Assume that strings that start and end with <> are HTML and skip the regex check
+ match = [ null, selector, null ];
+
+ } else {
+ match = rquickExpr.exec( selector );
+ }
+
+ // Match html or make sure no context is specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
+ doc = ( context && context.nodeType ? context.ownerDocument || context : document );
+
+ // scripts is true for back-compat
+ selector = jQuery.parseHTML( match[1], doc, true );
+ if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+ this.attr.call( selector, context, true );
+ }
+
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $(#id)
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return ( context || rootjQuery ).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if ( selector.selector !== undefined ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.8.3",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return core_slice.call( this );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+
+ // Build a new jQuery matched element set
+ var ret = jQuery.merge( this.constructor(), elems );
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" ) {
+ ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
+ } else if ( name ) {
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+ }
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Add the callback
+ jQuery.ready.promise().done( fn );
+
+ return this;
+ },
+
+ eq: function( i ) {
+ i = +i;
+ return i === -1 ?
+ this.slice( i ) :
+ this.slice( i, i + 1 );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ slice: function() {
+ return this.pushStack( core_slice.apply( this, arguments ),
+ "slice", core_slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: core_push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+
+ // Abort if there are pending holds or we're already ready
+ if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+ return;
+ }
+
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.resolveWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger("ready").off("ready");
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type(obj) === "array";
+ },
+
+ isWindow: function( obj ) {
+ return obj != null && obj == obj.window;
+ },
+
+ isNumeric: function( obj ) {
+ return !isNaN( parseFloat(obj) ) && isFinite( obj );
+ },
+
+ type: function( obj ) {
+ return obj == null ?
+ String( obj ) :
+ class2type[ core_toString.call(obj) ] || "object";
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ try {
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !core_hasOwn.call(obj, "constructor") &&
+ !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+ } catch ( e ) {
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || core_hasOwn.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ var name;
+ for ( name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw new Error( msg );
+ },
+
+ // data: string of html
+ // context (optional): If specified, the fragment will be created in this context, defaults to document
+ // scripts (optional): If true, will include scripts passed in the html string
+ parseHTML: function( data, context, scripts ) {
+ var parsed;
+ if ( !data || typeof data !== "string" ) {
+ return null;
+ }
+ if ( typeof context === "boolean" ) {
+ scripts = context;
+ context = 0;
+ }
+ context = context || document;
+
+ // Single tag
+ if ( (parsed = rsingleTag.exec( data )) ) {
+ return [ context.createElement( parsed[1] ) ];
+ }
+
+ parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] );
+ return jQuery.merge( [],
+ (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes );
+ },
+
+ parseJSON: function( data ) {
+ if ( !data || typeof data !== "string") {
+ return null;
+ }
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ // Attempt to parse using the native JSON parser first
+ if ( window.JSON && window.JSON.parse ) {
+ return window.JSON.parse( data );
+ }
+
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+ .replace( rvalidtokens, "]" )
+ .replace( rvalidbraces, "")) ) {
+
+ return ( new Function( "return " + data ) )();
+
+ }
+ jQuery.error( "Invalid JSON: " + data );
+ },
+
+ // Cross-browser xml parsing
+ parseXML: function( data ) {
+ var xml, tmp;
+ if ( !data || typeof data !== "string" ) {
+ return null;
+ }
+ try {
+ if ( window.DOMParser ) { // Standard
+ tmp = new DOMParser();
+ xml = tmp.parseFromString( data , "text/xml" );
+ } else { // IE
+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
+ xml.async = "false";
+ xml.loadXML( data );
+ }
+ } catch( e ) {
+ xml = undefined;
+ }
+ if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
+ jQuery.error( "Invalid XML: " + data );
+ }
+ return xml;
+ },
+
+ noop: function() {},
+
+ // Evaluates a script in a global context
+ // Workarounds based on findings by Jim Driscoll
+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+ globalEval: function( data ) {
+ if ( data && core_rnotwhite.test( data ) ) {
+ // We use execScript on Internet Explorer
+ // We use an anonymous function so that context is window
+ // rather than jQuery in Firefox
+ ( window.execScript || function( data ) {
+ window[ "eval" ].call( window, data );
+ } )( data );
+ }
+ },
+
+ // Convert dashed to camelCase; used by the css and data modules
+ // Microsoft forgot to hump their vendor prefix (#9572)
+ camelCase: function( string ) {
+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+ },
+
+ // args is for internal usage only
+ each: function( obj, callback, args ) {
+ var name,
+ i = 0,
+ length = obj.length,
+ isObj = length === undefined || jQuery.isFunction( obj );
+
+ if ( args ) {
+ if ( isObj ) {
+ for ( name in obj ) {
+ if ( callback.apply( obj[ name ], args ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.apply( obj[ i++ ], args ) === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isObj ) {
+ for ( name in obj ) {
+ if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return obj;
+ },
+
+ // Use native String.trim function wherever possible
+ trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
+ function( text ) {
+ return text == null ?
+ "" :
+ core_trim.call( text );
+ } :
+
+ // Otherwise use our own trimming functionality
+ function( text ) {
+ return text == null ?
+ "" :
+ ( text + "" ).replace( rtrim, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( arr, results ) {
+ var type,
+ ret = results || [];
+
+ if ( arr != null ) {
+ // The window, strings (and functions) also have 'length'
+ // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
+ type = jQuery.type( arr );
+
+ if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) {
+ core_push.call( ret, arr );
+ } else {
+ jQuery.merge( ret, arr );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, arr, i ) {
+ var len;
+
+ if ( arr ) {
+ if ( core_indexOf ) {
+ return core_indexOf.call( arr, elem, i );
+ }
+
+ len = arr.length;
+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+ for ( ; i < len; i++ ) {
+ // Skip accessing in sparse arrays
+ if ( i in arr && arr[ i ] === elem ) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var l = second.length,
+ i = first.length,
+ j = 0;
+
+ if ( typeof l === "number" ) {
+ for ( ; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var retVal,
+ ret = [],
+ i = 0,
+ length = elems.length;
+ inv = !!inv;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( ; i < length; i++ ) {
+ retVal = !!callback( elems[ i ], i );
+ if ( inv !== retVal ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var value, key,
+ ret = [],
+ i = 0,
+ length = elems.length,
+ // jquery objects are treated as arrays
+ isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
+
+ // Go through the array, translating each of the items to their
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( key in elems ) {
+ value = callback( elems[ key ], key, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return ret.concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ var tmp, args, proxy;
+
+ if ( typeof context === "string" ) {
+ tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ args = core_slice.call( arguments, 2 );
+ proxy = function() {
+ return fn.apply( context, args.concat( core_slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ // Multifunctional method to get and set values of a collection
+ // The value/s can optionally be executed if it's a function
+ access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
+ var exec,
+ bulk = key == null,
+ i = 0,
+ length = elems.length;
+
+ // Sets many values
+ if ( key && typeof key === "object" ) {
+ for ( i in key ) {
+ jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
+ }
+ chainable = 1;
+
+ // Sets one value
+ } else if ( value !== undefined ) {
+ // Optionally, function values get executed if exec is true
+ exec = pass === undefined && jQuery.isFunction( value );
+
+ if ( bulk ) {
+ // Bulk operations only iterate when executing function values
+ if ( exec ) {
+ exec = fn;
+ fn = function( elem, key, value ) {
+ return exec.call( jQuery( elem ), value );
+ };
+
+ // Otherwise they run against the entire set
+ } else {
+ fn.call( elems, value );
+ fn = null;
+ }
+ }
+
+ if ( fn ) {
+ for (; i < length; i++ ) {
+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+ }
+ }
+
+ chainable = 1;
+ }
+
+ return chainable ?
+ elems :
+
+ // Gets
+ bulk ?
+ fn.call( elems ) :
+ length ? fn( elems[0], key ) : emptyGet;
+ },
+
+ now: function() {
+ return ( new Date() ).getTime();
+ }
+});
+
+jQuery.ready.promise = function( obj ) {
+ if ( !readyList ) {
+
+ readyList = jQuery.Deferred();
+
+ // Catch cases where $(document).ready() is called after the browser event has already occurred.
+ // we once tried to use readyState "interactive" here, but it caused issues like the one
+ // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ setTimeout( jQuery.ready, 1 );
+
+ // Standards-based browsers support DOMContentLoaded
+ } else if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
+
+ // If IE event model is used
+ } else {
+ // Ensure firing before onload, maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", DOMContentLoaded );
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var top = false;
+
+ try {
+ top = window.frameElement == null && document.documentElement;
+ } catch(e) {}
+
+ if ( top && top.doScroll ) {
+ (function doScrollCheck() {
+ if ( !jQuery.isReady ) {
+
+ try {
+ // Use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ top.doScroll("left");
+ } catch(e) {
+ return setTimeout( doScrollCheck, 50 );
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+ }
+ })();
+ }
+ }
+ }
+ return readyList.promise( obj );
+};
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+// String to Object options format cache
+var optionsCache = {};
+
+// Convert String-formatted options into Object-formatted ones and store in cache
+function createOptions( options ) {
+ var object = optionsCache[ options ] = {};
+ jQuery.each( options.split( core_rspace ), function( _, flag ) {
+ object[ flag ] = true;
+ });
+ return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ * options: an optional list of space-separated options that will change how
+ * the callback list behaves or a more traditional option object
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible options:
+ *
+ * once: will ensure the callback list can only be fired once (like a Deferred)
+ *
+ * memory: will keep track of previous values and will call any callback added
+ * after the list has been fired right away with the latest "memorized"
+ * values (like a Deferred)
+ *
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
+ *
+ * stopOnFalse: interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( options ) {
+
+ // Convert options from String-formatted to Object-formatted if needed
+ // (we check in cache first)
+ options = typeof options === "string" ?
+ ( optionsCache[ options ] || createOptions( options ) ) :
+ jQuery.extend( {}, options );
+
+ var // Last fire value (for non-forgettable lists)
+ memory,
+ // Flag to know if list was already fired
+ fired,
+ // Flag to know if list is currently firing
+ firing,
+ // First callback to fire (used internally by add and fireWith)
+ firingStart,
+ // End of the loop when firing
+ firingLength,
+ // Index of currently firing callback (modified by remove if needed)
+ firingIndex,
+ // Actual callback list
+ list = [],
+ // Stack of fire calls for repeatable lists
+ stack = !options.once && [],
+ // Fire callbacks
+ fire = function( data ) {
+ memory = options.memory && data;
+ fired = true;
+ firingIndex = firingStart || 0;
+ firingStart = 0;
+ firingLength = list.length;
+ firing = true;
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+ if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+ memory = false; // To prevent further calls using add
+ break;
+ }
+ }
+ firing = false;
+ if ( list ) {
+ if ( stack ) {
+ if ( stack.length ) {
+ fire( stack.shift() );
+ }
+ } else if ( memory ) {
+ list = [];
+ } else {
+ self.disable();
+ }
+ }
+ },
+ // Actual Callbacks object
+ self = {
+ // Add a callback or a collection of callbacks to the list
+ add: function() {
+ if ( list ) {
+ // First, we save the current length
+ var start = list.length;
+ (function add( args ) {
+ jQuery.each( args, function( _, arg ) {
+ var type = jQuery.type( arg );
+ if ( type === "function" ) {
+ if ( !options.unique || !self.has( arg ) ) {
+ list.push( arg );
+ }
+ } else if ( arg && arg.length && type !== "string" ) {
+ // Inspect recursively
+ add( arg );
+ }
+ });
+ })( arguments );
+ // Do we need to add the callbacks to the
+ // current firing batch?
+ if ( firing ) {
+ firingLength = list.length;
+ // With memory, if we're not firing then
+ // we should call right away
+ } else if ( memory ) {
+ firingStart = start;
+ fire( memory );
+ }
+ }
+ return this;
+ },
+ // Remove a callback from the list
+ remove: function() {
+ if ( list ) {
+ jQuery.each( arguments, function( _, arg ) {
+ var index;
+ while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+ list.splice( index, 1 );
+ // Handle firing indexes
+ if ( firing ) {
+ if ( index <= firingLength ) {
+ firingLength--;
+ }
+ if ( index <= firingIndex ) {
+ firingIndex--;
+ }
+ }
+ }
+ });
+ }
+ return this;
+ },
+ // Control if a given callback is in the list
+ has: function( fn ) {
+ return jQuery.inArray( fn, list ) > -1;
+ },
+ // Remove all callbacks from the list
+ empty: function() {
+ list = [];
+ return this;
+ },
+ // Have the list do nothing anymore
+ disable: function() {
+ list = stack = memory = undefined;
+ return this;
+ },
+ // Is it disabled?
+ disabled: function() {
+ return !list;
+ },
+ // Lock the list in its current state
+ lock: function() {
+ stack = undefined;
+ if ( !memory ) {
+ self.disable();
+ }
+ return this;
+ },
+ // Is it locked?
+ locked: function() {
+ return !stack;
+ },
+ // Call all callbacks with the given context and arguments
+ fireWith: function( context, args ) {
+ args = args || [];
+ args = [ context, args.slice ? args.slice() : args ];
+ if ( list && ( !fired || stack ) ) {
+ if ( firing ) {
+ stack.push( args );
+ } else {
+ fire( args );
+ }
+ }
+ return this;
+ },
+ // Call all the callbacks with the given arguments
+ fire: function() {
+ self.fireWith( this, arguments );
+ return this;
+ },
+ // To know if the callbacks have already been called at least once
+ fired: function() {
+ return !!fired;
+ }
+ };
+
+ return self;
+};
+jQuery.extend({
+
+ Deferred: function( func ) {
+ var tuples = [
+ // action, add listener, listener list, final state
+ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+ [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+ [ "notify", "progress", jQuery.Callbacks("memory") ]
+ ],
+ state = "pending",
+ promise = {
+ state: function() {
+ return state;
+ },
+ always: function() {
+ deferred.done( arguments ).fail( arguments );
+ return this;
+ },
+ then: function( /* fnDone, fnFail, fnProgress */ ) {
+ var fns = arguments;
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( tuples, function( i, tuple ) {
+ var action = tuple[ 0 ],
+ fn = fns[ i ];
+ // deferred[ done | fail | progress ] for forwarding actions to newDefer
+ deferred[ tuple[1] ]( jQuery.isFunction( fn ) ?
+ function() {
+ var returned = fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise()
+ .done( newDefer.resolve )
+ .fail( newDefer.reject )
+ .progress( newDefer.notify );
+ } else {
+ newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
+ }
+ } :
+ newDefer[ action ]
+ );
+ });
+ fns = null;
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ return obj != null ? jQuery.extend( obj, promise ) : promise;
+ }
+ },
+ deferred = {};
+
+ // Keep pipe for back-compat
+ promise.pipe = promise.then;
+
+ // Add list-specific methods
+ jQuery.each( tuples, function( i, tuple ) {
+ var list = tuple[ 2 ],
+ stateString = tuple[ 3 ];
+
+ // promise[ done | fail | progress ] = list.add
+ promise[ tuple[1] ] = list.add;
+
+ // Handle state
+ if ( stateString ) {
+ list.add(function() {
+ // state = [ resolved | rejected ]
+ state = stateString;
+
+ // [ reject_list | resolve_list ].disable; progress_list.lock
+ }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+ }
+
+ // deferred[ resolve | reject | notify ] = list.fire
+ deferred[ tuple[0] ] = list.fire;
+ deferred[ tuple[0] + "With" ] = list.fireWith;
+ });
+
+ // Make the deferred a promise
+ promise.promise( deferred );
+
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+
+ // All done!
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( subordinate /* , ..., subordinateN */ ) {
+ var i = 0,
+ resolveValues = core_slice.call( arguments ),
+ length = resolveValues.length,
+
+ // the count of uncompleted subordinates
+ remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+ // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+ deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+ // Update function for both resolve and progress values
+ updateFunc = function( i, contexts, values ) {
+ return function( value ) {
+ contexts[ i ] = this;
+ values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
+ if( values === progressValues ) {
+ deferred.notifyWith( contexts, values );
+ } else if ( !( --remaining ) ) {
+ deferred.resolveWith( contexts, values );
+ }
+ };
+ },
+
+ progressValues, progressContexts, resolveContexts;
+
+ // add listeners to Deferred subordinates; treat others as resolved
+ if ( length > 1 ) {
+ progressValues = new Array( length );
+ progressContexts = new Array( length );
+ resolveContexts = new Array( length );
+ for ( ; i < length; i++ ) {
+ if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+ resolveValues[ i ].promise()
+ .done( updateFunc( i, resolveContexts, resolveValues ) )
+ .fail( deferred.reject )
+ .progress( updateFunc( i, progressContexts, progressValues ) );
+ } else {
+ --remaining;
+ }
+ }
+ }
+
+ // if we're not waiting on anything, resolve the master
+ if ( !remaining ) {
+ deferred.resolveWith( resolveContexts, resolveValues );
+ }
+
+ return deferred.promise();
+ }
+});
+jQuery.support = (function() {
+
+ var support,
+ all,
+ a,
+ select,
+ opt,
+ input,
+ fragment,
+ eventName,
+ i,
+ isSupported,
+ clickFn,
+ div = document.createElement("div");
+
+ // Setup
+ div.setAttribute( "className", "t" );
+ div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
+
+ // Support tests won't run in some limited or non-browser environments
+ all = div.getElementsByTagName("*");
+ a = div.getElementsByTagName("a")[ 0 ];
+ if ( !all || !a || !all.length ) {
+ return {};
+ }
+
+ // First batch of tests
+ select = document.createElement("select");
+ opt = select.appendChild( document.createElement("option") );
+ input = div.getElementsByTagName("input")[ 0 ];
+
+ a.style.cssText = "top:1px;float:left;opacity:.5";
+ support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: ( div.firstChild.nodeType === 3 ),
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName("tbody").length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName("link").length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText instead)
+ style: /top/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: ( a.getAttribute("href") === "/a" ),
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ // Use a regex to work around a WebKit issue. See #5145
+ opacity: /^0.5/.test( a.style.opacity ),
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Make sure that if no value is specified for a checkbox
+ // that it defaults to "on".
+ // (WebKit defaults to "" instead)
+ checkOn: ( input.value === "on" ),
+
+ // Make sure that a selected-by-default option has a working selected property.
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+ optSelected: opt.selected,
+
+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+ getSetAttribute: div.className !== "t",
+
+ // Tests for enctype support on a form (#6743)
+ enctype: !!document.createElement("form").enctype,
+
+ // Makes sure cloning an html5 element does not cause problems
+ // Where outerHTML is undefined, this still works
+ html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
+
+ // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode
+ boxModel: ( document.compatMode === "CSS1Compat" ),
+
+ // Will be defined later
+ submitBubbles: true,
+ changeBubbles: true,
+ focusinBubbles: false,
+ deleteExpando: true,
+ noCloneEvent: true,
+ inlineBlockNeedsLayout: false,
+ shrinkWrapBlocks: false,
+ reliableMarginRight: true,
+ boxSizingReliable: true,
+ pixelPosition: false
+ };
+
+ // Make sure checked status is properly cloned
+ input.checked = true;
+ support.noCloneChecked = input.cloneNode( true ).checked;
+
+ // Make sure that the options inside disabled selects aren't marked as disabled
+ // (WebKit marks them as disabled)
+ select.disabled = true;
+ support.optDisabled = !opt.disabled;
+
+ // Test to see if it's possible to delete an expando from an element
+ // Fails in Internet Explorer
+ try {
+ delete div.test;
+ } catch( e ) {
+ support.deleteExpando = false;
+ }
+
+ if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
+ div.attachEvent( "onclick", clickFn = function() {
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ support.noCloneEvent = false;
+ });
+ div.cloneNode( true ).fireEvent("onclick");
+ div.detachEvent( "onclick", clickFn );
+ }
+
+ // Check if a radio maintains its value
+ // after being appended to the DOM
+ input = document.createElement("input");
+ input.value = "t";
+ input.setAttribute( "type", "radio" );
+ support.radioValue = input.value === "t";
+
+ input.setAttribute( "checked", "checked" );
+
+ // #11217 - WebKit loses check when the name is after the checked attribute
+ input.setAttribute( "name", "t" );
+
+ div.appendChild( input );
+ fragment = document.createDocumentFragment();
+ fragment.appendChild( div.lastChild );
+
+ // WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ // Check if a disconnected checkbox will retain its checked
+ // value of true after appended to the DOM (IE6/7)
+ support.appendChecked = input.checked;
+
+ fragment.removeChild( input );
+ fragment.appendChild( div );
+
+ // Technique from Juriy Zaytsev
+ // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
+ // We only care about the case where non-standard event systems
+ // are used, namely in IE. Short-circuiting here helps us to
+ // avoid an eval call (in setAttribute) which can cause CSP
+ // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
+ if ( div.attachEvent ) {
+ for ( i in {
+ submit: true,
+ change: true,
+ focusin: true
+ }) {
+ eventName = "on" + i;
+ isSupported = ( eventName in div );
+ if ( !isSupported ) {
+ div.setAttribute( eventName, "return;" );
+ isSupported = ( typeof div[ eventName ] === "function" );
+ }
+ support[ i + "Bubbles" ] = isSupported;
+ }
+ }
+
+ // Run tests that need a body at doc ready
+ jQuery(function() {
+ var container, div, tds, marginDiv,
+ divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;",
+ body = document.getElementsByTagName("body")[0];
+
+ if ( !body ) {
+ // Return for frameset docs that don't have a body
+ return;
+ }
+
+ container = document.createElement("div");
+ container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px";
+ body.insertBefore( container, body.firstChild );
+
+ // Construct the test element
+ div = document.createElement("div");
+ container.appendChild( div );
+
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ // (only IE 8 fails this test)
+ div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
+ tds = div.getElementsByTagName("td");
+ tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+ tds[ 0 ].style.display = "";
+ tds[ 1 ].style.display = "none";
+
+ // Check if empty table cells still have offsetWidth/Height
+ // (IE <= 8 fail this test)
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+
+ // Check box-sizing and margin behavior
+ div.innerHTML = "";
+ div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
+ support.boxSizing = ( div.offsetWidth === 4 );
+ support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );
+
+ // NOTE: To any future maintainer, we've window.getComputedStyle
+ // because jsdom on node.js will break without it.
+ if ( window.getComputedStyle ) {
+ support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
+ support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
+
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. For more
+ // info see bug #3333
+ // Fails in WebKit before Feb 2011 nightlies
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ marginDiv = document.createElement("div");
+ marginDiv.style.cssText = div.style.cssText = divReset;
+ marginDiv.style.marginRight = marginDiv.style.width = "0";
+ div.style.width = "1px";
+ div.appendChild( marginDiv );
+ support.reliableMarginRight =
+ !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
+ }
+
+ if ( typeof div.style.zoom !== "undefined" ) {
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ // (IE < 8 does this)
+ div.innerHTML = "";
+ div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
+
+ // Check if elements with layout shrink-wrap their children
+ // (IE 6 does this)
+ div.style.display = "block";
+ div.style.overflow = "visible";
+ div.innerHTML = "<div></div>";
+ div.firstChild.style.width = "5px";
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
+
+ container.style.zoom = 1;
+ }
+
+ // Null elements to avoid leaks in IE
+ body.removeChild( container );
+ container = div = tds = marginDiv = null;
+ });
+
+ // Null elements to avoid leaks in IE
+ fragment.removeChild( div );
+ all = a = select = opt = input = fragment = div = null;
+
+ return support;
+})();
+var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
+ rmultiDash = /([A-Z])/g;
+
+jQuery.extend({
+ cache: {},
+
+ deletedIds: [],
+
+ // Remove at next major release (1.9/2.0)
+ uuid: 0,
+
+ // Unique for each copy of jQuery on the page
+ // Non-digits removed to match rinlinejQuery
+ expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
+
+ // The following elements throw uncatchable exceptions if you
+ // attempt to add expando properties to them.
+ noData: {
+ "embed": true,
+ // Ban all objects except for Flash (which handle expandos)
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+ "applet": true
+ },
+
+ hasData: function( elem ) {
+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+ return !!elem && !isEmptyDataObject( elem );
+ },
+
+ data: function( elem, name, data, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var thisCache, ret,
+ internalKey = jQuery.expando,
+ getByName = typeof name === "string",
+
+ // We have to handle DOM nodes and JS objects differently because IE6-7
+ // can't GC object references properly across the DOM-JS boundary
+ isNode = elem.nodeType,
+
+ // Only DOM nodes need the global jQuery cache; JS object data is
+ // attached directly to the object so GC can occur automatically
+ cache = isNode ? jQuery.cache : elem,
+
+ // Only defining an ID for JS objects if its cache already exists allows
+ // the code to shortcut on the same path as a DOM node with no cache
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
+
+ // Avoid doing any more work than we need to when trying to get data on an
+ // object that has no data at all
+ if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
+ return;
+ }
+
+ if ( !id ) {
+ // Only DOM nodes need a new unique ID for each element since their data
+ // ends up in the global cache
+ if ( isNode ) {
+ elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++;
+ } else {
+ id = internalKey;
+ }
+ }
+
+ if ( !cache[ id ] ) {
+ cache[ id ] = {};
+
+ // Avoids exposing jQuery metadata on plain JS objects when the object
+ // is serialized using JSON.stringify
+ if ( !isNode ) {
+ cache[ id ].toJSON = jQuery.noop;
+ }
+ }
+
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
+ // shallow copied over onto the existing cache
+ if ( typeof name === "object" || typeof name === "function" ) {
+ if ( pvt ) {
+ cache[ id ] = jQuery.extend( cache[ id ], name );
+ } else {
+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+ }
+ }
+
+ thisCache = cache[ id ];
+
+ // jQuery data() is stored in a separate object inside the object's internal data
+ // cache in order to avoid key collisions between internal data and user-defined
+ // data.
+ if ( !pvt ) {
+ if ( !thisCache.data ) {
+ thisCache.data = {};
+ }
+
+ thisCache = thisCache.data;
+ }
+
+ if ( data !== undefined ) {
+ thisCache[ jQuery.camelCase( name ) ] = data;
+ }
+
+ // Check for both converted-to-camel and non-converted data property names
+ // If a data property was specified
+ if ( getByName ) {
+
+ // First Try to find as-is property data
+ ret = thisCache[ name ];
+
+ // Test for null|undefined property data
+ if ( ret == null ) {
+
+ // Try to find the camelCased property
+ ret = thisCache[ jQuery.camelCase( name ) ];
+ }
+ } else {
+ ret = thisCache;
+ }
+
+ return ret;
+ },
+
+ removeData: function( elem, name, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var thisCache, i, l,
+
+ isNode = elem.nodeType,
+
+ // See jQuery.data for more information
+ cache = isNode ? jQuery.cache : elem,
+ id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
+
+ // If there is already no cache entry for this object, there is no
+ // purpose in continuing
+ if ( !cache[ id ] ) {
+ return;
+ }
+
+ if ( name ) {
+
+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+ if ( thisCache ) {
+
+ // Support array or space separated string names for data keys
+ if ( !jQuery.isArray( name ) ) {
+
+ // try the string as a key before any manipulation
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+
+ // split the camel cased version by spaces unless a key with the spaces exists
+ name = jQuery.camelCase( name );
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+ name = name.split(" ");
+ }
+ }
+ }
+
+ for ( i = 0, l = name.length; i < l; i++ ) {
+ delete thisCache[ name[i] ];
+ }
+
+ // If there is no data left in the cache, we want to continue
+ // and let the cache object itself get destroyed
+ if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
+ return;
+ }
+ }
+ }
+
+ // See jQuery.data for more information
+ if ( !pvt ) {
+ delete cache[ id ].data;
+
+ // Don't destroy the parent cache unless the internal data object
+ // had been the only thing left in it
+ if ( !isEmptyDataObject( cache[ id ] ) ) {
+ return;
+ }
+ }
+
+ // Destroy the cache
+ if ( isNode ) {
+ jQuery.cleanData( [ elem ], true );
+
+ // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
+ } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
+ delete cache[ id ];
+
+ // When all else fails, null
+ } else {
+ cache[ id ] = null;
+ }
+ },
+
+ // For internal use only.
+ _data: function( elem, name, data ) {
+ return jQuery.data( elem, name, data, true );
+ },
+
+ // A method for determining if a DOM node can handle the data expando
+ acceptData: function( elem ) {
+ var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+ // nodes accept data unless otherwise specified; rejection can be conditional
+ return !noData || noData !== true && elem.getAttribute("classid") === noData;
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ var parts, part, attr, name, l,
+ elem = this[0],
+ i = 0,
+ data = null;
+
+ // Gets all values
+ if ( key === undefined ) {
+ if ( this.length ) {
+ data = jQuery.data( elem );
+
+ if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+ attr = elem.attributes;
+ for ( l = attr.length; i < l; i++ ) {
+ name = attr[i].name;
+
+ if ( !name.indexOf( "data-" ) ) {
+ name = jQuery.camelCase( name.substring(5) );
+
+ dataAttr( elem, name, data[ name ] );
+ }
+ }
+ jQuery._data( elem, "parsedAttrs", true );
+ }
+ }
+
+ return data;
+ }
+
+ // Sets multiple values
+ if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ parts = key.split( ".", 2 );
+ parts[1] = parts[1] ? "." + parts[1] : "";
+ part = parts[1] + "!";
+
+ return jQuery.access( this, function( value ) {
+
+ if ( value === undefined ) {
+ data = this.triggerHandler( "getData" + part, [ parts[0] ] );
+
+ // Try to fetch any internally stored data first
+ if ( data === undefined && elem ) {
+ data = jQuery.data( elem, key );
+ data = dataAttr( elem, key, data );
+ }
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+ }
+
+ parts[1] = value;
+ this.each(function() {
+ var self = jQuery( this );
+
+ self.triggerHandler( "setData" + part, parts );
+ jQuery.data( this, key, value );
+ self.triggerHandler( "changeData" + part, parts );
+ });
+ }, null, value, arguments.length > 1, null, false );
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+
+function dataAttr( elem, key, data ) {
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+
+ var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+
+ data = elem.getAttribute( name );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ // Only convert to a number if it doesn't change the string
+ +data + "" === data ? +data :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ jQuery.data( elem, key, data );
+
+ } else {
+ data = undefined;
+ }
+ }
+
+ return data;
+}
+
+// checks a cache object for emptiness
+function isEmptyDataObject( obj ) {
+ var name;
+ for ( name in obj ) {
+
+ // if the public data object is empty, the private is still empty
+ if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+ continue;
+ }
+ if ( name !== "toJSON" ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+jQuery.extend({
+ queue: function( elem, type, data ) {
+ var queue;
+
+ if ( elem ) {
+ type = ( type || "fx" ) + "queue";
+ queue = jQuery._data( elem, type );
+
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !queue || jQuery.isArray(data) ) {
+ queue = jQuery._data( elem, type, jQuery.makeArray(data) );
+ } else {
+ queue.push( data );
+ }
+ }
+ return queue || [];
+ }
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ startLength = queue.length,
+ fn = queue.shift(),
+ hooks = jQuery._queueHooks( elem, type ),
+ next = function() {
+ jQuery.dequeue( elem, type );
+ };
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ startLength--;
+ }
+
+ if ( fn ) {
+
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift( "inprogress" );
+ }
+
+ // clear up the last queue stop function
+ delete hooks.stop;
+ fn.call( elem, next, hooks );
+ }
+
+ if ( !startLength && hooks ) {
+ hooks.empty.fire();
+ }
+ },
+
+ // not intended for public consumption - generates a queueHooks object, or returns the current one
+ _queueHooks: function( elem, type ) {
+ var key = type + "queueHooks";
+ return jQuery._data( elem, key ) || jQuery._data( elem, key, {
+ empty: jQuery.Callbacks("once memory").add(function() {
+ jQuery.removeData( elem, type + "queue", true );
+ jQuery.removeData( elem, key, true );
+ })
+ });
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ var setter = 2;
+
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ setter--;
+ }
+
+ if ( arguments.length < setter ) {
+ return jQuery.queue( this[0], type );
+ }
+
+ return data === undefined ?
+ this :
+ this.each(function() {
+ var queue = jQuery.queue( this, type, data );
+
+ // ensure a hooks for this queue
+ jQuery._queueHooks( this, type );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+ // Based off of the plugin by Clint Helfers, with permission.
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function( next, hooks ) {
+ var timeout = setTimeout( next, time );
+ hooks.stop = function() {
+ clearTimeout( timeout );
+ };
+ });
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, obj ) {
+ var tmp,
+ count = 1,
+ defer = jQuery.Deferred(),
+ elements = this,
+ i = this.length,
+ resolve = function() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ };
+
+ if ( typeof type !== "string" ) {
+ obj = type;
+ type = undefined;
+ }
+ type = type || "fx";
+
+ while( i-- ) {
+ tmp = jQuery._data( elements[ i ], type + "queueHooks" );
+ if ( tmp && tmp.empty ) {
+ count++;
+ tmp.empty.add( resolve );
+ }
+ }
+ resolve();
+ return defer.promise( obj );
+ }
+});
+var nodeHook, boolHook, fixSpecified,
+ rclass = /[\t\r\n]/g,
+ rreturn = /\r/g,
+ rtype = /^(?:button|input)$/i,
+ rfocusable = /^(?:button|input|object|select|textarea)$/i,
+ rclickable = /^a(?:rea|)$/i,
+ rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
+ getSetAttribute = jQuery.support.getSetAttribute;
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
+ },
+
+ removeAttr: function( name ) {
+ return this.each(function() {
+ jQuery.removeAttr( this, name );
+ });
+ },
+
+ prop: function( name, value ) {
+ return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+ },
+
+ removeProp: function( name ) {
+ name = jQuery.propFix[ name ] || name;
+ return this.each(function() {
+ // try/catch handles cases where IE balks (such as removing a property on window)
+ try {
+ this[ name ] = undefined;
+ delete this[ name ];
+ } catch( e ) {}
+ });
+ },
+
+ addClass: function( value ) {
+ var classNames, i, l, elem,
+ setClass, c, cl;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).addClass( value.call(this, j, this.className) );
+ });
+ }
+
+ if ( value && typeof value === "string" ) {
+ classNames = value.split( core_rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.nodeType === 1 ) {
+ if ( !elem.className && classNames.length === 1 ) {
+ elem.className = value;
+
+ } else {
+ setClass = " " + elem.className + " ";
+
+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+ if ( setClass.indexOf( " " + classNames[ c ] + " " ) < 0 ) {
+ setClass += classNames[ c ] + " ";
+ }
+ }
+ elem.className = jQuery.trim( setClass );
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ var removes, className, elem, c, cl, i, l;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).removeClass( value.call(this, j, this.className) );
+ });
+ }
+ if ( (value && typeof value === "string") || value === undefined ) {
+ removes = ( value || "" ).split( core_rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+ if ( elem.nodeType === 1 && elem.className ) {
+
+ className = (" " + elem.className + " ").replace( rclass, " " );
+
+ // loop over each item in the removal list
+ for ( c = 0, cl = removes.length; c < cl; c++ ) {
+ // Remove until there is nothing to remove,
+ while ( className.indexOf(" " + removes[ c ] + " ") >= 0 ) {
+ className = className.replace( " " + removes[ c ] + " " , " " );
+ }
+ }
+ elem.className = value ? jQuery.trim( className ) : "";
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value,
+ isBool = typeof stateVal === "boolean";
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( i ) {
+ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className,
+ i = 0,
+ self = jQuery( this ),
+ state = stateVal,
+ classNames = value.split( core_rspace );
+
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space separated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
+
+ } else if ( type === "undefined" || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ jQuery._data( this, "__className__", this.className );
+ }
+
+ // toggle whole className
+ this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ",
+ i = 0,
+ l = this.length;
+ for ( ; i < l; i++ ) {
+ if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ val: function( value ) {
+ var hooks, ret, isFunction,
+ elem = this[0];
+
+ if ( !arguments.length ) {
+ if ( elem ) {
+ hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+ return ret;
+ }
+
+ ret = elem.value;
+
+ return typeof ret === "string" ?
+ // handle most common string cases
+ ret.replace(rreturn, "") :
+ // handle cases where value is null/undef or number
+ ret == null ? "" : ret;
+ }
+
+ return;
+ }
+
+ isFunction = jQuery.isFunction( value );
+
+ return this.each(function( i ) {
+ var val,
+ self = jQuery(this);
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call( this, i, self.val() );
+ } else {
+ val = value;
+ }
+
+ // Treat null/undefined as ""; convert numbers to string
+ if ( val == null ) {
+ val = "";
+ } else if ( typeof val === "number" ) {
+ val += "";
+ } else if ( jQuery.isArray( val ) ) {
+ val = jQuery.map(val, function ( value ) {
+ return value == null ? "" : value + "";
+ });
+ }
+
+ hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+ // If set returns undefined, fall back to normal setting
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ valHooks: {
+ option: {
+ get: function( elem ) {
+ // attributes.value is undefined in Blackberry 4.7 but
+ // uses .value. See #6932
+ var val = elem.attributes.value;
+ return !val || val.specified ? elem.value : elem.text;
+ }
+ },
+ select: {
+ get: function( elem ) {
+ var value, option,
+ options = elem.options,
+ index = elem.selectedIndex,
+ one = elem.type === "select-one" || index < 0,
+ values = one ? null : [],
+ max = one ? index + 1 : options.length,
+ i = index < 0 ?
+ max :
+ one ? index : 0;
+
+ // Loop through all the selected options
+ for ( ; i < max; i++ ) {
+ option = options[ i ];
+
+ // oldIE doesn't update selected after form reset (#2551)
+ if ( ( option.selected || i === index ) &&
+ // Don't return options that are disabled or in a disabled optgroup
+ ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
+ ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
+
+ // Get the specific value for the option
+ value = jQuery( option ).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ return values;
+ },
+
+ set: function( elem, value ) {
+ var values = jQuery.makeArray( value );
+
+ jQuery(elem).find("option").each(function() {
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+ });
+
+ if ( !values.length ) {
+ elem.selectedIndex = -1;
+ }
+ return values;
+ }
+ }
+ },
+
+ // Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9
+ attrFn: {},
+
+ attr: function( elem, name, value, pass ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // don't get/set attributes on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) {
+ return jQuery( elem )[ name ]( value );
+ }
+
+ // Fallback to prop when attributes are not supported
+ if ( typeof elem.getAttribute === "undefined" ) {
+ return jQuery.prop( elem, name, value );
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ // All attributes are lowercase
+ // Grab necessary hook if one is defined
+ if ( notxml ) {
+ name = name.toLowerCase();
+ hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
+ }
+
+ if ( value !== undefined ) {
+
+ if ( value === null ) {
+ jQuery.removeAttr( elem, name );
+ return;
+
+ } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ elem.setAttribute( name, value + "" );
+ return value;
+ }
+
+ } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+
+ ret = elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return ret === null ?
+ undefined :
+ ret;
+ }
+ },
+
+ removeAttr: function( elem, value ) {
+ var propName, attrNames, name, isBool,
+ i = 0;
+
+ if ( value && elem.nodeType === 1 ) {
+
+ attrNames = value.split( core_rspace );
+
+ for ( ; i < attrNames.length; i++ ) {
+ name = attrNames[ i ];
+
+ if ( name ) {
+ propName = jQuery.propFix[ name ] || name;
+ isBool = rboolean.test( name );
+
+ // See #9699 for explanation of this approach (setting first, then removal)
+ // Do not do this for boolean attributes (see #10870)
+ if ( !isBool ) {
+ jQuery.attr( elem, name, "" );
+ }
+ elem.removeAttribute( getSetAttribute ? name : propName );
+
+ // Set corresponding property to false for boolean attributes
+ if ( isBool && propName in elem ) {
+ elem[ propName ] = false;
+ }
+ }
+ }
+ }
+ },
+
+ attrHooks: {
+ type: {
+ set: function( elem, value ) {
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
+ jQuery.error( "type property can't be changed" );
+ } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+ // Setting the type on a radio button after the value resets the value in IE6-9
+ // Reset value to it's default in case type is set after value
+ // This is for element creation
+ var val = elem.value;
+ elem.setAttribute( "type", value );
+ if ( val ) {
+ elem.value = val;
+ }
+ return value;
+ }
+ }
+ },
+ // Use the value property for back compat
+ // Use the nodeHook for button elements in IE6/7 (#1954)
+ value: {
+ get: function( elem, name ) {
+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+ return nodeHook.get( elem, name );
+ }
+ return name in elem ?
+ elem.value :
+ null;
+ },
+ set: function( elem, value, name ) {
+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+ return nodeHook.set( elem, value, name );
+ }
+ // Does not return so that setAttribute is also used
+ elem.value = value;
+ }
+ }
+ },
+
+ propFix: {
+ tabindex: "tabIndex",
+ readonly: "readOnly",
+ "for": "htmlFor",
+ "class": "className",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ cellpadding: "cellPadding",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ usemap: "useMap",
+ frameborder: "frameBorder",
+ contenteditable: "contentEditable"
+ },
+
+ prop: function( elem, name, value ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // don't get/set properties on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ if ( notxml ) {
+ // Fix name and attach hooks
+ name = jQuery.propFix[ name ] || name;
+ hooks = jQuery.propHooks[ name ];
+ }
+
+ if ( value !== undefined ) {
+ if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ return ( elem[ name ] = value );
+ }
+
+ } else {
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+ return elem[ name ];
+ }
+ }
+ },
+
+ propHooks: {
+ tabIndex: {
+ get: function( elem ) {
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+ var attributeNode = elem.getAttributeNode("tabindex");
+
+ return attributeNode && attributeNode.specified ?
+ parseInt( attributeNode.value, 10 ) :
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+ 0 :
+ undefined;
+ }
+ }
+ }
+});
+
+// Hook for boolean attributes
+boolHook = {
+ get: function( elem, name ) {
+ // Align boolean attributes with corresponding properties
+ // Fall back to attribute presence where some booleans are not supported
+ var attrNode,
+ property = jQuery.prop( elem, name );
+ return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
+ name.toLowerCase() :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ var propName;
+ if ( value === false ) {
+ // Remove boolean attributes when set to false
+ jQuery.removeAttr( elem, name );
+ } else {
+ // value is true since we know at this point it's type boolean and not false
+ // Set boolean attributes to the same name and set the DOM property
+ propName = jQuery.propFix[ name ] || name;
+ if ( propName in elem ) {
+ // Only set the IDL specifically if it already exists on the element
+ elem[ propName ] = true;
+ }
+
+ elem.setAttribute( name, name.toLowerCase() );
+ }
+ return name;
+ }
+};
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !getSetAttribute ) {
+
+ fixSpecified = {
+ name: true,
+ id: true,
+ coords: true
+ };
+
+ // Use this for any attribute in IE6/7
+ // This fixes almost every IE6/7 issue
+ nodeHook = jQuery.valHooks.button = {
+ get: function( elem, name ) {
+ var ret;
+ ret = elem.getAttributeNode( name );
+ return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ?
+ ret.value :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ // Set the existing or create a new attribute node
+ var ret = elem.getAttributeNode( name );
+ if ( !ret ) {
+ ret = document.createAttribute( name );
+ elem.setAttributeNode( ret );
+ }
+ return ( ret.value = value + "" );
+ }
+ };
+
+ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
+ // This is for removals
+ jQuery.each([ "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ set: function( elem, value ) {
+ if ( value === "" ) {
+ elem.setAttribute( name, "auto" );
+ return value;
+ }
+ }
+ });
+ });
+
+ // Set contenteditable to false on removals(#10429)
+ // Setting to empty string throws an error as an invalid value
+ jQuery.attrHooks.contenteditable = {
+ get: nodeHook.get,
+ set: function( elem, value, name ) {
+ if ( value === "" ) {
+ value = "false";
+ }
+ nodeHook.set( elem, value, name );
+ }
+ };
+}
+
+
+// Some attributes require a special call on IE
+if ( !jQuery.support.hrefNormalized ) {
+ jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ get: function( elem ) {
+ var ret = elem.getAttribute( name, 2 );
+ return ret === null ? undefined : ret;
+ }
+ });
+ });
+}
+
+if ( !jQuery.support.style ) {
+ jQuery.attrHooks.style = {
+ get: function( elem ) {
+ // Return undefined in the case of empty string
+ // Normalize to lowercase since IE uppercases css property names
+ return elem.style.cssText.toLowerCase() || undefined;
+ },
+ set: function( elem, value ) {
+ return ( elem.style.cssText = value + "" );
+ }
+ };
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+ jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+ get: function( elem ) {
+ var parent = elem.parentNode;
+
+ if ( parent ) {
+ parent.selectedIndex;
+
+ // Make sure that it also works with optgroups, see #5701
+ if ( parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ }
+ return null;
+ }
+ });
+}
+
+// IE6/7 call enctype encoding
+if ( !jQuery.support.enctype ) {
+ jQuery.propFix.enctype = "encoding";
+}
+
+// Radios and checkboxes getter/setter
+if ( !jQuery.support.checkOn ) {
+ jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = {
+ get: function( elem ) {
+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ }
+ };
+ });
+}
+jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+ set: function( elem, value ) {
+ if ( jQuery.isArray( value ) ) {
+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+ }
+ }
+ });
+});
+var rformElems = /^(?:textarea|input|select)$/i,
+ rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/,
+ rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
+ rkeyEvent = /^key/,
+ rmouseEvent = /^(?:mouse|contextmenu)|click/,
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+ hoverHack = function( events ) {
+ return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
+ };
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+ add: function( elem, types, handler, data, selector ) {
+
+ var elemData, eventHandle, events,
+ t, tns, type, namespaces, handleObj,
+ handleObjIn, handlers, special;
+
+ // Don't attach events to noData or text/comment nodes (allow plain objects tho)
+ if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
+ return;
+ }
+
+ // Caller can pass in an object of custom data in lieu of the handler
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ selector = handleObjIn.selector;
+ }
+
+ // Make sure that the handler has a unique ID, used to find/remove it later
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure and main handler, if this is the first
+ events = elemData.events;
+ if ( !events ) {
+ elemData.events = events = {};
+ }
+ eventHandle = elemData.handle;
+ if ( !eventHandle ) {
+ elemData.handle = eventHandle = function( e ) {
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
+ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+ eventHandle.elem = elem;
+ }
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ types = jQuery.trim( hoverHack(types) ).split( " " );
+ for ( t = 0; t < types.length; t++ ) {
+
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = tns[1];
+ namespaces = ( tns[2] || "" ).split( "." ).sort();
+
+ // If event changes its type, use the special event handlers for the changed type
+ special = jQuery.event.special[ type ] || {};
+
+ // If selector defined, determine special event api type, otherwise given type
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+
+ // Update special based on newly reset type
+ special = jQuery.event.special[ type ] || {};
+
+ // handleObj is passed to all event handlers
+ handleObj = jQuery.extend({
+ type: type,
+ origType: tns[1],
+ data: data,
+ handler: handler,
+ guid: handler.guid,
+ selector: selector,
+ needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+ namespace: namespaces.join(".")
+ }, handleObjIn );
+
+ // Init the event handler queue if we're the first
+ handlers = events[ type ];
+ if ( !handlers ) {
+ handlers = events[ type ] = [];
+ handlers.delegateCount = 0;
+
+ // Only use addEventListener/attachEvent if the special events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add to the element's handler list, delegates in front
+ if ( selector ) {
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
+ } else {
+ handlers.push( handleObj );
+ }
+
+ // Keep track of which events have ever been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, selector, mappedTypes ) {
+
+ var t, tns, type, origType, namespaces, origCount,
+ j, events, special, eventType, handleObj,
+ elemData = jQuery.hasData( elem ) && jQuery._data( elem );
+
+ if ( !elemData || !(events = elemData.events) ) {
+ return;
+ }
+
+ // Once for each type.namespace in types; type may be omitted
+ types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
+ for ( t = 0; t < types.length; t++ ) {
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tns[1];
+ namespaces = tns[2];
+
+ // Unbind all events (on this namespace, if provided) for the element
+ if ( !type ) {
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+ }
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+ type = ( selector? special.delegateType : special.bindType ) || type;
+ eventType = events[ type ] || [];
+ origCount = eventType.length;
+ namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
+
+ // Remove matching events
+ for ( j = 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
+ ( !handler || handler.guid === handleObj.guid ) &&
+ ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
+ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+ eventType.splice( j--, 1 );
+
+ if ( handleObj.selector ) {
+ eventType.delegateCount--;
+ }
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+ }
+
+ // Remove generic event handler if we removed something and no more handlers exist
+ // (avoids potential for endless recursion during removal of special event handlers)
+ if ( eventType.length === 0 && origCount !== eventType.length ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ delete elemData.handle;
+
+ // removeData also checks for emptiness and clears the expando if empty
+ // so use it instead of delete
+ jQuery.removeData( elem, "events", true );
+ }
+ },
+
+ // Events that are safe to short-circuit if no handlers are attached.
+ // Native DOM events should not be added, they may have inline handlers.
+ customEvent: {
+ "getData": true,
+ "setData": true,
+ "changeData": true
+ },
+
+ trigger: function( event, data, elem, onlyHandlers ) {
+ // Don't do events on text and comment nodes
+ if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
+ return;
+ }
+
+ // Event object or event type
+ var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType,
+ type = event.type || event,
+ namespaces = [];
+
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+ return;
+ }
+
+ if ( type.indexOf( "!" ) >= 0 ) {
+ // Exclusive events trigger only for the exact event (no namespaces)
+ type = type.slice(0, -1);
+ exclusive = true;
+ }
+
+ if ( type.indexOf( "." ) >= 0 ) {
+ // Namespaced trigger; create a regexp to match event type in handle()
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespaces.sort();
+ }
+
+ if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
+ // No jQuery handlers for this event type, and it can't have inline handlers
+ return;
+ }
+
+ // Caller can pass in an Event, Object, or just an event type string
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[ jQuery.expando ] ? event :
+ // Object literal
+ new jQuery.Event( type, event ) :
+ // Just the event type (string)
+ new jQuery.Event( type );
+
+ event.type = type;
+ event.isTrigger = true;
+ event.exclusive = exclusive;
+ event.namespace = namespaces.join( "." );
+ event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
+ ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
+
+ // Handle a global trigger
+ if ( !elem ) {
+
+ // TODO: Stop taunting the data cache; remove global events and always attach to document
+ cache = jQuery.cache;
+ for ( i in cache ) {
+ if ( cache[ i ].events && cache[ i ].events[ type ] ) {
+ jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
+ }
+ }
+ return;
+ }
+
+ // Clean up the event in case it is being reused
+ event.result = undefined;
+ if ( !event.target ) {
+ event.target = elem;
+ }
+
+ // Clone any incoming data and prepend the event, creating the handler arg list
+ data = data != null ? jQuery.makeArray( data ) : [];
+ data.unshift( event );
+
+ // Allow special events to draw outside the lines
+ special = jQuery.event.special[ type ] || {};
+ if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
+ return;
+ }
+
+ // Determine event propagation path in advance, per W3C events spec (#9951)
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+ eventPath = [[ elem, special.bindType || type ]];
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+ bubbleType = special.delegateType || type;
+ cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
+ for ( old = elem; cur; cur = cur.parentNode ) {
+ eventPath.push([ cur, bubbleType ]);
+ old = cur;
+ }
+
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
+ if ( old === (elem.ownerDocument || document) ) {
+ eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
+ }
+ }
+
+ // Fire handlers on the event path
+ for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
+
+ cur = eventPath[i][0];
+ event.type = eventPath[i][1];
+
+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
+ if ( handle ) {
+ handle.apply( cur, data );
+ }
+ // Note that this is a bare JS function and not a jQuery handler
+ handle = ontype && cur[ ontype ];
+ if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
+ event.preventDefault();
+ }
+ }
+ event.type = type;
+
+ // If nobody prevented the default action, do it now
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+ if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
+ !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
+
+ // Call a native DOM method on the target with the same name name as the event.
+ // Can't use an .isFunction() check here because IE6/7 fails that test.
+ // Don't do default actions on window, that's where global variables be (#6170)
+ // IE<9 dies on focus/blur to hidden element (#1486)
+ if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
+
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ old = elem[ ontype ];
+
+ if ( old ) {
+ elem[ ontype ] = null;
+ }
+
+ // Prevent re-triggering of the same event, since we already bubbled it above
+ jQuery.event.triggered = type;
+ elem[ type ]();
+ jQuery.event.triggered = undefined;
+
+ if ( old ) {
+ elem[ ontype ] = old;
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ dispatch: function( event ) {
+
+ // Make a writable jQuery.Event from the native event object
+ event = jQuery.event.fix( event || window.event );
+
+ var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related,
+ handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
+ delegateCount = handlers.delegateCount,
+ args = core_slice.call( arguments ),
+ run_all = !event.exclusive && !event.namespace,
+ special = jQuery.event.special[ event.type ] || {},
+ handlerQueue = [];
+
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
+ args[0] = event;
+ event.delegateTarget = this;
+
+ // Call the preDispatch hook for the mapped type, and let it bail if desired
+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+ return;
+ }
+
+ // Determine handlers that should run if there are delegated events
+ // Avoid non-left-click bubbling in Firefox (#3861)
+ if ( delegateCount && !(event.button && event.type === "click") ) {
+
+ for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
+
+ // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764)
+ if ( cur.disabled !== true || event.type !== "click" ) {
+ selMatch = {};
+ matches = [];
+ for ( i = 0; i < delegateCount; i++ ) {
+ handleObj = handlers[ i ];
+ sel = handleObj.selector;
+
+ if ( selMatch[ sel ] === undefined ) {
+ selMatch[ sel ] = handleObj.needsContext ?
+ jQuery( sel, this ).index( cur ) >= 0 :
+ jQuery.find( sel, this, null, [ cur ] ).length;
+ }
+ if ( selMatch[ sel ] ) {
+ matches.push( handleObj );
+ }
+ }
+ if ( matches.length ) {
+ handlerQueue.push({ elem: cur, matches: matches });
+ }
+ }
+ }
+ }
+
+ // Add the remaining (directly-bound) handlers
+ if ( handlers.length > delegateCount ) {
+ handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
+ }
+
+ // Run delegates first; they may want to stop propagation beneath us
+ for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
+ matched = handlerQueue[ i ];
+ event.currentTarget = matched.elem;
+
+ for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
+ handleObj = matched.matches[ j ];
+
+ // Triggered event must either 1) be non-exclusive and have no namespace, or
+ // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+ if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
+
+ event.data = handleObj.data;
+ event.handleObj = handleObj;
+
+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+ .apply( matched.elem, args );
+
+ if ( ret !== undefined ) {
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+ }
+ }
+
+ // Call the postDispatch hook for the mapped type
+ if ( special.postDispatch ) {
+ special.postDispatch.call( this, event );
+ }
+
+ return event.result;
+ },
+
+ // Includes some event props shared by KeyEvent and MouseEvent
+ // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
+ props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+ fixHooks: {},
+
+ keyHooks: {
+ props: "char charCode key keyCode".split(" "),
+ filter: function( event, original ) {
+
+ // Add which for key events
+ if ( event.which == null ) {
+ event.which = original.charCode != null ? original.charCode : original.keyCode;
+ }
+
+ return event;
+ }
+ },
+
+ mouseHooks: {
+ props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+ filter: function( event, original ) {
+ var eventDoc, doc, body,
+ button = original.button,
+ fromElement = original.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && original.clientX != null ) {
+ eventDoc = event.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+
+ event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && fromElement ) {
+ event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && button !== undefined ) {
+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+ }
+
+ return event;
+ }
+ },
+
+ fix: function( event ) {
+ if ( event[ jQuery.expando ] ) {
+ return event;
+ }
+
+ // Create a writable copy of the event object and normalize some properties
+ var i, prop,
+ originalEvent = event,
+ fixHook = jQuery.event.fixHooks[ event.type ] || {},
+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+ event = jQuery.Event( originalEvent );
+
+ for ( i = copy.length; i; ) {
+ prop = copy[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
+ if ( !event.target ) {
+ event.target = originalEvent.srcElement || document;
+ }
+
+ // Target should not be a text node (#504, Safari)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8)
+ event.metaKey = !!event.metaKey;
+
+ return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
+ },
+
+ special: {
+ load: {
+ // Prevent triggered image.load events from bubbling to window.load
+ noBubble: true
+ },
+
+ focus: {
+ delegateType: "focusin"
+ },
+ blur: {
+ delegateType: "focusout"
+ },
+
+ beforeunload: {
+ setup: function( data, namespaces, eventHandle ) {
+ // We only want to do this special case on windows
+ if ( jQuery.isWindow( this ) ) {
+ this.onbeforeunload = eventHandle;
+ }
+ },
+
+ teardown: function( namespaces, eventHandle ) {
+ if ( this.onbeforeunload === eventHandle ) {
+ this.onbeforeunload = null;
+ }
+ }
+ }
+ },
+
+ simulate: function( type, elem, event, bubble ) {
+ // Piggyback on a donor event to simulate a different one.
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
+ // simulated event prevents default then we do the same on the donor.
+ var e = jQuery.extend(
+ new jQuery.Event(),
+ event,
+ { type: type,
+ isSimulated: true,
+ originalEvent: {}
+ }
+ );
+ if ( bubble ) {
+ jQuery.event.trigger( e, null, elem );
+ } else {
+ jQuery.event.dispatch.call( elem, e );
+ }
+ if ( e.isDefaultPrevented() ) {
+ event.preventDefault();
+ }
+ }
+};
+
+// Some plugins are using, but it's undocumented/deprecated and will be removed.
+// The 1.7 special event interface should provide all the hooks needed now.
+jQuery.event.handle = jQuery.event.dispatch;
+
+jQuery.removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+ if ( elem.removeEventListener ) {
+ elem.removeEventListener( type, handle, false );
+ }
+ } :
+ function( elem, type, handle ) {
+ var name = "on" + type;
+
+ if ( elem.detachEvent ) {
+
+ // #8545, #7054, preventing memory leaks for custom events in IE6-8
+ // detachEvent needed property on element, by name of that event, to properly expose it to GC
+ if ( typeof elem[ name ] === "undefined" ) {
+ elem[ name ] = null;
+ }
+
+ elem.detachEvent( name, handle );
+ }
+ };
+
+jQuery.Event = function( src, props ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !(this instanceof jQuery.Event) ) {
+ return new jQuery.Event( src, props );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+
+ // Events bubbling up the document may have been marked as prevented
+ // by a handler lower down the tree; reflect the correct value.
+ this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+ src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
+
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // Put explicitly provided properties onto the event object
+ if ( props ) {
+ jQuery.extend( this, props );
+ }
+
+ // Create a timestamp if incoming event doesn't have one
+ this.timeStamp = src && src.timeStamp || jQuery.now();
+
+ // Mark it as fixed
+ this[ jQuery.expando ] = true;
+};
+
+function returnFalse() {
+ return false;
+}
+function returnTrue() {
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+
+ // otherwise set the returnValue property of the original event to false (IE)
+ } else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ delegateType: fix,
+ bindType: fix,
+
+ handle: function( event ) {
+ var ret,
+ target = this,
+ related = event.relatedTarget,
+ handleObj = event.handleObj,
+ selector = handleObj.selector;
+
+ // For mousenter/leave call the handler if related is outside the target.
+ // NB: No relatedTarget if the mouse left/entered the browser window
+ if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+ event.type = handleObj.origType;
+ ret = handleObj.handler.apply( this, arguments );
+ event.type = fix;
+ }
+ return ret;
+ }
+ };
+});
+
+// IE submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+ jQuery.event.special.submit = {
+ setup: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Lazy-add a submit handler when a descendant form may potentially be submitted
+ jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+ // Node name check avoids a VML-related crash in IE (#9807)
+ var elem = e.target,
+ form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+ if ( form && !jQuery._data( form, "_submit_attached" ) ) {
+ jQuery.event.add( form, "submit._submit", function( event ) {
+ event._submit_bubble = true;
+ });
+ jQuery._data( form, "_submit_attached", true );
+ }
+ });
+ // return undefined since we don't need an event listener
+ },
+
+ postDispatch: function( event ) {
+ // If form was submitted by the user, bubble the event up the tree
+ if ( event._submit_bubble ) {
+ delete event._submit_bubble;
+ if ( this.parentNode && !event.isTrigger ) {
+ jQuery.event.simulate( "submit", this.parentNode, event, true );
+ }
+ }
+ },
+
+ teardown: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+ jQuery.event.remove( this, "._submit" );
+ }
+ };
+}
+
+// IE change delegation and checkbox/radio fix
+if ( !jQuery.support.changeBubbles ) {
+
+ jQuery.event.special.change = {
+
+ setup: function() {
+
+ if ( rformElems.test( this.nodeName ) ) {
+ // IE doesn't fire change on a check/radio until blur; trigger it on click
+ // after a propertychange. Eat the blur-change in special.change.handle.
+ // This still fires onchange a second time for check/radio after blur.
+ if ( this.type === "checkbox" || this.type === "radio" ) {
+ jQuery.event.add( this, "propertychange._change", function( event ) {
+ if ( event.originalEvent.propertyName === "checked" ) {
+ this._just_changed = true;
+ }
+ });
+ jQuery.event.add( this, "click._change", function( event ) {
+ if ( this._just_changed && !event.isTrigger ) {
+ this._just_changed = false;
+ }
+ // Allow triggered, simulated change events (#11500)
+ jQuery.event.simulate( "change", this, event, true );
+ });
+ }
+ return false;
+ }
+ // Delegated event; lazy-add a change handler on descendant inputs
+ jQuery.event.add( this, "beforeactivate._change", function( e ) {
+ var elem = e.target;
+
+ if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) {
+ jQuery.event.add( elem, "change._change", function( event ) {
+ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+ jQuery.event.simulate( "change", this.parentNode, event, true );
+ }
+ });
+ jQuery._data( elem, "_change_attached", true );
+ }
+ });
+ },
+
+ handle: function( event ) {
+ var elem = event.target;
+
+ // Swallow native change events from checkbox/radio, we already triggered them above
+ if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+ return event.handleObj.handler.apply( this, arguments );
+ }
+ },
+
+ teardown: function() {
+ jQuery.event.remove( this, "._change" );
+
+ return !rformElems.test( this.nodeName );
+ }
+ };
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+ // Attach a single capturing handler while someone wants focusin/focusout
+ var attaches = 0,
+ handler = function( event ) {
+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+ };
+
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ if ( attaches++ === 0 ) {
+ document.addEventListener( orig, handler, true );
+ }
+ },
+ teardown: function() {
+ if ( --attaches === 0 ) {
+ document.removeEventListener( orig, handler, true );
+ }
+ }
+ };
+ });
+}
+
+jQuery.fn.extend({
+
+ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+ var origFn, type;
+
+ // Types can be a map of types/handlers
+ if ( typeof types === "object" ) {
+ // ( types-Object, selector, data )
+ if ( typeof selector !== "string" ) { // && selector != null
+ // ( types-Object, data )
+ data = data || selector;
+ selector = undefined;
+ }
+ for ( type in types ) {
+ this.on( type, selector, data, types[ type ], one );
+ }
+ return this;
+ }
+
+ if ( data == null && fn == null ) {
+ // ( types, fn )
+ fn = selector;
+ data = selector = undefined;
+ } else if ( fn == null ) {
+ if ( typeof selector === "string" ) {
+ // ( types, selector, fn )
+ fn = data;
+ data = undefined;
+ } else {
+ // ( types, data, fn )
+ fn = data;
+ data = selector;
+ selector = undefined;
+ }
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ } else if ( !fn ) {
+ return this;
+ }
+
+ if ( one === 1 ) {
+ origFn = fn;
+ fn = function( event ) {
+ // Can use an empty set, since event contains the info
+ jQuery().off( event );
+ return origFn.apply( this, arguments );
+ };
+ // Use same guid so caller can remove using origFn
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+ }
+ return this.each( function() {
+ jQuery.event.add( this, types, fn, data, selector );
+ });
+ },
+ one: function( types, selector, data, fn ) {
+ return this.on( types, selector, data, fn, 1 );
+ },
+ off: function( types, selector, fn ) {
+ var handleObj, type;
+ if ( types && types.preventDefault && types.handleObj ) {
+ // ( event ) dispatched jQuery.Event
+ handleObj = types.handleObj;
+ jQuery( types.delegateTarget ).off(
+ handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+ handleObj.selector,
+ handleObj.handler
+ );
+ return this;
+ }
+ if ( typeof types === "object" ) {
+ // ( types-object [, selector] )
+ for ( type in types ) {
+ this.off( type, selector, types[ type ] );
+ }
+ return this;
+ }
+ if ( selector === false || typeof selector === "function" ) {
+ // ( types [, fn] )
+ fn = selector;
+ selector = undefined;
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ }
+ return this.each(function() {
+ jQuery.event.remove( this, types, fn, selector );
+ });
+ },
+
+ bind: function( types, data, fn ) {
+ return this.on( types, null, data, fn );
+ },
+ unbind: function( types, fn ) {
+ return this.off( types, null, fn );
+ },
+
+ live: function( types, data, fn ) {
+ jQuery( this.context ).on( types, this.selector, data, fn );
+ return this;
+ },
+ die: function( types, fn ) {
+ jQuery( this.context ).off( types, this.selector || "**", fn );
+ return this;
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.on( types, selector, data, fn );
+ },
+ undelegate: function( selector, types, fn ) {
+ // ( namespace ) or ( selector, types [, fn] )
+ return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+ triggerHandler: function( type, data ) {
+ if ( this[0] ) {
+ return jQuery.event.trigger( type, data, this[0], true );
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments,
+ guid = fn.guid || jQuery.guid++,
+ i = 0,
+ toggler = function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ };
+
+ // link all the functions, so any of them can unbind this click handler
+ toggler.guid = guid;
+ while ( i < args.length ) {
+ args[ i++ ].guid = guid;
+ }
+
+ return this.click( toggler );
+ },
+
+ hover: function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ }
+});
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( data, fn ) {
+ if ( fn == null ) {
+ fn = data;
+ data = null;
+ }
+
+ return arguments.length > 0 ?
+ this.on( name, null, data, fn ) :
+ this.trigger( name );
+ };
+
+ if ( rkeyEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
+ }
+
+ if ( rmouseEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
+ }
+});
+/*!
+ * Sizzle CSS Selector Engine
+ * Copyright 2012 jQuery Foundation and other contributors
+ * Released under the MIT license
+ * http://sizzlejs.com/
+ */
+(function( window, undefined ) {
+
+var cachedruns,
+ assertGetIdNotName,
+ Expr,
+ getText,
+ isXML,
+ contains,
+ compile,
+ sortOrder,
+ hasDuplicate,
+ outermostContext,
+
+ baseHasDuplicate = true,
+ strundefined = "undefined",
+
+ expando = ( "sizcache" + Math.random() ).replace( ".", "" ),
+
+ Token = String,
+ document = window.document,
+ docElem = document.documentElement,
+ dirruns = 0,
+ done = 0,
+ pop = [].pop,
+ push = [].push,
+ slice = [].slice,
+ // Use a stripped-down indexOf if a native one is unavailable
+ indexOf = [].indexOf || function( elem ) {
+ var i = 0,
+ len = this.length;
+ for ( ; i < len; i++ ) {
+ if ( this[i] === elem ) {
+ return i;
+ }
+ }
+ return -1;
+ },
+
+ // Augment a function for special use by Sizzle
+ markFunction = function( fn, value ) {
+ fn[ expando ] = value == null || value;
+ return fn;
+ },
+
+ createCache = function() {
+ var cache = {},
+ keys = [];
+
+ return markFunction(function( key, value ) {
+ // Only keep the most recent entries
+ if ( keys.push( key ) > Expr.cacheLength ) {
+ delete cache[ keys.shift() ];
+ }
+
+ // Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157)
+ return (cache[ key + " " ] = value);
+ }, cache );
+ },
+
+ classCache = createCache(),
+ tokenCache = createCache(),
+ compilerCache = createCache(),
+
+ // Regex
+
+ // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
+ whitespace = "[\\x20\\t\\r\\n\\f]",
+ // http://www.w3.org/TR/css3-syntax/#characters
+ characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",
+
+ // Loosely modeled on CSS identifier characters
+ // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors)
+ // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+ identifier = characterEncoding.replace( "w", "w#" ),
+
+ // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
+ operators = "([*^$|!~]?=)",
+ attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
+ "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
+
+ // Prefer arguments not in parens/brackets,
+ // then attribute selectors and non-pseudos (denoted by :),
+ // then anything else
+ // These preferences are here to reduce the number of selectors
+ // needing tokenize in the PSEUDO preFilter
+ pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)",
+
+ // For matchExpr.POS and matchExpr.needsContext
+ pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
+ "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)",
+
+ // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+ rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+ rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+ rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
+ rpseudo = new RegExp( pseudos ),
+
+ // Easily-parseable/retrievable ID or TAG or CLASS selectors
+ rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,
+
+ rnot = /^:not/,
+ rsibling = /[\x20\t\r\n\f]*[+~]/,
+ rendsWithNot = /:not\($/,
+
+ rheader = /h\d/i,
+ rinputs = /input|select|textarea|button/i,
+
+ rbackslash = /\\(?!\\)/g,
+
+ matchExpr = {
+ "ID": new RegExp( "^#(" + characterEncoding + ")" ),
+ "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+ "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
+ "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+ "ATTR": new RegExp( "^" + attributes ),
+ "PSEUDO": new RegExp( "^" + pseudos ),
+ "POS": new RegExp( pos, "i" ),
+ "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace +
+ "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+ "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+ // For use in libraries implementing .is()
+ "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" )
+ },
+
+ // Support
+
+ // Used for testing something on an element
+ assert = function( fn ) {
+ var div = document.createElement("div");
+
+ try {
+ return fn( div );
+ } catch (e) {
+ return false;
+ } finally {
+ // release memory in IE
+ div = null;
+ }
+ },
+
+ // Check if getElementsByTagName("*") returns only elements
+ assertTagNameNoComments = assert(function( div ) {
+ div.appendChild( document.createComment("") );
+ return !div.getElementsByTagName("*").length;
+ }),
+
+ // Check if getAttribute returns normalized href attributes
+ assertHrefNotNormalized = assert(function( div ) {
+ div.innerHTML = "<a href='#'></a>";
+ return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
+ div.firstChild.getAttribute("href") === "#";
+ }),
+
+ // Check if attributes should be retrieved by attribute nodes
+ assertAttributes = assert(function( div ) {
+ div.innerHTML = "<select></select>";
+ var type = typeof div.lastChild.getAttribute("multiple");
+ // IE8 returns a string for some attributes even when not present
+ return type !== "boolean" && type !== "string";
+ }),
+
+ // Check if getElementsByClassName can be trusted
+ assertUsableClassName = assert(function( div ) {
+ // Opera can't find a second classname (in 9.6)
+ div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";
+ if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
+ return false;
+ }
+
+ // Safari 3.2 caches class attributes and doesn't catch changes
+ div.lastChild.className = "e";
+ return div.getElementsByClassName("e").length === 2;
+ }),
+
+ // Check if getElementById returns elements by name
+ // Check if getElementsByName privileges form controls or returns elements by ID
+ assertUsableName = assert(function( div ) {
+ // Inject content
+ div.id = expando + 0;
+ div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>";
+ docElem.insertBefore( div, docElem.firstChild );
+
+ // Test
+ var pass = document.getElementsByName &&
+ // buggy browsers will return fewer than the correct 2
+ document.getElementsByName( expando ).length === 2 +
+ // buggy browsers will return more than the correct 0
+ document.getElementsByName( expando + 0 ).length;
+ assertGetIdNotName = !document.getElementById( expando );
+
+ // Cleanup
+ docElem.removeChild( div );
+
+ return pass;
+ });
+
+// If slice is not available, provide a backup
+try {
+ slice.call( docElem.childNodes, 0 )[0].nodeType;
+} catch ( e ) {
+ slice = function( i ) {
+ var elem,
+ results = [];
+ for ( ; (elem = this[i]); i++ ) {
+ results.push( elem );
+ }
+ return results;
+ };
+}
+
+function Sizzle( selector, context, results, seed ) {
+ results = results || [];
+ context = context || document;
+ var match, elem, xml, m,
+ nodeType = context.nodeType;
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ if ( nodeType !== 1 && nodeType !== 9 ) {
+ return [];
+ }
+
+ xml = isXML( context );
+
+ if ( !xml && !seed ) {
+ if ( (match = rquickExpr.exec( selector )) ) {
+ // Speed-up: Sizzle("#ID")
+ if ( (m = match[1]) ) {
+ if ( nodeType === 9 ) {
+ elem = context.getElementById( m );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE, Opera, and Webkit return items
+ // by name instead of ID
+ if ( elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ } else {
+ return results;
+ }
+ } else {
+ // Context is not a document
+ if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+ contains( context, elem ) && elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ }
+
+ // Speed-up: Sizzle("TAG")
+ } else if ( match[2] ) {
+ push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );
+ return results;
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) {
+ push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );
+ return results;
+ }
+ }
+ }
+
+ // All others
+ return select( selector.replace( rtrim, "$1" ), context, results, seed, xml );
+}
+
+Sizzle.matches = function( expr, elements ) {
+ return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+ return Sizzle( expr, null, null, [ elem ] ).length > 0;
+};
+
+// Returns a function to use in pseudos for input types
+function createInputPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === type;
+ };
+}
+
+// Returns a function to use in pseudos for buttons
+function createButtonPseudo( type ) {
+ return function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && elem.type === type;
+ };
+}
+
+// Returns a function to use in pseudos for positionals
+function createPositionalPseudo( fn ) {
+ return markFunction(function( argument ) {
+ argument = +argument;
+ return markFunction(function( seed, matches ) {
+ var j,
+ matchIndexes = fn( [], seed.length, argument ),
+ i = matchIndexes.length;
+
+ // Match elements found at the specified indexes
+ while ( i-- ) {
+ if ( seed[ (j = matchIndexes[i]) ] ) {
+ seed[j] = !(matches[j] = seed[j]);
+ }
+ }
+ });
+ });
+}
+
+/**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+getText = Sizzle.getText = function( elem ) {
+ var node,
+ ret = "",
+ i = 0,
+ nodeType = elem.nodeType;
+
+ if ( nodeType ) {
+ if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ // Use textContent for elements
+ // innerText usage removed for consistency of new lines (see #11153)
+ if ( typeof elem.textContent === "string" ) {
+ return elem.textContent;
+ } else {
+ // Traverse its children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ // Do not include comment or processing instruction nodes
+ } else {
+
+ // If no nodeType, this is expected to be an array
+ for ( ; (node = elem[i]); i++ ) {
+ // Do not traverse comment nodes
+ ret += getText( node );
+ }
+ }
+ return ret;
+};
+
+isXML = Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+// Element contains another
+contains = Sizzle.contains = docElem.contains ?
+ function( a, b ) {
+ var adown = a.nodeType === 9 ? a.documentElement : a,
+ bup = b && b.parentNode;
+ return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );
+ } :
+ docElem.compareDocumentPosition ?
+ function( a, b ) {
+ return b && !!( a.compareDocumentPosition( b ) & 16 );
+ } :
+ function( a, b ) {
+ while ( (b = b.parentNode) ) {
+ if ( b === a ) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+Sizzle.attr = function( elem, name ) {
+ var val,
+ xml = isXML( elem );
+
+ if ( !xml ) {
+ name = name.toLowerCase();
+ }
+ if ( (val = Expr.attrHandle[ name ]) ) {
+ return val( elem );
+ }
+ if ( xml || assertAttributes ) {
+ return elem.getAttribute( name );
+ }
+ val = elem.getAttributeNode( name );
+ return val ?
+ typeof elem[ name ] === "boolean" ?
+ elem[ name ] ? name : null :
+ val.specified ? val.value : null :
+ null;
+};
+
+Expr = Sizzle.selectors = {
+
+ // Can be adjusted by the user
+ cacheLength: 50,
+
+ createPseudo: markFunction,
+
+ match: matchExpr,
+
+ // IE6/7 return a modified href
+ attrHandle: assertHrefNotNormalized ?
+ {} :
+ {
+ "href": function( elem ) {
+ return elem.getAttribute( "href", 2 );
+ },
+ "type": function( elem ) {
+ return elem.getAttribute("type");
+ }
+ },
+
+ find: {
+ "ID": assertGetIdNotName ?
+ function( id, context, xml ) {
+ if ( typeof context.getElementById !== strundefined && !xml ) {
+ var m = context.getElementById( id );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ } :
+ function( id, context, xml ) {
+ if ( typeof context.getElementById !== strundefined && !xml ) {
+ var m = context.getElementById( id );
+
+ return m ?
+ m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
+ [m] :
+ undefined :
+ [];
+ }
+ },
+
+ "TAG": assertTagNameNoComments ?
+ function( tag, context ) {
+ if ( typeof context.getElementsByTagName !== strundefined ) {
+ return context.getElementsByTagName( tag );
+ }
+ } :
+ function( tag, context ) {
+ var results = context.getElementsByTagName( tag );
+
+ // Filter out possible comments
+ if ( tag === "*" ) {
+ var elem,
+ tmp = [],
+ i = 0;
+
+ for ( ; (elem = results[i]); i++ ) {
+ if ( elem.nodeType === 1 ) {
+ tmp.push( elem );
+ }
+ }
+
+ return tmp;
+ }
+ return results;
+ },
+
+ "NAME": assertUsableName && function( tag, context ) {
+ if ( typeof context.getElementsByName !== strundefined ) {
+ return context.getElementsByName( name );
+ }
+ },
+
+ "CLASS": assertUsableClassName && function( className, context, xml ) {
+ if ( typeof context.getElementsByClassName !== strundefined && !xml ) {
+ return context.getElementsByClassName( className );
+ }
+ }
+ },
+
+ relative: {
+ ">": { dir: "parentNode", first: true },
+ " ": { dir: "parentNode" },
+ "+": { dir: "previousSibling", first: true },
+ "~": { dir: "previousSibling" }
+ },
+
+ preFilter: {
+ "ATTR": function( match ) {
+ match[1] = match[1].replace( rbackslash, "" );
+
+ // Move the given value to match[3] whether quoted or unquoted
+ match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" );
+
+ if ( match[2] === "~=" ) {
+ match[3] = " " + match[3] + " ";
+ }
+
+ return match.slice( 0, 4 );
+ },
+
+ "CHILD": function( match ) {
+ /* matches from matchExpr["CHILD"]
+ 1 type (only|nth|...)
+ 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+ 3 xn-component of xn+y argument ([+-]?\d*n|)
+ 4 sign of xn-component
+ 5 x of xn-component
+ 6 sign of y-component
+ 7 y of y-component
+ */
+ match[1] = match[1].toLowerCase();
+
+ if ( match[1] === "nth" ) {
+ // nth-child requires argument
+ if ( !match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // numeric x and y parameters for Expr.filter.CHILD
+ // remember that false/true cast respectively to 0/1
+ match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) );
+ match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" );
+
+ // other types prohibit arguments
+ } else if ( match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ return match;
+ },
+
+ "PSEUDO": function( match ) {
+ var unquoted, excess;
+ if ( matchExpr["CHILD"].test( match[0] ) ) {
+ return null;
+ }
+
+ if ( match[3] ) {
+ match[2] = match[3];
+ } else if ( (unquoted = match[4]) ) {
+ // Only check arguments that contain a pseudo
+ if ( rpseudo.test(unquoted) &&
+ // Get excess from tokenize (recursively)
+ (excess = tokenize( unquoted, true )) &&
+ // advance to the next closing parenthesis
+ (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+ // excess is a negative index
+ unquoted = unquoted.slice( 0, excess );
+ match[0] = match[0].slice( 0, excess );
+ }
+ match[2] = unquoted;
+ }
+
+ // Return only captures needed by the pseudo filter method (type and argument)
+ return match.slice( 0, 3 );
+ }
+ },
+
+ filter: {
+ "ID": assertGetIdNotName ?
+ function( id ) {
+ id = id.replace( rbackslash, "" );
+ return function( elem ) {
+ return elem.getAttribute("id") === id;
+ };
+ } :
+ function( id ) {
+ id = id.replace( rbackslash, "" );
+ return function( elem ) {
+ var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+ return node && node.value === id;
+ };
+ },
+
+ "TAG": function( nodeName ) {
+ if ( nodeName === "*" ) {
+ return function() { return true; };
+ }
+ nodeName = nodeName.replace( rbackslash, "" ).toLowerCase();
+
+ return function( elem ) {
+ return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+ };
+ },
+
+ "CLASS": function( className ) {
+ var pattern = classCache[ expando ][ className + " " ];
+
+ return pattern ||
+ (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+ classCache( className, function( elem ) {
+ return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
+ });
+ },
+
+ "ATTR": function( name, operator, check ) {
+ return function( elem, context ) {
+ var result = Sizzle.attr( elem, name );
+
+ if ( result == null ) {
+ return operator === "!=";
+ }
+ if ( !operator ) {
+ return true;
+ }
+
+ result += "";
+
+ return operator === "=" ? result === check :
+ operator === "!=" ? result !== check :
+ operator === "^=" ? check && result.indexOf( check ) === 0 :
+ operator === "*=" ? check && result.indexOf( check ) > -1 :
+ operator === "$=" ? check && result.substr( result.length - check.length ) === check :
+ operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
+ operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" :
+ false;
+ };
+ },
+
+ "CHILD": function( type, argument, first, last ) {
+
+ if ( type === "nth" ) {
+ return function( elem ) {
+ var node, diff,
+ parent = elem.parentNode;
+
+ if ( first === 1 && last === 0 ) {
+ return true;
+ }
+
+ if ( parent ) {
+ diff = 0;
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ diff++;
+ if ( elem === node ) {
+ break;
+ }
+ }
+ }
+ }
+
+ // Incorporate the offset (or cast to NaN), then check against cycle size
+ diff -= last;
+ return diff === first || ( diff % first === 0 && diff / first >= 0 );
+ };
+ }
+
+ return function( elem ) {
+ var node = elem;
+
+ switch ( type ) {
+ case "only":
+ case "first":
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ if ( type === "first" ) {
+ return true;
+ }
+
+ node = elem;
+
+ /* falls through */
+ case "last":
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ };
+ },
+
+ "PSEUDO": function( pseudo, argument ) {
+ // pseudo-class names are case-insensitive
+ // http://www.w3.org/TR/selectors/#pseudo-classes
+ // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+ // Remember that setFilters inherits from pseudos
+ var args,
+ fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+ Sizzle.error( "unsupported pseudo: " + pseudo );
+
+ // The user may use createPseudo to indicate that
+ // arguments are needed to create the filter function
+ // just as Sizzle does
+ if ( fn[ expando ] ) {
+ return fn( argument );
+ }
+
+ // But maintain support for old signatures
+ if ( fn.length > 1 ) {
+ args = [ pseudo, pseudo, "", argument ];
+ return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+ markFunction(function( seed, matches ) {
+ var idx,
+ matched = fn( seed, argument ),
+ i = matched.length;
+ while ( i-- ) {
+ idx = indexOf.call( seed, matched[i] );
+ seed[ idx ] = !( matches[ idx ] = matched[i] );
+ }
+ }) :
+ function( elem ) {
+ return fn( elem, 0, args );
+ };
+ }
+
+ return fn;
+ }
+ },
+
+ pseudos: {
+ "not": markFunction(function( selector ) {
+ // Trim the selector passed to compile
+ // to avoid treating leading and trailing
+ // spaces as combinators
+ var input = [],
+ results = [],
+ matcher = compile( selector.replace( rtrim, "$1" ) );
+
+ return matcher[ expando ] ?
+ markFunction(function( seed, matches, context, xml ) {
+ var elem,
+ unmatched = matcher( seed, null, xml, [] ),
+ i = seed.length;
+
+ // Match elements unmatched by `matcher`
+ while ( i-- ) {
+ if ( (elem = unmatched[i]) ) {
+ seed[i] = !(matches[i] = elem);
+ }
+ }
+ }) :
+ function( elem, context, xml ) {
+ input[0] = elem;
+ matcher( input, null, xml, results );
+ return !results.pop();
+ };
+ }),
+
+ "has": markFunction(function( selector ) {
+ return function( elem ) {
+ return Sizzle( selector, elem ).length > 0;
+ };
+ }),
+
+ "contains": markFunction(function( text ) {
+ return function( elem ) {
+ return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+ };
+ }),
+
+ "enabled": function( elem ) {
+ return elem.disabled === false;
+ },
+
+ "disabled": function( elem ) {
+ return elem.disabled === true;
+ },
+
+ "checked": function( elem ) {
+ // In CSS3, :checked should return both checked and selected elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ var nodeName = elem.nodeName.toLowerCase();
+ return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+ },
+
+ "selected": function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ "parent": function( elem ) {
+ return !Expr.pseudos["empty"]( elem );
+ },
+
+ "empty": function( elem ) {
+ // http://www.w3.org/TR/selectors/#empty-pseudo
+ // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
+ // not comment, processing instructions, or others
+ // Thanks to Diego Perini for the nodeName shortcut
+ // Greater than "@" means alpha characters (specifically not starting with "#" or "?")
+ var nodeType;
+ elem = elem.firstChild;
+ while ( elem ) {
+ if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) {
+ return false;
+ }
+ elem = elem.nextSibling;
+ }
+ return true;
+ },
+
+ "header": function( elem ) {
+ return rheader.test( elem.nodeName );
+ },
+
+ "text": function( elem ) {
+ var type, attr;
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return elem.nodeName.toLowerCase() === "input" &&
+ (type = elem.type) === "text" &&
+ ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type );
+ },
+
+ // Input types
+ "radio": createInputPseudo("radio"),
+ "checkbox": createInputPseudo("checkbox"),
+ "file": createInputPseudo("file"),
+ "password": createInputPseudo("password"),
+ "image": createInputPseudo("image"),
+
+ "submit": createButtonPseudo("submit"),
+ "reset": createButtonPseudo("reset"),
+
+ "button": function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && elem.type === "button" || name === "button";
+ },
+
+ "input": function( elem ) {
+ return rinputs.test( elem.nodeName );
+ },
+
+ "focus": function( elem ) {
+ var doc = elem.ownerDocument;
+ return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+ },
+
+ "active": function( elem ) {
+ return elem === elem.ownerDocument.activeElement;
+ },
+
+ // Positional types
+ "first": createPositionalPseudo(function() {
+ return [ 0 ];
+ }),
+
+ "last": createPositionalPseudo(function( matchIndexes, length ) {
+ return [ length - 1 ];
+ }),
+
+ "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ return [ argument < 0 ? argument + length : argument ];
+ }),
+
+ "even": createPositionalPseudo(function( matchIndexes, length ) {
+ for ( var i = 0; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "odd": createPositionalPseudo(function( matchIndexes, length ) {
+ for ( var i = 1; i < length; i += 2 ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ }),
+
+ "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+ for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) {
+ matchIndexes.push( i );
+ }
+ return matchIndexes;
+ })
+ }
+};
+
+function siblingCheck( a, b, ret ) {
+ if ( a === b ) {
+ return ret;
+ }
+
+ var cur = a.nextSibling;
+
+ while ( cur ) {
+ if ( cur === b ) {
+ return -1;
+ }
+
+ cur = cur.nextSibling;
+ }
+
+ return 1;
+}
+
+sortOrder = docElem.compareDocumentPosition ?
+ function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ return ( !a.compareDocumentPosition || !b.compareDocumentPosition ?
+ a.compareDocumentPosition :
+ a.compareDocumentPosition(b) & 4
+ ) ? -1 : 1;
+ } :
+ function( a, b ) {
+ // The nodes are identical, we can exit early
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // Fallback to using sourceIndex (in IE) if it's available on both nodes
+ } else if ( a.sourceIndex && b.sourceIndex ) {
+ return a.sourceIndex - b.sourceIndex;
+ }
+
+ var al, bl,
+ ap = [],
+ bp = [],
+ aup = a.parentNode,
+ bup = b.parentNode,
+ cur = aup;
+
+ // If the nodes are siblings (or identical) we can do a quick check
+ if ( aup === bup ) {
+ return siblingCheck( a, b );
+
+ // If no parents were found then the nodes are disconnected
+ } else if ( !aup ) {
+ return -1;
+
+ } else if ( !bup ) {
+ return 1;
+ }
+
+ // Otherwise they're somewhere else in the tree so we need
+ // to build up a full list of the parentNodes for comparison
+ while ( cur ) {
+ ap.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ cur = bup;
+
+ while ( cur ) {
+ bp.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ al = ap.length;
+ bl = bp.length;
+
+ // Start walking down the tree looking for a discrepancy
+ for ( var i = 0; i < al && i < bl; i++ ) {
+ if ( ap[i] !== bp[i] ) {
+ return siblingCheck( ap[i], bp[i] );
+ }
+ }
+
+ // We ended someplace up the tree so do a sibling check
+ return i === al ?
+ siblingCheck( a, bp[i], -1 ) :
+ siblingCheck( ap[i], b, 1 );
+ };
+
+// Always assume the presence of duplicates if sort doesn't
+// pass them to our comparison function (as in Google Chrome).
+[0, 0].sort( sortOrder );
+baseHasDuplicate = !hasDuplicate;
+
+// Document sorting and removing duplicates
+Sizzle.uniqueSort = function( results ) {
+ var elem,
+ duplicates = [],
+ i = 1,
+ j = 0;
+
+ hasDuplicate = baseHasDuplicate;
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ for ( ; (elem = results[i]); i++ ) {
+ if ( elem === results[ i - 1 ] ) {
+ j = duplicates.push( i );
+ }
+ }
+ while ( j-- ) {
+ results.splice( duplicates[ j ], 1 );
+ }
+ }
+
+ return results;
+};
+
+Sizzle.error = function( msg ) {
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+function tokenize( selector, parseOnly ) {
+ var matched, match, tokens, type,
+ soFar, groups, preFilters,
+ cached = tokenCache[ expando ][ selector + " " ];
+
+ if ( cached ) {
+ return parseOnly ? 0 : cached.slice( 0 );
+ }
+
+ soFar = selector;
+ groups = [];
+ preFilters = Expr.preFilter;
+
+ while ( soFar ) {
+
+ // Comma and first run
+ if ( !matched || (match = rcomma.exec( soFar )) ) {
+ if ( match ) {
+ // Don't consume trailing commas as valid
+ soFar = soFar.slice( match[0].length ) || soFar;
+ }
+ groups.push( tokens = [] );
+ }
+
+ matched = false;
+
+ // Combinators
+ if ( (match = rcombinators.exec( soFar )) ) {
+ tokens.push( matched = new Token( match.shift() ) );
+ soFar = soFar.slice( matched.length );
+
+ // Cast descendant combinators to space
+ matched.type = match[0].replace( rtrim, " " );
+ }
+
+ // Filters
+ for ( type in Expr.filter ) {
+ if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+ (match = preFilters[ type ]( match ))) ) {
+
+ tokens.push( matched = new Token( match.shift() ) );
+ soFar = soFar.slice( matched.length );
+ matched.type = type;
+ matched.matches = match;
+ }
+ }
+
+ if ( !matched ) {
+ break;
+ }
+ }
+
+ // Return the length of the invalid excess
+ // if we're just parsing
+ // Otherwise, throw an error or return tokens
+ return parseOnly ?
+ soFar.length :
+ soFar ?
+ Sizzle.error( selector ) :
+ // Cache the tokens
+ tokenCache( selector, groups ).slice( 0 );
+}
+
+function addCombinator( matcher, combinator, base ) {
+ var dir = combinator.dir,
+ checkNonElements = base && combinator.dir === "parentNode",
+ doneName = done++;
+
+ return combinator.first ?
+ // Check against closest ancestor/preceding element
+ function( elem, context, xml ) {
+ while ( (elem = elem[ dir ]) ) {
+ if ( checkNonElements || elem.nodeType === 1 ) {
+ return matcher( elem, context, xml );
+ }
+ }
+ } :
+
+ // Check against all ancestor/preceding elements
+ function( elem, context, xml ) {
+ // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
+ if ( !xml ) {
+ var cache,
+ dirkey = dirruns + " " + doneName + " ",
+ cachedkey = dirkey + cachedruns;
+ while ( (elem = elem[ dir ]) ) {
+ if ( checkNonElements || elem.nodeType === 1 ) {
+ if ( (cache = elem[ expando ]) === cachedkey ) {
+ return elem.sizset;
+ } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) {
+ if ( elem.sizset ) {
+ return elem;
+ }
+ } else {
+ elem[ expando ] = cachedkey;
+ if ( matcher( elem, context, xml ) ) {
+ elem.sizset = true;
+ return elem;
+ }
+ elem.sizset = false;
+ }
+ }
+ }
+ } else {
+ while ( (elem = elem[ dir ]) ) {
+ if ( checkNonElements || elem.nodeType === 1 ) {
+ if ( matcher( elem, context, xml ) ) {
+ return elem;
+ }
+ }
+ }
+ }
+ };
+}
+
+function elementMatcher( matchers ) {
+ return matchers.length > 1 ?
+ function( elem, context, xml ) {
+ var i = matchers.length;
+ while ( i-- ) {
+ if ( !matchers[i]( elem, context, xml ) ) {
+ return false;
+ }
+ }
+ return true;
+ } :
+ matchers[0];
+}
+
+function condense( unmatched, map, filter, context, xml ) {
+ var elem,
+ newUnmatched = [],
+ i = 0,
+ len = unmatched.length,
+ mapped = map != null;
+
+ for ( ; i < len; i++ ) {
+ if ( (elem = unmatched[i]) ) {
+ if ( !filter || filter( elem, context, xml ) ) {
+ newUnmatched.push( elem );
+ if ( mapped ) {
+ map.push( i );
+ }
+ }
+ }
+ }
+
+ return newUnmatched;
+}
+
+function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+ if ( postFilter && !postFilter[ expando ] ) {
+ postFilter = setMatcher( postFilter );
+ }
+ if ( postFinder && !postFinder[ expando ] ) {
+ postFinder = setMatcher( postFinder, postSelector );
+ }
+ return markFunction(function( seed, results, context, xml ) {
+ var temp, i, elem,
+ preMap = [],
+ postMap = [],
+ preexisting = results.length,
+
+ // Get initial elements from seed or context
+ elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+ // Prefilter to get matcher input, preserving a map for seed-results synchronization
+ matcherIn = preFilter && ( seed || !selector ) ?
+ condense( elems, preMap, preFilter, context, xml ) :
+ elems,
+
+ matcherOut = matcher ?
+ // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+ postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+ // ...intermediate processing is necessary
+ [] :
+
+ // ...otherwise use results directly
+ results :
+ matcherIn;
+
+ // Find primary matches
+ if ( matcher ) {
+ matcher( matcherIn, matcherOut, context, xml );
+ }
+
+ // Apply postFilter
+ if ( postFilter ) {
+ temp = condense( matcherOut, postMap );
+ postFilter( temp, [], context, xml );
+
+ // Un-match failing elements by moving them back to matcherIn
+ i = temp.length;
+ while ( i-- ) {
+ if ( (elem = temp[i]) ) {
+ matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+ }
+ }
+ }
+
+ if ( seed ) {
+ if ( postFinder || preFilter ) {
+ if ( postFinder ) {
+ // Get the final matcherOut by condensing this intermediate into postFinder contexts
+ temp = [];
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) ) {
+ // Restore matcherIn since elem is not yet a final match
+ temp.push( (matcherIn[i] = elem) );
+ }
+ }
+ postFinder( null, (matcherOut = []), temp, xml );
+ }
+
+ // Move matched elements from seed to results to keep them synchronized
+ i = matcherOut.length;
+ while ( i-- ) {
+ if ( (elem = matcherOut[i]) &&
+ (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+
+ seed[temp] = !(results[temp] = elem);
+ }
+ }
+ }
+
+ // Add elements to results, through postFinder if defined
+ } else {
+ matcherOut = condense(
+ matcherOut === results ?
+ matcherOut.splice( preexisting, matcherOut.length ) :
+ matcherOut
+ );
+ if ( postFinder ) {
+ postFinder( null, results, matcherOut, xml );
+ } else {
+ push.apply( results, matcherOut );
+ }
+ }
+ });
+}
+
+function matcherFromTokens( tokens ) {
+ var checkContext, matcher, j,
+ len = tokens.length,
+ leadingRelative = Expr.relative[ tokens[0].type ],
+ implicitRelative = leadingRelative || Expr.relative[" "],
+ i = leadingRelative ? 1 : 0,
+
+ // The foundational matcher ensures that elements are reachable from top-level context(s)
+ matchContext = addCombinator( function( elem ) {
+ return elem === checkContext;
+ }, implicitRelative, true ),
+ matchAnyContext = addCombinator( function( elem ) {
+ return indexOf.call( checkContext, elem ) > -1;
+ }, implicitRelative, true ),
+ matchers = [ function( elem, context, xml ) {
+ return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+ (checkContext = context).nodeType ?
+ matchContext( elem, context, xml ) :
+ matchAnyContext( elem, context, xml ) );
+ } ];
+
+ for ( ; i < len; i++ ) {
+ if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+ matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
+ } else {
+ matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+ // Return special upon seeing a positional matcher
+ if ( matcher[ expando ] ) {
+ // Find the next relative operator (if any) for proper handling
+ j = ++i;
+ for ( ; j < len; j++ ) {
+ if ( Expr.relative[ tokens[j].type ] ) {
+ break;
+ }
+ }
+ return setMatcher(
+ i > 1 && elementMatcher( matchers ),
+ i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ),
+ matcher,
+ i < j && matcherFromTokens( tokens.slice( i, j ) ),
+ j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+ j < len && tokens.join("")
+ );
+ }
+ matchers.push( matcher );
+ }
+ }
+
+ return elementMatcher( matchers );
+}
+
+function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+ var bySet = setMatchers.length > 0,
+ byElement = elementMatchers.length > 0,
+ superMatcher = function( seed, context, xml, results, expandContext ) {
+ var elem, j, matcher,
+ setMatched = [],
+ matchedCount = 0,
+ i = "0",
+ unmatched = seed && [],
+ outermost = expandContext != null,
+ contextBackup = outermostContext,
+ // We must always have either seed elements or context
+ elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
+ // Nested matchers should use non-integer dirruns
+ dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E);
+
+ if ( outermost ) {
+ outermostContext = context !== document && context;
+ cachedruns = superMatcher.el;
+ }
+
+ // Add elements passing elementMatchers directly to results
+ for ( ; (elem = elems[i]) != null; i++ ) {
+ if ( byElement && elem ) {
+ for ( j = 0; (matcher = elementMatchers[j]); j++ ) {
+ if ( matcher( elem, context, xml ) ) {
+ results.push( elem );
+ break;
+ }
+ }
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ cachedruns = ++superMatcher.el;
+ }
+ }
+
+ // Track unmatched elements for set filters
+ if ( bySet ) {
+ // They will have gone through all possible matchers
+ if ( (elem = !matcher && elem) ) {
+ matchedCount--;
+ }
+
+ // Lengthen the array for every element, matched or not
+ if ( seed ) {
+ unmatched.push( elem );
+ }
+ }
+ }
+
+ // Apply set filters to unmatched elements
+ matchedCount += i;
+ if ( bySet && i !== matchedCount ) {
+ for ( j = 0; (matcher = setMatchers[j]); j++ ) {
+ matcher( unmatched, setMatched, context, xml );
+ }
+
+ if ( seed ) {
+ // Reintegrate element matches to eliminate the need for sorting
+ if ( matchedCount > 0 ) {
+ while ( i-- ) {
+ if ( !(unmatched[i] || setMatched[i]) ) {
+ setMatched[i] = pop.call( results );
+ }
+ }
+ }
+
+ // Discard index placeholder values to get only actual matches
+ setMatched = condense( setMatched );
+ }
+
+ // Add matches to results
+ push.apply( results, setMatched );
+
+ // Seedless set matches succeeding multiple successful matchers stipulate sorting
+ if ( outermost && !seed && setMatched.length > 0 &&
+ ( matchedCount + setMatchers.length ) > 1 ) {
+
+ Sizzle.uniqueSort( results );
+ }
+ }
+
+ // Override manipulation of globals by nested matchers
+ if ( outermost ) {
+ dirruns = dirrunsUnique;
+ outermostContext = contextBackup;
+ }
+
+ return unmatched;
+ };
+
+ superMatcher.el = 0;
+ return bySet ?
+ markFunction( superMatcher ) :
+ superMatcher;
+}
+
+compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
+ var i,
+ setMatchers = [],
+ elementMatchers = [],
+ cached = compilerCache[ expando ][ selector + " " ];
+
+ if ( !cached ) {
+ // Generate a function of recursive functions that can be used to check each element
+ if ( !group ) {
+ group = tokenize( selector );
+ }
+ i = group.length;
+ while ( i-- ) {
+ cached = matcherFromTokens( group[i] );
+ if ( cached[ expando ] ) {
+ setMatchers.push( cached );
+ } else {
+ elementMatchers.push( cached );
+ }
+ }
+
+ // Cache the compiled function
+ cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+ }
+ return cached;
+};
+
+function multipleContexts( selector, contexts, results ) {
+ var i = 0,
+ len = contexts.length;
+ for ( ; i < len; i++ ) {
+ Sizzle( selector, contexts[i], results );
+ }
+ return results;
+}
+
+function select( selector, context, results, seed, xml ) {
+ var i, tokens, token, type, find,
+ match = tokenize( selector ),
+ j = match.length;
+
+ if ( !seed ) {
+ // Try to minimize operations if there is only one group
+ if ( match.length === 1 ) {
+
+ // Take a shortcut and set the context if the root selector is an ID
+ tokens = match[0] = match[0].slice( 0 );
+ if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+ context.nodeType === 9 && !xml &&
+ Expr.relative[ tokens[1].type ] ) {
+
+ context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0];
+ if ( !context ) {
+ return results;
+ }
+
+ selector = selector.slice( tokens.shift().length );
+ }
+
+ // Fetch a seed set for right-to-left matching
+ for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) {
+ token = tokens[i];
+
+ // Abort if we hit a combinator
+ if ( Expr.relative[ (type = token.type) ] ) {
+ break;
+ }
+ if ( (find = Expr.find[ type ]) ) {
+ // Search, expanding context for leading sibling combinators
+ if ( (seed = find(
+ token.matches[0].replace( rbackslash, "" ),
+ rsibling.test( tokens[0].type ) && context.parentNode || context,
+ xml
+ )) ) {
+
+ // If seed is empty or no tokens remain, we can return early
+ tokens.splice( i, 1 );
+ selector = seed.length && tokens.join("");
+ if ( !selector ) {
+ push.apply( results, slice.call( seed, 0 ) );
+ return results;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Compile and execute a filtering function
+ // Provide `match` to avoid retokenization if we modified the selector above
+ compile( selector, match )(
+ seed,
+ context,
+ xml,
+ results,
+ rsibling.test( selector )
+ );
+ return results;
+}
+
+if ( document.querySelectorAll ) {
+ (function() {
+ var disconnectedMatch,
+ oldSelect = select,
+ rescape = /'|\\/g,
+ rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
+
+ // qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA
+ // A support test would require too much code (would include document ready)
+ rbuggyQSA = [ ":focus" ],
+
+ // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+ // A support test would require too much code (would include document ready)
+ // just skip matchesSelector for :active
+ rbuggyMatches = [ ":active" ],
+ matches = docElem.matchesSelector ||
+ docElem.mozMatchesSelector ||
+ docElem.webkitMatchesSelector ||
+ docElem.oMatchesSelector ||
+ docElem.msMatchesSelector;
+
+ // Build QSA regex
+ // Regex strategy adopted from Diego Perini
+ assert(function( div ) {
+ // Select is set to empty string on purpose
+ // This is to test IE's treatment of not explictly
+ // setting a boolean content attribute,
+ // since its presence should be enough
+ // http://bugs.jquery.com/ticket/12359
+ div.innerHTML = "<select><option selected=''></option></select>";
+
+ // IE8 - Some boolean attributes are not treated correctly
+ if ( !div.querySelectorAll("[selected]").length ) {
+ rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
+ }
+
+ // Webkit/Opera - :checked should return selected option elements
+ // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+ // IE8 throws error here (do not put tests after this one)
+ if ( !div.querySelectorAll(":checked").length ) {
+ rbuggyQSA.push(":checked");
+ }
+ });
+
+ assert(function( div ) {
+
+ // Opera 10-12/IE9 - ^= $= *= and empty values
+ // Should not select anything
+ div.innerHTML = "<p test=''></p>";
+ if ( div.querySelectorAll("[test^='']").length ) {
+ rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
+ }
+
+ // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+ // IE8 throws error here (do not put tests after this one)
+ div.innerHTML = "<input type='hidden'/>";
+ if ( !div.querySelectorAll(":enabled").length ) {
+ rbuggyQSA.push(":enabled", ":disabled");
+ }
+ });
+
+ // rbuggyQSA always contains :focus, so no need for a length check
+ rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") );
+
+ select = function( selector, context, results, seed, xml ) {
+ // Only use querySelectorAll when not filtering,
+ // when this is not xml,
+ // and when no QSA bugs apply
+ if ( !seed && !xml && !rbuggyQSA.test( selector ) ) {
+ var groups, i,
+ old = true,
+ nid = expando,
+ newContext = context,
+ newSelector = context.nodeType === 9 && selector;
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ groups = tokenize( selector );
+
+ if ( (old = context.getAttribute("id")) ) {
+ nid = old.replace( rescape, "\\$&" );
+ } else {
+ context.setAttribute( "id", nid );
+ }
+ nid = "[id='" + nid + "'] ";
+
+ i = groups.length;
+ while ( i-- ) {
+ groups[i] = nid + groups[i].join("");
+ }
+ newContext = rsibling.test( selector ) && context.parentNode || context;
+ newSelector = groups.join(",");
+ }
+
+ if ( newSelector ) {
+ try {
+ push.apply( results, slice.call( newContext.querySelectorAll(
+ newSelector
+ ), 0 ) );
+ return results;
+ } catch(qsaError) {
+ } finally {
+ if ( !old ) {
+ context.removeAttribute("id");
+ }
+ }
+ }
+ }
+
+ return oldSelect( selector, context, results, seed, xml );
+ };
+
+ if ( matches ) {
+ assert(function( div ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9)
+ disconnectedMatch = matches.call( div, "div" );
+
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ try {
+ matches.call( div, "[test!='']:sizzle" );
+ rbuggyMatches.push( "!=", pseudos );
+ } catch ( e ) {}
+ });
+
+ // rbuggyMatches always contains :active and :focus, so no need for a length check
+ rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") );
+
+ Sizzle.matchesSelector = function( elem, expr ) {
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace( rattributeQuotes, "='$1']" );
+
+ // rbuggyMatches always contains :active, so no need for an existence check
+ if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) {
+ try {
+ var ret = matches.call( elem, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9
+ elem.document && elem.document.nodeType !== 11 ) {
+ return ret;
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle( expr, null, null, [ elem ] ).length > 0;
+ };
+ }
+ })();
+}
+
+// Deprecated
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Back-compat
+function setFilters() {}
+Expr.filters = setFilters.prototype = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
+// Override sizzle attribute retrieval
+Sizzle.attr = jQuery.attr;
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.pseudos;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})( window );
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prev(?:Until|All))/,
+ isSimple = /^.[^:#\[\.,]*$/,
+ rneedsContext = jQuery.expr.match.needsContext,
+ // methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var i, l, length, n, r, ret,
+ self = this;
+
+ if ( typeof selector !== "string" ) {
+ return jQuery( selector ).filter(function() {
+ for ( i = 0, l = self.length; i < l; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ });
+ }
+
+ ret = this.pushStack( "", "find", selector );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ length = ret.length;
+ jQuery.find( selector, this[i], ret );
+
+ if ( i > 0 ) {
+ // Make sure that the results are unique
+ for ( n = length; n < ret.length; n++ ) {
+ for ( r = 0; r < length; r++ ) {
+ if ( ret[r] === ret[n] ) {
+ ret.splice(n--, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ has: function( target ) {
+ var i,
+ targets = jQuery( target, this ),
+ len = targets.length;
+
+ return this.filter(function() {
+ for ( i = 0; i < len; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false), "not", selector);
+ },
+
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true), "filter", selector );
+ },
+
+ is: function( selector ) {
+ return !!selector && (
+ typeof selector === "string" ?
+ // If this is a positional/relative selector, check membership in the returned set
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
+ rneedsContext.test( selector ) ?
+ jQuery( selector, this.context ).index( this[0] ) >= 0 :
+ jQuery.filter( selector, this ).length > 0 :
+ this.filter( selector ).length > 0 );
+ },
+
+ closest: function( selectors, context ) {
+ var cur,
+ i = 0,
+ l = this.length,
+ ret = [],
+ pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
+
+ for ( ; i < l; i++ ) {
+ cur = this[i];
+
+ while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+ ret.push( cur );
+ break;
+ }
+ cur = cur.parentNode;
+ }
+ }
+
+ ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
+
+ return this.pushStack( ret, "closest", selectors );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+
+ // No argument, return index in parent
+ if ( !elem ) {
+ return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
+ }
+
+ // index in selector
+ if ( typeof elem === "string" ) {
+ return jQuery.inArray( this[0], jQuery( elem ) );
+ }
+
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ var set = typeof selector === "string" ?
+ jQuery( selector, context ) :
+ jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+ all = jQuery.merge( this.get(), set );
+
+ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+ all :
+ jQuery.unique( all ) );
+ },
+
+ addBack: function( selector ) {
+ return this.add( selector == null ?
+ this.prevObject : this.prevObject.filter(selector)
+ );
+ }
+});
+
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+ return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+function sibling( cur, dir ) {
+ do {
+ cur = cur[ dir ];
+ } while ( cur && cur.nodeType !== 1 );
+
+ return cur;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return sibling( elem, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return sibling( elem, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.merge( [], elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until );
+
+ if ( !runtil.test( name ) ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
+
+ if ( this.length > 1 && rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+
+ return this.pushStack( ret, name, core_slice.call( arguments ).join(",") );
+ };
+});
+
+jQuery.extend({
+ filter: function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 ?
+ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+ jQuery.find.matches(expr, elems);
+ },
+
+ dir: function( elem, dir, until ) {
+ var matched = [],
+ cur = elem[ dir ];
+
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+
+ // Can't pass null or undefined to indexOf in Firefox 4
+ // Set to 0 to skip string check
+ qualifier = qualifier || 0;
+
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ var retVal = !!qualifier.call( elem, i, elem );
+ return retVal === keep;
+ });
+
+ } else if ( qualifier.nodeType ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return ( elem === qualifier ) === keep;
+ });
+
+ } else if ( typeof qualifier === "string" ) {
+ var filtered = jQuery.grep(elements, function( elem ) {
+ return elem.nodeType === 1;
+ });
+
+ if ( isSimple.test( qualifier ) ) {
+ return jQuery.filter(qualifier, filtered, !keep);
+ } else {
+ qualifier = jQuery.filter( qualifier, filtered );
+ }
+ }
+
+ return jQuery.grep(elements, function( elem, i ) {
+ return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
+ });
+}
+function createSafeFragment( document ) {
+ var list = nodeNames.split( "|" ),
+ safeFrag = document.createDocumentFragment();
+
+ if ( safeFrag.createElement ) {
+ while ( list.length ) {
+ safeFrag.createElement(
+ list.pop()
+ );
+ }
+ }
+ return safeFrag;
+}
+
+var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
+ "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+ rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
+ rleadingWhitespace = /^\s+/,
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+ rtagName = /<([\w:]+)/,
+ rtbody = /<tbody/i,
+ rhtml = /<|&#?\w+;/,
+ rnoInnerhtml = /<(?:script|style|link)/i,
+ rnocache = /<(?:script|object|embed|option|style)/i,
+ rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
+ rcheckableType = /^(?:checkbox|radio)$/,
+ // checked="checked" or checked
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+ rscriptType = /\/(java|ecma)script/i,
+ rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g,
+ wrapMap = {
+ option: [ 1, "<select multiple='multiple'>", "</select>" ],
+ legend: [ 1, "<fieldset>", "</fieldset>" ],
+ thead: [ 1, "<table>", "</table>" ],
+ tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+ td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+ col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
+ area: [ 1, "<map>", "</map>" ],
+ _default: [ 0, "", "" ]
+ },
+ safeFragment = createSafeFragment( document ),
+ fragmentDiv = safeFragment.appendChild( document.createElement("div") );
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
+// unless wrapped in a div with non-breaking characters in front of it.
+if ( !jQuery.support.htmlSerialize ) {
+ wrapMap._default = [ 1, "X<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+ text: function( value ) {
+ return jQuery.access( this, function( value ) {
+ return value === undefined ?
+ jQuery.text( this ) :
+ this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
+ }, null, value, arguments.length );
+ },
+
+ wrapAll: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapAll( html.call(this, i) );
+ });
+ }
+
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+ if ( this[0].parentNode ) {
+ wrap.insertBefore( this[0] );
+ }
+
+ wrap.map(function() {
+ var elem = this;
+
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+ elem = elem.firstChild;
+ }
+
+ return elem;
+ }).append( this );
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapInner( html.call(this, i) );
+ });
+ }
+
+ return this.each(function() {
+ var self = jQuery( this ),
+ contents = self.contents();
+
+ if ( contents.length ) {
+ contents.wrapAll( html );
+
+ } else {
+ self.append( html );
+ }
+ });
+ },
+
+ wrap: function( html ) {
+ var isFunction = jQuery.isFunction( html );
+
+ return this.each(function(i) {
+ jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+ });
+ },
+
+ unwrap: function() {
+ return this.parent().each(function() {
+ if ( !jQuery.nodeName( this, "body" ) ) {
+ jQuery( this ).replaceWith( this.childNodes );
+ }
+ }).end();
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 || this.nodeType === 11 ) {
+ this.appendChild( elem );
+ }
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 || this.nodeType === 11 ) {
+ this.insertBefore( elem, this.firstChild );
+ }
+ });
+ },
+
+ before: function() {
+ if ( !isDisconnected( this[0] ) ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this );
+ });
+ }
+
+ if ( arguments.length ) {
+ var set = jQuery.clean( arguments );
+ return this.pushStack( jQuery.merge( set, this ), "before", this.selector );
+ }
+ },
+
+ after: function() {
+ if ( !isDisconnected( this[0] ) ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ }
+
+ if ( arguments.length ) {
+ var set = jQuery.clean( arguments );
+ return this.pushStack( jQuery.merge( this, set ), "after", this.selector );
+ }
+ },
+
+ // keepData is for internal use only--do not document
+ remove: function( selector, keepData ) {
+ var elem,
+ i = 0;
+
+ for ( ; (elem = this[i]) != null; i++ ) {
+ if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+ if ( !keepData && elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ jQuery.cleanData( [ elem ] );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ }
+ }
+
+ return this;
+ },
+
+ empty: function() {
+ var elem,
+ i = 0;
+
+ for ( ; (elem = this[i]) != null; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ }
+
+ // Remove any remaining nodes
+ while ( elem.firstChild ) {
+ elem.removeChild( elem.firstChild );
+ }
+ }
+
+ return this;
+ },
+
+ clone: function( dataAndEvents, deepDataAndEvents ) {
+ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+ return this.map( function () {
+ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+ });
+ },
+
+ html: function( value ) {
+ return jQuery.access( this, function( value ) {
+ var elem = this[0] || {},
+ i = 0,
+ l = this.length;
+
+ if ( value === undefined ) {
+ return elem.nodeType === 1 ?
+ elem.innerHTML.replace( rinlinejQuery, "" ) :
+ undefined;
+ }
+
+ // See if we can take a shortcut and just use innerHTML
+ if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+ ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) &&
+ ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
+ !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
+
+ value = value.replace( rxhtmlTag, "<$1></$2>" );
+
+ try {
+ for (; i < l; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ elem = this[i] || {};
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName( "*" ) );
+ elem.innerHTML = value;
+ }
+ }
+
+ elem = 0;
+
+ // If using innerHTML throws an exception, use the fallback method
+ } catch(e) {}
+ }
+
+ if ( elem ) {
+ this.empty().append( value );
+ }
+ }, null, value, arguments.length );
+ },
+
+ replaceWith: function( value ) {
+ if ( !isDisconnected( this[0] ) ) {
+ // Make sure that the elements are removed from the DOM before they are inserted
+ // this can help fix replacing a parent with child elements
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this), old = self.html();
+ self.replaceWith( value.call( this, i, old ) );
+ });
+ }
+
+ if ( typeof value !== "string" ) {
+ value = jQuery( value ).detach();
+ }
+
+ return this.each(function() {
+ var next = this.nextSibling,
+ parent = this.parentNode;
+
+ jQuery( this ).remove();
+
+ if ( next ) {
+ jQuery(next).before( value );
+ } else {
+ jQuery(parent).append( value );
+ }
+ });
+ }
+
+ return this.length ?
+ this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
+ this;
+ },
+
+ detach: function( selector ) {
+ return this.remove( selector, true );
+ },
+
+ domManip: function( args, table, callback ) {
+
+ // Flatten any nested arrays
+ args = [].concat.apply( [], args );
+
+ var results, first, fragment, iNoClone,
+ i = 0,
+ value = args[0],
+ scripts = [],
+ l = this.length;
+
+ // We can't cloneNode fragments that contain checked, in WebKit
+ if ( !jQuery.support.checkClone && l > 1 && typeof value === "string" && rchecked.test( value ) ) {
+ return this.each(function() {
+ jQuery(this).domManip( args, table, callback );
+ });
+ }
+
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ args[0] = value.call( this, i, table ? self.html() : undefined );
+ self.domManip( args, table, callback );
+ });
+ }
+
+ if ( this[0] ) {
+ results = jQuery.buildFragment( args, this, scripts );
+ fragment = results.fragment;
+ first = fragment.firstChild;
+
+ if ( fragment.childNodes.length === 1 ) {
+ fragment = first;
+ }
+
+ if ( first ) {
+ table = table && jQuery.nodeName( first, "tr" );
+
+ // Use the original fragment for the last item instead of the first because it can end up
+ // being emptied incorrectly in certain situations (#8070).
+ // Fragments from the fragment cache must always be cloned and never used in place.
+ for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) {
+ callback.call(
+ table && jQuery.nodeName( this[i], "table" ) ?
+ findOrAppend( this[i], "tbody" ) :
+ this[i],
+ i === iNoClone ?
+ fragment :
+ jQuery.clone( fragment, true, true )
+ );
+ }
+ }
+
+ // Fix #11809: Avoid leaking memory
+ fragment = first = null;
+
+ if ( scripts.length ) {
+ jQuery.each( scripts, function( i, elem ) {
+ if ( elem.src ) {
+ if ( jQuery.ajax ) {
+ jQuery.ajax({
+ url: elem.src,
+ type: "GET",
+ dataType: "script",
+ async: false,
+ global: false,
+ "throws": true
+ });
+ } else {
+ jQuery.error("no ajax");
+ }
+ } else {
+ jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "" ) );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ });
+ }
+ }
+
+ return this;
+ }
+});
+
+function findOrAppend( elem, tag ) {
+ return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) );
+}
+
+function cloneCopyEvent( src, dest ) {
+
+ if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+ return;
+ }
+
+ var type, i, l,
+ oldData = jQuery._data( src ),
+ curData = jQuery._data( dest, oldData ),
+ events = oldData.events;
+
+ if ( events ) {
+ delete curData.handle;
+ curData.events = {};
+
+ for ( type in events ) {
+ for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+ jQuery.event.add( dest, type, events[ type ][ i ] );
+ }
+ }
+ }
+
+ // make the cloned public data object a copy from the original
+ if ( curData.data ) {
+ curData.data = jQuery.extend( {}, curData.data );
+ }
+}
+
+function cloneFixAttributes( src, dest ) {
+ var nodeName;
+
+ // We do not need to do anything for non-Elements
+ if ( dest.nodeType !== 1 ) {
+ return;
+ }
+
+ // clearAttributes removes the attributes, which we don't want,
+ // but also removes the attachEvent events, which we *do* want
+ if ( dest.clearAttributes ) {
+ dest.clearAttributes();
+ }
+
+ // mergeAttributes, in contrast, only merges back on the
+ // original attributes, not the events
+ if ( dest.mergeAttributes ) {
+ dest.mergeAttributes( src );
+ }
+
+ nodeName = dest.nodeName.toLowerCase();
+
+ if ( nodeName === "object" ) {
+ // IE6-10 improperly clones children of object elements using classid.
+ // IE10 throws NoModificationAllowedError if parent is null, #12132.
+ if ( dest.parentNode ) {
+ dest.outerHTML = src.outerHTML;
+ }
+
+ // This path appears unavoidable for IE9. When cloning an object
+ // element in IE9, the outerHTML strategy above is not sufficient.
+ // If the src has innerHTML and the destination does not,
+ // copy the src.innerHTML into the dest.innerHTML. #10324
+ if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) {
+ dest.innerHTML = src.innerHTML;
+ }
+
+ } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
+ // IE6-8 fails to persist the checked state of a cloned checkbox
+ // or radio button. Worse, IE6-7 fail to give the cloned element
+ // a checked appearance if the defaultChecked value isn't also set
+
+ dest.defaultChecked = dest.checked = src.checked;
+
+ // IE6-7 get confused and end up setting the value of a cloned
+ // checkbox/radio button to an empty string instead of "on"
+ if ( dest.value !== src.value ) {
+ dest.value = src.value;
+ }
+
+ // IE6-8 fails to return the selected option to the default selected
+ // state when cloning options
+ } else if ( nodeName === "option" ) {
+ dest.selected = src.defaultSelected;
+
+ // IE6-8 fails to set the defaultValue to the correct value when
+ // cloning other types of input fields
+ } else if ( nodeName === "input" || nodeName === "textarea" ) {
+ dest.defaultValue = src.defaultValue;
+
+ // IE blanks contents when cloning scripts
+ } else if ( nodeName === "script" && dest.text !== src.text ) {
+ dest.text = src.text;
+ }
+
+ // Event data gets referenced instead of copied if the expando
+ // gets copied too
+ dest.removeAttribute( jQuery.expando );
+}
+
+jQuery.buildFragment = function( args, context, scripts ) {
+ var fragment, cacheable, cachehit,
+ first = args[ 0 ];
+
+ // Set context from what may come in as undefined or a jQuery collection or a node
+ // Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 &
+ // also doubles as fix for #8950 where plain objects caused createDocumentFragment exception
+ context = context || document;
+ context = !context.nodeType && context[0] || context;
+ context = context.ownerDocument || context;
+
+ // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
+ // Cloning options loses the selected state, so don't cache them
+ // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
+ // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
+ // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
+ if ( args.length === 1 && typeof first === "string" && first.length < 512 && context === document &&
+ first.charAt(0) === "<" && !rnocache.test( first ) &&
+ (jQuery.support.checkClone || !rchecked.test( first )) &&
+ (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
+
+ // Mark cacheable and look for a hit
+ cacheable = true;
+ fragment = jQuery.fragments[ first ];
+ cachehit = fragment !== undefined;
+ }
+
+ if ( !fragment ) {
+ fragment = context.createDocumentFragment();
+ jQuery.clean( args, context, fragment, scripts );
+
+ // Update the cache, but only store false
+ // unless this is a second parsing of the same content
+ if ( cacheable ) {
+ jQuery.fragments[ first ] = cachehit && fragment;
+ }
+ }
+
+ return { fragment: fragment, cacheable: cacheable };
+};
+
+jQuery.fragments = {};
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var elems,
+ i = 0,
+ ret = [],
+ insert = jQuery( selector ),
+ l = insert.length,
+ parent = this.length === 1 && this[0].parentNode;
+
+ if ( (parent == null || parent && parent.nodeType === 11 && parent.childNodes.length === 1) && l === 1 ) {
+ insert[ original ]( this[0] );
+ return this;
+ } else {
+ for ( ; i < l; i++ ) {
+ elems = ( i > 0 ? this.clone(true) : this ).get();
+ jQuery( insert[i] )[ original ]( elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, insert.selector );
+ }
+ };
+});
+
+function getAll( elem ) {
+ if ( typeof elem.getElementsByTagName !== "undefined" ) {
+ return elem.getElementsByTagName( "*" );
+
+ } else if ( typeof elem.querySelectorAll !== "undefined" ) {
+ return elem.querySelectorAll( "*" );
+
+ } else {
+ return [];
+ }
+}
+
+// Used in clean, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+ if ( rcheckableType.test( elem.type ) ) {
+ elem.defaultChecked = elem.checked;
+ }
+}
+
+jQuery.extend({
+ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+ var srcElements,
+ destElements,
+ i,
+ clone;
+
+ if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
+ clone = elem.cloneNode( true );
+
+ // IE<=8 does not properly clone detached, unknown element nodes
+ } else {
+ fragmentDiv.innerHTML = elem.outerHTML;
+ fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
+ }
+
+ if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
+ (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
+ // IE copies events bound via attachEvent when using cloneNode.
+ // Calling detachEvent on the clone will also remove the events
+ // from the original. In order to get around this, we use some
+ // proprietary methods to clear the events. Thanks to MooTools
+ // guys for this hotness.
+
+ cloneFixAttributes( elem, clone );
+
+ // Using Sizzle here is crazy slow, so we use getElementsByTagName instead
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ // Weird iteration because IE will replace the length property
+ // with an element if you are cloning the body and one of the
+ // elements on the page has a name or id of "length"
+ for ( i = 0; srcElements[i]; ++i ) {
+ // Ensure that the destination node is not null; Fixes #9587
+ if ( destElements[i] ) {
+ cloneFixAttributes( srcElements[i], destElements[i] );
+ }
+ }
+ }
+
+ // Copy the events from the original to the clone
+ if ( dataAndEvents ) {
+ cloneCopyEvent( elem, clone );
+
+ if ( deepDataAndEvents ) {
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ for ( i = 0; srcElements[i]; ++i ) {
+ cloneCopyEvent( srcElements[i], destElements[i] );
+ }
+ }
+ }
+
+ srcElements = destElements = null;
+
+ // Return the cloned set
+ return clone;
+ },
+
+ clean: function( elems, context, fragment, scripts ) {
+ var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags,
+ safe = context === document && safeFragment,
+ ret = [];
+
+ // Ensure that context is a document
+ if ( !context || typeof context.createDocumentFragment === "undefined" ) {
+ context = document;
+ }
+
+ // Use the already-created safe fragment if context permits
+ for ( i = 0; (elem = elems[i]) != null; i++ ) {
+ if ( typeof elem === "number" ) {
+ elem += "";
+ }
+
+ if ( !elem ) {
+ continue;
+ }
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" ) {
+ if ( !rhtml.test( elem ) ) {
+ elem = context.createTextNode( elem );
+ } else {
+ // Ensure a safe container in which to render the html
+ safe = safe || createSafeFragment( context );
+ div = context.createElement("div");
+ safe.appendChild( div );
+
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(rxhtmlTag, "<$1></$2>");
+
+ // Go to html and back, then peel off extra wrappers
+ tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase();
+ wrap = wrapMap[ tag ] || wrapMap._default;
+ depth = wrap[0];
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( depth-- ) {
+ div = div.lastChild;
+ }
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ hasBody = rtbody.test(elem);
+ tbody = tag === "table" && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] === "<table>" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( j = tbody.length - 1; j >= 0 ; --j ) {
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+ }
+ }
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
+ div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
+ }
+
+ elem = div.childNodes;
+
+ // Take out of fragment container (we need a fresh div each time)
+ div.parentNode.removeChild( div );
+ }
+ }
+
+ if ( elem.nodeType ) {
+ ret.push( elem );
+ } else {
+ jQuery.merge( ret, elem );
+ }
+ }
+
+ // Fix #11356: Clear elements from safeFragment
+ if ( div ) {
+ elem = div = safe = null;
+ }
+
+ // Reset defaultChecked for any radios and checkboxes
+ // about to be appended to the DOM in IE 6/7 (#8060)
+ if ( !jQuery.support.appendChecked ) {
+ for ( i = 0; (elem = ret[i]) != null; i++ ) {
+ if ( jQuery.nodeName( elem, "input" ) ) {
+ fixDefaultChecked( elem );
+ } else if ( typeof elem.getElementsByTagName !== "undefined" ) {
+ jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
+ }
+ }
+ }
+
+ // Append elements to a provided document fragment
+ if ( fragment ) {
+ // Special handling of each script element
+ handleScript = function( elem ) {
+ // Check if we consider it executable
+ if ( !elem.type || rscriptType.test( elem.type ) ) {
+ // Detach the script and store it in the scripts array (if provided) or the fragment
+ // Return truthy to indicate that it has been handled
+ return scripts ?
+ scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :
+ fragment.appendChild( elem );
+ }
+ };
+
+ for ( i = 0; (elem = ret[i]) != null; i++ ) {
+ // Check if we're done after handling an executable script
+ if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) {
+ // Append to fragment and handle embedded scripts
+ fragment.appendChild( elem );
+ if ( typeof elem.getElementsByTagName !== "undefined" ) {
+ // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
+ jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript );
+
+ // Splice the scripts into ret after their former ancestor and advance our index beyond them
+ ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
+ i += jsTags.length;
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ cleanData: function( elems, /* internal */ acceptData ) {
+ var data, id, elem, type,
+ i = 0,
+ internalKey = jQuery.expando,
+ cache = jQuery.cache,
+ deleteExpando = jQuery.support.deleteExpando,
+ special = jQuery.event.special;
+
+ for ( ; (elem = elems[i]) != null; i++ ) {
+
+ if ( acceptData || jQuery.acceptData( elem ) ) {
+
+ id = elem[ internalKey ];
+ data = id && cache[ id ];
+
+ if ( data ) {
+ if ( data.events ) {
+ for ( type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
+
+ // This is a shortcut to avoid jQuery.event.remove's overhead
+ } else {
+ jQuery.removeEvent( elem, type, data.handle );
+ }
+ }
+ }
+
+ // Remove cache only if it was not already removed by jQuery.event.remove
+ if ( cache[ id ] ) {
+
+ delete cache[ id ];
+
+ // IE does not allow us to delete expando properties from nodes,
+ // nor does it have a removeAttribute function on Document nodes;
+ // we must handle all of these cases
+ if ( deleteExpando ) {
+ delete elem[ internalKey ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( internalKey );
+
+ } else {
+ elem[ internalKey ] = null;
+ }
+
+ jQuery.deletedIds.push( id );
+ }
+ }
+ }
+ }
+ }
+});
+// Limit scope pollution from any deprecated API
+(function() {
+
+var matched, browser;
+
+// Use of jQuery.browser is frowned upon.
+// More details: http://api.jquery.com/jQuery.browser
+// jQuery.uaMatch maintained for back-compat
+jQuery.uaMatch = function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
+ /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
+ /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
+ /(msie) ([\w.]+)/.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
+ [];
+
+ return {
+ browser: match[ 1 ] || "",
+ version: match[ 2 ] || "0"
+ };
+};
+
+matched = jQuery.uaMatch( navigator.userAgent );
+browser = {};
+
+if ( matched.browser ) {
+ browser[ matched.browser ] = true;
+ browser.version = matched.version;
+}
+
+// Chrome is Webkit, but Webkit is also Safari.
+if ( browser.chrome ) {
+ browser.webkit = true;
+} else if ( browser.webkit ) {
+ browser.safari = true;
+}
+
+jQuery.browser = browser;
+
+jQuery.sub = function() {
+ function jQuerySub( selector, context ) {
+ return new jQuerySub.fn.init( selector, context );
+ }
+ jQuery.extend( true, jQuerySub, this );
+ jQuerySub.superclass = this;
+ jQuerySub.fn = jQuerySub.prototype = this();
+ jQuerySub.fn.constructor = jQuerySub;
+ jQuerySub.sub = this.sub;
+ jQuerySub.fn.init = function init( selector, context ) {
+ if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
+ context = jQuerySub( context );
+ }
+
+ return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
+ };
+ jQuerySub.fn.init.prototype = jQuerySub.fn;
+ var rootjQuerySub = jQuerySub(document);
+ return jQuerySub;
+};
+
+})();
+var curCSS, iframe, iframeDoc,
+ ralpha = /alpha\([^)]*\)/i,
+ ropacity = /opacity=([^)]*)/,
+ rposition = /^(top|right|bottom|left)$/,
+ // swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
+ // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
+ rdisplayswap = /^(none|table(?!-c[ea]).+)/,
+ rmargin = /^margin/,
+ rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
+ rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
+ rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ),
+ elemdisplay = { BODY: "block" },
+
+ cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+ cssNormalTransform = {
+ letterSpacing: 0,
+ fontWeight: 400
+ },
+
+ cssExpand = [ "Top", "Right", "Bottom", "Left" ],
+ cssPrefixes = [ "Webkit", "O", "Moz", "ms" ],
+
+ eventsToggle = jQuery.fn.toggle;
+
+// return a css property mapped to a potentially vendor prefixed property
+function vendorPropName( style, name ) {
+
+ // shortcut for names that are not vendor prefixed
+ if ( name in style ) {
+ return name;
+ }
+
+ // check for vendor prefixed names
+ var capName = name.charAt(0).toUpperCase() + name.slice(1),
+ origName = name,
+ i = cssPrefixes.length;
+
+ while ( i-- ) {
+ name = cssPrefixes[ i ] + capName;
+ if ( name in style ) {
+ return name;
+ }
+ }
+
+ return origName;
+}
+
+function isHidden( elem, el ) {
+ elem = el || elem;
+ return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
+}
+
+function showHide( elements, show ) {
+ var elem, display,
+ values = [],
+ index = 0,
+ length = elements.length;
+
+ for ( ; index < length; index++ ) {
+ elem = elements[ index ];
+ if ( !elem.style ) {
+ continue;
+ }
+ values[ index ] = jQuery._data( elem, "olddisplay" );
+ if ( show ) {
+ // Reset the inline display of this element to learn if it is
+ // being hidden by cascaded rules or not
+ if ( !values[ index ] && elem.style.display === "none" ) {
+ elem.style.display = "";
+ }
+
+ // Set elements which have been overridden with display: none
+ // in a stylesheet to whatever the default browser style is
+ // for such an element
+ if ( elem.style.display === "" && isHidden( elem ) ) {
+ values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
+ }
+ } else {
+ display = curCSS( elem, "display" );
+
+ if ( !values[ index ] && display !== "none" ) {
+ jQuery._data( elem, "olddisplay", display );
+ }
+ }
+ }
+
+ // Set the display of most of the elements in a second loop
+ // to avoid the constant reflow
+ for ( index = 0; index < length; index++ ) {
+ elem = elements[ index ];
+ if ( !elem.style ) {
+ continue;
+ }
+ if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
+ elem.style.display = show ? values[ index ] || "" : "none";
+ }
+ }
+
+ return elements;
+}
+
+jQuery.fn.extend({
+ css: function( name, value ) {
+ return jQuery.access( this, function( elem, name, value ) {
+ return value !== undefined ?
+ jQuery.style( elem, name, value ) :
+ jQuery.css( elem, name );
+ }, name, value, arguments.length > 1 );
+ },
+ show: function() {
+ return showHide( this, true );
+ },
+ hide: function() {
+ return showHide( this );
+ },
+ toggle: function( state, fn2 ) {
+ var bool = typeof state === "boolean";
+
+ if ( jQuery.isFunction( state ) && jQuery.isFunction( fn2 ) ) {
+ return eventsToggle.apply( this, arguments );
+ }
+
+ return this.each(function() {
+ if ( bool ? state : isHidden( this ) ) {
+ jQuery( this ).show();
+ } else {
+ jQuery( this ).hide();
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ // Add in style property hooks for overriding the default
+ // behavior of getting and setting a style property
+ cssHooks: {
+ opacity: {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ // We should always get a number back from opacity
+ var ret = curCSS( elem, "opacity" );
+ return ret === "" ? "1" : ret;
+
+ }
+ }
+ }
+ },
+
+ // Exclude the following css properties to add px
+ cssNumber: {
+ "fillOpacity": true,
+ "fontWeight": true,
+ "lineHeight": true,
+ "opacity": true,
+ "orphans": true,
+ "widows": true,
+ "zIndex": true,
+ "zoom": true
+ },
+
+ // Add in properties whose names you wish to fix before
+ // setting or getting the value
+ cssProps: {
+ // normalize float css property
+ "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
+ },
+
+ // Get and set the style property on a DOM Node
+ style: function( elem, name, value, extra ) {
+ // Don't set styles on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+ return;
+ }
+
+ // Make sure that we're working with the right name
+ var ret, type, hooks,
+ origName = jQuery.camelCase( name ),
+ style = elem.style;
+
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
+
+ // gets hook for the prefixed version
+ // followed by the unprefixed version
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+ // Check if we're setting a value
+ if ( value !== undefined ) {
+ type = typeof value;
+
+ // convert relative number strings (+= or -=) to relative numbers. #7345
+ if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+ value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
+ // Fixes bug #9237
+ type = "number";
+ }
+
+ // Make sure that NaN and null values aren't set. See: #7116
+ if ( value == null || type === "number" && isNaN( value ) ) {
+ return;
+ }
+
+ // If a number was passed in, add 'px' to the (except for certain CSS properties)
+ if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+ value += "px";
+ }
+
+ // If a hook was provided, use that value, otherwise just set the specified value
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
+ // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
+ // Fixes bug #5509
+ try {
+ style[ name ] = value;
+ } catch(e) {}
+ }
+
+ } else {
+ // If a hook was provided get the non-computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+ return ret;
+ }
+
+ // Otherwise just get the value from the style object
+ return style[ name ];
+ }
+ },
+
+ css: function( elem, name, numeric, extra ) {
+ var val, num, hooks,
+ origName = jQuery.camelCase( name );
+
+ // Make sure that we're working with the right name
+ name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
+
+ // gets hook for the prefixed version
+ // followed by the unprefixed version
+ hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+ // If a hook was provided get the computed value from there
+ if ( hooks && "get" in hooks ) {
+ val = hooks.get( elem, true, extra );
+ }
+
+ // Otherwise, if a way to get the computed value exists, use that
+ if ( val === undefined ) {
+ val = curCSS( elem, name );
+ }
+
+ //convert "normal" to computed value
+ if ( val === "normal" && name in cssNormalTransform ) {
+ val = cssNormalTransform[ name ];
+ }
+
+ // Return, converting to number if forced or a qualifier was provided and val looks numeric
+ if ( numeric || extra !== undefined ) {
+ num = parseFloat( val );
+ return numeric || jQuery.isNumeric( num ) ? num || 0 : val;
+ }
+ return val;
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var ret, name,
+ old = {};
+
+ // Remember the old values, and insert the new ones
+ for ( name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ ret = callback.call( elem );
+
+ // Revert the old values
+ for ( name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+
+ return ret;
+ }
+});
+
+// NOTE: To any future maintainer, we've window.getComputedStyle
+// because jsdom on node.js will break without it.
+if ( window.getComputedStyle ) {
+ curCSS = function( elem, name ) {
+ var ret, width, minWidth, maxWidth,
+ computed = window.getComputedStyle( elem, null ),
+ style = elem.style;
+
+ if ( computed ) {
+
+ // getPropertyValue is only needed for .css('filter') in IE9, see #12537
+ ret = computed.getPropertyValue( name ) || computed[ name ];
+
+ if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
+ ret = jQuery.style( elem, name );
+ }
+
+ // A tribute to the "awesome hack by Dean Edwards"
+ // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
+ // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
+ // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+ if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
+ width = style.width;
+ minWidth = style.minWidth;
+ maxWidth = style.maxWidth;
+
+ style.minWidth = style.maxWidth = style.width = ret;
+ ret = computed.width;
+
+ style.width = width;
+ style.minWidth = minWidth;
+ style.maxWidth = maxWidth;
+ }
+ }
+
+ return ret;
+ };
+} else if ( document.documentElement.currentStyle ) {
+ curCSS = function( elem, name ) {
+ var left, rsLeft,
+ ret = elem.currentStyle && elem.currentStyle[ name ],
+ style = elem.style;
+
+ // Avoid setting ret to empty string here
+ // so we don't default to auto
+ if ( ret == null && style && style[ name ] ) {
+ ret = style[ name ];
+ }
+
+ // From the awesome hack by Dean Edwards
+ // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ // but not position css attributes, as those are proportional to the parent element instead
+ // and we can't measure the parent instead because it might trigger a "stacking dolls" problem
+ if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
+
+ // Remember the original values
+ left = style.left;
+ rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ }
+ style.left = name === "fontSize" ? "1em" : ret;
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret === "" ? "auto" : ret;
+ };
+}
+
+function setPositiveNumber( elem, value, subtract ) {
+ var matches = rnumsplit.exec( value );
+ return matches ?
+ Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
+ value;
+}
+
+function augmentWidthOrHeight( elem, name, extra, isBorderBox ) {
+ var i = extra === ( isBorderBox ? "border" : "content" ) ?
+ // If we already have the right measurement, avoid augmentation
+ 4 :
+ // Otherwise initialize for horizontal or vertical properties
+ name === "width" ? 1 : 0,
+
+ val = 0;
+
+ for ( ; i < 4; i += 2 ) {
+ // both box models exclude margin, so add it if we want it
+ if ( extra === "margin" ) {
+ // we use jQuery.css instead of curCSS here
+ // because of the reliableMarginRight CSS hook!
+ val += jQuery.css( elem, extra + cssExpand[ i ], true );
+ }
+
+ // From this point on we use curCSS for maximum performance (relevant in animations)
+ if ( isBorderBox ) {
+ // border-box includes padding, so remove it if we want content
+ if ( extra === "content" ) {
+ val -= parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0;
+ }
+
+ // at this point, extra isn't border nor margin, so remove border
+ if ( extra !== "margin" ) {
+ val -= parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
+ }
+ } else {
+ // at this point, extra isn't content, so add padding
+ val += parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0;
+
+ // at this point, extra isn't content nor padding, so add border
+ if ( extra !== "padding" ) {
+ val += parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
+ }
+ }
+ }
+
+ return val;
+}
+
+function getWidthOrHeight( elem, name, extra ) {
+
+ // Start with offset property, which is equivalent to the border-box value
+ var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+ valueIsBorderBox = true,
+ isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box";
+
+ // some non-html elements return undefined for offsetWidth, so check for null/undefined
+ // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
+ // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
+ if ( val <= 0 || val == null ) {
+ // Fall back to computed then uncomputed css if necessary
+ val = curCSS( elem, name );
+ if ( val < 0 || val == null ) {
+ val = elem.style[ name ];
+ }
+
+ // Computed unit is not pixels. Stop here and return.
+ if ( rnumnonpx.test(val) ) {
+ return val;
+ }
+
+ // we need the check for style in case a browser which returns unreliable values
+ // for getComputedStyle silently falls back to the reliable elem.style
+ valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
+
+ // Normalize "", auto, and prepare for extra
+ val = parseFloat( val ) || 0;
+ }
+
+ // use the active box-sizing model to add/subtract irrelevant styles
+ return ( val +
+ augmentWidthOrHeight(
+ elem,
+ name,
+ extra || ( isBorderBox ? "border" : "content" ),
+ valueIsBorderBox
+ )
+ ) + "px";
+}
+
+
+// Try to determine the default display value of an element
+function css_defaultDisplay( nodeName ) {
+ if ( elemdisplay[ nodeName ] ) {
+ return elemdisplay[ nodeName ];
+ }
+
+ var elem = jQuery( "<" + nodeName + ">" ).appendTo( document.body ),
+ display = elem.css("display");
+ elem.remove();
+
+ // If the simple way fails,
+ // get element's real default display by attaching it to a temp iframe
+ if ( display === "none" || display === "" ) {
+ // Use the already-created iframe if possible
+ iframe = document.body.appendChild(
+ iframe || jQuery.extend( document.createElement("iframe"), {
+ frameBorder: 0,
+ width: 0,
+ height: 0
+ })
+ );
+
+ // Create a cacheable copy of the iframe document on first call.
+ // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
+ // document to it; WebKit & Firefox won't allow reusing the iframe document.
+ if ( !iframeDoc || !iframe.createElement ) {
+ iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
+ iframeDoc.write("<!doctype html><html><body>");
+ iframeDoc.close();
+ }
+
+ elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) );
+
+ display = curCSS( elem, "display" );
+ document.body.removeChild( iframe );
+ }
+
+ // Store the correct default display
+ elemdisplay[ nodeName ] = display;
+
+ return display;
+}
+
+jQuery.each([ "height", "width" ], function( i, name ) {
+ jQuery.cssHooks[ name ] = {
+ get: function( elem, computed, extra ) {
+ if ( computed ) {
+ // certain elements can have dimension info if we invisibly show them
+ // however, it must have a current display style that would benefit from this
+ if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, "display" ) ) ) {
+ return jQuery.swap( elem, cssShow, function() {
+ return getWidthOrHeight( elem, name, extra );
+ });
+ } else {
+ return getWidthOrHeight( elem, name, extra );
+ }
+ }
+ },
+
+ set: function( elem, value, extra ) {
+ return setPositiveNumber( elem, value, extra ?
+ augmentWidthOrHeight(
+ elem,
+ name,
+ extra,
+ jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"
+ ) : 0
+ );
+ }
+ };
+});
+
+if ( !jQuery.support.opacity ) {
+ jQuery.cssHooks.opacity = {
+ get: function( elem, computed ) {
+ // IE uses filters for opacity
+ return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
+ ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
+ computed ? "1" : "";
+ },
+
+ set: function( elem, value ) {
+ var style = elem.style,
+ currentStyle = elem.currentStyle,
+ opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
+ filter = currentStyle && currentStyle.filter || style.filter || "";
+
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ style.zoom = 1;
+
+ // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
+ if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
+ style.removeAttribute ) {
+
+ // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
+ // if "filter:" is present at all, clearType is disabled, we want to avoid this
+ // style.removeAttribute is IE Only, but so apparently is this code path...
+ style.removeAttribute( "filter" );
+
+ // if there there is no filter style applied in a css rule, we are done
+ if ( currentStyle && !currentStyle.filter ) {
+ return;
+ }
+ }
+
+ // otherwise, set new filter values
+ style.filter = ralpha.test( filter ) ?
+ filter.replace( ralpha, opacity ) :
+ filter + " " + opacity;
+ }
+ };
+}
+
+// These hooks cannot be added until DOM ready because the support test
+// for it is not run until after DOM ready
+jQuery(function() {
+ if ( !jQuery.support.reliableMarginRight ) {
+ jQuery.cssHooks.marginRight = {
+ get: function( elem, computed ) {
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ // Work around by temporarily setting element display to inline-block
+ return jQuery.swap( elem, { "display": "inline-block" }, function() {
+ if ( computed ) {
+ return curCSS( elem, "marginRight" );
+ }
+ });
+ }
+ };
+ }
+
+ // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+ // getComputedStyle returns percent when specified for top/left/bottom/right
+ // rather than make the css module depend on the offset module, we just check for it here
+ if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
+ jQuery.each( [ "top", "left" ], function( i, prop ) {
+ jQuery.cssHooks[ prop ] = {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ var ret = curCSS( elem, prop );
+ // if curCSS returns percentage, fallback to offset
+ return rnumnonpx.test( ret ) ? jQuery( elem ).position()[ prop ] + "px" : ret;
+ }
+ }
+ };
+ });
+ }
+
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.hidden = function( elem ) {
+ return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || curCSS( elem, "display" )) === "none");
+ };
+
+ jQuery.expr.filters.visible = function( elem ) {
+ return !jQuery.expr.filters.hidden( elem );
+ };
+}
+
+// These hooks are used by animate to expand properties
+jQuery.each({
+ margin: "",
+ padding: "",
+ border: "Width"
+}, function( prefix, suffix ) {
+ jQuery.cssHooks[ prefix + suffix ] = {
+ expand: function( value ) {
+ var i,
+
+ // assumes a single number if not a string
+ parts = typeof value === "string" ? value.split(" ") : [ value ],
+ expanded = {};
+
+ for ( i = 0; i < 4; i++ ) {
+ expanded[ prefix + cssExpand[ i ] + suffix ] =
+ parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+ }
+
+ return expanded;
+ }
+ };
+
+ if ( !rmargin.test( prefix ) ) {
+ jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
+ }
+});
+var r20 = /%20/g,
+ rbracket = /\[\]$/,
+ rCRLF = /\r?\n/g,
+ rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+ rselectTextarea = /^(?:select|textarea)/i;
+
+jQuery.fn.extend({
+ serialize: function() {
+ return jQuery.param( this.serializeArray() );
+ },
+ serializeArray: function() {
+ return this.map(function(){
+ return this.elements ? jQuery.makeArray( this.elements ) : this;
+ })
+ .filter(function(){
+ return this.name && !this.disabled &&
+ ( this.checked || rselectTextarea.test( this.nodeName ) ||
+ rinput.test( this.type ) );
+ })
+ .map(function( i, elem ){
+ var val = jQuery( this ).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray( val ) ?
+ jQuery.map( val, function( val, i ){
+ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }) :
+ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }).get();
+ }
+});
+
+//Serialize an array of form elements or a set of
+//key/values into a query string
+jQuery.param = function( a, traditional ) {
+ var prefix,
+ s = [],
+ add = function( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
+ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+ };
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( prefix in a ) {
+ buildParams( prefix, a[ prefix ], traditional, add );
+ }
+ }
+
+ // Return the resulting serialization
+ return s.join( "&" ).replace( r20, "+" );
+};
+
+function buildParams( prefix, obj, traditional, add ) {
+ var name;
+
+ if ( jQuery.isArray( obj ) ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || rbracket.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+
+ } else {
+ // If array item is non-scalar (array or object), encode its
+ // numeric index to resolve deserialization ambiguity issues.
+ // Note that rack (as of 1.0.0) can't currently deserialize
+ // nested arrays properly, and attempting to do so may cause
+ // a server error. Possible fixes are to modify rack's
+ // deserialization algorithm or to provide an option or flag
+ // to force array serialization to be shallow.
+ buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+ }
+ });
+
+ } else if ( !traditional && jQuery.type( obj ) === "object" ) {
+ // Serialize object item.
+ for ( name in obj ) {
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+ }
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+}
+var
+ // Document location
+ ajaxLocParts,
+ ajaxLocation,
+
+ rhash = /#.*$/,
+ rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+ // #7653, #8125, #8152: local protocol detection
+ rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,
+ rnoContent = /^(?:GET|HEAD)$/,
+ rprotocol = /^\/\//,
+ rquery = /\?/,
+ rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+ rts = /([?&])_=[^&]*/,
+ rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
+
+ // Keep a copy of the old load method
+ _load = jQuery.fn.load,
+
+ /* Prefilters
+ * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+ * 2) These are called:
+ * - BEFORE asking for a transport
+ * - AFTER param serialization (s.data is a string if s.processData is true)
+ * 3) key is the dataType
+ * 4) the catchall symbol "*" can be used
+ * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+ */
+ prefilters = {},
+
+ /* Transports bindings
+ * 1) key is the dataType
+ * 2) the catchall symbol "*" can be used
+ * 3) selection will start with transport dataType and THEN go to "*" if needed
+ */
+ transports = {},
+
+ // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+ allTypes = ["*/"] + ["*"];
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+ ajaxLocation = location.href;
+} catch( e ) {
+ // Use the href attribute of an A element
+ // since IE will modify it given document.location
+ ajaxLocation = document.createElement( "a" );
+ ajaxLocation.href = "";
+ ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+ // dataTypeExpression is optional and defaults to "*"
+ return function( dataTypeExpression, func ) {
+
+ if ( typeof dataTypeExpression !== "string" ) {
+ func = dataTypeExpression;
+ dataTypeExpression = "*";
+ }
+
+ var dataType, list, placeBefore,
+ dataTypes = dataTypeExpression.toLowerCase().split( core_rspace ),
+ i = 0,
+ length = dataTypes.length;
+
+ if ( jQuery.isFunction( func ) ) {
+ // For each dataType in the dataTypeExpression
+ for ( ; i < length; i++ ) {
+ dataType = dataTypes[ i ];
+ // We control if we're asked to add before
+ // any existing element
+ placeBefore = /^\+/.test( dataType );
+ if ( placeBefore ) {
+ dataType = dataType.substr( 1 ) || "*";
+ }
+ list = structure[ dataType ] = structure[ dataType ] || [];
+ // then we add to the structure accordingly
+ list[ placeBefore ? "unshift" : "push" ]( func );
+ }
+ }
+ };
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
+ dataType /* internal */, inspected /* internal */ ) {
+
+ dataType = dataType || options.dataTypes[ 0 ];
+ inspected = inspected || {};
+
+ inspected[ dataType ] = true;
+
+ var selection,
+ list = structure[ dataType ],
+ i = 0,
+ length = list ? list.length : 0,
+ executeOnly = ( structure === prefilters );
+
+ for ( ; i < length && ( executeOnly || !selection ); i++ ) {
+ selection = list[ i ]( options, originalOptions, jqXHR );
+ // If we got redirected to another dataType
+ // we try there if executing only and not done already
+ if ( typeof selection === "string" ) {
+ if ( !executeOnly || inspected[ selection ] ) {
+ selection = undefined;
+ } else {
+ options.dataTypes.unshift( selection );
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, selection, inspected );
+ }
+ }
+ }
+ // If we're only executing or nothing was selected
+ // we try the catchall dataType if not done already
+ if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, "*", inspected );
+ }
+ // unnecessary when only executing (prefilters)
+ // but it'll be ignored by the caller in that case
+ return selection;
+}
+
+// A special extend for ajax options
+// that takes "flat" options (not to be deep extended)
+// Fixes #9887
+function ajaxExtend( target, src ) {
+ var key, deep,
+ flatOptions = jQuery.ajaxSettings.flatOptions || {};
+ for ( key in src ) {
+ if ( src[ key ] !== undefined ) {
+ ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
+ }
+ }
+ if ( deep ) {
+ jQuery.extend( true, target, deep );
+ }
+}
+
+jQuery.fn.load = function( url, params, callback ) {
+ if ( typeof url !== "string" && _load ) {
+ return _load.apply( this, arguments );
+ }
+
+ // Don't do a request if no elements are being requested
+ if ( !this.length ) {
+ return this;
+ }
+
+ var selector, type, response,
+ self = this,
+ off = url.indexOf(" ");
+
+ if ( off >= 0 ) {
+ selector = url.slice( off, url.length );
+ url = url.slice( 0, off );
+ }
+
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+
+ // We assume that it's the callback
+ callback = params;
+ params = undefined;
+
+ // Otherwise, build a param string
+ } else if ( params && typeof params === "object" ) {
+ type = "POST";
+ }
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+
+ // if "type" variable is undefined, then "GET" method will be used
+ type: type,
+ dataType: "html",
+ data: params,
+ complete: function( jqXHR, status ) {
+ if ( callback ) {
+ self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
+ }
+ }
+ }).done(function( responseText ) {
+
+ // Save response for use in complete callback
+ response = arguments;
+
+ // See if a selector was specified
+ self.html( selector ?
+
+ // Create a dummy div to hold the results
+ jQuery("<div>")
+
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append( responseText.replace( rscript, "" ) )
+
+ // Locate the specified elements
+ .find( selector ) :
+
+ // If not, just inject the full result
+ responseText );
+
+ });
+
+ return this;
+};
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
+ jQuery.fn[ o ] = function( f ){
+ return this.on( o, f );
+ };
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+ jQuery[ method ] = function( url, data, callback, type ) {
+ // shift arguments if data argument was omitted
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = undefined;
+ }
+
+ return jQuery.ajax({
+ type: method,
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ };
+});
+
+jQuery.extend({
+
+ getScript: function( url, callback ) {
+ return jQuery.get( url, undefined, callback, "script" );
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get( url, data, callback, "json" );
+ },
+
+ // Creates a full fledged settings object into target
+ // with both ajaxSettings and settings fields.
+ // If target is omitted, writes into ajaxSettings.
+ ajaxSetup: function( target, settings ) {
+ if ( settings ) {
+ // Building a settings object
+ ajaxExtend( target, jQuery.ajaxSettings );
+ } else {
+ // Extending ajaxSettings
+ settings = target;
+ target = jQuery.ajaxSettings;
+ }
+ ajaxExtend( target, settings );
+ return target;
+ },
+
+ ajaxSettings: {
+ url: ajaxLocation,
+ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+ global: true,
+ type: "GET",
+ contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+ processData: true,
+ async: true,
+ /*
+ timeout: 0,
+ data: null,
+ dataType: null,
+ username: null,
+ password: null,
+ cache: null,
+ throws: false,
+ traditional: false,
+ headers: {},
+ */
+
+ accepts: {
+ xml: "application/xml, text/xml",
+ html: "text/html",
+ text: "text/plain",
+ json: "application/json, text/javascript",
+ "*": allTypes
+ },
+
+ contents: {
+ xml: /xml/,
+ html: /html/,
+ json: /json/
+ },
+
+ responseFields: {
+ xml: "responseXML",
+ text: "responseText"
+ },
+
+ // List of data converters
+ // 1) key format is "source_type destination_type" (a single space in-between)
+ // 2) the catchall symbol "*" can be used for source_type
+ converters: {
+
+ // Convert anything to text
+ "* text": window.String,
+
+ // Text to html (true = no transformation)
+ "text html": true,
+
+ // Evaluate text as a json expression
+ "text json": jQuery.parseJSON,
+
+ // Parse text as xml
+ "text xml": jQuery.parseXML
+ },
+
+ // For options that shouldn't be deep extended:
+ // you can add your own custom options here if
+ // and when you create one that shouldn't be
+ // deep extended (see ajaxExtend)
+ flatOptions: {
+ context: true,
+ url: true
+ }
+ },
+
+ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+ ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+ // Main method
+ ajax: function( url, options ) {
+
+ // If url is an object, simulate pre-1.5 signature
+ if ( typeof url === "object" ) {
+ options = url;
+ url = undefined;
+ }
+
+ // Force options to be an object
+ options = options || {};
+
+ var // ifModified key
+ ifModifiedKey,
+ // Response headers
+ responseHeadersString,
+ responseHeaders,
+ // transport
+ transport,
+ // timeout handle
+ timeoutTimer,
+ // Cross-domain detection vars
+ parts,
+ // To know if global events are to be dispatched
+ fireGlobals,
+ // Loop variable
+ i,
+ // Create the final options object
+ s = jQuery.ajaxSetup( {}, options ),
+ // Callbacks context
+ callbackContext = s.context || s,
+ // Context for global events
+ // It's the callbackContext if one was provided in the options
+ // and if it's a DOM node or a jQuery collection
+ globalEventContext = callbackContext !== s &&
+ ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
+ jQuery( callbackContext ) : jQuery.event,
+ // Deferreds
+ deferred = jQuery.Deferred(),
+ completeDeferred = jQuery.Callbacks( "once memory" ),
+ // Status-dependent callbacks
+ statusCode = s.statusCode || {},
+ // Headers (they are sent all at once)
+ requestHeaders = {},
+ requestHeadersNames = {},
+ // The jqXHR state
+ state = 0,
+ // Default abort message
+ strAbort = "canceled",
+ // Fake xhr
+ jqXHR = {
+
+ readyState: 0,
+
+ // Caches the header
+ setRequestHeader: function( name, value ) {
+ if ( !state ) {
+ var lname = name.toLowerCase();
+ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+ requestHeaders[ name ] = value;
+ }
+ return this;
+ },
+
+ // Raw string
+ getAllResponseHeaders: function() {
+ return state === 2 ? responseHeadersString : null;
+ },
+
+ // Builds headers hashtable if needed
+ getResponseHeader: function( key ) {
+ var match;
+ if ( state === 2 ) {
+ if ( !responseHeaders ) {
+ responseHeaders = {};
+ while( ( match = rheaders.exec( responseHeadersString ) ) ) {
+ responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+ }
+ }
+ match = responseHeaders[ key.toLowerCase() ];
+ }
+ return match === undefined ? null : match;
+ },
+
+ // Overrides response content-type header
+ overrideMimeType: function( type ) {
+ if ( !state ) {
+ s.mimeType = type;
+ }
+ return this;
+ },
+
+ // Cancel the request
+ abort: function( statusText ) {
+ statusText = statusText || strAbort;
+ if ( transport ) {
+ transport.abort( statusText );
+ }
+ done( 0, statusText );
+ return this;
+ }
+ };
+
+ // Callback for when everything is done
+ // It is defined here because jslint complains if it is declared
+ // at the end of the function (which would be more logical and readable)
+ function done( status, nativeStatusText, responses, headers ) {
+ var isSuccess, success, error, response, modified,
+ statusText = nativeStatusText;
+
+ // Called once
+ if ( state === 2 ) {
+ return;
+ }
+
+ // State is "done" now
+ state = 2;
+
+ // Clear timeout if it exists
+ if ( timeoutTimer ) {
+ clearTimeout( timeoutTimer );
+ }
+
+ // Dereference transport for early garbage collection
+ // (no matter how long the jqXHR object will be used)
+ transport = undefined;
+
+ // Cache response headers
+ responseHeadersString = headers || "";
+
+ // Set readyState
+ jqXHR.readyState = status > 0 ? 4 : 0;
+
+ // Get response data
+ if ( responses ) {
+ response = ajaxHandleResponses( s, jqXHR, responses );
+ }
+
+ // If successful, handle type chaining
+ if ( status >= 200 && status < 300 || status === 304 ) {
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+
+ modified = jqXHR.getResponseHeader("Last-Modified");
+ if ( modified ) {
+ jQuery.lastModified[ ifModifiedKey ] = modified;
+ }
+ modified = jqXHR.getResponseHeader("Etag");
+ if ( modified ) {
+ jQuery.etag[ ifModifiedKey ] = modified;
+ }
+ }
+
+ // If not modified
+ if ( status === 304 ) {
+
+ statusText = "notmodified";
+ isSuccess = true;
+
+ // If we have data
+ } else {
+
+ isSuccess = ajaxConvert( s, response );
+ statusText = isSuccess.state;
+ success = isSuccess.data;
+ error = isSuccess.error;
+ isSuccess = !error;
+ }
+ } else {
+ // We extract error from statusText
+ // then normalize statusText and status for non-aborts
+ error = statusText;
+ if ( !statusText || status ) {
+ statusText = "error";
+ if ( status < 0 ) {
+ status = 0;
+ }
+ }
+ }
+
+ // Set data for the fake xhr object
+ jqXHR.status = status;
+ jqXHR.statusText = ( nativeStatusText || statusText ) + "";
+
+ // Success/Error
+ if ( isSuccess ) {
+ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+ } else {
+ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+ }
+
+ // Status-dependent callbacks
+ jqXHR.statusCode( statusCode );
+ statusCode = undefined;
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
+ [ jqXHR, s, isSuccess ? success : error ] );
+ }
+
+ // Complete
+ completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+ // Handle the global AJAX counter
+ if ( !( --jQuery.active ) ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ }
+ }
+
+ // Attach deferreds
+ deferred.promise( jqXHR );
+ jqXHR.success = jqXHR.done;
+ jqXHR.error = jqXHR.fail;
+ jqXHR.complete = completeDeferred.add;
+
+ // Status-dependent callbacks
+ jqXHR.statusCode = function( map ) {
+ if ( map ) {
+ var tmp;
+ if ( state < 2 ) {
+ for ( tmp in map ) {
+ statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
+ }
+ } else {
+ tmp = map[ jqXHR.status ];
+ jqXHR.always( tmp );
+ }
+ }
+ return this;
+ };
+
+ // Remove hash character (#7531: and string promotion)
+ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+ // We also use the url parameter if available
+ s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+ // Extract dataTypes list
+ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace );
+
+ // A cross-domain request is in order when we have a protocol:host:port mismatch
+ if ( s.crossDomain == null ) {
+ parts = rurl.exec( s.url.toLowerCase() );
+ s.crossDomain = !!( parts &&
+ ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
+ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
+ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
+ );
+ }
+
+ // Convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
+ s.data = jQuery.param( s.data, s.traditional );
+ }
+
+ // Apply prefilters
+ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+ // If request was aborted inside a prefilter, stop there
+ if ( state === 2 ) {
+ return jqXHR;
+ }
+
+ // We can fire global events as of now if asked to
+ fireGlobals = s.global;
+
+ // Uppercase the type
+ s.type = s.type.toUpperCase();
+
+ // Determine if request has content
+ s.hasContent = !rnoContent.test( s.type );
+
+ // Watch for a new set of requests
+ if ( fireGlobals && jQuery.active++ === 0 ) {
+ jQuery.event.trigger( "ajaxStart" );
+ }
+
+ // More options handling for requests with no content
+ if ( !s.hasContent ) {
+
+ // If data is available, append data to url
+ if ( s.data ) {
+ s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
+ // #9682: remove data so that it's not used in an eventual retry
+ delete s.data;
+ }
+
+ // Get ifModifiedKey before adding the anti-cache parameter
+ ifModifiedKey = s.url;
+
+ // Add anti-cache in url if needed
+ if ( s.cache === false ) {
+
+ var ts = jQuery.now(),
+ // try replacing _= if it is there
+ ret = s.url.replace( rts, "$1_=" + ts );
+
+ // if nothing was replaced, add timestamp to the end
+ s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
+ }
+ }
+
+ // Set the correct header, if data is being sent
+ if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+ jqXHR.setRequestHeader( "Content-Type", s.contentType );
+ }
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ ifModifiedKey = ifModifiedKey || s.url;
+ if ( jQuery.lastModified[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
+ }
+ if ( jQuery.etag[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
+ }
+ }
+
+ // Set the Accepts header for the server, depending on the dataType
+ jqXHR.setRequestHeader(
+ "Accept",
+ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+ s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+ s.accepts[ "*" ]
+ );
+
+ // Check for headers option
+ for ( i in s.headers ) {
+ jqXHR.setRequestHeader( i, s.headers[ i ] );
+ }
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+ // Abort if not done already and return
+ return jqXHR.abort();
+
+ }
+
+ // aborting is no longer a cancellation
+ strAbort = "abort";
+
+ // Install callbacks on deferreds
+ for ( i in { success: 1, error: 1, complete: 1 } ) {
+ jqXHR[ i ]( s[ i ] );
+ }
+
+ // Get transport
+ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+ // If no transport, we auto-abort
+ if ( !transport ) {
+ done( -1, "No Transport" );
+ } else {
+ jqXHR.readyState = 1;
+ // Send global event
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+ }
+ // Timeout
+ if ( s.async && s.timeout > 0 ) {
+ timeoutTimer = setTimeout( function(){
+ jqXHR.abort( "timeout" );
+ }, s.timeout );
+ }
+
+ try {
+ state = 1;
+ transport.send( requestHeaders, done );
+ } catch (e) {
+ // Propagate exception as error if not done
+ if ( state < 2 ) {
+ done( -1, e );
+ // Simply rethrow otherwise
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ return jqXHR;
+ },
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ etag: {}
+
+});
+
+/* Handles responses to an ajax request:
+ * - sets all responseXXX fields accordingly
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+
+ var ct, type, finalDataType, firstDataType,
+ contents = s.contents,
+ dataTypes = s.dataTypes,
+ responseFields = s.responseFields;
+
+ // Fill responseXXX fields
+ for ( type in responseFields ) {
+ if ( type in responses ) {
+ jqXHR[ responseFields[type] ] = responses[ type ];
+ }
+ }
+
+ // Remove auto dataType and get content-type in the process
+ while( dataTypes[ 0 ] === "*" ) {
+ dataTypes.shift();
+ if ( ct === undefined ) {
+ ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
+ }
+ }
+
+ // Check if we're dealing with a known content-type
+ if ( ct ) {
+ for ( type in contents ) {
+ if ( contents[ type ] && contents[ type ].test( ct ) ) {
+ dataTypes.unshift( type );
+ break;
+ }
+ }
+ }
+
+ // Check to see if we have a response for the expected dataType
+ if ( dataTypes[ 0 ] in responses ) {
+ finalDataType = dataTypes[ 0 ];
+ } else {
+ // Try convertible dataTypes
+ for ( type in responses ) {
+ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+ finalDataType = type;
+ break;
+ }
+ if ( !firstDataType ) {
+ firstDataType = type;
+ }
+ }
+ // Or just use first one
+ finalDataType = finalDataType || firstDataType;
+ }
+
+ // If we found a dataType
+ // We add the dataType to the list if needed
+ // and return the corresponding response
+ if ( finalDataType ) {
+ if ( finalDataType !== dataTypes[ 0 ] ) {
+ dataTypes.unshift( finalDataType );
+ }
+ return responses[ finalDataType ];
+ }
+}
+
+// Chain conversions given the request and the original response
+function ajaxConvert( s, response ) {
+
+ var conv, conv2, current, tmp,
+ // Work with a copy of dataTypes in case we need to modify it for conversion
+ dataTypes = s.dataTypes.slice(),
+ prev = dataTypes[ 0 ],
+ converters = {},
+ i = 0;
+
+ // Apply the dataFilter if provided
+ if ( s.dataFilter ) {
+ response = s.dataFilter( response, s.dataType );
+ }
+
+ // Create converters map with lowercased keys
+ if ( dataTypes[ 1 ] ) {
+ for ( conv in s.converters ) {
+ converters[ conv.toLowerCase() ] = s.converters[ conv ];
+ }
+ }
+
+ // Convert to each sequential dataType, tolerating list modification
+ for ( ; (current = dataTypes[++i]); ) {
+
+ // There's only work to do if current dataType is non-auto
+ if ( current !== "*" ) {
+
+ // Convert response if prev dataType is non-auto and differs from current
+ if ( prev !== "*" && prev !== current ) {
+
+ // Seek a direct converter
+ conv = converters[ prev + " " + current ] || converters[ "* " + current ];
+
+ // If none found, seek a pair
+ if ( !conv ) {
+ for ( conv2 in converters ) {
+
+ // If conv2 outputs current
+ tmp = conv2.split(" ");
+ if ( tmp[ 1 ] === current ) {
+
+ // If prev can be converted to accepted input
+ conv = converters[ prev + " " + tmp[ 0 ] ] ||
+ converters[ "* " + tmp[ 0 ] ];
+ if ( conv ) {
+ // Condense equivalence converters
+ if ( conv === true ) {
+ conv = converters[ conv2 ];
+
+ // Otherwise, insert the intermediate dataType
+ } else if ( converters[ conv2 ] !== true ) {
+ current = tmp[ 0 ];
+ dataTypes.splice( i--, 0, current );
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ // Apply converter (if not an equivalence)
+ if ( conv !== true ) {
+
+ // Unless errors are allowed to bubble, catch and return them
+ if ( conv && s["throws"] ) {
+ response = conv( response );
+ } else {
+ try {
+ response = conv( response );
+ } catch ( e ) {
+ return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
+ }
+ }
+ }
+ }
+
+ // Update prev for next iteration
+ prev = current;
+ }
+ }
+
+ return { state: "success", data: response };
+}
+var oldCallbacks = [],
+ rquestion = /\?/,
+ rjsonp = /(=)\?(?=&|$)|\?\?/,
+ nonce = jQuery.now();
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+ jsonp: "callback",
+ jsonpCallback: function() {
+ var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
+ this[ callback ] = true;
+ return callback;
+ }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+ var callbackName, overwritten, responseContainer,
+ data = s.data,
+ url = s.url,
+ hasCallback = s.jsonp !== false,
+ replaceInUrl = hasCallback && rjsonp.test( url ),
+ replaceInData = hasCallback && !replaceInUrl && typeof data === "string" &&
+ !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") &&
+ rjsonp.test( data );
+
+ // Handle iff the expected data type is "jsonp" or we have a parameter to set
+ if ( s.dataTypes[ 0 ] === "jsonp" || replaceInUrl || replaceInData ) {
+
+ // Get callback name, remembering preexisting value associated with it
+ callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
+ s.jsonpCallback() :
+ s.jsonpCallback;
+ overwritten = window[ callbackName ];
+
+ // Insert callback into url or form data
+ if ( replaceInUrl ) {
+ s.url = url.replace( rjsonp, "$1" + callbackName );
+ } else if ( replaceInData ) {
+ s.data = data.replace( rjsonp, "$1" + callbackName );
+ } else if ( hasCallback ) {
+ s.url += ( rquestion.test( url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
+ }
+
+ // Use data converter to retrieve json after script execution
+ s.converters["script json"] = function() {
+ if ( !responseContainer ) {
+ jQuery.error( callbackName + " was not called" );
+ }
+ return responseContainer[ 0 ];
+ };
+
+ // force json dataType
+ s.dataTypes[ 0 ] = "json";
+
+ // Install callback
+ window[ callbackName ] = function() {
+ responseContainer = arguments;
+ };
+
+ // Clean-up function (fires after converters)
+ jqXHR.always(function() {
+ // Restore preexisting value
+ window[ callbackName ] = overwritten;
+
+ // Save back as free
+ if ( s[ callbackName ] ) {
+ // make sure that re-using the options doesn't screw things around
+ s.jsonpCallback = originalSettings.jsonpCallback;
+
+ // save the callback name for future use
+ oldCallbacks.push( callbackName );
+ }
+
+ // Call if it was a function and we have a response
+ if ( responseContainer && jQuery.isFunction( overwritten ) ) {
+ overwritten( responseContainer[ 0 ] );
+ }
+
+ responseContainer = overwritten = undefined;
+ });
+
+ // Delegate to script
+ return "script";
+ }
+});
+// Install script dataType
+jQuery.ajaxSetup({
+ accepts: {
+ script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+ },
+ contents: {
+ script: /javascript|ecmascript/
+ },
+ converters: {
+ "text script": function( text ) {
+ jQuery.globalEval( text );
+ return text;
+ }
+ }
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter( "script", function( s ) {
+ if ( s.cache === undefined ) {
+ s.cache = false;
+ }
+ if ( s.crossDomain ) {
+ s.type = "GET";
+ s.global = false;
+ }
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+ // This transport only deals with cross domain requests
+ if ( s.crossDomain ) {
+
+ var script,
+ head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
+
+ return {
+
+ send: function( _, callback ) {
+
+ script = document.createElement( "script" );
+
+ script.async = "async";
+
+ if ( s.scriptCharset ) {
+ script.charset = s.scriptCharset;
+ }
+
+ script.src = s.url;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+ if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+
+ // Remove the script
+ if ( head && script.parentNode ) {
+ head.removeChild( script );
+ }
+
+ // Dereference the script
+ script = undefined;
+
+ // Callback if not abort
+ if ( !isAbort ) {
+ callback( 200, "success" );
+ }
+ }
+ };
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709 and #4378).
+ head.insertBefore( script, head.firstChild );
+ },
+
+ abort: function() {
+ if ( script ) {
+ script.onload( 0, 1 );
+ }
+ }
+ };
+ }
+});
+var xhrCallbacks,
+ // #5280: Internet Explorer will keep connections alive if we don't abort on unload
+ xhrOnUnloadAbort = window.ActiveXObject ? function() {
+ // Abort all pending requests
+ for ( var key in xhrCallbacks ) {
+ xhrCallbacks[ key ]( 0, 1 );
+ }
+ } : false,
+ xhrId = 0;
+
+// Functions to create xhrs
+function createStandardXHR() {
+ try {
+ return new window.XMLHttpRequest();
+ } catch( e ) {}
+}
+
+function createActiveXHR() {
+ try {
+ return new window.ActiveXObject( "Microsoft.XMLHTTP" );
+ } catch( e ) {}
+}
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+ /* Microsoft failed to properly
+ * implement the XMLHttpRequest in IE7 (can't request local files),
+ * so we use the ActiveXObject when it is available
+ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+ * we need a fallback.
+ */
+ function() {
+ return !this.isLocal && createStandardXHR() || createActiveXHR();
+ } :
+ // For all other browsers, use the standard XMLHttpRequest object
+ createStandardXHR;
+
+// Determine support properties
+(function( xhr ) {
+ jQuery.extend( jQuery.support, {
+ ajax: !!xhr,
+ cors: !!xhr && ( "withCredentials" in xhr )
+ });
+})( jQuery.ajaxSettings.xhr() );
+
+// Create transport if the browser can provide an xhr
+if ( jQuery.support.ajax ) {
+
+ jQuery.ajaxTransport(function( s ) {
+ // Cross domain only allowed if supported through XMLHttpRequest
+ if ( !s.crossDomain || jQuery.support.cors ) {
+
+ var callback;
+
+ return {
+ send: function( headers, complete ) {
+
+ // Get a new xhr
+ var handle, i,
+ xhr = s.xhr();
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if ( s.username ) {
+ xhr.open( s.type, s.url, s.async, s.username, s.password );
+ } else {
+ xhr.open( s.type, s.url, s.async );
+ }
+
+ // Apply custom fields if provided
+ if ( s.xhrFields ) {
+ for ( i in s.xhrFields ) {
+ xhr[ i ] = s.xhrFields[ i ];
+ }
+ }
+
+ // Override mime type if needed
+ if ( s.mimeType && xhr.overrideMimeType ) {
+ xhr.overrideMimeType( s.mimeType );
+ }
+
+ // X-Requested-With header
+ // For cross-domain requests, seeing as conditions for a preflight are
+ // akin to a jigsaw puzzle, we simply never set it to be sure.
+ // (it can always be set on a per-request basis or even using ajaxSetup)
+ // For same-domain requests, won't change header if already provided.
+ if ( !s.crossDomain && !headers["X-Requested-With"] ) {
+ headers[ "X-Requested-With" ] = "XMLHttpRequest";
+ }
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ for ( i in headers ) {
+ xhr.setRequestHeader( i, headers[ i ] );
+ }
+ } catch( _ ) {}
+
+ // Do send the request
+ // This may raise an exception which is actually
+ // handled in jQuery.ajax (so no try/catch here)
+ xhr.send( ( s.hasContent && s.data ) || null );
+
+ // Listener
+ callback = function( _, isAbort ) {
+
+ var status,
+ statusText,
+ responseHeaders,
+ responses,
+ xml;
+
+ // Firefox throws exceptions when accessing properties
+ // of an xhr when a network error occurred
+ // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
+ try {
+
+ // Was never called and is aborted or complete
+ if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+ // Only called once
+ callback = undefined;
+
+ // Do not keep as active anymore
+ if ( handle ) {
+ xhr.onreadystatechange = jQuery.noop;
+ if ( xhrOnUnloadAbort ) {
+ delete xhrCallbacks[ handle ];
+ }
+ }
+
+ // If it's an abort
+ if ( isAbort ) {
+ // Abort it manually if needed
+ if ( xhr.readyState !== 4 ) {
+ xhr.abort();
+ }
+ } else {
+ status = xhr.status;
+ responseHeaders = xhr.getAllResponseHeaders();
+ responses = {};
+ xml = xhr.responseXML;
+
+ // Construct response list
+ if ( xml && xml.documentElement /* #4958 */ ) {
+ responses.xml = xml;
+ }
+
+ // When requesting binary data, IE6-9 will throw an exception
+ // on any attempt to access responseText (#11426)
+ try {
+ responses.text = xhr.responseText;
+ } catch( e ) {
+ }
+
+ // Firefox throws an exception when accessing
+ // statusText for faulty cross-domain requests
+ try {
+ statusText = xhr.statusText;
+ } catch( e ) {
+ // We normalize with Webkit giving an empty statusText
+ statusText = "";
+ }
+
+ // Filter status for non standard behaviors
+
+ // If the request is local and we have data: assume a success
+ // (success with no data won't get notified, that's the best we
+ // can do given current implementations)
+ if ( !status && s.isLocal && !s.crossDomain ) {
+ status = responses.text ? 200 : 404;
+ // IE - #1450: sometimes returns 1223 when it should be 204
+ } else if ( status === 1223 ) {
+ status = 204;
+ }
+ }
+ }
+ } catch( firefoxAccessException ) {
+ if ( !isAbort ) {
+ complete( -1, firefoxAccessException );
+ }
+ }
+
+ // Call complete if needed
+ if ( responses ) {
+ complete( status, statusText, responses, responseHeaders );
+ }
+ };
+
+ if ( !s.async ) {
+ // if we're in sync mode we fire the callback
+ callback();
+ } else if ( xhr.readyState === 4 ) {
+ // (IE6 & IE7) if it's in cache and has been
+ // retrieved directly we need to fire the callback
+ setTimeout( callback, 0 );
+ } else {
+ handle = ++xhrId;
+ if ( xhrOnUnloadAbort ) {
+ // Create the active xhrs callbacks list if needed
+ // and attach the unload handler
+ if ( !xhrCallbacks ) {
+ xhrCallbacks = {};
+ jQuery( window ).unload( xhrOnUnloadAbort );
+ }
+ // Add to list of active xhrs callbacks
+ xhrCallbacks[ handle ] = callback;
+ }
+ xhr.onreadystatechange = callback;
+ }
+ },
+
+ abort: function() {
+ if ( callback ) {
+ callback(0,1);
+ }
+ }
+ };
+ }
+ });
+}
+var fxNow, timerId,
+ rfxtypes = /^(?:toggle|show|hide)$/,
+ rfxnum = new RegExp( "^(?:([-+])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
+ rrun = /queueHooks$/,
+ animationPrefilters = [ defaultPrefilter ],
+ tweeners = {
+ "*": [function( prop, value ) {
+ var end, unit,
+ tween = this.createTween( prop, value ),
+ parts = rfxnum.exec( value ),
+ target = tween.cur(),
+ start = +target || 0,
+ scale = 1,
+ maxIterations = 20;
+
+ if ( parts ) {
+ end = +parts[2];
+ unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+
+ // We need to compute starting value
+ if ( unit !== "px" && start ) {
+ // Iteratively approximate from a nonzero starting point
+ // Prefer the current property, because this process will be trivial if it uses the same units
+ // Fallback to end or a simple constant
+ start = jQuery.css( tween.elem, prop, true ) || end || 1;
+
+ do {
+ // If previous iteration zeroed out, double until we get *something*
+ // Use a string for doubling factor so we don't accidentally see scale as unchanged below
+ scale = scale || ".5";
+
+ // Adjust and apply
+ start = start / scale;
+ jQuery.style( tween.elem, prop, start + unit );
+
+ // Update scale, tolerating zero or NaN from tween.cur()
+ // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
+ } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
+ }
+
+ tween.unit = unit;
+ tween.start = start;
+ // If a +=/-= token was provided, we're doing a relative animation
+ tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
+ }
+ return tween;
+ }]
+ };
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+ setTimeout(function() {
+ fxNow = undefined;
+ }, 0 );
+ return ( fxNow = jQuery.now() );
+}
+
+function createTweens( animation, props ) {
+ jQuery.each( props, function( prop, value ) {
+ var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+ index = 0,
+ length = collection.length;
+ for ( ; index < length; index++ ) {
+ if ( collection[ index ].call( animation, prop, value ) ) {
+
+ // we're done with this property
+ return;
+ }
+ }
+ });
+}
+
+function Animation( elem, properties, options ) {
+ var result,
+ index = 0,
+ tweenerIndex = 0,
+ length = animationPrefilters.length,
+ deferred = jQuery.Deferred().always( function() {
+ // don't match elem in the :animated selector
+ delete tick.elem;
+ }),
+ tick = function() {
+ var currentTime = fxNow || createFxNow(),
+ remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
+ // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
+ temp = remaining / animation.duration || 0,
+ percent = 1 - temp,
+ index = 0,
+ length = animation.tweens.length;
+
+ for ( ; index < length ; index++ ) {
+ animation.tweens[ index ].run( percent );
+ }
+
+ deferred.notifyWith( elem, [ animation, percent, remaining ]);
+
+ if ( percent < 1 && length ) {
+ return remaining;
+ } else {
+ deferred.resolveWith( elem, [ animation ] );
+ return false;
+ }
+ },
+ animation = deferred.promise({
+ elem: elem,
+ props: jQuery.extend( {}, properties ),
+ opts: jQuery.extend( true, { specialEasing: {} }, options ),
+ originalProperties: properties,
+ originalOptions: options,
+ startTime: fxNow || createFxNow(),
+ duration: options.duration,
+ tweens: [],
+ createTween: function( prop, end, easing ) {
+ var tween = jQuery.Tween( elem, animation.opts, prop, end,
+ animation.opts.specialEasing[ prop ] || animation.opts.easing );
+ animation.tweens.push( tween );
+ return tween;
+ },
+ stop: function( gotoEnd ) {
+ var index = 0,
+ // if we are going to the end, we want to run all the tweens
+ // otherwise we skip this part
+ length = gotoEnd ? animation.tweens.length : 0;
+
+ for ( ; index < length ; index++ ) {
+ animation.tweens[ index ].run( 1 );
+ }
+
+ // resolve when we played the last frame
+ // otherwise, reject
+ if ( gotoEnd ) {
+ deferred.resolveWith( elem, [ animation, gotoEnd ] );
+ } else {
+ deferred.rejectWith( elem, [ animation, gotoEnd ] );
+ }
+ return this;
+ }
+ }),
+ props = animation.props;
+
+ propFilter( props, animation.opts.specialEasing );
+
+ for ( ; index < length ; index++ ) {
+ result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
+ if ( result ) {
+ return result;
+ }
+ }
+
+ createTweens( animation, props );
+
+ if ( jQuery.isFunction( animation.opts.start ) ) {
+ animation.opts.start.call( elem, animation );
+ }
+
+ jQuery.fx.timer(
+ jQuery.extend( tick, {
+ anim: animation,
+ queue: animation.opts.queue,
+ elem: elem
+ })
+ );
+
+ // attach callbacks from options
+ return animation.progress( animation.opts.progress )
+ .done( animation.opts.done, animation.opts.complete )
+ .fail( animation.opts.fail )
+ .always( animation.opts.always );
+}
+
+function propFilter( props, specialEasing ) {
+ var index, name, easing, value, hooks;
+
+ // camelCase, specialEasing and expand cssHook pass
+ for ( index in props ) {
+ name = jQuery.camelCase( index );
+ easing = specialEasing[ name ];
+ value = props[ index ];
+ if ( jQuery.isArray( value ) ) {
+ easing = value[ 1 ];
+ value = props[ index ] = value[ 0 ];
+ }
+
+ if ( index !== name ) {
+ props[ name ] = value;
+ delete props[ index ];
+ }
+
+ hooks = jQuery.cssHooks[ name ];
+ if ( hooks && "expand" in hooks ) {
+ value = hooks.expand( value );
+ delete props[ name ];
+
+ // not quite $.extend, this wont overwrite keys already present.
+ // also - reusing 'index' from above because we have the correct "name"
+ for ( index in value ) {
+ if ( !( index in props ) ) {
+ props[ index ] = value[ index ];
+ specialEasing[ index ] = easing;
+ }
+ }
+ } else {
+ specialEasing[ name ] = easing;
+ }
+ }
+}
+
+jQuery.Animation = jQuery.extend( Animation, {
+
+ tweener: function( props, callback ) {
+ if ( jQuery.isFunction( props ) ) {
+ callback = props;
+ props = [ "*" ];
+ } else {
+ props = props.split(" ");
+ }
+
+ var prop,
+ index = 0,
+ length = props.length;
+
+ for ( ; index < length ; index++ ) {
+ prop = props[ index ];
+ tweeners[ prop ] = tweeners[ prop ] || [];
+ tweeners[ prop ].unshift( callback );
+ }
+ },
+
+ prefilter: function( callback, prepend ) {
+ if ( prepend ) {
+ animationPrefilters.unshift( callback );
+ } else {
+ animationPrefilters.push( callback );
+ }
+ }
+});
+
+function defaultPrefilter( elem, props, opts ) {
+ var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire,
+ anim = this,
+ style = elem.style,
+ orig = {},
+ handled = [],
+ hidden = elem.nodeType && isHidden( elem );
+
+ // handle queue: false promises
+ if ( !opts.queue ) {
+ hooks = jQuery._queueHooks( elem, "fx" );
+ if ( hooks.unqueued == null ) {
+ hooks.unqueued = 0;
+ oldfire = hooks.empty.fire;
+ hooks.empty.fire = function() {
+ if ( !hooks.unqueued ) {
+ oldfire();
+ }
+ };
+ }
+ hooks.unqueued++;
+
+ anim.always(function() {
+ // doing this makes sure that the complete handler will be called
+ // before this completes
+ anim.always(function() {
+ hooks.unqueued--;
+ if ( !jQuery.queue( elem, "fx" ).length ) {
+ hooks.empty.fire();
+ }
+ });
+ });
+ }
+
+ // height/width overflow pass
+ if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
+ // Make sure that nothing sneaks out
+ // Record all 3 overflow attributes because IE does not
+ // change the overflow attribute when overflowX and
+ // overflowY are set to the same value
+ opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
+
+ // Set display property to inline-block for height/width
+ // animations on inline elements that are having width/height animated
+ if ( jQuery.css( elem, "display" ) === "inline" &&
+ jQuery.css( elem, "float" ) === "none" ) {
+
+ // inline-level elements accept inline-block;
+ // block-level elements need to be inline with layout
+ if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
+ style.display = "inline-block";
+
+ } else {
+ style.zoom = 1;
+ }
+ }
+ }
+
+ if ( opts.overflow ) {
+ style.overflow = "hidden";
+ if ( !jQuery.support.shrinkWrapBlocks ) {
+ anim.done(function() {
+ style.overflow = opts.overflow[ 0 ];
+ style.overflowX = opts.overflow[ 1 ];
+ style.overflowY = opts.overflow[ 2 ];
+ });
+ }
+ }
+
+
+ // show/hide pass
+ for ( index in props ) {
+ value = props[ index ];
+ if ( rfxtypes.exec( value ) ) {
+ delete props[ index ];
+ toggle = toggle || value === "toggle";
+ if ( value === ( hidden ? "hide" : "show" ) ) {
+ continue;
+ }
+ handled.push( index );
+ }
+ }
+
+ length = handled.length;
+ if ( length ) {
+ dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
+ if ( "hidden" in dataShow ) {
+ hidden = dataShow.hidden;
+ }
+
+ // store state if its toggle - enables .stop().toggle() to "reverse"
+ if ( toggle ) {
+ dataShow.hidden = !hidden;
+ }
+ if ( hidden ) {
+ jQuery( elem ).show();
+ } else {
+ anim.done(function() {
+ jQuery( elem ).hide();
+ });
+ }
+ anim.done(function() {
+ var prop;
+ jQuery.removeData( elem, "fxshow", true );
+ for ( prop in orig ) {
+ jQuery.style( elem, prop, orig[ prop ] );
+ }
+ });
+ for ( index = 0 ; index < length ; index++ ) {
+ prop = handled[ index ];
+ tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
+ orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
+
+ if ( !( prop in dataShow ) ) {
+ dataShow[ prop ] = tween.start;
+ if ( hidden ) {
+ tween.end = tween.start;
+ tween.start = prop === "width" || prop === "height" ? 1 : 0;
+ }
+ }
+ }
+ }
+}
+
+function Tween( elem, options, prop, end, easing ) {
+ return new Tween.prototype.init( elem, options, prop, end, easing );
+}
+jQuery.Tween = Tween;
+
+Tween.prototype = {
+ constructor: Tween,
+ init: function( elem, options, prop, end, easing, unit ) {
+ this.elem = elem;
+ this.prop = prop;
+ this.easing = easing || "swing";
+ this.options = options;
+ this.start = this.now = this.cur();
+ this.end = end;
+ this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+ },
+ cur: function() {
+ var hooks = Tween.propHooks[ this.prop ];
+
+ return hooks && hooks.get ?
+ hooks.get( this ) :
+ Tween.propHooks._default.get( this );
+ },
+ run: function( percent ) {
+ var eased,
+ hooks = Tween.propHooks[ this.prop ];
+
+ if ( this.options.duration ) {
+ this.pos = eased = jQuery.easing[ this.easing ](
+ percent, this.options.duration * percent, 0, 1, this.options.duration
+ );
+ } else {
+ this.pos = eased = percent;
+ }
+ this.now = ( this.end - this.start ) * eased + this.start;
+
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
+ }
+
+ if ( hooks && hooks.set ) {
+ hooks.set( this );
+ } else {
+ Tween.propHooks._default.set( this );
+ }
+ return this;
+ }
+};
+
+Tween.prototype.init.prototype = Tween.prototype;
+
+Tween.propHooks = {
+ _default: {
+ get: function( tween ) {
+ var result;
+
+ if ( tween.elem[ tween.prop ] != null &&
+ (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
+ return tween.elem[ tween.prop ];
+ }
+
+ // passing any value as a 4th parameter to .css will automatically
+ // attempt a parseFloat and fallback to a string if the parse fails
+ // so, simple values such as "10px" are parsed to Float.
+ // complex values such as "rotate(1rad)" are returned as is.
+ result = jQuery.css( tween.elem, tween.prop, false, "" );
+ // Empty strings, null, undefined and "auto" are converted to 0.
+ return !result || result === "auto" ? 0 : result;
+ },
+ set: function( tween ) {
+ // use step hook for back compat - use cssHook if its there - use .style if its
+ // available and use plain properties where available
+ if ( jQuery.fx.step[ tween.prop ] ) {
+ jQuery.fx.step[ tween.prop ]( tween );
+ } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
+ jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
+ } else {
+ tween.elem[ tween.prop ] = tween.now;
+ }
+ }
+ }
+};
+
+// Remove in 2.0 - this supports IE8's panic based approach
+// to setting things on disconnected nodes
+
+Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
+ set: function( tween ) {
+ if ( tween.elem.nodeType && tween.elem.parentNode ) {
+ tween.elem[ tween.prop ] = tween.now;
+ }
+ }
+};
+
+jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
+ var cssFn = jQuery.fn[ name ];
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return speed == null || typeof speed === "boolean" ||
+ // special check for .toggle( handler, handler, ... )
+ ( !i && jQuery.isFunction( speed ) && jQuery.isFunction( easing ) ) ?
+ cssFn.apply( this, arguments ) :
+ this.animate( genFx( name, true ), speed, easing, callback );
+ };
+});
+
+jQuery.fn.extend({
+ fadeTo: function( speed, to, easing, callback ) {
+
+ // show any hidden elements after setting opacity to 0
+ return this.filter( isHidden ).css( "opacity", 0 ).show()
+
+ // animate to the value specified
+ .end().animate({ opacity: to }, speed, easing, callback );
+ },
+ animate: function( prop, speed, easing, callback ) {
+ var empty = jQuery.isEmptyObject( prop ),
+ optall = jQuery.speed( speed, easing, callback ),
+ doAnimation = function() {
+ // Operate on a copy of prop so per-property easing won't be lost
+ var anim = Animation( this, jQuery.extend( {}, prop ), optall );
+
+ // Empty animations resolve immediately
+ if ( empty ) {
+ anim.stop( true );
+ }
+ };
+
+ return empty || optall.queue === false ?
+ this.each( doAnimation ) :
+ this.queue( optall.queue, doAnimation );
+ },
+ stop: function( type, clearQueue, gotoEnd ) {
+ var stopQueue = function( hooks ) {
+ var stop = hooks.stop;
+ delete hooks.stop;
+ stop( gotoEnd );
+ };
+
+ if ( typeof type !== "string" ) {
+ gotoEnd = clearQueue;
+ clearQueue = type;
+ type = undefined;
+ }
+ if ( clearQueue && type !== false ) {
+ this.queue( type || "fx", [] );
+ }
+
+ return this.each(function() {
+ var dequeue = true,
+ index = type != null && type + "queueHooks",
+ timers = jQuery.timers,
+ data = jQuery._data( this );
+
+ if ( index ) {
+ if ( data[ index ] && data[ index ].stop ) {
+ stopQueue( data[ index ] );
+ }
+ } else {
+ for ( index in data ) {
+ if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
+ stopQueue( data[ index ] );
+ }
+ }
+ }
+
+ for ( index = timers.length; index--; ) {
+ if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+ timers[ index ].anim.stop( gotoEnd );
+ dequeue = false;
+ timers.splice( index, 1 );
+ }
+ }
+
+ // start the next in the queue if the last step wasn't forced
+ // timers currently will call their complete callbacks, which will dequeue
+ // but only if they were gotoEnd
+ if ( dequeue || !gotoEnd ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ }
+});
+
+// Generate parameters to create a standard animation
+function genFx( type, includeWidth ) {
+ var which,
+ attrs = { height: type },
+ i = 0;
+
+ // if we include width, step value is 1 to do all cssExpand values,
+ // if we don't include width, step value is 2 to skip over Left and Right
+ includeWidth = includeWidth? 1 : 0;
+ for( ; i < 4 ; i += 2 - includeWidth ) {
+ which = cssExpand[ i ];
+ attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
+ }
+
+ if ( includeWidth ) {
+ attrs.opacity = attrs.width = type;
+ }
+
+ return attrs;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx("show"),
+ slideUp: genFx("hide"),
+ slideToggle: genFx("toggle"),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" },
+ fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return this.animate( props, speed, easing, callback );
+ };
+});
+
+jQuery.speed = function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+ // normalize opt.queue - true/undefined/null -> "fx"
+ if ( opt.queue == null || opt.queue === true ) {
+ opt.queue = "fx";
+ }
+
+ // Queueing
+ opt.old = opt.complete;
+
+ opt.complete = function() {
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
+ }
+
+ if ( opt.queue ) {
+ jQuery.dequeue( this, opt.queue );
+ }
+ };
+
+ return opt;
+};
+
+jQuery.easing = {
+ linear: function( p ) {
+ return p;
+ },
+ swing: function( p ) {
+ return 0.5 - Math.cos( p*Math.PI ) / 2;
+ }
+};
+
+jQuery.timers = [];
+jQuery.fx = Tween.prototype.init;
+jQuery.fx.tick = function() {
+ var timer,
+ timers = jQuery.timers,
+ i = 0;
+
+ fxNow = jQuery.now();
+
+ for ( ; i < timers.length; i++ ) {
+ timer = timers[ i ];
+ // Checks the timer has not already been removed
+ if ( !timer() && timers[ i ] === timer ) {
+ timers.splice( i--, 1 );
+ }
+ }
+
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+ fxNow = undefined;
+};
+
+jQuery.fx.timer = function( timer ) {
+ if ( timer() && jQuery.timers.push( timer ) && !timerId ) {
+ timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
+ }
+};
+
+jQuery.fx.interval = 13;
+
+jQuery.fx.stop = function() {
+ clearInterval( timerId );
+ timerId = null;
+};
+
+jQuery.fx.speeds = {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+};
+
+// Back Compat <1.8 extension point
+jQuery.fx.step = {};
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+ };
+}
+var rroot = /^(?:body|html)$/i;
+
+jQuery.fn.offset = function( options ) {
+ if ( arguments.length ) {
+ return options === undefined ?
+ this :
+ this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ var docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft,
+ box = { top: 0, left: 0 },
+ elem = this[ 0 ],
+ doc = elem && elem.ownerDocument;
+
+ if ( !doc ) {
+ return;
+ }
+
+ if ( (body = doc.body) === elem ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ docElem = doc.documentElement;
+
+ // Make sure it's not a disconnected DOM node
+ if ( !jQuery.contains( docElem, elem ) ) {
+ return box;
+ }
+
+ // If we don't have gBCR, just use 0,0 rather than error
+ // BlackBerry 5, iOS 3 (original iPhone)
+ if ( typeof elem.getBoundingClientRect !== "undefined" ) {
+ box = elem.getBoundingClientRect();
+ }
+ win = getWindow( doc );
+ clientTop = docElem.clientTop || body.clientTop || 0;
+ clientLeft = docElem.clientLeft || body.clientLeft || 0;
+ scrollTop = win.pageYOffset || docElem.scrollTop;
+ scrollLeft = win.pageXOffset || docElem.scrollLeft;
+ return {
+ top: box.top + scrollTop - clientTop,
+ left: box.left + scrollLeft - clientLeft
+ };
+};
+
+jQuery.offset = {
+
+ bodyOffset: function( body ) {
+ var top = body.offsetTop,
+ left = body.offsetLeft;
+
+ if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) {
+ top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
+ left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
+ }
+
+ return { top: top, left: left };
+ },
+
+ setOffset: function( elem, options, i ) {
+ var position = jQuery.css( elem, "position" );
+
+ // set position first, in-case top/left are set even on static elem
+ if ( position === "static" ) {
+ elem.style.position = "relative";
+ }
+
+ var curElem = jQuery( elem ),
+ curOffset = curElem.offset(),
+ curCSSTop = jQuery.css( elem, "top" ),
+ curCSSLeft = jQuery.css( elem, "left" ),
+ calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+ props = {}, curPosition = {}, curTop, curLeft;
+
+ // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+ if ( calculatePosition ) {
+ curPosition = curElem.position();
+ curTop = curPosition.top;
+ curLeft = curPosition.left;
+ } else {
+ curTop = parseFloat( curCSSTop ) || 0;
+ curLeft = parseFloat( curCSSLeft ) || 0;
+ }
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ if ( options.top != null ) {
+ props.top = ( options.top - curOffset.top ) + curTop;
+ }
+ if ( options.left != null ) {
+ props.left = ( options.left - curOffset.left ) + curLeft;
+ }
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+
+jQuery.fn.extend({
+
+ position: function() {
+ if ( !this[0] ) {
+ return;
+ }
+
+ var elem = this[0],
+
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent(),
+
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
+ offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
+
+ // Add offsetParent borders
+ parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
+ parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
+
+ // Subtract the two offsets
+ return {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || document.body;
+ while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent || document.body;
+ });
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
+ var top = /Y/.test( prop );
+
+ jQuery.fn[ method ] = function( val ) {
+ return jQuery.access( this, function( elem, method, val ) {
+ var win = getWindow( elem );
+
+ if ( val === undefined ) {
+ return win ? (prop in win) ? win[ prop ] :
+ win.document.documentElement[ method ] :
+ elem[ method ];
+ }
+
+ if ( win ) {
+ win.scrollTo(
+ !top ? val : jQuery( win ).scrollLeft(),
+ top ? val : jQuery( win ).scrollTop()
+ );
+
+ } else {
+ elem[ method ] = val;
+ }
+ }, method, val, arguments.length, null );
+ };
+});
+
+function getWindow( elem ) {
+ return jQuery.isWindow( elem ) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+ jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+ // margin is only for outerHeight, outerWidth
+ jQuery.fn[ funcName ] = function( margin, value ) {
+ var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+ extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+
+ return jQuery.access( this, function( elem, type, value ) {
+ var doc;
+
+ if ( jQuery.isWindow( elem ) ) {
+ // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+ // isn't a whole lot we can do. See pull request at this URL for discussion:
+ // https://github.com/jquery/jquery/pull/764
+ return elem.document.documentElement[ "client" + name ];
+ }
+
+ // Get document width or height
+ if ( elem.nodeType === 9 ) {
+ doc = elem.documentElement;
+
+ // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
+ // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
+ return Math.max(
+ elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+ elem.body[ "offset" + name ], doc[ "offset" + name ],
+ doc[ "client" + name ]
+ );
+ }
+
+ return value === undefined ?
+ // Get width or height on the element, requesting but not forcing parseFloat
+ jQuery.css( elem, type, value, extra ) :
+
+ // Set width or height on the element
+ jQuery.style( elem, type, value, extra );
+ }, type, chainable ? margin : undefined, chainable, null );
+ };
+ });
+});
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+// Expose jQuery as an AMD module, but only for AMD loaders that
+// understand the issues with loading multiple versions of jQuery
+// in a page that all might call define(). The loader will indicate
+// they have special allowances for multiple jQuery versions by
+// specifying define.amd.jQuery = true. Register as a named module,
+// since jQuery can be concatenated with other files that may use define,
+// but not use a proper concatenation script that understands anonymous
+// AMD modules. A named AMD is safest and most robust way to register.
+// Lowercase jquery is used because AMD module names are derived from
+// file names, and jQuery is normally delivered in a lowercase file name.
+// Do this after creating the global so that if an AMD module wants to call
+// noConflict to hide this version of jQuery, it will work.
+if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
+ define( "jquery", [], function () { return jQuery; } );
+}
+
+})( window );
diff --git a/storage/mroonga/vendor/groonga/packages/debian/missing-sources/underscore.js b/storage/mroonga/vendor/groonga/packages/debian/missing-sources/underscore.js
new file mode 100644
index 00000000000..208d4cd890c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/missing-sources/underscore.js
@@ -0,0 +1,999 @@
+// Underscore.js 1.3.1
+// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
+// Underscore is freely distributable under the MIT license.
+// Portions of Underscore are inspired or borrowed from Prototype,
+// Oliver Steele's Functional, and John Resig's Micro-Templating.
+// For all details and documentation:
+// http://documentcloud.github.com/underscore
+
+(function() {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `global` on the server.
+ var root = this;
+
+ // Save the previous value of the `_` variable.
+ var previousUnderscore = root._;
+
+ // Establish the object that gets returned to break out of a loop iteration.
+ var breaker = {};
+
+ // Save bytes in the minified (but not gzipped) version:
+ var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
+
+ // Create quick reference variables for speed access to core prototypes.
+ var slice = ArrayProto.slice,
+ unshift = ArrayProto.unshift,
+ toString = ObjProto.toString,
+ hasOwnProperty = ObjProto.hasOwnProperty;
+
+ // All **ECMAScript 5** native function implementations that we hope to use
+ // are declared here.
+ var
+ nativeForEach = ArrayProto.forEach,
+ nativeMap = ArrayProto.map,
+ nativeReduce = ArrayProto.reduce,
+ nativeReduceRight = ArrayProto.reduceRight,
+ nativeFilter = ArrayProto.filter,
+ nativeEvery = ArrayProto.every,
+ nativeSome = ArrayProto.some,
+ nativeIndexOf = ArrayProto.indexOf,
+ nativeLastIndexOf = ArrayProto.lastIndexOf,
+ nativeIsArray = Array.isArray,
+ nativeKeys = Object.keys,
+ nativeBind = FuncProto.bind;
+
+ // Create a safe reference to the Underscore object for use below.
+ var _ = function(obj) { return new wrapper(obj); };
+
+ // Export the Underscore object for **Node.js**, with
+ // backwards-compatibility for the old `require()` API. If we're in
+ // the browser, add `_` as a global object via a string identifier,
+ // for Closure Compiler "advanced" mode.
+ if (typeof exports !== 'undefined') {
+ if (typeof module !== 'undefined' && module.exports) {
+ exports = module.exports = _;
+ }
+ exports._ = _;
+ } else {
+ root['_'] = _;
+ }
+
+ // Current version.
+ _.VERSION = '1.3.1';
+
+ // Collection Functions
+ // --------------------
+
+ // The cornerstone, an `each` implementation, aka `forEach`.
+ // Handles objects with the built-in `forEach`, arrays, and raw objects.
+ // Delegates to **ECMAScript 5**'s native `forEach` if available.
+ var each = _.each = _.forEach = function(obj, iterator, context) {
+ if (obj == null) return;
+ if (nativeForEach && obj.forEach === nativeForEach) {
+ obj.forEach(iterator, context);
+ } else if (obj.length === +obj.length) {
+ for (var i = 0, l = obj.length; i < l; i++) {
+ if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
+ }
+ } else {
+ for (var key in obj) {
+ if (_.has(obj, key)) {
+ if (iterator.call(context, obj[key], key, obj) === breaker) return;
+ }
+ }
+ }
+ };
+
+ // Return the results of applying the iterator to each element.
+ // Delegates to **ECMAScript 5**'s native `map` if available.
+ _.map = _.collect = function(obj, iterator, context) {
+ var results = [];
+ if (obj == null) return results;
+ if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
+ each(obj, function(value, index, list) {
+ results[results.length] = iterator.call(context, value, index, list);
+ });
+ if (obj.length === +obj.length) results.length = obj.length;
+ return results;
+ };
+
+ // **Reduce** builds up a single result from a list of values, aka `inject`,
+ // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
+ _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
+ var initial = arguments.length > 2;
+ if (obj == null) obj = [];
+ if (nativeReduce && obj.reduce === nativeReduce) {
+ if (context) iterator = _.bind(iterator, context);
+ return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
+ }
+ each(obj, function(value, index, list) {
+ if (!initial) {
+ memo = value;
+ initial = true;
+ } else {
+ memo = iterator.call(context, memo, value, index, list);
+ }
+ });
+ if (!initial) throw new TypeError('Reduce of empty array with no initial value');
+ return memo;
+ };
+
+ // The right-associative version of reduce, also known as `foldr`.
+ // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
+ _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
+ var initial = arguments.length > 2;
+ if (obj == null) obj = [];
+ if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
+ if (context) iterator = _.bind(iterator, context);
+ return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
+ }
+ var reversed = _.toArray(obj).reverse();
+ if (context && !initial) iterator = _.bind(iterator, context);
+ return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
+ };
+
+ // Return the first value which passes a truth test. Aliased as `detect`.
+ _.find = _.detect = function(obj, iterator, context) {
+ var result;
+ any(obj, function(value, index, list) {
+ if (iterator.call(context, value, index, list)) {
+ result = value;
+ return true;
+ }
+ });
+ return result;
+ };
+
+ // Return all the elements that pass a truth test.
+ // Delegates to **ECMAScript 5**'s native `filter` if available.
+ // Aliased as `select`.
+ _.filter = _.select = function(obj, iterator, context) {
+ var results = [];
+ if (obj == null) return results;
+ if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
+ each(obj, function(value, index, list) {
+ if (iterator.call(context, value, index, list)) results[results.length] = value;
+ });
+ return results;
+ };
+
+ // Return all the elements for which a truth test fails.
+ _.reject = function(obj, iterator, context) {
+ var results = [];
+ if (obj == null) return results;
+ each(obj, function(value, index, list) {
+ if (!iterator.call(context, value, index, list)) results[results.length] = value;
+ });
+ return results;
+ };
+
+ // Determine whether all of the elements match a truth test.
+ // Delegates to **ECMAScript 5**'s native `every` if available.
+ // Aliased as `all`.
+ _.every = _.all = function(obj, iterator, context) {
+ var result = true;
+ if (obj == null) return result;
+ if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
+ each(obj, function(value, index, list) {
+ if (!(result = result && iterator.call(context, value, index, list))) return breaker;
+ });
+ return result;
+ };
+
+ // Determine if at least one element in the object matches a truth test.
+ // Delegates to **ECMAScript 5**'s native `some` if available.
+ // Aliased as `any`.
+ var any = _.some = _.any = function(obj, iterator, context) {
+ iterator || (iterator = _.identity);
+ var result = false;
+ if (obj == null) return result;
+ if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
+ each(obj, function(value, index, list) {
+ if (result || (result = iterator.call(context, value, index, list))) return breaker;
+ });
+ return !!result;
+ };
+
+ // Determine if a given value is included in the array or object using `===`.
+ // Aliased as `contains`.
+ _.include = _.contains = function(obj, target) {
+ var found = false;
+ if (obj == null) return found;
+ if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
+ found = any(obj, function(value) {
+ return value === target;
+ });
+ return found;
+ };
+
+ // Invoke a method (with arguments) on every item in a collection.
+ _.invoke = function(obj, method) {
+ var args = slice.call(arguments, 2);
+ return _.map(obj, function(value) {
+ return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
+ });
+ };
+
+ // Convenience version of a common use case of `map`: fetching a property.
+ _.pluck = function(obj, key) {
+ return _.map(obj, function(value){ return value[key]; });
+ };
+
+ // Return the maximum element or (element-based computation).
+ _.max = function(obj, iterator, context) {
+ if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
+ if (!iterator && _.isEmpty(obj)) return -Infinity;
+ var result = {computed : -Infinity};
+ each(obj, function(value, index, list) {
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
+ computed >= result.computed && (result = {value : value, computed : computed});
+ });
+ return result.value;
+ };
+
+ // Return the minimum element (or element-based computation).
+ _.min = function(obj, iterator, context) {
+ if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
+ if (!iterator && _.isEmpty(obj)) return Infinity;
+ var result = {computed : Infinity};
+ each(obj, function(value, index, list) {
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
+ computed < result.computed && (result = {value : value, computed : computed});
+ });
+ return result.value;
+ };
+
+ // Shuffle an array.
+ _.shuffle = function(obj) {
+ var shuffled = [], rand;
+ each(obj, function(value, index, list) {
+ if (index == 0) {
+ shuffled[0] = value;
+ } else {
+ rand = Math.floor(Math.random() * (index + 1));
+ shuffled[index] = shuffled[rand];
+ shuffled[rand] = value;
+ }
+ });
+ return shuffled;
+ };
+
+ // Sort the object's values by a criterion produced by an iterator.
+ _.sortBy = function(obj, iterator, context) {
+ return _.pluck(_.map(obj, function(value, index, list) {
+ return {
+ value : value,
+ criteria : iterator.call(context, value, index, list)
+ };
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }), 'value');
+ };
+
+ // Groups the object's values by a criterion. Pass either a string attribute
+ // to group by, or a function that returns the criterion.
+ _.groupBy = function(obj, val) {
+ var result = {};
+ var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
+ each(obj, function(value, index) {
+ var key = iterator(value, index);
+ (result[key] || (result[key] = [])).push(value);
+ });
+ return result;
+ };
+
+ // Use a comparator function to figure out at what index an object should
+ // be inserted so as to maintain order. Uses binary search.
+ _.sortedIndex = function(array, obj, iterator) {
+ iterator || (iterator = _.identity);
+ var low = 0, high = array.length;
+ while (low < high) {
+ var mid = (low + high) >> 1;
+ iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
+ }
+ return low;
+ };
+
+ // Safely convert anything iterable into a real, live array.
+ _.toArray = function(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) return iterable.toArray();
+ if (_.isArray(iterable)) return slice.call(iterable);
+ if (_.isArguments(iterable)) return slice.call(iterable);
+ return _.values(iterable);
+ };
+
+ // Return the number of elements in an object.
+ _.size = function(obj) {
+ return _.toArray(obj).length;
+ };
+
+ // Array Functions
+ // ---------------
+
+ // Get the first element of an array. Passing **n** will return the first N
+ // values in the array. Aliased as `head`. The **guard** check allows it to work
+ // with `_.map`.
+ _.first = _.head = function(array, n, guard) {
+ return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
+ };
+
+ // Returns everything but the last entry of the array. Especcialy useful on
+ // the arguments object. Passing **n** will return all the values in
+ // the array, excluding the last N. The **guard** check allows it to work with
+ // `_.map`.
+ _.initial = function(array, n, guard) {
+ return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
+ };
+
+ // Get the last element of an array. Passing **n** will return the last N
+ // values in the array. The **guard** check allows it to work with `_.map`.
+ _.last = function(array, n, guard) {
+ if ((n != null) && !guard) {
+ return slice.call(array, Math.max(array.length - n, 0));
+ } else {
+ return array[array.length - 1];
+ }
+ };
+
+ // Returns everything but the first entry of the array. Aliased as `tail`.
+ // Especially useful on the arguments object. Passing an **index** will return
+ // the rest of the values in the array from that index onward. The **guard**
+ // check allows it to work with `_.map`.
+ _.rest = _.tail = function(array, index, guard) {
+ return slice.call(array, (index == null) || guard ? 1 : index);
+ };
+
+ // Trim out all falsy values from an array.
+ _.compact = function(array) {
+ return _.filter(array, function(value){ return !!value; });
+ };
+
+ // Return a completely flattened version of an array.
+ _.flatten = function(array, shallow) {
+ return _.reduce(array, function(memo, value) {
+ if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
+ memo[memo.length] = value;
+ return memo;
+ }, []);
+ };
+
+ // Return a version of the array that does not contain the specified value(s).
+ _.without = function(array) {
+ return _.difference(array, slice.call(arguments, 1));
+ };
+
+ // Produce a duplicate-free version of the array. If the array has already
+ // been sorted, you have the option of using a faster algorithm.
+ // Aliased as `unique`.
+ _.uniq = _.unique = function(array, isSorted, iterator) {
+ var initial = iterator ? _.map(array, iterator) : array;
+ var result = [];
+ _.reduce(initial, function(memo, el, i) {
+ if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
+ memo[memo.length] = el;
+ result[result.length] = array[i];
+ }
+ return memo;
+ }, []);
+ return result;
+ };
+
+ // Produce an array that contains the union: each distinct element from all of
+ // the passed-in arrays.
+ _.union = function() {
+ return _.uniq(_.flatten(arguments, true));
+ };
+
+ // Produce an array that contains every item shared between all the
+ // passed-in arrays. (Aliased as "intersect" for back-compat.)
+ _.intersection = _.intersect = function(array) {
+ var rest = slice.call(arguments, 1);
+ return _.filter(_.uniq(array), function(item) {
+ return _.every(rest, function(other) {
+ return _.indexOf(other, item) >= 0;
+ });
+ });
+ };
+
+ // Take the difference between one array and a number of other arrays.
+ // Only the elements present in just the first array will remain.
+ _.difference = function(array) {
+ var rest = _.flatten(slice.call(arguments, 1));
+ return _.filter(array, function(value){ return !_.include(rest, value); });
+ };
+
+ // Zip together multiple lists into a single array -- elements that share
+ // an index go together.
+ _.zip = function() {
+ var args = slice.call(arguments);
+ var length = _.max(_.pluck(args, 'length'));
+ var results = new Array(length);
+ for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
+ return results;
+ };
+
+ // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
+ // we need this function. Return the position of the first occurrence of an
+ // item in an array, or -1 if the item is not included in the array.
+ // Delegates to **ECMAScript 5**'s native `indexOf` if available.
+ // If the array is large and already in sort order, pass `true`
+ // for **isSorted** to use binary search.
+ _.indexOf = function(array, item, isSorted) {
+ if (array == null) return -1;
+ var i, l;
+ if (isSorted) {
+ i = _.sortedIndex(array, item);
+ return array[i] === item ? i : -1;
+ }
+ if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
+ for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
+ return -1;
+ };
+
+ // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
+ _.lastIndexOf = function(array, item) {
+ if (array == null) return -1;
+ if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
+ var i = array.length;
+ while (i--) if (i in array && array[i] === item) return i;
+ return -1;
+ };
+
+ // Generate an integer Array containing an arithmetic progression. A port of
+ // the native Python `range()` function. See
+ // [the Python documentation](http://docs.python.org/library/functions.html#range).
+ _.range = function(start, stop, step) {
+ if (arguments.length <= 1) {
+ stop = start || 0;
+ start = 0;
+ }
+ step = arguments[2] || 1;
+
+ var len = Math.max(Math.ceil((stop - start) / step), 0);
+ var idx = 0;
+ var range = new Array(len);
+
+ while(idx < len) {
+ range[idx++] = start;
+ start += step;
+ }
+
+ return range;
+ };
+
+ // Function (ahem) Functions
+ // ------------------
+
+ // Reusable constructor function for prototype setting.
+ var ctor = function(){};
+
+ // Create a function bound to a given object (assigning `this`, and arguments,
+ // optionally). Binding with arguments is also known as `curry`.
+ // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
+ // We check for `func.bind` first, to fail fast when `func` is undefined.
+ _.bind = function bind(func, context) {
+ var bound, args;
+ if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
+ if (!_.isFunction(func)) throw new TypeError;
+ args = slice.call(arguments, 2);
+ return bound = function() {
+ if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
+ ctor.prototype = func.prototype;
+ var self = new ctor;
+ var result = func.apply(self, args.concat(slice.call(arguments)));
+ if (Object(result) === result) return result;
+ return self;
+ };
+ };
+
+ // Bind all of an object's methods to that object. Useful for ensuring that
+ // all callbacks defined on an object belong to it.
+ _.bindAll = function(obj) {
+ var funcs = slice.call(arguments, 1);
+ if (funcs.length == 0) funcs = _.functions(obj);
+ each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
+ return obj;
+ };
+
+ // Memoize an expensive function by storing its results.
+ _.memoize = function(func, hasher) {
+ var memo = {};
+ hasher || (hasher = _.identity);
+ return function() {
+ var key = hasher.apply(this, arguments);
+ return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
+ };
+ };
+
+ // Delays a function for the given number of milliseconds, and then calls
+ // it with the arguments supplied.
+ _.delay = function(func, wait) {
+ var args = slice.call(arguments, 2);
+ return setTimeout(function(){ return func.apply(func, args); }, wait);
+ };
+
+ // Defers a function, scheduling it to run after the current call stack has
+ // cleared.
+ _.defer = function(func) {
+ return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
+ };
+
+ // Returns a function, that, when invoked, will only be triggered at most once
+ // during a given window of time.
+ _.throttle = function(func, wait) {
+ var context, args, timeout, throttling, more;
+ var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
+ return function() {
+ context = this; args = arguments;
+ var later = function() {
+ timeout = null;
+ if (more) func.apply(context, args);
+ whenDone();
+ };
+ if (!timeout) timeout = setTimeout(later, wait);
+ if (throttling) {
+ more = true;
+ } else {
+ func.apply(context, args);
+ }
+ whenDone();
+ throttling = true;
+ };
+ };
+
+ // Returns a function, that, as long as it continues to be invoked, will not
+ // be triggered. The function will be called after it stops being called for
+ // N milliseconds.
+ _.debounce = function(func, wait) {
+ var timeout;
+ return function() {
+ var context = this, args = arguments;
+ var later = function() {
+ timeout = null;
+ func.apply(context, args);
+ };
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ };
+ };
+
+ // Returns a function that will be executed at most one time, no matter how
+ // often you call it. Useful for lazy initialization.
+ _.once = function(func) {
+ var ran = false, memo;
+ return function() {
+ if (ran) return memo;
+ ran = true;
+ return memo = func.apply(this, arguments);
+ };
+ };
+
+ // Returns the first function passed as an argument to the second,
+ // allowing you to adjust arguments, run code before and after, and
+ // conditionally execute the original function.
+ _.wrap = function(func, wrapper) {
+ return function() {
+ var args = [func].concat(slice.call(arguments, 0));
+ return wrapper.apply(this, args);
+ };
+ };
+
+ // Returns a function that is the composition of a list of functions, each
+ // consuming the return value of the function that follows.
+ _.compose = function() {
+ var funcs = arguments;
+ return function() {
+ var args = arguments;
+ for (var i = funcs.length - 1; i >= 0; i--) {
+ args = [funcs[i].apply(this, args)];
+ }
+ return args[0];
+ };
+ };
+
+ // Returns a function that will only be executed after being called N times.
+ _.after = function(times, func) {
+ if (times <= 0) return func();
+ return function() {
+ if (--times < 1) { return func.apply(this, arguments); }
+ };
+ };
+
+ // Object Functions
+ // ----------------
+
+ // Retrieve the names of an object's properties.
+ // Delegates to **ECMAScript 5**'s native `Object.keys`
+ _.keys = nativeKeys || function(obj) {
+ if (obj !== Object(obj)) throw new TypeError('Invalid object');
+ var keys = [];
+ for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
+ return keys;
+ };
+
+ // Retrieve the values of an object's properties.
+ _.values = function(obj) {
+ return _.map(obj, _.identity);
+ };
+
+ // Return a sorted list of the function names available on the object.
+ // Aliased as `methods`
+ _.functions = _.methods = function(obj) {
+ var names = [];
+ for (var key in obj) {
+ if (_.isFunction(obj[key])) names.push(key);
+ }
+ return names.sort();
+ };
+
+ // Extend a given object with all the properties in passed-in object(s).
+ _.extend = function(obj) {
+ each(slice.call(arguments, 1), function(source) {
+ for (var prop in source) {
+ obj[prop] = source[prop];
+ }
+ });
+ return obj;
+ };
+
+ // Fill in a given object with default properties.
+ _.defaults = function(obj) {
+ each(slice.call(arguments, 1), function(source) {
+ for (var prop in source) {
+ if (obj[prop] == null) obj[prop] = source[prop];
+ }
+ });
+ return obj;
+ };
+
+ // Create a (shallow-cloned) duplicate of an object.
+ _.clone = function(obj) {
+ if (!_.isObject(obj)) return obj;
+ return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
+ };
+
+ // Invokes interceptor with the obj, and then returns obj.
+ // The primary purpose of this method is to "tap into" a method chain, in
+ // order to perform operations on intermediate results within the chain.
+ _.tap = function(obj, interceptor) {
+ interceptor(obj);
+ return obj;
+ };
+
+ // Internal recursive comparison function.
+ function eq(a, b, stack) {
+ // Identical objects are equal. `0 === -0`, but they aren't identical.
+ // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
+ if (a === b) return a !== 0 || 1 / a == 1 / b;
+ // A strict comparison is necessary because `null == undefined`.
+ if (a == null || b == null) return a === b;
+ // Unwrap any wrapped objects.
+ if (a._chain) a = a._wrapped;
+ if (b._chain) b = b._wrapped;
+ // Invoke a custom `isEqual` method if one is provided.
+ if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
+ if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
+ // Compare `[[Class]]` names.
+ var className = toString.call(a);
+ if (className != toString.call(b)) return false;
+ switch (className) {
+ // Strings, numbers, dates, and booleans are compared by value.
+ case '[object String]':
+ // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
+ // equivalent to `new String("5")`.
+ return a == String(b);
+ case '[object Number]':
+ // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
+ // other numeric values.
+ return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
+ case '[object Date]':
+ case '[object Boolean]':
+ // Coerce dates and booleans to numeric primitive values. Dates are compared by their
+ // millisecond representations. Note that invalid dates with millisecond representations
+ // of `NaN` are not equivalent.
+ return +a == +b;
+ // RegExps are compared by their source patterns and flags.
+ case '[object RegExp]':
+ return a.source == b.source &&
+ a.global == b.global &&
+ a.multiline == b.multiline &&
+ a.ignoreCase == b.ignoreCase;
+ }
+ if (typeof a != 'object' || typeof b != 'object') return false;
+ // Assume equality for cyclic structures. The algorithm for detecting cyclic
+ // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
+ var length = stack.length;
+ while (length--) {
+ // Linear search. Performance is inversely proportional to the number of
+ // unique nested structures.
+ if (stack[length] == a) return true;
+ }
+ // Add the first object to the stack of traversed objects.
+ stack.push(a);
+ var size = 0, result = true;
+ // Recursively compare objects and arrays.
+ if (className == '[object Array]') {
+ // Compare array lengths to determine if a deep comparison is necessary.
+ size = a.length;
+ result = size == b.length;
+ if (result) {
+ // Deep compare the contents, ignoring non-numeric properties.
+ while (size--) {
+ // Ensure commutative equality for sparse arrays.
+ if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
+ }
+ }
+ } else {
+ // Objects with different constructors are not equivalent.
+ if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
+ // Deep compare objects.
+ for (var key in a) {
+ if (_.has(a, key)) {
+ // Count the expected number of properties.
+ size++;
+ // Deep compare each member.
+ if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
+ }
+ }
+ // Ensure that both objects contain the same number of properties.
+ if (result) {
+ for (key in b) {
+ if (_.has(b, key) && !(size--)) break;
+ }
+ result = !size;
+ }
+ }
+ // Remove the first object from the stack of traversed objects.
+ stack.pop();
+ return result;
+ }
+
+ // Perform a deep comparison to check if two objects are equal.
+ _.isEqual = function(a, b) {
+ return eq(a, b, []);
+ };
+
+ // Is a given array, string, or object empty?
+ // An "empty" object has no enumerable own-properties.
+ _.isEmpty = function(obj) {
+ if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
+ for (var key in obj) if (_.has(obj, key)) return false;
+ return true;
+ };
+
+ // Is a given value a DOM element?
+ _.isElement = function(obj) {
+ return !!(obj && obj.nodeType == 1);
+ };
+
+ // Is a given value an array?
+ // Delegates to ECMA5's native Array.isArray
+ _.isArray = nativeIsArray || function(obj) {
+ return toString.call(obj) == '[object Array]';
+ };
+
+ // Is a given variable an object?
+ _.isObject = function(obj) {
+ return obj === Object(obj);
+ };
+
+ // Is a given variable an arguments object?
+ _.isArguments = function(obj) {
+ return toString.call(obj) == '[object Arguments]';
+ };
+ if (!_.isArguments(arguments)) {
+ _.isArguments = function(obj) {
+ return !!(obj && _.has(obj, 'callee'));
+ };
+ }
+
+ // Is a given value a function?
+ _.isFunction = function(obj) {
+ return toString.call(obj) == '[object Function]';
+ };
+
+ // Is a given value a string?
+ _.isString = function(obj) {
+ return toString.call(obj) == '[object String]';
+ };
+
+ // Is a given value a number?
+ _.isNumber = function(obj) {
+ return toString.call(obj) == '[object Number]';
+ };
+
+ // Is the given value `NaN`?
+ _.isNaN = function(obj) {
+ // `NaN` is the only value for which `===` is not reflexive.
+ return obj !== obj;
+ };
+
+ // Is a given value a boolean?
+ _.isBoolean = function(obj) {
+ return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
+ };
+
+ // Is a given value a date?
+ _.isDate = function(obj) {
+ return toString.call(obj) == '[object Date]';
+ };
+
+ // Is the given value a regular expression?
+ _.isRegExp = function(obj) {
+ return toString.call(obj) == '[object RegExp]';
+ };
+
+ // Is a given value equal to null?
+ _.isNull = function(obj) {
+ return obj === null;
+ };
+
+ // Is a given variable undefined?
+ _.isUndefined = function(obj) {
+ return obj === void 0;
+ };
+
+ // Has own property?
+ _.has = function(obj, key) {
+ return hasOwnProperty.call(obj, key);
+ };
+
+ // Utility Functions
+ // -----------------
+
+ // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
+ // previous owner. Returns a reference to the Underscore object.
+ _.noConflict = function() {
+ root._ = previousUnderscore;
+ return this;
+ };
+
+ // Keep the identity function around for default iterators.
+ _.identity = function(value) {
+ return value;
+ };
+
+ // Run a function **n** times.
+ _.times = function (n, iterator, context) {
+ for (var i = 0; i < n; i++) iterator.call(context, i);
+ };
+
+ // Escape a string for HTML interpolation.
+ _.escape = function(string) {
+ return (''+string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
+ };
+
+ // Add your own custom functions to the Underscore object, ensuring that
+ // they're correctly added to the OOP wrapper as well.
+ _.mixin = function(obj) {
+ each(_.functions(obj), function(name){
+ addToWrapper(name, _[name] = obj[name]);
+ });
+ };
+
+ // Generate a unique integer id (unique within the entire client session).
+ // Useful for temporary DOM ids.
+ var idCounter = 0;
+ _.uniqueId = function(prefix) {
+ var id = idCounter++;
+ return prefix ? prefix + id : id;
+ };
+
+ // By default, Underscore uses ERB-style template delimiters, change the
+ // following template settings to use alternative delimiters.
+ _.templateSettings = {
+ evaluate : /<%([\s\S]+?)%>/g,
+ interpolate : /<%=([\s\S]+?)%>/g,
+ escape : /<%-([\s\S]+?)%>/g
+ };
+
+ // When customizing `templateSettings`, if you don't want to define an
+ // interpolation, evaluation or escaping regex, we need one that is
+ // guaranteed not to match.
+ var noMatch = /.^/;
+
+ // Within an interpolation, evaluation, or escaping, remove HTML escaping
+ // that had been previously added.
+ var unescape = function(code) {
+ return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
+ };
+
+ // JavaScript micro-templating, similar to John Resig's implementation.
+ // Underscore templating handles arbitrary delimiters, preserves whitespace,
+ // and correctly escapes quotes within interpolated code.
+ _.template = function(str, data) {
+ var c = _.templateSettings;
+ var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
+ 'with(obj||{}){__p.push(\'' +
+ str.replace(/\\/g, '\\\\')
+ .replace(/'/g, "\\'")
+ .replace(c.escape || noMatch, function(match, code) {
+ return "',_.escape(" + unescape(code) + "),'";
+ })
+ .replace(c.interpolate || noMatch, function(match, code) {
+ return "'," + unescape(code) + ",'";
+ })
+ .replace(c.evaluate || noMatch, function(match, code) {
+ return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
+ })
+ .replace(/\r/g, '\\r')
+ .replace(/\n/g, '\\n')
+ .replace(/\t/g, '\\t')
+ + "');}return __p.join('');";
+ var func = new Function('obj', '_', tmpl);
+ if (data) return func(data, _);
+ return function(data) {
+ return func.call(this, data, _);
+ };
+ };
+
+ // Add a "chain" function, which will delegate to the wrapper.
+ _.chain = function(obj) {
+ return _(obj).chain();
+ };
+
+ // The OOP Wrapper
+ // ---------------
+
+ // If Underscore is called as a function, it returns a wrapped object that
+ // can be used OO-style. This wrapper holds altered versions of all the
+ // underscore functions. Wrapped objects may be chained.
+ var wrapper = function(obj) { this._wrapped = obj; };
+
+ // Expose `wrapper.prototype` as `_.prototype`
+ _.prototype = wrapper.prototype;
+
+ // Helper function to continue chaining intermediate results.
+ var result = function(obj, chain) {
+ return chain ? _(obj).chain() : obj;
+ };
+
+ // A method to easily add functions to the OOP wrapper.
+ var addToWrapper = function(name, func) {
+ wrapper.prototype[name] = function() {
+ var args = slice.call(arguments);
+ unshift.call(args, this._wrapped);
+ return result(func.apply(_, args), this._chain);
+ };
+ };
+
+ // Add all of the Underscore functions to the wrapper object.
+ _.mixin(_);
+
+ // Add all mutator Array functions to the wrapper.
+ each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+ var method = ArrayProto[name];
+ wrapper.prototype[name] = function() {
+ var wrapped = this._wrapped;
+ method.apply(wrapped, arguments);
+ var length = wrapped.length;
+ if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
+ return result(wrapped, this._chain);
+ };
+ });
+
+ // Add all accessor Array functions to the wrapper.
+ each(['concat', 'join', 'slice'], function(name) {
+ var method = ArrayProto[name];
+ wrapper.prototype[name] = function() {
+ return result(method.apply(this._wrapped, arguments), this._chain);
+ };
+ });
+
+ // Start chaining a wrapped Underscore object.
+ wrapper.prototype.chain = function() {
+ this._chain = true;
+ return this;
+ };
+
+ // Extracts the result from a wrapped and chained object.
+ wrapper.prototype.value = function() {
+ return this._wrapped;
+ };
+
+}).call(this);
diff --git a/storage/mroonga/vendor/groonga/packages/debian/patches/series b/storage/mroonga/vendor/groonga/packages/debian/patches/series
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/patches/series
diff --git a/storage/mroonga/vendor/groonga/packages/debian/rules b/storage/mroonga/vendor/groonga/packages/debian/rules
new file mode 100755
index 00000000000..d1be8f8fceb
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/rules
@@ -0,0 +1,31 @@
+#!/usr/bin/make -f
+# -*- makefile-gmake -*-
+#
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+# This has to be exported to make some magic below work.
+export DH_OPTIONS
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all
+export DEB_BUILD_HARDENING = 1
+DPKG_EXPORT_BUILDFLAGS = 1
+include /usr/share/dpkg/buildflags.mk
+DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
+
+%:
+ dh $@
+
+override_dh_auto_configure:
+ dh_auto_configure -- --with-munin-plugins
+
+# disable 'make check'.
+override_dh_auto_test:
+
+override_dh_install:
+ find $(CURDIR)/debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/ -name *.la -delete
+ find $(CURDIR)/debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/ -name underscore.js -delete
+ install -d debian/tmp/etc/munin/plugin-conf.d/
+ install -m 0644 debian/groonga-munin-plugins.conf \
+ debian/tmp/etc/munin/plugin-conf.d/groonga
+ mv debian/tmp/usr/share/doc/groonga/ \
+ debian/tmp/usr/share/doc/groonga-doc/
+ dh_install
diff --git a/storage/mroonga/vendor/groonga/packages/debian/source/format b/storage/mroonga/vendor/groonga/packages/debian/source/format
new file mode 100644
index 00000000000..163aaf8d82b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/storage/mroonga/vendor/groonga/packages/debian/watch b/storage/mroonga/vendor/groonga/packages/debian/watch
new file mode 100644
index 00000000000..1ef60d391a4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/debian/watch
@@ -0,0 +1,3 @@
+version=3
+
+http://packages.groonga.org/source/groonga/groonga-(.*).tar.gz debian uupdate
diff --git a/storage/mroonga/vendor/groonga/packages/release-key-secret.asc.gpg.hayashi b/storage/mroonga/vendor/groonga/packages/release-key-secret.asc.gpg.hayashi
new file mode 100644
index 00000000000..8fe39a41949
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/release-key-secret.asc.gpg.hayashi
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/packages/release-key-secret.asc.gpg.kou b/storage/mroonga/vendor/groonga/packages/release-key-secret.asc.gpg.kou
new file mode 100644
index 00000000000..3d2e6421530
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/release-key-secret.asc.gpg.kou
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/packages/release-key-secret.asc.gpg.yoshihara b/storage/mroonga/vendor/groonga/packages/release-key-secret.asc.gpg.yoshihara
new file mode 100644
index 00000000000..c99adecea09
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/release-key-secret.asc.gpg.yoshihara
Binary files differ
diff --git a/storage/mroonga/vendor/groonga/packages/rpm/Makefile.am b/storage/mroonga/vendor/groonga/packages/rpm/Makefile.am
new file mode 100644
index 00000000000..06031c93f6f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/rpm/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = centos fedora
diff --git a/storage/mroonga/vendor/groonga/packages/rpm/centos/Makefile.am b/storage/mroonga/vendor/groonga/packages/rpm/centos/Makefile.am
new file mode 100644
index 00000000000..321bd0c379d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/rpm/centos/Makefile.am
@@ -0,0 +1,2 @@
+EXTRA_DIST = groonga.spec.in
+noinst_DATA = groonga.spec
diff --git a/storage/mroonga/vendor/groonga/packages/rpm/centos/groonga.spec.in b/storage/mroonga/vendor/groonga/packages/rpm/centos/groonga.spec.in
new file mode 100644
index 00000000000..8d0eacde93d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/rpm/centos/groonga.spec.in
@@ -0,0 +1,573 @@
+%{!?use_lzo:%define use_lzo 0}
+%{!?use_mecab:%define use_mecab 1}
+%{?additional_configure_options:%define use_additional_configure_options 1}
+%{!?additional_configure_options:%define use_additional_configure_options 0}
+
+%global _initddir %{_sysconfdir}/init.d/
+
+Name: groonga
+Version: @VERSION@
+Release: 1%{?dist}
+Summary: An Embeddable Fulltext Search Engine
+
+Group: Applications/Text
+License: LGPLv2
+URL: http://groonga.org/
+Source0: http://packages.groonga.org/source/groonga/groonga-%{version}.tar.gz
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n)
+%if %{use_mecab}
+BuildRequires: mecab-devel
+%endif
+BuildRequires: zlib-devel
+%if %{use_lzo}
+BuildRequires: lzo-devel
+%endif
+BuildRequires: pcre-devel
+Requires: %{name}-libs = %{version}-%{release}
+Requires: %{name}-plugin-suggest = %{version}-%{release}
+#BuildRequires: messagepack-devel
+#BuildRequires: zeromq-devel
+#BuildRequires: libevent-devel
+Obsoletes: %{name} < 1.2.2-0
+
+%description
+Groonga is an embeddable full-text search engine library. It can
+integrate with DBMS and scripting languages to enhance their search
+functionality. It also provides a standalone data store server based
+on relational data model.
+
+%package libs
+Summary: Runtime libraries for Groonga
+Group: System Environment/Libraries
+License: LGPLv2 and (MIT or GPLv2)
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description libs
+This package contains the libraries for Groonga
+
+%package server-common
+Summary: Common packages for the Groonga server and the Groonga HTTP server
+Group: Applications/Text
+License: LGPLv2
+Requires: %{name} = %{version}-%{release}
+Requires(pre): shadow-utils
+
+%description server-common
+This package provides common settings for server use
+
+%package server-gqtp
+Summary: Groonga GQTP server
+Group: Applications/Text
+License: LGPLv2
+Requires: %{name}-server-common = %{version}-%{release}
+Requires(pre): shadow-utils
+Requires(post): /sbin/chkconfig
+Requires(preun): /sbin/chkconfig
+Requires(preun): /sbin/service
+Requires(postun): /sbin/service
+Obsoletes: %{name} < 1.2.2-0
+Obsoletes: %{name}-server < 2.0.7-0
+
+%description server-gqtp
+This package contains the Groonga GQTP server
+
+%package server-http
+Summary: Groonga HTTP server (stable)
+Group: Applications/Text
+License: LGPLv2
+Requires: %{name}-server-common = %{version}-%{release}
+Requires: curl
+Requires(pre): shadow-utils
+Requires(post): /sbin/chkconfig
+Requires(preun): /sbin/chkconfig
+Requires(preun): /sbin/service
+Requires(postun): /sbin/service
+Obsoletes: %{name} < 1.2.2-0
+Obsoletes: %{name}-server < 2.0.7-0
+Conflicts: %{name}-httpd
+
+%description server-http
+This package contains the Groonga HTTP server. It is stable but
+has only requisite minimum features.
+
+%package httpd
+Summary: Groonga HTTP server (experimental)
+Group: Applications/Text
+License: LGPLv2 and BSD
+Requires: %{name}-server-common = %{version}-%{release}
+Conflicts: %{name}-server-http
+
+%description httpd
+This package contains the Groonga HTTP server. It is experimental
+but has many features. Because it is based on nginx HTTP server.
+It will obsolete groonga-server-http when it is stable.
+
+%package doc
+Summary: Documentation for Groonga
+Group: Documentation
+License: LGPLv2 and BSD
+
+%description doc
+Documentation for Groonga
+
+%package devel
+Summary: Libraries and header files for Groonga
+Group: Development/Libraries
+Requires: %{name}-libs = %{version}-%{release}
+
+%description devel
+Libraries and header files for Groonga
+
+%if %{use_mecab}
+%package tokenizer-mecab
+Summary: MeCab tokenizer for Groonga
+Group: Applications/Text
+Requires: %{name}-libs = %{version}-%{release}
+Requires: mecab-dic
+
+%description tokenizer-mecab
+MeCab tokenizer for Groonga
+%endif
+
+%package plugin-suggest
+Summary: Suggest plugin for Groonga
+Group: Applications/Text
+Requires: %{name}-libs = %{version}-%{release}
+
+%description plugin-suggest
+Sugget plugin for Groonga
+
+%package munin-plugins
+Summary: Munin plugins for Groonga
+Group: Applications/System
+Requires: %{name}-libs = %{version}-%{release}
+Requires: munin-node
+Requires(post): munin-node
+Requires(post): /sbin/service
+Requires(postun): /sbin/service
+
+%description munin-plugins
+Munin plugins for Groonga
+
+%prep
+#% define optflags -O0
+%setup -q
+
+
+%build
+%configure \
+ --disable-static \
+ --with-package-platform=redhat \
+ --with-zlib \
+%if %{use_lzo}
+ --with-lzo \
+%endif
+%if %{use_mecab}
+ --with-mecab \
+%else
+ --without-mecab \
+%endif
+ --with-munin-plugins \
+%if %{use_additional_configure_options}
+ %{additional_configure_options}
+%endif
+
+sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool
+sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool
+make %{?_smp_mflags}
+
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT INSTALL="install -p"
+rm $RPM_BUILD_ROOT%{_libdir}/groonga/plugins/*/*.la
+rm $RPM_BUILD_ROOT%{_libdir}/*.la
+
+mv $RPM_BUILD_ROOT%{_datadir}/doc/groonga groonga-doc
+
+mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/run/groonga
+mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/groonga/db
+mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/log/groonga
+
+mv $RPM_BUILD_ROOT%{_datadir}/groonga/munin/ $RPM_BUILD_ROOT%{_datadir}/
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/munin/plugin-conf.d/
+cat <<EOC > $RPM_BUILD_ROOT%{_sysconfdir}/munin/plugin-conf.d/groonga
+[groonga_*]
+ user groonga
+ group groonga
+ env.PATH ${_bindir}
+ env.database_path %{_localstatedir}/lib/groonga/db/db
+ env.host 127.0.0.1
+
+ env.http_host 127.0.0.1
+ env.http_port 10041
+ env.http_database_path %{_localstatedir}/lib/groonga/db/db
+ env.http_pid_path %{_localstatedir}/run/groonga/groonga-http.pid
+ env.http_query_log_path %{_localstatedir}/log/groonga/query-http.log
+
+ env.httpd_host 127.0.0.1
+ env.httpd_port 10041
+ env.httpd_database_path %{_localstatedir}/lib/groonga/db/db
+ env.httpd_pid_path %{_localstatedir}/run/groonga/groonga-httpd.pid
+ env.httpd_query_log_path %{_localstatedir}/log/groonga/httpd/groonga-query.log
+
+ env.gqtp_host 127.0.0.1
+ env.gqtp_port 10043
+ env.gqtp_database_path %{_localstatedir}/lib/groonga/db/db
+ env.gqtp_pid_path %{_localstatedir}/run/groonga/groonga-gqtp.pid
+ env.gqtp_query_log_path %{_localstatedir}/log/groonga/query-gqtp.log
+EOC
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%pre server-common
+getent group groonga >/dev/null || groupadd -r groonga
+getent passwd groonga >/dev/null || \
+ useradd -r -g groonga -d %{_localstatedir}/lib/groonga -s /sbin/nologin \
+ -c 'groonga' groonga
+if [ $1 = 1 ]; then
+ mkdir -p %{_localstatedir}/lib/groonga/db
+ groonga -n %{_localstatedir}/lib/groonga/db/db shutdown > /dev/null
+ chown -R groonga:groonga %{_localstatedir}/lib/groonga
+ mkdir -p %{_localstatedir}/run/groonga
+ chown -R groonga:groonga %{_localstatedir}/run/groonga
+fi
+exit 0
+
+%post server-gqtp
+/sbin/chkconfig --add groonga-server-gqtp
+
+%post server-http
+/sbin/chkconfig --add groonga-server-http
+
+%post httpd
+if [ $1 = 1 ] ; then
+ mkdir -p %{_localstatedir}/log/groonga/httpd
+ chown -R groonga:groonga %{_localstatedir}/log/groonga/httpd
+elif [ $1 = 2 ] ; then
+ /sbin/service groonga-httpd restart >/dev/null 2>&1 || :
+fi
+
+%post libs -p /sbin/ldconfig
+
+%post munin-plugins
+%{_sbindir}/munin-node-configure --shell --remove-also | grep -e 'groonga_' | sh
+[ -f %{_localstatedir}/lock/subsys/munin-node ] && \
+ /sbin/service munin-node restart > /dev/null 2>&1
+:
+
+%preun server-http
+if [ $1 = 0 ] ; then
+ /sbin/service groonga-server-http stop >/dev/null 2>&1 || :
+ /sbin/chkconfig --del groonga-server-http
+fi
+
+%postun server-http
+if [ $1 -ge 1 ] ; then
+ /sbin/service groonga-server-http condrestart >/dev/null 2>&1 || :
+fi
+
+%preun server-gqtp
+if [ $1 = 0 ] ; then
+ /sbin/service groonga-server-http stop >/dev/null 2>&1 || :
+ /sbin/chkconfig --del groonga-server-gqtp
+fi
+
+%postun server-gqtp
+if [ $1 -ge 1 ] ; then
+ /sbin/service groonga-server-gqtp condrestart >/dev/null 2>&1 || :
+fi
+
+%postun libs -p /sbin/ldconfig
+
+%postun munin-plugins
+if [ $1 -eq 0 ]; then
+ [ -f %{_localstatedir}/lock/subsys/munin-node ] && \
+ /sbin/service munin-node restart >/dev/null 2>&1
+ :
+fi
+
+
+%files
+%defattr(-,root,root,-)
+%{_datadir}/man/man1/*
+%{_datadir}/man/*/man1/*
+%{_bindir}/groonga
+%{_bindir}/groonga-benchmark
+
+%files libs
+%defattr(-,root,root,-)
+%doc README.md COPYING
+%{_libdir}/*.so.*
+%dir %{_libdir}/groonga
+%dir %{_libdir}/groonga/plugins
+%dir %{_libdir}/groonga/plugins/tokenizers
+%{_libdir}/groonga/plugins/table/table.so
+%{_libdir}/groonga/plugins/query_expanders/tsv.so
+%{_datadir}/groonga/
+%config(noreplace) %{_sysconfdir}/groonga/synonyms.tsv
+
+%files server-common
+
+%files server-gqtp
+%defattr(-,root,root,-)
+%config(noreplace) %{_sysconfdir}/groonga/
+%config(noreplace) %{_sysconfdir}/sysconfig/groonga-server-gqtp
+%config(noreplace) %{_sysconfdir}/logrotate.d/groonga-server-gqtp
+%{_initddir}/groonga-server-gqtp
+%ghost %dir %{_localstatedir}/run/%{name}
+%attr(0750,groonga,groonga) %dir %{_localstatedir}/lib/%{name}
+%attr(0750,groonga,groonga) %dir %{_localstatedir}/lib/%{name}/db
+
+%files server-http
+%defattr(-,root,root,-)
+%config(noreplace) %{_sysconfdir}/groonga/
+%config(noreplace) %{_sysconfdir}/sysconfig/groonga-server-http
+%config(noreplace) %{_sysconfdir}/logrotate.d/groonga-server-http
+%{_initddir}/groonga-server-http
+%ghost %dir %{_localstatedir}/run/%{name}
+%attr(0750,groonga,groonga) %dir %{_localstatedir}/lib/%{name}
+%attr(0750,groonga,groonga) %dir %{_localstatedir}/lib/%{name}/db
+
+%files httpd
+%defattr(-,root,root,-)
+%config(noreplace) %{_sysconfdir}/groonga/httpd/*
+%config(noreplace) %{_sysconfdir}/sysconfig/groonga-httpd
+%config(noreplace) %{_sysconfdir}/logrotate.d/groonga-httpd
+%{_initddir}/groonga-httpd
+%{_sbindir}/groonga-httpd
+%{_sbindir}/groonga-httpd-restart
+
+%files doc
+%defattr(-,root,root,-)
+%doc README.md COPYING
+%doc groonga-doc/*
+
+%files devel
+%defattr(-,root,root,-)
+%{_includedir}/groonga/
+%{_libdir}/*.so
+%{_libdir}/pkgconfig/groonga*.pc
+
+%files plugin-suggest
+%defattr(-,root,root,-)
+%{_bindir}/groonga-suggest-*
+%dir %{_libdir}/groonga/plugins
+%{_libdir}/groonga/plugins/suggest/suggest.so
+
+%if %{use_mecab}
+%files tokenizer-mecab
+%defattr(-,root,root,-)
+%{_libdir}/groonga/plugins/tokenizers/mecab.so
+%endif
+
+%files munin-plugins
+%defattr(-,root,root,-)
+%{_datadir}/munin/plugins/*
+%config(noreplace) %{_sysconfdir}/munin/plugin-conf.d/*
+
+%changelog
+* Fri Aug 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.0.5-1
+- new upstream release.
+
+* Tue Jul 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.0.4-1
+- new upstream release.
+
+* Sun Jun 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.0.3-1
+- new upstream release.
+
+* Thu May 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.0.2-0
+- new upstream release.
+
+* Sat Mar 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.0.1-1
+- new upstream release.
+
+* Sun Feb 09 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.0.0-1
+- new upstream release.
+
+* Wed Jan 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 3.1.2-1
+- new upstream release.
+
+* Sun Dec 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.1.1-1
+- new upstream release.
+
+* Fri Nov 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.1.0-1
+- new upstream release.
+
+* Tue Oct 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.9-1
+- new upstream release.
+
+* Sun Sep 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.8-1
+- new upstream release.
+
+* Thu Aug 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.7-1
+- new upstream release.
+
+* Mon Jul 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.6-1
+- new upstream release.
+
+* Sat Jun 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.5-0
+- new upstream release.
+
+* Wed May 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.4-0
+- new upstream release.
+
+* Mon Apr 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.3-0
+- Add additional_configure_options parameter.
+- Make MeCab optional. Use use_mecab parameter for it.
+
+* Fri Mar 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.2-0
+- new upstream release.
+
+* Thu Feb 28 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.1-0
+- new upstream release.
+
+* Sat Feb 09 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.0-0
+- new upstream release.
+
+* Tue Jan 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 2.1.2-0
+- new upstream release.
+
+* Sat Dec 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.1.1-0
+- new upstream release.
+
+* Sat Dec 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.1.0-0
+- new upstream release.
+
+* Thu Nov 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.0.9-0
+- new upstream release.
+
+* Mon Oct 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.8-0
+- new upstream release.
+- Remove needless "Requires". They will be added by rpmbuild automatically.
+ Reported by by Daiki Ueno. Thanks!!!
+- Fix license of server-gqtp.
+- Fix license of server-http.
+- Add more description to server-http and httpd.
+
+* Sat Sep 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.0.7-0
+- new upstream release.
+- Split groonga-server package into groonga-server-gqtp and
+ groonga-server-http package.
+
+* Wed Aug 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.0.6-0
+- new upstream release.
+- Split common tasks for server use into groonga-server-common package.
+- groonga-server and groonga-httpd require groonga-server-common package.
+
+* Sun Jul 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.5-0
+- new upstream release.
+- split groonga-httpd related files into groonga-httpd package.
+
+* Fri Jun 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.4-0
+- new upstream release.
+- groonga package does not require groonga-tokenizer-mecab package.
+
+* Tue May 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.3-0
+- new upstream release.
+
+* Sun Apr 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.2-0
+- new upstream release.
+
+* Fri Mar 30 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.1-2
+- Use shutdown command for stop.
+
+* Fri Mar 30 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.1-1
+- Fix bind address argument parameter.
+ Patch by Masaharu IWAI. Thanks!!!
+
+* Thu Mar 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.1-0
+- new upstream release.
+- grntest -> groonga-benchmark.
+- remove groong-tools package.
+
+* Wed Feb 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.0-0
+- new upstream release.
+- remove other permission from DB directory.
+- install init.d related files directly.
+- use HTTP as the default protocol.
+
+* Sun Jan 29 2012 Kouhei Sutou <kou@clear-code.com> - 1.3.0-0
+- new upstream release.
+- groonga-server package does not require groonga-munin-plugins package.
+ suggested by Masaharu IWAI. Thanks!!!
+- groonga package does not require groonga-doc package.
+ suggested by Masaharu IWAI. Thanks!!!
+
+* Thu Dec 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.9-0
+- new upstream release.
+
+* Tue Nov 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.8-0
+- new upstream release.
+- enable zlib support.
+- enable lzo support.
+- add --with-package-platform=redhat configure option to install init script.
+- add --with-munin-plugins cofnigure option to install Munin plugins.
+
+* Sat Oct 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.7-0
+- new upstream release.
+
+* Thu Sep 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.6-0
+- new upstream release.
+
+* Mon Aug 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.5-0
+- new upstream release.
+
+* Fri Jul 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.4-0
+- new upstream release.
+
+* Wed Jun 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.3-0
+- new upstream release.
+- add a new groong-tools package.
+
+* Sun May 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.2-0
+- new upstream release.
+- split server files into groonga-server package.
+
+* Fri Apr 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.1-0
+- new upstream release.
+
+* Tue Mar 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.0-0
+- new upstream release.
+
+* Wed Feb 09 2011 Kouhei Sutou <kou@clear-code.com> - 1.1.0-0
+- new upstream release.
+
+* Wed Feb 02 2011 Kouhei Sutou <kou@clear-code.com> - 1.0.8-0
+- new upstream release.
+
+* Sat Jan 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.0.7-0
+- new upstream release.
+
+* Fri Dec 31 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.6-0
+- new upstream release
+
+* Wed Dec 29 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.5-0
+- new upstream release.
+
+* Mon Nov 29 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.4-1
+- new upstream release
+
+* Wed Nov 24 2010 Daiki Ueno <dueno@redhat.com> - 1.0.3-2
+- %%ghost /var/run/*.
+
+* Fri Oct 29 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.3-1
+- new upstream release.
+
+* Thu Oct 09 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.2-2
+- merge Fedora changes.
+
+* Thu Sep 09 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.2-1
+- new upstream release.
+
+* Mon Sep 06 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.1-1
+- new upstream release.
+
+* Thu Sep 02 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.0-1
+- split packages.
+
+* Tue Aug 24 2010 Daiki Ueno <dueno@redhat.com> - 0.7.6-1
+- initial packaging for Fedora
diff --git a/storage/mroonga/vendor/groonga/packages/rpm/fedora/Makefile.am b/storage/mroonga/vendor/groonga/packages/rpm/fedora/Makefile.am
new file mode 100644
index 00000000000..0fe3a443eed
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/rpm/fedora/Makefile.am
@@ -0,0 +1,3 @@
+EXTRA_DIST = groonga.spec.in
+noinst_DATA = groonga.spec
+
diff --git a/storage/mroonga/vendor/groonga/packages/rpm/fedora/groonga.spec.in b/storage/mroonga/vendor/groonga/packages/rpm/fedora/groonga.spec.in
new file mode 100644
index 00000000000..7073c56236e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/rpm/fedora/groonga.spec.in
@@ -0,0 +1,641 @@
+%global php_extdir %(php-config --extension-dir 2>/dev/null || echo "undefined")
+
+Name: groonga
+Version: @VERSION@
+Release: 1%{?dist}
+Summary: An Embeddable Fulltext Search Engine
+
+Group: Applications/Text
+License: LGPLv2
+URL: http://groonga.org/
+Source0: http://packages.groonga.org/source/groonga/groonga-%{version}.tar.gz
+
+BuildRequires: mecab-devel
+BuildRequires: zlib-devel
+BuildRequires: lzo-devel
+#BuildRequires: messagepack-devel
+#BuildRequires: zeromq-devel
+#BuildRequires: libevent-devel
+BuildRequires: python2-devel
+BuildRequires: php-devel
+BuildRequires: libedit-devel
+BuildRequires: pcre-devel
+BuildRequires: systemd
+Requires: %{name}-libs = %{version}-%{release}
+Requires: %{name}-plugin-suggest = %{version}-%{release}
+Requires(post): systemd
+Requires(preun): systemd
+Requires(postun): systemd
+ExclusiveArch: %{ix86} x86_64
+
+%description
+Groonga is an embeddable full-text search engine library. It can
+integrate with DBMS and scripting languages to enhance their search
+functionality. It also provides a standalone data store server based
+on relational data model.
+
+%package libs
+Summary: Runtime libraries for Groonga
+Group: System Environment/Libraries
+License: LGPLv2 and (MIT or GPLv2)
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description libs
+This package contains the libraries for Groonga
+
+%package server-common
+Summary: Common packages for the Groonga server and the Groonga HTTP server
+Group: Applications/Text
+License: LGPLv2
+Requires: %{name} = %{version}-%{release}
+Requires(pre): shadow-utils
+
+%description server-common
+This package provides common settings for server use
+
+%package server-gqtp
+Summary: Groonga GQTP server
+Group: Applications/Text
+License: LGPLv2
+Requires: %{name}-server-common = %{version}-%{release}
+Requires(pre): shadow-utils
+Requires(post): /sbin/chkconfig
+Requires(preun): /sbin/chkconfig
+Requires(preun): /sbin/service
+Requires(postun): /sbin/service
+Obsoletes: %{name}-server < 2.0.7-0
+
+%description server-gqtp
+This package contains the Groonga GQTP server
+
+%package server-http
+Summary: Groonga HTTP server (stable)
+Group: Applications/Text
+License: LGPLv2
+Requires: %{name}-server-common = %{version}-%{release}
+Requires: curl
+Requires(pre): shadow-utils
+Requires(post): /sbin/chkconfig
+Requires(preun): /sbin/chkconfig
+Requires(preun): /sbin/service
+Requires(postun): /sbin/service
+Obsoletes: %{name}-server < 2.0.7-0
+Conflicts: %{name}-httpd
+
+%description server-http
+This package contains the Groonga HTTP server. It is stable but
+has only requisite minimum features.
+
+%package httpd
+Summary: Groonga HTTP server (experimental)
+Group: Applications/Text
+License: LGPLv2 and BSD
+Requires: %{name}-server-common = %{version}-%{release}
+Conflicts: %{name}-server-http
+
+%description httpd
+This package contains the Groonga HTTP server. It is experimental
+but has many features. Because it is based on nginx HTTP server.
+It will obsolete groonga-server-http when it is stable.
+
+%package doc
+Summary: Documentation for Groonga
+Group: Documentation
+License: LGPLv2 and BSD
+
+%description doc
+Documentation for Groonga
+
+%package devel
+Summary: Libraries and header files for Groonga
+Group: Development/Libraries
+Requires: %{name}-libs = %{version}-%{release}
+
+%description devel
+Libraries and header files for Groonga
+
+%package tokenizer-mecab
+Summary: MeCab tokenizer for Groonga
+Group: Applications/Text
+Requires: %{name}-libs = %{version}-%{release}
+Requires: mecab-dic
+
+%description tokenizer-mecab
+MeCab tokenizer for Groonga
+
+%package plugin-suggest
+Summary: Suggest plugin for Groonga
+Group: Applications/Text
+Requires: %{name}-libs = %{version}-%{release}
+
+%description plugin-suggest
+Sugget plugin for Groonga
+
+%package munin-plugins
+Summary: Munin plugins for Groonga
+Group: Applications/System
+Requires: %{name}-libs = %{version}-%{release}
+Requires: munin-node
+Requires(post): munin-node
+Requires(post): /sbin/service
+Requires(postun): /sbin/service
+
+%description munin-plugins
+Munin plugins for Groonga
+
+%package python
+Summary: Python language binding for Groonga
+Group: Development/Libraries
+Requires: %{name}-libs = %{version}-%{release}
+
+%description python
+Python language binding for Groonga
+
+%package php
+Summary: PHP language binding for Groonga
+Group: Development/Libraries
+Requires: %{name}-libs = %{version}-%{release}
+
+%description php
+PHP language binding for Groonga
+
+
+%prep
+#% define optflags -O0
+%setup -q
+
+
+%build
+%configure \
+ --disable-static \
+ --with-package-platform=fedora \
+ --with-zlib --with-lzo \
+ --with-munin-plugins
+sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool
+sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool
+make %{?_smp_mflags}
+
+# build python binding
+cd %{_builddir}/%{name}-%{version}/bindings/python/ql
+python setup.py config
+sed -i.cflags -e 's|^cflags =.*|cflags = []|' setup.py
+CFLAGS=-I%{_builddir}/%{name}-%{version}/include
+export CFLAGS
+LDFLAGS=-L%{_builddir}/%{name}-%{version}/lib/.libs
+export LDFLAGS
+python setup.py build
+
+# build php binding
+cd %{_builddir}/%{name}-%{version}/bindings/php
+sed -i.ldflags -e 's|PHP_ADD_LIBRARY_WITH_PATH(groonga, .*)|PHP_ADD_LIBRARY(groonga, GROONGA_SHARED_LIBADD)|' config.m4
+phpize
+CFLAGS="%{optflags}"
+export CFLAGS
+LDFLAGS=-L%{_builddir}/%{name}-%{version}/lib/.libs
+export LDFLAGS
+# --with-groonga is only necessary to avoid error in configure
+%configure --disable-static --with-groonga=%{_builddir}/%{name}-%{version}
+make %{?_smp_mflags}
+
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT INSTALL="install -p"
+rm $RPM_BUILD_ROOT%{_libdir}/groonga/plugins/*/*.la
+rm $RPM_BUILD_ROOT%{_libdir}/*.la
+
+mv $RPM_BUILD_ROOT%{_datadir}/doc/groonga groonga-doc
+
+mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/run/groonga
+mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/groonga/db
+mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/log/groonga
+mkdir -p $RPM_BUILD_ROOT%{_libdir}/groonga/plugins/normalizers
+
+mv $RPM_BUILD_ROOT%{_datadir}/groonga/munin/ $RPM_BUILD_ROOT%{_datadir}/
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/munin/plugin-conf.d/
+cat <<EOC > $RPM_BUILD_ROOT%{_sysconfdir}/munin/plugin-conf.d/groonga
+[groonga_*]
+ user groonga
+ group groonga
+ env.PATH ${_bindir}
+ env.database_path %{_localstatedir}/lib/groonga/db/db
+ env.host 127.0.0.1
+
+ env.http_host 127.0.0.1
+ env.http_port 10041
+ env.http_database_path %{_localstatedir}/lib/groonga/db/db
+ env.http_pid_path %{_localstatedir}/run/groonga/groonga-http.pid
+ env.http_query_log_path %{_localstatedir}/log/groonga/query-http.log
+
+ env.httpd_host 127.0.0.1
+ env.httpd_port 10041
+ env.httpd_database_path %{_localstatedir}/lib/groonga/db/db
+ env.httpd_pid_path %{_localstatedir}/run/groonga/groonga-httpd.pid
+ env.httpd_query_log_path %{_localstatedir}/log/groonga/httpd/groonga-query.log
+
+ env.gqtp_host 127.0.0.1
+ env.gqtp_port 10043
+ env.gqtp_database_path %{_localstatedir}/lib/groonga/db/db
+ env.gqtp_pid_path %{_localstatedir}/run/groonga/groonga-gqtp.pid
+ env.gqtp_query_log_path %{_localstatedir}/log/groonga/query-gqtp.log
+EOC
+
+# install python binding
+cd %{_builddir}/%{name}-%{version}/bindings/python/ql
+python setup.py install --root=$RPM_BUILD_ROOT
+
+# install php binding
+cd %{_builddir}/%{name}-%{version}/bindings/php
+make install INSTALL_ROOT=$RPM_BUILD_ROOT INSTALL="install -p"
+
+
+%pre server-common
+getent group groonga >/dev/null || groupadd -r groonga
+getent passwd groonga >/dev/null || \
+ useradd -r -g groonga -d %{_localstatedir}/lib/groonga -s /sbin/nologin \
+ -c 'groonga' groonga
+if [ $1 = 1 ] ; then
+ mkdir -p %{_localstatedir}/lib/groonga/db
+ groonga -n %{_localstatedir}/lib/groonga/db/db shutdown > /dev/null
+ chown -R groonga:groonga %{_localstatedir}/lib/groonga
+ mkdir -p %{_localstatedir}/run/groonga
+ chown -R groonga:groonga %{_localstatedir}/run/groonga
+fi
+exit 0
+
+%post server-http
+/bin/systemctl daemon-reload >/dev/null 2>&1 || :
+
+%post server-gqtp
+/bin/systemctl daemon-reload >/dev/null 2>&1 || :
+
+%post httpd
+/bin/systemctl daemon-reload >/dev/null 2>&1 || :
+if [ $1 = 1 ]; then
+ mkdir -p %{_localstatedir}/log/groonga/httpd
+ chown -R groonga:groonga %{_localstatedir}/log/groonga/httpd
+elif [ $1 = 2 ]; then
+ %{_sbindir}/groonga-httpd-restart
+fi
+
+%post libs -p /sbin/ldconfig
+
+%post munin-plugins
+%{_sbindir}/munin-node-configure --shell --remove-also | grep -e 'groonga_' | sh
+[ -f %{_localstatedir}/lock/subsys/munin-node ] && \
+ /sbin/service munin-node restart > /dev/null 2>&1
+:
+
+%preun server-http
+if [ $1 = 0 ] ; then
+ /bin/systemctl --no-reload disable groonga.service > /dev/null 2>&1 || :
+ /bin/systemctl stop groonga.service > /dev/null 2>&1 || :
+fi
+
+%postun server-http
+/bin/systemctl daemon-reload >/dev/null 2>&1 || :
+if [ $1 -ge 1 ] ; then
+ /bin/systemctl try-restart groonga.service >/dev/null 2>&1 || :
+fi
+
+%preun httpd
+if [ $1 = 0 ] ; then
+ /bin/systemctl --no-reload disable groonga-httpd.service > /dev/null 2>&1 || :
+ /bin/systemctl stop groonga-httpd.service > /dev/null 2>&1 || :
+fi
+
+%postun httpd
+/bin/systemctl daemon-reload >/dev/null 2>&1 || :
+if [ $1 -ge 1 ] ; then
+ %{_sbindir}/groonga-httpd-restart try-restart >/dev/null 2>&1 || :
+fi
+
+%triggerun -- groonga < 1.3.0-1
+/usr/bin/systemd-sysv-convert --save groonga >/dev/null 2>&1 ||:
+/bin/systemctl --no-reload enable groonga.service >/dev/null 2>&1 ||:
+/sbin/chkconfig --del groonga >/dev/null 2>&1 || :
+/bin/systemctl try-restart groonga.service >/dev/null 2>&1 || :
+
+%postun libs -p /sbin/ldconfig
+
+%postun munin-plugins
+if [ $1 -eq 0 ]; then
+ [ -f %{_localstatedir}/lock/subsys/munin-node ] && \
+ /sbin/service munin-node restart >/dev/null 2>&1
+ :
+fi
+
+
+%files
+%defattr(-,root,root,-)
+%{_datadir}/man/man1/*
+%{_datadir}/man/*/man1/*
+%{_bindir}/groonga
+%{_bindir}/groonga-benchmark
+
+%files libs
+%defattr(-,root,root,-)
+%doc README.md COPYING
+%{_libdir}/*.so.*
+%dir %{_libdir}/groonga
+%dir %{_libdir}/groonga/plugins
+%dir %{_libdir}/groonga/plugins/normalizers
+%dir %{_libdir}/groonga/plugins/query_expanders
+%dir %{_libdir}/groonga/plugins/suggest
+%dir %{_libdir}/groonga/plugins/table
+%dir %{_libdir}/groonga/plugins/tokenizers
+%{_libdir}/groonga/plugins/table/table.so
+%{_libdir}/groonga/plugins/query_expanders/tsv.so
+%{_datadir}/groonga/
+%config(noreplace) %{_sysconfdir}/groonga/synonyms.tsv
+
+%files server-common
+
+%files server-http
+%defattr(-,root,root,-)
+%config(noreplace) %{_sysconfdir}/groonga/
+%config(noreplace) %{_sysconfdir}/sysconfig/groonga-server-http
+%{_unitdir}/groonga-server-http.service
+%ghost %dir %{_localstatedir}/run/%{name}
+%attr(0750,groonga,groonga) %dir %{_localstatedir}/lib/%{name}
+%attr(0750,groonga,groonga) %dir %{_localstatedir}/lib/%{name}/db
+
+%files server-gqtp
+%defattr(-,root,root,-)
+%config(noreplace) %{_sysconfdir}/groonga/
+%config(noreplace) %{_sysconfdir}/sysconfig/groonga-server-gqtp
+%{_unitdir}/groonga-server-gqtp.service
+%ghost %dir %{_localstatedir}/run/%{name}
+%attr(0750,groonga,groonga) %dir %{_localstatedir}/lib/%{name}
+%attr(0750,groonga,groonga) %dir %{_localstatedir}/lib/%{name}/db
+
+%files httpd
+%defattr(-,root,root,-)
+%config(noreplace) %{_sysconfdir}/groonga/httpd/*
+%{_unitdir}/groonga-httpd.service
+%{_sbindir}/groonga-httpd
+%{_sbindir}/groonga-httpd-restart
+
+%files doc
+%defattr(-,root,root,-)
+%doc README.md COPYING
+%doc groonga-doc/*
+
+%files devel
+%defattr(-,root,root,-)
+%{_includedir}/groonga/
+%{_libdir}/*.so
+%{_libdir}/pkgconfig/groonga*.pc
+
+%files tokenizer-mecab
+%defattr(-,root,root,-)
+%{_libdir}/groonga/plugins/tokenizers/mecab.so
+
+%files plugin-suggest
+%defattr(-,root,root,-)
+%{_bindir}/groonga-suggest-*
+%{_libdir}/groonga/plugins/suggest/suggest.so
+
+%files munin-plugins
+%defattr(-,root,root,-)
+%{_datadir}/munin/plugins/*
+%config(noreplace) %{_sysconfdir}/munin/plugin-conf.d/*
+
+%files python
+%defattr(-,root,root,-)
+%{python_sitearch}/groongaql*
+
+%files php
+%defattr(-,root,root,-)
+%{php_extdir}/groonga.so
+
+%changelog
+* Fri Aug 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.0.5-1
+- new upstream release.
+
+* Tue Jul 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.0.4-1
+- new upstream release.
+
+* Sun Jun 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.0.3-1
+- new upstream release.
+
+* Thu May 29 2014 Kouhei Sutou <kou@clear-code.com> - 4.0.2-0
+- new upstream release.
+
+* Sat Mar 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.0.1-1
+- new upstream release.
+
+* Sun Feb 09 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 4.0.0-1
+- new upstream release.
+
+* Wed Jan 29 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 3.1.2-1
+- new upstream release.
+
+* Sun Dec 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.1.1-1
+- new upstream release.
+
+* Fri Nov 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.1.0-1
+- new upstream release.
+
+* Tue Oct 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.9-1
+- new upstream release.
+
+* Sun Sep 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.8-1
+- new upstream release.
+
+* Thu Aug 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.7-1
+- new upstream release.
+
+* Fri Mar 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.2-0
+- new upstream release.
+
+* Thu Feb 28 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.1-0
+- new upstream release.
+
+* Sat Feb 09 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 3.0.0-0
+- new upstream release.
+
+* Tue Jan 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 2.1.2-0
+- new upstream release.
+
+* Sat Dec 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.1.1-0
+- new upstream release.
+
+* Sat Dec 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.1.0-0
+- new upstream release.
+
+* Thu Nov 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.0.9-0
+- new upstream release.
+
+* Mon Oct 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.8-0
+- new upstream release.
+- Remove needless "Requires". They will be added by rpmbuild automatically.
+ Reported by by Daiki Ueno. Thanks!!!
+- Fix license of server-gqtp.
+- Fix license of server-http.
+- Add more description to server-http and httpd.
+
+* Sat Sep 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.0.7-0
+- new upstream release.
+- Split groonga-server package into groonga-server-gqtp and
+ groonga-server-http package.
+
+* Wed Aug 29 2012 HAYASHI Kentaro <hayashi@clear-code.com> - 2.0.6-0
+- new upstream release.
+- Split common tasks for server use into groonga-server-common package.
+- groonga-server and groonga-httpd require groonga-server-common package.
+
+* Sun Jul 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.5-0
+- new upstream release.
+
+* Fri Jun 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.4-0
+- new upstream release.
+- groonga package does not require groonga-tokenizer-mecab package.
+
+* Tue May 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.3-0
+- new upstream release.
+
+* Sun Apr 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.2-0
+- new upstream release.
+- use libedit.
+
+* Fri Mar 30 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.1-2
+- Use shutdown command for stop.
+
+* Fri Mar 30 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.1-1
+- Fix bind address argument parameter.
+ Patch by Masaharu IWAI. Thanks!!!
+
+* Thu Mar 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.1-0
+- new upstream release.
+- ensure removing build directory before installing.
+- grntest -> groonga-benchmark.
+- remove groonga-tools package.
+
+* Wed Feb 29 2012 Kouhei Sutou <kou@clear-code.com> - 2.0.0-0
+- new upstream release.
+- remove other permission from DB directory.
+- use HTTP as the default protocol.
+- support effective user and group in systemd.
+ Patch by Daiki Ueno. Thanks!!!
+
+* Mon Jan 30 2012 Daiki Ueno <dueno@redhat.com> - 1.3.0-1
+- built in Fedora
+- migrate groonga-server initscript to systemd service (#781503)
+
+* Sun Jan 29 2012 Kouhei Sutou <kou@clear-code.com> - 1.3.0-0
+- new upstream release.
+- groonga-server package does not require groonga-munin-plugins package.
+ suggested by Masaharu IWAI. Thanks!!!
+- groonga package does not require groonga-doc package.
+ suggested by Masaharu IWAI. Thanks!!!
+
+* Thu Dec 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.9-0
+- new upstream release.
+
+* Tue Nov 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.8-0
+- new upstream release.
+- enable zlib support.
+- enable lzo support.
+- add --with-package-platform=redhat configure option to install init script.
+- add --with-munin-plugins cofnigure option to install Munin plugins.
+
+* Sat Oct 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.7-0
+- new upstream release.
+
+* Thu Sep 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.6-0
+- new upstream release.
+
+* Mon Aug 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.5-0
+- new upstream release.
+
+* Fri Jul 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.4-0
+- new upstream release.
+
+* Wed Jun 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.3-0
+- new upstream release.
+- add a new groong-tools package.
+
+* Sun May 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.2-0
+- new upstream release.
+- split server files into groonga-server package.
+
+* Fri Apr 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.1-0
+- new upstream release.
+
+* Tue Mar 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.2.0-0
+- new upstream release.
+
+* Wed Feb 09 2011 Kouhei Sutou <kou@clear-code.com> - 1.1.0-0
+- new upstream release.
+
+* Wed Feb 02 2011 Kouhei Sutou <kou@clear-code.com> - 1.0.8-0
+- new upstream release.
+
+* Sat Jan 29 2011 Kouhei Sutou <kou@clear-code.com> - 1.0.7-0
+- new upstream release.
+
+* Fri Dec 31 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.6-0
+- new upstream release
+
+* Wed Dec 29 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.5-0
+- new upstream release.
+
+* Mon Nov 29 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.4-1
+- new upstream release
+
+* Wed Nov 24 2010 Daiki Ueno <dueno@redhat.com> - 1.0.3-2
+- %%ghost /var/run/*.
+
+* Sat Oct 09 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.3-1
+- new upstream release.
+
+* Thu Oct 7 2010 Daiki Ueno <dueno@redhat.com> - 1.0.2-7
+- own %%_localstatedir/lib/%%name/db.
+- use %%_sbindir RPM macro.
+
+* Wed Oct 6 2010 Daiki Ueno <dueno@redhat.com> - 1.0.2-6
+- use %%python_sitearch and %%php_extdir macros.
+- correct directory ownership for -munin-plugins subpackage.
+- supply %%optflags when building PHP binding.
+- don't set CGROUP_DAEMON in initscript.
+
+* Tue Oct 5 2010 Daiki Ueno <dueno@redhat.com> - 1.0.2-5
+- correct directory ownership for -munin-plugins subpackage.
+- make -doc subpackage require -libs.
+- correct directory ownership for directories under %%_localstatedir.
+- make initscript disabled by default
+- move "build process" of Python and PHP bindings to %%build from %%install
+- build against Python 2.7
+- fix naming of Python and PHP bindings (python-%%{name} to %%{name}-python)
+
+* Mon Oct 4 2010 Daiki Ueno <dueno@redhat.com> - 1.0.2-4
+- package Python and PHP bindings.
+
+* Mon Oct 4 2010 Daiki Ueno <dueno@redhat.com> - 1.0.2-3
+- fix License.
+- pass "-p" to the install command to preserve timestamps.
+- use RPM macros %%_initddir, %%_localstatedir, %%_prefix, etc.
+- use the standard snippet to creating user/group for groonga; don't
+ call userdel/groupdel.
+- add missing "Require(foo): bar" for /sbin/service, /sbin/chkconfig,
+ /sbin/ldconfig, and /usr/sbin/munin-node-configure.
+- fix attributes in %%files.
+- correct directory ownership.
+
+* Fri Oct 1 2010 Daiki Ueno <dueno@redhat.com> - 1.0.2-2
+- don't require autotools when building
+- pass --disable-static to %%configure
+
+* Thu Sep 09 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.2-1
+- new upstream release.
+
+* Mon Sep 06 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.1-1
+- new upstream release.
+
+* Thu Sep 02 2010 Kouhei Sutou <kou@clear-code.com> - 1.0.0-1
+- split packages.
+
+* Tue Aug 24 2010 Daiki Ueno <dueno@redhat.com> - 0.7.6-1
+- initial packaging for Fedora
diff --git a/storage/mroonga/vendor/groonga/packages/source/Makefile.am b/storage/mroonga/vendor/groonga/packages/source/Makefile.am
new file mode 100644
index 00000000000..9940b6800df
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/source/Makefile.am
@@ -0,0 +1,25 @@
+all:
+
+release: upload
+
+ensure-rsync-path:
+ @if test -z "$(RSYNC_PATH)"; then \
+ echo "--with-rsync-path configure option must be specified."; \
+ false; \
+ fi
+
+download: ensure-rsync-path
+ rsync -avz --progress $(RSYNC_PATH)/source/groonga/ files
+
+upload: ensure-rsync-path files/$(PACKAGE)-$(VERSION).tar.gz files/$(PACKAGE)-$(VERSION).zip
+ rsync -avz --progress --delete files/ $(RSYNC_PATH)/source/groonga
+
+files/$(PACKAGE)-$(VERSION).tar.gz: $(top_builddir)/$(PACKAGE)-$(VERSION).tar.gz
+ mkdir -p files
+ cp -p $< $@
+
+files/$(PACKAGE)-$(VERSION).zip: files/$(PACKAGE)-$(VERSION).tar.gz
+ rm -rf $(PACKAGE)-$(VERSION)
+ tar xvzf files/$(PACKAGE)-$(VERSION).tar.gz
+ zip -r $@ $(PACKAGE)-$(VERSION)
+ rm -rf $(PACKAGE)-$(VERSION)
diff --git a/storage/mroonga/vendor/groonga/packages/ubuntu/Makefile.am b/storage/mroonga/vendor/groonga/packages/ubuntu/Makefile.am
new file mode 100644
index 00000000000..af677d733e0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/ubuntu/Makefile.am
@@ -0,0 +1,24 @@
+CODE_NAMES = precise,trusty
+SOURCE = ../$(PACKAGE)-$(VERSION).tar.gz
+
+all:
+
+ensure-launchpad-configuration:
+ @if test -z "$(LAUNCHPAD_UPLOADER_PGP_KEY)"; then \
+ echo "--with-launchpad-uploader-pgp-key configure option must be specified."; \
+ false; \
+ fi
+
+upload: source ensure-launchpad-configuration
+ ./upload.rb \
+ --package '$(PACKAGE)' \
+ --version '$(VERSION)' \
+ --source-archive '$(SOURCE)' \
+ --code-names '$(CODE_NAMES)' \
+ --debian-directory '$(srcdir)/../debian/' \
+ --pgp-sign-key '$(LAUNCHPAD_UPLOADER_PGP_KEY)'
+
+source: $(SOURCE)
+
+$(SOURCE):
+ ln -s $(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz $(SOURCE)
diff --git a/storage/mroonga/vendor/groonga/packages/ubuntu/upload.rb b/storage/mroonga/vendor/groonga/packages/ubuntu/upload.rb
new file mode 100755
index 00000000000..508d1d4f447
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/ubuntu/upload.rb
@@ -0,0 +1,135 @@
+#!/usr/bin/env ruby
+#
+# Copyright(C) 2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+require "optparse"
+require "fileutils"
+require "pathname"
+
+class Uploader
+ def initialize
+ @dput_configuration_name = "groonga-ppa"
+ end
+
+ def run
+ ensure_dput_configuration
+
+ parse_command_line!
+
+ @code_names.each do |code_name|
+ upload(code_name)
+ end
+ end
+
+ private
+ def ensure_dput_configuration
+ dput_cf_path = Pathname.new("~/.dput.cf").expand_path
+ if dput_cf_path.exist?
+ dput_cf_content = dput_cf_path.read
+ else
+ dput_cf_content = ""
+ end
+ dput_cf_content.each_line do |line|
+ return if line.chomp == "[#{@dput_configuration_name}]"
+ end
+
+ dput_cf_path.open("w") do |dput_cf|
+ dput_cf.puts(dput_cf_content)
+ dput_cf.puts(<<-CONFIGURATION)
+[#{@dput_configuration_name}]
+fqdn = ppa.launchpad.net
+method = ftp
+incoming = ~groonga/ppa/ubuntu/
+login = anonymous
+allow_unsigned_uploads = 0
+ CONFIGURATION
+ end
+ end
+
+ def parse_command_line!
+
+ parser = OptionParser.new
+ parser.on("--package=NAME",
+ "The package name") do |name|
+ @package = name
+ end
+ parser.on("--version=VERSION",
+ "The version") do |version|
+ @version = version
+ end
+ parser.on("--source-archive=ARCHIVE",
+ "The source archive") do |source_archive|
+ @source_archive = Pathname.new(source_archive).expand_path
+ end
+ parser.on("--code-names=CODE_NAME1,CODE_NAME2,CODE_NAME3,...", Array,
+ "The target code names") do |code_names|
+ @code_names = code_names
+ end
+ parser.on("--debian-directory=DIRECTORY",
+ "The debian/ directory") do |debian_directory|
+ @debian_directory = Pathname.new(debian_directory).expand_path
+ end
+ parser.on("--pgp-sign-key=KEY",
+ "The PGP key to sign .changes and .dsc") do |pgp_sign_key|
+ @pgp_sign_key = pgp_sign_key
+ end
+
+ parser.parse!
+ end
+
+ def upload(code_name)
+ in_temporary_directory do
+ FileUtils.cp(@source_archive.to_s,
+ "#{@package}_#{@version}.orig.tar.gz")
+ run_command("tar", "xf", @source_archive.to_s)
+ directory_name = "#{@package}-#{@version}"
+ Dir.chdir(directory_name) do
+ FileUtils.cp_r(@debian_directory.to_s, "debian")
+ deb_version = "#{current_deb_version.succ}~#{code_name}1"
+ run_command("dch",
+ "--distribution", code_name,
+ "--newversion", deb_version,
+ "Build for #{code_name}.")
+ run_command("debuild", "-S", "-sa", "-pgpg2", "-k#{@pgp_sign_key}")
+ run_command("dput", @dput_configuration_name,
+ "../#{@package}_#{deb_version}_source.changes")
+ end
+ end
+ end
+
+ def current_deb_version
+ /\((.+)\)/ =~ File.read("debian/changelog").lines.first
+ $1
+ end
+
+ def in_temporary_directory
+ name = "tmp"
+ FileUtils.rm_rf(name)
+ FileUtils.mkdir_p(name)
+ Dir.chdir(name) do
+ yield
+ end
+ end
+
+ def run_command(*command_line)
+ unless system(*command_line)
+ raise "failed to run command: #{command_line.join(' ')}"
+ end
+ end
+end
+
+uploader = Uploader.new
+uploader.run
diff --git a/storage/mroonga/vendor/groonga/packages/windows/Makefile.am b/storage/mroonga/vendor/groonga/packages/windows/Makefile.am
new file mode 100644
index 00000000000..a3b86c00958
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/Makefile.am
@@ -0,0 +1,130 @@
+SUBDIRS = \
+ patches \
+ language-files
+EXTRA_DIST = Rakefile
+LICENSE_DIR = dist-x64/share/license
+ARCHITECTURES = x86 x64
+
+all:
+
+release: build package installer upload
+
+ensure-rsync-path:
+ @if test -z "$(RSYNC_PATH)"; then \
+ echo "--with-rsync-path configure option must be specified."; \
+ false; \
+ fi
+
+download: ensure-rsync-path
+ rsync -avz --progress $(RSYNC_PATH)/windows/groonga/ files
+
+upload: ensure-rsync-path
+ rsync -avz --progress --delete files/ $(RSYNC_PATH)/windows/groonga
+
+build_options = \
+ VERSION=$(VERSION) \
+ SOURCE=$(SOURCE) \
+ DEBUG_BUILD=$(DEBUG_BUILD)
+
+build: source
+ for architecture in $(ARCHITECTURES); do \
+ $(RUBY) -S rake build $(build_options) \
+ ARCHITECTURE=$${architecture}; \
+ done
+
+build-groonga: source
+ for architecture in $(ARCHITECTURES); do \
+ $(RUBY) -S rake build:groonga $(build_options) \
+ ARCHITECTURE=$${architecture}; \
+ done
+
+LICENSE:
+ @(echo "groonga"; \
+ echo "======="; \
+ echo; \
+ echo "AUTHORS"; \
+ echo "-------"; \
+ echo; \
+ cat $(LICENSE_DIR)/groonga/AUTHORS; \
+ echo; \
+ echo "COPYING"; \
+ echo "-------"; \
+ echo; \
+ cat $(LICENSE_DIR)/groonga/COPYING; \
+ echo; \
+ echo; \
+ echo "MeCab"; \
+ echo "====="; \
+ echo; \
+ echo "AUTHORS"; \
+ echo "-------"; \
+ echo; \
+ cat $(LICENSE_DIR)/mecab/AUTHORS; \
+ echo; \
+ echo "COPYING"; \
+ echo "-------"; \
+ echo; \
+ cat $(LICENSE_DIR)/mecab/COPYING; \
+ echo; \
+ echo "BSD"; \
+ echo "---"; \
+ echo; \
+ echo; cat $(LICENSE_DIR)/mecab/BSD; \
+ echo; \
+ echo "GPL"; \
+ echo "---"; \
+ echo; \
+ cat $(LICENSE_DIR)/mecab/GPL; \
+ echo; \
+ echo "LGPL"; \
+ echo "----"; \
+ echo; \
+ cat $(LICENSE_DIR)/mecab/LGPL; \
+ echo; \
+ echo; \
+ echo "NAIST-jdic"; \
+ echo "=========="; \
+ echo; \
+ echo "AUTHORS"; \
+ echo "-------"; \
+ echo; \
+ cat $(LICENSE_DIR)/naist-jdic/AUTHORS; \
+ echo; \
+ echo "COPYING"; \
+ echo "-------"; \
+ echo; \
+ cat $(LICENSE_DIR)/naist-jdic/COPYING; \
+ ) > LICENSE
+
+installer: installer-x86 installer-x64
+
+setup-x86.nsi: setup-x64.nsi
+ sed \
+ -e 's/64/86/g' \
+ -e 's/PROGRAMFILES86/PROGRAMFILES32/g' \
+ setup-x64.nsi > setup-x86.nsi
+
+installer-x86: LICENSE setup-x86.nsi
+ makensis setup-x86.nsi
+
+installer-x64: LICENSE setup-x64.nsi
+ makensis setup-x64.nsi
+
+package:
+ for architecture in $(ARCHITECTURES); do \
+ mkdir -p files; \
+ rm -rf files/$(PACKAGE)-$(VERSION)-$${architecture}; \
+ cp -a dist-$${architecture} \
+ files/$(PACKAGE)-$(VERSION)-$${architecture}; \
+ (cd files && \
+ zip -r $(PACKAGE)-$(VERSION)-$${architecture}.zip \
+ $(PACKAGE)-$(VERSION)-$${architecture}); \
+ rm -rf files/$(PACKAGE)-$(VERSION)-$${architecture}; \
+ done
+
+SOURCE=../$(PACKAGE)-$(VERSION).tar.gz
+
+source: $(SOURCE)
+
+$(SOURCE):
+ ln -s $(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz ../
diff --git a/storage/mroonga/vendor/groonga/packages/windows/Rakefile b/storage/mroonga/vendor/groonga/packages/windows/Rakefile
new file mode 100644
index 00000000000..490f8246403
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/Rakefile
@@ -0,0 +1,248 @@
+# -*- coding: utf-8; mode: ruby -*-
+#
+# Copyright (C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+require 'pathname'
+
+base_dir = Pathname.new(__FILE__).dirname
+
+groonga_win32_x86_p = ENV["ARCHITECTURE"] == "x86"
+groonga_version = ENV["VERSION"]
+groonga_source = Pathname.new(ENV["SOURCE"]).expand_path
+debug_build_p = ENV["DEBUG_BUILD"] == "yes"
+debug_flags = ["CFLAGS=-ggdb3 -O0", "CXXFLAGS=-ggdb3 -O0"]
+
+if groonga_win32_x86_p
+ dist_dir = Pathname.new("dist-x86").expand_path
+else
+ dist_dir = Pathname.new("dist-x64").expand_path
+end
+license_dir = dist_dir + "share" + "license"
+binary_dir = base_dir + dist_dir
+
+patches_dir = (base_dir + "patches").expand_path
+mecab_patches = [
+ "mecab-0.98-not-use-locale-on-mingw.diff",
+ "mecab-0.98-add-missing-dll-export.diff",
+]
+if groonga_win32_x86_p
+ host = "i686-w64-mingw32"
+else
+ host = "x86_64-w64-mingw32"
+ mecab_patches << "mecab-0.98-mingw-w64.diff"
+end
+
+namespace :build do
+ download_dir = Pathname.new("tmp/download").expand_path
+
+ desc "Build MessagePack and install it into #{dist_dir}."
+ task(:msgpack) do
+ tmp_dir = Pathname.new("tmp/msgpack")
+ rm_rf(tmp_dir)
+ mkdir_p(tmp_dir)
+ require 'open-uri'
+ msgpack_version = "0.5.9"
+ msgpack_base = "msgpack-#{msgpack_version}"
+ msgpack_tar_gz = "#{msgpack_base}.tar.gz"
+ msgpack_tar_gz_url_base =
+ "https://github.com/msgpack/msgpack-c/releases/download"
+ msgpack_tar_gz_url =
+ "#{msgpack_tar_gz_url_base}/cpp-#{msgpack_version}/#{msgpack_tar_gz}"
+ Dir.chdir(tmp_dir) do
+ msgpack_tar_gz = download_dir + msgpack_tar_gz
+ unless msgpack_tar_gz.exist?
+ mkdir_p(download_dir)
+ open(msgpack_tar_gz_url) do |downloaded_tar_gz|
+ File.open(msgpack_tar_gz, "wb") do |tar_gz|
+ tar_gz.print(downloaded_tar_gz.read)
+ end
+ end
+ end
+ sh("tar", "xzf", msgpack_tar_gz.to_s) or exit(false)
+ end
+ Dir.chdir(tmp_dir + msgpack_base) do
+ sh("autoreconf", "--install", "--force")
+ sh("./configure",
+ "--prefix=#{binary_dir}",
+ "--host=#{host}") or exit(false)
+ sh("env", "GREP_OPTIONS=--text", "nice", "make", "-j8") or exit(false)
+ sh("env", "GREP_OPTIONS=--text", "make", "install") or exit(false)
+
+ msgpack_license_dir = license_dir + "msgpack"
+ mkdir_p(msgpack_license_dir)
+ files = ["AUTHORS", "COPYING", "LICENSE"]
+ cp(files, msgpack_license_dir)
+ end
+ end
+
+ desc "Build MeCab and install it into #{dist_dir}."
+ task(:mecab) do
+ tmp_dir = Pathname.new("tmp/mecab")
+ rm_rf(tmp_dir)
+ mkdir_p(tmp_dir)
+ require 'open-uri'
+ mecab_version = "0.98"
+ mecab_base = "mecab-#{mecab_version}"
+ mecab_tar_gz = "#{mecab_base}.tar.gz"
+ mecab_tar_gz_url = "http://mecab.googlecode.com/files/#{mecab_tar_gz}"
+ Dir.chdir(tmp_dir) do
+ mecab_tar_gz = download_dir + mecab_tar_gz
+ unless mecab_tar_gz.exist?
+ mkdir_p(download_dir)
+ open(mecab_tar_gz_url) do |downloaded_tar_gz|
+ File.open(mecab_tar_gz, "wb") do |tar_gz|
+ tar_gz.print(downloaded_tar_gz.read)
+ end
+ end
+ end
+ sh("tar", "xzf", mecab_tar_gz.to_s) or exit(false)
+ end
+ Dir.chdir(tmp_dir + mecab_base) do
+ mecab_patches.each do |patch|
+ sh("patch -p1 < #{patches_dir + patch}")
+ end
+ sh("autoreconf", "--install", "--force")
+ sh("./configure",
+ "--prefix=#{binary_dir}",
+ "--host=#{host}") or exit(false)
+ sh("env", "GREP_OPTIONS=--text", "nice", "make", "-j8") or exit(false)
+ sh("env", "GREP_OPTIONS=--text", "make", "install") or exit(false)
+
+ mecab_rc_path = binary_dir + "etc" + "mecabrc"
+ win32_mecab_rc_path = binary_dir + "bin" + "mecabrc"
+ mv(mecab_rc_path, win32_mecab_rc_path)
+
+ mecab_license_dir = license_dir + "mecab"
+ mkdir_p(mecab_license_dir)
+ files = ["AUTHORS", "BSD", "COPYING", "GPL", "LGPL"]
+ cp(files, mecab_license_dir)
+ end
+ end
+
+ desc "Build MeCab dictionary and install it into #{dist_dir}."
+ task(:mecab_dict) do
+ tmp_dir = Pathname.new("tmp/mecab_dict")
+ rm_rf(tmp_dir)
+ mkdir_p(tmp_dir)
+ require 'open-uri'
+ naist_jdic_base = "mecab-naist-jdic-0.6.3-20100801"
+ naist_jdic_tar_gz = "#{naist_jdic_base}.tar.gz"
+ naist_jdic_tar_gz_url = "http://osdn.dl.sourceforge.jp/naist-jdic/48487/#{naist_jdic_tar_gz}"
+ Dir.chdir(tmp_dir) do
+ naist_jdic_tar_gz = download_dir + naist_jdic_tar_gz
+ unless naist_jdic_tar_gz.exist?
+ mkdir_p(download_dir)
+ open(naist_jdic_tar_gz_url) do |downloaded_tar_gz|
+ File.open(naist_jdic_tar_gz, "wb") do |tar_gz|
+ tar_gz.print(downloaded_tar_gz.read)
+ end
+ end
+ end
+ sh("tar", "xzf", naist_jdic_tar_gz.to_s) or exit(false)
+ end
+ Dir.chdir(tmp_dir + naist_jdic_base) do
+ sh("./configure",
+ "--with-dicdir=#{binary_dir}/share/mecab/dic/naist-jdic",
+ "--with-charset=utf-8") or exit(false)
+ sh("make", "-j8") or exit(false)
+ sh("make", "install-data") or exit(false)
+
+ naist_jdic_license_dir = license_dir + "naist-jdic"
+ mkdir_p(naist_jdic_license_dir)
+ files = ["AUTHORS", "COPYING"]
+ cp(files, naist_jdic_license_dir)
+ end
+ dictionary_dir = '$(rcpath)\..\share\mecab\dic\naist-jdic'
+ mecab_rc_path = binary_dir + "bin" + "mecabrc"
+ mecab_rc_content = mecab_rc_path.read
+ File.open(mecab_rc_path, "w") do |mecab_rc|
+ mecab_rc.print(mecab_rc_content.gsub(/^dicdir\s*=.+$/,
+ "dicdir = #{dictionary_dir}"))
+ end
+ end
+
+ desc "Build groonga and install it into #{dist_dir}/."
+ task(:groonga) do
+ tmp_dir = Pathname.new("tmp/groonga")
+ rm_rf(tmp_dir)
+ mkdir_p(tmp_dir)
+ Dir.chdir(tmp_dir) do
+ sh("tar", "xzf", groonga_source.to_s) or exit(false)
+ end
+ Dir.chdir(tmp_dir + "groonga-#{groonga_version}") do
+ mecab_config = binary_dir + "bin" + "mecab-config"
+ options = [
+ "--prefix=#{binary_dir}",
+ "--host=#{host}",
+ "--disable-libedit",
+ "--without-kytea",
+ "--without-cutter",
+ "--disable-benchmark",
+ "--disable-groonga-httpd",
+ "--with-message-pack=#{binary_dir}",
+ ]
+ if mecab_config.exist?
+ options << "--with-mecab-config=#{mecab_config}"
+ else
+ options << "--without-mecab"
+ end
+ options.concat(debug_flags) if debug_build_p
+ sh("./configure", *options) or exit(false)
+ sh("env", "GREP_OPTIONS=--text", "nice", "make", "-j8") or exit(false)
+ sh("env", "GREP_OPTIONS=--text", "make", "install") or exit(false)
+
+ groonga_license_dir = license_dir + "groonga"
+ mkdir_p(groonga_license_dir)
+ files = ["README.md", "COPYING"]
+ cp(files, groonga_license_dir)
+ end
+ end
+
+ task(:clean) do
+ rm_rf(dist_dir)
+ end
+
+ task(:pre => :clean)
+ task(:post)
+end
+
+namespace :gcc do
+ namespace :dll do
+ desc "Bundle GCC related DLLs"
+ task(:bundle) do
+ dlls = ["libstdc++-6.dll", "libwinpthread-1.dll"]
+ if groonga_win32_x86_p
+ dlls << "libgcc_s_sjlj-1.dll"
+ else
+ dlls << "libgcc_s_seh-1.dll"
+ end
+ dlls.each do |dll|
+ full_path = `#{host}-g++ -print-file-name=#{dll}`.strip
+ cp(full_path, (binary_dir + "bin").to_s)
+ end
+ end
+ end
+end
+
+task("build:post" => "gcc:dll:bundle")
+
+desc "Build MeCab and groonga and install them into #{dist_dir}/."
+task(:build => ["build:pre",
+ "build:msgpack",
+ "build:mecab",
+ "build:mecab_dict",
+ "build:groonga",
+ "build:post"])
diff --git a/storage/mroonga/vendor/groonga/packages/windows/create-setup.bat b/storage/mroonga/vendor/groonga/packages/windows/create-setup.bat
new file mode 100644
index 00000000000..1496f758411
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/create-setup.bat
@@ -0,0 +1,2 @@
+echo @echo off > %1\setup.bat
+echo set PATH=%1;%%PATH%% >> %1\setup.bat
diff --git a/storage/mroonga/vendor/groonga/packages/windows/language-files/Makefile.am b/storage/mroonga/vendor/groonga/packages/windows/language-files/Makefile.am
new file mode 100644
index 00000000000..34025ea6409
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/language-files/Makefile.am
@@ -0,0 +1,3 @@
+EXTRA_DIST = \
+ english.nsi \
+ japanese.nsi
diff --git a/storage/mroonga/vendor/groonga/packages/windows/language-files/english.nsi b/storage/mroonga/vendor/groonga/packages/windows/language-files/english.nsi
new file mode 100644
index 00000000000..989b1e95d92
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/language-files/english.nsi
@@ -0,0 +1,2 @@
+LangString FinishUninstallSuccessfully ${LANG_ENGLISH} "Uninstall $(^Name) successfully."
+LangString ConfirmUninstall ${LANG_ENGLISH} "Really uninstall $(^Name)?"
diff --git a/storage/mroonga/vendor/groonga/packages/windows/language-files/japanese.nsi b/storage/mroonga/vendor/groonga/packages/windows/language-files/japanese.nsi
new file mode 100644
index 00000000000..25df8102fd6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/language-files/japanese.nsi
@@ -0,0 +1,2 @@
+LangString FinishUninstallSuccessfully ${LANG_JAPANESE} "$(^Name)Sɍ폜܂B"
+LangString ConfirmUninstall ${LANG_JAPANESE} "{$(^Name)ACXg[ĂłH"
diff --git a/storage/mroonga/vendor/groonga/packages/windows/patches/Makefile.am b/storage/mroonga/vendor/groonga/packages/windows/patches/Makefile.am
new file mode 100644
index 00000000000..1012252c155
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/patches/Makefile.am
@@ -0,0 +1,3 @@
+EXTRA_DIST = \
+ mecab-0.98-mingw-w64.diff \
+ mecab-0.98-not-use-locale-on-mingw.diff
diff --git a/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.98-add-missing-dll-export.diff b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.98-add-missing-dll-export.diff
new file mode 100644
index 00000000000..e10adb442fb
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.98-add-missing-dll-export.diff
@@ -0,0 +1,11 @@
+diff -ru mecab-0.98.orig/src/Makefile.am mecab-0.98/src/Makefile.am
+--- mecab-0.98.orig/src/Makefile.am 2008-09-14 02:01:17.000000000 +0900
++++ mecab-0.98/src/Makefile.am 2014-08-28 22:16:44.262074429 +0900
+@@ -3,6 +3,7 @@
+ EXTRA_DIST = Makefile.msvc.in make.bat
+ pkglibexecdir = ${libexecdir}/mecab
+ INCLUDES = -DDIC_VERSION=$(DIC_VERSION) $(MECAB_WITHOUT_SHARE_DIC) $(MECAB_WITHOUT_MUTEX_LOCK) $(MECAB_USE_UTF8_ONLY) -DMECAB_DEFAULT_RC="\"$(MECAB_DEFAULT_RC)\""
++libmecab_la_CPPFLAGS = -DDLL_EXPORT
+ libmecab_la_LDFLAGS = -no-undefined -version-info $(LTVERSION)
+ libmecab_la_SOURCES = utils.cpp utils.h eval.cpp iconv_utils.cpp iconv_utils.h \
+ dictionary_rewriter.h dictionary_rewriter.cpp dictionary_generator.cpp \
diff --git a/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.98-mingw-w64.diff b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.98-mingw-w64.diff
new file mode 100644
index 00000000000..07cc97ed38c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.98-mingw-w64.diff
@@ -0,0 +1,13 @@
+diff -ru mecab-0.98.orig/src/string_buffer.h mecab-0.98/src/string_buffer.h
+--- mecab-0.98.orig/src/string_buffer.h 2009-04-19 00:03:04.000000000 +0900
++++ mecab-0.98/src/string_buffer.h 2011-04-24 09:40:23.166985912 +0900
+@@ -45,6 +45,9 @@
+ StringBuffer& operator<<(unsigned short int n) { _UITOA(n); }
+ StringBuffer& operator<<(unsigned int n) { _UITOA(n); }
+ StringBuffer& operator<<(unsigned long int n) { _UITOA(n); }
++#if SIZEOF_SIZE_T == SIZEOF_LONG_LONG
++ StringBuffer& operator<<(unsigned long long int n) { _UITOA(n); }
++#endif
+
+ StringBuffer& operator<< (char n) {
+ return this->write(n);
diff --git a/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.98-not-use-locale-on-mingw.diff b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.98-not-use-locale-on-mingw.diff
new file mode 100644
index 00000000000..386dda95153
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.98-not-use-locale-on-mingw.diff
@@ -0,0 +1,15 @@
+diff -ru mecab-0.98.orig/src/libmecab.cpp mecab-0.98/src/libmecab.cpp
+--- mecab-0.98.orig/src/libmecab.cpp 2009-04-19 00:03:04.000000000 +0900
++++ mecab-0.98/src/libmecab.cpp 2011-04-29 23:45:03.331006297 +0900
+@@ -57,9 +57,11 @@
+ if (!DllInstance) {
+ DllInstance = hinst;
+ }
++#if !defined(__GNUC__)
+ std::locale loc(std::locale("japanese"),
+ "C", std::locale::numeric);
+ std::locale::global(loc);
++#endif
+ return TRUE;
+ }
+ #ifdef __cplusplus
diff --git a/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-cflags.diff b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-cflags.diff
new file mode 100644
index 00000000000..1fe8cd61b85
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-cflags.diff
@@ -0,0 +1,12 @@
+diff -ur mecab-0.994.orig/configure.in mecab-0.994/configure.in
+--- mecab-0.994.orig/configure.in 2012-04-01 23:21:02.000000000 +0900
++++ mecab-0.994/configure.in 2012-09-14 15:58:19.759200168 +0900
+@@ -302,6 +302,8 @@
+ case "$host_os" in
+ mingw* | os2*)
+ MECAB_DEFAULT_RC='c:\\\\Program Files\\\\mecab\\\\etc\\\\mecabrc'
++ CFLAGS="$CFLAGS -municode -DUNICODE -D_UNICODE"
++ CXXFLAGS="$CXXFLAGS -municode -DUNICODE -D_UNICODE"
+ ;;
+ *)
+ MECAB_DEFAULT_RC="$sysconfdir/mecabrc"
diff --git a/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-externc.diff b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-externc.diff
new file mode 100644
index 00000000000..0e0a4e6b0a5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-externc.diff
@@ -0,0 +1,12 @@
+--- mecab-0.994.orig/src/winmain.h 2011-11-01 23:50:54.000000000 +0900
++++ mecab-0.994/src/winmain.h 2012-09-19 15:58:36.549360996 +0900
+@@ -59,6 +59,9 @@
+
+ int wmain_to_main_wrapper(int argc, char **argv);
+
++#if defined(__MINGW32__)
++extern "C"
++#endif
+ int wmain(int argc, wchar_t **argv) {
+ CommandLine cmd(argc, argv);
+ return wmain_to_main_wrapper(cmd.argc(), cmd.argv());
diff --git a/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-ltmain-wmain.diff b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-ltmain-wmain.diff
new file mode 100644
index 00000000000..9151c7b1695
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-ltmain-wmain.diff
@@ -0,0 +1,11 @@
+--- mecab-0.994.orig/ltmain.sh 2012-06-04 23:15:21.000000000 +0900
++++ mecab-0.994/ltmain.sh 2012-09-19 11:42:59.429371976 +0900
+@@ -4348,7 +4348,7 @@
+ static const char *debug_opt = LTWRAPPER_OPTION_PREFIX "debug";
+
+ int
+-main (int argc, char *argv[])
++wmain (int argc, wchar_t *argv[])
+ {
+ char **newargz;
+ int newargc;
diff --git a/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-yieldprocessor-macro.diff b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-yieldprocessor-macro.diff
new file mode 100644
index 00000000000..6058f19bb92
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-0.994-mingw-yieldprocessor-macro.diff
@@ -0,0 +1,11 @@
+--- mecab-0.994.orig/src/thread.h 2011-12-05 02:31:09.000000000 +0900
++++ mecab-0.994/src/thread.h 2012-09-20 12:15:46.695377866 +0900
+@@ -47,7 +47,7 @@
+ #undef yield_processor
+ #define atomic_add(a, b) ::InterlockedExchangeAdd(a, b)
+ #define compare_and_swap(a, b, c) ::InterlockedCompareExchange(a, c, b)
+-#define yield_processor() ::YieldProcessor()
++#define yield_processor() YieldProcessor()
+ #define HAVE_ATOMIC_OPS 1
+ #endif
+
diff --git a/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-mingw-unsigned-long-long-int.diff b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-mingw-unsigned-long-long-int.diff
new file mode 100644
index 00000000000..9e115e6a6f4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/patches/mecab-mingw-unsigned-long-long-int.diff
@@ -0,0 +1,24 @@
+diff --exclude .svn -ur mecab.orig/configure.in mecab/configure.in
+--- mecab.orig/configure.in 2012-09-20 13:46:17.431392052 +0900
++++ mecab/configure.in 2012-09-20 15:09:21.695405072 +0900
+@@ -47,6 +47,7 @@
+ AC_CHECK_SIZEOF(long long)
+ AC_CHECK_SIZEOF(size_t)
+ AC_TYPE_SIZE_T
++AC_TYPE_UNSIGNED_LONG_LONG_INT
+
+ AC_FUNC_MMAP
+ AC_CHECK_LIB(stdc++, main, MECAB_LIBS="-lstdc++")
+diff --exclude .svn -ur mecab.orig/src/string_buffer.h mecab/src/string_buffer.h
+--- mecab.orig/src/string_buffer.h 2012-09-20 13:46:15.875392048 +0900
++++ mecab/src/string_buffer.h 2012-09-20 15:09:46.783405140 +0900
+@@ -44,6 +44,9 @@
+ StringBuffer& operator<<(unsigned short int n) { _UITOA(n); }
+ StringBuffer& operator<<(unsigned int n) { _UITOA(n); }
+ StringBuffer& operator<<(unsigned long int n) { _UITOA(n); }
++#ifdef HAVE_UNSIGNED_LONG_LONG_INT
++ StringBuffer& operator<<(unsigned long long int n) { _UITOA(n); }
++#endif
+
+ StringBuffer& operator<< (char n) {
+ return this->write(n);
diff --git a/storage/mroonga/vendor/groonga/packages/windows/setup-x64.nsi.in b/storage/mroonga/vendor/groonga/packages/windows/setup-x64.nsi.in
new file mode 100644
index 00000000000..67270f17fc7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/windows/setup-x64.nsi.in
@@ -0,0 +1,112 @@
+; Copyright (c) 2010-2011 Kouhei Sutou <kou@clear-code.com>
+
+!define PRODUCT_NAME "@PACKAGE@"
+!define PRODUCT_VERSION "@VERSION@"
+!define PRODUCT_PUBLISHER "Brazil"
+!define PRODUCT_WEB_SITE "http://groonga.org/"
+!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
+!define PRODUCT_UNINST_ROOT_KEY "HKLM"
+!define PRODUCT_STARTMENU_REGVAL "NSIS:StartMenuDir"
+
+SetCompress force
+SetCompressor lzma
+
+!include "MUI2.nsh"
+!include "LogicLib.nsh"
+
+; MUI Settings
+!define MUI_ABORTWARNING
+!define MUI_LANGDLL_ALLLANGUAGES
+!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
+!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
+
+!insertmacro MUI_PAGE_WELCOME
+!define MUI_LICENSEPAGE_CHECKBOX
+!insertmacro MUI_PAGE_LICENSE "LICENSE"
+!insertmacro MUI_PAGE_COMPONENTS
+!insertmacro MUI_PAGE_DIRECTORY
+Var START_MENU_FOLDER
+!define MUI_STARTMENUPAGE_NODISABLE
+!define MUI_STARTMENUPAGE_DEFAULTFOLDER "${PRODUCT_NAME}"
+!define MUI_STARTMENUPAGE_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}"
+!define MUI_STARTMENUPAGE_REGISTRY_KEY "${PRODUCT_UNINST_KEY}"
+!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${PRODUCT_STARTMENU_REGVAL}"
+!insertmacro MUI_PAGE_STARTMENU Application $START_MENU_FOLDER
+!insertmacro MUI_PAGE_INSTFILES
+; !define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\doc\README.txt"
+!insertmacro MUI_PAGE_FINISH
+
+!insertmacro MUI_UNPAGE_WELCOME
+!insertmacro MUI_UNPAGE_CONFIRM
+!insertmacro MUI_UNPAGE_INSTFILES
+!insertmacro MUI_UNPAGE_FINISH
+
+!insertmacro MUI_LANGUAGE "English"
+!insertmacro MUI_LANGUAGE "Japanese"
+!insertmacro MUI_RESERVEFILE_LANGDLL
+
+!include "language-files\english.nsi"
+!include "language-files\japanese.nsi"
+
+Function .onInit
+ !insertmacro MUI_LANGDLL_DISPLAY
+FunctionEnd
+
+
+Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
+OutFile "files\${PRODUCT_NAME}-${PRODUCT_VERSION}-x64.exe"
+InstallDir "$PROGRAMFILES64\${PRODUCT_NAME}"
+ShowInstDetails show
+ShowUnInstDetails show
+
+
+# Installer
+
+Section "groonga"
+ SectionIn 1 RO
+ SetOverwrite ifdiff
+
+ SetOutPath $INSTDIR
+ File /r dist-x64\*
+ File /oname=bin\create-setup.bat create-setup.bat
+
+ ExecWait '"$INSTDIR\bin\create-setup.bat" "$INSTDIR\bin"'
+
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+ SetShellVarContext all
+ CreateDirectory "$SMPROGRAMS\$START_MENU_FOLDER"
+ CreateShortCut "$SMPROGRAMS\$START_MENU_FOLDER\Uninstall.lnk" $INSTDIR\uninstall.exe
+ CreateShortCut "$SMPROGRAMS\$START_MENU_FOLDER\groonga command prompt.lnk" "$SYSDIR\cmd.exe" '/E:ON /K ""$INSTDIR\bin\setup.bat""'
+ !insertmacro MUI_STARTMENU_WRITE_END
+SectionEnd
+
+Section -Post
+ WriteUninstaller $INSTDIR\uninstall.exe
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" $INSTDIR\uninstall.exe
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "StartMenuFolder" "$START_MENU_FOLDER"
+SectionEnd
+
+# Uninstaller
+
+Function un.onUninstSuccess
+ HideWindow
+ MessageBox MB_ICONINFORMATION|MB_OK $(FinishUninstallSuccessfully)
+FunctionEnd
+
+Function un.onInit
+ !insertmacro MUI_UNGETLANGUAGE
+FunctionEnd
+
+Section "Uninstall"
+ ReadRegStr $START_MENU_FOLDER ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "StartMenuFolder"
+ DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
+
+ RMDir /r $INSTDIR
+
+ SetShellVarContext all
+ RMDir /r "$SMPROGRAMS\$START_MENU_FOLDER"
+SectionEnd
diff --git a/storage/mroonga/vendor/groonga/packages/yum/Makefile.am b/storage/mroonga/vendor/groonga/packages/yum/Makefile.am
new file mode 100644
index 00000000000..93cf4b8f5b6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/yum/Makefile.am
@@ -0,0 +1,82 @@
+REPOSITORIES_PATH = repositories
+DISTRIBUTIONS = centos
+ARCHITECTURES = i386 x86_64
+BASE_URL_PREFIX = http://packages.groonga.org
+HAVE_DEVELOPMENT_BRANCH = no
+
+release: download build sign-packages update-repository upload
+
+ensure-rsync-path:
+ @if test -z "$(RSYNC_PATH)"; then \
+ echo "--with-rsync-path configure option must be specified."; \
+ false; \
+ fi
+
+sign-packages:
+ ./sign-rpm.sh '$(GPG_UID)' '$(REPOSITORIES_PATH)/' \
+ '$(DISTRIBUTIONS)'
+
+update-repository:
+ ./update-repository.sh '$(PACKAGE)' '$(REPOSITORIES_PATH)/' \
+ '$(DISTRIBUTIONS)'
+
+upload: ensure-rsync-path
+ for distribution in $(DISTRIBUTIONS); do \
+ rsync -avz --progress --delete --exclude .gitignore \
+ $(REPOSITORIES_PATH)/$${distribution}/ \
+ $(RSYNC_PATH)/$${distribution}; \
+ done
+
+download: ensure-rsync-path
+ mkdir -p $(REPOSITORIES_PATH)
+ for distribution in $(DISTRIBUTIONS); do \
+ rsync -avz --progress --delete \
+ $(RSYNC_PATH)/$${distribution}/ \
+ $(REPOSITORIES_PATH)/$${distribution}; \
+ done
+
+build: build-in-vm build-release-rpm
+
+build-in-vm: source specs env.sh
+ vagrant destroy --force
+ for architecture in $(ARCHITECTURES); do \
+ for version in 5 6 7; do \
+ if [ $$version = 7 -a $$architecture = i386 ]; then \
+ continue; \
+ fi; \
+ id=centos-$$version-$$architecture; \
+ vagrant up $$id; \
+ vagrant destroy --force $$id; \
+ done; \
+ done
+
+build-release-rpm: RPM-GPG-KEY-$(PACKAGE)
+ ./build-release-rpm.sh \
+ $(PACKAGE) \
+ '$(PACKAGE_TITLE)' \
+ $(BASE_URL_PREFIX) \
+ $(REPOSITORIES_PATH)/ \
+ '$(DISTRIBUTIONS)' \
+ $(HAVE_DEVELOPMENT_BRANCH)
+
+ensure-public-key:
+ gpg --list-keys '$(GPG_UID)' > /dev/null || \
+ gpg --keyserver keyserver.ubuntu.com --recv-key '$(GPG_UID)'
+
+RPM-GPG-KEY-$(PACKAGE): ensure-public-key
+ gpg --armor --export '$(GPG_UID)' > $@
+
+source: tmp/$(PACKAGE)-$(VERSION).tar.gz
+
+tmp/$(PACKAGE)-$(VERSION).tar.gz: $(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz
+ mkdir -p tmp/
+ cp $(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz tmp/
+
+$(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz:
+ cd $(abs_top_builddir) && $(MAKE) dist
+
+specs: tmp/centos/$(PACKAGE).spec
+
+tmp/centos/$(PACKAGE).spec: $(builddir)/../rpm/centos/$(PACKAGE).spec
+ mkdir -p tmp/centos
+ cp $(builddir)/../rpm/centos/$(PACKAGE).spec tmp/centos/
diff --git a/storage/mroonga/vendor/groonga/packages/yum/Vagrantfile b/storage/mroonga/vendor/groonga/packages/yum/Vagrantfile
new file mode 100644
index 00000000000..a0afb49dc26
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/yum/Vagrantfile
@@ -0,0 +1,41 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
+VAGRANTFILE_API_VERSION = "2"
+
+Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+ vms = [
+ {
+ :id => "centos-5-i386",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-5.10-i386_chef-provisionerless.box",
+ },
+ {
+ :id => "centos-5-x86_64",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-5.10_chef-provisionerless.box",
+ },
+ {
+ :id => "centos-6-i386",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5-i386_chef-provisionerless.box",
+ },
+ {
+ :id => "centos-6-x86_64",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box",
+ },
+ {
+ :id => "centos-7-x86_64",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-7.0_chef-provisionerless.box",
+ },
+ ]
+
+ vms.each do |vm|
+ config.vm.define(vm[:id]) do |node|
+ node.vm.box = vm[:id]
+ node.vm.box_url = vm[:box_url]
+ node.vm.provision(:shell, :path => "build-rpm.sh")
+ node.vm.provider("virtualbox") do |virtual_box|
+ virtual_box.memory = 4096
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/packages/yum/build-release-rpm.sh b/storage/mroonga/vendor/groonga/packages/yum/build-release-rpm.sh
new file mode 100755
index 00000000000..f606c5d9c26
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/yum/build-release-rpm.sh
@@ -0,0 +1,99 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 6 ]; then
+ echo "Usage: $0 PACKAGE PACKAGE_TITLE BASE_URL_PREFIX DESTINATION DISTRIBUTIONS HAVE_DEVELOPMENT_BRANCH"
+ echo " e.g.: $0 milter-manager 'milter manager' http://downloads.sourceforge.net/milter-manager' repositories/ 'fedora centos' yes"
+ exit 1
+fi
+
+PACKAGE=$1
+PACKAGE_TITLE=$2
+BASE_URL_PREFIX=$3
+DESTINATION=$4
+DISTRIBUTIONS=$5
+HAVE_DEVELOPMENT_BRANCH=$6
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+rpm_base_dir=$HOME/rpm
+
+if [ ! -f ~/.rpmmacros ]; then
+ run cat <<EOM > ~/.rpmmacros
+%_topdir $rpm_base_dir
+EOM
+fi
+
+run mkdir -p $rpm_base_dir/SOURCES
+run mkdir -p $rpm_base_dir/SPECS
+run mkdir -p $rpm_base_dir/BUILD
+run mkdir -p $rpm_base_dir/RPMS
+run mkdir -p $rpm_base_dir/SRPMS
+
+for distribution in ${DISTRIBUTIONS}; do
+ case $distribution in
+ fedora)
+ distribution_label=Fedora
+ distribution_versions="20"
+ ;;
+ centos)
+ distribution_label=CentOS
+ distribution_versions="5 6"
+ ;;
+ esac
+ repo=${PACKAGE}.repo
+ if test "$HAVE_DEVELOPMENT_BRANCH" = "yes"; then
+ run cat <<EOR > $repo
+[$PACKAGE]
+name=$PACKAGE_TITLE for $distribution_label \$releasever - \$basearch
+baseurl=$BASE_URL_PREFIX/$distribution/\$releasever/stable/\$basearch/
+gpgcheck=1
+enabled=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-$PACKAGE
+
+[$PACKAGE-development]
+name=$PACKAGE_TITLE for $distribution_label \$releasever - development - \$basearch
+baseurl=$BASE_URL_PREFIX/$distribution/\$releasever/development/\$basearch/
+gpgcheck=1
+enabled=0
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-$PACKAGE
+EOR
+ else
+ run cat <<EOR > $repo
+[$PACKAGE]
+name=$PACKAGE_TITLE for $distribution_label \$releasever - \$basearch
+baseurl=$BASE_URL_PREFIX/$distribution/\$releasever/\$basearch/
+gpgcheck=1
+enabled=1
+gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-$PACKAGE
+EOR
+ fi
+ run tar cfz $rpm_base_dir/SOURCES/${PACKAGE}-release.tar.gz \
+ -C ${script_base_dir} ${repo} RPM-GPG-KEY-${PACKAGE}
+ run cp ${script_base_dir}/${PACKAGE}-release.spec $rpm_base_dir/SPECS/
+
+ run rpmbuild -ba $rpm_base_dir/SPECS/${PACKAGE}-release.spec
+
+ top_dir=${DESTINATION}${distribution}
+
+ run mkdir -p $top_dir
+ run cp -p \
+ $rpm_base_dir/RPMS/noarch/${PACKAGE}-release-* \
+ $rpm_base_dir/SRPMS/${PACKAGE}-release-* \
+ ${script_base_dir}/RPM-GPG-KEY-${PACKAGE} \
+ $top_dir
+
+ for distribution_version in $distribution_versions; do
+ cp $top_dir/*.src.rpm $top_dir/$distribution_version/source/SRPMS/
+ cp $top_dir/*.noarch.rpm $top_dir/$distribution_version/i386/Packages/
+ cp $top_dir/*.noarch.rpm $top_dir/$distribution_version/x86_64/Packages/
+ done
+done
diff --git a/storage/mroonga/vendor/groonga/packages/yum/build-rpm.sh b/storage/mroonga/vendor/groonga/packages/yum/build-rpm.sh
new file mode 100755
index 00000000000..2996aff7975
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/yum/build-rpm.sh
@@ -0,0 +1,124 @@
+#!/bin/sh
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+rpmbuild_options=
+
+. /vagrant/env.sh
+
+distribution=$(cut -d " " -f 1 /etc/redhat-release | tr "A-Z" "a-z")
+if grep -q Linux /etc/redhat-release; then
+ distribution_version=$(cut -d " " -f 4 /etc/redhat-release)
+else
+ distribution_version=$(cut -d " " -f 3 /etc/redhat-release)
+fi
+distribution_version=$(echo ${distribution_version} | sed -e 's/\..*$//g')
+
+architecture="$(arch)"
+case "${architecture}" in
+ i*86)
+ architecture=i386
+ ;;
+esac
+
+run yum groupinstall -y "Development Tools"
+run yum install -y rpm-build rpmdevtools tar ${DEPENDED_PACKAGES}
+
+if [ -x /usr/bin/rpmdev-setuptree ]; then
+ rm -rf .rpmmacros
+ run rpmdev-setuptree
+else
+ run cat <<EOM > ~/.rpmmacros
+%_topdir ${HOME}/rpmbuild
+EOM
+ run mkdir -p ~/rpmbuild/SOURCES
+ run mkdir -p ~/rpmbuild/SPECS
+ run mkdir -p ~/rpmbuild/BUILD
+ run mkdir -p ~/rpmbuild/RPMS
+ run mkdir -p ~/rpmbuild/SRPMS
+fi
+
+repository="/vagrant/repositories/${distribution}/${distribution_version}"
+rpm_dir="${repository}/${architecture}/Packages"
+srpm_dir="${repository}/source/SRPMS"
+run mkdir -p "${rpm_dir}" "${srpm_dir}"
+
+build_fedora_srpm()
+{
+ base=http://download.fedoraproject.org/pub/fedora/linux/releases/20/Everything/source/SRPMS/m
+ update=http://download.fedoraproject.org/pub/fedora/linux/updates/20/SRPMS
+ srpm="$1"
+ srpm_base="$2"
+
+ run cd
+
+ run mkdir -p tmp
+ run cd tmp
+ wget "${update}/${srpm}"
+ if [ $? -ne 0 ]; then
+ run wget "${base}/${srpm}"
+ fi
+ run rpm2cpio "${srpm}" | run cpio -id
+ run rm "${srpm}"
+
+ case "${srpm}" in
+ mecab-ipadic*)
+ patch -p0 < /vagrant/patches/mecab-ipadic-provides-mecab-dic.diff
+ ;;
+ mecab-jumandic-*)
+ patch -p0 < /vagrant/patches/mecab-jumandic-provides-mecab-dic.diff
+ ;;
+ esac
+ run rm -rf ~/rpmbuild/SPECS/
+ run mkdir -p ~/rpmbuild/SPECS/
+ run mv *.spec ~/rpmbuild/SPECS/
+ run mv * ~/rpmbuild/SOURCES/
+ run cd -
+ rn rm -rf tmp
+
+ mecab_build_options="--buildroot ${HOME}/rpmbuild/BUILDROOT/${srpm_base}"
+ case "${architecture}" in
+ i*86)
+ run rpmbuild -ba rpmbuild/SPECS/*.spec ${mecab_build_options} \
+ --define "optflags -O2 -g -march=i586"
+ ;;
+ *)
+ run rpmbuild -ba rpmbuild/SPECS/*.spec ${mecab_build_options}
+ ;;
+ esac
+
+ run rpm -Uvh rpmbuild/RPMS/*/*.rpm
+ run mv rpmbuild/RPMS/*/*.rpm "${rpm_dir}/"
+ run mv rpmbuild/SRPMS/*.rpm "${srpm_dir}/"
+}
+
+if ! rpm -q mecab-devel > /dev/null; then
+ run yum install -y wget
+
+ for rpm in mecab-0.996-1.fc20.src.rpm \
+ mecab-ipadic-2.7.0.20070801-8.fc20.1.src.rpm \
+ mecab-jumandic-5.1.20070304-9.fc20.src.rpm; do
+ srpm_base=`echo $rpm | sed -e 's/\.fc20.*//g'`
+ run build_fedora_srpm "${rpm}" "${srpm_base}"
+ done
+fi
+
+# for debug
+# rpmbuild_options="$rpmbuild_options --define 'optflags -O0 -g3'"
+
+cd
+
+run cp /vagrant/tmp/${PACKAGE}-${VERSION}.* rpmbuild/SOURCES/
+run cp /vagrant/tmp/${distribution}/${PACKAGE}.spec rpmbuild/SPECS/
+
+run rpmbuild -ba ${rpmbuild_options} rpmbuild/SPECS/${PACKAGE}.spec
+
+run mv rpmbuild/RPMS/*/* "${rpm_dir}/"
+run mv rpmbuild/SRPMS/* "${srpm_dir}/"
diff --git a/storage/mroonga/vendor/groonga/packages/yum/env.sh.in b/storage/mroonga/vendor/groonga/packages/yum/env.sh.in
new file mode 100644
index 00000000000..3dbe0fd76a5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/yum/env.sh.in
@@ -0,0 +1,19 @@
+PACKAGE=@PACKAGE@
+VERSION=@VERSION@
+DEPENDED_PACKAGES="
+intltool
+gcc-c++
+make
+mecab
+mecab-devel
+zeromq3-devel
+libevent-devel
+python2-devel
+php-devel
+zlib-devel
+lzo-devel
+libedit-devel
+ruby
+tar
+pcre-devel
+"
diff --git a/storage/mroonga/vendor/groonga/packages/yum/patches/mecab-ipadic-provides-mecab-dic.diff b/storage/mroonga/vendor/groonga/packages/yum/patches/mecab-ipadic-provides-mecab-dic.diff
new file mode 100644
index 00000000000..79df8114bbc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/yum/patches/mecab-ipadic-provides-mecab-dic.diff
@@ -0,0 +1,10 @@
+--- mecab-ipadic.spec.orig 2013-01-15 21:57:41.969580098 -0500
++++ mecab-ipadic.spec 2013-01-15 21:58:32.629580230 -0500
+@@ -27,6 +27,7 @@
+
+ BuildRequires: mecab-devel >= %{mecabver}
+ Requires: mecab >= %{mecabver}
++Provides: mecab-dic
+
+ %description
+ MeCab IPA is a dictionary for MeCab using CRF estimation
diff --git a/storage/mroonga/vendor/groonga/packages/yum/patches/mecab-jumandic-provides-mecab-dic.diff b/storage/mroonga/vendor/groonga/packages/yum/patches/mecab-jumandic-provides-mecab-dic.diff
new file mode 100644
index 00000000000..edfb899e6f2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/yum/patches/mecab-jumandic-provides-mecab-dic.diff
@@ -0,0 +1,10 @@
+--- mecab-jumandic.spec.orig 2013-01-15 21:57:48.337580114 -0500
++++ mecab-jumandic.spec 2013-01-15 21:58:47.513580269 -0500
+@@ -20,6 +20,7 @@
+
+ BuildRequires: mecab-devel
+ Requires: mecab
++Provides: mecab-dic
+
+ %description
+ MeCab JUMAN is a dictionary for MeCab using CRF estimation
diff --git a/storage/mroonga/vendor/groonga/packages/yum/sign-rpm.sh b/storage/mroonga/vendor/groonga/packages/yum/sign-rpm.sh
new file mode 100755
index 00000000000..511fb3df0a4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/yum/sign-rpm.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 3 ]; then
+ echo "Usage: $0 GPG_UID DESTINATION DISTRIBUTIONS"
+ echo " e.g.: $0 'F10399C0' repositories/ 'fedora centos'"
+ exit 1
+fi
+
+GPG_UID=$1
+DESTINATION=$2
+DISTRIBUTIONS=$3
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+unsigned_rpms()
+{
+ while read rpm; do
+ rpm --checksig "$rpm" | grep -v 'gpg OK' | cut -d":" -f1
+ done
+}
+
+rpms=""
+for distribution in ${DISTRIBUTIONS}; do
+ rpms="${rpms} $(find ${DESTINATION}${distribution} -name '*.rpm' | unsigned_rpms)"
+done
+
+echo "NOTE: YOU JUST ENTER! YOU DON'T NEED TO INPUT PASSWORD!"
+echo " IT'S JUST FOR rpm COMMAND RESTRICTION!"
+run echo $rpms | xargs rpm \
+ -D "_gpg_name ${GPG_UID}" \
+ -D "_gpg_digest_algo sha1" \
+ -D "__gpg /usr/bin/gpg2" \
+ -D "__gpg_check_password_cmd /bin/true true" \
+ -D "__gpg_sign_cmd %{__gpg} gpg --batch --no-verbose --no-armor %{?_gpg_digest_algo:--digest-algo %{_gpg_digest_algo}} --no-secmem-warning -u \"%{_gpg_name}\" -sbo %{__signature_filename} %{__plaintext_filename}" \
+ --resign
diff --git a/storage/mroonga/vendor/groonga/packages/yum/update-repository.sh b/storage/mroonga/vendor/groonga/packages/yum/update-repository.sh
new file mode 100755
index 00000000000..04058598dce
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/packages/yum/update-repository.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 3 ]; then
+ echo "Usage: $0 GPG_KEY_NAME DESTINATION DISTRIBUTIONS"
+ echo " e.g.: $0 mitler-manager repositories/ 'fedora centos'"
+ exit 1
+fi
+
+GPG_KEY_NAME=$1
+DESTINATION=$2
+DISTRIBUTIONS=$3
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+for distribution in ${DISTRIBUTIONS}; do
+ for dir in ${DESTINATION}${distribution}/*/*; do
+ # "--checksum sha" is for CentOS 5. If we drop CentOS 5 support,
+ # we can remove the option.
+ test -d $dir && run createrepo --checksum sha $dir
+ done;
+
+ run cp $script_base_dir/RPM-GPG-KEY-${GPG_KEY_NAME} \
+ ${DESTINATION}${distribution}/RPM-GPG-KEY-${GPG_KEY_NAME};
+done
diff --git a/storage/mroonga/vendor/groonga/plugins/CMakeLists.txt b/storage/mroonga/vendor/groonga/plugins/CMakeLists.txt
new file mode 100644
index 00000000000..a2a8f288d52
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/CMakeLists.txt
@@ -0,0 +1,20 @@
+# Copyright(C) 2012 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+add_subdirectory(suggest)
+add_subdirectory(tokenizers)
+add_subdirectory(table)
+add_subdirectory(query_expanders)
+add_subdirectory(ruby)
diff --git a/storage/mroonga/vendor/groonga/plugins/Makefile.am b/storage/mroonga/vendor/groonga/plugins/Makefile.am
new file mode 100644
index 00000000000..75a00597e06
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/Makefile.am
@@ -0,0 +1,9 @@
+SUBDIRS = \
+ tokenizers \
+ suggest \
+ table \
+ query_expanders \
+ ruby
+
+EXTRA_DIST = \
+ CMakeLists.txt
diff --git a/storage/mroonga/vendor/groonga/plugins/query_expanders/CMakeLists.txt b/storage/mroonga/vendor/groonga/plugins/query_expanders/CMakeLists.txt
new file mode 100644
index 00000000000..1e2a2c23b09
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/query_expanders/CMakeLists.txt
@@ -0,0 +1,32 @@
+# Copyright(C) 2012-2013 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../lib
+ )
+
+set(QUERY_EXPANDERS_DIR "${GRN_RELATIVE_PLUGINS_DIR}/query_expanders")
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/tsv_sources.am TSV_SOURCES)
+set_source_files_properties(${TSV_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+add_library(tsv_query_expander MODULE ${TSV_SOURCES})
+set_target_properties(tsv_query_expander PROPERTIES
+ PREFIX ""
+ OUTPUT_NAME "tsv")
+target_link_libraries(tsv_query_expander libgroonga)
+if(NOT MRN_GROONGA_BUNDLED)
+ install(TARGETS tsv_query_expander DESTINATION "${QUERY_EXPANDERS_DIR}")
+endif()
diff --git a/storage/mroonga/vendor/groonga/plugins/query_expanders/Makefile.am b/storage/mroonga/vendor/groonga/plugins/query_expanders/Makefile.am
new file mode 100644
index 00000000000..ca26760d332
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/query_expanders/Makefile.am
@@ -0,0 +1,20 @@
+EXTRA_DIST = \
+ CMakeLists.txt
+
+AM_CPPFLAGS = \
+ -I$(top_builddir) \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib
+
+AM_LDFLAGS = \
+ -avoid-version \
+ -module \
+ -no-undefined
+
+LIBS = \
+ $(top_builddir)/lib/libgroonga.la
+
+query_expanders_plugins_LTLIBRARIES =
+query_expanders_plugins_LTLIBRARIES += tsv.la
+
+include tsv_sources.am
diff --git a/storage/mroonga/vendor/groonga/plugins/query_expanders/tsv.c b/storage/mroonga/vendor/groonga/plugins/query_expanders/tsv.c
new file mode 100644
index 00000000000..6b1fc51d6dc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/query_expanders/tsv.c
@@ -0,0 +1,299 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <groonga/plugin.h>
+
+/* groonga's internal headers */
+/* for grn_text_fgets(): We don't want to require stdio.h for groonga.h.
+ What should we do? Should we split header file such as groonga/stdio.h? */
+#include <str.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE__STRNICMP
+# define strncasecmp(s1,s2,n) _strnicmp(s1,s2,n)
+#endif /* HAVE__STRNICMP */
+
+#define MAX_SYNONYM_BYTES 4096
+
+static grn_hash *synonyms = NULL;
+
+#ifdef WIN32
+static char *win32_synonyms_file = NULL;
+const char *
+get_system_synonyms_file(void)
+{
+ if (!win32_synonyms_file) {
+ const char *base_dir;
+ const char *relative_path = GRN_QUERY_EXPANDER_TSV_RELATIVE_SYNONYMS_FILE;
+ char *synonyms_file;
+ char *path;
+ size_t base_dir_length;
+
+ base_dir = grn_plugin_win32_base_dir();
+ base_dir_length = strlen(base_dir);
+ synonyms_file =
+ malloc(base_dir_length + strlen("/") + strlen(relative_path) + 1);
+ strcpy(synonyms_file, base_dir);
+ strcat(synonyms_file, "/");
+ strcat(synonyms_file, relative_path);
+ win32_synonyms_file = synonyms_file;
+ }
+ return win32_synonyms_file;
+}
+
+#else /* WIN32 */
+const char *
+get_system_synonyms_file(void)
+{
+ return GRN_QUERY_EXPANDER_TSV_SYNONYMS_FILE;
+}
+#endif /* WIN32 */
+
+static inline grn_bool
+is_comment_mark(char character)
+{
+ return character == '#';
+}
+
+static inline grn_encoding
+detect_coding_part(grn_ctx *ctx, const char *line, size_t line_length)
+{
+ grn_encoding encoding = GRN_ENC_NONE;
+ grn_obj null_terminated_line_buffer;
+ const char *c_line;
+ const char *coding_part_keyword = "coding: ";
+ const char *coding_part;
+ const char *encoding_name;
+
+ GRN_TEXT_INIT(&null_terminated_line_buffer, 0);
+ GRN_TEXT_PUT(ctx, &null_terminated_line_buffer, line, line_length);
+ GRN_TEXT_PUTC(ctx, &null_terminated_line_buffer, '\0');
+
+ c_line = GRN_TEXT_VALUE(&null_terminated_line_buffer);
+ coding_part = strstr(c_line, coding_part_keyword);
+ if (coding_part) {
+ encoding_name = coding_part + strlen(coding_part_keyword);
+ if (strncasecmp(encoding_name, "utf-8", strlen("utf-8")) == 0 ||
+ strncasecmp(encoding_name, "utf8", strlen("utf8")) == 0) {
+ encoding = GRN_ENC_UTF8;
+ } else if (strncasecmp(encoding_name, "sjis", strlen("sjis")) == 0 ||
+ strncasecmp(encoding_name, "Shift_JIS", strlen("Shift_JIS")) == 0) {
+ encoding = GRN_ENC_SJIS;
+ } else if (strncasecmp(encoding_name, "EUC-JP", strlen("EUC-JP")) == 0 ||
+ strncasecmp(encoding_name, "euc_jp", strlen("euc_jp")) == 0) {
+ encoding = GRN_ENC_EUC_JP;
+ } else if (strncasecmp(encoding_name, "latin1", strlen("latin1")) == 0) {
+ encoding = GRN_ENC_LATIN1;
+ } else if (strncasecmp(encoding_name, "KOI8-R", strlen("KOI8-R")) == 0 ||
+ strncasecmp(encoding_name, "koi8r", strlen("koi8r")) == 0) {
+ encoding = GRN_ENC_KOI8R;
+ }
+ } else {
+ encoding = ctx->encoding;
+ }
+ GRN_OBJ_FIN(ctx, &null_terminated_line_buffer);
+
+ return encoding;
+}
+
+static inline grn_encoding
+guess_encoding(grn_ctx *ctx, const char **line, size_t *line_length)
+{
+ const char bom[] = {0xef, 0xbb, 0xbf};
+ size_t bom_length = sizeof(bom);
+
+ if (*line_length >= bom_length && memcmp(*line, bom, bom_length) == 0) {
+ *line += bom_length;
+ *line_length -= bom_length;
+ return GRN_ENC_UTF8;
+ }
+
+ if (!is_comment_mark((*line)[0])) {
+ return ctx->encoding;
+ }
+
+ return detect_coding_part(ctx, (*line) + 1, (*line_length) - 1);
+}
+
+static void
+parse_synonyms_file_line(grn_ctx *ctx, const char *line, int line_length,
+ grn_obj *key, grn_obj *value)
+{
+ size_t i = 0;
+
+ if (is_comment_mark(line[i])) {
+ return;
+ }
+
+ while (i < line_length) {
+ char character = line[i];
+ i++;
+ if (character == '\t') {
+ break;
+ }
+ GRN_TEXT_PUTC(ctx, key, character);
+ }
+
+ if (i == line_length) {
+ return;
+ }
+
+ GRN_TEXT_PUTS(ctx, value, "((");
+ while (i < line_length) {
+ char character = line[i];
+ i++;
+ if (character == '\t') {
+ GRN_TEXT_PUTS(ctx, value, ") OR (");
+ } else {
+ GRN_TEXT_PUTC(ctx, value, character);
+ }
+ }
+ GRN_TEXT_PUTS(ctx, value, "))");
+
+ {
+ grn_id id;
+ void *value_location = NULL;
+
+ id = grn_hash_add(ctx, synonyms, GRN_TEXT_VALUE(key), GRN_TEXT_LEN(key),
+ &value_location, NULL);
+ if (id == GRN_ID_NIL) {
+ GRN_PLUGIN_LOG(ctx, GRN_LOG_WARNING,
+ "[plugin][query-expander][tsv] "
+ "failed to register key: <%.*s>",
+ (int)GRN_TEXT_LEN(key), GRN_TEXT_VALUE(key));
+ return;
+ }
+
+ grn_bulk_truncate(ctx, value, MAX_SYNONYM_BYTES - 1);
+ GRN_TEXT_PUTC(ctx, value, '\0');
+ memcpy(value_location, GRN_TEXT_VALUE(value), MAX_SYNONYM_BYTES);
+ }
+}
+
+static void
+load_synonyms(grn_ctx *ctx)
+{
+ const char *path;
+ FILE *file;
+ int number_of_lines;
+ grn_encoding encoding;
+ grn_obj line, key, value;
+
+ path = getenv("GRN_QUERY_EXPANDER_TSV_SYNONYMS_FILE");
+ if (!path) {
+ path = get_system_synonyms_file();
+ }
+ file = fopen(path, "r");
+ if (!file) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "[plugin][query-expander][tsv] "
+ "synonyms file doesn't exist: <%s>",
+ path);
+ return;
+ }
+
+ GRN_TEXT_INIT(&line, 0);
+ GRN_TEXT_INIT(&key, 0);
+ GRN_TEXT_INIT(&value, 0);
+ grn_bulk_reserve(ctx, &value, MAX_SYNONYM_BYTES);
+ number_of_lines = 0;
+ while (grn_text_fgets(ctx, &line, file) == GRN_SUCCESS) {
+ const char *line_value = GRN_TEXT_VALUE(&line);
+ size_t line_length = GRN_TEXT_LEN(&line);
+
+ number_of_lines++;
+ if (number_of_lines == 1) {
+ encoding = guess_encoding(ctx, &line_value, &line_length);
+ }
+ GRN_BULK_REWIND(&key);
+ GRN_BULK_REWIND(&value);
+ parse_synonyms_file_line(ctx, line_value, line_length, &key, &value);
+ GRN_BULK_REWIND(&line);
+ }
+ GRN_OBJ_FIN(ctx, &line);
+ GRN_OBJ_FIN(ctx, &key);
+ GRN_OBJ_FIN(ctx, &value);
+
+ fclose(file);
+}
+
+static grn_obj *
+func_query_expander_tsv(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data)
+{
+ grn_rc rc = GRN_END_OF_DATA;
+ grn_id id;
+ grn_obj *term, *expanded_term;
+ void *value;
+ grn_obj *rc_object;
+
+ term = args[0];
+ expanded_term = args[1];
+ id = grn_hash_get(ctx, synonyms,
+ GRN_TEXT_VALUE(term), GRN_TEXT_LEN(term),
+ &value);
+ if (id != GRN_ID_NIL) {
+ const char *query = value;
+ GRN_TEXT_PUTS(ctx, expanded_term, query);
+ rc = GRN_SUCCESS;
+ }
+
+ rc_object = grn_plugin_proc_alloc(ctx, user_data, GRN_DB_INT32, 0);
+ if (rc_object) {
+ GRN_INT32_SET(ctx, rc_object, rc);
+ }
+
+ return rc_object;
+}
+
+grn_rc
+GRN_PLUGIN_INIT(grn_ctx *ctx)
+{
+ if (!synonyms) {
+ synonyms = grn_hash_create(ctx, NULL,
+ GRN_TABLE_MAX_KEY_SIZE,
+ MAX_SYNONYM_BYTES,
+ GRN_OBJ_TABLE_HASH_KEY | GRN_OBJ_KEY_VAR_SIZE);
+ if (!synonyms) {
+ return ctx->rc;
+ }
+ load_synonyms(ctx);
+ }
+ return ctx->rc;
+}
+
+grn_rc
+GRN_PLUGIN_REGISTER(grn_ctx *ctx)
+{
+ grn_proc_create(ctx, "QueryExpanderTSV", strlen("QueryExpanderTSV"),
+ GRN_PROC_FUNCTION,
+ func_query_expander_tsv, NULL, NULL,
+ 0, NULL);
+ return GRN_SUCCESS;
+}
+
+grn_rc
+GRN_PLUGIN_FIN(grn_ctx *ctx)
+{
+ if (synonyms) {
+ grn_hash_close(ctx, synonyms);
+ synonyms = NULL;
+ }
+ return GRN_SUCCESS;
+}
diff --git a/storage/mroonga/vendor/groonga/plugins/query_expanders/tsv_sources.am b/storage/mroonga/vendor/groonga/plugins/query_expanders/tsv_sources.am
new file mode 100644
index 00000000000..f1bdabede85
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/query_expanders/tsv_sources.am
@@ -0,0 +1,2 @@
+tsv_la_SOURCES = \
+ tsv.c
diff --git a/storage/mroonga/vendor/groonga/plugins/ruby/CMakeLists.txt b/storage/mroonga/vendor/groonga/plugins/ruby/CMakeLists.txt
new file mode 100644
index 00000000000..d82b154098c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/ruby/CMakeLists.txt
@@ -0,0 +1,44 @@
+# Copyright(C) 2013 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../lib
+ ${MRUBY_INCLUDE_DIRS})
+
+if(GRN_WITH_MRUBY)
+ set(GRN_RELATIVE_RUBY_PLUGINS_DIR "${GRN_RELATIVE_PLUGINS_DIR}/ruby")
+
+ read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/eval_sources.am RUBY_EVAL_SOURCES)
+ add_library(eval MODULE ${RUBY_EVAL_SOURCES})
+ set_source_files_properties(${RUBY_EVAL_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+ set_target_properties(eval PROPERTIES PREFIX "")
+ target_link_libraries(eval libgroonga)
+ if(NOT MRN_GROONGA_BUNDLED)
+ install(TARGETS eval DESTINATION "${GRN_RELATIVE_RUBY_PLUGINS_DIR}")
+ endif()
+
+ read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/load_sources.am RUBY_LOAD_SOURCES)
+ add_library(load MODULE ${RUBY_LOAD_SOURCES})
+ set_source_files_properties(${RUBY_LOAD_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+ set_target_properties(load PROPERTIES PREFIX "")
+ target_link_libraries(load libgroonga)
+ if(NOT MRN_GROONGA_BUNDLED)
+ install(TARGETS load DESTINATION "${GRN_RELATIVE_RUBY_PLUGINS_DIR}")
+ endif()
+endif()
diff --git a/storage/mroonga/vendor/groonga/plugins/ruby/Makefile.am b/storage/mroonga/vendor/groonga/plugins/ruby/Makefile.am
new file mode 100644
index 00000000000..381fb47160b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/ruby/Makefile.am
@@ -0,0 +1,29 @@
+EXTRA_DIST = \
+ CMakeLists.txt
+
+AM_CFLAGS = \
+ $(MESSAGE_PACK_CFLAGS) \
+ $(MRUBY_CFLAGS)
+
+AM_CPPFLAGS = \
+ -I$(top_builddir) \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib
+
+AM_LDFLAGS = \
+ -avoid-version \
+ -module \
+ -no-undefined
+
+LIBS = \
+ $(top_builddir)/lib/libgroonga.la \
+ $(MESSAGE_PACK_LIBS)
+
+if WITH_MRUBY
+ruby_plugins_LTLIBRARIES = \
+ eval.la \
+ load.la
+endif
+
+include eval_sources.am
+include load_sources.am
diff --git a/storage/mroonga/vendor/groonga/plugins/ruby/eval.c b/storage/mroonga/vendor/groonga/plugins/ruby/eval.c
new file mode 100644
index 00000000000..ad1e7948249
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/ruby/eval.c
@@ -0,0 +1,64 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "ruby_plugin.h"
+
+static grn_obj *
+command_ruby_eval(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data)
+{
+ mrb_state *mrb = ctx->impl->mrb.state;
+ grn_obj *script;
+ mrb_value result;
+
+ script = VAR(0);
+ switch (script->header.domain) {
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ break;
+ default :
+ {
+ grn_obj inspected;
+ GRN_TEXT_INIT(&inspected, 0);
+ grn_inspect(ctx, &inspected, script);
+ ERR(GRN_INVALID_ARGUMENT, "script must be a string: <%.*s>",
+ (int)GRN_TEXT_LEN(&inspected), GRN_TEXT_VALUE(&inspected));
+ GRN_OBJ_FIN(ctx, &inspected);
+ return NULL;
+ }
+ break;
+ }
+
+ mrb->exc = NULL;
+ result = grn_mrb_eval(ctx, GRN_TEXT_VALUE(script), GRN_TEXT_LEN(script));
+ output_result(ctx, result);
+
+ return NULL;
+}
+
+grn_rc
+GRN_PLUGIN_REGISTER(grn_ctx *ctx)
+{
+ grn_expr_var vars[1];
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "script", -1);
+ grn_plugin_command_create(ctx, "ruby_eval", -1, command_ruby_eval, 1, vars);
+
+ return ctx->rc;
+}
diff --git a/storage/mroonga/vendor/groonga/plugins/ruby/eval_sources.am b/storage/mroonga/vendor/groonga/plugins/ruby/eval_sources.am
new file mode 100644
index 00000000000..08543e43fb4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/ruby/eval_sources.am
@@ -0,0 +1,3 @@
+eval_la_SOURCES = \
+ ruby_plugin.h \
+ eval.c
diff --git a/storage/mroonga/vendor/groonga/plugins/ruby/load.c b/storage/mroonga/vendor/groonga/plugins/ruby/load.c
new file mode 100644
index 00000000000..a4e60acc357
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/ruby/load.c
@@ -0,0 +1,63 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "ruby_plugin.h"
+
+static grn_obj *
+command_ruby_load(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data)
+{
+ grn_obj *path;
+ mrb_value result;
+
+ path = VAR(0);
+ switch (path->header.domain) {
+ case GRN_DB_SHORT_TEXT :
+ case GRN_DB_TEXT :
+ case GRN_DB_LONG_TEXT :
+ break;
+ default :
+ {
+ grn_obj inspected;
+ GRN_TEXT_INIT(&inspected, 0);
+ grn_inspect(ctx, &inspected, path);
+ ERR(GRN_INVALID_ARGUMENT, "path must be a string: <%.*s>",
+ (int)GRN_TEXT_LEN(&inspected), GRN_TEXT_VALUE(&inspected));
+ GRN_OBJ_FIN(ctx, &inspected);
+ return NULL;
+ }
+ break;
+ }
+
+ GRN_TEXT_PUTC(ctx, path, '\0');
+ result = grn_mrb_load(ctx, GRN_TEXT_VALUE(path));
+ output_result(ctx, result);
+
+ return NULL;
+}
+
+grn_rc
+GRN_PLUGIN_REGISTER(grn_ctx *ctx)
+{
+ grn_expr_var vars[1];
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "path", -1);
+ grn_plugin_command_create(ctx, "ruby_load", -1, command_ruby_load, 1, vars);
+
+ return ctx->rc;
+}
diff --git a/storage/mroonga/vendor/groonga/plugins/ruby/load_sources.am b/storage/mroonga/vendor/groonga/plugins/ruby/load_sources.am
new file mode 100644
index 00000000000..d1cce258caa
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/ruby/load_sources.am
@@ -0,0 +1,3 @@
+load_la_SOURCES = \
+ ruby_plugin.h \
+ load.c
diff --git a/storage/mroonga/vendor/groonga/plugins/ruby/ruby_plugin.h b/storage/mroonga/vendor/groonga/plugins/ruby/ruby_plugin.h
new file mode 100644
index 00000000000..5314ea68fe5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/ruby/ruby_plugin.h
@@ -0,0 +1,76 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <mrb.h>
+#include <output.h>
+#include <db.h>
+#include <ctx_impl.h>
+#include <util.h>
+
+#include <groonga/plugin.h>
+
+#include <mruby.h>
+
+#define VAR GRN_PROC_GET_VAR_BY_OFFSET
+
+static void
+output_result(grn_ctx *ctx, mrb_value result)
+{
+ mrb_state *mrb = ctx->impl->mrb.state;
+
+ GRN_OUTPUT_MAP_OPEN("result", 1);
+ if (mrb->exc) {
+ mrb_value mrb_message;
+ grn_obj grn_message;
+ GRN_OUTPUT_CSTR("exception");
+ GRN_OUTPUT_MAP_OPEN("exception", 1);
+ GRN_OUTPUT_CSTR("message");
+ mrb_message = mrb_funcall(mrb, mrb_obj_value(mrb->exc), "message", 0);
+ GRN_VOID_INIT(&grn_message);
+ if (grn_mrb_to_grn(ctx, mrb_message, &grn_message) == GRN_SUCCESS) {
+ GRN_OUTPUT_OBJ(&grn_message, NULL);
+ } else {
+ GRN_OUTPUT_CSTR("unsupported message type");
+ }
+ grn_obj_unlink(ctx, &grn_message);
+ GRN_OUTPUT_MAP_CLOSE();
+ } else {
+ grn_obj grn_result;
+ GRN_OUTPUT_CSTR("value");
+ GRN_VOID_INIT(&grn_result);
+ if (grn_mrb_to_grn(ctx, result, &grn_result) == GRN_SUCCESS) {
+ GRN_OUTPUT_OBJ(&grn_result, NULL);
+ } else {
+ GRN_OUTPUT_CSTR("unsupported return value");
+ }
+ grn_obj_unlink(ctx, &grn_result);
+ }
+ GRN_OUTPUT_MAP_CLOSE();
+}
+
+grn_rc
+GRN_PLUGIN_INIT(grn_ctx *ctx)
+{
+ return GRN_SUCCESS;
+}
+
+grn_rc
+GRN_PLUGIN_FIN(grn_ctx *ctx)
+{
+ return GRN_SUCCESS;
+}
diff --git a/storage/mroonga/vendor/groonga/plugins/suggest/CMakeLists.txt b/storage/mroonga/vendor/groonga/plugins/suggest/CMakeLists.txt
new file mode 100644
index 00000000000..72b86362c55
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/suggest/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright(C) 2012-2013 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../lib
+ ${MRUBY_INCLUDE_DIRS})
+
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/sources.am SUGGEST_SOURCES)
+add_library(suggest MODULE ${SUGGEST_SOURCES})
+set_source_files_properties(${SUGGEST_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+set_target_properties(suggest PROPERTIES PREFIX "")
+target_link_libraries(suggest libgroonga)
+if(NOT MRN_GROONGA_BUNDLED)
+ install(TARGETS suggest DESTINATION "${GRN_RELATIVE_PLUGINS_DIR}/suggest")
+endif()
diff --git a/storage/mroonga/vendor/groonga/plugins/suggest/Makefile.am b/storage/mroonga/vendor/groonga/plugins/suggest/Makefile.am
new file mode 100644
index 00000000000..7f321b6c482
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/suggest/Makefile.am
@@ -0,0 +1,24 @@
+EXTRA_DIST = \
+ CMakeLists.txt
+
+AM_CFLAGS = \
+ $(MESSAGE_PACK_CFLAGS) \
+ $(MRUBY_CFLAGS)
+
+AM_CPPFLAGS = \
+ -I$(top_builddir) \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib
+
+AM_LDFLAGS = \
+ -avoid-version \
+ -module \
+ -no-undefined
+
+LIBS = \
+ $(top_builddir)/lib/libgroonga.la \
+ $(MESSAGE_PACK_LIBS)
+
+suggest_plugins_LTLIBRARIES = suggest.la
+
+include sources.am
diff --git a/storage/mroonga/vendor/groonga/plugins/suggest/sources.am b/storage/mroonga/vendor/groonga/plugins/suggest/sources.am
new file mode 100644
index 00000000000..798a431a8e8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/suggest/sources.am
@@ -0,0 +1,2 @@
+suggest_la_SOURCES = \
+ suggest.c
diff --git a/storage/mroonga/vendor/groonga/plugins/suggest/suggest.c b/storage/mroonga/vendor/groonga/plugins/suggest/suggest.c
new file mode 100644
index 00000000000..ea7b6adbf6b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/suggest/suggest.c
@@ -0,0 +1,1022 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/* Copyright(C) 2010-2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "ctx.h"
+#include "db.h"
+#include "ii.h"
+#include "token.h"
+#include "output.h"
+#include <groonga/plugin.h>
+#include <string.h>
+
+#ifdef HAVE__STRNICMP
+# define strncasecmp(s1,s2,n) _strnicmp(s1,s2,n)
+#endif /* HAVE__STRNICMP */
+
+#define VAR GRN_PROC_GET_VAR_BY_OFFSET
+#define CONST_STR_LEN(x) x, x ? sizeof(x) - 1 : 0
+#define TEXT_VALUE_LEN(x) GRN_TEXT_VALUE(x), GRN_TEXT_LEN(x)
+
+#define MIN_LEARN_DISTANCE (60 * GRN_TIME_USEC_PER_SEC)
+
+#define COMPLETE 1
+#define CORRECT 2
+#define SUGGEST 4
+
+typedef enum {
+ GRN_SUGGEST_SEARCH_YES,
+ GRN_SUGGEST_SEARCH_NO,
+ GRN_SUGGEST_SEARCH_AUTO
+} grn_suggest_search_mode;
+
+typedef struct {
+ grn_obj *post_event;
+ grn_obj *post_type;
+ grn_obj *post_item;
+ grn_obj *seq;
+ grn_obj *post_time;
+ grn_obj *pairs;
+
+ int learn_distance_in_seconds;
+
+ grn_id post_event_id;
+ grn_id post_type_id;
+ grn_id post_item_id;
+ grn_id seq_id;
+ int64_t post_time_value;
+
+ grn_obj *seqs;
+ grn_obj *seqs_events;
+ grn_obj *events;
+ grn_obj *events_item;
+ grn_obj *events_type;
+ grn_obj *events_time;
+ grn_obj *event_types;
+ grn_obj *items;
+ grn_obj *items_freq;
+ grn_obj *items_freq2;
+ grn_obj *items_last;
+ grn_obj *pairs_pre;
+ grn_obj *pairs_post;
+ grn_obj *pairs_freq0;
+ grn_obj *pairs_freq1;
+ grn_obj *pairs_freq2;
+
+ grn_obj dataset_name;
+
+ grn_obj *configuration;
+
+ grn_obj weight;
+ grn_obj pre_events;
+
+ uint64_t key_prefix;
+ grn_obj pre_item;
+} grn_suggest_learner;
+
+static int
+grn_parse_suggest_types(grn_obj *text)
+{
+ const char *nptr = GRN_TEXT_VALUE(text);
+ const char *end = GRN_BULK_CURR(text);
+ int types = 0;
+ while (nptr < end) {
+ if (*nptr == '|') {
+ nptr += 1;
+ continue;
+ }
+ {
+ const char string[] = "complete";
+ size_t length = sizeof(string) - 1;
+ if (nptr + length <= end && memcmp(nptr, string, length) == 0) {
+ types |= COMPLETE;
+ nptr += length;
+ continue;
+ }
+ }
+ {
+ const char string[] = "correct";
+ size_t length = sizeof(string) - 1;
+ if (nptr + length <= end && memcmp(nptr, string, length) == 0) {
+ types |= CORRECT;
+ nptr += length;
+ continue;
+ }
+ }
+ {
+ const char string[] = "suggest";
+ size_t length = sizeof(string) - 1;
+ if (nptr + length <= end && memcmp(nptr, string, length) == 0) {
+ types |= SUGGEST;
+ nptr += length;
+ continue;
+ }
+ }
+ break;
+ }
+ return types;
+}
+
+static int32_t
+cooccurrence_search(grn_ctx *ctx, grn_obj *items, grn_obj *items_boost, grn_id id,
+ grn_obj *res, int query_type, int frequency_threshold,
+ double conditional_probability_threshold)
+{
+ int32_t max_score = 0;
+ if (id) {
+ grn_ii_cursor *c;
+ grn_obj *co = grn_obj_column(ctx, items, CONST_STR_LEN("co"));
+ grn_obj *pairs = grn_ctx_at(ctx, grn_obj_get_range(ctx, co));
+ grn_obj *items_freq = grn_obj_column(ctx, items, CONST_STR_LEN("freq"));
+ grn_obj *items_freq2 = grn_obj_column(ctx, items, CONST_STR_LEN("freq2"));
+ grn_obj *pairs_freq, *pairs_post = grn_obj_column(ctx, pairs, CONST_STR_LEN("post"));
+ switch (query_type) {
+ case COMPLETE :
+ pairs_freq = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq0"));
+ break;
+ case CORRECT :
+ pairs_freq = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq1"));
+ break;
+ case SUGGEST :
+ pairs_freq = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq2"));
+ break;
+ default :
+ return max_score;
+ }
+ if ((c = grn_ii_cursor_open(ctx, (grn_ii *)co, id, GRN_ID_NIL, GRN_ID_MAX,
+ ((grn_ii *)co)->n_elements - 1, 0))) {
+ grn_ii_posting *p;
+ grn_obj post, pair_freq, item_freq, item_freq2, item_boost;
+ GRN_RECORD_INIT(&post, 0, grn_obj_id(ctx, items));
+ GRN_INT32_INIT(&pair_freq, 0);
+ GRN_INT32_INIT(&item_freq, 0);
+ GRN_INT32_INIT(&item_freq2, 0);
+ GRN_INT32_INIT(&item_boost, 0);
+ while ((p = grn_ii_cursor_next(ctx, c))) {
+ grn_id post_id;
+ int pfreq, ifreq, ifreq2, boost;
+ double conditional_probability;
+ GRN_BULK_REWIND(&post);
+ GRN_BULK_REWIND(&pair_freq);
+ GRN_BULK_REWIND(&item_freq);
+ GRN_BULK_REWIND(&item_freq2);
+ GRN_BULK_REWIND(&item_boost);
+ grn_obj_get_value(ctx, pairs_post, p->rid, &post);
+ grn_obj_get_value(ctx, pairs_freq, p->rid, &pair_freq);
+ post_id = GRN_RECORD_VALUE(&post);
+ grn_obj_get_value(ctx, items_freq, post_id, &item_freq);
+ grn_obj_get_value(ctx, items_freq2, post_id, &item_freq2);
+ grn_obj_get_value(ctx, items_boost, post_id, &item_boost);
+ pfreq = GRN_INT32_VALUE(&pair_freq);
+ ifreq = GRN_INT32_VALUE(&item_freq);
+ ifreq2 = GRN_INT32_VALUE(&item_freq2);
+ if (ifreq2 > 0) {
+ conditional_probability = (double)pfreq / (double)ifreq2;
+ } else {
+ conditional_probability = 0.0;
+ }
+ boost = GRN_INT32_VALUE(&item_boost);
+ if (pfreq >= frequency_threshold && ifreq >= frequency_threshold &&
+ conditional_probability >= conditional_probability_threshold &&
+ boost >= 0) {
+ grn_rset_recinfo *ri;
+ void *value;
+ int32_t score = pfreq;
+ int added;
+ if (max_score < score + boost) { max_score = score + boost; }
+ /* put any formula if desired */
+ if (grn_hash_add(ctx, (grn_hash *)res,
+ &post_id, sizeof(grn_id), &value, &added)) {
+ ri = value;
+ ri->score += score;
+ if (added) {
+ ri->score += boost;
+ }
+ }
+ }
+ }
+ GRN_OBJ_FIN(ctx, &post);
+ GRN_OBJ_FIN(ctx, &pair_freq);
+ GRN_OBJ_FIN(ctx, &item_freq);
+ GRN_OBJ_FIN(ctx, &item_freq2);
+ GRN_OBJ_FIN(ctx, &item_boost);
+ grn_ii_cursor_close(ctx, c);
+ }
+ }
+ return max_score;
+}
+
+#define DEFAULT_LIMIT 10
+#define DEFAULT_SORTBY "-_score"
+#define DEFAULT_OUTPUT_COLUMNS "_key,_score"
+#define DEFAULT_FREQUENCY_THRESHOLD 100
+#define DEFAULT_CONDITIONAL_PROBABILITY_THRESHOLD 0.2
+
+static void
+output(grn_ctx *ctx, grn_obj *table, grn_obj *res, grn_id tid,
+ grn_obj *sortby, grn_obj *output_columns, int offset, int limit)
+{
+ grn_obj *sorted;
+ if ((sorted = grn_table_create(ctx, NULL, 0, NULL, GRN_OBJ_TABLE_NO_KEY, NULL, res))) {
+ uint32_t nkeys;
+ grn_obj_format format;
+ grn_table_sort_key *keys;
+ const char *sortby_val = GRN_TEXT_VALUE(sortby);
+ unsigned int sortby_len = GRN_TEXT_LEN(sortby);
+ const char *oc_val = GRN_TEXT_VALUE(output_columns);
+ unsigned int oc_len = GRN_TEXT_LEN(output_columns);
+ if (!sortby_val || !sortby_len) {
+ sortby_val = DEFAULT_SORTBY;
+ sortby_len = sizeof(DEFAULT_SORTBY) - 1;
+ }
+ if (!oc_val || !oc_len) {
+ oc_val = DEFAULT_OUTPUT_COLUMNS;
+ oc_len = sizeof(DEFAULT_OUTPUT_COLUMNS) - 1;
+ }
+ if ((keys = grn_table_sort_key_from_str(ctx, sortby_val, sortby_len, res, &nkeys))) {
+ grn_table_sort(ctx, res, offset, limit, sorted, keys, nkeys);
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
+ ":", "sort(%d)", limit);
+ GRN_OBJ_FORMAT_INIT(&format, grn_table_size(ctx, res), 0, limit, offset);
+ format.flags =
+ GRN_OBJ_FORMAT_WITH_COLUMN_NAMES|
+ GRN_OBJ_FORMAT_XML_ELEMENT_RESULTSET;
+ grn_obj_columns(ctx, sorted, oc_val, oc_len, &format.columns);
+ GRN_OUTPUT_OBJ(sorted, &format);
+ GRN_OBJ_FORMAT_FIN(ctx, &format);
+ grn_table_sort_key_close(ctx, keys, nkeys);
+ }
+ grn_obj_unlink(ctx, sorted);
+ } else {
+ ERR(GRN_UNKNOWN_ERROR, "cannot create temporary sort table.");
+ }
+}
+
+static inline void
+complete_add_item(grn_ctx *ctx, grn_id id, grn_obj *res, int frequency_threshold,
+ grn_obj *items_freq, grn_obj *items_boost,
+ grn_obj *item_freq, grn_obj *item_boost)
+{
+ GRN_BULK_REWIND(item_freq);
+ GRN_BULK_REWIND(item_boost);
+ grn_obj_get_value(ctx, items_freq, id, item_freq);
+ grn_obj_get_value(ctx, items_boost, id, item_boost);
+ if (GRN_INT32_VALUE(item_boost) >= 0) {
+ int32_t score;
+ score = 1 +
+ GRN_INT32_VALUE(item_freq) +
+ GRN_INT32_VALUE(item_boost);
+ if (score >= frequency_threshold) {
+ void *value;
+ if (grn_hash_add(ctx, (grn_hash *)res, &id, sizeof(grn_id),
+ &value, NULL)) {
+ grn_rset_recinfo *ri;
+ ri = value;
+ ri->score += score;
+ }
+ }
+ }
+}
+
+static void
+complete(grn_ctx *ctx, grn_obj *items, grn_obj *items_boost, grn_obj *col,
+ grn_obj *query, grn_obj *sortby,
+ grn_obj *output_columns, int offset, int limit,
+ int frequency_threshold, double conditional_probability_threshold,
+ grn_suggest_search_mode prefix_search_mode)
+{
+ grn_obj *res;
+ grn_obj *items_freq = grn_obj_column(ctx, items, CONST_STR_LEN("freq"));
+ grn_obj item_freq, item_boost;
+ GRN_INT32_INIT(&item_freq, 0);
+ GRN_INT32_INIT(&item_boost, 0);
+ if ((res = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, items, NULL))) {
+ grn_id tid = grn_table_get(ctx, items, TEXT_VALUE_LEN(query));
+ if (GRN_TEXT_LEN(query)) {
+ grn_table_cursor *cur;
+ /* RK search + prefix search */
+ grn_obj *index;
+ /* FIXME: support index selection */
+ if (grn_column_index(ctx, col, GRN_OP_PREFIX, &index, 1, NULL)) {
+ if ((cur = grn_table_cursor_open(ctx, grn_ctx_at(ctx, index->header.domain),
+ GRN_TEXT_VALUE(query),
+ GRN_TEXT_LEN(query),
+ NULL, 0, 0, -1,
+ GRN_CURSOR_PREFIX|GRN_CURSOR_RK))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next(ctx, cur))) {
+ grn_ii_cursor *icur;
+ if ((icur = grn_ii_cursor_open(ctx, (grn_ii *)index, id,
+ GRN_ID_NIL, GRN_ID_MAX, 1, 0))) {
+ grn_ii_posting *p;
+ while ((p = grn_ii_cursor_next(ctx, icur))) {
+ complete_add_item(ctx, p->rid, res, frequency_threshold,
+ items_freq, items_boost,
+ &item_freq, &item_boost);
+ }
+ grn_ii_cursor_close(ctx, icur);
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ } else {
+ ERR(GRN_UNKNOWN_ERROR, "cannot open cursor for prefix RK search.");
+ }
+ } else {
+ ERR(GRN_UNKNOWN_ERROR, "cannot find index for prefix RK search.");
+ }
+ cooccurrence_search(ctx, items, items_boost, tid, res, COMPLETE,
+ frequency_threshold,
+ conditional_probability_threshold);
+ if (((prefix_search_mode == GRN_SUGGEST_SEARCH_YES) ||
+ (prefix_search_mode == GRN_SUGGEST_SEARCH_AUTO &&
+ !grn_table_size(ctx, res))) &&
+ (cur = grn_table_cursor_open(ctx, items,
+ GRN_TEXT_VALUE(query),
+ GRN_TEXT_LEN(query),
+ NULL, 0, 0, -1, GRN_CURSOR_PREFIX))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next(ctx, cur))) {
+ complete_add_item(ctx, id, res, frequency_threshold,
+ items_freq, items_boost, &item_freq, &item_boost);
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+ }
+ output(ctx, items, res, tid, sortby, output_columns, offset, limit);
+ grn_obj_close(ctx, res);
+ } else {
+ ERR(GRN_UNKNOWN_ERROR, "cannot create temporary table.");
+ }
+ GRN_OBJ_FIN(ctx, &item_boost);
+ GRN_OBJ_FIN(ctx, &item_freq);
+}
+
+static void
+correct(grn_ctx *ctx, grn_obj *items, grn_obj *items_boost,
+ grn_obj *query, grn_obj *sortby,
+ grn_obj *output_columns, int offset, int limit,
+ int frequency_threshold, double conditional_probability_threshold,
+ grn_suggest_search_mode similar_search_mode)
+{
+ grn_obj *res;
+ grn_obj *items_freq2 = grn_obj_column(ctx, items, CONST_STR_LEN("freq2"));
+ grn_obj item_freq2, item_boost;
+ GRN_INT32_INIT(&item_freq2, 0);
+ GRN_INT32_INIT(&item_boost, 0);
+ if ((res = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, items, NULL))) {
+ grn_id tid = grn_table_get(ctx, items, TEXT_VALUE_LEN(query));
+ int32_t max_score;
+ max_score = cooccurrence_search(ctx, items, items_boost, tid, res, CORRECT,
+ frequency_threshold,
+ conditional_probability_threshold);
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SCORE,
+ ":", "cooccur(%d)", max_score);
+ if (GRN_TEXT_LEN(query) &&
+ ((similar_search_mode == GRN_SUGGEST_SEARCH_YES) ||
+ (similar_search_mode == GRN_SUGGEST_SEARCH_AUTO &&
+ max_score < frequency_threshold))) {
+ grn_obj *key, *index;
+ if ((key = grn_obj_column(ctx, items,
+ GRN_COLUMN_NAME_KEY,
+ GRN_COLUMN_NAME_KEY_LEN))) {
+ if (grn_column_index(ctx, key, GRN_OP_MATCH, &index, 1, NULL)) {
+ grn_select_optarg optarg;
+ memset(&optarg, 0, sizeof(grn_select_optarg));
+ optarg.mode = GRN_OP_SIMILAR;
+ optarg.similarity_threshold = 0;
+ optarg.max_size = 2;
+ grn_ii_select(ctx, (grn_ii *)index, TEXT_VALUE_LEN(query),
+ (grn_hash *)res, GRN_OP_OR, &optarg);
+ grn_obj_unlink(ctx, index);
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
+ ":", "similar(%d)", grn_table_size(ctx, res));
+ {
+ grn_hash_cursor *hc = grn_hash_cursor_open(ctx, (grn_hash *)res, NULL,
+ 0, NULL, 0, 0, -1, 0);
+ if (hc) {
+ while (grn_hash_cursor_next(ctx, hc)) {
+ void *key, *value;
+ if (grn_hash_cursor_get_key_value(ctx, hc, &key, NULL, &value)) {
+ grn_id *rp;
+ rp = key;
+ GRN_BULK_REWIND(&item_freq2);
+ GRN_BULK_REWIND(&item_boost);
+ grn_obj_get_value(ctx, items_freq2, *rp, &item_freq2);
+ grn_obj_get_value(ctx, items_boost, *rp, &item_boost);
+ if (GRN_INT32_VALUE(&item_boost) >= 0) {
+ int32_t score;
+ grn_rset_recinfo *ri;
+ score = 1 +
+ (GRN_INT32_VALUE(&item_freq2) >> 4) +
+ GRN_INT32_VALUE(&item_boost);
+ ri = value;
+ ri->score += score;
+ if (score >= frequency_threshold) { continue; }
+ }
+ /* score < frequency_threshold || item_boost < 0 */
+ grn_hash_cursor_delete(ctx, hc, NULL);
+ }
+ }
+ grn_hash_cursor_close(ctx, hc);
+ }
+ }
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_SIZE,
+ ":", "filter(%d)", grn_table_size(ctx, res));
+ {
+ /* exec _score -= edit_distance(_key, "query string") for all records */
+ grn_obj *var;
+ grn_obj *expr;
+
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, res, expr, var);
+ if (expr) {
+ grn_table_cursor *tc;
+ grn_obj *score = grn_obj_column(ctx, res,
+ GRN_COLUMN_NAME_SCORE,
+ GRN_COLUMN_NAME_SCORE_LEN);
+ grn_obj *key = grn_obj_column(ctx, res,
+ GRN_COLUMN_NAME_KEY,
+ GRN_COLUMN_NAME_KEY_LEN);
+ grn_expr_append_obj(ctx, expr,
+ score,
+ GRN_OP_GET_VALUE, 1);
+ grn_expr_append_obj(ctx, expr,
+ grn_ctx_get(ctx, CONST_STR_LEN("edit_distance")),
+ GRN_OP_PUSH, 1);
+ grn_expr_append_obj(ctx, expr,
+ key,
+ GRN_OP_GET_VALUE, 1);
+ grn_expr_append_const(ctx, expr, query, GRN_OP_PUSH, 1);
+ grn_expr_append_op(ctx, expr, GRN_OP_CALL, 2);
+ grn_expr_append_op(ctx, expr, GRN_OP_MINUS_ASSIGN, 2);
+
+ if ((tc = grn_table_cursor_open(ctx, res, NULL, 0, NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ grn_obj score_value;
+ GRN_INT32_INIT(&score_value, 0);
+ while ((id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) {
+ GRN_RECORD_SET(ctx, var, id);
+ grn_expr_exec(ctx, expr, 0);
+ GRN_BULK_REWIND(&score_value);
+ grn_obj_get_value(ctx, score, id, &score_value);
+ if (GRN_INT32_VALUE(&score_value) < frequency_threshold) {
+ grn_table_cursor_delete(ctx, tc);
+ }
+ }
+ grn_obj_unlink(ctx, &score_value);
+ grn_table_cursor_close(ctx, tc);
+ }
+ grn_obj_unlink(ctx, score);
+ grn_obj_unlink(ctx, key);
+ grn_obj_unlink(ctx, expr);
+ } else {
+ ERR(GRN_UNKNOWN_ERROR,
+ "error on building expr. for calicurating edit distance");
+ }
+ }
+ }
+ grn_obj_unlink(ctx, key);
+ }
+ }
+ output(ctx, items, res, tid, sortby, output_columns, offset, limit);
+ grn_obj_close(ctx, res);
+ } else {
+ ERR(GRN_UNKNOWN_ERROR, "cannot create temporary table.");
+ }
+ GRN_OBJ_FIN(ctx, &item_boost);
+ GRN_OBJ_FIN(ctx, &item_freq2);
+}
+
+static void
+suggest(grn_ctx *ctx, grn_obj *items, grn_obj *items_boost,
+ grn_obj *query, grn_obj *sortby,
+ grn_obj *output_columns, int offset, int limit,
+ int frequency_threshold, double conditional_probability_threshold)
+{
+ grn_obj *res;
+ if ((res = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, items, NULL))) {
+ grn_id tid = grn_table_get(ctx, items, TEXT_VALUE_LEN(query));
+ cooccurrence_search(ctx, items, items_boost, tid, res, SUGGEST,
+ frequency_threshold, conditional_probability_threshold);
+ output(ctx, items, res, tid, sortby, output_columns, offset, limit);
+ grn_obj_close(ctx, res);
+ } else {
+ ERR(GRN_UNKNOWN_ERROR, "cannot create temporary table.");
+ }
+}
+
+static grn_suggest_search_mode
+parse_search_mode(grn_ctx *ctx, grn_obj *mode_text)
+{
+ grn_suggest_search_mode mode;
+ int mode_length;
+
+ mode_length = GRN_TEXT_LEN(mode_text);
+ if (mode_length == 3 &&
+ strncasecmp("yes", GRN_TEXT_VALUE(mode_text), 3) == 0) {
+ mode = GRN_SUGGEST_SEARCH_YES;
+ } else if (mode_length == 2 &&
+ strncasecmp("no", GRN_TEXT_VALUE(mode_text), 2) == 0) {
+ mode = GRN_SUGGEST_SEARCH_NO;
+ } else {
+ mode = GRN_SUGGEST_SEARCH_AUTO;
+ }
+
+ return mode;
+}
+
+static grn_obj *
+command_suggest(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *items, *col, *items_boost;
+ int types;
+ int offset = 0;
+ int limit = DEFAULT_LIMIT;
+ int frequency_threshold = DEFAULT_FREQUENCY_THRESHOLD;
+ double conditional_probability_threshold =
+ DEFAULT_CONDITIONAL_PROBABILITY_THRESHOLD;
+ grn_suggest_search_mode prefix_search_mode;
+ grn_suggest_search_mode similar_search_mode;
+
+ types = grn_parse_suggest_types(VAR(0));
+ if (GRN_TEXT_LEN(VAR(6)) > 0) {
+ offset = grn_atoi(GRN_TEXT_VALUE(VAR(6)), GRN_BULK_CURR(VAR(6)), NULL);
+ }
+ if (GRN_TEXT_LEN(VAR(7)) > 0) {
+ limit = grn_atoi(GRN_TEXT_VALUE(VAR(7)), GRN_BULK_CURR(VAR(7)), NULL);
+ }
+ if (GRN_TEXT_LEN(VAR(8)) > 0) {
+ frequency_threshold = grn_atoi(GRN_TEXT_VALUE(VAR(8)), GRN_BULK_CURR(VAR(8)), NULL);
+ }
+ if (GRN_TEXT_LEN(VAR(9)) > 0) {
+ GRN_TEXT_PUTC(ctx, VAR(9), '\0');
+ conditional_probability_threshold = strtod(GRN_TEXT_VALUE(VAR(9)), NULL);
+ }
+
+ prefix_search_mode = parse_search_mode(ctx, VAR(10));
+ similar_search_mode = parse_search_mode(ctx, VAR(11));
+
+ if ((items = grn_ctx_get(ctx, TEXT_VALUE_LEN(VAR(1))))) {
+ if ((items_boost = grn_obj_column(ctx, items, CONST_STR_LEN("boost")))) {
+ GRN_OUTPUT_MAP_OPEN("RESULT_SET", -1);
+ if (types & COMPLETE) {
+ if ((col = grn_obj_column(ctx, items, TEXT_VALUE_LEN(VAR(2))))) {
+ GRN_OUTPUT_CSTR("complete");
+ complete(ctx, items, items_boost, col, VAR(3), VAR(4),
+ VAR(5), offset, limit,
+ frequency_threshold, conditional_probability_threshold,
+ prefix_search_mode);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "invalid column.");
+ }
+ }
+ if (types & CORRECT) {
+ GRN_OUTPUT_CSTR("correct");
+ correct(ctx, items, items_boost, VAR(3), VAR(4),
+ VAR(5), offset, limit,
+ frequency_threshold, conditional_probability_threshold,
+ similar_search_mode);
+ }
+ if (types & SUGGEST) {
+ GRN_OUTPUT_CSTR("suggest");
+ suggest(ctx, items, items_boost, VAR(3), VAR(4),
+ VAR(5), offset, limit,
+ frequency_threshold, conditional_probability_threshold);
+ }
+ GRN_OUTPUT_MAP_CLOSE();
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "nonexistent column: <%.*s.boost>",
+ (int)GRN_TEXT_LEN(VAR(1)), GRN_TEXT_VALUE(VAR(1)));
+ }
+ grn_obj_unlink(ctx, items);
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "nonexistent table: <%.*s>",
+ (int)GRN_TEXT_LEN(VAR(1)), GRN_TEXT_VALUE(VAR(1)));
+ }
+ return NULL;
+}
+
+static void
+learner_init_values(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ learner->post_event_id = GRN_RECORD_VALUE(learner->post_event);
+ learner->post_type_id = GRN_RECORD_VALUE(learner->post_type);
+ learner->post_item_id = GRN_RECORD_VALUE(learner->post_item);
+ learner->seq_id = GRN_RECORD_VALUE(learner->seq);
+ learner->post_time_value = GRN_TIME_VALUE(learner->post_time);
+}
+
+static void
+learner_init(grn_ctx *ctx, grn_suggest_learner *learner,
+ grn_obj *post_event, grn_obj *post_type, grn_obj *post_item,
+ grn_obj *seq, grn_obj *post_time, grn_obj *pairs)
+{
+ learner->post_event = post_event;
+ learner->post_type = post_type;
+ learner->post_item = post_item;
+ learner->seq = seq;
+ learner->post_time = post_time;
+ learner->pairs = pairs;
+
+ learner->learn_distance_in_seconds = 0;
+
+ learner_init_values(ctx, learner);
+}
+
+static void
+learner_init_columns(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ grn_id events_id, event_types_id;
+ grn_obj *seqs, *events, *post_item, *items, *pairs;
+
+ learner->seqs = seqs = grn_ctx_at(ctx, GRN_OBJ_GET_DOMAIN(learner->seq));
+ learner->seqs_events = grn_obj_column(ctx, seqs, CONST_STR_LEN("events"));
+
+ events_id = grn_obj_get_range(ctx, learner->seqs_events);
+ learner->events = events = grn_ctx_at(ctx, events_id);
+ learner->events_item = grn_obj_column(ctx, events, CONST_STR_LEN("item"));
+ learner->events_type = grn_obj_column(ctx, events, CONST_STR_LEN("type"));
+ learner->events_time = grn_obj_column(ctx, events, CONST_STR_LEN("time"));
+
+ event_types_id = grn_obj_get_range(ctx, learner->events_type);
+ learner->event_types = grn_obj_column(ctx, events, CONST_STR_LEN("time"));
+
+ post_item = learner->post_item;
+ learner->items = items = grn_ctx_at(ctx, GRN_OBJ_GET_DOMAIN(post_item));
+ learner->items_freq = grn_obj_column(ctx, items, CONST_STR_LEN("freq"));
+ learner->items_freq2 = grn_obj_column(ctx, items, CONST_STR_LEN("freq2"));
+ learner->items_last = grn_obj_column(ctx, items, CONST_STR_LEN("last"));
+
+ pairs = learner->pairs;
+ learner->pairs_pre = grn_obj_column(ctx, pairs, CONST_STR_LEN("pre"));
+ learner->pairs_post = grn_obj_column(ctx, pairs, CONST_STR_LEN("post"));
+ learner->pairs_freq0 = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq0"));
+ learner->pairs_freq1 = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq1"));
+ learner->pairs_freq2 = grn_obj_column(ctx, pairs, CONST_STR_LEN("freq2"));
+}
+
+static void
+learner_fin_columns(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ grn_obj_unlink(ctx, learner->seqs);
+ grn_obj_unlink(ctx, learner->seqs_events);
+
+ grn_obj_unlink(ctx, learner->events);
+ grn_obj_unlink(ctx, learner->events_item);
+ grn_obj_unlink(ctx, learner->events_type);
+ grn_obj_unlink(ctx, learner->events_time);
+
+ grn_obj_unlink(ctx, learner->event_types);
+
+ grn_obj_unlink(ctx, learner->items);
+ grn_obj_unlink(ctx, learner->items_freq);
+ grn_obj_unlink(ctx, learner->items_freq2);
+ grn_obj_unlink(ctx, learner->items_last);
+
+ grn_obj_unlink(ctx, learner->pairs_pre);
+ grn_obj_unlink(ctx, learner->pairs_post);
+ grn_obj_unlink(ctx, learner->pairs_freq0);
+ grn_obj_unlink(ctx, learner->pairs_freq1);
+ grn_obj_unlink(ctx, learner->pairs_freq2);
+}
+
+static void
+learner_init_weight(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ grn_obj *weight_column = NULL;
+ unsigned int weight = 1;
+
+ if (learner->configuration) {
+ weight_column = grn_obj_column(ctx,
+ learner->configuration,
+ CONST_STR_LEN("weight"));
+ }
+ if (weight_column) {
+ grn_id id;
+ id = grn_table_get(ctx, learner->configuration,
+ GRN_TEXT_VALUE(&(learner->dataset_name)),
+ GRN_TEXT_LEN(&(learner->dataset_name)));
+ if (id != GRN_ID_NIL) {
+ grn_obj weight_value;
+ GRN_UINT32_INIT(&weight_value, 0);
+ grn_obj_get_value(ctx, weight_column, id, &weight_value);
+ weight = GRN_UINT32_VALUE(&weight_value);
+ GRN_OBJ_FIN(ctx, &weight_value);
+ }
+ grn_obj_unlink(ctx, weight_column);
+ }
+
+ GRN_UINT32_INIT(&(learner->weight), 0);
+ GRN_UINT32_SET(ctx, &(learner->weight), weight);
+}
+
+static void
+learner_init_dataset_name(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ char events_name[GRN_TABLE_MAX_KEY_SIZE];
+ unsigned int events_name_size;
+ unsigned int events_name_prefix_size;
+
+ events_name_size = grn_obj_name(ctx, learner->events,
+ events_name, GRN_TABLE_MAX_KEY_SIZE);
+ GRN_TEXT_INIT(&(learner->dataset_name), 0);
+ events_name_prefix_size = strlen("event_");
+ if (events_name_size > events_name_prefix_size) {
+ GRN_TEXT_PUT(ctx,
+ &(learner->dataset_name),
+ events_name + events_name_prefix_size,
+ events_name_size - events_name_prefix_size);
+ }
+}
+
+static void
+learner_fin_dataset_name(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ GRN_OBJ_FIN(ctx, &(learner->dataset_name));
+}
+
+static void
+learner_init_configuration(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ learner->configuration = grn_ctx_get(ctx, "configuration", -1);
+}
+
+static void
+learner_fin_configuration(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ if (learner->configuration) {
+ grn_obj_unlink(ctx, learner->configuration);
+ }
+}
+
+static void
+learner_init_buffers(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ learner_init_weight(ctx, learner);
+ GRN_RECORD_INIT(&(learner->pre_events), 0, grn_obj_id(ctx, learner->events));
+}
+
+static void
+learner_fin_buffers(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ grn_obj_unlink(ctx, &(learner->weight));
+ grn_obj_unlink(ctx, &(learner->pre_events));
+}
+
+static void
+learner_init_submit_learn(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ grn_id items_id;
+
+ learner->key_prefix = ((uint64_t)learner->post_item_id) << 32;
+
+ items_id = grn_obj_get_range(ctx, learner->events_item);
+ GRN_RECORD_INIT(&(learner->pre_item), 0, items_id);
+
+ grn_obj_get_value(ctx, learner->seqs_events, learner->seq_id,
+ &(learner->pre_events));
+}
+
+static void
+learner_fin_submit_learn(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ grn_obj_unlink(ctx, &(learner->pre_item));
+ GRN_BULK_REWIND(&(learner->pre_events));
+}
+
+static grn_bool
+learner_is_valid_input(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ return learner->post_event_id && learner->post_item_id && learner->seq_id;
+}
+
+static void
+learner_increment(grn_ctx *ctx, grn_suggest_learner *learner,
+ grn_obj *column, grn_id record_id)
+{
+ grn_obj_set_value(ctx, column, record_id, &(learner->weight), GRN_OBJ_INCR);
+}
+
+static void
+learner_increment_item_freq(grn_ctx *ctx, grn_suggest_learner *learner,
+ grn_obj *column)
+{
+ learner_increment(ctx, learner, column, learner->post_item_id);
+}
+
+static void
+learner_set_last_post_time(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ grn_obj_set_value(ctx, learner->items_last, learner->post_item_id,
+ learner->post_time, GRN_OBJ_SET);
+}
+
+static void
+learner_learn_for_complete_and_correcnt(grn_ctx *ctx,
+ grn_suggest_learner *learner)
+{
+ grn_obj *pre_item, *post_item, *pre_events;
+ grn_obj pre_type, pre_time;
+ grn_id *ep, *es;
+ uint64_t key;
+ int64_t post_time_value;
+
+ pre_item = &(learner->pre_item);
+ post_item = learner->post_item;
+ pre_events = &(learner->pre_events);
+ post_time_value = learner->post_time_value;
+ GRN_RECORD_INIT(&pre_type, 0, grn_obj_get_range(ctx, learner->events_type));
+ GRN_TIME_INIT(&pre_time, 0);
+ ep = (grn_id *)GRN_BULK_CURR(pre_events);
+ es = (grn_id *)GRN_BULK_HEAD(pre_events);
+ while (es < ep--) {
+ grn_id pair_id;
+ int added;
+ int64_t learn_distance;
+
+ GRN_BULK_REWIND(&pre_type);
+ GRN_BULK_REWIND(&pre_time);
+ GRN_BULK_REWIND(pre_item);
+ grn_obj_get_value(ctx, learner->events_type, *ep, &pre_type);
+ grn_obj_get_value(ctx, learner->events_time, *ep, &pre_time);
+ grn_obj_get_value(ctx, learner->events_item, *ep, pre_item);
+ learn_distance = post_time_value - GRN_TIME_VALUE(&pre_time);
+ if (learn_distance >= MIN_LEARN_DISTANCE) {
+ learner->learn_distance_in_seconds =
+ (int)(learn_distance / GRN_TIME_USEC_PER_SEC);
+ break;
+ }
+ key = learner->key_prefix + GRN_RECORD_VALUE(pre_item);
+ pair_id = grn_table_add(ctx, learner->pairs, &key, sizeof(uint64_t),
+ &added);
+ if (added) {
+ grn_obj_set_value(ctx, learner->pairs_pre, pair_id, pre_item,
+ GRN_OBJ_SET);
+ grn_obj_set_value(ctx, learner->pairs_post, pair_id, post_item,
+ GRN_OBJ_SET);
+ }
+ if (GRN_RECORD_VALUE(&pre_type)) {
+ learner_increment(ctx, learner, learner->pairs_freq1, pair_id);
+ break;
+ } else {
+ learner_increment(ctx, learner, learner->pairs_freq0, pair_id);
+ }
+ }
+ GRN_OBJ_FIN(ctx, &pre_type);
+ GRN_OBJ_FIN(ctx, &pre_time);
+}
+
+static void
+learner_learn_for_suggest(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ char keybuf[GRN_TABLE_MAX_KEY_SIZE];
+ int keylen = grn_table_get_key(ctx, learner->items, learner->post_item_id,
+ keybuf, GRN_TABLE_MAX_KEY_SIZE);
+ unsigned int token_flags = 0;
+ grn_token *token = grn_token_open(ctx, learner->items, keybuf, keylen,
+ GRN_TOKEN_ADD, token_flags);
+ if (token) {
+ grn_id tid;
+ grn_obj *pre_item = &(learner->pre_item);
+ grn_obj *post_item = learner->post_item;
+ grn_hash *token_ids = NULL;
+ while ((tid = grn_token_next(ctx, token)) && tid != learner->post_item_id) {
+ uint64_t key;
+ int added;
+ grn_id pair_id;
+ key = learner->key_prefix + tid;
+ pair_id = grn_table_add(ctx, learner->pairs, &key, sizeof(uint64_t),
+ &added);
+ if (added) {
+ GRN_RECORD_SET(ctx, pre_item, tid);
+ grn_obj_set_value(ctx, learner->pairs_pre, pair_id,
+ pre_item, GRN_OBJ_SET);
+ grn_obj_set_value(ctx, learner->pairs_post, pair_id,
+ post_item, GRN_OBJ_SET);
+ }
+ if (!token_ids) {
+ token_ids = grn_hash_create(ctx, NULL, sizeof(grn_id), 0,
+ GRN_OBJ_TABLE_HASH_KEY|GRN_HASH_TINY);
+ }
+ if (token_ids) {
+ int token_added;
+ grn_hash_add(ctx, token_ids, &tid, sizeof(grn_id), NULL, &token_added);
+ if (token_added) {
+ learner_increment(ctx, learner, learner->pairs_freq2, pair_id);
+ }
+ }
+ }
+ if (token_ids) {
+ grn_hash_close(ctx, token_ids);
+ }
+ grn_token_close(ctx, token);
+ }
+}
+
+static void
+learner_append_post_event(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ GRN_RECORD_SET(ctx, &(learner->pre_events), learner->post_event_id);
+ grn_obj_set_value(ctx, learner->seqs_events, learner->seq_id,
+ &(learner->pre_events), GRN_OBJ_APPEND);
+}
+
+static void
+learner_learn(grn_ctx *ctx, grn_suggest_learner *learner)
+{
+ if (learner_is_valid_input(ctx, learner)) {
+ learner_init_columns(ctx, learner);
+ learner_init_dataset_name(ctx, learner);
+ learner_init_configuration(ctx, learner);
+ learner_init_buffers(ctx, learner);
+ learner_increment_item_freq(ctx, learner, learner->items_freq);
+ learner_set_last_post_time(ctx, learner);
+ if (learner->post_type_id) {
+ learner_init_submit_learn(ctx, learner);
+ learner_increment_item_freq(ctx, learner, learner->items_freq2);
+ learner_learn_for_complete_and_correcnt(ctx, learner);
+ learner_learn_for_suggest(ctx, learner);
+ learner_fin_submit_learn(ctx, learner);
+ }
+ learner_append_post_event(ctx, learner);
+ learner_fin_buffers(ctx, learner);
+ learner_fin_configuration(ctx, learner);
+ learner_fin_dataset_name(ctx, learner);
+ learner_fin_columns(ctx, learner);
+ }
+}
+
+static grn_obj *
+func_suggest_preparer(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ int learn_distance_in_seconds = 0;
+ grn_obj *obj;
+ if (nargs == 6) {
+ grn_obj *post_event = args[0];
+ grn_obj *post_type = args[1];
+ grn_obj *post_item = args[2];
+ grn_obj *seq = args[3];
+ grn_obj *post_time = args[4];
+ grn_obj *pairs = args[5];
+ grn_suggest_learner learner;
+ learner_init(ctx, &learner,
+ post_event, post_type, post_item, seq, post_time, pairs);
+ learner_learn(ctx, &learner);
+ learn_distance_in_seconds = learner.learn_distance_in_seconds;
+ }
+ if ((obj = GRN_PROC_ALLOC(GRN_DB_UINT32, 0))) {
+ GRN_UINT32_SET(ctx, obj, learn_distance_in_seconds);
+ }
+ return obj;
+}
+
+grn_rc
+GRN_PLUGIN_INIT(grn_ctx *ctx)
+{
+ return GRN_SUCCESS;
+}
+
+grn_rc
+GRN_PLUGIN_REGISTER(grn_ctx *ctx)
+{
+ grn_expr_var vars[12];
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "types", -1);
+ grn_plugin_expr_var_init(ctx, &vars[1], "table", -1);
+ grn_plugin_expr_var_init(ctx, &vars[2], "column", -1);
+ grn_plugin_expr_var_init(ctx, &vars[3], "query", -1);
+ grn_plugin_expr_var_init(ctx, &vars[4], "sortby", -1);
+ grn_plugin_expr_var_init(ctx, &vars[5], "output_columns", -1);
+ grn_plugin_expr_var_init(ctx, &vars[6], "offset", -1);
+ grn_plugin_expr_var_init(ctx, &vars[7], "limit", -1);
+ grn_plugin_expr_var_init(ctx, &vars[8], "frequency_threshold", -1);
+ grn_plugin_expr_var_init(ctx, &vars[9], "conditional_probability_threshold", -1);
+ grn_plugin_expr_var_init(ctx, &vars[10], "prefix_search", -1);
+ grn_plugin_expr_var_init(ctx, &vars[11], "similar_search", -1);
+ grn_plugin_command_create(ctx, "suggest", -1, command_suggest, 12, vars);
+
+ grn_proc_create(ctx, CONST_STR_LEN("suggest_preparer"), GRN_PROC_FUNCTION,
+ func_suggest_preparer, NULL, NULL, 0, NULL);
+ return ctx->rc;
+}
+
+grn_rc
+GRN_PLUGIN_FIN(grn_ctx *ctx)
+{
+ return GRN_SUCCESS;
+}
diff --git a/storage/mroonga/vendor/groonga/plugins/table/CMakeLists.txt b/storage/mroonga/vendor/groonga/plugins/table/CMakeLists.txt
new file mode 100644
index 00000000000..cba4697f042
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/table/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright(C) 2012-2013 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../lib
+ ${MRUBY_INCLUDE_DIRS})
+
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/sources.am TABLE_SOURCES)
+add_library(table MODULE ${TABLE_SOURCES})
+set_source_files_properties(${TABLE_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+set_target_properties(table PROPERTIES PREFIX "")
+target_link_libraries(table libgroonga)
+if(NOT MRN_GROONGA_BUNDLED)
+ install(TARGETS table DESTINATION "${GRN_RELATIVE_PLUGINS_DIR}/table")
+endif()
diff --git a/storage/mroonga/vendor/groonga/plugins/table/Makefile.am b/storage/mroonga/vendor/groonga/plugins/table/Makefile.am
new file mode 100644
index 00000000000..4b49b0a3224
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/table/Makefile.am
@@ -0,0 +1,24 @@
+EXTRA_DIST = \
+ CMakeLists.txt
+
+AM_CFLAGS = \
+ $(MESSAGE_PACK_CFLAGS) \
+ $(MRUBY_CFLAGS)
+
+AM_CPPFLAGS = \
+ -I$(top_builddir) \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib
+
+AM_LDFLAGS = \
+ -avoid-version \
+ -module \
+ -no-undefined
+
+LIBS = \
+ $(top_builddir)/lib/libgroonga.la \
+ $(MESSAGE_PACK_LIBS)
+
+table_plugins_LTLIBRARIES = table.la
+
+include sources.am
diff --git a/storage/mroonga/vendor/groonga/plugins/table/sources.am b/storage/mroonga/vendor/groonga/plugins/table/sources.am
new file mode 100644
index 00000000000..943e79b1f5c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/table/sources.am
@@ -0,0 +1,2 @@
+table_la_SOURCES = \
+ table.c
diff --git a/storage/mroonga/vendor/groonga/plugins/table/table.c b/storage/mroonga/vendor/groonga/plugins/table/table.c
new file mode 100644
index 00000000000..a4fcd17bf90
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/table/table.c
@@ -0,0 +1,747 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/* Copyright(C) 2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <string.h>
+
+#include "ctx.h"
+#include "db.h"
+#include "output.h"
+#include "util.h"
+#include <groonga/plugin.h>
+
+#define VAR GRN_PROC_GET_VAR_BY_OFFSET
+#define TEXT_VALUE_LEN(x) GRN_TEXT_VALUE(x), GRN_TEXT_LEN(x)
+
+static grn_obj *
+grn_ctx_get_table_by_name_or_id(grn_ctx *ctx,
+ const char *name, unsigned int name_len)
+{
+ grn_obj *table;
+ const char *end = name + name_len;
+ const char *rest = NULL;
+ grn_id id = grn_atoui(name, end, &rest);
+ if (rest == end) {
+ table = grn_ctx_at(ctx, id);
+ } else {
+ table = grn_ctx_get(ctx, name, name_len);
+ }
+ if (!GRN_OBJ_TABLEP(table)) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid table name: <%.*s>", name_len, name);
+ if (table) {
+ grn_obj_unlink(ctx, table);
+ table = NULL;
+ }
+ }
+ return table;
+}
+
+static void
+grn_output_table_name_or_id(grn_ctx *ctx, grn_obj *table)
+{
+ if (table) {
+ if (((grn_db_obj *)table)->id & GRN_OBJ_TMP_OBJECT) {
+ GRN_OUTPUT_INT64(((grn_db_obj *)table)->id);
+ } else {
+ int name_len;
+ char name_buf[GRN_TABLE_MAX_KEY_SIZE];
+ name_len = grn_obj_name(ctx, table, name_buf, GRN_TABLE_MAX_KEY_SIZE);
+ GRN_OUTPUT_STR(name_buf, name_len);
+ }
+ } else {
+ GRN_OUTPUT_INT64(0);
+ }
+}
+
+static grn_bool
+parse_bool_value(grn_ctx *ctx, grn_obj *text)
+{
+ grn_bool value = GRN_FALSE;
+ if (GRN_TEXT_LEN(text) == 3 &&
+ memcmp("yes", GRN_TEXT_VALUE(text), 3) == 0) {
+ value = GRN_TRUE;
+ }
+ return value;
+}
+
+static grn_operator
+parse_set_operator_value(grn_ctx *ctx, grn_obj *text)
+{
+ grn_operator value = GRN_OP_OR;
+ if (GRN_TEXT_LEN(text) == 3) {
+ if (memcmp("and", GRN_TEXT_VALUE(text), 3) == 0) {
+ value = GRN_OP_AND;
+ } else if (memcmp("but", GRN_TEXT_VALUE(text), 3) == 0) {
+ value = GRN_OP_AND_NOT;
+ }
+ } else if (GRN_TEXT_LEN(text) == 6 &&
+ memcmp("adjust", GRN_TEXT_VALUE(text), 6) == 0) {
+ value = GRN_OP_ADJUST;
+ } else if (GRN_TEXT_LEN(text) == 7 &&
+ memcmp("and_not", GRN_TEXT_VALUE(text), 7) == 0) {
+ value = GRN_OP_AND_NOT;
+ }
+ return value;
+}
+
+static grn_obj *
+command_match(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *result_set = NULL;
+ grn_obj *table = grn_ctx_get_table_by_name_or_id(ctx, TEXT_VALUE_LEN(VAR(0)));
+ if (table) {
+ grn_expr_flags flags = GRN_EXPR_SYNTAX_QUERY;
+ grn_obj *v, *query, *columns = NULL;
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, table, query, v);
+ if (query) {
+ if (GRN_TEXT_LEN(VAR(1))) {
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, table, columns, v);
+ if (columns) {
+ grn_expr_parse(ctx, columns, TEXT_VALUE_LEN(VAR(1)),
+ NULL, GRN_OP_MATCH, GRN_OP_AND,
+ GRN_EXPR_SYNTAX_SCRIPT);
+ }
+ }
+ if (parse_bool_value(ctx, VAR(5))) {
+ flags |= GRN_EXPR_ALLOW_COLUMN;
+ }
+ if (parse_bool_value(ctx, VAR(6))) {
+ flags |= GRN_EXPR_ALLOW_PRAGMA;
+ }
+ grn_expr_parse(ctx, query, TEXT_VALUE_LEN(VAR(2)),
+ columns, GRN_OP_MATCH, GRN_OP_AND, flags);
+ if (GRN_TEXT_LEN(VAR(3))) {
+ result_set = grn_ctx_get_table_by_name_or_id(ctx, TEXT_VALUE_LEN(VAR(3)));
+ } else {
+ result_set = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|
+ GRN_OBJ_WITH_SUBREC,
+ table, NULL);
+ }
+ if (result_set) {
+ grn_table_select(ctx, table, query, result_set,
+ parse_set_operator_value(ctx, VAR(4)));
+ }
+ grn_obj_unlink(ctx, columns);
+ grn_obj_unlink(ctx, query);
+ }
+ }
+ grn_output_table_name_or_id(ctx, result_set);
+ return NULL;
+}
+
+static grn_obj *
+command_filter_by_script(grn_ctx *ctx, int nargs,
+ grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *result_set = NULL;
+ grn_obj *table = grn_ctx_get_table_by_name_or_id(ctx, TEXT_VALUE_LEN(VAR(0)));
+ if (table) {
+ grn_expr_flags flags = GRN_EXPR_SYNTAX_SCRIPT;
+ grn_obj *v, *query;
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, table, query, v);
+ if (query) {
+ if (parse_bool_value(ctx, VAR(4))) {
+ flags |= GRN_EXPR_ALLOW_UPDATE;
+ }
+ grn_expr_parse(ctx, query, TEXT_VALUE_LEN(VAR(1)),
+ NULL, GRN_OP_MATCH, GRN_OP_AND, flags);
+ if (GRN_TEXT_LEN(VAR(2))) {
+ result_set = grn_ctx_get_table_by_name_or_id(ctx, TEXT_VALUE_LEN(VAR(2)));
+ } else {
+ result_set = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|
+ GRN_OBJ_WITH_SUBREC,
+ table, NULL);
+ }
+ if (result_set) {
+ grn_table_select(ctx, table, query, result_set,
+ parse_set_operator_value(ctx, VAR(3)));
+ }
+ grn_obj_unlink(ctx, query);
+ }
+ }
+ grn_output_table_name_or_id(ctx, result_set);
+ return NULL;
+}
+
+static grn_obj *
+command_filter(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_operator operator = GRN_OP_NOP;
+ grn_obj *table, *column, *result_set = NULL;
+ if (!(table = grn_ctx_get_table_by_name_or_id(ctx, TEXT_VALUE_LEN(VAR(0))))) {
+ goto exit;
+ }
+ if (!(column = grn_obj_column(ctx, table, TEXT_VALUE_LEN(VAR(1))))) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid column name: <%.*s>",
+ (int)GRN_TEXT_LEN(VAR(1)), GRN_TEXT_VALUE(VAR(1)));
+ goto exit;
+ }
+ if (GRN_TEXT_LEN(VAR(2)) == 0) {
+ ERR(GRN_INVALID_ARGUMENT, "missing mandatory argument: operator");
+ goto exit;
+ } else {
+ uint32_t operator_len = GRN_TEXT_LEN(VAR(2));
+ const char *operator_text = GRN_TEXT_VALUE(VAR(2));
+ switch (operator_text[0]) {
+ case '<' :
+ if (operator_len == 1) {
+ operator = GRN_OP_LESS;
+ }
+ break;
+ }
+ if (operator == GRN_OP_NOP) {
+ ERR(GRN_INVALID_ARGUMENT, "invalid operator: <%.*s>",
+ operator_len, operator_text);
+ goto exit;
+ }
+ }
+ if (GRN_TEXT_LEN(VAR(4))) {
+ result_set = grn_ctx_get_table_by_name_or_id(ctx, TEXT_VALUE_LEN(VAR(4)));
+ } else {
+ result_set = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_TABLE_HASH_KEY|
+ GRN_OBJ_WITH_SUBREC,
+ table, NULL);
+ }
+ if (result_set) {
+ grn_column_filter(ctx, column, operator, VAR(3), result_set,
+ parse_set_operator_value(ctx, VAR(5)));
+ }
+exit :
+ grn_output_table_name_or_id(ctx, result_set);
+ return NULL;
+}
+
+static grn_obj *
+command_group(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data)
+{
+ const char *table = GRN_TEXT_VALUE(VAR(0));
+ unsigned int table_len = GRN_TEXT_LEN(VAR(0));
+ const char *key = GRN_TEXT_VALUE(VAR(1));
+ unsigned int key_len = GRN_TEXT_LEN(VAR(1));
+ const char *set = GRN_TEXT_VALUE(VAR(2));
+ unsigned int set_len = GRN_TEXT_LEN(VAR(2));
+ grn_obj *table_ = grn_ctx_get_table_by_name_or_id(ctx, table, table_len);
+ grn_obj *set_ = NULL;
+ if (table_) {
+ uint32_t ngkeys;
+ grn_table_sort_key *gkeys;
+ gkeys = grn_table_sort_key_from_str(ctx, key, key_len, table_, &ngkeys);
+ if (gkeys) {
+ if (set_len) {
+ set_ = grn_ctx_get_table_by_name_or_id(ctx, set, set_len);
+ } else {
+ set_ = grn_table_create_for_group(ctx, NULL, 0, NULL,
+ gkeys[0].key, table_, 0);
+ }
+ if (set_) {
+ if (GRN_TEXT_LEN(VAR(3))) {
+ uint32_t gap = grn_atoui(GRN_TEXT_VALUE(VAR(3)),
+ GRN_BULK_CURR(VAR(3)), NULL);
+ grn_table_group_with_range_gap(ctx, table_, gkeys, set_, gap);
+ } else {
+ grn_table_group_result g = {
+ set_, 0, 0, 1,
+ GRN_TABLE_GROUP_CALC_COUNT, 0
+ };
+ grn_table_group(ctx, table_, gkeys, 1, &g, 1);
+ }
+ }
+ grn_table_sort_key_close(ctx, gkeys, ngkeys);
+ }
+ }
+ grn_output_table_name_or_id(ctx, set_);
+ return NULL;
+}
+
+#define DEFAULT_LIMIT 10
+
+static grn_obj *
+command_sort(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data)
+{
+ const char *table = GRN_TEXT_VALUE(VAR(0));
+ unsigned int table_len = GRN_TEXT_LEN(VAR(0));
+ const char *keys = GRN_TEXT_VALUE(VAR(1));
+ unsigned int keys_len = GRN_TEXT_LEN(VAR(1));
+ int offset = GRN_TEXT_LEN(VAR(2))
+ ? grn_atoi(GRN_TEXT_VALUE(VAR(2)), GRN_BULK_CURR(VAR(2)), NULL)
+ : 0;
+ int limit = GRN_TEXT_LEN(VAR(3))
+ ? grn_atoi(GRN_TEXT_VALUE(VAR(3)), GRN_BULK_CURR(VAR(3)), NULL)
+ : DEFAULT_LIMIT;
+ grn_obj *table_ = grn_ctx_get_table_by_name_or_id(ctx, table, table_len);
+ grn_obj *sorted = NULL;
+ if (table_) {
+ uint32_t nkeys;
+ grn_table_sort_key *keys_;
+ if (keys_len &&
+ (keys_ = grn_table_sort_key_from_str(ctx, keys, keys_len,
+ table_, &nkeys))) {
+ if ((sorted = grn_table_create(ctx, NULL, 0, NULL,
+ GRN_OBJ_TABLE_NO_KEY, NULL, table_))) {
+ int table_size = (int)grn_table_size(ctx, table_);
+ grn_normalize_offset_and_limit(ctx, table_size, &offset, &limit);
+ grn_table_sort(ctx, table_, offset, limit, sorted, keys_, nkeys);
+ grn_table_sort_key_close(ctx, keys_, nkeys);
+ }
+ }
+ }
+ grn_output_table_name_or_id(ctx, sorted);
+ return NULL;
+}
+
+static grn_obj *
+command_output(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data)
+{
+ const char *table = GRN_TEXT_VALUE(VAR(0));
+ unsigned int table_len = GRN_TEXT_LEN(VAR(0));
+ const char *columns = GRN_TEXT_VALUE(VAR(1));
+ unsigned int columns_len = GRN_TEXT_LEN(VAR(1));
+ int offset = GRN_TEXT_LEN(VAR(2))
+ ? grn_atoi(GRN_TEXT_VALUE(VAR(2)), GRN_BULK_CURR(VAR(2)), NULL)
+ : 0;
+ int limit = GRN_TEXT_LEN(VAR(3))
+ ? grn_atoi(GRN_TEXT_VALUE(VAR(3)), GRN_BULK_CURR(VAR(3)), NULL)
+ : DEFAULT_LIMIT;
+ grn_obj *table_ = grn_ctx_get_table_by_name_or_id(ctx, table, table_len);
+ if (table_) {
+ grn_obj_format format;
+ int table_size = (int)grn_table_size(ctx, table_);
+ GRN_OBJ_FORMAT_INIT(&format, table_size, 0, limit, offset);
+ format.flags =
+ GRN_OBJ_FORMAT_WITH_COLUMN_NAMES|
+ GRN_OBJ_FORMAT_XML_ELEMENT_RESULTSET;
+ /* TODO: accept only comma separated expr as columns */
+ grn_obj_columns(ctx, table_, columns, columns_len, &format.columns);
+ GRN_OUTPUT_OBJ(table_, &format);
+ GRN_OBJ_FORMAT_FIN(ctx, &format);
+ }
+ return NULL;
+}
+
+static grn_obj *
+command_each(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data)
+{
+ const char *table = GRN_TEXT_VALUE(VAR(0));
+ unsigned int table_len = GRN_TEXT_LEN(VAR(0));
+ const char *expr = GRN_TEXT_VALUE(VAR(1));
+ unsigned int expr_len = GRN_TEXT_LEN(VAR(1));
+ grn_obj *table_ = grn_ctx_get_table_by_name_or_id(ctx, table, table_len);
+ if (table_) {
+ grn_obj *v, *expr_;
+ GRN_EXPR_CREATE_FOR_QUERY(ctx, table_, expr_, v);
+ if (expr_ && v) {
+ grn_table_cursor *tc;
+ grn_expr_parse(ctx, expr_, expr, expr_len,
+ NULL, GRN_OP_MATCH, GRN_OP_AND,
+ GRN_EXPR_SYNTAX_SCRIPT|GRN_EXPR_ALLOW_UPDATE);
+ if ((tc = grn_table_cursor_open(ctx, table_, NULL, 0,
+ NULL, 0, 0, -1, 0))) {
+ grn_id id;
+ while ((id = grn_table_cursor_next(ctx, tc)) != GRN_ID_NIL) {
+ GRN_RECORD_SET(ctx, v, id);
+ grn_expr_exec(ctx, expr_, 0);
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ grn_obj_unlink(ctx, expr_);
+ }
+ }
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static grn_obj *
+command_unlink(grn_ctx *ctx, int nargs, grn_obj **args,
+ grn_user_data *user_data)
+{
+ const char *table = GRN_TEXT_VALUE(VAR(0));
+ unsigned int table_len = GRN_TEXT_LEN(VAR(0));
+ grn_obj *table_ = grn_ctx_get_table_by_name_or_id(ctx, table, table_len);
+ if (table_) {
+ grn_obj_unlink(ctx, table_);
+ }
+ GRN_OUTPUT_BOOL(!ctx->rc);
+ return NULL;
+}
+
+static grn_obj *
+command_add(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_load_(ctx, GRN_CONTENT_JSON,
+ GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)),
+ NULL, 0,
+ GRN_TEXT_VALUE(VAR(1)), GRN_TEXT_LEN(VAR(1)),
+ NULL, 0, NULL, 0, 0);
+ GRN_OUTPUT_BOOL(ctx->impl->loader.nrecords);
+ if (ctx->impl->loader.table) {
+ grn_db_touch(ctx, DB_OBJ(ctx->impl->loader.table)->db);
+ }
+ return NULL;
+}
+
+static grn_obj *
+command_set(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ int table_name_len = GRN_TEXT_LEN(VAR(0));
+ const char *table_name = GRN_TEXT_VALUE(VAR(0));
+ grn_obj *table = grn_ctx_get(ctx, table_name, table_name_len);
+ if (table) {
+ grn_id id = GRN_ID_NIL;
+ int key_len = GRN_TEXT_LEN(VAR(2));
+ int id_len = GRN_TEXT_LEN(VAR(5));
+ if (key_len) {
+ const char *key = GRN_TEXT_VALUE(VAR(2));
+ id = grn_table_get(ctx, table, key, key_len);
+ } else {
+ if (id_len) {
+ id = grn_atoui(GRN_TEXT_VALUE(VAR(5)), GRN_BULK_CURR(VAR(5)), NULL);
+ }
+ id = grn_table_at(ctx, table, id);
+ }
+ if (id) {
+ grn_obj obj;
+ grn_obj_format format;
+ GRN_RECORD_INIT(&obj, 0, ((grn_db_obj *)table)->id);
+ GRN_OBJ_FORMAT_INIT(&format, 1, 0, 1, 0);
+ GRN_RECORD_SET(ctx, &obj, id);
+ grn_obj_columns(ctx, table,
+ GRN_TEXT_VALUE(VAR(4)),
+ GRN_TEXT_LEN(VAR(4)), &format.columns);
+ format.flags = 0 /* GRN_OBJ_FORMAT_WITH_COLUMN_NAMES */;
+ GRN_OUTPUT_OBJ(&obj, &format);
+ GRN_OBJ_FORMAT_FIN(ctx, &format);
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT,
+ "nonexistent table name: <%.*s>", table_name_len, table_name);
+ }
+ return NULL;
+}
+
+static grn_rc
+command_get_resolve_parameters(grn_ctx *ctx, grn_user_data *user_data,
+ grn_obj **table, grn_id *id)
+{
+ const char *table_text, *id_text, *key_text;
+ int table_length, id_length, key_length;
+
+ table_text = GRN_TEXT_VALUE(VAR(0));
+ table_length = GRN_TEXT_LEN(VAR(0));
+ if (table_length == 0) {
+ ERR(GRN_INVALID_ARGUMENT, "[table][get] table isn't specified");
+ return ctx->rc;
+ }
+
+ *table = grn_ctx_get(ctx, table_text, table_length);
+ if (!*table) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][get] table doesn't exist: <%.*s>", table_length, table_text);
+ return ctx->rc;
+ }
+
+ key_text = GRN_TEXT_VALUE(VAR(1));
+ key_length = GRN_TEXT_LEN(VAR(1));
+ id_text = GRN_TEXT_VALUE(VAR(3));
+ id_length = GRN_TEXT_LEN(VAR(3));
+ switch ((*table)->header.type) {
+ case GRN_TABLE_NO_KEY:
+ if (key_length) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][get] should not specify key for NO_KEY table: <%.*s>: "
+ "table: <%.*s>",
+ key_length, key_text,
+ table_length, table_text);
+ return ctx->rc;
+ }
+ if (id_length) {
+ const char *rest = NULL;
+ *id = grn_atoi(id_text, id_text + id_length, &rest);
+ if (rest == id_text) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][get] ID should be a number: <%.*s>: table: <%.*s>",
+ id_length, id_text,
+ table_length, table_text);
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][get] ID isn't specified: table: <%.*s>",
+ table_length, table_text);
+ }
+ break;
+ case GRN_TABLE_HASH_KEY:
+ case GRN_TABLE_PAT_KEY:
+ case GRN_TABLE_DAT_KEY:
+ if (key_length && id_length) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][get] should not specify both key and ID: "
+ "key: <%.*s>: ID: <%.*s>: table: <%.*s>",
+ key_length, key_text,
+ id_length, id_text,
+ table_length, table_text);
+ return ctx->rc;
+ }
+ if (key_length) {
+ *id = grn_table_get(ctx, *table, key_text, key_length);
+ if (!*id) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][get] nonexistent key: <%.*s>: table: <%.*s>",
+ key_length, key_text,
+ table_length, table_text);
+ }
+ } else {
+ if (id_length) {
+ const char *rest = NULL;
+ *id = grn_atoi(id_text, id_text + id_length, &rest);
+ if (rest == id_text) {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][get] ID should be a number: <%.*s>: table: <%.*s>",
+ id_length, id_text,
+ table_length, table_text);
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][get] key nor ID isn't specified: table: <%.*s>",
+ table_length, table_text);
+ }
+ }
+ break;
+ default:
+ ERR(GRN_INVALID_ARGUMENT,
+ "[table][get] not a table: <%.*s>", table_length, table_text);
+ break;
+ }
+
+ return ctx->rc;
+}
+
+static grn_obj *
+command_get(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_id id = GRN_ID_NIL;
+ grn_obj *table = NULL;
+ if (!command_get_resolve_parameters(ctx, user_data, &table, &id)) {
+ grn_obj obj;
+ grn_obj_format format;
+ GRN_OUTPUT_ARRAY_OPEN("RESULT", 2);
+ GRN_RECORD_INIT(&obj, 0, ((grn_db_obj *)table)->id);
+ GRN_OBJ_FORMAT_INIT(&format, 1, 0, 1, 0);
+ GRN_RECORD_SET(ctx, &obj, id);
+ grn_obj_columns(ctx, table, GRN_TEXT_VALUE(VAR(2)), GRN_TEXT_LEN(VAR(2)),
+ &format.columns);
+ format.flags =
+ GRN_OBJ_FORMAT_WITH_COLUMN_NAMES |
+ GRN_OBJ_FORMAT_XML_ELEMENT_RESULTSET;
+ GRN_OUTPUT_OBJ(&obj, &format);
+ GRN_OBJ_FORMAT_FIN(ctx, &format);
+ GRN_OUTPUT_ARRAY_CLOSE();
+ }
+ return NULL;
+}
+
+static grn_obj *
+command_push(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *table = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)));
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_NO_KEY:
+ {
+ grn_array *array = (grn_array *)table;
+ grn_table_queue *queue = grn_array_queue(ctx, array);
+ if (queue) {
+ MUTEX_LOCK(queue->mutex);
+ if (grn_table_queue_head(queue) == queue->cap) {
+ grn_array_clear_curr_rec(ctx, array);
+ }
+ grn_load_(ctx, GRN_CONTENT_JSON,
+ GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)),
+ NULL, 0,
+ GRN_TEXT_VALUE(VAR(1)), GRN_TEXT_LEN(VAR(1)),
+ NULL, 0, NULL, 0, 0);
+ if (grn_table_queue_size(queue) == queue->cap) {
+ grn_table_queue_tail_increment(queue);
+ }
+ grn_table_queue_head_increment(queue);
+ COND_SIGNAL(queue->cond);
+ MUTEX_UNLOCK(queue->mutex);
+ GRN_OUTPUT_BOOL(ctx->impl->loader.nrecords);
+ if (ctx->impl->loader.table) {
+ grn_db_touch(ctx, DB_OBJ(ctx->impl->loader.table)->db);
+ }
+ } else {
+ ERR(GRN_OPERATION_NOT_SUPPORTED, "table '%.*s' doesn't support push",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)));
+ }
+ }
+ break;
+ default :
+ ERR(GRN_OPERATION_NOT_SUPPORTED, "table '%.*s' doesn't support push",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)));
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "table '%.*s' does not exist.",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)));
+ }
+ return NULL;
+}
+
+static grn_obj *
+command_pull(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_obj *table = grn_ctx_get(ctx, GRN_TEXT_VALUE(VAR(0)), GRN_TEXT_LEN(VAR(0)));
+ if (table) {
+ switch (table->header.type) {
+ case GRN_TABLE_NO_KEY:
+ {
+ grn_array *array = (grn_array *)table;
+ grn_table_queue *queue = grn_array_queue(ctx, array);
+ if (queue) {
+ MUTEX_LOCK(queue->mutex);
+ while (grn_table_queue_size(queue) == 0) {
+ if (GRN_TEXT_LEN(VAR(2))) {
+ MUTEX_UNLOCK(queue->mutex);
+ GRN_OUTPUT_BOOL(0);
+ return NULL;
+ }
+ COND_WAIT(queue->cond, queue->mutex);
+ }
+ grn_table_queue_tail_increment(queue);
+ {
+ grn_obj obj;
+ grn_obj_format format;
+ GRN_RECORD_INIT(&obj, 0, ((grn_db_obj *)table)->id);
+ GRN_OBJ_FORMAT_INIT(&format, 1, 0, 1, 0);
+ GRN_RECORD_SET(ctx, &obj, grn_table_queue_tail(queue));
+ grn_obj_columns(ctx, table, GRN_TEXT_VALUE(VAR(1)), GRN_TEXT_LEN(VAR(1)),
+ &format.columns);
+ format.flags = 0 /* GRN_OBJ_FORMAT_WITH_COLUMN_NAMES */;
+ GRN_OUTPUT_OBJ(&obj, &format);
+ GRN_OBJ_FORMAT_FIN(ctx, &format);
+ }
+ MUTEX_UNLOCK(queue->mutex);
+ } else {
+ ERR(GRN_OPERATION_NOT_SUPPORTED, "table '%.*s' doesn't support pull",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)));
+ }
+ }
+ break;
+ default :
+ ERR(GRN_OPERATION_NOT_SUPPORTED, "table '%.*s' doesn't support pull",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)));
+ }
+ } else {
+ ERR(GRN_INVALID_ARGUMENT, "table '%.*s' does not exist.",
+ (int)GRN_TEXT_LEN(VAR(0)), GRN_TEXT_VALUE(VAR(0)));
+ }
+ return NULL;
+}
+
+grn_rc
+GRN_PLUGIN_INIT(grn_ctx *ctx)
+{
+ return GRN_SUCCESS;
+}
+
+grn_rc
+GRN_PLUGIN_REGISTER(grn_ctx *ctx)
+{
+ grn_expr_var vars[18];
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "table", -1);
+ grn_plugin_expr_var_init(ctx, &vars[1], "expression", -1);
+ grn_plugin_expr_var_init(ctx, &vars[2], "result_set", -1);
+ grn_plugin_expr_var_init(ctx, &vars[3], "set_operation", -1);
+ grn_plugin_expr_var_init(ctx, &vars[4], "allow_update", -1);
+ grn_plugin_command_create(ctx, "filter_by_script", -1, command_filter_by_script, 5, vars);
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "table", -1);
+ grn_plugin_expr_var_init(ctx, &vars[1], "column", -1);
+ grn_plugin_expr_var_init(ctx, &vars[2], "operator", -1);
+ grn_plugin_expr_var_init(ctx, &vars[3], "value", -1);
+ grn_plugin_expr_var_init(ctx, &vars[4], "result_set", -1);
+ grn_plugin_expr_var_init(ctx, &vars[5], "set_operation", -1);
+ grn_plugin_command_create(ctx, "filter", -1, command_filter, 6, vars);
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "table", -1);
+ grn_plugin_expr_var_init(ctx, &vars[1], "key", -1);
+ grn_plugin_expr_var_init(ctx, &vars[2], "result_set", -1);
+ grn_plugin_expr_var_init(ctx, &vars[3], "range_gap", -1);
+ grn_plugin_command_create(ctx, "group", -1, command_group, 4, vars);
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "table", -1);
+ grn_plugin_expr_var_init(ctx, &vars[1], "keys", -1);
+ grn_plugin_expr_var_init(ctx, &vars[2], "offset", -1);
+ grn_plugin_expr_var_init(ctx, &vars[3], "limit", -1);
+ grn_plugin_command_create(ctx, "sort", -1, command_sort, 4, vars);
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "table", -1);
+ grn_plugin_expr_var_init(ctx, &vars[1], "columns", -1);
+ grn_plugin_expr_var_init(ctx, &vars[2], "offset", -1);
+ grn_plugin_expr_var_init(ctx, &vars[3], "limit", -1);
+ grn_plugin_command_create(ctx, "output", -1, command_output, 4, vars);
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "table", -1);
+ grn_plugin_expr_var_init(ctx, &vars[1], "expression", -1);
+ grn_plugin_command_create(ctx, "each", -1, command_each, 2, vars);
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "table", -1);
+ grn_plugin_command_create(ctx, "unlink", -1, command_unlink, 1, vars);
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "table", -1);
+ grn_plugin_expr_var_init(ctx, &vars[1], "values", -1);
+ grn_plugin_expr_var_init(ctx, &vars[2], "key", -1);
+ grn_plugin_expr_var_init(ctx, &vars[3], "columns", -1);
+ grn_plugin_expr_var_init(ctx, &vars[4], "output_columns", -1);
+ grn_plugin_expr_var_init(ctx, &vars[5], "id", -1);
+ grn_plugin_command_create(ctx, "add", -1, command_add, 2, vars);
+ grn_plugin_command_create(ctx, "push", -1, command_push, 2, vars);
+ grn_plugin_command_create(ctx, "set", -1, command_set, 6, vars);
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "table", -1);
+ grn_plugin_expr_var_init(ctx, &vars[1], "key", -1);
+ grn_plugin_expr_var_init(ctx, &vars[2], "output_columns", -1);
+ grn_plugin_expr_var_init(ctx, &vars[3], "id", -1);
+ grn_plugin_command_create(ctx, "get", -1, command_get, 4, vars);
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "table", -1);
+ grn_plugin_expr_var_init(ctx, &vars[1], "output_columns", -1);
+ grn_plugin_expr_var_init(ctx, &vars[2], "non_block", -1);
+ grn_plugin_command_create(ctx, "pull", -1, command_pull, 3, vars);
+
+ grn_plugin_expr_var_init(ctx, &vars[0], "table", -1);
+ grn_plugin_expr_var_init(ctx, &vars[1], "columns", -1);
+ grn_plugin_expr_var_init(ctx, &vars[2], "query", -1);
+ grn_plugin_expr_var_init(ctx, &vars[3], "result_set", -1);
+ grn_plugin_expr_var_init(ctx, &vars[4], "set_operation", -1);
+ grn_plugin_expr_var_init(ctx, &vars[5], "allow_column_expression", -1);
+ grn_plugin_expr_var_init(ctx, &vars[6], "allow_pragma", -1);
+ grn_plugin_command_create(ctx, "match", -1, command_match, 7, vars);
+
+ return ctx->rc;
+}
+
+grn_rc
+GRN_PLUGIN_FIN(grn_ctx *ctx)
+{
+ return GRN_SUCCESS;
+}
diff --git a/storage/mroonga/vendor/groonga/plugins/tokenizers/CMakeLists.txt b/storage/mroonga/vendor/groonga/plugins/tokenizers/CMakeLists.txt
new file mode 100644
index 00000000000..e044c1fbb93
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/tokenizers/CMakeLists.txt
@@ -0,0 +1,53 @@
+# Copyright(C) 2012-2013 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../lib
+ )
+
+set(TOKENIZERS_DIR "${GRN_RELATIVE_PLUGINS_DIR}/tokenizers")
+if(GRN_WITH_MECAB)
+ read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/mecab_sources.am MECAB_SOURCES)
+ include_directories(${MECAB_INCLUDE_DIRS})
+ link_directories(${MECAB_LIBRARY_DIRS})
+ add_library(mecab_tokenizer MODULE ${MECAB_SOURCES})
+ set_source_files_properties(${MECAB_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+ set_target_properties(mecab_tokenizer PROPERTIES
+ PREFIX ""
+ OUTPUT_NAME "mecab")
+ target_link_libraries(mecab_tokenizer libgroonga ${MECAB_LIBRARIES})
+ if(NOT MRN_GROONGA_BUNDLED)
+ install(TARGETS mecab_tokenizer DESTINATION "${TOKENIZERS_DIR}")
+ endif()
+endif()
+
+if(GRN_WITH_KYTEA)
+ read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/kytea_sources.am KYTEA_SOURCES)
+ include_directories(${KYTEA_INCLUDE_DIRS})
+ link_directories(${KYTEA_LIBRARY_DIRS})
+ add_library(kytea_tokenizer MODULE ${KYTEA_SOURCES})
+ set_source_files_properties(${KYTEA_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_CXX_COMPILE_FLAGS}")
+ set_target_properties(kytea_tokenizer PROPERTIES
+ PREFIX ""
+ OUTPUT_NAME "kytea")
+ target_link_libraries(kytea_tokenizer libgroonga ${KYTEA_LIBRARIES})
+ if(NOT MRN_GROONGA_BUNDLED)
+ install(TARGETS kytea_tokenizer DESTINATION "${TOKENIZERS_DIR}")
+ endif()
+endif()
diff --git a/storage/mroonga/vendor/groonga/plugins/tokenizers/Makefile.am b/storage/mroonga/vendor/groonga/plugins/tokenizers/Makefile.am
new file mode 100644
index 00000000000..386b1554f73
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/tokenizers/Makefile.am
@@ -0,0 +1,33 @@
+EXTRA_DIST = \
+ CMakeLists.txt
+
+AM_CPPFLAGS = \
+ -I$(top_builddir) \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib
+
+AM_LDFLAGS = \
+ -avoid-version \
+ -module \
+ -no-undefined
+
+LIBS = \
+ $(top_builddir)/lib/libgroonga.la
+
+tokenizers_plugins_LTLIBRARIES =
+if WITH_MECAB
+tokenizers_plugins_LTLIBRARIES += mecab.la
+endif
+if WITH_KYTEA
+tokenizers_plugins_LTLIBRARIES += kytea.la
+endif
+
+include mecab_sources.am
+mecab_la_CPPFLAGS = $(AM_CPPFLAGS) $(MECAB_CPPFLAGS)
+mecab_la_LIBADD = $(LIBS) $(MECAB_LIBS)
+mecab_la_LDFLAGS = $(AM_LDFLAGS) $(MECAB_LDFLAGS)
+
+include kytea_sources.am
+kytea_la_CPPFLAGS = $(AM_CPPFLAGS) $(KYTEA_CFLAGS)
+kytea_la_LIBADD = $(LIBS) $(KYTEA_LIBS)
+kytea_la_LDFLAGS = $(AM_LDFLAGS) $(KYTEA_LDFLAGS)
diff --git a/storage/mroonga/vendor/groonga/plugins/tokenizers/kytea.cpp b/storage/mroonga/vendor/groonga/plugins/tokenizers/kytea.cpp
new file mode 100644
index 00000000000..a7ee4104592
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/tokenizers/kytea.cpp
@@ -0,0 +1,354 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <groonga/tokenizer.h>
+
+#include <kytea/kytea.h>
+#include <kytea/string-util.h>
+
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+namespace {
+
+grn_plugin_mutex *kytea_mutex = NULL;
+kytea::KyteaConfig *kytea_config = NULL;
+kytea::Kytea *kytea_tagger = NULL;
+kytea::StringUtil *kytea_util = NULL;
+
+void kytea_init(grn_ctx *ctx);
+void kytea_fin(grn_ctx *ctx);
+
+void kytea_init(grn_ctx *ctx) {
+ if (kytea_mutex || kytea_config || kytea_tagger || kytea_util) {
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][kytea] "
+ "TokenKytea is already initialized");
+ return;
+ }
+
+ kytea_mutex = grn_plugin_mutex_open(ctx);
+ if (!kytea_mutex) {
+ kytea_fin(ctx);
+ GRN_PLUGIN_ERROR(ctx, GRN_NO_MEMORY_AVAILABLE,
+ "[tokenizer][kytea] "
+ "grn_plugin_mutex_open() failed");
+ return;
+ }
+
+ kytea::KyteaConfig * const config = static_cast<kytea::KyteaConfig *>(
+ GRN_PLUGIN_MALLOC(ctx, sizeof(kytea::KyteaConfig)));
+ if (!config) {
+ kytea_fin(ctx);
+ GRN_PLUGIN_ERROR(ctx, GRN_NO_MEMORY_AVAILABLE,
+ "[tokenizer][kytea] "
+ "memory allocation to kytea::KyteaConfig failed");
+ return;
+ }
+
+ try {
+ new (config) kytea::KyteaConfig;
+ kytea_config = config;
+ try {
+ kytea_config->setDebug(0);
+ kytea_config->setOnTraining(false);
+ kytea_config->parseRunCommandLine(0, NULL);
+ } catch (...) {
+ kytea_fin(ctx);
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][kytea] "
+ "kytea::KyteaConfig settings failed");
+ return;
+ }
+ } catch (...) {
+ GRN_PLUGIN_FREE(ctx, config);
+ kytea_fin(ctx);
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][kytea] "
+ "kytea::KyteaConfig initialization failed");
+ return;
+ }
+
+ kytea::Kytea * const tagger = static_cast<kytea::Kytea *>(
+ GRN_PLUGIN_MALLOC(ctx, sizeof(kytea::Kytea)));
+ if (!tagger) {
+ kytea_fin(ctx);
+ GRN_PLUGIN_ERROR(ctx, GRN_NO_MEMORY_AVAILABLE,
+ "[tokenizer][kytea] "
+ "memory allocation to kytea::Kytea failed");
+ return;
+ }
+
+ try {
+ new (tagger) kytea::Kytea;
+ kytea_tagger = tagger;
+ try {
+ kytea_tagger->readModel(kytea_config->getModelFile().c_str());
+ } catch (...) {
+ kytea_fin(ctx);
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][kytea] "
+ "kytea::Kytea::readModel() failed");
+ return;
+ }
+ } catch (...) {
+ GRN_PLUGIN_FREE(ctx, tagger);
+ kytea_fin(ctx);
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][kytea] "
+ "kytea::Kytea initialization failed");
+ return;
+ }
+
+ try {
+ kytea_util = kytea_tagger->getStringUtil();
+ } catch (...) {
+ kytea_fin(ctx);
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][kytea] "
+ "kytea::Kytea::getStringUtil() failed");
+ return;
+ }
+}
+
+void kytea_fin(grn_ctx *ctx) {
+ kytea_util = NULL;
+
+ if (kytea_tagger) {
+ kytea_tagger->~Kytea();
+ GRN_PLUGIN_FREE(ctx, kytea_tagger);
+ kytea_tagger = NULL;
+ }
+
+ if (kytea_config) {
+ kytea_config->~KyteaConfig();
+ GRN_PLUGIN_FREE(ctx, kytea_config);
+ kytea_config = NULL;
+ }
+
+ if (kytea_mutex) {
+ grn_plugin_mutex_close(ctx, kytea_mutex);
+ kytea_mutex = NULL;
+ }
+}
+
+struct grn_tokenizer_kytea {
+ grn_tokenizer_query *query;
+ kytea::KyteaSentence sentence;
+ std::vector<std::string> tokens;
+ std::size_t id;
+ grn_tokenizer_token token;
+ const char *rest_query_string;
+ unsigned int rest_query_string_length;
+
+ grn_tokenizer_kytea() :
+ query(NULL),
+ sentence(),
+ tokens(),
+ id(0),
+ token(),
+ rest_query_string(NULL)
+ {
+ }
+ ~grn_tokenizer_kytea() {}
+};
+
+void grn_tokenizer_kytea_init(grn_ctx *ctx, grn_tokenizer_kytea *tokenizer) {
+ new (tokenizer) grn_tokenizer_kytea;
+ grn_tokenizer_token_init(ctx, &tokenizer->token);
+}
+
+void grn_tokenizer_kytea_fin(grn_ctx *ctx, grn_tokenizer_kytea *tokenizer) {
+ grn_tokenizer_token_fin(ctx, &tokenizer->token);
+ if (tokenizer->query) {
+ grn_tokenizer_query_close(ctx, tokenizer->query);
+ }
+ tokenizer->~grn_tokenizer_kytea();
+}
+
+grn_obj *grn_kytea_init(grn_ctx *ctx, int num_args, grn_obj **args,
+ grn_user_data *user_data) {
+ unsigned int normalizer_flags = 0;
+ grn_tokenizer_query * const query =
+ grn_tokenizer_query_open(ctx, num_args, args, normalizer_flags);
+ if (!query) {
+ return NULL;
+ }
+
+ grn_tokenizer_kytea * const tokenizer = static_cast<grn_tokenizer_kytea *>(
+ GRN_PLUGIN_MALLOC(ctx, sizeof(grn_tokenizer_kytea)));
+ if (!tokenizer) {
+ grn_tokenizer_query_close(ctx, query);
+ GRN_PLUGIN_ERROR(ctx, GRN_NO_MEMORY_AVAILABLE,
+ "[tokenizer][kytea] "
+ "memory allocation to grn_tokenizer_kytea failed");
+ return NULL;
+ }
+
+ try {
+ grn_tokenizer_kytea_init(ctx, tokenizer);
+ } catch (...) {
+ grn_tokenizer_query_close(ctx, query);
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][kytea] "
+ "tokenizer initialization failed");
+ return NULL;
+ }
+
+ tokenizer->query = query;
+
+ grn_obj *normalized_query = query->normalized_query;
+ const char *normalized_string;
+ unsigned int normalized_string_length;
+ grn_string_get_normalized(ctx,
+ normalized_query,
+ &normalized_string,
+ &normalized_string_length,
+ NULL);
+ if (tokenizer->query->have_tokenized_delimiter) {
+ tokenizer->rest_query_string = normalized_string;
+ tokenizer->rest_query_string_length = normalized_string_length;
+ } else {
+ grn_plugin_mutex_lock(ctx, kytea_mutex);
+ try {
+ const std::string str(normalized_string, normalized_string_length);
+ const kytea::KyteaString &surface_str = kytea_util->mapString(str);
+ const kytea::KyteaString &normalized_str = kytea_util->normalize(surface_str);
+ tokenizer->sentence = kytea::KyteaSentence(surface_str, normalized_str);
+ kytea_tagger->calculateWS(tokenizer->sentence);
+ } catch (...) {
+ grn_plugin_mutex_unlock(ctx, kytea_mutex);
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][kytea] "
+ "tokenization failed");
+ return NULL;
+ }
+ grn_plugin_mutex_unlock(ctx, kytea_mutex);
+
+ try {
+ for (std::size_t i = 0; i < tokenizer->sentence.words.size(); ++i) {
+ const std::string &token =
+ kytea_util->showString(tokenizer->sentence.words[i].surface);
+ const char *ptr = token.c_str();
+ unsigned int left = static_cast<unsigned int>(token.length());
+ while (left > 0) {
+ const int char_length =
+ grn_tokenizer_charlen(ctx, ptr, left, query->encoding);
+ if ((char_length == 0) ||
+ (grn_tokenizer_isspace(ctx, ptr, left, query->encoding) != 0)) {
+ break;
+ }
+ ptr += char_length;
+ left -= char_length;
+ }
+ if (left == 0) {
+ tokenizer->tokens.push_back(token);
+ }
+ }
+ } catch (...) {
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][kytea] "
+ "adjustment failed");
+ return NULL;
+ }
+ }
+
+ user_data->ptr = tokenizer;
+ return NULL;
+}
+
+grn_obj *grn_kytea_next(grn_ctx *ctx, int num_args, grn_obj **args,
+ grn_user_data *user_data) {
+ grn_tokenizer_kytea * const tokenizer =
+ static_cast<grn_tokenizer_kytea *>(user_data->ptr);
+
+ if (tokenizer->query->have_tokenized_delimiter) {
+ unsigned int rest_query_string_length =
+ tokenizer->rest_query_string_length;
+ const char *rest_query_string =
+ grn_tokenizer_tokenized_delimiter_next(ctx,
+ &(tokenizer->token),
+ tokenizer->rest_query_string,
+ rest_query_string_length,
+ tokenizer->query->encoding);
+ if (rest_query_string) {
+ tokenizer->rest_query_string_length -=
+ rest_query_string - tokenizer->rest_query_string;
+ }
+ tokenizer->rest_query_string = rest_query_string;
+ } else {
+ const grn_tokenizer_status status =
+ ((tokenizer->id + 1) < tokenizer->tokens.size()) ?
+ GRN_TOKENIZER_CONTINUE : GRN_TOKENIZER_LAST;
+ if (tokenizer->id < tokenizer->tokens.size()) {
+ const std::string &token = tokenizer->tokens[tokenizer->id++];
+ grn_tokenizer_token_push(ctx, &tokenizer->token,
+ token.c_str(), token.length(), status);
+ } else {
+ grn_tokenizer_token_push(ctx, &tokenizer->token, "", 0, status);
+ }
+ }
+
+ return NULL;
+}
+
+grn_obj *grn_kytea_fin(grn_ctx *ctx, int num_args, grn_obj **args,
+ grn_user_data *user_data) {
+ grn_tokenizer_kytea * const tokenizer =
+ static_cast<grn_tokenizer_kytea *>(user_data->ptr);
+ if (tokenizer) {
+ grn_tokenizer_kytea_fin(ctx, tokenizer);
+ GRN_PLUGIN_FREE(ctx, tokenizer);
+ }
+ return NULL;
+}
+
+} // namespace
+
+extern "C" {
+
+/*
+ GRN_PLUGIN_INIT() is called to initialize this plugin. Note that an error
+ code must be set in `ctx->rc' on failure.
+ */
+grn_rc GRN_PLUGIN_INIT(grn_ctx *ctx) {
+ kytea_init(ctx);
+ return ctx->rc;
+}
+
+/*
+ GRN_PLUGIN_REGISTER() registers this plugin to the database associated with
+ `ctx'. The registration requires the plugin name and the functions to be
+ called for tokenization.
+ */
+grn_rc GRN_PLUGIN_REGISTER(grn_ctx *ctx) {
+ return grn_tokenizer_register(ctx, "TokenKytea", 10, grn_kytea_init,
+ grn_kytea_next, grn_kytea_fin);
+}
+
+/*
+ GRN_PLUGIN_FIN() is called to finalize the plugin that was initialized by
+ GRN_PLUGIN_INIT().
+ */
+grn_rc GRN_PLUGIN_FIN(grn_ctx *ctx) {
+ kytea_fin(ctx);
+ return GRN_SUCCESS;
+}
+
+} // extern "C"
diff --git a/storage/mroonga/vendor/groonga/plugins/tokenizers/kytea_sources.am b/storage/mroonga/vendor/groonga/plugins/tokenizers/kytea_sources.am
new file mode 100644
index 00000000000..182f38577e3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/tokenizers/kytea_sources.am
@@ -0,0 +1,2 @@
+kytea_la_SOURCES = \
+ kytea.cpp
diff --git a/storage/mroonga/vendor/groonga/plugins/tokenizers/mecab.c b/storage/mroonga/vendor/groonga/plugins/tokenizers/mecab.c
new file mode 100644
index 00000000000..4ac99f9a9e8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/tokenizers/mecab.c
@@ -0,0 +1,338 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <str.h>
+
+#include <groonga.h>
+#include <groonga/tokenizer.h>
+
+#include <mecab.h>
+
+#include <string.h>
+#include <ctype.h>
+
+static mecab_t *sole_mecab = NULL;
+static grn_plugin_mutex *sole_mecab_mutex = NULL;
+static grn_encoding sole_mecab_encoding = GRN_ENC_NONE;
+
+typedef struct {
+ mecab_t *mecab;
+ grn_obj buf;
+ const char *next;
+ const char *end;
+ grn_tokenizer_query *query;
+ grn_tokenizer_token token;
+} grn_mecab_tokenizer;
+
+static grn_encoding
+translate_mecab_charset_to_grn_encoding(const char *charset)
+{
+ if (strcasecmp(charset, "euc-jp") == 0) {
+ return GRN_ENC_EUC_JP;
+ } else if (strcasecmp(charset, "utf-8") == 0 ||
+ strcasecmp(charset, "utf8") == 0) {
+ return GRN_ENC_UTF8;
+ } else if (strcasecmp(charset, "shift_jis") == 0 ||
+ strcasecmp(charset, "shift-jis") == 0 ||
+ strcasecmp(charset, "sjis") == 0) {
+ return GRN_ENC_SJIS;
+ }
+ return GRN_ENC_NONE;
+}
+
+static grn_encoding
+get_mecab_encoding(mecab_t *mecab)
+{
+ grn_encoding encoding = GRN_ENC_NONE;
+ const mecab_dictionary_info_t *dictionary_info;
+ dictionary_info = mecab_dictionary_info(mecab);
+ if (dictionary_info) {
+ const char *charset = dictionary_info->charset;
+ encoding = translate_mecab_charset_to_grn_encoding(charset);
+ }
+ return encoding;
+}
+
+/*
+ This function is called for a full text search query or a document to be
+ indexed. This means that both short/long strings are given.
+ The return value of this function is ignored. When an error occurs in this
+ function, `ctx->rc' is overwritten with an error code (not GRN_SUCCESS).
+ */
+static grn_obj *
+mecab_init(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ const char *s;
+ grn_mecab_tokenizer *tokenizer;
+ unsigned int normalizer_flags = 0;
+ grn_tokenizer_query *query;
+ grn_obj *normalized_query;
+ const char *normalized_string;
+ unsigned int normalized_string_length;
+
+ query = grn_tokenizer_query_open(ctx, nargs, args, normalizer_flags);
+ if (!query) {
+ return NULL;
+ }
+ if (!sole_mecab) {
+ grn_plugin_mutex_lock(ctx, sole_mecab_mutex);
+ if (!sole_mecab) {
+ sole_mecab = mecab_new2("-Owakati");
+ if (!sole_mecab) {
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][mecab] "
+ "mecab_new2() failed on mecab_init(): %s",
+ mecab_strerror(NULL));
+ } else {
+ sole_mecab_encoding = get_mecab_encoding(sole_mecab);
+ }
+ }
+ grn_plugin_mutex_unlock(ctx, sole_mecab_mutex);
+ }
+ if (!sole_mecab) {
+ grn_tokenizer_query_close(ctx, query);
+ return NULL;
+ }
+
+ if (query->encoding != sole_mecab_encoding) {
+ grn_tokenizer_query_close(ctx, query);
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][mecab] "
+ "MeCab dictionary charset (%s) does not match "
+ "the table encoding: <%s>",
+ grn_encoding_to_string(sole_mecab_encoding),
+ grn_encoding_to_string(query->encoding));
+ return NULL;
+ }
+
+ if (!(tokenizer = GRN_PLUGIN_MALLOC(ctx, sizeof(grn_mecab_tokenizer)))) {
+ grn_tokenizer_query_close(ctx, query);
+ GRN_PLUGIN_ERROR(ctx, GRN_NO_MEMORY_AVAILABLE,
+ "[tokenizer][mecab] "
+ "memory allocation to grn_mecab_tokenizer failed");
+ return NULL;
+ }
+ tokenizer->mecab = sole_mecab;
+ tokenizer->query = query;
+
+ normalized_query = query->normalized_query;
+ grn_string_get_normalized(ctx,
+ normalized_query,
+ &normalized_string,
+ &normalized_string_length,
+ NULL);
+ GRN_TEXT_INIT(&(tokenizer->buf), 0);
+ if (query->have_tokenized_delimiter) {
+ tokenizer->next = normalized_string;
+ tokenizer->end = tokenizer->next + normalized_string_length;
+ } else if (normalized_string_length == 0) {
+ tokenizer->next = "";
+ tokenizer->end = tokenizer->next;
+ } else {
+ grn_plugin_mutex_lock(ctx, sole_mecab_mutex);
+ s = mecab_sparse_tostr2(tokenizer->mecab,
+ normalized_string,
+ normalized_string_length);
+ if (!s) {
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][mecab] "
+ "mecab_sparse_tostr() failed len=%d err=%s",
+ normalized_string_length,
+ mecab_strerror(tokenizer->mecab));
+ } else {
+ GRN_TEXT_PUTS(ctx, &(tokenizer->buf), s);
+ }
+ grn_plugin_mutex_unlock(ctx, sole_mecab_mutex);
+ if (!s) {
+ grn_tokenizer_query_close(ctx, tokenizer->query);
+ GRN_PLUGIN_FREE(ctx, tokenizer);
+ return NULL;
+ }
+ {
+ char *buf, *p;
+ unsigned int bufsize;
+
+ buf = GRN_TEXT_VALUE(&(tokenizer->buf));
+ bufsize = GRN_TEXT_LEN(&(tokenizer->buf));
+ /* A certain version of mecab returns trailing lf or spaces. */
+ for (p = buf + bufsize - 2;
+ buf <= p && isspace(*(unsigned char *)p);
+ p--) { *p = '\0'; }
+ tokenizer->next = buf;
+ tokenizer->end = p + 1;
+ }
+ }
+ user_data->ptr = tokenizer;
+
+ grn_tokenizer_token_init(ctx, &(tokenizer->token));
+
+ return NULL;
+}
+
+/*
+ This function returns tokens one by one.
+ */
+static grn_obj *
+mecab_next(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ /* grn_obj *table = args[0]; */
+ grn_mecab_tokenizer *tokenizer = user_data->ptr;
+ grn_encoding encoding = tokenizer->query->encoding;
+
+ if (tokenizer->query->have_tokenized_delimiter) {
+ tokenizer->next =
+ grn_tokenizer_tokenized_delimiter_next(ctx,
+ &(tokenizer->token),
+ tokenizer->next,
+ tokenizer->end - tokenizer->next,
+ encoding);
+ } else {
+ size_t cl;
+ const char *p = tokenizer->next, *r;
+ const char *e = tokenizer->end;
+ grn_tokenizer_status status;
+
+ for (r = p; r < e; r += cl) {
+ if (!(cl = grn_charlen_(ctx, r, e, encoding))) {
+ tokenizer->next = e;
+ break;
+ }
+ if (grn_isspace(r, encoding)) {
+ const char *q = r;
+ while ((cl = grn_isspace(q, encoding))) { q += cl; }
+ tokenizer->next = q;
+ break;
+ }
+ }
+
+ if (r == e) {
+ status = GRN_TOKENIZER_LAST;
+ } else {
+ status = GRN_TOKENIZER_CONTINUE;
+ }
+ grn_tokenizer_token_push(ctx, &(tokenizer->token), p, r - p, status);
+ }
+
+ return NULL;
+}
+
+/*
+ This function finalizes a tokenization.
+ */
+static grn_obj *
+mecab_fin(grn_ctx *ctx, int nargs, grn_obj **args, grn_user_data *user_data)
+{
+ grn_mecab_tokenizer *tokenizer = user_data->ptr;
+ if (!tokenizer) {
+ return NULL;
+ }
+ grn_tokenizer_token_fin(ctx, &(tokenizer->token));
+ grn_tokenizer_query_close(ctx, tokenizer->query);
+ grn_obj_unlink(ctx, &(tokenizer->buf));
+ GRN_PLUGIN_FREE(ctx, tokenizer);
+ return NULL;
+}
+
+static void
+check_mecab_dictionary_encoding(grn_ctx *ctx)
+{
+#ifdef HAVE_MECAB_DICTIONARY_INFO_T
+ mecab_t *mecab;
+
+ mecab = mecab_new2("-Owakati");
+ if (mecab) {
+ grn_encoding encoding;
+ int have_same_encoding_dictionary = 0;
+
+ encoding = GRN_CTX_GET_ENCODING(ctx);
+ have_same_encoding_dictionary = encoding == get_mecab_encoding(mecab);
+ mecab_destroy(mecab);
+
+ if (!have_same_encoding_dictionary) {
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][mecab] "
+ "MeCab has no dictionary that uses the context encoding"
+ ": <%s>",
+ grn_encoding_to_string(encoding));
+ }
+ } else {
+ GRN_PLUGIN_ERROR(ctx, GRN_TOKENIZER_ERROR,
+ "[tokenizer][mecab] "
+ "mecab_new2 failed in check_mecab_dictionary_encoding: %s",
+ mecab_strerror(NULL));
+ }
+#endif
+}
+
+/*
+ This function initializes a plugin. This function fails if there is no
+ dictionary that uses the context encoding of groonga.
+ */
+grn_rc
+GRN_PLUGIN_INIT(grn_ctx *ctx)
+{
+ sole_mecab = NULL;
+ sole_mecab_mutex = grn_plugin_mutex_open(ctx);
+ if (!sole_mecab_mutex) {
+ GRN_PLUGIN_ERROR(ctx, GRN_NO_MEMORY_AVAILABLE,
+ "[tokenizer][mecab] grn_plugin_mutex_open() failed");
+ return ctx->rc;
+ }
+
+ check_mecab_dictionary_encoding(ctx);
+ return ctx->rc;
+}
+
+/*
+ This function registers a plugin to a database.
+ */
+grn_rc
+GRN_PLUGIN_REGISTER(grn_ctx *ctx)
+{
+ grn_rc rc;
+
+ rc = grn_tokenizer_register(ctx, "TokenMecab", 10,
+ mecab_init, mecab_next, mecab_fin);
+ if (rc == GRN_SUCCESS) {
+ grn_obj *token_mecab;
+ token_mecab = grn_ctx_get(ctx, "TokenMecab", 10);
+ /* Just for backward compatibility. TokenMecab was built-in not plugin. */
+ if (token_mecab && grn_obj_id(ctx, token_mecab) != GRN_DB_MECAB) {
+ rc = GRN_FILE_CORRUPT;
+ }
+ }
+
+ return rc;
+}
+
+/*
+ This function finalizes a plugin.
+ */
+grn_rc
+GRN_PLUGIN_FIN(grn_ctx *ctx)
+{
+ if (sole_mecab) {
+ mecab_destroy(sole_mecab);
+ sole_mecab = NULL;
+ }
+ if (sole_mecab_mutex) {
+ grn_plugin_mutex_close(ctx, sole_mecab_mutex);
+ sole_mecab_mutex = NULL;
+ }
+
+ return GRN_SUCCESS;
+}
diff --git a/storage/mroonga/vendor/groonga/plugins/tokenizers/mecab_sources.am b/storage/mroonga/vendor/groonga/plugins/tokenizers/mecab_sources.am
new file mode 100644
index 00000000000..56912727c3d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/plugins/tokenizers/mecab_sources.am
@@ -0,0 +1,2 @@
+mecab_la_SOURCES = \
+ mecab.c
diff --git a/storage/mroonga/vendor/groonga/src/.gitignore b/storage/mroonga/vendor/groonga/src/.gitignore
new file mode 100644
index 00000000000..a8278d75b4b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/.gitignore
@@ -0,0 +1,2 @@
+/grntest
+/*.exe
diff --git a/storage/mroonga/vendor/groonga/src/CMakeLists.txt b/storage/mroonga/vendor/groonga/src/CMakeLists.txt
new file mode 100644
index 00000000000..4d02109dae5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/CMakeLists.txt
@@ -0,0 +1,54 @@
+# Copyright(C) 2012-2013 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+include_directories(
+ ${MRUBY_INCLUDE_DIRS}
+ )
+
+add_subdirectory(suggest)
+
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/groonga_sources.am GROONGA_SOURCES)
+add_executable(groonga ${GROONGA_SOURCES})
+set_source_files_properties(${GROONGA_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+target_link_libraries(groonga libgroonga)
+if(NOT MRN_GROONGA_BUNDLED)
+ install(TARGETS groonga DESTINATION ${BIN_DIR})
+endif()
+
+if(NOT WIN32)
+ read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/grnslap_sources.am GRNSLAP_SOURCES)
+ add_executable(grnslap ${GRNSLAP_SOURCES})
+ set_source_files_properties(${GRNSLAP_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+ target_link_libraries(grnslap libgroonga)
+ if(NOT MRN_GROONGA_BUNDLED)
+ install(TARGETS grnslap DESTINATION ${BIN_DIR})
+ endif()
+endif()
+
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/groonga_benchmark_sources.am
+ GROONGA_BENCHMARK_SOURCES)
+add_executable(groonga-benchmark ${GROONGA_BENCHMARK_SOURCES})
+set_source_files_properties(${GROONGA_BENCHMARK_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+target_link_libraries(groonga-benchmark libgroonga)
+if(NOT MRN_GROONGA_BUNDLED)
+ install(TARGETS groonga-benchmark DESTINATION ${BIN_DIR})
+endif()
+
diff --git a/storage/mroonga/vendor/groonga/src/Makefile.am b/storage/mroonga/vendor/groonga/src/Makefile.am
new file mode 100644
index 00000000000..b125db54c3d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/Makefile.am
@@ -0,0 +1,48 @@
+SUBDIRS = \
+ suggest \
+ httpd
+
+NONEXISTENT_CXX_SOURCE = nonexistent.cpp
+
+bin_PROGRAMS = groonga groonga-benchmark
+noinst_PROGRAMS = grnslap
+
+EXTRA_DIST = \
+ CMakeLists.txt
+
+AM_CFLAGS = \
+ $(NO_STRICT_ALIASING_CFLAGS) \
+ $(COVERAGE_CFLAGS) \
+ $(GRN_CFLAGS) \
+ $(MESSAGE_PACK_CFLAGS) \
+ $(MRUBY_CFLAGS)
+DEFS += $(GRN_DEFS)
+
+AM_LDFLAGS = -no-undefined
+
+DEFAULT_INCLUDES = \
+ -I$(top_builddir) \
+ -I$(srcdir) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/include \
+ $(GROONGA_INCLUDEDIR)
+
+include groonga_sources.am
+nodist_EXTRA_groonga_SOURCES = $(NONEXISTENT_CXX_SOURCE)
+groonga_CFLAGS = $(AM_CFLAGS) $(LIBEDIT_CFLAGS)
+groonga_LDADD = \
+ $(top_builddir)/lib/libgroonga.la \
+ $(LIBEDIT_LIBS) \
+ $(MESSAGE_PACK_LIBS)
+
+include grnslap_sources.am
+nodist_EXTRA_grnslap_SOURCES = $(NONEXISTENT_CXX_SOURCE)
+grnslap_LDADD = \
+ $(top_builddir)/lib/libgroonga.la \
+ $(MESSAGE_PACK_LIBS)
+
+include groonga_benchmark_sources.am
+nodist_EXTRA_groonga_benchmark_SOURCES = $(NONEXISTENT_CXX_SOURCE)
+groonga_benchmark_LDADD = \
+ $(top_builddir)/lib/libgroonga.la \
+ $(MESSAGE_PACK_LIBS)
diff --git a/storage/mroonga/vendor/groonga/src/grnslap.c b/storage/mroonga/vendor/groonga/src/grnslap.c
new file mode 100644
index 00000000000..5400fbb23e0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/grnslap.c
@@ -0,0 +1,373 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2009-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "lib/com.h"
+#include "lib/ctx_impl.h"
+#include <string.h>
+#include <stdio.h>
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif /* HAVE_SYS_WAIT_H */
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#define DEFAULT_PORT 10041
+#define DEFAULT_HOST "localhost"
+#define DEFAULT_MAX_CONCURRENCY 10
+#define DEFAULT_MAX_THROUGHPUT 10000
+#define MAX_DEST 256
+
+typedef struct {
+ const char *host;
+ uint16_t port;
+} grn_slap_dest;
+
+static int proto = 'g';
+static int verbose = 0;
+static int dest_cnt = 0;
+static grn_slap_dest dests[MAX_DEST];
+static int max_con = DEFAULT_MAX_CONCURRENCY;
+static int max_tp = DEFAULT_MAX_THROUGHPUT;
+
+#include <stdarg.h>
+static void
+lprint(grn_ctx *ctx, const char *fmt, ...)
+{
+ char buf[1024];
+ grn_timeval tv;
+ int len;
+ va_list argp;
+ grn_timeval_now(ctx, &tv);
+ grn_timeval2str(ctx, &tv, buf);
+ len = strlen(buf);
+ buf[len++] = '|';
+ va_start(argp, fmt);
+ vsnprintf(buf + len, 1023 - len, fmt, argp);
+ va_end(argp);
+ buf[1023] = '\0';
+ puts(buf);
+}
+
+static void
+parse_dest(char *deststr, grn_slap_dest *dest)
+{
+ int p;
+ char *d;
+ if ((d = strchr(deststr, ':'))) {
+ if ((p = atoi(d + 1))) {
+ *d = '\0';
+ dest->host = deststr;
+ dest->port = p;
+ return;
+ }
+ }
+ dest->host = NULL;
+ dest->port = 0;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "Usage: grnslap [options...] [dest...]\n"
+ "options:\n"
+ " -P <protocol>: http or gqtp (default: gqtp)\n"
+ " -m <max concurrency>: number of max concurrency (default: %d)\n"
+ "dest: hostname:port number (default: \"%s:%d\")\n",
+ DEFAULT_MAX_CONCURRENCY, DEFAULT_HOST, DEFAULT_PORT);
+}
+
+#define BUFSIZE 0x1000000
+
+typedef struct _session session;
+
+struct _session {
+ grn_com_queue_entry eq;
+ grn_com *com;
+ struct timeval tv;
+ grn_id id;
+ int stat;
+ int query_id;
+ int n_query;
+ int n_sessions;
+};
+
+static grn_com_event ev;
+static grn_com_queue fsessions;
+static grn_hash *sessions;
+static int done = 0;
+static int nsent = 0;
+static int nrecv = 0;
+static int etime_min = INT32_MAX;
+static int etime_max = 0;
+static int64_t etime_amount = 0;
+
+static session *
+session_open(grn_ctx *ctx, grn_slap_dest *dest)
+{
+ grn_id id;
+ session *s;
+ grn_com *com;
+ if (!(com = grn_com_copen(ctx, &ev, dest->host, dest->port))) { return NULL; }
+ id = grn_hash_add(ctx, sessions, &com->fd, sizeof(grn_sock), (void **)&s, NULL);
+ com->opaque = s;
+ s->com = com;
+ s->id = id;
+ s->stat = 1;
+ return s;
+}
+
+static void
+session_close(grn_ctx *ctx, session *s)
+{
+ if (!s->stat) { return; }
+ grn_com_close(ctx, s->com);
+ s->stat = 0;
+ grn_hash_delete_by_id(ctx, sessions, s->id, NULL);
+}
+
+static session *
+session_alloc(grn_ctx *ctx, grn_slap_dest *dest)
+{
+ session *s;
+ while ((s = (session *)grn_com_queue_deque(ctx, &fsessions))) {
+ if (s->n_query < 1000000 && !s->com->closed) { return s; }
+ //session_close(ctx, s);
+ }
+ return session_open(ctx, dest);
+}
+
+static void
+msg_handler(grn_ctx *ctx, grn_obj *msg)
+{
+ uint32_t etime;
+ struct timeval tv;
+ grn_msg *m = (grn_msg *)msg;
+ grn_com *com = ((grn_msg *)msg)->u.peer;
+ session *s = com->opaque;
+ s->stat = 3;
+ gettimeofday(&tv, NULL);
+ etime = (tv.tv_sec - s->tv.tv_sec) * 1000000 + (tv.tv_usec - s->tv.tv_usec);
+ if (etime > etime_max) { etime_max = etime; }
+ if (etime < etime_min) { etime_min = etime; }
+ if (ctx->rc) { m->header.proto = 0; }
+ switch (m->header.proto) {
+ case GRN_COM_PROTO_GQTP :
+ if (GRN_BULK_VSIZE(msg) == 2) {
+ etime_amount += etime;
+ } else {
+ if (verbose) {
+ GRN_TEXT_PUTC(ctx, msg, '\0');
+ lprint(ctx, "%8d(%4d) %8d : %s", s->query_id, s->n_sessions, etime, GRN_BULK_HEAD(msg));
+ }
+ }
+ if ((m->header.flags & GRN_CTX_TAIL)) {
+ grn_com_queue_enque(ctx, &fsessions, (grn_com_queue_entry *)s);
+ nrecv++;
+ }
+ break;
+ case GRN_COM_PROTO_HTTP :
+ nrecv++;
+ /* lprint(ctx, "recv: %d, %d", (int)GRN_BULK_VSIZE(msg), nrecv); */
+ grn_com_close_(ctx, com);
+ grn_com_queue_enque(ctx, &fsessions, (grn_com_queue_entry *)s);
+ break;
+ default :
+ grn_com_close_(ctx, com);
+ grn_com_queue_enque(ctx, &fsessions, (grn_com_queue_entry *)s);
+ break;
+ }
+ grn_msg_close(ctx, msg);
+}
+
+static void * CALLBACK
+receiver(void *arg)
+{
+ grn_ctx ctx_, *ctx = &ctx_;
+ grn_ctx_init(ctx, 0);
+ while (!grn_com_event_poll(ctx, &ev, 100)) {
+ if (nsent == nrecv && done) { break; }
+ /*
+ {
+ session *s;
+ GRN_HASH_EACH(ctx, sessions, id, NULL, NULL, &s, {
+ printf("id=%d: fd=%d stat=%d q=%d n=%d\n", s->id, s->com->fd, s->stat, s->query_id, s->n_query);
+ });
+ }
+ */
+ }
+ grn_ctx_fin(ctx);
+ return NULL;
+}
+
+static int
+do_client()
+{
+ int rc = -1;
+ grn_obj text;
+ grn_thread thread;
+ struct timeval tvb, tve;
+ grn_com_header sheader;
+ grn_ctx ctx_, *ctx = &ctx_;
+ grn_ctx_init(ctx, 0);
+ GRN_COM_QUEUE_INIT(&fsessions);
+ sessions = grn_hash_create(ctx, NULL, sizeof(grn_sock), sizeof(session), 0);
+ sheader.proto = GRN_COM_PROTO_GQTP;
+ sheader.qtype = 0;
+ sheader.keylen = 0;
+ sheader.level = 0;
+ sheader.flags = 0;
+ sheader.status = 0;
+ sheader.opaque = 0;
+ sheader.cas = 0;
+ GRN_TEXT_INIT(&text, 0);
+ rc = grn_bulk_reserve(ctx, &text, BUFSIZE);
+ if (!rc) {
+ char *buf = GRN_TEXT_VALUE(&text);
+ if (!grn_com_event_init(ctx, &ev, 1000, sizeof(grn_com))) {
+ ev.msg_handler = msg_handler;
+ if (!THREAD_CREATE(thread, receiver, NULL)) {
+ int cnt = 0;
+ gettimeofday(&tvb, NULL);
+ lprint(ctx, "begin: procotol=%c max_concurrency=%d max_tp=%d", proto, max_con, max_tp);
+ while (fgets(buf, BUFSIZE, stdin)) {
+ uint32_t size = strlen(buf) - 1;
+ session *s = session_alloc(ctx, dests + (cnt++ % dest_cnt));
+ if (s) {
+ gettimeofday(&s->tv, NULL);
+ s->n_query++;
+ s->query_id = ++nsent;
+ s->n_sessions = (nsent - nrecv);
+ switch (proto) {
+ case 'H' :
+ case 'h' :
+ if (grn_com_send_http(ctx, s->com, buf, size, 0)) {
+ fprintf(stderr, "grn_com_send_http failed\n");
+ }
+ s->stat = 2;
+ /*
+ lprint(ctx, "sent %04d %04d %d",
+ s->n_query, s->query_id, s->com->fd);
+ */
+ break;
+ default :
+ if (grn_com_send(ctx, s->com, &sheader, buf, size, 0)) {
+ fprintf(stderr, "grn_com_send failed\n");
+ }
+ break;
+ }
+ } else {
+ fprintf(stderr, "grn_com_copen failed\n");
+ }
+ for (;;) {
+ gettimeofday(&tve, NULL);
+ if ((nrecv < max_tp * (tve.tv_sec - tvb.tv_sec)) &&
+ (nsent - nrecv) < max_con) { break; }
+ /* lprint(ctx, "s:%d r:%d", nsent, nrecv); */
+ grn_nanosleep(1000000);
+ }
+ if (!(nsent % 1000)) { lprint(ctx, " : %d", nsent); }
+ }
+ done = 1;
+ if (THREAD_JOIN(thread)) {
+ fprintf(stderr, "THREAD_JOIN failed\n");
+ }
+ gettimeofday(&tve, NULL);
+ {
+ double qps;
+ uint64_t etime = (tve.tv_sec - tvb.tv_sec);
+ etime *= 1000000;
+ etime += (tve.tv_usec - tvb.tv_usec);
+ qps = (double)nsent * 1000000 / etime;
+ lprint(ctx, "end : n=%d min=%d max=%d avg=%d qps=%f etime=%d.%06d", nsent, etime_min, etime_max, (int)(etime_amount / nsent), qps, etime / 1000000, etime % 1000000);
+ }
+ {
+ session *s;
+ GRN_HASH_EACH(ctx, sessions, id, NULL, NULL, &s, {
+ session_close(ctx, s);
+ });
+ }
+ rc = 0;
+ } else {
+ fprintf(stderr, "THREAD_CREATE failed\n");
+ }
+ grn_com_event_fin(ctx, &ev);
+ } else {
+ fprintf(stderr, "grn_com_event_init failed\n");
+ }
+ }
+ grn_obj_unlink(ctx, &text);
+ grn_hash_close(ctx, sessions);
+ grn_ctx_fin(ctx);
+ return rc;
+}
+
+enum {
+ flag_usage = 1,
+ flag_verbose = 2
+};
+
+int
+main(int argc, char **argv)
+{
+ const char *protostr = NULL, *maxconstr = NULL, *maxtpstr = NULL;
+ int r, i, flags = 0;
+ static grn_str_getopt_opt opts[] = {
+ {'P', NULL, NULL, 0, GETOPT_OP_NONE},
+ {'m', NULL, NULL, 0, GETOPT_OP_NONE},
+ {'t', NULL, NULL, 0, GETOPT_OP_NONE},
+ {'h', NULL, NULL, flag_usage, GETOPT_OP_ON},
+ {'v', NULL, NULL, flag_verbose, GETOPT_OP_ON},
+ {'\0', NULL, NULL, 0, 0}
+ };
+ opts[0].arg = &protostr;
+ opts[1].arg = &maxconstr;
+ opts[2].arg = &maxtpstr;
+ i = grn_str_getopt(argc, argv, opts, &flags);
+ if (protostr) { proto = *protostr; }
+ if (maxconstr) { max_con = atoi(maxconstr); }
+ if (maxtpstr) { max_tp = atoi(maxtpstr); }
+ if (flags & flag_verbose) { verbose = 1; }
+
+ if (argc <= i) {
+ dests[0].host = DEFAULT_HOST;
+ dests[0].port = DEFAULT_PORT;
+ dest_cnt = 1;
+ } else if (i > 0 && argc <= (i + MAX_DEST)){
+ for (dest_cnt = 0; i < argc; i++) {
+ parse_dest(argv[i], &dests[dest_cnt]);
+ if (dests[dest_cnt].host) {
+ dest_cnt++;
+ }
+ }
+ if (!dest_cnt) { flags |= flag_usage; }
+ } else {
+ /* too much dests */
+ flags |= flag_usage;
+ }
+
+ if (grn_init()) { return -1; }
+ if (flags & flag_usage) {
+ usage(); r = -1;
+ } else {
+ r = do_client();
+ }
+ grn_fin();
+ return r;
+}
diff --git a/storage/mroonga/vendor/groonga/src/grnslap_sources.am b/storage/mroonga/vendor/groonga/src/grnslap_sources.am
new file mode 100644
index 00000000000..244f5bfc8f5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/grnslap_sources.am
@@ -0,0 +1,2 @@
+grnslap_SOURCES = \
+ grnslap.c
diff --git a/storage/mroonga/vendor/groonga/src/groonga.c b/storage/mroonga/vendor/groonga/src/groonga.c
new file mode 100644
index 00000000000..b62c462c21e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/groonga.c
@@ -0,0 +1,2819 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2009-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifdef WIN32
+# define GROONGA_MAIN
+#endif /* WIN32 */
+#include "lib/groonga_in.h"
+
+#include "lib/com.h"
+#include "lib/ctx_impl.h"
+#include "lib/proc.h"
+#include "lib/db.h"
+#include "lib/util.h"
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif /* HAVE_SYS_WAIT_H */
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+#endif /* HAVE_SYS_RESOURCE_H */
+
+#ifdef HAVE_SYS_SYSCTL_H
+# include <sys/sysctl.h>
+#endif /* HAVE_SYS_SYSCTL_H */
+
+#ifdef HAVE__STRNICMP
+# define strncasecmp(s1,s2,n) _strnicmp(s1,s2,n)
+#endif /* HAVE__STRNICMP */
+
+#ifndef USE_MSG_NOSIGNAL
+# ifdef MSG_NOSIGNAL
+# undef MSG_NOSIGNAL
+# endif
+# define MSG_NOSIGNAL 0
+#endif /* USE_MSG_NOSIGNAL */
+
+#ifndef STDIN_FILENO
+# define STDIN_FILENO 0
+#endif /* STDIN_FILENO */
+#ifndef STDOUT_FILENO
+# define STDOUT_FILENO 1
+#endif /* STDOUT_FILENO */
+#ifndef STDERR_FILENO
+# define STDERR_FILENO 2
+#endif /* STDERR_FILENO */
+
+#define DEFAULT_HTTP_PORT 10041
+#define DEFAULT_GQTP_PORT 10043
+#define DEFAULT_DEST "localhost"
+#define DEFAULT_MAX_NFTHREADS 8
+#define MAX_CON 0x10000
+
+#define RLIMIT_NOFILE_MINIMUM 4096
+
+static char bind_address[HOST_NAME_MAX + 1];
+static char hostname[HOST_NAME_MAX + 1];
+static int port = DEFAULT_GQTP_PORT;
+static int batchmode;
+static int number_of_lines = 0;
+static int newdb;
+static grn_bool is_daemon_mode = GRN_FALSE;
+static int (*do_client)(int argc, char **argv);
+static int (*do_server)(char *path);
+static const char *pid_file_path = NULL;
+static const char *input_path = NULL;
+static FILE *output = NULL;
+
+static int ready_notify_pipe[2];
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+
+static grn_encoding encoding;
+static grn_command_version default_command_version;
+static int64_t default_match_escalation_threshold;
+static int log_level;
+
+static int
+grn_rc_to_exit_code(grn_rc rc)
+{
+ if (rc == GRN_SUCCESS) {
+ return EXIT_SUCCESS;
+ } else {
+ return EXIT_FAILURE;
+ }
+}
+
+#ifdef GRN_WITH_LIBEDIT
+#include <locale.h>
+#include <histedit.h>
+static EditLine *line_editor = NULL;
+static HistoryW *line_editor_history = NULL;
+static HistEventW line_editor_history_event;
+static char line_editor_history_path[PATH_MAX] = "";
+
+static const wchar_t *
+line_editor_prompt(EditLine *e __attribute__((unused)))
+{
+ return L"> ";
+}
+static const wchar_t * const line_editor_editor = L"emacs";
+
+static void
+line_editor_init(int argc __attribute__((unused)), char *argv[])
+{
+ const char * const HOME_PATH = getenv("HOME");
+ const char * const HISTORY_PATH = "/.groonga-history";
+
+ setlocale(LC_ALL, "");
+
+ if (strlen(HOME_PATH) + strlen(HISTORY_PATH) < PATH_MAX) {
+ strcpy(line_editor_history_path, HOME_PATH);
+ strcat(line_editor_history_path, HISTORY_PATH);
+ } else {
+ line_editor_history_path[0] = '\0';
+ }
+
+ line_editor_history = history_winit();
+ history_w(line_editor_history, &line_editor_history_event, H_SETSIZE, 200);
+ if (line_editor_history_path[0]) {
+ history_w(line_editor_history, &line_editor_history_event,
+ H_LOAD, line_editor_history_path);
+ }
+
+ line_editor = el_init(argv[0], stdin, stdout, stderr);
+ el_wset(line_editor, EL_PROMPT, &line_editor_prompt);
+ el_wset(line_editor, EL_EDITOR, line_editor_editor);
+ el_wset(line_editor, EL_HIST, history_w, line_editor_history);
+ el_source(line_editor, NULL);
+}
+
+static void
+line_editor_fin(void)
+{
+ if (line_editor) {
+ el_end(line_editor);
+ if (line_editor_history) {
+ if (line_editor_history_path[0]) {
+ history_w(line_editor_history, &line_editor_history_event,
+ H_SAVE, line_editor_history_path);
+ }
+ history_wend(line_editor_history);
+ }
+ }
+}
+
+static grn_rc
+line_editor_fgets(grn_ctx *ctx, grn_obj *buf)
+{
+ grn_rc rc = GRN_SUCCESS;
+ const wchar_t *line;
+ int nchar;
+ line = el_wgets(line_editor, &nchar);
+ if (nchar > 0) {
+ int i;
+ char multibyte_buf[MB_CUR_MAX];
+ size_t multibyte_len;
+ mbstate_t ps;
+ history_w(line_editor_history, &line_editor_history_event, H_ENTER, line);
+ memset(&ps, 0, sizeof(ps));
+ wcrtomb(NULL, L'\0', &ps);
+ for (i = 0; i < nchar; i++) {
+ multibyte_len = wcrtomb(multibyte_buf, line[i], &ps);
+ if (multibyte_len == (size_t)-1) {
+ GRN_LOG(ctx, GRN_LOG_WARNING,
+ "[prompt][libedit] failed to read input: %s", strerror(errno));
+ rc = GRN_INVALID_ARGUMENT;
+ } else {
+ GRN_TEXT_PUT(ctx, buf, multibyte_buf, multibyte_len);
+ }
+ }
+ } else {
+ rc = GRN_END_OF_DATA;
+ }
+ return rc;
+}
+#endif /* GRN_WITH_LIBEDIT */
+
+inline static grn_rc
+read_next_line(grn_ctx *ctx, grn_obj *buf)
+{
+ static int the_first_read = GRN_TRUE;
+ grn_rc rc = GRN_SUCCESS;
+ if (!batchmode) {
+#ifdef GRN_WITH_LIBEDIT
+ rc = line_editor_fgets(ctx, buf);
+#else
+ fprintf(stderr, "> ");
+ rc = grn_text_fgets(ctx, buf, stdin);
+#endif
+ } else {
+ rc = grn_text_fgets(ctx, buf, stdin);
+ if (rc != GRN_END_OF_DATA) {
+ number_of_lines++;
+ }
+ }
+ if (the_first_read && GRN_TEXT_LEN(buf) > 0) {
+ const char bom[] = {0xef, 0xbb, 0xbf};
+ if (GRN_CTX_GET_ENCODING(ctx) == GRN_ENC_UTF8 &&
+ GRN_TEXT_LEN(buf) > 3 && !memcmp(GRN_TEXT_VALUE(buf), bom, 3)) {
+ grn_obj buf_without_bom;
+ GRN_TEXT_INIT(&buf_without_bom, 0);
+ GRN_TEXT_PUT(ctx, &buf_without_bom,
+ GRN_TEXT_VALUE(buf) + 3, GRN_TEXT_LEN(buf) - 3);
+ GRN_TEXT_SET(ctx, buf,
+ GRN_TEXT_VALUE(&buf_without_bom),
+ GRN_TEXT_LEN(&buf_without_bom));
+ grn_obj_unlink(ctx, &buf_without_bom);
+ }
+ the_first_read = GRN_FALSE;
+ }
+ if (GRN_TEXT_LEN(buf) > 0 &&
+ GRN_TEXT_VALUE(buf)[GRN_TEXT_LEN(buf) - 1] == '\n') {
+ grn_bulk_truncate(ctx, buf, GRN_TEXT_LEN(buf) - 1);
+ }
+ if (GRN_TEXT_LEN(buf) > 0 &&
+ GRN_TEXT_VALUE(buf)[GRN_TEXT_LEN(buf) - 1] == '\r') {
+ grn_bulk_truncate(ctx, buf, GRN_TEXT_LEN(buf) - 1);
+ }
+ return rc;
+}
+
+inline static grn_rc
+prompt(grn_ctx *ctx, grn_obj *buf)
+{
+ grn_rc rc = GRN_SUCCESS;
+ grn_bool need_next_line = GRN_TRUE;
+ GRN_BULK_REWIND(buf);
+ while (need_next_line) {
+ rc = read_next_line(ctx, buf);
+ if (rc == GRN_SUCCESS &&
+ GRN_TEXT_LEN(buf) > 0 &&
+ GRN_TEXT_VALUE(buf)[GRN_TEXT_LEN(buf) - 1] == '\\') {
+ grn_bulk_truncate(ctx, buf, GRN_TEXT_LEN(buf) - 1);
+ need_next_line = GRN_TRUE;
+ } else {
+ need_next_line = GRN_FALSE;
+ }
+ }
+ return rc;
+}
+
+static void
+output_envelope(grn_ctx *ctx, grn_rc rc, grn_obj *head, grn_obj *body, grn_obj *foot)
+{
+ grn_output_envelope(ctx, rc, head, body, foot, input_path, number_of_lines);
+}
+
+static void
+s_output(grn_ctx *ctx, int flags, void *arg)
+{
+ if (ctx && ctx->impl && (flags & GRN_CTX_TAIL)) {
+ grn_obj *buf = ctx->impl->outbuf;
+ grn_obj *command;
+ if (GRN_TEXT_LEN(buf) || ctx->rc) {
+ FILE * stream = (FILE *) arg;
+ grn_obj head, foot;
+ GRN_TEXT_INIT(&head, 0);
+ GRN_TEXT_INIT(&foot, 0);
+ output_envelope(ctx, ctx->rc, &head, buf, &foot);
+ fwrite(GRN_TEXT_VALUE(&head), 1, GRN_TEXT_LEN(&head), stream);
+ fwrite(GRN_TEXT_VALUE(buf), 1, GRN_TEXT_LEN(buf), stream);
+ fwrite(GRN_TEXT_VALUE(&foot), 1, GRN_TEXT_LEN(&foot), stream);
+ fputc('\n', stream);
+ fflush(stream);
+ GRN_BULK_REWIND(buf);
+ GRN_OBJ_FIN(ctx, &head);
+ GRN_OBJ_FIN(ctx, &foot);
+ }
+ command = GRN_CTX_USER_DATA(ctx)->ptr;
+ GRN_BULK_REWIND(command);
+ }
+}
+
+static int
+do_alone(int argc, char **argv)
+{
+ int exit_code = EXIT_FAILURE;
+ char *path = NULL;
+ grn_obj *db;
+ grn_ctx ctx_, *ctx = &ctx_;
+ grn_ctx_init(ctx, 0);
+ if (argc > 0 && argv) { path = *argv++; argc--; }
+ db = (newdb || !path) ? grn_db_create(ctx, path, NULL) : grn_db_open(ctx, path);
+ if (db) {
+ grn_obj command;
+ GRN_TEXT_INIT(&command, 0);
+ GRN_CTX_USER_DATA(ctx)->ptr = &command;
+ grn_ctx_recv_handler_set(ctx, s_output, output);
+ if (!argc) {
+ grn_obj text;
+ GRN_TEXT_INIT(&text, 0);
+ while (prompt(ctx, &text) != GRN_END_OF_DATA) {
+ GRN_TEXT_PUT(ctx, &command, GRN_TEXT_VALUE(&text), GRN_TEXT_LEN(&text));
+ grn_ctx_send(ctx, GRN_TEXT_VALUE(&text), GRN_TEXT_LEN(&text), 0);
+ if (ctx->stat == GRN_CTX_QUIT) { break; }
+ }
+ exit_code = grn_rc_to_exit_code(ctx->rc);
+ grn_obj_unlink(ctx, &text);
+ } else {
+ grn_rc rc;
+ rc = grn_ctx_sendv(ctx, argc, argv, 0);
+ exit_code = grn_rc_to_exit_code(rc);
+ }
+ grn_obj_unlink(ctx, &command);
+ grn_obj_close(ctx, db);
+ } else {
+ fprintf(stderr, "db open failed (%s): %s\n", path, ctx->errbuf);
+ }
+ grn_ctx_fin(ctx);
+ return exit_code;
+}
+
+static int
+c_output(grn_ctx *ctx)
+{
+ int flags;
+ char *str;
+ unsigned int str_len;
+ do {
+ grn_ctx_recv(ctx, &str, &str_len, &flags);
+ /*
+ if (ctx->rc) {
+ fprintf(stderr, "grn_ctx_recv failed\n");
+ return -1;
+ }
+ */
+ if (str_len || ctx->rc) {
+ grn_obj head, body, foot;
+ GRN_TEXT_INIT(&head, 0);
+ GRN_TEXT_INIT(&body, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_INIT(&foot, 0);
+ if (ctx->rc == GRN_SUCCESS) {
+ GRN_TEXT_SET(ctx, &body, str, str_len);
+ } else {
+ ERR(ctx->rc, "%.*s", str_len, str);
+ }
+ output_envelope(ctx, ctx->rc, &head, &body, &foot);
+ fwrite(GRN_TEXT_VALUE(&head), 1, GRN_TEXT_LEN(&head), output);
+ fwrite(GRN_TEXT_VALUE(&body), 1, GRN_TEXT_LEN(&body), output);
+ fwrite(GRN_TEXT_VALUE(&foot), 1, GRN_TEXT_LEN(&foot), output);
+ fputc('\n', output);
+ fflush(output);
+ GRN_OBJ_FIN(ctx, &head);
+ GRN_OBJ_FIN(ctx, &body);
+ GRN_OBJ_FIN(ctx, &foot);
+ }
+ } while ((flags & GRN_CTX_MORE));
+ return 0;
+}
+
+static int
+g_client(int argc, char **argv)
+{
+ int exit_code = EXIT_FAILURE;
+ grn_ctx ctx_, *ctx = &ctx_;
+ const char *hostname = DEFAULT_DEST;
+ if (argc > 0 && argv) { hostname = *argv++; argc--; }
+ grn_ctx_init(ctx, 0);
+ if (!grn_ctx_connect(ctx, hostname, port, 0)) {
+ if (!argc) {
+ grn_obj text;
+ GRN_TEXT_INIT(&text, 0);
+ while (prompt(ctx, &text) != GRN_END_OF_DATA) {
+ grn_ctx_send(ctx, GRN_TEXT_VALUE(&text), GRN_TEXT_LEN(&text), 0);
+ exit_code = grn_rc_to_exit_code(ctx->rc);
+ if (ctx->rc != GRN_SUCCESS) { break; }
+ if (c_output(ctx)) { goto exit; }
+ if (ctx->stat == GRN_CTX_QUIT) { break; }
+ }
+ grn_obj_unlink(ctx, &text);
+ } else {
+ grn_rc rc;
+ rc = grn_ctx_sendv(ctx, argc, argv, 0);
+ exit_code = grn_rc_to_exit_code(rc);
+ if (c_output(ctx)) { goto exit; }
+ }
+ } else {
+ fprintf(stderr, "grn_ctx_connect failed (%s:%d)\n", hostname, port);
+ }
+exit :
+ grn_ctx_fin(ctx);
+ return exit_code;
+}
+
+/* server */
+
+typedef void (*grn_edge_dispatcher_func)(grn_ctx *ctx, grn_edge *edge);
+typedef void (*grn_handler_func)(grn_ctx *ctx, grn_obj *msg);
+
+static grn_com_queue ctx_new;
+static grn_com_queue ctx_old;
+static grn_mutex q_mutex;
+static grn_cond q_cond;
+static uint32_t nthreads = 0, nfthreads = 0, max_nfthreads;
+
+static void
+reset_ready_notify_pipe(void)
+{
+ ready_notify_pipe[PIPE_READ] = 0;
+ ready_notify_pipe[PIPE_WRITE] = 0;
+}
+
+static void
+close_ready_notify_pipe(void)
+{
+ if (ready_notify_pipe[PIPE_READ] > 0) {
+ close(ready_notify_pipe[PIPE_READ]);
+ }
+ if (ready_notify_pipe[PIPE_WRITE] > 0) {
+ close(ready_notify_pipe[PIPE_WRITE]);
+ }
+ reset_ready_notify_pipe();
+}
+
+static void
+send_ready_notify(void)
+{
+ if (ready_notify_pipe[PIPE_WRITE] > 0) {
+ const char *ready_notify_message = "ready";
+ write(ready_notify_pipe[PIPE_WRITE],
+ ready_notify_message,
+ strlen(ready_notify_message));
+ }
+ close_ready_notify_pipe();
+}
+
+static int
+daemonize(void)
+{
+ int exit_code = EXIT_SUCCESS;
+#ifndef WIN32
+ pid_t pid;
+
+ if (pipe(ready_notify_pipe) == -1) {
+ reset_ready_notify_pipe();
+ }
+
+ switch (fork()) {
+ case 0:
+ break;
+ case -1:
+ perror("fork");
+ return EXIT_FAILURE;
+ default:
+ wait(NULL);
+ if (ready_notify_pipe[PIPE_READ] > 0) {
+ int max_fd;
+ fd_set read_fds;
+ FD_ZERO(&read_fds);
+ FD_SET(ready_notify_pipe[PIPE_READ], &read_fds);
+ max_fd = ready_notify_pipe[PIPE_READ] + 1;
+ select(max_fd, &read_fds, NULL, NULL, NULL);
+ }
+ close_ready_notify_pipe();
+ _exit(EXIT_SUCCESS);
+ }
+ switch (fork()) {
+ case 0:
+ {
+ FILE *pid_file = NULL;
+ if (pid_file_path) {
+ pid_file = fopen(pid_file_path, "w");
+ }
+ pid = getpid();
+ if (!pid_file) {
+ fprintf(stderr, "%d\n", pid);
+ } else {
+ fprintf(pid_file, "%d\n", pid);
+ fclose(pid_file);
+ pid_file = NULL;
+ }
+ }
+ break;
+ case -1:
+ perror("fork");
+ return EXIT_FAILURE;
+ default:
+ close_ready_notify_pipe();
+ _exit(EXIT_SUCCESS);
+ }
+ {
+ int null_fd = GRN_OPEN("/dev/null", O_RDWR, 0);
+ if (null_fd != -1) {
+ dup2(null_fd, STDIN_FILENO);
+ dup2(null_fd, STDOUT_FILENO);
+ dup2(null_fd, STDERR_FILENO);
+ if (null_fd > STDERR_FILENO) { GRN_CLOSE(null_fd); }
+ }
+ }
+#endif /* WIN32 */
+ return exit_code;
+}
+
+static void
+clean_pid_file(void)
+{
+#ifndef WIN32
+ if (pid_file_path) {
+ unlink(pid_file_path);
+ }
+#endif
+}
+
+static void
+run_server_loop(grn_ctx *ctx, grn_com_event *ev)
+{
+ while (!grn_com_event_poll(ctx, ev, 1000) && grn_gctx.stat != GRN_CTX_QUIT) {
+ grn_edge *edge;
+ while ((edge = (grn_edge *)grn_com_queue_deque(ctx, &ctx_old))) {
+ grn_obj *msg;
+ while ((msg = (grn_obj *)grn_com_queue_deque(ctx, &edge->send_old))) {
+ grn_msg_close(&edge->ctx, msg);
+ }
+ while ((msg = (grn_obj *)grn_com_queue_deque(ctx, &edge->recv_new))) {
+ grn_msg_close(ctx, msg);
+ }
+ grn_ctx_fin(&edge->ctx);
+ if (edge->com->has_sid && edge->com->opaque == edge) {
+ grn_com_close(ctx, edge->com);
+ }
+ grn_edges_delete(ctx, edge);
+ }
+ /* todo : log stat */
+ }
+ for (;;) {
+ MUTEX_LOCK(q_mutex);
+ if (nthreads == nfthreads) { break; }
+ MUTEX_UNLOCK(q_mutex);
+ grn_nanosleep(1000000);
+ }
+ {
+ grn_edge *edge;
+ GRN_HASH_EACH(ctx, grn_edges, id, NULL, NULL, &edge, {
+ grn_obj *obj;
+ while ((obj = (grn_obj *)grn_com_queue_deque(ctx, &edge->send_old))) {
+ grn_msg_close(&edge->ctx, obj);
+ }
+ while ((obj = (grn_obj *)grn_com_queue_deque(ctx, &edge->recv_new))) {
+ grn_msg_close(ctx, obj);
+ }
+ grn_ctx_fin(&edge->ctx);
+ if (edge->com->has_sid) {
+ grn_com_close(ctx, edge->com);
+ }
+ grn_edges_delete(ctx, edge);
+ });
+ }
+ {
+ grn_com *com;
+ GRN_HASH_EACH(ctx, ev->hash, id, NULL, NULL, &com, { grn_com_close(ctx, com); });
+ }
+}
+
+static int
+run_server(grn_ctx *ctx, grn_obj *db, grn_com_event *ev,
+ grn_edge_dispatcher_func dispatcher, grn_handler_func handler)
+{
+ int exit_code = EXIT_SUCCESS;
+ struct hostent *he;
+ if (!(he = gethostbyname(hostname))) {
+ send_ready_notify();
+ SERR("gethostbyname");
+ } else {
+ ev->opaque = db;
+ grn_edges_init(ctx, dispatcher);
+ if (!grn_com_sopen(ctx, ev, bind_address, port, handler, he)) {
+ send_ready_notify();
+ run_server_loop(ctx, ev);
+ exit_code = EXIT_SUCCESS;
+ } else {
+ send_ready_notify();
+ fprintf(stderr, "grn_com_sopen failed (%s:%d): %s\n",
+ bind_address, port, ctx->errbuf);
+ }
+ grn_edges_fin(ctx);
+ }
+ return exit_code;
+}
+
+static int
+start_service(grn_ctx *ctx, const char *db_path,
+ grn_edge_dispatcher_func dispatcher, grn_handler_func handler)
+{
+ int exit_code = EXIT_SUCCESS;
+ grn_com_event ev;
+
+ if (is_daemon_mode) {
+ exit_code = daemonize();
+ if (exit_code != EXIT_SUCCESS) {
+ return exit_code;
+ }
+ }
+
+ if (!grn_com_event_init(ctx, &ev, MAX_CON, sizeof(grn_com))) {
+ grn_obj *db;
+ db = (newdb || !db_path) ? grn_db_create(ctx, db_path, NULL) : grn_db_open(ctx, db_path);
+ if (db) {
+ exit_code = run_server(ctx, db, &ev, dispatcher, handler);
+ grn_obj_close(ctx, db);
+ } else {
+ fprintf(stderr, "db open failed (%s)\n", db_path);
+ exit_code = EXIT_FAILURE;
+ send_ready_notify();
+ }
+ grn_com_event_fin(ctx, &ev);
+ } else {
+ fprintf(stderr, "grn_com_event_init failed\n");
+ exit_code = EXIT_FAILURE;
+ send_ready_notify();
+ }
+
+ if (is_daemon_mode) {
+ clean_pid_file();
+ }
+
+ return exit_code;
+}
+
+typedef struct {
+ grn_msg *msg;
+} ht_context;
+
+static void
+h_output(grn_ctx *ctx, int flags, void *arg)
+{
+ grn_rc expr_rc = ctx->rc;
+ ht_context *hc = (ht_context *)arg;
+ grn_sock fd = hc->msg->u.fd;
+ grn_obj header, head, foot, *outbuf = ctx->impl->outbuf;
+ if (!(flags & GRN_CTX_TAIL)) { return; }
+ GRN_TEXT_INIT(&header, 0);
+ GRN_TEXT_INIT(&head, 0);
+ GRN_TEXT_INIT(&foot, 0);
+ output_envelope(ctx, expr_rc, &head, outbuf, &foot);
+ switch (expr_rc) {
+ case GRN_SUCCESS :
+ GRN_TEXT_SETS(ctx, &header, "HTTP/1.1 200 OK\r\n");
+ break;
+ case GRN_INVALID_ARGUMENT :
+ case GRN_SYNTAX_ERROR :
+ GRN_TEXT_SETS(ctx, &header, "HTTP/1.1 400 Bad Request\r\n");
+ break;
+ case GRN_NO_SUCH_FILE_OR_DIRECTORY :
+ GRN_TEXT_SETS(ctx, &header, "HTTP/1.1 404 Not Found\r\n");
+ break;
+ default :
+ GRN_TEXT_SETS(ctx, &header, "HTTP/1.1 500 Internal Server Error\r\n");
+ break;
+ }
+ GRN_TEXT_PUTS(ctx, &header, "Connection: close\r\n");
+ GRN_TEXT_PUTS(ctx, &header, "Content-Type: ");
+ GRN_TEXT_PUTS(ctx, &header, grn_ctx_get_mime_type(ctx));
+ GRN_TEXT_PUTS(ctx, &header, "\r\nContent-Length: ");
+ grn_text_lltoa(ctx, &header,
+ GRN_TEXT_LEN(&head) + GRN_TEXT_LEN(outbuf) + GRN_TEXT_LEN(&foot));
+ GRN_TEXT_PUTS(ctx, &header, "\r\n\r\n");
+ {
+ ssize_t ret, len;
+#ifdef WIN32
+ WSABUF wsabufs[4];
+ wsabufs[0].buf = GRN_TEXT_VALUE(&header);
+ wsabufs[0].len = GRN_TEXT_LEN(&header);
+ wsabufs[1].buf = GRN_TEXT_VALUE(&head);
+ wsabufs[1].len = GRN_TEXT_LEN(&head);
+ wsabufs[2].buf = GRN_TEXT_VALUE(outbuf);
+ wsabufs[2].len = GRN_TEXT_LEN(outbuf);
+ wsabufs[3].buf = GRN_TEXT_VALUE(&foot);
+ wsabufs[3].len = GRN_TEXT_LEN(&foot);
+ if (WSASend(fd, wsabufs, 4, &ret, 0, NULL, NULL) == SOCKET_ERROR) {
+ SERR("WSASend");
+ }
+#else /* WIN32 */
+ struct iovec msg_iov[4];
+ struct msghdr msg;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = msg_iov;
+ msg.msg_iovlen = 4;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ msg_iov[0].iov_base = GRN_TEXT_VALUE(&header);
+ msg_iov[0].iov_len = GRN_TEXT_LEN(&header);
+ msg_iov[1].iov_base = GRN_TEXT_VALUE(&head);
+ msg_iov[1].iov_len = GRN_TEXT_LEN(&head);
+ msg_iov[2].iov_base = GRN_TEXT_VALUE(outbuf);
+ msg_iov[2].iov_len = GRN_TEXT_LEN(outbuf);
+ msg_iov[3].iov_base = GRN_TEXT_VALUE(&foot);
+ msg_iov[3].iov_len = GRN_TEXT_LEN(&foot);
+ if ((ret = sendmsg(fd, &msg, MSG_NOSIGNAL)) == -1) {
+ SERR("sendmsg");
+ }
+#endif /* WIN32 */
+ len = GRN_TEXT_LEN(&header) + GRN_TEXT_LEN(&head) +
+ GRN_TEXT_LEN(outbuf) + GRN_TEXT_LEN(&foot);
+ if (ret != len) {
+ GRN_LOG(&grn_gctx, GRN_LOG_NOTICE,
+ "couldn't send all data (%" GRN_FMT_LLD "/%" GRN_FMT_LLD ")",
+ (long long int)ret, (long long int)len);
+ }
+ }
+ GRN_BULK_REWIND(outbuf);
+ GRN_OBJ_FIN(ctx, &foot);
+ GRN_OBJ_FIN(ctx, &head);
+ GRN_OBJ_FIN(ctx, &header);
+}
+
+static void
+do_htreq_get(grn_ctx *ctx, grn_msg *msg)
+{
+ char *path = NULL;
+ char *pathe = GRN_BULK_HEAD((grn_obj *)msg);
+ char *e = GRN_BULK_CURR((grn_obj *)msg);
+ for (;; pathe++) {
+ if (e <= pathe + 6) {
+ /* invalid request */
+ return;
+ }
+ if (*pathe == ' ') {
+ if (!path) {
+ path = pathe + 1;
+ } else {
+ if (!memcmp(pathe + 1, "HTTP/1", 6)) {
+ break;
+ }
+ }
+ }
+ }
+ grn_ctx_send(ctx, path, pathe - path, 0);
+}
+
+typedef struct {
+ const char *path_start;
+ int path_length;
+ int content_length;
+ grn_bool have_100_continue;
+ const char *body_start;
+} h_post_header;
+
+#define STRING_EQUAL(string, string_length, constant_string)\
+ (string_length == strlen(constant_string) &&\
+ strncmp(string, constant_string, string_length) == 0)
+
+#define STRING_EQUAL_CI(string, string_length, constant_string)\
+ (string_length == strlen(constant_string) &&\
+ strncasecmp(string, constant_string, string_length) == 0)
+
+static const char *
+do_htreq_post_parse_header_request_line(grn_ctx *ctx,
+ const char *start,
+ const char *end,
+ h_post_header *header)
+{
+ const char *current;
+
+ {
+ const char *method = start;
+ int method_length = -1;
+
+ for (current = method; current < end; current++) {
+ if (current[0] == '\n') {
+ return NULL;
+ }
+ if (current[0] == ' ') {
+ method_length = current - method;
+ current++;
+ break;
+ }
+ }
+ if (method_length == -1) {
+ return NULL;
+ }
+ if (!STRING_EQUAL_CI(method, method_length, "POST")) {
+ return NULL;
+ }
+ }
+
+ {
+ header->path_start = current;
+ header->path_length = -1;
+ for (; current < end; current++) {
+ if (current[0] == '\n') {
+ return NULL;
+ }
+ if (current[0] == ' ') {
+ header->path_length = current - header->path_start;
+ current++;
+ break;
+ }
+ }
+ if (header->path_length == -1) {
+ return NULL;
+ }
+ }
+
+ {
+ const char *http_version_start = current;
+ int http_version_length = -1;
+ for (; current < end; current++) {
+ if (current[0] == '\n') {
+ http_version_length = current - http_version_start;
+ if (http_version_length > 0 &&
+ http_version_start[http_version_length - 1] == '\r') {
+ http_version_length--;
+ }
+ current++;
+ break;
+ }
+ }
+ if (http_version_length == -1) {
+ return NULL;
+ }
+ if (!(STRING_EQUAL_CI(http_version_start, http_version_length, "HTTP/1.0") ||
+ STRING_EQUAL_CI(http_version_start, http_version_length, "HTTP/1.1"))) {
+ return NULL;
+ }
+ }
+
+ return current;
+}
+
+static const char *
+do_htreq_post_parse_header_values(grn_ctx *ctx,
+ const char *start,
+ const char *end,
+ h_post_header *header)
+{
+ const char *current;
+ const char *name = start;
+ int name_length = -1;
+ const char *value = NULL;
+ int value_length = -1;
+
+ for (current = start; current < end; current++) {
+ switch (current[0]) {
+ case '\n' :
+ if (name_length == -1) {
+ if (current - name == 1 && current[-1] == '\r') {
+ return current + 1;
+ } else {
+ /* No ":" header line. TODO: report error. */
+ return NULL;
+ }
+ } else {
+ while (value < current && value[0] == ' ') {
+ value++;
+ }
+ value_length = current - value;
+ if (value_length > 0 && value[value_length - 1] == '\r') {
+ value_length--;
+ }
+ if (STRING_EQUAL_CI(name, name_length, "Content-Length")) {
+ const char *rest;
+ header->content_length = grn_atoi(value, value + value_length, &rest);
+ if (rest != value + value_length) {
+ /* Invalid Content-Length value. TODO: report error. */
+ header->content_length = -1;
+ }
+ } else if (STRING_EQUAL_CI(name, name_length, "Expect")) {
+ if (STRING_EQUAL(value, value_length, "100-continue")) {
+ header->have_100_continue = GRN_TRUE;
+ }
+ }
+ }
+ name = current + 1;
+ name_length = -1;
+ value = NULL;
+ value_length = -1;
+ break;
+ case ':' :
+ if (name_length == -1) {
+ name_length = current - name;
+ value = current + 1;
+ }
+ break;
+ default :
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+static grn_bool
+do_htreq_post_parse_header(grn_ctx *ctx,
+ const char *start,
+ const char *end,
+ h_post_header *header)
+{
+ const char *current;
+
+ current = do_htreq_post_parse_header_request_line(ctx, start, end, header);
+ if (!current) {
+ return GRN_FALSE;
+ }
+ current = do_htreq_post_parse_header_values(ctx, current, end, header);
+ if (!current) {
+ return GRN_FALSE;
+ }
+
+ if (!header->have_100_continue && current == end) {
+ return GRN_FALSE;
+ }
+
+ if (current == end) {
+ header->body_start = NULL;
+ } else {
+ header->body_start = current;
+ }
+
+ return GRN_TRUE;
+}
+
+static void
+do_htreq_post(grn_ctx *ctx, grn_msg *msg)
+{
+ grn_sock fd = msg->u.fd;
+ const char *end;
+ h_post_header header;
+
+ header.path_start = NULL;
+ header.path_length = -1;
+ header.content_length = -1;
+ header.body_start = NULL;
+ header.have_100_continue = GRN_FALSE;
+
+ end = GRN_BULK_CURR((grn_obj *)msg);
+ if (!do_htreq_post_parse_header(ctx,
+ GRN_BULK_HEAD((grn_obj *)msg),
+ end,
+ &header)) {
+ return;
+ }
+
+ grn_ctx_send(ctx, header.path_start, header.path_length, GRN_CTX_QUIET);
+ if (ctx->rc != GRN_SUCCESS) {
+ ht_context context;
+ context.msg = msg;
+ h_output(ctx, GRN_CTX_TAIL, &context);
+ return;
+ }
+
+ if (header.have_100_continue) {
+ const char *continue_message = "HTTP/1.1 100 Continue\r\n";
+ ssize_t send_size;
+ int send_flags = MSG_NOSIGNAL;
+ send_size = send(fd, continue_message, strlen(continue_message), send_flags);
+ if (send_size == -1) {
+ SERR("send");
+ return;
+ }
+ }
+
+ {
+ grn_obj line_buffer;
+ int read_content_length = 0;
+
+ GRN_TEXT_INIT(&line_buffer, 0);
+ while (read_content_length < header.content_length) {
+#define POST_BUFFER_SIZE 8192
+ grn_rc rc;
+ char buffer[POST_BUFFER_SIZE];
+ const char *buffer_start, *buffer_current, *buffer_end;
+
+ if (header.body_start) {
+ buffer_start = header.body_start;
+ buffer_end = end;
+ header.body_start = NULL;
+ } else {
+ ssize_t recv_length;
+ int recv_flags = 0;
+ recv_length = recv(fd, buffer, POST_BUFFER_SIZE, recv_flags);
+ if (recv_length == 0) {
+ break;
+ }
+ if (recv_length == -1) {
+ SERR("recv");
+ break;
+ }
+ buffer_start = buffer;
+ buffer_end = buffer_start + recv_length;
+ }
+ read_content_length += buffer_end - buffer_start;
+
+ rc = GRN_SUCCESS;
+ buffer_current = buffer_start;
+ for (; rc == GRN_SUCCESS && buffer_current < buffer_end; buffer_current++) {
+ if (buffer_current[0] != '\n') {
+ continue;
+ }
+ GRN_TEXT_PUT(ctx,
+ &line_buffer,
+ buffer_start,
+ buffer_current - buffer_start);
+ {
+ int flags = 0;
+ if (!(read_content_length == header.content_length &&
+ buffer_current + 1 == buffer_end)) {
+ flags |= GRN_CTX_QUIET;
+ }
+ rc = grn_ctx_send(ctx,
+ GRN_TEXT_VALUE(&line_buffer),
+ GRN_TEXT_LEN(&line_buffer),
+ flags);
+ }
+ buffer_start = buffer_current + 1;
+ GRN_BULK_REWIND(&line_buffer);
+ }
+ GRN_TEXT_PUT(ctx, &line_buffer, buffer_start, buffer_end - buffer_start);
+#undef POST_BUFFER_SIZE
+ }
+
+ if (GRN_TEXT_LEN(&line_buffer) > 0) {
+ grn_ctx_send(ctx,
+ GRN_TEXT_VALUE(&line_buffer),
+ GRN_TEXT_LEN(&line_buffer),
+ 0);
+ }
+
+ GRN_OBJ_FIN(ctx, &line_buffer);
+ }
+}
+
+static void
+do_htreq(grn_ctx *ctx, grn_msg *msg)
+{
+ grn_com_header *header = &msg->header;
+ switch (header->qtype) {
+ case 'G' : /* GET */
+ do_htreq_get(ctx, msg);
+ break;
+ case 'P' : /* POST */
+ do_htreq_post(ctx, msg);
+ break;
+ }
+ /* TODO: support "Connection: keep-alive" */
+ ctx->stat = GRN_CTX_QUIT;
+ /* TODO: support a command in multi requests. e.g.: load command */
+ grn_ctx_set_next_expr(ctx, NULL);
+ /* if (ctx->rc != GRN_OPERATION_WOULD_BLOCK) {...} */
+ grn_msg_close(ctx, (grn_obj *)msg);
+ /* if not keep alive connection */
+ grn_sock_close(msg->u.fd);
+ grn_com_event_start_accept(ctx, msg->acceptor->ev);
+}
+
+enum {
+ MBRES_SUCCESS = 0x00,
+ MBRES_KEY_ENOENT = 0x01,
+ MBRES_KEY_EEXISTS = 0x02,
+ MBRES_E2BIG = 0x03,
+ MBRES_EINVAL = 0x04,
+ MBRES_NOT_STORED = 0x05,
+ MBRES_UNKNOWN_COMMAND = 0x81,
+ MBRES_ENOMEM = 0x82,
+};
+
+enum {
+ MBCMD_GET = 0x00,
+ MBCMD_SET = 0x01,
+ MBCMD_ADD = 0x02,
+ MBCMD_REPLACE = 0x03,
+ MBCMD_DELETE = 0x04,
+ MBCMD_INCREMENT = 0x05,
+ MBCMD_DECREMENT = 0x06,
+ MBCMD_QUIT = 0x07,
+ MBCMD_FLUSH = 0x08,
+ MBCMD_GETQ = 0x09,
+ MBCMD_NOOP = 0x0a,
+ MBCMD_VERSION = 0x0b,
+ MBCMD_GETK = 0x0c,
+ MBCMD_GETKQ = 0x0d,
+ MBCMD_APPEND = 0x0e,
+ MBCMD_PREPEND = 0x0f,
+ MBCMD_STAT = 0x10,
+ MBCMD_SETQ = 0x11,
+ MBCMD_ADDQ = 0x12,
+ MBCMD_REPLACEQ = 0x13,
+ MBCMD_DELETEQ = 0x14,
+ MBCMD_INCREMENTQ = 0x15,
+ MBCMD_DECREMENTQ = 0x16,
+ MBCMD_QUITQ = 0x17,
+ MBCMD_FLUSHQ = 0x18,
+ MBCMD_APPENDQ = 0x19,
+ MBCMD_PREPENDQ = 0x1a
+};
+
+static grn_critical_section cache_lock;
+static grn_obj *cache_table = NULL;
+static grn_obj *cache_value = NULL;
+static grn_obj *cache_flags = NULL;
+static grn_obj *cache_expire = NULL;
+static grn_obj *cache_cas = NULL;
+
+#define CTX_GET(name) (grn_ctx_get(ctx, (name), strlen(name)))
+
+static grn_obj *
+cache_init(grn_ctx *ctx)
+{
+ if (cache_cas) { return cache_cas; }
+ CRITICAL_SECTION_ENTER(cache_lock);
+ if (!cache_cas) {
+ if ((cache_table = CTX_GET("Memcache"))) {
+ cache_value = CTX_GET("Memcache.value");
+ cache_flags = CTX_GET("Memcache.flags");
+ cache_expire = CTX_GET("Memcache.expire");
+ cache_cas = CTX_GET("Memcache.cas");
+ } else {
+ if (!cache_table) {
+ grn_obj *uint32_type = grn_ctx_at(ctx, GRN_DB_UINT32);
+ grn_obj *uint64_type = grn_ctx_at(ctx, GRN_DB_UINT64);
+ grn_obj *shorttext_type = grn_ctx_at(ctx, GRN_DB_SHORT_TEXT);
+ if ((cache_table = grn_table_create(ctx, "Memcache", 8, NULL,
+ GRN_OBJ_TABLE_PAT_KEY|GRN_OBJ_PERSISTENT,
+ shorttext_type, NULL))) {
+ cache_value = grn_column_create(ctx, cache_table, "value", 5, NULL,
+ GRN_OBJ_PERSISTENT, shorttext_type);
+ cache_flags = grn_column_create(ctx, cache_table, "flags", 5, NULL,
+ GRN_OBJ_PERSISTENT, uint32_type);
+ cache_expire = grn_column_create(ctx, cache_table, "expire", 6, NULL,
+ GRN_OBJ_PERSISTENT, uint32_type);
+ cache_cas = grn_column_create(ctx, cache_table, "cas", 3, NULL,
+ GRN_OBJ_PERSISTENT, uint64_type);
+ }
+ }
+ }
+ }
+ CRITICAL_SECTION_LEAVE(cache_lock);
+ return cache_cas;
+}
+
+#define RELATIVE_TIME_THRESH 1000000000
+
+#define MBRES(ctx,re,status,key_len,extra_len,flags) do {\
+ grn_msg_set_property((ctx), (re), (status), (key_len), (extra_len));\
+ grn_msg_send((ctx), (re), (flags));\
+} while (0)
+
+#define GRN_MSG_MBRES(block) do {\
+ if (!quiet) {\
+ grn_obj *re = grn_msg_open_for_reply(ctx, (grn_obj *)msg, &edge->send_old);\
+ ((grn_msg *)re)->header.qtype = header->qtype;\
+ block\
+ }\
+} while (0)
+
+static uint64_t
+get_mbreq_cas_id()
+{
+ static uint64_t cas_id = 0;
+ /* FIXME: use GRN_ATOMIC_ADD_EX_64, but it is not implemented */
+ return ++cas_id;
+}
+
+static void
+do_mbreq(grn_ctx *ctx, grn_edge *edge)
+{
+ int quiet = 0;
+ int flags = 0;
+ grn_msg *msg = edge->msg;
+ grn_com_header *header = &msg->header;
+
+ switch (header->qtype) {
+ case MBCMD_GETQ :
+ flags = GRN_CTX_MORE;
+ /* fallthru */
+ case MBCMD_GET :
+ {
+ grn_id rid;
+ uint16_t keylen = ntohs(header->keylen);
+ char *key = GRN_BULK_HEAD((grn_obj *)msg);
+ cache_init(ctx);
+ rid = grn_table_get(ctx, cache_table, key, keylen);
+ if (!rid) {
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
+ });
+ } else {
+ grn_timeval tv;
+ uint32_t expire;
+ {
+ grn_obj expire_buf;
+ GRN_UINT32_INIT(&expire_buf, 0);
+ grn_obj_get_value(ctx, cache_expire, rid, &expire_buf);
+ expire = GRN_UINT32_VALUE(&expire_buf);
+ grn_obj_close(ctx, &expire_buf);
+ }
+ grn_timeval_now(ctx, &tv);
+ if (expire && expire < tv.tv_sec) {
+ grn_table_delete_by_id(ctx, cache_table, rid);
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
+ });
+ } else {
+ grn_obj cas_buf;
+ GRN_UINT64_INIT(&cas_buf, 0);
+ grn_obj_get_value(ctx, cache_cas, rid, &cas_buf);
+ GRN_MSG_MBRES({
+ grn_obj_get_value(ctx, cache_flags, rid, re);
+ grn_obj_get_value(ctx, cache_value, rid, re);
+ ((grn_msg *)re)->header.cas = GRN_UINT64_VALUE(&cas_buf);
+ MBRES(ctx, re, MBRES_SUCCESS, 0, 4, flags);
+ });
+ grn_obj_close(ctx, &cas_buf);
+ }
+ }
+ }
+ break;
+ case MBCMD_SETQ :
+ case MBCMD_ADDQ :
+ case MBCMD_REPLACEQ :
+ quiet = 1;
+ /* fallthru */
+ case MBCMD_SET :
+ case MBCMD_ADD :
+ case MBCMD_REPLACE :
+ {
+ grn_id rid;
+ uint32_t size = ntohl(header->size);
+ uint16_t keylen = ntohs(header->keylen);
+ uint8_t extralen = header->level;
+ char *body = GRN_BULK_HEAD((grn_obj *)msg);
+ uint32_t flags = *((uint32_t *)body);
+ uint32_t expire = ntohl(*((uint32_t *)(body + 4)));
+ uint32_t valuelen = size - keylen - extralen;
+ char *key = body + 8;
+ char *value = key + keylen;
+ int added = 0;
+ int f = (header->qtype == MBCMD_REPLACE ||
+ header->qtype == MBCMD_REPLACEQ) ? 0 : GRN_TABLE_ADD;
+ GRN_ASSERT(extralen == 8);
+ cache_init(ctx);
+ if (header->qtype == MBCMD_REPLACE || header->qtype == MBCMD_REPLACEQ) {
+ rid = grn_table_get(ctx, cache_table, key, keylen);
+ } else {
+ rid = grn_table_add(ctx, cache_table, key, keylen, &added);
+ }
+ if (!rid) {
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, (f & GRN_TABLE_ADD) ? MBRES_ENOMEM : MBRES_NOT_STORED, 0, 0, 0);
+ });
+ } else {
+ if (added) {
+ if (header->cas) {
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_EINVAL, 0, 0, 0);
+ });
+ } else {
+ grn_obj text_buf, uint32_buf;
+ GRN_TEXT_INIT(&text_buf, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET_REF(&text_buf, value, valuelen);
+ grn_obj_set_value(ctx, cache_value, rid, &text_buf, GRN_OBJ_SET);
+ GRN_UINT32_INIT(&uint32_buf, 0);
+ GRN_UINT32_SET(ctx, &uint32_buf, flags);
+ grn_obj_set_value(ctx, cache_flags, rid, &uint32_buf, GRN_OBJ_SET);
+ if (expire && expire < RELATIVE_TIME_THRESH) {
+ grn_timeval tv;
+ grn_timeval_now(ctx, &tv);
+ expire += tv.tv_sec;
+ }
+ GRN_UINT32_SET(ctx, &uint32_buf, expire);
+ grn_obj_set_value(ctx, cache_expire, rid, &uint32_buf, GRN_OBJ_SET);
+ grn_obj_close(ctx, &uint32_buf);
+ {
+ grn_obj cas_buf;
+ uint64_t cas_id = get_mbreq_cas_id();
+ GRN_UINT64_INIT(&cas_buf, 0);
+ GRN_UINT64_SET(ctx, &cas_buf, cas_id);
+ grn_obj_set_value(ctx, cache_cas, rid, &cas_buf, GRN_OBJ_SET);
+ grn_obj_close(ctx, &cas_buf);
+ GRN_MSG_MBRES({
+ ((grn_msg *)re)->header.cas = cas_id;
+ MBRES(ctx, re, MBRES_SUCCESS, 0, 0, 0);
+ });
+ }
+ }
+ } else {
+ if (header->qtype != MBCMD_SET && header->qtype != MBCMD_SETQ) {
+ grn_obj uint32_buf;
+ grn_timeval tv;
+ uint32_t oexpire;
+
+ GRN_UINT32_INIT(&uint32_buf, 0);
+ grn_obj_get_value(ctx, cache_expire, rid, &uint32_buf);
+ oexpire = GRN_UINT32_VALUE(&uint32_buf);
+ grn_timeval_now(ctx, &tv);
+
+ if (oexpire && oexpire < tv.tv_sec) {
+ if (header->qtype == MBCMD_REPLACE ||
+ header->qtype == MBCMD_REPLACEQ) {
+ grn_table_delete_by_id(ctx, cache_table, rid);
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_NOT_STORED, 0, 0, 0);
+ });
+ break;
+ }
+ } else if (header->qtype == MBCMD_ADD ||
+ header->qtype == MBCMD_ADDQ) {
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_NOT_STORED, 0, 0, 0);
+ });
+ break;
+ }
+ }
+ {
+ if (header->cas) {
+ grn_obj cas_buf;
+ GRN_UINT64_INIT(&cas_buf, 0);
+ grn_obj_get_value(ctx, cache_cas, rid, &cas_buf);
+ if (header->cas != GRN_UINT64_VALUE(&cas_buf)) {
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_NOT_STORED, 0, 0, 0);
+ });
+ }
+ }
+ {
+ grn_obj text_buf, uint32_buf;
+ GRN_TEXT_INIT(&text_buf, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET_REF(&text_buf, value, valuelen);
+ grn_obj_set_value(ctx, cache_value, rid, &text_buf, GRN_OBJ_SET);
+ GRN_UINT32_INIT(&uint32_buf, 0);
+ GRN_UINT32_SET(ctx, &uint32_buf, flags);
+ grn_obj_set_value(ctx, cache_flags, rid, &uint32_buf, GRN_OBJ_SET);
+ if (expire && expire < RELATIVE_TIME_THRESH) {
+ grn_timeval tv;
+ grn_timeval_now(ctx, &tv);
+ expire += tv.tv_sec;
+ }
+ GRN_UINT32_SET(ctx, &uint32_buf, expire);
+ grn_obj_set_value(ctx, cache_expire, rid, &uint32_buf, GRN_OBJ_SET);
+ {
+ grn_obj cas_buf;
+ uint64_t cas_id = get_mbreq_cas_id();
+ GRN_UINT64_INIT(&cas_buf, 0);
+ GRN_UINT64_SET(ctx, &cas_buf, cas_id);
+ grn_obj_set_value(ctx, cache_cas, rid, &cas_buf, GRN_OBJ_SET);
+ GRN_MSG_MBRES({
+ ((grn_msg *)re)->header.cas = cas_id;
+ MBRES(ctx, re, MBRES_SUCCESS, 0, 0, 0);
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ case MBCMD_DELETEQ :
+ quiet = 1;
+ /* fallthru */
+ case MBCMD_DELETE :
+ {
+ grn_id rid;
+ uint16_t keylen = ntohs(header->keylen);
+ char *key = GRN_BULK_HEAD((grn_obj *)msg);
+ cache_init(ctx);
+ rid = grn_table_get(ctx, cache_table, key, keylen);
+ if (!rid) {
+ /* GRN_LOG(ctx, GRN_LOG_NOTICE, "GET k=%d not found", keylen); */
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
+ });
+ } else {
+ grn_table_delete_by_id(ctx, cache_table, rid);
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_SUCCESS, 0, 4, 0);
+ });
+ }
+ }
+ break;
+ case MBCMD_INCREMENTQ :
+ case MBCMD_DECREMENTQ :
+ quiet = 1;
+ /* fallthru */
+ case MBCMD_INCREMENT :
+ case MBCMD_DECREMENT :
+ {
+ grn_id rid;
+ int added = 0;
+ uint64_t delta, init;
+ uint16_t keylen = ntohs(header->keylen);
+ char *body = GRN_BULK_HEAD((grn_obj *)msg);
+ char *key = body + 20;
+ uint32_t expire = ntohl(*((uint32_t *)(body + 16)));
+ grn_ntoh(&delta, body, 8);
+ grn_ntoh(&init, body + 8, 8);
+ GRN_ASSERT(header->level == 20); /* extralen */
+ cache_init(ctx);
+ if (expire == 0xffffffff) {
+ rid = grn_table_get(ctx, cache_table, key, keylen);
+ } else {
+ rid = grn_table_add(ctx, cache_table, key, keylen, &added);
+ }
+ if (!rid) {
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
+ });
+ } else {
+ grn_obj uint32_buf, text_buf;
+ GRN_UINT32_INIT(&uint32_buf, 0);
+ GRN_TEXT_INIT(&text_buf, GRN_OBJ_DO_SHALLOW_COPY);
+ if (added) {
+ GRN_TEXT_SET_REF(&text_buf, &init, 8);
+ grn_obj_set_value(ctx, cache_value, rid, &text_buf, GRN_OBJ_SET);
+ GRN_UINT32_SET(ctx, &uint32_buf, 0);
+ grn_obj_set_value(ctx, cache_flags, rid, &uint32_buf, GRN_OBJ_SET);
+ } else {
+ grn_timeval tv;
+ uint32_t oexpire;
+
+ grn_obj_get_value(ctx, cache_expire, rid, &uint32_buf);
+ oexpire = GRN_UINT32_VALUE(&uint32_buf);
+ grn_timeval_now(ctx, &tv);
+
+ if (oexpire && oexpire < tv.tv_sec) {
+ if (expire == 0xffffffffU) {
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
+ });
+ break;
+ } else {
+ GRN_TEXT_SET_REF(&text_buf, &init, 8);
+ grn_obj_set_value(ctx, cache_value, rid, &text_buf, GRN_OBJ_SET);
+ GRN_UINT32_SET(ctx, &uint32_buf, 0);
+ grn_obj_set_value(ctx, cache_flags, rid, &uint32_buf, GRN_OBJ_SET);
+ }
+ } else {
+ grn_obj uint64_buf;
+ GRN_UINT64_INIT(&uint64_buf, 0);
+ GRN_UINT64_SET(ctx, &uint64_buf, delta);
+ grn_obj_set_value(ctx, cache_value, rid, &uint64_buf,
+ header->qtype == MBCMD_INCREMENT ||
+ header->qtype == MBCMD_INCREMENTQ
+ ? GRN_OBJ_INCR
+ : GRN_OBJ_DECR);
+ }
+ }
+ if (expire && expire < RELATIVE_TIME_THRESH) {
+ grn_timeval tv;
+ grn_timeval_now(ctx, &tv);
+ expire += tv.tv_sec;
+ }
+ GRN_UINT32_SET(ctx, &uint32_buf, expire);
+ grn_obj_set_value(ctx, cache_expire, rid, &uint32_buf, GRN_OBJ_SET);
+ GRN_MSG_MBRES({
+ /* TODO: get_mbreq_cas_id() */
+ grn_obj_get_value(ctx, cache_value, rid, re);
+ grn_hton(&delta, (uint64_t *)GRN_BULK_HEAD(re), 8);
+ GRN_TEXT_SET(ctx, re, &delta, sizeof(uint64_t));
+ MBRES(ctx, re, MBRES_SUCCESS, 0, sizeof(uint64_t), 0);
+ });
+ }
+ }
+ break;
+ case MBCMD_FLUSHQ :
+ quiet = 1;
+ /* fallthru */
+ case MBCMD_FLUSH :
+ {
+ uint32_t expire;
+ uint8_t extralen = header->level;
+ if (extralen) {
+ char *body = GRN_BULK_HEAD((grn_obj *)msg);
+ GRN_ASSERT(extralen == 4);
+ expire = ntohl(*((uint32_t *)(body)));
+ if (expire < RELATIVE_TIME_THRESH) {
+ grn_timeval tv;
+ grn_timeval_now(ctx, &tv);
+ if (expire) {
+ expire += tv.tv_sec;
+ } else {
+ expire = tv.tv_sec - 1;
+ }
+ }
+ } else {
+ grn_timeval tv;
+ grn_timeval_now(ctx, &tv);
+ expire = tv.tv_sec - 1;
+ }
+ {
+ grn_obj exp_buf;
+ GRN_UINT32_INIT(&exp_buf, 0);
+ GRN_UINT32_SET(ctx, &exp_buf, expire);
+ GRN_TABLE_EACH(ctx, cache_table, 0, 0, rid, NULL, NULL, NULL, {
+ grn_obj_set_value(ctx, cache_expire, rid, &exp_buf, GRN_OBJ_SET);
+ });
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_SUCCESS, 0, 4, 0);
+ });
+ grn_obj_close(ctx, &exp_buf);
+ }
+ }
+ break;
+ case MBCMD_NOOP :
+ break;
+ case MBCMD_VERSION :
+ GRN_MSG_MBRES({
+ grn_bulk_write(ctx, re, PACKAGE_VERSION, strlen(PACKAGE_VERSION));
+ MBRES(ctx, re, MBRES_SUCCESS, 0, 0, 0);
+ });
+ break;
+ case MBCMD_GETKQ :
+ flags = GRN_CTX_MORE;
+ /* fallthru */
+ case MBCMD_GETK :
+ {
+ grn_id rid;
+ uint16_t keylen = ntohs(header->keylen);
+ char *key = GRN_BULK_HEAD((grn_obj *)msg);
+ cache_init(ctx);
+ rid = grn_table_get(ctx, cache_table, key, keylen);
+ if (!rid) {
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
+ });
+ } else {
+ grn_obj uint32_buf;
+ grn_timeval tv;
+ uint32_t expire;
+ GRN_UINT32_INIT(&uint32_buf, 0);
+ grn_obj_get_value(ctx, cache_expire, rid, &uint32_buf);
+ expire = GRN_UINT32_VALUE(&uint32_buf);
+ grn_timeval_now(ctx, &tv);
+ if (expire && expire < tv.tv_sec) {
+ grn_table_delete_by_id(ctx, cache_table, rid);
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_KEY_ENOENT, 0, 0, 0);
+ });
+ } else {
+ grn_obj uint64_buf;
+ GRN_UINT64_INIT(&uint64_buf, 0);
+ grn_obj_get_value(ctx, cache_cas, rid, &uint64_buf);
+ GRN_MSG_MBRES({
+ grn_obj_get_value(ctx, cache_flags, rid, re);
+ grn_bulk_write(ctx, re, key, keylen);
+ grn_obj_get_value(ctx, cache_value, rid, re);
+ ((grn_msg *)re)->header.cas = GRN_UINT64_VALUE(&uint64_buf);
+ MBRES(ctx, re, MBRES_SUCCESS, keylen, 4, flags);
+ });
+ }
+ }
+ }
+ break;
+ case MBCMD_APPENDQ :
+ case MBCMD_PREPENDQ :
+ quiet = 1;
+ /* fallthru */
+ case MBCMD_APPEND :
+ case MBCMD_PREPEND :
+ {
+ grn_id rid;
+ uint32_t size = ntohl(header->size);
+ uint16_t keylen = ntohs(header->keylen);
+ char *key = GRN_BULK_HEAD((grn_obj *)msg);
+ char *value = key + keylen;
+ uint32_t valuelen = size - keylen;
+ cache_init(ctx);
+ rid = grn_table_add(ctx, cache_table, key, keylen, NULL);
+ if (!rid) {
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_ENOMEM, 0, 0, 0);
+ });
+ } else {
+ /* FIXME: check expire */
+ grn_obj buf;
+ int flags = header->qtype == MBCMD_APPEND ? GRN_OBJ_APPEND : GRN_OBJ_PREPEND;
+ GRN_TEXT_INIT(&buf, GRN_OBJ_DO_SHALLOW_COPY);
+ GRN_TEXT_SET_REF(&buf, value, valuelen);
+ grn_obj_set_value(ctx, cache_value, rid, &buf, flags);
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_SUCCESS, 0, 0, 0);
+ });
+ }
+ }
+ break;
+ case MBCMD_STAT :
+ {
+ pid_t pid = getpid();
+ GRN_MSG_MBRES({
+ grn_bulk_write(ctx, re, "pid", 3);
+ grn_text_itoa(ctx, re, pid);
+ MBRES(ctx, re, MBRES_SUCCESS, 3, 0, 0);
+ });
+ }
+ break;
+ case MBCMD_QUITQ :
+ quiet = 1;
+ /* fallthru */
+ case MBCMD_QUIT :
+ GRN_MSG_MBRES({
+ MBRES(ctx, re, MBRES_SUCCESS, 0, 0, 0);
+ });
+ /* fallthru */
+ default :
+ ctx->stat = GRN_CTX_QUIT;
+ break;
+ }
+}
+
+/* worker thread */
+
+enum {
+ EDGE_IDLE = 0x00,
+ EDGE_WAIT = 0x01,
+ EDGE_DOING = 0x02,
+ EDGE_ABORT = 0x03,
+};
+
+static void
+check_rlimit_nofile(grn_ctx *ctx)
+{
+#ifndef WIN32
+ struct rlimit limit;
+ limit.rlim_cur = 0;
+ limit.rlim_max = 0;
+ getrlimit(RLIMIT_NOFILE, &limit);
+ if (limit.rlim_cur < RLIMIT_NOFILE_MINIMUM) {
+ limit.rlim_cur = RLIMIT_NOFILE_MINIMUM;
+ limit.rlim_max = RLIMIT_NOFILE_MINIMUM;
+ setrlimit(RLIMIT_NOFILE, &limit);
+ limit.rlim_cur = 0;
+ limit.rlim_max = 0;
+ getrlimit(RLIMIT_NOFILE, &limit);
+ }
+ GRN_LOG(ctx, GRN_LOG_NOTICE,
+ "RLIMIT_NOFILE(%" GRN_FMT_LLD ",%" GRN_FMT_LLD ")",
+ (long long int)limit.rlim_cur, (long long int)limit.rlim_max);
+#endif /* WIN32 */
+}
+
+static void * CALLBACK
+h_worker(void *arg)
+{
+ ht_context hc;
+ grn_ctx ctx_, *ctx = &ctx_;
+ grn_ctx_init(ctx, 0);
+ grn_ctx_use(ctx, (grn_obj *)arg);
+ grn_ctx_recv_handler_set(ctx, h_output, &hc);
+ GRN_LOG(&grn_gctx, GRN_LOG_NOTICE, "thread start (%d/%d)", nfthreads, nthreads + 1);
+ MUTEX_LOCK(q_mutex);
+ do {
+ grn_obj *msg;
+ nfthreads++;
+ while (!(msg = (grn_obj *)grn_com_queue_deque(&grn_gctx, &ctx_new))) {
+ COND_WAIT(q_cond, q_mutex);
+ if (grn_gctx.stat == GRN_CTX_QUIT) {
+ nfthreads--;
+ goto exit;
+ }
+ }
+ nfthreads--;
+ MUTEX_UNLOCK(q_mutex);
+ hc.msg = (grn_msg *)msg;
+ do_htreq(ctx, (grn_msg *)msg);
+ MUTEX_LOCK(q_mutex);
+ } while (nfthreads < max_nfthreads && grn_gctx.stat != GRN_CTX_QUIT);
+exit :
+ nthreads--;
+ MUTEX_UNLOCK(q_mutex);
+ GRN_LOG(&grn_gctx, GRN_LOG_NOTICE, "thread end (%d/%d)", nfthreads, nthreads);
+ grn_ctx_fin(ctx);
+ return NULL;
+}
+
+static void
+h_handler(grn_ctx *ctx, grn_obj *msg)
+{
+ grn_com *com = ((grn_msg *)msg)->u.peer;
+ if (ctx->rc) {
+ grn_com_close(ctx, com);
+ grn_msg_close(ctx, msg);
+ } else {
+ grn_sock fd = com->fd;
+ void *arg = com->ev->opaque;
+ /* if not keep alive connection */
+ grn_com_event_del(ctx, com->ev, fd);
+ ((grn_msg *)msg)->u.fd = fd;
+ MUTEX_LOCK(q_mutex);
+ grn_com_queue_enque(ctx, &ctx_new, (grn_com_queue_entry *)msg);
+ if (!nfthreads && nthreads < max_nfthreads) {
+ grn_thread thread;
+ nthreads++;
+ if (THREAD_CREATE(thread, h_worker, arg)) { SERR("pthread_create"); }
+ }
+ COND_SIGNAL(q_cond);
+ MUTEX_UNLOCK(q_mutex);
+ }
+}
+
+static int
+h_server(char *path)
+{
+ int exit_code = EXIT_FAILURE;
+ grn_ctx ctx_, *ctx = &ctx_;
+ grn_ctx_init(ctx, 0);
+ MUTEX_INIT(q_mutex);
+ COND_INIT(q_cond);
+ CRITICAL_SECTION_INIT(cache_lock);
+ GRN_COM_QUEUE_INIT(&ctx_new);
+ GRN_COM_QUEUE_INIT(&ctx_old);
+ check_rlimit_nofile(ctx);
+ exit_code = start_service(ctx, path, NULL, h_handler);
+ grn_ctx_fin(ctx);
+ return exit_code;
+}
+
+static void * CALLBACK
+g_worker(void *arg)
+{
+ GRN_LOG(&grn_gctx, GRN_LOG_NOTICE, "thread start (%d/%d)", nfthreads, nthreads + 1);
+ MUTEX_LOCK(q_mutex);
+ do {
+ grn_ctx *ctx;
+ grn_edge *edge;
+ nfthreads++;
+ while (!(edge = (grn_edge *)grn_com_queue_deque(&grn_gctx, &ctx_new))) {
+ COND_WAIT(q_cond, q_mutex);
+ if (grn_gctx.stat == GRN_CTX_QUIT) {
+ nfthreads--;
+ goto exit;
+ }
+ }
+ ctx = &edge->ctx;
+ nfthreads--;
+ if (edge->stat == EDGE_DOING) { continue; }
+ if (edge->stat == EDGE_WAIT) {
+ edge->stat = EDGE_DOING;
+ while (!GRN_COM_QUEUE_EMPTYP(&edge->recv_new)) {
+ grn_obj *msg;
+ MUTEX_UNLOCK(q_mutex);
+ /* if (edge->flags == GRN_EDGE_WORKER) */
+ while (ctx->stat != GRN_CTX_QUIT &&
+ (edge->msg = (grn_msg *)grn_com_queue_deque(ctx, &edge->recv_new))) {
+ grn_com_header *header = &edge->msg->header;
+ msg = (grn_obj *)edge->msg;
+ switch (header->proto) {
+ case GRN_COM_PROTO_MBREQ :
+ do_mbreq(ctx, edge);
+ break;
+ case GRN_COM_PROTO_GQTP :
+ grn_ctx_send(ctx, GRN_BULK_HEAD(msg), GRN_BULK_VSIZE(msg), header->flags);
+ ERRCLR(ctx);
+ break;
+ default :
+ ctx->stat = GRN_CTX_QUIT;
+ break;
+ }
+ grn_msg_close(ctx, msg);
+ }
+ while ((msg = (grn_obj *)grn_com_queue_deque(ctx, &edge->send_old))) {
+ grn_msg_close(ctx, msg);
+ }
+ MUTEX_LOCK(q_mutex);
+ if (ctx->stat == GRN_CTX_QUIT || edge->stat == EDGE_ABORT) { break; }
+ }
+ }
+ if (ctx->stat == GRN_CTX_QUIT || edge->stat == EDGE_ABORT) {
+ grn_com_queue_enque(&grn_gctx, &ctx_old, (grn_com_queue_entry *)edge);
+ edge->stat = EDGE_ABORT;
+ } else {
+ edge->stat = EDGE_IDLE;
+ }
+ } while (nfthreads < max_nfthreads && grn_gctx.stat != GRN_CTX_QUIT);
+exit :
+ nthreads--;
+ MUTEX_UNLOCK(q_mutex);
+ GRN_LOG(&grn_gctx, GRN_LOG_NOTICE, "thread end (%d/%d)", nfthreads, nthreads);
+ return NULL;
+}
+
+static void
+g_dispatcher(grn_ctx *ctx, grn_edge *edge)
+{
+ MUTEX_LOCK(q_mutex);
+ if (edge->stat == EDGE_IDLE) {
+ grn_com_queue_enque(ctx, &ctx_new, (grn_com_queue_entry *)edge);
+ edge->stat = EDGE_WAIT;
+ if (!nfthreads && nthreads < max_nfthreads) {
+ grn_thread thread;
+ nthreads++;
+ if (THREAD_CREATE(thread, g_worker, NULL)) { SERR("pthread_create"); }
+ }
+ COND_SIGNAL(q_cond);
+ }
+ MUTEX_UNLOCK(q_mutex);
+}
+
+static void
+g_output(grn_ctx *ctx, int flags, void *arg)
+{
+ grn_edge *edge = arg;
+ grn_com *com = edge->com;
+ grn_msg *req = edge->msg, *msg = (grn_msg *)ctx->impl->outbuf;
+ msg->edge_id = req->edge_id;
+ msg->header.proto = req->header.proto == GRN_COM_PROTO_MBREQ
+ ? GRN_COM_PROTO_MBRES : req->header.proto;
+ if (ctx->rc != GRN_SUCCESS && GRN_BULK_VSIZE(ctx->impl->outbuf) == 0) {
+ GRN_TEXT_PUTS(ctx, ctx->impl->outbuf, ctx->errbuf);
+ }
+ if (grn_msg_send(ctx, (grn_obj *)msg,
+ (flags & GRN_CTX_MORE) ? GRN_CTX_MORE : GRN_CTX_TAIL)) {
+ edge->stat = EDGE_ABORT;
+ }
+ ctx->impl->outbuf = grn_msg_open(ctx, com, &edge->send_old);
+}
+
+static void
+g_handler(grn_ctx *ctx, grn_obj *msg)
+{
+ grn_edge *edge;
+ grn_com *com = ((grn_msg *)msg)->u.peer;
+ if (ctx->rc) {
+ if (com->has_sid) {
+ if ((edge = com->opaque)) {
+ MUTEX_LOCK(q_mutex);
+ if (edge->stat == EDGE_IDLE) {
+ grn_com_queue_enque(ctx, &ctx_old, (grn_com_queue_entry *)edge);
+ }
+ edge->stat = EDGE_ABORT;
+ MUTEX_UNLOCK(q_mutex);
+ } else {
+ grn_com_close(ctx, com);
+ }
+ }
+ grn_msg_close(ctx, msg);
+ } else {
+ int added;
+ edge = grn_edges_add(ctx, &((grn_msg *)msg)->edge_id, &added);
+ if (added) {
+ grn_ctx_init(&edge->ctx, 0);
+ GRN_COM_QUEUE_INIT(&edge->recv_new);
+ GRN_COM_QUEUE_INIT(&edge->send_old);
+ grn_ctx_use(&edge->ctx, (grn_obj *)com->ev->opaque);
+ grn_ctx_recv_handler_set(&edge->ctx, g_output, edge);
+ com->opaque = edge;
+ grn_obj_close(&edge->ctx, edge->ctx.impl->outbuf);
+ edge->ctx.impl->outbuf = grn_msg_open(&edge->ctx, com, &edge->send_old);
+ edge->com = com;
+ edge->stat = EDGE_IDLE;
+ edge->flags = GRN_EDGE_WORKER;
+ }
+ if (edge->ctx.stat == GRN_CTX_QUIT || edge->stat == EDGE_ABORT) {
+ grn_msg_close(ctx, msg);
+ } else {
+ grn_com_queue_enque(ctx, &edge->recv_new, (grn_com_queue_entry *)msg);
+ g_dispatcher(ctx, edge);
+ }
+ }
+}
+
+static int
+g_server(char *path)
+{
+ int exit_code = EXIT_FAILURE;
+ grn_ctx ctx_, *ctx = &ctx_;
+ grn_ctx_init(ctx, 0);
+ MUTEX_INIT(q_mutex);
+ COND_INIT(q_cond);
+ CRITICAL_SECTION_INIT(cache_lock);
+ GRN_COM_QUEUE_INIT(&ctx_new);
+ GRN_COM_QUEUE_INIT(&ctx_old);
+ check_rlimit_nofile(ctx);
+ exit_code = start_service(ctx, path, g_dispatcher, g_handler);
+ grn_ctx_fin(ctx);
+ return exit_code;
+}
+
+enum {
+ mode_alone = 0,
+ mode_client,
+ mode_daemon,
+ mode_server,
+ mode_usage,
+ mode_version,
+ mode_config,
+ mode_error
+};
+
+#define MODE_MASK 0x007f
+#define MODE_NEW_DB 0x0100
+
+static uint32_t
+get_core_number(void)
+{
+#ifdef WIN32
+ SYSTEM_INFO sinfo;
+ GetSystemInfo(&sinfo);
+ return sinfo.dwNumberOfProcessors;
+#else /* WIN32 */
+# ifdef _SC_NPROCESSORS_CONF
+ return sysconf(_SC_NPROCESSORS_CONF);
+# else
+ int n_processors;
+ size_t length = sizeof(n_processors);
+ int mib[] = {CTL_HW, HW_NCPU};
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]),
+ &n_processors, &length, NULL, 0) == 0 &&
+ length == sizeof(n_processors) &&
+ 0 < n_processors) {
+ return n_processors;
+ } else {
+ return 1;
+ }
+# endif /* _SC_NPROCESSORS_CONF */
+#endif /* WIN32 */
+}
+
+/*
+ * The length of each line, including an end-of-line, in config file should be
+ * shorter than (CONFIG_FILE_BUF_SIZE - 1) bytes. Too long lines are ignored.
+ * Note that both '\r' and '\n' are handled as end-of-lines.
+ *
+ * '#' and ';' are special symbols to start comments. A comment ends with an
+ * end-of-line.
+ *
+ * Format: name[=value]
+ * - Preceding/trailing white-spaces of each line are removed.
+ * - White-spaces aroung '=' are removed.
+ * - name does not allow white-spaces.
+ */
+#define CONFIG_FILE_BUF_SIZE 4096
+#define CONFIG_FILE_MAX_NAME_LENGTH 128
+#define CONFIG_FILE_MAX_VALUE_LENGTH 2048
+
+typedef enum {
+ CONFIG_FILE_SUCCESS,
+ CONFIG_FILE_FORMAT_ERROR,
+ CONFIG_FILE_FOPEN_ERROR,
+ CONFIG_FILE_MALLOC_ERROR,
+ CONFIG_FILE_ATEXIT_ERROR
+} config_file_status;
+
+/*
+ * The node type of a linked list for storing values. Note that a value is
+ * stored in the extra space of an object.
+ */
+typedef struct _config_file_entry {
+ struct _config_file_entry *next;
+} config_file_entry;
+
+static config_file_entry *config_file_entry_head = NULL;
+
+static void
+config_file_clear(void) {
+ while (config_file_entry_head) {
+ config_file_entry *next = config_file_entry_head->next;
+ free(config_file_entry_head);
+ config_file_entry_head = next;
+ }
+}
+
+static config_file_status
+config_file_register(const char *path, const grn_str_getopt_opt *opts,
+ int *flags, const char *name, size_t name_length,
+ const char *value, size_t value_length)
+{
+ char name_buf[CONFIG_FILE_MAX_NAME_LENGTH + 3];
+ config_file_entry *entry = NULL;
+ char *args[4];
+
+ name_buf[0] = name_buf[1] = '-';
+ strcpy(name_buf + 2, name);
+
+ if (value) {
+ const size_t entry_size = sizeof(config_file_entry) + value_length + 1;
+ entry = (config_file_entry *)malloc(entry_size);
+ if (!entry) {
+ fprintf(stderr, "memory allocation failed: %u bytes\n",
+ (unsigned int)entry_size);
+ return CONFIG_FILE_MALLOC_ERROR;
+ }
+ strcpy((char *)(entry + 1), value);
+ entry->next = config_file_entry_head;
+ if (!config_file_entry_head) {
+ if (atexit(config_file_clear)) {
+ free(entry);
+ return CONFIG_FILE_ATEXIT_ERROR;
+ }
+ }
+ config_file_entry_head = entry;
+ }
+
+ args[0] = (char *)path;
+ args[1] = name_buf;
+ args[2] = entry ? (char *)(entry + 1) : NULL;
+ args[3] = NULL;
+ grn_str_getopt(entry ? 3 : 2, args, opts, flags);
+ return CONFIG_FILE_SUCCESS;
+}
+
+static config_file_status
+config_file_parse(const char *path, const grn_str_getopt_opt *opts,
+ int *flags, char *buf) {
+ char *ptr, *name, *value;
+ size_t name_length, value_length;
+
+ while (isspace(*buf)) {
+ buf++;
+ }
+
+ ptr = buf;
+ while (*ptr && *ptr != '#' && *ptr != ';') {
+ ptr++;
+ }
+
+ do {
+ *ptr-- = '\0';
+ } while (ptr >= buf && isspace(*ptr));
+
+ if (!*buf) {
+ return CONFIG_FILE_SUCCESS;
+ }
+
+ name = ptr = buf;
+ while (*ptr && !isspace(*ptr) && *ptr != '=') {
+ ptr++;
+ }
+ while (isspace(*ptr)) {
+ *ptr++ = '\0';
+ }
+
+ name_length = strlen(name);
+ if (name_length == 0) {
+ return CONFIG_FILE_SUCCESS;
+ } else if (name_length > CONFIG_FILE_MAX_NAME_LENGTH) {
+ fprintf(stderr, "too long name in config file: %u bytes\n",
+ (unsigned int)name_length);
+ return CONFIG_FILE_FORMAT_ERROR;
+ }
+
+ if (*ptr == '=') {
+ *ptr++ = '\0';
+ while (isspace(*ptr)) {
+ ptr++;
+ }
+ value = ptr;
+ } else if (*ptr) {
+ fprintf(stderr, "invalid name in config file\n");
+ return CONFIG_FILE_FORMAT_ERROR;
+ } else {
+ value = NULL;
+ }
+
+ value_length = value ? strlen(value) : 0;
+ if (value_length > CONFIG_FILE_MAX_VALUE_LENGTH) {
+ fprintf(stderr, "too long value in config file: %u bytes\n",
+ (unsigned int)value_length);
+ return CONFIG_FILE_FORMAT_ERROR;
+ }
+
+ return config_file_register(path, opts, flags,
+ name, name_length, value, value_length);
+}
+
+static config_file_status
+config_file_load(const char *path, const grn_str_getopt_opt *opts, int *flags)
+{
+ config_file_status status = CONFIG_FILE_SUCCESS;
+ char buf[CONFIG_FILE_BUF_SIZE];
+ size_t length = 0;
+ FILE * const file = fopen(path, "rb");
+ if (!file) {
+ return CONFIG_FILE_FOPEN_ERROR;
+ }
+
+ for ( ; ; ) {
+ int c = fgetc(file);
+ if (c == '\r' || c == '\n' || c == EOF) {
+ if (length < sizeof(buf) - 1) {
+ buf[length] = '\0';
+ status = config_file_parse(path, opts, flags, buf);
+ if (status != CONFIG_FILE_SUCCESS) {
+ break;
+ }
+ }
+ length = 0;
+ } else if (c == '\0') {
+ fprintf(stderr, "prohibited '\\0' in config file: %s\n", path);
+ status = CONFIG_FILE_FORMAT_ERROR;
+ break;
+ } else {
+ if (length < sizeof(buf) - 1) {
+ buf[length] = (char)c;
+ }
+ length++;
+ }
+
+ if (c == EOF) {
+ break;
+ }
+ }
+
+ fclose(file);
+ return status;
+}
+
+static const int default_http_port = DEFAULT_HTTP_PORT;
+static const int default_gqtp_port = DEFAULT_GQTP_PORT;
+static grn_encoding default_encoding = GRN_ENC_DEFAULT;
+static uint32_t default_max_num_threads = DEFAULT_MAX_NFTHREADS;
+static const int default_mode = mode_alone;
+static const int default_log_level = GRN_LOG_DEFAULT_LEVEL;
+static const char * const default_protocol = "gqtp";
+static const char *default_hostname = "localhost";
+static const char * const default_dest = "localhost";
+static const char *default_log_path = "";
+static const char *default_query_log_path = "";
+static const char *default_config_path = "";
+static const char *default_document_root = "";
+static grn_command_version default_default_command_version =
+ GRN_COMMAND_VERSION_DEFAULT;
+static int64_t default_default_match_escalation_threshold = 0;
+static const char * const default_bind_address = "0.0.0.0";
+
+static void
+init_default_hostname(void)
+{
+ static char hostname[HOST_NAME_MAX + 1];
+ struct addrinfo hints, *result;
+
+ hostname[HOST_NAME_MAX] = '\0';
+ if (gethostname(hostname, HOST_NAME_MAX) == -1)
+ return;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_addr = NULL;
+ hints.ai_canonname = NULL;
+ hints.ai_next = NULL;
+ if (getaddrinfo(hostname, NULL, &hints, &result) != 0)
+ return;
+ freeaddrinfo(result);
+
+ default_hostname = hostname;
+}
+
+static void
+init_default_settings(void)
+{
+ output = stdout;
+
+ default_encoding = grn_encoding_parse(GRN_DEFAULT_ENCODING);
+
+ {
+ const uint32_t num_cores = get_core_number();
+ if (num_cores != 0) {
+ default_max_num_threads = num_cores;
+ }
+ }
+
+ init_default_hostname();
+
+ default_log_path = grn_default_logger_get_path();
+ default_query_log_path = grn_default_query_logger_get_path();
+
+ default_config_path = getenv("GRN_CONFIG_PATH");
+ if (!default_config_path) {
+ default_config_path = GRN_CONFIG_PATH;
+ if (!default_config_path) {
+ default_config_path = "";
+ }
+ }
+
+#ifdef WIN32
+ {
+ static char win32_default_document_root[PATH_MAX];
+ size_t document_root_length = strlen(grn_win32_base_dir()) + 1 +
+ strlen(GRN_DEFAULT_RELATIVE_DOCUMENT_ROOT) + 1;
+ if (document_root_length >= PATH_MAX) {
+ fprintf(stderr, "can't use default root: too long path\n");
+ } else {
+ strcpy(win32_default_document_root, grn_win32_base_dir());
+ strcat(win32_default_document_root, "/");
+ strcat(win32_default_document_root, GRN_DEFAULT_RELATIVE_DOCUMENT_ROOT);
+ default_document_root = win32_default_document_root;
+ }
+ }
+#else
+ default_document_root = GRN_DEFAULT_DOCUMENT_ROOT;
+#endif
+
+ default_default_command_version = grn_get_default_command_version();
+ default_default_match_escalation_threshold =
+ grn_get_default_match_escalation_threshold();
+}
+
+static void
+show_config(FILE *out, const grn_str_getopt_opt *opts, int flags)
+{
+ const grn_str_getopt_opt *o;
+
+ for (o = opts; o->opt || o->longopt; o++) {
+ switch (o->op) {
+ case GETOPT_OP_NONE:
+ if (o->arg && *o->arg) {
+ if (o->longopt && strcmp(o->longopt, "config-path")) {
+ fprintf(out, "%s=%s\n", o->longopt, *o->arg);
+ }
+ }
+ break;
+ case GETOPT_OP_ON:
+ if (flags & o->flag) {
+ goto no_arg;
+ }
+ break;
+ case GETOPT_OP_OFF:
+ if (!(flags & o->flag)) {
+ goto no_arg;
+ }
+ break;
+ case GETOPT_OP_UPDATE:
+ if (flags == o->flag) {
+ no_arg:
+ if (o->longopt) {
+ fprintf(out, "%s\n", o->longopt);
+ }
+ }
+ break;
+ }
+ }
+}
+
+static void
+show_version(void)
+{
+ printf("%s %s [",
+ grn_get_package(),
+ grn_get_version());
+
+ /* FIXME: Should we detect host information dynamically on Windows? */
+#ifdef HOST_OS
+ printf("%s,", HOST_OS);
+#endif
+#ifdef HOST_CPU
+ printf("%s,", HOST_CPU);
+#endif
+ printf("%s", GRN_DEFAULT_ENCODING);
+
+ printf(",match-escalation-threshold=%" GRN_FMT_LLD,
+ grn_get_default_match_escalation_threshold());
+
+#ifndef NO_NFKC
+ printf(",nfkc");
+#endif
+#ifdef GRN_WITH_MECAB
+ printf(",mecab");
+#endif
+#ifdef GRN_WITH_MESSAGE_PACK
+ printf(",msgpack");
+#endif
+#ifndef NO_ZLIB
+ printf(",zlib");
+#endif
+#ifndef NO_LZO
+ printf(",lzo");
+#endif
+#ifdef USE_KQUEUE
+ printf(",kqueue");
+#endif
+#ifdef USE_EPOLL
+ printf(",epoll");
+#endif
+#ifdef USE_POLL
+ printf(",poll");
+#endif
+ printf("]\n");
+
+#ifdef CONFIGURE_OPTIONS
+ printf("\n");
+ printf("configure options: <%s>\n", CONFIGURE_OPTIONS);
+#endif
+}
+
+static void
+show_usage(FILE *output)
+{
+ uint32_t default_cache_limit = GRN_CACHE_DEFAULT_MAX_N_ENTRIES;
+
+ fprintf(output,
+ "Usage: groonga [options...] [dest]\n"
+ "\n"
+ "Mode options: (default: standalone)\n"
+ " By default, groonga runs in standalone mode.\n"
+ " -c: run in client mode\n"
+ " -s: run in server mode\n"
+ " -d: run in daemon mode\n"
+ "\n"
+ "Database creation options:\n"
+ " -n: create new database (except client mode)\n"
+ " -e, --encoding <encoding>:\n"
+ " specify encoding for new database\n"
+ " [none|euc|utf8|sjis|latin1|koi8r] (default: %s)\n"
+ "\n"
+ "Standalone/client options:\n"
+ " --file <path>: read commands from specified file\n"
+ " --input-fd <FD>: read commands from specified file descriptor\n"
+ " --file has a prioriry over --input-fd\n"
+ " --output-fd <FD>: output response to specifid file descriptor\n"
+ " -p, --port <port number>: specify server port number (client mode only)\n"
+ " (default: %d)\n"
+ "\n"
+ "Server/daemon options:\n"
+ " --bind-address <ip/hostname>:\n"
+ " specify server address to bind\n"
+ " (default: %s)\n"
+ " -p, --port <port number>: specify server port number\n"
+ " (HTTP default: %d, GQTP default: %d)\n"
+ " -i, --server-id <ip/hostname>:\n"
+ " specify server ID address (default: %s)\n"
+ " --protocol <protocol>: specify server protocol to listen\n"
+ " [gqtp|http|memcached] (default: %s)\n"
+ " --document-root <path>: specify document root path (http only)\n"
+ " (default: %s)\n"
+ " --cache-limit <limit>: specify max number of cache data (default: %u)\n"
+ " -t, --max-threads <max threads>:\n"
+ " specify max number of threads (default: %u)\n"
+ " --pid-path <path>: specify file to write process ID to\n"
+ " (daemon mode only)\n"
+ "\n"
+ "Logging options:\n"
+ " -l, --log-level <log level>:\n"
+ " specify log level (default: %d)\n"
+ " --log-path <path>: specify log path\n"
+ " (default: %s)\n"
+ " --query-log-path <path>:\n"
+ " specify query log path\n"
+ " (default: %s)\n"
+ "\n"
+ "Common options:\n"
+ " --working-directory <path>:\n"
+ " specify working directory path\n"
+ " (none)\n"
+ " --config-path <path>:\n"
+ " specify config file path\n"
+ " (default: %s)\n"
+ " --default-command-version <version>:\n"
+ " specify default command version (default: %d)\n"
+ " --default-match-escalation-threshold <threshold>:\n"
+ " specify default match escalation threshold"
+ " (default: %" GRN_FMT_LLD ")\n"
+ "\n"
+ " --show-config: show config\n"
+ " -h, --help: show usage\n"
+ " --version: show groonga version\n"
+ "\n"
+ "dest:\n"
+ " <db pathname> [<commands>]: in standalone mode\n"
+ " <db pathname>: in server/daemon mode\n"
+ " <dest hostname> [<commands>]: in client mode (default: %s)\n",
+ grn_encoding_to_string(default_encoding),
+ default_gqtp_port, default_bind_address,
+ default_http_port, default_gqtp_port, default_hostname, default_protocol,
+ default_document_root, default_cache_limit, default_max_num_threads,
+ default_log_level, default_log_path, default_query_log_path,
+ default_config_path, default_default_command_version,
+ (long long int)default_default_match_escalation_threshold,
+ default_dest);
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *port_arg = NULL, *encoding_arg = NULL,
+ *max_num_threads_arg = NULL, *log_level_arg = NULL,
+ *bind_address_arg = NULL, *hostname_arg = NULL, *protocol_arg = NULL,
+ *log_path_arg = NULL, *query_log_path_arg = NULL,
+ *cache_limit_arg = NULL, *document_root_arg = NULL,
+ *default_command_version_arg = NULL,
+ *default_match_escalation_threshold_arg = NULL,
+ *input_fd_arg = NULL, *output_fd_arg = NULL,
+ *working_directory_arg = NULL;
+ const char *config_path = NULL;
+ int exit_code = EXIT_SUCCESS;
+ int i, mode = mode_alone;
+ uint32_t cache_limit = 0;
+ static grn_str_getopt_opt opts[] = {
+ {'p', "port", NULL, 0, GETOPT_OP_NONE},
+ {'e', "encoding", NULL, 0, GETOPT_OP_NONE},
+ {'t', "max-threads", NULL, 0, GETOPT_OP_NONE},
+ {'h', "help", NULL, mode_usage, GETOPT_OP_UPDATE},
+ {'c', NULL, NULL, mode_client, GETOPT_OP_UPDATE},
+ {'d', NULL, NULL, mode_daemon, GETOPT_OP_UPDATE},
+ {'s', NULL, NULL, mode_server, GETOPT_OP_UPDATE},
+ {'l', "log-level", NULL, 0, GETOPT_OP_NONE},
+ {'i', "server-id", NULL, 0, GETOPT_OP_NONE},
+ {'n', NULL, NULL, MODE_NEW_DB, GETOPT_OP_ON},
+ {'\0', "protocol", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "version", NULL, mode_version, GETOPT_OP_UPDATE},
+ {'\0', "log-path", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "query-log-path", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "pid-path", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "config-path", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "show-config", NULL, mode_config, GETOPT_OP_UPDATE},
+ {'\0', "cache-limit", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "file", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "document-root", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "default-command-version", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "default-match-escalation-threshold", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "bind-address", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "input-fd", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "output-fd", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "working-directory", NULL, 0, GETOPT_OP_NONE},
+ {'\0', NULL, NULL, 0, 0}
+ };
+ opts[0].arg = &port_arg;
+ opts[1].arg = &encoding_arg;
+ opts[2].arg = &max_num_threads_arg;
+ opts[7].arg = &log_level_arg;
+ opts[8].arg = &hostname_arg;
+ opts[10].arg = &protocol_arg;
+ opts[12].arg = &log_path_arg;
+ opts[13].arg = &query_log_path_arg;
+ opts[14].arg = &pid_file_path;
+ opts[15].arg = &config_path;
+ opts[17].arg = &cache_limit_arg;
+ opts[18].arg = &input_path;
+ opts[19].arg = &document_root_arg;
+ opts[20].arg = &default_command_version_arg;
+ opts[21].arg = &default_match_escalation_threshold_arg;
+ opts[22].arg = &bind_address_arg;
+ opts[23].arg = &input_fd_arg;
+ opts[24].arg = &output_fd_arg;
+ opts[25].arg = &working_directory_arg;
+
+ reset_ready_notify_pipe();
+
+ init_default_settings();
+
+ /* only for parsing --config-path. */
+ i = grn_str_getopt(argc, argv, opts, &mode);
+ if (i < 0) {
+ show_usage(stderr);
+ return EXIT_FAILURE;
+ }
+
+ if (config_path) {
+ const config_file_status status = config_file_load(config_path, opts, &mode);
+ if (status == CONFIG_FILE_FOPEN_ERROR) {
+ fprintf(stderr, "%s: can't open config file: %s (%s)\n",
+ argv[0], config_path, strerror(errno));
+ return EXIT_FAILURE;
+ } else if (status != CONFIG_FILE_SUCCESS) {
+ fprintf(stderr, "%s: failed to parse config file: %s (%s)\n",
+ argv[0], config_path,
+ (status == CONFIG_FILE_FORMAT_ERROR) ? "Invalid format" : strerror(errno));
+ return EXIT_FAILURE;
+ }
+ } else if (*default_config_path) {
+ const config_file_status status =
+ config_file_load(default_config_path, opts, &mode);
+ if (status != CONFIG_FILE_SUCCESS && status != CONFIG_FILE_FOPEN_ERROR) {
+ fprintf(stderr, "%s: failed to parse config file: %s (%s)\n",
+ argv[0], default_config_path,
+ (status == CONFIG_FILE_FORMAT_ERROR) ? "Invalid format" : strerror(errno));
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (working_directory_arg) {
+ if (chdir(working_directory_arg) == -1) {
+ fprintf(stderr, "%s: failed to change directory: %s: %s\n",
+ argv[0], working_directory_arg, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* ignore mode option in config file */
+ mode = (mode == mode_error) ? default_mode :
+ ((mode & ~MODE_MASK) | default_mode);
+
+ i = grn_str_getopt(argc, argv, opts, &mode);
+ if (i < 0) { mode = mode_error; }
+ switch (mode & MODE_MASK) {
+ case mode_version :
+ show_version();
+ return EXIT_SUCCESS;
+ case mode_usage :
+ show_usage(output);
+ return EXIT_SUCCESS;
+ case mode_config :
+ show_config(output, opts, mode & ~MODE_MASK);
+ return EXIT_SUCCESS;
+ case mode_error :
+ show_usage(stderr);
+ return EXIT_FAILURE;
+ }
+
+ if (port_arg) {
+ const char * const end = port_arg + strlen(port_arg);
+ const char *rest = NULL;
+ const int value = grn_atoi(port_arg, end, &rest);
+ if (rest != end || value <= 0 || value > 65535) {
+ fprintf(stderr, "invalid port number: <%s>\n", port_arg);
+ return EXIT_FAILURE;
+ }
+ port = value;
+ } else {
+ if (protocol_arg) {
+ if (*protocol_arg == 'h' || *protocol_arg == 'H') {
+ port = default_http_port;
+ }
+ }
+ }
+
+ if (encoding_arg) {
+ switch (*encoding_arg) {
+ case 'n' :
+ case 'N' :
+ encoding = GRN_ENC_NONE;
+ break;
+ case 'e' :
+ case 'E' :
+ encoding = GRN_ENC_EUC_JP;
+ break;
+ case 'u' :
+ case 'U' :
+ encoding = GRN_ENC_UTF8;
+ break;
+ case 's' :
+ case 'S' :
+ encoding = GRN_ENC_SJIS;
+ break;
+ case 'l' :
+ case 'L' :
+ encoding = GRN_ENC_LATIN1;
+ break;
+ case 'k' :
+ case 'K' :
+ encoding = GRN_ENC_KOI8R;
+ break;
+ default:
+ encoding = GRN_ENC_DEFAULT;
+ break;
+ }
+ } else {
+ encoding = GRN_ENC_DEFAULT;
+ }
+
+ if (!grn_document_root) {
+ grn_document_root = default_document_root;
+ }
+
+ if (protocol_arg) {
+ switch (*protocol_arg) {
+ case 'g' :
+ case 'G' :
+ do_client = g_client;
+ do_server = g_server;
+ break;
+ case 'h' :
+ case 'H' :
+ do_client = g_client;
+ do_server = h_server;
+ break;
+ case 'm' :
+ case 'M' :
+ do_client = g_client;
+ do_server = g_server;
+ break;
+ default :
+ do_client = g_client;
+ do_server = g_server;
+ break;
+ }
+ } else {
+ do_client = g_client;
+ do_server = g_server;
+ }
+
+ if (log_path_arg) {
+ grn_default_logger_set_path(log_path_arg);
+ }
+
+ if (query_log_path_arg) {
+ grn_default_query_logger_set_path(query_log_path_arg);
+ }
+
+ if (log_level_arg) {
+ const char * const end = log_level_arg + strlen(log_level_arg);
+ const char *rest = NULL;
+ const int value = grn_atoi(log_level_arg, end, &rest);
+ if (end != rest || value < 0 || value > 9) {
+ fprintf(stderr, "invalid log level: <%s>\n", log_level_arg);
+ return EXIT_FAILURE;
+ }
+ log_level = value;
+ } else {
+ log_level = default_log_level;
+ }
+ grn_default_logger_set_max_level(log_level);
+
+ if (max_num_threads_arg) {
+ const char * const end = max_num_threads_arg + strlen(max_num_threads_arg);
+ const char *rest = NULL;
+ const uint32_t value = grn_atoui(max_num_threads_arg, end, &rest);
+ if (end != rest || value < 1 || value > 100) {
+ fprintf(stderr, "invalid max number of threads: <%s>\n",
+ max_num_threads_arg);
+ return EXIT_FAILURE;
+ }
+ max_nfthreads = value;
+ } else {
+ max_nfthreads = default_max_num_threads;
+ }
+
+ if (input_path) {
+ if (!freopen(input_path, "r", stdin)) {
+ fprintf(stderr, "can't open input file: %s (%s)\n",
+ input_path, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ batchmode = GRN_TRUE;
+ } else {
+ if (input_fd_arg) {
+ const char * const end = input_fd_arg + strlen(input_fd_arg);
+ const char *rest = NULL;
+ const int input_fd = grn_atoi(input_fd_arg, end, &rest);
+ if (rest != end || input_fd == 0) {
+ fprintf(stderr, "invalid input FD: <%s>\n", input_fd_arg);
+ return EXIT_FAILURE;
+ }
+ if (dup2(input_fd, STDIN_FILENO) == -1) {
+ fprintf(stderr, "can't open input FD: %d (%s)\n",
+ input_fd, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ batchmode = GRN_TRUE;
+ } else {
+ if (argc - i > 1) {
+ batchmode = GRN_TRUE;
+ } else {
+ batchmode = !isatty(0);
+ }
+ }
+ }
+
+ if (output_fd_arg) {
+ const char * const end = output_fd_arg + strlen(output_fd_arg);
+ const char *rest = NULL;
+ const int output_fd = grn_atoi(output_fd_arg, end, &rest);
+ if (rest != end || output_fd == 0) {
+ fprintf(stderr, "invalid output FD: <%s>\n", output_fd_arg);
+ return EXIT_FAILURE;
+ }
+ output = fdopen(output_fd, "w");
+ if (!output) {
+ fprintf(stderr, "can't open output FD: %d (%s)\n",
+ output_fd, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ }
+
+
+ if (bind_address_arg) {
+ const size_t bind_address_length = strlen(bind_address_arg);
+ if (bind_address_length > HOST_NAME_MAX) {
+ fprintf(stderr, "too long bind address: %s (%u bytes):"
+ " must not be longer than %u bytes\n",
+ bind_address_arg, (unsigned int)bind_address_length, HOST_NAME_MAX);
+ return EXIT_FAILURE;
+ }
+ strcpy(bind_address, bind_address_arg);
+ } else {
+ strcpy(bind_address, default_bind_address);
+ }
+
+ if (hostname_arg) {
+ const size_t hostname_length = strlen(hostname_arg);
+ if (hostname_length > HOST_NAME_MAX) {
+ fprintf(stderr, "too long hostname: %s (%u bytes):"
+ " must not be longer than %u bytes\n",
+ hostname_arg, (unsigned int)hostname_length, HOST_NAME_MAX);
+ return EXIT_FAILURE;
+ }
+ strcpy(hostname, hostname_arg);
+ } else {
+ strcpy(hostname, default_hostname);
+ }
+
+ if (document_root_arg) {
+ grn_document_root = document_root_arg;
+ }
+
+ if (default_command_version_arg) {
+ const char * const end = default_command_version_arg
+ + strlen(default_command_version_arg);
+ const char *rest = NULL;
+ const int value = grn_atoi(default_command_version_arg, end, &rest);
+ if (end != rest || value < GRN_COMMAND_VERSION_MIN ||
+ value > GRN_COMMAND_VERSION_MAX) {
+ fprintf(stderr, "invalid command version: <%s>\n",
+ default_command_version_arg);
+ return EXIT_FAILURE;
+ }
+ switch (value) {
+ case 1 :
+ default_command_version = GRN_COMMAND_VERSION_1;
+ break;
+ case 2 :
+ default_command_version = GRN_COMMAND_VERSION_2;
+ break;
+ default :
+ fprintf(stderr, "invalid command version: <%s>\n",
+ default_command_version_arg);
+ return EXIT_FAILURE;
+ }
+ } else {
+ default_command_version = default_default_command_version;
+ }
+
+ if (default_match_escalation_threshold_arg) {
+ const char * const end = default_match_escalation_threshold_arg
+ + strlen(default_match_escalation_threshold_arg);
+ const char *rest = NULL;
+ const int64_t value = grn_atoll(default_match_escalation_threshold_arg, end, &rest);
+ if (end != rest) {
+ fprintf(stderr, "invalid match escalation threshold: <%s>\n",
+ default_match_escalation_threshold_arg);
+ return EXIT_FAILURE;
+ }
+ default_match_escalation_threshold = value;
+ } else {
+ default_match_escalation_threshold = default_default_match_escalation_threshold;
+ }
+
+ if (cache_limit_arg) {
+ const char * const end = cache_limit_arg + strlen(cache_limit_arg);
+ const char *rest = NULL;
+ const uint32_t value = grn_atoui(cache_limit_arg, end, &rest);
+ if (end != rest) {
+ fprintf(stderr, "invalid --cache-limit value: <%s>\n", cache_limit_arg);
+ return EXIT_FAILURE;
+ }
+ cache_limit = value;
+ }
+
+#ifdef GRN_WITH_LIBEDIT
+ if (!batchmode) {
+ line_editor_init(argc, argv);
+ }
+#endif
+ if (grn_init()) { return EXIT_FAILURE; }
+
+ grn_set_default_encoding(encoding);
+
+ if (default_command_version_arg) {
+ grn_set_default_command_version(default_command_version);
+ }
+
+ if (default_match_escalation_threshold_arg) {
+ grn_set_default_match_escalation_threshold(default_match_escalation_threshold);
+ }
+
+ grn_set_segv_handler();
+ grn_set_int_handler();
+ grn_set_term_handler();
+
+ if (cache_limit_arg) {
+ grn_cache *cache;
+ cache = grn_cache_current_get(&grn_gctx);
+ grn_cache_set_max_n_entries(&grn_gctx, cache, cache_limit);
+ }
+
+ newdb = (mode & MODE_NEW_DB);
+ switch (mode & MODE_MASK) {
+ case mode_alone :
+ exit_code = do_alone(argc - i, argv + i);
+ break;
+ case mode_client :
+ exit_code = do_client(argc - i, argv + i);
+ break;
+ case mode_daemon :
+ is_daemon_mode = GRN_TRUE;
+ /* fallthru */
+ case mode_server :
+ exit_code = do_server(argc > i ? argv[i] : NULL);
+ break;
+ default:
+ exit_code = EXIT_FAILURE;
+ break;
+ }
+
+#ifdef GRN_WITH_LIBEDIT
+ if (!batchmode) {
+ line_editor_fin();
+ }
+#endif
+ if (output != stdout) {
+ fclose(output);
+ }
+ grn_fin();
+ return exit_code;
+}
diff --git a/storage/mroonga/vendor/groonga/src/groonga_benchmark.c b/storage/mroonga/vendor/groonga/src/groonga_benchmark.c
new file mode 100644
index 00000000000..61575174950
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/groonga_benchmark.c
@@ -0,0 +1,3172 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2010-2012 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif /* HAVE_SYS_WAIT_H */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif /* HAVE_SYS_SOCKET_H */
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#include "lib/str.h"
+#include "lib/com.h"
+#include "lib/db.h"
+
+#ifdef WIN32
+#include <windows.h>
+#include <stddef.h>
+#else
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include <sys/statvfs.h>
+#include <libgen.h>
+#endif /* WIN32 */
+
+/*
+#define DEBUG_FTP
+#define DEBUG_HTTP
+*/
+
+#define FTPUSER "anonymous"
+#define FTPPASSWD "grntest"
+#define FTPSERVER "ftp.groonga.org"
+#define FTPBUF 20000
+#define DEFAULT_PORT 10041
+#define DEFAULT_DEST "localhost"
+
+#define OUT_JSON 0
+#define OUT_TSV 1
+
+static int grntest_outtype = OUT_JSON;
+
+static grn_critical_section grntest_cs;
+
+static int grntest_stop_flag = 0;
+static int grntest_detail_on = 0;
+static int grntest_remote_mode = 0;
+static int grntest_localonly_mode = 0;
+static int grntest_owndb_mode = 0;
+static int grntest_onmemory_mode = 0;
+static grn_bool grntest_ftp_mode = GRN_FALSE;
+#define TMPFILE "_grntest.tmp"
+
+static grn_ctx grntest_server_context;
+static FILE *grntest_log_file;
+
+#define OS_LINUX64 "LINUX64"
+#define OS_LINUX32 "LINUX32"
+#define OS_WINDOWS64 "WINDOWS64"
+#define OS_WINDOWS32 "WINDOWS32"
+
+#ifdef WIN32
+typedef SOCKET socket_t;
+#define SOCKETERROR INVALID_SOCKET
+#define socketclose closesocket
+static const char *groonga_path = "groonga.exe";
+static PROCESS_INFORMATION grntest_pi;
+#else
+static pid_t grntest_server_id = 0;
+typedef int socket_t;
+#define socketclose close
+#define SOCKETERROR -1
+static const char *groonga_path = "groonga";
+#endif /* WIN32 */
+
+static const char *groonga_protocol = "gqtp";
+static const char *grntest_osinfo;
+static int grntest_sigint = 0;
+
+
+
+static grn_obj *grntest_db = NULL;
+
+#define MAX_CON_JOB 10
+#define MAX_CON 64
+
+#define BUF_LEN 1024
+#define MAX_PATH_LEN 256
+
+#define J_DO_LOCAL 1 /* do_local */
+#define J_DO_GQTP 2 /* do_gqtp */
+#define J_DO_HTTP 3 /* do_http */
+#define J_REP_LOCAL 4 /* rep_local */
+#define J_REP_GQTP 5 /* rep_gqtp */
+#define J_REP_HTTP 6 /* rep_http */
+#define J_OUT_LOCAL 7 /* out_local */
+#define J_OUT_GQTP 8 /* out_gqtp */
+#define J_OUT_HTTP 9 /* out_http */
+#define J_TEST_LOCAL 10 /* test_local */
+#define J_TEST_GQTP 11 /* test_gqtp */
+#define J_TEST_HTTP 12 /* test_http */
+
+static char grntest_username[BUF_LEN];
+static char grntest_scriptname[BUF_LEN];
+static char grntest_date[BUF_LEN];
+static char grntest_serverhost[BUF_LEN];
+static int grntest_serverport;
+static const char *grntest_dbpath;
+
+struct job {
+ char jobname[BUF_LEN];
+ char commandfile[BUF_LEN];
+ int qnum;
+ int jobtype;
+ int concurrency;
+ int ntimes;
+ int done;
+ long long int max;
+ long long int min;
+ FILE *outputlog;
+ FILE *inputlog;
+ char logfile[BUF_LEN];
+};
+
+struct task {
+ char *file;
+ grn_obj *commands;
+ int jobtype;
+ int ntimes;
+ int qnum;
+ int job_id;
+ long long int max;
+ long long int min;
+ socket_t http_socket;
+ grn_obj http_response;
+};
+
+static struct task grntest_task[MAX_CON];
+static struct job grntest_job[MAX_CON];
+static int grntest_jobdone;
+static int grntest_jobnum;
+static grn_ctx grntest_ctx[MAX_CON];
+static grn_obj *grntest_owndb[MAX_CON];
+
+static grn_obj grntest_starttime, grntest_jobs_start;
+
+static int
+grntest_atoi(const char *str, const char *end, const char **rest)
+{
+ while (grn_isspace(str, GRN_ENC_UTF8) == 1) {
+ str++;
+ }
+ return grn_atoi(str, end, rest);
+}
+
+static int
+out_p(int jobtype)
+{
+ if (jobtype == J_OUT_LOCAL) {
+ return 1;
+ }
+ if (jobtype == J_OUT_GQTP) {
+ return 1;
+ }
+ if (jobtype == J_OUT_HTTP) {
+ return 1;
+ }
+ return 0;
+}
+
+static int
+test_p(int jobtype)
+{
+ if (jobtype == J_TEST_LOCAL) {
+ return 1;
+ }
+ if (jobtype == J_TEST_GQTP) {
+ return 1;
+ }
+ if (jobtype == J_TEST_HTTP) {
+ return 1;
+ }
+ return 0;
+}
+
+static int
+report_p(int jobtype)
+{
+ if (jobtype == J_REP_LOCAL) {
+ return 1;
+ }
+ if (jobtype == J_REP_GQTP) {
+ return 1;
+ }
+ if (jobtype == J_REP_HTTP) {
+ return 1;
+ }
+ return 0;
+}
+
+static int
+gqtp_p(int jobtype)
+{
+ if (jobtype == J_DO_GQTP) {
+ return 1;
+ }
+ if (jobtype == J_REP_GQTP) {
+ return 1;
+ }
+ if (jobtype == J_OUT_GQTP) {
+ return 1;
+ }
+ if (jobtype == J_TEST_GQTP) {
+ return 1;
+ }
+ return 0;
+}
+
+static int
+http_p(int jobtype)
+{
+ if (jobtype == J_DO_HTTP) {
+ return 1;
+ }
+ if (jobtype == J_REP_HTTP) {
+ return 1;
+ }
+ if (jobtype == J_OUT_HTTP) {
+ return 1;
+ }
+ if (jobtype == J_TEST_HTTP) {
+ return 1;
+ }
+ return 0;
+}
+
+static int
+error_exit_in_thread(intptr_t code)
+{
+ fprintf(stderr,
+ "Fatal error! Check script file or database!: %ld\n", (long)code);
+ fflush(stderr);
+ CRITICAL_SECTION_ENTER(grntest_cs);
+ grntest_stop_flag = 1;
+ CRITICAL_SECTION_LEAVE(grntest_cs);
+#ifdef WIN32
+ _endthreadex(code);
+#else
+ pthread_exit((void *)code);
+#endif /* WIN32 */
+ return 0;
+}
+
+
+static void
+escape_command(grn_ctx *ctx, const char *in, int ilen, grn_obj *escaped_command)
+{
+ int i = 0;
+
+ while (i < ilen) {
+ if ((in[i] == '\\') || (in[i] == '\"') || (in[i] == '/')) {
+ GRN_TEXT_PUTC(ctx, escaped_command, '\\');
+ GRN_TEXT_PUTC(ctx, escaped_command, in[i]);
+ i++;
+ } else {
+ switch (in[i]) {
+ case '\b':
+ GRN_TEXT_PUTS(ctx, escaped_command, "\\b");
+ i++;
+ break;
+ case '\f':
+ GRN_TEXT_PUTS(ctx, escaped_command, "\\f");
+ i++;
+ break;
+ case '\n':
+ GRN_TEXT_PUTS(ctx, escaped_command, "\\n");
+ i++;
+ break;
+ case '\r':
+ GRN_TEXT_PUTS(ctx, escaped_command, "\\r");
+ i++;
+ break;
+ case '\t':
+ GRN_TEXT_PUTS(ctx, escaped_command, "\\t");
+ i++;
+ break;
+ default:
+ GRN_TEXT_PUTC(ctx, escaped_command, in[i]);
+ i++;
+ break;
+ }
+ }
+ }
+ GRN_TEXT_PUTC(ctx, escaped_command, '\0');
+}
+
+static int
+report_command(grn_ctx *ctx, const char *command, const char *ret, int task_id,
+ grn_obj *start_time, grn_obj *end_time)
+{
+ int i, len, clen;
+ long long int start, end;
+ grn_obj result, escaped_command;
+
+ GRN_TEXT_INIT(&result, 0);
+ if (strncmp(ret, "[[", 2) == 0) {
+ i = 2;
+ len = 1;
+ while (ret[i] != ']') {
+ i++;
+ len++;
+ if (ret[i] == '\0') {
+ fprintf(stderr, "Error results:command=[%s]\n", command);
+ error_exit_in_thread(3);
+ }
+ }
+ len++;
+ grn_text_esc(ctx, &result, ret + 1, len);
+ } else {
+ grn_text_esc(ctx, &result, ret, strlen(ret));
+ }
+
+ start = GRN_TIME_VALUE(start_time) - GRN_TIME_VALUE(&grntest_starttime);
+ end = GRN_TIME_VALUE(end_time) - GRN_TIME_VALUE(&grntest_starttime);
+ clen = strlen(command);
+ GRN_TEXT_INIT(&escaped_command, 0);
+ escape_command(ctx, command, clen, &escaped_command);
+ if (grntest_outtype == OUT_TSV) {
+ fprintf(grntest_log_file, "report\t%d\t%s\t%" GRN_FMT_LLD "\t%" GRN_FMT_LLD "\t%.*s\n",
+ task_id, GRN_TEXT_VALUE(&escaped_command), start, end,
+ (int)GRN_TEXT_LEN(&result), GRN_TEXT_VALUE(&result));
+ } else {
+ fprintf(grntest_log_file, "[%d, \"%s\", %" GRN_FMT_LLD ", %" GRN_FMT_LLD ", %.*s],\n",
+ task_id, GRN_TEXT_VALUE(&escaped_command), start, end,
+ (int)GRN_TEXT_LEN(&result), GRN_TEXT_VALUE(&result));
+ }
+ fflush(grntest_log_file);
+ GRN_OBJ_FIN(ctx, &escaped_command);
+ GRN_OBJ_FIN(ctx, &result);
+ return 0;
+}
+
+static int
+output_result_final(grn_ctx *ctx, int qnum)
+{
+ grn_obj end_time;
+ long long int latency, self;
+ double sec, qps;
+
+ GRN_TIME_INIT(&end_time, 0);
+ GRN_TIME_NOW(ctx, &end_time);
+
+ latency = GRN_TIME_VALUE(&end_time) - GRN_TIME_VALUE(&grntest_starttime);
+ self = latency;
+ sec = self / (double)1000000;
+ qps = (double)qnum / sec;
+ if (grntest_outtype == OUT_TSV) {
+ fprintf(grntest_log_file, "total\t%" GRN_FMT_LLD "\t%f\t%d\n", latency, qps, qnum);
+ } else {
+ fprintf(grntest_log_file,
+ "{\"total\": %" GRN_FMT_LLD ", \"qps\": %f, \"queries\": %d}]\n", latency, qps, qnum);
+ }
+ grn_obj_close(ctx, &end_time);
+ return 0;
+}
+
+static int
+output_sysinfo(char *sysinfo)
+{
+ if (grntest_outtype == OUT_TSV) {
+ fprintf(grntest_log_file, "%s", sysinfo);
+ } else {
+ fprintf(grntest_log_file, "[%s\n", sysinfo);
+ }
+ return 0;
+}
+
+/* #define ENABLE_ERROR_REPORT 1 */
+#ifdef ENABLE_ERROR_REPORT
+static int
+error_command(grn_ctx *ctx, char *command, int task_id)
+{
+ fprintf(stderr, "error!:command=[%s] task_id = %d\n", command, task_id);
+ fflush(stderr);
+ error_exit_in_thread(1);
+ return 0;
+}
+#endif
+
+static void
+normalize_output(char *output, int length,
+ char **normalized_output, int *normalized_length)
+{
+ int i;
+
+ *normalized_output = NULL;
+ *normalized_length = length;
+ for (i = 0; i < length; i++) {
+ if (!strncmp(output + i, "],", 2)) {
+ *normalized_output = output + i + 2;
+ *normalized_length -= i + 2;
+ break;
+ }
+ }
+
+ if (!*normalized_output) {
+ if (length > 2 && strncmp(output + length - 2, "]]", 2)) {
+ *normalized_output = output + length;
+ *normalized_length = 0;
+ } else {
+ *normalized_output = output;
+ }
+ }
+}
+
+static grn_bool
+same_result_p(char *expect, int expected_length, char *result, int result_length)
+{
+ char *normalized_expected, *normalized_result;
+ int normalized_expected_length, normalized_result_length;
+
+ normalize_output(expect, expected_length,
+ &normalized_expected, &normalized_expected_length);
+ normalize_output(result, result_length,
+ &normalized_result, &normalized_result_length);
+
+ return((normalized_expected_length == normalized_result_length) &&
+ strncmp(normalized_expected, normalized_result,
+ normalized_expected_length) == 0);
+}
+
+static socket_t
+open_socket(const char *host, int port)
+{
+ socket_t sock;
+ struct hostent *servhost;
+ struct sockaddr_in server;
+ u_long inaddr;
+ int ret;
+
+ servhost = gethostbyname(host);
+ if (servhost == NULL){
+ fprintf(stderr, "Bad hostname [%s]\n", host);
+ return -1;
+ }
+ inaddr = *(u_long*)(servhost->h_addr_list[0]);
+
+ memset(&server, 0, sizeof(struct sockaddr_in));
+ server.sin_family = AF_INET;
+ server.sin_port = htons(port);
+ server.sin_addr = *(struct in_addr*)&inaddr;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock == -1) {
+ fprintf(stderr, "socket error\n");
+ return -1;
+ }
+ ret = connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_in));
+ if (ret == -1) {
+ fprintf(stderr, "connect error\n");
+ return -1;
+ }
+ return sock;
+}
+
+static int
+write_to_server(socket_t socket, const char *buf)
+{
+#ifdef DEBUG_FTP
+ fprintf(stderr, "send:%s", buf);
+#endif
+ send(socket, buf, strlen(buf), 0);
+ return 0;
+}
+
+#define OUTPUT_TYPE "output_type"
+#define OUTPUT_TYPE_LEN (sizeof(OUTPUT_TYPE) - 1)
+
+static void
+command_line_to_uri_path(grn_ctx *ctx, grn_obj *uri, const char *command)
+{
+ char tok_type;
+ int offset = 0, have_key = 0;
+ const char *p, *e, *v;
+ grn_obj buf, *expr = NULL;
+ grn_expr_var *vars;
+ unsigned int nvars;
+
+ GRN_TEXT_INIT(&buf, 0);
+ p = command;
+ e = command + strlen(command);
+ p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
+ if ((expr = grn_ctx_get(ctx, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf)))) {
+ grn_obj params, output_type;
+
+ GRN_TEXT_INIT(&params, 0);
+ GRN_TEXT_INIT(&output_type, 0);
+ vars = ((grn_proc *)expr)->vars;
+ nvars = ((grn_proc *)expr)->nvars;
+ GRN_TEXT_PUTS(ctx, uri, "/d/");
+ GRN_TEXT_PUT(ctx, uri, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ while (p < e) {
+ GRN_BULK_REWIND(&buf);
+ p = grn_text_unesc_tok(ctx, &buf, p, e, &tok_type);
+ v = GRN_TEXT_VALUE(&buf);
+ switch (tok_type) {
+ case GRN_TOK_VOID :
+ p = e;
+ break;
+ case GRN_TOK_SYMBOL :
+ if (GRN_TEXT_LEN(&buf) > 2 && v[0] == '-' && v[1] == '-') {
+ int l = GRN_TEXT_LEN(&buf) - 2;
+ v += 2;
+ if (l == OUTPUT_TYPE_LEN && !memcmp(v, OUTPUT_TYPE, OUTPUT_TYPE_LEN)) {
+ GRN_BULK_REWIND(&output_type);
+ p = grn_text_unesc_tok(ctx, &output_type, p, e, &tok_type);
+ break;
+ }
+ if (GRN_TEXT_LEN(&params)) {
+ GRN_TEXT_PUTS(ctx, &params, "&");
+ }
+ grn_text_urlenc(ctx, &params, v, l);
+ have_key = 1;
+ break;
+ }
+ /* fallthru */
+ case GRN_TOK_STRING :
+ case GRN_TOK_QUOTE :
+ if (!have_key) {
+ if (offset < nvars) {
+ if (GRN_TEXT_LEN(&params)) {
+ GRN_TEXT_PUTS(ctx, &params, "&");
+ }
+ grn_text_urlenc(ctx, &params,
+ vars[offset].name, vars[offset].name_size);
+ offset++;
+ }
+ }
+ GRN_TEXT_PUTS(ctx, &params, "=");
+ grn_text_urlenc(ctx, &params, GRN_TEXT_VALUE(&buf), GRN_TEXT_LEN(&buf));
+ have_key = 0;
+ break;
+ }
+ }
+ GRN_TEXT_PUTS(ctx, uri, ".");
+ if (GRN_TEXT_LEN(&output_type)) {
+ GRN_TEXT_PUT(ctx, uri,
+ GRN_TEXT_VALUE(&output_type), GRN_TEXT_LEN(&output_type));
+ } else {
+ GRN_TEXT_PUTS(ctx, uri, "json");
+ }
+ if (GRN_TEXT_LEN(&params) > 0) {
+ GRN_TEXT_PUTS(ctx, uri, "?");
+ GRN_TEXT_PUT(ctx, uri, GRN_TEXT_VALUE(&params), GRN_TEXT_LEN(&params));
+ }
+ GRN_OBJ_FIN(ctx, &params);
+ GRN_OBJ_FIN(ctx, &output_type);
+ }
+ GRN_OBJ_FIN(ctx, &buf);
+}
+
+static void
+command_send_http(grn_ctx *ctx, const char *command, int type, int task_id)
+{
+ socket_t http_socket;
+ grn_obj buf;
+
+ http_socket = open_socket(grntest_serverhost, grntest_serverport);
+ if (http_socket == SOCKETERROR) {
+ fprintf(stderr, "failed to connect to groonga at %s:%d via HTTP: ",
+ grntest_serverhost, grntest_serverport);
+#ifdef WIN32
+ fprintf(stderr, "%d\n", GetLastError());
+#else
+ fprintf(stderr, "%s\n", strerror(errno));
+#endif
+ error_exit_in_thread(100);
+ }
+ grntest_task[task_id].http_socket = http_socket;
+ GRN_BULK_REWIND(&grntest_task[task_id].http_response);
+
+ GRN_TEXT_INIT(&buf, 0);
+ GRN_TEXT_PUTS(ctx, &buf, "GET ");
+ if (strncmp(command, "/d/", 3) == 0) {
+ GRN_TEXT_PUTS(ctx, &buf, command);
+ } else {
+ command_line_to_uri_path(ctx, &buf, command);
+ }
+#ifdef DEBUG_HTTP
+ fprintf(stderr, "command: <%s>\n", command);
+ fprintf(stderr, "path: <%.*s>\n",
+ (int)GRN_TEXT_LEN(&buf), GRN_TEXT_VALUE(&buf));
+#endif
+ GRN_TEXT_PUTS(ctx, &buf, " HTTP/1.1\r\n");
+ GRN_TEXT_PUTS(ctx, &buf, "Host: ");
+ GRN_TEXT_PUTS(ctx, &buf, grntest_serverhost);
+ GRN_TEXT_PUTS(ctx, &buf, "\r\n");
+ GRN_TEXT_PUTS(ctx, &buf, "User-Agent: grntest/");
+ GRN_TEXT_PUTS(ctx, &buf, grn_get_version());
+ GRN_TEXT_PUTS(ctx, &buf, "\r\n");
+ GRN_TEXT_PUTS(ctx, &buf, "Connection: close\r\n");
+ GRN_TEXT_PUTS(ctx, &buf, "\r\n");
+ GRN_TEXT_PUTC(ctx, &buf, '\0');
+ write_to_server(http_socket, GRN_TEXT_VALUE(&buf));
+ GRN_OBJ_FIN(ctx, &buf);
+}
+
+static void
+command_send_ctx(grn_ctx *ctx, const char *command, int type, int task_id)
+{
+ grn_ctx_send(ctx, command, strlen(command), 0);
+/* fix me.
+ when command fails, ctx->rc is not 0 in local mode!
+ if (ctx->rc) {
+ fprintf(stderr, "ctx_send:rc=%d:command:%s\n", ctx->rc, command);
+ error_exit_in_thread(1);
+ }
+*/
+}
+
+static void
+command_send(grn_ctx *ctx, const char *command, int type, int task_id)
+{
+ if (http_p(type)) {
+ command_send_http(ctx, command, type, task_id);
+ } else {
+ command_send_ctx(ctx, command, type, task_id);
+ }
+}
+
+static void
+command_recv_http(grn_ctx *ctx, int type, int task_id,
+ char **res, unsigned int *res_len, int *flags)
+{
+ int len;
+ char buf[BUF_LEN];
+ char *p, *e;
+ socket_t http_socket;
+ grn_obj *http_response;
+
+ http_socket = grntest_task[task_id].http_socket;
+ http_response = &grntest_task[task_id].http_response;
+ while ((len = recv(http_socket, buf, BUF_LEN - 1, 0))) {
+#ifdef DEBUG_HTTP
+ fprintf(stderr, "receive: <%.*s>\n", len, buf);
+#endif
+ GRN_TEXT_PUT(ctx, http_response, buf, len);
+ }
+
+ p = GRN_TEXT_VALUE(http_response);
+ e = p + GRN_TEXT_LEN(http_response);
+ while (p < e) {
+ if (p[0] != '\r') {
+ p++;
+ continue;
+ }
+ if (e - p >= 4) {
+ if (!memcmp(p, "\r\n\r\n", 4)) {
+ *res = p + 4;
+ *res_len = e - *res;
+#ifdef DEBUG_HTTP
+ fprintf(stderr, "body: <%.*s>\n", *res_len, *res);
+#endif
+ break;
+ }
+ p += 4;
+ } else {
+ *res = NULL;
+ *res_len = 0;
+ break;
+ }
+ }
+
+ socketclose(http_socket);
+ grntest_task[task_id].http_socket = 0;
+}
+
+static void
+command_recv_ctx(grn_ctx *ctx, int type, int task_id,
+ char **res, unsigned int *res_len, int *flags)
+{
+ grn_ctx_recv(ctx, res, res_len, flags);
+ if (ctx->rc) {
+ fprintf(stderr, "ctx_recv:rc=%d\n", ctx->rc);
+ error_exit_in_thread(1);
+ }
+}
+
+static void
+command_recv(grn_ctx *ctx, int type, int task_id,
+ char **res, unsigned int *res_len, int *flags)
+{
+ if (http_p(type)) {
+ command_recv_http(ctx, type, task_id, res, res_len, flags);
+ } else {
+ command_recv_ctx(ctx, type, task_id, res, res_len, flags);
+ }
+}
+
+static int
+shutdown_server(void)
+{
+ char *res;
+ int flags;
+ unsigned int res_len;
+ int job_type;
+ int task_id = 0;
+
+ if (grntest_remote_mode) {
+ return 0;
+ }
+ job_type = grntest_task[task_id].jobtype;
+ command_send(&grntest_server_context, "shutdown", job_type, task_id);
+ if (grntest_server_context.rc) {
+ fprintf(stderr, "ctx_send:rc=%d\n", grntest_server_context.rc);
+ exit(1);
+ }
+ command_recv(&grntest_server_context, job_type, task_id,
+ &res, &res_len, &flags);
+
+ return 0;
+}
+
+static int
+do_load_command(grn_ctx *ctx, char *command, int type, int task_id,
+ long long int *load_start)
+{
+ char *res;
+ unsigned int res_len;
+ int flags, ret;
+ grn_obj start_time, end_time;
+
+ GRN_TIME_INIT(&start_time, 0);
+ if (*load_start == 0) {
+ GRN_TIME_NOW(ctx, &start_time);
+ *load_start = GRN_TIME_VALUE(&start_time);
+ } else {
+ GRN_TIME_SET(ctx, &start_time, *load_start);
+ }
+
+ command_send(ctx, command, type, task_id);
+ do {
+ command_recv(ctx, type, task_id, &res, &res_len, &flags);
+ if (res_len) {
+ long long int self;
+ GRN_TIME_INIT(&end_time, 0);
+ GRN_TIME_NOW(ctx, &end_time);
+
+ self = GRN_TIME_VALUE(&end_time) - *load_start;
+
+ if (grntest_task[task_id].max < self) {
+ grntest_task[task_id].max = self;
+ }
+ if (grntest_task[task_id].min > self) {
+ grntest_task[task_id].min = self;
+ }
+
+ if (report_p(grntest_task[task_id].jobtype)) {
+ char tmpbuf[BUF_LEN];
+
+ if (res_len < BUF_LEN) {
+ strncpy(tmpbuf, res, res_len);
+ tmpbuf[res_len] = '\0';
+ } else {
+ strncpy(tmpbuf, res, BUF_LEN - 2);
+ tmpbuf[BUF_LEN -2] = '\0';
+ }
+ report_command(ctx, "load", tmpbuf, task_id, &start_time, &end_time);
+ }
+ if (out_p(grntest_task[task_id].jobtype)) {
+ fwrite(res, 1, res_len, grntest_job[grntest_task[task_id].job_id].outputlog);
+ fputc('\n', grntest_job[grntest_task[task_id].job_id].outputlog);
+ fflush(grntest_job[grntest_task[task_id].job_id].outputlog);
+ }
+ if (test_p(grntest_task[task_id].jobtype)) {
+ grn_obj log;
+ FILE *input;
+ FILE *output;
+ GRN_TEXT_INIT(&log, 0);
+ input = grntest_job[grntest_task[task_id].job_id].inputlog;
+ output = grntest_job[grntest_task[task_id].job_id].outputlog;
+ if (grn_text_fgets(ctx, &log, input) != GRN_SUCCESS) {
+ GRN_LOG(ctx, GRN_ERROR, "Cannot get input-log");
+ error_exit_in_thread(55);
+ }
+ if (GRN_TEXT_VALUE(&log)[GRN_TEXT_LEN(&log) - 1] == '\n') {
+ grn_bulk_truncate(ctx, &log, GRN_TEXT_LEN(&log) - 1);
+ }
+
+ if (!same_result_p(GRN_TEXT_VALUE(&log), GRN_TEXT_LEN(&log),
+ res, res_len)) {
+ fprintf(output, "DIFF:command:%s\n", command);
+ fprintf(output, "DIFF:result:");
+ fwrite(res, 1, res_len, output);
+ fputc('\n', output);
+ fprintf(output, "DIFF:expect:%.*s\n",
+ (int)GRN_TEXT_LEN(&log), GRN_TEXT_VALUE(&log));
+ fflush(output);
+ }
+ GRN_OBJ_FIN(ctx, &log);
+ }
+ grn_obj_close(ctx, &end_time);
+ ret = 1;
+ break;
+ } else {
+ ret = 0;
+ break;
+ }
+ } while ((flags & GRN_CTX_MORE));
+ grn_obj_close(ctx, &start_time);
+
+ return ret;
+}
+
+
+static int
+do_command(grn_ctx *ctx, char *command, int type, int task_id)
+{
+ char *res;
+ unsigned int res_len;
+ int flags;
+ grn_obj start_time, end_time;
+
+ GRN_TIME_INIT(&start_time, 0);
+ GRN_TIME_NOW(ctx, &start_time);
+
+ command_send(ctx, command, type, task_id);
+ do {
+ command_recv(ctx, type, task_id, &res, &res_len, &flags);
+ if (res_len) {
+ long long int self;
+ GRN_TIME_INIT(&end_time, 0);
+ GRN_TIME_NOW(ctx, &end_time);
+
+ self = GRN_TIME_VALUE(&end_time) - GRN_TIME_VALUE(&start_time);
+
+ if (grntest_task[task_id].max < self) {
+ grntest_task[task_id].max = self;
+ }
+ if (grntest_task[task_id].min > self) {
+ grntest_task[task_id].min = self;
+ }
+
+ if (report_p(grntest_task[task_id].jobtype)) {
+ char tmpbuf[BUF_LEN];
+
+ if (res_len < BUF_LEN) {
+ strncpy(tmpbuf, res, res_len);
+ tmpbuf[res_len] = '\0';
+ } else {
+ strncpy(tmpbuf, res, BUF_LEN - 2);
+ tmpbuf[BUF_LEN -2] = '\0';
+ }
+ report_command(ctx, command, tmpbuf, task_id, &start_time, &end_time);
+ }
+ if (out_p(grntest_task[task_id].jobtype)) {
+ fwrite(res, 1, res_len, grntest_job[grntest_task[task_id].job_id].outputlog);
+ fputc('\n', grntest_job[grntest_task[task_id].job_id].outputlog);
+ fflush(grntest_job[grntest_task[task_id].job_id].outputlog);
+ }
+ if (test_p(grntest_task[task_id].jobtype)) {
+ grn_obj log;
+ FILE *input;
+ FILE *output;
+ GRN_TEXT_INIT(&log, 0);
+ input = grntest_job[grntest_task[task_id].job_id].inputlog;
+ output = grntest_job[grntest_task[task_id].job_id].outputlog;
+ if (grn_text_fgets(ctx, &log, input) != GRN_SUCCESS) {
+ GRN_LOG(ctx, GRN_ERROR, "Cannot get input-log");
+ error_exit_in_thread(55);
+ }
+ if (GRN_TEXT_VALUE(&log)[GRN_TEXT_LEN(&log) - 1] == '\n') {
+ grn_bulk_truncate(ctx, &log, GRN_TEXT_LEN(&log) - 1);
+ }
+
+ if (!same_result_p(GRN_TEXT_VALUE(&log), GRN_TEXT_LEN(&log),
+ res, res_len)) {
+ fprintf(output, "DIFF:command:%s\n", command);
+ fprintf(output, "DIFF:result:");
+ fwrite(res, 1, res_len, output);
+ fputc('\n', output);
+ fprintf(output, "DIFF:expect:%.*s\n",
+ (int)GRN_TEXT_LEN(&log), GRN_TEXT_VALUE(&log));
+ fflush(output);
+ }
+ GRN_OBJ_FIN(ctx, &log);
+ }
+ grn_obj_close(ctx, &end_time);
+ break;
+ } else {
+#ifdef ENABLE_ERROR_REPORT
+ error_command(ctx, command, task_id);
+#endif
+ }
+ } while ((flags & GRN_CTX_MORE));
+
+ grn_obj_close(ctx, &start_time);
+
+ return 0;
+}
+
+static int
+comment_p(char *command)
+{
+ if (command[0] == '#') {
+ return 1;
+ }
+ return 0;
+}
+
+static int
+load_command_p(char *command)
+{
+ int i = 0;
+
+ while (grn_isspace(&command[i], GRN_ENC_UTF8) == 1) {
+ i++;
+ }
+ if (command[i] == '\0') {
+ return 0;
+ }
+ if (!strncmp(&command[i], "load", 4)) {
+ return 1;
+ }
+ return 0;
+}
+
+static int
+worker_sub(grn_ctx *ctx, grn_obj *log, int task_id)
+{
+ int i, load_mode, load_count;
+ grn_obj end_time;
+ long long int total_elapsed_time, job_elapsed_time;
+ double sec, qps;
+ long long int load_start;
+ struct task *task;
+ struct job *job;
+
+ task = &(grntest_task[task_id]);
+ task->max = 0LL;
+ task->min = 9223372036854775807LL;
+ task->qnum = 0;
+
+ for (i = 0; i < task->ntimes; i++) {
+ if (task->file != NULL) {
+ FILE *fp;
+ grn_obj line;
+ fp = fopen(task->file, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s\n",grntest_task[task_id].file);
+ error_exit_in_thread(1);
+ }
+ load_mode = 0;
+ load_count = 0;
+ load_start = 0LL;
+ GRN_TEXT_INIT(&line, 0);
+ while (grn_text_fgets(ctx, &line, fp) == GRN_SUCCESS) {
+ if (GRN_TEXT_VALUE(&line)[GRN_TEXT_LEN(&line) - 1] == '\n') {
+ grn_bulk_truncate(ctx, &line, GRN_TEXT_LEN(&line) - 1);
+ }
+ if (GRN_TEXT_LEN(&line) == 0) {
+ GRN_BULK_REWIND(&line);
+ continue;
+ }
+ GRN_TEXT_PUTC(ctx, &line, '\0');
+ if (comment_p(GRN_TEXT_VALUE(&line))) {
+ GRN_BULK_REWIND(&line);
+ continue;
+ }
+ if (load_command_p(GRN_TEXT_VALUE(&line))) {
+ load_mode = 1;
+ load_count = 1;
+ }
+ if (load_mode == 1) {
+ if (do_load_command(&grntest_ctx[task_id], GRN_TEXT_VALUE(&line),
+ task->jobtype,
+ task_id, &load_start)) {
+ task->qnum += load_count;
+ load_mode = 0;
+ load_count = 0;
+ load_start = 0LL;
+ }
+ load_count++;
+ GRN_BULK_REWIND(&line);
+ continue;
+ }
+ do_command(&grntest_ctx[task_id], GRN_TEXT_VALUE(&line),
+ task->jobtype,
+ task_id);
+ task->qnum++;
+ GRN_BULK_REWIND(&line);
+ if (grntest_sigint) {
+ goto exit;
+ }
+ }
+ GRN_OBJ_FIN(ctx, &line);
+ fclose(fp);
+ } else {
+ int i, n_commands;
+ grn_obj *commands;
+ commands = task->commands;
+ if (!commands) {
+ error_exit_in_thread(1);
+ }
+ load_mode = 0;
+ n_commands = GRN_BULK_VSIZE(commands) / sizeof(grn_obj *);
+ for (i = 0; i < n_commands; i++) {
+ grn_obj *command;
+ command = GRN_PTR_VALUE_AT(commands, i);
+ if (load_command_p(GRN_TEXT_VALUE(command))) {
+ load_mode = 1;
+ }
+ if (load_mode == 1) {
+ if (do_load_command(&grntest_ctx[task_id],
+ GRN_TEXT_VALUE(command),
+ task->jobtype, task_id, &load_start)) {
+ load_mode = 0;
+ load_start = 0LL;
+ task->qnum++;
+ }
+ continue;
+ }
+ do_command(&grntest_ctx[task_id],
+ GRN_TEXT_VALUE(command),
+ task->jobtype, task_id);
+ task->qnum++;
+ if (grntest_sigint) {
+ goto exit;
+ }
+ }
+ }
+ }
+
+exit:
+ GRN_TIME_INIT(&end_time, 0);
+ GRN_TIME_NOW(&grntest_ctx[task_id], &end_time);
+ total_elapsed_time = GRN_TIME_VALUE(&end_time) - GRN_TIME_VALUE(&grntest_starttime);
+ job_elapsed_time = GRN_TIME_VALUE(&end_time) - GRN_TIME_VALUE(&grntest_jobs_start);
+
+ CRITICAL_SECTION_ENTER(grntest_cs);
+ job = &(grntest_job[task->job_id]);
+ if (job->max < task->max) {
+ job->max = task->max;
+ }
+ if (job->min > task->min) {
+ job->min = task->min;
+ }
+
+ job->qnum += task->qnum;
+ job->done++;
+ if (job->done == job->concurrency) {
+ char tmpbuf[BUF_LEN];
+ sec = job_elapsed_time / (double)1000000;
+ qps = (double)job->qnum/ sec;
+ grntest_jobdone++;
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf,
+ "job\t"
+ "%s\t"
+ "%" GRN_FMT_LLD "\t"
+ "%" GRN_FMT_LLD "\t"
+ "%f\t"
+ "%" GRN_FMT_LLD "\t"
+ "%" GRN_FMT_LLD "\t"
+ "%d\n",
+ job->jobname,
+ total_elapsed_time,
+ job_elapsed_time,
+ qps,
+ job->min,
+ job->max,
+ job->qnum);
+ } else {
+ sprintf(tmpbuf,
+ "{\"job\": \"%s\", "
+ "\"total_elapsed_time\": %" GRN_FMT_LLD ", "
+ "\"job_elapsed_time\": %" GRN_FMT_LLD ", "
+ "\"qps\": %f, "
+ "\"min\": %" GRN_FMT_LLD ", "
+ "\"max\": %" GRN_FMT_LLD ", "
+ "\"queries\": %d}",
+ job->jobname,
+ total_elapsed_time,
+ job_elapsed_time,
+ qps,
+ job->min,
+ job->max,
+ job->qnum);
+ if (grntest_jobdone < grntest_jobnum) {
+ strcat(tmpbuf, ",");
+ }
+ }
+ GRN_TEXT_PUTS(ctx, log, tmpbuf);
+ if (grntest_jobdone == grntest_jobnum) {
+ if (grntest_outtype == OUT_TSV) {
+ fprintf(grntest_log_file, "%.*s",
+ (int)GRN_TEXT_LEN(log), GRN_TEXT_VALUE(log));
+ } else {
+ if (grntest_detail_on) {
+ fseek(grntest_log_file, -2, SEEK_CUR);
+ fprintf(grntest_log_file, "],\n");
+ }
+ fprintf(grntest_log_file, "\"summary\": [");
+ fprintf(grntest_log_file, "%.*s",
+ (int)GRN_TEXT_LEN(log), GRN_TEXT_VALUE(log));
+ fprintf(grntest_log_file, "]");
+ }
+ fflush(grntest_log_file);
+ }
+ }
+ grn_obj_close(&grntest_ctx[task_id], &end_time);
+ CRITICAL_SECTION_LEAVE(grntest_cs);
+
+ return 0;
+}
+
+typedef struct _grntest_worker {
+ grn_ctx *ctx;
+ grn_obj log;
+ int task_id;
+} grntest_worker;
+
+#ifdef WIN32
+static int
+__stdcall
+worker(void *val)
+{
+ grntest_worker *worker = val;
+ worker_sub(worker->ctx, &worker->log, worker->task_id);
+ return 0;
+}
+#else
+static void *
+worker(void *val)
+{
+ grntest_worker *worker = val;
+ worker_sub(worker->ctx, &worker->log, worker->task_id);
+ return NULL;
+}
+#endif /* WIN32 */
+
+#ifdef WIN32
+static int
+thread_main(grn_ctx *ctx, int num)
+{
+ int i;
+ int ret;
+ HANDLE pthread[MAX_CON];
+ grntest_worker *workers[MAX_CON];
+
+ for (i = 0; i < num; i++) {
+ workers[i] = GRN_MALLOC(sizeof(grntest_worker));
+ workers[i]->ctx = &grntest_ctx[i];
+ GRN_TEXT_INIT(&workers[i]->log, 0);
+ workers[i]->task_id = i;
+ pthread[i] = (HANDLE)_beginthreadex(NULL, 0, worker, (void *)workers[i],
+ 0, NULL);
+ if (pthread[i]== (HANDLE)0) {
+ fprintf(stderr, "thread failed:%d\n", i);
+ error_exit_in_thread(1);
+ }
+ }
+
+ ret = WaitForMultipleObjects(num, pthread, TRUE, INFINITE);
+ if (ret == WAIT_TIMEOUT) {
+ fprintf(stderr, "timeout\n");
+ error_exit_in_thread(1);
+ }
+
+ for (i = 0; i < num; i++) {
+ CloseHandle(pthread[i]);
+ GRN_OBJ_FIN(workers[i]->ctx, &workers[i]->log);
+ GRN_FREE(workers[i]);
+ }
+ return 0;
+}
+#else
+static int
+thread_main(grn_ctx *ctx, int num)
+{
+ intptr_t i;
+ int ret;
+ pthread_t pthread[MAX_CON];
+ grntest_worker *workers[MAX_CON];
+
+ for (i = 0; i < num; i++) {
+ workers[i] = GRN_MALLOC(sizeof(grntest_worker));
+ workers[i]->ctx = &grntest_ctx[i];
+ GRN_TEXT_INIT(&workers[i]->log, 0);
+ workers[i]->task_id = i;
+ ret = pthread_create(&pthread[i], NULL, worker, (void *)workers[i]);
+ if (ret) {
+ fprintf(stderr, "Cannot create thread:ret=%d\n", ret);
+ error_exit_in_thread(1);
+ }
+ }
+
+ for (i = 0; i < num; i++) {
+ ret = pthread_join(pthread[i], NULL);
+ GRN_OBJ_FIN(workers[i]->ctx, &workers[i]->log);
+ GRN_FREE(workers[i]);
+ if (ret) {
+ fprintf(stderr, "Cannot join thread:ret=%d\n", ret);
+ error_exit_in_thread(1);
+ }
+ }
+ return 0;
+}
+#endif
+
+static int
+error_exit(grn_ctx *ctx, int ret)
+{
+ fflush(stderr);
+ shutdown_server();
+ grn_ctx_fin(ctx);
+ grn_fin();
+ exit(ret);
+}
+
+static int
+get_sysinfo(const char *path, char *result, int olen)
+{
+ char tmpbuf[256];
+
+#ifdef WIN32
+ int cinfo[4];
+ ULARGE_INTEGER dinfo;
+ char cpustring[64];
+ SYSTEM_INFO sinfo;
+ MEMORYSTATUSEX minfo;
+ OSVERSIONINFO osinfo;
+
+ if (grntest_outtype == OUT_TSV) {
+ result[0] = '\0';
+ sprintf(tmpbuf, "script\t%s\n", grntest_scriptname);
+ strcat(result, tmpbuf);
+ sprintf(tmpbuf, "user\t%s\n", grntest_username);
+ strcat(result, tmpbuf);
+ sprintf(tmpbuf, "date\t%s\n", grntest_date);
+ strcat(result, tmpbuf);
+ } else {
+ strcpy(result, "{");
+ sprintf(tmpbuf, "\"script\": \"%s.scr\",\n", grntest_scriptname);
+ strcat(result, tmpbuf);
+ sprintf(tmpbuf, " \"user\": \"%s\",\n", grntest_username);
+ strcat(result, tmpbuf);
+ sprintf(tmpbuf, " \"date\": \"%s\",\n", grntest_date);
+ strcat(result, tmpbuf);
+ }
+
+ memset(cpustring, 0, 64);
+#ifndef __GNUC__
+ __cpuid(cinfo, 0x80000002);
+ memcpy(cpustring, cinfo, 16);
+ __cpuid(cinfo, 0x80000003);
+ memcpy(cpustring+16, cinfo, 16);
+ __cpuid(cinfo, 0x80000004);
+ memcpy(cpustring+32, cinfo, 16);
+#endif
+
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "%s\n", cpustring);
+ } else {
+ sprintf(tmpbuf, " \"CPU\": \"%s\",\n", cpustring);
+ }
+ strcat(result, tmpbuf);
+
+ if (sizeof(int *) == 8) {
+ grntest_osinfo = OS_WINDOWS64;
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "64BIT\n");
+ } else {
+ sprintf(tmpbuf, " \"BIT\": 64,\n");
+ }
+ } else {
+ grntest_osinfo = OS_WINDOWS32;
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "32BIT\n");
+ } else {
+ sprintf(tmpbuf, " \"BIT\": 32,\n");
+ }
+ }
+ strcat(result, tmpbuf);
+
+ GetSystemInfo(&sinfo);
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "CORE\t%d\n", sinfo.dwNumberOfProcessors);
+ } else {
+ sprintf(tmpbuf, " \"CORE\": %d,\n", sinfo.dwNumberOfProcessors);
+ }
+ strcat(result, tmpbuf);
+
+ minfo.dwLength = sizeof(MEMORYSTATUSEX);
+ GlobalMemoryStatusEx(&minfo);
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "RAM\t%I64dMByte\n", minfo.ullTotalPhys/(1024*1024));
+ } else {
+ sprintf(tmpbuf, " \"RAM\": \"%I64dMByte\",\n", minfo.ullTotalPhys/(1024*1024));
+ }
+ strcat(result, tmpbuf);
+
+ GetDiskFreeSpaceEx(NULL, NULL, &dinfo, NULL);
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "HDD\t%I64dKBytes\n", dinfo.QuadPart/1024 );
+ } else {
+ sprintf(tmpbuf, " \"HDD\": \"%I64dKBytes\",\n", dinfo.QuadPart/1024 );
+ }
+ strcat(result, tmpbuf);
+
+ osinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osinfo);
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "Windows %d.%d\n", osinfo.dwMajorVersion, osinfo.dwMinorVersion);
+ } else {
+ sprintf(tmpbuf, " \"OS\": \"Windows %d.%d\",\n", osinfo.dwMajorVersion,
+ osinfo.dwMinorVersion);
+ }
+ strcat(result, tmpbuf);
+
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "%s\n", grntest_serverhost);
+ } else {
+ sprintf(tmpbuf, " \"HOST\": \"%s\",\n", grntest_serverhost);
+ }
+ strcat(result, tmpbuf);
+
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "%d\n", grntest_serverport);
+ } else {
+ sprintf(tmpbuf, " \"PORT\": \"%d\",\n", grntest_serverport);
+ }
+ strcat(result, tmpbuf);
+
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "%s\"\n", grn_get_version());
+ } else {
+ sprintf(tmpbuf, " \"VERSION\": \"%s\"\n", grn_get_version());
+ }
+
+ strcat(result, tmpbuf);
+ if (grntest_outtype != OUT_TSV) {
+ strcat(result, "}");
+ }
+
+#else /* linux only */
+ FILE *fp;
+ int ret;
+ int cpunum = 0;
+ int minfo = 0;
+ int unevictable = 0;
+ int mlocked = 0;
+ char cpustring[256];
+ struct utsname ubuf;
+ struct statvfs vfsbuf;
+
+ if (grntest_outtype == OUT_TSV) {
+ result[0] = '\0';
+ sprintf(tmpbuf, "sctipt\t%s\n", grntest_scriptname);
+ strcat(result, tmpbuf);
+ sprintf(tmpbuf, "user\t%s\n", grntest_username);
+ strcat(result, tmpbuf);
+ sprintf(tmpbuf, "date\t%s\n", grntest_date);
+ strcat(result, tmpbuf);
+ } else {
+ strcpy(result, "{");
+ sprintf(tmpbuf, "\"script\": \"%s.scr\",\n", grntest_scriptname);
+ strcat(result, tmpbuf);
+ sprintf(tmpbuf, " \"user\": \"%s\",\n", grntest_username);
+ strcat(result, tmpbuf);
+ sprintf(tmpbuf, " \"date\": \"%s\",\n", grntest_date);
+ strcat(result, tmpbuf);
+ }
+
+ fp = fopen("/proc/cpuinfo", "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open cpuinfo\n");
+ exit(1);
+ }
+ while (fgets(tmpbuf, 256, fp) != NULL) {
+ tmpbuf[strlen(tmpbuf)-1] = '\0';
+ if (!strncmp(tmpbuf, "model name\t: ", 13)) {
+ strcpy(cpustring, &tmpbuf[13]);
+ }
+ }
+ fclose(fp);
+ cpunum = sysconf(_SC_NPROCESSORS_CONF);
+
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "%s\n", cpustring);
+ } else {
+ sprintf(tmpbuf, " \"CPU\": \"%s\",\n", cpustring);
+ }
+ strcat(result, tmpbuf);
+
+ if (sizeof(int *) == 8) {
+ grntest_osinfo = OS_LINUX64;
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "64BIT\n");
+ } else {
+ sprintf(tmpbuf, " \"BIT\": 64,\n");
+ }
+ } else {
+ grntest_osinfo = OS_LINUX32;
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "32BIT\n");
+ } else {
+ sprintf(tmpbuf, " \"BIT\": 32,\n");
+ }
+ }
+ strcat(result, tmpbuf);
+
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "CORE\t%d\n", cpunum);
+ } else {
+ sprintf(tmpbuf, " \"CORE\": %d,\n", cpunum);
+ }
+ strcat(result, tmpbuf);
+
+ fp = fopen("/proc/meminfo", "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open meminfo\n");
+ exit(1);
+ }
+ while (fgets(tmpbuf, 256, fp) != NULL) {
+ tmpbuf[strlen(tmpbuf)-1] = '\0';
+ if (!strncmp(tmpbuf, "MemTotal:", 9)) {
+ minfo = grntest_atoi(&tmpbuf[10], &tmpbuf[10] + 40, NULL);
+ }
+ if (!strncmp(tmpbuf, "Unevictable:", 12)) {
+ unevictable = grntest_atoi(&tmpbuf[13], &tmpbuf[13] + 40, NULL);
+ }
+ if (!strncmp(tmpbuf, "Mlocked:", 8)) {
+ mlocked = grntest_atoi(&tmpbuf[9], &tmpbuf[9] + 40, NULL);
+ }
+ }
+ fclose(fp);
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "%dMBytes\n", minfo/1024);
+ strcat(result, tmpbuf);
+ sprintf(tmpbuf, "%dMBytes_Unevictable\n", unevictable/1024);
+ strcat(result, tmpbuf);
+ sprintf(tmpbuf, "%dMBytes_Mlocked\n", mlocked/1024);
+ strcat(result, tmpbuf);
+ } else {
+ sprintf(tmpbuf, " \"RAM\": \"%dMBytes\",\n", minfo/1024);
+ strcat(result, tmpbuf);
+ sprintf(tmpbuf, " \"Unevictable\": \"%dMBytes\",\n", unevictable/1024);
+ strcat(result, tmpbuf);
+ sprintf(tmpbuf, " \"Mlocked\": \"%dMBytes\",\n", mlocked/1024);
+ strcat(result, tmpbuf);
+ }
+
+ ret = statvfs(path, &vfsbuf);
+ if (ret) {
+ fprintf(stderr, "Cannot access %s\n", path);
+ exit(1);
+ }
+
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "%" GRN_FMT_INT64U "KBytes\n", vfsbuf.f_blocks * 4);
+ } else {
+ sprintf(tmpbuf,
+ " \"HDD\": \"%" GRN_FMT_INT64U "KBytes\",\n",
+ vfsbuf.f_blocks * 4);
+ }
+ strcat(result, tmpbuf);
+
+ uname(&ubuf);
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "%s %s\n", ubuf.sysname, ubuf.release);
+ } else {
+ sprintf(tmpbuf, " \"OS\": \"%s %s\",\n", ubuf.sysname, ubuf.release);
+ }
+ strcat(result, tmpbuf);
+
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "%s\n", grntest_serverhost);
+ } else {
+ sprintf(tmpbuf, " \"HOST\": \"%s\",\n", grntest_serverhost);
+ }
+ strcat(result, tmpbuf);
+
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "%d\n", grntest_serverport);
+ } else {
+ sprintf(tmpbuf, " \"PORT\": \"%d\",\n", grntest_serverport);
+ }
+ strcat(result, tmpbuf);
+
+ if (grntest_outtype == OUT_TSV) {
+ sprintf(tmpbuf, "%s\n", grn_get_version());
+ } else {
+ sprintf(tmpbuf, " \"VERSION\": \"%s\"\n", grn_get_version());
+ }
+ strcat(result, tmpbuf);
+
+ if (grntest_outtype != OUT_TSV) {
+ strcat(result, "},");
+ }
+#endif /* WIN32 */
+ if (strlen(result) >= olen) {
+ fprintf(stderr, "buffer overrun in get_sysinfo!\n");
+ exit(1);
+ }
+ return 0;
+}
+
+static int
+start_server(const char *dbpath, int r)
+{
+ int ret;
+ char optbuf[BUF_LEN];
+#ifdef WIN32
+ char tmpbuf[BUF_LEN];
+
+ STARTUPINFO si;
+
+ if (strlen(dbpath) > BUF_LEN - 100) {
+ fprintf(stderr, "too long dbpath!\n");
+ exit(1);
+ }
+
+ strcpy(tmpbuf, groonga_path);
+ strcat(tmpbuf, " -s --protocol ");
+ strcat(tmpbuf, groonga_protocol);
+ strcat(tmpbuf, " -p ");
+ sprintf(optbuf, "%d ", grntest_serverport);
+ strcat(tmpbuf, optbuf);
+ strcat(tmpbuf, dbpath);
+ memset(&si, 0, sizeof(STARTUPINFO));
+ si.cb=sizeof(STARTUPINFO);
+ ret = CreateProcess(NULL, tmpbuf, NULL, NULL, FALSE,
+ 0, NULL, NULL, &si, &grntest_pi);
+
+ if (ret == 0) {
+ fprintf(stderr, "Cannot start groonga server: <%s>: error=%d\n",
+ groonga_path, GetLastError());
+ exit(1);
+ }
+
+#else
+ pid_t pid;
+ pid = fork();
+ if (pid < 0) {
+ fprintf(stderr, "Cannot start groonga server:Cannot fork\n");
+ exit(1);
+ }
+ sprintf(optbuf, "%d", grntest_serverport);
+ if (pid == 0) {
+ ret = execlp(groonga_path, groonga_path,
+ "-s",
+ "--protocol", groonga_protocol,
+ "-p", optbuf,
+ dbpath, (char*)NULL);
+ if (ret == -1) {
+ fprintf(stderr, "Cannot start groonga server: <%s>: errno=%d\n",
+ groonga_path, errno);
+ exit(1);
+ }
+ }
+ else {
+ grntest_server_id = pid;
+ }
+
+#endif /* WIN32 */
+
+ return 0;
+}
+
+static int
+parse_line(char *buf, int start, int end, int num)
+{
+ int i, j, error_flag = 0, out_or_test = 0;
+ char tmpbuf[BUF_LEN];
+
+ grntest_job[num].concurrency = 1;
+ grntest_job[num].ntimes = 1;
+ grntest_job[num].done = 0;
+ grntest_job[num].qnum = 0;
+ grntest_job[num].max = 0LL;
+ grntest_job[num].min = 9223372036854775807LL;
+ grntest_job[num].outputlog = NULL;
+ grntest_job[num].inputlog = NULL;
+
+ strncpy(grntest_job[num].jobname, &buf[start], end - start);
+ grntest_job[num].jobname[end - start] = '\0';
+ i = start;
+ while (i < end) {
+ if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
+ i++;
+ continue;
+ }
+ if (!strncmp(&buf[i], "do_local", 8)) {
+ grntest_job[num].jobtype = J_DO_LOCAL;
+ i = i + 8;
+ break;
+ }
+ if (!strncmp(&buf[i], "do_gqtp", 7)) {
+ grntest_job[num].jobtype = J_DO_GQTP;
+ i = i + 7;
+ break;
+ }
+ if (!strncmp(&buf[i], "do_http", 7)) {
+ grntest_job[num].jobtype = J_DO_HTTP;
+ i = i + 7;
+ break;
+ }
+ if (!strncmp(&buf[i], "rep_local", 9)) {
+ grntest_job[num].jobtype = J_REP_LOCAL;
+ i = i + 9;
+ break;
+ }
+ if (!strncmp(&buf[i], "rep_gqtp", 8)) {
+ grntest_job[num].jobtype = J_REP_GQTP;
+ i = i + 8;
+ break;
+ }
+ if (!strncmp(&buf[i], "rep_http", 8)) {
+ grntest_job[num].jobtype = J_REP_HTTP;
+ i = i + 8;
+ break;
+ }
+ if (!strncmp(&buf[i], "out_local", 9)) {
+ grntest_job[num].jobtype = J_OUT_LOCAL;
+ i = i + 9;
+ out_or_test = 1;
+ break;
+ }
+ if (!strncmp(&buf[i], "out_gqtp", 8)) {
+ grntest_job[num].jobtype = J_OUT_GQTP;
+ i = i + 8;
+ out_or_test = 1;
+ break;
+ }
+ if (!strncmp(&buf[i], "out_http", 8)) {
+ grntest_job[num].jobtype = J_OUT_HTTP;
+ i = i + 8;
+ out_or_test = 1;
+ break;
+ }
+ if (!strncmp(&buf[i], "test_local", 10)) {
+ grntest_job[num].jobtype = J_TEST_LOCAL;
+ i = i + 10;
+ out_or_test = 1;
+ break;
+ }
+ if (!strncmp(&buf[i], "test_gqtp", 9)) {
+ grntest_job[num].jobtype = J_TEST_GQTP;
+ i = i + 9;
+ out_or_test = 1;
+ break;
+ }
+ if (!strncmp(&buf[i], "test_http", 9)) {
+ grntest_job[num].jobtype = J_TEST_HTTP;
+ i = i + 9;
+ out_or_test = 1;
+ break;
+ }
+ error_flag = 1;
+ i++;
+ }
+
+ if (error_flag) {
+ return 3;
+ }
+ if (i == end) {
+ return 1;
+ }
+
+ if (grn_isspace(&buf[i], GRN_ENC_UTF8) != 1) {
+ return 4;
+ }
+ i++;
+
+ while (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
+ i++;
+ continue;
+ }
+ j = 0;
+ while (i < end) {
+ if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
+ break;
+ }
+ grntest_job[num].commandfile[j] = buf[i];
+ i++;
+ j++;
+ if (j > 255) {
+ return 5;
+ }
+ }
+ grntest_job[num].commandfile[j] = '\0';
+
+ while (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
+ i++;
+ }
+
+ if (i == end) {
+ if (out_or_test) {
+ fprintf(stderr, "log(test)_local(gqtp|http) needs log(test)_filename\n");
+ return 11;
+ }
+ return 0;
+ }
+
+ j = 0;
+ while (i < end) {
+ if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
+ break;
+ }
+ tmpbuf[j] = buf[i];
+ i++;
+ j++;
+ if (j >= BUF_LEN) {
+ return 6;
+ }
+ }
+ tmpbuf[j] ='\0';
+ if (out_or_test) {
+ if (out_p(grntest_job[num].jobtype)) {
+ grntest_job[num].outputlog = fopen(tmpbuf, "wb");
+ if (grntest_job[num].outputlog == NULL) {
+ fprintf(stderr, "Cannot open %s\n", tmpbuf);
+ return 13;
+ }
+ } else {
+ char outlog[BUF_LEN];
+ grntest_job[num].inputlog = fopen(tmpbuf, "rb");
+ if (grntest_job[num].inputlog == NULL) {
+ fprintf(stderr, "Cannot open %s\n", tmpbuf);
+ return 14;
+ }
+ sprintf(outlog, "%s.diff", tmpbuf);
+ grntest_job[num].outputlog = fopen(outlog, "wb");
+ if (grntest_job[num].outputlog == NULL) {
+ fprintf(stderr, "Cannot open %s\n", outlog);
+ return 15;
+ }
+ }
+ strcpy(grntest_job[num].logfile, tmpbuf);
+ return 0;
+ } else {
+ grntest_job[num].concurrency = grntest_atoi(tmpbuf, tmpbuf + j, NULL);
+ if (grntest_job[num].concurrency == 0) {
+ return 7;
+ }
+ }
+
+ while (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
+ i++;
+ }
+
+ if (i == end) {
+ return 0;
+ }
+
+ j = 0;
+ while (i < end) {
+ if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
+ break;
+ }
+ tmpbuf[j] = buf[i];
+ i++;
+ j++;
+ if (j > 16) {
+ return 8;
+ }
+ }
+ tmpbuf[j] ='\0';
+ grntest_job[num].ntimes = grntest_atoi(tmpbuf, tmpbuf + j, NULL);
+ if (grntest_job[num].ntimes == 0) {
+ return 9;
+ }
+ if (i == end) {
+ return 0;
+ }
+
+ while (i < end) {
+ if (grn_isspace(&buf[i], GRN_ENC_UTF8) == 1) {
+ i++;
+ continue;
+ }
+ return 10;
+ }
+ return 0;
+}
+
+static int
+get_jobs(grn_ctx *ctx, char *buf, int line)
+{
+ int i, len, start, end, ret;
+ int jnum = 0;
+
+ len = strlen(buf);
+ i = 0;
+ while (i < len) {
+ if ((buf[i] == '#') || (buf[i] == '\r') || (buf[i] == '\n')) {
+ buf[i] = '\0';
+ len = i;
+ break;
+ }
+ i++;
+ }
+
+ i = 0;
+ start = 0;
+ while (i < len) {
+ if (buf[i] == ';') {
+ end = i;
+ ret = parse_line(buf, start, end, jnum);
+ if (ret) {
+ if (ret > 1) {
+ fprintf(stderr, "Syntax error:line=%d:ret=%d:%s\n", line, ret, buf);
+ error_exit(ctx, 1);
+ }
+ } else {
+ jnum++;
+ }
+ start = end + 1;
+ }
+ i++;
+ }
+ end = len;
+ ret = parse_line(buf, start, end, jnum);
+ if (ret) {
+ if (ret > 1) {
+ fprintf(stderr, "Syntax error:line=%d:ret=%d:%s\n", line, ret, buf);
+ error_exit(ctx, 1);
+ }
+ } else {
+ jnum++;
+ }
+ return jnum;
+}
+
+static int
+make_task_table(grn_ctx *ctx, int jobnum)
+{
+ int i, j;
+ int tid = 0;
+ FILE *fp;
+ grn_obj *commands = NULL;
+
+ for (i = 0; i < jobnum; i++) {
+ if ((grntest_job[i].concurrency == 1) && (!grntest_onmemory_mode)) {
+ grntest_task[tid].file = grntest_job[i].commandfile;
+ grntest_task[tid].commands = NULL;
+ grntest_task[tid].ntimes = grntest_job[i].ntimes;
+ grntest_task[tid].jobtype = grntest_job[i].jobtype;
+ grntest_task[tid].job_id = i;
+ tid++;
+ continue;
+ }
+ for (j = 0; j < grntest_job[i].concurrency; j++) {
+ if (j == 0) {
+ grn_obj line;
+ GRN_TEXT_INIT(&line, 0);
+ commands = grn_obj_open(ctx, GRN_PVECTOR, 0, GRN_VOID);
+ if (!commands) {
+ fprintf(stderr, "Cannot alloc commands\n");
+ error_exit(ctx, 1);
+ }
+ fp = fopen(grntest_job[i].commandfile, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot alloc commandfile:%s\n",
+ grntest_job[i].commandfile);
+ error_exit(ctx, 1);
+ }
+ while (grn_text_fgets(ctx, &line, fp) == GRN_SUCCESS) {
+ grn_obj *command;
+ if (GRN_TEXT_VALUE(&line)[GRN_TEXT_LEN(&line) - 1] == '\n') {
+ grn_bulk_truncate(ctx, &line, GRN_TEXT_LEN(&line) - 1);
+ }
+ if (GRN_TEXT_LEN(&line) == 0) {
+ GRN_BULK_REWIND(&line);
+ continue;
+ }
+ GRN_TEXT_PUTC(ctx, &line, '\0');
+ if (comment_p(GRN_TEXT_VALUE(&line))) {
+ GRN_BULK_REWIND(&line);
+ continue;
+ }
+ command = grn_obj_open(ctx, GRN_BULK, 0, GRN_VOID);
+ if (!command) {
+ fprintf(stderr, "Cannot alloc command: %s: %s\n",
+ grntest_job[i].commandfile, GRN_TEXT_VALUE(&line));
+ GRN_OBJ_FIN(ctx, &line);
+ error_exit(ctx, 1);
+ }
+ GRN_TEXT_SET(ctx, command, GRN_TEXT_VALUE(&line), GRN_TEXT_LEN(&line));
+ GRN_PTR_PUT(ctx, commands, command);
+ GRN_BULK_REWIND(&line);
+ }
+ GRN_OBJ_FIN(ctx, &line);
+ }
+ grntest_task[tid].file = NULL;
+ grntest_task[tid].commands = commands;
+ grntest_task[tid].ntimes = grntest_job[i].ntimes;
+ grntest_task[tid].jobtype = grntest_job[i].jobtype;
+ grntest_task[tid].job_id = i;
+ tid++;
+ }
+ }
+ return tid;
+}
+
+/*
+static int
+print_commandlist(int task_id)
+{
+ int i;
+
+ for (i = 0; i < GRN_TEXT_LEN(grntest_task[task_id].commands); i++) {
+ grn_obj *command;
+ command = GRN_PTR_VALUE_AT(grntest_task[task_id].commands, i);
+ printf("%s\n", GRN_TEXT_VALUE(command));
+ }
+ return 0;
+}
+*/
+
+/* return num of query */
+static int
+do_jobs(grn_ctx *ctx, int jobnum, int line)
+{
+ int i, task_num, ret, qnum = 0, thread_num = 0;
+
+ for (i = 0; i < jobnum; i++) {
+/*
+printf("%d:type =%d:file=%s:con=%d:ntimes=%d\n", i, grntest_job[i].jobtype,
+ grntest_job[i].commandfile, JobTable[i].concurrency, JobTable[i].ntimes);
+
+*/
+ thread_num = thread_num + grntest_job[i].concurrency;
+ }
+
+ if (thread_num >= MAX_CON) {
+ fprintf(stderr, "Too many threads requested(MAX=64):line=%d\n", line);
+ error_exit(ctx, 1);
+ }
+
+ task_num = make_task_table(ctx, jobnum);
+ if (task_num != thread_num) {
+ fprintf(stderr, "Logical error\n");
+ error_exit(ctx, 9);
+ }
+
+ grntest_detail_on = 0;
+ for (i = 0; i < task_num; i++) {
+ grn_ctx_init(&grntest_ctx[i], 0);
+ grntest_owndb[i] = NULL;
+ if (gqtp_p(grntest_task[i].jobtype)) {
+ ret = grn_ctx_connect(&grntest_ctx[i], grntest_serverhost, grntest_serverport, 0);
+ if (ret) {
+ fprintf(stderr, "Cannot connect groonga server:host=%s:port=%d:ret=%d\n",
+ grntest_serverhost, grntest_serverport, ret);
+ error_exit(ctx, 1);
+ }
+ } else if (http_p(grntest_task[i].jobtype)) {
+ grntest_task[i].http_socket = 0;
+ GRN_TEXT_INIT(&grntest_task[i].http_response, 0);
+ if (grntest_owndb_mode) {
+ grntest_owndb[i] = grn_db_open(&grntest_ctx[i], grntest_dbpath);
+ if (grntest_owndb[i] == NULL) {
+ fprintf(stderr, "Cannot open db:%s\n", grntest_dbpath);
+ exit(1);
+ }
+ } else {
+ grntest_owndb[i] = grn_db_create(&grntest_ctx[i], NULL, NULL);
+ }
+ } else {
+ if (grntest_owndb_mode) {
+ grntest_owndb[i] = grn_db_open(&grntest_ctx[i], grntest_dbpath);
+ if (grntest_owndb[i] == NULL) {
+ fprintf(stderr, "Cannot open db:%s\n", grntest_dbpath);
+ exit(1);
+ }
+ }
+ else {
+ grn_ctx_use(&grntest_ctx[i], grntest_db);
+ }
+ }
+ if (report_p(grntest_task[i].jobtype)) {
+ grntest_detail_on++;
+ }
+ }
+ if (grntest_detail_on) {
+ if (grntest_outtype == OUT_TSV) {
+ ;
+ }
+ else {
+ fprintf(grntest_log_file, "\"detail\": [\n");
+ }
+ fflush(grntest_log_file);
+ }
+
+ thread_main(ctx, task_num);
+
+ for (i = 0; i < task_num; i++) {
+ if (grntest_owndb[i]) {
+ grn_obj_close(&grntest_ctx[i], grntest_owndb[i]);
+ }
+ if (http_p(grntest_task[i].jobtype)) {
+ GRN_OBJ_FIN(&grntest_ctx[i], &grntest_task[i].http_response);
+ }
+ grn_ctx_fin(&grntest_ctx[i]);
+ qnum = qnum + grntest_task[i].qnum;
+ }
+
+ i = 0;
+ while (i < task_num) {
+ int job_id;
+ if (grntest_task[i].commands) {
+ job_id = grntest_task[i].job_id;
+ GRN_OBJ_FIN(ctx, grntest_task[i].commands);
+ while (job_id == grntest_task[i].job_id) {
+ i++;
+ }
+ } else {
+ i++;
+ }
+ }
+ for (i = 0; i < jobnum; i++) {
+ if (grntest_job[i].outputlog) {
+ int ret;
+ ret = fclose(grntest_job[i].outputlog);
+ if (ret) {
+ fprintf(stderr, "Cannot close %s\n", grntest_job[i].logfile);
+ exit(1);
+ }
+ }
+ if (grntest_job[i].inputlog) {
+ int ret;
+ ret = fclose(grntest_job[i].inputlog);
+ if (ret) {
+ fprintf(stderr, "Cannot close %s\n", grntest_job[i].logfile);
+ exit(1);
+ }
+ }
+ }
+ return qnum;
+}
+
+/* return num of query */
+static int
+do_script(grn_ctx *ctx, const char *script_file_path)
+{
+ int n_lines = 0;
+ int n_jobs;
+ int n_queries, total_n_queries = 0;
+ FILE *script_file;
+ grn_obj line;
+
+ script_file = fopen(script_file_path, "r");
+ if (script_file == NULL) {
+ fprintf(stderr, "Cannot open script file: <%s>\n", script_file_path);
+ error_exit(ctx, 1);
+ }
+
+ GRN_TEXT_INIT(&line, 0);
+ while (grn_text_fgets(ctx, &line, script_file) == GRN_SUCCESS) {
+ if (grntest_sigint) {
+ break;
+ }
+ n_lines++;
+ grntest_jobdone = 0;
+ n_jobs = get_jobs(ctx, GRN_TEXT_VALUE(&line), n_lines);
+ grntest_jobnum = n_jobs;
+
+ if (n_jobs > 0) {
+ GRN_TIME_INIT(&grntest_jobs_start, 0);
+ GRN_TIME_NOW(ctx, &grntest_jobs_start);
+ if (grntest_outtype == OUT_TSV) {
+ fprintf(grntest_log_file, "jobs-start\t%s\n", GRN_TEXT_VALUE(&line));
+ } else {
+ fprintf(grntest_log_file, "{\"jobs\": \"%s\",\n", GRN_TEXT_VALUE(&line));
+ }
+ n_queries = do_jobs(ctx, n_jobs, n_lines);
+ if (grntest_outtype == OUT_TSV) {
+ fprintf(grntest_log_file, "jobs-end\t%s\n", GRN_TEXT_VALUE(&line));
+ } else {
+ fprintf(grntest_log_file, "},\n");
+ }
+ total_n_queries += n_queries;
+
+ grn_obj_close(ctx, &grntest_jobs_start);
+ }
+ if (grntest_stop_flag) {
+ fprintf(stderr, "Error:Quit\n");
+ break;
+ }
+ GRN_BULK_REWIND(&line);
+ }
+ grn_obj_unlink(ctx, &line);
+
+ fclose(script_file);
+
+ return total_n_queries;
+}
+
+static int
+start_local(grn_ctx *ctx, const char *dbpath)
+{
+ grntest_db = grn_db_open(ctx, dbpath);
+ if (!grntest_db) {
+ grntest_db = grn_db_create(ctx, dbpath, NULL);
+ }
+ if (!grntest_db) {
+ fprintf(stderr, "Cannot open db:%s\n", dbpath);
+ exit(1);
+ }
+ return 0;
+}
+
+static int
+check_server(grn_ctx *ctx)
+{
+ int ret, retry = 0;
+ while (1) {
+ ret = grn_ctx_connect(ctx, grntest_serverhost, grntest_serverport, 0);
+ if (ret == GRN_CONNECTION_REFUSED) {
+ grn_sleep(1);
+ retry++;
+ if (retry > 5) {
+ fprintf(stderr, "Cannot connect groonga server:host=%s:port=%d:ret=%d\n",
+ grntest_serverhost, grntest_serverport, ret);
+ return 1;
+ }
+ continue;
+ }
+ if (ret) {
+ fprintf(stderr, "Cannot connect groonga server:host=%s:port=%d:ret=%d\n",
+ grntest_serverhost, grntest_serverport, ret);
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+#define MODE_LIST 1
+#define MODE_GET 2
+#define MODE_PUT 3
+#define MODE_TIME 4
+
+static int
+check_response(char *buf)
+{
+ if (buf[0] == '1') {
+ return 1;
+ }
+ if (buf[0] == '2') {
+ return 1;
+ }
+ if (buf[0] == '3') {
+ return 1;
+ }
+ return 0;
+}
+
+static int
+read_response(socket_t socket, char *buf)
+{
+ int ret;
+ ret = recv(socket, buf, BUF_LEN - 1, 0);
+ if (ret == -1) {
+ fprintf(stderr, "recv error:3\n");
+ exit(1);
+ }
+ buf[ret] ='\0';
+#ifdef DEBUG_FTP
+ fprintf(stderr, "recv:%s", buf);
+#endif
+ return ret;
+}
+
+static int
+put_file(socket_t socket, const char *filename)
+{
+ FILE *fp;
+ int c, ret, size = 0;
+ char buf[1];
+
+ fp = fopen(filename, "rb");
+ if (!fp) {
+ fprintf(stderr, "LOCAL:no such file:%s\n", filename);
+ return 0;
+ }
+
+ while ((c = fgetc(fp)) != EOF) {
+ buf[0] = c;
+ ret = send(socket, buf, 1, 0);
+ if (ret == -1) {
+ fprintf(stderr, "send error\n");
+ exit(1);
+ }
+ size++;
+ }
+ fclose(fp);
+ return size;
+}
+
+static int
+ftp_list(socket_t data_socket)
+{
+ int ret;
+ char buf[BUF_LEN];
+
+ while (1) {
+ ret = recv(data_socket, buf, BUF_LEN - 2, 0);
+ if (ret == 0) {
+ fflush(stdout);
+ return 0;
+ }
+ buf[ret] = '\0';
+ fprintf(stdout, "%s", buf);
+ }
+
+ return 0;
+}
+
+static int
+get_file(socket_t socket, const char *filename, int size)
+{
+ FILE *fp;
+ int ret, total;
+ char buf[FTPBUF];
+
+ fp = fopen(filename, "wb");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s\n", filename);
+ return -1;
+ }
+
+ total = 0;
+ while (total != size) {
+ ret = recv(socket, buf, FTPBUF, 0);
+ if (ret == -1) {
+ fprintf(stderr, "recv error:2:ret=%d:size=%d:total\n", ret, size);
+ return -1;
+ }
+ if (ret == 0) {
+ break;
+ }
+ fwrite(buf, ret, 1, fp);
+ total = total + ret;
+ }
+
+ fclose(fp);
+ return size;
+}
+
+static int
+get_port(char *buf, char *host, int *port)
+{
+ int ret,d1,d2,d3,d4,d5,d6;
+ ret = sscanf(buf, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)",
+ &d1, &d2, &d3, &d4, &d5, &d6);
+ if (ret != 6) {
+ fprintf(stderr, "Cannot enter passsive mode\n");
+ return 0;
+ }
+
+ *port = d5 * 256 + d6;
+ sprintf(host, "%d.%d.%d.%d", d1, d2, d3, d4);
+ return 1;
+}
+
+static char *
+get_ftp_date(char *buf)
+{
+ while (*buf !=' ') {
+ buf++;
+ if (*buf == '\0') {
+ return NULL;
+ }
+ }
+ buf++;
+
+ return buf;
+}
+
+static int
+get_size(char *buf)
+{
+ int size;
+
+ while (*buf !='(') {
+ buf++;
+ if (*buf == '\0') {
+ return 0;
+ }
+ }
+ buf++;
+ size = grntest_atoi(buf, buf + strlen(buf), NULL);
+
+ return size;
+}
+
+int
+ftp_sub(const char *user, const char *passwd, const char *host,
+ const char *filename, int mode,
+ const char *cd_dirname, char *retval)
+{
+ int size = 0;
+ int status = 0;
+ socket_t command_socket, data_socket;
+ int data_port;
+ char data_host[BUF_LEN];
+ char send_mesg[BUF_LEN];
+ char buf[BUF_LEN];
+#ifdef WIN32
+ char base[BUF_LEN];
+ char fname[BUF_LEN];
+ char ext[BUF_LEN];
+#else
+ char *base;
+#endif /* WIN32 */
+
+#ifdef WIN32
+ WSADATA ws;
+
+ WSAStartup(MAKEWORD(2,0), &ws);
+#endif /* WIN32 */
+
+ if ((filename != NULL) && (strlen(filename) >= MAX_PATH_LEN)) {
+ fprintf(stderr, "too long filename\n");
+ exit(1);
+ }
+
+ if ((cd_dirname != NULL) && (strlen(cd_dirname) >= MAX_PATH_LEN)) {
+ fprintf(stderr, "too long dirname\n");
+ exit(1);
+ }
+
+ command_socket = open_socket(host, 21);
+ if (command_socket == SOCKETERROR) {
+ return 0;
+ }
+
+ read_response(command_socket, buf);
+ if (!check_response(buf)) {
+ goto exit;
+ }
+
+ /* send username */
+ sprintf(send_mesg, "USER %s\r\n", user);
+ write_to_server(command_socket, send_mesg);
+ read_response(command_socket, buf);
+ if (!check_response(buf)) {
+ goto exit;
+ }
+
+ /* send passwd */
+ sprintf(send_mesg, "PASS %s\r\n", passwd);
+ write_to_server(command_socket, send_mesg);
+ read_response(command_socket, buf);
+ if (!check_response(buf)) {
+ goto exit;
+ }
+
+ /* send TYPE I */
+ sprintf(send_mesg, "TYPE I\r\n");
+ write_to_server(command_socket, send_mesg);
+ read_response(command_socket, buf);
+ if (!check_response(buf)) {
+ goto exit;
+ }
+
+ /* send PASV */
+ sprintf(send_mesg, "PASV\r\n");
+ write_to_server(command_socket, send_mesg);
+ read_response(command_socket, buf);
+ if (!check_response(buf)) {
+ goto exit;
+ }
+
+ if (!get_port(buf, data_host, &data_port)) {
+ goto exit;
+ }
+
+ data_socket = open_socket(data_host, data_port);
+ if (data_socket == SOCKETERROR) {
+ goto exit;
+ }
+
+ if (cd_dirname) {
+ sprintf(send_mesg, "CWD %s\r\n", cd_dirname);
+ write_to_server(command_socket, send_mesg);
+ }
+
+ read_response(command_socket, buf);
+ if (!check_response(buf)) {
+ socketclose(data_socket);
+ goto exit;
+ }
+
+#ifdef WIN32
+ _splitpath(filename, NULL, NULL, fname, ext);
+ strcpy(base, fname);
+ strcat(base, ext);
+#else
+ strcpy(buf, filename);
+ base = basename(buf);
+#endif /* WIN32 */
+
+ switch (mode) {
+ case MODE_LIST:
+ if (filename) {
+ sprintf(send_mesg, "LIST %s\r\n", filename);
+ } else {
+ sprintf(send_mesg, "LIST \r\n");
+ }
+ write_to_server(command_socket, send_mesg);
+ break;
+ case MODE_PUT:
+ sprintf(send_mesg, "STOR %s\r\n", base);
+ write_to_server(command_socket, send_mesg);
+ break;
+ case MODE_GET:
+ sprintf(send_mesg, "RETR %s\r\n", base);
+ write_to_server(command_socket, send_mesg);
+ break;
+ case MODE_TIME:
+ sprintf(send_mesg, "MDTM %s\r\n", base);
+ write_to_server(command_socket, send_mesg);
+ break;
+ default:
+ fprintf(stderr, "invalid mode\n");
+ socketclose(data_socket);
+ goto exit;
+ }
+
+ read_response(command_socket, buf);
+ if (!check_response(buf)) {
+ socketclose(data_socket);
+ goto exit;
+ }
+ if (!strncmp(buf, "150", 3)) {
+ size = get_size(buf);
+ }
+ if (!strncmp(buf, "213", 3)) {
+ retval[BUF_LEN-2] = '\0';
+ strcpy(retval, get_ftp_date(buf));
+ if (retval[BUF_LEN-2] != '\0' ) {
+ fprintf(stderr, "buffer over run in ftp\n");
+ exit(1);
+ }
+ }
+
+ switch (mode) {
+ case MODE_LIST:
+ ftp_list(data_socket);
+ break;
+ case MODE_GET:
+ if (get_file(data_socket, filename, size) == -1) {
+ socketclose(data_socket);
+ goto exit;
+ }
+ fprintf(stderr, "get:%s\n", filename);
+ break;
+ case MODE_PUT:
+ if (put_file(data_socket, filename) == -1) {
+ socketclose(data_socket);
+ goto exit;
+ }
+ fprintf(stderr, "put:%s\n", filename);
+ break;
+ default:
+ break;
+ }
+
+ socketclose(data_socket);
+ if ((mode == MODE_GET) || (mode == MODE_PUT)) {
+ read_response(command_socket, buf);
+ }
+ write_to_server(command_socket, "QUIT\n");
+ status = 1;
+exit:
+ socketclose(command_socket);
+
+#ifdef WIN32
+ WSACleanup();
+#endif
+ return status;
+}
+
+/*
+static int
+ftp_main(int argc, char **argv)
+{
+ char val[BUF_LEN];
+ val[0] = '\0';
+ ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, argv[2],
+ grntest_atoi(argv[3], argv[3] + strlen(argv[3]), NULL), argv[4], val);
+ if (val[0] != '\0') {
+ printf("val=%s\n", val);
+ }
+ return 0;
+}
+*/
+
+static int
+get_username(char *name, int maxlen)
+{
+ char *env=NULL;
+ strcpy(name, "nobody");
+#ifdef WIN32
+ env = getenv("USERNAME");
+#else
+ env = getenv("USER");
+#endif /* WIN32 */
+ if (strlen(env) > maxlen) {
+ fprintf(stderr, "too long username:%s\n", env);
+ exit(1);
+ }
+ if (env) {
+ strcpy(name, env);
+ }
+ return 0;
+}
+
+static int
+get_date(char *date, time_t *sec)
+{
+#if defined(WIN32) && !defined(__GNUC__)
+ struct tm tmbuf;
+ struct tm *tm = &tmbuf;
+ localtime_s(tm, sec);
+#else /* defined(WIN32) && !defined(__GNUC__) */
+# ifdef HAVE_LOCALTIME_R
+ struct tm result;
+ struct tm *tm = &result;
+ localtime_r(sec, tm);
+# else /* HAVE_LOCALTIME_R */
+ struct tm *tm = localtime(sec);
+# endif /* HAVE_LOCALTIME_R */
+#endif /* defined(WIN32) && !defined(__GNUC__) */
+
+#ifdef WIN32
+ strftime(date, 128, "%Y-%m-%d %H:%M:%S", tm);
+#else
+ strftime(date, 128, "%F %T", tm);
+#endif /* WIN32 */
+
+ return 1;
+}
+
+static int
+get_scriptname(const char *path, char *name, const char *suffix)
+{
+ int slen = strlen(suffix);
+ int len = strlen(path);
+
+ if (len >= BUF_LEN) {
+ fprintf(stderr, "too long script name\n");
+ exit(1);
+ }
+ if (slen > len) {
+ fprintf(stderr, "too long suffux\n");
+ exit(1);
+ }
+
+ strcpy(name, path);
+ if (strncmp(&name[len-slen], suffix, slen)) {
+ name[0] = '\0';
+ return 0;
+ }
+ name[len-slen] = '\0';
+ return 1;
+}
+
+#ifdef WIN32
+static int
+get_tm_from_serverdate(char *serverdate, struct tm *tm)
+{
+ int res;
+ int year, month, day, hour, minute, second;
+
+ res = sscanf(serverdate, "%4d%2d%2d%2d%2d%2d",
+ &year, &month, &day, &hour, &minute, &second);
+
+/*
+ printf("%d %d %d %d %d %d\n", year, month, day, hour, minute, second);
+*/
+
+ tm->tm_sec = second;
+ tm->tm_min = minute;
+ tm->tm_hour = hour;
+ tm->tm_mday = day;
+ tm->tm_mon = month - 1;
+ tm->tm_year = year - 1900;
+ tm->tm_isdst = -1;
+
+ return 0;
+}
+#endif /* WIN32 */
+
+
+
+static int
+sync_sub(grn_ctx *ctx, const char *filename)
+{
+ int ret;
+ char serverdate[BUF_LEN];
+#ifdef WIN32
+ struct _stat statbuf;
+#else
+ struct stat statbuf;
+#endif /* WIN32 */
+ time_t st, lt;
+ struct tm stm;
+
+ ret = ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, filename, MODE_TIME, "data",
+ serverdate);
+ if (ret == 0) {
+ fprintf(stderr, "[%s] does not exist in server\n", filename);
+ return 0;
+ }
+#ifdef WIN32
+ get_tm_from_serverdate(serverdate, &stm);
+#else
+ strptime(serverdate, "%Y%m%d %H%M%S", &stm);
+#endif /* WIN32 */
+
+ /* fixme! needs timezone info */
+ st = mktime(&stm) + 3600 * 9;
+ lt = st;
+
+#ifdef WIN32
+ ret = _stat(filename, &statbuf);
+#else
+ ret = stat(filename, &statbuf);
+#endif /* WIN32 */
+
+ if (!ret) {
+ lt = statbuf.st_mtime;
+ if (lt < st) {
+ fprintf(stderr, "newer [%s] exists in server\n", filename);
+ fflush(stderr);
+ ret = ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, filename, MODE_GET, "data",
+ NULL);
+ return ret;
+ }
+ } else {
+ fprintf(stderr, "[%s] does not exist in local\n", filename);
+ fflush(stderr);
+ ret = ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, filename, MODE_GET, "data",
+ NULL);
+ return ret;
+ }
+ return 0;
+}
+
+static int
+cache_file(grn_ctx *ctx, char **flist, char *file, int fnum)
+{
+ int i;
+
+ for (i = 0; i < fnum; i++) {
+ if (!strcmp(flist[i], file) ) {
+ return fnum;
+ }
+ }
+ flist[fnum] = GRN_STRDUP(file);
+ fnum++;
+ if (fnum >= BUF_LEN) {
+ fprintf(stderr, "too many uniq commands file!\n");
+ exit(1);
+ }
+ return fnum;
+}
+
+static int
+sync_datafile(grn_ctx *ctx, const char *script_file_path)
+{
+ int line = 0;
+ int fnum = 0;
+ int i, job_num;
+ FILE *fp;
+ char buf[BUF_LEN];
+ char *filelist[BUF_LEN];
+
+ fp = fopen(script_file_path, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "Cannot open script file: <%s>\n", script_file_path);
+ error_exit(ctx, 1);
+ }
+ buf[BUF_LEN-2] = '\0';
+ while (fgets(buf, BUF_LEN, fp) != NULL) {
+ line++;
+ if (buf[BUF_LEN-2] != '\0') {
+ fprintf(stderr, "Too long line in script file:%d\n", line);
+ error_exit(ctx, 1);
+ }
+ job_num = get_jobs(ctx, buf, line);
+
+ if (job_num > 0) {
+ for (i = 0; i < job_num; i++) {
+/*
+printf("commandfile=[%s]:buf=%s\n", grntest_job[i].commandfile, buf);
+*/
+ fnum = cache_file(ctx, filelist, grntest_job[i].commandfile, fnum);
+ }
+ }
+ }
+ for (i = 0; i < fnum; i++) {
+ if (sync_sub(ctx, filelist[i])) {
+ fprintf(stderr, "updated!:%s\n", filelist[i]);
+ fflush(stderr);
+ }
+ GRN_FREE(filelist[i]);
+ }
+ fclose(fp);
+ return fnum;
+}
+
+static int
+sync_script(grn_ctx *ctx, const char *filename)
+{
+ int ret, filenum;
+
+ ret = sync_sub(ctx, filename);
+ if (!ret) {
+ return 0;
+ }
+
+ fprintf(stderr, "updated!:%s\n", filename);
+ fflush(stderr);
+ filenum = sync_datafile(ctx, filename);
+ return 1;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "Usage: grntest [options...] [script] [db]\n"
+ "options:\n"
+ " --dir: show script files on ftp server\n"
+ " -i, --host <ip/hostname>: server address to listen (default: %s)\n"
+ " --localonly: omit server connection\n"
+ " --log-output-dir: specify output dir (default: current)\n"
+ " --ftp: connect to ftp server\n"
+ " --onmemory: load all commands into memory\n"
+ " --output-type <tsv/json>: specify output-type (default: json)\n"
+ " --owndb: open dbs for each ctx\n"
+ " -p, --port <port number>: server port number (default: %d)\n"
+ " --groonga <groonga_path>: groonga command path (default: %s)\n"
+ " --protocol <gqtp|http>: groonga server protocol (default: %s)\n"
+ " --log-path <path>: specify log file path\n"
+ " --pid-path <path>: specify file path to store PID file\n",
+ DEFAULT_DEST, DEFAULT_PORT,
+ groonga_path, groonga_protocol);
+ exit(1);
+}
+
+enum {
+ mode_default = 0,
+ mode_list,
+ mode_usage,
+};
+
+#define MODE_MASK 0x007f
+#define MODE_FTP 0x0080
+#define MODE_LOCALONLY 0x0100
+#define MODE_OWNDB 0x0800
+#define MODE_ONMEMORY 0x1000
+
+
+static int
+get_token(char *line, char *token, int maxlen, char **next)
+{
+ int i = 0;
+
+ *next = NULL;
+ token[i] = '\0';
+
+ while (*line) {
+ if (grn_isspace(line, GRN_ENC_UTF8) == 1) {
+ line++;
+ continue;
+ }
+ if (*line == ';') {
+ token[0] = ';';
+ token[1] = '\0';
+ *next = line + 1;
+ return 1;
+ }
+ if (*line == '#') {
+ token[0] = ';';
+ token[1] = '\0';
+ *next = line + 1;
+ return 1;
+ }
+ break;
+ }
+
+ while (*line) {
+ token[i] = *line;
+ i++;
+ if (grn_isspace(line + 1, GRN_ENC_UTF8) == 1) {
+ token[i] = '\0';
+ *next = line + 1;
+ return 1;
+ }
+ if (*(line + 1) == ';') {
+ token[i] = '\0';
+ *next = line + 1;
+ return 1;
+ }
+ if (*(line + 1) == '#') {
+ token[i] = '\0';
+ *next = line + 1;
+ return 1;
+ }
+ if (*(line + 1) == '\0') {
+ token[i] = '\0';
+ return 1;
+ }
+
+ line++;
+ }
+ return 0;
+}
+
+/* SET_PORT and SET_HOST */
+static grn_bool
+check_script(grn_ctx *ctx, const char *script_file_path)
+{
+ FILE *script_file;
+ grn_obj line;
+ char token[BUF_LEN];
+ char prev[BUF_LEN];
+ char *next = NULL;
+
+ script_file = fopen(script_file_path, "r");
+ if (!script_file) {
+ fprintf(stderr, "Cannot open script file: <%s>\n", script_file_path);
+ return GRN_FALSE;
+ }
+
+ GRN_TEXT_INIT(&line, 0);
+ while (grn_text_fgets(ctx, &line, script_file) == GRN_SUCCESS) {
+ GRN_TEXT_VALUE(&line)[GRN_TEXT_LEN(&line) - 1] = '\0';
+ get_token(GRN_TEXT_VALUE(&line), token, BUF_LEN, &next);
+ strcpy(prev, token);
+
+ while (next) {
+ get_token(next, token, BUF_LEN, &next);
+ if (!strncmp(prev, "SET_PORT", 8)) {
+ grntest_serverport = grn_atoi(token, token + strlen(token), NULL);
+ }
+ if (!strncmp(prev, "SET_HOST", 8)) {
+ strcpy(grntest_serverhost, token);
+ grntest_remote_mode = 1;
+ }
+ strcpy(prev, token);
+ }
+ }
+ grn_obj_unlink(ctx, &line);
+
+ fclose(script_file);
+ return GRN_TRUE;
+}
+
+#ifndef WIN32
+static void
+timeout(int sig)
+{
+ fprintf(stderr, "timeout:groonga server cannot shutdown!!\n");
+ fprintf(stderr, "Use \"kill -9 %d\"\n", grntest_server_id);
+ alarm(0);
+}
+
+static void
+setexit(int sig)
+{
+ grntest_sigint = 1;
+}
+
+static int
+setsigalarm(int sec)
+{
+ int ret;
+ struct sigaction sig;
+
+ alarm(sec);
+ sig.sa_handler = timeout;
+ sig.sa_flags = 0;
+ sigemptyset(&sig.sa_mask);
+ ret = sigaction(SIGALRM, &sig, NULL);
+ if (ret == -1) {
+ fprintf(stderr, "setsigalarm:errno= %d\n", errno);
+ }
+ return ret;
+}
+
+static int
+setsigint(void)
+{
+ int ret;
+ struct sigaction sig;
+
+ sig.sa_handler = setexit;
+ sig.sa_flags = 0;
+ sigemptyset(&sig.sa_mask);
+ ret = sigaction(SIGINT, &sig, NULL);
+ if (ret == -1) {
+ fprintf(stderr, "setsigint:errno= %d\n", errno);
+ }
+ return ret;
+}
+#endif /* WIN32 */
+
+int
+main(int argc, char **argv)
+{
+ int qnum, i, mode = 0;
+ int exit_code = EXIT_SUCCESS;
+ grn_ctx context;
+ char sysinfo[BUF_LEN];
+ char log_path_buffer[BUF_LEN];
+ const char *log_path = NULL;
+ const char *pid_path = NULL;
+ const char *portstr = NULL, *hoststr = NULL, *dbname = NULL, *scrname = NULL, *outdir = NULL, *outtype = NULL;
+ time_t sec;
+
+ static grn_str_getopt_opt opts[] = {
+ {'i', "host", NULL, 0, GETOPT_OP_NONE},
+ {'p', "port", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "log-output-dir", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "output-type", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "dir", NULL, mode_list, GETOPT_OP_UPDATE},
+ {'\0', "ftp", NULL, MODE_FTP, GETOPT_OP_ON},
+ {'h', "help", NULL, mode_usage, GETOPT_OP_UPDATE},
+ {'\0', "localonly", NULL, MODE_LOCALONLY, GETOPT_OP_ON},
+ {'\0', "onmemory", NULL, MODE_ONMEMORY, GETOPT_OP_ON},
+ {'\0', "owndb", NULL, MODE_OWNDB, GETOPT_OP_ON},
+ {'\0', "groonga", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "protocol", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "log-path", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "pid-path", NULL, 0, GETOPT_OP_NONE},
+ {'\0', NULL, NULL, 0, 0}
+ };
+
+ opts[0].arg = &hoststr;
+ opts[1].arg = &portstr;
+ opts[2].arg = &outdir;
+ opts[3].arg = &outtype;
+ opts[10].arg = &groonga_path;
+ opts[11].arg = &groonga_protocol;
+ opts[12].arg = &log_path;
+ opts[13].arg = &pid_path;
+
+ i = grn_str_getopt(argc, argv, opts, &mode);
+ if (i < 0) {
+ usage();
+ }
+
+ switch (mode & MODE_MASK) {
+ case mode_list :
+ ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, "*.scr", 1, "data",
+ NULL);
+ return 0;
+ break;
+ case mode_usage :
+ usage();
+ break;
+ default :
+ break;
+ }
+
+ if (pid_path) {
+ FILE *pid_file;
+ pid_file = fopen(pid_path, "w");
+ if (pid_file) {
+ fprintf(pid_file, "%d", getpid());
+ fclose(pid_file);
+ } else {
+ fprintf(stderr,
+ "failed to open PID file: <%s>: %s\n",
+ pid_path, strerror(errno));
+ }
+ }
+
+ if (i < argc) {
+ scrname = argv[i];
+ }
+ if (i < argc - 1) {
+ dbname = argv[i+1];
+ }
+ grntest_dbpath = dbname;
+
+ if (mode & MODE_LOCALONLY) {
+ grntest_localonly_mode = 1;
+ grntest_remote_mode = 1;
+ }
+
+ if (mode & MODE_OWNDB) {
+ grntest_localonly_mode = 1;
+ grntest_remote_mode = 1;
+ grntest_owndb_mode = 1;
+ }
+
+ if (mode & MODE_ONMEMORY) {
+ grntest_onmemory_mode= 1;
+ }
+
+ if (mode & MODE_FTP) {
+ grntest_ftp_mode = GRN_TRUE;
+ }
+
+ if ((scrname == NULL) || (dbname == NULL)) {
+ usage();
+ }
+
+ strcpy(grntest_serverhost, DEFAULT_DEST);
+ if (hoststr) {
+ grntest_remote_mode = 1;
+ strcpy(grntest_serverhost, hoststr);
+ }
+ grntest_serverport = DEFAULT_PORT;
+ if (portstr) {
+ grntest_serverport = grn_atoi(portstr, portstr + strlen(portstr), NULL);
+ }
+
+ if (outtype && !strcmp(outtype, "tsv")) {
+ grntest_outtype = OUT_TSV;
+ }
+
+ grn_init();
+ CRITICAL_SECTION_INIT(grntest_cs);
+
+ grn_ctx_init(&context, 0);
+ grn_ctx_init(&grntest_server_context, 0);
+ grn_db_create(&grntest_server_context, NULL, NULL);
+ grn_set_default_encoding(GRN_ENC_UTF8);
+
+ if (grntest_ftp_mode) {
+ sync_script(&context, scrname);
+ }
+ if (!check_script(&context, scrname)) {
+ exit_code = EXIT_FAILURE;
+ goto exit;
+ }
+
+ start_local(&context, dbname);
+ if (!grntest_remote_mode) {
+ start_server(dbname, 0);
+ }
+
+ if (!grntest_localonly_mode) {
+ if (check_server(&grntest_server_context)) {
+ goto exit;
+ }
+ }
+
+ get_scriptname(scrname, grntest_scriptname, ".scr");
+ get_username(grntest_username, 256);
+
+ GRN_TIME_INIT(&grntest_starttime, 0);
+ GRN_TIME_NOW(&context, &grntest_starttime);
+ sec = (time_t)(GRN_TIME_VALUE(&grntest_starttime)/1000000);
+ get_date(grntest_date, &sec);
+
+ if (!log_path) {
+ if (outdir) {
+ sprintf(log_path_buffer,
+ "%s/%s-%s-%" GRN_FMT_LLD "-%s.log", outdir, grntest_scriptname,
+ grntest_username,
+ GRN_TIME_VALUE(&grntest_starttime), grn_get_version());
+ } else {
+ sprintf(log_path_buffer,
+ "%s-%s-%" GRN_FMT_LLD "-%s.log", grntest_scriptname,
+ grntest_username,
+ GRN_TIME_VALUE(&grntest_starttime), grn_get_version());
+ }
+ log_path = log_path_buffer;
+ }
+
+ grntest_log_file = fopen(log_path, "w+b");
+ if (!grntest_log_file) {
+ fprintf(stderr, "Cannot open log file: <%s>\n", log_path);
+ goto exit;
+ }
+
+ get_sysinfo(dbname, sysinfo, BUF_LEN);
+ output_sysinfo(sysinfo);
+
+#ifndef WIN32
+ setsigint();
+#endif /* WIN32 */
+ qnum = do_script(&context, scrname);
+ output_result_final(&context, qnum);
+ fclose(grntest_log_file);
+
+ if (grntest_ftp_mode) {
+ ftp_sub(FTPUSER, FTPPASSWD, FTPSERVER, log_path, 3,
+ "report", NULL);
+ }
+ fprintf(stderr, "grntest done. logfile=%s\n", log_path);
+
+exit:
+ if (pid_path) {
+ remove(pid_path);
+ }
+
+ shutdown_server();
+#ifdef WIN32
+ if (!grntest_remote_mode) {
+ int ret;
+ ret = WaitForSingleObject(grntest_pi.hProcess, 20000);
+ if (ret == WAIT_TIMEOUT) {
+ fprintf(stderr, "timeout:groonga server cannot shutdown!!\n");
+ fprintf(stderr, "Cannot wait\n");
+ exit(1);
+ }
+ }
+#else
+ if (grntest_server_id) {
+ int ret, pstatus;
+ setsigalarm(20);
+ ret = waitpid(grntest_server_id, &pstatus, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Cannot wait\n");
+ exit(1);
+ }
+/*
+ else {
+ fprintf(stderr, "pstatus = %d\n", pstatus);
+ }
+*/
+ alarm(0);
+ }
+#endif /* WIN32 */
+ CRITICAL_SECTION_FIN(grntest_cs);
+ grn_obj_close(&context, &grntest_starttime);
+ grn_obj_close(&context, grntest_db);
+ grn_ctx_fin(&context);
+ grn_obj_close(&grntest_server_context, grn_ctx_db(&grntest_server_context));
+ grn_ctx_fin(&grntest_server_context);
+ grn_fin();
+ return exit_code;
+}
diff --git a/storage/mroonga/vendor/groonga/src/groonga_benchmark_sources.am b/storage/mroonga/vendor/groonga/src/groonga_benchmark_sources.am
new file mode 100644
index 00000000000..bf05fbff632
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/groonga_benchmark_sources.am
@@ -0,0 +1,2 @@
+groonga_benchmark_SOURCES = \
+ groonga_benchmark.c
diff --git a/storage/mroonga/vendor/groonga/src/groonga_sources.am b/storage/mroonga/vendor/groonga/src/groonga_sources.am
new file mode 100644
index 00000000000..308fdc3df96
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/groonga_sources.am
@@ -0,0 +1,2 @@
+groonga_SOURCES = \
+ groonga.c
diff --git a/storage/mroonga/vendor/groonga/src/httpd/Makefile.am b/storage/mroonga/vendor/groonga/src/httpd/Makefile.am
new file mode 100644
index 00000000000..88bcc03a33c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/httpd/Makefile.am
@@ -0,0 +1,32 @@
+NGINX_DIR = $(top_builddir)/vendor/nginx-$(NGINX_VERSION)
+
+EXTRA_DIST = \
+ nginx-module \
+ configure
+
+if WITH_GROONGA_HTTPD
+NGINX_MAKEILE = $(NGINX_DIR)/Makefile
+NGINX_MAKEFILE_DEPEND_FILES = \
+ configure \
+ Makefile.am \
+ $(top_builddir)/config.status \
+ $(srcdir)/nginx-module/config
+
+$(NGINX_MAKEILE): $(NGINX_MAKEFILE_DEPEND_FILES)
+ $(srcdir)/configure --srcdir="$(srcdir)" `../../config.status --config`
+
+# nginx's Makefile specify 'build' as the default rule.
+# This isn't compatible with the 'all' default rule generated by Automake
+# So, override the all rule.
+all-nginx: $(NGINX_MAKEILE)
+ (cd $(NGINX_DIR) && $(MAKE) build)
+all-local: all-nginx
+
+clean-nginx: $(NGINX_MAKEILE)
+ (cd $(NGINX_DIR) && $(MAKE) clean)
+clean-local: clean-nginx
+
+install-exec-nginx: $(NGINX_MAKEILE)
+ (cd $(NGINX_DIR) && $(MAKE) install)
+install-exec-local: install-exec-nginx
+endif
diff --git a/storage/mroonga/vendor/groonga/src/httpd/nginx-module/config b/storage/mroonga/vendor/groonga/src/httpd/nginx-module/config
new file mode 100644
index 00000000000..fc1fe16fad7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/httpd/nginx-module/config
@@ -0,0 +1,52 @@
+# -*- sh -*-
+
+groonga_strip_switch()
+{
+ # skip "-I" from "-I/usr/..."
+ tail -c +3
+}
+
+if [ "$GROONGA_HTTPD_IN_TREE" = yes ]; then
+ groonga_cflags="-I${GROONGA_HTTPD_IN_TREE_INCLUDE_PATH}"
+ groonga_cflags="${groonga_cflags} -DNGX_HTTP_GROONGA_LOG_PATH=\\\"\"${GROONGA_HTTPD_GROONGA_LOG_PATH}\"\\\""
+ groonga_cflags="${groonga_cflags} -DNGX_HTTP_GROONGA_QUERY_LOG_PATH=\\\"\"${GROONGA_HTTPD_GROONGA_QUERY_LOG_PATH}\"\\\""
+ groonga_libs="-L${GROONGA_HTTPD_IN_TREE_LINK_PATH} -lgroonga"
+ if [ -n "${GROONGA_HTTPD_RPATH}" ]; then
+ groonga_libs="$groonga_libs -Wl,-rpath -Wl,${GROONGA_HTTPD_RPATH}"
+ fi
+
+ ngx_addon_name=ngx_http_groonga_module
+ HTTP_MODULES="$HTTP_MODULES ngx_http_groonga_module"
+ NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_groonga_module.c"
+ CFLAGS="$CFLAGS $groonga_cflags"
+ CORE_LIBS="$CORE_LIBS $groonga_libs"
+
+ return 0
+fi
+
+groonga_cflags="$(pkg-config --cflags groonga)"
+groonga_feature_path="$(pkg-config --cflags-only-I groonga |
+ groonga_strip_switch)"
+groonga_libs="$(pkg-config --libs groonga)"
+
+ngx_feature="groonga"
+ngx_feature_name=
+ngx_feature_run=no
+ngx_feature_incs="#include <groonga.h>"
+ngx_feature_path="$groonga_feature_path"
+ngx_feature_libs="$groonga_libs"
+ngx_feature_test="grn_get_version()"
+. auto/feature
+
+if [ $ngx_found = yes ]; then
+ ngx_addon_name=ngx_http_groonga_module
+ HTTP_MODULES="$HTTP_MODULES ngx_http_groonga_module"
+ NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_groonga_module.c"
+ CFLAGS="$CFLAGS $groonga_cflags"
+ CORE_LIBS="$CORE_LIBS $groonga_libs"
+else
+ cat << END
+$0: error: the groonga module requires the groonga library.
+END
+ exit 1
+fi
diff --git a/storage/mroonga/vendor/groonga/src/httpd/nginx-module/ngx_http_groonga_module.c b/storage/mroonga/vendor/groonga/src/httpd/nginx-module/ngx_http_groonga_module.c
new file mode 100644
index 00000000000..26be6be7d0a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/httpd/nginx-module/ngx_http_groonga_module.c
@@ -0,0 +1,1440 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2012-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <groonga.h>
+
+#define GRN_NO_FLAGS 0
+
+typedef struct {
+ ngx_flag_t enabled;
+ ngx_str_t database_path;
+ char *database_path_cstr;
+ ngx_flag_t database_auto_create;
+ ngx_str_t base_path;
+ ngx_str_t log_path;
+ ngx_open_file_t *log_file;
+ grn_log_level log_level;
+ ngx_str_t query_log_path;
+ ngx_open_file_t *query_log_file;
+ size_t cache_limit;
+ char *config_file;
+ int config_line;
+ char *name;
+ grn_ctx context;
+ grn_cache *cache;
+} ngx_http_groonga_loc_conf_t;
+
+typedef struct {
+ ngx_log_t *log;
+ ngx_pool_t *pool;
+ ngx_int_t rc;
+} ngx_http_groonga_database_callback_data_t;
+
+typedef struct {
+ grn_bool initialized;
+ grn_ctx context;
+ grn_obj head;
+ grn_obj body;
+ grn_obj foot;
+} ngx_http_groonga_handler_data_t;
+
+typedef struct {
+ ngx_pool_t *pool;
+ ngx_open_file_t *file;
+} ngx_http_groonga_logger_data_t;
+
+typedef struct {
+ ngx_pool_t *pool;
+ ngx_open_file_t *file;
+ ngx_str_t *path;
+} ngx_http_groonga_query_logger_data_t;
+
+typedef void (*ngx_http_groonga_loc_conf_callback_pt)(ngx_http_groonga_loc_conf_t *conf, void *user_data);
+
+ngx_module_t ngx_http_groonga_module;
+
+static char *
+ngx_str_null_terminate(ngx_pool_t *pool, const ngx_str_t *string)
+{
+ char *null_terminated_c_string;
+
+ null_terminated_c_string = ngx_pnalloc(pool, string->len + 1);
+ if (!null_terminated_c_string) {
+ return NULL;
+ }
+
+ memcpy(null_terminated_c_string, string->data, string->len);
+ null_terminated_c_string[string->len] = '\0';
+
+ return null_terminated_c_string;
+}
+
+static grn_bool
+ngx_str_equal_c_string(ngx_str_t *string, const char *c_string)
+{
+ if (string->len != strlen(c_string)) {
+ return GRN_FALSE;
+ }
+
+ return memcmp(c_string, string->data, string->len) == 0;
+}
+
+static grn_bool
+ngx_str_is_custom_path(ngx_str_t *string)
+{
+ if (string->len == 0) {
+ return GRN_FALSE;
+ }
+
+ if (strncmp((const char *)(string->data), "off", string->len) == 0) {
+ return GRN_FALSE;
+ }
+
+ return GRN_TRUE;
+}
+
+static void
+ngx_http_groonga_logger_log(grn_ctx *ctx, grn_log_level level,
+ const char *timestamp, const char *title,
+ const char *message, const char *location,
+ void *user_data)
+{
+ ngx_http_groonga_logger_data_t *logger_data = user_data;
+ const char level_marks[] = " EACewnid-";
+ u_char buffer[NGX_MAX_ERROR_STR];
+ u_char *last;
+
+ if (location && *location) {
+ last = ngx_slprintf(buffer, buffer + NGX_MAX_ERROR_STR,
+ "%s|%c|%s %s %s\n",
+ timestamp, *(level_marks + level), title, message,
+ location);
+ } else {
+ last = ngx_slprintf(buffer, buffer + NGX_MAX_ERROR_STR,
+ "%s|%c|%s %s\n",
+ timestamp, *(level_marks + level), title, message);
+ }
+ ngx_write_fd(logger_data->file->fd, buffer, last - buffer);
+}
+
+static void
+ngx_http_groonga_logger_reopen(grn_ctx *ctx, void *user_data)
+{
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "log will be closed.");
+ ngx_reopen_files((ngx_cycle_t *)ngx_cycle, -1);
+ GRN_LOG(ctx, GRN_LOG_NOTICE, "log opened.");
+}
+
+static void
+ngx_http_groonga_logger_fin(grn_ctx *ctx, void *user_data)
+{
+ ngx_http_groonga_logger_data_t *logger_data = user_data;
+
+ ngx_pfree(logger_data->pool, logger_data);
+}
+
+static grn_logger ngx_http_groonga_logger = {
+ GRN_LOG_DEFAULT_LEVEL,
+ GRN_LOG_TIME | GRN_LOG_MESSAGE,
+ NULL,
+ ngx_http_groonga_logger_log,
+ ngx_http_groonga_logger_reopen,
+ ngx_http_groonga_logger_fin
+};
+
+static ngx_int_t
+ngx_http_groonga_context_init_logger(grn_ctx *context,
+ ngx_http_groonga_loc_conf_t *location_conf,
+ ngx_pool_t *pool,
+ ngx_log_t *log)
+{
+ ngx_http_groonga_logger_data_t *logger_data;
+
+ if (!location_conf->log_file) {
+ return NGX_OK;
+ }
+
+ logger_data = ngx_pcalloc(pool, sizeof(ngx_http_groonga_logger_data_t));
+ if (!logger_data) {
+ ngx_log_error(NGX_LOG_ERR, log, 0,
+ "http_groonga: failed to allocate memory for logger");
+ return NGX_ERROR;
+ }
+
+ logger_data->pool = pool;
+ logger_data->file = location_conf->log_file;
+ ngx_http_groonga_logger.max_level = location_conf->log_level;
+ ngx_http_groonga_logger.user_data = logger_data;
+ grn_logger_set(context, &ngx_http_groonga_logger);
+
+ return NGX_OK;
+}
+
+static void
+ngx_http_groonga_query_logger_log(grn_ctx *ctx, unsigned int flag,
+ const char *timestamp, const char *info,
+ const char *message, void *user_data)
+{
+ ngx_http_groonga_query_logger_data_t *data = user_data;
+ u_char buffer[NGX_MAX_ERROR_STR];
+ u_char *last;
+
+ last = ngx_slprintf(buffer, buffer + NGX_MAX_ERROR_STR,
+ "%s|%s%s\n",
+ timestamp, info, message);
+ ngx_write_fd(data->file->fd, buffer, last - buffer);
+}
+
+static void
+ngx_http_groonga_query_logger_reopen(grn_ctx *ctx, void *user_data)
+{
+ ngx_http_groonga_query_logger_data_t *data = user_data;
+
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_DESTINATION, " ",
+ "query log will be closed: <%.*s>",
+ (int)(data->path->len), data->path->data);
+ ngx_reopen_files((ngx_cycle_t *)ngx_cycle, -1);
+ GRN_QUERY_LOG(ctx, GRN_QUERY_LOG_DESTINATION, " ",
+ "query log is opened: <%.*s>",
+ (int)(data->path->len), data->path->data);
+}
+
+static void
+ngx_http_groonga_query_logger_fin(grn_ctx *ctx, void *user_data)
+{
+ ngx_http_groonga_query_logger_data_t *data = user_data;
+
+ ngx_pfree(data->pool, data);
+}
+
+static grn_query_logger ngx_http_groonga_query_logger = {
+ GRN_QUERY_LOG_DEFAULT,
+ NULL,
+ ngx_http_groonga_query_logger_log,
+ ngx_http_groonga_query_logger_reopen,
+ ngx_http_groonga_query_logger_fin
+};
+
+static ngx_int_t
+ngx_http_groonga_context_init_query_logger(grn_ctx *context,
+ ngx_http_groonga_loc_conf_t *location_conf,
+ ngx_pool_t *pool,
+ ngx_log_t *log)
+{
+ ngx_http_groonga_query_logger_data_t *query_logger_data;
+
+ if (!location_conf->query_log_file) {
+ return NGX_OK;
+ }
+
+ query_logger_data = ngx_pcalloc(pool,
+ sizeof(ngx_http_groonga_query_logger_data_t));
+ if (!query_logger_data) {
+ ngx_log_error(NGX_LOG_ERR, log, 0,
+ "http_groonga: failed to allocate memory for query logger");
+ return NGX_ERROR;
+ }
+
+ query_logger_data->pool = pool;
+ query_logger_data->file = location_conf->query_log_file;
+ query_logger_data->path = &(location_conf->query_log_path);
+ ngx_http_groonga_query_logger.user_data = query_logger_data;
+ grn_query_logger_set(context, &ngx_http_groonga_query_logger);
+
+ return NGX_OK;
+}
+
+static ngx_int_t
+ngx_http_groonga_context_init(grn_ctx *context,
+ ngx_http_groonga_loc_conf_t *location_conf,
+ ngx_pool_t *pool,
+ ngx_log_t *log)
+{
+ ngx_int_t status;
+
+ grn_ctx_init(context, GRN_NO_FLAGS);
+
+ status = ngx_http_groonga_context_init_logger(context,
+ location_conf,
+ pool,
+ log);
+ if (status == NGX_ERROR) {
+ grn_ctx_fin(context);
+ return status;
+ }
+
+ status = ngx_http_groonga_context_init_query_logger(context,
+ location_conf,
+ pool,
+ log);
+ if (status == NGX_ERROR) {
+ grn_ctx_fin(context);
+ return status;
+ }
+
+ if (location_conf->cache) {
+ grn_cache_current_set(context, location_conf->cache);
+ }
+
+ return status;
+}
+
+static void
+ngx_http_groonga_context_log_error(ngx_log_t *log, grn_ctx *context)
+{
+ if (context->rc == GRN_SUCCESS) {
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, log, 0, "%s", context->errbuf);
+}
+
+static ngx_int_t
+ngx_http_groonga_context_check_error(ngx_log_t *log, grn_ctx *context)
+{
+ if (context->rc == GRN_SUCCESS) {
+ return NGX_OK;
+ } else {
+ ngx_http_groonga_context_log_error(log, context);
+ return NGX_HTTP_BAD_REQUEST;
+ }
+}
+
+static ngx_buf_t *
+ngx_http_groonga_grn_obj_to_ngx_buf(ngx_pool_t *pool, grn_obj *object)
+{
+ ngx_buf_t *buffer;
+ buffer = ngx_pcalloc(pool, sizeof(ngx_buf_t));
+ if (buffer == NULL) {
+ return NULL;
+ }
+
+ /* adjust the pointers of the buffer */
+ buffer->pos = (u_char *)GRN_TEXT_VALUE(object);
+ buffer->last = (u_char *)GRN_TEXT_VALUE(object) + GRN_TEXT_LEN(object);
+ buffer->memory = 1; /* this buffer is in memory */
+
+ return buffer;
+}
+
+static void
+ngx_http_groonga_handler_cleanup(void *user_data)
+{
+ ngx_http_groonga_handler_data_t *data = user_data;
+ grn_ctx *context;
+
+ if (!data->initialized) {
+ return;
+ }
+
+ context = &(data->context);
+ GRN_OBJ_FIN(context, &(data->head));
+ GRN_OBJ_FIN(context, &(data->body));
+ GRN_OBJ_FIN(context, &(data->foot));
+ grn_logger_set(context, NULL);
+ grn_query_logger_set(context, NULL);
+ grn_ctx_fin(context);
+}
+
+static void
+ngx_http_groonga_context_receive_handler(grn_ctx *context,
+ int flags,
+ void *callback_data)
+{
+ ngx_http_groonga_handler_data_t *data = callback_data;
+ char *result = NULL;
+ unsigned int result_size = 0;
+ int recv_flags;
+
+ if (!(flags & GRN_CTX_TAIL)) {
+ return;
+ }
+
+ grn_ctx_recv(context, &result, &result_size, &recv_flags);
+
+ if (recv_flags == GRN_CTX_QUIT) {
+ ngx_int_t ngx_rc;
+ ngx_int_t ngx_pid;
+
+ if (ngx_process == NGX_PROCESS_SINGLE) {
+ ngx_pid = getpid();
+ } else {
+ ngx_pid = getppid();
+ }
+
+ ngx_rc = ngx_os_signal_process((ngx_cycle_t*)ngx_cycle,
+ "stop",
+ ngx_pid);
+ if (ngx_rc == NGX_OK) {
+ context->stat &= ~GRN_CTX_QUIT;
+ grn_ctx_recv(context, &result, &result_size, &recv_flags);
+ context->stat |= GRN_CTX_QUIT;
+ } else {
+ context->rc = GRN_OPERATION_NOT_PERMITTED;
+ GRN_TEXT_PUTS(context, &(data->body), "false");
+ context->stat &= ~GRN_CTX_QUIT;
+ }
+ }
+
+ if (result_size > 0 ||
+ GRN_TEXT_LEN(&(data->body)) > 0 ||
+ context->rc != GRN_SUCCESS) {
+ if (result_size > 0) {
+ GRN_TEXT_PUT(context, &(data->body), result, result_size);
+ }
+
+ grn_output_envelope(context,
+ context->rc,
+ &(data->head),
+ &(data->body),
+ &(data->foot),
+ NULL,
+ 0);
+ }
+}
+
+static ngx_int_t
+ngx_http_groonga_extract_command_path(ngx_http_request_t *r,
+ ngx_str_t *command_path)
+{
+ size_t base_path_length;
+
+ ngx_http_core_loc_conf_t *http_location_conf;
+ ngx_http_groonga_loc_conf_t *location_conf;
+
+ http_location_conf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+ location_conf = ngx_http_get_module_loc_conf(r, ngx_http_groonga_module);
+
+ command_path->data = r->unparsed_uri.data;
+ command_path->len = r->unparsed_uri.len;
+ base_path_length = http_location_conf->name.len;
+ if (location_conf->base_path.len > 0) {
+ if (command_path->len < location_conf->base_path.len) {
+ ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+ "requested URI is shorter than groonga_base_path: "
+ "URI: <%V>, groonga_base_path: <%V>",
+ &(r->unparsed_uri), &(location_conf->base_path));
+ } else if (strncmp((const char *)command_path->data,
+ (const char *)(location_conf->base_path.data),
+ location_conf->base_path.len) < 0) {
+ ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+ "groonga_base_path doesn't match requested URI: "
+ "URI: <%V>, groonga_base_path: <%V>",
+ &(r->unparsed_uri), &(location_conf->base_path));
+ } else {
+ base_path_length = location_conf->base_path.len;
+ }
+ }
+ command_path->data += base_path_length;
+ command_path->len -= base_path_length;
+ if (command_path->len > 0 && command_path->data[0] == '/') {
+ command_path->data += 1;
+ command_path->len -= 1;
+ }
+ if (command_path->len == 0) {
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ return NGX_OK;
+}
+
+static void
+ngx_http_groonga_handler_set_content_type(ngx_http_request_t *r,
+ const char *content_type)
+{
+ r->headers_out.content_type.len = strlen(content_type);
+ r->headers_out.content_type.data = (u_char *)content_type;
+ r->headers_out.content_type_len = r->headers_out.content_type.len;
+}
+
+static ngx_int_t
+ngx_http_groonga_handler_create_data(ngx_http_request_t *r,
+ ngx_http_groonga_handler_data_t **data_return)
+{
+ ngx_int_t rc;
+
+ ngx_http_groonga_loc_conf_t *location_conf;
+
+ ngx_http_cleanup_t *cleanup;
+ ngx_http_groonga_handler_data_t *data;
+
+ grn_ctx *context;
+
+ location_conf = ngx_http_get_module_loc_conf(r, ngx_http_groonga_module);
+
+ cleanup = ngx_http_cleanup_add(r, sizeof(ngx_http_groonga_handler_data_t));
+ cleanup->handler = ngx_http_groonga_handler_cleanup;
+ data = cleanup->data;
+ *data_return = data;
+
+ context = &(data->context);
+ rc = ngx_http_groonga_context_init(context, location_conf,
+ r->pool, r->connection->log);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+ data->initialized = GRN_TRUE;
+ GRN_TEXT_INIT(&(data->head), GRN_NO_FLAGS);
+ GRN_TEXT_INIT(&(data->body), GRN_NO_FLAGS);
+ GRN_TEXT_INIT(&(data->foot), GRN_NO_FLAGS);
+ grn_ctx_use(context, grn_ctx_db(&(location_conf->context)));
+ rc = ngx_http_groonga_context_check_error(r->connection->log, context);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ grn_ctx_recv_handler_set(context,
+ ngx_http_groonga_context_receive_handler,
+ data);
+
+ return NGX_OK;
+}
+
+static ngx_int_t
+ngx_http_groonga_handler_process_command_path(ngx_http_request_t *r,
+ ngx_str_t *command_path,
+ ngx_http_groonga_handler_data_t *data)
+{
+ grn_ctx *context;
+ grn_obj uri;
+
+ context = &(data->context);
+ GRN_TEXT_INIT(&uri, 0);
+ GRN_TEXT_PUTS(context, &uri, "/d/");
+ GRN_TEXT_PUT(context, &uri, command_path->data, command_path->len);
+ grn_ctx_send(context, GRN_TEXT_VALUE(&uri), GRN_TEXT_LEN(&uri),
+ GRN_NO_FLAGS);
+ ngx_http_groonga_context_log_error(r->connection->log, context);
+ GRN_OBJ_FIN(context, &uri);
+
+ return NGX_OK;
+}
+
+static ngx_int_t
+ngx_http_groonga_handler_validate_post_command(ngx_http_request_t *r,
+ ngx_str_t *command_path,
+ ngx_http_groonga_handler_data_t *data)
+{
+ grn_ctx *context;
+ ngx_str_t command;
+
+ command.data = command_path->data;
+ if (r->args.len == 0) {
+ command.len = command_path->len;
+ } else {
+ command.len = command_path->len - r->args.len - strlen("?");
+ }
+ if (ngx_str_equal_c_string(&command, "load")) {
+ return NGX_OK;
+ }
+
+ context = &(data->context);
+ ngx_http_groonga_handler_set_content_type(r, "text/plain");
+ GRN_TEXT_PUTS(context, &(data->body), "command for POST must be <load>: <");
+ GRN_TEXT_PUT(context, &(data->body), command.data, command.len);
+ GRN_TEXT_PUTS(context, &(data->body), ">");
+
+ return NGX_HTTP_BAD_REQUEST;
+}
+
+static ngx_int_t
+ngx_http_groonga_send_lines(grn_ctx *context,
+ ngx_http_request_t *r,
+ u_char *current,
+ u_char *last)
+{
+ ngx_int_t rc;
+
+ u_char *line_start;
+
+ for (line_start = current; current < last; current++) {
+ if (*current != '\n') {
+ continue;
+ }
+
+ grn_ctx_send(context, (const char *)line_start, current - line_start,
+ GRN_NO_FLAGS);
+ rc = ngx_http_groonga_context_check_error(r->connection->log, context);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+ line_start = current + 1;
+ }
+ if (line_start < current) {
+ grn_ctx_send(context, (const char *)line_start, current - line_start,
+ GRN_NO_FLAGS);
+ rc = ngx_http_groonga_context_check_error(r->connection->log, context);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+ }
+
+ return NGX_OK;
+}
+
+static ngx_int_t
+ngx_http_groonga_join_request_body_chain(ngx_http_request_t *r,
+ ngx_chain_t *chain,
+ u_char **out_start,
+ u_char **out_end)
+{
+ ngx_int_t rc;
+
+ ngx_log_t *log = r->connection->log;
+
+ ngx_chain_t *current;
+ u_char *out;
+ size_t out_size;
+
+ u_char *out_cursor;
+ ngx_buf_t *buffer;
+ size_t buffer_size;
+
+ out_size = 0;
+ for (current = chain; current; current = current->next) {
+ out_size += ngx_buf_size(current->buf);
+ }
+ out = ngx_palloc(r->pool, out_size);
+ if (!out) {
+ ngx_log_error(NGX_LOG_ERR, log, 0,
+ "http_groonga: failed to allocate memory for request body");
+ return NGX_ERROR;
+ }
+
+ out_cursor = out;
+ for (current = chain; current; current = current->next) {
+ buffer = current->buf;
+ buffer_size = ngx_buf_size(current->buf);
+
+ if (buffer->file) {
+ rc = ngx_read_file(buffer->file, out_cursor, buffer_size, 0);
+ if (rc < 0) {
+ ngx_log_error(NGX_LOG_ERR, log, 0,
+ "http_groonga: failed to read a request body stored in a file");
+ return rc;
+ }
+ } else {
+ ngx_memcpy(out_cursor, buffer->pos, buffer_size);
+ }
+ out_cursor += buffer_size;
+ }
+
+ *out_start = out;
+ *out_end = out + out_size;
+
+ return NGX_OK;
+}
+
+static ngx_int_t
+ngx_http_groonga_handler_process_body(ngx_http_request_t *r,
+ ngx_http_groonga_handler_data_t *data)
+{
+ ngx_int_t rc;
+
+ grn_ctx *context;
+
+ ngx_buf_t *body;
+ u_char *body_data;
+ u_char *body_data_end;
+
+ context = &(data->context);
+
+ body = r->request_body->bufs->buf;
+ if (!body) {
+ ngx_http_groonga_handler_set_content_type(r, "text/plain");
+ GRN_TEXT_PUTS(context, &(data->body), "must send load data as body");
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ rc = ngx_http_groonga_join_request_body_chain(r,
+ r->request_body->bufs,
+ &body_data,
+ &body_data_end);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ rc = ngx_http_groonga_send_lines(context, r, body_data, body_data_end);
+ ngx_pfree(r->pool, body_data);
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_groonga_handler_process_load(ngx_http_request_t *r,
+ ngx_str_t *command_path,
+ ngx_http_groonga_handler_data_t *data)
+{
+ ngx_int_t rc;
+
+ rc = ngx_http_groonga_handler_validate_post_command(r, command_path, data);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ rc = ngx_http_groonga_handler_process_command_path(r, command_path, data);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ rc = ngx_http_groonga_handler_process_body(r, data);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ return NGX_OK;
+}
+
+static ngx_chain_t *
+ngx_http_groonga_attach_chain(ngx_chain_t *chain, ngx_chain_t *new_chain)
+{
+ ngx_chain_t *last_chain;
+
+ if (new_chain->buf->last == new_chain->buf->pos) {
+ return chain;
+ }
+
+ new_chain->buf->last_buf = 1;
+ new_chain->next = NULL;
+ if (!chain) {
+ return new_chain;
+ }
+
+ chain->buf->last_buf = 0;
+ last_chain = chain;
+ while (last_chain->next) {
+ last_chain = last_chain->next;
+ }
+ last_chain->next = new_chain;
+ return chain;
+}
+
+static ngx_int_t
+ngx_http_groonga_handler_send_response(ngx_http_request_t *r,
+ ngx_http_groonga_handler_data_t *data)
+{
+ ngx_int_t rc;
+ grn_ctx *context;
+ const char *content_type;
+ ngx_buf_t *head_buf, *body_buf, *foot_buf;
+ ngx_chain_t head_chain, body_chain, foot_chain;
+ ngx_chain_t *output_chain = NULL;
+
+ context = &(data->context);
+
+ /* set the 'Content-type' header */
+ if (r->headers_out.content_type.len == 0) {
+ content_type = grn_ctx_get_mime_type(context);
+ ngx_http_groonga_handler_set_content_type(r, content_type);
+ }
+
+ /* allocate buffers for a response body */
+ head_buf = ngx_http_groonga_grn_obj_to_ngx_buf(r->pool, &(data->head));
+ if (!head_buf) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ body_buf = ngx_http_groonga_grn_obj_to_ngx_buf(r->pool, &(data->body));
+ if (!body_buf) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ foot_buf = ngx_http_groonga_grn_obj_to_ngx_buf(r->pool, &(data->foot));
+ if (!foot_buf) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* attach buffers to the buffer chain */
+ head_chain.buf = head_buf;
+ output_chain = ngx_http_groonga_attach_chain(output_chain, &head_chain);
+ body_chain.buf = body_buf;
+ output_chain = ngx_http_groonga_attach_chain(output_chain, &body_chain);
+ foot_chain.buf = foot_buf;
+ output_chain = ngx_http_groonga_attach_chain(output_chain, &foot_chain);
+
+ /* set the status line */
+ r->headers_out.status = NGX_HTTP_OK;
+ r->headers_out.content_length_n = GRN_TEXT_LEN(&(data->head)) +
+ GRN_TEXT_LEN(&(data->body)) +
+ GRN_TEXT_LEN(&(data->foot));
+ if (r->headers_out.content_length_n == 0) {
+ r->header_only = 1;
+ }
+
+ /* send the headers of your response */
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ return rc;
+ }
+
+ /* send the buffer chain of your response */
+ rc = ngx_http_output_filter(r, output_chain);
+
+ return rc;
+}
+
+static ngx_int_t
+ngx_http_groonga_handler_get(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_str_t command_path;
+ ngx_http_groonga_handler_data_t *data;
+
+ rc = ngx_http_groonga_extract_command_path(r, &command_path);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ rc = ngx_http_groonga_handler_create_data(r, &data);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ rc = ngx_http_groonga_handler_process_command_path(r, &command_path, data);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ /* discard request body, since we don't need it here */
+ rc = ngx_http_discard_request_body(r);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ rc = ngx_http_groonga_handler_send_response(r, data);
+
+ return rc;
+}
+
+static void
+ngx_http_groonga_handler_post_send_error_response(ngx_http_request_t *r,
+ ngx_int_t rc)
+{
+ r->headers_out.status = rc;
+ r->headers_out.content_length_n = 0;
+ r->header_only = 1;
+ rc = ngx_http_send_header(r);
+ ngx_http_finalize_request(r, rc);
+}
+
+static void
+ngx_http_groonga_handler_post(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_str_t command_path;
+ ngx_http_groonga_handler_data_t *data = NULL;
+
+ rc = ngx_http_groonga_extract_command_path(r, &command_path);
+ if (rc != NGX_OK) {
+ ngx_http_groonga_handler_post_send_error_response(r, rc);
+ return;
+ }
+
+ rc = ngx_http_groonga_handler_create_data(r, &data);
+ if (rc != NGX_OK) {
+ ngx_http_groonga_handler_post_send_error_response(r, rc);
+ return;
+ }
+
+ rc = ngx_http_groonga_handler_process_load(r, &command_path, data);
+ if (rc != NGX_OK) {
+ ngx_http_groonga_handler_post_send_error_response(r, rc);
+ return;
+ }
+
+ ngx_http_groonga_handler_send_response(r, data);
+ ngx_http_finalize_request(r, rc);
+}
+
+static ngx_int_t
+ngx_http_groonga_handler(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+
+ switch (r->method) {
+ case NGX_HTTP_GET:
+ case NGX_HTTP_HEAD:
+ rc = ngx_http_groonga_handler_get(r);
+ break;
+ case NGX_HTTP_POST:
+ rc = ngx_http_read_client_request_body(r, ngx_http_groonga_handler_post);
+ if (rc < NGX_HTTP_SPECIAL_RESPONSE) {
+ rc = NGX_DONE;
+ }
+ break;
+ default:
+ rc = NGX_HTTP_NOT_ALLOWED;
+ break;
+ }
+
+ return rc;
+}
+
+static char *
+ngx_http_groonga_conf_set_groonga_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *status;
+ ngx_http_core_loc_conf_t *location_conf;
+ ngx_http_groonga_loc_conf_t *groonga_location_conf = conf;
+
+ status = ngx_conf_set_flag_slot(cf, cmd, conf);
+ if (status != NGX_CONF_OK) {
+ return status;
+ }
+
+ location_conf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+ if (groonga_location_conf->enabled) {
+ location_conf->handler = ngx_http_groonga_handler;
+ groonga_location_conf->name =
+ ngx_str_null_terminate(cf->pool, &(location_conf->name));
+ groonga_location_conf->config_file =
+ ngx_str_null_terminate(cf->pool, &(cf->conf_file->file.name));
+ groonga_location_conf->config_line = cf->conf_file->line;
+ } else {
+ location_conf->handler = NULL;
+ }
+
+ return NGX_CONF_OK;
+}
+
+static char *
+ngx_http_groonga_conf_set_log_path_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *status;
+ ngx_http_groonga_loc_conf_t *groonga_location_conf = conf;
+
+ status = ngx_conf_set_str_slot(cf, cmd, conf);
+ if (status != NGX_CONF_OK) {
+ return status;
+ }
+
+ if (!groonga_location_conf->log_path.data) {
+ return NGX_CONF_OK;
+ }
+
+ if (!ngx_str_is_custom_path(&(groonga_location_conf->log_path))) {
+ return NGX_CONF_OK;
+ }
+
+ groonga_location_conf->log_file =
+ ngx_conf_open_file(cf->cycle, &(groonga_location_conf->log_path));
+ if (!groonga_location_conf->log_file) {
+ ngx_log_error(NGX_LOG_ERR, cf->cycle->log, 0,
+ "http_groonga: failed to open groonga log file: <%V>",
+ &(groonga_location_conf->log_path));
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+static char *
+ngx_http_groonga_conf_set_log_level_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *status = NGX_CONF_OK;
+ ngx_http_groonga_loc_conf_t *groonga_location_conf = conf;
+ char *value;
+
+ value = ngx_str_null_terminate(cf->cycle->pool,
+ ((ngx_str_t *)cf->args->elts) + 1);
+ if (strcasecmp(value, "none") == 0) {
+ groonga_location_conf->log_level = GRN_LOG_NONE;
+ } else if (strcasecmp(value, "emergency") == 0) {
+ groonga_location_conf->log_level = GRN_LOG_EMERG;
+ } else if (strcasecmp(value, "alert") == 0) {
+ groonga_location_conf->log_level = GRN_LOG_ALERT;
+ } else if (strcasecmp(value, "critical") == 0) {
+ groonga_location_conf->log_level = GRN_LOG_CRIT;
+ } else if (strcasecmp(value, "error") == 0) {
+ groonga_location_conf->log_level = GRN_LOG_ERROR;
+ } else if (strcasecmp(value, "warning") == 0) {
+ groonga_location_conf->log_level = GRN_LOG_WARNING;
+ } else if (strcasecmp(value, "notice") == 0) {
+ groonga_location_conf->log_level = GRN_LOG_NOTICE;
+ } else if (strcasecmp(value, "info") == 0) {
+ groonga_location_conf->log_level = GRN_LOG_INFO;
+ } else if (strcasecmp(value, "debug") == 0) {
+ groonga_location_conf->log_level = GRN_LOG_DEBUG;
+ } else if (strcasecmp(value, "dump") == 0) {
+ groonga_location_conf->log_level = GRN_LOG_DUMP;
+ } else {
+ status = "must be one of 'none', 'emergency', 'alert', "
+ "'ciritical', 'error', 'warning', 'notice', 'info', 'debug' and 'dump'";
+ }
+ ngx_pfree(cf->cycle->pool, value);
+
+ return status;
+}
+
+static char *
+ngx_http_groonga_conf_set_query_log_path_slot(ngx_conf_t *cf,
+ ngx_command_t *cmd,
+ void *conf)
+{
+ char *status;
+ ngx_http_groonga_loc_conf_t *groonga_location_conf = conf;
+
+ status = ngx_conf_set_str_slot(cf, cmd, conf);
+ if (status != NGX_CONF_OK) {
+ return status;
+ }
+
+ if (!groonga_location_conf->query_log_path.data) {
+ return NGX_CONF_OK;
+ }
+
+ if (!ngx_str_is_custom_path(&(groonga_location_conf->query_log_path))) {
+ return NGX_CONF_OK;
+ }
+
+ groonga_location_conf->query_log_file =
+ ngx_conf_open_file(cf->cycle, &(groonga_location_conf->query_log_path));
+ if (!groonga_location_conf->query_log_file) {
+ ngx_log_error(NGX_LOG_ERR, cf->cycle->log, 0,
+ "http_groonga: failed to open groonga query log file: <%V>",
+ &(groonga_location_conf->query_log_path));
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+static void *
+ngx_http_groonga_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_groonga_loc_conf_t *conf;
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_groonga_loc_conf_t));
+ if (conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->enabled = NGX_CONF_UNSET;
+ conf->database_path.data = NULL;
+ conf->database_path.len = 0;
+ conf->database_path_cstr = NULL;
+ conf->database_auto_create = NGX_CONF_UNSET;
+ conf->base_path.data = NULL;
+ conf->base_path.len = 0;
+ conf->log_path.data = NULL;
+ conf->log_path.len = 0;
+ conf->log_file = NULL;
+ conf->log_level = GRN_LOG_DEFAULT_LEVEL;
+ conf->query_log_path.data = NULL;
+ conf->query_log_path.len = 0;
+ conf->query_log_file = NULL;
+ conf->cache_limit = NGX_CONF_UNSET_SIZE;
+ conf->config_file = NULL;
+ conf->config_line = 0;
+ conf->cache = NULL;
+
+ return conf;
+}
+
+static char *
+ngx_http_groonga_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_groonga_loc_conf_t *prev = parent;
+ ngx_http_groonga_loc_conf_t *conf = child;
+
+ ngx_conf_merge_str_value(conf->database_path, prev->database_path, NULL);
+ ngx_conf_merge_value(conf->database_auto_create,
+ prev->database_auto_create,
+ GRN_TRUE);
+ ngx_conf_merge_size_value(conf->cache_limit, prev->cache_limit,
+ GRN_CACHE_DEFAULT_MAX_N_ENTRIES);
+
+#ifdef NGX_HTTP_GROONGA_LOG_PATH
+ {
+ ngx_str_t default_log_path;
+ default_log_path.data = (u_char *)NGX_HTTP_GROONGA_LOG_PATH;
+ default_log_path.len = strlen(NGX_HTTP_GROONGA_LOG_PATH);
+ conf->log_file = ngx_conf_open_file(cf->cycle, &default_log_path);
+ if (!conf->log_file) {
+ ngx_log_error(NGX_LOG_ERR, cf->cycle->log, 0,
+ "http_groonga: "
+ "failed to open the default groonga log file: <%V>",
+ &default_log_path);
+ return NGX_CONF_ERROR;
+ }
+ }
+#else
+ conf->log_file = NULL;
+#endif
+
+ ngx_conf_merge_str_value(conf->query_log_path, prev->query_log_path,
+ NGX_HTTP_GROONGA_QUERY_LOG_PATH);
+ if (!conf->query_log_file &&
+ ngx_str_is_custom_path(&(conf->query_log_path)) &&
+ conf->enabled) {
+ conf->query_log_file = ngx_conf_open_file(cf->cycle,
+ &(conf->query_log_path));
+ if (!conf->query_log_file) {
+ ngx_log_error(NGX_LOG_ERR, cf->cycle->log, 0,
+ "http_groonga: "
+ "failed to open the default groonga query log file: <%V>",
+ &(conf->query_log_path));
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+static void
+ngx_http_groonga_each_loc_conf_in_tree(ngx_http_location_tree_node_t *node,
+ ngx_http_groonga_loc_conf_callback_pt callback,
+ void *user_data)
+{
+ if (!node) {
+ return;
+ }
+
+ if (node->exact && node->exact->handler == ngx_http_groonga_handler) {
+ callback(node->exact->loc_conf[ngx_http_groonga_module.ctx_index],
+ user_data);
+ }
+
+ if (node->inclusive && node->inclusive->handler == ngx_http_groonga_handler) {
+ callback(node->inclusive->loc_conf[ngx_http_groonga_module.ctx_index],
+ user_data);
+ }
+
+ ngx_http_groonga_each_loc_conf_in_tree(node->left, callback, user_data);
+ ngx_http_groonga_each_loc_conf_in_tree(node->right, callback, user_data);
+ ngx_http_groonga_each_loc_conf_in_tree(node->tree, callback, user_data);
+}
+
+static void
+ngx_http_groonga_each_loc_conf(ngx_http_conf_ctx_t *http_conf,
+ ngx_http_groonga_loc_conf_callback_pt callback,
+ void *user_data)
+{
+ ngx_http_core_main_conf_t *main_conf;
+ ngx_http_core_srv_conf_t **server_confs;
+ ngx_uint_t i;
+
+ if (!http_conf) {
+ return;
+ }
+
+ main_conf = http_conf->main_conf[ngx_http_core_module.ctx_index];
+ server_confs = main_conf->servers.elts;
+ for (i = 0; i < main_conf->servers.nelts; i++) {
+ ngx_http_core_srv_conf_t *server_conf;
+ ngx_http_core_loc_conf_t *location_conf;
+
+ server_conf = server_confs[i];
+ location_conf = server_conf->ctx->loc_conf[ngx_http_core_module.ctx_index];
+ ngx_http_groonga_each_loc_conf_in_tree(location_conf->static_locations,
+ callback,
+ user_data);
+ }
+}
+
+static ngx_int_t
+ngx_http_groonga_mkdir_p(ngx_log_t *log, const char *dir_name)
+{
+ char sub_path[PATH_MAX];
+ size_t i, dir_name_length;
+
+ dir_name_length = strlen(dir_name);
+ sub_path[0] = dir_name[0];
+ for (i = 1; i < dir_name_length + 1; i++) {
+ if (dir_name[i] == '/' || dir_name[i] == '\0') {
+ struct stat stat_buffer;
+ sub_path[i] = '\0';
+ if (stat(sub_path, &stat_buffer) == -1) {
+ if (ngx_create_dir(sub_path, 0700) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, 0,
+ "failed to create directory: %s (%s): %s",
+ sub_path, dir_name,
+ strerror(errno));
+ return NGX_ERROR;
+ }
+ }
+ }
+ sub_path[i] = dir_name[i];
+ }
+
+ return NGX_OK;
+}
+
+static void
+ngx_http_groonga_create_database(ngx_http_groonga_loc_conf_t *location_conf,
+ ngx_http_groonga_database_callback_data_t *data)
+{
+ const char *database_base_name;
+ grn_ctx *context;
+
+ database_base_name = strrchr(location_conf->database_path_cstr, '/');
+ if (database_base_name) {
+ char database_dir[PATH_MAX];
+ database_dir[0] = '\0';
+ strncat(database_dir,
+ location_conf->database_path_cstr,
+ database_base_name - location_conf->database_path_cstr);
+ data->rc = ngx_http_groonga_mkdir_p(data->log, database_dir);
+ if (data->rc != NGX_OK) {
+ return;
+ }
+ }
+
+ context = &(location_conf->context);
+ grn_db_create(context, location_conf->database_path_cstr, NULL);
+ if (context->rc == GRN_SUCCESS) {
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_EMERG, data->log, 0,
+ "failed to create groonga database: %s",
+ context->errbuf);
+ data->rc = NGX_ERROR;
+}
+
+static void
+ngx_http_groonga_open_database_callback(ngx_http_groonga_loc_conf_t *location_conf,
+ void *user_data)
+{
+ ngx_http_groonga_database_callback_data_t *data = user_data;
+ grn_ctx *context;
+
+ context = &(location_conf->context);
+ data->rc = ngx_http_groonga_context_init(context, location_conf,
+ data->pool, data->log);
+ if (data->rc != NGX_OK) {
+ return;
+ }
+
+ if (!location_conf->database_path.data) {
+ ngx_log_error(NGX_LOG_EMERG, data->log, 0,
+ "%s: \"groonga_database\" must be specified in block at %s:%d",
+ location_conf->name,
+ location_conf->config_file,
+ location_conf->config_line);
+ data->rc = NGX_ERROR;
+ return;
+ }
+
+ if (!location_conf->database_path_cstr) {
+ location_conf->database_path_cstr =
+ ngx_str_null_terminate(data->pool, &(location_conf->database_path));
+ }
+
+ grn_db_open(context, location_conf->database_path_cstr);
+ if (context->rc != GRN_SUCCESS) {
+ if (location_conf->database_auto_create) {
+ ngx_http_groonga_create_database(location_conf, data);
+ } else {
+ ngx_log_error(NGX_LOG_EMERG, data->log, 0,
+ "failed to open groonga database: %s",
+ context->errbuf);
+ data->rc = NGX_ERROR;
+ return;
+ }
+ }
+
+ location_conf->cache = grn_cache_open(context);
+ if (!location_conf->cache) {
+ ngx_log_error(NGX_LOG_EMERG, data->log, 0,
+ "failed to open groonga cache: %s",
+ context->errbuf);
+ data->rc = NGX_ERROR;
+ return;
+ }
+ if (location_conf->cache_limit != NGX_CONF_UNSET_SIZE) {
+ grn_cache_set_max_n_entries(context,
+ location_conf->cache,
+ location_conf->cache_limit);
+ }
+}
+
+static void
+ngx_http_groonga_close_database_callback(ngx_http_groonga_loc_conf_t *location_conf,
+ void *user_data)
+{
+ ngx_http_groonga_database_callback_data_t *data = user_data;
+ grn_ctx *context;
+
+ context = &(location_conf->context);
+ ngx_http_groonga_context_init_logger(context,
+ location_conf,
+ data->pool,
+ data->log);
+ ngx_http_groonga_context_init_query_logger(context,
+ location_conf,
+ data->pool,
+ data->log);
+ grn_cache_current_set(context, location_conf->cache);
+
+ grn_obj_close(context, grn_ctx_db(context));
+ ngx_http_groonga_context_log_error(data->log, context);
+
+ grn_cache_current_set(context, NULL);
+ grn_cache_close(context, location_conf->cache);
+
+ grn_ctx_fin(context);
+}
+
+static ngx_int_t
+ngx_http_groonga_init_process(ngx_cycle_t *cycle)
+{
+ grn_rc rc;
+ ngx_http_conf_ctx_t *http_conf;
+ ngx_http_groonga_database_callback_data_t data;
+
+ rc = grn_init();
+ if (rc != GRN_SUCCESS) {
+ return NGX_ERROR;
+ }
+
+ grn_set_segv_handler();
+
+ http_conf =
+ (ngx_http_conf_ctx_t *)ngx_get_conf(cycle->conf_ctx, ngx_http_module);
+
+ data.log = cycle->log;
+ data.pool = cycle->pool;
+ data.rc = NGX_OK;
+ ngx_http_groonga_each_loc_conf(http_conf,
+ ngx_http_groonga_open_database_callback,
+ &data);
+
+ return data.rc;
+}
+
+static void
+ngx_http_groonga_exit_process(ngx_cycle_t *cycle)
+{
+ ngx_http_conf_ctx_t *http_conf;
+ ngx_http_groonga_database_callback_data_t data;
+
+ http_conf =
+ (ngx_http_conf_ctx_t *)ngx_get_conf(cycle->conf_ctx, ngx_http_module);
+ data.log = cycle->log;
+ data.pool = cycle->pool;
+ ngx_http_groonga_each_loc_conf(http_conf,
+ ngx_http_groonga_close_database_callback,
+ &data);
+
+ grn_fin();
+
+ return;
+}
+
+/* entry point */
+static ngx_command_t ngx_http_groonga_commands[] = {
+ { ngx_string("groonga"),
+ NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_groonga_conf_set_groonga_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_groonga_loc_conf_t, enabled),
+ NULL },
+
+ { ngx_string("groonga_database"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_groonga_loc_conf_t, database_path),
+ NULL },
+
+ { ngx_string("groonga_database_auto_create"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_groonga_loc_conf_t, database_auto_create),
+ NULL },
+
+ { ngx_string("groonga_base_path"),
+ NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_groonga_loc_conf_t, base_path),
+ NULL },
+
+ { ngx_string("groonga_log_path"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_groonga_conf_set_log_path_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_groonga_loc_conf_t, log_path),
+ NULL },
+
+ { ngx_string("groonga_log_level"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_groonga_conf_set_log_level_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("groonga_query_log_path"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_groonga_conf_set_query_log_path_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_groonga_loc_conf_t, query_log_path),
+ NULL },
+
+ { ngx_string("groonga_cache_limit"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_groonga_loc_conf_t, cache_limit),
+ NULL },
+
+ ngx_null_command
+};
+
+static ngx_http_module_t ngx_http_groonga_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_groonga_create_loc_conf, /* create location configuration */
+ ngx_http_groonga_merge_loc_conf, /* merge location configuration */
+};
+
+ngx_module_t ngx_http_groonga_module = {
+ NGX_MODULE_V1,
+ &ngx_http_groonga_module_ctx, /* module context */
+ ngx_http_groonga_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ ngx_http_groonga_init_process, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ ngx_http_groonga_exit_process, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
diff --git a/storage/mroonga/vendor/groonga/src/suggest/CMakeLists.txt b/storage/mroonga/vendor/groonga/src/suggest/CMakeLists.txt
new file mode 100644
index 00000000000..0773026a618
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/suggest/CMakeLists.txt
@@ -0,0 +1,90 @@
+# Copyright(C) 2012-2013 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../lib
+ ${MRUBY_INCLUDE_DIRS})
+
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/create_dataset_sources.am
+ GROONGA_SUGGEST_CREATE_DATASET_SOURCES)
+add_executable(groonga-suggest-create-dataset
+ ${GROONGA_SUGGEST_CREATE_DATASET_SOURCES})
+set_source_files_properties(${GROONGA_SUGGEST_CREATE_DATASET_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+target_link_libraries(groonga-suggest-create-dataset libgroonga)
+if(NOT MRN_GROONGA_BUNDLED)
+ install(
+ TARGETS groonga-suggest-create-dataset
+ DESTINATION ${BIN_DIR})
+endif()
+
+if(GRN_WITH_LIBEVENT AND GRN_WITH_ZEROMQ AND GRN_WITH_MESSAGE_PACK)
+ set(GRN_WITH_SUGGEST_LEARNER TRUE)
+else()
+ set(GRN_WITH_SUGGEST_LEARNER FALSE)
+endif()
+
+if(GRN_WITH_SUGGEST_LEARNER)
+ include_directories(
+ ${LIBEVENT_INCLUDE_DIRS}
+ ${ZEROMQ_INCLUDE_DIRS}
+ ${MESSAGE_PACK_INCLUDE_DIRS}
+ )
+ link_directories(
+ ${LIBEVENT_LIBRARY_DIRS}
+ ${ZEROMQ_LIBRARY_DIRS}
+ ${MESSAGE_PACK_LIBRARY_DIRS}
+ )
+
+ read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/util_sources.am
+ GROONGA_SUGGEST_UTIL_SOURCES)
+ add_library(groonga-suggest-util STATIC ${GROONGA_SUGGEST_UTIL_SOURCES})
+ set_source_files_properties(${GROONGA_SUGGEST_UTIL_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+
+ read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/learner_sources.am
+ GROONGA_SUGGEST_LEARNER_SOURCES)
+ add_executable(groonga-suggest-learner ${GROONGA_SUGGEST_LEARNER_SOURCES})
+ set_source_files_properties(${GROONGA_SUGGEST_LEARNER_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+ target_link_libraries(groonga-suggest-learner
+ groonga-suggest-util
+ libgroonga
+ ${LIBEVENT_LIBRARIES}
+ ${ZEROMQ_LIBRARIES}
+ ${MESSAGE_PACK_LIBRARIES})
+
+ read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/httpd_sources.am
+ GROONGA_SUGGEST_HTTPD_SOURCES)
+ add_executable(groonga-suggest-httpd ${GROONGA_SUGGEST_HTTPD_SOURCES})
+ set_source_files_properties(${GROONGA_SUGGEST_HTTPD_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+ target_link_libraries(groonga-suggest-httpd
+ groonga-suggest-util
+ libgroonga
+ ${LIBEVENT_LIBRARIES}
+ ${ZEROMQ_LIBRARIES}
+ ${MESSAGE_PACK_LIBRARIES})
+
+ if(NOT MRN_GROONGA_BUNDLED)
+ install(
+ TARGETS groonga-suggest-learner groonga-suggest-httpd
+ DESTINATION ${BIN_DIR})
+ endif()
+endif()
diff --git a/storage/mroonga/vendor/groonga/src/suggest/Makefile.am b/storage/mroonga/vendor/groonga/src/suggest/Makefile.am
new file mode 100644
index 00000000000..cecf4001e9a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/suggest/Makefile.am
@@ -0,0 +1,77 @@
+bin_PROGRAMS =
+
+NONEXISTENT_CXX_SOURCE = nonexistent.cpp
+
+if !PLATFORM_WIN32
+bin_PROGRAMS += \
+ groonga-suggest-create-dataset
+
+if ENABLE_SUGGEST_LEARNER
+bin_PROGRAMS += \
+ groonga-suggest-learner \
+ groonga-suggest-httpd
+noinst_LTLIBRARIES = libutil.la
+endif
+
+endif
+
+EXTRA_DIST = \
+ CMakeLists.txt
+
+AM_CFLAGS = \
+ $(NO_STRICT_ALIASING_CFLAGS) \
+ $(COVERAGE_CFLAGS) \
+ $(GRN_CFLAGS) \
+ $(MESSAGE_PACK_CFLAGS) \
+ $(MRUBY_CFLAGS)
+
+DEFS += $(GRN_DEFS)
+
+AM_LDFLAGS = -no-undefined
+
+DEFAULT_INCLUDES = \
+ -I$(top_builddir) \
+ -I$(srcdir) \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/lib \
+ $(GROONGA_INCLUDEDIR)
+
+include learner_sources.am
+nodist_EXTRA_groonga_suggest_learner_SOURCES = $(NONEXISTENT_CXX_SOURCE)
+groonga_suggest_learner_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(LIBEVENT_CFLAGS) \
+ $(LIBZMQ_CFLAGS) \
+ $(MESSAGE_PACK_CFLAGS)
+groonga_suggest_learner_LDADD = \
+ libutil.la \
+ $(top_builddir)/lib/libgroonga.la \
+ $(LIBEVENT_LIBS) \
+ $(LIBZMQ_LIBS) \
+ $(MESSAGE_PACK_LIBS)
+
+include httpd_sources.am
+nodist_EXTRA_groonga_suggest_httpd_SOURCES = $(NONEXISTENT_CXX_SOURCE)
+groonga_suggest_httpd_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(LIBEVENT_CFLAGS) \
+ $(LIBZMQ_CFLAGS) \
+ $(MESSAGE_PACK_CFLAGS)
+groonga_suggest_httpd_LDADD = \
+ libutil.la \
+ $(top_builddir)/lib/libgroonga.la \
+ $(LIBEVENT_LIBS) \
+ $(LIBZMQ_LIBS) \
+ $(MESSAGE_PACK_LIBS)
+
+include create_dataset_sources.am
+nodist_EXTRA_groonga_suggest_create_dataset_SOURCES = $(NONEXISTENT_CXX_SOURCE)
+groonga_suggest_create_dataset_CFLAGS = \
+ $(AM_CFLAGS)
+groonga_suggest_create_dataset_LDADD = \
+ $(top_builddir)/lib/libgroonga.la
+
+include util_sources.am
+libutil_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(LIBEVENT_CFLAGS)
diff --git a/storage/mroonga/vendor/groonga/src/suggest/create_dataset_sources.am b/storage/mroonga/vendor/groonga/src/suggest/create_dataset_sources.am
new file mode 100644
index 00000000000..cfecd6500dd
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/suggest/create_dataset_sources.am
@@ -0,0 +1,2 @@
+groonga_suggest_create_dataset_SOURCES = \
+ groonga_suggest_create_dataset.c
diff --git a/storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_create_dataset.c b/storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_create_dataset.c
new file mode 100644
index 00000000000..35f2cd09fc0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_create_dataset.c
@@ -0,0 +1,220 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2010-2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <groonga.h>
+
+/* For grn_str_getopt() */
+#include <str.h>
+
+typedef enum {
+ MODE_NONE,
+ MODE_USAGE
+} ModeFlags;
+
+static const char *DEFAULT_DEFAULT_TOKENIZER = "TokenBigram";
+
+static void
+usage(FILE *output, int argc, char **argv)
+{
+#define OUTPUT(...) fprintf(output, __VA_ARGS__)
+
+ OUTPUT("Usage: %s [OPTIONS] DB_PATH DATASET_NAME\n", argv[0]);
+ OUTPUT(" e.g.: %s /tmp/db shops\n", argv[0]);
+ OUTPUT("\n");
+ OUTPUT("Options:\n");
+ OUTPUT(" --default-tokenizer=TOKENIZER Use TOKENIZER as the default\n");
+ OUTPUT(" tokenizer for item name\n");
+ OUTPUT(" (default: %s)\n",
+ DEFAULT_DEFAULT_TOKENIZER);
+ OUTPUT(" -h, --help Show this message and exit\n");
+
+#undef OUTPUT
+}
+
+static void
+output(grn_ctx *ctx)
+{
+ int flags = 0;
+ char *str;
+ unsigned int str_len;
+
+ do {
+ grn_ctx_recv(ctx, &str, &str_len, &flags);
+ if (str_len > 0 || ctx->rc) {
+ if (ctx->rc) {
+ printf("ERROR (%d): %s\n", ctx->rc, ctx->errbuf);
+ }
+ if (str_len > 0) {
+ printf("%.*s\n", str_len, str);
+ }
+ }
+ } while (flags & GRN_CTX_MORE);
+}
+
+static void
+send_command(grn_ctx *ctx, grn_obj *buffer, const char *command,
+ const char *dataset_name)
+{
+ const char *p = command;
+ const char *dataset_place_holder = "${DATASET}";
+ char *dataset_place_holder_position;
+
+ if (ctx->rc != GRN_SUCCESS) {
+ return;
+ }
+
+ GRN_BULK_REWIND(buffer);
+ while ((dataset_place_holder_position = strstr(p, dataset_place_holder))) {
+ GRN_TEXT_PUT(ctx, buffer, p, dataset_place_holder_position - p);
+ GRN_TEXT_PUTS(ctx, buffer, dataset_name);
+ p = dataset_place_holder_position + strlen(dataset_place_holder);
+ }
+ GRN_TEXT_PUTS(ctx, buffer, p);
+ printf("> %.*s\n", (int)GRN_TEXT_LEN(buffer), GRN_TEXT_VALUE(buffer));
+ grn_ctx_send(ctx, GRN_TEXT_VALUE(buffer), GRN_TEXT_LEN(buffer), 0);
+ output(ctx);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ const char *db_path;
+ const char *dataset_name;
+ grn_ctx ctx_, *ctx;
+ grn_obj *db;
+ grn_bool success = GRN_TRUE;
+ int parsed_argc, rest_argc;
+ int flags = MODE_NONE;
+ const char *default_tokenizer = NULL;
+ static grn_str_getopt_opt opts[] = {
+ {'\0', "default-tokenizer", NULL, 0, GETOPT_OP_NONE},
+ {'h', "help", NULL, MODE_USAGE, GETOPT_OP_UPDATE}
+ };
+
+ opts[0].arg = &default_tokenizer;
+
+ parsed_argc = grn_str_getopt(argc, argv, opts, &flags);
+ if (parsed_argc < 0) {
+ usage(stderr, argc, argv);
+ return EXIT_FAILURE;
+ }
+
+ if (flags & MODE_USAGE) {
+ usage(stdout, argc, argv);
+ return EXIT_SUCCESS;
+ }
+
+ rest_argc = argc - parsed_argc;
+ if (rest_argc != 2) {
+ usage(stderr, argc, argv);
+ return EXIT_FAILURE;
+ }
+
+ db_path = argv[parsed_argc];
+ dataset_name = argv[parsed_argc + 1];
+
+ grn_init();
+
+ ctx = &ctx_;
+ grn_ctx_init(ctx, 0);
+ db = grn_db_open(ctx, db_path);
+ if (!db) {
+ if (ctx->rc == GRN_NO_SUCH_FILE_OR_DIRECTORY) {
+ db = grn_db_create(ctx, db_path, NULL);
+ if (!db) {
+ fprintf(stderr, "DB create failed (%s): %s\n", db_path, ctx->errbuf);
+ }
+ } else {
+ fprintf(stderr, "DB open failed (%s): %s\n", db_path, ctx->errbuf);
+ }
+ }
+
+ if (db) {
+ grn_obj text;
+ GRN_TEXT_INIT(&text, 0);
+#define SEND(string) send_command(ctx, &text, string, dataset_name)
+ SEND("register suggest/suggest");
+ SEND("table_create event_type TABLE_HASH_KEY ShortText");
+ {
+ grn_obj query;
+ GRN_TEXT_INIT(&query, 0);
+ GRN_TEXT_PUTS(ctx, &query,
+ "table_create bigram TABLE_PAT_KEY|KEY_NORMALIZE ShortText "
+ "--default_tokenizer ");
+ if (default_tokenizer) {
+ GRN_TEXT_PUTS(ctx, &query, default_tokenizer);
+ } else {
+ GRN_TEXT_PUTS(ctx, &query, DEFAULT_DEFAULT_TOKENIZER);
+ }
+ GRN_TEXT_PUTC(ctx, &query, '\0');
+ SEND(GRN_TEXT_VALUE(&query));
+ GRN_OBJ_FIN(ctx, &query);
+ }
+ SEND("table_create kana TABLE_PAT_KEY|KEY_NORMALIZE ShortText");
+ SEND("table_create item_${DATASET} TABLE_PAT_KEY|KEY_NORMALIZE "
+ "ShortText --default_tokenizer TokenDelimit");
+ SEND("column_create bigram item_${DATASET}_key "
+ "COLUMN_INDEX|WITH_POSITION item_${DATASET} _key");
+ SEND("column_create item_${DATASET} kana COLUMN_VECTOR kana");
+ SEND("column_create kana item_${DATASET}_kana COLUMN_INDEX "
+ "item_${DATASET} kana");
+ SEND("column_create item_${DATASET} freq COLUMN_SCALAR Int32");
+ SEND("column_create item_${DATASET} last COLUMN_SCALAR Time");
+ SEND("column_create item_${DATASET} boost COLUMN_SCALAR Int32");
+ SEND("column_create item_${DATASET} freq2 COLUMN_SCALAR Int32");
+ SEND("column_create item_${DATASET} buzz COLUMN_SCALAR Int32");
+
+ SEND("table_create pair_${DATASET} TABLE_HASH_KEY UInt64");
+ SEND("column_create pair_${DATASET} pre COLUMN_SCALAR item_${DATASET}");
+ SEND("column_create pair_${DATASET} post COLUMN_SCALAR item_${DATASET}");
+ SEND("column_create pair_${DATASET} freq0 COLUMN_SCALAR Int32");
+ SEND("column_create pair_${DATASET} freq1 COLUMN_SCALAR Int32");
+ SEND("column_create pair_${DATASET} freq2 COLUMN_SCALAR Int32");
+ SEND("column_create item_${DATASET} co COLUMN_INDEX pair_${DATASET} pre");
+
+ SEND("table_create sequence_${DATASET} TABLE_HASH_KEY ShortText");
+ SEND("table_create event_${DATASET} TABLE_NO_KEY");
+ SEND("column_create sequence_${DATASET} events "
+ "COLUMN_VECTOR|RING_BUFFER event_${DATASET}");
+ SEND("column_create event_${DATASET} type COLUMN_SCALAR event_type");
+ SEND("column_create event_${DATASET} time COLUMN_SCALAR Time");
+ SEND("column_create event_${DATASET} item COLUMN_SCALAR item_${DATASET}");
+ SEND("column_create event_${DATASET} sequence COLUMN_SCALAR "
+ "sequence_${DATASET}");
+
+ SEND("table_create configuration TABLE_HASH_KEY ShortText");
+ SEND("column_create configuration weight COLUMN_SCALAR UInt32");
+ SEND("load --table configuration");
+ SEND("[");
+ SEND("{\"_key\": \"${DATASET}\", \"weight\": 1}");
+ SEND("]");
+#undef SEND
+ success = ctx->rc == GRN_SUCCESS;
+ GRN_OBJ_FIN(ctx, &text);
+ GRN_OBJ_FIN(ctx, db);
+ } else {
+ success = GRN_FALSE;
+ }
+ grn_ctx_fin(ctx);
+ grn_fin();
+
+ return success ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_ddl.txt b/storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_ddl.txt
new file mode 100644
index 00000000000..f82763f8f1d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_ddl.txt
@@ -0,0 +1,62 @@
+suggest向けDDL解説
+==================
+
+suggest機能で必要となるスキーマを説明する。なお、このドキュメントはsuggest機能完成とともにdocsディレクトリ内に移動される。
+
+1. 概要
+
+ suggestデータベースには、学習過程でのみ必要になるテーブルと、
+ 学習時およびサジェスト時に必要になるテーブルがあります。
+ 前者を作業用テーブルと呼び、後者を学習結果テーブルと呼びます。
+
+2. 学習結果テーブル
+
+ 学習結果テーブルは二つのファクトテーブルと二つの語彙テーブルから構成されます。
+
+2.1. 学習結果ファクトテーブル
+
+item: suggest候補として提示する個々の文字列を格納するテーブルです。
+
+ 以下のカラムを用意しています。
+
+ _key: itemテーブルの主キーは、サジェスト対象文字列そのものとなります。
+ kana: 対象文字列の読み仮名を格納します。必須ではありませんが、セットされていればローマ字入力途中の文字列に対しても補完候補を提示可能となります。
+ freq: ユーザクエリにおいて入力された回数を記録します。
+ last: ユーザクエリに最後に入力された時刻を記録します。
+ freq2: ユーザクエリにおいてサブミットされた回数を記録します。
+ boost: 当該文字列の露出を制御します。正値を指定すれば露出が頻繁になり、-1を指定すれば、露出されません。
+ buzz: 入力回数の差分を記録します。
+ co: 共起する他の文字列を参照するための索引です。
+
+pair: item間の共起関係を管理するテーブルです。
+
+ 以下のカラムを用意しています。
+
+ _kay: preおよびpostのIDを結合した数値が主キーとなります。
+ pre: 事前に出現するアイティムです。
+ post: 事後に出現するアイティムです。
+ freq0: completeイベントにおける共起頻度を記録します。
+ freq1: correctイベントにおける共起頻度を記録します。
+ freq2: suggestイベントにおける共起頻度を記録します。
+
+2.2. 学習結果語彙テーブル
+
+ bigram: itemの主キーによって全文検索を行うための語彙表です。
+ kana: itemのkanaカラムの入力補完を行うための語彙表です。
+
+3. 作業用テーブル
+
+ 作業用テーブルにはeventとsequenceの2テーブルがあります。
+
+event: ユーザの個々のクエリイベントに対応します。レコードの追加のみが行われます。
+ 適宜dropして再作成することが望ましいです。
+
+ type: submitの有無を記録します。
+ time: イベントが発生した時刻を記録します。
+ item: ユーザが入力した文字列を記録します。
+ sequence: ユーザの識別子(cookie, ipアドレス等)を記録します。
+
+sequence: 同一ユーザの一連の操作を記録します。レコードの追加のみが行われます。
+ 適宜dropして再作成することが望ましいです。
+
+ events: 当該ユーザの操作履歴を記録します。
diff --git a/storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_httpd.c b/storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_httpd.c
new file mode 100644
index 00000000000..6d94be87a8a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_httpd.c
@@ -0,0 +1,842 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2010-2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/* groonga origin headers */
+#include <str.h>
+
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+
+#include <fcntl.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/resource.h>
+
+#include "zmq_compatible.h"
+#include <event.h>
+#include <evhttp.h>
+#include <msgpack.h>
+#include <groonga.h>
+#include <pthread.h>
+
+#include "util.h"
+
+#define DEFAULT_PORT 8080
+#define DEFAULT_MAX_THREADS 8
+
+#define CONST_STR_LEN(x) x, x ? sizeof(x) - 1 : 0
+
+#define LISTEN_BACKLOG 756
+#define MIN_MAX_FDS 2048
+#define MAX_THREADS 128 /* max 256 */
+
+typedef enum {
+ run_mode_none = 0,
+ run_mode_usage,
+ run_mode_daemon,
+ run_mode_error
+} run_mode;
+
+#define RUN_MODE_MASK 0x007f
+#define RUN_MODE_ENABLE_MAX_FD_CHECK 0x0080
+
+
+typedef struct {
+ grn_ctx *ctx;
+ grn_obj *db;
+ void *zmq_sock;
+ grn_obj cmd_buf;
+ grn_obj pass_through_parameters;
+ pthread_t thd;
+ uint32_t thread_id;
+ struct event_base *base;
+ struct evhttp *httpd;
+ struct event pulse;
+ const char *log_base_path;
+ FILE *log_file;
+ uint32_t log_count;
+ grn_bool request_reopen_log_file;
+} thd_data;
+
+typedef struct {
+ const char *db_path;
+ const char *recv_endpoint;
+ pthread_t thd;
+ void *zmq_ctx;
+} recv_thd_data;
+
+#define CMD_BUF_SIZE 1024
+
+static thd_data threads[MAX_THREADS];
+static uint32_t default_max_threads = DEFAULT_MAX_THREADS;
+static uint32_t max_threads;
+static volatile sig_atomic_t loop = 1;
+static grn_obj *db;
+static uint32_t n_lines_per_log_file = 1000000;
+
+static int
+suggest_result(grn_ctx *ctx,
+ struct evbuffer *res_buf, const char *types, const char *query,
+ const char *target_name, int frequency_threshold,
+ double conditional_probability_threshold, int limit,
+ grn_obj *cmd_buf, grn_obj *pass_through_parameters)
+{
+ if (target_name && types && query) {
+ GRN_BULK_REWIND(cmd_buf);
+ GRN_TEXT_PUTS(ctx, cmd_buf, "/d/suggest?table=item_");
+ grn_text_urlenc(ctx, cmd_buf, target_name, strlen(target_name));
+ GRN_TEXT_PUTS(ctx, cmd_buf, "&column=kana&types=");
+ grn_text_urlenc(ctx, cmd_buf, types, strlen(types));
+ GRN_TEXT_PUTS(ctx, cmd_buf, "&query=");
+ grn_text_urlenc(ctx, cmd_buf, query, strlen(query));
+ GRN_TEXT_PUTS(ctx, cmd_buf, "&frequency_threshold=");
+ grn_text_itoa(ctx, cmd_buf, frequency_threshold);
+ GRN_TEXT_PUTS(ctx, cmd_buf, "&conditional_probability_threshold=");
+ grn_text_ftoa(ctx, cmd_buf, conditional_probability_threshold);
+ GRN_TEXT_PUTS(ctx, cmd_buf, "&limit=");
+ grn_text_itoa(ctx, cmd_buf, limit);
+ if (GRN_TEXT_LEN(pass_through_parameters) > 0) {
+ GRN_TEXT_PUTS(ctx, cmd_buf, "&");
+ GRN_TEXT_PUT(ctx, cmd_buf,
+ GRN_TEXT_VALUE(pass_through_parameters),
+ GRN_TEXT_LEN(pass_through_parameters));
+ }
+ {
+ char *res;
+ int flags;
+ unsigned int res_len;
+
+ grn_ctx_send(ctx, GRN_TEXT_VALUE(cmd_buf), GRN_TEXT_LEN(cmd_buf), 0);
+ grn_ctx_recv(ctx, &res, &res_len, &flags);
+
+ evbuffer_add(res_buf, res, res_len);
+ return res_len;
+ }
+ } else {
+ evbuffer_add(res_buf, "{}", 2);
+ return 2;
+ }
+}
+
+static void
+log_send(struct evkeyvalq *output_headers, struct evbuffer *res_buf,
+ thd_data *thd, struct evkeyvalq *get_args)
+{
+ uint64_t millisec;
+ int frequency_threshold, limit;
+ double conditional_probability_threshold;
+ const char *callback, *types, *query, *client_id, *target_name,
+ *learn_target_name;
+
+ GRN_BULK_REWIND(&(thd->pass_through_parameters));
+ parse_keyval(thd->ctx, get_args, &query, &types, &client_id, &target_name,
+ &learn_target_name, &callback, &millisec, &frequency_threshold,
+ &conditional_probability_threshold, &limit,
+ &(thd->pass_through_parameters));
+
+ /* send data to learn client */
+ if (thd->zmq_sock && millisec && client_id && query && learn_target_name) {
+ char c;
+ size_t l;
+ msgpack_packer pk;
+ msgpack_sbuffer sbuf;
+ int cnt, submit_flag = 0;
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
+
+ cnt = 4;
+ if (types && !strcmp(types, "submit")) {
+ cnt++;
+ types = NULL;
+ submit_flag = 1;
+ }
+ msgpack_pack_map(&pk, cnt);
+
+ c = 'i';
+ msgpack_pack_raw(&pk, 1);
+ msgpack_pack_raw_body(&pk, &c, 1);
+ l = strlen(client_id);
+ msgpack_pack_raw(&pk, l);
+ msgpack_pack_raw_body(&pk, client_id, l);
+
+ c = 'q';
+ msgpack_pack_raw(&pk, 1);
+ msgpack_pack_raw_body(&pk, &c, 1);
+ l = strlen(query);
+ msgpack_pack_raw(&pk, l);
+ msgpack_pack_raw_body(&pk, query, l);
+
+ c = 's';
+ msgpack_pack_raw(&pk, 1);
+ msgpack_pack_raw_body(&pk, &c, 1);
+ msgpack_pack_uint64(&pk, millisec);
+
+ c = 'l';
+ msgpack_pack_raw(&pk, 1);
+ msgpack_pack_raw_body(&pk, &c, 1);
+ l = strlen(learn_target_name);
+ msgpack_pack_raw(&pk, l);
+ msgpack_pack_raw_body(&pk, learn_target_name, l);
+
+ if (submit_flag) {
+ c = 't';
+ msgpack_pack_raw(&pk, 1);
+ msgpack_pack_raw_body(&pk, &c, 1);
+ msgpack_pack_true(&pk);
+ }
+ {
+ zmq_msg_t msg;
+ if (!zmq_msg_init_size(&msg, sbuf.size)) {
+ memcpy((void *)zmq_msg_data(&msg), sbuf.data, sbuf.size);
+ if (zmq_msg_send(&msg, thd->zmq_sock, 0)) {
+ print_error("zmq_msg_send() error");
+ }
+ zmq_msg_close(&msg);
+ }
+ }
+ msgpack_sbuffer_destroy(&sbuf);
+ }
+ /* make result */
+ {
+ int content_length;
+ if (callback) {
+ evhttp_add_header(output_headers,
+ "Content-Type", "text/javascript; charset=UTF-8");
+ content_length = strlen(callback);
+ evbuffer_add(res_buf, callback, content_length);
+ evbuffer_add(res_buf, "(", 1);
+ content_length += suggest_result(thd->ctx,
+ res_buf, types, query, target_name,
+ frequency_threshold,
+ conditional_probability_threshold,
+ limit,
+ &(thd->cmd_buf),
+ &(thd->pass_through_parameters)) + 3;
+ evbuffer_add(res_buf, ");", 2);
+ } else {
+ evhttp_add_header(output_headers,
+ "Content-Type", "application/json; charset=UTF-8");
+ content_length = suggest_result(thd->ctx,
+ res_buf, types, query, target_name,
+ frequency_threshold,
+ conditional_probability_threshold,
+ limit,
+ &(thd->cmd_buf),
+ &(thd->pass_through_parameters));
+ }
+ if (content_length >= 0) {
+ char num_buf[16];
+ snprintf(num_buf, 16, "%d", content_length);
+ evhttp_add_header(output_headers, "Content-Length", num_buf);
+ }
+ }
+}
+
+static void
+cleanup_httpd_thread(thd_data *thd) {
+ if (thd->log_file) {
+ fclose(thd->log_file);
+ }
+ if (thd->httpd) {
+ evhttp_free(thd->httpd);
+ }
+ if (thd->zmq_sock) {
+ zmq_close(thd->zmq_sock);
+ }
+ grn_obj_unlink(thd->ctx, &(thd->cmd_buf));
+ grn_obj_unlink(thd->ctx, &(thd->pass_through_parameters));
+ if (thd->ctx) {
+ grn_ctx_close(thd->ctx);
+ }
+ event_base_free(thd->base);
+}
+
+static void
+close_log_file(thd_data *thread)
+{
+ fclose(thread->log_file);
+ thread->log_file = NULL;
+ thread->request_reopen_log_file = GRN_FALSE;
+}
+
+static void
+generic_handler(struct evhttp_request *req, void *arg)
+{
+ struct evkeyvalq args;
+ thd_data *thd = arg;
+
+ if (!loop) {
+ event_base_loopexit(thd->base, NULL);
+ return;
+ }
+ if (!req->uri) { return; }
+
+ evhttp_parse_query(req->uri, &args);
+ {
+ struct evbuffer *res_buf;
+ if (!(res_buf = evbuffer_new())) {
+ err(1, "failed to create response buffer");
+ }
+
+ evhttp_add_header(req->output_headers, "Connection", "close");
+
+ log_send(req->output_headers, res_buf, thd, &args);
+ evhttp_send_reply(req, HTTP_OK, "OK", res_buf);
+ evbuffer_free(res_buf);
+ /* logging */
+ {
+ if (thd->log_base_path) {
+ if (thd->log_file && thd->request_reopen_log_file) {
+ close_log_file(thd);
+ }
+ if (!thd->log_file) {
+ time_t n;
+ struct tm *t_st;
+ char p[PATH_MAX + 1];
+
+ time(&n);
+ t_st = localtime(&n);
+
+ snprintf(p, PATH_MAX, "%s%04d%02d%02d%02d%02d%02d-%02d",
+ thd->log_base_path,
+ t_st->tm_year + 1900, t_st->tm_mon + 1, t_st->tm_mday,
+ t_st->tm_hour, t_st->tm_min, t_st->tm_sec, thd->thread_id);
+
+ if (!(thd->log_file = fopen(p, "a"))) {
+ print_error("cannot open log_file %s.", p);
+ } else {
+ thd->log_count = 0;
+ }
+ }
+ if (thd->log_file) {
+ fprintf(thd->log_file, "%s\n", req->uri);
+ thd->log_count++;
+ if (n_lines_per_log_file > 0 &&
+ thd->log_count >= n_lines_per_log_file) {
+ close_log_file(thd);
+ }
+ }
+ }
+ }
+ }
+ evhttp_clear_headers(&args);
+}
+
+static int
+bind_socket(int port)
+{
+ int nfd;
+ if ((nfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ print_error("cannot open socket for http.");
+ return -1;
+ } else {
+ int r, one = 1;
+ struct sockaddr_in addr;
+
+ r = setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(int));
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = htons(port);
+
+ if ((r = bind(nfd, (struct sockaddr *)&addr, sizeof(addr))) < 0) {
+ print_error("cannot bind socket for http.");
+ return r;
+ }
+ if ((r = listen(nfd, LISTEN_BACKLOG)) < 0) {
+ print_error("cannot listen socket for http.");
+ return r;
+ }
+ if ((r = fcntl(nfd, F_GETFL, 0)) < 0 || fcntl(nfd, F_SETFL, r | O_NONBLOCK) < 0 ) {
+ print_error("cannot fcntl socket for http.");
+ return -1;
+ }
+ return nfd;
+ }
+}
+
+static void
+signal_handler(int sig)
+{
+ loop = 0;
+}
+
+static void
+signal_reopen_log_file(int sig)
+{
+ uint32_t i;
+
+ for (i = 0; i < max_threads; i++) {
+ threads[i].request_reopen_log_file = GRN_TRUE;
+ }
+}
+
+void
+timeout_handler(int fd, short events, void *arg) {
+ thd_data *thd = arg;
+ if (!loop) {
+ event_base_loopexit(thd->base, NULL);
+ } else {
+ struct timeval tv = {1, 0};
+ evtimer_add(&(thd->pulse), &tv);
+ }
+}
+
+static void *
+dispatch(void *arg)
+{
+ event_base_dispatch((struct event_base *)arg);
+ return NULL;
+}
+
+static void
+msgpack2json(msgpack_object *o, grn_ctx *ctx, grn_obj *buf)
+{
+ switch (o->type) {
+ case MSGPACK_OBJECT_POSITIVE_INTEGER:
+ grn_text_ulltoa(ctx, buf, o->via.u64);
+ break;
+ case MSGPACK_OBJECT_RAW:
+ grn_text_esc(ctx, buf, o->via.raw.ptr, o->via.raw.size);
+ break;
+ case MSGPACK_OBJECT_ARRAY:
+ GRN_TEXT_PUTC(ctx, buf, '[');
+ {
+ int i;
+ for (i = 0; i < o->via.array.size; i++) {
+ msgpack2json(o->via.array.ptr, ctx, buf);
+ }
+ }
+ GRN_TEXT_PUTC(ctx, buf, ']');
+ break;
+ case MSGPACK_OBJECT_DOUBLE:
+ grn_text_ftoa(ctx, buf, o->via.dec);
+ break;
+ default:
+ print_error("cannot handle this msgpack type.");
+ }
+}
+
+static void
+load_from_learner(msgpack_object *o, grn_ctx *ctx, grn_obj *cmd_buf)
+{
+ if (o->type == MSGPACK_OBJECT_MAP && o->via.map.size) {
+ msgpack_object_kv *kv;
+ kv = &(o->via.map.ptr[0]);
+ if (kv->key.type == MSGPACK_OBJECT_RAW && kv->key.via.raw.size == 6 &&
+ !memcmp(kv->key.via.raw.ptr, CONST_STR_LEN("target"))) {
+ if (kv->val.type == MSGPACK_OBJECT_RAW) {
+ int i;
+ GRN_BULK_REWIND(cmd_buf);
+ GRN_TEXT_PUTS(ctx, cmd_buf, "load --table ");
+ GRN_TEXT_PUT(ctx, cmd_buf, kv->val.via.raw.ptr, kv->val.via.raw.size);
+ grn_ctx_send(ctx, GRN_TEXT_VALUE(cmd_buf), GRN_TEXT_LEN(cmd_buf), GRN_CTX_MORE);
+ grn_ctx_send(ctx, CONST_STR_LEN("["), GRN_CTX_MORE);
+ if (kv->val.via.raw.size > 5) {
+ if (!memcmp(kv->val.via.raw.ptr, CONST_STR_LEN("item_")) ||
+ !memcmp(kv->val.via.raw.ptr, CONST_STR_LEN("pair_"))) {
+ char delim = '{';
+ GRN_BULK_REWIND(cmd_buf);
+ for (i = 1; i < o->via.map.size; i++) {
+ GRN_TEXT_PUTC(ctx, cmd_buf, delim);
+ kv = &(o->via.map.ptr[i]);
+ msgpack2json(&(kv->key), ctx, cmd_buf);
+ GRN_TEXT_PUTC(ctx, cmd_buf, ':');
+ msgpack2json(&(kv->val), ctx, cmd_buf);
+ delim = ',';
+ }
+ GRN_TEXT_PUTC(ctx, cmd_buf, '}');
+ /* printf("msg: %.*s\n", GRN_TEXT_LEN(cmd_buf), GRN_TEXT_VALUE(cmd_buf)); */
+ grn_ctx_send(ctx, GRN_TEXT_VALUE(cmd_buf), GRN_TEXT_LEN(cmd_buf), GRN_CTX_MORE);
+ }
+ }
+ grn_ctx_send(ctx, CONST_STR_LEN("]"), 0);
+ {
+ char *res;
+ int flags;
+ unsigned int res_len;
+ grn_ctx_recv(ctx, &res, &res_len, &flags);
+ }
+ }
+ }
+ }
+}
+
+static void
+recv_handler(grn_ctx *ctx, void *zmq_recv_sock, msgpack_zone *mempool, grn_obj *cmd_buf)
+{
+ zmq_msg_t msg;
+
+ if (zmq_msg_init(&msg)) {
+ print_error("cannot init zmq message.");
+ } else {
+ if (zmq_msg_recv(&msg, zmq_recv_sock, 0)) {
+ print_error("cannot recv zmq message.");
+ } else {
+ msgpack_object obj;
+ msgpack_unpack_return ret;
+
+ ret = msgpack_unpack(zmq_msg_data(&msg), zmq_msg_size(&msg), NULL, mempool, &obj);
+ if (MSGPACK_UNPACK_SUCCESS == ret) {
+ load_from_learner(&obj, ctx, cmd_buf);
+ } else {
+ print_error("invalid recv data.");
+ }
+ msgpack_zone_clear(mempool);
+ }
+ zmq_msg_close(&msg);
+ }
+}
+
+static void *
+recv_from_learner(void *arg)
+{
+ void *zmq_recv_sock;
+ recv_thd_data *thd = arg;
+
+ if ((zmq_recv_sock = zmq_socket(thd->zmq_ctx, ZMQ_SUB))) {
+ if (!zmq_connect(zmq_recv_sock, thd->recv_endpoint)) {
+ grn_ctx ctx;
+ if (!grn_ctx_init(&ctx, 0)) {
+ if ((!grn_ctx_use(&ctx, db))) {
+ msgpack_zone *mempool;
+ if ((mempool = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE))) {
+ grn_obj cmd_buf;
+ zmq_pollitem_t items[] = {
+ { zmq_recv_sock, 0, ZMQ_POLLIN, 0}
+ };
+ GRN_TEXT_INIT(&cmd_buf, 0);
+ zmq_setsockopt(zmq_recv_sock, ZMQ_SUBSCRIBE, "", 0);
+ while (loop) {
+ zmq_poll(items, 1, 10000);
+ if (items[0].revents & ZMQ_POLLIN) {
+ recv_handler(&ctx, zmq_recv_sock, mempool, &cmd_buf);
+ }
+ }
+ grn_obj_unlink(&ctx, &cmd_buf);
+ msgpack_zone_free(mempool);
+ } else {
+ print_error("cannot create msgpack zone.");
+ }
+ /* db_close */
+ } else {
+ print_error("error in grn_db_open() on recv thread.");
+ }
+ grn_ctx_fin(&ctx);
+ } else {
+ print_error("error in grn_ctx_init() on recv thread.");
+ }
+ } else {
+ print_error("cannot create recv zmq_socket.");
+ }
+ } else {
+ print_error("cannot connect zmq_socket.");
+ }
+ return NULL;
+}
+
+static int
+serve_threads(int nthreads, int port, const char *db_path, void *zmq_ctx,
+ const char *send_endpoint, const char *recv_endpoint,
+ const char *log_base_path)
+{
+ int nfd;
+ uint32_t i;
+ if ((nfd = bind_socket(port)) < 0) {
+ print_error("cannot bind socket. please check port number with netstat.");
+ return -1;
+ }
+
+ for (i = 0; i < nthreads; i++) {
+ memset(&threads[i], 0, sizeof(threads[i]));
+ threads[i].request_reopen_log_file = GRN_FALSE;
+ if (!(threads[i].base = event_init())) {
+ print_error("error in event_init() on thread %d.", i);
+ } else {
+ if (!(threads[i].httpd = evhttp_new(threads[i].base))) {
+ print_error("error in evhttp_new() on thread %d.", i);
+ } else {
+ int r;
+ if ((r = evhttp_accept_socket(threads[i].httpd, nfd))) {
+ print_error("error in evhttp_accept_socket() on thread %d.", i);
+ } else {
+ if (send_endpoint) {
+ if (!(threads[i].zmq_sock = zmq_socket(zmq_ctx, ZMQ_PUB))) {
+ print_error("cannot create zmq_socket.");
+ } else if (zmq_connect(threads[i].zmq_sock, send_endpoint)) {
+ print_error("cannot connect zmq_socket.");
+ zmq_close(threads[i].zmq_sock);
+ threads[i].zmq_sock = NULL;
+ } else {
+ uint64_t hwm = 1;
+ zmq_setsockopt(threads[i].zmq_sock, ZMQ_SNDHWM, &hwm, sizeof(uint64_t));
+ }
+ } else {
+ threads[i].zmq_sock = NULL;
+ }
+ if (!(threads[i].ctx = grn_ctx_open(0))) {
+ print_error("error in grn_ctx_open() on thread %d.", i);
+ } else if (grn_ctx_use(threads[i].ctx, db)) {
+ print_error("error in grn_db_open() on thread %d.", i);
+ } else {
+ GRN_TEXT_INIT(&(threads[i].cmd_buf), 0);
+ GRN_TEXT_INIT(&(threads[i].pass_through_parameters), 0);
+ threads[i].log_base_path = log_base_path;
+ threads[i].thread_id = i;
+ evhttp_set_gencb(threads[i].httpd, generic_handler, &threads[i]);
+ evhttp_set_timeout(threads[i].httpd, 10);
+ {
+ struct timeval tv = {1, 0};
+ evtimer_set(&(threads[i].pulse), timeout_handler, &threads[i]);
+ evtimer_add(&(threads[i].pulse), &tv);
+ }
+ if ((r = pthread_create(&(threads[i].thd), NULL, dispatch, threads[i].base))) {
+ print_error("error in pthread_create() on thread %d.", i);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* recv thread from learner */
+ if (recv_endpoint) {
+ recv_thd_data rthd;
+ rthd.db_path = db_path;
+ rthd.recv_endpoint = recv_endpoint;
+ rthd.zmq_ctx = zmq_ctx;
+
+ if (pthread_create(&(rthd.thd), NULL, recv_from_learner, &rthd)) {
+ print_error("error in pthread_create() on thread %d.", i);
+ }
+ if (pthread_join(rthd.thd, NULL)) {
+ print_error("error in pthread_join() on thread %d.", i);
+ }
+ } else {
+ while (loop) { sleep(1); }
+ }
+
+ /* join all httpd thread */
+ for (i = 0; i < nthreads; i++) {
+ if (threads[i].thd) {
+ if (pthread_join(threads[i].thd, NULL)) {
+ print_error("error in pthread_join() on thread %d.", i);
+ }
+ }
+ cleanup_httpd_thread(&(threads[i]));
+ }
+ return 0;
+}
+
+static uint32_t
+get_core_number(void)
+{
+#ifdef ACTUALLY_GET_CORE_NUMBER
+#ifdef _SC_NPROCESSORS_CONF
+ return sysconf(_SC_NPROCESSORS_CONF);
+#else /* _SC_NPROCESSORS_CONF */
+ int n_processors;
+ size_t length = sizeof(n_processors);
+ int mib[] = {CTL_HW, HW_NCPU};
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]),
+ &n_processors, &length, NULL, 0) == 0 &&
+ length == sizeof(n_processors) &&
+ 0 < n_processors) {
+ return n_processors;
+ } else {
+ return 1;
+ }
+#endif /* _SC_NPROCESSORS_CONF */
+#endif /* ACTUALLY_GET_CORE_NUMBER */
+ return 0;
+}
+
+static void
+usage(FILE *output)
+{
+ fprintf(
+ output,
+ "Usage: groonga-suggest-httpd [options...] db_path\n"
+ "db_path:\n"
+ " specify groonga database path which is used for suggestion.\n"
+ "\n"
+ "options:\n"
+ " -p, --port <port number> : http server port number\n"
+ " (default: %d)\n"
+ /*
+ " --address <ip/hostname> : server address to listen\n"
+ " (default: %s)\n"
+ */
+ " -c <thread number> : number of server threads\n"
+ " (deprecated. use --n-threads)\n"
+ " -t, --n-threads <thread number> : number of server threads\n"
+ " (default: %d)\n"
+ " -s, --send-endpoint <send endpoint> : send endpoint\n"
+ " (ex. tcp://example.com:1234)\n"
+ " -r, --receive-endpoint <receive endpoint> : receive endpoint\n"
+ " (ex. tcp://example.com:1235)\n"
+ " -l, --log-base-path <path prefix> : log path prefix\n"
+ " --n-lines-per-log-file <lines number> : number of lines in a log file\n"
+ " use 0 for disabling this\n"
+ " (default: %d)\n"
+ " -d, --daemon : daemonize\n"
+ " --disable-max-fd-check : disable max FD check on start\n"
+ " -h, --help : show this message\n",
+ DEFAULT_PORT, default_max_threads, n_lines_per_log_file);
+}
+
+int
+main(int argc, char **argv)
+{
+ int port_no = DEFAULT_PORT;
+ const char *max_threads_string = NULL, *port_string = NULL;
+ const char *address;
+ const char *send_endpoint = NULL, *recv_endpoint = NULL, *log_base_path = NULL;
+ const char *n_lines_per_log_file_string = NULL;
+ int n_processed_args, flags = RUN_MODE_ENABLE_MAX_FD_CHECK;
+ run_mode mode = run_mode_none;
+
+ if (!(default_max_threads = get_core_number())) {
+ default_max_threads = DEFAULT_MAX_THREADS;
+ }
+
+ /* parse options */
+ {
+ static grn_str_getopt_opt opts[] = {
+ {'c', NULL, NULL, 0, GETOPT_OP_NONE}, /* deprecated */
+ {'t', "n-threads", NULL, 0, GETOPT_OP_NONE},
+ {'h', "help", NULL, run_mode_usage, GETOPT_OP_UPDATE},
+ {'p', "port", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "bind-address", NULL, 0, GETOPT_OP_NONE}, /* not supported yet */
+ {'s', "send-endpoint", NULL, 0, GETOPT_OP_NONE},
+ {'r', "receive-endpoint", NULL, 0, GETOPT_OP_NONE},
+ {'l', "log-base-path", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "n-lines-per-log-file", NULL, 0, GETOPT_OP_NONE},
+ {'d', "daemon", NULL, run_mode_daemon, GETOPT_OP_UPDATE},
+ {'\0', "disable-max-fd-check", NULL, RUN_MODE_ENABLE_MAX_FD_CHECK,
+ GETOPT_OP_OFF},
+ {'\0', NULL, NULL, 0, 0}
+ };
+ opts[0].arg = &max_threads_string;
+ opts[1].arg = &max_threads_string;
+ opts[3].arg = &port_string;
+ opts[4].arg = &address;
+ opts[5].arg = &send_endpoint;
+ opts[6].arg = &recv_endpoint;
+ opts[7].arg = &log_base_path;
+ opts[8].arg = &n_lines_per_log_file_string;
+
+ n_processed_args = grn_str_getopt(argc, argv, opts, &flags);
+ }
+
+ /* main */
+ mode = (flags & RUN_MODE_MASK);
+ if (n_processed_args < 0 ||
+ (argc - n_processed_args) != 1 ||
+ mode == run_mode_error) {
+ usage(stderr);
+ return EXIT_FAILURE;
+ } else if (mode == run_mode_usage) {
+ usage(stdout);
+ return EXIT_SUCCESS;
+ } else {
+ grn_ctx ctx;
+ void *zmq_ctx;
+
+ if (max_threads_string) {
+ max_threads = atoi(max_threads_string);
+ if (max_threads > MAX_THREADS) {
+ print_error("too many threads. limit to %d.", MAX_THREADS);
+ max_threads = MAX_THREADS;
+ }
+ } else {
+ max_threads = default_max_threads;
+ }
+
+ if (port_string) {
+ port_no = atoi(port_string);
+ }
+
+ if (flags & RUN_MODE_ENABLE_MAX_FD_CHECK) {
+ /* check environment */
+ struct rlimit rlim;
+ if (!getrlimit(RLIMIT_NOFILE, &rlim)) {
+ if (rlim.rlim_max < MIN_MAX_FDS) {
+ print_error("too small max fds. %d required.", MIN_MAX_FDS);
+ return -1;
+ }
+ rlim.rlim_cur = rlim.rlim_cur;
+ setrlimit(RLIMIT_NOFILE, &rlim);
+ }
+ }
+
+ if (n_lines_per_log_file_string) {
+ int64_t n_lines;
+ n_lines = grn_atoll(n_lines_per_log_file_string,
+ n_lines_per_log_file_string + strlen(n_lines_per_log_file_string),
+ NULL);
+ if (n_lines < 0) {
+ print_error("--n-lines-per-log-file must be >= 0: <%s>",
+ n_lines_per_log_file_string);
+ return(EXIT_FAILURE);
+ }
+ if (n_lines > UINT32_MAX) {
+ print_error("--n-lines-per-log-file must be <= %ld: <%s>",
+ UINT32_MAX, n_lines_per_log_file_string);
+ return(EXIT_FAILURE);
+ }
+ n_lines_per_log_file = (uint32_t)n_lines;
+ }
+
+ if (mode == run_mode_daemon) {
+ daemonize();
+ }
+
+ grn_init();
+ grn_ctx_init(&ctx, 0);
+ if ((db = grn_db_open(&ctx, argv[n_processed_args]))) {
+ if ((zmq_ctx = zmq_init(1))) {
+ signal(SIGTERM, signal_handler);
+ signal(SIGINT, signal_handler);
+ signal(SIGQUIT, signal_handler);
+ signal(SIGUSR1, signal_reopen_log_file);
+
+ serve_threads(max_threads, port_no, argv[n_processed_args], zmq_ctx,
+ send_endpoint, recv_endpoint, log_base_path);
+ zmq_term(zmq_ctx);
+ } else {
+ print_error("cannot create zmq context.");
+ }
+ grn_obj_close(&ctx, db);
+ } else {
+ print_error("cannot open db.");
+ }
+ grn_ctx_fin(&ctx);
+ grn_fin();
+ }
+ return 0;
+}
diff --git a/storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_learner.c b/storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_learner.c
new file mode 100644
index 00000000000..060d238565a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/suggest/groonga_suggest_learner.c
@@ -0,0 +1,839 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2010-2014 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/* for grn_str_getopt() */
+#include <str.h>
+
+#include "zmq_compatible.h"
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <msgpack.h>
+#include <pthread.h>
+#include <groonga.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "util.h"
+
+#include <evhttp.h>
+
+#define DEFAULT_RECV_ENDPOINT "tcp://*:1234"
+#define DEFAULT_SEND_ENDPOINT "tcp://*:1235"
+#define SEND_WAIT 1000 /* 0.001sec */
+
+#define CONST_STR_LEN(x) x, x ? sizeof(x) - 1 : 0
+
+typedef enum {
+ RUN_MODE_NONE = 0x00,
+ RUN_MODE_USAGE = 0x01,
+ RUN_MODE_DAEMON = 0x02,
+ RUN_MODE_ERROR = 0x04
+} run_mode;
+
+#define RUN_MODE_MASK 0x007f
+
+typedef struct {
+ const char *db_path;
+ const char *send_endpoint;
+ pthread_t thd;
+ void *zmq_ctx;
+} send_thd_data;
+
+static volatile sig_atomic_t loop = 1;
+
+static void
+load_to_groonga(grn_ctx *ctx,
+ grn_obj *buf,
+ const char *query, uint32_t query_len,
+ const char *client_id, uint32_t client_id_len,
+ const char *learn_target_name, uint32_t learn_target_name_len,
+ uint64_t millisec,
+ int submit)
+{
+ GRN_BULK_REWIND(buf);
+ GRN_TEXT_PUTS(ctx, buf, "load --table event_");
+ GRN_TEXT_PUT(ctx, buf, learn_target_name, learn_target_name_len);
+ GRN_TEXT_PUTS(ctx, buf, " --each 'suggest_preparer(_id,type,item,sequence,time,pair_");
+ GRN_TEXT_PUT(ctx, buf, learn_target_name, learn_target_name_len);
+ GRN_TEXT_PUTS(ctx, buf, ")'");
+ grn_ctx_send(ctx, GRN_TEXT_VALUE(buf), GRN_TEXT_LEN(buf), GRN_CTX_MORE);
+ grn_ctx_send(ctx, CONST_STR_LEN("["), GRN_CTX_MORE);
+
+ GRN_BULK_REWIND(buf);
+ GRN_TEXT_PUTS(ctx, buf, "{\"item\":");
+ grn_text_esc(ctx, buf, query, query_len);
+ GRN_TEXT_PUTS(ctx, buf, ",\"sequence\":");
+ grn_text_esc(ctx, buf, client_id, client_id_len);
+ GRN_TEXT_PUTS(ctx, buf, ",\"time\":");
+ grn_text_ftoa(ctx, buf, (double)millisec / 1000);
+ if (submit) {
+ GRN_TEXT_PUTS(ctx, buf, ",\"type\":\"submit\"}");
+ } else {
+ GRN_TEXT_PUTS(ctx, buf, "}");
+ }
+ /* printf("%.*s\n", GRN_TEXT_LEN(buf), GRN_TEXT_VALUE(buf)); */
+ grn_ctx_send(ctx, GRN_TEXT_VALUE(buf), GRN_TEXT_LEN(buf), GRN_CTX_MORE);
+
+ grn_ctx_send(ctx, CONST_STR_LEN("]"), 0);
+
+ {
+ char *res;
+ int flags;
+ unsigned int res_len;
+ grn_ctx_recv(ctx, &res, &res_len, &flags);
+ }
+}
+
+void
+load_to_multi_targets(grn_ctx *ctx,
+ grn_obj *buf,
+ const char *query, uint32_t query_len,
+ const char *client_id, uint32_t client_id_len,
+ const char *learn_target_names,
+ uint32_t learn_target_names_len,
+ uint64_t millisec,
+ int submit)
+{
+ if (millisec && query && client_id && learn_target_names) {
+ unsigned int tn_len;
+ const char *tn, *tnp, *tne;
+ tn = tnp = learn_target_names;
+ tne = learn_target_names + learn_target_names_len;
+ while (tnp <= tne) {
+ if (tnp == tne || *tnp == '|') {
+ tn_len = tnp - tn;
+
+ /*
+ printf("sec: %" PRIu64 " query %.*s client_id: %.*s target: %.*s\n",
+ millisec,
+ query_len, query,
+ client_id_len, client_id,
+ tn_len, tn);
+ */
+ load_to_groonga(ctx, buf, query, query_len, client_id, client_id_len,
+ tn, tn_len, millisec, submit);
+
+ tn = ++tnp;
+ } else {
+ tnp++;
+ }
+ }
+ }
+}
+
+#define PACK_KEY_FROM_ID(id) do { \
+ int _k_len; \
+ char _k_buf[GRN_TABLE_MAX_KEY_SIZE]; \
+ _k_len = grn_table_get_key(ctx, ref_table, (id), _k_buf, GRN_TABLE_MAX_KEY_SIZE); \
+ msgpack_pack_raw(&pk, _k_len); \
+ msgpack_pack_raw_body(&pk, _k_buf, _k_len); \
+} while (0)
+
+#define PACK_MAP_ITEM(col_name) do { \
+ grn_obj _v; \
+ msgpack_pack_raw(&pk, sizeof(#col_name) - 1); \
+ msgpack_pack_raw_body(&pk, CONST_STR_LEN(#col_name)); \
+ switch (col_##col_name->header.type) { \
+ case GRN_COLUMN_FIX_SIZE: \
+ GRN_VALUE_FIX_SIZE_INIT(&_v, 0, grn_obj_get_range(ctx, col_##col_name)); \
+ break; \
+ case GRN_COLUMN_VAR_SIZE: \
+ if ((col_##col_name->header.flags & GRN_OBJ_COLUMN_TYPE_MASK) == GRN_OBJ_COLUMN_VECTOR) { \
+ GRN_VALUE_FIX_SIZE_INIT(&_v, GRN_OBJ_VECTOR, grn_obj_get_range(ctx, col_##col_name)); \
+ } else { \
+ GRN_VALUE_VAR_SIZE_INIT(&_v, 0, grn_obj_get_range(ctx, col_##col_name)); \
+ } \
+ break; \
+ } \
+ grn_obj_get_value(ctx, col_##col_name, rec_id, &_v); \
+ \
+ switch (_v.header.type) { \
+ case GRN_BULK: \
+ switch (_v.header.domain) { \
+ case GRN_DB_SHORT_TEXT: \
+ msgpack_pack_raw(&pk, GRN_TEXT_LEN(&_v)); \
+ msgpack_pack_raw_body(&pk, GRN_TEXT_VALUE(&_v), GRN_TEXT_LEN(&_v)); \
+ break; \
+ case GRN_DB_INT32: \
+ msgpack_pack_int32(&pk, GRN_INT32_VALUE(&_v)); \
+ break; \
+ case GRN_DB_UINT32: \
+ msgpack_pack_uint32(&pk, GRN_UINT32_VALUE(&_v)); \
+ break; \
+ case GRN_DB_TIME: \
+ msgpack_pack_double(&pk, (double)GRN_TIME_VALUE(&_v) / GRN_TIME_USEC_PER_SEC); \
+ break; \
+ default: /* ref. to ShortText key */ \
+ PACK_KEY_FROM_ID(GRN_RECORD_VALUE(&_v)); \
+ } \
+ break; \
+ case GRN_UVECTOR: /* ref.s to ShortText key */ \
+ { \
+ grn_id *_idv = (grn_id *)GRN_BULK_HEAD(&_v), *_idve = (grn_id *)GRN_BULK_CURR(&_v); \
+ msgpack_pack_array(&pk, _idve - _idv); \
+ for (; _idv < _idve; _idv++) { \
+ PACK_KEY_FROM_ID(*_idv); \
+ } \
+ } \
+ break; \
+ default: \
+ print_error("invalid groonga object type(%d) for msgpack.", _v.header.type); \
+ msgpack_pack_nil(&pk); \
+ break; \
+ } \
+ grn_obj_close(ctx, &_v); \
+} while (0)
+
+static int
+zmq_send_to_httpd(void *zmq_send_sock, void *data, size_t size)
+{
+ zmq_msg_t msg;
+ if (!zmq_msg_init_size(&msg, size)) {
+ memcpy((void *)zmq_msg_data(&msg), data, size);
+ if (zmq_msg_send(&msg, zmq_send_sock, 0)) {
+ print_error("zmq_send() error");
+ return -1;
+ }
+ zmq_msg_close(&msg);
+ } else {
+ print_error("zmq_msg_init_size() error");
+ }
+ return 0;
+}
+
+static void
+send_handler(void *zmq_send_sock, grn_ctx *ctx)
+{
+ grn_table_cursor *cur;
+ if ((cur = grn_table_cursor_open(ctx, grn_ctx_db(ctx), NULL, 0, NULL, 0,
+ 0, -1, 0))) {
+ grn_id table_id;
+ while (loop && (table_id = grn_table_cursor_next(ctx, cur)) != GRN_ID_NIL) {
+ grn_obj *table;
+ if ((table = grn_ctx_at(ctx, table_id))) {
+ int name_len;
+ char name_buf[GRN_TABLE_MAX_KEY_SIZE];
+
+ name_len = grn_obj_name(ctx, table, name_buf,
+ GRN_TABLE_MAX_KEY_SIZE);
+
+ if (name_len > 5) {
+ if (table->header.type == GRN_TABLE_PAT_KEY &&
+ !memcmp(name_buf, CONST_STR_LEN("item_"))) {
+ /* ["_key","ShortText"],["last","Time"],["kana","kana"],["freq2","Int32"],["freq","Int32"],["co","pair_all"],["buzz","Int32"],["boost","Int32"] */
+ grn_obj *ref_table;
+ grn_table_cursor *tc;
+ grn_obj *col_last, *col_kana, *col_freq, *col_freq2,
+ *col_buzz, *col_boost;
+
+ col_kana = grn_obj_column(ctx, table, CONST_STR_LEN("kana"));
+ col_freq = grn_obj_column(ctx, table, CONST_STR_LEN("freq"));
+ col_last = grn_obj_column(ctx, table, CONST_STR_LEN("last"));
+ col_boost = grn_obj_column(ctx, table, CONST_STR_LEN("boost"));
+ col_freq2 = grn_obj_column(ctx, table, CONST_STR_LEN("freq2"));
+ col_buzz = grn_obj_column(ctx, table, CONST_STR_LEN("buzz"));
+
+ ref_table = grn_ctx_at(ctx, grn_obj_get_range(ctx, col_kana));
+
+ if ((tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL,
+ 0, 0, -1, 0))) {
+ grn_id rec_id;
+ while (loop && (rec_id = grn_table_cursor_next(ctx, tc))
+ != GRN_ID_NIL) {
+ char *key;
+ size_t key_len;
+ msgpack_packer pk;
+ msgpack_sbuffer sbuf;
+
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_map(&pk, 8);
+
+ /* ["_key","ShortText"],["last","Time"],["kana","kana"],["freq2","Int32"],["freq","Int32"],["co","pair_all"],["buzz","Int32"],["boost","Int32"] */
+ msgpack_pack_raw(&pk, 6);
+ msgpack_pack_raw_body(&pk, CONST_STR_LEN("target"));
+ msgpack_pack_raw(&pk, name_len);
+ msgpack_pack_raw_body(&pk, name_buf, name_len);
+
+ msgpack_pack_raw(&pk, 4);
+ msgpack_pack_raw_body(&pk,
+ GRN_COLUMN_NAME_KEY,
+ GRN_COLUMN_NAME_KEY_LEN);
+ key_len = grn_table_cursor_get_key(ctx, tc, (void **)&key);
+ msgpack_pack_raw(&pk, key_len);
+ msgpack_pack_raw_body(&pk, key, key_len);
+
+ PACK_MAP_ITEM(last);
+ PACK_MAP_ITEM(kana);
+ PACK_MAP_ITEM(freq);
+ PACK_MAP_ITEM(freq2);
+ PACK_MAP_ITEM(buzz);
+ PACK_MAP_ITEM(boost);
+
+ zmq_send_to_httpd(zmq_send_sock, sbuf.data, sbuf.size);
+
+ usleep(SEND_WAIT);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ } else if (table->header.type == GRN_TABLE_HASH_KEY &&
+ !memcmp(name_buf, CONST_STR_LEN("pair_"))) {
+ grn_obj *ref_table;
+ grn_table_cursor *tc;
+ grn_obj *col_pre, *col_post, *col_freq0, *col_freq1, *col_freq2;
+
+ col_pre = grn_obj_column(ctx, table, CONST_STR_LEN("pre"));
+ col_post = grn_obj_column(ctx, table, CONST_STR_LEN("post"));
+ col_freq0 = grn_obj_column(ctx, table, CONST_STR_LEN("freq0"));
+ col_freq1 = grn_obj_column(ctx, table, CONST_STR_LEN("freq1"));
+ col_freq2 = grn_obj_column(ctx, table, CONST_STR_LEN("freq2"));
+
+ ref_table = grn_ctx_at(ctx, grn_obj_get_range(ctx, col_pre));
+
+ if ((tc = grn_table_cursor_open(ctx, table, NULL, 0, NULL,
+ 0, 0, -1, 0))) {
+ grn_id rec_id;
+ while (loop && (rec_id = grn_table_cursor_next(ctx, tc))
+ != GRN_ID_NIL) {
+ uint64_t *key;
+ msgpack_packer pk;
+ msgpack_sbuffer sbuf;
+
+ /* skip freq0 == 0 && freq1 == 0 && freq2 == 0 */
+ {
+ grn_obj f;
+ grn_obj_get_value(ctx, col_freq0, rec_id, &f);
+ if (!GRN_INT32_VALUE(&f)) {
+ grn_obj_get_value(ctx, col_freq1, rec_id, &f);
+ if (!GRN_INT32_VALUE(&f)) {
+ grn_obj_get_value(ctx, col_freq2, rec_id, &f);
+ if (!GRN_INT32_VALUE(&f)) { continue; }
+ }
+ }
+ }
+
+ /* make pair_* message */
+ msgpack_sbuffer_init(&sbuf);
+ msgpack_packer_init(&pk, &sbuf, msgpack_sbuffer_write);
+
+ msgpack_pack_map(&pk, 7);
+ /* ["_key","UInt64"],["pre","item_all"],["post","item_all"],["freq2","Int32"],["freq1","Int32"],["freq0","Int32"] */
+
+ msgpack_pack_raw(&pk, 6);
+ msgpack_pack_raw_body(&pk, CONST_STR_LEN("target"));
+ msgpack_pack_raw(&pk, name_len);
+ msgpack_pack_raw_body(&pk, name_buf, name_len);
+
+ msgpack_pack_raw(&pk, 4);
+ msgpack_pack_raw_body(&pk,
+ GRN_COLUMN_NAME_KEY,
+ GRN_COLUMN_NAME_KEY_LEN);
+ grn_table_cursor_get_key(ctx, tc, (void **)&key);
+ msgpack_pack_uint64(&pk, *key);
+
+ PACK_MAP_ITEM(pre);
+ PACK_MAP_ITEM(post);
+ PACK_MAP_ITEM(freq0);
+ PACK_MAP_ITEM(freq1);
+ PACK_MAP_ITEM(freq2);
+
+ zmq_send_to_httpd(zmq_send_sock, sbuf.data, sbuf.size);
+
+ usleep(SEND_WAIT);
+
+ msgpack_sbuffer_destroy(&sbuf);
+ }
+ grn_table_cursor_close(ctx, tc);
+ }
+ }
+ }
+ grn_obj_unlink(ctx, table);
+ }
+ }
+ grn_table_cursor_close(ctx, cur);
+ }
+}
+
+static void *
+send_to_httpd(void *arg)
+{
+ send_thd_data *thd = arg;
+ void *zmq_send_sock;
+ if ((zmq_send_sock = zmq_socket(thd->zmq_ctx, ZMQ_PUB))) {
+ if (!zmq_bind(zmq_send_sock, thd->send_endpoint)) {
+ grn_ctx ctx;
+ if (!(grn_ctx_init(&ctx, 0))) {
+ grn_obj *db;
+ if ((db = grn_db_open(&ctx, thd->db_path))) {
+ uint64_t hwm = 1;
+ zmq_setsockopt(zmq_send_sock, ZMQ_SNDHWM, &hwm, sizeof(uint64_t));
+ while (loop) {
+ send_handler(zmq_send_sock, &ctx);
+ }
+ grn_obj_close(&ctx, db);
+ } else {
+ print_error("error in grn_db_open() on send thread.");
+ }
+ grn_ctx_fin(&ctx);
+ } else {
+ print_error("error in grn_ctx_init() on send thread.");
+ }
+ } else {
+ print_error("cannot bind zmq_socket.");
+ }
+ } else {
+ print_error("cannot create zmq_socket.");
+ }
+ return NULL;
+}
+
+static void
+handle_msg(msgpack_object *obj, grn_ctx *ctx, grn_obj *buf)
+{
+ int submit_flag = 0;
+ uint64_t millisec = 0;
+ const char *query = NULL,
+ *client_id = NULL, *learn_target_names = NULL;
+ uint32_t query_len = 0, client_id_len = 0, learn_target_names_len = 0;
+ if (obj->type == MSGPACK_OBJECT_MAP) {
+ int i;
+ for (i = 0; i < obj->via.map.size; i++) {
+ msgpack_object_kv *kv;
+ kv = &(obj->via.map.ptr[i]);
+ if (kv->key.type == MSGPACK_OBJECT_RAW && kv->key.via.raw.size) {
+ switch (kv->key.via.raw.ptr[0]) {
+ case 'i':
+ if (kv->val.type == MSGPACK_OBJECT_RAW) {
+ client_id_len = kv->val.via.raw.size;
+ client_id = kv->val.via.raw.ptr;
+ }
+ break;
+ case 'q':
+ if (kv->val.type == MSGPACK_OBJECT_RAW) {
+ query_len = kv->val.via.raw.size;
+ query = kv->val.via.raw.ptr;
+ }
+ break;
+ case 'l':
+ if (kv->val.type == MSGPACK_OBJECT_RAW) {
+ learn_target_names_len = kv->val.via.raw.size;
+ learn_target_names = kv->val.via.raw.ptr;
+ }
+ break;
+ case 's':
+ if (kv->val.type == MSGPACK_OBJECT_POSITIVE_INTEGER) {
+ millisec = kv->val.via.u64;
+ }
+ break;
+ case 't':
+ if (kv->val.type == MSGPACK_OBJECT_BOOLEAN) {
+ submit_flag = (kv->val.via.boolean ? 1 : 0);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ load_to_multi_targets(ctx, buf, query, query_len,
+ client_id, client_id_len,
+ learn_target_names, learn_target_names_len,
+ millisec, submit_flag);
+ }
+}
+
+static void
+recv_event_loop(msgpack_zone *mempool, void *zmq_sock, grn_ctx *ctx)
+{
+ grn_obj buf;
+ zmq_pollitem_t items[] = {
+ { zmq_sock, 0, ZMQ_POLLIN, 0}
+ };
+ GRN_TEXT_INIT(&buf, 0);
+ while (loop) {
+ zmq_poll(items, 1, 10000);
+ if (items[0].revents & ZMQ_POLLIN) { /* always true */
+ zmq_msg_t msg;
+ if (zmq_msg_init(&msg)) {
+ print_error("cannot init zmq message.");
+ } else {
+ if (zmq_msg_recv(&msg, zmq_sock, 0)) {
+ print_error("cannot recv zmq message.");
+ } else {
+ msgpack_object obj;
+ msgpack_unpack_return ret;
+ ret = msgpack_unpack(zmq_msg_data(&msg), zmq_msg_size(&msg), NULL, mempool, &obj);
+ if (MSGPACK_UNPACK_SUCCESS == ret) {
+ /* msgpack_object_print(stdout, obj); */
+ handle_msg(&obj, ctx, &buf);
+ }
+ msgpack_zone_clear(mempool);
+ }
+ zmq_msg_close(&msg);
+ }
+ }
+ }
+ grn_obj_unlink(ctx, &buf);
+}
+
+struct _suggest_log_file {
+ FILE *fp;
+ char *path;
+ uint64_t line;
+ /* datas from one line */
+ int submit;
+ char *query;
+ uint64_t millisec;
+ char *client_id;
+ char *learn_target_name;
+ /* link list */
+ struct _suggest_log_file *next;
+};
+typedef struct _suggest_log_file suggest_log_file;
+
+#if 0
+static void
+print_log_file_list(suggest_log_file *list)
+{
+ while (list) {
+ printf("fp:%p millisec:%" PRIu64 " next:%p\n",
+ list->fp, list->millisec, list->next);
+ list = list->next;
+ }
+}
+#endif
+
+static void
+free_log_line_data(suggest_log_file *l)
+{
+ if (l->query) {
+ free(l->query);
+ l->query = NULL;
+ }
+ if (l->client_id) {
+ free(l->client_id);
+ l->client_id = NULL;
+ }
+ if (l->learn_target_name) {
+ free(l->learn_target_name);
+ l->learn_target_name = NULL;
+ }
+}
+
+#define MAX_LOG_LENGTH 0x2000
+
+static void
+read_log_line(suggest_log_file **list)
+{
+ suggest_log_file *t = *list;
+ char line_buf[MAX_LOG_LENGTH];
+ while (1) {
+ free_log_line_data(t);
+ if (fgets(line_buf, MAX_LOG_LENGTH, t->fp)) {
+ char *eol;
+ t->line++;
+ if ((eol = strrchr(line_buf, '\n'))) {
+ const char *query, *types, *client_id, *learn_target_name;
+ struct evkeyvalq get_args;
+ *eol = '\0';
+ evhttp_parse_query(line_buf, &get_args);
+ parse_keyval(NULL,
+ &get_args, &query, &types, &client_id, NULL,
+ &learn_target_name, NULL, &(t->millisec), NULL, NULL, NULL,
+ NULL);
+ if (query && client_id && learn_target_name && t->millisec) {
+ t->query = evhttp_decode_uri(query);
+ t->submit = (types && !strcmp(types, "submit"));
+ t->client_id = evhttp_decode_uri(client_id);
+ t->learn_target_name = evhttp_decode_uri(learn_target_name);
+ evhttp_clear_headers(&get_args);
+ break;
+ }
+ print_error("invalid line path:%s line:%" PRIu64,
+ t->path, t->line);
+ evhttp_clear_headers(&get_args);
+ } else {
+ /* read until new line */
+ while (1) {
+ int c = fgetc(t->fp);
+ if (c == '\n' || c == EOF) { break; }
+ }
+ }
+ } else {
+ /* terminate reading log */
+ fclose(t->fp);
+ free(t->path);
+ *list = t->next;
+ free(t);
+ break;
+ }
+ }
+}
+
+/* re-sorting by list->millisec asc with moving a head item. */
+static void
+sort_log_file_list(suggest_log_file **list)
+{
+ suggest_log_file *p, *target;
+ target = *list;
+ if (!target || !target->next || target->millisec < target->next->millisec) {
+ return;
+ }
+ *list = target->next;
+ for (p = *list; p; p = p->next) {
+ if (!p->next || target->millisec > p->next->millisec) {
+ target->next = p->next;
+ p->next = target;
+ return;
+ }
+ }
+}
+
+#define PATH_SEPARATOR '/'
+
+static suggest_log_file *
+gather_log_file(const char *dir_path, unsigned int dir_path_len)
+{
+ DIR *dir;
+ struct dirent *dirent;
+ char path[PATH_MAX + 1];
+ suggest_log_file *list = NULL;
+ if (!(dir = opendir(dir_path))) {
+ print_error("cannot open log directory.");
+ return NULL;
+ }
+ memcpy(path, dir_path, dir_path_len);
+ path[dir_path_len] = PATH_SEPARATOR;
+ while ((dirent = readdir(dir))) {
+ struct stat fstat;
+ unsigned int d_namlen, path_len;
+ if (*(dirent->d_name) == '.' && (
+ dirent->d_name[1] == '\0' ||
+ (dirent->d_name[1] == '.' && dirent->d_name[2] == '\0'))) {
+ continue;
+ }
+ d_namlen = strlen(dirent->d_name);
+ path_len = dir_path_len + 1 + d_namlen;
+ if (dir_path_len + d_namlen >= PATH_MAX) { continue; }
+ memcpy(path + dir_path_len + 1, dirent->d_name, d_namlen);
+ path[path_len] = '\0';
+ lstat(path, &fstat);
+ if (S_ISDIR(fstat.st_mode)) {
+ gather_log_file(path, path_len);
+ } else {
+ suggest_log_file *p = calloc(1, sizeof(suggest_log_file));
+ if (!(p->fp = fopen(path, "r"))) {
+ free(p);
+ } else {
+ if (list) {
+ p->next = list;
+ }
+ p->path = strdup(path);
+ list = p;
+ read_log_line(&list);
+ sort_log_file_list(&list);
+ }
+ }
+ /* print_log_file_list(list); */
+ }
+ return list;
+}
+
+static void
+load_log(grn_ctx *ctx, const char *log_dir_name)
+{
+ grn_obj buf;
+ suggest_log_file *list;
+ GRN_TEXT_INIT(&buf, 0);
+ list = gather_log_file(log_dir_name, strlen(log_dir_name));
+ while (list) {
+ /*
+ printf("file:%s line:%" PRIu64 " query:%s millisec:%" PRIu64 "\n",
+ list->path, list->line, list->query, list->millisec);
+ */
+ load_to_multi_targets(ctx, &buf,
+ list->query, strlen(list->query),
+ list->client_id, strlen(list->client_id),
+ list->learn_target_name, strlen(list->learn_target_name),
+ list->millisec,
+ list->submit);
+ read_log_line(&list);
+ sort_log_file_list(&list);
+ }
+ grn_obj_close(ctx, &buf);
+}
+
+static void
+usage(FILE *output)
+{
+ fprintf(output,
+ "Usage: groonga-suggest-learner [options...] db_path\n"
+ "options:\n"
+ " -r <recv endpoint>: recv endpoint (default: %s)\n"
+ " --receive-endpoint <recv endpoint>\n"
+ "\n"
+ " -s <send endpoint>: send endpoint (default: %s)\n"
+ " --send-endpoint <send endpoint>\n"
+ "\n"
+ " -l <log directory>: load from log files made on webserver.\n"
+ " --log-base-path <log directory>\n"
+ "\n"
+ " --log-path <path> : output logs to <path>\n"
+ " --log-level <level> : set log level to <level> (default: %d)\n"
+ " -d, --daemon : daemonize\n",
+ DEFAULT_RECV_ENDPOINT, DEFAULT_SEND_ENDPOINT,
+ GRN_LOG_DEFAULT_LEVEL);
+}
+
+static void
+signal_handler(int sig)
+{
+ loop = 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ run_mode mode = RUN_MODE_NONE;
+ int n_processed_args;
+ const char *recv_endpoint = DEFAULT_RECV_ENDPOINT;
+ const char *send_endpoint = DEFAULT_SEND_ENDPOINT;
+ const char *log_base_path = NULL;
+ const char *db_path = NULL;
+
+ /* parse options */
+ {
+ int flags = mode;
+ const char *log_path = NULL;
+ const char *log_level = NULL;
+ static grn_str_getopt_opt opts[] = {
+ {'r', "receive-endpoint", NULL, 0, GETOPT_OP_NONE},
+ {'s', "send-endpoint", NULL, 0, GETOPT_OP_NONE},
+ {'l', "log-base-path", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "log-path", NULL, 0, GETOPT_OP_NONE},
+ {'\0', "log-level", NULL, 0, GETOPT_OP_NONE},
+ {'d', "daemon", NULL, RUN_MODE_DAEMON, GETOPT_OP_UPDATE},
+ {'h', "help", NULL, RUN_MODE_USAGE, GETOPT_OP_UPDATE},
+ {'\0', NULL, NULL, 0, 0}
+ };
+ opts[0].arg = &recv_endpoint;
+ opts[1].arg = &send_endpoint;
+ opts[2].arg = &log_base_path;
+ opts[3].arg = &log_path;
+ opts[4].arg = &log_level;
+
+ n_processed_args = grn_str_getopt(argc, argv, opts, &flags);
+
+ if (log_path) {
+ grn_default_logger_set_path(log_path);
+ }
+
+ if (log_level) {
+ const char * const end = log_level + strlen(log_level);
+ const char *rest = NULL;
+ const int value = grn_atoi(log_level, end, &rest);
+ if (end != rest || value < 0 || value > 9) {
+ fprintf(stderr, "invalid log level: <%s>\n", log_level);
+ return EXIT_FAILURE;
+ }
+ grn_default_logger_set_max_level(value);
+ }
+
+ mode = (flags & RUN_MODE_MASK);
+
+ if (mode & RUN_MODE_USAGE) {
+ usage(stdout);
+ return EXIT_SUCCESS;
+ }
+
+ if ((n_processed_args < 0) ||
+ (argc - n_processed_args) != 1) {
+ usage(stderr);
+ }
+
+ db_path = argv[n_processed_args];
+ }
+
+ /* main */
+ {
+ grn_ctx *ctx;
+ msgpack_zone *mempool;
+
+ if (mode == RUN_MODE_DAEMON) {
+ daemonize();
+ }
+
+ grn_init();
+
+ ctx = grn_ctx_open(0);
+ if (!(grn_db_open(ctx, db_path))) {
+ print_error("cannot open database.");
+ } else {
+ if (log_base_path) {
+ /* loading log mode */
+ load_log(ctx, log_base_path);
+ } else {
+ /* zeromq/msgpack recv mode */
+ if (!(mempool = msgpack_zone_new(MSGPACK_ZONE_CHUNK_SIZE))) {
+ print_error("cannot create msgpack zone.");
+ } else {
+ void *zmq_ctx, *zmq_recv_sock;
+ if (!(zmq_ctx = zmq_init(1))) {
+ print_error("cannot create zmq context.");
+ } else {
+ if (!(zmq_recv_sock = zmq_socket(zmq_ctx, ZMQ_SUB))) {
+ print_error("cannot create zmq_socket.");
+ } else if (zmq_bind(zmq_recv_sock, recv_endpoint)) {
+ print_error("cannot bind zmq_socket.");
+ } else {
+ send_thd_data thd;
+
+ signal(SIGTERM, signal_handler);
+ signal(SIGINT, signal_handler);
+ signal(SIGQUIT, signal_handler);
+
+ zmq_setsockopt(zmq_recv_sock, ZMQ_SUBSCRIBE, "", 0);
+ thd.db_path = db_path;
+ thd.send_endpoint = send_endpoint;
+ thd.zmq_ctx = zmq_ctx;
+
+ if (pthread_create(&(thd.thd), NULL, send_to_httpd, &thd)) {
+ print_error("error in pthread_create() for sending datas.");
+ }
+ recv_event_loop(mempool, zmq_recv_sock, ctx);
+ if (pthread_join(thd.thd, NULL)) {
+ print_error("error in pthread_join() for waiting completion of sending data.");
+ }
+ }
+ zmq_term(zmq_ctx);
+ }
+ msgpack_zone_free(mempool);
+ }
+ }
+ }
+ grn_obj_close(ctx, grn_ctx_db(ctx));
+ grn_ctx_fin(ctx);
+ grn_fin();
+ }
+ return 0;
+}
diff --git a/storage/mroonga/vendor/groonga/src/suggest/httpd_sources.am b/storage/mroonga/vendor/groonga/src/suggest/httpd_sources.am
new file mode 100644
index 00000000000..a09328d51e1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/suggest/httpd_sources.am
@@ -0,0 +1,3 @@
+groonga_suggest_httpd_SOURCES = \
+ groonga_suggest_httpd.c \
+ zmq_compatible.h
diff --git a/storage/mroonga/vendor/groonga/src/suggest/learner_sources.am b/storage/mroonga/vendor/groonga/src/suggest/learner_sources.am
new file mode 100644
index 00000000000..03b49087116
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/suggest/learner_sources.am
@@ -0,0 +1,3 @@
+groonga_suggest_learner_SOURCES = \
+ groonga_suggest_learner.c \
+ zmq_compatible.h
diff --git a/storage/mroonga/vendor/groonga/src/suggest/util.c b/storage/mroonga/vendor/groonga/src/suggest/util.c
new file mode 100644
index 00000000000..f6a717b8d7a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/suggest/util.c
@@ -0,0 +1,215 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2010- Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "util.h"
+
+#define DEFAULT_FREQUENCY_THRESHOLD 100
+#define DEFAULT_CONDITIONAL_PROBABILITY_THRESHOLD 0.2
+
+int
+print_error(const char *format, ...)
+{
+ int r;
+ va_list l;
+
+ va_start(l, format);
+ vfprintf(stderr, format, l);
+ r = fprintf(stderr, "\n");
+ fflush(stderr);
+ va_end(l);
+
+ return r;
+}
+
+int
+daemonize(void)
+{
+ pid_t pid;
+
+ switch (fork()) {
+ case 0:
+ break;
+ case -1:
+ print_error("fork failed.");
+ return -1;
+ default:
+ wait(NULL);
+ _exit(0);
+ }
+ switch ((pid = fork())) {
+ case 0:
+ break;
+ case -1:
+ perror("fork");
+ return -1;
+ default:
+ fprintf(stderr, "%d\n", pid);
+ _exit(0);
+ }
+ {
+ int null_fd = open("/dev/null", O_RDWR, 0);
+ if (null_fd != -1) {
+ dup2(null_fd, 0);
+ dup2(null_fd, 1);
+ dup2(null_fd, 2);
+ if (null_fd > 2) { close(null_fd); }
+ }
+ }
+ return 1;
+}
+
+static uint64_t
+atouint64_t(const char *s)
+{
+ uint64_t r;
+ for (r = 0; *s; s++) {
+ r *= 10;
+ r += (*s - '0');
+ }
+ return r;
+}
+
+void
+parse_keyval(grn_ctx *ctx,
+ struct evkeyvalq *get_args,
+ const char **query, const char **types,
+ const char **client_id, const char **target_name,
+ const char **learn_target_name,
+ const char **callback,
+ uint64_t *millisec,
+ int *frequency_threshold,
+ double *conditional_probability_threshold,
+ int *limit,
+ grn_obj *pass_through_parameters)
+{
+ struct evkeyval *get;
+
+ if (query) { *query = NULL; }
+ if (types) { *types = NULL; }
+ if (client_id) { *client_id = NULL; }
+ if (target_name) { *target_name = NULL; }
+ if (learn_target_name) { *learn_target_name = NULL; }
+ if (callback) { *callback = NULL; }
+ if (millisec) { *millisec = 0; }
+ if (frequency_threshold) {
+ *frequency_threshold = DEFAULT_FREQUENCY_THRESHOLD;
+ }
+ if (conditional_probability_threshold) {
+ *conditional_probability_threshold = DEFAULT_CONDITIONAL_PROBABILITY_THRESHOLD;
+ }
+ if (limit) { *limit = -1; }
+
+ TAILQ_FOREACH(get, get_args, next) {
+ grn_bool is_pass_through_parameter = GRN_FALSE;
+ size_t key_length;
+
+ key_length = strlen(get->key);
+ switch (key_length) {
+ case 0:
+ break;
+ case 1:
+ switch(get->key[0]) {
+ case 'q':
+ if (query) {
+ *query = get->value;
+ }
+ break;
+ case 't':
+ /* TODO: check types */
+ if (types) {
+ *types = get->value;
+ }
+ break;
+ case 'i':
+ if (client_id) {
+ *client_id = get->value;
+ }
+ break;
+ case 's':
+ if (millisec) {
+ *millisec = atouint64_t(get->value);
+ }
+ break;
+ case 'n':
+ /* TODO: check target_name */
+ if (target_name) {
+ *target_name = get->value;
+ }
+ break;
+ case 'l':
+ if (learn_target_name) {
+ *learn_target_name = get->value;
+ }
+ break;
+ case 'h':
+ if (frequency_threshold) {
+ *frequency_threshold = atoi(get->value);
+ }
+ break;
+ case 'p':
+ if (conditional_probability_threshold) {
+ *conditional_probability_threshold = strtod(get->value, NULL);
+ }
+ break;
+ case 'm':
+ if (limit) {
+ *limit = atoi(get->value);
+ }
+ break;
+ default:
+ is_pass_through_parameter = GRN_TRUE;
+ break;
+ }
+ break;
+ default:
+ switch (get->key[0]) {
+ case 'c':
+ if (!strcmp(get->key, "callback")) {
+ if (callback) {
+ *callback = get->value;
+ }
+ } else {
+ is_pass_through_parameter = GRN_TRUE;
+ }
+ break;
+ default:
+ is_pass_through_parameter = GRN_TRUE;
+ }
+ }
+
+ if (is_pass_through_parameter && pass_through_parameters) {
+ if (GRN_TEXT_LEN(pass_through_parameters) > 0) {
+ GRN_TEXT_PUTS(ctx, pass_through_parameters, "&");
+ }
+ grn_text_urlenc(ctx, pass_through_parameters, get->key, strlen(get->key));
+ GRN_TEXT_PUTS(ctx, pass_through_parameters, "=");
+ grn_text_urlenc(ctx, pass_through_parameters,
+ get->value, strlen(get->value));
+ }
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/src/suggest/util.h b/storage/mroonga/vendor/groonga/src/suggest/util.h
new file mode 100644
index 00000000000..468fe1045ba
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/suggest/util.h
@@ -0,0 +1,40 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2010- Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_SUGGEST_UTIL_H
+#define GRN_SUGGEST_UTIL_H
+
+#include <sys/queue.h>
+#include <event.h>
+#include <stdint.h>
+
+#include <groonga.h>
+
+int print_error(const char *format, ...);
+int daemonize(void);
+void parse_keyval(grn_ctx *ctx,
+ struct evkeyvalq *get_args,
+ const char **query, const char **types,
+ const char **client_id, const char **target_name,
+ const char **learn_target_name,
+ const char **callback,
+ uint64_t *millisec,
+ int *frequency_threshold,
+ double *conditional_probability_threshold,
+ int *limit,
+ grn_obj *pass_through_parameters);
+
+#endif /* GRN_SUGGEST_UTIL_H */
diff --git a/storage/mroonga/vendor/groonga/src/suggest/util_sources.am b/storage/mroonga/vendor/groonga/src/suggest/util_sources.am
new file mode 100644
index 00000000000..d4b74860e2b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/suggest/util_sources.am
@@ -0,0 +1,3 @@
+libutil_la_SOURCES = \
+ util.c \
+ util.h
diff --git a/storage/mroonga/vendor/groonga/src/suggest/zmq_compatible.h b/storage/mroonga/vendor/groonga/src/suggest/zmq_compatible.h
new file mode 100644
index 00000000000..28bc035a46a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/src/suggest/zmq_compatible.h
@@ -0,0 +1,33 @@
+/* -*- c-basic-offset: 2 -*- */
+/* Copyright(C) 2013 Brazil
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License version 2.1 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#ifndef GRN_SUGGEST_ZMQ_COMPATIBLE_H
+#define GRN_SUGGEST_ZMQ_COMPATIBLE_H
+
+#include <zmq.h>
+
+#ifndef ZMQ_SNDHWM
+# define ZMQ_SNDHWM ZMQ_HWM
+#endif
+
+#if ZMQ_VERSION_MAJOR == 2
+# define zmq_msg_send(message, socket, flags) \
+ zmq_send((socket), (message), (flags))
+# define zmq_msg_recv(message, socket, flags) \
+ zmq_recv((socket), (message), (flags))
+#endif
+
+#endif /* GRN_SUGGEST_ZMQ_COMPATIBLE_H */
diff --git a/storage/mroonga/vendor/groonga/tools/Makefile.am b/storage/mroonga/vendor/groonga/tools/Makefile.am
new file mode 100644
index 00000000000..00e14ea5da5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/tools/Makefile.am
@@ -0,0 +1,6 @@
+noinstall_ruby_scripts = \
+ groonga-memory-leak-checker.rb \
+ prepare-sphinx-html.rb
+
+EXTRA_DIST = \
+ $(noinstall_ruby_scripts)
diff --git a/storage/mroonga/vendor/groonga/tools/groonga-memory-leak-checker.rb b/storage/mroonga/vendor/groonga/tools/groonga-memory-leak-checker.rb
new file mode 100755
index 00000000000..4e4e10a4371
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/tools/groonga-memory-leak-checker.rb
@@ -0,0 +1,93 @@
+#!/usr/bin/env ruby
+
+unless respond_to?(:spawn, true)
+ puts("Ruby 1.9 is required.")
+ exit(false)
+end
+
+require 'ostruct'
+require 'optparse'
+require 'tempfile'
+require 'time'
+
+options = OpenStruct.new
+options.groonga = "groonga"
+options.show_result = false
+options.report_progress = true
+
+option_parser = OptionParser.new do |parser|
+ parser.banner += " DATABASE COMMAND_FILE1 ..."
+
+ parser.on("--groonga=PATH",
+ "Use PATH as groonga command path") do |path|
+ options.groonga = path
+ end
+
+ parser.on("--[no-]show-result",
+ "Show result of command") do |boolean|
+ options.show_result = boolean
+ end
+
+ parser.on("--[no-]report-progress",
+ "Report progress") do |boolean|
+ options.report_progress = boolean
+ end
+end
+
+database, *command_files = option_parser.parse!(ARGV)
+if database.nil?
+ puts(option_parser)
+ exit(false)
+end
+
+i = 0
+command_files.each do |path|
+ File.open(path) do |file|
+ file.each_line do |command|
+ if options.report_progress
+ i += 1
+ puts("#{Time.now.iso8601}: #{i} commands done.") if (i % 1000).zero?
+ end
+ command = command.chomp
+ base_name = File.basename($0, ".*")
+ log = Tempfile.new("#{base_name}-log")
+ command_file = Tempfile.new("#{base_name}-command")
+ command_file.puts(command)
+ command_file.close
+ command_line = [options.groonga,
+ "--log-path", log.path,
+ "--file", command_file.path]
+ command_file << "-n" unless File.exist?(database)
+ command_line << database
+ result = Tempfile.new("#{base_name}-result")
+ pid = spawn(*command_line, :out => result.fileno)
+ pid, status = Process.waitpid2(pid)
+ unless status.success?
+ puts("failed to run: (#{status.exitstatus}): " +
+ "[#{command_line.join(' ')}]")
+ puts("command:")
+ puts(command)
+ puts("result:")
+ result.open
+ puts(result.read)
+ next
+ # exit(false)
+ end
+ if options.show_result
+ result.open
+ puts(result.read)
+ end
+ log.open
+ log.each_line do |log_line|
+ case log_line
+ when /grn_fin \((\d+)\)/
+ n_unfreed_allocations = $1.to_i
+ unless n_unfreed_allocations.zero?
+ puts("maybe memory leak: #{n_unfreed_allocations}: <#{command}>")
+ exit(false)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/tools/groonga-suggest-httpd-client.rb b/storage/mroonga/vendor/groonga/tools/groonga-suggest-httpd-client.rb
new file mode 100755
index 00000000000..ac4c30778e6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/tools/groonga-suggest-httpd-client.rb
@@ -0,0 +1,181 @@
+#!/usr/bin/env ruby
+#
+# Copyright(C) 2011 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+require "optparse"
+require "cool.io"
+require "digest"
+
+class Terms
+ def initialize
+ @sources = []
+ end
+
+ def next
+ until @sources.empty?
+ source = @sources.first
+ term = source.next
+ @sources.shift if source.empty?
+ break if term
+ end
+ term
+ end
+
+ def empty?
+ @sources.all?(&:empty?)
+ end
+
+ def add_source_file(path)
+ @sources << InputSource.new(File.open(path))
+ end
+
+ def add_source_input(input)
+ @sources << InputSource.new(input)
+ end
+
+ class InputSource
+ def initialize(source)
+ @source = source
+ @lines = @source.each_line
+ @look_ahead_term = nil
+ end
+
+ def next
+ if @look_ahead_term
+ term = @look_ahead_term
+ @look_ahead_term = nil
+ else
+ loop do
+ term = @lines.next.strip
+ break unless term.empty?
+ end
+ end
+ term
+ rescue StopIteration
+ @lines = nil
+ @source.close
+ @source = nil
+ nil
+ end
+
+ def empty?
+ if @source.nil?
+ true
+ else
+ @look_ahead_term ||= self.next
+ @look_ahead_term.nil?
+ end
+ end
+ end
+end
+
+class GroongaSuggestHTTPDClient < Coolio::HttpClient
+ def initialize(socket, dataset, id, term)
+ super(socket)
+ @dataset = dataset
+ @id = id
+ @term = term
+ @callbacks = []
+ end
+
+ def request
+ query = {}
+ query["q"] = @term.dup.force_encoding("ASCII-8BIT")
+ query["s"] = (Time.now.to_f * 1_000).round.to_s
+ query["i"] = @id
+ query["t"] = "submit" if rand(10).zero?
+ query["l"] = @dataset
+ super("GET", "/", :query => query)
+ end
+
+ def on_body_data(data)
+ end
+
+ def on_request_complete(&block)
+ if block
+ @callbacks << block
+ else
+ @callbacks.each do |callback|
+ callback.call(self)
+ end
+ end
+ end
+
+ def on_error(reason)
+ close
+ $stderr.puts("Error: #{reason}")
+ end
+end
+
+n_connections = 100
+host = "localhost"
+port = 8080
+terms = Terms.new
+parser = OptionParser.new
+parser.banner += " DATASET"
+parser.on("--host=HOST",
+ "Use HOST as groonga suggest HTTPD host.",
+ "(#{host})") do |_host|
+ host = _host
+end
+parser.on("--port=PORT", Integer,
+ "Use PORT as groonga suggest HTTPD port.",
+ "(#{port})") do |_port|
+ port = _port
+end
+parser.on("--n-connections=N", Integer,
+ "Use N connections.",
+ "(#{n_connections})") do |n|
+ n_connections = n
+end
+parser.on("--terms=PATH",
+ "Use terns in PATH.",
+ "(none)") do |path|
+ terms.add_source_file(path)
+end
+
+parser.parse!
+if ARGV.size != 1
+ puts(parser)
+ exit(false)
+end
+dataset = ARGV.shift
+
+if terms.empty? and !$stdin.tty?
+ terms.add_source_input($stdin)
+end
+
+if terms.empty?
+ puts("no terms")
+ exit(false)
+end
+
+loop = Coolio::Loop.default
+run_client = lambda do |id|
+ term = terms.next
+ return if term.nil?
+ client = GroongaSuggestHTTPDClient.connect(host, port, dataset, id, term)
+ client.on_request_complete do
+ run_client.call(id)
+ end
+ client.attach(loop)
+ client.request
+end
+n_connections.times do |i|
+ id = Digest::SHA2.hexdigest(Time.now.to_f.to_s)
+ run_client.call(id)
+end
+loop.run
diff --git a/storage/mroonga/vendor/groonga/tools/prepare-sphinx-html.rb b/storage/mroonga/vendor/groonga/tools/prepare-sphinx-html.rb
new file mode 100755
index 00000000000..5396d267bfd
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/tools/prepare-sphinx-html.rb
@@ -0,0 +1,183 @@
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
+
+if ARGV.size != 2
+ puts "Usage: #{$0} SOURCE_DIR DEST_DIR"
+ exit(false)
+end
+
+require 'pathname'
+
+def fix_link(text, extension, language)
+ send("fix_#{extension}_link", text, language)
+end
+
+def fix_link_path(text)
+ text.gsub(/\b_(sources|static|images)\b/, '\1')
+end
+
+def fix_language_link(url, language)
+ url.gsub(/\A((?:\.\.\/){2,})([a-z]{2})\/html\//) do
+ relative_base_path = $1
+ link_language = $2
+ close_quote = $3
+ if language == "en"
+ relative_base_path = relative_base_path.gsub(/\A\.\.\//, '')
+ end
+ if link_language != "en"
+ relative_base_path += "#{link_language}/"
+ end
+ "#{relative_base_path}docs/"
+ end
+end
+
+def fix_html_link(html, language)
+ html = html.gsub(/(href|src)="(.+?)"/) do
+ attribute = $1
+ link = $2
+ link = fix_link_path(link)
+ link = fix_language_link(link, language)
+ "#{attribute}=\"#{link}\""
+ end
+ html.gsub(/(id="top-link" href=)"(.+?)"/) do
+ prefix = $1
+ top_path = $2.gsub(/\/index\.html\z/, '/')
+ top_path = "./" if ["index.html", "#"].include?(top_path)
+ "#{prefix}\"#{top_path}../\""
+ end
+end
+
+def add_language_annotation_to_source_label(html, language)
+ return html unless language == "ja"
+ html.gsub(/>(ソースコードを表示)</) do
+ label = $1
+ ">#{label}(英語)<"
+ end
+end
+
+def fix_js_link(js, language)
+ fix_link_path(js)
+end
+
+LANGUAGE_TO_LOCALE = {
+ "ja" => "ja_JP",
+ "en" => "en_US",
+}
+
+def insert_facebook_html_header(html)
+ html.gsub(/<\/head>/) do
+ <<-HTML
+ <meta property="fb:page_id" content="201193596592346" /><!-- groonga -->
+ <meta property="fb:admins" content="664204556" /><!-- kouhei.sutou -->
+ <meta property="og:type" content="product" />
+ <meta property="og:image" content="http://groonga.org/images/logos/groonga-icon-full-size.png" />
+ <meta property="og:site_name" content="groonga" />
+
+ <link rel="stylesheet" href="/css/sphinx.css" type="text/css" />
+ </head>
+ HTML
+ end
+end
+
+def insert_facebook_html_fb_root(html)
+ html.gsub(/<body>/) do
+ <<-HTML
+ <body>
+ <div id="fb-root"></div>
+ HTML
+ end
+end
+
+def insert_facebook_html_buttons(html)
+ html.gsub(/(<div class="other-language-links">)/) do
+ <<-HTML
+ <div class="facebook-buttons">
+ <fb:like href="http://www.facebook.com/pages/groonga/201193596592346"
+ layout="standard"
+ width="290"></fb:like>
+ </div>
+ #{$1}
+ HTML
+ end
+end
+
+def insert_facebook_html_footer(html, language)
+ locale = LANGUAGE_TO_LOCALE[language]
+ raise "unknown locale for language #{language.inspect}" if locale.nil?
+ html.gsub(/<\/body>/) do
+ <<-HTML
+ <script src="http://connect.facebook.net/#{locale}/all.js"></script>
+
+ <script>
+ FB.init({
+ appId : null,
+ status : true, // check login status
+ cookie : true, // enable cookies to allow the server to access the session
+ xfbml : true // parse XFBML
+ });
+ </script>
+ </body>
+ HTML
+ end
+end
+
+def insert_facebook_html(html, language)
+ html = insert_facebook_html_header(html)
+ html = insert_facebook_html_fb_root(html)
+ html = insert_facebook_html_buttons(html)
+ html = insert_facebook_html_footer(html, language)
+ html
+end
+
+source_dir, dest_dir = ARGV
+
+source_dir = Pathname.new(source_dir)
+dest_dir = Pathname.new(dest_dir)
+
+language_dirs = []
+source_dir.each_entry do |top_level_path|
+ language_dirs << top_level_path if /\A[a-z]{2}\z/ =~ top_level_path.to_s
+end
+
+language_dirs.each do |language_dir|
+ language = language_dir.to_s
+ language_source_dir = source_dir + language_dir + "html"
+ language_dest_dir = dest_dir + language_dir
+ language_source_dir.find do |source_path|
+ relative_path = source_path.relative_path_from(language_source_dir)
+ dest_path = language_dest_dir + relative_path
+ if source_path.directory?
+ dest_path.mkpath
+ else
+ case source_path.extname
+ when ".html", ".js"
+ content = source_path.read
+ extension = source_path.extname.gsub(/\A\./, '')
+ content = fix_link(content, extension, language)
+ if extension == "html"
+ content = insert_facebook_html(content, language)
+ content = add_language_annotation_to_source_label(content, language)
+ end
+ dest_path.open("wb") do |dest|
+ dest.print(content.strip)
+ end
+ FileUtils.touch(dest_path, :mtime => source_path.mtime)
+ else
+ case source_path.basename.to_s
+ when ".buildinfo"
+ # ignore
+ else
+ FileUtils.cp(source_path, dest_path, :preserve => true)
+ end
+ end
+ end
+ end
+end
+
+dest_dir.find do |dest_path|
+ if dest_path.directory? and /\A_/ =~ dest_path.basename.to_s
+ normalized_dest_path = dest_path + ".."
+ normalized_dest_path += dest_path.basename.to_s.gsub(/\A_/, '')
+ FileUtils.mv(dest_path, normalized_dest_path)
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/tools/travis-before-script.sh b/storage/mroonga/vendor/groonga/tools/travis-before-script.sh
new file mode 100755
index 00000000000..943f41b0013
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/tools/travis-before-script.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+set -e
+
+git submodule update --init --depth 1
+
+case "${BUILD_TOOL}" in
+ autotools)
+ ./autogen.sh
+
+ configure_args=""
+ #if [ "$CC" = "clang" ]; then
+ configure_args="${configure_args} --enable-debug"
+ #fi
+ if [ "$ENABLE_MRUBY" = "yes" ]; then
+ configure_args="${configure_args} --with-ruby --enable-mruby"
+ fi
+
+ ./configure --with-ruby ${configure_args}
+ ;;
+ cmake)
+ cmake_args=""
+ cmake_args="${cmake_args} -DGRN_WITH_DEBUG=yes"
+ if [ "$ENABLE_MRUBY" = "yes" ]; then
+ cmake_args="${cmake_args} -DGRN_WITH_MRUBY=yes"
+ fi
+
+ cmake . ${cmake_args}
+ ;;
+esac
+
+n_processors="$(grep '^processor' /proc/cpuinfo | wc -l)"
+make -j${n_processors} > /dev/null
diff --git a/storage/mroonga/vendor/groonga/tools/travis-script.sh b/storage/mroonga/vendor/groonga/tools/travis-script.sh
new file mode 100755
index 00000000000..7931903d419
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/tools/travis-script.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+set -e
+
+case "${BUILD_TOOL}" in
+ autotools)
+ test/unit/run-test.sh
+ test/command/run-test.sh
+ # test/command/run-test.sh --interface http
+ # test/command/run-test.sh --interface http --testee groonga-httpd
+ ;;
+ cmake)
+ test/command/run-test.sh
+ ;;
+esac
diff --git a/storage/mroonga/vendor/groonga/vendor/CMakeLists.txt b/storage/mroonga/vendor/groonga/vendor/CMakeLists.txt
new file mode 100644
index 00000000000..30c9befebb6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/CMakeLists.txt
@@ -0,0 +1,16 @@
+# Copyright(C) 2013 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+add_subdirectory(mruby)
diff --git a/storage/mroonga/vendor/groonga/vendor/Makefile.am b/storage/mroonga/vendor/groonga/vendor/Makefile.am
new file mode 100644
index 00000000000..77ab6b3e31f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/Makefile.am
@@ -0,0 +1,17 @@
+NGINX_DIR = nginx-$(NGINX_VERSION)
+
+SUBDIRS = \
+ onigmo \
+ mruby
+
+EXTRA_DIST = \
+ $(NGINX_DIR) \
+ CMakeLists.txt \
+ plugins/CMakeLists.txt
+
+dist-hook:
+ GIT_DIR=$(srcdir)/mruby-source/.git git archive --format=tar HEAD | \
+ tar xf - -C $(distdir)/mruby-source
+ $(MKDIR_P) $(distdir)/onigmo-source
+ GIT_DIR=$(srcdir)/onigmo-source/.git git archive --format=tar HEAD | \
+ tar xf - -C $(distdir)/onigmo-source
diff --git a/storage/mroonga/vendor/groonga/vendor/mruby/CMakeLists.txt b/storage/mroonga/vendor/groonga/vendor/mruby/CMakeLists.txt
new file mode 100644
index 00000000000..9f4aad4c32b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/mruby/CMakeLists.txt
@@ -0,0 +1,35 @@
+# Copyright(C) 2013 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+add_definitions(
+ -DDISABLE_GEMS
+ )
+
+include_directories(
+ "${CMAKE_CURRENT_SOURCE_DIR}/../mruby-${MRUBY_VERSION}/include"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../mruby-${MRUBY_VERSION}/src"
+ )
+
+if(GRN_WITH_MRUBY)
+ read_file_list("${CMAKE_CURRENT_SOURCE_DIR}/sources.am" MRUBY_SOURCES)
+ add_library(mruby OBJECT ${MRUBY_SOURCES})
+ set_source_files_properties(${MRUBY_SOURCES}
+ PROPERTIES
+ COMPILE_FLAGS "${GRN_C_COMPILE_FLAGS}")
+ set_target_properties(
+ mruby
+ PROPERTIES
+ POSITION_INDEPENDENT_CODE ON)
+endif()
diff --git a/storage/mroonga/vendor/groonga/vendor/mruby/Makefile.am b/storage/mroonga/vendor/groonga/vendor/mruby/Makefile.am
new file mode 100644
index 00000000000..870f1d3f9ab
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/mruby/Makefile.am
@@ -0,0 +1,60 @@
+EXTRA_DIST = \
+ build_config.rb
+
+DEFAULT_INCLUDES = \
+ -I$(srcdir)/../mruby-source/include \
+ -I$(srcdir)/../mruby-source/src
+
+if WITH_MRUBY
+noinst_LTLIBRARIES = libmruby.la
+
+AM_CFLAGS = \
+ -I$(srcdir)/../onigmo-source
+
+include sources.am
+
+BUILT_SOURCES =
+
+MRUBY_BUILD_DIR = $(abs_top_builddir)/vendor/mruby-build
+
+BUILT_SOURCES += parse.c
+libmruby_la_SOURCES += parse.c
+parse.c: mruby-build.timestamp
+ cp $(MRUBY_BUILD_DIR)/host/src/y.tab.c $@
+
+BUILT_SOURCES += mrblib.c
+libmruby_la_SOURCES += mrblib.c
+mrblib.c: mruby-build.timestamp
+ cp $(MRUBY_BUILD_DIR)/host/mrblib/mrblib.c $@
+
+BUILT_SOURCES += mrbgems_init.c
+libmruby_la_SOURCES += mrbgems_init.c
+mrbgems_init.c: mruby-build.timestamp
+ ( \
+ cat $(MRUBY_BUILD_DIR)/host/mrbgems/gem_init.c; \
+ cat $(MRUBY_BUILD_DIR)/host/mrbgems/*/gem_init.c; \
+ ) > $@
+
+BUILT_SOURCES += mruby_onig_regexp.c
+libmruby_la_SOURCES += mruby_onig_regexp.c
+MRUBY_ONIG_REGEXP_DIR = $(MRUBY_BUILD_DIR)/mrbgems/mruby-onig-regexp
+mruby_onig_regexp.c: mruby-build.timestamp
+ cp $(MRUBY_ONIG_REGEXP_DIR)/src/mruby_onig_regexp.c $@
+
+MRUBY_CONFIG = $(abs_srcdir)/build_config.rb
+mruby-build.timestamp: build_config.rb
+ rm -rf $(MRUBY_BUILD_DIR)
+ cd $(srcdir)/../mruby-source && \
+ $(RUBY) minirake \
+ MRUBY_BUILD_DIR=$(MRUBY_BUILD_DIR) \
+ MRUBY_CONFIG=$(MRUBY_CONFIG)
+ touch $@
+
+CLEANFILES = *.gcno *gcda
+endif
+
+update:
+ cd "$(srcdir)/../mruby-source" && \
+ (git checkout master && git pull --rebase)
+ cd "$(srcdir)" && \
+ ./update.rb build_config.rb ../mruby-source > sources.am
diff --git a/storage/mroonga/vendor/groonga/vendor/mruby/build_config.rb b/storage/mroonga/vendor/groonga/vendor/mruby/build_config.rb
new file mode 100644
index 00000000000..2380c04789d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/mruby/build_config.rb
@@ -0,0 +1,33 @@
+MRuby::Build.new do |conf|
+ if ENV['VisualStudioVersion'] || ENV['VSINSTALLDIR']
+ toolchain :visualcpp
+ else
+ toolchain :gcc
+ end
+
+ enable_debug
+
+ conf.gem :core => "mruby-sprintf"
+ conf.gem :core => "mruby-print"
+ conf.gem :core => "mruby-math"
+ conf.gem :core => "mruby-time"
+ conf.gem :core => "mruby-struct"
+ conf.gem :core => "mruby-enum-ext"
+ conf.gem :core => "mruby-string-ext"
+ conf.gem :core => "mruby-numeric-ext"
+ conf.gem :core => "mruby-array-ext"
+ conf.gem :core => "mruby-hash-ext"
+ conf.gem :core => "mruby-range-ext"
+ conf.gem :core => "mruby-proc-ext"
+ conf.gem :core => "mruby-symbol-ext"
+ conf.gem :core => "mruby-random"
+ conf.gem :core => "mruby-object-ext"
+ conf.gem :core => "mruby-objectspace"
+ conf.gem :core => "mruby-fiber"
+ conf.gem :core => "mruby-enumerator"
+ conf.gem :core => "mruby-enum-lazy"
+ conf.gem :core => "mruby-toplevel-ext"
+ conf.gem :core => "mruby-kernel-ext"
+
+ conf.gem :github => "mattn/mruby-onig-regexp"
+end
diff --git a/storage/mroonga/vendor/groonga/vendor/mruby/sources.am b/storage/mroonga/vendor/groonga/vendor/mruby/sources.am
new file mode 100644
index 00000000000..b470d58b803
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/mruby/sources.am
@@ -0,0 +1,56 @@
+libmruby_la_SOURCES = \
+ ../mruby-source/src/array.c \
+ ../mruby-source/src/backtrace.c \
+ ../mruby-source/src/class.c \
+ ../mruby-source/src/codegen.c \
+ ../mruby-source/src/compar.c \
+ ../mruby-source/src/crc.c \
+ ../mruby-source/src/debug.c \
+ ../mruby-source/src/dump.c \
+ ../mruby-source/src/enum.c \
+ ../mruby-source/src/error.c \
+ ../mruby-source/src/error.h \
+ ../mruby-source/src/etc.c \
+ ../mruby-source/src/gc.c \
+ ../mruby-source/src/hash.c \
+ ../mruby-source/src/init.c \
+ ../mruby-source/src/kernel.c \
+ ../mruby-source/src/load.c \
+ ../mruby-source/src/mrb_throw.h \
+ ../mruby-source/src/node.h \
+ ../mruby-source/src/numeric.c \
+ ../mruby-source/src/object.c \
+ ../mruby-source/src/opcode.h \
+ ../mruby-source/src/pool.c \
+ ../mruby-source/src/print.c \
+ ../mruby-source/src/proc.c \
+ ../mruby-source/src/range.c \
+ ../mruby-source/src/state.c \
+ ../mruby-source/src/string.c \
+ ../mruby-source/src/symbol.c \
+ ../mruby-source/src/value_array.h \
+ ../mruby-source/src/variable.c \
+ ../mruby-source/src/version.c \
+ ../mruby-source/src/vm.c \
+ ../mruby-source/mrbgems/mruby-sprintf/src/kernel.c \
+ ../mruby-source/mrbgems/mruby-sprintf/src/sprintf.c \
+ ../mruby-source/mrbgems/mruby-print/src/print.c \
+ ../mruby-source/mrbgems/mruby-math/src/math.c \
+ ../mruby-source/mrbgems/mruby-time/src/time.c \
+ ../mruby-source/mrbgems/mruby-struct/src/struct.c \
+ ../mruby-source/mrbgems/mruby-string-ext/src/string.c \
+ ../mruby-source/mrbgems/mruby-numeric-ext/src/numeric_ext.c \
+ ../mruby-source/mrbgems/mruby-array-ext/src/array.c \
+ ../mruby-source/mrbgems/mruby-hash-ext/src/hash-ext.c \
+ ../mruby-source/mrbgems/mruby-range-ext/src/range.c \
+ ../mruby-source/mrbgems/mruby-proc-ext/src/proc.c \
+ ../mruby-source/mrbgems/mruby-proc-ext/test/proc.c \
+ ../mruby-source/mrbgems/mruby-symbol-ext/src/symbol.c \
+ ../mruby-source/mrbgems/mruby-random/src/mt19937ar.c \
+ ../mruby-source/mrbgems/mruby-random/src/mt19937ar.h \
+ ../mruby-source/mrbgems/mruby-random/src/random.c \
+ ../mruby-source/mrbgems/mruby-random/src/random.h \
+ ../mruby-source/mrbgems/mruby-object-ext/src/object.c \
+ ../mruby-source/mrbgems/mruby-objectspace/src/mruby_objectspace.c \
+ ../mruby-source/mrbgems/mruby-fiber/src/fiber.c \
+ ../mruby-source/mrbgems/mruby-kernel-ext/src/kernel.c
diff --git a/storage/mroonga/vendor/groonga/vendor/mruby/update.rb b/storage/mroonga/vendor/groonga/vendor/mruby/update.rb
new file mode 100755
index 00000000000..888c3d3066d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/mruby/update.rb
@@ -0,0 +1,88 @@
+#!/usr/bin/env ruby
+
+if ARGV.size != 2
+ puts("Usage: #{$0} BUILD_COFNIG.RB MRUBY_SOURCE_DIR")
+ exit(false)
+end
+
+require "find"
+
+build_config_rb = ARGV.shift
+mruby_source_dir = ARGV.shift
+
+module MRuby
+ class Build
+ class << self
+ def source_dir=(dir)
+ @@source_dir = dir
+ end
+
+ def latest
+ @@latest
+ end
+ end
+
+ attr_reader :config
+ def initialize(&block)
+ @@latest = self
+ @config = Config.new(@@source_dir)
+ @config.instance_eval(&block)
+ end
+ end
+
+ class Config
+ attr_reader :gem_dirs
+ def initialize(source_dir)
+ @source_dir = source_dir
+ @gem_dirs = []
+ end
+
+ def toolchain(*args)
+ # ignore
+ end
+
+ def enable_debug
+ # ignore
+ end
+
+ def gem(gem_dir)
+ if gem_dir.is_a?(Hash)
+ gem_dir = load_special_path_gem(gem_dir)
+ return if gem_dir.nil?
+ end
+ @gem_dirs << gem_dir
+ end
+
+ private
+ def load_special_path_gem(params)
+ if params[:core]
+ "#{@source_dir}/mrbgems/#{params[:core]}"
+ elsif params[:github]
+ nil
+ else
+ raise "Unsupported gem options: #{params.inspect}"
+ end
+ end
+ end
+end
+
+MRuby::Build.source_dir = mruby_source_dir
+load build_config_rb
+build = MRuby::Build.latest
+
+sources = []
+source_dirs = ["#{mruby_source_dir}/src"] + build.config.gem_dirs
+source_dirs.each do |source_dir|
+ Find.find(source_dir) do |path|
+ case path
+ when /\.[ch]\z/
+ sources << path
+ end
+ end
+end
+
+indented_sources = sources.collect do |source|
+ "\t#{source}"
+end
+puts("libmruby_la_SOURCES = \\")
+puts(indented_sources.join(" \\\n"))
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/CHANGES b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/CHANGES
new file mode 100644
index 00000000000..5ff1c201df0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/CHANGES
@@ -0,0 +1,6766 @@
+
+Changes with nginx 1.7.4 05 Aug 2014
+
+ *) Security: pipelined commands were not discarded after STARTTLS
+ command in SMTP proxy (CVE-2014-3556); the bug had appeared in 1.5.6.
+ Thanks to Chris Boulton.
+
+ *) Change: URI escaping now uses uppercase hexadecimal digits.
+ Thanks to Piotr Sikora.
+
+ *) Feature: now nginx can be build with BoringSSL and LibreSSL.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: requests might hang if resolver was used and a DNS server
+ returned a malformed response; the bug had appeared in 1.5.8.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: the $uri variable might contain garbage when returning errors
+ with code 400.
+ Thanks to Sergey Bobrov.
+
+ *) Bugfix: in error handling in the "proxy_store" directive and the
+ ngx_http_dav_module.
+ Thanks to Feng Gu.
+
+ *) Bugfix: a segmentation fault might occur if logging of errors to
+ syslog was used; the bug had appeared in 1.7.1.
+
+ *) Bugfix: the $geoip_latitude, $geoip_longitude, $geoip_dma_code, and
+ $geoip_area_code variables might not work.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: in memory allocation error handling.
+ Thanks to Tatsuhiko Kubo and Piotr Sikora.
+
+
+Changes with nginx 1.7.3 08 Jul 2014
+
+ *) Feature: weak entity tags are now preserved on response
+ modifications, and strong ones are changed to weak.
+
+ *) Feature: cache revalidation now uses If-None-Match header if
+ possible.
+
+ *) Feature: the "ssl_password_file" directive.
+
+ *) Bugfix: the If-None-Match request header line was ignored if there
+ was no Last-Modified header in a response returned from cache.
+
+ *) Bugfix: "peer closed connection in SSL handshake" messages were
+ logged at "info" level instead of "error" while connecting to
+ backends.
+
+ *) Bugfix: in the ngx_http_dav_module module in nginx/Windows.
+
+ *) Bugfix: SPDY connections might be closed prematurely if caching was
+ used.
+
+
+Changes with nginx 1.7.2 17 Jun 2014
+
+ *) Feature: the "hash" directive inside the "upstream" block.
+
+ *) Feature: defragmentation of free shared memory blocks.
+ Thanks to Wandenberg Peixoto and Yichun Zhang.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ default value of the "access_log" directive was used; the bug had
+ appeared in 1.7.0.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: trailing slash was mistakenly removed from the last parameter
+ of the "try_files" directive.
+
+ *) Bugfix: nginx could not be built on OS X in some cases.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+
+
+Changes with nginx 1.7.1 27 May 2014
+
+ *) Feature: the "$upstream_cookie_..." variables.
+
+ *) Feature: the $ssl_client_fingerprint variable.
+
+ *) Feature: the "error_log" and "access_log" directives now support
+ logging to syslog.
+
+ *) Feature: the mail proxy now logs client port on connect.
+
+ *) Bugfix: memory leak if the "ssl_stapling" directive was used.
+ Thanks to Filipe da Silva.
+
+ *) Bugfix: the "alias" directive used inside a location given by a
+ regular expression worked incorrectly if the "if" or "limit_except"
+ directives were used.
+
+ *) Bugfix: the "charset" directive did not set a charset to encoded
+ backend responses.
+
+ *) Bugfix: a "proxy_pass" directive without URI part might use original
+ request after the $args variable was set.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: in the "none" parameter in the "smtp_auth" directive; the bug
+ had appeared in 1.5.6.
+ Thanks to Svyatoslav Nikolsky.
+
+ *) Bugfix: if sub_filter and SSI were used together, then responses
+ might be transferred incorrectly.
+
+ *) Bugfix: nginx could not be built with the --with-file-aio option on
+ Linux/aarch64.
+
+
+Changes with nginx 1.7.0 24 Apr 2014
+
+ *) Feature: backend SSL certificate verification.
+
+ *) Feature: support for SNI while working with SSL backends.
+
+ *) Feature: the $ssl_server_name variable.
+
+ *) Feature: the "if" parameter of the "access_log" directive.
+
+
+Changes with nginx 1.5.13 08 Apr 2014
+
+ *) Change: improved hash table handling; the default values of the
+ "variables_hash_max_size" and "types_hash_bucket_size" were changed
+ to 1024 and 64 respectively.
+
+ *) Feature: the ngx_http_mp4_module now supports the "end" argument.
+
+ *) Feature: byte ranges support in the ngx_http_mp4_module and while
+ saving responses to cache.
+
+ *) Bugfix: alerts "ngx_slab_alloc() failed: no memory" no longer logged
+ when using shared memory in the "ssl_session_cache" directive and in
+ the ngx_http_limit_req_module.
+
+ *) Bugfix: the "underscores_in_headers" directive did not allow
+ underscore as a first character of a header.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: cache manager might hog CPU on exit in nginx/Windows.
+
+ *) Bugfix: nginx/Windows terminated abnormally if the
+ "ssl_session_cache" directive was used with the "shared" parameter.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+
+
+Changes with nginx 1.5.12 18 Mar 2014
+
+ *) Security: a heap memory buffer overflow might occur in a worker
+ process while handling a specially crafted request by
+ ngx_http_spdy_module, potentially resulting in arbitrary code
+ execution (CVE-2014-0133).
+ Thanks to Lucas Molas, researcher at Programa STIC, Fundación Dr.
+ Manuel Sadosky, Buenos Aires, Argentina.
+
+ *) Feature: the "proxy_protocol" parameters of the "listen" and
+ "real_ip_header" directives, the $proxy_protocol_addr variable.
+
+ *) Bugfix: in the "fastcgi_next_upstream" directive.
+ Thanks to Lucas Molas.
+
+
+Changes with nginx 1.5.11 04 Mar 2014
+
+ *) Security: memory corruption might occur in a worker process on 32-bit
+ platforms while handling a specially crafted request by
+ ngx_http_spdy_module, potentially resulting in arbitrary code
+ execution (CVE-2014-0088); the bug had appeared in 1.5.10.
+ Thanks to Lucas Molas, researcher at Programa STIC, Fundación Dr.
+ Manuel Sadosky, Buenos Aires, Argentina.
+
+ *) Feature: the $ssl_session_reused variable.
+
+ *) Bugfix: the "client_max_body_size" directive might not work when
+ reading a request body using chunked transfer encoding; the bug had
+ appeared in 1.3.9.
+ Thanks to Lucas Molas.
+
+ *) Bugfix: a segmentation fault might occur in a worker process when
+ proxying WebSocket connections.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ ngx_http_spdy_module was used on 32-bit platforms; the bug had
+ appeared in 1.5.10.
+
+ *) Bugfix: the $upstream_status variable might contain wrong data if the
+ "proxy_cache_use_stale" or "proxy_cache_revalidate" directives were
+ used.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ errors with code 400 were redirected to a named location using the
+ "error_page" directive.
+
+ *) Bugfix: nginx/Windows could not be built with Visual Studio 2013.
+
+
+Changes with nginx 1.5.10 04 Feb 2014
+
+ *) Feature: the ngx_http_spdy_module now uses SPDY 3.1 protocol.
+ Thanks to Automattic and MaxCDN for sponsoring this work.
+
+ *) Feature: the ngx_http_mp4_module now skips tracks too short for a
+ seek requested.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ $ssl_session_id variable was used in logs; the bug had appeared in
+ 1.5.9.
+
+ *) Bugfix: the $date_local and $date_gmt variables used wrong format
+ outside of the ngx_http_ssi_filter_module.
+
+ *) Bugfix: client connections might be immediately closed if deferred
+ accept was used; the bug had appeared in 1.3.15.
+
+ *) Bugfix: alerts "getsockopt(TCP_FASTOPEN) ... failed" appeared in logs
+ during binary upgrade on Linux; the bug had appeared in 1.5.8.
+ Thanks to Piotr Sikora.
+
+
+Changes with nginx 1.5.9 22 Jan 2014
+
+ *) Change: now nginx expects escaped URIs in "X-Accel-Redirect" headers.
+
+ *) Feature: the "ssl_buffer_size" directive.
+
+ *) Feature: the "limit_rate" directive can now be used to rate limit
+ responses sent in SPDY connections.
+
+ *) Feature: the "spdy_chunk_size" directive.
+
+ *) Feature: the "ssl_session_tickets" directive.
+ Thanks to Dirkjan Bussink.
+
+ *) Bugfix: the $ssl_session_id variable contained full session
+ serialized instead of just a session id.
+ Thanks to Ivan Ristić.
+
+ *) Bugfix: nginx incorrectly handled escaped "?" character in the
+ "include" SSI command.
+
+ *) Bugfix: the ngx_http_dav_module did not unescape destination URI of
+ the COPY and MOVE methods.
+
+ *) Bugfix: resolver did not understand domain names with a trailing dot.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: alerts "zero size buf in output" might appear in logs while
+ proxying; the bug had appeared in 1.3.9.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ ngx_http_spdy_module was used.
+
+ *) Bugfix: proxied WebSocket connections might hang right after
+ handshake if the select, poll, or /dev/poll methods were used.
+
+ *) Bugfix: the "xclient" directive of the mail proxy module incorrectly
+ handled IPv6 client addresses.
+
+
+Changes with nginx 1.5.8 17 Dec 2013
+
+ *) Feature: IPv6 support in resolver.
+
+ *) Feature: the "listen" directive supports the "fastopen" parameter.
+ Thanks to Mathew Rodley.
+
+ *) Feature: SSL support in the ngx_http_uwsgi_module.
+ Thanks to Roberto De Ioris.
+
+ *) Feature: vim syntax highlighting scripts were added to contrib.
+ Thanks to Evan Miller.
+
+ *) Bugfix: a timeout might occur while reading client request body in an
+ SSL connection using chunked transfer encoding.
+
+ *) Bugfix: the "master_process" directive did not work correctly in
+ nginx/Windows.
+
+ *) Bugfix: the "setfib" parameter of the "listen" directive might not
+ work.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+
+
+Changes with nginx 1.5.7 19 Nov 2013
+
+ *) Security: a character following an unescaped space in a request line
+ was handled incorrectly (CVE-2013-4547); the bug had appeared in
+ 0.8.41.
+ Thanks to Ivan Fratric of the Google Security Team.
+
+ *) Change: a logging level of auth_basic errors about no user/password
+ provided has been lowered from "error" to "info".
+
+ *) Feature: the "proxy_cache_revalidate", "fastcgi_cache_revalidate",
+ "scgi_cache_revalidate", and "uwsgi_cache_revalidate" directives.
+
+ *) Feature: the "ssl_session_ticket_key" directive.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: the directive "add_header Cache-Control ''" added a
+ "Cache-Control" response header line with an empty value.
+
+ *) Bugfix: the "satisfy any" directive might return 403 error instead of
+ 401 if auth_request and auth_basic directives were used.
+ Thanks to Jan Marc Hoffmann.
+
+ *) Bugfix: the "accept_filter" and "deferred" parameters of the "listen"
+ directive were ignored for listen sockets created during binary
+ upgrade.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: some data received from a backend with unbufferred proxy
+ might not be sent to a client immediately if "gzip" or "gunzip"
+ directives were used.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: in error handling in ngx_http_gunzip_filter_module.
+
+ *) Bugfix: responses might hang if the ngx_http_spdy_module was used
+ with the "auth_request" directive.
+
+ *) Bugfix: memory leak in nginx/Windows.
+
+
+Changes with nginx 1.5.6 01 Oct 2013
+
+ *) Feature: the "fastcgi_buffering" directive.
+
+ *) Feature: the "proxy_ssl_protocols" and "proxy_ssl_ciphers"
+ directives.
+ Thanks to Piotr Sikora.
+
+ *) Feature: optimization of SSL handshakes when using long certificate
+ chains.
+
+ *) Feature: the mail proxy supports SMTP pipelining.
+
+ *) Bugfix: in the ngx_http_auth_basic_module when using "$apr1$"
+ password encryption method.
+ Thanks to Markus Linnala.
+
+ *) Bugfix: in MacOSX, Cygwin, and nginx/Windows incorrect location might
+ be used to process a request if locations were given using characters
+ in different cases.
+
+ *) Bugfix: automatic redirect with appended trailing slash for proxied
+ locations might not work.
+
+ *) Bugfix: in the mail proxy server.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+
+
+Changes with nginx 1.5.5 17 Sep 2013
+
+ *) Change: now nginx assumes HTTP/1.0 by default if it is not able to
+ detect protocol reliably.
+
+ *) Feature: the "disable_symlinks" directive now uses O_PATH on Linux.
+
+ *) Feature: now nginx uses EPOLLRDHUP events to detect premature
+ connection close by clients if the "epoll" method is used.
+
+ *) Bugfix: in the "valid_referers" directive if the "server_names"
+ parameter was used.
+
+ *) Bugfix: the $request_time variable did not work in nginx/Windows.
+
+ *) Bugfix: in the "image_filter" directive.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: OpenSSL 1.0.1f compatibility.
+ Thanks to Piotr Sikora.
+
+
+Changes with nginx 1.5.4 27 Aug 2013
+
+ *) Change: the "js" extension MIME type has been changed to
+ "application/javascript"; default value of the "charset_types"
+ directive was changed accordingly.
+
+ *) Change: now the "image_filter" directive with the "size" parameter
+ returns responses with the "application/json" MIME type.
+
+ *) Feature: the ngx_http_auth_request_module.
+
+ *) Bugfix: a segmentation fault might occur on start or during
+ reconfiguration if the "try_files" directive was used with an empty
+ parameter.
+
+ *) Bugfix: memory leak if relative paths were specified using variables
+ in the "root" or "auth_basic_user_file" directives.
+
+ *) Bugfix: the "valid_referers" directive incorrectly executed regular
+ expressions if a "Referer" header started with "https://".
+ Thanks to Liangbin Li.
+
+ *) Bugfix: responses might hang if subrequests were used and an SSL
+ handshake error happened during subrequest processing.
+ Thanks to Aviram Cohen.
+
+ *) Bugfix: in the ngx_http_autoindex_module.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+
+
+Changes with nginx 1.5.3 30 Jul 2013
+
+ *) Change in internal API: now u->length defaults to -1 if working with
+ backends in unbuffered mode.
+
+ *) Change: now after receiving an incomplete response from a backend
+ server nginx tries to send an available part of the response to a
+ client, and then closes client connection.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ ngx_http_spdy_module was used with the "client_body_in_file_only"
+ directive.
+
+ *) Bugfix: the "so_keepalive" parameter of the "listen" directive might
+ be handled incorrectly on DragonFlyBSD.
+ Thanks to Sepherosa Ziehau.
+
+ *) Bugfix: in the ngx_http_xslt_filter_module.
+
+ *) Bugfix: in the ngx_http_sub_filter_module.
+
+
+Changes with nginx 1.5.2 02 Jul 2013
+
+ *) Feature: now several "error_log" directives can be used.
+
+ *) Bugfix: the $r->header_in() embedded perl method did not return value
+ of the "Cookie" and "X-Forwarded-For" request header lines; the bug
+ had appeared in 1.3.14.
+
+ *) Bugfix: in the ngx_http_spdy_module.
+ Thanks to Jim Radford.
+
+ *) Bugfix: nginx could not be built on Linux with x32 ABI.
+ Thanks to Serguei Ivantsov.
+
+
+Changes with nginx 1.5.1 04 Jun 2013
+
+ *) Feature: the "ssi_last_modified", "sub_filter_last_modified", and
+ "xslt_last_modified" directives.
+ Thanks to Alexey Kolpakov.
+
+ *) Feature: the "http_403" parameter of the "proxy_next_upstream",
+ "fastcgi_next_upstream", "scgi_next_upstream", and
+ "uwsgi_next_upstream" directives.
+
+ *) Feature: the "allow" and "deny" directives now support unix domain
+ sockets.
+
+ *) Bugfix: nginx could not be built with the ngx_mail_ssl_module, but
+ without ngx_http_ssl_module; the bug had appeared in 1.3.14.
+
+ *) Bugfix: in the "proxy_set_body" directive.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: in the "lingering_time" directive.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: the "fail_timeout" parameter of the "server" directive in the
+ "upstream" context might not work if "max_fails" parameter was used;
+ the bug had appeared in 1.3.0.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "ssl_stapling" directive was used.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the mail proxy server.
+ Thanks to Filipe Da Silva.
+
+ *) Bugfix: nginx/Windows might stop accepting connections if several
+ worker processes were used.
+
+
+Changes with nginx 1.5.0 07 May 2013
+
+ *) Security: a stack-based buffer overflow might occur in a worker
+ process while handling a specially crafted request, potentially
+ resulting in arbitrary code execution (CVE-2013-2028); the bug had
+ appeared in 1.3.9.
+ Thanks to Greg MacManus, iSIGHT Partners Labs.
+
+
+Changes with nginx 1.4.0 24 Apr 2013
+
+ *) Bugfix: nginx could not be built with the ngx_http_perl_module if the
+ --with-openssl option was used; the bug had appeared in 1.3.16.
+
+ *) Bugfix: in a request body handling in the ngx_http_perl_module; the
+ bug had appeared in 1.3.9.
+
+
+Changes with nginx 1.3.16 16 Apr 2013
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ subrequests were used; the bug had appeared in 1.3.9.
+
+ *) Bugfix: the "tcp_nodelay" directive caused an error if a WebSocket
+ connection was proxied into a unix domain socket.
+
+ *) Bugfix: the $upstream_response_length variable has an incorrect value
+ "0" if buffering was not used.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the eventport and /dev/poll methods.
+
+
+Changes with nginx 1.3.15 26 Mar 2013
+
+ *) Change: opening and closing a connection without sending any data in
+ it is no longer logged to access_log with error code 400.
+
+ *) Feature: the ngx_http_spdy_module.
+ Thanks to Automattic for sponsoring this work.
+
+ *) Feature: the "limit_req_status" and "limit_conn_status" directives.
+ Thanks to Nick Marden.
+
+ *) Feature: the "image_filter_interlace" directive.
+ Thanks to Ian Babrou.
+
+ *) Feature: $connections_waiting variable in the
+ ngx_http_stub_status_module.
+
+ *) Feature: the mail proxy module now supports IPv6 backends.
+
+ *) Bugfix: request body might be transmitted incorrectly when retrying a
+ request to the next upstream server; the bug had appeared in 1.3.9.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the "client_body_in_file_only" directive; the bug had
+ appeared in 1.3.9.
+
+ *) Bugfix: responses might hang if subrequests were used and a DNS error
+ happened during subrequest processing.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: in backend usage accounting.
+
+
+Changes with nginx 1.3.14 05 Mar 2013
+
+ *) Feature: $connections_active, $connections_reading, and
+ $connections_writing variables in the ngx_http_stub_status_module.
+
+ *) Feature: support of WebSocket connections in the
+ ngx_http_uwsgi_module and ngx_http_scgi_module.
+
+ *) Bugfix: in virtual servers handling with SNI.
+
+ *) Bugfix: new sessions were not always stored if the "ssl_session_cache
+ shared" directive was used and there was no free space in shared
+ memory.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: multiple X-Forwarded-For headers were handled incorrectly.
+ Thanks to Neal Poole for sponsoring this work.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+ Thanks to Gernot Vormayr.
+
+
+Changes with nginx 1.3.13 19 Feb 2013
+
+ *) Change: a compiler with name "cc" is now used by default.
+
+ *) Feature: support for proxying of WebSocket connections.
+ Thanks to Apcera and CloudBees for sponsoring this work.
+
+ *) Feature: the "auth_basic_user_file" directive supports "{SHA}"
+ password encryption method.
+ Thanks to Louis Opter.
+
+
+Changes with nginx 1.3.12 05 Feb 2013
+
+ *) Feature: variables support in the "proxy_bind", "fastcgi_bind",
+ "memcached_bind", "scgi_bind", and "uwsgi_bind" directives.
+
+ *) Feature: the $pipe, $request_length, $time_iso8601, and $time_local
+ variables can now be used not only in the "log_format" directive.
+ Thanks to Kiril Kalchev.
+
+ *) Feature: IPv6 support in the ngx_http_geoip_module.
+ Thanks to Gregor Kališnik.
+
+ *) Bugfix: in the "proxy_method" directive.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ resolver was used with the poll method.
+
+ *) Bugfix: nginx might hog CPU during SSL handshake with a backend if
+ the select, poll, or /dev/poll methods were used.
+
+ *) Bugfix: the "[crit] SSL_write() failed (SSL:)" error.
+
+ *) Bugfix: in the "client_body_in_file_only" directive; the bug had
+ appeared in 1.3.9.
+
+ *) Bugfix: in the "fastcgi_keep_conn" directive.
+
+
+Changes with nginx 1.3.11 10 Jan 2013
+
+ *) Bugfix: a segmentation fault might occur if logging was used; the bug
+ had appeared in 1.3.10.
+
+ *) Bugfix: the "proxy_pass" directive did not work with IP addresses
+ without port specified; the bug had appeared in 1.3.10.
+
+ *) Bugfix: a segmentation fault occurred on start or during
+ reconfiguration if the "keepalive" directive was specified more than
+ once in a single upstream block.
+
+ *) Bugfix: parameter "default" of the "geo" directive did not set
+ default value for IPv6 addresses.
+
+
+Changes with nginx 1.3.10 25 Dec 2012
+
+ *) Change: domain names specified in configuration file are now resolved
+ to IPv6 addresses as well as IPv4 ones.
+
+ *) Change: now if the "include" directive with mask is used on Unix
+ systems, included files are sorted in alphabetical order.
+
+ *) Change: the "add_header" directive adds headers to 201 responses.
+
+ *) Feature: the "geo" directive now supports IPv6 addresses in CIDR
+ notation.
+
+ *) Feature: the "flush" and "gzip" parameters of the "access_log"
+ directive.
+
+ *) Feature: variables support in the "auth_basic" directive.
+
+ *) Bugfix: nginx could not be built with the ngx_http_perl_module in
+ some cases.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ ngx_http_xslt_module was used.
+
+ *) Bugfix: nginx could not be built on MacOSX in some cases.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: the "limit_rate" directive with high rates might result in
+ truncated responses on 32-bit platforms.
+ Thanks to Alexey Antropov.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "if" directive was used.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: a "100 Continue" response was issued with "413 Request Entity
+ Too Large" responses.
+
+ *) Bugfix: the "image_filter", "image_filter_jpeg_quality" and
+ "image_filter_sharpen" directives might be inherited incorrectly.
+ Thanks to Ian Babrou.
+
+ *) Bugfix: "crypt_r() failed" errors might appear if the "auth_basic"
+ directive was used on Linux.
+
+ *) Bugfix: in backup servers handling.
+ Thanks to Thomas Chen.
+
+ *) Bugfix: proxied HEAD requests might return incorrect response if the
+ "gzip" directive was used.
+
+
+Changes with nginx 1.3.9 27 Nov 2012
+
+ *) Feature: support for chunked transfer encoding while reading client
+ request body.
+
+ *) Feature: the $request_time and $msec variables can now be used not
+ only in the "log_format" directive.
+
+ *) Bugfix: cache manager and cache loader processes might not be able to
+ start if more than 512 listen sockets were used.
+
+ *) Bugfix: in the ngx_http_dav_module.
+
+
+Changes with nginx 1.3.8 30 Oct 2012
+
+ *) Feature: the "optional_no_ca" parameter of the "ssl_verify_client"
+ directive.
+ Thanks to Mike Kazantsev and Eric O'Connor.
+
+ *) Feature: the $bytes_sent, $connection, and $connection_requests
+ variables can now be used not only in the "log_format" directive.
+ Thanks to Benjamin Grössing.
+
+ *) Feature: the "auto" parameter of the "worker_processes" directive.
+
+ *) Bugfix: "cache file ... has md5 collision" alert.
+
+ *) Bugfix: in the ngx_http_gunzip_filter_module.
+
+ *) Bugfix: in the "ssl_stapling" directive.
+
+
+Changes with nginx 1.3.7 02 Oct 2012
+
+ *) Feature: OCSP stapling support.
+ Thanks to Comodo, DigiCert and GlobalSign for sponsoring this work.
+
+ *) Feature: the "ssl_trusted_certificate" directive.
+
+ *) Feature: resolver now randomly rotates addresses returned from cache.
+ Thanks to Anton Jouline.
+
+ *) Bugfix: OpenSSL 0.9.7 compatibility.
+
+
+Changes with nginx 1.3.6 12 Sep 2012
+
+ *) Feature: the ngx_http_gunzip_filter_module.
+
+ *) Feature: the "memcached_gzip_flag" directive.
+
+ *) Feature: the "always" parameter of the "gzip_static" directive.
+
+ *) Bugfix: in the "limit_req" directive; the bug had appeared in 1.1.14.
+ Thanks to Charles Chen.
+
+ *) Bugfix: nginx could not be built by gcc 4.7 with -O2 optimization if
+ the --with-ipv6 option was used.
+
+
+Changes with nginx 1.3.5 21 Aug 2012
+
+ *) Change: the ngx_http_mp4_module module no longer skips tracks in
+ formats other than H.264 and AAC.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "map" directive was used with variables as values.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "geo" directive was used with the "ranges" parameter but without the
+ "default" parameter; the bug had appeared in 0.8.43.
+ Thanks to Zhen Chen and Weibin Yao.
+
+ *) Bugfix: in the -p command-line parameter handling.
+
+ *) Bugfix: in the mail proxy server.
+
+ *) Bugfix: of minor potential bugs.
+ Thanks to Coverity.
+
+ *) Bugfix: nginx/Windows could not be built with Visual Studio 2005
+ Express.
+ Thanks to HAYASHI Kentaro.
+
+
+Changes with nginx 1.3.4 31 Jul 2012
+
+ *) Change: the "ipv6only" parameter is now turned on by default for
+ listening IPv6 sockets.
+
+ *) Feature: the Clang compiler support.
+
+ *) Bugfix: extra listening sockets might be created.
+ Thanks to Roman Odaisky.
+
+ *) Bugfix: nginx/Windows might hog CPU if a worker process failed to
+ start.
+ Thanks to Ricardo Villalobos Guevara.
+
+ *) Bugfix: the "proxy_pass_header", "fastcgi_pass_header",
+ "scgi_pass_header", "uwsgi_pass_header", "proxy_hide_header",
+ "fastcgi_hide_header", "scgi_hide_header", and "uwsgi_hide_header"
+ directives might be inherited incorrectly.
+
+
+Changes with nginx 1.3.3 10 Jul 2012
+
+ *) Feature: entity tags support and the "etag" directive.
+
+ *) Bugfix: trailing dot in a source value was not ignored if the "map"
+ directive was used with the "hostnames" parameter.
+
+ *) Bugfix: incorrect location might be used to process a request if a
+ URI was changed via a "rewrite" directive before an internal redirect
+ to a named location.
+
+
+Changes with nginx 1.3.2 26 Jun 2012
+
+ *) Change: the "single" parameter of the "keepalive" directive is now
+ ignored.
+
+ *) Change: SSL compression is now disabled when using all versions of
+ OpenSSL, including ones prior to 1.0.0.
+
+ *) Feature: it is now possible to use the "ip_hash" directive to balance
+ IPv6 clients.
+
+ *) Feature: the $status variable can now be used not only in the
+ "log_format" directive.
+
+ *) Bugfix: a segmentation fault might occur in a worker process on
+ shutdown if the "resolver" directive was used.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ ngx_http_mp4_module was used.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ conflicting wildcard server names were used.
+
+ *) Bugfix: nginx might be terminated abnormally on a SIGBUS signal on
+ ARM platform.
+
+ *) Bugfix: an alert "sendmsg() failed (9: Bad file number)" on HP-UX
+ while reconfiguration.
+
+
+Changes with nginx 1.3.1 05 Jun 2012
+
+ *) Security: now nginx/Windows ignores trailing dot in URI path
+ component, and does not allow URIs with ":$" in it.
+ Thanks to Vladimir Kochetkov, Positive Research Center.
+
+ *) Feature: the "proxy_pass", "fastcgi_pass", "scgi_pass", "uwsgi_pass"
+ directives, and the "server" directive inside the "upstream" block,
+ now support IPv6 addresses.
+
+ *) Feature: the "resolver" directive now supports IPv6 addresses and an
+ optional port specification.
+
+ *) Feature: the "least_conn" directive inside the "upstream" block.
+
+ *) Feature: it is now possible to specify a weight for servers while
+ using the "ip_hash" directive.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "image_filter" directive was used; the bug had appeared in 1.3.0.
+
+ *) Bugfix: nginx could not be built with ngx_cpp_test_module; the bug
+ had appeared in 1.1.12.
+
+ *) Bugfix: access to variables from SSI and embedded perl module might
+ not work after reconfiguration.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: in the ngx_http_xslt_filter_module.
+ Thanks to Kuramoto Eiji.
+
+ *) Bugfix: memory leak if $geoip_org variable was used.
+ Thanks to Denis F. Latypoff.
+
+ *) Bugfix: in the "proxy_cookie_domain" and "proxy_cookie_path"
+ directives.
+
+
+Changes with nginx 1.3.0 15 May 2012
+
+ *) Feature: the "debug_connection" directive now supports IPv6 addresses
+ and the "unix:" parameter.
+
+ *) Feature: the "set_real_ip_from" directive and the "proxy" parameter
+ of the "geo" directive now support IPv6 addresses.
+
+ *) Feature: the "real_ip_recursive", "geoip_proxy", and
+ "geoip_proxy_recursive" directives.
+
+ *) Feature: the "proxy_recursive" parameter of the "geo" directive.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "resolver" directive was used.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "fastcgi_pass", "scgi_pass", or "uwsgi_pass" directives were used and
+ backend returned incorrect response.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "rewrite" directive was used and new request arguments in a
+ replacement used variables.
+
+ *) Bugfix: nginx might hog CPU if the open file resource limit was
+ reached.
+
+ *) Bugfix: nginx might loop infinitely over backends if the
+ "proxy_next_upstream" directive with the "http_404" parameter was
+ used and there were backup servers specified in an upstream block.
+
+ *) Bugfix: adding the "down" parameter of the "server" directive might
+ cause unneeded client redistribution among backend servers if the
+ "ip_hash" directive was used.
+
+ *) Bugfix: socket leak.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: in the ngx_http_fastcgi_module.
+
+
+Changes with nginx 1.2.0 23 Apr 2012
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "try_files" directive was used; the bug had appeared in 1.1.19.
+
+ *) Bugfix: response might be truncated if there were more than IOV_MAX
+ buffers used.
+
+ *) Bugfix: in the "crop" parameter of the "image_filter" directive.
+ Thanks to Maxim Bublis.
+
+
+Changes with nginx 1.1.19 12 Apr 2012
+
+ *) Security: specially crafted mp4 file might allow to overwrite memory
+ locations in a worker process if the ngx_http_mp4_module was used,
+ potentially resulting in arbitrary code execution (CVE-2012-2089).
+ Thanks to Matthew Daley.
+
+ *) Bugfix: nginx/Windows might be terminated abnormally.
+ Thanks to Vincent Lee.
+
+ *) Bugfix: nginx hogged CPU if all servers in an upstream were marked as
+ "backup".
+
+ *) Bugfix: the "allow" and "deny" directives might be inherited
+ incorrectly if they were used with IPv6 addresses.
+
+ *) Bugfix: the "modern_browser" and "ancient_browser" directives might
+ be inherited incorrectly.
+
+ *) Bugfix: timeouts might be handled incorrectly on Solaris/SPARC.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+
+
+Changes with nginx 1.1.18 28 Mar 2012
+
+ *) Change: keepalive connections are no longer disabled for Safari by
+ default.
+
+ *) Feature: the $connection_requests variable.
+
+ *) Feature: $tcpinfo_rtt, $tcpinfo_rttvar, $tcpinfo_snd_cwnd and
+ $tcpinfo_rcv_space variables.
+
+ *) Feature: the "worker_cpu_affinity" directive now works on FreeBSD.
+
+ *) Feature: the "xslt_param" and "xslt_string_param" directives.
+ Thanks to Samuel Behan.
+
+ *) Bugfix: in configure tests.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the ngx_http_xslt_filter_module.
+
+ *) Bugfix: nginx could not be built on Debian GNU/Hurd.
+
+
+Changes with nginx 1.1.17 15 Mar 2012
+
+ *) Security: content of previously freed memory might be sent to a
+ client if backend returned specially crafted response.
+ Thanks to Matthew Daley.
+
+ *) Bugfix: in the embedded perl module if used from SSI.
+ Thanks to Matthew Daley.
+
+ *) Bugfix: in the ngx_http_uwsgi_module.
+
+
+Changes with nginx 1.1.16 29 Feb 2012
+
+ *) Change: the simultaneous subrequest limit has been raised to 200.
+
+ *) Feature: the "from" parameter of the "disable_symlinks" directive.
+
+ *) Feature: the "return" and "error_page" directives can now be used to
+ return 307 redirections.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "resolver" directive was used and there was no "error_log" directive
+ specified at global level.
+ Thanks to Roman Arutyunyan.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if the
+ "proxy_http_version 1.1" or "fastcgi_keep_conn on" directives were
+ used.
+
+ *) Bugfix: memory leaks.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: in the "disable_symlinks" directive.
+
+ *) Bugfix: on ZFS filesystem disk cache size might be calculated
+ incorrectly; the bug had appeared in 1.0.1.
+
+ *) Bugfix: nginx could not be built by the icc 12.1 compiler.
+
+ *) Bugfix: nginx could not be built by gcc on Solaris; the bug had
+ appeared in 1.1.15.
+
+
+Changes with nginx 1.1.15 15 Feb 2012
+
+ *) Feature: the "disable_symlinks" directive.
+
+ *) Feature: the "proxy_cookie_domain" and "proxy_cookie_path"
+ directives.
+
+ *) Bugfix: nginx might log incorrect error "upstream prematurely closed
+ connection" instead of correct "upstream sent too big header" one.
+ Thanks to Feibo Li.
+
+ *) Bugfix: nginx could not be built with the ngx_http_perl_module if the
+ --with-openssl option was used.
+
+ *) Bugfix: the number of internal redirects to named locations was not
+ limited.
+
+ *) Bugfix: calling $r->flush() multiple times might cause errors in the
+ ngx_http_gzip_filter_module.
+
+ *) Bugfix: temporary files might be not removed if the "proxy_store"
+ directive was used with SSI includes.
+
+ *) Bugfix: in some cases non-cacheable variables (such as the $args
+ variable) returned old empty cached value.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if too
+ many SSI subrequests were issued simultaneously; the bug had appeared
+ in 0.7.25.
+
+
+Changes with nginx 1.1.14 30 Jan 2012
+
+ *) Feature: multiple "limit_req" limits may be used simultaneously.
+
+ *) Bugfix: in error handling while connecting to a backend.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in AIO error handling on FreeBSD.
+
+ *) Bugfix: in the OpenSSL library initialization.
+
+ *) Bugfix: the "proxy_redirect" directives might be inherited
+ incorrectly.
+
+ *) Bugfix: memory leak during reconfiguration if the "pcre_jit"
+ directive was used.
+
+
+Changes with nginx 1.1.13 16 Jan 2012
+
+ *) Feature: the "TLSv1.1" and "TLSv1.2" parameters of the
+ "ssl_protocols" directive.
+
+ *) Bugfix: the "limit_req" directive parameters were not inherited
+ correctly; the bug had appeared in 1.1.12.
+
+ *) Bugfix: the "proxy_redirect" directive incorrectly processed
+ "Refresh" header if regular expression were used.
+
+ *) Bugfix: the "proxy_cache_use_stale" directive with "error" parameter
+ did not return answer from cache if there were no live upstreams.
+
+ *) Bugfix: the "worker_cpu_affinity" directive might not work.
+
+ *) Bugfix: nginx could not be built on Solaris; the bug had appeared in
+ 1.1.12.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+
+
+Changes with nginx 1.1.12 26 Dec 2011
+
+ *) Change: a "proxy_pass" directive without URI part now uses changed
+ URI after redirection with the "error_page" directive.
+ Thanks to Lanshun Zhou.
+
+ *) Feature: the "proxy/fastcgi/scgi/uwsgi_cache_lock",
+ "proxy/fastcgi/scgi/uwsgi_cache_lock_timeout" directives.
+
+ *) Feature: the "pcre_jit" directive.
+
+ *) Feature: the "if" SSI command supports captures in regular
+ expressions.
+
+ *) Bugfix: the "if" SSI command did not work inside the "block" command.
+
+ *) Bugfix: the "limit_conn_log_level" and "limit_req_log_level"
+ directives might not work.
+
+ *) Bugfix: the "limit_rate" directive did not allow to use full
+ throughput, even if limit value was very high.
+
+ *) Bugfix: the "sendfile_max_chunk" directive did not work, if the
+ "limit_rate" directive was used.
+
+ *) Bugfix: a "proxy_pass" directive without URI part always used
+ original request URI if variables were used.
+
+ *) Bugfix: a "proxy_pass" directive without URI part might use original
+ request after redirection with the "try_files" directive.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: in the ngx_http_scgi_module.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+
+ *) Bugfix: nginx could not be built on Solaris; the bug had appeared in
+ 1.1.9.
+
+
+Changes with nginx 1.1.11 12 Dec 2011
+
+ *) Feature: the "so_keepalive" parameter of the "listen" directive.
+ Thanks to Vsevolod Stakhov.
+
+ *) Feature: the "if_not_empty" parameter of the
+ "fastcgi/scgi/uwsgi_param" directives.
+
+ *) Feature: the $https variable.
+
+ *) Feature: the "proxy_redirect" directive supports variables in the
+ first parameter.
+
+ *) Feature: the "proxy_redirect" directive supports regular expressions.
+
+ *) Bugfix: the $sent_http_cache_control variable might contain a wrong
+ value if the "expires" directive was used.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: the "read_ahead" directive might not work combined with
+ "try_files" and "open_file_cache".
+
+ *) Bugfix: a segmentation fault might occur in a worker process if small
+ time was used in the "inactive" parameter of the "proxy_cache_path"
+ directive.
+
+ *) Bugfix: responses from cache might hang.
+
+
+Changes with nginx 1.1.10 30 Nov 2011
+
+ *) Bugfix: a segmentation fault occured in a worker process if AIO was
+ used on Linux; the bug had appeared in 1.1.9.
+
+
+Changes with nginx 1.1.9 28 Nov 2011
+
+ *) Change: now double quotes are encoded in an "echo" SSI-command
+ output.
+ Thanks to Zaur Abasmirzoev.
+
+ *) Feature: the "valid" parameter of the "resolver" directive. By
+ default TTL returned by a DNS server is used.
+ Thanks to Kirill A. Korinskiy.
+
+ *) Bugfix: nginx might hang after a worker process abnormal termination.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if SNI
+ was used; the bug had appeared in 1.1.2.
+
+ *) Bugfix: in the "keepalive_disable" directive; the bug had appeared in
+ 1.1.8.
+ Thanks to Alexander Usov.
+
+ *) Bugfix: SIGWINCH signal did not work after first binary upgrade; the
+ bug had appeared in 1.1.1.
+
+ *) Bugfix: backend responses with length not matching "Content-Length"
+ header line are no longer cached.
+
+ *) Bugfix: in the "scgi_param" directive, if complex parameters were
+ used.
+
+ *) Bugfix: in the "epoll" event method.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: in the ngx_http_flv_module.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: in the ngx_http_mp4_module.
+
+ *) Bugfix: IPv6 addresses are now handled properly in a request line and
+ in a "Host" request header line.
+
+ *) Bugfix: "add_header" and "expires" directives did not work if a
+ request was proxied and response status code was 206.
+
+ *) Bugfix: nginx could not be built on FreeBSD 10.
+
+ *) Bugfix: nginx could not be built on AIX.
+
+
+Changes with nginx 1.1.8 14 Nov 2011
+
+ *) Change: the ngx_http_limit_zone_module was renamed to the
+ ngx_http_limit_conn_module.
+
+ *) Change: the "limit_zone" directive was superseded by the
+ "limit_conn_zone" directive with a new syntax.
+
+ *) Feature: support for multiple "limit_conn" limits on the same level.
+
+ *) Feature: the "image_filter_sharpen" directive.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ resolver got a big DNS response.
+ Thanks to Ben Hawkes.
+
+ *) Bugfix: in cache key calculation if internal MD5 implementation was
+ used; the bug had appeared in 1.0.4.
+
+ *) Bugfix: the "If-Modified-Since", "If-Range", etc. client request
+ header lines might be passed to backend while caching; or not passed
+ without caching if caching was enabled in another part of the
+ configuration.
+
+ *) Bugfix: the module ngx_http_mp4_module sent incorrect
+ "Content-Length" response header line if the "start" argument was
+ used.
+ Thanks to Piotr Sikora.
+
+
+Changes with nginx 1.1.7 31 Oct 2011
+
+ *) Feature: support of several DNS servers in the "resolver" directive.
+ Thanks to Kirill A. Korinskiy.
+
+ *) Bugfix: a segmentation fault occurred on start or during
+ reconfiguration if the "ssl" directive was used at http level and
+ there was no "ssl_certificate" defined.
+
+ *) Bugfix: reduced memory consumption while proxying big files if they
+ were buffered to disk.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ "proxy_http_version 1.1" directive was used.
+
+ *) Bugfix: in the "expires @time" directive.
+
+
+Changes with nginx 1.1.6 17 Oct 2011
+
+ *) Change in internal API: now module context data are cleared while
+ internal redirect to named location.
+ Requested by Yichun Zhang.
+
+ *) Change: if a server in an upstream failed, only one request will be
+ sent to it after fail_timeout; the server will be considered alive if
+ it will successfully respond to the request.
+
+ *) Change: now the 0x7F-0x1F characters are escaped as \xXX in an
+ access_log.
+
+ *) Feature: "proxy/fastcgi/scgi/uwsgi_ignore_headers" directives support
+ the following additional values: X-Accel-Limit-Rate,
+ X-Accel-Buffering, X-Accel-Charset.
+
+ *) Feature: decrease of memory consumption if SSL is used.
+
+ *) Bugfix: some UTF-8 characters were processed incorrectly.
+ Thanks to Alexey Kuts.
+
+ *) Bugfix: the ngx_http_rewrite_module directives specified at "server"
+ level were executed twice if no matching locations were defined.
+
+ *) Bugfix: a socket leak might occurred if "aio sendfile" was used.
+
+ *) Bugfix: connections with fast clients might be closed after
+ send_timeout if file AIO was used.
+
+ *) Bugfix: in the ngx_http_autoindex_module.
+
+ *) Bugfix: the module ngx_http_mp4_module did not support seeking on
+ 32-bit platforms.
+
+
+Changes with nginx 1.1.5 05 Oct 2011
+
+ *) Feature: the "uwsgi_buffering" and "scgi_buffering" directives.
+ Thanks to Peter Smit.
+
+ *) Bugfix: non-cacheable responses might be cached if
+ "proxy_cache_bypass" directive was used.
+ Thanks to John Ferlito.
+
+ *) Bugfix: in HTTP/1.1 support in the ngx_http_proxy_module.
+
+ *) Bugfix: cached responses with an empty body were returned
+ incorrectly; the bug had appeared in 0.8.31.
+
+ *) Bugfix: 201 responses of the ngx_http_dav_module were incorrect; the
+ bug had appeared in 0.8.32.
+
+ *) Bugfix: in the "return" directive.
+
+ *) Bugfix: the "ssl_session_cache builtin" directive caused segmentation
+ fault; the bug had appeared in 1.1.1.
+
+
+Changes with nginx 1.1.4 20 Sep 2011
+
+ *) Feature: the ngx_http_upstream_keepalive module.
+
+ *) Feature: the "proxy_http_version" directive.
+
+ *) Feature: the "fastcgi_keep_conn" directive.
+
+ *) Feature: the "worker_aio_requests" directive.
+
+ *) Bugfix: if nginx was built --with-file-aio it could not be run on
+ Linux kernel which did not support AIO.
+
+ *) Bugfix: in Linux AIO error processing.
+ Thanks to Hagai Avrahami.
+
+ *) Bugfix: reduced memory consumption for long-lived requests.
+
+ *) Bugfix: the module ngx_http_mp4_module did not support 64-bit MP4
+ "co64" atom.
+
+
+Changes with nginx 1.1.3 14 Sep 2011
+
+ *) Feature: the module ngx_http_mp4_module.
+
+ *) Bugfix: in Linux AIO combined with open_file_cache.
+
+ *) Bugfix: open_file_cache did not update file info on retest if file
+ was not atomically changed.
+
+ *) Bugfix: nginx could not be built on MacOSX 10.7.
+
+
+Changes with nginx 1.1.2 05 Sep 2011
+
+ *) Change: now if total size of all ranges is greater than source
+ response size, then nginx disables ranges and returns just the source
+ response.
+
+ *) Feature: the "max_ranges" directive.
+
+ *) Bugfix: the "ssl_verify_client", "ssl_verify_depth", and
+ "ssl_prefer_server_ciphers" directives might work incorrectly if SNI
+ was used.
+
+ *) Bugfix: in the "proxy/fastcgi/scgi/uwsgi_ignore_client_abort"
+ directives.
+
+
+Changes with nginx 1.1.1 22 Aug 2011
+
+ *) Change: now cache loader processes either as many files as specified
+ by "loader_files" parameter or works no longer than time specified by
+ the "loader_threshold" parameter during each iteration.
+
+ *) Change: now SIGWINCH signal works only in daemon mode.
+
+ *) Feature: now shared zones and caches use POSIX semaphores on Solaris.
+ Thanks to Den Ivanov.
+
+ *) Feature: accept filters are now supported on NetBSD.
+
+ *) Bugfix: nginx could not be built on Linux 3.0.
+
+ *) Bugfix: nginx did not use gzipping in some cases; the bug had
+ appeared in 1.1.0.
+
+ *) Bugfix: request body might be processed incorrectly if client used
+ pipelining.
+
+ *) Bugfix: in the "request_body_in_single_buf" directive.
+
+ *) Bugfix: in "proxy_set_body" and "proxy_pass_request_body" directives
+ if SSL connection to backend was used.
+
+ *) Bugfix: nginx hogged CPU if all servers in an upstream were marked as
+ "down".
+
+ *) Bugfix: a segmentation fault might occur during reconfiguration if
+ ssl_session_cache was defined but not used in previous configuration.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if many
+ backup servers were used in an upstream.
+
+ *) Bugfix: a segmentation fault might occur in a worker process if
+ "fastcgi/scgi/uwsgi_param" directives were used with values starting
+ with "HTTP_"; the bug had appeared in 0.8.40.
+
+
+Changes with nginx 1.1.0 01 Aug 2011
+
+ *) Feature: cache loader run time decrease.
+
+ *) Feature: "loader_files", "loader_sleep", and "loader_threshold"
+ options of the "proxy/fastcgi/scgi/uwsgi_cache_path" directives.
+
+ *) Feature: loading time decrease of configuration with large number of
+ HTTPS sites.
+
+ *) Feature: now nginx supports ECDHE key exchange ciphers.
+ Thanks to Adrian Kotelba.
+
+ *) Feature: the "lingering_close" directive.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: in closing connection for pipelined requests.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx did not disable gzipping if client sent "gzip;q=0" in
+ "Accept-Encoding" request header line.
+
+ *) Bugfix: in timeout in unbuffered proxied mode.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: memory leaks when a "proxy_pass" directive contains variables
+ and proxies to an HTTPS backend.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: in parameter validaiton of a "proxy_pass" directive with
+ variables.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: SSL did not work on QNX.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: SSL modules could not be built by gcc 4.6 without
+ --with-debug option.
+
+
+Changes with nginx 1.0.5 19 Jul 2011
+
+ *) Change: now default SSL ciphers are "HIGH:!aNULL:!MD5".
+ Thanks to Rob Stradling.
+
+ *) Feature: the "referer_hash_max_size" and "referer_hash_bucket_size"
+ directives.
+ Thanks to Witold Filipczyk.
+
+ *) Feature: $uid_reset variable.
+
+ *) Bugfix: a segmentation fault might occur in a worker process, if a
+ caching was used.
+ Thanks to Lanshun Zhou.
+
+ *) Bugfix: worker processes may got caught in an endless loop during
+ reconfiguration, if a caching was used; the bug had appeared in
+ 0.8.48.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: "stalled cache updating" alert.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 1.0.4 01 Jun 2011
+
+ *) Change: now regular expressions case sensitivity in the "map"
+ directive is given by prefixes "~" or "~*".
+
+ *) Feature: now shared zones and caches use POSIX semaphores on Linux.
+ Thanks to Denis F. Latypoff.
+
+ *) Bugfix: "stalled cache updating" alert.
+
+ *) Bugfix: nginx could not be built --without-http_auth_basic_module;
+ the bug had appeared in 1.0.3.
+
+
+Changes with nginx 1.0.3 25 May 2011
+
+ *) Feature: the "auth_basic_user_file" directive supports "$apr1",
+ "{PLAIN}", and "{SSHA}" password encryption methods.
+ Thanks to Maxim Dounin.
+
+ *) Feature: the "geoip_org" directive and $geoip_org variable.
+ Thanks to Alexander Uskov, Arnaud Granal, and Denis F. Latypoff.
+
+ *) Feature: ngx_http_geo_module and ngx_http_geoip_module support IPv4
+ addresses mapped to IPv6 addresses.
+
+ *) Bugfix: a segmentation fault occurred in a worker process during
+ testing IPv4 address mapped to IPv6 address, if access or deny rules
+ were defined only for IPv6; the bug had appeared in 0.8.22.
+
+ *) Bugfix: a cached response may be broken if "proxy/fastcgi/scgi/
+ uwsgi_cache_bypass" and "proxy/fastcgi/scgi/uwsgi_no_cache" directive
+ values were different; the bug had appeared in 0.8.46.
+
+
+Changes with nginx 1.0.2 10 May 2011
+
+ *) Feature: now shared zones and caches use POSIX semaphores.
+
+ *) Bugfix: in the "rotate" parameter of the "image_filter" directive.
+ Thanks to Adam Bocim.
+
+ *) Bugfix: nginx could not be built on Solaris; the bug had appeared in
+ 1.0.1.
+
+
+Changes with nginx 1.0.1 03 May 2011
+
+ *) Change: now the "split_clients" directive uses MurmurHash2 algorithm
+ because of better distribution.
+ Thanks to Oleg Mamontov.
+
+ *) Change: now long strings starting with zero are not considered as
+ false values.
+ Thanks to Maxim Dounin.
+
+ *) Change: now nginx uses a default listen backlog value 511 on Linux.
+
+ *) Feature: the $upstream_... variables may be used in the SSI and perl
+ modules.
+
+ *) Bugfix: now nginx limits better disk cache size.
+ Thanks to Oleg Mamontov.
+
+ *) Bugfix: a segmentation fault might occur while parsing incorrect IPv4
+ address; the bug had appeared in 0.9.3.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx could not be built by gcc 4.6 without --with-debug
+ option.
+
+ *) Bugfix: nginx could not be built on Solaris 9 and earlier; the bug
+ had appeared in 0.9.3.
+ Thanks to Dagobert Michelsen.
+
+ *) Bugfix: $request_time variable had invalid values if subrequests were
+ used; the bug had appeared in 0.8.47.
+ Thanks to Igor A. Valcov.
+
+
+Changes with nginx 1.0.0 12 Apr 2011
+
+ *) Bugfix: a cache manager might hog CPU after reload.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: an "image_filter crop" directive worked incorrectly coupled
+ with an "image_filter rotate 180" directive.
+
+ *) Bugfix: a "satisfy any" directive disabled custom 401 error page.
+
+
+Changes with nginx 0.9.7 04 Apr 2011
+
+ *) Feature: now keepalive connections may be closed premature, if there
+ are no free worker connections.
+ Thanks to Maxim Dounin.
+
+ *) Feature: the "rotate" parameter of the "image_filter" directive.
+ Thanks to Adam Bocim.
+
+ *) Bugfix: a case when a backend in "fastcgi_pass", "scgi_pass", or
+ "uwsgi_pass" directives is given by expression and refers to a
+ defined upstream.
+
+
+Changes with nginx 0.9.6 21 Mar 2011
+
+ *) Feature: the "map" directive supports regular expressions as value of
+ the first parameter.
+
+ *) Feature: $time_iso8601 access_log variable.
+ Thanks to Michael Lustfield.
+
+
+Changes with nginx 0.9.5 21 Feb 2011
+
+ *) Change: now nginx uses a default listen backlog value -1 on Linux.
+ Thanks to Andrei Nigmatulin.
+
+ *) Feature: the "utf8" parameter of "geoip_country" and "geoip_city"
+ directives.
+ Thanks to Denis F. Latypoff.
+
+ *) Bugfix: in a default "proxy_redirect" directive if "proxy_pass"
+ directive has no URI part.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: an "error_page" directive did not work with nonstandard error
+ codes; the bug had appeared in 0.8.53.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.9.4 21 Jan 2011
+
+ *) Feature: the "server_name" directive supports the $hostname variable.
+
+ *) Feature: 494 code for "Request Header Too Large" error.
+
+
+Changes with nginx 0.9.3 13 Dec 2010
+
+ *) Bugfix: if there was a single server for given IPv6 address:port
+ pair, then captures in regular expressions in a "server_name"
+ directive did not work.
+
+ *) Bugfix: nginx could not be built on Solaris; the bug had appeared in
+ 0.9.0.
+
+
+Changes with nginx 0.9.2 06 Dec 2010
+
+ *) Feature: the "If-Unmodified-Since" client request header line
+ support.
+
+ *) Workaround: fallback to accept() syscall if accept4() was not
+ implemented; the issue had appeared in 0.9.0.
+
+ *) Bugfix: nginx could not be built on Cygwin; the bug had appeared in
+ 0.9.0.
+
+ *) Bugfix: for OpenSSL vulnerability CVE-2010-4180.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.9.1 30 Nov 2010
+
+ *) Bugfix: "return CODE message" directives did not work; the bug had
+ appeared in 0.9.0.
+
+
+Changes with nginx 0.9.0 29 Nov 2010
+
+ *) Feature: the "keepalive_disable" directive.
+
+ *) Feature: the "map" directive supports variables as value of a defined
+ variable.
+
+ *) Feature: the "map" directive supports empty strings as value of the
+ first parameter.
+
+ *) Feature: the "map" directive supports expressions as the first
+ parameter.
+
+ *) Feature: nginx(8) manual page.
+ Thanks to Sergey Osokin.
+
+ *) Feature: Linux accept4() support.
+ Thanks to Simon Liu.
+
+ *) Workaround: elimination of Linux linker warning about "sys_errlist"
+ and "sys_nerr"; the warning had appeared in 0.8.35.
+
+ *) Bugfix: a segmentation fault might occur in a worker process, if the
+ "auth_basic" directive was used.
+ Thanks to Michail Laletin.
+
+ *) Bugfix: compatibility with ngx_http_eval_module; the bug had appeared
+ in 0.8.42.
+
+
+Changes with nginx 0.8.53 18 Oct 2010
+
+ *) Feature: now the "error_page" directive allows to change a status
+ code in a redirect.
+
+ *) Feature: the "gzip_disable" directive supports special "degradation"
+ mask.
+
+ *) Bugfix: a socket leak might occurred if file AIO was used.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: if the first server had no "listen" directive and there was
+ no explicit default server, then a next server with a "listen"
+ directive became the default server; the bug had appeared in 0.8.21.
+
+
+Changes with nginx 0.8.52 28 Sep 2010
+
+ *) Bugfix: nginx used SSL mode for a listen socket if any listen option
+ was set; the bug had appeared in 0.8.51.
+
+
+Changes with nginx 0.8.51 27 Sep 2010
+
+ *) Change: the "secure_link_expires" directive has been canceled.
+
+ *) Change: a logging level of resolver errors has been lowered from
+ "alert" to "error".
+
+ *) Feature: now a listen socket "ssl" parameter may be set several
+ times.
+
+
+Changes with nginx 0.8.50 02 Sep 2010
+
+ *) Feature: the "secure_link", "secure_link_md5", and
+ "secure_link_expires" directives of the ngx_http_secure_link_module.
+
+ *) Feature: the -q switch.
+ Thanks to Gena Makhomed.
+
+ *) Bugfix: worker processes may got caught in an endless loop during
+ reconfiguration, if a caching was used; the bug had appeared in
+ 0.8.48.
+
+ *) Bugfix: in the "gzip_disable" directive.
+ Thanks to Derrick Petzold.
+
+ *) Bugfix: nginx/Windows could not send stop, quit, reopen, and reload
+ signals to a process run in other session.
+
+
+Changes with nginx 0.8.49 09 Aug 2010
+
+ *) Feature: the "image_filter_jpeg_quality" directive supports
+ variables.
+
+ *) Bugfix: a segmentation fault might occur in a worker process, if the
+ $geoip_region_name variables was used; the bug had appeared in
+ 0.8.48.
+
+ *) Bugfix: errors intercepted by error_page were cached only for next
+ request; the bug had appeared in 0.8.48.
+
+
+Changes with nginx 0.8.48 03 Aug 2010
+
+ *) Change: now the "server_name" directive default value is an empty
+ name "".
+ Thanks to Gena Makhomed.
+
+ *) Change: now the "server_name_in_redirect" directive default value is
+ "off".
+
+ *) Feature: the $geoip_dma_code, $geoip_area_code, and
+ $geoip_region_name variables.
+ Thanks to Christine McGonagle.
+
+ *) Bugfix: the "proxy_pass", "fastcgi_pass", "uwsgi_pass", and
+ "scgi_pass" directives were not inherited inside "limit_except"
+ blocks.
+
+ *) Bugfix: the "proxy_cache_min_uses", "fastcgi_cache_min_uses"
+ "uwsgi_cache_min_uses", and "scgi_cache_min_uses" directives did not
+ work; the bug had appeared in 0.8.46.
+
+ *) Bugfix: the "fastcgi_split_path_info" directive used incorrectly
+ captures, if only parts of an URI were captured.
+ Thanks to Yuriy Taraday and Frank Enderle.
+
+ *) Bugfix: the "rewrite" directive did not escape a ";" character during
+ copying from URI to query string.
+ Thanks to Daisuke Murase.
+
+ *) Bugfix: the ngx_http_image_filter_module closed a connection, if an
+ image was larger than "image_filter_buffer" size.
+
+
+Changes with nginx 0.8.47 28 Jul 2010
+
+ *) Bugfix: $request_time variable had invalid values for subrequests.
+
+ *) Bugfix: errors intercepted by error_page could not be cached.
+
+ *) Bugfix: a cache manager process may got caught in an endless loop, if
+ max_size parameter was used; the bug had appeared in 0.8.46.
+
+
+Changes with nginx 0.8.46 19 Jul 2010
+
+ *) Change: now the "proxy_no_cache", "fastcgi_no_cache",
+ "uwsgi_no_cache", and "scgi_no_cache" directives affect on a cached
+ response saving only.
+
+ *) Feature: the "proxy_cache_bypass", "fastcgi_cache_bypass",
+ "uwsgi_cache_bypass", and "scgi_cache_bypass" directives.
+
+ *) Bugfix: nginx did not free memory in cache keys zones if there was an
+ error during working with backend: the memory was freed only after
+ inactivity time or on memory low condition.
+
+
+Changes with nginx 0.8.45 13 Jul 2010
+
+ *) Feature: ngx_http_xslt_filter improvements.
+ Thanks to Laurence Rowe.
+
+ *) Bugfix: SSI response might be truncated after include with
+ wait="yes"; the bug had appeared in 0.7.25.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: the "listen" directive did not support the "setfib=0"
+ parameter.
+
+
+Changes with nginx 0.8.44 05 Jul 2010
+
+ *) Change: now nginx does not cache by default backend responses, if
+ they have a "Set-Cookie" header line.
+
+ *) Feature: the "listen" directive supports the "setfib" parameter.
+ Thanks to Andrew Filonov.
+
+ *) Bugfix: the "sub_filter" directive might change character case on
+ partial match.
+
+ *) Bugfix: compatibility with HP/UX.
+
+ *) Bugfix: compatibility with AIX xlC_r compiler.
+
+ *) Bugfix: nginx treated large SSLv2 packets as plain requests.
+ Thanks to Miroslaw Jaworski.
+
+
+Changes with nginx 0.8.43 30 Jun 2010
+
+ *) Feature: large geo ranges base loading speed-up.
+
+ *) Bugfix: an error_page redirection to "location /zero {return 204;}"
+ without changing status code kept the error body; the bug had
+ appeared in 0.8.42.
+
+ *) Bugfix: nginx might close IPv6 listen socket during reconfiguration.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: the $uid_set variable may be used at any request processing
+ stage.
+
+
+Changes with nginx 0.8.42 21 Jun 2010
+
+ *) Change: now nginx tests locations given by regular expressions, if
+ request was matched exactly by a location given by a prefix string.
+ The previous behavior has been introduced in 0.7.1.
+
+ *) Feature: the ngx_http_scgi_module.
+ Thanks to Manlio Perillo.
+
+ *) Feature: a text answer may be added to a "return" directive.
+
+
+Changes with nginx 0.8.41 15 Jun 2010
+
+ *) Security: nginx/Windows worker might be terminated abnormally if a
+ requested file name has invalid UTF-8 encoding.
+
+ *) Change: now nginx allows to use spaces in a request line.
+
+ *) Bugfix: the "proxy_redirect" directive changed incorrectly a backend
+ "Refresh" response header line.
+ Thanks to Andrey Andreew and Max Sogin.
+
+ *) Bugfix: nginx did not support path without host name in "Destination"
+ request header line.
+
+
+Changes with nginx 0.8.40 07 Jun 2010
+
+ *) Security: now nginx/Windows ignores default file stream name.
+ Thanks to Jose Antonio Vazquez Gonzalez.
+
+ *) Feature: the ngx_http_uwsgi_module.
+ Thanks to Roberto De Ioris.
+
+ *) Feature: a "fastcgi_param" directive with value starting with "HTTP_"
+ overrides a client request header line.
+
+ *) Bugfix: the "If-Modified-Since", "If-Range", etc. client request
+ header lines were passed to FastCGI-server while caching.
+
+ *) Bugfix: listen unix domain socket could not be changed during
+ reconfiguration.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.8.39 31 May 2010
+
+ *) Bugfix: an inherited "alias" directive worked incorrectly in
+ inclusive location.
+
+ *) Bugfix: in "alias" with variables and "try_files" directives
+ combination.
+
+ *) Bugfix: listen unix domain and IPv6 sockets did not inherit while
+ online upgrade.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.8.38 24 May 2010
+
+ *) Feature: the "proxy_no_cache" and "fastcgi_no_cache" directives.
+
+ *) Feature: now the "rewrite" directive does a redirect automatically if
+ the $scheme variable is used.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: now "limit_req" delay directive conforms to the described
+ algorithm.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: the $uid_got variable might not be used in the SSI and perl
+ modules.
+
+
+Changes with nginx 0.8.37 17 May 2010
+
+ *) Feature: the ngx_http_split_clients_module.
+
+ *) Feature: the "map" directive supports keys more than 255 characters.
+
+ *) Bugfix: nginx ignored the "private" and "no-store" values in the
+ "Cache-Control" backend response header line.
+
+ *) Bugfix: a "stub" parameter of an "include" SSI directive was not
+ used, if empty response has 200 status code.
+
+ *) Bugfix: if a proxied or FastCGI request was internally redirected to
+ another proxied or FastCGI location, then a segmentation fault might
+ occur in a worker process; the bug had appeared in 0.8.33.
+ Thanks to Yichun Zhang.
+
+ *) Bugfix: IMAP connections may hang until they timed out while talking
+ to Zimbra server.
+ Thanks to Alan Batie.
+
+
+Changes with nginx 0.8.36 22 Apr 2010
+
+ *) Bugfix: the ngx_http_dav_module handled incorrectly the DELETE, COPY,
+ and MOVE methods for symlinks.
+
+ *) Bugfix: values of the $query_string, $arg_..., etc. variables cached
+ in main request were used by the SSI module in subrequests.
+
+ *) Bugfix: a variable value was repeatedly encoded after each an "echo"
+ SSI-command output; the bug had appeared in 0.6.14.
+
+ *) Bugfix: a worker process hung if a FIFO file was requested.
+ Thanks to Vicente Aguilar and Maxim Dounin.
+
+ *) Bugfix: OpenSSL-1.0.0 compatibility on 64-bit Linux.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx could not be built --without-http-cache; the bug had
+ appeared in 0.8.35.
+
+
+Changes with nginx 0.8.35 01 Apr 2010
+
+ *) Change: now the charset filter runs before the SSI filter.
+
+ *) Feature: the "chunked_transfer_encoding" directive.
+
+ *) Bugfix: an "&" character was not escaped when it was copied in
+ arguments part in a rewrite rule.
+
+ *) Bugfix: nginx might be terminated abnormally while a signal
+ processing or if the directive "timer_resolution" was used on
+ platforms which do not support kqueue or eventport notification
+ methods.
+ Thanks to George Xie and Maxim Dounin.
+
+ *) Bugfix: if temporary files and permanent storage area resided at
+ different file systems, then permanent file modification times were
+ incorrect.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: ngx_http_memcached_module might issue the error message
+ "memcached sent invalid trailer".
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx could not built zlib-1.2.4 library using the library
+ sources.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a segmentation fault occurred in a worker process, if there
+ was large stderr output before FastCGI response; the bug had appeared
+ in 0.8.34.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.8.34 03 Mar 2010
+
+ *) Bugfix: nginx did not support all ciphers and digests used in client
+ certificates.
+ Thanks to Innocenty Enikeew.
+
+ *) Bugfix: nginx cached incorrectly FastCGI responses if there was large
+ stderr output before response.
+
+ *) Bugfix: nginx did not support HTTPS referrers.
+
+ *) Bugfix: nginx/Windows might not find file if path in configuration
+ was given in other character case; the bug had appeared in 0.8.33.
+
+ *) Bugfix: the $date_local variable has an incorrect value, if the "%s"
+ format was used.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: if ssl_session_cache was not set or was set to "none", then
+ during client certificate verify the error "session id context
+ uninitialized" might occur; the bug had appeared in 0.7.1.
+
+ *) Bugfix: a geo range returned default value if the range included two
+ or more /16 networks and did not begin at /16 network boundary.
+
+ *) Bugfix: a block used in a "stub" parameter of an "include" SSI
+ directive was output with "text/plain" MIME type.
+
+ *) Bugfix: $r->sleep() did not work; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.33 01 Feb 2010
+
+ *) Security: now nginx/Windows ignores trailing spaces in URI.
+ Thanks to Dan Crowley, Core Security Technologies.
+
+ *) Security: now nginx/Windows ignores short files names.
+ Thanks to Dan Crowley, Core Security Technologies.
+
+ *) Change: now keepalive connections after POST requests are not
+ disabled for MSIE 7.0+.
+ Thanks to Adam Lounds.
+
+ *) Workaround: now keepalive connections are disabled for Safari.
+ Thanks to Joshua Sierles.
+
+ *) Bugfix: if a proxied or FastCGI request was internally redirected to
+ another proxied or FastCGI location, then $upstream_response_time
+ variable may have abnormally large value; the bug had appeared in
+ 0.8.7.
+
+ *) Bugfix: a segmentation fault might occur in a worker process, while
+ discarding a request body; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.32 11 Jan 2010
+
+ *) Bugfix: UTF-8 encoding usage in the ngx_http_autoindex_module.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: regular expression named captures worked for two names only.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: now the "localhost" name is used in the "Host" request header
+ line, if an unix domain socket is defined in the "auth_http"
+ directive.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx did not support chunked transfer encoding for 201
+ responses.
+ Thanks to Julian Reich.
+
+ *) Bugfix: if the "expires modified" set date in the past, then a
+ negative number was set in the "Cache-Control" response header line.
+ Thanks to Alex Kapranoff.
+
+
+Changes with nginx 0.8.31 23 Dec 2009
+
+ *) Feature: now the "error_page" directive may redirect the 301 and 302
+ responses.
+
+ *) Feature: the $geoip_city_continent_code, $geoip_latitude, and
+ $geoip_longitude variables.
+ Thanks to Arvind Sundararajan.
+
+ *) Feature: now the ngx_http_image_filter_module deletes always EXIF and
+ other application specific data if the data consume more than 5% of a
+ JPEG file.
+
+ *) Bugfix: nginx closed a connection if a cached response had an empty
+ body.
+ Thanks to Piotr Sikora.
+
+ *) Bugfix: nginx might not be built by gcc 4.x if the -O2 or higher
+ optimization option was used.
+ Thanks to Maxim Dounin and Denis F. Latypoff.
+
+ *) Bugfix: regular expressions in location were always tested in
+ case-sensitive mode; the bug had appeared in 0.8.25.
+
+ *) Bugfix: nginx cached a 304 response if there was the "If-None-Match"
+ header line in a proxied request.
+ Thanks to Tim Dettrick and David Kostal.
+
+ *) Bugfix: nginx/Windows tried to delete a temporary file twice if the
+ file should replace an already existent file.
+
+
+Changes with nginx 0.8.30 15 Dec 2009
+
+ *) Change: now the default buffer size of the
+ "large_client_header_buffers" directive is 8K.
+ Thanks to Andrew Cholakian.
+
+ *) Feature: the conf/fastcgi.conf for simple FastCGI configurations.
+
+ *) Bugfix: nginx/Windows tried to rename a temporary file twice if the
+ file should replace an already existent file.
+
+ *) Bugfix: of "double free or corruption" error issued if host could not
+ be resolved; the bug had appeared in 0.8.22.
+ Thanks to Konstantin Svist.
+
+ *) Bugfix: in libatomic usage on some platforms.
+ Thanks to W-Mark Kubacki.
+
+
+Changes with nginx 0.8.29 30 Nov 2009
+
+ *) Change: now the "009" status code is written to an access log for
+ proxied HTTP/0.9 responses.
+
+ *) Feature: the "addition_types", "charset_types", "gzip_types",
+ "ssi_types", "sub_filter_types", and "xslt_types" directives support
+ an "*" parameter.
+
+ *) Feature: GCC 4.1+ built-in atomic operations usage.
+ Thanks to W-Mark Kubacki.
+
+ *) Feature: the --with-libatomic[=DIR] option in the configure.
+ Thanks to W-Mark Kubacki.
+
+ *) Bugfix: listen unix domain socket had limited access rights.
+
+ *) Bugfix: cached HTTP/0.9 responses were handled incorrectly.
+
+ *) Bugfix: regular expression named captures given by "?P<...>" did not
+ work in a "server_name" directive.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.8.28 23 Nov 2009
+
+ *) Bugfix: nginx could not be built with the --without-pcre parameter;
+ the bug had appeared in 0.8.25.
+
+
+Changes with nginx 0.8.27 17 Nov 2009
+
+ *) Bugfix: regular expressions did not work in nginx/Windows; the bug
+ had appeared in 0.8.25.
+
+
+Changes with nginx 0.8.26 16 Nov 2009
+
+ *) Bugfix: in captures usage in "rewrite" directive; the bug had
+ appeared in 0.8.25.
+
+ *) Bugfix: nginx could not be built without the --with-debug option; the
+ bug had appeared in 0.8.25.
+
+
+Changes with nginx 0.8.25 16 Nov 2009
+
+ *) Change: now no message is written in an error log if a variable is
+ not found by $r->variable() method.
+
+ *) Feature: the ngx_http_degradation_module.
+
+ *) Feature: regular expression named captures.
+
+ *) Feature: now URI part is not required a "proxy_pass" directive if
+ variables are used.
+
+ *) Feature: now the "msie_padding" directive works for Chrome too.
+
+ *) Bugfix: a segmentation fault occurred in a worker process on low
+ memory condition; the bug had appeared in 0.8.18.
+
+ *) Bugfix: nginx sent gzipped responses to clients those do not support
+ gzip, if "gzip_static on" and "gzip_vary off"; the bug had appeared
+ in 0.8.16.
+
+
+Changes with nginx 0.8.24 11 Nov 2009
+
+ *) Bugfix: nginx always added "Content-Encoding: gzip" response header
+ line in 304 responses sent by ngx_http_gzip_static_module.
+
+ *) Bugfix: nginx could not be built without the --with-debug option; the
+ bug had appeared in 0.8.23.
+
+ *) Bugfix: the "unix:" parameter of the "set_real_ip_from" directive
+ inherited incorrectly from previous level.
+
+ *) Bugfix: in resolving empty name.
+
+
+Changes with nginx 0.8.23 11 Nov 2009
+
+ *) Security: now SSL/TLS renegotiation is disabled.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: listen unix domain socket did not inherit while online
+ upgrade.
+
+ *) Bugfix: the "unix:" parameter of the "set_real_ip_from" directive did
+ not without yet another directive with any IP address.
+
+ *) Bugfix: segmentation fault and infinite looping in resolver.
+
+ *) Bugfix: in resolver.
+ Thanks to Artem Bokhan.
+
+
+Changes with nginx 0.8.22 03 Nov 2009
+
+ *) Feature: the "proxy_bind", "fastcgi_bind", and "memcached_bind"
+ directives.
+
+ *) Feature: the "access" and the "deny" directives support IPv6.
+
+ *) Feature: the "set_real_ip_from" directive supports IPv6 addresses in
+ request headers.
+
+ *) Feature: the "unix:" parameter of the "set_real_ip_from" directive.
+
+ *) Bugfix: nginx did not delete unix domain socket after configuration
+ testing.
+
+ *) Bugfix: nginx deleted unix domain socket while online upgrade.
+
+ *) Bugfix: the "!-x" operator did not work.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a segmentation fault might occur in a worker process, if
+ limit_rate was used in HTTPS server.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a segmentation fault might occur in a worker process while
+ $limit_rate logging.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a segmentation fault might occur in a worker process, if
+ there was no "listen" directive in "server" block; the bug had
+ appeared in 0.8.21.
+
+
+Changes with nginx 0.8.21 26 Oct 2009
+
+ *) Feature: now the "-V" switch shows TLS SNI support.
+
+ *) Feature: the "listen" directive of the HTTP module supports unix
+ domain sockets.
+ Thanks to Hongli Lai.
+
+ *) Feature: the "default_server" parameter of the "listen" directive.
+
+ *) Feature: now a "default" parameter is not required to set listen
+ socket options.
+
+ *) Bugfix: nginx did not support dates in 2038 year on 32-bit platforms;
+
+ *) Bugfix: socket leak; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.20 14 Oct 2009
+
+ *) Change: now default SSL ciphers are "HIGH:!ADH:!MD5".
+
+ *) Bugfix: the ngx_http_autoindex_module did not show the trailing slash
+ in links to a directory; the bug had appeared in 0.7.15.
+
+ *) Bugfix: nginx did not close a log file set by the --error-log-path
+ configuration option; the bug had appeared in 0.7.53.
+
+ *) Bugfix: nginx did not treat a comma as separator in the
+ "Cache-Control" backend response header line.
+
+ *) Bugfix: nginx/Windows might not create temporary file, a cache file,
+ or "proxy/fastcgi_store"d file if a worker had no enough access
+ rights for top level directories.
+
+ *) Bugfix: the "Set-Cookie" and "P3P" FastCGI response header lines were
+ not hidden while caching if no "fastcgi_hide_header" directives were
+ used with any parameters.
+
+ *) Bugfix: nginx counted incorrectly disk cache size.
+
+
+Changes with nginx 0.8.19 06 Oct 2009
+
+ *) Change: now SSLv2 protocol is disabled by default.
+
+ *) Change: now default SSL ciphers are "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM".
+
+ *) Bugfix: a "limit_req" directive did not work; the bug had appeared in
+ 0.8.18.
+
+
+Changes with nginx 0.8.18 06 Oct 2009
+
+ *) Feature: the "read_ahead" directive.
+
+ *) Feature: now several "perl_modules" directives may be used.
+
+ *) Feature: the "limit_req_log_level" and "limit_conn_log_level"
+ directives.
+
+ *) Bugfix: now "limit_req" directive conforms to the leaky bucket
+ algorithm.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx did not work on Linux/sparc.
+ Thanks to Marcus Ramberg.
+
+ *) Bugfix: nginx sent '\0' in a "Location" response header line on MKCOL
+ request.
+ Thanks to Xie Zhenye.
+
+ *) Bugfix: zero status code was logged instead of 499 status code; the
+ bug had appeared in 0.8.11.
+
+ *) Bugfix: socket leak; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.17 28 Sep 2009
+
+ *) Security: now "/../" are disabled in "Destination" request header
+ line.
+
+ *) Change: now $host variable value is always low case.
+
+ *) Feature: the $ssl_session_id variable.
+
+ *) Bugfix: socket leak; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.16 22 Sep 2009
+
+ *) Feature: the "image_filter_transparency" directive.
+
+ *) Bugfix: "addition_types" directive was incorrectly named
+ "addtion_types".
+
+ *) Bugfix: resolver cache poisoning.
+ Thanks to Matthew Dempsky.
+
+ *) Bugfix: memory leak in resolver.
+ Thanks to Matthew Dempsky.
+
+ *) Bugfix: invalid request line in $request variable was written in
+ access_log only if error_log was set to "info" or "debug" level.
+
+ *) Bugfix: in PNG alpha-channel support in the
+ ngx_http_image_filter_module.
+
+ *) Bugfix: nginx always added "Vary: Accept-Encoding" response header
+ line, if both "gzip_static" and "gzip_vary" were on.
+
+ *) Bugfix: in UTF-8 encoding support by "try_files" directive in
+ nginx/Windows.
+
+ *) Bugfix: in "post_action" directive usage; the bug had appeared in
+ 0.8.11.
+ Thanks to Igor Artemiev.
+
+
+Changes with nginx 0.8.15 14 Sep 2009
+
+ *) Security: a segmentation fault might occur in worker process while
+ specially crafted request handling.
+ Thanks to Chris Ries.
+
+ *) Bugfix: if names .domain.tld, .sub.domain.tld, and .domain-some.tld
+ were defined, then the name .sub.domain.tld was matched by
+ .domain.tld.
+
+ *) Bugfix: in transparency support in the ngx_http_image_filter_module.
+
+ *) Bugfix: in file AIO.
+
+ *) Bugfix: in X-Accel-Redirect usage; the bug had appeared in 0.8.11.
+
+ *) Bugfix: in embedded perl module; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.14 07 Sep 2009
+
+ *) Bugfix: an expired cached response might stick in the "UPDATING"
+ state.
+
+ *) Bugfix: a segmentation fault might occur in worker process, if
+ error_log was set to info or debug level.
+ Thanks to Sergey Bochenkov.
+
+ *) Bugfix: in embedded perl module; the bug had appeared in 0.8.11.
+
+ *) Bugfix: an "error_page" directive did not redirect a 413 error; the
+ bug had appeared in 0.6.10.
+
+
+Changes with nginx 0.8.13 31 Aug 2009
+
+ *) Bugfix: in the "aio sendfile" directive; the bug had appeared in
+ 0.8.12.
+
+ *) Bugfix: nginx could not be built without the --with-file-aio option
+ on FreeBSD; the bug had appeared in 0.8.12.
+
+
+Changes with nginx 0.8.12 31 Aug 2009
+
+ *) Feature: the "sendfile" parameter in the "aio" directive on FreeBSD.
+
+ *) Bugfix: in try_files; the bug had appeared in 0.8.11.
+
+ *) Bugfix: in memcached; the bug had appeared in 0.8.11.
+
+
+Changes with nginx 0.8.11 28 Aug 2009
+
+ *) Change: now directive "gzip_disable msie6" does not disable gzipping
+ for MSIE 6.0 SV1.
+
+ *) Feature: file AIO support on FreeBSD and Linux.
+
+ *) Feature: the "directio_alignment" directive.
+
+
+Changes with nginx 0.8.10 24 Aug 2009
+
+ *) Bugfix: memory leaks if GeoIP City database was used.
+
+ *) Bugfix: in copying temporary files to permanent storage area; the bug
+ had appeared in 0.8.9.
+
+
+Changes with nginx 0.8.9 17 Aug 2009
+
+ *) Feature: now the start cache loader runs in a separate process; this
+ should improve large caches handling.
+
+ *) Feature: now temporary files and permanent storage area may reside at
+ different file systems.
+
+
+Changes with nginx 0.8.8 10 Aug 2009
+
+ *) Bugfix: in handling FastCGI headers split in records.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if a request
+ was handled in two proxied or FastCGIed locations and a caching was
+ enabled in the first location; the bug had appeared in 0.8.7.
+
+
+Changes with nginx 0.8.7 27 Jul 2009
+
+ *) Change: minimum supported OpenSSL version is 0.9.7.
+
+ *) Change: the "ask" parameter of the "ssl_verify_client" directive was
+ changed to the "optional" parameter and now it checks a client
+ certificate if it was offered.
+ Thanks to Brice Figureau.
+
+ *) Feature: the $ssl_client_verify variable.
+ Thanks to Brice Figureau.
+
+ *) Feature: the "ssl_crl" directive.
+ Thanks to Brice Figureau.
+
+ *) Feature: the "proxy" parameter of the "geo" directive.
+
+ *) Feature: the "image_filter" directive supports variables for setting
+ size.
+
+ *) Bugfix: the $ssl_client_cert variable usage corrupted memory; the bug
+ had appeared in 0.7.7.
+ Thanks to Sergey Zhuravlev.
+
+ *) Bugfix: "proxy_pass_header" and "fastcgi_pass_header" directives did
+ not pass to a client the "X-Accel-Redirect", "X-Accel-Limit-Rate",
+ "X-Accel-Buffering", and "X-Accel-Charset" lines from backend
+ response header.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: in handling "Last-Modified" and "Accept-Ranges" backend
+ response header lines; the bug had appeared in 0.7.44.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: the "[alert] zero size buf" error if subrequest returns an
+ empty response; the bug had appeared in 0.8.5.
+
+
+Changes with nginx 0.8.6 20 Jul 2009
+
+ *) Feature: the ngx_http_geoip_module.
+
+ *) Bugfix: XSLT filter may fail with message "not well formed XML
+ document" for valid XML document.
+ Thanks to Kuramoto Eiji.
+
+ *) Bugfix: now in MacOSX, Cygwin, and nginx/Windows locations given by a
+ regular expression are always tested in case insensitive mode.
+
+ *) Bugfix: now nginx/Windows ignores trailing dots in URI.
+ Thanks to Hugo Leisink.
+
+ *) Bugfix: name of file specified in --conf-path was not honored during
+ installation; the bug had appeared in 0.6.6.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.8.5 13 Jul 2009
+
+ *) Bugfix: now nginx allows underscores in a request method.
+
+ *) Bugfix: a 500 error code was returned for invalid login/password
+ while HTTP Basic authentication on Windows.
+
+ *) Bugfix: ngx_http_perl_module responses did not work in subrequests.
+
+ *) Bugfix: in ngx_http_limit_req_module.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.8.4 22 Jun 2009
+
+ *) Bugfix: nginx could not be built --without-http-cache; the bug had
+ appeared in 0.8.3.
+
+
+Changes with nginx 0.8.3 19 Jun 2009
+
+ *) Feature: the $upstream_cache_status variable.
+
+ *) Bugfix: nginx could not be built on MacOSX 10.6.
+
+ *) Bugfix: nginx could not be built --without-http-cache; the bug had
+ appeared in 0.8.2.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if a backend
+ 401 error was intercepted and the backend did not set the
+ "WWW-Authenticate" response header line.
+ Thanks to Eugene Mychlo.
+
+
+Changes with nginx 0.8.2 15 Jun 2009
+
+ *) Bugfix: in open_file_cache and proxy/fastcgi cache interaction on
+ start up.
+
+ *) Bugfix: open_file_cache might cache open file descriptors too long;
+ the bug had appeared in 0.7.4.
+
+
+Changes with nginx 0.8.1 08 Jun 2009
+
+ *) Feature: the "updating" parameter in "proxy_cache_use_stale" and
+ "fastcgi_cache_use_stale" directives.
+
+ *) Bugfix: the "If-Modified-Since", "If-Range", etc. client request
+ header lines were passed to backend while caching if no
+ "proxy_set_header" directive was used with any parameters.
+
+ *) Bugfix: the "Set-Cookie" and "P3P" response header lines were not
+ hidden while caching if no "proxy_hide_header/fastcgi_hide_header"
+ directives were used with any parameters.
+
+ *) Bugfix: the ngx_http_image_filter_module did not support GIF87a
+ format.
+ Thanks to Denis Ilyinyh.
+
+ *) Bugfix: nginx could not be built modules on Solaris 10 and early; the
+ bug had appeared in 0.7.56.
+
+
+Changes with nginx 0.8.0 02 Jun 2009
+
+ *) Feature: the "keepalive_requests" directive.
+
+ *) Feature: the "limit_rate_after" directive.
+ Thanks to Ivan Debnar.
+
+ *) Bugfix: XLST filter did not work in subrequests.
+
+ *) Bugfix: in relative paths handling in nginx/Windows.
+
+ *) Bugfix: in proxy_store, fastcgi_store, proxy_cache, and fastcgi_cache
+ in nginx/Windows.
+
+ *) Bugfix: in memory allocation error handling.
+ Thanks to Maxim Dounin and Kirill A. Korinskiy.
+
+
+Changes with nginx 0.7.59 25 May 2009
+
+ *) Feature: the "proxy_cache_methods" and "fastcgi_cache_methods"
+ directives.
+
+ *) Bugfix: socket leak; the bug had appeared in 0.7.25.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if a request
+ had no body and the $request_body variable was used;
+ the bug had appeared in 0.7.58.
+
+ *) Bugfix: the SSL modules might not built on Solaris and Linux;
+ the bug had appeared in 0.7.56.
+
+ *) Bugfix: ngx_http_xslt_filter_module responses were not handled by
+ SSI, charset, and gzip filters.
+
+ *) Bugfix: a "charset" directive did not set a charset to
+ ngx_http_gzip_static_module responses.
+
+
+Changes with nginx 0.7.58 18 May 2009
+
+ *) Feature: a "listen" directive of the mail proxy module supports IPv6.
+
+ *) Feature: the "image_filter_jpeg_quality" directive.
+
+ *) Feature: the "client_body_in_single_buffer" directive.
+
+ *) Feature: the $request_body variable.
+
+ *) Bugfix: in ngx_http_autoindex_module in file name links having a ":"
+ symbol in the name.
+
+ *) Bugfix: "make upgrade" procedure did not work; the bug had appeared
+ in 0.7.53.
+ Thanks to Denis F. Latypoff.
+
+
+Changes with nginx 0.7.57 12 May 2009
+
+ *) Bugfix: a floating-point fault occurred in worker process, if the
+ ngx_http_image_filter_module errors were redirected to named
+ location; the bug had appeared in 0.7.56.
+
+
+Changes with nginx 0.7.56 11 May 2009
+
+ *) Feature: nginx/Windows supports IPv6 in a "listen" directive of the
+ HTTP module.
+
+ *) Bugfix: in ngx_http_image_filter_module.
+
+
+Changes with nginx 0.7.55 06 May 2009
+
+ *) Bugfix: the http_XXX parameters in "proxy_cache_use_stale" and
+ "fastcgi_cache_use_stale" directives did not work.
+
+ *) Bugfix: fastcgi cache did not cache header only responses.
+
+ *) Bugfix: of "select() failed (9: Bad file descriptor)" error in
+ nginx/Unix and "select() failed (10038: ...)" error in nginx/Windows.
+
+ *) Bugfix: a segmentation fault might occur in worker process, if an
+ "debug_connection" directive was used; the bug had appeared in
+ 0.7.54.
+
+ *) Bugfix: fix ngx_http_image_filter_module building errors.
+
+ *) Bugfix: the files bigger than 2G could not be transferred using
+ $r->sendfile.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.7.54 01 May 2009
+
+ *) Feature: the ngx_http_image_filter_module.
+
+ *) Feature: the "proxy_ignore_headers" and "fastcgi_ignore_headers"
+ directives.
+
+ *) Bugfix: a segmentation fault might occur in worker process, if an
+ "open_file_cache_errors off" directive was used; the bug had appeared
+ in 0.7.53.
+
+ *) Bugfix: the "port_in_redirect off" directive did not work; the bug
+ had appeared in 0.7.39.
+
+ *) Bugfix: improve handling of "select" method errors.
+
+ *) Bugfix: of "select() failed (10022: ...)" error in nginx/Windows.
+
+ *) Bugfix: in error text descriptions in nginx/Windows; the bug had
+ appeared in 0.7.53.
+
+
+Changes with nginx 0.7.53 27 Apr 2009
+
+ *) Change: now a log set by --error-log-path is created from the very
+ start-up.
+
+ *) Feature: now the start up errors and warnings are outputted to an
+ error_log and stderr.
+
+ *) Feature: the empty --prefix= configure parameter forces nginx to use
+ a directory where it was run as prefix.
+
+ *) Feature: the -p switch.
+
+ *) Feature: the -s switch on Unix platforms.
+
+ *) Feature: the -? and -h switches.
+ Thanks to Jerome Loyet.
+
+ *) Feature: now switches may be set in condensed form.
+
+ *) Bugfix: nginx/Windows did not work if configuration file was given by
+ the -c switch.
+
+ *) Bugfix: temporary files might be not removed if the "proxy_store",
+ "fastcgi_store", "proxy_cache", or "fastcgi_cache" were used.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: an incorrect value was passed to mail proxy authentication
+ server in "Auth-Method" header line; the bug had appeared
+ in 0.7.34.
+ Thanks to Simon Lecaille.
+
+ *) Bugfix: system error text descriptions were not logged on Linux;
+ the bug had appeared in 0.7.45.
+
+ *) Bugfix: the "fastcgi_cache_min_uses" directive did not work.
+ Thanks to Andrew Vorobyoff.
+
+
+Changes with nginx 0.7.52 20 Apr 2009
+
+ *) Feature: the first native Windows binary release.
+
+ *) Bugfix: in processing HEAD method while caching.
+
+ *) Bugfix: in processing the "If-Modified-Since", "If-Range", etc.
+ client request header lines while caching.
+
+ *) Bugfix: now the "Set-Cookie" and "P3P" header lines are hidden in
+ cacheable responses.
+
+ *) Bugfix: if nginx was built with the ngx_http_perl_module and with a
+ perl which supports threads, then during a master process exit the
+ message "panic: MUTEX_LOCK" might be issued.
+
+ *) Bugfix: nginx could not be built --without-http-cache; the bug had
+ appeared in 0.7.48.
+
+ *) Bugfix: nginx could not be built on platforms different from i386,
+ amd64, sparc, and ppc; the bug had appeared in 0.7.42.
+
+
+Changes with nginx 0.7.51 12 Apr 2009
+
+ *) Feature: the "try_files" directive supports a response code in the
+ fallback parameter.
+
+ *) Feature: now any response code can be used in the "return" directive.
+
+ *) Bugfix: the "error_page" directive made an external redirect without
+ query string; the bug had appeared in 0.7.44.
+
+ *) Bugfix: if servers listened on several defined explicitly addresses,
+ then virtual servers might not work; the bug had appeared in 0.7.39.
+
+
+Changes with nginx 0.7.50 06 Apr 2009
+
+ *) Bugfix: the $arg_... variables did not work; the bug had appeared in
+ 0.7.49.
+
+
+Changes with nginx 0.7.49 06 Apr 2009
+
+ *) Bugfix: a segmentation fault might occur in worker process, if the
+ $arg_... variables were used; the bug had appeared in 0.7.48.
+
+
+Changes with nginx 0.7.48 06 Apr 2009
+
+ *) Feature: the "proxy_cache_key" directive.
+
+ *) Bugfix: now nginx takes into account the "X-Accel-Expires",
+ "Expires", and "Cache-Control" header lines in a backend response.
+
+ *) Bugfix: now nginx caches responses for the GET requests only.
+
+ *) Bugfix: the "fastcgi_cache_key" directive was not inherited.
+
+ *) Bugfix: the $arg_... variables did not work with SSI subrequests.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx could not be built with uclibc library.
+ Thanks to Timothy Redaelli.
+
+ *) Bugfix: nginx could not be built on OpenBSD; the bug had
+ appeared in 0.7.46.
+
+
+Changes with nginx 0.7.47 01 Apr 2009
+
+ *) Bugfix: nginx could not be built on FreeBSD 6 and early versions; the
+ bug had appeared in 0.7.46.
+
+ *) Bugfix: nginx could not be built on MacOSX; the bug had
+ appeared in 0.7.46.
+
+ *) Bugfix: if the "max_size" parameter was set, then the cache manager
+ might purge a whole cache; the bug had appeared in 0.7.46.
+
+ *) Change: a segmentation fault might occur in worker process, if the
+ "proxy_cache"/"fastcgi_cache" and the "proxy_cache_valid"/
+ "fastcgi_cache_valid" were set on different levels; the bug had
+ appeared in 0.7.46.
+
+ *) Bugfix: a segmentation fault might occur in worker process, if a
+ request was redirected to a proxied or FastCGI server via error_page
+ or try_files; the bug had appeared in 0.7.44.
+
+
+Changes with nginx 0.7.46 30 Mar 2009
+
+ *) Bugfix: the previous release tarball was incorrect.
+
+
+Changes with nginx 0.7.45 30 Mar 2009
+
+ *) Change: now the "proxy_cache" and the "proxy_cache_valid" directives
+ can be set on different levels.
+
+ *) Change: the "clean_time" parameter of the "proxy_cache_path"
+ directive is canceled.
+
+ *) Feature: the "max_size" parameter of the "proxy_cache_path"
+ directive.
+
+ *) Feature: the ngx_http_fastcgi_module preliminary cache support.
+
+ *) Feature: now on shared memory allocation errors directive and zone
+ names are logged.
+
+ *) Bugfix: the directive "add_header last-modified ''" did not delete a
+ "Last-Modified" response header line; the bug had appeared in 0.7.44.
+
+ *) Bugfix: a relative path in the "auth_basic_user_file" directive given
+ without variables did not work; the bug had appeared in 0.7.44.
+ Thanks to Jerome Loyet.
+
+ *) Bugfix: in an "alias" directive given using variables without
+ references to captures of regular expressions; the bug had appeared
+ in 0.7.42.
+
+
+Changes with nginx 0.7.44 23 Mar 2009
+
+ *) Feature: the ngx_http_proxy_module preliminary cache support.
+
+ *) Feature: the --with-pcre option in the configure.
+
+ *) Feature: the "try_files" directive is now allowed on the server block
+ level.
+
+ *) Bugfix: the "try_files" directive handled incorrectly a query string
+ in a fallback parameter.
+
+ *) Bugfix: the "try_files" directive might test incorrectly directories.
+
+ *) Bugfix: if there was a single server for given address:port pair,
+ then captures in regular expressions in a "server_name" directive did
+ not work.
+
+
+Changes with nginx 0.7.43 18 Mar 2009
+
+ *) Bugfix: a request was handled incorrectly, if a "root" directive used
+ variables; the bug had appeared in 0.7.42.
+
+ *) Bugfix: if a server listened on wildcard address, then the
+ $server_addr variable value was "0.0.0.0"; the bug had appeared in
+ 0.7.36.
+
+
+Changes with nginx 0.7.42 16 Mar 2009
+
+ *) Change: now the "Invalid argument" error returned by
+ setsockopt(TCP_NODELAY) on Solaris, is ignored.
+
+ *) Change: now if a file specified in a "auth_basic_user_file" directive
+ is absent, then the 403 error is returned instead of the 500 one.
+
+ *) Feature: the "auth_basic_user_file" directive supports variables.
+ Thanks to Kirill A. Korinskiy.
+
+ *) Feature: the "listen" directive supports the "ipv6only" parameter.
+ Thanks to Zhang Hua.
+
+ *) Bugfix: in an "alias" directive with references to captures of
+ regular expressions; the bug had appeared in 0.7.40.
+
+ *) Bugfix: compatibility with Tru64 UNIX.
+ Thanks to Dustin Marquess.
+
+ *) Bugfix: nginx could not be built without PCRE library; the bug had
+ appeared in 0.7.41.
+
+
+Changes with nginx 0.7.41 11 Mar 2009
+
+ *) Bugfix: a segmentation fault might occur in worker process, if a
+ "server_name" or a "location" directives had captures in regular
+ expressions; the issue had appeared in 0.7.40.
+ Thanks to Vladimir Sopot.
+
+
+Changes with nginx 0.7.40 09 Mar 2009
+
+ *) Feature: the "location" directive supports captures in regular
+ expressions.
+
+ *) Feature: an "alias" directive with capture references may be used
+ inside a location given by a regular expression with captures.
+
+ *) Feature: the "server_name" directive supports captures in regular
+ expressions.
+
+ *) Workaround: the ngx_http_autoindex_module did not show the trailing
+ slash in directories on XFS filesystem; the issue had appeared in
+ 0.7.15.
+ Thanks to Dmitry Kuzmenko.
+
+
+Changes with nginx 0.7.39 02 Mar 2009
+
+ *) Bugfix: large response with SSI might hang, if gzipping was enabled;
+ the bug had appeared in 0.7.28.
+ Thanks to Artem Bokhan.
+
+ *) Bugfix: a segmentation fault might occur in worker process, if short
+ static variants are used in a "try_files" directive.
+
+
+Changes with nginx 0.7.38 23 Feb 2009
+
+ *) Feature: authentication failures logging.
+
+ *) Bugfix: name/password in auth_basic_user_file were ignored after odd
+ number of empty lines.
+ Thanks to Alexander Zagrebin.
+
+ *) Bugfix: a segmentation fault occurred in a master process, if long
+ path was used in unix domain socket; the bug had appeared in 0.7.36.
+
+
+Changes with nginx 0.7.37 21 Feb 2009
+
+ *) Bugfix: directives using upstreams did not work; the bug had appeared
+ in 0.7.36.
+
+
+Changes with nginx 0.7.36 21 Feb 2009
+
+ *) Feature: a preliminary IPv6 support; the "listen" directive of the
+ HTTP module supports IPv6.
+
+ *) Bugfix: the $ancient_browser variable did not work for browsers
+ preset by a "modern_browser" directives.
+
+
+Changes with nginx 0.7.35 16 Feb 2009
+
+ *) Bugfix: a "ssl_engine" directive did not use a SSL-accelerator for
+ asymmetric ciphers.
+ Thanks to Marcin Gozdalik.
+
+ *) Bugfix: a "try_files" directive set MIME type depending on an
+ original request extension.
+
+ *) Bugfix: "*domain.tld" names were handled incorrectly in
+ "server_name", "valid_referers", and "map" directives, if
+ ".domain.tld" and ".subdomain.domain.tld" wildcards were used;
+ the bug had appeared in 0.7.9.
+
+
+Changes with nginx 0.7.34 10 Feb 2009
+
+ *) Feature: the "off" parameter of the "if_modified_since" directive.
+
+ *) Feature: now nginx sends an HELO/EHLO command after a XCLIENT
+ command.
+ Thanks to Maxim Dounin.
+
+ *) Feature: Microsoft specific "AUTH LOGIN with User Name" mode support
+ in mail proxy server.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: in a redirect rewrite directive original arguments were
+ concatenated with new arguments by a "?" rather than an "&";
+ the bug had appeared in 0.1.18.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx could not be built on AIX.
+
+
+Changes with nginx 0.7.33 02 Feb 2009
+
+ *) Bugfix: a double response might be returned if the epoll or rtsig
+ methods are used and a redirect was returned to a request with body.
+ Thanks to Eden Li.
+
+ *) Bugfix: the $sent_http_location variable was empty for some redirects
+ types.
+
+ *) Bugfix: a segmentation fault might occur in worker process if
+ "resolver" directive was used in SMTP proxy.
+
+
+Changes with nginx 0.7.32 26 Jan 2009
+
+ *) Feature: now a directory existence testing can be set explicitly in
+ the "try_files" directive.
+
+ *) Bugfix: fastcgi_store stored files not always.
+
+ *) Bugfix: in geo ranges.
+
+ *) Bugfix: in shared memory allocations if nginx was built without
+ debugging.
+ Thanks to Andrey Kvasov.
+
+
+Changes with nginx 0.7.31 19 Jan 2009
+
+ *) Change: now the "try_files" directive tests files only and ignores
+ directories.
+
+ *) Feature: the "fastcgi_split_path_info" directive.
+
+ *) Bugfixes in an "Expect" request header line support.
+
+ *) Bugfixes in geo ranges.
+
+ *) Bugfix: in a miss case ngx_http_memcached_module returned the "END"
+ line as response body instead of default 404 page body; the bug had
+ appeared in 0.7.18.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: while SMTP proxying nginx issued message "250 2.0.0 OK"
+ instead of "235 2.0.0 OK"; the bug had appeared in 0.7.22.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.7.30 24 Dec 2008
+
+ *) Bugfix: a segmentation fault occurred in worker process, if variables
+ were used in the "fastcgi_pass" or "proxy_pass" directives and host
+ name must be resolved; the bug had appeared in 0.7.29.
+
+
+Changes with nginx 0.7.29 24 Dec 2008
+
+ *) Bugfix: the "fastcgi_pass" and "proxy_pass" directives did not
+ support variables if unix domain sockets were used.
+
+ *) Bugfixes in subrequest processing; the bugs had appeared in 0.7.25.
+
+ *) Bugfix: a "100 Continue" response was issued for HTTP/1.0 requests;
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: in memory allocation in the ngx_http_gzip_filter_module on
+ Cygwin.
+
+
+Changes with nginx 0.7.28 22 Dec 2008
+
+ *) Change: in memory allocation in the ngx_http_gzip_filter_module.
+
+ *) Change: the default "gzip_buffers" directive values have been changed
+ to 32 4k or 16 8k from 4 4k/8k.
+
+
+Changes with nginx 0.7.27 15 Dec 2008
+
+ *) Feature: the "try_files" directive.
+
+ *) Feature: variables support in the "fastcgi_pass" directive.
+
+ *) Feature: now the $geo variable may get an address from a variable.
+ Thanks to Andrei Nigmatulin.
+
+ *) Feature: now a location's modifier may be used without space before
+ name.
+
+ *) Feature: the $upstream_response_length variable.
+
+ *) Bugfix: now a "add_header" directive does not add an empty value.
+
+ *) Bugfix: if zero length static file was requested, then nginx just
+ closed connection; the bug had appeared in 0.7.25.
+
+ *) Bugfix: a MOVE method could not move file in non-existent directory.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if no one
+ named location was defined in server, but some one was used in an
+ error_page directive.
+ Thanks to Sergey Bochenkov.
+
+
+Changes with nginx 0.7.26 08 Dec 2008
+
+ *) Bugfix: in subrequest processing; the bug had appeared in 0.7.25.
+
+
+Changes with nginx 0.7.25 08 Dec 2008
+
+ *) Change: in subrequest processing.
+
+ *) Change: now POSTs without "Content-Length" header line are allowed.
+
+ *) Bugfix: now the "limit_req" and "limit_conn" directives log a
+ prohibition reason.
+
+ *) Bugfix: in the "delete" parameter of the "geo" directive.
+
+
+Changes with nginx 0.7.24 01 Dec 2008
+
+ *) Feature: the "if_modified_since" directive.
+
+ *) Bugfix: nginx did not process a FastCGI server response, if the
+ server send too many messages to stderr before response.
+
+ *) Bugfix: the "$cookie_..." variables did not work in the SSI and the
+ perl module.
+
+
+Changes with nginx 0.7.23 27 Nov 2008
+
+ *) Feature: the "delete" and "ranges" parameters in the "geo" directive.
+
+ *) Feature: speeding up loading of geo base with large number of values.
+
+ *) Feature: decrease of memory required for geo base load.
+
+
+Changes with nginx 0.7.22 20 Nov 2008
+
+ *) Feature: the "none" parameter in the "smtp_auth" directive.
+ Thanks to Maxim Dounin.
+
+ *) Feature: the "$cookie_..." variables.
+
+ *) Bugfix: the "directio" directive did not work in XFS filesystem.
+
+ *) Bugfix: the resolver did not understand big DNS responses.
+ Thanks to Zyb.
+
+
+Changes with nginx 0.7.21 11 Nov 2008
+
+ *) Changes in the ngx_http_limit_req_module.
+
+ *) Feature: the EXSLT support in the ngx_http_xslt_module.
+ Thanks to Denis F. Latypoff.
+
+ *) Workaround: compatibility with glibc 2.3.
+ Thanks to Eric Benson and Maxim Dounin.
+
+ *) Bugfix: nginx could not run on MacOSX 10.4 and earlier; the bug had
+ appeared in 0.7.6.
+
+
+Changes with nginx 0.7.20 10 Nov 2008
+
+ *) Changes in the ngx_http_gzip_filter_module.
+
+ *) Feature: the ngx_http_limit_req_module.
+
+ *) Bugfix: worker processes might exit on a SIGBUS signal on sparc and
+ ppc platforms; the bug had appeared in 0.7.3.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: the "proxy_pass http://host/some:uri" directives did not
+ work; the bug had appeared in 0.7.12.
+
+ *) Bugfix: in HTTPS mode requests might fail with the "bad write retry"
+ error.
+
+ *) Bugfix: the ngx_http_secure_link_module did not work inside
+ locations, whose names are less than 3 characters.
+
+ *) Bugfix: $server_addr variable might have no value.
+
+
+Changes with nginx 0.7.19 13 Oct 2008
+
+ *) Bugfix: version number update.
+
+
+Changes with nginx 0.7.18 13 Oct 2008
+
+ *) Change: the "underscores_in_headers" directive; now nginx does not
+ allows underscores in a client request header line names.
+
+ *) Feature: the ngx_http_secure_link_module.
+
+ *) Feature: the "real_ip_header" directive supports any header.
+
+ *) Feature: the "log_subrequest" directive.
+
+ *) Feature: the $realpath_root variable.
+
+ *) Feature: the "http_502" and "http_504" parameters of the
+ "proxy_next_upstream" directive.
+
+ *) Bugfix: the "http_503" parameter of the "proxy_next_upstream" or
+ "fastcgi_next_upstream" directives did not work.
+
+ *) Bugfix: nginx might send a "Transfer-Encoding: chunked" header line
+ for HEAD requests.
+
+ *) Bugfix: now accept threshold depends on worker_connections.
+
+
+Changes with nginx 0.7.17 15 Sep 2008
+
+ *) Feature: now the "directio" directive works on Linux.
+
+ *) Feature: the $pid variable.
+
+ *) Bugfix: the "directio" optimization that had appeared in 0.7.15 did
+ not work with open_file_cache.
+
+ *) Bugfix: the "access_log" with variables did not work on Linux; the
+ bug had appeared in 0.7.7.
+
+ *) Bugfix: the ngx_http_charset_module did not understand quoted charset
+ name received from backend.
+
+
+Changes with nginx 0.7.16 08 Sep 2008
+
+ *) Bugfix: nginx could not be built on 64-bit platforms; the bug had
+ appeared in 0.7.15.
+
+
+Changes with nginx 0.7.15 08 Sep 2008
+
+ *) Feature: the ngx_http_random_index_module.
+
+ *) Feature: the "directio" directive has been optimized for file
+ requests starting from arbitrary position.
+
+ *) Feature: the "directio" directive turns off sendfile if it is
+ necessary.
+
+ *) Feature: now nginx allows underscores in a client request header line
+ names.
+
+
+Changes with nginx 0.7.14 01 Sep 2008
+
+ *) Change: now the ssl_certificate and ssl_certificate_key directives
+ have no default values.
+
+ *) Feature: the "listen" directive supports the "ssl" parameter.
+
+ *) Feature: now nginx takes into account a time zone change while
+ reconfiguration on FreeBSD and Linux.
+
+ *) Bugfix: the "listen" directive parameters such as "backlog",
+ "rcvbuf", etc. were not set, if a default server was not the first
+ one.
+
+ *) Bugfix: if URI part captured by a "rewrite" directive was used as a
+ query string, then the query string was not escaped.
+
+ *) Bugfix: configuration file validity test improvements.
+
+
+Changes with nginx 0.7.13 26 Aug 2008
+
+ *) Bugfix: nginx could not be built on Linux and Solaris; the bug had
+ appeared in 0.7.12.
+
+
+Changes with nginx 0.7.12 26 Aug 2008
+
+ *) Feature: the "server_name" directive supports empty name "".
+
+ *) Feature: the "gzip_disable" directive supports special "msie6" mask.
+
+ *) Bugfix: if the "max_fails=0" parameter was used in upstream with
+ several servers, then a worker process exited on a SIGFPE signal.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a request body was dropped while redirection via an
+ "error_page" directive.
+
+ *) Bugfix: a full response was returned for request method HEAD while
+ redirection via an "error_page" directive.
+
+ *) Bugfix: the $r->header_in() method did not return value of the
+ "Host", "User-Agent", and "Connection" request header lines; the bug
+ had appeared in 0.7.0.
+
+
+Changes with nginx 0.7.11 18 Aug 2008
+
+ *) Change: now ngx_http_charset_module does not work by default with
+ text/css MIME type.
+
+ *) Feature: now nginx returns the 405 status code for POST method
+ requesting a static file only if the file exists.
+
+ *) Feature: the "proxy_ssl_session_reuse" directive.
+
+ *) Bugfix: a "proxy_pass" directive without URI part might use original
+ request after the "X-Accel-Redirect" redirection was used;
+
+ *) Bugfix: if a directory has search only rights and the first index
+ file was absent, then nginx returned the 500 status code.
+
+ *) Bugfix: in inclusive locations; the bugs had appeared in 0.7.1.
+
+
+Changes with nginx 0.7.10 13 Aug 2008
+
+ *) Bugfix: in the "addition_types", "charset_types", "gzip_types",
+ "ssi_types", "sub_filter_types", and "xslt_types" directives; the
+ bugs had appeared in 0.7.9.
+
+ *) Bugfix: of recursive error_page for 500 status code.
+
+ *) Bugfix: now the ngx_http_realip_module sets address not for whole
+ keepalive connection, but for each request passed via the connection.
+
+
+Changes with nginx 0.7.9 12 Aug 2008
+
+ *) Change: now ngx_http_charset_module works by default with following
+ MIME types: text/html, text/css, text/xml, text/plain,
+ text/vnd.wap.wml, application/x-javascript, and application/rss+xml.
+
+ *) Feature: the "charset_types" and "addition_types" directives.
+
+ *) Feature: now the "gzip_types", "ssi_types", and "sub_filter_types"
+ directives use hash.
+
+ *) Feature: the ngx_cpp_test_module.
+
+ *) Feature: the "expires" directive supports daily time.
+
+ *) Feature: the ngx_http_xslt_module improvements and bug fixing.
+ Thanks to Denis F. Latypoff and Maxim Dounin.
+
+ *) Bugfix: the "log_not_found" directive did not work for index files
+ tests.
+
+ *) Bugfix: HTTPS connections might hang, if kqueue, epoll, rtsig, or
+ eventport methods were used; the bug had appeared in 0.7.7.
+
+ *) Bugfix: if the "server_name", "valid_referers", and "map" directives
+ used an "*.domain.tld" wildcard and exact name "domain.tld" was not
+ set, then the exact name was matched by the wildcard; the bug had
+ appeared in 0.3.18.
+
+
+Changes with nginx 0.7.8 04 Aug 2008
+
+ *) Feature: the ngx_http_xslt_module.
+
+ *) Feature: the "$arg_..." variables.
+
+ *) Feature: Solaris directio support.
+ Thanks to Ivan Debnar.
+
+ *) Bugfix: now if FastCGI server sends a "Location" header line without
+ status line, then nginx uses 302 status code.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.7.7 30 Jul 2008
+
+ *) Change: now the EAGAIN error returned by connect() is not considered
+ as temporary error.
+
+ *) Change: now the $ssl_client_cert variable value is a certificate with
+ TAB character intended before each line except first one; an
+ unchanged certificate is available in the $ssl_client_raw_cert
+ variable.
+
+ *) Feature: the "ask" parameter in the "ssl_verify_client" directive.
+
+ *) Feature: byte-range processing improvements.
+ Thanks to Maxim Dounin.
+
+ *) Feature: the "directio" directive.
+ Thanks to Jiang Hong.
+
+ *) Feature: MacOSX 10.5 sendfile() support.
+
+ *) Bugfix: now in MacOSX and Cygwin locations are tested in case
+ insensitive mode; however, the compare is provided by single-byte
+ locales only.
+
+ *) Bugfix: mail proxy SSL connections hanged, if select, poll, or
+ /dev/poll methods were used.
+
+ *) Bugfix: UTF-8 encoding usage in the ngx_http_autoindex_module.
+
+
+Changes with nginx 0.7.6 07 Jul 2008
+
+ *) Bugfix: now if variables are used in the "access_log" directive a
+ request root existence is always tested.
+
+ *) Bugfix: the ngx_http_flv_module did not support several values in a
+ query string.
+
+
+Changes with nginx 0.7.5 01 Jul 2008
+
+ *) Bugfixes in variables support in the "access_log" directive; the bugs
+ had appeared in 0.7.4.
+
+ *) Bugfix: nginx could not be built --without-http_gzip_module; the bug
+ had appeared in 0.7.3.
+ Thanks to Kirill A. Korinskiy.
+
+ *) Bugfix: if sub_filter and SSI were used together, then responses
+ might were transferred incorrectly.
+
+
+Changes with nginx 0.7.4 30 Jun 2008
+
+ *) Feature: variables support in the "access_log" directive.
+
+ *) Feature: the "open_log_file_cache" directive.
+
+ *) Feature: the -g switch.
+
+ *) Feature: the "Expect" request header line support.
+
+ *) Bugfix: large SSI inclusions might be truncated.
+
+
+Changes with nginx 0.7.3 23 Jun 2008
+
+ *) Change: the "rss" extension MIME type has been changed to
+ "application/rss+xml".
+
+ *) Change: now the "gzip_vary" directive turned on issues a
+ "Vary: Accept-Encoding" header line for uncompressed responses too.
+
+ *) Feature: now the "rewrite" directive does a redirect automatically if
+ the "https://" protocol is used.
+
+ *) Bugfix: the "proxy_pass" directive did not work with the HTTPS
+ protocol; the bug had appeared in 0.6.9.
+
+
+Changes with nginx 0.7.2 16 Jun 2008
+
+ *) Feature: now nginx supports EDH key exchange ciphers.
+
+ *) Feature: the "ssl_dhparam" directive.
+
+ *) Feature: the $ssl_client_cert variable.
+ Thanks to Manlio Perillo.
+
+ *) Bugfix: after changing URI via a "rewrite" directive nginx did not
+ search a new location; the bug had appeared in 0.7.1.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: nginx could not be built without PCRE library; the bug had
+ appeared in 0.7.1.
+
+ *) Bugfix: when a request to a directory was redirected with the slash
+ added, nginx dropped a query string from the original request.
+
+
+Changes with nginx 0.7.1 26 May 2008
+
+ *) Change: now locations are searched in a tree.
+
+ *) Change: the "optimize_server_names" directive was canceled due to the
+ "server_name_in_redirect" directive introduction.
+
+ *) Change: some long deprecated directives are not supported anymore.
+
+ *) Change: the "none" parameter in the "ssl_session_cache" directive;
+ now this is default parameter.
+ Thanks to Rob Mueller.
+
+ *) Bugfix: worker processes might not catch reconfiguration and log
+ rotation signals.
+
+ *) Bugfix: nginx could not be built on latest Fedora 9 Linux.
+ Thanks to Roxis.
+
+
+Changes with nginx 0.7.0 19 May 2008
+
+ *) Change: now the 0x00-0x1F, '"' and '\' characters are escaped as \xXX
+ in an access_log.
+ Thanks to Maxim Dounin.
+
+ *) Change: now nginx allows several "Host" request header line.
+
+ *) Feature: the "modified" flag in the "expires" directive.
+
+ *) Feature: the $uid_got and $uid_set variables may be used at any
+ request processing stage.
+
+ *) Feature: the $hostname variable.
+ Thanks to Andrei Nigmatulin.
+
+ *) Feature: DESTDIR support.
+ Thanks to Todd A. Fisher and Andras Voroskoi.
+
+ *) Bugfix: a segmentation fault might occur in worker process on Linux,
+ if keepalive was enabled.
+
+
+Changes with nginx 0.6.31 12 May 2008
+
+ *) Bugfix: nginx did not process FastCGI response if header was at the
+ end of FastCGI record; the bug had appeared in 0.6.2.
+ Thanks to Sergey Serov.
+
+ *) Bugfix: a segmentation fault might occur in worker process if a file
+ was deleted and the "open_file_cache_errors" directive was off.
+
+
+Changes with nginx 0.6.30 29 Apr 2008
+
+ *) Change: now if an "include" directive pattern does not match any
+ file, then nginx does not issue an error.
+
+ *) Feature: now the time in directives may be specified without spaces,
+ for example, "1h50m".
+
+ *) Bugfix: memory leaks if the "ssl_verify_client" directive was on.
+ Thanks to Chavelle Vincent.
+
+ *) Bugfix: the "sub_filter" directive might set text to change into
+ output.
+
+ *) Bugfix: the "error_page" directive did not take into account
+ arguments in redirected URI.
+
+ *) Bugfix: now nginx always opens files in binary mode under Cygwin.
+
+ *) Bugfix: nginx could not be built on OpenBSD; the bug had appeared in
+ 0.6.15.
+
+
+Changes with nginx 0.6.29 18 Mar 2008
+
+ *) Feature: the ngx_google_perftools_module.
+
+ *) Bugfix: the ngx_http_perl_module could not be built on 64-bit
+ platforms; the bug had appeared in 0.6.27.
+
+
+Changes with nginx 0.6.28 13 Mar 2008
+
+ *) Bugfix: the rtsig method could not be built; the bug had appeared in
+ 0.6.27.
+
+
+Changes with nginx 0.6.27 12 Mar 2008
+
+ *) Change: now by default the rtsig method is not built on
+ Linux 2.6.18+.
+
+ *) Change: now a request method is not changed while redirection to a
+ named location via an "error_page" directive.
+
+ *) Feature: the "resolver" and "resolver_timeout" directives in SMTP
+ proxy.
+
+ *) Feature: the "post_action" directive supports named locations.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if a request
+ was redirected from proxy, FastCGI, or memcached location to static
+ named locations.
+
+ *) Bugfix: browsers did not repeat SSL handshake if there is no valid
+ client certificate in first handshake.
+ Thanks to Alexander V. Inyukhin.
+
+ *) Bugfix: if response code 495-497 was redirected via an "error_page"
+ directive without code change, then nginx tried to allocate too many
+ memory.
+
+ *) Bugfix: memory leak in long-lived non buffered connections.
+
+ *) Bugfix: memory leak in resolver.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if a request
+ was redirected from proxy, FastCGI, or memcached location to static
+ named locations.
+
+ *) Bugfix: in the $proxy_host and $proxy_port variables caching.
+ Thanks to Sergey Bochenkov.
+
+ *) Bugfix: a "proxy_pass" directive with variables used incorrectly the
+ same port as in another "proxy_pass" directive with the same host
+ name and without variables.
+ Thanks to Sergey Bochenkov.
+
+ *) Bugfix: an alert "sendmsg() failed (9: Bad file descriptor)" on some
+ 64-bit platforms while reconfiguration.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if empty
+ stub block was used second time in SSI.
+
+ *) Bugfix: in copying URI part contained escaped symbols into arguments.
+
+
+Changes with nginx 0.6.26 11 Feb 2008
+
+ *) Bugfix: the "proxy_store" and "fastcgi_store" directives did not
+ check a response length.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if big value
+ was used in a "expires" directive.
+ Thanks to Joaquin Cuenca Abela.
+
+ *) Bugfix: nginx incorrectly detected cache line size on Pentium 4.
+ Thanks to Gena Makhomed.
+
+ *) Bugfix: in proxied or FastCGI subrequests a client original method
+ was used instead of the GET method.
+
+ *) Bugfix: socket leak in HTTPS mode if deferred accept was used.
+ Thanks to Ben Maurer.
+
+ *) Bugfix: nginx issued the bogus error message "SSL_shutdown() failed
+ (SSL: )"; the bug had appeared in 0.6.23.
+
+ *) Bugfix: in HTTPS mode requests might fail with the "bad write retry"
+ error; the bug had appeared in 0.6.23.
+
+
+Changes with nginx 0.6.25 08 Jan 2008
+
+ *) Change: now the "server_name_in_redirect" directive is used instead
+ of the "server_name" directive's special "*" parameter.
+
+ *) Change: now wildcard and regex names can be used as main name in a
+ "server_name" directive.
+
+ *) Change: the "satisfy_any" directive was replaced by the "satisfy"
+ directive.
+
+ *) Workaround: old worker processes might hog CPU after reconfiguration
+ if they was run under Linux OpenVZ.
+
+ *) Feature: the "min_delete_depth" directive.
+
+ *) Bugfix: the COPY and MOVE methods did not work with single files.
+
+ *) Bugfix: the ngx_http_gzip_static_module did not allow the
+ ngx_http_dav_module to work; the bug had appeared in 0.6.23.
+
+ *) Bugfix: socket leak in HTTPS mode if deferred accept was used.
+ Thanks to Ben Maurer.
+
+ *) Bugfix: nginx could not be built without PCRE library; the bug had
+ appeared in 0.6.23.
+
+
+Changes with nginx 0.6.24 27 Dec 2007
+
+ *) Bugfix: a segmentation fault might occur in worker process if HTTPS
+ was used; the bug had appeared in 0.6.23.
+
+
+Changes with nginx 0.6.23 27 Dec 2007
+
+ *) Change: the "off" parameter in the "ssl_session_cache" directive; now
+ this is default parameter.
+
+ *) Change: the "open_file_cache_retest" directive was renamed to the
+ "open_file_cache_valid".
+
+ *) Feature: the "open_file_cache_min_uses" directive.
+
+ *) Feature: the ngx_http_gzip_static_module.
+
+ *) Feature: the "gzip_disable" directive.
+
+ *) Feature: the "memcached_pass" directive may be used inside the "if"
+ block.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if the
+ "memcached_pass" and "if" directives were used in the same location.
+
+ *) Bugfix: if a "satisfy_any on" directive was used and not all access
+ and auth modules directives were set, then other given access and
+ auth directives were not tested;
+
+ *) Bugfix: regex parameters in a "valid_referers" directive were not
+ inherited from previous level.
+
+ *) Bugfix: a "post_action" directive did run if a request was completed
+ with 499 status code.
+
+ *) Bugfix: optimization of 16K buffer usage in a SSL connection.
+ Thanks to Ben Maurer.
+
+ *) Bugfix: the STARTTLS in SMTP mode did not work.
+ Thanks to Oleg Motienko.
+
+ *) Bugfix: in HTTPS mode requests might fail with the "bad write retry"
+ error; the bug had appeared in 0.5.13.
+
+
+Changes with nginx 0.6.22 19 Dec 2007
+
+ *) Change: now all ngx_http_perl_module methods return values copied to
+ perl's allocated memory.
+
+ *) Bugfix: if nginx was built with ngx_http_perl_module, the perl before
+ 5.8.6 was used, and perl supported threads, then during
+ reconfiguration the master process aborted; the bug had appeared in
+ 0.5.9.
+ Thanks to Boris Zhmurov.
+
+ *) Bugfix: the ngx_http_perl_module methods may get invalid values of
+ the regex captures.
+
+ *) Bugfix: a segmentation fault occurred in worker process, if the
+ $r->has_request_body() method was called for a request whose small
+ request body was already received.
+
+ *) Bugfix: large_client_header_buffers did not freed before going to
+ keep-alive state.
+ Thanks to Olexander Shtepa.
+
+ *) Bugfix: the last address was missed in the $upstream_addr variable;
+ the bug had appeared in 0.6.18.
+
+ *) Bugfix: the "fastcgi_catch_stderr" directive did return error code;
+ now it returns 502 code, that can be rerouted to a next server using
+ the "fastcgi_next_upstream invalid_header" directive.
+
+ *) Bugfix: a segmentation fault occurred in master process if the
+ "fastcgi_catch_stderr" directive was used; the bug had appeared in
+ 0.6.10.
+ Thanks to Manlio Perillo.
+
+
+Changes with nginx 0.6.21 03 Dec 2007
+
+ *) Change: if variable values used in a "proxy_pass" directive contain
+ IP-addresses only, then a "resolver" directive is not mandatory.
+
+ *) Bugfix: a segmentation fault might occur in worker process if a
+ "proxy_pass" directive with URI-part was used; the bug had appeared
+ in 0.6.19.
+
+ *) Bugfix: if resolver was used on platform that does not support
+ kqueue, then nginx issued an alert "name is out of response".
+ Thanks to Andrei Nigmatulin.
+
+ *) Bugfix: if the $server_protocol was used in FastCGI parameters and a
+ request line length was near to the "client_header_buffer_size"
+ directive value, then nginx issued an alert "fastcgi: the request
+ record is too big".
+
+ *) Bugfix: if a plain text HTTP/0.9 version request was made to HTTPS
+ server, then nginx returned usual response.
+
+
+Changes with nginx 0.6.20 28 Nov 2007
+
+ *) Bugfix: a segmentation fault might occur in worker process if a
+ "proxy_pass" directive with URI-part was used; the bug had appeared
+ in 0.6.19.
+
+
+Changes with nginx 0.6.19 27 Nov 2007
+
+ *) Bugfix: the 0.6.18 version could not be built.
+
+
+Changes with nginx 0.6.18 27 Nov 2007
+
+ *) Change: now the ngx_http_userid_module adds start time microseconds
+ to the cookie field contains a pid value.
+
+ *) Change: now the full request line instead of URI only is written to
+ error_log.
+
+ *) Feature: variables support in the "proxy_pass" directive.
+
+ *) Feature: the "resolver" and "resolver_timeout" directives.
+
+ *) Feature: now the directive "add_header last-modified ''" deletes a
+ "Last-Modified" response header line.
+
+ *) Bugfix: the "limit_rate" directive did not allow to use full
+ throughput, even if limit value was very high.
+
+
+Changes with nginx 0.6.17 15 Nov 2007
+
+ *) Feature: the "If-Range" request header line support.
+ Thanks to Alexander V. Inyukhin.
+
+ *) Bugfix: URL double escaping in a redirect of the "msie_refresh"
+ directive; the bug had appeared in 0.6.4.
+
+ *) Bugfix: the "autoindex" directive did not work with the "alias /"
+ directive.
+
+ *) Bugfix: a segmentation fault might occur in worker process if
+ subrequests were used.
+
+ *) Bugfix: the big responses may be transferred truncated if SSL and
+ gzip were used.
+
+ *) Bugfix: the $status variable was equal to 0 if a proxied server
+ returned response in HTTP/0.9 version.
+
+
+Changes with nginx 0.6.16 29 Oct 2007
+
+ *) Change: now the uname(2) is used on Linux instead of procfs.
+ Thanks to Ilya Novikov.
+
+ *) Bugfix: if the "?" character was in a "error_page" directive, then it
+ was escaped in a proxied request; the bug had appeared in 0.6.11.
+
+ *) Bugfix: compatibility with mget.
+
+
+Changes with nginx 0.6.15 22 Oct 2007
+
+ *) Feature: Cygwin compatibility.
+ Thanks to Vladimir Kutakov.
+
+ *) Feature: the "merge_slashes" directive.
+
+ *) Feature: the "gzip_vary" directive.
+
+ *) Feature: the "server_tokens" directive.
+
+ *) Bugfix: nginx did not unescape URI in the "include" SSI command.
+
+ *) Bugfix: the segmentation fault was occurred on start or while
+ reconfiguration if variable was used in the "charset" or
+ "source_charset" directives.
+
+ *) Bugfix: nginx returned the 400 response on requests like
+ "GET http://www.domain.com HTTP/1.0".
+ Thanks to James Oakley.
+
+ *) Bugfix: if request with request body was redirected using the
+ "error_page" directive, then nginx tried to read the request body
+ again; the bug had appeared in 0.6.7.
+
+ *) Bugfix: a segmentation fault occurred in worker process if no
+ server_name was explicitly defined for server processing request; the
+ bug had appeared in 0.6.7.
+
+
+Changes with nginx 0.6.14 15 Oct 2007
+
+ *) Change: now by default the "echo" SSI command uses entity encoding.
+
+ *) Feature: the "encoding" parameter in the "echo" SSI command.
+
+ *) Feature: the "access_log" directive may be used inside the
+ "limit_except" block.
+
+ *) Bugfix: if all upstream servers were failed, then all servers had got
+ weight the was equal one until servers became alive; the bug had
+ appeared in 0.6.6.
+
+ *) Bugfix: a segmentation fault occurred in worker process if
+ $date_local and $date_gmt were used outside the
+ ngx_http_ssi_filter_module.
+
+ *) Bugfix: a segmentation fault might occur in worker process if debug
+ log was enabled.
+ Thanks to Andrei Nigmatulin.
+
+ *) Bugfix: ngx_http_memcached_module did not set
+ $upstream_response_time.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a worker process may got caught in an endless loop, if the
+ memcached was used.
+
+ *) Bugfix: nginx supported low case only "close" and "keep-alive" values
+ in the "Connection" request header line; the bug had appeared in
+ 0.6.11.
+
+ *) Bugfix: sub_filter did not work with empty substitution.
+
+ *) Bugfix: in sub_filter parsing.
+
+
+Changes with nginx 0.6.13 24 Sep 2007
+
+ *) Bugfix: nginx did not close directory file on HEAD request if
+ autoindex was used.
+ Thanks to Arkadiusz Patyk.
+
+
+Changes with nginx 0.6.12 21 Sep 2007
+
+ *) Change: mail proxy was split on three modules: pop3, imap and smtp.
+
+ *) Feature: the --without-mail_pop3_module, --without-mail_imap_module,
+ and --without-mail_smtp_module configuration parameters.
+
+ *) Feature: the "smtp_greeting_delay" and "smtp_client_buffer"
+ directives of the ngx_mail_smtp_module.
+
+ *) Bugfix: the trailing wildcards did not work; the bug had appeared in
+ 0.6.9.
+
+ *) Bugfix: nginx could not start on Solaris if the shared PCRE library
+ located in non-standard place was used.
+
+ *) Bugfix: the "proxy_hide_header" and "fastcgi_hide_header" directives
+ did not hide response header lines whose name was longer than 32
+ characters.
+ Thanks to Manlio Perillo.
+
+
+Changes with nginx 0.6.11 11 Sep 2007
+
+ *) Bugfix: active connection counter always increased if mail proxy was
+ used.
+
+ *) Bugfix: if backend returned response header only using non-buffered
+ proxy, then nginx closed backend connection on timeout.
+
+ *) Bugfix: nginx did not support several "Connection" request header
+ lines.
+
+ *) Bugfix: if the "max_fails" was set for upstream server, then after
+ first failure server weight was always one; the bug had appeared in
+ 0.6.6.
+
+
+Changes with nginx 0.6.10 03 Sep 2007
+
+ *) Feature: the "open_file_cache", "open_file_cache_retest", and
+ "open_file_cache_errors" directives.
+
+ *) Bugfix: socket leak; the bug had appeared in 0.6.7.
+
+ *) Bugfix: a charset set by the "charset" directive was not appended to
+ the "Content-Type" header set by $r->send_http_header().
+
+ *) Bugfix: a segmentation fault might occur in worker process if
+ /dev/poll method was used.
+
+
+Changes with nginx 0.6.9 28 Aug 2007
+
+ *) Bugfix: a worker process may got caught in an endless loop, if the
+ HTTPS protocol was used; the bug had appeared in 0.6.7.
+
+ *) Bugfix: if server listened on two addresses or ports and trailing
+ wildcard was used, then nginx did not run.
+
+ *) Bugfix: the "ip_hash" directive might incorrectly mark servers as
+ down.
+
+ *) Bugfix: nginx could not be built on amd64; the bug had appeared in
+ 0.6.8.
+
+
+Changes with nginx 0.6.8 20 Aug 2007
+
+ *) Change: now nginx tries to set the "worker_priority",
+ "worker_rlimit_nofile", "worker_rlimit_core", and
+ "worker_rlimit_sigpending" without super-user privileges.
+
+ *) Change: now nginx escapes space and "%" in request to a mail proxy
+ authentication server.
+
+ *) Change: now nginx escapes "%" in $memcached_key variable.
+
+ *) Bugfix: nginx used path relative to configuration prefix for
+ non-absolute configuration file path specified in the "-c" key; the
+ bug had appeared in 0.6.6.
+
+ *) Bugfix: nginx did not work on FreeBSD/sparc64.
+
+
+Changes with nginx 0.6.7 15 Aug 2007
+
+ *) Change: now the paths specified in the "include",
+ "auth_basic_user_file", "perl_modules", "ssl_certificate",
+ "ssl_certificate_key", and "ssl_client_certificate" directives are
+ relative to directory of nginx configuration file nginx.conf, but not
+ to nginx prefix directory.
+
+ *) Change: the --sysconfdir=PATH option in configure was canceled.
+
+ *) Change: the special make target "upgrade1" was defined for online
+ upgrade of 0.1.x versions.
+
+ *) Feature: the "server_name" and "valid_referers" directives support
+ regular expressions.
+
+ *) Feature: the "server" directive in the "upstream" context supports
+ the "backup" parameter.
+
+ *) Feature: the ngx_http_perl_module supports the
+ $r->discard_request_body.
+
+ *) Feature: the "add_header Last-Modified ..." directive changes the
+ "Last-Modified" response header line.
+
+ *) Bugfix: if a response different than 200 was returned to a request
+ with body and connection went to the keep-alive state after the
+ request, then nginx returned 400 for the next request.
+
+ *) Bugfix: a segmentation fault occurred in worker process if invalid
+ address was set in the "auth_http" directive.
+
+ *) Bugfix: now nginx uses default listen backlog value 511 on all
+ platforms except FreeBSD.
+ Thanks to Jiang Hong.
+
+ *) Bugfix: a worker process may got caught in an endless loop, if a
+ "server" inside "upstream" block was marked as "down"; the bug had
+ appeared in 0.6.6.
+
+ *) Bugfix: now Solaris sendfilev() is not used to transfer the client
+ request body to FastCGI-server via the unix domain socket.
+
+
+Changes with nginx 0.6.6 30 Jul 2007
+
+ *) Feature: the --sysconfdir=PATH option in configure.
+
+ *) Feature: named locations.
+
+ *) Feature: the $args variable can be set with the "set" directive.
+
+ *) Feature: the $is_args variable.
+
+ *) Bugfix: fair big weight upstream balancer.
+
+ *) Bugfix: if a client has closed connection to mail proxy then nginx
+ might not close connection to backend.
+
+ *) Bugfix: if the same host without specified port was used as backend
+ for HTTP and HTTPS, then nginx used only one port - 80 or 443.
+
+ *) Bugfix: fix building on Solaris/amd64 by Sun Studio 11 and early
+ versions; the bug had appeared in 0.6.4.
+
+
+Changes with nginx 0.6.5 23 Jul 2007
+
+ *) Feature: $nginx_version variable.
+ Thanks to Nick S. Grechukh.
+
+ *) Feature: the mail proxy supports AUTHENTICATE in IMAP mode.
+ Thanks to Maxim Dounin.
+
+ *) Feature: the mail proxy supports STARTTLS in SMTP mode.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: now nginx escapes space in $memcached_key variable.
+
+ *) Bugfix: nginx was incorrectly built by Sun Studio on Solaris/amd64.
+ Thanks to Jiang Hong.
+
+ *) Bugfix: of minor potential bugs.
+ Thanks to Coverity's Scan.
+
+
+Changes with nginx 0.6.4 17 Jul 2007
+
+ *) Security: the "msie_refresh" directive allowed XSS.
+ Thanks to Maxim Boguk.
+
+ *) Change: the "proxy_store" and "fastcgi_store" directives were
+ changed.
+
+ *) Feature: the "proxy_store_access" and "fastcgi_store_access"
+ directives.
+
+ *) Bugfix: nginx did not work on Solaris/sparc64 if it was built by Sun
+ Studio.
+ Thanks to Andrei Nigmatulin.
+
+ *) Workaround: for Sun Studio 12.
+ Thanks to Jiang Hong.
+
+
+Changes with nginx 0.6.3 12 Jul 2007
+
+ *) Feature: the "proxy_store" and "fastcgi_store" directives.
+
+ *) Bugfix: a segmentation fault might occur in worker process if the
+ "auth_http_header" directive was used.
+ Thanks to Maxim Dounin.
+
+ *) Bugfix: a segmentation fault occurred in worker process if the
+ CRAM-MD5 authentication method was used, but it was not enabled.
+
+ *) Bugfix: a segmentation fault might occur in worker process when the
+ HTTPS protocol was used in the "proxy_pass" directive.
+
+ *) Bugfix: a segmentation fault might occur in worker process if the
+ eventport method was used.
+
+ *) Bugfix: the "proxy_ignore_client_abort" and
+ "fastcgi_ignore_client_abort" directives did not work; the bug had
+ appeared in 0.5.13.
+
+
+Changes with nginx 0.6.2 09 Jul 2007
+
+ *) Bugfix: if the FastCGI header was split in records, then nginx passed
+ garbage in the header to a client.
+
+
+Changes with nginx 0.6.1 17 Jun 2007
+
+ *) Bugfix: in SSI parsing.
+
+ *) Bugfix: if remote SSI subrequest was used, then posterior local file
+ subrequest might transferred to client in wrong order.
+
+ *) Bugfix: large SSI inclusions buffered in temporary files were
+ truncated.
+
+ *) Bugfix: the perl $$ variable value in ngx_http_perl_module was equal
+ to the master process identification number.
+
+
+Changes with nginx 0.6.0 14 Jun 2007
+
+ *) Feature: the "server_name", "map", and "valid_referers" directives
+ support the "www.example.*" wildcards.
+
+
+Changes with nginx 0.5.25 11 Jun 2007
+
+ *) Bugfix: nginx could not be built with the
+ --without-http_rewrite_module parameter; the bug had appeared in
+ 0.5.24.
+
+
+Changes with nginx 0.5.24 06 Jun 2007
+
+ *) Security: the "ssl_verify_client" directive did not work if request
+ was made using HTTP/0.9.
+
+ *) Bugfix: a part of response body might be passed uncompressed if gzip
+ was used; the bug had appeared in 0.5.23.
+
+
+Changes with nginx 0.5.23 04 Jun 2007
+
+ *) Feature: the ngx_http_ssl_module supports Server Name Indication TLS
+ extension.
+
+ *) Feature: the "fastcgi_catch_stderr" directive.
+ Thanks to Nick S. Grechukh, OWOX project.
+
+ *) Bugfix: a segmentation fault occurred in master process if two
+ virtual servers should bind() to the overlapping ports.
+
+ *) Bugfix: if nginx was built with ngx_http_perl_module and perl
+ supported threads, then during second reconfiguration the error
+ messages "panic: MUTEX_LOCK" and "perl_parse() failed" were issued.
+
+ *) Bugfix: in the HTTPS protocol in the "proxy_pass" directive.
+
+
+Changes with nginx 0.5.22 29 May 2007
+
+ *) Bugfix: a big request body might not be passed to backend; the bug
+ had appeared in 0.5.21.
+
+
+Changes with nginx 0.5.21 28 May 2007
+
+ *) Bugfix: if server has more than about ten locations, then regex
+ locations might be choosen not in that order as they were specified.
+
+ *) Bugfix: a worker process may got caught in an endless loop on 64-bit
+ platform, if the 33-rd or next in succession backend has failed.
+ Thanks to Anton Povarov.
+
+ *) Bugfix: a bus error might occur on Solaris/sparc64 if the PCRE
+ library was used.
+ Thanks to Andrei Nigmatulin.
+
+ *) Bugfix: in the HTTPS protocol in the "proxy_pass" directive.
+
+
+Changes with nginx 0.5.20 07 May 2007
+
+ *) Feature: the "sendfile_max_chunk" directive.
+
+ *) Feature: the "$http_...", "$sent_http_...", and "$upstream_http_..."
+ variables may be changed using the "set" directive.
+
+ *) Bugfix: a segmentation fault might occur in worker process if the SSI
+ command 'if expr="$var = /"' was used.
+
+ *) Bugfix: trailing boundary of multipart range response was transferred
+ incorrectly.
+ Thanks to Evan Miller.
+
+ *) Bugfix: nginx did not work on Solaris/sparc64 if it was built by Sun
+ Studio.
+ Thanks to Andrei Nigmatulin.
+
+ *) Bugfix: the ngx_http_perl_module could not be built by Solaris make.
+ Thanks to Andrei Nigmatulin.
+
+
+Changes with nginx 0.5.19 24 Apr 2007
+
+ *) Change: now the $request_time variable has millisecond precision.
+
+ *) Change: the method $r->rflush of ngx_http_perl_module was renamed to
+ the $r->flush.
+
+ *) Feature: the $upstream_addr variable.
+
+ *) Feature: the "proxy_headers_hash_max_size" and
+ "proxy_headers_hash_bucket_size" directives.
+ Thanks to Volodymyr Kostyrko.
+
+ *) Bugfix: the files more than 2G could not be transferred using
+ sendfile and limit_rate on 64-bit platforms.
+
+ *) Bugfix: the files more than 2G could not be transferred using
+ sendfile on 64-bit Linux.
+
+
+Changes with nginx 0.5.18 19 Apr 2007
+
+ *) Feature: the ngx_http_sub_filter_module.
+
+ *) Feature: the "$upstream_http_..." variables.
+
+ *) Feature: now the $upstream_status and $upstream_response_time
+ variables keep data about all upstreams before X-Accel-Redirect.
+
+ *) Bugfix: a segmentation fault occurred in master process after first
+ reconfiguration and receiving any signal if nginx was built with
+ ngx_http_perl_module and perl did not support multiplicity; the bug
+ had appeared in 0.5.9.
+
+ *) Bugfix: if perl did not support multiplicity, then after
+ reconfiguration perl code did not work; the bug had appeared in
+ 0.3.38.
+
+
+Changes with nginx 0.5.17 02 Apr 2007
+
+ *) Change: now nginx always returns the 405 status for the TRACE method.
+
+ *) Feature: now nginx supports the "include" directive inside the
+ "types" block.
+
+ *) Bugfix: the $document_root variable usage in the "root" and "alias"
+ directives is disabled: this caused recursive stack overflow.
+
+ *) Bugfix: in the HTTPS protocol in the "proxy_pass" directive.
+
+ *) Bugfix: in some cases non-cachable variables (such as $uri variable)
+ returned old cached value.
+
+
+Changes with nginx 0.5.16 26 Mar 2007
+
+ *) Bugfix: the C-class network was not used as hash key in the "ip_hash"
+ directive.
+ Thanks to Pavel Yarkovoy.
+
+ *) Bugfix: a segmentation fault might occur in worker process if a
+ charset was set in the "Content-Type" header line and the line has
+ trailing ";"; the bug had appeared in 0.3.50.
+
+ *) Bugfix: the "[alert] zero size buf" error when FastCGI server was
+ used and a request body written in a temporary file was multiple of
+ 32K.
+
+ *) Bugfix: nginx could not be built on Solaris without the --with-debug
+ option; the bug had appeared in 0.5.15.
+
+
+Changes with nginx 0.5.15 19 Mar 2007
+
+ *) Feature: the mail proxy supports authenticated SMTP proxying and the
+ "smtp_auth", "smtp_capablities", and "xclient" directives.
+ Thanks to Anton Yuzhaninov and Maxim Dounin.
+
+ *) Feature: now the keep-alive connections are closed just after
+ receiving the reconfiguration signal.
+
+ *) Change: the "imap" and "auth" directives were renamed to the "mail"
+ and "pop3_auth" directives.
+
+ *) Bugfix: a segmentation fault occurred in worker process if the
+ CRAM-MD5 authentication method was used and the APOP method was
+ disabled.
+
+ *) Bugfix: if the "starttls only" directive was used in POP3 protocol,
+ then nginx allowed authentication without switching to the SSL mode.
+
+ *) Bugfix: worker processes did not exit after reconfiguration and did
+ not rotate logs if the eventport method was used.
+
+ *) Bugfix: a worker process may got caught in an endless loop, if the
+ "ip_hash" directive was used.
+
+ *) Bugfix: now nginx does not log some alerts if eventport or /dev/poll
+ methods are used.
+
+
+Changes with nginx 0.5.14 23 Feb 2007
+
+ *) Bugfix: nginx ignored superfluous closing "}" in the end of
+ configuration file.
+
+
+Changes with nginx 0.5.13 19 Feb 2007
+
+ *) Feature: the COPY and MOVE methods.
+
+ *) Bugfix: the ngx_http_realip_module set garbage for requests passed
+ via keep-alive connection.
+
+ *) Bugfix: nginx did not work on big-endian 64-bit Linux.
+ Thanks to Andrei Nigmatulin.
+
+ *) Bugfix: now when IMAP/POP3 proxy receives too long command it closes
+ the connection right away, but not after timeout.
+
+ *) Bugfix: if the "epoll" method was used and a client closed a
+ connection prematurely, then nginx closed the connection after a send
+ timeout only.
+
+ *) Bugfix: nginx could not be built on platforms different from i386,
+ amd64, sparc, and ppc; the bug had appeared in 0.5.8.
+
+
+Changes with nginx 0.5.12 12 Feb 2007
+
+ *) Bugfix: nginx could not be built on platforms different from i386,
+ amd64, sparc, and ppc; the bug had appeared in 0.5.8.
+
+ *) Bugfix: a segmentation fault might occur in worker process if the
+ temporary files were used while working with FastCGI server; the bug
+ had appeared in 0.5.8.
+
+ *) Bugfix: a segmentation fault might occur in worker process if the
+ $fastcgi_script_name variable was logged.
+
+ *) Bugfix: ngx_http_perl_module could not be built on Solaris.
+
+
+Changes with nginx 0.5.11 05 Feb 2007
+
+ *) Feature: now configure detects system PCRE library in MacPorts.
+ Thanks to Chris McGrath.
+
+ *) Bugfix: the response was incorrect if several ranges were requested;
+ the bug had appeared in 0.5.6.
+
+ *) Bugfix: the "create_full_put_path" directive could not create the
+ intermediate directories if no "dav_access" directive was set.
+ Thanks to Evan Miller.
+
+ *) Bugfix: the "0" response code might be logged in the access_log
+ instead of the "400" and "408" error codes.
+
+ *) Bugfix: a segmentation fault might occur in worker process if nginx
+ was built with -O2 optimization.
+
+
+Changes with nginx 0.5.10 26 Jan 2007
+
+ *) Bugfix: while online executable file upgrade the new master process
+ did not inherit the listening sockets; the bug had appeared in 0.5.9.
+
+ *) Bugfix: a segmentation fault might occur in worker process if nginx
+ was built with -O2 optimization; the bug had appeared in 0.5.1.
+
+
+Changes with nginx 0.5.9 25 Jan 2007
+
+ *) Change: now the ngx_http_memcached_module uses the $memcached_key
+ variable value as a key.
+
+ *) Feature: the $memcached_key variable.
+
+ *) Feature: the "clean" parameter in the "client_body_in_file_only"
+ directive.
+
+ *) Feature: the "env" directive.
+
+ *) Feature: the "sendfile" directive is available inside the "if" block.
+
+ *) Feature: now on failure of the writing to access nginx logs a message
+ to error_log, but not more often than once a minute.
+
+ *) Bugfix: the "access_log off" directive did not always turn off the
+ logging.
+
+
+Changes with nginx 0.5.8 19 Jan 2007
+
+ *) Bugfix: a segmentation fault might occur if
+ "client_body_in_file_only on" was used and a request body was small.
+
+ *) Bugfix: a segmentation fault occurred if
+ "client_body_in_file_only on" and "proxy_pass_request_body off" or
+ "fastcgi_pass_request_body off" directives were used, and nginx
+ switched to a next upstream.
+
+ *) Bugfix: if the "proxy_buffering off" directive was used and a client
+ connection was non-active, then the connection was closed after send
+ timeout; the bug had appeared in 0.4.7.
+
+ *) Bugfix: if the "epoll" method was used and a client closed a
+ connection prematurely, then nginx closed the connection after a send
+ timeout only.
+
+ *) Bugfix: the "[alert] zero size buf" error when FastCGI server was
+ used.
+
+ *) Bugfixes in the "limit_zone" directive.
+
+
+Changes with nginx 0.5.7 15 Jan 2007
+
+ *) Feature: the ssl_session_cache storage optimization.
+
+ *) Bugfixes in the "ssl_session_cache" and "limit_zone" directives.
+
+ *) Bugfix: the segmentation fault was occurred on start or while
+ reconfiguration if the "ssl_session_cache" or "limit_zone" directives
+ were used on 64-bit platforms.
+
+ *) Bugfix: a segmentation fault occurred if the "add_before_body" or
+ "add_after_body" directives were used and there was no "Content-Type"
+ header line in response.
+
+ *) Bugfix: the OpenSSL library was always built with the threads
+ support.
+ Thanks to Den Ivanov.
+
+ *) Bugfix: the PCRE-6.5+ library and the icc compiler compatibility.
+
+
+Changes with nginx 0.5.6 09 Jan 2007
+
+ *) Change: now the ngx_http_index_module ignores all methods except the
+ GET, HEAD, and POST methods.
+
+ *) Feature: the ngx_http_limit_zone_module.
+
+ *) Feature: the $binary_remote_addr variable.
+
+ *) Feature: the "ssl_session_cache" directives of the
+ ngx_http_ssl_module and ngx_imap_ssl_module.
+
+ *) Feature: the DELETE method supports recursive removal.
+
+ *) Bugfix: the byte-ranges were transferred incorrectly if the
+ $r->sendfile() was used.
+
+
+Changes with nginx 0.5.5 24 Dec 2006
+
+ *) Change: the -v switch does not show compiler information any more.
+
+ *) Feature: the -V switch.
+
+ *) Feature: the "worker_rlimit_core" directive supports size in K, M,
+ and G.
+
+ *) Bugfix: the nginx.pm module now could be installed by an unprivileged
+ user.
+
+ *) Bugfix: a segmentation fault might occur if the $r->request_body or
+ $r->request_body_file methods were used.
+
+ *) Bugfix: the ppc platform specific bugs.
+
+
+Changes with nginx 0.5.4 15 Dec 2006
+
+ *) Feature: the "perl" directive may be used inside the "limit_except"
+ block.
+
+ *) Bugfix: the ngx_http_dav_module required the "Date" request header
+ line for the DELETE method.
+
+ *) Bugfix: if one only parameter was used in the "dav_access" directive,
+ then nginx might report about configuration error.
+
+ *) Bugfix: a segmentation fault might occur if the $host variable was
+ used; the bug had appeared in 0.4.14.
+
+
+Changes with nginx 0.5.3 13 Dec 2006
+
+ *) Feature: the ngx_http_perl_module supports the $r->status,
+ $r->log_error, and $r->sleep methods.
+
+ *) Feature: the $r->variable method supports variables that do not exist
+ in nginx configuration.
+
+ *) Bugfix: the $r->has_request_body method did not work.
+
+
+Changes with nginx 0.5.2 11 Dec 2006
+
+ *) Bugfix: if the "proxy_pass" directive used the name of the "upstream"
+ block, then nginx tried to resolve the name; the bug had appeared in
+ 0.5.1.
+
+
+Changes with nginx 0.5.1 11 Dec 2006
+
+ *) Bugfix: the "post_action" directive might not run after a
+ unsuccessful completion of a request.
+
+ *) Workaround: for Eudora for Mac; the bug had appeared in 0.4.11.
+ Thanks to Bron Gondwana.
+
+ *) Bugfix: if the "upstream" name was used in the "fastcgi_pass", then
+ the message "no port in upstream" was issued; the bug had appeared in
+ 0.5.0.
+
+ *) Bugfix: if the "proxy_pass" and "fastcgi_pass" directives used the
+ same servers but different ports, then these directives uses the
+ first described port; the bug had appeared in 0.5.0.
+
+ *) Bugfix: if the "proxy_pass" and "fastcgi_pass" directives used the
+ unix domain sockets, then these directives used first described
+ socket; the bug had appeared in 0.5.0.
+
+ *) Bugfix: ngx_http_auth_basic_module ignored the user if it was in the
+ last line in the password file and there was no the carriage return,
+ the line feed, or the ":" symbol after the password.
+
+ *) Bugfix: the $upstream_response_time variable might be equal to
+ "0.000", although response time was more than 1 millisecond.
+
+
+Changes with nginx 0.5.0 04 Dec 2006
+
+ *) Change: the parameters in the "%name" form in the "log_format"
+ directive are not supported anymore.
+
+ *) Change: the "proxy_upstream_max_fails",
+ "proxy_upstream_fail_timeout", "fastcgi_upstream_max_fails",
+ "fastcgi_upstream_fail_timeout", "memcached_upstream_max_fails", and
+ "memcached_upstream_fail_timeout" directives are not supported
+ anymore.
+
+ *) Feature: the "server" directive in the "upstream" context supports
+ the "max_fails", "fail_timeout", and "down" parameters.
+
+ *) Feature: the "ip_hash" directive inside the "upstream" block.
+
+ *) Feature: the WAIT status in the "Auth-Status" header line of the
+ IMAP/POP3 proxy authentication server response.
+
+ *) Bugfix: nginx could not be built on 64-bit platforms; the bug had
+ appeared in 0.4.14.
+
+
+Changes with nginx 0.4.14 27 Nov 2006
+
+ *) Feature: the "proxy_pass_error_message" directive in IMAP/POP3 proxy.
+
+ *) Feature: now configure detects system PCRE library on FreeBSD, Linux,
+ and NetBSD.
+
+ *) Bugfix: ngx_http_perl_module did not work with perl built with the
+ threads support; the bug had appeared in 0.3.38.
+
+ *) Bugfix: ngx_http_perl_module did not work if perl was called
+ recursively.
+
+ *) Bugfix: nginx ignored a host name in a request line.
+
+ *) Bugfix: a worker process may got caught in an endless loop, if a
+ FastCGI server sent too many data to the stderr.
+
+ *) Bugfix: the $upstream_response_time variable may be negative if the
+ system time was changed backward.
+
+ *) Bugfix: the "Auth-Login-Attempt" parameter was not sent to IMAP/POP3
+ proxy authentication server when POP3 was used.
+
+ *) Bugfix: a segmentation fault might occur if connect to IMAP/POP3
+ proxy authentication server failed.
+
+
+Changes with nginx 0.4.13 15 Nov 2006
+
+ *) Feature: the "proxy_pass" directive may be used inside the
+ "limit_except" block.
+
+ *) Feature: the "limit_except" directive supports all WebDAV methods.
+
+ *) Bugfix: if the "add_before_body" directive was used without the
+ "add_after_body" directive, then a response did not transferred
+ complete.
+
+ *) Bugfix: a large request body did not receive if the epoll method and
+ the deferred accept() were used.
+
+ *) Bugfix: a charset could not be set for ngx_http_autoindex_module
+ responses; the bug had appeared in 0.3.50.
+
+ *) Bugfix: the "[alert] zero size buf" error when FastCGI server was
+ used;
+
+ *) Bugfix: the --group= configuration parameter was ignored.
+ Thanks to Thomas Moschny.
+
+ *) Bugfix: the 50th subrequest in SSI response did not work; the bug had
+ appeared in 0.3.50.
+
+
+Changes with nginx 0.4.12 31 Oct 2006
+
+ *) Feature: the ngx_http_perl_module supports the $r->variable method.
+
+ *) Bugfix: if a big static file was included using SSI in a response,
+ then the response may be transferred incomplete.
+
+ *) Bugfix: nginx did not omit the "#fragment" part in URI.
+
+
+Changes with nginx 0.4.11 25 Oct 2006
+
+ *) Feature: the POP3 proxy supports the AUTH LOGIN PLAIN and CRAM-MD5.
+
+ *) Feature: the ngx_http_perl_module supports the $r->allow_ranges
+ method.
+
+ *) Bugfix: if the APOP was enabled in the POP3 proxy, then the USER/PASS
+ commands might not work; the bug had appeared in 0.4.10.
+
+
+Changes with nginx 0.4.10 23 Oct 2006
+
+ *) Feature: the POP3 proxy supports the APOP command.
+
+ *) Bugfix: if the select, poll or /dev/poll methods were used, then
+ while waiting authentication server response the IMAP/POP3 proxy
+ hogged CPU.
+
+ *) Bugfix: a segmentation fault might occur if the $server_addr variable
+ was used in the "map" directive.
+
+ *) Bugfix: the ngx_http_flv_module did not support the byte ranges for
+ full responses; the bug had appeared in 0.4.7.
+
+ *) Bugfix: nginx could not be built on Debian amd64; the bug had
+ appeared in 0.4.9.
+
+
+Changes with nginx 0.4.9 13 Oct 2006
+
+ *) Feature: the "set" parameter in the "include" SSI command.
+
+ *) Feature: the ngx_http_perl_module now tests the nginx.pm module
+ version.
+
+
+Changes with nginx 0.4.8 11 Oct 2006
+
+ *) Bugfix: if an "include" SSI command were before another "include" SSI
+ command with a "wait" parameter, then the "wait" parameter might not
+ work.
+
+ *) Bugfix: the ngx_http_flv_module added the FLV header to the full
+ responses.
+ Thanks to Alexey Kovyrin.
+
+
+Changes with nginx 0.4.7 10 Oct 2006
+
+ *) Feature: the ngx_http_flv_module.
+
+ *) Feature: the $request_body_file variable.
+
+ *) Feature: the "charset" and "source_charset" directives support the
+ variables.
+
+ *) Bugfix: if an "include" SSI command were before another "include" SSI
+ command with a "wait" parameter, then the "wait" parameter might not
+ work.
+
+ *) Bugfix: if the "proxy_buffering off" directive was used or while
+ working with memcached the connections might not be closed on
+ timeout.
+
+ *) Bugfix: nginx did not run on 64-bit platforms except amd64, sparc64,
+ and ppc64.
+
+
+Changes with nginx 0.4.6 06 Oct 2006
+
+ *) Bugfix: nginx did not run on 64-bit platforms except amd64, sparc64,
+ and ppc64.
+
+ *) Bugfix: nginx sent the chunked response for HTTP/1.1 request,
+ if its length was set by text string in the
+ $r->headers_out("Content-Length", ...) method.
+
+ *) Bugfix: after redirecting error by an "error_page" directive any
+ ngx_http_rewrite_module directive returned this error code; the bug
+ had appeared in 0.4.4.
+
+
+Changes with nginx 0.4.5 02 Oct 2006
+
+ *) Bugfix: nginx could not be built on Linux and Solaris; the bug had
+ appeared in 0.4.4.
+
+
+Changes with nginx 0.4.4 02 Oct 2006
+
+ *) Feature: the $scheme variable.
+
+ *) Feature: the "expires" directive supports the "max" parameter.
+
+ *) Feature: the "include" directive supports the "*" mask.
+ Thanks to Jonathan Dance.
+
+ *) Bugfix: the "return" directive always overrode the "error_page"
+ response code redirected by the "error_page" directive.
+
+ *) Bugfix: a segmentation fault occurred if zero-length body was in PUT
+ method.
+
+ *) Bugfix: the redirect was changed incorrectly if the variables were
+ used in the "proxy_redirect" directive.
+
+
+Changes with nginx 0.4.3 26 Sep 2006
+
+ *) Change: now the 499 error could not be redirected using an
+ "error_page" directive.
+
+ *) Feature: the Solaris 10 event ports support.
+
+ *) Feature: the ngx_http_browser_module.
+
+ *) Bugfix: a segmentation fault may occur while redirecting the 400
+ error to the proxied server using a "proxy_pass" directive.
+
+ *) Bugfix: a segmentation fault occurred if an unix domain socket was
+ used in a "proxy_pass" directive; the bug had appeared in 0.3.47.
+
+ *) Bugfix: SSI did work with memcached and nonbuffered responses.
+
+ *) Workaround: of the Sun Studio PAUSE hardware capability bug.
+
+
+Changes with nginx 0.4.2 14 Sep 2006
+
+ *) Bugfix: the O_NOATIME flag support on Linux was canceled; the bug had
+ appeared in 0.4.1.
+
+
+Changes with nginx 0.4.1 14 Sep 2006
+
+ *) Bugfix: the DragonFlyBSD compatibility.
+ Thanks to Pavel Nazarov.
+
+ *) Workaround: of bug in 64-bit Linux sendfile(), when file is more than
+ 2G.
+
+ *) Feature: now on Linux nginx uses O_NOATIME flag for static requests.
+ Thanks to Yusuf Goolamabbas.
+
+
+Changes with nginx 0.4.0 30 Aug 2006
+
+ *) Change in internal API: the HTTP modules initialization was moved
+ from the init module phase to the HTTP postconfiguration phase.
+
+ *) Change: now the request body is not read beforehand for the
+ ngx_http_perl_module: it's required to start the reading using the
+ $r->has_request_body method.
+
+ *) Feature: the ngx_http_perl_module supports the DECLINED return code.
+
+ *) Feature: the ngx_http_dav_module supports the incoming "Date" header
+ line for the PUT method.
+
+ *) Feature: the "ssi" directive is available inside the "if" block.
+
+ *) Bugfix: a segmentation fault occurred if there was an "index"
+ directive with variables and the first index name was without
+ variables; the bug had appeared in 0.1.29.
+
+
+Changes with nginx 0.3.61 28 Aug 2006
+
+ *) Change: now the "tcp_nodelay" directive is turned on by default.
+
+ *) Feature: the "msie_refresh" directive.
+
+ *) Feature: the "recursive_error_pages" directive.
+
+ *) Bugfix: the "rewrite" directive returned incorrect redirect, if the
+ redirect had the captured escaped symbols from original URI.
+
+
+Changes with nginx 0.3.60 18 Aug 2006
+
+ *) Bugfix: a worker process may got caught in an endless loop while an
+ error redirection; the bug had appeared in 0.3.59.
+
+
+Changes with nginx 0.3.59 16 Aug 2006
+
+ *) Feature: now is possible to do several redirection using the
+ "error_page" directive.
+
+ *) Bugfix: the "dav_access" directive did not support three parameters.
+
+ *) Bugfix: the "error_page" directive did not changes the "Content-Type"
+ header line after the "X-Accel-Redirect" was used; the bug had
+ appeared in 0.3.58.
+
+
+Changes with nginx 0.3.58 14 Aug 2006
+
+ *) Feature: the "error_page" directive supports the variables.
+
+ *) Change: now the procfs interface instead of sysctl is used on Linux.
+
+ *) Change: now the "Content-Type" header line is inherited from first
+ response when the "X-Accel-Redirect" was used.
+
+ *) Bugfix: the "error_page" directive did not redirect the 413 error.
+
+ *) Bugfix: the trailing "?" did not remove old arguments if no new
+ arguments were added to a rewritten URI.
+
+ *) Bugfix: nginx could not run on 64-bit FreeBSD 7.0-CURRENT.
+
+
+Changes with nginx 0.3.57 09 Aug 2006
+
+ *) Feature: the $ssl_client_serial variable.
+
+ *) Bugfix: in the "!-e" operator of the "if" directive.
+ Thanks to Andrian Budanstov.
+
+ *) Bugfix: while a client certificate verification nginx did not send to
+ a client the required certificates information.
+
+ *) Bugfix: the $document_root variable did not support the variables in
+ the "root" directive.
+
+
+Changes with nginx 0.3.56 04 Aug 2006
+
+ *) Feature: the "dav_access" directive.
+
+ *) Feature: the "if" directive supports the "-d", "!-d", "-e", "!-e",
+ "-x", and "!-x" operators.
+
+ *) Bugfix: a segmentation fault occurred if a request returned a
+ redirect and some sent to client header lines were logged in the
+ access log.
+
+
+Changes with nginx 0.3.55 28 Jul 2006
+
+ *) Feature: the "stub" parameter in the "include" SSI command.
+
+ *) Feature: the "block" SSI command.
+
+ *) Feature: the unicode2nginx script was added to contrib.
+
+ *) Bugfix: if a "root" was specified by variable only, then the root was
+ relative to a server prefix.
+
+ *) Bugfix: if the request contained "//" or "/./" and escaped symbols
+ after them, then the proxied request was sent unescaped.
+
+ *) Bugfix: the $r->header_in("Cookie") of the ngx_http_perl_module now
+ returns all "Cookie" header lines.
+
+ *) Bugfix: a segmentation fault occurred if
+ "client_body_in_file_only on" was used and nginx switched to a next
+ upstream.
+
+ *) Bugfix: on some condition while reconfiguration character codes
+ inside the "charset_map" may be treated invalid; the bug had appeared
+ in 0.3.50.
+
+
+Changes with nginx 0.3.54 11 Jul 2006
+
+ *) Feature: nginx now logs the subrequest information to the error log.
+
+ *) Feature: the "proxy_next_upstream", "fastcgi_next_upstream", and
+ "memcached_next_upstream" directives support the "off" parameter.
+
+ *) Feature: the "debug_connection" directive supports the CIDR address
+ form.
+
+ *) Bugfix: if a response of proxied server or FastCGI server was
+ converted from UTF-8 or back, then it may be transferred incomplete.
+
+ *) Bugfix: the $upstream_response_time variable had the time of the
+ first request to a backend only.
+
+ *) Bugfix: nginx could not be built on amd64 platform; the bug had
+ appeared in 0.3.53.
+
+
+Changes with nginx 0.3.53 07 Jul 2006
+
+ *) Change: the "add_header" directive adds the string to 204, 301, and
+ 302 responses.
+
+ *) Feature: the "server" directive in the "upstream" context supports
+ the "weight" parameter.
+
+ *) Feature: the "server_name" directive supports the "*" wildcard.
+
+ *) Feature: nginx supports the request body size more than 2G.
+
+ *) Bugfix: if a client was successfully authorized using "satisfy_any
+ on", then anyway the message "access forbidden by rule" was written
+ in the log.
+
+ *) Bugfix: the "PUT" method may erroneously not create a file and return
+ the 409 code.
+
+ *) Bugfix: if the IMAP/POP3 backend returned an error, then nginx
+ continued proxying anyway.
+
+
+Changes with nginx 0.3.52 03 Jul 2006
+
+ *) Change: the ngx_http_index_module behavior for the "POST /" requests
+ is reverted to the 0.3.40 version state: the module now does not
+ return the 405 error.
+
+ *) Bugfix: the worker process may got caught in an endless loop if the
+ limit rate was used; the bug had appeared in 0.3.37.
+
+ *) Bugfix: ngx_http_charset_module logged "unknown charset" alert, even
+ if the recoding was not needed; the bug had appeared in 0.3.50.
+
+ *) Bugfix: if a code response of the PUT request was 409, then a
+ temporary file was not removed.
+
+
+Changes with nginx 0.3.51 30 Jun 2006
+
+ *) Bugfix: the "<" symbols might disappeared some conditions in the SSI;
+ the bug had appeared in 0.3.50.
+
+
+Changes with nginx 0.3.50 28 Jun 2006
+
+ *) Change: the "proxy_redirect_errors" and "fastcgi_redirect_errors"
+ directives was renamed to the "proxy_intercept_errors" and
+ "fastcgi_intercept_errors" directives.
+
+ *) Feature: the ngx_http_charset_module supports the recoding from the
+ single byte encodings to the UTF-8 encoding and back.
+
+ *) Feature: the "X-Accel-Charset" response header line is supported in
+ proxy and FastCGI mode.
+
+ *) Bugfix: the "\" escape symbol in the "\"" and "\'" pairs in the SSI
+ command was removed only if the command also has the "$" symbol.
+
+ *) Bugfix: the "<!--" string might be added on some conditions in the
+ SSI after inclusion.
+
+ *) Bugfix: if the "Content-Length: 0" header line was in response, then
+ in nonbuffered proxying mode the client connection was not closed.
+
+
+Changes with nginx 0.3.49 31 May 2006
+
+ *) Bugfix: in the "set" directive.
+
+ *) Bugfix: if two or more FastCGI subrequests was in SSI, then first
+ subrequest output was included instead of second and following
+ subrequests.
+
+
+Changes with nginx 0.3.48 29 May 2006
+
+ *) Change: now the ngx_http_charset_module works for subrequests, if the
+ response has no "Content-Type" header line.
+
+ *) Bugfix: if the "proxy_pass" directive has no URI part, then the
+ "proxy_redirect default" directive add the unnecessary slash in start
+ of the rewritten redirect.
+
+ *) Bugfix: the internal redirect always transform client's HTTP method
+ to GET, now the transformation is made for the "X-Accel-Redirect"
+ redirects only and if the method is not HEAD; the bug had appeared in
+ 0.3.42.
+
+ *) Bugfix: the ngx_http_perl_module could not be built, if the perl was
+ built with the threads support; the bug had appeared in 0.3.46.
+
+
+Changes with nginx 0.3.47 23 May 2006
+
+ *) Feature: the "upstream" directive.
+
+ *) Change: now the "\" escape symbol in the "\"" and "\'" pairs in the
+ SSI command is always removed.
+
+
+Changes with nginx 0.3.46 11 May 2006
+
+ *) Feature: the "proxy_hide_header", "proxy_pass_header",
+ "fastcgi_hide_header", and "fastcgi_pass_header" directives.
+
+ *) Change: the "proxy_pass_x_powered_by", "fastcgi_x_powered_by", and
+ "proxy_pass_server" directives were canceled.
+
+ *) Feature: the "X-Accel-Buffering" response header line is supported in
+ proxy mode.
+
+ *) Bugfix: the reconfiguration bug and memory leaks in the
+ ngx_http_perl_module.
+
+
+Changes with nginx 0.3.45 06 May 2006
+
+ *) Feature: the "ssl_verify_client", "ssl_verify_depth", and
+ "ssl_client_certificate" directives.
+
+ *) Change: the $request_method variable now returns the main request
+ method.
+
+ *) Change: the &deg; symbol codes were changed in koi-win conversion
+ table.
+
+ *) Feature: the euro and N symbols were added to koi-win conversion
+ table.
+
+ *) Bugfix: if nginx distributed the requests among several backends and
+ some backend failed, then requests intended for this backend was
+ directed to one live backend only instead of being distributed among
+ the rest.
+
+
+Changes with nginx 0.3.44 04 May 2006
+
+ *) Feature: the "wait" parameter in the "include" SSI command.
+
+ *) Feature: the Ukrainian and Byelorussian characters were added to
+ koi-win conversion table.
+
+ *) Bugfix: in the SSI.
+
+
+Changes with nginx 0.3.43 26 Apr 2006
+
+ *) Bugfix: in the SSI.
+
+
+Changes with nginx 0.3.42 26 Apr 2006
+
+ *) Feature: the "bind" option of the "listen" directive in IMAP/POP3
+ proxy.
+
+ *) Bugfix: if the same capture in the "rewrite" directive was used more
+ then once.
+
+ *) Bugfix: the $sent_http_content_type, $sent_http_content_length,
+ $sent_http_last_modified, $sent_http_connection,
+ $sent_http_keep_alive, and $sent_http_transfer_encoding variables
+ were not written to access log.
+
+ *) Bugfix: the $sent_http_cache_control returned value of the single
+ "Cache-Control" response header line.
+
+
+Changes with nginx 0.3.41 21 Apr 2006
+
+ *) Feature: the -v switch.
+
+ *) Bugfix: the segmentation fault may occurred if the SSI page has
+ remote subrequests.
+
+ *) Bugfix: in FastCGI handling.
+
+ *) Bugfix: if the perl modules path was not set using
+ --with-perl_modules_path=PATH or the "perl_modules", then the
+ segmentation fault was occurred.
+
+
+Changes with nginx 0.3.40 19 Apr 2006
+
+ *) Feature: the ngx_http_dav_module supports the MKCOL method.
+
+ *) Feature: the "create_full_put_path" directive.
+
+ *) Feature: the "$limit_rate" variable.
+
+
+Changes with nginx 0.3.39 17 Apr 2006
+
+ *) Feature: the "uninitialized_variable_warn" directive; the logging
+ level of the "uninitialized variable" message was lowered from
+ "alert" to "warn".
+
+ *) Feature: the "override_charset" directive.
+
+ *) Change: now if the unknown variable is used in the "echo" and "if
+ expr='$name'" SSI-commands, then the "unknown variable" message is
+ not logged.
+
+ *) Bugfix: the active connection counter increased on the exceeding of
+ the connection limit specified by the "worker_connections" directive;
+ the bug had appeared in 0.2.0.
+
+ *) Bugfix: the limit rate might not work on some condition; the bug had
+ appeared in 0.3.38.
+
+
+Changes with nginx 0.3.38 14 Apr 2006
+
+ *) Feature: the ngx_http_dav_module.
+
+ *) Change: the ngx_http_perl_module optimizations.
+ Thanks to Sergey Skvortsov.
+
+ *) Feature: the ngx_http_perl_module supports the $r->request_body_file
+ method.
+
+ *) Feature: the "client_body_in_file_only" directive.
+
+ *) Workaround: now on disk overflow nginx tries to write access logs
+ once a second only.
+ Thanks to Anton Yuzhaninov and Maxim Dounin.
+
+ *) Bugfix: now the "limit_rate" directive more precisely limits rate if
+ rate is more than 100 Kbyte/s.
+ Thanks to ForJest.
+
+ *) Bugfix: now the IMAP/POP3 proxy escapes the "\r" and "\n" symbols in
+ login and password to pass authorization server.
+ Thanks to Maxim Dounin.
+
+
+Changes with nginx 0.3.37 07 Apr 2006
+
+ *) Feature: the "limit_except" directive.
+
+ *) Feature: the "if" directive supports the "!~", "!~*", "-f", and "!-f"
+ operators.
+
+ *) Feature: the ngx_http_perl_module supports the $r->request_body
+ method.
+
+ *) Bugfix: in the ngx_http_addition_filter_module.
+
+
+Changes with nginx 0.3.36 05 Apr 2006
+
+ *) Feature: the ngx_http_addition_filter_module.
+
+ *) Feature: the "proxy_pass" and "fastcgi_pass" directives may be used
+ inside the "if" block.
+
+ *) Feature: the "proxy_ignore_client_abort" and
+ "fastcgi_ignore_client_abort" directives.
+
+ *) Feature: the "$request_completion" variable.
+
+ *) Feature: the ngx_http_perl_module supports the $r->request_method and
+ $r->remote_addr.
+
+ *) Feature: the ngx_http_ssi_module supports the "elif" command.
+
+ *) Bugfix: the "\/" string in the expression of the "if" command of the
+ ngx_http_ssi_module was treated incorrectly.
+
+ *) Bugfix: in the regular expressions in the "if" command of the
+ ngx_http_ssi_module.
+
+ *) Bugfix: if the relative path was specified in the
+ "client_body_temp_path", "proxy_temp_path", "fastcgi_temp_path", and
+ "perl_modules" directives, then the directory was used relatively to
+ a current path but not to a server prefix.
+
+
+Changes with nginx 0.3.35 22 Mar 2006
+
+ *) Bugfix: the accept-filter and the TCP_DEFER_ACCEPT option were set
+ for first "listen" directive only; the bug had appeared in 0.3.31.
+
+ *) Bugfix: in the "proxy_pass" directive without the URI part in a
+ subrequest.
+
+
+Changes with nginx 0.3.34 21 Mar 2006
+
+ *) Feature: the "add_header" directive supports the variables.
+
+
+Changes with nginx 0.3.33 15 Mar 2006
+
+ *) Feature: the "http_503" parameter of the "proxy_next_upstream" or
+ "fastcgi_next_upstream" directives.
+
+ *) Bugfix: ngx_http_perl_module did not work with inlined in the
+ configuration code, if it was not started with the "sub" word.
+
+ *) Bugfix: in the "post_action" directive.
+
+
+Changes with nginx 0.3.32 11 Mar 2006
+
+ *) Bugfix: the debug logging on startup and reconfiguration time was
+ removed; the bug had appeared in 0.3.31.
+
+
+Changes with nginx 0.3.31 10 Mar 2006
+
+ *) Change: now nginx passes the malformed proxied backend responses.
+
+ *) Feature: the "listen" directives support the address in the "*:port"
+ form.
+
+ *) Feature: the EVFILER_TIMER support in MacOSX 10.4.
+
+ *) Workaround: for MacOSX 64-bit kernel kqueue millisecond timeout bug.
+ Thanks to Andrei Nigmatulin.
+
+ *) Bugfix: if there were several "listen" directives listening one
+ various addresses inside one server, then server names like
+ "*.domain.tld" worked for first address only; the bug had appeared in
+ 0.3.18.
+
+ *) Bugfix: if the HTTPS protocol was used in the "proxy_pass" directive
+ and the request body was in temporary file then the request was not
+ transferred.
+
+ *) Bugfix: perl 5.8.8 compatibility.
+
+
+Changes with nginx 0.3.30 22 Feb 2006
+
+ *) Change: the ECONNABORTED error log level was changed to "error" from
+ "crit".
+
+ *) Bugfix: the ngx_http_perl_module could not be build without the
+ ngx_http_ssi_filter_module.
+
+ *) Bugfix: nginx could not be built on i386 platform, if the PIC was
+ used; the bug had appeared in 0.3.27.
+
+
+Changes with nginx 0.3.29 20 Feb 2006
+
+ *) Feature: now nginx uses less memory, if PHP in FastCGI mode sends
+ many warnings before the response.
+
+ *) Bugfix: the "Transfer-Encoding: chunked" header line was issued in
+ the 204 responses for the HTTP/1.1 requests.
+
+ *) Bugfix: nginx returned the 502 response, if the complete response
+ header lines were transferred in a separate FastCGI records.
+
+ *) Bugfix: if the proxied URI was specified in the "post_action"
+ directive, then it ran only after a successful completion of a
+ request.
+
+
+Changes with nginx 0.3.28 16 Feb 2006
+
+ *) Feature: the "restrict_host_names" directive was canceled.
+
+ *) Feature: the --with-cpu-opt=ppc64 configuration parameter.
+
+ *) Bugfix: on some condition the proxied connection with a client was
+ terminated prematurely.
+ Thanks to Vladimir Shutoff.
+
+ *) Bugfix: the "X-Accel-Limit-Rate" header line was not taken into
+ account if the request was redirected using the "X-Accel-Redirect"
+ header line.
+
+ *) Bugfix: the "post_action" directive ran only after a successful
+ completion of a request.
+
+ *) Bugfix: the proxied response body generated by the "post_action"
+ directive was transferred to a client.
+
+
+Changes with nginx 0.3.27 08 Feb 2006
+
+ *) Change: the "variables_hash_max_size" and
+ "variables_hash_bucket_size" directives.
+
+ *) Feature: the $body_bytes_sent variable can be used not only in the
+ "log_format" directive.
+
+ *) Feature: the $ssl_protocol and $ssl_cipher variables.
+
+ *) Feature: the cache line size detection for widespread CPUs at start
+ time.
+
+ *) Feature: now the "accept_mutex" directive is supported using fcntl(2)
+ on platforms different from i386, amd64, sparc64, and ppc.
+
+ *) Feature: the "lock_file" directive and the --with-lock-path=PATH
+ autoconfiguration directive.
+
+ *) Bugfix: if the HTTPS protocol was used in the "proxy_pass" directive
+ then the requests with the body was not transferred.
+
+
+Changes with nginx 0.3.26 03 Feb 2006
+
+ *) Change: the "optimize_host_names" directive was renamed to the
+ "optimize_server_names".
+
+ *) Bugfix: if in the "proxy_pass" directive was no the URI part, then
+ the main request URI was transferred to a backend while proxying the
+ SSI subrequest.
+
+
+Changes with nginx 0.3.25 01 Feb 2006
+
+ *) Bugfix: the segmentation fault was occurred on start or while
+ reconfiguration if there was invalid configuration; the bug had
+ appeared in 0.3.24.
+
+
+Changes with nginx 0.3.24 01 Feb 2006
+
+ *) Workaround: for bug in FreeBSD kqueue.
+
+ *) Bugfix: now a response generated by the "post_action" directive is
+ not transferred to a client.
+
+ *) Bugfix: the memory leaks were occurring if many log files were used.
+
+ *) Bugfix: the first "proxy_redirect" directive was working inside one
+ location.
+
+ *) Bugfix: on 64-bit platforms segmentation fault may occurred on start
+ if the many names were used in the "server_name" directives; the bug
+ had appeared in 0.3.18.
+
+
+Changes with nginx 0.3.23 24 Jan 2006
+
+ *) Feature: the "optimize_host_names" directive.
+
+ *) Bugfix: in using of the variables in the "path" and "alias"
+ directives.
+
+ *) Bugfix: the ngx_http_perl_module was incorrectly built on Linux and
+ Solaris.
+
+
+Changes with nginx 0.3.22 17 Jan 2006
+
+ *) Feature: the ngx_http_perl_module supports the $r->args and
+ $r->unescape methods.
+
+ *) Feature: the method $r->query_string of ngx_http_perl_module was
+ canceled.
+
+ *) Bugfix: segmentation fault was occurred if the "none" or "blocked"
+ values was specified in the "valid_referers" directive; the bug had
+ appeared in 0.3.18.
+
+
+Changes with nginx 0.3.21 16 Jan 2006
+
+ *) Feature: the ngx_http_perl_module.
+
+ *) Change: the "valid_referers" directive allows the referreres without
+ URI part.
+
+
+Changes with nginx 0.3.20 11 Jan 2006
+
+ *) Bugfix: in SSI handling.
+
+ *) Bugfix: the ngx_http_memcached_module did not support the keys in the
+ "/usr?args" form.
+
+
+Changes with nginx 0.3.19 28 Dec 2005
+
+ *) Feature: the "path" and "alias" directives support the variables.
+
+ *) Change: now the "valid_referers" directive again checks the URI part.
+
+ *) Bugfix: in SSI handling.
+
+
+Changes with nginx 0.3.18 26 Dec 2005
+
+ *) Feature: the "server_names" directive supports the ".domain.tld"
+ names.
+
+ *) Feature: the "server_names" directive uses the hash for the
+ "*.domain.tld" names and more effective hash for usual names.
+
+ *) Change: the "server_names_hash_max_size" and
+ "server_names_hash_bucket_size" directives.
+
+ *) Change: the "server_names_hash" and "server_names_hash_threshold"
+ directives were canceled.
+
+ *) Feature: the "valid_referers" directive uses the hash site names.
+
+ *) Change: now the "valid_referers" directive checks the site names only
+ without the URI part.
+
+ *) Bugfix: some ".domain.tld" names incorrectly processed by the
+ ngx_http_map_module.
+
+ *) Bugfix: segmentation fault was occurred if configuration file did not
+ exist; the bug had appeared in 0.3.12.
+
+ *) Bugfix: on 64-bit platforms segmentation fault may occurred on start;
+ the bug had appeared in 0.3.16.
+
+
+Changes with nginx 0.3.17 18 Dec 2005
+
+ *) Change: now on Linux configure checks the presence of epoll and
+ sendfile64() in kernel.
+
+ *) Feature: the "map" directive supports domain names in the
+ ".domain.tld" form.
+
+ *) Bugfix: the timeouts were not used in SSL handshake; the bug had
+ appeared in 0.2.4.
+
+ *) Bugfix: in the HTTPS protocol in the "proxy_pass" directive.
+
+ *) Bugfix: when the HTTPS protocol was used in the "proxy_pass"
+ directive the port 80 was used by default.
+
+
+Changes with nginx 0.3.16 16 Dec 2005
+
+ *) Feature: the ngx_http_map_module.
+
+ *) Feature: the "types_hash_max_size" and "types_hash_bucket_size"
+ directives.
+
+ *) Feature: the "ssi_value_length" directive.
+
+ *) Feature: the "worker_rlimit_core" directive.
+
+ *) Workaround: the connection number in logs was always 1 if nginx was
+ built by the icc 8.1 or 9.0 compilers with optimization for
+ Pentium 4.
+
+ *) Bugfix: the "config timefmt" SSI command set incorrect time format.
+
+ *) Bugfix: nginx did not close connection to IMAP/POP3 backend for the
+ SSL connections; the bug had appeared in 0.3.13.
+ Thanks to Rob Mueller.
+
+ *) Bugfix: segmentation fault may occurred in at SSL shutdown; the bug
+ had appeared in 0.3.13.
+
+
+Changes with nginx 0.3.15 07 Dec 2005
+
+ *) Feature: the new 444 code of the "return" directive to close
+ connection.
+
+ *) Feature: the "so_keepalive" directive in IMAP/POP3 proxy.
+
+ *) Bugfix: if there are unclosed connection nginx now calls abort() only
+ on gracefull quit and active "debug_points" directive.
+
+
+Changes with nginx 0.3.14 05 Dec 2005
+
+ *) Bugfix: in the 304 response the body was transferred; the bug had
+ appeared in 0.3.13.
+
+
+Changes with nginx 0.3.13 05 Dec 2005
+
+ *) Feature: the IMAP/POP3 proxy supports STARTTLS and STLS.
+
+ *) Bugfix: the IMAP/POP3 proxy did not work with the select, poll, and
+ /dev/poll methods.
+
+ *) Bugfix: in SSI handling.
+
+ *) Bugfix: now Solaris sendfilev() is not used to transfer the client
+ request body to FastCGI-server via the unix domain socket.
+
+ *) Bugfix: the "auth_basic" directive did not disable the authorization;
+ the bug had appeared in 0.3.11.
+
+
+Changes with nginx 0.3.12 26 Nov 2005
+
+ *) Security: if nginx was built with the ngx_http_realip_module and the
+ "satisfy_any on" directive was used, then access and authorization
+ directives did not work. The ngx_http_realip_module was not built and
+ is not built by default.
+
+ *) Change: the "$time_gmt" variable name was changed to "$time_local".
+
+ *) Change: the "proxy_header_buffer_size" and
+ "fastcgi_header_buffer_size" directives was renamed to the
+ "proxy_buffer_size" and "fastcgi_buffer_size" directives.
+
+ *) Feature: the ngx_http_memcached_module.
+
+ *) Feature: the "proxy_buffering" directive.
+
+ *) Bugfix: the changes in accept mutex handling when the "rtsig" method
+ was used; the bug had appeared in 0.3.0.
+
+ *) Bugfix: if the client sent the "Transfer-Encoding: chunked" header
+ line, then nginx returns the 411 error.
+
+ *) Bugfix: if the "auth_basic" directive was inherited from the http
+ level, then the realm in the "WWW-Authenticate" header line was
+ without the "Basic realm" text.
+
+ *) Bugfix: if the "combined" format was explicitly specified in the
+ "access_log" directive, then the empty lines was written to the log;
+ the bug had appeared in 0.3.8.
+
+ *) Bugfix: nginx did not run on the sparc platform under any OS except
+ Solaris.
+
+ *) Bugfix: now it is not necessary to place space between the quoted
+ string and closing bracket in the "if" directive.
+
+
+Changes with nginx 0.3.11 15 Nov 2005
+
+ *) Bugfix: nginx did not pass the client request headers and body while
+ proxying; the bug had appeared in 0.3.10.
+
+
+Changes with nginx 0.3.10 15 Nov 2005
+
+ *) Change: the "valid_referers" directive and the "$invalid_referer"
+ variable were moved to the new ngx_http_referer_module from the
+ ngx_http_rewrite_module.
+
+ *) Change: the "$apache_bytes_sent" variable name was changed to
+ "$body_bytes_sent".
+
+ *) Feature: the "$sent_http_..." variables.
+
+ *) Feature: the "if" directive supports the "=" and "!=" operations.
+
+ *) Feature: the "proxy_pass" directive supports the HTTPS protocol.
+
+ *) Feature: the "proxy_set_body" directive.
+
+ *) Feature: the "post_action" directive.
+
+ *) Feature: the ngx_http_empty_gif_module.
+
+ *) Feature: the "worker_cpu_affinity" directive for Linux.
+
+ *) Bugfix: the "rewrite" directive did not unescape URI part in
+ redirect, now it is unescaped except the %00-%25 and %7F-%FF
+ characters.
+
+ *) Bugfix: nginx could not be built by the icc 9.0 compiler.
+
+ *) Bugfix: if the SSI was enabled for zero size static file, then the
+ chunked response was encoded incorrectly.
+
+
+Changes with nginx 0.3.9 10 Nov 2005
+
+ *) Bugfix: nginx considered URI as unsafe if two any symbols was between
+ two slashes; the bug had appeared in 0.3.8.
+
+
+Changes with nginx 0.3.8 09 Nov 2005
+
+ *) Security: nginx now checks URI got from a backend in
+ "X-Accel-Redirect" header line or in SSI file for the "/../" paths
+ and zeroes.
+
+ *) Change: nginx now does not treat the empty user name in the
+ "Authorization" header line as valid one.
+
+ *) Feature: the "ssl_session_timeout" directives of the
+ ngx_http_ssl_module and ngx_imap_ssl_module.
+
+ *) Feature: the "auth_http_header" directive of the
+ ngx_imap_auth_http_module.
+
+ *) Feature: the "add_header" directive.
+
+ *) Feature: the ngx_http_realip_module.
+
+ *) Feature: the new variables to use in the "log_format" directive:
+ $bytes_sent, $apache_bytes_sent, $status, $time_gmt, $uri,
+ $request_time, $request_length, $upstream_status,
+ $upstream_response_time, $gzip_ratio, $uid_got, $uid_set,
+ $connection, $pipe, and $msec. The parameters in the "%name" form
+ will be canceled soon.
+
+ *) Change: now the false variable values in the "if" directive are the
+ empty string "" and string starting with "0".
+
+ *) Bugfix: while using proxied or FastCGI-server nginx may leave
+ connections and temporary files with client requests in open state.
+
+ *) Bugfix: the worker processes did not flush the buffered logs on
+ graceful exit.
+
+ *) Bugfix: if the request URI was changes by the "rewrite" directive and
+ the request was proxied in location given by regular expression, then
+ the incorrect request was transferred to backend; the bug had
+ appeared in 0.2.6.
+
+ *) Bugfix: the "expires" directive did not remove the previous "Expires"
+ header.
+
+ *) Bugfix: nginx may stop to accept requests if the "rtsig" method and
+ several worker processes were used.
+
+ *) Bugfix: the "\"" and "\'" escape symbols were incorrectly handled in
+ SSI commands.
+
+ *) Bugfix: if the response was ended just after the SSI command and
+ gzipping was used, then the response did not transferred complete or
+ did not transferred at all.
+
+
+Changes with nginx 0.3.7 27 Oct 2005
+
+ *) Feature: the "access_log" supports the "buffer=" parameter.
+
+ *) Bugfix: nginx could not be built on platforms different from i386,
+ amd64, sparc, and ppc; the bug had appeared in 0.3.2.
+
+
+Changes with nginx 0.3.6 24 Oct 2005
+
+ *) Change: now the IMAP/POP3 proxy do not send the empty login to
+ authorization server.
+
+ *) Feature: the "log_format" supports the variables in the $name form.
+
+ *) Bugfix: if at least in one server was no the "listen" directive, then
+ nginx did not listen on the 80 port; the bug had appeared in 0.3.3.
+
+ *) Bugfix: if the URI part is omitted in "proxy_pass" directive, the 80
+ port was always used.
+
+
+Changes with nginx 0.3.5 21 Oct 2005
+
+ *) Bugfix: the segmentation fault may occurred if the IMAP/POP3 login
+ was changed by authorization server; the bug had appeared in 0.2.2.
+
+ *) Bugfix: the accept mutex did not work and all connections were
+ handled by one process; the bug had appeared in 0.3.3.
+
+ *) Bugfix: the timeout did not work if the "rtsig" method and the
+ "timer_resolution" directive were used.
+
+
+Changes with nginx 0.3.4 19 Oct 2005
+
+ *) Bugfix: nginx could not be built on Linux 2.4+ and MacOS X; the bug
+ had appeared in 0.3.3.
+
+
+Changes with nginx 0.3.3 19 Oct 2005
+
+ *) Change: the "bl" and "af" parameters of the "listen" directive was
+ renamed to the "backlog" and "accept_filter".
+
+ *) Feature: the "rcvbuf" and "sndbuf" parameters of the "listen"
+ directive.
+
+ *) Change: the "$msec" log parameter does not require now the additional
+ the gettimeofday() system call.
+
+ *) Feature: the -t switch now tests the "listen" directives.
+
+ *) Bugfix: if the invalid address was specified in the "listen"
+ directive, then after the -HUP signal nginx left an open socket in
+ the CLOSED state.
+
+ *) Bugfix: the mime type may be incorrectly set to default value for
+ index file with variable in the name; the bug had appeared in 0.3.0.
+
+ *) Feature: the "timer_resolution" directive.
+
+ *) Feature: the millisecond "$upstream_response_time" log parameter.
+
+ *) Bugfix: a temporary file with client request body now is removed just
+ after the response header was transferred to a client.
+
+ *) Bugfix: OpenSSL 0.9.6 compatibility.
+
+ *) Bugfix: the SSL certificate and key file paths could not be relative.
+
+ *) Bugfix: the "ssl_prefer_server_ciphers" directive did not work in the
+ ngx_imap_ssl_module.
+
+ *) Bugfix: the "ssl_protocols" directive allowed to specify the single
+ protocol only.
+
+
+Changes with nginx 0.3.2 12 Oct 2005
+
+ *) Feature: the Sun Studio 10 C compiler support.
+
+ *) Feature: the "proxy_upstream_max_fails",
+ "proxy_upstream_fail_timeout", "fastcgi_upstream_max_fails", and
+ "fastcgi_upstream_fail_timeout" directives.
+
+
+Changes with nginx 0.3.1 10 Oct 2005
+
+ *) Bugfix: the segmentation fault occurred when the signal queue
+ overflowed if the "rtsig" method was used; the bug had appeared in
+ 0.2.0.
+
+ *) Change: correct handling of the "\\", "\"", "\'", and "\$" pairs in
+ SSI.
+
+
+Changes with nginx 0.3.0 07 Oct 2005
+
+ *) Change: the 10-days live time limit of worker process was eliminated.
+ The limit was introduced because of millisecond timers overflow.
+
+
+Changes with nginx 0.2.6 05 Oct 2005
+
+ *) Change: while using load-balancing the time before the failed backend
+ retry was decreased from 60 to 10 seconds.
+
+ *) Change: the "proxy_pass_unparsed_uri" was canceled, the original URI
+ now passed, if the URI part is omitted in "proxy_pass" directive.
+
+ *) Feature: the "error_page" directive supports redirects and allows
+ more flexible to change an error code.
+
+ *) Change: the charset in the "Content-Type" header line now is ignored
+ in proxied subrequests.
+
+ *) Bugfix: if the URI was changed in the "if" block and request did not
+ found new configuration, then the ngx_http_rewrite_module rules ran
+ again.
+
+ *) Bugfix: if the "set" directive set the ngx_http_geo_module variable
+ in some configuration part, the this variable was not available in
+ other configuration parts and the "using uninitialized variable"
+ error was occurred; the bug had appeared in 0.2.2.
+
+
+Changes with nginx 0.2.5 04 Oct 2005
+
+ *) Change: the duplicate value of the ngx_http_geo_module variable now
+ causes the warning and changes old value.
+
+ *) Feature: the ngx_http_ssi_module supports the "set" command.
+
+ *) Feature: the ngx_http_ssi_module supports the "file" parameter in the
+ "include" command.
+
+ *) Feature: the ngx_http_ssi_module supports the variable value
+ substitutions in expressions of the "if" command.
+
+
+Changes with nginx 0.2.4 03 Oct 2005
+
+ *) Feature: the ngx_http_ssi_module supports "$var=text", "$var!=text",
+ "$var=/text/", and "$var!=/text/" expressions in the "if" command.
+
+ *) Bugfix: in proxying location without trailing slash; the bug had
+ appeared in 0.1.44.
+
+ *) Bugfix: the segmentation fault may occurred if the "rtsig" method was
+ used; the bug had appeared in 0.2.0.
+
+
+Changes with nginx 0.2.3 30 Sep 2005
+
+ *) Bugfix: nginx could not be built without the --with-debug option; the
+ bug had appeared in 0.2.2.
+
+
+Changes with nginx 0.2.2 30 Sep 2005
+
+ *) Feature: the "config errmsg" command of the ngx_http_ssi_module.
+
+ *) Change: the ngx_http_geo_module variables can be overridden by the
+ "set" directive.
+
+ *) Feature: the "ssl_protocols" and "ssl_prefer_server_ciphers"
+ directives of the ngx_http_ssl_module and ngx_imap_ssl_module.
+
+ *) Bugfix: the ngx_http_autoindex_module did not show correctly the long
+ file names;
+
+ *) Bugfix: the ngx_http_autoindex_module now do not show the files
+ starting by dot.
+
+ *) Bugfix: if the SSL handshake failed then another connection may be
+ closed too.
+ Thanks to Rob Mueller.
+
+ *) Bugfix: the export versions of MSIE 5.x could not connect via HTTPS.
+
+
+Changes with nginx 0.2.1 23 Sep 2005
+
+ *) Bugfix: if all backend using in load-balancing failed after one
+ error, then nginx may got caught in an endless loop; the bug had
+ appeared in 0.2.0.
+
+
+Changes with nginx 0.2.0 23 Sep 2005
+
+ *) The pid-file names used during online upgrade was changed and now is
+ not required a manual rename operation. The old master process adds
+ the ".oldbin" suffix to its pid-file and executes a new binary file.
+ The new master process creates usual pid-file without the ".newbin"
+ suffix. If the master process exits, then old master process renames
+ back its pid-file with the ".oldbin" suffix to the pid-file without
+ suffix.
+
+ *) Change: the "worker_connections" directive, new name of the
+ "connections" directive; now the directive specifies maximum number
+ of connections, but not maximum socket descriptor number.
+
+ *) Feature: SSL supports the session cache inside one worker process.
+
+ *) Feature: the "satisfy_any" directive.
+
+ *) Change: the ngx_http_access_module and ngx_http_auth_basic_module do
+ not run for subrequests.
+
+ *) Feature: the "worker_rlimit_nofile" and "worker_rlimit_sigpending"
+ directives.
+
+ *) Bugfix: if all backend using in load-balancing failed after one
+ error, then nginx did not try do connect to them during 60 seconds.
+
+ *) Bugfix: in IMAP/POP3 command argument parsing.
+ Thanks to Rob Mueller.
+
+ *) Bugfix: errors while using SSL in IMAP/POP3 proxy.
+
+ *) Bugfix: errors while using SSI and gzipping.
+
+ *) Bugfix: the "Expires" and "Cache-Control" header lines were omitted
+ from the 304 responses.
+ Thanks to Alexandr Kukushkin.
+
+
+Changes with nginx 0.1.45 08 Sep 2005
+
+ *) Change: the "ssl_engine" directive was canceled in the
+ ngx_http_ssl_module and now is introduced at global level.
+
+ *) Bugfix: the responses with SSI subrequests did not transferred via
+ SSL connection.
+
+ *) Various bug fixes in the IMAP/POP3 proxy.
+
+
+Changes with nginx 0.1.44 06 Sep 2005
+
+ *) Feature: the IMAP/POP3 proxy supports SSL.
+
+ *) Feature: the "proxy_timeout" directive of the ngx_imap_proxy_module.
+
+ *) Feature: the "userid_mark" directive.
+
+ *) Feature: the $remote_user variable value is determined independently
+ of authorization use.
+
+
+Changes with nginx 0.1.43 30 Aug 2005
+
+ *) Feature: the listen(2) backlog in the "listen" directive can be
+ changed using the -HUP signal.
+
+ *) Feature: the geo2nginx.pl script was added to contrib.
+
+ *) Change: the FastCGI parameters with the empty values now are passed
+ to a server.
+
+ *) Bugfix: the segmentation fault occurred or the worker process may got
+ caught in an endless loop if the proxied or FastCGI server sent the
+ "Cache-Control" header line and the "expires" directive was used; in
+ the proxied mode the bug had appeared in 0.1.29.
+
+
+Changes with nginx 0.1.42 23 Aug 2005
+
+ *) Bugfix: if the request URI had a zero length after the processing in
+ the ngx_http_proxy_module, then the segmentation fault or bus error
+ occurred in the ngx_http_proxy_module.
+
+ *) Bugfix: the "limit_rate" directive did not work inside the "if"
+ block; the bug had appeared in 0.1.38.
+
+
+Changes with nginx 0.1.41 25 Jul 2005
+
+ *) Bugfix: if the variable was used in the configuration file, then it
+ can not be used in SSI.
+
+
+Changes with nginx 0.1.40 22 Jul 2005
+
+ *) Bugfix: if a client sent too long header line, then the request
+ information did not logged in the error log.
+
+ *) Bugfix: the "Set-Cookie" header line was not transferred when the
+ "X-Accel-Redirect" was used; the bug had appeared in 0.1.39.
+
+ *) Bugfix: the "Content-Disposition" header line was not transferred
+ when the "X-Accel-Redirect" was used.
+
+ *) Bugfix: the master process did not close the listen socket on the
+ SIGQUIT signal.
+
+ *) Bugfix: after on-line upgrade on Linux and Solaris the process name
+ became shorter in the "ps" command.
+
+
+Changes with nginx 0.1.39 14 Jul 2005
+
+ *) The changes in the ngx_http_charset_module: the "default_charset"
+ directive was canceled; the "charset" directive sets the response
+ charset; the "source_charset" directive sets the source charset only.
+
+ *) Bugfix: the backend "WWW-Authenticate" header line did not
+ transferred while the 401 response code redirecting.
+
+ *) Bugfix: the ngx_http_proxy_module and ngx_http_fastcgi_module may
+ close a connection before anything was transferred to a client; the
+ bug had appeared in 0.1.38.
+
+ *) Workaround: the Linux glibc crypt_r() initialization bug.
+
+ *) Bugfix: the ngx_http_ssi_module did not support the relative URI in
+ the "include virtual" command.
+
+ *) Bugfix: if the backend response had the "Location" header line and
+ nginx should not rewrite this line, then the 500 code response body
+ was transferred; the bug had appeared in 0.1.29.
+
+ *) Bugfix: some directives of the ngx_http_proxy_module and
+ ngx_http_fastcgi_module were not inherited from the server to the
+ location level; the bug had appeared in 0.1.29.
+
+ *) Bugfix: the ngx_http_ssl_module did not support the certificate
+ chain.
+
+ *) Bugfix: the ngx_http_autoindex_module did not show correctly the long
+ file names; the bug had appeared in 0.1.38.
+
+ *) Bugfixes in IMAP/POP3 proxy in interaction with a backend at the
+ login state.
+
+
+Changes with nginx 0.1.38 08 Jul 2005
+
+ *) Feature: the "limit_rate" directive is supported in proxy and FastCGI
+ mode.
+
+ *) Feature: the "X-Accel-Limit-Rate" response header line is supported
+ in proxy and FastCGI mode.
+
+ *) Feature: the "break" directive.
+
+ *) Feature: the "log_not_found" directive.
+
+ *) Bugfix: the response status code was not changed when request was
+ redirected by the ""X-Accel-Redirect" header line.
+
+ *) Bugfix: the variables set by the "set" directive could not be used in
+ SSI.
+
+ *) Bugfix: the segmentation fault may occurred if the SSI page has more
+ than one remote subrequest.
+
+ *) Bugfix: nginx treated the backend response as invalid if the status
+ line in the header was transferred in two packets; the bug had
+ appeared in 0.1.29.
+
+ *) Feature: the "ssi_types" directive.
+
+ *) Feature: the "autoindex_exact_size" directive.
+
+ *) Bugfix: the ngx_http_autoindex_module did not support the long file
+ names in UTF-8.
+
+ *) Feature: the IMAP/POP3 proxy.
+
+
+Changes with nginx 0.1.37 23 Jun 2005
+
+ *) Change: now the "\n" is added to the end of the "nginx.pid" file.
+
+ *) Bugfix: the responses may be transferred not completely, if many
+ parts or the big parts were included by SSI.
+
+ *) Bugfix: if all backends had returned the 404 reponse and the
+ "http_404" parameter of the "proxy_next_upstream" or
+ "fastcgi_next_upstream" directives was used, then nginx started to
+ request all backends again.
+
+
+Changes with nginx 0.1.36 15 Jun 2005
+
+ *) Change: if the request header has duplicate the "Host", "Connection",
+ "Content-Length", or "Authorization" lines, then nginx now returns
+ the 400 error.
+
+ *) Change: the "post_accept_timeout" directive was canceled.
+
+ *) Feature: the "default", "af=", "bl=", "deferred", and "bind"
+ parameters of the "listen" directive.
+
+ *) Feature: the FreeBSD accept filters support.
+
+ *) Feature: the Linux TCP_DEFER_ACCEPT support.
+
+ *) Bugfix: the ngx_http_autoindex_module did not support the file names
+ in UTF-8.
+
+ *) Bugfix: the new log file can be rotated by the -USR1 signal only if
+ the reconfiguration by the -HUP signal was made twice.
+
+
+Changes with nginx 0.1.35 07 Jun 2005
+
+ *) Feature: the "working_directory" directive.
+
+ *) Feature: the "port_in_redirect" directive.
+
+ *) Bugfix: the segmentation fault was occurred if the backend response
+ header was in several packets; the bug had appeared in 0.1.29.
+
+ *) Bugfix: if more than 10 servers were configured or some server did
+ not use the "listen" directive, then the segmentation fault was
+ occurred on the start.
+
+ *) Bugfix: the segmentation fault might occur if the response was bigger
+ than the temporary file.
+
+ *) Bugfix: nginx returned the 400 response on requests like
+ "GET http://www.domain.com/uri HTTP/1.0"; the bug had appeared in
+ 0.1.28.
+
+
+Changes with nginx 0.1.34 26 May 2005
+
+ *) Bugfix: the worker process may got caught in an endless loop if the
+ big response part were include by SSI.
+
+ *) Bugfix: the variables set by the "set" directive were not available
+ in SSI.
+
+ *) Feature: the "autoindex_localtime" directive.
+
+ *) Bugfix: the empty value of the "proxy_set_header" directive forbids
+ the client request header line passing.
+
+
+Changes with nginx 0.1.33 23 May 2005
+
+ *) Bugfix: nginx could not be built with the --without-pcre parameter;
+ the bug had appeared in 0.1.29.
+
+ *) Bugfix: 3, 4, 7, and 8 the "proxy_set_header" directives in one level
+ cause the bus fault on start up.
+
+ *) Bugfix: the HTTP protocol was specified in the HTTPS redirects.
+
+ *) Bugfix: if the "rewrite" directive used the captures inside the "if"
+ directive, then the 500 error code was returned.
+
+
+Changes with nginx 0.1.32 19 May 2005
+
+ *) Bugfix: the arguments were omitted in the redirects, issued by the
+ "rewrite" directive; the bug had appeared in 0.1.29.
+
+ *) Feature: the "if" directive supports the captures in regular
+ expressions.
+
+ *) Feature: the "set" directive supports the variables and the captures
+ of regular expressions.
+
+ *) Feature: the "X-Accel-Redirect" response header line is supported in
+ proxy and FastCGI mode.
+
+
+Changes with nginx 0.1.31 16 May 2005
+
+ *) Bugfix: the response encrypted by SSL may not transferred complete.
+
+ *) Bugfix: errors while processing FastCGI response by SSI.
+
+ *) Bugfix: errors while using SSI and gzipping.
+
+ *) Bugfix: the redirect with the 301 code was transferred without
+ response body; the bug had appeared in 0.1.30.
+
+
+Changes with nginx 0.1.30 14 May 2005
+
+ *) Bugfix: the worker process may got caught in an endless loop if the
+ SSI was used.
+
+ *) Bugfix: the response encrypted by SSL may not transferred complete.
+
+ *) Bugfix: if the length of the response part received at once from
+ proxied or FastCGI server was equal to 500, then nginx returns the
+ 500 response code; in proxy mode the bug had appeared in 0.1.29 only.
+
+ *) Bugfix: nginx did not consider the directives with 8 or 9 parameters
+ as invalid.
+
+ *) Feature: the "return" directive can return the 204 response code.
+
+ *) Feature: the "ignore_invalid_headers" directive.
+
+
+Changes with nginx 0.1.29 12 May 2005
+
+ *) Feature: the ngx_http_ssi_module supports "include virtual" command.
+
+ *) Feature: the ngx_http_ssi_module supports the condition command like
+ 'if expr="$NAME"' and "else" and "endif" commands. Only one nested
+ level is supported.
+
+ *) Feature: the ngx_http_ssi_module supports the DATE_LOCAL and DATE_GMT
+ variables and "config timefmt" command.
+
+ *) Feature: the "ssi_ignore_recycled_buffers" directive.
+
+ *) Bugfix: the "echo" command did not show the default value for the
+ empty QUERY_STRING variable.
+
+ *) Change: the ngx_http_proxy_module was rewritten.
+
+ *) Feature: the "proxy_redirect", "proxy_pass_request_headers",
+ "proxy_pass_request_body", and "proxy_method" directives.
+
+ *) Feature: the "proxy_set_header" directive. The "proxy_x_var" was
+ canceled and must be replaced with the proxy_set_header directive.
+
+ *) Change: the "proxy_preserve_host" is canceled and must be replaced
+ with the "proxy_set_header Host $host" and the "proxy_redirect off"
+ directives, the "proxy_set_header Host $host:$proxy_port" directive
+ and the appropriate proxy_redirect directives.
+
+ *) Change: the "proxy_set_x_real_ip" is canceled and must be replaced
+ with the "proxy_set_header X-Real-IP $remote_addr" directive.
+
+ *) Change: the "proxy_add_x_forwarded_for" is canceled and must be
+ replaced with
+ the "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for"
+ directive.
+
+ *) Change: the "proxy_set_x_url" is canceled and must be replaced with
+ the "proxy_set_header X-URL http://$host:$server_port$request_uri"
+ directive.
+
+ *) Feature: the "fastcgi_param" directive.
+
+ *) Change: the "fastcgi_root", "fastcgi_set_var" and "fastcgi_params"
+ directive are canceled and must be replaced with the fastcgi_param
+ directives.
+
+ *) Feature: the "index" directive can use the variables.
+
+ *) Feature: the "index" directive can be used at http and server levels.
+
+ *) Change: the last index only in the "index" directive can be absolute.
+
+ *) Feature: the "rewrite" directive can use the variables.
+
+ *) Feature: the "internal" directive.
+
+ *) Feature: the CONTENT_LENGTH, CONTENT_TYPE, REMOTE_PORT, SERVER_ADDR,
+ SERVER_PORT, SERVER_PROTOCOL, DOCUMENT_ROOT, SERVER_NAME,
+ REQUEST_METHOD, REQUEST_URI, and REMOTE_USER variables.
+
+ *) Change: nginx now passes the invalid lines in a client request
+ headers or a backend response header.
+
+ *) Bugfix: if the backend did not transfer response for a long time and
+ the "send_timeout" was less than "proxy_read_timeout", then nginx
+ returned the 408 response.
+
+ *) Bugfix: the segmentation fault was occurred if the backend sent an
+ invalid line in response header; the bug had appeared in 0.1.26.
+
+ *) Bugfix: the segmentation fault may occurred in FastCGI fault
+ tolerance configuration.
+
+ *) Bugfix: the "expires" directive did not remove the previous "Expires"
+ and "Cache-Control" headers.
+
+ *) Bugfix: nginx did not take into account trailing dot in "Host" header
+ line.
+
+ *) Bugfix: the ngx_http_auth_module did not work under Linux.
+
+ *) Bugfix: the rewrite directive worked incorrectly, if the arguments
+ were in a request.
+
+ *) Bugfix: nginx could not be built on MacOS X.
+
+
+Changes with nginx 0.1.28 08 Apr 2005
+
+ *) Bugfix: nginx hogs CPU while proxying the huge files.
+
+ *) Bugfix: nginx could not be built by gcc 4.0 on Linux.
+
+
+Changes with nginx 0.1.27 28 Mar 2005
+
+ *) Feature: the "blocked" parameter of the "valid_referers" directive.
+
+ *) Change: the errors while handling the request header now logged at
+ "info" level. The server name and the "Host" and "Referer" header
+ lines also logged.
+
+ *) Change: the "Host" header line is also logged in error log.
+
+ *) Feature: the proxy_pass_unparsed_uri directive. The special handling
+ of the "://" symbols in URI, appeared in 0.1.11 version, now is
+ canceled.
+
+ *) Bugfix: nginx could not be built on FreeBSD and Linux, if the
+ --without-ngx_http_auth_basic_module configuration parameter was
+ used.
+
+
+Changes with nginx 0.1.26 22 Mar 2005
+
+ *) Change: the invalid client header lines are now ignored and logged at
+ the info level.
+
+ *) Change: the server name is also logged in error log.
+
+ *) Feature: the ngx_http_auth_basic_module module and the auth_basic and
+ auth_basic_user_file directives.
+
+
+Changes with nginx 0.1.25 19 Mar 2005
+
+ *) Bugfix: nginx did run on Linux parisc.
+
+ *) Feature: nginx now does not start under FreeBSD if the sysctl
+ kern.ipc.somaxconn value is too big.
+
+ *) Bugfix: if a request was internally redirected by the
+ ngx_http_index_module module to the ngx_http_proxy_module or
+ ngx_http_fastcgi_module modules, then the index file was not closed
+ after request completion.
+
+ *) Feature: the "proxy_pass" can be used in location with regular
+ expression.
+
+ *) Feature: the ngx_http_rewrite_filter_module module supports the
+ condition like "if ($HTTP_USER_AGENT ~ MSIE)".
+
+ *) Bugfix: nginx started too slow if the large number of addresses and
+ text values were used in the "geo" directive.
+
+ *) Change: a variable name must be declared as "$name" in the "geo"
+ directive. The previous variant without "$" is still supported, but
+ will be removed soon.
+
+ *) Feature: the "%{VARIABLE}v" logging parameter.
+
+ *) Feature: the "set $name value" directive.
+
+ *) Bugfix: gcc 4.0 compatibility.
+
+ *) Feature: the --with-openssl-opt=OPTIONS autoconfiguration directive.
+
+
+Changes with nginx 0.1.24 04 Mar 2005
+
+ *) Feature: the ngx_http_ssi_filter_module supports the QUERY_STRING and
+ DOCUMENT_URI variables.
+
+ *) Bugfix: the ngx_http_autoindex_module may some times return the 404
+ response for existent directory, if this directory was used in
+ "alias" directive.
+
+ *) Bugfix: the ngx_http_ssi_filter_module ran incorrectly for large
+ responses.
+
+ *) Bugfix: the lack of the "Referer" header line was always accounted as
+ valid referrer.
+
+
+Changes with nginx 0.1.23 01 Mar 2005
+
+ *) Feature: the ngx_http_ssi_filter_module and the ssi,
+ ssi_silent_errors, and ssi_min_file_chunk directives. The 'echo
+ var="HTTP_..." default=""' and 'echo var="REMOTE_ADDR"' commands are
+ supported.
+
+ *) Feature: the %request_time log parameter.
+
+ *) Feature: if the request has no the "Host" header line, then the
+ "proxy_preserve_host" directive set this header line to the first
+ server name of the "server_name" directive.
+
+ *) Bugfix: nginx could not be built on platforms different from i386,
+ amd64, sparc, and ppc; the bug had appeared in 0.1.22.
+
+ *) Bugfix: the ngx_http_autoindex_module now shows the information not
+ about the symlink, but about file or directory it points to.
+
+ *) Bugfix: the %apache_length parameter logged the negative length of
+ the response header if the no response was transferred to a client.
+
+
+Changes with nginx 0.1.22 22 Feb 2005
+
+ *) Bugfix: the ngx_http_stub_status_module showed incorrect handled
+ connections statistics if the proxying or FastCGI server were used.
+
+ *) Bugfix: the installation paths were incorrectly quoted on Linux and
+ Solaris; the bug had appeared in 0.1.21.
+
+
+Changes with nginx 0.1.21 22 Feb 2005
+
+ *) Bugfix: the ngx_http_stub_status_module showed incorrect statistics
+ if "rtsig" method was used or if several worker process ran on SMP.
+
+ *) Bugfix: nginx could not be built by the icc compiler on Linux or if
+ the zlib-1.2.x library was building from sources.
+
+ *) Bugfix: nginx could not be built on NetBSD 2.0.
+
+
+Changes with nginx 0.1.20 17 Feb 2005
+
+ *) Feature: the new "script_filename" and "remote_port" parameters of
+ the fastcgi_params directive.
+
+ *) Bugfix: the FastCGI stderr stream was handled incorrectly.
+
+
+Changes with nginx 0.1.19 16 Feb 2005
+
+ *) Bugfix: now, if request contains the zero, then the 404 error is
+ returned for the local requests.
+
+ *) Bugfix: nginx could not be built on NetBSD 2.0.
+
+ *) Bugfix: the timeout may occur while reading of the client request
+ body via SSL connections.
+
+
+Changes with nginx 0.1.18 09 Feb 2005
+
+ *) Workaround: the default values of the devpoll_events and the
+ devpoll_changes directives changed from 512 to 32 to be compatible
+ with Solaris 10.
+
+ *) Bugfix: the proxy_set_x_var and fastcgi_set_var directives were not
+ inherited.
+
+ *) Bugfix: in a redirect rewrite directive arguments were concatenated
+ with URI by an "&" rather than a "?".
+
+ *) Bugfix: the lines without trailing ";" in the file being included by
+ the ngx_http_geo_module were silently ignored.
+
+ *) Feature: the ngx_http_stub_status_module.
+
+ *) Bugfix: the unknown log format in the access_log directive caused the
+ segmentation fault.
+
+ *) Feature: the new "document_root" parameter of the fastcgi_params
+ directive.
+
+ *) Feature: the fastcgi_redirect_errors directive.
+
+ *) Feature: the new "break" modifier of the "rewrite" directive allows
+ to stop the rewrite/location cycle and sets the current configuration
+ to the request.
+
+
+Changes with nginx 0.1.17 03 Feb 2005
+
+ *) Change: the ngx_http_rewrite_module was rewritten from the scratch.
+ Now it is possible to redirect, to return the error codes, to check
+ the variables and referrers. The directives can be used inside
+ locations. The redirect directive was canceled.
+
+ *) Feature: the ngx_http_geo_module.
+
+ *) Feature: the proxy_set_x_var and fastcgi_set_var directives.
+
+ *) Bugfix: the location configuration with "=" modifier may be used in
+ another location.
+
+ *) Bugfix: the correct content type was set only for requests that use
+ small caps letters in extension.
+
+ *) Bugfix: if the proxy_pass or fastcgi_pass directives were set in the
+ location, and access was denied, and the error was redirected to a
+ static page, then the segmentation fault occurred.
+
+ *) Bugfix: if in a proxied "Location" header was a relative URL, then a
+ host name and a slash were added to them; the bug had appeared in
+ 0.1.14.
+
+ *) Bugfix: the system error message was not logged on Linux.
+
+
+Changes with nginx 0.1.16 25 Jan 2005
+
+ *) Bugfix: if the response were transferred by chunks, then on the HEAD
+ request the final chunk was issued.
+
+ *) Bugfix: the "Connection: keep-alive" header were issued, even if the
+ keepalive_timeout directive forbade the keep-alive use.
+
+ *) Bugfix: the errors in the ngx_http_fastcgi_module caused the
+ segmentation faults.
+
+ *) Bugfix: the compressed response encrypted by SSL may not transferred
+ complete.
+
+ *) Bugfix: the TCP-specific TCP_NODELAY, TCP_NOPUSH, and TCP_CORK
+ options, are not used for the unix domain sockets.
+
+ *) Feature: the rewrite directive supports the arguments rewriting.
+
+ *) Bugfix: the response code 400 was returned for the POST request with
+ the "Content-Length: 0" header; the bug had appeared in 0.1.14.
+
+
+Changes with nginx 0.1.15 19 Jan 2005
+
+ *) Bugfix: the error while the connecting to the FastCGI server caused
+ segmentation fault.
+
+ *) Bugfix: the correct handling of the regular expression, that has
+ different number of the captures and substitutions.
+
+ *) Feature: the location, that is passed to the FastCGI server, can be
+ regular expression.
+
+ *) Bugfix: the FastCGI's parameter REQUEST_URI is now passed with the
+ arguments and in the original state.
+
+ *) Bugfix: the ngx_http_rewrite_module module was required to be built
+ to use the regular expressions in locations.
+
+ *) Bugfix: the directive "proxy_preserve_host on" adds port 80 to the
+ "Host" headers, if upstream listen on port 80; the bug had appeared
+ in 0.1.14.
+
+ *) Bugfix: the same paths in autoconfiguration parameters
+ --http-client-body-temp-path=PATH and --http-proxy-temp-path=PATH, or
+ --http-client-body-temp-path=PATH and --http-fastcgi-temp-path=PATH
+ caused segmentation fault.
+
+
+Changes with nginx 0.1.14 18 Jan 2005
+
+ *) Feature: the autoconfiguration directives:
+ --http-client-body-temp-path=PATH, --http-proxy-temp-path=PATH, and
+ --http-fastcgi-temp-path=PATH
+
+ *) Change: the directory name for the temporary files with the client
+ request body is specified by directive client_body_temp_path, by
+ default it is <prefix>/client_body_temp.
+
+ *) Feature: the ngx_http_fastcgi_module and the directives:
+ fastcgi_pass, fastcgi_root, fastcgi_index, fastcgi_params,
+ fastcgi_connect_timeout, fastcgi_send_timeout, fastcgi_read_timeout,
+ fastcgi_send_lowat, fastcgi_header_buffer_size, fastcgi_buffers,
+ fastcgi_busy_buffers_size, fastcgi_temp_path,
+ fastcgi_max_temp_file_size, fastcgi_temp_file_write_size,
+ fastcgi_next_upstream, and fastcgi_x_powered_by.
+
+ *) Bugfix: the "[alert] zero size buf" error; the bug had appeared in
+ 0.1.3.
+
+ *) Change: the URI must be specified after the host name in the
+ proxy_pass directive.
+
+ *) Change: the %3F symbol in the URI was considered as the argument
+ string start.
+
+ *) Feature: the unix domain sockets support in the
+ ngx_http_proxy_module.
+
+ *) Feature: the ssl_engine and ssl_ciphers directives.
+ Thanks to Sergey Skvortsov for SSL-accelerator.
+
+
+Changes with nginx 0.1.13 21 Dec 2004
+
+ *) Feature: the server_names_hash and server_names_hash_threshold
+ directives.
+
+ *) Bugfix: the *.domain.tld names in the "server_name" directive did not
+ work.
+
+ *) Bugfix: the %request_length log parameter logged the incorrect
+ length.
+
+
+Changes with nginx 0.1.12 06 Dec 2004
+
+ *) Feature: the %request_length log parameter.
+
+ *) Bugfix: when using the /dev/poll, select and poll on the platforms,
+ where these methods may do the false reports, there may be the long
+ delay when the request was passed via the keep-alive connection. It
+ may be at least on Solaris when using the /dev/poll.
+
+ *) Bugfix: the send_lowat directive is ignored on Linux because Linux
+ does not support the SO_SNDLOWAT option.
+
+
+Changes with nginx 0.1.11 02 Dec 2004
+
+ *) Feature: the worker_priority directive.
+
+ *) Change: both tcp_nopush and tcp_nodelay directives affect the
+ transferred response.
+
+ *) Bugfix: nginx did not call initgroups().
+ Thanks to Andrew Sitnikov and Andrei Nigmatulin.
+
+ *) Change: now the ngx_http_autoindex_module shows the file size in the
+ bytes.
+
+ *) Bugfix: the ngx_http_autoindex_module returned the 500 error if the
+ broken symlink was in a directory.
+
+ *) Bugfix: the files bigger than 4G could not be transferred using
+ sendfile.
+
+ *) Bugfix: if the backend was resolved to several backends and there was
+ an error while the response waiting then process may got caught in an
+ endless loop.
+
+ *) Bugfix: the worker process may exit with the "unknown cycle" message
+ when the /dev/poll method was used.
+
+ *) Bugfix: "close() channel failed" errors.
+
+ *) Bugfix: the autodetection of the "nobody" and "nogroup" groups.
+
+ *) Bugfix: the send_lowat directive did not work on Linux.
+
+ *) Bugfix: the segmentation fault occurred if there was no events
+ section in configuration.
+
+ *) Bugfix: nginx could not be built on OpenBSD.
+
+ *) Bugfix: the double slashes in "://" in the URI were converted to
+ ":/".
+
+
+Changes with nginx 0.1.10 26 Nov 2004
+
+ *) Bugfix: if the request without arguments contains "//", "/./", "/../"
+ or "%XX" then the last character in the request line was lost; the
+ bug had appeared in 0.1.9.
+
+ *) Bugfix: the fix in 0.1.9 for the files bigger than 2G on Linux did
+ not work.
+
+
+Changes with nginx 0.1.9 25 Nov 2004
+
+ *) Bugfix: the proxied request was sent without arguments if the request
+ contains "//", "/./", "/../" or "%XX".
+
+ *) Bugfix: the large compressed responses may be transferred not
+ completely.
+
+ *) Bugfix: the files bigger than 2G was not transferred on Linux that
+ does not support sendfile64().
+
+ *) Bugfix: while the build configuration on Linux the --with-poll_module
+ parameter was required; the bug had appeared in 0.1.8.
+
+
+Changes with nginx 0.1.8 20 Nov 2004
+
+ *) Bugfix: in the ngx_http_autoindex_module if the long file names were
+ in the listing.
+
+ *) Feature: the "^~" modifier in the location directive.
+
+ *) Feature: the proxy_max_temp_file_size directive.
+
+
+Changes with nginx 0.1.7 12 Nov 2004
+
+ *) Bugfix: on FreeBSD the segmentation fault may occur if the size of
+ the transferred file was changed; the bug had appeared in 0.1.5.
+
+
+Changes with nginx 0.1.6 11 Nov 2004
+
+ *) Bugfix: some location directive combinations with the regular
+ expressions caused the wrong configuration choose.
+
+
+Changes with nginx 0.1.5 11 Nov 2004
+
+ *) Bugfix: on Solaris and Linux there may be too many "recvmsg()
+ returned not enough data" alerts.
+
+ *) Bugfix: there were the "writev() failed (22: Invalid argument)"
+ errors on Solaris in proxy mode without sendfile. On other platforms
+ that do not support sendfile at all the process got caught in an
+ endless loop.
+
+ *) Bugfix: segmentation fault on Solaris in proxy mode and using
+ sendfile.
+
+ *) Bugfix: segmentation fault on Solaris.
+
+ *) Bugfix: on-line upgrade did not work on Linux.
+
+ *) Bugfix: the ngx_http_autoindex_module module did not escape the
+ spaces, the quotes, and the percent signs in the directory listing.
+
+ *) Change: the decrease of the copy operations.
+
+ *) Feature: the userid_p3p directive.
+
+
+Changes with nginx 0.1.4 26 Oct 2004
+
+ *) Bugfix: in the ngx_http_autoindex_module.
+
+
+Changes with nginx 0.1.3 25 Oct 2004
+
+ *) Feature: the ngx_http_autoindex_module and the autoindex directive.
+
+ *) Feature: the proxy_set_x_url directive.
+
+ *) Bugfix: proxy module may get caught in an endless loop when sendfile
+ is not used.
+
+
+Changes with nginx 0.1.2 21 Oct 2004
+
+ *) Feature: the --user=USER, --group=GROUP, and --with-ld-opt=OPTIONS
+ options in configure.
+
+ *) Feature: the server_name directive supports *.domain.tld.
+
+ *) Bugfix: the portability improvements.
+
+ *) Bugfix: if configuration file was set in command line, the
+ reconfiguration was impossible; the bug had appeared in 0.1.1.
+
+ *) Bugfix: proxy module may get caught in an endless loop when sendfile
+ is not used.
+
+ *) Bugfix: with sendfile the response was not recoded according to the
+ charset module directives; the bug had appeared in 0.1.1.
+
+ *) Bugfix: very seldom bug in the kqueue processing.
+
+ *) Bugfix: the gzip module compressed the proxied responses that was
+ already compressed.
+
+
+Changes with nginx 0.1.1 11 Oct 2004
+
+ *) Feature: the gzip_types directive.
+
+ *) Feature: the tcp_nodelay directive.
+
+ *) Feature: the send_lowat directive is working not only on OSes that
+ support kqueue NOTE_LOWAT, but also on OSes that support SO_SNDLOWAT.
+
+ *) Feature: the setproctitle() emulation for Linux and Solaris.
+
+ *) Bugfix: the "Location" header rewrite bug fixed while the proxying.
+
+ *) Bugfix: the ngx_http_chunked_module module may get caught in an
+ endless loop.
+
+ *) Bugfix: the /dev/poll module bugs fixed.
+
+ *) Bugfix: the responses were corrupted when the temporary files were
+ used while the proxying.
+
+ *) Bugfix: the unescaped requests were passed to the backend.
+
+ *) Bugfix: while the build configuration on Linux 2.4 the
+ --with-poll_module parameter was required.
+
+
+Changes with nginx 0.1.0 04 Oct 2004
+
+ *) The first public version.
+
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/CHANGES.ru b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/CHANGES.ru
new file mode 100644
index 00000000000..720180c01af
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/CHANGES.ru
@@ -0,0 +1,6875 @@
+
+Изменения в nginx 1.7.4 05.08.2014
+
+ *) Безопасность: pipelined-команды не отбрасывались после команды
+ STARTTLS в SMTP прокси-сервере (CVE-2014-3556); ошибка появилась в
+ 1.5.6.
+ Спасибо Chris Boulton.
+
+ *) Изменение: экранирование символов в URI теперь использует
+ шестнадцатеричные цифры в верхнем регистре.
+ Спасибо Piotr Sikora.
+
+ *) Добавление: теперь nginx можно собрать с BoringSSL и LibreSSL.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: запросы могли зависать, если использовался resolver и
+ DNS-сервер возвращал некорректный ответ; ошибка появилась в 1.5.8.
+
+ *) Исправление: в модуле ngx_http_spdy_module.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: переменная $uri могла содержать мусор при возврате
+ ошибок с кодом 400.
+ Спасибо Сергею Боброву.
+
+ *) Исправление: в обработке ошибок в директиве proxy_store и в модуле
+ ngx_http_dav_module.
+ Спасибо Feng Gu.
+
+ *) Исправление: при логгировании ошибок в syslog мог происходить
+ segmentation fault; ошибка появилась в 1.7.1.
+
+ *) Исправление: переменные $geoip_latitude, $geoip_longitude,
+ $geoip_dma_code и $geoip_area_code могли не работать.
+ Спасибо Yichun Zhang.
+
+ *) Исправление: в обработке ошибок выделения памяти.
+ Спасибо Tatsuhiko Kubo и Piotr Sikora.
+
+
+Изменения в nginx 1.7.3 08.07.2014
+
+ *) Добавление: weak entity tags теперь не удаляются при изменениях
+ ответа, а strong entity tags преобразуются в weak.
+
+ *) Добавление: ревалидация элементов кэша теперь, если это возможно,
+ использует заголовок If-None-Match.
+
+ *) Добавление: директива ssl_password_file.
+
+ *) Исправление: при возврате ответа из кэша заголовок запроса
+ If-None-Match игнорировался, если в ответе не было заголовка
+ Last-Modified.
+
+ *) Исправление: сообщения "peer closed connection in SSL handshake" при
+ соединении с бэкендами логгировались на уровне info вместо error.
+
+ *) Исправление: в модуле ngx_http_dav_module в nginx/Windows.
+
+ *) Исправление: SPDY-соединения могли неожиданно закрываться, если
+ использовалось кэширование.
+
+
+Изменения в nginx 1.7.2 17.06.2014
+
+ *) Добавление: директива hash в блоке upstream.
+
+ *) Добавление: дефрагментация свободных блоков разделяемой памяти.
+ Спасибо Wandenberg Peixoto и Yichun Zhang.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовалось значение access_log по умолчанию; ошибка
+ появилась в 1.7.0.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: завершающий слэш ошибочно удалялся из последнего
+ параметра директивы try_files.
+
+ *) Исправление: nginx мог не собираться на OS X.
+
+ *) Исправление: в модуле ngx_http_spdy_module.
+
+
+Изменения в nginx 1.7.1 27.05.2014
+
+ *) Добавление: переменные "$upstream_cookie_...".
+
+ *) Добавление: переменная $ssl_client_fingerprint.
+
+ *) Добавление: директивы error_log и access_log теперь поддерживают
+ логгирование в syslog.
+
+ *) Добавление: почтовый прокси-сервер теперь логгирует порт клиента при
+ соединении.
+
+ *) Исправление: утечки памяти при использовании директивы
+ "ssl_stapling".
+ Спасибо Filipe da Silva.
+
+ *) Исправление: директива alias внутри location'а, заданного регулярным
+ выражением, работала неправильно, если использовались директивы if
+ или limit_except.
+
+ *) Исправление: директива charset не ставила кодировку для сжатых
+ ответов бэкендов.
+
+ *) Исправление: директива proxy_pass без URI могла использовать
+ оригинальный запрос после установки переменной $args.
+ Спасибо Yichun Zhang.
+
+ *) Исправление: в работе параметра none директивы smtp_auth; ошибка
+ появилась в 1.5.6.
+ Спасибо Святославу Никольскому.
+
+ *) Исправление: при совместном использовании sub_filter и SSI ответы
+ могли передаваться неверно.
+
+ *) Исправление: nginx не собирался с параметром --with-file-aio на
+ Linux/aarch64.
+
+
+Изменения в nginx 1.7.0 24.04.2014
+
+ *) Добавление: проверка SSL-сертификатов бэкендов.
+
+ *) Добавление: поддержка SNI при работе с бэкендами по SSL.
+
+ *) Добавление: переменная $ssl_server_name.
+
+ *) Добавление: параметр if директивы access_log.
+
+
+Изменения в nginx 1.5.13 08.04.2014
+
+ *) Изменение: улучшена обработка хэш-таблиц; в директивах
+ variables_hash_max_size и types_hash_bucket_size значения по
+ умолчанию изменены на 1024 и 64 соответственно.
+
+ *) Добавление: модуль ngx_http_mp4_module теперь понимает аргумент end.
+
+ *) Добавление: поддержка byte ranges модулем ngx_http_mp4_module и при
+ сохранении ответов в кэш.
+
+ *) Исправление: теперь nginx не пишет в лог сообщения "ngx_slab_alloc()
+ failed: no memory" при использовании разделяемой памяти в
+ ssl_session_cache и в модуле ngx_http_limit_req_module.
+
+ *) Исправление: директива underscores_in_headers не разрешала
+ подчёркивание в первом символе заголовка.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: cache manager мог нагружать процессор при выходе в
+ nginx/Windows.
+
+ *) Исправление: при использовании ssl_session_cache с параметром shared
+ рабочий процесс nginx/Windows завершался аварийно.
+
+ *) Исправление: в модуле ngx_http_spdy_module.
+
+
+Изменения в nginx 1.5.12 18.03.2014
+
+ *) Безопасность: при обработке специально созданного запроса модулем
+ ngx_http_spdy_module могло происходить переполнение буфера в рабочем
+ процессе, что потенциально могло приводить к выполнению произвольного
+ кода (CVE-2014-0133).
+ Спасибо Lucas Molas из Programa STIC, Fundación Dr. Manuel Sadosky,
+ Buenos Aires, Argentina.
+
+ *) Добавление: параметр proxy_protocol в директивах listen и
+ real_ip_header, переменная $proxy_protocol_addr.
+
+ *) Исправление: в директиве fastcgi_next_upstream.
+ Спасибо Lucas Molas.
+
+
+Изменения в nginx 1.5.11 04.03.2014
+
+ *) Безопасность: при обработке специально созданного запроса модулем
+ ngx_http_spdy_module на 32-битных платформах могла повреждаться
+ память рабочего процесса, что потенциально могло приводить к
+ выполнению произвольного кода (CVE-2014-0088); ошибка появилась в
+ 1.5.10.
+ Спасибо Lucas Molas из Programa STIC, Fundación Dr. Manuel Sadosky,
+ Buenos Aires, Argentina.
+
+ *) Добавление: переменная $ssl_session_reused.
+
+ *) Исправление: директива client_max_body_size могла не работать при
+ чтении тела запроса с использованием chunked transfer encoding;
+ ошибка появилась в 1.3.9.
+ Спасибо Lucas Molas.
+
+ *) Исправление: при проксировании WebSocket-соединений в рабочем
+ процессе мог произойти segmentation fault.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовался модуль ngx_http_spdy_module на 32-битных
+ платформах; ошибка появилась в 1.5.10.
+
+ *) Исправление: значение переменной $upstream_status могло быть
+ неверным, если использовались директивы proxy_cache_use_stale или
+ proxy_cache_revalidate.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если ошибки с кодом 400 с помощью директивы error_page
+ перенаправлялись в именованный location.
+
+ *) Исправление: nginx/Windows не собирался с Visual Studio 2013.
+
+
+Изменения в nginx 1.5.10 04.02.2014
+
+ *) Добавление: модуль ngx_http_spdy_module теперь использует протокол
+ SPDY 3.1.
+ Спасибо Automattic и MaxCDN за спонсирование разработки.
+
+ *) Добавление: модуль ngx_http_mp4_module теперь пропускает дорожки,
+ имеющие меньшую длину, чем запрошенная перемотка.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если переменная $ssl_session_id использовалась при логгировании;
+ ошибка появилась в 1.5.9.
+
+ *) Исправление: переменные $date_local и $date_gmt использовали неверный
+ формат вне модуля ngx_http_ssi_filter_module.
+
+ *) Исправление: клиентские соединения могли сразу закрываться, если
+ использовался отложенный accept; ошибка появилась в 1.3.15.
+
+ *) Исправление: сообщения "getsockopt(TCP_FASTOPEN) ... failed"
+ записывались в лог в процессе обновления исполняемого файла на Linux;
+ ошибка появилась в 1.5.8.
+ Спасибо Piotr Sikora.
+
+
+Изменения в nginx 1.5.9 22.01.2014
+
+ *) Изменение: теперь в заголовке X-Accel-Redirect nginx ожидает
+ закодированный URI.
+
+ *) Добавление: директива ssl_buffer_size.
+
+ *) Добавление: директиву limit_rate теперь можно использовать для
+ ограничения скорости передачи ответов клиенту в SPDY-соединениях.
+
+ *) Добавление: директива spdy_chunk_size.
+
+ *) Добавление: директива ssl_session_tickets.
+ Спасибо Dirkjan Bussink.
+
+ *) Исправление: переменная $ssl_session_id содержала всю сессию в
+ сериализованном виде вместо её идентификатора.
+ Спасибо Ivan Ristić.
+
+ *) Исправление: nginx неправильно обрабатывал закодированный символ "?"
+ в команде SSI include.
+
+ *) Исправление: модуль ngx_http_dav_module не раскодировал целевой URI
+ при обработке методов COPY и MOVE.
+
+ *) Исправление: resolver не понимал доменные имена с точкой в конце.
+ Спасибо Yichun Zhang.
+
+ *) Исправление: при проксировании в логах могли появляться сообщения
+ "zero size buf in output"; ошибка появилась в 1.3.9.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовался модуль ngx_http_spdy_module.
+
+ *) Исправление: при использовании методов обработки соединений select,
+ poll и /dev/poll проксируемые WebSocket-соединения могли зависать
+ сразу после открытия.
+
+ *) Исправление: директива xclient почтового прокси-сервера некорректно
+ передавала IPv6-адреса.
+
+
+Изменения в nginx 1.5.8 17.12.2013
+
+ *) Добавление: теперь resolver поддерживает IPv6.
+
+ *) Добавление: директива listen поддерживает параметр fastopen.
+ Спасибо Mathew Rodley.
+
+ *) Добавление: поддержка SSL в модуле ngx_http_uwsgi_module.
+ Спасибо Roberto De Ioris.
+
+ *) Добавление: скрипты подсветки синтаксиса для vim добавлены в contrib.
+ Спасибо Evan Miller.
+
+ *) Исправление: при чтении тела запроса с использованием chunked
+ transfer encoding по SSL-соединению мог произойти таймаут.
+
+ *) Исправление: директива master_process работала неправильно в
+ nginx/Windows.
+
+ *) Исправление: параметр setfib директивы listen мог не работать.
+
+ *) Исправление: в модуле ngx_http_spdy_module.
+
+
+Изменения в nginx 1.5.7 19.11.2013
+
+ *) Безопасность: символ, следующий за незакодированным пробелом в строке
+ запроса, обрабатывался неправильно (CVE-2013-4547); ошибка появилась
+ в 0.8.41.
+ Спасибо Ivan Fratric из Google Security Team.
+
+ *) Изменение: уровень логгирования ошибок auth_basic об отсутствии
+ пароля понижен с уровня error до info.
+
+ *) Добавление: директивы proxy_cache_revalidate,
+ fastcgi_cache_revalidate, scgi_cache_revalidate и
+ uwsgi_cache_revalidate.
+
+ *) Добавление: директива ssl_session_ticket_key.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: директива "add_header Cache-Control ''" добавляла строку
+ заголовка ответа "Cache-Control" с пустым значением.
+
+ *) Исправление: директива "satisfy any" могла вернуть ошибку 403 вместо
+ 401 при использовании директив auth_request и auth_basic.
+ Спасибо Jan Marc Hoffmann.
+
+ *) Исправление: параметры accept_filter и deferred директивы listen
+ игнорировались для listen-сокетов, создаваемых в процессе обновления
+ исполняемого файла.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: часть данных, полученных от бэкенда при
+ небуферизированном проксировании, могла не отправляться клиенту
+ сразу, если использовались директивы gzip или gunzip.
+ Спасибо Yichun Zhang.
+
+ *) Исправление: в обработке ошибок в модуле
+ ngx_http_gunzip_filter_module.
+
+ *) Исправление: ответы могли зависать, если использовался модуль
+ ngx_http_spdy_module и директива auth_request.
+
+ *) Исправление: утечки памяти в nginx/Windows.
+
+
+Изменения в nginx 1.5.6 01.10.2013
+
+ *) Добавление: директива fastcgi_buffering.
+
+ *) Добавление: директивы proxy_ssl_protocols и proxy_ssl_ciphers.
+ Спасибо Piotr Sikora.
+
+ *) Добавление: оптимизация SSL handshake при использовании длинных
+ цепочек сертификатов.
+
+ *) Добавление: почтовый прокси-сервер поддерживает SMTP pipelining.
+
+ *) Исправление: в модуле ngx_http_auth_basic_module при использовании
+ метода шифрования паролей "$apr1$".
+ Спасибо Markus Linnala.
+
+ *) Исправление: на MacOSX, Cygwin и nginx/Windows для обработки запроса
+ мог использоваться неверный location, если для задания location'ов
+ использовались символы разных регистров.
+
+ *) Исправление: автоматическое перенаправление с добавлением
+ завершающего слэша для проксированных location'ов могло не работать.
+
+ *) Исправление: в почтовом прокси-сервере.
+
+ *) Исправление: в модуле ngx_http_spdy_module.
+
+
+Изменения в nginx 1.5.5 17.09.2013
+
+ *) Изменение: теперь nginx по умолчанию использует HTTP/1.0, если точно
+ определить протокол не удалось.
+
+ *) Добавление: директива disable_symlinks теперь использует O_PATH на
+ Linux.
+
+ *) Добавление: для определения того, что клиент закрыл соединение, при
+ использовании метода epoll теперь используются события EPOLLRDHUP.
+
+ *) Исправление: в директиве valid_referers при использовании параметра
+ server_names.
+
+ *) Исправление: переменная $request_time не работала в nginx/Windows.
+
+ *) Исправление: в директиве image_filter.
+ Спасибо Lanshun Zhou.
+
+ *) Исправление: совместимость с OpenSSL 1.0.1f.
+ Спасибо Piotr Sikora.
+
+
+Изменения в nginx 1.5.4 27.08.2013
+
+ *) Изменение: MIME-тип для расширения js изменён на
+ "application/javascript"; значение по умолчанию директивы
+ charset_types изменено соответственно.
+
+ *) Изменение: теперь директива image_filter с параметром size возвращает
+ ответ с MIME-типом "application/json".
+
+ *) Добавление: модуль ngx_http_auth_request_module.
+
+ *) Исправление: на старте или во время переконфигурации мог произойти
+ segmentation fault, если использовалась директива try_files с пустым
+ параметром.
+
+ *) Исправление: утечки памяти при использовании в директивах root и
+ auth_basic_user_file относительных путей, заданных с помощью
+ переменных.
+
+ *) Исправление: директива valid_referers неправильно выполняла
+ регулярные выражения, если заголовок Referer начинался с "https://".
+ Спасибо Liangbin Li.
+
+ *) Исправление: ответы могли зависать, если использовались подзапросы и
+ при обработке подзапроса происходила ошибка во время SSL handshake с
+ бэкендом.
+ Спасибо Aviram Cohen.
+
+ *) Исправление: в модуле ngx_http_autoindex_module.
+
+ *) Исправление: в модуле ngx_http_spdy_module.
+
+
+Изменения в nginx 1.5.3 30.07.2013
+
+ *) Изменение во внутреннем API: теперь при небуферизированной работе с
+ бэкендами u->length по умолчанию устанавливается в -1.
+
+ *) Изменение: теперь при получении неполного ответа от бэкенда nginx
+ отправляет полученную часть ответа, после чего закрывает соединение с
+ клиентом.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовался модуль ngx_http_spdy_module и директива
+ client_body_in_file_only.
+
+ *) Исправление: параметр so_keepalive директивы listen мог работать
+ некорректно на DragonFlyBSD.
+ Спасибо Sepherosa Ziehau.
+
+ *) Исправление: в модуле ngx_http_xslt_filter_module.
+
+ *) Исправление: в модуле ngx_http_sub_filter_module.
+
+
+Изменения в nginx 1.5.2 02.07.2013
+
+ *) Добавление: теперь можно использовать несколько директив error_log.
+
+ *) Исправление: метод $r->header_in() встроенного перла не возвращал
+ значения строк "Cookie" и "X-Forwarded-For" из заголовка запроса;
+ ошибка появилась в 1.3.14.
+
+ *) Исправление: в модуле ngx_http_spdy_module.
+ Спасибо Jim Radford.
+
+ *) Исправление: nginx не собирался на Linux при использовании x32 ABI.
+ Спасибо Сергею Иванцову.
+
+
+Изменения в nginx 1.5.1 04.06.2013
+
+ *) Добавление: директивы ssi_last_modified, sub_filter_last_modified и
+ xslt_last_modified.
+ Спасибо Алексею Колпакову.
+
+ *) Добавление: параметр http_403 в директивах proxy_next_upstream,
+ fastcgi_next_upstream, scgi_next_upstream и uwsgi_next_upstream.
+
+ *) Добавление: директивы allow и deny теперь поддерживают unix domain
+ сокеты.
+
+ *) Исправление: nginx не собирался с модулем ngx_mail_ssl_module, но без
+ модуля ngx_http_ssl_module; ошибка появилась в 1.3.14.
+
+ *) Исправление: в директиве proxy_set_body.
+ Спасибо Lanshun Zhou.
+
+ *) Исправление: в директиве lingering_time.
+ Спасибо Lanshun Zhou.
+
+ *) Исправление: параметр fail_timeout директивы server в блоке upstream
+ мог не работать, если использовался параметр max_fails; ошибка
+ появилась в 1.3.0.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовалась директива ssl_stapling.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: в почтовом прокси-сервере.
+ Спасибо Filipe Da Silva.
+
+ *) Исправление: nginx/Windows мог перестать принимать соединения, если
+ использовалось несколько рабочих процессов.
+
+
+Изменения в nginx 1.5.0 07.05.2013
+
+ *) Безопасность: при обработке специально созданного запроса мог
+ перезаписываться стек рабочего процесса, что могло приводить к
+ выполнению произвольного кода (CVE-2013-2028); ошибка появилась в
+ 1.3.9.
+ Спасибо Greg MacManus, iSIGHT Partners Labs.
+
+
+Изменения в nginx 1.4.0 24.04.2013
+
+ *) Исправление: nginx не собирался с модулем ngx_http_perl_module, если
+ использовался параметр --with-openssl; ошибка появилась в 1.3.16.
+
+ *) Исправление: в работе с телом запроса из модуля ngx_http_perl_module;
+ ошибка появилась в 1.3.9.
+
+
+Изменения в nginx 1.3.16 16.04.2013
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовались подзапросы; ошибка появилась в 1.3.9.
+
+ *) Исправление: директива tcp_nodelay вызывала ошибку при проксировании
+ WebSocket-соединений в unix domain сокет.
+
+ *) Исправление: переменная $upstream_response_length возвращала значение
+ "0", если не использовалась буферизация.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: в методах обработки соединений eventport и /dev/poll.
+
+
+Изменения в nginx 1.3.15 26.03.2013
+
+ *) Изменение: открытие и закрытие соединения без отправки в нём
+ каких-либо данных больше не записывается в access_log с кодом ошибки
+ 400.
+
+ *) Добавление: модуль ngx_http_spdy_module.
+ Спасибо Automattic за спонсирование разработки.
+
+ *) Добавление: директивы limit_req_status и limit_conn_status.
+ Спасибо Nick Marden.
+
+ *) Добавление: директива image_filter_interlace.
+ Спасибо Ивану Боброву.
+
+ *) Добавление: переменная $connections_waiting в модуле
+ ngx_http_stub_status_module.
+
+ *) Добавление: теперь почтовый прокси-сервер поддерживает IPv6-бэкенды.
+
+ *) Исправление: при повторной отправке запроса на бэкенд тело запроса
+ могло передаваться неправильно; ошибка появилась в 1.3.9.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: в директиве client_body_in_file_only; ошибка появилась в
+ 1.3.9.
+
+ *) Исправление: ответы могли зависать, если использовались подзапросы и
+ при обработке подзапроса происходила DNS-ошибка.
+ Спасибо Lanshun Zhou.
+
+ *) Исправление: в процедуре учёта использования бэкендов.
+
+
+Изменения в nginx 1.3.14 05.03.2013
+
+ *) Добавление: переменные $connections_active, $connections_reading и
+ $connections_writing в модуле ngx_http_stub_status_module.
+
+ *) Добавление: поддержка WebSocket-соединений в модулях
+ ngx_http_uwsgi_module и ngx_http_scgi_module.
+
+ *) Исправление: в обработке виртуальных серверов при использовании SNI.
+
+ *) Исправление: при использовании директивы "ssl_session_cache shared"
+ новые сессии могли не сохраняться, если заканчивалось место в
+ разделяемой памяти.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: несколько заголовков X-Forwarded-For обрабатывались
+ неправильно.
+ Спасибо Neal Poole за спонсирование разработки.
+
+ *) Исправление: в модуле ngx_http_mp4_module.
+ Спасибо Gernot Vormayr.
+
+
+Изменения в nginx 1.3.13 19.02.2013
+
+ *) Изменение: теперь для сборки по умолчанию используется компилятор с
+ именем "cc".
+
+ *) Добавление: поддержка проксирования WebSocket-соединений.
+ Спасибо Apcera и CloudBees за спонсирование разработки.
+
+ *) Добавление: директива auth_basic_user_file поддерживает шифрование
+ паролей методом "{SHA}".
+ Спасибо Louis Opter.
+
+
+Изменения в nginx 1.3.12 05.02.2013
+
+ *) Добавление: директивы proxy_bind, fastcgi_bind, memcached_bind,
+ scgi_bind и uwsgi_bind поддерживают переменные.
+
+ *) Добавление: переменные $pipe, $request_length, $time_iso8601 и
+ $time_local теперь можно использовать не только в директиве
+ log_format.
+ Спасибо Kiril Kalchev.
+
+ *) Добавление: поддержка IPv6 в модуле ngx_http_geoip_module.
+ Спасибо Gregor Kališnik.
+
+ *) Исправление: директива proxy_method работала неверно, если была
+ указана на уровне http.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовался resolver и метод poll.
+
+ *) Исправление: nginx мог нагружать процессор во время SSL handshake с
+ бэкендом при использовании методов обработки соединений select, poll
+ и /dev/poll.
+
+ *) Исправление: ошибка "[crit] SSL_write() failed (SSL:)".
+
+ *) Исправление: в директиве client_body_in_file_only; ошибка появилась в
+ 1.3.9.
+
+ *) Исправление: в директиве fastcgi_keep_conn.
+
+
+Изменения в nginx 1.3.11 10.01.2013
+
+ *) Исправление: при записи в лог мог происходить segmentation fault;
+ ошибка появилась в 1.3.10.
+
+ *) Исправление: директива proxy_pass не работала с IP-адресами без
+ явного указания порта; ошибка появилась в 1.3.10.
+
+ *) Исправление: на старте или во время переконфигурации происходил
+ segmentation fault, если директива keepalive была указана несколько
+ раз в одном блоке upstream.
+
+ *) Исправление: параметр default директивы geo не определял значение по
+ умолчанию для IPv6-адресов.
+
+
+Изменения в nginx 1.3.10 25.12.2012
+
+ *) Изменение: для указанных в конфигурационном файле доменных имён
+ теперь используются не только IPv4, но и IPv6 адреса.
+
+ *) Изменение: теперь при использовании директивы include с маской на
+ Unix-системах включаемые файлы сортируются в алфавитном порядке.
+
+ *) Изменение: директива add_header добавляет строки в ответы с кодом
+ 201.
+
+ *) Добавление: директива geo теперь поддерживает IPv6 адреса в формате
+ CIDR.
+
+ *) Добавление: параметры flush и gzip в директиве access_log.
+
+ *) Добавление: директива auth_basic поддерживает переменные.
+
+ *) Исправление: nginx в некоторых случаях не собирался с модулем
+ ngx_http_perl_module.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовался модуль ngx_http_xslt_module.
+
+ *) Исправление: nginx мог не собираться на MacOSX.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: при использовании директивы limit_rate с большими
+ значениями скорости на 32-битных системах ответ мог возвращаться не
+ целиком.
+ Спасибо Алексею Антропову.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовалась директива if.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: ответ "100 Continue" выдавался вместе с ответом "413
+ Request Entity Too Large".
+
+ *) Исправление: директивы image_filter, image_filter_jpeg_quality и
+ image_filter_sharpen могли наследоваться некорректно.
+ Спасибо Ивану Боброву.
+
+ *) Исправление: при использовании директивы auth_basic под Linux могли
+ возникать ошибки "crypt_r() failed".
+
+ *) Исправление: в обработке backup-серверов.
+ Спасибо Thomas Chen.
+
+ *) Исправление: при проксировании HEAD-запросов мог возвращаться
+ некорректный ответ, если использовалась директива gzip.
+
+
+Изменения в nginx 1.3.9 27.11.2012
+
+ *) Добавление: поддержка chunked transfer encoding при получении тела
+ запроса.
+
+ *) Добавление: переменные $request_time и $msec теперь можно
+ использовать не только в директиве log_format.
+
+ *) Исправление: cache manager и cache loader могли не запускаться, если
+ использовалось более 512 listen-сокетов.
+
+ *) Исправление: в модуле ngx_http_dav_module.
+
+
+Изменения в nginx 1.3.8 30.10.2012
+
+ *) Добавление: параметр optional_no_ca директивы ssl_verify_client.
+ Спасибо Михаилу Казанцеву и Eric O'Connor.
+
+ *) Добавление: переменные $bytes_sent, $connection и
+ $connection_requests теперь можно использовать не только в директиве
+ log_format.
+ Спасибо Benjamin Grössing.
+
+ *) Добавление: параметр auto директивы worker_processes.
+
+ *) Исправление: сообщения "cache file ... has md5 collision".
+
+ *) Исправление: в модуле ngx_http_gunzip_filter_module.
+
+ *) Исправление: в директиве ssl_stapling.
+
+
+Изменения в nginx 1.3.7 02.10.2012
+
+ *) Добавление: поддержка OCSP stapling.
+ Спасибо Comodo, DigiCert и GlobalSign за спонсирование разработки.
+
+ *) Добавление: директива ssl_trusted_certificate.
+
+ *) Добавление: теперь resolver случайным образом меняет порядок
+ возвращаемых закэшированных адресов.
+ Спасибо Антону Жулину.
+
+ *) Исправление: совместимость с OpenSSL 0.9.7.
+
+
+Изменения в nginx 1.3.6 12.09.2012
+
+ *) Добавление: модуль ngx_http_gunzip_filter_module.
+
+ *) Добавление: директива memcached_gzip_flag.
+
+ *) Добавление: параметр always директивы gzip_static.
+
+ *) Исправление: в директиве "limit_req"; ошибка появилась в 1.1.14.
+ Спасибо Charles Chen.
+
+ *) Исправление: nginx не собирался gcc 4.7 с оптимизацией -O2 если
+ использовался параметр --with-ipv6.
+
+
+Изменения в nginx 1.3.5 21.08.2012
+
+ *) Изменение: модуль ngx_http_mp4_module больше не отфильтровывает
+ дорожки в форматах, отличных от H.264 и AAC.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если в директиве map в качестве значений использовались переменные.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault при
+ использовании директивы geo с параметром ranges, но без параметра
+ default; ошибка появилась в 0.8.43.
+ Спасибо Zhen Chen и Weibin Yao.
+
+ *) Исправление: в обработке параметра командной строки -p.
+
+ *) Исправление: в почтовом прокси-сервере.
+
+ *) Исправление: незначительных потенциальных ошибок.
+ Спасибо Coverity.
+
+ *) Исправление: nginx/Windows не собирался с Visual Studio 2005 Express.
+ Спасибо HAYASHI Kentaro.
+
+
+Изменения в nginx 1.3.4 31.07.2012
+
+ *) Изменение: теперь на слушающих IPv6-сокетах параметр ipv6only включён
+ по умолчанию.
+
+ *) Добавление: поддержка компилятора Clang.
+
+ *) Исправление: могли создаваться лишние слушающие сокеты.
+ Спасибо Роману Одайскому.
+
+ *) Исправление: nginx/Windows мог нагружать процессор, если при запуске
+ рабочего процесса происходила ошибка.
+ Спасибо Ricardo Villalobos Guevara.
+
+ *) Исправление: директивы proxy_pass_header, fastcgi_pass_header,
+ scgi_pass_header, uwsgi_pass_header, proxy_hide_header,
+ fastcgi_hide_header, scgi_hide_header и uwsgi_hide_header могли
+ наследоваться некорректно.
+
+
+Изменения в nginx 1.3.3 10.07.2012
+
+ *) Добавление: поддержка entity tags и директива etag.
+
+ *) Исправление: при использовании директивы map с параметром hostnames
+ не игнорировалась конечная точка в исходном значении.
+
+ *) Исправление: для обработки запроса мог использоваться неверный
+ location, если переход в именованный location происходил после
+ изменения URI с помощью директивы rewrite.
+
+
+Изменения в nginx 1.3.2 26.06.2012
+
+ *) Изменение: параметр single директивы keepalive теперь игнорируется.
+
+ *) Изменение: сжатие SSL теперь отключено в том числе при использовании
+ OpenSSL старее 1.0.0.
+
+ *) Добавление: директиву "ip_hash" теперь можно использовать для
+ балансировки IPv6 клиентов.
+
+ *) Добавление: переменную $status теперь можно использовать не только в
+ директиве log_format.
+
+ *) Исправление: при завершении рабочего процесса мог произойти
+ segmentation fault, если использовалась директива resolver.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовался модуль ngx_http_mp4_module.
+
+ *) Исправление: в модуле ngx_http_mp4_module.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовались конфликтующие имена серверов с масками.
+
+ *) Исправление: на платформе ARM nginx мог аварийно завершаться по
+ сигналу SIGBUS.
+
+ *) Исправление: во время переконфигурации на HP-UX в лог записывался
+ alert "sendmsg() failed (9: Bad file number)".
+
+
+Изменения в nginx 1.3.1 05.06.2012
+
+ *) Безопасность: теперь nginx/Windows игнорирует точку в конце
+ компонента URI и не разрешает URI, содержащие последовательность
+ ":$".
+ Спасибо Владимиру Кочеткову, Positive Research Center.
+
+ *) Добавление: директивы proxy_pass, fastcgi_pass, scgi_pass, uwsgi_pass
+ и директива server в блоке upstream теперь поддерживают IPv6-адреса.
+
+ *) Добавление: в директиве resolver теперь можно указывать порт и
+ задавать IPv6-адреса DNS-серверов.
+
+ *) Добавление: директива least_conn в блоке upstream.
+
+ *) Добавление: при использовании директивы ip_hash теперь можно задавать
+ веса серверов.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовалась директива image_filter; ошибка появилась в 1.3.0.
+
+ *) Исправление: nginx не собирался с модулем ngx_cpp_test_module; ошибка
+ появилась в 1.1.12.
+
+ *) Исправление: доступ к переменным из SSI и встроенного перла мог не
+ работать после переконфигурации.
+ Спасибо Yichun Zhang.
+
+ *) Исправление: в модуле ngx_http_xslt_filter_module.
+ Спасибо Kuramoto Eiji.
+
+ *) Исправление: утечки памяти при использовании переменной $geoip_org.
+ Спасибо Денису Латыпову.
+
+ *) Исправление: в директивах proxy_cookie_domain и proxy_cookie_path.
+
+
+Изменения в nginx 1.3.0 15.05.2012
+
+ *) Добавление: директива debug_connection теперь поддерживает
+ IPv6-адреса и параметр "unix:".
+
+ *) Добавление: директива set_real_ip_from и параметр proxy директивы geo
+ теперь поддерживают IPv6-адреса.
+
+ *) Добавление: директивы real_ip_recursive, geoip_proxy и
+ geoip_proxy_recursive.
+
+ *) Добавление: параметр proxy_recursive директивы geo.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовалась директива resolver.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовались директивы fastcgi_pass, scgi_pass или uwsgi_pass
+ и бэкенд возвращал некорректный ответ.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовалась директива rewrite и в новых аргументах запроса в
+ строке замены использовались переменные.
+
+ *) Исправление: nginx мог нагружать процессор, если было достигнуто
+ ограничение на количество открытых файлов.
+
+ *) Исправление: при использовании директивы proxy_next_upstream с
+ параметром http_404 nginx мог бесконечно перебирать бэкенды, если в
+ блоке upstream был хотя бы один сервер с флагом backup.
+
+ *) Исправление: при использовании директивы ip_hash установка параметра
+ down директивы server могла приводить к ненужному перераспределению
+ клиентов между бэкендами.
+
+ *) Исправление: утечки сокетов.
+ Спасибо Yichun Zhang.
+
+ *) Исправление: в модуле ngx_http_fastcgi_module.
+
+
+Изменения в nginx 1.2.0 23.04.2012
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовалась директива try_files; ошибка появилась в 1.1.19.
+
+ *) Исправление: ответ мог быть передан не полностью, если использовалось
+ больше IOV_MAX буферов.
+
+ *) Исправление: в работе параметра crop директивы image_filter.
+ Спасибо Maxim Bublis.
+
+
+Изменения в nginx 1.1.19 12.04.2012
+
+ *) Безопасность: при обработке специально созданного mp4 файла модулем
+ ngx_http_mp4_module могли перезаписываться области памяти рабочего
+ процесса, что могло приводить к выполнению произвольного кода
+ (CVE-2012-2089).
+ Спасибо Matthew Daley.
+
+ *) Исправление: nginx/Windows мог завершаться аварийно.
+ Спасибо Vincent Lee.
+
+ *) Исправление: nginx нагружал процессор, если все серверы в upstream'е
+ были помечены флагом backup.
+
+ *) Исправление: директивы allow и deny могли наследоваться некорректно,
+ если в них использовались IPv6 адреса.
+
+ *) Исправление: директивы modern_browser и ancient_browser могли
+ наследоваться некорректно.
+
+ *) Исправление: таймауты могли работать некорректно на Solaris/SPARC.
+
+ *) Исправление: в модуле ngx_http_mp4_module.
+
+
+Изменения в nginx 1.1.18 28.03.2012
+
+ *) Изменение: теперь keepalive соединения не запрещены для Safari по
+ умолчанию.
+
+ *) Добавление: переменная $connection_requests.
+
+ *) Добавление: переменные $tcpinfo_rtt, $tcpinfo_rttvar,
+ $tcpinfo_snd_cwnd и $tcpinfo_rcv_space.
+
+ *) Добавление: директива worker_cpu_affinity теперь работает на FreeBSD.
+
+ *) Добавление: директивы xslt_param и xslt_string_param.
+ Спасибо Samuel Behan.
+
+ *) Исправление: в configure.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: в модуле ngx_http_xslt_filter_module.
+
+ *) Исправление: nginx не собирался на Debian GNU/Hurd.
+
+
+Изменения в nginx 1.1.17 15.03.2012
+
+ *) Безопасность: содержимое ранее освобождённой памяти могло быть
+ отправлено клиенту, если бэкенд возвращал специально созданный ответ.
+ Спасибо Matthew Daley.
+
+ *) Исправление: при использовании встроенного перла из SSI.
+ Спасибо Matthew Daley.
+
+ *) Исправление: в модуле ngx_http_uwsgi_module.
+
+
+Изменения в nginx 1.1.16 29.02.2012
+
+ *) Изменение: ограничение на количество одновременных подзапросов
+ поднято до 200.
+
+ *) Добавление: параметр from в директиве disable_symlinks.
+
+ *) Добавление: директивы return и error_page теперь могут использоваться
+ для возврата перенаправлений с кодом 307.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовалась директива resolver и на глобальном уровне не была
+ задана директива error_log.
+ Спасибо Роману Арутюняну.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовались директивы "proxy_http_version 1.1" или
+ "fastcgi_keep_conn on".
+
+ *) Исправление: утечек памяти.
+ Спасибо Lanshun Zhou.
+
+ *) Исправление: в директиве disable_symlinks.
+
+ *) Исправление: при использовании ZFS размер кэша на диске мог считаться
+ некорректно; ошибка появилась в 1.0.1.
+
+ *) Исправление: nginx не собирался компилятором icc 12.1.
+
+ *) Исправление: nginx не собирался gcc на Solaris; ошибка появилась в
+ 1.1.15.
+
+
+Изменения в nginx 1.1.15 15.02.2012
+
+ *) Добавление: директива disable_symlinks.
+
+ *) Добавление: директивы proxy_cookie_domain и proxy_cookie_path.
+
+ *) Исправление: nginx мог некорректно сообщать об ошибке "upstream
+ prematurely closed connection" вместо "upstream sent too big header".
+ Спасибо Feibo Li.
+
+ *) Исправление: nginx не собирался с модулем ngx_http_perl_module, если
+ использовался параметр --with-openssl.
+
+ *) Исправление: количество внутренних перенаправлений в именованные
+ location'ы не ограничивалось.
+
+ *) Исправление: вызов $r->flush() несколько раз подряд мог приводить к
+ ошибкам в модуле ngx_http_gzip_filter_module.
+
+ *) Исправление: при использовании директивы proxy_store с
+ SSI-подзапросами временные файлы могли не удаляться.
+
+ *) Исправление: в некоторых случаях некэшируемые переменные (такие, как
+ $args) возвращали старое пустое закэшированное значение.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если одновременно создавалось слишком много SSI-подзапросов; ошибка
+ появилась в 0.7.25.
+
+
+Изменения в nginx 1.1.14 30.01.2012
+
+ *) Добавление: теперь можно указать несколько ограничений limit_req
+ одновременно.
+
+ *) Исправление: в обработке ошибок при соединении с бэкендом.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: в обработке ошибок при использовании AIO на FreeBSD.
+
+ *) Исправление: в инициализации библиотеки OpenSSL.
+
+ *) Исправление: директивы proxy_redirect могли наследоваться
+ некорректно.
+
+ *) Исправление: утечки памяти при переконфигурации, если использовалась
+ директива pcre_jit.
+
+
+Изменения в nginx 1.1.13 16.01.2012
+
+ *) Добавление: параметры TLSv1.1 и TLSv1.2 в директиве ssl_protocols.
+
+ *) Исправление: параметры директивы limit_req наследовались некорректно;
+ ошибка появилась в 1.1.12.
+
+ *) Исправление: директива proxy_redirect некорректно обрабатывала
+ заголовок Refresh при использовании регулярных выражений.
+
+ *) Исправление: директива proxy_cache_use_stale с параметром error не
+ возвращала ответ из кэша, если все бэкенды были признаны
+ неработающими.
+
+ *) Исправление: директива worker_cpu_affinity могла не работать.
+
+ *) Исправление: nginx не собирался на Solaris; ошибка появилась в
+ 1.1.12.
+
+ *) Исправление: в модуле ngx_http_mp4_module.
+
+
+Изменения в nginx 1.1.12 26.12.2011
+
+ *) Изменение: после перенаправления запроса с помощью директивы
+ error_page директива proxy_pass без URI теперь использует изменённый
+ URI.
+ Спасибо Lanshun Zhou.
+
+ *) Добавление: директивы proxy/fastcgi/scgi/uwsgi_cache_lock,
+ proxy/fastcgi/scgi/uwsgi_cache_lock_timeout.
+
+ *) Добавление: директива pcre_jit.
+
+ *) Добавление: SSI команда if поддерживает выделения в регулярных
+ выражениях.
+
+ *) Исправление: SSI команда if не работала внутри команды block.
+
+ *) Исправление: директивы limit_conn_log_level и limit_req_log_level
+ могли не работать.
+
+ *) Исправление: директива limit_rate не позволяла передавать на полной
+ скорости, даже если был указан очень большой лимит.
+
+ *) Исправление: директива sendfile_max_chunk не работала, если
+ использовалась директива limit_rate.
+
+ *) Исправление: если в директиве proxy_pass использовались переменные и
+ не был указан URI, всегда использовался URI исходного запроса.
+
+ *) Исправление: после перенаправления запроса с помощью директивы
+ try_files директива proxy_pass без URI могла использовать URI
+ исходного запроса.
+ Спасибо Lanshun Zhou.
+
+ *) Исправление: в модуле ngx_http_scgi_module.
+
+ *) Исправление: в модуле ngx_http_mp4_module.
+
+ *) Исправление: nginx не собирался на Solaris; ошибка появилась в 1.1.9.
+
+
+Изменения в nginx 1.1.11 12.12.2011
+
+ *) Добавление: параметр so_keepalive в директиве listen.
+ Спасибо Всеволоду Стахову.
+
+ *) Добавление: параметр if_not_empty в директивах
+ fastcgi/scgi/uwsgi_param.
+
+ *) Добавление: переменная $https.
+
+ *) Добавление: директива proxy_redirect поддерживает переменные в первом
+ параметре.
+
+ *) Добавление: директива proxy_redirect поддерживает регулярные
+ выражения.
+
+ *) Исправление: переменная $sent_http_cache_control могла содержать
+ неверное значение при использовании директивы expires.
+ Спасибо Yichun Zhang.
+
+ *) Исправление: директива read_ahead могла не работать при использовании
+ совместно с try_files и open_file_cache.
+
+ *) Исправление: если в параметре inactive директивы proxy_cache_path
+ было указано малое время, в рабочем процессе мог произойти
+ segmentation fault.
+
+ *) Исправление: ответы из кэша могли зависать.
+
+
+Изменения в nginx 1.1.10 30.11.2011
+
+ *) Исправление: при использовании AIO на Linux в рабочем процессе
+ происходил segmentation fault; ошибка появилась в 1.1.9.
+
+
+Изменения в nginx 1.1.9 28.11.2011
+
+ *) Изменение: теперь двойные кавычки экранируется при выводе
+ SSI-командой echo.
+ Спасибо Зауру Абасмирзоеву.
+
+ *) Добавление: параметр valid в директиве resolver. По умолчанию теперь
+ используется TTL, возвращённый DNS-сервером.
+ Спасибо Кириллу Коринскому.
+
+ *) Исправление: nginx мог перестать отвечать, если рабочий процесс
+ завершался аварийно.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовалось SNI; ошибка появилась в 1.1.2.
+
+ *) Исправление: в директиве keepalive_disable; ошибка появилась в 1.1.8.
+ Спасибо Александру Усову.
+
+ *) Исправление: сигнал SIGWINCH переставал работать после первого
+ обновления исполняемого файла; ошибка появилась в 1.1.1.
+
+ *) Исправление: теперь ответы бэкендов, длина которых не соответствует
+ заголовку Content-Length, не кэширутся.
+
+ *) Исправление: в директиве scgi_param при использовании составных
+ параметров.
+
+ *) Исправление: в методе epoll.
+ Спасибо Yichun Zhang.
+
+ *) Исправление: в модуле ngx_http_flv_module.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: в модуле ngx_http_mp4_module.
+
+ *) Исправление: теперь nginx понимает IPv6-адреса в строке запроса и в
+ заголовке Host.
+
+ *) Исправление: директивы add_header и expires не работали для ответов с
+ кодом 206, если запрос проксировался.
+
+ *) Исправление: nginx не собирался на FreeBSD 10.
+
+ *) Исправление: nginx не собирался на AIX.
+
+
+Изменения в nginx 1.1.8 14.11.2011
+
+ *) Изменение: модуль ngx_http_limit_zone_module переименован в
+ ngx_http_limit_conn_module.
+
+ *) Изменение: директива limit_zone заменена директивой limit_conn_zone с
+ новым синтаксисом.
+
+ *) Добавление: поддержка ограничения по нескольким limit_conn на одном
+ уровне.
+
+ *) Добавление: директива image_filter_sharpen.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если resolver получил большой DNS-ответ.
+ Спасибо Ben Hawkes.
+
+ *) Исправление: в вычислении ключа для кэширования, если использовалась
+ внутренняя реализация MD5; ошибка появилась в 1.0.4.
+
+ *) Исправление: строки "If-Modified-Since", "If-Range" и им подобные в
+ заголовке запроса клиента могли передаваться бэкенду при кэшировании;
+ или не передаваться при выключенном кэшировании, если кэширование
+ было включено в другой части конфигурации.
+
+ *) Исправление: модуль ngx_http_mp4_module выдавал неверную строку
+ "Content-Length" в заголовке ответа, использовался аргумент start.
+ Спасибо Piotr Sikora.
+
+
+Изменения в nginx 1.1.7 31.10.2011
+
+ *) Добавление: поддержка нескольких DNS серверов в директиве "resolver".
+ Спасибо Кириллу Коринскому.
+
+ *) Исправление: на старте или во время переконфигурации происходил
+ segmentation fault, если директива ssl использовалась на уровне http
+ и не был указан ssl_certificate.
+
+ *) Исправление: уменьшено потребление памяти при проксировании больших
+ файлов, если они буферизировались на диск.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовалась директива "proxy_http_version 1.1".
+
+ *) Исправление: в директиве "expires @time".
+
+
+Изменения в nginx 1.1.6 17.10.2011
+
+ *) Изменение во внутреннем API: теперь при внутреннем редиректе в
+ именованный location контексты модулей очищаются.
+ По запросу Yichun Zhang.
+
+ *) Изменение: теперь если сервер, описанный в блоке upstream, был
+ признан неработающим, то после истечения fail_timeout на него будет
+ отправлен только один запрос; сервер будет считаться работающим, если
+ успешно ответит на этот запрос.
+
+ *) Изменение: теперь символы 0x7F-0xFF в access_log записываются в виде
+ \xXX.
+
+ *) Добавление: директивы "proxy/fastcgi/scgi/uwsgi_ignore_headers"
+ теперь поддерживают значения X-Accel-Limit-Rate, X-Accel-Buffering и
+ X-Accel-Charset.
+
+ *) Добавление: уменьшение потребления памяти при использовании SSL.
+
+ *) Исправление: некоторые UTF-8 символы обрабатывались неправильно.
+ Спасибо Алексею Куцу.
+
+ *) Исправление: директивы модуля ngx_http_rewrite_module, заданные на
+ уровне server, применялись повторно, если для запроса не находилось
+ ни одного location'а.
+
+ *) Исправление: при использовании "aio sendfile" могла происходить
+ утечка сокетов.
+
+ *) Исправление: при использовании файлового AIO соединения с быстрыми
+ клиентами могли быть закрыты по истечению send_timeout.
+
+ *) Исправление: в модуле ngx_http_autoindex_module.
+
+ *) Исправление: модуль ngx_http_mp4_module не поддерживал перемотку на
+ 32-битных платформах.
+
+
+Изменения в nginx 1.1.5 05.10.2011
+
+ *) Добавление: директивы uwsgi_buffering и scgi_buffering.
+ Спасибо Peter Smit.
+
+ *) Исправление: при использовании proxy_cache_bypass могли быть
+ закэшированы некэшируемые ответы.
+ Спасибо John Ferlito.
+
+ *) Исправление: в модуле ngx_http_proxy_module при работе с бэкендами по
+ HTTP/1.1.
+
+ *) Исправление: закэшированные ответы с пустым телом возвращались
+ некорректно; ошибка появилась в 0.8.31.
+
+ *) Исправление: ответы с кодом 201 модуля ngx_http_dav_module были
+ некорректны; ошибка появилась в 0.8.32.
+
+ *) Исправление: в директиве return.
+
+ *) Исправление: при использовании директивы "ssl_session_cache builtin"
+ происходил segmentation fault; ошибка появилась в 1.1.1.
+
+
+Изменения в nginx 1.1.4 20.09.2011
+
+ *) Добавление: модуль ngx_http_upstream_keepalive.
+
+ *) Добавление: директива proxy_http_version.
+
+ *) Добавление: директива fastcgi_keep_conn.
+
+ *) Добавление: директива worker_aio_requests.
+
+ *) Исправление: если nginx был собран с файловым AIO, он не мог
+ запускаться на Linux без поддержки AIO.
+
+ *) Исправление: в обработке ошибок при работе с Linux AIO.
+ Спасибо Hagai Avrahami.
+
+ *) Исправление: уменьшено потребление памяти для долгоживущих запросов.
+
+ *) Исправление: модуль ngx_http_mp4_module не поддерживал 64-битный
+ MP4-атом co64.
+
+
+Изменения в nginx 1.1.3 14.09.2011
+
+ *) Добавление: модуль ngx_http_mp4_module.
+
+ *) Исправление: в Linux AIO, используемым совместно с open_file_cache.
+
+ *) Исправление: open_file_cache не обновлял информацию о файле, если
+ файл был изменён не атомарно.
+
+ *) Исправление: nginx не собирался на MacOSX 10.7.
+
+
+Изменения в nginx 1.1.2 05.09.2011
+
+ *) Изменение: теперь, если суммарный размер всех диапазонов больше
+ размера исходного ответа, то nginx возвращает только исходный ответ,
+ не обрабатывая диапазоны.
+
+ *) Добавление: директива max_ranges.
+
+ *) Исправление: директивы ssl_verify_client, ssl_verify_depth и
+ ssl_prefer_server_cipher могли работать некорректно, если
+ использовался SNI.
+
+ *) Исправление: в директивах proxy/fastcgi/scgi/
+ uwsgi_ignore_client_abort.
+
+
+Изменения в nginx 1.1.1 22.08.2011
+
+ *) Изменение: теперь загрузчик кэша за каждую итерацию либо обрабатывает
+ число файлов, указанное в параметре load_files, либо работает не
+ дольше времени, указанного в параметре loader_threshold.
+
+ *) Изменение: SIGWINCH сигнал теперь работает только в режиме демона.
+
+ *) Добавление: теперь разделяемые зоны и кэши используют семафоры POSIX
+ на Solaris.
+ Спасибо Денису Иванову.
+
+ *) Добавление: теперь на NetBSD поддерживаются accept фильтры.
+
+ *) Исправление: nginx не собирался на Linux 3.0.
+
+ *) Исправление: в некоторых случаях nginx не использовал сжатие; ошибка
+ появилась в 1.1.0.
+
+ *) Исправление: обработка тела запроса могла быть неверной, если клиент
+ использовал pipelining.
+
+ *) Исправление: в директиве request_body_in_single_buf.
+
+ *) Исправление: в директивах proxy_set_body и proxy_pass_request_body
+ при использовании SSL-соединения с бэкендом.
+
+ *) Исправление: nginx нагружал процессор, если все серверы в upstream'е
+ были помечены флагом down.
+
+ *) Исправление: при переконфигурации мог произойти segmentation fault,
+ если в предыдущей конфигурации был определён, но не использовался
+ ssl_session_cache.
+
+ *) Исправление: при использовании большого количества backup-серверов в
+ рабочем процессе мог произойти segmentation fault.
+
+ *) Исправление: при использовании директив fastcgi/scgi/uwsgi_param со
+ значениями, начинающимися со строки "HTTP_", в рабочем процессе мог
+ произойти segmentation fault; ошибка появилась в 0.8.40.
+
+
+Изменения в nginx 1.1.0 01.08.2011
+
+ *) Добавление: уменьшение времени работы загрузчика кэша.
+
+ *) Добавление: параметры loader_files, loader_sleep и loader_threshold
+ директив proxy/fastcgi/scgi/uwsgi_cache_path.
+
+ *) Добавление: уменьшение времени загрузки конфигураций с большим
+ количеством HTTPS серверов.
+
+ *) Добавление: теперь nginx поддерживает шифры с обменом ECDHE-ключами.
+ Спасибо Adrian Kotelba.
+
+ *) Добавление: директива lingering_close.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: закрытия соединения для pipelined-запросов.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: nginx не запрещал сжатие при получении значения
+ "gzip;q=0" в строке "Accept-Encoding" в заголовке запроса клиента.
+
+ *) Исправление: таймаута при небуферизированном проксировании.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: утечки памяти при использовании переменных в директиве
+ proxy_pass при работе с бэкендом по HTTPS.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: в проверке параметра директивы proxy_pass, заданного
+ переменными.
+ Спасибо Lanshun Zhou.
+
+ *) Исправление: SSL не работал на QNX.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: SSL модули не собирались gcc 4.6 без параметра
+ --with-debug.
+
+
+Изменения в nginx 1.0.5 19.07.2011
+
+ *) Изменение: теперь по умолчанию используются следующие шифры SSL:
+ "HIGH:!aNULL:!MD5".
+ Спасибо Rob Stradling.
+
+ *) Добавление: директивы referer_hash_max_size и
+ referer_hash_bucket_size.
+ Спасибо Witold Filipczyk.
+
+ *) Добавление: переменная $uid_reset.
+
+ *) Исправление: при использовании кэширования в рабочем процессе мог
+ произойти segmentation fault.
+ Спасибо Lanshun Zhou.
+
+ *) Исправление: при использовании кэширования рабочие процессы могли
+ зациклиться во время переконфигурации; ошибка появилась в 0.8.48.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: сообщения "stalled cache updating".
+ Спасибо Максиму Дунину.
+
+
+Изменения в nginx 1.0.4 01.06.2011
+
+ *) Изменение: теперь в регулярных выражениях в директиве map можно
+ задать чувствительность к регистру с помощью префиксов "~" и "~*".
+
+ *) Добавление: теперь разделяемые зоны и кэши используют семафоры POSIX
+ на Linux.
+ Спасибо Денису Латыпову.
+
+ *) Исправление: сообщения "stalled cache updating".
+
+ *) Исправление: nginx не собирался с параметром
+ --without-http_auth_basic_module; ошибка появилась в 1.0.3.
+
+
+Изменения в nginx 1.0.3 25.05.2011
+
+ *) Добавление: директива auth_basic_user_file поддерживает шифрование
+ пароля методами "$apr1", "{PLAIN}" и "{SSHA}".
+ Спасибо Максиму Дунину.
+
+ *) Добавление: директива geoip_org и переменная $geoip_org.
+ Спасибо Александру Ускову, Arnaud Granal и Денису Латыпову.
+
+ *) Добавление: модули ngx_http_geo_module и ngx_http_geoip_module
+ поддерживают адреса IPv4, отображённые на IPv6 адреса.
+
+ *) Исправление: при проверке адреса IPv4, отображённого на адрес IPv6, в
+ рабочем процессе происходил segmentation fault, если директивы access
+ или deny были определены только для адресов IPv6; ошибка появилась в
+ 0.8.22.
+
+ *) Исправление: закэшированный ответ мог быть испорчен, если значения
+ директив proxy/fastcgi/scgi/uwsgi_cache_bypass и proxy/fastcgi/scgi/
+ uwsgi_no_cache были разными; ошибка появилась в 0.8.46.
+
+
+Изменения в nginx 1.0.2 10.05.2011
+
+ *) Добавление: теперь разделяемые зоны и кэши используют семафоры POSIX.
+
+ *) Исправление: в работе параметра rotate директивы image_filter.
+ Спасибо Adam Bocim.
+
+ *) Исправление: nginx не собирался на Solaris; ошибка появилась в 1.0.1.
+
+
+Изменения в nginx 1.0.1 03.05.2011
+
+ *) Изменение: теперь директива split_clients использует алгоритм
+ MurmurHash2 из-за лучшего распределения.
+ Спасибо Олегу Мамонтову.
+
+ *) Изменение: теперь длинные строки, начинающиеся с нуля, не считаются
+ ложными значениями.
+ Спасибо Максиму Дунину.
+
+ *) Изменение: теперь по умолчанию nginx использует значение 511 для
+ listen backlog на Linux.
+
+ *) Добавление: переменные $upstream_... можно использовать в SSI и
+ перловом модулях.
+
+ *) Исправление: теперь nginx лучше ограничивает размер кэша на диске.
+ Спасибо Олегу Мамонтову.
+
+ *) Исправление: при парсинге неправильного IPv4 адреса мог произойти
+ segmentation fault; ошибка появилась в 0.8.22.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: nginx не собирался gcc 4.6 без параметра --with-debug.
+
+ *) Исправление: nginx не собирался на Solaris 9 и более ранних; ошибка
+ появилась в 0.9.3.
+ Спасибо Dagobert Michelsen.
+
+ *) Исправление: переменная $request_time имела неверные значения, если
+ использовались подзапросы; ошибка появилась в 0.8.47.
+ Спасибо Игорю А. Валькову.
+
+
+Изменения в nginx 1.0.0 12.04.2011
+
+ *) Исправление: cache manager мог нагружать процессор после
+ переконфигурации.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: директива "image_filter crop" неправильно работала в
+ сочетании с "image_filter rotate 180".
+
+ *) Исправление: директива "satisfy any" запрещала выдачу
+ пользовательской страницы для 401 кода.
+
+
+Изменения в nginx 0.9.7 04.04.2011
+
+ *) Добавление: теперь соединения в состоянии keepalive могут быть
+ закрыты преждевременно, если у воркера нет свободных соединений.
+ Спасибо Максиму Дунину.
+
+ *) Добавление: параметр rotate директивы image_filter.
+ Спасибо Adam Bocim.
+
+ *) Исправление: ситуации, когда бэкенд в директивах fastcgi_pass,
+ scgi_pass или uwsgi_pass задан выражением и ссылается на описанный
+ upstream.
+
+
+Изменения в nginx 0.9.6 21.03.2011
+
+ *) Добавление: директива map поддерживает регулярные выражения в
+ качестве значения первого параметра.
+
+ *) Добавление: переменная $time_iso8601 для access_log.
+ Спасибо Michael Lustfield.
+
+
+Изменения в nginx 0.9.5 21.02.2011
+
+ *) Изменение: теперь по умолчанию nginx использует значение -1 для
+ listen backlog на Linux.
+ Спасибо Андрею Нигматулину.
+
+ *) Добавление: параметр utf8 в директивах geoip_country и geoip_city.
+ Спасибо Денису Латыпову.
+
+ *) Исправление: исправление в умолчательной директиве proxy_redirect,
+ если в директиве proxy_pass не был описан URI.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: директива error_page не работала с нестандартными кодами
+ ошибок; ошибка появилась в 0.8.53.
+ Спасибо Максиму Дунину.
+
+
+Изменения в nginx 0.9.4 21.01.2011
+
+ *) Добавление: директива server_name поддерживает переменную $hostname.
+
+ *) Добавление: 494 код для ошибки "Request Header Too Large".
+
+
+Изменения в nginx 0.9.3 13.12.2010
+
+ *) Исправление: если для пары IPv6-адрес:порт описан только один сервер,
+ то выделения в регулярных выражениях в директиве server_name не
+ работали.
+
+ *) Исправление: nginx не собирался под Solaris; ошибка появилась в
+ 0.9.0.
+
+
+Изменения в nginx 0.9.2 06.12.2010
+
+ *) Добавление: поддержка строки "If-Unmodified-Since" в заголовке
+ запроса клиента.
+
+ *) Изменение: использование accept(), если accept4() не реализован;
+ ошибка появилась в 0.9.0.
+
+ *) Исправление: nginx не собирался под Cygwin; ошибка появилась в 0.9.0.
+
+ *) Исправление: уязвимости в OpenSSL CVE-2010-4180.
+ Спасибо Максиму Дунину.
+
+
+Изменения в nginx 0.9.1 30.11.2010
+
+ *) Исправление: директивы вида "return CODE message" не работали; ошибка
+ появилась в 0.9.0.
+
+
+Изменения в nginx 0.9.0 29.11.2010
+
+ *) Добавление: директива keepalive_disable.
+
+ *) Добавление: директива map поддерживает переменные в качестве значения
+ определяемой переменной.
+
+ *) Добавление: директива map поддерживает пустые строки в качестве
+ значения первого параметра.
+
+ *) Добавление: директива map поддерживает выражения в первом параметре.
+
+ *) Добавление: страница руководства nginx(8).
+ Спасибо Сергею Осокину.
+
+ *) Добавление: поддержка accept4() в Linux.
+ Спасибо Simon Liu.
+
+ *) Изменение: устранение предупреждения линкера о "sys_errlist" и
+ "sys_nerr" под Linux; предупреждение появилось в 0.8.35.
+
+ *) Исправление: при использовании директивы auth_basic в рабочем
+ процессе мог произойти segmentation fault.
+ Спасибо Михаилу Лалетину.
+
+ *) Исправление: совместимость с модулем ngx_http_eval_module; ошибка
+ появилась в 0.8.42.
+
+
+Изменения в nginx 0.8.53 18.10.2010
+
+ *) Добавление: теперь директива error_page позволяет менять код статуса
+ у редиректа.
+
+ *) Добавление: директива gzip_disable поддерживает специальную маску
+ degradation.
+
+ *) Исправление: при использовании файлового AIO могла происходить утечка
+ сокетов.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: если в первом сервере не была описана директива listen и
+ нигде явно не описан сервер по умолчанию, то сервером по умолчанию
+ становился следующий сервер с директивой listen; ошибка появилась в
+ 0.8.21.
+
+
+Изменения в nginx 0.8.52 28.09.2010
+
+ *) Исправление: nginx использовал режим SSL для listen сокета, если для
+ него был установлен любой listen-параметр; ошибка появилась в 0.8.51.
+
+
+Изменения в nginx 0.8.51 27.09.2010
+
+ *) Изменение: директива secure_link_expires упразднена.
+
+ *) Изменение: уровень логгирования ошибок resolver'а понижен с уровня
+ alert на error.
+
+ *) Добавление: теперь параметр "ssl" listen-сокета можно устанавливать
+ несколько раз.
+
+
+Изменения в nginx 0.8.50 02.09.2010
+
+ *) Добавление: директивы secure_link, secure_link_md5 и
+ secure_link_expires модуля ngx_http_secure_link_module.
+
+ *) Добавление: ключ -q.
+ Спасибо Геннадию Махомеду.
+
+ *) Исправление: при использовании кэширования рабочие процессы и могли
+ зациклиться во время переконфигурации; ошибка появилась в 0.8.48.
+
+ *) Исправление: в директиве gzip_disable.
+ Спасибо Derrick Petzold.
+
+ *) Исправление: nginx/Windows не мог посылать сигналы stop, quit,
+ reopen, reload процессу, запущенному в другой сессии.
+
+
+Изменения в nginx 0.8.49 09.08.2010
+
+ *) Добавление: директива image_filter_jpeg_quality поддерживает
+ переменные.
+
+ *) Исправление: при использовании переменной $geoip_region_name в
+ рабочем процессе мог произойти segmentation fault; ошибка появилась в
+ 0.8.48.
+
+ *) Исправление: ошибки, перехваченные error_page, кэшировались только до
+ следующего запроса; ошибка появилась в 0.8.48.
+
+
+Изменения в nginx 0.8.48 03.08.2010
+
+ *) Изменение: теперь по умолчанию директива server_name имеет значение
+ пустое имя "".
+ Спасибо Геннадию Махомеду.
+
+ *) Изменение: теперь по умолчанию директива server_name_in_redirect
+ имеет значение off.
+
+ *) Добавление: переменные $geoip_dma_code, $geoip_area_code и
+ $geoip_region_name.
+ Спасибо Christine McGonagle.
+
+ *) Исправление: директивы proxy_pass, fastcgi_pass, uwsgi_pass и
+ scgi_pass не наследовались в блоки limit_except.
+
+ *) Исправление: директивы proxy_cache_min_uses, fastcgi_cache_min_uses
+ uwsgi_cache_min_uses и scgi_cache_min_uses не работали; ошибка
+ появилась в 0.8.46.
+
+ *) Исправление: директива fastcgi_split_path_info неверно использовала
+ выделения, если в выделения попадала только часть URI.
+ Спасибо Юрию Тарадаю и Frank Enderle.
+
+ *) Исправление: директива rewrite не экранировала символ ";" при
+ копировании из URI в аргументы.
+ Спасибо Daisuke Murase.
+
+ *) Исправление: модуль ngx_http_image_filter_module закрывал соединение,
+ если изображение было больше размера image_filter_buffer.
+
+
+Изменения в nginx 0.8.47 28.07.2010
+
+ *) Исправление: переменная $request_time имела неверные значения для
+ подзапросов.
+
+ *) Исправление: ошибки, перехваченные error_page, не кэшировались.
+
+ *) Исправление: если использовался параметр max_size, то cache manager
+ мог зациклиться; ошибка появилась в 0.8.46.
+
+
+Изменения в nginx 0.8.46 19.07.2010
+
+ *) Изменение: директивы proxy_no_cache, fastcgi_no_cache, uwsgi_no_cache
+ и scgi_no_cache теперь влияют только на сохранение закэшированного
+ ответа.
+
+ *) Добавление: директивы proxy_cache_bypass, fastcgi_cache_bypass,
+ uwsgi_cache_bypass и scgi_cache_bypass.
+
+ *) Исправление: nginx не освобождал память в keys_zone кэшей в случае
+ ошибки работы с бэкендом: память освобождалась только по истечении
+ времени неактивности или при недостатке памяти.
+
+
+Изменения в nginx 0.8.45 13.07.2010
+
+ *) Добавление: улучшения в модуле ngx_http_xslt_filter.
+ Спасибо Laurence Rowe.
+
+ *) Исправление: ответ SSI модуля мог передаваться не полностью после
+ команды include с параметром wait="yes"; ошибка появилась в 0.7.25.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: директива listen не поддерживала параметр setfib=0.
+
+
+Изменения в nginx 0.8.44 05.07.2010
+
+ *) Изменение: теперь nginx по умолчанию не кэширует ответы бэкендов, в
+ заголовке которых есть строка "Set-Cookie".
+
+ *) Добавление: директива listen поддерживает параметр setfib.
+ Спасибо Андрею Филонову.
+
+ *) Исправление: директива sub_filter могла изменять регистр букв при
+ частичном совпадении.
+
+ *) Исправление: совместимость с HP/UX.
+
+ *) Исправление: совместимость с компилятором AIX xlC_r.
+
+ *) Исправление: nginx считал большие пакеты SSLv2 как обычные текстовые
+ запросы.
+ Спасибо Miroslaw Jaworski.
+
+
+Изменения в nginx 0.8.43 30.06.2010
+
+ *) Добавление: ускорение загрузки больших баз geo-диапазонов.
+
+ *) Исправление: перенаправление ошибки в "location /zero {return 204;}"
+ без изменения кода ответа оставляло тело ошибки; ошибка появилась в
+ 0.8.42.
+
+ *) Исправление: nginx мог закрывать IPv6 listen сокет во время
+ переконфигурации.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: переменную $uid_set можно использовать на любой стадии
+ обработки запроса.
+
+
+Изменения в nginx 0.8.42 21.06.2010
+
+ *) Изменение: теперь nginx проверяет location'ы, заданные регулярными
+ выражениями, если запрос полностью совпал с location'ом, заданным
+ строкой префикса. Предыдущее поведение появилось в 0.7.1.
+
+ *) Добавление: модуль ngx_http_scgi_module.
+ Спасибо Manlio Perillo.
+
+ *) Добавление: в директиве return можно добавлять текст ответа.
+
+
+Изменения в nginx 0.8.41 15.06.2010
+
+ *) Безопасность: рабочий процесс nginx/Windows мог завершаться аварийно
+ при запросе файла с неверной кодировкой UTF-8.
+
+ *) Изменение: теперь nginx разрешает использовать пробелы в строке
+ запроса.
+
+ *) Исправление: директива proxy_redirect неправильно изменяла строку
+ "Refresh" в заголовке ответа бэкенда.
+ Спасибо Андрею Андрееву и Максиму Согину.
+
+ *) Исправление: nginx не поддерживал путь без имени хоста в строке
+ "Destination" в заголовке запроса.
+
+
+Изменения в nginx 0.8.40 07.06.2010
+
+ *) Безопасность: теперь nginx/Windows игнорирует имя потока файла по
+ умолчанию.
+ Спасибо Jose Antonio Vazquez Gonzalez.
+
+ *) Добавление: модуль ngx_http_uwsgi_module.
+ Спасибо Roberto De Ioris.
+
+ *) Добавление: директива fastcgi_param со значением, начинающимся со
+ строки "HTTP_", изменяет строку заголовка в запросе клиента.
+
+ *) Исправление: строки "If-Modified-Since", "If-Range" и им подобные в
+ заголовке запроса клиента передавались FastCGI-серверу при
+ кэшировании.
+
+ *) Исправление: listen unix domain сокет нельзя было изменить во время
+ переконфигурации.
+ Спасибо Максиму Дунину.
+
+
+Изменения в nginx 0.8.39 31.05.2010
+
+ *) Исправление: наследуемая директива alias неправильно работала во
+ вложенном location'е.
+
+ *) Исправление: в комбинации директив alias с переменными и try_files;
+
+ *) Исправление: listen unix domain и IPv6 сокеты не наследовались во
+ время обновления без перерыва.
+ Спасибо Максиму Дунину.
+
+
+Изменения в nginx 0.8.38 24.05.2010
+
+ *) Добавление: директивы proxy_no_cache и fastcgi_no_cache.
+
+ *) Добавление: теперь при использовании переменной $scheme в директиве
+ rewrite автоматически делается редирект.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: теперь задержки в директиве limit_req соответствует
+ описанному алгоритму.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: переменную $uid_got нельзя было использовать в SSI и
+ перловом модулях.
+
+
+Изменения в nginx 0.8.37 17.05.2010
+
+ *) Добавление: модуль ngx_http_split_clients_module.
+
+ *) Добавление: директива map поддерживает ключи больше 255 символов.
+
+ *) Исправление: nginx игнорировал значения "private" и "no-store" в
+ строке "Cache-Control" в заголовке ответа бэкенда.
+
+ *) Исправление: параметр stub в SSI-директиве include не использовался,
+ если пустой ответ имел код 200.
+
+ *) Исправление: если проксированный или FastCGI запрос внутренне
+ перенаправлялся в другой проксированный или FastCGI location, то в
+ рабочем процессе мог произойти segmentation fault; ошибка появилась в
+ 0.8.33.
+ Спасибо Yichun Zhang.
+
+ *) Исправление: соединения IMAP к серверу Zimbra могло зависнуть до
+ таймаута.
+ Спасибо Alan Batie.
+
+
+Изменения в nginx 0.8.36 22.04.2010
+
+ *) Исправление: модуль ngx_http_dav_module неправильно обрабатывал
+ методы DELETE, COPY и MOVE для симлинков.
+
+ *) Исправление: модуль SSI в подзапросах использовал закэшированные в
+ основном запросе значения переменных $query_string, $arg_... и им
+ подобных.
+
+ *) Исправление: значение переменной повторно экранировалось после
+ каждого вывода SSI-команды echo; ошибка появилась в 0.6.14.
+
+ *) Исправление: рабочий процесс зависал при запросе файла FIFO.
+ Спасибо Vicente Aguilar и Максиму Дунину.
+
+ *) Исправление: совместимость с OpenSSL-1.0.0 на 64-битном Linux.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: nginx не собирался с параметром --without-http-cache;
+ ошибка появилась в 0.8.35.
+
+
+Изменения в nginx 0.8.35 01.04.2010
+
+ *) Изменение: теперь charset-фильтр работает до SSI-фильтра.
+
+ *) Добавление: директива chunked_transfer_encoding.
+
+ *) Исправление: символ "&" при копировании в аргументы в правилах
+ rewrite не экранировался.
+
+ *) Исправление: nginx мог завершаться аварийно во время обработки
+ сигнала или при использовании директивы timer_resolution на
+ платформах, не поддерживающих методы kqueue или eventport.
+ Спасибо George Xie и Максиму Дунину.
+
+ *) Исправление: если временные файлы и постоянное место хранения
+ располагались на разных файловых системах, то у постоянных файлов
+ время изменения было неверным.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: модуль ngx_http_memcached_module мог выдавать ошибку
+ "memcached sent invalid trailer".
+ Спасибо Максиму Дунину.
+
+ *) Исправление: nginx не мог собрать библиотеку zlib-1.2.4 из исходных
+ текстов.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: в рабочем процессе происходил segmentation fault, если
+ перед ответом FastCGI-сервера было много вывода в stderr; ошибка
+ появилась в 0.8.34.
+ Спасибо Максиму Дунину.
+
+
+Изменения в nginx 0.8.34 03.03.2010
+
+ *) Исправление: nginx не поддерживал все шифры, используемые в
+ клиентских сертификатах.
+ Спасибо Иннокентию Еникееву.
+
+ *) Исправление: nginx неправильно кэшировал FastCGI-ответы, если перед
+ ответом было много вывода в stderr.
+
+ *) Исправление: nginx не поддерживал HTTPS-рефереры.
+
+ *) Исправление: nginx/Windows мог не находить файлы, если путь в
+ конфигурации был задан в другом регистре; ошибка появилась в 0.8.33.
+
+ *) Исправление: переменная $date_local выдавала неверное время, если
+ использовался формат "%s".
+ Спасибо Максиму Дунину.
+
+ *) Исправление: если ssl_session_cache не был установлен или установлен
+ в none, то при проверке клиентского сертификаты могла происходить
+ ошибка "session id context uninitialized"; ошибка появилась в 0.7.1.
+
+ *) Исправление: geo-диапазон возвращал значение по умолчанию, если
+ диапазон включал в себя одну и более сетей размером /16 и не
+ начинался на границе сети размером /16.
+
+ *) Исправление: блок, используемый в параметре stub в SSI-директиве
+ include, выводился с MIME-типом "text/plain".
+
+ *) Исправление: $r->sleep() не работал; ошибка появилась в 0.8.11.
+
+
+Изменения в nginx 0.8.33 01.02.2010
+
+ *) Безопасность: теперь nginx/Windows игнорирует пробелы в конце URI.
+ Спасибо Dan Crowley, Core Security Technologies.
+
+ *) Безопасность: теперь nginx/Windows игнорирует короткие имена файлов.
+ Спасибо Dan Crowley, Core Security Technologies.
+
+ *) Изменение: теперь keepalive соединения после запросов POST не
+ запрещаются для MSIE 7.0+.
+ Спасибо Adam Lounds.
+
+ *) Изменение: теперь keepalive соединения запрещены для Safari.
+ Спасибо Joshua Sierles.
+
+ *) Исправление: если проксированный или FastCGI запрос внутренне
+ перенаправлялся в другой проксированный или FastCGI location, то
+ переменная $upstream_response_time могла иметь ненормально большое
+ значение; ошибка появилась в 0.8.7.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault при
+ отбрасывания тела запроса; ошибка появилась в 0.8.11.
+
+
+Изменения в nginx 0.8.32 11.01.2010
+
+ *) Исправление: ошибки при использовании кодировки UTF-8 в
+ ngx_http_autoindex_module.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: именованные выделения в регулярных выражениях работали
+ только для двух переменных.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: теперь в строке заголовка запроса "Host" используется
+ имя "localhost", если в директиве auth_http указан unix domain сокет.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: nginx не поддерживал передачу chunk'ами для 201-ых
+ ответов.
+ Спасибо Julian Reich.
+
+ *) Исправление: если директива "expires modified" выставляла дату в
+ прошлом, то в строке заголовка ответа "Cache-Control" выдавалось
+ отрицательное число.
+ Спасибо Алексею Капранову.
+
+
+Изменения в nginx 0.8.31 23.12.2009
+
+ *) Добавление: теперь директива error_page может перенаправлять ответы
+ со статусом 301 и 302.
+
+ *) Добавление: переменные $geoip_city_continent_code, $geoip_latitude и
+ $geoip_longitude.
+ Спасибо Arvind Sundararajan.
+
+ *) Добавление: модуль ngx_http_image_filter_module теперь всегда удаляет
+ EXIF и другие данные, если они занимают больше 5% в JPEG-файле.
+
+ *) Исправление: nginx закрывал соединение при запросе закэшированного
+ ответа с пустым телом.
+ Спасибо Piotr Sikora.
+
+ *) Исправление: nginx мог не собираться gcc 4.x при использовании
+ оптимизации -O2 и выше.
+ Спасибо Максиму Дунину и Денису Латыпову.
+
+ *) Исправление: регулярные выражения в location всегда тестировались с
+ учётом регистра; ошибка появилась в 0.8.25.
+
+ *) Исправление: nginx кэшировал 304 ответ, если в заголовке
+ проксируемого запроса была строка "If-None-Match".
+ Спасибо Tim Dettrick и David Kostal.
+
+ *) Исправление: nginx/Windows пытался дважды удалить временный файл при
+ перезаписи уже существующего файла.
+
+
+Изменения в nginx 0.8.30 15.12.2009
+
+ *) Изменение: теперь по умолчанию размер буфера директивы
+ large_client_header_buffers равен 8K.
+ Спасибо Andrew Cholakian.
+
+ *) Добавление: файл conf/fastcgi.conf для простых конфигураций FastCGI.
+
+ *) Исправление: nginx/Windows пытался дважды переименовать временный
+ файл при перезаписи уже существующего файла.
+
+ *) Исправление: ошибки double free or corruption, возникающей, если имя
+ хоста не было найдено; ошибка появилась в 0.8.22.
+ Спасибо Константину Свисту.
+
+ *) Исправление: в использовании libatomic на некоторых платформах.
+ Спасибо W-Mark Kubacki.
+
+
+Изменения в nginx 0.8.29 30.11.2009
+
+ *) Изменение: теперь для проксируемых ответов HTTP/0.9 в лог пишется код
+ ответа "009".
+
+ *) Добавление: директивы addition_types, charset_types, gzip_types,
+ ssi_types, sub_filter_types и xslt_types поддерживают параметр "*".
+
+ *) Добавление: использование встроенных атомарных операций GCC 4.1+.
+ Спасибо W-Mark Kubacki.
+
+ *) Добавление: параметр --with-libatomic[=DIR] в configure.
+ Спасибо W-Mark Kubacki.
+
+ *) Исправление: listen unix domain сокет имели ограниченные права
+ доступа.
+
+ *) Исправление: закэшированные ответы ответов HTTP/0.9 неправильно
+ обрабатывались.
+
+ *) Исправление: именованные выделения в регулярных выражениях, заданные
+ как "?P<...>", не работали в директиве server_name.
+ Спасибо Максиму Дунину.
+
+
+Изменения в nginx 0.8.28 23.11.2009
+
+ *) Исправление: nginx не собирался с параметром --without-pcre; ошибка
+ появилась в 0.8.25.
+
+
+Изменения в nginx 0.8.27 17.11.2009
+
+ *) Исправление: регулярные выражения не работали в nginx/Windows; ошибка
+ появилась в 0.8.25.
+
+
+Изменения в nginx 0.8.26 16.11.2009
+
+ *) Исправление: ошибки при использовании выделений в директиве rewrite;
+ ошибка появилась в 0.8.25.
+
+ *) Исправление: nginx не собирался без параметра --with-debug; ошибка
+ появилась в 0.8.25.
+
+
+Изменения в nginx 0.8.25 16.11.2009
+
+ *) Изменение: теперь в лог ошибок не пишется сообщение, если переменная
+ не найдена с помощью метода $r->variable().
+
+ *) Добавление: модуль ngx_http_degradation_module.
+
+ *) Добавление: именованные выделения в регулярных выражениях.
+
+ *) Добавление: теперь при использовании переменных в директиве
+ proxy_pass не требуется задавать URI.
+
+ *) Добавление: теперь директива msie_padding работает и для Chrome.
+
+ *) Исправление: в рабочем процессе происходил segmentation fault при
+ недостатке памяти; ошибка появилась в 0.8.18.
+
+ *) Исправление: nginx передавал сжатые ответы клиентам, не
+ поддерживающим сжатие, при настройках gzip_static on и gzip_vary off;
+ ошибка появилась в 0.8.16.
+
+
+Изменения в nginx 0.8.24 11.11.2009
+
+ *) Исправление: nginx всегда добавлял строку "Content-Encoding: gzip" в
+ заголовок 304-ых ответов модуля ngx_http_gzip_static_module.
+
+ *) Исправление: nginx не собирался без параметра --with-debug; ошибка
+ появилась в 0.8.23.
+
+ *) Исправление: параметр "unix:" в директиве set_real_ip_from
+ неправильно наследовался с предыдущего уровня.
+
+ *) Исправление: в resolver'е при определении пустого имени.
+
+
+Изменения в nginx 0.8.23 11.11.2009
+
+ *) Безопасность: теперь SSL/TLS renegotiation запрещён.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: listen unix domain сокет не наследовался во время
+ обновления без перерыва.
+
+ *) Исправление: параметр "unix:" в директиве set_real_ip_from не работал
+ без ещё одной директивы с любым IP-адресом.
+
+ *) Исправление: segmentation fault и зацикливания в resolver'е.
+
+ *) Исправление: в resolver'е.
+ Спасибо Артёму Бохану.
+
+
+Изменения в nginx 0.8.22 03.11.2009
+
+ *) Добавление: директивы proxy_bind, fastcgi_bind и memcached_bind.
+
+ *) Добавление: директивы access и deny поддерживают IPv6.
+
+ *) Добавление: директива set_real_ip_from поддерживает IPv6 адреса в
+ заголовках запроса.
+
+ *) Добавление: параметр "unix:" в директиве set_real_ip_from.
+
+ *) Исправление: nginx не удалял unix domain сокет после тестирования
+ конфигурации.
+
+ *) Исправление: nginx удалял unix domain сокет во время обновления без
+ перерыва.
+
+ *) Исправление: оператор "!-x" не работал.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault при
+ использовании limit_rate в HTTPS сервере.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: при записи в лог переменной $limit_rate в рабочем
+ процессе происходил segmentation fault.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если внутри блока server не было директивы listen; ошибка появилась в
+ 0.8.21.
+
+
+Изменения в nginx 0.8.21 26.10.2009
+
+ *) Добавление: теперь ключ -V показывает статус поддержки TLS SNI.
+
+ *) Добавление: директива listen модуля HTTP поддерживает unix domain
+ сокеты.
+ Спасибо Hongli Lai.
+
+ *) Добавление: параметр "default_server" в директиве listen.
+
+ *) Добавление: теперь параметр "default" не обязателен для установки
+ параметров listen-сокета.
+
+ *) Исправление: nginx не поддерживал даты в 2038 году на 32-битных
+ платформах;
+
+ *) Исправление: утечки сокетов; ошибка появилась в 0.8.11.
+
+
+Изменения в nginx 0.8.20 14.10.2009
+
+ *) Изменение: теперь по умолчанию используются следующие шифры SSL:
+ "HIGH:!ADH:!MD5".
+
+ *) Исправление: модуль ngx_http_autoindex_module не показывал последний
+ слэш для линков на каталоги; ошибка появилась в 0.7.15.
+
+ *) Исправление: nginx не закрывал лог, заданный параметром конфигурации
+ --error-log-path; ошибка появилась в 0.7.53.
+
+ *) Исправление: nginx не считал запятую разделителем в строке
+ "Cache-Control" в заголовке ответа бэкенда.
+
+ *) Исправление: nginx/Windows мог не создать временный файл, файл в кэше
+ или файл с помощью директив proxy/fastcgi_store, если рабочий процесс
+ не имел достаточно прав для работы с каталогами верхнего уровня.
+
+ *) Исправление: строки "Set-Cookie" и "P3P" в заголовке ответа
+ FastCGI-сервера не скрывались при кэшировании, если не использовались
+ директивы fastcgi_hide_header с любыми параметрами.
+
+ *) Исправление: nginx неверно считал размер кэша на диске.
+
+
+Изменения в nginx 0.8.19 06.10.2009
+
+ *) Изменение: теперь протокол SSLv2 по умолчанию запрещён.
+
+ *) Изменение: теперь по умолчанию используются следующие шифры SSL:
+ "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM".
+
+ *) Исправление: директива limit_req не работала; ошибка появилась в
+ 0.8.18.
+
+
+Изменения в nginx 0.8.18 06.10.2009
+
+ *) Добавление: директива read_ahead.
+
+ *) Добавление: теперь можно использовать несколько директив
+ perl_modules.
+
+ *) Добавление: директивы limit_req_log_level и limit_conn_log_level.
+
+ *) Исправление: теперь директива limit_req соответствует алгоритму leaky
+ bucket.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: nginx не работал на Linux/sparc.
+ Спасибо Marcus Ramberg.
+
+ *) Исправление: nginx слал символ '\0' в строке "Location" в заголовке в
+ ответе на запрос MKCOL.
+ Спасибо Xie Zhenye.
+
+ *) Исправление: вместо кода ответа 499 в лог записывался код 0; ошибка
+ появилась в 0.8.11.
+
+ *) Исправление: утечки сокетов; ошибка появилась в 0.8.11.
+
+
+Изменения в nginx 0.8.17 28.09.2009
+
+ *) Безопасность: теперь символы "/../" запрещены в строке "Destination"
+ в заголовке запроса.
+
+ *) Изменение: теперь значение переменной $host всегда в нижнем регистре.
+
+ *) Добавление: переменная $ssl_session_id.
+
+ *) Исправление: утечки сокетов; ошибка появилась в 0.8.11.
+
+
+Изменения в nginx 0.8.16 22.09.2009
+
+ *) Добавление: директива image_filter_transparency.
+
+ *) Исправление: директива "addition_types" была неверно названа
+ "addtion_types".
+
+ *) Исправление: порчи кэша resolver'а.
+ Спасибо Matthew Dempsky.
+
+ *) Исправление: утечки памяти в resolver'е.
+ Спасибо Matthew Dempsky.
+
+ *) Исправление: неверная строка запроса в переменной $request
+ записывалась в access_log только при использовании error_log на
+ уровне info или debug.
+
+ *) Исправление: в поддержке альфа-канала PNG в модуле
+ ngx_http_image_filter_module.
+
+ *) Исправление: nginx всегда добавлял строку "Vary: Accept-Encoding" в
+ заголовок ответа, если обе директивы gzip_static и gzip_vary были
+ включены.
+
+ *) Исправление: в поддержке кодировки UTF-8 директивой try_files в
+ nginx/Windows.
+
+ *) Исправление: ошибки при использовании post_action; ошибка появилась в
+ 0.8.11.
+ Спасибо Игорю Артемьеву.
+
+
+Изменения в nginx 0.8.15 14.09.2009
+
+ *) Безопасность: при обработке специально созданного запроса в рабочем
+ процессе мог произойти segmentation fault.
+ Спасибо Chris Ries.
+
+ *) Исправление: если были описаны имена .domain.tld, .sub.domain.tld и
+ .domain-some.tld, то имя .sub.domain.tld попадало под маску
+ .domain.tld.
+
+ *) Исправление: в поддержке прозрачности в модуле
+ ngx_http_image_filter_module.
+
+ *) Исправление: в файловом AIO.
+
+ *) Исправление: ошибки при использовании X-Accel-Redirect; ошибка
+ появилась в 0.8.11.
+
+ *) Исправление: ошибки при использовании встроенного перла; ошибка
+ появилась в 0.8.11.
+
+
+Изменения в nginx 0.8.14 07.09.2009
+
+ *) Исправление: устаревший закэшированный запрос мог залипнуть в
+ состоянии "UPDATING".
+
+ *) Исправление: при использовании error_log на уровне info или debug в
+ рабочем процессе мог произойти segmentation fault.
+ Спасибо Сергею Боченкову.
+
+ *) Исправление: ошибки при использовании встроенного перла; ошибка
+ появилась в 0.8.11.
+
+ *) Исправление: директива error_page не перенаправляла ошибку 413;
+ ошибка появилась в 0.6.10.
+
+
+Изменения в nginx 0.8.13 31.08.2009
+
+ *) Исправление: в директиве "aio sendfile"; ошибка появилась в 0.8.12.
+
+ *) Исправление: nginx не собирался без параметра --with-file-aio на
+ FreeBSD; ошибка появилась в 0.8.12.
+
+
+Изменения в nginx 0.8.12 31.08.2009
+
+ *) Добавление: параметр sendfile в директиве aio во FreeBSD.
+
+ *) Исправление: ошибки при использовании try_files; ошибка появилась в
+ 0.8.11.
+
+ *) Исправление: ошибки при использовании memcached; ошибка появилась в
+ 0.8.11.
+
+
+Изменения в nginx 0.8.11 28.08.2009
+
+ *) Изменение: теперь директива "gzip_disable msie6" не запрещает сжатие
+ для MSIE 6.0 SV1.
+
+ *) Добавление: поддержка файлового AIO во FreeBSD и Linux.
+
+ *) Добавление: директива directio_alignment.
+
+
+Изменения в nginx 0.8.10 24.08.2009
+
+ *) Исправление: утечек памяти при использовании базы GeoIP City.
+
+ *) Исправление: ошибки при копировании временных файлов в постоянное
+ место хранения; ошибка появилась в 0.8.9.
+
+
+Изменения в nginx 0.8.9 17.08.2009
+
+ *) Добавление: теперь стартовый загрузчик кэша работает в отдельном
+ процесс; это должно улучшить обработку больших кэшей.
+
+ *) Добавление: теперь временные файлы и постоянное место хранения могут
+ располагаться на разных файловых системах.
+
+
+Изменения в nginx 0.8.8 10.08.2009
+
+ *) Исправление: в обработке заголовков ответа, разделённых в
+ FastCGI-записях.
+
+ *) Исправление: если запрос обрабатывался в двух проксированных или
+ FastCGI location'ах и в первом из них использовалось кэширование, то
+ в рабочем процессе происходил segmentation fault; ошибка появилась в
+ 0.8.7.
+
+
+Изменения в nginx 0.8.7 27.07.2009
+
+ *) Изменение: минимальная поддерживаемая версия OpenSSL - 0.9.7.
+
+ *) Изменение: параметр ask директивы ssl_verify_client изменён на
+ параметр optional и теперь он проверяет клиентский сертификат, если
+ он был предложен.
+ Спасибо Brice Figureau.
+
+ *) Добавление: переменная $ssl_client_verify.
+ Спасибо Brice Figureau.
+
+ *) Добавление: директива ssl_crl.
+ Спасибо Brice Figureau.
+
+ *) Добавление: параметр proxy директивы geo.
+
+ *) Добавление: директива image_filter поддерживает переменные для
+ задания размеров.
+
+ *) Исправление: использование переменной $ssl_client_cert портило
+ память; ошибка появилась в 0.7.7.
+ Спасибо Сергею Журавлёву.
+
+ *) Исправление: директивы proxy_pass_header и fastcgi_pass_header" не
+ передавали клиенту строки "X-Accel-Redirect", "X-Accel-Limit-Rate",
+ "X-Accel-Buffering" и "X-Accel-Charset" из заголовка ответа бэкенда.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: в обработке строк "Last-Modified" и "Accept-Ranges" в
+ заголовке ответа бэкенда; ошибка появилась в 0.7.44.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: ошибки "[alert] zero size buf" при получении пустых
+ ответы в подзапросах; ошибка появилась в 0.8.5.
+
+
+Изменения в nginx 0.8.6 20.07.2009
+
+ *) Добавление: модуль ngx_http_geoip_module.
+
+ *) Исправление: XSLT-фильтр мог выдавать ошибку "not well formed XML
+ document" для правильного документа.
+ Спасибо Kuramoto Eiji.
+
+ *) Исправление: в MacOSX, Cygwin и nginx/Windows при проверке
+ location'ов, заданных регулярным выражением, теперь всегда делается
+ сравнение без учёта регистра символов.
+
+ *) Исправление: теперь nginx/Windows игнорирует точки в конце URI.
+ Спасибо Hugo Leisink.
+
+ *) Исправление: имя файла указанного в --conf-path игнорировалось при
+ установке; ошибка появилась в 0.6.6.
+ Спасибо Максиму Дунину.
+
+
+Изменения в nginx 0.8.5 13.07.2009
+
+ *) Исправление: теперь nginx разрешает подчёркивания в методе запроса.
+
+ *) Исправление: при использовании HTTP Basic-аутентификации на Windows
+ для неверных имени/пароля возвращалась 500-ая ошибка.
+
+ *) Исправление: ответы модуля ngx_http_perl_module не работали в
+ подзапросах.
+
+ *) Исправление: в модуле ngx_http_limit_req_module.
+ Спасибо Максиму Дунину.
+
+
+Изменения в nginx 0.8.4 22.06.2009
+
+ *) Исправление: nginx не собирался с параметром --without-http-cache;
+ ошибка появилась в 0.8.3.
+
+
+Изменения в nginx 0.8.3 19.06.2009
+
+ *) Добавление: переменная $upstream_cache_status.
+
+ *) Исправление: nginx не собирался на MacOSX 10.6.
+
+ *) Исправление: nginx не собирался с параметром --without-http-cache;
+ ошибка появилась в 0.8.2.
+
+ *) Исправление: если использовался перехват 401 ошибки от бэкенда и
+ бэкенд не возвращал строку "WWW-Authenticate" в заголовке ответа, то
+ в рабочем процессе происходил segmentation fault.
+ Спасибо Евгению Мычло.
+
+
+Изменения в nginx 0.8.2 15.06.2009
+
+ *) Исправление: во взаимодействии open_file_cache и proxy/fastcgi кэша
+ на старте.
+
+ *) Исправление: open_file_cache мог кэшировать открытые файлы очень
+ долго; ошибка появилась в 0.7.4.
+
+
+Изменения в nginx 0.8.1 08.06.2009
+
+ *) Добавление: параметр updating в директивах proxy_cache_use_stale и
+ fastcgi_cache_use_stale.
+
+ *) Исправление: строки "If-Modified-Since", "If-Range" и им подобные в
+ заголовке запроса клиента передавались бэкенду при кэшировании, если
+ не использовалась директива proxy_set_header с любыми параметрами.
+
+ *) Исправление: строки "Set-Cookie" и "P3P" в заголовке ответа бэкенда
+ не скрывались при кэшировании, если не использовались директивы
+ proxy_hide_header/fastcgi_hide_header с любыми параметрами.
+
+ *) Исправление: модуль ngx_http_image_filter_module не понимал формат
+ GIF87a.
+ Спасибо Денису Ильиных.
+
+ *) Исправление: nginx не собирался на Solaris 10 и более ранних; ошибка
+ появилась в 0.7.56.
+
+
+Изменения в nginx 0.8.0 02.06.2009
+
+ *) Добавление: директива keepalive_requests.
+
+ *) Добавление: директива limit_rate_after.
+ Спасибо Ivan Debnar.
+
+ *) Исправление: XSLT-фильтр не работал в подзапросах.
+
+ *) Исправление: обработке относительных путей в nginx/Windows.
+
+ *) Исправление: в proxy_store, fastcgi_store, proxy_cache и
+ fastcgi_cache в nginx/Windows.
+
+ *) Исправление: в обработке ошибок выделения памяти.
+ Спасибо Максиму Дунину и Кириллу Коринскому.
+
+
+Изменения в nginx 0.7.59 25.05.2009
+
+ *) Добавление: директивы proxy_cache_methods и fastcgi_cache_methods.
+
+ *) Исправление: утечки сокетов; ошибка появилась в 0.7.25.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: при использовании переменной $request_body в рабочем
+ процессе происходил segmentation fault, если в запросе не было тела;
+ ошибка появилась в 0.7.58.
+
+ *) Исправление: SSL-модули могли не собираться на Solaris и Linux;
+ ошибка появилась в 0.7.56.
+
+ *) Исправление: ответы модуля ngx_http_xslt_filter_module не
+ обрабатывались SSI-, charset- и gzip-фильтрами.
+
+ *) Исправление: директива charset не ставила кодировку для ответов
+ модуля ngx_http_gzip_static_module.
+
+
+Изменения в nginx 0.7.58 18.05.2009
+
+ *) Добавление: директива listen почтового прокси-сервера поддерживает
+ IPv6.
+
+ *) Добавление: директива image_filter_jpeg_quality.
+
+ *) Добавление: директива client_body_in_single_buffer.
+
+ *) Добавление: переменная $request_body.
+
+ *) Исправление: в модуле ngx_http_autoindex_module в ссылках на имена
+ файлов, содержащих символ ":".
+
+ *) Исправление: процедура "make upgrade" не работала; ошибка появилась в
+ 0.7.53.
+ Спасибо Денису Латыпову.
+
+
+Изменения в nginx 0.7.57 12.05.2009
+
+ *) Исправление: при перенаправлении ошибок модуля
+ ngx_http_image_filter_module в именованный location в рабочем
+ процессе происходил floating-point fault; ошибка появилась в 0.7.56.
+
+
+Изменения в nginx 0.7.56 11.05.2009
+
+ *) Добавление: nginx/Windows поддерживает IPv6 в директиве listen модуля
+ HTTP.
+
+ *) Исправление: в модуле ngx_http_image_filter_module.
+
+
+Изменения в nginx 0.7.55 06.05.2009
+
+ *) Исправление: параметры http_XXX в директивах proxy_cache_use_stale и
+ fastcgi_cache_use_stale не работали.
+
+ *) Исправление: fastcgi кэш не кэшировал ответы, состоящие только из
+ заголовка.
+
+ *) Исправление: ошибки "select() failed (9: Bad file descriptor)" в
+ nginx/Unix и "select() failed (10038: ...)" в nginx/Windows.
+
+ *) Исправление: при использовании директивы debug_connection в рабочем
+ процессе мог произойти segmentation fault; ошибка появилась в 0.7.54.
+
+ *) Исправление: в сборке модуля ngx_http_image_filter_module.
+
+ *) Исправление: файлы больше 2G не передавались с использованием
+ $r->sendfile.
+ Спасибо Максиму Дунину.
+
+
+Изменения в nginx 0.7.54 01.05.2009
+
+ *) Добавление: модуль ngx_http_image_filter_module.
+
+ *) Добавление: директивы proxy_ignore_headers и fastcgi_ignore_headers.
+
+ *) Исправление: при использовании переменных "open_file_cache_errors on"
+ в рабочем процессе мог произойти segmentation fault; ошибка появилась
+ в 0.7.53.
+
+ *) Исправление: директива "port_in_redirect off" не работала; ошибка
+ появилась в 0.7.39.
+
+ *) Исправление: улучшение обработки ошибок метода select.
+
+ *) Исправление: ошибки "select() failed (10022: ...)" в nginx/Windows.
+
+ *) Исправление: в текстовых сообщениях об ошибках в nginx/Windows;
+ ошибка появилась в 0.7.53.
+
+
+Изменения в nginx 0.7.53 27.04.2009
+
+ *) Изменение: теперь лог, указанный в --error-log-path, создаётся с
+ самого начала работы.
+
+ *) Добавление: теперь ошибки и предупреждения при старте записываются в
+ error_log и выводятся на stderr.
+
+ *) Добавление: при сборке с пустым параметром --prefix= nginx использует
+ как префикс каталог, в котором он был запущен.
+
+ *) Добавление: ключ -p.
+
+ *) Добавление: ключ -s на Unix-платформах.
+
+ *) Добавление: ключи -? и -h.
+ Спасибо Jerome Loyet.
+
+ *) Добавление: теперь ключи можно задавать в сжатой форме.
+
+ *) Исправление: nginx/Windows не работал, если файл конфигурации был
+ задан ключом -c.
+
+ *) Исправление: при использовании директив proxy_store, fastcgi_store,
+ proxy_cache или fastcgi_cache временные файлы могли не удаляться.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: в заголовке Auth-Method запроса серверу аутентификации
+ почтового прокси-сервера передавалось неверное значение; ошибка
+ появилась в 0.7.34.
+ Спасибо Simon Lecaille.
+
+ *) Исправление: при логгировании на Linux не писались текстовые описания
+ системных ошибок; ошибка появилась в 0.7.45.
+
+ *) Исправление: директива fastcgi_cache_min_uses не работала.
+ Спасибо Андрею Воробьёву.
+
+
+Изменения в nginx 0.7.52 20.04.2009
+
+ *) Добавление: первая бинарная версия под Windows.
+
+ *) Исправление: корректная обработка метода HEAD при кэшировании.
+
+ *) Исправление: корректная обработка строк "If-Modified-Since",
+ "If-Range" и им подобных в заголовке запроса клиента при кэшировании.
+
+ *) Исправление: теперь строки "Set-Cookie" и "P3P" скрываются в
+ заголовке ответа для закэшированных ответов.
+
+ *) Исправление: если nginx был собран с модулем ngx_http_perl_module и
+ perl поддерживал потоки, то при выходе основного процесса могла
+ выдаваться ошибка "panic: MUTEX_LOCK".
+
+ *) Исправление: nginx не собирался с параметром --without-http-cache;
+ ошибка появилась в 0.7.48.
+
+ *) Исправление: nginx не собирался на платформах, отличных от i386,
+ amd64, sparc и ppc; ошибка появилась в 0.7.42.
+
+
+Изменения в nginx 0.7.51 12.04.2009
+
+ *) Добавление: директива try_files поддерживает код ответа в последнем
+ параметре.
+
+ *) Добавление: теперь в директиве return можно использовать любой код
+ ответа.
+
+ *) Исправление: директива error_page делала внешний редирект без строки
+ запроса; ошибка появилась в 0.7.44.
+
+ *) Исправление: если сервера слушали на нескольких явно описанных
+ адресах, то виртуальные сервера могли не работать; ошибка появилась в
+ 0.7.39.
+
+
+Изменения в nginx 0.7.50 06.04.2009
+
+ *) Исправление: переменные $arg_... не работали; ошибка появилась в
+ 0.7.49.
+
+
+Изменения в nginx 0.7.49 06.04.2009
+
+ *) Исправление: при использовании переменных $arg_... в рабочем процессе
+ мог произойти segmentation fault; ошибка появилась в 0.7.48.
+
+
+Изменения в nginx 0.7.48 06.04.2009
+
+ *) Добавление: директива proxy_cache_key.
+
+ *) Исправление: теперь nginx учитывает при кэшировании строки
+ "X-Accel-Expires", "Expires" и "Cache-Control" в заголовке ответа
+ бэкенда.
+
+ *) Исправление: теперь nginx кэширует только ответы на запросы GET.
+
+ *) Исправление: директива fastcgi_cache_key не наследовалась.
+
+ *) Исправление: переменные $arg_... не работали с SSI-подзапросами.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: nginx не собирался с библиотекой uclibc.
+ Спасибо Timothy Redaelli.
+
+ *) Исправление: nginx не собирался на OpenBSD; ошибка появилась
+ в 0.7.46.
+
+
+Изменения в nginx 0.7.47 01.04.2009
+
+ *) Исправление: nginx не собирался на FreeBSD 6 и более ранних версиях;
+ ошибка появилась в 0.7.46.
+
+ *) Исправление: nginx не собирался на MacOSX; ошибка появилась в 0.7.46.
+
+ *) Исправление: если использовался параметр max_size, то cache manager
+ мог удалить весь кэш; ошибка появилась в 0.7.46.
+
+ *) Изменение: в рабочем процессе мог произойти segmentation fault, если
+ директивы proxy_cache/fastcgi_cache и proxy_cache_valid/
+ fastcgi_cache_valid не были заданы на одном уровне; ошибка появилась
+ в 0.7.46.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault при
+ перенаправлении запроса проксированному или FastCGI-серверу с помощью
+ error_page или try_files; ошибка появилась в 0.7.44.
+
+
+Изменения в nginx 0.7.46 30.03.2009
+
+ *) Исправление: архив предыдущего релиза был неверным.
+
+
+Изменения в nginx 0.7.45 30.03.2009
+
+ *) Изменение: теперь директивы proxy_cache и proxy_cache_valid можно
+ задавать на разных уровнях.
+
+ *) Изменение: параметр clean_time в директиве proxy_cache_path удалён.
+
+ *) Добавление: параметр max_size в директиве proxy_cache_path.
+
+ *) Добавление: предварительная поддержка кэширования в модуле
+ ngx_http_fastcgi_module.
+
+ *) Добавление: теперь при ошибках выделения в разделяемой памяти в логе
+ указываются названия директивы и зоны.
+
+ *) Исправление: директива "add_header last-modified ''" не удаляла в
+ заголовке ответа строку "Last-Modified"; ошибка появилась в 0.7.44.
+
+ *) Исправление: в директиве auth_basic_user_file не работал
+ относительный путь, заданный строкой без переменных; ошибка появилась
+ в 0.7.44.
+ Спасибо Jerome Loyet.
+
+ *) Исправление: в директиве alias, заданной переменными без ссылок на
+ выделения в регулярных выражениях; ошибка появилась в 0.7.42.
+
+
+Изменения в nginx 0.7.44 23.03.2009
+
+ *) Добавление: предварительная поддержка кэширования в модуле
+ ngx_http_proxy_module.
+
+ *) Добавление: параметр --with-pcre в configure.
+
+ *) Добавление: теперь директива try_files может быть использована на
+ уровне server.
+
+ *) Исправление: директива try_files неправильно обрабатывала строку
+ запроса в последнем параметре.
+
+ *) Исправление: директива try_files могла неверно тестировать каталоги.
+
+ *) Исправление: если для пары адрес:порт описан только один сервер, то
+ выделения в регулярных выражениях в директиве server_name не
+ работали.
+
+
+Изменения в nginx 0.7.43 18.03.2009
+
+ *) Исправление: запрос обрабатывался неверно, если директива root
+ использовала переменные; ошибка появилась в 0.7.42.
+
+ *) Исправление: если сервер слушал на адресах типа "*", то значение
+ переменной $server_addr было "0.0.0.0"; ошибка появилась в 0.7.36.
+
+
+Изменения в nginx 0.7.42 16.03.2009
+
+ *) Изменение: ошибка "Invalid argument", возвращаемая
+ setsockopt(TCP_NODELAY) на Solaris, теперь игнорируется.
+
+ *) Изменение: при отсутствии файла, указанного в директиве
+ auth_basic_user_file, теперь возвращается ошибка 403 вместо 500.
+
+ *) Добавление: директива auth_basic_user_file поддерживает переменные.
+ Спасибо Кириллу Коринскому.
+
+ *) Добавление: директива listen поддерживает параметр ipv6only.
+ Спасибо Zhang Hua.
+
+ *) Исправление: в директиве alias со ссылками на выделения в регулярных
+ выражениях; ошибка появилась в 0.7.40.
+
+ *) Исправление: совместимость с Tru64 UNIX.
+ Спасибо Dustin Marquess.
+
+ *) Исправление: nginx не собирался без библиотеки PCRE; ошибка появилась
+ в 0.7.41.
+
+
+Изменения в nginx 0.7.41 11.03.2009
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если в server_name или location были выделения в регулярных
+ выражениях; ошибка появилась в 0.7.40.
+ Спасибо Владимиру Сопоту.
+
+
+Изменения в nginx 0.7.40 09.03.2009
+
+ *) Добавление: директива location поддерживает выделения в регулярных
+ выражениях.
+
+ *) Добавление: директиву alias с ссылками на выделения в регулярных
+ выражениях можно использовать внутри location'а, заданного регулярным
+ выражением с выделениями.
+
+ *) Добавление: директива server_name поддерживает выделения в регулярных
+ выражениях.
+
+ *) Изменение: модуль ngx_http_autoindex_module не показывал последний
+ слэш для каталогов на файловой системе XFS; ошибка появилась в
+ 0.7.15.
+ Спасибо Дмитрию Кузьменко.
+
+
+Изменения в nginx 0.7.39 02.03.2009
+
+ *) Исправление: при включённом сжатии большие ответы с использованием
+ SSI могли зависать; ошибка появилась в 0.7.28.
+ Спасибо Артёму Бохану.
+
+ *) Исправление: при использовании коротких статических вариантов в
+ директиве try_files в рабочем процессе мог произойти segmentation
+ fault.
+
+
+Изменения в nginx 0.7.38 23.02.2009
+
+ *) Добавление: логгирование ошибок аутентификации.
+
+ *) Исправление: имя/пароль, заданные в auth_basic_user_file,
+ игнорировались после нечётного числа пустых строк.
+ Спасибо Александру Загребину.
+
+ *) Исправление: при использовании длинного пути в unix domain сокете в
+ главном процессе происходил segmentation fault; ошибка появилась в
+ 0.7.36.
+
+
+Изменения в nginx 0.7.37 21.02.2009
+
+ *) Исправление: директивы, использующие upstream'ы, не работали; ошибка
+ появилась в 0.7.36.
+
+
+Изменения в nginx 0.7.36 21.02.2009
+
+ *) Добавление: предварительная поддержка IPv6; директива listen модуля
+ HTTP поддерживает IPv6.
+
+ *) Исправление: переменная $ancient_browser не работала для браузеров,
+ заданных директивами modern_browser.
+
+
+Изменения в nginx 0.7.35 16.02.2009
+
+ *) Исправление: директива ssl_engine не использовала SSL-акселератор для
+ асимметричных шифров.
+ Спасибо Marcin Gozdalik.
+
+ *) Исправление: директива try_files выставляла MIME-type, исходя из
+ расширения первоначального запроса.
+
+ *) Исправление: в директивах server_name, valid_referers и map
+ неправильно обрабатывались имена вида "*domain.tld", если
+ использовались маски вида ".domain.tld" и ".subdomain.domain.tld";
+ ошибка появилась в 0.7.9.
+
+
+Изменения в nginx 0.7.34 10.02.2009
+
+ *) Добавление: параметр off в директиве if_modified_since.
+
+ *) Добавление: теперь после команды XCLIENT nginx посылает команду
+ HELO/EHLO.
+ Спасибо Максиму Дунину.
+
+ *) Добавление: поддержка Microsoft-специфичного режима
+ "AUTH LOGIN with User Name" в почтовом прокси-сервере.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: в директиве rewrite, возвращающей редирект, старые
+ аргументы присоединялись к новым через символ "?" вместо "&";
+ ошибка появилась в 0.1.18.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: nginx не собирался на AIX.
+
+
+Изменения в nginx 0.7.33 02.02.2009
+
+ *) Исправление: если на запрос с телом возвращался редирект, то ответ
+ мог быть двойным при использовании методов epoll или rtsig.
+ Спасибо Eden Li.
+
+ *) Исправление: для некоторых типов редиректов в переменной
+ $sent_http_location было пустое значение.
+
+ *) Исправление: при использовании директивы resolver в SMTP
+ прокси-сервере в рабочем процессе мог произойти segmentation fault.
+
+
+Изменения в nginx 0.7.32 26.01.2009
+
+ *) Добавление: теперь в директиве try_files можно явно указать проверку
+ каталога.
+
+ *) Исправление: fastcgi_store не всегда сохранял файлы.
+
+ *) Исправление: в гео-диапазонах.
+
+ *) Исправление: ошибки выделения больших блоков в разделяемой памяти,
+ если nginx был собран без отладки.
+ Спасибо Андрею Квасову.
+
+
+Изменения в nginx 0.7.31 19.01.2009
+
+ *) Изменение: теперь директива try_files проверяет только файлы,
+ игнорируя каталоги.
+
+ *) Добавление: директива fastcgi_split_path_info.
+
+ *) Исправления в поддержке строки "Expect" в заголовке запроса.
+
+ *) Исправления в гео-диапазонах.
+
+ *) Исправление: при отсутствии ответа ngx_http_memcached_module
+ возвращал в теле ответа строку "END" вместо 404-ой страницы по
+ умолчанию; ошибка появилась в 0.7.18.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: при проксировании SMTP nginx выдавал сообщение
+ "250 2.0.0 OK" вместо "235 2.0.0 OK"; ошибка появилась в 0.7.22.
+ Спасибо Максиму Дунину.
+
+
+Изменения в nginx 0.7.30 24.12.2008
+
+ *) Исправление: в рабочем процессе происходил segmentation fault, если в
+ директивах fastcgi_pass или proxy_pass использовались переменные и
+ имя хоста должно было резолвиться; ошибка появилась в 0.7.29.
+
+
+Изменения в nginx 0.7.29 24.12.2008
+
+ *) Исправление: директивы fastcgi_pass и proxy_pass не поддерживали
+ переменные при использовании unix domain сокетов.
+
+ *) Исправления в обработке подзапросов; ошибки появились в 0.7.25.
+
+ *) Исправление: ответ "100 Continue" выдавался для запросов версии
+ HTTP/1.0;
+ Спасибо Максиму Дунину.
+
+ *) Исправление: в выделении памяти в модуле ngx_http_gzip_filter_module
+ под Cygwin.
+
+
+Изменения в nginx 0.7.28 22.12.2008
+
+ *) Изменение: в выделении памяти в модуле ngx_http_gzip_filter_module.
+
+ *) Изменение: значения по умолчанию для директивы gzip_buffers изменены
+ с 4 4k/8k на 32 4k или 16 8k.
+
+
+Изменения в nginx 0.7.27 15.12.2008
+
+ *) Добавление: директива try_files.
+
+ *) Добавление: директива fastcgi_pass поддерживает переменные.
+
+ *) Добавление: теперь директива geo может брать адрес из переменной.
+ Спасибо Андрею Нигматулину.
+
+ *) Добавление: теперь модификатор location'а можно указывать без пробела
+ перед названием.
+
+ *) Добавление: переменная $upstream_response_length.
+
+ *) Исправление: теперь директива add_header не добавляет пустое
+ значение.
+
+ *) Исправление: при запросе файла нулевой длины nginx закрывал
+ соединение, ничего не передав; ошибка появилась в 0.7.25.
+
+ *) Исправление: метод MOVE не мог перемещать файл в несуществующий
+ каталог.
+
+ *) Исправление: если в сервере не был описан ни один именованный
+ location, но такой location использовался в директиве error_page, то
+ в рабочем процессе происходил segmentation fault.
+ Спасибо Сергею Боченкову.
+
+
+Изменения в nginx 0.7.26 08.12.2008
+
+ *) Исправление: в обработке подзапросов; ошибка появилась в 0.7.25.
+
+
+Изменения в nginx 0.7.25 08.12.2008
+
+ *) Изменение: в обработке подзапросов.
+
+ *) Изменение: теперь разрешаются POST'ы без строки "Content-Length" в
+ заголовке запроса.
+
+ *) Исправление: теперь директивы limit_req и limit_conn указывают
+ причину запрета запроса.
+
+ *) Исправление: в параметре delete директивы geo.
+
+
+Изменения в nginx 0.7.24 01.12.2008
+
+ *) Добавление: директива if_modified_since.
+
+ *) Исправление: nginx не обрабатывал ответ FastCGI-сервера, если перед
+ ответом сервер передавал много сообщений в stderr.
+
+ *) Исправление: переменные "$cookie_..." не работали в SSI and в
+ перловом модуле.
+
+
+Изменения в nginx 0.7.23 27.11.2008
+
+ *) Добавление: параметры delete и ranges в директиве geo.
+
+ *) Добавление: ускорение загрузки geo-базы с большим числом значений.
+
+ *) Добавление: уменьшение памяти, необходимой для загрузки geo-базы.
+
+
+Изменения в nginx 0.7.22 20.11.2008
+
+ *) Добавление: параметр none в директиве smtp_auth.
+ Спасибо Максиму Дунину.
+
+ *) Добавление: переменные "$cookie_...".
+
+ *) Исправление: директива directio не работала с файловой системой XFS.
+
+ *) Исправление: resolver не понимал большие DNS-ответы.
+ Спасибо Zyb.
+
+
+Изменения в nginx 0.7.21 11.11.2008
+
+ *) Изменения в модуле ngx_http_limit_req_module.
+
+ *) Добавление: поддержка EXSLT в модуле ngx_http_xslt_module.
+ Спасибо Денису Латыпову.
+
+ *) Изменение: совместимость с glibc 2.3.
+ Спасибо Eric Benson и Максиму Дунину.
+
+ *) Исправление: nginx не запускался на MacOSX 10.4 и более ранних;
+ ошибка появилась в 0.7.6.
+
+
+Изменения в nginx 0.7.20 10.11.2008
+
+ *) Изменения в модуле ngx_http_gzip_filter_module.
+
+ *) Добавление: модуль ngx_http_limit_req_module.
+
+ *) Исправление: на платформах sparc и ppc рабочие процессы могли
+ выходить по сигналу SIGBUS; ошибка появилась в 0.7.3.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: директивы вида "proxy_pass http://host/some:uri" не
+ работали; ошибка появилась в 0.7.12.
+
+ *) Исправление: при использовании HTTPS запросы могли завершаться с
+ ошибкой "bad write retry".
+
+ *) Исправление: модуль ngx_http_secure_link_module не работал внутри
+ location'ов с именами меньше 3 символов.
+
+ *) Исправление: переменная $server_addr могла не иметь значения.
+
+
+Изменения в nginx 0.7.19 13.10.2008
+
+ *) Исправление: обновление номера версии.
+
+
+Изменения в nginx 0.7.18 13.10.2008
+
+ *) Изменение: директива underscores_in_headers; теперь nginx по
+ умолчанию не разрешает подчёркивания в именах строк в заголовке
+ запроса клиента.
+
+ *) Добавление: модуль ngx_http_secure_link_module.
+
+ *) Добавление: директива real_ip_header поддерживает любой заголовок.
+
+ *) Добавление: директива log_subrequest.
+
+ *) Добавление: переменная $realpath_root.
+
+ *) Добавление: параметры http_502 и http_504 в директиве
+ proxy_next_upstream.
+
+ *) Исправление: параметр http_503 в директивах proxy_next_upstream или
+ fastcgi_next_upstream не работал.
+
+ *) Исправление: nginx мог выдавать строку "Transfer-Encoding: chunked"
+ для запросов HEAD.
+
+ *) Исправление: теперь accept-лимит зависит от числа worker_connections.
+
+
+Изменения в nginx 0.7.17 15.09.2008
+
+ *) Добавление: директива directio теперь работает на Linux.
+
+ *) Добавление: переменная $pid.
+
+ *) Исправление: оптимизация directio, появившаяся в 0.7.15, не работала
+ при использовании open_file_cache.
+
+ *) Исправление: access_log с переменными не работал на Linux; ошибка
+ появилась в 0.7.7.
+
+ *) Исправление: модуль ngx_http_charset_module не понимал название
+ кодировки в кавычках, полученное от бэкенда.
+
+
+Изменения в nginx 0.7.16 08.09.2008
+
+ *) Исправление: nginx не собирался на 64-битных платформах; ошибка
+ появилась в 0.7.15.
+
+
+Изменения в nginx 0.7.15 08.09.2008
+
+ *) Добавление: модуль ngx_http_random_index_module.
+
+ *) Добавление: директива directio оптимизирована для запросов файлов,
+ начинающихся с произвольной позиции.
+
+ *) Добавление: директива directio при необходимости запрещает
+ использование sendfile.
+
+ *) Добавление: теперь nginx разрешает подчёркивания в именах строк в
+ заголовке запроса клиента.
+
+
+Изменения в nginx 0.7.14 01.09.2008
+
+ *) Изменение: теперь директивы ssl_certificate и ssl_certificate_key не
+ имеют значений по умолчанию.
+
+ *) Добавление: директива listen поддерживает параметр ssl.
+
+ *) Добавление: теперь при переконфигурации nginx учитывает изменение
+ временной зоны на FreeBSD и Linux.
+
+ *) Исправление: параметры директивы listen, такие как backlog, rcvbuf и
+ прочие, не устанавливались, если сервером по умолчанию был не первый
+ сервер.
+
+ *) Исправление: при использовании в качестве аргументов части URI,
+ выделенного с помощью директивы rewrite, эти аргументы не
+ экранировались.
+
+ *) Исправление: улучшения тестирования правильности конфигурационного
+ файла.
+
+
+Изменения в nginx 0.7.13 26.08.2008
+
+ *) Исправление: nginx не собирался на Linux и Solaris; ошибка появилась
+ в 0.7.12.
+
+
+Изменения в nginx 0.7.12 26.08.2008
+
+ *) Добавление: директива server_name поддерживает пустое имя "".
+
+ *) Добавление: директива gzip_disable поддерживает специальную маску
+ msie6.
+
+ *) Исправление: при использовании параметра max_fails=0 в upstream'е с
+ несколькими серверами рабочий процесс выходил по сигналу SIGFPE.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: при перенаправлении запроса с помощью директивы
+ error_page терялось тело запроса.
+
+ *) Исправление: при перенаправлении запроса с методом HEAD с помощью
+ директивы error_page возвращался полный ответ.
+
+ *) Исправление: метод $r->header_in() не возвращал значения строк
+ "Host", "User-Agent", и "Connection" из заголовка запроса; ошибка
+ появилась в 0.7.0.
+
+
+Изменения в nginx 0.7.11 18.08.2008
+
+ *) Изменение: теперь ngx_http_charset_module по умолчанию не работает
+ MIME-типом text/css.
+
+ *) Добавление: теперь nginx возвращает код 405 для метода POST при
+ запросе статического файла, только если файл существует.
+
+ *) Добавление: директива proxy_ssl_session_reuse.
+
+ *) Исправление: после перенаправления запроса с помощью
+ "X-Accel-Redirect" директива proxy_pass без URI могла использовать
+ оригинальный запрос.
+
+ *) Исправление: если у каталога были права доступа только на поиск
+ файлов и первый индексный файл отсутствовал, то nginx возвращал
+ ошибку 500.
+
+ *) Исправление: ошибок во вложенных location'ах; ошибки появились в
+ 0.7.1.
+
+
+Изменения в nginx 0.7.10 13.08.2008
+
+ *) Исправление: ошибок в директивах addition_types, charset_types,
+ gzip_types, ssi_types, sub_filter_types и xslt_types; ошибки
+ появились в 0.7.9.
+
+ *) Исправление: рекурсивной error_page для 500 ошибки.
+
+ *) Исправление: теперь модуль ngx_http_realip_module устанавливает адрес
+ не для всего keepalive соединения, а для каждого запроса по этому
+ соединению.
+
+
+Изменения в nginx 0.7.9 12.08.2008
+
+ *) Изменение: теперь ngx_http_charset_module по умолчанию работает со
+ следующими MIME-типами: text/html, text/css, text/xml, text/plain,
+ text/vnd.wap.wml, application/x-javascript и application/rss+xml.
+
+ *) Добавление: директивы charset_types и addition_types.
+
+ *) Добавление: теперь директивы gzip_types, ssi_types и sub_filter_types
+ используют хэш.
+
+ *) Добавление: модуль ngx_cpp_test_module.
+
+ *) Добавление: директива expires поддерживает суточное время.
+
+ *) Добавление: улучшения и исправления в модуле ngx_http_xslt_module.
+ Спасибо Денису Латыпову и Максиму Дунину.
+
+ *) Исправление: директива log_not_found не работала при поиске индексных
+ файлов.
+
+ *) Исправление: HTTPS-соединения могли зависнуть, если использовались
+ методы kqueue, epoll, rtsig или eventport; ошибка появилась в 0.7.7.
+
+ *) Исправление: если в директивах server_name, valid_referers и map
+ использовалась маска вида "*.domain.tld" и при этом полное имя вида
+ "domain.tld" не было описано, то это имя попадало под маску; ошибка
+ появилась в 0.3.18.
+
+
+Изменения в nginx 0.7.8 04.08.2008
+
+ *) Добавление: модуль ngx_http_xslt_module.
+
+ *) Добавление: переменные "$arg_...".
+
+ *) Добавление: поддержка directio в Solaris.
+ Спасибо Ivan Debnar.
+
+ *) Исправление: теперь, если FastCGI-сервер присылает строку "Location"
+ в заголовке ответа без строки статуса, то nginx использует код
+ статуса 302.
+ Спасибо Максиму Дунину.
+
+
+Изменения в nginx 0.7.7 30.07.2008
+
+ *) Изменение: теперь ошибка EAGAIN при вызове connect() не считается
+ временной.
+
+ *) Изменение: значением переменной $ssl_client_cert теперь является
+ сертификат, перед каждой строкой которого, кроме первой, вставляется
+ символ табуляции; неизменённый сертификат доступен через переменную
+ $ssl_client_raw_cert.
+
+ *) Добавление: параметр ask директивы ssl_verify_client.
+
+ *) Добавление: улучшения в обработке byte-range.
+ Спасибо Максиму Дунину.
+
+ *) Добавление: директива directio.
+ Спасибо Jiang Hong.
+
+ *) Добавление: поддержка sendfile() в MacOSX 10.5.
+
+ *) Исправление: в MacOSX и Cygwin при проверке location'ов теперь
+ делается сравнение без учёта регистра символов; однако, сравнение
+ ограничено только однобайтными locale'ями.
+
+ *) Исправление: соединения почтового прокси-сервера зависали в режиме
+ SSL, если использовались методы select, poll или /dev/poll.
+
+ *) Исправление: ошибки при использовании кодировки UTF-8 в
+ ngx_http_autoindex_module.
+
+
+Изменения в nginx 0.7.6 07.07.2008
+
+ *) Исправление: теперь при использовании переменных в директиве
+ access_log всегда проверяется существовании root'а для запроса.
+
+ *) Исправление: модуль ngx_http_flv_module не поддерживал несколько
+ значений в аргументах запроса.
+
+
+Изменения в nginx 0.7.5 01.07.2008
+
+ *) Исправления в поддержке переменных в директиве access_log; ошибки
+ появились в 0.7.4.
+
+ *) Исправление: nginx не собирался с параметром
+ --without-http_gzip_module; ошибка появилась в 0.7.3.
+ Спасибо Кириллу Коринскому.
+
+ *) Исправление: при совместном использовании sub_filter и SSI ответы
+ могли передаваться неверно.
+
+
+Изменения в nginx 0.7.4 30.06.2008
+
+ *) Добавление: директива access_log поддерживает переменные.
+
+ *) Добавление: директива open_log_file_cache.
+
+ *) Добавление: ключ -g.
+
+ *) Добавление: поддержка строки "Expect" в заголовке запроса.
+
+ *) Исправление: большие включения в SSI могли передавались не полностью.
+
+
+Изменения в nginx 0.7.3 23.06.2008
+
+ *) Изменение: MIME-тип для расширения rss изменён на
+ "application/rss+xml".
+
+ *) Изменение: теперь директива "gzip_vary on" выдаёт строку
+ "Vary: Accept-Encoding" в заголовке ответа и для несжатых ответов.
+
+ *) Добавление: теперь при использовании протокола "https://" в директиве
+ rewrite автоматически делается редирект.
+
+ *) Исправление: директива proxy_pass не работала с протоколом HTTPS;
+ ошибка появилась в 0.6.9.
+
+
+Изменения в nginx 0.7.2 16.06.2008
+
+ *) Добавление: теперь nginx поддерживает шифры с обменом EDH-ключами.
+
+ *) Добавление: директива ssl_dhparam.
+
+ *) Добавление: переменная $ssl_client_cert.
+ Спасибо Manlio Perillo.
+
+ *) Исправление: после изменения URI с помощью директивы rewrite nginx не
+ искал новый location; ошибка появилась в 0.7.1.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: nginx не собирался без библиотеки PCRE; ошибка появилась
+ в 0.7.1.
+
+ *) Исправление: при редиректе запроса к каталогу с добавлением слэша
+ nginx не добавлял аргументы из оригинального запроса.
+
+
+Изменения в nginx 0.7.1 26.05.2008
+
+ *) Изменение: теперь поиск location'а делается с помощью дерева.
+
+ *) Изменение: директива optimize_server_names упразднена в связи с
+ появлением директивы server_name_in_redirect.
+
+ *) Изменение: некоторые давно устаревшие директивы больше не
+ поддерживаются.
+
+ *) Изменение: параметр "none" в директиве ssl_session_cache; теперь этот
+ параметр используется по умолчанию.
+ Спасибо Rob Mueller.
+
+ *) Исправление: рабочие процессы могли не реагировать на сигналы
+ переконфигурации и ротации логов.
+
+ *) Исправление: nginx не собирался на последних Fedora 9 Linux.
+ Спасибо Roxis.
+
+
+Изменения в nginx 0.7.0 19.05.2008
+
+ *) Изменение: теперь символы 0x00-0x1F, '"' и '\' в access_log
+ записываются в виде \xXX.
+ Спасибо Максиму Дунину.
+
+ *) Изменение: теперь nginx разрешает несколько строк "Host" в заголовке
+ запроса.
+
+ *) Добавление: директива expires поддерживает флаг modified.
+
+ *) Добавление: переменные $uid_got и $uid_set можно использовать на
+ любой стадии обработки запроса.
+
+ *) Добавление: переменная $hostname.
+ Спасибо Андрею Нигматулину.
+
+ *) Добавление: поддержка DESTDIR.
+ Спасибо Todd A. Fisher и Andras Voroskoi.
+
+ *) Исправление: при использовании keepalive на Linux в рабочем процессе
+ мог произойти segmentation fault.
+
+
+Изменения в nginx 0.6.31 12.05.2008
+
+ *) Исправление: nginx не обрабатывал ответ FastCGI-сервера, если строка
+ заголовка ответа была в конце записи FastCGI; ошибка появилась в
+ 0.6.2.
+ Спасибо Сергею Серову.
+
+ *) Исправление: при удалении файла и использовании директивы
+ open_file_cache_errors off в рабочем процессе мог произойти
+ segmentation fault.
+
+
+Изменения в nginx 0.6.30 29.04.2008
+
+ *) Изменение: теперь, если маске, заданной в директиве include, не
+ соответствует ни один файл, то nginx не выдаёт ошибку.
+
+ *) Добавление: теперь время в директивах можно задавать без пробела,
+ например, "1h50m".
+
+ *) Исправление: утечек памяти, если директива ssl_verify_client имела
+ значение on.
+ Спасибо Chavelle Vincent.
+
+ *) Исправление: директива sub_filter могла вставлять заменяемый текст в
+ вывод.
+
+ *) Исправление: директива error_page не воспринимала параметры в
+ перенаправляемом URI.
+
+ *) Исправление: теперь при сборке с Cygwin nginx всегда открывает файлы
+ в бинарном режиме.
+
+ *) Исправление: nginx не собирался под OpenBSD; ошибка появилась в
+ 0.6.15.
+
+
+Изменения в nginx 0.6.29 18.03.2008
+
+ *) Добавление: модуль ngx_google_perftools_module.
+
+ *) Исправление: модуль ngx_http_perl_module не собирался на 64-битных
+ платформах; ошибка появилась в 0.6.27.
+
+
+Изменения в nginx 0.6.28 13.03.2008
+
+ *) Исправление: метод rtsig не собирался; ошибка появилась в 0.6.27.
+
+
+Изменения в nginx 0.6.27 12.03.2008
+
+ *) Изменение: теперь на Linux 2.6.18+ по умолчанию не собирается метод
+ rtsig.
+
+ *) Изменение: теперь при перенаправлении запроса в именованный location
+ с помощью директивы error_page метод запроса не изменяется.
+
+ *) Добавление: директивы resolver и resolver_timeout в SMTP
+ прокси-сервере.
+
+ *) Добавление: директива post_action поддерживает именованные
+ location'ы.
+
+ *) Исправление: при перенаправлении запроса из location'а c обработчиком
+ proxy, FastCGI или memcached в именованный location со статическим
+ обработчиком в рабочем процессе происходил segmentation fault.
+
+ *) Исправление: браузеры не повторяли SSL handshake, если при первом
+ handshake не оказалось правильного клиентского сертификата.
+ Спасибо Александру Инюхину.
+
+ *) Исправление: при перенаправлении ошибок 495-497 с помощью директивы
+ error_page без изменения кода ошибки nginx пытался выделить очень
+ много памяти.
+
+ *) Исправление: утечки памяти в долгоживущих небуфферизированных
+ соединениях.
+
+ *) Исправление: утечки памяти в resolver'е.
+
+ *) Исправление: при перенаправлении запроса из location'а c обработчиком
+ proxy в другой location с обработчиком proxy в рабочем процессе
+ происходил segmentation fault.
+
+ *) Исправление: ошибки в кэшировании переменных $proxy_host и
+ $proxy_port.
+ Спасибо Сергею Боченкову.
+
+ *) Исправление: директива proxy_pass с переменными использовала порт,
+ описанной в другой директиве proxy_pass без переменных, но с таким же
+ именем хоста.
+ Спасибо Сергею Боченкову.
+
+ *) Исправление: во время переконфигурации на некоторых 64-битном
+ платформах в лог записывался alert "sendmsg() failed (9: Bad file
+ descriptor)".
+
+ *) Исправление: при повторном использовании в SSI пустого block'а в
+ качестве заглушки в рабочем процессе происходил segmentation fault.
+
+ *) Исправление: ошибки при копировании части URI, содержащего
+ экранированные символы, в аргументы.
+
+
+Изменения в nginx 0.6.26 11.02.2008
+
+ *) Исправление: директивы proxy_store и fastcgi_store не проверяли длину
+ ответа.
+
+ *) Исправление: при использовании большого значения в директиве expires
+ в рабочем процессе происходил segmentation fault.
+ Спасибо Joaquin Cuenca Abela.
+
+ *) Исправление: nginx неверно определял длину строки кэша на Pentium 4.
+ Спасибо Геннадию Махомеду.
+
+ *) Исправление: в проксированных подзапросах и подзапросах к
+ FastCGI-серверу вместо метода GET использовался оригинальный метод
+ клиента.
+
+ *) Исправление: утечки сокетов в режиме HTTPS при использовании
+ отложенного accept'а.
+ Спасибо Ben Maurer.
+
+ *) Исправление: nginx выдавал ошибочное сообщение "SSL_shutdown() failed
+ (SSL: )"; ошибка появилась в 0.6.23.
+
+ *) Исправление: при использовании HTTPS запросы могли завершаться с
+ ошибкой "bad write retry"; ошибка появилась в 0.6.23.
+
+
+Изменения в nginx 0.6.25 08.01.2008
+
+ *) Изменение: вместо специального параметра "*" в директиве server_name
+ теперь используется директива server_name_in_redirect.
+
+ *) Изменение: в качестве основного имени в директиве server_name теперь
+ можно использовать имена с масками и регулярными выражениями.
+
+ *) Изменение: директива satisfy_any заменена директивой satisfy.
+
+ *) Изменение: после переконфигурации старые рабочие процесс могли сильно
+ нагружать процессор при запуске под Linux OpenVZ.
+
+ *) Добавление: директива min_delete_depth.
+
+ *) Исправление: методы COPY и MOVE не работали с одиночными файлами.
+
+ *) Исправление: модуль ngx_http_gzip_static_module не позволял работать
+ модулю ngx_http_dav_module; ошибка появилась в 0.6.23.
+
+ *) Исправление: утечки сокетов в режиме HTTPS при использовании
+ отложенного accept'а.
+ Спасибо Ben Maurer.
+
+ *) Исправление: nginx не собирался без библиотеки PCRE; ошибка появилась
+ в 0.6.23.
+
+
+Изменения в nginx 0.6.24 27.12.2007
+
+ *) Исправление: при использовании HTTPS в рабочем процессе мог произойти
+ segmentation fault; ошибка появилась в 0.6.23.
+
+
+Изменения в nginx 0.6.23 27.12.2007
+
+ *) Изменение: параметр "off" в директиве ssl_session_cache; теперь этот
+ параметр используется по умолчанию.
+
+ *) Изменение: директива open_file_cache_retest переименована в
+ open_file_cache_valid.
+
+ *) Добавление: директива open_file_cache_min_uses.
+
+ *) Добавление: модуль ngx_http_gzip_static_module.
+
+ *) Добавление: директива gzip_disable.
+
+ *) Добавление: директиву memcached_pass можно использовать внутри блока
+ if.
+
+ *) Исправление: если внутри одного location'а использовались директивы
+ "memcached_pass" и "if", то в рабочем процессе происходил
+ segmentation fault.
+
+ *) Исправление: если при использовании директивы satisfy_any on" были
+ заданы директивы не всех модулей доступа, то заданные директивы не
+ проверялись.
+
+ *) Исправление: параметры, заданные регулярным выражением в директиве
+ valid_referers, не наследовалась с предыдущего уровня.
+
+ *) Исправление: директива post_action не работала, если запрос
+ завершался с кодом 499.
+
+ *) Исправление: оптимизация использования 16K буфера для SSL-соединения.
+ Спасибо Ben Maurer.
+
+ *) Исправление: STARTTLS в режиме SMTP не работал.
+ Спасибо Олегу Мотиенко.
+
+ *) Исправление: при использовании HTTPS запросы могли завершаться с
+ ошибкой "bad write retry"; ошибка появилась в 0.5.13.
+
+
+Изменения в nginx 0.6.22 19.12.2007
+
+ *) Изменение: теперь все методы модуля ngx_http_perl_module возвращают
+ значения, скопированные в память, выделенную perl'ом.
+
+ *) Исправление: если nginx был собран с модулем ngx_http_perl_module,
+ использовался perl до версии 5.8.6 и perl поддерживал потоки, то во
+ время переконфигурации основной процесс аварийно выходил; ошибка
+ появилась в 0.5.9.
+ Спасибо Борису Жмурову.
+
+ *) Исправление: в методы модуля ngx_http_perl_module могли передаваться
+ неверные результаты выделения в регулярных выражениях.
+
+ *) Исправление: если метод $r->has_request_body() вызывался для запроса,
+ у которого небольшое тело запроса было уже полностью получено, то в
+ рабочем процессе происходил segmentation fault.
+
+ *) Исправление: large_client_header_buffers не освобождались перед
+ переходом в состояние keep-alive.
+ Спасибо Олександру Штепе.
+
+ *) Исправление: в переменной $upstream_addr не записывался последний
+ адрес; ошибка появилась в 0.6.18.
+
+ *) Исправление: директива fastcgi_catch_stderr не возвращала ошибку;
+ теперь она возвращает ошибку 502, которую можно направить на
+ следующий сервер с помощью "fastcgi_next_upstream invalid_header".
+
+ *) Исправление: при использовании директивы fastcgi_catch_stderr в
+ основном процессе происходил segmentation fault; ошибка появилась в
+ 0.6.10.
+ Спасибо Manlio Perillo.
+
+
+Изменения в nginx 0.6.21 03.12.2007
+
+ *) Изменение: если в значениях переменных директивы proxy_pass
+ используются только IP-адреса, то указывать resolver не нужно.
+
+ *) Исправление: при использовании директивы proxy_pass c URI-частью в
+ рабочем процессе мог произойти segmentation fault; ошибка появилась в
+ 0.6.19.
+
+ *) Исправление: если resolver использовался на платформах, не
+ поддерживающих метод kqueue, то nginx выдавал alert "name is out of
+ response".
+ Спасибо Андрею Нигматулину.
+
+ *) Исправление: При использовании переменной $server_protocol в
+ FastCGI-параметрах и запросе, длина которого была близка к значению
+ директивы client_header_buffer_size, nginx выдавал alert "fastcgi:
+ the request record is too big".
+
+ *) Исправление: при обычном запросе версии HTTP/0.9 к HTTPS серверу
+ nginx возвращал обычный ответ.
+
+
+Изменения в nginx 0.6.20 28.11.2007
+
+ *) Исправление: при использовании директивы proxy_pass c URI-частью в
+ рабочем процессе мог произойти segmentation fault; ошибка появилась в
+ 0.6.19.
+
+
+Изменения в nginx 0.6.19 27.11.2007
+
+ *) Исправление: версия 0.6.18 не собиралась.
+
+
+Изменения в nginx 0.6.18 27.11.2007
+
+ *) Изменение: теперь модуль ngx_http_userid_module в поле куки с номером
+ процесса добавляет микросекунды на время старта.
+
+ *) Изменение: в error_log теперь записывается полная строка запроса
+ вместо только URI.
+
+ *) Добавление: директива proxy_pass поддерживает переменные.
+
+ *) Добавление: директивы resolver и resolver_timeout.
+
+ *) Добавление: теперь директива "add_header last-modified ''" удаляет в
+ заголовке ответа строку "Last-Modified".
+
+ *) Исправление: директива limit_rate не позволяла передавать на полной
+ скорости, даже если был указан очень большой лимит.
+
+
+Изменения в nginx 0.6.17 15.11.2007
+
+ *) Добавление: поддержка строки "If-Range" в заголовке запроса.
+ Спасибо Александру Инюхину.
+
+ *) Исправление: при использовании директивы msie_refresh повторно
+ экранировались уже экранированные символы; ошибка появилась в 0.6.4.
+
+ *) Исправление: директива autoindex не работала при использовании "alias
+ /".
+
+ *) Исправление: при использовании подзапросов в рабочем процессе мог
+ произойти segmentation fault.
+
+ *) Исправление: при использовании SSL и gzip большие ответы могли
+ передаваться не полностью.
+
+ *) Исправление: если ответ проксированного сервера был версии HTTP/0.9,
+ то переменная $status была равна 0.
+
+
+Изменения в nginx 0.6.16 29.10.2007
+
+ *) Изменение: теперь на Linux используется uname(2) вместо procfs.
+ Спасибо Илье Новикову.
+
+ *) Исправление: если в директиве error_page использовался символ "?", то
+ он экранировался при проксировании запроса; ошибка появилась в
+ 0.6.11.
+
+ *) Исправление: совместимость с mget.
+
+
+Изменения в nginx 0.6.15 22.10.2007
+
+ *) Добавление: совместимость с Cygwin.
+ Спасибо Владимиру Кутакову.
+
+ *) Добавление: директива merge_slashes.
+
+ *) Добавление: директива gzip_vary.
+
+ *) Добавление: директива server_tokens.
+
+ *) Исправление: nginx не раскодировал URI в команде SSI include.
+
+ *) Исправление: при использовании переменной в директивах charset или
+ source_charset на старте или во время переконфигурации происходил
+ segmentation fault,
+
+ *) Исправление: nginx возвращал ошибку 400 на запросы вида
+ "GET http://www.domain.com HTTP/1.0".
+ Спасибо James Oakley.
+
+ *) Исправление: после перенаправления запроса с телом запроса с помощью
+ директивы error_page nginx пытался снова прочитать тело запроса;
+ ошибка появилась в 0.6.7.
+
+ *) Исправление: в рабочем процессе происходил segmentation fault, если у
+ сервера, обрабатывающему запрос, не был явно определён server_name;
+ ошибка появилась в 0.6.7.
+
+
+Изменения в nginx 0.6.14 15.10.2007
+
+ *) Изменение: теперь по умолчанию команда SSI echo использует
+ кодирование entity.
+
+ *) Добавление: параметр encoding в команде SSI echo.
+
+ *) Добавление: директиву access_log можно использовать внутри блока
+ limit_except.
+
+ *) Исправление: если все сервера апстрима оказывались недоступными, то
+ до восстановления работоспособности у всех серверов вес становился
+ равным одному; ошибка появилась в 0.6.6.
+
+ *) Исправление: при использовании переменных $date_local и $date_gmt вне
+ модуля ngx_http_ssi_filter_module в рабочем процессе происходил
+ segmentation fault.
+
+ *) Исправление: при использовании включённом отладочном логе в рабочем
+ процессе мог произойти segmentation fault.
+ Спасибо Андрею Нигматулину.
+
+ *) Исправление: ngx_http_memcached_module не устанавливал
+ $upstream_response_time.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: рабочий процесс мог зациклиться при использовании
+ memcached.
+
+ *) Исправление: nginx распознавал параметры "close" и "keep-alive" в
+ строке "Connection" в заголовке запроса только, если они были в
+ нижнем регистре; ошибка появилась в 0.6.11.
+
+ *) Исправление: sub_filter не работал с пустой строкой замены.
+
+ *) Исправление: в парсинге sub_filter.
+
+
+Изменения в nginx 0.6.13 24.09.2007
+
+ *) Исправление: nginx не закрывал файл каталога для запроса HEAD, если
+ использовался autoindex
+ Спасибо Arkadiusz Patyk.
+
+
+Изменения в nginx 0.6.12 21.09.2007
+
+ *) Изменение: почтовый прокси-сервер разделён на три модуля: pop3, imap
+ и smtp.
+
+ *) Добавление: параметры конфигурации --without-mail_pop3_module,
+ --without-mail_imap_module и --without-mail_smtp_module.
+
+ *) Добавление: директивы smtp_greeting_delay и smtp_client_buffer модуля
+ ngx_mail_smtp_module.
+
+ *) Исправление: wildcard в конце имени сервера не работали; ошибка
+ появилась в 0.6.9.
+
+ *) Исправление: при использовании разделяемой библиотеки PCRE,
+ расположенной в нестандартном месте, nginx не запускался на Solaris.
+
+ *) Исправление: директивы proxy_hide_header и fastcgi_hide_header не
+ скрывали строки заголовка ответа с именем больше 32 символов.
+ Спасибо Manlio Perillo.
+
+
+Изменения в nginx 0.6.11 11.09.2007
+
+ *) Исправление: счётчик активных соединений всегда рос при использовании
+ почтового прокси-сервера.
+
+ *) Исправление: если бэкенд возвращал только заголовок ответа при
+ небуферизированном проксировании, то nginx закрывал соединение с
+ бэкендом по таймауту.
+
+ *) Исправление: nginx не поддерживал несколько строк "Connection" в
+ заголовке запроса.
+
+ *) Исправление: если в сервере апстрима был задан max_fails, то после
+ первой же неудачной попытки вес сервера навсегда становился равным
+ одному; ошибка появилась в 0.6.6.
+
+
+Изменения в nginx 0.6.10 03.09.2007
+
+ *) Добавление: директивы open_file_cache, open_file_cache_retest и
+ open_file_cache_errors.
+
+ *) Исправление: утечки сокетов; ошибка появилась в 0.6.7.
+
+ *) Исправление: В строку заголовка ответа "Content-Type", указанную в
+ методе $r->send_http_header(), не добавлялась кодировка, указанная в
+ директиве charset.
+
+ *) Исправление: при использовании метода /dev/poll в рабочем процессе
+ мог произойти segmentation fault.
+
+
+Изменения в nginx 0.6.9 28.08.2007
+
+ *) Исправление: рабочий процесс мог зациклиться при использовании
+ протокола HTTPS; ошибка появилась в 0.6.7.
+
+ *) Исправление: если сервер слушал на двух адресах или портах, то nginx
+ не запускался при использовании wildcard в конце имени сервера.
+
+ *) Исправление: директива ip_hash могла неверно помечать сервера как
+ нерабочие.
+
+ *) Исправление: nginx не собирался на amd64; ошибка появилась в 0.6.8.
+
+
+Изменения в nginx 0.6.8 20.08.2007
+
+ *) Изменение: теперь nginx пытается установить директивы
+ worker_priority, worker_rlimit_nofile, worker_rlimit_core,
+ worker_rlimit_sigpending без привилегий root'а.
+
+ *) Изменение: теперь nginx экранирует символы пробела и "%" при передаче
+ запроса серверу аутентификации почтового прокси-сервера.
+
+ *) Изменение: теперь nginx экранирует символ "%" в переменной
+ $memcached_key.
+
+ *) Исправление: при указании относительного пути к конфигурационному
+ файлу в качестве параметра ключа -c nginx определял путь относительно
+ конфигурационного префикса; ошибка появилась в 0.6.6.
+
+ *) Исправление: nginx не работал на FreeBSD/sparc64.
+
+
+Изменения в nginx 0.6.7 15.08.2007
+
+ *) Изменение: теперь пути, указанные в директивах include,
+ auth_basic_user_file, perl_modules, ssl_certificate,
+ ssl_certificate_key и ssl_client_certificate, определяются
+ относительно каталога конфигурационного файла nginx.conf, а не
+ относительно префикса.
+
+ *) Изменение: параметр --sysconfdir=PATH в configure упразднён.
+
+ *) Изменение: для обновления на лету версий 0.1.x создан специальный
+ сценарий make upgrade1.
+
+ *) Добавление: директивы server_name и valid_referers поддерживают
+ регулярные выражения.
+
+ *) Добавление: директива server в блоке upstream поддерживает параметр
+ backup.
+
+ *) Добавление: модуль ngx_http_perl_module поддерживает метод
+ $r->discard_request_body.
+
+ *) Добавление: директива "add_header Last-Modified ..." меняет строку
+ "Last-Modified" в заголовке ответа.
+
+ *) Исправление: если на запрос с телом возвращался ответ с кодом HTTP
+ отличным от 200, и после этого запроса соединение переходило в
+ состояние keep-alive, то на следующий запрос nginx возвращал 400.
+
+ *) Исправление: если в директиве auth_http был задан неправильный адрес,
+ то в рабочем процессе происходил segmentation fault.
+
+ *) Исправление: теперь по умолчанию nginx использует значение 511 для
+ listen backlog на всех платформах, кроме FreeBSD.
+ Спасибо Jiang Hong.
+
+ *) Исправление: рабочий процесс мог зациклиться, если server в блоке
+ upstream был помечен как down; ошибка появилась в 0.6.6.
+
+ *) Исправление: sendfilev() в Solaris теперь не используется при
+ передаче тела запроса FastCGI-серверу через unix domain сокет.
+
+
+Изменения в nginx 0.6.6 30.07.2007
+
+ *) Добавление: параметр --sysconfdir=PATH в configure.
+
+ *) Добавление: именованные location'ы.
+
+ *) Добавление: переменную $args можно устанавливать с помощью set.
+
+ *) Добавление: переменная $is_args.
+
+ *) Исправление: равномерное распределение запросов к апстримам с
+ большими весами.
+
+ *) Исправление: если клиент в почтовом прокси-сервере закрывал
+ соединение, то nginx мог не закрывать соединение с бэкендом.
+
+ *) Исправление: при использовании одного хоста в качестве бэкендов для
+ протоколов HTTP и HTTPS без явного указания портов, nginx использовал
+ только один порт - 80 или 443.
+
+ *) Исправление: nginx не собирался на Solaris/amd64 Sun Studio 11 и
+ более ранними версиями; ошибка появилась в 0.6.4.
+
+
+Изменения в nginx 0.6.5 23.07.2007
+
+ *) Добавление: переменная $nginx_version.
+ Спасибо Николаю Гречуху.
+
+ *) Добавление: почтовый прокси-сервер поддерживает AUTHENTICATE в режиме
+ IMAP.
+ Спасибо Максиму Дунину.
+
+ *) Добавление: почтовый прокси-сервер поддерживает STARTTLS в режиме
+ SMTP.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: теперь nginx экранирует пробел в переменной
+ $memcached_key.
+
+ *) Исправление: nginx неправильно собирался Sun Studio на Solaris/amd64.
+ Спасибо Jiang Hong.
+
+ *) Исправление: незначительных потенциальных ошибок.
+ Спасибо Coverity's Scan.
+
+
+Изменения в nginx 0.6.4 17.07.2007
+
+ *) Безопасность: при использовании директивы msie_refresh был возможен
+ XSS.
+ Спасибо Максиму Богуку.
+
+ *) Изменение: директивы proxy_store и fastcgi_store изменены.
+
+ *) Добавление: директивы proxy_store_access и fastcgi_store_access.
+
+ *) Исправление: nginx не работал на Solaris/sparc64, если был собран Sun
+ Studio.
+ Спасибо Андрею Нигматулину.
+
+ *) Изменение: обход ошибки в Sun Studio 12.
+ Спасибо Jiang Hong.
+
+
+Изменения в nginx 0.6.3 12.07.2007
+
+ *) Добавление: директивы proxy_store и fastcgi_store.
+
+ *) Исправление: при использовании директивы auth_http_header в рабочем
+ процессе мог произойти segmentation fault.
+ Спасибо Максиму Дунину.
+
+ *) Исправление: если использовался метод аутентификации CRAM-MD5, но он
+ не был разрешён, то в рабочем процессе происходил segmentation fault.
+
+ *) Исправление: при использовании протокола HTTPS в директиве proxy_pass
+ в рабочем процессе мог произойти segmentation fault.
+
+ *) Исправление: в рабочем процессе мог произойти segmentation fault,
+ если использовался метод eventport.
+
+ *) Исправление: директивы proxy_ignore_client_abort и
+ fastcgi_ignore_client_abort не работали; ошибка появилась в 0.5.13.
+
+
+Изменения в nginx 0.6.2 09.07.2007
+
+ *) Исправление: если заголовок ответа был разделён в FastCGI-записях, то
+ nginx передавал клиенту мусор в таких заголовках.
+
+
+Изменения в nginx 0.6.1 17.06.2007
+
+ *) Исправление: в парсинге SSI.
+
+ *) Исправление: при использовании удалённого подзапроса в SSI
+ последующий подзапрос локального файла мог отдаваться клиенту в
+ неверном порядке.
+
+ *) Исправление: большие включения в SSI, сохранённые во временные файлы,
+ передавались не полностью.
+
+ *) Исправление: значение perl'овой переменной $$ модуля
+ ngx_http_perl_module было равно номеру главного процесса.
+
+
+Изменения в nginx 0.6.0 14.06.2007
+
+ *) Добавление: директивы "server_name", "map", and "valid_referers"
+ поддерживают маски вида "www.example.*".
+
+
+Изменения в nginx 0.5.25 11.06.2007
+
+ *) Исправление: nginx не собирался с параметром
+ --without-http_rewrite_module; ошибка появилась в 0.5.24.
+
+
+Изменения в nginx 0.5.24 06.06.2007
+
+ *) Безопасность: директива ssl_verify_client не работала, если запрос
+ выполнялся по протоколу HTTP/0.9.
+
+ *) Исправление: при использовании сжатия часть ответа могла передаваться
+ несжатой; ошибка появилась в 0.5.23.
+
+
+Изменения в nginx 0.5.23 04.06.2007
+
+ *) Добавление: модуль ngx_http_ssl_module поддерживает расширение TLS
+ Server Name Indication.
+
+ *) Добавление: директива fastcgi_catch_stderr.
+ Спасибо Николаю Гречуху, проект OWOX.
+
+ *) Исправление: на Линуксе в основном процессе происходил segmentation
+ fault, если два виртуальных сервера должны bind()ится к
+ пересекающимся портам.
+
+ *) Исправление: если nginx был собран с модулем ngx_http_perl_module и
+ perl поддерживал потоки, то во время второй переконфигурации
+ выдавались ошибки "panic: MUTEX_LOCK" и "perl_parse() failed".
+
+ *) Исправление: в использовании протокола HTTPS в директиве proxy_pass.
+
+
+Изменения в nginx 0.5.22 29.05.2007
+
+ *) Исправление: большое тело запроса могло не передаваться бэкенду;
+ ошибка появилась в 0.5.21.
+
+
+Изменения в nginx 0.5.21 28.05.2007
+
+ *) Исправление: если внутри сервера описано больше примерно десяти
+ location'ов, то location'ы, заданные с помощью регулярного выражения,
+ могли выполняться не в том, порядке, в каком они описаны.
+
+ *) Исправление: на 64-битной платформе рабочий процесс мог зациклиться,
+ если 33-тий по счёту или последующий бэкенд упал.
+ Спасибо Антону Поварову.
+
+ *) Исправление: при использовании библиотеки PCRE на Solaris/sparc64 мог
+ произойти bus error.
+ Спасибо Андрею Нигматулину.
+
+ *) Исправление: в использовании протокола HTTPS в директиве proxy_pass.
+
+
+Изменения в nginx 0.5.20 07.05.2007
+
+ *) Добавление: директива sendfile_max_chunk.
+
+ *) Добавление: переменные "$http_...", "$sent_http_..." и
+ "$upstream_http_..." можно менять директивой set.
+
+ *) Исправление: при использовании SSI-команды 'if expr="$var = /"' в
+ рабочем процессе мог произойти segmentation fault.
+
+ *) Исправление: завершающая строка multipart range ответа передавалась
+ неверно.
+ Спасибо Evan Miller.
+
+ *) Исправление: nginx не работал на Solaris/sparc64, если был собран Sun
+ Studio.
+ Спасибо Андрею Нигматулину.
+
+ *) Исправление: модуль ngx_http_perl_module не собирался make в Solaris.
+ Спасибо Андрею Нигматулину.
+
+
+Изменения в nginx 0.5.19 24.04.2007
+
+ *) Изменение: значение переменной $request_time теперь записывается с
+ точностью до миллисекунд.
+
+ *) Изменение: метод $r->rflush в модуле ngx_http_perl_module
+ переименован в $r->flush.
+
+ *) Добавление: переменная $upstream_addr.
+
+ *) Добавление: директивы proxy_headers_hash_max_size и
+ proxy_headers_hash_bucket_size.
+ Спасибо Володымыру Костырко.
+
+ *) Исправление: при использовании sendfile и limit_rate на 64-битных
+ платформах нельзя было передавать файлы больше 2G.
+
+ *) Исправление: при использовании sendfile на 64-битном Linux нельзя
+ было передавать файлы больше 2G.
+
+
+Изменения в nginx 0.5.18 19.04.2007
+
+ *) Добавление: модуль ngx_http_sub_filter_module.
+
+ *) Добавление: переменные "$upstream_http_...".
+
+ *) Добавление: теперь переменные $upstream_status и
+ $upstream_response_time содержат данные о всех обращениях к
+ апстримам, сделанным до X-Accel-Redirect.
+
+ *) Исправление: если nginx был собран с модулем ngx_http_perl_module и
+ perl не поддерживал multiplicity, то после первой переконфигурации и
+ после получения любого сигнала в основном процессе происходил
+ segmentation fault; ошибка появилась в 0.5.9.
+
+ *) Исправление: если perl не поддерживал multiplicity, то после
+ переконфигурации перловый код не работал; ошибка появилась в 0.3.38.
+
+
+Изменения в nginx 0.5.17 02.04.2007
+
+ *) Изменение: теперь nginx для метода TRACE всегда возвращает код 405.
+
+ *) Добавление: теперь nginx поддерживает директиву include внутри блока
+ types.
+
+ *) Исправление: использование переменной $document_root в директиве root
+ и alias запрещено: оно вызывало рекурсивное переполнение стека.
+
+ *) Исправление: в использовании протокола HTTPS в директиве proxy_pass.
+
+ *) Исправление: в некоторых случаях некэшируемые переменные (такие, как
+ $uri) возвращали старое закэшированное значение.
+
+
+Изменения в nginx 0.5.16 26.03.2007
+
+ *) Исправление: в качестве ключа для хэша в директиве ip_hash не
+ использовалась сеть класса С.
+ Спасибо Павлу Ярковому.
+
+ *) Исправление: если в строке "Content-Type" в заголовке ответа бэкенда
+ был указан charset и строка завершалась символом ";", то в рабочем
+ процессе мог произойти segmentation fault; ошибка появилась в 0.3.50.
+
+ *) Исправление: ошибки "[alert] zero size buf" при работе с
+ FastCGI-сервером, если тело запроса, записанное во временный файл,
+ было кратно 32K.
+
+ *) Исправление: nginx не собирался на Solaris без параметра
+ --with-debug; ошибка появилась в 0.5.15.
+
+
+Изменения в nginx 0.5.15 19.03.2007
+
+ *) Добавление: почтовый прокси-сервер поддерживает аутентифицированное
+ SMTP-проксирование и директивы smtp_auth, smtp_capablities и xclient.
+ Спасибо Антону Южанинову и Максиму Дунину.
+
+ *) Добавление: теперь keep-alive соединения закрываются сразу же по
+ получении сигнала переконфигурации.
+
+ *) Изменение: директивы imap и auth переименованы соответственно в mail
+ и pop3_auth.
+
+ *) Исправление: если использовался метод аутентификации CRAM-MD5 и не
+ был разрешён метод APOP, то в рабочем процессе происходил
+ segmentation fault.
+
+ *) Исправление: при использовании директивы starttls only в протоколе
+ POP3 nginx разрешал аутентификацию без перехода в режим SSL.
+
+ *) Исправление: рабочие процессы не выходили после переконфигурации и не
+ переоткрывали логи, если использовался метод eventport.
+
+ *) Исправление: при использовании директивы ip_hash рабочий процесс мог
+ зациклиться.
+
+ *) Исправление: теперь nginx не пишет в лог некоторые alert'ы, если
+ используются методы eventport или /dev/poll.
+
+
+Изменения в nginx 0.5.14 23.02.2007
+
+ *) Исправление: nginx игнорировал лишние закрывающие скобки "}" в конце
+ конфигурационного файла.
+
+
+Изменения в nginx 0.5.13 19.02.2007
+
+ *) Добавление: методы COPY и MOVE.
+
+ *) Исправление: модуль ngx_http_realip_module устанавливал мусор для
+ запросов, переданных по keep-alive соединению.
+
+ *) Исправление: nginx не работал на 64-битном big-endian Linux.
+ Спасибо Андрею Нигматулину.
+
+ *) Исправление: при получении слишком длинной команды IMAP/POP3-прокси
+ теперь сразу закрывает соединение, а не по таймауту.
+
+ *) Исправление: если при использовании метода epoll клиент закрывал
+ преждевременно соединение со своей стороны, то nginx закрывал это
+ соединение только по истечении таймаута на передачу.
+
+ *) Исправление: nginx не собирался на платформах, отличных от i386,
+ amd64, sparc и ppc; ошибка появилась в 0.5.8.
+
+
+Изменения в nginx 0.5.12 12.02.2007
+
+ *) Исправление: nginx не собирался на платформах, отличных от i386,
+ amd64, sparc и ppc; ошибка появилась в 0.5.8.
+
+ *) Исправление: при использовании временных файлов в время работы с
+ FastCGI-сервером в рабочем процессе мог произойти segmentation fault;
+ ошибка появилась в 0.5.8.
+
+ *) Исправление: если переменная $fastcgi_script_name записывалась в лог,
+ то в рабочем процессе мог произойти segmentation fault.
+
+ *) Исправление: ngx_http_perl_module не собирался на Solaris.
+
+
+Изменения в nginx 0.5.11 05.02.2007
+
+ *) Добавление: теперь configure определяет библиотеку PCRE в MacPorts.
+ Спасибо Chris McGrath.
+
+ *) Исправление: ответ был неверным, если запрашивалось несколько
+ диапазонов; ошибка появилась в 0.5.6.
+
+ *) Исправление: директива create_full_put_path не могла создавать
+ промежуточные каталоги, если не была установлена директива
+ dav_access.
+ Спасибо Evan Miller.
+
+ *) Исправление: вместо кодов ошибок "400" и "408" в access_log мог
+ записываться код "0".
+
+ *) Исправление: при сборке с оптимизацией -O2 в рабочем процессе мог
+ произойти segmentation fault.
+
+
+Изменения в nginx 0.5.10 26.01.2007
+
+ *) Исправление: во время обновления исполняемого файла новый процесс не
+ наследовал слушающие сокеты; ошибка появилась в 0.5.9.
+
+ *) Исправление: при сборке с оптимизацией -O2 в рабочем процессе мог
+ произойти segmentation fault; ошибка появилась в 0.5.1.
+
+
+Изменения в nginx 0.5.9 25.01.2007
+
+ *) Изменение: модуль ngx_http_memcached_module теперь в качестве ключа
+ использует значение переменной $memcached_key.
+
+ *) Добавление: переменная $memcached_key.
+
+ *) Добавление: параметр clean в директиве client_body_in_file_only.
+
+ *) Добавление: директива env.
+
+ *) Добавление: директива sendfile работает внутри блока if.
+
+ *) Добавление: теперь при ошибке записи в access_log nginx записывает
+ сообщение в error_log, но не чаще одного раза в минуту.
+
+ *) Исправление: директива "access_log off" не всегда запрещала запись в
+ лог.
+
+
+Изменения в nginx 0.5.8 19.01.2007
+
+ *) Исправление: если использовалась директива
+ "client_body_in_file_only on" и тело запроса было небольшое, то мог
+ произойти segmentation fault.
+
+ *) Исправление: происходил segmentation fault, если использовались
+ директивы "client_body_in_file_only on" и
+ "proxy_pass_request_body off" или "fastcgi_pass_request_body off", и
+ делался переход к следующему бэкенду.
+
+ *) Исправление: если при использовании директивы "proxy_buffering off"
+ соединение с клиентом было неактивно, то оно закрывалось по таймауту,
+ заданному директивой send_timeout; ошибка появилась в 0.4.7.
+
+ *) Исправление: если при использовании метода epoll клиент закрывал
+ преждевременно соединение со своей стороны, то nginx закрывал это
+ соединение только по истечении таймаута на передачу.
+
+ *) Исправление: ошибки "[alert] zero size buf" при работе с
+ FastCGI-сервером.
+
+ *) Исправление ошибок в директиве limit_zone.
+
+
+Изменения в nginx 0.5.7 15.01.2007
+
+ *) Добавление: оптимизация использования памяти в ssl_session_cache.
+
+ *) Исправление ошибок в директивах ssl_session_cache и limit_zone.
+
+ *) Исправление: на старте или во время переконфигурации происходил
+ segmentation fault, если директивы ssl_session_cache или limit_zone
+ использовались на 64-битных платформах.
+
+ *) Исправление: при использовании директив add_before_body или
+ add_after_body происходил segmentation fault, если в заголовке ответа
+ нет строки "Content-Type".
+
+ *) Исправление: библиотека OpenSSL всегда собиралась с поддержкой
+ потоков.
+ Спасибо Дену Иванову.
+
+ *) Исправление: совместимость библиотеки PCRE-6.5+ и компилятора icc.
+
+
+Изменения в nginx 0.5.6 09.01.2007
+
+ *) Изменение: теперь модуль ngx_http_index_module игнорирует все методы,
+ кроме GET, HEAD и POST.
+
+ *) Добавление: модуль ngx_http_limit_zone_module.
+
+ *) Добавление: переменная $binary_remote_addr.
+
+ *) Добавление: директивы ssl_session_cache модулей ngx_http_ssl_module и
+ ngx_imap_ssl_module.
+
+ *) Добавление: метод DELETE поддерживает рекурсивное удаление.
+
+ *) Исправление: при использовании $r->sendfile() byte-ranges
+ передавались неверно.
+
+
+Изменения в nginx 0.5.5 24.12.2006
+
+ *) Изменение: ключ -v больше не выводит информацию о компиляторе.
+
+ *) Добавление: ключ -V.
+
+ *) Добавление: директива worker_rlimit_core поддерживает указание
+ размера в K, M и G.
+
+ *) Исправление: модуль nginx.pm теперь может устанавливаться
+ непривилегированным пользователем.
+
+ *) Исправление: при использовании методов $r->request_body или
+ $r->request_body_file мог произойти segmentation fault.
+
+ *) Исправление: ошибок, специфичных для платформы ppc.
+
+
+Изменения в nginx 0.5.4 15.12.2006
+
+ *) Добавление: директиву perl можно использовать внутри блока
+ limit_except.
+
+ *) Исправление: модуль ngx_http_dav_module требовал строку "Date" в
+ заголовке запроса для метода DELETE.
+
+ *) Исправление: при использовании одного параметра в директиве
+ dav_access nginx мог сообщить об ошибке в конфигурации.
+
+ *) Исправление: при использовании переменной $host мог произойти
+ segmentation fault; ошибка появилась в 0.4.14.
+
+
+Изменения в nginx 0.5.3 13.12.2006
+
+ *) Добавление: модуль ngx_http_perl_module поддерживает методы
+ $r->status, $r->log_error и $r->sleep.
+
+ *) Добавление: метод $r->variable поддерживает переменные, неописанные в
+ конфигурации nginx'а.
+
+ *) Исправление: метод $r->has_request_body не работал.
+
+
+Изменения в nginx 0.5.2 11.12.2006
+
+ *) Исправление: если в директивах proxy_pass использовалось имя,
+ указанное в upstream, то nginx пытался найти IP-адрес этого имени;
+ ошибка появилась в 0.5.1.
+
+
+Изменения в nginx 0.5.1 11.12.2006
+
+ *) Исправление: директива post_action могла не работать после неудачного
+ завершения запроса.
+
+ *) Изменение: обход ошибки в Eudora для Mac; ошибка появилась в 0.4.11.
+ Спасибо Bron Gondwana.
+
+ *) Исправление: при указании в директиве fastcgi_pass имени описанного
+ upstream'а выдавалось сообщение "no port in upstream"; ошибка
+ появилась в 0.5.0.
+
+ *) Исправление: если в директивах proxy_pass и fastcgi_pass
+ использовались одинаковых имена серверов, но с разными портами, то
+ эти директивы использовали первый описанный порт; ошибка появилась в
+ 0.5.0.
+
+ *) Исправление: если в директивах proxy_pass и fastcgi_pass
+ использовались unix domain сокеты, то эти директивы использовали
+ первый описанный сокет; ошибка появилась в 0.5.0.
+
+ *) Исправление: ngx_http_auth_basic_module игнорировал пользователя,
+ если он был указан в последней строке файла паролей и после пароля не
+ было перевода строки, возврата каретки или символа ":".
+
+ *) Исправление: переменная $upstream_response_time могла быть равна
+ "0.000", хотя время обработки было больше 1 миллисекунды.
+
+
+Изменения в nginx 0.5.0 04.12.2006
+
+ *) Изменение: параметры в виде "%name" в директиве log_format больше не
+ поддерживаются.
+
+ *) Изменение: директивы proxy_upstream_max_fails,
+ proxy_upstream_fail_timeout, fastcgi_upstream_max_fails, и
+ fastcgi_upstream_fail_timeout, memcached_upstream_max_fails и
+ memcached_upstream_fail_timeout больше не поддерживаются.
+
+ *) Добавление: директива server в блоке upstream поддерживает параметры
+ max_fails, fail_timeout и down.
+
+ *) Добавление: директива ip_hash в блоке upstream.
+
+ *) Добавление: статус WAIT в строке "Auth-Status" в заголовке ответа
+ сервера аутентификации IMAP/POP3 прокси.
+
+ *) Исправление: nginx не собирался на 64-битных платформах; ошибка
+ появилась в 0.4.14.
+
+
+Изменения в nginx 0.4.14 27.11.2006
+
+ *) Добавление: директива proxy_pass_error_message в IMAP/POP3 прокси.
+
+ *) Добавление: теперь configure определяет библиотеку PCRE на FreeBSD,
+ Linux и NetBSD.
+
+ *) Исправление: ngx_http_perl_module не работал с перлом, собранным с
+ поддержкой потоков; ошибка появилась в 0.3.38.
+
+ *) Исправление: ngx_http_perl_module не работал корректно, если перл
+ вызывался рекурсивно.
+
+ *) Исправление: nginx игнорировал имя сервера в строке запроса.
+
+ *) Исправление: если FastCGI сервер передавал много в stderr, то рабочий
+ процесс мог зациклиться.
+
+ *) Исправление: при изменении системного времени переменная
+ $upstream_response_time могла быть отрицательной.
+
+ *) Исправление: при использовании POP3 серверу аутентификации IMAP/POP3
+ прокси не передавался параметр Auth-Login-Attempt.
+
+ *) Исправление: при ошибке соединения с сервером аутентификации
+ IMAP/POP3 прокси мог произойти segmentation fault.
+
+
+Изменения в nginx 0.4.13 15.11.2006
+
+ *) Добавление: директиву proxy_pass можно использовать внутри блока
+ limit_except.
+
+ *) Добавление: директива limit_except поддерживает все WebDAV методы.
+
+ *) Исправление: при использовании директивы add_before_body без
+ директивы add_after_body ответ передавался не полностью.
+
+ *) Исправление: большое тело запроса не принималось, если использовались
+ метод epoll и deferred accept().
+
+ *) Исправление: для ответов модуля ngx_http_autoindex_module не
+ выставлялась кодировка; ошибка появилась в 0.3.50.
+
+ *) Исправление: ошибки "[alert] zero size buf" при работе с
+ FastCGI-сервером;
+
+ *) Исправление: параметр конфигурации --group= игнорировался.
+ Спасибо Thomas Moschny.
+
+ *) Исправление: 50-й подзапрос в SSI ответе не работал; ошибка появилась
+ в 0.3.50.
+
+
+Изменения в nginx 0.4.12 31.10.2006
+
+ *) Добавление: модуль ngx_http_perl_module поддерживает метод
+ $r->variable.
+
+ *) Исправление: при включении в ответ большого статического файла с
+ помощью SSI ответ мог передаваться не полностью.
+
+ *) Исправление: nginx не убирал "#fragment" в URI.
+
+
+Изменения в nginx 0.4.11 25.10.2006
+
+ *) Добавление: POP3 прокси поддерживает AUTH LOIGN PLAIN и CRAM-MD5.
+
+ *) Добавление: модуль ngx_http_perl_module поддерживает метод
+ $r->allow_ranges.
+
+ *) Исправление: при включённой поддержке команды APOP в POP3 прокси
+ могли не работать команды USER/PASS; ошибка появилась в 0.4.10.
+
+
+Изменения в nginx 0.4.10 23.10.2006
+
+ *) Добавление: POP3 прокси поддерживает APOP.
+
+ *) Исправление: при использовании методов select, poll и /dev/poll во
+ время ожидания ответа от сервера аутентификации IMAP/POP3 прокси
+ нагружал процессор.
+
+ *) Исправление: при использовании переменной $server_addr в директиве
+ map мог произойти segmentation fault.
+
+ *) Исправление: модуль ngx_http_flv_module не поддерживал byte ranges
+ для полных ответов; ошибка появилась в 0.4.7.
+
+ *) Исправление: nginx не собирался на Debian amd64; ошибка появилась в
+ 0.4.9.
+
+
+Изменения в nginx 0.4.9 13.10.2006
+
+ *) Добавление: параметр set в команде SSI include.
+
+ *) Добавление: модуль ngx_http_perl_module теперь проверяет версию
+ модуля nginx.pm.
+
+
+Изменения в nginx 0.4.8 11.10.2006
+
+ *) Исправление: если до команды SSI include с параметром wait
+ выполнялась ещё одна команда SSI include, то параметр wait мог не
+ работать.
+
+ *) Исправление: модуль ngx_http_flv_module добавлял FLV-заголовок для
+ полных ответов.
+ Спасибо Алексею Ковырину.
+
+
+Изменения в nginx 0.4.7 10.10.2006
+
+ *) Добавление: модуль ngx_http_flv_module.
+
+ *) Добавление: переменная $request_body_file.
+
+ *) Добавление: директивы charset и source_charset поддерживают
+ переменные.
+
+ *) Исправление: если до команды SSI include с параметром wait
+ выполнялась ещё одна команда SSI include, то параметр wait мог не
+ работать.
+
+ *) Исправление: при использовании директивы "proxy_buffering off" или
+ при работе с memcached соединения могли не закрываться по таймауту.
+
+ *) Исправление: nginx не запускался на 64-битных платформах, отличных от
+ amd64, sparc64 и ppc64.
+
+
+Изменения в nginx 0.4.6 06.10.2006
+
+ *) Исправление: nginx не запускался на 64-битных платформах, отличных от
+ amd64, sparc64 и ppc64.
+
+ *) Исправление: при запросе версии HTTP/1.1 nginx передавал ответ
+ chunk'ами, если длина ответа в методе
+ $r->headers_out("Content-Length", ...) была задана текстовой строкой.
+
+ *) Исправление: после перенаправления ошибки с помощью директивы
+ error_page любая директива модуля ngx_http_rewrite_module возвращала
+ эту ошибку; ошибка появилась в 0.4.4.
+
+
+Изменения в nginx 0.4.5 02.10.2006
+
+ *) Исправление: nginx не собирался на Linux и Solaris; ошибка появилась
+ в 0.4.4.
+
+
+Изменения в nginx 0.4.4 02.10.2006
+
+ *) Добавление: переменная $scheme.
+
+ *) Добавление: директива expires поддерживает параметр max.
+
+ *) Добавление: директива include поддерживает маску "*".
+ Спасибо Jonathan Dance.
+
+ *) Исправление: директива return всегда изменяла код ответа,
+ перенаправленного директивой error_page.
+
+ *) Исправление: происходил segmentation fault, если в методе PUT
+ передавалось тело нулевой длины.
+
+ *) Исправление: при использовании переменных в директиве proxy_redirect
+ редирект изменялся неверно.
+
+
+Изменения в nginx 0.4.3 26.09.2006
+
+ *) Изменение: ошибку 499 теперь нельзя перенаправить с помощью директивы
+ error_page.
+
+ *) Добавление: поддержка Solaris 10 event ports.
+
+ *) Добавление: модуль ngx_http_browser_module.
+
+ *) Исправление: при перенаправлении ошибки 400 проксированному серверу
+ помощью директивы error_page мог произойти segmentation fault.
+
+ *) Исправление: происходил segmentation fault, если в директиве
+ proxy_pass использовался unix domain сокет; ошибка появилась в
+ 0.3.47.
+
+ *) Исправление: SSI не работал с ответами memcached и
+ небуферизированными проксированными ответами.
+
+ *) Изменение: обход ошибки PAUSE hardware capability в Sun Studio.
+
+
+Изменения в nginx 0.4.2 14.09.2006
+
+ *) Исправление: убрана поддержка флага O_NOATIME на Linux; ошибка
+ появилась в 0.4.1.
+
+
+Изменения в nginx 0.4.1 14.09.2006
+
+ *) Исправление: совместимость с DragonFlyBSD.
+ Спасибо Павлу Назарову.
+
+ *) Изменение: обход ошибки в sendfile() в 64-битном Linux при передаче
+ файлов больше 2G.
+
+ *) Добавление: теперь на Linux nginx для статических запросов использует
+ флаг O_NOATIME.
+ Спасибо Yusuf Goolamabbas.
+
+
+Изменения в nginx 0.4.0 30.08.2006
+
+ *) Изменение во внутреннем API: инициализация модулей HTTP перенесена из
+ фазы init module в фазу HTTP postconfiguration.
+
+ *) Изменение: теперь тело запроса в модуле ngx_http_perl_module не
+ считывается заранее: нужно явно инициировать чтение с помощью метода
+ $r->has_request_body.
+
+ *) Добавление: модуль ngx_http_perl_module поддерживает код возврата
+ DECLINED.
+
+ *) Добавление: модуль ngx_http_dav_module поддерживает входящую строку
+ заголовка "Date" для метода PUT.
+
+ *) Добавление: директива ssi работает внутри блока if.
+
+ *) Исправление: происходил segmentation fault, если в директиве index
+ использовалась переменные и при этом первое имя индексного файла было
+ без переменных; ошибка появилась в 0.1.29.
+
+
+Изменения в nginx 0.3.61 28.08.2006
+
+ *) Изменение: директива tcp_nodelay теперь по умолчанию включена.
+
+ *) Добавление: директива msie_refresh.
+
+ *) Добавление: директива recursive_error_pages.
+
+ *) Исправление: директива rewrite возвращала неправильный редирект, если
+ редирект включал в себя выделенные закодированные символы из
+ оригинального URI.
+
+
+Изменения в nginx 0.3.60 18.08.2006
+
+ *) Исправление: во время перенаправления ошибки рабочий процесс мог
+ зациклиться; ошибка появилась в 0.3.59.
+
+
+Изменения в nginx 0.3.59 16.08.2006
+
+ *) Добавление: теперь можно делать несколько перенаправлений через
+ директиву error_page.
+
+ *) Исправление: директива dav_access не поддерживала три параметра.
+
+ *) Исправление: директива error_page не изменяла строку "Content-Type"
+ после перенаправления с помощью "X-Accel-Redirect"; ошибка появилась
+ в 0.3.58.
+
+
+Изменения в nginx 0.3.58 14.08.2006
+
+ *) Добавление: директива error_page поддерживает переменные.
+
+ *) Изменение: теперь на Linux используется интерфейс procfs вместо
+ sysctl.
+
+ *) Изменение: теперь при использовании "X-Accel-Redirect" строка
+ "Content-Type" наследуется из первоначального ответа.
+
+ *) Исправление: директива error_page не перенаправляла ошибку 413.
+
+ *) Исправление: завершающий "?" не удалял старые аргументы, если в
+ переписанном URI не было новых аргументов.
+
+ *) Исправление: nginx не запускался на 64-битной FreeBSD 7.0-CURRENT.
+
+
+Изменения в nginx 0.3.57 09.08.2006
+
+ *) Добавление: переменная $ssl_client_serial.
+
+ *) Исправление: в операторе "!-e" в директиве if.
+ Спасибо Андриану Буданцову.
+
+ *) Исправление: при проверке клиентского сертификата nginx не передавал
+ клиенту информацию о требуемых сертификатах.
+
+ *) Исправление: переменная $document_root не поддерживала переменные в
+ директиве root.
+
+
+Изменения в nginx 0.3.56 04.08.2006
+
+ *) Добавление: директива dav_access.
+
+ *) Добавление: директива if поддерживает операторы "-d", "!-d", "-e",
+ "!-e", "-x" и "!-x".
+
+ *) Исправление: при записи в access_log некоторых передаваемых клиенту
+ строк заголовков происходил segmentation fault, если запрос возвращал
+ редирект.
+
+
+Изменения в nginx 0.3.55 28.07.2006
+
+ *) Добавление: параметр stub в команде SSI include.
+
+ *) Добавление: команда SSI block.
+
+ *) Добавление: скрипт unicode2nginx добавлен в contrib.
+
+ *) Исправление: если root был задан только переменной, то корень
+ задавался относительно префикса сервера.
+
+ *) Исправление: если в запросе был "//" или "/.", и после этого
+ закодированные символы в виде "%XX", то проксируемый запрос
+ передавался незакодированным.
+
+ *) Исправление: метод $r->header_in("Cookie") модуля
+ ngx_http_perl_module теперь возвращает все строки "Cookie" в
+ заголовке запроса.
+
+ *) Исправление: происходил segmentation fault, если использовался
+ "client_body_in_file_only on" и делался переход к следующему бэкенду.
+
+ *) Исправление: при некоторых условиях во время переконфигурации коды
+ символов внутри директивы charset_map могли считаться неверными;
+ ошибка появилась в 0.3.50.
+
+
+Изменения в nginx 0.3.54 11.07.2006
+
+ *) Добавление: nginx теперь записывает в лог информацию о подзапросах.
+
+ *) Добавление: директивы proxy_next_upstream, fastcgi_next_upstream и
+ memcached_next_upstream поддерживают параметр off.
+
+ *) Добавление: директива debug_connection поддерживает запись адресов в
+ формате CIDR.
+
+ *) Исправление: при перекодировании ответа проксированного сервера или
+ сервера FastCGI в UTF-8 или наоборот ответ мог передаваться не
+ полностью.
+
+ *) Исправление: переменная $upstream_response_time содержала время
+ только первого обращения к бэкенду.
+
+ *) Исправление: nginx не собирался на платформе amd64; ошибка появилась
+ в 0.3.53.
+
+
+Изменения в nginx 0.3.53 07.07.2006
+
+ *) Изменение: директива add_header добавляет строки в ответы с кодом
+ 204, 301 и 302.
+
+ *) Добавление: директива server в блоке upstream поддерживает параметр
+ weight.
+
+ *) Добавление: директива server_name поддерживает маску "*".
+
+ *) Добавление: nginx поддерживает тело запроса больше 2G.
+
+ *) Исправление: если при использовании "satisfy_any on" клиент успешно
+ проходил аутентификацию, в лог всё равно записалоcь сообщение "access
+ forbidden by rule".
+
+ *) Исправление: метод PUT мог ошибочно не создать файл и вернуть код
+ 409.
+
+ *) Исправление: если во время аутентификации IMAP/POP3 бэкенд возвращал
+ ошибку, nginx продолжал проксирование.
+
+
+Изменения в nginx 0.3.52 03.07.2006
+
+ *) Изменение: восстановлено поведение модуля ngx_http_index_module для
+ запросов "POST /": как в версии до 0.3.40, модуль теперь не выдаёт
+ ошибку 405.
+
+ *) Исправление: при использовании ограничения скорости рабочий процесс
+ мог зациклиться; ошибка появилась в 0.3.37.
+
+ *) Исправление: модуль ngx_http_charset_module записывал в лог ошибку
+ "unknown charset", даже если перекодировка не требовалась; ошибка
+ появилась в 0.3.50.
+
+ *) Исправление: если в результате запроса PUT возвращался код 409, то
+ временный файл не удалялся.
+
+
+Изменения в nginx 0.3.51 30.06.2006
+
+ *) Исправление: при некоторых условиях в SSI мог пропадать символы "<";
+ ошибка появилась в 0.3.50.
+
+
+Изменения в nginx 0.3.50 28.06.2006
+
+ *) Изменение: директивы proxy_redirect_errors и fastcgi_redirect_errors
+ переименованы соответственно в proxy_intercept_errors и
+ fastcgi_intercept_errors.
+
+ *) Добавление: модуль ngx_http_charset_module поддерживает
+ перекодирование из однобайтных кодировок в UTF-8 и обратно.
+
+ *) Добавление: в режиме прокси и FastCGI поддерживается строка заголовка
+ "X-Accel-Charset" в ответе бэкенда.
+
+ *) Исправление: символ "\" в парах "\"" и "\'" в SSI командах убирался,
+ только если также использовался символ "$".
+
+ *) Исправление: при некоторых условиях в SSI после вставки могла быть
+ добавлена строка "<!--".
+
+ *) Исправление: если в заголовке ответа была строка "Content-Length: 0",
+ то при использовании небуферизированного проксировании не закрывалось
+ соединение с клиентом.
+
+
+Изменения в nginx 0.3.49 31.05.2006
+
+ *) Исправление: в директиве set.
+
+ *) Исправление: при включении в ssi двух и более подзапросов,
+ обрабатываемых через FastCGI, вместо вывода второго и остальных
+ подзапросов в ответ включался вывод первого подзапроса.
+
+
+Изменения в nginx 0.3.48 29.05.2006
+
+ *) Изменение: теперь модуль ngx_http_charset_module работает для
+ подзапросов, в ответах которых нет строки заголовка "Content-Type".
+
+ *) Исправление: если в директиве proxy_pass не было URI, то директива
+ "proxy_redirect default" добавляла в переписанный редирект в начало
+ лишний слэш.
+
+ *) Исправление: внутренний редирект всегда превращал любой HTTP-метод в
+ GET, теперь это делается только для редиректов, выполняемых с помощью
+ X-Accel-Redirect, и у которых метод не равен HEAD; ошибка появилась в
+ 0.3.42.
+
+ *) Исправление: модуль ngx_http_perl_module не собирался, если перл был
+ с поддержкой потоков; ошибка появилась в 0.3.46.
+
+
+Изменения в nginx 0.3.47 23.05.2006
+
+ *) Добавление: директива upstream.
+
+ *) Изменение: символ "\" в парах "\"" и "\'" в SSI командах теперь
+ всегда убирается.
+
+
+Изменения в nginx 0.3.46 11.05.2006
+
+ *) Добавление: директивы proxy_hide_header, proxy_pass_header,
+ fastcgi_hide_header и fastcgi_pass_header.
+
+ *) Изменение: директивы proxy_pass_x_powered_by, fastcgi_x_powered_by и
+ proxy_pass_server упразднены.
+
+ *) Добавление: в режиме прокси поддерживается строка заголовка
+ "X-Accel-Buffering" в ответе бэкенда.
+
+ *) Исправление: ошибок и утечек памяти при переконфигурации в модуле
+ ngx_http_perl_module.
+
+
+Изменения в nginx 0.3.45 06.05.2006
+
+ *) Добавление: директивы ssl_verify_client, ssl_verify_depth и
+ ssl_client_certificate.
+
+ *) Изменение: теперь переменная $request_method возвращает метод только
+ основного запроса.
+
+ *) Изменение: в таблице перекодировки koi-win изменены коды символа
+ &deg;.
+
+ *) Добавление: в таблицу перекодировки koi-win добавлены символы евро и
+ номера.
+
+ *) Исправление: если nginx распределял запросы на несколько машин, то
+ при падении одной из них запросы, предназначенные для этой машины,
+ перенаправлялись только на одну машину вместо того, чтобы равномерно
+ распределяться между остальными.
+
+
+Изменения в nginx 0.3.44 04.05.2006
+
+ *) Добавление: параметр wait в команде SSI include.
+
+ *) Добавление: в таблицу перекодировки koi-win добавлены украинские и
+ белорусские символы.
+
+ *) Исправление: в SSI.
+
+
+Изменения в nginx 0.3.43 26.04.2006
+
+ *) Исправление: в SSI.
+
+
+Изменения в nginx 0.3.42 26.04.2006
+
+ *) Добавление: параметр bind в директиве listen в IMAP/POP3 прокси.
+
+ *) Исправление: ошибки при использовании в директиве rewrite одного и
+ того же выделения более одного раза.
+
+ *) Исправление: в лог не записывались переменные
+ $sent_http_content_type, $sent_http_content_length,
+ $sent_http_last_modified, $sent_http_connection,
+ $sent_http_keep_alive и $sent_http_transfer_encoding.
+
+ *) Исправление: переменная $sent_http_cache_control возвращала
+ содержимое только одной строки "Cache-Control" в заголовке ответа.
+
+
+Изменения в nginx 0.3.41 21.04.2006
+
+ *) Добавление: ключ -v.
+
+ *) Исправление: при включении в SSI удалённых подзапросов мог произойти
+ segmentation fault.
+
+ *) Исправление: в обработке FastCGI.
+
+ *) Исправление: если путь к перловым модулям не был указан с помощью
+ --with-perl_modules_path=PATH или директивы perl_modules, то на
+ старте происходил segmentation fault.
+
+
+Изменения в nginx 0.3.40 19.04.2006
+
+ *) Добавление: модуль ngx_http_dav_module поддерживает метод MKCOL.
+
+ *) Добавление: директива create_full_put_path.
+
+ *) Добавление: переменная $limit_rate.
+
+
+Изменения в nginx 0.3.39 17.04.2006
+
+ *) Добавление: директива uninitialized_variable_warn; уровень
+ логгирования сообщения о неинициализированной переменной понижен с
+ уровня alert на warn.
+
+ *) Добавление: директива override_charset.
+
+ *) Изменение: при использовании неизвестной переменной в SSI-командах
+ echo и if expr='$name' теперь не записывается в лог сообщение о
+ неизвестной переменной.
+
+ *) Исправление: счётчик активных соединений рос при превышении лимита
+ соединений, заданного директивой worker_connections; ошибка появилась
+ в 0.2.0.
+
+ *) Исправление: при некоторых условия ограничение скорости соединения
+ могло не работать; ошибка появилась в 0.3.38.
+
+
+Изменения в nginx 0.3.38 14.04.2006
+
+ *) Добавление: модуль ngx_http_dav_module.
+
+ *) Изменение: оптимизация модуля ngx_http_perl_module.
+ Спасибо Сергею Скворцову.
+
+ *) Добавление: модуль ngx_http_perl_module поддерживает метод
+ $r->request_body_file.
+
+ *) Добавление: директива client_body_in_file_only.
+
+ *) Изменение: теперь при переполнении диска nginx пытается писать
+ access_log'и только раз в секунду.
+ Спасибо Антону Южанинову и Максиму Дунину.
+
+ *) Исправление: теперь директива limit_rate точнее ограничивает скорость
+ при значениях больше 100 Kbyte/s.
+ Спасибо ForJest.
+
+ *) Исправление: IMAP/POP3 прокси теперь передаёт серверу авторизации
+ символы "\r" и "\n" в логине и пароле в закодированном виде.
+ Спасибо Максиму Дунину.
+
+
+Изменения в nginx 0.3.37 07.04.2006
+
+ *) Добавление: директива limit_except.
+
+ *) Добавление: директива if поддерживает операторы "!~", "!~*", "-f" и
+ "!-f".
+
+ *) Добавление: модуль ngx_http_perl_module поддерживает метод
+ $r->request_body.
+
+ *) Исправление: в модуле ngx_http_addition_filter_module.
+
+
+Изменения в nginx 0.3.36 05.04.2006
+
+ *) Добавление: модуль ngx_http_addition_filter_module.
+
+ *) Добавление: директивы proxy_pass и fastcgi_pass можно использовать
+ внутри блока if.
+
+ *) Добавление: директивы proxy_ignore_client_abort и
+ fastcgi_ignore_client_abort.
+
+ *) Добавление: переменная $request_completion.
+
+ *) Добавление: модуль ngx_http_perl_module поддерживает методы
+ $r->request_method и $r->remote_addr.
+
+ *) Добавление: модуль ngx_http_ssi_module поддерживает команду elif.
+
+ *) Исправление: строка "\/" в начале выражения команды if модуля
+ ngx_http_ssi_module воспринималась неверно.
+
+ *) Исправление: в использовании регулярных выражениях в команде if
+ модуля ngx_http_ssi_module.
+
+ *) Исправление: при задании относительного пути в директивах
+ client_body_temp_path, proxy_temp_path, fastcgi_temp_path и
+ perl_modules использовался каталог относительно текущего каталога, а
+ не относительно префикса сервера.
+
+
+Изменения в nginx 0.3.35 22.03.2006
+
+ *) Исправление: accept-фильтр и TCP_DEFER_ACCEPT устанавливались только
+ для первой директивы listen; ошибка появилась в 0.3.31.
+
+ *) Исправление: в директиве proxy_pass без URI при использовании в
+ подзапросе.
+
+
+Изменения в nginx 0.3.34 21.03.2006
+
+ *) Добавление: директива add_header поддерживает переменные.
+
+
+Изменения в nginx 0.3.33 15.03.2006
+
+ *) Добавление: параметр http_503 в директивах proxy_next_upstream или
+ fastcgi_next_upstream.
+
+ *) Исправление: ngx_http_perl_module не работал со встроенным в
+ конфигурационный файл кодом, если он не начинался сразу же с "sub".
+
+ *) Исправление: в директиве post_action.
+
+
+Изменения в nginx 0.3.32 11.03.2006
+
+ *) Исправление: удаление отладочного логгирования на старте и при
+ переконфигурации; ошибка появилась в 0.3.31.
+
+
+Изменения в nginx 0.3.31 10.03.2006
+
+ *) Изменение: теперь nginx передаёт неверные ответы проксированного
+ бэкенда.
+
+ *) Добавление: директивы listen поддерживают адрес в виде "*:порт".
+
+ *) Добавление: поддержка EVFILER_TIMER в MacOSX 10.4.
+
+ *) Изменение: обход ошибки обработки миллисекундных таймаутов kqueue в
+ 64-битном ядре MacOSX.
+ Спасибо Андрею Нигматулину.
+
+ *) Исправление: если внутри одного сервера описаны несколько директив
+ listen, слушающих на разных адресах, то имена серверов вида
+ "*.domain.tld" работали только для первого адреса; ошибка появилась в
+ 0.3.18.
+
+ *) Исправление: при использовании протокола HTTPS в директиве proxy_pass
+ не передавались запросы с телом, записанным во временный файл.
+
+ *) Исправление: совместимость с perl 5.8.8.
+
+
+Изменения в nginx 0.3.30 22.02.2006
+
+ *) Изменение: уровень записи в лог ошибки ECONNABORTED изменён на error
+ с уровня crit.
+
+ *) Исправление: модуль ngx_http_perl_module не собирался без модуля
+ ngx_http_ssi_filter_module.
+
+ *) Исправление: nginx не собирался на i386 платформе, если использовался
+ PIC; ошибка появилась в 0.3.27.
+
+
+Изменения в nginx 0.3.29 20.02.2006
+
+ *) Добавление: теперь nginx использует меньше памяти, если PHP в режиме
+ FastCGI передаёт большое количество предупреждений перед ответом.
+
+ *) Исправление: в ответах 204 для запросов версии HTTP/1.1 выдавалась
+ строка заголовка "Transfer-Encoding: chunked".
+
+ *) Исправление: nginx возвращал 502 код ответа, если FastCGI сервер
+ передавал полные строки заголовка ответа в отдельных FastCGI записях.
+
+ *) Исправление: если в директиве post_action был указан проксируемый
+ URI, то он выполнялся только после успешного завершения запроса.
+
+
+Изменения в nginx 0.3.28 16.02.2006
+
+ *) Добавление: директива restrict_host_names упразднена.
+
+ *) Добавление: параметр конфигурации --with-cpu-opt=ppc64.
+
+ *) Исправление: при некоторых условиях проксированное соединение с
+ клиентом завершалось преждевременно.
+ Спасибо Владимиру Шутову.
+
+ *) Исправление: строка заголовка "X-Accel-Limit-Rate" не учитывалась для
+ запросов, перенаправленных с помощью строки "X-Accel-Redirect".
+
+ *) Исправление: директива post_action работала только после успешного
+ завершения запроса.
+
+ *) Исправление: тело проксированного ответа, создаваемого директивой
+ post_action, передавалось клиенту.
+
+
+Изменения в nginx 0.3.27 08.02.2006
+
+ *) Изменение: директивы variables_hash_max_size и
+ variables_hash_bucket_size.
+
+ *) Добавление: переменная $body_bytes_sent доступна не только в
+ директиве log_format.
+
+ *) Добавление: переменные $ssl_protocol и $ssl_cipher.
+
+ *) Добавление: определение размера строки кэша распространённых
+ процессоров при старте.
+
+ *) Добавление: директива accept_mutex теперь поддерживается посредством
+ fcntl(2) на платформах, отличных от i386, amd64, sparc64 и ppc.
+
+ *) Добавление: директива lock_file и параметр автоконфигурации
+ --with-lock-path=PATH.
+
+ *) Исправление: при использовании протокола HTTPS в директиве proxy_pass
+ не передавались запросы с телом.
+
+
+Изменения в nginx 0.3.26 03.02.2006
+
+ *) Изменение: директива optimize_host_names переименована в
+ optimize_server_names.
+
+ *) Исправление: при проксировании подзапроса в SSI бэкенду передавался
+ URI основного запроса, если в директиве proxy_pass отсутствовал URI.
+
+
+Изменения в nginx 0.3.25 01.02.2006
+
+ *) Исправление: при неверной конфигурации на старте или во время
+ переконфигурации происходил segmentation fault; ошибка появилась в
+ 0.3.24.
+
+
+Изменения в nginx 0.3.24 01.02.2006
+
+ *) Изменение: обход ошибки в kqueue во FreeBSD.
+
+ *) Исправление: ответ, создаваемый директивой post_action, теперь не
+ передаётся клиенту.
+
+ *) Исправление: при использовании большого количества лог-файлов
+ происходила утечка памяти.
+
+ *) Исправление: внутри одного location работала только первая директива
+ proxy_redirect.
+
+ *) Исправление: на 64-битных платформах при старте мог произойти
+ segmentation fault, если использовалось большое количество имён в
+ директивах server_name; ошибка появилась в 0.3.18.
+
+
+Изменения в nginx 0.3.23 24.01.2006
+
+ *) Добавление: директива optimize_host_names.
+
+ *) Исправление: ошибки при использовании переменных в директивах path и
+ alias.
+
+ *) Исправление: модуль ngx_http_perl_module неправильно собирался на
+ Linux и Solaris.
+
+
+Изменения в nginx 0.3.22 17.01.2006
+
+ *) Добавление: модуль ngx_http_perl_module поддерживает методы $r->args
+ и $r->unescape.
+
+ *) Добавление: метод $r->query_string в модуле ngx_http_perl_module
+ упразднён.
+
+ *) Исправление: если в директиве valid_referers указаны только none или
+ blocked, то происходил segmentation fault; ошибка появилась в 0.3.18.
+
+
+Изменения в nginx 0.3.21 16.01.2006
+
+ *) Добавление: модуль ngx_http_perl_module.
+
+ *) Изменение: директива valid_referers разрешает использовать рефереры
+ совсем без URI.
+
+
+Изменения в nginx 0.3.20 11.01.2006
+
+ *) Исправление: ошибки в обработке SSI.
+
+ *) Исправление: модуль ngx_http_memcached_module не поддерживал ключи в
+ виде /uri?args.
+
+
+Изменения в nginx 0.3.19 28.12.2005
+
+ *) Добавление: директивы path и alias поддерживают переменные.
+
+ *) Изменение: теперь директива valid_referers опять учитывает URI.
+
+ *) Исправление: ошибки в обработке SSI.
+
+
+Изменения в nginx 0.3.18 26.12.2005
+
+ *) Добавление: директива server_names поддерживает имена вида
+ ".domain.tld".
+
+ *) Добавление: директива server_names использует хэш для имён вида
+ "*.domain.tld" и более эффективный хэш для обычных имён.
+
+ *) Изменение: директивы server_names_hash_max_size и
+ server_names_hash_bucket_size.
+
+ *) Изменение: директивы server_names_hash и server_names_hash_threshold
+ упразднены.
+
+ *) Добавление: директива valid_referers использует хэш для имён сайтов.
+
+ *) Изменение: теперь директива valid_referers проверяет только имена
+ сайтов без учёта URI.
+
+ *) Исправление: некоторые имена вида ".domain.tld" неверно
+ обрабатывались модулем ngx_http_map_module.
+
+ *) Исправление: если конфигурационного файла не было, то происходил
+ segmentation fault; ошибка появилась в 0.3.12.
+
+ *) Исправление: на 64-битных платформах при старте мог произойти
+ segmentation fault; ошибка появилась в 0.3.16.
+
+
+Изменения в nginx 0.3.17 18.12.2005
+
+ *) Изменение: на Linux configure теперь проверяет наличие epoll и
+ sendfile64() в ядре.
+
+ *) Добавление: директива map поддерживает доменные имена в формате
+ ".domain.tld".
+
+ *) Исправление: во время SSL handshake не иcпользовались таймауты;
+ ошибка появилась в 0.2.4.
+
+ *) Исправление: в использовании протокола HTTPS в директиве proxy_pass.
+
+ *) Исправление: при использовании протокола HTTPS в директиве proxy_pass
+ по умолчанию использовался порт 80.
+
+
+Изменения в nginx 0.3.16 16.12.2005
+
+ *) Добавление: модуль ngx_http_map_module.
+
+ *) Добавление: директивы types_hash_max_size и types_hash_bucket_size.
+
+ *) Добавление: директива ssi_value_length.
+
+ *) Добавление: директива worker_rlimit_core.
+
+ *) Изменение: при сборке компиляторами icc 8.1 и 9.0 с оптимизацией для
+ Pentium 4 номер соединения в логах всегда был равен 1.
+
+ *) Исправление: команда config timefmt в SSI задавала неверный формат
+ времени.
+
+ *) Исправление: nginx не закрывал соединения с IMAP/POP3 бэкендом при
+ использовании SSL соединений; ошибка появилась в 0.3.13.
+ Спасибо Rob Mueller.
+
+ *) Исправление: segmentation fault мог произойти во время SSL shutdown;
+ ошибка появилась в 0.3.13.
+
+
+Изменения в nginx 0.3.15 07.12.2005
+
+ *) Добавление: новой код 444 в директиве return для закрытия соединения.
+
+ *) Добавление: директива so_keepalive в IMAP/POP3 прокси.
+
+ *) Исправление: nginx теперь вызывает abort() при обнаружении незакрытых
+ соединений только при планом выходе и включённой директиве
+ debug_points.
+
+
+Изменения в nginx 0.3.14 05.12.2005
+
+ *) Исправление: в ответе 304 передавалось тело ответа; ошибка появилась
+ в 0.3.13.
+
+
+Изменения в nginx 0.3.13 05.12.2005
+
+ *) Добавление: IMAP/POP3 прокси поддерживает STARTTLS и STLS.
+
+ *) Исправление: IMAP/POP3 прокси не работала с методами select, poll и
+ /dev/poll.
+
+ *) Исправление: ошибки в обработке SSI.
+
+ *) Исправление: sendfilev() в Solaris теперь не используется при
+ передаче тела запроса FastCGI-серверу через unix domain сокет.
+
+ *) Исправление: директива auth_basic не запрещала аутентификацию; ошибка
+ появилась в 0.3.11.
+
+
+Изменения в nginx 0.3.12 26.11.2005
+
+ *) Безопасность: если nginx был собран с модулем ngx_http_realip_module,
+ то при использовании директивы "satisfy_any on" директивы доступа и
+ аутентификации не работали. Модуль ngx_http_realip_module не
+ собирался и не собирается по умолчанию.
+
+ *) Изменение: имя переменной "$time_gmt" изменено на "$time_local".
+
+ *) Изменение: директивы proxy_header_buffer_size и
+ fastcgi_header_buffer_size переименованы соответственно в
+ proxy_buffer_size и fastcgi_buffer_size.
+
+ *) Добавление: модуль ngx_http_memcached_module.
+
+ *) Добавление: директива proxy_buffering.
+
+ *) Исправление: изменение в работе с accept mutex при использовании
+ метода rtsig; ошибка появилась в 0.3.0.
+
+ *) Исправление: если клиент передал строку "Transfer-Encoding: chunked"
+ в заголовке запроса, то nginx теперь выдаёт ошибку 411.
+
+ *) Исправление: при наследовании директивы auth_basic с уровня http в
+ строке "WWW-Authenticate" заголовка ответа выводился realm без текста
+ "Basic realm".
+
+ *) Исправление: если в директиве access_log был явно указан формат
+ combined, то в лог записывались пустые строки; ошибка появилась в
+ 0.3.8.
+
+ *) Исправление: nginx не работал на платформе sparc под любыми OS, кроме
+ Solaris.
+
+ *) Исправление: в директиве if теперь не нужно разделять пробелом строку
+ в кавычках и закрывающую скобку.
+
+
+Изменения в nginx 0.3.11 15.11.2005
+
+ *) Исправление: nginx не передавал при проксировании тело запроса и
+ строки заголовка клиента; ошибка появилась в 0.3.10.
+
+
+Изменения в nginx 0.3.10 15.11.2005
+
+ *) Изменение: директива valid_referers и переменная $invalid_referer
+ перенесены из модуля ngx_http_rewrite_module в новый модуль
+ ngx_http_referer_module.
+
+ *) Изменение: имя переменной "$apache_bytes_sent" изменено на
+ "$body_bytes_sent".
+
+ *) Добавление: переменные "$sent_http_...".
+
+ *) Добавление: директива if поддерживает операции "=" и "!=".
+
+ *) Добавление: директива proxy_pass поддерживает протокол HTTPS.
+
+ *) Добавление: директива proxy_set_body.
+
+ *) Добавление: директива post_action.
+
+ *) Добавление: модуль ngx_http_empty_gif_module.
+
+ *) Добавление: директива worker_cpu_affinity для Linux.
+
+ *) Исправление: директива rewrite не раскодировала символы в редиректах
+ в URI, теперь символы раскодируются, кроме символов %00-%25 и
+ %7F-%FF.
+
+ *) Исправление: nginx не собирался компилятором icc 9.0.
+
+ *) Исправление: если для статического файла нулевого размера был
+ разрешён SSI, то ответ передавался неверно при кодировании chunk'ами.
+
+
+Изменения в nginx 0.3.9 10.11.2005
+
+ *) Исправление: nginx считал небезопасными URI, в которых между двумя
+ слэшами находилось два любых символа; ошибка появилась в 0.3.8.
+
+
+Изменения в nginx 0.3.8 09.11.2005
+
+ *) Безопасность: nginx теперь проверят URI, полученные от бэкенда в
+ строке "X-Accel-Redirect" в заголовке ответа, или в SSI файле на
+ наличие путей "/../" и нулей.
+
+ *) Изменение: nginx теперь не воспринимает пустое имя как правильное в
+ строке "Authorization" в заголовке запроса.
+
+ *) Добавление: директива ssl_session_timeout модулей ngx_http_ssl_module
+ и ngx_imap_ssl_module.
+
+ *) Добавление: директива auth_http_header модуля
+ ngx_imap_auth_http_module.
+
+ *) Добавление: директива add_header.
+
+ *) Добавление: модуль ngx_http_realip_module.
+
+ *) Добавление: новые переменные для использования в директиве
+ log_format: $bytes_sent, $apache_bytes_sent, $status, $time_gmt,
+ $uri, $request_time, $request_length, $upstream_status,
+ $upstream_response_time, $gzip_ratio, $uid_got, $uid_set,
+ $connection, $pipe и $msec. Параметры в виде "%name" скоро будут
+ упразднены.
+
+ *) Изменение: в директиве "if" ложными значениями переменных теперь
+ являются пустая строка "" и строки, начинающиеся на "0".
+
+ *) Исправление: при работает с проксированными или FastCGI-серверами
+ nginx мог оставлять открытыми соединения и временные файлы с
+ запросами клиентов.
+
+ *) Исправление: рабочие процессы не сбрасывали буферизированные логи при
+ плавном выходе.
+
+ *) Исправление: если URI запроса изменялось с помощью rewrite, а затем
+ запрос проксировался в location, заданном регулярным выражением, то
+ бэкенду передавался неверный запрос; ошибка появилась в 0.2.6.
+
+ *) Исправление: директива expires не удаляла уже установленную строку
+ заголовка "Expires".
+
+ *) Исправление: при использовании метода rtsig и нескольких рабочих
+ процессах nginx мог перестать принимать запросы.
+
+ *) Исправление: в SSI командах неверно обрабатывались строки "\"" и
+ "\'".
+
+ *) Исправление: если ответ заканчивался сразу же после SSI команды, то
+ при использовании сжатия ответ передавался не до конца или не
+ передавался вообще.
+
+
+Изменения в nginx 0.3.7 27.10.2005
+
+ *) Добавление: директива access_log поддерживает параметр buffer=.
+
+ *) Исправление: nginx не собирался на платформах, отличных от i386,
+ amd64, sparc и ppc; ошибка появилась в 0.3.2.
+
+
+Изменения в nginx 0.3.6 24.10.2005
+
+ *) Изменение: IMAP/POP3 прокси теперь не передаёт серверу авторизации
+ пустой логин.
+
+ *) Добавление: директива log_format поддерживает переменные в виде
+ $name.
+
+ *) Исправление: если хотя бы в одном сервере не было описано ни одной
+ директивы listen, то nginx не слушал на 80 порту; ошибка появилась в
+ 0.3.3.
+
+ *) Исправление: если в директиве proxy_pass отсутствовал URI, то всегда
+ использовался порт 80.
+
+
+Изменения в nginx 0.3.5 21.10.2005
+
+ *) Исправление: если логин IMAP/POP3 менялся сервером авторизации, то
+ мог произойти segmentation fault; ошибка появилась в 0.2.2.
+
+ *) Исправление: accept mutex не работал, все соединения обрабатывались
+ одним рабочим процессом; ошибка появилась в 0.3.3.
+
+ *) Исправление: при использовании метода rtsig и директивы
+ timer_resolution не работали таймауты.
+
+
+Изменения в nginx 0.3.4 19.10.2005
+
+ *) Исправление: nginx не собирался на Linux 2.4+ и MacOS X; ошибка
+ появилась в 0.3.3.
+
+
+Изменения в nginx 0.3.3 19.10.2005
+
+ *) Изменение: параметры "bl" и "af" директивы listen переименованы в
+ "backlog" и "accept_filter".
+
+ *) Добавление: параметры "rcvbuf" и "sndbuf" в директиве listen.
+
+ *) Изменение: параметр лога $msec теперь не требует дополнительного
+ системного вызова gettimeofday().
+
+ *) Добавление: ключ -t теперь проверяет директивы listen.
+
+ *) Исправление: если в директиве listen был указан неверный адрес, то
+ nginx после сигнала -HUP оставлял открытый сокет в состоянии CLOSED.
+
+ *) Исправление: для индексных файлов, содержащих в имени переменную, мог
+ неверно выставляться тип mime по умолчанию; ошибка появилась в 0.3.0.
+
+ *) Добавление: директива timer_resolution.
+
+ *) Добавление: параметр лога $upstream_response_time в миллисекундах.
+
+ *) Исправление: временный файл с телом запроса клиента теперь удаляется
+ сразу после того, как клиенту передан заголовок ответа.
+
+ *) Исправление: совместимость с OpenSSL 0.9.6.
+
+ *) Исправление: пути к файлам с SSL сертификатом и ключом не могли быть
+ относительными.
+
+ *) Исправление: директива ssl_prefer_server_ciphers не работала для
+ модуля ngx_imap_ssl_module.
+
+ *) Исправление: директива ssl_protocols позволяла задать только один
+ протокол.
+
+
+Изменения в nginx 0.3.2 12.10.2005
+
+ *) Добавление: поддержка Sun Studio 10 C compiler.
+
+ *) Добавление: директивы proxy_upstream_max_fails,
+ proxy_upstream_fail_timeout, fastcgi_upstream_max_fails и
+ fastcgi_upstream_fail_timeout.
+
+
+Изменения в nginx 0.3.1 10.10.2005
+
+ *) Исправление: во время переполнения очереди сигналов при использовании
+ метода rtsig происходил segmentation fault; ошибка появилась в 0.2.0.
+
+ *) Изменение: корректная обработка пар "\\", "\"", "\'" и "\$" в SSI.
+
+
+Изменения в nginx 0.3.0 07.10.2005
+
+ *) Изменение: убрано десятидневное ограничение времени работы рабочего
+ процесса. Ограничение было введено из-за переполнения миллисекундных
+ таймеров.
+
+
+Изменения в nginx 0.2.6 05.10.2005
+
+ *) Изменение: с 60 до 10 секунд уменьшено время повторного обращения к
+ бэкенду при использовании распределения нагрузки.
+
+ *) Изменение: директива proxy_pass_unparsed_uri упразднена, оригинальный
+ запрос теперь передаётся, если в директиве proxy_pass отсутствует
+ URI.
+
+ *) Добавление: директива error_page поддерживает редиректы и позволяет
+ более гибко менять код ошибки.
+
+ *) Изменение: в проксированных подзапросах теперь игнорируется
+ переданный charset.
+
+ *) Исправление: если после изменения URI в блоке if для запроса не
+ находилась новая конфигурация, то правила модуля
+ ngx_http_rewrite_module выполнялись снова.
+
+ *) Исправление: если директива set устанавливала переменную модуля
+ ngx_http_geo_module в какой-либо части конфигурации, то эта
+ переменная не была доступна в других частях конфигурации и выдавалась
+ ошибка "using uninitialized variable"; ошибка появилась в 0.2.2.
+
+
+Изменения в nginx 0.2.5 04.10.2005
+
+ *) Изменение: дублирующее значение переменной модуля ngx_http_geo_module
+ теперь выдаёт предупреждение и изменяет старое значение.
+
+ *) Добавление: модуль ngx_http_ssi_module поддерживает команду set.
+
+ *) Добавление: модуль ngx_http_ssi_module поддерживает параметр file в
+ команде include.
+
+ *) Добавление: модуль ngx_http_ssi_module поддерживает подстановку
+ значений переменных в выражениях команды if.
+
+
+Изменения в nginx 0.2.4 03.10.2005
+
+ *) Добавление: модуль ngx_http_ssi_module поддерживает выражения
+ "$var=text", "$var!=text", "$var=/text/" и "$var!=/text/" в команде
+ if.
+
+ *) Исправление: ошибки при проксировании location без слэша в конце;
+ ошибка появилась в 0.1.44.
+
+ *) Исправление: при использовании метода rtsig мог произойти
+ segmentation fault; ошибка появилась в 0.2.0.
+
+
+Изменения в nginx 0.2.3 30.09.2005
+
+ *) Исправление: nginx не собирался без параметра --with-debug; ошибка
+ появилась в 0.2.2.
+
+
+Изменения в nginx 0.2.2 30.09.2005
+
+ *) Добавление: команда config errmsg в модуле ngx_http_ssi_module.
+
+ *) Изменение: переменные модуля ngx_http_geo_module можно переопределять
+ директивой set.
+
+ *) Добавление: директивы ssl_protocols и ssl_prefer_server_ciphers
+ модулей ngx_http_ssl_module и ngx_imap_ssl_module.
+
+ *) Исправление: ошибка в модуле ngx_http_autoindex_module при показе
+ длинных имён файлов;
+
+ *) Исправление: модуль ngx_http_autoindex_module теперь не показывает
+ файлы, начинающиеся на точку.
+
+ *) Исправление: если SSL handshake завершался с ошибкой, то это могло
+ привести также к закрытию другого соединения.
+ Спасибо Rob Mueller.
+
+ *) Исправление: экспортные версии MSIE 5.x не могли соединиться по
+ HTTPS.
+
+
+Изменения в nginx 0.2.1 23.09.2005
+
+ *) Исправление: если все бэкенды, используемые для балансировки
+ нагрузки, оказывались в нерабочем состоянии после одной ошибки, то
+ nginx мог зациклится; ошибка появилась в 0.2.0.
+
+
+Изменения в nginx 0.2.0 23.09.2005
+
+ *) Изменились имена pid-файлов, используемые во время обновления
+ исполняемого файла. Ручное переименование теперь не нужно. Старый
+ основной процесс добавляет к своему pid-файл суффикс ".oldbin" и
+ запускает новый исполняемый файл. Новый основной процесс создаёт
+ обычный pid-файл без суффикса ".newbin". Если новый основной процесс
+ выходит, то старый процесс переименовывает свой pid-файл c суффиксом
+ ".oldbin" в pid-файл без суффикса. При обновлении с версии 0.1.х до
+ 0.2.0 нужно учитывать, что оба процесса - старый 0.1.x и новый
+ 0.2.0 - используют pid-файл без суффиксов.
+
+ *) Изменение: директива worker_connections, новое название директивы
+ connections; директива теперь задаёт максимальное число соединений, а
+ не максимально возможный номер дескриптора для сокета.
+
+ *) Добавление: SSL поддерживает кэширование сессий в пределах одного
+ рабочего процесса.
+
+ *) Добавление: директива satisfy_any.
+
+ *) Изменение: модули ngx_http_access_module и ngx_http_auth_basic_module
+ не работают для подзапросов.
+
+ *) Добавление: директивы worker_rlimit_nofile и
+ worker_rlimit_sigpending.
+
+ *) Исправление: если все бэкенды, используемые для балансировки
+ нагрузки, оказывались в нерабочем состоянии после одной ошибки, то
+ nginx не обращался к ним в течение 60 секунд.
+
+ *) Исправление: в парсинге аргументов IMAP/POP3 команд.
+ Спасибо Rob Mueller.
+
+ *) Исправление: ошибки при использовании SSL в IMAP/POP3 прокси.
+
+ *) Исправление: ошибки при использовании SSI и сжатия.
+
+ *) Исправление: в ответах 304 не добавлялись строки заголовка ответа
+ "Expires" и "Cache-Control".
+ Спасибо Александру Кукушкину.
+
+
+Изменения в nginx 0.1.45 08.09.2005
+
+ *) Изменение: директива ssl_engine упразднена в модуле
+ ngx_http_ssl_module и перенесена на глобальный уровень.
+
+ *) Исправление: ответы с подзапросами, включённые с помощью SSI, не
+ передавались через SSL соединение.
+
+ *) Разные исправления в IMAP/POP3 прокси.
+
+
+Изменения в nginx 0.1.44 06.09.2005
+
+ *) Добавление: IMAP/POP3 прокси поддерживает SSL.
+
+ *) Добавление: директива proxy_timeout модуля ngx_imap_proxy_module.
+
+ *) Добавление: директива userid_mark.
+
+ *) Добавление: значение переменной $remote_user определяется независимо
+ от того, используется ли авторизация или нет.
+
+
+Изменения в nginx 0.1.43 30.08.2005
+
+ *) Добавление: listen(2) backlog в директиве listen можно менять по
+ сигналу -HUP.
+
+ *) Добавление: скрипт geo2nginx.pl добавлен в contrib.
+
+ *) Изменение: параметры FastCGI с пустым значениями теперь передаются
+ серверу.
+
+ *) Исправление: если в ответе проксированного сервера или FastCGI
+ сервера была строка "Cache-Control", то при использовании директивы
+ expires происходил segmentation fault или рабочий процесс мог
+ зациклится; в режиме прокси ошибка появилась в 0.1.29.
+
+
+Изменения в nginx 0.1.42 23.08.2005
+
+ *) Исправление: если URI запроса получался нулевой длины после обработки
+ модулем ngx_http_rewrite_module, то в модуле ngx_http_proxy_module
+ происходил segmentation fault или bus error.
+
+ *) Исправление: директива limit_rate не работала внутри блока if; ошибка
+ появилась в 0.1.38.
+
+
+Изменения в nginx 0.1.41 25.07.2005
+
+ *) Исправление: если переменная использовалась в файле конфигурации, то
+ она не могла использоваться в SSI.
+
+
+Изменения в nginx 0.1.40 22.07.2005
+
+ *) Исправление: если клиент слал очень длинную строку заголовка, то в
+ логе не помещалась информация, связанная с этим запросом.
+
+ *) Исправление: при использовании "X-Accel-Redirect" не передавалась
+ строка "Set-Cookie"; ошибка появилась в 0.1.39.
+
+ *) Исправление: при использовании "X-Accel-Redirect" не передавалась
+ строка "Content-Disposition".
+
+ *) Исправление: по сигналу SIGQUIT основной процесс не закрывал сокеты,
+ на которых он слушал.
+
+ *) Исправление: после обновления исполняемого файла на лету на Linux и
+ Solaris название процесса в команде ps становилось короче.
+
+
+Изменения в nginx 0.1.39 14.07.2005
+
+ *) Изменения в модуле ngx_http_charset_module: директива default_charset
+ упразднена; директива charset задаёт кодировку ответа; директива
+ source_charset задаёт только исходную кодировку.
+
+ *) Исправление: при перенаправлении ошибки 401, полученной от бэкенда,
+ не передавалась строка заголовка "WWW-Authenticate".
+
+ *) Исправление: модули ngx_http_proxy_module и ngx_http_fastcgi_module
+ могли закрыть соединение до того, как что-нибудь было передано
+ клиенту; ошибка появилась в 0.1.38.
+
+ *) Изменение: обработка ошибки инициализации в crypt_r() в Linux glibc.
+
+ *) Исправление: модуль ngx_http_ssi_module не поддерживал относительные
+ URI в команде include virtual.
+
+ *) Исправление: если в строке заголовка ответа бэкенда была строка
+ "Location", которую nginx не должен был изменять, то в ответе
+ передавалось тело 500 ошибки; ошибка появилась в 0.1.29.
+
+ *) Исправление: некоторые директивы модулей ngx_http_proxy_module и
+ ngx_http_fastcgi_module не наследовались с уровня server на уровень
+ location; ошибка появилась в 0.1.29.
+
+ *) Исправление: модуль ngx_http_ssl_module не поддерживал цепочки
+ сертификатов.
+
+ *) Исправление: ошибка в модуле ngx_http_autoindex_module при показе
+ длинных имён файлов; ошибка появилась в 0.1.38.
+
+ *) Исправления в IMAP/POP3 прокси при взаимодействии с бэкендом на
+ стадии login.
+
+
+Изменения в nginx 0.1.38 08.07.2005
+
+ *) Добавление: директива limit_rate поддерживается в режиме прокси и
+ FastCGI.
+
+ *) Добавление: в режиме прокси и FastCGI поддерживается строка заголовка
+ "X-Accel-Limit-Rate" в ответе бэкенда.
+
+ *) Добавление: директива break.
+
+ *) Добавление: директива log_not_found.
+
+ *) Исправление: при перенаправлении запроса с помощью строки заголовка
+ "X-Accel-Redirect" не изменялся код ответа.
+
+ *) Исправление: переменные, установленные директивой set не могли
+ использоваться в SSI.
+
+ *) Исправление: при включении в SSI более одного удалённого подзапроса
+ мог произойти segmentation fault.
+
+ *) Исправление: если статусная строка в ответе бэкенда передавалась в
+ двух пакетах, то nginx считал ответ неверным; ошибка появилась в
+ 0.1.29.
+
+ *) Добавление: директива ssi_types.
+
+ *) Добавление: директива autoindex_exact_size.
+
+ *) Исправление: модуль ngx_http_autoindex_module не поддерживал длинные
+ имена файлов в UTF-8.
+
+ *) Добавление: IMAP/POP3 прокси.
+
+
+Изменения в nginx 0.1.37 23.06.2005
+
+ *) Изменение: в конце файла nginx.pid теперь добавляется "\n".
+
+ *) Исправление: при включении большого количества вставок или нескольких
+ больших вставок с помощью SSI ответ мог передаваться не полностью.
+
+ *) Исправление: если все бэкенды возвращали ответ 404, то при
+ использовании параметра http_404 в директивах proxy_next_upstream или
+ fastcgi_next_upstream, nginx начинал запрашивать все бэкенды снова.
+
+
+Изменения в nginx 0.1.36 15.06.2005
+
+ *) Изменение: если в заголовке запроса есть дублирующиеся строки "Host",
+ "Connection", "Content-Length" и "Authorization", то nginx теперь
+ выдаёт ошибку 400.
+
+ *) Изменение: директива post_accept_timeout упразднена.
+
+ *) Добавление: параметры default, af=, bl=, deferred и bind в директиве
+ listen.
+
+ *) Добавление: поддержка accept фильтров во FreeBSD.
+
+ *) Добавление: поддержка TCP_DEFER_ACCEPT в Linux.
+
+ *) Исправление: модуль ngx_http_autoindex_module не поддерживал имена
+ файлов в UTF-8.
+
+ *) Исправление: после добавления новый лог-файл ротация этого лога по
+ сигналу -USR1 выполнялась, только если переконфигурировать nginx два
+ раза по сигналу -HUP.
+
+
+Изменения в nginx 0.1.35 07.06.2005
+
+ *) Добавление: директива working_directory.
+
+ *) Добавление: директива port_in_redirect.
+
+ *) Исправление: если заголовок ответа бэкенда не помещался в один пакет,
+ то происходил segmentation fault; ошибка появилась в 0.1.29.
+
+ *) Исправление: если было сконфигурировано более 10 серверов или в
+ сервере не описана директива "listen", то при запуске мог произойти
+ segmentation fault.
+
+ *) Исправление: если ответ не помещался во временный файл, то мог
+ произойти segmentation fault.
+
+ *) Исправление: nginx возвращал ошибку 400 на запросы вида
+ "GET http://www.domain.com/uri HTTP/1.0"; ошибка появилась в 0.1.28.
+
+
+Изменения в nginx 0.1.34 26.05.2005
+
+ *) Исправление: при включении больших ответов с помощью SSI рабочий
+ процесс мог зациклиться.
+
+ *) Исправление: переменные, устанавливаемые директивой "set", не были
+ доступны в SSI.
+
+ *) Добавление: директива autoindex_localtime.
+
+ *) Исправление: пустое значение в директиве proxy_set_header запрещает
+ передачу заголовка.
+
+
+Изменения в nginx 0.1.33 23.05.2005
+
+ *) Исправление: nginx не собирался с параметром --without-pcre; ошибка
+ появилась в 0.1.29.
+
+ *) Исправление: 3, 5, 7 и 8 директив proxy_set_header на одном уровне
+ вызывали bus fault при запуске.
+
+ *) Исправление: в редиректах внутри HTTPS сервера был указан протокол
+ HTTP.
+
+ *) Исправление: если директива rewrite использовала выделения внутри
+ директивы if, то возвращалась ошибка 500.
+
+
+Изменения в nginx 0.1.32 19.05.2005
+
+ *) Исправление: в редиректах, выдаваемых с помощью директивы rewrite, не
+ передавались аргументы; ошибка появилась в 0.1.29.
+
+ *) Добавление: директива if поддерживает выделения в регулярных
+ выражениях.
+
+ *) Добавление: директива set поддерживает переменные и выделения из
+ регулярных выражений.
+
+ *) Добавление: в режиме прокси и FastCGI поддерживается строка заголовка
+ "X-Accel-Redirect" в ответе бэкенда.
+
+
+Изменения в nginx 0.1.31 16.05.2005
+
+ *) Исправление: при использовании SSL ответ мог передаваться не до
+ конца.
+
+ *) Исправление: ошибки при обработке SSI в ответе, полученного от
+ FastCGI-сервера.
+
+ *) Исправление: ошибки при использовании SSI и сжатия.
+
+ *) Исправление: редирект с кодом 301 передавался без тела ответа; ошибка
+ появилась в 0.1.30.
+
+
+Изменения в nginx 0.1.30 14.05.2005
+
+ *) Исправление: при использовании SSI рабочий процесс мог зациклиться.
+
+ *) Исправление: при использовании SSL ответ мог передаваться не до
+ конца.
+
+ *) Исправление: если длина части ответа, полученного за один раз от
+ проксируемого или FastCGI сервера была равна 500 байт, то nginx
+ возвращал код ответа 500; в режиме прокси ошибка появилась только в
+ 0.1.29.
+
+ *) Исправление: nginx не считал неверными директивы с 8-ю или 9-ю
+ параметрами.
+
+ *) Добавление: директива return может возвращать код ответа 204.
+
+ *) Добавление: директива ignore_invalid_headers.
+
+
+Изменения в nginx 0.1.29 12.05.2005
+
+ *) Добавление: модуль ngx_http_ssi_module поддерживает команду include
+ virtual.
+
+ *) Добавление: модуль ngx_http_ssi_module поддерживает условную команду
+ вида 'if expr="$NAME"' и команды else и endif. Допускается только
+ один уровень вложенности.
+
+ *) Добавление: модуль ngx_http_ssi_module поддерживает две переменные
+ DATE_LOCAL и DATE_GMT и команду config timefmt.
+
+ *) Добавление: директива ssi_ignore_recycled_buffers.
+
+ *) Исправление: если переменная QUERY_STRING не была определена, то в
+ команде echo не ставилось значение по умолчанию.
+
+ *) Изменение: модуль ngx_http_proxy_module полностью переписан.
+
+ *) Добавление: директивы proxy_redirect, proxy_pass_request_headers,
+ proxy_pass_request_body и proxy_method.
+
+ *) Добавление: директива proxy_set_header. Директива proxy_x_var
+ упразднена и должна быть заменена директивой proxy_set_header.
+
+ *) Изменение: директива proxy_preserve_host упразднена и должна быть
+ заменена директивами "proxy_set_header Host $host" и "proxy_redirect
+ off" или директивой "proxy_set_header Host $host:$proxy_port" и
+ соответствующими ей директивами proxy_redirect.
+
+ *) Изменение: директива proxy_set_x_real_ip упразднена и должна быть
+ заменена директивой "proxy_set_header X-Real-IP $remote_addr".
+
+ *) Изменение: директива proxy_add_x_forwarded_for упразднена и должна
+ быть заменена директивой
+ "proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for".
+
+ *) Изменение: директива proxy_set_x_url упразднена и должна быть
+ заменена директивой
+ "proxy_set_header X-URL http://$host:$server_port$request_uri".
+
+ *) Добавление: директива fastcgi_param.
+
+ *) Изменение: директивы fastcgi_root, fastcgi_set_var и fastcgi_params
+ упразднены и должны быть замены директивами fastcgi_param.
+
+ *) Добавление: директива index может использовать переменные.
+
+ *) Добавление: директива index может быть указана на уровне http и
+ server.
+
+ *) Изменение: только последний параметр в директиве index может быть
+ абсолютным.
+
+ *) Добавление: в директиве rewrite могут использоваться переменные.
+
+ *) Добавление: директива internal.
+
+ *) Добавление: переменные CONTENT_LENGTH, CONTENT_TYPE, REMOTE_PORT,
+ SERVER_ADDR, SERVER_PORT, SERVER_PROTOCOL, DOCUMENT_ROOT,
+ SERVER_NAME, REQUEST_METHOD, REQUEST_URI и REMOTE_USER.
+
+ *) Изменение: nginx теперь передаёт неверные строки в заголовках запроса
+ клиента и ответа бэкенда.
+
+ *) Исправление: если бэкенд долго не передавал ответ и send_timeout был
+ меньше, чем proxy_read_timeout, то клиенту возвращался ответ 408.
+
+ *) Исправление: если бэкенд передавал неверную строку в заголовке
+ ответа, то происходил segmentation fault; ошибка появилась в 0.1.26.
+
+ *) Исправление: при использовании отказоустойчивой конфигурации в
+ FastCGI мог происходить segmentation fault.
+
+ *) Исправление: директива expires не удаляла уже установленные строки
+ заголовка "Expires" и "Cache-Control".
+
+ *) Исправление: nginx не учитывал завершающую точку в строке заголовка
+ запроса "Host".
+
+ *) Исправление: модуль ngx_http_auth_module не работал на Linux.
+
+ *) Исправление: директива rewrite неверно работала, если в запросе
+ присутствовали аргументы.
+
+ *) Исправление: nginx не собирался на MacOS X.
+
+
+Изменения в nginx 0.1.28 08.04.2005
+
+ *) Исправление: при проксировании больших файлов nginx сильно нагружал
+ процессор.
+
+ *) Исправление: nginx не собирался gcc 4.0 на Linux.
+
+
+Изменения в nginx 0.1.27 28.03.2005
+
+ *) Добавление: параметр blocked в директиве valid_referers.
+
+ *) Изменение: ошибки обработки заголовка запроса теперь записываются на
+ уровне info, в лог также записывается имя сервера и строки заголовка
+ запроса "Host" и "Referer".
+
+ *) Изменение: при записи ошибок в лог записывается также строка
+ заголовка запроса "Host".
+
+ *) Добавление: директива proxy_pass_unparsed_uri. Специальная обработка
+ символов "://" в URI, введённая в версии 0.1.11, теперь упразднена.
+
+ *) Исправление: nginx не собирался на FreeBSD и Linux, если был указан
+ параметр конфигурации --without-ngx_http_auth_basic_module.
+
+
+Изменения в nginx 0.1.26 22.03.2005
+
+ *) Изменение: неверные строки заголовка, переданные клиентом, теперь
+ игнорируется и записываются в error_log на уровне info.
+
+ *) Изменение: при записи ошибок в лог записывается также имя сервера,
+ при обращении к которому произошла ошибка.
+
+ *) Добавление: модуль ngx_http_auth_basic_module и директивы auth_basic
+ и auth_basic_user_file.
+
+
+Изменения в nginx 0.1.25 19.03.2005
+
+ *) Исправление: nginx не работал на Linux parisc.
+
+ *) Добавление: nginx теперь не запускается под FreeBSD, если значение
+ sysctl kern.ipc.somaxconn слишком большое.
+
+ *) Исправление: если модуль ngx_http_index_module делал внутреннее
+ перенаправление запроса в модули ngx_http_proxy_module или
+ ngx_http_fastcgi_module, то файл индекса не закрывался после
+ обслуживания запроса.
+
+ *) Добавление: директива proxy_pass может использоваться в location,
+ заданных регулярным выражением.
+
+ *) Добавление: модуль ngx_http_rewrite_filter_module поддерживает
+ условия вида "if ($HTTP_USER_AGENT ~ MSIE)".
+
+ *) Исправление: nginx очень медленно запускался при большом количестве
+ адресов и использовании текстовых значений в директиве geo.
+
+ *) Изменение: имя переменной в директиве geo нужно указывать, как $name.
+ Прежний вариант без "$" пока работает, но вскоре будет убран.
+
+ *) Добавление: параметр лога "%{VARIABLE}v".
+
+ *) Добавление: директива "set $name value".
+
+ *) Исправление: совместимость с gcc 4.0.
+
+ *) Добавление: параметр автоконфигурации --with-openssl-opt=OPTIONS.
+
+
+Изменения в nginx 0.1.24 04.03.2005
+
+ *) Добавление: модуль ngx_http_ssi_filter_module поддерживает переменные
+ QUERY_STRING и DOCUMENT_URI.
+
+ *) Исправление: модуль ngx_http_autoindex_module мог выдавать ответ 404
+ на существующий каталог, если этот каталог был указан как alias.
+
+ *) Исправление: модуль ngx_http_ssi_filter_module неправильно работал
+ при больших ответах.
+
+ *) Исправление: отсутствие строки заголовка "Referer" всегда считалось
+ правильным referrer'ом.
+
+
+Изменения в nginx 0.1.23 01.03.2005
+
+ *) Добавление: модуль ngx_http_ssi_filter_module и директивы ssi,
+ ssi_silent_errors и ssi_min_file_chunk. Поддерживаются команды 'echo
+ var="HTTP_..." default=""' и 'echo var="REMOTE_ADDR"'.
+
+ *) Добавление: параметр лога %request_time.
+
+ *) Добавление: если запрос пришёл без строки заголовка "Host", то
+ директива proxy_preserve_host устанавливает в качестве этого
+ заголовка первое имя сервера из директивы server_name.
+
+ *) Исправление: nginx не собирался на платформах, отличных от i386,
+ amd64, sparc и ppc; ошибка появилась в 0.1.22.
+
+ *) Исправление: модуль ngx_http_autoindex_module теперь показывает
+ информацию не о символическом линке, а о файле или каталоге, на
+ который он указывает.
+
+ *) Исправление: если клиенту ничего не передавалось, то параметр
+ %apache_length записывал в лог отрицательную длину заголовка ответа.
+
+
+Изменения в nginx 0.1.22 22.02.2005
+
+ *) Исправление: модуль ngx_http_stub_status_module показывал неверную
+ статистику для обработанных соединений, если использовалось
+ проксирование или FastCGI-сервер.
+
+ *) Исправление: на Linux и Solaris установочные пути были неверно
+ заключены в кавычки; ошибка появилась в 0.1.21.
+
+
+Изменения в nginx 0.1.21 22.02.2005
+
+ *) Исправление: модуль ngx_http_stub_status_module показывал неверную
+ статистику при использовании метода rtsig или при использовании
+ нескольких рабочих процессов на SMP машине.
+
+ *) Исправление: nginx не собирался компилятором icc под Линуксом или
+ если библиотека zlib-1.2.x собиралась из исходных текстов.
+
+ *) Исправление: nginx не собирался под NetBSD 2.0.
+
+
+Изменения в nginx 0.1.20 17.02.2005
+
+ *) Добавление: новые параметры script_filename и remote_port в директиве
+ fastcgi_params.
+
+ *) Исправление: неправильно обрабатывался поток stderr от
+ FastCGI-сервера.
+
+
+Изменения в nginx 0.1.19 16.02.2005
+
+ *) Исправление: если в запросе есть нуль, то для локальных запросов
+ теперь возвращается ошибка 404.
+
+ *) Исправление: nginx не собирался под NetBSD 2.0.
+
+ *) Исправление: во время чтения тела запроса клиента в SSL соединении
+ мог произойти таймаут.
+
+
+Изменения в nginx 0.1.18 09.02.2005
+
+ *) Изменение: для совместимости с Solaris 10 в директивах devpoll_events
+ и devpoll_changes значения по умолчанию уменьшены с 512 до 32.
+
+ *) Исправление: директивы proxy_set_x_var и fastcgi_set_var не
+ наследовались.
+
+ *) Исправление: в директиве rewrite, возвращающей редирект, аргументы
+ присоединялись к URI через символ "&" вместо "?".
+
+ *) Исправление: строки для модуля ngx_http_geo_module без символа ";" во
+ включённом файле игнорировались.
+
+ *) Добавление: модуль ngx_http_stub_status_module.
+
+ *) Исправление: неизвестный формат лог-файла в директиве access_log
+ вызывал segmentation fault.
+
+ *) Добавление: новый параметр document_root в директиве fastcgi_params.
+
+ *) Добавление: директива fastcgi_redirect_errors.
+
+ *) Добавление: новый модификатор break в директиве rewrite позволяет
+ прекратить цикл rewrite/location и устанавливает текущую конфигурацию
+ для запроса.
+
+
+Изменения в nginx 0.1.17 03.02.2005
+
+ *) Изменение: модуль ngx_http_rewrite_module полностью переписан. Теперь
+ можно делать редиректы, возвращать коды ошибок и проверять переменные
+ и рефереры. Эти директивы можно использовать внутри location.
+ Директива redirect упразднена.
+
+ *) Добавление: модуль ngx_http_geo_module.
+
+ *) Добавление: директивы proxy_set_x_var и fastcgi_set_var.
+
+ *) Исправление: конфигурация location с модификатором "=" могла
+ использоваться в другом location.
+
+ *) Исправление: правильный тип ответа выставлялся только для запросов, у
+ которых в расширении были только маленькие буквы.
+
+ *) Исправление: если для location установлен proxy_pass или
+ fastcgi_pass, и доступ к нему запрещался, а ошибка перенаправлялась
+ на статическую страницу, то происходил segmentation fault.
+
+ *) Исправление: если в проксированном ответе в заголовке "Location"
+ передавался относительный URL, то к нему добавлялось имя хоста и
+ слэш; ошибка появилась в 0.1.14.
+
+ *) Исправление: на Linux в лог не записывался текст системной ошибки.
+
+
+Изменения в nginx 0.1.16 25.01.2005
+
+ *) Исправление: если ответ передавался chunk'ами, то при запросе HEAD
+ выдавался завершающий chunk.
+
+ *) Исправление: заголовок "Connection: keep-alive" выдавался, даже если
+ директива keepalive_timeout запрещала использование keep-alive.
+
+ *) Исправление: ошибки в модуле ngx_http_fastcgi_module вызывали
+ segmentation fault.
+
+ *) Исправление: при использовании SSL сжатый ответ мог передаваться не
+ до конца.
+
+ *) Исправление: опции TCP_NODELAY, TCP_NOPUSH и TCP_CORK, специфичные
+ для TCP сокетов, не используются для unix domain сокетов.
+
+ *) Добавление: директива rewrite поддерживает перезаписывание
+ аргументов.
+
+ *) Исправление: на запрос POST с заголовком "Content-Length: 0"
+ возвращался ответ 400; ошибка появилась в 0.1.14.
+
+
+Изменения в nginx 0.1.15 19.01.2005
+
+ *) Исправление: ошибка соединения с FastCGI-сервером вызывала
+ segmentation fault.
+
+ *) Исправление: корректная обработка регулярного выражения, в котором
+ число выделенных частей не совпадает с числом подстановок.
+
+ *) Добавление: location, который передаётся FastCGI-серверу, может быть
+ задан с помощью регулярного выражения.
+
+ *) Исправление: параметр FastCGI REQUEST_URI теперь передаётся вместе с
+ аргументами и в том виде, в котором был получен от клиента.
+
+ *) Исправление: для использования регулярных выражений в location нужно
+ было собирать nginx вместе с ngx_http_rewrite_module.
+
+ *) Исправление: если бэкенд слушал на 80-ом порту, то при использовании
+ директивы "proxy_preserve_host on" в заголовке "Host" указывался
+ также порт 80; ошибка появилась в 0.1.14.
+
+ *) Исправление: если задать одинаковые пути в параметрах
+ автоконфигурации --http-client-body-temp-path=PATH и
+ --http-proxy-temp-path=PATH или --http-client-body-temp-path=PATH и
+ --http-fastcgi-temp-path=PATH, то происходил segmentation fault.
+
+
+Изменения в nginx 0.1.14 18.01.2005
+
+ *) Добавление: параметры автоконфигурации
+ --http-client-body-temp-path=PATH, --http-proxy-temp-path=PATH и
+ --http-fastcgi-temp-path=PATH
+
+ *) Изменение: имя каталога с временными файлами, содержащие тело запроса
+ клиента, задаётся директивой client_body_temp_path, по умолчанию
+ <prefix>/client_body_temp.
+
+ *) Добавление: модуль ngx_http_fastcgi_module и директивы fastcgi_pass,
+ fastcgi_root, fastcgi_index, fastcgi_params, fastcgi_connect_timeout,
+ fastcgi_send_timeout, fastcgi_read_timeout, fastcgi_send_lowat,
+ fastcgi_header_buffer_size, fastcgi_buffers,
+ fastcgi_busy_buffers_size, fastcgi_temp_path,
+ fastcgi_max_temp_file_size, fastcgi_temp_file_write_size,
+ fastcgi_next_upstream и fastcgi_x_powered_by.
+
+ *) Исправление: ошибка "[alert] zero size buf"; ошибка появилась в
+ 0.1.3.
+
+ *) Изменение: в директиве proxy_pass нужно обязательно указывать URI
+ после имени хоста.
+
+ *) Изменение: если в URI встречался символ %3F, то он считался началом
+ строки аргументов.
+
+ *) Добавление: поддержка unix domain сокетов в модуле
+ ngx_http_proxy_module.
+
+ *) Добавление: директивы ssl_engine и ssl_ciphers.
+ Спасибо Сергею Скворцову за SSL-акселератор.
+
+
+Изменения в nginx 0.1.13 21.12.2004
+
+ *) Добавление: директивы server_names_hash и
+ server_names_hash_threshold.
+
+ *) Исправление: имена *.domain.tld в директиве server_name не работали.
+
+ *) Исправление: параметр лога %request_length записывал неверную длину.
+
+
+Изменения в nginx 0.1.12 06.12.2004
+
+ *) Добавление: параметр лога %request_length.
+
+ *) Исправление: при использовании /dev/poll, select и poll на
+ платформах, где возможны ложные срабатывания указанных методов, могли
+ быть длительные задержки при обработке запроса по keep-alive
+ соединению. Наблюдалось по крайней мере на Solaris с использованием
+ /dev/poll.
+
+ *) Исправление: директива send_lowat игнорируется на Linux, так как
+ Linux не поддерживает опцию SO_SNDLOWAT.
+
+
+Изменения в nginx 0.1.11 02.12.2004
+
+ *) Добавление: директива worker_priority.
+
+ *) Изменение: под FreeBSD директивы tcp_nopush и tcp_nodelay вместе
+ влияют на передачу ответа.
+
+ *) Исправление: nginx не вызывал initgroups().
+ Спасибо Андрею Ситникову и Андрею Нигматулину.
+
+ *) Изменение: ngx_http_auto_index_module теперь выдаёт размер файлов в
+ байтах.
+
+ *) Исправление: ngx_http_auto_index_module возвращал ошибку 500, если в
+ каталоге есть битый symlink.
+
+ *) Исправление: файлы больше 4G не передавались с использованием
+ sendfile.
+
+ *) Исправление: если бэкенд резолвился в несколько адресов и при
+ ожидании от него ответа происходила ошибка, то процесс зацикливался.
+
+ *) Исправление: при использовании метода /dev/poll рабочий процесс мог
+ завершиться с сообщением "unknown cycle".
+
+ *) Исправление: ошибки "close() channel failed".
+
+ *) Исправление: автоматическое определение групп nobody и nogroup.
+
+ *) Исправление: директива send_lowat не работала на Linux.
+
+ *) Исправление: если в конфигурации не было раздела events, то
+ происходил segmentation fault.
+
+ *) Исправление: nginx не собирался под OpenBSD.
+
+ *) Исправление: двойные слэшы в "://" в URI превращались в ":/".
+
+
+Изменения в nginx 0.1.10 26.11.2004
+
+ *) Исправление: если в запросе без аргументов есть "//", "/./", "/../"
+ или "%XX", то терялся последний символ в строке запроса; ошибка
+ появилась в 0.1.9.
+
+ *) Исправление: исправление в версии 0.1.9 для файлов больше 2G на Linux
+ не работало.
+
+
+Изменения в nginx 0.1.9 25.11.2004
+
+ *) Исправление: если в запросе есть "//", "/./", "/../" или "%XX", то
+ проксируемый запрос передавался без аргументов.
+
+ *) Исправление: при сжатии больших ответов иногда они передавались не
+ полностью.
+
+ *) Исправление: не передавались файлы больше 2G на Linux,
+ неподдерживающем sendfile64().
+
+ *) Исправление: на Linux при конфигурации сборки нужно было обязательно
+ использовать параметр --with-poll_module; ошибка появилась в 0.1.8.
+
+
+Изменения в nginx 0.1.8 20.11.2004
+
+ *) Исправление: ошибка в модуле ngx_http_autoindex_module при показе
+ длинных имён файлов.
+
+ *) Добавление: модификатор "^~" в директиве location.
+
+ *) Добавление: директива proxy_max_temp_file_size.
+
+
+Изменения в nginx 0.1.7 12.11.2004
+
+ *) Исправление: при использовании sendfile, если передаваемый файл
+ менялся, то мог произойти segmentation fault на FreeBSD; ошибка
+ появилась в 0.1.5.
+
+
+Изменения в nginx 0.1.6 11.11.2004
+
+ *) Исправление: при некоторых комбинациях директив location c
+ регулярными выражениями использовалась конфигурация не из того
+ location.
+
+
+Изменения в nginx 0.1.5 11.11.2004
+
+ *) Исправление: на Solaris и Linux могло быть очень много сообщений
+ "recvmsg() returned not enough data".
+
+ *) Исправление: в режиме прокси без использования sendfile на Solaris
+ возникала ошибка "writev() failed (22: Invalid argument)". На других
+ платформах, не поддерживающих sendfile, процесс зацикливался.
+
+ *) Исправление: при использовании sendfile в режиме прокси на Solaris
+ возникал segmentation fault.
+
+ *) Исправление: segmentation fault на Solaris.
+
+ *) Исправление: обновление исполняемого файла на лету не работало на
+ Linux.
+
+ *) Исправление: в списке файлов, выдаваемом модулем
+ ngx_http_autoindex_module, не перекодировались пробелы, кавычки и
+ знаки процента.
+
+ *) Изменение: уменьшение операций копирования.
+
+ *) Добавление: директива userid_p3p.
+
+
+Изменения в nginx 0.1.4 26.10.2004
+
+ *) Исправление: ошибка в модуле ngx_http_autoindex_module.
+
+
+Изменения в nginx 0.1.3 25.10.2004
+
+ *) Добавление: модуль ngx_http_autoindex_module и директива autoindex.
+
+ *) Добавление: директива proxy_set_x_url.
+
+ *) Исправление: модуль проксировании мог привести к зацикливанию, если
+ не использовался sendfile.
+
+
+Изменения в nginx 0.1.2 21.10.2004
+
+ *) Добавление: параметры --user=USER, --group=GROUP и
+ --with-ld-opt=OPTIONS в configure.
+
+ *) Добавление: директива server_name поддерживает *.domain.tld.
+
+ *) Исправление: улучшена переносимость на неизвестные платформы.
+
+ *) Исправление: нельзя переконфигурировать nginx, если конфигурационный
+ файл указан в командной строке; ошибка появилась в 0.1.1.
+
+ *) Исправление: модуль проксировании мог привести к зацикливанию, если
+ не использовался sendfile.
+
+ *) Исправление: при использовании sendfile текст ответа не
+ перекодировался согласно директивам модуля charset; ошибка появилась
+ в 0.1.1.
+
+ *) Исправление: очень редкая ошибка при обработке kqueue.
+
+ *) Исправление: модуль сжатия сжимал уже сжатые ответы, полученные при
+ проксировании.
+
+
+Изменения в nginx 0.1.1 11.10.2004
+
+ *) Добавление: директива gzip_types.
+
+ *) Добавление: директива tcp_nodelay.
+
+ *) Добавление: директива send_lowat работает не только на платформах,
+ поддерживающих kqueue NOTE_LOWAT, но и на всех, поддерживающих
+ SO_SNDLOWAT.
+
+ *) Добавление: эмуляция setproctitle() для Linux и Solaris.
+
+ *) Исправление: ошибка при переписывании заголовка "Location" при
+ проксировании.
+
+ *) Исправление: ошибка в модуле ngx_http_chunked_module, приводившая к
+ зацикливанию.
+
+ *) Исправление: ошибки в модуле /dev/poll.
+
+ *) Исправление: при проксировании и использовании временных файлов
+ ответы портились.
+
+ *) Исправление: бэкенду передавались запросы с неперекодированными
+ символами.
+
+ *) Исправление: на Linux 2.4 при конфигурации сборки нужно было
+ обязательно использовать параметр --with-poll_module.
+
+
+Изменения в nginx 0.1.0 04.10.2004
+
+ *) Первая публично доступная версия.
+
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/LICENSE b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/LICENSE
new file mode 100644
index 00000000000..4ed7a6fc7c6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/LICENSE
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2002-2014 Igor Sysoev
+ * Copyright (C) 2011-2014 Nginx, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/README b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/README
new file mode 100644
index 00000000000..2f68e14e0d8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/README
@@ -0,0 +1,3 @@
+
+Documentation is available at http://nginx.org
+
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/acc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/acc
new file mode 100644
index 00000000000..6baee672ad4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/acc
@@ -0,0 +1,15 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+# aCC: HP ANSI C++ B3910B A.03.55.02
+
+# C89 mode
+
+CFLAGS="$CFLAGS -Ae"
+CC_TEST_FLAGS="-Ae"
+
+PCRE_OPT="$PCRE_OPT -Ae"
+ZLIB_OPT="$ZLIB_OPT -Ae"
+MD5_OPT="$MD5_OPT -Ae"
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/bcc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/bcc
new file mode 100644
index 00000000000..ec82e60fb13
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/bcc
@@ -0,0 +1,72 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+# Borland C++ 5.5
+
+# optimizations
+
+# maximize speed
+CFLAGS="$CFLAGS -O2"
+
+case $CPU in
+ pentium)
+ # optimize for Pentium and Athlon
+ CPU_OPT="-5"
+ ;;
+
+ pentiumpro)
+ # optimize for Pentium Pro, Pentium II and Pentium III
+ CPU_OPT="-6"
+ ;;
+esac
+
+# __stdcall
+#CPU_OPT="$CPU_OPT -ps"
+# __fastcall
+#CPU_OPT="$CPU_OPT -pr"
+
+CFLAGS="$CFLAGS $CPU_OPT"
+
+# multithreaded
+CFLAGS="$CFLAGS -tWM"
+
+# stop on warning
+CFLAGS="$CFLAGS -w!"
+
+# disable logo
+CFLAGS="$CFLAGS -q"
+
+
+# precompiled headers
+CORE_DEPS="$CORE_DEPS $NGX_OBJS/ngx_config.csm"
+NGX_PCH="$NGX_OBJS/ngx_config.csm"
+NGX_BUILD_PCH="-H=$NGX_OBJS/ngx_config.csm"
+NGX_USE_PCH="-Hu -H=$NGX_OBJS/ngx_config.csm"
+
+
+# Win32 GUI mode application
+#LINK="\$(CC) -laa"
+
+
+# the resource file
+NGX_RES="$NGX_OBJS/nginx.res"
+NGX_RCC="brcc32 -fo$NGX_OBJS/nginx.res \$(CORE_INCS) $NGX_WIN32_RC"
+# the pragma allows to link the resource file using bcc32 and
+# to avoid the direct ilink32 calling and the c0w32.obj's WinMain/main problem
+NGX_PRAGMA="#pragma resource \"$NGX_OBJS/nginx.res\""
+
+
+ngx_include_opt="-I"
+ngx_objout="-o"
+ngx_binout="-e"
+ngx_objext="obj"
+ngx_binext=".exe"
+
+ngx_long_start='@&&|
+ '
+ngx_long_end='|'
+
+ngx_regex_dirsep='\\'
+ngx_dirsep="\\"
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/ccc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/ccc
new file mode 100644
index 00000000000..c964045105d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/ccc
@@ -0,0 +1,46 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+# Compaq C V6.5-207
+
+ngx_include_opt="-I"
+
+# warnings
+
+CFLAGS="$CFLAGS -msg_enable level6 -msg_fatal level6"
+
+CFLAGS="$CFLAGS -msg_disable unknownmacro"
+CFLAGS="$CFLAGS -msg_disable unusedincl"
+CFLAGS="$CFLAGS -msg_disable unnecincl"
+CFLAGS="$CFLAGS -msg_disable nestincl"
+CFLAGS="$CFLAGS -msg_disable strctpadding"
+CFLAGS="$CFLAGS -msg_disable ansialiascast"
+CFLAGS="$CFLAGS -msg_disable inlinestoclsmod"
+CFLAGS="$CFLAGS -msg_disable cxxkeyword"
+CFLAGS="$CFLAGS -msg_disable longlongsufx"
+CFLAGS="$CFLAGS -msg_disable valuepres"
+
+# STUB
+CFLAGS="$CFLAGS -msg_disable truncintcast"
+CFLAGS="$CFLAGS -msg_disable trunclongcast"
+
+CFLAGS="$CFLAGS -msg_disable truncintasn"
+CFLAGS="$CFLAGS -msg_disable trunclongint"
+CFLAGS="$CFLAGS -msg_disable intconcastsgn"
+CFLAGS="$CFLAGS -msg_disable intconstsign"
+CFLAGS="$CFLAGS -msg_disable switchlong"
+CFLAGS="$CFLAGS -msg_disable subscrbounds2"
+
+CFLAGS="$CFLAGS -msg_disable hexoctunsign"
+
+CFLAGS="$CFLAGS -msg_disable ignorecallval"
+CFLAGS="$CFLAGS -msg_disable nonstandcast"
+CFLAGS="$CFLAGS -msg_disable embedcomment"
+CFLAGS="$CFLAGS -msg_disable unreachcode"
+CFLAGS="$CFLAGS -msg_disable questcompare2"
+CFLAGS="$CFLAGS -msg_disable unusedtop"
+CFLAGS="$CFLAGS -msg_disable unrefdecl"
+
+CFLAGS="$CFLAGS -msg_disable bitnotint"
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/clang b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/clang
new file mode 100644
index 00000000000..25707b42db2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/clang
@@ -0,0 +1,104 @@
+
+# Copyright (C) Nginx, Inc.
+
+
+# clang
+
+
+NGX_CLANG_VER=`$CC -v 2>&1 | grep '\(clang\|LLVM\) version' 2>&1 \
+ | sed -e 's/^.* version \(.*\)/\1/'`
+
+echo " + clang version: $NGX_CLANG_VER"
+
+have=NGX_COMPILER value="\"clang $NGX_CLANG_VER\"" . auto/define
+
+
+CC_TEST_FLAGS="-pipe"
+
+
+# optimizations
+
+#NGX_CLANG_OPT="-O2"
+#NGX_CLANG_OPT="-Oz"
+NGX_CLANG_OPT="-O"
+
+case $CPU in
+ pentium)
+ # optimize for Pentium
+ CPU_OPT="-march=pentium"
+ NGX_CPU_CACHE_LINE=32
+ ;;
+
+ pentiumpro | pentium3)
+ # optimize for Pentium Pro, Pentium II and Pentium III
+ CPU_OPT="-march=pentiumpro"
+ NGX_CPU_CACHE_LINE=32
+ ;;
+
+ pentium4)
+ # optimize for Pentium 4
+ CPU_OPT="-march=pentium4"
+ NGX_CPU_CACHE_LINE=128
+ ;;
+
+ athlon)
+ # optimize for Athlon
+ CPU_OPT="-march=athlon"
+ NGX_CPU_CACHE_LINE=64
+ ;;
+
+ opteron)
+ # optimize for Opteron
+ CPU_OPT="-march=opteron"
+ NGX_CPU_CACHE_LINE=64
+ ;;
+
+esac
+
+CC_AUX_FLAGS="$CC_AUX_FLAGS $CPU_OPT"
+
+
+CFLAGS="$CFLAGS -pipe $CPU_OPT"
+
+if [ ".$PCRE_OPT" = "." ]; then
+ PCRE_OPT="-O2 -pipe $CPU_OPT"
+else
+ PCRE_OPT="$PCRE_OPT -pipe"
+fi
+
+if [ ".$MD5_OPT" = "." ]; then
+ MD5_OPT="-O2 -pipe $CPU_OPT"
+else
+ MD5_OPT="$MD5_OPT -pipe"
+fi
+
+if [ ".$ZLIB_OPT" = "." ]; then
+ ZLIB_OPT="-O2 -pipe $CPU_OPT"
+else
+ ZLIB_OPT="$ZLIB_OPT -pipe"
+fi
+
+
+# warnings
+
+CFLAGS="$CFLAGS $NGX_CLANG_OPT -Wall -Wextra -Wpointer-arith"
+CFLAGS="$CFLAGS -Wconditional-uninitialized"
+#CFLAGS="$CFLAGS -Wmissing-prototypes"
+
+# we have a lot of unused function arguments
+CFLAGS="$CFLAGS -Wno-unused-parameter"
+
+# deprecated system OpenSSL library on OS X
+if [ "$NGX_SYSTEM" = "Darwin" ]; then
+ CFLAGS="$CFLAGS -Wno-deprecated-declarations"
+fi
+
+# stop on warning
+CFLAGS="$CFLAGS -Werror"
+
+# debug
+CFLAGS="$CFLAGS -g"
+
+if [ ".$CPP" = "." ]; then
+ CPP="$CC -E"
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/conf
new file mode 100644
index 00000000000..edc6d74ddfa
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/conf
@@ -0,0 +1,218 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+LINK="\$(CC)"
+
+ngx_include_opt="-I "
+ngx_compile_opt="-c"
+ngx_objout="-o "
+ngx_binout="-o "
+ngx_objext="o"
+ngx_binext=
+
+ngx_long_start=
+ngx_long_end=
+
+ngx_regex_dirsep="\/"
+ngx_dirsep='/'
+
+ngx_regex_cont=' \\\
+ '
+ngx_cont=' \
+ '
+ngx_tab=' \
+ '
+ngx_spacer=
+
+ngx_long_regex_cont=$ngx_regex_cont
+ngx_long_cont=$ngx_cont
+
+. auto/cc/name
+
+if test -n "$CFLAGS"; then
+
+ CC_TEST_FLAGS="$CFLAGS $NGX_CC_OPT"
+
+ case $NGX_CC_NAME in
+
+ ccc)
+ # Compaq C V6.5-207
+
+ ngx_include_opt="-I"
+ ;;
+
+ sunc)
+
+ case "$NGX_MACHINE" in
+
+ i86pc)
+ NGX_AUX=" src/os/unix/ngx_sunpro_x86.il"
+ ;;
+
+ sun4u | sun4v)
+ NGX_AUX=" src/os/unix/ngx_sunpro_sparc64.il"
+ ;;
+
+ esac
+
+ case $CPU in
+
+ amd64)
+ NGX_AUX=" src/os/unix/ngx_sunpro_amd64.il"
+ ;;
+
+ esac
+ ;;
+
+ esac
+
+else
+
+ case $NGX_CC_NAME in
+ gcc)
+ # gcc 2.7.2.3, 2.8.1, 2.95.4, egcs-1.1.2
+ # 3.0.4, 3.1.1, 3.2.3, 3.3.2, 3.3.3, 3.3.4, 3.4.0, 3.4.2
+ # 4.0.0, 4.0.1, 4.1.0
+
+ . auto/cc/gcc
+ ;;
+
+ clang)
+ # Clang C compiler
+
+ . auto/cc/clang
+ ;;
+
+ icc)
+ # Intel C++ compiler 7.1, 8.0, 8.1
+
+ . auto/cc/icc
+ ;;
+
+ sunc)
+ # Sun C 5.7 Patch 117837-04 2005/05/11
+
+ . auto/cc/sunc
+ ;;
+
+ ccc)
+ # Compaq C V6.5-207
+
+ . auto/cc/ccc
+ ;;
+
+ acc)
+ # aCC: HP ANSI C++ B3910B A.03.55.02
+
+ . auto/cc/acc
+ ;;
+
+ msvc*)
+ # MSVC++ 6.0 SP2, MSVC++ Toolkit 2003
+
+ . auto/cc/msvc
+ ;;
+
+ owc)
+ # Open Watcom C 1.0, 1.2
+
+ . auto/cc/owc
+ ;;
+
+ bcc)
+ # Borland C++ 5.5
+
+ . auto/cc/bcc
+ ;;
+
+ esac
+
+ CC_TEST_FLAGS="$CC_TEST_FLAGS $NGX_CC_OPT"
+
+fi
+
+CFLAGS="$CFLAGS $NGX_CC_OPT"
+NGX_TEST_LD_OPT="$NGX_LD_OPT"
+
+if [ "$NGX_PLATFORM" != win32 ]; then
+
+ if test -n "$NGX_LD_OPT"; then
+ ngx_feature=--with-ld-opt=\"$NGX_LD_OPT\"
+ ngx_feature_name=
+ ngx_feature_run=no
+ ngx_feature_incs=
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test=
+ . auto/feature
+
+ if [ $ngx_found = no ]; then
+ echo $0: error: the invalid value in --with-ld-opt=\"$NGX_LD_OPT\"
+ echo
+ exit 1
+ fi
+ fi
+
+
+ ngx_feature="gcc builtin atomic operations"
+ ngx_feature_name=NGX_HAVE_GCC_ATOMIC
+ ngx_feature_run=yes
+ ngx_feature_incs=
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test="long n = 0;
+ if (!__sync_bool_compare_and_swap(&n, 0, 1))
+ return 1;
+ if (__sync_fetch_and_add(&n, 1) != 1)
+ return 1;
+ if (n != 2)
+ return 1;
+ __sync_synchronize();"
+ . auto/feature
+
+
+ if [ "$NGX_CC_NAME" = "ccc" ]; then
+ echo "checking for C99 variadic macros ... disabled"
+ else
+ ngx_feature="C99 variadic macros"
+ ngx_feature_name="NGX_HAVE_C99_VARIADIC_MACROS"
+ ngx_feature_run=yes
+ ngx_feature_incs="#include <stdio.h>
+#define var(dummy, ...) sprintf(__VA_ARGS__)"
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test="char buf[30]; buf[0] = '0';
+ var(0, buf, \"%d\", 1);
+ if (buf[0] != '1') return 1"
+ . auto/feature
+ fi
+
+
+ ngx_feature="gcc variadic macros"
+ ngx_feature_name="NGX_HAVE_GCC_VARIADIC_MACROS"
+ ngx_feature_run=yes
+ ngx_feature_incs="#include <stdio.h>
+#define var(dummy, args...) sprintf(args)"
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test="char buf[30]; buf[0] = '0';
+ var(0, buf, \"%d\", 1);
+ if (buf[0] != '1') return 1"
+ . auto/feature
+
+
+# ngx_feature="inline"
+# ngx_feature_name=
+# ngx_feature_run=no
+# ngx_feature_incs="int inline f(void) { return 1 }"
+# ngx_feature_path=
+# ngx_feature_libs=
+# ngx_feature_test=
+# . auto/feature
+#
+# if [ $ngx_found = yes ]; then
+# fi
+
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/gcc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/gcc
new file mode 100644
index 00000000000..727f11e1dfc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/gcc
@@ -0,0 +1,186 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+# gcc 2.7.2.3, 2.8.1, 2.95.4, egcs-1.1.2
+# 3.0.4, 3.1.1, 3.2.3, 3.3.2, 3.3.3, 3.3.4, 3.4.0, 3.4.2
+# 4.0.0, 4.0.1, 4.1.0
+
+
+NGX_GCC_VER=`$CC -v 2>&1 | grep 'gcc version' 2>&1 \
+ | sed -e 's/^.* version \(.*\)/\1/'`
+
+echo " + gcc version: $NGX_GCC_VER"
+
+have=NGX_COMPILER value="\"gcc $NGX_GCC_VER\"" . auto/define
+
+
+# Solaris 7's /usr/ccs/bin/as does not support "-pipe"
+
+CC_TEST_FLAGS="-pipe"
+
+ngx_feature="gcc -pipe switch"
+ngx_feature_name=
+ngx_feature_run=no
+ngx_feature_incs=
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test=
+. auto/feature
+
+CC_TEST_FLAGS=
+
+if [ $ngx_found = yes ]; then
+ PIPE="-pipe"
+fi
+
+
+case "$NGX_MACHINE" in
+
+ sun4u | sun4v | sparc | sparc64 )
+ # "-mcpu=v9" enables the "casa" assembler instruction
+ CFLAGS="$CFLAGS -mcpu=v9"
+ ;;
+
+esac
+
+
+# optimizations
+
+#NGX_GCC_OPT="-O2"
+#NGX_GCC_OPT="-Os"
+NGX_GCC_OPT="-O"
+
+#CFLAGS="$CFLAGS -fomit-frame-pointer"
+
+case $CPU in
+ pentium)
+ # optimize for Pentium and Athlon
+ CPU_OPT="-march=pentium"
+ NGX_CPU_CACHE_LINE=32
+ ;;
+
+ pentiumpro | pentium3)
+ # optimize for Pentium Pro, Pentium II and Pentium III
+ CPU_OPT="-march=pentiumpro"
+ NGX_CPU_CACHE_LINE=32
+ ;;
+
+ pentium4)
+ # optimize for Pentium 4, gcc 3.x
+ CPU_OPT="-march=pentium4"
+ NGX_CPU_CACHE_LINE=128
+ ;;
+
+ athlon)
+ # optimize for Athlon, gcc 3.x
+ CPU_OPT="-march=athlon"
+ NGX_CPU_CACHE_LINE=64
+ ;;
+
+ opteron)
+ # optimize for Opteron, gcc 3.x
+ CPU_OPT="-march=opteron"
+ NGX_CPU_CACHE_LINE=64
+ ;;
+
+ sparc32)
+ # build 32-bit UltraSparc binary
+ CPU_OPT="-m32"
+ CORE_LINK="$CORE_LINK -m32"
+ NGX_CPU_CACHE_LINE=64
+ ;;
+
+ sparc64)
+ # build 64-bit UltraSparc binary
+ CPU_OPT="-m64"
+ CORE_LINK="$CORE_LINK -m64"
+ NGX_CPU_CACHE_LINE=64
+ ;;
+
+ ppc64)
+ # build 64-bit PowerPC binary
+ CPU_OPT="-m64"
+ CPU_OPT="$CPU_OPT -falign-functions=32 -falign-labels=32"
+ CPU_OPT="$CPU_OPT -falign-loops=32 -falign-jumps=32"
+ CORE_LINK="$CORE_LINK -m64"
+ NGX_CPU_CACHE_LINE=128
+ ;;
+
+esac
+
+CC_AUX_FLAGS="$CC_AUX_FLAGS $CPU_OPT"
+
+case "$NGX_GCC_VER" in
+ 2.7*)
+ # batch build
+ CPU_OPT=
+ ;;
+esac
+
+
+CFLAGS="$CFLAGS $PIPE $CPU_OPT"
+
+if [ ".$PCRE_OPT" = "." ]; then
+ PCRE_OPT="-O2 -fomit-frame-pointer $PIPE $CPU_OPT"
+else
+ PCRE_OPT="$PCRE_OPT $PIPE"
+fi
+
+if [ ".$MD5_OPT" = "." ]; then
+ MD5_OPT="-O2 -fomit-frame-pointer $PIPE $CPU_OPT"
+else
+ MD5_OPT="$MD5_OPT $PIPE"
+fi
+
+if [ ".$ZLIB_OPT" = "." ]; then
+ ZLIB_OPT="-O2 -fomit-frame-pointer $PIPE $CPU_OPT"
+else
+ ZLIB_OPT="$ZLIB_OPT $PIPE"
+fi
+
+
+# warnings
+
+# -W requires at least -O
+CFLAGS="$CFLAGS ${NGX_GCC_OPT:--O} -W"
+
+CFLAGS="$CFLAGS -Wall -Wpointer-arith"
+#CFLAGS="$CFLAGS -Wconversion"
+#CFLAGS="$CFLAGS -Winline"
+#CFLAGS="$CFLAGS -Wmissing-prototypes"
+
+
+case "$NGX_GCC_VER" in
+ 3.* | 4.* )
+ # we have a lot of the unused function arguments
+ CFLAGS="$CFLAGS -Wno-unused-parameter"
+ # 4.2.1 shows the warning in wrong places
+ #CFLAGS="$CFLAGS -Wunreachable-code"
+
+ # deprecated system OpenSSL library on OS X
+ if [ "$NGX_SYSTEM" = "Darwin" ]; then
+ CFLAGS="$CFLAGS -Wno-deprecated-declarations"
+ fi
+ ;;
+
+ *)
+ # we have a lot of the unused function arguments
+ CFLAGS="$CFLAGS -Wno-unused"
+ ;;
+esac
+
+
+# stop on warning
+CFLAGS="$CFLAGS -Werror"
+
+# debug
+CFLAGS="$CFLAGS -g"
+
+# DragonFly's gcc3 generates DWARF
+#CFLAGS="$CFLAGS -g -gstabs"
+
+if [ ".$CPP" = "." ]; then
+ CPP="$CC -E"
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/icc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/icc
new file mode 100644
index 00000000000..1d83ed37a03
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/icc
@@ -0,0 +1,121 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+# Intel C++ compiler 7.1, 8.0, 8.1, 9.0, 11.1
+
+NGX_ICC_VER=`$CC -V 2>&1 | grep 'Version' 2>&1 \
+ | sed -e 's/^.* Version \([^ ]*\) *Build.*$/\1/'`
+
+echo " + icc version: $NGX_ICC_VER"
+
+have=NGX_COMPILER value="\"Intel C Compiler $NGX_ICC_VER\"" . auto/define
+
+
+# optimizations
+
+CFLAGS="$CFLAGS -O"
+
+CORE_LINK="$CORE_LINK -opt_report_file=$NGX_OBJS/opt_report_file"
+
+
+case $CPU in
+ pentium)
+ # optimize for Pentium and Athlon
+ CPU_OPT="-march=pentium"
+ ;;
+
+ pentiumpro)
+ # optimize for Pentium Pro, Pentium II and Pentium III
+ CPU_OPT="-mcpu=pentiumpro -march=pentiumpro"
+ ;;
+
+ pentium4)
+ # optimize for Pentium 4, default
+ CPU_OPT="-march=pentium4"
+ ;;
+esac
+
+CFLAGS="$CFLAGS $CPU_OPT"
+
+if [ ".$PCRE_OPT" = "." ]; then
+ PCRE_OPT="-O $CPU_OPT"
+fi
+
+if [ ".$MD5_OPT" = "." ]; then
+ MD5_OPT="-O $CPU_OPT"
+fi
+
+if [ ".$ZLIB_OPT" = "." ]; then
+ ZLIB_OPT="-O $CPU_OPT"
+fi
+
+
+# warnings
+
+CFLAGS="$CFLAGS -w2"
+
+# disable some warnings
+
+# invalid type conversion: "int" to "char *"
+CFLAGS="$CFLAGS -wd171"
+# argument is incompatible with corresponding format string conversion
+CFLAGS="$CFLAGS -wd181"
+# zero used for undefined preprocessing identifier
+CFLAGS="$CFLAGS -wd193"
+# the format string ends before this argument
+CFLAGS="$CFLAGS -wd268"
+# invalid format string conversion
+CFLAGS="$CFLAGS -wd269"
+# conversion from "long long" to "size_t" may lose significant bits
+CFLAGS="$CFLAGS -wd810"
+# parameter was never referenced
+CFLAGS="$CFLAGS -wd869"
+# attribute "unused" is only allowed in a function definition, warning on pTHX_
+CFLAGS="$CFLAGS -wd1301"
+
+# STUB
+# enumerated type mixed with another type
+CFLAGS="$CFLAGS -wd188"
+# controlling expression is constant
+CFLAGS="$CFLAGS -wd279"
+# operands are evaluated in unspecified order
+CFLAGS="$CFLAGS -wd981"
+# external definition with no prior declaration
+CFLAGS="$CFLAGS -wd1418"
+# external declaration in primary source file
+CFLAGS="$CFLAGS -wd1419"
+
+case "$NGX_ICC_VER" in
+ 9.*)
+ # "cc" clobber ignored, warnings for Liunx's htonl()/htons()
+ CFLAGS="$CFLAGS -wd1469"
+ # explicit conversion of a 64-bit integral type to a smaller
+ # integral type
+ CFLAGS="$CFLAGS -wd1683"
+ # conversion from pointer to same-sized integral type,
+ # warning on offsetof()
+ CFLAGS="$CFLAGS -wd1684"
+ # floating-point equality and inequality comparisons are unreliable,
+ # warning on SvTRUE()
+ CFLAGS="$CFLAGS -wd1572"
+ ;;
+
+ 8.*)
+ # "cc" clobber ignored, warnings for Liunx's htonl()/htons()
+ CFLAGS="$CFLAGS -wd1469"
+ # floating-point equality and inequality comparisons are unreliable,
+ # warning on SvTRUE()
+ CFLAGS="$CFLAGS -wd1572"
+ ;;
+
+ *)
+ ;;
+esac
+
+# stop on warning
+CFLAGS="$CFLAGS -Werror"
+
+# debug
+CFLAGS="$CFLAGS -g"
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/msvc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/msvc
new file mode 100644
index 00000000000..393ba321499
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/msvc
@@ -0,0 +1,136 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+# MSVC 6.0 SP2
+# MSVC Toolkit 2003 (7.1)
+# MSVC 2005 Express Edition SP1 (8.0)
+
+# optimizations
+
+# maximize speed, equivalent to -Og -Oi -Ot -Oy -Ob2 -Gs -GF -Gy
+CFLAGS="$CFLAGS -O2"
+
+# enable global optimization
+#CFLAGS="$CFLAGS -Og"
+# enable intrinsic functions
+#CFLAGS="$CFLAGS -Oi"
+
+# disable inline expansion
+#CFLAGS="$CFLAGS -Ob0"
+# explicit inline expansion
+#CFLAGS="$CFLAGS -Ob1"
+# explicit and implicit inline expansion
+#CFLAGS="$CFLAGS -Ob2"
+
+# enable frame pointer omission
+#CFLAGS="$CFLAGS -Oy"
+# disable stack checking calls
+#CFLAGS="$CFLAGS -Gs"
+
+# pools strings as read/write
+#CFLAGS="$CFLAGS -Gf"
+# pools strings as read-only
+#CFLAGS="$CFLAGS -GF"
+
+
+case $CPU in
+ pentium)
+ # optimize for Pentium and Athlon
+ CPU_OPT="-G5"
+ ;;
+
+ pentiumpro)
+ # optimize for Pentium Pro, Pentium II and Pentium III
+ CPU_OPT="-G6"
+ ;;
+
+ pentium4)
+ # optimize for Pentium 4, MSVC 7
+ CPU_OPT="-G7"
+ ;;
+esac
+
+# __cdecl, default, must be used with OpenSSL, md5 asm, and sha1 asm
+#CPU_OPT="$CPU_OPT -Gd"
+# __stdcall
+#CPU_OPT="$CPU_OPT -Gz"
+# __fastcall
+#CPU_OPT="$CPU_OPT -Gr"
+
+
+CFLAGS="$CFLAGS $CPU_OPT"
+
+
+# warnings
+
+CFLAGS="$CFLAGS -W4"
+
+# stop on warning
+CFLAGS="$CFLAGS -WX"
+
+# disable logo
+CFLAGS="$CFLAGS -nologo"
+
+# the link flags
+CORE_LINK="$CORE_LINK -link -verbose:lib"
+
+# link with libcmt.lib, multithreaded
+LIBC="-MT"
+# link with msvcrt.dll
+# however, MSVC Toolkit 2003 has no MSVCRT.LIB
+#LIBC="-MD"
+
+CFLAGS="$CFLAGS $LIBC"
+
+CORE_LIBS="$CORE_LIBS kernel32.lib user32.lib"
+
+# Win32 GUI mode application
+#CORE_LINK="$CORE_LINK -subsystem:windows -entry:mainCRTStartup"
+
+# debug
+# msvc8 under Wine issues
+# Program database manager mismatch; please check your installation
+if [ $NGX_CC_NAME != msvc8 ]; then
+ CFLAGS="$CFLAGS -Zi"
+ CORE_LINK="$CORE_LINK -debug"
+fi
+
+
+# MSVC 2005 supports C99 variadic macros
+if [ $NGX_CC_NAME = msvc8 ]; then
+ have=NGX_HAVE_C99_VARIADIC_MACROS . auto/have
+fi
+
+
+# precompiled headers
+CORE_DEPS="$CORE_DEPS $NGX_OBJS/ngx_config.pch"
+CORE_LINK="$CORE_LINK $NGX_OBJS/ngx_pch.obj"
+NGX_PCH="$NGX_OBJS/ngx_config.pch"
+NGX_BUILD_PCH="-Ycngx_config.h -Fp$NGX_OBJS/ngx_config.pch"
+NGX_USE_PCH="-Yungx_config.h -Fp$NGX_OBJS/ngx_config.pch"
+
+
+# the resource file
+NGX_RES="$NGX_OBJS/nginx.res"
+NGX_RCC="rc -fo$NGX_RES \$(CORE_INCS) $NGX_WIN32_RC"
+CORE_LINK="$NGX_RES $CORE_LINK"
+
+
+ngx_objout="-Fo"
+ngx_binout="-Fe"
+ngx_objext="obj"
+ngx_binext=".exe"
+
+ngx_long_start='@<<
+ '
+ngx_long_end='<<'
+ngx_long_regex_cont=' \
+ '
+ngx_long_cont='
+ '
+
+# MSVC understand / in path
+#ngx_regex_dirsep='\\'
+#ngx_dirsep="\\"
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/name b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/name
new file mode 100644
index 00000000000..51a7ed92e1d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/name
@@ -0,0 +1,89 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+if [ "$NGX_PLATFORM" != win32 ]; then
+
+ ngx_feature="C compiler"
+ ngx_feature_name=
+ ngx_feature_run=yes
+ ngx_feature_incs=
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test=
+ . auto/feature
+
+ if [ $ngx_found = no ]; then
+ echo
+ echo $0: error: C compiler $CC is not found
+ echo
+ exit 1
+ fi
+
+fi
+
+
+if [ "$CC" = cl ]; then
+ if `$NGX_WINE $CC -v 2>&1 \
+ | grep '^Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16' \
+ >/dev/null 2>&1`; then
+
+ NGX_CC_NAME=msvc10
+ echo " + using Microsoft Visual C++ 10 compiler"
+
+ elif `$NGX_WINE $CC -v 2>&1 \
+ | grep '^Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14' \
+ >/dev/null 2>&1`; then
+
+ NGX_CC_NAME=msvc8
+ echo " + using Microsoft Visual C++ 8 compiler"
+
+ elif `$NGX_WINE $CC -v 2>&1 \
+ | grep '^Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 13' \
+ >/dev/null 2>&1`; then
+
+ NGX_CC_NAME=msvc7
+ echo " + using Microsoft Visual C++ 7 compiler"
+
+ else
+ NGX_CC_NAME=msvc
+ echo " + using Microsoft Visual C++ compiler"
+ fi
+
+elif [ "$CC" = wcl386 ]; then
+ NGX_CC_NAME=owc
+ echo " + using Open Watcom C compiler"
+
+elif [ "$CC" = bcc32 ]; then
+ NGX_CC_NAME=bcc
+ echo " + using Borland C++ compiler"
+
+elif `$CC -V 2>&1 | grep '^Intel(R) C' >/dev/null 2>&1`; then
+ NGX_CC_NAME=icc
+ echo " + using Intel C++ compiler"
+
+elif `$CC -v 2>&1 | grep 'gcc version' >/dev/null 2>&1`; then
+ NGX_CC_NAME=gcc
+ echo " + using GNU C compiler"
+
+elif `$CC -v 2>&1 | grep '\(clang\|LLVM\) version' >/dev/null 2>&1`; then
+ NGX_CC_NAME=clang
+ echo " + using Clang C compiler"
+
+elif `$CC -V 2>&1 | grep 'Sun C' >/dev/null 2>&1`; then
+ NGX_CC_NAME=sunc
+ echo " + using Sun C compiler"
+
+elif `$CC -V 2>&1 | grep '^Compaq C' >/dev/null 2>&1`; then
+ NGX_CC_NAME=ccc
+ echo " + using Compaq C compiler"
+
+elif `$CC -V 2>&1 | grep '^aCC: ' >/dev/null 2>&1`; then
+ NGX_CC_NAME=acc
+ echo " + using HP aC++ compiler"
+
+else
+ NGX_CC_NAME=unknown
+
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/owc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/owc
new file mode 100644
index 00000000000..a063aa34121
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/owc
@@ -0,0 +1,104 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+# Open Watcom C 1.0, 1.2, 1.3
+
+# optimizations
+
+# maximize speed
+CFLAGS="$CFLAGS -ot"
+# reorder instructions for best pipeline usage
+CFLAGS="$CFLAGS -op"
+# inline intrinsic functions
+CFLAGS="$CFLAGS -oi"
+# inline expansion
+CFLAGS="$CFLAGS -oe"
+# disable stack checking calls
+CFLAGS="$CFLAGS -s"
+
+case $CPU in
+ pentium)
+ # optimize for Pentium and Athlon
+ # register-based arguments passing conventions
+ CPU_OPT="-5r"
+ # stack-based arguments passing conventions
+ #CPU_OPT="-5s"
+ ;;
+
+ pentiumpro)
+ # optimize for Pentium Pro, Pentium II and Pentium III
+ # register-based arguments passing conventions
+ CPU_OPT="-6r"
+ # stack-based arguments passing conventions
+ #CPU_OPT="-6s"
+ ;;
+esac
+
+CFLAGS="$CFLAGS $CPU_OPT"
+
+
+# warnings
+
+# maximum level
+CFLAGS="$CFLAGS -wx"
+#CFLAGS="$CFLAGS -w3"
+
+# stop on warning
+CFLAGS="$CFLAGS -we"
+
+# built target is NT
+CFLAGS="$CFLAGS -bt=nt"
+
+# multithreaded
+CFLAGS="$CFLAGS -bm"
+
+# debug
+CFLAGS="$CFLAGS -d2"
+
+# quiet
+CFLAGS="$CFLAGS -zq"
+
+# Open Watcom C 1.2
+have=NGX_HAVE_C99_VARIADIC_MACROS . auto/have
+
+
+# the precompiled headers
+#CORE_DEPS="$CORE_DEPS $NGX_OBJS/ngx_config.pch"
+#NGX_PCH="$NGX_OBJS/ngx_config.pch"
+#NGX_BUILD_PCH="-fhq=$NGX_OBJS/ngx_config.pch"
+#NGX_USE_PCH="-fh=$NGX_OBJS/ngx_config.pch"
+
+
+# the link flags, built target is NT GUI mode application
+#CORE_LINK="$CORE_LINK -l=nt_win"
+
+
+# the resource file
+NGX_RCC="wrc \$(CORE_INCS) -fo=$NGX_OBJS/nginx.res "
+NGX_RCC="$NGX_RCC $NGX_WIN32_RC $NGX_OBJS/nginx.exe"
+
+
+ngx_include_opt="-i="
+ngx_objout="-fo"
+ngx_binout="-fe="
+ngx_objext="obj"
+ngx_binext=".exe"
+
+ngx_regex_dirsep='\\'
+ngx_dirsep="\\"
+
+ngx_long_start=' '
+ngx_long_end=' '
+ngx_long_regex_cont=' \&\
+ '
+ngx_long_cont=' &
+ '
+
+ngx_regex_cont=' \&\
+ '
+ngx_cont=' &
+ '
+ngx_tab=' &
+ '
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/sunc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/sunc
new file mode 100644
index 00000000000..8f12d7cd7fe
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/cc/sunc
@@ -0,0 +1,158 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+# Sun C 5.7 Patch 117837-04 2005/05/11 Sun Studio 10
+# Sun C 5.8 2005/10/13 Sun Studio 11
+# Sun C 5.9 SunOS_i386 2007/05/03 Sun Studio 12
+# Sun C 5.9 SunOS_sparc 2007/05/03
+# Sun C 5.10 SunOS_i386 2009/06/03 Sun Studio 12.1
+# Sun C 5.11 SunOS_i386 2010/08/13 Sun Studio 12.2
+
+NGX_SUNC_VER=`$CC -V 2>&1 | grep 'Sun C' 2>&1 \
+ | sed -e 's/^.* Sun C \(.*\)/\1/'`
+
+echo " + Sun C version: $NGX_SUNC_VER"
+
+have=NGX_COMPILER value="\"Sun C $NGX_SUNC_VER\"" . auto/define
+
+
+cat << END > $NGX_AUTOTEST.c
+
+int main() { printf("%d", __SUNPRO_C); }
+
+END
+
+eval "$CC -o $NGX_AUTOTEST $NGX_AUTOTEST.c >> $NGX_ERR 2>&1"
+
+if [ -x $NGX_AUTOTEST ]; then
+ ngx_sunc_ver=`$NGX_AUTOTEST`
+fi
+
+rm -rf $NGX_AUTOTEST*
+
+# 1424 == 0x590, Sun Studio 12
+
+if [ "$ngx_sunc_ver" -ge 1424 ]; then
+ ngx_sparc32="-m32"
+ ngx_sparc64="-m64"
+ ngx_amd64="-m64"
+
+else
+ ngx_sparc32="-xarch=v8plus"
+ ngx_sparc64="-xarch=v9"
+ ngx_amd64="-xarch=amd64"
+fi
+
+case "$NGX_MACHINE" in
+
+ i86pc)
+ NGX_AUX=" src/os/unix/ngx_sunpro_x86.il"
+ ;;
+
+ sun4u | sun4v)
+ NGX_AUX=" src/os/unix/ngx_sunpro_sparc64.il"
+ ;;
+
+esac
+
+
+# optimizations
+
+# 20736 == 0x5100, Sun Studio 12.1
+
+if [ "$ngx_sunc_ver" -ge 20736 ]; then
+ ngx_fast="-fast"
+
+else
+ # older versions had problems with bit-fields
+ ngx_fast="-fast -xalias_level=any"
+fi
+
+IPO=-xipo
+CFLAGS="$CFLAGS $ngx_fast $IPO"
+CORE_LINK="$CORE_LINK $ngx_fast $IPO"
+
+
+case $CPU in
+ pentium)
+ # optimize for Pentium and Athlon
+ CPU_OPT="-xchip=pentium"
+ ;;
+
+ pentiumpro)
+ # optimize for Pentium Pro, Pentium II
+ CPU_OPT="-xchip=pentium_pro"
+ ;;
+
+ pentium3)
+ # optimize for Pentium III
+ CPU_OPT="-xchip=pentium3"
+ #CPU_OPT="$CPU_OPT -xarch=sse"
+ CPU_OPT="$CPU_OPT -xcache=16/32/4:256/32/4"
+ ;;
+
+ pentium4)
+ # optimize for Pentium 4
+ CPU_OPT="-xchip=pentium4"
+ #CPU_OPT="$CPU_OPT -xarch=sse2"
+ CPU_OPT="$CPU_OPT -xcache=8/64/4:256/128/8"
+ ;;
+
+ opteron)
+ # optimize for Opteron
+ CPU_OPT="-xchip=opteron"
+ #CPU_OPT="$CPU_OPT -xarch=sse2"
+ CPU_OPT="$CPU_OPT -xcache=64/64/2:1024/64/16"
+ ;;
+
+ sparc32)
+ # build 32-bit UltraSparc binary
+ CPU_OPT="$ngx_sparc32"
+ CORE_LINK="$CORE_LINK $ngx_sparc32"
+ CC_AUX_FLAGS="$CC_AUX_FLAGS $ngx_sparc32"
+ NGX_CPU_CACHE_LINE=64
+ ;;
+
+ sparc64)
+ # build 64-bit UltraSparc binary
+ CPU_OPT="$ngx_sparc64"
+ CORE_LINK="$CORE_LINK $ngx_sparc64"
+ CC_AUX_FLAGS="$CC_AUX_FLAGS $ngx_sparc64"
+ NGX_CPU_CACHE_LINE=64
+ ;;
+
+ amd64)
+ # build 64-bit amd64 binary
+ CPU_OPT="$ngx_amd64"
+ CORE_LINK="$CORE_LINK $ngx_amd64"
+ CC_AUX_FLAGS="$CC_AUX_FLAGS $ngx_amd64"
+ NGX_AUX=" src/os/unix/ngx_sunpro_amd64.il"
+ NGX_CPU_CACHE_LINE=64
+ ;;
+
+esac
+
+
+CFLAGS="$CFLAGS $CPU_OPT"
+
+
+if [ ".$PCRE_OPT" = "." ]; then
+ PCRE_OPT="$ngx_fast $IPO $CPU_OPT"
+fi
+
+if [ ".$MD5_OPT" = "." ]; then
+ MD5_OPT="$ngx_fast $IPO $CPU_OPT"
+fi
+
+if [ ".$ZLIB_OPT" = "." ]; then
+ ZLIB_OPT="$ngx_fast $IPO $CPU_OPT"
+fi
+
+
+# stop on warning
+CFLAGS="$CFLAGS -errwarn=%all"
+
+# debug
+CFLAGS="$CFLAGS -g"
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/define b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/define
new file mode 100644
index 00000000000..b5a7622590d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/define
@@ -0,0 +1,12 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+cat << END >> $NGX_AUTO_CONFIG_H
+
+#ifndef $have
+#define $have $value
+#endif
+
+END
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/endianness b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/endianness
new file mode 100644
index 00000000000..93da2f80d99
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/endianness
@@ -0,0 +1,45 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+echo $ngx_n "checking for system byte ordering ...$ngx_c"
+echo >> $NGX_ERR
+echo "checking for system byte ordering" >> $NGX_ERR
+
+
+cat << END > $NGX_AUTOTEST.c
+
+int main() {
+ int i = 0x11223344;
+ char *p;
+
+ p = (char *) &i;
+ if (*p == 0x44) return 0;
+ return 1;
+}
+
+END
+
+ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \
+ -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT $ngx_feature_libs"
+
+eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"
+
+if [ -x $NGX_AUTOTEST ]; then
+ if $NGX_AUTOTEST >/dev/null 2>&1; then
+ echo " little endian"
+ have=NGX_HAVE_LITTLE_ENDIAN . auto/have
+ else
+ echo " big endian"
+ fi
+
+ rm -rf $NGX_AUTOTEST*
+
+else
+ rm -rf $NGX_AUTOTEST*
+
+ echo
+ echo "$0: error: cannot detect system byte ordering"
+ exit 1
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/feature b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/feature
new file mode 100644
index 00000000000..1145f28684b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/feature
@@ -0,0 +1,123 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+echo $ngx_n "checking for $ngx_feature ...$ngx_c"
+
+cat << END >> $NGX_AUTOCONF_ERR
+
+----------------------------------------
+checking for $ngx_feature
+
+END
+
+ngx_found=no
+
+if test -n "$ngx_feature_name"; then
+ ngx_have_feature=`echo $ngx_feature_name \
+ | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ`
+fi
+
+if test -n "$ngx_feature_path"; then
+ for ngx_temp in $ngx_feature_path; do
+ ngx_feature_inc_path="$ngx_feature_inc_path -I $ngx_temp"
+ done
+fi
+
+cat << END > $NGX_AUTOTEST.c
+
+#include <sys/types.h>
+$NGX_INCLUDE_UNISTD_H
+$ngx_feature_incs
+
+int main() {
+ $ngx_feature_test;
+ return 0;
+}
+
+END
+
+
+ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS $ngx_feature_inc_path \
+ -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_TEST_LD_OPT $ngx_feature_libs"
+
+ngx_feature_inc_path=
+
+eval "/bin/sh -c \"$ngx_test\" >> $NGX_AUTOCONF_ERR 2>&1"
+
+
+if [ -x $NGX_AUTOTEST ]; then
+
+ case "$ngx_feature_run" in
+
+ yes)
+ # /bin/sh is used to intercept "Killed" or "Abort trap" messages
+ if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+
+ else
+ echo " found but is not working"
+ fi
+ ;;
+
+ value)
+ # /bin/sh is used to intercept "Killed" or "Abort trap" messages
+ if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
+ echo " found"
+ ngx_found=yes
+
+ cat << END >> $NGX_AUTO_CONFIG_H
+
+#ifndef $ngx_feature_name
+#define $ngx_feature_name `$NGX_AUTOTEST`
+#endif
+
+END
+ else
+ echo " found but is not working"
+ fi
+ ;;
+
+ bug)
+ # /bin/sh is used to intercept "Killed" or "Abort trap" messages
+ if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then
+ echo " not found"
+
+ else
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+ fi
+ ;;
+
+ *)
+ echo " found"
+ ngx_found=yes
+
+ if test -n "$ngx_feature_name"; then
+ have=$ngx_have_feature . auto/have
+ fi
+ ;;
+
+ esac
+
+else
+ echo " not found"
+
+ echo "----------" >> $NGX_AUTOCONF_ERR
+ cat $NGX_AUTOTEST.c >> $NGX_AUTOCONF_ERR
+ echo "----------" >> $NGX_AUTOCONF_ERR
+ echo $ngx_test >> $NGX_AUTOCONF_ERR
+ echo "----------" >> $NGX_AUTOCONF_ERR
+fi
+
+rm -rf $NGX_AUTOTEST*
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/have b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/have
new file mode 100644
index 00000000000..f8e3751c5f0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/have
@@ -0,0 +1,12 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+cat << END >> $NGX_AUTO_CONFIG_H
+
+#ifndef $have
+#define $have 1
+#endif
+
+END
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/have_headers b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/have_headers
new file mode 100644
index 00000000000..a3a75430aa4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/have_headers
@@ -0,0 +1,12 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+cat << END >> $NGX_AUTO_HEADERS_H
+
+#ifndef $have
+#define $have 1
+#endif
+
+END
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/headers b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/headers
new file mode 100644
index 00000000000..5a2e6b9023f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/headers
@@ -0,0 +1,13 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+ngx_include="unistd.h"; . auto/include
+ngx_include="inttypes.h"; . auto/include
+ngx_include="limits.h"; . auto/include
+ngx_include="sys/filio.h"; . auto/include
+ngx_include="sys/param.h"; . auto/include
+ngx_include="sys/mount.h"; . auto/include
+ngx_include="sys/statvfs.h"; . auto/include
+ngx_include="crypt.h"; . auto/include
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/include b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/include
new file mode 100644
index 00000000000..e34dabdae1d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/include
@@ -0,0 +1,61 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+echo $ngx_n "checking for $ngx_include ...$ngx_c"
+
+cat << END >> $NGX_AUTOCONF_ERR
+
+----------------------------------------
+checking for $ngx_include
+
+END
+
+
+ngx_found=no
+
+cat << END > $NGX_AUTOTEST.c
+
+$NGX_INCLUDE_SYS_PARAM_H
+#include <$ngx_include>
+
+int main() {
+ return 0;
+}
+
+END
+
+
+ngx_test="$CC -o $NGX_AUTOTEST $NGX_AUTOTEST.c"
+
+eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"
+
+if [ -x $NGX_AUTOTEST ]; then
+
+ ngx_found=yes
+
+ echo " found"
+
+ ngx_name=`echo $ngx_include \
+ | tr abcdefghijklmnopqrstuvwxyz/. ABCDEFGHIJKLMNOPQRSTUVWXYZ__`
+
+
+ have=NGX_HAVE_$ngx_name . auto/have_headers
+
+ eval "NGX_INCLUDE_$ngx_name='#include <$ngx_include>'"
+
+ #STUB
+ eval "NGX_$ngx_name='#include <$ngx_include>'"
+
+else
+ echo " not found"
+
+ echo "----------" >> $NGX_AUTOCONF_ERR
+ cat $NGX_AUTOTEST.c >> $NGX_AUTOCONF_ERR
+ echo "----------" >> $NGX_AUTOCONF_ERR
+ echo $ngx_test >> $NGX_AUTOCONF_ERR
+ echo "----------" >> $NGX_AUTOCONF_ERR
+fi
+
+rm -rf $NGX_AUTOTEST*
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/init b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/init
new file mode 100644
index 00000000000..910f5294b66
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/init
@@ -0,0 +1,51 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+NGX_MAKEFILE=$NGX_OBJS/Makefile
+NGX_MODULES_C=$NGX_OBJS/ngx_modules.c
+
+NGX_AUTO_HEADERS_H=$NGX_OBJS/ngx_auto_headers.h
+NGX_AUTO_CONFIG_H=$NGX_OBJS/ngx_auto_config.h
+
+NGX_AUTOTEST=$NGX_OBJS/autotest
+NGX_AUTOCONF_ERR=$NGX_OBJS/autoconf.err
+
+# STUBs
+NGX_ERR=$NGX_OBJS/autoconf.err
+MAKEFILE=$NGX_OBJS/Makefile
+
+
+NGX_PCH=
+NGX_USE_PCH=
+
+
+# check the echo's "-n" option and "\c" capability
+
+if echo "test\c" | grep c >/dev/null; then
+
+ if echo -n test | grep n >/dev/null; then
+ ngx_n=
+ ngx_c=
+
+ else
+ ngx_n=-n
+ ngx_c=
+ fi
+
+else
+ ngx_n=
+ ngx_c='\c'
+fi
+
+
+# create Makefile
+
+cat << END > Makefile
+
+default: build
+
+clean:
+ rm -rf Makefile $NGX_OBJS
+END
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/conf
new file mode 100644
index 00000000000..e1e447557e9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/conf
@@ -0,0 +1,83 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+if [ $USE_PCRE = YES -o $PCRE != NONE ]; then
+ . auto/lib/pcre/conf
+
+else
+ if [ $USE_PCRE = DISABLED -a $HTTP_REWRITE = YES ]; then
+
+cat << END
+
+$0: error: the HTTP rewrite module requires the PCRE library.
+You can either disable the module by using --without-http_rewrite_module
+option or you have to enable the PCRE support.
+
+END
+ exit 1
+ fi
+fi
+
+
+if [ $USE_OPENSSL = YES ]; then
+ . auto/lib/openssl/conf
+fi
+
+if [ $USE_MD5 = YES ]; then
+
+ if [ $USE_OPENSSL = YES ]; then
+ have=NGX_HAVE_OPENSSL_MD5_H . auto/have
+ have=NGX_OPENSSL_MD5 . auto/have
+ have=NGX_HAVE_MD5 . auto/have
+ MD5=YES
+ MD5_LIB=OpenSSL
+
+ else
+ . auto/lib/md5/conf
+ fi
+
+fi
+
+if [ $USE_SHA1 = YES ]; then
+
+ if [ $USE_OPENSSL = YES ]; then
+ have=NGX_HAVE_OPENSSL_SHA1_H . auto/have
+ have=NGX_HAVE_SHA1 . auto/have
+ SHA1=YES
+ SHA1_LIB=OpenSSL
+
+ else
+ . auto/lib/sha1/conf
+ fi
+
+fi
+
+if [ $USE_ZLIB = YES ]; then
+ . auto/lib/zlib/conf
+fi
+
+if [ $USE_LIBXSLT = YES ]; then
+ . auto/lib/libxslt/conf
+fi
+
+if [ $USE_LIBGD = YES ]; then
+ . auto/lib/libgd/conf
+fi
+
+if [ $USE_PERL = YES ]; then
+ . auto/lib/perl/conf
+fi
+
+if [ $HTTP_GEOIP = YES ]; then
+ . auto/lib/geoip/conf
+fi
+
+if [ $NGX_GOOGLE_PERFTOOLS = YES ]; then
+ . auto/lib/google-perftools/conf
+fi
+
+if [ $NGX_LIBATOMIC != NO ]; then
+ . auto/lib/libatomic/conf
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/geoip/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/geoip/conf
new file mode 100644
index 00000000000..53c274d54dc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/geoip/conf
@@ -0,0 +1,94 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+ ngx_feature="GeoIP library"
+ ngx_feature_name=
+ ngx_feature_run=no
+ ngx_feature_incs="#include <GeoIP.h>"
+ ngx_feature_path=
+ ngx_feature_libs="-lGeoIP"
+ ngx_feature_test="GeoIP_open(NULL, 0)"
+ . auto/feature
+
+
+if [ $ngx_found = no ]; then
+
+ # FreeBSD port
+
+ ngx_feature="GeoIP library in /usr/local/"
+ ngx_feature_path="/usr/local/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lGeoIP"
+ else
+ ngx_feature_libs="-L/usr/local/lib -lGeoIP"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+ # NetBSD port
+
+ ngx_feature="GeoIP library in /usr/pkg/"
+ ngx_feature_path="/usr/pkg/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lGeoIP"
+ else
+ ngx_feature_libs="-L/usr/pkg/lib -lGeoIP"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+ # MacPorts
+
+ ngx_feature="GeoIP library in /opt/local/"
+ ngx_feature_path="/opt/local/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lGeoIP"
+ else
+ ngx_feature_libs="-L/opt/local/lib -lGeoIP"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = yes ]; then
+
+ CORE_INCS="$CORE_INCS $ngx_feature_path"
+ CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+
+ if [ $NGX_IPV6 = YES ]; then
+ ngx_feature="GeoIP IPv6 support"
+ ngx_feature_name="NGX_HAVE_GEOIP_V6"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <stdio.h>
+ #include <GeoIP.h>"
+ #ngx_feature_path=
+ #ngx_feature_libs=
+ ngx_feature_test="printf(\"%d\", GEOIP_CITY_EDITION_REV0_V6);"
+ . auto/feature
+ fi
+
+else
+
+cat << END
+
+$0: error: the GeoIP module requires the GeoIP library.
+You can either do not enable the module or install the library.
+
+END
+
+ exit 1
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/google-perftools/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/google-perftools/conf
new file mode 100644
index 00000000000..5d5ddaef60d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/google-perftools/conf
@@ -0,0 +1,61 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+ ngx_feature="Google perftools"
+ ngx_feature_name=
+ ngx_feature_run=no
+ ngx_feature_incs=
+ ngx_feature_path=
+ ngx_feature_libs="-lprofiler"
+ ngx_feature_test="ProfilerStop()"
+ . auto/feature
+
+
+if [ $ngx_found = no ]; then
+
+ # FreeBSD port
+
+ ngx_feature="Google perftools in /usr/local/"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lprofiler"
+ else
+ ngx_feature_libs="-L/usr/local/lib -lprofiler"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+ # MacPorts
+
+ ngx_feature="Google perftools in /opt/local/"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lprofiler"
+ else
+ ngx_feature_libs="-L/opt/local/lib -lprofiler"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = yes ]; then
+ CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+
+else
+
+cat << END
+
+$0: error: the Google perftools module requires the Google perftools
+library. You can either do not enable the module or install the library.
+
+END
+
+ exit 1
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libatomic/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libatomic/conf
new file mode 100644
index 00000000000..d1e484ab32e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libatomic/conf
@@ -0,0 +1,43 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+if [ $NGX_LIBATOMIC != YES ]; then
+
+ have=NGX_HAVE_LIBATOMIC . auto/have
+ CORE_INCS="$CORE_INCS $NGX_LIBATOMIC/src"
+ LINK_DEPS="$LINK_DEPS $NGX_LIBATOMIC/src/libatomic_ops.a"
+ CORE_LIBS="$CORE_LIBS $NGX_LIBATOMIC/src/libatomic_ops.a"
+
+else
+
+ ngx_feature="atomic_ops library"
+ ngx_feature_name=NGX_HAVE_LIBATOMIC
+ ngx_feature_run=yes
+ ngx_feature_incs="#define AO_REQUIRE_CAS
+ #include <atomic_ops.h>"
+ ngx_feature_path=
+ ngx_feature_libs="-latomic_ops"
+ ngx_feature_test="long n = 0;
+ if (!AO_compare_and_swap(&n, 0, 1))
+ return 1;
+ if (AO_fetch_and_add(&n, 1) != 1)
+ return 1;
+ if (n != 2)
+ return 1;
+ AO_nop();"
+ . auto/feature
+
+ if [ $ngx_found = yes ]; then
+ CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+ else
+
+cat << END
+
+$0: error: libatomic_ops library was not found.
+
+END
+ exit 1
+ fi
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libatomic/make b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libatomic/make
new file mode 100644
index 00000000000..c90318ea122
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libatomic/make
@@ -0,0 +1,16 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+ cat << END >> $NGX_MAKEFILE
+
+$NGX_LIBATOMIC/src/libatomic_ops.a: $NGX_LIBATOMIC/Makefile
+ cd $NGX_LIBATOMIC && \$(MAKE)
+
+$NGX_LIBATOMIC/Makefile: $NGX_MAKEFILE
+ cd $NGX_LIBATOMIC \\
+ && if [ -f Makefile ]; then \$(MAKE) distclean; fi \\
+ && ./configure
+
+END
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libgd/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libgd/conf
new file mode 100644
index 00000000000..ff99054db68
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libgd/conf
@@ -0,0 +1,83 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+ ngx_feature="GD library"
+ ngx_feature_name=
+ ngx_feature_run=no
+ ngx_feature_incs="#include <gd.h>"
+ ngx_feature_path=
+ ngx_feature_libs="-lgd"
+ ngx_feature_test="gdImagePtr img = gdImageCreateFromGifPtr(1, NULL);"
+ . auto/feature
+
+
+if [ $ngx_found = no ]; then
+
+ # FreeBSD port
+
+ ngx_feature="GD library in /usr/local/"
+ ngx_feature_path="/usr/local/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lgd"
+ else
+ ngx_feature_libs="-L/usr/local/lib -lgd"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+ # NetBSD port
+
+ ngx_feature="GD library in /usr/pkg/"
+ ngx_feature_path="/usr/pkg/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lgd"
+ else
+ ngx_feature_libs="-L/usr/pkg/lib -lgd"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+ # MacPorts
+
+ ngx_feature="GD library in /opt/local/"
+ ngx_feature_path="/opt/local/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lgd"
+ else
+ ngx_feature_libs="-L/opt/local/lib -lgd"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = yes ]; then
+
+ CORE_INCS="$CORE_INCS $ngx_feature_path"
+ CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+
+else
+
+cat << END
+
+$0: error: the HTTP image filter module requires the GD library.
+You can either do not enable the module or install the libraries.
+
+END
+
+ exit 1
+
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libxslt/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libxslt/conf
new file mode 100644
index 00000000000..bc19d83ddce
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/libxslt/conf
@@ -0,0 +1,156 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+ ngx_feature="libxslt"
+ ngx_feature_name=
+ ngx_feature_run=no
+ ngx_feature_incs="#include <libxml/parser.h>
+ #include <libxml/tree.h>
+ #include <libxslt/xslt.h>
+ #include <libxslt/xsltInternals.h>
+ #include <libxslt/transform.h>
+ #include <libxslt/xsltutils.h>"
+ ngx_feature_path="/usr/include/libxml2"
+ ngx_feature_libs="-lxml2 -lxslt"
+ ngx_feature_test="xmlParserCtxtPtr ctxt = NULL;
+ xsltStylesheetPtr sheet = NULL;
+ xmlDocPtr doc;
+ doc = xmlParseChunk(ctxt, NULL, 0, 0);
+ xsltApplyStylesheet(sheet, doc, NULL);"
+ . auto/feature
+
+
+if [ $ngx_found = no ]; then
+
+ # FreeBSD port
+
+ ngx_feature="libxslt in /usr/local/"
+ ngx_feature_path="/usr/local/include/libxml2 /usr/local/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lxml2 -lxslt"
+ else
+ ngx_feature_libs="-L/usr/local/lib -lxml2 -lxslt"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+ # NetBSD port
+
+ ngx_feature="libxslt in /usr/pkg/"
+ ngx_feature_path="/usr/pkg/include/libxml2 /usr/pkg/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lxml2 -lxslt"
+ else
+ ngx_feature_libs="-L/usr/pkg/lib -lxml2 -lxslt"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+ # MacPorts
+
+ ngx_feature="libxslt in /opt/local/"
+ ngx_feature_path="/opt/local/include/libxml2 /opt/local/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lxml2 -lxslt"
+ else
+ ngx_feature_libs="-L/opt/local/lib -lxml2 -lxslt"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = yes ]; then
+
+ CORE_INCS="$CORE_INCS $ngx_feature_path"
+ CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+
+else
+
+cat << END
+
+$0: error: the HTTP XSLT module requires the libxml2/libxslt
+libraries. You can either do not enable the module or install the libraries.
+
+END
+
+ exit 1
+fi
+
+
+ ngx_feature="libexslt"
+ ngx_feature_name=NGX_HAVE_EXSLT
+ ngx_feature_run=no
+ ngx_feature_incs="#include <libexslt/exslt.h>"
+ ngx_feature_path="/usr/include/libxml2"
+ ngx_feature_libs="-lexslt"
+ ngx_feature_test="exsltRegisterAll();"
+ . auto/feature
+
+if [ $ngx_found = no ]; then
+
+ # FreeBSD port
+
+ ngx_feature="libexslt in /usr/local/"
+ ngx_feature_path="/usr/local/include/libxml2 /usr/local/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lexslt"
+ else
+ ngx_feature_libs="-L/usr/local/lib -lexslt"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+ # NetBSD port
+
+ ngx_feature="libexslt in /usr/pkg/"
+ ngx_feature_path="/usr/pkg/include/libxml2 /usr/local/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lexslt"
+ else
+ ngx_feature_libs="-L/usr/pkg/lib -lexslt"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+ # MacPorts
+
+ ngx_feature="libexslt in /opt/local/"
+ ngx_feature_path="/opt/local/include/libxml2 /opt/local/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lexslt"
+ else
+ ngx_feature_libs="-L/opt/local/lib -lexslt"
+ fi
+
+ . auto/feature
+fi
+
+
+if [ $ngx_found = yes ]; then
+ CORE_LIBS="$CORE_LIBS -lexslt"
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/make b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/make
new file mode 100644
index 00000000000..58a84a34cac
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/make
@@ -0,0 +1,32 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+if [ $PCRE != NONE -a $PCRE != NO -a $PCRE != YES ]; then
+ . auto/lib/pcre/make
+fi
+
+if [ $MD5 != NONE -a $MD5 != NO -a $MD5 != YES ]; then
+ . auto/lib/md5/make
+fi
+
+if [ $SHA1 != NONE -a $SHA1 != NO -a $SHA1 != YES ]; then
+ . auto/lib/sha1/make
+fi
+
+if [ $OPENSSL != NONE -a $OPENSSL != NO -a $OPENSSL != YES ]; then
+ . auto/lib/openssl/make
+fi
+
+if [ $ZLIB != NONE -a $ZLIB != NO -a $ZLIB != YES ]; then
+ . auto/lib/zlib/make
+fi
+
+if [ $NGX_LIBATOMIC != NO -a $NGX_LIBATOMIC != YES ]; then
+ . auto/lib/libatomic/make
+fi
+
+if [ $USE_PERL = YES ]; then
+ . auto/lib/perl/make
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/conf
new file mode 100644
index 00000000000..eb5dfd1f236
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/conf
@@ -0,0 +1,103 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+if [ $MD5 != NONE ]; then
+
+ if grep MD5_Init $MD5/md5.h 2>&1 >/dev/null; then
+ # OpenSSL md5
+ OPENSSL_MD5=YES
+ have=NGX_HAVE_OPENSSL_MD5 . auto/have
+ have=NGX_OPENSSL_MD5 . auto/have
+ else
+ # rsaref md5
+ OPENSSL_MD5=NO
+ fi
+
+ have=NGX_HAVE_MD5 . auto/have
+ CORE_INCS="$CORE_INCS $MD5"
+
+ case "$NGX_CC_NAME" in
+
+ msvc* | owc* | bcc)
+ LINK_DEPS="$LINK_DEPS $MD5/md5.lib"
+ CORE_LIBS="$CORE_LIBS $MD5/md5.lib"
+ ;;
+
+ icc*)
+ LINK_DEPS="$LINK_DEPS $MD5/libmd5.a"
+
+ # to allow -ipo optimization we link with the *.o but not library
+ CORE_LIBS="$CORE_LIBS $MD5/md5_dgst.o"
+
+ if [ $MD5_ASM = YES ]; then
+ CORE_LIBS="$CORE_LIBS $MD5/asm/mx86-elf.o"
+ fi
+ ;;
+
+ *)
+ LINK_DEPS="$LINK_DEPS $MD5/libmd5.a"
+ CORE_LIBS="$CORE_LIBS $MD5/libmd5.a"
+ #CORE_LIBS="$CORE_LIBS -L $MD5 -lmd5"
+ ;;
+
+ esac
+
+else
+
+ if [ "$NGX_PLATFORM" != win32 ]; then
+
+ MD5=NO
+
+ # FreeBSD, Solaris 10
+
+ ngx_feature="md5 in system md library"
+ ngx_feature_name=NGX_HAVE_MD5
+ ngx_feature_run=no
+ ngx_feature_incs="#include <md5.h>"
+ ngx_feature_path=
+ ngx_feature_libs="-lmd"
+ ngx_feature_test="MD5_CTX md5; MD5Init(&md5)"
+ . auto/feature
+
+ ngx_md5_lib="system md"
+
+ if [ $ngx_found = no ]; then
+
+ # Solaris 8/9
+
+ ngx_feature="md5 in system md5 library"
+ ngx_feature_libs="-lmd5"
+ . auto/feature
+
+ ngx_md5_lib="system md5"
+ fi
+
+ if [ $ngx_found = no ]; then
+
+ # OpenSSL crypto library
+
+ ngx_feature="md5 in system OpenSSL crypto library"
+ ngx_feature_name="NGX_OPENSSL_MD5"
+ ngx_feature_incs="#include <openssl/md5.h>"
+ ngx_feature_libs="-lcrypto"
+ ngx_feature_test="MD5_CTX md5; MD5_Init(&md5)"
+ . auto/feature
+
+ ngx_md5_lib="system crypto"
+
+ if [ $ngx_found = yes ]; then
+ have=NGX_HAVE_OPENSSL_MD5_H . auto/have
+ have=NGX_HAVE_MD5 . auto/have
+ fi
+ fi
+
+ if [ $ngx_found = yes ]; then
+ CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+ MD5=YES
+ MD5_LIB=$ngx_md5_lib
+ fi
+ fi
+
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/make b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/make
new file mode 100644
index 00000000000..81f138ab671
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/make
@@ -0,0 +1,96 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+case "$NGX_CC_NAME" in
+
+ msvc*)
+ ngx_makefile=makefile.msvc
+ ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC MD5_ASM=$MD5_ASM"
+ ngx_md5="MD5=\"$MD5\""
+ ;;
+
+ owc*)
+ ngx_makefile=makefile.owc
+ ngx_opt="CPU_OPT=\"$CPU_OPT\""
+ ngx_md5=`echo MD5=\"$MD5\" | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ;;
+
+ bcc)
+ ngx_makefile=makefile.bcc
+ ngx_opt="-DCPU_OPT=\"$CPU_OPT\" -DMD5_ASM=$MD5_ASM"
+ ngx_md5=`echo \-DMD5=\"$MD5\" | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ;;
+
+esac
+
+
+done=NO
+
+
+case "$NGX_PLATFORM" in
+
+ win32)
+ cat << END >> $NGX_MAKEFILE
+
+`echo "$MD5/md5.lib: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"`
+ \$(MAKE) -f auto/lib/md5/$ngx_makefile $ngx_opt $ngx_md5
+
+END
+
+ done=YES
+ ;;
+
+ SunOS:*:i86pc)
+ if [ $MD5_ASM = YES ]; then
+
+ cat << END >> $NGX_MAKEFILE
+
+$MD5/libmd5.a: $NGX_MAKEFILE
+ cd $MD5 \\
+ && \$(MAKE) CFLAGS="$MD5_OPT -DSOL -DMD5_ASM -DL_ENDIAN" \\
+ CC="\$(CC)" CPP="\$(CPP)" \\
+ MD5_ASM_OBJ=asm/mx86-sol.o clean libmd5.a
+
+END
+
+ done=YES
+ fi
+ ;;
+
+ # FreeBSD: i386
+ # Linux: i686
+
+ *:i386 | *:i686)
+ if [ $MD5_ASM = YES ]; then
+
+ cat << END >> $NGX_MAKEFILE
+
+$MD5/libmd5.a: $NGX_MAKEFILE
+ cd $MD5 \\
+ && \$(MAKE) CFLAGS="$MD5_OPT -DELF -DMD5_ASM -DL_ENDIAN" \\
+ CC="\$(CC)" CPP="\$(CPP)" \\
+ MD5_ASM_OBJ=asm/mx86-elf.o clean libmd5.a
+
+END
+
+ done=YES
+ fi
+ ;;
+
+esac
+
+
+if [ $done = NO ]; then
+
+ cat << END >> $NGX_MAKEFILE
+
+$MD5/libmd5.a: $NGX_MAKEFILE
+ cd $MD5 \\
+ && \$(MAKE) CFLAGS="$MD5_OPT" \\
+ CC="\$(CC)" MD5_ASM_OBJ= clean libmd5.a
+
+END
+
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/makefile.bcc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/makefile.bcc
new file mode 100644
index 00000000000..eb6fb624178
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/makefile.bcc
@@ -0,0 +1,22 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+CFLAGS = -q -O2 -tWM $(CPU_OPT) -DL_ENDIAN
+
+!if "$(MD5_ASM)" == "YES"
+
+md5.lib:
+ cd $(MD5)
+ bcc32 -c $(CFLAGS) -DMD5_ASM md5_dgst.c
+ tlib md5.lib +md5_dgst.obj +"asm\m-win32.obj"
+
+!else
+
+md5.lib:
+ cd $(MD5)
+ bcc32 -c $(CFLAGS) md5_dgst.c
+ tlib md5.lib +md5_dgst.obj
+
+!endif
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/makefile.msvc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/makefile.msvc
new file mode 100644
index 00000000000..90d62fac7f0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/makefile.msvc
@@ -0,0 +1,22 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT) -D L_ENDIAN
+
+!IF "$(MD5_ASM)" == "YES"
+
+md5.lib:
+ cd $(MD5)
+ cl -c $(CFLAGS) -D MD5_ASM md5_dgst.c
+ link -lib -out:md5.lib md5_dgst.obj asm/m-win32.obj
+
+!ELSE
+
+md5.lib:
+ cd $(MD5)
+ cl -c $(CFLAGS) md5_dgst.c
+ link -lib -out:md5.lib md5_dgst.obj
+
+!ENDIF
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/makefile.owc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/makefile.owc
new file mode 100644
index 00000000000..78c1e61dde7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/md5/makefile.owc
@@ -0,0 +1,11 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+CFLAGS = -zq -bt=nt -bm -ot -op -oi -oe -s $(CPU_OPT)
+
+md5.lib:
+ cd $(MD5)
+ wcl386 -c $(CFLAGS) -dL_ENDIAN md5_dgst.c
+ wlib -n md5.lib md5_dgst.obj
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/conf
new file mode 100644
index 00000000000..a65815f6364
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/conf
@@ -0,0 +1,78 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+if [ $OPENSSL != NONE ]; then
+
+ case "$CC" in
+
+ cl | bcc32)
+ have=NGX_OPENSSL . auto/have
+ have=NGX_SSL . auto/have
+
+ CFLAGS="$CFLAGS -DNO_SYS_TYPES_H"
+
+ CORE_INCS="$CORE_INCS $OPENSSL/openssl/include"
+ CORE_DEPS="$CORE_DEPS $OPENSSL/openssl/include/openssl/ssl.h"
+ CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/ssleay32.lib"
+ CORE_LIBS="$CORE_LIBS $OPENSSL/openssl/lib/libeay32.lib"
+
+ # libeay32.lib requires gdi32.lib
+ CORE_LIBS="$CORE_LIBS gdi32.lib"
+ # OpenSSL 1.0.0 requires crypt32.lib
+ CORE_LIBS="$CORE_LIBS crypt32.lib"
+ ;;
+
+ *)
+ have=NGX_OPENSSL . auto/have
+ have=NGX_SSL . auto/have
+
+ CORE_INCS="$CORE_INCS $OPENSSL/.openssl/include"
+ CORE_DEPS="$CORE_DEPS $OPENSSL/.openssl/include/openssl/ssl.h"
+ CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libssl.a"
+ CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libcrypto.a"
+ CORE_LIBS="$CORE_LIBS $NGX_LIBDL"
+
+ if [ "$NGX_PLATFORM" = win32 ]; then
+ CORE_LIBS="$CORE_LIBS -lgdi32 -lcrypt32 -lws2_32"
+ fi
+ ;;
+ esac
+
+else
+
+ if [ "$NGX_PLATFORM" != win32 ]; then
+
+ OPENSSL=NO
+
+ ngx_feature="OpenSSL library"
+ ngx_feature_name="NGX_OPENSSL"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <openssl/ssl.h>"
+ ngx_feature_path=
+ ngx_feature_libs="-lssl -lcrypto"
+ ngx_feature_test="SSL_library_init()"
+ . auto/feature
+
+ if [ $ngx_found = yes ]; then
+ have=NGX_SSL . auto/have
+ CORE_LIBS="$CORE_LIBS $ngx_feature_libs $NGX_LIBDL"
+ OPENSSL=YES
+ fi
+ fi
+
+ if [ $OPENSSL != YES ]; then
+
+cat << END
+
+$0: error: SSL modules require the OpenSSL library.
+You can either do not enable the modules, or install the OpenSSL library
+into the system, or build the OpenSSL library statically from the source
+with nginx by using --with-openssl=<path> option.
+
+END
+ exit 1
+ fi
+
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/make b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/make
new file mode 100644
index 00000000000..e64acd973ec
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/make
@@ -0,0 +1,67 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+case "$CC" in
+
+ cl)
+
+ cat << END >> $NGX_MAKEFILE
+
+$OPENSSL/openssl/include/openssl/ssl.h: $NGX_MAKEFILE
+ \$(MAKE) -f auto/lib/openssl/makefile.msvc \
+ OPENSSL="$OPENSSL" OPENSSL_OPT="$OPENSSL_OPT"
+
+END
+
+ ;;
+
+ bcc32)
+
+ ngx_opt=`echo "-DOPENSSL=\"$OPENSSL\" -DOPENSSL_OPT=\"$OPENSSL_OPT\"" \
+ | sed -e "s/\//$ngx_regex_dirsep/g"`
+
+ cat << END >> $NGX_MAKEFILE
+
+`echo "$OPENSSL\\openssl\\lib\\libeay32.lib: \
+ $OPENSSL\\openssl\\include\\openssl\\ssl.h" \
+ | sed -e "s/\//$ngx_regex_dirsep/g"`
+
+`echo "$OPENSSL\\openssl\\lib\\ssleay32.lib: \
+ $OPENSSL\\openssl\\include\\openssl\\ssl.h" \
+ | sed -e "s/\//$ngx_regex_dirsep/g"`
+
+`echo "$OPENSSL\\openssl\\include\\openssl\\ssl.h: $NGX_MAKEFILE" \
+ | sed -e "s/\//$ngx_regex_dirsep/g"`
+ \$(MAKE) -f auto/lib/openssl/makefile.bcc $ngx_opt
+
+END
+
+ ;;
+
+ *)
+ case $USE_THREADS in
+ NO) OPENSSL_OPT="$OPENSSL_OPT no-threads" ;;
+ *) OPENSSL_OPT="$OPENSSL_OPT threads" ;;
+ esac
+
+ case $OPENSSL in
+ /*) ngx_prefix="$OPENSSL/.openssl" ;;
+ *) ngx_prefix="$PWD/$OPENSSL/.openssl" ;;
+ esac
+
+ cat << END >> $NGX_MAKEFILE
+
+$OPENSSL/.openssl/include/openssl/ssl.h: $NGX_MAKEFILE
+ cd $OPENSSL \\
+ && if [ -f Makefile ]; then \$(MAKE) clean; fi \\
+ && ./config --prefix=$ngx_prefix no-shared $OPENSSL_OPT \\
+ && \$(MAKE) \\
+ && \$(MAKE) install LIBDIR=lib
+
+END
+
+ ;;
+
+esac
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/makefile.bcc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/makefile.bcc
new file mode 100644
index 00000000000..6a94ff74982
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/makefile.bcc
@@ -0,0 +1,18 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+all:
+ cd $(OPENSSL)
+
+ perl Configure BC-32 no-shared --prefix=openssl $(OPENSSL_OPT)
+
+ ms\do_nasm
+
+ $(MAKE) -f ms\bcb.mak
+ $(MAKE) -f ms\bcb.mak install
+
+ # Borland's make does not expand "[ch]" in
+ # copy "inc32\openssl\*.[ch]" "openssl\include\openssl"
+ copy inc32\openssl\*.h openssl\include\openssl
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/makefile.msvc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/makefile.msvc
new file mode 100644
index 00000000000..fc9e57864ae
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/openssl/makefile.msvc
@@ -0,0 +1,14 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+all:
+ cd $(OPENSSL)
+
+ perl Configure VC-WIN32 no-shared --prefix=openssl $(OPENSSL_OPT)
+
+ ms\do_ms
+
+ $(MAKE) -f ms\nt.mak
+ $(MAKE) -f ms\nt.mak install
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/conf
new file mode 100644
index 00000000000..939f01b77a4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/conf
@@ -0,0 +1,203 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+if [ $PCRE != NONE ]; then
+ CORE_INCS="$CORE_INCS $PCRE"
+
+ case "$NGX_CC_NAME" in
+
+ msvc* | owc* | bcc)
+ have=NGX_PCRE . auto/have
+ have=PCRE_STATIC . auto/have
+ CORE_DEPS="$CORE_DEPS $PCRE/pcre.h"
+ LINK_DEPS="$LINK_DEPS $PCRE/pcre.lib"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre.lib"
+ ;;
+
+ icc* )
+ have=NGX_PCRE . auto/have
+ CORE_DEPS="$CORE_DEPS $PCRE/pcre.h"
+
+ LINK_DEPS="$LINK_DEPS $PCRE/.libs/libpcre.a"
+
+ echo $ngx_n "checking for PCRE library ...$ngx_c"
+
+ if [ -f $PCRE/pcre.h ]; then
+ ngx_pcre_ver=`grep PCRE_MAJOR $PCRE/pcre.h \
+ | sed -e 's/^.*PCRE_MAJOR.* \(.*\)$/\1/'`
+
+ else if [ -f $PCRE/configure.in ]; then
+ ngx_pcre_ver=`grep PCRE_MAJOR= $PCRE/configure.in \
+ | sed -e 's/^.*=\(.*\)$/\1/'`
+
+ else
+ ngx_pcre_ver=`grep pcre_major, $PCRE/configure.ac \
+ | sed -e 's/^.*pcre_major,.*\[\(.*\)\].*$/\1/'`
+ fi
+ fi
+
+ echo " $ngx_pcre_ver major version found"
+
+ # to allow -ipo optimization we link with the *.o but not library
+
+ case "$ngx_pcre_ver" in
+ 4|5)
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre.o"
+ ;;
+
+ 6)
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_chartables.o"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_compile.o"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_exec.o"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_fullinfo.o"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_globals.o"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_tables.o"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_try_flipped.o"
+ ;;
+
+ *)
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_chartables.o"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_compile.o"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_exec.o"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_fullinfo.o"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_globals.o"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_tables.o"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_try_flipped.o"
+ CORE_LIBS="$CORE_LIBS $PCRE/pcre_newline.o"
+ ;;
+
+ esac
+ ;;
+
+ *)
+ have=NGX_PCRE . auto/have
+
+ if [ "$NGX_PLATFORM" = win32 ]; then
+ have=PCRE_STATIC . auto/have
+ fi
+
+ CORE_DEPS="$CORE_DEPS $PCRE/pcre.h"
+ LINK_DEPS="$LINK_DEPS $PCRE/.libs/libpcre.a"
+ CORE_LIBS="$CORE_LIBS $PCRE/.libs/libpcre.a"
+ ;;
+
+ esac
+
+
+ if [ $PCRE_JIT = YES ]; then
+ have=NGX_HAVE_PCRE_JIT . auto/have
+ PCRE_CONF_OPT="$PCRE_CONF_OPT --enable-jit"
+ fi
+
+else
+
+ if [ "$NGX_PLATFORM" != win32 ]; then
+
+ PCRE=NO
+
+ ngx_feature="PCRE library"
+ ngx_feature_name="NGX_PCRE"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <pcre.h>"
+ ngx_feature_path=
+ ngx_feature_libs="-lpcre"
+ ngx_feature_test="pcre *re;
+ re = pcre_compile(NULL, 0, NULL, 0, NULL);
+ if (re == NULL) return 1"
+ . auto/feature
+
+ if [ $ngx_found = no ]; then
+
+ # FreeBSD port
+
+ ngx_feature="PCRE library in /usr/local/"
+ ngx_feature_path="/usr/local/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lpcre"
+ else
+ ngx_feature_libs="-L/usr/local/lib -lpcre"
+ fi
+
+ . auto/feature
+ fi
+
+ if [ $ngx_found = no ]; then
+
+ # RedHat RPM, Solaris package
+
+ ngx_feature="PCRE library in /usr/include/pcre/"
+ ngx_feature_path="/usr/include/pcre"
+ ngx_feature_libs="-lpcre"
+
+ . auto/feature
+ fi
+
+ if [ $ngx_found = no ]; then
+
+ # NetBSD port
+
+ ngx_feature="PCRE library in /usr/pkg/"
+ ngx_feature_path="/usr/pkg/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lpcre"
+ else
+ ngx_feature_libs="-L/usr/pkg/lib -lpcre"
+ fi
+
+ . auto/feature
+ fi
+
+ if [ $ngx_found = no ]; then
+
+ # MacPorts
+
+ ngx_feature="PCRE library in /opt/local/"
+ ngx_feature_path="/opt/local/include"
+
+ if [ $NGX_RPATH = YES ]; then
+ ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lpcre"
+ else
+ ngx_feature_libs="-L/opt/local/lib -lpcre"
+ fi
+
+ . auto/feature
+ fi
+
+ if [ $ngx_found = yes ]; then
+ CORE_INCS="$CORE_INCS $ngx_feature_path"
+ CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+ PCRE=YES
+ fi
+
+ if [ $PCRE = YES ]; then
+ ngx_feature="PCRE JIT support"
+ ngx_feature_name="NGX_HAVE_PCRE_JIT"
+ ngx_feature_test="int jit = 0;
+ pcre_free_study(NULL);
+ pcre_config(PCRE_CONFIG_JIT, &jit);
+ if (jit != 1) return 1;"
+ . auto/feature
+
+ if [ $ngx_found = yes ]; then
+ PCRE_JIT=YES
+ fi
+ fi
+ fi
+
+ if [ $PCRE != YES ]; then
+cat << END
+
+$0: error: the HTTP rewrite module requires the PCRE library.
+You can either disable the module by using --without-http_rewrite_module
+option, or install the PCRE library into the system, or build the PCRE library
+statically from the source with nginx by using --with-pcre=<path> option.
+
+END
+ exit 1
+ fi
+
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/make b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/make
new file mode 100644
index 00000000000..0a27a112c11
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/make
@@ -0,0 +1,64 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+case "$NGX_CC_NAME" in
+
+ msvc*)
+ ngx_makefile=makefile.msvc
+ ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC"
+ ngx_pcre="PCRE=\"$PCRE\""
+ ;;
+
+ owc*)
+ ngx_makefile=makefile.owc
+ ngx_opt="CPU_OPT=\"$CPU_OPT\""
+ ngx_pcre=`echo PCRE=\"$PCRE\" | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ;;
+
+ bcc)
+ ngx_makefile=makefile.bcc
+ ngx_opt="-DCPU_OPT=\"$CPU_OPT\""
+ ngx_pcre=`echo \-DPCRE=\"$PCRE\" | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ;;
+
+ *)
+ ngx_makefile=
+ ;;
+
+esac
+
+
+if [ -n "$ngx_makefile" ]; then
+
+ cat << END >> $NGX_MAKEFILE
+
+`echo "$PCRE/pcre.lib: $PCRE/pcre.h $NGX_MAKEFILE" \
+ | sed -e "s/\//$ngx_regex_dirsep/g"`
+ \$(MAKE) -f auto/lib/pcre/$ngx_makefile $ngx_pcre $ngx_opt
+
+`echo "$PCRE/pcre.h:" | sed -e "s/\//$ngx_regex_dirsep/g"`
+ \$(MAKE) -f auto/lib/pcre/$ngx_makefile $ngx_pcre pcre.h
+
+END
+
+else
+
+ cat << END >> $NGX_MAKEFILE
+
+$PCRE/pcre.h: $PCRE/Makefile
+
+$PCRE/Makefile: $NGX_MAKEFILE
+ cd $PCRE \\
+ && if [ -f Makefile ]; then \$(MAKE) distclean; fi \\
+ && CC="\$(CC)" CFLAGS="$PCRE_OPT" \\
+ ./configure --disable-shared $PCRE_CONF_OPT
+
+$PCRE/.libs/libpcre.a: $PCRE/Makefile
+ cd $PCRE \\
+ && \$(MAKE) libpcre.la
+
+END
+
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/makefile.bcc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/makefile.bcc
new file mode 100644
index 00000000000..7a0f2beafc6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/makefile.bcc
@@ -0,0 +1,27 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+CFLAGS = -q -O2 -tWM -w-8004 $(CPU_OPT)
+PCREFLAGS = -DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 \
+ -DSUPPORT_PCRE8 -DHAVE_MEMMOVE
+
+
+pcre.lib:
+ cd $(PCRE)
+
+ bcc32 -c $(CFLAGS) -I. $(PCREFLAGS) pcre_*.c
+
+ copy /y nul pcre.lst
+ for %n in (*.obj) do @echo +%n ^^& >> pcre.lst
+ echo + >> pcre.lst
+
+ tlib pcre.lib @pcre.lst
+
+pcre.h:
+ cd $(PCRE)
+
+ copy /y pcre.h.generic pcre.h
+ copy /y config.h.generic config.h
+ copy /y pcre_chartables.c.dist pcre_chartables.c
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/makefile.msvc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/makefile.msvc
new file mode 100644
index 00000000000..07fd9a2853a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/makefile.msvc
@@ -0,0 +1,23 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+CFLAGS = -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT)
+PCREFLAGS = -DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 \
+ -DSUPPORT_PCRE8 -DHAVE_MEMMOVE
+
+
+pcre.lib:
+ cd $(PCRE)
+
+ cl -nologo -c $(CFLAGS) -I . $(PCREFLAGS) pcre_*.c
+
+ link -lib -out:pcre.lib -verbose:lib pcre_*.obj
+
+pcre.h:
+ cd $(PCRE)
+
+ copy /y pcre.h.generic pcre.h
+ copy /y config.h.generic config.h
+ copy /y pcre_chartables.c.dist pcre_chartables.c
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/makefile.owc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/makefile.owc
new file mode 100644
index 00000000000..122fd5b27f2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/pcre/makefile.owc
@@ -0,0 +1,25 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+CFLAGS = -c -zq -bt=nt -ot -op -oi -oe -s -bm $(CPU_OPT)
+PCREFLAGS = -DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 &
+ -DSUPPORT_PCRE8 -DHAVE_MEMMOVE
+
+
+pcre.lib:
+ cd $(PCRE)
+
+ wcl386 $(CFLAGS) -i=. $(PCREFLAGS) pcre_*.c
+
+ dir /b *.obj > pcre.lst
+
+ wlib -n pcre.lib @pcre.lst
+
+pcre.h:
+ cd $(PCRE)
+
+ copy /y pcre.h.generic pcre.h
+ copy /y config.h.generic config.h
+ copy /y pcre_chartables.c.dist pcre_chartables.c
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/perl/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/perl/conf
new file mode 100644
index 00000000000..2a1a3fe3cf2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/perl/conf
@@ -0,0 +1,78 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+echo "checking for perl"
+
+
+NGX_PERL_VER=`$NGX_PERL -v 2>&1 | grep '^This is perl' 2>&1 \
+ | sed -e 's/^This is perl, \(.*\)/\1/'`
+
+if test -n "$NGX_PERL_VER"; then
+ echo " + perl version: $NGX_PERL_VER"
+
+ if [ "`$NGX_PERL -e 'use 5.006001; print "OK"'`" != "OK" ]; then
+ echo
+ echo "$0: error: perl 5.6.1 or higher is required"
+ echo
+
+ exit 1;
+ fi
+
+ if [ "`$NGX_PERL -MExtUtils::Embed -e 'print "OK"'`" != "OK" ]; then
+ echo
+ echo "$0: error: perl module ExtUtils::Embed is required"
+ echo
+
+ exit 1;
+ fi
+
+ NGX_PERL_CFLAGS="$CFLAGS `$NGX_PERL -MExtUtils::Embed -e ccopts`"
+ NGX_PM_CFLAGS=`$NGX_PERL -MExtUtils::Embed -e ccopts`
+
+ # gcc 4.1/4.2 warn about unused values in pTHX_
+ NGX_PERL_CFLAGS=`echo $NGX_PERL_CFLAGS \
+ | sed -e 's/-Wunused-value/-Wno-unused-value/'`
+ # icc8 warns 'declaration hides parameter "my_perl"' in ENTER and LEAVE
+ NGX_PERL_CFLAGS=`echo $NGX_PERL_CFLAGS \
+ | sed -e 's/-wd171/-wd171 -wd1599/'`
+
+ ngx_perl_ldopts=`$NGX_PERL -MExtUtils::Embed -e ldopts`
+
+ ngx_perl_dlext=`$NGX_PERL -MConfig -e 'print $Config{dlext}'`
+ ngx_perl_libdir="src/http/modules/perl/blib/arch/auto"
+ ngx_perl_module="$ngx_perl_libdir/nginx/nginx.$ngx_perl_dlext"
+
+ if $NGX_PERL -V:usemultiplicity | grep define > /dev/null; then
+ have=NGX_HAVE_PERL_MULTIPLICITY . auto/have
+ echo " + perl interpreter multiplicity found"
+ fi
+
+ if $NGX_PERL -V:useithreads | grep undef > /dev/null; then
+ # FreeBSD port wants to link with -pthread non-threaded perl
+ ngx_perl_ldopts=`echo $ngx_perl_ldopts | sed 's/ -pthread//'`
+ fi
+
+ if [ "$NGX_SYSTEM" = "Darwin" ]; then
+ # OS X system perl wants to link universal binaries
+ ngx_perl_ldopts=`echo $ngx_perl_ldopts \
+ | sed -e 's/-arch x86_64 -arch i386//'`
+ fi
+
+ CORE_LINK="$CORE_LINK $ngx_perl_ldopts"
+ LINK_DEPS="$LINK_DEPS $NGX_OBJS/$ngx_perl_module"
+
+ if test -n "$NGX_PERL_MODULES"; then
+ have=NGX_PERL_MODULES value="(u_char *) \"$NGX_PERL_MODULES\""
+ . auto/define
+ NGX_PERL_MODULES_MAN=$NGX_PERL_MODULES/man3
+ fi
+
+else
+ echo
+ echo "$0: error: perl 5.6.1 or higher is required"
+ echo
+
+ exit 1;
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/perl/make b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/perl/make
new file mode 100644
index 00000000000..d1c1b9e48c1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/perl/make
@@ -0,0 +1,41 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+v=`grep 'define NGINX_VERSION' src/core/nginx.h | sed -e 's/^.*"\(.*\)".*/\1/'`
+
+
+cat << END >> $NGX_MAKEFILE
+
+$NGX_OBJS/src/http/modules/perl/blib/arch/auto/nginx/nginx.$ngx_perl_dlext: \\
+ \$(CORE_DEPS) \$(HTTP_DEPS) \\
+ src/http/modules/perl/ngx_http_perl_module.h \\
+ $NGX_OBJS/src/http/modules/perl/Makefile
+ cd $NGX_OBJS/src/http/modules/perl && \$(MAKE)
+
+ rm -rf $NGX_OBJS/install_perl
+
+
+$NGX_OBJS/src/http/modules/perl/Makefile: \\
+ $NGX_AUTO_CONFIG_H \\
+ src/core/nginx.h \\
+ src/http/modules/perl/Makefile.PL \\
+ src/http/modules/perl/nginx.pm \\
+ src/http/modules/perl/nginx.xs \\
+ src/http/modules/perl/typemap
+ sed "s/%%VERSION%%/$v/" src/http/modules/perl/nginx.pm > \\
+ $NGX_OBJS/src/http/modules/perl/nginx.pm
+ cp -p src/http/modules/perl/nginx.xs $NGX_OBJS/src/http/modules/perl/
+ cp -p src/http/modules/perl/typemap $NGX_OBJS/src/http/modules/perl/
+ cp -p src/http/modules/perl/Makefile.PL $NGX_OBJS/src/http/modules/perl/
+
+ cd $NGX_OBJS/src/http/modules/perl \\
+ && NGX_PM_CFLAGS="\$(NGX_PM_CFLAGS) -g $NGX_CC_OPT" \\
+ NGX_INCS="$CORE_INCS $NGX_OBJS $HTTP_INCS" \\
+ NGX_DEPS="\$(CORE_DEPS) \$(HTTP_DEPS)" \\
+ $NGX_PERL Makefile.PL \\
+ LIB=$NGX_PERL_MODULES \\
+ INSTALLSITEMAN3DIR=$NGX_PERL_MODULES_MAN
+
+END
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/conf
new file mode 100644
index 00000000000..fd69afda22b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/conf
@@ -0,0 +1,79 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+if [ $SHA1 != NONE ]; then
+
+ have=NGX_HAVE_SHA1 . auto/have
+ CORE_INCS="$CORE_INCS $SHA1"
+
+ case "$NGX_CC_NAME" in
+
+ msvc* | owc* | bcc)
+ LINK_DEPS="$LINK_DEPS $SHA1/sha1.lib"
+ CORE_LIBS="$CORE_LIBS $SHA1/sha1.lib"
+ ;;
+
+ icc*)
+ LINK_DEPS="$LINK_DEPS $SHA1/libsha.a"
+
+ # to allow -ipo optimization we link with the *.o but not library
+ CORE_LIBS="$CORE_LIBS $SHA1/sha1_dgst.o"
+
+ if [ $SHA1_ASM = YES ]; then
+ CORE_LIBS="$CORE_LIBS $SHA1/asm/sx86-elf.o"
+ fi
+ ;;
+
+ *)
+ LINK_DEPS="$LINK_DEPS $SHA1/libsha.a"
+ CORE_LIBS="$CORE_LIBS $SHA1/libsha.a"
+ #CORE_LIBS="$CORE_LIBS -L $SHA1 -lsha"
+ ;;
+
+ esac
+
+else
+
+ if [ "$NGX_PLATFORM" != win32 ]; then
+
+ SHA1=NO
+
+ # FreeBSD
+
+ ngx_feature="sha1 in system md library"
+ ngx_feature_name=NGX_HAVE_SHA1
+ ngx_feature_run=no
+ ngx_feature_incs="#include <sha.h>"
+ ngx_feature_path=
+ ngx_feature_libs="-lmd"
+ ngx_feature_test="SHA_CTX sha1; SHA1_Init(&sha1)"
+ . auto/feature
+
+ ngx_sha1_lib="system md"
+
+ if [ $ngx_found = no ]; then
+
+ # OpenSSL crypto library
+
+ ngx_feature="sha1 in system OpenSSL crypto library"
+ ngx_feature_incs="#include <openssl/sha.h>"
+ ngx_feature_libs="-lcrypto"
+ . auto/feature
+
+ ngx_sha1_lib="system crypto"
+
+ if [ $ngx_found = yes ]; then
+ have=NGX_HAVE_OPENSSL_SHA1_H . auto/have
+ fi
+ fi
+
+ if [ $ngx_found = yes ]; then
+ CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+ SHA1=YES
+ SHA1_LIB=$ngx_sha1_lib
+ fi
+ fi
+
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/make b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/make
new file mode 100644
index 00000000000..fc10aaef38b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/make
@@ -0,0 +1,96 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+case "$NGX_CC_NAME" in
+
+ msvc*)
+ ngx_makefile=makefile.msvc
+ ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC SHA1_ASM=$SHA1_ASM"
+ ngx_sha1="SHA1=\"$SHA1\""
+ ;;
+
+ owc*)
+ ngx_makefile=makefile.owc
+ ngx_opt="CPU_OPT=\"$CPU_OPT\""
+ ngx_sha1=`echo SHA1=\"$SHA1\" | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ;;
+
+ bcc)
+ ngx_makefile=makefile.bcc
+ ngx_opt="-DCPU_OPT=\"$CPU_OPT\" -DSHA1_ASM=$SHA1_ASM"
+ ngx_sha1=`echo \-DSHA1=\"$SHA1\" | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ;;
+
+esac
+
+
+done=NO
+
+
+case "$NGX_PLATFORM" in
+
+ win32)
+ cat << END >> $NGX_MAKEFILE
+
+`echo "$SHA1/sha1.lib: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"`
+ \$(MAKE) -f auto/lib/sha1/$ngx_makefile $ngx_opt $ngx_sha1
+
+END
+
+ done=YES
+ ;;
+
+ SunOS:*:i86pc)
+ if [ $SHA1_ASM = YES ]; then
+
+ cat << END >> $NGX_MAKEFILE
+
+$SHA1/libsha.a: $NGX_MAKEFILE
+ cd $SHA1 \\
+ && \$(MAKE) CFLAGS="$SHA1_OPT -DSOL -DSHA1_ASM -DL_ENDIAN" \\
+ CC="\$(CC)" CPP="\$(CPP)" \\
+ SHA_ASM_OBJ=asm/sx86-sol.o clean libsha.a
+
+END
+
+ done=YES
+ fi
+ ;;
+
+ # FreeBSD: i386
+ # Linux: i686
+
+ *:i386 | *:i686)
+ if [ $SHA1_ASM = YES ]; then
+
+ cat << END >> $NGX_MAKEFILE
+
+$SHA1/libsha.a: $NGX_MAKEFILE
+ cd $SHA1 \\
+ && \$(MAKE) CFLAGS="$SHA1_OPT -DELF -DSHA1_ASM -DL_ENDIAN" \\
+ CC="\$(CC)" CPP="\$(CPP)" \\
+ SHA_ASM_OBJ=asm/sx86-elf.o clean libsha.a
+
+END
+
+ done=YES
+ fi
+ ;;
+
+esac
+
+
+if [ $done = NO ]; then
+
+ cat << END >> $NGX_MAKEFILE
+
+$SHA1/libsha.a: $NGX_MAKEFILE
+ cd $SHA1 \\
+ && \$(MAKE) CFLAGS="$SHA1_OPT" \\
+ CC="\$(CC)" SHA_ASM_OBJ= clean libsha.a
+
+END
+
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/makefile.bcc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/makefile.bcc
new file mode 100644
index 00000000000..b0685fa4512
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/makefile.bcc
@@ -0,0 +1,22 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+CFLAGS = -q -O2 -tWM $(CPU_OPT) -DL_ENDIAN
+
+!if "$(SHA1_ASM)" == "YES"
+
+sha1.lib:
+ cd $(SHA1)
+ bcc32 -c $(CFLAGS) -DSHA1_ASM sha1dgst.c
+ tlib sha1.lib +sha1dgst.obj +"asm\s-win32.obj"
+
+!else
+
+sha1.lib:
+ cd $(SHA1)
+ bcc32 -c $(CFLAGS) sha1dgst.c
+ tlib sha1.lib +sha1dgst.obj
+
+!endif
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/makefile.msvc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/makefile.msvc
new file mode 100644
index 00000000000..3cbd21b3dbd
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/makefile.msvc
@@ -0,0 +1,22 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT) -D L_ENDIAN
+
+!IF "$(SHA1_ASM)" == "YES"
+
+sha1.lib:
+ cd $(SHA1)
+ cl -c $(CFLAGS) -D SHA1_ASM sha1dgst.c
+ link -lib -out:sha1.lib sha1dgst.obj asm/s-win32.obj
+
+!ELSE
+
+sha1.lib:
+ cd $(SHA1)
+ cl -c $(CFLAGS) sha1dgst.c
+ link -lib -out:sha1.lib sha1dgst.obj
+
+!ENDIF
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/makefile.owc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/makefile.owc
new file mode 100644
index 00000000000..fc095cc980a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/sha1/makefile.owc
@@ -0,0 +1,11 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+CFLAGS = -zq -bt=nt -bm -ot -op -oi -oe -s $(CPU_OPT)
+
+sha1.lib:
+ cd $(SHA1)
+ wcl386 -c $(CFLAGS) -dL_ENDIAN sha1dgst.c
+ wlib -n sha1.lib sha1dgst.obj
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/test b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/test
new file mode 100644
index 00000000000..ba943a29d40
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/test
@@ -0,0 +1,40 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+echo $ngx_n "checking for $ngx_lib ...$ngx_c"
+
+cat << END >> $NGX_AUTOCONF_ERR
+
+----------------------------------------
+checking for $ngx_lib
+
+END
+
+ngx_found=no
+
+cat << END > $NGX_AUTOTEST.c
+
+$ngx_lib_incs
+
+int main() {
+ $ngx_lib_test;
+ return 0;
+}
+
+
+eval "$CC $cc_test_flags $ngx_lib_cflags \
+ -o $NGX_AUTOTEST $NGX_AUTOTEST.c $ngx_libs \
+ >> $NGX_ERR 2>&1"
+
+if [ -x $NGX_AUTOTEST ]; then
+ echo " found"
+
+ ngx_found=yes
+
+else
+ echo " not found"
+fi
+
+rm -rf $NGX_AUTOTEST*
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/conf
new file mode 100644
index 00000000000..26db642ac90
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/conf
@@ -0,0 +1,79 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+if [ $ZLIB != NONE ]; then
+ CORE_INCS="$CORE_INCS $ZLIB"
+
+ case "$NGX_CC_NAME" in
+
+ msvc* | owc* | bcc)
+ have=NGX_ZLIB . auto/have
+ LINK_DEPS="$LINK_DEPS $ZLIB/zlib.lib"
+ CORE_LIBS="$CORE_LIBS $ZLIB/zlib.lib"
+ ;;
+
+ icc*)
+ have=NGX_ZLIB . auto/have
+ LINK_DEPS="$LINK_DEPS $ZLIB/libz.a"
+
+ # to allow -ipo optimization we link with the *.o but not library
+ CORE_LIBS="$CORE_LIBS $ZLIB/adler32.o"
+ CORE_LIBS="$CORE_LIBS $ZLIB/crc32.o"
+ CORE_LIBS="$CORE_LIBS $ZLIB/deflate.o"
+ CORE_LIBS="$CORE_LIBS $ZLIB/trees.o"
+ CORE_LIBS="$CORE_LIBS $ZLIB/zutil.o"
+ CORE_LIBS="$CORE_LIBS $ZLIB/compress.o"
+
+ if [ $ZLIB_ASM != NO ]; then
+ CORE_LIBS="$CORE_LIBS $ZLIB/match.o"
+ fi
+ ;;
+
+ *)
+ have=NGX_ZLIB . auto/have
+ LINK_DEPS="$LINK_DEPS $ZLIB/libz.a"
+ CORE_LIBS="$CORE_LIBS $ZLIB/libz.a"
+ #CORE_LIBS="$CORE_LIBS -L $ZLIB -lz"
+ ;;
+
+ esac
+
+else
+
+ if [ "$NGX_PLATFORM" != win32 ]; then
+ ZLIB=NO
+
+ # FreeBSD, Solaris, Linux
+
+ ngx_feature="zlib library"
+ ngx_feature_name="NGX_ZLIB"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <zlib.h>"
+ ngx_feature_path=
+ ngx_feature_libs="-lz"
+ ngx_feature_test="z_stream z; deflate(&z, Z_NO_FLUSH)"
+ . auto/feature
+
+
+ if [ $ngx_found = yes ]; then
+ CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+ ZLIB=YES
+ ngx_found=no
+ fi
+ fi
+
+ if [ $ZLIB != YES ]; then
+cat << END
+
+$0: error: the HTTP gzip module requires the zlib library.
+You can either disable the module by using --without-http_gzip_module
+option, or install the zlib library into the system, or build the zlib library
+statically from the source with nginx by using --with-zlib=<path> option.
+
+END
+ exit 1
+ fi
+
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/make b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/make
new file mode 100644
index 00000000000..7875ef67fe0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/make
@@ -0,0 +1,135 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+case "$NGX_CC_NAME" in
+
+ msvc*)
+ ngx_makefile=makefile.msvc
+ ngx_opt="CPU_OPT=\"$CPU_OPT\" LIBC=$LIBC"
+ ngx_zlib="ZLIB=\"$ZLIB\""
+
+ ;;
+
+ owc*)
+ ngx_makefile=makefile.owc
+ ngx_opt="CPU_OPT=\"$CPU_OPT\""
+ ngx_zlib=`echo ZLIB=\"$ZLIB\" | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ;;
+
+ bcc)
+ ngx_makefile=makefile.bcc
+ ngx_opt="-DCPU_OPT=\"$CPU_OPT\""
+ ngx_zlib=`echo \-DZLIB=\"$ZLIB\" | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ;;
+
+ *)
+ ngx_makefile=
+ ;;
+
+esac
+
+
+done=NO
+
+
+case "$NGX_PLATFORM" in
+
+ win32)
+
+ if [ -n "$ngx_makefile" ]; then
+ cat << END >> $NGX_MAKEFILE
+
+`echo "$ZLIB/zlib.lib: $NGX_MAKEFILE" | sed -e "s/\//$ngx_regex_dirsep/g"`
+ \$(MAKE) -f auto/lib/zlib/$ngx_makefile $ngx_opt $ngx_zlib
+
+END
+
+ else
+
+ cat << END >> $NGX_MAKEFILE
+
+$ZLIB/libz.a: $NGX_MAKEFILE
+ cd $ZLIB \\
+ && \$(MAKE) distclean \\
+ && \$(MAKE) -f win32/Makefile.gcc \\
+ CFLAGS="$ZLIB_OPT" CC="\$(CC)" \\
+ libz.a
+
+END
+
+ fi
+
+ done=YES
+ ;;
+
+ # FreeBSD: i386
+ # Linux: i686
+
+ *:i386 | *:i686)
+ case $ZLIB_ASM in
+ pentium)
+
+ cat << END >> $NGX_MAKEFILE
+
+$ZLIB/libz.a: $NGX_MAKEFILE
+ cd $ZLIB \\
+ && \$(MAKE) distclean \\
+ && cp contrib/asm586/match.S . \\
+ && CFLAGS="$ZLIB_OPT -DASMV" CC="\$(CC)" \\
+ ./configure \\
+ && \$(MAKE) OBJA=match.o libz.a
+
+END
+
+ done=YES
+ ;;
+
+ pentiumpro)
+
+ cat << END >> $NGX_MAKEFILE
+
+$ZLIB/libz.a: $NGX_MAKEFILE
+ cd $ZLIB \\
+ && \$(MAKE) distclean \\
+ && cp contrib/asm686/match.S . \\
+ && CFLAGS="$ZLIB_OPT -DASMV" CC="\$(CC)" \\
+ ./configure \\
+ && \$(MAKE) OBJA=match.o libz.a
+
+END
+
+ done=YES
+ ;;
+
+ NO)
+ ;;
+
+ *)
+ echo "$0: error: invalid --with-zlib-asm=$ZLIB_ASM option."
+ echo "The valid values are \"pentium\" and \"pentiumpro\" only".
+ echo
+
+ exit 1;
+ ;;
+ esac
+ ;;
+
+esac
+
+
+if [ $done = NO ]; then
+
+ cat << END >> $NGX_MAKEFILE
+
+$ZLIB/libz.a: $NGX_MAKEFILE
+ cd $ZLIB \\
+ && \$(MAKE) distclean \\
+ && CFLAGS="$ZLIB_OPT" CC="\$(CC)" \\
+ ./configure \\
+ && \$(MAKE) libz.a
+
+END
+
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/makefile.bcc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/makefile.bcc
new file mode 100644
index 00000000000..97a30ea3951
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/makefile.bcc
@@ -0,0 +1,17 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+CFLAGS = -q -O2 -tWM -w-8004 -w-8012 $(CPU_OPT)
+
+zlib.lib:
+ cd $(ZLIB)
+
+ bcc32 -c $(CFLAGS) adler32.c crc32.c deflate.c \
+ trees.c zutil.c compress.c \
+ inflate.c inffast.c inftrees.c
+
+ tlib zlib.lib +adler32.obj +crc32.obj +deflate.obj \
+ +trees.obj +zutil.obj +compress.obj \
+ +inflate.obj +inffast.obj +inftrees.obj
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/makefile.msvc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/makefile.msvc
new file mode 100644
index 00000000000..6fbd6918c2f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/makefile.msvc
@@ -0,0 +1,17 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+CFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT)
+
+zlib.lib:
+ cd $(ZLIB)
+
+ cl -c $(CFLAGS) adler32.c crc32.c deflate.c \
+ trees.c zutil.c compress.c \
+ inflate.c inffast.c inftrees.c
+
+ link -lib -out:zlib.lib adler32.obj crc32.obj deflate.obj \
+ trees.obj zutil.obj compress.obj \
+ inflate.obj inffast.obj inftrees.obj
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/makefile.owc b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/makefile.owc
new file mode 100644
index 00000000000..9e123be83e9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/lib/zlib/makefile.owc
@@ -0,0 +1,14 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+CFLAGS = -zq -bt=nt -ot -op -oi -oe -s -bm $(CPU_OPT)
+
+zlib.lib:
+ cd $(ZLIB)
+
+ wcl386 -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c &
+ compress.c inflate.c inffast.c inftrees.c
+ wlib -n zlib.lib adler32.obj crc32.obj deflate.obj trees.obj &
+ zutil.obj compress.obj inflate.obj inffast.obj inftrees.obj
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/make b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/make
new file mode 100644
index 00000000000..05b74543c53
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/make
@@ -0,0 +1,417 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+echo "creating $NGX_MAKEFILE"
+
+mkdir -p $NGX_OBJS/src/core $NGX_OBJS/src/event $NGX_OBJS/src/event/modules \
+ $NGX_OBJS/src/os/unix $NGX_OBJS/src/os/win32 \
+ $NGX_OBJS/src/http $NGX_OBJS/src/http/modules \
+ $NGX_OBJS/src/http/modules/perl \
+ $NGX_OBJS/src/mail \
+ $NGX_OBJS/src/misc
+
+
+ngx_objs_dir=$NGX_OBJS$ngx_regex_dirsep
+ngx_use_pch=`echo $NGX_USE_PCH | sed -e "s/\//$ngx_regex_dirsep/g"`
+
+
+cat << END > $NGX_MAKEFILE
+
+CC = $CC
+CFLAGS = $CFLAGS
+CPP = $CPP
+LINK = $LINK
+
+END
+
+
+if test -n "$NGX_PERL_CFLAGS"; then
+ echo NGX_PERL_CFLAGS = $NGX_PERL_CFLAGS >> $NGX_MAKEFILE
+ echo NGX_PM_CFLAGS = $NGX_PM_CFLAGS >> $NGX_MAKEFILE
+fi
+
+
+# ALL_INCS, required by the addons and by OpenWatcom C precompiled headers
+
+ngx_incs=`echo $CORE_INCS $NGX_OBJS $HTTP_INCS $MAIL_INCS\
+ | sed -e "s/ *\([^ ][^ ]*\)/$ngx_regex_cont$ngx_include_opt\1/g" \
+ -e "s/\//$ngx_regex_dirsep/g"`
+
+cat << END >> $NGX_MAKEFILE
+
+ALL_INCS = $ngx_include_opt$ngx_incs
+
+END
+
+
+ngx_all_srcs="$CORE_SRCS"
+
+
+# the core dependences and include paths
+
+ngx_deps=`echo $CORE_DEPS $NGX_AUTO_CONFIG_H $NGX_PCH \
+ | sed -e "s/ *\([^ ][^ ]*\)/$ngx_regex_cont\1/g" \
+ -e "s/\//$ngx_regex_dirsep/g"`
+
+ngx_incs=`echo $CORE_INCS $NGX_OBJS \
+ | sed -e "s/ *\([^ ][^ ]*\)/$ngx_regex_cont$ngx_include_opt\1/g" \
+ -e "s/\//$ngx_regex_dirsep/g"`
+
+cat << END >> $NGX_MAKEFILE
+
+CORE_DEPS = $ngx_deps
+
+
+CORE_INCS = $ngx_include_opt$ngx_incs
+
+END
+
+
+# the http dependences and include paths
+
+if [ $HTTP = YES ]; then
+
+ ngx_all_srcs="$ngx_all_srcs $HTTP_SRCS"
+
+ ngx_deps=`echo $HTTP_DEPS \
+ | sed -e "s/ *\([^ ][^ ]*\)/$ngx_regex_cont\1/g" \
+ -e "s/\//$ngx_regex_dirsep/g"`
+
+ ngx_incs=`echo $HTTP_INCS \
+ | sed -e "s/ *\([^ ][^ ]*\)/$ngx_regex_cont$ngx_include_opt\1/g" \
+ -e "s/\//$ngx_regex_dirsep/g"`
+
+ cat << END >> $NGX_MAKEFILE
+
+HTTP_DEPS = $ngx_deps
+
+
+HTTP_INCS = $ngx_include_opt$ngx_incs
+
+END
+
+fi
+
+
+# the mail dependences and include paths
+
+if [ $MAIL = YES ]; then
+
+ ngx_all_srcs="$ngx_all_srcs $MAIL_SRCS"
+
+ ngx_deps=`echo $MAIL_DEPS \
+ | sed -e "s/ *\([^ ][^ ]*\)/$ngx_regex_cont\1/g" \
+ -e "s/\//$ngx_regex_dirsep/g"`
+
+ ngx_incs=`echo $MAIL_INCS \
+ | sed -e "s/ *\([^ ][^ ]*\)/$ngx_regex_cont$ngx_include_opt\1/g" \
+ -e "s/\//$ngx_regex_dirsep/g"`
+
+ cat << END >> $NGX_MAKEFILE
+
+MAIL_DEPS = $ngx_deps
+
+
+MAIL_INCS = $ngx_include_opt$ngx_incs
+
+END
+
+fi
+
+
+ngx_all_srcs="$ngx_all_srcs $NGX_MISC_SRCS"
+
+
+if test -n "$NGX_ADDON_SRCS"; then
+
+cat << END >> $NGX_MAKEFILE
+
+ADDON_DEPS = \$(CORE_DEPS) $NGX_ADDON_DEPS
+
+END
+
+fi
+
+
+# nginx
+
+ngx_all_srcs=`echo $ngx_all_srcs | sed -e "s/\//$ngx_regex_dirsep/g"`
+
+for ngx_src in $NGX_ADDON_SRCS
+do
+ ngx_obj="addon/`basename \`dirname $ngx_src\``"
+
+ test -d $NGX_OBJS/$ngx_obj || mkdir -p $NGX_OBJS/$ngx_obj
+
+ ngx_obj=`echo $ngx_obj/\`basename $ngx_src\` \
+ | sed -e "s/\//$ngx_regex_dirsep/g"`
+
+ ngx_all_srcs="$ngx_all_srcs $ngx_obj"
+done
+
+ngx_all_objs=`echo $ngx_all_srcs \
+ | sed -e "s#\([^ ]*\.\)cpp#$NGX_OBJS\/\1$ngx_objext#g" \
+ -e "s#\([^ ]*\.\)cc#$NGX_OBJS\/\1$ngx_objext#g" \
+ -e "s#\([^ ]*\.\)c#$NGX_OBJS\/\1$ngx_objext#g" \
+ -e "s#\([^ ]*\.\)S#$NGX_OBJS\/\1$ngx_objext#g"`
+
+ngx_modules_c=`echo $NGX_MODULES_C | sed -e "s/\//$ngx_regex_dirsep/g"`
+
+ngx_modules_obj=`echo $ngx_modules_c | sed -e "s/\(.*\.\)c/\1$ngx_objext/"`
+
+
+if test -n "$NGX_RES"; then
+ ngx_res=$NGX_RES
+else
+ ngx_res="$NGX_RC $NGX_ICONS"
+ ngx_rcc=`echo $NGX_RCC | sed -e "s/\//$ngx_regex_dirsep/g"`
+fi
+
+ngx_deps=`echo $ngx_all_objs $ngx_modules_obj $ngx_res $LINK_DEPS \
+ | sed -e "s/ *\([^ ][^ ]*\)/$ngx_regex_cont\1/g" \
+ -e "s/\//$ngx_regex_dirsep/g"`
+
+ngx_objs=`echo $ngx_all_objs $ngx_modules_obj \
+ | sed -e "s/ *\([^ ][^ ]*\)/$ngx_long_regex_cont\1/g" \
+ -e "s/\//$ngx_regex_dirsep/g"`
+
+if test -n "$NGX_LD_OPT$CORE_LIBS"; then
+ ngx_libs=`echo $NGX_LD_OPT $CORE_LIBS \
+ | sed -e "s/\//$ngx_regex_dirsep/g" -e "s/^/$ngx_long_regex_cont/"`
+fi
+
+ngx_link=${CORE_LINK:+`echo $CORE_LINK \
+ | sed -e "s/\//$ngx_regex_dirsep/g" -e "s/^/$ngx_long_regex_cont/"`}
+
+
+cat << END >> $NGX_MAKEFILE
+
+$NGX_OBJS${ngx_dirsep}nginx${ngx_binext}: $ngx_deps$ngx_spacer
+ \$(LINK) ${ngx_long_start}${ngx_binout}$NGX_OBJS${ngx_dirsep}nginx$ngx_long_cont$ngx_objs$ngx_libs$ngx_link
+ $ngx_rcc
+${ngx_long_end}
+END
+
+
+# ngx_modules.c
+
+if test -n "$NGX_PCH"; then
+ ngx_cc="\$(CC) $ngx_compile_opt \$(CFLAGS) $ngx_use_pch \$(ALL_INCS)"
+else
+ ngx_cc="\$(CC) $ngx_compile_opt \$(CFLAGS) \$(CORE_INCS)"
+fi
+
+cat << END >> $NGX_MAKEFILE
+
+$ngx_modules_obj: \$(CORE_DEPS)$ngx_cont$ngx_modules_c
+ $ngx_cc$ngx_tab$ngx_objout$ngx_modules_obj$ngx_tab$ngx_modules_c$NGX_AUX
+
+END
+
+
+# the core sources
+
+for ngx_src in $CORE_SRCS
+do
+ ngx_src=`echo $ngx_src | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ngx_obj=`echo $ngx_src \
+ | sed -e "s#^\(.*\.\)cpp\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)cc\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)c\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)S\\$#$ngx_objs_dir\1$ngx_objext#g"`
+
+ cat << END >> $NGX_MAKEFILE
+
+$ngx_obj: \$(CORE_DEPS)$ngx_cont$ngx_src
+ $ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX
+
+END
+
+done
+
+
+# the http sources
+
+if [ $HTTP = YES ]; then
+
+ if test -n "$NGX_PCH"; then
+ ngx_cc="\$(CC) $ngx_compile_opt \$(CFLAGS) $ngx_use_pch \$(ALL_INCS)"
+ else
+ ngx_cc="\$(CC) $ngx_compile_opt \$(CFLAGS) \$(CORE_INCS) \$(HTTP_INCS)"
+ ngx_perl_cc="\$(CC) $ngx_compile_opt \$(NGX_PERL_CFLAGS) "
+ ngx_perl_cc="$ngx_perl_cc \$(CORE_INCS) \$(HTTP_INCS)"
+ fi
+
+ for ngx_source in $HTTP_SRCS
+ do
+ ngx_src=`echo $ngx_source | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ngx_obj=`echo $ngx_src \
+ | sed -e "s#^\(.*\.\)cpp\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)cc\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)c\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)S\\$#$ngx_objs_dir\1$ngx_objext#g"`
+
+ if [ $ngx_source = src/http/modules/perl/ngx_http_perl_module.c ]; then
+
+ cat << END >> $NGX_MAKEFILE
+
+$ngx_obj: \$(CORE_DEPS) \$(HTTP_DEPS)$ngx_cont$ngx_src
+ $ngx_perl_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX
+
+END
+ else
+
+ cat << END >> $NGX_MAKEFILE
+
+$ngx_obj: \$(CORE_DEPS) \$(HTTP_DEPS)$ngx_cont$ngx_src
+ $ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX
+
+END
+
+ fi
+ done
+
+fi
+
+
+# the mail sources
+
+if [ $MAIL = YES ]; then
+
+ if test -n "$NGX_PCH"; then
+ ngx_cc="\$(CC) $ngx_compile_opt \$(CFLAGS) $ngx_use_pch \$(ALL_INCS)"
+ else
+ ngx_cc="\$(CC) $ngx_compile_opt \$(CFLAGS) \$(CORE_INCS) \$(MAIL_INCS)"
+ fi
+
+ for ngx_src in $MAIL_SRCS
+ do
+ ngx_src=`echo $ngx_src | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ngx_obj=`echo $ngx_src \
+ | sed -e "s#^\(.*\.\)cpp\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)cc\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)c\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)S\\$#$ngx_objs_dir\1$ngx_objext#g"`
+
+ cat << END >> $NGX_MAKEFILE
+
+$ngx_obj: \$(CORE_DEPS) \$(MAIL_DEPS)$ngx_cont$ngx_src
+ $ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX
+
+END
+ done
+
+fi
+
+
+# the misc sources
+
+if test -n "$NGX_MISC_SRCS"; then
+
+ ngx_cc="\$(CC) $ngx_compile_opt \$(CFLAGS) $ngx_use_pch \$(ALL_INCS)"
+
+ for ngx_src in $NGX_MISC_SRCS
+ do
+ ngx_src=`echo $ngx_src | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ngx_obj=`echo $ngx_src \
+ | sed -e "s#^\(.*\.\)cpp\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)cc\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)c\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)S\\$#$ngx_objs_dir\1$ngx_objext#g"`
+
+ cat << END >> $NGX_MAKEFILE
+
+$ngx_obj: \$(CORE_DEPS) $ngx_cont$ngx_src
+ $ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX
+
+END
+ done
+
+fi
+
+
+# the addons sources
+
+if test -n "$NGX_ADDON_SRCS"; then
+
+ ngx_cc="\$(CC) $ngx_compile_opt \$(CFLAGS) $ngx_use_pch \$(ALL_INCS)"
+
+ for ngx_src in $NGX_ADDON_SRCS
+ do
+ ngx_obj="addon/`basename \`dirname $ngx_src\``"
+
+ ngx_obj=`echo $ngx_obj/\`basename $ngx_src\` \
+ | sed -e "s/\//$ngx_regex_dirsep/g"`
+
+ ngx_obj=`echo $ngx_obj \
+ | sed -e "s#^\(.*\.\)cpp\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)cc\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)c\\$#$ngx_objs_dir\1$ngx_objext#g" \
+ -e "s#^\(.*\.\)S\\$#$ngx_objs_dir\1$ngx_objext#g"`
+
+ ngx_src=`echo $ngx_src | sed -e "s/\//$ngx_regex_dirsep/g"`
+
+ cat << END >> $NGX_MAKEFILE
+
+$ngx_obj: \$(ADDON_DEPS)$ngx_cont$ngx_src
+ $ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX
+
+END
+ done
+
+fi
+
+
+# the addons config.make
+
+if test -n "$NGX_ADDONS"; then
+
+ for ngx_addon_dir in $NGX_ADDONS
+ do
+ if test -f $ngx_addon_dir/config.make; then
+ . $ngx_addon_dir/config.make
+ fi
+ done
+fi
+
+
+# Win32 resource file
+
+if test -n "$NGX_RES"; then
+
+ ngx_res=`echo "$NGX_RES: $NGX_RC $NGX_ICONS" \
+ | sed -e "s/\//$ngx_regex_dirsep/g"`
+ ngx_rcc=`echo $NGX_RCC | sed -e "s/\//$ngx_regex_dirsep/g"`
+
+ cat << END >> $NGX_MAKEFILE
+
+$ngx_res
+ $ngx_rcc
+
+END
+
+fi
+
+
+# the precompiled headers
+
+if test -n "$NGX_PCH"; then
+ echo "#include <ngx_config.h>" > $NGX_OBJS/ngx_pch.c
+
+ ngx_pch="src/core/ngx_config.h $OS_CONFIG $NGX_OBJS/ngx_auto_config.h"
+ ngx_pch=`echo "$NGX_PCH: $ngx_pch" | sed -e "s/\//$ngx_regex_dirsep/g"`
+
+ ngx_src="\$(CC) \$(CFLAGS) $NGX_BUILD_PCH $ngx_compile_opt \$(ALL_INCS)"
+ ngx_src="$ngx_src $ngx_objout$NGX_OBJS/ngx_pch.obj $NGX_OBJS/ngx_pch.c"
+ ngx_src=`echo $ngx_src | sed -e "s/\//$ngx_regex_dirsep/g"`
+
+ cat << END >> $NGX_MAKEFILE
+
+$ngx_pch
+ $ngx_src
+
+END
+
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/modules b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/modules
new file mode 100644
index 00000000000..78859676357
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/modules
@@ -0,0 +1,534 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+if [ $EVENT_SELECT = NO -a $EVENT_FOUND = NO ]; then
+ EVENT_SELECT=YES
+fi
+
+if [ $EVENT_SELECT = YES ]; then
+ have=NGX_HAVE_SELECT . auto/have
+ CORE_SRCS="$CORE_SRCS $SELECT_SRCS"
+ EVENT_MODULES="$EVENT_MODULES $SELECT_MODULE"
+fi
+
+
+if [ $EVENT_POLL = NO -a $EVENT_FOUND = NO ]; then
+ EVENT_POLL=YES
+fi
+
+if [ $EVENT_POLL = YES ]; then
+ have=NGX_HAVE_POLL . auto/have
+ CORE_SRCS="$CORE_SRCS $POLL_SRCS"
+ EVENT_MODULES="$EVENT_MODULES $POLL_MODULE"
+fi
+
+
+if [ $NGX_TEST_BUILD_DEVPOLL = YES ]; then
+ have=NGX_HAVE_DEVPOLL . auto/have
+ have=NGX_TEST_BUILD_DEVPOLL . auto/have
+ EVENT_MODULES="$EVENT_MODULES $DEVPOLL_MODULE"
+ CORE_SRCS="$CORE_SRCS $DEVPOLL_SRCS"
+fi
+
+
+if [ $NGX_TEST_BUILD_EVENTPORT = YES ]; then
+ have=NGX_HAVE_EVENTPORT . auto/have
+ have=NGX_TEST_BUILD_EVENTPORT . auto/have
+ EVENT_MODULES="$EVENT_MODULES $EVENTPORT_MODULE"
+ CORE_SRCS="$CORE_SRCS $EVENTPORT_SRCS"
+fi
+
+if [ $NGX_TEST_BUILD_EPOLL = YES ]; then
+ have=NGX_HAVE_EPOLL . auto/have
+ have=NGX_HAVE_EPOLLRDHUP . auto/have
+ have=NGX_HAVE_EVENTFD . auto/have
+ have=NGX_TEST_BUILD_EPOLL . auto/have
+ EVENT_MODULES="$EVENT_MODULES $EPOLL_MODULE"
+ CORE_SRCS="$CORE_SRCS $EPOLL_SRCS"
+fi
+
+if [ $NGX_TEST_BUILD_RTSIG = YES ]; then
+ have=NGX_HAVE_RTSIG . auto/have
+ have=NGX_TEST_BUILD_RTSIG . auto/have
+ EVENT_MODULES="$EVENT_MODULES $RTSIG_MODULE"
+ CORE_SRCS="$CORE_SRCS $RTSIG_SRCS"
+fi
+
+if [ $NGX_TEST_BUILD_SOLARIS_SENDFILEV = YES ]; then
+ have=NGX_TEST_BUILD_SOLARIS_SENDFILEV . auto/have
+ CORE_SRCS="$CORE_SRCS $SOLARIS_SENDFILEV_SRCS"
+fi
+
+
+if [ $HTTP != YES ]; then
+ have=NGX_CRYPT . auto/nohave
+ CRYPT_LIB=
+fi
+
+
+if [ $HTTP_CACHE = YES ]; then
+ USE_MD5=YES
+ have=NGX_HTTP_CACHE . auto/have
+ HTTP_SRCS="$HTTP_SRCS $HTTP_FILE_CACHE_SRCS"
+fi
+
+
+if [ $HTTP_SSI = YES ]; then
+ HTTP_POSTPONE=YES
+fi
+
+
+if [ $HTTP_ADDITION = YES ]; then
+ HTTP_POSTPONE=YES
+fi
+
+
+# the module order is important
+# ngx_http_static_module
+# ngx_http_gzip_static_module
+# ngx_http_dav_module
+# ngx_http_autoindex_module
+# ngx_http_index_module
+# ngx_http_random_index_module
+#
+# ngx_http_access_module
+# ngx_http_realip_module
+#
+#
+# the filter order is important
+# ngx_http_write_filter
+# ngx_http_header_filter
+# ngx_http_chunked_filter
+# ngx_http_spdy_filter
+# ngx_http_range_header_filter
+# ngx_http_gzip_filter
+# ngx_http_postpone_filter
+# ngx_http_ssi_filter
+# ngx_http_charset_filter
+# ngx_http_xslt_filter
+# ngx_http_image_filter
+# ngx_http_sub_filter
+# ngx_http_addition_filter
+# ngx_http_gunzip_filter
+# ngx_http_userid_filter
+# ngx_http_headers_filter
+# ngx_http_copy_filter
+# ngx_http_range_body_filter
+# ngx_http_not_modified_filter
+
+HTTP_FILTER_MODULES="$HTTP_WRITE_FILTER_MODULE \
+ $HTTP_HEADER_FILTER_MODULE \
+ $HTTP_CHUNKED_FILTER_MODULE"
+
+if [ $HTTP_SPDY = YES ]; then
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SPDY_FILTER_MODULE"
+fi
+
+HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_RANGE_HEADER_FILTER_MODULE"
+
+if [ $HTTP_GZIP = YES ]; then
+ have=NGX_HTTP_GZIP . auto/have
+ USE_ZLIB=YES
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_GZIP_FILTER_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_GZIP_SRCS"
+fi
+
+if [ $HTTP_POSTPONE = YES ]; then
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_POSTPONE_FILTER_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_POSTPONE_FILTER_SRCS"
+fi
+
+if [ $HTTP_SSI = YES ]; then
+ have=NGX_HTTP_SSI . auto/have
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SSI_FILTER_MODULE"
+ HTTP_DEPS="$HTTP_DEPS $HTTP_SSI_DEPS"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_SSI_SRCS"
+fi
+
+if [ $HTTP_CHARSET = YES ]; then
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_CHARSET_FILTER_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_CHARSET_SRCS"
+fi
+
+if [ $HTTP_XSLT = YES ]; then
+ USE_LIBXSLT=YES
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_XSLT_FILTER_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_XSLT_SRCS"
+fi
+
+if [ $HTTP_IMAGE_FILTER = YES ]; then
+ USE_LIBGD=YES
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_IMAGE_FILTER_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_IMAGE_SRCS"
+fi
+
+if [ $HTTP_SUB = YES ]; then
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SUB_FILTER_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_SUB_SRCS"
+fi
+
+if [ $HTTP_ADDITION = YES ]; then
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_ADDITION_FILTER_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_ADDITION_SRCS"
+fi
+
+if [ $HTTP_GUNZIP = YES ]; then
+ have=NGX_HTTP_GZIP . auto/have
+ USE_ZLIB=YES
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_GUNZIP_FILTER_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_GUNZIP_SRCS"
+fi
+
+if [ $HTTP_USERID = YES ]; then
+ HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_USERID_FILTER_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_USERID_SRCS"
+fi
+
+
+if [ $HTTP_SPDY = YES ]; then
+ have=NGX_HTTP_SPDY . auto/have
+ USE_ZLIB=YES
+ HTTP_MODULES="$HTTP_MODULES $HTTP_SPDY_MODULE"
+ HTTP_DEPS="$HTTP_DEPS $HTTP_SPDY_DEPS"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_SPDY_SRCS"
+fi
+
+HTTP_MODULES="$HTTP_MODULES $HTTP_STATIC_MODULE"
+
+if [ $HTTP_GZIP_STATIC = YES ]; then
+ have=NGX_HTTP_GZIP . auto/have
+ HTTP_MODULES="$HTTP_MODULES $HTTP_GZIP_STATIC_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_GZIP_STATIC_SRCS"
+fi
+
+if [ $HTTP_DAV = YES ]; then
+ have=NGX_HTTP_DAV . auto/have
+ HTTP_MODULES="$HTTP_MODULES $HTTP_DAV_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_DAV_SRCS"
+fi
+
+if [ $HTTP_AUTOINDEX = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_AUTOINDEX_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_AUTOINDEX_SRCS"
+fi
+
+HTTP_MODULES="$HTTP_MODULES $HTTP_INDEX_MODULE"
+
+if [ $HTTP_RANDOM_INDEX = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_RANDOM_INDEX_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_RANDOM_INDEX_SRCS"
+fi
+
+if [ $HTTP_AUTH_REQUEST = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_AUTH_REQUEST_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_AUTH_REQUEST_SRCS"
+fi
+
+if [ $HTTP_AUTH_BASIC = YES ]; then
+ USE_MD5=YES
+ USE_SHA1=YES
+ have=NGX_CRYPT . auto/have
+ HTTP_MODULES="$HTTP_MODULES $HTTP_AUTH_BASIC_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_AUTH_BASIC_SRCS"
+ CORE_LIBS="$CORE_LIBS $CRYPT_LIB"
+fi
+
+if [ $HTTP_ACCESS = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_ACCESS_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_ACCESS_SRCS"
+fi
+
+if [ $HTTP_LIMIT_CONN = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_LIMIT_CONN_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_CONN_SRCS"
+fi
+
+if [ $HTTP_LIMIT_REQ = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_LIMIT_REQ_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_LIMIT_REQ_SRCS"
+fi
+
+if [ $HTTP_REALIP = YES ]; then
+ have=NGX_HTTP_REALIP . auto/have
+ have=NGX_HTTP_X_FORWARDED_FOR . auto/have
+ HTTP_MODULES="$HTTP_MODULES $HTTP_REALIP_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_REALIP_SRCS"
+fi
+
+if [ $HTTP_STATUS = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_STATUS_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_STATUS_SRCS"
+fi
+
+if [ $HTTP_GEO = YES ]; then
+ have=NGX_HTTP_X_FORWARDED_FOR . auto/have
+ HTTP_MODULES="$HTTP_MODULES $HTTP_GEO_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_GEO_SRCS"
+fi
+
+if [ $HTTP_GEOIP = YES ]; then
+ have=NGX_HTTP_X_FORWARDED_FOR . auto/have
+ HTTP_MODULES="$HTTP_MODULES $HTTP_GEOIP_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_GEOIP_SRCS"
+fi
+
+if [ $HTTP_MAP = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_MAP_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_MAP_SRCS"
+fi
+
+if [ $HTTP_SPLIT_CLIENTS = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_SPLIT_CLIENTS_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_SPLIT_CLIENTS_SRCS"
+fi
+
+if [ $HTTP_REFERER = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_REFERER_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_REFERER_SRCS"
+fi
+
+if [ $HTTP_REWRITE = YES -a $USE_PCRE != DISABLED ]; then
+ USE_PCRE=YES
+ HTTP_MODULES="$HTTP_MODULES $HTTP_REWRITE_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_REWRITE_SRCS"
+fi
+
+if [ $HTTP_SSL = YES ]; then
+ USE_OPENSSL=YES
+ have=NGX_HTTP_SSL . auto/have
+ HTTP_MODULES="$HTTP_MODULES $HTTP_SSL_MODULE"
+ HTTP_DEPS="$HTTP_DEPS $HTTP_SSL_DEPS"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_SSL_SRCS"
+fi
+
+if [ $HTTP_PROXY = YES ]; then
+ have=NGX_HTTP_X_FORWARDED_FOR . auto/have
+ #USE_MD5=YES
+ HTTP_MODULES="$HTTP_MODULES $HTTP_PROXY_MODULE"
+ HTTP_DEPS="$HTTP_DEPS $HTTP_PROXY_DEPS"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_PROXY_SRCS"
+fi
+
+if [ $HTTP_FASTCGI = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_FASTCGI_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_FASTCGI_SRCS"
+fi
+
+if [ $HTTP_UWSGI = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_UWSGI_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_UWSGI_SRCS"
+fi
+
+if [ $HTTP_SCGI = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_SCGI_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_SCGI_SRCS"
+fi
+
+if [ $HTTP_PERL = YES ]; then
+ USE_PERL=YES
+ HTTP_MODULES="$HTTP_MODULES $HTTP_PERL_MODULE"
+ HTTP_INCS="$HTTP_INCS $HTTP_PERL_INCS"
+ HTTP_DEPS="$HTTP_DEPS $HTTP_PERL_DEPS"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_PERL_SRCS"
+fi
+
+if [ $HTTP_MEMCACHED = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_MEMCACHED_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_MEMCACHED_SRCS"
+fi
+
+if [ $HTTP_EMPTY_GIF = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_EMPTY_GIF_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_EMPTY_GIF_SRCS"
+fi
+
+if [ $HTTP_BROWSER = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_BROWSER_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_BROWSER_SRCS"
+fi
+
+if [ $HTTP_SECURE_LINK = YES ]; then
+ USE_MD5=YES
+ HTTP_MODULES="$HTTP_MODULES $HTTP_SECURE_LINK_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_SECURE_LINK_SRCS"
+fi
+
+if [ $HTTP_DEGRADATION = YES ]; then
+ have=NGX_HTTP_DEGRADATION . auto/have
+ HTTP_MODULES="$HTTP_MODULES $HTTP_DEGRADATION_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_DEGRADATION_SRCS"
+fi
+
+if [ $HTTP_FLV = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_FLV_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_FLV_SRCS"
+fi
+
+if [ $HTTP_MP4 = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_MP4_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_MP4_SRCS"
+fi
+
+if [ $HTTP_UPSTREAM_HASH = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_UPSTREAM_HASH_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_HASH_SRCS"
+fi
+
+if [ $HTTP_UPSTREAM_IP_HASH = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_UPSTREAM_IP_HASH_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_IP_HASH_SRCS"
+fi
+
+if [ $HTTP_UPSTREAM_LEAST_CONN = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_UPSTREAM_LEAST_CONN_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_LEAST_CONN_SRCS"
+fi
+
+if [ $HTTP_UPSTREAM_KEEPALIVE = YES ]; then
+ HTTP_MODULES="$HTTP_MODULES $HTTP_UPSTREAM_KEEPALIVE_MODULE"
+ HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_KEEPALIVE_SRCS"
+fi
+
+if [ $HTTP_STUB_STATUS = YES ]; then
+ have=NGX_STAT_STUB . auto/have
+ HTTP_MODULES="$HTTP_MODULES ngx_http_stub_status_module"
+ HTTP_SRCS="$HTTP_SRCS src/http/modules/ngx_http_stub_status_module.c"
+fi
+
+#if [ -r $NGX_OBJS/auto ]; then
+# . $NGX_OBJS/auto
+#fi
+
+
+if test -n "$NGX_ADDONS"; then
+
+ echo configuring additional modules
+
+ for ngx_addon_dir in $NGX_ADDONS
+ do
+ echo "adding module in $ngx_addon_dir"
+
+ if test -f $ngx_addon_dir/config; then
+ . $ngx_addon_dir/config
+
+ echo " + $ngx_addon_name was configured"
+
+ else
+ echo "$0: error: no $ngx_addon_dir/config was found"
+ exit 1
+ fi
+ done
+fi
+
+
+if [ $MAIL_SSL = YES ]; then
+ have=NGX_MAIL_SSL . auto/have
+ USE_OPENSSL=YES
+fi
+
+
+modules="$CORE_MODULES $EVENT_MODULES"
+
+
+if [ $USE_OPENSSL = YES ]; then
+ modules="$modules $OPENSSL_MODULE"
+ CORE_DEPS="$CORE_DEPS $OPENSSL_DEPS"
+ CORE_SRCS="$CORE_SRCS $OPENSSL_SRCS"
+fi
+
+if [ $USE_PCRE = YES ]; then
+ modules="$modules $REGEX_MODULE"
+ CORE_DEPS="$CORE_DEPS $REGEX_DEPS"
+ CORE_SRCS="$CORE_SRCS $REGEX_SRCS"
+fi
+
+if [ $HTTP = YES ]; then
+ modules="$modules $HTTP_MODULES $HTTP_FILTER_MODULES \
+ $HTTP_HEADERS_FILTER_MODULE \
+ $HTTP_AUX_FILTER_MODULES \
+ $HTTP_COPY_FILTER_MODULE \
+ $HTTP_RANGE_BODY_FILTER_MODULE \
+ $HTTP_NOT_MODIFIED_FILTER_MODULE"
+
+ NGX_ADDON_DEPS="$NGX_ADDON_DEPS \$(HTTP_DEPS)"
+fi
+
+
+if [ $MAIL = YES ]; then
+ modules="$modules $MAIL_MODULES"
+
+ if [ $MAIL_SSL = YES ]; then
+ modules="$modules $MAIL_SSL_MODULE"
+ MAIL_DEPS="$MAIL_DEPS $MAIL_SSL_DEPS"
+ MAIL_SRCS="$MAIL_SRCS $MAIL_SSL_SRCS"
+ fi
+
+ if [ $MAIL_POP3 = YES ]; then
+ modules="$modules $MAIL_POP3_MODULE"
+ MAIL_DEPS="$MAIL_DEPS $MAIL_POP3_DEPS"
+ MAIL_SRCS="$MAIL_SRCS $MAIL_POP3_SRCS"
+ fi
+
+ if [ $MAIL_IMAP = YES ]; then
+ modules="$modules $MAIL_IMAP_MODULE"
+ MAIL_DEPS="$MAIL_DEPS $MAIL_IMAP_DEPS"
+ MAIL_SRCS="$MAIL_SRCS $MAIL_IMAP_SRCS"
+ fi
+
+ if [ $MAIL_SMTP = YES ]; then
+ modules="$modules $MAIL_SMTP_MODULE"
+ MAIL_DEPS="$MAIL_DEPS $MAIL_SMTP_DEPS"
+ MAIL_SRCS="$MAIL_SRCS $MAIL_SMTP_SRCS"
+ fi
+
+ modules="$modules $MAIL_AUTH_HTTP_MODULE"
+ MAIL_SRCS="$MAIL_SRCS $MAIL_AUTH_HTTP_SRCS"
+
+ modules="$modules $MAIL_PROXY_MODULE"
+ MAIL_SRCS="$MAIL_SRCS $MAIL_PROXY_SRCS"
+
+ NGX_ADDON_DEPS="$NGX_ADDON_DEPS \$(MAIL_DEPS)"
+fi
+
+
+if [ $NGX_GOOGLE_PERFTOOLS = YES ]; then
+ modules="$modules $NGX_GOOGLE_PERFTOOLS_MODULE"
+ NGX_MISC_SRCS="$NGX_MISC_SRCS $NGX_GOOGLE_PERFTOOLS_SRCS"
+fi
+
+
+if [ $NGX_CPP_TEST = YES ]; then
+ NGX_MISC_SRCS="$NGX_MISC_SRCS $NGX_CPP_TEST_SRCS"
+ CORE_LIBS="$CORE_LIBS -lstdc++"
+fi
+
+
+cat << END > $NGX_MODULES_C
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+$NGX_PRAGMA
+
+END
+
+for mod in $modules
+do
+ echo "extern ngx_module_t $mod;" >> $NGX_MODULES_C
+done
+
+echo >> $NGX_MODULES_C
+echo 'ngx_module_t *ngx_modules[] = {' >> $NGX_MODULES_C
+
+for mod in $modules
+do
+ echo " &$mod," >> $NGX_MODULES_C
+done
+
+cat << END >> $NGX_MODULES_C
+ NULL
+};
+
+END
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/nohave b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/nohave
new file mode 100644
index 00000000000..dfb171837c5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/nohave
@@ -0,0 +1,12 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+cat << END >> $NGX_AUTO_CONFIG_H
+
+#ifndef $have
+#define $have 0
+#endif
+
+END
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/options b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/options
new file mode 100644
index 00000000000..0d296ac6027
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/options
@@ -0,0 +1,527 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+help=no
+
+NGX_PREFIX=
+NGX_SBIN_PATH=
+NGX_CONF_PREFIX=
+NGX_CONF_PATH=
+NGX_ERROR_LOG_PATH=
+NGX_PID_PATH=
+NGX_LOCK_PATH=
+NGX_USER=
+NGX_GROUP=
+NGX_BUILD=
+
+CC=${CC:-cc}
+CPP=
+NGX_OBJS=objs
+
+NGX_DEBUG=NO
+NGX_CC_OPT=
+NGX_LD_OPT=
+CPU=NO
+
+NGX_RPATH=NO
+
+NGX_TEST_BUILD_DEVPOLL=NO
+NGX_TEST_BUILD_EVENTPORT=NO
+NGX_TEST_BUILD_EPOLL=NO
+NGX_TEST_BUILD_RTSIG=NO
+NGX_TEST_BUILD_SOLARIS_SENDFILEV=NO
+
+NGX_PLATFORM=
+NGX_WINE=
+
+EVENT_FOUND=NO
+
+EVENT_RTSIG=NO
+EVENT_SELECT=NO
+EVENT_POLL=NO
+EVENT_AIO=NO
+
+USE_THREADS=NO
+
+NGX_FILE_AIO=NO
+NGX_IPV6=NO
+
+HTTP=YES
+
+NGX_HTTP_LOG_PATH=
+NGX_HTTP_CLIENT_TEMP_PATH=
+NGX_HTTP_PROXY_TEMP_PATH=
+NGX_HTTP_FASTCGI_TEMP_PATH=
+NGX_HTTP_UWSGI_TEMP_PATH=
+NGX_HTTP_SCGI_TEMP_PATH=
+
+HTTP_CACHE=YES
+HTTP_CHARSET=YES
+HTTP_GZIP=YES
+HTTP_SSL=NO
+HTTP_SPDY=NO
+HTTP_SSI=YES
+HTTP_POSTPONE=NO
+HTTP_REALIP=NO
+HTTP_XSLT=NO
+HTTP_IMAGE_FILTER=NO
+HTTP_SUB=NO
+HTTP_ADDITION=NO
+HTTP_DAV=NO
+HTTP_ACCESS=YES
+HTTP_AUTH_BASIC=YES
+HTTP_AUTH_REQUEST=NO
+HTTP_USERID=YES
+HTTP_AUTOINDEX=YES
+HTTP_RANDOM_INDEX=NO
+HTTP_STATUS=NO
+HTTP_GEO=YES
+HTTP_GEOIP=NO
+HTTP_MAP=YES
+HTTP_SPLIT_CLIENTS=YES
+HTTP_REFERER=YES
+HTTP_REWRITE=YES
+HTTP_PROXY=YES
+HTTP_FASTCGI=YES
+HTTP_UWSGI=YES
+HTTP_SCGI=YES
+HTTP_PERL=NO
+HTTP_MEMCACHED=YES
+HTTP_LIMIT_CONN=YES
+HTTP_LIMIT_REQ=YES
+HTTP_EMPTY_GIF=YES
+HTTP_BROWSER=YES
+HTTP_SECURE_LINK=NO
+HTTP_DEGRADATION=NO
+HTTP_FLV=NO
+HTTP_MP4=NO
+HTTP_GUNZIP=NO
+HTTP_GZIP_STATIC=NO
+HTTP_UPSTREAM_HASH=YES
+HTTP_UPSTREAM_IP_HASH=YES
+HTTP_UPSTREAM_LEAST_CONN=YES
+HTTP_UPSTREAM_KEEPALIVE=YES
+
+# STUB
+HTTP_STUB_STATUS=NO
+
+MAIL=NO
+MAIL_SSL=NO
+MAIL_POP3=YES
+MAIL_IMAP=YES
+MAIL_SMTP=YES
+
+NGX_ADDONS=
+
+USE_PCRE=NO
+PCRE=NONE
+PCRE_OPT=
+PCRE_CONF_OPT=
+PCRE_JIT=NO
+
+USE_OPENSSL=NO
+OPENSSL=NONE
+
+USE_MD5=NO
+MD5=NONE
+MD5_OPT=
+MD5_ASM=NO
+
+USE_SHA1=NO
+SHA1=NONE
+SHA1_OPT=
+SHA1_ASM=NO
+
+USE_ZLIB=NO
+ZLIB=NONE
+ZLIB_OPT=
+ZLIB_ASM=NO
+
+USE_PERL=NO
+NGX_PERL=perl
+
+USE_LIBXSLT=NO
+USE_LIBGD=NO
+
+NGX_GOOGLE_PERFTOOLS=NO
+NGX_CPP_TEST=NO
+
+NGX_LIBATOMIC=NO
+
+NGX_CPU_CACHE_LINE=
+
+NGX_POST_CONF_MSG=
+
+opt=
+
+for option
+do
+ opt="$opt `echo $option | sed -e \"s/\(--[^=]*=\)\(.* .*\)/\1'\2'/\"`"
+
+ case "$option" in
+ -*=*) value=`echo "$option" | sed -e 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) value="" ;;
+ esac
+
+ case "$option" in
+ --help) help=yes ;;
+
+ --prefix=) NGX_PREFIX="!" ;;
+ --prefix=*) NGX_PREFIX="$value" ;;
+ --sbin-path=*) NGX_SBIN_PATH="$value" ;;
+ --conf-path=*) NGX_CONF_PATH="$value" ;;
+ --error-log-path=*) NGX_ERROR_LOG_PATH="$value";;
+ --pid-path=*) NGX_PID_PATH="$value" ;;
+ --lock-path=*) NGX_LOCK_PATH="$value" ;;
+ --user=*) NGX_USER="$value" ;;
+ --group=*) NGX_GROUP="$value" ;;
+
+ --crossbuild=*) NGX_PLATFORM="$value" ;;
+
+ --build=*) NGX_BUILD="$value" ;;
+ --builddir=*) NGX_OBJS="$value" ;;
+
+ --with-rtsig_module) EVENT_RTSIG=YES ;;
+ --with-select_module) EVENT_SELECT=YES ;;
+ --without-select_module) EVENT_SELECT=NONE ;;
+ --with-poll_module) EVENT_POLL=YES ;;
+ --without-poll_module) EVENT_POLL=NONE ;;
+ --with-aio_module) EVENT_AIO=YES ;;
+
+ #--with-threads=*) USE_THREADS="$value" ;;
+ #--with-threads) USE_THREADS="pthreads" ;;
+
+ --with-file-aio) NGX_FILE_AIO=YES ;;
+ --with-ipv6) NGX_IPV6=YES ;;
+
+ --without-http) HTTP=NO ;;
+ --without-http-cache) HTTP_CACHE=NO ;;
+
+ --http-log-path=*) NGX_HTTP_LOG_PATH="$value" ;;
+ --http-client-body-temp-path=*) NGX_HTTP_CLIENT_TEMP_PATH="$value" ;;
+ --http-proxy-temp-path=*) NGX_HTTP_PROXY_TEMP_PATH="$value" ;;
+ --http-fastcgi-temp-path=*) NGX_HTTP_FASTCGI_TEMP_PATH="$value" ;;
+ --http-uwsgi-temp-path=*) NGX_HTTP_UWSGI_TEMP_PATH="$value" ;;
+ --http-scgi-temp-path=*) NGX_HTTP_SCGI_TEMP_PATH="$value" ;;
+
+ --with-http_ssl_module) HTTP_SSL=YES ;;
+ --with-http_spdy_module) HTTP_SPDY=YES ;;
+ --with-http_realip_module) HTTP_REALIP=YES ;;
+ --with-http_addition_module) HTTP_ADDITION=YES ;;
+ --with-http_xslt_module) HTTP_XSLT=YES ;;
+ --with-http_image_filter_module) HTTP_IMAGE_FILTER=YES ;;
+ --with-http_geoip_module) HTTP_GEOIP=YES ;;
+ --with-http_sub_module) HTTP_SUB=YES ;;
+ --with-http_dav_module) HTTP_DAV=YES ;;
+ --with-http_flv_module) HTTP_FLV=YES ;;
+ --with-http_mp4_module) HTTP_MP4=YES ;;
+ --with-http_gunzip_module) HTTP_GUNZIP=YES ;;
+ --with-http_gzip_static_module) HTTP_GZIP_STATIC=YES ;;
+ --with-http_auth_request_module) HTTP_AUTH_REQUEST=YES ;;
+ --with-http_random_index_module) HTTP_RANDOM_INDEX=YES ;;
+ --with-http_secure_link_module) HTTP_SECURE_LINK=YES ;;
+ --with-http_degradation_module) HTTP_DEGRADATION=YES ;;
+
+ --without-http_charset_module) HTTP_CHARSET=NO ;;
+ --without-http_gzip_module) HTTP_GZIP=NO ;;
+ --without-http_ssi_module) HTTP_SSI=NO ;;
+ --without-http_userid_module) HTTP_USERID=NO ;;
+ --without-http_access_module) HTTP_ACCESS=NO ;;
+ --without-http_auth_basic_module) HTTP_AUTH_BASIC=NO ;;
+ --without-http_autoindex_module) HTTP_AUTOINDEX=NO ;;
+ --without-http_status_module) HTTP_STATUS=NO ;;
+ --without-http_geo_module) HTTP_GEO=NO ;;
+ --without-http_map_module) HTTP_MAP=NO ;;
+ --without-http_split_clients_module) HTTP_SPLIT_CLIENTS=NO ;;
+ --without-http_referer_module) HTTP_REFERER=NO ;;
+ --without-http_rewrite_module) HTTP_REWRITE=NO ;;
+ --without-http_proxy_module) HTTP_PROXY=NO ;;
+ --without-http_fastcgi_module) HTTP_FASTCGI=NO ;;
+ --without-http_uwsgi_module) HTTP_UWSGI=NO ;;
+ --without-http_scgi_module) HTTP_SCGI=NO ;;
+ --without-http_memcached_module) HTTP_MEMCACHED=NO ;;
+ --without-http_limit_zone_module)
+ HTTP_LIMIT_CONN=NO
+ NGX_POST_CONF_MSG="$NGX_POST_CONF_MSG
+$0: warning: the \"--without-http_limit_zone_module\" option is deprecated, \
+use the \"--without-http_limit_conn_module\" option instead"
+ ;;
+ --without-http_limit_conn_module) HTTP_LIMIT_CONN=NO ;;
+ --without-http_limit_req_module) HTTP_LIMIT_REQ=NO ;;
+ --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO ;;
+ --without-http_browser_module) HTTP_BROWSER=NO ;;
+ --without-http_upstream_hash_module) HTTP_UPSTREAM_HASH=NO ;;
+ --without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;;
+ --without-http_upstream_least_conn_module)
+ HTTP_UPSTREAM_LEAST_CONN=NO ;;
+ --without-http_upstream_keepalive_module) HTTP_UPSTREAM_KEEPALIVE=NO ;;
+
+ --with-http_perl_module) HTTP_PERL=YES ;;
+ --with-perl_modules_path=*) NGX_PERL_MODULES="$value" ;;
+ --with-perl=*) NGX_PERL="$value" ;;
+
+ # STUB
+ --with-http_stub_status_module) HTTP_STUB_STATUS=YES ;;
+
+ --with-mail) MAIL=YES ;;
+ --with-mail_ssl_module) MAIL_SSL=YES ;;
+ # STUB
+ --with-imap) MAIL=YES ;;
+ --with-imap_ssl_module) MAIL_SSL=YES ;;
+ --without-mail_pop3_module) MAIL_POP3=NO ;;
+ --without-mail_imap_module) MAIL_IMAP=NO ;;
+ --without-mail_smtp_module) MAIL_SMTP=NO ;;
+
+ --with-google_perftools_module) NGX_GOOGLE_PERFTOOLS=YES ;;
+ --with-cpp_test_module) NGX_CPP_TEST=YES ;;
+
+ --add-module=*) NGX_ADDONS="$NGX_ADDONS $value" ;;
+
+ --with-cc=*) CC="$value" ;;
+ --with-cpp=*) CPP="$value" ;;
+ --with-cc-opt=*) NGX_CC_OPT="$value" ;;
+ --with-ld-opt=*) NGX_LD_OPT="$value" ;;
+ --with-cpu-opt=*) CPU="$value" ;;
+ --with-debug) NGX_DEBUG=YES ;;
+
+ --without-pcre) USE_PCRE=DISABLED ;;
+ --with-pcre) USE_PCRE=YES ;;
+ --with-pcre=*) PCRE="$value" ;;
+ --with-pcre-opt=*) PCRE_OPT="$value" ;;
+ --with-pcre-jit) PCRE_JIT=YES ;;
+
+ --with-openssl=*) OPENSSL="$value" ;;
+ --with-openssl-opt=*) OPENSSL_OPT="$value" ;;
+
+ --with-md5=*) MD5="$value" ;;
+ --with-md5-opt=*) MD5_OPT="$value" ;;
+ --with-md5-asm) MD5_ASM=YES ;;
+
+ --with-sha1=*) SHA1="$value" ;;
+ --with-sha1-opt=*) SHA1_OPT="$value" ;;
+ --with-sha1-asm) SHA1_ASM=YES ;;
+
+ --with-zlib=*) ZLIB="$value" ;;
+ --with-zlib-opt=*) ZLIB_OPT="$value" ;;
+ --with-zlib-asm=*) ZLIB_ASM="$value" ;;
+
+ --with-libatomic) NGX_LIBATOMIC=YES ;;
+ --with-libatomic=*) NGX_LIBATOMIC="$value" ;;
+
+ --test-build-devpoll) NGX_TEST_BUILD_DEVPOLL=YES ;;
+ --test-build-eventport) NGX_TEST_BUILD_EVENTPORT=YES ;;
+ --test-build-epoll) NGX_TEST_BUILD_EPOLL=YES ;;
+ --test-build-rtsig) NGX_TEST_BUILD_RTSIG=YES ;;
+ --test-build-solaris-sendfilev) NGX_TEST_BUILD_SOLARIS_SENDFILEV=YES ;;
+
+ *)
+ echo "$0: error: invalid option \"$option\""
+ exit 1
+ ;;
+ esac
+done
+
+
+NGX_CONFIGURE="$opt"
+
+
+if [ $help = yes ]; then
+
+cat << END
+
+ --help print this message
+
+ --prefix=PATH set installation prefix
+ --sbin-path=PATH set nginx binary pathname
+ --conf-path=PATH set nginx.conf pathname
+ --error-log-path=PATH set error log pathname
+ --pid-path=PATH set nginx.pid pathname
+ --lock-path=PATH set nginx.lock pathname
+
+ --user=USER set non-privileged user for
+ worker processes
+ --group=GROUP set non-privileged group for
+ worker processes
+
+ --build=NAME set build name
+ --builddir=DIR set build directory
+
+ --with-rtsig_module enable rtsig module
+ --with-select_module enable select module
+ --without-select_module disable select module
+ --with-poll_module enable poll module
+ --without-poll_module disable poll module
+
+ --with-file-aio enable file AIO support
+ --with-ipv6 enable IPv6 support
+
+ --with-http_ssl_module enable ngx_http_ssl_module
+ --with-http_spdy_module enable ngx_http_spdy_module
+ --with-http_realip_module enable ngx_http_realip_module
+ --with-http_addition_module enable ngx_http_addition_module
+ --with-http_xslt_module enable ngx_http_xslt_module
+ --with-http_image_filter_module enable ngx_http_image_filter_module
+ --with-http_geoip_module enable ngx_http_geoip_module
+ --with-http_sub_module enable ngx_http_sub_module
+ --with-http_dav_module enable ngx_http_dav_module
+ --with-http_flv_module enable ngx_http_flv_module
+ --with-http_mp4_module enable ngx_http_mp4_module
+ --with-http_gunzip_module enable ngx_http_gunzip_module
+ --with-http_gzip_static_module enable ngx_http_gzip_static_module
+ --with-http_auth_request_module enable ngx_http_auth_request_module
+ --with-http_random_index_module enable ngx_http_random_index_module
+ --with-http_secure_link_module enable ngx_http_secure_link_module
+ --with-http_degradation_module enable ngx_http_degradation_module
+ --with-http_stub_status_module enable ngx_http_stub_status_module
+
+ --without-http_charset_module disable ngx_http_charset_module
+ --without-http_gzip_module disable ngx_http_gzip_module
+ --without-http_ssi_module disable ngx_http_ssi_module
+ --without-http_userid_module disable ngx_http_userid_module
+ --without-http_access_module disable ngx_http_access_module
+ --without-http_auth_basic_module disable ngx_http_auth_basic_module
+ --without-http_autoindex_module disable ngx_http_autoindex_module
+ --without-http_geo_module disable ngx_http_geo_module
+ --without-http_map_module disable ngx_http_map_module
+ --without-http_split_clients_module disable ngx_http_split_clients_module
+ --without-http_referer_module disable ngx_http_referer_module
+ --without-http_rewrite_module disable ngx_http_rewrite_module
+ --without-http_proxy_module disable ngx_http_proxy_module
+ --without-http_fastcgi_module disable ngx_http_fastcgi_module
+ --without-http_uwsgi_module disable ngx_http_uwsgi_module
+ --without-http_scgi_module disable ngx_http_scgi_module
+ --without-http_memcached_module disable ngx_http_memcached_module
+ --without-http_limit_conn_module disable ngx_http_limit_conn_module
+ --without-http_limit_req_module disable ngx_http_limit_req_module
+ --without-http_empty_gif_module disable ngx_http_empty_gif_module
+ --without-http_browser_module disable ngx_http_browser_module
+ --without-http_upstream_hash_module
+ disable ngx_http_upstream_hash_module
+ --without-http_upstream_ip_hash_module
+ disable ngx_http_upstream_ip_hash_module
+ --without-http_upstream_least_conn_module
+ disable ngx_http_upstream_least_conn_module
+ --without-http_upstream_keepalive_module
+ disable ngx_http_upstream_keepalive_module
+
+ --with-http_perl_module enable ngx_http_perl_module
+ --with-perl_modules_path=PATH set Perl modules path
+ --with-perl=PATH set perl binary pathname
+
+ --http-log-path=PATH set http access log pathname
+ --http-client-body-temp-path=PATH set path to store
+ http client request body temporary files
+ --http-proxy-temp-path=PATH set path to store
+ http proxy temporary files
+ --http-fastcgi-temp-path=PATH set path to store
+ http fastcgi temporary files
+ --http-uwsgi-temp-path=PATH set path to store
+ http uwsgi temporary files
+ --http-scgi-temp-path=PATH set path to store
+ http scgi temporary files
+
+ --without-http disable HTTP server
+ --without-http-cache disable HTTP cache
+
+ --with-mail enable POP3/IMAP4/SMTP proxy module
+ --with-mail_ssl_module enable ngx_mail_ssl_module
+ --without-mail_pop3_module disable ngx_mail_pop3_module
+ --without-mail_imap_module disable ngx_mail_imap_module
+ --without-mail_smtp_module disable ngx_mail_smtp_module
+
+ --with-google_perftools_module enable ngx_google_perftools_module
+ --with-cpp_test_module enable ngx_cpp_test_module
+
+ --add-module=PATH enable an external module
+
+ --with-cc=PATH set C compiler pathname
+ --with-cpp=PATH set C preprocessor pathname
+ --with-cc-opt=OPTIONS set additional C compiler options
+ --with-ld-opt=OPTIONS set additional linker options
+ --with-cpu-opt=CPU build for the specified CPU, valid values:
+ pentium, pentiumpro, pentium3, pentium4,
+ athlon, opteron, sparc32, sparc64, ppc64
+
+ --without-pcre disable PCRE library usage
+ --with-pcre force PCRE library usage
+ --with-pcre=DIR set path to PCRE library sources
+ --with-pcre-opt=OPTIONS set additional build options for PCRE
+ --with-pcre-jit build PCRE with JIT compilation support
+
+ --with-md5=DIR set path to md5 library sources
+ --with-md5-opt=OPTIONS set additional build options for md5
+ --with-md5-asm use md5 assembler sources
+
+ --with-sha1=DIR set path to sha1 library sources
+ --with-sha1-opt=OPTIONS set additional build options for sha1
+ --with-sha1-asm use sha1 assembler sources
+
+ --with-zlib=DIR set path to zlib library sources
+ --with-zlib-opt=OPTIONS set additional build options for zlib
+ --with-zlib-asm=CPU use zlib assembler sources optimized
+ for the specified CPU, valid values:
+ pentium, pentiumpro
+
+ --with-libatomic force libatomic_ops library usage
+ --with-libatomic=DIR set path to libatomic_ops library sources
+
+ --with-openssl=DIR set path to OpenSSL library sources
+ --with-openssl-opt=OPTIONS set additional build options for OpenSSL
+
+ --with-debug enable debug logging
+
+END
+
+ exit 1
+fi
+
+
+if [ $HTTP = NO ]; then
+ HTTP_CHARSET=NO
+ HTTP_GZIP=NO
+ HTTP_SSI=NO
+ HTTP_USERID=NO
+ HTTP_ACCESS=NO
+ HTTP_STATUS=NO
+ HTTP_REWRITE=NO
+ HTTP_PROXY=NO
+ HTTP_FASTCGI=NO
+fi
+
+
+if [ ".$NGX_PLATFORM" = ".win32" ]; then
+ NGX_WINE=$WINE
+fi
+
+
+NGX_CONF_PATH=${NGX_CONF_PATH:-conf/nginx.conf}
+NGX_CONF_PREFIX=`dirname $NGX_CONF_PATH`
+NGX_PID_PATH=${NGX_PID_PATH:-logs/nginx.pid}
+NGX_LOCK_PATH=${NGX_LOCK_PATH:-logs/nginx.lock}
+
+if [ ".$NGX_ERROR_LOG_PATH" = ".stderr" ]; then
+ NGX_ERROR_LOG_PATH=
+else
+ NGX_ERROR_LOG_PATH=${NGX_ERROR_LOG_PATH:-logs/error.log}
+fi
+
+NGX_HTTP_LOG_PATH=${NGX_HTTP_LOG_PATH:-logs/access.log}
+NGX_HTTP_CLIENT_TEMP_PATH=${NGX_HTTP_CLIENT_TEMP_PATH:-client_body_temp}
+NGX_HTTP_PROXY_TEMP_PATH=${NGX_HTTP_PROXY_TEMP_PATH:-proxy_temp}
+NGX_HTTP_FASTCGI_TEMP_PATH=${NGX_HTTP_FASTCGI_TEMP_PATH:-fastcgi_temp}
+NGX_HTTP_UWSGI_TEMP_PATH=${NGX_HTTP_UWSGI_TEMP_PATH:-uwsgi_temp}
+NGX_HTTP_SCGI_TEMP_PATH=${NGX_HTTP_SCGI_TEMP_PATH:-scgi_temp}
+
+case ".$NGX_PERL_MODULES" in
+ ./*)
+ ;;
+
+ .)
+ ;;
+
+ *)
+ NGX_PERL_MODULES=$NGX_PREFIX/$NGX_PERL_MODULES
+ ;;
+esac
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/conf
new file mode 100644
index 00000000000..fe720160ada
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/conf
@@ -0,0 +1,107 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+echo "checking for $NGX_SYSTEM specific features"
+
+case "$NGX_PLATFORM" in
+
+ FreeBSD:*)
+ . auto/os/freebsd
+ ;;
+
+ Linux:*)
+ . auto/os/linux
+ ;;
+
+ SunOS:*)
+ . auto/os/solaris
+ ;;
+
+ Darwin:*)
+ . auto/os/darwin
+ ;;
+
+ win32)
+ . auto/os/win32
+ ;;
+
+ DragonFly:*)
+ have=NGX_FREEBSD . auto/have_headers
+ CORE_INCS="$UNIX_INCS"
+ CORE_DEPS="$UNIX_DEPS $FREEBSD_DEPS"
+ CORE_SRCS="$UNIX_SRCS $FREEBSD_SRCS"
+
+ echo " + sendfile() found"
+ have=NGX_HAVE_SENDFILE . auto/have
+ CORE_SRCS="$CORE_SRCS $FREEBSD_SENDFILE_SRCS"
+
+ ngx_spacer='
+'
+ ;;
+
+ HP-UX:*)
+ # HP/UX
+ have=NGX_HPUX . auto/have_headers
+ CORE_INCS="$UNIX_INCS"
+ CORE_DEPS="$UNIX_DEPS $POSIX_DEPS"
+ CORE_SRCS="$UNIX_SRCS"
+ CC_AUX_FLAGS="$CC_AUX_FLAGS -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1"
+ CC_AUX_FLAGS="$CC_AUX_FLAGS -D_HPUX_ALT_XOPEN_SOCKET_API"
+ ;;
+
+ OSF1:*)
+ # Tru64 UNIX
+ have=NGX_TRU64 . auto/have_headers
+ have=NGX_HAVE_STRERROR_R . auto/nohave
+ CORE_INCS="$UNIX_INCS"
+ CORE_DEPS="$UNIX_DEPS $POSIX_DEPS"
+ CORE_SRCS="$UNIX_SRCS"
+ ;;
+
+ *)
+ CORE_INCS="$UNIX_INCS"
+ CORE_DEPS="$UNIX_DEPS $POSIX_DEPS"
+ CORE_SRCS="$UNIX_SRCS"
+ ;;
+
+esac
+
+
+case "$NGX_MACHINE" in
+
+ i386 | i686 | i86pc)
+ have=NGX_HAVE_NONALIGNED . auto/have
+ NGX_MACH_CACHE_LINE=32
+ ;;
+
+ amd64 | x86_64)
+ have=NGX_HAVE_NONALIGNED . auto/have
+ NGX_MACH_CACHE_LINE=64
+ ;;
+
+ sun4u | sun4v | sparc | sparc64)
+ have=NGX_ALIGNMENT value=16 . auto/define
+ # TODO
+ NGX_MACH_CACHE_LINE=64
+ ;;
+
+ ia64 )
+ have=NGX_ALIGNMENT value=16 . auto/define
+ # TODO
+ NGX_MACH_CACHE_LINE=64
+ ;;
+
+ *)
+ have=NGX_ALIGNMENT value=16 . auto/define
+ NGX_MACH_CACHE_LINE=32
+ ;;
+
+esac
+
+if test -z "$NGX_CPU_CACHE_LINE"; then
+ NGX_CPU_CACHE_LINE=$NGX_MACH_CACHE_LINE
+fi
+
+have=NGX_CPU_CACHE_LINE value=$NGX_CPU_CACHE_LINE . auto/define
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/darwin b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/darwin
new file mode 100644
index 00000000000..b97518a6ea0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/darwin
@@ -0,0 +1,116 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+have=NGX_DARWIN . auto/have_headers
+
+CORE_INCS="$UNIX_INCS"
+CORE_DEPS="$UNIX_DEPS $DARWIN_DEPS"
+CORE_SRCS="$UNIX_SRCS $DARWIN_SRCS"
+
+
+
+ngx_spacer='
+'
+
+# kqueue
+
+echo " + kqueue found"
+have=NGX_HAVE_KQUEUE . auto/have
+have=NGX_HAVE_CLEAR_EVENT . auto/have
+EVENT_MODULES="$EVENT_MODULES $KQUEUE_MODULE"
+CORE_SRCS="$CORE_SRCS $KQUEUE_SRCS"
+EVENT_FOUND=YES
+NGX_KQUEUE_CHECKED=YES
+
+ngx_feature="kqueue's EVFILT_TIMER"
+ngx_feature_name="NGX_HAVE_TIMER_EVENT"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/event.h>
+ #include <sys/time.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int kq;
+ struct kevent kev;
+ struct timespec ts;
+
+ if ((kq = kqueue()) == -1) return 1;
+
+ kev.ident = 0;
+ kev.filter = EVFILT_TIMER;
+ kev.flags = EV_ADD|EV_ENABLE;
+ kev.fflags = 0;
+ kev.data = 1000;
+ kev.udata = 0;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+
+ if (kevent(kq, &kev, 1, &kev, 1, &ts) == -1) return 1;
+
+ if (kev.flags & EV_ERROR) return 1;"
+
+. auto/feature
+
+
+ngx_feature="Darwin 64-bit kqueue millisecond timeout bug"
+ngx_feature_name=NGX_DARWIN_KEVENT_BUG
+ngx_feature_run=bug
+ngx_feature_incs="#include <sys/event.h>
+ #include <sys/time.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int kq;
+ struct kevent kev;
+ struct timespec ts;
+ struct timeval tv, tv0;
+
+ kq = kqueue();
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 999000000;
+
+ gettimeofday(&tv, 0);
+ kevent(kq, NULL, 0, &kev, 1, &ts);
+ gettimeofday(&tv0, 0);
+ timersub(&tv0, &tv, &tv);
+
+ if (tv.tv_sec * 1000000 + tv.tv_usec < 900000) return 1;"
+
+. auto/feature
+
+
+# sendfile()
+
+CC_AUX_FLAGS="$CC_AUX_FLAGS"
+ngx_feature="sendfile()"
+ngx_feature_name="NGX_HAVE_SENDFILE"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/types.h>
+ #include <sys/socket.h>
+ #include <sys/uio.h>
+ #include <sys/errno.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int s = 0, fd = 1;
+ off_t n; off_t off = 0;
+ n = sendfile(s, fd, off, &n, NULL, 0);
+ if (n == -1 && errno == ENOSYS) return 1"
+. auto/feature
+
+if [ $ngx_found = yes ]; then
+ have=NGX_HAVE_SENDFILE . auto/have
+ CORE_SRCS="$CORE_SRCS $DARWIN_SENDFILE_SRCS"
+fi
+
+
+ngx_feature="atomic(3)"
+ngx_feature_name=NGX_DARWIN_ATOMIC
+ngx_feature_run=no
+ngx_feature_incs="#include <libkern/OSAtomic.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int32_t lock, n;
+ n = OSAtomicCompareAndSwap32Barrier(0, 1, &lock)"
+. auto/feature
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/freebsd b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/freebsd
new file mode 100644
index 00000000000..6aa823f9250
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/freebsd
@@ -0,0 +1,144 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+have=NGX_FREEBSD . auto/have_headers
+
+CORE_INCS="$UNIX_INCS"
+CORE_DEPS="$UNIX_DEPS $FREEBSD_DEPS"
+CORE_SRCS="$UNIX_SRCS $FREEBSD_SRCS"
+
+ngx_spacer='
+'
+
+
+# __FreeBSD_version and sysctl kern.osreldate are the best ways
+# to determine whether some capability exists and is safe to use.
+# __FreeBSD_version is used for the testing of the build environment.
+# sysctl kern.osreldate is used for the testing of the kernel capabilities.
+
+version=`grep "#define __FreeBSD_version" /usr/include/osreldate.h \
+ | sed -e 's/^.* \(.*\)$/\1/'`
+
+osreldate=`/sbin/sysctl -n kern.osreldate`
+
+
+# setproctitle() in libutil
+
+if [ \( $version -ge 500000 -a $version -lt 500012 \) \
+ -o $version -lt 410002 ]
+then
+ echo " + setproctitle() in libutil"
+
+ CORE_LIBS="$CORE_LIBS -lutil"
+ NGX_SETPROCTITLE_LIB="-lutil"
+fi
+
+# sendfile
+
+if [ $osreldate -gt 300007 ]; then
+ echo " + sendfile() found"
+
+ have=NGX_HAVE_SENDFILE . auto/have
+ CORE_SRCS="$CORE_SRCS $FREEBSD_SENDFILE_SRCS"
+fi
+
+if [ $osreldate -gt 502103 ]; then
+ echo " + sendfile()'s SF_NODISKIO found"
+
+ have=NGX_HAVE_AIO_SENDFILE . auto/have
+fi
+
+# POSIX semaphores
+# http://www.freebsd.org/cgi/query-pr.cgi?pr=kern/127545
+
+if [ $osreldate -ge 701106 ]; then
+ echo " + POSIX semaphores should work"
+else
+ have=NGX_HAVE_POSIX_SEM . auto/nohave
+fi
+
+
+# kqueue
+
+if [ \( $osreldate -lt 500000 -a $osreldate -ge 410000 \) \
+ -o $osreldate -ge 500011 ]
+then
+ echo " + kqueue found"
+
+ have=NGX_HAVE_KQUEUE . auto/have
+ have=NGX_HAVE_CLEAR_EVENT . auto/have
+ EVENT_MODULES="$EVENT_MODULES $KQUEUE_MODULE"
+ CORE_SRCS="$CORE_SRCS $KQUEUE_SRCS"
+ EVENT_FOUND=YES
+fi
+
+
+NGX_KQUEUE_CHECKED=YES
+
+
+# kqueue's NOTE_LAWAT
+
+if [ \( $version -lt 500000 -a $version -ge 430000 \) \
+ -o $version -ge 500018 ]
+then
+ echo " + kqueue's NOTE_LOWAT found"
+ have=NGX_HAVE_LOWAT_EVENT . auto/have
+fi
+
+# kqueue's EVFILT_TIMER
+
+if [ \( $version -lt 500000 -a $version -ge 440001 \) \
+ -o $version -ge 500023 ]
+then
+ echo " + kqueue's EVFILT_TIMER found"
+ have=NGX_HAVE_TIMER_EVENT . auto/have
+fi
+
+
+if [ $USE_THREADS = "rfork" ]; then
+
+ echo " + using rfork()"
+
+# # kqueue's EVFILT_SIGNAL is safe
+#
+# if [ $version -gt 460101 ]; then
+# echo " + kqueue's EVFILT_SIGNAL is safe"
+# have=NGX_HAVE_SAFE_EVFILT_SIGNAL . auto/have
+# else
+# echo "$0: error: the kqueue's EVFILT_SIGNAL is unsafe on this"
+# echo "FreeBSD version, so --with-threads=rfork could not be used"
+# echo
+#
+# exit 1
+# fi
+fi
+
+
+if [ $EVENT_AIO = YES ]; then
+ if [ \( $version -lt 500000 -a $version -ge 430000 \) \
+ -o $version -ge 500014 ]
+ then
+ have=NGX_HAVE_AIO . auto/have
+ EVENT_MODULES="$EVENT_MODULES $AIO_MODULE"
+ CORE_SRCS="$CORE_SRCS $AIO_SRCS"
+ else
+
+cat << END
+
+$0: error: the kqueue does not support AIO on this FreeBSD version
+
+END
+
+ exit 1
+ fi
+fi
+
+
+# cpuset_setaffinity()
+
+if [ $version -ge 701000 ]; then
+ echo " + cpuset_setaffinity() found"
+ have=NGX_HAVE_CPUSET_SETAFFINITY . auto/have
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/linux b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/linux
new file mode 100644
index 00000000000..19bf832ce3e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/linux
@@ -0,0 +1,185 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+have=NGX_LINUX . auto/have_headers
+
+CORE_INCS="$UNIX_INCS"
+CORE_DEPS="$UNIX_DEPS $LINUX_DEPS"
+CORE_SRCS="$UNIX_SRCS $LINUX_SRCS"
+
+ngx_spacer='
+'
+
+cc_aux_flags="$CC_AUX_FLAGS"
+CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
+
+
+# Linux kernel version
+
+version=$((`uname -r \
+ | sed -n -e 's/^\([0-9][0-9]*\)\.\([0-9][0-9]*\)\.\([0-9][0-9]*\).*/ \
+ \1*256*256+\2*256+\3/p' \
+ -e 's/^\([0-9][0-9]*\)\.\([0-9][0-9]*\).*/\1*256*256+\2*256/p'`))
+
+version=${version:-0}
+
+
+# enable the rt signals on Linux between 2.2.19 and 2.6.17
+
+if [ \( $version -ge 131603 -a $version -lt 132626 \) -o $EVENT_RTSIG = YES ]
+then
+ echo " + rt signals found"
+ have=NGX_HAVE_RTSIG . auto/have
+ EVENT_MODULES="$EVENT_MODULES $RTSIG_MODULE"
+ CORE_SRCS="$CORE_SRCS $RTSIG_SRCS"
+ EVENT_FOUND=YES
+fi
+
+
+# posix_fadvise64() had been implemented in 2.5.60
+
+if [ $version -lt 132412 ]; then
+ have=NGX_HAVE_POSIX_FADVISE . auto/nohave
+fi
+
+# epoll, EPOLLET version
+
+ngx_feature="epoll"
+ngx_feature_name="NGX_HAVE_EPOLL"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/epoll.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int efd = 0;
+ struct epoll_event ee;
+ ee.events = EPOLLIN|EPOLLOUT|EPOLLET;
+ ee.data.ptr = NULL;
+ efd = epoll_create(100);
+ if (efd == -1) return 1;"
+. auto/feature
+
+if [ $ngx_found = yes ]; then
+ have=NGX_HAVE_CLEAR_EVENT . auto/have
+ CORE_SRCS="$CORE_SRCS $EPOLL_SRCS"
+ EVENT_MODULES="$EVENT_MODULES $EPOLL_MODULE"
+ EVENT_FOUND=YES
+
+
+ # EPOLLRDHUP appeared in Linux 2.6.17, glibc 2.8
+
+ ngx_feature="EPOLLRDHUP"
+ ngx_feature_name="NGX_HAVE_EPOLLRDHUP"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <sys/epoll.h>"
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test="int efd = 0, fd = 0;
+ struct epoll_event ee;
+ ee.events = EPOLLIN|EPOLLRDHUP|EPOLLET;
+ ee.data.ptr = NULL;
+ epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ee)"
+ . auto/feature
+fi
+
+
+# O_PATH and AT_EMPTY_PATH were introduced in 2.6.39, glibc 2.14
+
+ngx_feature="O_PATH"
+ngx_feature_name="NGX_HAVE_O_PATH"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/types.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int fd; struct stat sb;
+ fd = openat(AT_FDCWD, \".\", O_PATH|O_DIRECTORY|O_NOFOLLOW);
+ if (fstatat(fd, \"\", &sb, AT_EMPTY_PATH) != 0) return 1"
+. auto/feature
+
+
+# sendfile()
+
+CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE"
+ngx_feature="sendfile()"
+ngx_feature_name="NGX_HAVE_SENDFILE"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/sendfile.h>
+ #include <errno.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int s = 0, fd = 1;
+ ssize_t n; off_t off = 0;
+ n = sendfile(s, fd, &off, 1);
+ if (n == -1 && errno == ENOSYS) return 1"
+. auto/feature
+
+if [ $ngx_found = yes ]; then
+ CORE_SRCS="$CORE_SRCS $LINUX_SENDFILE_SRCS"
+fi
+
+
+# sendfile64()
+
+CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
+ngx_feature="sendfile64()"
+ngx_feature_name="NGX_HAVE_SENDFILE64"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/sendfile.h>
+ #include <errno.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int s = 0, fd = 1;
+ ssize_t n; off_t off = 0;
+ n = sendfile(s, fd, &off, 1);
+ if (n == -1 && errno == ENOSYS) return 1"
+. auto/feature
+
+
+ngx_include="sys/prctl.h"; . auto/include
+
+# prctl(PR_SET_DUMPABLE)
+
+ngx_feature="prctl(PR_SET_DUMPABLE)"
+ngx_feature_name="NGX_HAVE_PR_SET_DUMPABLE"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/prctl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) return 1"
+. auto/feature
+
+
+# sched_setaffinity()
+
+ngx_feature="sched_setaffinity()"
+ngx_feature_name="NGX_HAVE_SCHED_SETAFFINITY"
+ngx_feature_run=no
+ngx_feature_incs="#include <sched.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="cpu_set_t mask;
+ CPU_ZERO(&mask);
+ sched_setaffinity(0, sizeof(cpu_set_t), &mask)"
+. auto/feature
+
+
+# crypt_r()
+
+ngx_feature="crypt_r()"
+ngx_feature_name="NGX_HAVE_GNU_CRYPT_R"
+ngx_feature_run=no
+ngx_feature_incs="#include <crypt.h>"
+ngx_feature_path=
+ngx_feature_libs=-lcrypt
+ngx_feature_test="struct crypt_data cd;
+ crypt_r(\"key\", \"salt\", &cd);"
+. auto/feature
+
+
+ngx_include="sys/vfs.h"; . auto/include
+
+
+CC_AUX_FLAGS="$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64"
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/solaris b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/solaris
new file mode 100644
index 00000000000..d39df0bf713
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/solaris
@@ -0,0 +1,61 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+have=NGX_SOLARIS . auto/have_headers
+
+CORE_INCS="$UNIX_INCS"
+CORE_DEPS="$UNIX_DEPS $SOLARIS_DEPS"
+CORE_SRCS="$UNIX_SRCS $SOLARIS_SRCS "
+CORE_LIBS="$CORE_LIBS -lsocket -lnsl"
+
+NGX_RPATH=YES
+
+# Solaris's make does not support a blank line between target and rules
+ngx_spacer=
+
+CC_AUX_FLAGS="$CC_AUX_FLAGS -D_FILE_OFFSET_BITS=64 -lsocket -lnsl"
+
+
+if [ $ZLIB_ASM != NO ]; then
+ echo "$0: error: the --with-zlib-asm=CPU option is not supported"
+ echo "on that platform"
+ echo
+
+ exit 1
+fi
+
+
+ngx_feature="sendfilev()"
+ngx_feature_name="NGX_HAVE_SENDFILE"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/sendfile.h>"
+ngx_feature_path=
+ngx_feature_libs="-lsendfile"
+ngx_feature_test="int fd = 1; sendfilevec_t vec[1];
+ size_t sent; ssize_t n;
+ n = sendfilev(fd, vec, 1, &sent);
+ if (n == -1) return 1"
+. auto/feature
+
+
+if [ $ngx_found = yes ]; then
+ CORE_SRCS="$CORE_SRCS $SOLARIS_SENDFILEV_SRCS"
+ CORE_LIBS="$CORE_LIBS -lsendfile"
+fi
+
+
+ngx_feature="event ports"
+ngx_feature_name="NGX_HAVE_EVENTPORT"
+ngx_feature_run=no
+ngx_feature_incs="#include <port.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int n = port_create()"
+. auto/feature
+
+if [ $ngx_found = yes ]; then
+ CORE_SRCS="$CORE_SRCS $EVENTPORT_SRCS"
+ EVENT_MODULES="$EVENT_MODULES $EVENTPORT_MODULE"
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/win32 b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/win32
new file mode 100644
index 00000000000..0b9b461875f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/os/win32
@@ -0,0 +1,40 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+have=NGX_WIN32 . auto/have_headers
+
+CORE_INCS="$WIN32_INCS"
+CORE_DEPS="$WIN32_DEPS"
+CORE_SRCS="$WIN32_SRCS $IOCP_SRCS"
+OS_CONFIG="$WIN32_CONFIG"
+NGX_ICONS="$NGX_WIN32_ICONS"
+SELECT_SRCS=$WIN32_SELECT_SRCS
+
+case "$NGX_CC_NAME" in
+
+ gcc)
+ CORE_LIBS="$CORE_LIBS -ladvapi32 -lws2_32"
+ ;;
+
+ *)
+ CORE_LIBS="$CORE_LIBS advapi32.lib ws2_32.lib"
+ ;;
+
+esac
+
+EVENT_MODULES="$EVENT_MODULES $IOCP_MODULE"
+EVENT_FOUND=YES
+
+if [ $EVENT_SELECT = NO ]; then
+ CORE_SRCS="$CORE_SRCS $SELECT_SRCS"
+ EVENT_MODULES="$EVENT_MODULES $SELECT_MODULE"
+fi
+
+if [ $NGX_IPV6 = YES ]; then
+ have=NGX_HAVE_INET6 . auto/have
+fi
+
+have=NGX_HAVE_AIO . auto/have
+have=NGX_HAVE_IOCP . auto/have
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/sources b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/sources
new file mode 100644
index 00000000000..1287782ead8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/sources
@@ -0,0 +1,557 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+CORE_MODULES="ngx_core_module ngx_errlog_module ngx_conf_module"
+
+CORE_INCS="src/core"
+
+CORE_DEPS="src/core/nginx.h \
+ src/core/ngx_config.h \
+ src/core/ngx_core.h \
+ src/core/ngx_log.h \
+ src/core/ngx_palloc.h \
+ src/core/ngx_array.h \
+ src/core/ngx_list.h \
+ src/core/ngx_hash.h \
+ src/core/ngx_buf.h \
+ src/core/ngx_queue.h \
+ src/core/ngx_string.h \
+ src/core/ngx_parse.h \
+ src/core/ngx_inet.h \
+ src/core/ngx_file.h \
+ src/core/ngx_crc.h \
+ src/core/ngx_crc32.h \
+ src/core/ngx_murmurhash.h \
+ src/core/ngx_md5.h \
+ src/core/ngx_sha1.h \
+ src/core/ngx_rbtree.h \
+ src/core/ngx_radix_tree.h \
+ src/core/ngx_slab.h \
+ src/core/ngx_times.h \
+ src/core/ngx_shmtx.h \
+ src/core/ngx_connection.h \
+ src/core/ngx_cycle.h \
+ src/core/ngx_conf_file.h \
+ src/core/ngx_resolver.h \
+ src/core/ngx_open_file_cache.h \
+ src/core/ngx_crypt.h \
+ src/core/ngx_proxy_protocol.h \
+ src/core/ngx_syslog.h"
+
+
+CORE_SRCS="src/core/nginx.c \
+ src/core/ngx_log.c \
+ src/core/ngx_palloc.c \
+ src/core/ngx_array.c \
+ src/core/ngx_list.c \
+ src/core/ngx_hash.c \
+ src/core/ngx_buf.c \
+ src/core/ngx_queue.c \
+ src/core/ngx_output_chain.c \
+ src/core/ngx_string.c \
+ src/core/ngx_parse.c \
+ src/core/ngx_inet.c \
+ src/core/ngx_file.c \
+ src/core/ngx_crc32.c \
+ src/core/ngx_murmurhash.c \
+ src/core/ngx_md5.c \
+ src/core/ngx_rbtree.c \
+ src/core/ngx_radix_tree.c \
+ src/core/ngx_slab.c \
+ src/core/ngx_times.c \
+ src/core/ngx_shmtx.c \
+ src/core/ngx_connection.c \
+ src/core/ngx_cycle.c \
+ src/core/ngx_spinlock.c \
+ src/core/ngx_cpuinfo.c \
+ src/core/ngx_conf_file.c \
+ src/core/ngx_resolver.c \
+ src/core/ngx_open_file_cache.c \
+ src/core/ngx_crypt.c \
+ src/core/ngx_proxy_protocol.c \
+ src/core/ngx_syslog.c"
+
+
+REGEX_MODULE=ngx_regex_module
+REGEX_DEPS=src/core/ngx_regex.h
+REGEX_SRCS=src/core/ngx_regex.c
+
+
+OPENSSL_MODULE=ngx_openssl_module
+OPENSSL_DEPS=src/event/ngx_event_openssl.h
+OPENSSL_SRCS="src/event/ngx_event_openssl.c \
+ src/event/ngx_event_openssl_stapling.c"
+
+
+EVENT_MODULES="ngx_events_module ngx_event_core_module"
+
+EVENT_INCS="src/event src/event/modules"
+
+EVENT_DEPS="src/event/ngx_event.h \
+ src/event/ngx_event_timer.h \
+ src/event/ngx_event_posted.h \
+ src/event/ngx_event_busy_lock.h \
+ src/event/ngx_event_connect.h \
+ src/event/ngx_event_pipe.h"
+
+EVENT_SRCS="src/event/ngx_event.c \
+ src/event/ngx_event_timer.c \
+ src/event/ngx_event_posted.c \
+ src/event/ngx_event_busy_lock.c \
+ src/event/ngx_event_accept.c \
+ src/event/ngx_event_connect.c \
+ src/event/ngx_event_pipe.c"
+
+
+SELECT_MODULE=ngx_select_module
+SELECT_SRCS=src/event/modules/ngx_select_module.c
+WIN32_SELECT_SRCS=src/event/modules/ngx_win32_select_module.c
+
+POLL_MODULE=ngx_poll_module
+POLL_SRCS=src/event/modules/ngx_poll_module.c
+
+KQUEUE_MODULE=ngx_kqueue_module
+KQUEUE_SRCS=src/event/modules/ngx_kqueue_module.c
+
+DEVPOLL_MODULE=ngx_devpoll_module
+DEVPOLL_SRCS=src/event/modules/ngx_devpoll_module.c
+
+EVENTPORT_MODULE=ngx_eventport_module
+EVENTPORT_SRCS=src/event/modules/ngx_eventport_module.c
+
+EPOLL_MODULE=ngx_epoll_module
+EPOLL_SRCS=src/event/modules/ngx_epoll_module.c
+
+RTSIG_MODULE=ngx_rtsig_module
+RTSIG_SRCS=src/event/modules/ngx_rtsig_module.c
+
+IOCP_MODULE=ngx_iocp_module
+IOCP_SRCS=src/event/modules/ngx_iocp_module.c
+
+AIO_MODULE=ngx_aio_module
+AIO_SRCS="src/event/modules/ngx_aio_module.c \
+ src/os/unix/ngx_aio_read.c \
+ src/os/unix/ngx_aio_write.c \
+ src/os/unix/ngx_aio_read_chain.c \
+ src/os/unix/ngx_aio_write_chain.c"
+
+FILE_AIO_SRCS="src/os/unix/ngx_file_aio_read.c"
+LINUX_AIO_SRCS="src/os/unix/ngx_linux_aio_read.c"
+
+UNIX_INCS="$CORE_INCS $EVENT_INCS src/os/unix"
+
+UNIX_DEPS="$CORE_DEPS $EVENT_DEPS \
+ src/os/unix/ngx_time.h \
+ src/os/unix/ngx_errno.h \
+ src/os/unix/ngx_alloc.h \
+ src/os/unix/ngx_files.h \
+ src/os/unix/ngx_channel.h \
+ src/os/unix/ngx_shmem.h \
+ src/os/unix/ngx_process.h \
+ src/os/unix/ngx_setaffinity.h \
+ src/os/unix/ngx_setproctitle.h \
+ src/os/unix/ngx_atomic.h \
+ src/os/unix/ngx_gcc_atomic_x86.h \
+ src/os/unix/ngx_thread.h \
+ src/os/unix/ngx_socket.h \
+ src/os/unix/ngx_os.h \
+ src/os/unix/ngx_user.h \
+ src/os/unix/ngx_process_cycle.h"
+
+# add to UNIX_DEPS
+# src/os/unix/ngx_gcc_atomic_amd64.h \
+# src/os/unix/ngx_gcc_atomic_sparc64.h \
+# src/os/unix/ngx_gcc_atomic_ppc.h \
+# src/os/unix/ngx_sunpro_atomic_sparc64.h \
+# src/os/unix/ngx_sunpro_x86.il \
+# src/os/unix/ngx_sunpro_amd64.il \
+# src/os/unix/ngx_sunpro_sparc64.il \
+
+
+UNIX_SRCS="$CORE_SRCS $EVENT_SRCS \
+ src/os/unix/ngx_time.c \
+ src/os/unix/ngx_errno.c \
+ src/os/unix/ngx_alloc.c \
+ src/os/unix/ngx_files.c \
+ src/os/unix/ngx_socket.c \
+ src/os/unix/ngx_recv.c \
+ src/os/unix/ngx_readv_chain.c \
+ src/os/unix/ngx_udp_recv.c \
+ src/os/unix/ngx_send.c \
+ src/os/unix/ngx_writev_chain.c \
+ src/os/unix/ngx_channel.c \
+ src/os/unix/ngx_shmem.c \
+ src/os/unix/ngx_process.c \
+ src/os/unix/ngx_daemon.c \
+ src/os/unix/ngx_setaffinity.c \
+ src/os/unix/ngx_setproctitle.c \
+ src/os/unix/ngx_posix_init.c \
+ src/os/unix/ngx_user.c \
+ src/os/unix/ngx_process_cycle.c"
+
+POSIX_DEPS=src/os/unix/ngx_posix_config.h
+
+FREEBSD_DEPS="src/os/unix/ngx_freebsd_config.h src/os/unix/ngx_freebsd.h"
+FREEBSD_SRCS=src/os/unix/ngx_freebsd_init.c
+FREEBSD_SENDFILE_SRCS=src/os/unix/ngx_freebsd_sendfile_chain.c
+FREEBSD_RFORK_DEPS="src/os/unix/ngx_freebsd_rfork_thread.h"
+FREEBSD_RFORK_SRCS="src/os/unix/ngx_freebsd_rfork_thread.c"
+FREEBSD_RFORK_THREAD_SRCS="src/os/unix/rfork_thread.S"
+
+PTHREAD_SRCS="src/os/unix/ngx_pthread_thread.c"
+
+LINUX_DEPS="src/os/unix/ngx_linux_config.h src/os/unix/ngx_linux.h"
+LINUX_SRCS=src/os/unix/ngx_linux_init.c
+LINUX_SENDFILE_SRCS=src/os/unix/ngx_linux_sendfile_chain.c
+
+
+SOLARIS_DEPS="src/os/unix/ngx_solaris_config.h src/os/unix/ngx_solaris.h"
+SOLARIS_SRCS=src/os/unix/ngx_solaris_init.c
+SOLARIS_SENDFILEV_SRCS=src/os/unix/ngx_solaris_sendfilev_chain.c
+
+
+DARWIN_DEPS="src/os/unix/ngx_darwin_config.h src/os/unix/ngx_darwin.h"
+DARWIN_SRCS=src/os/unix/ngx_darwin_init.c
+DARWIN_SENDFILE_SRCS=src/os/unix/ngx_darwin_sendfile_chain.c
+
+
+WIN32_INCS="$CORE_INCS $EVENT_INCS src/os/win32"
+
+WIN32_DEPS="$CORE_DEPS $EVENT_DEPS \
+ src/os/win32/ngx_win32_config.h \
+ src/os/win32/ngx_time.h \
+ src/os/win32/ngx_errno.h \
+ src/os/win32/ngx_alloc.h \
+ src/os/win32/ngx_files.h \
+ src/os/win32/ngx_shmem.h \
+ src/os/win32/ngx_process.h \
+ src/os/win32/ngx_atomic.h \
+ src/os/win32/ngx_thread.h \
+ src/os/win32/ngx_socket.h \
+ src/os/win32/ngx_os.h \
+ src/os/win32/ngx_user.h \
+ src/os/win32/ngx_process_cycle.h"
+
+WIN32_CONFIG=src/os/win32/ngx_win32_config.h
+
+WIN32_SRCS="$CORE_SRCS $EVENT_SRCS \
+ src/os/win32/ngx_errno.c \
+ src/os/win32/ngx_alloc.c \
+ src/os/win32/ngx_files.c \
+ src/os/win32/ngx_shmem.c \
+ src/os/win32/ngx_time.c \
+ src/os/win32/ngx_process.c \
+ src/os/win32/ngx_thread.c \
+ src/os/win32/ngx_socket.c \
+ src/os/win32/ngx_wsarecv.c \
+ src/os/win32/ngx_wsarecv_chain.c \
+ src/os/win32/ngx_udp_wsarecv.c \
+ src/os/win32/ngx_wsasend.c \
+ src/os/win32/ngx_wsasend_chain.c \
+ src/os/win32/ngx_win32_init.c \
+ src/os/win32/ngx_user.c \
+ src/os/win32/ngx_event_log.c \
+ src/os/win32/ngx_process_cycle.c \
+ src/event/ngx_event_acceptex.c"
+
+NGX_WIN32_ICONS="src/os/win32/nginx.ico"
+NGX_WIN32_RC="src/os/win32/nginx.rc"
+
+
+# the http modules that have their logging formats
+# must be after ngx_http_log_module
+
+HTTP_MODULES="ngx_http_module \
+ ngx_http_core_module \
+ ngx_http_log_module \
+ ngx_http_upstream_module"
+
+HTTP_WRITE_FILTER_MODULE="ngx_http_write_filter_module"
+HTTP_HEADER_FILTER_MODULE="ngx_http_header_filter_module"
+
+HTTP_POSTPONE_FILTER_MODULE=ngx_http_postpone_filter_module
+HTTP_COPY_FILTER_MODULE=ngx_http_copy_filter_module
+
+HTTP_CHUNKED_FILTER_MODULE=ngx_http_chunked_filter_module
+HTTP_HEADERS_FILTER_MODULE=ngx_http_headers_filter_module
+
+HTTP_RANGE_HEADER_FILTER_MODULE=ngx_http_range_header_filter_module
+HTTP_RANGE_BODY_FILTER_MODULE=ngx_http_range_body_filter_module
+
+HTTP_NOT_MODIFIED_FILTER_MODULE=ngx_http_not_modified_filter_module
+
+HTTP_STATIC_MODULE=ngx_http_static_module
+HTTP_INDEX_MODULE=ngx_http_index_module
+
+HTTP_INCS="src/http src/http/modules"
+
+HTTP_DEPS="src/http/ngx_http.h \
+ src/http/ngx_http_request.h \
+ src/http/ngx_http_config.h \
+ src/http/ngx_http_core_module.h \
+ src/http/ngx_http_cache.h \
+ src/http/ngx_http_variables.h \
+ src/http/ngx_http_script.h \
+ src/http/ngx_http_upstream.h \
+ src/http/ngx_http_upstream_round_robin.h \
+ src/http/ngx_http_busy_lock.h"
+
+HTTP_SRCS="src/http/ngx_http.c \
+ src/http/ngx_http_core_module.c \
+ src/http/ngx_http_special_response.c \
+ src/http/ngx_http_request.c \
+ src/http/ngx_http_parse.c \
+ src/http/ngx_http_header_filter_module.c \
+ src/http/ngx_http_write_filter_module.c \
+ src/http/ngx_http_copy_filter_module.c \
+ src/http/modules/ngx_http_log_module.c \
+ src/http/ngx_http_request_body.c \
+ src/http/ngx_http_variables.c \
+ src/http/ngx_http_script.c \
+ src/http/ngx_http_upstream.c \
+ src/http/ngx_http_upstream_round_robin.c \
+ src/http/ngx_http_parse_time.c \
+ src/http/modules/ngx_http_static_module.c \
+ src/http/modules/ngx_http_index_module.c \
+ src/http/modules/ngx_http_chunked_filter_module.c \
+ src/http/modules/ngx_http_range_filter_module.c \
+ src/http/modules/ngx_http_headers_filter_module.c \
+ src/http/modules/ngx_http_not_modified_filter_module.c"
+
+# STUB
+HTTP_SRCS="$HTTP_SRCS src/http/ngx_http_busy_lock.c"
+
+HTTP_POSTPONE_FILTER_SRCS=src/http/ngx_http_postpone_filter_module.c
+
+HTTP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c
+
+
+HTTP_SPDY_MODULE=ngx_http_spdy_module
+HTTP_SPDY_FILTER_MODULE=ngx_http_spdy_filter_module
+HTTP_SPDY_DEPS="src/http/ngx_http_spdy.h \
+ src/http/ngx_http_spdy_module.h"
+HTTP_SPDY_SRCS="src/http/ngx_http_spdy.c \
+ src/http/ngx_http_spdy_module.c \
+ src/http/ngx_http_spdy_filter_module.c"
+
+
+HTTP_CHARSET_FILTER_MODULE=ngx_http_charset_filter_module
+HTTP_CHARSET_SRCS=src/http/modules/ngx_http_charset_filter_module.c
+
+
+HTTP_GZIP_FILTER_MODULE=ngx_http_gzip_filter_module
+HTTP_GZIP_SRCS=src/http/modules/ngx_http_gzip_filter_module.c
+
+
+HTTP_GUNZIP_FILTER_MODULE=ngx_http_gunzip_filter_module
+HTTP_GUNZIP_SRCS=src/http/modules/ngx_http_gunzip_filter_module.c
+
+
+HTTP_SSI_FILTER_MODULE=ngx_http_ssi_filter_module
+HTTP_SSI_DEPS=src/http/modules/ngx_http_ssi_filter_module.h
+HTTP_SSI_SRCS=src/http/modules/ngx_http_ssi_filter_module.c
+
+
+HTTP_XSLT_FILTER_MODULE=ngx_http_xslt_filter_module
+HTTP_XSLT_SRCS=src/http/modules/ngx_http_xslt_filter_module.c
+
+
+HTTP_IMAGE_FILTER_MODULE=ngx_http_image_filter_module
+HTTP_IMAGE_SRCS=src/http/modules/ngx_http_image_filter_module.c
+
+
+HTTP_SUB_FILTER_MODULE=ngx_http_sub_filter_module
+HTTP_SUB_SRCS=src/http/modules/ngx_http_sub_filter_module.c
+
+
+HTTP_USERID_FILTER_MODULE=ngx_http_userid_filter_module
+HTTP_USERID_SRCS=src/http/modules/ngx_http_userid_filter_module.c
+
+
+HTTP_REALIP_MODULE=ngx_http_realip_module
+HTTP_REALIP_SRCS=src/http/modules/ngx_http_realip_module.c
+
+
+HTTP_ADDITION_FILTER_MODULE=ngx_http_addition_filter_module
+HTTP_ADDITION_SRCS=src/http/modules/ngx_http_addition_filter_module.c
+
+
+HTTP_DAV_MODULE=ngx_http_dav_module
+HTTP_DAV_SRCS=src/http/modules/ngx_http_dav_module.c
+
+
+HTTP_ACCESS_MODULE=ngx_http_access_module
+HTTP_ACCESS_SRCS=src/http/modules/ngx_http_access_module.c
+
+
+HTTP_AUTH_BASIC_MODULE=ngx_http_auth_basic_module
+HTTP_AUTH_BASIC_SRCS=src/http/modules/ngx_http_auth_basic_module.c
+
+
+HTTP_AUTH_REQUEST_MODULE=ngx_http_auth_request_module
+HTTP_AUTH_REQUEST_SRCS=src/http/modules/ngx_http_auth_request_module.c
+
+
+HTTP_AUTOINDEX_MODULE=ngx_http_autoindex_module
+HTTP_AUTOINDEX_SRCS=src/http/modules/ngx_http_autoindex_module.c
+
+
+HTTP_RANDOM_INDEX_MODULE=ngx_http_random_index_module
+HTTP_RANDOM_INDEX_SRCS=src/http/modules/ngx_http_random_index_module.c
+
+
+HTTP_STATUS_MODULE=ngx_http_status_module
+HTTP_STATUS_SRCS=src/http/modules/ngx_http_status_module.c
+
+
+HTTP_GEO_MODULE=ngx_http_geo_module
+HTTP_GEO_SRCS=src/http/modules/ngx_http_geo_module.c
+
+
+HTTP_GEOIP_MODULE=ngx_http_geoip_module
+HTTP_GEOIP_SRCS=src/http/modules/ngx_http_geoip_module.c
+
+
+HTTP_MAP_MODULE=ngx_http_map_module
+HTTP_MAP_SRCS=src/http/modules/ngx_http_map_module.c
+
+
+HTTP_SPLIT_CLIENTS_MODULE=ngx_http_split_clients_module
+HTTP_SPLIT_CLIENTS_SRCS=src/http/modules/ngx_http_split_clients_module.c
+
+
+HTTP_REFERER_MODULE=ngx_http_referer_module
+HTTP_REFERER_SRCS=src/http/modules/ngx_http_referer_module.c
+
+
+HTTP_REWRITE_MODULE=ngx_http_rewrite_module
+HTTP_REWRITE_SRCS=src/http/modules/ngx_http_rewrite_module.c
+
+
+HTTP_SSL_MODULE=ngx_http_ssl_module
+HTTP_SSL_DEPS=src/http/modules/ngx_http_ssl_module.h
+HTTP_SSL_SRCS=src/http/modules/ngx_http_ssl_module.c
+
+
+HTTP_PROXY_MODULE=ngx_http_proxy_module
+HTTP_PROXY_SRCS=src/http/modules/ngx_http_proxy_module.c
+
+
+HTTP_FASTCGI_MODULE=ngx_http_fastcgi_module
+HTTP_FASTCGI_SRCS=src/http/modules/ngx_http_fastcgi_module.c
+
+
+HTTP_UWSGI_MODULE=ngx_http_uwsgi_module
+HTTP_UWSGI_SRCS=src/http/modules/ngx_http_uwsgi_module.c
+
+
+HTTP_SCGI_MODULE=ngx_http_scgi_module
+HTTP_SCGI_SRCS=src/http/modules/ngx_http_scgi_module.c
+
+
+HTTP_PERL_MODULE=ngx_http_perl_module
+HTTP_PERL_INCS=src/http/modules/perl
+HTTP_PERL_DEPS=src/http/modules/perl/ngx_http_perl_module.h
+HTTP_PERL_SRCS=src/http/modules/perl/ngx_http_perl_module.c
+
+
+HTTP_MEMCACHED_MODULE=ngx_http_memcached_module
+HTTP_MEMCACHED_SRCS=src/http/modules/ngx_http_memcached_module.c
+
+
+HTTP_LIMIT_CONN_MODULE=ngx_http_limit_conn_module
+HTTP_LIMIT_CONN_SRCS=src/http/modules/ngx_http_limit_conn_module.c
+
+
+HTTP_LIMIT_REQ_MODULE=ngx_http_limit_req_module
+HTTP_LIMIT_REQ_SRCS=src/http/modules/ngx_http_limit_req_module.c
+
+
+HTTP_EMPTY_GIF_MODULE=ngx_http_empty_gif_module
+HTTP_EMPTY_GIF_SRCS=src/http/modules/ngx_http_empty_gif_module.c
+
+
+HTTP_BROWSER_MODULE=ngx_http_browser_module
+HTTP_BROWSER_SRCS=src/http/modules/ngx_http_browser_module.c
+
+
+HTTP_SECURE_LINK_MODULE=ngx_http_secure_link_module
+HTTP_SECURE_LINK_SRCS=src/http/modules/ngx_http_secure_link_module.c
+
+
+HTTP_DEGRADATION_MODULE=ngx_http_degradation_module
+HTTP_DEGRADATION_SRCS=src/http/modules/ngx_http_degradation_module.c
+
+
+HTTP_FLV_MODULE=ngx_http_flv_module
+HTTP_FLV_SRCS=src/http/modules/ngx_http_flv_module.c
+
+
+HTTP_MP4_MODULE=ngx_http_mp4_module
+HTTP_MP4_SRCS=src/http/modules/ngx_http_mp4_module.c
+
+
+HTTP_GZIP_STATIC_MODULE=ngx_http_gzip_static_module
+HTTP_GZIP_STATIC_SRCS=src/http/modules/ngx_http_gzip_static_module.c
+
+
+HTTP_UPSTREAM_HASH_MODULE=ngx_http_upstream_hash_module
+HTTP_UPSTREAM_HASH_SRCS=src/http/modules/ngx_http_upstream_hash_module.c
+
+
+HTTP_UPSTREAM_IP_HASH_MODULE=ngx_http_upstream_ip_hash_module
+HTTP_UPSTREAM_IP_HASH_SRCS=src/http/modules/ngx_http_upstream_ip_hash_module.c
+
+
+HTTP_UPSTREAM_LEAST_CONN_MODULE=ngx_http_upstream_least_conn_module
+HTTP_UPSTREAM_LEAST_CONN_SRCS=" \
+ src/http/modules/ngx_http_upstream_least_conn_module.c"
+
+
+HTTP_UPSTREAM_KEEPALIVE_MODULE=ngx_http_upstream_keepalive_module
+HTTP_UPSTREAM_KEEPALIVE_SRCS=" \
+ src/http/modules/ngx_http_upstream_keepalive_module.c"
+
+
+MAIL_INCS="src/mail"
+
+MAIL_DEPS="src/mail/ngx_mail.h"
+
+MAIL_MODULES="ngx_mail_module ngx_mail_core_module"
+
+MAIL_SRCS="src/mail/ngx_mail.c \
+ src/mail/ngx_mail_core_module.c \
+ src/mail/ngx_mail_handler.c \
+ src/mail/ngx_mail_parse.c"
+
+MAIL_POP3_MODULE="ngx_mail_pop3_module"
+MAIL_POP3_DEPS="src/mail/ngx_mail_pop3_module.h"
+MAIL_POP3_SRCS="src/mail/ngx_mail_pop3_module.c \
+ src/mail/ngx_mail_pop3_handler.c"
+
+MAIL_IMAP_MODULE="ngx_mail_imap_module"
+MAIL_IMAP_DEPS="src/mail/ngx_mail_imap_module.h"
+MAIL_IMAP_SRCS="src/mail/ngx_mail_imap_module.c \
+ src/mail/ngx_mail_imap_handler.c"
+
+MAIL_SMTP_MODULE="ngx_mail_smtp_module"
+MAIL_SMTP_DEPS="src/mail/ngx_mail_smtp_module.h"
+MAIL_SMTP_SRCS="src/mail/ngx_mail_smtp_module.c \
+ src/mail/ngx_mail_smtp_handler.c"
+
+MAIL_SSL_MODULE="ngx_mail_ssl_module"
+MAIL_SSL_DEPS="src/mail/ngx_mail_ssl_module.h"
+MAIL_SSL_SRCS="src/mail/ngx_mail_ssl_module.c"
+
+MAIL_AUTH_HTTP_MODULE="ngx_mail_auth_http_module"
+MAIL_AUTH_HTTP_SRCS="src/mail/ngx_mail_auth_http_module.c"
+
+MAIL_PROXY_MODULE="ngx_mail_proxy_module"
+MAIL_PROXY_SRCS="src/mail/ngx_mail_proxy_module.c"
+
+NGX_GOOGLE_PERFTOOLS_MODULE=ngx_google_perftools_module
+NGX_GOOGLE_PERFTOOLS_SRCS=src/misc/ngx_google_perftools_module.c
+
+NGX_CPP_TEST_SRCS=src/misc/ngx_cpp_test_module.cpp
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/stubs b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/stubs
new file mode 100644
index 00000000000..d8bc1f0e02a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/stubs
@@ -0,0 +1,8 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+have=NGX_SUPPRESS_WARN . auto/have
+
+have=NGX_SMP . auto/have
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/summary b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/summary
new file mode 100644
index 00000000000..dcebec9f0fe
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/summary
@@ -0,0 +1,116 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+### STUB
+
+if [ $USE_THREADS != NO ]; then
+
+cat << END
+
+$0: error: the threads support is broken now.
+
+END
+ exit 1
+ fi
+
+###
+
+
+echo
+echo "Configuration summary"
+
+
+#case $USE_THREADS in
+# rfork) echo " + using rfork()ed threads" ;;
+# pthreads) echo " + using libpthread threads library" ;;
+# libthr) echo " + using FreeBSD libthr threads library" ;;
+# libc_r) echo " + using FreeBSD libc_r threads library" ;;
+# linuxthreads) echo " + using FreeBSD LinuxThreads port library" ;;
+# NO) echo " + threads are not used" ;;
+# *) echo " + using lib$USE_THREADS threads library" ;;
+#esac
+
+if [ $USE_PCRE = DISABLED ]; then
+ echo " + PCRE library is disabled"
+
+else
+ case $PCRE in
+ YES) echo " + using system PCRE library" ;;
+ NONE) echo " + PCRE library is not used" ;;
+ *) echo " + using PCRE library: $PCRE" ;;
+ esac
+fi
+
+case $OPENSSL in
+ YES) echo " + using system OpenSSL library" ;;
+ NONE) echo " + OpenSSL library is not used" ;;
+ *) echo " + using OpenSSL library: $OPENSSL" ;;
+esac
+
+case $MD5 in
+ YES) echo " + md5: using $MD5_LIB library" ;;
+ NONE) echo " + md5 library is not used" ;;
+ NO) echo " + using builtin md5 code" ;;
+ *) echo " + using md5 library: $MD5" ;;
+esac
+
+case $SHA1 in
+ YES) echo " + sha1: using $SHA1_LIB library" ;;
+ NONE) echo " + sha1 library is not used" ;;
+ NO) echo " + sha1 library is not found" ;;
+ *) echo " + using sha1 library: $SHA1" ;;
+esac
+
+case $ZLIB in
+ YES) echo " + using system zlib library" ;;
+ NONE) echo " + zlib library is not used" ;;
+ *) echo " + using zlib library: $ZLIB" ;;
+esac
+
+case $NGX_LIBATOMIC in
+ YES) echo " + using system libatomic_ops library" ;;
+ NO) ;; # not used
+ *) echo " + using libatomic_ops library: $NGX_LIBATOMIC" ;;
+esac
+
+echo
+
+
+cat << END
+ nginx path prefix: "$NGX_PREFIX"
+ nginx binary file: "$NGX_SBIN_PATH"
+ nginx configuration prefix: "$NGX_CONF_PREFIX"
+ nginx configuration file: "$NGX_CONF_PATH"
+ nginx pid file: "$NGX_PID_PATH"
+END
+
+if test -n "$NGX_ERROR_LOG_PATH"; then
+ echo " nginx error log file: \"$NGX_ERROR_LOG_PATH\""
+else
+ echo " nginx logs errors to stderr"
+fi
+
+cat << END
+ nginx http access log file: "$NGX_HTTP_LOG_PATH"
+ nginx http client request body temporary files: "$NGX_HTTP_CLIENT_TEMP_PATH"
+END
+
+if [ $HTTP_PROXY = YES ]; then
+ echo " nginx http proxy temporary files: \"$NGX_HTTP_PROXY_TEMP_PATH\""
+fi
+
+if [ $HTTP_FASTCGI = YES ]; then
+ echo " nginx http fastcgi temporary files: \"$NGX_HTTP_FASTCGI_TEMP_PATH\""
+fi
+
+if [ $HTTP_UWSGI = YES ]; then
+ echo " nginx http uwsgi temporary files: \"$NGX_HTTP_UWSGI_TEMP_PATH\""
+fi
+
+if [ $HTTP_SCGI = YES ]; then
+ echo " nginx http scgi temporary files: \"$NGX_HTTP_SCGI_TEMP_PATH\""
+fi
+
+echo "$NGX_POST_CONF_MSG"
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/sizeof b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/sizeof
new file mode 100644
index 00000000000..9215a545fc4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/sizeof
@@ -0,0 +1,84 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+echo $ngx_n "checking for $ngx_type size ...$ngx_c"
+
+cat << END >> $NGX_AUTOCONF_ERR
+
+----------------------------------------
+checking for $ngx_type size
+
+END
+
+ngx_size=
+
+cat << END > $NGX_AUTOTEST.c
+
+#include <sys/types.h>
+#include <sys/time.h>
+$NGX_INCLUDE_UNISTD_H
+#include <signal.h>
+#include <stdio.h>
+#include <sys/resource.h>
+$NGX_INCLUDE_INTTYPES_H
+$NGX_INCLUDE_AUTO_CONFIG_H
+
+int main() {
+ printf("%d", (int) sizeof($ngx_type));
+ return 0;
+}
+
+END
+
+
+ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \
+ -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT $ngx_feature_libs"
+
+eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"
+
+
+if [ -x $NGX_AUTOTEST ]; then
+ ngx_size=`$NGX_AUTOTEST`
+ echo " $ngx_size bytes"
+fi
+
+
+rm -rf $NGX_AUTOTEST*
+
+
+case $ngx_size in
+ 4)
+ if [ "$ngx_type"="long" ]; then
+ ngx_max_value=2147483647L
+ else
+ ngx_max_value=2147483647
+ fi
+
+ ngx_max_len='(sizeof("-2147483648") - 1)'
+ ;;
+
+ 8)
+ if [ "$ngx_type"="long long" ]; then
+ ngx_max_value=9223372036854775807LL
+ else
+ ngx_max_value=9223372036854775807L
+ fi
+
+ ngx_max_len='(sizeof("-9223372036854775808") - 1)'
+ ;;
+
+ *)
+ echo
+ echo "$0: error: can not detect $ngx_type size"
+
+ echo "----------" >> $NGX_AUTOCONF_ERR
+ cat $NGX_AUTOTEST.c >> $NGX_AUTOCONF_ERR
+ echo "----------" >> $NGX_AUTOCONF_ERR
+ echo $ngx_test >> $NGX_AUTOCONF_ERR
+ echo "----------" >> $NGX_AUTOCONF_ERR
+
+ exit 1
+esac
+
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/typedef b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/typedef
new file mode 100644
index 00000000000..8b5c3689ced
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/typedef
@@ -0,0 +1,77 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+echo $ngx_n "checking for $ngx_type ...$ngx_c"
+
+cat << END >> $NGX_AUTOCONF_ERR
+
+----------------------------------------
+checking for $ngx_type
+
+END
+
+ngx_found=no
+
+for ngx_try in $ngx_type $ngx_types
+do
+
+ cat << END > $NGX_AUTOTEST.c
+
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <netinet/in.h>
+$NGX_INCLUDE_INTTYPES_H
+
+int main() {
+ $ngx_try i = 0;
+ return (int) i;
+}
+
+END
+
+ ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \
+ -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT $ngx_feature_libs"
+
+ eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"
+
+ if [ -x $NGX_AUTOTEST ]; then
+ if [ $ngx_try = $ngx_type ]; then
+ echo " found"
+ ngx_found=yes
+ else
+ echo ", $ngx_try used"
+ ngx_found=$ngx_try
+ fi
+ fi
+
+ rm -rf $NGX_AUTOTEST*
+
+ if [ $ngx_found = no ]; then
+ echo $ngx_n " $ngx_try not found$ngx_c"
+
+ echo "----------" >> $NGX_AUTOCONF_ERR
+ cat $NGX_AUTOTEST.c >> $NGX_AUTOCONF_ERR
+ echo "----------" >> $NGX_AUTOCONF_ERR
+ echo $ngx_test >> $NGX_AUTOCONF_ERR
+ echo "----------" >> $NGX_AUTOCONF_ERR
+
+ else
+ break
+ fi
+done
+
+if [ $ngx_found = no ]; then
+ echo
+ echo "$0: error: can not define $ngx_type"
+
+ exit 1
+fi
+
+if [ $ngx_found != yes ]; then
+ echo "typedef $ngx_found $ngx_type;" >> $NGX_AUTO_CONFIG_H
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/uintptr_t b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/uintptr_t
new file mode 100644
index 00000000000..f3cdccb2274
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/uintptr_t
@@ -0,0 +1,45 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+echo $ngx_n "checking for uintptr_t ...$ngx_c"
+echo >> $NGX_AUTOCONF_ERR
+echo "checking for uintptr_t" >> $NGX_AUTOCONF_ERR
+
+found=no
+
+cat << END > $NGX_AUTOTEST.c
+
+#include <sys/types.h>
+$NGX_INTTYPES_H
+
+int main() {
+ uintptr_t i = 0;
+ return (int) i;
+}
+
+END
+
+ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \
+ -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT"
+
+eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"
+
+if [ -x $NGX_AUTOTEST ]; then
+ echo " uintptr_t found"
+ found=yes
+else
+ echo $ngx_n " uintptr_t not found" $ngx_c
+fi
+
+rm -rf $NGX_AUTOTEST*
+
+
+if [ $found = no ]; then
+ found="uint`expr 8 \* $ngx_ptr_size`_t"
+ echo ", $found used"
+
+ echo "typedef $found uintptr_t;" >> $NGX_AUTO_CONFIG_H
+ echo "typedef $found intptr_t;" | sed -e 's/u//g' >> $NGX_AUTO_CONFIG_H
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/value b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/value
new file mode 100644
index 00000000000..ac88a3919ef
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/types/value
@@ -0,0 +1,12 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+cat << END >> $NGX_AUTO_CONFIG_H
+
+#ifndef $ngx_param
+#define $ngx_param $ngx_value
+#endif
+
+END
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/unix b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/unix
new file mode 100755
index 00000000000..9b4764c6d24
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/auto/unix
@@ -0,0 +1,827 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+
+NGX_USER=${NGX_USER:-nobody}
+
+if [ -z "$NGX_GROUP" ]; then
+ if [ $NGX_USER = nobody ]; then
+ if grep nobody /etc/group 2>&1 >/dev/null; then
+ echo "checking for nobody group ... found"
+ NGX_GROUP=nobody
+ else
+ echo "checking for nobody group ... not found"
+
+ if grep nogroup /etc/group 2>&1 >/dev/null; then
+ echo "checking for nogroup group ... found"
+ NGX_GROUP=nogroup
+ else
+ echo "checking for nogroup group ... not found"
+ NGX_GROUP=nobody
+ fi
+ fi
+ else
+ NGX_GROUP=$NGX_USER
+ fi
+fi
+
+
+ngx_feature="poll()"
+ngx_feature_name=
+ngx_feature_run=no
+ngx_feature_incs="#include <poll.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int n; struct pollfd pl;
+ pl.fd = 0;
+ pl.events = 0;
+ pl.revents = 0;
+ n = poll(&pl, 1, 0);
+ if (n == -1) return 1"
+. auto/feature
+
+if [ $ngx_found = no ]; then
+ EVENT_POLL=NONE
+fi
+
+
+ngx_feature="/dev/poll"
+ngx_feature_name="NGX_HAVE_DEVPOLL"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/devpoll.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int n, dp; struct dvpoll dvp;
+ dp = 0;
+ dvp.dp_fds = NULL;
+ dvp.dp_nfds = 0;
+ dvp.dp_timeout = 0;
+ n = ioctl(dp, DP_POLL, &dvp);
+ if (n == -1) return 1"
+. auto/feature
+
+if [ $ngx_found = yes ]; then
+ CORE_SRCS="$CORE_SRCS $DEVPOLL_SRCS"
+ EVENT_MODULES="$EVENT_MODULES $DEVPOLL_MODULE"
+ EVENT_FOUND=YES
+fi
+
+
+if test -z "$NGX_KQUEUE_CHECKED"; then
+ ngx_feature="kqueue"
+ ngx_feature_name="NGX_HAVE_KQUEUE"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <sys/event.h>"
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test="int kq; kq = kqueue()"
+ . auto/feature
+
+ if [ $ngx_found = yes ]; then
+
+ have=NGX_HAVE_CLEAR_EVENT . auto/have
+ EVENT_MODULES="$EVENT_MODULES $KQUEUE_MODULE"
+ CORE_SRCS="$CORE_SRCS $KQUEUE_SRCS"
+ EVENT_FOUND=YES
+
+ ngx_feature="kqueue's NOTE_LOWAT"
+ ngx_feature_name="NGX_HAVE_LOWAT_EVENT"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <sys/event.h>"
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test="struct kevent kev;
+ kev.fflags = NOTE_LOWAT;"
+ . auto/feature
+
+
+ ngx_feature="kqueue's EVFILT_TIMER"
+ ngx_feature_name="NGX_HAVE_TIMER_EVENT"
+ ngx_feature_run=yes
+ ngx_feature_incs="#include <sys/event.h>
+ #include <sys/time.h>"
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test="int kq;
+ struct kevent kev;
+ struct timespec ts;
+
+ if ((kq = kqueue()) == -1) return 1;
+
+ kev.ident = 0;
+ kev.filter = EVFILT_TIMER;
+ kev.flags = EV_ADD|EV_ENABLE;
+ kev.fflags = 0;
+ kev.data = 1000;
+ kev.udata = 0;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+
+ if (kevent(kq, &kev, 1, &kev, 1, &ts) == -1) return 1;
+
+ if (kev.flags & EV_ERROR) return 1;"
+
+ . auto/feature
+ fi
+fi
+
+
+if [ "$NGX_SYSTEM" = "NetBSD" ]; then
+
+ # NetBSD 2.0 incompatibly defines kevent.udata as "intptr_t"
+
+ cat << END >> $NGX_AUTO_CONFIG_H
+
+#define NGX_KQUEUE_UDATA_T
+
+END
+
+else
+ cat << END >> $NGX_AUTO_CONFIG_H
+
+#define NGX_KQUEUE_UDATA_T (void *)
+
+END
+
+fi
+
+
+ngx_feature="crypt()"
+ngx_feature_name=
+ngx_feature_run=no
+ngx_feature_incs=
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="crypt(\"test\", \"salt\");"
+. auto/feature
+
+
+if [ $ngx_found = no ]; then
+
+ ngx_feature="crypt() in libcrypt"
+ ngx_feature_name=
+ ngx_feature_run=no
+ ngx_feature_incs=
+ ngx_feature_path=
+ ngx_feature_libs=-lcrypt
+ . auto/feature
+
+ if [ $ngx_found = yes ]; then
+ CRYPT_LIB="-lcrypt"
+ fi
+fi
+
+
+ngx_feature="F_READAHEAD"
+ngx_feature_name="NGX_HAVE_F_READAHEAD"
+ngx_feature_run=no
+ngx_feature_incs="#include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="fcntl(0, F_READAHEAD, 1);"
+. auto/feature
+
+
+ngx_feature="posix_fadvise()"
+ngx_feature_name="NGX_HAVE_POSIX_FADVISE"
+ngx_feature_run=no
+ngx_feature_incs="#include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="posix_fadvise(0, 0, 0, POSIX_FADV_SEQUENTIAL);"
+. auto/feature
+
+
+ngx_feature="O_DIRECT"
+ngx_feature_name="NGX_HAVE_O_DIRECT"
+ngx_feature_run=no
+ngx_feature_incs="#include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="fcntl(0, F_SETFL, O_DIRECT);"
+. auto/feature
+
+
+if [ $ngx_found = yes -a "$NGX_SYSTEM" = "Linux" ]; then
+ have=NGX_HAVE_ALIGNED_DIRECTIO . auto/have
+fi
+
+ngx_feature="F_NOCACHE"
+ngx_feature_name="NGX_HAVE_F_NOCACHE"
+ngx_feature_run=no
+ngx_feature_incs="#include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="fcntl(0, F_NOCACHE, 1);"
+. auto/feature
+
+
+ngx_feature="directio()"
+ngx_feature_name="NGX_HAVE_DIRECTIO"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/types.h>
+ #include <sys/fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="directio(0, DIRECTIO_ON);"
+. auto/feature
+
+
+ngx_feature="statfs()"
+ngx_feature_name="NGX_HAVE_STATFS"
+ngx_feature_run=no
+ngx_feature_incs="$NGX_INCLUDE_SYS_PARAM_H
+ $NGX_INCLUDE_SYS_MOUNT_H
+ $NGX_INCLUDE_SYS_VFS_H"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct statfs fs;
+ statfs(\".\", &fs);"
+. auto/feature
+
+
+ngx_feature="statvfs()"
+ngx_feature_name="NGX_HAVE_STATVFS"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/types.h>
+ #include <sys/statvfs.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct statvfs fs;
+ statvfs(\".\", &fs);"
+. auto/feature
+
+
+ngx_feature="dlopen()"
+ngx_feature_name=
+ngx_feature_run=no
+ngx_feature_incs="#include <dlfcn.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="dlopen(NULL, 0)"
+. auto/feature
+
+
+if [ $ngx_found != yes ]; then
+
+ ngx_feature="dlopen() in libdl"
+ ngx_feature_libs="-ldl"
+ . auto/feature
+
+ if [ $ngx_found = yes ]; then
+ NGX_LIBDL="-ldl"
+ fi
+fi
+
+
+ngx_feature="sched_yield()"
+ngx_feature_name="NGX_HAVE_SCHED_YIELD"
+ngx_feature_run=no
+ngx_feature_incs="#include <sched.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="sched_yield()"
+. auto/feature
+
+
+if [ $ngx_found != yes ]; then
+
+ ngx_feature="sched_yield() in librt"
+ ngx_feature_libs="-lrt"
+ . auto/feature
+
+ if [ $ngx_found = yes ]; then
+ CORE_LIBS="$CORE_LIBS -lrt"
+ fi
+fi
+
+
+ngx_feature="SO_SETFIB"
+ngx_feature_name="NGX_HAVE_SETFIB"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="setsockopt(0, SOL_SOCKET, SO_SETFIB, NULL, 4)"
+. auto/feature
+
+
+ngx_feature="SO_ACCEPTFILTER"
+ngx_feature_name="NGX_HAVE_DEFERRED_ACCEPT"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="setsockopt(0, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0)"
+. auto/feature
+
+
+ngx_feature="TCP_DEFER_ACCEPT"
+ngx_feature_name="NGX_HAVE_DEFERRED_ACCEPT"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <netinet/tcp.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_DEFER_ACCEPT, NULL, 0)"
+. auto/feature
+
+
+ngx_feature="TCP_KEEPIDLE"
+ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <netinet/tcp.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_KEEPIDLE, NULL, 0);
+ setsockopt(0, IPPROTO_TCP, TCP_KEEPINTVL, NULL, 0);
+ setsockopt(0, IPPROTO_TCP, TCP_KEEPCNT, NULL, 0)"
+. auto/feature
+
+
+ngx_feature="TCP_FASTOPEN"
+ngx_feature_name="NGX_HAVE_TCP_FASTOPEN"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <netinet/tcp.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_FASTOPEN, NULL, 0)"
+. auto/feature
+
+
+ngx_feature="TCP_INFO"
+ngx_feature_name="NGX_HAVE_TCP_INFO"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <netinet/tcp.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="socklen_t optlen = sizeof(struct tcp_info);
+ struct tcp_info ti;
+ ti.tcpi_rtt = 0;
+ ti.tcpi_rttvar = 0;
+ ti.tcpi_snd_cwnd = 0;
+ ti.tcpi_rcv_space = 0;
+ getsockopt(0, IPPROTO_TCP, TCP_INFO, &ti, &optlen)"
+. auto/feature
+
+
+ngx_feature="accept4()"
+ngx_feature_name="NGX_HAVE_ACCEPT4"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="accept4(0, NULL, NULL, SOCK_NONBLOCK)"
+. auto/feature
+
+if [ $NGX_FILE_AIO = YES ]; then
+
+ ngx_feature="kqueue AIO support"
+ ngx_feature_name="NGX_HAVE_FILE_AIO"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <aio.h>"
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test="int n; struct aiocb iocb;
+ iocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
+ n = aio_read(&iocb)"
+ . auto/feature
+
+ if [ $ngx_found = yes ]; then
+ CORE_SRCS="$CORE_SRCS $FILE_AIO_SRCS"
+ fi
+
+ if [ $ngx_found = no ]; then
+
+ ngx_feature="Linux AIO support"
+ ngx_feature_name="NGX_HAVE_FILE_AIO"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <linux/aio_abi.h>
+ #include <sys/eventfd.h>"
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test="struct iocb iocb;
+ iocb.aio_lio_opcode = IOCB_CMD_PREAD;
+ iocb.aio_flags = IOCB_FLAG_RESFD;
+ iocb.aio_resfd = -1;
+ (void) eventfd(0, 0)"
+ . auto/feature
+
+ if [ $ngx_found = yes ]; then
+ have=NGX_HAVE_EVENTFD . auto/have
+ have=NGX_HAVE_SYS_EVENTFD_H . auto/have
+ CORE_SRCS="$CORE_SRCS $LINUX_AIO_SRCS"
+ fi
+ fi
+
+ if [ $ngx_found = no ]; then
+
+ ngx_feature="Linux AIO support (SYS_eventfd)"
+ ngx_feature_incs="#include <linux/aio_abi.h>
+ #include <sys/syscall.h>"
+ ngx_feature_test="int n = SYS_eventfd;
+ struct iocb iocb;
+ iocb.aio_lio_opcode = IOCB_CMD_PREAD;
+ iocb.aio_flags = IOCB_FLAG_RESFD;
+ iocb.aio_resfd = -1;"
+ . auto/feature
+
+ if [ $ngx_found = yes ]; then
+ have=NGX_HAVE_EVENTFD . auto/have
+ CORE_SRCS="$CORE_SRCS $LINUX_AIO_SRCS"
+ fi
+ fi
+
+ if [ $ngx_found = no ]; then
+ cat << END
+
+$0: no supported file AIO was found
+Currently file AIO is supported on FreeBSD 4.3+ and Linux 2.6.22+ only
+
+END
+ exit 1
+ fi
+fi
+
+
+have=NGX_HAVE_UNIX_DOMAIN . auto/have
+
+ngx_feature_libs=
+
+
+# C types
+
+ngx_type="int"; . auto/types/sizeof
+
+ngx_type="long"; . auto/types/sizeof
+
+ngx_type="long long"; . auto/types/sizeof
+
+ngx_type="void *"; . auto/types/sizeof; ngx_ptr_size=$ngx_size
+ngx_param=NGX_PTR_SIZE; ngx_value=$ngx_size; . auto/types/value
+
+
+# POSIX types
+
+case "$NGX_AUTO_CONFIG_H" in
+ /*)
+ NGX_INCLUDE_AUTO_CONFIG_H="#include \"$NGX_AUTO_CONFIG_H\""
+ ;;
+ *)
+ NGX_INCLUDE_AUTO_CONFIG_H="#include \"../$NGX_AUTO_CONFIG_H\""
+ ;;
+esac
+
+ngx_type="uint64_t"; ngx_types="u_int64_t"; . auto/types/typedef
+
+ngx_type="sig_atomic_t"; ngx_types="int"; . auto/types/typedef
+. auto/types/sizeof
+ngx_param=NGX_SIG_ATOMIC_T_SIZE; ngx_value=$ngx_size; . auto/types/value
+
+ngx_type="socklen_t"; ngx_types="int"; . auto/types/typedef
+
+ngx_type="in_addr_t"; ngx_types="uint32_t"; . auto/types/typedef
+
+ngx_type="in_port_t"; ngx_types="u_short"; . auto/types/typedef
+
+ngx_type="rlim_t"; ngx_types="int"; . auto/types/typedef
+
+. auto/types/uintptr_t
+
+. auto/endianness
+
+ngx_type="size_t"; . auto/types/sizeof
+ngx_param=NGX_MAX_SIZE_T_VALUE; ngx_value=$ngx_max_value; . auto/types/value
+ngx_param=NGX_SIZE_T_LEN; ngx_value=$ngx_max_len; . auto/types/value
+
+ngx_type="off_t"; . auto/types/sizeof
+ngx_param=NGX_MAX_OFF_T_VALUE; ngx_value=$ngx_max_value; . auto/types/value
+ngx_param=NGX_OFF_T_LEN; ngx_value=$ngx_max_len; . auto/types/value
+
+ngx_type="time_t"; . auto/types/sizeof
+ngx_param=NGX_TIME_T_SIZE; ngx_value=$ngx_size; . auto/types/value
+ngx_param=NGX_TIME_T_LEN; ngx_value=$ngx_max_len; . auto/types/value
+
+
+# syscalls, libc calls and some features
+
+
+if [ $NGX_IPV6 = YES ]; then
+ ngx_feature="AF_INET6"
+ ngx_feature_name="NGX_HAVE_INET6"
+ ngx_feature_run=no
+ ngx_feature_incs="#include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>"
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test="struct sockaddr_in6 sin6;
+ sin6.sin6_family = AF_INET6;"
+ . auto/feature
+fi
+
+
+ngx_feature="setproctitle()"
+ngx_feature_name="NGX_HAVE_SETPROCTITLE"
+ngx_feature_run=no
+ngx_feature_incs="#include <stdlib.h>"
+ngx_feature_path=
+ngx_feature_libs=$NGX_SETPROCTITLE_LIB
+ngx_feature_test="setproctitle(\"test\");"
+. auto/feature
+
+
+ngx_feature="pread()"
+ngx_feature_name="NGX_HAVE_PREAD"
+ngx_feature_run=no
+ngx_feature_incs=
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="char buf[1]; ssize_t n; n = pread(0, buf, 1, 0);
+ if (n == -1) return 1"
+. auto/feature
+
+
+ngx_feature="pwrite()"
+ngx_feature_name="NGX_HAVE_PWRITE"
+ngx_feature_run=no
+ngx_feature_incs=
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="char buf[1]; ssize_t n; n = pwrite(1, buf, 1, 0);
+ if (n == -1) return 1"
+. auto/feature
+
+
+ngx_feature="sys_nerr"
+ngx_feature_name="NGX_SYS_NERR"
+ngx_feature_run=value
+ngx_feature_incs='#include <errno.h>
+ #include <stdio.h>'
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test='printf("%d", sys_nerr);'
+. auto/feature
+
+
+if [ $ngx_found = no ]; then
+
+ # Cygiwn defines _sys_nerr
+ ngx_feature="_sys_nerr"
+ ngx_feature_name="NGX_SYS_NERR"
+ ngx_feature_run=value
+ ngx_feature_incs='#include <errno.h>
+ #include <stdio.h>'
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test='printf("%d", _sys_nerr);'
+ . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+ # Solaris has no sys_nerr
+ ngx_feature='maximum errno'
+ ngx_feature_name=NGX_SYS_NERR
+ ngx_feature_run=value
+ ngx_feature_incs='#include <errno.h>
+ #include <string.h>
+ #include <stdio.h>'
+ ngx_feature_path=
+ ngx_feature_libs=
+ ngx_feature_test='int n;
+ char *p;
+ for (n = 1; n < 1000; n++) {
+ errno = 0;
+ p = strerror(n);
+ if (errno == EINVAL
+ || p == NULL
+ || strncmp(p, "Unknown error", 13) == 0)
+ {
+ break;
+ }
+ }
+ printf("%d", n);'
+ . auto/feature
+fi
+
+
+ngx_feature="localtime_r()"
+ngx_feature_name="NGX_HAVE_LOCALTIME_R"
+ngx_feature_run=no
+ngx_feature_incs="#include <time.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct tm t; time_t c=0; localtime_r(&c, &t)"
+. auto/feature
+
+
+ngx_feature="posix_memalign()"
+ngx_feature_name="NGX_HAVE_POSIX_MEMALIGN"
+ngx_feature_run=no
+ngx_feature_incs="#include <stdlib.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="void *p; int n; n = posix_memalign(&p, 4096, 4096);
+ if (n != 0) return 1"
+. auto/feature
+
+
+ngx_feature="memalign()"
+ngx_feature_name="NGX_HAVE_MEMALIGN"
+ngx_feature_run=no
+ngx_feature_incs="#include <stdlib.h>
+ #include <malloc.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="void *p; p = memalign(4096, 4096);
+ if (p == NULL) return 1"
+. auto/feature
+
+
+ngx_feature="mmap(MAP_ANON|MAP_SHARED)"
+ngx_feature_name="NGX_HAVE_MAP_ANON"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/mman.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="void *p;
+ p = mmap(NULL, 4096, PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_SHARED, -1, 0);
+ if (p == MAP_FAILED) return 1;"
+. auto/feature
+
+
+ngx_feature='mmap("/dev/zero", MAP_SHARED)'
+ngx_feature_name="NGX_HAVE_MAP_DEVZERO"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/mman.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test='void *p; int fd;
+ fd = open("/dev/zero", O_RDWR);
+ p = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (p == MAP_FAILED) return 1;'
+. auto/feature
+
+
+ngx_feature="System V shared memory"
+ngx_feature_name="NGX_HAVE_SYSVSHM"
+ngx_feature_run=yes
+ngx_feature_incs="#include <sys/ipc.h>
+ #include <sys/shm.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int id;
+ id = shmget(IPC_PRIVATE, 4096, (SHM_R|SHM_W|IPC_CREAT));
+ if (id == -1) return 1;
+ shmctl(id, IPC_RMID, NULL);"
+. auto/feature
+
+
+ngx_feature="POSIX semaphores"
+ngx_feature_name="NGX_HAVE_POSIX_SEM"
+ngx_feature_run=yes
+ngx_feature_incs="#include <semaphore.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="sem_t sem;
+ if (sem_init(&sem, 1, 0) == -1) return 1;
+ sem_destroy(&sem);"
+. auto/feature
+
+
+if [ $ngx_found = no ]; then
+
+ # Linux has POSIX semaphores in libpthread
+ ngx_feature="POSIX semaphores in libpthread"
+ ngx_feature_libs=-lpthread
+ . auto/feature
+
+ if [ $ngx_found = yes ]; then
+ CORE_LIBS="$CORE_LIBS -lpthread"
+ fi
+fi
+
+
+if [ $ngx_found = no ]; then
+
+ # Solaris has POSIX semaphores in librt
+ ngx_feature="POSIX semaphores in librt"
+ ngx_feature_libs=-lrt
+ . auto/feature
+
+ if [ $ngx_found = yes ]; then
+ CORE_LIBS="$CORE_LIBS -lrt"
+ fi
+fi
+
+
+ngx_feature="struct msghdr.msg_control"
+ngx_feature_name="NGX_HAVE_MSGHDR_MSG_CONTROL"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>
+ #include <stdio.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct msghdr msg;
+ printf(\"%d\", (int) sizeof(msg.msg_control))"
+. auto/feature
+
+
+ngx_feature="ioctl(FIONBIO)"
+ngx_feature_name="NGX_HAVE_FIONBIO"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/ioctl.h>
+ #include <stdio.h>
+ $NGX_INCLUDE_SYS_FILIO_H"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="int i = FIONBIO; printf(\"%d\", i)"
+. auto/feature
+
+
+ngx_feature="struct tm.tm_gmtoff"
+ngx_feature_name="NGX_HAVE_GMTOFF"
+ngx_feature_run=no
+ngx_feature_incs="#include <time.h>
+ #include <stdio.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct tm tm; tm.tm_gmtoff = 0;
+ printf(\"%d\", (int) tm.tm_gmtoff)"
+. auto/feature
+
+
+ngx_feature="struct dirent.d_namlen"
+ngx_feature_name="NGX_HAVE_D_NAMLEN"
+ngx_feature_run=no
+ngx_feature_incs="#include <dirent.h>
+ #include <stdio.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct dirent dir; dir.d_namlen = 0;
+ printf(\"%d\", (int) dir.d_namlen)"
+. auto/feature
+
+
+ngx_feature="struct dirent.d_type"
+ngx_feature_name="NGX_HAVE_D_TYPE"
+ngx_feature_run=no
+ngx_feature_incs="#include <dirent.h>
+ #include <stdio.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct dirent dir; dir.d_type = DT_REG;
+ printf(\"%d\", (int) dir.d_type)"
+. auto/feature
+
+
+ngx_feature="sysconf(_SC_NPROCESSORS_ONLN)"
+ngx_feature_name="NGX_HAVE_SC_NPROCESSORS_ONLN"
+ngx_feature_run=no
+ngx_feature_incs=
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="sysconf(_SC_NPROCESSORS_ONLN)"
+. auto/feature
+
+
+ngx_feature="openat(), fstatat()"
+ngx_feature_name="NGX_HAVE_OPENAT"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/types.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="struct stat sb;
+ openat(AT_FDCWD, \".\", O_RDONLY|O_NOFOLLOW);
+ fstatat(AT_FDCWD, \".\", &sb, AT_SYMLINK_NOFOLLOW);"
+. auto/feature
+
+
+ngx_feature="getaddrinfo()"
+ngx_feature_name="NGX_HAVE_GETADDRINFO"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netdb.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test='struct addrinfo *res;
+ if (getaddrinfo("localhost", NULL, NULL, &res) != 0) return 1;
+ freeaddrinfo(res)'
+. auto/feature
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/fastcgi.conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/fastcgi.conf
new file mode 100644
index 00000000000..ac9ff92049f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/fastcgi.conf
@@ -0,0 +1,25 @@
+
+fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+fastcgi_param QUERY_STRING $query_string;
+fastcgi_param REQUEST_METHOD $request_method;
+fastcgi_param CONTENT_TYPE $content_type;
+fastcgi_param CONTENT_LENGTH $content_length;
+
+fastcgi_param SCRIPT_NAME $fastcgi_script_name;
+fastcgi_param REQUEST_URI $request_uri;
+fastcgi_param DOCUMENT_URI $document_uri;
+fastcgi_param DOCUMENT_ROOT $document_root;
+fastcgi_param SERVER_PROTOCOL $server_protocol;
+fastcgi_param HTTPS $https if_not_empty;
+
+fastcgi_param GATEWAY_INTERFACE CGI/1.1;
+fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
+
+fastcgi_param REMOTE_ADDR $remote_addr;
+fastcgi_param REMOTE_PORT $remote_port;
+fastcgi_param SERVER_ADDR $server_addr;
+fastcgi_param SERVER_PORT $server_port;
+fastcgi_param SERVER_NAME $server_name;
+
+# PHP only, required if PHP was built with --enable-force-cgi-redirect
+fastcgi_param REDIRECT_STATUS 200;
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/fastcgi_params b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/fastcgi_params
new file mode 100644
index 00000000000..71e2c2e3bed
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/fastcgi_params
@@ -0,0 +1,24 @@
+
+fastcgi_param QUERY_STRING $query_string;
+fastcgi_param REQUEST_METHOD $request_method;
+fastcgi_param CONTENT_TYPE $content_type;
+fastcgi_param CONTENT_LENGTH $content_length;
+
+fastcgi_param SCRIPT_NAME $fastcgi_script_name;
+fastcgi_param REQUEST_URI $request_uri;
+fastcgi_param DOCUMENT_URI $document_uri;
+fastcgi_param DOCUMENT_ROOT $document_root;
+fastcgi_param SERVER_PROTOCOL $server_protocol;
+fastcgi_param HTTPS $https if_not_empty;
+
+fastcgi_param GATEWAY_INTERFACE CGI/1.1;
+fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
+
+fastcgi_param REMOTE_ADDR $remote_addr;
+fastcgi_param REMOTE_PORT $remote_port;
+fastcgi_param SERVER_ADDR $server_addr;
+fastcgi_param SERVER_PORT $server_port;
+fastcgi_param SERVER_NAME $server_name;
+
+# PHP only, required if PHP was built with --enable-force-cgi-redirect
+fastcgi_param REDIRECT_STATUS 200;
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/koi-utf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/koi-utf
new file mode 100644
index 00000000000..e7974ff6ad9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/koi-utf
@@ -0,0 +1,109 @@
+
+# This map is not a full koi8-r <> utf8 map: it does not contain
+# box-drawing and some other characters. Besides this map contains
+# several koi8-u and Byelorussian letters which are not in koi8-r.
+# If you need a full and standard map, use contrib/unicode2nginx/koi-utf
+# map instead.
+
+charset_map koi8-r utf-8 {
+
+ 80 E282AC ; # euro
+
+ 95 E280A2 ; # bullet
+
+ 9A C2A0 ; # &nbsp;
+
+ 9E C2B7 ; # &middot;
+
+ A3 D191 ; # small yo
+ A4 D194 ; # small Ukrainian ye
+
+ A6 D196 ; # small Ukrainian i
+ A7 D197 ; # small Ukrainian yi
+
+ AD D291 ; # small Ukrainian soft g
+ AE D19E ; # small Byelorussian short u
+
+ B0 C2B0 ; # &deg;
+
+ B3 D081 ; # capital YO
+ B4 D084 ; # capital Ukrainian YE
+
+ B6 D086 ; # capital Ukrainian I
+ B7 D087 ; # capital Ukrainian YI
+
+ B9 E28496 ; # numero sign
+
+ BD D290 ; # capital Ukrainian soft G
+ BE D18E ; # capital Byelorussian short U
+
+ BF C2A9 ; # (C)
+
+ C0 D18E ; # small yu
+ C1 D0B0 ; # small a
+ C2 D0B1 ; # small b
+ C3 D186 ; # small ts
+ C4 D0B4 ; # small d
+ C5 D0B5 ; # small ye
+ C6 D184 ; # small f
+ C7 D0B3 ; # small g
+ C8 D185 ; # small kh
+ C9 D0B8 ; # small i
+ CA D0B9 ; # small j
+ CB D0BA ; # small k
+ CC D0BB ; # small l
+ CD D0BC ; # small m
+ CE D0BD ; # small n
+ CF D0BE ; # small o
+
+ D0 D0BF ; # small p
+ D1 D18F ; # small ya
+ D2 D180 ; # small r
+ D3 D181 ; # small s
+ D4 D182 ; # small t
+ D5 D183 ; # small u
+ D6 D0B6 ; # small zh
+ D7 D0B2 ; # small v
+ D8 D18C ; # small soft sign
+ D9 D18B ; # small y
+ DA D0B7 ; # small z
+ DB D188 ; # small sh
+ DC D18D ; # small e
+ DD D189 ; # small shch
+ DE D187 ; # small ch
+ DF D18A ; # small hard sign
+
+ E0 D0AE ; # capital YU
+ E1 D090 ; # capital A
+ E2 D091 ; # capital B
+ E3 D0A6 ; # capital TS
+ E4 D094 ; # capital D
+ E5 D095 ; # capital YE
+ E6 D0A4 ; # capital F
+ E7 D093 ; # capital G
+ E8 D0A5 ; # capital KH
+ E9 D098 ; # capital I
+ EA D099 ; # capital J
+ EB D09A ; # capital K
+ EC D09B ; # capital L
+ ED D09C ; # capital M
+ EE D09D ; # capital N
+ EF D09E ; # capital O
+
+ F0 D09F ; # capital P
+ F1 D0AF ; # capital YA
+ F2 D0A0 ; # capital R
+ F3 D0A1 ; # capital S
+ F4 D0A2 ; # capital T
+ F5 D0A3 ; # capital U
+ F6 D096 ; # capital ZH
+ F7 D092 ; # capital V
+ F8 D0AC ; # capital soft sign
+ F9 D0AB ; # capital Y
+ FA D097 ; # capital Z
+ FB D0A8 ; # capital SH
+ FC D0AD ; # capital E
+ FD D0A9 ; # capital SHCH
+ FE D0A7 ; # capital CH
+ FF D0AA ; # capital hard sign
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/koi-win b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/koi-win
new file mode 100644
index 00000000000..72afabe89b8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/koi-win
@@ -0,0 +1,103 @@
+
+charset_map koi8-r windows-1251 {
+
+ 80 88 ; # euro
+
+ 95 95 ; # bullet
+
+ 9A A0 ; # &nbsp;
+
+ 9E B7 ; # &middot;
+
+ A3 B8 ; # small yo
+ A4 BA ; # small Ukrainian ye
+
+ A6 B3 ; # small Ukrainian i
+ A7 BF ; # small Ukrainian yi
+
+ AD B4 ; # small Ukrainian soft g
+ AE A2 ; # small Byelorussian short u
+
+ B0 B0 ; # &deg;
+
+ B3 A8 ; # capital YO
+ B4 AA ; # capital Ukrainian YE
+
+ B6 B2 ; # capital Ukrainian I
+ B7 AF ; # capital Ukrainian YI
+
+ B9 B9 ; # numero sign
+
+ BD A5 ; # capital Ukrainian soft G
+ BE A1 ; # capital Byelorussian short U
+
+ BF A9 ; # (C)
+
+ C0 FE ; # small yu
+ C1 E0 ; # small a
+ C2 E1 ; # small b
+ C3 F6 ; # small ts
+ C4 E4 ; # small d
+ C5 E5 ; # small ye
+ C6 F4 ; # small f
+ C7 E3 ; # small g
+ C8 F5 ; # small kh
+ C9 E8 ; # small i
+ CA E9 ; # small j
+ CB EA ; # small k
+ CC EB ; # small l
+ CD EC ; # small m
+ CE ED ; # small n
+ CF EE ; # small o
+
+ D0 EF ; # small p
+ D1 FF ; # small ya
+ D2 F0 ; # small r
+ D3 F1 ; # small s
+ D4 F2 ; # small t
+ D5 F3 ; # small u
+ D6 E6 ; # small zh
+ D7 E2 ; # small v
+ D8 FC ; # small soft sign
+ D9 FB ; # small y
+ DA E7 ; # small z
+ DB F8 ; # small sh
+ DC FD ; # small e
+ DD F9 ; # small shch
+ DE F7 ; # small ch
+ DF FA ; # small hard sign
+
+ E0 DE ; # capital YU
+ E1 C0 ; # capital A
+ E2 C1 ; # capital B
+ E3 D6 ; # capital TS
+ E4 C4 ; # capital D
+ E5 C5 ; # capital YE
+ E6 D4 ; # capital F
+ E7 C3 ; # capital G
+ E8 D5 ; # capital KH
+ E9 C8 ; # capital I
+ EA C9 ; # capital J
+ EB CA ; # capital K
+ EC CB ; # capital L
+ ED CC ; # capital M
+ EE CD ; # capital N
+ EF CE ; # capital O
+
+ F0 CF ; # capital P
+ F1 DF ; # capital YA
+ F2 D0 ; # capital R
+ F3 D1 ; # capital S
+ F4 D2 ; # capital T
+ F5 D3 ; # capital U
+ F6 C6 ; # capital ZH
+ F7 C2 ; # capital V
+ F8 DC ; # capital soft sign
+ F9 DB ; # capital Y
+ FA C7 ; # capital Z
+ FB D8 ; # capital SH
+ FC DD ; # capital E
+ FD D9 ; # capital SHCH
+ FE D7 ; # capital CH
+ FF DA ; # capital hard sign
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/mime.types b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/mime.types
new file mode 100644
index 00000000000..89be9a4cd63
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/mime.types
@@ -0,0 +1,89 @@
+
+types {
+ text/html html htm shtml;
+ text/css css;
+ text/xml xml;
+ image/gif gif;
+ image/jpeg jpeg jpg;
+ application/javascript js;
+ application/atom+xml atom;
+ application/rss+xml rss;
+
+ text/mathml mml;
+ text/plain txt;
+ text/vnd.sun.j2me.app-descriptor jad;
+ text/vnd.wap.wml wml;
+ text/x-component htc;
+
+ image/png png;
+ image/tiff tif tiff;
+ image/vnd.wap.wbmp wbmp;
+ image/x-icon ico;
+ image/x-jng jng;
+ image/x-ms-bmp bmp;
+ image/svg+xml svg svgz;
+ image/webp webp;
+
+ application/font-woff woff;
+ application/java-archive jar war ear;
+ application/json json;
+ application/mac-binhex40 hqx;
+ application/msword doc;
+ application/pdf pdf;
+ application/postscript ps eps ai;
+ application/rtf rtf;
+ application/vnd.apple.mpegurl m3u8;
+ application/vnd.ms-excel xls;
+ application/vnd.ms-fontobject eot;
+ application/vnd.ms-powerpoint ppt;
+ application/vnd.wap.wmlc wmlc;
+ application/vnd.google-earth.kml+xml kml;
+ application/vnd.google-earth.kmz kmz;
+ application/x-7z-compressed 7z;
+ application/x-cocoa cco;
+ application/x-java-archive-diff jardiff;
+ application/x-java-jnlp-file jnlp;
+ application/x-makeself run;
+ application/x-perl pl pm;
+ application/x-pilot prc pdb;
+ application/x-rar-compressed rar;
+ application/x-redhat-package-manager rpm;
+ application/x-sea sea;
+ application/x-shockwave-flash swf;
+ application/x-stuffit sit;
+ application/x-tcl tcl tk;
+ application/x-x509-ca-cert der pem crt;
+ application/x-xpinstall xpi;
+ application/xhtml+xml xhtml;
+ application/xspf+xml xspf;
+ application/zip zip;
+
+ application/octet-stream bin exe dll;
+ application/octet-stream deb;
+ application/octet-stream dmg;
+ application/octet-stream iso img;
+ application/octet-stream msi msp msm;
+
+ application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
+ application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx;
+ application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
+
+ audio/midi mid midi kar;
+ audio/mpeg mp3;
+ audio/ogg ogg;
+ audio/x-m4a m4a;
+ audio/x-realaudio ra;
+
+ video/3gpp 3gpp 3gp;
+ video/mp2t ts;
+ video/mp4 mp4;
+ video/mpeg mpeg mpg;
+ video/quicktime mov;
+ video/webm webm;
+ video/x-flv flv;
+ video/x-m4v m4v;
+ video/x-mng mng;
+ video/x-ms-asf asx asf;
+ video/x-ms-wmv wmv;
+ video/x-msvideo avi;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/nginx.conf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/nginx.conf
new file mode 100644
index 00000000000..29bc085f28c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/nginx.conf
@@ -0,0 +1,117 @@
+
+#user nobody;
+worker_processes 1;
+
+#error_log logs/error.log;
+#error_log logs/error.log notice;
+#error_log logs/error.log info;
+
+#pid logs/nginx.pid;
+
+
+events {
+ worker_connections 1024;
+}
+
+
+http {
+ include mime.types;
+ default_type application/octet-stream;
+
+ #log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ # '$status $body_bytes_sent "$http_referer" '
+ # '"$http_user_agent" "$http_x_forwarded_for"';
+
+ #access_log logs/access.log main;
+
+ sendfile on;
+ #tcp_nopush on;
+
+ #keepalive_timeout 0;
+ keepalive_timeout 65;
+
+ #gzip on;
+
+ server {
+ listen 80;
+ server_name localhost;
+
+ #charset koi8-r;
+
+ #access_log logs/host.access.log main;
+
+ location / {
+ root html;
+ index index.html index.htm;
+ }
+
+ #error_page 404 /404.html;
+
+ # redirect server error pages to the static page /50x.html
+ #
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root html;
+ }
+
+ # proxy the PHP scripts to Apache listening on 127.0.0.1:80
+ #
+ #location ~ \.php$ {
+ # proxy_pass http://127.0.0.1;
+ #}
+
+ # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
+ #
+ #location ~ \.php$ {
+ # root html;
+ # fastcgi_pass 127.0.0.1:9000;
+ # fastcgi_index index.php;
+ # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
+ # include fastcgi_params;
+ #}
+
+ # deny access to .htaccess files, if Apache's document root
+ # concurs with nginx's one
+ #
+ #location ~ /\.ht {
+ # deny all;
+ #}
+ }
+
+
+ # another virtual host using mix of IP-, name-, and port-based configuration
+ #
+ #server {
+ # listen 8000;
+ # listen somename:8080;
+ # server_name somename alias another.alias;
+
+ # location / {
+ # root html;
+ # index index.html index.htm;
+ # }
+ #}
+
+
+ # HTTPS server
+ #
+ #server {
+ # listen 443 ssl;
+ # server_name localhost;
+
+ # ssl_certificate cert.pem;
+ # ssl_certificate_key cert.key;
+
+ # ssl_session_cache shared:SSL:1m;
+ # ssl_session_timeout 5m;
+
+ # ssl_ciphers HIGH:!aNULL:!MD5;
+ # ssl_prefer_server_ciphers on;
+
+ # location / {
+ # root html;
+ # index index.html index.htm;
+ # }
+ #}
+
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/scgi_params b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/scgi_params
new file mode 100644
index 00000000000..47348ca381c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/scgi_params
@@ -0,0 +1,16 @@
+
+scgi_param REQUEST_METHOD $request_method;
+scgi_param REQUEST_URI $request_uri;
+scgi_param QUERY_STRING $query_string;
+scgi_param CONTENT_TYPE $content_type;
+
+scgi_param DOCUMENT_URI $document_uri;
+scgi_param DOCUMENT_ROOT $document_root;
+scgi_param SCGI 1;
+scgi_param SERVER_PROTOCOL $server_protocol;
+scgi_param HTTPS $https if_not_empty;
+
+scgi_param REMOTE_ADDR $remote_addr;
+scgi_param REMOTE_PORT $remote_port;
+scgi_param SERVER_PORT $server_port;
+scgi_param SERVER_NAME $server_name;
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/uwsgi_params b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/uwsgi_params
new file mode 100644
index 00000000000..f539451b6f5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/uwsgi_params
@@ -0,0 +1,16 @@
+
+uwsgi_param QUERY_STRING $query_string;
+uwsgi_param REQUEST_METHOD $request_method;
+uwsgi_param CONTENT_TYPE $content_type;
+uwsgi_param CONTENT_LENGTH $content_length;
+
+uwsgi_param REQUEST_URI $request_uri;
+uwsgi_param PATH_INFO $document_uri;
+uwsgi_param DOCUMENT_ROOT $document_root;
+uwsgi_param SERVER_PROTOCOL $server_protocol;
+uwsgi_param HTTPS $https if_not_empty;
+
+uwsgi_param REMOTE_ADDR $remote_addr;
+uwsgi_param REMOTE_PORT $remote_port;
+uwsgi_param SERVER_PORT $server_port;
+uwsgi_param SERVER_NAME $server_name;
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/win-utf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/win-utf
new file mode 100644
index 00000000000..ed8bc007a72
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/conf/win-utf
@@ -0,0 +1,126 @@
+
+# This map is not a full windows-1251 <> utf8 map: it does not
+# contain Serbian and Macedonian letters. If you need a full map,
+# use contrib/unicode2nginx/win-utf map instead.
+
+charset_map windows-1251 utf-8 {
+
+ 82 E2809A ; # single low-9 quotation mark
+
+ 84 E2809E ; # double low-9 quotation mark
+ 85 E280A6 ; # ellipsis
+ 86 E280A0 ; # dagger
+ 87 E280A1 ; # double dagger
+ 88 E282AC ; # euro
+ 89 E280B0 ; # per mille
+
+ 91 E28098 ; # left single quotation mark
+ 92 E28099 ; # right single quotation mark
+ 93 E2809C ; # left double quotation mark
+ 94 E2809D ; # right double quotation mark
+ 95 E280A2 ; # bullet
+ 96 E28093 ; # en dash
+ 97 E28094 ; # em dash
+
+ 99 E284A2 ; # trade mark sign
+
+ A0 C2A0 ; # &nbsp;
+ A1 D18E ; # capital Byelorussian short U
+ A2 D19E ; # small Byelorussian short u
+
+ A4 C2A4 ; # currency sign
+ A5 D290 ; # capital Ukrainian soft G
+ A6 C2A6 ; # borken bar
+ A7 C2A7 ; # section sign
+ A8 D081 ; # capital YO
+ A9 C2A9 ; # (C)
+ AA D084 ; # capital Ukrainian YE
+ AB C2AB ; # left-pointing double angle quotation mark
+ AC C2AC ; # not sign
+ AD C2AD ; # soft hypen
+ AE C2AE ; # (R)
+ AF D087 ; # capital Ukrainian YI
+
+ B0 C2B0 ; # &deg;
+ B1 C2B1 ; # plus-minus sign
+ B2 D086 ; # capital Ukrainian I
+ B3 D196 ; # small Ukrainian i
+ B4 D291 ; # small Ukrainian soft g
+ B5 C2B5 ; # micro sign
+ B6 C2B6 ; # pilcrow sign
+ B7 C2B7 ; # &middot;
+ B8 D191 ; # small yo
+ B9 E28496 ; # numero sign
+ BA D194 ; # small Ukrainian ye
+ BB C2BB ; # right-pointing double angle quotation mark
+
+ BF D197 ; # small Ukrainian yi
+
+ C0 D090 ; # capital A
+ C1 D091 ; # capital B
+ C2 D092 ; # capital V
+ C3 D093 ; # capital G
+ C4 D094 ; # capital D
+ C5 D095 ; # capital YE
+ C6 D096 ; # capital ZH
+ C7 D097 ; # capital Z
+ C8 D098 ; # capital I
+ C9 D099 ; # capital J
+ CA D09A ; # capital K
+ CB D09B ; # capital L
+ CC D09C ; # capital M
+ CD D09D ; # capital N
+ CE D09E ; # capital O
+ CF D09F ; # capital P
+
+ D0 D0A0 ; # capital R
+ D1 D0A1 ; # capital S
+ D2 D0A2 ; # capital T
+ D3 D0A3 ; # capital U
+ D4 D0A4 ; # capital F
+ D5 D0A5 ; # capital KH
+ D6 D0A6 ; # capital TS
+ D7 D0A7 ; # capital CH
+ D8 D0A8 ; # capital SH
+ D9 D0A9 ; # capital SHCH
+ DA D0AA ; # capital hard sign
+ DB D0AB ; # capital Y
+ DC D0AC ; # capital soft sign
+ DD D0AD ; # capital E
+ DE D0AE ; # capital YU
+ DF D0AF ; # capital YA
+
+ E0 D0B0 ; # small a
+ E1 D0B1 ; # small b
+ E2 D0B2 ; # small v
+ E3 D0B3 ; # small g
+ E4 D0B4 ; # small d
+ E5 D0B5 ; # small ye
+ E6 D0B6 ; # small zh
+ E7 D0B7 ; # small z
+ E8 D0B8 ; # small i
+ E9 D0B9 ; # small j
+ EA D0BA ; # small k
+ EB D0BB ; # small l
+ EC D0BC ; # small m
+ ED D0BD ; # small n
+ EE D0BE ; # small o
+ EF D0BF ; # small p
+
+ F0 D180 ; # small r
+ F1 D181 ; # small s
+ F2 D182 ; # small t
+ F3 D183 ; # small u
+ F4 D184 ; # small f
+ F5 D185 ; # small kh
+ F6 D186 ; # small ts
+ F7 D187 ; # small ch
+ F8 D188 ; # small sh
+ F9 D189 ; # small shch
+ FA D18A ; # small hard sign
+ FB D18B ; # small y
+ FC D18C ; # small soft sign
+ FD D18D ; # small e
+ FE D18E ; # small yu
+ FF D18F ; # small ya
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/README b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/README
new file mode 100644
index 00000000000..fec4b200863
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/README
@@ -0,0 +1,21 @@
+
+geo2nginx.pl by Andrei Nigmatulin
+
+ The perl script to convert CSV geoip database ( free download
+ at http://www.maxmind.com/app/geoip_country ) to format, suitable
+ for use by the ngx_http_geo_module.
+
+
+unicode2nginx by Maxim Dounin
+
+ The perl script to convert unicode mappings ( available
+ at http://www.unicode.org/Public/MAPPINGS/ ) to the nginx
+ configuration file format.
+ Two generated full maps for windows-1251 and koi8-r.
+
+
+vim by Evan Miller
+
+ Syntax highlighting of nginx configuration for vim, to be
+ placed into ~/.vim/.
+
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/geo2nginx.pl b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/geo2nginx.pl
new file mode 100644
index 00000000000..29243ecf2b2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/geo2nginx.pl
@@ -0,0 +1,58 @@
+#!/usr/bin/perl -w
+
+# (c) Andrei Nigmatulin, 2005
+#
+# this script provided "as is", without any warranties. use it at your own risk.
+#
+# special thanx to Andrew Sitnikov for perl port
+#
+# this script converts CSV geoip database (free download at http://www.maxmind.com/app/geoip_country)
+# to format, suitable for use with nginx_http_geo module (http://sysoev.ru/nginx)
+#
+# for example, line with ip range
+#
+# "62.16.68.0","62.16.127.255","1041253376","1041268735","RU","Russian Federation"
+#
+# will be converted to four subnetworks:
+#
+# 62.16.68.0/22 RU;
+# 62.16.72.0/21 RU;
+# 62.16.80.0/20 RU;
+# 62.16.96.0/19 RU;
+
+
+use warnings;
+use strict;
+
+while( <STDIN> ){
+ if (/"[^"]+","[^"]+","([^"]+)","([^"]+)","([^"]+)"/){
+ print_subnets($1, $2, $3);
+ }
+}
+
+sub print_subnets {
+ my ($a1, $a2, $c) = @_;
+ my $l;
+ while ($a1 <= $a2) {
+ for ($l = 0; ($a1 & (1 << $l)) == 0 && ($a1 + ((1 << ($l + 1)) - 1)) <= $a2; $l++){};
+ print long2ip($a1) . "/" . (32 - $l) . " " . $c . ";\n";
+ $a1 += (1 << $l);
+ }
+}
+
+sub long2ip {
+ my $ip = shift;
+
+ my $str = 0;
+
+ $str = ($ip & 255);
+
+ $ip >>= 8;
+ $str = ($ip & 255).".$str";
+
+ $ip >>= 8;
+ $str = ($ip & 255).".$str";
+
+ $ip >>= 8;
+ $str = ($ip & 255).".$str";
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/unicode2nginx/koi-utf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/unicode2nginx/koi-utf
new file mode 100644
index 00000000000..48853af9af9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/unicode2nginx/koi-utf
@@ -0,0 +1,131 @@
+charset_map koi8-r utf-8 {
+
+ 80 E29480 ; # BOX DRAWINGS LIGHT HORIZONTAL
+ 81 E29482 ; # BOX DRAWINGS LIGHT VERTICAL
+ 82 E2948C ; # BOX DRAWINGS LIGHT DOWN AND RIGHT
+ 83 E29490 ; # BOX DRAWINGS LIGHT DOWN AND LEFT
+ 84 E29494 ; # BOX DRAWINGS LIGHT UP AND RIGHT
+ 85 E29498 ; # BOX DRAWINGS LIGHT UP AND LEFT
+ 86 E2949C ; # BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ 87 E294A4 ; # BOX DRAWINGS LIGHT VERTICAL AND LEFT
+ 88 E294AC ; # BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+ 89 E294B4 ; # BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ 8A E294BC ; # BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+ 8B E29680 ; # UPPER HALF BLOCK
+ 8C E29684 ; # LOWER HALF BLOCK
+ 8D E29688 ; # FULL BLOCK
+ 8E E2968C ; # LEFT HALF BLOCK
+ 8F E29690 ; # RIGHT HALF BLOCK
+ 90 E29691 ; # LIGHT SHADE
+ 91 E29692 ; # MEDIUM SHADE
+ 92 E29693 ; # DARK SHADE
+ 93 E28CA0 ; # TOP HALF INTEGRAL
+ 94 E296A0 ; # BLACK SQUARE
+ 95 E28899 ; # BULLET OPERATOR
+ 96 E2889A ; # SQUARE ROOT
+ 97 E28988 ; # ALMOST EQUAL TO
+ 98 E289A4 ; # LESS-THAN OR EQUAL TO
+ 99 E289A5 ; # GREATER-THAN OR EQUAL TO
+ 9A C2A0 ; # NO-BREAK SPACE
+ 9B E28CA1 ; # BOTTOM HALF INTEGRAL
+ 9C C2B0 ; # DEGREE SIGN
+ 9D C2B2 ; # SUPERSCRIPT TWO
+ 9E C2B7 ; # MIDDLE DOT
+ 9F C3B7 ; # DIVISION SIGN
+ A0 E29590 ; # BOX DRAWINGS DOUBLE HORIZONTAL
+ A1 E29591 ; # BOX DRAWINGS DOUBLE VERTICAL
+ A2 E29592 ; # BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+ A3 D191 ; # CYRILLIC SMALL LETTER IO
+ A4 E29593 ; # BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+ A5 E29594 ; # BOX DRAWINGS DOUBLE DOWN AND RIGHT
+ A6 E29595 ; # BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+ A7 E29596 ; # BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+ A8 E29597 ; # BOX DRAWINGS DOUBLE DOWN AND LEFT
+ A9 E29598 ; # BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+ AA E29599 ; # BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+ AB E2959A ; # BOX DRAWINGS DOUBLE UP AND RIGHT
+ AC E2959B ; # BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+ AD E2959C ; # BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+ AE E2959D ; # BOX DRAWINGS DOUBLE UP AND LEFT
+ AF E2959E ; # BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+ B0 E2959F ; # BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+ B1 E295A0 ; # BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+ B2 E295A1 ; # BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+ B3 D081 ; # CYRILLIC CAPITAL LETTER IO
+ B4 E295A2 ; # BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+ B5 E295A3 ; # BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+ B6 E295A4 ; # BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+ B7 E295A5 ; # BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+ B8 E295A6 ; # BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+ B9 E295A7 ; # BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+ BA E295A8 ; # BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+ BB E295A9 ; # BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+ BC E295AA ; # BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+ BD E295AB ; # BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+ BE E295AC ; # BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+ BF C2A9 ; # COPYRIGHT SIGN
+ C0 D18E ; # CYRILLIC SMALL LETTER YU
+ C1 D0B0 ; # CYRILLIC SMALL LETTER A
+ C2 D0B1 ; # CYRILLIC SMALL LETTER BE
+ C3 D186 ; # CYRILLIC SMALL LETTER TSE
+ C4 D0B4 ; # CYRILLIC SMALL LETTER DE
+ C5 D0B5 ; # CYRILLIC SMALL LETTER IE
+ C6 D184 ; # CYRILLIC SMALL LETTER EF
+ C7 D0B3 ; # CYRILLIC SMALL LETTER GHE
+ C8 D185 ; # CYRILLIC SMALL LETTER HA
+ C9 D0B8 ; # CYRILLIC SMALL LETTER I
+ CA D0B9 ; # CYRILLIC SMALL LETTER SHORT I
+ CB D0BA ; # CYRILLIC SMALL LETTER KA
+ CC D0BB ; # CYRILLIC SMALL LETTER EL
+ CD D0BC ; # CYRILLIC SMALL LETTER EM
+ CE D0BD ; # CYRILLIC SMALL LETTER EN
+ CF D0BE ; # CYRILLIC SMALL LETTER O
+ D0 D0BF ; # CYRILLIC SMALL LETTER PE
+ D1 D18F ; # CYRILLIC SMALL LETTER YA
+ D2 D180 ; # CYRILLIC SMALL LETTER ER
+ D3 D181 ; # CYRILLIC SMALL LETTER ES
+ D4 D182 ; # CYRILLIC SMALL LETTER TE
+ D5 D183 ; # CYRILLIC SMALL LETTER U
+ D6 D0B6 ; # CYRILLIC SMALL LETTER ZHE
+ D7 D0B2 ; # CYRILLIC SMALL LETTER VE
+ D8 D18C ; # CYRILLIC SMALL LETTER SOFT SIGN
+ D9 D18B ; # CYRILLIC SMALL LETTER YERU
+ DA D0B7 ; # CYRILLIC SMALL LETTER ZE
+ DB D188 ; # CYRILLIC SMALL LETTER SHA
+ DC D18D ; # CYRILLIC SMALL LETTER E
+ DD D189 ; # CYRILLIC SMALL LETTER SHCHA
+ DE D187 ; # CYRILLIC SMALL LETTER CHE
+ DF D18A ; # CYRILLIC SMALL LETTER HARD SIGN
+ E0 D0AE ; # CYRILLIC CAPITAL LETTER YU
+ E1 D090 ; # CYRILLIC CAPITAL LETTER A
+ E2 D091 ; # CYRILLIC CAPITAL LETTER BE
+ E3 D0A6 ; # CYRILLIC CAPITAL LETTER TSE
+ E4 D094 ; # CYRILLIC CAPITAL LETTER DE
+ E5 D095 ; # CYRILLIC CAPITAL LETTER IE
+ E6 D0A4 ; # CYRILLIC CAPITAL LETTER EF
+ E7 D093 ; # CYRILLIC CAPITAL LETTER GHE
+ E8 D0A5 ; # CYRILLIC CAPITAL LETTER HA
+ E9 D098 ; # CYRILLIC CAPITAL LETTER I
+ EA D099 ; # CYRILLIC CAPITAL LETTER SHORT I
+ EB D09A ; # CYRILLIC CAPITAL LETTER KA
+ EC D09B ; # CYRILLIC CAPITAL LETTER EL
+ ED D09C ; # CYRILLIC CAPITAL LETTER EM
+ EE D09D ; # CYRILLIC CAPITAL LETTER EN
+ EF D09E ; # CYRILLIC CAPITAL LETTER O
+ F0 D09F ; # CYRILLIC CAPITAL LETTER PE
+ F1 D0AF ; # CYRILLIC CAPITAL LETTER YA
+ F2 D0A0 ; # CYRILLIC CAPITAL LETTER ER
+ F3 D0A1 ; # CYRILLIC CAPITAL LETTER ES
+ F4 D0A2 ; # CYRILLIC CAPITAL LETTER TE
+ F5 D0A3 ; # CYRILLIC CAPITAL LETTER U
+ F6 D096 ; # CYRILLIC CAPITAL LETTER ZHE
+ F7 D092 ; # CYRILLIC CAPITAL LETTER VE
+ F8 D0AC ; # CYRILLIC CAPITAL LETTER SOFT SIGN
+ F9 D0AB ; # CYRILLIC CAPITAL LETTER YERU
+ FA D097 ; # CYRILLIC CAPITAL LETTER ZE
+ FB D0A8 ; # CYRILLIC CAPITAL LETTER SHA
+ FC D0AD ; # CYRILLIC CAPITAL LETTER E
+ FD D0A9 ; # CYRILLIC CAPITAL LETTER SHCHA
+ FE D0A7 ; # CYRILLIC CAPITAL LETTER CHE
+ FF D0AA ; # CYRILLIC CAPITAL LETTER HARD SIGN
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/unicode2nginx/unicode-to-nginx.pl b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/unicode2nginx/unicode-to-nginx.pl
new file mode 100755
index 00000000000..daaf354a830
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/unicode2nginx/unicode-to-nginx.pl
@@ -0,0 +1,45 @@
+#!/usr/bin/perl -w
+
+# Convert unicode mappings to nginx configuration file format.
+
+# You may find useful mappings in various places, including
+# unicode.org official site:
+#
+# http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1251.TXT
+# http://www.unicode.org/Public/MAPPINGS/VENDORS/MISC/KOI8-R.TXT
+
+# Needs perl 5.6 or later.
+
+# Written by Maxim Dounin, mdounin@rambler-co.ru
+
+###############################################################################
+
+require 5.006;
+
+while (<>) {
+ # Skip comments and empty lines
+
+ next if /^#/;
+ next if /^\s*$/;
+ chomp;
+
+ # Convert mappings
+
+ if (/^\s*0x(..)\s*0x(....)\s*(#.*)/) {
+ # Mapping <from-code> <unicode-code> "#" <unicode-name>
+ my $cs_code = $1;
+ my $un_code = $2;
+ my $un_name = $3;
+
+ # Produce UTF-8 sequence from character code;
+
+ my $un_utf8 = join('', map { sprintf("%02X", $_) } unpack("C*", pack("U", hex($un_code))));
+
+ print " $cs_code $un_utf8 ; $un_name\n";
+
+ } else {
+ warn "Unrecognized line: '$_'";
+ }
+}
+
+###############################################################################
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/unicode2nginx/win-utf b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/unicode2nginx/win-utf
new file mode 100644
index 00000000000..af9f9aaa502
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/unicode2nginx/win-utf
@@ -0,0 +1,130 @@
+charset_map windows-1251 utf-8 {
+
+ 80 D082 ; #CYRILLIC CAPITAL LETTER DJE
+ 81 D083 ; #CYRILLIC CAPITAL LETTER GJE
+ 82 E2809A ; #SINGLE LOW-9 QUOTATION MARK
+ 83 D193 ; #CYRILLIC SMALL LETTER GJE
+ 84 E2809E ; #DOUBLE LOW-9 QUOTATION MARK
+ 85 E280A6 ; #HORIZONTAL ELLIPSIS
+ 86 E280A0 ; #DAGGER
+ 87 E280A1 ; #DOUBLE DAGGER
+ 88 E282AC ; #EURO SIGN
+ 89 E280B0 ; #PER MILLE SIGN
+ 8A D089 ; #CYRILLIC CAPITAL LETTER LJE
+ 8B E280B9 ; #SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ 8C D08A ; #CYRILLIC CAPITAL LETTER NJE
+ 8D D08C ; #CYRILLIC CAPITAL LETTER KJE
+ 8E D08B ; #CYRILLIC CAPITAL LETTER TSHE
+ 8F D08F ; #CYRILLIC CAPITAL LETTER DZHE
+ 90 D192 ; #CYRILLIC SMALL LETTER DJE
+ 91 E28098 ; #LEFT SINGLE QUOTATION MARK
+ 92 E28099 ; #RIGHT SINGLE QUOTATION MARK
+ 93 E2809C ; #LEFT DOUBLE QUOTATION MARK
+ 94 E2809D ; #RIGHT DOUBLE QUOTATION MARK
+ 95 E280A2 ; #BULLET
+ 96 E28093 ; #EN DASH
+ 97 E28094 ; #EM DASH
+ 99 E284A2 ; #TRADE MARK SIGN
+ 9A D199 ; #CYRILLIC SMALL LETTER LJE
+ 9B E280BA ; #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ 9C D19A ; #CYRILLIC SMALL LETTER NJE
+ 9D D19C ; #CYRILLIC SMALL LETTER KJE
+ 9E D19B ; #CYRILLIC SMALL LETTER TSHE
+ 9F D19F ; #CYRILLIC SMALL LETTER DZHE
+ A0 C2A0 ; #NO-BREAK SPACE
+ A1 D08E ; #CYRILLIC CAPITAL LETTER SHORT U
+ A2 D19E ; #CYRILLIC SMALL LETTER SHORT U
+ A3 D088 ; #CYRILLIC CAPITAL LETTER JE
+ A4 C2A4 ; #CURRENCY SIGN
+ A5 D290 ; #CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+ A6 C2A6 ; #BROKEN BAR
+ A7 C2A7 ; #SECTION SIGN
+ A8 D081 ; #CYRILLIC CAPITAL LETTER IO
+ A9 C2A9 ; #COPYRIGHT SIGN
+ AA D084 ; #CYRILLIC CAPITAL LETTER UKRAINIAN IE
+ AB C2AB ; #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ AC C2AC ; #NOT SIGN
+ AD C2AD ; #SOFT HYPHEN
+ AE C2AE ; #REGISTERED SIGN
+ AF D087 ; #CYRILLIC CAPITAL LETTER YI
+ B0 C2B0 ; #DEGREE SIGN
+ B1 C2B1 ; #PLUS-MINUS SIGN
+ B2 D086 ; #CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+ B3 D196 ; #CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+ B4 D291 ; #CYRILLIC SMALL LETTER GHE WITH UPTURN
+ B5 C2B5 ; #MICRO SIGN
+ B6 C2B6 ; #PILCROW SIGN
+ B7 C2B7 ; #MIDDLE DOT
+ B8 D191 ; #CYRILLIC SMALL LETTER IO
+ B9 E28496 ; #NUMERO SIGN
+ BA D194 ; #CYRILLIC SMALL LETTER UKRAINIAN IE
+ BB C2BB ; #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ BC D198 ; #CYRILLIC SMALL LETTER JE
+ BD D085 ; #CYRILLIC CAPITAL LETTER DZE
+ BE D195 ; #CYRILLIC SMALL LETTER DZE
+ BF D197 ; #CYRILLIC SMALL LETTER YI
+ C0 D090 ; #CYRILLIC CAPITAL LETTER A
+ C1 D091 ; #CYRILLIC CAPITAL LETTER BE
+ C2 D092 ; #CYRILLIC CAPITAL LETTER VE
+ C3 D093 ; #CYRILLIC CAPITAL LETTER GHE
+ C4 D094 ; #CYRILLIC CAPITAL LETTER DE
+ C5 D095 ; #CYRILLIC CAPITAL LETTER IE
+ C6 D096 ; #CYRILLIC CAPITAL LETTER ZHE
+ C7 D097 ; #CYRILLIC CAPITAL LETTER ZE
+ C8 D098 ; #CYRILLIC CAPITAL LETTER I
+ C9 D099 ; #CYRILLIC CAPITAL LETTER SHORT I
+ CA D09A ; #CYRILLIC CAPITAL LETTER KA
+ CB D09B ; #CYRILLIC CAPITAL LETTER EL
+ CC D09C ; #CYRILLIC CAPITAL LETTER EM
+ CD D09D ; #CYRILLIC CAPITAL LETTER EN
+ CE D09E ; #CYRILLIC CAPITAL LETTER O
+ CF D09F ; #CYRILLIC CAPITAL LETTER PE
+ D0 D0A0 ; #CYRILLIC CAPITAL LETTER ER
+ D1 D0A1 ; #CYRILLIC CAPITAL LETTER ES
+ D2 D0A2 ; #CYRILLIC CAPITAL LETTER TE
+ D3 D0A3 ; #CYRILLIC CAPITAL LETTER U
+ D4 D0A4 ; #CYRILLIC CAPITAL LETTER EF
+ D5 D0A5 ; #CYRILLIC CAPITAL LETTER HA
+ D6 D0A6 ; #CYRILLIC CAPITAL LETTER TSE
+ D7 D0A7 ; #CYRILLIC CAPITAL LETTER CHE
+ D8 D0A8 ; #CYRILLIC CAPITAL LETTER SHA
+ D9 D0A9 ; #CYRILLIC CAPITAL LETTER SHCHA
+ DA D0AA ; #CYRILLIC CAPITAL LETTER HARD SIGN
+ DB D0AB ; #CYRILLIC CAPITAL LETTER YERU
+ DC D0AC ; #CYRILLIC CAPITAL LETTER SOFT SIGN
+ DD D0AD ; #CYRILLIC CAPITAL LETTER E
+ DE D0AE ; #CYRILLIC CAPITAL LETTER YU
+ DF D0AF ; #CYRILLIC CAPITAL LETTER YA
+ E0 D0B0 ; #CYRILLIC SMALL LETTER A
+ E1 D0B1 ; #CYRILLIC SMALL LETTER BE
+ E2 D0B2 ; #CYRILLIC SMALL LETTER VE
+ E3 D0B3 ; #CYRILLIC SMALL LETTER GHE
+ E4 D0B4 ; #CYRILLIC SMALL LETTER DE
+ E5 D0B5 ; #CYRILLIC SMALL LETTER IE
+ E6 D0B6 ; #CYRILLIC SMALL LETTER ZHE
+ E7 D0B7 ; #CYRILLIC SMALL LETTER ZE
+ E8 D0B8 ; #CYRILLIC SMALL LETTER I
+ E9 D0B9 ; #CYRILLIC SMALL LETTER SHORT I
+ EA D0BA ; #CYRILLIC SMALL LETTER KA
+ EB D0BB ; #CYRILLIC SMALL LETTER EL
+ EC D0BC ; #CYRILLIC SMALL LETTER EM
+ ED D0BD ; #CYRILLIC SMALL LETTER EN
+ EE D0BE ; #CYRILLIC SMALL LETTER O
+ EF D0BF ; #CYRILLIC SMALL LETTER PE
+ F0 D180 ; #CYRILLIC SMALL LETTER ER
+ F1 D181 ; #CYRILLIC SMALL LETTER ES
+ F2 D182 ; #CYRILLIC SMALL LETTER TE
+ F3 D183 ; #CYRILLIC SMALL LETTER U
+ F4 D184 ; #CYRILLIC SMALL LETTER EF
+ F5 D185 ; #CYRILLIC SMALL LETTER HA
+ F6 D186 ; #CYRILLIC SMALL LETTER TSE
+ F7 D187 ; #CYRILLIC SMALL LETTER CHE
+ F8 D188 ; #CYRILLIC SMALL LETTER SHA
+ F9 D189 ; #CYRILLIC SMALL LETTER SHCHA
+ FA D18A ; #CYRILLIC SMALL LETTER HARD SIGN
+ FB D18B ; #CYRILLIC SMALL LETTER YERU
+ FC D18C ; #CYRILLIC SMALL LETTER SOFT SIGN
+ FD D18D ; #CYRILLIC SMALL LETTER E
+ FE D18E ; #CYRILLIC SMALL LETTER YU
+ FF D18F ; #CYRILLIC SMALL LETTER YA
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/vim/ftdetect/nginx.vim b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/vim/ftdetect/nginx.vim
new file mode 100644
index 00000000000..3ae470d2494
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/vim/ftdetect/nginx.vim
@@ -0,0 +1,4 @@
+au BufRead,BufNewFile *.nginx set ft=nginx
+au BufRead,BufNewFile */etc/nginx/* set ft=nginx
+au BufRead,BufNewFile */usr/local/nginx/conf/* set ft=nginx
+au BufRead,BufNewFile nginx.conf set ft=nginx
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/vim/indent/nginx.vim b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/vim/indent/nginx.vim
new file mode 100644
index 00000000000..8601366854a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/vim/indent/nginx.vim
@@ -0,0 +1,11 @@
+if exists("b:did_indent")
+ finish
+endif
+let b:did_indent = 1
+
+setlocal indentexpr=
+
+" cindent actually works for nginx' simple file structure
+setlocal cindent
+" Just make sure that the comments are not reset as defs would be.
+setlocal cinkeys-=0#
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/vim/syntax/nginx.vim b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/vim/syntax/nginx.vim
new file mode 100644
index 00000000000..50d809bc460
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/contrib/vim/syntax/nginx.vim
@@ -0,0 +1,703 @@
+" Vim syntax file
+" Language: nginx.conf
+
+if exists("b:current_syntax")
+ finish
+end
+
+setlocal iskeyword+=.
+setlocal iskeyword+=/
+setlocal iskeyword+=:
+
+syn match ngxVariable '\$\(\w\+\|{\w\+}\)'
+syn match ngxVariableBlock '\$\(\w\+\|{\w\+}\)' contained
+syn match ngxVariableString '\$\(\w\+\|{\w\+}\)' contained
+syn region ngxBlock start=+^+ end=+{+ skip=+\${+ contains=ngxComment,ngxDirectiveBlock,ngxVariableBlock,ngxString oneline
+syn region ngxString start=+\z(["']\)+ end=+\z1+ skip=+\\\\\|\\\z1+ contains=ngxVariableString
+syn match ngxComment ' *#.*$'
+
+syn keyword ngxBoolean on
+syn keyword ngxBoolean off
+
+syn keyword ngxDirectiveBlock http contained
+syn keyword ngxDirectiveBlock mail contained
+syn keyword ngxDirectiveBlock events contained
+syn keyword ngxDirectiveBlock server contained
+syn keyword ngxDirectiveBlock types contained
+syn keyword ngxDirectiveBlock location contained
+syn keyword ngxDirectiveBlock upstream contained
+syn keyword ngxDirectiveBlock charset_map contained
+syn keyword ngxDirectiveBlock limit_except contained
+syn keyword ngxDirectiveBlock if contained
+syn keyword ngxDirectiveBlock geo contained
+syn keyword ngxDirectiveBlock map contained
+
+syn keyword ngxDirectiveImportant include
+syn keyword ngxDirectiveImportant root
+syn keyword ngxDirectiveImportant server
+syn keyword ngxDirectiveImportant server_name
+syn keyword ngxDirectiveImportant listen
+syn keyword ngxDirectiveImportant internal
+syn keyword ngxDirectiveImportant proxy_pass
+syn keyword ngxDirectiveImportant memcached_pass
+syn keyword ngxDirectiveImportant fastcgi_pass
+syn keyword ngxDirectiveImportant try_files
+
+syn keyword ngxDirectiveControl break
+syn keyword ngxDirectiveControl return
+syn keyword ngxDirectiveControl rewrite
+syn keyword ngxDirectiveControl set
+
+syn keyword ngxDirectiveError error_page
+syn keyword ngxDirectiveError post_action
+
+syn keyword ngxDirectiveDeprecated connections
+syn keyword ngxDirectiveDeprecated imap
+syn keyword ngxDirectiveDeprecated open_file_cache_retest
+syn keyword ngxDirectiveDeprecated optimize_server_names
+syn keyword ngxDirectiveDeprecated satisfy_any
+
+syn keyword ngxDirective accept_mutex
+syn keyword ngxDirective accept_mutex_delay
+syn keyword ngxDirective access_log
+syn keyword ngxDirective add_after_body
+syn keyword ngxDirective add_before_body
+syn keyword ngxDirective add_header
+syn keyword ngxDirective addition_types
+syn keyword ngxDirective aio
+syn keyword ngxDirective alias
+syn keyword ngxDirective allow
+syn keyword ngxDirective ancient_browser
+syn keyword ngxDirective ancient_browser_value
+syn keyword ngxDirective auth_basic
+syn keyword ngxDirective auth_basic_user_file
+syn keyword ngxDirective auth_http
+syn keyword ngxDirective auth_http_header
+syn keyword ngxDirective auth_http_timeout
+syn keyword ngxDirective autoindex
+syn keyword ngxDirective autoindex_exact_size
+syn keyword ngxDirective autoindex_localtime
+syn keyword ngxDirective charset
+syn keyword ngxDirective charset_types
+syn keyword ngxDirective client_body_buffer_size
+syn keyword ngxDirective client_body_in_file_only
+syn keyword ngxDirective client_body_in_single_buffer
+syn keyword ngxDirective client_body_temp_path
+syn keyword ngxDirective client_body_timeout
+syn keyword ngxDirective client_header_buffer_size
+syn keyword ngxDirective client_header_timeout
+syn keyword ngxDirective client_max_body_size
+syn keyword ngxDirective connection_pool_size
+syn keyword ngxDirective create_full_put_path
+syn keyword ngxDirective daemon
+syn keyword ngxDirective dav_access
+syn keyword ngxDirective dav_methods
+syn keyword ngxDirective debug_connection
+syn keyword ngxDirective debug_points
+syn keyword ngxDirective default_type
+syn keyword ngxDirective degradation
+syn keyword ngxDirective degrade
+syn keyword ngxDirective deny
+syn keyword ngxDirective devpoll_changes
+syn keyword ngxDirective devpoll_events
+syn keyword ngxDirective directio
+syn keyword ngxDirective directio_alignment
+syn keyword ngxDirective empty_gif
+syn keyword ngxDirective env
+syn keyword ngxDirective epoll_events
+syn keyword ngxDirective error_log
+syn keyword ngxDirective eventport_events
+syn keyword ngxDirective expires
+syn keyword ngxDirective fastcgi_bind
+syn keyword ngxDirective fastcgi_buffer_size
+syn keyword ngxDirective fastcgi_buffers
+syn keyword ngxDirective fastcgi_busy_buffers_size
+syn keyword ngxDirective fastcgi_cache
+syn keyword ngxDirective fastcgi_cache_key
+syn keyword ngxDirective fastcgi_cache_methods
+syn keyword ngxDirective fastcgi_cache_min_uses
+syn keyword ngxDirective fastcgi_cache_path
+syn keyword ngxDirective fastcgi_cache_use_stale
+syn keyword ngxDirective fastcgi_cache_valid
+syn keyword ngxDirective fastcgi_catch_stderr
+syn keyword ngxDirective fastcgi_connect_timeout
+syn keyword ngxDirective fastcgi_hide_header
+syn keyword ngxDirective fastcgi_ignore_client_abort
+syn keyword ngxDirective fastcgi_ignore_headers
+syn keyword ngxDirective fastcgi_index
+syn keyword ngxDirective fastcgi_intercept_errors
+syn keyword ngxDirective fastcgi_max_temp_file_size
+syn keyword ngxDirective fastcgi_next_upstream
+syn keyword ngxDirective fastcgi_param
+syn keyword ngxDirective fastcgi_pass_header
+syn keyword ngxDirective fastcgi_pass_request_body
+syn keyword ngxDirective fastcgi_pass_request_headers
+syn keyword ngxDirective fastcgi_read_timeout
+syn keyword ngxDirective fastcgi_send_lowat
+syn keyword ngxDirective fastcgi_send_timeout
+syn keyword ngxDirective fastcgi_split_path_info
+syn keyword ngxDirective fastcgi_store
+syn keyword ngxDirective fastcgi_store_access
+syn keyword ngxDirective fastcgi_temp_file_write_size
+syn keyword ngxDirective fastcgi_temp_path
+syn keyword ngxDirective fastcgi_upstream_fail_timeout
+syn keyword ngxDirective fastcgi_upstream_max_fails
+syn keyword ngxDirective flv
+syn keyword ngxDirective geoip_city
+syn keyword ngxDirective geoip_country
+syn keyword ngxDirective google_perftools_profiles
+syn keyword ngxDirective gzip
+syn keyword ngxDirective gzip_buffers
+syn keyword ngxDirective gzip_comp_level
+syn keyword ngxDirective gzip_disable
+syn keyword ngxDirective gzip_hash
+syn keyword ngxDirective gzip_http_version
+syn keyword ngxDirective gzip_min_length
+syn keyword ngxDirective gzip_no_buffer
+syn keyword ngxDirective gzip_proxied
+syn keyword ngxDirective gzip_static
+syn keyword ngxDirective gzip_types
+syn keyword ngxDirective gzip_vary
+syn keyword ngxDirective gzip_window
+syn keyword ngxDirective if_modified_since
+syn keyword ngxDirective ignore_invalid_headers
+syn keyword ngxDirective image_filter
+syn keyword ngxDirective image_filter_buffer
+syn keyword ngxDirective image_filter_jpeg_quality
+syn keyword ngxDirective image_filter_transparency
+syn keyword ngxDirective imap_auth
+syn keyword ngxDirective imap_capabilities
+syn keyword ngxDirective imap_client_buffer
+syn keyword ngxDirective index
+syn keyword ngxDirective ip_hash
+syn keyword ngxDirective keepalive_requests
+syn keyword ngxDirective keepalive_timeout
+syn keyword ngxDirective kqueue_changes
+syn keyword ngxDirective kqueue_events
+syn keyword ngxDirective large_client_header_buffers
+syn keyword ngxDirective limit_conn
+syn keyword ngxDirective limit_conn_log_level
+syn keyword ngxDirective limit_rate
+syn keyword ngxDirective limit_rate_after
+syn keyword ngxDirective limit_req
+syn keyword ngxDirective limit_req_log_level
+syn keyword ngxDirective limit_req_zone
+syn keyword ngxDirective limit_zone
+syn keyword ngxDirective lingering_time
+syn keyword ngxDirective lingering_timeout
+syn keyword ngxDirective lock_file
+syn keyword ngxDirective log_format
+syn keyword ngxDirective log_not_found
+syn keyword ngxDirective log_subrequest
+syn keyword ngxDirective map_hash_bucket_size
+syn keyword ngxDirective map_hash_max_size
+syn keyword ngxDirective master_process
+syn keyword ngxDirective memcached_bind
+syn keyword ngxDirective memcached_buffer_size
+syn keyword ngxDirective memcached_connect_timeout
+syn keyword ngxDirective memcached_next_upstream
+syn keyword ngxDirective memcached_read_timeout
+syn keyword ngxDirective memcached_send_timeout
+syn keyword ngxDirective memcached_upstream_fail_timeout
+syn keyword ngxDirective memcached_upstream_max_fails
+syn keyword ngxDirective merge_slashes
+syn keyword ngxDirective min_delete_depth
+syn keyword ngxDirective modern_browser
+syn keyword ngxDirective modern_browser_value
+syn keyword ngxDirective msie_padding
+syn keyword ngxDirective msie_refresh
+syn keyword ngxDirective multi_accept
+syn keyword ngxDirective open_file_cache
+syn keyword ngxDirective open_file_cache_errors
+syn keyword ngxDirective open_file_cache_events
+syn keyword ngxDirective open_file_cache_min_uses
+syn keyword ngxDirective open_file_cache_valid
+syn keyword ngxDirective open_log_file_cache
+syn keyword ngxDirective output_buffers
+syn keyword ngxDirective override_charset
+syn keyword ngxDirective perl
+syn keyword ngxDirective perl_modules
+syn keyword ngxDirective perl_require
+syn keyword ngxDirective perl_set
+syn keyword ngxDirective pid
+syn keyword ngxDirective pop3_auth
+syn keyword ngxDirective pop3_capabilities
+syn keyword ngxDirective port_in_redirect
+syn keyword ngxDirective postpone_gzipping
+syn keyword ngxDirective postpone_output
+syn keyword ngxDirective protocol
+syn keyword ngxDirective proxy
+syn keyword ngxDirective proxy_bind
+syn keyword ngxDirective proxy_buffer
+syn keyword ngxDirective proxy_buffer_size
+syn keyword ngxDirective proxy_buffering
+syn keyword ngxDirective proxy_buffers
+syn keyword ngxDirective proxy_busy_buffers_size
+syn keyword ngxDirective proxy_cache
+syn keyword ngxDirective proxy_cache_key
+syn keyword ngxDirective proxy_cache_methods
+syn keyword ngxDirective proxy_cache_min_uses
+syn keyword ngxDirective proxy_cache_path
+syn keyword ngxDirective proxy_cache_use_stale
+syn keyword ngxDirective proxy_cache_valid
+syn keyword ngxDirective proxy_connect_timeout
+syn keyword ngxDirective proxy_headers_hash_bucket_size
+syn keyword ngxDirective proxy_headers_hash_max_size
+syn keyword ngxDirective proxy_hide_header
+syn keyword ngxDirective proxy_ignore_client_abort
+syn keyword ngxDirective proxy_ignore_headers
+syn keyword ngxDirective proxy_intercept_errors
+syn keyword ngxDirective proxy_max_temp_file_size
+syn keyword ngxDirective proxy_method
+syn keyword ngxDirective proxy_next_upstream
+syn keyword ngxDirective proxy_pass_error_message
+syn keyword ngxDirective proxy_pass_header
+syn keyword ngxDirective proxy_pass_request_body
+syn keyword ngxDirective proxy_pass_request_headers
+syn keyword ngxDirective proxy_read_timeout
+syn keyword ngxDirective proxy_redirect
+syn keyword ngxDirective proxy_send_lowat
+syn keyword ngxDirective proxy_send_timeout
+syn keyword ngxDirective proxy_set_body
+syn keyword ngxDirective proxy_set_header
+syn keyword ngxDirective proxy_ssl_session_reuse
+syn keyword ngxDirective proxy_store
+syn keyword ngxDirective proxy_store_access
+syn keyword ngxDirective proxy_temp_file_write_size
+syn keyword ngxDirective proxy_temp_path
+syn keyword ngxDirective proxy_timeout
+syn keyword ngxDirective proxy_upstream_fail_timeout
+syn keyword ngxDirective proxy_upstream_max_fails
+syn keyword ngxDirective random_index
+syn keyword ngxDirective read_ahead
+syn keyword ngxDirective real_ip_header
+syn keyword ngxDirective recursive_error_pages
+syn keyword ngxDirective request_pool_size
+syn keyword ngxDirective reset_timedout_connection
+syn keyword ngxDirective resolver
+syn keyword ngxDirective resolver_timeout
+syn keyword ngxDirective rewrite_log
+syn keyword ngxDirective rtsig_overflow_events
+syn keyword ngxDirective rtsig_overflow_test
+syn keyword ngxDirective rtsig_overflow_threshold
+syn keyword ngxDirective rtsig_signo
+syn keyword ngxDirective satisfy
+syn keyword ngxDirective secure_link_secret
+syn keyword ngxDirective send_lowat
+syn keyword ngxDirective send_timeout
+syn keyword ngxDirective sendfile
+syn keyword ngxDirective sendfile_max_chunk
+syn keyword ngxDirective server_name_in_redirect
+syn keyword ngxDirective server_names_hash_bucket_size
+syn keyword ngxDirective server_names_hash_max_size
+syn keyword ngxDirective server_tokens
+syn keyword ngxDirective set_real_ip_from
+syn keyword ngxDirective smtp_auth
+syn keyword ngxDirective smtp_capabilities
+syn keyword ngxDirective smtp_client_buffer
+syn keyword ngxDirective smtp_greeting_delay
+syn keyword ngxDirective so_keepalive
+syn keyword ngxDirective source_charset
+syn keyword ngxDirective ssi
+syn keyword ngxDirective ssi_ignore_recycled_buffers
+syn keyword ngxDirective ssi_min_file_chunk
+syn keyword ngxDirective ssi_silent_errors
+syn keyword ngxDirective ssi_types
+syn keyword ngxDirective ssi_value_length
+syn keyword ngxDirective ssl
+syn keyword ngxDirective ssl_certificate
+syn keyword ngxDirective ssl_certificate_key
+syn keyword ngxDirective ssl_ciphers
+syn keyword ngxDirective ssl_client_certificate
+syn keyword ngxDirective ssl_crl
+syn keyword ngxDirective ssl_dhparam
+syn keyword ngxDirective ssl_engine
+syn keyword ngxDirective ssl_prefer_server_ciphers
+syn keyword ngxDirective ssl_protocols
+syn keyword ngxDirective ssl_session_cache
+syn keyword ngxDirective ssl_session_timeout
+syn keyword ngxDirective ssl_verify_client
+syn keyword ngxDirective ssl_verify_depth
+syn keyword ngxDirective starttls
+syn keyword ngxDirective stub_status
+syn keyword ngxDirective sub_filter
+syn keyword ngxDirective sub_filter_once
+syn keyword ngxDirective sub_filter_types
+syn keyword ngxDirective tcp_nodelay
+syn keyword ngxDirective tcp_nopush
+syn keyword ngxDirective thread_stack_size
+syn keyword ngxDirective timeout
+syn keyword ngxDirective timer_resolution
+syn keyword ngxDirective types_hash_bucket_size
+syn keyword ngxDirective types_hash_max_size
+syn keyword ngxDirective underscores_in_headers
+syn keyword ngxDirective uninitialized_variable_warn
+syn keyword ngxDirective use
+syn keyword ngxDirective user
+syn keyword ngxDirective userid
+syn keyword ngxDirective userid_domain
+syn keyword ngxDirective userid_expires
+syn keyword ngxDirective userid_mark
+syn keyword ngxDirective userid_name
+syn keyword ngxDirective userid_p3p
+syn keyword ngxDirective userid_path
+syn keyword ngxDirective userid_service
+syn keyword ngxDirective valid_referers
+syn keyword ngxDirective variables_hash_bucket_size
+syn keyword ngxDirective variables_hash_max_size
+syn keyword ngxDirective worker_connections
+syn keyword ngxDirective worker_cpu_affinity
+syn keyword ngxDirective worker_priority
+syn keyword ngxDirective worker_processes
+syn keyword ngxDirective worker_rlimit_core
+syn keyword ngxDirective worker_rlimit_nofile
+syn keyword ngxDirective worker_rlimit_sigpending
+syn keyword ngxDirective worker_threads
+syn keyword ngxDirective working_directory
+syn keyword ngxDirective xclient
+syn keyword ngxDirective xml_entities
+syn keyword ngxDirective xslt_stylesheet
+syn keyword ngxDirective xslt_types
+
+" 3rd party module list:
+" http://wiki.nginx.org/Nginx3rdPartyModules
+
+" Accept Language Module <http://wiki.nginx.org/NginxAcceptLanguageModule>
+" Parses the Accept-Language header and gives the most suitable locale from a list of supported locales.
+syn keyword ngxDirectiveThirdParty set_from_accept_language
+
+" Access Key Module <http://wiki.nginx.org/NginxHttpAccessKeyModule>
+" Denies access unless the request URL contains an access key.
+syn keyword ngxDirectiveThirdParty accesskey
+syn keyword ngxDirectiveThirdParty accesskey_arg
+syn keyword ngxDirectiveThirdParty accesskey_hashmethod
+syn keyword ngxDirectiveThirdParty accesskey_signature
+
+" Auth PAM Module <http://web.iti.upv.es/~sto/nginx/>
+" HTTP Basic Authentication using PAM.
+syn keyword ngxDirectiveThirdParty auth_pam
+syn keyword ngxDirectiveThirdParty auth_pam_service_name
+
+" Cache Purge Module <http://labs.frickle.com/nginx_ngx_cache_purge/>
+" Module adding ability to purge content from FastCGI and proxy caches.
+syn keyword ngxDirectiveThirdParty fastcgi_cache_purge
+syn keyword ngxDirectiveThirdParty proxy_cache_purge
+
+" Chunkin Module <http://wiki.nginx.org/NginxHttpChunkinModule>
+" HTTP 1.1 chunked-encoding request body support for Nginx.
+syn keyword ngxDirectiveThirdParty chunkin
+syn keyword ngxDirectiveThirdParty chunkin_keepalive
+syn keyword ngxDirectiveThirdParty chunkin_max_chunks_per_buf
+syn keyword ngxDirectiveThirdParty chunkin_resume
+
+" Circle GIF Module <http://wiki.nginx.org/NginxHttpCircleGifModule>
+" Generates simple circle images with the colors and size specified in the URL.
+syn keyword ngxDirectiveThirdParty circle_gif
+syn keyword ngxDirectiveThirdParty circle_gif_max_radius
+syn keyword ngxDirectiveThirdParty circle_gif_min_radius
+syn keyword ngxDirectiveThirdParty circle_gif_step_radius
+
+" Drizzle Module <http://github.com/chaoslawful/drizzle-nginx-module>
+" Make nginx talk directly to mysql, drizzle, and sqlite3 by libdrizzle.
+syn keyword ngxDirectiveThirdParty drizzle_connect_timeout
+syn keyword ngxDirectiveThirdParty drizzle_dbname
+syn keyword ngxDirectiveThirdParty drizzle_keepalive
+syn keyword ngxDirectiveThirdParty drizzle_module_header
+syn keyword ngxDirectiveThirdParty drizzle_pass
+syn keyword ngxDirectiveThirdParty drizzle_query
+syn keyword ngxDirectiveThirdParty drizzle_recv_cols_timeout
+syn keyword ngxDirectiveThirdParty drizzle_recv_rows_timeout
+syn keyword ngxDirectiveThirdParty drizzle_send_query_timeout
+syn keyword ngxDirectiveThirdParty drizzle_server
+
+" Echo Module <http://wiki.nginx.org/NginxHttpEchoModule>
+" Brings 'echo', 'sleep', 'time', 'exec' and more shell-style goodies to Nginx config file.
+syn keyword ngxDirectiveThirdParty echo
+syn keyword ngxDirectiveThirdParty echo_after_body
+syn keyword ngxDirectiveThirdParty echo_before_body
+syn keyword ngxDirectiveThirdParty echo_blocking_sleep
+syn keyword ngxDirectiveThirdParty echo_duplicate
+syn keyword ngxDirectiveThirdParty echo_end
+syn keyword ngxDirectiveThirdParty echo_exec
+syn keyword ngxDirectiveThirdParty echo_flush
+syn keyword ngxDirectiveThirdParty echo_foreach_split
+syn keyword ngxDirectiveThirdParty echo_location
+syn keyword ngxDirectiveThirdParty echo_location_async
+syn keyword ngxDirectiveThirdParty echo_read_request_body
+syn keyword ngxDirectiveThirdParty echo_request_body
+syn keyword ngxDirectiveThirdParty echo_reset_timer
+syn keyword ngxDirectiveThirdParty echo_sleep
+syn keyword ngxDirectiveThirdParty echo_subrequest
+syn keyword ngxDirectiveThirdParty echo_subrequest_async
+
+" Events Module <http://docs.dutov.org/nginx_modules_events_en.html>
+" Privides options for start/stop events.
+syn keyword ngxDirectiveThirdParty on_start
+syn keyword ngxDirectiveThirdParty on_stop
+
+" EY Balancer Module <http://github.com/ry/nginx-ey-balancer>
+" Adds a request queue to Nginx that allows the limiting of concurrent requests passed to the upstream.
+syn keyword ngxDirectiveThirdParty max_connections
+syn keyword ngxDirectiveThirdParty max_connections_max_queue_length
+syn keyword ngxDirectiveThirdParty max_connections_queue_timeout
+
+" Fancy Indexes Module <https://connectical.com/projects/ngx-fancyindex/wiki>
+" Like the built-in autoindex module, but fancier.
+syn keyword ngxDirectiveThirdParty fancyindex
+syn keyword ngxDirectiveThirdParty fancyindex_exact_size
+syn keyword ngxDirectiveThirdParty fancyindex_footer
+syn keyword ngxDirectiveThirdParty fancyindex_header
+syn keyword ngxDirectiveThirdParty fancyindex_localtime
+syn keyword ngxDirectiveThirdParty fancyindex_readme
+syn keyword ngxDirectiveThirdParty fancyindex_readme_mode
+
+" GeoIP Module (DEPRECATED) <http://wiki.nginx.org/NginxHttp3rdPartyGeoIPModule>
+" Country code lookups via the MaxMind GeoIP API.
+syn keyword ngxDirectiveThirdParty geoip_country_file
+
+" Headers More Module <http://wiki.nginx.org/NginxHttpHeadersMoreModule>
+" Set and clear input and output headers...more than "add"!
+syn keyword ngxDirectiveThirdParty more_clear_headers
+syn keyword ngxDirectiveThirdParty more_clear_input_headers
+syn keyword ngxDirectiveThirdParty more_set_headers
+syn keyword ngxDirectiveThirdParty more_set_input_headers
+
+" HTTP Push Module <http://pushmodule.slact.net/>
+" Turn Nginx into an adept long-polling HTTP Push (Comet) server.
+syn keyword ngxDirectiveThirdParty push_buffer_size
+syn keyword ngxDirectiveThirdParty push_listener
+syn keyword ngxDirectiveThirdParty push_message_timeout
+syn keyword ngxDirectiveThirdParty push_queue_messages
+syn keyword ngxDirectiveThirdParty push_sender
+
+" HTTP Redis Module <http://people.FreeBSD.ORG/~osa/ngx_http_redis-0.3.1.tar.gz>>
+" Redis <http://code.google.com/p/redis/> support.>
+syn keyword ngxDirectiveThirdParty redis_bind
+syn keyword ngxDirectiveThirdParty redis_buffer_size
+syn keyword ngxDirectiveThirdParty redis_connect_timeout
+syn keyword ngxDirectiveThirdParty redis_next_upstream
+syn keyword ngxDirectiveThirdParty redis_pass
+syn keyword ngxDirectiveThirdParty redis_read_timeout
+syn keyword ngxDirectiveThirdParty redis_send_timeout
+
+" HTTP JavaScript Module <http://wiki.github.com/kung-fu-tzu/ngx_http_js_module>
+" Embedding SpiderMonkey. Nearly full port on Perl module.
+syn keyword ngxDirectiveThirdParty js
+syn keyword ngxDirectiveThirdParty js_filter
+syn keyword ngxDirectiveThirdParty js_filter_types
+syn keyword ngxDirectiveThirdParty js_load
+syn keyword ngxDirectiveThirdParty js_maxmem
+syn keyword ngxDirectiveThirdParty js_require
+syn keyword ngxDirectiveThirdParty js_set
+syn keyword ngxDirectiveThirdParty js_utf8
+
+" Log Request Speed <http://wiki.nginx.org/NginxHttpLogRequestSpeed>
+" Log the time it took to process each request.
+syn keyword ngxDirectiveThirdParty log_request_speed_filter
+syn keyword ngxDirectiveThirdParty log_request_speed_filter_timeout
+
+" Memc Module <http://wiki.nginx.org/NginxHttpMemcModule>
+" An extended version of the standard memcached module that supports set, add, delete, and many more memcached commands.
+syn keyword ngxDirectiveThirdParty memc_buffer_size
+syn keyword ngxDirectiveThirdParty memc_cmds_allowed
+syn keyword ngxDirectiveThirdParty memc_connect_timeout
+syn keyword ngxDirectiveThirdParty memc_flags_to_last_modified
+syn keyword ngxDirectiveThirdParty memc_next_upstream
+syn keyword ngxDirectiveThirdParty memc_pass
+syn keyword ngxDirectiveThirdParty memc_read_timeout
+syn keyword ngxDirectiveThirdParty memc_send_timeout
+syn keyword ngxDirectiveThirdParty memc_upstream_fail_timeout
+syn keyword ngxDirectiveThirdParty memc_upstream_max_fails
+
+" Mogilefs Module <http://www.grid.net.ru/nginx/mogilefs.en.html>
+" Implements a MogileFS client, provides a replace to the Perlbal reverse proxy of the original MogileFS.
+syn keyword ngxDirectiveThirdParty mogilefs_connect_timeout
+syn keyword ngxDirectiveThirdParty mogilefs_domain
+syn keyword ngxDirectiveThirdParty mogilefs_methods
+syn keyword ngxDirectiveThirdParty mogilefs_noverify
+syn keyword ngxDirectiveThirdParty mogilefs_pass
+syn keyword ngxDirectiveThirdParty mogilefs_read_timeout
+syn keyword ngxDirectiveThirdParty mogilefs_send_timeout
+syn keyword ngxDirectiveThirdParty mogilefs_tracker
+
+" MP4 Streaming Lite Module <http://wiki.nginx.org/NginxMP4StreamingLite>
+" Will seek to a certain time within H.264/MP4 files when provided with a 'start' parameter in the URL.
+syn keyword ngxDirectiveThirdParty mp4
+
+" Nginx Notice Module <http://xph.us/software/nginx-notice/>
+" Serve static file to POST requests.
+syn keyword ngxDirectiveThirdParty notice
+syn keyword ngxDirectiveThirdParty notice_type
+
+" Phusion Passenger <http://www.modrails.com/documentation.html>
+" Easy and robust deployment of Ruby on Rails application on Apache and Nginx webservers.
+syn keyword ngxDirectiveThirdParty passenger_base_uri
+syn keyword ngxDirectiveThirdParty passenger_default_user
+syn keyword ngxDirectiveThirdParty passenger_enabled
+syn keyword ngxDirectiveThirdParty passenger_log_level
+syn keyword ngxDirectiveThirdParty passenger_max_instances_per_app
+syn keyword ngxDirectiveThirdParty passenger_max_pool_size
+syn keyword ngxDirectiveThirdParty passenger_pool_idle_time
+syn keyword ngxDirectiveThirdParty passenger_root
+syn keyword ngxDirectiveThirdParty passenger_ruby
+syn keyword ngxDirectiveThirdParty passenger_use_global_queue
+syn keyword ngxDirectiveThirdParty passenger_user_switching
+syn keyword ngxDirectiveThirdParty rack_env
+syn keyword ngxDirectiveThirdParty rails_app_spawner_idle_time
+syn keyword ngxDirectiveThirdParty rails_env
+syn keyword ngxDirectiveThirdParty rails_framework_spawner_idle_time
+syn keyword ngxDirectiveThirdParty rails_spawn_method
+
+" RDS JSON Module <http://github.com/agentzh/rds-json-nginx-module>
+" Help ngx_drizzle and other DBD modules emit JSON data.
+syn keyword ngxDirectiveThirdParty rds_json
+syn keyword ngxDirectiveThirdParty rds_json_content_type
+syn keyword ngxDirectiveThirdParty rds_json_format
+syn keyword ngxDirectiveThirdParty rds_json_ret
+
+" RRD Graph Module <http://wiki.nginx.org/NginxNgx_rrd_graph>
+" This module provides an HTTP interface to RRDtool's graphing facilities.
+syn keyword ngxDirectiveThirdParty rrd_graph
+syn keyword ngxDirectiveThirdParty rrd_graph_root
+
+" Secure Download <http://wiki.nginx.org/NginxHttpSecureDownload>
+" Create expiring links.
+syn keyword ngxDirectiveThirdParty secure_download
+syn keyword ngxDirectiveThirdParty secure_download_fail_location
+syn keyword ngxDirectiveThirdParty secure_download_path_mode
+syn keyword ngxDirectiveThirdParty secure_download_secret
+
+" SlowFS Cache Module <http://labs.frickle.com/nginx_ngx_slowfs_cache/>
+" Module adding ability to cache static files.
+syn keyword ngxDirectiveThirdParty slowfs_big_file_size
+syn keyword ngxDirectiveThirdParty slowfs_cache
+syn keyword ngxDirectiveThirdParty slowfs_cache_key
+syn keyword ngxDirectiveThirdParty slowfs_cache_min_uses
+syn keyword ngxDirectiveThirdParty slowfs_cache_path
+syn keyword ngxDirectiveThirdParty slowfs_cache_purge
+syn keyword ngxDirectiveThirdParty slowfs_cache_valid
+syn keyword ngxDirectiveThirdParty slowfs_temp_path
+
+" Strip Module <http://wiki.nginx.org/NginxHttpStripModule>
+" Whitespace remover.
+syn keyword ngxDirectiveThirdParty strip
+
+" Substitutions Module <http://wiki.nginx.org/NginxHttpSubsModule>
+" A filter module which can do both regular expression and fixed string substitutions on response bodies.
+syn keyword ngxDirectiveThirdParty subs_filter
+syn keyword ngxDirectiveThirdParty subs_filter_types
+
+" Supervisord Module <http://labs.frickle.com/nginx_ngx_supervisord/>
+" Module providing nginx with API to communicate with supervisord and manage (start/stop) backends on-demand.
+syn keyword ngxDirectiveThirdParty supervisord
+syn keyword ngxDirectiveThirdParty supervisord_inherit_backend_status
+syn keyword ngxDirectiveThirdParty supervisord_name
+syn keyword ngxDirectiveThirdParty supervisord_start
+syn keyword ngxDirectiveThirdParty supervisord_stop
+
+" Upload Module <http://www.grid.net.ru/nginx/upload.en.html>
+" Parses multipart/form-data allowing arbitrary handling of uploaded files.
+syn keyword ngxDirectiveThirdParty upload_aggregate_form_field
+syn keyword ngxDirectiveThirdParty upload_buffer_size
+syn keyword ngxDirectiveThirdParty upload_cleanup
+syn keyword ngxDirectiveThirdParty upload_limit_rate
+syn keyword ngxDirectiveThirdParty upload_max_file_size
+syn keyword ngxDirectiveThirdParty upload_max_output_body_len
+syn keyword ngxDirectiveThirdParty upload_max_part_header_len
+syn keyword ngxDirectiveThirdParty upload_pass
+syn keyword ngxDirectiveThirdParty upload_pass_args
+syn keyword ngxDirectiveThirdParty upload_pass_form_field
+syn keyword ngxDirectiveThirdParty upload_set_form_field
+syn keyword ngxDirectiveThirdParty upload_store
+syn keyword ngxDirectiveThirdParty upload_store_access
+
+" Upload Progress Module <http://wiki.nginx.org/NginxHttpUploadProgressModule>
+" Tracks and reports upload progress.
+syn keyword ngxDirectiveThirdParty report_uploads
+syn keyword ngxDirectiveThirdParty track_uploads
+syn keyword ngxDirectiveThirdParty upload_progress
+syn keyword ngxDirectiveThirdParty upload_progress_content_type
+syn keyword ngxDirectiveThirdParty upload_progress_header
+syn keyword ngxDirectiveThirdParty upload_progress_json_output
+syn keyword ngxDirectiveThirdParty upload_progress_template
+
+" Upstream Fair Balancer <http://wiki.nginx.org/NginxHttpUpstreamFairModule>
+" Sends an incoming request to the least-busy backend server, rather than distributing requests round-robin.
+syn keyword ngxDirectiveThirdParty fair
+syn keyword ngxDirectiveThirdParty upstream_fair_shm_size
+
+" Upstream Consistent Hash <http://wiki.nginx.org/NginxHttpUpstreamConsistentHash>
+" Select backend based on Consistent hash ring.
+syn keyword ngxDirectiveThirdParty consistent_hash
+
+" Upstream Hash Module <http://wiki.nginx.org/NginxHttpUpstreamRequestHashModule>
+" Provides simple upstream load distribution by hashing a configurable variable.
+syn keyword ngxDirectiveThirdParty hash
+syn keyword ngxDirectiveThirdParty hash_again
+
+" XSS Module <http://github.com/agentzh/xss-nginx-module>
+" Native support for cross-site scripting (XSS) in an nginx.
+syn keyword ngxDirectiveThirdParty xss_callback_arg
+syn keyword ngxDirectiveThirdParty xss_get
+syn keyword ngxDirectiveThirdParty xss_input_types
+syn keyword ngxDirectiveThirdParty xss_output_type
+
+" uWSGI Module <http://wiki.nginx.org/HttpUwsgiModule>
+" Allows Nginx to interact with uWSGI processes and control what parameters are passed to the process.
+syn keyword ngxDirectiveThirdParty uwsgi_bind
+syn keyword ngxDirectiveThirdParty uwsgi_buffer_size
+syn keyword ngxDirectiveThirdParty uwsgi_buffering
+syn keyword ngxDirectiveThirdParty uwsgi_buffers
+syn keyword ngxDirectiveThirdParty uwsgi_busy_buffers_size
+syn keyword ngxDirectiveThirdParty uwsgi_cache
+syn keyword ngxDirectiveThirdParty uwsgi_cache_bypass
+syn keyword ngxDirectiveThirdParty uwsgi_cache_key
+syn keyword ngxDirectiveThirdParty uwsgi_cache_lock
+syn keyword ngxDirectiveThirdParty uwsgi_cache_lock_timeout
+syn keyword ngxDirectiveThirdParty uwsgi_cache_methods
+syn keyword ngxDirectiveThirdParty uwsgi_cache_min_uses
+syn keyword ngxDirectiveThirdParty uwsgi_cache_path
+syn keyword ngxDirectiveThirdParty uwsgi_cache_use_stale
+syn keyword ngxDirectiveThirdParty uwsgi_cache_valid
+syn keyword ngxDirectiveThirdParty uwsgi_connect_timeout
+syn keyword ngxDirectiveThirdParty uwsgi_hide_header
+syn keyword ngxDirectiveThirdParty uwsgi_ignore_client_abort
+syn keyword ngxDirectiveThirdParty uwsgi_ignore_headers
+syn keyword ngxDirectiveThirdParty uwsgi_intercept_errors
+syn keyword ngxDirectiveThirdParty uwsgi_max_temp_file_size
+syn keyword ngxDirectiveThirdParty uwsgi_modifier1
+syn keyword ngxDirectiveThirdParty uwsgi_modifier2
+syn keyword ngxDirectiveThirdParty uwsgi_next_upstream
+syn keyword ngxDirectiveThirdParty uwsgi_no_cache
+syn keyword ngxDirectiveThirdParty uwsgi_param
+syn keyword ngxDirectiveThirdParty uwsgi_pass
+syn keyword ngxDirectiveThirdParty uwsgi_pass_header
+syn keyword ngxDirectiveThirdParty uwsgi_pass_request_body
+syn keyword ngxDirectiveThirdParty uwsgi_pass_request_headers
+syn keyword ngxDirectiveThirdParty uwsgi_read_timeout
+syn keyword ngxDirectiveThirdParty uwsgi_send_timeout
+syn keyword ngxDirectiveThirdParty uwsgi_store
+syn keyword ngxDirectiveThirdParty uwsgi_store_access
+syn keyword ngxDirectiveThirdParty uwsgi_string
+syn keyword ngxDirectiveThirdParty uwsgi_temp_file_write_size
+syn keyword ngxDirectiveThirdParty uwsgi_temp_path
+
+" highlight
+
+hi link ngxComment Comment
+hi link ngxVariable Identifier
+hi link ngxVariableBlock Identifier
+hi link ngxVariableString PreProc
+hi link ngxBlock Normal
+hi link ngxString String
+
+hi link ngxBoolean Boolean
+hi link ngxDirectiveBlock Statement
+hi link ngxDirectiveImportant Type
+hi link ngxDirectiveControl Keyword
+hi link ngxDirectiveError Constant
+hi link ngxDirectiveDeprecated Error
+hi link ngxDirective Identifier
+hi link ngxDirectiveThirdParty Special
+
+let b:current_syntax = "nginx"
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/html/50x.html b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/html/50x.html
new file mode 100644
index 00000000000..f60f5e72d17
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/html/50x.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Error</title>
+<style>
+ body {
+ width: 35em;
+ margin: 0 auto;
+ font-family: Tahoma, Verdana, Arial, sans-serif;
+ }
+</style>
+</head>
+<body>
+<h1>An error occurred.</h1>
+<p>Sorry, the page you are looking for is currently unavailable.<br/>
+Please try again later.</p>
+<p>If you are the system administrator of this resource then you should check
+the <a href="http://nginx.org/r/error_log">error log</a> for details.</p>
+<p><em>Faithfully yours, nginx.</em></p>
+</body>
+</html>
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/html/index.html b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/html/index.html
new file mode 100644
index 00000000000..2ca3b9543c0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/html/index.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Welcome to nginx!</title>
+<style>
+ body {
+ width: 35em;
+ margin: 0 auto;
+ font-family: Tahoma, Verdana, Arial, sans-serif;
+ }
+</style>
+</head>
+<body>
+<h1>Welcome to nginx!</h1>
+<p>If you see this page, the nginx web server is successfully installed and
+working. Further configuration is required.</p>
+
+<p>For online documentation and support please refer to
+<a href="http://nginx.org/">nginx.org</a>.<br/>
+Commercial support is available at
+<a href="http://nginx.com/">nginx.com</a>.</p>
+
+<p><em>Thank you for using nginx.</em></p>
+</body>
+</html>
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/man/nginx.8 b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/man/nginx.8
new file mode 100644
index 00000000000..f119a2327a8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/man/nginx.8
@@ -0,0 +1,202 @@
+.\"
+.\" Copyright (C) 2010 Sergey A. Osokin
+.\" Copyright (C) Nginx, Inc.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"
+.Dd March 6, 2012
+.Dt NGINX 8
+.Os
+.Sh NAME
+.Nm nginx
+.Nd "HTTP and reverse proxy server, mail proxy server"
+.Sh SYNOPSIS
+.Nm
+.Op Fl ?hqtVv
+.Op Fl c Ar file
+.Op Fl g Ar directives
+.Op Fl p Ar prefix
+.Op Fl s Ar signal
+.Sh DESCRIPTION
+.Nm
+(pronounced
+.Dq engine x )
+is an HTTP and reverse proxy server, as well as a mail proxy server.
+It is known for its high performance, stability, rich feature set, simple
+configuration, and low resource consumption.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl d Ar directives"
+.It Fl ?\& , h
+Print help.
+.It Fl c Ar file
+Use an alternative configuration
+.Ar file .
+.It Fl g Ar directives
+Set global configuration directives.
+See
+.Sx EXAMPLES
+for details.
+.It Fl p Ar prefix
+Set the prefix path.
+The default value is
+.Pa %%PREFIX%% .
+.It Fl q
+Suppress non-error messages during configuration testing.
+.It Fl s Ar signal
+Send a signal to the master process.
+The argument
+.Ar signal
+can be one of:
+.Cm stop , quit , reopen , reload .
+The following table shows the corresponding system signals:
+.Pp
+.Bl -tag -width ".Cm reopen" -compact
+.It Cm stop
+.Dv SIGTERM
+.It Cm quit
+.Dv SIGQUIT
+.It Cm reopen
+.Dv SIGUSR1
+.It Cm reload
+.Dv SIGHUP
+.El
+.It Fl t
+Do not run, just test the configuration file.
+.Nm
+checks the configuration file syntax and then tries to open files
+referenced in the configuration file.
+.It Fl V
+Print the
+.Nm
+version, compiler version, and
+.Pa configure
+script parameters.
+.It Fl v
+Print the
+.Nm
+version.
+.El
+.Sh SIGNALS
+The master process of
+.Nm
+can handle the following signals:
+.Pp
+.Bl -tag -width ".Dv SIGINT , SIGTERM" -compact
+.It Dv SIGINT , SIGTERM
+Shut down quickly.
+.It Dv SIGHUP
+Reload configuration, start the new worker process with a new
+configuration, and gracefully shut down old worker processes.
+.It Dv SIGQUIT
+Shut down gracefully.
+.It Dv SIGUSR1
+Reopen log files.
+.It Dv SIGUSR2
+Upgrade the
+.Nm
+executable on the fly.
+.It Dv SIGWINCH
+Shut down worker processes gracefully.
+.El
+.Pp
+While there is no need to explicitly control worker processes normally,
+they support some signals too:
+.Pp
+.Bl -tag -width ".Dv SIGINT , SIGTERM" -compact
+.It Dv SIGTERM
+Shut down quickly.
+.It Dv SIGQUIT
+Shut down gracefully.
+.It Dv SIGUSR1
+Reopen log files.
+.El
+.Sh DEBUGGING LOG
+To enable a debugging log, reconfigure
+.Nm
+to build with debugging:
+.Pp
+.Dl "./configure --with-debug ..."
+.Pp
+and then set the
+.Cm debug
+level of the
+.Va error_log :
+.Pp
+.Dl "error_log /path/to/log debug;"
+.Pp
+It is also possible to enable the debugging for a particular IP address:
+.Bd -literal -offset indent
+events {
+ debug_connection 127.0.0.1;
+}
+.Ed
+.Sh ENVIRONMENT
+The
+.Ev NGINX
+environment variable is used internally by
+.Nm
+and should not be set directly by the user.
+.Sh FILES
+.Bl -tag -width indent
+.It Pa %%PID_PATH%%
+Contains the process ID of
+.Nm .
+The contents of this file are not sensitive, so it can be world-readable.
+.It Pa %%CONF_PATH%%
+The main configuration file.
+.It Pa %%ERROR_LOG_PATH%%
+Error log file.
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success, or 1 if the command fails.
+.Sh EXAMPLES
+Test configuration file
+.Pa ~/mynginx.conf
+with global directives for PID and quantity of worker processes:
+.Bd -literal -offset indent
+nginx -t -c ~/mynginx.conf \e
+ -g "pid /var/run/mynginx.pid; worker_processes 2;"
+.Ed
+.Sh SEE ALSO
+.\"Xr nginx.conf 5
+.\"Pp
+Documentation at
+.Pa http://nginx.org/en/docs/ .
+.Pp
+For questions and technical support, please refer to
+.Pa http://nginx.org/en/support.html .
+.Sh HISTORY
+Development of
+.Nm
+started in 2002, with the first public release on October 4, 2004.
+.Sh AUTHORS
+.An -nosplit
+.An Igor Sysoev Aq igor@sysoev.ru .
+.Pp
+This manual page was originally written by
+.An Sergey A. Osokin Aq osa@FreeBSD.org.ru
+as a result of compiling many
+.Nm
+documents from all over the world.
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_aio_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_aio_module.c
new file mode 100644
index 00000000000..c881319d179
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_aio_module.c
@@ -0,0 +1,171 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+extern ngx_event_module_t ngx_kqueue_module_ctx;
+
+
+static ngx_int_t ngx_aio_init(ngx_cycle_t *cycle, ngx_msec_t timer);
+static void ngx_aio_done(ngx_cycle_t *cycle);
+static ngx_int_t ngx_aio_add_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_aio_del_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_aio_del_connection(ngx_connection_t *c, ngx_uint_t flags);
+static ngx_int_t ngx_aio_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+ ngx_uint_t flags);
+
+
+ngx_os_io_t ngx_os_aio = {
+ ngx_aio_read,
+ ngx_aio_read_chain,
+ NULL,
+ ngx_aio_write,
+ ngx_aio_write_chain,
+ 0
+};
+
+
+static ngx_str_t aio_name = ngx_string("aio");
+
+ngx_event_module_t ngx_aio_module_ctx = {
+ &aio_name,
+ NULL, /* create configuration */
+ NULL, /* init configuration */
+
+ {
+ ngx_aio_add_event, /* add an event */
+ ngx_aio_del_event, /* delete an event */
+ NULL, /* enable an event */
+ NULL, /* disable an event */
+ NULL, /* add an connection */
+ ngx_aio_del_connection, /* delete an connection */
+ NULL, /* process the changes */
+ ngx_aio_process_events, /* process the events */
+ ngx_aio_init, /* init the events */
+ ngx_aio_done /* done the events */
+ }
+
+};
+
+ngx_module_t ngx_aio_module = {
+ NGX_MODULE_V1,
+ &ngx_aio_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_EVENT_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+#if (NGX_HAVE_KQUEUE)
+
+static ngx_int_t
+ngx_aio_init(ngx_cycle_t *cycle, ngx_msec_t timer)
+{
+ if (ngx_kqueue_module_ctx.actions.init(cycle, timer) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ ngx_io = ngx_os_aio;
+
+ ngx_event_flags = NGX_USE_AIO_EVENT;
+ ngx_event_actions = ngx_aio_module_ctx.actions;
+
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_aio_done(ngx_cycle_t *cycle)
+{
+ ngx_kqueue_module_ctx.actions.done(cycle);
+}
+
+
+/* the event adding and deleting are needed for the listening sockets */
+
+static ngx_int_t
+ngx_aio_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ return ngx_kqueue_module_ctx.actions.add(ev, event, flags);
+}
+
+
+static ngx_int_t
+ngx_aio_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ return ngx_kqueue_module_ctx.actions.del(ev, event, flags);
+}
+
+
+static ngx_int_t
+ngx_aio_del_connection(ngx_connection_t *c, ngx_uint_t flags)
+{
+ int rc;
+
+ if (c->read->active == 0 && c->write->active == 0) {
+ return NGX_OK;
+ }
+
+ if (flags & NGX_CLOSE_EVENT) {
+ return NGX_OK;
+ }
+
+ rc = aio_cancel(c->fd, NULL);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_cancel: %d", rc);
+
+ if (rc == AIO_CANCELED) {
+ c->read->active = 0;
+ c->write->active = 0;
+ return NGX_OK;
+ }
+
+ if (rc == AIO_ALLDONE) {
+ c->read->active = 0;
+ c->write->active = 0;
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "aio_cancel() returned AIO_ALLDONE");
+ return NGX_OK;
+ }
+
+ if (rc == -1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
+ "aio_cancel() failed");
+ return NGX_ERROR;
+ }
+
+ if (rc == AIO_NOTCANCELED) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "aio_cancel() returned AIO_NOTCANCELED");
+
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_aio_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
+{
+ return ngx_kqueue_module_ctx.actions.process_events(cycle, timer, flags);
+}
+
+#endif /* NGX_HAVE_KQUEUE */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_devpoll_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_devpoll_module.c
new file mode 100644
index 00000000000..0506103e616
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_devpoll_module.c
@@ -0,0 +1,575 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if (NGX_TEST_BUILD_DEVPOLL)
+
+/* Solaris declarations */
+
+#define POLLREMOVE 0x0800
+#define DP_POLL 0xD001
+#define DP_ISPOLLED 0xD002
+
+struct dvpoll {
+ struct pollfd *dp_fds;
+ int dp_nfds;
+ int dp_timeout;
+};
+
+#endif
+
+
+typedef struct {
+ ngx_uint_t changes;
+ ngx_uint_t events;
+} ngx_devpoll_conf_t;
+
+
+static ngx_int_t ngx_devpoll_init(ngx_cycle_t *cycle, ngx_msec_t timer);
+static void ngx_devpoll_done(ngx_cycle_t *cycle);
+static ngx_int_t ngx_devpoll_add_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_devpoll_del_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_devpoll_set_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_devpoll_process_events(ngx_cycle_t *cycle,
+ ngx_msec_t timer, ngx_uint_t flags);
+
+static void *ngx_devpoll_create_conf(ngx_cycle_t *cycle);
+static char *ngx_devpoll_init_conf(ngx_cycle_t *cycle, void *conf);
+
+static int dp = -1;
+static struct pollfd *change_list, *event_list;
+static ngx_uint_t nchanges, max_changes, nevents;
+
+static ngx_event_t **change_index;
+
+
+static ngx_str_t devpoll_name = ngx_string("/dev/poll");
+
+static ngx_command_t ngx_devpoll_commands[] = {
+
+ { ngx_string("devpoll_changes"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_devpoll_conf_t, changes),
+ NULL },
+
+ { ngx_string("devpoll_events"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_devpoll_conf_t, events),
+ NULL },
+
+ ngx_null_command
+};
+
+
+ngx_event_module_t ngx_devpoll_module_ctx = {
+ &devpoll_name,
+ ngx_devpoll_create_conf, /* create configuration */
+ ngx_devpoll_init_conf, /* init configuration */
+
+ {
+ ngx_devpoll_add_event, /* add an event */
+ ngx_devpoll_del_event, /* delete an event */
+ ngx_devpoll_add_event, /* enable an event */
+ ngx_devpoll_del_event, /* disable an event */
+ NULL, /* add an connection */
+ NULL, /* delete an connection */
+ NULL, /* process the changes */
+ ngx_devpoll_process_events, /* process the events */
+ ngx_devpoll_init, /* init the events */
+ ngx_devpoll_done, /* done the events */
+ }
+
+};
+
+ngx_module_t ngx_devpoll_module = {
+ NGX_MODULE_V1,
+ &ngx_devpoll_module_ctx, /* module context */
+ ngx_devpoll_commands, /* module directives */
+ NGX_EVENT_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_devpoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
+{
+ size_t n;
+ ngx_devpoll_conf_t *dpcf;
+
+ dpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_devpoll_module);
+
+ if (dp == -1) {
+ dp = open("/dev/poll", O_RDWR);
+
+ if (dp == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "open(/dev/poll) failed");
+ return NGX_ERROR;
+ }
+ }
+
+ if (max_changes < dpcf->changes) {
+ if (nchanges) {
+ n = nchanges * sizeof(struct pollfd);
+ if (write(dp, change_list, n) != (ssize_t) n) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "write(/dev/poll) failed");
+ return NGX_ERROR;
+ }
+
+ nchanges = 0;
+ }
+
+ if (change_list) {
+ ngx_free(change_list);
+ }
+
+ change_list = ngx_alloc(sizeof(struct pollfd) * dpcf->changes,
+ cycle->log);
+ if (change_list == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (change_index) {
+ ngx_free(change_index);
+ }
+
+ change_index = ngx_alloc(sizeof(ngx_event_t *) * dpcf->changes,
+ cycle->log);
+ if (change_index == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ max_changes = dpcf->changes;
+
+ if (nevents < dpcf->events) {
+ if (event_list) {
+ ngx_free(event_list);
+ }
+
+ event_list = ngx_alloc(sizeof(struct pollfd) * dpcf->events,
+ cycle->log);
+ if (event_list == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ nevents = dpcf->events;
+
+ ngx_io = ngx_os_io;
+
+ ngx_event_actions = ngx_devpoll_module_ctx.actions;
+
+ ngx_event_flags = NGX_USE_LEVEL_EVENT|NGX_USE_FD_EVENT;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_devpoll_done(ngx_cycle_t *cycle)
+{
+ if (close(dp) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "close(/dev/poll) failed");
+ }
+
+ dp = -1;
+
+ ngx_free(change_list);
+ ngx_free(event_list);
+ ngx_free(change_index);
+
+ change_list = NULL;
+ event_list = NULL;
+ change_index = NULL;
+ max_changes = 0;
+ nchanges = 0;
+ nevents = 0;
+}
+
+
+static ngx_int_t
+ngx_devpoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+#if (NGX_DEBUG)
+ ngx_connection_t *c;
+#endif
+
+#if (NGX_READ_EVENT != POLLIN)
+ event = (event == NGX_READ_EVENT) ? POLLIN : POLLOUT;
+#endif
+
+#if (NGX_DEBUG)
+ c = ev->data;
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "devpoll add event: fd:%d ev:%04Xi", c->fd, event);
+#endif
+
+ ev->active = 1;
+
+ return ngx_devpoll_set_event(ev, event, 0);
+}
+
+
+static ngx_int_t
+ngx_devpoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ ngx_event_t *e;
+ ngx_connection_t *c;
+
+ c = ev->data;
+
+#if (NGX_READ_EVENT != POLLIN)
+ event = (event == NGX_READ_EVENT) ? POLLIN : POLLOUT;
+#endif
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "devpoll del event: fd:%d ev:%04Xi", c->fd, event);
+
+ if (ngx_devpoll_set_event(ev, POLLREMOVE, flags) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ ev->active = 0;
+
+ if (flags & NGX_CLOSE_EVENT) {
+ e = (event == POLLIN) ? c->write : c->read;
+
+ if (e) {
+ e->active = 0;
+ }
+
+ return NGX_OK;
+ }
+
+ /* restore the pair event if it exists */
+
+ if (event == POLLIN) {
+ e = c->write;
+ event = POLLOUT;
+
+ } else {
+ e = c->read;
+ event = POLLIN;
+ }
+
+ if (e && e->active) {
+ return ngx_devpoll_set_event(e, event, 0);
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_devpoll_set_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ size_t n;
+ ngx_connection_t *c;
+
+ c = ev->data;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "devpoll fd:%d ev:%04Xi fl:%04Xi", c->fd, event, flags);
+
+ if (nchanges >= max_changes) {
+ ngx_log_error(NGX_LOG_WARN, ev->log, 0,
+ "/dev/pool change list is filled up");
+
+ n = nchanges * sizeof(struct pollfd);
+ if (write(dp, change_list, n) != (ssize_t) n) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
+ "write(/dev/poll) failed");
+ return NGX_ERROR;
+ }
+
+ nchanges = 0;
+ }
+
+ change_list[nchanges].fd = c->fd;
+ change_list[nchanges].events = (short) event;
+ change_list[nchanges].revents = 0;
+
+ change_index[nchanges] = ev;
+ ev->index = nchanges;
+
+ nchanges++;
+
+ if (flags & NGX_CLOSE_EVENT) {
+ n = nchanges * sizeof(struct pollfd);
+ if (write(dp, change_list, n) != (ssize_t) n) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
+ "write(/dev/poll) failed");
+ return NGX_ERROR;
+ }
+
+ nchanges = 0;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_devpoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+ ngx_uint_t flags)
+{
+ int events, revents, rc;
+ size_t n;
+ ngx_fd_t fd;
+ ngx_err_t err;
+ ngx_int_t i;
+ ngx_uint_t level, instance;
+ ngx_event_t *rev, *wev, **queue;
+ ngx_connection_t *c;
+ struct pollfd pfd;
+ struct dvpoll dvp;
+
+ /* NGX_TIMER_INFINITE == INFTIM */
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "devpoll timer: %M", timer);
+
+ if (nchanges) {
+ n = nchanges * sizeof(struct pollfd);
+ if (write(dp, change_list, n) != (ssize_t) n) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "write(/dev/poll) failed");
+ return NGX_ERROR;
+ }
+
+ nchanges = 0;
+ }
+
+ dvp.dp_fds = event_list;
+ dvp.dp_nfds = (int) nevents;
+ dvp.dp_timeout = timer;
+ events = ioctl(dp, DP_POLL, &dvp);
+
+ err = (events == -1) ? ngx_errno : 0;
+
+ if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
+ ngx_time_update();
+ }
+
+ if (err) {
+ if (err == NGX_EINTR) {
+
+ if (ngx_event_timer_alarm) {
+ ngx_event_timer_alarm = 0;
+ return NGX_OK;
+ }
+
+ level = NGX_LOG_INFO;
+
+ } else {
+ level = NGX_LOG_ALERT;
+ }
+
+ ngx_log_error(level, cycle->log, err, "ioctl(DP_POLL) failed");
+ return NGX_ERROR;
+ }
+
+ if (events == 0) {
+ if (timer != NGX_TIMER_INFINITE) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "ioctl(DP_POLL) returned no events without timeout");
+ return NGX_ERROR;
+ }
+
+ ngx_mutex_lock(ngx_posted_events_mutex);
+
+ for (i = 0; i < events; i++) {
+
+ fd = event_list[i].fd;
+ revents = event_list[i].revents;
+
+ c = ngx_cycle->files[fd];
+
+ if (c == NULL || c->fd == -1) {
+
+ pfd.fd = fd;
+ pfd.events = 0;
+ pfd.revents = 0;
+
+ rc = ioctl(dp, DP_ISPOLLED, &pfd);
+
+ switch (rc) {
+
+ case -1:
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "ioctl(DP_ISPOLLED) failed for socket %d, event %04Xd",
+ fd, revents);
+ break;
+
+ case 0:
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "phantom event %04Xd for closed and removed socket %d",
+ revents, fd);
+ break;
+
+ default:
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "unexpected event %04Xd for closed and removed socket %d, ",
+ "ioctl(DP_ISPOLLED) returned rc:%d, fd:%d, event %04Xd",
+ revents, fd, rc, pfd.fd, pfd.revents);
+
+ pfd.fd = fd;
+ pfd.events = POLLREMOVE;
+ pfd.revents = 0;
+
+ if (write(dp, &pfd, sizeof(struct pollfd))
+ != (ssize_t) sizeof(struct pollfd))
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "write(/dev/poll) for %d failed", fd);
+ }
+
+ if (close(fd) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "close(%d) failed", fd);
+ }
+
+ break;
+ }
+
+ continue;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "devpoll: fd:%d, ev:%04Xd, rev:%04Xd",
+ fd, event_list[i].events, revents);
+
+ if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "ioctl(DP_POLL) error fd:%d ev:%04Xd rev:%04Xd",
+ fd, event_list[i].events, revents);
+ }
+
+ if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "strange ioctl(DP_POLL) events "
+ "fd:%d ev:%04Xd rev:%04Xd",
+ fd, event_list[i].events, revents);
+ }
+
+ if ((revents & (POLLERR|POLLHUP|POLLNVAL))
+ && (revents & (POLLIN|POLLOUT)) == 0)
+ {
+ /*
+ * if the error events were returned without POLLIN or POLLOUT,
+ * then add these flags to handle the events at least in one
+ * active handler
+ */
+
+ revents |= POLLIN|POLLOUT;
+ }
+
+ rev = c->read;
+
+ if ((revents & POLLIN) && rev->active) {
+
+ if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) {
+ rev->posted_ready = 1;
+
+ } else {
+ rev->ready = 1;
+ }
+
+ if (flags & NGX_POST_EVENTS) {
+ queue = (ngx_event_t **) (rev->accept ?
+ &ngx_posted_accept_events : &ngx_posted_events);
+
+ ngx_locked_post_event(rev, queue);
+
+ } else {
+ instance = rev->instance;
+
+ rev->handler(rev);
+
+ if (c->fd == -1 || rev->instance != instance) {
+ continue;
+ }
+ }
+ }
+
+ wev = c->write;
+
+ if ((revents & POLLOUT) && wev->active) {
+
+ if (flags & NGX_POST_THREAD_EVENTS) {
+ wev->posted_ready = 1;
+
+ } else {
+ wev->ready = 1;
+ }
+
+ if (flags & NGX_POST_EVENTS) {
+ ngx_locked_post_event(wev, &ngx_posted_events);
+
+ } else {
+ wev->handler(wev);
+ }
+ }
+ }
+
+ ngx_mutex_unlock(ngx_posted_events_mutex);
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_devpoll_create_conf(ngx_cycle_t *cycle)
+{
+ ngx_devpoll_conf_t *dpcf;
+
+ dpcf = ngx_palloc(cycle->pool, sizeof(ngx_devpoll_conf_t));
+ if (dpcf == NULL) {
+ return NULL;
+ }
+
+ dpcf->changes = NGX_CONF_UNSET;
+ dpcf->events = NGX_CONF_UNSET;
+
+ return dpcf;
+}
+
+
+static char *
+ngx_devpoll_init_conf(ngx_cycle_t *cycle, void *conf)
+{
+ ngx_devpoll_conf_t *dpcf = conf;
+
+ ngx_conf_init_uint_value(dpcf->changes, 32);
+ ngx_conf_init_uint_value(dpcf->events, 32);
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_epoll_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_epoll_module.c
new file mode 100644
index 00000000000..a098c1c00c7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_epoll_module.c
@@ -0,0 +1,845 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if (NGX_TEST_BUILD_EPOLL)
+
+/* epoll declarations */
+
+#define EPOLLIN 0x001
+#define EPOLLPRI 0x002
+#define EPOLLOUT 0x004
+#define EPOLLRDNORM 0x040
+#define EPOLLRDBAND 0x080
+#define EPOLLWRNORM 0x100
+#define EPOLLWRBAND 0x200
+#define EPOLLMSG 0x400
+#define EPOLLERR 0x008
+#define EPOLLHUP 0x010
+
+#define EPOLLRDHUP 0x2000
+
+#define EPOLLET 0x80000000
+#define EPOLLONESHOT 0x40000000
+
+#define EPOLL_CTL_ADD 1
+#define EPOLL_CTL_DEL 2
+#define EPOLL_CTL_MOD 3
+
+typedef union epoll_data {
+ void *ptr;
+ int fd;
+ uint32_t u32;
+ uint64_t u64;
+} epoll_data_t;
+
+struct epoll_event {
+ uint32_t events;
+ epoll_data_t data;
+};
+
+
+int epoll_create(int size);
+
+int epoll_create(int size)
+{
+ return -1;
+}
+
+
+int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
+
+int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
+{
+ return -1;
+}
+
+
+int epoll_wait(int epfd, struct epoll_event *events, int nevents, int timeout);
+
+int epoll_wait(int epfd, struct epoll_event *events, int nevents, int timeout)
+{
+ return -1;
+}
+
+#if (NGX_HAVE_FILE_AIO)
+
+#define SYS_io_setup 245
+#define SYS_io_destroy 246
+#define SYS_io_getevents 247
+#define SYS_eventfd 323
+
+typedef u_int aio_context_t;
+
+struct io_event {
+ uint64_t data; /* the data field from the iocb */
+ uint64_t obj; /* what iocb this event came from */
+ int64_t res; /* result code for this event */
+ int64_t res2; /* secondary result */
+};
+
+
+#endif
+#endif
+
+
+typedef struct {
+ ngx_uint_t events;
+ ngx_uint_t aio_requests;
+} ngx_epoll_conf_t;
+
+
+static ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer);
+static void ngx_epoll_done(ngx_cycle_t *cycle);
+static ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_epoll_add_connection(ngx_connection_t *c);
+static ngx_int_t ngx_epoll_del_connection(ngx_connection_t *c,
+ ngx_uint_t flags);
+static ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+ ngx_uint_t flags);
+
+#if (NGX_HAVE_FILE_AIO)
+static void ngx_epoll_eventfd_handler(ngx_event_t *ev);
+#endif
+
+static void *ngx_epoll_create_conf(ngx_cycle_t *cycle);
+static char *ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf);
+
+static int ep = -1;
+static struct epoll_event *event_list;
+static ngx_uint_t nevents;
+
+#if (NGX_HAVE_FILE_AIO)
+
+int ngx_eventfd = -1;
+aio_context_t ngx_aio_ctx = 0;
+
+static ngx_event_t ngx_eventfd_event;
+static ngx_connection_t ngx_eventfd_conn;
+
+#endif
+
+static ngx_str_t epoll_name = ngx_string("epoll");
+
+static ngx_command_t ngx_epoll_commands[] = {
+
+ { ngx_string("epoll_events"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_epoll_conf_t, events),
+ NULL },
+
+ { ngx_string("worker_aio_requests"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_epoll_conf_t, aio_requests),
+ NULL },
+
+ ngx_null_command
+};
+
+
+ngx_event_module_t ngx_epoll_module_ctx = {
+ &epoll_name,
+ ngx_epoll_create_conf, /* create configuration */
+ ngx_epoll_init_conf, /* init configuration */
+
+ {
+ ngx_epoll_add_event, /* add an event */
+ ngx_epoll_del_event, /* delete an event */
+ ngx_epoll_add_event, /* enable an event */
+ ngx_epoll_del_event, /* disable an event */
+ ngx_epoll_add_connection, /* add an connection */
+ ngx_epoll_del_connection, /* delete an connection */
+ NULL, /* process the changes */
+ ngx_epoll_process_events, /* process the events */
+ ngx_epoll_init, /* init the events */
+ ngx_epoll_done, /* done the events */
+ }
+};
+
+ngx_module_t ngx_epoll_module = {
+ NGX_MODULE_V1,
+ &ngx_epoll_module_ctx, /* module context */
+ ngx_epoll_commands, /* module directives */
+ NGX_EVENT_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+#if (NGX_HAVE_FILE_AIO)
+
+/*
+ * We call io_setup(), io_destroy() io_submit(), and io_getevents() directly
+ * as syscalls instead of libaio usage, because the library header file
+ * supports eventfd() since 0.3.107 version only.
+ */
+
+static int
+io_setup(u_int nr_reqs, aio_context_t *ctx)
+{
+ return syscall(SYS_io_setup, nr_reqs, ctx);
+}
+
+
+static int
+io_destroy(aio_context_t ctx)
+{
+ return syscall(SYS_io_destroy, ctx);
+}
+
+
+static int
+io_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events,
+ struct timespec *tmo)
+{
+ return syscall(SYS_io_getevents, ctx, min_nr, nr, events, tmo);
+}
+
+
+static void
+ngx_epoll_aio_init(ngx_cycle_t *cycle, ngx_epoll_conf_t *epcf)
+{
+ int n;
+ struct epoll_event ee;
+
+#if (NGX_HAVE_SYS_EVENTFD_H)
+ ngx_eventfd = eventfd(0, 0);
+#else
+ ngx_eventfd = syscall(SYS_eventfd, 0);
+#endif
+
+ if (ngx_eventfd == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "eventfd() failed");
+ ngx_file_aio = 0;
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "eventfd: %d", ngx_eventfd);
+
+ n = 1;
+
+ if (ioctl(ngx_eventfd, FIONBIO, &n) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "ioctl(eventfd, FIONBIO) failed");
+ goto failed;
+ }
+
+ if (io_setup(epcf->aio_requests, &ngx_aio_ctx) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "io_setup() failed");
+ goto failed;
+ }
+
+ ngx_eventfd_event.data = &ngx_eventfd_conn;
+ ngx_eventfd_event.handler = ngx_epoll_eventfd_handler;
+ ngx_eventfd_event.log = cycle->log;
+ ngx_eventfd_event.active = 1;
+ ngx_eventfd_conn.fd = ngx_eventfd;
+ ngx_eventfd_conn.read = &ngx_eventfd_event;
+ ngx_eventfd_conn.log = cycle->log;
+
+ ee.events = EPOLLIN|EPOLLET;
+ ee.data.ptr = &ngx_eventfd_conn;
+
+ if (epoll_ctl(ep, EPOLL_CTL_ADD, ngx_eventfd, &ee) != -1) {
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "epoll_ctl(EPOLL_CTL_ADD, eventfd) failed");
+
+ if (io_destroy(ngx_aio_ctx) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "io_destroy() failed");
+ }
+
+failed:
+
+ if (close(ngx_eventfd) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "eventfd close() failed");
+ }
+
+ ngx_eventfd = -1;
+ ngx_aio_ctx = 0;
+ ngx_file_aio = 0;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
+{
+ ngx_epoll_conf_t *epcf;
+
+ epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);
+
+ if (ep == -1) {
+ ep = epoll_create(cycle->connection_n / 2);
+
+ if (ep == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "epoll_create() failed");
+ return NGX_ERROR;
+ }
+
+#if (NGX_HAVE_FILE_AIO)
+
+ ngx_epoll_aio_init(cycle, epcf);
+
+#endif
+ }
+
+ if (nevents < epcf->events) {
+ if (event_list) {
+ ngx_free(event_list);
+ }
+
+ event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events,
+ cycle->log);
+ if (event_list == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ nevents = epcf->events;
+
+ ngx_io = ngx_os_io;
+
+ ngx_event_actions = ngx_epoll_module_ctx.actions;
+
+#if (NGX_HAVE_CLEAR_EVENT)
+ ngx_event_flags = NGX_USE_CLEAR_EVENT
+#else
+ ngx_event_flags = NGX_USE_LEVEL_EVENT
+#endif
+ |NGX_USE_GREEDY_EVENT
+ |NGX_USE_EPOLL_EVENT;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_epoll_done(ngx_cycle_t *cycle)
+{
+ if (close(ep) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "epoll close() failed");
+ }
+
+ ep = -1;
+
+#if (NGX_HAVE_FILE_AIO)
+
+ if (ngx_eventfd != -1) {
+
+ if (io_destroy(ngx_aio_ctx) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "io_destroy() failed");
+ }
+
+ if (close(ngx_eventfd) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "eventfd close() failed");
+ }
+
+ ngx_eventfd = -1;
+ }
+
+ ngx_aio_ctx = 0;
+
+#endif
+
+ ngx_free(event_list);
+
+ event_list = NULL;
+ nevents = 0;
+}
+
+
+static ngx_int_t
+ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ int op;
+ uint32_t events, prev;
+ ngx_event_t *e;
+ ngx_connection_t *c;
+ struct epoll_event ee;
+
+ c = ev->data;
+
+ events = (uint32_t) event;
+
+ if (event == NGX_READ_EVENT) {
+ e = c->write;
+ prev = EPOLLOUT;
+#if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP)
+ events = EPOLLIN|EPOLLRDHUP;
+#endif
+
+ } else {
+ e = c->read;
+ prev = EPOLLIN|EPOLLRDHUP;
+#if (NGX_WRITE_EVENT != EPOLLOUT)
+ events = EPOLLOUT;
+#endif
+ }
+
+ if (e->active) {
+ op = EPOLL_CTL_MOD;
+ events |= prev;
+
+ } else {
+ op = EPOLL_CTL_ADD;
+ }
+
+ ee.events = events | (uint32_t) flags;
+ ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "epoll add event: fd:%d op:%d ev:%08XD",
+ c->fd, op, ee.events);
+
+ if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
+ "epoll_ctl(%d, %d) failed", op, c->fd);
+ return NGX_ERROR;
+ }
+
+ ev->active = 1;
+#if 0
+ ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ int op;
+ uint32_t prev;
+ ngx_event_t *e;
+ ngx_connection_t *c;
+ struct epoll_event ee;
+
+ /*
+ * when the file descriptor is closed, the epoll automatically deletes
+ * it from its queue, so we do not need to delete explicitly the event
+ * before the closing the file descriptor
+ */
+
+ if (flags & NGX_CLOSE_EVENT) {
+ ev->active = 0;
+ return NGX_OK;
+ }
+
+ c = ev->data;
+
+ if (event == NGX_READ_EVENT) {
+ e = c->write;
+ prev = EPOLLOUT;
+
+ } else {
+ e = c->read;
+ prev = EPOLLIN|EPOLLRDHUP;
+ }
+
+ if (e->active) {
+ op = EPOLL_CTL_MOD;
+ ee.events = prev | (uint32_t) flags;
+ ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);
+
+ } else {
+ op = EPOLL_CTL_DEL;
+ ee.events = 0;
+ ee.data.ptr = NULL;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "epoll del event: fd:%d op:%d ev:%08XD",
+ c->fd, op, ee.events);
+
+ if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
+ "epoll_ctl(%d, %d) failed", op, c->fd);
+ return NGX_ERROR;
+ }
+
+ ev->active = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_epoll_add_connection(ngx_connection_t *c)
+{
+ struct epoll_event ee;
+
+ ee.events = EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP;
+ ee.data.ptr = (void *) ((uintptr_t) c | c->read->instance);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "epoll add connection: fd:%d ev:%08XD", c->fd, ee.events);
+
+ if (epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
+ "epoll_ctl(EPOLL_CTL_ADD, %d) failed", c->fd);
+ return NGX_ERROR;
+ }
+
+ c->read->active = 1;
+ c->write->active = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_epoll_del_connection(ngx_connection_t *c, ngx_uint_t flags)
+{
+ int op;
+ struct epoll_event ee;
+
+ /*
+ * when the file descriptor is closed the epoll automatically deletes
+ * it from its queue so we do not need to delete explicitly the event
+ * before the closing the file descriptor
+ */
+
+ if (flags & NGX_CLOSE_EVENT) {
+ c->read->active = 0;
+ c->write->active = 0;
+ return NGX_OK;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "epoll del connection: fd:%d", c->fd);
+
+ op = EPOLL_CTL_DEL;
+ ee.events = 0;
+ ee.data.ptr = NULL;
+
+ if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
+ "epoll_ctl(%d, %d) failed", op, c->fd);
+ return NGX_ERROR;
+ }
+
+ c->read->active = 0;
+ c->write->active = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
+{
+ int events;
+ uint32_t revents;
+ ngx_int_t instance, i;
+ ngx_uint_t level;
+ ngx_err_t err;
+ ngx_event_t *rev, *wev, **queue;
+ ngx_connection_t *c;
+
+ /* NGX_TIMER_INFINITE == INFTIM */
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "epoll timer: %M", timer);
+
+ events = epoll_wait(ep, event_list, (int) nevents, timer);
+
+ err = (events == -1) ? ngx_errno : 0;
+
+ if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
+ ngx_time_update();
+ }
+
+ if (err) {
+ if (err == NGX_EINTR) {
+
+ if (ngx_event_timer_alarm) {
+ ngx_event_timer_alarm = 0;
+ return NGX_OK;
+ }
+
+ level = NGX_LOG_INFO;
+
+ } else {
+ level = NGX_LOG_ALERT;
+ }
+
+ ngx_log_error(level, cycle->log, err, "epoll_wait() failed");
+ return NGX_ERROR;
+ }
+
+ if (events == 0) {
+ if (timer != NGX_TIMER_INFINITE) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "epoll_wait() returned no events without timeout");
+ return NGX_ERROR;
+ }
+
+ ngx_mutex_lock(ngx_posted_events_mutex);
+
+ for (i = 0; i < events; i++) {
+ c = event_list[i].data.ptr;
+
+ instance = (uintptr_t) c & 1;
+ c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);
+
+ rev = c->read;
+
+ if (c->fd == -1 || rev->instance != instance) {
+
+ /*
+ * the stale event from a file descriptor
+ * that was just closed in this iteration
+ */
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "epoll: stale event %p", c);
+ continue;
+ }
+
+ revents = event_list[i].events;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "epoll: fd:%d ev:%04XD d:%p",
+ c->fd, revents, event_list[i].data.ptr);
+
+ if (revents & (EPOLLERR|EPOLLHUP)) {
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "epoll_wait() error on fd:%d ev:%04XD",
+ c->fd, revents);
+ }
+
+#if 0
+ if (revents & ~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "strange epoll_wait() events fd:%d ev:%04XD",
+ c->fd, revents);
+ }
+#endif
+
+ if ((revents & (EPOLLERR|EPOLLHUP))
+ && (revents & (EPOLLIN|EPOLLOUT)) == 0)
+ {
+ /*
+ * if the error events were returned without EPOLLIN or EPOLLOUT,
+ * then add these flags to handle the events at least in one
+ * active handler
+ */
+
+ revents |= EPOLLIN|EPOLLOUT;
+ }
+
+ if ((revents & EPOLLIN) && rev->active) {
+
+#if (NGX_HAVE_EPOLLRDHUP)
+ if (revents & EPOLLRDHUP) {
+ rev->pending_eof = 1;
+ }
+#endif
+
+ if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) {
+ rev->posted_ready = 1;
+
+ } else {
+ rev->ready = 1;
+ }
+
+ if (flags & NGX_POST_EVENTS) {
+ queue = (ngx_event_t **) (rev->accept ?
+ &ngx_posted_accept_events : &ngx_posted_events);
+
+ ngx_locked_post_event(rev, queue);
+
+ } else {
+ rev->handler(rev);
+ }
+ }
+
+ wev = c->write;
+
+ if ((revents & EPOLLOUT) && wev->active) {
+
+ if (c->fd == -1 || wev->instance != instance) {
+
+ /*
+ * the stale event from a file descriptor
+ * that was just closed in this iteration
+ */
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "epoll: stale event %p", c);
+ continue;
+ }
+
+ if (flags & NGX_POST_THREAD_EVENTS) {
+ wev->posted_ready = 1;
+
+ } else {
+ wev->ready = 1;
+ }
+
+ if (flags & NGX_POST_EVENTS) {
+ ngx_locked_post_event(wev, &ngx_posted_events);
+
+ } else {
+ wev->handler(wev);
+ }
+ }
+ }
+
+ ngx_mutex_unlock(ngx_posted_events_mutex);
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HAVE_FILE_AIO)
+
+static void
+ngx_epoll_eventfd_handler(ngx_event_t *ev)
+{
+ int n, events;
+ long i;
+ uint64_t ready;
+ ngx_err_t err;
+ ngx_event_t *e;
+ ngx_event_aio_t *aio;
+ struct io_event event[64];
+ struct timespec ts;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "eventfd handler");
+
+ n = read(ngx_eventfd, &ready, 8);
+
+ err = ngx_errno;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, "eventfd: %d", n);
+
+ if (n != 8) {
+ if (n == -1) {
+ if (err == NGX_EAGAIN) {
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, ev->log, err, "read(eventfd) failed");
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+ "read(eventfd) returned only %d bytes", n);
+ return;
+ }
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+
+ while (ready) {
+
+ events = io_getevents(ngx_aio_ctx, 1, 64, event, &ts);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "io_getevents: %l", events);
+
+ if (events > 0) {
+ ready -= events;
+
+ for (i = 0; i < events; i++) {
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "io_event: %uXL %uXL %L %L",
+ event[i].data, event[i].obj,
+ event[i].res, event[i].res2);
+
+ e = (ngx_event_t *) (uintptr_t) event[i].data;
+
+ e->complete = 1;
+ e->active = 0;
+ e->ready = 1;
+
+ aio = e->data;
+ aio->res = event[i].res;
+
+ ngx_post_event(e, &ngx_posted_events);
+ }
+
+ continue;
+ }
+
+ if (events == 0) {
+ return;
+ }
+
+ /* events == -1 */
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
+ "io_getevents() failed");
+ return;
+ }
+}
+
+#endif
+
+
+static void *
+ngx_epoll_create_conf(ngx_cycle_t *cycle)
+{
+ ngx_epoll_conf_t *epcf;
+
+ epcf = ngx_palloc(cycle->pool, sizeof(ngx_epoll_conf_t));
+ if (epcf == NULL) {
+ return NULL;
+ }
+
+ epcf->events = NGX_CONF_UNSET;
+ epcf->aio_requests = NGX_CONF_UNSET;
+
+ return epcf;
+}
+
+
+static char *
+ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf)
+{
+ ngx_epoll_conf_t *epcf = conf;
+
+ ngx_conf_init_uint_value(epcf->events, 512);
+ ngx_conf_init_uint_value(epcf->aio_requests, 32);
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_eventport_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_eventport_module.c
new file mode 100644
index 00000000000..5f9cf4e35fb
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_eventport_module.c
@@ -0,0 +1,633 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if (NGX_TEST_BUILD_EVENTPORT)
+
+#define ushort_t u_short
+#define uint_t u_int
+
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+typedef int clockid_t;
+typedef void * timer_t;
+#endif
+
+/* Solaris declarations */
+
+#define PORT_SOURCE_AIO 1
+#define PORT_SOURCE_TIMER 2
+#define PORT_SOURCE_USER 3
+#define PORT_SOURCE_FD 4
+#define PORT_SOURCE_ALERT 5
+#define PORT_SOURCE_MQ 6
+
+#ifndef ETIME
+#define ETIME 64
+#endif
+
+#define SIGEV_PORT 4
+
+typedef struct {
+ int portev_events; /* event data is source specific */
+ ushort_t portev_source; /* event source */
+ ushort_t portev_pad; /* port internal use */
+ uintptr_t portev_object; /* source specific object */
+ void *portev_user; /* user cookie */
+} port_event_t;
+
+typedef struct port_notify {
+ int portnfy_port; /* bind request(s) to port */
+ void *portnfy_user; /* user defined */
+} port_notify_t;
+
+#if (__FreeBSD_version < 700005)
+
+typedef struct itimerspec { /* definition per POSIX.4 */
+ struct timespec it_interval;/* timer period */
+ struct timespec it_value; /* timer expiration */
+} itimerspec_t;
+
+#endif
+
+int port_create(void);
+
+int port_create(void)
+{
+ return -1;
+}
+
+
+int port_associate(int port, int source, uintptr_t object, int events,
+ void *user);
+
+int port_associate(int port, int source, uintptr_t object, int events,
+ void *user)
+{
+ return -1;
+}
+
+
+int port_dissociate(int port, int source, uintptr_t object);
+
+int port_dissociate(int port, int source, uintptr_t object)
+{
+ return -1;
+}
+
+
+int port_getn(int port, port_event_t list[], uint_t max, uint_t *nget,
+ struct timespec *timeout);
+
+int port_getn(int port, port_event_t list[], uint_t max, uint_t *nget,
+ struct timespec *timeout)
+{
+ return -1;
+}
+
+
+int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid);
+
+int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
+{
+ return -1;
+}
+
+
+int timer_settime(timer_t timerid, int flags, const struct itimerspec *value,
+ struct itimerspec *ovalue);
+
+int timer_settime(timer_t timerid, int flags, const struct itimerspec *value,
+ struct itimerspec *ovalue)
+{
+ return -1;
+}
+
+
+int timer_delete(timer_t timerid);
+
+int timer_delete(timer_t timerid)
+{
+ return -1;
+}
+
+#endif
+
+
+typedef struct {
+ ngx_uint_t events;
+} ngx_eventport_conf_t;
+
+
+static ngx_int_t ngx_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer);
+static void ngx_eventport_done(ngx_cycle_t *cycle);
+static ngx_int_t ngx_eventport_add_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_eventport_del_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_eventport_process_events(ngx_cycle_t *cycle,
+ ngx_msec_t timer, ngx_uint_t flags);
+
+static void *ngx_eventport_create_conf(ngx_cycle_t *cycle);
+static char *ngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf);
+
+static int ep = -1;
+static port_event_t *event_list;
+static ngx_uint_t nevents;
+static timer_t event_timer = (timer_t) -1;
+
+static ngx_str_t eventport_name = ngx_string("eventport");
+
+
+static ngx_command_t ngx_eventport_commands[] = {
+
+ { ngx_string("eventport_events"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_eventport_conf_t, events),
+ NULL },
+
+ ngx_null_command
+};
+
+
+ngx_event_module_t ngx_eventport_module_ctx = {
+ &eventport_name,
+ ngx_eventport_create_conf, /* create configuration */
+ ngx_eventport_init_conf, /* init configuration */
+
+ {
+ ngx_eventport_add_event, /* add an event */
+ ngx_eventport_del_event, /* delete an event */
+ ngx_eventport_add_event, /* enable an event */
+ ngx_eventport_del_event, /* disable an event */
+ NULL, /* add an connection */
+ NULL, /* delete an connection */
+ NULL, /* process the changes */
+ ngx_eventport_process_events, /* process the events */
+ ngx_eventport_init, /* init the events */
+ ngx_eventport_done, /* done the events */
+ }
+
+};
+
+ngx_module_t ngx_eventport_module = {
+ NGX_MODULE_V1,
+ &ngx_eventport_module_ctx, /* module context */
+ ngx_eventport_commands, /* module directives */
+ NGX_EVENT_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer)
+{
+ port_notify_t pn;
+ struct itimerspec its;
+ struct sigevent sev;
+ ngx_eventport_conf_t *epcf;
+
+ epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_eventport_module);
+
+ if (ep == -1) {
+ ep = port_create();
+
+ if (ep == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "port_create() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ if (nevents < epcf->events) {
+ if (event_list) {
+ ngx_free(event_list);
+ }
+
+ event_list = ngx_alloc(sizeof(port_event_t) * epcf->events,
+ cycle->log);
+ if (event_list == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ ngx_event_flags = NGX_USE_EVENTPORT_EVENT;
+
+ if (timer) {
+ ngx_memzero(&pn, sizeof(port_notify_t));
+ pn.portnfy_port = ep;
+
+ ngx_memzero(&sev, sizeof(struct sigevent));
+ sev.sigev_notify = SIGEV_PORT;
+#if !(NGX_TEST_BUILD_EVENTPORT)
+ sev.sigev_value.sival_ptr = &pn;
+#endif
+
+ if (timer_create(CLOCK_REALTIME, &sev, &event_timer) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "timer_create() failed");
+ return NGX_ERROR;
+ }
+
+ its.it_interval.tv_sec = timer / 1000;
+ its.it_interval.tv_nsec = (timer % 1000) * 1000000;
+ its.it_value.tv_sec = timer / 1000;
+ its.it_value.tv_nsec = (timer % 1000) * 1000000;
+
+ if (timer_settime(event_timer, 0, &its, NULL) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "timer_settime() failed");
+ return NGX_ERROR;
+ }
+
+ ngx_event_flags |= NGX_USE_TIMER_EVENT;
+ }
+
+ nevents = epcf->events;
+
+ ngx_io = ngx_os_io;
+
+ ngx_event_actions = ngx_eventport_module_ctx.actions;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_eventport_done(ngx_cycle_t *cycle)
+{
+ if (event_timer != (timer_t) -1) {
+ if (timer_delete(event_timer) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "timer_delete() failed");
+ }
+
+ event_timer = (timer_t) -1;
+ }
+
+ if (close(ep) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "close() event port failed");
+ }
+
+ ep = -1;
+
+ ngx_free(event_list);
+
+ event_list = NULL;
+ nevents = 0;
+}
+
+
+static ngx_int_t
+ngx_eventport_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ ngx_int_t events, prev;
+ ngx_event_t *e;
+ ngx_connection_t *c;
+
+ c = ev->data;
+
+ events = event;
+
+ if (event == NGX_READ_EVENT) {
+ e = c->write;
+ prev = POLLOUT;
+#if (NGX_READ_EVENT != POLLIN)
+ events = POLLIN;
+#endif
+
+ } else {
+ e = c->read;
+ prev = POLLIN;
+#if (NGX_WRITE_EVENT != POLLOUT)
+ events = POLLOUT;
+#endif
+ }
+
+ if (e->oneshot) {
+ events |= prev;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "eventport add event: fd:%d ev:%04Xi", c->fd, events);
+
+ if (port_associate(ep, PORT_SOURCE_FD, c->fd, events,
+ (void *) ((uintptr_t) ev | ev->instance))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
+ "port_associate() failed");
+ return NGX_ERROR;
+ }
+
+ ev->active = 1;
+ ev->oneshot = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_eventport_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ ngx_event_t *e;
+ ngx_connection_t *c;
+
+ /*
+ * when the file descriptor is closed, the event port automatically
+ * dissociates it from the port, so we do not need to dissociate explicitly
+ * the event before the closing the file descriptor
+ */
+
+ if (flags & NGX_CLOSE_EVENT) {
+ ev->active = 0;
+ ev->oneshot = 0;
+ return NGX_OK;
+ }
+
+ c = ev->data;
+
+ if (event == NGX_READ_EVENT) {
+ e = c->write;
+ event = POLLOUT;
+
+ } else {
+ e = c->read;
+ event = POLLIN;
+ }
+
+ if (e->oneshot) {
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "eventport change event: fd:%d ev:%04Xi", c->fd, event);
+
+ if (port_associate(ep, PORT_SOURCE_FD, c->fd, event,
+ (void *) ((uintptr_t) ev | ev->instance))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
+ "port_associate() failed");
+ return NGX_ERROR;
+ }
+
+ } else {
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "eventport del event: fd:%d", c->fd);
+
+ if (port_dissociate(ep, PORT_SOURCE_FD, c->fd) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
+ "port_dissociate() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ ev->active = 0;
+ ev->oneshot = 0;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_eventport_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+ ngx_uint_t flags)
+{
+ int n, revents;
+ u_int events;
+ ngx_err_t err;
+ ngx_int_t instance;
+ ngx_uint_t i, level;
+ ngx_event_t *ev, *rev, *wev, **queue;
+ ngx_connection_t *c;
+ struct timespec ts, *tp;
+
+ if (timer == NGX_TIMER_INFINITE) {
+ tp = NULL;
+
+ } else {
+ ts.tv_sec = timer / 1000;
+ ts.tv_nsec = (timer % 1000) * 1000000;
+ tp = &ts;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "eventport timer: %M", timer);
+
+ events = 1;
+
+ n = port_getn(ep, event_list, (u_int) nevents, &events, tp);
+
+ err = ngx_errno;
+
+ if (flags & NGX_UPDATE_TIME) {
+ ngx_time_update();
+ }
+
+ if (n == -1) {
+ if (err == ETIME) {
+ if (timer != NGX_TIMER_INFINITE) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "port_getn() returned no events without timeout");
+ return NGX_ERROR;
+ }
+
+ level = (err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT;
+ ngx_log_error(level, cycle->log, err, "port_getn() failed");
+ return NGX_ERROR;
+ }
+
+ if (events == 0) {
+ if (timer != NGX_TIMER_INFINITE) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "port_getn() returned no events without timeout");
+ return NGX_ERROR;
+ }
+
+ ngx_mutex_lock(ngx_posted_events_mutex);
+
+ for (i = 0; i < events; i++) {
+
+ if (event_list[i].portev_source == PORT_SOURCE_TIMER) {
+ ngx_time_update();
+ continue;
+ }
+
+ ev = event_list[i].portev_user;
+
+ switch (event_list[i].portev_source) {
+
+ case PORT_SOURCE_FD:
+
+ instance = (uintptr_t) ev & 1;
+ ev = (ngx_event_t *) ((uintptr_t) ev & (uintptr_t) ~1);
+
+ if (ev->closed || ev->instance != instance) {
+
+ /*
+ * the stale event from a file descriptor
+ * that was just closed in this iteration
+ */
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "eventport: stale event %p", ev);
+ continue;
+ }
+
+ revents = event_list[i].portev_events;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "eventport: fd:%d, ev:%04Xd",
+ event_list[i].portev_object, revents);
+
+ if (revents & (POLLERR|POLLHUP|POLLNVAL)) {
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "port_getn() error fd:%d ev:%04Xd",
+ event_list[i].portev_object, revents);
+ }
+
+ if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "strange port_getn() events fd:%d ev:%04Xd",
+ event_list[i].portev_object, revents);
+ }
+
+ if ((revents & (POLLERR|POLLHUP|POLLNVAL))
+ && (revents & (POLLIN|POLLOUT)) == 0)
+ {
+ /*
+ * if the error events were returned without POLLIN or POLLOUT,
+ * then add these flags to handle the events at least in one
+ * active handler
+ */
+
+ revents |= POLLIN|POLLOUT;
+ }
+
+ c = ev->data;
+ rev = c->read;
+ wev = c->write;
+
+ rev->active = 0;
+ wev->active = 0;
+
+ if (revents & POLLIN) {
+
+ if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) {
+ rev->posted_ready = 1;
+
+ } else {
+ rev->ready = 1;
+ }
+
+ if (flags & NGX_POST_EVENTS) {
+ queue = (ngx_event_t **) (rev->accept ?
+ &ngx_posted_accept_events : &ngx_posted_events);
+
+ ngx_locked_post_event(rev, queue);
+
+ } else {
+ rev->handler(rev);
+
+ if (ev->closed || ev->instance != instance) {
+ continue;
+ }
+ }
+
+ if (rev->accept) {
+ if (ngx_use_accept_mutex) {
+ ngx_accept_events = 1;
+ continue;
+ }
+
+ if (port_associate(ep, PORT_SOURCE_FD, c->fd, POLLIN,
+ (void *) ((uintptr_t) ev | ev->instance))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
+ "port_associate() failed");
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ if (revents & POLLOUT) {
+
+ if (flags & NGX_POST_THREAD_EVENTS) {
+ wev->posted_ready = 1;
+
+ } else {
+ wev->ready = 1;
+ }
+
+ if (flags & NGX_POST_EVENTS) {
+ ngx_locked_post_event(wev, &ngx_posted_events);
+
+ } else {
+ wev->handler(wev);
+ }
+ }
+
+ continue;
+
+ default:
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "unexpected even_port object %d",
+ event_list[i].portev_object);
+ continue;
+ }
+ }
+
+ ngx_mutex_unlock(ngx_posted_events_mutex);
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_eventport_create_conf(ngx_cycle_t *cycle)
+{
+ ngx_eventport_conf_t *epcf;
+
+ epcf = ngx_palloc(cycle->pool, sizeof(ngx_eventport_conf_t));
+ if (epcf == NULL) {
+ return NULL;
+ }
+
+ epcf->events = NGX_CONF_UNSET;
+
+ return epcf;
+}
+
+
+static char *
+ngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf)
+{
+ ngx_eventport_conf_t *epcf = conf;
+
+ ngx_conf_init_uint_value(epcf->events, 32);
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_kqueue_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_kqueue_module.c
new file mode 100644
index 00000000000..30e456c4da0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_kqueue_module.c
@@ -0,0 +1,785 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+typedef struct {
+ ngx_uint_t changes;
+ ngx_uint_t events;
+} ngx_kqueue_conf_t;
+
+
+static ngx_int_t ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer);
+static void ngx_kqueue_done(ngx_cycle_t *cycle);
+static ngx_int_t ngx_kqueue_add_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_kqueue_del_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_kqueue_set_event(ngx_event_t *ev, ngx_int_t filter,
+ ngx_uint_t flags);
+static ngx_int_t ngx_kqueue_process_changes(ngx_cycle_t *cycle, ngx_uint_t try);
+static ngx_int_t ngx_kqueue_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+ ngx_uint_t flags);
+static ngx_inline void ngx_kqueue_dump_event(ngx_log_t *log,
+ struct kevent *kev);
+
+static void *ngx_kqueue_create_conf(ngx_cycle_t *cycle);
+static char *ngx_kqueue_init_conf(ngx_cycle_t *cycle, void *conf);
+
+
+int ngx_kqueue = -1;
+
+/*
+ * The "change_list" should be declared as ngx_thread_volatile.
+ * However, the use of the change_list is localized in kqueue functions and
+ * is protected by the mutex so even the "icc -ipo" should not build the code
+ * with the race condition. Thus we avoid the declaration to make a more
+ * readable code.
+ */
+
+static struct kevent *change_list, *change_list0, *change_list1;
+static struct kevent *event_list;
+static ngx_uint_t max_changes, nchanges, nevents;
+
+#if (NGX_THREADS)
+static ngx_mutex_t *list_mutex;
+static ngx_mutex_t *kevent_mutex;
+#endif
+
+
+
+static ngx_str_t kqueue_name = ngx_string("kqueue");
+
+static ngx_command_t ngx_kqueue_commands[] = {
+
+ { ngx_string("kqueue_changes"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_kqueue_conf_t, changes),
+ NULL },
+
+ { ngx_string("kqueue_events"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_kqueue_conf_t, events),
+ NULL },
+
+ ngx_null_command
+};
+
+
+ngx_event_module_t ngx_kqueue_module_ctx = {
+ &kqueue_name,
+ ngx_kqueue_create_conf, /* create configuration */
+ ngx_kqueue_init_conf, /* init configuration */
+
+ {
+ ngx_kqueue_add_event, /* add an event */
+ ngx_kqueue_del_event, /* delete an event */
+ ngx_kqueue_add_event, /* enable an event */
+ ngx_kqueue_del_event, /* disable an event */
+ NULL, /* add an connection */
+ NULL, /* delete an connection */
+ ngx_kqueue_process_changes, /* process the changes */
+ ngx_kqueue_process_events, /* process the events */
+ ngx_kqueue_init, /* init the events */
+ ngx_kqueue_done /* done the events */
+ }
+
+};
+
+ngx_module_t ngx_kqueue_module = {
+ NGX_MODULE_V1,
+ &ngx_kqueue_module_ctx, /* module context */
+ ngx_kqueue_commands, /* module directives */
+ NGX_EVENT_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer)
+{
+ ngx_kqueue_conf_t *kcf;
+ struct timespec ts;
+#if (NGX_HAVE_TIMER_EVENT)
+ struct kevent kev;
+#endif
+
+ kcf = ngx_event_get_conf(cycle->conf_ctx, ngx_kqueue_module);
+
+ if (ngx_kqueue == -1) {
+ ngx_kqueue = kqueue();
+
+ if (ngx_kqueue == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "kqueue() failed");
+ return NGX_ERROR;
+ }
+
+#if (NGX_THREADS)
+
+ list_mutex = ngx_mutex_init(cycle->log, 0);
+ if (list_mutex == NULL) {
+ return NGX_ERROR;
+ }
+
+ kevent_mutex = ngx_mutex_init(cycle->log, 0);
+ if (kevent_mutex == NULL) {
+ return NGX_ERROR;
+ }
+
+#endif
+ }
+
+ if (max_changes < kcf->changes) {
+ if (nchanges) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+
+ if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts)
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "kevent() failed");
+ return NGX_ERROR;
+ }
+ nchanges = 0;
+ }
+
+ if (change_list0) {
+ ngx_free(change_list0);
+ }
+
+ change_list0 = ngx_alloc(kcf->changes * sizeof(struct kevent),
+ cycle->log);
+ if (change_list0 == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (change_list1) {
+ ngx_free(change_list1);
+ }
+
+ change_list1 = ngx_alloc(kcf->changes * sizeof(struct kevent),
+ cycle->log);
+ if (change_list1 == NULL) {
+ return NGX_ERROR;
+ }
+
+ change_list = change_list0;
+ }
+
+ max_changes = kcf->changes;
+
+ if (nevents < kcf->events) {
+ if (event_list) {
+ ngx_free(event_list);
+ }
+
+ event_list = ngx_alloc(kcf->events * sizeof(struct kevent), cycle->log);
+ if (event_list == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ ngx_event_flags = NGX_USE_ONESHOT_EVENT
+ |NGX_USE_KQUEUE_EVENT
+ |NGX_USE_VNODE_EVENT;
+
+#if (NGX_HAVE_TIMER_EVENT)
+
+ if (timer) {
+ kev.ident = 0;
+ kev.filter = EVFILT_TIMER;
+ kev.flags = EV_ADD|EV_ENABLE;
+ kev.fflags = 0;
+ kev.data = timer;
+ kev.udata = 0;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+
+ if (kevent(ngx_kqueue, &kev, 1, NULL, 0, &ts) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "kevent(EVFILT_TIMER) failed");
+ return NGX_ERROR;
+ }
+
+ ngx_event_flags |= NGX_USE_TIMER_EVENT;
+ }
+
+#endif
+
+#if (NGX_HAVE_CLEAR_EVENT)
+ ngx_event_flags |= NGX_USE_CLEAR_EVENT;
+#else
+ ngx_event_flags |= NGX_USE_LEVEL_EVENT;
+#endif
+
+#if (NGX_HAVE_LOWAT_EVENT)
+ ngx_event_flags |= NGX_USE_LOWAT_EVENT;
+#endif
+
+ nevents = kcf->events;
+
+ ngx_io = ngx_os_io;
+
+ ngx_event_actions = ngx_kqueue_module_ctx.actions;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_kqueue_done(ngx_cycle_t *cycle)
+{
+ if (close(ngx_kqueue) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "kqueue close() failed");
+ }
+
+ ngx_kqueue = -1;
+
+#if (NGX_THREADS)
+ ngx_mutex_destroy(kevent_mutex);
+ ngx_mutex_destroy(list_mutex);
+#endif
+
+ ngx_free(change_list1);
+ ngx_free(change_list0);
+ ngx_free(event_list);
+
+ change_list1 = NULL;
+ change_list0 = NULL;
+ change_list = NULL;
+ event_list = NULL;
+ max_changes = 0;
+ nchanges = 0;
+ nevents = 0;
+}
+
+
+static ngx_int_t
+ngx_kqueue_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ ngx_int_t rc;
+#if 0
+ ngx_event_t *e;
+ ngx_connection_t *c;
+#endif
+
+ ev->active = 1;
+ ev->disabled = 0;
+ ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;
+
+ ngx_mutex_lock(list_mutex);
+
+#if 0
+
+ if (ev->index < nchanges
+ && ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1)
+ == (uintptr_t) ev)
+ {
+ if (change_list[ev->index].flags == EV_DISABLE) {
+
+ /*
+ * if the EV_DISABLE is still not passed to a kernel
+ * we will not pass it
+ */
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "kevent activated: %d: ft:%i",
+ ngx_event_ident(ev->data), event);
+
+ if (ev->index < --nchanges) {
+ e = (ngx_event_t *)
+ ((uintptr_t) change_list[nchanges].udata & (uintptr_t) ~1);
+ change_list[ev->index] = change_list[nchanges];
+ e->index = ev->index;
+ }
+
+ ngx_mutex_unlock(list_mutex);
+
+ return NGX_OK;
+ }
+
+ c = ev->data;
+
+ ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+ "previous event on #%d were not passed in kernel", c->fd);
+
+ ngx_mutex_unlock(list_mutex);
+
+ return NGX_ERROR;
+ }
+
+#endif
+
+ rc = ngx_kqueue_set_event(ev, event, EV_ADD|EV_ENABLE|flags);
+
+ ngx_mutex_unlock(list_mutex);
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_kqueue_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ ngx_int_t rc;
+ ngx_event_t *e;
+
+ ev->active = 0;
+ ev->disabled = 0;
+
+ ngx_mutex_lock(list_mutex);
+
+ if (ev->index < nchanges
+ && ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1)
+ == (uintptr_t) ev)
+ {
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "kevent deleted: %d: ft:%i",
+ ngx_event_ident(ev->data), event);
+
+ /* if the event is still not passed to a kernel we will not pass it */
+
+ nchanges--;
+
+ if (ev->index < nchanges) {
+ e = (ngx_event_t *)
+ ((uintptr_t) change_list[nchanges].udata & (uintptr_t) ~1);
+ change_list[ev->index] = change_list[nchanges];
+ e->index = ev->index;
+ }
+
+ ngx_mutex_unlock(list_mutex);
+
+ return NGX_OK;
+ }
+
+ /*
+ * when the file descriptor is closed the kqueue automatically deletes
+ * its filters so we do not need to delete explicitly the event
+ * before the closing the file descriptor.
+ */
+
+ if (flags & NGX_CLOSE_EVENT) {
+ ngx_mutex_unlock(list_mutex);
+ return NGX_OK;
+ }
+
+ if (flags & NGX_DISABLE_EVENT) {
+ ev->disabled = 1;
+
+ } else {
+ flags |= EV_DELETE;
+ }
+
+ rc = ngx_kqueue_set_event(ev, event, flags);
+
+ ngx_mutex_unlock(list_mutex);
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_kqueue_set_event(ngx_event_t *ev, ngx_int_t filter, ngx_uint_t flags)
+{
+ struct kevent *kev;
+ struct timespec ts;
+ ngx_connection_t *c;
+
+ c = ev->data;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "kevent set event: %d: ft:%i fl:%04Xi",
+ c->fd, filter, flags);
+
+ if (nchanges >= max_changes) {
+ ngx_log_error(NGX_LOG_WARN, ev->log, 0,
+ "kqueue change list is filled up");
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+
+ if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts)
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "kevent() failed");
+ return NGX_ERROR;
+ }
+
+ nchanges = 0;
+ }
+
+ kev = &change_list[nchanges];
+
+ kev->ident = c->fd;
+ kev->filter = (short) filter;
+ kev->flags = (u_short) flags;
+ kev->udata = NGX_KQUEUE_UDATA_T ((uintptr_t) ev | ev->instance);
+
+ if (filter == EVFILT_VNODE) {
+ kev->fflags = NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND
+ |NOTE_ATTRIB|NOTE_RENAME
+#if (__FreeBSD__ == 4 && __FreeBSD_version >= 430000) \
+ || __FreeBSD_version >= 500018
+ |NOTE_REVOKE
+#endif
+ ;
+ kev->data = 0;
+
+ } else {
+#if (NGX_HAVE_LOWAT_EVENT)
+ if (flags & NGX_LOWAT_EVENT) {
+ kev->fflags = NOTE_LOWAT;
+ kev->data = ev->available;
+
+ } else {
+ kev->fflags = 0;
+ kev->data = 0;
+ }
+#else
+ kev->fflags = 0;
+ kev->data = 0;
+#endif
+ }
+
+ ev->index = nchanges;
+ nchanges++;
+
+ if (flags & NGX_FLUSH_EVENT) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "kevent flush");
+
+ if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts)
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "kevent() failed");
+ return NGX_ERROR;
+ }
+
+ nchanges = 0;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_kqueue_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+ ngx_uint_t flags)
+{
+ int events, n;
+ ngx_int_t i, instance;
+ ngx_uint_t level;
+ ngx_err_t err;
+ ngx_event_t *ev, **queue;
+ struct timespec ts, *tp;
+
+ if (ngx_threaded) {
+ if (ngx_kqueue_process_changes(cycle, 0) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ n = 0;
+
+ } else {
+ n = (int) nchanges;
+ nchanges = 0;
+ }
+
+ if (timer == NGX_TIMER_INFINITE) {
+ tp = NULL;
+
+ } else {
+
+ ts.tv_sec = timer / 1000;
+ ts.tv_nsec = (timer % 1000) * 1000000;
+
+ /*
+ * 64-bit Darwin kernel has the bug: kernel level ts.tv_nsec is
+ * the int32_t while user level ts.tv_nsec is the long (64-bit),
+ * so on the big endian PowerPC all nanoseconds are lost.
+ */
+
+#if (NGX_DARWIN_KEVENT_BUG)
+ ts.tv_nsec <<= 32;
+#endif
+
+ tp = &ts;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "kevent timer: %M, changes: %d", timer, n);
+
+ events = kevent(ngx_kqueue, change_list, n, event_list, (int) nevents, tp);
+
+ err = (events == -1) ? ngx_errno : 0;
+
+ if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
+ ngx_time_update();
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "kevent events: %d", events);
+
+ if (err) {
+ if (err == NGX_EINTR) {
+
+ if (ngx_event_timer_alarm) {
+ ngx_event_timer_alarm = 0;
+ return NGX_OK;
+ }
+
+ level = NGX_LOG_INFO;
+
+ } else {
+ level = NGX_LOG_ALERT;
+ }
+
+ ngx_log_error(level, cycle->log, err, "kevent() failed");
+ return NGX_ERROR;
+ }
+
+ if (events == 0) {
+ if (timer != NGX_TIMER_INFINITE) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "kevent() returned no events without timeout");
+ return NGX_ERROR;
+ }
+
+ ngx_mutex_lock(ngx_posted_events_mutex);
+
+ for (i = 0; i < events; i++) {
+
+ ngx_kqueue_dump_event(cycle->log, &event_list[i]);
+
+ if (event_list[i].flags & EV_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, event_list[i].data,
+ "kevent() error on %d filter:%d flags:%04Xd",
+ event_list[i].ident, event_list[i].filter,
+ event_list[i].flags);
+ continue;
+ }
+
+#if (NGX_HAVE_TIMER_EVENT)
+
+ if (event_list[i].filter == EVFILT_TIMER) {
+ ngx_time_update();
+ continue;
+ }
+
+#endif
+
+ ev = (ngx_event_t *) event_list[i].udata;
+
+ switch (event_list[i].filter) {
+
+ case EVFILT_READ:
+ case EVFILT_WRITE:
+
+ instance = (uintptr_t) ev & 1;
+ ev = (ngx_event_t *) ((uintptr_t) ev & (uintptr_t) ~1);
+
+ if (ev->closed || ev->instance != instance) {
+
+ /*
+ * the stale event from a file descriptor
+ * that was just closed in this iteration
+ */
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "kevent: stale event %p", ev);
+ continue;
+ }
+
+ if (ev->log && (ev->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
+ ngx_kqueue_dump_event(ev->log, &event_list[i]);
+ }
+
+ if (ev->oneshot) {
+ ev->active = 0;
+ }
+
+#if (NGX_THREADS)
+
+ if ((flags & NGX_POST_THREAD_EVENTS) && !ev->accept) {
+ ev->posted_ready = 1;
+ ev->posted_available = event_list[i].data;
+
+ if (event_list[i].flags & EV_EOF) {
+ ev->posted_eof = 1;
+ ev->posted_errno = event_list[i].fflags;
+ }
+
+ ngx_locked_post_event(ev, &ngx_posted_events);
+
+ continue;
+ }
+
+#endif
+
+ ev->available = event_list[i].data;
+
+ if (event_list[i].flags & EV_EOF) {
+ ev->pending_eof = 1;
+ ev->kq_errno = event_list[i].fflags;
+ }
+
+ ev->ready = 1;
+
+ break;
+
+ case EVFILT_VNODE:
+ ev->kq_vnode = 1;
+
+ break;
+
+ case EVFILT_AIO:
+ ev->complete = 1;
+ ev->ready = 1;
+
+ break;
+
+ default:
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "unexpected kevent() filter %d",
+ event_list[i].filter);
+ continue;
+ }
+
+ if (flags & NGX_POST_EVENTS) {
+ queue = (ngx_event_t **) (ev->accept ? &ngx_posted_accept_events:
+ &ngx_posted_events);
+ ngx_locked_post_event(ev, queue);
+
+ continue;
+ }
+
+ ev->handler(ev);
+ }
+
+ ngx_mutex_unlock(ngx_posted_events_mutex);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_kqueue_process_changes(ngx_cycle_t *cycle, ngx_uint_t try)
+{
+ int n;
+ ngx_int_t rc;
+ ngx_err_t err;
+ struct timespec ts;
+ struct kevent *changes;
+
+ ngx_mutex_lock(kevent_mutex);
+
+ ngx_mutex_lock(list_mutex);
+
+ if (nchanges == 0) {
+ ngx_mutex_unlock(list_mutex);
+ ngx_mutex_unlock(kevent_mutex);
+ return NGX_OK;
+ }
+
+ changes = change_list;
+ if (change_list == change_list0) {
+ change_list = change_list1;
+ } else {
+ change_list = change_list0;
+ }
+
+ n = (int) nchanges;
+ nchanges = 0;
+
+ ngx_mutex_unlock(list_mutex);
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "kevent changes: %d", n);
+
+ if (kevent(ngx_kqueue, changes, n, NULL, 0, &ts) == -1) {
+ err = ngx_errno;
+ ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,
+ cycle->log, err, "kevent() failed");
+ rc = NGX_ERROR;
+
+ } else {
+ rc = NGX_OK;
+ }
+
+ ngx_mutex_unlock(kevent_mutex);
+
+ return rc;
+}
+
+
+static ngx_inline void
+ngx_kqueue_dump_event(ngx_log_t *log, struct kevent *kev)
+{
+ ngx_log_debug6(NGX_LOG_DEBUG_EVENT, log, 0,
+ (kev->ident > 0x8000000 && kev->ident != (unsigned) -1) ?
+ "kevent: %p: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p":
+ "kevent: %d: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p",
+ kev->ident, kev->filter,
+ kev->flags, kev->fflags,
+ kev->data, kev->udata);
+}
+
+
+static void *
+ngx_kqueue_create_conf(ngx_cycle_t *cycle)
+{
+ ngx_kqueue_conf_t *kcf;
+
+ kcf = ngx_palloc(cycle->pool, sizeof(ngx_kqueue_conf_t));
+ if (kcf == NULL) {
+ return NULL;
+ }
+
+ kcf->changes = NGX_CONF_UNSET;
+ kcf->events = NGX_CONF_UNSET;
+
+ return kcf;
+}
+
+
+static char *
+ngx_kqueue_init_conf(ngx_cycle_t *cycle, void *conf)
+{
+ ngx_kqueue_conf_t *kcf = conf;
+
+ ngx_conf_init_uint_value(kcf->changes, 512);
+ ngx_conf_init_uint_value(kcf->events, 512);
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_poll_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_poll_module.c
new file mode 100644
index 00000000000..4d4521845a8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_poll_module.c
@@ -0,0 +1,443 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+static ngx_int_t ngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer);
+static void ngx_poll_done(ngx_cycle_t *cycle);
+static ngx_int_t ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+ ngx_uint_t flags);
+static char *ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf);
+
+
+static struct pollfd *event_list;
+static ngx_int_t nevents;
+
+
+static ngx_str_t poll_name = ngx_string("poll");
+
+ngx_event_module_t ngx_poll_module_ctx = {
+ &poll_name,
+ NULL, /* create configuration */
+ ngx_poll_init_conf, /* init configuration */
+
+ {
+ ngx_poll_add_event, /* add an event */
+ ngx_poll_del_event, /* delete an event */
+ ngx_poll_add_event, /* enable an event */
+ ngx_poll_del_event, /* disable an event */
+ NULL, /* add an connection */
+ NULL, /* delete an connection */
+ NULL, /* process the changes */
+ ngx_poll_process_events, /* process the events */
+ ngx_poll_init, /* init the events */
+ ngx_poll_done /* done the events */
+ }
+
+};
+
+ngx_module_t ngx_poll_module = {
+ NGX_MODULE_V1,
+ &ngx_poll_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_EVENT_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+
+static ngx_int_t
+ngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer)
+{
+ struct pollfd *list;
+
+ if (event_list == NULL) {
+ nevents = 0;
+ }
+
+ if (ngx_process >= NGX_PROCESS_WORKER
+ || cycle->old_cycle == NULL
+ || cycle->old_cycle->connection_n < cycle->connection_n)
+ {
+ list = ngx_alloc(sizeof(struct pollfd) * cycle->connection_n,
+ cycle->log);
+ if (list == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (event_list) {
+ ngx_memcpy(list, event_list, sizeof(ngx_event_t *) * nevents);
+ ngx_free(event_list);
+ }
+
+ event_list = list;
+ }
+
+ ngx_io = ngx_os_io;
+
+ ngx_event_actions = ngx_poll_module_ctx.actions;
+
+ ngx_event_flags = NGX_USE_LEVEL_EVENT|NGX_USE_FD_EVENT;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_poll_done(ngx_cycle_t *cycle)
+{
+ ngx_free(event_list);
+
+ event_list = NULL;
+}
+
+
+static ngx_int_t
+ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ ngx_event_t *e;
+ ngx_connection_t *c;
+
+ c = ev->data;
+
+ ev->active = 1;
+
+ if (ev->index != NGX_INVALID_INDEX) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+ "poll event fd:%d ev:%i is already set", c->fd, event);
+ return NGX_OK;
+ }
+
+ if (event == NGX_READ_EVENT) {
+ e = c->write;
+#if (NGX_READ_EVENT != POLLIN)
+ event = POLLIN;
+#endif
+
+ } else {
+ e = c->read;
+#if (NGX_WRITE_EVENT != POLLOUT)
+ event = POLLOUT;
+#endif
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "poll add event: fd:%d ev:%i", c->fd, event);
+
+ if (e == NULL || e->index == NGX_INVALID_INDEX) {
+ event_list[nevents].fd = c->fd;
+ event_list[nevents].events = (short) event;
+ event_list[nevents].revents = 0;
+
+ ev->index = nevents;
+ nevents++;
+
+ } else {
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "poll add index: %i", e->index);
+
+ event_list[e->index].events |= (short) event;
+ ev->index = e->index;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ ngx_event_t *e;
+ ngx_connection_t *c;
+
+ c = ev->data;
+
+ ev->active = 0;
+
+ if (ev->index == NGX_INVALID_INDEX) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+ "poll event fd:%d ev:%i is already deleted",
+ c->fd, event);
+ return NGX_OK;
+ }
+
+ if (event == NGX_READ_EVENT) {
+ e = c->write;
+#if (NGX_READ_EVENT != POLLIN)
+ event = POLLIN;
+#endif
+
+ } else {
+ e = c->read;
+#if (NGX_WRITE_EVENT != POLLOUT)
+ event = POLLOUT;
+#endif
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "poll del event: fd:%d ev:%i", c->fd, event);
+
+ if (e == NULL || e->index == NGX_INVALID_INDEX) {
+ nevents--;
+
+ if (ev->index < (ngx_uint_t) nevents) {
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "index: copy event %ui to %i", nevents, ev->index);
+
+ event_list[ev->index] = event_list[nevents];
+
+ c = ngx_cycle->files[event_list[nevents].fd];
+
+ if (c->fd == -1) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+ "unexpected last event");
+
+ } else {
+ if (c->read->index == (ngx_uint_t) nevents) {
+ c->read->index = ev->index;
+ }
+
+ if (c->write->index == (ngx_uint_t) nevents) {
+ c->write->index = ev->index;
+ }
+ }
+ }
+
+ } else {
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "poll del index: %i", e->index);
+
+ event_list[e->index].events &= (short) ~event;
+ }
+
+ ev->index = NGX_INVALID_INDEX;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
+{
+ int ready, revents;
+ ngx_err_t err;
+ ngx_int_t i, nready;
+ ngx_uint_t found, level;
+ ngx_event_t *ev, **queue;
+ ngx_connection_t *c;
+
+ /* NGX_TIMER_INFINITE == INFTIM */
+
+#if (NGX_DEBUG0)
+ if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {
+ for (i = 0; i < nevents; i++) {
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "poll: %d: fd:%d ev:%04Xd",
+ i, event_list[i].fd, event_list[i].events);
+ }
+ }
+#endif
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "poll timer: %M", timer);
+
+ ready = poll(event_list, (u_int) nevents, (int) timer);
+
+ err = (ready == -1) ? ngx_errno : 0;
+
+ if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
+ ngx_time_update();
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "poll ready %d of %d", ready, nevents);
+
+ if (err) {
+ if (err == NGX_EINTR) {
+
+ if (ngx_event_timer_alarm) {
+ ngx_event_timer_alarm = 0;
+ return NGX_OK;
+ }
+
+ level = NGX_LOG_INFO;
+
+ } else {
+ level = NGX_LOG_ALERT;
+ }
+
+ ngx_log_error(level, cycle->log, err, "poll() failed");
+ return NGX_ERROR;
+ }
+
+ if (ready == 0) {
+ if (timer != NGX_TIMER_INFINITE) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "poll() returned no events without timeout");
+ return NGX_ERROR;
+ }
+
+ ngx_mutex_lock(ngx_posted_events_mutex);
+
+ nready = 0;
+
+ for (i = 0; i < nevents && ready; i++) {
+
+ revents = event_list[i].revents;
+
+#if 1
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "poll: %d: fd:%d ev:%04Xd rev:%04Xd",
+ i, event_list[i].fd, event_list[i].events, revents);
+#else
+ if (revents) {
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "poll: %d: fd:%d ev:%04Xd rev:%04Xd",
+ i, event_list[i].fd, event_list[i].events, revents);
+ }
+#endif
+
+ if (revents & POLLNVAL) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "poll() error fd:%d ev:%04Xd rev:%04Xd",
+ event_list[i].fd, event_list[i].events, revents);
+ }
+
+ if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "strange poll() events fd:%d ev:%04Xd rev:%04Xd",
+ event_list[i].fd, event_list[i].events, revents);
+ }
+
+ if (event_list[i].fd == -1) {
+ /*
+ * the disabled event, a workaround for our possible bug,
+ * see the comment below
+ */
+ continue;
+ }
+
+ c = ngx_cycle->files[event_list[i].fd];
+
+ if (c->fd == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "unexpected event");
+
+ /*
+ * it is certainly our fault and it should be investigated,
+ * in the meantime we disable this event to avoid a CPU spinning
+ */
+
+ if (i == nevents - 1) {
+ nevents--;
+ } else {
+ event_list[i].fd = -1;
+ }
+
+ continue;
+ }
+
+ if ((revents & (POLLERR|POLLHUP|POLLNVAL))
+ && (revents & (POLLIN|POLLOUT)) == 0)
+ {
+ /*
+ * if the error events were returned without POLLIN or POLLOUT,
+ * then add these flags to handle the events at least in one
+ * active handler
+ */
+
+ revents |= POLLIN|POLLOUT;
+ }
+
+ found = 0;
+
+ if ((revents & POLLIN) && c->read->active) {
+ found = 1;
+
+ ev = c->read;
+
+ if ((flags & NGX_POST_THREAD_EVENTS) && !ev->accept) {
+ ev->posted_ready = 1;
+
+ } else {
+ ev->ready = 1;
+ }
+
+ queue = (ngx_event_t **) (ev->accept ? &ngx_posted_accept_events:
+ &ngx_posted_events);
+ ngx_locked_post_event(ev, queue);
+ }
+
+ if ((revents & POLLOUT) && c->write->active) {
+ found = 1;
+ ev = c->write;
+
+ if (flags & NGX_POST_THREAD_EVENTS) {
+ ev->posted_ready = 1;
+
+ } else {
+ ev->ready = 1;
+ }
+
+ ngx_locked_post_event(ev, &ngx_posted_events);
+ }
+
+ if (found) {
+ ready--;
+ continue;
+ }
+ }
+
+ ngx_mutex_unlock(ngx_posted_events_mutex);
+
+ if (ready != 0) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "poll ready != events");
+ }
+
+ return nready;
+}
+
+
+static char *
+ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf)
+{
+ ngx_event_conf_t *ecf;
+
+ ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
+
+ if (ecf->use != ngx_poll_module.ctx_index) {
+ return NGX_CONF_OK;
+ }
+
+#if (NGX_THREADS)
+
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+ "poll() is not supported in the threaded mode");
+ return NGX_CONF_ERROR;
+
+#else
+
+ return NGX_CONF_OK;
+
+#endif
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_rtsig_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_rtsig_module.c
new file mode 100644
index 00000000000..b36230c7650
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_rtsig_module.c
@@ -0,0 +1,747 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if (NGX_TEST_BUILD_RTSIG)
+
+#if (NGX_DARWIN)
+
+#define SIGRTMIN 33
+#define si_fd __pad[0]
+
+#else
+
+#ifdef SIGRTMIN
+#define si_fd _reason.__spare__.__spare2__[0]
+#else
+#define SIGRTMIN 33
+#define si_fd __spare__[0]
+#endif
+
+#endif
+
+#define F_SETSIG 10
+#define KERN_RTSIGNR 30
+#define KERN_RTSIGMAX 31
+
+int sigtimedwait(const sigset_t *set, siginfo_t *info,
+ const struct timespec *timeout);
+
+int sigtimedwait(const sigset_t *set, siginfo_t *info,
+ const struct timespec *timeout)
+{
+ return -1;
+}
+
+int ngx_linux_rtsig_max;
+
+#endif
+
+
+typedef struct {
+ ngx_uint_t signo;
+ ngx_uint_t overflow_events;
+ ngx_uint_t overflow_test;
+ ngx_uint_t overflow_threshold;
+} ngx_rtsig_conf_t;
+
+
+extern ngx_event_module_t ngx_poll_module_ctx;
+
+static ngx_int_t ngx_rtsig_init(ngx_cycle_t *cycle, ngx_msec_t timer);
+static void ngx_rtsig_done(ngx_cycle_t *cycle);
+static ngx_int_t ngx_rtsig_add_connection(ngx_connection_t *c);
+static ngx_int_t ngx_rtsig_del_connection(ngx_connection_t *c,
+ ngx_uint_t flags);
+static ngx_int_t ngx_rtsig_process_events(ngx_cycle_t *cycle,
+ ngx_msec_t timer, ngx_uint_t flags);
+static ngx_int_t ngx_rtsig_process_overflow(ngx_cycle_t *cycle,
+ ngx_msec_t timer, ngx_uint_t flags);
+
+static void *ngx_rtsig_create_conf(ngx_cycle_t *cycle);
+static char *ngx_rtsig_init_conf(ngx_cycle_t *cycle, void *conf);
+static char *ngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf,
+ void *post, void *data);
+
+
+static sigset_t set;
+static ngx_uint_t overflow, overflow_current;
+static struct pollfd *overflow_list;
+
+
+static ngx_str_t rtsig_name = ngx_string("rtsig");
+
+static ngx_conf_num_bounds_t ngx_overflow_threshold_bounds = {
+ ngx_check_ngx_overflow_threshold_bounds, 2, 10
+};
+
+
+static ngx_command_t ngx_rtsig_commands[] = {
+
+ { ngx_string("rtsig_signo"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_rtsig_conf_t, signo),
+ NULL },
+
+ { ngx_string("rtsig_overflow_events"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_rtsig_conf_t, overflow_events),
+ NULL },
+
+ { ngx_string("rtsig_overflow_test"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_rtsig_conf_t, overflow_test),
+ NULL },
+
+ { ngx_string("rtsig_overflow_threshold"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_rtsig_conf_t, overflow_threshold),
+ &ngx_overflow_threshold_bounds },
+
+ ngx_null_command
+};
+
+
+ngx_event_module_t ngx_rtsig_module_ctx = {
+ &rtsig_name,
+ ngx_rtsig_create_conf, /* create configuration */
+ ngx_rtsig_init_conf, /* init configuration */
+
+ {
+ NULL, /* add an event */
+ NULL, /* delete an event */
+ NULL, /* enable an event */
+ NULL, /* disable an event */
+ ngx_rtsig_add_connection, /* add an connection */
+ ngx_rtsig_del_connection, /* delete an connection */
+ NULL, /* process the changes */
+ ngx_rtsig_process_events, /* process the events */
+ ngx_rtsig_init, /* init the events */
+ ngx_rtsig_done, /* done the events */
+ }
+
+};
+
+ngx_module_t ngx_rtsig_module = {
+ NGX_MODULE_V1,
+ &ngx_rtsig_module_ctx, /* module context */
+ ngx_rtsig_commands, /* module directives */
+ NGX_EVENT_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_rtsig_init(ngx_cycle_t *cycle, ngx_msec_t timer)
+{
+ ngx_rtsig_conf_t *rtscf;
+
+ rtscf = ngx_event_get_conf(cycle->conf_ctx, ngx_rtsig_module);
+
+ sigemptyset(&set);
+ sigaddset(&set, (int) rtscf->signo);
+ sigaddset(&set, (int) rtscf->signo + 1);
+ sigaddset(&set, SIGIO);
+ sigaddset(&set, SIGALRM);
+
+ if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "sigprocmask() failed");
+ return NGX_ERROR;
+ }
+
+ if (overflow_list) {
+ ngx_free(overflow_list);
+ }
+
+ overflow_list = ngx_alloc(sizeof(struct pollfd) * rtscf->overflow_events,
+ cycle->log);
+ if (overflow_list == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_io = ngx_os_io;
+
+ ngx_event_actions = ngx_rtsig_module_ctx.actions;
+
+ ngx_event_flags = NGX_USE_RTSIG_EVENT
+ |NGX_USE_GREEDY_EVENT
+ |NGX_USE_FD_EVENT;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_rtsig_done(ngx_cycle_t *cycle)
+{
+ ngx_free(overflow_list);
+
+ overflow_list = NULL;
+}
+
+
+static ngx_int_t
+ngx_rtsig_add_connection(ngx_connection_t *c)
+{
+ ngx_uint_t signo;
+ ngx_rtsig_conf_t *rtscf;
+
+ if (c->read->accept && c->read->disabled) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "rtsig enable connection: fd:%d", c->fd);
+
+ if (fcntl(c->fd, F_SETOWN, ngx_pid) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
+ "fcntl(F_SETOWN) failed");
+ return NGX_ERROR;
+ }
+
+ c->read->active = 1;
+ c->read->disabled = 0;
+ }
+
+ rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);
+
+ signo = rtscf->signo + c->read->instance;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "rtsig add connection: fd:%d signo:%ui", c->fd, signo);
+
+ if (fcntl(c->fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
+ "fcntl(O_RDWR|O_NONBLOCK|O_ASYNC) failed");
+ return NGX_ERROR;
+ }
+
+ if (fcntl(c->fd, F_SETSIG, (int) signo) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
+ "fcntl(F_SETSIG) failed");
+ return NGX_ERROR;
+ }
+
+ if (fcntl(c->fd, F_SETOWN, ngx_pid) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
+ "fcntl(F_SETOWN) failed");
+ return NGX_ERROR;
+ }
+
+#if (NGX_HAVE_ONESIGFD)
+ if (fcntl(c->fd, F_SETAUXFL, O_ONESIGFD) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
+ "fcntl(F_SETAUXFL) failed");
+ return NGX_ERROR;
+ }
+#endif
+
+ c->read->active = 1;
+ c->write->active = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtsig_del_connection(ngx_connection_t *c, ngx_uint_t flags)
+{
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "rtsig del connection: fd:%d", c->fd);
+
+ if ((flags & NGX_DISABLE_EVENT) && c->read->accept) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "rtsig disable connection: fd:%d", c->fd);
+
+ c->read->active = 0;
+ c->read->disabled = 1;
+ return NGX_OK;
+ }
+
+ if (flags & NGX_CLOSE_EVENT) {
+ c->read->active = 0;
+ c->write->active = 0;
+ return NGX_OK;
+ }
+
+ if (fcntl(c->fd, F_SETFL, O_RDWR|O_NONBLOCK) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
+ "fcntl(O_RDWR|O_NONBLOCK) failed");
+ return NGX_ERROR;
+ }
+
+ c->read->active = 0;
+ c->write->active = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_rtsig_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)
+{
+ int signo;
+ ngx_int_t instance;
+ ngx_err_t err;
+ siginfo_t si;
+ ngx_event_t *rev, *wev, **queue;
+ struct timespec ts, *tp;
+ struct sigaction sa;
+ ngx_connection_t *c;
+ ngx_rtsig_conf_t *rtscf;
+
+ if (timer == NGX_TIMER_INFINITE) {
+ tp = NULL;
+
+ } else {
+ ts.tv_sec = timer / 1000;
+ ts.tv_nsec = (timer % 1000) * 1000000;
+ tp = &ts;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "rtsig timer: %M", timer);
+
+ /* Linux's sigwaitinfo() is sigtimedwait() with the NULL timeout pointer */
+
+ signo = sigtimedwait(&set, &si, tp);
+
+ if (signo == -1) {
+ err = ngx_errno;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, err,
+ "rtsig signo:%d", signo);
+
+ if (flags & NGX_UPDATE_TIME) {
+ ngx_time_update();
+ }
+
+ if (err == NGX_EAGAIN) {
+
+ /* timeout */
+
+ if (timer != NGX_TIMER_INFINITE) {
+ return NGX_AGAIN;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+ "sigtimedwait() returned EAGAIN without timeout");
+ return NGX_ERROR;
+ }
+
+ ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,
+ cycle->log, err, "sigtimedwait() failed");
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "rtsig signo:%d fd:%d band:%04Xd",
+ signo, si.si_fd, si.si_band);
+
+ if (flags & NGX_UPDATE_TIME) {
+ ngx_time_update();
+ }
+
+ rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);
+
+ if (signo == (int) rtscf->signo || signo == (int) rtscf->signo + 1) {
+
+ if (overflow && (ngx_uint_t) si.si_fd > overflow_current) {
+ return NGX_OK;
+ }
+
+ c = ngx_cycle->files[si.si_fd];
+
+ if (c == NULL) {
+
+ /* the stale event */
+
+ return NGX_OK;
+ }
+
+ instance = signo - (int) rtscf->signo;
+
+ rev = c->read;
+
+ if (rev->instance != instance) {
+
+ /*
+ * the stale event from a file descriptor
+ * that was just closed in this iteration
+ */
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "rtsig: stale event %p", c);
+
+ return NGX_OK;
+ }
+
+ if ((si.si_band & (POLLIN|POLLHUP|POLLERR)) && rev->active) {
+
+ rev->ready = 1;
+
+ if (flags & NGX_POST_EVENTS) {
+ queue = (ngx_event_t **) (rev->accept ?
+ &ngx_posted_accept_events : &ngx_posted_events);
+
+ ngx_locked_post_event(rev, queue);
+
+ } else {
+ rev->handler(rev);
+ }
+ }
+
+ wev = c->write;
+
+ if ((si.si_band & (POLLOUT|POLLHUP|POLLERR)) && wev->active) {
+
+ wev->ready = 1;
+
+ if (flags & NGX_POST_EVENTS) {
+ ngx_locked_post_event(wev, &ngx_posted_events);
+
+ } else {
+ wev->handler(wev);
+ }
+ }
+
+ return NGX_OK;
+
+ } else if (signo == SIGALRM) {
+
+ ngx_time_update();
+
+ return NGX_OK;
+
+ } else if (signo == SIGIO) {
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "rt signal queue overflowed");
+
+ /* flush the RT signal queue */
+
+ ngx_memzero(&sa, sizeof(struct sigaction));
+ sa.sa_handler = SIG_DFL;
+ sigemptyset(&sa.sa_mask);
+
+ if (sigaction(rtscf->signo, &sa, NULL) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "sigaction(%d, SIG_DFL) failed", rtscf->signo);
+ }
+
+ if (sigaction(rtscf->signo + 1, &sa, NULL) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "sigaction(%d, SIG_DFL) failed", rtscf->signo + 1);
+ }
+
+ overflow = 1;
+ overflow_current = 0;
+ ngx_event_actions.process_events = ngx_rtsig_process_overflow;
+
+ return NGX_ERROR;
+
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "sigtimedwait() returned unexpected signal: %d", signo);
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_rtsig_process_overflow(ngx_cycle_t *cycle, ngx_msec_t timer,
+ ngx_uint_t flags)
+{
+ int name[2], rtsig_max, rtsig_nr, events, ready;
+ size_t len;
+ ngx_err_t err;
+ ngx_uint_t tested, n, i;
+ ngx_event_t *rev, *wev, **queue;
+ ngx_connection_t *c;
+ ngx_rtsig_conf_t *rtscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "rtsig process overflow");
+
+ rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);
+
+ tested = 0;
+
+ for ( ;; ) {
+
+ n = 0;
+ while (n < rtscf->overflow_events) {
+
+ if (overflow_current == cycle->connection_n) {
+ break;
+ }
+
+ c = cycle->files[overflow_current++];
+
+ if (c == NULL || c->fd == -1) {
+ continue;
+ }
+
+ events = 0;
+
+ if (c->read->active && c->read->handler) {
+ events |= POLLIN;
+ }
+
+ if (c->write->active && c->write->handler) {
+ events |= POLLOUT;
+ }
+
+ if (events == 0) {
+ continue;
+ }
+
+ overflow_list[n].fd = c->fd;
+ overflow_list[n].events = events;
+ overflow_list[n].revents = 0;
+ n++;
+ }
+
+ if (n == 0) {
+ break;
+ }
+
+ for ( ;; ) {
+ ready = poll(overflow_list, n, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "rtsig overflow poll:%d", ready);
+
+ if (ready == -1) {
+ err = ngx_errno;
+ ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,
+ cycle->log, 0,
+ "poll() failed while the overflow recover");
+
+ if (err == NGX_EINTR) {
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ if (ready <= 0) {
+ continue;
+ }
+
+ ngx_mutex_lock(ngx_posted_events_mutex);
+
+ for (i = 0; i < n; i++) {
+ c = cycle->files[overflow_list[i].fd];
+
+ if (c == NULL) {
+ continue;
+ }
+
+ rev = c->read;
+
+ if (rev->active
+ && !rev->closed
+ && rev->handler
+ && (overflow_list[i].revents
+ & (POLLIN|POLLERR|POLLHUP|POLLNVAL)))
+ {
+ tested++;
+
+ if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) {
+ rev->posted_ready = 1;
+
+ } else {
+ rev->ready = 1;
+ }
+
+ if (flags & NGX_POST_EVENTS) {
+ queue = (ngx_event_t **) (rev->accept ?
+ &ngx_posted_accept_events : &ngx_posted_events);
+
+ ngx_locked_post_event(rev, queue);
+
+ } else {
+ rev->handler(rev);
+ }
+ }
+
+ wev = c->write;
+
+ if (wev->active
+ && !wev->closed
+ && wev->handler
+ && (overflow_list[i].revents
+ & (POLLOUT|POLLERR|POLLHUP|POLLNVAL)))
+ {
+ tested++;
+
+ if (flags & NGX_POST_THREAD_EVENTS) {
+ wev->posted_ready = 1;
+
+ } else {
+ wev->ready = 1;
+ }
+
+ if (flags & NGX_POST_EVENTS) {
+ ngx_locked_post_event(wev, &ngx_posted_events);
+
+ } else {
+ wev->handler(wev);
+ }
+ }
+ }
+
+ ngx_mutex_unlock(ngx_posted_events_mutex);
+
+ if (tested >= rtscf->overflow_test) {
+
+ if (ngx_linux_rtsig_max) {
+
+ /*
+ * Check the current rt queue length to prevent
+ * the new overflow.
+ *
+ * learn the "/proc/sys/kernel/rtsig-max" value because
+ * it can be changed since the last checking
+ */
+
+ name[0] = CTL_KERN;
+ name[1] = KERN_RTSIGMAX;
+ len = sizeof(rtsig_max);
+
+ if (sysctl(name, 2, &rtsig_max, &len, NULL, 0) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, errno,
+ "sysctl(KERN_RTSIGMAX) failed");
+ return NGX_ERROR;
+ }
+
+ /* name[0] = CTL_KERN; */
+ name[1] = KERN_RTSIGNR;
+ len = sizeof(rtsig_nr);
+
+ if (sysctl(name, 2, &rtsig_nr, &len, NULL, 0) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, errno,
+ "sysctl(KERN_RTSIGNR) failed");
+ return NGX_ERROR;
+ }
+
+ /*
+ * drain the rt signal queue if the /"proc/sys/kernel/rtsig-nr"
+ * is bigger than
+ * "/proc/sys/kernel/rtsig-max" / "rtsig_overflow_threshold"
+ */
+
+ if (rtsig_max / (int) rtscf->overflow_threshold < rtsig_nr) {
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "rtsig queue state: %d/%d",
+ rtsig_nr, rtsig_max);
+ while (ngx_rtsig_process_events(cycle, 0, flags) == NGX_OK)
+ {
+ /* void */
+ }
+ }
+
+ } else {
+
+ /*
+ * Linux has not KERN_RTSIGMAX since 2.6.6-mm2
+ * so drain the rt signal queue unconditionally
+ */
+
+ while (ngx_rtsig_process_events(cycle, 0, flags) == NGX_OK) {
+ /* void */
+ }
+ }
+
+ tested = 0;
+ }
+ }
+
+ if (flags & NGX_UPDATE_TIME) {
+ ngx_time_update();
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "rt signal queue overflow recovered");
+
+ overflow = 0;
+ ngx_event_actions.process_events = ngx_rtsig_process_events;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_rtsig_create_conf(ngx_cycle_t *cycle)
+{
+ ngx_rtsig_conf_t *rtscf;
+
+ rtscf = ngx_palloc(cycle->pool, sizeof(ngx_rtsig_conf_t));
+ if (rtscf == NULL) {
+ return NULL;
+ }
+
+ rtscf->signo = NGX_CONF_UNSET;
+ rtscf->overflow_events = NGX_CONF_UNSET;
+ rtscf->overflow_test = NGX_CONF_UNSET;
+ rtscf->overflow_threshold = NGX_CONF_UNSET;
+
+ return rtscf;
+}
+
+
+static char *
+ngx_rtsig_init_conf(ngx_cycle_t *cycle, void *conf)
+{
+ ngx_rtsig_conf_t *rtscf = conf;
+
+ /* LinuxThreads use the first 3 RT signals */
+ ngx_conf_init_uint_value(rtscf->signo, SIGRTMIN + 10);
+
+ ngx_conf_init_uint_value(rtscf->overflow_events, 16);
+ ngx_conf_init_uint_value(rtscf->overflow_test, 32);
+ ngx_conf_init_uint_value(rtscf->overflow_threshold, 10);
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf, void *post, void *data)
+{
+ if (ngx_linux_rtsig_max) {
+ return ngx_conf_check_num_bounds(cf, post, data);
+ }
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "\"rtsig_overflow_threshold\" is not supported "
+ "since Linux 2.6.6-mm2, ignored");
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_select_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_select_module.c
new file mode 100644
index 00000000000..51690558e91
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_select_module.c
@@ -0,0 +1,435 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer);
+static void ngx_select_done(ngx_cycle_t *cycle);
+static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+ ngx_uint_t flags);
+static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle);
+static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf);
+
+
+static fd_set master_read_fd_set;
+static fd_set master_write_fd_set;
+static fd_set work_read_fd_set;
+static fd_set work_write_fd_set;
+
+static ngx_int_t max_fd;
+static ngx_uint_t nevents;
+
+static ngx_event_t **event_index;
+
+
+static ngx_str_t select_name = ngx_string("select");
+
+ngx_event_module_t ngx_select_module_ctx = {
+ &select_name,
+ NULL, /* create configuration */
+ ngx_select_init_conf, /* init configuration */
+
+ {
+ ngx_select_add_event, /* add an event */
+ ngx_select_del_event, /* delete an event */
+ ngx_select_add_event, /* enable an event */
+ ngx_select_del_event, /* disable an event */
+ NULL, /* add an connection */
+ NULL, /* delete an connection */
+ NULL, /* process the changes */
+ ngx_select_process_events, /* process the events */
+ ngx_select_init, /* init the events */
+ ngx_select_done /* done the events */
+ }
+
+};
+
+ngx_module_t ngx_select_module = {
+ NGX_MODULE_V1,
+ &ngx_select_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_EVENT_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer)
+{
+ ngx_event_t **index;
+
+ if (event_index == NULL) {
+ FD_ZERO(&master_read_fd_set);
+ FD_ZERO(&master_write_fd_set);
+ nevents = 0;
+ }
+
+ if (ngx_process >= NGX_PROCESS_WORKER
+ || cycle->old_cycle == NULL
+ || cycle->old_cycle->connection_n < cycle->connection_n)
+ {
+ index = ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n,
+ cycle->log);
+ if (index == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (event_index) {
+ ngx_memcpy(index, event_index, sizeof(ngx_event_t *) * nevents);
+ ngx_free(event_index);
+ }
+
+ event_index = index;
+ }
+
+ ngx_io = ngx_os_io;
+
+ ngx_event_actions = ngx_select_module_ctx.actions;
+
+ ngx_event_flags = NGX_USE_LEVEL_EVENT;
+
+ max_fd = -1;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_select_done(ngx_cycle_t *cycle)
+{
+ ngx_free(event_index);
+
+ event_index = NULL;
+}
+
+
+static ngx_int_t
+ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ ngx_connection_t *c;
+
+ c = ev->data;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "select add event fd:%d ev:%i", c->fd, event);
+
+ if (ev->index != NGX_INVALID_INDEX) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+ "select event fd:%d ev:%i is already set", c->fd, event);
+ return NGX_OK;
+ }
+
+ if ((event == NGX_READ_EVENT && ev->write)
+ || (event == NGX_WRITE_EVENT && !ev->write))
+ {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+ "invalid select %s event fd:%d ev:%i",
+ ev->write ? "write" : "read", c->fd, event);
+ return NGX_ERROR;
+ }
+
+ if (event == NGX_READ_EVENT) {
+ FD_SET(c->fd, &master_read_fd_set);
+
+ } else if (event == NGX_WRITE_EVENT) {
+ FD_SET(c->fd, &master_write_fd_set);
+ }
+
+ if (max_fd != -1 && max_fd < c->fd) {
+ max_fd = c->fd;
+ }
+
+ ev->active = 1;
+
+ event_index[nevents] = ev;
+ ev->index = nevents;
+ nevents++;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ ngx_event_t *e;
+ ngx_connection_t *c;
+
+ c = ev->data;
+
+ ev->active = 0;
+
+ if (ev->index == NGX_INVALID_INDEX) {
+ return NGX_OK;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "select del event fd:%d ev:%i", c->fd, event);
+
+ if (event == NGX_READ_EVENT) {
+ FD_CLR(c->fd, &master_read_fd_set);
+
+ } else if (event == NGX_WRITE_EVENT) {
+ FD_CLR(c->fd, &master_write_fd_set);
+ }
+
+ if (max_fd == c->fd) {
+ max_fd = -1;
+ }
+
+ if (ev->index < --nevents) {
+ e = event_index[nevents];
+ event_index[ev->index] = e;
+ e->index = ev->index;
+ }
+
+ ev->index = NGX_INVALID_INDEX;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+ ngx_uint_t flags)
+{
+ int ready, nready;
+ ngx_err_t err;
+ ngx_uint_t i, found;
+ ngx_event_t *ev, **queue;
+ struct timeval tv, *tp;
+ ngx_connection_t *c;
+
+ if (max_fd == -1) {
+ for (i = 0; i < nevents; i++) {
+ c = event_index[i]->data;
+ if (max_fd < c->fd) {
+ max_fd = c->fd;
+ }
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "change max_fd: %d", max_fd);
+ }
+
+#if (NGX_DEBUG)
+ if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {
+ for (i = 0; i < nevents; i++) {
+ ev = event_index[i];
+ c = ev->data;
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "select event: fd:%d wr:%d", c->fd, ev->write);
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "max_fd: %d", max_fd);
+ }
+#endif
+
+ if (timer == NGX_TIMER_INFINITE) {
+ tp = NULL;
+
+ } else {
+ tv.tv_sec = (long) (timer / 1000);
+ tv.tv_usec = (long) ((timer % 1000) * 1000);
+ tp = &tv;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "select timer: %M", timer);
+
+ work_read_fd_set = master_read_fd_set;
+ work_write_fd_set = master_write_fd_set;
+
+ ready = select(max_fd + 1, &work_read_fd_set, &work_write_fd_set, NULL, tp);
+
+ err = (ready == -1) ? ngx_errno : 0;
+
+ if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
+ ngx_time_update();
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "select ready %d", ready);
+
+ if (err) {
+ ngx_uint_t level;
+
+ if (err == NGX_EINTR) {
+
+ if (ngx_event_timer_alarm) {
+ ngx_event_timer_alarm = 0;
+ return NGX_OK;
+ }
+
+ level = NGX_LOG_INFO;
+
+ } else {
+ level = NGX_LOG_ALERT;
+ }
+
+ ngx_log_error(level, cycle->log, err, "select() failed");
+
+ if (err == NGX_EBADF) {
+ ngx_select_repair_fd_sets(cycle);
+ }
+
+ return NGX_ERROR;
+ }
+
+ if (ready == 0) {
+ if (timer != NGX_TIMER_INFINITE) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "select() returned no events without timeout");
+ return NGX_ERROR;
+ }
+
+ ngx_mutex_lock(ngx_posted_events_mutex);
+
+ nready = 0;
+
+ for (i = 0; i < nevents; i++) {
+ ev = event_index[i];
+ c = ev->data;
+ found = 0;
+
+ if (ev->write) {
+ if (FD_ISSET(c->fd, &work_write_fd_set)) {
+ found = 1;
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "select write %d", c->fd);
+ }
+
+ } else {
+ if (FD_ISSET(c->fd, &work_read_fd_set)) {
+ found = 1;
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "select read %d", c->fd);
+ }
+ }
+
+ if (found) {
+ ev->ready = 1;
+
+ queue = (ngx_event_t **) (ev->accept ? &ngx_posted_accept_events:
+ &ngx_posted_events);
+ ngx_locked_post_event(ev, queue);
+
+ nready++;
+ }
+ }
+
+ ngx_mutex_unlock(ngx_posted_events_mutex);
+
+ if (ready != nready) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "select ready != events: %d:%d", ready, nready);
+
+ ngx_select_repair_fd_sets(cycle);
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_select_repair_fd_sets(ngx_cycle_t *cycle)
+{
+ int n;
+ socklen_t len;
+ ngx_err_t err;
+ ngx_socket_t s;
+
+ for (s = 0; s <= max_fd; s++) {
+
+ if (FD_ISSET(s, &master_read_fd_set) == 0) {
+ continue;
+ }
+
+ len = sizeof(int);
+
+ if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {
+ err = ngx_socket_errno;
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+ "invalid descriptor #%d in read fd_set", s);
+
+ FD_CLR(s, &master_read_fd_set);
+ }
+ }
+
+ for (s = 0; s <= max_fd; s++) {
+
+ if (FD_ISSET(s, &master_write_fd_set) == 0) {
+ continue;
+ }
+
+ len = sizeof(int);
+
+ if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {
+ err = ngx_socket_errno;
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+ "invalid descriptor #%d in write fd_set", s);
+
+ FD_CLR(s, &master_write_fd_set);
+ }
+ }
+
+ max_fd = -1;
+}
+
+
+static char *
+ngx_select_init_conf(ngx_cycle_t *cycle, void *conf)
+{
+ ngx_event_conf_t *ecf;
+
+ ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
+
+ if (ecf->use != ngx_select_module.ctx_index) {
+ return NGX_CONF_OK;
+ }
+
+ /* disable warning: the default FD_SETSIZE is 1024U in FreeBSD 5.x */
+
+ if (cycle->connection_n > FD_SETSIZE) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+ "the maximum number of files "
+ "supported by select() is %ud", FD_SETSIZE);
+ return NGX_CONF_ERROR;
+ }
+
+#if (NGX_THREADS)
+
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+ "select() is not supported in the threaded mode");
+ return NGX_CONF_ERROR;
+
+#else
+
+ return NGX_CONF_OK;
+
+#endif
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_win32_select_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_win32_select_module.c
new file mode 100644
index 00000000000..eb5382d4e0b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/modules/ngx_win32_select_module.c
@@ -0,0 +1,400 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+static ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer);
+static void ngx_select_done(ngx_cycle_t *cycle);
+static ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event,
+ ngx_uint_t flags);
+static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+ ngx_uint_t flags);
+static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle);
+static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf);
+
+
+static fd_set master_read_fd_set;
+static fd_set master_write_fd_set;
+static fd_set work_read_fd_set;
+static fd_set work_write_fd_set;
+
+static ngx_uint_t max_read;
+static ngx_uint_t max_write;
+static ngx_uint_t nevents;
+
+static ngx_event_t **event_index;
+
+
+static ngx_str_t select_name = ngx_string("select");
+
+ngx_event_module_t ngx_select_module_ctx = {
+ &select_name,
+ NULL, /* create configuration */
+ ngx_select_init_conf, /* init configuration */
+
+ {
+ ngx_select_add_event, /* add an event */
+ ngx_select_del_event, /* delete an event */
+ ngx_select_add_event, /* enable an event */
+ ngx_select_del_event, /* disable an event */
+ NULL, /* add an connection */
+ NULL, /* delete an connection */
+ NULL, /* process the changes */
+ ngx_select_process_events, /* process the events */
+ ngx_select_init, /* init the events */
+ ngx_select_done /* done the events */
+ }
+
+};
+
+ngx_module_t ngx_select_module = {
+ NGX_MODULE_V1,
+ &ngx_select_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_EVENT_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer)
+{
+ ngx_event_t **index;
+
+ if (event_index == NULL) {
+ FD_ZERO(&master_read_fd_set);
+ FD_ZERO(&master_write_fd_set);
+ nevents = 0;
+ }
+
+ if (ngx_process >= NGX_PROCESS_WORKER
+ || cycle->old_cycle == NULL
+ || cycle->old_cycle->connection_n < cycle->connection_n)
+ {
+ index = ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n,
+ cycle->log);
+ if (index == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (event_index) {
+ ngx_memcpy(index, event_index, sizeof(ngx_event_t *) * nevents);
+ ngx_free(event_index);
+ }
+
+ event_index = index;
+ }
+
+ ngx_io = ngx_os_io;
+
+ ngx_event_actions = ngx_select_module_ctx.actions;
+
+ ngx_event_flags = NGX_USE_LEVEL_EVENT;
+
+ max_read = 0;
+ max_write = 0;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_select_done(ngx_cycle_t *cycle)
+{
+ ngx_free(event_index);
+
+ event_index = NULL;
+}
+
+
+static ngx_int_t
+ngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ ngx_connection_t *c;
+
+ c = ev->data;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "select add event fd:%d ev:%i", c->fd, event);
+
+ if (ev->index != NGX_INVALID_INDEX) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+ "select event fd:%d ev:%i is already set", c->fd, event);
+ return NGX_OK;
+ }
+
+ if ((event == NGX_READ_EVENT && ev->write)
+ || (event == NGX_WRITE_EVENT && !ev->write))
+ {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+ "invalid select %s event fd:%d ev:%i",
+ ev->write ? "write" : "read", c->fd, event);
+ return NGX_ERROR;
+ }
+
+ if ((event == NGX_READ_EVENT && max_read >= FD_SETSIZE)
+ || (event == NGX_WRITE_EVENT && max_write >= FD_SETSIZE))
+ {
+ ngx_log_error(NGX_LOG_ERR, ev->log, 0,
+ "maximum number of descriptors "
+ "supported by select() is %d", FD_SETSIZE);
+ return NGX_ERROR;
+ }
+
+ if (event == NGX_READ_EVENT) {
+ FD_SET(c->fd, &master_read_fd_set);
+ max_read++;
+
+ } else if (event == NGX_WRITE_EVENT) {
+ FD_SET(c->fd, &master_write_fd_set);
+ max_write++;
+ }
+
+ ev->active = 1;
+
+ event_index[nevents] = ev;
+ ev->index = nevents;
+ nevents++;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
+{
+ ngx_event_t *e;
+ ngx_connection_t *c;
+
+ c = ev->data;
+
+ ev->active = 0;
+
+ if (ev->index == NGX_INVALID_INDEX) {
+ return NGX_OK;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "select del event fd:%d ev:%i", c->fd, event);
+
+ if (event == NGX_READ_EVENT) {
+ FD_CLR(c->fd, &master_read_fd_set);
+ max_read--;
+
+ } else if (event == NGX_WRITE_EVENT) {
+ FD_CLR(c->fd, &master_write_fd_set);
+ max_write--;
+ }
+
+ if (ev->index < --nevents) {
+ e = event_index[nevents];
+ event_index[ev->index] = e;
+ e->index = ev->index;
+ }
+
+ ev->index = NGX_INVALID_INDEX;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
+ ngx_uint_t flags)
+{
+ int ready, nready;
+ ngx_err_t err;
+ ngx_uint_t i, found;
+ ngx_event_t *ev, **queue;
+ struct timeval tv, *tp;
+ ngx_connection_t *c;
+
+#if (NGX_DEBUG)
+ if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {
+ for (i = 0; i < nevents; i++) {
+ ev = event_index[i];
+ c = ev->data;
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "select event: fd:%d wr:%d", c->fd, ev->write);
+ }
+ }
+#endif
+
+ if (timer == NGX_TIMER_INFINITE) {
+ tp = NULL;
+
+ } else {
+ tv.tv_sec = (long) (timer / 1000);
+ tv.tv_usec = (long) ((timer % 1000) * 1000);
+ tp = &tv;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "select timer: %M", timer);
+
+ work_read_fd_set = master_read_fd_set;
+ work_write_fd_set = master_write_fd_set;
+
+ if (max_read || max_write) {
+ ready = select(0, &work_read_fd_set, &work_write_fd_set, NULL, tp);
+
+ } else {
+
+ /*
+ * Winsock select() requires that at least one descriptor set must be
+ * be non-null, and any non-null descriptor set must contain at least
+ * one handle to a socket. Otherwise select() returns WSAEINVAL.
+ */
+
+ ngx_msleep(timer);
+
+ ready = 0;
+ }
+
+ err = (ready == -1) ? ngx_socket_errno : 0;
+
+ if (flags & NGX_UPDATE_TIME) {
+ ngx_time_update();
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "select ready %d", ready);
+
+ if (err) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "select() failed");
+
+ if (err == WSAENOTSOCK) {
+ ngx_select_repair_fd_sets(cycle);
+ }
+
+ return NGX_ERROR;
+ }
+
+ if (ready == 0) {
+ if (timer != NGX_TIMER_INFINITE) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "select() returned no events without timeout");
+ return NGX_ERROR;
+ }
+
+ ngx_mutex_lock(ngx_posted_events_mutex);
+
+ nready = 0;
+
+ for (i = 0; i < nevents; i++) {
+ ev = event_index[i];
+ c = ev->data;
+ found = 0;
+
+ if (ev->write) {
+ if (FD_ISSET(c->fd, &work_write_fd_set)) {
+ found = 1;
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "select write %d", c->fd);
+ }
+
+ } else {
+ if (FD_ISSET(c->fd, &work_read_fd_set)) {
+ found = 1;
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "select read %d", c->fd);
+ }
+ }
+
+ if (found) {
+ ev->ready = 1;
+
+ queue = (ngx_event_t **) (ev->accept ? &ngx_posted_accept_events:
+ &ngx_posted_events);
+ ngx_locked_post_event(ev, queue);
+
+ nready++;
+ }
+ }
+
+ ngx_mutex_unlock(ngx_posted_events_mutex);
+
+ if (ready != nready) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "select ready != events: %d:%d", ready, nready);
+
+ ngx_select_repair_fd_sets(cycle);
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_select_repair_fd_sets(ngx_cycle_t *cycle)
+{
+ int n;
+ u_int i;
+ socklen_t len;
+ ngx_err_t err;
+ ngx_socket_t s;
+
+ for (i = 0; i < master_read_fd_set.fd_count; i++) {
+
+ s = master_read_fd_set.fd_array[i];
+ len = sizeof(int);
+
+ if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {
+ err = ngx_socket_errno;
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+ "invalid descriptor #%d in read fd_set", s);
+
+ FD_CLR(s, &master_read_fd_set);
+ }
+ }
+
+ for (i = 0; i < master_write_fd_set.fd_count; i++) {
+
+ s = master_write_fd_set.fd_array[i];
+ len = sizeof(int);
+
+ if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {
+ err = ngx_socket_errno;
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+ "invalid descriptor #%d in write fd_set", s);
+
+ FD_CLR(s, &master_write_fd_set);
+ }
+ }
+}
+
+
+static char *
+ngx_select_init_conf(ngx_cycle_t *cycle, void *conf)
+{
+ ngx_event_conf_t *ecf;
+
+ ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
+
+ if (ecf->use != ngx_select_module.ctx_index) {
+ return NGX_CONF_OK;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event.c
new file mode 100644
index 00000000000..e2857f0c05c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event.c
@@ -0,0 +1,1339 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#define DEFAULT_CONNECTIONS 512
+
+
+extern ngx_module_t ngx_kqueue_module;
+extern ngx_module_t ngx_eventport_module;
+extern ngx_module_t ngx_devpoll_module;
+extern ngx_module_t ngx_epoll_module;
+extern ngx_module_t ngx_rtsig_module;
+extern ngx_module_t ngx_select_module;
+
+
+static char *ngx_event_init_conf(ngx_cycle_t *cycle, void *conf);
+static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle);
+static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle);
+static char *ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+
+static char *ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+static void *ngx_event_core_create_conf(ngx_cycle_t *cycle);
+static char *ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf);
+
+
+static ngx_uint_t ngx_timer_resolution;
+sig_atomic_t ngx_event_timer_alarm;
+
+static ngx_uint_t ngx_event_max_module;
+
+ngx_uint_t ngx_event_flags;
+ngx_event_actions_t ngx_event_actions;
+
+
+static ngx_atomic_t connection_counter = 1;
+ngx_atomic_t *ngx_connection_counter = &connection_counter;
+
+
+ngx_atomic_t *ngx_accept_mutex_ptr;
+ngx_shmtx_t ngx_accept_mutex;
+ngx_uint_t ngx_use_accept_mutex;
+ngx_uint_t ngx_accept_events;
+ngx_uint_t ngx_accept_mutex_held;
+ngx_msec_t ngx_accept_mutex_delay;
+ngx_int_t ngx_accept_disabled;
+
+
+#if (NGX_STAT_STUB)
+
+ngx_atomic_t ngx_stat_accepted0;
+ngx_atomic_t *ngx_stat_accepted = &ngx_stat_accepted0;
+ngx_atomic_t ngx_stat_handled0;
+ngx_atomic_t *ngx_stat_handled = &ngx_stat_handled0;
+ngx_atomic_t ngx_stat_requests0;
+ngx_atomic_t *ngx_stat_requests = &ngx_stat_requests0;
+ngx_atomic_t ngx_stat_active0;
+ngx_atomic_t *ngx_stat_active = &ngx_stat_active0;
+ngx_atomic_t ngx_stat_reading0;
+ngx_atomic_t *ngx_stat_reading = &ngx_stat_reading0;
+ngx_atomic_t ngx_stat_writing0;
+ngx_atomic_t *ngx_stat_writing = &ngx_stat_writing0;
+ngx_atomic_t ngx_stat_waiting0;
+ngx_atomic_t *ngx_stat_waiting = &ngx_stat_waiting0;
+
+#endif
+
+
+
+static ngx_command_t ngx_events_commands[] = {
+
+ { ngx_string("events"),
+ NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_events_block,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_core_module_t ngx_events_module_ctx = {
+ ngx_string("events"),
+ NULL,
+ ngx_event_init_conf
+};
+
+
+ngx_module_t ngx_events_module = {
+ NGX_MODULE_V1,
+ &ngx_events_module_ctx, /* module context */
+ ngx_events_commands, /* module directives */
+ NGX_CORE_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t event_core_name = ngx_string("event_core");
+
+
+static ngx_command_t ngx_event_core_commands[] = {
+
+ { ngx_string("worker_connections"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_event_connections,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("connections"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_event_connections,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("use"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_event_use,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("multi_accept"),
+ NGX_EVENT_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ 0,
+ offsetof(ngx_event_conf_t, multi_accept),
+ NULL },
+
+ { ngx_string("accept_mutex"),
+ NGX_EVENT_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ 0,
+ offsetof(ngx_event_conf_t, accept_mutex),
+ NULL },
+
+ { ngx_string("accept_mutex_delay"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ 0,
+ offsetof(ngx_event_conf_t, accept_mutex_delay),
+ NULL },
+
+ { ngx_string("debug_connection"),
+ NGX_EVENT_CONF|NGX_CONF_TAKE1,
+ ngx_event_debug_connection,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+ngx_event_module_t ngx_event_core_module_ctx = {
+ &event_core_name,
+ ngx_event_core_create_conf, /* create configuration */
+ ngx_event_core_init_conf, /* init configuration */
+
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+
+ngx_module_t ngx_event_core_module = {
+ NGX_MODULE_V1,
+ &ngx_event_core_module_ctx, /* module context */
+ ngx_event_core_commands, /* module directives */
+ NGX_EVENT_MODULE, /* module type */
+ NULL, /* init master */
+ ngx_event_module_init, /* init module */
+ ngx_event_process_init, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+void
+ngx_process_events_and_timers(ngx_cycle_t *cycle)
+{
+ ngx_uint_t flags;
+ ngx_msec_t timer, delta;
+
+ if (ngx_timer_resolution) {
+ timer = NGX_TIMER_INFINITE;
+ flags = 0;
+
+ } else {
+ timer = ngx_event_find_timer();
+ flags = NGX_UPDATE_TIME;
+
+#if (NGX_THREADS)
+
+ if (timer == NGX_TIMER_INFINITE || timer > 500) {
+ timer = 500;
+ }
+
+#endif
+ }
+
+ if (ngx_use_accept_mutex) {
+ if (ngx_accept_disabled > 0) {
+ ngx_accept_disabled--;
+
+ } else {
+ if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
+ return;
+ }
+
+ if (ngx_accept_mutex_held) {
+ flags |= NGX_POST_EVENTS;
+
+ } else {
+ if (timer == NGX_TIMER_INFINITE
+ || timer > ngx_accept_mutex_delay)
+ {
+ timer = ngx_accept_mutex_delay;
+ }
+ }
+ }
+ }
+
+ delta = ngx_current_msec;
+
+ (void) ngx_process_events(cycle, timer, flags);
+
+ delta = ngx_current_msec - delta;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "timer delta: %M", delta);
+
+ if (ngx_posted_accept_events) {
+ ngx_event_process_posted(cycle, &ngx_posted_accept_events);
+ }
+
+ if (ngx_accept_mutex_held) {
+ ngx_shmtx_unlock(&ngx_accept_mutex);
+ }
+
+ if (delta) {
+ ngx_event_expire_timers();
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "posted events %p", ngx_posted_events);
+
+ if (ngx_posted_events) {
+ if (ngx_threaded) {
+ ngx_wakeup_worker_thread(cycle);
+
+ } else {
+ ngx_event_process_posted(cycle, &ngx_posted_events);
+ }
+ }
+}
+
+
+ngx_int_t
+ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags)
+{
+ if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
+
+ /* kqueue, epoll */
+
+ if (!rev->active && !rev->ready) {
+ if (ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+
+ } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {
+
+ /* select, poll, /dev/poll */
+
+ if (!rev->active && !rev->ready) {
+ if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+
+ if (rev->active && (rev->ready || (flags & NGX_CLOSE_EVENT))) {
+ if (ngx_del_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT | flags)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+
+ } else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {
+
+ /* event ports */
+
+ if (!rev->active && !rev->ready) {
+ if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+
+ if (rev->oneshot && !rev->ready) {
+ if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+ }
+
+ /* aio, iocp, rtsig */
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_handle_write_event(ngx_event_t *wev, size_t lowat)
+{
+ ngx_connection_t *c;
+
+ if (lowat) {
+ c = wev->data;
+
+ if (ngx_send_lowat(c, lowat) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
+
+ /* kqueue, epoll */
+
+ if (!wev->active && !wev->ready) {
+ if (ngx_add_event(wev, NGX_WRITE_EVENT,
+ NGX_CLEAR_EVENT | (lowat ? NGX_LOWAT_EVENT : 0))
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+
+ } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {
+
+ /* select, poll, /dev/poll */
+
+ if (!wev->active && !wev->ready) {
+ if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+
+ if (wev->active && wev->ready) {
+ if (ngx_del_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+
+ } else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {
+
+ /* event ports */
+
+ if (!wev->active && !wev->ready) {
+ if (ngx_add_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+
+ if (wev->oneshot && wev->ready) {
+ if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+ }
+
+ /* aio, iocp, rtsig */
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_event_init_conf(ngx_cycle_t *cycle, void *conf)
+{
+ if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+ "no \"events\" section in configuration");
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_event_module_init(ngx_cycle_t *cycle)
+{
+ void ***cf;
+ u_char *shared;
+ size_t size, cl;
+ ngx_shm_t shm;
+ ngx_time_t *tp;
+ ngx_core_conf_t *ccf;
+ ngx_event_conf_t *ecf;
+
+ cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);
+ ecf = (*cf)[ngx_event_core_module.ctx_index];
+
+ if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
+ "using the \"%s\" event method", ecf->name);
+ }
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+ ngx_timer_resolution = ccf->timer_resolution;
+
+#if !(NGX_WIN32)
+ {
+ ngx_int_t limit;
+ struct rlimit rlmt;
+
+ if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "getrlimit(RLIMIT_NOFILE) failed, ignored");
+
+ } else {
+ if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur
+ && (ccf->rlimit_nofile == NGX_CONF_UNSET
+ || ecf->connections > (ngx_uint_t) ccf->rlimit_nofile))
+ {
+ limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ?
+ (ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile;
+
+ ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
+ "%ui worker_connections exceed "
+ "open file resource limit: %i",
+ ecf->connections, limit);
+ }
+ }
+ }
+#endif /* !(NGX_WIN32) */
+
+
+ if (ccf->master == 0) {
+ return NGX_OK;
+ }
+
+ if (ngx_accept_mutex_ptr) {
+ return NGX_OK;
+ }
+
+
+ /* cl should be equal to or greater than cache line size */
+
+ cl = 128;
+
+ size = cl /* ngx_accept_mutex */
+ + cl /* ngx_connection_counter */
+ + cl; /* ngx_temp_number */
+
+#if (NGX_STAT_STUB)
+
+ size += cl /* ngx_stat_accepted */
+ + cl /* ngx_stat_handled */
+ + cl /* ngx_stat_requests */
+ + cl /* ngx_stat_active */
+ + cl /* ngx_stat_reading */
+ + cl /* ngx_stat_writing */
+ + cl; /* ngx_stat_waiting */
+
+#endif
+
+ shm.size = size;
+ shm.name.len = sizeof("nginx_shared_zone");
+ shm.name.data = (u_char *) "nginx_shared_zone";
+ shm.log = cycle->log;
+
+ if (ngx_shm_alloc(&shm) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ shared = shm.addr;
+
+ ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;
+ ngx_accept_mutex.spin = (ngx_uint_t) -1;
+
+ if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared,
+ cycle->lock_file.data)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);
+
+ (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "counter: %p, %d",
+ ngx_connection_counter, *ngx_connection_counter);
+
+ ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);
+
+ tp = ngx_timeofday();
+
+ ngx_random_number = (tp->msec << 16) + ngx_pid;
+
+#if (NGX_STAT_STUB)
+
+ ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl);
+ ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl);
+ ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl);
+ ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);
+ ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);
+ ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);
+ ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);
+
+#endif
+
+ return NGX_OK;
+}
+
+
+#if !(NGX_WIN32)
+
+static void
+ngx_timer_signal_handler(int signo)
+{
+ ngx_event_timer_alarm = 1;
+
+#if 1
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, "timer signal");
+#endif
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_event_process_init(ngx_cycle_t *cycle)
+{
+ ngx_uint_t m, i;
+ ngx_event_t *rev, *wev;
+ ngx_listening_t *ls;
+ ngx_connection_t *c, *next, *old;
+ ngx_core_conf_t *ccf;
+ ngx_event_conf_t *ecf;
+ ngx_event_module_t *module;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+ ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);
+
+ if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
+ ngx_use_accept_mutex = 1;
+ ngx_accept_mutex_held = 0;
+ ngx_accept_mutex_delay = ecf->accept_mutex_delay;
+
+ } else {
+ ngx_use_accept_mutex = 0;
+ }
+
+#if (NGX_WIN32)
+
+ /*
+ * disable accept mutex on win32 as it may cause deadlock if
+ * grabbed by a process which can't accept connections
+ */
+
+ ngx_use_accept_mutex = 0;
+
+#endif
+
+#if (NGX_THREADS)
+ ngx_posted_events_mutex = ngx_mutex_init(cycle->log, 0);
+ if (ngx_posted_events_mutex == NULL) {
+ return NGX_ERROR;
+ }
+#endif
+
+ if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
+ continue;
+ }
+
+ if (ngx_modules[m]->ctx_index != ecf->use) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+
+ if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
+ /* fatal */
+ exit(2);
+ }
+
+ break;
+ }
+
+#if !(NGX_WIN32)
+
+ if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
+ struct sigaction sa;
+ struct itimerval itv;
+
+ ngx_memzero(&sa, sizeof(struct sigaction));
+ sa.sa_handler = ngx_timer_signal_handler;
+ sigemptyset(&sa.sa_mask);
+
+ if (sigaction(SIGALRM, &sa, NULL) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "sigaction(SIGALRM) failed");
+ return NGX_ERROR;
+ }
+
+ itv.it_interval.tv_sec = ngx_timer_resolution / 1000;
+ itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;
+ itv.it_value.tv_sec = ngx_timer_resolution / 1000;
+ itv.it_value.tv_usec = (ngx_timer_resolution % 1000 ) * 1000;
+
+ if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "setitimer() failed");
+ }
+ }
+
+ if (ngx_event_flags & NGX_USE_FD_EVENT) {
+ struct rlimit rlmt;
+
+ if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "getrlimit(RLIMIT_NOFILE) failed");
+ return NGX_ERROR;
+ }
+
+ cycle->files_n = (ngx_uint_t) rlmt.rlim_cur;
+
+ cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n,
+ cycle->log);
+ if (cycle->files == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+#endif
+
+ cycle->connections =
+ ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
+ if (cycle->connections == NULL) {
+ return NGX_ERROR;
+ }
+
+ c = cycle->connections;
+
+ cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
+ cycle->log);
+ if (cycle->read_events == NULL) {
+ return NGX_ERROR;
+ }
+
+ rev = cycle->read_events;
+ for (i = 0; i < cycle->connection_n; i++) {
+ rev[i].closed = 1;
+ rev[i].instance = 1;
+#if (NGX_THREADS)
+ rev[i].lock = &c[i].lock;
+ rev[i].own_lock = &c[i].lock;
+#endif
+ }
+
+ cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
+ cycle->log);
+ if (cycle->write_events == NULL) {
+ return NGX_ERROR;
+ }
+
+ wev = cycle->write_events;
+ for (i = 0; i < cycle->connection_n; i++) {
+ wev[i].closed = 1;
+#if (NGX_THREADS)
+ wev[i].lock = &c[i].lock;
+ wev[i].own_lock = &c[i].lock;
+#endif
+ }
+
+ i = cycle->connection_n;
+ next = NULL;
+
+ do {
+ i--;
+
+ c[i].data = next;
+ c[i].read = &cycle->read_events[i];
+ c[i].write = &cycle->write_events[i];
+ c[i].fd = (ngx_socket_t) -1;
+
+ next = &c[i];
+
+#if (NGX_THREADS)
+ c[i].lock = 0;
+#endif
+ } while (i);
+
+ cycle->free_connections = next;
+ cycle->free_connection_n = cycle->connection_n;
+
+ /* for each listening socket */
+
+ ls = cycle->listening.elts;
+ for (i = 0; i < cycle->listening.nelts; i++) {
+
+ c = ngx_get_connection(ls[i].fd, cycle->log);
+
+ if (c == NULL) {
+ return NGX_ERROR;
+ }
+
+ c->log = &ls[i].log;
+
+ c->listening = &ls[i];
+ ls[i].connection = c;
+
+ rev = c->read;
+
+ rev->log = c->log;
+ rev->accept = 1;
+
+#if (NGX_HAVE_DEFERRED_ACCEPT)
+ rev->deferred_accept = ls[i].deferred_accept;
+#endif
+
+ if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
+ if (ls[i].previous) {
+
+ /*
+ * delete the old accept events that were bound to
+ * the old cycle read events array
+ */
+
+ old = ls[i].previous->connection;
+
+ if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+
+ old->fd = (ngx_socket_t) -1;
+ }
+ }
+
+#if (NGX_WIN32)
+
+ if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
+ ngx_iocp_conf_t *iocpcf;
+
+ rev->handler = ngx_event_acceptex;
+
+ if (ngx_use_accept_mutex) {
+ continue;
+ }
+
+ if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ ls[i].log.handler = ngx_acceptex_log_error;
+
+ iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module);
+ if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+
+ } else {
+ rev->handler = ngx_event_accept;
+
+ if (ngx_use_accept_mutex) {
+ continue;
+ }
+
+ if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+
+#else
+
+ rev->handler = ngx_event_accept;
+
+ if (ngx_use_accept_mutex) {
+ continue;
+ }
+
+ if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
+ if (ngx_add_conn(c) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+
+#endif
+
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_send_lowat(ngx_connection_t *c, size_t lowat)
+{
+ int sndlowat;
+
+#if (NGX_HAVE_LOWAT_EVENT)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ c->write->available = lowat;
+ return NGX_OK;
+ }
+
+#endif
+
+ if (lowat == 0 || c->sndlowat) {
+ return NGX_OK;
+ }
+
+ sndlowat = (int) lowat;
+
+ if (setsockopt(c->fd, SOL_SOCKET, SO_SNDLOWAT,
+ (const void *) &sndlowat, sizeof(int))
+ == -1)
+ {
+ ngx_connection_error(c, ngx_socket_errno,
+ "setsockopt(SO_SNDLOWAT) failed");
+ return NGX_ERROR;
+ }
+
+ c->sndlowat = 1;
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ void ***ctx;
+ ngx_uint_t i;
+ ngx_conf_t pcf;
+ ngx_event_module_t *m;
+
+ if (*(void **) conf) {
+ return "is duplicate";
+ }
+
+ /* count the number of the event modules and set up their indices */
+
+ ngx_event_max_module = 0;
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
+ continue;
+ }
+
+ ngx_modules[i]->ctx_index = ngx_event_max_module++;
+ }
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(void *));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
+ if (*ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *(void **) conf = ctx;
+
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
+ continue;
+ }
+
+ m = ngx_modules[i]->ctx;
+
+ if (m->create_conf) {
+ (*ctx)[ngx_modules[i]->ctx_index] = m->create_conf(cf->cycle);
+ if ((*ctx)[ngx_modules[i]->ctx_index] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+ }
+
+ pcf = *cf;
+ cf->ctx = ctx;
+ cf->module_type = NGX_EVENT_MODULE;
+ cf->cmd_type = NGX_EVENT_CONF;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = pcf;
+
+ if (rv != NGX_CONF_OK)
+ return rv;
+
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
+ continue;
+ }
+
+ m = ngx_modules[i]->ctx;
+
+ if (m->init_conf) {
+ rv = m->init_conf(cf->cycle, (*ctx)[ngx_modules[i]->ctx_index]);
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_event_conf_t *ecf = conf;
+
+ ngx_str_t *value;
+
+ if (ecf->connections != NGX_CONF_UNSET_UINT) {
+ return "is duplicate";
+ }
+
+ if (ngx_strcmp(cmd->name.data, "connections") == 0) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "the \"connections\" directive is deprecated, "
+ "use the \"worker_connections\" directive instead");
+ }
+
+ value = cf->args->elts;
+ ecf->connections = ngx_atoi(value[1].data, value[1].len);
+ if (ecf->connections == (ngx_uint_t) NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid number \"%V\"", &value[1]);
+
+ return NGX_CONF_ERROR;
+ }
+
+ cf->cycle->connection_n = ecf->connections;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_event_conf_t *ecf = conf;
+
+ ngx_int_t m;
+ ngx_str_t *value;
+ ngx_event_conf_t *old_ecf;
+ ngx_event_module_t *module;
+
+ if (ecf->use != NGX_CONF_UNSET_UINT) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (cf->cycle->old_cycle->conf_ctx) {
+ old_ecf = ngx_event_get_conf(cf->cycle->old_cycle->conf_ctx,
+ ngx_event_core_module);
+ } else {
+ old_ecf = NULL;
+ }
+
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_EVENT_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+ if (module->name->len == value[1].len) {
+ if (ngx_strcmp(module->name->data, value[1].data) == 0) {
+ ecf->use = ngx_modules[m]->ctx_index;
+ ecf->name = module->name->data;
+
+ if (ngx_process == NGX_PROCESS_SINGLE
+ && old_ecf
+ && old_ecf->use != ecf->use)
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "when the server runs without a master process "
+ "the \"%V\" event type must be the same as "
+ "in previous configuration - \"%s\" "
+ "and it cannot be changed on the fly, "
+ "to change it you need to stop server "
+ "and start it again",
+ &value[1], old_ecf->name);
+
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+ }
+ }
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid event type \"%V\"", &value[1]);
+
+ return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+#if (NGX_DEBUG)
+ ngx_event_conf_t *ecf = conf;
+
+ ngx_int_t rc;
+ ngx_str_t *value;
+ ngx_url_t u;
+ ngx_cidr_t c, *cidr;
+ ngx_uint_t i;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ value = cf->args->elts;
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+ if (ngx_strcmp(value[1].data, "unix:") == 0) {
+ cidr = ngx_array_push(&ecf->debug_connection);
+ if (cidr == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ cidr->family = AF_UNIX;
+ return NGX_CONF_OK;
+ }
+
+#endif
+
+ rc = ngx_ptocidr(&value[1], &c);
+
+ if (rc != NGX_ERROR) {
+ if (rc == NGX_DONE) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "low address bits of %V are meaningless",
+ &value[1]);
+ }
+
+ cidr = ngx_array_push(&ecf->debug_connection);
+ if (cidr == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *cidr = c;
+
+ return NGX_CONF_OK;
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+ u.host = value[1];
+
+ if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in debug_connection \"%V\"",
+ u.err, &u.host);
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ cidr = ngx_array_push_n(&ecf->debug_connection, u.naddrs);
+ if (cidr == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t));
+
+ for (i = 0; i < u.naddrs; i++) {
+ cidr[i].family = u.addrs[i].sockaddr->sa_family;
+
+ switch (cidr[i].family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr;
+ cidr[i].u.in6.addr = sin6->sin6_addr;
+ ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16);
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) u.addrs[i].sockaddr;
+ cidr[i].u.in.addr = sin->sin_addr.s_addr;
+ cidr[i].u.in.mask = 0xffffffff;
+ break;
+ }
+ }
+
+#else
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "\"debug_connection\" is ignored, you need to rebuild "
+ "nginx using --with-debug option to enable it");
+
+#endif
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_event_core_create_conf(ngx_cycle_t *cycle)
+{
+ ngx_event_conf_t *ecf;
+
+ ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));
+ if (ecf == NULL) {
+ return NULL;
+ }
+
+ ecf->connections = NGX_CONF_UNSET_UINT;
+ ecf->use = NGX_CONF_UNSET_UINT;
+ ecf->multi_accept = NGX_CONF_UNSET;
+ ecf->accept_mutex = NGX_CONF_UNSET;
+ ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;
+ ecf->name = (void *) NGX_CONF_UNSET;
+
+#if (NGX_DEBUG)
+
+ if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4,
+ sizeof(ngx_cidr_t)) == NGX_ERROR)
+ {
+ return NULL;
+ }
+
+#endif
+
+ return ecf;
+}
+
+
+static char *
+ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)
+{
+ ngx_event_conf_t *ecf = conf;
+
+#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
+ int fd;
+#endif
+#if (NGX_HAVE_RTSIG)
+ ngx_uint_t rtsig;
+ ngx_core_conf_t *ccf;
+#endif
+ ngx_int_t i;
+ ngx_module_t *module;
+ ngx_event_module_t *event_module;
+
+ module = NULL;
+
+#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
+
+ fd = epoll_create(100);
+
+ if (fd != -1) {
+ (void) close(fd);
+ module = &ngx_epoll_module;
+
+ } else if (ngx_errno != NGX_ENOSYS) {
+ module = &ngx_epoll_module;
+ }
+
+#endif
+
+#if (NGX_HAVE_RTSIG)
+
+ if (module == NULL) {
+ module = &ngx_rtsig_module;
+ rtsig = 1;
+
+ } else {
+ rtsig = 0;
+ }
+
+#endif
+
+#if (NGX_HAVE_DEVPOLL)
+
+ module = &ngx_devpoll_module;
+
+#endif
+
+#if (NGX_HAVE_KQUEUE)
+
+ module = &ngx_kqueue_module;
+
+#endif
+
+#if (NGX_HAVE_SELECT)
+
+ if (module == NULL) {
+ module = &ngx_select_module;
+ }
+
+#endif
+
+ if (module == NULL) {
+ for (i = 0; ngx_modules[i]; i++) {
+
+ if (ngx_modules[i]->type != NGX_EVENT_MODULE) {
+ continue;
+ }
+
+ event_module = ngx_modules[i]->ctx;
+
+ if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0)
+ {
+ continue;
+ }
+
+ module = ngx_modules[i];
+ break;
+ }
+ }
+
+ if (module == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found");
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);
+ cycle->connection_n = ecf->connections;
+
+ ngx_conf_init_uint_value(ecf->use, module->ctx_index);
+
+ event_module = module->ctx;
+ ngx_conf_init_ptr_value(ecf->name, event_module->name->data);
+
+ ngx_conf_init_value(ecf->multi_accept, 0);
+ ngx_conf_init_value(ecf->accept_mutex, 1);
+ ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);
+
+
+#if (NGX_HAVE_RTSIG)
+
+ if (!rtsig) {
+ return NGX_CONF_OK;
+ }
+
+ if (ecf->accept_mutex) {
+ return NGX_CONF_OK;
+ }
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+ if (ccf->worker_processes == 0) {
+ return NGX_CONF_OK;
+ }
+
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+ "the \"rtsig\" method requires \"accept_mutex\" to be on");
+
+ return NGX_CONF_ERROR;
+
+#else
+
+ return NGX_CONF_OK;
+
+#endif
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event.h
new file mode 100644
index 00000000000..530c9486c51
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event.h
@@ -0,0 +1,569 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_EVENT_H_INCLUDED_
+#define _NGX_EVENT_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#define NGX_INVALID_INDEX 0xd0d0d0d0
+
+
+#if (NGX_HAVE_IOCP)
+
+typedef struct {
+ WSAOVERLAPPED ovlp;
+ ngx_event_t *event;
+ int error;
+} ngx_event_ovlp_t;
+
+#endif
+
+
+typedef struct {
+ ngx_uint_t lock;
+
+ ngx_event_t *events;
+ ngx_event_t *last;
+} ngx_event_mutex_t;
+
+
+struct ngx_event_s {
+ void *data;
+
+ unsigned write:1;
+
+ unsigned accept:1;
+
+ /* used to detect the stale events in kqueue, rtsig, and epoll */
+ unsigned instance:1;
+
+ /*
+ * the event was passed or would be passed to a kernel;
+ * in aio mode - operation was posted.
+ */
+ unsigned active:1;
+
+ unsigned disabled:1;
+
+ /* the ready event; in aio mode 0 means that no operation can be posted */
+ unsigned ready:1;
+
+ unsigned oneshot:1;
+
+ /* aio operation is complete */
+ unsigned complete:1;
+
+ unsigned eof:1;
+ unsigned error:1;
+
+ unsigned timedout:1;
+ unsigned timer_set:1;
+
+ unsigned delayed:1;
+
+ unsigned deferred_accept:1;
+
+ /* the pending eof reported by kqueue, epoll or in aio chain operation */
+ unsigned pending_eof:1;
+
+#if !(NGX_THREADS)
+ unsigned posted_ready:1;
+#endif
+
+#if (NGX_WIN32)
+ /* setsockopt(SO_UPDATE_ACCEPT_CONTEXT) was successful */
+ unsigned accept_context_updated:1;
+#endif
+
+#if (NGX_HAVE_KQUEUE)
+ unsigned kq_vnode:1;
+
+ /* the pending errno reported by kqueue */
+ int kq_errno;
+#endif
+
+ /*
+ * kqueue only:
+ * accept: number of sockets that wait to be accepted
+ * read: bytes to read when event is ready
+ * or lowat when event is set with NGX_LOWAT_EVENT flag
+ * write: available space in buffer when event is ready
+ * or lowat when event is set with NGX_LOWAT_EVENT flag
+ *
+ * iocp: TODO
+ *
+ * otherwise:
+ * accept: 1 if accept many, 0 otherwise
+ */
+
+#if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP)
+ int available;
+#else
+ unsigned available:1;
+#endif
+
+ ngx_event_handler_pt handler;
+
+
+#if (NGX_HAVE_AIO)
+
+#if (NGX_HAVE_IOCP)
+ ngx_event_ovlp_t ovlp;
+#else
+ struct aiocb aiocb;
+#endif
+
+#endif
+
+ ngx_uint_t index;
+
+ ngx_log_t *log;
+
+ ngx_rbtree_node_t timer;
+
+ unsigned closed:1;
+
+ /* to test on worker exit */
+ unsigned channel:1;
+ unsigned resolver:1;
+
+#if (NGX_THREADS)
+
+ unsigned locked:1;
+
+ unsigned posted_ready:1;
+ unsigned posted_timedout:1;
+ unsigned posted_eof:1;
+
+#if (NGX_HAVE_KQUEUE)
+ /* the pending errno reported by kqueue */
+ int posted_errno;
+#endif
+
+#if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP)
+ int posted_available;
+#else
+ unsigned posted_available:1;
+#endif
+
+ ngx_atomic_t *lock;
+ ngx_atomic_t *own_lock;
+
+#endif
+
+ /* the links of the posted queue */
+ ngx_event_t *next;
+ ngx_event_t **prev;
+
+
+#if 0
+
+ /* the threads support */
+
+ /*
+ * the event thread context, we store it here
+ * if $(CC) does not understand __thread declaration
+ * and pthread_getspecific() is too costly
+ */
+
+ void *thr_ctx;
+
+#if (NGX_EVENT_T_PADDING)
+
+ /* event should not cross cache line in SMP */
+
+ uint32_t padding[NGX_EVENT_T_PADDING];
+#endif
+#endif
+};
+
+
+#if (NGX_HAVE_FILE_AIO)
+
+struct ngx_event_aio_s {
+ void *data;
+ ngx_event_handler_pt handler;
+ ngx_file_t *file;
+
+ ngx_fd_t fd;
+
+#if (NGX_HAVE_EVENTFD)
+ int64_t res;
+#if (NGX_TEST_BUILD_EPOLL)
+ ngx_err_t err;
+ size_t nbytes;
+#endif
+#else
+ ngx_err_t err;
+ size_t nbytes;
+#endif
+
+#if (NGX_HAVE_AIO_SENDFILE)
+ off_t last_offset;
+#endif
+
+ ngx_aiocb_t aiocb;
+ ngx_event_t event;
+};
+
+#endif
+
+
+typedef struct {
+ ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
+ ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
+
+ ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
+ ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
+
+ ngx_int_t (*add_conn)(ngx_connection_t *c);
+ ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
+
+ ngx_int_t (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
+ ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
+ ngx_uint_t flags);
+
+ ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
+ void (*done)(ngx_cycle_t *cycle);
+} ngx_event_actions_t;
+
+
+extern ngx_event_actions_t ngx_event_actions;
+
+
+/*
+ * The event filter requires to read/write the whole data:
+ * select, poll, /dev/poll, kqueue, epoll.
+ */
+#define NGX_USE_LEVEL_EVENT 0x00000001
+
+/*
+ * The event filter is deleted after a notification without an additional
+ * syscall: kqueue, epoll.
+ */
+#define NGX_USE_ONESHOT_EVENT 0x00000002
+
+/*
+ * The event filter notifies only the changes and an initial level:
+ * kqueue, epoll.
+ */
+#define NGX_USE_CLEAR_EVENT 0x00000004
+
+/*
+ * The event filter has kqueue features: the eof flag, errno,
+ * available data, etc.
+ */
+#define NGX_USE_KQUEUE_EVENT 0x00000008
+
+/*
+ * The event filter supports low water mark: kqueue's NOTE_LOWAT.
+ * kqueue in FreeBSD 4.1-4.2 has no NOTE_LOWAT so we need a separate flag.
+ */
+#define NGX_USE_LOWAT_EVENT 0x00000010
+
+/*
+ * The event filter requires to do i/o operation until EAGAIN: epoll, rtsig.
+ */
+#define NGX_USE_GREEDY_EVENT 0x00000020
+
+/*
+ * The event filter is epoll.
+ */
+#define NGX_USE_EPOLL_EVENT 0x00000040
+
+/*
+ * No need to add or delete the event filters: rtsig.
+ */
+#define NGX_USE_RTSIG_EVENT 0x00000080
+
+/*
+ * No need to add or delete the event filters: overlapped, aio_read,
+ * aioread, io_submit.
+ */
+#define NGX_USE_AIO_EVENT 0x00000100
+
+/*
+ * Need to add socket or handle only once: i/o completion port.
+ * It also requires NGX_HAVE_AIO and NGX_USE_AIO_EVENT to be set.
+ */
+#define NGX_USE_IOCP_EVENT 0x00000200
+
+/*
+ * The event filter has no opaque data and requires file descriptors table:
+ * poll, /dev/poll, rtsig.
+ */
+#define NGX_USE_FD_EVENT 0x00000400
+
+/*
+ * The event module handles periodic or absolute timer event by itself:
+ * kqueue in FreeBSD 4.4, NetBSD 2.0, and MacOSX 10.4, Solaris 10's event ports.
+ */
+#define NGX_USE_TIMER_EVENT 0x00000800
+
+/*
+ * All event filters on file descriptor are deleted after a notification:
+ * Solaris 10's event ports.
+ */
+#define NGX_USE_EVENTPORT_EVENT 0x00001000
+
+/*
+ * The event filter support vnode notifications: kqueue.
+ */
+#define NGX_USE_VNODE_EVENT 0x00002000
+
+
+/*
+ * The event filter is deleted just before the closing file.
+ * Has no meaning for select and poll.
+ * kqueue, epoll, rtsig, eventport: allows to avoid explicit delete,
+ * because filter automatically is deleted
+ * on file close,
+ *
+ * /dev/poll: we need to flush POLLREMOVE event
+ * before closing file.
+ */
+#define NGX_CLOSE_EVENT 1
+
+/*
+ * disable temporarily event filter, this may avoid locks
+ * in kernel malloc()/free(): kqueue.
+ */
+#define NGX_DISABLE_EVENT 2
+
+/*
+ * event must be passed to kernel right now, do not wait until batch processing.
+ */
+#define NGX_FLUSH_EVENT 4
+
+
+/* these flags have a meaning only for kqueue */
+#define NGX_LOWAT_EVENT 0
+#define NGX_VNODE_EVENT 0
+
+
+#if (NGX_HAVE_EPOLL) && !(NGX_HAVE_EPOLLRDHUP)
+#define EPOLLRDHUP 0
+#endif
+
+
+#if (NGX_HAVE_KQUEUE)
+
+#define NGX_READ_EVENT EVFILT_READ
+#define NGX_WRITE_EVENT EVFILT_WRITE
+
+#undef NGX_VNODE_EVENT
+#define NGX_VNODE_EVENT EVFILT_VNODE
+
+/*
+ * NGX_CLOSE_EVENT, NGX_LOWAT_EVENT, and NGX_FLUSH_EVENT are the module flags
+ * and they must not go into a kernel so we need to choose the value
+ * that must not interfere with any existent and future kqueue flags.
+ * kqueue has such values - EV_FLAG1, EV_EOF, and EV_ERROR:
+ * they are reserved and cleared on a kernel entrance.
+ */
+#undef NGX_CLOSE_EVENT
+#define NGX_CLOSE_EVENT EV_EOF
+
+#undef NGX_LOWAT_EVENT
+#define NGX_LOWAT_EVENT EV_FLAG1
+
+#undef NGX_FLUSH_EVENT
+#define NGX_FLUSH_EVENT EV_ERROR
+
+#define NGX_LEVEL_EVENT 0
+#define NGX_ONESHOT_EVENT EV_ONESHOT
+#define NGX_CLEAR_EVENT EV_CLEAR
+
+#undef NGX_DISABLE_EVENT
+#define NGX_DISABLE_EVENT EV_DISABLE
+
+
+#elif (NGX_HAVE_DEVPOLL || NGX_HAVE_EVENTPORT)
+
+#define NGX_READ_EVENT POLLIN
+#define NGX_WRITE_EVENT POLLOUT
+
+#define NGX_LEVEL_EVENT 0
+#define NGX_ONESHOT_EVENT 1
+
+
+#elif (NGX_HAVE_EPOLL)
+
+#define NGX_READ_EVENT (EPOLLIN|EPOLLRDHUP)
+#define NGX_WRITE_EVENT EPOLLOUT
+
+#define NGX_LEVEL_EVENT 0
+#define NGX_CLEAR_EVENT EPOLLET
+#define NGX_ONESHOT_EVENT 0x70000000
+#if 0
+#define NGX_ONESHOT_EVENT EPOLLONESHOT
+#endif
+
+
+#elif (NGX_HAVE_POLL)
+
+#define NGX_READ_EVENT POLLIN
+#define NGX_WRITE_EVENT POLLOUT
+
+#define NGX_LEVEL_EVENT 0
+#define NGX_ONESHOT_EVENT 1
+
+
+#else /* select */
+
+#define NGX_READ_EVENT 0
+#define NGX_WRITE_EVENT 1
+
+#define NGX_LEVEL_EVENT 0
+#define NGX_ONESHOT_EVENT 1
+
+#endif /* NGX_HAVE_KQUEUE */
+
+
+#if (NGX_HAVE_IOCP)
+#define NGX_IOCP_ACCEPT 0
+#define NGX_IOCP_IO 1
+#define NGX_IOCP_CONNECT 2
+#endif
+
+
+#ifndef NGX_CLEAR_EVENT
+#define NGX_CLEAR_EVENT 0 /* dummy declaration */
+#endif
+
+
+#define ngx_process_changes ngx_event_actions.process_changes
+#define ngx_process_events ngx_event_actions.process_events
+#define ngx_done_events ngx_event_actions.done
+
+#define ngx_add_event ngx_event_actions.add
+#define ngx_del_event ngx_event_actions.del
+#define ngx_add_conn ngx_event_actions.add_conn
+#define ngx_del_conn ngx_event_actions.del_conn
+
+#define ngx_add_timer ngx_event_add_timer
+#define ngx_del_timer ngx_event_del_timer
+
+
+extern ngx_os_io_t ngx_io;
+
+#define ngx_recv ngx_io.recv
+#define ngx_recv_chain ngx_io.recv_chain
+#define ngx_udp_recv ngx_io.udp_recv
+#define ngx_send ngx_io.send
+#define ngx_send_chain ngx_io.send_chain
+
+
+#define NGX_EVENT_MODULE 0x544E5645 /* "EVNT" */
+#define NGX_EVENT_CONF 0x02000000
+
+
+typedef struct {
+ ngx_uint_t connections;
+ ngx_uint_t use;
+
+ ngx_flag_t multi_accept;
+ ngx_flag_t accept_mutex;
+
+ ngx_msec_t accept_mutex_delay;
+
+ u_char *name;
+
+#if (NGX_DEBUG)
+ ngx_array_t debug_connection;
+#endif
+} ngx_event_conf_t;
+
+
+typedef struct {
+ ngx_str_t *name;
+
+ void *(*create_conf)(ngx_cycle_t *cycle);
+ char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
+
+ ngx_event_actions_t actions;
+} ngx_event_module_t;
+
+
+extern ngx_atomic_t *ngx_connection_counter;
+
+extern ngx_atomic_t *ngx_accept_mutex_ptr;
+extern ngx_shmtx_t ngx_accept_mutex;
+extern ngx_uint_t ngx_use_accept_mutex;
+extern ngx_uint_t ngx_accept_events;
+extern ngx_uint_t ngx_accept_mutex_held;
+extern ngx_msec_t ngx_accept_mutex_delay;
+extern ngx_int_t ngx_accept_disabled;
+
+
+#if (NGX_STAT_STUB)
+
+extern ngx_atomic_t *ngx_stat_accepted;
+extern ngx_atomic_t *ngx_stat_handled;
+extern ngx_atomic_t *ngx_stat_requests;
+extern ngx_atomic_t *ngx_stat_active;
+extern ngx_atomic_t *ngx_stat_reading;
+extern ngx_atomic_t *ngx_stat_writing;
+extern ngx_atomic_t *ngx_stat_waiting;
+
+#endif
+
+
+#define NGX_UPDATE_TIME 1
+#define NGX_POST_EVENTS 2
+#define NGX_POST_THREAD_EVENTS 4
+
+
+extern sig_atomic_t ngx_event_timer_alarm;
+extern ngx_uint_t ngx_event_flags;
+extern ngx_module_t ngx_events_module;
+extern ngx_module_t ngx_event_core_module;
+
+
+#define ngx_event_get_conf(conf_ctx, module) \
+ (*(ngx_get_conf(conf_ctx, ngx_events_module))) [module.ctx_index];
+
+
+
+void ngx_event_accept(ngx_event_t *ev);
+ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle);
+u_char *ngx_accept_log_error(ngx_log_t *log, u_char *buf, size_t len);
+
+
+void ngx_process_events_and_timers(ngx_cycle_t *cycle);
+ngx_int_t ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags);
+ngx_int_t ngx_handle_write_event(ngx_event_t *wev, size_t lowat);
+
+
+#if (NGX_WIN32)
+void ngx_event_acceptex(ngx_event_t *ev);
+ngx_int_t ngx_event_post_acceptex(ngx_listening_t *ls, ngx_uint_t n);
+u_char *ngx_acceptex_log_error(ngx_log_t *log, u_char *buf, size_t len);
+#endif
+
+
+ngx_int_t ngx_send_lowat(ngx_connection_t *c, size_t lowat);
+
+
+/* used in ngx_log_debugX() */
+#define ngx_event_ident(p) ((ngx_connection_t *) (p))->fd
+
+
+#include <ngx_event_timer.h>
+#include <ngx_event_posted.h>
+#include <ngx_event_busy_lock.h>
+
+#if (NGX_WIN32)
+#include <ngx_iocp_module.h>
+#endif
+
+
+#endif /* _NGX_EVENT_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_accept.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_accept.c
new file mode 100644
index 00000000000..575ee4bdb03
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_accept.c
@@ -0,0 +1,515 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+static ngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle);
+static ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle);
+static void ngx_close_accepted_connection(ngx_connection_t *c);
+
+
+void
+ngx_event_accept(ngx_event_t *ev)
+{
+ socklen_t socklen;
+ ngx_err_t err;
+ ngx_log_t *log;
+ ngx_uint_t level;
+ ngx_socket_t s;
+ ngx_event_t *rev, *wev;
+ ngx_listening_t *ls;
+ ngx_connection_t *c, *lc;
+ ngx_event_conf_t *ecf;
+ u_char sa[NGX_SOCKADDRLEN];
+#if (NGX_HAVE_ACCEPT4)
+ static ngx_uint_t use_accept4 = 1;
+#endif
+
+ if (ev->timedout) {
+ if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {
+ return;
+ }
+
+ ev->timedout = 0;
+ }
+
+ ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
+
+ if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
+ ev->available = 1;
+
+ } else if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
+ ev->available = ecf->multi_accept;
+ }
+
+ lc = ev->data;
+ ls = lc->listening;
+ ev->ready = 0;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "accept on %V, ready: %d", &ls->addr_text, ev->available);
+
+ do {
+ socklen = NGX_SOCKADDRLEN;
+
+#if (NGX_HAVE_ACCEPT4)
+ if (use_accept4) {
+ s = accept4(lc->fd, (struct sockaddr *) sa, &socklen,
+ SOCK_NONBLOCK);
+ } else {
+ s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
+ }
+#else
+ s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
+#endif
+
+ if (s == (ngx_socket_t) -1) {
+ err = ngx_socket_errno;
+
+ if (err == NGX_EAGAIN) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,
+ "accept() not ready");
+ return;
+ }
+
+ level = NGX_LOG_ALERT;
+
+ if (err == NGX_ECONNABORTED) {
+ level = NGX_LOG_ERR;
+
+ } else if (err == NGX_EMFILE || err == NGX_ENFILE) {
+ level = NGX_LOG_CRIT;
+ }
+
+#if (NGX_HAVE_ACCEPT4)
+ ngx_log_error(level, ev->log, err,
+ use_accept4 ? "accept4() failed" : "accept() failed");
+
+ if (use_accept4 && err == NGX_ENOSYS) {
+ use_accept4 = 0;
+ ngx_inherited_nonblocking = 0;
+ continue;
+ }
+#else
+ ngx_log_error(level, ev->log, err, "accept() failed");
+#endif
+
+ if (err == NGX_ECONNABORTED) {
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ ev->available--;
+ }
+
+ if (ev->available) {
+ continue;
+ }
+ }
+
+ if (err == NGX_EMFILE || err == NGX_ENFILE) {
+ if (ngx_disable_accept_events((ngx_cycle_t *) ngx_cycle)
+ != NGX_OK)
+ {
+ return;
+ }
+
+ if (ngx_use_accept_mutex) {
+ if (ngx_accept_mutex_held) {
+ ngx_shmtx_unlock(&ngx_accept_mutex);
+ ngx_accept_mutex_held = 0;
+ }
+
+ ngx_accept_disabled = 1;
+
+ } else {
+ ngx_add_timer(ev, ecf->accept_mutex_delay);
+ }
+ }
+
+ return;
+ }
+
+#if (NGX_STAT_STUB)
+ (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);
+#endif
+
+ ngx_accept_disabled = ngx_cycle->connection_n / 8
+ - ngx_cycle->free_connection_n;
+
+ c = ngx_get_connection(s, ev->log);
+
+ if (c == NULL) {
+ if (ngx_close_socket(s) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
+ ngx_close_socket_n " failed");
+ }
+
+ return;
+ }
+
+#if (NGX_STAT_STUB)
+ (void) ngx_atomic_fetch_add(ngx_stat_active, 1);
+#endif
+
+ c->pool = ngx_create_pool(ls->pool_size, ev->log);
+ if (c->pool == NULL) {
+ ngx_close_accepted_connection(c);
+ return;
+ }
+
+ c->sockaddr = ngx_palloc(c->pool, socklen);
+ if (c->sockaddr == NULL) {
+ ngx_close_accepted_connection(c);
+ return;
+ }
+
+ ngx_memcpy(c->sockaddr, sa, socklen);
+
+ log = ngx_palloc(c->pool, sizeof(ngx_log_t));
+ if (log == NULL) {
+ ngx_close_accepted_connection(c);
+ return;
+ }
+
+ /* set a blocking mode for aio and non-blocking mode for others */
+
+ if (ngx_inherited_nonblocking) {
+ if (ngx_event_flags & NGX_USE_AIO_EVENT) {
+ if (ngx_blocking(s) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
+ ngx_blocking_n " failed");
+ ngx_close_accepted_connection(c);
+ return;
+ }
+ }
+
+ } else {
+ if (!(ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_RTSIG_EVENT))) {
+ if (ngx_nonblocking(s) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
+ ngx_nonblocking_n " failed");
+ ngx_close_accepted_connection(c);
+ return;
+ }
+ }
+ }
+
+ *log = ls->log;
+
+ c->recv = ngx_recv;
+ c->send = ngx_send;
+ c->recv_chain = ngx_recv_chain;
+ c->send_chain = ngx_send_chain;
+
+ c->log = log;
+ c->pool->log = log;
+
+ c->socklen = socklen;
+ c->listening = ls;
+ c->local_sockaddr = ls->sockaddr;
+ c->local_socklen = ls->socklen;
+
+ c->unexpected_eof = 1;
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ if (c->sockaddr->sa_family == AF_UNIX) {
+ c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;
+ c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
+#if (NGX_SOLARIS)
+ /* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */
+ c->sendfile = 0;
+#endif
+ }
+#endif
+
+ rev = c->read;
+ wev = c->write;
+
+ wev->ready = 1;
+
+ if (ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_RTSIG_EVENT)) {
+ /* rtsig, aio, iocp */
+ rev->ready = 1;
+ }
+
+ if (ev->deferred_accept) {
+ rev->ready = 1;
+#if (NGX_HAVE_KQUEUE)
+ rev->available = 1;
+#endif
+ }
+
+ rev->log = log;
+ wev->log = log;
+
+ /*
+ * TODO: MT: - ngx_atomic_fetch_add()
+ * or protection by critical section or light mutex
+ *
+ * TODO: MP: - allocated in a shared memory
+ * - ngx_atomic_fetch_add()
+ * or protection by critical section or light mutex
+ */
+
+ c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
+
+#if (NGX_STAT_STUB)
+ (void) ngx_atomic_fetch_add(ngx_stat_handled, 1);
+#endif
+
+#if (NGX_THREADS)
+ rev->lock = &c->lock;
+ wev->lock = &c->lock;
+ rev->own_lock = &c->lock;
+ wev->own_lock = &c->lock;
+#endif
+
+ if (ls->addr_ntop) {
+ c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
+ if (c->addr_text.data == NULL) {
+ ngx_close_accepted_connection(c);
+ return;
+ }
+
+ c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,
+ c->addr_text.data,
+ ls->addr_text_max_len, 0);
+ if (c->addr_text.len == 0) {
+ ngx_close_accepted_connection(c);
+ return;
+ }
+ }
+
+#if (NGX_DEBUG)
+ {
+
+ ngx_str_t addr;
+ struct sockaddr_in *sin;
+ ngx_cidr_t *cidr;
+ ngx_uint_t i;
+ u_char text[NGX_SOCKADDR_STRLEN];
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+ ngx_uint_t n;
+#endif
+
+ cidr = ecf->debug_connection.elts;
+ for (i = 0; i < ecf->debug_connection.nelts; i++) {
+ if (cidr[i].family != (ngx_uint_t) c->sockaddr->sa_family) {
+ goto next;
+ }
+
+ switch (cidr[i].family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) c->sockaddr;
+ for (n = 0; n < 16; n++) {
+ if ((sin6->sin6_addr.s6_addr[n]
+ & cidr[i].u.in6.mask.s6_addr[n])
+ != cidr[i].u.in6.addr.s6_addr[n])
+ {
+ goto next;
+ }
+ }
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) c->sockaddr;
+ if ((sin->sin_addr.s_addr & cidr[i].u.in.mask)
+ != cidr[i].u.in.addr)
+ {
+ goto next;
+ }
+ break;
+ }
+
+ log->log_level = NGX_LOG_DEBUG_CONNECTION|NGX_LOG_DEBUG_ALL;
+ break;
+
+ next:
+ continue;
+ }
+
+ if (log->log_level & NGX_LOG_DEBUG_EVENT) {
+ addr.data = text;
+ addr.len = ngx_sock_ntop(c->sockaddr, c->socklen, text,
+ NGX_SOCKADDR_STRLEN, 1);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, log, 0,
+ "*%uA accept: %V fd:%d", c->number, &addr, s);
+ }
+
+ }
+#endif
+
+ if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {
+ if (ngx_add_conn(c) == NGX_ERROR) {
+ ngx_close_accepted_connection(c);
+ return;
+ }
+ }
+
+ log->data = NULL;
+ log->handler = NULL;
+
+ ls->handler(c);
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ ev->available--;
+ }
+
+ } while (ev->available);
+}
+
+
+ngx_int_t
+ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
+{
+ if (ngx_shmtx_trylock(&ngx_accept_mutex)) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "accept mutex locked");
+
+ if (ngx_accept_mutex_held
+ && ngx_accept_events == 0
+ && !(ngx_event_flags & NGX_USE_RTSIG_EVENT))
+ {
+ return NGX_OK;
+ }
+
+ if (ngx_enable_accept_events(cycle) == NGX_ERROR) {
+ ngx_shmtx_unlock(&ngx_accept_mutex);
+ return NGX_ERROR;
+ }
+
+ ngx_accept_events = 0;
+ ngx_accept_mutex_held = 1;
+
+ return NGX_OK;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "accept mutex lock failed: %ui", ngx_accept_mutex_held);
+
+ if (ngx_accept_mutex_held) {
+ if (ngx_disable_accept_events(cycle) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ ngx_accept_mutex_held = 0;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_enable_accept_events(ngx_cycle_t *cycle)
+{
+ ngx_uint_t i;
+ ngx_listening_t *ls;
+ ngx_connection_t *c;
+
+ ls = cycle->listening.elts;
+ for (i = 0; i < cycle->listening.nelts; i++) {
+
+ c = ls[i].connection;
+
+ if (c->read->active) {
+ continue;
+ }
+
+ if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
+
+ if (ngx_add_conn(c) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ if (ngx_add_event(c->read, NGX_READ_EVENT, 0) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_disable_accept_events(ngx_cycle_t *cycle)
+{
+ ngx_uint_t i;
+ ngx_listening_t *ls;
+ ngx_connection_t *c;
+
+ ls = cycle->listening.elts;
+ for (i = 0; i < cycle->listening.nelts; i++) {
+
+ c = ls[i].connection;
+
+ if (!c->read->active) {
+ continue;
+ }
+
+ if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
+ if (ngx_del_conn(c, NGX_DISABLE_EVENT) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ if (ngx_del_event(c->read, NGX_READ_EVENT, NGX_DISABLE_EVENT)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_close_accepted_connection(ngx_connection_t *c)
+{
+ ngx_socket_t fd;
+
+ ngx_free_connection(c);
+
+ fd = c->fd;
+ c->fd = (ngx_socket_t) -1;
+
+ if (ngx_close_socket(fd) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno,
+ ngx_close_socket_n " failed");
+ }
+
+ if (c->pool) {
+ ngx_destroy_pool(c->pool);
+ }
+
+#if (NGX_STAT_STUB)
+ (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
+#endif
+}
+
+
+u_char *
+ngx_accept_log_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ return ngx_snprintf(buf, len, " while accepting new connection on %V",
+ log->data);
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_busy_lock.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_busy_lock.c
new file mode 100644
index 00000000000..fdac0da8fd2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_busy_lock.c
@@ -0,0 +1,286 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+static ngx_int_t ngx_event_busy_lock_look_cacheable(ngx_event_busy_lock_t *bl,
+ ngx_event_busy_lock_ctx_t *ctx);
+static void ngx_event_busy_lock_handler(ngx_event_t *ev);
+static void ngx_event_busy_lock_posted_handler(ngx_event_t *ev);
+
+
+/*
+ * NGX_OK: the busy lock is held
+ * NGX_AGAIN: the all busy locks are held but we will wait the specified time
+ * NGX_BUSY: ctx->timer == 0: there are many the busy locks
+ * ctx->timer != 0: there are many the waiting locks
+ */
+
+ngx_int_t
+ngx_event_busy_lock(ngx_event_busy_lock_t *bl, ngx_event_busy_lock_ctx_t *ctx)
+{
+ ngx_int_t rc;
+
+ ngx_mutex_lock(bl->mutex);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->event->log, 0,
+ "event busy lock: b:%d mb:%d",
+ bl->busy, bl->max_busy);
+
+ if (bl->busy < bl->max_busy) {
+ bl->busy++;
+
+ rc = NGX_OK;
+
+ } else if (ctx->timer && bl->waiting < bl->max_waiting) {
+ bl->waiting++;
+ ngx_add_timer(ctx->event, ctx->timer);
+ ctx->event->handler = ngx_event_busy_lock_handler;
+
+ if (bl->events) {
+ bl->last->next = ctx;
+
+ } else {
+ bl->events = ctx;
+ }
+
+ bl->last = ctx;
+
+ rc = NGX_AGAIN;
+
+ } else {
+ rc = NGX_BUSY;
+ }
+
+ ngx_mutex_unlock(bl->mutex);
+
+ return rc;
+}
+
+
+ngx_int_t
+ngx_event_busy_lock_cacheable(ngx_event_busy_lock_t *bl,
+ ngx_event_busy_lock_ctx_t *ctx)
+{
+ ngx_int_t rc;
+
+ ngx_mutex_lock(bl->mutex);
+
+ rc = ngx_event_busy_lock_look_cacheable(bl, ctx);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->event->log, 0,
+ "event busy lock: %d w:%d mw:%d",
+ rc, bl->waiting, bl->max_waiting);
+
+ /*
+ * NGX_OK: no the same request, there is free slot and we locked it
+ * NGX_BUSY: no the same request and there is no free slot
+ * NGX_AGAIN: the same request is processing
+ */
+
+ if (rc == NGX_AGAIN) {
+
+ if (ctx->timer && bl->waiting < bl->max_waiting) {
+ bl->waiting++;
+ ngx_add_timer(ctx->event, ctx->timer);
+ ctx->event->handler = ngx_event_busy_lock_handler;
+
+ if (bl->events == NULL) {
+ bl->events = ctx;
+ } else {
+ bl->last->next = ctx;
+ }
+ bl->last = ctx;
+
+ } else {
+ rc = NGX_BUSY;
+ }
+ }
+
+ ngx_mutex_unlock(bl->mutex);
+
+ return rc;
+}
+
+
+void
+ngx_event_busy_unlock(ngx_event_busy_lock_t *bl,
+ ngx_event_busy_lock_ctx_t *ctx)
+{
+ ngx_event_t *ev;
+ ngx_event_busy_lock_ctx_t *wakeup;
+
+ ngx_mutex_lock(bl->mutex);
+
+ if (bl->events) {
+ wakeup = bl->events;
+ bl->events = bl->events->next;
+
+ } else {
+ wakeup = NULL;
+ bl->busy--;
+ }
+
+ /*
+ * MP: all ctx's and their queue must be in shared memory,
+ * each ctx has pid to wake up
+ */
+
+ if (wakeup == NULL) {
+ ngx_mutex_unlock(bl->mutex);
+ return;
+ }
+
+ if (ctx->md5) {
+ for (wakeup = bl->events; wakeup; wakeup = wakeup->next) {
+ if (wakeup->md5 == NULL || wakeup->slot != ctx->slot) {
+ continue;
+ }
+
+ wakeup->handler = ngx_event_busy_lock_posted_handler;
+ wakeup->cache_updated = 1;
+
+ ev = wakeup->event;
+
+ ngx_post_event(ev, &ngx_posted_events);
+ }
+
+ ngx_mutex_unlock(bl->mutex);
+
+ } else {
+ bl->waiting--;
+
+ ngx_mutex_unlock(bl->mutex);
+
+ wakeup->handler = ngx_event_busy_lock_posted_handler;
+ wakeup->locked = 1;
+
+ ev = wakeup->event;
+
+ if (ev->timer_set) {
+ ngx_del_timer(ev);
+ }
+
+ ngx_post_event(ev, &ngx_posted_events);
+ }
+}
+
+
+void
+ngx_event_busy_lock_cancel(ngx_event_busy_lock_t *bl,
+ ngx_event_busy_lock_ctx_t *ctx)
+{
+ ngx_event_busy_lock_ctx_t *c, *p;
+
+ ngx_mutex_lock(bl->mutex);
+
+ bl->waiting--;
+
+ if (ctx == bl->events) {
+ bl->events = ctx->next;
+
+ } else {
+ p = bl->events;
+ for (c = bl->events->next; c; c = c->next) {
+ if (c == ctx) {
+ p->next = ctx->next;
+ break;
+ }
+ p = c;
+ }
+ }
+
+ ngx_mutex_unlock(bl->mutex);
+}
+
+
+static ngx_int_t
+ngx_event_busy_lock_look_cacheable(ngx_event_busy_lock_t *bl,
+ ngx_event_busy_lock_ctx_t *ctx)
+{
+ ngx_int_t free;
+ ngx_uint_t i, bit, cacheable, mask;
+
+ bit = 0;
+ cacheable = 0;
+ free = -1;
+
+#if (NGX_SUPPRESS_WARN)
+ mask = 0;
+#endif
+
+ for (i = 0; i < bl->max_busy; i++) {
+
+ if ((bit & 7) == 0) {
+ mask = bl->md5_mask[i / 8];
+ }
+
+ if (mask & 1) {
+ if (ngx_memcmp(&bl->md5[i * 16], ctx->md5, 16) == 0) {
+ ctx->waiting = 1;
+ ctx->slot = i;
+ return NGX_AGAIN;
+ }
+ cacheable++;
+
+ } else if (free == -1) {
+ free = i;
+ }
+
+ if (cacheable == bl->cacheable) {
+ if (free == -1 && cacheable < bl->max_busy) {
+ free = i + 1;
+ }
+
+ break;
+ }
+
+ mask >>= 1;
+ bit++;
+ }
+
+ if (free == -1) {
+ return NGX_BUSY;
+ }
+
+#if 0
+ if (bl->busy == bl->max_busy) {
+ return NGX_BUSY;
+ }
+#endif
+
+ ngx_memcpy(&bl->md5[free * 16], ctx->md5, 16);
+ bl->md5_mask[free / 8] |= 1 << (free & 7);
+ ctx->slot = free;
+
+ bl->cacheable++;
+ bl->busy++;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_event_busy_lock_handler(ngx_event_t *ev)
+{
+ ev->handler = ngx_event_busy_lock_posted_handler;
+
+ ngx_post_event(ev, &ngx_posted_events);
+}
+
+
+static void
+ngx_event_busy_lock_posted_handler(ngx_event_t *ev)
+{
+ ngx_event_busy_lock_ctx_t *ctx;
+
+ ctx = ev->data;
+ ctx->handler(ev);
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_busy_lock.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_busy_lock.h
new file mode 100644
index 00000000000..254c233e776
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_busy_lock.h
@@ -0,0 +1,65 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_EVENT_BUSY_LOCK_H_INCLUDED_
+#define _NGX_EVENT_BUSY_LOCK_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+typedef struct ngx_event_busy_lock_ctx_s ngx_event_busy_lock_ctx_t;
+
+struct ngx_event_busy_lock_ctx_s {
+ ngx_event_t *event;
+ ngx_event_handler_pt handler;
+ void *data;
+ ngx_msec_t timer;
+
+ unsigned locked:1;
+ unsigned waiting:1;
+ unsigned cache_updated:1;
+
+ char *md5;
+ ngx_int_t slot;
+
+ ngx_event_busy_lock_ctx_t *next;
+};
+
+
+typedef struct {
+ u_char *md5_mask;
+ char *md5;
+ ngx_uint_t cacheable;
+
+ ngx_uint_t busy;
+ ngx_uint_t max_busy;
+
+ ngx_uint_t waiting;
+ ngx_uint_t max_waiting;
+
+ ngx_event_busy_lock_ctx_t *events;
+ ngx_event_busy_lock_ctx_t *last;
+
+#if (NGX_THREADS)
+ ngx_mutex_t *mutex;
+#endif
+} ngx_event_busy_lock_t;
+
+
+ngx_int_t ngx_event_busy_lock(ngx_event_busy_lock_t *bl,
+ ngx_event_busy_lock_ctx_t *ctx);
+ngx_int_t ngx_event_busy_lock_cacheable(ngx_event_busy_lock_t *bl,
+ ngx_event_busy_lock_ctx_t *ctx);
+void ngx_event_busy_unlock(ngx_event_busy_lock_t *bl,
+ ngx_event_busy_lock_ctx_t *ctx);
+void ngx_event_busy_lock_cancel(ngx_event_busy_lock_t *bl,
+ ngx_event_busy_lock_ctx_t *ctx);
+
+
+#endif /* _NGX_EVENT_BUSY_LOCK_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_connect.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_connect.c
new file mode 100644
index 00000000000..5fcabcfd1fa
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_connect.c
@@ -0,0 +1,258 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+
+
+ngx_int_t
+ngx_event_connect_peer(ngx_peer_connection_t *pc)
+{
+ int rc;
+ ngx_int_t event;
+ ngx_err_t err;
+ ngx_uint_t level;
+ ngx_socket_t s;
+ ngx_event_t *rev, *wev;
+ ngx_connection_t *c;
+
+ rc = pc->get(pc, pc->data);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ s = ngx_socket(pc->sockaddr->sa_family, SOCK_STREAM, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, 0, "socket %d", s);
+
+ if (s == (ngx_socket_t) -1) {
+ ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+ ngx_socket_n " failed");
+ return NGX_ERROR;
+ }
+
+
+ c = ngx_get_connection(s, pc->log);
+
+ if (c == NULL) {
+ if (ngx_close_socket(s) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+ ngx_close_socket_n "failed");
+ }
+
+ return NGX_ERROR;
+ }
+
+ if (pc->rcvbuf) {
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
+ (const void *) &pc->rcvbuf, sizeof(int)) == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+ "setsockopt(SO_RCVBUF) failed");
+ goto failed;
+ }
+ }
+
+ if (ngx_nonblocking(s) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+ ngx_nonblocking_n " failed");
+
+ goto failed;
+ }
+
+ if (pc->local) {
+ if (bind(s, pc->local->sockaddr, pc->local->socklen) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, pc->log, ngx_socket_errno,
+ "bind(%V) failed", &pc->local->name);
+
+ goto failed;
+ }
+ }
+
+ c->recv = ngx_recv;
+ c->send = ngx_send;
+ c->recv_chain = ngx_recv_chain;
+ c->send_chain = ngx_send_chain;
+
+ c->sendfile = 1;
+
+ c->log_error = pc->log_error;
+
+ if (pc->sockaddr->sa_family == AF_UNIX) {
+ c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;
+ c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
+
+#if (NGX_SOLARIS)
+ /* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */
+ c->sendfile = 0;
+#endif
+ }
+
+ rev = c->read;
+ wev = c->write;
+
+ rev->log = pc->log;
+ wev->log = pc->log;
+
+ pc->connection = c;
+
+ c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
+
+#if (NGX_THREADS)
+
+ /* TODO: lock event when call completion handler */
+
+ rev->lock = pc->lock;
+ wev->lock = pc->lock;
+ rev->own_lock = &c->lock;
+ wev->own_lock = &c->lock;
+
+#endif
+
+ if (ngx_add_conn) {
+ if (ngx_add_conn(c) == NGX_ERROR) {
+ goto failed;
+ }
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pc->log, 0,
+ "connect to %V, fd:%d #%uA", pc->name, s, c->number);
+
+ rc = connect(s, pc->sockaddr, pc->socklen);
+
+ if (rc == -1) {
+ err = ngx_socket_errno;
+
+
+ if (err != NGX_EINPROGRESS
+#if (NGX_WIN32)
+ /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */
+ && err != NGX_EAGAIN
+#endif
+ )
+ {
+ if (err == NGX_ECONNREFUSED
+#if (NGX_LINUX)
+ /*
+ * Linux returns EAGAIN instead of ECONNREFUSED
+ * for unix sockets if listen queue is full
+ */
+ || err == NGX_EAGAIN
+#endif
+ || err == NGX_ECONNRESET
+ || err == NGX_ENETDOWN
+ || err == NGX_ENETUNREACH
+ || err == NGX_EHOSTDOWN
+ || err == NGX_EHOSTUNREACH)
+ {
+ level = NGX_LOG_ERR;
+
+ } else {
+ level = NGX_LOG_CRIT;
+ }
+
+ ngx_log_error(level, c->log, err, "connect() to %V failed",
+ pc->name);
+
+ ngx_close_connection(c);
+ pc->connection = NULL;
+
+ return NGX_DECLINED;
+ }
+ }
+
+ if (ngx_add_conn) {
+ if (rc == -1) {
+
+ /* NGX_EINPROGRESS */
+
+ return NGX_AGAIN;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected");
+
+ wev->ready = 1;
+
+ return NGX_OK;
+ }
+
+ if (ngx_event_flags & NGX_USE_AIO_EVENT) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, ngx_socket_errno,
+ "connect(): %d", rc);
+
+ /* aio, iocp */
+
+ if (ngx_blocking(s) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+ ngx_blocking_n " failed");
+ goto failed;
+ }
+
+ /*
+ * FreeBSD's aio allows to post an operation on non-connected socket.
+ * NT does not support it.
+ *
+ * TODO: check in Win32, etc. As workaround we can use NGX_ONESHOT_EVENT
+ */
+
+ rev->ready = 1;
+ wev->ready = 1;
+
+ return NGX_OK;
+ }
+
+ if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
+
+ /* kqueue */
+
+ event = NGX_CLEAR_EVENT;
+
+ } else {
+
+ /* select, poll, /dev/poll */
+
+ event = NGX_LEVEL_EVENT;
+ }
+
+ if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {
+ goto failed;
+ }
+
+ if (rc == -1) {
+
+ /* NGX_EINPROGRESS */
+
+ if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) {
+ goto failed;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, "connected");
+
+ wev->ready = 1;
+
+ return NGX_OK;
+
+failed:
+
+ ngx_close_connection(c);
+ pc->connection = NULL;
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_event_get_peer(ngx_peer_connection_t *pc, void *data)
+{
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_connect.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_connect.h
new file mode 100644
index 00000000000..951c24f411c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_connect.h
@@ -0,0 +1,76 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_EVENT_CONNECT_H_INCLUDED_
+#define _NGX_EVENT_CONNECT_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#define NGX_PEER_KEEPALIVE 1
+#define NGX_PEER_NEXT 2
+#define NGX_PEER_FAILED 4
+
+
+typedef struct ngx_peer_connection_s ngx_peer_connection_t;
+
+typedef ngx_int_t (*ngx_event_get_peer_pt)(ngx_peer_connection_t *pc,
+ void *data);
+typedef void (*ngx_event_free_peer_pt)(ngx_peer_connection_t *pc, void *data,
+ ngx_uint_t state);
+#if (NGX_SSL)
+
+typedef ngx_int_t (*ngx_event_set_peer_session_pt)(ngx_peer_connection_t *pc,
+ void *data);
+typedef void (*ngx_event_save_peer_session_pt)(ngx_peer_connection_t *pc,
+ void *data);
+#endif
+
+
+struct ngx_peer_connection_s {
+ ngx_connection_t *connection;
+
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+ ngx_str_t *name;
+
+ ngx_uint_t tries;
+
+ ngx_event_get_peer_pt get;
+ ngx_event_free_peer_pt free;
+ void *data;
+
+#if (NGX_SSL)
+ ngx_event_set_peer_session_pt set_session;
+ ngx_event_save_peer_session_pt save_session;
+#endif
+
+#if (NGX_THREADS)
+ ngx_atomic_t *lock;
+#endif
+
+ ngx_addr_t *local;
+
+ int rcvbuf;
+
+ ngx_log_t *log;
+
+ unsigned cached:1;
+
+ /* ngx_connection_log_error_e */
+ unsigned log_error:2;
+};
+
+
+ngx_int_t ngx_event_connect_peer(ngx_peer_connection_t *pc);
+ngx_int_t ngx_event_get_peer(ngx_peer_connection_t *pc, void *data);
+
+
+#endif /* _NGX_EVENT_CONNECT_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_mutex.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_mutex.c
new file mode 100644
index 00000000000..2bdfd5b4ecc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_mutex.c
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+ngx_int_t ngx_event_mutex_timedlock(ngx_event_mutex_t *m, ngx_msec_t timer,
+ ngx_event_t *ev)
+{
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "lock event mutex %p lock:%XD", m, m->lock);
+
+ if (m->lock) {
+
+ if (m->events == NULL) {
+ m->events = ev;
+
+ } else {
+ m->last->next = ev;
+ }
+
+ m->last = ev;
+ ev->next = NULL;
+
+#if (NGX_THREADS0)
+ ev->light = 1;
+#endif
+
+ ngx_add_timer(ev, timer);
+
+ return NGX_AGAIN;
+ }
+
+ m->lock = 1;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t ngx_event_mutex_unlock(ngx_event_mutex_t *m, ngx_log_t *log)
+{
+ ngx_event_t *ev;
+
+ if (m->lock == 0) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "tring to unlock the free event mutex %p", m);
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,
+ "unlock event mutex %p, next event: %p", m, m->events);
+
+ m->lock = 0;
+
+ if (m->events) {
+ ev = m->events;
+ m->events = ev->next;
+
+ ev->next = (ngx_event_t *) ngx_posted_events;
+ ngx_posted_events = ev;
+ }
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_openssl.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_openssl.c
new file mode 100644
index 00000000000..bb82143d3ed
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_openssl.c
@@ -0,0 +1,3344 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#define NGX_SSL_PASSWORD_BUFFER_SIZE 4096
+
+
+typedef struct {
+ ngx_uint_t engine; /* unsigned engine:1; */
+} ngx_openssl_conf_t;
+
+
+static int ngx_ssl_password_callback(char *buf, int size, int rwflag,
+ void *userdata);
+static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
+static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
+ int ret);
+static void ngx_ssl_passwords_cleanup(void *data);
+static void ngx_ssl_handshake_handler(ngx_event_t *ev);
+static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n);
+static void ngx_ssl_write_handler(ngx_event_t *wev);
+static void ngx_ssl_read_handler(ngx_event_t *rev);
+static void ngx_ssl_shutdown_handler(ngx_event_t *ev);
+static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,
+ ngx_err_t err, char *text);
+static void ngx_ssl_clear_error(ngx_log_t *log);
+
+ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
+static int ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn,
+ ngx_ssl_session_t *sess);
+static ngx_ssl_session_t *ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
+ u_char *id, int len, int *copy);
+static void ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess);
+static void ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,
+ ngx_slab_pool_t *shpool, ngx_uint_t n);
+static void ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+
+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+static int ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
+ unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
+ HMAC_CTX *hctx, int enc);
+#endif
+
+#if (OPENSSL_VERSION_NUMBER < 0x10002002L || defined LIBRESSL_VERSION_NUMBER)
+static ngx_int_t ngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *str);
+#endif
+
+static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
+static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static void ngx_openssl_exit(ngx_cycle_t *cycle);
+
+
+static ngx_command_t ngx_openssl_commands[] = {
+
+ { ngx_string("ssl_engine"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_openssl_engine,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_core_module_t ngx_openssl_module_ctx = {
+ ngx_string("openssl"),
+ ngx_openssl_create_conf,
+ NULL
+};
+
+
+ngx_module_t ngx_openssl_module = {
+ NGX_MODULE_V1,
+ &ngx_openssl_module_ctx, /* module context */
+ ngx_openssl_commands, /* module directives */
+ NGX_CORE_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ ngx_openssl_exit, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+int ngx_ssl_connection_index;
+int ngx_ssl_server_conf_index;
+int ngx_ssl_session_cache_index;
+int ngx_ssl_session_ticket_keys_index;
+int ngx_ssl_certificate_index;
+int ngx_ssl_stapling_index;
+
+
+ngx_int_t
+ngx_ssl_init(ngx_log_t *log)
+{
+#ifndef OPENSSL_IS_BORINGSSL
+ OPENSSL_config(NULL);
+#endif
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ OpenSSL_add_all_algorithms();
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+#ifndef SSL_OP_NO_COMPRESSION
+ {
+ /*
+ * Disable gzip compression in OpenSSL prior to 1.0.0 version,
+ * this saves about 522K per connection.
+ */
+ int n;
+ STACK_OF(SSL_COMP) *ssl_comp_methods;
+
+ ssl_comp_methods = SSL_COMP_get_compression_methods();
+ n = sk_SSL_COMP_num(ssl_comp_methods);
+
+ while (n--) {
+ (void) sk_SSL_COMP_pop(ssl_comp_methods);
+ }
+ }
+#endif
+#endif
+
+ ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+
+ if (ngx_ssl_connection_index == -1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed");
+ return NGX_ERROR;
+ }
+
+ ngx_ssl_server_conf_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
+ NULL);
+ if (ngx_ssl_server_conf_index == -1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+ "SSL_CTX_get_ex_new_index() failed");
+ return NGX_ERROR;
+ }
+
+ ngx_ssl_session_cache_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
+ NULL);
+ if (ngx_ssl_session_cache_index == -1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+ "SSL_CTX_get_ex_new_index() failed");
+ return NGX_ERROR;
+ }
+
+ ngx_ssl_session_ticket_keys_index = SSL_CTX_get_ex_new_index(0, NULL, NULL,
+ NULL, NULL);
+ if (ngx_ssl_session_ticket_keys_index == -1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+ "SSL_CTX_get_ex_new_index() failed");
+ return NGX_ERROR;
+ }
+
+ ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
+ NULL);
+ if (ngx_ssl_certificate_index == -1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+ "SSL_CTX_get_ex_new_index() failed");
+ return NGX_ERROR;
+ }
+
+ ngx_ssl_stapling_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
+ NULL);
+ if (ngx_ssl_stapling_index == -1) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0,
+ "SSL_CTX_get_ex_new_index() failed");
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data)
+{
+ ssl->ctx = SSL_CTX_new(SSLv23_method());
+
+ if (ssl->ctx == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_new() failed");
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_server_conf_index, data) == 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_set_ex_data() failed");
+ return NGX_ERROR;
+ }
+
+ ssl->buffer_size = NGX_SSL_BUFSIZE;
+
+ /* client side options */
+
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_SESS_ID_BUG);
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_NETSCAPE_CHALLENGE_BUG);
+
+ /* server side options */
+
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG);
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);
+
+#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING
+ /* this option allow a potential SSL 2.0 rollback (CAN-2005-2969) */
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_MSIE_SSLV2_RSA_PADDING);
+#endif
+
+#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLEAY_080_CLIENT_DH_BUG);
+#endif
+
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_D5_BUG);
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_BLOCK_PADDING_BUG);
+
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
+
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE);
+
+ if (!(protocols & NGX_SSL_SSLv2)) {
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv2);
+ }
+ if (!(protocols & NGX_SSL_SSLv3)) {
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv3);
+ }
+ if (!(protocols & NGX_SSL_TLSv1)) {
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1);
+ }
+#ifdef SSL_OP_NO_TLSv1_1
+ if (!(protocols & NGX_SSL_TLSv1_1)) {
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_1);
+ }
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+ if (!(protocols & NGX_SSL_TLSv1_2)) {
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_2);
+ }
+#endif
+
+#ifdef SSL_OP_NO_COMPRESSION
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_COMPRESSION);
+#endif
+
+#ifdef SSL_MODE_RELEASE_BUFFERS
+ SSL_CTX_set_mode(ssl->ctx, SSL_MODE_RELEASE_BUFFERS);
+#endif
+
+ SSL_CTX_set_read_ahead(ssl->ctx, 1);
+
+ SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
+ ngx_str_t *key, ngx_array_t *passwords)
+{
+ BIO *bio;
+ X509 *x509;
+ u_long n;
+ ngx_str_t *pwd;
+ ngx_uint_t tries;
+
+ if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ /*
+ * we can't use SSL_CTX_use_certificate_chain_file() as it doesn't
+ * allow to access certificate later from SSL_CTX, so we reimplement
+ * it here
+ */
+
+ bio = BIO_new_file((char *) cert->data, "r");
+ if (bio == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "BIO_new_file(\"%s\") failed", cert->data);
+ return NGX_ERROR;
+ }
+
+ x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
+ if (x509 == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "PEM_read_bio_X509_AUX(\"%s\") failed", cert->data);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_use_certificate(\"%s\") failed", cert->data);
+ X509_free(x509);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_set_ex_data() failed");
+ X509_free(x509);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ X509_free(x509);
+
+ /* read rest of the chain */
+
+ for ( ;; ) {
+
+ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+ if (x509 == NULL) {
+ n = ERR_peek_last_error();
+
+ if (ERR_GET_LIB(n) == ERR_LIB_PEM
+ && ERR_GET_REASON(n) == PEM_R_NO_START_LINE)
+ {
+ /* end of file */
+ ERR_clear_error();
+ break;
+ }
+
+ /* some real error */
+
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "PEM_read_bio_X509(\"%s\") failed", cert->data);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_add_extra_chain_cert(\"%s\") failed",
+ cert->data);
+ X509_free(x509);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+ }
+
+ BIO_free(bio);
+
+ if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (passwords) {
+ tries = passwords->nelts;
+ pwd = passwords->elts;
+
+ SSL_CTX_set_default_passwd_cb(ssl->ctx, ngx_ssl_password_callback);
+ SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, pwd);
+
+ } else {
+ tries = 1;
+#if (NGX_SUPPRESS_WARN)
+ pwd = NULL;
+#endif
+ }
+
+ for ( ;; ) {
+
+ if (SSL_CTX_use_PrivateKey_file(ssl->ctx, (char *) key->data,
+ SSL_FILETYPE_PEM)
+ != 0)
+ {
+ break;
+ }
+
+ if (--tries) {
+ n = ERR_peek_error();
+
+#ifdef OPENSSL_IS_BORINGSSL
+ if (ERR_GET_LIB(n) == ERR_LIB_CIPHER
+ && ERR_GET_REASON(n) == CIPHER_R_BAD_DECRYPT)
+#else
+ if (ERR_GET_LIB(n) == ERR_LIB_EVP
+ && ERR_GET_REASON(n) == EVP_R_BAD_DECRYPT)
+#endif
+ {
+ ERR_clear_error();
+ SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, ++pwd);
+ continue;
+ }
+ }
+
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_use_PrivateKey_file(\"%s\") failed", key->data);
+ return NGX_ERROR;
+ }
+
+ SSL_CTX_set_default_passwd_cb(ssl->ctx, NULL);
+
+ return NGX_OK;
+}
+
+
+static int
+ngx_ssl_password_callback(char *buf, int size, int rwflag, void *userdata)
+{
+ ngx_str_t *pwd = userdata;
+
+ if (rwflag) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "ngx_ssl_password_callback() is called for encryption");
+ return 0;
+ }
+
+ if (pwd->len > (size_t) size) {
+ ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
+ "password is truncated to %d bytes", size);
+ } else {
+ size = pwd->len;
+ }
+
+ ngx_memcpy(buf, pwd->data, size);
+
+ return size;
+}
+
+
+ngx_int_t
+ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
+ ngx_int_t depth)
+{
+ STACK_OF(X509_NAME) *list;
+
+ SSL_CTX_set_verify(ssl->ctx, SSL_VERIFY_PEER, ngx_ssl_verify_callback);
+
+ SSL_CTX_set_verify_depth(ssl->ctx, depth);
+
+ if (cert->len == 0) {
+ return NGX_OK;
+ }
+
+ if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_load_verify_locations(\"%s\") failed",
+ cert->data);
+ return NGX_ERROR;
+ }
+
+ /*
+ * SSL_CTX_load_verify_locations() may leave errors in the error queue
+ * while returning success
+ */
+
+ ERR_clear_error();
+
+ list = SSL_load_client_CA_file((char *) cert->data);
+
+ if (list == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_load_client_CA_file(\"%s\") failed", cert->data);
+ return NGX_ERROR;
+ }
+
+ /*
+ * before 0.9.7h and 0.9.8 SSL_load_client_CA_file()
+ * always leaved an error in the error queue
+ */
+
+ ERR_clear_error();
+
+ SSL_CTX_set_client_CA_list(ssl->ctx, list);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
+ ngx_int_t depth)
+{
+ SSL_CTX_set_verify_depth(ssl->ctx, depth);
+
+ if (cert->len == 0) {
+ return NGX_OK;
+ }
+
+ if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_load_verify_locations(\"%s\") failed",
+ cert->data);
+ return NGX_ERROR;
+ }
+
+ /*
+ * SSL_CTX_load_verify_locations() may leave errors in the error queue
+ * while returning success
+ */
+
+ ERR_clear_error();
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)
+{
+ X509_STORE *store;
+ X509_LOOKUP *lookup;
+
+ if (crl->len == 0) {
+ return NGX_OK;
+ }
+
+ if (ngx_conf_full_name(cf->cycle, crl, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ store = SSL_CTX_get_cert_store(ssl->ctx);
+
+ if (store == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_get_cert_store() failed");
+ return NGX_ERROR;
+ }
+
+ lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
+
+ if (lookup == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "X509_STORE_add_lookup() failed");
+ return NGX_ERROR;
+ }
+
+ if (X509_LOOKUP_load_file(lookup, (char *) crl->data, X509_FILETYPE_PEM)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "X509_LOOKUP_load_file(\"%s\") failed", crl->data);
+ return NGX_ERROR;
+ }
+
+ X509_STORE_set_flags(store,
+ X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
+
+ return NGX_OK;
+}
+
+
+static int
+ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)
+{
+#if (NGX_DEBUG)
+ char *subject, *issuer;
+ int err, depth;
+ X509 *cert;
+ X509_NAME *sname, *iname;
+ ngx_connection_t *c;
+ ngx_ssl_conn_t *ssl_conn;
+
+ ssl_conn = X509_STORE_CTX_get_ex_data(x509_store,
+ SSL_get_ex_data_X509_STORE_CTX_idx());
+
+ c = ngx_ssl_get_connection(ssl_conn);
+
+ cert = X509_STORE_CTX_get_current_cert(x509_store);
+ err = X509_STORE_CTX_get_error(x509_store);
+ depth = X509_STORE_CTX_get_error_depth(x509_store);
+
+ sname = X509_get_subject_name(cert);
+ subject = sname ? X509_NAME_oneline(sname, NULL, 0) : "(none)";
+
+ iname = X509_get_issuer_name(cert);
+ issuer = iname ? X509_NAME_oneline(iname, NULL, 0) : "(none)";
+
+ ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "verify:%d, error:%d, depth:%d, "
+ "subject:\"%s\", issuer:\"%s\"",
+ ok, err, depth, subject, issuer);
+
+ if (sname) {
+ OPENSSL_free(subject);
+ }
+
+ if (iname) {
+ OPENSSL_free(issuer);
+ }
+#endif
+
+ return 1;
+}
+
+
+static void
+ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret)
+{
+ BIO *rbio, *wbio;
+ ngx_connection_t *c;
+
+ if (where & SSL_CB_HANDSHAKE_START) {
+ c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+
+ if (c->ssl->handshaked) {
+ c->ssl->renegotiation = 1;
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL renegotiation");
+ }
+ }
+
+ if ((where & SSL_CB_ACCEPT_LOOP) == SSL_CB_ACCEPT_LOOP) {
+ c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+
+ if (!c->ssl->handshake_buffer_set) {
+ /*
+ * By default OpenSSL uses 4k buffer during a handshake,
+ * which is too low for long certificate chains and might
+ * result in extra round-trips.
+ *
+ * To adjust a buffer size we detect that buffering was added
+ * to write side of the connection by comparing rbio and wbio.
+ * If they are different, we assume that it's due to buffering
+ * added to wbio, and set buffer size.
+ */
+
+ rbio = SSL_get_rbio((ngx_ssl_conn_t *) ssl_conn);
+ wbio = SSL_get_wbio((ngx_ssl_conn_t *) ssl_conn);
+
+ if (rbio != wbio) {
+ (void) BIO_set_write_buffer_size(wbio, NGX_SSL_BUFSIZE);
+ c->ssl->handshake_buffer_set = 1;
+ }
+ }
+ }
+}
+
+
+RSA *
+ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export,
+ int key_length)
+{
+ static RSA *key;
+
+ if (key_length != 512) {
+ return NULL;
+ }
+
+#ifndef OPENSSL_NO_DEPRECATED
+
+ if (key == NULL) {
+ key = RSA_generate_key(512, RSA_F4, NULL, NULL);
+ }
+
+#endif
+
+ return key;
+}
+
+
+ngx_array_t *
+ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file)
+{
+ u_char *p, *last, *end;
+ size_t len;
+ ssize_t n;
+ ngx_fd_t fd;
+ ngx_str_t *pwd;
+ ngx_array_t *passwords;
+ ngx_pool_cleanup_t *cln;
+ u_char buf[NGX_SSL_PASSWORD_BUFFER_SIZE];
+
+ if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
+ return NULL;
+ }
+
+ cln = ngx_pool_cleanup_add(cf->temp_pool, 0);
+ passwords = ngx_array_create(cf->temp_pool, 4, sizeof(ngx_str_t));
+
+ if (cln == NULL || passwords == NULL) {
+ return NULL;
+ }
+
+ cln->handler = ngx_ssl_passwords_cleanup;
+ cln->data = passwords;
+
+ fd = ngx_open_file(file->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+ if (fd == NGX_INVALID_FILE) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
+ ngx_open_file_n " \"%s\" failed", file->data);
+ return NULL;
+ }
+
+ len = 0;
+ last = buf;
+
+ do {
+ n = ngx_read_fd(fd, last, NGX_SSL_PASSWORD_BUFFER_SIZE - len);
+
+ if (n == -1) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
+ ngx_read_fd_n " \"%s\" failed", file->data);
+ passwords = NULL;
+ goto cleanup;
+ }
+
+ end = last + n;
+
+ if (len && n == 0) {
+ *end++ = LF;
+ }
+
+ p = buf;
+
+ for ( ;; ) {
+ last = ngx_strlchr(last, end, LF);
+
+ if (last == NULL) {
+ break;
+ }
+
+ len = last++ - p;
+
+ if (len && p[len - 1] == CR) {
+ len--;
+ }
+
+ if (len) {
+ pwd = ngx_array_push(passwords);
+ if (pwd == NULL) {
+ passwords = NULL;
+ goto cleanup;
+ }
+
+ pwd->len = len;
+ pwd->data = ngx_pnalloc(cf->temp_pool, len);
+
+ if (pwd->data == NULL) {
+ passwords->nelts--;
+ passwords = NULL;
+ goto cleanup;
+ }
+
+ ngx_memcpy(pwd->data, p, len);
+ }
+
+ p = last;
+ }
+
+ len = end - p;
+
+ if (len == NGX_SSL_PASSWORD_BUFFER_SIZE) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "too long line in \"%s\"", file->data);
+ passwords = NULL;
+ goto cleanup;
+ }
+
+ ngx_memmove(buf, p, len);
+ last = buf + len;
+
+ } while (n != 0);
+
+ if (passwords->nelts == 0) {
+ pwd = ngx_array_push(passwords);
+ if (pwd == NULL) {
+ passwords = NULL;
+ goto cleanup;
+ }
+
+ ngx_memzero(pwd, sizeof(ngx_str_t));
+ }
+
+cleanup:
+
+ if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+ ngx_conf_log_error(NGX_LOG_ALERT, cf, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", file->data);
+ }
+
+ ngx_memzero(buf, NGX_SSL_PASSWORD_BUFFER_SIZE);
+
+ return passwords;
+}
+
+
+static void
+ngx_ssl_passwords_cleanup(void *data)
+{
+ ngx_array_t *passwords = data;
+
+ ngx_str_t *pwd;
+ ngx_uint_t i;
+
+ pwd = passwords->elts;
+
+ for (i = 0; i < passwords->nelts; i++) {
+ ngx_memzero(pwd[i].data, pwd[i].len);
+ }
+}
+
+
+ngx_int_t
+ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
+{
+ DH *dh;
+ BIO *bio;
+
+ /*
+ * -----BEGIN DH PARAMETERS-----
+ * MIGHAoGBALu8LcrYRnSQfEP89YDpz9vZWKP1aLQtSwju1OsPs1BMbAMCducQgAxc
+ * y7qokiYUxb7spWWl/fHSh6K8BJvmd4Bg6RqSp1fjBI9osHb302zI8pul34HcLKcl
+ * 7OZicMyaUDXYzs7vnqAnSmOrHlj6/UmI0PZdFGdX2gcd8EXP4WubAgEC
+ * -----END DH PARAMETERS-----
+ */
+
+ static unsigned char dh1024_p[] = {
+ 0xBB, 0xBC, 0x2D, 0xCA, 0xD8, 0x46, 0x74, 0x90, 0x7C, 0x43, 0xFC, 0xF5,
+ 0x80, 0xE9, 0xCF, 0xDB, 0xD9, 0x58, 0xA3, 0xF5, 0x68, 0xB4, 0x2D, 0x4B,
+ 0x08, 0xEE, 0xD4, 0xEB, 0x0F, 0xB3, 0x50, 0x4C, 0x6C, 0x03, 0x02, 0x76,
+ 0xE7, 0x10, 0x80, 0x0C, 0x5C, 0xCB, 0xBA, 0xA8, 0x92, 0x26, 0x14, 0xC5,
+ 0xBE, 0xEC, 0xA5, 0x65, 0xA5, 0xFD, 0xF1, 0xD2, 0x87, 0xA2, 0xBC, 0x04,
+ 0x9B, 0xE6, 0x77, 0x80, 0x60, 0xE9, 0x1A, 0x92, 0xA7, 0x57, 0xE3, 0x04,
+ 0x8F, 0x68, 0xB0, 0x76, 0xF7, 0xD3, 0x6C, 0xC8, 0xF2, 0x9B, 0xA5, 0xDF,
+ 0x81, 0xDC, 0x2C, 0xA7, 0x25, 0xEC, 0xE6, 0x62, 0x70, 0xCC, 0x9A, 0x50,
+ 0x35, 0xD8, 0xCE, 0xCE, 0xEF, 0x9E, 0xA0, 0x27, 0x4A, 0x63, 0xAB, 0x1E,
+ 0x58, 0xFA, 0xFD, 0x49, 0x88, 0xD0, 0xF6, 0x5D, 0x14, 0x67, 0x57, 0xDA,
+ 0x07, 0x1D, 0xF0, 0x45, 0xCF, 0xE1, 0x6B, 0x9B
+ };
+
+ static unsigned char dh1024_g[] = { 0x02 };
+
+
+ if (file->len == 0) {
+
+ dh = DH_new();
+ if (dh == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "DH_new() failed");
+ return NGX_ERROR;
+ }
+
+ dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
+ dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
+
+ if (dh->p == NULL || dh->g == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "BN_bin2bn() failed");
+ DH_free(dh);
+ return NGX_ERROR;
+ }
+
+ SSL_CTX_set_tmp_dh(ssl->ctx, dh);
+
+ DH_free(dh);
+
+ return NGX_OK;
+ }
+
+ if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ bio = BIO_new_file((char *) file->data, "r");
+ if (bio == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "BIO_new_file(\"%s\") failed", file->data);
+ return NGX_ERROR;
+ }
+
+ dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+ if (dh == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "PEM_read_bio_DHparams(\"%s\") failed", file->data);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ SSL_CTX_set_tmp_dh(ssl->ctx, dh);
+
+ DH_free(dh);
+ BIO_free(bio);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+#ifndef OPENSSL_NO_ECDH
+ int nid;
+ EC_KEY *ecdh;
+
+ /*
+ * Elliptic-Curve Diffie-Hellman parameters are either "named curves"
+ * from RFC 4492 section 5.1.1, or explicitly described curves over
+ * binary fields. OpenSSL only supports the "named curves", which provide
+ * maximum interoperability.
+ */
+
+ nid = OBJ_sn2nid((const char *) name->data);
+ if (nid == 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "Unknown curve name \"%s\"", name->data);
+ return NGX_ERROR;
+ }
+
+ ecdh = EC_KEY_new_by_curve_name(nid);
+ if (ecdh == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "Unable to create curve \"%s\"", name->data);
+ return NGX_ERROR;
+ }
+
+ SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE);
+
+ SSL_CTX_set_tmp_ecdh(ssl->ctx, ecdh);
+
+ EC_KEY_free(ecdh);
+#endif
+#endif
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
+{
+ ngx_ssl_connection_t *sc;
+
+ sc = ngx_pcalloc(c->pool, sizeof(ngx_ssl_connection_t));
+ if (sc == NULL) {
+ return NGX_ERROR;
+ }
+
+ sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);
+ sc->buffer_size = ssl->buffer_size;
+
+ sc->connection = SSL_new(ssl->ctx);
+
+ if (sc->connection == NULL) {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_new() failed");
+ return NGX_ERROR;
+ }
+
+ if (SSL_set_fd(sc->connection, c->fd) == 0) {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_fd() failed");
+ return NGX_ERROR;
+ }
+
+ if (flags & NGX_SSL_CLIENT) {
+ SSL_set_connect_state(sc->connection);
+
+ } else {
+ SSL_set_accept_state(sc->connection);
+ }
+
+ if (SSL_set_ex_data(sc->connection, ngx_ssl_connection_index, c) == 0) {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed");
+ return NGX_ERROR;
+ }
+
+ c->ssl = sc;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session)
+{
+ if (session) {
+ if (SSL_set_session(c->ssl->connection, session) == 0) {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_session() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_handshake(ngx_connection_t *c)
+{
+ int n, sslerr;
+ ngx_err_t err;
+
+ ngx_ssl_clear_error(c->log);
+
+ n = SSL_do_handshake(c->ssl->connection);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
+
+ if (n == 1) {
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+#if (NGX_DEBUG)
+ {
+ char buf[129], *s, *d;
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+ const
+#endif
+ SSL_CIPHER *cipher;
+
+ cipher = SSL_get_current_cipher(c->ssl->connection);
+
+ if (cipher) {
+ SSL_CIPHER_description(cipher, &buf[1], 128);
+
+ for (s = &buf[1], d = buf; *s; s++) {
+ if (*s == ' ' && *d == ' ') {
+ continue;
+ }
+
+ if (*s == LF || *s == CR) {
+ continue;
+ }
+
+ *++d = *s;
+ }
+
+ if (*d != ' ') {
+ d++;
+ }
+
+ *d = '\0';
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL: %s, cipher: \"%s\"",
+ SSL_get_version(c->ssl->connection), &buf[1]);
+
+ if (SSL_session_reused(c->ssl->connection)) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL reused session");
+ }
+
+ } else {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL no shared ciphers");
+ }
+ }
+#endif
+
+ c->ssl->handshaked = 1;
+
+ c->recv = ngx_ssl_recv;
+ c->send = ngx_ssl_write;
+ c->recv_chain = ngx_ssl_recv_chain;
+ c->send_chain = ngx_ssl_send_chain;
+
+ /* initial handshake done, disable renegotiation (CVE-2009-3555) */
+ if (c->ssl->connection->s3) {
+ c->ssl->connection->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
+ }
+
+ return NGX_OK;
+ }
+
+ sslerr = SSL_get_error(c->ssl->connection, n);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
+
+ if (sslerr == SSL_ERROR_WANT_READ) {
+ c->read->ready = 0;
+ c->read->handler = ngx_ssl_handshake_handler;
+ c->write->handler = ngx_ssl_handshake_handler;
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ if (sslerr == SSL_ERROR_WANT_WRITE) {
+ c->write->ready = 0;
+ c->read->handler = ngx_ssl_handshake_handler;
+ c->write->handler = ngx_ssl_handshake_handler;
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
+
+ c->ssl->no_wait_shutdown = 1;
+ c->ssl->no_send_shutdown = 1;
+ c->read->eof = 1;
+
+ if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
+ ngx_connection_error(c, err,
+ "peer closed connection in SSL handshake");
+
+ return NGX_ERROR;
+ }
+
+ c->read->error = 1;
+
+ ngx_ssl_connection_error(c, sslerr, err, "SSL_do_handshake() failed");
+
+ return NGX_ERROR;
+}
+
+
+static void
+ngx_ssl_handshake_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *c;
+
+ c = ev->data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL handshake handler: %d", ev->write);
+
+ if (ev->timedout) {
+ c->ssl->handler(c);
+ return;
+ }
+
+ if (ngx_ssl_handshake(c) == NGX_AGAIN) {
+ return;
+ }
+
+ c->ssl->handler(c);
+}
+
+
+ssize_t
+ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl)
+{
+ u_char *last;
+ ssize_t n, bytes;
+ ngx_buf_t *b;
+
+ bytes = 0;
+
+ b = cl->buf;
+ last = b->last;
+
+ for ( ;; ) {
+
+ n = ngx_ssl_recv(c, last, b->end - last);
+
+ if (n > 0) {
+ last += n;
+ bytes += n;
+
+ if (last == b->end) {
+ cl = cl->next;
+
+ if (cl == NULL) {
+ return bytes;
+ }
+
+ b = cl->buf;
+ last = b->last;
+ }
+
+ continue;
+ }
+
+ if (bytes) {
+
+ if (n == 0 || n == NGX_ERROR) {
+ c->read->ready = 1;
+ }
+
+ return bytes;
+ }
+
+ return n;
+ }
+}
+
+
+ssize_t
+ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size)
+{
+ int n, bytes;
+
+ if (c->ssl->last == NGX_ERROR) {
+ c->read->error = 1;
+ return NGX_ERROR;
+ }
+
+ if (c->ssl->last == NGX_DONE) {
+ c->read->ready = 0;
+ c->read->eof = 1;
+ return 0;
+ }
+
+ bytes = 0;
+
+ ngx_ssl_clear_error(c->log);
+
+ /*
+ * SSL_read() may return data in parts, so try to read
+ * until SSL_read() would return no data
+ */
+
+ for ( ;; ) {
+
+ n = SSL_read(c->ssl->connection, buf, size);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_read: %d", n);
+
+ if (n > 0) {
+ bytes += n;
+ }
+
+ c->ssl->last = ngx_ssl_handle_recv(c, n);
+
+ if (c->ssl->last == NGX_OK) {
+
+ size -= n;
+
+ if (size == 0) {
+ c->read->ready = 1;
+ return bytes;
+ }
+
+ buf += n;
+
+ continue;
+ }
+
+ if (bytes) {
+ if (c->ssl->last != NGX_AGAIN) {
+ c->read->ready = 1;
+ }
+
+ return bytes;
+ }
+
+ switch (c->ssl->last) {
+
+ case NGX_DONE:
+ c->read->ready = 0;
+ c->read->eof = 1;
+ return 0;
+
+ case NGX_ERROR:
+ c->read->error = 1;
+
+ /* fall through */
+
+ case NGX_AGAIN:
+ return c->ssl->last;
+ }
+ }
+}
+
+
+static ngx_int_t
+ngx_ssl_handle_recv(ngx_connection_t *c, int n)
+{
+ int sslerr;
+ ngx_err_t err;
+
+ if (c->ssl->renegotiation) {
+ /*
+ * disable renegotiation (CVE-2009-3555):
+ * OpenSSL (at least up to 0.9.8l) does not handle disabled
+ * renegotiation gracefully, so drop connection here
+ */
+
+ ngx_log_error(NGX_LOG_NOTICE, c->log, 0, "SSL renegotiation disabled");
+
+ while (ERR_peek_error()) {
+ ngx_ssl_error(NGX_LOG_DEBUG, c->log, 0,
+ "ignoring stale global SSL error");
+ }
+
+ ERR_clear_error();
+
+ c->ssl->no_wait_shutdown = 1;
+ c->ssl->no_send_shutdown = 1;
+
+ return NGX_ERROR;
+ }
+
+ if (n > 0) {
+
+ if (c->ssl->saved_write_handler) {
+
+ c->write->handler = c->ssl->saved_write_handler;
+ c->ssl->saved_write_handler = NULL;
+ c->write->ready = 1;
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_post_event(c->write, &ngx_posted_events);
+ }
+
+ return NGX_OK;
+ }
+
+ sslerr = SSL_get_error(c->ssl->connection, n);
+
+ err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
+
+ if (sslerr == SSL_ERROR_WANT_READ) {
+ c->read->ready = 0;
+ return NGX_AGAIN;
+ }
+
+ if (sslerr == SSL_ERROR_WANT_WRITE) {
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "peer started SSL renegotiation");
+
+ c->write->ready = 0;
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ /*
+ * we do not set the timer because there is already the read event timer
+ */
+
+ if (c->ssl->saved_write_handler == NULL) {
+ c->ssl->saved_write_handler = c->write->handler;
+ c->write->handler = ngx_ssl_write_handler;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ c->ssl->no_wait_shutdown = 1;
+ c->ssl->no_send_shutdown = 1;
+
+ if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "peer shutdown SSL cleanly");
+ return NGX_DONE;
+ }
+
+ ngx_ssl_connection_error(c, sslerr, err, "SSL_read() failed");
+
+ return NGX_ERROR;
+}
+
+
+static void
+ngx_ssl_write_handler(ngx_event_t *wev)
+{
+ ngx_connection_t *c;
+
+ c = wev->data;
+
+ c->read->handler(c->read);
+}
+
+
+/*
+ * OpenSSL has no SSL_writev() so we copy several bufs into our 16K buffer
+ * before the SSL_write() call to decrease a SSL overhead.
+ *
+ * Besides for protocols such as HTTP it is possible to always buffer
+ * the output to decrease a SSL overhead some more.
+ */
+
+ngx_chain_t *
+ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
+{
+ int n;
+ ngx_uint_t flush;
+ ssize_t send, size;
+ ngx_buf_t *buf;
+
+ if (!c->ssl->buffer) {
+
+ while (in) {
+ if (ngx_buf_special(in->buf)) {
+ in = in->next;
+ continue;
+ }
+
+ n = ngx_ssl_write(c, in->buf->pos, in->buf->last - in->buf->pos);
+
+ if (n == NGX_ERROR) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ if (n == NGX_AGAIN) {
+ return in;
+ }
+
+ in->buf->pos += n;
+ c->sent += n;
+
+ if (in->buf->pos == in->buf->last) {
+ in = in->next;
+ }
+ }
+
+ return in;
+ }
+
+
+ /* the maximum limit size is the maximum int32_t value - the page size */
+
+ if (limit == 0 || limit > (off_t) (NGX_MAX_INT32_VALUE - ngx_pagesize)) {
+ limit = NGX_MAX_INT32_VALUE - ngx_pagesize;
+ }
+
+ buf = c->ssl->buf;
+
+ if (buf == NULL) {
+ buf = ngx_create_temp_buf(c->pool, c->ssl->buffer_size);
+ if (buf == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ c->ssl->buf = buf;
+ }
+
+ if (buf->start == NULL) {
+ buf->start = ngx_palloc(c->pool, c->ssl->buffer_size);
+ if (buf->start == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ buf->pos = buf->start;
+ buf->last = buf->start;
+ buf->end = buf->start + c->ssl->buffer_size;
+ }
+
+ send = buf->last - buf->pos;
+ flush = (in == NULL) ? 1 : buf->flush;
+
+ for ( ;; ) {
+
+ while (in && buf->last < buf->end && send < limit) {
+ if (in->buf->last_buf || in->buf->flush) {
+ flush = 1;
+ }
+
+ if (ngx_buf_special(in->buf)) {
+ in = in->next;
+ continue;
+ }
+
+ size = in->buf->last - in->buf->pos;
+
+ if (size > buf->end - buf->last) {
+ size = buf->end - buf->last;
+ }
+
+ if (send + size > limit) {
+ size = (ssize_t) (limit - send);
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL buf copy: %d", size);
+
+ ngx_memcpy(buf->last, in->buf->pos, size);
+
+ buf->last += size;
+ in->buf->pos += size;
+ send += size;
+
+ if (in->buf->pos == in->buf->last) {
+ in = in->next;
+ }
+ }
+
+ if (!flush && send < limit && buf->last < buf->end) {
+ break;
+ }
+
+ size = buf->last - buf->pos;
+
+ if (size == 0) {
+ buf->flush = 0;
+ c->buffered &= ~NGX_SSL_BUFFERED;
+ return in;
+ }
+
+ n = ngx_ssl_write(c, buf->pos, size);
+
+ if (n == NGX_ERROR) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ if (n == NGX_AGAIN) {
+ break;
+ }
+
+ buf->pos += n;
+ c->sent += n;
+
+ if (n < size) {
+ break;
+ }
+
+ flush = 0;
+
+ buf->pos = buf->start;
+ buf->last = buf->start;
+
+ if (in == NULL || send == limit) {
+ break;
+ }
+ }
+
+ buf->flush = flush;
+
+ if (buf->pos < buf->last) {
+ c->buffered |= NGX_SSL_BUFFERED;
+
+ } else {
+ c->buffered &= ~NGX_SSL_BUFFERED;
+ }
+
+ return in;
+}
+
+
+ssize_t
+ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size)
+{
+ int n, sslerr;
+ ngx_err_t err;
+
+ ngx_ssl_clear_error(c->log);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL to write: %d", size);
+
+ n = SSL_write(c->ssl->connection, data, size);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_write: %d", n);
+
+ if (n > 0) {
+
+ if (c->ssl->saved_read_handler) {
+
+ c->read->handler = c->ssl->saved_read_handler;
+ c->ssl->saved_read_handler = NULL;
+ c->read->ready = 1;
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_post_event(c->read, &ngx_posted_events);
+ }
+
+ return n;
+ }
+
+ sslerr = SSL_get_error(c->ssl->connection, n);
+
+ err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
+
+ if (sslerr == SSL_ERROR_WANT_WRITE) {
+ c->write->ready = 0;
+ return NGX_AGAIN;
+ }
+
+ if (sslerr == SSL_ERROR_WANT_READ) {
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "peer started SSL renegotiation");
+
+ c->read->ready = 0;
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ /*
+ * we do not set the timer because there is already
+ * the write event timer
+ */
+
+ if (c->ssl->saved_read_handler == NULL) {
+ c->ssl->saved_read_handler = c->read->handler;
+ c->read->handler = ngx_ssl_read_handler;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ c->ssl->no_wait_shutdown = 1;
+ c->ssl->no_send_shutdown = 1;
+ c->write->error = 1;
+
+ ngx_ssl_connection_error(c, sslerr, err, "SSL_write() failed");
+
+ return NGX_ERROR;
+}
+
+
+static void
+ngx_ssl_read_handler(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+
+ c = rev->data;
+
+ c->write->handler(c->write);
+}
+
+
+void
+ngx_ssl_free_buffer(ngx_connection_t *c)
+{
+ if (c->ssl->buf && c->ssl->buf->start) {
+ if (ngx_pfree(c->pool, c->ssl->buf->start) == NGX_OK) {
+ c->ssl->buf->start = NULL;
+ }
+ }
+}
+
+
+ngx_int_t
+ngx_ssl_shutdown(ngx_connection_t *c)
+{
+ int n, sslerr, mode;
+ ngx_err_t err;
+
+ if (c->timedout) {
+ mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN;
+ SSL_set_quiet_shutdown(c->ssl->connection, 1);
+
+ } else {
+ mode = SSL_get_shutdown(c->ssl->connection);
+
+ if (c->ssl->no_wait_shutdown) {
+ mode |= SSL_RECEIVED_SHUTDOWN;
+ }
+
+ if (c->ssl->no_send_shutdown) {
+ mode |= SSL_SENT_SHUTDOWN;
+ }
+
+ if (c->ssl->no_wait_shutdown && c->ssl->no_send_shutdown) {
+ SSL_set_quiet_shutdown(c->ssl->connection, 1);
+ }
+ }
+
+ SSL_set_shutdown(c->ssl->connection, mode);
+
+ ngx_ssl_clear_error(c->log);
+
+ n = SSL_shutdown(c->ssl->connection);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n);
+
+ sslerr = 0;
+
+ /* SSL_shutdown() never returns -1, on error it returns 0 */
+
+ if (n != 1 && ERR_peek_error()) {
+ sslerr = SSL_get_error(c->ssl->connection, n);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL_get_error: %d", sslerr);
+ }
+
+ if (n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN) {
+ SSL_free(c->ssl->connection);
+ c->ssl = NULL;
+
+ return NGX_OK;
+ }
+
+ if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) {
+ c->read->handler = ngx_ssl_shutdown_handler;
+ c->write->handler = ngx_ssl_shutdown_handler;
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (sslerr == SSL_ERROR_WANT_READ) {
+ ngx_add_timer(c->read, 30000);
+ }
+
+ return NGX_AGAIN;
+ }
+
+ err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
+
+ ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed");
+
+ SSL_free(c->ssl->connection);
+ c->ssl = NULL;
+
+ return NGX_ERROR;
+}
+
+
+static void
+ngx_ssl_shutdown_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *c;
+ ngx_connection_handler_pt handler;
+
+ c = ev->data;
+ handler = c->ssl->handler;
+
+ if (ev->timedout) {
+ c->timedout = 1;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "SSL shutdown handler");
+
+ if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+ return;
+ }
+
+ handler(c);
+}
+
+
+static void
+ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
+ char *text)
+{
+ int n;
+ ngx_uint_t level;
+
+ level = NGX_LOG_CRIT;
+
+ if (sslerr == SSL_ERROR_SYSCALL) {
+
+ if (err == NGX_ECONNRESET
+ || err == NGX_EPIPE
+ || err == NGX_ENOTCONN
+ || err == NGX_ETIMEDOUT
+ || err == NGX_ECONNREFUSED
+ || err == NGX_ENETDOWN
+ || err == NGX_ENETUNREACH
+ || err == NGX_EHOSTDOWN
+ || err == NGX_EHOSTUNREACH)
+ {
+ switch (c->log_error) {
+
+ case NGX_ERROR_IGNORE_ECONNRESET:
+ case NGX_ERROR_INFO:
+ level = NGX_LOG_INFO;
+ break;
+
+ case NGX_ERROR_ERR:
+ level = NGX_LOG_ERR;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ } else if (sslerr == SSL_ERROR_SSL) {
+
+ n = ERR_GET_REASON(ERR_peek_error());
+
+ /* handshake failures */
+ if (n == SSL_R_BAD_CHANGE_CIPHER_SPEC /* 103 */
+ || n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG /* 129 */
+ || n == SSL_R_DIGEST_CHECK_FAILED /* 149 */
+ || n == SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST /* 151 */
+ || n == SSL_R_EXCESSIVE_MESSAGE_SIZE /* 152 */
+ || n == SSL_R_LENGTH_MISMATCH /* 159 */
+ || n == SSL_R_NO_CIPHERS_PASSED /* 182 */
+ || n == SSL_R_NO_CIPHERS_SPECIFIED /* 183 */
+ || n == SSL_R_NO_COMPRESSION_SPECIFIED /* 187 */
+ || n == SSL_R_NO_SHARED_CIPHER /* 193 */
+ || n == SSL_R_RECORD_LENGTH_MISMATCH /* 213 */
+#ifdef SSL_R_PARSE_TLSEXT
+ || n == SSL_R_PARSE_TLSEXT /* 227 */
+#endif
+ || n == SSL_R_UNEXPECTED_MESSAGE /* 244 */
+ || n == SSL_R_UNEXPECTED_RECORD /* 245 */
+ || n == SSL_R_UNKNOWN_ALERT_TYPE /* 246 */
+ || n == SSL_R_UNKNOWN_PROTOCOL /* 252 */
+ || n == SSL_R_WRONG_VERSION_NUMBER /* 267 */
+ || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC /* 281 */
+#ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG
+ || n == SSL_R_RENEGOTIATE_EXT_TOO_LONG /* 335 */
+ || n == SSL_R_RENEGOTIATION_ENCODING_ERR /* 336 */
+ || n == SSL_R_RENEGOTIATION_MISMATCH /* 337 */
+#endif
+#ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED
+ || n == SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED /* 338 */
+#endif
+#ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING
+ || n == SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING /* 345 */
+#endif
+ || n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */
+ || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE /* 1010 */
+ || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC /* 1020 */
+ || n == SSL_R_TLSV1_ALERT_DECRYPTION_FAILED /* 1021 */
+ || n == SSL_R_TLSV1_ALERT_RECORD_OVERFLOW /* 1022 */
+ || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE /* 1030 */
+ || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE /* 1040 */
+ || n == SSL_R_SSLV3_ALERT_NO_CERTIFICATE /* 1041 */
+ || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE /* 1042 */
+ || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE /* 1043 */
+ || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED /* 1044 */
+ || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED /* 1045 */
+ || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN /* 1046 */
+ || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER /* 1047 */
+ || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA /* 1048 */
+ || n == SSL_R_TLSV1_ALERT_ACCESS_DENIED /* 1049 */
+ || n == SSL_R_TLSV1_ALERT_DECODE_ERROR /* 1050 */
+ || n == SSL_R_TLSV1_ALERT_DECRYPT_ERROR /* 1051 */
+ || n == SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION /* 1060 */
+ || n == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION /* 1070 */
+ || n == SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY /* 1071 */
+ || n == SSL_R_TLSV1_ALERT_INTERNAL_ERROR /* 1080 */
+ || n == SSL_R_TLSV1_ALERT_USER_CANCELLED /* 1090 */
+ || n == SSL_R_TLSV1_ALERT_NO_RENEGOTIATION) /* 1100 */
+ {
+ switch (c->log_error) {
+
+ case NGX_ERROR_IGNORE_ECONNRESET:
+ case NGX_ERROR_INFO:
+ level = NGX_LOG_INFO;
+ break;
+
+ case NGX_ERROR_ERR:
+ level = NGX_LOG_ERR;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ ngx_ssl_error(level, c->log, err, text);
+}
+
+
+static void
+ngx_ssl_clear_error(ngx_log_t *log)
+{
+ while (ERR_peek_error()) {
+ ngx_ssl_error(NGX_LOG_ALERT, log, 0, "ignoring stale global SSL error");
+ }
+
+ ERR_clear_error();
+}
+
+
+void ngx_cdecl
+ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)
+{
+ int flags;
+ u_long n;
+ va_list args;
+ u_char *p, *last;
+ u_char errstr[NGX_MAX_CONF_ERRSTR];
+ const char *data;
+
+ last = errstr + NGX_MAX_CONF_ERRSTR;
+
+ va_start(args, fmt);
+ p = ngx_vslprintf(errstr, last - 1, fmt, args);
+ va_end(args);
+
+ p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p);
+
+ for ( ;; ) {
+
+ n = ERR_peek_error_line_data(NULL, NULL, &data, &flags);
+
+ if (n == 0) {
+ break;
+ }
+
+ if (p >= last) {
+ goto next;
+ }
+
+ *p++ = ' ';
+
+ ERR_error_string_n(n, (char *) p, last - p);
+
+ while (p < last && *p) {
+ p++;
+ }
+
+ if (p < last && *data && (flags & ERR_TXT_STRING)) {
+ *p++ = ':';
+ p = ngx_cpystrn(p, (u_char *) data, last - p);
+ }
+
+ next:
+
+ (void) ERR_get_error();
+ }
+
+ ngx_log_error(level, log, err, "%s)", errstr);
+}
+
+
+ngx_int_t
+ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
+ ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout)
+{
+ long cache_mode;
+
+ SSL_CTX_set_timeout(ssl->ctx, (long) timeout);
+
+ if (builtin_session_cache == NGX_SSL_NO_SCACHE) {
+ SSL_CTX_set_session_cache_mode(ssl->ctx, SSL_SESS_CACHE_OFF);
+ return NGX_OK;
+ }
+
+ SSL_CTX_set_session_id_context(ssl->ctx, sess_ctx->data, sess_ctx->len);
+
+ if (builtin_session_cache == NGX_SSL_NONE_SCACHE) {
+
+ /*
+ * If the server explicitly says that it does not support
+ * session reuse (see SSL_SESS_CACHE_OFF above), then
+ * Outlook Express fails to upload a sent email to
+ * the Sent Items folder on the IMAP server via a separate IMAP
+ * connection in the background. Therefore we have a special
+ * mode (SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_INTERNAL_STORE)
+ * where the server pretends that it supports session reuse,
+ * but it does not actually store any session.
+ */
+
+ SSL_CTX_set_session_cache_mode(ssl->ctx,
+ SSL_SESS_CACHE_SERVER
+ |SSL_SESS_CACHE_NO_AUTO_CLEAR
+ |SSL_SESS_CACHE_NO_INTERNAL_STORE);
+
+ SSL_CTX_sess_set_cache_size(ssl->ctx, 1);
+
+ return NGX_OK;
+ }
+
+ cache_mode = SSL_SESS_CACHE_SERVER;
+
+ if (shm_zone && builtin_session_cache == NGX_SSL_NO_BUILTIN_SCACHE) {
+ cache_mode |= SSL_SESS_CACHE_NO_INTERNAL;
+ }
+
+ SSL_CTX_set_session_cache_mode(ssl->ctx, cache_mode);
+
+ if (builtin_session_cache != NGX_SSL_NO_BUILTIN_SCACHE) {
+
+ if (builtin_session_cache != NGX_SSL_DFLT_BUILTIN_SCACHE) {
+ SSL_CTX_sess_set_cache_size(ssl->ctx, builtin_session_cache);
+ }
+ }
+
+ if (shm_zone) {
+ SSL_CTX_sess_set_new_cb(ssl->ctx, ngx_ssl_new_session);
+ SSL_CTX_sess_set_get_cb(ssl->ctx, ngx_ssl_get_cached_session);
+ SSL_CTX_sess_set_remove_cb(ssl->ctx, ngx_ssl_remove_session);
+
+ if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_cache_index, shm_zone)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_set_ex_data() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)
+{
+ size_t len;
+ ngx_slab_pool_t *shpool;
+ ngx_ssl_session_cache_t *cache;
+
+ if (data) {
+ shm_zone->data = data;
+ return NGX_OK;
+ }
+
+ shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+ if (shm_zone->shm.exists) {
+ shm_zone->data = shpool->data;
+ return NGX_OK;
+ }
+
+ cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_session_cache_t));
+ if (cache == NULL) {
+ return NGX_ERROR;
+ }
+
+ shpool->data = cache;
+ shm_zone->data = cache;
+
+ ngx_rbtree_init(&cache->session_rbtree, &cache->sentinel,
+ ngx_ssl_session_rbtree_insert_value);
+
+ ngx_queue_init(&cache->expire_queue);
+
+ len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len;
+
+ shpool->log_ctx = ngx_slab_alloc(shpool, len);
+ if (shpool->log_ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_sprintf(shpool->log_ctx, " in SSL session shared cache \"%V\"%Z",
+ &shm_zone->shm.name);
+
+ shpool->log_nomem = 0;
+
+ return NGX_OK;
+}
+
+
+/*
+ * The length of the session id is 16 bytes for SSLv2 sessions and
+ * between 1 and 32 bytes for SSLv3/TLSv1, typically 32 bytes.
+ * It seems that the typical length of the external ASN1 representation
+ * of a session is 118 or 119 bytes for SSLv3/TSLv1.
+ *
+ * Thus on 32-bit platforms we allocate separately an rbtree node,
+ * a session id, and an ASN1 representation, they take accordingly
+ * 64, 32, and 128 bytes.
+ *
+ * On 64-bit platforms we allocate separately an rbtree node + session_id,
+ * and an ASN1 representation, they take accordingly 128 and 128 bytes.
+ *
+ * OpenSSL's i2d_SSL_SESSION() and d2i_SSL_SESSION are slow,
+ * so they are outside the code locked by shared pool mutex
+ */
+
+static int
+ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
+{
+ int len;
+ u_char *p, *id, *cached_sess, *session_id;
+ uint32_t hash;
+ SSL_CTX *ssl_ctx;
+ unsigned int session_id_length;
+ ngx_shm_zone_t *shm_zone;
+ ngx_connection_t *c;
+ ngx_slab_pool_t *shpool;
+ ngx_ssl_sess_id_t *sess_id;
+ ngx_ssl_session_cache_t *cache;
+ u_char buf[NGX_SSL_MAX_SESSION_SIZE];
+
+ len = i2d_SSL_SESSION(sess, NULL);
+
+ /* do not cache too big session */
+
+ if (len > (int) NGX_SSL_MAX_SESSION_SIZE) {
+ return 0;
+ }
+
+ p = buf;
+ i2d_SSL_SESSION(sess, &p);
+
+ c = ngx_ssl_get_connection(ssl_conn);
+
+ ssl_ctx = SSL_get_SSL_CTX(ssl_conn);
+ shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index);
+
+ cache = shm_zone->data;
+ shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+ ngx_shmtx_lock(&shpool->mutex);
+
+ /* drop one or two expired sessions */
+ ngx_ssl_expire_sessions(cache, shpool, 1);
+
+ cached_sess = ngx_slab_alloc_locked(shpool, len);
+
+ if (cached_sess == NULL) {
+
+ /* drop the oldest non-expired session and try once more */
+
+ ngx_ssl_expire_sessions(cache, shpool, 0);
+
+ cached_sess = ngx_slab_alloc_locked(shpool, len);
+
+ if (cached_sess == NULL) {
+ sess_id = NULL;
+ goto failed;
+ }
+ }
+
+ sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t));
+
+ if (sess_id == NULL) {
+
+ /* drop the oldest non-expired session and try once more */
+
+ ngx_ssl_expire_sessions(cache, shpool, 0);
+
+ sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t));
+
+ if (sess_id == NULL) {
+ goto failed;
+ }
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+
+ session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length);
+
+#else
+
+ session_id = sess->session_id;
+ session_id_length = sess->session_id_length;
+
+#endif
+
+#if (NGX_PTR_SIZE == 8)
+
+ id = sess_id->sess_id;
+
+#else
+
+ id = ngx_slab_alloc_locked(shpool, session_id_length);
+
+ if (id == NULL) {
+
+ /* drop the oldest non-expired session and try once more */
+
+ ngx_ssl_expire_sessions(cache, shpool, 0);
+
+ id = ngx_slab_alloc_locked(shpool, session_id_length);
+
+ if (id == NULL) {
+ goto failed;
+ }
+ }
+
+#endif
+
+ ngx_memcpy(cached_sess, buf, len);
+
+ ngx_memcpy(id, session_id, session_id_length);
+
+ hash = ngx_crc32_short(session_id, session_id_length);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "ssl new session: %08XD:%ud:%d",
+ hash, session_id_length, len);
+
+ sess_id->node.key = hash;
+ sess_id->node.data = (u_char) session_id_length;
+ sess_id->id = id;
+ sess_id->len = len;
+ sess_id->session = cached_sess;
+
+ sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx);
+
+ ngx_queue_insert_head(&cache->expire_queue, &sess_id->queue);
+
+ ngx_rbtree_insert(&cache->session_rbtree, &sess_id->node);
+
+ ngx_shmtx_unlock(&shpool->mutex);
+
+ return 0;
+
+failed:
+
+ if (cached_sess) {
+ ngx_slab_free_locked(shpool, cached_sess);
+ }
+
+ if (sess_id) {
+ ngx_slab_free_locked(shpool, sess_id);
+ }
+
+ ngx_shmtx_unlock(&shpool->mutex);
+
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "could not allocate new session%s", shpool->log_ctx);
+
+ return 0;
+}
+
+
+static ngx_ssl_session_t *
+ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn, u_char *id, int len,
+ int *copy)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x0090707fL
+ const
+#endif
+ u_char *p;
+ uint32_t hash;
+ ngx_int_t rc;
+ ngx_shm_zone_t *shm_zone;
+ ngx_slab_pool_t *shpool;
+ ngx_rbtree_node_t *node, *sentinel;
+ ngx_ssl_session_t *sess;
+ ngx_ssl_sess_id_t *sess_id;
+ ngx_ssl_session_cache_t *cache;
+ u_char buf[NGX_SSL_MAX_SESSION_SIZE];
+#if (NGX_DEBUG)
+ ngx_connection_t *c;
+#endif
+
+ hash = ngx_crc32_short(id, (size_t) len);
+ *copy = 0;
+
+#if (NGX_DEBUG)
+ c = ngx_ssl_get_connection(ssl_conn);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "ssl get session: %08XD:%d", hash, len);
+#endif
+
+ shm_zone = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn),
+ ngx_ssl_session_cache_index);
+
+ cache = shm_zone->data;
+
+ sess = NULL;
+
+ shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+ ngx_shmtx_lock(&shpool->mutex);
+
+ node = cache->session_rbtree.root;
+ sentinel = cache->session_rbtree.sentinel;
+
+ while (node != sentinel) {
+
+ if (hash < node->key) {
+ node = node->left;
+ continue;
+ }
+
+ if (hash > node->key) {
+ node = node->right;
+ continue;
+ }
+
+ /* hash == node->key */
+
+ sess_id = (ngx_ssl_sess_id_t *) node;
+
+ rc = ngx_memn2cmp(id, sess_id->id, (size_t) len, (size_t) node->data);
+
+ if (rc == 0) {
+
+ if (sess_id->expire > ngx_time()) {
+ ngx_memcpy(buf, sess_id->session, sess_id->len);
+
+ ngx_shmtx_unlock(&shpool->mutex);
+
+ p = buf;
+ sess = d2i_SSL_SESSION(NULL, &p, sess_id->len);
+
+ return sess;
+ }
+
+ ngx_queue_remove(&sess_id->queue);
+
+ ngx_rbtree_delete(&cache->session_rbtree, node);
+
+ ngx_slab_free_locked(shpool, sess_id->session);
+#if (NGX_PTR_SIZE == 4)
+ ngx_slab_free_locked(shpool, sess_id->id);
+#endif
+ ngx_slab_free_locked(shpool, sess_id);
+
+ sess = NULL;
+
+ goto done;
+ }
+
+ node = (rc < 0) ? node->left : node->right;
+ }
+
+done:
+
+ ngx_shmtx_unlock(&shpool->mutex);
+
+ return sess;
+}
+
+
+void
+ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
+{
+ SSL_CTX_remove_session(ssl, sess);
+
+ ngx_ssl_remove_session(ssl, sess);
+}
+
+
+static void
+ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
+{
+ u_char *id;
+ uint32_t hash;
+ ngx_int_t rc;
+ unsigned int len;
+ ngx_shm_zone_t *shm_zone;
+ ngx_slab_pool_t *shpool;
+ ngx_rbtree_node_t *node, *sentinel;
+ ngx_ssl_sess_id_t *sess_id;
+ ngx_ssl_session_cache_t *cache;
+
+ shm_zone = SSL_CTX_get_ex_data(ssl, ngx_ssl_session_cache_index);
+
+ if (shm_zone == NULL) {
+ return;
+ }
+
+ cache = shm_zone->data;
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+
+ id = (u_char *) SSL_SESSION_get_id(sess, &len);
+
+#else
+
+ id = sess->session_id;
+ len = sess->session_id_length;
+
+#endif
+
+ hash = ngx_crc32_short(id, len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
+ "ssl remove session: %08XD:%ud", hash, len);
+
+ shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+ ngx_shmtx_lock(&shpool->mutex);
+
+ node = cache->session_rbtree.root;
+ sentinel = cache->session_rbtree.sentinel;
+
+ while (node != sentinel) {
+
+ if (hash < node->key) {
+ node = node->left;
+ continue;
+ }
+
+ if (hash > node->key) {
+ node = node->right;
+ continue;
+ }
+
+ /* hash == node->key */
+
+ sess_id = (ngx_ssl_sess_id_t *) node;
+
+ rc = ngx_memn2cmp(id, sess_id->id, len, (size_t) node->data);
+
+ if (rc == 0) {
+
+ ngx_queue_remove(&sess_id->queue);
+
+ ngx_rbtree_delete(&cache->session_rbtree, node);
+
+ ngx_slab_free_locked(shpool, sess_id->session);
+#if (NGX_PTR_SIZE == 4)
+ ngx_slab_free_locked(shpool, sess_id->id);
+#endif
+ ngx_slab_free_locked(shpool, sess_id);
+
+ goto done;
+ }
+
+ node = (rc < 0) ? node->left : node->right;
+ }
+
+done:
+
+ ngx_shmtx_unlock(&shpool->mutex);
+}
+
+
+static void
+ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,
+ ngx_slab_pool_t *shpool, ngx_uint_t n)
+{
+ time_t now;
+ ngx_queue_t *q;
+ ngx_ssl_sess_id_t *sess_id;
+
+ now = ngx_time();
+
+ while (n < 3) {
+
+ if (ngx_queue_empty(&cache->expire_queue)) {
+ return;
+ }
+
+ q = ngx_queue_last(&cache->expire_queue);
+
+ sess_id = ngx_queue_data(q, ngx_ssl_sess_id_t, queue);
+
+ if (n++ != 0 && sess_id->expire > now) {
+ return;
+ }
+
+ ngx_queue_remove(q);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
+ "expire session: %08Xi", sess_id->node.key);
+
+ ngx_rbtree_delete(&cache->session_rbtree, &sess_id->node);
+
+ ngx_slab_free_locked(shpool, sess_id->session);
+#if (NGX_PTR_SIZE == 4)
+ ngx_slab_free_locked(shpool, sess_id->id);
+#endif
+ ngx_slab_free_locked(shpool, sess_id);
+ }
+}
+
+
+static void
+ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+ ngx_rbtree_node_t **p;
+ ngx_ssl_sess_id_t *sess_id, *sess_id_temp;
+
+ for ( ;; ) {
+
+ if (node->key < temp->key) {
+
+ p = &temp->left;
+
+ } else if (node->key > temp->key) {
+
+ p = &temp->right;
+
+ } else { /* node->key == temp->key */
+
+ sess_id = (ngx_ssl_sess_id_t *) node;
+ sess_id_temp = (ngx_ssl_sess_id_t *) temp;
+
+ p = (ngx_memn2cmp(sess_id->id, sess_id_temp->id,
+ (size_t) node->data, (size_t) temp->data)
+ < 0) ? &temp->left : &temp->right;
+ }
+
+ if (*p == sentinel) {
+ break;
+ }
+
+ temp = *p;
+ }
+
+ *p = node;
+ node->parent = temp;
+ node->left = sentinel;
+ node->right = sentinel;
+ ngx_rbt_red(node);
+}
+
+
+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+
+ngx_int_t
+ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)
+{
+ u_char buf[48];
+ ssize_t n;
+ ngx_str_t *path;
+ ngx_file_t file;
+ ngx_uint_t i;
+ ngx_array_t *keys;
+ ngx_file_info_t fi;
+ ngx_ssl_session_ticket_key_t *key;
+
+ if (paths == NULL) {
+ return NGX_OK;
+ }
+
+ keys = ngx_array_create(cf->pool, paths->nelts,
+ sizeof(ngx_ssl_session_ticket_key_t));
+ if (keys == NULL) {
+ return NGX_ERROR;
+ }
+
+ path = paths->elts;
+ for (i = 0; i < paths->nelts; i++) {
+
+ if (ngx_conf_full_name(cf->cycle, &path[i], 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_memzero(&file, sizeof(ngx_file_t));
+ file.name = path[i];
+ file.log = cf->log;
+
+ file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, 0, 0);
+ if (file.fd == NGX_INVALID_FILE) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
+ ngx_open_file_n " \"%V\" failed", &file.name);
+ return NGX_ERROR;
+ }
+
+ if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
+ ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
+ ngx_fd_info_n " \"%V\" failed", &file.name);
+ goto failed;
+ }
+
+ if (ngx_file_size(&fi) != 48) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"%V\" must be 48 bytes", &file.name);
+ goto failed;
+ }
+
+ n = ngx_read_file(&file, buf, 48, 0);
+
+ if (n == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
+ ngx_read_file_n " \"%V\" failed", &file.name);
+ goto failed;
+ }
+
+ if (n != 48) {
+ ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
+ ngx_read_file_n " \"%V\" returned only "
+ "%z bytes instead of 48", &file.name, n);
+ goto failed;
+ }
+
+ key = ngx_array_push(keys);
+ if (key == NULL) {
+ goto failed;
+ }
+
+ ngx_memcpy(key->name, buf, 16);
+ ngx_memcpy(key->aes_key, buf + 16, 16);
+ ngx_memcpy(key->hmac_key, buf + 32, 16);
+
+ if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_close_file_n " \"%V\" failed", &file.name);
+ }
+ }
+
+ if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_ticket_keys_index, keys)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_set_ex_data() failed");
+ return NGX_ERROR;
+ }
+
+ if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx,
+ ngx_ssl_session_ticket_key_callback)
+ == 0)
+ {
+ ngx_log_error(NGX_LOG_WARN, cf->log, 0,
+ "nginx was built with Session Tickets support, however, "
+ "now it is linked dynamically to an OpenSSL library "
+ "which has no tlsext support, therefore Session Tickets "
+ "are not available");
+ }
+
+ return NGX_OK;
+
+failed:
+
+ if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_close_file_n " \"%V\" failed", &file.name);
+ }
+
+ return NGX_ERROR;
+}
+
+
+#ifdef OPENSSL_NO_SHA256
+#define ngx_ssl_session_ticket_md EVP_sha1
+#else
+#define ngx_ssl_session_ticket_md EVP_sha256
+#endif
+
+
+static int
+ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
+ unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
+ HMAC_CTX *hctx, int enc)
+{
+ SSL_CTX *ssl_ctx;
+ ngx_uint_t i;
+ ngx_array_t *keys;
+ ngx_ssl_session_ticket_key_t *key;
+#if (NGX_DEBUG)
+ u_char buf[32];
+ ngx_connection_t *c;
+#endif
+
+ ssl_ctx = SSL_get_SSL_CTX(ssl_conn);
+
+ keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_ticket_keys_index);
+ if (keys == NULL) {
+ return -1;
+ }
+
+ key = keys->elts;
+
+#if (NGX_DEBUG)
+ c = ngx_ssl_get_connection(ssl_conn);
+#endif
+
+ if (enc == 1) {
+ /* encrypt session ticket */
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "ssl session ticket encrypt, key: \"%*s\" (%s session)",
+ ngx_hex_dump(buf, key[0].name, 16) - buf, buf,
+ SSL_session_reused(ssl_conn) ? "reused" : "new");
+
+ RAND_pseudo_bytes(iv, 16);
+ EVP_EncryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, key[0].aes_key, iv);
+ HMAC_Init_ex(hctx, key[0].hmac_key, 16,
+ ngx_ssl_session_ticket_md(), NULL);
+ ngx_memcpy(name, key[0].name, 16);
+
+ return 0;
+
+ } else {
+ /* decrypt session ticket */
+
+ for (i = 0; i < keys->nelts; i++) {
+ if (ngx_memcmp(name, key[i].name, 16) == 0) {
+ goto found;
+ }
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "ssl session ticket decrypt, key: \"%*s\" not found",
+ ngx_hex_dump(buf, name, 16) - buf, buf);
+
+ return 0;
+
+ found:
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "ssl session ticket decrypt, key: \"%*s\"%s",
+ ngx_hex_dump(buf, key[i].name, 16) - buf, buf,
+ (i == 0) ? " (default)" : "");
+
+ HMAC_Init_ex(hctx, key[i].hmac_key, 16,
+ ngx_ssl_session_ticket_md(), NULL);
+ EVP_DecryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, key[i].aes_key, iv);
+
+ return (i == 0) ? 1 : 2 /* renew */;
+ }
+}
+
+#else
+
+ngx_int_t
+ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)
+{
+ if (paths) {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_session_ticket_keys\" ignored, not supported");
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
+void
+ngx_ssl_cleanup_ctx(void *data)
+{
+ ngx_ssl_t *ssl = data;
+
+ SSL_CTX_free(ssl->ctx);
+}
+
+
+ngx_int_t
+ngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name)
+{
+ X509 *cert;
+
+ cert = SSL_get_peer_certificate(c->ssl->connection);
+ if (cert == NULL) {
+ return NGX_ERROR;
+ }
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10002002L && !defined LIBRESSL_VERSION_NUMBER)
+
+ /* X509_check_host() is only available in OpenSSL 1.0.2+ */
+
+ if (name->len == 0) {
+ goto failed;
+ }
+
+ if (X509_check_host(cert, (char *) name->data, name->len, 0, NULL) != 1) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "X509_check_host(): no match");
+ goto failed;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "X509_check_host(): match");
+
+ goto found;
+
+#else
+ {
+ int n, i;
+ X509_NAME *sname;
+ ASN1_STRING *str;
+ X509_NAME_ENTRY *entry;
+ GENERAL_NAME *altname;
+ STACK_OF(GENERAL_NAME) *altnames;
+
+ /*
+ * As per RFC6125 and RFC2818, we check subjectAltName extension,
+ * and if it's not present - commonName in Subject is checked.
+ */
+
+ altnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+ if (altnames) {
+ n = sk_GENERAL_NAME_num(altnames);
+
+ for (i = 0; i < n; i++) {
+ altname = sk_GENERAL_NAME_value(altnames, i);
+
+ if (altname->type != GEN_DNS) {
+ continue;
+ }
+
+ str = altname->d.dNSName;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL subjectAltName: \"%*s\"",
+ ASN1_STRING_length(str), ASN1_STRING_data(str));
+
+ if (ngx_ssl_check_name(name, str) == NGX_OK) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL subjectAltName: match");
+ GENERAL_NAMES_free(altnames);
+ goto found;
+ }
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL subjectAltName: no match");
+
+ GENERAL_NAMES_free(altnames);
+ goto failed;
+ }
+
+ /*
+ * If there is no subjectAltName extension, check commonName
+ * in Subject. While RFC2818 requires to only check "most specific"
+ * CN, both Apache and OpenSSL check all CNs, and so do we.
+ */
+
+ sname = X509_get_subject_name(cert);
+
+ if (sname == NULL) {
+ goto failed;
+ }
+
+ i = -1;
+ for ( ;; ) {
+ i = X509_NAME_get_index_by_NID(sname, NID_commonName, i);
+
+ if (i < 0) {
+ break;
+ }
+
+ entry = X509_NAME_get_entry(sname, i);
+ str = X509_NAME_ENTRY_get_data(entry);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL commonName: \"%*s\"",
+ ASN1_STRING_length(str), ASN1_STRING_data(str));
+
+ if (ngx_ssl_check_name(name, str) == NGX_OK) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL commonName: match");
+ goto found;
+ }
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL commonName: no match");
+ }
+#endif
+
+failed:
+
+ X509_free(cert);
+ return NGX_ERROR;
+
+found:
+
+ X509_free(cert);
+ return NGX_OK;
+}
+
+
+#if (OPENSSL_VERSION_NUMBER < 0x10002002L || defined LIBRESSL_VERSION_NUMBER)
+
+static ngx_int_t
+ngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *pattern)
+{
+ u_char *s, *p, *end;
+ size_t slen, plen;
+
+ s = name->data;
+ slen = name->len;
+
+ p = ASN1_STRING_data(pattern);
+ plen = ASN1_STRING_length(pattern);
+
+ if (slen == plen && ngx_strncasecmp(s, p, plen) == 0) {
+ return NGX_OK;
+ }
+
+ if (plen > 2 && p[0] == '*' && p[1] == '.') {
+ plen -= 1;
+ p += 1;
+
+ end = s + slen;
+ s = ngx_strlchr(s, end, '.');
+
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ slen = end - s;
+
+ if (plen == slen && ngx_strncasecmp(s, p, plen) == 0) {
+ return NGX_OK;
+ }
+ }
+
+ return NGX_ERROR;
+}
+
+#endif
+
+
+ngx_int_t
+ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+ s->data = (u_char *) SSL_get_version(c->ssl->connection);
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+ s->data = (u_char *) SSL_get_cipher_name(c->ssl->connection);
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+ u_char *buf;
+ SSL_SESSION *sess;
+ unsigned int len;
+
+ sess = SSL_get0_session(c->ssl->connection);
+ if (sess == NULL) {
+ s->len = 0;
+ return NGX_OK;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+
+ buf = (u_char *) SSL_SESSION_get_id(sess, &len);
+
+#else
+
+ buf = sess->session_id;
+ len = sess->session_id_length;
+
+#endif
+
+ s->len = 2 * len;
+ s->data = ngx_pnalloc(pool, 2 * len);
+ if (s->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_hex_dump(s->data, buf, len);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+ if (SSL_session_reused(c->ssl->connection)) {
+ ngx_str_set(s, "r");
+
+ } else {
+ ngx_str_set(s, ".");
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_server_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+
+ const char *servername;
+
+ servername = SSL_get_servername(c->ssl->connection,
+ TLSEXT_NAMETYPE_host_name);
+ if (servername) {
+ s->data = (u_char *) servername;
+ s->len = ngx_strlen(servername);
+ return NGX_OK;
+ }
+
+#endif
+
+ s->len = 0;
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+ size_t len;
+ BIO *bio;
+ X509 *cert;
+
+ s->len = 0;
+
+ cert = SSL_get_peer_certificate(c->ssl->connection);
+ if (cert == NULL) {
+ return NGX_OK;
+ }
+
+ bio = BIO_new(BIO_s_mem());
+ if (bio == NULL) {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed");
+ X509_free(cert);
+ return NGX_ERROR;
+ }
+
+ if (PEM_write_bio_X509(bio, cert) == 0) {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "PEM_write_bio_X509() failed");
+ goto failed;
+ }
+
+ len = BIO_pending(bio);
+ s->len = len;
+
+ s->data = ngx_pnalloc(pool, len);
+ if (s->data == NULL) {
+ goto failed;
+ }
+
+ BIO_read(bio, s->data, len);
+
+ BIO_free(bio);
+ X509_free(cert);
+
+ return NGX_OK;
+
+failed:
+
+ BIO_free(bio);
+ X509_free(cert);
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_str_t cert;
+
+ if (ngx_ssl_get_raw_certificate(c, pool, &cert) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (cert.len == 0) {
+ s->len = 0;
+ return NGX_OK;
+ }
+
+ len = cert.len - 1;
+
+ for (i = 0; i < cert.len - 1; i++) {
+ if (cert.data[i] == LF) {
+ len++;
+ }
+ }
+
+ s->len = len;
+ s->data = ngx_pnalloc(pool, len);
+ if (s->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = s->data;
+
+ for (i = 0; i < cert.len - 1; i++) {
+ *p++ = cert.data[i];
+ if (cert.data[i] == LF) {
+ *p++ = '\t';
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+ char *p;
+ size_t len;
+ X509 *cert;
+ X509_NAME *name;
+
+ s->len = 0;
+
+ cert = SSL_get_peer_certificate(c->ssl->connection);
+ if (cert == NULL) {
+ return NGX_OK;
+ }
+
+ name = X509_get_subject_name(cert);
+ if (name == NULL) {
+ X509_free(cert);
+ return NGX_ERROR;
+ }
+
+ p = X509_NAME_oneline(name, NULL, 0);
+
+ for (len = 0; p[len]; len++) { /* void */ }
+
+ s->len = len;
+ s->data = ngx_pnalloc(pool, len);
+ if (s->data == NULL) {
+ OPENSSL_free(p);
+ X509_free(cert);
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->data, p, len);
+
+ OPENSSL_free(p);
+ X509_free(cert);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+ char *p;
+ size_t len;
+ X509 *cert;
+ X509_NAME *name;
+
+ s->len = 0;
+
+ cert = SSL_get_peer_certificate(c->ssl->connection);
+ if (cert == NULL) {
+ return NGX_OK;
+ }
+
+ name = X509_get_issuer_name(cert);
+ if (name == NULL) {
+ X509_free(cert);
+ return NGX_ERROR;
+ }
+
+ p = X509_NAME_oneline(name, NULL, 0);
+
+ for (len = 0; p[len]; len++) { /* void */ }
+
+ s->len = len;
+ s->data = ngx_pnalloc(pool, len);
+ if (s->data == NULL) {
+ OPENSSL_free(p);
+ X509_free(cert);
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->data, p, len);
+
+ OPENSSL_free(p);
+ X509_free(cert);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+ size_t len;
+ X509 *cert;
+ BIO *bio;
+
+ s->len = 0;
+
+ cert = SSL_get_peer_certificate(c->ssl->connection);
+ if (cert == NULL) {
+ return NGX_OK;
+ }
+
+ bio = BIO_new(BIO_s_mem());
+ if (bio == NULL) {
+ X509_free(cert);
+ return NGX_ERROR;
+ }
+
+ i2a_ASN1_INTEGER(bio, X509_get_serialNumber(cert));
+ len = BIO_pending(bio);
+
+ s->len = len;
+ s->data = ngx_pnalloc(pool, len);
+ if (s->data == NULL) {
+ BIO_free(bio);
+ X509_free(cert);
+ return NGX_ERROR;
+ }
+
+ BIO_read(bio, s->data, len);
+ BIO_free(bio);
+ X509_free(cert);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_fingerprint(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+ X509 *cert;
+ unsigned int len;
+ u_char buf[EVP_MAX_MD_SIZE];
+
+ s->len = 0;
+
+ cert = SSL_get_peer_certificate(c->ssl->connection);
+ if (cert == NULL) {
+ return NGX_OK;
+ }
+
+ if (!X509_digest(cert, EVP_sha1(), buf, &len)) {
+ X509_free(cert);
+ return NGX_ERROR;
+ }
+
+ s->len = 2 * len;
+ s->data = ngx_pnalloc(pool, 2 * len);
+ if (s->data == NULL) {
+ X509_free(cert);
+ return NGX_ERROR;
+ }
+
+ ngx_hex_dump(s->data, buf, len);
+
+ X509_free(cert);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+ X509 *cert;
+
+ if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) {
+ ngx_str_set(s, "FAILED");
+ return NGX_OK;
+ }
+
+ cert = SSL_get_peer_certificate(c->ssl->connection);
+
+ if (cert) {
+ ngx_str_set(s, "SUCCESS");
+
+ } else {
+ ngx_str_set(s, "NONE");
+ }
+
+ X509_free(cert);
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_openssl_create_conf(ngx_cycle_t *cycle)
+{
+ ngx_openssl_conf_t *oscf;
+
+ oscf = ngx_pcalloc(cycle->pool, sizeof(ngx_openssl_conf_t));
+ if (oscf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * oscf->engine = 0;
+ */
+
+ return oscf;
+}
+
+
+static char *
+ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+#ifndef OPENSSL_NO_ENGINE
+
+ ngx_openssl_conf_t *oscf = conf;
+
+ ENGINE *engine;
+ ngx_str_t *value;
+
+ if (oscf->engine) {
+ return "is duplicate";
+ }
+
+ oscf->engine = 1;
+
+ value = cf->args->elts;
+
+ engine = ENGINE_by_id((const char *) value[1].data);
+
+ if (engine == NULL) {
+ ngx_ssl_error(NGX_LOG_WARN, cf->log, 0,
+ "ENGINE_by_id(\"%V\") failed", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (ENGINE_set_default(engine, ENGINE_METHOD_ALL) == 0) {
+ ngx_ssl_error(NGX_LOG_WARN, cf->log, 0,
+ "ENGINE_set_default(\"%V\", ENGINE_METHOD_ALL) failed",
+ &value[1]);
+
+ ENGINE_free(engine);
+
+ return NGX_CONF_ERROR;
+ }
+
+ ENGINE_free(engine);
+
+ return NGX_CONF_OK;
+
+#else
+
+ return "is not supported";
+
+#endif
+}
+
+
+static void
+ngx_openssl_exit(ngx_cycle_t *cycle)
+{
+ EVP_cleanup();
+#ifndef OPENSSL_NO_ENGINE
+ ENGINE_cleanup();
+#endif
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_openssl.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_openssl.h
new file mode 100644
index 00000000000..40869403519
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_openssl.h
@@ -0,0 +1,215 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_EVENT_OPENSSL_H_INCLUDED_
+#define _NGX_EVENT_OPENSSL_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/bn.h>
+#include <openssl/conf.h>
+#include <openssl/crypto.h>
+#include <openssl/dh.h>
+#ifndef OPENSSL_NO_ENGINE
+#include <openssl/engine.h>
+#endif
+#include <openssl/evp.h>
+#ifndef OPENSSL_NO_OCSP
+#include <openssl/ocsp.h>
+#endif
+#include <openssl/rand.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#define NGX_SSL_NAME "OpenSSL"
+
+
+#define ngx_ssl_session_t SSL_SESSION
+#define ngx_ssl_conn_t SSL
+
+
+typedef struct {
+ SSL_CTX *ctx;
+ ngx_log_t *log;
+ size_t buffer_size;
+} ngx_ssl_t;
+
+
+typedef struct {
+ ngx_ssl_conn_t *connection;
+
+ ngx_int_t last;
+ ngx_buf_t *buf;
+ size_t buffer_size;
+
+ ngx_connection_handler_pt handler;
+
+ ngx_event_handler_pt saved_read_handler;
+ ngx_event_handler_pt saved_write_handler;
+
+ unsigned handshaked:1;
+ unsigned renegotiation:1;
+ unsigned buffer:1;
+ unsigned no_wait_shutdown:1;
+ unsigned no_send_shutdown:1;
+ unsigned handshake_buffer_set:1;
+} ngx_ssl_connection_t;
+
+
+#define NGX_SSL_NO_SCACHE -2
+#define NGX_SSL_NONE_SCACHE -3
+#define NGX_SSL_NO_BUILTIN_SCACHE -4
+#define NGX_SSL_DFLT_BUILTIN_SCACHE -5
+
+
+#define NGX_SSL_MAX_SESSION_SIZE 4096
+
+typedef struct ngx_ssl_sess_id_s ngx_ssl_sess_id_t;
+
+struct ngx_ssl_sess_id_s {
+ ngx_rbtree_node_t node;
+ u_char *id;
+ size_t len;
+ u_char *session;
+ ngx_queue_t queue;
+ time_t expire;
+#if (NGX_PTR_SIZE == 8)
+ void *stub;
+ u_char sess_id[32];
+#endif
+};
+
+
+typedef struct {
+ ngx_rbtree_t session_rbtree;
+ ngx_rbtree_node_t sentinel;
+ ngx_queue_t expire_queue;
+} ngx_ssl_session_cache_t;
+
+
+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+
+typedef struct {
+ u_char name[16];
+ u_char aes_key[16];
+ u_char hmac_key[16];
+} ngx_ssl_session_ticket_key_t;
+
+#endif
+
+
+#define NGX_SSL_SSLv2 0x0002
+#define NGX_SSL_SSLv3 0x0004
+#define NGX_SSL_TLSv1 0x0008
+#define NGX_SSL_TLSv1_1 0x0010
+#define NGX_SSL_TLSv1_2 0x0020
+
+
+#define NGX_SSL_BUFFER 1
+#define NGX_SSL_CLIENT 2
+
+#define NGX_SSL_BUFSIZE 16384
+
+
+ngx_int_t ngx_ssl_init(ngx_log_t *log);
+ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data);
+ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
+ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_str_t *cert, ngx_int_t depth);
+ngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_str_t *cert, ngx_int_t depth);
+ngx_int_t ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl);
+ngx_int_t ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify);
+ngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_resolver_t *resolver, ngx_msec_t resolver_timeout);
+RSA *ngx_ssl_rsa512_key_callback(ngx_ssl_conn_t *ssl_conn, int is_export,
+ int key_length);
+ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file);
+ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
+ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);
+ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
+ ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout);
+ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_array_t *paths);
+ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
+ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
+ ngx_uint_t flags);
+
+void ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess);
+ngx_int_t ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session);
+#define ngx_ssl_get_session(c) SSL_get1_session(c->ssl->connection)
+#define ngx_ssl_free_session SSL_SESSION_free
+#define ngx_ssl_get_connection(ssl_conn) \
+ SSL_get_ex_data(ssl_conn, ngx_ssl_connection_index)
+#define ngx_ssl_get_server_conf(ssl_ctx) \
+ SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_server_conf_index)
+
+#define ngx_ssl_verify_error_optional(n) \
+ (n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT \
+ || n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN \
+ || n == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY \
+ || n == X509_V_ERR_CERT_UNTRUSTED \
+ || n == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)
+
+ngx_int_t ngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name);
+
+
+ngx_int_t ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool,
+ ngx_str_t *s);
+ngx_int_t ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool,
+ ngx_str_t *s);
+ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool,
+ ngx_str_t *s);
+ngx_int_t ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool,
+ ngx_str_t *s);
+ngx_int_t ngx_ssl_get_server_name(ngx_connection_t *c, ngx_pool_t *pool,
+ ngx_str_t *s);
+ngx_int_t ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool,
+ ngx_str_t *s);
+ngx_int_t ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool,
+ ngx_str_t *s);
+ngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool,
+ ngx_str_t *s);
+ngx_int_t ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool,
+ ngx_str_t *s);
+ngx_int_t ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool,
+ ngx_str_t *s);
+ngx_int_t ngx_ssl_get_fingerprint(ngx_connection_t *c, ngx_pool_t *pool,
+ ngx_str_t *s);
+ngx_int_t ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool,
+ ngx_str_t *s);
+
+
+ngx_int_t ngx_ssl_handshake(ngx_connection_t *c);
+ssize_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size);
+ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size);
+ssize_t ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl);
+ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in,
+ off_t limit);
+void ngx_ssl_free_buffer(ngx_connection_t *c);
+ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c);
+void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
+ char *fmt, ...);
+void ngx_ssl_cleanup_ctx(void *data);
+
+
+extern int ngx_ssl_connection_index;
+extern int ngx_ssl_server_conf_index;
+extern int ngx_ssl_session_cache_index;
+extern int ngx_ssl_session_ticket_keys_index;
+extern int ngx_ssl_certificate_index;
+extern int ngx_ssl_stapling_index;
+
+
+#endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_openssl_stapling.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_openssl_stapling.c
new file mode 100644
index 00000000000..2fa0673093e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_openssl_stapling.c
@@ -0,0 +1,1762 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+
+
+#if (!defined OPENSSL_NO_OCSP && defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB)
+
+
+typedef struct {
+ ngx_str_t staple;
+ ngx_msec_t timeout;
+
+ ngx_resolver_t *resolver;
+ ngx_msec_t resolver_timeout;
+
+ ngx_addr_t *addrs;
+ ngx_str_t host;
+ ngx_str_t uri;
+ in_port_t port;
+
+ SSL_CTX *ssl_ctx;
+
+ X509 *cert;
+ X509 *issuer;
+
+ time_t valid;
+
+ unsigned verify:1;
+ unsigned loading:1;
+} ngx_ssl_stapling_t;
+
+
+typedef struct ngx_ssl_ocsp_ctx_s ngx_ssl_ocsp_ctx_t;
+
+struct ngx_ssl_ocsp_ctx_s {
+ X509 *cert;
+ X509 *issuer;
+
+ ngx_uint_t naddrs;
+
+ ngx_addr_t *addrs;
+ ngx_str_t host;
+ ngx_str_t uri;
+ in_port_t port;
+
+ ngx_resolver_t *resolver;
+ ngx_msec_t resolver_timeout;
+
+ ngx_msec_t timeout;
+
+ void (*handler)(ngx_ssl_ocsp_ctx_t *r);
+ void *data;
+
+ ngx_buf_t *request;
+ ngx_buf_t *response;
+ ngx_peer_connection_t peer;
+
+ ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *r);
+
+ ngx_uint_t state;
+
+ ngx_uint_t code;
+ ngx_uint_t count;
+
+ ngx_uint_t done;
+
+ u_char *header_name_start;
+ u_char *header_name_end;
+ u_char *header_start;
+ u_char *header_end;
+
+ ngx_pool_t *pool;
+ ngx_log_t *log;
+};
+
+
+static ngx_int_t ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_str_t *file);
+static ngx_int_t ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl);
+static ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_str_t *responder);
+
+static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn,
+ void *data);
+static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple);
+static void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx);
+
+static void ngx_ssl_stapling_cleanup(void *data);
+
+static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void);
+static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx);
+static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx);
+static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve);
+static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx);
+static void ngx_ssl_ocsp_write_handler(ngx_event_t *wev);
+static void ngx_ssl_ocsp_read_handler(ngx_event_t *rev);
+static void ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev);
+
+static ngx_int_t ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx);
+static ngx_int_t ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx);
+
+static u_char *ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len);
+
+
+ngx_int_t
+ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
+ ngx_str_t *responder, ngx_uint_t verify)
+{
+ ngx_int_t rc;
+ ngx_pool_cleanup_t *cln;
+ ngx_ssl_stapling_t *staple;
+
+ staple = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_stapling_t));
+ if (staple == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_ssl_stapling_cleanup;
+ cln->data = staple;
+
+ if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_stapling_index, staple)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_set_ex_data() failed");
+ return NGX_ERROR;
+ }
+
+ staple->ssl_ctx = ssl->ctx;
+ staple->timeout = 60000;
+ staple->verify = verify;
+
+ if (file->len) {
+ /* use OCSP response from the file */
+
+ if (ngx_ssl_stapling_file(cf, ssl, file) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ goto done;
+ }
+
+ rc = ngx_ssl_stapling_issuer(cf, ssl);
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ rc = ngx_ssl_stapling_responder(cf, ssl, responder);
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+ if (rc != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+done:
+
+ SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback);
+ SSL_CTX_set_tlsext_status_arg(ssl->ctx, staple);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
+{
+ BIO *bio;
+ int len;
+ u_char *p, *buf;
+ OCSP_RESPONSE *response;
+ ngx_ssl_stapling_t *staple;
+
+ staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
+
+ if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ bio = BIO_new_file((char *) file->data, "r");
+ if (bio == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "BIO_new_file(\"%s\") failed", file->data);
+ return NGX_ERROR;
+ }
+
+ response = d2i_OCSP_RESPONSE_bio(bio, NULL);
+ if (response == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "d2i_OCSP_RESPONSE_bio(\"%s\") failed", file->data);
+ BIO_free(bio);
+ return NGX_ERROR;
+ }
+
+ len = i2d_OCSP_RESPONSE(response, NULL);
+ if (len <= 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "i2d_OCSP_RESPONSE(\"%s\") failed", file->data);
+ goto failed;
+ }
+
+ buf = ngx_alloc(len, ssl->log);
+ if (buf == NULL) {
+ goto failed;
+ }
+
+ p = buf;
+ len = i2d_OCSP_RESPONSE(response, &p);
+ if (len <= 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "i2d_OCSP_RESPONSE(\"%s\") failed", file->data);
+ ngx_free(buf);
+ goto failed;
+ }
+
+ OCSP_RESPONSE_free(response);
+ BIO_free(bio);
+
+ staple->staple.data = buf;
+ staple->staple.len = len;
+
+ return NGX_OK;
+
+failed:
+
+ OCSP_RESPONSE_free(response);
+ BIO_free(bio);
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl)
+{
+ int i, n, rc;
+ X509 *cert, *issuer;
+ X509_STORE *store;
+ X509_STORE_CTX *store_ctx;
+ STACK_OF(X509) *chain;
+ ngx_ssl_stapling_t *staple;
+
+ staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
+ cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ SSL_CTX_get_extra_chain_certs(ssl->ctx, &chain);
+#else
+ chain = ssl->ctx->extra_certs;
+#endif
+
+ n = sk_X509_num(chain);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
+ "SSL get issuer: %d extra certs", n);
+
+ for (i = 0; i < n; i++) {
+ issuer = sk_X509_value(chain, i);
+ if (X509_check_issued(issuer, cert) == X509_V_OK) {
+ CRYPTO_add(&issuer->references, 1, CRYPTO_LOCK_X509);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
+ "SSL get issuer: found %p in extra certs", issuer);
+
+ staple->cert = cert;
+ staple->issuer = issuer;
+
+ return NGX_OK;
+ }
+ }
+
+ store = SSL_CTX_get_cert_store(ssl->ctx);
+ if (store == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "SSL_CTX_get_cert_store() failed");
+ return NGX_ERROR;
+ }
+
+ store_ctx = X509_STORE_CTX_new();
+ if (store_ctx == NULL) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "X509_STORE_CTX_new() failed");
+ return NGX_ERROR;
+ }
+
+ if (X509_STORE_CTX_init(store_ctx, store, NULL, NULL) == 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "X509_STORE_CTX_init() failed");
+ return NGX_ERROR;
+ }
+
+ rc = X509_STORE_CTX_get1_issuer(&issuer, store_ctx, cert);
+
+ if (rc == -1) {
+ ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+ "X509_STORE_CTX_get1_issuer() failed");
+ X509_STORE_CTX_free(store_ctx);
+ return NGX_ERROR;
+ }
+
+ if (rc == 0) {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, issuer certificate not found");
+ X509_STORE_CTX_free(store_ctx);
+ return NGX_DECLINED;
+ }
+
+ X509_STORE_CTX_free(store_ctx);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
+ "SSL get issuer: found %p in cert store", issuer);
+
+ staple->cert = cert;
+ staple->issuer = issuer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder)
+{
+ ngx_url_t u;
+ char *s;
+ ngx_ssl_stapling_t *staple;
+ STACK_OF(OPENSSL_STRING) *aia;
+
+ staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
+
+ if (responder->len == 0) {
+
+ /* extract OCSP responder URL from certificate */
+
+ aia = X509_get1_ocsp(staple->cert);
+ if (aia == NULL) {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, "
+ "no OCSP responder URL in the certificate");
+ return NGX_DECLINED;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+ s = sk_OPENSSL_STRING_value(aia, 0);
+#else
+ s = sk_value(aia, 0);
+#endif
+ if (s == NULL) {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, "
+ "no OCSP responder URL in the certificate");
+ X509_email_free(aia);
+ return NGX_DECLINED;
+ }
+
+ responder->len = ngx_strlen(s);
+ responder->data = ngx_palloc(cf->pool, responder->len);
+ if (responder->data == NULL) {
+ X509_email_free(aia);
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(responder->data, s, responder->len);
+ X509_email_free(aia);
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = *responder;
+ u.default_port = 80;
+ u.uri_part = 1;
+
+ if (u.url.len > 7
+ && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0)
+ {
+ u.url.len -= 7;
+ u.url.data += 7;
+
+ } else {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, "
+ "invalid URL prefix in OCSP responder \"%V\"", &u.url);
+ return NGX_DECLINED;
+ }
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, "
+ "%s in OCSP responder \"%V\"", u.err, &u.url);
+ return NGX_DECLINED;
+ }
+
+ return NGX_ERROR;
+ }
+
+ staple->addrs = u.addrs;
+ staple->host = u.host;
+ staple->uri = u.uri;
+ staple->port = u.port;
+
+ if (staple->uri.len == 0) {
+ ngx_str_set(&staple->uri, "/");
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
+{
+ ngx_ssl_stapling_t *staple;
+
+ staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
+
+ staple->resolver = resolver;
+ staple->resolver_timeout = resolver_timeout;
+
+ return NGX_OK;
+}
+
+
+static int
+ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)
+{
+ int rc;
+ u_char *p;
+ ngx_connection_t *c;
+ ngx_ssl_stapling_t *staple;
+
+ c = ngx_ssl_get_connection(ssl_conn);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "SSL certificate status callback");
+
+ staple = data;
+ rc = SSL_TLSEXT_ERR_NOACK;
+
+ if (staple->staple.len) {
+ /* we have to copy ocsp response as OpenSSL will free it by itself */
+
+ p = OPENSSL_malloc(staple->staple.len);
+ if (p == NULL) {
+ ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "OPENSSL_malloc() failed");
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ ngx_memcpy(p, staple->staple.data, staple->staple.len);
+
+ SSL_set_tlsext_status_ocsp_resp(ssl_conn, p, staple->staple.len);
+
+ rc = SSL_TLSEXT_ERR_OK;
+ }
+
+ ngx_ssl_stapling_update(staple);
+
+ return rc;
+}
+
+
+static void
+ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple)
+{
+ ngx_ssl_ocsp_ctx_t *ctx;
+
+ if (staple->host.len == 0
+ || staple->loading || staple->valid >= ngx_time())
+ {
+ return;
+ }
+
+ staple->loading = 1;
+
+ ctx = ngx_ssl_ocsp_start();
+ if (ctx == NULL) {
+ return;
+ }
+
+ ctx->cert = staple->cert;
+ ctx->issuer = staple->issuer;
+
+ ctx->addrs = staple->addrs;
+ ctx->host = staple->host;
+ ctx->uri = staple->uri;
+ ctx->port = staple->port;
+ ctx->timeout = staple->timeout;
+
+ ctx->resolver = staple->resolver;
+ ctx->resolver_timeout = staple->resolver_timeout;
+
+ ctx->handler = ngx_ssl_stapling_ocsp_handler;
+ ctx->data = staple;
+
+ ngx_ssl_ocsp_request(ctx);
+
+ return;
+}
+
+
+static void
+ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x0090707fL
+ const
+#endif
+ u_char *p;
+ int n;
+ size_t len;
+ ngx_str_t response;
+ X509_STORE *store;
+ STACK_OF(X509) *chain;
+ OCSP_CERTID *id;
+ OCSP_RESPONSE *ocsp;
+ OCSP_BASICRESP *basic;
+ ngx_ssl_stapling_t *staple;
+ ASN1_GENERALIZEDTIME *thisupdate, *nextupdate;
+
+ staple = ctx->data;
+ ocsp = NULL;
+ basic = NULL;
+ id = NULL;
+
+ if (ctx->code != 200) {
+ goto error;
+ }
+
+ /* check the response */
+
+ len = ctx->response->last - ctx->response->pos;
+ p = ctx->response->pos;
+
+ ocsp = d2i_OCSP_RESPONSE(NULL, &p, len);
+ if (ocsp == NULL) {
+ ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+ "d2i_OCSP_RESPONSE() failed");
+ goto error;
+ }
+
+ n = OCSP_response_status(ocsp);
+
+ if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP response not successful (%d: %s)",
+ n, OCSP_response_status_str(n));
+ goto error;
+ }
+
+ basic = OCSP_response_get1_basic(ocsp);
+ if (basic == NULL) {
+ ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP_response_get1_basic() failed");
+ goto error;
+ }
+
+ store = SSL_CTX_get_cert_store(staple->ssl_ctx);
+ if (store == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "SSL_CTX_get_cert_store() failed");
+ goto error;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain);
+#else
+ chain = staple->ssl_ctx->extra_certs;
+#endif
+
+ if (OCSP_basic_verify(basic, chain, store,
+ staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY)
+ != 1)
+ {
+ ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP_basic_verify() failed");
+ goto error;
+ }
+
+ id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
+ if (id == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "OCSP_cert_to_id() failed");
+ goto error;
+ }
+
+ if (OCSP_resp_find_status(basic, id, &n, NULL, NULL,
+ &thisupdate, &nextupdate)
+ != 1)
+ {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "certificate status not found in the OCSP response");
+ goto error;
+ }
+
+ if (n != V_OCSP_CERTSTATUS_GOOD) {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "certificate status \"%s\" in the OCSP response",
+ OCSP_cert_status_str(n));
+ goto error;
+ }
+
+ if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {
+ ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP_check_validity() failed");
+ goto error;
+ }
+
+ OCSP_CERTID_free(id);
+ OCSP_BASICRESP_free(basic);
+ OCSP_RESPONSE_free(ocsp);
+
+ /* copy the response to memory not in ctx->pool */
+
+ response.len = len;
+ response.data = ngx_alloc(response.len, ctx->log);
+
+ if (response.data == NULL) {
+ goto done;
+ }
+
+ ngx_memcpy(response.data, ctx->response->pos, response.len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp response, %s, %uz",
+ OCSP_cert_status_str(n), response.len);
+
+ if (staple->staple.data) {
+ ngx_free(staple->staple.data);
+ }
+
+ staple->staple = response;
+
+done:
+
+ staple->loading = 0;
+ staple->valid = ngx_time() + 3600; /* ssl_stapling_valid */
+
+ ngx_ssl_ocsp_done(ctx);
+ return;
+
+error:
+
+ staple->loading = 0;
+ staple->valid = ngx_time() + 300; /* ssl_stapling_err_valid */
+
+ if (id) {
+ OCSP_CERTID_free(id);
+ }
+
+ if (basic) {
+ OCSP_BASICRESP_free(basic);
+ }
+
+ if (ocsp) {
+ OCSP_RESPONSE_free(ocsp);
+ }
+
+ ngx_ssl_ocsp_done(ctx);
+}
+
+
+static void
+ngx_ssl_stapling_cleanup(void *data)
+{
+ ngx_ssl_stapling_t *staple = data;
+
+ if (staple->issuer) {
+ X509_free(staple->issuer);
+ }
+
+ if (staple->staple.data) {
+ ngx_free(staple->staple.data);
+ }
+}
+
+
+static ngx_ssl_ocsp_ctx_t *
+ngx_ssl_ocsp_start(void)
+{
+ ngx_log_t *log;
+ ngx_pool_t *pool;
+ ngx_ssl_ocsp_ctx_t *ctx;
+
+ pool = ngx_create_pool(2048, ngx_cycle->log);
+ if (pool == NULL) {
+ return NULL;
+ }
+
+ ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t));
+ if (ctx == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ log = ngx_palloc(pool, sizeof(ngx_log_t));
+ if (log == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ ctx->pool = pool;
+
+ *log = *ctx->pool->log;
+
+ ctx->pool->log = log;
+ ctx->log = log;
+
+ log->handler = ngx_ssl_ocsp_log_error;
+ log->data = ctx;
+ log->action = "requesting certificate status";
+
+ return ctx;
+}
+
+
+static void
+ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp done");
+
+ if (ctx->peer.connection) {
+ ngx_close_connection(ctx->peer.connection);
+ }
+
+ ngx_destroy_pool(ctx->pool);
+}
+
+
+static void
+ngx_ssl_ocsp_error(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp error");
+
+ ctx->code = 0;
+ ctx->handler(ctx);
+}
+
+
+static void
+ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_resolver_ctx_t *resolve, temp;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp request");
+
+ if (ngx_ssl_ocsp_create_request(ctx) != NGX_OK) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ if (ctx->resolver) {
+ /* resolve OCSP responder hostname */
+
+ temp.name = ctx->host;
+
+ resolve = ngx_resolve_start(ctx->resolver, &temp);
+ if (resolve == NULL) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ if (resolve == NGX_NO_RESOLVER) {
+ ngx_log_error(NGX_LOG_WARN, ctx->log, 0,
+ "no resolver defined to resolve %V", &ctx->host);
+ goto connect;
+ }
+
+ resolve->name = ctx->host;
+ resolve->handler = ngx_ssl_ocsp_resolve_handler;
+ resolve->data = ctx;
+ resolve->timeout = ctx->resolver_timeout;
+
+ if (ngx_resolve_name(resolve) != NGX_OK) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ return;
+ }
+
+connect:
+
+ ngx_ssl_ocsp_connect(ctx);
+}
+
+
+static void
+ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve)
+{
+ ngx_ssl_ocsp_ctx_t *ctx = resolve->data;
+
+ u_char *p;
+ size_t len;
+ in_port_t port;
+ socklen_t socklen;
+ ngx_uint_t i;
+ struct sockaddr *sockaddr;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp resolve handler");
+
+ if (resolve->state) {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "%V could not be resolved (%i: %s)",
+ &resolve->name, resolve->state,
+ ngx_resolver_strerror(resolve->state));
+ goto failed;
+ }
+
+#if (NGX_DEBUG)
+ {
+ u_char text[NGX_SOCKADDR_STRLEN];
+ ngx_str_t addr;
+
+ addr.data = text;
+
+ for (i = 0; i < resolve->naddrs; i++) {
+ addr.len = ngx_sock_ntop(resolve->addrs[i].sockaddr,
+ resolve->addrs[i].socklen,
+ text, NGX_SOCKADDR_STRLEN, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "name was resolved to %V", &addr);
+
+ }
+ }
+#endif
+
+ ctx->naddrs = resolve->naddrs;
+ ctx->addrs = ngx_pcalloc(ctx->pool, ctx->naddrs * sizeof(ngx_addr_t));
+
+ if (ctx->addrs == NULL) {
+ goto failed;
+ }
+
+ port = htons(ctx->port);
+
+ for (i = 0; i < resolve->naddrs; i++) {
+
+ socklen = resolve->addrs[i].socklen;
+
+ sockaddr = ngx_palloc(ctx->pool, socklen);
+ if (sockaddr == NULL) {
+ goto failed;
+ }
+
+ ngx_memcpy(sockaddr, resolve->addrs[i].sockaddr, socklen);
+
+ switch (sockaddr->sa_family) {
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ ((struct sockaddr_in6 *) sockaddr)->sin6_port = port;
+ break;
+#endif
+ default: /* AF_INET */
+ ((struct sockaddr_in *) sockaddr)->sin_port = port;
+ }
+
+ ctx->addrs[i].sockaddr = sockaddr;
+ ctx->addrs[i].socklen = socklen;
+
+ p = ngx_pnalloc(ctx->pool, NGX_SOCKADDR_STRLEN);
+ if (p == NULL) {
+ goto failed;
+ }
+
+ len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);
+
+ ctx->addrs[i].name.len = len;
+ ctx->addrs[i].name.data = p;
+ }
+
+ ngx_resolve_name_done(resolve);
+
+ ngx_ssl_ocsp_connect(ctx);
+ return;
+
+failed:
+
+ ngx_resolve_name_done(resolve);
+ ngx_ssl_ocsp_error(ctx);
+}
+
+
+static void
+ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_int_t rc;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp connect");
+
+ /* TODO: use all ip addresses */
+
+ ctx->peer.sockaddr = ctx->addrs[0].sockaddr;
+ ctx->peer.socklen = ctx->addrs[0].socklen;
+ ctx->peer.name = &ctx->addrs[0].name;
+ ctx->peer.get = ngx_event_get_peer;
+ ctx->peer.log = ctx->log;
+ ctx->peer.log_error = NGX_ERROR_ERR;
+
+ rc = ngx_event_connect_peer(&ctx->peer);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp connect peer done");
+
+ if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ ctx->peer.connection->data = ctx;
+ ctx->peer.connection->pool = ctx->pool;
+
+ ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler;
+ ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler;
+
+ ctx->process = ngx_ssl_ocsp_process_status_line;
+
+ ngx_add_timer(ctx->peer.connection->read, ctx->timeout);
+ ngx_add_timer(ctx->peer.connection->write, ctx->timeout);
+
+ if (rc == NGX_OK) {
+ ngx_ssl_ocsp_write_handler(ctx->peer.connection->write);
+ return;
+ }
+}
+
+
+static void
+ngx_ssl_ocsp_write_handler(ngx_event_t *wev)
+{
+ ssize_t n, size;
+ ngx_connection_t *c;
+ ngx_ssl_ocsp_ctx_t *ctx;
+
+ c = wev->data;
+ ctx = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0,
+ "ssl ocsp write handler");
+
+ if (wev->timedout) {
+ ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
+ "OCSP responder timed out");
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ size = ctx->request->last - ctx->request->pos;
+
+ n = ngx_send(c, ctx->request->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ if (n > 0) {
+ ctx->request->pos += n;
+
+ if (n == size) {
+ wev->handler = ngx_ssl_ocsp_dummy_handler;
+
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+
+ if (ngx_handle_write_event(wev, 0) != NGX_OK) {
+ ngx_ssl_ocsp_error(ctx);
+ }
+
+ return;
+ }
+ }
+
+ if (!wev->timer_set) {
+ ngx_add_timer(wev, ctx->timeout);
+ }
+}
+
+
+static void
+ngx_ssl_ocsp_read_handler(ngx_event_t *rev)
+{
+ ssize_t n, size;
+ ngx_int_t rc;
+ ngx_ssl_ocsp_ctx_t *ctx;
+ ngx_connection_t *c;
+
+ c = rev->data;
+ ctx = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0,
+ "ssl ocsp read handler");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
+ "OCSP responder timed out");
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ if (ctx->response == NULL) {
+ ctx->response = ngx_create_temp_buf(ctx->pool, 16384);
+ if (ctx->response == NULL) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+ }
+
+ for ( ;; ) {
+
+ size = ctx->response->end - ctx->response->last;
+
+ n = ngx_recv(c, ctx->response->last, size);
+
+ if (n > 0) {
+ ctx->response->last += n;
+
+ rc = ctx->process(ctx);
+
+ if (rc == NGX_ERROR) {
+ ngx_ssl_ocsp_error(ctx);
+ return;
+ }
+
+ continue;
+ }
+
+ if (n == NGX_AGAIN) {
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_ssl_ocsp_error(ctx);
+ }
+
+ return;
+ }
+
+ break;
+ }
+
+ ctx->done = 1;
+
+ rc = ctx->process(ctx);
+
+ if (rc == NGX_DONE) {
+ /* ctx->handler() was called */
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP responder prematurely closed connection");
+
+ ngx_ssl_ocsp_error(ctx);
+}
+
+
+static void
+ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "ssl ocsp dummy handler");
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ int len;
+ u_char *p;
+ uintptr_t escape;
+ ngx_str_t binary, base64;
+ ngx_buf_t *b;
+ OCSP_CERTID *id;
+ OCSP_REQUEST *ocsp;
+
+ ocsp = OCSP_REQUEST_new();
+ if (ocsp == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "OCSP_REQUEST_new() failed");
+ return NGX_ERROR;
+ }
+
+ id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
+ if (id == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "OCSP_cert_to_id() failed");
+ goto failed;
+ }
+
+ if (OCSP_request_add0_id(ocsp, id) == NULL) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "OCSP_request_add0_id() failed");
+ goto failed;
+ }
+
+ len = i2d_OCSP_REQUEST(ocsp, NULL);
+ if (len <= 0) {
+ ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
+ "i2d_OCSP_REQUEST() failed");
+ goto failed;
+ }
+
+ binary.len = len;
+ binary.data = ngx_palloc(ctx->pool, len);
+ if (binary.data == NULL) {
+ goto failed;
+ }
+
+ p = binary.data;
+ len = i2d_OCSP_REQUEST(ocsp, &p);
+ if (len <= 0) {
+ ngx_ssl_error(NGX_LOG_EMERG, ctx->log, 0,
+ "i2d_OCSP_REQUEST() failed");
+ goto failed;
+ }
+
+ base64.len = ngx_base64_encoded_length(binary.len);
+ base64.data = ngx_palloc(ctx->pool, base64.len);
+ if (base64.data == NULL) {
+ goto failed;
+ }
+
+ ngx_encode_base64(&base64, &binary);
+
+ escape = ngx_escape_uri(NULL, base64.data, base64.len,
+ NGX_ESCAPE_URI_COMPONENT);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp request length %z, escape %d",
+ base64.len, escape);
+
+ len = sizeof("GET ") - 1 + ctx->uri.len + sizeof("/") - 1
+ + base64.len + 2 * escape + sizeof(" HTTP/1.0" CRLF) - 1
+ + sizeof("Host: ") - 1 + ctx->host.len + sizeof(CRLF) - 1
+ + sizeof(CRLF) - 1;
+
+ b = ngx_create_temp_buf(ctx->pool, len);
+ if (b == NULL) {
+ goto failed;
+ }
+
+ p = b->last;
+
+ p = ngx_cpymem(p, "GET ", sizeof("GET ") - 1);
+ p = ngx_cpymem(p, ctx->uri.data, ctx->uri.len);
+
+ if (ctx->uri.data[ctx->uri.len - 1] != '/') {
+ *p++ = '/';
+ }
+
+ if (escape == 0) {
+ p = ngx_cpymem(p, base64.data, base64.len);
+
+ } else {
+ p = (u_char *) ngx_escape_uri(p, base64.data, base64.len,
+ NGX_ESCAPE_URI_COMPONENT);
+ }
+
+ p = ngx_cpymem(p, " HTTP/1.0" CRLF, sizeof(" HTTP/1.0" CRLF) - 1);
+ p = ngx_cpymem(p, "Host: ", sizeof("Host: ") - 1);
+ p = ngx_cpymem(p, ctx->host.data, ctx->host.len);
+ *p++ = CR; *p++ = LF;
+
+ /* add "\r\n" at the header end */
+ *p++ = CR; *p++ = LF;
+
+ b->last = p;
+ ctx->request = b;
+
+ OCSP_REQUEST_free(ocsp);
+
+ return NGX_OK;
+
+failed:
+
+ OCSP_REQUEST_free(ocsp);
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_int_t rc;
+
+ rc = ngx_ssl_ocsp_parse_status_line(ctx);
+
+ if (rc == NGX_OK) {
+#if 0
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp status line \"%*s\"",
+ ctx->response->pos - ctx->response->start,
+ ctx->response->start);
+#endif
+
+ ctx->process = ngx_ssl_ocsp_process_headers;
+ return ctx->process(ctx);
+ }
+
+ if (rc == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ /* rc == NGX_ERROR */
+
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP responder sent invalid response");
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ u_char ch;
+ u_char *p;
+ ngx_buf_t *b;
+ enum {
+ sw_start = 0,
+ sw_H,
+ sw_HT,
+ sw_HTT,
+ sw_HTTP,
+ sw_first_major_digit,
+ sw_major_digit,
+ sw_first_minor_digit,
+ sw_minor_digit,
+ sw_status,
+ sw_space_after_status,
+ sw_status_text,
+ sw_almost_done
+ } state;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp process status line");
+
+ state = ctx->state;
+ b = ctx->response;
+
+ for (p = b->pos; p < b->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* "HTTP/" */
+ case sw_start:
+ switch (ch) {
+ case 'H':
+ state = sw_H;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_H:
+ switch (ch) {
+ case 'T':
+ state = sw_HT;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HT:
+ switch (ch) {
+ case 'T':
+ state = sw_HTT;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HTT:
+ switch (ch) {
+ case 'P':
+ state = sw_HTTP;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HTTP:
+ switch (ch) {
+ case '/':
+ state = sw_first_major_digit;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ /* the first digit of major HTTP version */
+ case sw_first_major_digit:
+ if (ch < '1' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ state = sw_major_digit;
+ break;
+
+ /* the major HTTP version or dot */
+ case sw_major_digit:
+ if (ch == '.') {
+ state = sw_first_minor_digit;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ break;
+
+ /* the first digit of minor HTTP version */
+ case sw_first_minor_digit:
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ state = sw_minor_digit;
+ break;
+
+ /* the minor HTTP version or the end of the request line */
+ case sw_minor_digit:
+ if (ch == ' ') {
+ state = sw_status;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ break;
+
+ /* HTTP status code */
+ case sw_status:
+ if (ch == ' ') {
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ ctx->code = ctx->code * 10 + ch - '0';
+
+ if (++ctx->count == 3) {
+ state = sw_space_after_status;
+ }
+
+ break;
+
+ /* space or end of line */
+ case sw_space_after_status:
+ switch (ch) {
+ case ' ':
+ state = sw_status_text;
+ break;
+ case '.': /* IIS may send 403.1, 403.2, etc */
+ state = sw_status_text;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ /* any text until end of line */
+ case sw_status_text:
+ switch (ch) {
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+
+ /* end of status line */
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ b->pos = p;
+ ctx->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ b->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ size_t len;
+ ngx_int_t rc;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp process headers");
+
+ for ( ;; ) {
+ rc = ngx_ssl_ocsp_parse_header_line(ctx);
+
+ if (rc == NGX_OK) {
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp header \"%*s: %*s\"",
+ ctx->header_name_end - ctx->header_name_start,
+ ctx->header_name_start,
+ ctx->header_end - ctx->header_start,
+ ctx->header_start);
+
+ len = ctx->header_name_end - ctx->header_name_start;
+
+ if (len == sizeof("Content-Type") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Content-Type",
+ sizeof("Content-Type") - 1)
+ == 0)
+ {
+ len = ctx->header_end - ctx->header_start;
+
+ if (len != sizeof("application/ocsp-response") - 1
+ || ngx_strncasecmp(ctx->header_start,
+ (u_char *) "application/ocsp-response",
+ sizeof("application/ocsp-response") - 1)
+ != 0)
+ {
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP responder sent invalid "
+ "\"Content-Type\" header: \"%*s\"",
+ ctx->header_end - ctx->header_start,
+ ctx->header_start);
+ return NGX_ERROR;
+ }
+
+ continue;
+ }
+
+ /* TODO: honor Content-Length */
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ /* rc == NGX_ERROR */
+
+ ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
+ "OCSP responder sent invalid response");
+
+ return NGX_ERROR;
+ }
+
+ ctx->process = ngx_ssl_ocsp_process_body;
+ return ctx->process(ctx);
+}
+
+static ngx_int_t
+ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ u_char c, ch, *p;
+ enum {
+ sw_start = 0,
+ sw_name,
+ sw_space_before_value,
+ sw_value,
+ sw_space_after_value,
+ sw_almost_done,
+ sw_header_almost_done
+ } state;
+
+ state = ctx->state;
+
+ for (p = ctx->response->pos; p < ctx->response->last; p++) {
+ ch = *p;
+
+#if 0
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "s:%d in:'%02Xd:%c'", state, ch, ch);
+#endif
+
+ switch (state) {
+
+ /* first char */
+ case sw_start:
+
+ switch (ch) {
+ case CR:
+ ctx->header_end = p;
+ state = sw_header_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto header_done;
+ default:
+ state = sw_name;
+ ctx->header_name_start = p;
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ return NGX_ERROR;
+ }
+ break;
+
+ /* header name */
+ case sw_name:
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if (ch == ':') {
+ ctx->header_name_end = p;
+ state = sw_space_before_value;
+ break;
+ }
+
+ if (ch == '-') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ if (ch == CR) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ }
+
+ return NGX_ERROR;
+
+ /* space* before header value */
+ case sw_space_before_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ default:
+ ctx->header_start = p;
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* header value */
+ case sw_value:
+ switch (ch) {
+ case ' ':
+ ctx->header_end = p;
+ state = sw_space_after_value;
+ break;
+ case CR:
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto done;
+ }
+ break;
+
+ /* space* before end of header line */
+ case sw_space_after_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* end of header line */
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+
+ /* end of header */
+ case sw_header_almost_done:
+ switch (ch) {
+ case LF:
+ goto header_done;
+ default:
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ ctx->response->pos = p;
+ ctx->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_OK;
+
+header_done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
+ "ssl ocsp process body");
+
+ if (ctx->done) {
+ ctx->handler(ctx);
+ return NGX_DONE;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+static u_char *
+ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ u_char *p;
+ ngx_ssl_ocsp_ctx_t *ctx;
+
+ p = buf;
+
+ if (log->action) {
+ p = ngx_snprintf(buf, len, " while %s", log->action);
+ len -= p - buf;
+ }
+
+ ctx = log->data;
+
+ if (ctx) {
+ p = ngx_snprintf(p, len, ", responder: %V", &ctx->host);
+ }
+
+ return p;
+}
+
+
+#else
+
+
+ngx_int_t
+ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
+ ngx_str_t *responder, ngx_uint_t verify)
+{
+ ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
+ "\"ssl_stapling\" ignored, not supported");
+
+ return NGX_OK;
+}
+
+ngx_int_t
+ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
+ ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
+{
+ return NGX_OK;
+}
+
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_pipe.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_pipe.c
new file mode 100644
index 00000000000..64fb07bde42
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_pipe.c
@@ -0,0 +1,1006 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_pipe.h>
+
+
+static ngx_int_t ngx_event_pipe_read_upstream(ngx_event_pipe_t *p);
+static ngx_int_t ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p);
+
+static ngx_int_t ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p);
+static ngx_inline void ngx_event_pipe_remove_shadow_links(ngx_buf_t *buf);
+static ngx_int_t ngx_event_pipe_drain_chains(ngx_event_pipe_t *p);
+
+
+ngx_int_t
+ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write)
+{
+ u_int flags;
+ ngx_int_t rc;
+ ngx_event_t *rev, *wev;
+
+ for ( ;; ) {
+ if (do_write) {
+ p->log->action = "sending to client";
+
+ rc = ngx_event_pipe_write_to_downstream(p);
+
+ if (rc == NGX_ABORT) {
+ return NGX_ABORT;
+ }
+
+ if (rc == NGX_BUSY) {
+ return NGX_OK;
+ }
+ }
+
+ p->read = 0;
+ p->upstream_blocked = 0;
+
+ p->log->action = "reading upstream";
+
+ if (ngx_event_pipe_read_upstream(p) == NGX_ABORT) {
+ return NGX_ABORT;
+ }
+
+ if (!p->read && !p->upstream_blocked) {
+ break;
+ }
+
+ do_write = 1;
+ }
+
+ if (p->upstream->fd != (ngx_socket_t) -1) {
+ rev = p->upstream->read;
+
+ flags = (rev->eof || rev->error) ? NGX_CLOSE_EVENT : 0;
+
+ if (ngx_handle_read_event(rev, flags) != NGX_OK) {
+ return NGX_ABORT;
+ }
+
+ if (rev->active && !rev->ready) {
+ ngx_add_timer(rev, p->read_timeout);
+
+ } else if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+ }
+
+ if (p->downstream->fd != (ngx_socket_t) -1
+ && p->downstream->data == p->output_ctx)
+ {
+ wev = p->downstream->write;
+ if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {
+ return NGX_ABORT;
+ }
+
+ if (!wev->delayed) {
+ if (wev->active && !wev->ready) {
+ ngx_add_timer(wev, p->send_timeout);
+
+ } else if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
+{
+ ssize_t n, size;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t *chain, *cl, *ln;
+
+ if (p->upstream_eof || p->upstream_error || p->upstream_done) {
+ return NGX_OK;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe read upstream: %d", p->upstream->read->ready);
+
+ for ( ;; ) {
+
+ if (p->upstream_eof || p->upstream_error || p->upstream_done) {
+ break;
+ }
+
+ if (p->preread_bufs == NULL && !p->upstream->read->ready) {
+ break;
+ }
+
+ if (p->preread_bufs) {
+
+ /* use the pre-read bufs if they exist */
+
+ chain = p->preread_bufs;
+ p->preread_bufs = NULL;
+ n = p->preread_size;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe preread: %z", n);
+
+ if (n) {
+ p->read = 1;
+ }
+
+ } else {
+
+#if (NGX_HAVE_KQUEUE)
+
+ /*
+ * kqueue notifies about the end of file or a pending error.
+ * This test allows not to allocate a buf on these conditions
+ * and not to call c->recv_chain().
+ */
+
+ if (p->upstream->read->available == 0
+ && p->upstream->read->pending_eof)
+ {
+ p->upstream->read->ready = 0;
+ p->upstream->read->eof = 1;
+ p->upstream_eof = 1;
+ p->read = 1;
+
+ if (p->upstream->read->kq_errno) {
+ p->upstream->read->error = 1;
+ p->upstream_error = 1;
+ p->upstream_eof = 0;
+
+ ngx_log_error(NGX_LOG_ERR, p->log,
+ p->upstream->read->kq_errno,
+ "kevent() reported that upstream "
+ "closed connection");
+ }
+
+ break;
+ }
+#endif
+
+ if (p->free_raw_bufs) {
+
+ /* use the free bufs if they exist */
+
+ chain = p->free_raw_bufs;
+ if (p->single_buf) {
+ p->free_raw_bufs = p->free_raw_bufs->next;
+ chain->next = NULL;
+ } else {
+ p->free_raw_bufs = NULL;
+ }
+
+ } else if (p->allocated < p->bufs.num) {
+
+ /* allocate a new buf if it's still allowed */
+
+ b = ngx_create_temp_buf(p->pool, p->bufs.size);
+ if (b == NULL) {
+ return NGX_ABORT;
+ }
+
+ p->allocated++;
+
+ chain = ngx_alloc_chain_link(p->pool);
+ if (chain == NULL) {
+ return NGX_ABORT;
+ }
+
+ chain->buf = b;
+ chain->next = NULL;
+
+ } else if (!p->cacheable
+ && p->downstream->data == p->output_ctx
+ && p->downstream->write->ready
+ && !p->downstream->write->delayed)
+ {
+ /*
+ * if the bufs are not needed to be saved in a cache and
+ * a downstream is ready then write the bufs to a downstream
+ */
+
+ p->upstream_blocked = 1;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe downstream ready");
+
+ break;
+
+ } else if (p->cacheable
+ || p->temp_file->offset < p->max_temp_file_size)
+ {
+
+ /*
+ * if it is allowed, then save some bufs from p->in
+ * to a temporary file, and add them to a p->out chain
+ */
+
+ rc = ngx_event_pipe_write_chain_to_temp_file(p);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe temp offset: %O", p->temp_file->offset);
+
+ if (rc == NGX_BUSY) {
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+ if (ngx_event_flags & NGX_USE_LEVEL_EVENT
+ && p->upstream->read->active
+ && p->upstream->read->ready)
+ {
+ if (ngx_del_event(p->upstream->read, NGX_READ_EVENT, 0)
+ == NGX_ERROR)
+ {
+ return NGX_ABORT;
+ }
+ }
+ }
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ chain = p->free_raw_bufs;
+ if (p->single_buf) {
+ p->free_raw_bufs = p->free_raw_bufs->next;
+ chain->next = NULL;
+ } else {
+ p->free_raw_bufs = NULL;
+ }
+
+ } else {
+
+ /* there are no bufs to read in */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "no pipe bufs to read in");
+
+ break;
+ }
+
+ n = p->upstream->recv_chain(p->upstream, chain);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe recv chain: %z", n);
+
+ if (p->free_raw_bufs) {
+ chain->next = p->free_raw_bufs;
+ }
+ p->free_raw_bufs = chain;
+
+ if (n == NGX_ERROR) {
+ p->upstream_error = 1;
+ return NGX_ERROR;
+ }
+
+ if (n == NGX_AGAIN) {
+ if (p->single_buf) {
+ ngx_event_pipe_remove_shadow_links(chain->buf);
+ }
+
+ break;
+ }
+
+ p->read = 1;
+
+ if (n == 0) {
+ p->upstream_eof = 1;
+ break;
+ }
+ }
+
+ p->read_length += n;
+ cl = chain;
+ p->free_raw_bufs = NULL;
+
+ while (cl && n > 0) {
+
+ ngx_event_pipe_remove_shadow_links(cl->buf);
+
+ size = cl->buf->end - cl->buf->last;
+
+ if (n >= size) {
+ cl->buf->last = cl->buf->end;
+
+ /* STUB */ cl->buf->num = p->num++;
+
+ if (p->input_filter(p, cl->buf) == NGX_ERROR) {
+ return NGX_ABORT;
+ }
+
+ n -= size;
+ ln = cl;
+ cl = cl->next;
+ ngx_free_chain(p->pool, ln);
+
+ } else {
+ cl->buf->last += n;
+ n = 0;
+ }
+ }
+
+ if (cl) {
+ for (ln = cl; ln->next; ln = ln->next) { /* void */ }
+
+ ln->next = p->free_raw_bufs;
+ p->free_raw_bufs = cl;
+ }
+ }
+
+#if (NGX_DEBUG)
+
+ for (cl = p->busy; cl; cl = cl->next) {
+ ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe buf busy s:%d t:%d f:%d "
+ "%p, pos %p, size: %z "
+ "file: %O, size: %z",
+ (cl->buf->shadow ? 1 : 0),
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+ }
+
+ for (cl = p->out; cl; cl = cl->next) {
+ ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe buf out s:%d t:%d f:%d "
+ "%p, pos %p, size: %z "
+ "file: %O, size: %z",
+ (cl->buf->shadow ? 1 : 0),
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+ }
+
+ for (cl = p->in; cl; cl = cl->next) {
+ ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe buf in s:%d t:%d f:%d "
+ "%p, pos %p, size: %z "
+ "file: %O, size: %z",
+ (cl->buf->shadow ? 1 : 0),
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+ }
+
+ for (cl = p->free_raw_bufs; cl; cl = cl->next) {
+ ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe buf free s:%d t:%d f:%d "
+ "%p, pos %p, size: %z "
+ "file: %O, size: %z",
+ (cl->buf->shadow ? 1 : 0),
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe length: %O", p->length);
+
+#endif
+
+ if (p->free_raw_bufs && p->length != -1) {
+ cl = p->free_raw_bufs;
+
+ if (cl->buf->last - cl->buf->pos >= p->length) {
+
+ p->free_raw_bufs = cl->next;
+
+ /* STUB */ cl->buf->num = p->num++;
+
+ if (p->input_filter(p, cl->buf) == NGX_ERROR) {
+ return NGX_ABORT;
+ }
+
+ ngx_free_chain(p->pool, cl);
+ }
+ }
+
+ if (p->length == 0) {
+ p->upstream_done = 1;
+ p->read = 1;
+ }
+
+ if ((p->upstream_eof || p->upstream_error) && p->free_raw_bufs) {
+
+ /* STUB */ p->free_raw_bufs->buf->num = p->num++;
+
+ if (p->input_filter(p, p->free_raw_bufs->buf) == NGX_ERROR) {
+ return NGX_ABORT;
+ }
+
+ p->free_raw_bufs = p->free_raw_bufs->next;
+
+ if (p->free_bufs && p->buf_to_file == NULL) {
+ for (cl = p->free_raw_bufs; cl; cl = cl->next) {
+ if (cl->buf->shadow == NULL) {
+ ngx_pfree(p->pool, cl->buf->start);
+ }
+ }
+ }
+ }
+
+ if (p->cacheable && (p->in || p->buf_to_file)) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe write chain");
+
+ if (ngx_event_pipe_write_chain_to_temp_file(p) == NGX_ABORT) {
+ return NGX_ABORT;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)
+{
+ u_char *prev;
+ size_t bsize;
+ ngx_int_t rc;
+ ngx_uint_t flush, flushed, prev_last_shadow;
+ ngx_chain_t *out, **ll, *cl;
+ ngx_connection_t *downstream;
+
+ downstream = p->downstream;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe write downstream: %d", downstream->write->ready);
+
+ flushed = 0;
+
+ for ( ;; ) {
+ if (p->downstream_error) {
+ return ngx_event_pipe_drain_chains(p);
+ }
+
+ if (p->upstream_eof || p->upstream_error || p->upstream_done) {
+
+ /* pass the p->out and p->in chains to the output filter */
+
+ for (cl = p->busy; cl; cl = cl->next) {
+ cl->buf->recycled = 0;
+ }
+
+ if (p->out) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe write downstream flush out");
+
+ for (cl = p->out; cl; cl = cl->next) {
+ cl->buf->recycled = 0;
+ }
+
+ rc = p->output_filter(p->output_ctx, p->out);
+
+ if (rc == NGX_ERROR) {
+ p->downstream_error = 1;
+ return ngx_event_pipe_drain_chains(p);
+ }
+
+ p->out = NULL;
+ }
+
+ if (p->in) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe write downstream flush in");
+
+ for (cl = p->in; cl; cl = cl->next) {
+ cl->buf->recycled = 0;
+ }
+
+ rc = p->output_filter(p->output_ctx, p->in);
+
+ if (rc == NGX_ERROR) {
+ p->downstream_error = 1;
+ return ngx_event_pipe_drain_chains(p);
+ }
+
+ p->in = NULL;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe write downstream done");
+
+ /* TODO: free unused bufs */
+
+ p->downstream_done = 1;
+ break;
+ }
+
+ if (downstream->data != p->output_ctx
+ || !downstream->write->ready
+ || downstream->write->delayed)
+ {
+ break;
+ }
+
+ /* bsize is the size of the busy recycled bufs */
+
+ prev = NULL;
+ bsize = 0;
+
+ for (cl = p->busy; cl; cl = cl->next) {
+
+ if (cl->buf->recycled) {
+ if (prev == cl->buf->start) {
+ continue;
+ }
+
+ bsize += cl->buf->end - cl->buf->start;
+ prev = cl->buf->start;
+ }
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe write busy: %uz", bsize);
+
+ out = NULL;
+
+ if (bsize >= (size_t) p->busy_size) {
+ flush = 1;
+ goto flush;
+ }
+
+ flush = 0;
+ ll = NULL;
+ prev_last_shadow = 1;
+
+ for ( ;; ) {
+ if (p->out) {
+ cl = p->out;
+
+ if (cl->buf->recycled) {
+ ngx_log_error(NGX_LOG_ALERT, p->log, 0,
+ "recycled buffer in pipe out chain");
+ }
+
+ p->out = p->out->next;
+
+ } else if (!p->cacheable && p->in) {
+ cl = p->in;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe write buf ls:%d %p %z",
+ cl->buf->last_shadow,
+ cl->buf->pos,
+ cl->buf->last - cl->buf->pos);
+
+ if (cl->buf->recycled && prev_last_shadow) {
+ if (bsize + cl->buf->end - cl->buf->start > p->busy_size) {
+ flush = 1;
+ break;
+ }
+
+ bsize += cl->buf->end - cl->buf->start;
+ }
+
+ prev_last_shadow = cl->buf->last_shadow;
+
+ p->in = p->in->next;
+
+ } else {
+ break;
+ }
+
+ cl->next = NULL;
+
+ if (out) {
+ *ll = cl;
+ } else {
+ out = cl;
+ }
+ ll = &cl->next;
+ }
+
+ flush:
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe write: out:%p, f:%d", out, flush);
+
+ if (out == NULL) {
+
+ if (!flush) {
+ break;
+ }
+
+ /* a workaround for AIO */
+ if (flushed++ > 10) {
+ return NGX_BUSY;
+ }
+ }
+
+ rc = p->output_filter(p->output_ctx, out);
+
+ ngx_chain_update_chains(p->pool, &p->free, &p->busy, &out, p->tag);
+
+ if (rc == NGX_ERROR) {
+ p->downstream_error = 1;
+ return ngx_event_pipe_drain_chains(p);
+ }
+
+ for (cl = p->free; cl; cl = cl->next) {
+
+ if (cl->buf->temp_file) {
+ if (p->cacheable || !p->cyclic_temp_file) {
+ continue;
+ }
+
+ /* reset p->temp_offset if all bufs had been sent */
+
+ if (cl->buf->file_last == p->temp_file->offset) {
+ p->temp_file->offset = 0;
+ }
+ }
+
+ /* TODO: free buf if p->free_bufs && upstream done */
+
+ /* add the free shadow raw buf to p->free_raw_bufs */
+
+ if (cl->buf->last_shadow) {
+ if (ngx_event_pipe_add_free_buf(p, cl->buf->shadow) != NGX_OK) {
+ return NGX_ABORT;
+ }
+
+ cl->buf->last_shadow = 0;
+ }
+
+ cl->buf->shadow = NULL;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p)
+{
+ ssize_t size, bsize, n;
+ ngx_buf_t *b;
+ ngx_uint_t prev_last_shadow;
+ ngx_chain_t *cl, *tl, *next, *out, **ll, **last_out, **last_free, fl;
+
+ if (p->buf_to_file) {
+ fl.buf = p->buf_to_file;
+ fl.next = p->in;
+ out = &fl;
+
+ } else {
+ out = p->in;
+ }
+
+ if (!p->cacheable) {
+
+ size = 0;
+ cl = out;
+ ll = NULL;
+ prev_last_shadow = 1;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe offset: %O", p->temp_file->offset);
+
+ do {
+ bsize = cl->buf->last - cl->buf->pos;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "pipe buf ls:%d %p, pos %p, size: %z",
+ cl->buf->last_shadow, cl->buf->start,
+ cl->buf->pos, bsize);
+
+ if (prev_last_shadow
+ && ((size + bsize > p->temp_file_write_size)
+ || (p->temp_file->offset + size + bsize
+ > p->max_temp_file_size)))
+ {
+ break;
+ }
+
+ prev_last_shadow = cl->buf->last_shadow;
+
+ size += bsize;
+ ll = &cl->next;
+ cl = cl->next;
+
+ } while (cl);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "size: %z", size);
+
+ if (ll == NULL) {
+ return NGX_BUSY;
+ }
+
+ if (cl) {
+ p->in = cl;
+ *ll = NULL;
+
+ } else {
+ p->in = NULL;
+ p->last_in = &p->in;
+ }
+
+ } else {
+ p->in = NULL;
+ p->last_in = &p->in;
+ }
+
+ n = ngx_write_chain_to_temp_file(p->temp_file, out);
+
+ if (n == NGX_ERROR) {
+ return NGX_ABORT;
+ }
+
+ if (p->buf_to_file) {
+ p->temp_file->offset = p->buf_to_file->last - p->buf_to_file->pos;
+ n -= p->buf_to_file->last - p->buf_to_file->pos;
+ p->buf_to_file = NULL;
+ out = out->next;
+ }
+
+ if (n > 0) {
+ /* update previous buffer or add new buffer */
+
+ if (p->out) {
+ for (cl = p->out; cl->next; cl = cl->next) { /* void */ }
+
+ b = cl->buf;
+
+ if (b->file_last == p->temp_file->offset) {
+ p->temp_file->offset += n;
+ b->file_last = p->temp_file->offset;
+ goto free;
+ }
+
+ last_out = &cl->next;
+
+ } else {
+ last_out = &p->out;
+ }
+
+ cl = ngx_chain_get_free_buf(p->pool, &p->free);
+ if (cl == NULL) {
+ return NGX_ABORT;
+ }
+
+ b = cl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->tag = p->tag;
+
+ b->file = &p->temp_file->file;
+ b->file_pos = p->temp_file->offset;
+ p->temp_file->offset += n;
+ b->file_last = p->temp_file->offset;
+
+ b->in_file = 1;
+ b->temp_file = 1;
+
+ *last_out = cl;
+ }
+
+free:
+
+ for (last_free = &p->free_raw_bufs;
+ *last_free != NULL;
+ last_free = &(*last_free)->next)
+ {
+ /* void */
+ }
+
+ for (cl = out; cl; cl = next) {
+ next = cl->next;
+
+ cl->next = p->free;
+ p->free = cl;
+
+ b = cl->buf;
+
+ if (b->last_shadow) {
+
+ tl = ngx_alloc_chain_link(p->pool);
+ if (tl == NULL) {
+ return NGX_ABORT;
+ }
+
+ tl->buf = b->shadow;
+ tl->next = NULL;
+
+ *last_free = tl;
+ last_free = &tl->next;
+
+ b->shadow->pos = b->shadow->start;
+ b->shadow->last = b->shadow->start;
+
+ ngx_event_pipe_remove_shadow_links(b->shadow);
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+/* the copy input filter */
+
+ngx_int_t
+ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
+{
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+
+ if (buf->pos == buf->last) {
+ return NGX_OK;
+ }
+
+ cl = ngx_chain_get_free_buf(p->pool, &p->free);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = cl->buf;
+
+ ngx_memcpy(b, buf, sizeof(ngx_buf_t));
+ b->shadow = buf;
+ b->tag = p->tag;
+ b->last_shadow = 1;
+ b->recycled = 1;
+ buf->shadow = b;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "input buf #%d", b->num);
+
+ if (p->in) {
+ *p->last_in = cl;
+ } else {
+ p->in = cl;
+ }
+ p->last_in = &cl->next;
+
+ if (p->length == -1) {
+ return NGX_OK;
+ }
+
+ p->length -= b->last - b->pos;
+
+ return NGX_OK;
+}
+
+
+static ngx_inline void
+ngx_event_pipe_remove_shadow_links(ngx_buf_t *buf)
+{
+ ngx_buf_t *b, *next;
+
+ b = buf->shadow;
+
+ if (b == NULL) {
+ return;
+ }
+
+ while (!b->last_shadow) {
+ next = b->shadow;
+
+ b->temporary = 0;
+ b->recycled = 0;
+
+ b->shadow = NULL;
+ b = next;
+ }
+
+ b->temporary = 0;
+ b->recycled = 0;
+ b->last_shadow = 0;
+
+ b->shadow = NULL;
+
+ buf->shadow = NULL;
+}
+
+
+ngx_int_t
+ngx_event_pipe_add_free_buf(ngx_event_pipe_t *p, ngx_buf_t *b)
+{
+ ngx_chain_t *cl;
+
+ cl = ngx_alloc_chain_link(p->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (p->buf_to_file && b->start == p->buf_to_file->start) {
+ b->pos = p->buf_to_file->last;
+ b->last = p->buf_to_file->last;
+
+ } else {
+ b->pos = b->start;
+ b->last = b->start;
+ }
+
+ b->shadow = NULL;
+
+ cl->buf = b;
+
+ if (p->free_raw_bufs == NULL) {
+ p->free_raw_bufs = cl;
+ cl->next = NULL;
+
+ return NGX_OK;
+ }
+
+ if (p->free_raw_bufs->buf->pos == p->free_raw_bufs->buf->last) {
+
+ /* add the free buf to the list start */
+
+ cl->next = p->free_raw_bufs;
+ p->free_raw_bufs = cl;
+
+ return NGX_OK;
+ }
+
+ /* the first free buf is partially filled, thus add the free buf after it */
+
+ cl->next = p->free_raw_bufs->next;
+ p->free_raw_bufs->next = cl;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_event_pipe_drain_chains(ngx_event_pipe_t *p)
+{
+ ngx_chain_t *cl, *tl;
+
+ for ( ;; ) {
+ if (p->busy) {
+ cl = p->busy;
+ p->busy = NULL;
+
+ } else if (p->out) {
+ cl = p->out;
+ p->out = NULL;
+
+ } else if (p->in) {
+ cl = p->in;
+ p->in = NULL;
+
+ } else {
+ return NGX_OK;
+ }
+
+ while (cl) {
+ if (cl->buf->last_shadow) {
+ if (ngx_event_pipe_add_free_buf(p, cl->buf->shadow) != NGX_OK) {
+ return NGX_ABORT;
+ }
+
+ cl->buf->last_shadow = 0;
+ }
+
+ cl->buf->shadow = NULL;
+ tl = cl->next;
+ cl->next = p->free;
+ p->free = cl;
+ cl = tl;
+ }
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_pipe.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_pipe.h
new file mode 100644
index 00000000000..f24e6d148f5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_pipe.h
@@ -0,0 +1,94 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_EVENT_PIPE_H_INCLUDED_
+#define _NGX_EVENT_PIPE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+typedef struct ngx_event_pipe_s ngx_event_pipe_t;
+
+typedef ngx_int_t (*ngx_event_pipe_input_filter_pt)(ngx_event_pipe_t *p,
+ ngx_buf_t *buf);
+typedef ngx_int_t (*ngx_event_pipe_output_filter_pt)(void *data,
+ ngx_chain_t *chain);
+
+
+struct ngx_event_pipe_s {
+ ngx_connection_t *upstream;
+ ngx_connection_t *downstream;
+
+ ngx_chain_t *free_raw_bufs;
+ ngx_chain_t *in;
+ ngx_chain_t **last_in;
+
+ ngx_chain_t *out;
+ ngx_chain_t *free;
+ ngx_chain_t *busy;
+
+ /*
+ * the input filter i.e. that moves HTTP/1.1 chunks
+ * from the raw bufs to an incoming chain
+ */
+
+ ngx_event_pipe_input_filter_pt input_filter;
+ void *input_ctx;
+
+ ngx_event_pipe_output_filter_pt output_filter;
+ void *output_ctx;
+
+ unsigned read:1;
+ unsigned cacheable:1;
+ unsigned single_buf:1;
+ unsigned free_bufs:1;
+ unsigned upstream_done:1;
+ unsigned upstream_error:1;
+ unsigned upstream_eof:1;
+ unsigned upstream_blocked:1;
+ unsigned downstream_done:1;
+ unsigned downstream_error:1;
+ unsigned cyclic_temp_file:1;
+
+ ngx_int_t allocated;
+ ngx_bufs_t bufs;
+ ngx_buf_tag_t tag;
+
+ ssize_t busy_size;
+
+ off_t read_length;
+ off_t length;
+
+ off_t max_temp_file_size;
+ ssize_t temp_file_write_size;
+
+ ngx_msec_t read_timeout;
+ ngx_msec_t send_timeout;
+ ssize_t send_lowat;
+
+ ngx_pool_t *pool;
+ ngx_log_t *log;
+
+ ngx_chain_t *preread_bufs;
+ size_t preread_size;
+ ngx_buf_t *buf_to_file;
+
+ ngx_temp_file_t *temp_file;
+
+ /* STUB */ int num;
+};
+
+
+ngx_int_t ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write);
+ngx_int_t ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf);
+ngx_int_t ngx_event_pipe_add_free_buf(ngx_event_pipe_t *p, ngx_buf_t *b);
+
+
+#endif /* _NGX_EVENT_PIPE_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_posted.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_posted.c
new file mode 100644
index 00000000000..e548145ed73
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_posted.c
@@ -0,0 +1,173 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+ngx_thread_volatile ngx_event_t *ngx_posted_accept_events;
+ngx_thread_volatile ngx_event_t *ngx_posted_events;
+
+#if (NGX_THREADS)
+ngx_mutex_t *ngx_posted_events_mutex;
+#endif
+
+
+void
+ngx_event_process_posted(ngx_cycle_t *cycle,
+ ngx_thread_volatile ngx_event_t **posted)
+{
+ ngx_event_t *ev;
+
+ for ( ;; ) {
+
+ ev = (ngx_event_t *) *posted;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "posted event %p", ev);
+
+ if (ev == NULL) {
+ return;
+ }
+
+ ngx_delete_posted_event(ev);
+
+ ev->handler(ev);
+ }
+}
+
+
+#if (NGX_THREADS) && !(NGX_WIN32)
+
+void
+ngx_wakeup_worker_thread(ngx_cycle_t *cycle)
+{
+ ngx_int_t i;
+#if 0
+ ngx_uint_t busy;
+ ngx_event_t *ev;
+
+ busy = 1;
+
+ if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) {
+ return;
+ }
+
+ for (ev = (ngx_event_t *) ngx_posted_events; ev; ev = ev->next) {
+ if (*(ev->lock) == 0) {
+ busy = 0;
+ break;
+ }
+ }
+
+ ngx_mutex_unlock(ngx_posted_events_mutex);
+
+ if (busy) {
+ return;
+ }
+#endif
+
+ for (i = 0; i < ngx_threads_n; i++) {
+ if (ngx_threads[i].state == NGX_THREAD_FREE) {
+ ngx_cond_signal(ngx_threads[i].cv);
+ return;
+ }
+ }
+}
+
+
+ngx_int_t
+ngx_event_thread_process_posted(ngx_cycle_t *cycle)
+{
+ ngx_event_t *ev;
+
+ for ( ;; ) {
+
+ ev = (ngx_event_t *) ngx_posted_events;
+
+ for ( ;; ) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "posted event %p", ev);
+
+ if (ev == NULL) {
+ return NGX_OK;
+ }
+
+ if (ngx_trylock(ev->lock) == 0) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "posted event %p is busy", ev);
+
+ ev = ev->next;
+ continue;
+ }
+
+ if (ev->lock != ev->own_lock) {
+ if (*(ev->own_lock)) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "the own lock of the posted event %p is busy", ev);
+ ngx_unlock(ev->lock);
+ ev = ev->next;
+ continue;
+ }
+ *(ev->own_lock) = 1;
+ }
+
+ ngx_delete_posted_event(ev);
+
+ ev->locked = 1;
+
+ ev->ready |= ev->posted_ready;
+ ev->timedout |= ev->posted_timedout;
+ ev->pending_eof |= ev->posted_eof;
+#if (NGX_HAVE_KQUEUE)
+ ev->kq_errno |= ev->posted_errno;
+#endif
+ if (ev->posted_available) {
+ ev->available = ev->posted_available;
+ }
+
+ ev->posted_ready = 0;
+ ev->posted_timedout = 0;
+ ev->posted_eof = 0;
+#if (NGX_HAVE_KQUEUE)
+ ev->posted_errno = 0;
+#endif
+ ev->posted_available = 0;
+
+ ngx_mutex_unlock(ngx_posted_events_mutex);
+
+ ev->handler(ev);
+
+ ngx_mutex_lock(ngx_posted_events_mutex);
+
+ if (ev->locked) {
+ ngx_unlock(ev->lock);
+
+ if (ev->lock != ev->own_lock) {
+ ngx_unlock(ev->own_lock);
+ }
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "posted event %p is done", ev);
+
+ break;
+ }
+ }
+}
+
+#else
+
+void
+ngx_wakeup_worker_thread(ngx_cycle_t *cycle)
+{
+}
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_posted.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_posted.h
new file mode 100644
index 00000000000..abd2e261dff
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_posted.h
@@ -0,0 +1,75 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_EVENT_POSTED_H_INCLUDED_
+#define _NGX_EVENT_POSTED_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if (NGX_THREADS)
+extern ngx_mutex_t *ngx_posted_events_mutex;
+#endif
+
+
+#define ngx_locked_post_event(ev, queue) \
+ \
+ if (ev->prev == NULL) { \
+ ev->next = (ngx_event_t *) *queue; \
+ ev->prev = (ngx_event_t **) queue; \
+ *queue = ev; \
+ \
+ if (ev->next) { \
+ ev->next->prev = &ev->next; \
+ } \
+ \
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "post event %p", ev); \
+ \
+ } else { \
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, \
+ "update posted event %p", ev); \
+ }
+
+
+#define ngx_post_event(ev, queue) \
+ \
+ ngx_mutex_lock(ngx_posted_events_mutex); \
+ ngx_locked_post_event(ev, queue); \
+ ngx_mutex_unlock(ngx_posted_events_mutex);
+
+
+#define ngx_delete_posted_event(ev) \
+ \
+ *(ev->prev) = ev->next; \
+ \
+ if (ev->next) { \
+ ev->next->prev = ev->prev; \
+ } \
+ \
+ ev->prev = NULL; \
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, \
+ "delete posted event %p", ev);
+
+
+
+void ngx_event_process_posted(ngx_cycle_t *cycle,
+ ngx_thread_volatile ngx_event_t **posted);
+void ngx_wakeup_worker_thread(ngx_cycle_t *cycle);
+
+#if (NGX_THREADS)
+ngx_int_t ngx_event_thread_process_posted(ngx_cycle_t *cycle);
+#endif
+
+
+extern ngx_thread_volatile ngx_event_t *ngx_posted_accept_events;
+extern ngx_thread_volatile ngx_event_t *ngx_posted_events;
+
+
+#endif /* _NGX_EVENT_POSTED_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_timer.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_timer.c
new file mode 100644
index 00000000000..177ac1cf113
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_timer.c
@@ -0,0 +1,158 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if (NGX_THREADS)
+ngx_mutex_t *ngx_event_timer_mutex;
+#endif
+
+
+ngx_thread_volatile ngx_rbtree_t ngx_event_timer_rbtree;
+static ngx_rbtree_node_t ngx_event_timer_sentinel;
+
+/*
+ * the event timer rbtree may contain the duplicate keys, however,
+ * it should not be a problem, because we use the rbtree to find
+ * a minimum timer value only
+ */
+
+ngx_int_t
+ngx_event_timer_init(ngx_log_t *log)
+{
+ ngx_rbtree_init(&ngx_event_timer_rbtree, &ngx_event_timer_sentinel,
+ ngx_rbtree_insert_timer_value);
+
+#if (NGX_THREADS)
+
+ if (ngx_event_timer_mutex) {
+ ngx_event_timer_mutex->log = log;
+ return NGX_OK;
+ }
+
+ ngx_event_timer_mutex = ngx_mutex_init(log, 0);
+ if (ngx_event_timer_mutex == NULL) {
+ return NGX_ERROR;
+ }
+
+#endif
+
+ return NGX_OK;
+}
+
+
+ngx_msec_t
+ngx_event_find_timer(void)
+{
+ ngx_msec_int_t timer;
+ ngx_rbtree_node_t *node, *root, *sentinel;
+
+ if (ngx_event_timer_rbtree.root == &ngx_event_timer_sentinel) {
+ return NGX_TIMER_INFINITE;
+ }
+
+ ngx_mutex_lock(ngx_event_timer_mutex);
+
+ root = ngx_event_timer_rbtree.root;
+ sentinel = ngx_event_timer_rbtree.sentinel;
+
+ node = ngx_rbtree_min(root, sentinel);
+
+ ngx_mutex_unlock(ngx_event_timer_mutex);
+
+ timer = (ngx_msec_int_t) (node->key - ngx_current_msec);
+
+ return (ngx_msec_t) (timer > 0 ? timer : 0);
+}
+
+
+void
+ngx_event_expire_timers(void)
+{
+ ngx_event_t *ev;
+ ngx_rbtree_node_t *node, *root, *sentinel;
+
+ sentinel = ngx_event_timer_rbtree.sentinel;
+
+ for ( ;; ) {
+
+ ngx_mutex_lock(ngx_event_timer_mutex);
+
+ root = ngx_event_timer_rbtree.root;
+
+ if (root == sentinel) {
+ return;
+ }
+
+ node = ngx_rbtree_min(root, sentinel);
+
+ /* node->key <= ngx_current_time */
+
+ if ((ngx_msec_int_t) (node->key - ngx_current_msec) <= 0) {
+ ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));
+
+#if (NGX_THREADS)
+
+ if (ngx_threaded && ngx_trylock(ev->lock) == 0) {
+
+ /*
+ * We cannot change the timer of the event that is being
+ * handled by another thread. And we cannot easy walk
+ * the rbtree to find next expired timer so we exit the loop.
+ * However, it should be a rare case when the event that is
+ * being handled has an expired timer.
+ */
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "event %p is busy in expire timers", ev);
+ break;
+ }
+#endif
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "event timer del: %d: %M",
+ ngx_event_ident(ev->data), ev->timer.key);
+
+ ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);
+
+ ngx_mutex_unlock(ngx_event_timer_mutex);
+
+#if (NGX_DEBUG)
+ ev->timer.left = NULL;
+ ev->timer.right = NULL;
+ ev->timer.parent = NULL;
+#endif
+
+ ev->timer_set = 0;
+
+#if (NGX_THREADS)
+ if (ngx_threaded) {
+ ev->posted_timedout = 1;
+
+ ngx_post_event(ev, &ngx_posted_events);
+
+ ngx_unlock(ev->lock);
+
+ continue;
+ }
+#endif
+
+ ev->timedout = 1;
+
+ ev->handler(ev);
+
+ continue;
+ }
+
+ break;
+ }
+
+ ngx_mutex_unlock(ngx_event_timer_mutex);
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_timer.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_timer.h
new file mode 100644
index 00000000000..ec9b316bdc8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/event/ngx_event_timer.h
@@ -0,0 +1,102 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_EVENT_TIMER_H_INCLUDED_
+#define _NGX_EVENT_TIMER_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#define NGX_TIMER_INFINITE (ngx_msec_t) -1
+
+#define NGX_TIMER_LAZY_DELAY 300
+
+
+ngx_int_t ngx_event_timer_init(ngx_log_t *log);
+ngx_msec_t ngx_event_find_timer(void);
+void ngx_event_expire_timers(void);
+
+
+#if (NGX_THREADS)
+extern ngx_mutex_t *ngx_event_timer_mutex;
+#endif
+
+
+extern ngx_thread_volatile ngx_rbtree_t ngx_event_timer_rbtree;
+
+
+static ngx_inline void
+ngx_event_del_timer(ngx_event_t *ev)
+{
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "event timer del: %d: %M",
+ ngx_event_ident(ev->data), ev->timer.key);
+
+ ngx_mutex_lock(ngx_event_timer_mutex);
+
+ ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);
+
+ ngx_mutex_unlock(ngx_event_timer_mutex);
+
+#if (NGX_DEBUG)
+ ev->timer.left = NULL;
+ ev->timer.right = NULL;
+ ev->timer.parent = NULL;
+#endif
+
+ ev->timer_set = 0;
+}
+
+
+static ngx_inline void
+ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)
+{
+ ngx_msec_t key;
+ ngx_msec_int_t diff;
+
+ key = ngx_current_msec + timer;
+
+ if (ev->timer_set) {
+
+ /*
+ * Use a previous timer value if difference between it and a new
+ * value is less than NGX_TIMER_LAZY_DELAY milliseconds: this allows
+ * to minimize the rbtree operations for fast connections.
+ */
+
+ diff = (ngx_msec_int_t) (key - ev->timer.key);
+
+ if (ngx_abs(diff) < NGX_TIMER_LAZY_DELAY) {
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "event timer: %d, old: %M, new: %M",
+ ngx_event_ident(ev->data), ev->timer.key, key);
+ return;
+ }
+
+ ngx_del_timer(ev);
+ }
+
+ ev->timer.key = key;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "event timer add: %d: %M:%M",
+ ngx_event_ident(ev->data), timer, ev->timer.key);
+
+ ngx_mutex_lock(ngx_event_timer_mutex);
+
+ ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);
+
+ ngx_mutex_unlock(ngx_event_timer_mutex);
+
+ ev->timer_set = 1;
+}
+
+
+#endif /* _NGX_EVENT_TIMER_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_access_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_access_module.c
new file mode 100644
index 00000000000..c553e46106b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_access_module.c
@@ -0,0 +1,469 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ in_addr_t mask;
+ in_addr_t addr;
+ ngx_uint_t deny; /* unsigned deny:1; */
+} ngx_http_access_rule_t;
+
+#if (NGX_HAVE_INET6)
+
+typedef struct {
+ struct in6_addr addr;
+ struct in6_addr mask;
+ ngx_uint_t deny; /* unsigned deny:1; */
+} ngx_http_access_rule6_t;
+
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+typedef struct {
+ ngx_uint_t deny; /* unsigned deny:1; */
+} ngx_http_access_rule_un_t;
+
+#endif
+
+typedef struct {
+ ngx_array_t *rules; /* array of ngx_http_access_rule_t */
+#if (NGX_HAVE_INET6)
+ ngx_array_t *rules6; /* array of ngx_http_access_rule6_t */
+#endif
+#if (NGX_HAVE_UNIX_DOMAIN)
+ ngx_array_t *rules_un; /* array of ngx_http_access_rule_un_t */
+#endif
+} ngx_http_access_loc_conf_t;
+
+
+static ngx_int_t ngx_http_access_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_access_inet(ngx_http_request_t *r,
+ ngx_http_access_loc_conf_t *alcf, in_addr_t addr);
+#if (NGX_HAVE_INET6)
+static ngx_int_t ngx_http_access_inet6(ngx_http_request_t *r,
+ ngx_http_access_loc_conf_t *alcf, u_char *p);
+#endif
+#if (NGX_HAVE_UNIX_DOMAIN)
+static ngx_int_t ngx_http_access_unix(ngx_http_request_t *r,
+ ngx_http_access_loc_conf_t *alcf);
+#endif
+static ngx_int_t ngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny);
+static char *ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static void *ngx_http_access_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_access_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static ngx_int_t ngx_http_access_init(ngx_conf_t *cf);
+
+
+static ngx_command_t ngx_http_access_commands[] = {
+
+ { ngx_string("allow"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
+ |NGX_CONF_TAKE1,
+ ngx_http_access_rule,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("deny"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
+ |NGX_CONF_TAKE1,
+ ngx_http_access_rule,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+
+static ngx_http_module_t ngx_http_access_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_access_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_access_create_loc_conf, /* create location configuration */
+ ngx_http_access_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_access_module = {
+ NGX_MODULE_V1,
+ &ngx_http_access_module_ctx, /* module context */
+ ngx_http_access_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_access_handler(ngx_http_request_t *r)
+{
+ struct sockaddr_in *sin;
+ ngx_http_access_loc_conf_t *alcf;
+#if (NGX_HAVE_INET6)
+ u_char *p;
+ in_addr_t addr;
+ struct sockaddr_in6 *sin6;
+#endif
+
+ alcf = ngx_http_get_module_loc_conf(r, ngx_http_access_module);
+
+ switch (r->connection->sockaddr->sa_family) {
+
+ case AF_INET:
+ if (alcf->rules) {
+ sin = (struct sockaddr_in *) r->connection->sockaddr;
+ return ngx_http_access_inet(r, alcf, sin->sin_addr.s_addr);
+ }
+ break;
+
+#if (NGX_HAVE_INET6)
+
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
+ p = sin6->sin6_addr.s6_addr;
+
+ if (alcf->rules && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+ addr = p[12] << 24;
+ addr += p[13] << 16;
+ addr += p[14] << 8;
+ addr += p[15];
+ return ngx_http_access_inet(r, alcf, htonl(addr));
+ }
+
+ if (alcf->rules6) {
+ return ngx_http_access_inet6(r, alcf, p);
+ }
+
+ break;
+
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+ case AF_UNIX:
+ if (alcf->rules_un) {
+ return ngx_http_access_unix(r, alcf);
+ }
+
+ break;
+
+#endif
+ }
+
+ return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_access_inet(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf,
+ in_addr_t addr)
+{
+ ngx_uint_t i;
+ ngx_http_access_rule_t *rule;
+
+ rule = alcf->rules->elts;
+ for (i = 0; i < alcf->rules->nelts; i++) {
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "access: %08XD %08XD %08XD",
+ addr, rule[i].mask, rule[i].addr);
+
+ if ((addr & rule[i].mask) == rule[i].addr) {
+ return ngx_http_access_found(r, rule[i].deny);
+ }
+ }
+
+ return NGX_DECLINED;
+}
+
+
+#if (NGX_HAVE_INET6)
+
+static ngx_int_t
+ngx_http_access_inet6(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf,
+ u_char *p)
+{
+ ngx_uint_t n;
+ ngx_uint_t i;
+ ngx_http_access_rule6_t *rule6;
+
+ rule6 = alcf->rules6->elts;
+ for (i = 0; i < alcf->rules6->nelts; i++) {
+
+#if (NGX_DEBUG)
+ {
+ size_t cl, ml, al;
+ u_char ct[NGX_INET6_ADDRSTRLEN];
+ u_char mt[NGX_INET6_ADDRSTRLEN];
+ u_char at[NGX_INET6_ADDRSTRLEN];
+
+ cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN);
+ ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN);
+ al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN);
+
+ ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "access: %*s %*s %*s", cl, ct, ml, mt, al, at);
+ }
+#endif
+
+ for (n = 0; n < 16; n++) {
+ if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) {
+ goto next;
+ }
+ }
+
+ return ngx_http_access_found(r, rule6[i].deny);
+
+ next:
+ continue;
+ }
+
+ return NGX_DECLINED;
+}
+
+#endif
+
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+static ngx_int_t
+ngx_http_access_unix(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf)
+{
+ ngx_uint_t i;
+ ngx_http_access_rule_un_t *rule_un;
+
+ rule_un = alcf->rules_un->elts;
+ for (i = 0; i < alcf->rules_un->nelts; i++) {
+
+ /* TODO: check path */
+ if (1) {
+ return ngx_http_access_found(r, rule_un[i].deny);
+ }
+ }
+
+ return NGX_DECLINED;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny)
+{
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (deny) {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "access forbidden by rule");
+ }
+
+ return NGX_HTTP_FORBIDDEN;
+ }
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_access_loc_conf_t *alcf = conf;
+
+ ngx_int_t rc;
+ ngx_uint_t all;
+ ngx_str_t *value;
+ ngx_cidr_t cidr;
+ ngx_http_access_rule_t *rule;
+#if (NGX_HAVE_INET6)
+ ngx_http_access_rule6_t *rule6;
+#endif
+#if (NGX_HAVE_UNIX_DOMAIN)
+ ngx_http_access_rule_un_t *rule_un;
+#endif
+
+ ngx_memzero(&cidr, sizeof(ngx_cidr_t));
+
+ value = cf->args->elts;
+
+ all = (value[1].len == 3 && ngx_strcmp(value[1].data, "all") == 0);
+
+ if (!all) {
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+ if (value[1].len == 5 && ngx_strcmp(value[1].data, "unix:") == 0) {
+ cidr.family = AF_UNIX;
+ rc = NGX_OK;
+
+ } else {
+ rc = ngx_ptocidr(&value[1], &cidr);
+ }
+
+#else
+ rc = ngx_ptocidr(&value[1], &cidr);
+#endif
+
+ if (rc == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "low address bits of %V are meaningless", &value[1]);
+ }
+ }
+
+ if (cidr.family == AF_INET || all) {
+
+ if (alcf->rules == NULL) {
+ alcf->rules = ngx_array_create(cf->pool, 4,
+ sizeof(ngx_http_access_rule_t));
+ if (alcf->rules == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ rule = ngx_array_push(alcf->rules);
+ if (rule == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ rule->mask = cidr.u.in.mask;
+ rule->addr = cidr.u.in.addr;
+ rule->deny = (value[0].data[0] == 'd') ? 1 : 0;
+ }
+
+#if (NGX_HAVE_INET6)
+ if (cidr.family == AF_INET6 || all) {
+
+ if (alcf->rules6 == NULL) {
+ alcf->rules6 = ngx_array_create(cf->pool, 4,
+ sizeof(ngx_http_access_rule6_t));
+ if (alcf->rules6 == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ rule6 = ngx_array_push(alcf->rules6);
+ if (rule6 == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ rule6->mask = cidr.u.in6.mask;
+ rule6->addr = cidr.u.in6.addr;
+ rule6->deny = (value[0].data[0] == 'd') ? 1 : 0;
+ }
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ if (cidr.family == AF_UNIX || all) {
+
+ if (alcf->rules_un == NULL) {
+ alcf->rules_un = ngx_array_create(cf->pool, 1,
+ sizeof(ngx_http_access_rule_un_t));
+ if (alcf->rules_un == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ rule_un = ngx_array_push(alcf->rules_un);
+ if (rule_un == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ rule_un->deny = (value[0].data[0] == 'd') ? 1 : 0;
+ }
+#endif
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_access_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_access_loc_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_access_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ return conf;
+}
+
+
+static char *
+ngx_http_access_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_access_loc_conf_t *prev = parent;
+ ngx_http_access_loc_conf_t *conf = child;
+
+ if (conf->rules == NULL
+#if (NGX_HAVE_INET6)
+ && conf->rules6 == NULL
+#endif
+#if (NGX_HAVE_UNIX_DOMAIN)
+ && conf->rules_un == NULL
+#endif
+ ) {
+ conf->rules = prev->rules;
+#if (NGX_HAVE_INET6)
+ conf->rules6 = prev->rules6;
+#endif
+#if (NGX_HAVE_UNIX_DOMAIN)
+ conf->rules_un = prev->rules_un;
+#endif
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_access_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_access_handler;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_addition_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_addition_filter_module.c
new file mode 100644
index 00000000000..db4970bf1d6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_addition_filter_module.c
@@ -0,0 +1,251 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_str_t before_body;
+ ngx_str_t after_body;
+
+ ngx_hash_t types;
+ ngx_array_t *types_keys;
+} ngx_http_addition_conf_t;
+
+
+typedef struct {
+ ngx_uint_t before_body_sent;
+} ngx_http_addition_ctx_t;
+
+
+static void *ngx_http_addition_create_conf(ngx_conf_t *cf);
+static char *ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static ngx_int_t ngx_http_addition_filter_init(ngx_conf_t *cf);
+
+
+static ngx_command_t ngx_http_addition_commands[] = {
+
+ { ngx_string("add_before_body"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_addition_conf_t, before_body),
+ NULL },
+
+ { ngx_string("add_after_body"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_addition_conf_t, after_body),
+ NULL },
+
+ { ngx_string("addition_types"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_types_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_addition_conf_t, types_keys),
+ &ngx_http_html_default_types[0] },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_addition_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_addition_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_addition_create_conf, /* create location configuration */
+ ngx_http_addition_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_addition_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_addition_filter_module_ctx, /* module context */
+ ngx_http_addition_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_addition_header_filter(ngx_http_request_t *r)
+{
+ ngx_http_addition_ctx_t *ctx;
+ ngx_http_addition_conf_t *conf;
+
+ if (r->headers_out.status != NGX_HTTP_OK || r != r->main) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module);
+
+ if (conf->before_body.len == 0 && conf->after_body.len == 0) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ if (ngx_http_test_content_type(r, &conf->types) == NULL) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_addition_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_addition_filter_module);
+
+ ngx_http_clear_content_length(r);
+ ngx_http_clear_accept_ranges(r);
+ ngx_http_weak_etag(r);
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_addition_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ ngx_int_t rc;
+ ngx_uint_t last;
+ ngx_chain_t *cl;
+ ngx_http_request_t *sr;
+ ngx_http_addition_ctx_t *ctx;
+ ngx_http_addition_conf_t *conf;
+
+ if (in == NULL || r->header_only) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_addition_filter_module);
+
+ if (ctx == NULL) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module);
+
+ if (!ctx->before_body_sent) {
+ ctx->before_body_sent = 1;
+
+ if (conf->before_body.len) {
+ if (ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ if (conf->after_body.len == 0) {
+ ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ last = 0;
+
+ for (cl = in; cl; cl = cl->next) {
+ if (cl->buf->last_buf) {
+ cl->buf->last_buf = 0;
+ cl->buf->sync = 1;
+ last = 1;
+ }
+ }
+
+ rc = ngx_http_next_body_filter(r, in);
+
+ if (rc == NGX_ERROR || !last || conf->after_body.len == 0) {
+ return rc;
+ }
+
+ if (ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);
+
+ return ngx_http_send_special(r, NGX_HTTP_LAST);
+}
+
+
+static ngx_int_t
+ngx_http_addition_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_addition_header_filter;
+
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_addition_body_filter;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_addition_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_addition_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_addition_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->before_body = { 0, NULL };
+ * conf->after_body = { 0, NULL };
+ * conf->types = { NULL };
+ * conf->types_keys = NULL;
+ */
+
+ return conf;
+}
+
+
+static char *
+ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_addition_conf_t *prev = parent;
+ ngx_http_addition_conf_t *conf = child;
+
+ ngx_conf_merge_str_value(conf->before_body, prev->before_body, "");
+ ngx_conf_merge_str_value(conf->after_body, prev->after_body, "");
+
+ if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
+ &prev->types_keys, &prev->types,
+ ngx_http_html_default_types)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_auth_basic_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_auth_basic_module.c
new file mode 100644
index 00000000000..8ec53295fba
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_auth_basic_module.c
@@ -0,0 +1,467 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_crypt.h>
+
+
+#define NGX_HTTP_AUTH_BUF_SIZE 2048
+
+
+typedef struct {
+ ngx_str_t passwd;
+} ngx_http_auth_basic_ctx_t;
+
+
+typedef struct {
+ ngx_http_complex_value_t *realm;
+ ngx_http_complex_value_t user_file;
+} ngx_http_auth_basic_loc_conf_t;
+
+
+static ngx_int_t ngx_http_auth_basic_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,
+ ngx_http_auth_basic_ctx_t *ctx, ngx_str_t *passwd, ngx_str_t *realm);
+static ngx_int_t ngx_http_auth_basic_set_realm(ngx_http_request_t *r,
+ ngx_str_t *realm);
+static void ngx_http_auth_basic_close(ngx_file_t *file);
+static void *ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static ngx_int_t ngx_http_auth_basic_init(ngx_conf_t *cf);
+static char *ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_http_auth_basic_commands[] = {
+
+ { ngx_string("auth_basic"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
+ |NGX_CONF_TAKE1,
+ ngx_http_set_complex_value_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_auth_basic_loc_conf_t, realm),
+ NULL },
+
+ { ngx_string("auth_basic_user_file"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF
+ |NGX_CONF_TAKE1,
+ ngx_http_auth_basic_user_file,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_auth_basic_loc_conf_t, user_file),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_auth_basic_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_auth_basic_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_auth_basic_create_loc_conf, /* create location configuration */
+ ngx_http_auth_basic_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_auth_basic_module = {
+ NGX_MODULE_V1,
+ &ngx_http_auth_basic_module_ctx, /* module context */
+ ngx_http_auth_basic_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_auth_basic_handler(ngx_http_request_t *r)
+{
+ off_t offset;
+ ssize_t n;
+ ngx_fd_t fd;
+ ngx_int_t rc;
+ ngx_err_t err;
+ ngx_str_t pwd, realm, user_file;
+ ngx_uint_t i, level, login, left, passwd;
+ ngx_file_t file;
+ ngx_http_auth_basic_ctx_t *ctx;
+ ngx_http_auth_basic_loc_conf_t *alcf;
+ u_char buf[NGX_HTTP_AUTH_BUF_SIZE];
+ enum {
+ sw_login,
+ sw_passwd,
+ sw_skip
+ } state;
+
+ alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_basic_module);
+
+ if (alcf->realm == NULL || alcf->user_file.value.data == NULL) {
+ return NGX_DECLINED;
+ }
+
+ if (ngx_http_complex_value(r, alcf->realm, &realm) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (realm.len == 3 && ngx_strncmp(realm.data, "off", 3) == 0) {
+ return NGX_DECLINED;
+ }
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_auth_basic_module);
+
+ if (ctx) {
+ return ngx_http_auth_basic_crypt_handler(r, ctx, &ctx->passwd,
+ &realm);
+ }
+
+ rc = ngx_http_auth_basic_user(r);
+
+ if (rc == NGX_DECLINED) {
+
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "no user/password was provided for basic authentication");
+
+ return ngx_http_auth_basic_set_realm(r, &realm);
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_http_complex_value(r, &alcf->user_file, &user_file) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+
+ if (fd == NGX_INVALID_FILE) {
+ err = ngx_errno;
+
+ if (err == NGX_ENOENT) {
+ level = NGX_LOG_ERR;
+ rc = NGX_HTTP_FORBIDDEN;
+
+ } else {
+ level = NGX_LOG_CRIT;
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_log_error(level, r->connection->log, err,
+ ngx_open_file_n " \"%s\" failed", user_file.data);
+
+ return rc;
+ }
+
+ ngx_memzero(&file, sizeof(ngx_file_t));
+
+ file.fd = fd;
+ file.name = user_file;
+ file.log = r->connection->log;
+
+ state = sw_login;
+ passwd = 0;
+ login = 0;
+ left = 0;
+ offset = 0;
+
+ for ( ;; ) {
+ i = left;
+
+ n = ngx_read_file(&file, buf + left, NGX_HTTP_AUTH_BUF_SIZE - left,
+ offset);
+
+ if (n == NGX_ERROR) {
+ ngx_http_auth_basic_close(&file);
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (n == 0) {
+ break;
+ }
+
+ for (i = left; i < left + n; i++) {
+ switch (state) {
+
+ case sw_login:
+ if (login == 0) {
+
+ if (buf[i] == '#' || buf[i] == CR) {
+ state = sw_skip;
+ break;
+ }
+
+ if (buf[i] == LF) {
+ break;
+ }
+ }
+
+ if (buf[i] != r->headers_in.user.data[login]) {
+ state = sw_skip;
+ break;
+ }
+
+ if (login == r->headers_in.user.len) {
+ state = sw_passwd;
+ passwd = i + 1;
+ }
+
+ login++;
+
+ break;
+
+ case sw_passwd:
+ if (buf[i] == LF || buf[i] == CR || buf[i] == ':') {
+ buf[i] = '\0';
+
+ ngx_http_auth_basic_close(&file);
+
+ pwd.len = i - passwd;
+ pwd.data = &buf[passwd];
+
+ return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd,
+ &realm);
+ }
+
+ break;
+
+ case sw_skip:
+ if (buf[i] == LF) {
+ state = sw_login;
+ login = 0;
+ }
+
+ break;
+ }
+ }
+
+ if (state == sw_passwd) {
+ left = left + n - passwd;
+ ngx_memmove(buf, &buf[passwd], left);
+ passwd = 0;
+
+ } else {
+ left = 0;
+ }
+
+ offset += n;
+ }
+
+ ngx_http_auth_basic_close(&file);
+
+ if (state == sw_passwd) {
+ pwd.len = i - passwd;
+ pwd.data = ngx_pnalloc(r->pool, pwd.len + 1);
+ if (pwd.data == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1);
+
+ return ngx_http_auth_basic_crypt_handler(r, NULL, &pwd, &realm);
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "user \"%V\" was not found in \"%V\"",
+ &r->headers_in.user, &user_file);
+
+ return ngx_http_auth_basic_set_realm(r, &realm);
+}
+
+
+static ngx_int_t
+ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,
+ ngx_http_auth_basic_ctx_t *ctx, ngx_str_t *passwd, ngx_str_t *realm)
+{
+ ngx_int_t rc;
+ u_char *encrypted;
+
+ rc = ngx_crypt(r->pool, r->headers_in.passwd.data, passwd->data,
+ &encrypted);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "rc: %d user: \"%V\" salt: \"%s\"",
+ rc, &r->headers_in.user, passwd->data);
+
+ if (rc == NGX_OK) {
+ if (ngx_strcmp(encrypted, passwd->data) == 0) {
+ return NGX_OK;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "encrypted: \"%s\"", encrypted);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "user \"%V\": password mismatch",
+ &r->headers_in.user);
+
+ return ngx_http_auth_basic_set_realm(r, realm);
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* rc == NGX_AGAIN */
+
+ if (ctx == NULL) {
+ ctx = ngx_palloc(r->pool, sizeof(ngx_http_auth_basic_ctx_t));
+ if (ctx == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_auth_basic_module);
+
+ ctx->passwd.len = passwd->len;
+ passwd->len++;
+
+ ctx->passwd.data = ngx_pstrdup(r->pool, passwd);
+ if (ctx->passwd.data == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ }
+
+ /* TODO: add mutex event */
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_auth_basic_set_realm(ngx_http_request_t *r, ngx_str_t *realm)
+{
+ size_t len;
+ u_char *basic, *p;
+
+ r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers);
+ if (r->headers_out.www_authenticate == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ len = sizeof("Basic realm=\"\"") - 1 + realm->len;
+
+ basic = ngx_pnalloc(r->pool, len);
+ if (basic == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ p = ngx_cpymem(basic, "Basic realm=\"", sizeof("Basic realm=\"") - 1);
+ p = ngx_cpymem(p, realm->data, realm->len);
+ *p = '"';
+
+ r->headers_out.www_authenticate->hash = 1;
+ ngx_str_set(&r->headers_out.www_authenticate->key, "WWW-Authenticate");
+ r->headers_out.www_authenticate->value.data = basic;
+ r->headers_out.www_authenticate->value.len = len;
+
+ return NGX_HTTP_UNAUTHORIZED;
+}
+
+static void
+ngx_http_auth_basic_close(ngx_file_t *file)
+{
+ if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", file->name.data);
+ }
+}
+
+
+static void *
+ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_auth_basic_loc_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_basic_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ return conf;
+}
+
+
+static char *
+ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_auth_basic_loc_conf_t *prev = parent;
+ ngx_http_auth_basic_loc_conf_t *conf = child;
+
+ if (conf->realm == NULL) {
+ conf->realm = prev->realm;
+ }
+
+ if (conf->user_file.value.data == NULL) {
+ conf->user_file = prev->user_file;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_auth_basic_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_auth_basic_handler;
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_auth_basic_loc_conf_t *alcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_compile_complex_value_t ccv;
+
+ if (alcf->user_file.value.data) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &alcf->user_file;
+ ccv.zero = 1;
+ ccv.conf_prefix = 1;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_auth_request_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_auth_request_module.c
new file mode 100644
index 00000000000..b4307be2e7d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_auth_request_module.c
@@ -0,0 +1,444 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_str_t uri;
+ ngx_array_t *vars;
+} ngx_http_auth_request_conf_t;
+
+
+typedef struct {
+ ngx_uint_t done;
+ ngx_uint_t status;
+ ngx_http_request_t *subrequest;
+} ngx_http_auth_request_ctx_t;
+
+
+typedef struct {
+ ngx_int_t index;
+ ngx_http_complex_value_t value;
+ ngx_http_set_variable_pt set_handler;
+} ngx_http_auth_request_variable_t;
+
+
+static ngx_int_t ngx_http_auth_request_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_auth_request_done(ngx_http_request_t *r,
+ void *data, ngx_int_t rc);
+static ngx_int_t ngx_http_auth_request_set_variables(ngx_http_request_t *r,
+ ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx);
+static ngx_int_t ngx_http_auth_request_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static void *ngx_http_auth_request_create_conf(ngx_conf_t *cf);
+static char *ngx_http_auth_request_merge_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static ngx_int_t ngx_http_auth_request_init(ngx_conf_t *cf);
+static char *ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_http_auth_request_commands[] = {
+
+ { ngx_string("auth_request"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_auth_request,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("auth_request_set"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_http_auth_request_set,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_auth_request_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_auth_request_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_auth_request_create_conf, /* create location configuration */
+ ngx_http_auth_request_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_auth_request_module = {
+ NGX_MODULE_V1,
+ &ngx_http_auth_request_module_ctx, /* module context */
+ ngx_http_auth_request_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_auth_request_handler(ngx_http_request_t *r)
+{
+ ngx_table_elt_t *h, *ho;
+ ngx_http_request_t *sr;
+ ngx_http_post_subrequest_t *ps;
+ ngx_http_auth_request_ctx_t *ctx;
+ ngx_http_auth_request_conf_t *arcf;
+
+ arcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_request_module);
+
+ if (arcf->uri.len == 0) {
+ return NGX_DECLINED;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "auth request handler");
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_auth_request_module);
+
+ if (ctx != NULL) {
+ if (!ctx->done) {
+ return NGX_AGAIN;
+ }
+
+ /*
+ * as soon as we are done - explicitly set variables to make
+ * sure they will be available after internal redirects
+ */
+
+ if (ngx_http_auth_request_set_variables(r, arcf, ctx) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ /* return appropriate status */
+
+ if (ctx->status == NGX_HTTP_FORBIDDEN) {
+ return ctx->status;
+ }
+
+ if (ctx->status == NGX_HTTP_UNAUTHORIZED) {
+ sr = ctx->subrequest;
+
+ h = sr->headers_out.www_authenticate;
+
+ if (!h && sr->upstream) {
+ h = sr->upstream->headers_in.www_authenticate;
+ }
+
+ if (h) {
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ r->headers_out.www_authenticate = ho;
+ }
+
+ return ctx->status;
+ }
+
+ if (ctx->status >= NGX_HTTP_OK
+ && ctx->status < NGX_HTTP_SPECIAL_RESPONSE)
+ {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "auth request unexpected status: %d", ctx->status);
+
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_request_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
+ if (ps == NULL) {
+ return NGX_ERROR;
+ }
+
+ ps->handler = ngx_http_auth_request_done;
+ ps->data = ctx;
+
+ if (ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps,
+ NGX_HTTP_SUBREQUEST_WAITED)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ /*
+ * allocate fake request body to avoid attempts to read it and to make
+ * sure real body file (if already read) won't be closed by upstream
+ */
+
+ sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
+ if (sr->request_body == NULL) {
+ return NGX_ERROR;
+ }
+
+ sr->header_only = 1;
+
+ ctx->subrequest = sr;
+
+ ngx_http_set_ctx(r, ctx, ngx_http_auth_request_module);
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_http_auth_request_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
+{
+ ngx_http_auth_request_ctx_t *ctx = data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "auth request done s:%d", r->headers_out.status);
+
+ ctx->done = 1;
+ ctx->status = r->headers_out.status;
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_auth_request_set_variables(ngx_http_request_t *r,
+ ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx)
+{
+ ngx_str_t val;
+ ngx_http_variable_t *v;
+ ngx_http_variable_value_t *vv;
+ ngx_http_auth_request_variable_t *av, *last;
+ ngx_http_core_main_conf_t *cmcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "auth request set variables");
+
+ if (arcf->vars == NULL) {
+ return NGX_OK;
+ }
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+ v = cmcf->variables.elts;
+
+ av = arcf->vars->elts;
+ last = av + arcf->vars->nelts;
+
+ while (av < last) {
+ /*
+ * explicitly set new value to make sure it will be available after
+ * internal redirects
+ */
+
+ vv = &r->variables[av->index];
+
+ if (ngx_http_complex_value(ctx->subrequest, &av->value, &val)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ vv->valid = 1;
+ vv->not_found = 0;
+ vv->data = val.data;
+ vv->len = val.len;
+
+ if (av->set_handler) {
+ /*
+ * set_handler only available in cmcf->variables_keys, so we store
+ * it explicitly
+ */
+
+ av->set_handler(r, vv, v[av->index].data);
+ }
+
+ av++;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_auth_request_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "auth request variable");
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_auth_request_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_auth_request_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_request_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->uri = { 0, NULL };
+ */
+
+ conf->vars = NGX_CONF_UNSET_PTR;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_auth_request_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_auth_request_conf_t *prev = parent;
+ ngx_http_auth_request_conf_t *conf = child;
+
+ ngx_conf_merge_str_value(conf->uri, prev->uri, "");
+ ngx_conf_merge_ptr_value(conf->vars, prev->vars, NULL);
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_auth_request_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_auth_request_handler;
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_auth_request_conf_t *arcf = conf;
+
+ ngx_str_t *value;
+
+ if (arcf->uri.data != NULL) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ arcf->uri.len = 0;
+ arcf->uri.data = (u_char *) "";
+
+ return NGX_CONF_OK;
+ }
+
+ arcf->uri = value[1];
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_auth_request_conf_t *arcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_variable_t *v;
+ ngx_http_auth_request_variable_t *av;
+ ngx_http_compile_complex_value_t ccv;
+
+ value = cf->args->elts;
+
+ if (value[1].data[0] != '$') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ value[1].len--;
+ value[1].data++;
+
+ if (arcf->vars == NGX_CONF_UNSET_PTR) {
+ arcf->vars = ngx_array_create(cf->pool, 1,
+ sizeof(ngx_http_auth_request_variable_t));
+ if (arcf->vars == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ av = ngx_array_push(arcf->vars);
+ if (av == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
+ if (v == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ av->index = ngx_http_get_variable_index(cf, &value[1]);
+ if (av->index == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (v->get_handler == NULL) {
+ v->get_handler = ngx_http_auth_request_variable;
+ v->data = (uintptr_t) av;
+ }
+
+ av->set_handler = v->set_handler;
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[2];
+ ccv.complex_value = &av->value;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_autoindex_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_autoindex_module.c
new file mode 100644
index 00000000000..f694df075db
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_autoindex_module.c
@@ -0,0 +1,705 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#if 0
+
+typedef struct {
+ ngx_buf_t *buf;
+ size_t size;
+ ngx_pool_t *pool;
+ size_t alloc_size;
+ ngx_chain_t **last_out;
+} ngx_http_autoindex_ctx_t;
+
+#endif
+
+
+typedef struct {
+ ngx_str_t name;
+ size_t utf_len;
+ size_t escape;
+ size_t escape_html;
+
+ unsigned dir:1;
+
+ time_t mtime;
+ off_t size;
+} ngx_http_autoindex_entry_t;
+
+
+typedef struct {
+ ngx_flag_t enable;
+ ngx_flag_t localtime;
+ ngx_flag_t exact_size;
+} ngx_http_autoindex_loc_conf_t;
+
+
+#define NGX_HTTP_AUTOINDEX_PREALLOCATE 50
+
+#define NGX_HTTP_AUTOINDEX_NAME_LEN 50
+
+
+static int ngx_libc_cdecl ngx_http_autoindex_cmp_entries(const void *one,
+ const void *two);
+static ngx_int_t ngx_http_autoindex_error(ngx_http_request_t *r,
+ ngx_dir_t *dir, ngx_str_t *name);
+static ngx_int_t ngx_http_autoindex_init(ngx_conf_t *cf);
+static void *ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+
+
+static ngx_command_t ngx_http_autoindex_commands[] = {
+
+ { ngx_string("autoindex"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_autoindex_loc_conf_t, enable),
+ NULL },
+
+ { ngx_string("autoindex_localtime"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_autoindex_loc_conf_t, localtime),
+ NULL },
+
+ { ngx_string("autoindex_exact_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_autoindex_loc_conf_t, exact_size),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_autoindex_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_autoindex_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_autoindex_create_loc_conf, /* create location configuration */
+ ngx_http_autoindex_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_autoindex_module = {
+ NGX_MODULE_V1,
+ &ngx_http_autoindex_module_ctx, /* module context */
+ ngx_http_autoindex_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static u_char title[] =
+"<html>" CRLF
+"<head><title>Index of "
+;
+
+
+static u_char header[] =
+"</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<h1>Index of "
+;
+
+static u_char tail[] =
+"</body>" CRLF
+"</html>" CRLF
+;
+
+
+static ngx_int_t
+ngx_http_autoindex_handler(ngx_http_request_t *r)
+{
+ u_char *last, *filename, scale;
+ off_t length;
+ size_t len, char_len, escape_html, allocated, root;
+ ngx_tm_t tm;
+ ngx_err_t err;
+ ngx_buf_t *b;
+ ngx_int_t rc, size;
+ ngx_str_t path;
+ ngx_dir_t dir;
+ ngx_uint_t i, level, utf8;
+ ngx_pool_t *pool;
+ ngx_time_t *tp;
+ ngx_chain_t out;
+ ngx_array_t entries;
+ ngx_http_autoindex_entry_t *entry;
+ ngx_http_autoindex_loc_conf_t *alcf;
+
+ static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+ if (r->uri.data[r->uri.len - 1] != '/') {
+ return NGX_DECLINED;
+ }
+
+ if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
+ return NGX_DECLINED;
+ }
+
+ alcf = ngx_http_get_module_loc_conf(r, ngx_http_autoindex_module);
+
+ if (!alcf->enable) {
+ return NGX_DECLINED;
+ }
+
+ /* NGX_DIR_MASK_LEN is lesser than NGX_HTTP_AUTOINDEX_PREALLOCATE */
+
+ last = ngx_http_map_uri_to_path(r, &path, &root,
+ NGX_HTTP_AUTOINDEX_PREALLOCATE);
+ if (last == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ allocated = path.len;
+ path.len = last - path.data;
+ if (path.len > 1) {
+ path.len--;
+ }
+ path.data[path.len] = '\0';
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http autoindex: \"%s\"", path.data);
+
+ if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
+ err = ngx_errno;
+
+ if (err == NGX_ENOENT
+ || err == NGX_ENOTDIR
+ || err == NGX_ENAMETOOLONG)
+ {
+ level = NGX_LOG_ERR;
+ rc = NGX_HTTP_NOT_FOUND;
+
+ } else if (err == NGX_EACCES) {
+ level = NGX_LOG_ERR;
+ rc = NGX_HTTP_FORBIDDEN;
+
+ } else {
+ level = NGX_LOG_CRIT;
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_log_error(level, r->connection->log, err,
+ ngx_open_dir_n " \"%s\" failed", path.data);
+
+ return rc;
+ }
+
+#if (NGX_SUPPRESS_WARN)
+
+ /* MSVC thinks 'entries' may be used without having been initialized */
+ ngx_memzero(&entries, sizeof(ngx_array_t));
+
+#endif
+
+ /* TODO: pool should be temporary pool */
+ pool = r->pool;
+
+ if (ngx_array_init(&entries, pool, 40, sizeof(ngx_http_autoindex_entry_t))
+ != NGX_OK)
+ {
+ return ngx_http_autoindex_error(r, &dir, &path);
+ }
+
+ r->headers_out.status = NGX_HTTP_OK;
+ r->headers_out.content_type_len = sizeof("text/html") - 1;
+ ngx_str_set(&r->headers_out.content_type, "text/html");
+ r->headers_out.content_type_lowcase = NULL;
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ if (ngx_close_dir(&dir) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+ ngx_close_dir_n " \"%V\" failed", &path);
+ }
+
+ return rc;
+ }
+
+ filename = path.data;
+ filename[path.len] = '/';
+
+ if (r->headers_out.charset.len == 5
+ && ngx_strncasecmp(r->headers_out.charset.data, (u_char *) "utf-8", 5)
+ == 0)
+ {
+ utf8 = 1;
+
+ } else {
+ utf8 = 0;
+ }
+
+ for ( ;; ) {
+ ngx_set_errno(0);
+
+ if (ngx_read_dir(&dir) == NGX_ERROR) {
+ err = ngx_errno;
+
+ if (err != NGX_ENOMOREFILES) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
+ ngx_read_dir_n " \"%V\" failed", &path);
+ return ngx_http_autoindex_error(r, &dir, &path);
+ }
+
+ break;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http autoindex file: \"%s\"", ngx_de_name(&dir));
+
+ len = ngx_de_namelen(&dir);
+
+ if (ngx_de_name(&dir)[0] == '.') {
+ continue;
+ }
+
+ if (!dir.valid_info) {
+
+ /* 1 byte for '/' and 1 byte for terminating '\0' */
+
+ if (path.len + 1 + len + 1 > allocated) {
+ allocated = path.len + 1 + len + 1
+ + NGX_HTTP_AUTOINDEX_PREALLOCATE;
+
+ filename = ngx_pnalloc(pool, allocated);
+ if (filename == NULL) {
+ return ngx_http_autoindex_error(r, &dir, &path);
+ }
+
+ last = ngx_cpystrn(filename, path.data, path.len + 1);
+ *last++ = '/';
+ }
+
+ ngx_cpystrn(last, ngx_de_name(&dir), len + 1);
+
+ if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
+ err = ngx_errno;
+
+ if (err != NGX_ENOENT && err != NGX_ELOOP) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
+ ngx_de_info_n " \"%s\" failed", filename);
+
+ if (err == NGX_EACCES) {
+ continue;
+ }
+
+ return ngx_http_autoindex_error(r, &dir, &path);
+ }
+
+ if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+ ngx_de_link_info_n " \"%s\" failed",
+ filename);
+ return ngx_http_autoindex_error(r, &dir, &path);
+ }
+ }
+ }
+
+ entry = ngx_array_push(&entries);
+ if (entry == NULL) {
+ return ngx_http_autoindex_error(r, &dir, &path);
+ }
+
+ entry->name.len = len;
+
+ entry->name.data = ngx_pnalloc(pool, len + 1);
+ if (entry->name.data == NULL) {
+ return ngx_http_autoindex_error(r, &dir, &path);
+ }
+
+ ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1);
+
+ entry->escape = 2 * ngx_escape_uri(NULL, ngx_de_name(&dir), len,
+ NGX_ESCAPE_URI_COMPONENT);
+
+ entry->escape_html = ngx_escape_html(NULL, entry->name.data,
+ entry->name.len);
+
+ if (utf8) {
+ entry->utf_len = ngx_utf8_length(entry->name.data, entry->name.len);
+ } else {
+ entry->utf_len = len;
+ }
+
+ entry->dir = ngx_de_is_dir(&dir);
+ entry->mtime = ngx_de_mtime(&dir);
+ entry->size = ngx_de_size(&dir);
+ }
+
+ if (ngx_close_dir(&dir) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+ ngx_close_dir_n " \"%V\" failed", &path);
+ }
+
+ escape_html = ngx_escape_html(NULL, r->uri.data, r->uri.len);
+
+ len = sizeof(title) - 1
+ + r->uri.len + escape_html
+ + sizeof(header) - 1
+ + r->uri.len + escape_html
+ + sizeof("</h1>") - 1
+ + sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1
+ + sizeof("</pre><hr>") - 1
+ + sizeof(tail) - 1;
+
+ entry = entries.elts;
+ for (i = 0; i < entries.nelts; i++) {
+ len += sizeof("<a href=\"") - 1
+ + entry[i].name.len + entry[i].escape
+ + 1 /* 1 is for "/" */
+ + sizeof("\">") - 1
+ + entry[i].name.len - entry[i].utf_len
+ + entry[i].escape_html
+ + NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof("&gt;") - 2
+ + sizeof("</a>") - 1
+ + sizeof(" 28-Sep-1970 12:00 ") - 1
+ + 20 /* the file size */
+ + 2;
+ }
+
+ b = ngx_create_temp_buf(r->pool, len);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (entries.nelts > 1) {
+ ngx_qsort(entry, (size_t) entries.nelts,
+ sizeof(ngx_http_autoindex_entry_t),
+ ngx_http_autoindex_cmp_entries);
+ }
+
+ b->last = ngx_cpymem(b->last, title, sizeof(title) - 1);
+
+ if (escape_html) {
+ b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len);
+ b->last = ngx_cpymem(b->last, header, sizeof(header) - 1);
+ b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len);
+
+ } else {
+ b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);
+ b->last = ngx_cpymem(b->last, header, sizeof(header) - 1);
+ b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);
+ }
+
+ b->last = ngx_cpymem(b->last, "</h1>", sizeof("</h1>") - 1);
+
+ b->last = ngx_cpymem(b->last, "<hr><pre><a href=\"../\">../</a>" CRLF,
+ sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1);
+
+ tp = ngx_timeofday();
+
+ for (i = 0; i < entries.nelts; i++) {
+ b->last = ngx_cpymem(b->last, "<a href=\"", sizeof("<a href=\"") - 1);
+
+ if (entry[i].escape) {
+ ngx_escape_uri(b->last, entry[i].name.data, entry[i].name.len,
+ NGX_ESCAPE_URI_COMPONENT);
+
+ b->last += entry[i].name.len + entry[i].escape;
+
+ } else {
+ b->last = ngx_cpymem(b->last, entry[i].name.data,
+ entry[i].name.len);
+ }
+
+ if (entry[i].dir) {
+ *b->last++ = '/';
+ }
+
+ *b->last++ = '"';
+ *b->last++ = '>';
+
+ len = entry[i].utf_len;
+
+ if (entry[i].name.len != len) {
+ if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
+ char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;
+
+ } else {
+ char_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;
+ }
+
+ last = b->last;
+ b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data,
+ char_len, entry[i].name.len + 1);
+
+ if (entry[i].escape_html) {
+ b->last = (u_char *) ngx_escape_html(last, entry[i].name.data,
+ b->last - last);
+ }
+
+ last = b->last;
+
+ } else {
+ if (entry[i].escape_html) {
+ if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
+ char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3;
+
+ } else {
+ char_len = len;
+ }
+
+ b->last = (u_char *) ngx_escape_html(b->last,
+ entry[i].name.data, char_len);
+ last = b->last;
+
+ } else {
+ b->last = ngx_cpystrn(b->last, entry[i].name.data,
+ NGX_HTTP_AUTOINDEX_NAME_LEN + 1);
+ last = b->last - 3;
+ }
+ }
+
+ if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
+ b->last = ngx_cpymem(last, "..&gt;</a>", sizeof("..&gt;</a>") - 1);
+
+ } else {
+ if (entry[i].dir && NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {
+ *b->last++ = '/';
+ len++;
+ }
+
+ b->last = ngx_cpymem(b->last, "</a>", sizeof("</a>") - 1);
+
+ if (NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {
+ ngx_memset(b->last, ' ', NGX_HTTP_AUTOINDEX_NAME_LEN - len);
+ b->last += NGX_HTTP_AUTOINDEX_NAME_LEN - len;
+ }
+ }
+
+ *b->last++ = ' ';
+
+ ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm);
+
+ b->last = ngx_sprintf(b->last, "%02d-%s-%d %02d:%02d ",
+ tm.ngx_tm_mday,
+ months[tm.ngx_tm_mon - 1],
+ tm.ngx_tm_year,
+ tm.ngx_tm_hour,
+ tm.ngx_tm_min);
+
+ if (alcf->exact_size) {
+ if (entry[i].dir) {
+ b->last = ngx_cpymem(b->last, " -",
+ sizeof(" -") - 1);
+ } else {
+ b->last = ngx_sprintf(b->last, "%19O", entry[i].size);
+ }
+
+ } else {
+ if (entry[i].dir) {
+ b->last = ngx_cpymem(b->last, " -",
+ sizeof(" -") - 1);
+
+ } else {
+ length = entry[i].size;
+
+ if (length > 1024 * 1024 * 1024 - 1) {
+ size = (ngx_int_t) (length / (1024 * 1024 * 1024));
+ if ((length % (1024 * 1024 * 1024))
+ > (1024 * 1024 * 1024 / 2 - 1))
+ {
+ size++;
+ }
+ scale = 'G';
+
+ } else if (length > 1024 * 1024 - 1) {
+ size = (ngx_int_t) (length / (1024 * 1024));
+ if ((length % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) {
+ size++;
+ }
+ scale = 'M';
+
+ } else if (length > 9999) {
+ size = (ngx_int_t) (length / 1024);
+ if (length % 1024 > 511) {
+ size++;
+ }
+ scale = 'K';
+
+ } else {
+ size = (ngx_int_t) length;
+ scale = '\0';
+ }
+
+ if (scale) {
+ b->last = ngx_sprintf(b->last, "%6i%c", size, scale);
+
+ } else {
+ b->last = ngx_sprintf(b->last, " %6i", size);
+ }
+ }
+ }
+
+ *b->last++ = CR;
+ *b->last++ = LF;
+ }
+
+ /* TODO: free temporary pool */
+
+ b->last = ngx_cpymem(b->last, "</pre><hr>", sizeof("</pre><hr>") - 1);
+
+ b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);
+
+ if (r == r->main) {
+ b->last_buf = 1;
+ }
+
+ b->last_in_chain = 1;
+
+ out.buf = b;
+ out.next = NULL;
+
+ return ngx_http_output_filter(r, &out);
+}
+
+
+static int ngx_libc_cdecl
+ngx_http_autoindex_cmp_entries(const void *one, const void *two)
+{
+ ngx_http_autoindex_entry_t *first = (ngx_http_autoindex_entry_t *) one;
+ ngx_http_autoindex_entry_t *second = (ngx_http_autoindex_entry_t *) two;
+
+ if (first->dir && !second->dir) {
+ /* move the directories to the start */
+ return -1;
+ }
+
+ if (!first->dir && second->dir) {
+ /* move the directories to the start */
+ return 1;
+ }
+
+ return (int) ngx_strcmp(first->name.data, second->name.data);
+}
+
+
+#if 0
+
+static ngx_buf_t *
+ngx_http_autoindex_alloc(ngx_http_autoindex_ctx_t *ctx, size_t size)
+{
+ ngx_chain_t *cl;
+
+ if (ctx->buf) {
+
+ if ((size_t) (ctx->buf->end - ctx->buf->last) >= size) {
+ return ctx->buf;
+ }
+
+ ctx->size += ctx->buf->last - ctx->buf->pos;
+ }
+
+ ctx->buf = ngx_create_temp_buf(ctx->pool, ctx->alloc_size);
+ if (ctx->buf == NULL) {
+ return NULL;
+ }
+
+ cl = ngx_alloc_chain_link(ctx->pool);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ cl->buf = ctx->buf;
+ cl->next = NULL;
+
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ return ctx->buf;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_autoindex_error(ngx_http_request_t *r, ngx_dir_t *dir, ngx_str_t *name)
+{
+ if (ngx_close_dir(dir) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+ ngx_close_dir_n " \"%V\" failed", name);
+ }
+
+ return r->header_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR;
+}
+
+
+static void *
+ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_autoindex_loc_conf_t *conf;
+
+ conf = ngx_palloc(cf->pool, sizeof(ngx_http_autoindex_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->enable = NGX_CONF_UNSET;
+ conf->localtime = NGX_CONF_UNSET;
+ conf->exact_size = NGX_CONF_UNSET;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_autoindex_loc_conf_t *prev = parent;
+ ngx_http_autoindex_loc_conf_t *conf = child;
+
+ ngx_conf_merge_value(conf->enable, prev->enable, 0);
+ ngx_conf_merge_value(conf->localtime, prev->localtime, 0);
+ ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1);
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_autoindex_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_autoindex_handler;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_browser_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_browser_module.c
new file mode 100644
index 00000000000..80da0d8fa86
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_browser_module.c
@@ -0,0 +1,715 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+/*
+ * The module can check browser versions conforming to the following formats:
+ * X, X.X, X.X.X, and X.X.X.X. The maximum values of each format may be
+ * 4000, 4000.99, 4000.99.99, and 4000.99.99.99.
+ */
+
+
+#define NGX_HTTP_MODERN_BROWSER 0
+#define NGX_HTTP_ANCIENT_BROWSER 1
+
+
+typedef struct {
+ u_char browser[12];
+ size_t skip;
+ size_t add;
+ u_char name[12];
+} ngx_http_modern_browser_mask_t;
+
+
+typedef struct {
+ ngx_uint_t version;
+ size_t skip;
+ size_t add;
+ u_char name[12];
+} ngx_http_modern_browser_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_http_get_variable_pt handler;
+ uintptr_t data;
+} ngx_http_browser_variable_t;
+
+
+typedef struct {
+ ngx_array_t *modern_browsers;
+ ngx_array_t *ancient_browsers;
+ ngx_http_variable_value_t *modern_browser_value;
+ ngx_http_variable_value_t *ancient_browser_value;
+
+ unsigned modern_unlisted_browsers:1;
+ unsigned netscape4:1;
+} ngx_http_browser_conf_t;
+
+
+static ngx_int_t ngx_http_msie_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_browser_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_uint_t ngx_http_browser(ngx_http_request_t *r,
+ ngx_http_browser_conf_t *cf);
+
+static ngx_int_t ngx_http_browser_add_variable(ngx_conf_t *cf);
+static void *ngx_http_browser_create_conf(ngx_conf_t *cf);
+static char *ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static int ngx_libc_cdecl ngx_http_modern_browser_sort(const void *one,
+ const void *two);
+static char *ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_http_browser_commands[] = {
+
+ { ngx_string("modern_browser"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
+ ngx_http_modern_browser,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("ancient_browser"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_ancient_browser,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("modern_browser_value"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_modern_browser_value,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("ancient_browser_value"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_ancient_browser_value,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_browser_module_ctx = {
+ ngx_http_browser_add_variable, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_browser_create_conf, /* create location configuration */
+ ngx_http_browser_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_browser_module = {
+ NGX_MODULE_V1,
+ &ngx_http_browser_module_ctx, /* module context */
+ ngx_http_browser_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_modern_browser_mask_t ngx_http_modern_browser_masks[] = {
+
+ /* Opera must be the first browser to check */
+
+ /*
+ * "Opera/7.50 (X11; FreeBSD i386; U) [en]"
+ * "Mozilla/5.0 (X11; FreeBSD i386; U) Opera 7.50 [en]"
+ * "Mozilla/4.0 (compatible; MSIE 6.0; X11; FreeBSD i386) Opera 7.50 [en]"
+ * "Opera/8.0 (Windows NT 5.1; U; ru)"
+ * "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.0"
+ * "Opera/9.01 (X11; FreeBSD 6 i386; U; en)"
+ */
+
+ { "opera",
+ 0,
+ sizeof("Opera ") - 1,
+ "Opera"},
+
+ /* "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)" */
+
+ { "msie",
+ sizeof("Mozilla/4.0 (compatible; ") - 1,
+ sizeof("MSIE ") - 1,
+ "MSIE "},
+
+ /*
+ * "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.0) Gecko/20020610"
+ * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.5) Gecko/20031006"
+ * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.6) Gecko/20040206
+ * Firefox/0.8"
+ * "Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.7.8)
+ * Gecko/20050511 Firefox/1.0.4"
+ * "Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.0.5) Gecko/20060729
+ * Firefox/1.5.0.5"
+ */
+
+ { "gecko",
+ sizeof("Mozilla/5.0 (") - 1,
+ sizeof("rv:") - 1,
+ "rv:"},
+
+ /*
+ * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/125.2
+ * (KHTML, like Gecko) Safari/125.7"
+ * "Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413
+ * (KHTML, like Gecko) Safari/413"
+ * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418
+ * (KHTML, like Gecko) Safari/417.9.3"
+ * "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/418.8
+ * (KHTML, like Gecko) Safari/419.3"
+ */
+
+ { "safari",
+ sizeof("Mozilla/5.0 (") - 1,
+ sizeof("Safari/") - 1,
+ "Safari/"},
+
+ /*
+ * "Mozilla/5.0 (compatible; Konqueror/3.1; Linux)"
+ * "Mozilla/5.0 (compatible; Konqueror/3.4; Linux) KHTML/3.4.2 (like Gecko)"
+ * "Mozilla/5.0 (compatible; Konqueror/3.5; FreeBSD) KHTML/3.5.1
+ * (like Gecko)"
+ */
+
+ { "konqueror",
+ sizeof("Mozilla/5.0 (compatible; ") - 1,
+ sizeof("Konqueror/") - 1,
+ "Konqueror/"},
+
+ { "", 0, 0, "" }
+
+};
+
+
+static ngx_http_browser_variable_t ngx_http_browsers[] = {
+ { ngx_string("msie"), ngx_http_msie_variable, 0 },
+ { ngx_string("modern_browser"), ngx_http_browser_variable,
+ NGX_HTTP_MODERN_BROWSER },
+ { ngx_string("ancient_browser"), ngx_http_browser_variable,
+ NGX_HTTP_ANCIENT_BROWSER },
+ { ngx_null_string, NULL, 0 }
+};
+
+
+static ngx_int_t
+ngx_http_browser_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_uint_t rc;
+ ngx_http_browser_conf_t *cf;
+
+ cf = ngx_http_get_module_loc_conf(r, ngx_http_browser_module);
+
+ rc = ngx_http_browser(r, cf);
+
+ if (data == NGX_HTTP_MODERN_BROWSER && rc == NGX_HTTP_MODERN_BROWSER) {
+ *v = *cf->modern_browser_value;
+ return NGX_OK;
+ }
+
+ if (data == NGX_HTTP_ANCIENT_BROWSER && rc == NGX_HTTP_ANCIENT_BROWSER) {
+ *v = *cf->ancient_browser_value;
+ return NGX_OK;
+ }
+
+ *v = ngx_http_variable_null_value;
+ return NGX_OK;
+}
+
+
+static ngx_uint_t
+ngx_http_browser(ngx_http_request_t *r, ngx_http_browser_conf_t *cf)
+{
+ size_t len;
+ u_char *name, *ua, *last, c;
+ ngx_str_t *ancient;
+ ngx_uint_t i, version, ver, scale;
+ ngx_http_modern_browser_t *modern;
+
+ if (r->headers_in.user_agent == NULL) {
+ if (cf->modern_unlisted_browsers) {
+ return NGX_HTTP_MODERN_BROWSER;
+ }
+
+ return NGX_HTTP_ANCIENT_BROWSER;
+ }
+
+ ua = r->headers_in.user_agent->value.data;
+ len = r->headers_in.user_agent->value.len;
+ last = ua + len;
+
+ if (cf->modern_browsers) {
+ modern = cf->modern_browsers->elts;
+
+ for (i = 0; i < cf->modern_browsers->nelts; i++) {
+ name = ua + modern[i].skip;
+
+ if (name >= last) {
+ continue;
+ }
+
+ name = (u_char *) ngx_strstr(name, modern[i].name);
+
+ if (name == NULL) {
+ continue;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "browser: \"%s\"", name);
+
+ name += modern[i].add;
+
+ if (name >= last) {
+ continue;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "version: \"%ui\" \"%s\"", modern[i].version, name);
+
+ version = 0;
+ ver = 0;
+ scale = 1000000;
+
+ while (name < last) {
+
+ c = *name++;
+
+ if (c >= '0' && c <= '9') {
+ ver = ver * 10 + (c - '0');
+ continue;
+ }
+
+ if (c == '.') {
+ version += ver * scale;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "version: \"%ui\" \"%ui\"",
+ modern[i].version, version);
+
+ if (version > modern[i].version) {
+ return NGX_HTTP_MODERN_BROWSER;
+ }
+
+ ver = 0;
+ scale /= 100;
+ continue;
+ }
+
+ break;
+ }
+
+ version += ver * scale;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "version: \"%ui\" \"%ui\"",
+ modern[i].version, version);
+
+ if (version >= modern[i].version) {
+ return NGX_HTTP_MODERN_BROWSER;
+ }
+
+ return NGX_HTTP_ANCIENT_BROWSER;
+ }
+
+ if (!cf->modern_unlisted_browsers) {
+ return NGX_HTTP_ANCIENT_BROWSER;
+ }
+ }
+
+ if (cf->netscape4) {
+ if (len > sizeof("Mozilla/4.72 ") - 1
+ && ngx_strncmp(ua, "Mozilla/", sizeof("Mozilla/") - 1) == 0
+ && ua[8] > '0' && ua[8] < '5')
+ {
+ return NGX_HTTP_ANCIENT_BROWSER;
+ }
+ }
+
+ if (cf->ancient_browsers) {
+ ancient = cf->ancient_browsers->elts;
+
+ for (i = 0; i < cf->ancient_browsers->nelts; i++) {
+ if (len >= ancient[i].len
+ && ngx_strstr(ua, ancient[i].data) != NULL)
+ {
+ return NGX_HTTP_ANCIENT_BROWSER;
+ }
+ }
+ }
+
+ if (cf->modern_unlisted_browsers) {
+ return NGX_HTTP_MODERN_BROWSER;
+ }
+
+ return NGX_HTTP_ANCIENT_BROWSER;
+}
+
+
+static ngx_int_t
+ngx_http_msie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ if (r->headers_in.msie) {
+ *v = ngx_http_variable_true_value;
+ return NGX_OK;
+ }
+
+ *v = ngx_http_variable_null_value;
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_browser_add_variable(ngx_conf_t *cf)
+{
+ ngx_http_browser_variable_t *var;
+ ngx_http_variable_t *v;
+
+ for (var = ngx_http_browsers; var->name.len; var++) {
+
+ v = ngx_http_add_variable(cf, &var->name, NGX_HTTP_VAR_CHANGEABLE);
+ if (v == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->get_handler = var->handler;
+ v->data = var->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_browser_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_browser_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_browser_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->modern_browsers = NULL;
+ * conf->ancient_browsers = NULL;
+ * conf->modern_browser_value = NULL;
+ * conf->ancient_browser_value = NULL;
+ *
+ * conf->modern_unlisted_browsers = 0;
+ * conf->netscape4 = 0;
+ */
+
+ return conf;
+}
+
+
+static char *
+ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_browser_conf_t *prev = parent;
+ ngx_http_browser_conf_t *conf = child;
+
+ ngx_uint_t i, n;
+ ngx_http_modern_browser_t *browsers, *opera;
+
+ /*
+ * At the merge the skip field is used to store the browser slot,
+ * it will be used in sorting and then will overwritten
+ * with a real skip value. The zero value means Opera.
+ */
+
+ if (conf->modern_browsers == NULL && conf->modern_unlisted_browsers == 0) {
+ conf->modern_browsers = prev->modern_browsers;
+ conf->modern_unlisted_browsers = prev->modern_unlisted_browsers;
+
+ } else if (conf->modern_browsers != NULL) {
+ browsers = conf->modern_browsers->elts;
+
+ for (i = 0; i < conf->modern_browsers->nelts; i++) {
+ if (browsers[i].skip == 0) {
+ goto found;
+ }
+ }
+
+ /*
+ * Opera may contain MSIE string, so if Opera was not enumerated
+ * as modern browsers, then add it and set a unreachable version
+ */
+
+ opera = ngx_array_push(conf->modern_browsers);
+ if (opera == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ opera->skip = 0;
+ opera->version = 4001000000U;
+
+ browsers = conf->modern_browsers->elts;
+
+found:
+
+ ngx_qsort(browsers, (size_t) conf->modern_browsers->nelts,
+ sizeof(ngx_http_modern_browser_t),
+ ngx_http_modern_browser_sort);
+
+ for (i = 0; i < conf->modern_browsers->nelts; i++) {
+ n = browsers[i].skip;
+
+ browsers[i].skip = ngx_http_modern_browser_masks[n].skip;
+ browsers[i].add = ngx_http_modern_browser_masks[n].add;
+ (void) ngx_cpystrn(browsers[i].name,
+ ngx_http_modern_browser_masks[n].name, 12);
+ }
+ }
+
+ if (conf->ancient_browsers == NULL && conf->netscape4 == 0) {
+ conf->ancient_browsers = prev->ancient_browsers;
+ conf->netscape4 = prev->netscape4;
+ }
+
+ if (conf->modern_browser_value == NULL) {
+ conf->modern_browser_value = prev->modern_browser_value;
+ }
+
+ if (conf->modern_browser_value == NULL) {
+ conf->modern_browser_value = &ngx_http_variable_true_value;
+ }
+
+ if (conf->ancient_browser_value == NULL) {
+ conf->ancient_browser_value = prev->ancient_browser_value;
+ }
+
+ if (conf->ancient_browser_value == NULL) {
+ conf->ancient_browser_value = &ngx_http_variable_true_value;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static int ngx_libc_cdecl
+ngx_http_modern_browser_sort(const void *one, const void *two)
+{
+ ngx_http_modern_browser_t *first = (ngx_http_modern_browser_t *) one;
+ ngx_http_modern_browser_t *second = (ngx_http_modern_browser_t *) two;
+
+ return (first->skip - second->skip);
+}
+
+
+static char *
+ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_browser_conf_t *bcf = conf;
+
+ u_char c;
+ ngx_str_t *value;
+ ngx_uint_t i, n, version, ver, scale;
+ ngx_http_modern_browser_t *browser;
+ ngx_http_modern_browser_mask_t *mask;
+
+ value = cf->args->elts;
+
+ if (cf->args->nelts == 2) {
+ if (ngx_strcmp(value[1].data, "unlisted") == 0) {
+ bcf->modern_unlisted_browsers = 1;
+ return NGX_CONF_OK;
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (bcf->modern_browsers == NULL) {
+ bcf->modern_browsers = ngx_array_create(cf->pool, 5,
+ sizeof(ngx_http_modern_browser_t));
+ if (bcf->modern_browsers == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ browser = ngx_array_push(bcf->modern_browsers);
+ if (browser == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ mask = ngx_http_modern_browser_masks;
+
+ for (n = 0; mask[n].browser[0] != '\0'; n++) {
+ if (ngx_strcasecmp(mask[n].browser, value[1].data) == 0) {
+ goto found;
+ }
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unknown browser name \"%V\"", &value[1]);
+
+ return NGX_CONF_ERROR;
+
+found:
+
+ /*
+ * at this stage the skip field is used to store the browser slot,
+ * it will be used in sorting in merge stage and then will overwritten
+ * with a real value
+ */
+
+ browser->skip = n;
+
+ version = 0;
+ ver = 0;
+ scale = 1000000;
+
+ for (i = 0; i < value[2].len; i++) {
+
+ c = value[2].data[i];
+
+ if (c >= '0' && c <= '9') {
+ ver = ver * 10 + (c - '0');
+ continue;
+ }
+
+ if (c == '.') {
+ version += ver * scale;
+ ver = 0;
+ scale /= 100;
+ continue;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid browser version \"%V\"", &value[2]);
+
+ return NGX_CONF_ERROR;
+ }
+
+ version += ver * scale;
+
+ browser->version = version;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_browser_conf_t *bcf = conf;
+
+ ngx_str_t *value, *browser;
+ ngx_uint_t i;
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+ if (ngx_strcmp(value[i].data, "netscape4") == 0) {
+ bcf->netscape4 = 1;
+ continue;
+ }
+
+ if (bcf->ancient_browsers == NULL) {
+ bcf->ancient_browsers = ngx_array_create(cf->pool, 4,
+ sizeof(ngx_str_t));
+ if (bcf->ancient_browsers == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ browser = ngx_array_push(bcf->ancient_browsers);
+ if (browser == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *browser = value[i];
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_browser_conf_t *bcf = conf;
+
+ ngx_str_t *value;
+
+ bcf->modern_browser_value = ngx_palloc(cf->pool,
+ sizeof(ngx_http_variable_value_t));
+ if (bcf->modern_browser_value == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ bcf->modern_browser_value->len = value[1].len;
+ bcf->modern_browser_value->valid = 1;
+ bcf->modern_browser_value->no_cacheable = 0;
+ bcf->modern_browser_value->not_found = 0;
+ bcf->modern_browser_value->data = value[1].data;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_browser_conf_t *bcf = conf;
+
+ ngx_str_t *value;
+
+ bcf->ancient_browser_value = ngx_palloc(cf->pool,
+ sizeof(ngx_http_variable_value_t));
+ if (bcf->ancient_browser_value == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ bcf->ancient_browser_value->len = value[1].len;
+ bcf->ancient_browser_value->valid = 1;
+ bcf->ancient_browser_value->no_cacheable = 0;
+ bcf->ancient_browser_value->not_found = 0;
+ bcf->ancient_browser_value->data = value[1].data;
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_charset_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_charset_filter_module.c
new file mode 100644
index 00000000000..4ea98184b26
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_charset_filter_module.c
@@ -0,0 +1,1685 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_CHARSET_OFF -2
+#define NGX_HTTP_NO_CHARSET -3
+#define NGX_HTTP_CHARSET_VAR 0x10000
+
+/* 1 byte length and up to 3 bytes for the UTF-8 encoding of the UCS-2 */
+#define NGX_UTF_LEN 4
+
+#define NGX_HTML_ENTITY_LEN (sizeof("&#1114111;") - 1)
+
+
+typedef struct {
+ u_char **tables;
+ ngx_str_t name;
+
+ unsigned length:16;
+ unsigned utf8:1;
+} ngx_http_charset_t;
+
+
+typedef struct {
+ ngx_int_t src;
+ ngx_int_t dst;
+} ngx_http_charset_recode_t;
+
+
+typedef struct {
+ ngx_int_t src;
+ ngx_int_t dst;
+ u_char *src2dst;
+ u_char *dst2src;
+} ngx_http_charset_tables_t;
+
+
+typedef struct {
+ ngx_array_t charsets; /* ngx_http_charset_t */
+ ngx_array_t tables; /* ngx_http_charset_tables_t */
+ ngx_array_t recodes; /* ngx_http_charset_recode_t */
+} ngx_http_charset_main_conf_t;
+
+
+typedef struct {
+ ngx_int_t charset;
+ ngx_int_t source_charset;
+ ngx_flag_t override_charset;
+
+ ngx_hash_t types;
+ ngx_array_t *types_keys;
+} ngx_http_charset_loc_conf_t;
+
+
+typedef struct {
+ u_char *table;
+ ngx_int_t charset;
+ ngx_str_t charset_name;
+
+ ngx_chain_t *busy;
+ ngx_chain_t *free_bufs;
+ ngx_chain_t *free_buffers;
+
+ size_t saved_len;
+ u_char saved[NGX_UTF_LEN];
+
+ unsigned length:16;
+ unsigned from_utf8:1;
+ unsigned to_utf8:1;
+} ngx_http_charset_ctx_t;
+
+
+typedef struct {
+ ngx_http_charset_tables_t *table;
+ ngx_http_charset_t *charset;
+ ngx_uint_t characters;
+} ngx_http_charset_conf_ctx_t;
+
+
+static ngx_int_t ngx_http_destination_charset(ngx_http_request_t *r,
+ ngx_str_t *name);
+static ngx_int_t ngx_http_main_request_charset(ngx_http_request_t *r,
+ ngx_str_t *name);
+static ngx_int_t ngx_http_source_charset(ngx_http_request_t *r,
+ ngx_str_t *name);
+static ngx_int_t ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name);
+static ngx_inline void ngx_http_set_charset(ngx_http_request_t *r,
+ ngx_str_t *charset);
+static ngx_int_t ngx_http_charset_ctx(ngx_http_request_t *r,
+ ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset);
+static ngx_uint_t ngx_http_charset_recode(ngx_buf_t *b, u_char *table);
+static ngx_chain_t *ngx_http_charset_recode_from_utf8(ngx_pool_t *pool,
+ ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);
+static ngx_chain_t *ngx_http_charset_recode_to_utf8(ngx_pool_t *pool,
+ ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);
+
+static ngx_chain_t *ngx_http_charset_get_buf(ngx_pool_t *pool,
+ ngx_http_charset_ctx_t *ctx);
+static ngx_chain_t *ngx_http_charset_get_buffer(ngx_pool_t *pool,
+ ngx_http_charset_ctx_t *ctx, size_t size);
+
+static char *ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy,
+ void *conf);
+
+static char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static ngx_int_t ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name);
+
+static void *ngx_http_charset_create_main_conf(ngx_conf_t *cf);
+static void *ngx_http_charset_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_charset_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static ngx_int_t ngx_http_charset_postconfiguration(ngx_conf_t *cf);
+
+
+ngx_str_t ngx_http_charset_default_types[] = {
+ ngx_string("text/html"),
+ ngx_string("text/xml"),
+ ngx_string("text/plain"),
+ ngx_string("text/vnd.wap.wml"),
+ ngx_string("application/javascript"),
+ ngx_string("application/rss+xml"),
+ ngx_null_string
+};
+
+
+static ngx_command_t ngx_http_charset_filter_commands[] = {
+
+ { ngx_string("charset"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
+ |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
+ ngx_http_set_charset_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_charset_loc_conf_t, charset),
+ NULL },
+
+ { ngx_string("source_charset"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
+ |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
+ ngx_http_set_charset_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_charset_loc_conf_t, source_charset),
+ NULL },
+
+ { ngx_string("override_charset"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
+ |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_charset_loc_conf_t, override_charset),
+ NULL },
+
+ { ngx_string("charset_types"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_types_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_charset_loc_conf_t, types_keys),
+ &ngx_http_charset_default_types[0] },
+
+ { ngx_string("charset_map"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
+ ngx_http_charset_map_block,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_charset_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_charset_postconfiguration, /* postconfiguration */
+
+ ngx_http_charset_create_main_conf, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_charset_create_loc_conf, /* create location configuration */
+ ngx_http_charset_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_charset_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_charset_filter_module_ctx, /* module context */
+ ngx_http_charset_filter_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_charset_header_filter(ngx_http_request_t *r)
+{
+ ngx_int_t charset, source_charset;
+ ngx_str_t dst, src;
+ ngx_http_charset_t *charsets;
+ ngx_http_charset_main_conf_t *mcf;
+
+ if (r == r->main) {
+ charset = ngx_http_destination_charset(r, &dst);
+
+ } else {
+ charset = ngx_http_main_request_charset(r, &dst);
+ }
+
+ if (charset == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (charset == NGX_DECLINED) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ /* charset: charset index or NGX_HTTP_NO_CHARSET */
+
+ source_charset = ngx_http_source_charset(r, &src);
+
+ if (source_charset == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ /*
+ * source_charset: charset index, NGX_HTTP_NO_CHARSET,
+ * or NGX_HTTP_CHARSET_OFF
+ */
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "charset: \"%V\" > \"%V\"", &src, &dst);
+
+ if (source_charset == NGX_HTTP_CHARSET_OFF) {
+ ngx_http_set_charset(r, &dst);
+
+ return ngx_http_next_header_filter(r);
+ }
+
+ if (charset == NGX_HTTP_NO_CHARSET
+ || source_charset == NGX_HTTP_NO_CHARSET)
+ {
+ if (source_charset != charset
+ || ngx_strncasecmp(dst.data, src.data, dst.len) != 0)
+ {
+ goto no_charset_map;
+ }
+
+ ngx_http_set_charset(r, &dst);
+
+ return ngx_http_next_header_filter(r);
+ }
+
+ if (source_charset == charset) {
+ r->headers_out.content_type.len = r->headers_out.content_type_len;
+
+ ngx_http_set_charset(r, &dst);
+
+ return ngx_http_next_header_filter(r);
+ }
+
+ /* source_charset != charset */
+
+ if (r->headers_out.content_encoding
+ && r->headers_out.content_encoding->value.len)
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
+ charsets = mcf->charsets.elts;
+
+ if (charsets[source_charset].tables == NULL
+ || charsets[source_charset].tables[charset] == NULL)
+ {
+ goto no_charset_map;
+ }
+
+ r->headers_out.content_type.len = r->headers_out.content_type_len;
+
+ ngx_http_set_charset(r, &dst);
+
+ return ngx_http_charset_ctx(r, charsets, charset, source_charset);
+
+no_charset_map:
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "no \"charset_map\" between the charsets \"%V\" and \"%V\"",
+ &src, &dst);
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_destination_charset(ngx_http_request_t *r, ngx_str_t *name)
+{
+ ngx_int_t charset;
+ ngx_http_charset_t *charsets;
+ ngx_http_variable_value_t *vv;
+ ngx_http_charset_loc_conf_t *mlcf;
+ ngx_http_charset_main_conf_t *mcf;
+
+ if (r->headers_out.content_type.len == 0) {
+ return NGX_DECLINED;
+ }
+
+ if (r->headers_out.override_charset
+ && r->headers_out.override_charset->len)
+ {
+ *name = *r->headers_out.override_charset;
+
+ charset = ngx_http_get_charset(r, name);
+
+ if (charset != NGX_HTTP_NO_CHARSET) {
+ return charset;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "unknown charset \"%V\" to override", name);
+
+ return NGX_DECLINED;
+ }
+
+ mlcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
+ charset = mlcf->charset;
+
+ if (charset == NGX_HTTP_CHARSET_OFF) {
+ return NGX_DECLINED;
+ }
+
+ if (r->headers_out.charset.len) {
+ if (mlcf->override_charset == 0) {
+ return NGX_DECLINED;
+ }
+
+ } else {
+ if (ngx_http_test_content_type(r, &mlcf->types) == NULL) {
+ return NGX_DECLINED;
+ }
+ }
+
+ if (charset < NGX_HTTP_CHARSET_VAR) {
+ mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
+ charsets = mcf->charsets.elts;
+ *name = charsets[charset].name;
+ return charset;
+ }
+
+ vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);
+
+ if (vv == NULL || vv->not_found) {
+ return NGX_ERROR;
+ }
+
+ name->len = vv->len;
+ name->data = vv->data;
+
+ return ngx_http_get_charset(r, name);
+}
+
+
+static ngx_int_t
+ngx_http_main_request_charset(ngx_http_request_t *r, ngx_str_t *src)
+{
+ ngx_int_t charset;
+ ngx_str_t *main_charset;
+ ngx_http_charset_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module);
+
+ if (ctx) {
+ *src = ctx->charset_name;
+ return ctx->charset;
+ }
+
+ main_charset = &r->main->headers_out.charset;
+
+ if (main_charset->len == 0) {
+ return NGX_DECLINED;
+ }
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module);
+
+ charset = ngx_http_get_charset(r, main_charset);
+
+ ctx->charset = charset;
+ ctx->charset_name = *main_charset;
+ *src = *main_charset;
+
+ return charset;
+}
+
+
+static ngx_int_t
+ngx_http_source_charset(ngx_http_request_t *r, ngx_str_t *name)
+{
+ ngx_int_t charset;
+ ngx_http_charset_t *charsets;
+ ngx_http_variable_value_t *vv;
+ ngx_http_charset_loc_conf_t *lcf;
+ ngx_http_charset_main_conf_t *mcf;
+
+ if (r->headers_out.charset.len) {
+ *name = r->headers_out.charset;
+ return ngx_http_get_charset(r, name);
+ }
+
+ lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
+
+ charset = lcf->source_charset;
+
+ if (charset == NGX_HTTP_CHARSET_OFF) {
+ name->len = 0;
+ return charset;
+ }
+
+ if (charset < NGX_HTTP_CHARSET_VAR) {
+ mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
+ charsets = mcf->charsets.elts;
+ *name = charsets[charset].name;
+ return charset;
+ }
+
+ vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);
+
+ if (vv == NULL || vv->not_found) {
+ return NGX_ERROR;
+ }
+
+ name->len = vv->len;
+ name->data = vv->data;
+
+ return ngx_http_get_charset(r, name);
+}
+
+
+static ngx_int_t
+ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name)
+{
+ ngx_uint_t i, n;
+ ngx_http_charset_t *charset;
+ ngx_http_charset_main_conf_t *mcf;
+
+ mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
+
+ charset = mcf->charsets.elts;
+ n = mcf->charsets.nelts;
+
+ for (i = 0; i < n; i++) {
+ if (charset[i].name.len != name->len) {
+ continue;
+ }
+
+ if (ngx_strncasecmp(charset[i].name.data, name->data, name->len) == 0) {
+ return i;
+ }
+ }
+
+ return NGX_HTTP_NO_CHARSET;
+}
+
+
+static ngx_inline void
+ngx_http_set_charset(ngx_http_request_t *r, ngx_str_t *charset)
+{
+ if (r != r->main) {
+ return;
+ }
+
+ if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY
+ || r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY)
+ {
+ /*
+ * do not set charset for the redirect because NN 4.x
+ * use this charset instead of the next page charset
+ */
+
+ r->headers_out.charset.len = 0;
+ return;
+ }
+
+ r->headers_out.charset = *charset;
+}
+
+
+static ngx_int_t
+ngx_http_charset_ctx(ngx_http_request_t *r, ngx_http_charset_t *charsets,
+ ngx_int_t charset, ngx_int_t source_charset)
+{
+ ngx_http_charset_ctx_t *ctx;
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_charset_filter_module);
+
+ ctx->table = charsets[source_charset].tables[charset];
+ ctx->charset = charset;
+ ctx->charset_name = charsets[charset].name;
+ ctx->length = charsets[charset].length;
+ ctx->from_utf8 = charsets[source_charset].utf8;
+ ctx->to_utf8 = charsets[charset].utf8;
+
+ r->filter_need_in_memory = 1;
+
+ if ((ctx->to_utf8 || ctx->from_utf8) && r == r->main) {
+ ngx_http_clear_content_length(r);
+
+ } else {
+ r->filter_need_temporary = 1;
+ }
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_charset_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl, *out, **ll;
+ ngx_http_charset_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_charset_filter_module);
+
+ if (ctx == NULL || ctx->table == NULL) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ if ((ctx->to_utf8 || ctx->from_utf8) || ctx->busy) {
+
+ out = NULL;
+ ll = &out;
+
+ for (cl = in; cl; cl = cl->next) {
+ b = cl->buf;
+
+ if (ngx_buf_size(b) == 0) {
+
+ *ll = ngx_alloc_chain_link(r->pool);
+ if (*ll == NULL) {
+ return NGX_ERROR;
+ }
+
+ (*ll)->buf = b;
+ (*ll)->next = NULL;
+
+ ll = &(*ll)->next;
+
+ continue;
+ }
+
+ if (ctx->to_utf8) {
+ *ll = ngx_http_charset_recode_to_utf8(r->pool, b, ctx);
+
+ } else {
+ *ll = ngx_http_charset_recode_from_utf8(r->pool, b, ctx);
+ }
+
+ if (*ll == NULL) {
+ return NGX_ERROR;
+ }
+
+ while (*ll) {
+ ll = &(*ll)->next;
+ }
+ }
+
+ rc = ngx_http_next_body_filter(r, out);
+
+ if (out) {
+ if (ctx->busy == NULL) {
+ ctx->busy = out;
+
+ } else {
+ for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
+ cl->next = out;
+ }
+ }
+
+ while (ctx->busy) {
+
+ cl = ctx->busy;
+ b = cl->buf;
+
+ if (ngx_buf_size(b) != 0) {
+ break;
+ }
+
+ ctx->busy = cl->next;
+
+ if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) {
+ continue;
+ }
+
+ if (b->shadow) {
+ b->shadow->pos = b->shadow->last;
+ }
+
+ if (b->pos) {
+ cl->next = ctx->free_buffers;
+ ctx->free_buffers = cl;
+ continue;
+ }
+
+ cl->next = ctx->free_bufs;
+ ctx->free_bufs = cl;
+ }
+
+ return rc;
+ }
+
+ for (cl = in; cl; cl = cl->next) {
+ (void) ngx_http_charset_recode(cl->buf, ctx->table);
+ }
+
+ return ngx_http_next_body_filter(r, in);
+}
+
+
+static ngx_uint_t
+ngx_http_charset_recode(ngx_buf_t *b, u_char *table)
+{
+ u_char *p, *last;
+
+ last = b->last;
+
+ for (p = b->pos; p < last; p++) {
+
+ if (*p != table[*p]) {
+ goto recode;
+ }
+ }
+
+ return 0;
+
+recode:
+
+ do {
+ if (*p != table[*p]) {
+ *p = table[*p];
+ }
+
+ p++;
+
+ } while (p < last);
+
+ b->in_file = 0;
+
+ return 1;
+}
+
+
+static ngx_chain_t *
+ngx_http_charset_recode_from_utf8(ngx_pool_t *pool, ngx_buf_t *buf,
+ ngx_http_charset_ctx_t *ctx)
+{
+ size_t len, size;
+ u_char c, *p, *src, *dst, *saved, **table;
+ uint32_t n;
+ ngx_buf_t *b;
+ ngx_uint_t i;
+ ngx_chain_t *out, *cl, **ll;
+
+ src = buf->pos;
+
+ if (ctx->saved_len == 0) {
+
+ for ( /* void */ ; src < buf->last; src++) {
+
+ if (*src < 0x80) {
+ continue;
+ }
+
+ len = src - buf->pos;
+
+ if (len > 512) {
+ out = ngx_http_charset_get_buf(pool, ctx);
+ if (out == NULL) {
+ return NULL;
+ }
+
+ b = out->buf;
+
+ b->temporary = buf->temporary;
+ b->memory = buf->memory;
+ b->mmap = buf->mmap;
+ b->flush = buf->flush;
+
+ b->pos = buf->pos;
+ b->last = src;
+
+ out->buf = b;
+ out->next = NULL;
+
+ size = buf->last - src;
+
+ saved = src;
+ n = ngx_utf8_decode(&saved, size);
+
+ if (n == 0xfffffffe) {
+ /* incomplete UTF-8 symbol */
+
+ ngx_memcpy(ctx->saved, src, size);
+ ctx->saved_len = size;
+
+ b->shadow = buf;
+
+ return out;
+ }
+
+ } else {
+ out = NULL;
+ size = len + buf->last - src;
+ src = buf->pos;
+ }
+
+ if (size < NGX_HTML_ENTITY_LEN) {
+ size += NGX_HTML_ENTITY_LEN;
+ }
+
+ cl = ngx_http_charset_get_buffer(pool, ctx, size);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ if (out) {
+ out->next = cl;
+
+ } else {
+ out = cl;
+ }
+
+ b = cl->buf;
+ dst = b->pos;
+
+ goto recode;
+ }
+
+ out = ngx_alloc_chain_link(pool);
+ if (out == NULL) {
+ return NULL;
+ }
+
+ out->buf = buf;
+ out->next = NULL;
+
+ return out;
+ }
+
+ /* process incomplete UTF sequence from previous buffer */
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0,
+ "http charset utf saved: %z", ctx->saved_len);
+
+ p = src;
+
+ for (i = ctx->saved_len; i < NGX_UTF_LEN; i++) {
+ ctx->saved[i] = *p++;
+
+ if (p == buf->last) {
+ break;
+ }
+ }
+
+ saved = ctx->saved;
+ n = ngx_utf8_decode(&saved, i);
+
+ c = '\0';
+
+ if (n < 0x10000) {
+ table = (u_char **) ctx->table;
+ p = table[n >> 8];
+
+ if (p) {
+ c = p[n & 0xff];
+ }
+
+ } else if (n == 0xfffffffe) {
+
+ /* incomplete UTF-8 symbol */
+
+ if (i < NGX_UTF_LEN) {
+ out = ngx_http_charset_get_buf(pool, ctx);
+ if (out == NULL) {
+ return NULL;
+ }
+
+ b = out->buf;
+
+ b->pos = buf->pos;
+ b->last = buf->last;
+ b->sync = 1;
+ b->shadow = buf;
+
+ ngx_memcpy(&ctx->saved[ctx->saved_len], src, i);
+ ctx->saved_len += i;
+
+ return out;
+ }
+ }
+
+ size = buf->last - buf->pos;
+
+ if (size < NGX_HTML_ENTITY_LEN) {
+ size += NGX_HTML_ENTITY_LEN;
+ }
+
+ cl = ngx_http_charset_get_buffer(pool, ctx, size);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ out = cl;
+
+ b = cl->buf;
+ dst = b->pos;
+
+ if (c) {
+ *dst++ = c;
+
+ } else if (n == 0xfffffffe) {
+ *dst++ = '?';
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
+ "http charset invalid utf 0");
+
+ saved = &ctx->saved[NGX_UTF_LEN];
+
+ } else if (n > 0x10ffff) {
+ *dst++ = '?';
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
+ "http charset invalid utf 1");
+
+ } else {
+ dst = ngx_sprintf(dst, "&#%uD;", n);
+ }
+
+ src += (saved - ctx->saved) - ctx->saved_len;
+ ctx->saved_len = 0;
+
+recode:
+
+ ll = &cl->next;
+
+ table = (u_char **) ctx->table;
+
+ while (src < buf->last) {
+
+ if ((size_t) (b->end - dst) < NGX_HTML_ENTITY_LEN) {
+ b->last = dst;
+
+ size = buf->last - src + NGX_HTML_ENTITY_LEN;
+
+ cl = ngx_http_charset_get_buffer(pool, ctx, size);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ *ll = cl;
+ ll = &cl->next;
+
+ b = cl->buf;
+ dst = b->pos;
+ }
+
+ if (*src < 0x80) {
+ *dst++ = *src++;
+ continue;
+ }
+
+ len = buf->last - src;
+
+ n = ngx_utf8_decode(&src, len);
+
+ if (n < 0x10000) {
+
+ p = table[n >> 8];
+
+ if (p) {
+ c = p[n & 0xff];
+
+ if (c) {
+ *dst++ = c;
+ continue;
+ }
+ }
+
+ dst = ngx_sprintf(dst, "&#%uD;", n);
+
+ continue;
+ }
+
+ if (n == 0xfffffffe) {
+ /* incomplete UTF-8 symbol */
+
+ ngx_memcpy(ctx->saved, src, len);
+ ctx->saved_len = len;
+
+ if (b->pos == dst) {
+ b->sync = 1;
+ b->temporary = 0;
+ }
+
+ break;
+ }
+
+ if (n > 0x10ffff) {
+ *dst++ = '?';
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
+ "http charset invalid utf 2");
+
+ continue;
+ }
+
+ /* n > 0xffff */
+
+ dst = ngx_sprintf(dst, "&#%uD;", n);
+ }
+
+ b->last = dst;
+
+ b->last_buf = buf->last_buf;
+ b->last_in_chain = buf->last_in_chain;
+ b->flush = buf->flush;
+
+ b->shadow = buf;
+
+ return out;
+}
+
+
+static ngx_chain_t *
+ngx_http_charset_recode_to_utf8(ngx_pool_t *pool, ngx_buf_t *buf,
+ ngx_http_charset_ctx_t *ctx)
+{
+ size_t len, size;
+ u_char *p, *src, *dst, *table;
+ ngx_buf_t *b;
+ ngx_chain_t *out, *cl, **ll;
+
+ table = ctx->table;
+
+ for (src = buf->pos; src < buf->last; src++) {
+ if (table[*src * NGX_UTF_LEN] == '\1') {
+ continue;
+ }
+
+ goto recode;
+ }
+
+ out = ngx_alloc_chain_link(pool);
+ if (out == NULL) {
+ return NULL;
+ }
+
+ out->buf = buf;
+ out->next = NULL;
+
+ return out;
+
+recode:
+
+ /*
+ * we assume that there are about half of characters to be recoded,
+ * so we preallocate "size / 2 + size / 2 * ctx->length"
+ */
+
+ len = src - buf->pos;
+
+ if (len > 512) {
+ out = ngx_http_charset_get_buf(pool, ctx);
+ if (out == NULL) {
+ return NULL;
+ }
+
+ b = out->buf;
+
+ b->temporary = buf->temporary;
+ b->memory = buf->memory;
+ b->mmap = buf->mmap;
+ b->flush = buf->flush;
+
+ b->pos = buf->pos;
+ b->last = src;
+
+ out->buf = b;
+ out->next = NULL;
+
+ size = buf->last - src;
+ size = size / 2 + size / 2 * ctx->length;
+
+ } else {
+ out = NULL;
+
+ size = buf->last - src;
+ size = len + size / 2 + size / 2 * ctx->length;
+
+ src = buf->pos;
+ }
+
+ cl = ngx_http_charset_get_buffer(pool, ctx, size);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ if (out) {
+ out->next = cl;
+
+ } else {
+ out = cl;
+ }
+
+ ll = &cl->next;
+
+ b = cl->buf;
+ dst = b->pos;
+
+ while (src < buf->last) {
+
+ p = &table[*src++ * NGX_UTF_LEN];
+ len = *p++;
+
+ if ((size_t) (b->end - dst) < len) {
+ b->last = dst;
+
+ size = buf->last - src;
+ size = len + size / 2 + size / 2 * ctx->length;
+
+ cl = ngx_http_charset_get_buffer(pool, ctx, size);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ *ll = cl;
+ ll = &cl->next;
+
+ b = cl->buf;
+ dst = b->pos;
+ }
+
+ while (len) {
+ *dst++ = *p++;
+ len--;
+ }
+ }
+
+ b->last = dst;
+
+ b->last_buf = buf->last_buf;
+ b->last_in_chain = buf->last_in_chain;
+ b->flush = buf->flush;
+
+ b->shadow = buf;
+
+ return out;
+}
+
+
+static ngx_chain_t *
+ngx_http_charset_get_buf(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx)
+{
+ ngx_chain_t *cl;
+
+ cl = ctx->free_bufs;
+
+ if (cl) {
+ ctx->free_bufs = cl->next;
+
+ cl->buf->shadow = NULL;
+ cl->next = NULL;
+
+ return cl;
+ }
+
+ cl = ngx_alloc_chain_link(pool);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ cl->buf = ngx_calloc_buf(pool);
+ if (cl->buf == NULL) {
+ return NULL;
+ }
+
+ cl->next = NULL;
+
+ cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;
+
+ return cl;
+}
+
+
+static ngx_chain_t *
+ngx_http_charset_get_buffer(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx,
+ size_t size)
+{
+ ngx_buf_t *b;
+ ngx_chain_t *cl, **ll;
+
+ for (ll = &ctx->free_buffers, cl = ctx->free_buffers;
+ cl;
+ ll = &cl->next, cl = cl->next)
+ {
+ b = cl->buf;
+
+ if ((size_t) (b->end - b->start) >= size) {
+ *ll = cl->next;
+ cl->next = NULL;
+
+ b->pos = b->start;
+ b->temporary = 1;
+ b->shadow = NULL;
+
+ return cl;
+ }
+ }
+
+ cl = ngx_alloc_chain_link(pool);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ cl->buf = ngx_create_temp_buf(pool, size);
+ if (cl->buf == NULL) {
+ return NULL;
+ }
+
+ cl->next = NULL;
+
+ cl->buf->temporary = 1;
+ cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;
+
+ return cl;
+}
+
+
+static char *
+ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_charset_main_conf_t *mcf = conf;
+
+ char *rv;
+ u_char *p, *dst2src, **pp;
+ ngx_int_t src, dst;
+ ngx_uint_t i, n;
+ ngx_str_t *value;
+ ngx_conf_t pvcf;
+ ngx_http_charset_t *charset;
+ ngx_http_charset_tables_t *table;
+ ngx_http_charset_conf_ctx_t ctx;
+
+ value = cf->args->elts;
+
+ src = ngx_http_add_charset(&mcf->charsets, &value[1]);
+ if (src == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ dst = ngx_http_add_charset(&mcf->charsets, &value[2]);
+ if (dst == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (src == dst) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"charset_map\" between the same charsets "
+ "\"%V\" and \"%V\"", &value[1], &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ table = mcf->tables.elts;
+ for (i = 0; i < mcf->tables.nelts; i++) {
+ if ((src == table->src && dst == table->dst)
+ || (src == table->dst && dst == table->src))
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate \"charset_map\" between "
+ "\"%V\" and \"%V\"", &value[1], &value[2]);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ table = ngx_array_push(&mcf->tables);
+ if (table == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ table->src = src;
+ table->dst = dst;
+
+ if (ngx_strcasecmp(value[2].data, (u_char *) "utf-8") == 0) {
+ table->src2dst = ngx_pcalloc(cf->pool, 256 * NGX_UTF_LEN);
+ if (table->src2dst == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ table->dst2src = ngx_pcalloc(cf->pool, 256 * sizeof(void *));
+ if (table->dst2src == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ dst2src = ngx_pcalloc(cf->pool, 256);
+ if (dst2src == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ pp = (u_char **) &table->dst2src[0];
+ pp[0] = dst2src;
+
+ for (i = 0; i < 128; i++) {
+ p = &table->src2dst[i * NGX_UTF_LEN];
+ p[0] = '\1';
+ p[1] = (u_char) i;
+ dst2src[i] = (u_char) i;
+ }
+
+ for (/* void */; i < 256; i++) {
+ p = &table->src2dst[i * NGX_UTF_LEN];
+ p[0] = '\1';
+ p[1] = '?';
+ }
+
+ } else {
+ table->src2dst = ngx_palloc(cf->pool, 256);
+ if (table->src2dst == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ table->dst2src = ngx_palloc(cf->pool, 256);
+ if (table->dst2src == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (i = 0; i < 128; i++) {
+ table->src2dst[i] = (u_char) i;
+ table->dst2src[i] = (u_char) i;
+ }
+
+ for (/* void */; i < 256; i++) {
+ table->src2dst[i] = '?';
+ table->dst2src[i] = '?';
+ }
+ }
+
+ charset = mcf->charsets.elts;
+
+ ctx.table = table;
+ ctx.charset = &charset[dst];
+ ctx.characters = 0;
+
+ pvcf = *cf;
+ cf->ctx = &ctx;
+ cf->handler = ngx_http_charset_map;
+ cf->handler_conf = conf;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = pvcf;
+
+ if (ctx.characters) {
+ n = ctx.charset->length;
+ ctx.charset->length /= ctx.characters;
+
+ if (((n * 10) / ctx.characters) % 10 > 4) {
+ ctx.charset->length++;
+ }
+ }
+
+ return rv;
+}
+
+
+static char *
+ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
+{
+ u_char *p, *dst2src, **pp;
+ uint32_t n;
+ ngx_int_t src, dst;
+ ngx_str_t *value;
+ ngx_uint_t i;
+ ngx_http_charset_tables_t *table;
+ ngx_http_charset_conf_ctx_t *ctx;
+
+ if (cf->args->nelts != 2) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameters number");
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ src = ngx_hextoi(value[0].data, value[0].len);
+ if (src == NGX_ERROR || src > 255) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%V\"", &value[0]);
+ return NGX_CONF_ERROR;
+ }
+
+ ctx = cf->ctx;
+ table = ctx->table;
+
+ if (ctx->charset->utf8) {
+ p = &table->src2dst[src * NGX_UTF_LEN];
+
+ *p++ = (u_char) (value[1].len / 2);
+
+ for (i = 0; i < value[1].len; i += 2) {
+ dst = ngx_hextoi(&value[1].data[i], 2);
+ if (dst == NGX_ERROR || dst > 255) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ *p++ = (u_char) dst;
+ }
+
+ i /= 2;
+
+ ctx->charset->length += i;
+ ctx->characters++;
+
+ p = &table->src2dst[src * NGX_UTF_LEN] + 1;
+
+ n = ngx_utf8_decode(&p, i);
+
+ if (n > 0xffff) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ pp = (u_char **) &table->dst2src[0];
+
+ dst2src = pp[n >> 8];
+
+ if (dst2src == NULL) {
+ dst2src = ngx_pcalloc(cf->pool, 256);
+ if (dst2src == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ pp[n >> 8] = dst2src;
+ }
+
+ dst2src[n & 0xff] = (u_char) src;
+
+ } else {
+ dst = ngx_hextoi(value[1].data, value[1].len);
+ if (dst == NGX_ERROR || dst > 255) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ table->src2dst[src] = (u_char) dst;
+ table->dst2src[dst] = (u_char) src;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_int_t *cp;
+ ngx_str_t *value, var;
+ ngx_http_charset_main_conf_t *mcf;
+
+ cp = (ngx_int_t *) (p + cmd->offset);
+
+ if (*cp != NGX_CONF_UNSET) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, charset)
+ && ngx_strcmp(value[1].data, "off") == 0)
+ {
+ *cp = NGX_HTTP_CHARSET_OFF;
+ return NGX_CONF_OK;
+ }
+
+
+ if (value[1].data[0] == '$') {
+ var.len = value[1].len - 1;
+ var.data = value[1].data + 1;
+
+ *cp = ngx_http_get_variable_index(cf, &var);
+
+ if (*cp == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ *cp += NGX_HTTP_CHARSET_VAR;
+
+ return NGX_CONF_OK;
+ }
+
+ mcf = ngx_http_conf_get_module_main_conf(cf,
+ ngx_http_charset_filter_module);
+
+ *cp = ngx_http_add_charset(&mcf->charsets, &value[1]);
+ if (*cp == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name)
+{
+ ngx_uint_t i;
+ ngx_http_charset_t *c;
+
+ c = charsets->elts;
+ for (i = 0; i < charsets->nelts; i++) {
+ if (name->len != c[i].name.len) {
+ continue;
+ }
+
+ if (ngx_strcasecmp(name->data, c[i].name.data) == 0) {
+ break;
+ }
+ }
+
+ if (i < charsets->nelts) {
+ return i;
+ }
+
+ c = ngx_array_push(charsets);
+ if (c == NULL) {
+ return NGX_ERROR;
+ }
+
+ c->tables = NULL;
+ c->name = *name;
+ c->length = 0;
+
+ if (ngx_strcasecmp(name->data, (u_char *) "utf-8") == 0) {
+ c->utf8 = 1;
+
+ } else {
+ c->utf8 = 0;
+ }
+
+ return i;
+}
+
+
+static void *
+ngx_http_charset_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_http_charset_main_conf_t *mcf;
+
+ mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t));
+ if (mcf == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(&mcf->charsets, cf->pool, 2, sizeof(ngx_http_charset_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ if (ngx_array_init(&mcf->tables, cf->pool, 1,
+ sizeof(ngx_http_charset_tables_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ if (ngx_array_init(&mcf->recodes, cf->pool, 2,
+ sizeof(ngx_http_charset_recode_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return mcf;
+}
+
+
+static void *
+ngx_http_charset_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_charset_loc_conf_t *lcf;
+
+ lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t));
+ if (lcf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * lcf->types = { NULL };
+ * lcf->types_keys = NULL;
+ */
+
+ lcf->charset = NGX_CONF_UNSET;
+ lcf->source_charset = NGX_CONF_UNSET;
+ lcf->override_charset = NGX_CONF_UNSET;
+
+ return lcf;
+}
+
+
+static char *
+ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_charset_loc_conf_t *prev = parent;
+ ngx_http_charset_loc_conf_t *conf = child;
+
+ ngx_uint_t i;
+ ngx_http_charset_recode_t *recode;
+ ngx_http_charset_main_conf_t *mcf;
+
+ if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
+ &prev->types_keys, &prev->types,
+ ngx_http_charset_default_types)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_value(conf->override_charset, prev->override_charset, 0);
+ ngx_conf_merge_value(conf->charset, prev->charset, NGX_HTTP_CHARSET_OFF);
+ ngx_conf_merge_value(conf->source_charset, prev->source_charset,
+ NGX_HTTP_CHARSET_OFF);
+
+ if (conf->charset == NGX_HTTP_CHARSET_OFF
+ || conf->source_charset == NGX_HTTP_CHARSET_OFF
+ || conf->charset == conf->source_charset)
+ {
+ return NGX_CONF_OK;
+ }
+
+ if (conf->source_charset >= NGX_HTTP_CHARSET_VAR
+ || conf->charset >= NGX_HTTP_CHARSET_VAR)
+ {
+ return NGX_CONF_OK;
+ }
+
+ mcf = ngx_http_conf_get_module_main_conf(cf,
+ ngx_http_charset_filter_module);
+ recode = mcf->recodes.elts;
+ for (i = 0; i < mcf->recodes.nelts; i++) {
+ if (conf->source_charset == recode[i].src
+ && conf->charset == recode[i].dst)
+ {
+ return NGX_CONF_OK;
+ }
+ }
+
+ recode = ngx_array_push(&mcf->recodes);
+ if (recode == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ recode->src = conf->source_charset;
+ recode->dst = conf->charset;
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_charset_postconfiguration(ngx_conf_t *cf)
+{
+ u_char **src, **dst;
+ ngx_int_t c;
+ ngx_uint_t i, t;
+ ngx_http_charset_t *charset;
+ ngx_http_charset_recode_t *recode;
+ ngx_http_charset_tables_t *tables;
+ ngx_http_charset_main_conf_t *mcf;
+
+ mcf = ngx_http_conf_get_module_main_conf(cf,
+ ngx_http_charset_filter_module);
+
+ recode = mcf->recodes.elts;
+ tables = mcf->tables.elts;
+ charset = mcf->charsets.elts;
+
+ for (i = 0; i < mcf->recodes.nelts; i++) {
+
+ c = recode[i].src;
+
+ for (t = 0; t < mcf->tables.nelts; t++) {
+
+ if (c == tables[t].src && recode[i].dst == tables[t].dst) {
+ goto next;
+ }
+
+ if (c == tables[t].dst && recode[i].dst == tables[t].src) {
+ goto next;
+ }
+ }
+
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"charset_map\" between the charsets \"%V\" and \"%V\"",
+ &charset[c].name, &charset[recode[i].dst].name);
+ return NGX_ERROR;
+
+ next:
+ continue;
+ }
+
+
+ for (t = 0; t < mcf->tables.nelts; t++) {
+
+ src = charset[tables[t].src].tables;
+
+ if (src == NULL) {
+ src = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);
+ if (src == NULL) {
+ return NGX_ERROR;
+ }
+
+ charset[tables[t].src].tables = src;
+ }
+
+ dst = charset[tables[t].dst].tables;
+
+ if (dst == NULL) {
+ dst = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);
+ if (dst == NULL) {
+ return NGX_ERROR;
+ }
+
+ charset[tables[t].dst].tables = dst;
+ }
+
+ src[tables[t].dst] = tables[t].src2dst;
+ dst[tables[t].src] = tables[t].dst2src;
+ }
+
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_charset_header_filter;
+
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_charset_body_filter;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_chunked_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_chunked_filter_module.c
new file mode 100644
index 00000000000..a7dc5bf4d4b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_chunked_filter_module.c
@@ -0,0 +1,243 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_chain_t *free;
+ ngx_chain_t *busy;
+} ngx_http_chunked_filter_ctx_t;
+
+
+static ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf);
+
+
+static ngx_http_module_t ngx_http_chunked_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_chunked_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_chunked_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_chunked_filter_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_chunked_header_filter(ngx_http_request_t *r)
+{
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_chunked_filter_ctx_t *ctx;
+
+ if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED
+ || r->headers_out.status == NGX_HTTP_NO_CONTENT
+ || r->headers_out.status < NGX_HTTP_OK
+ || r != r->main
+ || (r->method & NGX_HTTP_HEAD))
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ if (r->headers_out.content_length_n == -1) {
+ if (r->http_version < NGX_HTTP_VERSION_11) {
+ r->keepalive = 0;
+
+ } else {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->chunked_transfer_encoding) {
+ r->chunked = 1;
+
+ ctx = ngx_pcalloc(r->pool,
+ sizeof(ngx_http_chunked_filter_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module);
+
+ } else {
+ r->keepalive = 0;
+ }
+ }
+ }
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ u_char *chunk;
+ off_t size;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t *out, *cl, *tl, **ll;
+ ngx_http_chunked_filter_ctx_t *ctx;
+
+ if (in == NULL || !r->chunked || r->header_only) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_chunked_filter_module);
+
+ out = NULL;
+ ll = &out;
+
+ size = 0;
+ cl = in;
+
+ for ( ;; ) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http chunk: %d", ngx_buf_size(cl->buf));
+
+ size += ngx_buf_size(cl->buf);
+
+ if (cl->buf->flush
+ || cl->buf->sync
+ || ngx_buf_in_memory(cl->buf)
+ || cl->buf->in_file)
+ {
+ tl = ngx_alloc_chain_link(r->pool);
+ if (tl == NULL) {
+ return NGX_ERROR;
+ }
+
+ tl->buf = cl->buf;
+ *ll = tl;
+ ll = &tl->next;
+ }
+
+ if (cl->next == NULL) {
+ break;
+ }
+
+ cl = cl->next;
+ }
+
+ if (size) {
+ tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (tl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = tl->buf;
+ chunk = b->start;
+
+ if (chunk == NULL) {
+ /* the "0000000000000000" is 64-bit hexadecimal string */
+
+ chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1);
+ if (chunk == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->start = chunk;
+ b->end = chunk + sizeof("0000000000000000" CRLF) - 1;
+ }
+
+ b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
+ b->memory = 0;
+ b->temporary = 1;
+ b->pos = chunk;
+ b->last = ngx_sprintf(chunk, "%xO" CRLF, size);
+
+ tl->next = out;
+ out = tl;
+ }
+
+ if (cl->buf->last_buf) {
+ tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (tl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = tl->buf;
+
+ b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
+ b->temporary = 0;
+ b->memory = 1;
+ b->last_buf = 1;
+ b->pos = (u_char *) CRLF "0" CRLF CRLF;
+ b->last = b->pos + 7;
+
+ cl->buf->last_buf = 0;
+
+ *ll = tl;
+
+ if (size == 0) {
+ b->pos += 2;
+ }
+
+ } else if (size > 0) {
+ tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (tl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = tl->buf;
+
+ b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;
+ b->temporary = 0;
+ b->memory = 1;
+ b->pos = (u_char *) CRLF;
+ b->last = b->pos + 2;
+
+ *ll = tl;
+
+ } else {
+ *ll = NULL;
+ }
+
+ rc = ngx_http_next_body_filter(r, out);
+
+ ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,
+ (ngx_buf_tag_t) &ngx_http_chunked_filter_module);
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_chunked_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_chunked_header_filter;
+
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_chunked_body_filter;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_dav_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_dav_module.c
new file mode 100644
index 00000000000..529aba53341
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_dav_module.c
@@ -0,0 +1,1156 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_DAV_OFF 2
+
+
+#define NGX_HTTP_DAV_NO_DEPTH -3
+#define NGX_HTTP_DAV_INVALID_DEPTH -2
+#define NGX_HTTP_DAV_INFINITY_DEPTH -1
+
+
+typedef struct {
+ ngx_uint_t methods;
+ ngx_uint_t access;
+ ngx_uint_t min_delete_depth;
+ ngx_flag_t create_full_put_path;
+} ngx_http_dav_loc_conf_t;
+
+
+typedef struct {
+ ngx_str_t path;
+ size_t len;
+} ngx_http_dav_copy_ctx_t;
+
+
+static ngx_int_t ngx_http_dav_handler(ngx_http_request_t *r);
+
+static void ngx_http_dav_put_handler(ngx_http_request_t *r);
+
+static ngx_int_t ngx_http_dav_delete_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_dav_delete_path(ngx_http_request_t *r,
+ ngx_str_t *path, ngx_uint_t dir);
+static ngx_int_t ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
+static ngx_int_t ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path);
+static ngx_int_t ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path);
+
+static ngx_int_t ngx_http_dav_mkcol_handler(ngx_http_request_t *r,
+ ngx_http_dav_loc_conf_t *dlcf);
+
+static ngx_int_t ngx_http_dav_copy_move_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);
+static ngx_int_t ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx,
+ ngx_str_t *path);
+static ngx_int_t ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx,
+ ngx_str_t *path);
+
+static ngx_int_t ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt);
+static ngx_int_t ngx_http_dav_error(ngx_log_t *log, ngx_err_t err,
+ ngx_int_t not_found, char *failed, u_char *path);
+static ngx_int_t ngx_http_dav_location(ngx_http_request_t *r, u_char *path);
+static void *ngx_http_dav_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_dav_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static ngx_int_t ngx_http_dav_init(ngx_conf_t *cf);
+
+
+static ngx_conf_bitmask_t ngx_http_dav_methods_mask[] = {
+ { ngx_string("off"), NGX_HTTP_DAV_OFF },
+ { ngx_string("put"), NGX_HTTP_PUT },
+ { ngx_string("delete"), NGX_HTTP_DELETE },
+ { ngx_string("mkcol"), NGX_HTTP_MKCOL },
+ { ngx_string("copy"), NGX_HTTP_COPY },
+ { ngx_string("move"), NGX_HTTP_MOVE },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_command_t ngx_http_dav_commands[] = {
+
+ { ngx_string("dav_methods"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_dav_loc_conf_t, methods),
+ &ngx_http_dav_methods_mask },
+
+ { ngx_string("create_full_put_path"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_dav_loc_conf_t, create_full_put_path),
+ NULL },
+
+ { ngx_string("min_delete_depth"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_dav_loc_conf_t, min_delete_depth),
+ NULL },
+
+ { ngx_string("dav_access"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
+ ngx_conf_set_access_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_dav_loc_conf_t, access),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_dav_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_dav_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_dav_create_loc_conf, /* create location configuration */
+ ngx_http_dav_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_dav_module = {
+ NGX_MODULE_V1,
+ &ngx_http_dav_module_ctx, /* module context */
+ ngx_http_dav_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_dav_handler(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_http_dav_loc_conf_t *dlcf;
+
+ dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
+
+ if (!(r->method & dlcf->methods)) {
+ return NGX_DECLINED;
+ }
+
+ switch (r->method) {
+
+ case NGX_HTTP_PUT:
+
+ if (r->uri.data[r->uri.len - 1] == '/') {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "cannot PUT to a collection");
+ return NGX_HTTP_CONFLICT;
+ }
+
+ r->request_body_in_file_only = 1;
+ r->request_body_in_persistent_file = 1;
+ r->request_body_in_clean_file = 1;
+ r->request_body_file_group_access = 1;
+ r->request_body_file_log_level = 0;
+
+ rc = ngx_http_read_client_request_body(r, ngx_http_dav_put_handler);
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ return rc;
+ }
+
+ return NGX_DONE;
+
+ case NGX_HTTP_DELETE:
+
+ return ngx_http_dav_delete_handler(r);
+
+ case NGX_HTTP_MKCOL:
+
+ return ngx_http_dav_mkcol_handler(r, dlcf);
+
+ case NGX_HTTP_COPY:
+
+ return ngx_http_dav_copy_move_handler(r);
+
+ case NGX_HTTP_MOVE:
+
+ return ngx_http_dav_copy_move_handler(r);
+ }
+
+ return NGX_DECLINED;
+}
+
+
+static void
+ngx_http_dav_put_handler(ngx_http_request_t *r)
+{
+ size_t root;
+ time_t date;
+ ngx_str_t *temp, path;
+ ngx_uint_t status;
+ ngx_file_info_t fi;
+ ngx_ext_rename_file_t ext;
+ ngx_http_dav_loc_conf_t *dlcf;
+
+ if (r->request_body == NULL || r->request_body->temp_file == NULL) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ path.len--;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http put filename: \"%s\"", path.data);
+
+ temp = &r->request_body->temp_file->file.name;
+
+ if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
+ status = NGX_HTTP_CREATED;
+
+ } else {
+ status = NGX_HTTP_NO_CONTENT;
+
+ if (ngx_is_dir(&fi)) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
+ "\"%s\" could not be created", path.data);
+
+ if (ngx_delete_file(temp->data) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+ ngx_delete_file_n " \"%s\" failed",
+ temp->data);
+ }
+
+ ngx_http_finalize_request(r, NGX_HTTP_CONFLICT);
+ return;
+ }
+ }
+
+ dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
+
+ ext.access = dlcf->access;
+ ext.path_access = dlcf->access;
+ ext.time = -1;
+ ext.create_path = dlcf->create_full_put_path;
+ ext.delete_file = 1;
+ ext.log = r->connection->log;
+
+ if (r->headers_in.date) {
+ date = ngx_http_parse_time(r->headers_in.date->value.data,
+ r->headers_in.date->value.len);
+
+ if (date != NGX_ERROR) {
+ ext.time = date;
+ ext.fd = r->request_body->temp_file->file.fd;
+ }
+ }
+
+ if (ngx_ext_rename_file(temp, &path, &ext) != NGX_OK) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (status == NGX_HTTP_CREATED) {
+ if (ngx_http_dav_location(r, path.data) != NGX_OK) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ r->headers_out.content_length_n = 0;
+ }
+
+ r->headers_out.status = status;
+ r->header_only = 1;
+
+ ngx_http_finalize_request(r, ngx_http_send_header(r));
+ return;
+}
+
+
+static ngx_int_t
+ngx_http_dav_delete_handler(ngx_http_request_t *r)
+{
+ size_t root;
+ ngx_err_t err;
+ ngx_int_t rc, depth;
+ ngx_uint_t i, d, dir;
+ ngx_str_t path;
+ ngx_file_info_t fi;
+ ngx_http_dav_loc_conf_t *dlcf;
+
+ if (r->headers_in.content_length_n > 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "DELETE with body is unsupported");
+ return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
+ }
+
+ dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
+
+ if (dlcf->min_delete_depth) {
+ d = 0;
+
+ for (i = 0; i < r->uri.len; /* void */) {
+ if (r->uri.data[i++] == '/') {
+ if (++d >= dlcf->min_delete_depth && i < r->uri.len) {
+ goto ok;
+ }
+ }
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "insufficient URI depth:%i to DELETE", d);
+ return NGX_HTTP_CONFLICT;
+ }
+
+ok:
+
+ if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http delete filename: \"%s\"", path.data);
+
+ if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
+ err = ngx_errno;
+
+ rc = (err == NGX_ENOTDIR) ? NGX_HTTP_CONFLICT : NGX_HTTP_NOT_FOUND;
+
+ return ngx_http_dav_error(r->connection->log, err,
+ rc, ngx_link_info_n, path.data);
+ }
+
+ if (ngx_is_dir(&fi)) {
+
+ if (r->uri.data[r->uri.len - 1] != '/') {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,
+ "DELETE \"%s\" failed", path.data);
+ return NGX_HTTP_CONFLICT;
+ }
+
+ depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
+
+ if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "\"Depth\" header must be infinity");
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ path.len -= 2; /* omit "/\0" */
+
+ dir = 1;
+
+ } else {
+
+ /*
+ * we do not need to test (r->uri.data[r->uri.len - 1] == '/')
+ * because ngx_link_info("/file/") returned NGX_ENOTDIR above
+ */
+
+ depth = ngx_http_dav_depth(r, 0);
+
+ if (depth != 0 && depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "\"Depth\" header must be 0 or infinity");
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ dir = 0;
+ }
+
+ rc = ngx_http_dav_delete_path(r, &path, dir);
+
+ if (rc == NGX_OK) {
+ return NGX_HTTP_NO_CONTENT;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_dav_delete_path(ngx_http_request_t *r, ngx_str_t *path, ngx_uint_t dir)
+{
+ char *failed;
+ ngx_tree_ctx_t tree;
+
+ if (dir) {
+
+ tree.init_handler = NULL;
+ tree.file_handler = ngx_http_dav_delete_file;
+ tree.pre_tree_handler = ngx_http_dav_noop;
+ tree.post_tree_handler = ngx_http_dav_delete_dir;
+ tree.spec_handler = ngx_http_dav_delete_file;
+ tree.data = NULL;
+ tree.alloc = 0;
+ tree.log = r->connection->log;
+
+ /* TODO: 207 */
+
+ if (ngx_walk_tree(&tree, path) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_delete_dir(path->data) != NGX_FILE_ERROR) {
+ return NGX_OK;
+ }
+
+ failed = ngx_delete_dir_n;
+
+ } else {
+
+ if (ngx_delete_file(path->data) != NGX_FILE_ERROR) {
+ return NGX_OK;
+ }
+
+ failed = ngx_delete_file_n;
+ }
+
+ return ngx_http_dav_error(r->connection->log, ngx_errno,
+ NGX_HTTP_NOT_FOUND, failed, path->data);
+}
+
+
+static ngx_int_t
+ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+ "http delete dir: \"%s\"", path->data);
+
+ if (ngx_delete_dir(path->data) == NGX_FILE_ERROR) {
+
+ /* TODO: add to 207 */
+
+ (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_dir_n,
+ path->data);
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+ "http delete file: \"%s\"", path->data);
+
+ if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
+
+ /* TODO: add to 207 */
+
+ (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_file_n,
+ path->data);
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_dav_mkcol_handler(ngx_http_request_t *r, ngx_http_dav_loc_conf_t *dlcf)
+{
+ u_char *p;
+ size_t root;
+ ngx_str_t path;
+
+ if (r->headers_in.content_length_n > 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "MKCOL with body is unsupported");
+ return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
+ }
+
+ if (r->uri.data[r->uri.len - 1] != '/') {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "MKCOL can create a collection only");
+ return NGX_HTTP_CONFLICT;
+ }
+
+ p = ngx_http_map_uri_to_path(r, &path, &root, 0);
+ if (p == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ *(p - 1) = '\0';
+ r->uri.len--;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http mkcol path: \"%s\"", path.data);
+
+ if (ngx_create_dir(path.data, ngx_dir_access(dlcf->access))
+ != NGX_FILE_ERROR)
+ {
+ if (ngx_http_dav_location(r, path.data) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ return NGX_HTTP_CREATED;
+ }
+
+ return ngx_http_dav_error(r->connection->log, ngx_errno,
+ NGX_HTTP_CONFLICT, ngx_create_dir_n, path.data);
+}
+
+
+static ngx_int_t
+ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
+{
+ u_char *p, *host, *last, ch;
+ size_t len, root;
+ ngx_err_t err;
+ ngx_int_t rc, depth;
+ ngx_uint_t overwrite, slash, dir, flags;
+ ngx_str_t path, uri, duri, args;
+ ngx_tree_ctx_t tree;
+ ngx_copy_file_t cf;
+ ngx_file_info_t fi;
+ ngx_table_elt_t *dest, *over;
+ ngx_ext_rename_file_t ext;
+ ngx_http_dav_copy_ctx_t copy;
+ ngx_http_dav_loc_conf_t *dlcf;
+
+ if (r->headers_in.content_length_n > 0) {
+ return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
+ }
+
+ dest = r->headers_in.destination;
+
+ if (dest == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client sent no \"Destination\" header");
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ p = dest->value.data;
+ /* there is always '\0' even after empty header value */
+ if (p[0] == '/') {
+ last = p + dest->value.len;
+ goto destination_done;
+ }
+
+ len = r->headers_in.server.len;
+
+ if (len == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client sent no \"Host\" header");
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+#if (NGX_HTTP_SSL)
+
+ if (r->connection->ssl) {
+ if (ngx_strncmp(dest->value.data, "https://", sizeof("https://") - 1)
+ != 0)
+ {
+ goto invalid_destination;
+ }
+
+ host = dest->value.data + sizeof("https://") - 1;
+
+ } else
+#endif
+ {
+ if (ngx_strncmp(dest->value.data, "http://", sizeof("http://") - 1)
+ != 0)
+ {
+ goto invalid_destination;
+ }
+
+ host = dest->value.data + sizeof("http://") - 1;
+ }
+
+ if (ngx_strncmp(host, r->headers_in.server.data, len) != 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "\"Destination\" URI \"%V\" is handled by "
+ "different repository than the source URI",
+ &dest->value);
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ last = dest->value.data + dest->value.len;
+
+ for (p = host + len; p < last; p++) {
+ if (*p == '/') {
+ goto destination_done;
+ }
+ }
+
+invalid_destination:
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client sent invalid \"Destination\" header: \"%V\"",
+ &dest->value);
+ return NGX_HTTP_BAD_REQUEST;
+
+destination_done:
+
+ duri.len = last - p;
+ duri.data = p;
+ flags = NGX_HTTP_LOG_UNSAFE;
+
+ if (ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) {
+ goto invalid_destination;
+ }
+
+ if ((r->uri.data[r->uri.len - 1] == '/' && *(last - 1) != '/')
+ || (r->uri.data[r->uri.len - 1] != '/' && *(last - 1) == '/'))
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "both URI \"%V\" and \"Destination\" URI \"%V\" "
+ "should be either collections or non-collections",
+ &r->uri, &dest->value);
+ return NGX_HTTP_CONFLICT;
+ }
+
+ depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
+
+ if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
+
+ if (r->method == NGX_HTTP_COPY) {
+ if (depth != 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "\"Depth\" header must be 0 or infinity");
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ } else {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "\"Depth\" header must be infinity");
+ return NGX_HTTP_BAD_REQUEST;
+ }
+ }
+
+ over = r->headers_in.overwrite;
+
+ if (over) {
+ if (over->value.len == 1) {
+ ch = over->value.data[0];
+
+ if (ch == 'T' || ch == 't') {
+ overwrite = 1;
+ goto overwrite_done;
+ }
+
+ if (ch == 'F' || ch == 'f') {
+ overwrite = 0;
+ goto overwrite_done;
+ }
+
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client sent invalid \"Overwrite\" header: \"%V\"",
+ &over->value);
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ overwrite = 1;
+
+overwrite_done:
+
+ if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http copy from: \"%s\"", path.data);
+
+ uri = r->uri;
+ r->uri = duri;
+
+ if (ngx_http_map_uri_to_path(r, &copy.path, &root, 0) == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ r->uri = uri;
+
+ copy.path.len--; /* omit "\0" */
+
+ if (copy.path.data[copy.path.len - 1] == '/') {
+ slash = 1;
+ copy.path.len--;
+ copy.path.data[copy.path.len] = '\0';
+
+ } else {
+ slash = 0;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http copy to: \"%s\"", copy.path.data);
+
+ if (ngx_link_info(copy.path.data, &fi) == NGX_FILE_ERROR) {
+ err = ngx_errno;
+
+ if (err != NGX_ENOENT) {
+ return ngx_http_dav_error(r->connection->log, err,
+ NGX_HTTP_NOT_FOUND, ngx_link_info_n,
+ copy.path.data);
+ }
+
+ /* destination does not exist */
+
+ overwrite = 0;
+ dir = 0;
+
+ } else {
+
+ /* destination exists */
+
+ if (ngx_is_dir(&fi) && !slash) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "\"%V\" could not be %Ved to collection \"%V\"",
+ &r->uri, &r->method_name, &dest->value);
+ return NGX_HTTP_CONFLICT;
+ }
+
+ if (!overwrite) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EEXIST,
+ "\"%s\" could not be created", copy.path.data);
+ return NGX_HTTP_PRECONDITION_FAILED;
+ }
+
+ dir = ngx_is_dir(&fi);
+ }
+
+ if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {
+ return ngx_http_dav_error(r->connection->log, ngx_errno,
+ NGX_HTTP_NOT_FOUND, ngx_link_info_n,
+ path.data);
+ }
+
+ if (ngx_is_dir(&fi)) {
+
+ if (r->uri.data[r->uri.len - 1] != '/') {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "\"%V\" is collection", &r->uri);
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ if (overwrite) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http delete: \"%s\"", copy.path.data);
+
+ rc = ngx_http_dav_delete_path(r, &copy.path, dir);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+ }
+ }
+
+ if (ngx_is_dir(&fi)) {
+
+ path.len -= 2; /* omit "/\0" */
+
+ if (r->method == NGX_HTTP_MOVE) {
+ if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) {
+ return NGX_HTTP_CREATED;
+ }
+ }
+
+ if (ngx_create_dir(copy.path.data, ngx_file_access(&fi))
+ == NGX_FILE_ERROR)
+ {
+ return ngx_http_dav_error(r->connection->log, ngx_errno,
+ NGX_HTTP_NOT_FOUND,
+ ngx_create_dir_n, copy.path.data);
+ }
+
+ copy.len = path.len;
+
+ tree.init_handler = NULL;
+ tree.file_handler = ngx_http_dav_copy_tree_file;
+ tree.pre_tree_handler = ngx_http_dav_copy_dir;
+ tree.post_tree_handler = ngx_http_dav_copy_dir_time;
+ tree.spec_handler = ngx_http_dav_noop;
+ tree.data = &copy;
+ tree.alloc = 0;
+ tree.log = r->connection->log;
+
+ if (ngx_walk_tree(&tree, &path) == NGX_OK) {
+
+ if (r->method == NGX_HTTP_MOVE) {
+ rc = ngx_http_dav_delete_path(r, &path, 1);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+ }
+
+ return NGX_HTTP_CREATED;
+ }
+
+ } else {
+
+ if (r->method == NGX_HTTP_MOVE) {
+
+ dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
+
+ ext.access = 0;
+ ext.path_access = dlcf->access;
+ ext.time = -1;
+ ext.create_path = 1;
+ ext.delete_file = 0;
+ ext.log = r->connection->log;
+
+ if (ngx_ext_rename_file(&path, &copy.path, &ext) == NGX_OK) {
+ return NGX_HTTP_NO_CONTENT;
+ }
+
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
+
+ cf.size = ngx_file_size(&fi);
+ cf.buf_size = 0;
+ cf.access = dlcf->access;
+ cf.time = ngx_file_mtime(&fi);
+ cf.log = r->connection->log;
+
+ if (ngx_copy_file(path.data, copy.path.data, &cf) == NGX_OK) {
+ return NGX_HTTP_NO_CONTENT;
+ }
+ }
+
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+ u_char *p, *dir;
+ size_t len;
+ ngx_http_dav_copy_ctx_t *copy;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+ "http copy dir: \"%s\"", path->data);
+
+ copy = ctx->data;
+
+ len = copy->path.len + path->len;
+
+ dir = ngx_alloc(len + 1, ctx->log);
+ if (dir == NULL) {
+ return NGX_ABORT;
+ }
+
+ p = ngx_cpymem(dir, copy->path.data, copy->path.len);
+ (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+ "http copy dir to: \"%s\"", dir);
+
+ if (ngx_create_dir(dir, ngx_dir_access(ctx->access)) == NGX_FILE_ERROR) {
+ (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_create_dir_n,
+ dir);
+ }
+
+ ngx_free(dir);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+ u_char *p, *dir;
+ size_t len;
+ ngx_http_dav_copy_ctx_t *copy;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+ "http copy dir time: \"%s\"", path->data);
+
+ copy = ctx->data;
+
+ len = copy->path.len + path->len;
+
+ dir = ngx_alloc(len + 1, ctx->log);
+ if (dir == NULL) {
+ return NGX_ABORT;
+ }
+
+ p = ngx_cpymem(dir, copy->path.data, copy->path.len);
+ (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+ "http copy dir time to: \"%s\"", dir);
+
+#if (NGX_WIN32)
+ {
+ ngx_fd_t fd;
+
+ fd = ngx_open_file(dir, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);
+
+ if (fd == NGX_INVALID_FILE) {
+ (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n, dir);
+ goto failed;
+ }
+
+ if (ngx_set_file_time(NULL, fd, ctx->mtime) != NGX_OK) {
+ ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
+ ngx_set_file_time_n " \"%s\" failed", dir);
+ }
+
+ if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", dir);
+ }
+ }
+
+failed:
+
+#else
+
+ if (ngx_set_file_time(dir, 0, ctx->mtime) != NGX_OK) {
+ ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
+ ngx_set_file_time_n " \"%s\" failed", dir);
+ }
+
+#endif
+
+ ngx_free(dir);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+ u_char *p, *file;
+ size_t len;
+ ngx_copy_file_t cf;
+ ngx_http_dav_copy_ctx_t *copy;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+ "http copy file: \"%s\"", path->data);
+
+ copy = ctx->data;
+
+ len = copy->path.len + path->len;
+
+ file = ngx_alloc(len + 1, ctx->log);
+ if (file == NULL) {
+ return NGX_ABORT;
+ }
+
+ p = ngx_cpymem(file, copy->path.data, copy->path.len);
+ (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+ "http copy file to: \"%s\"", file);
+
+ cf.size = ctx->size;
+ cf.buf_size = 0;
+ cf.access = ctx->access;
+ cf.time = ctx->mtime;
+ cf.log = ctx->log;
+
+ (void) ngx_copy_file(path->data, file, &cf);
+
+ ngx_free(file);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt)
+{
+ ngx_table_elt_t *depth;
+
+ depth = r->headers_in.depth;
+
+ if (depth == NULL) {
+ return dflt;
+ }
+
+ if (depth->value.len == 1) {
+
+ if (depth->value.data[0] == '0') {
+ return 0;
+ }
+
+ if (depth->value.data[0] == '1') {
+ return 1;
+ }
+
+ } else {
+
+ if (depth->value.len == sizeof("infinity") - 1
+ && ngx_strcmp(depth->value.data, "infinity") == 0)
+ {
+ return NGX_HTTP_DAV_INFINITY_DEPTH;
+ }
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client sent invalid \"Depth\" header: \"%V\"",
+ &depth->value);
+
+ return NGX_HTTP_DAV_INVALID_DEPTH;
+}
+
+
+static ngx_int_t
+ngx_http_dav_error(ngx_log_t *log, ngx_err_t err, ngx_int_t not_found,
+ char *failed, u_char *path)
+{
+ ngx_int_t rc;
+ ngx_uint_t level;
+
+ if (err == NGX_ENOENT || err == NGX_ENOTDIR || err == NGX_ENAMETOOLONG) {
+ level = NGX_LOG_ERR;
+ rc = not_found;
+
+ } else if (err == NGX_EACCES || err == NGX_EPERM) {
+ level = NGX_LOG_ERR;
+ rc = NGX_HTTP_FORBIDDEN;
+
+ } else if (err == NGX_EEXIST) {
+ level = NGX_LOG_ERR;
+ rc = NGX_HTTP_NOT_ALLOWED;
+
+ } else if (err == NGX_ENOSPC) {
+ level = NGX_LOG_CRIT;
+ rc = NGX_HTTP_INSUFFICIENT_STORAGE;
+
+ } else {
+ level = NGX_LOG_CRIT;
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_log_error(level, log, err, "%s \"%s\" failed", failed, path);
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_dav_location(ngx_http_request_t *r, u_char *path)
+{
+ u_char *location;
+ ngx_http_core_loc_conf_t *clcf;
+
+ r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));
+ if (r->headers_out.location == NULL) {
+ return NGX_ERROR;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (!clcf->alias && clcf->root_lengths == NULL) {
+ location = path + clcf->root.len;
+
+ } else {
+ location = ngx_pnalloc(r->pool, r->uri.len);
+ if (location == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(location, r->uri.data, r->uri.len);
+ }
+
+ /*
+ * we do not need to set the r->headers_out.location->hash and
+ * r->headers_out.location->key fields
+ */
+
+ r->headers_out.location->value.len = r->uri.len;
+ r->headers_out.location->value.data = location;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_dav_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_dav_loc_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->methods = 0;
+ */
+
+ conf->min_delete_depth = NGX_CONF_UNSET_UINT;
+ conf->access = NGX_CONF_UNSET_UINT;
+ conf->create_full_put_path = NGX_CONF_UNSET;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_dav_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_dav_loc_conf_t *prev = parent;
+ ngx_http_dav_loc_conf_t *conf = child;
+
+ ngx_conf_merge_bitmask_value(conf->methods, prev->methods,
+ (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_OFF));
+
+ ngx_conf_merge_uint_value(conf->min_delete_depth,
+ prev->min_delete_depth, 0);
+
+ ngx_conf_merge_uint_value(conf->access, prev->access, 0600);
+
+ ngx_conf_merge_value(conf->create_full_put_path,
+ prev->create_full_put_path, 0);
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_dav_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_dav_handler;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_degradation_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_degradation_module.c
new file mode 100644
index 00000000000..b9c65cdc9e0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_degradation_module.c
@@ -0,0 +1,243 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ size_t sbrk_size;
+} ngx_http_degradation_main_conf_t;
+
+
+typedef struct {
+ ngx_uint_t degrade;
+} ngx_http_degradation_loc_conf_t;
+
+
+static ngx_conf_enum_t ngx_http_degrade[] = {
+ { ngx_string("204"), 204 },
+ { ngx_string("444"), 444 },
+ { ngx_null_string, 0 }
+};
+
+
+static void *ngx_http_degradation_create_main_conf(ngx_conf_t *cf);
+static void *ngx_http_degradation_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static ngx_int_t ngx_http_degradation_init(ngx_conf_t *cf);
+
+
+static ngx_command_t ngx_http_degradation_commands[] = {
+
+ { ngx_string("degradation"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_http_degradation,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("degrade"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_degradation_loc_conf_t, degrade),
+ &ngx_http_degrade },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_degradation_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_degradation_init, /* postconfiguration */
+
+ ngx_http_degradation_create_main_conf, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_degradation_create_loc_conf, /* create location configuration */
+ ngx_http_degradation_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_degradation_module = {
+ NGX_MODULE_V1,
+ &ngx_http_degradation_module_ctx, /* module context */
+ ngx_http_degradation_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_degradation_handler(ngx_http_request_t *r)
+{
+ ngx_http_degradation_loc_conf_t *dlcf;
+
+ dlcf = ngx_http_get_module_loc_conf(r, ngx_http_degradation_module);
+
+ if (dlcf->degrade && ngx_http_degraded(r)) {
+ return dlcf->degrade;
+ }
+
+ return NGX_DECLINED;
+}
+
+
+ngx_uint_t
+ngx_http_degraded(ngx_http_request_t *r)
+{
+ time_t now;
+ ngx_uint_t log;
+ static size_t sbrk_size;
+ static time_t sbrk_time;
+ ngx_http_degradation_main_conf_t *dmcf;
+
+ dmcf = ngx_http_get_module_main_conf(r, ngx_http_degradation_module);
+
+ if (dmcf->sbrk_size) {
+
+ log = 0;
+ now = ngx_time();
+
+ /* lock mutex */
+
+ if (now != sbrk_time) {
+
+ /*
+ * ELF/i386 is loaded at 0x08000000, 128M
+ * ELF/amd64 is loaded at 0x00400000, 4M
+ *
+ * use a function address to subtract the loading address
+ */
+
+ sbrk_size = (size_t) sbrk(0) - ((uintptr_t) ngx_palloc & ~0x3FFFFF);
+ sbrk_time = now;
+ log = 1;
+ }
+
+ /* unlock mutex */
+
+ if (sbrk_size >= dmcf->sbrk_size) {
+ if (log) {
+ ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
+ "degradation sbrk:%uzM",
+ sbrk_size / (1024 * 1024));
+ }
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void *
+ngx_http_degradation_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_http_degradation_main_conf_t *dmcf;
+
+ dmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_degradation_main_conf_t));
+ if (dmcf == NULL) {
+ return NULL;
+ }
+
+ return dmcf;
+}
+
+
+static void *
+ngx_http_degradation_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_degradation_loc_conf_t *conf;
+
+ conf = ngx_palloc(cf->pool, sizeof(ngx_http_degradation_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->degrade = NGX_CONF_UNSET_UINT;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_degradation_loc_conf_t *prev = parent;
+ ngx_http_degradation_loc_conf_t *conf = child;
+
+ ngx_conf_merge_uint_value(conf->degrade, prev->degrade, 0);
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_degradation_main_conf_t *dmcf = conf;
+
+ ngx_str_t *value, s;
+
+ value = cf->args->elts;
+
+ if (ngx_strncmp(value[1].data, "sbrk=", 5) == 0) {
+
+ s.len = value[1].len - 5;
+ s.data = value[1].data + 5;
+
+ dmcf->sbrk_size = ngx_parse_size(&s);
+ if (dmcf->sbrk_size == (size_t) NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid sbrk size \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[1]);
+
+ return NGX_CONF_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_degradation_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_degradation_handler;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_empty_gif_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_empty_gif_module.c
new file mode 100644
index 00000000000..04114dc3f41
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_empty_gif_module.c
@@ -0,0 +1,140 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static char *ngx_http_empty_gif(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+static ngx_command_t ngx_http_empty_gif_commands[] = {
+
+ { ngx_string("empty_gif"),
+ NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
+ ngx_http_empty_gif,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+/* the minimal single pixel transparent GIF, 43 bytes */
+
+static u_char ngx_empty_gif[] = {
+
+ 'G', 'I', 'F', '8', '9', 'a', /* header */
+
+ /* logical screen descriptor */
+ 0x01, 0x00, /* logical screen width */
+ 0x01, 0x00, /* logical screen height */
+ 0x80, /* global 1-bit color table */
+ 0x01, /* background color #1 */
+ 0x00, /* no aspect ratio */
+
+ /* global color table */
+ 0x00, 0x00, 0x00, /* #0: black */
+ 0xff, 0xff, 0xff, /* #1: white */
+
+ /* graphic control extension */
+ 0x21, /* extension introducer */
+ 0xf9, /* graphic control label */
+ 0x04, /* block size */
+ 0x01, /* transparent color is given, */
+ /* no disposal specified, */
+ /* user input is not expected */
+ 0x00, 0x00, /* delay time */
+ 0x01, /* transparent color #1 */
+ 0x00, /* block terminator */
+
+ /* image descriptor */
+ 0x2c, /* image separator */
+ 0x00, 0x00, /* image left position */
+ 0x00, 0x00, /* image top position */
+ 0x01, 0x00, /* image width */
+ 0x01, 0x00, /* image height */
+ 0x00, /* no local color table, no interlaced */
+
+ /* table based image data */
+ 0x02, /* LZW minimum code size, */
+ /* must be at least 2-bit */
+ 0x02, /* block size */
+ 0x4c, 0x01, /* compressed bytes 01_001_100, 0000000_1 */
+ /* 100: clear code */
+ /* 001: 1 */
+ /* 101: end of information code */
+ 0x00, /* block terminator */
+
+ 0x3B /* trailer */
+};
+
+
+static ngx_http_module_t ngx_http_empty_gif_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_empty_gif_module = {
+ NGX_MODULE_V1,
+ &ngx_http_empty_gif_module_ctx, /* module context */
+ ngx_http_empty_gif_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t ngx_http_gif_type = ngx_string("image/gif");
+
+
+static ngx_int_t
+ngx_http_empty_gif_handler(ngx_http_request_t *r)
+{
+ ngx_http_complex_value_t cv;
+
+ if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
+ return NGX_HTTP_NOT_ALLOWED;
+ }
+
+ ngx_memzero(&cv, sizeof(ngx_http_complex_value_t));
+
+ cv.value.len = sizeof(ngx_empty_gif);
+ cv.value.data = ngx_empty_gif;
+ r->headers_out.last_modified_time = 23349600;
+
+ return ngx_http_send_response(r, NGX_HTTP_OK, &ngx_http_gif_type, &cv);
+}
+
+
+static char *
+ngx_http_empty_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+ clcf->handler = ngx_http_empty_gif_handler;
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_fastcgi_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_fastcgi_module.c
new file mode 100644
index 00000000000..8016f5b75ba
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_fastcgi_module.c
@@ -0,0 +1,3279 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_http_upstream_conf_t upstream;
+
+ ngx_str_t index;
+
+ ngx_array_t *flushes;
+ ngx_array_t *params_len;
+ ngx_array_t *params;
+ ngx_array_t *params_source;
+ ngx_array_t *catch_stderr;
+
+ ngx_array_t *fastcgi_lengths;
+ ngx_array_t *fastcgi_values;
+
+ ngx_hash_t headers_hash;
+ ngx_uint_t header_params;
+
+ ngx_flag_t keep_conn;
+
+#if (NGX_HTTP_CACHE)
+ ngx_http_complex_value_t cache_key;
+#endif
+
+#if (NGX_PCRE)
+ ngx_regex_t *split_regex;
+ ngx_str_t split_name;
+#endif
+} ngx_http_fastcgi_loc_conf_t;
+
+
+typedef enum {
+ ngx_http_fastcgi_st_version = 0,
+ ngx_http_fastcgi_st_type,
+ ngx_http_fastcgi_st_request_id_hi,
+ ngx_http_fastcgi_st_request_id_lo,
+ ngx_http_fastcgi_st_content_length_hi,
+ ngx_http_fastcgi_st_content_length_lo,
+ ngx_http_fastcgi_st_padding_length,
+ ngx_http_fastcgi_st_reserved,
+ ngx_http_fastcgi_st_data,
+ ngx_http_fastcgi_st_padding
+} ngx_http_fastcgi_state_e;
+
+
+typedef struct {
+ u_char *start;
+ u_char *end;
+} ngx_http_fastcgi_split_part_t;
+
+
+typedef struct {
+ ngx_http_fastcgi_state_e state;
+ u_char *pos;
+ u_char *last;
+ ngx_uint_t type;
+ size_t length;
+ size_t padding;
+
+ unsigned fastcgi_stdout:1;
+ unsigned large_stderr:1;
+
+ ngx_array_t *split_parts;
+
+ ngx_str_t script_name;
+ ngx_str_t path_info;
+} ngx_http_fastcgi_ctx_t;
+
+
+#define NGX_HTTP_FASTCGI_RESPONDER 1
+
+#define NGX_HTTP_FASTCGI_KEEP_CONN 1
+
+#define NGX_HTTP_FASTCGI_BEGIN_REQUEST 1
+#define NGX_HTTP_FASTCGI_ABORT_REQUEST 2
+#define NGX_HTTP_FASTCGI_END_REQUEST 3
+#define NGX_HTTP_FASTCGI_PARAMS 4
+#define NGX_HTTP_FASTCGI_STDIN 5
+#define NGX_HTTP_FASTCGI_STDOUT 6
+#define NGX_HTTP_FASTCGI_STDERR 7
+#define NGX_HTTP_FASTCGI_DATA 8
+
+
+typedef struct {
+ u_char version;
+ u_char type;
+ u_char request_id_hi;
+ u_char request_id_lo;
+ u_char content_length_hi;
+ u_char content_length_lo;
+ u_char padding_length;
+ u_char reserved;
+} ngx_http_fastcgi_header_t;
+
+
+typedef struct {
+ u_char role_hi;
+ u_char role_lo;
+ u_char flags;
+ u_char reserved[5];
+} ngx_http_fastcgi_begin_request_t;
+
+
+typedef struct {
+ u_char version;
+ u_char type;
+ u_char request_id_hi;
+ u_char request_id_lo;
+} ngx_http_fastcgi_header_small_t;
+
+
+typedef struct {
+ ngx_http_fastcgi_header_t h0;
+ ngx_http_fastcgi_begin_request_t br;
+ ngx_http_fastcgi_header_small_t h1;
+} ngx_http_fastcgi_request_start_t;
+
+
+static ngx_int_t ngx_http_fastcgi_eval(ngx_http_request_t *r,
+ ngx_http_fastcgi_loc_conf_t *flcf);
+#if (NGX_HTTP_CACHE)
+static ngx_int_t ngx_http_fastcgi_create_key(ngx_http_request_t *r);
+#endif
+static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r);
+static ngx_int_t ngx_http_fastcgi_input_filter_init(void *data);
+static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p,
+ ngx_buf_t *buf);
+static ngx_int_t ngx_http_fastcgi_non_buffered_filter(void *data,
+ ssize_t bytes);
+static ngx_int_t ngx_http_fastcgi_process_record(ngx_http_request_t *r,
+ ngx_http_fastcgi_ctx_t *f);
+static void ngx_http_fastcgi_abort_request(ngx_http_request_t *r);
+static void ngx_http_fastcgi_finalize_request(ngx_http_request_t *r,
+ ngx_int_t rc);
+
+static ngx_int_t ngx_http_fastcgi_add_variables(ngx_conf_t *cf);
+static void *ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static ngx_int_t ngx_http_fastcgi_merge_params(ngx_conf_t *cf,
+ ngx_http_fastcgi_loc_conf_t *conf, ngx_http_fastcgi_loc_conf_t *prev);
+
+static ngx_int_t ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_http_fastcgi_ctx_t *ngx_http_fastcgi_split(ngx_http_request_t *r,
+ ngx_http_fastcgi_loc_conf_t *flcf);
+
+static char *ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_fastcgi_split_path_info(ngx_conf_t *cf,
+ ngx_command_t *cmd, void *conf);
+static char *ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+#if (NGX_HTTP_CACHE)
+static char *ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+#endif
+
+static char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post,
+ void *data);
+
+
+static ngx_conf_post_t ngx_http_fastcgi_lowat_post =
+ { ngx_http_fastcgi_lowat_check };
+
+
+static ngx_conf_bitmask_t ngx_http_fastcgi_next_upstream_masks[] = {
+ { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
+ { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
+ { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
+ { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },
+ { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
+ { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },
+ { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
+ { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },
+ { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
+ { ngx_null_string, 0 }
+};
+
+
+ngx_module_t ngx_http_fastcgi_module;
+
+
+static ngx_command_t ngx_http_fastcgi_commands[] = {
+
+ { ngx_string("fastcgi_pass"),
+ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
+ ngx_http_fastcgi_pass,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("fastcgi_index"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, index),
+ NULL },
+
+ { ngx_string("fastcgi_split_path_info"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_fastcgi_split_path_info,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("fastcgi_store"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_fastcgi_store,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("fastcgi_store_access"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
+ ngx_conf_set_access_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.store_access),
+ NULL },
+
+ { ngx_string("fastcgi_buffering"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffering),
+ NULL },
+
+ { ngx_string("fastcgi_ignore_client_abort"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_client_abort),
+ NULL },
+
+ { ngx_string("fastcgi_bind"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_upstream_bind_set_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.local),
+ NULL },
+
+ { ngx_string("fastcgi_connect_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.connect_timeout),
+ NULL },
+
+ { ngx_string("fastcgi_send_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_timeout),
+ NULL },
+
+ { ngx_string("fastcgi_send_lowat"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_lowat),
+ &ngx_http_fastcgi_lowat_post },
+
+ { ngx_string("fastcgi_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffer_size),
+ NULL },
+
+ { ngx_string("fastcgi_pass_request_headers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_headers),
+ NULL },
+
+ { ngx_string("fastcgi_pass_request_body"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_body),
+ NULL },
+
+ { ngx_string("fastcgi_intercept_errors"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.intercept_errors),
+ NULL },
+
+ { ngx_string("fastcgi_read_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.read_timeout),
+ NULL },
+
+ { ngx_string("fastcgi_buffers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_conf_set_bufs_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.bufs),
+ NULL },
+
+ { ngx_string("fastcgi_busy_buffers_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.busy_buffers_size_conf),
+ NULL },
+
+#if (NGX_HTTP_CACHE)
+
+ { ngx_string("fastcgi_cache"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_fastcgi_cache,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("fastcgi_cache_key"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_fastcgi_cache_key,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("fastcgi_cache_path"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
+ ngx_http_file_cache_set_slot,
+ 0,
+ 0,
+ &ngx_http_fastcgi_module },
+
+ { ngx_string("fastcgi_cache_bypass"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_set_predicate_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_bypass),
+ NULL },
+
+ { ngx_string("fastcgi_no_cache"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_set_predicate_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.no_cache),
+ NULL },
+
+ { ngx_string("fastcgi_cache_valid"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_file_cache_valid_set_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_valid),
+ NULL },
+
+ { ngx_string("fastcgi_cache_min_uses"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_min_uses),
+ NULL },
+
+ { ngx_string("fastcgi_cache_use_stale"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_use_stale),
+ &ngx_http_fastcgi_next_upstream_masks },
+
+ { ngx_string("fastcgi_cache_methods"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_methods),
+ &ngx_http_upstream_cache_method_mask },
+
+ { ngx_string("fastcgi_cache_lock"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock),
+ NULL },
+
+ { ngx_string("fastcgi_cache_lock_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock_timeout),
+ NULL },
+
+ { ngx_string("fastcgi_cache_revalidate"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_revalidate),
+ NULL },
+
+#endif
+
+ { ngx_string("fastcgi_temp_path"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
+ ngx_conf_set_path_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_path),
+ NULL },
+
+ { ngx_string("fastcgi_max_temp_file_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.max_temp_file_size_conf),
+ NULL },
+
+ { ngx_string("fastcgi_temp_file_write_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_file_write_size_conf),
+ NULL },
+
+ { ngx_string("fastcgi_next_upstream"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream),
+ &ngx_http_fastcgi_next_upstream_masks },
+
+ { ngx_string("fastcgi_param"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,
+ ngx_http_upstream_param_set_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, params_source),
+ NULL },
+
+ { ngx_string("fastcgi_pass_header"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_headers),
+ NULL },
+
+ { ngx_string("fastcgi_hide_header"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.hide_headers),
+ NULL },
+
+ { ngx_string("fastcgi_ignore_headers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_headers),
+ &ngx_http_upstream_ignore_headers_masks },
+
+ { ngx_string("fastcgi_catch_stderr"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, catch_stderr),
+ NULL },
+
+ { ngx_string("fastcgi_keep_conn"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_fastcgi_loc_conf_t, keep_conn),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_fastcgi_module_ctx = {
+ ngx_http_fastcgi_add_variables, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_fastcgi_create_loc_conf, /* create location configuration */
+ ngx_http_fastcgi_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_fastcgi_module = {
+ NGX_MODULE_V1,
+ &ngx_http_fastcgi_module_ctx, /* module context */
+ ngx_http_fastcgi_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_fastcgi_request_start_t ngx_http_fastcgi_request_start = {
+ { 1, /* version */
+ NGX_HTTP_FASTCGI_BEGIN_REQUEST, /* type */
+ 0, /* request_id_hi */
+ 1, /* request_id_lo */
+ 0, /* content_length_hi */
+ sizeof(ngx_http_fastcgi_begin_request_t), /* content_length_lo */
+ 0, /* padding_length */
+ 0 }, /* reserved */
+
+ { 0, /* role_hi */
+ NGX_HTTP_FASTCGI_RESPONDER, /* role_lo */
+ 0, /* NGX_HTTP_FASTCGI_KEEP_CONN */ /* flags */
+ { 0, 0, 0, 0, 0 } }, /* reserved[5] */
+
+ { 1, /* version */
+ NGX_HTTP_FASTCGI_PARAMS, /* type */
+ 0, /* request_id_hi */
+ 1 }, /* request_id_lo */
+
+};
+
+
+static ngx_http_variable_t ngx_http_fastcgi_vars[] = {
+
+ { ngx_string("fastcgi_script_name"), NULL,
+ ngx_http_fastcgi_script_name_variable, 0,
+ NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+ { ngx_string("fastcgi_path_info"), NULL,
+ ngx_http_fastcgi_path_info_variable, 0,
+ NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+ { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
+
+
+static ngx_str_t ngx_http_fastcgi_hide_headers[] = {
+ ngx_string("Status"),
+ ngx_string("X-Accel-Expires"),
+ ngx_string("X-Accel-Redirect"),
+ ngx_string("X-Accel-Limit-Rate"),
+ ngx_string("X-Accel-Buffering"),
+ ngx_string("X-Accel-Charset"),
+ ngx_null_string
+};
+
+
+#if (NGX_HTTP_CACHE)
+
+static ngx_keyval_t ngx_http_fastcgi_cache_headers[] = {
+ { ngx_string("HTTP_IF_MODIFIED_SINCE"),
+ ngx_string("$upstream_cache_last_modified") },
+ { ngx_string("HTTP_IF_UNMODIFIED_SINCE"), ngx_string("") },
+ { ngx_string("HTTP_IF_NONE_MATCH"), ngx_string("$upstream_cache_etag") },
+ { ngx_string("HTTP_IF_MATCH"), ngx_string("") },
+ { ngx_string("HTTP_RANGE"), ngx_string("") },
+ { ngx_string("HTTP_IF_RANGE"), ngx_string("") },
+ { ngx_null_string, ngx_null_string }
+};
+
+#endif
+
+
+static ngx_path_init_t ngx_http_fastcgi_temp_path = {
+ ngx_string(NGX_HTTP_FASTCGI_TEMP_PATH), { 1, 2, 0 }
+};
+
+
+static ngx_int_t
+ngx_http_fastcgi_handler(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_http_upstream_t *u;
+ ngx_http_fastcgi_ctx_t *f;
+ ngx_http_fastcgi_loc_conf_t *flcf;
+
+ if (ngx_http_upstream_create(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
+ if (f == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
+
+ flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+ if (flcf->fastcgi_lengths) {
+ if (ngx_http_fastcgi_eval(r, flcf) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ u = r->upstream;
+
+ ngx_str_set(&u->schema, "fastcgi://");
+ u->output.tag = (ngx_buf_tag_t) &ngx_http_fastcgi_module;
+
+ u->conf = &flcf->upstream;
+
+#if (NGX_HTTP_CACHE)
+ u->create_key = ngx_http_fastcgi_create_key;
+#endif
+ u->create_request = ngx_http_fastcgi_create_request;
+ u->reinit_request = ngx_http_fastcgi_reinit_request;
+ u->process_header = ngx_http_fastcgi_process_header;
+ u->abort_request = ngx_http_fastcgi_abort_request;
+ u->finalize_request = ngx_http_fastcgi_finalize_request;
+ r->state = 0;
+
+ u->buffering = flcf->upstream.buffering;
+
+ u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
+ if (u->pipe == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ u->pipe->input_filter = ngx_http_fastcgi_input_filter;
+ u->pipe->input_ctx = r;
+
+ u->input_filter_init = ngx_http_fastcgi_input_filter_init;
+ u->input_filter = ngx_http_fastcgi_non_buffered_filter;
+ u->input_filter_ctx = r;
+
+ rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ return rc;
+ }
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)
+{
+ ngx_url_t url;
+ ngx_http_upstream_t *u;
+
+ ngx_memzero(&url, sizeof(ngx_url_t));
+
+ if (ngx_http_script_run(r, &url.url, flcf->fastcgi_lengths->elts, 0,
+ flcf->fastcgi_values->elts)
+ == NULL)
+ {
+ return NGX_ERROR;
+ }
+
+ url.no_resolve = 1;
+
+ if (ngx_parse_url(r->pool, &url) != NGX_OK) {
+ if (url.err) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "%s in upstream \"%V\"", url.err, &url.url);
+ }
+
+ return NGX_ERROR;
+ }
+
+ u = r->upstream;
+
+ u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
+ if (u->resolved == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (url.addrs && url.addrs[0].sockaddr) {
+ u->resolved->sockaddr = url.addrs[0].sockaddr;
+ u->resolved->socklen = url.addrs[0].socklen;
+ u->resolved->naddrs = 1;
+ u->resolved->host = url.addrs[0].name;
+
+ } else {
+ u->resolved->host = url.host;
+ u->resolved->port = url.port;
+ u->resolved->no_port = url.no_port;
+ }
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HTTP_CACHE)
+
+static ngx_int_t
+ngx_http_fastcgi_create_key(ngx_http_request_t *r)
+{
+ ngx_str_t *key;
+ ngx_http_fastcgi_loc_conf_t *flcf;
+
+ key = ngx_array_push(&r->cache->keys);
+ if (key == NULL) {
+ return NGX_ERROR;
+ }
+
+ flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+ if (ngx_http_complex_value(r, &flcf->cache_key, key) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_fastcgi_create_request(ngx_http_request_t *r)
+{
+ off_t file_pos;
+ u_char ch, *pos, *lowcase_key;
+ size_t size, len, key_len, val_len, padding,
+ allocated;
+ ngx_uint_t i, n, next, hash, skip_empty, header_params;
+ ngx_buf_t *b;
+ ngx_chain_t *cl, *body;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *header, **ignored;
+ ngx_http_script_code_pt code;
+ ngx_http_script_engine_t e, le;
+ ngx_http_fastcgi_header_t *h;
+ ngx_http_fastcgi_loc_conf_t *flcf;
+ ngx_http_script_len_code_pt lcode;
+
+ len = 0;
+ header_params = 0;
+ ignored = NULL;
+
+ flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+ if (flcf->params_len) {
+ ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
+
+ ngx_http_script_flush_no_cacheable_variables(r, flcf->flushes);
+ le.flushed = 1;
+
+ le.ip = flcf->params_len->elts;
+ le.request = r;
+
+ while (*(uintptr_t *) le.ip) {
+
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ key_len = lcode(&le);
+
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ skip_empty = lcode(&le);
+
+ for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ }
+ le.ip += sizeof(uintptr_t);
+
+ if (skip_empty && val_len == 0) {
+ continue;
+ }
+
+ len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len;
+ }
+ }
+
+ if (flcf->upstream.pass_request_headers) {
+
+ allocated = 0;
+ lowcase_key = NULL;
+
+ if (flcf->header_params) {
+ n = 0;
+ part = &r->headers_in.headers.part;
+
+ while (part) {
+ n += part->nelts;
+ part = part->next;
+ }
+
+ ignored = ngx_palloc(r->pool, n * sizeof(void *));
+ if (ignored == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ part = &r->headers_in.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (flcf->header_params) {
+ if (allocated < header[i].key.len) {
+ allocated = header[i].key.len + 16;
+ lowcase_key = ngx_pnalloc(r->pool, allocated);
+ if (lowcase_key == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ hash = 0;
+
+ for (n = 0; n < header[i].key.len; n++) {
+ ch = header[i].key.data[n];
+
+ if (ch >= 'A' && ch <= 'Z') {
+ ch |= 0x20;
+
+ } else if (ch == '-') {
+ ch = '_';
+ }
+
+ hash = ngx_hash(hash, ch);
+ lowcase_key[n] = ch;
+ }
+
+ if (ngx_hash_find(&flcf->headers_hash, hash, lowcase_key, n)) {
+ ignored[header_params++] = &header[i];
+ continue;
+ }
+
+ n += sizeof("HTTP_") - 1;
+
+ } else {
+ n = sizeof("HTTP_") - 1 + header[i].key.len;
+ }
+
+ len += ((n > 127) ? 4 : 1) + ((header[i].value.len > 127) ? 4 : 1)
+ + n + header[i].value.len;
+ }
+ }
+
+
+ if (len > 65535) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "fastcgi request record is too big: %uz", len);
+ return NGX_ERROR;
+ }
+
+
+ padding = 8 - len % 8;
+ padding = (padding == 8) ? 0 : padding;
+
+
+ size = sizeof(ngx_http_fastcgi_header_t)
+ + sizeof(ngx_http_fastcgi_begin_request_t)
+
+ + sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */
+ + len + padding
+ + sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */
+
+ + sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */
+
+
+ b = ngx_create_temp_buf(r->pool, size);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+
+ ngx_http_fastcgi_request_start.br.flags =
+ flcf->keep_conn ? NGX_HTTP_FASTCGI_KEEP_CONN : 0;
+
+ ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start,
+ sizeof(ngx_http_fastcgi_request_start_t));
+
+ h = (ngx_http_fastcgi_header_t *)
+ (b->pos + sizeof(ngx_http_fastcgi_header_t)
+ + sizeof(ngx_http_fastcgi_begin_request_t));
+
+ h->content_length_hi = (u_char) ((len >> 8) & 0xff);
+ h->content_length_lo = (u_char) (len & 0xff);
+ h->padding_length = (u_char) padding;
+ h->reserved = 0;
+
+ b->last = b->pos + sizeof(ngx_http_fastcgi_header_t)
+ + sizeof(ngx_http_fastcgi_begin_request_t)
+ + sizeof(ngx_http_fastcgi_header_t);
+
+
+ if (flcf->params_len) {
+ ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
+
+ e.ip = flcf->params->elts;
+ e.pos = b->last;
+ e.request = r;
+ e.flushed = 1;
+
+ le.ip = flcf->params_len->elts;
+
+ while (*(uintptr_t *) le.ip) {
+
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ key_len = (u_char) lcode(&le);
+
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ skip_empty = lcode(&le);
+
+ for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ }
+ le.ip += sizeof(uintptr_t);
+
+ if (skip_empty && val_len == 0) {
+ e.skip = 1;
+
+ while (*(uintptr_t *) e.ip) {
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) &e);
+ }
+ e.ip += sizeof(uintptr_t);
+
+ e.skip = 0;
+
+ continue;
+ }
+
+ *e.pos++ = (u_char) key_len;
+
+ if (val_len > 127) {
+ *e.pos++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);
+ *e.pos++ = (u_char) ((val_len >> 16) & 0xff);
+ *e.pos++ = (u_char) ((val_len >> 8) & 0xff);
+ *e.pos++ = (u_char) (val_len & 0xff);
+
+ } else {
+ *e.pos++ = (u_char) val_len;
+ }
+
+ while (*(uintptr_t *) e.ip) {
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) &e);
+ }
+ e.ip += sizeof(uintptr_t);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "fastcgi param: \"%*s: %*s\"",
+ key_len, e.pos - (key_len + val_len),
+ val_len, e.pos - val_len);
+ }
+
+ b->last = e.pos;
+ }
+
+
+ if (flcf->upstream.pass_request_headers) {
+
+ part = &r->headers_in.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ for (n = 0; n < header_params; n++) {
+ if (&header[i] == ignored[n]) {
+ goto next;
+ }
+ }
+
+ key_len = sizeof("HTTP_") - 1 + header[i].key.len;
+ if (key_len > 127) {
+ *b->last++ = (u_char) (((key_len >> 24) & 0x7f) | 0x80);
+ *b->last++ = (u_char) ((key_len >> 16) & 0xff);
+ *b->last++ = (u_char) ((key_len >> 8) & 0xff);
+ *b->last++ = (u_char) (key_len & 0xff);
+
+ } else {
+ *b->last++ = (u_char) key_len;
+ }
+
+ val_len = header[i].value.len;
+ if (val_len > 127) {
+ *b->last++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);
+ *b->last++ = (u_char) ((val_len >> 16) & 0xff);
+ *b->last++ = (u_char) ((val_len >> 8) & 0xff);
+ *b->last++ = (u_char) (val_len & 0xff);
+
+ } else {
+ *b->last++ = (u_char) val_len;
+ }
+
+ b->last = ngx_cpymem(b->last, "HTTP_", sizeof("HTTP_") - 1);
+
+ for (n = 0; n < header[i].key.len; n++) {
+ ch = header[i].key.data[n];
+
+ if (ch >= 'a' && ch <= 'z') {
+ ch &= ~0x20;
+
+ } else if (ch == '-') {
+ ch = '_';
+ }
+
+ *b->last++ = ch;
+ }
+
+ b->last = ngx_copy(b->last, header[i].value.data, val_len);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "fastcgi param: \"%*s: %*s\"",
+ key_len, b->last - (key_len + val_len),
+ val_len, b->last - val_len);
+ next:
+
+ continue;
+ }
+ }
+
+
+ if (padding) {
+ ngx_memzero(b->last, padding);
+ b->last += padding;
+ }
+
+
+ h = (ngx_http_fastcgi_header_t *) b->last;
+ b->last += sizeof(ngx_http_fastcgi_header_t);
+
+ h->version = 1;
+ h->type = NGX_HTTP_FASTCGI_PARAMS;
+ h->request_id_hi = 0;
+ h->request_id_lo = 1;
+ h->content_length_hi = 0;
+ h->content_length_lo = 0;
+ h->padding_length = 0;
+ h->reserved = 0;
+
+ h = (ngx_http_fastcgi_header_t *) b->last;
+ b->last += sizeof(ngx_http_fastcgi_header_t);
+
+ if (flcf->upstream.pass_request_body) {
+ body = r->upstream->request_bufs;
+ r->upstream->request_bufs = cl;
+
+#if (NGX_SUPPRESS_WARN)
+ file_pos = 0;
+ pos = NULL;
+#endif
+
+ while (body) {
+
+ if (body->buf->in_file) {
+ file_pos = body->buf->file_pos;
+
+ } else {
+ pos = body->buf->pos;
+ }
+
+ next = 0;
+
+ do {
+ b = ngx_alloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
+
+ if (body->buf->in_file) {
+ b->file_pos = file_pos;
+ file_pos += 32 * 1024;
+
+ if (file_pos >= body->buf->file_last) {
+ file_pos = body->buf->file_last;
+ next = 1;
+ }
+
+ b->file_last = file_pos;
+ len = (ngx_uint_t) (file_pos - b->file_pos);
+
+ } else {
+ b->pos = pos;
+ pos += 32 * 1024;
+
+ if (pos >= body->buf->last) {
+ pos = body->buf->last;
+ next = 1;
+ }
+
+ b->last = pos;
+ len = (ngx_uint_t) (pos - b->pos);
+ }
+
+ padding = 8 - len % 8;
+ padding = (padding == 8) ? 0 : padding;
+
+ h->version = 1;
+ h->type = NGX_HTTP_FASTCGI_STDIN;
+ h->request_id_hi = 0;
+ h->request_id_lo = 1;
+ h->content_length_hi = (u_char) ((len >> 8) & 0xff);
+ h->content_length_lo = (u_char) (len & 0xff);
+ h->padding_length = (u_char) padding;
+ h->reserved = 0;
+
+ cl->next = ngx_alloc_chain_link(r->pool);
+ if (cl->next == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = cl->next;
+ cl->buf = b;
+
+ b = ngx_create_temp_buf(r->pool,
+ sizeof(ngx_http_fastcgi_header_t)
+ + padding);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (padding) {
+ ngx_memzero(b->last, padding);
+ b->last += padding;
+ }
+
+ h = (ngx_http_fastcgi_header_t *) b->last;
+ b->last += sizeof(ngx_http_fastcgi_header_t);
+
+ cl->next = ngx_alloc_chain_link(r->pool);
+ if (cl->next == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = cl->next;
+ cl->buf = b;
+
+ } while (!next);
+
+ body = body->next;
+ }
+
+ } else {
+ r->upstream->request_bufs = cl;
+ }
+
+ h->version = 1;
+ h->type = NGX_HTTP_FASTCGI_STDIN;
+ h->request_id_hi = 0;
+ h->request_id_lo = 1;
+ h->content_length_hi = 0;
+ h->content_length_lo = 0;
+ h->padding_length = 0;
+ h->reserved = 0;
+
+ cl->next = NULL;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_fastcgi_reinit_request(ngx_http_request_t *r)
+{
+ ngx_http_fastcgi_ctx_t *f;
+
+ f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
+
+ if (f == NULL) {
+ return NGX_OK;
+ }
+
+ f->state = ngx_http_fastcgi_st_version;
+ f->fastcgi_stdout = 0;
+ f->large_stderr = 0;
+
+ if (f->split_parts) {
+ f->split_parts->nelts = 0;
+ }
+
+ r->state = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_fastcgi_process_header(ngx_http_request_t *r)
+{
+ u_char *p, *msg, *start, *last,
+ *part_start, *part_end;
+ size_t size;
+ ngx_str_t *status_line, *pattern;
+ ngx_int_t rc, status;
+ ngx_buf_t buf;
+ ngx_uint_t i;
+ ngx_table_elt_t *h;
+ ngx_http_upstream_t *u;
+ ngx_http_fastcgi_ctx_t *f;
+ ngx_http_upstream_header_t *hh;
+ ngx_http_fastcgi_loc_conf_t *flcf;
+ ngx_http_fastcgi_split_part_t *part;
+ ngx_http_upstream_main_conf_t *umcf;
+
+ f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
+
+ umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
+
+ u = r->upstream;
+
+ for ( ;; ) {
+
+ if (f->state < ngx_http_fastcgi_st_data) {
+
+ f->pos = u->buffer.pos;
+ f->last = u->buffer.last;
+
+ rc = ngx_http_fastcgi_process_record(r, f);
+
+ u->buffer.pos = f->pos;
+ u->buffer.last = f->last;
+
+ if (rc == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+
+ if (f->type != NGX_HTTP_FASTCGI_STDOUT
+ && f->type != NGX_HTTP_FASTCGI_STDERR)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent unexpected FastCGI record: %d",
+ f->type);
+
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+
+ if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream prematurely closed FastCGI stdout");
+
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+ }
+
+ if (f->state == ngx_http_fastcgi_st_padding) {
+
+ if (u->buffer.pos + f->padding < u->buffer.last) {
+ f->state = ngx_http_fastcgi_st_version;
+ u->buffer.pos += f->padding;
+
+ continue;
+ }
+
+ if (u->buffer.pos + f->padding == u->buffer.last) {
+ f->state = ngx_http_fastcgi_st_version;
+ u->buffer.pos = u->buffer.last;
+
+ return NGX_AGAIN;
+ }
+
+ f->padding -= u->buffer.last - u->buffer.pos;
+ u->buffer.pos = u->buffer.last;
+
+ return NGX_AGAIN;
+ }
+
+
+ /* f->state == ngx_http_fastcgi_st_data */
+
+ if (f->type == NGX_HTTP_FASTCGI_STDERR) {
+
+ if (f->length) {
+ msg = u->buffer.pos;
+
+ if (u->buffer.pos + f->length <= u->buffer.last) {
+ u->buffer.pos += f->length;
+ f->length = 0;
+ f->state = ngx_http_fastcgi_st_padding;
+
+ } else {
+ f->length -= u->buffer.last - u->buffer.pos;
+ u->buffer.pos = u->buffer.last;
+ }
+
+ for (p = u->buffer.pos - 1; msg < p; p--) {
+ if (*p != LF && *p != CR && *p != '.' && *p != ' ') {
+ break;
+ }
+ }
+
+ p++;
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "FastCGI sent in stderr: \"%*s\"", p - msg, msg);
+
+ flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+ if (flcf->catch_stderr) {
+ pattern = flcf->catch_stderr->elts;
+
+ for (i = 0; i < flcf->catch_stderr->nelts; i++) {
+ if (ngx_strnstr(msg, (char *) pattern[i].data,
+ p - msg)
+ != NULL)
+ {
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+ }
+ }
+
+ if (u->buffer.pos == u->buffer.last) {
+
+ if (!f->fastcgi_stdout) {
+
+ /*
+ * the special handling the large number
+ * of the PHP warnings to not allocate memory
+ */
+
+#if (NGX_HTTP_CACHE)
+ if (r->cache) {
+ u->buffer.pos = u->buffer.start
+ + r->cache->header_start;
+ } else {
+ u->buffer.pos = u->buffer.start;
+ }
+#else
+ u->buffer.pos = u->buffer.start;
+#endif
+ u->buffer.last = u->buffer.pos;
+ f->large_stderr = 1;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ } else {
+ f->state = ngx_http_fastcgi_st_padding;
+ }
+
+ continue;
+ }
+
+
+ /* f->type == NGX_HTTP_FASTCGI_STDOUT */
+
+#if (NGX_HTTP_CACHE)
+
+ if (f->large_stderr && r->cache) {
+ u_char *start;
+ ssize_t len;
+ ngx_http_fastcgi_header_t *fh;
+
+ start = u->buffer.start + r->cache->header_start;
+
+ len = u->buffer.pos - start - 2 * sizeof(ngx_http_fastcgi_header_t);
+
+ /*
+ * A tail of large stderr output before HTTP header is placed
+ * in a cache file without a FastCGI record header.
+ * To workaround it we put a dummy FastCGI record header at the
+ * start of the stderr output or update r->cache_header_start,
+ * if there is no enough place for the record header.
+ */
+
+ if (len >= 0) {
+ fh = (ngx_http_fastcgi_header_t *) start;
+ fh->version = 1;
+ fh->type = NGX_HTTP_FASTCGI_STDERR;
+ fh->request_id_hi = 0;
+ fh->request_id_lo = 1;
+ fh->content_length_hi = (u_char) ((len >> 8) & 0xff);
+ fh->content_length_lo = (u_char) (len & 0xff);
+ fh->padding_length = 0;
+ fh->reserved = 0;
+
+ } else {
+ r->cache->header_start += u->buffer.pos - start
+ - sizeof(ngx_http_fastcgi_header_t);
+ }
+
+ f->large_stderr = 0;
+ }
+
+#endif
+
+ f->fastcgi_stdout = 1;
+
+ start = u->buffer.pos;
+
+ if (u->buffer.pos + f->length < u->buffer.last) {
+
+ /*
+ * set u->buffer.last to the end of the FastCGI record data
+ * for ngx_http_parse_header_line()
+ */
+
+ last = u->buffer.last;
+ u->buffer.last = u->buffer.pos + f->length;
+
+ } else {
+ last = NULL;
+ }
+
+ for ( ;; ) {
+
+ part_start = u->buffer.pos;
+ part_end = u->buffer.last;
+
+ rc = ngx_http_parse_header_line(r, &u->buffer, 1);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http fastcgi parser: %d", rc);
+
+ if (rc == NGX_AGAIN) {
+ break;
+ }
+
+ if (rc == NGX_OK) {
+
+ /* a header line has been parsed successfully */
+
+ h = ngx_list_push(&u->headers_in.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (f->split_parts && f->split_parts->nelts) {
+
+ part = f->split_parts->elts;
+ size = u->buffer.pos - part_start;
+
+ for (i = 0; i < f->split_parts->nelts; i++) {
+ size += part[i].end - part[i].start;
+ }
+
+ p = ngx_pnalloc(r->pool, size);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf.pos = p;
+
+ for (i = 0; i < f->split_parts->nelts; i++) {
+ p = ngx_cpymem(p, part[i].start,
+ part[i].end - part[i].start);
+ }
+
+ p = ngx_cpymem(p, part_start, u->buffer.pos - part_start);
+
+ buf.last = p;
+
+ f->split_parts->nelts = 0;
+
+ rc = ngx_http_parse_header_line(r, &buf, 1);
+
+ if (rc != NGX_OK) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "invalid header after joining "
+ "FastCGI records");
+ return NGX_ERROR;
+ }
+
+ h->key.len = r->header_name_end - r->header_name_start;
+ h->key.data = r->header_name_start;
+ h->key.data[h->key.len] = '\0';
+
+ h->value.len = r->header_end - r->header_start;
+ h->value.data = r->header_start;
+ h->value.data[h->value.len] = '\0';
+
+ h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
+ if (h->lowcase_key == NULL) {
+ return NGX_ERROR;
+ }
+
+ } else {
+
+ h->key.len = r->header_name_end - r->header_name_start;
+ h->value.len = r->header_end - r->header_start;
+
+ h->key.data = ngx_pnalloc(r->pool,
+ h->key.len + 1 + h->value.len + 1
+ + h->key.len);
+ if (h->key.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->value.data = h->key.data + h->key.len + 1;
+ h->lowcase_key = h->key.data + h->key.len + 1
+ + h->value.len + 1;
+
+ ngx_memcpy(h->key.data, r->header_name_start, h->key.len);
+ h->key.data[h->key.len] = '\0';
+ ngx_memcpy(h->value.data, r->header_start, h->value.len);
+ h->value.data[h->value.len] = '\0';
+ }
+
+ h->hash = r->header_hash;
+
+ if (h->key.len == r->lowcase_index) {
+ ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
+
+ } else {
+ ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
+ }
+
+ hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
+ h->lowcase_key, h->key.len);
+
+ if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http fastcgi header: \"%V: %V\"",
+ &h->key, &h->value);
+
+ if (u->buffer.pos < u->buffer.last) {
+ continue;
+ }
+
+ /* the end of the FastCGI record */
+
+ break;
+ }
+
+ if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
+
+ /* a whole header has been parsed successfully */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http fastcgi header done");
+
+ if (u->headers_in.status) {
+ status_line = &u->headers_in.status->value;
+
+ status = ngx_atoi(status_line->data, 3);
+
+ if (status == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent invalid status \"%V\"",
+ status_line);
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+
+ u->headers_in.status_n = status;
+ u->headers_in.status_line = *status_line;
+
+ } else if (u->headers_in.location) {
+ u->headers_in.status_n = 302;
+ ngx_str_set(&u->headers_in.status_line,
+ "302 Moved Temporarily");
+
+ } else {
+ u->headers_in.status_n = 200;
+ ngx_str_set(&u->headers_in.status_line, "200 OK");
+ }
+
+ if (u->state && u->state->status == 0) {
+ u->state->status = u->headers_in.status_n;
+ }
+
+ break;
+ }
+
+ /* there was error while a header line parsing */
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent invalid header");
+
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+
+ if (last) {
+ u->buffer.last = last;
+ }
+
+ f->length -= u->buffer.pos - start;
+
+ if (f->length == 0) {
+ f->state = ngx_http_fastcgi_st_padding;
+ }
+
+ if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
+ return NGX_OK;
+ }
+
+ if (rc == NGX_OK) {
+ continue;
+ }
+
+ /* rc == NGX_AGAIN */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "upstream split a header line in FastCGI records");
+
+ if (f->split_parts == NULL) {
+ f->split_parts = ngx_array_create(r->pool, 1,
+ sizeof(ngx_http_fastcgi_split_part_t));
+ if (f->split_parts == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ part = ngx_array_push(f->split_parts);
+ if (part == NULL) {
+ return NGX_ERROR;
+ }
+
+ part->start = part_start;
+ part->end = part_end;
+
+ if (u->buffer.pos < u->buffer.last) {
+ continue;
+ }
+
+ return NGX_AGAIN;
+ }
+}
+
+
+static ngx_int_t
+ngx_http_fastcgi_input_filter_init(void *data)
+{
+ ngx_http_request_t *r = data;
+ ngx_http_fastcgi_loc_conf_t *flcf;
+
+ flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+ r->upstream->pipe->length = flcf->keep_conn ?
+ (off_t) sizeof(ngx_http_fastcgi_header_t) : -1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
+{
+ u_char *m, *msg;
+ ngx_int_t rc;
+ ngx_buf_t *b, **prev;
+ ngx_chain_t *cl;
+ ngx_http_request_t *r;
+ ngx_http_fastcgi_ctx_t *f;
+ ngx_http_fastcgi_loc_conf_t *flcf;
+
+ if (buf->pos == buf->last) {
+ return NGX_OK;
+ }
+
+ r = p->input_ctx;
+ f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
+ flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+ b = NULL;
+ prev = &buf->shadow;
+
+ f->pos = buf->pos;
+ f->last = buf->last;
+
+ for ( ;; ) {
+ if (f->state < ngx_http_fastcgi_st_data) {
+
+ rc = ngx_http_fastcgi_process_record(r, f);
+
+ if (rc == NGX_AGAIN) {
+ break;
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
+ f->state = ngx_http_fastcgi_st_padding;
+
+ if (!flcf->keep_conn) {
+ p->upstream_done = 1;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
+ "http fastcgi closed stdout");
+
+ continue;
+ }
+
+ if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,
+ "http fastcgi sent end request");
+
+ if (!flcf->keep_conn) {
+ p->upstream_done = 1;
+ break;
+ }
+
+ continue;
+ }
+ }
+
+
+ if (f->state == ngx_http_fastcgi_st_padding) {
+
+ if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
+
+ if (f->pos + f->padding < f->last) {
+ p->upstream_done = 1;
+ break;
+ }
+
+ if (f->pos + f->padding == f->last) {
+ p->upstream_done = 1;
+ r->upstream->keepalive = 1;
+ break;
+ }
+
+ f->padding -= f->last - f->pos;
+
+ break;
+ }
+
+ if (f->pos + f->padding < f->last) {
+ f->state = ngx_http_fastcgi_st_version;
+ f->pos += f->padding;
+
+ continue;
+ }
+
+ if (f->pos + f->padding == f->last) {
+ f->state = ngx_http_fastcgi_st_version;
+
+ break;
+ }
+
+ f->padding -= f->last - f->pos;
+
+ break;
+ }
+
+
+ /* f->state == ngx_http_fastcgi_st_data */
+
+ if (f->type == NGX_HTTP_FASTCGI_STDERR) {
+
+ if (f->length) {
+
+ if (f->pos == f->last) {
+ break;
+ }
+
+ msg = f->pos;
+
+ if (f->pos + f->length <= f->last) {
+ f->pos += f->length;
+ f->length = 0;
+ f->state = ngx_http_fastcgi_st_padding;
+
+ } else {
+ f->length -= f->last - f->pos;
+ f->pos = f->last;
+ }
+
+ for (m = f->pos - 1; msg < m; m--) {
+ if (*m != LF && *m != CR && *m != '.' && *m != ' ') {
+ break;
+ }
+ }
+
+ ngx_log_error(NGX_LOG_ERR, p->log, 0,
+ "FastCGI sent in stderr: \"%*s\"",
+ m + 1 - msg, msg);
+
+ } else {
+ f->state = ngx_http_fastcgi_st_padding;
+ }
+
+ continue;
+ }
+
+ if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
+
+ if (f->pos + f->length <= f->last) {
+ f->state = ngx_http_fastcgi_st_padding;
+ f->pos += f->length;
+
+ continue;
+ }
+
+ f->length -= f->last - f->pos;
+
+ break;
+ }
+
+
+ /* f->type == NGX_HTTP_FASTCGI_STDOUT */
+
+ if (f->pos == f->last) {
+ break;
+ }
+
+ cl = ngx_chain_get_free_buf(p->pool, &p->free);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = cl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->pos = f->pos;
+ b->start = buf->start;
+ b->end = buf->end;
+ b->tag = p->tag;
+ b->temporary = 1;
+ b->recycled = 1;
+
+ *prev = b;
+ prev = &b->shadow;
+
+ if (p->in) {
+ *p->last_in = cl;
+ } else {
+ p->in = cl;
+ }
+ p->last_in = &cl->next;
+
+
+ /* STUB */ b->num = buf->num;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "input buf #%d %p", b->num, b->pos);
+
+ if (f->pos + f->length <= f->last) {
+ f->state = ngx_http_fastcgi_st_padding;
+ f->pos += f->length;
+ b->last = f->pos;
+
+ continue;
+ }
+
+ f->length -= f->last - f->pos;
+
+ b->last = f->last;
+
+ break;
+
+ }
+
+ if (flcf->keep_conn) {
+
+ /* set p->length, minimal amount of data we want to see */
+
+ if (f->state < ngx_http_fastcgi_st_data) {
+ p->length = 1;
+
+ } else if (f->state == ngx_http_fastcgi_st_padding) {
+ p->length = f->padding;
+
+ } else {
+ /* ngx_http_fastcgi_st_data */
+
+ p->length = f->length;
+ }
+ }
+
+ if (b) {
+ b->shadow = buf;
+ b->last_shadow = 1;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "input buf %p %z", b->pos, b->last - b->pos);
+
+ return NGX_OK;
+ }
+
+ /* there is no data record in the buf, add it to free chain */
+
+ if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_fastcgi_non_buffered_filter(void *data, ssize_t bytes)
+{
+ u_char *m, *msg;
+ ngx_int_t rc;
+ ngx_buf_t *b, *buf;
+ ngx_chain_t *cl, **ll;
+ ngx_http_request_t *r;
+ ngx_http_upstream_t *u;
+ ngx_http_fastcgi_ctx_t *f;
+
+ r = data;
+ f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
+
+ u = r->upstream;
+ buf = &u->buffer;
+
+ buf->pos = buf->last;
+ buf->last += bytes;
+
+ for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
+ ll = &cl->next;
+ }
+
+ f->pos = buf->pos;
+ f->last = buf->last;
+
+ for ( ;; ) {
+ if (f->state < ngx_http_fastcgi_st_data) {
+
+ rc = ngx_http_fastcgi_process_record(r, f);
+
+ if (rc == NGX_AGAIN) {
+ break;
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {
+ f->state = ngx_http_fastcgi_st_padding;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http fastcgi closed stdout");
+
+ continue;
+ }
+ }
+
+ if (f->state == ngx_http_fastcgi_st_padding) {
+
+ if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
+
+ if (f->pos + f->padding < f->last) {
+ u->length = 0;
+ break;
+ }
+
+ if (f->pos + f->padding == f->last) {
+ u->length = 0;
+ u->keepalive = 1;
+ break;
+ }
+
+ f->padding -= f->last - f->pos;
+
+ break;
+ }
+
+ if (f->pos + f->padding < f->last) {
+ f->state = ngx_http_fastcgi_st_version;
+ f->pos += f->padding;
+
+ continue;
+ }
+
+ if (f->pos + f->padding == f->last) {
+ f->state = ngx_http_fastcgi_st_version;
+
+ break;
+ }
+
+ f->padding -= f->last - f->pos;
+
+ break;
+ }
+
+
+ /* f->state == ngx_http_fastcgi_st_data */
+
+ if (f->type == NGX_HTTP_FASTCGI_STDERR) {
+
+ if (f->length) {
+
+ if (f->pos == f->last) {
+ break;
+ }
+
+ msg = f->pos;
+
+ if (f->pos + f->length <= f->last) {
+ f->pos += f->length;
+ f->length = 0;
+ f->state = ngx_http_fastcgi_st_padding;
+
+ } else {
+ f->length -= f->last - f->pos;
+ f->pos = f->last;
+ }
+
+ for (m = f->pos - 1; msg < m; m--) {
+ if (*m != LF && *m != CR && *m != '.' && *m != ' ') {
+ break;
+ }
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "FastCGI sent in stderr: \"%*s\"",
+ m + 1 - msg, msg);
+
+ } else {
+ f->state = ngx_http_fastcgi_st_padding;
+ }
+
+ continue;
+ }
+
+ if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {
+
+ if (f->pos + f->length <= f->last) {
+ f->state = ngx_http_fastcgi_st_padding;
+ f->pos += f->length;
+
+ continue;
+ }
+
+ f->length -= f->last - f->pos;
+
+ break;
+ }
+
+
+ /* f->type == NGX_HTTP_FASTCGI_STDOUT */
+
+ if (f->pos == f->last) {
+ break;
+ }
+
+ cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ll = cl;
+ ll = &cl->next;
+
+ b = cl->buf;
+
+ b->flush = 1;
+ b->memory = 1;
+
+ b->pos = f->pos;
+ b->tag = u->output.tag;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http fastcgi output buf %p", b->pos);
+
+ if (f->pos + f->length <= f->last) {
+ f->state = ngx_http_fastcgi_st_padding;
+ f->pos += f->length;
+ b->last = f->pos;
+
+ continue;
+ }
+
+ f->length -= f->last - f->pos;
+ b->last = f->last;
+
+ break;
+ }
+
+ /* provide continuous buffer for subrequests in memory */
+
+ if (r->subrequest_in_memory) {
+
+ cl = u->out_bufs;
+
+ if (cl) {
+ buf->pos = cl->buf->pos;
+ }
+
+ buf->last = buf->pos;
+
+ for (cl = u->out_bufs; cl; cl = cl->next) {
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http fastcgi in memory %p-%p %uz",
+ cl->buf->pos, cl->buf->last, ngx_buf_size(cl->buf));
+
+ if (buf->last == cl->buf->pos) {
+ buf->last = cl->buf->last;
+ continue;
+ }
+
+ buf->last = ngx_movemem(buf->last, cl->buf->pos,
+ cl->buf->last - cl->buf->pos);
+
+ cl->buf->pos = buf->last - (cl->buf->last - cl->buf->pos);
+ cl->buf->last = buf->last;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_fastcgi_process_record(ngx_http_request_t *r,
+ ngx_http_fastcgi_ctx_t *f)
+{
+ u_char ch, *p;
+ ngx_http_fastcgi_state_e state;
+
+ state = f->state;
+
+ for (p = f->pos; p < f->last; p++) {
+
+ ch = *p;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http fastcgi record byte: %02Xd", ch);
+
+ switch (state) {
+
+ case ngx_http_fastcgi_st_version:
+ if (ch != 1) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent unsupported FastCGI "
+ "protocol version: %d", ch);
+ return NGX_ERROR;
+ }
+ state = ngx_http_fastcgi_st_type;
+ break;
+
+ case ngx_http_fastcgi_st_type:
+ switch (ch) {
+ case NGX_HTTP_FASTCGI_STDOUT:
+ case NGX_HTTP_FASTCGI_STDERR:
+ case NGX_HTTP_FASTCGI_END_REQUEST:
+ f->type = (ngx_uint_t) ch;
+ break;
+ default:
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent invalid FastCGI "
+ "record type: %d", ch);
+ return NGX_ERROR;
+
+ }
+ state = ngx_http_fastcgi_st_request_id_hi;
+ break;
+
+ /* we support the single request per connection */
+
+ case ngx_http_fastcgi_st_request_id_hi:
+ if (ch != 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent unexpected FastCGI "
+ "request id high byte: %d", ch);
+ return NGX_ERROR;
+ }
+ state = ngx_http_fastcgi_st_request_id_lo;
+ break;
+
+ case ngx_http_fastcgi_st_request_id_lo:
+ if (ch != 1) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent unexpected FastCGI "
+ "request id low byte: %d", ch);
+ return NGX_ERROR;
+ }
+ state = ngx_http_fastcgi_st_content_length_hi;
+ break;
+
+ case ngx_http_fastcgi_st_content_length_hi:
+ f->length = ch << 8;
+ state = ngx_http_fastcgi_st_content_length_lo;
+ break;
+
+ case ngx_http_fastcgi_st_content_length_lo:
+ f->length |= (size_t) ch;
+ state = ngx_http_fastcgi_st_padding_length;
+ break;
+
+ case ngx_http_fastcgi_st_padding_length:
+ f->padding = (size_t) ch;
+ state = ngx_http_fastcgi_st_reserved;
+ break;
+
+ case ngx_http_fastcgi_st_reserved:
+ state = ngx_http_fastcgi_st_data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http fastcgi record length: %z", f->length);
+
+ f->pos = p + 1;
+ f->state = state;
+
+ return NGX_OK;
+
+ /* suppress warning */
+ case ngx_http_fastcgi_st_data:
+ case ngx_http_fastcgi_st_padding:
+ break;
+ }
+ }
+
+ f->state = state;
+
+ return NGX_AGAIN;
+}
+
+
+static void
+ngx_http_fastcgi_abort_request(ngx_http_request_t *r)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "abort http fastcgi request");
+
+ return;
+}
+
+
+static void
+ngx_http_fastcgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "finalize http fastcgi request");
+
+ return;
+}
+
+
+static ngx_int_t
+ngx_http_fastcgi_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var, *v;
+
+ for (v = ngx_http_fastcgi_vars; v->name.len; v++) {
+ var = ngx_http_add_variable(cf, &v->name, v->flags);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = v->get_handler;
+ var->data = v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_fastcgi_loc_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->upstream.bufs.num = 0;
+ * conf->upstream.ignore_headers = 0;
+ * conf->upstream.next_upstream = 0;
+ * conf->upstream.cache_use_stale = 0;
+ * conf->upstream.cache_methods = 0;
+ * conf->upstream.temp_path = NULL;
+ * conf->upstream.hide_headers_hash = { NULL, 0 };
+ * conf->upstream.uri = { 0, NULL };
+ * conf->upstream.location = NULL;
+ * conf->upstream.store_lengths = NULL;
+ * conf->upstream.store_values = NULL;
+ *
+ * conf->index.len = { 0, NULL };
+ */
+
+ conf->upstream.store = NGX_CONF_UNSET;
+ conf->upstream.store_access = NGX_CONF_UNSET_UINT;
+ conf->upstream.buffering = NGX_CONF_UNSET;
+ conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
+
+ conf->upstream.local = NGX_CONF_UNSET_PTR;
+
+ conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
+
+ conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;
+ conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
+
+ conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;
+ conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
+ conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;
+
+ conf->upstream.pass_request_headers = NGX_CONF_UNSET;
+ conf->upstream.pass_request_body = NGX_CONF_UNSET;
+
+#if (NGX_HTTP_CACHE)
+ conf->upstream.cache = NGX_CONF_UNSET_PTR;
+ conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;
+ conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;
+ conf->upstream.no_cache = NGX_CONF_UNSET_PTR;
+ conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;
+ conf->upstream.cache_lock = NGX_CONF_UNSET;
+ conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.cache_revalidate = NGX_CONF_UNSET;
+#endif
+
+ conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
+ conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
+
+ conf->upstream.intercept_errors = NGX_CONF_UNSET;
+
+ /* "fastcgi_cyclic_temp_file" is disabled */
+ conf->upstream.cyclic_temp_file = 0;
+
+ conf->upstream.change_buffering = 1;
+
+ conf->catch_stderr = NGX_CONF_UNSET_PTR;
+
+ conf->keep_conn = NGX_CONF_UNSET;
+
+ ngx_str_set(&conf->upstream.module, "fastcgi");
+
+ return conf;
+}
+
+
+static char *
+ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_fastcgi_loc_conf_t *prev = parent;
+ ngx_http_fastcgi_loc_conf_t *conf = child;
+
+ size_t size;
+ ngx_hash_init_t hash;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (conf->upstream.store != 0) {
+ ngx_conf_merge_value(conf->upstream.store,
+ prev->upstream.store, 0);
+
+ if (conf->upstream.store_lengths == NULL) {
+ conf->upstream.store_lengths = prev->upstream.store_lengths;
+ conf->upstream.store_values = prev->upstream.store_values;
+ }
+ }
+
+ ngx_conf_merge_uint_value(conf->upstream.store_access,
+ prev->upstream.store_access, 0600);
+
+ ngx_conf_merge_value(conf->upstream.buffering,
+ prev->upstream.buffering, 1);
+
+ ngx_conf_merge_value(conf->upstream.ignore_client_abort,
+ prev->upstream.ignore_client_abort, 0);
+
+ ngx_conf_merge_ptr_value(conf->upstream.local,
+ prev->upstream.local, NULL);
+
+ ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
+ prev->upstream.connect_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->upstream.send_timeout,
+ prev->upstream.send_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->upstream.read_timeout,
+ prev->upstream.read_timeout, 60000);
+
+ ngx_conf_merge_size_value(conf->upstream.send_lowat,
+ prev->upstream.send_lowat, 0);
+
+ ngx_conf_merge_size_value(conf->upstream.buffer_size,
+ prev->upstream.buffer_size,
+ (size_t) ngx_pagesize);
+
+
+ ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,
+ 8, ngx_pagesize);
+
+ if (conf->upstream.bufs.num < 2) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "there must be at least 2 \"fastcgi_buffers\"");
+ return NGX_CONF_ERROR;
+ }
+
+
+ size = conf->upstream.buffer_size;
+ if (size < conf->upstream.bufs.size) {
+ size = conf->upstream.bufs.size;
+ }
+
+
+ ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,
+ prev->upstream.busy_buffers_size_conf,
+ NGX_CONF_UNSET_SIZE);
+
+ if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {
+ conf->upstream.busy_buffers_size = 2 * size;
+ } else {
+ conf->upstream.busy_buffers_size =
+ conf->upstream.busy_buffers_size_conf;
+ }
+
+ if (conf->upstream.busy_buffers_size < size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"fastcgi_busy_buffers_size\" must be equal to or greater than "
+ "the maximum of the value of \"fastcgi_buffer_size\" and "
+ "one of the \"fastcgi_buffers\"");
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->upstream.busy_buffers_size
+ > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"fastcgi_busy_buffers_size\" must be less than "
+ "the size of all \"fastcgi_buffers\" minus one buffer");
+
+ return NGX_CONF_ERROR;
+ }
+
+
+ ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,
+ prev->upstream.temp_file_write_size_conf,
+ NGX_CONF_UNSET_SIZE);
+
+ if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {
+ conf->upstream.temp_file_write_size = 2 * size;
+ } else {
+ conf->upstream.temp_file_write_size =
+ conf->upstream.temp_file_write_size_conf;
+ }
+
+ if (conf->upstream.temp_file_write_size < size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"fastcgi_temp_file_write_size\" must be equal to or greater "
+ "than the maximum of the value of \"fastcgi_buffer_size\" and "
+ "one of the \"fastcgi_buffers\"");
+
+ return NGX_CONF_ERROR;
+ }
+
+
+ ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,
+ prev->upstream.max_temp_file_size_conf,
+ NGX_CONF_UNSET_SIZE);
+
+ if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {
+ conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;
+ } else {
+ conf->upstream.max_temp_file_size =
+ conf->upstream.max_temp_file_size_conf;
+ }
+
+ if (conf->upstream.max_temp_file_size != 0
+ && conf->upstream.max_temp_file_size < size)
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"fastcgi_max_temp_file_size\" must be equal to zero to disable "
+ "temporary files usage or must be equal to or greater than "
+ "the maximum of the value of \"fastcgi_buffer_size\" and "
+ "one of the \"fastcgi_buffers\"");
+
+ return NGX_CONF_ERROR;
+ }
+
+
+ ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
+ prev->upstream.ignore_headers,
+ NGX_CONF_BITMASK_SET);
+
+
+ ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
+ prev->upstream.next_upstream,
+ (NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_ERROR
+ |NGX_HTTP_UPSTREAM_FT_TIMEOUT));
+
+ if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
+ conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_OFF;
+ }
+
+ if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
+ prev->upstream.temp_path,
+ &ngx_http_fastcgi_temp_path)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ ngx_conf_merge_ptr_value(conf->upstream.cache,
+ prev->upstream.cache, NULL);
+
+ if (conf->upstream.cache && conf->upstream.cache->data == NULL) {
+ ngx_shm_zone_t *shm_zone;
+
+ shm_zone = conf->upstream.cache;
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"fastcgi_cache\" zone \"%V\" is unknown",
+ &shm_zone->shm.name);
+
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,
+ prev->upstream.cache_min_uses, 1);
+
+ ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,
+ prev->upstream.cache_use_stale,
+ (NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_OFF));
+
+ if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {
+ conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_OFF;
+ }
+
+ if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {
+ conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;
+ }
+
+ if (conf->upstream.cache_methods == 0) {
+ conf->upstream.cache_methods = prev->upstream.cache_methods;
+ }
+
+ conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;
+
+ ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,
+ prev->upstream.cache_bypass, NULL);
+
+ ngx_conf_merge_ptr_value(conf->upstream.no_cache,
+ prev->upstream.no_cache, NULL);
+
+ ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
+ prev->upstream.cache_valid, NULL);
+
+ if (conf->cache_key.value.data == NULL) {
+ conf->cache_key = prev->cache_key;
+ }
+
+ ngx_conf_merge_value(conf->upstream.cache_lock,
+ prev->upstream.cache_lock, 0);
+
+ ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,
+ prev->upstream.cache_lock_timeout, 5000);
+
+ ngx_conf_merge_value(conf->upstream.cache_revalidate,
+ prev->upstream.cache_revalidate, 0);
+
+#endif
+
+ ngx_conf_merge_value(conf->upstream.pass_request_headers,
+ prev->upstream.pass_request_headers, 1);
+ ngx_conf_merge_value(conf->upstream.pass_request_body,
+ prev->upstream.pass_request_body, 1);
+
+ ngx_conf_merge_value(conf->upstream.intercept_errors,
+ prev->upstream.intercept_errors, 0);
+
+ ngx_conf_merge_ptr_value(conf->catch_stderr, prev->catch_stderr, NULL);
+
+ ngx_conf_merge_value(conf->keep_conn, prev->keep_conn, 0);
+
+
+ ngx_conf_merge_str_value(conf->index, prev->index, "");
+
+ hash.max_size = 512;
+ hash.bucket_size = ngx_align(64, ngx_cacheline_size);
+ hash.name = "fastcgi_hide_headers_hash";
+
+ if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
+ &prev->upstream, ngx_http_fastcgi_hide_headers, &hash)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->upstream.upstream == NULL) {
+ conf->upstream.upstream = prev->upstream.upstream;
+ }
+
+ if (conf->fastcgi_lengths == NULL) {
+ conf->fastcgi_lengths = prev->fastcgi_lengths;
+ conf->fastcgi_values = prev->fastcgi_values;
+ }
+
+ if (conf->upstream.upstream || conf->fastcgi_lengths) {
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+ if (clcf->handler == NULL && clcf->lmt_excpt) {
+ clcf->handler = ngx_http_fastcgi_handler;
+ }
+ }
+
+#if (NGX_PCRE)
+ if (conf->split_regex == NULL) {
+ conf->split_regex = prev->split_regex;
+ conf->split_name = prev->split_name;
+ }
+#endif
+
+ if (ngx_http_fastcgi_merge_params(cf, conf, prev) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_fastcgi_merge_params(ngx_conf_t *cf,
+ ngx_http_fastcgi_loc_conf_t *conf, ngx_http_fastcgi_loc_conf_t *prev)
+{
+ u_char *p;
+ size_t size;
+ uintptr_t *code;
+ ngx_uint_t i, nsrc;
+ ngx_array_t headers_names;
+#if (NGX_HTTP_CACHE)
+ ngx_array_t params_merged;
+#endif
+ ngx_hash_key_t *hk;
+ ngx_hash_init_t hash;
+ ngx_http_upstream_param_t *src;
+ ngx_http_script_compile_t sc;
+ ngx_http_script_copy_code_t *copy;
+
+ if (conf->params_source == NULL) {
+ conf->params_source = prev->params_source;
+
+ if (prev->headers_hash.buckets
+#if (NGX_HTTP_CACHE)
+ && ((conf->upstream.cache == NULL)
+ == (prev->upstream.cache == NULL))
+#endif
+ )
+ {
+ conf->flushes = prev->flushes;
+ conf->params_len = prev->params_len;
+ conf->params = prev->params;
+ conf->headers_hash = prev->headers_hash;
+ conf->header_params = prev->header_params;
+
+ return NGX_OK;
+ }
+ }
+
+ if (conf->params_source == NULL
+#if (NGX_HTTP_CACHE)
+ && (conf->upstream.cache == NULL)
+#endif
+ )
+ {
+ conf->headers_hash.buckets = (void *) 1;
+ return NGX_OK;
+ }
+
+ conf->params_len = ngx_array_create(cf->pool, 64, 1);
+ if (conf->params_len == NULL) {
+ return NGX_ERROR;
+ }
+
+ conf->params = ngx_array_create(cf->pool, 512, 1);
+ if (conf->params == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (conf->params_source) {
+ src = conf->params_source->elts;
+ nsrc = conf->params_source->nelts;
+
+ } else {
+ src = NULL;
+ nsrc = 0;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (conf->upstream.cache) {
+ ngx_keyval_t *h;
+ ngx_http_upstream_param_t *s;
+
+ if (ngx_array_init(&params_merged, cf->temp_pool, 4,
+ sizeof(ngx_http_upstream_param_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ for (i = 0; i < nsrc; i++) {
+
+ s = ngx_array_push(&params_merged);
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ *s = src[i];
+ }
+
+ h = ngx_http_fastcgi_cache_headers;
+
+ while (h->key.len) {
+
+ src = params_merged.elts;
+ nsrc = params_merged.nelts;
+
+ for (i = 0; i < nsrc; i++) {
+ if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {
+ goto next;
+ }
+ }
+
+ s = ngx_array_push(&params_merged);
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ s->key = h->key;
+ s->value = h->value;
+ s->skip_empty = 1;
+
+ next:
+
+ h++;
+ }
+
+ src = params_merged.elts;
+ nsrc = params_merged.nelts;
+ }
+
+#endif
+
+ for (i = 0; i < nsrc; i++) {
+
+ if (src[i].key.len > sizeof("HTTP_") - 1
+ && ngx_strncmp(src[i].key.data, "HTTP_", sizeof("HTTP_") - 1) == 0)
+ {
+ hk = ngx_array_push(&headers_names);
+ if (hk == NULL) {
+ return NGX_ERROR;
+ }
+
+ hk->key.len = src[i].key.len - 5;
+ hk->key.data = src[i].key.data + 5;
+ hk->key_hash = ngx_hash_key_lc(hk->key.data, hk->key.len);
+ hk->value = (void *) 1;
+
+ if (src[i].value.len == 0) {
+ continue;
+ }
+ }
+
+ copy = ngx_array_push_n(conf->params_len,
+ sizeof(ngx_http_script_copy_code_t));
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+ copy->len = src[i].key.len;
+
+ copy = ngx_array_push_n(conf->params_len,
+ sizeof(ngx_http_script_copy_code_t));
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+ copy->len = src[i].skip_empty;
+
+
+ size = (sizeof(ngx_http_script_copy_code_t)
+ + src[i].key.len + sizeof(uintptr_t) - 1)
+ & ~(sizeof(uintptr_t) - 1);
+
+ copy = ngx_array_push_n(conf->params, size);
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = ngx_http_script_copy_code;
+ copy->len = src[i].key.len;
+
+ p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
+ ngx_memcpy(p, src[i].key.data, src[i].key.len);
+
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &src[i].value;
+ sc.flushes = &conf->flushes;
+ sc.lengths = &conf->params_len;
+ sc.values = &conf->params;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+
+
+ code = ngx_array_push_n(conf->params, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+ }
+
+ code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+
+ conf->header_params = headers_names.nelts;
+
+ hash.hash = &conf->headers_hash;
+ hash.key = ngx_hash_key_lc;
+ hash.max_size = 512;
+ hash.bucket_size = 64;
+ hash.name = "fastcgi_params_hash";
+ hash.pool = cf->pool;
+ hash.temp_pool = NULL;
+
+ return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);
+}
+
+
+static ngx_int_t
+ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ ngx_http_fastcgi_ctx_t *f;
+ ngx_http_fastcgi_loc_conf_t *flcf;
+
+ flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+ f = ngx_http_fastcgi_split(r, flcf);
+
+ if (f == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (f->script_name.len == 0
+ || f->script_name.data[f->script_name.len - 1] != '/')
+ {
+ v->len = f->script_name.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = f->script_name.data;
+
+ return NGX_OK;
+ }
+
+ v->len = f->script_name.len + flcf->index.len;
+
+ v->data = ngx_pnalloc(r->pool, v->len);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_copy(v->data, f->script_name.data, f->script_name.len);
+ ngx_memcpy(p, flcf->index.data, flcf->index.len);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_fastcgi_ctx_t *f;
+ ngx_http_fastcgi_loc_conf_t *flcf;
+
+ flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
+
+ f = ngx_http_fastcgi_split(r, flcf);
+
+ if (f == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = f->path_info.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = f->path_info.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_http_fastcgi_ctx_t *
+ngx_http_fastcgi_split(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)
+{
+ ngx_http_fastcgi_ctx_t *f;
+#if (NGX_PCRE)
+ ngx_int_t n;
+ int captures[(1 + 2) * 3];
+
+ f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
+
+ if (f == NULL) {
+ f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
+ if (f == NULL) {
+ return NULL;
+ }
+
+ ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
+ }
+
+ if (f->script_name.len) {
+ return f;
+ }
+
+ if (flcf->split_regex == NULL) {
+ f->script_name = r->uri;
+ return f;
+ }
+
+ n = ngx_regex_exec(flcf->split_regex, &r->uri, captures, (1 + 2) * 3);
+
+ if (n >= 0) { /* match */
+ f->script_name.len = captures[3] - captures[2];
+ f->script_name.data = r->uri.data + captures[2];
+
+ f->path_info.len = captures[5] - captures[4];
+ f->path_info.data = r->uri.data + captures[4];
+
+ return f;
+ }
+
+ if (n == NGX_REGEX_NO_MATCHED) {
+ f->script_name = r->uri;
+ return f;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
+ n, &r->uri, &flcf->split_name);
+ return NULL;
+
+#else
+
+ f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
+
+ if (f == NULL) {
+ f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
+ if (f == NULL) {
+ return NULL;
+ }
+
+ ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
+ }
+
+ f->script_name = r->uri;
+
+ return f;
+
+#endif
+}
+
+
+static char *
+ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_fastcgi_loc_conf_t *flcf = conf;
+
+ ngx_url_t u;
+ ngx_str_t *value, *url;
+ ngx_uint_t n;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_script_compile_t sc;
+
+ if (flcf->upstream.upstream || flcf->fastcgi_lengths) {
+ return "is duplicate";
+ }
+
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+
+ clcf->handler = ngx_http_fastcgi_handler;
+
+ if (clcf->name.data[clcf->name.len - 1] == '/') {
+ clcf->auto_redirect = 1;
+ }
+
+ value = cf->args->elts;
+
+ url = &value[1];
+
+ n = ngx_http_script_variables_count(url);
+
+ if (n) {
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = url;
+ sc.lengths = &flcf->fastcgi_lengths;
+ sc.values = &flcf->fastcgi_values;
+ sc.variables = n;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = value[1];
+ u.no_resolve = 1;
+
+ flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
+ if (flcf->upstream.upstream == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_fastcgi_split_path_info(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+#if (NGX_PCRE)
+ ngx_http_fastcgi_loc_conf_t *flcf = conf;
+
+ ngx_str_t *value;
+ ngx_regex_compile_t rc;
+ u_char errstr[NGX_MAX_CONF_ERRSTR];
+
+ value = cf->args->elts;
+
+ flcf->split_name = value[1];
+
+ ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+ rc.pattern = value[1];
+ rc.pool = cf->pool;
+ rc.err.len = NGX_MAX_CONF_ERRSTR;
+ rc.err.data = errstr;
+
+ if (ngx_regex_compile(&rc) != NGX_OK) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
+ return NGX_CONF_ERROR;
+ }
+
+ if (rc.captures != 2) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "pattern \"%V\" must have 2 captures", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ flcf->split_regex = rc.regex;
+
+ return NGX_CONF_OK;
+
+#else
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"%V\" requires PCRE library", &cmd->name);
+ return NGX_CONF_ERROR;
+
+#endif
+}
+
+
+static char *
+ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_fastcgi_loc_conf_t *flcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_script_compile_t sc;
+
+ if (flcf->upstream.store != NGX_CONF_UNSET
+ || flcf->upstream.store_lengths)
+ {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ flcf->upstream.store = 0;
+ return NGX_CONF_OK;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (flcf->upstream.cache != NGX_CONF_UNSET_PTR
+ && flcf->upstream.cache != NULL)
+ {
+ return "is incompatible with \"fastcgi_cache\"";
+ }
+
+#endif
+
+ if (ngx_strcmp(value[1].data, "on") == 0) {
+ flcf->upstream.store = 1;
+ return NGX_CONF_OK;
+ }
+
+ /* include the terminating '\0' into script */
+ value[1].len++;
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &value[1];
+ sc.lengths = &flcf->upstream.store_lengths;
+ sc.values = &flcf->upstream.store_values;
+ sc.variables = ngx_http_script_variables_count(&value[1]);
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+#if (NGX_HTTP_CACHE)
+
+static char *
+ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_fastcgi_loc_conf_t *flcf = conf;
+
+ ngx_str_t *value;
+
+ value = cf->args->elts;
+
+ if (flcf->upstream.cache != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ flcf->upstream.cache = NULL;
+ return NGX_CONF_OK;
+ }
+
+ if (flcf->upstream.store > 0 || flcf->upstream.store_lengths) {
+ return "is incompatible with \"fastcgi_store\"";
+ }
+
+ flcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0,
+ &ngx_http_fastcgi_module);
+ if (flcf->upstream.cache == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_fastcgi_loc_conf_t *flcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_compile_complex_value_t ccv;
+
+ value = cf->args->elts;
+
+ if (flcf->cache_key.value.data) {
+ return "is duplicate";
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &flcf->cache_key;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+#endif
+
+
+static char *
+ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post, void *data)
+{
+#if (NGX_FREEBSD)
+ ssize_t *np = data;
+
+ if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"fastcgi_send_lowat\" must be less than %d "
+ "(sysctl net.inet.tcp.sendspace)",
+ ngx_freebsd_net_inet_tcp_sendspace);
+
+ return NGX_CONF_ERROR;
+ }
+
+#elif !(NGX_HAVE_SO_SNDLOWAT)
+ ssize_t *np = data;
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "\"fastcgi_send_lowat\" is not supported, ignored");
+
+ *np = 0;
+
+#endif
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_flv_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_flv_module.c
new file mode 100644
index 00000000000..cc2532027cc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_flv_module.c
@@ -0,0 +1,266 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static char *ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+
+static ngx_command_t ngx_http_flv_commands[] = {
+
+ { ngx_string("flv"),
+ NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
+ ngx_http_flv,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static u_char ngx_flv_header[] = "FLV\x1\x5\0\0\0\x9\0\0\0\0";
+
+
+static ngx_http_module_t ngx_http_flv_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_flv_module = {
+ NGX_MODULE_V1,
+ &ngx_http_flv_module_ctx, /* module context */
+ ngx_http_flv_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_flv_handler(ngx_http_request_t *r)
+{
+ u_char *last;
+ off_t start, len;
+ size_t root;
+ ngx_int_t rc;
+ ngx_uint_t level, i;
+ ngx_str_t path, value;
+ ngx_log_t *log;
+ ngx_buf_t *b;
+ ngx_chain_t out[2];
+ ngx_open_file_info_t of;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
+ return NGX_HTTP_NOT_ALLOWED;
+ }
+
+ if (r->uri.data[r->uri.len - 1] == '/') {
+ return NGX_DECLINED;
+ }
+
+ rc = ngx_http_discard_request_body(r);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ last = ngx_http_map_uri_to_path(r, &path, &root, 0);
+ if (last == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ log = r->connection->log;
+
+ path.len = last - path.data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
+ "http flv filename: \"%V\"", &path);
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+ of.read_ahead = clcf->read_ahead;
+ of.directio = clcf->directio;
+ of.valid = clcf->open_file_cache_valid;
+ of.min_uses = clcf->open_file_cache_min_uses;
+ of.errors = clcf->open_file_cache_errors;
+ of.events = clcf->open_file_cache_events;
+
+ if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+ != NGX_OK)
+ {
+ switch (of.err) {
+
+ case 0:
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+
+ case NGX_ENOENT:
+ case NGX_ENOTDIR:
+ case NGX_ENAMETOOLONG:
+
+ level = NGX_LOG_ERR;
+ rc = NGX_HTTP_NOT_FOUND;
+ break;
+
+ case NGX_EACCES:
+#if (NGX_HAVE_OPENAT)
+ case NGX_EMLINK:
+ case NGX_ELOOP:
+#endif
+
+ level = NGX_LOG_ERR;
+ rc = NGX_HTTP_FORBIDDEN;
+ break;
+
+ default:
+
+ level = NGX_LOG_CRIT;
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ break;
+ }
+
+ if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
+ ngx_log_error(level, log, of.err,
+ "%s \"%s\" failed", of.failed, path.data);
+ }
+
+ return rc;
+ }
+
+ if (!of.is_file) {
+
+ if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", path.data);
+ }
+
+ return NGX_DECLINED;
+ }
+
+ r->root_tested = !r->error_page;
+
+ start = 0;
+ len = of.size;
+ i = 1;
+
+ if (r->args.len) {
+
+ if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {
+
+ start = ngx_atoof(value.data, value.len);
+
+ if (start == NGX_ERROR || start >= len) {
+ start = 0;
+ }
+
+ if (start) {
+ len = sizeof(ngx_flv_header) - 1 + len - start;
+ i = 0;
+ }
+ }
+ }
+
+ log->action = "sending flv to client";
+
+ r->headers_out.status = NGX_HTTP_OK;
+ r->headers_out.content_length_n = len;
+ r->headers_out.last_modified_time = of.mtime;
+
+ if (ngx_http_set_etag(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_http_set_content_type(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (i == 0) {
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b->pos = ngx_flv_header;
+ b->last = ngx_flv_header + sizeof(ngx_flv_header) - 1;
+ b->memory = 1;
+
+ out[0].buf = b;
+ out[0].next = &out[1];
+ }
+
+
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
+ if (b->file == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ r->allow_ranges = 1;
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ return rc;
+ }
+
+ b->file_pos = start;
+ b->file_last = of.size;
+
+ b->in_file = b->file_last ? 1: 0;
+ b->last_buf = (r == r->main) ? 1 : 0;
+ b->last_in_chain = 1;
+
+ b->file->fd = of.fd;
+ b->file->name = path;
+ b->file->log = log;
+ b->file->directio = of.is_directio;
+
+ out[1].buf = b;
+ out[1].next = NULL;
+
+ return ngx_http_output_filter(r, &out[i]);
+}
+
+
+static char *
+ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+ clcf->handler = ngx_http_flv_handler;
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_geo_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_geo_module.c
new file mode 100644
index 00000000000..9b3c6cb9cea
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_geo_module.c
@@ -0,0 +1,1644 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_http_variable_value_t *value;
+ u_short start;
+ u_short end;
+} ngx_http_geo_range_t;
+
+
+typedef struct {
+ ngx_radix_tree_t *tree;
+#if (NGX_HAVE_INET6)
+ ngx_radix_tree_t *tree6;
+#endif
+} ngx_http_geo_trees_t;
+
+
+typedef struct {
+ ngx_http_geo_range_t **low;
+ ngx_http_variable_value_t *default_value;
+} ngx_http_geo_high_ranges_t;
+
+
+typedef struct {
+ ngx_str_node_t sn;
+ ngx_http_variable_value_t *value;
+ size_t offset;
+} ngx_http_geo_variable_value_node_t;
+
+
+typedef struct {
+ ngx_http_variable_value_t *value;
+ ngx_str_t *net;
+ ngx_http_geo_high_ranges_t high;
+ ngx_radix_tree_t *tree;
+#if (NGX_HAVE_INET6)
+ ngx_radix_tree_t *tree6;
+#endif
+ ngx_rbtree_t rbtree;
+ ngx_rbtree_node_t sentinel;
+ ngx_array_t *proxies;
+ ngx_pool_t *pool;
+ ngx_pool_t *temp_pool;
+
+ size_t data_size;
+
+ ngx_str_t include_name;
+ ngx_uint_t includes;
+ ngx_uint_t entries;
+
+ unsigned ranges:1;
+ unsigned outside_entries:1;
+ unsigned allow_binary_include:1;
+ unsigned binary_include:1;
+ unsigned proxy_recursive:1;
+} ngx_http_geo_conf_ctx_t;
+
+
+typedef struct {
+ union {
+ ngx_http_geo_trees_t trees;
+ ngx_http_geo_high_ranges_t high;
+ } u;
+
+ ngx_array_t *proxies;
+ unsigned proxy_recursive:1;
+
+ ngx_int_t index;
+} ngx_http_geo_ctx_t;
+
+
+static ngx_int_t ngx_http_geo_addr(ngx_http_request_t *r,
+ ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
+static ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r,
+ ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);
+static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
+static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+ ngx_str_t *value);
+static char *ngx_http_geo_add_range(ngx_conf_t *cf,
+ ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
+static ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,
+ ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);
+static char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+ ngx_str_t *value);
+static char *ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+ ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net);
+static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
+ ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
+static char *ngx_http_geo_add_proxy(ngx_conf_t *cf,
+ ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);
+static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
+ ngx_cidr_t *cidr);
+static char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+ ngx_str_t *name);
+static ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf,
+ ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name);
+static void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx);
+static u_char *ngx_http_geo_copy_values(u_char *base, u_char *p,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+
+
+static ngx_command_t ngx_http_geo_commands[] = {
+
+ { ngx_string("geo"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
+ ngx_http_geo_block,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_geo_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_geo_module = {
+ NGX_MODULE_V1,
+ &ngx_http_geo_module_ctx, /* module context */
+ ngx_http_geo_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+typedef struct {
+ u_char GEORNG[6];
+ u_char version;
+ u_char ptr_size;
+ uint32_t endianness;
+ uint32_t crc32;
+} ngx_http_geo_header_t;
+
+
+static ngx_http_geo_header_t ngx_http_geo_header = {
+ { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0
+};
+
+
+/* geo range is AF_INET only */
+
+static ngx_int_t
+ngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
+
+ in_addr_t inaddr;
+ ngx_addr_t addr;
+ struct sockaddr_in *sin;
+ ngx_http_variable_value_t *vv;
+#if (NGX_HAVE_INET6)
+ u_char *p;
+ struct in6_addr *inaddr6;
+#endif
+
+ if (ngx_http_geo_addr(r, ctx, &addr) != NGX_OK) {
+ vv = (ngx_http_variable_value_t *)
+ ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);
+ goto done;
+ }
+
+ switch (addr.sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
+ p = inaddr6->s6_addr;
+
+ if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
+ inaddr = p[12] << 24;
+ inaddr += p[13] << 16;
+ inaddr += p[14] << 8;
+ inaddr += p[15];
+
+ vv = (ngx_http_variable_value_t *)
+ ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
+
+ } else {
+ vv = (ngx_http_variable_value_t *)
+ ngx_radix128tree_find(ctx->u.trees.tree6, p);
+ }
+
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) addr.sockaddr;
+ inaddr = ntohl(sin->sin_addr.s_addr);
+
+ vv = (ngx_http_variable_value_t *)
+ ngx_radix32tree_find(ctx->u.trees.tree, inaddr);
+
+ break;
+ }
+
+done:
+
+ *v = *vv;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http geo: %v", v);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;
+
+ in_addr_t inaddr;
+ ngx_addr_t addr;
+ ngx_uint_t n;
+ struct sockaddr_in *sin;
+ ngx_http_geo_range_t *range;
+#if (NGX_HAVE_INET6)
+ u_char *p;
+ struct in6_addr *inaddr6;
+#endif
+
+ *v = *ctx->u.high.default_value;
+
+ if (ngx_http_geo_addr(r, ctx, &addr) == NGX_OK) {
+
+ switch (addr.sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
+
+ if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
+ p = inaddr6->s6_addr;
+
+ inaddr = p[12] << 24;
+ inaddr += p[13] << 16;
+ inaddr += p[14] << 8;
+ inaddr += p[15];
+
+ } else {
+ inaddr = INADDR_NONE;
+ }
+
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) addr.sockaddr;
+ inaddr = ntohl(sin->sin_addr.s_addr);
+ break;
+ }
+
+ } else {
+ inaddr = INADDR_NONE;
+ }
+
+ if (ctx->u.high.low) {
+ range = ctx->u.high.low[inaddr >> 16];
+
+ if (range) {
+ n = inaddr & 0xffff;
+ do {
+ if (n >= (ngx_uint_t) range->start
+ && n <= (ngx_uint_t) range->end)
+ {
+ *v = *range->value;
+ break;
+ }
+ } while ((++range)->value);
+ }
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http geo: %v", v);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
+ ngx_addr_t *addr)
+{
+ ngx_array_t *xfwd;
+
+ if (ngx_http_geo_real_addr(r, ctx, addr) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ xfwd = &r->headers_in.x_forwarded_for;
+
+ if (xfwd->nelts > 0 && ctx->proxies != NULL) {
+ (void) ngx_http_get_forwarded_addr(r, addr, xfwd, NULL,
+ ctx->proxies, ctx->proxy_recursive);
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,
+ ngx_addr_t *addr)
+{
+ ngx_http_variable_value_t *v;
+
+ if (ctx->index == -1) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http geo started: %V", &r->connection->addr_text);
+
+ addr->sockaddr = r->connection->sockaddr;
+ addr->socklen = r->connection->socklen;
+ /* addr->name = r->connection->addr_text; */
+
+ return NGX_OK;
+ }
+
+ v = ngx_http_get_flushed_variable(r, ctx->index);
+
+ if (v == NULL || v->not_found) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http geo not found");
+
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http geo started: %v", v);
+
+ if (ngx_parse_addr(r->pool, addr, v->data, v->len) == NGX_OK) {
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+}
+
+
+static char *
+ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ size_t len;
+ ngx_str_t *value, name;
+ ngx_uint_t i;
+ ngx_conf_t save;
+ ngx_pool_t *pool;
+ ngx_array_t *a;
+ ngx_http_variable_t *var;
+ ngx_http_geo_ctx_t *geo;
+ ngx_http_geo_conf_ctx_t ctx;
+#if (NGX_HAVE_INET6)
+ static struct in6_addr zero;
+#endif
+
+ value = cf->args->elts;
+
+ geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));
+ if (geo == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ name = value[1];
+
+ if (name.data[0] != '$') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"%V\"", &name);
+ return NGX_CONF_ERROR;
+ }
+
+ name.len--;
+ name.data++;
+
+ if (cf->args->nelts == 3) {
+
+ geo->index = ngx_http_get_variable_index(cf, &name);
+ if (geo->index == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ name = value[2];
+
+ if (name.data[0] != '$') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"%V\"", &name);
+ return NGX_CONF_ERROR;
+ }
+
+ name.len--;
+ name.data++;
+
+ } else {
+ geo->index = -1;
+ }
+
+ var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
+ if (var == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
+ if (pool == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t));
+
+ ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
+ if (ctx.temp_pool == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value);
+
+ ctx.pool = cf->pool;
+ ctx.data_size = sizeof(ngx_http_geo_header_t)
+ + sizeof(ngx_http_variable_value_t)
+ + 0x10000 * sizeof(ngx_http_geo_range_t *);
+ ctx.allow_binary_include = 1;
+
+ save = *cf;
+ cf->pool = pool;
+ cf->ctx = &ctx;
+ cf->handler = ngx_http_geo;
+ cf->handler_conf = conf;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = save;
+
+ geo->proxies = ctx.proxies;
+ geo->proxy_recursive = ctx.proxy_recursive;
+
+ if (ctx.ranges) {
+
+ if (ctx.high.low && !ctx.binary_include) {
+ for (i = 0; i < 0x10000; i++) {
+ a = (ngx_array_t *) ctx.high.low[i];
+
+ if (a == NULL || a->nelts == 0) {
+ continue;
+ }
+
+ len = a->nelts * sizeof(ngx_http_geo_range_t);
+
+ ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));
+ if (ctx.high.low[i] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memcpy(ctx.high.low[i], a->elts, len);
+ ctx.high.low[i][a->nelts].value = NULL;
+ ctx.data_size += len + sizeof(void *);
+ }
+
+ if (ctx.allow_binary_include
+ && !ctx.outside_entries
+ && ctx.entries > 100000
+ && ctx.includes == 1)
+ {
+ ngx_http_geo_create_binary_base(&ctx);
+ }
+ }
+
+ if (ctx.high.default_value == NULL) {
+ ctx.high.default_value = &ngx_http_variable_null_value;
+ }
+
+ geo->u.high = ctx.high;
+
+ var->get_handler = ngx_http_geo_range_variable;
+ var->data = (uintptr_t) geo;
+
+ ngx_destroy_pool(ctx.temp_pool);
+ ngx_destroy_pool(pool);
+
+ } else {
+ if (ctx.tree == NULL) {
+ ctx.tree = ngx_radix_tree_create(cf->pool, -1);
+ if (ctx.tree == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ geo->u.trees.tree = ctx.tree;
+
+#if (NGX_HAVE_INET6)
+ if (ctx.tree6 == NULL) {
+ ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);
+ if (ctx.tree6 == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ geo->u.trees.tree6 = ctx.tree6;
+#endif
+
+ var->get_handler = ngx_http_geo_cidr_variable;
+ var->data = (uintptr_t) geo;
+
+ ngx_destroy_pool(ctx.temp_pool);
+ ngx_destroy_pool(pool);
+
+ if (ngx_radix32tree_insert(ctx.tree, 0, 0,
+ (uintptr_t) &ngx_http_variable_null_value)
+ == NGX_ERROR)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ /* NGX_BUSY is okay (default was set explicitly) */
+
+#if (NGX_HAVE_INET6)
+ if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,
+ (uintptr_t) &ngx_http_variable_null_value)
+ == NGX_ERROR)
+ {
+ return NGX_CONF_ERROR;
+ }
+#endif
+ }
+
+ return rv;
+}
+
+
+static char *
+ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
+{
+ char *rv;
+ ngx_str_t *value;
+ ngx_cidr_t cidr;
+ ngx_http_geo_conf_ctx_t *ctx;
+
+ ctx = cf->ctx;
+
+ value = cf->args->elts;
+
+ if (cf->args->nelts == 1) {
+
+ if (ngx_strcmp(value[0].data, "ranges") == 0) {
+
+ if (ctx->tree
+#if (NGX_HAVE_INET6)
+ || ctx->tree6
+#endif
+ )
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"ranges\" directive must be "
+ "the first directive inside \"geo\" block");
+ goto failed;
+ }
+
+ ctx->ranges = 1;
+
+ rv = NGX_CONF_OK;
+
+ goto done;
+ }
+
+ else if (ngx_strcmp(value[0].data, "proxy_recursive") == 0) {
+ ctx->proxy_recursive = 1;
+ rv = NGX_CONF_OK;
+ goto done;
+ }
+ }
+
+ if (cf->args->nelts != 2) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid number of the geo parameters");
+ goto failed;
+ }
+
+ if (ngx_strcmp(value[0].data, "include") == 0) {
+
+ rv = ngx_http_geo_include(cf, ctx, &value[1]);
+
+ goto done;
+
+ } else if (ngx_strcmp(value[0].data, "proxy") == 0) {
+
+ if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
+ goto failed;
+ }
+
+ rv = ngx_http_geo_add_proxy(cf, ctx, &cidr);
+
+ goto done;
+ }
+
+ if (ctx->ranges) {
+ rv = ngx_http_geo_range(cf, ctx, value);
+
+ } else {
+ rv = ngx_http_geo_cidr(cf, ctx, value);
+ }
+
+done:
+
+ ngx_reset_pool(cf->pool);
+
+ return rv;
+
+failed:
+
+ ngx_reset_pool(cf->pool);
+
+ return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+ ngx_str_t *value)
+{
+ u_char *p, *last;
+ in_addr_t start, end;
+ ngx_str_t *net;
+ ngx_uint_t del;
+
+ if (ngx_strcmp(value[0].data, "default") == 0) {
+
+ if (ctx->high.default_value) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "duplicate default geo range value: \"%V\", old value: \"%v\"",
+ &value[1], ctx->high.default_value);
+ }
+
+ ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);
+ if (ctx->high.default_value == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+ }
+
+ if (ctx->binary_include) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "binary geo range base \"%s\" cannot be mixed with usual entries",
+ ctx->include_name.data);
+ return NGX_CONF_ERROR;
+ }
+
+ if (ctx->high.low == NULL) {
+ ctx->high.low = ngx_pcalloc(ctx->pool,
+ 0x10000 * sizeof(ngx_http_geo_range_t *));
+ if (ctx->high.low == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ ctx->entries++;
+ ctx->outside_entries = 1;
+
+ if (ngx_strcmp(value[0].data, "delete") == 0) {
+ net = &value[1];
+ del = 1;
+
+ } else {
+ net = &value[0];
+ del = 0;
+ }
+
+ last = net->data + net->len;
+
+ p = ngx_strlchr(net->data, last, '-');
+
+ if (p == NULL) {
+ goto invalid;
+ }
+
+ start = ngx_inet_addr(net->data, p - net->data);
+
+ if (start == INADDR_NONE) {
+ goto invalid;
+ }
+
+ start = ntohl(start);
+
+ p++;
+
+ end = ngx_inet_addr(p, last - p);
+
+ if (end == INADDR_NONE) {
+ goto invalid;
+ }
+
+ end = ntohl(end);
+
+ if (start > end) {
+ goto invalid;
+ }
+
+ if (del) {
+ if (ngx_http_geo_delete_range(cf, ctx, start, end)) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "no address range \"%V\" to delete", net);
+ }
+
+ return NGX_CONF_OK;
+ }
+
+ ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);
+
+ if (ctx->value == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->net = net;
+
+ return ngx_http_geo_add_range(cf, ctx, start, end);
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid range \"%V\"", net);
+
+ return NGX_CONF_ERROR;
+}
+
+
+/* the add procedure is optimized to add a growing up sequence */
+
+static char *
+ngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+ in_addr_t start, in_addr_t end)
+{
+ in_addr_t n;
+ ngx_uint_t h, i, s, e;
+ ngx_array_t *a;
+ ngx_http_geo_range_t *range;
+
+ for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {
+
+ h = n >> 16;
+
+ if (n == start) {
+ s = n & 0xffff;
+ } else {
+ s = 0;
+ }
+
+ if ((n | 0xffff) > end) {
+ e = end & 0xffff;
+
+ } else {
+ e = 0xffff;
+ }
+
+ a = (ngx_array_t *) ctx->high.low[h];
+
+ if (a == NULL) {
+ a = ngx_array_create(ctx->temp_pool, 64,
+ sizeof(ngx_http_geo_range_t));
+ if (a == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->high.low[h] = (ngx_http_geo_range_t *) a;
+ }
+
+ i = a->nelts;
+ range = a->elts;
+
+ while (i) {
+
+ i--;
+
+ if (e < (ngx_uint_t) range[i].start) {
+ continue;
+ }
+
+ if (s > (ngx_uint_t) range[i].end) {
+
+ /* add after the range */
+
+ range = ngx_array_push(a);
+ if (range == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ range = a->elts;
+
+ ngx_memmove(&range[i + 2], &range[i + 1],
+ (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
+
+ range[i + 1].start = (u_short) s;
+ range[i + 1].end = (u_short) e;
+ range[i + 1].value = ctx->value;
+
+ goto next;
+ }
+
+ if (s == (ngx_uint_t) range[i].start
+ && e == (ngx_uint_t) range[i].end)
+ {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "duplicate range \"%V\", value: \"%v\", old value: \"%v\"",
+ ctx->net, ctx->value, range[i].value);
+
+ range[i].value = ctx->value;
+
+ goto next;
+ }
+
+ if (s > (ngx_uint_t) range[i].start
+ && e < (ngx_uint_t) range[i].end)
+ {
+ /* split the range and insert the new one */
+
+ range = ngx_array_push(a);
+ if (range == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ range = ngx_array_push(a);
+ if (range == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ range = a->elts;
+
+ ngx_memmove(&range[i + 3], &range[i + 1],
+ (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t));
+
+ range[i + 2].start = (u_short) (e + 1);
+ range[i + 2].end = range[i].end;
+ range[i + 2].value = range[i].value;
+
+ range[i + 1].start = (u_short) s;
+ range[i + 1].end = (u_short) e;
+ range[i + 1].value = ctx->value;
+
+ range[i].end = (u_short) (s - 1);
+
+ goto next;
+ }
+
+ if (s == (ngx_uint_t) range[i].start
+ && e < (ngx_uint_t) range[i].end)
+ {
+ /* shift the range start and insert the new range */
+
+ range = ngx_array_push(a);
+ if (range == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ range = a->elts;
+
+ ngx_memmove(&range[i + 1], &range[i],
+ (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
+
+ range[i + 1].start = (u_short) (e + 1);
+
+ range[i].start = (u_short) s;
+ range[i].end = (u_short) e;
+ range[i].value = ctx->value;
+
+ goto next;
+ }
+
+ if (s > (ngx_uint_t) range[i].start
+ && e == (ngx_uint_t) range[i].end)
+ {
+ /* shift the range end and insert the new range */
+
+ range = ngx_array_push(a);
+ if (range == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ range = a->elts;
+
+ ngx_memmove(&range[i + 2], &range[i + 1],
+ (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));
+
+ range[i + 1].start = (u_short) s;
+ range[i + 1].end = (u_short) e;
+ range[i + 1].value = ctx->value;
+
+ range[i].end = (u_short) (s - 1);
+
+ goto next;
+ }
+
+ s = (ngx_uint_t) range[i].start;
+ e = (ngx_uint_t) range[i].end;
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "range \"%V\" overlaps \"%d.%d.%d.%d-%d.%d.%d.%d\"",
+ ctx->net,
+ h >> 8, h & 0xff, s >> 8, s & 0xff,
+ h >> 8, h & 0xff, e >> 8, e & 0xff);
+
+ return NGX_CONF_ERROR;
+ }
+
+ /* add the first range */
+
+ range = ngx_array_push(a);
+ if (range == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ range->start = (u_short) s;
+ range->end = (u_short) e;
+ range->value = ctx->value;
+
+ next:
+
+ continue;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_uint_t
+ngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+ in_addr_t start, in_addr_t end)
+{
+ in_addr_t n;
+ ngx_uint_t h, i, s, e, warn;
+ ngx_array_t *a;
+ ngx_http_geo_range_t *range;
+
+ warn = 0;
+
+ for (n = start; n <= end; n += 0x10000) {
+
+ h = n >> 16;
+
+ if (n == start) {
+ s = n & 0xffff;
+ } else {
+ s = 0;
+ }
+
+ if ((n | 0xffff) > end) {
+ e = end & 0xffff;
+
+ } else {
+ e = 0xffff;
+ }
+
+ a = (ngx_array_t *) ctx->high.low[h];
+
+ if (a == NULL) {
+ warn = 1;
+ continue;
+ }
+
+ range = a->elts;
+ for (i = 0; i < a->nelts; i++) {
+
+ if (s == (ngx_uint_t) range[i].start
+ && e == (ngx_uint_t) range[i].end)
+ {
+ ngx_memmove(&range[i], &range[i + 1],
+ (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));
+
+ a->nelts--;
+
+ break;
+ }
+
+ if (s != (ngx_uint_t) range[i].start
+ && e != (ngx_uint_t) range[i].end)
+ {
+ continue;
+ }
+
+ warn = 1;
+ }
+ }
+
+ return warn;
+}
+
+
+static char *
+ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+ ngx_str_t *value)
+{
+ char *rv;
+ ngx_int_t rc, del;
+ ngx_str_t *net;
+ ngx_cidr_t cidr;
+
+ if (ctx->tree == NULL) {
+ ctx->tree = ngx_radix_tree_create(ctx->pool, -1);
+ if (ctx->tree == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+#if (NGX_HAVE_INET6)
+ if (ctx->tree6 == NULL) {
+ ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);
+ if (ctx->tree6 == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+#endif
+
+ if (ngx_strcmp(value[0].data, "default") == 0) {
+ cidr.family = AF_INET;
+ cidr.u.in.addr = 0;
+ cidr.u.in.mask = 0;
+
+ rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);
+
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+
+#if (NGX_HAVE_INET6)
+ cidr.family = AF_INET6;
+ ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));
+
+ rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);
+
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+#endif
+
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_strcmp(value[0].data, "delete") == 0) {
+ net = &value[1];
+ del = 1;
+
+ } else {
+ net = &value[0];
+ del = 0;
+ }
+
+ if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (cidr.family == AF_INET) {
+ cidr.u.in.addr = ntohl(cidr.u.in.addr);
+ cidr.u.in.mask = ntohl(cidr.u.in.mask);
+ }
+
+ if (del) {
+ switch (cidr.family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ rc = ngx_radix128tree_delete(ctx->tree6,
+ cidr.u.in6.addr.s6_addr,
+ cidr.u.in6.mask.s6_addr);
+ break;
+#endif
+
+ default: /* AF_INET */
+ rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,
+ cidr.u.in.mask);
+ break;
+ }
+
+ if (rc != NGX_OK) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "no network \"%V\" to delete", net);
+ }
+
+ return NGX_CONF_OK;
+ }
+
+ return ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], net);
+}
+
+
+static char *
+ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+ ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net)
+{
+ ngx_int_t rc;
+ ngx_http_variable_value_t *val, *old;
+
+ val = ngx_http_geo_value(cf, ctx, value);
+
+ if (val == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ switch (cidr->family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
+ cidr->u.in6.mask.s6_addr,
+ (uintptr_t) val);
+
+ if (rc == NGX_OK) {
+ return NGX_CONF_OK;
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ /* rc == NGX_BUSY */
+
+ old = (ngx_http_variable_value_t *)
+ ngx_radix128tree_find(ctx->tree6,
+ cidr->u.in6.addr.s6_addr);
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
+ net, val, old);
+
+ rc = ngx_radix128tree_delete(ctx->tree6,
+ cidr->u.in6.addr.s6_addr,
+ cidr->u.in6.mask.s6_addr);
+
+ if (rc == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
+ return NGX_CONF_ERROR;
+ }
+
+ rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,
+ cidr->u.in6.mask.s6_addr,
+ (uintptr_t) val);
+
+ break;
+#endif
+
+ default: /* AF_INET */
+ rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,
+ cidr->u.in.mask, (uintptr_t) val);
+
+ if (rc == NGX_OK) {
+ return NGX_CONF_OK;
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ /* rc == NGX_BUSY */
+
+ old = (ngx_http_variable_value_t *)
+ ngx_radix32tree_find(ctx->tree, cidr->u.in.addr);
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "duplicate network \"%V\", value: \"%v\", old value: \"%v\"",
+ net, val, old);
+
+ rc = ngx_radix32tree_delete(ctx->tree,
+ cidr->u.in.addr, cidr->u.in.mask);
+
+ if (rc == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid radix tree");
+ return NGX_CONF_ERROR;
+ }
+
+ rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,
+ cidr->u.in.mask, (uintptr_t) val);
+
+ break;
+ }
+
+ if (rc == NGX_OK) {
+ return NGX_CONF_OK;
+ }
+
+ return NGX_CONF_ERROR;
+}
+
+
+static ngx_http_variable_value_t *
+ngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+ ngx_str_t *value)
+{
+ uint32_t hash;
+ ngx_http_variable_value_t *val;
+ ngx_http_geo_variable_value_node_t *gvvn;
+
+ hash = ngx_crc32_long(value->data, value->len);
+
+ gvvn = (ngx_http_geo_variable_value_node_t *)
+ ngx_str_rbtree_lookup(&ctx->rbtree, value, hash);
+
+ if (gvvn) {
+ return gvvn->value;
+ }
+
+ val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));
+ if (val == NULL) {
+ return NULL;
+ }
+
+ val->len = value->len;
+ val->data = ngx_pstrdup(ctx->pool, value);
+ if (val->data == NULL) {
+ return NULL;
+ }
+
+ val->valid = 1;
+ val->no_cacheable = 0;
+ val->not_found = 0;
+
+ gvvn = ngx_palloc(ctx->temp_pool,
+ sizeof(ngx_http_geo_variable_value_node_t));
+ if (gvvn == NULL) {
+ return NULL;
+ }
+
+ gvvn->sn.node.key = hash;
+ gvvn->sn.str.len = val->len;
+ gvvn->sn.str.data = val->data;
+ gvvn->value = val;
+ gvvn->offset = 0;
+
+ ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node);
+
+ ctx->data_size += ngx_align(sizeof(ngx_http_variable_value_t) + value->len,
+ sizeof(void *));
+
+ return val;
+}
+
+
+static char *
+ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+ ngx_cidr_t *cidr)
+{
+ ngx_cidr_t *c;
+
+ if (ctx->proxies == NULL) {
+ ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_cidr_t));
+ if (ctx->proxies == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ c = ngx_array_push(ctx->proxies);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = *cidr;
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
+{
+ ngx_int_t rc;
+
+ if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
+ cidr->family = AF_INET;
+ cidr->u.in.addr = 0xffffffff;
+ cidr->u.in.mask = 0xffffffff;
+
+ return NGX_OK;
+ }
+
+ rc = ngx_ptocidr(net, cidr);
+
+ if (rc == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
+ return NGX_ERROR;
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "low address bits of %V are meaningless", net);
+ }
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+ ngx_str_t *name)
+{
+ char *rv;
+ ngx_str_t file;
+
+ file.len = name->len + 4;
+ file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);
+ if (file.data == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_sprintf(file.data, "%V.bin%Z", name);
+
+ if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ctx->ranges) {
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
+
+ switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {
+ case NGX_OK:
+ return NGX_CONF_OK;
+ case NGX_ERROR:
+ return NGX_CONF_ERROR;
+ default:
+ break;
+ }
+ }
+
+ file.len -= 4;
+ file.data[file.len] = '\0';
+
+ ctx->include_name = file;
+
+ if (ctx->outside_entries) {
+ ctx->allow_binary_include = 0;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
+
+ rv = ngx_conf_parse(cf, &file);
+
+ ctx->includes++;
+ ctx->outside_entries = 0;
+
+ return rv;
+}
+
+
+static ngx_int_t
+ngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+ ngx_str_t *name)
+{
+ u_char *base, ch;
+ time_t mtime;
+ size_t size, len;
+ ssize_t n;
+ uint32_t crc32;
+ ngx_err_t err;
+ ngx_int_t rc;
+ ngx_uint_t i;
+ ngx_file_t file;
+ ngx_file_info_t fi;
+ ngx_http_geo_range_t *range, **ranges;
+ ngx_http_geo_header_t *header;
+ ngx_http_variable_value_t *vv;
+
+ ngx_memzero(&file, sizeof(ngx_file_t));
+ file.name = *name;
+ file.log = cf->log;
+
+ file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, 0, 0);
+ if (file.fd == NGX_INVALID_FILE) {
+ err = ngx_errno;
+ if (err != NGX_ENOENT) {
+ ngx_conf_log_error(NGX_LOG_CRIT, cf, err,
+ ngx_open_file_n " \"%s\" failed", name->data);
+ }
+ return NGX_DECLINED;
+ }
+
+ if (ctx->outside_entries) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "binary geo range base \"%s\" cannot be mixed with usual entries",
+ name->data);
+ rc = NGX_ERROR;
+ goto done;
+ }
+
+ if (ctx->binary_include) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "second binary geo range base \"%s\" cannot be mixed with \"%s\"",
+ name->data, ctx->include_name.data);
+ rc = NGX_ERROR;
+ goto done;
+ }
+
+ if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
+ ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
+ ngx_fd_info_n " \"%s\" failed", name->data);
+ goto failed;
+ }
+
+ size = (size_t) ngx_file_size(&fi);
+ mtime = ngx_file_mtime(&fi);
+
+ ch = name->data[name->len - 4];
+ name->data[name->len - 4] = '\0';
+
+ if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
+ ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
+ ngx_file_info_n " \"%s\" failed", name->data);
+ goto failed;
+ }
+
+ name->data[name->len - 4] = ch;
+
+ if (mtime < ngx_file_mtime(&fi)) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "stale binary geo range base \"%s\"", name->data);
+ goto failed;
+ }
+
+ base = ngx_palloc(ctx->pool, size);
+ if (base == NULL) {
+ goto failed;
+ }
+
+ n = ngx_read_file(&file, base, size, 0);
+
+ if (n == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
+ ngx_read_file_n " \"%s\" failed", name->data);
+ goto failed;
+ }
+
+ if ((size_t) n != size) {
+ ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
+ ngx_read_file_n " \"%s\" returned only %z bytes instead of %z",
+ name->data, n, size);
+ goto failed;
+ }
+
+ header = (ngx_http_geo_header_t *) base;
+
+ if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "incompatible binary geo range base \"%s\"", name->data);
+ goto failed;
+ }
+
+ ngx_crc32_init(crc32);
+
+ vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t));
+
+ while (vv->data) {
+ len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,
+ sizeof(void *));
+ ngx_crc32_update(&crc32, (u_char *) vv, len);
+ vv->data += (size_t) base;
+ vv = (ngx_http_variable_value_t *) ((u_char *) vv + len);
+ }
+ ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));
+ vv++;
+
+ ranges = (ngx_http_geo_range_t **) vv;
+
+ for (i = 0; i < 0x10000; i++) {
+ ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));
+ if (ranges[i]) {
+ ranges[i] = (ngx_http_geo_range_t *)
+ ((u_char *) ranges[i] + (size_t) base);
+ }
+ }
+
+ range = (ngx_http_geo_range_t *) &ranges[0x10000];
+
+ while ((u_char *) range < base + size) {
+ while (range->value) {
+ ngx_crc32_update(&crc32, (u_char *) range,
+ sizeof(ngx_http_geo_range_t));
+ range->value = (ngx_http_variable_value_t *)
+ ((u_char *) range->value + (size_t) base);
+ range++;
+ }
+ ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));
+ range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));
+ }
+
+ ngx_crc32_final(crc32);
+
+ if (crc32 != header->crc32) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "CRC32 mismatch in binary geo range base \"%s\"", name->data);
+ goto failed;
+ }
+
+ ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,
+ "using binary geo range base \"%s\"", name->data);
+
+ ctx->include_name = *name;
+ ctx->binary_include = 1;
+ ctx->high.low = ranges;
+ rc = NGX_OK;
+
+ goto done;
+
+failed:
+
+ rc = NGX_DECLINED;
+
+done:
+
+ if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", name->data);
+ }
+
+ return rc;
+}
+
+
+static void
+ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)
+{
+ u_char *p;
+ uint32_t hash;
+ ngx_str_t s;
+ ngx_uint_t i;
+ ngx_file_mapping_t fm;
+ ngx_http_geo_range_t *r, *range, **ranges;
+ ngx_http_geo_header_t *header;
+ ngx_http_geo_variable_value_node_t *gvvn;
+
+ fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);
+ if (fm.name == NULL) {
+ return;
+ }
+
+ ngx_sprintf(fm.name, "%V.bin%Z", &ctx->include_name);
+
+ fm.size = ctx->data_size;
+ fm.log = ctx->pool->log;
+
+ ngx_log_error(NGX_LOG_NOTICE, fm.log, 0,
+ "creating binary geo range base \"%s\"", fm.name);
+
+ if (ngx_create_file_mapping(&fm) != NGX_OK) {
+ return;
+ }
+
+ p = ngx_cpymem(fm.addr, &ngx_http_geo_header,
+ sizeof(ngx_http_geo_header_t));
+
+ p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root,
+ ctx->rbtree.sentinel);
+
+ p += sizeof(ngx_http_variable_value_t);
+
+ ranges = (ngx_http_geo_range_t **) p;
+
+ p += 0x10000 * sizeof(ngx_http_geo_range_t *);
+
+ for (i = 0; i < 0x10000; i++) {
+ r = ctx->high.low[i];
+ if (r == NULL) {
+ continue;
+ }
+
+ range = (ngx_http_geo_range_t *) p;
+ ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr);
+
+ do {
+ s.len = r->value->len;
+ s.data = r->value->data;
+ hash = ngx_crc32_long(s.data, s.len);
+ gvvn = (ngx_http_geo_variable_value_node_t *)
+ ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);
+
+ range->value = (ngx_http_variable_value_t *) gvvn->offset;
+ range->start = r->start;
+ range->end = r->end;
+ range++;
+
+ } while ((++r)->value);
+
+ range->value = NULL;
+
+ p = (u_char *) range + sizeof(void *);
+ }
+
+ header = fm.addr;
+ header->crc32 = ngx_crc32_long((u_char *) fm.addr
+ + sizeof(ngx_http_geo_header_t),
+ fm.size - sizeof(ngx_http_geo_header_t));
+
+ ngx_close_file_mapping(&fm);
+}
+
+
+static u_char *
+ngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,
+ ngx_rbtree_node_t *sentinel)
+{
+ ngx_http_variable_value_t *vv;
+ ngx_http_geo_variable_value_node_t *gvvn;
+
+ if (node == sentinel) {
+ return p;
+ }
+
+ gvvn = (ngx_http_geo_variable_value_node_t *) node;
+ gvvn->offset = p - base;
+
+ vv = (ngx_http_variable_value_t *) p;
+ *vv = *gvvn->value;
+ p += sizeof(ngx_http_variable_value_t);
+ vv->data = (u_char *) (p - base);
+
+ p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len);
+
+ p = ngx_align_ptr(p, sizeof(void *));
+
+ p = ngx_http_geo_copy_values(base, p, node->left, sentinel);
+
+ return ngx_http_geo_copy_values(base, p, node->right, sentinel);
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_geoip_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_geoip_module.c
new file mode 100644
index 00000000000..8e151aa8f20
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_geoip_module.c
@@ -0,0 +1,925 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <GeoIP.h>
+#include <GeoIPCity.h>
+
+
+#define NGX_GEOIP_COUNTRY_CODE 0
+#define NGX_GEOIP_COUNTRY_CODE3 1
+#define NGX_GEOIP_COUNTRY_NAME 2
+
+
+typedef struct {
+ GeoIP *country;
+ GeoIP *org;
+ GeoIP *city;
+ ngx_array_t *proxies; /* array of ngx_cidr_t */
+ ngx_flag_t proxy_recursive;
+#if (NGX_HAVE_GEOIP_V6)
+ unsigned country_v6:1;
+ unsigned org_v6:1;
+ unsigned city_v6:1;
+#endif
+} ngx_http_geoip_conf_t;
+
+
+typedef struct {
+ ngx_str_t *name;
+ uintptr_t data;
+} ngx_http_geoip_var_t;
+
+
+typedef const char *(*ngx_http_geoip_variable_handler_pt)(GeoIP *,
+ u_long addr);
+
+
+ngx_http_geoip_variable_handler_pt ngx_http_geoip_country_functions[] = {
+ GeoIP_country_code_by_ipnum,
+ GeoIP_country_code3_by_ipnum,
+ GeoIP_country_name_by_ipnum,
+};
+
+
+#if (NGX_HAVE_GEOIP_V6)
+
+typedef const char *(*ngx_http_geoip_variable_handler_v6_pt)(GeoIP *,
+ geoipv6_t addr);
+
+
+ngx_http_geoip_variable_handler_v6_pt ngx_http_geoip_country_v6_functions[] = {
+ GeoIP_country_code_by_ipnum_v6,
+ GeoIP_country_code3_by_ipnum_v6,
+ GeoIP_country_name_by_ipnum_v6,
+};
+
+#endif
+
+
+static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_geoip_org_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_geoip_city_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_geoip_region_name_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_geoip_city_float_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_geoip_city_int_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static GeoIPRecord *ngx_http_geoip_get_city_record(ngx_http_request_t *r);
+
+static ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf);
+static void *ngx_http_geoip_create_conf(ngx_conf_t *cf);
+static char *ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf);
+static char *ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static ngx_int_t ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
+ ngx_cidr_t *cidr);
+static void ngx_http_geoip_cleanup(void *data);
+
+
+static ngx_command_t ngx_http_geoip_commands[] = {
+
+ { ngx_string("geoip_country"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
+ ngx_http_geoip_country,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("geoip_org"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
+ ngx_http_geoip_org,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("geoip_city"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,
+ ngx_http_geoip_city,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("geoip_proxy"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_http_geoip_proxy,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("geoip_proxy_recursive"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ offsetof(ngx_http_geoip_conf_t, proxy_recursive),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_geoip_module_ctx = {
+ ngx_http_geoip_add_variables, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ ngx_http_geoip_create_conf, /* create main configuration */
+ ngx_http_geoip_init_conf, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_geoip_module = {
+ NGX_MODULE_V1,
+ &ngx_http_geoip_module_ctx, /* module context */
+ ngx_http_geoip_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_variable_t ngx_http_geoip_vars[] = {
+
+ { ngx_string("geoip_country_code"), NULL,
+ ngx_http_geoip_country_variable,
+ NGX_GEOIP_COUNTRY_CODE, 0, 0 },
+
+ { ngx_string("geoip_country_code3"), NULL,
+ ngx_http_geoip_country_variable,
+ NGX_GEOIP_COUNTRY_CODE3, 0, 0 },
+
+ { ngx_string("geoip_country_name"), NULL,
+ ngx_http_geoip_country_variable,
+ NGX_GEOIP_COUNTRY_NAME, 0, 0 },
+
+ { ngx_string("geoip_org"), NULL,
+ ngx_http_geoip_org_variable,
+ 0, 0, 0 },
+
+ { ngx_string("geoip_city_continent_code"), NULL,
+ ngx_http_geoip_city_variable,
+ offsetof(GeoIPRecord, continent_code), 0, 0 },
+
+ { ngx_string("geoip_city_country_code"), NULL,
+ ngx_http_geoip_city_variable,
+ offsetof(GeoIPRecord, country_code), 0, 0 },
+
+ { ngx_string("geoip_city_country_code3"), NULL,
+ ngx_http_geoip_city_variable,
+ offsetof(GeoIPRecord, country_code3), 0, 0 },
+
+ { ngx_string("geoip_city_country_name"), NULL,
+ ngx_http_geoip_city_variable,
+ offsetof(GeoIPRecord, country_name), 0, 0 },
+
+ { ngx_string("geoip_region"), NULL,
+ ngx_http_geoip_city_variable,
+ offsetof(GeoIPRecord, region), 0, 0 },
+
+ { ngx_string("geoip_region_name"), NULL,
+ ngx_http_geoip_region_name_variable,
+ 0, 0, 0 },
+
+ { ngx_string("geoip_city"), NULL,
+ ngx_http_geoip_city_variable,
+ offsetof(GeoIPRecord, city), 0, 0 },
+
+ { ngx_string("geoip_postal_code"), NULL,
+ ngx_http_geoip_city_variable,
+ offsetof(GeoIPRecord, postal_code), 0, 0 },
+
+ { ngx_string("geoip_latitude"), NULL,
+ ngx_http_geoip_city_float_variable,
+ offsetof(GeoIPRecord, latitude), 0, 0 },
+
+ { ngx_string("geoip_longitude"), NULL,
+ ngx_http_geoip_city_float_variable,
+ offsetof(GeoIPRecord, longitude), 0, 0 },
+
+ { ngx_string("geoip_dma_code"), NULL,
+ ngx_http_geoip_city_int_variable,
+ offsetof(GeoIPRecord, dma_code), 0, 0 },
+
+ { ngx_string("geoip_area_code"), NULL,
+ ngx_http_geoip_city_int_variable,
+ offsetof(GeoIPRecord, area_code), 0, 0 },
+
+ { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
+
+
+static u_long
+ngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
+{
+ ngx_addr_t addr;
+ ngx_array_t *xfwd;
+ struct sockaddr_in *sin;
+
+ addr.sockaddr = r->connection->sockaddr;
+ addr.socklen = r->connection->socklen;
+ /* addr.name = r->connection->addr_text; */
+
+ xfwd = &r->headers_in.x_forwarded_for;
+
+ if (xfwd->nelts > 0 && gcf->proxies != NULL) {
+ (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
+ gcf->proxies, gcf->proxy_recursive);
+ }
+
+#if (NGX_HAVE_INET6)
+
+ if (addr.sockaddr->sa_family == AF_INET6) {
+ u_char *p;
+ in_addr_t inaddr;
+ struct in6_addr *inaddr6;
+
+ inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;
+
+ if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
+ p = inaddr6->s6_addr;
+
+ inaddr = p[12] << 24;
+ inaddr += p[13] << 16;
+ inaddr += p[14] << 8;
+ inaddr += p[15];
+
+ return inaddr;
+ }
+ }
+
+#endif
+
+ if (addr.sockaddr->sa_family != AF_INET) {
+ return INADDR_NONE;
+ }
+
+ sin = (struct sockaddr_in *) addr.sockaddr;
+ return ntohl(sin->sin_addr.s_addr);
+}
+
+
+#if (NGX_HAVE_GEOIP_V6)
+
+static geoipv6_t
+ngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)
+{
+ ngx_addr_t addr;
+ ngx_array_t *xfwd;
+ in_addr_t addr4;
+ struct in6_addr addr6;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ addr.sockaddr = r->connection->sockaddr;
+ addr.socklen = r->connection->socklen;
+ /* addr.name = r->connection->addr_text; */
+
+ xfwd = &r->headers_in.x_forwarded_for;
+
+ if (xfwd->nelts > 0 && gcf->proxies != NULL) {
+ (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,
+ gcf->proxies, gcf->proxy_recursive);
+ }
+
+ switch (addr.sockaddr->sa_family) {
+
+ case AF_INET:
+ /* Produce IPv4-mapped IPv6 address. */
+ sin = (struct sockaddr_in *) addr.sockaddr;
+ addr4 = ntohl(sin->sin_addr.s_addr);
+
+ ngx_memzero(&addr6, sizeof(struct in6_addr));
+ addr6.s6_addr[10] = 0xff;
+ addr6.s6_addr[11] = 0xff;
+ addr6.s6_addr[12] = addr4 >> 24;
+ addr6.s6_addr[13] = addr4 >> 16;
+ addr6.s6_addr[14] = addr4 >> 8;
+ addr6.s6_addr[15] = addr4;
+ return addr6;
+
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) addr.sockaddr;
+ return sin6->sin6_addr;
+
+ default:
+ return in6addr_any;
+ }
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_geoip_country_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_geoip_variable_handler_pt handler =
+ ngx_http_geoip_country_functions[data];
+#if (NGX_HAVE_GEOIP_V6)
+ ngx_http_geoip_variable_handler_v6_pt handler_v6 =
+ ngx_http_geoip_country_v6_functions[data];
+#endif
+
+ const char *val;
+ ngx_http_geoip_conf_t *gcf;
+
+ gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
+
+ if (gcf->country == NULL) {
+ goto not_found;
+ }
+
+#if (NGX_HAVE_GEOIP_V6)
+ val = gcf->country_v6
+ ? handler_v6(gcf->country, ngx_http_geoip_addr_v6(r, gcf))
+ : handler(gcf->country, ngx_http_geoip_addr(r, gcf));
+#else
+ val = handler(gcf->country, ngx_http_geoip_addr(r, gcf));
+#endif
+
+ if (val == NULL) {
+ goto not_found;
+ }
+
+ v->len = ngx_strlen(val);
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) val;
+
+ return NGX_OK;
+
+not_found:
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_geoip_org_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ size_t len;
+ char *val;
+ ngx_http_geoip_conf_t *gcf;
+
+ gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
+
+ if (gcf->org == NULL) {
+ goto not_found;
+ }
+
+#if (NGX_HAVE_GEOIP_V6)
+ val = gcf->org_v6
+ ? GeoIP_name_by_ipnum_v6(gcf->org,
+ ngx_http_geoip_addr_v6(r, gcf))
+ : GeoIP_name_by_ipnum(gcf->org,
+ ngx_http_geoip_addr(r, gcf));
+#else
+ val = GeoIP_name_by_ipnum(gcf->org, ngx_http_geoip_addr(r, gcf));
+#endif
+
+ if (val == NULL) {
+ goto not_found;
+ }
+
+ len = ngx_strlen(val);
+ v->data = ngx_pnalloc(r->pool, len);
+ if (v->data == NULL) {
+ ngx_free(val);
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(v->data, val, len);
+
+ v->len = len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ ngx_free(val);
+
+ return NGX_OK;
+
+not_found:
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_geoip_city_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ char *val;
+ size_t len;
+ GeoIPRecord *gr;
+
+ gr = ngx_http_geoip_get_city_record(r);
+ if (gr == NULL) {
+ goto not_found;
+ }
+
+ val = *(char **) ((char *) gr + data);
+ if (val == NULL) {
+ goto no_value;
+ }
+
+ len = ngx_strlen(val);
+ v->data = ngx_pnalloc(r->pool, len);
+ if (v->data == NULL) {
+ GeoIPRecord_delete(gr);
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(v->data, val, len);
+
+ v->len = len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ GeoIPRecord_delete(gr);
+
+ return NGX_OK;
+
+no_value:
+
+ GeoIPRecord_delete(gr);
+
+not_found:
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_geoip_region_name_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ size_t len;
+ const char *val;
+ GeoIPRecord *gr;
+
+ gr = ngx_http_geoip_get_city_record(r);
+ if (gr == NULL) {
+ goto not_found;
+ }
+
+ val = GeoIP_region_name_by_code(gr->country_code, gr->region);
+
+ GeoIPRecord_delete(gr);
+
+ if (val == NULL) {
+ goto not_found;
+ }
+
+ len = ngx_strlen(val);
+ v->data = ngx_pnalloc(r->pool, len);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(v->data, val, len);
+
+ v->len = len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+
+not_found:
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_geoip_city_float_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ float val;
+ GeoIPRecord *gr;
+
+ gr = ngx_http_geoip_get_city_record(r);
+ if (gr == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN + 5);
+ if (v->data == NULL) {
+ GeoIPRecord_delete(gr);
+ return NGX_ERROR;
+ }
+
+ val = *(float *) ((char *) gr + data);
+
+ v->len = ngx_sprintf(v->data, "%.4f", val) - v->data;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ GeoIPRecord_delete(gr);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_geoip_city_int_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ int val;
+ GeoIPRecord *gr;
+
+ gr = ngx_http_geoip_get_city_record(r);
+ if (gr == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN);
+ if (v->data == NULL) {
+ GeoIPRecord_delete(gr);
+ return NGX_ERROR;
+ }
+
+ val = *(int *) ((char *) gr + data);
+
+ v->len = ngx_sprintf(v->data, "%d", val) - v->data;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ GeoIPRecord_delete(gr);
+
+ return NGX_OK;
+}
+
+
+static GeoIPRecord *
+ngx_http_geoip_get_city_record(ngx_http_request_t *r)
+{
+ ngx_http_geoip_conf_t *gcf;
+
+ gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
+
+ if (gcf->city) {
+#if (NGX_HAVE_GEOIP_V6)
+ return gcf->city_v6
+ ? GeoIP_record_by_ipnum_v6(gcf->city,
+ ngx_http_geoip_addr_v6(r, gcf))
+ : GeoIP_record_by_ipnum(gcf->city,
+ ngx_http_geoip_addr(r, gcf));
+#else
+ return GeoIP_record_by_ipnum(gcf->city, ngx_http_geoip_addr(r, gcf));
+#endif
+ }
+
+ return NULL;
+}
+
+
+static ngx_int_t
+ngx_http_geoip_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var, *v;
+
+ for (v = ngx_http_geoip_vars; v->name.len; v++) {
+ var = ngx_http_add_variable(cf, &v->name, v->flags);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = v->get_handler;
+ var->data = v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_geoip_create_conf(ngx_conf_t *cf)
+{
+ ngx_pool_cleanup_t *cln;
+ ngx_http_geoip_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->proxy_recursive = NGX_CONF_UNSET;
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NULL;
+ }
+
+ cln->handler = ngx_http_geoip_cleanup;
+ cln->data = conf;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf)
+{
+ ngx_http_geoip_conf_t *gcf = conf;
+
+ ngx_conf_init_value(gcf->proxy_recursive, 0);
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_geoip_conf_t *gcf = conf;
+
+ ngx_str_t *value;
+
+ if (gcf->country) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
+
+ if (gcf->country == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "GeoIP_open(\"%V\") failed", &value[1]);
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (cf->args->nelts == 3) {
+ if (ngx_strcmp(value[2].data, "utf8") == 0) {
+ GeoIP_set_charset(gcf->country, GEOIP_CHARSET_UTF8);
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ switch (gcf->country->databaseType) {
+
+ case GEOIP_COUNTRY_EDITION:
+
+ return NGX_CONF_OK;
+
+#if (NGX_HAVE_GEOIP_V6)
+ case GEOIP_COUNTRY_EDITION_V6:
+
+ gcf->country_v6 = 1;
+ return NGX_CONF_OK;
+#endif
+
+ default:
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid GeoIP database \"%V\" type:%d",
+ &value[1], gcf->country->databaseType);
+ return NGX_CONF_ERROR;
+ }
+}
+
+
+static char *
+ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_geoip_conf_t *gcf = conf;
+
+ ngx_str_t *value;
+
+ if (gcf->org) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
+
+ if (gcf->org == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "GeoIP_open(\"%V\") failed", &value[1]);
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (cf->args->nelts == 3) {
+ if (ngx_strcmp(value[2].data, "utf8") == 0) {
+ GeoIP_set_charset(gcf->org, GEOIP_CHARSET_UTF8);
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ switch (gcf->org->databaseType) {
+
+ case GEOIP_ISP_EDITION:
+ case GEOIP_ORG_EDITION:
+ case GEOIP_DOMAIN_EDITION:
+ case GEOIP_ASNUM_EDITION:
+
+ return NGX_CONF_OK;
+
+#if (NGX_HAVE_GEOIP_V6)
+ case GEOIP_ISP_EDITION_V6:
+ case GEOIP_ORG_EDITION_V6:
+ case GEOIP_DOMAIN_EDITION_V6:
+ case GEOIP_ASNUM_EDITION_V6:
+
+ gcf->org_v6 = 1;
+ return NGX_CONF_OK;
+#endif
+
+ default:
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid GeoIP database \"%V\" type:%d",
+ &value[1], gcf->org->databaseType);
+ return NGX_CONF_ERROR;
+ }
+}
+
+
+static char *
+ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_geoip_conf_t *gcf = conf;
+
+ ngx_str_t *value;
+
+ if (gcf->city) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
+
+ if (gcf->city == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "GeoIP_open(\"%V\") failed", &value[1]);
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (cf->args->nelts == 3) {
+ if (ngx_strcmp(value[2].data, "utf8") == 0) {
+ GeoIP_set_charset(gcf->city, GEOIP_CHARSET_UTF8);
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ switch (gcf->city->databaseType) {
+
+ case GEOIP_CITY_EDITION_REV0:
+ case GEOIP_CITY_EDITION_REV1:
+
+ return NGX_CONF_OK;
+
+#if (NGX_HAVE_GEOIP_V6)
+ case GEOIP_CITY_EDITION_REV0_V6:
+ case GEOIP_CITY_EDITION_REV1_V6:
+
+ gcf->city_v6 = 1;
+ return NGX_CONF_OK;
+#endif
+
+ default:
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid GeoIP City database \"%V\" type:%d",
+ &value[1], gcf->city->databaseType);
+ return NGX_CONF_ERROR;
+ }
+}
+
+
+static char *
+ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_geoip_conf_t *gcf = conf;
+
+ ngx_str_t *value;
+ ngx_cidr_t cidr, *c;
+
+ value = cf->args->elts;
+
+ if (ngx_http_geoip_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (gcf->proxies == NULL) {
+ gcf->proxies = ngx_array_create(cf->pool, 4, sizeof(ngx_cidr_t));
+ if (gcf->proxies == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ c = ngx_array_push(gcf->proxies);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = cidr;
+
+ return NGX_CONF_OK;
+}
+
+static ngx_int_t
+ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
+{
+ ngx_int_t rc;
+
+ if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
+ cidr->family = AF_INET;
+ cidr->u.in.addr = 0xffffffff;
+ cidr->u.in.mask = 0xffffffff;
+
+ return NGX_OK;
+ }
+
+ rc = ngx_ptocidr(net, cidr);
+
+ if (rc == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
+ return NGX_ERROR;
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "low address bits of %V are meaningless", net);
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_geoip_cleanup(void *data)
+{
+ ngx_http_geoip_conf_t *gcf = data;
+
+ if (gcf->country) {
+ GeoIP_delete(gcf->country);
+ }
+
+ if (gcf->org) {
+ GeoIP_delete(gcf->org);
+ }
+
+ if (gcf->city) {
+ GeoIP_delete(gcf->city);
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_gunzip_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_gunzip_filter_module.c
new file mode 100644
index 00000000000..70ec0aacea6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_gunzip_filter_module.c
@@ -0,0 +1,681 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Maxim Dounin
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <zlib.h>
+
+
+typedef struct {
+ ngx_flag_t enable;
+ ngx_bufs_t bufs;
+} ngx_http_gunzip_conf_t;
+
+
+typedef struct {
+ ngx_chain_t *in;
+ ngx_chain_t *free;
+ ngx_chain_t *busy;
+ ngx_chain_t *out;
+ ngx_chain_t **last_out;
+
+ ngx_buf_t *in_buf;
+ ngx_buf_t *out_buf;
+ ngx_int_t bufs;
+
+ unsigned started:1;
+ unsigned flush:4;
+ unsigned redo:1;
+ unsigned done:1;
+ unsigned nomem:1;
+
+ z_stream zstream;
+ ngx_http_request_t *request;
+} ngx_http_gunzip_ctx_t;
+
+
+static ngx_int_t ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx);
+
+static void *ngx_http_gunzip_filter_alloc(void *opaque, u_int items,
+ u_int size);
+static void ngx_http_gunzip_filter_free(void *opaque, void *address);
+
+static ngx_int_t ngx_http_gunzip_filter_init(ngx_conf_t *cf);
+static void *ngx_http_gunzip_create_conf(ngx_conf_t *cf);
+static char *ngx_http_gunzip_merge_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+
+
+static ngx_command_t ngx_http_gunzip_filter_commands[] = {
+
+ { ngx_string("gunzip"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_gunzip_conf_t, enable),
+ NULL },
+
+ { ngx_string("gunzip_buffers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_conf_set_bufs_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_gunzip_conf_t, bufs),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_gunzip_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_gunzip_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_gunzip_create_conf, /* create location configuration */
+ ngx_http_gunzip_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_gunzip_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_gunzip_filter_module_ctx, /* module context */
+ ngx_http_gunzip_filter_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_gunzip_header_filter(ngx_http_request_t *r)
+{
+ ngx_http_gunzip_ctx_t *ctx;
+ ngx_http_gunzip_conf_t *conf;
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);
+
+ /* TODO support multiple content-codings */
+ /* TODO always gunzip - due to configuration or module request */
+ /* TODO ignore content encoding? */
+
+ if (!conf->enable
+ || r->headers_out.content_encoding == NULL
+ || r->headers_out.content_encoding->value.len != 4
+ || ngx_strncasecmp(r->headers_out.content_encoding->value.data,
+ (u_char *) "gzip", 4) != 0)
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ r->gzip_vary = 1;
+
+ if (!r->gzip_tested) {
+ if (ngx_http_gzip_ok(r) == NGX_OK) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ } else if (r->gzip_ok) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gunzip_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_gunzip_filter_module);
+
+ ctx->request = r;
+
+ r->filter_need_in_memory = 1;
+
+ r->headers_out.content_encoding->hash = 0;
+ r->headers_out.content_encoding = NULL;
+
+ ngx_http_clear_content_length(r);
+ ngx_http_clear_accept_ranges(r);
+ ngx_http_weak_etag(r);
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ int rc;
+ ngx_chain_t *cl;
+ ngx_http_gunzip_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_gunzip_filter_module);
+
+ if (ctx == NULL || ctx->done) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http gunzip filter");
+
+ if (!ctx->started) {
+ if (ngx_http_gunzip_filter_inflate_start(r, ctx) != NGX_OK) {
+ goto failed;
+ }
+ }
+
+ if (in) {
+ if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
+ goto failed;
+ }
+ }
+
+ if (ctx->nomem || in == NULL) {
+
+ /* flush busy buffers */
+
+ if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
+ goto failed;
+ }
+
+ cl = NULL;
+
+ ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
+ (ngx_buf_tag_t) &ngx_http_gunzip_filter_module);
+ ctx->nomem = 0;
+ }
+
+ for ( ;; ) {
+
+ /* cycle while we can write to a client */
+
+ for ( ;; ) {
+
+ /* cycle while there is data to feed zlib and ... */
+
+ rc = ngx_http_gunzip_filter_add_data(r, ctx);
+
+ if (rc == NGX_DECLINED) {
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+ continue;
+ }
+
+
+ /* ... there are buffers to write zlib output */
+
+ rc = ngx_http_gunzip_filter_get_buf(r, ctx);
+
+ if (rc == NGX_DECLINED) {
+ break;
+ }
+
+ if (rc == NGX_ERROR) {
+ goto failed;
+ }
+
+ rc = ngx_http_gunzip_filter_inflate(r, ctx);
+
+ if (rc == NGX_OK) {
+ break;
+ }
+
+ if (rc == NGX_ERROR) {
+ goto failed;
+ }
+
+ /* rc == NGX_AGAIN */
+ }
+
+ if (ctx->out == NULL) {
+ return ctx->busy ? NGX_AGAIN : NGX_OK;
+ }
+
+ rc = ngx_http_next_body_filter(r, ctx->out);
+
+ if (rc == NGX_ERROR) {
+ goto failed;
+ }
+
+ ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
+ (ngx_buf_tag_t) &ngx_http_gunzip_filter_module);
+ ctx->last_out = &ctx->out;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gunzip out: %p", ctx->out);
+
+ ctx->nomem = 0;
+
+ if (ctx->done) {
+ return rc;
+ }
+ }
+
+ /* unreachable */
+
+failed:
+
+ ctx->done = 1;
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx)
+{
+ int rc;
+
+ ctx->zstream.next_in = Z_NULL;
+ ctx->zstream.avail_in = 0;
+
+ ctx->zstream.zalloc = ngx_http_gunzip_filter_alloc;
+ ctx->zstream.zfree = ngx_http_gunzip_filter_free;
+ ctx->zstream.opaque = ctx;
+
+ /* windowBits +16 to decode gzip, zlib 1.2.0.4+ */
+ rc = inflateInit2(&ctx->zstream, MAX_WBITS + 16);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "inflateInit2() failed: %d", rc);
+ return NGX_ERROR;
+ }
+
+ ctx->started = 1;
+
+ ctx->last_out = &ctx->out;
+ ctx->flush = Z_NO_FLUSH;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx)
+{
+ if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {
+ return NGX_OK;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gunzip in: %p", ctx->in);
+
+ if (ctx->in == NULL) {
+ return NGX_DECLINED;
+ }
+
+ ctx->in_buf = ctx->in->buf;
+ ctx->in = ctx->in->next;
+
+ ctx->zstream.next_in = ctx->in_buf->pos;
+ ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gunzip in_buf:%p ni:%p ai:%ud",
+ ctx->in_buf,
+ ctx->zstream.next_in, ctx->zstream.avail_in);
+
+ if (ctx->in_buf->last_buf || ctx->in_buf->last_in_chain) {
+ ctx->flush = Z_FINISH;
+
+ } else if (ctx->in_buf->flush) {
+ ctx->flush = Z_SYNC_FLUSH;
+
+ } else if (ctx->zstream.avail_in == 0) {
+ /* ctx->flush == Z_NO_FLUSH */
+ return NGX_AGAIN;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx)
+{
+ ngx_http_gunzip_conf_t *conf;
+
+ if (ctx->zstream.avail_out) {
+ return NGX_OK;
+ }
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);
+
+ if (ctx->free) {
+ ctx->out_buf = ctx->free->buf;
+ ctx->free = ctx->free->next;
+
+ ctx->out_buf->flush = 0;
+
+ } else if (ctx->bufs < conf->bufs.num) {
+
+ ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);
+ if (ctx->out_buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gunzip_filter_module;
+ ctx->out_buf->recycled = 1;
+ ctx->bufs++;
+
+ } else {
+ ctx->nomem = 1;
+ return NGX_DECLINED;
+ }
+
+ ctx->zstream.next_out = ctx->out_buf->pos;
+ ctx->zstream.avail_out = conf->bufs.size;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx)
+{
+ int rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+
+ ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "inflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
+ ctx->zstream.next_in, ctx->zstream.next_out,
+ ctx->zstream.avail_in, ctx->zstream.avail_out,
+ ctx->flush, ctx->redo);
+
+ rc = inflate(&ctx->zstream, ctx->flush);
+
+ if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "inflate() failed: %d, %d", ctx->flush, rc);
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
+ ctx->zstream.next_in, ctx->zstream.next_out,
+ ctx->zstream.avail_in, ctx->zstream.avail_out,
+ rc);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gunzip in_buf:%p pos:%p",
+ ctx->in_buf, ctx->in_buf->pos);
+
+ if (ctx->zstream.next_in) {
+ ctx->in_buf->pos = ctx->zstream.next_in;
+
+ if (ctx->zstream.avail_in == 0) {
+ ctx->zstream.next_in = NULL;
+ }
+ }
+
+ ctx->out_buf->last = ctx->zstream.next_out;
+
+ if (ctx->zstream.avail_out == 0) {
+
+ /* zlib wants to output some more data */
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = ctx->out_buf;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ ctx->redo = 1;
+
+ return NGX_AGAIN;
+ }
+
+ ctx->redo = 0;
+
+ if (ctx->flush == Z_SYNC_FLUSH) {
+
+ ctx->flush = Z_NO_FLUSH;
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = ctx->out_buf;
+
+ if (ngx_buf_size(b) == 0) {
+
+ b = ngx_calloc_buf(ctx->request->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ ctx->zstream.avail_out = 0;
+ }
+
+ b->flush = 1;
+
+ cl->buf = b;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ return NGX_OK;
+ }
+
+ if (ctx->flush == Z_FINISH && ctx->zstream.avail_in == 0) {
+
+ if (rc != Z_STREAM_END) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "inflate() returned %d on response end", rc);
+ return NGX_ERROR;
+ }
+
+ if (ngx_http_gunzip_filter_inflate_end(r, ctx) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+
+ if (rc == Z_STREAM_END && ctx->zstream.avail_in > 0) {
+
+ rc = inflateReset(&ctx->zstream);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "inflateReset() failed: %d", rc);
+ return NGX_ERROR;
+ }
+
+ ctx->redo = 1;
+
+ return NGX_AGAIN;
+ }
+
+ if (ctx->in == NULL) {
+
+ b = ctx->out_buf;
+
+ if (ngx_buf_size(b) == 0) {
+ return NGX_OK;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->zstream.avail_out = 0;
+
+ cl->buf = b;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ return NGX_OK;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,
+ ngx_http_gunzip_ctx_t *ctx)
+{
+ int rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gunzip inflate end");
+
+ rc = inflateEnd(&ctx->zstream);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "inflateEnd() failed: %d", rc);
+ return NGX_ERROR;
+ }
+
+ b = ctx->out_buf;
+
+ if (ngx_buf_size(b) == 0) {
+
+ b = ngx_calloc_buf(ctx->request->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ b->last_buf = (r == r->main) ? 1 : 0;
+ b->last_in_chain = 1;
+ b->sync = 1;
+
+ ctx->done = 1;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_gunzip_filter_alloc(void *opaque, u_int items, u_int size)
+{
+ ngx_http_gunzip_ctx_t *ctx = opaque;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
+ "gunzip alloc: n:%ud s:%ud",
+ items, size);
+
+ return ngx_palloc(ctx->request->pool, items * size);
+}
+
+
+static void
+ngx_http_gunzip_filter_free(void *opaque, void *address)
+{
+#if 0
+ ngx_http_gunzip_ctx_t *ctx = opaque;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
+ "gunzip free: %p", address);
+#endif
+}
+
+
+static void *
+ngx_http_gunzip_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_gunzip_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gunzip_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->bufs.num = 0;
+ */
+
+ conf->enable = NGX_CONF_UNSET;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_gunzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_gunzip_conf_t *prev = parent;
+ ngx_http_gunzip_conf_t *conf = child;
+
+ ngx_conf_merge_value(conf->enable, prev->enable, 0);
+
+ ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,
+ (128 * 1024) / ngx_pagesize, ngx_pagesize);
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gunzip_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_gunzip_header_filter;
+
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_gunzip_body_filter;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_gzip_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_gzip_filter_module.c
new file mode 100644
index 00000000000..c57a4a3c7cc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_gzip_filter_module.c
@@ -0,0 +1,1229 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <zlib.h>
+
+
+typedef struct {
+ ngx_flag_t enable;
+ ngx_flag_t no_buffer;
+
+ ngx_hash_t types;
+
+ ngx_bufs_t bufs;
+
+ size_t postpone_gzipping;
+ ngx_int_t level;
+ size_t wbits;
+ size_t memlevel;
+ ssize_t min_length;
+
+ ngx_array_t *types_keys;
+} ngx_http_gzip_conf_t;
+
+
+typedef struct {
+ ngx_chain_t *in;
+ ngx_chain_t *free;
+ ngx_chain_t *busy;
+ ngx_chain_t *out;
+ ngx_chain_t **last_out;
+
+ ngx_chain_t *copied;
+ ngx_chain_t *copy_buf;
+
+ ngx_buf_t *in_buf;
+ ngx_buf_t *out_buf;
+ ngx_int_t bufs;
+
+ void *preallocated;
+ char *free_mem;
+ ngx_uint_t allocated;
+
+ int wbits;
+ int memlevel;
+
+ unsigned flush:4;
+ unsigned redo:1;
+ unsigned done:1;
+ unsigned nomem:1;
+ unsigned gzheader:1;
+ unsigned buffering:1;
+
+ size_t zin;
+ size_t zout;
+
+ uint32_t crc32;
+ z_stream zstream;
+ ngx_http_request_t *request;
+} ngx_http_gzip_ctx_t;
+
+
+#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
+
+struct gztrailer {
+ uint32_t crc32;
+ uint32_t zlen;
+};
+
+#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */
+
+struct gztrailer {
+ u_char crc32[4];
+ u_char zlen[4];
+};
+
+#endif
+
+
+static void ngx_http_gzip_filter_memory(ngx_http_request_t *r,
+ ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx,
+ ngx_chain_t *in);
+static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
+ ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r,
+ ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r,
+ ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r,
+ ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r,
+ ngx_http_gzip_ctx_t *ctx);
+static ngx_int_t ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
+ ngx_http_gzip_ctx_t *ctx);
+
+static void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,
+ u_int size);
+static void ngx_http_gzip_filter_free(void *opaque, void *address);
+static void ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
+ ngx_http_gzip_ctx_t *ctx);
+
+static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf);
+static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_gzip_filter_init(ngx_conf_t *cf);
+static void *ngx_http_gzip_create_conf(ngx_conf_t *cf);
+static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static char *ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data);
+
+
+static ngx_conf_num_bounds_t ngx_http_gzip_comp_level_bounds = {
+ ngx_conf_check_num_bounds, 1, 9
+};
+
+static ngx_conf_post_handler_pt ngx_http_gzip_window_p = ngx_http_gzip_window;
+static ngx_conf_post_handler_pt ngx_http_gzip_hash_p = ngx_http_gzip_hash;
+
+
+static ngx_command_t ngx_http_gzip_filter_commands[] = {
+
+ { ngx_string("gzip"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_gzip_conf_t, enable),
+ NULL },
+
+ { ngx_string("gzip_buffers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_conf_set_bufs_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_gzip_conf_t, bufs),
+ NULL },
+
+ { ngx_string("gzip_types"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_types_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_gzip_conf_t, types_keys),
+ &ngx_http_html_default_types[0] },
+
+ { ngx_string("gzip_comp_level"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_gzip_conf_t, level),
+ &ngx_http_gzip_comp_level_bounds },
+
+ { ngx_string("gzip_window"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_gzip_conf_t, wbits),
+ &ngx_http_gzip_window_p },
+
+ { ngx_string("gzip_hash"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_gzip_conf_t, memlevel),
+ &ngx_http_gzip_hash_p },
+
+ { ngx_string("postpone_gzipping"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_gzip_conf_t, postpone_gzipping),
+ NULL },
+
+ { ngx_string("gzip_no_buffer"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_gzip_conf_t, no_buffer),
+ NULL },
+
+ { ngx_string("gzip_min_length"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_gzip_conf_t, min_length),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_gzip_filter_module_ctx = {
+ ngx_http_gzip_add_variables, /* preconfiguration */
+ ngx_http_gzip_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_gzip_create_conf, /* create location configuration */
+ ngx_http_gzip_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_gzip_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_gzip_filter_module_ctx, /* module context */
+ ngx_http_gzip_filter_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t ngx_http_gzip_ratio = ngx_string("gzip_ratio");
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_gzip_header_filter(ngx_http_request_t *r)
+{
+ ngx_table_elt_t *h;
+ ngx_http_gzip_ctx_t *ctx;
+ ngx_http_gzip_conf_t *conf;
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+ if (!conf->enable
+ || (r->headers_out.status != NGX_HTTP_OK
+ && r->headers_out.status != NGX_HTTP_FORBIDDEN
+ && r->headers_out.status != NGX_HTTP_NOT_FOUND)
+ || (r->headers_out.content_encoding
+ && r->headers_out.content_encoding->value.len)
+ || (r->headers_out.content_length_n != -1
+ && r->headers_out.content_length_n < conf->min_length)
+ || ngx_http_test_content_type(r, &conf->types) == NULL
+ || r->header_only)
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ r->gzip_vary = 1;
+
+#if (NGX_HTTP_DEGRADATION)
+ {
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->gzip_disable_degradation && ngx_http_degraded(r)) {
+ return ngx_http_next_header_filter(r);
+ }
+ }
+#endif
+
+ if (!r->gzip_tested) {
+ if (ngx_http_gzip_ok(r) != NGX_OK) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ } else if (!r->gzip_ok) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gzip_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);
+
+ ctx->request = r;
+ ctx->buffering = (conf->postpone_gzipping != 0);
+
+ ngx_http_gzip_filter_memory(r, ctx);
+
+ h = ngx_list_push(&r->headers_out.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = 1;
+ ngx_str_set(&h->key, "Content-Encoding");
+ ngx_str_set(&h->value, "gzip");
+ r->headers_out.content_encoding = h;
+
+ r->main_filter_need_in_memory = 1;
+
+ ngx_http_clear_content_length(r);
+ ngx_http_clear_accept_ranges(r);
+ ngx_http_weak_etag(r);
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ int rc;
+ ngx_chain_t *cl;
+ ngx_http_gzip_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
+
+ if (ctx == NULL || ctx->done || r->header_only) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http gzip filter");
+
+ if (ctx->buffering) {
+
+ /*
+ * With default memory settings zlib starts to output gzipped data
+ * only after it has got about 90K, so it makes sense to allocate
+ * zlib memory (200-400K) only after we have enough data to compress.
+ * Although we copy buffers, nevertheless for not big responses
+ * this allows to allocate zlib memory, to compress and to output
+ * the response in one step using hot CPU cache.
+ */
+
+ if (in) {
+ switch (ngx_http_gzip_filter_buffer(ctx, in)) {
+
+ case NGX_OK:
+ return NGX_OK;
+
+ case NGX_DONE:
+ in = NULL;
+ break;
+
+ default: /* NGX_ERROR */
+ goto failed;
+ }
+
+ } else {
+ ctx->buffering = 0;
+ }
+ }
+
+ if (ctx->preallocated == NULL) {
+ if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) {
+ goto failed;
+ }
+ }
+
+ if (in) {
+ if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
+ goto failed;
+ }
+
+ r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
+ }
+
+ if (ctx->nomem || in == NULL) {
+
+ /* flush busy buffers */
+
+ if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {
+ goto failed;
+ }
+
+ cl = NULL;
+
+ ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,
+ (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
+ ctx->nomem = 0;
+ }
+
+ for ( ;; ) {
+
+ /* cycle while we can write to a client */
+
+ for ( ;; ) {
+
+ /* cycle while there is data to feed zlib and ... */
+
+ rc = ngx_http_gzip_filter_add_data(r, ctx);
+
+ if (rc == NGX_DECLINED) {
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+ continue;
+ }
+
+
+ /* ... there are buffers to write zlib output */
+
+ rc = ngx_http_gzip_filter_get_buf(r, ctx);
+
+ if (rc == NGX_DECLINED) {
+ break;
+ }
+
+ if (rc == NGX_ERROR) {
+ goto failed;
+ }
+
+
+ rc = ngx_http_gzip_filter_deflate(r, ctx);
+
+ if (rc == NGX_OK) {
+ break;
+ }
+
+ if (rc == NGX_ERROR) {
+ goto failed;
+ }
+
+ /* rc == NGX_AGAIN */
+ }
+
+ if (ctx->out == NULL) {
+ ngx_http_gzip_filter_free_copy_buf(r, ctx);
+
+ return ctx->busy ? NGX_AGAIN : NGX_OK;
+ }
+
+ if (!ctx->gzheader) {
+ if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) {
+ goto failed;
+ }
+ }
+
+ rc = ngx_http_next_body_filter(r, ctx->out);
+
+ if (rc == NGX_ERROR) {
+ goto failed;
+ }
+
+ ngx_http_gzip_filter_free_copy_buf(r, ctx);
+
+ ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,
+ (ngx_buf_tag_t) &ngx_http_gzip_filter_module);
+ ctx->last_out = &ctx->out;
+
+ ctx->nomem = 0;
+
+ if (ctx->done) {
+ return rc;
+ }
+ }
+
+ /* unreachable */
+
+failed:
+
+ ctx->done = 1;
+
+ if (ctx->preallocated) {
+ deflateEnd(&ctx->zstream);
+
+ ngx_pfree(r->pool, ctx->preallocated);
+ }
+
+ ngx_http_gzip_filter_free_copy_buf(r, ctx);
+
+ return NGX_ERROR;
+}
+
+
+static void
+ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+ int wbits, memlevel;
+ ngx_http_gzip_conf_t *conf;
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+ wbits = conf->wbits;
+ memlevel = conf->memlevel;
+
+ if (r->headers_out.content_length_n > 0) {
+
+ /* the actual zlib window size is smaller by 262 bytes */
+
+ while (r->headers_out.content_length_n < ((1 << (wbits - 1)) - 262)) {
+ wbits--;
+ memlevel--;
+ }
+
+ if (memlevel < 1) {
+ memlevel = 1;
+ }
+ }
+
+ ctx->wbits = wbits;
+ ctx->memlevel = memlevel;
+
+ /*
+ * We preallocate a memory for zlib in one buffer (200K-400K), this
+ * decreases a number of malloc() and free() calls and also probably
+ * decreases a number of syscalls (sbrk()/mmap() and so on).
+ * Besides we free the memory as soon as a gzipping will complete
+ * and do not wait while a whole response will be sent to a client.
+ *
+ * 8K is for zlib deflate_state, it takes
+ * *) 5816 bytes on i386 and sparc64 (32-bit mode)
+ * *) 5920 bytes on amd64 and sparc64
+ */
+
+ ctx->allocated = 8192 + (1 << (wbits + 2)) + (1 << (memlevel + 9));
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx, ngx_chain_t *in)
+{
+ size_t size, buffered;
+ ngx_buf_t *b, *buf;
+ ngx_chain_t *cl, **ll;
+ ngx_http_request_t *r;
+ ngx_http_gzip_conf_t *conf;
+
+ r = ctx->request;
+
+ r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
+
+ buffered = 0;
+ ll = &ctx->in;
+
+ for (cl = ctx->in; cl; cl = cl->next) {
+ buffered += cl->buf->last - cl->buf->pos;
+ ll = &cl->next;
+ }
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+ while (in) {
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = in->buf;
+
+ size = b->last - b->pos;
+ buffered += size;
+
+ if (b->flush || b->last_buf || buffered > conf->postpone_gzipping) {
+ ctx->buffering = 0;
+ }
+
+ if (ctx->buffering && size) {
+
+ buf = ngx_create_temp_buf(r->pool, size);
+ if (buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf->last = ngx_cpymem(buf->pos, b->pos, size);
+ b->pos = b->last;
+
+ buf->last_buf = b->last_buf;
+ buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
+
+ cl->buf = buf;
+
+ } else {
+ cl->buf = b;
+ }
+
+ *ll = cl;
+ ll = &cl->next;
+ in = in->next;
+ }
+
+ *ll = NULL;
+
+ return ctx->buffering ? NGX_OK : NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
+ ngx_http_gzip_ctx_t *ctx)
+{
+ int rc;
+ ngx_http_gzip_conf_t *conf;
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+ ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);
+ if (ctx->preallocated == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->free_mem = ctx->preallocated;
+
+ ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;
+ ctx->zstream.zfree = ngx_http_gzip_filter_free;
+ ctx->zstream.opaque = ctx;
+
+ rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
+ - ctx->wbits, ctx->memlevel, Z_DEFAULT_STRATEGY);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "deflateInit2() failed: %d", rc);
+ return NGX_ERROR;
+ }
+
+ ctx->last_out = &ctx->out;
+ ctx->crc32 = crc32(0L, Z_NULL, 0);
+ ctx->flush = Z_NO_FLUSH;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+ static u_char gzheader[10] =
+ { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
+
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->memory = 1;
+ b->pos = gzheader;
+ b->last = b->pos + 10;
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ cl->next = ctx->out;
+ ctx->out = cl;
+
+ ctx->gzheader = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+ if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {
+ return NGX_OK;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gzip in: %p", ctx->in);
+
+ if (ctx->in == NULL) {
+ return NGX_DECLINED;
+ }
+
+ if (ctx->copy_buf) {
+
+ /*
+ * to avoid CPU cache trashing we do not free() just quit buf,
+ * but postpone free()ing after zlib compressing and data output
+ */
+
+ ctx->copy_buf->next = ctx->copied;
+ ctx->copied = ctx->copy_buf;
+ ctx->copy_buf = NULL;
+ }
+
+ ctx->in_buf = ctx->in->buf;
+
+ if (ctx->in_buf->tag == (ngx_buf_tag_t) &ngx_http_gzip_filter_module) {
+ ctx->copy_buf = ctx->in;
+ }
+
+ ctx->in = ctx->in->next;
+
+ ctx->zstream.next_in = ctx->in_buf->pos;
+ ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gzip in_buf:%p ni:%p ai:%ud",
+ ctx->in_buf,
+ ctx->zstream.next_in, ctx->zstream.avail_in);
+
+ if (ctx->in_buf->last_buf) {
+ ctx->flush = Z_FINISH;
+
+ } else if (ctx->in_buf->flush) {
+ ctx->flush = Z_SYNC_FLUSH;
+ }
+
+ if (ctx->zstream.avail_in) {
+
+ ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,
+ ctx->zstream.avail_in);
+
+ } else if (ctx->flush == Z_NO_FLUSH) {
+ return NGX_AGAIN;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+ ngx_http_gzip_conf_t *conf;
+
+ if (ctx->zstream.avail_out) {
+ return NGX_OK;
+ }
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+ if (ctx->free) {
+ ctx->out_buf = ctx->free->buf;
+ ctx->free = ctx->free->next;
+
+ } else if (ctx->bufs < conf->bufs.num) {
+
+ ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);
+ if (ctx->out_buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;
+ ctx->out_buf->recycled = 1;
+ ctx->bufs++;
+
+ } else {
+ ctx->nomem = 1;
+ return NGX_DECLINED;
+ }
+
+ ctx->zstream.next_out = ctx->out_buf->pos;
+ ctx->zstream.avail_out = conf->bufs.size;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
+{
+ int rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+ ngx_http_gzip_conf_t *conf;
+
+ ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d",
+ ctx->zstream.next_in, ctx->zstream.next_out,
+ ctx->zstream.avail_in, ctx->zstream.avail_out,
+ ctx->flush, ctx->redo);
+
+ rc = deflate(&ctx->zstream, ctx->flush);
+
+ if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "deflate() failed: %d, %d", ctx->flush, rc);
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
+ ctx->zstream.next_in, ctx->zstream.next_out,
+ ctx->zstream.avail_in, ctx->zstream.avail_out,
+ rc);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "gzip in_buf:%p pos:%p",
+ ctx->in_buf, ctx->in_buf->pos);
+
+ if (ctx->zstream.next_in) {
+ ctx->in_buf->pos = ctx->zstream.next_in;
+
+ if (ctx->zstream.avail_in == 0) {
+ ctx->zstream.next_in = NULL;
+ }
+ }
+
+ ctx->out_buf->last = ctx->zstream.next_out;
+
+ if (ctx->zstream.avail_out == 0) {
+
+ /* zlib wants to output some more gzipped data */
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = ctx->out_buf;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ ctx->redo = 1;
+
+ return NGX_AGAIN;
+ }
+
+ ctx->redo = 0;
+
+ if (ctx->flush == Z_SYNC_FLUSH) {
+
+ ctx->flush = Z_NO_FLUSH;
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = ctx->out_buf;
+
+ if (ngx_buf_size(b) == 0) {
+
+ b = ngx_calloc_buf(ctx->request->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ ctx->zstream.avail_out = 0;
+ }
+
+ b->flush = 1;
+
+ cl->buf = b;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
+
+ return NGX_OK;
+ }
+
+ if (rc == Z_STREAM_END) {
+
+ if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
+
+ if (conf->no_buffer && ctx->in == NULL) {
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = ctx->out_buf;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ return NGX_OK;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
+ ngx_http_gzip_ctx_t *ctx)
+{
+ int rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+ struct gztrailer *trailer;
+
+ ctx->zin = ctx->zstream.total_in;
+ ctx->zout = 10 + ctx->zstream.total_out + 8;
+
+ rc = deflateEnd(&ctx->zstream);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "deflateEnd() failed: %d", rc);
+ return NGX_ERROR;
+ }
+
+ ngx_pfree(r->pool, ctx->preallocated);
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = ctx->out_buf;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ if (ctx->zstream.avail_out >= 8) {
+ trailer = (struct gztrailer *) ctx->out_buf->last;
+ ctx->out_buf->last += 8;
+ ctx->out_buf->last_buf = 1;
+
+ } else {
+ b = ngx_create_temp_buf(r->pool, 8);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->last_buf = 1;
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+ trailer = (struct gztrailer *) b->pos;
+ b->last += 8;
+ }
+
+#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
+
+ trailer->crc32 = ctx->crc32;
+ trailer->zlen = ctx->zin;
+
+#else
+
+ trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff);
+ trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff);
+ trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff);
+ trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff);
+
+ trailer->zlen[0] = (u_char) (ctx->zin & 0xff);
+ trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff);
+ trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff);
+ trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff);
+
+#endif
+
+ ctx->zstream.avail_in = 0;
+ ctx->zstream.avail_out = 0;
+
+ ctx->done = 1;
+
+ r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size)
+{
+ ngx_http_gzip_ctx_t *ctx = opaque;
+
+ void *p;
+ ngx_uint_t alloc;
+
+ alloc = items * size;
+
+ if (alloc % 512 != 0 && alloc < 8192) {
+
+ /*
+ * The zlib deflate_state allocation, it takes about 6K,
+ * we allocate 8K. Other allocations are divisible by 512.
+ */
+
+ alloc = 8192;
+ }
+
+ if (alloc <= ctx->allocated) {
+ p = ctx->free_mem;
+ ctx->free_mem += alloc;
+ ctx->allocated -= alloc;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
+ "gzip alloc: n:%ud s:%ud a:%ud p:%p",
+ items, size, alloc, p);
+
+ return p;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0,
+ "gzip filter failed to use preallocated memory: %ud of %ud",
+ items * size, ctx->allocated);
+
+ p = ngx_palloc(ctx->request->pool, items * size);
+
+ return p;
+}
+
+
+static void
+ngx_http_gzip_filter_free(void *opaque, void *address)
+{
+#if 0
+ ngx_http_gzip_ctx_t *ctx = opaque;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
+ "gzip free: %p", address);
+#endif
+}
+
+
+static void
+ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,
+ ngx_http_gzip_ctx_t *ctx)
+{
+ ngx_chain_t *cl;
+
+ for (cl = ctx->copied; cl; cl = cl->next) {
+ ngx_pfree(r->pool, cl->buf->start);
+ }
+
+ ctx->copied = NULL;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var;
+
+ var = ngx_http_add_variable(cf, &ngx_http_gzip_ratio, NGX_HTTP_VAR_NOHASH);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = ngx_http_gzip_ratio_variable;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_uint_t zint, zfrac;
+ ngx_http_gzip_ctx_t *ctx;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
+
+ if (ctx == NULL || ctx->zout == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN + 3);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ zint = (ngx_uint_t) (ctx->zin / ctx->zout);
+ zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100);
+
+ if ((ctx->zin * 1000 / ctx->zout) % 10 > 4) {
+
+ /* the rounding, e.g., 2.125 to 2.13 */
+
+ zfrac++;
+
+ if (zfrac > 99) {
+ zint++;
+ zfrac = 0;
+ }
+ }
+
+ v->len = ngx_sprintf(v->data, "%ui.%02ui", zint, zfrac) - v->data;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_gzip_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_gzip_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->bufs.num = 0;
+ * conf->types = { NULL };
+ * conf->types_keys = NULL;
+ */
+
+ conf->enable = NGX_CONF_UNSET;
+ conf->no_buffer = NGX_CONF_UNSET;
+
+ conf->postpone_gzipping = NGX_CONF_UNSET_SIZE;
+ conf->level = NGX_CONF_UNSET;
+ conf->wbits = NGX_CONF_UNSET_SIZE;
+ conf->memlevel = NGX_CONF_UNSET_SIZE;
+ conf->min_length = NGX_CONF_UNSET;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_gzip_conf_t *prev = parent;
+ ngx_http_gzip_conf_t *conf = child;
+
+ ngx_conf_merge_value(conf->enable, prev->enable, 0);
+ ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);
+
+ ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,
+ (128 * 1024) / ngx_pagesize, ngx_pagesize);
+
+ ngx_conf_merge_size_value(conf->postpone_gzipping, prev->postpone_gzipping,
+ 0);
+ ngx_conf_merge_value(conf->level, prev->level, 1);
+ ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);
+ ngx_conf_merge_size_value(conf->memlevel, prev->memlevel,
+ MAX_MEM_LEVEL - 1);
+ ngx_conf_merge_value(conf->min_length, prev->min_length, 20);
+
+ if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
+ &prev->types_keys, &prev->types,
+ ngx_http_html_default_types)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_gzip_header_filter;
+
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_gzip_body_filter;
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data)
+{
+ size_t *np = data;
+
+ size_t wbits, wsize;
+
+ wbits = 15;
+
+ for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) {
+
+ if (wsize == *np) {
+ *np = wbits;
+
+ return NGX_CONF_OK;
+ }
+
+ wbits--;
+ }
+
+ return "must be 512, 1k, 2k, 4k, 8k, 16k, or 32k";
+}
+
+
+static char *
+ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data)
+{
+ size_t *np = data;
+
+ size_t memlevel, hsize;
+
+ memlevel = 9;
+
+ for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) {
+
+ if (hsize == *np) {
+ *np = memlevel;
+
+ return NGX_CONF_OK;
+ }
+
+ memlevel--;
+ }
+
+ return "must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k";
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_gzip_static_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_gzip_static_module.c
new file mode 100644
index 00000000000..4d54090772b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_gzip_static_module.c
@@ -0,0 +1,331 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_GZIP_STATIC_OFF 0
+#define NGX_HTTP_GZIP_STATIC_ON 1
+#define NGX_HTTP_GZIP_STATIC_ALWAYS 2
+
+
+typedef struct {
+ ngx_uint_t enable;
+} ngx_http_gzip_static_conf_t;
+
+
+static ngx_int_t ngx_http_gzip_static_handler(ngx_http_request_t *r);
+static void *ngx_http_gzip_static_create_conf(ngx_conf_t *cf);
+static char *ngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static ngx_int_t ngx_http_gzip_static_init(ngx_conf_t *cf);
+
+
+static ngx_conf_enum_t ngx_http_gzip_static[] = {
+ { ngx_string("off"), NGX_HTTP_GZIP_STATIC_OFF },
+ { ngx_string("on"), NGX_HTTP_GZIP_STATIC_ON },
+ { ngx_string("always"), NGX_HTTP_GZIP_STATIC_ALWAYS },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_command_t ngx_http_gzip_static_commands[] = {
+
+ { ngx_string("gzip_static"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_gzip_static_conf_t, enable),
+ &ngx_http_gzip_static },
+
+ ngx_null_command
+};
+
+
+ngx_http_module_t ngx_http_gzip_static_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_gzip_static_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_gzip_static_create_conf, /* create location configuration */
+ ngx_http_gzip_static_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_gzip_static_module = {
+ NGX_MODULE_V1,
+ &ngx_http_gzip_static_module_ctx, /* module context */
+ ngx_http_gzip_static_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_gzip_static_handler(ngx_http_request_t *r)
+{
+ u_char *p;
+ size_t root;
+ ngx_str_t path;
+ ngx_int_t rc;
+ ngx_uint_t level;
+ ngx_log_t *log;
+ ngx_buf_t *b;
+ ngx_chain_t out;
+ ngx_table_elt_t *h;
+ ngx_open_file_info_t of;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_gzip_static_conf_t *gzcf;
+
+ if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
+ return NGX_DECLINED;
+ }
+
+ if (r->uri.data[r->uri.len - 1] == '/') {
+ return NGX_DECLINED;
+ }
+
+ gzcf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_static_module);
+
+ if (gzcf->enable == NGX_HTTP_GZIP_STATIC_OFF) {
+ return NGX_DECLINED;
+ }
+
+ if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {
+ rc = ngx_http_gzip_ok(r);
+
+ } else {
+ /* always */
+ rc = NGX_OK;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (!clcf->gzip_vary && rc != NGX_OK) {
+ return NGX_DECLINED;
+ }
+
+ log = r->connection->log;
+
+ p = ngx_http_map_uri_to_path(r, &path, &root, sizeof(".gz") - 1);
+ if (p == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ *p++ = '.';
+ *p++ = 'g';
+ *p++ = 'z';
+ *p = '\0';
+
+ path.len = p - path.data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
+ "http filename: \"%s\"", path.data);
+
+ ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+ of.read_ahead = clcf->read_ahead;
+ of.directio = clcf->directio;
+ of.valid = clcf->open_file_cache_valid;
+ of.min_uses = clcf->open_file_cache_min_uses;
+ of.errors = clcf->open_file_cache_errors;
+ of.events = clcf->open_file_cache_events;
+
+ if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+ != NGX_OK)
+ {
+ switch (of.err) {
+
+ case 0:
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+
+ case NGX_ENOENT:
+ case NGX_ENOTDIR:
+ case NGX_ENAMETOOLONG:
+
+ return NGX_DECLINED;
+
+ case NGX_EACCES:
+#if (NGX_HAVE_OPENAT)
+ case NGX_EMLINK:
+ case NGX_ELOOP:
+#endif
+
+ level = NGX_LOG_ERR;
+ break;
+
+ default:
+
+ level = NGX_LOG_CRIT;
+ break;
+ }
+
+ ngx_log_error(level, log, of.err,
+ "%s \"%s\" failed", of.failed, path.data);
+
+ return NGX_DECLINED;
+ }
+
+ if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {
+ r->gzip_vary = 1;
+
+ if (rc != NGX_OK) {
+ return NGX_DECLINED;
+ }
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);
+
+ if (of.is_dir) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");
+ return NGX_DECLINED;
+ }
+
+#if !(NGX_WIN32) /* the not regular files are probably Unix specific */
+
+ if (!of.is_file) {
+ ngx_log_error(NGX_LOG_CRIT, log, 0,
+ "\"%s\" is not a regular file", path.data);
+
+ return NGX_HTTP_NOT_FOUND;
+ }
+
+#endif
+
+ r->root_tested = !r->error_page;
+
+ rc = ngx_http_discard_request_body(r);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ log->action = "sending response to client";
+
+ r->headers_out.status = NGX_HTTP_OK;
+ r->headers_out.content_length_n = of.size;
+ r->headers_out.last_modified_time = of.mtime;
+
+ if (ngx_http_set_etag(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_http_set_content_type(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ h = ngx_list_push(&r->headers_out.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = 1;
+ ngx_str_set(&h->key, "Content-Encoding");
+ ngx_str_set(&h->value, "gzip");
+ r->headers_out.content_encoding = h;
+
+ /* we need to allocate all before the header would be sent */
+
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
+ if (b->file == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ return rc;
+ }
+
+ b->file_pos = 0;
+ b->file_last = of.size;
+
+ b->in_file = b->file_last ? 1 : 0;
+ b->last_buf = (r == r->main) ? 1 : 0;
+ b->last_in_chain = 1;
+
+ b->file->fd = of.fd;
+ b->file->name = path;
+ b->file->log = log;
+ b->file->directio = of.is_directio;
+
+ out.buf = b;
+ out.next = NULL;
+
+ return ngx_http_output_filter(r, &out);
+}
+
+
+static void *
+ngx_http_gzip_static_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_gzip_static_conf_t *conf;
+
+ conf = ngx_palloc(cf->pool, sizeof(ngx_http_gzip_static_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->enable = NGX_CONF_UNSET_UINT;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_gzip_static_conf_t *prev = parent;
+ ngx_http_gzip_static_conf_t *conf = child;
+
+ ngx_conf_merge_uint_value(conf->enable, prev->enable,
+ NGX_HTTP_GZIP_STATIC_OFF);
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_gzip_static_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_gzip_static_handler;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_headers_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_headers_filter_module.c
new file mode 100644
index 00000000000..e33e7ce5271
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_headers_filter_module.c
@@ -0,0 +1,635 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct ngx_http_header_val_s ngx_http_header_val_t;
+
+typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,
+ ngx_http_header_val_t *hv, ngx_str_t *value);
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_uint_t offset;
+ ngx_http_set_header_pt handler;
+} ngx_http_set_header_t;
+
+
+struct ngx_http_header_val_s {
+ ngx_http_complex_value_t value;
+ ngx_str_t key;
+ ngx_http_set_header_pt handler;
+ ngx_uint_t offset;
+};
+
+
+typedef enum {
+ NGX_HTTP_EXPIRES_OFF,
+ NGX_HTTP_EXPIRES_EPOCH,
+ NGX_HTTP_EXPIRES_MAX,
+ NGX_HTTP_EXPIRES_ACCESS,
+ NGX_HTTP_EXPIRES_MODIFIED,
+ NGX_HTTP_EXPIRES_DAILY,
+ NGX_HTTP_EXPIRES_UNSET
+} ngx_http_expires_t;
+
+
+typedef struct {
+ ngx_http_expires_t expires;
+ time_t expires_time;
+ ngx_array_t *headers;
+} ngx_http_headers_conf_t;
+
+
+static ngx_int_t ngx_http_set_expires(ngx_http_request_t *r,
+ ngx_http_headers_conf_t *conf);
+static ngx_int_t ngx_http_add_cache_control(ngx_http_request_t *r,
+ ngx_http_header_val_t *hv, ngx_str_t *value);
+static ngx_int_t ngx_http_add_header(ngx_http_request_t *r,
+ ngx_http_header_val_t *hv, ngx_str_t *value);
+static ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r,
+ ngx_http_header_val_t *hv, ngx_str_t *value);
+static ngx_int_t ngx_http_set_response_header(ngx_http_request_t *r,
+ ngx_http_header_val_t *hv, ngx_str_t *value);
+
+static void *ngx_http_headers_create_conf(ngx_conf_t *cf);
+static char *ngx_http_headers_merge_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static ngx_int_t ngx_http_headers_filter_init(ngx_conf_t *cf);
+static char *ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_http_set_header_t ngx_http_set_headers[] = {
+
+ { ngx_string("Cache-Control"), 0, ngx_http_add_cache_control },
+
+ { ngx_string("Last-Modified"),
+ offsetof(ngx_http_headers_out_t, last_modified),
+ ngx_http_set_last_modified },
+
+ { ngx_string("ETag"),
+ offsetof(ngx_http_headers_out_t, etag),
+ ngx_http_set_response_header },
+
+ { ngx_null_string, 0, NULL }
+};
+
+
+static ngx_command_t ngx_http_headers_filter_commands[] = {
+
+ { ngx_string("expires"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_TAKE12,
+ ngx_http_headers_expires,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL},
+
+ { ngx_string("add_header"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_TAKE2,
+ ngx_http_headers_add,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL},
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_headers_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_headers_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_headers_create_conf, /* create location configuration */
+ ngx_http_headers_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_headers_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_headers_filter_module_ctx, /* module context */
+ ngx_http_headers_filter_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+
+
+static ngx_int_t
+ngx_http_headers_filter(ngx_http_request_t *r)
+{
+ ngx_str_t value;
+ ngx_uint_t i;
+ ngx_http_header_val_t *h;
+ ngx_http_headers_conf_t *conf;
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
+
+ if ((conf->expires == NGX_HTTP_EXPIRES_OFF && conf->headers == NULL)
+ || r != r->main
+ || (r->headers_out.status != NGX_HTTP_OK
+ && r->headers_out.status != NGX_HTTP_CREATED
+ && r->headers_out.status != NGX_HTTP_NO_CONTENT
+ && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT
+ && r->headers_out.status != NGX_HTTP_MOVED_PERMANENTLY
+ && r->headers_out.status != NGX_HTTP_MOVED_TEMPORARILY
+ && r->headers_out.status != NGX_HTTP_SEE_OTHER
+ && r->headers_out.status != NGX_HTTP_NOT_MODIFIED
+ && r->headers_out.status != NGX_HTTP_TEMPORARY_REDIRECT))
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ if (conf->expires != NGX_HTTP_EXPIRES_OFF) {
+ if (ngx_http_set_expires(r, conf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (conf->headers) {
+ h = conf->headers->elts;
+ for (i = 0; i < conf->headers->nelts; i++) {
+
+ if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (h[i].handler(r, &h[i], &value) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)
+{
+ size_t len;
+ time_t now, expires_time, max_age;
+ ngx_uint_t i;
+ ngx_table_elt_t *expires, *cc, **ccp;
+
+ expires = r->headers_out.expires;
+
+ if (expires == NULL) {
+
+ expires = ngx_list_push(&r->headers_out.headers);
+ if (expires == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->headers_out.expires = expires;
+
+ expires->hash = 1;
+ ngx_str_set(&expires->key, "Expires");
+ }
+
+ len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT");
+ expires->value.len = len - 1;
+
+ ccp = r->headers_out.cache_control.elts;
+
+ if (ccp == NULL) {
+
+ if (ngx_array_init(&r->headers_out.cache_control, r->pool,
+ 1, sizeof(ngx_table_elt_t *))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ ccp = ngx_array_push(&r->headers_out.cache_control);
+ if (ccp == NULL) {
+ return NGX_ERROR;
+ }
+
+ cc = ngx_list_push(&r->headers_out.headers);
+ if (cc == NULL) {
+ return NGX_ERROR;
+ }
+
+ cc->hash = 1;
+ ngx_str_set(&cc->key, "Cache-Control");
+ *ccp = cc;
+
+ } else {
+ for (i = 1; i < r->headers_out.cache_control.nelts; i++) {
+ ccp[i]->hash = 0;
+ }
+
+ cc = ccp[0];
+ }
+
+ if (conf->expires == NGX_HTTP_EXPIRES_EPOCH) {
+ expires->value.data = (u_char *) "Thu, 01 Jan 1970 00:00:01 GMT";
+ ngx_str_set(&cc->value, "no-cache");
+ return NGX_OK;
+ }
+
+ if (conf->expires == NGX_HTTP_EXPIRES_MAX) {
+ expires->value.data = (u_char *) "Thu, 31 Dec 2037 23:55:55 GMT";
+ /* 10 years */
+ ngx_str_set(&cc->value, "max-age=315360000");
+ return NGX_OK;
+ }
+
+ expires->value.data = ngx_pnalloc(r->pool, len);
+ if (expires->value.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (conf->expires_time == 0 && conf->expires != NGX_HTTP_EXPIRES_DAILY) {
+ ngx_memcpy(expires->value.data, ngx_cached_http_time.data,
+ ngx_cached_http_time.len + 1);
+ ngx_str_set(&cc->value, "max-age=0");
+ return NGX_OK;
+ }
+
+ now = ngx_time();
+
+ if (conf->expires == NGX_HTTP_EXPIRES_DAILY) {
+ expires_time = ngx_next_time(conf->expires_time);
+ max_age = expires_time - now;
+
+ } else if (conf->expires == NGX_HTTP_EXPIRES_ACCESS
+ || r->headers_out.last_modified_time == -1)
+ {
+ expires_time = now + conf->expires_time;
+ max_age = conf->expires_time;
+
+ } else {
+ expires_time = r->headers_out.last_modified_time + conf->expires_time;
+ max_age = expires_time - now;
+ }
+
+ ngx_http_time(expires->value.data, expires_time);
+
+ if (conf->expires_time < 0 || max_age < 0) {
+ ngx_str_set(&cc->value, "no-cache");
+ return NGX_OK;
+ }
+
+ cc->value.data = ngx_pnalloc(r->pool,
+ sizeof("max-age=") + NGX_TIME_T_LEN + 1);
+ if (cc->value.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ cc->value.len = ngx_sprintf(cc->value.data, "max-age=%T", max_age)
+ - cc->value.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+ ngx_str_t *value)
+{
+ ngx_table_elt_t *h;
+
+ if (value->len) {
+ h = ngx_list_push(&r->headers_out.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = 1;
+ h->key = hv->key;
+ h->value = *value;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_add_cache_control(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+ ngx_str_t *value)
+{
+ ngx_table_elt_t *cc, **ccp;
+
+ if (value->len == 0) {
+ return NGX_OK;
+ }
+
+ ccp = r->headers_out.cache_control.elts;
+
+ if (ccp == NULL) {
+
+ if (ngx_array_init(&r->headers_out.cache_control, r->pool,
+ 1, sizeof(ngx_table_elt_t *))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ ccp = ngx_array_push(&r->headers_out.cache_control);
+ if (ccp == NULL) {
+ return NGX_ERROR;
+ }
+
+ cc = ngx_list_push(&r->headers_out.headers);
+ if (cc == NULL) {
+ return NGX_ERROR;
+ }
+
+ cc->hash = 1;
+ ngx_str_set(&cc->key, "Cache-Control");
+ cc->value = *value;
+
+ *ccp = cc;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+ ngx_str_t *value)
+{
+ if (ngx_http_set_response_header(r, hv, value) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ r->headers_out.last_modified_time =
+ (value->len) ? ngx_http_parse_time(value->data, value->len) : -1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_set_response_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,
+ ngx_str_t *value)
+{
+ ngx_table_elt_t *h, **old;
+
+ old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);
+
+ if (value->len == 0) {
+ if (*old) {
+ (*old)->hash = 0;
+ *old = NULL;
+ }
+
+ return NGX_OK;
+ }
+
+ if (*old) {
+ h = *old;
+
+ } else {
+ h = ngx_list_push(&r->headers_out.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *old = h;
+ }
+
+ h->hash = 1;
+ h->key = hv->key;
+ h->value = *value;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_headers_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_headers_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_headers_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->headers = NULL;
+ * conf->expires_time = 0;
+ */
+
+ conf->expires = NGX_HTTP_EXPIRES_UNSET;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_headers_conf_t *prev = parent;
+ ngx_http_headers_conf_t *conf = child;
+
+ if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
+ conf->expires = prev->expires;
+ conf->expires_time = prev->expires_time;
+
+ if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
+ conf->expires = NGX_HTTP_EXPIRES_OFF;
+ }
+ }
+
+ if (conf->headers == NULL) {
+ conf->headers = prev->headers;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_headers_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_headers_filter;
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_headers_conf_t *hcf = conf;
+
+ ngx_uint_t minus, n;
+ ngx_str_t *value;
+
+ if (hcf->expires != NGX_HTTP_EXPIRES_UNSET) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (cf->args->nelts == 2) {
+
+ if (ngx_strcmp(value[1].data, "epoch") == 0) {
+ hcf->expires = NGX_HTTP_EXPIRES_EPOCH;
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_strcmp(value[1].data, "max") == 0) {
+ hcf->expires = NGX_HTTP_EXPIRES_MAX;
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ hcf->expires = NGX_HTTP_EXPIRES_OFF;
+ return NGX_CONF_OK;
+ }
+
+ hcf->expires = NGX_HTTP_EXPIRES_ACCESS;
+
+ n = 1;
+
+ } else { /* cf->args->nelts == 3 */
+
+ if (ngx_strcmp(value[1].data, "modified") != 0) {
+ return "invalid value";
+ }
+
+ hcf->expires = NGX_HTTP_EXPIRES_MODIFIED;
+
+ n = 2;
+ }
+
+ if (value[n].data[0] == '@') {
+ value[n].data++;
+ value[n].len--;
+ minus = 0;
+
+ if (hcf->expires == NGX_HTTP_EXPIRES_MODIFIED) {
+ return "daily time cannot be used with \"modified\" parameter";
+ }
+
+ hcf->expires = NGX_HTTP_EXPIRES_DAILY;
+
+ } else if (value[n].data[0] == '+') {
+ value[n].data++;
+ value[n].len--;
+ minus = 0;
+
+ } else if (value[n].data[0] == '-') {
+ value[n].data++;
+ value[n].len--;
+ minus = 1;
+
+ } else {
+ minus = 0;
+ }
+
+ hcf->expires_time = ngx_parse_time(&value[n], 1);
+
+ if (hcf->expires_time == (time_t) NGX_ERROR) {
+ return "invalid value";
+ }
+
+ if (hcf->expires == NGX_HTTP_EXPIRES_DAILY
+ && hcf->expires_time > 24 * 60 * 60)
+ {
+ return "daily time value must be less than 24 hours";
+ }
+
+ if (minus) {
+ hcf->expires_time = - hcf->expires_time;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_headers_conf_t *hcf = conf;
+
+ ngx_str_t *value;
+ ngx_uint_t i;
+ ngx_http_header_val_t *hv;
+ ngx_http_set_header_t *set;
+ ngx_http_compile_complex_value_t ccv;
+
+ value = cf->args->elts;
+
+ if (hcf->headers == NULL) {
+ hcf->headers = ngx_array_create(cf->pool, 1,
+ sizeof(ngx_http_header_val_t));
+ if (hcf->headers == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ hv = ngx_array_push(hcf->headers);
+ if (hv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ hv->key = value[1];
+ hv->handler = ngx_http_add_header;
+ hv->offset = 0;
+
+ set = ngx_http_set_headers;
+ for (i = 0; set[i].name.len; i++) {
+ if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) {
+ continue;
+ }
+
+ hv->offset = set[i].offset;
+ hv->handler = set[i].handler;
+
+ break;
+ }
+
+ if (value[2].len == 0) {
+ ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));
+ return NGX_CONF_OK;
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[2];
+ ccv.complex_value = &hv->value;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_image_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_image_filter_module.c
new file mode 100644
index 00000000000..c983b973b46
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_image_filter_module.c
@@ -0,0 +1,1520 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <gd.h>
+
+
+#define NGX_HTTP_IMAGE_OFF 0
+#define NGX_HTTP_IMAGE_TEST 1
+#define NGX_HTTP_IMAGE_SIZE 2
+#define NGX_HTTP_IMAGE_RESIZE 3
+#define NGX_HTTP_IMAGE_CROP 4
+#define NGX_HTTP_IMAGE_ROTATE 5
+
+
+#define NGX_HTTP_IMAGE_START 0
+#define NGX_HTTP_IMAGE_READ 1
+#define NGX_HTTP_IMAGE_PROCESS 2
+#define NGX_HTTP_IMAGE_PASS 3
+#define NGX_HTTP_IMAGE_DONE 4
+
+
+#define NGX_HTTP_IMAGE_NONE 0
+#define NGX_HTTP_IMAGE_JPEG 1
+#define NGX_HTTP_IMAGE_GIF 2
+#define NGX_HTTP_IMAGE_PNG 3
+
+
+#define NGX_HTTP_IMAGE_BUFFERED 0x08
+
+
+typedef struct {
+ ngx_uint_t filter;
+ ngx_uint_t width;
+ ngx_uint_t height;
+ ngx_uint_t angle;
+ ngx_uint_t jpeg_quality;
+ ngx_uint_t sharpen;
+
+ ngx_flag_t transparency;
+ ngx_flag_t interlace;
+
+ ngx_http_complex_value_t *wcv;
+ ngx_http_complex_value_t *hcv;
+ ngx_http_complex_value_t *acv;
+ ngx_http_complex_value_t *jqcv;
+ ngx_http_complex_value_t *shcv;
+
+ size_t buffer_size;
+} ngx_http_image_filter_conf_t;
+
+
+typedef struct {
+ u_char *image;
+ u_char *last;
+
+ size_t length;
+
+ ngx_uint_t width;
+ ngx_uint_t height;
+ ngx_uint_t max_width;
+ ngx_uint_t max_height;
+ ngx_uint_t angle;
+
+ ngx_uint_t phase;
+ ngx_uint_t type;
+ ngx_uint_t force;
+} ngx_http_image_filter_ctx_t;
+
+
+static ngx_int_t ngx_http_image_send(ngx_http_request_t *r,
+ ngx_http_image_filter_ctx_t *ctx, ngx_chain_t *in);
+static ngx_uint_t ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in);
+static ngx_int_t ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in);
+static ngx_buf_t *ngx_http_image_process(ngx_http_request_t *r);
+static ngx_buf_t *ngx_http_image_json(ngx_http_request_t *r,
+ ngx_http_image_filter_ctx_t *ctx);
+static ngx_buf_t *ngx_http_image_asis(ngx_http_request_t *r,
+ ngx_http_image_filter_ctx_t *ctx);
+static void ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b);
+static ngx_int_t ngx_http_image_size(ngx_http_request_t *r,
+ ngx_http_image_filter_ctx_t *ctx);
+
+static ngx_buf_t *ngx_http_image_resize(ngx_http_request_t *r,
+ ngx_http_image_filter_ctx_t *ctx);
+static gdImagePtr ngx_http_image_source(ngx_http_request_t *r,
+ ngx_http_image_filter_ctx_t *ctx);
+static gdImagePtr ngx_http_image_new(ngx_http_request_t *r, int w, int h,
+ int colors);
+static u_char *ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type,
+ gdImagePtr img, int *size);
+static void ngx_http_image_cleanup(void *data);
+static ngx_uint_t ngx_http_image_filter_get_value(ngx_http_request_t *r,
+ ngx_http_complex_value_t *cv, ngx_uint_t v);
+static ngx_uint_t ngx_http_image_filter_value(ngx_str_t *value);
+
+
+static void *ngx_http_image_filter_create_conf(ngx_conf_t *cf);
+static char *ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf,
+ ngx_command_t *cmd, void *conf);
+static char *ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf);
+
+
+static ngx_command_t ngx_http_image_filter_commands[] = {
+
+ { ngx_string("image_filter"),
+ NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
+ ngx_http_image_filter,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("image_filter_jpeg_quality"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_image_filter_jpeg_quality,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("image_filter_sharpen"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_image_filter_sharpen,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("image_filter_transparency"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_image_filter_conf_t, transparency),
+ NULL },
+
+ { ngx_string("image_filter_interlace"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_image_filter_conf_t, interlace),
+ NULL },
+
+ { ngx_string("image_filter_buffer"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_image_filter_conf_t, buffer_size),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_image_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_image_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_image_filter_create_conf, /* create location configuration */
+ ngx_http_image_filter_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_image_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_image_filter_module_ctx, /* module context */
+ ngx_http_image_filter_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_str_t ngx_http_image_types[] = {
+ ngx_string("image/jpeg"),
+ ngx_string("image/gif"),
+ ngx_string("image/png")
+};
+
+
+static ngx_int_t
+ngx_http_image_header_filter(ngx_http_request_t *r)
+{
+ off_t len;
+ ngx_http_image_filter_ctx_t *ctx;
+ ngx_http_image_filter_conf_t *conf;
+
+ if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
+
+ if (ctx) {
+ ngx_http_set_ctx(r, NULL, ngx_http_image_filter_module);
+ return ngx_http_next_header_filter(r);
+ }
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
+
+ if (conf->filter == NGX_HTTP_IMAGE_OFF) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ if (r->headers_out.content_type.len
+ >= sizeof("multipart/x-mixed-replace") - 1
+ && ngx_strncasecmp(r->headers_out.content_type.data,
+ (u_char *) "multipart/x-mixed-replace",
+ sizeof("multipart/x-mixed-replace") - 1)
+ == 0)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "image filter: multipart/x-mixed-replace response");
+
+ return NGX_ERROR;
+ }
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_image_filter_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_image_filter_module);
+
+ len = r->headers_out.content_length_n;
+
+ if (len != -1 && len > (off_t) conf->buffer_size) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "image filter: too big response: %O", len);
+
+ return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
+ }
+
+ if (len == -1) {
+ ctx->length = conf->buffer_size;
+
+ } else {
+ ctx->length = (size_t) len;
+ }
+
+ if (r->headers_out.refresh) {
+ r->headers_out.refresh->hash = 0;
+ }
+
+ r->main_filter_need_in_memory = 1;
+ r->allow_ranges = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ ngx_int_t rc;
+ ngx_str_t *ct;
+ ngx_chain_t out;
+ ngx_http_image_filter_ctx_t *ctx;
+ ngx_http_image_filter_conf_t *conf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "image filter");
+
+ if (in == NULL) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
+
+ if (ctx == NULL) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ switch (ctx->phase) {
+
+ case NGX_HTTP_IMAGE_START:
+
+ ctx->type = ngx_http_image_test(r, in);
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
+
+ if (ctx->type == NGX_HTTP_IMAGE_NONE) {
+
+ if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
+ out.buf = ngx_http_image_json(r, NULL);
+
+ if (out.buf) {
+ out.next = NULL;
+ ctx->phase = NGX_HTTP_IMAGE_DONE;
+
+ return ngx_http_image_send(r, ctx, &out);
+ }
+ }
+
+ return ngx_http_filter_finalize_request(r,
+ &ngx_http_image_filter_module,
+ NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
+ }
+
+ /* override content type */
+
+ ct = &ngx_http_image_types[ctx->type - 1];
+ r->headers_out.content_type_len = ct->len;
+ r->headers_out.content_type = *ct;
+ r->headers_out.content_type_lowcase = NULL;
+
+ if (conf->filter == NGX_HTTP_IMAGE_TEST) {
+ ctx->phase = NGX_HTTP_IMAGE_PASS;
+
+ return ngx_http_image_send(r, ctx, in);
+ }
+
+ ctx->phase = NGX_HTTP_IMAGE_READ;
+
+ /* fall through */
+
+ case NGX_HTTP_IMAGE_READ:
+
+ rc = ngx_http_image_read(r, in);
+
+ if (rc == NGX_AGAIN) {
+ return NGX_OK;
+ }
+
+ if (rc == NGX_ERROR) {
+ return ngx_http_filter_finalize_request(r,
+ &ngx_http_image_filter_module,
+ NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
+ }
+
+ /* fall through */
+
+ case NGX_HTTP_IMAGE_PROCESS:
+
+ out.buf = ngx_http_image_process(r);
+
+ if (out.buf == NULL) {
+ return ngx_http_filter_finalize_request(r,
+ &ngx_http_image_filter_module,
+ NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
+ }
+
+ out.next = NULL;
+ ctx->phase = NGX_HTTP_IMAGE_PASS;
+
+ return ngx_http_image_send(r, ctx, &out);
+
+ case NGX_HTTP_IMAGE_PASS:
+
+ return ngx_http_next_body_filter(r, in);
+
+ default: /* NGX_HTTP_IMAGE_DONE */
+
+ rc = ngx_http_next_body_filter(r, NULL);
+
+ /* NGX_ERROR resets any pending data */
+ return (rc == NGX_OK) ? NGX_ERROR : rc;
+ }
+}
+
+
+static ngx_int_t
+ngx_http_image_send(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx,
+ ngx_chain_t *in)
+{
+ ngx_int_t rc;
+
+ rc = ngx_http_next_header_filter(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ return NGX_ERROR;
+ }
+
+ rc = ngx_http_next_body_filter(r, in);
+
+ if (ctx->phase == NGX_HTTP_IMAGE_DONE) {
+ /* NGX_ERROR resets any pending data */
+ return (rc == NGX_OK) ? NGX_ERROR : rc;
+ }
+
+ return rc;
+}
+
+
+static ngx_uint_t
+ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ u_char *p;
+
+ p = in->buf->pos;
+
+ if (in->buf->last - p < 16) {
+ return NGX_HTTP_IMAGE_NONE;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "image filter: \"%c%c\"", p[0], p[1]);
+
+ if (p[0] == 0xff && p[1] == 0xd8) {
+
+ /* JPEG */
+
+ return NGX_HTTP_IMAGE_JPEG;
+
+ } else if (p[0] == 'G' && p[1] == 'I' && p[2] == 'F' && p[3] == '8'
+ && p[5] == 'a')
+ {
+ if (p[4] == '9' || p[4] == '7') {
+ /* GIF */
+ return NGX_HTTP_IMAGE_GIF;
+ }
+
+ } else if (p[0] == 0x89 && p[1] == 'P' && p[2] == 'N' && p[3] == 'G'
+ && p[4] == 0x0d && p[5] == 0x0a && p[6] == 0x1a && p[7] == 0x0a)
+ {
+ /* PNG */
+
+ return NGX_HTTP_IMAGE_PNG;
+ }
+
+ return NGX_HTTP_IMAGE_NONE;
+}
+
+
+static ngx_int_t
+ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ u_char *p;
+ size_t size, rest;
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+ ngx_http_image_filter_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
+
+ if (ctx->image == NULL) {
+ ctx->image = ngx_palloc(r->pool, ctx->length);
+ if (ctx->image == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->last = ctx->image;
+ }
+
+ p = ctx->last;
+
+ for (cl = in; cl; cl = cl->next) {
+
+ b = cl->buf;
+ size = b->last - b->pos;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "image buf: %uz", size);
+
+ rest = ctx->image + ctx->length - p;
+
+ if (size > rest) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "image filter: too big response");
+ return NGX_ERROR;
+ }
+
+ p = ngx_cpymem(p, b->pos, size);
+ b->pos += size;
+
+ if (b->last_buf) {
+ ctx->last = p;
+ return NGX_OK;
+ }
+ }
+
+ ctx->last = p;
+ r->connection->buffered |= NGX_HTTP_IMAGE_BUFFERED;
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_buf_t *
+ngx_http_image_process(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_http_image_filter_ctx_t *ctx;
+ ngx_http_image_filter_conf_t *conf;
+
+ r->connection->buffered &= ~NGX_HTTP_IMAGE_BUFFERED;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
+
+ rc = ngx_http_image_size(r, ctx);
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
+
+ if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
+ return ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL);
+ }
+
+ ctx->angle = ngx_http_image_filter_get_value(r, conf->acv, conf->angle);
+
+ if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {
+
+ if (ctx->angle != 90 && ctx->angle != 180 && ctx->angle != 270) {
+ return NULL;
+ }
+
+ return ngx_http_image_resize(r, ctx);
+ }
+
+ ctx->max_width = ngx_http_image_filter_get_value(r, conf->wcv, conf->width);
+ if (ctx->max_width == 0) {
+ return NULL;
+ }
+
+ ctx->max_height = ngx_http_image_filter_get_value(r, conf->hcv,
+ conf->height);
+ if (ctx->max_height == 0) {
+ return NULL;
+ }
+
+ if (rc == NGX_OK
+ && ctx->width <= ctx->max_width
+ && ctx->height <= ctx->max_height
+ && ctx->angle == 0
+ && !ctx->force)
+ {
+ return ngx_http_image_asis(r, ctx);
+ }
+
+ return ngx_http_image_resize(r, ctx);
+}
+
+
+static ngx_buf_t *
+ngx_http_image_json(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+ size_t len;
+ ngx_buf_t *b;
+
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ return NULL;
+ }
+
+ b->memory = 1;
+ b->last_buf = 1;
+
+ ngx_http_clean_header(r);
+
+ r->headers_out.status = NGX_HTTP_OK;
+ r->headers_out.content_type_len = sizeof("application/json") - 1;
+ ngx_str_set(&r->headers_out.content_type, "application/json");
+ r->headers_out.content_type_lowcase = NULL;
+
+ if (ctx == NULL) {
+ b->pos = (u_char *) "{}" CRLF;
+ b->last = b->pos + sizeof("{}" CRLF) - 1;
+
+ ngx_http_image_length(r, b);
+
+ return b;
+ }
+
+ len = sizeof("{ \"img\" : "
+ "{ \"width\": , \"height\": , \"type\": \"jpeg\" } }" CRLF) - 1
+ + 2 * NGX_SIZE_T_LEN;
+
+ b->pos = ngx_pnalloc(r->pool, len);
+ if (b->pos == NULL) {
+ return NULL;
+ }
+
+ b->last = ngx_sprintf(b->pos,
+ "{ \"img\" : "
+ "{ \"width\": %uz,"
+ " \"height\": %uz,"
+ " \"type\": \"%s\" } }" CRLF,
+ ctx->width, ctx->height,
+ ngx_http_image_types[ctx->type - 1].data + 6);
+
+ ngx_http_image_length(r, b);
+
+ return b;
+}
+
+
+static ngx_buf_t *
+ngx_http_image_asis(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+ ngx_buf_t *b;
+
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ return NULL;
+ }
+
+ b->pos = ctx->image;
+ b->last = ctx->last;
+ b->memory = 1;
+ b->last_buf = 1;
+
+ ngx_http_image_length(r, b);
+
+ return b;
+}
+
+
+static void
+ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b)
+{
+ r->headers_out.content_length_n = b->last - b->pos;
+
+ if (r->headers_out.content_length) {
+ r->headers_out.content_length->hash = 0;
+ }
+
+ r->headers_out.content_length = NULL;
+}
+
+
+static ngx_int_t
+ngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+ u_char *p, *last;
+ size_t len, app;
+ ngx_uint_t width, height;
+
+ p = ctx->image;
+
+ switch (ctx->type) {
+
+ case NGX_HTTP_IMAGE_JPEG:
+
+ p += 2;
+ last = ctx->image + ctx->length - 10;
+ width = 0;
+ height = 0;
+ app = 0;
+
+ while (p < last) {
+
+ if (p[0] == 0xff && p[1] != 0xff) {
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "JPEG: %02xd %02xd", p[0], p[1]);
+
+ p++;
+
+ if ((*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3
+ || *p == 0xc9 || *p == 0xca || *p == 0xcb)
+ && (width == 0 || height == 0))
+ {
+ width = p[6] * 256 + p[7];
+ height = p[4] * 256 + p[5];
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "JPEG: %02xd %02xd", p[1], p[2]);
+
+ len = p[1] * 256 + p[2];
+
+ if (*p >= 0xe1 && *p <= 0xef) {
+ /* application data, e.g., EXIF, Adobe XMP, etc. */
+ app += len;
+ }
+
+ p += len;
+
+ continue;
+ }
+
+ p++;
+ }
+
+ if (width == 0 || height == 0) {
+ return NGX_DECLINED;
+ }
+
+ if (ctx->length / 20 < app) {
+ /* force conversion if application data consume more than 5% */
+ ctx->force = 1;
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "app data size: %uz", app);
+ }
+
+ break;
+
+ case NGX_HTTP_IMAGE_GIF:
+
+ if (ctx->length < 10) {
+ return NGX_DECLINED;
+ }
+
+ width = p[7] * 256 + p[6];
+ height = p[9] * 256 + p[8];
+
+ break;
+
+ case NGX_HTTP_IMAGE_PNG:
+
+ if (ctx->length < 24) {
+ return NGX_DECLINED;
+ }
+
+ width = p[18] * 256 + p[19];
+ height = p[22] * 256 + p[23];
+
+ break;
+
+ default:
+
+ return NGX_DECLINED;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "image size: %d x %d", width, height);
+
+ ctx->width = width;
+ ctx->height = height;
+
+ return NGX_OK;
+}
+
+
+static ngx_buf_t *
+ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+ int sx, sy, dx, dy, ox, oy, ax, ay, size,
+ colors, palette, transparent, sharpen,
+ red, green, blue, t;
+ u_char *out;
+ ngx_buf_t *b;
+ ngx_uint_t resize;
+ gdImagePtr src, dst;
+ ngx_pool_cleanup_t *cln;
+ ngx_http_image_filter_conf_t *conf;
+
+ src = ngx_http_image_source(r, ctx);
+
+ if (src == NULL) {
+ return NULL;
+ }
+
+ sx = gdImageSX(src);
+ sy = gdImageSY(src);
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
+
+ if (!ctx->force
+ && ctx->angle == 0
+ && (ngx_uint_t) sx <= ctx->max_width
+ && (ngx_uint_t) sy <= ctx->max_height)
+ {
+ gdImageDestroy(src);
+ return ngx_http_image_asis(r, ctx);
+ }
+
+ colors = gdImageColorsTotal(src);
+
+ if (colors && conf->transparency) {
+ transparent = gdImageGetTransparent(src);
+
+ if (transparent != -1) {
+ palette = colors;
+ red = gdImageRed(src, transparent);
+ green = gdImageGreen(src, transparent);
+ blue = gdImageBlue(src, transparent);
+
+ goto transparent;
+ }
+ }
+
+ palette = 0;
+ transparent = -1;
+ red = 0;
+ green = 0;
+ blue = 0;
+
+transparent:
+
+ gdImageColorTransparent(src, -1);
+
+ dx = sx;
+ dy = sy;
+
+ if (conf->filter == NGX_HTTP_IMAGE_RESIZE) {
+
+ if ((ngx_uint_t) dx > ctx->max_width) {
+ dy = dy * ctx->max_width / dx;
+ dy = dy ? dy : 1;
+ dx = ctx->max_width;
+ }
+
+ if ((ngx_uint_t) dy > ctx->max_height) {
+ dx = dx * ctx->max_height / dy;
+ dx = dx ? dx : 1;
+ dy = ctx->max_height;
+ }
+
+ resize = 1;
+
+ } else if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {
+
+ resize = 0;
+
+ } else { /* NGX_HTTP_IMAGE_CROP */
+
+ resize = 0;
+
+ if ((double) dx / dy < (double) ctx->max_width / ctx->max_height) {
+ if ((ngx_uint_t) dx > ctx->max_width) {
+ dy = dy * ctx->max_width / dx;
+ dy = dy ? dy : 1;
+ dx = ctx->max_width;
+ resize = 1;
+ }
+
+ } else {
+ if ((ngx_uint_t) dy > ctx->max_height) {
+ dx = dx * ctx->max_height / dy;
+ dx = dx ? dx : 1;
+ dy = ctx->max_height;
+ resize = 1;
+ }
+ }
+ }
+
+ if (resize) {
+ dst = ngx_http_image_new(r, dx, dy, palette);
+ if (dst == NULL) {
+ gdImageDestroy(src);
+ return NULL;
+ }
+
+ if (colors == 0) {
+ gdImageSaveAlpha(dst, 1);
+ gdImageAlphaBlending(dst, 0);
+ }
+
+ gdImageCopyResampled(dst, src, 0, 0, 0, 0, dx, dy, sx, sy);
+
+ if (colors) {
+ gdImageTrueColorToPalette(dst, 1, 256);
+ }
+
+ gdImageDestroy(src);
+
+ } else {
+ dst = src;
+ }
+
+ if (ctx->angle) {
+ src = dst;
+
+ ax = (dx % 2 == 0) ? 1 : 0;
+ ay = (dy % 2 == 0) ? 1 : 0;
+
+ switch (ctx->angle) {
+
+ case 90:
+ case 270:
+ dst = ngx_http_image_new(r, dy, dx, palette);
+ if (dst == NULL) {
+ gdImageDestroy(src);
+ return NULL;
+ }
+ if (ctx->angle == 90) {
+ ox = dy / 2 + ay;
+ oy = dx / 2 - ax;
+
+ } else {
+ ox = dy / 2 - ay;
+ oy = dx / 2 + ax;
+ }
+
+ gdImageCopyRotated(dst, src, ox, oy, 0, 0,
+ dx + ax, dy + ay, ctx->angle);
+ gdImageDestroy(src);
+
+ t = dx;
+ dx = dy;
+ dy = t;
+ break;
+
+ case 180:
+ dst = ngx_http_image_new(r, dx, dy, palette);
+ if (dst == NULL) {
+ gdImageDestroy(src);
+ return NULL;
+ }
+ gdImageCopyRotated(dst, src, dx / 2 - ax, dy / 2 - ay, 0, 0,
+ dx + ax, dy + ay, ctx->angle);
+ gdImageDestroy(src);
+ break;
+ }
+ }
+
+ if (conf->filter == NGX_HTTP_IMAGE_CROP) {
+
+ src = dst;
+
+ if ((ngx_uint_t) dx > ctx->max_width) {
+ ox = dx - ctx->max_width;
+
+ } else {
+ ox = 0;
+ }
+
+ if ((ngx_uint_t) dy > ctx->max_height) {
+ oy = dy - ctx->max_height;
+
+ } else {
+ oy = 0;
+ }
+
+ if (ox || oy) {
+
+ dst = ngx_http_image_new(r, dx - ox, dy - oy, colors);
+
+ if (dst == NULL) {
+ gdImageDestroy(src);
+ return NULL;
+ }
+
+ ox /= 2;
+ oy /= 2;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "image crop: %d x %d @ %d x %d",
+ dx, dy, ox, oy);
+
+ if (colors == 0) {
+ gdImageSaveAlpha(dst, 1);
+ gdImageAlphaBlending(dst, 0);
+ }
+
+ gdImageCopy(dst, src, 0, 0, ox, oy, dx - ox, dy - oy);
+
+ if (colors) {
+ gdImageTrueColorToPalette(dst, 1, 256);
+ }
+
+ gdImageDestroy(src);
+ }
+ }
+
+ if (transparent != -1 && colors) {
+ gdImageColorTransparent(dst, gdImageColorExact(dst, red, green, blue));
+ }
+
+ sharpen = ngx_http_image_filter_get_value(r, conf->shcv, conf->sharpen);
+ if (sharpen > 0) {
+ gdImageSharpen(dst, sharpen);
+ }
+
+ gdImageInterlace(dst, (int) conf->interlace);
+
+ out = ngx_http_image_out(r, ctx->type, dst, &size);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "image: %d x %d %d", sx, sy, colors);
+
+ gdImageDestroy(dst);
+ ngx_pfree(r->pool, ctx->image);
+
+ if (out == NULL) {
+ return NULL;
+ }
+
+ cln = ngx_pool_cleanup_add(r->pool, 0);
+ if (cln == NULL) {
+ gdFree(out);
+ return NULL;
+ }
+
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ gdFree(out);
+ return NULL;
+ }
+
+ cln->handler = ngx_http_image_cleanup;
+ cln->data = out;
+
+ b->pos = out;
+ b->last = out + size;
+ b->memory = 1;
+ b->last_buf = 1;
+
+ ngx_http_image_length(r, b);
+
+ return b;
+}
+
+
+static gdImagePtr
+ngx_http_image_source(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+ char *failed;
+ gdImagePtr img;
+
+ img = NULL;
+
+ switch (ctx->type) {
+
+ case NGX_HTTP_IMAGE_JPEG:
+ img = gdImageCreateFromJpegPtr(ctx->length, ctx->image);
+ failed = "gdImageCreateFromJpegPtr() failed";
+ break;
+
+ case NGX_HTTP_IMAGE_GIF:
+ img = gdImageCreateFromGifPtr(ctx->length, ctx->image);
+ failed = "gdImageCreateFromGifPtr() failed";
+ break;
+
+ case NGX_HTTP_IMAGE_PNG:
+ img = gdImageCreateFromPngPtr(ctx->length, ctx->image);
+ failed = "gdImageCreateFromPngPtr() failed";
+ break;
+
+ default:
+ failed = "unknown image type";
+ break;
+ }
+
+ if (img == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);
+ }
+
+ return img;
+}
+
+
+static gdImagePtr
+ngx_http_image_new(ngx_http_request_t *r, int w, int h, int colors)
+{
+ gdImagePtr img;
+
+ if (colors == 0) {
+ img = gdImageCreateTrueColor(w, h);
+
+ if (img == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "gdImageCreateTrueColor() failed");
+ return NULL;
+ }
+
+ } else {
+ img = gdImageCreate(w, h);
+
+ if (img == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "gdImageCreate() failed");
+ return NULL;
+ }
+ }
+
+ return img;
+}
+
+
+static u_char *
+ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img,
+ int *size)
+{
+ char *failed;
+ u_char *out;
+ ngx_int_t jq;
+ ngx_http_image_filter_conf_t *conf;
+
+ out = NULL;
+
+ switch (type) {
+
+ case NGX_HTTP_IMAGE_JPEG:
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
+
+ jq = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality);
+ if (jq <= 0) {
+ return NULL;
+ }
+
+ out = gdImageJpegPtr(img, size, jq);
+ failed = "gdImageJpegPtr() failed";
+ break;
+
+ case NGX_HTTP_IMAGE_GIF:
+ out = gdImageGifPtr(img, size);
+ failed = "gdImageGifPtr() failed";
+ break;
+
+ case NGX_HTTP_IMAGE_PNG:
+ out = gdImagePngPtr(img, size);
+ failed = "gdImagePngPtr() failed";
+ break;
+
+ default:
+ failed = "unknown image type";
+ break;
+ }
+
+ if (out == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);
+ }
+
+ return out;
+}
+
+
+static void
+ngx_http_image_cleanup(void *data)
+{
+ gdFree(data);
+}
+
+
+static ngx_uint_t
+ngx_http_image_filter_get_value(ngx_http_request_t *r,
+ ngx_http_complex_value_t *cv, ngx_uint_t v)
+{
+ ngx_str_t val;
+
+ if (cv == NULL) {
+ return v;
+ }
+
+ if (ngx_http_complex_value(r, cv, &val) != NGX_OK) {
+ return 0;
+ }
+
+ return ngx_http_image_filter_value(&val);
+}
+
+
+static ngx_uint_t
+ngx_http_image_filter_value(ngx_str_t *value)
+{
+ ngx_int_t n;
+
+ if (value->len == 1 && value->data[0] == '-') {
+ return (ngx_uint_t) -1;
+ }
+
+ n = ngx_atoi(value->data, value->len);
+
+ if (n > 0) {
+ return (ngx_uint_t) n;
+ }
+
+ return 0;
+}
+
+
+static void *
+ngx_http_image_filter_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_image_filter_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_image_filter_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->width = 0;
+ * conf->height = 0;
+ * conf->angle = 0;
+ * conf->wcv = NULL;
+ * conf->hcv = NULL;
+ * conf->acv = NULL;
+ * conf->jqcv = NULL;
+ * conf->shcv = NULL;
+ */
+
+ conf->filter = NGX_CONF_UNSET_UINT;
+ conf->jpeg_quality = NGX_CONF_UNSET_UINT;
+ conf->sharpen = NGX_CONF_UNSET_UINT;
+ conf->transparency = NGX_CONF_UNSET;
+ conf->interlace = NGX_CONF_UNSET;
+ conf->buffer_size = NGX_CONF_UNSET_SIZE;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_image_filter_conf_t *prev = parent;
+ ngx_http_image_filter_conf_t *conf = child;
+
+ if (conf->filter == NGX_CONF_UNSET_UINT) {
+
+ if (prev->filter == NGX_CONF_UNSET_UINT) {
+ conf->filter = NGX_HTTP_IMAGE_OFF;
+
+ } else {
+ conf->filter = prev->filter;
+ conf->width = prev->width;
+ conf->height = prev->height;
+ conf->angle = prev->angle;
+ conf->wcv = prev->wcv;
+ conf->hcv = prev->hcv;
+ conf->acv = prev->acv;
+ }
+ }
+
+ if (conf->jpeg_quality == NGX_CONF_UNSET_UINT) {
+
+ /* 75 is libjpeg default quality */
+ ngx_conf_merge_uint_value(conf->jpeg_quality, prev->jpeg_quality, 75);
+
+ if (conf->jqcv == NULL) {
+ conf->jqcv = prev->jqcv;
+ }
+ }
+
+ if (conf->sharpen == NGX_CONF_UNSET_UINT) {
+ ngx_conf_merge_uint_value(conf->sharpen, prev->sharpen, 0);
+
+ if (conf->shcv == NULL) {
+ conf->shcv = prev->shcv;
+ }
+ }
+
+ ngx_conf_merge_value(conf->transparency, prev->transparency, 1);
+
+ ngx_conf_merge_value(conf->interlace, prev->interlace, 0);
+
+ ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
+ 1 * 1024 * 1024);
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_image_filter_conf_t *imcf = conf;
+
+ ngx_str_t *value;
+ ngx_int_t n;
+ ngx_uint_t i;
+ ngx_http_complex_value_t cv;
+ ngx_http_compile_complex_value_t ccv;
+
+ value = cf->args->elts;
+
+ i = 1;
+
+ if (cf->args->nelts == 2) {
+ if (ngx_strcmp(value[i].data, "off") == 0) {
+ imcf->filter = NGX_HTTP_IMAGE_OFF;
+
+ } else if (ngx_strcmp(value[i].data, "test") == 0) {
+ imcf->filter = NGX_HTTP_IMAGE_TEST;
+
+ } else if (ngx_strcmp(value[i].data, "size") == 0) {
+ imcf->filter = NGX_HTTP_IMAGE_SIZE;
+
+ } else {
+ goto failed;
+ }
+
+ return NGX_CONF_OK;
+
+ } else if (cf->args->nelts == 3) {
+
+ if (ngx_strcmp(value[i].data, "rotate") == 0) {
+ if (imcf->filter != NGX_HTTP_IMAGE_RESIZE
+ && imcf->filter != NGX_HTTP_IMAGE_CROP)
+ {
+ imcf->filter = NGX_HTTP_IMAGE_ROTATE;
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[++i];
+ ccv.complex_value = &cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (cv.lengths == NULL) {
+ n = ngx_http_image_filter_value(&value[i]);
+
+ if (n != 90 && n != 180 && n != 270) {
+ goto failed;
+ }
+
+ imcf->angle = (ngx_uint_t) n;
+
+ } else {
+ imcf->acv = ngx_palloc(cf->pool,
+ sizeof(ngx_http_complex_value_t));
+ if (imcf->acv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *imcf->acv = cv;
+ }
+
+ return NGX_CONF_OK;
+
+ } else {
+ goto failed;
+ }
+ }
+
+ if (ngx_strcmp(value[i].data, "resize") == 0) {
+ imcf->filter = NGX_HTTP_IMAGE_RESIZE;
+
+ } else if (ngx_strcmp(value[i].data, "crop") == 0) {
+ imcf->filter = NGX_HTTP_IMAGE_CROP;
+
+ } else {
+ goto failed;
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[++i];
+ ccv.complex_value = &cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (cv.lengths == NULL) {
+ n = ngx_http_image_filter_value(&value[i]);
+
+ if (n == 0) {
+ goto failed;
+ }
+
+ imcf->width = (ngx_uint_t) n;
+
+ } else {
+ imcf->wcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+ if (imcf->wcv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *imcf->wcv = cv;
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[++i];
+ ccv.complex_value = &cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (cv.lengths == NULL) {
+ n = ngx_http_image_filter_value(&value[i]);
+
+ if (n == 0) {
+ goto failed;
+ }
+
+ imcf->height = (ngx_uint_t) n;
+
+ } else {
+ imcf->hcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+ if (imcf->hcv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *imcf->hcv = cv;
+ }
+
+ return NGX_CONF_OK;
+
+failed:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
+ &value[i]);
+
+ return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ ngx_http_image_filter_conf_t *imcf = conf;
+
+ ngx_str_t *value;
+ ngx_int_t n;
+ ngx_http_complex_value_t cv;
+ ngx_http_compile_complex_value_t ccv;
+
+ value = cf->args->elts;
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (cv.lengths == NULL) {
+ n = ngx_http_image_filter_value(&value[1]);
+
+ if (n <= 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ imcf->jpeg_quality = (ngx_uint_t) n;
+
+ } else {
+ imcf->jqcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+ if (imcf->jqcv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *imcf->jqcv = cv;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ ngx_http_image_filter_conf_t *imcf = conf;
+
+ ngx_str_t *value;
+ ngx_int_t n;
+ ngx_http_complex_value_t cv;
+ ngx_http_compile_complex_value_t ccv;
+
+ value = cf->args->elts;
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (cv.lengths == NULL) {
+ n = ngx_http_image_filter_value(&value[1]);
+
+ if (n < 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ imcf->sharpen = (ngx_uint_t) n;
+
+ } else {
+ imcf->shcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+ if (imcf->shcv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *imcf->shcv = cv;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_image_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_image_header_filter;
+
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_image_body_filter;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_index_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_index_module.c
new file mode 100644
index 00000000000..d3544db5bf5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_index_module.c
@@ -0,0 +1,540 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_array_t *lengths;
+ ngx_array_t *values;
+} ngx_http_index_t;
+
+
+typedef struct {
+ ngx_array_t *indices; /* array of ngx_http_index_t */
+ size_t max_index_len;
+} ngx_http_index_loc_conf_t;
+
+
+#define NGX_HTTP_DEFAULT_INDEX "index.html"
+
+
+static ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r,
+ ngx_http_core_loc_conf_t *clcf, u_char *path, u_char *last);
+static ngx_int_t ngx_http_index_error(ngx_http_request_t *r,
+ ngx_http_core_loc_conf_t *clcf, u_char *file, ngx_err_t err);
+
+static ngx_int_t ngx_http_index_init(ngx_conf_t *cf);
+static void *ngx_http_index_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_index_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_http_index_commands[] = {
+
+ { ngx_string("index"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_index_set_index,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_index_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_index_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_index_create_loc_conf, /* create location configuration */
+ ngx_http_index_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_index_module = {
+ NGX_MODULE_V1,
+ &ngx_http_index_module_ctx, /* module context */
+ ngx_http_index_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+/*
+ * Try to open/test the first index file before the test of directory
+ * existence because valid requests should prevail over invalid ones.
+ * If open()/stat() of a file will fail then stat() of a directory
+ * should be faster because kernel may have already cached some data.
+ * Besides, Win32 may return ERROR_PATH_NOT_FOUND (NGX_ENOTDIR) at once.
+ * Unix has ENOTDIR error; however, it's less helpful than Win32's one:
+ * it only indicates that path points to a regular file, not a directory.
+ */
+
+static ngx_int_t
+ngx_http_index_handler(ngx_http_request_t *r)
+{
+ u_char *p, *name;
+ size_t len, root, reserve, allocated;
+ ngx_int_t rc;
+ ngx_str_t path, uri;
+ ngx_uint_t i, dir_tested;
+ ngx_http_index_t *index;
+ ngx_open_file_info_t of;
+ ngx_http_script_code_pt code;
+ ngx_http_script_engine_t e;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_index_loc_conf_t *ilcf;
+ ngx_http_script_len_code_pt lcode;
+
+ if (r->uri.data[r->uri.len - 1] != '/') {
+ return NGX_DECLINED;
+ }
+
+ if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
+ return NGX_DECLINED;
+ }
+
+ ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ allocated = 0;
+ root = 0;
+ dir_tested = 0;
+ name = NULL;
+ /* suppress MSVC warning */
+ path.data = NULL;
+
+ index = ilcf->indices->elts;
+ for (i = 0; i < ilcf->indices->nelts; i++) {
+
+ if (index[i].lengths == NULL) {
+
+ if (index[i].name.data[0] == '/') {
+ return ngx_http_internal_redirect(r, &index[i].name, &r->args);
+ }
+
+ reserve = ilcf->max_index_len;
+ len = index[i].name.len;
+
+ } else {
+ ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
+
+ e.ip = index[i].lengths->elts;
+ e.request = r;
+ e.flushed = 1;
+
+ /* 1 is for terminating '\0' as in static names */
+ len = 1;
+
+ while (*(uintptr_t *) e.ip) {
+ lcode = *(ngx_http_script_len_code_pt *) e.ip;
+ len += lcode(&e);
+ }
+
+ /* 16 bytes are preallocation */
+
+ reserve = len + 16;
+ }
+
+ if (reserve > allocated) {
+
+ name = ngx_http_map_uri_to_path(r, &path, &root, reserve);
+ if (name == NULL) {
+ return NGX_ERROR;
+ }
+
+ allocated = path.data + path.len - name;
+ }
+
+ if (index[i].values == NULL) {
+
+ /* index[i].name.len includes the terminating '\0' */
+
+ ngx_memcpy(name, index[i].name.data, index[i].name.len);
+
+ path.len = (name + index[i].name.len - 1) - path.data;
+
+ } else {
+ e.ip = index[i].values->elts;
+ e.pos = name;
+
+ while (*(uintptr_t *) e.ip) {
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) &e);
+ }
+
+ if (*name == '/') {
+ uri.len = len - 1;
+ uri.data = name;
+ return ngx_http_internal_redirect(r, &uri, &r->args);
+ }
+
+ path.len = e.pos - path.data;
+
+ *e.pos = '\0';
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "open index \"%V\"", &path);
+
+ ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+ of.read_ahead = clcf->read_ahead;
+ of.directio = clcf->directio;
+ of.valid = clcf->open_file_cache_valid;
+ of.min_uses = clcf->open_file_cache_min_uses;
+ of.test_only = 1;
+ of.errors = clcf->open_file_cache_errors;
+ of.events = clcf->open_file_cache_events;
+
+ if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+ != NGX_OK)
+ {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,
+ "%s \"%s\" failed", of.failed, path.data);
+
+ if (of.err == 0) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+#if (NGX_HAVE_OPENAT)
+ if (of.err == NGX_EMLINK
+ || of.err == NGX_ELOOP)
+ {
+ return NGX_HTTP_FORBIDDEN;
+ }
+#endif
+
+ if (of.err == NGX_ENOTDIR
+ || of.err == NGX_ENAMETOOLONG
+ || of.err == NGX_EACCES)
+ {
+ return ngx_http_index_error(r, clcf, path.data, of.err);
+ }
+
+ if (!dir_tested) {
+ rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ dir_tested = 1;
+ }
+
+ if (of.err == NGX_ENOENT) {
+ continue;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
+ "%s \"%s\" failed", of.failed, path.data);
+
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ uri.len = r->uri.len + len - 1;
+
+ if (!clcf->alias) {
+ uri.data = path.data + root;
+
+ } else {
+ uri.data = ngx_pnalloc(r->pool, uri.len);
+ if (uri.data == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ p = ngx_copy(uri.data, r->uri.data, r->uri.len);
+ ngx_memcpy(p, name, len - 1);
+ }
+
+ return ngx_http_internal_redirect(r, &uri, &r->args);
+ }
+
+ return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,
+ u_char *path, u_char *last)
+{
+ u_char c;
+ ngx_str_t dir;
+ ngx_open_file_info_t of;
+
+ c = *last;
+ if (c != '/' || path == last) {
+ /* "alias" without trailing slash */
+ c = *(++last);
+ }
+ *last = '\0';
+
+ dir.len = last - path;
+ dir.data = path;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http index check dir: \"%V\"", &dir);
+
+ ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+ of.test_dir = 1;
+ of.test_only = 1;
+ of.valid = clcf->open_file_cache_valid;
+ of.errors = clcf->open_file_cache_errors;
+
+ if (ngx_http_set_disable_symlinks(r, clcf, &dir, &of) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool)
+ != NGX_OK)
+ {
+ if (of.err) {
+
+#if (NGX_HAVE_OPENAT)
+ if (of.err == NGX_EMLINK
+ || of.err == NGX_ELOOP)
+ {
+ return NGX_HTTP_FORBIDDEN;
+ }
+#endif
+
+ if (of.err == NGX_ENOENT) {
+ *last = c;
+ return ngx_http_index_error(r, clcf, dir.data, NGX_ENOENT);
+ }
+
+ if (of.err == NGX_EACCES) {
+
+ *last = c;
+
+ /*
+ * ngx_http_index_test_dir() is called after the first index
+ * file testing has returned an error distinct from NGX_EACCES.
+ * This means that directory searching is allowed.
+ */
+
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
+ "%s \"%s\" failed", of.failed, dir.data);
+ }
+
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ *last = c;
+
+ if (of.is_dir) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "\"%s\" is not a directory", dir.data);
+
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_index_error(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,
+ u_char *file, ngx_err_t err)
+{
+ if (err == NGX_EACCES) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
+ "\"%s\" is forbidden", file);
+
+ return NGX_HTTP_FORBIDDEN;
+ }
+
+ if (clcf->log_not_found) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, err,
+ "\"%s\" is not found", file);
+ }
+
+ return NGX_HTTP_NOT_FOUND;
+}
+
+
+static void *
+ngx_http_index_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_index_loc_conf_t *conf;
+
+ conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->indices = NULL;
+ conf->max_index_len = 0;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_index_loc_conf_t *prev = parent;
+ ngx_http_index_loc_conf_t *conf = child;
+
+ ngx_http_index_t *index;
+
+ if (conf->indices == NULL) {
+ conf->indices = prev->indices;
+ conf->max_index_len = prev->max_index_len;
+ }
+
+ if (conf->indices == NULL) {
+ conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t));
+ if (conf->indices == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ index = ngx_array_push(conf->indices);
+ if (index == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX);
+ index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX;
+ index->lengths = NULL;
+ index->values = NULL;
+
+ conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX);
+
+ return NGX_CONF_OK;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_index_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_index_handler;
+
+ return NGX_OK;
+}
+
+
+/* TODO: warn about duplicate indices */
+
+static char *
+ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_index_loc_conf_t *ilcf = conf;
+
+ ngx_str_t *value;
+ ngx_uint_t i, n;
+ ngx_http_index_t *index;
+ ngx_http_script_compile_t sc;
+
+ if (ilcf->indices == NULL) {
+ ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t));
+ if (ilcf->indices == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (value[i].data[0] == '/' && i != cf->args->nelts - 1) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "only the last index in \"index\" directive "
+ "should be absolute");
+ }
+
+ if (value[i].len == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "index \"%V\" in \"index\" directive is invalid",
+ &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ index = ngx_array_push(ilcf->indices);
+ if (index == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ index->name.len = value[i].len;
+ index->name.data = value[i].data;
+ index->lengths = NULL;
+ index->values = NULL;
+
+ n = ngx_http_script_variables_count(&value[i]);
+
+ if (n == 0) {
+ if (ilcf->max_index_len < index->name.len) {
+ ilcf->max_index_len = index->name.len;
+ }
+
+ if (index->name.data[0] == '/') {
+ continue;
+ }
+
+ /* include the terminating '\0' to the length to use ngx_memcpy() */
+ index->name.len++;
+
+ continue;
+ }
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &value[i];
+ sc.lengths = &index->lengths;
+ sc.values = &index->values;
+ sc.variables = n;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_limit_conn_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_limit_conn_module.c
new file mode 100644
index 00000000000..7f0eea7ab00
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_limit_conn_module.c
@@ -0,0 +1,767 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ u_char color;
+ u_char len;
+ u_short conn;
+ u_char data[1];
+} ngx_http_limit_conn_node_t;
+
+
+typedef struct {
+ ngx_shm_zone_t *shm_zone;
+ ngx_rbtree_node_t *node;
+} ngx_http_limit_conn_cleanup_t;
+
+
+typedef struct {
+ ngx_rbtree_t *rbtree;
+ ngx_int_t index;
+ ngx_str_t var;
+} ngx_http_limit_conn_ctx_t;
+
+
+typedef struct {
+ ngx_shm_zone_t *shm_zone;
+ ngx_uint_t conn;
+} ngx_http_limit_conn_limit_t;
+
+
+typedef struct {
+ ngx_array_t limits;
+ ngx_uint_t log_level;
+ ngx_uint_t status_code;
+} ngx_http_limit_conn_conf_t;
+
+
+static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree,
+ ngx_http_variable_value_t *vv, uint32_t hash);
+static void ngx_http_limit_conn_cleanup(void *data);
+static ngx_inline void ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool);
+
+static void *ngx_http_limit_conn_create_conf(ngx_conf_t *cf);
+static char *ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf);
+
+
+static ngx_conf_deprecated_t ngx_conf_deprecated_limit_zone = {
+ ngx_conf_deprecated, "limit_zone", "limit_conn_zone"
+};
+
+
+static ngx_conf_enum_t ngx_http_limit_conn_log_levels[] = {
+ { ngx_string("info"), NGX_LOG_INFO },
+ { ngx_string("notice"), NGX_LOG_NOTICE },
+ { ngx_string("warn"), NGX_LOG_WARN },
+ { ngx_string("error"), NGX_LOG_ERR },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_conf_num_bounds_t ngx_http_limit_conn_status_bounds = {
+ ngx_conf_check_num_bounds, 400, 599
+};
+
+
+static ngx_command_t ngx_http_limit_conn_commands[] = {
+
+ { ngx_string("limit_conn_zone"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,
+ ngx_http_limit_conn_zone,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("limit_zone"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,
+ ngx_http_limit_zone,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("limit_conn"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_http_limit_conn,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("limit_conn_log_level"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_limit_conn_conf_t, log_level),
+ &ngx_http_limit_conn_log_levels },
+
+ { ngx_string("limit_conn_status"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_limit_conn_conf_t, status_code),
+ &ngx_http_limit_conn_status_bounds },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_limit_conn_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_limit_conn_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_limit_conn_create_conf, /* create location configuration */
+ ngx_http_limit_conn_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_limit_conn_module = {
+ NGX_MODULE_V1,
+ &ngx_http_limit_conn_module_ctx, /* module context */
+ ngx_http_limit_conn_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_limit_conn_handler(ngx_http_request_t *r)
+{
+ size_t len, n;
+ uint32_t hash;
+ ngx_uint_t i;
+ ngx_slab_pool_t *shpool;
+ ngx_rbtree_node_t *node;
+ ngx_pool_cleanup_t *cln;
+ ngx_http_variable_value_t *vv;
+ ngx_http_limit_conn_ctx_t *ctx;
+ ngx_http_limit_conn_node_t *lc;
+ ngx_http_limit_conn_conf_t *lccf;
+ ngx_http_limit_conn_limit_t *limits;
+ ngx_http_limit_conn_cleanup_t *lccln;
+
+ if (r->main->limit_conn_set) {
+ return NGX_DECLINED;
+ }
+
+ lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module);
+ limits = lccf->limits.elts;
+
+ for (i = 0; i < lccf->limits.nelts; i++) {
+ ctx = limits[i].shm_zone->data;
+
+ vv = ngx_http_get_indexed_variable(r, ctx->index);
+
+ if (vv == NULL || vv->not_found) {
+ continue;
+ }
+
+ len = vv->len;
+
+ if (len == 0) {
+ continue;
+ }
+
+ if (len > 255) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "the value of the \"%V\" variable "
+ "is more than 255 bytes: \"%v\"",
+ &ctx->var, vv);
+ continue;
+ }
+
+ r->main->limit_conn_set = 1;
+
+ hash = ngx_crc32_short(vv->data, len);
+
+ shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr;
+
+ ngx_shmtx_lock(&shpool->mutex);
+
+ node = ngx_http_limit_conn_lookup(ctx->rbtree, vv, hash);
+
+ if (node == NULL) {
+
+ n = offsetof(ngx_rbtree_node_t, color)
+ + offsetof(ngx_http_limit_conn_node_t, data)
+ + len;
+
+ node = ngx_slab_alloc_locked(shpool, n);
+
+ if (node == NULL) {
+ ngx_shmtx_unlock(&shpool->mutex);
+ ngx_http_limit_conn_cleanup_all(r->pool);
+ return lccf->status_code;
+ }
+
+ lc = (ngx_http_limit_conn_node_t *) &node->color;
+
+ node->key = hash;
+ lc->len = (u_char) len;
+ lc->conn = 1;
+ ngx_memcpy(lc->data, vv->data, len);
+
+ ngx_rbtree_insert(ctx->rbtree, node);
+
+ } else {
+
+ lc = (ngx_http_limit_conn_node_t *) &node->color;
+
+ if ((ngx_uint_t) lc->conn >= limits[i].conn) {
+
+ ngx_shmtx_unlock(&shpool->mutex);
+
+ ngx_log_error(lccf->log_level, r->connection->log, 0,
+ "limiting connections by zone \"%V\"",
+ &limits[i].shm_zone->shm.name);
+
+ ngx_http_limit_conn_cleanup_all(r->pool);
+ return lccf->status_code;
+ }
+
+ lc->conn++;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "limit conn: %08XD %d", node->key, lc->conn);
+
+ ngx_shmtx_unlock(&shpool->mutex);
+
+ cln = ngx_pool_cleanup_add(r->pool,
+ sizeof(ngx_http_limit_conn_cleanup_t));
+ if (cln == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ cln->handler = ngx_http_limit_conn_cleanup;
+ lccln = cln->data;
+
+ lccln->shm_zone = limits[i].shm_zone;
+ lccln->node = node;
+ }
+
+ return NGX_DECLINED;
+}
+
+
+static void
+ngx_http_limit_conn_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+ ngx_rbtree_node_t **p;
+ ngx_http_limit_conn_node_t *lcn, *lcnt;
+
+ for ( ;; ) {
+
+ if (node->key < temp->key) {
+
+ p = &temp->left;
+
+ } else if (node->key > temp->key) {
+
+ p = &temp->right;
+
+ } else { /* node->key == temp->key */
+
+ lcn = (ngx_http_limit_conn_node_t *) &node->color;
+ lcnt = (ngx_http_limit_conn_node_t *) &temp->color;
+
+ p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)
+ ? &temp->left : &temp->right;
+ }
+
+ if (*p == sentinel) {
+ break;
+ }
+
+ temp = *p;
+ }
+
+ *p = node;
+ node->parent = temp;
+ node->left = sentinel;
+ node->right = sentinel;
+ ngx_rbt_red(node);
+}
+
+
+static ngx_rbtree_node_t *
+ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_http_variable_value_t *vv,
+ uint32_t hash)
+{
+ ngx_int_t rc;
+ ngx_rbtree_node_t *node, *sentinel;
+ ngx_http_limit_conn_node_t *lcn;
+
+ node = rbtree->root;
+ sentinel = rbtree->sentinel;
+
+ while (node != sentinel) {
+
+ if (hash < node->key) {
+ node = node->left;
+ continue;
+ }
+
+ if (hash > node->key) {
+ node = node->right;
+ continue;
+ }
+
+ /* hash == node->key */
+
+ lcn = (ngx_http_limit_conn_node_t *) &node->color;
+
+ rc = ngx_memn2cmp(vv->data, lcn->data,
+ (size_t) vv->len, (size_t) lcn->len);
+ if (rc == 0) {
+ return node;
+ }
+
+ node = (rc < 0) ? node->left : node->right;
+ }
+
+ return NULL;
+}
+
+
+static void
+ngx_http_limit_conn_cleanup(void *data)
+{
+ ngx_http_limit_conn_cleanup_t *lccln = data;
+
+ ngx_slab_pool_t *shpool;
+ ngx_rbtree_node_t *node;
+ ngx_http_limit_conn_ctx_t *ctx;
+ ngx_http_limit_conn_node_t *lc;
+
+ ctx = lccln->shm_zone->data;
+ shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr;
+ node = lccln->node;
+ lc = (ngx_http_limit_conn_node_t *) &node->color;
+
+ ngx_shmtx_lock(&shpool->mutex);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0,
+ "limit conn cleanup: %08XD %d", node->key, lc->conn);
+
+ lc->conn--;
+
+ if (lc->conn == 0) {
+ ngx_rbtree_delete(ctx->rbtree, node);
+ ngx_slab_free_locked(shpool, node);
+ }
+
+ ngx_shmtx_unlock(&shpool->mutex);
+}
+
+
+static ngx_inline void
+ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool)
+{
+ ngx_pool_cleanup_t *cln;
+
+ cln = pool->cleanup;
+
+ while (cln && cln->handler == ngx_http_limit_conn_cleanup) {
+ ngx_http_limit_conn_cleanup(cln->data);
+ cln = cln->next;
+ }
+
+ pool->cleanup = cln;
+}
+
+
+static ngx_int_t
+ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)
+{
+ ngx_http_limit_conn_ctx_t *octx = data;
+
+ size_t len;
+ ngx_slab_pool_t *shpool;
+ ngx_rbtree_node_t *sentinel;
+ ngx_http_limit_conn_ctx_t *ctx;
+
+ ctx = shm_zone->data;
+
+ if (octx) {
+ if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) {
+ ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
+ "limit_conn_zone \"%V\" uses the \"%V\" variable "
+ "while previously it used the \"%V\" variable",
+ &shm_zone->shm.name, &ctx->var, &octx->var);
+ return NGX_ERROR;
+ }
+
+ ctx->rbtree = octx->rbtree;
+
+ return NGX_OK;
+ }
+
+ shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+ if (shm_zone->shm.exists) {
+ ctx->rbtree = shpool->data;
+
+ return NGX_OK;
+ }
+
+ ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t));
+ if (ctx->rbtree == NULL) {
+ return NGX_ERROR;
+ }
+
+ shpool->data = ctx->rbtree;
+
+ sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t));
+ if (sentinel == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_rbtree_init(ctx->rbtree, sentinel,
+ ngx_http_limit_conn_rbtree_insert_value);
+
+ len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len;
+
+ shpool->log_ctx = ngx_slab_alloc(shpool, len);
+ if (shpool->log_ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z",
+ &shm_zone->shm.name);
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_limit_conn_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_limit_conn_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->limits.elts = NULL;
+ */
+
+ conf->log_level = NGX_CONF_UNSET_UINT;
+ conf->status_code = NGX_CONF_UNSET_UINT;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_limit_conn_conf_t *prev = parent;
+ ngx_http_limit_conn_conf_t *conf = child;
+
+ if (conf->limits.elts == NULL) {
+ conf->limits = prev->limits;
+ }
+
+ ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);
+ ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
+ NGX_HTTP_SERVICE_UNAVAILABLE);
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ u_char *p;
+ ssize_t size;
+ ngx_str_t *value, name, s;
+ ngx_uint_t i;
+ ngx_shm_zone_t *shm_zone;
+ ngx_http_limit_conn_ctx_t *ctx;
+
+ value = cf->args->elts;
+
+ ctx = NULL;
+ size = 0;
+ name.len = 0;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
+
+ name.data = value[i].data + 5;
+
+ p = (u_char *) ngx_strchr(name.data, ':');
+
+ if (p == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid zone size \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ name.len = p - name.data;
+
+ s.data = p + 1;
+ s.len = value[i].data + value[i].len - s.data;
+
+ size = ngx_parse_size(&s);
+
+ if (size == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid zone size \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (size < (ssize_t) (8 * ngx_pagesize)) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "zone \"%V\" is too small", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (value[i].data[0] == '$') {
+
+ value[i].len--;
+ value[i].data++;
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->index = ngx_http_get_variable_index(cf, &value[i]);
+ if (ctx->index == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->var = value[i];
+
+ continue;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (name.len == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"%V\" must have \"zone\" parameter",
+ &cmd->name);
+ return NGX_CONF_ERROR;
+ }
+
+ if (ctx == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "no variable is defined for %V \"%V\"",
+ &cmd->name, &name);
+ return NGX_CONF_ERROR;
+ }
+
+ shm_zone = ngx_shared_memory_add(cf, &name, size,
+ &ngx_http_limit_conn_module);
+ if (shm_zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (shm_zone->data) {
+ ctx = shm_zone->data;
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%V \"%V\" is already bound to variable \"%V\"",
+ &cmd->name, &name, &ctx->var);
+ return NGX_CONF_ERROR;
+ }
+
+ shm_zone->init = ngx_http_limit_conn_init_zone;
+ shm_zone->data = ctx;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_limit_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ssize_t n;
+ ngx_str_t *value;
+ ngx_shm_zone_t *shm_zone;
+ ngx_http_limit_conn_ctx_t *ctx;
+
+ ngx_conf_deprecated(cf, &ngx_conf_deprecated_limit_zone, NULL);
+
+ value = cf->args->elts;
+
+ if (value[2].data[0] != '$') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ value[2].len--;
+ value[2].data++;
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->index = ngx_http_get_variable_index(cf, &value[2]);
+ if (ctx->index == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->var = value[2];
+
+ n = ngx_parse_size(&value[3]);
+
+ if (n == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid size of limit_zone \"%V\"", &value[3]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (n < (ngx_int_t) (8 * ngx_pagesize)) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "limit_zone \"%V\" is too small", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+
+ shm_zone = ngx_shared_memory_add(cf, &value[1], n,
+ &ngx_http_limit_conn_module);
+ if (shm_zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (shm_zone->data) {
+ ctx = shm_zone->data;
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "limit_zone \"%V\" is already bound to variable \"%V\"",
+ &value[1], &ctx->var);
+ return NGX_CONF_ERROR;
+ }
+
+ shm_zone->init = ngx_http_limit_conn_init_zone;
+ shm_zone->data = ctx;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_shm_zone_t *shm_zone;
+ ngx_http_limit_conn_conf_t *lccf = conf;
+ ngx_http_limit_conn_limit_t *limit, *limits;
+
+ ngx_str_t *value;
+ ngx_int_t n;
+ ngx_uint_t i;
+
+ value = cf->args->elts;
+
+ shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
+ &ngx_http_limit_conn_module);
+ if (shm_zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ limits = lccf->limits.elts;
+
+ if (limits == NULL) {
+ if (ngx_array_init(&lccf->limits, cf->pool, 1,
+ sizeof(ngx_http_limit_conn_limit_t))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ for (i = 0; i < lccf->limits.nelts; i++) {
+ if (shm_zone == limits[i].shm_zone) {
+ return "is duplicate";
+ }
+ }
+
+ n = ngx_atoi(value[2].data, value[2].len);
+ if (n <= 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid number of connections \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (n > 65535) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "connection limit must be less 65536");
+ return NGX_CONF_ERROR;
+ }
+
+ limit = ngx_array_push(&lccf->limits);
+ if (limit == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ limit->conn = n;
+ limit->shm_zone = shm_zone;
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_limit_conn_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_limit_conn_handler;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_limit_req_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_limit_req_module.c
new file mode 100644
index 00000000000..74f7fdaa75f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_limit_req_module.c
@@ -0,0 +1,989 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ u_char color;
+ u_char dummy;
+ u_short len;
+ ngx_queue_t queue;
+ ngx_msec_t last;
+ /* integer value, 1 corresponds to 0.001 r/s */
+ ngx_uint_t excess;
+ ngx_uint_t count;
+ u_char data[1];
+} ngx_http_limit_req_node_t;
+
+
+typedef struct {
+ ngx_rbtree_t rbtree;
+ ngx_rbtree_node_t sentinel;
+ ngx_queue_t queue;
+} ngx_http_limit_req_shctx_t;
+
+
+typedef struct {
+ ngx_http_limit_req_shctx_t *sh;
+ ngx_slab_pool_t *shpool;
+ /* integer value, 1 corresponds to 0.001 r/s */
+ ngx_uint_t rate;
+ ngx_int_t index;
+ ngx_str_t var;
+ ngx_http_limit_req_node_t *node;
+} ngx_http_limit_req_ctx_t;
+
+
+typedef struct {
+ ngx_shm_zone_t *shm_zone;
+ /* integer value, 1 corresponds to 0.001 r/s */
+ ngx_uint_t burst;
+ ngx_uint_t nodelay; /* unsigned nodelay:1 */
+} ngx_http_limit_req_limit_t;
+
+
+typedef struct {
+ ngx_array_t limits;
+ ngx_uint_t limit_log_level;
+ ngx_uint_t delay_log_level;
+ ngx_uint_t status_code;
+} ngx_http_limit_req_conf_t;
+
+
+static void ngx_http_limit_req_delay(ngx_http_request_t *r);
+static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit,
+ ngx_uint_t hash, u_char *data, size_t len, ngx_uint_t *ep,
+ ngx_uint_t account);
+static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits,
+ ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit);
+static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
+ ngx_uint_t n);
+
+static void *ngx_http_limit_req_create_conf(ngx_conf_t *cf);
+static char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf);
+
+
+static ngx_conf_enum_t ngx_http_limit_req_log_levels[] = {
+ { ngx_string("info"), NGX_LOG_INFO },
+ { ngx_string("notice"), NGX_LOG_NOTICE },
+ { ngx_string("warn"), NGX_LOG_WARN },
+ { ngx_string("error"), NGX_LOG_ERR },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_conf_num_bounds_t ngx_http_limit_req_status_bounds = {
+ ngx_conf_check_num_bounds, 400, 599
+};
+
+
+static ngx_command_t ngx_http_limit_req_commands[] = {
+
+ { ngx_string("limit_req_zone"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,
+ ngx_http_limit_req_zone,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("limit_req"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
+ ngx_http_limit_req,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("limit_req_log_level"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_limit_req_conf_t, limit_log_level),
+ &ngx_http_limit_req_log_levels },
+
+ { ngx_string("limit_req_status"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_limit_req_conf_t, status_code),
+ &ngx_http_limit_req_status_bounds },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_limit_req_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_limit_req_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_limit_req_create_conf, /* create location configuration */
+ ngx_http_limit_req_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_limit_req_module = {
+ NGX_MODULE_V1,
+ &ngx_http_limit_req_module_ctx, /* module context */
+ ngx_http_limit_req_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_limit_req_handler(ngx_http_request_t *r)
+{
+ size_t len;
+ uint32_t hash;
+ ngx_int_t rc;
+ ngx_uint_t n, excess;
+ ngx_msec_t delay;
+ ngx_http_variable_value_t *vv;
+ ngx_http_limit_req_ctx_t *ctx;
+ ngx_http_limit_req_conf_t *lrcf;
+ ngx_http_limit_req_limit_t *limit, *limits;
+
+ if (r->main->limit_req_set) {
+ return NGX_DECLINED;
+ }
+
+ lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
+ limits = lrcf->limits.elts;
+
+ excess = 0;
+
+ rc = NGX_DECLINED;
+
+#if (NGX_SUPPRESS_WARN)
+ limit = NULL;
+#endif
+
+ for (n = 0; n < lrcf->limits.nelts; n++) {
+
+ limit = &limits[n];
+
+ ctx = limit->shm_zone->data;
+
+ vv = ngx_http_get_indexed_variable(r, ctx->index);
+
+ if (vv == NULL || vv->not_found) {
+ continue;
+ }
+
+ len = vv->len;
+
+ if (len == 0) {
+ continue;
+ }
+
+ if (len > 65535) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "the value of the \"%V\" variable "
+ "is more than 65535 bytes: \"%v\"",
+ &ctx->var, vv);
+ continue;
+ }
+
+ hash = ngx_crc32_short(vv->data, len);
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+ rc = ngx_http_limit_req_lookup(limit, hash, vv->data, len, &excess,
+ (n == lrcf->limits.nelts - 1));
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "limit_req[%ui]: %i %ui.%03ui",
+ n, rc, excess / 1000, excess % 1000);
+
+ if (rc != NGX_AGAIN) {
+ break;
+ }
+ }
+
+ if (rc == NGX_DECLINED) {
+ return NGX_DECLINED;
+ }
+
+ r->main->limit_req_set = 1;
+
+ if (rc == NGX_BUSY || rc == NGX_ERROR) {
+
+ if (rc == NGX_BUSY) {
+ ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
+ "limiting requests, excess: %ui.%03ui by zone \"%V\"",
+ excess / 1000, excess % 1000,
+ &limit->shm_zone->shm.name);
+ }
+
+ while (n--) {
+ ctx = limits[n].shm_zone->data;
+
+ if (ctx->node == NULL) {
+ continue;
+ }
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+ ctx->node->count--;
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ ctx->node = NULL;
+ }
+
+ return lrcf->status_code;
+ }
+
+ /* rc == NGX_AGAIN || rc == NGX_OK */
+
+ if (rc == NGX_AGAIN) {
+ excess = 0;
+ }
+
+ delay = ngx_http_limit_req_account(limits, n, &excess, &limit);
+
+ if (!delay) {
+ return NGX_DECLINED;
+ }
+
+ ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
+ "delaying request, excess: %ui.%03ui, by zone \"%V\"",
+ excess / 1000, excess % 1000, &limit->shm_zone->shm.name);
+
+ if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ r->read_event_handler = ngx_http_test_reading;
+ r->write_event_handler = ngx_http_limit_req_delay;
+ ngx_add_timer(r->connection->write, delay);
+
+ return NGX_AGAIN;
+}
+
+
+static void
+ngx_http_limit_req_delay(ngx_http_request_t *r)
+{
+ ngx_event_t *wev;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "limit_req delay");
+
+ wev = r->connection->write;
+
+ if (!wev->timedout) {
+
+ if (ngx_handle_write_event(wev, 0) != NGX_OK) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ }
+
+ return;
+ }
+
+ wev->timedout = 0;
+
+ if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ r->read_event_handler = ngx_http_block_reading;
+ r->write_event_handler = ngx_http_core_run_phases;
+
+ ngx_http_core_run_phases(r);
+}
+
+
+static void
+ngx_http_limit_req_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+ ngx_rbtree_node_t **p;
+ ngx_http_limit_req_node_t *lrn, *lrnt;
+
+ for ( ;; ) {
+
+ if (node->key < temp->key) {
+
+ p = &temp->left;
+
+ } else if (node->key > temp->key) {
+
+ p = &temp->right;
+
+ } else { /* node->key == temp->key */
+
+ lrn = (ngx_http_limit_req_node_t *) &node->color;
+ lrnt = (ngx_http_limit_req_node_t *) &temp->color;
+
+ p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0)
+ ? &temp->left : &temp->right;
+ }
+
+ if (*p == sentinel) {
+ break;
+ }
+
+ temp = *p;
+ }
+
+ *p = node;
+ node->parent = temp;
+ node->left = sentinel;
+ node->right = sentinel;
+ ngx_rbt_red(node);
+}
+
+
+static ngx_int_t
+ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash,
+ u_char *data, size_t len, ngx_uint_t *ep, ngx_uint_t account)
+{
+ size_t size;
+ ngx_int_t rc, excess;
+ ngx_time_t *tp;
+ ngx_msec_t now;
+ ngx_msec_int_t ms;
+ ngx_rbtree_node_t *node, *sentinel;
+ ngx_http_limit_req_ctx_t *ctx;
+ ngx_http_limit_req_node_t *lr;
+
+ tp = ngx_timeofday();
+ now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
+
+ ctx = limit->shm_zone->data;
+
+ node = ctx->sh->rbtree.root;
+ sentinel = ctx->sh->rbtree.sentinel;
+
+ while (node != sentinel) {
+
+ if (hash < node->key) {
+ node = node->left;
+ continue;
+ }
+
+ if (hash > node->key) {
+ node = node->right;
+ continue;
+ }
+
+ /* hash == node->key */
+
+ lr = (ngx_http_limit_req_node_t *) &node->color;
+
+ rc = ngx_memn2cmp(data, lr->data, len, (size_t) lr->len);
+
+ if (rc == 0) {
+ ngx_queue_remove(&lr->queue);
+ ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
+
+ ms = (ngx_msec_int_t) (now - lr->last);
+
+ excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
+
+ if (excess < 0) {
+ excess = 0;
+ }
+
+ *ep = excess;
+
+ if ((ngx_uint_t) excess > limit->burst) {
+ return NGX_BUSY;
+ }
+
+ if (account) {
+ lr->excess = excess;
+ lr->last = now;
+ return NGX_OK;
+ }
+
+ lr->count++;
+
+ ctx->node = lr;
+
+ return NGX_AGAIN;
+ }
+
+ node = (rc < 0) ? node->left : node->right;
+ }
+
+ *ep = 0;
+
+ size = offsetof(ngx_rbtree_node_t, color)
+ + offsetof(ngx_http_limit_req_node_t, data)
+ + len;
+
+ ngx_http_limit_req_expire(ctx, 1);
+
+ node = ngx_slab_alloc_locked(ctx->shpool, size);
+
+ if (node == NULL) {
+ ngx_http_limit_req_expire(ctx, 0);
+
+ node = ngx_slab_alloc_locked(ctx->shpool, size);
+ if (node == NULL) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "could not allocate node%s", ctx->shpool->log_ctx);
+ return NGX_ERROR;
+ }
+ }
+
+ node->key = hash;
+
+ lr = (ngx_http_limit_req_node_t *) &node->color;
+
+ lr->len = (u_char) len;
+ lr->excess = 0;
+
+ ngx_memcpy(lr->data, data, len);
+
+ ngx_rbtree_insert(&ctx->sh->rbtree, node);
+
+ ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
+
+ if (account) {
+ lr->last = now;
+ lr->count = 0;
+ return NGX_OK;
+ }
+
+ lr->last = 0;
+ lr->count = 1;
+
+ ctx->node = lr;
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_msec_t
+ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n,
+ ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit)
+{
+ ngx_int_t excess;
+ ngx_time_t *tp;
+ ngx_msec_t now, delay, max_delay;
+ ngx_msec_int_t ms;
+ ngx_http_limit_req_ctx_t *ctx;
+ ngx_http_limit_req_node_t *lr;
+
+ excess = *ep;
+
+ if (excess == 0 || (*limit)->nodelay) {
+ max_delay = 0;
+
+ } else {
+ ctx = (*limit)->shm_zone->data;
+ max_delay = excess * 1000 / ctx->rate;
+ }
+
+ while (n--) {
+ ctx = limits[n].shm_zone->data;
+ lr = ctx->node;
+
+ if (lr == NULL) {
+ continue;
+ }
+
+ ngx_shmtx_lock(&ctx->shpool->mutex);
+
+ tp = ngx_timeofday();
+
+ now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
+ ms = (ngx_msec_int_t) (now - lr->last);
+
+ excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
+
+ if (excess < 0) {
+ excess = 0;
+ }
+
+ lr->last = now;
+ lr->excess = excess;
+ lr->count--;
+
+ ngx_shmtx_unlock(&ctx->shpool->mutex);
+
+ ctx->node = NULL;
+
+ if (limits[n].nodelay) {
+ continue;
+ }
+
+ delay = excess * 1000 / ctx->rate;
+
+ if (delay > max_delay) {
+ max_delay = delay;
+ *ep = excess;
+ *limit = &limits[n];
+ }
+ }
+
+ return max_delay;
+}
+
+
+static void
+ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
+{
+ ngx_int_t excess;
+ ngx_time_t *tp;
+ ngx_msec_t now;
+ ngx_queue_t *q;
+ ngx_msec_int_t ms;
+ ngx_rbtree_node_t *node;
+ ngx_http_limit_req_node_t *lr;
+
+ tp = ngx_timeofday();
+
+ now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
+
+ /*
+ * n == 1 deletes one or two zero rate entries
+ * n == 0 deletes oldest entry by force
+ * and one or two zero rate entries
+ */
+
+ while (n < 3) {
+
+ if (ngx_queue_empty(&ctx->sh->queue)) {
+ return;
+ }
+
+ q = ngx_queue_last(&ctx->sh->queue);
+
+ lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
+
+ if (lr->count) {
+
+ /*
+ * There is not much sense in looking further,
+ * because we bump nodes on the lookup stage.
+ */
+
+ return;
+ }
+
+ if (n++ != 0) {
+
+ ms = (ngx_msec_int_t) (now - lr->last);
+ ms = ngx_abs(ms);
+
+ if (ms < 60000) {
+ return;
+ }
+
+ excess = lr->excess - ctx->rate * ms / 1000;
+
+ if (excess > 0) {
+ return;
+ }
+ }
+
+ ngx_queue_remove(q);
+
+ node = (ngx_rbtree_node_t *)
+ ((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
+
+ ngx_rbtree_delete(&ctx->sh->rbtree, node);
+
+ ngx_slab_free_locked(ctx->shpool, node);
+ }
+}
+
+
+static ngx_int_t
+ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)
+{
+ ngx_http_limit_req_ctx_t *octx = data;
+
+ size_t len;
+ ngx_http_limit_req_ctx_t *ctx;
+
+ ctx = shm_zone->data;
+
+ if (octx) {
+ if (ngx_strcmp(ctx->var.data, octx->var.data) != 0) {
+ ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
+ "limit_req \"%V\" uses the \"%V\" variable "
+ "while previously it used the \"%V\" variable",
+ &shm_zone->shm.name, &ctx->var, &octx->var);
+ return NGX_ERROR;
+ }
+
+ ctx->sh = octx->sh;
+ ctx->shpool = octx->shpool;
+
+ return NGX_OK;
+ }
+
+ ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+ if (shm_zone->shm.exists) {
+ ctx->sh = ctx->shpool->data;
+
+ return NGX_OK;
+ }
+
+ ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_req_shctx_t));
+ if (ctx->sh == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->shpool->data = ctx->sh;
+
+ ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
+ ngx_http_limit_req_rbtree_insert_value);
+
+ ngx_queue_init(&ctx->sh->queue);
+
+ len = sizeof(" in limit_req zone \"\"") + shm_zone->shm.name.len;
+
+ ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len);
+ if (ctx->shpool->log_ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z",
+ &shm_zone->shm.name);
+
+ ctx->shpool->log_nomem = 0;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_limit_req_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_limit_req_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->limits.elts = NULL;
+ */
+
+ conf->limit_log_level = NGX_CONF_UNSET_UINT;
+ conf->status_code = NGX_CONF_UNSET_UINT;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_limit_req_conf_t *prev = parent;
+ ngx_http_limit_req_conf_t *conf = child;
+
+ if (conf->limits.elts == NULL) {
+ conf->limits = prev->limits;
+ }
+
+ ngx_conf_merge_uint_value(conf->limit_log_level, prev->limit_log_level,
+ NGX_LOG_ERR);
+
+ conf->delay_log_level = (conf->limit_log_level == NGX_LOG_INFO) ?
+ NGX_LOG_INFO : conf->limit_log_level + 1;
+
+ ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
+ NGX_HTTP_SERVICE_UNAVAILABLE);
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ u_char *p;
+ size_t len;
+ ssize_t size;
+ ngx_str_t *value, name, s;
+ ngx_int_t rate, scale;
+ ngx_uint_t i;
+ ngx_shm_zone_t *shm_zone;
+ ngx_http_limit_req_ctx_t *ctx;
+
+ value = cf->args->elts;
+
+ ctx = NULL;
+ size = 0;
+ rate = 1;
+ scale = 1;
+ name.len = 0;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
+
+ name.data = value[i].data + 5;
+
+ p = (u_char *) ngx_strchr(name.data, ':');
+
+ if (p == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid zone size \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ name.len = p - name.data;
+
+ s.data = p + 1;
+ s.len = value[i].data + value[i].len - s.data;
+
+ size = ngx_parse_size(&s);
+
+ if (size == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid zone size \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (size < (ssize_t) (8 * ngx_pagesize)) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "zone \"%V\" is too small", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "rate=", 5) == 0) {
+
+ len = value[i].len;
+ p = value[i].data + len - 3;
+
+ if (ngx_strncmp(p, "r/s", 3) == 0) {
+ scale = 1;
+ len -= 3;
+
+ } else if (ngx_strncmp(p, "r/m", 3) == 0) {
+ scale = 60;
+ len -= 3;
+ }
+
+ rate = ngx_atoi(value[i].data + 5, len - 5);
+ if (rate <= 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid rate \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (value[i].data[0] == '$') {
+
+ value[i].len--;
+ value[i].data++;
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->index = ngx_http_get_variable_index(cf, &value[i]);
+ if (ctx->index == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->var = value[i];
+
+ continue;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (name.len == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"%V\" must have \"zone\" parameter",
+ &cmd->name);
+ return NGX_CONF_ERROR;
+ }
+
+ if (ctx == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "no variable is defined for %V \"%V\"",
+ &cmd->name, &name);
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->rate = rate * 1000 / scale;
+
+ shm_zone = ngx_shared_memory_add(cf, &name, size,
+ &ngx_http_limit_req_module);
+ if (shm_zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (shm_zone->data) {
+ ctx = shm_zone->data;
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%V \"%V\" is already bound to variable \"%V\"",
+ &cmd->name, &name, &ctx->var);
+ return NGX_CONF_ERROR;
+ }
+
+ shm_zone->init = ngx_http_limit_req_init_zone;
+ shm_zone->data = ctx;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_limit_req_conf_t *lrcf = conf;
+
+ ngx_int_t burst;
+ ngx_str_t *value, s;
+ ngx_uint_t i, nodelay;
+ ngx_shm_zone_t *shm_zone;
+ ngx_http_limit_req_limit_t *limit, *limits;
+
+ value = cf->args->elts;
+
+ shm_zone = NULL;
+ burst = 0;
+ nodelay = 0;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (ngx_strncmp(value[i].data, "zone=", 5) == 0) {
+
+ s.len = value[i].len - 5;
+ s.data = value[i].data + 5;
+
+ shm_zone = ngx_shared_memory_add(cf, &s, 0,
+ &ngx_http_limit_req_module);
+ if (shm_zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "burst=", 6) == 0) {
+
+ burst = ngx_atoi(value[i].data + 6, value[i].len - 6);
+ if (burst <= 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid burst rate \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "nodelay") == 0) {
+ nodelay = 1;
+ continue;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (shm_zone == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"%V\" must have \"zone\" parameter",
+ &cmd->name);
+ return NGX_CONF_ERROR;
+ }
+
+ if (shm_zone->data == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unknown limit_req_zone \"%V\"",
+ &shm_zone->shm.name);
+ return NGX_CONF_ERROR;
+ }
+
+ limits = lrcf->limits.elts;
+
+ if (limits == NULL) {
+ if (ngx_array_init(&lrcf->limits, cf->pool, 1,
+ sizeof(ngx_http_limit_req_limit_t))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ for (i = 0; i < lrcf->limits.nelts; i++) {
+ if (shm_zone == limits[i].shm_zone) {
+ return "is duplicate";
+ }
+ }
+
+ limit = ngx_array_push(&lrcf->limits);
+ if (limit == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ limit->shm_zone = shm_zone;
+ limit->burst = burst * 1000;
+ limit->nodelay = nodelay;
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_limit_req_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_limit_req_handler;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_log_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_log_module.c
new file mode 100644
index 00000000000..bc660cdd6f8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_log_module.c
@@ -0,0 +1,1792 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#if (NGX_ZLIB)
+#include <zlib.h>
+#endif
+
+
+typedef struct ngx_http_log_op_s ngx_http_log_op_t;
+
+typedef u_char *(*ngx_http_log_op_run_pt) (ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op);
+
+typedef size_t (*ngx_http_log_op_getlen_pt) (ngx_http_request_t *r,
+ uintptr_t data);
+
+
+struct ngx_http_log_op_s {
+ size_t len;
+ ngx_http_log_op_getlen_pt getlen;
+ ngx_http_log_op_run_pt run;
+ uintptr_t data;
+};
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_array_t *flushes;
+ ngx_array_t *ops; /* array of ngx_http_log_op_t */
+} ngx_http_log_fmt_t;
+
+
+typedef struct {
+ ngx_array_t formats; /* array of ngx_http_log_fmt_t */
+ ngx_uint_t combined_used; /* unsigned combined_used:1 */
+} ngx_http_log_main_conf_t;
+
+
+typedef struct {
+ u_char *start;
+ u_char *pos;
+ u_char *last;
+
+ ngx_event_t *event;
+ ngx_msec_t flush;
+ ngx_int_t gzip;
+} ngx_http_log_buf_t;
+
+
+typedef struct {
+ ngx_array_t *lengths;
+ ngx_array_t *values;
+} ngx_http_log_script_t;
+
+
+typedef struct {
+ ngx_open_file_t *file;
+ ngx_http_log_script_t *script;
+ time_t disk_full_time;
+ time_t error_log_time;
+ ngx_syslog_peer_t *syslog_peer;
+ ngx_http_log_fmt_t *format;
+ ngx_http_complex_value_t *filter;
+} ngx_http_log_t;
+
+
+typedef struct {
+ ngx_array_t *logs; /* array of ngx_http_log_t */
+
+ ngx_open_file_cache_t *open_file_cache;
+ time_t open_file_cache_valid;
+ ngx_uint_t open_file_cache_min_uses;
+
+ ngx_uint_t off; /* unsigned off:1 */
+} ngx_http_log_loc_conf_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ size_t len;
+ ngx_http_log_op_run_pt run;
+} ngx_http_log_var_t;
+
+
+static void ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log,
+ u_char *buf, size_t len);
+static ssize_t ngx_http_log_script_write(ngx_http_request_t *r,
+ ngx_http_log_script_t *script, u_char **name, u_char *buf, size_t len);
+
+#if (NGX_ZLIB)
+static ssize_t ngx_http_log_gzip(ngx_fd_t fd, u_char *buf, size_t len,
+ ngx_int_t level, ngx_log_t *log);
+
+static void *ngx_http_log_gzip_alloc(void *opaque, u_int items, u_int size);
+static void ngx_http_log_gzip_free(void *opaque, void *address);
+#endif
+
+static void ngx_http_log_flush(ngx_open_file_t *file, ngx_log_t *log);
+static void ngx_http_log_flush_handler(ngx_event_t *ev);
+
+static u_char *ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op);
+static u_char *ngx_http_log_time(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op);
+static u_char *ngx_http_log_iso8601(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op);
+static u_char *ngx_http_log_msec(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op);
+static u_char *ngx_http_log_request_time(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op);
+static u_char *ngx_http_log_status(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op);
+static u_char *ngx_http_log_bytes_sent(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op);
+static u_char *ngx_http_log_body_bytes_sent(ngx_http_request_t *r,
+ u_char *buf, ngx_http_log_op_t *op);
+static u_char *ngx_http_log_request_length(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op);
+
+static ngx_int_t ngx_http_log_variable_compile(ngx_conf_t *cf,
+ ngx_http_log_op_t *op, ngx_str_t *value);
+static size_t ngx_http_log_variable_getlen(ngx_http_request_t *r,
+ uintptr_t data);
+static u_char *ngx_http_log_variable(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op);
+static uintptr_t ngx_http_log_escape(u_char *dst, u_char *src, size_t size);
+
+
+static void *ngx_http_log_create_main_conf(ngx_conf_t *cf);
+static void *ngx_http_log_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_log_compile_format(ngx_conf_t *cf,
+ ngx_array_t *flushes, ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s);
+static char *ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static ngx_int_t ngx_http_log_init(ngx_conf_t *cf);
+
+
+static ngx_command_t ngx_http_log_commands[] = {
+
+ { ngx_string("log_format"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE,
+ ngx_http_log_set_format,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("access_log"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_HTTP_LMT_CONF|NGX_CONF_1MORE,
+ ngx_http_log_set_log,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("open_log_file_cache"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
+ ngx_http_log_open_file_cache,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_log_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_log_init, /* postconfiguration */
+
+ ngx_http_log_create_main_conf, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_log_create_loc_conf, /* create location configuration */
+ ngx_http_log_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_log_module = {
+ NGX_MODULE_V1,
+ &ngx_http_log_module_ctx, /* module context */
+ ngx_http_log_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t ngx_http_access_log = ngx_string(NGX_HTTP_LOG_PATH);
+
+
+static ngx_str_t ngx_http_combined_fmt =
+ ngx_string("$remote_addr - $remote_user [$time_local] "
+ "\"$request\" $status $body_bytes_sent "
+ "\"$http_referer\" \"$http_user_agent\"");
+
+
+static ngx_http_log_var_t ngx_http_log_vars[] = {
+ { ngx_string("pipe"), 1, ngx_http_log_pipe },
+ { ngx_string("time_local"), sizeof("28/Sep/1970:12:00:00 +0600") - 1,
+ ngx_http_log_time },
+ { ngx_string("time_iso8601"), sizeof("1970-09-28T12:00:00+06:00") - 1,
+ ngx_http_log_iso8601 },
+ { ngx_string("msec"), NGX_TIME_T_LEN + 4, ngx_http_log_msec },
+ { ngx_string("request_time"), NGX_TIME_T_LEN + 4,
+ ngx_http_log_request_time },
+ { ngx_string("status"), NGX_INT_T_LEN, ngx_http_log_status },
+ { ngx_string("bytes_sent"), NGX_OFF_T_LEN, ngx_http_log_bytes_sent },
+ { ngx_string("body_bytes_sent"), NGX_OFF_T_LEN,
+ ngx_http_log_body_bytes_sent },
+ { ngx_string("request_length"), NGX_SIZE_T_LEN,
+ ngx_http_log_request_length },
+
+ { ngx_null_string, 0, NULL }
+};
+
+
+static ngx_int_t
+ngx_http_log_handler(ngx_http_request_t *r)
+{
+ u_char *line, *p;
+ size_t len, size;
+ ssize_t n;
+ ngx_str_t val;
+ ngx_uint_t i, l;
+ ngx_http_log_t *log;
+ ngx_http_log_op_t *op;
+ ngx_http_log_buf_t *buffer;
+ ngx_http_log_loc_conf_t *lcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http log handler");
+
+ lcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);
+
+ if (lcf->off) {
+ return NGX_OK;
+ }
+
+ log = lcf->logs->elts;
+ for (l = 0; l < lcf->logs->nelts; l++) {
+
+ if (log[l].filter) {
+ if (ngx_http_complex_value(r, log[l].filter, &val) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) {
+ continue;
+ }
+ }
+
+ if (ngx_time() == log[l].disk_full_time) {
+
+ /*
+ * on FreeBSD writing to a full filesystem with enabled softupdates
+ * may block process for much longer time than writing to non-full
+ * filesystem, so we skip writing to a log for one second
+ */
+
+ continue;
+ }
+
+ ngx_http_script_flush_no_cacheable_variables(r, log[l].format->flushes);
+
+ len = 0;
+ op = log[l].format->ops->elts;
+ for (i = 0; i < log[l].format->ops->nelts; i++) {
+ if (op[i].len == 0) {
+ len += op[i].getlen(r, op[i].data);
+
+ } else {
+ len += op[i].len;
+ }
+ }
+
+ if (log[l].syslog_peer) {
+
+ /* length of syslog's PRI and HEADER message parts */
+ len += sizeof("<255>Jan 01 00:00:00 ") - 1
+ + ngx_cycle->hostname.len + 1
+ + log[l].syslog_peer->tag.len + 2;
+
+ goto alloc_line;
+ }
+
+ len += NGX_LINEFEED_SIZE;
+
+ buffer = log[l].file ? log[l].file->data : NULL;
+
+ if (buffer) {
+
+ if (len > (size_t) (buffer->last - buffer->pos)) {
+
+ ngx_http_log_write(r, &log[l], buffer->start,
+ buffer->pos - buffer->start);
+
+ buffer->pos = buffer->start;
+ }
+
+ if (len <= (size_t) (buffer->last - buffer->pos)) {
+
+ p = buffer->pos;
+
+ if (buffer->event && p == buffer->start) {
+ ngx_add_timer(buffer->event, buffer->flush);
+ }
+
+ for (i = 0; i < log[l].format->ops->nelts; i++) {
+ p = op[i].run(r, p, &op[i]);
+ }
+
+ ngx_linefeed(p);
+
+ buffer->pos = p;
+
+ continue;
+ }
+
+ if (buffer->event && buffer->event->timer_set) {
+ ngx_del_timer(buffer->event);
+ }
+ }
+
+ alloc_line:
+
+ line = ngx_pnalloc(r->pool, len);
+ if (line == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = line;
+
+ if (log[l].syslog_peer) {
+ p = ngx_syslog_add_header(log[l].syslog_peer, line);
+ }
+
+ for (i = 0; i < log[l].format->ops->nelts; i++) {
+ p = op[i].run(r, p, &op[i]);
+ }
+
+ if (log[l].syslog_peer) {
+
+ size = p - line;
+
+ n = ngx_syslog_send(log[l].syslog_peer, line, size);
+
+ if (n < 0) {
+ ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+ "send() to syslog failed");
+
+ } else if ((size_t) n != size) {
+ ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+ "send() to syslog has written only %z of %uz",
+ n, size);
+ }
+
+ continue;
+ }
+
+ ngx_linefeed(p);
+
+ ngx_http_log_write(r, &log[l], line, p - line);
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log, u_char *buf,
+ size_t len)
+{
+ u_char *name;
+ time_t now;
+ ssize_t n;
+ ngx_err_t err;
+#if (NGX_ZLIB)
+ ngx_http_log_buf_t *buffer;
+#endif
+
+ if (log->script == NULL) {
+ name = log->file->name.data;
+
+#if (NGX_ZLIB)
+ buffer = log->file->data;
+
+ if (buffer && buffer->gzip) {
+ n = ngx_http_log_gzip(log->file->fd, buf, len, buffer->gzip,
+ r->connection->log);
+ } else {
+ n = ngx_write_fd(log->file->fd, buf, len);
+ }
+#else
+ n = ngx_write_fd(log->file->fd, buf, len);
+#endif
+
+ } else {
+ name = NULL;
+ n = ngx_http_log_script_write(r, log->script, &name, buf, len);
+ }
+
+ if (n == (ssize_t) len) {
+ return;
+ }
+
+ now = ngx_time();
+
+ if (n == -1) {
+ err = ngx_errno;
+
+ if (err == NGX_ENOSPC) {
+ log->disk_full_time = now;
+ }
+
+ if (now - log->error_log_time > 59) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, err,
+ ngx_write_fd_n " to \"%s\" failed", name);
+
+ log->error_log_time = now;
+ }
+
+ return;
+ }
+
+ if (now - log->error_log_time > 59) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
+ name, n, len);
+
+ log->error_log_time = now;
+ }
+}
+
+
+static ssize_t
+ngx_http_log_script_write(ngx_http_request_t *r, ngx_http_log_script_t *script,
+ u_char **name, u_char *buf, size_t len)
+{
+ size_t root;
+ ssize_t n;
+ ngx_str_t log, path;
+ ngx_open_file_info_t of;
+ ngx_http_log_loc_conf_t *llcf;
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (!r->root_tested) {
+
+ /* test root directory existence */
+
+ if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
+ /* simulate successful logging */
+ return len;
+ }
+
+ path.data[root] = '\0';
+
+ ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+ of.valid = clcf->open_file_cache_valid;
+ of.min_uses = clcf->open_file_cache_min_uses;
+ of.test_dir = 1;
+ of.test_only = 1;
+ of.errors = clcf->open_file_cache_errors;
+ of.events = clcf->open_file_cache_events;
+
+ if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+ /* simulate successful logging */
+ return len;
+ }
+
+ if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+ != NGX_OK)
+ {
+ if (of.err == 0) {
+ /* simulate successful logging */
+ return len;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err,
+ "testing \"%s\" existence failed", path.data);
+
+ /* simulate successful logging */
+ return len;
+ }
+
+ if (!of.is_dir) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ENOTDIR,
+ "testing \"%s\" existence failed", path.data);
+
+ /* simulate successful logging */
+ return len;
+ }
+ }
+
+ if (ngx_http_script_run(r, &log, script->lengths->elts, 1,
+ script->values->elts)
+ == NULL)
+ {
+ /* simulate successful logging */
+ return len;
+ }
+
+ log.data[log.len - 1] = '\0';
+ *name = log.data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http log \"%s\"", log.data);
+
+ llcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);
+
+ ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+ of.log = 1;
+ of.valid = llcf->open_file_cache_valid;
+ of.min_uses = llcf->open_file_cache_min_uses;
+ of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
+
+ if (ngx_http_set_disable_symlinks(r, clcf, &log, &of) != NGX_OK) {
+ /* simulate successful logging */
+ return len;
+ }
+
+ if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool)
+ != NGX_OK)
+ {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+ "%s \"%s\" failed", of.failed, log.data);
+ /* simulate successful logging */
+ return len;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http log #%d", of.fd);
+
+ n = ngx_write_fd(of.fd, buf, len);
+
+ return n;
+}
+
+
+#if (NGX_ZLIB)
+
+static ssize_t
+ngx_http_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, ngx_int_t level,
+ ngx_log_t *log)
+{
+ int rc, wbits, memlevel;
+ u_char *out;
+ size_t size;
+ ssize_t n;
+ z_stream zstream;
+ ngx_err_t err;
+ ngx_pool_t *pool;
+
+ wbits = MAX_WBITS;
+ memlevel = MAX_MEM_LEVEL - 1;
+
+ while ((ssize_t) len < ((1 << (wbits - 1)) - 262)) {
+ wbits--;
+ memlevel--;
+ }
+
+ /*
+ * This is a formula from deflateBound() for conservative upper bound of
+ * compressed data plus 18 bytes of gzip wrapper.
+ */
+
+ size = len + ((len + 7) >> 3) + ((len + 63) >> 6) + 5 + 18;
+
+ ngx_memzero(&zstream, sizeof(z_stream));
+
+ pool = ngx_create_pool(256, log);
+ if (pool == NULL) {
+ /* simulate successful logging */
+ return len;
+ }
+
+ pool->log = log;
+
+ zstream.zalloc = ngx_http_log_gzip_alloc;
+ zstream.zfree = ngx_http_log_gzip_free;
+ zstream.opaque = pool;
+
+ out = ngx_pnalloc(pool, size);
+ if (out == NULL) {
+ goto done;
+ }
+
+ zstream.next_in = buf;
+ zstream.avail_in = len;
+ zstream.next_out = out;
+ zstream.avail_out = size;
+
+ rc = deflateInit2(&zstream, (int) level, Z_DEFLATED, wbits + 16, memlevel,
+ Z_DEFAULT_STRATEGY);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateInit2() failed: %d", rc);
+ goto done;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0,
+ "deflate in: ni:%p no:%p ai:%ud ao:%ud",
+ zstream.next_in, zstream.next_out,
+ zstream.avail_in, zstream.avail_out);
+
+ rc = deflate(&zstream, Z_FINISH);
+
+ if (rc != Z_STREAM_END) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "deflate(Z_FINISH) failed: %d", rc);
+ goto done;
+ }
+
+ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, log, 0,
+ "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
+ zstream.next_in, zstream.next_out,
+ zstream.avail_in, zstream.avail_out,
+ rc);
+
+ size -= zstream.avail_out;
+
+ rc = deflateEnd(&zstream);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateEnd() failed: %d", rc);
+ goto done;
+ }
+
+ n = ngx_write_fd(fd, out, size);
+
+ if (n != (ssize_t) size) {
+ err = (n == -1) ? ngx_errno : 0;
+
+ ngx_destroy_pool(pool);
+
+ ngx_set_errno(err);
+ return -1;
+ }
+
+done:
+
+ ngx_destroy_pool(pool);
+
+ /* simulate successful logging */
+ return len;
+}
+
+
+static void *
+ngx_http_log_gzip_alloc(void *opaque, u_int items, u_int size)
+{
+ ngx_pool_t *pool = opaque;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pool->log, 0,
+ "gzip alloc: n:%ud s:%ud", items, size);
+
+ return ngx_palloc(pool, items * size);
+}
+
+
+static void
+ngx_http_log_gzip_free(void *opaque, void *address)
+{
+#if 0
+ ngx_pool_t *pool = opaque;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0, "gzip free: %p", address);
+#endif
+}
+
+#endif
+
+
+static void
+ngx_http_log_flush(ngx_open_file_t *file, ngx_log_t *log)
+{
+ size_t len;
+ ssize_t n;
+ ngx_http_log_buf_t *buffer;
+
+ buffer = file->data;
+
+ len = buffer->pos - buffer->start;
+
+ if (len == 0) {
+ return;
+ }
+
+#if (NGX_ZLIB)
+ if (buffer->gzip) {
+ n = ngx_http_log_gzip(file->fd, buffer->start, len, buffer->gzip, log);
+ } else {
+ n = ngx_write_fd(file->fd, buffer->start, len);
+ }
+#else
+ n = ngx_write_fd(file->fd, buffer->start, len);
+#endif
+
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_write_fd_n " to \"%s\" failed",
+ file->name.data);
+
+ } else if ((size_t) n != len) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
+ file->name.data, n, len);
+ }
+
+ buffer->pos = buffer->start;
+
+ if (buffer->event && buffer->event->timer_set) {
+ ngx_del_timer(buffer->event);
+ }
+}
+
+
+static void
+ngx_http_log_flush_handler(ngx_event_t *ev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+ "http log buffer flush handler");
+
+ ngx_http_log_flush(ev->data, ev->log);
+}
+
+
+static u_char *
+ngx_http_log_copy_short(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op)
+{
+ size_t len;
+ uintptr_t data;
+
+ len = op->len;
+ data = op->data;
+
+ while (len--) {
+ *buf++ = (u_char) (data & 0xff);
+ data >>= 8;
+ }
+
+ return buf;
+}
+
+
+static u_char *
+ngx_http_log_copy_long(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op)
+{
+ return ngx_cpymem(buf, (u_char *) op->data, op->len);
+}
+
+
+static u_char *
+ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
+{
+ if (r->pipeline) {
+ *buf = 'p';
+ } else {
+ *buf = '.';
+ }
+
+ return buf + 1;
+}
+
+
+static u_char *
+ngx_http_log_time(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
+{
+ return ngx_cpymem(buf, ngx_cached_http_log_time.data,
+ ngx_cached_http_log_time.len);
+}
+
+static u_char *
+ngx_http_log_iso8601(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
+{
+ return ngx_cpymem(buf, ngx_cached_http_log_iso8601.data,
+ ngx_cached_http_log_iso8601.len);
+}
+
+static u_char *
+ngx_http_log_msec(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
+{
+ ngx_time_t *tp;
+
+ tp = ngx_timeofday();
+
+ return ngx_sprintf(buf, "%T.%03M", tp->sec, tp->msec);
+}
+
+
+static u_char *
+ngx_http_log_request_time(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op)
+{
+ ngx_time_t *tp;
+ ngx_msec_int_t ms;
+
+ tp = ngx_timeofday();
+
+ ms = (ngx_msec_int_t)
+ ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));
+ ms = ngx_max(ms, 0);
+
+ return ngx_sprintf(buf, "%T.%03M", (time_t) ms / 1000, ms % 1000);
+}
+
+
+static u_char *
+ngx_http_log_status(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
+{
+ ngx_uint_t status;
+
+ if (r->err_status) {
+ status = r->err_status;
+
+ } else if (r->headers_out.status) {
+ status = r->headers_out.status;
+
+ } else if (r->http_version == NGX_HTTP_VERSION_9) {
+ status = 9;
+
+ } else {
+ status = 0;
+ }
+
+ return ngx_sprintf(buf, "%03ui", status);
+}
+
+
+static u_char *
+ngx_http_log_bytes_sent(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op)
+{
+ return ngx_sprintf(buf, "%O", r->connection->sent);
+}
+
+
+/*
+ * although there is a real $body_bytes_sent variable,
+ * this log operation code function is more optimized for logging
+ */
+
+static u_char *
+ngx_http_log_body_bytes_sent(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op)
+{
+ off_t length;
+
+ length = r->connection->sent - r->header_size;
+
+ if (length > 0) {
+ return ngx_sprintf(buf, "%O", length);
+ }
+
+ *buf = '0';
+
+ return buf + 1;
+}
+
+
+static u_char *
+ngx_http_log_request_length(ngx_http_request_t *r, u_char *buf,
+ ngx_http_log_op_t *op)
+{
+ return ngx_sprintf(buf, "%O", r->request_length);
+}
+
+
+static ngx_int_t
+ngx_http_log_variable_compile(ngx_conf_t *cf, ngx_http_log_op_t *op,
+ ngx_str_t *value)
+{
+ ngx_int_t index;
+
+ index = ngx_http_get_variable_index(cf, value);
+ if (index == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ op->len = 0;
+ op->getlen = ngx_http_log_variable_getlen;
+ op->run = ngx_http_log_variable;
+ op->data = index;
+
+ return NGX_OK;
+}
+
+
+static size_t
+ngx_http_log_variable_getlen(ngx_http_request_t *r, uintptr_t data)
+{
+ uintptr_t len;
+ ngx_http_variable_value_t *value;
+
+ value = ngx_http_get_indexed_variable(r, data);
+
+ if (value == NULL || value->not_found) {
+ return 1;
+ }
+
+ len = ngx_http_log_escape(NULL, value->data, value->len);
+
+ value->escape = len ? 1 : 0;
+
+ return value->len + len * 3;
+}
+
+
+static u_char *
+ngx_http_log_variable(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
+{
+ ngx_http_variable_value_t *value;
+
+ value = ngx_http_get_indexed_variable(r, op->data);
+
+ if (value == NULL || value->not_found) {
+ *buf = '-';
+ return buf + 1;
+ }
+
+ if (value->escape == 0) {
+ return ngx_cpymem(buf, value->data, value->len);
+
+ } else {
+ return (u_char *) ngx_http_log_escape(buf, value->data, value->len);
+ }
+}
+
+
+static uintptr_t
+ngx_http_log_escape(u_char *dst, u_char *src, size_t size)
+{
+ ngx_uint_t n;
+ static u_char hex[] = "0123456789ABCDEF";
+
+ static uint32_t escape[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x00000004, /* 0000 0000 0000 0000 0000 0000 0000 0100 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x10000000, /* 0001 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ };
+
+
+ if (dst == NULL) {
+
+ /* find the number of the characters to be escaped */
+
+ n = 0;
+
+ while (size) {
+ if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
+ n++;
+ }
+ src++;
+ size--;
+ }
+
+ return (uintptr_t) n;
+ }
+
+ while (size) {
+ if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
+ *dst++ = '\\';
+ *dst++ = 'x';
+ *dst++ = hex[*src >> 4];
+ *dst++ = hex[*src & 0xf];
+ src++;
+
+ } else {
+ *dst++ = *src++;
+ }
+ size--;
+ }
+
+ return (uintptr_t) dst;
+}
+
+
+static void *
+ngx_http_log_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_http_log_main_conf_t *conf;
+
+ ngx_http_log_fmt_t *fmt;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_main_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(&conf->formats, cf->pool, 4, sizeof(ngx_http_log_fmt_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ fmt = ngx_array_push(&conf->formats);
+ if (fmt == NULL) {
+ return NULL;
+ }
+
+ ngx_str_set(&fmt->name, "combined");
+
+ fmt->flushes = NULL;
+
+ fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t));
+ if (fmt->ops == NULL) {
+ return NULL;
+ }
+
+ return conf;
+}
+
+
+static void *
+ngx_http_log_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_log_loc_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->open_file_cache = NGX_CONF_UNSET_PTR;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_log_loc_conf_t *prev = parent;
+ ngx_http_log_loc_conf_t *conf = child;
+
+ ngx_http_log_t *log;
+ ngx_http_log_fmt_t *fmt;
+ ngx_http_log_main_conf_t *lmcf;
+
+ if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {
+
+ conf->open_file_cache = prev->open_file_cache;
+ conf->open_file_cache_valid = prev->open_file_cache_valid;
+ conf->open_file_cache_min_uses = prev->open_file_cache_min_uses;
+
+ if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {
+ conf->open_file_cache = NULL;
+ }
+ }
+
+ if (conf->logs || conf->off) {
+ return NGX_CONF_OK;
+ }
+
+ conf->logs = prev->logs;
+ conf->off = prev->off;
+
+ if (conf->logs || conf->off) {
+ return NGX_CONF_OK;
+ }
+
+ conf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t));
+ if (conf->logs == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ log = ngx_array_push(conf->logs);
+ if (log == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(log, sizeof(ngx_http_log_t));
+
+ log->file = ngx_conf_open_file(cf->cycle, &ngx_http_access_log);
+ if (log->file == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);
+ fmt = lmcf->formats.elts;
+
+ /* the default "combined" format */
+ log->format = &fmt[0];
+ lmcf->combined_used = 1;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_log_loc_conf_t *llcf = conf;
+
+ ssize_t size;
+ ngx_int_t gzip;
+ ngx_uint_t i, n;
+ ngx_msec_t flush;
+ ngx_str_t *value, name, s, filter;
+ ngx_http_log_t *log;
+ ngx_syslog_peer_t *peer;
+ ngx_http_log_buf_t *buffer;
+ ngx_http_log_fmt_t *fmt;
+ ngx_http_log_main_conf_t *lmcf;
+ ngx_http_script_compile_t sc;
+ ngx_http_compile_complex_value_t ccv;
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ llcf->off = 1;
+ if (cf->args->nelts == 2) {
+ return NGX_CONF_OK;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (llcf->logs == NULL) {
+ llcf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t));
+ if (llcf->logs == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);
+
+ log = ngx_array_push(llcf->logs);
+ if (log == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(log, sizeof(ngx_http_log_t));
+
+
+ if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) {
+
+ peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));
+ if (peer == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ log->syslog_peer = peer;
+
+ goto process_formats;
+ }
+
+ n = ngx_http_script_variables_count(&value[1]);
+
+ if (n == 0) {
+ log->file = ngx_conf_open_file(cf->cycle, &value[1]);
+ if (log->file == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+ if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ log->script = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_script_t));
+ if (log->script == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &value[1];
+ sc.lengths = &log->script->lengths;
+ sc.values = &log->script->values;
+ sc.variables = n;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+process_formats:
+
+ if (cf->args->nelts >= 3) {
+ name = value[2];
+
+ if (ngx_strcmp(name.data, "combined") == 0) {
+ lmcf->combined_used = 1;
+ }
+
+ } else {
+ ngx_str_set(&name, "combined");
+ lmcf->combined_used = 1;
+ }
+
+ fmt = lmcf->formats.elts;
+ for (i = 0; i < lmcf->formats.nelts; i++) {
+ if (fmt[i].name.len == name.len
+ && ngx_strcasecmp(fmt[i].name.data, name.data) == 0)
+ {
+ log->format = &fmt[i];
+ break;
+ }
+ }
+
+ if (log->format == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unknown log format \"%V\"", &name);
+ return NGX_CONF_ERROR;
+ }
+
+ if (log->syslog_peer != NULL) {
+ if (cf->args->nelts > 3) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "parameter \"%V\" is not supported by syslog",
+ &value[3]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+ }
+
+ size = 0;
+ flush = 0;
+ gzip = 0;
+ filter.len = 0;
+
+ for (i = 3; i < cf->args->nelts; i++) {
+
+ if (ngx_strncmp(value[i].data, "buffer=", 7) == 0) {
+ s.len = value[i].len - 7;
+ s.data = value[i].data + 7;
+
+ size = ngx_parse_size(&s);
+
+ if (size == NGX_ERROR || size == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid buffer size \"%V\"", &s);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "flush=", 6) == 0) {
+ s.len = value[i].len - 6;
+ s.data = value[i].data + 6;
+
+ flush = ngx_parse_time(&s, 0);
+
+ if (flush == (ngx_msec_t) NGX_ERROR || flush == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid flush time \"%V\"", &s);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "gzip", 4) == 0
+ && (value[i].len == 4 || value[i].data[4] == '='))
+ {
+#if (NGX_ZLIB)
+ if (size == 0) {
+ size = 64 * 1024;
+ }
+
+ if (value[i].len == 4) {
+ gzip = Z_BEST_SPEED;
+ continue;
+ }
+
+ s.len = value[i].len - 5;
+ s.data = value[i].data + 5;
+
+ gzip = ngx_atoi(s.data, s.len);
+
+ if (gzip < 1 || gzip > 9) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid compression level \"%V\"", &s);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "nginx was built without zlib support");
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ if (ngx_strncmp(value[i].data, "if=", 3) == 0) {
+ filter.len = value[i].len - 3;
+ filter.data = value[i].data + 3;
+ continue;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (flush && size == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "no buffer is defined for access_log \"%V\"",
+ &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (size) {
+
+ if (log->script) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "buffered logs cannot have variables in name");
+ return NGX_CONF_ERROR;
+ }
+
+ if (log->file->data) {
+ buffer = log->file->data;
+
+ if (buffer->last - buffer->start != size
+ || buffer->flush != flush
+ || buffer->gzip != gzip)
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "access_log \"%V\" already defined "
+ "with conflicting parameters",
+ &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+ }
+
+ buffer = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_buf_t));
+ if (buffer == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ buffer->start = ngx_pnalloc(cf->pool, size);
+ if (buffer->start == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ buffer->pos = buffer->start;
+ buffer->last = buffer->start + size;
+
+ if (flush) {
+ buffer->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t));
+ if (buffer->event == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ buffer->event->data = log->file;
+ buffer->event->handler = ngx_http_log_flush_handler;
+ buffer->event->log = &cf->cycle->new_log;
+
+ buffer->flush = flush;
+ }
+
+ buffer->gzip = gzip;
+
+ log->file->flush = ngx_http_log_flush;
+ log->file->data = buffer;
+ }
+
+ if (filter.len) {
+ log->filter = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+ if (log->filter == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &filter;
+ ccv.complex_value = log->filter;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_log_main_conf_t *lmcf = conf;
+
+ ngx_str_t *value;
+ ngx_uint_t i;
+ ngx_http_log_fmt_t *fmt;
+
+ if (cf->cmd_type != NGX_HTTP_MAIN_CONF) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "the \"log_format\" directive may be used "
+ "only on \"http\" level");
+ }
+
+ value = cf->args->elts;
+
+ fmt = lmcf->formats.elts;
+ for (i = 0; i < lmcf->formats.nelts; i++) {
+ if (fmt[i].name.len == value[1].len
+ && ngx_strcmp(fmt[i].name.data, value[1].data) == 0)
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate \"log_format\" name \"%V\"",
+ &value[1]);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ fmt = ngx_array_push(&lmcf->formats);
+ if (fmt == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ fmt->name = value[1];
+
+ fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t));
+ if (fmt->flushes == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t));
+ if (fmt->ops == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return ngx_http_log_compile_format(cf, fmt->flushes, fmt->ops, cf->args, 2);
+}
+
+
+static char *
+ngx_http_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes,
+ ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s)
+{
+ u_char *data, *p, ch;
+ size_t i, len;
+ ngx_str_t *value, var;
+ ngx_int_t *flush;
+ ngx_uint_t bracket;
+ ngx_http_log_op_t *op;
+ ngx_http_log_var_t *v;
+
+ value = args->elts;
+
+ for ( /* void */ ; s < args->nelts; s++) {
+
+ i = 0;
+
+ while (i < value[s].len) {
+
+ op = ngx_array_push(ops);
+ if (op == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ data = &value[s].data[i];
+
+ if (value[s].data[i] == '$') {
+
+ if (++i == value[s].len) {
+ goto invalid;
+ }
+
+ if (value[s].data[i] == '{') {
+ bracket = 1;
+
+ if (++i == value[s].len) {
+ goto invalid;
+ }
+
+ var.data = &value[s].data[i];
+
+ } else {
+ bracket = 0;
+ var.data = &value[s].data[i];
+ }
+
+ for (var.len = 0; i < value[s].len; i++, var.len++) {
+ ch = value[s].data[i];
+
+ if (ch == '}' && bracket) {
+ i++;
+ bracket = 0;
+ break;
+ }
+
+ if ((ch >= 'A' && ch <= 'Z')
+ || (ch >= 'a' && ch <= 'z')
+ || (ch >= '0' && ch <= '9')
+ || ch == '_')
+ {
+ continue;
+ }
+
+ break;
+ }
+
+ if (bracket) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the closing bracket in \"%V\" "
+ "variable is missing", &var);
+ return NGX_CONF_ERROR;
+ }
+
+ if (var.len == 0) {
+ goto invalid;
+ }
+
+ for (v = ngx_http_log_vars; v->name.len; v++) {
+
+ if (v->name.len == var.len
+ && ngx_strncmp(v->name.data, var.data, var.len) == 0)
+ {
+ op->len = v->len;
+ op->getlen = NULL;
+ op->run = v->run;
+ op->data = 0;
+
+ goto found;
+ }
+ }
+
+ if (ngx_http_log_variable_compile(cf, op, &var) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (flushes) {
+
+ flush = ngx_array_push(flushes);
+ if (flush == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *flush = op->data; /* variable index */
+ }
+
+ found:
+
+ continue;
+ }
+
+ i++;
+
+ while (i < value[s].len && value[s].data[i] != '$') {
+ i++;
+ }
+
+ len = &value[s].data[i] - data;
+
+ if (len) {
+
+ op->len = len;
+ op->getlen = NULL;
+
+ if (len <= sizeof(uintptr_t)) {
+ op->run = ngx_http_log_copy_short;
+ op->data = 0;
+
+ while (len--) {
+ op->data <<= 8;
+ op->data |= data[len];
+ }
+
+ } else {
+ op->run = ngx_http_log_copy_long;
+
+ p = ngx_pnalloc(cf->pool, len);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memcpy(p, data, len);
+ op->data = (uintptr_t) p;
+ }
+ }
+ }
+ }
+
+ return NGX_CONF_OK;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%s\"", data);
+
+ return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_log_loc_conf_t *llcf = conf;
+
+ time_t inactive, valid;
+ ngx_str_t *value, s;
+ ngx_int_t max, min_uses;
+ ngx_uint_t i;
+
+ if (llcf->open_file_cache != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ max = 0;
+ inactive = 10;
+ valid = 60;
+ min_uses = 1;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
+
+ max = ngx_atoi(value[i].data + 4, value[i].len - 4);
+ if (max == NGX_ERROR) {
+ goto failed;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
+
+ s.len = value[i].len - 9;
+ s.data = value[i].data + 9;
+
+ inactive = ngx_parse_time(&s, 1);
+ if (inactive == (time_t) NGX_ERROR) {
+ goto failed;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "min_uses=", 9) == 0) {
+
+ min_uses = ngx_atoi(value[i].data + 9, value[i].len - 9);
+ if (min_uses == NGX_ERROR) {
+ goto failed;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
+
+ s.len = value[i].len - 6;
+ s.data = value[i].data + 6;
+
+ valid = ngx_parse_time(&s, 1);
+ if (valid == (time_t) NGX_ERROR) {
+ goto failed;
+ }
+
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "off") == 0) {
+
+ llcf->open_file_cache = NULL;
+
+ continue;
+ }
+
+ failed:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid \"open_log_file_cache\" parameter \"%V\"",
+ &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (llcf->open_file_cache == NULL) {
+ return NGX_CONF_OK;
+ }
+
+ if (max == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"open_log_file_cache\" must have \"max\" parameter");
+ return NGX_CONF_ERROR;
+ }
+
+ llcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);
+
+ if (llcf->open_file_cache) {
+
+ llcf->open_file_cache_valid = valid;
+ llcf->open_file_cache_min_uses = min_uses;
+
+ return NGX_CONF_OK;
+ }
+
+ return NGX_CONF_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_log_init(ngx_conf_t *cf)
+{
+ ngx_str_t *value;
+ ngx_array_t a;
+ ngx_http_handler_pt *h;
+ ngx_http_log_fmt_t *fmt;
+ ngx_http_log_main_conf_t *lmcf;
+ ngx_http_core_main_conf_t *cmcf;
+
+ lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);
+
+ if (lmcf->combined_used) {
+ if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ value = ngx_array_push(&a);
+ if (value == NULL) {
+ return NGX_ERROR;
+ }
+
+ *value = ngx_http_combined_fmt;
+ fmt = lmcf->formats.elts;
+
+ if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0)
+ != NGX_CONF_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_log_handler;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_map_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_map_module.c
new file mode 100644
index 00000000000..3245e04132c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_map_module.c
@@ -0,0 +1,567 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_uint_t hash_max_size;
+ ngx_uint_t hash_bucket_size;
+} ngx_http_map_conf_t;
+
+
+typedef struct {
+ ngx_hash_keys_arrays_t keys;
+
+ ngx_array_t *values_hash;
+ ngx_array_t var_values;
+#if (NGX_PCRE)
+ ngx_array_t regexes;
+#endif
+
+ ngx_http_variable_value_t *default_value;
+ ngx_conf_t *cf;
+ ngx_uint_t hostnames; /* unsigned hostnames:1 */
+} ngx_http_map_conf_ctx_t;
+
+
+typedef struct {
+ ngx_http_map_t map;
+ ngx_http_complex_value_t value;
+ ngx_http_variable_value_t *default_value;
+ ngx_uint_t hostnames; /* unsigned hostnames:1 */
+} ngx_http_map_ctx_t;
+
+
+static int ngx_libc_cdecl ngx_http_map_cmp_dns_wildcards(const void *one,
+ const void *two);
+static void *ngx_http_map_create_conf(ngx_conf_t *cf);
+static char *ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
+
+
+static ngx_command_t ngx_http_map_commands[] = {
+
+ { ngx_string("map"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
+ ngx_http_map_block,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("map_hash_max_size"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ offsetof(ngx_http_map_conf_t, hash_max_size),
+ NULL },
+
+ { ngx_string("map_hash_bucket_size"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ offsetof(ngx_http_map_conf_t, hash_bucket_size),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_map_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ ngx_http_map_create_conf, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_map_module = {
+ NGX_MODULE_V1,
+ &ngx_http_map_module_ctx, /* module context */
+ ngx_http_map_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_http_map_ctx_t *map = (ngx_http_map_ctx_t *) data;
+
+ ngx_str_t val;
+ ngx_http_variable_value_t *value;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http map started");
+
+ if (ngx_http_complex_value(r, &map->value, &val) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') {
+ val.len--;
+ }
+
+ value = ngx_http_map_find(r, &map->map, &val);
+
+ if (value == NULL) {
+ value = map->default_value;
+ }
+
+ if (!value->valid) {
+ value = ngx_http_get_flushed_variable(r, (uintptr_t) value->data);
+
+ if (value == NULL || value->not_found) {
+ value = &ngx_http_variable_null_value;
+ }
+ }
+
+ *v = *value;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http map: \"%v\" \"%v\"", &val, v);
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_map_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_map_conf_t *mcf;
+
+ mcf = ngx_palloc(cf->pool, sizeof(ngx_http_map_conf_t));
+ if (mcf == NULL) {
+ return NULL;
+ }
+
+ mcf->hash_max_size = NGX_CONF_UNSET_UINT;
+ mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;
+
+ return mcf;
+}
+
+
+static char *
+ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_map_conf_t *mcf = conf;
+
+ char *rv;
+ ngx_str_t *value, name;
+ ngx_conf_t save;
+ ngx_pool_t *pool;
+ ngx_hash_init_t hash;
+ ngx_http_map_ctx_t *map;
+ ngx_http_variable_t *var;
+ ngx_http_map_conf_ctx_t ctx;
+ ngx_http_compile_complex_value_t ccv;
+
+ if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {
+ mcf->hash_max_size = 2048;
+ }
+
+ if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {
+ mcf->hash_bucket_size = ngx_cacheline_size;
+
+ } else {
+ mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,
+ ngx_cacheline_size);
+ }
+
+ map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t));
+ if (map == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &map->value;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ name = value[2];
+
+ if (name.data[0] != '$') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"%V\"", &name);
+ return NGX_CONF_ERROR;
+ }
+
+ name.len--;
+ name.data++;
+
+ var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
+ if (var == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ var->get_handler = ngx_http_map_variable;
+ var->data = (uintptr_t) map;
+
+ pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
+ if (pool == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx.keys.pool = cf->pool;
+ ctx.keys.temp_pool = pool;
+
+ if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) {
+ ngx_destroy_pool(pool);
+ return NGX_CONF_ERROR;
+ }
+
+ ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize);
+ if (ctx.values_hash == NULL) {
+ ngx_destroy_pool(pool);
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_array_init(&ctx.var_values, cf->pool, 2,
+ sizeof(ngx_http_variable_value_t))
+ != NGX_OK)
+ {
+ ngx_destroy_pool(pool);
+ return NGX_CONF_ERROR;
+ }
+
+#if (NGX_PCRE)
+ if (ngx_array_init(&ctx.regexes, cf->pool, 2, sizeof(ngx_http_map_regex_t))
+ != NGX_OK)
+ {
+ ngx_destroy_pool(pool);
+ return NGX_CONF_ERROR;
+ }
+#endif
+
+ ctx.default_value = NULL;
+ ctx.cf = &save;
+ ctx.hostnames = 0;
+
+ save = *cf;
+ cf->pool = pool;
+ cf->ctx = &ctx;
+ cf->handler = ngx_http_map;
+ cf->handler_conf = conf;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = save;
+
+ if (rv != NGX_CONF_OK) {
+ ngx_destroy_pool(pool);
+ return rv;
+ }
+
+ map->default_value = ctx.default_value ? ctx.default_value:
+ &ngx_http_variable_null_value;
+
+ map->hostnames = ctx.hostnames;
+
+ hash.key = ngx_hash_key_lc;
+ hash.max_size = mcf->hash_max_size;
+ hash.bucket_size = mcf->hash_bucket_size;
+ hash.name = "map_hash";
+ hash.pool = cf->pool;
+
+ if (ctx.keys.keys.nelts) {
+ hash.hash = &map->map.hash.hash;
+ hash.temp_pool = NULL;
+
+ if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)
+ != NGX_OK)
+ {
+ ngx_destroy_pool(pool);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (ctx.keys.dns_wc_head.nelts) {
+
+ ngx_qsort(ctx.keys.dns_wc_head.elts,
+ (size_t) ctx.keys.dns_wc_head.nelts,
+ sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);
+
+ hash.hash = NULL;
+ hash.temp_pool = pool;
+
+ if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,
+ ctx.keys.dns_wc_head.nelts)
+ != NGX_OK)
+ {
+ ngx_destroy_pool(pool);
+ return NGX_CONF_ERROR;
+ }
+
+ map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
+ }
+
+ if (ctx.keys.dns_wc_tail.nelts) {
+
+ ngx_qsort(ctx.keys.dns_wc_tail.elts,
+ (size_t) ctx.keys.dns_wc_tail.nelts,
+ sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);
+
+ hash.hash = NULL;
+ hash.temp_pool = pool;
+
+ if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,
+ ctx.keys.dns_wc_tail.nelts)
+ != NGX_OK)
+ {
+ ngx_destroy_pool(pool);
+ return NGX_CONF_ERROR;
+ }
+
+ map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
+ }
+
+#if (NGX_PCRE)
+
+ if (ctx.regexes.nelts) {
+ map->map.regex = ctx.regexes.elts;
+ map->map.nregex = ctx.regexes.nelts;
+ }
+
+#endif
+
+ ngx_destroy_pool(pool);
+
+ return rv;
+}
+
+
+static int ngx_libc_cdecl
+ngx_http_map_cmp_dns_wildcards(const void *one, const void *two)
+{
+ ngx_hash_key_t *first, *second;
+
+ first = (ngx_hash_key_t *) one;
+ second = (ngx_hash_key_t *) two;
+
+ return ngx_dns_strcmp(first->key.data, second->key.data);
+}
+
+
+static char *
+ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
+{
+ ngx_int_t rc, index;
+ ngx_str_t *value, name;
+ ngx_uint_t i, key;
+ ngx_http_map_conf_ctx_t *ctx;
+ ngx_http_variable_value_t *var, **vp;
+
+ ctx = cf->ctx;
+
+ value = cf->args->elts;
+
+ if (cf->args->nelts == 1
+ && ngx_strcmp(value[0].data, "hostnames") == 0)
+ {
+ ctx->hostnames = 1;
+ return NGX_CONF_OK;
+
+ } else if (cf->args->nelts != 2) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid number of the map parameters");
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_strcmp(value[0].data, "include") == 0) {
+ return ngx_conf_include(cf, dummy, conf);
+ }
+
+ if (value[1].data[0] == '$') {
+ name = value[1];
+ name.len--;
+ name.data++;
+
+ index = ngx_http_get_variable_index(ctx->cf, &name);
+ if (index == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ var = ctx->var_values.elts;
+
+ for (i = 0; i < ctx->var_values.nelts; i++) {
+ if (index == (intptr_t) var[i].data) {
+ var = &var[i];
+ goto found;
+ }
+ }
+
+ var = ngx_array_push(&ctx->var_values);
+ if (var == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ var->valid = 0;
+ var->no_cacheable = 0;
+ var->not_found = 0;
+ var->len = 0;
+ var->data = (u_char *) (intptr_t) index;
+
+ goto found;
+ }
+
+ key = 0;
+
+ for (i = 0; i < value[1].len; i++) {
+ key = ngx_hash(key, value[1].data[i]);
+ }
+
+ key %= ctx->keys.hsize;
+
+ vp = ctx->values_hash[key].elts;
+
+ if (vp) {
+ for (i = 0; i < ctx->values_hash[key].nelts; i++) {
+ if (value[1].len != (size_t) vp[i]->len) {
+ continue;
+ }
+
+ if (ngx_strncmp(value[1].data, vp[i]->data, value[1].len) == 0) {
+ var = vp[i];
+ goto found;
+ }
+ }
+
+ } else {
+ if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4,
+ sizeof(ngx_http_variable_value_t *))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ var = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_variable_value_t));
+ if (var == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ var->len = value[1].len;
+ var->data = ngx_pstrdup(ctx->keys.pool, &value[1]);
+ if (var->data == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ var->valid = 1;
+ var->no_cacheable = 0;
+ var->not_found = 0;
+
+ vp = ngx_array_push(&ctx->values_hash[key]);
+ if (vp == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *vp = var;
+
+found:
+
+ if (ngx_strcmp(value[0].data, "default") == 0) {
+
+ if (ctx->default_value) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate default map parameter");
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->default_value = var;
+
+ return NGX_CONF_OK;
+ }
+
+#if (NGX_PCRE)
+
+ if (value[0].len && value[0].data[0] == '~') {
+ ngx_regex_compile_t rc;
+ ngx_http_map_regex_t *regex;
+ u_char errstr[NGX_MAX_CONF_ERRSTR];
+
+ regex = ngx_array_push(&ctx->regexes);
+ if (regex == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value[0].len--;
+ value[0].data++;
+
+ ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+ if (value[0].data[0] == '*') {
+ value[0].len--;
+ value[0].data++;
+ rc.options = NGX_REGEX_CASELESS;
+ }
+
+ rc.pattern = value[0];
+ rc.err.len = NGX_MAX_CONF_ERRSTR;
+ rc.err.data = errstr;
+
+ regex->regex = ngx_http_regex_compile(ctx->cf, &rc);
+ if (regex->regex == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ regex->value = var;
+
+ return NGX_CONF_OK;
+ }
+
+#endif
+
+ if (value[0].len && value[0].data[0] == '\\') {
+ value[0].len--;
+ value[0].data++;
+ }
+
+ rc = ngx_hash_add_key(&ctx->keys, &value[0], var,
+ (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);
+
+ if (rc == NGX_OK) {
+ return NGX_CONF_OK;
+ }
+
+ if (rc == NGX_DECLINED) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid hostname or wildcard \"%V\"", &value[0]);
+ }
+
+ if (rc == NGX_BUSY) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "conflicting parameter \"%V\"", &value[0]);
+ }
+
+ return NGX_CONF_ERROR;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_memcached_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_memcached_module.c
new file mode 100644
index 00000000000..bda038da451
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_memcached_module.c
@@ -0,0 +1,700 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_http_upstream_conf_t upstream;
+ ngx_int_t index;
+ ngx_uint_t gzip_flag;
+} ngx_http_memcached_loc_conf_t;
+
+
+typedef struct {
+ size_t rest;
+ ngx_http_request_t *request;
+ ngx_str_t key;
+} ngx_http_memcached_ctx_t;
+
+
+static ngx_int_t ngx_http_memcached_create_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_memcached_reinit_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_memcached_process_header(ngx_http_request_t *r);
+static ngx_int_t ngx_http_memcached_filter_init(void *data);
+static ngx_int_t ngx_http_memcached_filter(void *data, ssize_t bytes);
+static void ngx_http_memcached_abort_request(ngx_http_request_t *r);
+static void ngx_http_memcached_finalize_request(ngx_http_request_t *r,
+ ngx_int_t rc);
+
+static void *ngx_http_memcached_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+
+static char *ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_conf_bitmask_t ngx_http_memcached_next_upstream_masks[] = {
+ { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
+ { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
+ { ngx_string("invalid_response"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
+ { ngx_string("not_found"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
+ { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_command_t ngx_http_memcached_commands[] = {
+
+ { ngx_string("memcached_pass"),
+ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
+ ngx_http_memcached_pass,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("memcached_bind"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_upstream_bind_set_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_memcached_loc_conf_t, upstream.local),
+ NULL },
+
+ { ngx_string("memcached_connect_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_memcached_loc_conf_t, upstream.connect_timeout),
+ NULL },
+
+ { ngx_string("memcached_send_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_memcached_loc_conf_t, upstream.send_timeout),
+ NULL },
+
+ { ngx_string("memcached_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_memcached_loc_conf_t, upstream.buffer_size),
+ NULL },
+
+ { ngx_string("memcached_read_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_memcached_loc_conf_t, upstream.read_timeout),
+ NULL },
+
+ { ngx_string("memcached_next_upstream"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream),
+ &ngx_http_memcached_next_upstream_masks },
+
+ { ngx_string("memcached_gzip_flag"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_memcached_loc_conf_t, gzip_flag),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_memcached_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_memcached_create_loc_conf, /* create location configuration */
+ ngx_http_memcached_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_memcached_module = {
+ NGX_MODULE_V1,
+ &ngx_http_memcached_module_ctx, /* module context */
+ ngx_http_memcached_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t ngx_http_memcached_key = ngx_string("memcached_key");
+
+
+#define NGX_HTTP_MEMCACHED_END (sizeof(ngx_http_memcached_end) - 1)
+static u_char ngx_http_memcached_end[] = CRLF "END" CRLF;
+
+
+static ngx_int_t
+ngx_http_memcached_handler(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_http_upstream_t *u;
+ ngx_http_memcached_ctx_t *ctx;
+ ngx_http_memcached_loc_conf_t *mlcf;
+
+ if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
+ return NGX_HTTP_NOT_ALLOWED;
+ }
+
+ rc = ngx_http_discard_request_body(r);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ if (ngx_http_set_content_type(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_http_upstream_create(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ u = r->upstream;
+
+ ngx_str_set(&u->schema, "memcached://");
+ u->output.tag = (ngx_buf_tag_t) &ngx_http_memcached_module;
+
+ mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
+
+ u->conf = &mlcf->upstream;
+
+ u->create_request = ngx_http_memcached_create_request;
+ u->reinit_request = ngx_http_memcached_reinit_request;
+ u->process_header = ngx_http_memcached_process_header;
+ u->abort_request = ngx_http_memcached_abort_request;
+ u->finalize_request = ngx_http_memcached_finalize_request;
+
+ ctx = ngx_palloc(r->pool, sizeof(ngx_http_memcached_ctx_t));
+ if (ctx == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ctx->request = r;
+
+ ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);
+
+ u->input_filter_init = ngx_http_memcached_filter_init;
+ u->input_filter = ngx_http_memcached_filter;
+ u->input_filter_ctx = ctx;
+
+ r->main->count++;
+
+ ngx_http_upstream_init(r);
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_http_memcached_create_request(ngx_http_request_t *r)
+{
+ size_t len;
+ uintptr_t escape;
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+ ngx_http_memcached_ctx_t *ctx;
+ ngx_http_variable_value_t *vv;
+ ngx_http_memcached_loc_conf_t *mlcf;
+
+ mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
+
+ vv = ngx_http_get_indexed_variable(r, mlcf->index);
+
+ if (vv == NULL || vv->not_found || vv->len == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "the \"$memcached_key\" variable is not set");
+ return NGX_ERROR;
+ }
+
+ escape = 2 * ngx_escape_uri(NULL, vv->data, vv->len, NGX_ESCAPE_MEMCACHED);
+
+ len = sizeof("get ") - 1 + vv->len + escape + sizeof(CRLF) - 1;
+
+ b = ngx_create_temp_buf(r->pool, len);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ cl->next = NULL;
+
+ r->upstream->request_bufs = cl;
+
+ *b->last++ = 'g'; *b->last++ = 'e'; *b->last++ = 't'; *b->last++ = ' ';
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
+
+ ctx->key.data = b->last;
+
+ if (escape == 0) {
+ b->last = ngx_copy(b->last, vv->data, vv->len);
+
+ } else {
+ b->last = (u_char *) ngx_escape_uri(b->last, vv->data, vv->len,
+ NGX_ESCAPE_MEMCACHED);
+ }
+
+ ctx->key.len = b->last - ctx->key.data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http memcached request: \"%V\"", &ctx->key);
+
+ *b->last++ = CR; *b->last++ = LF;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_memcached_reinit_request(ngx_http_request_t *r)
+{
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_memcached_process_header(ngx_http_request_t *r)
+{
+ u_char *p, *start;
+ ngx_str_t line;
+ ngx_uint_t flags;
+ ngx_table_elt_t *h;
+ ngx_http_upstream_t *u;
+ ngx_http_memcached_ctx_t *ctx;
+ ngx_http_memcached_loc_conf_t *mlcf;
+
+ u = r->upstream;
+
+ for (p = u->buffer.pos; p < u->buffer.last; p++) {
+ if (*p == LF) {
+ goto found;
+ }
+ }
+
+ return NGX_AGAIN;
+
+found:
+
+ line.data = u->buffer.pos;
+ line.len = p - u->buffer.pos;
+
+ if (line.len == 0 || *(p - 1) != CR) {
+ goto no_valid;
+ }
+
+ *p = '\0';
+ line.len--;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "memcached: \"%V\"", &line);
+
+ p = u->buffer.pos;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
+ mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
+
+ if (ngx_strncmp(p, "VALUE ", sizeof("VALUE ") - 1) == 0) {
+
+ p += sizeof("VALUE ") - 1;
+
+ if (ngx_strncmp(p, ctx->key.data, ctx->key.len) != 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "memcached sent invalid key in response \"%V\" "
+ "for key \"%V\"",
+ &line, &ctx->key);
+
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+
+ p += ctx->key.len;
+
+ if (*p++ != ' ') {
+ goto no_valid;
+ }
+
+ /* flags */
+
+ start = p;
+
+ while (*p) {
+ if (*p++ == ' ') {
+ if (mlcf->gzip_flag) {
+ goto flags;
+ } else {
+ goto length;
+ }
+ }
+ }
+
+ goto no_valid;
+
+ flags:
+
+ flags = ngx_atoi(start, p - start - 1);
+
+ if (flags == (ngx_uint_t) NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "memcached sent invalid flags in response \"%V\" "
+ "for key \"%V\"",
+ &line, &ctx->key);
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+
+ if (flags & mlcf->gzip_flag) {
+ h = ngx_list_push(&r->headers_out.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = 1;
+ ngx_str_set(&h->key, "Content-Encoding");
+ ngx_str_set(&h->value, "gzip");
+ r->headers_out.content_encoding = h;
+ }
+
+ length:
+
+ start = p;
+ p = line.data + line.len;
+
+ u->headers_in.content_length_n = ngx_atoof(start, p - start);
+ if (u->headers_in.content_length_n == -1) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "memcached sent invalid length in response \"%V\" "
+ "for key \"%V\"",
+ &line, &ctx->key);
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+
+ u->headers_in.status_n = 200;
+ u->state->status = 200;
+ u->buffer.pos = p + sizeof(CRLF) - 1;
+
+ return NGX_OK;
+ }
+
+ if (ngx_strcmp(p, "END\x0d") == 0) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "key: \"%V\" was not found by memcached", &ctx->key);
+
+ u->headers_in.content_length_n = 0;
+ u->headers_in.status_n = 404;
+ u->state->status = 404;
+ u->buffer.pos = p + sizeof("END" CRLF) - 1;
+ u->keepalive = 1;
+
+ return NGX_OK;
+ }
+
+no_valid:
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "memcached sent invalid response: \"%V\"", &line);
+
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+}
+
+
+static ngx_int_t
+ngx_http_memcached_filter_init(void *data)
+{
+ ngx_http_memcached_ctx_t *ctx = data;
+
+ ngx_http_upstream_t *u;
+
+ u = ctx->request->upstream;
+
+ if (u->headers_in.status_n != 404) {
+ u->length = u->headers_in.content_length_n + NGX_HTTP_MEMCACHED_END;
+ ctx->rest = NGX_HTTP_MEMCACHED_END;
+
+ } else {
+ u->length = 0;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_memcached_filter(void *data, ssize_t bytes)
+{
+ ngx_http_memcached_ctx_t *ctx = data;
+
+ u_char *last;
+ ngx_buf_t *b;
+ ngx_chain_t *cl, **ll;
+ ngx_http_upstream_t *u;
+
+ u = ctx->request->upstream;
+ b = &u->buffer;
+
+ if (u->length == (ssize_t) ctx->rest) {
+
+ if (ngx_strncmp(b->last,
+ ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest,
+ bytes)
+ != 0)
+ {
+ ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
+ "memcached sent invalid trailer");
+
+ u->length = 0;
+ ctx->rest = 0;
+
+ return NGX_OK;
+ }
+
+ u->length -= bytes;
+ ctx->rest -= bytes;
+
+ if (u->length == 0) {
+ u->keepalive = 1;
+ }
+
+ return NGX_OK;
+ }
+
+ for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
+ ll = &cl->next;
+ }
+
+ cl = ngx_chain_get_free_buf(ctx->request->pool, &u->free_bufs);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf->flush = 1;
+ cl->buf->memory = 1;
+
+ *ll = cl;
+
+ last = b->last;
+ cl->buf->pos = last;
+ b->last += bytes;
+ cl->buf->last = b->last;
+ cl->buf->tag = u->output.tag;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,
+ "memcached filter bytes:%z size:%z length:%z rest:%z",
+ bytes, b->last - b->pos, u->length, ctx->rest);
+
+ if (bytes <= (ssize_t) (u->length - NGX_HTTP_MEMCACHED_END)) {
+ u->length -= bytes;
+ return NGX_OK;
+ }
+
+ last += (size_t) (u->length - NGX_HTTP_MEMCACHED_END);
+
+ if (ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) {
+ ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
+ "memcached sent invalid trailer");
+
+ b->last = last;
+ cl->buf->last = last;
+ u->length = 0;
+ ctx->rest = 0;
+
+ return NGX_OK;
+ }
+
+ ctx->rest -= b->last - last;
+ b->last = last;
+ cl->buf->last = last;
+ u->length = ctx->rest;
+
+ if (u->length == 0) {
+ u->keepalive = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_memcached_abort_request(ngx_http_request_t *r)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "abort http memcached request");
+ return;
+}
+
+
+static void
+ngx_http_memcached_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "finalize http memcached request");
+ return;
+}
+
+
+static void *
+ngx_http_memcached_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_memcached_loc_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_memcached_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->upstream.bufs.num = 0;
+ * conf->upstream.next_upstream = 0;
+ * conf->upstream.temp_path = NULL;
+ * conf->upstream.uri = { 0, NULL };
+ * conf->upstream.location = NULL;
+ */
+
+ conf->upstream.local = NGX_CONF_UNSET_PTR;
+ conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
+
+ conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
+
+ /* the hardcoded values */
+ conf->upstream.cyclic_temp_file = 0;
+ conf->upstream.buffering = 0;
+ conf->upstream.ignore_client_abort = 0;
+ conf->upstream.send_lowat = 0;
+ conf->upstream.bufs.num = 0;
+ conf->upstream.busy_buffers_size = 0;
+ conf->upstream.max_temp_file_size = 0;
+ conf->upstream.temp_file_write_size = 0;
+ conf->upstream.intercept_errors = 1;
+ conf->upstream.intercept_404 = 1;
+ conf->upstream.pass_request_headers = 0;
+ conf->upstream.pass_request_body = 0;
+
+ conf->index = NGX_CONF_UNSET;
+ conf->gzip_flag = NGX_CONF_UNSET_UINT;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_memcached_loc_conf_t *prev = parent;
+ ngx_http_memcached_loc_conf_t *conf = child;
+
+ ngx_conf_merge_ptr_value(conf->upstream.local,
+ prev->upstream.local, NULL);
+
+ ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
+ prev->upstream.connect_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->upstream.send_timeout,
+ prev->upstream.send_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->upstream.read_timeout,
+ prev->upstream.read_timeout, 60000);
+
+ ngx_conf_merge_size_value(conf->upstream.buffer_size,
+ prev->upstream.buffer_size,
+ (size_t) ngx_pagesize);
+
+ ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
+ prev->upstream.next_upstream,
+ (NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_ERROR
+ |NGX_HTTP_UPSTREAM_FT_TIMEOUT));
+
+ if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
+ conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_OFF;
+ }
+
+ if (conf->upstream.upstream == NULL) {
+ conf->upstream.upstream = prev->upstream.upstream;
+ }
+
+ if (conf->index == NGX_CONF_UNSET) {
+ conf->index = prev->index;
+ }
+
+ ngx_conf_merge_uint_value(conf->gzip_flag, prev->gzip_flag, 0);
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_memcached_loc_conf_t *mlcf = conf;
+
+ ngx_str_t *value;
+ ngx_url_t u;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (mlcf->upstream.upstream) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = value[1];
+ u.no_resolve = 1;
+
+ mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
+ if (mlcf->upstream.upstream == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+
+ clcf->handler = ngx_http_memcached_handler;
+
+ if (clcf->name.data[clcf->name.len - 1] == '/') {
+ clcf->auto_redirect = 1;
+ }
+
+ mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key);
+
+ if (mlcf->index == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_mp4_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_mp4_module.c
new file mode 100644
index 00000000000..8f439ba9239
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_mp4_module.c
@@ -0,0 +1,3500 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_MP4_TRAK_ATOM 0
+#define NGX_HTTP_MP4_TKHD_ATOM 1
+#define NGX_HTTP_MP4_MDIA_ATOM 2
+#define NGX_HTTP_MP4_MDHD_ATOM 3
+#define NGX_HTTP_MP4_HDLR_ATOM 4
+#define NGX_HTTP_MP4_MINF_ATOM 5
+#define NGX_HTTP_MP4_VMHD_ATOM 6
+#define NGX_HTTP_MP4_SMHD_ATOM 7
+#define NGX_HTTP_MP4_DINF_ATOM 8
+#define NGX_HTTP_MP4_STBL_ATOM 9
+#define NGX_HTTP_MP4_STSD_ATOM 10
+#define NGX_HTTP_MP4_STTS_ATOM 11
+#define NGX_HTTP_MP4_STTS_DATA 12
+#define NGX_HTTP_MP4_STSS_ATOM 13
+#define NGX_HTTP_MP4_STSS_DATA 14
+#define NGX_HTTP_MP4_CTTS_ATOM 15
+#define NGX_HTTP_MP4_CTTS_DATA 16
+#define NGX_HTTP_MP4_STSC_ATOM 17
+#define NGX_HTTP_MP4_STSC_START 18
+#define NGX_HTTP_MP4_STSC_DATA 19
+#define NGX_HTTP_MP4_STSC_END 20
+#define NGX_HTTP_MP4_STSZ_ATOM 21
+#define NGX_HTTP_MP4_STSZ_DATA 22
+#define NGX_HTTP_MP4_STCO_ATOM 23
+#define NGX_HTTP_MP4_STCO_DATA 24
+#define NGX_HTTP_MP4_CO64_ATOM 25
+#define NGX_HTTP_MP4_CO64_DATA 26
+
+#define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA
+
+
+typedef struct {
+ size_t buffer_size;
+ size_t max_buffer_size;
+} ngx_http_mp4_conf_t;
+
+
+typedef struct {
+ u_char chunk[4];
+ u_char samples[4];
+ u_char id[4];
+} ngx_mp4_stsc_entry_t;
+
+
+typedef struct {
+ uint32_t timescale;
+ uint32_t time_to_sample_entries;
+ uint32_t sample_to_chunk_entries;
+ uint32_t sync_samples_entries;
+ uint32_t composition_offset_entries;
+ uint32_t sample_sizes_entries;
+ uint32_t chunks;
+
+ ngx_uint_t start_sample;
+ ngx_uint_t end_sample;
+ ngx_uint_t start_chunk;
+ ngx_uint_t end_chunk;
+ ngx_uint_t start_chunk_samples;
+ ngx_uint_t end_chunk_samples;
+ uint64_t start_chunk_samples_size;
+ uint64_t end_chunk_samples_size;
+ off_t start_offset;
+ off_t end_offset;
+
+ size_t tkhd_size;
+ size_t mdhd_size;
+ size_t hdlr_size;
+ size_t vmhd_size;
+ size_t smhd_size;
+ size_t dinf_size;
+ size_t size;
+
+ ngx_chain_t out[NGX_HTTP_MP4_LAST_ATOM + 1];
+
+ ngx_buf_t trak_atom_buf;
+ ngx_buf_t tkhd_atom_buf;
+ ngx_buf_t mdia_atom_buf;
+ ngx_buf_t mdhd_atom_buf;
+ ngx_buf_t hdlr_atom_buf;
+ ngx_buf_t minf_atom_buf;
+ ngx_buf_t vmhd_atom_buf;
+ ngx_buf_t smhd_atom_buf;
+ ngx_buf_t dinf_atom_buf;
+ ngx_buf_t stbl_atom_buf;
+ ngx_buf_t stsd_atom_buf;
+ ngx_buf_t stts_atom_buf;
+ ngx_buf_t stts_data_buf;
+ ngx_buf_t stss_atom_buf;
+ ngx_buf_t stss_data_buf;
+ ngx_buf_t ctts_atom_buf;
+ ngx_buf_t ctts_data_buf;
+ ngx_buf_t stsc_atom_buf;
+ ngx_buf_t stsc_start_chunk_buf;
+ ngx_buf_t stsc_end_chunk_buf;
+ ngx_buf_t stsc_data_buf;
+ ngx_buf_t stsz_atom_buf;
+ ngx_buf_t stsz_data_buf;
+ ngx_buf_t stco_atom_buf;
+ ngx_buf_t stco_data_buf;
+ ngx_buf_t co64_atom_buf;
+ ngx_buf_t co64_data_buf;
+
+ ngx_mp4_stsc_entry_t stsc_start_chunk_entry;
+ ngx_mp4_stsc_entry_t stsc_end_chunk_entry;
+} ngx_http_mp4_trak_t;
+
+
+typedef struct {
+ ngx_file_t file;
+
+ u_char *buffer;
+ u_char *buffer_start;
+ u_char *buffer_pos;
+ u_char *buffer_end;
+ size_t buffer_size;
+
+ off_t offset;
+ off_t end;
+ off_t content_length;
+ ngx_uint_t start;
+ ngx_uint_t length;
+ uint32_t timescale;
+ ngx_http_request_t *request;
+ ngx_array_t trak;
+ ngx_http_mp4_trak_t traks[2];
+
+ size_t ftyp_size;
+ size_t moov_size;
+
+ ngx_chain_t *out;
+ ngx_chain_t ftyp_atom;
+ ngx_chain_t moov_atom;
+ ngx_chain_t mvhd_atom;
+ ngx_chain_t mdat_atom;
+ ngx_chain_t mdat_data;
+
+ ngx_buf_t ftyp_atom_buf;
+ ngx_buf_t moov_atom_buf;
+ ngx_buf_t mvhd_atom_buf;
+ ngx_buf_t mdat_atom_buf;
+ ngx_buf_t mdat_data_buf;
+
+ u_char moov_atom_header[8];
+ u_char mdat_atom_header[16];
+} ngx_http_mp4_file_t;
+
+
+typedef struct {
+ char *name;
+ ngx_int_t (*handler)(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+} ngx_http_mp4_atom_handler_t;
+
+
+#define ngx_mp4_atom_header(mp4) (mp4->buffer_pos - 8)
+#define ngx_mp4_atom_data(mp4) mp4->buffer_pos
+#define ngx_mp4_atom_data_size(t) (uint64_t) (sizeof(t) - 8)
+
+
+#define ngx_mp4_atom_next(mp4, n) \
+ mp4->buffer_pos += (size_t) n; \
+ mp4->offset += n
+
+
+#define ngx_mp4_set_atom_name(p, n1, n2, n3, n4) \
+ ((u_char *) (p))[4] = n1; \
+ ((u_char *) (p))[5] = n2; \
+ ((u_char *) (p))[6] = n3; \
+ ((u_char *) (p))[7] = n4
+
+#define ngx_mp4_get_32value(p) \
+ ( ((uint32_t) ((u_char *) (p))[0] << 24) \
+ + ( ((u_char *) (p))[1] << 16) \
+ + ( ((u_char *) (p))[2] << 8) \
+ + ( ((u_char *) (p))[3]) )
+
+#define ngx_mp4_set_32value(p, n) \
+ ((u_char *) (p))[0] = (u_char) ((n) >> 24); \
+ ((u_char *) (p))[1] = (u_char) ((n) >> 16); \
+ ((u_char *) (p))[2] = (u_char) ((n) >> 8); \
+ ((u_char *) (p))[3] = (u_char) (n)
+
+#define ngx_mp4_get_64value(p) \
+ ( ((uint64_t) ((u_char *) (p))[0] << 56) \
+ + ((uint64_t) ((u_char *) (p))[1] << 48) \
+ + ((uint64_t) ((u_char *) (p))[2] << 40) \
+ + ((uint64_t) ((u_char *) (p))[3] << 32) \
+ + ((uint64_t) ((u_char *) (p))[4] << 24) \
+ + ( ((u_char *) (p))[5] << 16) \
+ + ( ((u_char *) (p))[6] << 8) \
+ + ( ((u_char *) (p))[7]) )
+
+#define ngx_mp4_set_64value(p, n) \
+ ((u_char *) (p))[0] = (u_char) ((uint64_t) (n) >> 56); \
+ ((u_char *) (p))[1] = (u_char) ((uint64_t) (n) >> 48); \
+ ((u_char *) (p))[2] = (u_char) ((uint64_t) (n) >> 40); \
+ ((u_char *) (p))[3] = (u_char) ((uint64_t) (n) >> 32); \
+ ((u_char *) (p))[4] = (u_char) ( (n) >> 24); \
+ ((u_char *) (p))[5] = (u_char) ( (n) >> 16); \
+ ((u_char *) (p))[6] = (u_char) ( (n) >> 8); \
+ ((u_char *) (p))[7] = (u_char) (n)
+
+#define ngx_mp4_last_trak(mp4) \
+ &((ngx_http_mp4_trak_t *) mp4->trak.elts)[mp4->trak.nelts - 1]
+
+
+static ngx_int_t ngx_http_mp4_handler(ngx_http_request_t *r);
+
+static ngx_int_t ngx_http_mp4_process(ngx_http_mp4_file_t *mp4);
+static ngx_int_t ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_read(ngx_http_mp4_file_t *mp4, size_t size);
+static ngx_int_t ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static size_t ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4,
+ off_t start_offset, off_t end_offset);
+static ngx_int_t ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static void ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
+static ngx_int_t ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static void ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
+static ngx_int_t ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static void ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
+static ngx_int_t ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static void ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
+static ngx_int_t ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
+static ngx_int_t ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start);
+static ngx_int_t ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
+static void ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start);
+static ngx_int_t ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static void ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
+static void ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start);
+static ngx_int_t ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
+static ngx_int_t ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start);
+static ngx_int_t ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
+static ngx_int_t ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
+static void ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, int32_t adjustment);
+static ngx_int_t ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4,
+ uint64_t atom_data_size);
+static ngx_int_t ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
+static void ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, off_t adjustment);
+
+static char *ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static void *ngx_http_mp4_create_conf(ngx_conf_t *cf);
+static char *ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child);
+
+
+static ngx_command_t ngx_http_mp4_commands[] = {
+
+ { ngx_string("mp4"),
+ NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
+ ngx_http_mp4,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("mp4_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_mp4_conf_t, buffer_size),
+ NULL },
+
+ { ngx_string("mp4_max_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_mp4_conf_t, max_buffer_size),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_mp4_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_mp4_create_conf, /* create location configuration */
+ ngx_http_mp4_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_mp4_module = {
+ NGX_MODULE_V1,
+ &ngx_http_mp4_module_ctx, /* module context */
+ ngx_http_mp4_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_mp4_atom_handler_t ngx_http_mp4_atoms[] = {
+ { "ftyp", ngx_http_mp4_read_ftyp_atom },
+ { "moov", ngx_http_mp4_read_moov_atom },
+ { "mdat", ngx_http_mp4_read_mdat_atom },
+ { NULL, NULL }
+};
+
+static ngx_http_mp4_atom_handler_t ngx_http_mp4_moov_atoms[] = {
+ { "mvhd", ngx_http_mp4_read_mvhd_atom },
+ { "trak", ngx_http_mp4_read_trak_atom },
+ { "cmov", ngx_http_mp4_read_cmov_atom },
+ { NULL, NULL }
+};
+
+static ngx_http_mp4_atom_handler_t ngx_http_mp4_trak_atoms[] = {
+ { "tkhd", ngx_http_mp4_read_tkhd_atom },
+ { "mdia", ngx_http_mp4_read_mdia_atom },
+ { NULL, NULL }
+};
+
+static ngx_http_mp4_atom_handler_t ngx_http_mp4_mdia_atoms[] = {
+ { "mdhd", ngx_http_mp4_read_mdhd_atom },
+ { "hdlr", ngx_http_mp4_read_hdlr_atom },
+ { "minf", ngx_http_mp4_read_minf_atom },
+ { NULL, NULL }
+};
+
+static ngx_http_mp4_atom_handler_t ngx_http_mp4_minf_atoms[] = {
+ { "vmhd", ngx_http_mp4_read_vmhd_atom },
+ { "smhd", ngx_http_mp4_read_smhd_atom },
+ { "dinf", ngx_http_mp4_read_dinf_atom },
+ { "stbl", ngx_http_mp4_read_stbl_atom },
+ { NULL, NULL }
+};
+
+static ngx_http_mp4_atom_handler_t ngx_http_mp4_stbl_atoms[] = {
+ { "stsd", ngx_http_mp4_read_stsd_atom },
+ { "stts", ngx_http_mp4_read_stts_atom },
+ { "stss", ngx_http_mp4_read_stss_atom },
+ { "ctts", ngx_http_mp4_read_ctts_atom },
+ { "stsc", ngx_http_mp4_read_stsc_atom },
+ { "stsz", ngx_http_mp4_read_stsz_atom },
+ { "stco", ngx_http_mp4_read_stco_atom },
+ { "co64", ngx_http_mp4_read_co64_atom },
+ { NULL, NULL }
+};
+
+
+static ngx_int_t
+ngx_http_mp4_handler(ngx_http_request_t *r)
+{
+ u_char *last;
+ size_t root;
+ ngx_int_t rc, start, end;
+ ngx_uint_t level, length;
+ ngx_str_t path, value;
+ ngx_log_t *log;
+ ngx_buf_t *b;
+ ngx_chain_t out;
+ ngx_http_mp4_file_t *mp4;
+ ngx_open_file_info_t of;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
+ return NGX_HTTP_NOT_ALLOWED;
+ }
+
+ if (r->uri.data[r->uri.len - 1] == '/') {
+ return NGX_DECLINED;
+ }
+
+ rc = ngx_http_discard_request_body(r);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ last = ngx_http_map_uri_to_path(r, &path, &root, 0);
+ if (last == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ log = r->connection->log;
+
+ path.len = last - path.data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
+ "http mp4 filename: \"%V\"", &path);
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+ of.read_ahead = clcf->read_ahead;
+ of.directio = NGX_MAX_OFF_T_VALUE;
+ of.valid = clcf->open_file_cache_valid;
+ of.min_uses = clcf->open_file_cache_min_uses;
+ of.errors = clcf->open_file_cache_errors;
+ of.events = clcf->open_file_cache_events;
+
+ if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+ != NGX_OK)
+ {
+ switch (of.err) {
+
+ case 0:
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+
+ case NGX_ENOENT:
+ case NGX_ENOTDIR:
+ case NGX_ENAMETOOLONG:
+
+ level = NGX_LOG_ERR;
+ rc = NGX_HTTP_NOT_FOUND;
+ break;
+
+ case NGX_EACCES:
+#if (NGX_HAVE_OPENAT)
+ case NGX_EMLINK:
+ case NGX_ELOOP:
+#endif
+
+ level = NGX_LOG_ERR;
+ rc = NGX_HTTP_FORBIDDEN;
+ break;
+
+ default:
+
+ level = NGX_LOG_CRIT;
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ break;
+ }
+
+ if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
+ ngx_log_error(level, log, of.err,
+ "%s \"%s\" failed", of.failed, path.data);
+ }
+
+ return rc;
+ }
+
+ if (!of.is_file) {
+
+ if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", path.data);
+ }
+
+ return NGX_DECLINED;
+ }
+
+ r->root_tested = !r->error_page;
+ r->allow_ranges = 1;
+
+ start = -1;
+ length = 0;
+ r->headers_out.content_length_n = of.size;
+ mp4 = NULL;
+ b = NULL;
+
+ if (r->args.len) {
+
+ if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {
+
+ /*
+ * A Flash player may send start value with a lot of digits
+ * after dot so strtod() is used instead of atofp(). NaNs and
+ * infinities become negative numbers after (int) conversion.
+ */
+
+ ngx_set_errno(0);
+ start = (int) (strtod((char *) value.data, NULL) * 1000);
+
+ if (ngx_errno != 0) {
+ start = -1;
+ }
+ }
+
+ if (ngx_http_arg(r, (u_char *) "end", 3, &value) == NGX_OK) {
+
+ ngx_set_errno(0);
+ end = (int) (strtod((char *) value.data, NULL) * 1000);
+
+ if (ngx_errno != 0) {
+ end = -1;
+ }
+
+ if (end > 0) {
+ if (start < 0) {
+ start = 0;
+ }
+
+ if (end > start) {
+ length = end - start;
+ }
+ }
+ }
+ }
+
+ if (start >= 0) {
+ r->single_range = 1;
+
+ mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t));
+ if (mp4 == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ mp4->file.fd = of.fd;
+ mp4->file.name = path;
+ mp4->file.log = r->connection->log;
+ mp4->end = of.size;
+ mp4->start = (ngx_uint_t) start;
+ mp4->length = length;
+ mp4->request = r;
+
+ switch (ngx_http_mp4_process(mp4)) {
+
+ case NGX_DECLINED:
+ if (mp4->buffer) {
+ ngx_pfree(r->pool, mp4->buffer);
+ }
+
+ ngx_pfree(r->pool, mp4);
+ mp4 = NULL;
+
+ break;
+
+ case NGX_OK:
+ r->headers_out.content_length_n = mp4->content_length;
+ break;
+
+ default: /* NGX_ERROR */
+ if (mp4->buffer) {
+ ngx_pfree(r->pool, mp4->buffer);
+ }
+
+ ngx_pfree(r->pool, mp4);
+
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ log->action = "sending mp4 to client";
+
+ if (clcf->directio <= of.size) {
+
+ /*
+ * DIRECTIO is set on transfer only
+ * to allow kernel to cache "moov" atom
+ */
+
+ if (ngx_directio_on(of.fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_directio_on_n " \"%s\" failed", path.data);
+ }
+
+ of.is_directio = 1;
+
+ if (mp4) {
+ mp4->file.directio = 1;
+ }
+ }
+
+ r->headers_out.status = NGX_HTTP_OK;
+ r->headers_out.last_modified_time = of.mtime;
+
+ if (ngx_http_set_etag(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_http_set_content_type(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (mp4 == NULL) {
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
+ if (b->file == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ return rc;
+ }
+
+ if (mp4) {
+ return ngx_http_output_filter(r, mp4->out);
+ }
+
+ b->file_pos = 0;
+ b->file_last = of.size;
+
+ b->in_file = b->file_last ? 1 : 0;
+ b->last_buf = (r == r->main) ? 1 : 0;
+ b->last_in_chain = 1;
+
+ b->file->fd = of.fd;
+ b->file->name = path;
+ b->file->log = log;
+ b->file->directio = of.is_directio;
+
+ out.buf = b;
+ out.next = NULL;
+
+ return ngx_http_output_filter(r, &out);
+}
+
+
+static ngx_int_t
+ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
+{
+ off_t start_offset, end_offset, adjustment;
+ ngx_int_t rc;
+ ngx_uint_t i, j;
+ ngx_chain_t **prev;
+ ngx_http_mp4_trak_t *trak;
+ ngx_http_mp4_conf_t *conf;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 start:%ui, length:%ui", mp4->start, mp4->length);
+
+ conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);
+
+ mp4->buffer_size = conf->buffer_size;
+
+ rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_atoms, mp4->end);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ if (mp4->trak.nelts == 0) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "no mp4 trak atoms were found in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ if (mp4->mdat_atom.buf == NULL) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "no mp4 mdat atom was found in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ prev = &mp4->out;
+
+ if (mp4->ftyp_atom.buf) {
+ *prev = &mp4->ftyp_atom;
+ prev = &mp4->ftyp_atom.next;
+ }
+
+ *prev = &mp4->moov_atom;
+ prev = &mp4->moov_atom.next;
+
+ if (mp4->mvhd_atom.buf) {
+ mp4->moov_size += mp4->mvhd_atom_buf.last - mp4->mvhd_atom_buf.pos;
+ *prev = &mp4->mvhd_atom;
+ prev = &mp4->mvhd_atom.next;
+ }
+
+ start_offset = mp4->end;
+ end_offset = 0;
+ trak = mp4->trak.elts;
+
+ for (i = 0; i < mp4->trak.nelts; i++) {
+
+ if (ngx_http_mp4_update_stts_atom(mp4, &trak[i]) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_http_mp4_update_stss_atom(mp4, &trak[i]) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_mp4_update_ctts_atom(mp4, &trak[i]);
+
+ if (ngx_http_mp4_update_stsc_atom(mp4, &trak[i]) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_http_mp4_update_stsz_atom(mp4, &trak[i]) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
+ if (ngx_http_mp4_update_co64_atom(mp4, &trak[i]) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ ngx_http_mp4_update_stbl_atom(mp4, &trak[i]);
+ ngx_http_mp4_update_minf_atom(mp4, &trak[i]);
+ trak[i].size += trak[i].mdhd_size;
+ trak[i].size += trak[i].hdlr_size;
+ ngx_http_mp4_update_mdia_atom(mp4, &trak[i]);
+ trak[i].size += trak[i].tkhd_size;
+ ngx_http_mp4_update_trak_atom(mp4, &trak[i]);
+
+ mp4->moov_size += trak[i].size;
+
+ if (start_offset > trak[i].start_offset) {
+ start_offset = trak[i].start_offset;
+ }
+
+ if (end_offset < trak[i].end_offset) {
+ end_offset = trak[i].end_offset;
+ }
+
+ *prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM];
+ prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM].next;
+
+ for (j = 0; j < NGX_HTTP_MP4_LAST_ATOM + 1; j++) {
+ if (trak[i].out[j].buf) {
+ *prev = &trak[i].out[j];
+ prev = &trak[i].out[j].next;
+ }
+ }
+ }
+
+ if (end_offset < start_offset) {
+ end_offset = start_offset;
+ }
+
+ mp4->moov_size += 8;
+
+ ngx_mp4_set_32value(mp4->moov_atom_header, mp4->moov_size);
+ ngx_mp4_set_atom_name(mp4->moov_atom_header, 'm', 'o', 'o', 'v');
+ mp4->content_length += mp4->moov_size;
+
+ *prev = &mp4->mdat_atom;
+
+ if (start_offset > mp4->mdat_data.buf->file_last) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "start time is out mp4 mdat atom in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ adjustment = mp4->ftyp_size + mp4->moov_size
+ + ngx_http_mp4_update_mdat_atom(mp4, start_offset, end_offset)
+ - start_offset;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 adjustment:%O", adjustment);
+
+ for (i = 0; i < mp4->trak.nelts; i++) {
+ if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
+ ngx_http_mp4_adjust_co64_atom(mp4, &trak[i], adjustment);
+ } else {
+ ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment);
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+} ngx_mp4_atom_header_t;
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char size64[8];
+} ngx_mp4_atom_header64_t;
+
+
+static ngx_int_t
+ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size)
+{
+ off_t end;
+ size_t atom_header_size;
+ u_char *atom_header, *atom_name;
+ uint64_t atom_size;
+ ngx_int_t rc;
+ ngx_uint_t n;
+
+ end = mp4->offset + atom_data_size;
+
+ while (mp4->offset < end) {
+
+ if (ngx_http_mp4_read(mp4, sizeof(uint32_t)) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ atom_header = mp4->buffer_pos;
+ atom_size = ngx_mp4_get_32value(atom_header);
+ atom_header_size = sizeof(ngx_mp4_atom_header_t);
+
+ if (atom_size == 0) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 atom end");
+ return NGX_OK;
+ }
+
+ if (atom_size < sizeof(ngx_mp4_atom_header_t)) {
+
+ if (atom_size == 1) {
+
+ if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header64_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ /* 64-bit atom size */
+ atom_header = mp4->buffer_pos;
+ atom_size = ngx_mp4_get_64value(atom_header + 8);
+ atom_header_size = sizeof(ngx_mp4_atom_header64_t);
+
+ } else {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 atom is too small:%uL",
+ mp4->file.name.data, atom_size);
+ return NGX_ERROR;
+ }
+ }
+
+ if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header_t)) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ atom_header = mp4->buffer_pos;
+ atom_name = atom_header + sizeof(uint32_t);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 atom: %*s @%O:%uL",
+ 4, atom_name, mp4->offset, atom_size);
+
+ if (atom_size > (uint64_t) (NGX_MAX_OFF_T_VALUE - mp4->offset)
+ || mp4->offset + (off_t) atom_size > end)
+ {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 atom too large:%uL",
+ mp4->file.name.data, atom_size);
+ return NGX_ERROR;
+ }
+
+ for (n = 0; atom[n].name; n++) {
+
+ if (ngx_strncmp(atom_name, atom[n].name, 4) == 0) {
+
+ ngx_mp4_atom_next(mp4, atom_header_size);
+
+ rc = atom[n].handler(mp4, atom_size - atom_header_size);
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ goto next;
+ }
+ }
+
+ ngx_mp4_atom_next(mp4, atom_size);
+
+ next:
+ continue;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_read(ngx_http_mp4_file_t *mp4, size_t size)
+{
+ ssize_t n;
+
+ if (mp4->buffer_pos + size <= mp4->buffer_end) {
+ return NGX_OK;
+ }
+
+ if (mp4->offset + (off_t) mp4->buffer_size > mp4->end) {
+ mp4->buffer_size = (size_t) (mp4->end - mp4->offset);
+ }
+
+ if (mp4->buffer_size < size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 file truncated", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ if (mp4->buffer == NULL) {
+ mp4->buffer = ngx_palloc(mp4->request->pool, mp4->buffer_size);
+ if (mp4->buffer == NULL) {
+ return NGX_ERROR;
+ }
+
+ mp4->buffer_start = mp4->buffer;
+ }
+
+ n = ngx_read_file(&mp4->file, mp4->buffer_start, mp4->buffer_size,
+ mp4->offset);
+
+ if (n == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if ((size_t) n != mp4->buffer_size) {
+ ngx_log_error(NGX_LOG_CRIT, mp4->file.log, 0,
+ ngx_read_file_n " read only %z of %z from \"%s\"",
+ n, mp4->buffer_size, mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ mp4->buffer_pos = mp4->buffer_start;
+ mp4->buffer_end = mp4->buffer_start + mp4->buffer_size;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *ftyp_atom;
+ size_t atom_size;
+ ngx_buf_t *atom;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ftyp atom");
+
+ if (atom_data_size > 1024
+ || ngx_mp4_atom_data(mp4) + (size_t) atom_data_size > mp4->buffer_end)
+ {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 ftyp atom is too large:%uL",
+ mp4->file.name.data, atom_data_size);
+ return NGX_ERROR;
+ }
+
+ atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
+
+ ftyp_atom = ngx_palloc(mp4->request->pool, atom_size);
+ if (ftyp_atom == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_mp4_set_32value(ftyp_atom, atom_size);
+ ngx_mp4_set_atom_name(ftyp_atom, 'f', 't', 'y', 'p');
+
+ /*
+ * only moov atom content is guaranteed to be in mp4->buffer
+ * during sending response, so ftyp atom content should be copied
+ */
+ ngx_memcpy(ftyp_atom + sizeof(ngx_mp4_atom_header_t),
+ ngx_mp4_atom_data(mp4), (size_t) atom_data_size);
+
+ atom = &mp4->ftyp_atom_buf;
+ atom->temporary = 1;
+ atom->pos = ftyp_atom;
+ atom->last = ftyp_atom + atom_size;
+
+ mp4->ftyp_atom.buf = atom;
+ mp4->ftyp_size = atom_size;
+ mp4->content_length = atom_size;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+/*
+ * Small excess buffer to process atoms after moov atom, mp4->buffer_start
+ * will be set to this buffer part after moov atom processing.
+ */
+#define NGX_HTTP_MP4_MOOV_BUFFER_EXCESS (4 * 1024)
+
+static ngx_int_t
+ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ ngx_int_t rc;
+ ngx_uint_t no_mdat;
+ ngx_buf_t *atom;
+ ngx_http_mp4_conf_t *conf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom");
+
+ no_mdat = (mp4->mdat_atom.buf == NULL);
+
+ if (no_mdat && mp4->start == 0 && mp4->length == 0) {
+ /*
+ * send original file if moov atom resides before
+ * mdat atom and client requests integral file
+ */
+ return NGX_DECLINED;
+ }
+
+ conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);
+
+ if (atom_data_size > mp4->buffer_size) {
+
+ if (atom_data_size > conf->max_buffer_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 moov atom is too large:%uL, "
+ "you may want to increase mp4_max_buffer_size",
+ mp4->file.name.data, atom_data_size);
+ return NGX_ERROR;
+ }
+
+ ngx_pfree(mp4->request->pool, mp4->buffer);
+ mp4->buffer = NULL;
+ mp4->buffer_pos = NULL;
+ mp4->buffer_end = NULL;
+
+ mp4->buffer_size = (size_t) atom_data_size
+ + NGX_HTTP_MP4_MOOV_BUFFER_EXCESS * no_mdat;
+ }
+
+ if (ngx_http_mp4_read(mp4, (size_t) atom_data_size) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ mp4->trak.elts = &mp4->traks;
+ mp4->trak.size = sizeof(ngx_http_mp4_trak_t);
+ mp4->trak.nalloc = 2;
+ mp4->trak.pool = mp4->request->pool;
+
+ atom = &mp4->moov_atom_buf;
+ atom->temporary = 1;
+ atom->pos = mp4->moov_atom_header;
+ atom->last = mp4->moov_atom_header + 8;
+
+ mp4->moov_atom.buf = &mp4->moov_atom_buf;
+
+ rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_moov_atoms, atom_data_size);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom done");
+
+ if (no_mdat) {
+ mp4->buffer_start = mp4->buffer_pos;
+ mp4->buffer_size = NGX_HTTP_MP4_MOOV_BUFFER_EXCESS;
+
+ if (mp4->buffer_start + mp4->buffer_size > mp4->buffer_end) {
+ mp4->buffer = NULL;
+ mp4->buffer_pos = NULL;
+ mp4->buffer_end = NULL;
+ }
+
+ } else {
+ /* skip atoms after moov atom */
+ mp4->offset = mp4->end;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ ngx_buf_t *data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdat atom");
+
+ data = &mp4->mdat_data_buf;
+ data->file = &mp4->file;
+ data->in_file = 1;
+ data->last_buf = 1;
+ data->last_in_chain = 1;
+ data->file_last = mp4->offset + atom_data_size;
+
+ mp4->mdat_atom.buf = &mp4->mdat_atom_buf;
+ mp4->mdat_atom.next = &mp4->mdat_data;
+ mp4->mdat_data.buf = data;
+
+ if (mp4->trak.nelts) {
+ /* skip atoms after mdat atom */
+ mp4->offset = mp4->end;
+
+ } else {
+ ngx_mp4_atom_next(mp4, atom_data_size);
+ }
+
+ return NGX_OK;
+}
+
+
+static size_t
+ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset,
+ off_t end_offset)
+{
+ off_t atom_data_size;
+ u_char *atom_header;
+ uint32_t atom_header_size;
+ uint64_t atom_size;
+ ngx_buf_t *atom;
+
+ atom_data_size = end_offset - start_offset;
+ mp4->mdat_data.buf->file_pos = start_offset;
+ mp4->mdat_data.buf->file_last = end_offset;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mdat new offset @%O:%O", start_offset, atom_data_size);
+
+ atom_header = mp4->mdat_atom_header;
+
+ if ((uint64_t) atom_data_size > (uint64_t) 0xffffffff) {
+ atom_size = 1;
+ atom_header_size = sizeof(ngx_mp4_atom_header64_t);
+ ngx_mp4_set_64value(atom_header + sizeof(ngx_mp4_atom_header_t),
+ sizeof(ngx_mp4_atom_header64_t) + atom_data_size);
+ } else {
+ atom_size = sizeof(ngx_mp4_atom_header_t) + atom_data_size;
+ atom_header_size = sizeof(ngx_mp4_atom_header_t);
+ }
+
+ mp4->content_length += atom_header_size + atom_data_size;
+
+ ngx_mp4_set_32value(atom_header, atom_size);
+ ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'a', 't');
+
+ atom = &mp4->mdat_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_header + atom_header_size;
+
+ return atom_header_size;
+}
+
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char creation_time[4];
+ u_char modification_time[4];
+ u_char timescale[4];
+ u_char duration[4];
+ u_char rate[4];
+ u_char volume[2];
+ u_char reserved[10];
+ u_char matrix[36];
+ u_char preview_time[4];
+ u_char preview_duration[4];
+ u_char poster_time[4];
+ u_char selection_time[4];
+ u_char selection_duration[4];
+ u_char current_time[4];
+ u_char next_track_id[4];
+} ngx_mp4_mvhd_atom_t;
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char creation_time[8];
+ u_char modification_time[8];
+ u_char timescale[4];
+ u_char duration[8];
+ u_char rate[4];
+ u_char volume[2];
+ u_char reserved[10];
+ u_char matrix[36];
+ u_char preview_time[4];
+ u_char preview_duration[4];
+ u_char poster_time[4];
+ u_char selection_time[4];
+ u_char selection_duration[4];
+ u_char current_time[4];
+ u_char next_track_id[4];
+} ngx_mp4_mvhd64_atom_t;
+
+
+static ngx_int_t
+ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header;
+ size_t atom_size;
+ uint32_t timescale;
+ uint64_t duration, start_time, length_time;
+ ngx_buf_t *atom;
+ ngx_mp4_mvhd_atom_t *mvhd_atom;
+ ngx_mp4_mvhd64_atom_t *mvhd64_atom;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mvhd atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ mvhd_atom = (ngx_mp4_mvhd_atom_t *) atom_header;
+ mvhd64_atom = (ngx_mp4_mvhd64_atom_t *) atom_header;
+ ngx_mp4_set_atom_name(atom_header, 'm', 'v', 'h', 'd');
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_mvhd_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 mvhd atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ if (mvhd_atom->version[0] == 0) {
+ /* version 0: 32-bit duration */
+ timescale = ngx_mp4_get_32value(mvhd_atom->timescale);
+ duration = ngx_mp4_get_32value(mvhd_atom->duration);
+
+ } else {
+ /* version 1: 64-bit duration */
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_mvhd64_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 mvhd atom too small",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ timescale = ngx_mp4_get_32value(mvhd64_atom->timescale);
+ duration = ngx_mp4_get_64value(mvhd64_atom->duration);
+ }
+
+ mp4->timescale = timescale;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mvhd timescale:%uD, duration:%uL, time:%.3fs",
+ timescale, duration, (double) duration / timescale);
+
+ start_time = (uint64_t) mp4->start * timescale / 1000;
+
+ if (duration < start_time) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 start time exceeds file duration",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ duration -= start_time;
+
+ if (mp4->length) {
+ length_time = (uint64_t) mp4->length * timescale / 1000;
+
+ if (duration > length_time) {
+ duration = length_time;
+ }
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mvhd new duration:%uL, time:%.3fs",
+ duration, (double) duration / timescale);
+
+ atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
+ ngx_mp4_set_32value(mvhd_atom->size, atom_size);
+
+ if (mvhd_atom->version[0] == 0) {
+ ngx_mp4_set_32value(mvhd_atom->duration, duration);
+
+ } else {
+ ngx_mp4_set_64value(mvhd64_atom->duration, duration);
+ }
+
+ atom = &mp4->mvhd_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_header + atom_size;
+
+ mp4->mvhd_atom.buf = atom;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header, *atom_end;
+ off_t atom_file_end;
+ ngx_int_t rc;
+ ngx_buf_t *atom;
+ ngx_http_mp4_trak_t *trak;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 trak atom");
+
+ trak = ngx_array_push(&mp4->trak);
+ if (trak == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ ngx_mp4_set_atom_name(atom_header, 't', 'r', 'a', 'k');
+
+ atom = &trak->trak_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
+
+ trak->out[NGX_HTTP_MP4_TRAK_ATOM].buf = atom;
+
+ atom_end = mp4->buffer_pos + (size_t) atom_data_size;
+ atom_file_end = mp4->offset + atom_data_size;
+
+ rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_trak_atoms, atom_data_size);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 trak atom: %i", rc);
+
+ if (rc == NGX_DECLINED) {
+ /* skip this trak */
+ ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));
+ mp4->trak.nelts--;
+ mp4->buffer_pos = atom_end;
+ mp4->offset = atom_file_end;
+ return NGX_OK;
+ }
+
+ return rc;
+}
+
+
+static void
+ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ ngx_buf_t *atom;
+
+ trak->size += sizeof(ngx_mp4_atom_header_t);
+ atom = &trak->trak_atom_buf;
+ ngx_mp4_set_32value(atom->pos, trak->size);
+}
+
+
+static ngx_int_t
+ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 compressed moov atom (cmov) is not supported",
+ mp4->file.name.data);
+
+ return NGX_ERROR;
+}
+
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char creation_time[4];
+ u_char modification_time[4];
+ u_char track_id[4];
+ u_char reserved1[4];
+ u_char duration[4];
+ u_char reserved2[8];
+ u_char layer[2];
+ u_char group[2];
+ u_char volume[2];
+ u_char reverved3[2];
+ u_char matrix[36];
+ u_char width[4];
+ u_char heigth[4];
+} ngx_mp4_tkhd_atom_t;
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char creation_time[8];
+ u_char modification_time[8];
+ u_char track_id[4];
+ u_char reserved1[4];
+ u_char duration[8];
+ u_char reserved2[8];
+ u_char layer[2];
+ u_char group[2];
+ u_char volume[2];
+ u_char reverved3[2];
+ u_char matrix[36];
+ u_char width[4];
+ u_char heigth[4];
+} ngx_mp4_tkhd64_atom_t;
+
+
+static ngx_int_t
+ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header;
+ size_t atom_size;
+ uint64_t duration, start_time, length_time;
+ ngx_buf_t *atom;
+ ngx_http_mp4_trak_t *trak;
+ ngx_mp4_tkhd_atom_t *tkhd_atom;
+ ngx_mp4_tkhd64_atom_t *tkhd64_atom;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 tkhd atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ tkhd_atom = (ngx_mp4_tkhd_atom_t *) atom_header;
+ tkhd64_atom = (ngx_mp4_tkhd64_atom_t *) atom_header;
+ ngx_mp4_set_atom_name(tkhd_atom, 't', 'k', 'h', 'd');
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_tkhd_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 tkhd atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ if (tkhd_atom->version[0] == 0) {
+ /* version 0: 32-bit duration */
+ duration = ngx_mp4_get_32value(tkhd_atom->duration);
+
+ } else {
+ /* version 1: 64-bit duration */
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_tkhd64_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 tkhd atom too small",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ duration = ngx_mp4_get_64value(tkhd64_atom->duration);
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "tkhd duration:%uL, time:%.3fs",
+ duration, (double) duration / mp4->timescale);
+
+ start_time = (uint64_t) mp4->start * mp4->timescale / 1000;
+
+ if (duration <= start_time) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "tkhd duration is less than start time");
+ return NGX_DECLINED;
+ }
+
+ duration -= start_time;
+
+ if (mp4->length) {
+ length_time = (uint64_t) mp4->length * mp4->timescale / 1000;
+
+ if (duration > length_time) {
+ duration = length_time;
+ }
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "tkhd new duration:%uL, time:%.3fs",
+ duration, (double) duration / mp4->timescale);
+
+ atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
+
+ trak = ngx_mp4_last_trak(mp4);
+ trak->tkhd_size = atom_size;
+
+ ngx_mp4_set_32value(tkhd_atom->size, atom_size);
+
+ if (tkhd_atom->version[0] == 0) {
+ ngx_mp4_set_32value(tkhd_atom->duration, duration);
+
+ } else {
+ ngx_mp4_set_64value(tkhd64_atom->duration, duration);
+ }
+
+ atom = &trak->tkhd_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_header + atom_size;
+
+ trak->out[NGX_HTTP_MP4_TKHD_ATOM].buf = atom;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header;
+ ngx_buf_t *atom;
+ ngx_http_mp4_trak_t *trak;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process mdia atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'i', 'a');
+
+ trak = ngx_mp4_last_trak(mp4);
+
+ atom = &trak->mdia_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
+
+ trak->out[NGX_HTTP_MP4_MDIA_ATOM].buf = atom;
+
+ return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_mdia_atoms, atom_data_size);
+}
+
+
+static void
+ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ ngx_buf_t *atom;
+
+ trak->size += sizeof(ngx_mp4_atom_header_t);
+ atom = &trak->mdia_atom_buf;
+ ngx_mp4_set_32value(atom->pos, trak->size);
+}
+
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char creation_time[4];
+ u_char modification_time[4];
+ u_char timescale[4];
+ u_char duration[4];
+ u_char language[2];
+ u_char quality[2];
+} ngx_mp4_mdhd_atom_t;
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char creation_time[8];
+ u_char modification_time[8];
+ u_char timescale[4];
+ u_char duration[8];
+ u_char language[2];
+ u_char quality[2];
+} ngx_mp4_mdhd64_atom_t;
+
+
+static ngx_int_t
+ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header;
+ size_t atom_size;
+ uint32_t timescale;
+ uint64_t duration, start_time, length_time;
+ ngx_buf_t *atom;
+ ngx_http_mp4_trak_t *trak;
+ ngx_mp4_mdhd_atom_t *mdhd_atom;
+ ngx_mp4_mdhd64_atom_t *mdhd64_atom;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdhd atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ mdhd_atom = (ngx_mp4_mdhd_atom_t *) atom_header;
+ mdhd64_atom = (ngx_mp4_mdhd64_atom_t *) atom_header;
+ ngx_mp4_set_atom_name(mdhd_atom, 'm', 'd', 'h', 'd');
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_mdhd_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 mdhd atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ if (mdhd_atom->version[0] == 0) {
+ /* version 0: everything is 32-bit */
+ timescale = ngx_mp4_get_32value(mdhd_atom->timescale);
+ duration = ngx_mp4_get_32value(mdhd_atom->duration);
+
+ } else {
+ /* version 1: 64-bit duration and 32-bit timescale */
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_mdhd64_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 mdhd atom too small",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ timescale = ngx_mp4_get_32value(mdhd64_atom->timescale);
+ duration = ngx_mp4_get_64value(mdhd64_atom->duration);
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mdhd timescale:%uD, duration:%uL, time:%.3fs",
+ timescale, duration, (double) duration / timescale);
+
+ start_time = (uint64_t) mp4->start * timescale / 1000;
+
+ if (duration <= start_time) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mdhd duration is less than start time");
+ return NGX_DECLINED;
+ }
+
+ duration -= start_time;
+
+ if (mp4->length) {
+ length_time = (uint64_t) mp4->length * timescale / 1000;
+
+ if (duration > length_time) {
+ duration = length_time;
+ }
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mdhd new duration:%uL, time:%.3fs",
+ duration, (double) duration / timescale);
+
+ atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
+
+ trak = ngx_mp4_last_trak(mp4);
+ trak->mdhd_size = atom_size;
+ trak->timescale = timescale;
+
+ ngx_mp4_set_32value(mdhd_atom->size, atom_size);
+
+ if (mdhd_atom->version[0] == 0) {
+ ngx_mp4_set_32value(mdhd_atom->duration, duration);
+
+ } else {
+ ngx_mp4_set_64value(mdhd64_atom->duration, duration);
+ }
+
+ atom = &trak->mdhd_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_header + atom_size;
+
+ trak->out[NGX_HTTP_MP4_MDHD_ATOM].buf = atom;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header;
+ size_t atom_size;
+ ngx_buf_t *atom;
+ ngx_http_mp4_trak_t *trak;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 hdlr atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
+ ngx_mp4_set_32value(atom_header, atom_size);
+ ngx_mp4_set_atom_name(atom_header, 'h', 'd', 'l', 'r');
+
+ trak = ngx_mp4_last_trak(mp4);
+
+ atom = &trak->hdlr_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_header + atom_size;
+
+ trak->hdlr_size = atom_size;
+ trak->out[NGX_HTTP_MP4_HDLR_ATOM].buf = atom;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header;
+ ngx_buf_t *atom;
+ ngx_http_mp4_trak_t *trak;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process minf atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ ngx_mp4_set_atom_name(atom_header, 'm', 'i', 'n', 'f');
+
+ trak = ngx_mp4_last_trak(mp4);
+
+ atom = &trak->minf_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
+
+ trak->out[NGX_HTTP_MP4_MINF_ATOM].buf = atom;
+
+ return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_minf_atoms, atom_data_size);
+}
+
+
+static void
+ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ ngx_buf_t *atom;
+
+ trak->size += sizeof(ngx_mp4_atom_header_t)
+ + trak->vmhd_size
+ + trak->smhd_size
+ + trak->dinf_size;
+ atom = &trak->minf_atom_buf;
+ ngx_mp4_set_32value(atom->pos, trak->size);
+}
+
+
+static ngx_int_t
+ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header;
+ size_t atom_size;
+ ngx_buf_t *atom;
+ ngx_http_mp4_trak_t *trak;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 vmhd atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
+ ngx_mp4_set_32value(atom_header, atom_size);
+ ngx_mp4_set_atom_name(atom_header, 'v', 'm', 'h', 'd');
+
+ trak = ngx_mp4_last_trak(mp4);
+
+ atom = &trak->vmhd_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_header + atom_size;
+
+ trak->vmhd_size += atom_size;
+ trak->out[NGX_HTTP_MP4_VMHD_ATOM].buf = atom;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header;
+ size_t atom_size;
+ ngx_buf_t *atom;
+ ngx_http_mp4_trak_t *trak;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 smhd atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
+ ngx_mp4_set_32value(atom_header, atom_size);
+ ngx_mp4_set_atom_name(atom_header, 's', 'm', 'h', 'd');
+
+ trak = ngx_mp4_last_trak(mp4);
+
+ atom = &trak->smhd_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_header + atom_size;
+
+ trak->vmhd_size += atom_size;
+ trak->out[NGX_HTTP_MP4_SMHD_ATOM].buf = atom;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header;
+ size_t atom_size;
+ ngx_buf_t *atom;
+ ngx_http_mp4_trak_t *trak;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 dinf atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
+ ngx_mp4_set_32value(atom_header, atom_size);
+ ngx_mp4_set_atom_name(atom_header, 'd', 'i', 'n', 'f');
+
+ trak = ngx_mp4_last_trak(mp4);
+
+ atom = &trak->dinf_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_header + atom_size;
+
+ trak->dinf_size += atom_size;
+ trak->out[NGX_HTTP_MP4_DINF_ATOM].buf = atom;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header;
+ ngx_buf_t *atom;
+ ngx_http_mp4_trak_t *trak;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process stbl atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ ngx_mp4_set_atom_name(atom_header, 's', 't', 'b', 'l');
+
+ trak = ngx_mp4_last_trak(mp4);
+
+ atom = &trak->stbl_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
+
+ trak->out[NGX_HTTP_MP4_STBL_ATOM].buf = atom;
+
+ return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_stbl_atoms, atom_data_size);
+}
+
+
+static void
+ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ ngx_buf_t *atom;
+
+ trak->size += sizeof(ngx_mp4_atom_header_t);
+ atom = &trak->stbl_atom_buf;
+ ngx_mp4_set_32value(atom->pos, trak->size);
+}
+
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char entries[4];
+
+ u_char media_size[4];
+ u_char media_name[4];
+} ngx_mp4_stsd_atom_t;
+
+
+static ngx_int_t
+ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header, *atom_table;
+ size_t atom_size;
+ ngx_buf_t *atom;
+ ngx_mp4_stsd_atom_t *stsd_atom;
+ ngx_http_mp4_trak_t *trak;
+
+ /* sample description atom */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsd atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ stsd_atom = (ngx_mp4_stsd_atom_t *) atom_header;
+ atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
+ atom_table = atom_header + atom_size;
+ ngx_mp4_set_32value(stsd_atom->size, atom_size);
+ ngx_mp4_set_atom_name(stsd_atom, 's', 't', 's', 'd');
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_stsd_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 stsd atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "stsd entries:%uD, media:%*s",
+ ngx_mp4_get_32value(stsd_atom->entries),
+ 4, stsd_atom->media_name);
+
+ trak = ngx_mp4_last_trak(mp4);
+
+ atom = &trak->stsd_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_table;
+
+ trak->out[NGX_HTTP_MP4_STSD_ATOM].buf = atom;
+ trak->size += atom_size;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char entries[4];
+} ngx_mp4_stts_atom_t;
+
+typedef struct {
+ u_char count[4];
+ u_char duration[4];
+} ngx_mp4_stts_entry_t;
+
+
+static ngx_int_t
+ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header, *atom_table, *atom_end;
+ uint32_t entries;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_stts_atom_t *stts_atom;
+ ngx_http_mp4_trak_t *trak;
+
+ /* time-to-sample atom */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stts atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ stts_atom = (ngx_mp4_stts_atom_t *) atom_header;
+ ngx_mp4_set_atom_name(stts_atom, 's', 't', 't', 's');
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 stts atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ entries = ngx_mp4_get_32value(stts_atom->entries);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 time-to-sample entries:%uD", entries);
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t)
+ + entries * sizeof(ngx_mp4_stts_entry_t) > atom_data_size)
+ {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 stts atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ atom_table = atom_header + sizeof(ngx_mp4_stts_atom_t);
+ atom_end = atom_table + entries * sizeof(ngx_mp4_stts_entry_t);
+
+ trak = ngx_mp4_last_trak(mp4);
+ trak->time_to_sample_entries = entries;
+
+ atom = &trak->stts_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_table;
+
+ data = &trak->stts_data_buf;
+ data->temporary = 1;
+ data->pos = atom_table;
+ data->last = atom_end;
+
+ trak->out[NGX_HTTP_MP4_STTS_ATOM].buf = atom;
+ trak->out[NGX_HTTP_MP4_STTS_DATA].buf = data;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ size_t atom_size;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_stts_atom_t *stts_atom;
+
+ /*
+ * mdia.minf.stbl.stts updating requires trak->timescale
+ * from mdia.mdhd atom which may reside after mdia.minf
+ */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 stts atom update");
+
+ data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;
+
+ if (data == NULL) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "no mp4 stts atoms were found in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ if (ngx_http_mp4_crop_stts_data(mp4, trak, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_http_mp4_crop_stts_data(mp4, trak, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "time-to-sample entries:%uD", trak->time_to_sample_entries);
+
+ atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos);
+ trak->size += atom_size;
+
+ atom = trak->out[NGX_HTTP_MP4_STTS_ATOM].buf;
+ stts_atom = (ngx_mp4_stts_atom_t *) atom->pos;
+ ngx_mp4_set_32value(stts_atom->size, atom_size);
+ ngx_mp4_set_32value(stts_atom->entries, trak->time_to_sample_entries);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start)
+{
+ uint32_t count, duration, rest;
+ uint64_t start_time;
+ ngx_buf_t *data;
+ ngx_uint_t start_sample, entries, start_sec;
+ ngx_mp4_stts_entry_t *entry, *end;
+
+ if (start) {
+ start_sec = mp4->start;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 stts crop start_time:%ui", start_sec);
+
+ } else if (mp4->length) {
+ start_sec = mp4->length;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 stts crop end_time:%ui", start_sec);
+
+ } else {
+ return NGX_OK;
+ }
+
+ data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;
+
+ start_time = (uint64_t) start_sec * trak->timescale / 1000;
+
+ entries = trak->time_to_sample_entries;
+ start_sample = 0;
+ entry = (ngx_mp4_stts_entry_t *) data->pos;
+ end = (ngx_mp4_stts_entry_t *) data->last;
+
+ while (entry < end) {
+ count = ngx_mp4_get_32value(entry->count);
+ duration = ngx_mp4_get_32value(entry->duration);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "time:%uL, count:%uD, duration:%uD",
+ start_time, count, duration);
+
+ if (start_time < (uint64_t) count * duration) {
+ start_sample += (ngx_uint_t) (start_time / duration);
+ rest = (uint32_t) (start_time / duration);
+ goto found;
+ }
+
+ start_sample += count;
+ start_time -= count * duration;
+ entries--;
+ entry++;
+ }
+
+ if (start) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "start time is out mp4 stts samples in \"%s\"",
+ mp4->file.name.data);
+
+ return NGX_ERROR;
+
+ } else {
+ trak->end_sample = trak->start_sample + start_sample;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "end_sample:%ui", trak->end_sample);
+
+ return NGX_OK;
+ }
+
+found:
+
+ if (start) {
+ ngx_mp4_set_32value(entry->count, count - rest);
+ data->pos = (u_char *) entry;
+ trak->time_to_sample_entries = entries;
+ trak->start_sample = start_sample;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "start_sample:%ui, new count:%uD",
+ trak->start_sample, count - rest);
+
+ } else {
+ ngx_mp4_set_32value(entry->count, rest);
+ data->last = (u_char *) (entry + 1);
+ trak->time_to_sample_entries -= entries - 1;
+ trak->end_sample = trak->start_sample + start_sample;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "end_sample:%ui, new count:%uD",
+ trak->end_sample, rest);
+ }
+
+ return NGX_OK;
+}
+
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char entries[4];
+} ngx_http_mp4_stss_atom_t;
+
+
+static ngx_int_t
+ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header, *atom_table, *atom_end;
+ uint32_t entries;
+ ngx_buf_t *atom, *data;
+ ngx_http_mp4_trak_t *trak;
+ ngx_http_mp4_stss_atom_t *stss_atom;
+
+ /* sync samples atom */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stss atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ stss_atom = (ngx_http_mp4_stss_atom_t *) atom_header;
+ ngx_mp4_set_atom_name(stss_atom, 's', 't', 's', 's');
+
+ if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 stss atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ entries = ngx_mp4_get_32value(stss_atom->entries);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "sync sample entries:%uD", entries);
+
+ trak = ngx_mp4_last_trak(mp4);
+ trak->sync_samples_entries = entries;
+
+ atom_table = atom_header + sizeof(ngx_http_mp4_stss_atom_t);
+
+ atom = &trak->stss_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_table;
+
+ if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t)
+ + entries * sizeof(uint32_t) > atom_data_size)
+ {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 stss atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ atom_end = atom_table + entries * sizeof(uint32_t);
+
+ data = &trak->stss_data_buf;
+ data->temporary = 1;
+ data->pos = atom_table;
+ data->last = atom_end;
+
+ trak->out[NGX_HTTP_MP4_STSS_ATOM].buf = atom;
+ trak->out[NGX_HTTP_MP4_STSS_DATA].buf = data;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ size_t atom_size;
+ uint32_t sample, start_sample, *entry, *end;
+ ngx_buf_t *atom, *data;
+ ngx_http_mp4_stss_atom_t *stss_atom;
+
+ /*
+ * mdia.minf.stbl.stss updating requires trak->start_sample
+ * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
+ * atom which may reside after mdia.minf
+ */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 stss atom update");
+
+ data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;
+
+ if (data == NULL) {
+ return NGX_OK;
+ }
+
+ ngx_http_mp4_crop_stss_data(mp4, trak, 1);
+ ngx_http_mp4_crop_stss_data(mp4, trak, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "sync sample entries:%uD", trak->sync_samples_entries);
+
+ if (trak->sync_samples_entries) {
+ entry = (uint32_t *) data->pos;
+ end = (uint32_t *) data->last;
+
+ start_sample = trak->start_sample;
+
+ while (entry < end) {
+ sample = ngx_mp4_get_32value(entry);
+ sample -= start_sample;
+ ngx_mp4_set_32value(entry, sample);
+ entry++;
+ }
+
+ } else {
+ trak->out[NGX_HTTP_MP4_STSS_DATA].buf = NULL;
+ }
+
+ atom_size = sizeof(ngx_http_mp4_stss_atom_t) + (data->last - data->pos);
+ trak->size += atom_size;
+
+ atom = trak->out[NGX_HTTP_MP4_STSS_ATOM].buf;
+ stss_atom = (ngx_http_mp4_stss_atom_t *) atom->pos;
+
+ ngx_mp4_set_32value(stss_atom->size, atom_size);
+ ngx_mp4_set_32value(stss_atom->entries, trak->sync_samples_entries);
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start)
+{
+ uint32_t sample, start_sample, *entry, *end;
+ ngx_buf_t *data;
+ ngx_uint_t entries;
+
+ /* sync samples starts from 1 */
+
+ if (start) {
+ start_sample = trak->start_sample + 1;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 stss crop start_sample:%uD", start_sample);
+
+ } else if (mp4->length) {
+ start_sample = trak->end_sample + 1;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 stss crop end_sample:%uD", start_sample);
+
+ } else {
+ return;
+ }
+
+ data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;
+
+ entries = trak->sync_samples_entries;
+ entry = (uint32_t *) data->pos;
+ end = (uint32_t *) data->last;
+
+ while (entry < end) {
+ sample = ngx_mp4_get_32value(entry);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "sync:%uD", sample);
+
+ if (sample >= start_sample) {
+ goto found;
+ }
+
+ entries--;
+ entry++;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "sample is out of mp4 stss atom");
+
+found:
+
+ if (start) {
+ data->pos = (u_char *) entry;
+ trak->sync_samples_entries = entries;
+
+ } else {
+ data->last = (u_char *) entry;
+ trak->sync_samples_entries -= entries;
+ }
+}
+
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char entries[4];
+} ngx_mp4_ctts_atom_t;
+
+typedef struct {
+ u_char count[4];
+ u_char offset[4];
+} ngx_mp4_ctts_entry_t;
+
+
+static ngx_int_t
+ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header, *atom_table, *atom_end;
+ uint32_t entries;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_ctts_atom_t *ctts_atom;
+ ngx_http_mp4_trak_t *trak;
+
+ /* composition offsets atom */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ctts atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ ctts_atom = (ngx_mp4_ctts_atom_t *) atom_header;
+ ngx_mp4_set_atom_name(ctts_atom, 'c', 't', 't', 's');
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 ctts atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ entries = ngx_mp4_get_32value(ctts_atom->entries);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "composition offset entries:%uD", entries);
+
+ trak = ngx_mp4_last_trak(mp4);
+ trak->composition_offset_entries = entries;
+
+ atom_table = atom_header + sizeof(ngx_mp4_ctts_atom_t);
+
+ atom = &trak->ctts_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_table;
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t)
+ + entries * sizeof(ngx_mp4_ctts_entry_t) > atom_data_size)
+ {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 ctts atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ atom_end = atom_table + entries * sizeof(ngx_mp4_ctts_entry_t);
+
+ data = &trak->ctts_data_buf;
+ data->temporary = 1;
+ data->pos = atom_table;
+ data->last = atom_end;
+
+ trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = atom;
+ trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = data;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ size_t atom_size;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_ctts_atom_t *ctts_atom;
+
+ /*
+ * mdia.minf.stbl.ctts updating requires trak->start_sample
+ * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
+ * atom which may reside after mdia.minf
+ */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 ctts atom update");
+
+ data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;
+
+ if (data == NULL) {
+ return;
+ }
+
+ ngx_http_mp4_crop_ctts_data(mp4, trak, 1);
+ ngx_http_mp4_crop_ctts_data(mp4, trak, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "composition offset entries:%uD",
+ trak->composition_offset_entries);
+
+ if (trak->composition_offset_entries == 0) {
+ trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL;
+ trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL;
+ return;
+ }
+
+ atom_size = sizeof(ngx_mp4_ctts_atom_t) + (data->last - data->pos);
+ trak->size += atom_size;
+
+ atom = trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf;
+ ctts_atom = (ngx_mp4_ctts_atom_t *) atom->pos;
+
+ ngx_mp4_set_32value(ctts_atom->size, atom_size);
+ ngx_mp4_set_32value(ctts_atom->entries, trak->composition_offset_entries);
+
+ return;
+}
+
+
+static void
+ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start)
+{
+ uint32_t count, start_sample, rest;
+ ngx_buf_t *data;
+ ngx_uint_t entries;
+ ngx_mp4_ctts_entry_t *entry, *end;
+
+ /* sync samples starts from 1 */
+
+ if (start) {
+ start_sample = trak->start_sample + 1;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 ctts crop start_sample:%uD", start_sample);
+
+ } else if (mp4->length) {
+ start_sample = trak->end_sample - trak->start_sample + 1;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 ctts crop end_sample:%uD", start_sample);
+
+ } else {
+ return;
+ }
+
+ data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;
+
+ entries = trak->composition_offset_entries;
+ entry = (ngx_mp4_ctts_entry_t *) data->pos;
+ end = (ngx_mp4_ctts_entry_t *) data->last;
+
+ while (entry < end) {
+ count = ngx_mp4_get_32value(entry->count);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "sample:%uD, count:%uD, offset:%uD",
+ start_sample, count, ngx_mp4_get_32value(entry->offset));
+
+ if (start_sample <= count) {
+ rest = start_sample - 1;
+ goto found;
+ }
+
+ start_sample -= count;
+ entries--;
+ entry++;
+ }
+
+ if (start) {
+ data->pos = (u_char *) end;
+ trak->composition_offset_entries = 0;
+ }
+
+ return;
+
+found:
+
+ if (start) {
+ ngx_mp4_set_32value(entry->count, count - rest);
+ data->pos = (u_char *) entry;
+ trak->composition_offset_entries = entries;
+
+ } else {
+ ngx_mp4_set_32value(entry->count, rest);
+ data->last = (u_char *) (entry + 1);
+ trak->composition_offset_entries -= entries - 1;
+ }
+}
+
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char entries[4];
+} ngx_mp4_stsc_atom_t;
+
+
+static ngx_int_t
+ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header, *atom_table, *atom_end;
+ uint32_t entries;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_stsc_atom_t *stsc_atom;
+ ngx_http_mp4_trak_t *trak;
+
+ /* sample-to-chunk atom */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsc atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ stsc_atom = (ngx_mp4_stsc_atom_t *) atom_header;
+ ngx_mp4_set_atom_name(stsc_atom, 's', 't', 's', 'c');
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 stsc atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ entries = ngx_mp4_get_32value(stsc_atom->entries);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "sample-to-chunk entries:%uD", entries);
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t)
+ + entries * sizeof(ngx_mp4_stsc_entry_t) > atom_data_size)
+ {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 stsc atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ atom_table = atom_header + sizeof(ngx_mp4_stsc_atom_t);
+ atom_end = atom_table + entries * sizeof(ngx_mp4_stsc_entry_t);
+
+ trak = ngx_mp4_last_trak(mp4);
+ trak->sample_to_chunk_entries = entries;
+
+ atom = &trak->stsc_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_table;
+
+ data = &trak->stsc_data_buf;
+ data->temporary = 1;
+ data->pos = atom_table;
+ data->last = atom_end;
+
+ trak->out[NGX_HTTP_MP4_STSC_ATOM].buf = atom;
+ trak->out[NGX_HTTP_MP4_STSC_DATA].buf = data;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ size_t atom_size;
+ uint32_t chunk;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_stsc_atom_t *stsc_atom;
+ ngx_mp4_stsc_entry_t *entry, *end;
+
+ /*
+ * mdia.minf.stbl.stsc updating requires trak->start_sample
+ * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
+ * atom which may reside after mdia.minf
+ */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 stsc atom update");
+
+ data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;
+
+ if (data == NULL) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "no mp4 stsc atoms were found in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ if (trak->sample_to_chunk_entries == 0) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "zero number of entries in stsc atom in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ if (ngx_http_mp4_crop_stsc_data(mp4, trak, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_http_mp4_crop_stsc_data(mp4, trak, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "sample-to-chunk entries:%uD",
+ trak->sample_to_chunk_entries);
+
+ entry = (ngx_mp4_stsc_entry_t *) data->pos;
+ end = (ngx_mp4_stsc_entry_t *) data->last;
+
+ while (entry < end) {
+ chunk = ngx_mp4_get_32value(entry->chunk);
+ chunk -= trak->start_chunk;
+ ngx_mp4_set_32value(entry->chunk, chunk);
+ entry++;
+ }
+
+ atom_size = sizeof(ngx_mp4_stsc_atom_t)
+ + trak->sample_to_chunk_entries * sizeof(ngx_mp4_stsc_entry_t);
+
+ trak->size += atom_size;
+
+ atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf;
+ stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos;
+
+ ngx_mp4_set_32value(stsc_atom->size, atom_size);
+ ngx_mp4_set_32value(stsc_atom->entries, trak->sample_to_chunk_entries);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start)
+{
+ uint32_t start_sample, chunk, samples, id, next_chunk, n,
+ prev_samples;
+ ngx_buf_t *data, *buf;
+ ngx_uint_t entries, target_chunk, chunk_samples;
+ ngx_mp4_stsc_entry_t *entry, *end, *first;
+
+ entries = trak->sample_to_chunk_entries - 1;
+
+ if (start) {
+ start_sample = (uint32_t) trak->start_sample;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 stsc crop start_sample:%uD", start_sample);
+
+ } else if (mp4->length) {
+ start_sample = (uint32_t) (trak->end_sample - trak->start_sample);
+ samples = 0;
+
+ data = trak->out[NGX_HTTP_MP4_STSC_START].buf;
+
+ if (data) {
+ entry = (ngx_mp4_stsc_entry_t *) data->pos;
+ samples = ngx_mp4_get_32value(entry->samples);
+ entries--;
+
+ if (samples > start_sample) {
+ samples = start_sample;
+ ngx_mp4_set_32value(entry->samples, samples);
+ }
+
+ start_sample -= samples;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 stsc crop end_sample:%uD, ext_samples:%uD",
+ start_sample, samples);
+
+ } else {
+ return NGX_OK;
+ }
+
+ data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;
+
+ entry = (ngx_mp4_stsc_entry_t *) data->pos;
+ end = (ngx_mp4_stsc_entry_t *) data->last;
+
+ chunk = ngx_mp4_get_32value(entry->chunk);
+ samples = ngx_mp4_get_32value(entry->samples);
+ id = ngx_mp4_get_32value(entry->id);
+ prev_samples = 0;
+ entry++;
+
+ while (entry < end) {
+
+ next_chunk = ngx_mp4_get_32value(entry->chunk);
+
+ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "sample:%uD, chunk:%uD, chunks:%uD, "
+ "samples:%uD, id:%uD",
+ start_sample, chunk, next_chunk - chunk, samples, id);
+
+ n = (next_chunk - chunk) * samples;
+
+ if (start_sample < n) {
+ goto found;
+ }
+
+ start_sample -= n;
+
+ prev_samples = samples;
+ chunk = next_chunk;
+ samples = ngx_mp4_get_32value(entry->samples);
+ id = ngx_mp4_get_32value(entry->id);
+ entries--;
+ entry++;
+ }
+
+ next_chunk = trak->chunks + 1;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "sample:%uD, chunk:%uD, chunks:%uD, samples:%uD",
+ start_sample, chunk, next_chunk - chunk, samples);
+
+ n = (next_chunk - chunk) * samples;
+
+ if (start_sample > n) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "%s time is out mp4 stsc chunks in \"%s\"",
+ start ? "start" : "end", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+found:
+
+ entries++;
+ entry--;
+
+ if (samples == 0) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "zero number of samples in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ target_chunk = chunk - 1;
+ target_chunk += start_sample / samples;
+ chunk_samples = start_sample % samples;
+
+ if (start) {
+ data->pos = (u_char *) entry;
+
+ trak->sample_to_chunk_entries = entries;
+ trak->start_chunk = target_chunk;
+ trak->start_chunk_samples = chunk_samples;
+
+ ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 1);
+
+ samples -= chunk_samples;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "start_chunk:%ui, start_chunk_samples:%ui",
+ trak->start_chunk, trak->start_chunk_samples);
+
+ } else {
+ if (start_sample) {
+ data->last = (u_char *) (entry + 1);
+ trak->sample_to_chunk_entries -= entries - 1;
+ trak->end_chunk_samples = samples;
+
+ } else {
+ data->last = (u_char *) entry;
+ trak->sample_to_chunk_entries -= entries;
+ trak->end_chunk_samples = prev_samples;
+ }
+
+ if (chunk_samples) {
+ trak->end_chunk = target_chunk + 1;
+ trak->end_chunk_samples = chunk_samples;
+
+ } else {
+ trak->end_chunk = target_chunk;
+ }
+
+ samples = chunk_samples;
+ next_chunk = chunk + 1;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "end_chunk:%ui, end_chunk_samples:%ui",
+ trak->end_chunk, trak->end_chunk_samples);
+ }
+
+ if (chunk_samples && next_chunk - target_chunk == 2) {
+
+ ngx_mp4_set_32value(entry->samples, samples);
+
+ } else if (chunk_samples && start) {
+
+ first = &trak->stsc_start_chunk_entry;
+ ngx_mp4_set_32value(first->chunk, 1);
+ ngx_mp4_set_32value(first->samples, samples);
+ ngx_mp4_set_32value(first->id, id);
+
+ buf = &trak->stsc_start_chunk_buf;
+ buf->temporary = 1;
+ buf->pos = (u_char *) first;
+ buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);
+
+ trak->out[NGX_HTTP_MP4_STSC_START].buf = buf;
+
+ ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 2);
+
+ trak->sample_to_chunk_entries++;
+
+ } else if (chunk_samples) {
+
+ first = &trak->stsc_end_chunk_entry;
+ ngx_mp4_set_32value(first->chunk, trak->end_chunk - trak->start_chunk);
+ ngx_mp4_set_32value(first->samples, samples);
+ ngx_mp4_set_32value(first->id, id);
+
+ buf = &trak->stsc_end_chunk_buf;
+ buf->temporary = 1;
+ buf->pos = (u_char *) first;
+ buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);
+
+ trak->out[NGX_HTTP_MP4_STSC_END].buf = buf;
+
+ trak->sample_to_chunk_entries++;
+ }
+
+ return NGX_OK;
+}
+
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char uniform_size[4];
+ u_char entries[4];
+} ngx_mp4_stsz_atom_t;
+
+
+static ngx_int_t
+ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header, *atom_table, *atom_end;
+ size_t atom_size;
+ uint32_t entries, size;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_stsz_atom_t *stsz_atom;
+ ngx_http_mp4_trak_t *trak;
+
+ /* sample sizes atom */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsz atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ stsz_atom = (ngx_mp4_stsz_atom_t *) atom_header;
+ ngx_mp4_set_atom_name(stsz_atom, 's', 't', 's', 'z');
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 stsz atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ size = ngx_mp4_get_32value(stsz_atom->uniform_size);
+ entries = ngx_mp4_get_32value(stsz_atom->entries);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "sample uniform size:%uD, entries:%uD", size, entries);
+
+ trak = ngx_mp4_last_trak(mp4);
+ trak->sample_sizes_entries = entries;
+
+ atom_table = atom_header + sizeof(ngx_mp4_stsz_atom_t);
+
+ atom = &trak->stsz_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_table;
+
+ trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf = atom;
+
+ if (size == 0) {
+ if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t)
+ + entries * sizeof(uint32_t) > atom_data_size)
+ {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 stsz atom too small",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ atom_end = atom_table + entries * sizeof(uint32_t);
+
+ data = &trak->stsz_data_buf;
+ data->temporary = 1;
+ data->pos = atom_table;
+ data->last = atom_end;
+
+ trak->out[NGX_HTTP_MP4_STSZ_DATA].buf = data;
+
+ } else {
+ /* if size != 0 then all samples are the same size */
+ /* TODO : chunk samples */
+ atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
+ ngx_mp4_set_32value(atom_header, atom_size);
+ trak->size += atom_size;
+ }
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ size_t atom_size;
+ uint32_t *pos, *end, entries;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_stsz_atom_t *stsz_atom;
+
+ /*
+ * mdia.minf.stbl.stsz updating requires trak->start_sample
+ * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
+ * atom which may reside after mdia.minf
+ */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 stsz atom update");
+
+ data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf;
+
+ if (data) {
+ entries = trak->sample_sizes_entries;
+
+ if (trak->start_sample > entries) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "start time is out mp4 stsz samples in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ entries -= trak->start_sample;
+ data->pos += trak->start_sample * sizeof(uint32_t);
+ end = (uint32_t *) data->pos;
+
+ for (pos = end - trak->start_chunk_samples; pos < end; pos++) {
+ trak->start_chunk_samples_size += ngx_mp4_get_32value(pos);
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "chunk samples sizes:%uL",
+ trak->start_chunk_samples_size);
+
+ if (mp4->length) {
+ if (trak->end_sample - trak->start_sample > entries) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "end time is out mp4 stsz samples in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ entries = trak->end_sample - trak->start_sample;
+ data->last = data->pos + entries * sizeof(uint32_t);
+ end = (uint32_t *) data->last;
+
+ for (pos = end - trak->end_chunk_samples; pos < end; pos++) {
+ trak->end_chunk_samples_size += ngx_mp4_get_32value(pos);
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 stsz end_chunk_samples_size:%uL",
+ trak->end_chunk_samples_size);
+ }
+
+ atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos);
+ trak->size += atom_size;
+
+ atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf;
+ stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos;
+
+ ngx_mp4_set_32value(stsz_atom->size, atom_size);
+ ngx_mp4_set_32value(stsz_atom->entries, entries);
+ }
+
+ return NGX_OK;
+}
+
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char entries[4];
+} ngx_mp4_stco_atom_t;
+
+
+static ngx_int_t
+ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header, *atom_table, *atom_end;
+ uint32_t entries;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_stco_atom_t *stco_atom;
+ ngx_http_mp4_trak_t *trak;
+
+ /* chunk offsets atom */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stco atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ stco_atom = (ngx_mp4_stco_atom_t *) atom_header;
+ ngx_mp4_set_atom_name(stco_atom, 's', 't', 'c', 'o');
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 stco atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ entries = ngx_mp4_get_32value(stco_atom->entries);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t)
+ + entries * sizeof(uint32_t) > atom_data_size)
+ {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 stco atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ atom_table = atom_header + sizeof(ngx_mp4_stco_atom_t);
+ atom_end = atom_table + entries * sizeof(uint32_t);
+
+ trak = ngx_mp4_last_trak(mp4);
+ trak->chunks = entries;
+
+ atom = &trak->stco_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_table;
+
+ data = &trak->stco_data_buf;
+ data->temporary = 1;
+ data->pos = atom_table;
+ data->last = atom_end;
+
+ trak->out[NGX_HTTP_MP4_STCO_ATOM].buf = atom;
+ trak->out[NGX_HTTP_MP4_STCO_DATA].buf = data;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ size_t atom_size;
+ uint32_t entries;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_stco_atom_t *stco_atom;
+
+ /*
+ * mdia.minf.stbl.stco updating requires trak->start_chunk
+ * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd
+ * atom which may reside after mdia.minf
+ */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 stco atom update");
+
+ data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;
+
+ if (data == NULL) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "no mp4 stco atoms were found in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ if (trak->start_chunk > trak->chunks) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "start time is out mp4 stco chunks in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ data->pos += trak->start_chunk * sizeof(uint32_t);
+
+ trak->start_offset = ngx_mp4_get_32value(data->pos);
+ trak->start_offset += trak->start_chunk_samples_size;
+ ngx_mp4_set_32value(data->pos, trak->start_offset);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "start chunk offset:%O", trak->start_offset);
+
+ if (mp4->length) {
+
+ if (trak->end_chunk > trak->chunks) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "end time is out mp4 stco chunks in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ entries = trak->end_chunk - trak->start_chunk;
+ data->last = data->pos + entries * sizeof(uint32_t);
+
+ if (entries) {
+ trak->end_offset =
+ ngx_mp4_get_32value(data->last - sizeof(uint32_t));
+ trak->end_offset += trak->end_chunk_samples_size;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "end chunk offset:%O", trak->end_offset);
+ }
+
+ } else {
+ entries = trak->chunks - trak->start_chunk;
+ trak->end_offset = mp4->mdat_data.buf->file_last;
+ }
+
+ if (entries == 0) {
+ trak->start_offset = mp4->end;
+ trak->end_offset = 0;
+ }
+
+ atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos);
+ trak->size += atom_size;
+
+ atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf;
+ stco_atom = (ngx_mp4_stco_atom_t *) atom->pos;
+
+ ngx_mp4_set_32value(stco_atom->size, atom_size);
+ ngx_mp4_set_32value(stco_atom->entries, entries);
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, int32_t adjustment)
+{
+ uint32_t offset, *entry, *end;
+ ngx_buf_t *data;
+
+ /*
+ * moov.trak.mdia.minf.stbl.stco adjustment requires
+ * minimal start offset of all traks and new moov atom size
+ */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 stco atom adjustment");
+
+ data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;
+ entry = (uint32_t *) data->pos;
+ end = (uint32_t *) data->last;
+
+ while (entry < end) {
+ offset = ngx_mp4_get_32value(entry);
+ offset += adjustment;
+ ngx_mp4_set_32value(entry, offset);
+ entry++;
+ }
+}
+
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char entries[4];
+} ngx_mp4_co64_atom_t;
+
+
+static ngx_int_t
+ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
+{
+ u_char *atom_header, *atom_table, *atom_end;
+ uint32_t entries;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_co64_atom_t *co64_atom;
+ ngx_http_mp4_trak_t *trak;
+
+ /* chunk offsets atom */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 co64 atom");
+
+ atom_header = ngx_mp4_atom_header(mp4);
+ co64_atom = (ngx_mp4_co64_atom_t *) atom_header;
+ ngx_mp4_set_atom_name(co64_atom, 'c', 'o', '6', '4');
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t) > atom_data_size) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 co64 atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ entries = ngx_mp4_get_32value(co64_atom->entries);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);
+
+ if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t)
+ + entries * sizeof(uint64_t) > atom_data_size)
+ {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "\"%s\" mp4 co64 atom too small", mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ atom_table = atom_header + sizeof(ngx_mp4_co64_atom_t);
+ atom_end = atom_table + entries * sizeof(uint64_t);
+
+ trak = ngx_mp4_last_trak(mp4);
+ trak->chunks = entries;
+
+ atom = &trak->co64_atom_buf;
+ atom->temporary = 1;
+ atom->pos = atom_header;
+ atom->last = atom_table;
+
+ data = &trak->co64_data_buf;
+ data->temporary = 1;
+ data->pos = atom_table;
+ data->last = atom_end;
+
+ trak->out[NGX_HTTP_MP4_CO64_ATOM].buf = atom;
+ trak->out[NGX_HTTP_MP4_CO64_DATA].buf = data;
+
+ ngx_mp4_atom_next(mp4, atom_data_size);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ size_t atom_size;
+ uint64_t entries;
+ ngx_buf_t *atom, *data;
+ ngx_mp4_co64_atom_t *co64_atom;
+
+ /*
+ * mdia.minf.stbl.co64 updating requires trak->start_chunk
+ * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd
+ * atom which may reside after mdia.minf
+ */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 co64 atom update");
+
+ data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
+
+ if (data == NULL) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "no mp4 co64 atoms were found in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ if (trak->start_chunk > trak->chunks) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "start time is out mp4 co64 chunks in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ data->pos += trak->start_chunk * sizeof(uint64_t);
+
+ trak->start_offset = ngx_mp4_get_64value(data->pos);
+ trak->start_offset += trak->start_chunk_samples_size;
+ ngx_mp4_set_64value(data->pos, trak->start_offset);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "start chunk offset:%O", trak->start_offset);
+
+ if (mp4->length) {
+
+ if (trak->end_chunk > trak->chunks) {
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
+ "end time is out mp4 co64 chunks in \"%s\"",
+ mp4->file.name.data);
+ return NGX_ERROR;
+ }
+
+ entries = trak->end_chunk - trak->start_chunk;
+ data->last = data->pos + entries * sizeof(uint64_t);
+
+ if (entries) {
+ trak->end_offset =
+ ngx_mp4_get_64value(data->last - sizeof(uint64_t));
+ trak->end_offset += trak->end_chunk_samples_size;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "end chunk offset:%O", trak->end_offset);
+ }
+
+ } else {
+ entries = trak->chunks - trak->start_chunk;
+ trak->end_offset = mp4->mdat_data.buf->file_last;
+ }
+
+ if (entries == 0) {
+ trak->start_offset = mp4->end;
+ trak->end_offset = 0;
+ }
+
+ atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos);
+ trak->size += atom_size;
+
+ atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf;
+ co64_atom = (ngx_mp4_co64_atom_t *) atom->pos;
+
+ ngx_mp4_set_32value(co64_atom->size, atom_size);
+ ngx_mp4_set_32value(co64_atom->entries, entries);
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, off_t adjustment)
+{
+ uint64_t offset, *entry, *end;
+ ngx_buf_t *data;
+
+ /*
+ * moov.trak.mdia.minf.stbl.co64 adjustment requires
+ * minimal start offset of all traks and new moov atom size
+ */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 co64 atom adjustment");
+
+ data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
+ entry = (uint64_t *) data->pos;
+ end = (uint64_t *) data->last;
+
+ while (entry < end) {
+ offset = ngx_mp4_get_64value(entry);
+ offset += adjustment;
+ ngx_mp4_set_64value(entry, offset);
+ entry++;
+ }
+}
+
+
+static char *
+ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+ clcf->handler = ngx_http_mp4_handler;
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_mp4_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_mp4_conf_t *conf;
+
+ conf = ngx_palloc(cf->pool, sizeof(ngx_http_mp4_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->buffer_size = NGX_CONF_UNSET_SIZE;
+ conf->max_buffer_size = NGX_CONF_UNSET_SIZE;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_mp4_conf_t *prev = parent;
+ ngx_http_mp4_conf_t *conf = child;
+
+ ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 512 * 1024);
+ ngx_conf_merge_size_value(conf->max_buffer_size, prev->max_buffer_size,
+ 10 * 1024 * 1024);
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_not_modified_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_not_modified_filter_module.c
new file mode 100644
index 00000000000..acc94ded31a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_not_modified_filter_module.c
@@ -0,0 +1,266 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static ngx_uint_t ngx_http_test_if_unmodified(ngx_http_request_t *r);
+static ngx_uint_t ngx_http_test_if_modified(ngx_http_request_t *r);
+static ngx_uint_t ngx_http_test_if_match(ngx_http_request_t *r,
+ ngx_table_elt_t *header, ngx_uint_t weak);
+static ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf);
+
+
+static ngx_http_module_t ngx_http_not_modified_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_not_modified_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_not_modified_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_not_modified_filter_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+
+
+static ngx_int_t
+ngx_http_not_modified_header_filter(ngx_http_request_t *r)
+{
+ if (r->headers_out.status != NGX_HTTP_OK
+ || r != r->main
+ || r->disable_not_modified)
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ if (r->headers_in.if_unmodified_since
+ && !ngx_http_test_if_unmodified(r))
+ {
+ return ngx_http_filter_finalize_request(r, NULL,
+ NGX_HTTP_PRECONDITION_FAILED);
+ }
+
+ if (r->headers_in.if_match
+ && !ngx_http_test_if_match(r, r->headers_in.if_match, 0))
+ {
+ return ngx_http_filter_finalize_request(r, NULL,
+ NGX_HTTP_PRECONDITION_FAILED);
+ }
+
+ if (r->headers_in.if_modified_since || r->headers_in.if_none_match) {
+
+ if (r->headers_in.if_modified_since
+ && ngx_http_test_if_modified(r))
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ if (r->headers_in.if_none_match
+ && !ngx_http_test_if_match(r, r->headers_in.if_none_match, 1))
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ /* not modified */
+
+ r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
+ r->headers_out.status_line.len = 0;
+ r->headers_out.content_type.len = 0;
+ ngx_http_clear_content_length(r);
+ ngx_http_clear_accept_ranges(r);
+
+ if (r->headers_out.content_encoding) {
+ r->headers_out.content_encoding->hash = 0;
+ r->headers_out.content_encoding = NULL;
+ }
+
+ return ngx_http_next_header_filter(r);
+ }
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_uint_t
+ngx_http_test_if_unmodified(ngx_http_request_t *r)
+{
+ time_t iums;
+
+ if (r->headers_out.last_modified_time == (time_t) -1) {
+ return 0;
+ }
+
+ iums = ngx_http_parse_time(r->headers_in.if_unmodified_since->value.data,
+ r->headers_in.if_unmodified_since->value.len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http iums:%T lm:%T", iums, r->headers_out.last_modified_time);
+
+ if (iums >= r->headers_out.last_modified_time) {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static ngx_uint_t
+ngx_http_test_if_modified(ngx_http_request_t *r)
+{
+ time_t ims;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (r->headers_out.last_modified_time == (time_t) -1) {
+ return 1;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {
+ return 1;
+ }
+
+ ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
+ r->headers_in.if_modified_since->value.len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http ims:%T lm:%T", ims, r->headers_out.last_modified_time);
+
+ if (ims == r->headers_out.last_modified_time) {
+ return 0;
+ }
+
+ if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
+ || ims < r->headers_out.last_modified_time)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static ngx_uint_t
+ngx_http_test_if_match(ngx_http_request_t *r, ngx_table_elt_t *header,
+ ngx_uint_t weak)
+{
+ u_char *start, *end, ch;
+ ngx_str_t etag, *list;
+
+ list = &header->value;
+
+ if (list->len == 1 && list->data[0] == '*') {
+ return 1;
+ }
+
+ if (r->headers_out.etag == NULL) {
+ return 0;
+ }
+
+ etag = r->headers_out.etag->value;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http im:\"%V\" etag:%V", list, &etag);
+
+ if (weak
+ && etag.len > 2
+ && etag.data[0] == 'W'
+ && etag.data[1] == '/')
+ {
+ etag.len -= 2;
+ etag.data += 2;
+ }
+
+ start = list->data;
+ end = list->data + list->len;
+
+ while (start < end) {
+
+ if (weak
+ && end - start > 2
+ && start[0] == 'W'
+ && start[1] == '/')
+ {
+ start += 2;
+ }
+
+ if (etag.len > (size_t) (end - start)) {
+ return 0;
+ }
+
+ if (ngx_strncmp(start, etag.data, etag.len) != 0) {
+ goto skip;
+ }
+
+ start += etag.len;
+
+ while (start < end) {
+ ch = *start;
+
+ if (ch == ' ' || ch == '\t') {
+ start++;
+ continue;
+ }
+
+ break;
+ }
+
+ if (start == end || *start == ',') {
+ return 1;
+ }
+
+ skip:
+
+ while (start < end && *start != ',') { start++; }
+ while (start < end) {
+ ch = *start;
+
+ if (ch == ' ' || ch == '\t' || ch == ',') {
+ start++;
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static ngx_int_t
+ngx_http_not_modified_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_not_modified_header_filter;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_proxy_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_proxy_module.c
new file mode 100644
index 00000000000..52c63e1381f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_proxy_module.c
@@ -0,0 +1,3920 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct ngx_http_proxy_rewrite_s ngx_http_proxy_rewrite_t;
+
+typedef ngx_int_t (*ngx_http_proxy_rewrite_pt)(ngx_http_request_t *r,
+ ngx_table_elt_t *h, size_t prefix, size_t len,
+ ngx_http_proxy_rewrite_t *pr);
+
+struct ngx_http_proxy_rewrite_s {
+ ngx_http_proxy_rewrite_pt handler;
+
+ union {
+ ngx_http_complex_value_t complex;
+#if (NGX_PCRE)
+ ngx_http_regex_t *regex;
+#endif
+ } pattern;
+
+ ngx_http_complex_value_t replacement;
+};
+
+
+typedef struct {
+ ngx_str_t key_start;
+ ngx_str_t schema;
+ ngx_str_t host_header;
+ ngx_str_t port;
+ ngx_str_t uri;
+} ngx_http_proxy_vars_t;
+
+
+typedef struct {
+ ngx_http_upstream_conf_t upstream;
+
+ ngx_array_t *flushes;
+ ngx_array_t *body_set_len;
+ ngx_array_t *body_set;
+ ngx_array_t *headers_set_len;
+ ngx_array_t *headers_set;
+ ngx_hash_t headers_set_hash;
+
+ ngx_array_t *headers_source;
+
+ ngx_array_t *proxy_lengths;
+ ngx_array_t *proxy_values;
+
+ ngx_array_t *redirects;
+ ngx_array_t *cookie_domains;
+ ngx_array_t *cookie_paths;
+
+ ngx_str_t body_source;
+
+ ngx_str_t method;
+ ngx_str_t location;
+ ngx_str_t url;
+
+#if (NGX_HTTP_CACHE)
+ ngx_http_complex_value_t cache_key;
+#endif
+
+ ngx_http_proxy_vars_t vars;
+
+ ngx_flag_t redirect;
+
+ ngx_uint_t http_version;
+
+ ngx_uint_t headers_hash_max_size;
+ ngx_uint_t headers_hash_bucket_size;
+
+#if (NGX_HTTP_SSL)
+ ngx_uint_t ssl;
+ ngx_uint_t ssl_protocols;
+ ngx_str_t ssl_ciphers;
+ ngx_uint_t ssl_verify_depth;
+ ngx_str_t ssl_trusted_certificate;
+ ngx_str_t ssl_crl;
+#endif
+} ngx_http_proxy_loc_conf_t;
+
+
+typedef struct {
+ ngx_http_status_t status;
+ ngx_http_chunked_t chunked;
+ ngx_http_proxy_vars_t vars;
+ off_t internal_body_length;
+
+ ngx_uint_t head; /* unsigned head:1 */
+} ngx_http_proxy_ctx_t;
+
+
+static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r,
+ ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf);
+#if (NGX_HTTP_CACHE)
+static ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r);
+#endif
+static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r);
+static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r);
+static ngx_int_t ngx_http_proxy_input_filter_init(void *data);
+static ngx_int_t ngx_http_proxy_copy_filter(ngx_event_pipe_t *p,
+ ngx_buf_t *buf);
+static ngx_int_t ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p,
+ ngx_buf_t *buf);
+static ngx_int_t ngx_http_proxy_non_buffered_copy_filter(void *data,
+ ssize_t bytes);
+static ngx_int_t ngx_http_proxy_non_buffered_chunked_filter(void *data,
+ ssize_t bytes);
+static void ngx_http_proxy_abort_request(ngx_http_request_t *r);
+static void ngx_http_proxy_finalize_request(ngx_http_request_t *r,
+ ngx_int_t rc);
+
+static ngx_int_t ngx_http_proxy_host_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_proxy_port_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t
+ ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t
+ ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r,
+ ngx_table_elt_t *h, size_t prefix);
+static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r,
+ ngx_table_elt_t *h);
+static ngx_int_t ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r,
+ ngx_table_elt_t *h, u_char *value, ngx_array_t *rewrites);
+static ngx_int_t ngx_http_proxy_rewrite(ngx_http_request_t *r,
+ ngx_table_elt_t *h, size_t prefix, size_t len, ngx_str_t *replacement);
+
+static ngx_int_t ngx_http_proxy_add_variables(ngx_conf_t *cf);
+static void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static ngx_int_t ngx_http_proxy_merge_headers(ngx_conf_t *cf,
+ ngx_http_proxy_loc_conf_t *conf, ngx_http_proxy_loc_conf_t *prev);
+
+static char *ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+#if (NGX_HTTP_CACHE)
+static char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+#endif
+
+static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data);
+
+static ngx_int_t ngx_http_proxy_rewrite_regex(ngx_conf_t *cf,
+ ngx_http_proxy_rewrite_t *pr, ngx_str_t *regex, ngx_uint_t caseless);
+
+#if (NGX_HTTP_SSL)
+static ngx_int_t ngx_http_proxy_set_ssl(ngx_conf_t *cf,
+ ngx_http_proxy_loc_conf_t *plcf);
+#endif
+static void ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v);
+
+
+static ngx_conf_post_t ngx_http_proxy_lowat_post =
+ { ngx_http_proxy_lowat_check };
+
+
+static ngx_conf_bitmask_t ngx_http_proxy_next_upstream_masks[] = {
+ { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
+ { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
+ { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
+ { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },
+ { ngx_string("http_502"), NGX_HTTP_UPSTREAM_FT_HTTP_502 },
+ { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
+ { ngx_string("http_504"), NGX_HTTP_UPSTREAM_FT_HTTP_504 },
+ { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },
+ { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
+ { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },
+ { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
+ { ngx_null_string, 0 }
+};
+
+
+#if (NGX_HTTP_SSL)
+
+static ngx_conf_bitmask_t ngx_http_proxy_ssl_protocols[] = {
+ { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
+ { ngx_string("SSLv3"), NGX_SSL_SSLv3 },
+ { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
+ { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
+ { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
+ { ngx_null_string, 0 }
+};
+
+#endif
+
+
+static ngx_conf_enum_t ngx_http_proxy_http_version[] = {
+ { ngx_string("1.0"), NGX_HTTP_VERSION_10 },
+ { ngx_string("1.1"), NGX_HTTP_VERSION_11 },
+ { ngx_null_string, 0 }
+};
+
+
+ngx_module_t ngx_http_proxy_module;
+
+
+static ngx_command_t ngx_http_proxy_commands[] = {
+
+ { ngx_string("proxy_pass"),
+ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
+ ngx_http_proxy_pass,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("proxy_redirect"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
+ ngx_http_proxy_redirect,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("proxy_cookie_domain"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
+ ngx_http_proxy_cookie_domain,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("proxy_cookie_path"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
+ ngx_http_proxy_cookie_path,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("proxy_store"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_proxy_store,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("proxy_store_access"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
+ ngx_conf_set_access_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.store_access),
+ NULL },
+
+ { ngx_string("proxy_buffering"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.buffering),
+ NULL },
+
+ { ngx_string("proxy_ignore_client_abort"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_client_abort),
+ NULL },
+
+ { ngx_string("proxy_bind"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_upstream_bind_set_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.local),
+ NULL },
+
+ { ngx_string("proxy_connect_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.connect_timeout),
+ NULL },
+
+ { ngx_string("proxy_send_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.send_timeout),
+ NULL },
+
+ { ngx_string("proxy_send_lowat"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.send_lowat),
+ &ngx_http_proxy_lowat_post },
+
+ { ngx_string("proxy_intercept_errors"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.intercept_errors),
+ NULL },
+
+ { ngx_string("proxy_set_header"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_conf_set_keyval_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, headers_source),
+ NULL },
+
+ { ngx_string("proxy_headers_hash_max_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, headers_hash_max_size),
+ NULL },
+
+ { ngx_string("proxy_headers_hash_bucket_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, headers_hash_bucket_size),
+ NULL },
+
+ { ngx_string("proxy_set_body"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, body_source),
+ NULL },
+
+ { ngx_string("proxy_method"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, method),
+ NULL },
+
+ { ngx_string("proxy_pass_request_headers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_headers),
+ NULL },
+
+ { ngx_string("proxy_pass_request_body"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_body),
+ NULL },
+
+ { ngx_string("proxy_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.buffer_size),
+ NULL },
+
+ { ngx_string("proxy_read_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.read_timeout),
+ NULL },
+
+ { ngx_string("proxy_buffers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_conf_set_bufs_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.bufs),
+ NULL },
+
+ { ngx_string("proxy_busy_buffers_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.busy_buffers_size_conf),
+ NULL },
+
+#if (NGX_HTTP_CACHE)
+
+ { ngx_string("proxy_cache"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_proxy_cache,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("proxy_cache_key"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_proxy_cache_key,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("proxy_cache_path"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
+ ngx_http_file_cache_set_slot,
+ 0,
+ 0,
+ &ngx_http_proxy_module },
+
+ { ngx_string("proxy_cache_bypass"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_set_predicate_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_bypass),
+ NULL },
+
+ { ngx_string("proxy_no_cache"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_set_predicate_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.no_cache),
+ NULL },
+
+ { ngx_string("proxy_cache_valid"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_file_cache_valid_set_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_valid),
+ NULL },
+
+ { ngx_string("proxy_cache_min_uses"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_min_uses),
+ NULL },
+
+ { ngx_string("proxy_cache_use_stale"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_use_stale),
+ &ngx_http_proxy_next_upstream_masks },
+
+ { ngx_string("proxy_cache_methods"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_methods),
+ &ngx_http_upstream_cache_method_mask },
+
+ { ngx_string("proxy_cache_lock"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_lock),
+ NULL },
+
+ { ngx_string("proxy_cache_lock_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_lock_timeout),
+ NULL },
+
+ { ngx_string("proxy_cache_revalidate"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_revalidate),
+ NULL },
+
+#endif
+
+ { ngx_string("proxy_temp_path"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
+ ngx_conf_set_path_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.temp_path),
+ NULL },
+
+ { ngx_string("proxy_max_temp_file_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.max_temp_file_size_conf),
+ NULL },
+
+ { ngx_string("proxy_temp_file_write_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.temp_file_write_size_conf),
+ NULL },
+
+ { ngx_string("proxy_next_upstream"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.next_upstream),
+ &ngx_http_proxy_next_upstream_masks },
+
+ { ngx_string("proxy_pass_header"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_headers),
+ NULL },
+
+ { ngx_string("proxy_hide_header"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.hide_headers),
+ NULL },
+
+ { ngx_string("proxy_ignore_headers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_headers),
+ &ngx_http_upstream_ignore_headers_masks },
+
+ { ngx_string("proxy_http_version"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, http_version),
+ &ngx_http_proxy_http_version },
+
+#if (NGX_HTTP_SSL)
+
+ { ngx_string("proxy_ssl_session_reuse"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_session_reuse),
+ NULL },
+
+ { ngx_string("proxy_ssl_protocols"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, ssl_protocols),
+ &ngx_http_proxy_ssl_protocols },
+
+ { ngx_string("proxy_ssl_ciphers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, ssl_ciphers),
+ NULL },
+
+ { ngx_string("proxy_ssl_name"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_set_complex_value_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_name),
+ NULL },
+
+ { ngx_string("proxy_ssl_server_name"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_server_name),
+ NULL },
+
+ { ngx_string("proxy_ssl_verify"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_verify),
+ NULL },
+
+ { ngx_string("proxy_ssl_verify_depth"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, ssl_verify_depth),
+ NULL },
+
+ { ngx_string("proxy_ssl_trusted_certificate"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, ssl_trusted_certificate),
+ NULL },
+
+ { ngx_string("proxy_ssl_crl"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_proxy_loc_conf_t, ssl_crl),
+ NULL },
+
+#endif
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_proxy_module_ctx = {
+ ngx_http_proxy_add_variables, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_proxy_create_loc_conf, /* create location configuration */
+ ngx_http_proxy_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_proxy_module = {
+ NGX_MODULE_V1,
+ &ngx_http_proxy_module_ctx, /* module context */
+ ngx_http_proxy_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static char ngx_http_proxy_version[] = " HTTP/1.0" CRLF;
+static char ngx_http_proxy_version_11[] = " HTTP/1.1" CRLF;
+
+
+static ngx_keyval_t ngx_http_proxy_headers[] = {
+ { ngx_string("Host"), ngx_string("$proxy_host") },
+ { ngx_string("Connection"), ngx_string("close") },
+ { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
+ { ngx_string("Transfer-Encoding"), ngx_string("") },
+ { ngx_string("Keep-Alive"), ngx_string("") },
+ { ngx_string("Expect"), ngx_string("") },
+ { ngx_string("Upgrade"), ngx_string("") },
+ { ngx_null_string, ngx_null_string }
+};
+
+
+static ngx_str_t ngx_http_proxy_hide_headers[] = {
+ ngx_string("Date"),
+ ngx_string("Server"),
+ ngx_string("X-Pad"),
+ ngx_string("X-Accel-Expires"),
+ ngx_string("X-Accel-Redirect"),
+ ngx_string("X-Accel-Limit-Rate"),
+ ngx_string("X-Accel-Buffering"),
+ ngx_string("X-Accel-Charset"),
+ ngx_null_string
+};
+
+
+#if (NGX_HTTP_CACHE)
+
+static ngx_keyval_t ngx_http_proxy_cache_headers[] = {
+ { ngx_string("Host"), ngx_string("$proxy_host") },
+ { ngx_string("Connection"), ngx_string("close") },
+ { ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
+ { ngx_string("Transfer-Encoding"), ngx_string("") },
+ { ngx_string("Keep-Alive"), ngx_string("") },
+ { ngx_string("Expect"), ngx_string("") },
+ { ngx_string("Upgrade"), ngx_string("") },
+ { ngx_string("If-Modified-Since"),
+ ngx_string("$upstream_cache_last_modified") },
+ { ngx_string("If-Unmodified-Since"), ngx_string("") },
+ { ngx_string("If-None-Match"), ngx_string("$upstream_cache_etag") },
+ { ngx_string("If-Match"), ngx_string("") },
+ { ngx_string("Range"), ngx_string("") },
+ { ngx_string("If-Range"), ngx_string("") },
+ { ngx_null_string, ngx_null_string }
+};
+
+#endif
+
+
+static ngx_http_variable_t ngx_http_proxy_vars[] = {
+
+ { ngx_string("proxy_host"), NULL, ngx_http_proxy_host_variable, 0,
+ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+ { ngx_string("proxy_port"), NULL, ngx_http_proxy_port_variable, 0,
+ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+ { ngx_string("proxy_add_x_forwarded_for"), NULL,
+ ngx_http_proxy_add_x_forwarded_for_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },
+
+#if 0
+ { ngx_string("proxy_add_via"), NULL, NULL, 0, NGX_HTTP_VAR_NOHASH, 0 },
+#endif
+
+ { ngx_string("proxy_internal_body_length"), NULL,
+ ngx_http_proxy_internal_body_length_variable, 0,
+ NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+ { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
+
+
+static ngx_path_init_t ngx_http_proxy_temp_path = {
+ ngx_string(NGX_HTTP_PROXY_TEMP_PATH), { 1, 2, 0 }
+};
+
+
+static ngx_int_t
+ngx_http_proxy_handler(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_http_upstream_t *u;
+ ngx_http_proxy_ctx_t *ctx;
+ ngx_http_proxy_loc_conf_t *plcf;
+
+ if (ngx_http_upstream_create(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_proxy_module);
+
+ plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
+
+ u = r->upstream;
+
+ if (plcf->proxy_lengths == NULL) {
+ ctx->vars = plcf->vars;
+ u->schema = plcf->vars.schema;
+#if (NGX_HTTP_SSL)
+ u->ssl = (plcf->upstream.ssl != NULL);
+#endif
+
+ } else {
+ if (ngx_http_proxy_eval(r, ctx, plcf) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_module;
+
+ u->conf = &plcf->upstream;
+
+#if (NGX_HTTP_CACHE)
+ u->create_key = ngx_http_proxy_create_key;
+#endif
+ u->create_request = ngx_http_proxy_create_request;
+ u->reinit_request = ngx_http_proxy_reinit_request;
+ u->process_header = ngx_http_proxy_process_status_line;
+ u->abort_request = ngx_http_proxy_abort_request;
+ u->finalize_request = ngx_http_proxy_finalize_request;
+ r->state = 0;
+
+ if (plcf->redirects) {
+ u->rewrite_redirect = ngx_http_proxy_rewrite_redirect;
+ }
+
+ if (plcf->cookie_domains || plcf->cookie_paths) {
+ u->rewrite_cookie = ngx_http_proxy_rewrite_cookie;
+ }
+
+ u->buffering = plcf->upstream.buffering;
+
+ u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
+ if (u->pipe == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ u->pipe->input_filter = ngx_http_proxy_copy_filter;
+ u->pipe->input_ctx = r;
+
+ u->input_filter_init = ngx_http_proxy_input_filter_init;
+ u->input_filter = ngx_http_proxy_non_buffered_copy_filter;
+ u->input_filter_ctx = r;
+
+ u->accel = 1;
+
+ rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ return rc;
+ }
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,
+ ngx_http_proxy_loc_conf_t *plcf)
+{
+ u_char *p;
+ size_t add;
+ u_short port;
+ ngx_str_t proxy;
+ ngx_url_t url;
+ ngx_http_upstream_t *u;
+
+ if (ngx_http_script_run(r, &proxy, plcf->proxy_lengths->elts, 0,
+ plcf->proxy_values->elts)
+ == NULL)
+ {
+ return NGX_ERROR;
+ }
+
+ if (proxy.len > 7
+ && ngx_strncasecmp(proxy.data, (u_char *) "http://", 7) == 0)
+ {
+ add = 7;
+ port = 80;
+
+#if (NGX_HTTP_SSL)
+
+ } else if (proxy.len > 8
+ && ngx_strncasecmp(proxy.data, (u_char *) "https://", 8) == 0)
+ {
+ add = 8;
+ port = 443;
+ r->upstream->ssl = 1;
+
+#endif
+
+ } else {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid URL prefix in \"%V\"", &proxy);
+ return NGX_ERROR;
+ }
+
+ u = r->upstream;
+
+ u->schema.len = add;
+ u->schema.data = proxy.data;
+
+ ngx_memzero(&url, sizeof(ngx_url_t));
+
+ url.url.len = proxy.len - add;
+ url.url.data = proxy.data + add;
+ url.default_port = port;
+ url.uri_part = 1;
+ url.no_resolve = 1;
+
+ if (ngx_parse_url(r->pool, &url) != NGX_OK) {
+ if (url.err) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "%s in upstream \"%V\"", url.err, &url.url);
+ }
+
+ return NGX_ERROR;
+ }
+
+ if (url.uri.len) {
+ if (url.uri.data[0] == '?') {
+ p = ngx_pnalloc(r->pool, url.uri.len + 1);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ *p++ = '/';
+ ngx_memcpy(p, url.uri.data, url.uri.len);
+
+ url.uri.len++;
+ url.uri.data = p - 1;
+ }
+ }
+
+ ctx->vars.key_start = u->schema;
+
+ ngx_http_proxy_set_vars(&url, &ctx->vars);
+
+ u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
+ if (u->resolved == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (url.addrs && url.addrs[0].sockaddr) {
+ u->resolved->sockaddr = url.addrs[0].sockaddr;
+ u->resolved->socklen = url.addrs[0].socklen;
+ u->resolved->naddrs = 1;
+ u->resolved->host = url.addrs[0].name;
+
+ } else {
+ u->resolved->host = url.host;
+ u->resolved->port = (in_port_t) (url.no_port ? port : url.port);
+ u->resolved->no_port = url.no_port;
+ }
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HTTP_CACHE)
+
+static ngx_int_t
+ngx_http_proxy_create_key(ngx_http_request_t *r)
+{
+ size_t len, loc_len;
+ u_char *p;
+ uintptr_t escape;
+ ngx_str_t *key;
+ ngx_http_upstream_t *u;
+ ngx_http_proxy_ctx_t *ctx;
+ ngx_http_proxy_loc_conf_t *plcf;
+
+ u = r->upstream;
+
+ plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ key = ngx_array_push(&r->cache->keys);
+ if (key == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (plcf->cache_key.value.data) {
+
+ if (ngx_http_complex_value(r, &plcf->cache_key, key) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+
+ *key = ctx->vars.key_start;
+
+ key = ngx_array_push(&r->cache->keys);
+ if (key == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (plcf->proxy_lengths && ctx->vars.uri.len) {
+
+ *key = ctx->vars.uri;
+ u->uri = ctx->vars.uri;
+
+ return NGX_OK;
+
+ } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main)
+ {
+ *key = r->unparsed_uri;
+ u->uri = r->unparsed_uri;
+
+ return NGX_OK;
+ }
+
+ loc_len = (r->valid_location && ctx->vars.uri.len) ? plcf->location.len : 0;
+
+ if (r->quoted_uri || r->internal) {
+ escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
+ r->uri.len - loc_len, NGX_ESCAPE_URI);
+ } else {
+ escape = 0;
+ }
+
+ len = ctx->vars.uri.len + r->uri.len - loc_len + escape
+ + sizeof("?") - 1 + r->args.len;
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ key->data = p;
+
+ if (r->valid_location) {
+ p = ngx_copy(p, ctx->vars.uri.data, ctx->vars.uri.len);
+ }
+
+ if (escape) {
+ ngx_escape_uri(p, r->uri.data + loc_len,
+ r->uri.len - loc_len, NGX_ESCAPE_URI);
+ p += r->uri.len - loc_len + escape;
+
+ } else {
+ p = ngx_copy(p, r->uri.data + loc_len, r->uri.len - loc_len);
+ }
+
+ if (r->args.len > 0) {
+ *p++ = '?';
+ p = ngx_copy(p, r->args.data, r->args.len);
+ }
+
+ key->len = p - key->data;
+ u->uri = *key;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_proxy_create_request(ngx_http_request_t *r)
+{
+ size_t len, uri_len, loc_len, body_len;
+ uintptr_t escape;
+ ngx_buf_t *b;
+ ngx_str_t method;
+ ngx_uint_t i, unparsed_uri;
+ ngx_chain_t *cl, *body;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *header;
+ ngx_http_upstream_t *u;
+ ngx_http_proxy_ctx_t *ctx;
+ ngx_http_script_code_pt code;
+ ngx_http_script_engine_t e, le;
+ ngx_http_proxy_loc_conf_t *plcf;
+ ngx_http_script_len_code_pt lcode;
+
+ u = r->upstream;
+
+ plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
+
+ if (u->method.len) {
+ /* HEAD was changed to GET to cache response */
+ method = u->method;
+ method.len++;
+
+ } else if (plcf->method.len) {
+ method = plcf->method;
+
+ } else {
+ method = r->method_name;
+ method.len++;
+ }
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (method.len == 5
+ && ngx_strncasecmp(method.data, (u_char *) "HEAD ", 5) == 0)
+ {
+ ctx->head = 1;
+ }
+
+ len = method.len + sizeof(ngx_http_proxy_version) - 1 + sizeof(CRLF) - 1;
+
+ escape = 0;
+ loc_len = 0;
+ unparsed_uri = 0;
+
+ if (plcf->proxy_lengths && ctx->vars.uri.len) {
+ uri_len = ctx->vars.uri.len;
+
+ } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main)
+ {
+ unparsed_uri = 1;
+ uri_len = r->unparsed_uri.len;
+
+ } else {
+ loc_len = (r->valid_location && ctx->vars.uri.len) ?
+ plcf->location.len : 0;
+
+ if (r->quoted_uri || r->space_in_uri || r->internal) {
+ escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
+ r->uri.len - loc_len, NGX_ESCAPE_URI);
+ }
+
+ uri_len = ctx->vars.uri.len + r->uri.len - loc_len + escape
+ + sizeof("?") - 1 + r->args.len;
+ }
+
+ if (uri_len == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "zero length URI to proxy");
+ return NGX_ERROR;
+ }
+
+ len += uri_len;
+
+ ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
+
+ ngx_http_script_flush_no_cacheable_variables(r, plcf->flushes);
+
+ if (plcf->body_set_len) {
+ le.ip = plcf->body_set_len->elts;
+ le.request = r;
+ le.flushed = 1;
+ body_len = 0;
+
+ while (*(uintptr_t *) le.ip) {
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ body_len += lcode(&le);
+ }
+
+ ctx->internal_body_length = body_len;
+ len += body_len;
+
+ } else {
+ ctx->internal_body_length = r->headers_in.content_length_n;
+ }
+
+ le.ip = plcf->headers_set_len->elts;
+ le.request = r;
+ le.flushed = 1;
+
+ while (*(uintptr_t *) le.ip) {
+ while (*(uintptr_t *) le.ip) {
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ len += lcode(&le);
+ }
+ le.ip += sizeof(uintptr_t);
+ }
+
+
+ if (plcf->upstream.pass_request_headers) {
+ part = &r->headers_in.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (ngx_hash_find(&plcf->headers_set_hash, header[i].hash,
+ header[i].lowcase_key, header[i].key.len))
+ {
+ continue;
+ }
+
+ len += header[i].key.len + sizeof(": ") - 1
+ + header[i].value.len + sizeof(CRLF) - 1;
+ }
+ }
+
+
+ b = ngx_create_temp_buf(r->pool, len);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+
+
+ /* the request line */
+
+ b->last = ngx_copy(b->last, method.data, method.len);
+
+ u->uri.data = b->last;
+
+ if (plcf->proxy_lengths && ctx->vars.uri.len) {
+ b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);
+
+ } else if (unparsed_uri) {
+ b->last = ngx_copy(b->last, r->unparsed_uri.data, r->unparsed_uri.len);
+
+ } else {
+ if (r->valid_location) {
+ b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);
+ }
+
+ if (escape) {
+ ngx_escape_uri(b->last, r->uri.data + loc_len,
+ r->uri.len - loc_len, NGX_ESCAPE_URI);
+ b->last += r->uri.len - loc_len + escape;
+
+ } else {
+ b->last = ngx_copy(b->last, r->uri.data + loc_len,
+ r->uri.len - loc_len);
+ }
+
+ if (r->args.len > 0) {
+ *b->last++ = '?';
+ b->last = ngx_copy(b->last, r->args.data, r->args.len);
+ }
+ }
+
+ u->uri.len = b->last - u->uri.data;
+
+ if (plcf->http_version == NGX_HTTP_VERSION_11) {
+ b->last = ngx_cpymem(b->last, ngx_http_proxy_version_11,
+ sizeof(ngx_http_proxy_version_11) - 1);
+
+ } else {
+ b->last = ngx_cpymem(b->last, ngx_http_proxy_version,
+ sizeof(ngx_http_proxy_version) - 1);
+ }
+
+ ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
+
+ e.ip = plcf->headers_set->elts;
+ e.pos = b->last;
+ e.request = r;
+ e.flushed = 1;
+
+ le.ip = plcf->headers_set_len->elts;
+
+ while (*(uintptr_t *) le.ip) {
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+
+ /* skip the header line name length */
+ (void) lcode(&le);
+
+ if (*(ngx_http_script_len_code_pt *) le.ip) {
+
+ for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) {
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ }
+
+ e.skip = (len == sizeof(CRLF) - 1) ? 1 : 0;
+
+ } else {
+ e.skip = 0;
+ }
+
+ le.ip += sizeof(uintptr_t);
+
+ while (*(uintptr_t *) e.ip) {
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) &e);
+ }
+ e.ip += sizeof(uintptr_t);
+ }
+
+ b->last = e.pos;
+
+
+ if (plcf->upstream.pass_request_headers) {
+ part = &r->headers_in.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (ngx_hash_find(&plcf->headers_set_hash, header[i].hash,
+ header[i].lowcase_key, header[i].key.len))
+ {
+ continue;
+ }
+
+ b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
+
+ *b->last++ = ':'; *b->last++ = ' ';
+
+ b->last = ngx_copy(b->last, header[i].value.data,
+ header[i].value.len);
+
+ *b->last++ = CR; *b->last++ = LF;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy header: \"%V: %V\"",
+ &header[i].key, &header[i].value);
+ }
+ }
+
+
+ /* add "\r\n" at the header end */
+ *b->last++ = CR; *b->last++ = LF;
+
+ if (plcf->body_set) {
+ e.ip = plcf->body_set->elts;
+ e.pos = b->last;
+
+ while (*(uintptr_t *) e.ip) {
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) &e);
+ }
+
+ b->last = e.pos;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy header:%N\"%*s\"",
+ (size_t) (b->last - b->pos), b->pos);
+
+ if (plcf->body_set == NULL && plcf->upstream.pass_request_body) {
+
+ body = u->request_bufs;
+ u->request_bufs = cl;
+
+ while (body) {
+ b = ngx_alloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
+
+ cl->next = ngx_alloc_chain_link(r->pool);
+ if (cl->next == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = cl->next;
+ cl->buf = b;
+
+ body = body->next;
+ }
+
+ } else {
+ u->request_bufs = cl;
+ }
+
+ b->flush = 1;
+ cl->next = NULL;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_reinit_request(ngx_http_request_t *r)
+{
+ ngx_http_proxy_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (ctx == NULL) {
+ return NGX_OK;
+ }
+
+ ctx->status.code = 0;
+ ctx->status.count = 0;
+ ctx->status.start = NULL;
+ ctx->status.end = NULL;
+ ctx->chunked.state = 0;
+
+ r->upstream->process_header = ngx_http_proxy_process_status_line;
+ r->upstream->pipe->input_filter = ngx_http_proxy_copy_filter;
+ r->upstream->input_filter = ngx_http_proxy_non_buffered_copy_filter;
+ r->state = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_process_status_line(ngx_http_request_t *r)
+{
+ size_t len;
+ ngx_int_t rc;
+ ngx_http_upstream_t *u;
+ ngx_http_proxy_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ u = r->upstream;
+
+ rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status);
+
+ if (rc == NGX_AGAIN) {
+ return rc;
+ }
+
+ if (rc == NGX_ERROR) {
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache) {
+ r->http_version = NGX_HTTP_VERSION_9;
+ return NGX_OK;
+ }
+
+#endif
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent no valid HTTP/1.0 header");
+
+#if 0
+ if (u->accel) {
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+#endif
+
+ r->http_version = NGX_HTTP_VERSION_9;
+ u->state->status = NGX_HTTP_OK;
+ u->headers_in.connection_close = 1;
+
+ return NGX_OK;
+ }
+
+ if (u->state && u->state->status == 0) {
+ u->state->status = ctx->status.code;
+ }
+
+ u->headers_in.status_n = ctx->status.code;
+
+ len = ctx->status.end - ctx->status.start;
+ u->headers_in.status_line.len = len;
+
+ u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);
+ if (u->headers_in.status_line.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(u->headers_in.status_line.data, ctx->status.start, len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy status %ui \"%V\"",
+ u->headers_in.status_n, &u->headers_in.status_line);
+
+ if (ctx->status.http_version < NGX_HTTP_VERSION_11) {
+ u->headers_in.connection_close = 1;
+ }
+
+ u->process_header = ngx_http_proxy_process_header;
+
+ return ngx_http_proxy_process_header(r);
+}
+
+
+static ngx_int_t
+ngx_http_proxy_process_header(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_table_elt_t *h;
+ ngx_http_upstream_t *u;
+ ngx_http_proxy_ctx_t *ctx;
+ ngx_http_upstream_header_t *hh;
+ ngx_http_upstream_main_conf_t *umcf;
+
+ umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
+
+ for ( ;; ) {
+
+ rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);
+
+ if (rc == NGX_OK) {
+
+ /* a header line has been parsed successfully */
+
+ h = ngx_list_push(&r->upstream->headers_in.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = r->header_hash;
+
+ h->key.len = r->header_name_end - r->header_name_start;
+ h->value.len = r->header_end - r->header_start;
+
+ h->key.data = ngx_pnalloc(r->pool,
+ h->key.len + 1 + h->value.len + 1 + h->key.len);
+ if (h->key.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->value.data = h->key.data + h->key.len + 1;
+ h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;
+
+ ngx_memcpy(h->key.data, r->header_name_start, h->key.len);
+ h->key.data[h->key.len] = '\0';
+ ngx_memcpy(h->value.data, r->header_start, h->value.len);
+ h->value.data[h->value.len] = '\0';
+
+ if (h->key.len == r->lowcase_index) {
+ ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
+
+ } else {
+ ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
+ }
+
+ hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
+ h->lowcase_key, h->key.len);
+
+ if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy header: \"%V: %V\"",
+ &h->key, &h->value);
+
+ continue;
+ }
+
+ if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
+
+ /* a whole header has been parsed successfully */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy header done");
+
+ /*
+ * if no "Server" and "Date" in header line,
+ * then add the special empty headers
+ */
+
+ if (r->upstream->headers_in.server == NULL) {
+ h = ngx_list_push(&r->upstream->headers_in.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(
+ ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r');
+
+ ngx_str_set(&h->key, "Server");
+ ngx_str_null(&h->value);
+ h->lowcase_key = (u_char *) "server";
+ }
+
+ if (r->upstream->headers_in.date == NULL) {
+ h = ngx_list_push(&r->upstream->headers_in.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e');
+
+ ngx_str_set(&h->key, "Date");
+ ngx_str_null(&h->value);
+ h->lowcase_key = (u_char *) "date";
+ }
+
+ /* clear content length if response is chunked */
+
+ u = r->upstream;
+
+ if (u->headers_in.chunked) {
+ u->headers_in.content_length_n = -1;
+ }
+
+ /*
+ * set u->keepalive if response has no body; this allows to keep
+ * connections alive in case of r->header_only or X-Accel-Redirect
+ */
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
+ || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
+ || ctx->head
+ || (!u->headers_in.chunked
+ && u->headers_in.content_length_n == 0))
+ {
+ u->keepalive = !u->headers_in.connection_close;
+ }
+
+ if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS) {
+ u->keepalive = 0;
+
+ if (r->headers_in.upgrade) {
+ u->upgrade = 1;
+ }
+ }
+
+ return NGX_OK;
+ }
+
+ if (rc == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ /* there was error while a header line parsing */
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent invalid header");
+
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+}
+
+
+static ngx_int_t
+ngx_http_proxy_input_filter_init(void *data)
+{
+ ngx_http_request_t *r = data;
+ ngx_http_upstream_t *u;
+ ngx_http_proxy_ctx_t *ctx;
+
+ u = r->upstream;
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy filter init s:%d h:%d c:%d l:%O",
+ u->headers_in.status_n, ctx->head, u->headers_in.chunked,
+ u->headers_in.content_length_n);
+
+ /* as per RFC2616, 4.4 Message Length */
+
+ if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
+ || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
+ || ctx->head)
+ {
+ /* 1xx, 204, and 304 and replies to HEAD requests */
+ /* no 1xx since we don't send Expect and Upgrade */
+
+ u->pipe->length = 0;
+ u->length = 0;
+ u->keepalive = !u->headers_in.connection_close;
+
+ } else if (u->headers_in.chunked) {
+ /* chunked */
+
+ u->pipe->input_filter = ngx_http_proxy_chunked_filter;
+ u->pipe->length = 3; /* "0" LF LF */
+
+ u->input_filter = ngx_http_proxy_non_buffered_chunked_filter;
+ u->length = 1;
+
+ } else if (u->headers_in.content_length_n == 0) {
+ /* empty body: special case as filter won't be called */
+
+ u->pipe->length = 0;
+ u->length = 0;
+ u->keepalive = !u->headers_in.connection_close;
+
+ } else {
+ /* content length or connection close */
+
+ u->pipe->length = u->headers_in.content_length_n;
+ u->length = u->headers_in.content_length_n;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
+{
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+ ngx_http_request_t *r;
+
+ if (buf->pos == buf->last) {
+ return NGX_OK;
+ }
+
+ cl = ngx_chain_get_free_buf(p->pool, &p->free);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = cl->buf;
+
+ ngx_memcpy(b, buf, sizeof(ngx_buf_t));
+ b->shadow = buf;
+ b->tag = p->tag;
+ b->last_shadow = 1;
+ b->recycled = 1;
+ buf->shadow = b;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, "input buf #%d", b->num);
+
+ if (p->in) {
+ *p->last_in = cl;
+ } else {
+ p->in = cl;
+ }
+ p->last_in = &cl->next;
+
+ if (p->length == -1) {
+ return NGX_OK;
+ }
+
+ p->length -= b->last - b->pos;
+
+ if (p->length == 0) {
+ r = p->input_ctx;
+ p->upstream_done = 1;
+ r->upstream->keepalive = !r->upstream->headers_in.connection_close;
+
+ } else if (p->length < 0) {
+ r = p->input_ctx;
+ p->upstream_done = 1;
+
+ ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+ "upstream sent more data than specified in "
+ "\"Content-Length\" header");
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
+{
+ ngx_int_t rc;
+ ngx_buf_t *b, **prev;
+ ngx_chain_t *cl;
+ ngx_http_request_t *r;
+ ngx_http_proxy_ctx_t *ctx;
+
+ if (buf->pos == buf->last) {
+ return NGX_OK;
+ }
+
+ r = p->input_ctx;
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = NULL;
+ prev = &buf->shadow;
+
+ for ( ;; ) {
+
+ rc = ngx_http_parse_chunked(r, buf, &ctx->chunked);
+
+ if (rc == NGX_OK) {
+
+ /* a chunk has been parsed successfully */
+
+ cl = ngx_chain_get_free_buf(p->pool, &p->free);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = cl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->pos = buf->pos;
+ b->start = buf->start;
+ b->end = buf->end;
+ b->tag = p->tag;
+ b->temporary = 1;
+ b->recycled = 1;
+
+ *prev = b;
+ prev = &b->shadow;
+
+ if (p->in) {
+ *p->last_in = cl;
+ } else {
+ p->in = cl;
+ }
+ p->last_in = &cl->next;
+
+ /* STUB */ b->num = buf->num;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "input buf #%d %p", b->num, b->pos);
+
+ if (buf->last - buf->pos >= ctx->chunked.size) {
+
+ buf->pos += (size_t) ctx->chunked.size;
+ b->last = buf->pos;
+ ctx->chunked.size = 0;
+
+ continue;
+ }
+
+ ctx->chunked.size -= buf->last - buf->pos;
+ buf->pos = buf->last;
+ b->last = buf->last;
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+
+ /* a whole response has been parsed successfully */
+
+ p->upstream_done = 1;
+ r->upstream->keepalive = !r->upstream->headers_in.connection_close;
+
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+
+ /* set p->length, minimal amount of data we want to see */
+
+ p->length = ctx->chunked.length;
+
+ break;
+ }
+
+ /* invalid response */
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent invalid chunked response");
+
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy chunked state %d, length %d",
+ ctx->chunked.state, p->length);
+
+ if (b) {
+ b->shadow = buf;
+ b->last_shadow = 1;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
+ "input buf %p %z", b->pos, b->last - b->pos);
+
+ return NGX_OK;
+ }
+
+ /* there is no data record in the buf, add it to free chain */
+
+ if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_non_buffered_copy_filter(void *data, ssize_t bytes)
+{
+ ngx_http_request_t *r = data;
+
+ ngx_buf_t *b;
+ ngx_chain_t *cl, **ll;
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+
+ for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
+ ll = &cl->next;
+ }
+
+ cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ll = cl;
+
+ cl->buf->flush = 1;
+ cl->buf->memory = 1;
+
+ b = &u->buffer;
+
+ cl->buf->pos = b->last;
+ b->last += bytes;
+ cl->buf->last = b->last;
+ cl->buf->tag = u->output.tag;
+
+ if (u->length == -1) {
+ return NGX_OK;
+ }
+
+ u->length -= bytes;
+
+ if (u->length == 0) {
+ u->keepalive = !u->headers_in.connection_close;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
+{
+ ngx_http_request_t *r = data;
+
+ ngx_int_t rc;
+ ngx_buf_t *b, *buf;
+ ngx_chain_t *cl, **ll;
+ ngx_http_upstream_t *u;
+ ngx_http_proxy_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ u = r->upstream;
+ buf = &u->buffer;
+
+ buf->pos = buf->last;
+ buf->last += bytes;
+
+ for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
+ ll = &cl->next;
+ }
+
+ for ( ;; ) {
+
+ rc = ngx_http_parse_chunked(r, buf, &ctx->chunked);
+
+ if (rc == NGX_OK) {
+
+ /* a chunk has been parsed successfully */
+
+ cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ll = cl;
+ ll = &cl->next;
+
+ b = cl->buf;
+
+ b->flush = 1;
+ b->memory = 1;
+
+ b->pos = buf->pos;
+ b->tag = u->output.tag;
+
+ if (buf->last - buf->pos >= ctx->chunked.size) {
+ buf->pos += (size_t) ctx->chunked.size;
+ b->last = buf->pos;
+ ctx->chunked.size = 0;
+
+ } else {
+ ctx->chunked.size -= buf->last - buf->pos;
+ buf->pos = buf->last;
+ b->last = buf->last;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy out buf %p %z",
+ b->pos, b->last - b->pos);
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+
+ /* a whole response has been parsed successfully */
+
+ u->keepalive = !u->headers_in.connection_close;
+ u->length = 0;
+
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+ break;
+ }
+
+ /* invalid response */
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent invalid chunked response");
+
+ return NGX_ERROR;
+ }
+
+ /* provide continuous buffer for subrequests in memory */
+
+ if (r->subrequest_in_memory) {
+
+ cl = u->out_bufs;
+
+ if (cl) {
+ buf->pos = cl->buf->pos;
+ }
+
+ buf->last = buf->pos;
+
+ for (cl = u->out_bufs; cl; cl = cl->next) {
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http proxy in memory %p-%p %uz",
+ cl->buf->pos, cl->buf->last, ngx_buf_size(cl->buf));
+
+ if (buf->last == cl->buf->pos) {
+ buf->last = cl->buf->last;
+ continue;
+ }
+
+ buf->last = ngx_movemem(buf->last, cl->buf->pos,
+ cl->buf->last - cl->buf->pos);
+
+ cl->buf->pos = buf->last - (cl->buf->last - cl->buf->pos);
+ cl->buf->last = buf->last;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_proxy_abort_request(ngx_http_request_t *r)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "abort http proxy request");
+
+ return;
+}
+
+
+static void
+ngx_http_proxy_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "finalize http proxy request");
+
+ return;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_host_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_proxy_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (ctx == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->len = ctx->vars.host_header.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = ctx->vars.host_header.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_port_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_proxy_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (ctx == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->len = ctx->vars.port.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = ctx->vars.port.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ size_t len;
+ u_char *p;
+ ngx_uint_t i, n;
+ ngx_table_elt_t **h;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ n = r->headers_in.x_forwarded_for.nelts;
+ h = r->headers_in.x_forwarded_for.elts;
+
+ len = 0;
+
+ for (i = 0; i < n; i++) {
+ len += h[i]->value.len + sizeof(", ") - 1;
+ }
+
+ if (len == 0) {
+ v->len = r->connection->addr_text.len;
+ v->data = r->connection->addr_text.data;
+ return NGX_OK;
+ }
+
+ len += r->connection->addr_text.len;
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = len;
+ v->data = p;
+
+ for (i = 0; i < n; i++) {
+ p = ngx_copy(p, h[i]->value.data, h[i]->value.len);
+ *p++ = ','; *p++ = ' ';
+ }
+
+ ngx_memcpy(p, r->connection->addr_text.data, r->connection->addr_text.len);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_proxy_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (ctx == NULL || ctx->internal_body_length < 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ v->data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
+
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(v->data, "%O", ctx->internal_body_length) - v->data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h,
+ size_t prefix)
+{
+ size_t len;
+ ngx_int_t rc;
+ ngx_uint_t i;
+ ngx_http_proxy_rewrite_t *pr;
+ ngx_http_proxy_loc_conf_t *plcf;
+
+ plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
+
+ pr = plcf->redirects->elts;
+
+ if (pr == NULL) {
+ return NGX_DECLINED;
+ }
+
+ len = h->value.len - prefix;
+
+ for (i = 0; i < plcf->redirects->nelts; i++) {
+ rc = pr[i].handler(r, h, prefix, len, &pr[i]);
+
+ if (rc != NGX_DECLINED) {
+ return rc;
+ }
+ }
+
+ return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h)
+{
+ size_t prefix;
+ u_char *p;
+ ngx_int_t rc, rv;
+ ngx_http_proxy_loc_conf_t *plcf;
+
+ p = (u_char *) ngx_strchr(h->value.data, ';');
+ if (p == NULL) {
+ return NGX_DECLINED;
+ }
+
+ prefix = p + 1 - h->value.data;
+
+ rv = NGX_DECLINED;
+
+ plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
+
+ if (plcf->cookie_domains) {
+ p = ngx_strcasestrn(h->value.data + prefix, "domain=", 7 - 1);
+
+ if (p) {
+ rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 7,
+ plcf->cookie_domains);
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (rc != NGX_DECLINED) {
+ rv = rc;
+ }
+ }
+ }
+
+ if (plcf->cookie_paths) {
+ p = ngx_strcasestrn(h->value.data + prefix, "path=", 5 - 1);
+
+ if (p) {
+ rc = ngx_http_proxy_rewrite_cookie_value(r, h, p + 5,
+ plcf->cookie_paths);
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (rc != NGX_DECLINED) {
+ rv = rc;
+ }
+ }
+ }
+
+ return rv;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_table_elt_t *h,
+ u_char *value, ngx_array_t *rewrites)
+{
+ size_t len, prefix;
+ u_char *p;
+ ngx_int_t rc;
+ ngx_uint_t i;
+ ngx_http_proxy_rewrite_t *pr;
+
+ prefix = value - h->value.data;
+
+ p = (u_char *) ngx_strchr(value, ';');
+
+ len = p ? (size_t) (p - value) : (h->value.len - prefix);
+
+ pr = rewrites->elts;
+
+ for (i = 0; i < rewrites->nelts; i++) {
+ rc = pr[i].handler(r, h, prefix, len, &pr[i]);
+
+ if (rc != NGX_DECLINED) {
+ return rc;
+ }
+ }
+
+ return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r,
+ ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
+{
+ ngx_str_t pattern, replacement;
+
+ if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (pattern.len > len
+ || ngx_rstrncmp(h->value.data + prefix, pattern.data,
+ pattern.len) != 0)
+ {
+ return NGX_DECLINED;
+ }
+
+ if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return ngx_http_proxy_rewrite(r, h, prefix, pattern.len, &replacement);
+}
+
+
+#if (NGX_PCRE)
+
+static ngx_int_t
+ngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_table_elt_t *h,
+ size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
+{
+ ngx_str_t pattern, replacement;
+
+ pattern.len = len;
+ pattern.data = h->value.data + prefix;
+
+ if (ngx_http_regex_exec(r, pr->pattern.regex, &pattern) != NGX_OK) {
+ return NGX_DECLINED;
+ }
+
+ if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (prefix == 0 && h->value.len == len) {
+ h->value = replacement;
+ return NGX_OK;
+ }
+
+ return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement);
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r,
+ ngx_table_elt_t *h, size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)
+{
+ u_char *p;
+ ngx_str_t pattern, replacement;
+
+ if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ p = h->value.data + prefix;
+
+ if (p[0] == '.') {
+ p++;
+ prefix++;
+ len--;
+ }
+
+ if (pattern.len != len || ngx_rstrncasecmp(pattern.data, p, len) != 0) {
+ return NGX_DECLINED;
+ }
+
+ if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return ngx_http_proxy_rewrite(r, h, prefix, len, &replacement);
+}
+
+
+static ngx_int_t
+ngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix,
+ size_t len, ngx_str_t *replacement)
+{
+ u_char *p, *data;
+ size_t new_len;
+
+ new_len = replacement->len + h->value.len - len;
+
+ if (replacement->len > len) {
+
+ data = ngx_pnalloc(r->pool, new_len + 1);
+ if (data == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_copy(data, h->value.data, prefix);
+ p = ngx_copy(p, replacement->data, replacement->len);
+
+ ngx_memcpy(p, h->value.data + prefix + len,
+ h->value.len - len - prefix + 1);
+
+ h->value.data = data;
+
+ } else {
+ p = ngx_copy(h->value.data + prefix, replacement->data,
+ replacement->len);
+
+ ngx_memmove(p, h->value.data + prefix + len,
+ h->value.len - len - prefix + 1);
+ }
+
+ h->value.len = new_len;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var, *v;
+
+ for (v = ngx_http_proxy_vars; v->name.len; v++) {
+ var = ngx_http_add_variable(cf, &v->name, v->flags);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = v->get_handler;
+ var->data = v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_proxy_loc_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->upstream.bufs.num = 0;
+ * conf->upstream.ignore_headers = 0;
+ * conf->upstream.next_upstream = 0;
+ * conf->upstream.cache_use_stale = 0;
+ * conf->upstream.cache_methods = 0;
+ * conf->upstream.temp_path = NULL;
+ * conf->upstream.hide_headers_hash = { NULL, 0 };
+ * conf->upstream.uri = { 0, NULL };
+ * conf->upstream.location = NULL;
+ * conf->upstream.store_lengths = NULL;
+ * conf->upstream.store_values = NULL;
+ * conf->upstream.ssl_name = NULL;
+ *
+ * conf->method = { 0, NULL };
+ * conf->headers_source = NULL;
+ * conf->headers_set_len = NULL;
+ * conf->headers_set = NULL;
+ * conf->headers_set_hash = NULL;
+ * conf->body_set_len = NULL;
+ * conf->body_set = NULL;
+ * conf->body_source = { 0, NULL };
+ * conf->redirects = NULL;
+ * conf->ssl = 0;
+ * conf->ssl_protocols = 0;
+ * conf->ssl_ciphers = { 0, NULL };
+ * conf->ssl_trusted_certificate = { 0, NULL };
+ * conf->ssl_crl = { 0, NULL };
+ */
+
+ conf->upstream.store = NGX_CONF_UNSET;
+ conf->upstream.store_access = NGX_CONF_UNSET_UINT;
+ conf->upstream.buffering = NGX_CONF_UNSET;
+ conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
+
+ conf->upstream.local = NGX_CONF_UNSET_PTR;
+
+ conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
+
+ conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;
+ conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
+
+ conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;
+ conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
+ conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;
+
+ conf->upstream.pass_request_headers = NGX_CONF_UNSET;
+ conf->upstream.pass_request_body = NGX_CONF_UNSET;
+
+#if (NGX_HTTP_CACHE)
+ conf->upstream.cache = NGX_CONF_UNSET_PTR;
+ conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;
+ conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;
+ conf->upstream.no_cache = NGX_CONF_UNSET_PTR;
+ conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;
+ conf->upstream.cache_lock = NGX_CONF_UNSET;
+ conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.cache_revalidate = NGX_CONF_UNSET;
+#endif
+
+ conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
+ conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
+
+ conf->upstream.intercept_errors = NGX_CONF_UNSET;
+
+#if (NGX_HTTP_SSL)
+ conf->upstream.ssl_session_reuse = NGX_CONF_UNSET;
+ conf->upstream.ssl_server_name = NGX_CONF_UNSET;
+ conf->upstream.ssl_verify = NGX_CONF_UNSET;
+ conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
+#endif
+
+ /* "proxy_cyclic_temp_file" is disabled */
+ conf->upstream.cyclic_temp_file = 0;
+
+ conf->redirect = NGX_CONF_UNSET;
+ conf->upstream.change_buffering = 1;
+
+ conf->cookie_domains = NGX_CONF_UNSET_PTR;
+ conf->cookie_paths = NGX_CONF_UNSET_PTR;
+
+ conf->http_version = NGX_CONF_UNSET_UINT;
+
+ conf->headers_hash_max_size = NGX_CONF_UNSET_UINT;
+ conf->headers_hash_bucket_size = NGX_CONF_UNSET_UINT;
+
+ ngx_str_set(&conf->upstream.module, "proxy");
+
+ return conf;
+}
+
+
+static char *
+ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_proxy_loc_conf_t *prev = parent;
+ ngx_http_proxy_loc_conf_t *conf = child;
+
+ u_char *p;
+ size_t size;
+ ngx_hash_init_t hash;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_proxy_rewrite_t *pr;
+ ngx_http_script_compile_t sc;
+
+ if (conf->upstream.store != 0) {
+ ngx_conf_merge_value(conf->upstream.store,
+ prev->upstream.store, 0);
+
+ if (conf->upstream.store_lengths == NULL) {
+ conf->upstream.store_lengths = prev->upstream.store_lengths;
+ conf->upstream.store_values = prev->upstream.store_values;
+ }
+ }
+
+ ngx_conf_merge_uint_value(conf->upstream.store_access,
+ prev->upstream.store_access, 0600);
+
+ ngx_conf_merge_value(conf->upstream.buffering,
+ prev->upstream.buffering, 1);
+
+ ngx_conf_merge_value(conf->upstream.ignore_client_abort,
+ prev->upstream.ignore_client_abort, 0);
+
+ ngx_conf_merge_ptr_value(conf->upstream.local,
+ prev->upstream.local, NULL);
+
+ ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
+ prev->upstream.connect_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->upstream.send_timeout,
+ prev->upstream.send_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->upstream.read_timeout,
+ prev->upstream.read_timeout, 60000);
+
+ ngx_conf_merge_size_value(conf->upstream.send_lowat,
+ prev->upstream.send_lowat, 0);
+
+ ngx_conf_merge_size_value(conf->upstream.buffer_size,
+ prev->upstream.buffer_size,
+ (size_t) ngx_pagesize);
+
+ ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,
+ 8, ngx_pagesize);
+
+ if (conf->upstream.bufs.num < 2) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "there must be at least 2 \"proxy_buffers\"");
+ return NGX_CONF_ERROR;
+ }
+
+
+ size = conf->upstream.buffer_size;
+ if (size < conf->upstream.bufs.size) {
+ size = conf->upstream.bufs.size;
+ }
+
+
+ ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,
+ prev->upstream.busy_buffers_size_conf,
+ NGX_CONF_UNSET_SIZE);
+
+ if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {
+ conf->upstream.busy_buffers_size = 2 * size;
+ } else {
+ conf->upstream.busy_buffers_size =
+ conf->upstream.busy_buffers_size_conf;
+ }
+
+ if (conf->upstream.busy_buffers_size < size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"proxy_busy_buffers_size\" must be equal to or greater than "
+ "the maximum of the value of \"proxy_buffer_size\" and "
+ "one of the \"proxy_buffers\"");
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->upstream.busy_buffers_size
+ > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"proxy_busy_buffers_size\" must be less than "
+ "the size of all \"proxy_buffers\" minus one buffer");
+
+ return NGX_CONF_ERROR;
+ }
+
+
+ ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,
+ prev->upstream.temp_file_write_size_conf,
+ NGX_CONF_UNSET_SIZE);
+
+ if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {
+ conf->upstream.temp_file_write_size = 2 * size;
+ } else {
+ conf->upstream.temp_file_write_size =
+ conf->upstream.temp_file_write_size_conf;
+ }
+
+ if (conf->upstream.temp_file_write_size < size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"proxy_temp_file_write_size\" must be equal to or greater "
+ "than the maximum of the value of \"proxy_buffer_size\" and "
+ "one of the \"proxy_buffers\"");
+
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,
+ prev->upstream.max_temp_file_size_conf,
+ NGX_CONF_UNSET_SIZE);
+
+ if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {
+ conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;
+ } else {
+ conf->upstream.max_temp_file_size =
+ conf->upstream.max_temp_file_size_conf;
+ }
+
+ if (conf->upstream.max_temp_file_size != 0
+ && conf->upstream.max_temp_file_size < size)
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"proxy_max_temp_file_size\" must be equal to zero to disable "
+ "temporary files usage or must be equal to or greater than "
+ "the maximum of the value of \"proxy_buffer_size\" and "
+ "one of the \"proxy_buffers\"");
+
+ return NGX_CONF_ERROR;
+ }
+
+
+ ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
+ prev->upstream.ignore_headers,
+ NGX_CONF_BITMASK_SET);
+
+
+ ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
+ prev->upstream.next_upstream,
+ (NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_ERROR
+ |NGX_HTTP_UPSTREAM_FT_TIMEOUT));
+
+ if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
+ conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_OFF;
+ }
+
+ if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
+ prev->upstream.temp_path,
+ &ngx_http_proxy_temp_path)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+
+#if (NGX_HTTP_CACHE)
+
+ ngx_conf_merge_ptr_value(conf->upstream.cache,
+ prev->upstream.cache, NULL);
+
+ if (conf->upstream.cache && conf->upstream.cache->data == NULL) {
+ ngx_shm_zone_t *shm_zone;
+
+ shm_zone = conf->upstream.cache;
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"proxy_cache\" zone \"%V\" is unknown",
+ &shm_zone->shm.name);
+
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,
+ prev->upstream.cache_min_uses, 1);
+
+ ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,
+ prev->upstream.cache_use_stale,
+ (NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_OFF));
+
+ if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {
+ conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_OFF;
+ }
+
+ if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {
+ conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;
+ }
+
+ if (conf->upstream.cache_methods == 0) {
+ conf->upstream.cache_methods = prev->upstream.cache_methods;
+ }
+
+ conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;
+
+ ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,
+ prev->upstream.cache_bypass, NULL);
+
+ ngx_conf_merge_ptr_value(conf->upstream.no_cache,
+ prev->upstream.no_cache, NULL);
+
+ ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
+ prev->upstream.cache_valid, NULL);
+
+ if (conf->cache_key.value.data == NULL) {
+ conf->cache_key = prev->cache_key;
+ }
+
+ ngx_conf_merge_value(conf->upstream.cache_lock,
+ prev->upstream.cache_lock, 0);
+
+ ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,
+ prev->upstream.cache_lock_timeout, 5000);
+
+ ngx_conf_merge_value(conf->upstream.cache_revalidate,
+ prev->upstream.cache_revalidate, 0);
+
+#endif
+
+ ngx_conf_merge_str_value(conf->method, prev->method, "");
+
+ if (conf->method.len
+ && conf->method.data[conf->method.len - 1] != ' ')
+ {
+ conf->method.data[conf->method.len] = ' ';
+ conf->method.len++;
+ }
+
+ ngx_conf_merge_value(conf->upstream.pass_request_headers,
+ prev->upstream.pass_request_headers, 1);
+ ngx_conf_merge_value(conf->upstream.pass_request_body,
+ prev->upstream.pass_request_body, 1);
+
+ ngx_conf_merge_value(conf->upstream.intercept_errors,
+ prev->upstream.intercept_errors, 0);
+
+#if (NGX_HTTP_SSL)
+
+ ngx_conf_merge_value(conf->upstream.ssl_session_reuse,
+ prev->upstream.ssl_session_reuse, 1);
+
+ ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,
+ (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3
+ |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1
+ |NGX_SSL_TLSv1_2));
+
+ ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,
+ "DEFAULT");
+
+ if (conf->upstream.ssl_name == NULL) {
+ conf->upstream.ssl_name = prev->upstream.ssl_name;
+ }
+
+ ngx_conf_merge_value(conf->upstream.ssl_server_name,
+ prev->upstream.ssl_server_name, 0);
+ ngx_conf_merge_value(conf->upstream.ssl_verify,
+ prev->upstream.ssl_verify, 0);
+ ngx_conf_merge_uint_value(conf->ssl_verify_depth,
+ prev->ssl_verify_depth, 1);
+ ngx_conf_merge_str_value(conf->ssl_trusted_certificate,
+ prev->ssl_trusted_certificate, "");
+ ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, "");
+
+ if (conf->ssl && ngx_http_proxy_set_ssl(cf, conf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+#endif
+
+ ngx_conf_merge_value(conf->redirect, prev->redirect, 1);
+
+ if (conf->redirect) {
+
+ if (conf->redirects == NULL) {
+ conf->redirects = prev->redirects;
+ }
+
+ if (conf->redirects == NULL && conf->url.data) {
+
+ conf->redirects = ngx_array_create(cf->pool, 1,
+ sizeof(ngx_http_proxy_rewrite_t));
+ if (conf->redirects == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ pr = ngx_array_push(conf->redirects);
+ if (pr == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(&pr->pattern.complex,
+ sizeof(ngx_http_complex_value_t));
+
+ ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t));
+
+ pr->handler = ngx_http_proxy_rewrite_complex_handler;
+
+ if (conf->vars.uri.len) {
+ pr->pattern.complex.value = conf->url;
+ pr->replacement.value = conf->location;
+
+ } else {
+ pr->pattern.complex.value.len = conf->url.len
+ + sizeof("/") - 1;
+
+ p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ pr->pattern.complex.value.data = p;
+
+ p = ngx_cpymem(p, conf->url.data, conf->url.len);
+ *p = '/';
+
+ ngx_str_set(&pr->replacement.value, "/");
+ }
+ }
+ }
+
+ ngx_conf_merge_ptr_value(conf->cookie_domains, prev->cookie_domains, NULL);
+
+ ngx_conf_merge_ptr_value(conf->cookie_paths, prev->cookie_paths, NULL);
+
+#if (NGX_HTTP_SSL)
+ if (conf->upstream.ssl == NULL) {
+ conf->upstream.ssl = prev->upstream.ssl;
+ }
+#endif
+
+ ngx_conf_merge_uint_value(conf->http_version, prev->http_version,
+ NGX_HTTP_VERSION_10);
+
+ ngx_conf_merge_uint_value(conf->headers_hash_max_size,
+ prev->headers_hash_max_size, 512);
+
+ ngx_conf_merge_uint_value(conf->headers_hash_bucket_size,
+ prev->headers_hash_bucket_size, 64);
+
+ conf->headers_hash_bucket_size = ngx_align(conf->headers_hash_bucket_size,
+ ngx_cacheline_size);
+
+ hash.max_size = conf->headers_hash_max_size;
+ hash.bucket_size = conf->headers_hash_bucket_size;
+ hash.name = "proxy_headers_hash";
+
+ if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
+ &prev->upstream, ngx_http_proxy_hide_headers, &hash)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->upstream.upstream == NULL) {
+ conf->upstream.upstream = prev->upstream.upstream;
+ conf->vars = prev->vars;
+ }
+
+ if (conf->proxy_lengths == NULL) {
+ conf->proxy_lengths = prev->proxy_lengths;
+ conf->proxy_values = prev->proxy_values;
+ }
+
+ if (conf->upstream.upstream || conf->proxy_lengths) {
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+ if (clcf->handler == NULL && clcf->lmt_excpt) {
+ clcf->handler = ngx_http_proxy_handler;
+ conf->location = prev->location;
+ }
+ }
+
+ if (conf->body_source.data == NULL) {
+ conf->body_source = prev->body_source;
+ conf->body_set_len = prev->body_set_len;
+ conf->body_set = prev->body_set;
+ }
+
+ if (conf->body_source.data && conf->body_set_len == NULL) {
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &conf->body_source;
+ sc.flushes = &conf->flushes;
+ sc.lengths = &conf->body_set_len;
+ sc.values = &conf->body_set;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (ngx_http_proxy_merge_headers(cf, conf, prev) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_merge_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf,
+ ngx_http_proxy_loc_conf_t *prev)
+{
+ u_char *p;
+ size_t size;
+ uintptr_t *code;
+ ngx_uint_t i;
+ ngx_array_t headers_names, headers_merged;
+ ngx_keyval_t *src, *s, *h;
+ ngx_hash_key_t *hk;
+ ngx_hash_init_t hash;
+ ngx_http_script_compile_t sc;
+ ngx_http_script_copy_code_t *copy;
+
+ if (conf->headers_source == NULL) {
+ conf->flushes = prev->flushes;
+ conf->headers_set_len = prev->headers_set_len;
+ conf->headers_set = prev->headers_set;
+ conf->headers_set_hash = prev->headers_set_hash;
+ conf->headers_source = prev->headers_source;
+ }
+
+ if (conf->headers_set_hash.buckets
+#if (NGX_HTTP_CACHE)
+ && ((conf->upstream.cache == NULL) == (prev->upstream.cache == NULL))
+#endif
+ )
+ {
+ return NGX_OK;
+ }
+
+
+ if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&headers_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (conf->headers_source == NULL) {
+ conf->headers_source = ngx_array_create(cf->pool, 4,
+ sizeof(ngx_keyval_t));
+ if (conf->headers_source == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ conf->headers_set_len = ngx_array_create(cf->pool, 64, 1);
+ if (conf->headers_set_len == NULL) {
+ return NGX_ERROR;
+ }
+
+ conf->headers_set = ngx_array_create(cf->pool, 512, 1);
+ if (conf->headers_set == NULL) {
+ return NGX_ERROR;
+ }
+
+
+#if (NGX_HTTP_CACHE)
+
+ h = conf->upstream.cache ? ngx_http_proxy_cache_headers:
+ ngx_http_proxy_headers;
+#else
+
+ h = ngx_http_proxy_headers;
+
+#endif
+
+ src = conf->headers_source->elts;
+ for (i = 0; i < conf->headers_source->nelts; i++) {
+
+ s = ngx_array_push(&headers_merged);
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ *s = src[i];
+ }
+
+ while (h->key.len) {
+
+ src = headers_merged.elts;
+ for (i = 0; i < headers_merged.nelts; i++) {
+ if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {
+ goto next;
+ }
+ }
+
+ s = ngx_array_push(&headers_merged);
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ *s = *h;
+
+ next:
+
+ h++;
+ }
+
+
+ src = headers_merged.elts;
+ for (i = 0; i < headers_merged.nelts; i++) {
+
+ hk = ngx_array_push(&headers_names);
+ if (hk == NULL) {
+ return NGX_ERROR;
+ }
+
+ hk->key = src[i].key;
+ hk->key_hash = ngx_hash_key_lc(src[i].key.data, src[i].key.len);
+ hk->value = (void *) 1;
+
+ if (src[i].value.len == 0) {
+ continue;
+ }
+
+ if (ngx_http_script_variables_count(&src[i].value) == 0) {
+ copy = ngx_array_push_n(conf->headers_set_len,
+ sizeof(ngx_http_script_copy_code_t));
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = (ngx_http_script_code_pt)
+ ngx_http_script_copy_len_code;
+ copy->len = src[i].key.len + sizeof(": ") - 1
+ + src[i].value.len + sizeof(CRLF) - 1;
+
+
+ size = (sizeof(ngx_http_script_copy_code_t)
+ + src[i].key.len + sizeof(": ") - 1
+ + src[i].value.len + sizeof(CRLF) - 1
+ + sizeof(uintptr_t) - 1)
+ & ~(sizeof(uintptr_t) - 1);
+
+ copy = ngx_array_push_n(conf->headers_set, size);
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = ngx_http_script_copy_code;
+ copy->len = src[i].key.len + sizeof(": ") - 1
+ + src[i].value.len + sizeof(CRLF) - 1;
+
+ p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
+
+ p = ngx_cpymem(p, src[i].key.data, src[i].key.len);
+ *p++ = ':'; *p++ = ' ';
+ p = ngx_cpymem(p, src[i].value.data, src[i].value.len);
+ *p++ = CR; *p = LF;
+
+ } else {
+ copy = ngx_array_push_n(conf->headers_set_len,
+ sizeof(ngx_http_script_copy_code_t));
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = (ngx_http_script_code_pt)
+ ngx_http_script_copy_len_code;
+ copy->len = src[i].key.len + sizeof(": ") - 1;
+
+
+ size = (sizeof(ngx_http_script_copy_code_t)
+ + src[i].key.len + sizeof(": ") - 1 + sizeof(uintptr_t) - 1)
+ & ~(sizeof(uintptr_t) - 1);
+
+ copy = ngx_array_push_n(conf->headers_set, size);
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = ngx_http_script_copy_code;
+ copy->len = src[i].key.len + sizeof(": ") - 1;
+
+ p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
+ p = ngx_cpymem(p, src[i].key.data, src[i].key.len);
+ *p++ = ':'; *p = ' ';
+
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &src[i].value;
+ sc.flushes = &conf->flushes;
+ sc.lengths = &conf->headers_set_len;
+ sc.values = &conf->headers_set;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+
+ copy = ngx_array_push_n(conf->headers_set_len,
+ sizeof(ngx_http_script_copy_code_t));
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = (ngx_http_script_code_pt)
+ ngx_http_script_copy_len_code;
+ copy->len = sizeof(CRLF) - 1;
+
+
+ size = (sizeof(ngx_http_script_copy_code_t)
+ + sizeof(CRLF) - 1 + sizeof(uintptr_t) - 1)
+ & ~(sizeof(uintptr_t) - 1);
+
+ copy = ngx_array_push_n(conf->headers_set, size);
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = ngx_http_script_copy_code;
+ copy->len = sizeof(CRLF) - 1;
+
+ p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
+ *p++ = CR; *p = LF;
+ }
+
+ code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+
+ code = ngx_array_push_n(conf->headers_set, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+ }
+
+ code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+
+
+ hash.hash = &conf->headers_set_hash;
+ hash.key = ngx_hash_key_lc;
+ hash.max_size = conf->headers_hash_max_size;
+ hash.bucket_size = conf->headers_hash_bucket_size;
+ hash.name = "proxy_headers_hash";
+ hash.pool = cf->pool;
+ hash.temp_pool = NULL;
+
+ return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);
+}
+
+
+static char *
+ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_proxy_loc_conf_t *plcf = conf;
+
+ size_t add;
+ u_short port;
+ ngx_str_t *value, *url;
+ ngx_url_t u;
+ ngx_uint_t n;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_script_compile_t sc;
+
+ if (plcf->upstream.upstream || plcf->proxy_lengths) {
+ return "is duplicate";
+ }
+
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+
+ clcf->handler = ngx_http_proxy_handler;
+
+ if (clcf->name.data[clcf->name.len - 1] == '/') {
+ clcf->auto_redirect = 1;
+ }
+
+ value = cf->args->elts;
+
+ url = &value[1];
+
+ n = ngx_http_script_variables_count(url);
+
+ if (n) {
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = url;
+ sc.lengths = &plcf->proxy_lengths;
+ sc.values = &plcf->proxy_values;
+ sc.variables = n;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+#if (NGX_HTTP_SSL)
+ plcf->ssl = 1;
+#endif
+
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_strncasecmp(url->data, (u_char *) "http://", 7) == 0) {
+ add = 7;
+ port = 80;
+
+ } else if (ngx_strncasecmp(url->data, (u_char *) "https://", 8) == 0) {
+
+#if (NGX_HTTP_SSL)
+ plcf->ssl = 1;
+
+ add = 8;
+ port = 443;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "https protocol requires SSL support");
+ return NGX_CONF_ERROR;
+#endif
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid URL prefix");
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url.len = url->len - add;
+ u.url.data = url->data + add;
+ u.default_port = port;
+ u.uri_part = 1;
+ u.no_resolve = 1;
+
+ plcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
+ if (plcf->upstream.upstream == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ plcf->vars.schema.len = add;
+ plcf->vars.schema.data = url->data;
+ plcf->vars.key_start = plcf->vars.schema;
+
+ ngx_http_proxy_set_vars(&u, &plcf->vars);
+
+ plcf->location = clcf->name;
+
+ if (clcf->named
+#if (NGX_PCRE)
+ || clcf->regex
+#endif
+ || clcf->noname)
+ {
+ if (plcf->vars.uri.len) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"proxy_pass\" cannot have URI part in "
+ "location given by regular expression, "
+ "or inside named location, "
+ "or inside \"if\" statement, "
+ "or inside \"limit_except\" block");
+ return NGX_CONF_ERROR;
+ }
+
+ plcf->location.len = 0;
+ }
+
+ plcf->url = *url;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_proxy_loc_conf_t *plcf = conf;
+
+ u_char *p;
+ ngx_str_t *value;
+ ngx_http_proxy_rewrite_t *pr;
+ ngx_http_compile_complex_value_t ccv;
+
+ if (plcf->redirect == 0) {
+ return NGX_CONF_OK;
+ }
+
+ plcf->redirect = 1;
+
+ value = cf->args->elts;
+
+ if (cf->args->nelts == 2) {
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ plcf->redirect = 0;
+ plcf->redirects = NULL;
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_strcmp(value[1].data, "false") == 0) {
+ ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
+ "invalid parameter \"false\", use \"off\" instead");
+ plcf->redirect = 0;
+ plcf->redirects = NULL;
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_strcmp(value[1].data, "default") != 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (plcf->redirects == NULL) {
+ plcf->redirects = ngx_array_create(cf->pool, 1,
+ sizeof(ngx_http_proxy_rewrite_t));
+ if (plcf->redirects == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ pr = ngx_array_push(plcf->redirects);
+ if (pr == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_strcmp(value[1].data, "default") == 0) {
+ if (plcf->proxy_lengths) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"proxy_redirect default\" cannot be used "
+ "with \"proxy_pass\" directive with variables");
+ return NGX_CONF_ERROR;
+ }
+
+ if (plcf->url.data == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"proxy_redirect default\" should be placed "
+ "after the \"proxy_pass\" directive");
+ return NGX_CONF_ERROR;
+ }
+
+ pr->handler = ngx_http_proxy_rewrite_complex_handler;
+
+ ngx_memzero(&pr->pattern.complex, sizeof(ngx_http_complex_value_t));
+
+ ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t));
+
+ if (plcf->vars.uri.len) {
+ pr->pattern.complex.value = plcf->url;
+ pr->replacement.value = plcf->location;
+
+ } else {
+ pr->pattern.complex.value.len = plcf->url.len + sizeof("/") - 1;
+
+ p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ pr->pattern.complex.value.data = p;
+
+ p = ngx_cpymem(p, plcf->url.data, plcf->url.len);
+ *p = '/';
+
+ ngx_str_set(&pr->replacement.value, "/");
+ }
+
+ return NGX_CONF_OK;
+ }
+
+
+ if (value[1].data[0] == '~') {
+ value[1].len--;
+ value[1].data++;
+
+ if (value[1].data[0] == '*') {
+ value[1].len--;
+ value[1].data++;
+
+ if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+ if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ } else {
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &pr->pattern.complex;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ pr->handler = ngx_http_proxy_rewrite_complex_handler;
+ }
+
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[2];
+ ccv.complex_value = &pr->replacement;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_proxy_loc_conf_t *plcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_proxy_rewrite_t *pr;
+ ngx_http_compile_complex_value_t ccv;
+
+ if (plcf->cookie_domains == NULL) {
+ return NGX_CONF_OK;
+ }
+
+ value = cf->args->elts;
+
+ if (cf->args->nelts == 2) {
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ plcf->cookie_domains = NULL;
+ return NGX_CONF_OK;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (plcf->cookie_domains == NGX_CONF_UNSET_PTR) {
+ plcf->cookie_domains = ngx_array_create(cf->pool, 1,
+ sizeof(ngx_http_proxy_rewrite_t));
+ if (plcf->cookie_domains == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ pr = ngx_array_push(plcf->cookie_domains);
+ if (pr == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (value[1].data[0] == '~') {
+ value[1].len--;
+ value[1].data++;
+
+ if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+
+ if (value[1].data[0] == '.') {
+ value[1].len--;
+ value[1].data++;
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &pr->pattern.complex;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ pr->handler = ngx_http_proxy_rewrite_domain_handler;
+
+ if (value[2].data[0] == '.') {
+ value[2].len--;
+ value[2].data++;
+ }
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[2];
+ ccv.complex_value = &pr->replacement;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_proxy_loc_conf_t *plcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_proxy_rewrite_t *pr;
+ ngx_http_compile_complex_value_t ccv;
+
+ if (plcf->cookie_paths == NULL) {
+ return NGX_CONF_OK;
+ }
+
+ value = cf->args->elts;
+
+ if (cf->args->nelts == 2) {
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ plcf->cookie_paths = NULL;
+ return NGX_CONF_OK;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (plcf->cookie_paths == NGX_CONF_UNSET_PTR) {
+ plcf->cookie_paths = ngx_array_create(cf->pool, 1,
+ sizeof(ngx_http_proxy_rewrite_t));
+ if (plcf->cookie_paths == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ pr = ngx_array_push(plcf->cookie_paths);
+ if (pr == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (value[1].data[0] == '~') {
+ value[1].len--;
+ value[1].data++;
+
+ if (value[1].data[0] == '*') {
+ value[1].len--;
+ value[1].data++;
+
+ if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+ if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ } else {
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &pr->pattern.complex;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ pr->handler = ngx_http_proxy_rewrite_complex_handler;
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[2];
+ ccv.complex_value = &pr->replacement;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_proxy_rewrite_regex(ngx_conf_t *cf, ngx_http_proxy_rewrite_t *pr,
+ ngx_str_t *regex, ngx_uint_t caseless)
+{
+#if (NGX_PCRE)
+ u_char errstr[NGX_MAX_CONF_ERRSTR];
+ ngx_regex_compile_t rc;
+
+ ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+ rc.pattern = *regex;
+ rc.err.len = NGX_MAX_CONF_ERRSTR;
+ rc.err.data = errstr;
+
+ if (caseless) {
+ rc.options = NGX_REGEX_CASELESS;
+ }
+
+ pr->pattern.regex = ngx_http_regex_compile(cf, &rc);
+ if (pr->pattern.regex == NULL) {
+ return NGX_ERROR;
+ }
+
+ pr->handler = ngx_http_proxy_rewrite_regex_handler;
+
+ return NGX_OK;
+
+#else
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "using regex \"%V\" requires PCRE library", regex);
+ return NGX_ERROR;
+
+#endif
+}
+
+
+static char *
+ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_proxy_loc_conf_t *plcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_script_compile_t sc;
+
+ if (plcf->upstream.store != NGX_CONF_UNSET
+ || plcf->upstream.store_lengths)
+ {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ plcf->upstream.store = 0;
+ return NGX_CONF_OK;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (plcf->upstream.cache != NGX_CONF_UNSET_PTR
+ && plcf->upstream.cache != NULL)
+ {
+ return "is incompatible with \"proxy_cache\"";
+ }
+
+#endif
+
+ if (ngx_strcmp(value[1].data, "on") == 0) {
+ plcf->upstream.store = 1;
+ return NGX_CONF_OK;
+ }
+
+ /* include the terminating '\0' into script */
+ value[1].len++;
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &value[1];
+ sc.lengths = &plcf->upstream.store_lengths;
+ sc.values = &plcf->upstream.store_values;
+ sc.variables = ngx_http_script_variables_count(&value[1]);
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+#if (NGX_HTTP_CACHE)
+
+static char *
+ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_proxy_loc_conf_t *plcf = conf;
+
+ ngx_str_t *value;
+
+ value = cf->args->elts;
+
+ if (plcf->upstream.cache != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ plcf->upstream.cache = NULL;
+ return NGX_CONF_OK;
+ }
+
+ if (plcf->upstream.store > 0 || plcf->upstream.store_lengths) {
+ return "is incompatible with \"proxy_store\"";
+ }
+
+ plcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0,
+ &ngx_http_proxy_module);
+ if (plcf->upstream.cache == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_proxy_loc_conf_t *plcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_compile_complex_value_t ccv;
+
+ value = cf->args->elts;
+
+ if (plcf->cache_key.value.data) {
+ return "is duplicate";
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &plcf->cache_key;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+#endif
+
+
+static char *
+ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data)
+{
+#if (NGX_FREEBSD)
+ ssize_t *np = data;
+
+ if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"proxy_send_lowat\" must be less than %d "
+ "(sysctl net.inet.tcp.sendspace)",
+ ngx_freebsd_net_inet_tcp_sendspace);
+
+ return NGX_CONF_ERROR;
+ }
+
+#elif !(NGX_HAVE_SO_SNDLOWAT)
+ ssize_t *np = data;
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "\"proxy_send_lowat\" is not supported, ignored");
+
+ *np = 0;
+
+#endif
+
+ return NGX_CONF_OK;
+}
+
+
+#if (NGX_HTTP_SSL)
+
+static ngx_int_t
+ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf)
+{
+ ngx_pool_cleanup_t *cln;
+
+ plcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
+ if (plcf->upstream.ssl == NULL) {
+ return NGX_ERROR;
+ }
+
+ plcf->upstream.ssl->log = cf->log;
+
+ if (ngx_ssl_create(plcf->upstream.ssl, plcf->ssl_protocols, NULL)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_ssl_cleanup_ctx;
+ cln->data = plcf->upstream.ssl;
+
+ if (SSL_CTX_set_cipher_list(plcf->upstream.ssl->ctx,
+ (const char *) plcf->ssl_ciphers.data)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
+ "SSL_CTX_set_cipher_list(\"%V\") failed",
+ &plcf->ssl_ciphers);
+ return NGX_ERROR;
+ }
+
+ if (plcf->upstream.ssl_verify) {
+ if (plcf->ssl_trusted_certificate.len == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no proxy_ssl_trusted_certificate for proxy_ssl_verify");
+ return NGX_ERROR;
+ }
+
+ if (ngx_ssl_trusted_certificate(cf, plcf->upstream.ssl,
+ &plcf->ssl_trusted_certificate,
+ plcf->ssl_verify_depth)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_ssl_crl(cf, plcf->upstream.ssl, &plcf->ssl_crl) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static void
+ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v)
+{
+ if (u->family != AF_UNIX) {
+
+ if (u->no_port || u->port == u->default_port) {
+
+ v->host_header = u->host;
+
+ if (u->default_port == 80) {
+ ngx_str_set(&v->port, "80");
+
+ } else {
+ ngx_str_set(&v->port, "443");
+ }
+
+ } else {
+ v->host_header.len = u->host.len + 1 + u->port_text.len;
+ v->host_header.data = u->host.data;
+ v->port = u->port_text;
+ }
+
+ v->key_start.len += v->host_header.len;
+
+ } else {
+ ngx_str_set(&v->host_header, "localhost");
+ ngx_str_null(&v->port);
+ v->key_start.len += sizeof("unix:") - 1 + u->host.len + 1;
+ }
+
+ v->uri = u->uri;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_random_index_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_random_index_module.c
new file mode 100644
index 00000000000..b0f0e080912
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_random_index_module.c
@@ -0,0 +1,317 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_flag_t enable;
+} ngx_http_random_index_loc_conf_t;
+
+
+#define NGX_HTTP_RANDOM_INDEX_PREALLOCATE 50
+
+
+static ngx_int_t ngx_http_random_index_error(ngx_http_request_t *r,
+ ngx_dir_t *dir, ngx_str_t *name);
+static ngx_int_t ngx_http_random_index_init(ngx_conf_t *cf);
+static void *ngx_http_random_index_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+
+
+static ngx_command_t ngx_http_random_index_commands[] = {
+
+ { ngx_string("random_index"),
+ NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_random_index_loc_conf_t, enable),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_random_index_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_random_index_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_random_index_create_loc_conf, /* create location configuration */
+ ngx_http_random_index_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_random_index_module = {
+ NGX_MODULE_V1,
+ &ngx_http_random_index_module_ctx, /* module context */
+ ngx_http_random_index_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_random_index_handler(ngx_http_request_t *r)
+{
+ u_char *last, *filename;
+ size_t len, allocated, root;
+ ngx_err_t err;
+ ngx_int_t rc;
+ ngx_str_t path, uri, *name;
+ ngx_dir_t dir;
+ ngx_uint_t n, level;
+ ngx_array_t names;
+ ngx_http_random_index_loc_conf_t *rlcf;
+
+ if (r->uri.data[r->uri.len - 1] != '/') {
+ return NGX_DECLINED;
+ }
+
+ if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
+ return NGX_DECLINED;
+ }
+
+ rlcf = ngx_http_get_module_loc_conf(r, ngx_http_random_index_module);
+
+ if (!rlcf->enable) {
+ return NGX_DECLINED;
+ }
+
+#if (NGX_HAVE_D_TYPE)
+ len = NGX_DIR_MASK_LEN;
+#else
+ len = NGX_HTTP_RANDOM_INDEX_PREALLOCATE;
+#endif
+
+ last = ngx_http_map_uri_to_path(r, &path, &root, len);
+ if (last == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ allocated = path.len;
+
+ path.len = last - path.data - 1;
+ path.data[path.len] = '\0';
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http random index: \"%s\"", path.data);
+
+ if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
+ err = ngx_errno;
+
+ if (err == NGX_ENOENT
+ || err == NGX_ENOTDIR
+ || err == NGX_ENAMETOOLONG)
+ {
+ level = NGX_LOG_ERR;
+ rc = NGX_HTTP_NOT_FOUND;
+
+ } else if (err == NGX_EACCES) {
+ level = NGX_LOG_ERR;
+ rc = NGX_HTTP_FORBIDDEN;
+
+ } else {
+ level = NGX_LOG_CRIT;
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_log_error(level, r->connection->log, err,
+ ngx_open_dir_n " \"%s\" failed", path.data);
+
+ return rc;
+ }
+
+ if (ngx_array_init(&names, r->pool, 32, sizeof(ngx_str_t)) != NGX_OK) {
+ return ngx_http_random_index_error(r, &dir, &path);
+ }
+
+ filename = path.data;
+ filename[path.len] = '/';
+
+ for ( ;; ) {
+ ngx_set_errno(0);
+
+ if (ngx_read_dir(&dir) == NGX_ERROR) {
+ err = ngx_errno;
+
+ if (err != NGX_ENOMOREFILES) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
+ ngx_read_dir_n " \"%V\" failed", &path);
+ return ngx_http_random_index_error(r, &dir, &path);
+ }
+
+ break;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http random index file: \"%s\"", ngx_de_name(&dir));
+
+ if (ngx_de_name(&dir)[0] == '.') {
+ continue;
+ }
+
+ len = ngx_de_namelen(&dir);
+
+ if (dir.type == 0 || ngx_de_is_link(&dir)) {
+
+ /* 1 byte for '/' and 1 byte for terminating '\0' */
+
+ if (path.len + 1 + len + 1 > allocated) {
+ allocated = path.len + 1 + len + 1
+ + NGX_HTTP_RANDOM_INDEX_PREALLOCATE;
+
+ filename = ngx_pnalloc(r->pool, allocated);
+ if (filename == NULL) {
+ return ngx_http_random_index_error(r, &dir, &path);
+ }
+
+ last = ngx_cpystrn(filename, path.data, path.len + 1);
+ *last++ = '/';
+ }
+
+ ngx_cpystrn(last, ngx_de_name(&dir), len + 1);
+
+ if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
+ err = ngx_errno;
+
+ if (err != NGX_ENOENT) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
+ ngx_de_info_n " \"%s\" failed", filename);
+ return ngx_http_random_index_error(r, &dir, &path);
+ }
+
+ if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+ ngx_de_link_info_n " \"%s\" failed",
+ filename);
+ return ngx_http_random_index_error(r, &dir, &path);
+ }
+ }
+ }
+
+ if (!ngx_de_is_file(&dir)) {
+ continue;
+ }
+
+ name = ngx_array_push(&names);
+ if (name == NULL) {
+ return ngx_http_random_index_error(r, &dir, &path);
+ }
+
+ name->len = len;
+
+ name->data = ngx_pnalloc(r->pool, len);
+ if (name->data == NULL) {
+ return ngx_http_random_index_error(r, &dir, &path);
+ }
+
+ ngx_memcpy(name->data, ngx_de_name(&dir), len);
+ }
+
+ if (ngx_close_dir(&dir) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+ ngx_close_dir_n " \"%s\" failed", &path);
+ }
+
+ n = names.nelts;
+
+ if (n == 0) {
+ return NGX_DECLINED;
+ }
+
+ name = names.elts;
+
+ n = (ngx_uint_t) (((uint64_t) ngx_random() * n) / 0x80000000);
+
+ uri.len = r->uri.len + name[n].len;
+
+ uri.data = ngx_pnalloc(r->pool, uri.len);
+ if (uri.data == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ last = ngx_copy(uri.data, r->uri.data, r->uri.len);
+ ngx_memcpy(last, name[n].data, name[n].len);
+
+ return ngx_http_internal_redirect(r, &uri, &r->args);
+}
+
+
+static ngx_int_t
+ngx_http_random_index_error(ngx_http_request_t *r, ngx_dir_t *dir,
+ ngx_str_t *name)
+{
+ if (ngx_close_dir(dir) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+ ngx_close_dir_n " \"%V\" failed", name);
+ }
+
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+}
+
+
+static void *
+ngx_http_random_index_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_random_index_loc_conf_t *conf;
+
+ conf = ngx_palloc(cf->pool, sizeof(ngx_http_random_index_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->enable = NGX_CONF_UNSET;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_random_index_loc_conf_t *prev = parent;
+ ngx_http_random_index_loc_conf_t *conf = child;
+
+ ngx_conf_merge_value(conf->enable, prev->enable, 0);
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_random_index_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_random_index_handler;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_range_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_range_filter_module.c
new file mode 100644
index 00000000000..6a65e48498d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_range_filter_module.c
@@ -0,0 +1,890 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+/*
+ * the single part format:
+ *
+ * "HTTP/1.0 206 Partial Content" CRLF
+ * ... header ...
+ * "Content-Type: image/jpeg" CRLF
+ * "Content-Length: SIZE" CRLF
+ * "Content-Range: bytes START-END/SIZE" CRLF
+ * CRLF
+ * ... data ...
+ *
+ *
+ * the multipart format:
+ *
+ * "HTTP/1.0 206 Partial Content" CRLF
+ * ... header ...
+ * "Content-Type: multipart/byteranges; boundary=0123456789" CRLF
+ * CRLF
+ * CRLF
+ * "--0123456789" CRLF
+ * "Content-Type: image/jpeg" CRLF
+ * "Content-Range: bytes START0-END0/SIZE" CRLF
+ * CRLF
+ * ... data ...
+ * CRLF
+ * "--0123456789" CRLF
+ * "Content-Type: image/jpeg" CRLF
+ * "Content-Range: bytes START1-END1/SIZE" CRLF
+ * CRLF
+ * ... data ...
+ * CRLF
+ * "--0123456789--" CRLF
+ */
+
+
+typedef struct {
+ off_t start;
+ off_t end;
+ ngx_str_t content_range;
+} ngx_http_range_t;
+
+
+typedef struct {
+ off_t offset;
+ ngx_str_t boundary_header;
+ ngx_array_t ranges;
+} ngx_http_range_filter_ctx_t;
+
+
+static ngx_int_t ngx_http_range_parse(ngx_http_request_t *r,
+ ngx_http_range_filter_ctx_t *ctx, ngx_uint_t ranges);
+static ngx_int_t ngx_http_range_singlepart_header(ngx_http_request_t *r,
+ ngx_http_range_filter_ctx_t *ctx);
+static ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r,
+ ngx_http_range_filter_ctx_t *ctx);
+static ngx_int_t ngx_http_range_not_satisfiable(ngx_http_request_t *r);
+static ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r,
+ ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
+static ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r,
+ ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
+static ngx_int_t ngx_http_range_multipart_body(ngx_http_request_t *r,
+ ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);
+
+static ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf);
+static ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf);
+
+
+static ngx_http_module_t ngx_http_range_header_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_range_header_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL, /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_range_header_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_range_header_filter_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_module_t ngx_http_range_body_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_range_body_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL, /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_range_body_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_range_body_filter_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_range_header_filter(ngx_http_request_t *r)
+{
+ time_t if_range_time;
+ ngx_str_t *if_range, *etag;
+ ngx_uint_t ranges;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_range_filter_ctx_t *ctx;
+
+ if (r->http_version < NGX_HTTP_VERSION_10
+ || r->headers_out.status != NGX_HTTP_OK
+ || r != r->main
+ || r->headers_out.content_length_n == -1
+ || !r->allow_ranges)
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->max_ranges == 0) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ if (r->headers_in.range == NULL
+ || r->headers_in.range->value.len < 7
+ || ngx_strncasecmp(r->headers_in.range->value.data,
+ (u_char *) "bytes=", 6)
+ != 0)
+ {
+ goto next_filter;
+ }
+
+ if (r->headers_in.if_range) {
+
+ if_range = &r->headers_in.if_range->value;
+
+ if (if_range->len >= 2 && if_range->data[if_range->len - 1] == '"') {
+
+ if (r->headers_out.etag == NULL) {
+ goto next_filter;
+ }
+
+ etag = &r->headers_out.etag->value;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http ir:%V etag:%V", if_range, etag);
+
+ if (if_range->len != etag->len
+ || ngx_strncmp(if_range->data, etag->data, etag->len) != 0)
+ {
+ goto next_filter;
+ }
+
+ goto parse;
+ }
+
+ if (r->headers_out.last_modified_time == (time_t) -1) {
+ goto next_filter;
+ }
+
+ if_range_time = ngx_http_parse_time(if_range->data, if_range->len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http ir:%d lm:%d",
+ if_range_time, r->headers_out.last_modified_time);
+
+ if (if_range_time != r->headers_out.last_modified_time) {
+ goto next_filter;
+ }
+ }
+
+parse:
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ ranges = r->single_range ? 1 : clcf->max_ranges;
+
+ switch (ngx_http_range_parse(r, ctx, ranges)) {
+
+ case NGX_OK:
+ ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
+
+ r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
+ r->headers_out.status_line.len = 0;
+
+ if (ctx->ranges.nelts == 1) {
+ return ngx_http_range_singlepart_header(r, ctx);
+ }
+
+ return ngx_http_range_multipart_header(r, ctx);
+
+ case NGX_HTTP_RANGE_NOT_SATISFIABLE:
+ return ngx_http_range_not_satisfiable(r);
+
+ case NGX_ERROR:
+ return NGX_ERROR;
+
+ default: /* NGX_DECLINED */
+ break;
+ }
+
+next_filter:
+
+ r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
+ if (r->headers_out.accept_ranges == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->headers_out.accept_ranges->hash = 1;
+ ngx_str_set(&r->headers_out.accept_ranges->key, "Accept-Ranges");
+ ngx_str_set(&r->headers_out.accept_ranges->value, "bytes");
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx,
+ ngx_uint_t ranges)
+{
+ u_char *p;
+ off_t start, end, size, content_length;
+ ngx_uint_t suffix;
+ ngx_http_range_t *range;
+
+ p = r->headers_in.range->value.data + 6;
+ size = 0;
+ content_length = r->headers_out.content_length_n;
+
+ for ( ;; ) {
+ start = 0;
+ end = 0;
+ suffix = 0;
+
+ while (*p == ' ') { p++; }
+
+ if (*p != '-') {
+ if (*p < '0' || *p > '9') {
+ return NGX_HTTP_RANGE_NOT_SATISFIABLE;
+ }
+
+ while (*p >= '0' && *p <= '9') {
+ start = start * 10 + *p++ - '0';
+ }
+
+ while (*p == ' ') { p++; }
+
+ if (*p++ != '-') {
+ return NGX_HTTP_RANGE_NOT_SATISFIABLE;
+ }
+
+ while (*p == ' ') { p++; }
+
+ if (*p == ',' || *p == '\0') {
+ end = content_length;
+ goto found;
+ }
+
+ } else {
+ suffix = 1;
+ p++;
+ }
+
+ if (*p < '0' || *p > '9') {
+ return NGX_HTTP_RANGE_NOT_SATISFIABLE;
+ }
+
+ while (*p >= '0' && *p <= '9') {
+ end = end * 10 + *p++ - '0';
+ }
+
+ while (*p == ' ') { p++; }
+
+ if (*p != ',' && *p != '\0') {
+ return NGX_HTTP_RANGE_NOT_SATISFIABLE;
+ }
+
+ if (suffix) {
+ start = content_length - end;
+ end = content_length - 1;
+ }
+
+ if (end >= content_length) {
+ end = content_length;
+
+ } else {
+ end++;
+ }
+
+ found:
+
+ if (start < end) {
+ range = ngx_array_push(&ctx->ranges);
+ if (range == NULL) {
+ return NGX_ERROR;
+ }
+
+ range->start = start;
+ range->end = end;
+
+ size += end - start;
+
+ if (ranges-- == 0) {
+ return NGX_DECLINED;
+ }
+ }
+
+ if (*p++ != ',') {
+ break;
+ }
+ }
+
+ if (ctx->ranges.nelts == 0) {
+ return NGX_HTTP_RANGE_NOT_SATISFIABLE;
+ }
+
+ if (size > content_length) {
+ return NGX_DECLINED;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_range_singlepart_header(ngx_http_request_t *r,
+ ngx_http_range_filter_ctx_t *ctx)
+{
+ ngx_table_elt_t *content_range;
+ ngx_http_range_t *range;
+
+ content_range = ngx_list_push(&r->headers_out.headers);
+ if (content_range == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->headers_out.content_range = content_range;
+
+ content_range->hash = 1;
+ ngx_str_set(&content_range->key, "Content-Range");
+
+ content_range->value.data = ngx_pnalloc(r->pool,
+ sizeof("bytes -/") - 1 + 3 * NGX_OFF_T_LEN);
+ if (content_range->value.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ /* "Content-Range: bytes SSSS-EEEE/TTTT" header */
+
+ range = ctx->ranges.elts;
+
+ content_range->value.len = ngx_sprintf(content_range->value.data,
+ "bytes %O-%O/%O",
+ range->start, range->end - 1,
+ r->headers_out.content_length_n)
+ - content_range->value.data;
+
+ r->headers_out.content_length_n = range->end - range->start;
+
+ if (r->headers_out.content_length) {
+ r->headers_out.content_length->hash = 0;
+ r->headers_out.content_length = NULL;
+ }
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_range_multipart_header(ngx_http_request_t *r,
+ ngx_http_range_filter_ctx_t *ctx)
+{
+ size_t len;
+ ngx_uint_t i;
+ ngx_http_range_t *range;
+ ngx_atomic_uint_t boundary;
+
+ len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
+ + sizeof(CRLF "Content-Type: ") - 1
+ + r->headers_out.content_type.len
+ + sizeof(CRLF "Content-Range: bytes ") - 1;
+
+ if (r->headers_out.content_type_len == r->headers_out.content_type.len
+ && r->headers_out.charset.len)
+ {
+ len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
+ }
+
+ ctx->boundary_header.data = ngx_pnalloc(r->pool, len);
+ if (ctx->boundary_header.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ boundary = ngx_next_temp_number(0);
+
+ /*
+ * The boundary header of the range:
+ * CRLF
+ * "--0123456789" CRLF
+ * "Content-Type: image/jpeg" CRLF
+ * "Content-Range: bytes "
+ */
+
+ if (r->headers_out.content_type_len == r->headers_out.content_type.len
+ && r->headers_out.charset.len)
+ {
+ ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
+ CRLF "--%0muA" CRLF
+ "Content-Type: %V; charset=%V" CRLF
+ "Content-Range: bytes ",
+ boundary,
+ &r->headers_out.content_type,
+ &r->headers_out.charset)
+ - ctx->boundary_header.data;
+
+ } else if (r->headers_out.content_type.len) {
+ ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
+ CRLF "--%0muA" CRLF
+ "Content-Type: %V" CRLF
+ "Content-Range: bytes ",
+ boundary,
+ &r->headers_out.content_type)
+ - ctx->boundary_header.data;
+
+ } else {
+ ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,
+ CRLF "--%0muA" CRLF
+ "Content-Range: bytes ",
+ boundary)
+ - ctx->boundary_header.data;
+ }
+
+ r->headers_out.content_type.data =
+ ngx_pnalloc(r->pool,
+ sizeof("Content-Type: multipart/byteranges; boundary=") - 1
+ + NGX_ATOMIC_T_LEN);
+
+ if (r->headers_out.content_type.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->headers_out.content_type_lowcase = NULL;
+
+ /* "Content-Type: multipart/byteranges; boundary=0123456789" */
+
+ r->headers_out.content_type.len =
+ ngx_sprintf(r->headers_out.content_type.data,
+ "multipart/byteranges; boundary=%0muA",
+ boundary)
+ - r->headers_out.content_type.data;
+
+ r->headers_out.content_type_len = r->headers_out.content_type.len;
+
+ r->headers_out.charset.len = 0;
+
+ /* the size of the last boundary CRLF "--0123456789--" CRLF */
+
+ len = sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1;
+
+ range = ctx->ranges.elts;
+ for (i = 0; i < ctx->ranges.nelts; i++) {
+
+ /* the size of the range: "SSSS-EEEE/TTTT" CRLF CRLF */
+
+ range[i].content_range.data =
+ ngx_pnalloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4);
+
+ if (range[i].content_range.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ range[i].content_range.len = ngx_sprintf(range[i].content_range.data,
+ "%O-%O/%O" CRLF CRLF,
+ range[i].start, range[i].end - 1,
+ r->headers_out.content_length_n)
+ - range[i].content_range.data;
+
+ len += ctx->boundary_header.len + range[i].content_range.len
+ + (size_t) (range[i].end - range[i].start);
+ }
+
+ r->headers_out.content_length_n = len;
+
+ if (r->headers_out.content_length) {
+ r->headers_out.content_length->hash = 0;
+ r->headers_out.content_length = NULL;
+ }
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_range_not_satisfiable(ngx_http_request_t *r)
+{
+ ngx_table_elt_t *content_range;
+
+ r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;
+
+ content_range = ngx_list_push(&r->headers_out.headers);
+ if (content_range == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->headers_out.content_range = content_range;
+
+ content_range->hash = 1;
+ ngx_str_set(&content_range->key, "Content-Range");
+
+ content_range->value.data = ngx_pnalloc(r->pool,
+ sizeof("bytes */") - 1 + NGX_OFF_T_LEN);
+ if (content_range->value.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ content_range->value.len = ngx_sprintf(content_range->value.data,
+ "bytes */%O",
+ r->headers_out.content_length_n)
+ - content_range->value.data;
+
+ ngx_http_clear_content_length(r);
+
+ return NGX_HTTP_RANGE_NOT_SATISFIABLE;
+}
+
+
+static ngx_int_t
+ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ ngx_http_range_filter_ctx_t *ctx;
+
+ if (in == NULL) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module);
+
+ if (ctx == NULL) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ if (ctx->ranges.nelts == 1) {
+ return ngx_http_range_singlepart_body(r, ctx, in);
+ }
+
+ /*
+ * multipart ranges are supported only if whole body is in a single buffer
+ */
+
+ if (ngx_buf_special(in->buf)) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return ngx_http_range_multipart_body(r, ctx, in);
+}
+
+
+static ngx_int_t
+ngx_http_range_test_overlapped(ngx_http_request_t *r,
+ ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
+{
+ off_t start, last;
+ ngx_buf_t *buf;
+ ngx_uint_t i;
+ ngx_http_range_t *range;
+
+ if (ctx->offset) {
+ goto overlapped;
+ }
+
+ buf = in->buf;
+
+ if (!buf->last_buf) {
+ start = ctx->offset;
+ last = ctx->offset + ngx_buf_size(buf);
+
+ range = ctx->ranges.elts;
+ for (i = 0; i < ctx->ranges.nelts; i++) {
+ if (start > range[i].start || last < range[i].end) {
+ goto overlapped;
+ }
+ }
+ }
+
+ ctx->offset = ngx_buf_size(buf);
+
+ return NGX_OK;
+
+overlapped:
+
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "range in overlapped buffers");
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_range_singlepart_body(ngx_http_request_t *r,
+ ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
+{
+ off_t start, last;
+ ngx_buf_t *buf;
+ ngx_chain_t *out, *cl, **ll;
+ ngx_http_range_t *range;
+
+ out = NULL;
+ ll = &out;
+ range = ctx->ranges.elts;
+
+ for (cl = in; cl; cl = cl->next) {
+
+ buf = cl->buf;
+
+ start = ctx->offset;
+ last = ctx->offset + ngx_buf_size(buf);
+
+ ctx->offset = last;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http range body buf: %O-%O", start, last);
+
+ if (ngx_buf_special(buf)) {
+ *ll = cl;
+ ll = &cl->next;
+ continue;
+ }
+
+ if (range->end <= start || range->start >= last) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http range body skip");
+
+ if (buf->in_file) {
+ buf->file_pos = buf->file_last;
+ }
+
+ buf->pos = buf->last;
+ buf->sync = 1;
+
+ continue;
+ }
+
+ if (range->start > start) {
+
+ if (buf->in_file) {
+ buf->file_pos += range->start - start;
+ }
+
+ if (ngx_buf_in_memory(buf)) {
+ buf->pos += (size_t) (range->start - start);
+ }
+ }
+
+ if (range->end <= last) {
+
+ if (buf->in_file) {
+ buf->file_last -= last - range->end;
+ }
+
+ if (ngx_buf_in_memory(buf)) {
+ buf->last -= (size_t) (last - range->end);
+ }
+
+ buf->last_buf = 1;
+ *ll = cl;
+ cl->next = NULL;
+
+ break;
+ }
+
+ *ll = cl;
+ ll = &cl->next;
+ }
+
+ if (out == NULL) {
+ return NGX_OK;
+ }
+
+ return ngx_http_next_body_filter(r, out);
+}
+
+
+static ngx_int_t
+ngx_http_range_multipart_body(ngx_http_request_t *r,
+ ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)
+{
+ ngx_buf_t *b, *buf;
+ ngx_uint_t i;
+ ngx_chain_t *out, *hcl, *rcl, *dcl, **ll;
+ ngx_http_range_t *range;
+
+ ll = &out;
+ buf = in->buf;
+ range = ctx->ranges.elts;
+
+ for (i = 0; i < ctx->ranges.nelts; i++) {
+
+ /*
+ * The boundary header of the range:
+ * CRLF
+ * "--0123456789" CRLF
+ * "Content-Type: image/jpeg" CRLF
+ * "Content-Range: bytes "
+ */
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->memory = 1;
+ b->pos = ctx->boundary_header.data;
+ b->last = ctx->boundary_header.data + ctx->boundary_header.len;
+
+ hcl = ngx_alloc_chain_link(r->pool);
+ if (hcl == NULL) {
+ return NGX_ERROR;
+ }
+
+ hcl->buf = b;
+
+
+ /* "SSSS-EEEE/TTTT" CRLF CRLF */
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->temporary = 1;
+ b->pos = range[i].content_range.data;
+ b->last = range[i].content_range.data + range[i].content_range.len;
+
+ rcl = ngx_alloc_chain_link(r->pool);
+ if (rcl == NULL) {
+ return NGX_ERROR;
+ }
+
+ rcl->buf = b;
+
+
+ /* the range data */
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->in_file = buf->in_file;
+ b->temporary = buf->temporary;
+ b->memory = buf->memory;
+ b->mmap = buf->mmap;
+ b->file = buf->file;
+
+ if (buf->in_file) {
+ b->file_pos = buf->file_pos + range[i].start;
+ b->file_last = buf->file_pos + range[i].end;
+ }
+
+ if (ngx_buf_in_memory(buf)) {
+ b->pos = buf->pos + (size_t) range[i].start;
+ b->last = buf->pos + (size_t) range[i].end;
+ }
+
+ dcl = ngx_alloc_chain_link(r->pool);
+ if (dcl == NULL) {
+ return NGX_ERROR;
+ }
+
+ dcl->buf = b;
+
+ *ll = hcl;
+ hcl->next = rcl;
+ rcl->next = dcl;
+ ll = &dcl->next;
+ }
+
+ /* the last boundary CRLF "--0123456789--" CRLF */
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->temporary = 1;
+ b->last_buf = 1;
+
+ b->pos = ngx_pnalloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN
+ + sizeof("--" CRLF) - 1);
+ if (b->pos == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->last = ngx_cpymem(b->pos, ctx->boundary_header.data,
+ sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN);
+ *b->last++ = '-'; *b->last++ = '-';
+ *b->last++ = CR; *b->last++ = LF;
+
+ hcl = ngx_alloc_chain_link(r->pool);
+ if (hcl == NULL) {
+ return NGX_ERROR;
+ }
+
+ hcl->buf = b;
+ hcl->next = NULL;
+
+ *ll = hcl;
+
+ return ngx_http_next_body_filter(r, out);
+}
+
+
+static ngx_int_t
+ngx_http_range_header_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_range_header_filter;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_range_body_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_range_body_filter;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_realip_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_realip_module.c
new file mode 100644
index 00000000000..7a621180379
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_realip_module.c
@@ -0,0 +1,442 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_REALIP_XREALIP 0
+#define NGX_HTTP_REALIP_XFWD 1
+#define NGX_HTTP_REALIP_HEADER 2
+#define NGX_HTTP_REALIP_PROXY 3
+
+
+typedef struct {
+ ngx_array_t *from; /* array of ngx_cidr_t */
+ ngx_uint_t type;
+ ngx_uint_t hash;
+ ngx_str_t header;
+ ngx_flag_t recursive;
+} ngx_http_realip_loc_conf_t;
+
+
+typedef struct {
+ ngx_connection_t *connection;
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+ ngx_str_t addr_text;
+} ngx_http_realip_ctx_t;
+
+
+static ngx_int_t ngx_http_realip_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_realip_set_addr(ngx_http_request_t *r,
+ ngx_addr_t *addr);
+static void ngx_http_realip_cleanup(void *data);
+static char *ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static void *ngx_http_realip_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_realip_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static ngx_int_t ngx_http_realip_init(ngx_conf_t *cf);
+
+
+static ngx_command_t ngx_http_realip_commands[] = {
+
+ { ngx_string("set_real_ip_from"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_realip_from,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("real_ip_header"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_realip,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("real_ip_recursive"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_realip_loc_conf_t, recursive),
+ NULL },
+
+ ngx_null_command
+};
+
+
+
+static ngx_http_module_t ngx_http_realip_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_realip_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_realip_create_loc_conf, /* create location configuration */
+ ngx_http_realip_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_realip_module = {
+ NGX_MODULE_V1,
+ &ngx_http_realip_module_ctx, /* module context */
+ ngx_http_realip_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_realip_handler(ngx_http_request_t *r)
+{
+ u_char *p;
+ size_t len;
+ ngx_str_t *value;
+ ngx_uint_t i, hash;
+ ngx_addr_t addr;
+ ngx_array_t *xfwd;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *header;
+ ngx_connection_t *c;
+ ngx_http_realip_ctx_t *ctx;
+ ngx_http_realip_loc_conf_t *rlcf;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module);
+
+ if (ctx) {
+ return NGX_DECLINED;
+ }
+
+ rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module);
+
+ if (rlcf->from == NULL) {
+ return NGX_DECLINED;
+ }
+
+ switch (rlcf->type) {
+
+ case NGX_HTTP_REALIP_XREALIP:
+
+ if (r->headers_in.x_real_ip == NULL) {
+ return NGX_DECLINED;
+ }
+
+ value = &r->headers_in.x_real_ip->value;
+ xfwd = NULL;
+
+ break;
+
+ case NGX_HTTP_REALIP_XFWD:
+
+ xfwd = &r->headers_in.x_forwarded_for;
+
+ if (xfwd->elts == NULL) {
+ return NGX_DECLINED;
+ }
+
+ value = NULL;
+
+ break;
+
+ case NGX_HTTP_REALIP_PROXY:
+
+ value = &r->connection->proxy_protocol_addr;
+
+ if (value->len == 0) {
+ return NGX_DECLINED;
+ }
+
+ xfwd = NULL;
+
+ break;
+
+ default: /* NGX_HTTP_REALIP_HEADER */
+
+ part = &r->headers_in.headers.part;
+ header = part->elts;
+
+ hash = rlcf->hash;
+ len = rlcf->header.len;
+ p = rlcf->header.data;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (hash == header[i].hash
+ && len == header[i].key.len
+ && ngx_strncmp(p, header[i].lowcase_key, len) == 0)
+ {
+ value = &header[i].value;
+ xfwd = NULL;
+
+ goto found;
+ }
+ }
+
+ return NGX_DECLINED;
+ }
+
+found:
+
+ c = r->connection;
+
+ addr.sockaddr = c->sockaddr;
+ addr.socklen = c->socklen;
+ /* addr.name = c->addr_text; */
+
+ if (ngx_http_get_forwarded_addr(r, &addr, xfwd, value, rlcf->from,
+ rlcf->recursive)
+ != NGX_DECLINED)
+ {
+ return ngx_http_realip_set_addr(r, &addr);
+ }
+
+ return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_realip_set_addr(ngx_http_request_t *r, ngx_addr_t *addr)
+{
+ size_t len;
+ u_char *p;
+ u_char text[NGX_SOCKADDR_STRLEN];
+ ngx_connection_t *c;
+ ngx_pool_cleanup_t *cln;
+ ngx_http_realip_ctx_t *ctx;
+
+ cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_realip_ctx_t));
+ if (cln == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ctx = cln->data;
+ ngx_http_set_ctx(r, ctx, ngx_http_realip_module);
+
+ c = r->connection;
+
+ len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text,
+ NGX_SOCKADDR_STRLEN, 0);
+ if (len == 0) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ p = ngx_pnalloc(c->pool, len);
+ if (p == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_memcpy(p, text, len);
+
+ cln->handler = ngx_http_realip_cleanup;
+
+ ctx->connection = c;
+ ctx->sockaddr = c->sockaddr;
+ ctx->socklen = c->socklen;
+ ctx->addr_text = c->addr_text;
+
+ c->sockaddr = addr->sockaddr;
+ c->socklen = addr->socklen;
+ c->addr_text.len = len;
+ c->addr_text.data = p;
+
+ return NGX_DECLINED;
+}
+
+
+static void
+ngx_http_realip_cleanup(void *data)
+{
+ ngx_http_realip_ctx_t *ctx = data;
+
+ ngx_connection_t *c;
+
+ c = ctx->connection;
+
+ c->sockaddr = ctx->sockaddr;
+ c->socklen = ctx->socklen;
+ c->addr_text = ctx->addr_text;
+}
+
+
+static char *
+ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_realip_loc_conf_t *rlcf = conf;
+
+ ngx_int_t rc;
+ ngx_str_t *value;
+ ngx_cidr_t *cidr;
+
+ value = cf->args->elts;
+
+ if (rlcf->from == NULL) {
+ rlcf->from = ngx_array_create(cf->pool, 2,
+ sizeof(ngx_cidr_t));
+ if (rlcf->from == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ cidr = ngx_array_push(rlcf->from);
+ if (cidr == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+ if (ngx_strcmp(value[1].data, "unix:") == 0) {
+ cidr->family = AF_UNIX;
+ return NGX_CONF_OK;
+ }
+
+#endif
+
+ rc = ngx_ptocidr(&value[1], cidr);
+
+ if (rc == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
+ &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "low address bits of %V are meaningless", &value[1]);
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_realip_loc_conf_t *rlcf = conf;
+
+ ngx_str_t *value;
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "X-Real-IP") == 0) {
+ rlcf->type = NGX_HTTP_REALIP_XREALIP;
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_strcmp(value[1].data, "X-Forwarded-For") == 0) {
+ rlcf->type = NGX_HTTP_REALIP_XFWD;
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_strcmp(value[1].data, "proxy_protocol") == 0) {
+ rlcf->type = NGX_HTTP_REALIP_PROXY;
+ return NGX_CONF_OK;
+ }
+
+ rlcf->type = NGX_HTTP_REALIP_HEADER;
+ rlcf->hash = ngx_hash_strlow(value[1].data, value[1].data, value[1].len);
+ rlcf->header = value[1];
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_realip_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_realip_loc_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_realip_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->from = NULL;
+ * conf->hash = 0;
+ * conf->header = { 0, NULL };
+ */
+
+ conf->type = NGX_CONF_UNSET_UINT;
+ conf->recursive = NGX_CONF_UNSET;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_realip_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_realip_loc_conf_t *prev = parent;
+ ngx_http_realip_loc_conf_t *conf = child;
+
+ if (conf->from == NULL) {
+ conf->from = prev->from;
+ }
+
+ ngx_conf_merge_uint_value(conf->type, prev->type, NGX_HTTP_REALIP_XREALIP);
+ ngx_conf_merge_value(conf->recursive, prev->recursive, 0);
+
+ if (conf->header.len == 0) {
+ conf->hash = prev->hash;
+ conf->header = prev->header;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_realip_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_realip_handler;
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_realip_handler;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_referer_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_referer_module.c
new file mode 100644
index 00000000000..b417eb227b0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_referer_module.c
@@ -0,0 +1,671 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_REFERER_NO_URI_PART ((void *) 4)
+
+
+typedef struct {
+ ngx_hash_combined_t hash;
+
+#if (NGX_PCRE)
+ ngx_array_t *regex;
+ ngx_array_t *server_name_regex;
+#endif
+
+ ngx_flag_t no_referer;
+ ngx_flag_t blocked_referer;
+ ngx_flag_t server_names;
+
+ ngx_hash_keys_arrays_t *keys;
+
+ ngx_uint_t referer_hash_max_size;
+ ngx_uint_t referer_hash_bucket_size;
+} ngx_http_referer_conf_t;
+
+
+static void * ngx_http_referer_create_conf(ngx_conf_t *cf);
+static char * ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static ngx_int_t ngx_http_add_referer(ngx_conf_t *cf,
+ ngx_hash_keys_arrays_t *keys, ngx_str_t *value, ngx_str_t *uri);
+static ngx_int_t ngx_http_add_regex_referer(ngx_conf_t *cf,
+ ngx_http_referer_conf_t *rlcf, ngx_str_t *name);
+#if (NGX_PCRE)
+static ngx_int_t ngx_http_add_regex_server_name(ngx_conf_t *cf,
+ ngx_http_referer_conf_t *rlcf, ngx_http_regex_t *regex);
+#endif
+static int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one,
+ const void *two);
+
+
+static ngx_command_t ngx_http_referer_commands[] = {
+
+ { ngx_string("valid_referers"),
+ NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_valid_referers,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("referer_hash_max_size"),
+ NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_referer_conf_t, referer_hash_max_size),
+ NULL },
+
+ { ngx_string("referer_hash_bucket_size"),
+ NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_referer_conf_t, referer_hash_bucket_size),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_referer_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_referer_create_conf, /* create location configuration */
+ ngx_http_referer_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_referer_module = {
+ NGX_MODULE_V1,
+ &ngx_http_referer_module_ctx, /* module context */
+ ngx_http_referer_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ u_char *p, *ref, *last;
+ size_t len;
+ ngx_str_t *uri;
+ ngx_uint_t i, key;
+ ngx_http_referer_conf_t *rlcf;
+ u_char buf[256];
+#if (NGX_PCRE)
+ ngx_int_t rc;
+ ngx_str_t referer;
+#endif
+
+ rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);
+
+ if (rlcf->hash.hash.buckets == NULL
+ && rlcf->hash.wc_head == NULL
+ && rlcf->hash.wc_tail == NULL
+#if (NGX_PCRE)
+ && rlcf->regex == NULL
+ && rlcf->server_name_regex == NULL
+#endif
+ )
+ {
+ goto valid;
+ }
+
+ if (r->headers_in.referer == NULL) {
+ if (rlcf->no_referer) {
+ goto valid;
+ }
+
+ goto invalid;
+ }
+
+ len = r->headers_in.referer->value.len;
+ ref = r->headers_in.referer->value.data;
+
+ if (len >= sizeof("http://i.ru") - 1) {
+ last = ref + len;
+
+ if (ngx_strncasecmp(ref, (u_char *) "http://", 7) == 0) {
+ ref += 7;
+ len -= 7;
+ goto valid_scheme;
+
+ } else if (ngx_strncasecmp(ref, (u_char *) "https://", 8) == 0) {
+ ref += 8;
+ len -= 8;
+ goto valid_scheme;
+ }
+ }
+
+ if (rlcf->blocked_referer) {
+ goto valid;
+ }
+
+ goto invalid;
+
+valid_scheme:
+
+ i = 0;
+ key = 0;
+
+ for (p = ref; p < last; p++) {
+ if (*p == '/' || *p == ':') {
+ break;
+ }
+
+ if (i == 256) {
+ goto invalid;
+ }
+
+ buf[i] = ngx_tolower(*p);
+ key = ngx_hash(key, buf[i++]);
+ }
+
+ uri = ngx_hash_find_combined(&rlcf->hash, key, buf, p - ref);
+
+ if (uri) {
+ goto uri;
+ }
+
+#if (NGX_PCRE)
+
+ if (rlcf->server_name_regex) {
+ referer.len = p - ref;
+ referer.data = buf;
+
+ rc = ngx_regex_exec_array(rlcf->server_name_regex, &referer,
+ r->connection->log);
+
+ if (rc == NGX_OK) {
+ goto valid;
+ }
+
+ if (rc == NGX_ERROR) {
+ return rc;
+ }
+
+ /* NGX_DECLINED */
+ }
+
+ if (rlcf->regex) {
+ referer.len = len;
+ referer.data = ref;
+
+ rc = ngx_regex_exec_array(rlcf->regex, &referer, r->connection->log);
+
+ if (rc == NGX_OK) {
+ goto valid;
+ }
+
+ if (rc == NGX_ERROR) {
+ return rc;
+ }
+
+ /* NGX_DECLINED */
+ }
+
+#endif
+
+invalid:
+
+ *v = ngx_http_variable_true_value;
+
+ return NGX_OK;
+
+uri:
+
+ for ( /* void */ ; p < last; p++) {
+ if (*p == '/') {
+ break;
+ }
+ }
+
+ len = last - p;
+
+ if (uri == NGX_HTTP_REFERER_NO_URI_PART) {
+ goto valid;
+ }
+
+ if (len < uri->len || ngx_strncmp(uri->data, p, uri->len) != 0) {
+ goto invalid;
+ }
+
+valid:
+
+ *v = ngx_http_variable_null_value;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_referer_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_referer_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->hash = { NULL };
+ * conf->server_names = 0;
+ * conf->keys = NULL;
+ */
+
+#if (NGX_PCRE)
+ conf->regex = NGX_CONF_UNSET_PTR;
+ conf->server_name_regex = NGX_CONF_UNSET_PTR;
+#endif
+
+ conf->no_referer = NGX_CONF_UNSET;
+ conf->blocked_referer = NGX_CONF_UNSET;
+ conf->referer_hash_max_size = NGX_CONF_UNSET_UINT;
+ conf->referer_hash_bucket_size = NGX_CONF_UNSET_UINT;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_referer_conf_t *prev = parent;
+ ngx_http_referer_conf_t *conf = child;
+
+ ngx_uint_t n;
+ ngx_hash_init_t hash;
+ ngx_http_server_name_t *sn;
+ ngx_http_core_srv_conf_t *cscf;
+
+ if (conf->keys == NULL) {
+ conf->hash = prev->hash;
+
+#if (NGX_PCRE)
+ ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
+ ngx_conf_merge_ptr_value(conf->server_name_regex,
+ prev->server_name_regex, NULL);
+#endif
+ ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0);
+ ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0);
+ ngx_conf_merge_uint_value(conf->referer_hash_max_size,
+ prev->referer_hash_max_size, 2048);
+ ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,
+ prev->referer_hash_bucket_size, 64);
+
+ return NGX_CONF_OK;
+ }
+
+ if (conf->server_names == 1) {
+ cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);
+
+ sn = cscf->server_names.elts;
+ for (n = 0; n < cscf->server_names.nelts; n++) {
+
+#if (NGX_PCRE)
+ if (sn[n].regex) {
+
+ if (ngx_http_add_regex_server_name(cf, conf, sn[n].regex)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+#endif
+
+ if (ngx_http_add_referer(cf, conf->keys, &sn[n].name, NULL)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+ }
+ }
+
+ if ((conf->no_referer == 1 || conf->blocked_referer == 1)
+ && conf->keys->keys.nelts == 0
+ && conf->keys->dns_wc_head.nelts == 0
+ && conf->keys->dns_wc_tail.nelts == 0)
+ {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "the \"none\" or \"blocked\" referers are specified "
+ "in the \"valid_referers\" directive "
+ "without any valid referer");
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_uint_value(conf->referer_hash_max_size,
+ prev->referer_hash_max_size, 2048);
+ ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,
+ prev->referer_hash_bucket_size, 64);
+ conf->referer_hash_bucket_size = ngx_align(conf->referer_hash_bucket_size,
+ ngx_cacheline_size);
+
+ hash.key = ngx_hash_key_lc;
+ hash.max_size = conf->referer_hash_max_size;
+ hash.bucket_size = conf->referer_hash_bucket_size;
+ hash.name = "referer_hash";
+ hash.pool = cf->pool;
+
+ if (conf->keys->keys.nelts) {
+ hash.hash = &conf->hash.hash;
+ hash.temp_pool = NULL;
+
+ if (ngx_hash_init(&hash, conf->keys->keys.elts, conf->keys->keys.nelts)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (conf->keys->dns_wc_head.nelts) {
+
+ ngx_qsort(conf->keys->dns_wc_head.elts,
+ (size_t) conf->keys->dns_wc_head.nelts,
+ sizeof(ngx_hash_key_t),
+ ngx_http_cmp_referer_wildcards);
+
+ hash.hash = NULL;
+ hash.temp_pool = cf->temp_pool;
+
+ if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_head.elts,
+ conf->keys->dns_wc_head.nelts)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;
+ }
+
+ if (conf->keys->dns_wc_tail.nelts) {
+
+ ngx_qsort(conf->keys->dns_wc_tail.elts,
+ (size_t) conf->keys->dns_wc_tail.nelts,
+ sizeof(ngx_hash_key_t),
+ ngx_http_cmp_referer_wildcards);
+
+ hash.hash = NULL;
+ hash.temp_pool = cf->temp_pool;
+
+ if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_tail.elts,
+ conf->keys->dns_wc_tail.nelts)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;
+ }
+
+#if (NGX_PCRE)
+ ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);
+ ngx_conf_merge_ptr_value(conf->server_name_regex, prev->server_name_regex,
+ NULL);
+#endif
+
+ if (conf->no_referer == NGX_CONF_UNSET) {
+ conf->no_referer = 0;
+ }
+
+ if (conf->blocked_referer == NGX_CONF_UNSET) {
+ conf->blocked_referer = 0;
+ }
+
+ conf->keys = NULL;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_referer_conf_t *rlcf = conf;
+
+ u_char *p;
+ ngx_str_t *value, uri, name;
+ ngx_uint_t i;
+ ngx_http_variable_t *var;
+
+ ngx_str_set(&name, "invalid_referer");
+
+ var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
+ if (var == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ var->get_handler = ngx_http_referer_variable;
+
+ if (rlcf->keys == NULL) {
+ rlcf->keys = ngx_pcalloc(cf->temp_pool, sizeof(ngx_hash_keys_arrays_t));
+ if (rlcf->keys == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ rlcf->keys->pool = cf->pool;
+ rlcf->keys->temp_pool = cf->pool;
+
+ if (ngx_hash_keys_array_init(rlcf->keys, NGX_HASH_SMALL) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+ if (value[i].len == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid referer \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_strcmp(value[i].data, "none") == 0) {
+ rlcf->no_referer = 1;
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "blocked") == 0) {
+ rlcf->blocked_referer = 1;
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "server_names") == 0) {
+ rlcf->server_names = 1;
+ continue;
+ }
+
+ if (value[i].data[0] == '~') {
+ if (ngx_http_add_regex_referer(cf, rlcf, &value[i]) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ ngx_str_null(&uri);
+
+ p = (u_char *) ngx_strchr(value[i].data, '/');
+
+ if (p) {
+ uri.len = (value[i].data + value[i].len) - p;
+ uri.data = p;
+ value[i].len = p - value[i].data;
+ }
+
+ if (ngx_http_add_referer(cf, rlcf->keys, &value[i], &uri) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,
+ ngx_str_t *value, ngx_str_t *uri)
+{
+ ngx_int_t rc;
+ ngx_str_t *u;
+
+ if (uri == NULL || uri->len == 0) {
+ u = NGX_HTTP_REFERER_NO_URI_PART;
+
+ } else {
+ u = ngx_palloc(cf->pool, sizeof(ngx_str_t));
+ if (u == NULL) {
+ return NGX_ERROR;
+ }
+
+ *u = *uri;
+ }
+
+ rc = ngx_hash_add_key(keys, value, u, NGX_HASH_WILDCARD_KEY);
+
+ if (rc == NGX_OK) {
+ return NGX_OK;
+ }
+
+ if (rc == NGX_DECLINED) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid hostname or wildcard \"%V\"", value);
+ }
+
+ if (rc == NGX_BUSY) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "conflicting parameter \"%V\"", value);
+ }
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_add_regex_referer(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
+ ngx_str_t *name)
+{
+#if (NGX_PCRE)
+ ngx_regex_elt_t *re;
+ ngx_regex_compile_t rc;
+ u_char errstr[NGX_MAX_CONF_ERRSTR];
+
+ if (name->len == 1) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty regex in \"%V\"", name);
+ return NGX_ERROR;
+ }
+
+ if (rlcf->regex == NGX_CONF_UNSET_PTR) {
+ rlcf->regex = ngx_array_create(cf->pool, 2, sizeof(ngx_regex_elt_t));
+ if (rlcf->regex == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ re = ngx_array_push(rlcf->regex);
+ if (re == NULL) {
+ return NGX_ERROR;
+ }
+
+ name->len--;
+ name->data++;
+
+ ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+ rc.pattern = *name;
+ rc.pool = cf->pool;
+ rc.options = NGX_REGEX_CASELESS;
+ rc.err.len = NGX_MAX_CONF_ERRSTR;
+ rc.err.data = errstr;
+
+ if (ngx_regex_compile(&rc) != NGX_OK) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
+ return NGX_ERROR;
+ }
+
+ re->regex = rc.regex;
+ re->name = name->data;
+
+ return NGX_OK;
+
+#else
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the using of the regex \"%V\" requires PCRE library",
+ name);
+
+ return NGX_ERROR;
+
+#endif
+}
+
+
+#if (NGX_PCRE)
+
+static ngx_int_t
+ngx_http_add_regex_server_name(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,
+ ngx_http_regex_t *regex)
+{
+ ngx_regex_elt_t *re;
+
+ if (rlcf->server_name_regex == NGX_CONF_UNSET_PTR) {
+ rlcf->server_name_regex = ngx_array_create(cf->pool, 2,
+ sizeof(ngx_regex_elt_t));
+ if (rlcf->server_name_regex == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ re = ngx_array_push(rlcf->server_name_regex);
+ if (re == NULL) {
+ return NGX_ERROR;
+ }
+
+ re->regex = regex->regex;
+ re->name = regex->name.data;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static int ngx_libc_cdecl
+ngx_http_cmp_referer_wildcards(const void *one, const void *two)
+{
+ ngx_hash_key_t *first, *second;
+
+ first = (ngx_hash_key_t *) one;
+ second = (ngx_hash_key_t *) two;
+
+ return ngx_dns_strcmp(first->key.data, second->key.data);
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_rewrite_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_rewrite_module.c
new file mode 100644
index 00000000000..4081f877433
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_rewrite_module.c
@@ -0,0 +1,1025 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_array_t *codes; /* uintptr_t */
+
+ ngx_uint_t stack_size;
+
+ ngx_flag_t log;
+ ngx_flag_t uninitialized_variable_warn;
+} ngx_http_rewrite_loc_conf_t;
+
+
+static void *ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static ngx_int_t ngx_http_rewrite_init(ngx_conf_t *cf);
+static char *ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char * ngx_http_rewrite_if_condition(ngx_conf_t *cf,
+ ngx_http_rewrite_loc_conf_t *lcf);
+static char *ngx_http_rewrite_variable(ngx_conf_t *cf,
+ ngx_http_rewrite_loc_conf_t *lcf, ngx_str_t *value);
+static char *ngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char * ngx_http_rewrite_value(ngx_conf_t *cf,
+ ngx_http_rewrite_loc_conf_t *lcf, ngx_str_t *value);
+
+
+static ngx_command_t ngx_http_rewrite_commands[] = {
+
+ { ngx_string("rewrite"),
+ NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_TAKE23,
+ ngx_http_rewrite,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("return"),
+ NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_TAKE12,
+ ngx_http_rewrite_return,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("break"),
+ NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_NOARGS,
+ ngx_http_rewrite_break,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("if"),
+ NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE,
+ ngx_http_rewrite_if,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("set"),
+ NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_TAKE2,
+ ngx_http_rewrite_set,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("rewrite_log"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF
+ |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_rewrite_loc_conf_t, log),
+ NULL },
+
+ { ngx_string("uninitialized_variable_warn"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF
+ |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_rewrite_loc_conf_t, uninitialized_variable_warn),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_rewrite_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_rewrite_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_rewrite_create_loc_conf, /* create location configuration */
+ ngx_http_rewrite_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_rewrite_module = {
+ NGX_MODULE_V1,
+ &ngx_http_rewrite_module_ctx, /* module context */
+ ngx_http_rewrite_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_rewrite_handler(ngx_http_request_t *r)
+{
+ ngx_int_t index;
+ ngx_http_script_code_pt code;
+ ngx_http_script_engine_t *e;
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_core_main_conf_t *cmcf;
+ ngx_http_rewrite_loc_conf_t *rlcf;
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+ index = cmcf->phase_engine.location_rewrite_index;
+
+ if (r->phase_handler == index && r->loc_conf == cscf->ctx->loc_conf) {
+ /* skipping location rewrite phase for server null location */
+ return NGX_DECLINED;
+ }
+
+ rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);
+
+ if (rlcf->codes == NULL) {
+ return NGX_DECLINED;
+ }
+
+ e = ngx_pcalloc(r->pool, sizeof(ngx_http_script_engine_t));
+ if (e == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ e->sp = ngx_pcalloc(r->pool,
+ rlcf->stack_size * sizeof(ngx_http_variable_value_t));
+ if (e->sp == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ e->ip = rlcf->codes->elts;
+ e->request = r;
+ e->quote = 1;
+ e->log = rlcf->log;
+ e->status = NGX_DECLINED;
+
+ while (*(uintptr_t *) e->ip) {
+ code = *(ngx_http_script_code_pt *) e->ip;
+ code(e);
+ }
+
+ if (e->status < NGX_HTTP_BAD_REQUEST) {
+ return e->status;
+ }
+
+ if (r->err_status == 0) {
+ return e->status;
+ }
+
+ return r->err_status;
+}
+
+
+static ngx_int_t
+ngx_http_rewrite_var(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_http_variable_t *var;
+ ngx_http_core_main_conf_t *cmcf;
+ ngx_http_rewrite_loc_conf_t *rlcf;
+
+ rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);
+
+ if (rlcf->uninitialized_variable_warn == 0) {
+ *v = ngx_http_variable_null_value;
+ return NGX_OK;
+ }
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ var = cmcf->variables.elts;
+
+ /*
+ * the ngx_http_rewrite_module sets variables directly in r->variables,
+ * and they should be handled by ngx_http_get_indexed_variable(),
+ * so the handler is called only if the variable is not initialized
+ */
+
+ ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
+ "using uninitialized \"%V\" variable", &var[data].name);
+
+ *v = ngx_http_variable_null_value;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_rewrite_loc_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_rewrite_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->stack_size = NGX_CONF_UNSET_UINT;
+ conf->log = NGX_CONF_UNSET;
+ conf->uninitialized_variable_warn = NGX_CONF_UNSET;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_rewrite_loc_conf_t *prev = parent;
+ ngx_http_rewrite_loc_conf_t *conf = child;
+
+ uintptr_t *code;
+
+ ngx_conf_merge_value(conf->log, prev->log, 0);
+ ngx_conf_merge_value(conf->uninitialized_variable_warn,
+ prev->uninitialized_variable_warn, 1);
+ ngx_conf_merge_uint_value(conf->stack_size, prev->stack_size, 10);
+
+ if (conf->codes == NULL) {
+ return NGX_CONF_OK;
+ }
+
+ if (conf->codes == prev->codes) {
+ return NGX_CONF_OK;
+ }
+
+ code = ngx_array_push_n(conf->codes, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_rewrite_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_rewrite_handler;
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_rewrite_handler;
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_rewrite_loc_conf_t *lcf = conf;
+
+ ngx_str_t *value;
+ ngx_uint_t last;
+ ngx_regex_compile_t rc;
+ ngx_http_script_code_pt *code;
+ ngx_http_script_compile_t sc;
+ ngx_http_script_regex_code_t *regex;
+ ngx_http_script_regex_end_code_t *regex_end;
+ u_char errstr[NGX_MAX_CONF_ERRSTR];
+
+ regex = ngx_http_script_start_code(cf->pool, &lcf->codes,
+ sizeof(ngx_http_script_regex_code_t));
+ if (regex == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(regex, sizeof(ngx_http_script_regex_code_t));
+
+ value = cf->args->elts;
+
+ ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+ rc.pattern = value[1];
+ rc.err.len = NGX_MAX_CONF_ERRSTR;
+ rc.err.data = errstr;
+
+ /* TODO: NGX_REGEX_CASELESS */
+
+ regex->regex = ngx_http_regex_compile(cf, &rc);
+ if (regex->regex == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ regex->code = ngx_http_script_regex_start_code;
+ regex->uri = 1;
+ regex->name = value[1];
+
+ if (value[2].data[value[2].len - 1] == '?') {
+
+ /* the last "?" drops the original arguments */
+ value[2].len--;
+
+ } else {
+ regex->add_args = 1;
+ }
+
+ last = 0;
+
+ if (ngx_strncmp(value[2].data, "http://", sizeof("http://") - 1) == 0
+ || ngx_strncmp(value[2].data, "https://", sizeof("https://") - 1) == 0
+ || ngx_strncmp(value[2].data, "$scheme", sizeof("$scheme") - 1) == 0)
+ {
+ regex->status = NGX_HTTP_MOVED_TEMPORARILY;
+ regex->redirect = 1;
+ last = 1;
+ }
+
+ if (cf->args->nelts == 4) {
+ if (ngx_strcmp(value[3].data, "last") == 0) {
+ last = 1;
+
+ } else if (ngx_strcmp(value[3].data, "break") == 0) {
+ regex->break_cycle = 1;
+ last = 1;
+
+ } else if (ngx_strcmp(value[3].data, "redirect") == 0) {
+ regex->status = NGX_HTTP_MOVED_TEMPORARILY;
+ regex->redirect = 1;
+ last = 1;
+
+ } else if (ngx_strcmp(value[3].data, "permanent") == 0) {
+ regex->status = NGX_HTTP_MOVED_PERMANENTLY;
+ regex->redirect = 1;
+ last = 1;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[3]);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &value[2];
+ sc.lengths = &regex->lengths;
+ sc.values = &lcf->codes;
+ sc.variables = ngx_http_script_variables_count(&value[2]);
+ sc.main = regex;
+ sc.complete_lengths = 1;
+ sc.compile_args = !regex->redirect;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ regex = sc.main;
+
+ regex->size = sc.size;
+ regex->args = sc.args;
+
+ if (sc.variables == 0 && !sc.dup_capture) {
+ regex->lengths = NULL;
+ }
+
+ regex_end = ngx_http_script_add_code(lcf->codes,
+ sizeof(ngx_http_script_regex_end_code_t),
+ &regex);
+ if (regex_end == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ regex_end->code = ngx_http_script_regex_end_code;
+ regex_end->uri = regex->uri;
+ regex_end->args = regex->args;
+ regex_end->add_args = regex->add_args;
+ regex_end->redirect = regex->redirect;
+
+ if (last) {
+ code = ngx_http_script_add_code(lcf->codes, sizeof(uintptr_t), &regex);
+ if (code == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *code = NULL;
+ }
+
+ regex->next = (u_char *) lcf->codes->elts + lcf->codes->nelts
+ - (u_char *) regex;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_rewrite_loc_conf_t *lcf = conf;
+
+ u_char *p;
+ ngx_str_t *value, *v;
+ ngx_http_script_return_code_t *ret;
+ ngx_http_compile_complex_value_t ccv;
+
+ ret = ngx_http_script_start_code(cf->pool, &lcf->codes,
+ sizeof(ngx_http_script_return_code_t));
+ if (ret == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ ngx_memzero(ret, sizeof(ngx_http_script_return_code_t));
+
+ ret->code = ngx_http_script_return_code;
+
+ p = value[1].data;
+
+ ret->status = ngx_atoi(p, value[1].len);
+
+ if (ret->status == (uintptr_t) NGX_ERROR) {
+
+ if (cf->args->nelts == 2
+ && (ngx_strncmp(p, "http://", sizeof("http://") - 1) == 0
+ || ngx_strncmp(p, "https://", sizeof("https://") - 1) == 0
+ || ngx_strncmp(p, "$scheme", sizeof("$scheme") - 1) == 0))
+ {
+ ret->status = NGX_HTTP_MOVED_TEMPORARILY;
+ v = &value[1];
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid return code \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+
+ if (ret->status > 999) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid return code \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (cf->args->nelts == 2) {
+ return NGX_CONF_OK;
+ }
+
+ v = &value[2];
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = v;
+ ccv.complex_value = &ret->text;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_rewrite_loc_conf_t *lcf = conf;
+
+ ngx_http_script_code_pt *code;
+
+ code = ngx_http_script_start_code(cf->pool, &lcf->codes, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *code = ngx_http_script_break_code;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_rewrite_loc_conf_t *lcf = conf;
+
+ void *mconf;
+ char *rv;
+ u_char *elts;
+ ngx_uint_t i;
+ ngx_conf_t save;
+ ngx_http_module_t *module;
+ ngx_http_conf_ctx_t *ctx, *pctx;
+ ngx_http_core_loc_conf_t *clcf, *pclcf;
+ ngx_http_script_if_code_t *if_code;
+ ngx_http_rewrite_loc_conf_t *nlcf;
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ pctx = cf->ctx;
+ ctx->main_conf = pctx->main_conf;
+ ctx->srv_conf = pctx->srv_conf;
+
+ ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
+ if (ctx->loc_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[i]->ctx;
+
+ if (module->create_loc_conf) {
+
+ mconf = module->create_loc_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
+ }
+ }
+
+ pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
+
+ clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
+ clcf->loc_conf = ctx->loc_conf;
+ clcf->name = pclcf->name;
+ clcf->noname = 1;
+
+ if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_http_rewrite_if_condition(cf, lcf) != NGX_CONF_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if_code = ngx_array_push_n(lcf->codes, sizeof(ngx_http_script_if_code_t));
+ if (if_code == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if_code->code = ngx_http_script_if_code;
+
+ elts = lcf->codes->elts;
+
+
+ /* the inner directives must be compiled to the same code array */
+
+ nlcf = ctx->loc_conf[ngx_http_rewrite_module.ctx_index];
+ nlcf->codes = lcf->codes;
+
+
+ save = *cf;
+ cf->ctx = ctx;
+
+ if (pclcf->name.len == 0) {
+ if_code->loc_conf = NULL;
+ cf->cmd_type = NGX_HTTP_SIF_CONF;
+
+ } else {
+ if_code->loc_conf = ctx->loc_conf;
+ cf->cmd_type = NGX_HTTP_LIF_CONF;
+ }
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = save;
+
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+
+
+ if (elts != lcf->codes->elts) {
+ if_code = (ngx_http_script_if_code_t *)
+ ((u_char *) if_code + ((u_char *) lcf->codes->elts - elts));
+ }
+
+ if_code->next = (u_char *) lcf->codes->elts + lcf->codes->nelts
+ - (u_char *) if_code;
+
+ /* the code array belong to parent block */
+
+ nlcf->codes = NULL;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_rewrite_if_condition(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf)
+{
+ u_char *p;
+ size_t len;
+ ngx_str_t *value;
+ ngx_uint_t cur, last;
+ ngx_regex_compile_t rc;
+ ngx_http_script_code_pt *code;
+ ngx_http_script_file_code_t *fop;
+ ngx_http_script_regex_code_t *regex;
+ u_char errstr[NGX_MAX_CONF_ERRSTR];
+
+ value = cf->args->elts;
+ last = cf->args->nelts - 1;
+
+ if (value[1].len < 1 || value[1].data[0] != '(') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid condition \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (value[1].len == 1) {
+ cur = 2;
+
+ } else {
+ cur = 1;
+ value[1].len--;
+ value[1].data++;
+ }
+
+ if (value[last].len < 1 || value[last].data[value[last].len - 1] != ')') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid condition \"%V\"", &value[last]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (value[last].len == 1) {
+ last--;
+
+ } else {
+ value[last].len--;
+ value[last].data[value[last].len] = '\0';
+ }
+
+ len = value[cur].len;
+ p = value[cur].data;
+
+ if (len > 1 && p[0] == '$') {
+
+ if (cur != last && cur + 2 != last) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid condition \"%V\"", &value[cur]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_http_rewrite_variable(cf, lcf, &value[cur]) != NGX_CONF_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (cur == last) {
+ return NGX_CONF_OK;
+ }
+
+ cur++;
+
+ len = value[cur].len;
+ p = value[cur].data;
+
+ if (len == 1 && p[0] == '=') {
+
+ if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ code = ngx_http_script_start_code(cf->pool, &lcf->codes,
+ sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *code = ngx_http_script_equal_code;
+
+ return NGX_CONF_OK;
+ }
+
+ if (len == 2 && p[0] == '!' && p[1] == '=') {
+
+ if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ code = ngx_http_script_start_code(cf->pool, &lcf->codes,
+ sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *code = ngx_http_script_not_equal_code;
+ return NGX_CONF_OK;
+ }
+
+ if ((len == 1 && p[0] == '~')
+ || (len == 2 && p[0] == '~' && p[1] == '*')
+ || (len == 2 && p[0] == '!' && p[1] == '~')
+ || (len == 3 && p[0] == '!' && p[1] == '~' && p[2] == '*'))
+ {
+ regex = ngx_http_script_start_code(cf->pool, &lcf->codes,
+ sizeof(ngx_http_script_regex_code_t));
+ if (regex == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(regex, sizeof(ngx_http_script_regex_code_t));
+
+ ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+ rc.pattern = value[last];
+ rc.options = (p[len - 1] == '*') ? NGX_REGEX_CASELESS : 0;
+ rc.err.len = NGX_MAX_CONF_ERRSTR;
+ rc.err.data = errstr;
+
+ regex->regex = ngx_http_regex_compile(cf, &rc);
+ if (regex->regex == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ regex->code = ngx_http_script_regex_start_code;
+ regex->next = sizeof(ngx_http_script_regex_code_t);
+ regex->test = 1;
+ if (p[0] == '!') {
+ regex->negative_test = 1;
+ }
+ regex->name = value[last];
+
+ return NGX_CONF_OK;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unexpected \"%V\" in condition", &value[cur]);
+ return NGX_CONF_ERROR;
+
+ } else if ((len == 2 && p[0] == '-')
+ || (len == 3 && p[0] == '!' && p[1] == '-'))
+ {
+ if (cur + 1 != last) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid condition \"%V\"", &value[cur]);
+ return NGX_CONF_ERROR;
+ }
+
+ value[last].data[value[last].len] = '\0';
+ value[last].len++;
+
+ if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ fop = ngx_http_script_start_code(cf->pool, &lcf->codes,
+ sizeof(ngx_http_script_file_code_t));
+ if (fop == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ fop->code = ngx_http_script_file_code;
+
+ if (p[1] == 'f') {
+ fop->op = ngx_http_script_file_plain;
+ return NGX_CONF_OK;
+ }
+
+ if (p[1] == 'd') {
+ fop->op = ngx_http_script_file_dir;
+ return NGX_CONF_OK;
+ }
+
+ if (p[1] == 'e') {
+ fop->op = ngx_http_script_file_exists;
+ return NGX_CONF_OK;
+ }
+
+ if (p[1] == 'x') {
+ fop->op = ngx_http_script_file_exec;
+ return NGX_CONF_OK;
+ }
+
+ if (p[0] == '!') {
+ if (p[2] == 'f') {
+ fop->op = ngx_http_script_file_not_plain;
+ return NGX_CONF_OK;
+ }
+
+ if (p[2] == 'd') {
+ fop->op = ngx_http_script_file_not_dir;
+ return NGX_CONF_OK;
+ }
+
+ if (p[2] == 'e') {
+ fop->op = ngx_http_script_file_not_exists;
+ return NGX_CONF_OK;
+ }
+
+ if (p[2] == 'x') {
+ fop->op = ngx_http_script_file_not_exec;
+ return NGX_CONF_OK;
+ }
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid condition \"%V\"", &value[cur]);
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid condition \"%V\"", &value[cur]);
+
+ return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_http_rewrite_variable(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf,
+ ngx_str_t *value)
+{
+ ngx_int_t index;
+ ngx_http_script_var_code_t *var_code;
+
+ value->len--;
+ value->data++;
+
+ index = ngx_http_get_variable_index(cf, value);
+
+ if (index == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ var_code = ngx_http_script_start_code(cf->pool, &lcf->codes,
+ sizeof(ngx_http_script_var_code_t));
+ if (var_code == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ var_code->code = ngx_http_script_var_code;
+ var_code->index = index;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_rewrite_loc_conf_t *lcf = conf;
+
+ ngx_int_t index;
+ ngx_str_t *value;
+ ngx_http_variable_t *v;
+ ngx_http_script_var_code_t *vcode;
+ ngx_http_script_var_handler_code_t *vhcode;
+
+ value = cf->args->elts;
+
+ if (value[1].data[0] != '$') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ value[1].len--;
+ value[1].data++;
+
+ v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
+ if (v == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ index = ngx_http_get_variable_index(cf, &value[1]);
+ if (index == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (v->get_handler == NULL
+ && ngx_strncasecmp(value[1].data, (u_char *) "http_", 5) != 0
+ && ngx_strncasecmp(value[1].data, (u_char *) "sent_http_", 10) != 0
+ && ngx_strncasecmp(value[1].data, (u_char *) "upstream_http_", 14) != 0)
+ {
+ v->get_handler = ngx_http_rewrite_var;
+ v->data = index;
+ }
+
+ if (ngx_http_rewrite_value(cf, lcf, &value[2]) != NGX_CONF_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (v->set_handler) {
+ vhcode = ngx_http_script_start_code(cf->pool, &lcf->codes,
+ sizeof(ngx_http_script_var_handler_code_t));
+ if (vhcode == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ vhcode->code = ngx_http_script_var_set_handler_code;
+ vhcode->handler = v->set_handler;
+ vhcode->data = v->data;
+
+ return NGX_CONF_OK;
+ }
+
+ vcode = ngx_http_script_start_code(cf->pool, &lcf->codes,
+ sizeof(ngx_http_script_var_code_t));
+ if (vcode == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ vcode->code = ngx_http_script_set_var_code;
+ vcode->index = (uintptr_t) index;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_rewrite_value(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf,
+ ngx_str_t *value)
+{
+ ngx_int_t n;
+ ngx_http_script_compile_t sc;
+ ngx_http_script_value_code_t *val;
+ ngx_http_script_complex_value_code_t *complex;
+
+ n = ngx_http_script_variables_count(value);
+
+ if (n == 0) {
+ val = ngx_http_script_start_code(cf->pool, &lcf->codes,
+ sizeof(ngx_http_script_value_code_t));
+ if (val == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ n = ngx_atoi(value->data, value->len);
+
+ if (n == NGX_ERROR) {
+ n = 0;
+ }
+
+ val->code = ngx_http_script_value_code;
+ val->value = (uintptr_t) n;
+ val->text_len = (uintptr_t) value->len;
+ val->text_data = (uintptr_t) value->data;
+
+ return NGX_CONF_OK;
+ }
+
+ complex = ngx_http_script_start_code(cf->pool, &lcf->codes,
+ sizeof(ngx_http_script_complex_value_code_t));
+ if (complex == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ complex->code = ngx_http_script_complex_value_code;
+ complex->lengths = NULL;
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = value;
+ sc.lengths = &complex->lengths;
+ sc.values = &lcf->codes;
+ sc.variables = n;
+ sc.complete_lengths = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_scgi_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_scgi_module.c
new file mode 100644
index 00000000000..0be5066ec2a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_scgi_module.c
@@ -0,0 +1,1810 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Manlio Perillo (manlio.perillo@gmail.com)
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_http_upstream_conf_t upstream;
+
+ ngx_array_t *flushes;
+ ngx_array_t *params_len;
+ ngx_array_t *params;
+ ngx_array_t *params_source;
+
+ ngx_hash_t headers_hash;
+ ngx_uint_t header_params;
+
+ ngx_array_t *scgi_lengths;
+ ngx_array_t *scgi_values;
+
+#if (NGX_HTTP_CACHE)
+ ngx_http_complex_value_t cache_key;
+#endif
+} ngx_http_scgi_loc_conf_t;
+
+
+static ngx_int_t ngx_http_scgi_eval(ngx_http_request_t *r,
+ ngx_http_scgi_loc_conf_t *scf);
+static ngx_int_t ngx_http_scgi_create_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_scgi_reinit_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_scgi_process_status_line(ngx_http_request_t *r);
+static ngx_int_t ngx_http_scgi_process_header(ngx_http_request_t *r);
+static void ngx_http_scgi_abort_request(ngx_http_request_t *r);
+static void ngx_http_scgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
+
+static void *ngx_http_scgi_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static ngx_int_t ngx_http_scgi_merge_params(ngx_conf_t *cf,
+ ngx_http_scgi_loc_conf_t *conf, ngx_http_scgi_loc_conf_t *prev);
+
+static char *ngx_http_scgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_http_scgi_store(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+#if (NGX_HTTP_CACHE)
+static ngx_int_t ngx_http_scgi_create_key(ngx_http_request_t *r);
+static char *ngx_http_scgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_scgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+#endif
+
+
+static ngx_conf_bitmask_t ngx_http_scgi_next_upstream_masks[] = {
+ { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
+ { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
+ { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
+ { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },
+ { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
+ { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },
+ { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
+ { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },
+ { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
+ { ngx_null_string, 0 }
+};
+
+
+ngx_module_t ngx_http_scgi_module;
+
+
+static ngx_command_t ngx_http_scgi_commands[] = {
+
+ { ngx_string("scgi_pass"),
+ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
+ ngx_http_scgi_pass,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("scgi_store"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_scgi_store,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("scgi_store_access"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
+ ngx_conf_set_access_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.store_access),
+ NULL },
+
+ { ngx_string("scgi_buffering"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.buffering),
+ NULL },
+
+ { ngx_string("scgi_ignore_client_abort"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.ignore_client_abort),
+ NULL },
+
+ { ngx_string("scgi_bind"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_upstream_bind_set_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.local),
+ NULL },
+
+ { ngx_string("scgi_connect_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.connect_timeout),
+ NULL },
+
+ { ngx_string("scgi_send_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.send_timeout),
+ NULL },
+
+ { ngx_string("scgi_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.buffer_size),
+ NULL },
+
+ { ngx_string("scgi_pass_request_headers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_request_headers),
+ NULL },
+
+ { ngx_string("scgi_pass_request_body"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_request_body),
+ NULL },
+
+ { ngx_string("scgi_intercept_errors"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.intercept_errors),
+ NULL },
+
+ { ngx_string("scgi_read_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.read_timeout),
+ NULL },
+
+ { ngx_string("scgi_buffers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_conf_set_bufs_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.bufs),
+ NULL },
+
+ { ngx_string("scgi_busy_buffers_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.busy_buffers_size_conf),
+ NULL },
+
+#if (NGX_HTTP_CACHE)
+
+ { ngx_string("scgi_cache"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_scgi_cache,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("scgi_cache_key"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_scgi_cache_key,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("scgi_cache_path"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
+ ngx_http_file_cache_set_slot,
+ 0,
+ 0,
+ &ngx_http_scgi_module },
+
+ { ngx_string("scgi_cache_bypass"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_set_predicate_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_bypass),
+ NULL },
+
+ { ngx_string("scgi_no_cache"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_set_predicate_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.no_cache),
+ NULL },
+
+ { ngx_string("scgi_cache_valid"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_file_cache_valid_set_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_valid),
+ NULL },
+
+ { ngx_string("scgi_cache_min_uses"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_min_uses),
+ NULL },
+
+ { ngx_string("scgi_cache_use_stale"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_use_stale),
+ &ngx_http_scgi_next_upstream_masks },
+
+ { ngx_string("scgi_cache_methods"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_methods),
+ &ngx_http_upstream_cache_method_mask },
+
+ { ngx_string("scgi_cache_lock"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock),
+ NULL },
+
+ { ngx_string("scgi_cache_lock_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock_timeout),
+ NULL },
+
+ { ngx_string("scgi_cache_revalidate"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_revalidate),
+ NULL },
+
+#endif
+
+ { ngx_string("scgi_temp_path"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
+ ngx_conf_set_path_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.temp_path),
+ NULL },
+
+ { ngx_string("scgi_max_temp_file_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.max_temp_file_size_conf),
+ NULL },
+
+ { ngx_string("scgi_temp_file_write_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.temp_file_write_size_conf),
+ NULL },
+
+ { ngx_string("scgi_next_upstream"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.next_upstream),
+ &ngx_http_scgi_next_upstream_masks },
+
+ { ngx_string("scgi_param"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,
+ ngx_http_upstream_param_set_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, params_source),
+ NULL },
+
+ { ngx_string("scgi_pass_header"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_headers),
+ NULL },
+
+ { ngx_string("scgi_hide_header"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.hide_headers),
+ NULL },
+
+ { ngx_string("scgi_ignore_headers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_scgi_loc_conf_t, upstream.ignore_headers),
+ &ngx_http_upstream_ignore_headers_masks },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_scgi_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_scgi_create_loc_conf, /* create location configuration */
+ ngx_http_scgi_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_scgi_module = {
+ NGX_MODULE_V1,
+ &ngx_http_scgi_module_ctx, /* module context */
+ ngx_http_scgi_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t ngx_http_scgi_hide_headers[] = {
+ ngx_string("Status"),
+ ngx_string("X-Accel-Expires"),
+ ngx_string("X-Accel-Redirect"),
+ ngx_string("X-Accel-Limit-Rate"),
+ ngx_string("X-Accel-Buffering"),
+ ngx_string("X-Accel-Charset"),
+ ngx_null_string
+};
+
+
+#if (NGX_HTTP_CACHE)
+
+static ngx_keyval_t ngx_http_scgi_cache_headers[] = {
+ { ngx_string("HTTP_IF_MODIFIED_SINCE"),
+ ngx_string("$upstream_cache_last_modified") },
+ { ngx_string("HTTP_IF_UNMODIFIED_SINCE"), ngx_string("") },
+ { ngx_string("HTTP_IF_NONE_MATCH"), ngx_string("$upstream_cache_etag") },
+ { ngx_string("HTTP_IF_MATCH"), ngx_string("") },
+ { ngx_string("HTTP_RANGE"), ngx_string("") },
+ { ngx_string("HTTP_IF_RANGE"), ngx_string("") },
+ { ngx_null_string, ngx_null_string }
+};
+
+#endif
+
+
+static ngx_path_init_t ngx_http_scgi_temp_path = {
+ ngx_string(NGX_HTTP_SCGI_TEMP_PATH), { 1, 2, 0 }
+};
+
+
+static ngx_int_t
+ngx_http_scgi_handler(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_http_status_t *status;
+ ngx_http_upstream_t *u;
+ ngx_http_scgi_loc_conf_t *scf;
+
+ if (ngx_http_upstream_create(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ status = ngx_pcalloc(r->pool, sizeof(ngx_http_status_t));
+ if (status == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_http_set_ctx(r, status, ngx_http_scgi_module);
+
+ scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);
+
+ if (scf->scgi_lengths) {
+ if (ngx_http_scgi_eval(r, scf) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ u = r->upstream;
+
+ ngx_str_set(&u->schema, "scgi://");
+ u->output.tag = (ngx_buf_tag_t) &ngx_http_scgi_module;
+
+ u->conf = &scf->upstream;
+
+#if (NGX_HTTP_CACHE)
+ u->create_key = ngx_http_scgi_create_key;
+#endif
+ u->create_request = ngx_http_scgi_create_request;
+ u->reinit_request = ngx_http_scgi_reinit_request;
+ u->process_header = ngx_http_scgi_process_status_line;
+ u->abort_request = ngx_http_scgi_abort_request;
+ u->finalize_request = ngx_http_scgi_finalize_request;
+ r->state = 0;
+
+ u->buffering = scf->upstream.buffering;
+
+ u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
+ if (u->pipe == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ u->pipe->input_filter = ngx_event_pipe_copy_input_filter;
+ u->pipe->input_ctx = r;
+
+ rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ return rc;
+ }
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_http_scgi_eval(ngx_http_request_t *r, ngx_http_scgi_loc_conf_t * scf)
+{
+ ngx_url_t url;
+ ngx_http_upstream_t *u;
+
+ ngx_memzero(&url, sizeof(ngx_url_t));
+
+ if (ngx_http_script_run(r, &url.url, scf->scgi_lengths->elts, 0,
+ scf->scgi_values->elts)
+ == NULL)
+ {
+ return NGX_ERROR;
+ }
+
+ url.no_resolve = 1;
+
+ if (ngx_parse_url(r->pool, &url) != NGX_OK) {
+ if (url.err) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "%s in upstream \"%V\"", url.err, &url.url);
+ }
+
+ return NGX_ERROR;
+ }
+
+ u = r->upstream;
+
+ u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
+ if (u->resolved == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (url.addrs && url.addrs[0].sockaddr) {
+ u->resolved->sockaddr = url.addrs[0].sockaddr;
+ u->resolved->socklen = url.addrs[0].socklen;
+ u->resolved->naddrs = 1;
+ u->resolved->host = url.addrs[0].name;
+
+ } else {
+ u->resolved->host = url.host;
+ u->resolved->port = url.port;
+ u->resolved->no_port = url.no_port;
+ }
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HTTP_CACHE)
+
+static ngx_int_t
+ngx_http_scgi_create_key(ngx_http_request_t *r)
+{
+ ngx_str_t *key;
+ ngx_http_scgi_loc_conf_t *scf;
+
+ key = ngx_array_push(&r->cache->keys);
+ if (key == NULL) {
+ return NGX_ERROR;
+ }
+
+ scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);
+
+ if (ngx_http_complex_value(r, &scf->cache_key, key) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_scgi_create_request(ngx_http_request_t *r)
+{
+ off_t content_length_n;
+ u_char ch, *key, *val, *lowcase_key;
+ size_t len, key_len, val_len, allocated;
+ ngx_buf_t *b;
+ ngx_str_t content_length;
+ ngx_uint_t i, n, hash, skip_empty, header_params;
+ ngx_chain_t *cl, *body;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *header, **ignored;
+ ngx_http_script_code_pt code;
+ ngx_http_script_engine_t e, le;
+ ngx_http_scgi_loc_conf_t *scf;
+ ngx_http_script_len_code_pt lcode;
+ u_char buffer[NGX_OFF_T_LEN];
+
+ content_length_n = 0;
+ body = r->upstream->request_bufs;
+
+ while (body) {
+ content_length_n += ngx_buf_size(body->buf);
+ body = body->next;
+ }
+
+ content_length.data = buffer;
+ content_length.len = ngx_sprintf(buffer, "%O", content_length_n) - buffer;
+
+ len = sizeof("CONTENT_LENGTH") + content_length.len + 1;
+
+ header_params = 0;
+ ignored = NULL;
+
+ scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);
+
+ if (scf->params_len) {
+ ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
+
+ ngx_http_script_flush_no_cacheable_variables(r, scf->flushes);
+ le.flushed = 1;
+
+ le.ip = scf->params_len->elts;
+ le.request = r;
+
+ while (*(uintptr_t *) le.ip) {
+
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ key_len = lcode(&le);
+
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ skip_empty = lcode(&le);
+
+ for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ }
+ le.ip += sizeof(uintptr_t);
+
+ if (skip_empty && val_len == 0) {
+ continue;
+ }
+
+ len += key_len + val_len + 1;
+ }
+ }
+
+ if (scf->upstream.pass_request_headers) {
+
+ allocated = 0;
+ lowcase_key = NULL;
+
+ if (scf->header_params) {
+ n = 0;
+ part = &r->headers_in.headers.part;
+
+ while (part) {
+ n += part->nelts;
+ part = part->next;
+ }
+
+ ignored = ngx_palloc(r->pool, n * sizeof(void *));
+ if (ignored == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ part = &r->headers_in.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (scf->header_params) {
+ if (allocated < header[i].key.len) {
+ allocated = header[i].key.len + 16;
+ lowcase_key = ngx_pnalloc(r->pool, allocated);
+ if (lowcase_key == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ hash = 0;
+
+ for (n = 0; n < header[i].key.len; n++) {
+ ch = header[i].key.data[n];
+
+ if (ch >= 'A' && ch <= 'Z') {
+ ch |= 0x20;
+
+ } else if (ch == '-') {
+ ch = '_';
+ }
+
+ hash = ngx_hash(hash, ch);
+ lowcase_key[n] = ch;
+ }
+
+ if (ngx_hash_find(&scf->headers_hash, hash, lowcase_key, n)) {
+ ignored[header_params++] = &header[i];
+ continue;
+ }
+ }
+
+ len += sizeof("HTTP_") - 1 + header[i].key.len + 1
+ + header[i].value.len + 1;
+ }
+ }
+
+ /* netstring: "length:" + packet + "," */
+
+ b = ngx_create_temp_buf(r->pool, NGX_SIZE_T_LEN + 1 + len + 1);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+
+ b->last = ngx_sprintf(b->last, "%ui:CONTENT_LENGTH%Z%V%Z",
+ len, &content_length);
+
+ if (scf->params_len) {
+ ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
+
+ e.ip = scf->params->elts;
+ e.pos = b->last;
+ e.request = r;
+ e.flushed = 1;
+
+ le.ip = scf->params_len->elts;
+
+ while (*(uintptr_t *) le.ip) {
+
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ lcode(&le); /* key length */
+
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ skip_empty = lcode(&le);
+
+ for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ }
+ le.ip += sizeof(uintptr_t);
+
+ if (skip_empty && val_len == 0) {
+ e.skip = 1;
+
+ while (*(uintptr_t *) e.ip) {
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) &e);
+ }
+ e.ip += sizeof(uintptr_t);
+
+ e.skip = 0;
+
+ continue;
+ }
+
+#if (NGX_DEBUG)
+ key = e.pos;
+#endif
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) & e);
+
+#if (NGX_DEBUG)
+ val = e.pos;
+#endif
+ while (*(uintptr_t *) e.ip) {
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) &e);
+ }
+ *e.pos++ = '\0';
+ e.ip += sizeof(uintptr_t);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "scgi param: \"%s: %s\"", key, val);
+ }
+
+ b->last = e.pos;
+ }
+
+ if (scf->upstream.pass_request_headers) {
+
+ part = &r->headers_in.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ for (n = 0; n < header_params; n++) {
+ if (&header[i] == ignored[n]) {
+ goto next;
+ }
+ }
+
+ key = b->last;
+ b->last = ngx_cpymem(key, "HTTP_", sizeof("HTTP_") - 1);
+
+ for (n = 0; n < header[i].key.len; n++) {
+ ch = header[i].key.data[n];
+
+ if (ch >= 'a' && ch <= 'z') {
+ ch &= ~0x20;
+
+ } else if (ch == '-') {
+ ch = '_';
+ }
+
+ *b->last++ = ch;
+ }
+
+ *b->last++ = (u_char) 0;
+
+ val = b->last;
+ b->last = ngx_copy(val, header[i].value.data, header[i].value.len);
+ *b->last++ = (u_char) 0;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "scgi param: \"%s: %s\"", key, val);
+
+ next:
+
+ continue;
+ }
+ }
+
+ *b->last++ = (u_char) ',';
+
+ if (scf->upstream.pass_request_body) {
+ body = r->upstream->request_bufs;
+ r->upstream->request_bufs = cl;
+
+ while (body) {
+ b = ngx_alloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
+
+ cl->next = ngx_alloc_chain_link(r->pool);
+ if (cl->next == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = cl->next;
+ cl->buf = b;
+
+ body = body->next;
+ }
+
+ } else {
+ r->upstream->request_bufs = cl;
+ }
+
+ cl->next = NULL;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_scgi_reinit_request(ngx_http_request_t *r)
+{
+ ngx_http_status_t *status;
+
+ status = ngx_http_get_module_ctx(r, ngx_http_scgi_module);
+
+ if (status == NULL) {
+ return NGX_OK;
+ }
+
+ status->code = 0;
+ status->count = 0;
+ status->start = NULL;
+ status->end = NULL;
+
+ r->upstream->process_header = ngx_http_scgi_process_status_line;
+ r->state = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_scgi_process_status_line(ngx_http_request_t *r)
+{
+ size_t len;
+ ngx_int_t rc;
+ ngx_http_status_t *status;
+ ngx_http_upstream_t *u;
+
+ status = ngx_http_get_module_ctx(r, ngx_http_scgi_module);
+
+ if (status == NULL) {
+ return NGX_ERROR;
+ }
+
+ u = r->upstream;
+
+ rc = ngx_http_parse_status_line(r, &u->buffer, status);
+
+ if (rc == NGX_AGAIN) {
+ return rc;
+ }
+
+ if (rc == NGX_ERROR) {
+ u->process_header = ngx_http_scgi_process_header;
+ return ngx_http_scgi_process_header(r);
+ }
+
+ if (u->state && u->state->status == 0) {
+ u->state->status = status->code;
+ }
+
+ u->headers_in.status_n = status->code;
+
+ len = status->end - status->start;
+ u->headers_in.status_line.len = len;
+
+ u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);
+ if (u->headers_in.status_line.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(u->headers_in.status_line.data, status->start, len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http scgi status %ui \"%V\"",
+ u->headers_in.status_n, &u->headers_in.status_line);
+
+ u->process_header = ngx_http_scgi_process_header;
+
+ return ngx_http_scgi_process_header(r);
+}
+
+
+static ngx_int_t
+ngx_http_scgi_process_header(ngx_http_request_t *r)
+{
+ ngx_str_t *status_line;
+ ngx_int_t rc, status;
+ ngx_table_elt_t *h;
+ ngx_http_upstream_t *u;
+ ngx_http_upstream_header_t *hh;
+ ngx_http_upstream_main_conf_t *umcf;
+
+ umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
+
+ for ( ;; ) {
+
+ rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);
+
+ if (rc == NGX_OK) {
+
+ /* a header line has been parsed successfully */
+
+ h = ngx_list_push(&r->upstream->headers_in.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = r->header_hash;
+
+ h->key.len = r->header_name_end - r->header_name_start;
+ h->value.len = r->header_end - r->header_start;
+
+ h->key.data = ngx_pnalloc(r->pool,
+ h->key.len + 1 + h->value.len + 1
+ + h->key.len);
+ if (h->key.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->value.data = h->key.data + h->key.len + 1;
+ h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;
+
+ ngx_memcpy(h->key.data, r->header_name_start, h->key.len);
+ h->key.data[h->key.len] = '\0';
+ ngx_memcpy(h->value.data, r->header_start, h->value.len);
+ h->value.data[h->value.len] = '\0';
+
+ if (h->key.len == r->lowcase_index) {
+ ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
+
+ } else {
+ ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
+ }
+
+ hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
+ h->lowcase_key, h->key.len);
+
+ if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http scgi header: \"%V: %V\"", &h->key, &h->value);
+
+ continue;
+ }
+
+ if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
+
+ /* a whole header has been parsed successfully */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http scgi header done");
+
+ u = r->upstream;
+
+ if (u->headers_in.status_n) {
+ goto done;
+ }
+
+ if (u->headers_in.status) {
+ status_line = &u->headers_in.status->value;
+
+ status = ngx_atoi(status_line->data, 3);
+ if (status == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent invalid status \"%V\"",
+ status_line);
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+
+ u->headers_in.status_n = status;
+ u->headers_in.status_line = *status_line;
+
+ } else if (u->headers_in.location) {
+ u->headers_in.status_n = 302;
+ ngx_str_set(&u->headers_in.status_line,
+ "302 Moved Temporarily");
+
+ } else {
+ u->headers_in.status_n = 200;
+ ngx_str_set(&u->headers_in.status_line, "200 OK");
+ }
+
+ if (u->state && u->state->status == 0) {
+ u->state->status = u->headers_in.status_n;
+ }
+
+ done:
+
+ if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS
+ && r->headers_in.upgrade)
+ {
+ u->upgrade = 1;
+ }
+
+ return NGX_OK;
+ }
+
+ if (rc == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ /* there was error while a header line parsing */
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent invalid header");
+
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+}
+
+
+static void
+ngx_http_scgi_abort_request(ngx_http_request_t *r)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "abort http scgi request");
+
+ return;
+}
+
+
+static void
+ngx_http_scgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "finalize http scgi request");
+
+ return;
+}
+
+
+static void *
+ngx_http_scgi_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_scgi_loc_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_scgi_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->upstream.store = NGX_CONF_UNSET;
+ conf->upstream.store_access = NGX_CONF_UNSET_UINT;
+ conf->upstream.buffering = NGX_CONF_UNSET;
+ conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
+
+ conf->upstream.local = NGX_CONF_UNSET_PTR;
+
+ conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
+
+ conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;
+ conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
+
+ conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;
+ conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
+ conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;
+
+ conf->upstream.pass_request_headers = NGX_CONF_UNSET;
+ conf->upstream.pass_request_body = NGX_CONF_UNSET;
+
+#if (NGX_HTTP_CACHE)
+ conf->upstream.cache = NGX_CONF_UNSET_PTR;
+ conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;
+ conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;
+ conf->upstream.no_cache = NGX_CONF_UNSET_PTR;
+ conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;
+ conf->upstream.cache_lock = NGX_CONF_UNSET;
+ conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.cache_revalidate = NGX_CONF_UNSET;
+#endif
+
+ conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
+ conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
+
+ conf->upstream.intercept_errors = NGX_CONF_UNSET;
+
+ /* "scgi_cyclic_temp_file" is disabled */
+ conf->upstream.cyclic_temp_file = 0;
+
+ conf->upstream.change_buffering = 1;
+
+ ngx_str_set(&conf->upstream.module, "scgi");
+
+ return conf;
+}
+
+
+static char *
+ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_scgi_loc_conf_t *prev = parent;
+ ngx_http_scgi_loc_conf_t *conf = child;
+
+ size_t size;
+ ngx_hash_init_t hash;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (conf->upstream.store != 0) {
+ ngx_conf_merge_value(conf->upstream.store, prev->upstream.store, 0);
+
+ if (conf->upstream.store_lengths == NULL) {
+ conf->upstream.store_lengths = prev->upstream.store_lengths;
+ conf->upstream.store_values = prev->upstream.store_values;
+ }
+ }
+
+ ngx_conf_merge_uint_value(conf->upstream.store_access,
+ prev->upstream.store_access, 0600);
+
+ ngx_conf_merge_value(conf->upstream.buffering,
+ prev->upstream.buffering, 1);
+
+ ngx_conf_merge_value(conf->upstream.ignore_client_abort,
+ prev->upstream.ignore_client_abort, 0);
+
+ ngx_conf_merge_ptr_value(conf->upstream.local,
+ prev->upstream.local, NULL);
+
+ ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
+ prev->upstream.connect_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->upstream.send_timeout,
+ prev->upstream.send_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->upstream.read_timeout,
+ prev->upstream.read_timeout, 60000);
+
+ ngx_conf_merge_size_value(conf->upstream.send_lowat,
+ prev->upstream.send_lowat, 0);
+
+ ngx_conf_merge_size_value(conf->upstream.buffer_size,
+ prev->upstream.buffer_size,
+ (size_t) ngx_pagesize);
+
+
+ ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,
+ 8, ngx_pagesize);
+
+ if (conf->upstream.bufs.num < 2) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "there must be at least 2 \"scgi_buffers\"");
+ return NGX_CONF_ERROR;
+ }
+
+
+ size = conf->upstream.buffer_size;
+ if (size < conf->upstream.bufs.size) {
+ size = conf->upstream.bufs.size;
+ }
+
+
+ ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,
+ prev->upstream.busy_buffers_size_conf,
+ NGX_CONF_UNSET_SIZE);
+
+ if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {
+ conf->upstream.busy_buffers_size = 2 * size;
+ } else {
+ conf->upstream.busy_buffers_size =
+ conf->upstream.busy_buffers_size_conf;
+ }
+
+ if (conf->upstream.busy_buffers_size < size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"scgi_busy_buffers_size\" must be equal to or greater "
+ "than the maximum of the value of \"scgi_buffer_size\" and "
+ "one of the \"scgi_buffers\"");
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->upstream.busy_buffers_size
+ > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"scgi_busy_buffers_size\" must be less than "
+ "the size of all \"scgi_buffers\" minus one buffer");
+
+ return NGX_CONF_ERROR;
+ }
+
+
+ ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,
+ prev->upstream.temp_file_write_size_conf,
+ NGX_CONF_UNSET_SIZE);
+
+ if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {
+ conf->upstream.temp_file_write_size = 2 * size;
+ } else {
+ conf->upstream.temp_file_write_size =
+ conf->upstream.temp_file_write_size_conf;
+ }
+
+ if (conf->upstream.temp_file_write_size < size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"scgi_temp_file_write_size\" must be equal to or greater than "
+ "the maximum of the value of \"scgi_buffer_size\" and "
+ "one of the \"scgi_buffers\"");
+
+ return NGX_CONF_ERROR;
+ }
+
+
+ ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,
+ prev->upstream.max_temp_file_size_conf,
+ NGX_CONF_UNSET_SIZE);
+
+ if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {
+ conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;
+ } else {
+ conf->upstream.max_temp_file_size =
+ conf->upstream.max_temp_file_size_conf;
+ }
+
+ if (conf->upstream.max_temp_file_size != 0
+ && conf->upstream.max_temp_file_size < size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"scgi_max_temp_file_size\" must be equal to zero to disable "
+ "temporary files usage or must be equal to or greater than "
+ "the maximum of the value of \"scgi_buffer_size\" and "
+ "one of the \"scgi_buffers\"");
+
+ return NGX_CONF_ERROR;
+ }
+
+
+ ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
+ prev->upstream.ignore_headers,
+ NGX_CONF_BITMASK_SET);
+
+
+ ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
+ prev->upstream.next_upstream,
+ (NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_ERROR
+ |NGX_HTTP_UPSTREAM_FT_TIMEOUT));
+
+ if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
+ conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_OFF;
+ }
+
+ if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
+ prev->upstream.temp_path,
+ &ngx_http_scgi_temp_path)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ ngx_conf_merge_ptr_value(conf->upstream.cache,
+ prev->upstream.cache, NULL);
+
+ if (conf->upstream.cache && conf->upstream.cache->data == NULL) {
+ ngx_shm_zone_t *shm_zone;
+
+ shm_zone = conf->upstream.cache;
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"scgi_cache\" zone \"%V\" is unknown",
+ &shm_zone->shm.name);
+
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,
+ prev->upstream.cache_min_uses, 1);
+
+ ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,
+ prev->upstream.cache_use_stale,
+ (NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_OFF));
+
+ if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {
+ conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_OFF;
+ }
+
+ if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {
+ conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;
+ }
+
+ if (conf->upstream.cache_methods == 0) {
+ conf->upstream.cache_methods = prev->upstream.cache_methods;
+ }
+
+ conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;
+
+ ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,
+ prev->upstream.cache_bypass, NULL);
+
+ ngx_conf_merge_ptr_value(conf->upstream.no_cache,
+ prev->upstream.no_cache, NULL);
+
+ ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
+ prev->upstream.cache_valid, NULL);
+
+ if (conf->cache_key.value.data == NULL) {
+ conf->cache_key = prev->cache_key;
+ }
+
+ ngx_conf_merge_value(conf->upstream.cache_lock,
+ prev->upstream.cache_lock, 0);
+
+ ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,
+ prev->upstream.cache_lock_timeout, 5000);
+
+ ngx_conf_merge_value(conf->upstream.cache_revalidate,
+ prev->upstream.cache_revalidate, 0);
+
+#endif
+
+ ngx_conf_merge_value(conf->upstream.pass_request_headers,
+ prev->upstream.pass_request_headers, 1);
+ ngx_conf_merge_value(conf->upstream.pass_request_body,
+ prev->upstream.pass_request_body, 1);
+
+ ngx_conf_merge_value(conf->upstream.intercept_errors,
+ prev->upstream.intercept_errors, 0);
+
+ hash.max_size = 512;
+ hash.bucket_size = ngx_align(64, ngx_cacheline_size);
+ hash.name = "scgi_hide_headers_hash";
+
+ if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
+ &prev->upstream, ngx_http_scgi_hide_headers, &hash)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->upstream.upstream == NULL) {
+ conf->upstream.upstream = prev->upstream.upstream;
+ }
+
+ if (conf->scgi_lengths == NULL) {
+ conf->scgi_lengths = prev->scgi_lengths;
+ conf->scgi_values = prev->scgi_values;
+ }
+
+ if (conf->upstream.upstream || conf->scgi_lengths) {
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+ if (clcf->handler == NULL && clcf->lmt_excpt) {
+ clcf->handler = ngx_http_scgi_handler;
+ }
+ }
+
+ if (ngx_http_scgi_merge_params(cf, conf, prev) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_scgi_merge_params(ngx_conf_t *cf, ngx_http_scgi_loc_conf_t *conf,
+ ngx_http_scgi_loc_conf_t *prev)
+{
+ u_char *p;
+ size_t size;
+ uintptr_t *code;
+ ngx_uint_t i, nsrc;
+ ngx_array_t headers_names;
+#if (NGX_HTTP_CACHE)
+ ngx_array_t params_merged;
+#endif
+ ngx_hash_key_t *hk;
+ ngx_hash_init_t hash;
+ ngx_http_upstream_param_t *src;
+ ngx_http_script_compile_t sc;
+ ngx_http_script_copy_code_t *copy;
+
+ if (conf->params_source == NULL) {
+ conf->params_source = prev->params_source;
+
+ if (prev->headers_hash.buckets
+#if (NGX_HTTP_CACHE)
+ && ((conf->upstream.cache == NULL)
+ == (prev->upstream.cache == NULL))
+#endif
+ )
+ {
+ conf->flushes = prev->flushes;
+ conf->params_len = prev->params_len;
+ conf->params = prev->params;
+ conf->headers_hash = prev->headers_hash;
+ conf->header_params = prev->header_params;
+
+ return NGX_OK;
+ }
+ }
+
+ if (conf->params_source == NULL
+#if (NGX_HTTP_CACHE)
+ && (conf->upstream.cache == NULL)
+#endif
+ )
+ {
+ conf->headers_hash.buckets = (void *) 1;
+ return NGX_OK;
+ }
+
+ conf->params_len = ngx_array_create(cf->pool, 64, 1);
+ if (conf->params_len == NULL) {
+ return NGX_ERROR;
+ }
+
+ conf->params = ngx_array_create(cf->pool, 512, 1);
+ if (conf->params == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (conf->params_source) {
+ src = conf->params_source->elts;
+ nsrc = conf->params_source->nelts;
+
+ } else {
+ src = NULL;
+ nsrc = 0;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (conf->upstream.cache) {
+ ngx_keyval_t *h;
+ ngx_http_upstream_param_t *s;
+
+ if (ngx_array_init(&params_merged, cf->temp_pool, 4,
+ sizeof(ngx_http_upstream_param_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ for (i = 0; i < nsrc; i++) {
+
+ s = ngx_array_push(&params_merged);
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ *s = src[i];
+ }
+
+ h = ngx_http_scgi_cache_headers;
+
+ while (h->key.len) {
+
+ src = params_merged.elts;
+ nsrc = params_merged.nelts;
+
+ for (i = 0; i < nsrc; i++) {
+ if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {
+ goto next;
+ }
+ }
+
+ s = ngx_array_push(&params_merged);
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ s->key = h->key;
+ s->value = h->value;
+ s->skip_empty = 1;
+
+ next:
+
+ h++;
+ }
+
+ src = params_merged.elts;
+ nsrc = params_merged.nelts;
+ }
+
+#endif
+
+ for (i = 0; i < nsrc; i++) {
+
+ if (src[i].key.len > sizeof("HTTP_") - 1
+ && ngx_strncmp(src[i].key.data, "HTTP_", sizeof("HTTP_") - 1) == 0)
+ {
+ hk = ngx_array_push(&headers_names);
+ if (hk == NULL) {
+ return NGX_ERROR;
+ }
+
+ hk->key.len = src[i].key.len - 5;
+ hk->key.data = src[i].key.data + 5;
+ hk->key_hash = ngx_hash_key_lc(hk->key.data, hk->key.len);
+ hk->value = (void *) 1;
+
+ if (src[i].value.len == 0) {
+ continue;
+ }
+ }
+
+ copy = ngx_array_push_n(conf->params_len,
+ sizeof(ngx_http_script_copy_code_t));
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+ copy->len = src[i].key.len + 1;
+
+ copy = ngx_array_push_n(conf->params_len,
+ sizeof(ngx_http_script_copy_code_t));
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+ copy->len = src[i].skip_empty;
+
+
+ size = (sizeof(ngx_http_script_copy_code_t)
+ + src[i].key.len + 1 + sizeof(uintptr_t) - 1)
+ & ~(sizeof(uintptr_t) - 1);
+
+ copy = ngx_array_push_n(conf->params, size);
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = ngx_http_script_copy_code;
+ copy->len = src[i].key.len + 1;
+
+ p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
+ (void) ngx_cpystrn(p, src[i].key.data, src[i].key.len + 1);
+
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &src[i].value;
+ sc.flushes = &conf->flushes;
+ sc.lengths = &conf->params_len;
+ sc.values = &conf->params;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+
+
+ code = ngx_array_push_n(conf->params, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+ }
+
+ code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+
+ code = ngx_array_push_n(conf->params, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+
+ conf->header_params = headers_names.nelts;
+
+ hash.hash = &conf->headers_hash;
+ hash.key = ngx_hash_key_lc;
+ hash.max_size = 512;
+ hash.bucket_size = 64;
+ hash.name = "scgi_params_hash";
+ hash.pool = cf->pool;
+ hash.temp_pool = NULL;
+
+ return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);
+}
+
+
+static char *
+ngx_http_scgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_scgi_loc_conf_t *scf = conf;
+
+ ngx_url_t u;
+ ngx_str_t *value, *url;
+ ngx_uint_t n;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_script_compile_t sc;
+
+ if (scf->upstream.upstream || scf->scgi_lengths) {
+ return "is duplicate";
+ }
+
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+ clcf->handler = ngx_http_scgi_handler;
+
+ value = cf->args->elts;
+
+ url = &value[1];
+
+ n = ngx_http_script_variables_count(url);
+
+ if (n) {
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = url;
+ sc.lengths = &scf->scgi_lengths;
+ sc.values = &scf->scgi_values;
+ sc.variables = n;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = value[1];
+ u.no_resolve = 1;
+
+ scf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
+ if (scf->upstream.upstream == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (clcf->name.data[clcf->name.len - 1] == '/') {
+ clcf->auto_redirect = 1;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_scgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_scgi_loc_conf_t *scf = conf;
+
+ ngx_str_t *value;
+ ngx_http_script_compile_t sc;
+
+ if (scf->upstream.store != NGX_CONF_UNSET || scf->upstream.store_lengths) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ scf->upstream.store = 0;
+ return NGX_CONF_OK;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (scf->upstream.cache != NGX_CONF_UNSET_PTR
+ && scf->upstream.cache != NULL)
+ {
+ return "is incompatible with \"scgi_cache\"";
+ }
+
+#endif
+
+ if (ngx_strcmp(value[1].data, "on") == 0) {
+ scf->upstream.store = 1;
+ return NGX_CONF_OK;
+ }
+
+ /* include the terminating '\0' into script */
+ value[1].len++;
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &value[1];
+ sc.lengths = &scf->upstream.store_lengths;
+ sc.values = &scf->upstream.store_values;
+ sc.variables = ngx_http_script_variables_count(&value[1]);
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+#if (NGX_HTTP_CACHE)
+
+static char *
+ngx_http_scgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_scgi_loc_conf_t *scf = conf;
+
+ ngx_str_t *value;
+
+ value = cf->args->elts;
+
+ if (scf->upstream.cache != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ scf->upstream.cache = NULL;
+ return NGX_CONF_OK;
+ }
+
+ if (scf->upstream.store > 0 || scf->upstream.store_lengths) {
+ return "is incompatible with \"scgi_store\"";
+ }
+
+ scf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0,
+ &ngx_http_scgi_module);
+ if (scf->upstream.cache == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_scgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_scgi_loc_conf_t *scf = conf;
+
+ ngx_str_t *value;
+ ngx_http_compile_complex_value_t ccv;
+
+ value = cf->args->elts;
+
+ if (scf->cache_key.value.data) {
+ return "is duplicate";
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &scf->cache_key;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_secure_link_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_secure_link_module.c
new file mode 100644
index 00000000000..907ba6ef50c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_secure_link_module.c
@@ -0,0 +1,368 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_md5.h>
+
+
+typedef struct {
+ ngx_http_complex_value_t *variable;
+ ngx_http_complex_value_t *md5;
+ ngx_str_t secret;
+} ngx_http_secure_link_conf_t;
+
+
+typedef struct {
+ ngx_str_t expires;
+} ngx_http_secure_link_ctx_t;
+
+
+static ngx_int_t ngx_http_secure_link_old_variable(ngx_http_request_t *r,
+ ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,
+ uintptr_t data);
+static ngx_int_t ngx_http_secure_link_expires_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static void *ngx_http_secure_link_create_conf(ngx_conf_t *cf);
+static char *ngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static ngx_int_t ngx_http_secure_link_add_variables(ngx_conf_t *cf);
+
+
+static ngx_command_t ngx_http_secure_link_commands[] = {
+
+ { ngx_string("secure_link"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_set_complex_value_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_secure_link_conf_t, variable),
+ NULL },
+
+ { ngx_string("secure_link_md5"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_set_complex_value_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_secure_link_conf_t, md5),
+ NULL },
+
+ { ngx_string("secure_link_secret"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_secure_link_conf_t, secret),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_secure_link_module_ctx = {
+ ngx_http_secure_link_add_variables, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_secure_link_create_conf, /* create location configuration */
+ ngx_http_secure_link_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_secure_link_module = {
+ NGX_MODULE_V1,
+ &ngx_http_secure_link_module_ctx, /* module context */
+ ngx_http_secure_link_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t ngx_http_secure_link_name = ngx_string("secure_link");
+static ngx_str_t ngx_http_secure_link_expires_name =
+ ngx_string("secure_link_expires");
+
+
+static ngx_int_t
+ngx_http_secure_link_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p, *last;
+ ngx_str_t val, hash;
+ time_t expires;
+ ngx_md5_t md5;
+ ngx_http_secure_link_ctx_t *ctx;
+ ngx_http_secure_link_conf_t *conf;
+ u_char hash_buf[16], md5_buf[16];
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_link_module);
+
+ if (conf->secret.data) {
+ return ngx_http_secure_link_old_variable(r, conf, v, data);
+ }
+
+ if (conf->variable == NULL || conf->md5 == NULL) {
+ goto not_found;
+ }
+
+ if (ngx_http_complex_value(r, conf->variable, &val) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "secure link: \"%V\"", &val);
+
+ last = val.data + val.len;
+
+ p = ngx_strlchr(val.data, last, ',');
+ expires = 0;
+
+ if (p) {
+ val.len = p++ - val.data;
+
+ expires = ngx_atotm(p, last - p);
+ if (expires <= 0) {
+ goto not_found;
+ }
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_secure_link_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_secure_link_module);
+
+ ctx->expires.len = last - p;
+ ctx->expires.data = p;
+ }
+
+ if (val.len > 24) {
+ goto not_found;
+ }
+
+ hash.len = 16;
+ hash.data = hash_buf;
+
+ if (ngx_decode_base64url(&hash, &val) != NGX_OK) {
+ goto not_found;
+ }
+
+ if (hash.len != 16) {
+ goto not_found;
+ }
+
+ if (ngx_http_complex_value(r, conf->md5, &val) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "secure link md5: \"%V\"", &val);
+
+ ngx_md5_init(&md5);
+ ngx_md5_update(&md5, val.data, val.len);
+ ngx_md5_final(md5_buf, &md5);
+
+ if (ngx_memcmp(hash_buf, md5_buf, 16) != 0) {
+ goto not_found;
+ }
+
+ v->data = (u_char *) ((expires && expires < ngx_time()) ? "0" : "1");
+ v->len = 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+
+not_found:
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_secure_link_old_variable(ngx_http_request_t *r,
+ ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ u_char *p, *start, *end, *last;
+ size_t len;
+ ngx_int_t n;
+ ngx_uint_t i;
+ ngx_md5_t md5;
+ u_char hash[16];
+
+ p = &r->unparsed_uri.data[1];
+ last = r->unparsed_uri.data + r->unparsed_uri.len;
+
+ while (p < last) {
+ if (*p++ == '/') {
+ start = p;
+ goto md5_start;
+ }
+ }
+
+ goto not_found;
+
+md5_start:
+
+ while (p < last) {
+ if (*p++ == '/') {
+ end = p - 1;
+ goto url_start;
+ }
+ }
+
+ goto not_found;
+
+url_start:
+
+ len = last - p;
+
+ if (end - start != 32 || len == 0) {
+ goto not_found;
+ }
+
+ ngx_md5_init(&md5);
+ ngx_md5_update(&md5, p, len);
+ ngx_md5_update(&md5, conf->secret.data, conf->secret.len);
+ ngx_md5_final(hash, &md5);
+
+ for (i = 0; i < 16; i++) {
+ n = ngx_hextoi(&start[2 * i], 2);
+ if (n == NGX_ERROR || n != hash[i]) {
+ goto not_found;
+ }
+ }
+
+ v->len = len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+
+not_found:
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_secure_link_expires_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_secure_link_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_secure_link_module);
+
+ if (ctx) {
+ v->len = ctx->expires.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = ctx->expires.data;
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_secure_link_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_secure_link_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_secure_link_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->variable = NULL;
+ * conf->md5 = NULL;
+ * conf->secret = { 0, NULL };
+ */
+
+ return conf;
+}
+
+
+static char *
+ngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_secure_link_conf_t *prev = parent;
+ ngx_http_secure_link_conf_t *conf = child;
+
+ if (conf->secret.data) {
+ if (conf->variable || conf->md5) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"secure_link_secret\" cannot be mixed with "
+ "\"secure_link\" and \"secure_link_md5\"");
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+ }
+
+ if (conf->variable == NULL) {
+ conf->variable = prev->variable;
+ }
+
+ if (conf->md5 == NULL) {
+ conf->md5 = prev->md5;
+ }
+
+ if (conf->variable == NULL && conf->md5 == NULL) {
+ conf->secret = prev->secret;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_secure_link_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var;
+
+ var = ngx_http_add_variable(cf, &ngx_http_secure_link_name, 0);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = ngx_http_secure_link_variable;
+
+ var = ngx_http_add_variable(cf, &ngx_http_secure_link_expires_name, 0);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = ngx_http_secure_link_expires_variable;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_split_clients_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_split_clients_module.c
new file mode 100644
index 00000000000..2f92c9e1a8f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_split_clients_module.c
@@ -0,0 +1,246 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ uint32_t percent;
+ ngx_http_variable_value_t value;
+} ngx_http_split_clients_part_t;
+
+
+typedef struct {
+ ngx_http_complex_value_t value;
+ ngx_array_t parts;
+} ngx_http_split_clients_ctx_t;
+
+
+static char *ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy,
+ void *conf);
+
+static ngx_command_t ngx_http_split_clients_commands[] = {
+
+ { ngx_string("split_clients"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
+ ngx_conf_split_clients_block,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_split_clients_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_split_clients_module = {
+ NGX_MODULE_V1,
+ &ngx_http_split_clients_module_ctx, /* module context */
+ ngx_http_split_clients_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_split_clients_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_split_clients_ctx_t *ctx = (ngx_http_split_clients_ctx_t *) data;
+
+ uint32_t hash;
+ ngx_str_t val;
+ ngx_uint_t i;
+ ngx_http_split_clients_part_t *part;
+
+ *v = ngx_http_variable_null_value;
+
+ if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) {
+ return NGX_OK;
+ }
+
+ hash = ngx_murmur_hash2(val.data, val.len);
+
+ part = ctx->parts.elts;
+
+ for (i = 0; i < ctx->parts.nelts; i++) {
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http split: %uD %uD", hash, part[i].percent);
+
+ if (hash < part[i].percent || part[i].percent == 0) {
+ *v = part[i].value;
+ return NGX_OK;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ uint32_t sum, last;
+ ngx_str_t *value, name;
+ ngx_uint_t i;
+ ngx_conf_t save;
+ ngx_http_variable_t *var;
+ ngx_http_split_clients_ctx_t *ctx;
+ ngx_http_split_clients_part_t *part;
+ ngx_http_compile_complex_value_t ccv;
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_split_clients_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &ctx->value;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ name = value[2];
+
+ if (name.data[0] != '$') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"%V\"", &name);
+ return NGX_CONF_ERROR;
+ }
+
+ name.len--;
+ name.data++;
+
+ var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
+ if (var == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ var->get_handler = ngx_http_split_clients_variable;
+ var->data = (uintptr_t) ctx;
+
+ if (ngx_array_init(&ctx->parts, cf->pool, 2,
+ sizeof(ngx_http_split_clients_part_t))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ save = *cf;
+ cf->ctx = ctx;
+ cf->handler = ngx_http_split_clients;
+ cf->handler_conf = conf;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = save;
+
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+
+ sum = 0;
+ last = 0;
+ part = ctx->parts.elts;
+
+ for (i = 0; i < ctx->parts.nelts; i++) {
+ sum = part[i].percent ? sum + part[i].percent : 10000;
+ if (sum > 10000) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "percent total is greater than 100%%");
+ return NGX_CONF_ERROR;
+ }
+
+ if (part[i].percent) {
+ last += part[i].percent * (uint64_t) 0xffffffff / 10000;
+ part[i].percent = last;
+ }
+ }
+
+ return rv;
+}
+
+
+static char *
+ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
+{
+ ngx_int_t n;
+ ngx_str_t *value;
+ ngx_http_split_clients_ctx_t *ctx;
+ ngx_http_split_clients_part_t *part;
+
+ ctx = cf->ctx;
+ value = cf->args->elts;
+
+ part = ngx_array_push(&ctx->parts);
+ if (part == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (value[0].len == 1 && value[0].data[0] == '*') {
+ part->percent = 0;
+
+ } else {
+ if (value[0].len == 0 || value[0].data[value[0].len - 1] != '%') {
+ goto invalid;
+ }
+
+ n = ngx_atofp(value[0].data, value[0].len - 1, 2);
+ if (n == NGX_ERROR || n == 0) {
+ goto invalid;
+ }
+
+ part->percent = (uint32_t) n;
+ }
+
+ part->value.len = value[1].len;
+ part->value.valid = 1;
+ part->value.no_cacheable = 0;
+ part->value.not_found = 0;
+ part->value.data = value[1].data;
+
+ return NGX_CONF_OK;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid percent value \"%V\"", &value[0]);
+ return NGX_CONF_ERROR;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssi_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssi_filter_module.c
new file mode 100644
index 00000000000..8236320c295
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssi_filter_module.c
@@ -0,0 +1,2930 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#define NGX_HTTP_SSI_ERROR 1
+
+#define NGX_HTTP_SSI_DATE_LEN 2048
+
+#define NGX_HTTP_SSI_ADD_PREFIX 1
+#define NGX_HTTP_SSI_ADD_ZERO 2
+
+
+typedef struct {
+ ngx_flag_t enable;
+ ngx_flag_t silent_errors;
+ ngx_flag_t ignore_recycled_buffers;
+ ngx_flag_t last_modified;
+
+ ngx_hash_t types;
+
+ size_t min_file_chunk;
+ size_t value_len;
+
+ ngx_array_t *types_keys;
+} ngx_http_ssi_loc_conf_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_uint_t key;
+ ngx_str_t value;
+} ngx_http_ssi_var_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_chain_t *bufs;
+ ngx_uint_t count;
+} ngx_http_ssi_block_t;
+
+
+typedef enum {
+ ssi_start_state = 0,
+ ssi_tag_state,
+ ssi_comment0_state,
+ ssi_comment1_state,
+ ssi_sharp_state,
+ ssi_precommand_state,
+ ssi_command_state,
+ ssi_preparam_state,
+ ssi_param_state,
+ ssi_preequal_state,
+ ssi_prevalue_state,
+ ssi_double_quoted_value_state,
+ ssi_quoted_value_state,
+ ssi_quoted_symbol_state,
+ ssi_postparam_state,
+ ssi_comment_end0_state,
+ ssi_comment_end1_state,
+ ssi_error_state,
+ ssi_error_end0_state,
+ ssi_error_end1_state
+} ngx_http_ssi_state_e;
+
+
+static ngx_int_t ngx_http_ssi_output(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx);
+static void ngx_http_ssi_buffered(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx);
+static ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx);
+static ngx_str_t *ngx_http_ssi_get_variable(ngx_http_request_t *r,
+ ngx_str_t *name, ngx_uint_t key);
+static ngx_int_t ngx_http_ssi_evaluate_string(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx, ngx_str_t *text, ngx_uint_t flags);
+static ngx_int_t ngx_http_ssi_regex_match(ngx_http_request_t *r,
+ ngx_str_t *pattern, ngx_str_t *str);
+
+static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
+static ngx_int_t ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data,
+ ngx_int_t rc);
+static ngx_int_t ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data,
+ ngx_int_t rc);
+static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
+static ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
+static ngx_int_t ngx_http_ssi_set(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
+static ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
+static ngx_int_t ngx_http_ssi_else(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
+static ngx_int_t ngx_http_ssi_endif(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
+static ngx_int_t ngx_http_ssi_block(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
+static ngx_int_t ngx_http_ssi_endblock(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
+
+static ngx_int_t ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t gmt);
+
+static ngx_int_t ngx_http_ssi_preconfiguration(ngx_conf_t *cf);
+static void *ngx_http_ssi_create_main_conf(ngx_conf_t *cf);
+static char *ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf);
+static void *ngx_http_ssi_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static ngx_int_t ngx_http_ssi_filter_init(ngx_conf_t *cf);
+
+
+static ngx_command_t ngx_http_ssi_filter_commands[] = {
+
+ { ngx_string("ssi"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_ssi_loc_conf_t, enable),
+ NULL },
+
+ { ngx_string("ssi_silent_errors"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_ssi_loc_conf_t, silent_errors),
+ NULL },
+
+ { ngx_string("ssi_ignore_recycled_buffers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_ssi_loc_conf_t, ignore_recycled_buffers),
+ NULL },
+
+ { ngx_string("ssi_min_file_chunk"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_ssi_loc_conf_t, min_file_chunk),
+ NULL },
+
+ { ngx_string("ssi_value_length"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_ssi_loc_conf_t, value_len),
+ NULL },
+
+ { ngx_string("ssi_types"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_types_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_ssi_loc_conf_t, types_keys),
+ &ngx_http_html_default_types[0] },
+
+ { ngx_string("ssi_last_modified"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_ssi_loc_conf_t, last_modified),
+ NULL },
+
+ ngx_null_command
+};
+
+
+
+static ngx_http_module_t ngx_http_ssi_filter_module_ctx = {
+ ngx_http_ssi_preconfiguration, /* preconfiguration */
+ ngx_http_ssi_filter_init, /* postconfiguration */
+
+ ngx_http_ssi_create_main_conf, /* create main configuration */
+ ngx_http_ssi_init_main_conf, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_ssi_create_loc_conf, /* create location configuration */
+ ngx_http_ssi_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_ssi_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_ssi_filter_module_ctx, /* module context */
+ ngx_http_ssi_filter_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static u_char ngx_http_ssi_string[] = "<!--";
+
+static ngx_str_t ngx_http_ssi_none = ngx_string("(none)");
+static ngx_str_t ngx_http_ssi_timefmt = ngx_string("%A, %d-%b-%Y %H:%M:%S %Z");
+static ngx_str_t ngx_http_ssi_null_string = ngx_null_string;
+
+
+#define NGX_HTTP_SSI_INCLUDE_VIRTUAL 0
+#define NGX_HTTP_SSI_INCLUDE_FILE 1
+#define NGX_HTTP_SSI_INCLUDE_WAIT 2
+#define NGX_HTTP_SSI_INCLUDE_SET 3
+#define NGX_HTTP_SSI_INCLUDE_STUB 4
+
+#define NGX_HTTP_SSI_ECHO_VAR 0
+#define NGX_HTTP_SSI_ECHO_DEFAULT 1
+#define NGX_HTTP_SSI_ECHO_ENCODING 2
+
+#define NGX_HTTP_SSI_CONFIG_ERRMSG 0
+#define NGX_HTTP_SSI_CONFIG_TIMEFMT 1
+
+#define NGX_HTTP_SSI_SET_VAR 0
+#define NGX_HTTP_SSI_SET_VALUE 1
+
+#define NGX_HTTP_SSI_IF_EXPR 0
+
+#define NGX_HTTP_SSI_BLOCK_NAME 0
+
+
+static ngx_http_ssi_param_t ngx_http_ssi_include_params[] = {
+ { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0, 0 },
+ { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE, 0, 0 },
+ { ngx_string("wait"), NGX_HTTP_SSI_INCLUDE_WAIT, 0, 0 },
+ { ngx_string("set"), NGX_HTTP_SSI_INCLUDE_SET, 0, 0 },
+ { ngx_string("stub"), NGX_HTTP_SSI_INCLUDE_STUB, 0, 0 },
+ { ngx_null_string, 0, 0, 0 }
+};
+
+
+static ngx_http_ssi_param_t ngx_http_ssi_echo_params[] = {
+ { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1, 0 },
+ { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0, 0 },
+ { ngx_string("encoding"), NGX_HTTP_SSI_ECHO_ENCODING, 0, 0 },
+ { ngx_null_string, 0, 0, 0 }
+};
+
+
+static ngx_http_ssi_param_t ngx_http_ssi_config_params[] = {
+ { ngx_string("errmsg"), NGX_HTTP_SSI_CONFIG_ERRMSG, 0, 0 },
+ { ngx_string("timefmt"), NGX_HTTP_SSI_CONFIG_TIMEFMT, 0, 0 },
+ { ngx_null_string, 0, 0, 0 }
+};
+
+
+static ngx_http_ssi_param_t ngx_http_ssi_set_params[] = {
+ { ngx_string("var"), NGX_HTTP_SSI_SET_VAR, 1, 0 },
+ { ngx_string("value"), NGX_HTTP_SSI_SET_VALUE, 1, 0 },
+ { ngx_null_string, 0, 0, 0 }
+};
+
+
+static ngx_http_ssi_param_t ngx_http_ssi_if_params[] = {
+ { ngx_string("expr"), NGX_HTTP_SSI_IF_EXPR, 1, 0 },
+ { ngx_null_string, 0, 0, 0 }
+};
+
+
+static ngx_http_ssi_param_t ngx_http_ssi_block_params[] = {
+ { ngx_string("name"), NGX_HTTP_SSI_BLOCK_NAME, 1, 0 },
+ { ngx_null_string, 0, 0, 0 }
+};
+
+
+static ngx_http_ssi_param_t ngx_http_ssi_no_params[] = {
+ { ngx_null_string, 0, 0, 0 }
+};
+
+
+static ngx_http_ssi_command_t ngx_http_ssi_commands[] = {
+ { ngx_string("include"), ngx_http_ssi_include,
+ ngx_http_ssi_include_params, 0, 0, 1 },
+ { ngx_string("echo"), ngx_http_ssi_echo,
+ ngx_http_ssi_echo_params, 0, 0, 0 },
+ { ngx_string("config"), ngx_http_ssi_config,
+ ngx_http_ssi_config_params, 0, 0, 0 },
+ { ngx_string("set"), ngx_http_ssi_set, ngx_http_ssi_set_params, 0, 0, 0 },
+
+ { ngx_string("if"), ngx_http_ssi_if, ngx_http_ssi_if_params, 0, 0, 0 },
+ { ngx_string("elif"), ngx_http_ssi_if, ngx_http_ssi_if_params,
+ NGX_HTTP_SSI_COND_IF, 0, 0 },
+ { ngx_string("else"), ngx_http_ssi_else, ngx_http_ssi_no_params,
+ NGX_HTTP_SSI_COND_IF, 0, 0 },
+ { ngx_string("endif"), ngx_http_ssi_endif, ngx_http_ssi_no_params,
+ NGX_HTTP_SSI_COND_ELSE, 0, 0 },
+
+ { ngx_string("block"), ngx_http_ssi_block,
+ ngx_http_ssi_block_params, 0, 0, 0 },
+ { ngx_string("endblock"), ngx_http_ssi_endblock,
+ ngx_http_ssi_no_params, 0, 1, 0 },
+
+ { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
+
+
+static ngx_http_variable_t ngx_http_ssi_vars[] = {
+
+ { ngx_string("date_local"), NULL, ngx_http_ssi_date_gmt_local_variable, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("date_gmt"), NULL, ngx_http_ssi_date_gmt_local_variable, 1,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
+
+
+
+static ngx_int_t
+ngx_http_ssi_header_filter(ngx_http_request_t *r)
+{
+ ngx_http_ssi_ctx_t *ctx;
+ ngx_http_ssi_loc_conf_t *slcf;
+
+ slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
+
+ if (!slcf->enable
+ || r->headers_out.content_length_n == 0
+ || ngx_http_test_content_type(r, &slcf->types) == NULL)
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ssi_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_ssi_filter_module);
+
+
+ ctx->value_len = slcf->value_len;
+ ctx->last_out = &ctx->out;
+
+ ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;
+ ctx->output = 1;
+
+ ctx->params.elts = ctx->params_array;
+ ctx->params.size = sizeof(ngx_table_elt_t);
+ ctx->params.nalloc = NGX_HTTP_SSI_PARAMS_N;
+ ctx->params.pool = r->pool;
+
+ ctx->timefmt = ngx_http_ssi_timefmt;
+ ngx_str_set(&ctx->errmsg,
+ "[an error occurred while processing the directive]");
+
+ r->filter_need_in_memory = 1;
+
+ if (r == r->main) {
+ ngx_http_clear_content_length(r);
+ ngx_http_clear_accept_ranges(r);
+
+ if (!slcf->last_modified) {
+ ngx_http_clear_last_modified(r);
+ ngx_http_clear_etag(r);
+
+ } else {
+ ngx_http_weak_etag(r);
+ }
+ }
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ size_t len;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_uint_t i, index;
+ ngx_chain_t *cl, **ll;
+ ngx_table_elt_t *param;
+ ngx_http_ssi_ctx_t *ctx, *mctx;
+ ngx_http_ssi_block_t *bl;
+ ngx_http_ssi_param_t *prm;
+ ngx_http_ssi_command_t *cmd;
+ ngx_http_ssi_loc_conf_t *slcf;
+ ngx_http_ssi_main_conf_t *smcf;
+ ngx_str_t *params[NGX_HTTP_SSI_MAX_PARAMS + 1];
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
+
+ if (ctx == NULL
+ || (in == NULL
+ && ctx->buf == NULL
+ && ctx->in == NULL
+ && ctx->busy == NULL))
+ {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ /* add the incoming chain to the chain ctx->in */
+
+ if (in) {
+ if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http ssi filter \"%V?%V\"", &r->uri, &r->args);
+
+ if (ctx->wait) {
+
+ if (r != r->connection->data) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http ssi filter wait \"%V?%V\" non-active",
+ &ctx->wait->uri, &ctx->wait->args);
+
+ return NGX_AGAIN;
+ }
+
+ if (ctx->wait->done) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http ssi filter wait \"%V?%V\" done",
+ &ctx->wait->uri, &ctx->wait->args);
+
+ ctx->wait = NULL;
+
+ } else {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http ssi filter wait \"%V?%V\"",
+ &ctx->wait->uri, &ctx->wait->args);
+
+ return ngx_http_next_body_filter(r, NULL);
+ }
+ }
+
+ slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
+
+ while (ctx->in || ctx->buf) {
+
+ if (ctx->buf == NULL) {
+ ctx->buf = ctx->in->buf;
+ ctx->in = ctx->in->next;
+ ctx->pos = ctx->buf->pos;
+ }
+
+ if (ctx->state == ssi_start_state) {
+ ctx->copy_start = ctx->pos;
+ ctx->copy_end = ctx->pos;
+ }
+
+ b = NULL;
+
+ while (ctx->pos < ctx->buf->last) {
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "saved: %d state: %d", ctx->saved, ctx->state);
+
+ rc = ngx_http_ssi_parse(r, ctx);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "parse: %d, looked: %d %p-%p",
+ rc, ctx->looked, ctx->copy_start, ctx->copy_end);
+
+ if (rc == NGX_ERROR) {
+ return rc;
+ }
+
+ if (ctx->copy_start != ctx->copy_end) {
+
+ if (ctx->output) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "saved: %d", ctx->saved);
+
+ if (ctx->saved) {
+
+ if (ctx->free) {
+ cl = ctx->free;
+ ctx->free = ctx->free->next;
+ b = cl->buf;
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ } else {
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ }
+
+ b->memory = 1;
+ b->pos = ngx_http_ssi_string;
+ b->last = ngx_http_ssi_string + ctx->saved;
+
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ ctx->saved = 0;
+ }
+
+ if (ctx->free) {
+ cl = ctx->free;
+ ctx->free = ctx->free->next;
+ b = cl->buf;
+
+ } else {
+ b = ngx_alloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ }
+
+ ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));
+
+ b->pos = ctx->copy_start;
+ b->last = ctx->copy_end;
+ b->shadow = NULL;
+ b->last_buf = 0;
+ b->recycled = 0;
+
+ if (b->in_file) {
+ if (slcf->min_file_chunk < (size_t) (b->last - b->pos))
+ {
+ b->file_last = b->file_pos
+ + (b->last - ctx->buf->pos);
+ b->file_pos += b->pos - ctx->buf->pos;
+
+ } else {
+ b->in_file = 0;
+ }
+ }
+
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ } else {
+ if (ctx->block
+ && ctx->saved + (ctx->copy_end - ctx->copy_start))
+ {
+ b = ngx_create_temp_buf(r->pool,
+ ctx->saved + (ctx->copy_end - ctx->copy_start));
+
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ctx->saved) {
+ b->last = ngx_cpymem(b->pos, ngx_http_ssi_string,
+ ctx->saved);
+ }
+
+ b->last = ngx_cpymem(b->last, ctx->copy_start,
+ ctx->copy_end - ctx->copy_start);
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ cl->next = NULL;
+
+ b = NULL;
+
+ mctx = ngx_http_get_module_ctx(r->main,
+ ngx_http_ssi_filter_module);
+ bl = mctx->blocks->elts;
+ for (ll = &bl[mctx->blocks->nelts - 1].bufs;
+ *ll;
+ ll = &(*ll)->next)
+ {
+ /* void */
+ }
+
+ *ll = cl;
+ }
+
+ ctx->saved = 0;
+ }
+ }
+
+ if (ctx->state == ssi_start_state) {
+ ctx->copy_start = ctx->pos;
+ ctx->copy_end = ctx->pos;
+
+ } else {
+ ctx->copy_start = NULL;
+ ctx->copy_end = NULL;
+ }
+
+ if (rc == NGX_AGAIN) {
+ continue;
+ }
+
+
+ b = NULL;
+
+ if (rc == NGX_OK) {
+
+ smcf = ngx_http_get_module_main_conf(r,
+ ngx_http_ssi_filter_module);
+
+ cmd = ngx_hash_find(&smcf->hash, ctx->key, ctx->command.data,
+ ctx->command.len);
+
+ if (cmd == NULL) {
+ if (ctx->output) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid SSI command: \"%V\"",
+ &ctx->command);
+ goto ssi_error;
+ }
+
+ continue;
+ }
+
+ if (!ctx->output && !cmd->block) {
+
+ if (ctx->block) {
+
+ /* reconstruct the SSI command text */
+
+ len = 5 + ctx->command.len + 4;
+
+ param = ctx->params.elts;
+ for (i = 0; i < ctx->params.nelts; i++) {
+ len += 1 + param[i].key.len + 2
+ + param[i].value.len + 1;
+ }
+
+ b = ngx_create_temp_buf(r->pool, len);
+
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ cl->next = NULL;
+
+ *b->last++ = '<';
+ *b->last++ = '!';
+ *b->last++ = '-';
+ *b->last++ = '-';
+ *b->last++ = '#';
+
+ b->last = ngx_cpymem(b->last, ctx->command.data,
+ ctx->command.len);
+
+ for (i = 0; i < ctx->params.nelts; i++) {
+ *b->last++ = ' ';
+ b->last = ngx_cpymem(b->last, param[i].key.data,
+ param[i].key.len);
+ *b->last++ = '=';
+ *b->last++ = '"';
+ b->last = ngx_cpymem(b->last, param[i].value.data,
+ param[i].value.len);
+ *b->last++ = '"';
+ }
+
+ *b->last++ = ' ';
+ *b->last++ = '-';
+ *b->last++ = '-';
+ *b->last++ = '>';
+
+ mctx = ngx_http_get_module_ctx(r->main,
+ ngx_http_ssi_filter_module);
+ bl = mctx->blocks->elts;
+ for (ll = &bl[mctx->blocks->nelts - 1].bufs;
+ *ll;
+ ll = &(*ll)->next)
+ {
+ /* void */
+ }
+
+ *ll = cl;
+
+ b = NULL;
+
+ continue;
+ }
+
+ if (cmd->conditional == 0) {
+ continue;
+ }
+ }
+
+ if (cmd->conditional
+ && (ctx->conditional == 0
+ || ctx->conditional > cmd->conditional))
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid context of SSI command: \"%V\"",
+ &ctx->command);
+ goto ssi_error;
+ }
+
+ if (ctx->params.nelts > NGX_HTTP_SSI_MAX_PARAMS) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "too many SSI command parameters: \"%V\"",
+ &ctx->command);
+ goto ssi_error;
+ }
+
+ ngx_memzero(params,
+ (NGX_HTTP_SSI_MAX_PARAMS + 1) * sizeof(ngx_str_t *));
+
+ param = ctx->params.elts;
+
+ for (i = 0; i < ctx->params.nelts; i++) {
+
+ for (prm = cmd->params; prm->name.len; prm++) {
+
+ if (param[i].key.len != prm->name.len
+ || ngx_strncmp(param[i].key.data, prm->name.data,
+ prm->name.len) != 0)
+ {
+ continue;
+ }
+
+ if (!prm->multiple) {
+ if (params[prm->index]) {
+ ngx_log_error(NGX_LOG_ERR,
+ r->connection->log, 0,
+ "duplicate \"%V\" parameter "
+ "in \"%V\" SSI command",
+ &param[i].key, &ctx->command);
+
+ goto ssi_error;
+ }
+
+ params[prm->index] = &param[i].value;
+
+ break;
+ }
+
+ for (index = prm->index; params[index]; index++) {
+ /* void */
+ }
+
+ params[index] = &param[i].value;
+
+ break;
+ }
+
+ if (prm->name.len == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid parameter name: \"%V\" "
+ "in \"%V\" SSI command",
+ &param[i].key, &ctx->command);
+
+ goto ssi_error;
+ }
+ }
+
+ for (prm = cmd->params; prm->name.len; prm++) {
+ if (prm->mandatory && params[prm->index] == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "mandatory \"%V\" parameter is absent "
+ "in \"%V\" SSI command",
+ &prm->name, &ctx->command);
+
+ goto ssi_error;
+ }
+ }
+
+ if (cmd->flush && ctx->out) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "ssi flush");
+
+ if (ngx_http_ssi_output(r, ctx) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+
+ rc = cmd->handler(r, ctx, params);
+
+ if (rc == NGX_OK) {
+ continue;
+ }
+
+ if (rc == NGX_DONE || rc == NGX_AGAIN || rc == NGX_ERROR) {
+ ngx_http_ssi_buffered(r, ctx);
+ return rc;
+ }
+ }
+
+
+ /* rc == NGX_HTTP_SSI_ERROR */
+
+ ssi_error:
+
+ if (slcf->silent_errors) {
+ continue;
+ }
+
+ if (ctx->free) {
+ cl = ctx->free;
+ ctx->free = ctx->free->next;
+ b = cl->buf;
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ } else {
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ }
+
+ b->memory = 1;
+ b->pos = ctx->errmsg.data;
+ b->last = ctx->errmsg.data + ctx->errmsg.len;
+
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ continue;
+ }
+
+ if (ctx->buf->last_buf || ngx_buf_in_memory(ctx->buf)) {
+ if (b == NULL) {
+ if (ctx->free) {
+ cl = ctx->free;
+ ctx->free = ctx->free->next;
+ b = cl->buf;
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ } else {
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ }
+
+ b->sync = 1;
+
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+ }
+
+ b->last_buf = ctx->buf->last_buf;
+ b->shadow = ctx->buf;
+
+ if (slcf->ignore_recycled_buffers == 0) {
+ b->recycled = ctx->buf->recycled;
+ }
+ }
+
+ ctx->buf = NULL;
+
+ ctx->saved = ctx->looked;
+ }
+
+ if (ctx->out == NULL && ctx->busy == NULL) {
+ return NGX_OK;
+ }
+
+ return ngx_http_ssi_output(r, ctx);
+}
+
+
+static ngx_int_t
+ngx_http_ssi_output(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
+{
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+
+#if 1
+ b = NULL;
+ for (cl = ctx->out; cl; cl = cl->next) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "ssi out: %p %p", cl->buf, cl->buf->pos);
+ if (cl->buf == b) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "the same buf was used in ssi");
+ ngx_debug_point();
+ return NGX_ERROR;
+ }
+ b = cl->buf;
+ }
+#endif
+
+ rc = ngx_http_next_body_filter(r, ctx->out);
+
+ if (ctx->busy == NULL) {
+ ctx->busy = ctx->out;
+
+ } else {
+ for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
+ cl->next = ctx->out;
+ }
+
+ ctx->out = NULL;
+ ctx->last_out = &ctx->out;
+
+ while (ctx->busy) {
+
+ cl = ctx->busy;
+ b = cl->buf;
+
+ if (ngx_buf_size(b) != 0) {
+ break;
+ }
+
+ if (b->shadow) {
+ b->shadow->pos = b->shadow->last;
+ }
+
+ ctx->busy = cl->next;
+
+ if (ngx_buf_in_memory(b) || b->in_file) {
+ /* add data bufs only to the free buf chain */
+
+ cl->next = ctx->free;
+ ctx->free = cl;
+ }
+ }
+
+ ngx_http_ssi_buffered(r, ctx);
+
+ return rc;
+}
+
+
+static void
+ngx_http_ssi_buffered(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
+{
+ if (ctx->in || ctx->buf) {
+ r->buffered |= NGX_HTTP_SSI_BUFFERED;
+
+ } else {
+ r->buffered &= ~NGX_HTTP_SSI_BUFFERED;
+ }
+}
+
+
+static ngx_int_t
+ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
+{
+ u_char *p, *value, *last, *copy_end, ch;
+ size_t looked;
+ ngx_http_ssi_state_e state;
+
+ state = ctx->state;
+ looked = ctx->looked;
+ last = ctx->buf->last;
+ copy_end = ctx->copy_end;
+
+ for (p = ctx->pos; p < last; p++) {
+
+ ch = *p;
+
+ if (state == ssi_start_state) {
+
+ /* the tight loop */
+
+ for ( ;; ) {
+ if (ch == '<') {
+ copy_end = p;
+ looked = 1;
+ state = ssi_tag_state;
+
+ goto tag_started;
+ }
+
+ if (++p == last) {
+ break;
+ }
+
+ ch = *p;
+ }
+
+ ctx->state = state;
+ ctx->pos = p;
+ ctx->looked = looked;
+ ctx->copy_end = p;
+
+ if (ctx->copy_start == NULL) {
+ ctx->copy_start = ctx->buf->pos;
+ }
+
+ return NGX_AGAIN;
+
+ tag_started:
+
+ continue;
+ }
+
+ switch (state) {
+
+ case ssi_start_state:
+ /* not reached */
+ break;
+
+ case ssi_tag_state:
+ switch (ch) {
+ case '!':
+ looked = 2;
+ state = ssi_comment0_state;
+ break;
+
+ case '<':
+ copy_end = p;
+ break;
+
+ default:
+ copy_end = p;
+ looked = 0;
+ state = ssi_start_state;
+ break;
+ }
+
+ break;
+
+ case ssi_comment0_state:
+ switch (ch) {
+ case '-':
+ looked = 3;
+ state = ssi_comment1_state;
+ break;
+
+ case '<':
+ copy_end = p;
+ looked = 1;
+ state = ssi_tag_state;
+ break;
+
+ default:
+ copy_end = p;
+ looked = 0;
+ state = ssi_start_state;
+ break;
+ }
+
+ break;
+
+ case ssi_comment1_state:
+ switch (ch) {
+ case '-':
+ looked = 4;
+ state = ssi_sharp_state;
+ break;
+
+ case '<':
+ copy_end = p;
+ looked = 1;
+ state = ssi_tag_state;
+ break;
+
+ default:
+ copy_end = p;
+ looked = 0;
+ state = ssi_start_state;
+ break;
+ }
+
+ break;
+
+ case ssi_sharp_state:
+ switch (ch) {
+ case '#':
+ if (p - ctx->pos < 4) {
+ ctx->saved = 0;
+ }
+ looked = 0;
+ state = ssi_precommand_state;
+ break;
+
+ case '<':
+ copy_end = p;
+ looked = 1;
+ state = ssi_tag_state;
+ break;
+
+ default:
+ copy_end = p;
+ looked = 0;
+ state = ssi_start_state;
+ break;
+ }
+
+ break;
+
+ case ssi_precommand_state:
+ switch (ch) {
+ case ' ':
+ case CR:
+ case LF:
+ case '\t':
+ break;
+
+ default:
+ ctx->command.len = 1;
+ ctx->command.data = ngx_pnalloc(r->pool,
+ NGX_HTTP_SSI_COMMAND_LEN);
+ if (ctx->command.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->command.data[0] = ch;
+
+ ctx->key = 0;
+ ctx->key = ngx_hash(ctx->key, ch);
+
+ ctx->params.nelts = 0;
+
+ state = ssi_command_state;
+ break;
+ }
+
+ break;
+
+ case ssi_command_state:
+ switch (ch) {
+ case ' ':
+ case CR:
+ case LF:
+ case '\t':
+ state = ssi_preparam_state;
+ break;
+
+ case '-':
+ state = ssi_comment_end0_state;
+ break;
+
+ default:
+ if (ctx->command.len == NGX_HTTP_SSI_COMMAND_LEN) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "the \"%V%c...\" SSI command is too long",
+ &ctx->command, ch);
+
+ state = ssi_error_state;
+ break;
+ }
+
+ ctx->command.data[ctx->command.len++] = ch;
+ ctx->key = ngx_hash(ctx->key, ch);
+ }
+
+ break;
+
+ case ssi_preparam_state:
+ switch (ch) {
+ case ' ':
+ case CR:
+ case LF:
+ case '\t':
+ break;
+
+ case '-':
+ state = ssi_comment_end0_state;
+ break;
+
+ default:
+ ctx->param = ngx_array_push(&ctx->params);
+ if (ctx->param == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->param->key.len = 1;
+ ctx->param->key.data = ngx_pnalloc(r->pool,
+ NGX_HTTP_SSI_PARAM_LEN);
+ if (ctx->param->key.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->param->key.data[0] = ch;
+
+ ctx->param->value.len = 0;
+
+ if (ctx->value_buf == NULL) {
+ ctx->param->value.data = ngx_pnalloc(r->pool,
+ ctx->value_len + 1);
+ if (ctx->param->value.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ ctx->param->value.data = ctx->value_buf;
+ }
+
+ state = ssi_param_state;
+ break;
+ }
+
+ break;
+
+ case ssi_param_state:
+ switch (ch) {
+ case ' ':
+ case CR:
+ case LF:
+ case '\t':
+ state = ssi_preequal_state;
+ break;
+
+ case '=':
+ state = ssi_prevalue_state;
+ break;
+
+ case '-':
+ state = ssi_error_end0_state;
+
+ ctx->param->key.data[ctx->param->key.len++] = ch;
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid \"%V\" parameter in \"%V\" SSI command",
+ &ctx->param->key, &ctx->command);
+ break;
+
+ default:
+ if (ctx->param->key.len == NGX_HTTP_SSI_PARAM_LEN) {
+ state = ssi_error_state;
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "too long \"%V%c...\" parameter in "
+ "\"%V\" SSI command",
+ &ctx->param->key, ch, &ctx->command);
+ break;
+ }
+
+ ctx->param->key.data[ctx->param->key.len++] = ch;
+ }
+
+ break;
+
+ case ssi_preequal_state:
+ switch (ch) {
+ case ' ':
+ case CR:
+ case LF:
+ case '\t':
+ break;
+
+ case '=':
+ state = ssi_prevalue_state;
+ break;
+
+ default:
+ if (ch == '-') {
+ state = ssi_error_end0_state;
+ } else {
+ state = ssi_error_state;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "unexpected \"%c\" symbol after \"%V\" "
+ "parameter in \"%V\" SSI command",
+ ch, &ctx->param->key, &ctx->command);
+ break;
+ }
+
+ break;
+
+ case ssi_prevalue_state:
+ switch (ch) {
+ case ' ':
+ case CR:
+ case LF:
+ case '\t':
+ break;
+
+ case '"':
+ state = ssi_double_quoted_value_state;
+ break;
+
+ case '\'':
+ state = ssi_quoted_value_state;
+ break;
+
+ default:
+ if (ch == '-') {
+ state = ssi_error_end0_state;
+ } else {
+ state = ssi_error_state;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "unexpected \"%c\" symbol before value of "
+ "\"%V\" parameter in \"%V\" SSI command",
+ ch, &ctx->param->key, &ctx->command);
+ break;
+ }
+
+ break;
+
+ case ssi_double_quoted_value_state:
+ switch (ch) {
+ case '"':
+ state = ssi_postparam_state;
+ break;
+
+ case '\\':
+ ctx->saved_state = ssi_double_quoted_value_state;
+ state = ssi_quoted_symbol_state;
+
+ /* fall through */
+
+ default:
+ if (ctx->param->value.len == ctx->value_len) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "too long \"%V%c...\" value of \"%V\" "
+ "parameter in \"%V\" SSI command",
+ &ctx->param->value, ch, &ctx->param->key,
+ &ctx->command);
+ state = ssi_error_state;
+ break;
+ }
+
+ ctx->param->value.data[ctx->param->value.len++] = ch;
+ }
+
+ break;
+
+ case ssi_quoted_value_state:
+ switch (ch) {
+ case '\'':
+ state = ssi_postparam_state;
+ break;
+
+ case '\\':
+ ctx->saved_state = ssi_quoted_value_state;
+ state = ssi_quoted_symbol_state;
+
+ /* fall through */
+
+ default:
+ if (ctx->param->value.len == ctx->value_len) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "too long \"%V%c...\" value of \"%V\" "
+ "parameter in \"%V\" SSI command",
+ &ctx->param->value, ch, &ctx->param->key,
+ &ctx->command);
+ state = ssi_error_state;
+ break;
+ }
+
+ ctx->param->value.data[ctx->param->value.len++] = ch;
+ }
+
+ break;
+
+ case ssi_quoted_symbol_state:
+ state = ctx->saved_state;
+
+ if (ctx->param->value.len == ctx->value_len) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "too long \"%V%c...\" value of \"%V\" "
+ "parameter in \"%V\" SSI command",
+ &ctx->param->value, ch, &ctx->param->key,
+ &ctx->command);
+ state = ssi_error_state;
+ break;
+ }
+
+ ctx->param->value.data[ctx->param->value.len++] = ch;
+
+ break;
+
+ case ssi_postparam_state:
+
+ if (ctx->param->value.len + 1 < ctx->value_len / 2) {
+ value = ngx_pnalloc(r->pool, ctx->param->value.len + 1);
+ if (value == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(value, ctx->param->value.data,
+ ctx->param->value.len);
+
+ ctx->value_buf = ctx->param->value.data;
+ ctx->param->value.data = value;
+
+ } else {
+ ctx->value_buf = NULL;
+ }
+
+ switch (ch) {
+ case ' ':
+ case CR:
+ case LF:
+ case '\t':
+ state = ssi_preparam_state;
+ break;
+
+ case '-':
+ state = ssi_comment_end0_state;
+ break;
+
+ default:
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "unexpected \"%c\" symbol after \"%V\" value "
+ "of \"%V\" parameter in \"%V\" SSI command",
+ ch, &ctx->param->value, &ctx->param->key,
+ &ctx->command);
+ state = ssi_error_state;
+ break;
+ }
+
+ break;
+
+ case ssi_comment_end0_state:
+ switch (ch) {
+ case '-':
+ state = ssi_comment_end1_state;
+ break;
+
+ default:
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "unexpected \"%c\" symbol in \"%V\" SSI command",
+ ch, &ctx->command);
+ state = ssi_error_state;
+ break;
+ }
+
+ break;
+
+ case ssi_comment_end1_state:
+ switch (ch) {
+ case '>':
+ ctx->state = ssi_start_state;
+ ctx->pos = p + 1;
+ ctx->looked = looked;
+ ctx->copy_end = copy_end;
+
+ if (ctx->copy_start == NULL && copy_end) {
+ ctx->copy_start = ctx->buf->pos;
+ }
+
+ return NGX_OK;
+
+ default:
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "unexpected \"%c\" symbol in \"%V\" SSI command",
+ ch, &ctx->command);
+ state = ssi_error_state;
+ break;
+ }
+
+ break;
+
+ case ssi_error_state:
+ switch (ch) {
+ case '-':
+ state = ssi_error_end0_state;
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case ssi_error_end0_state:
+ switch (ch) {
+ case '-':
+ state = ssi_error_end1_state;
+ break;
+
+ default:
+ state = ssi_error_state;
+ break;
+ }
+
+ break;
+
+ case ssi_error_end1_state:
+ switch (ch) {
+ case '>':
+ ctx->state = ssi_start_state;
+ ctx->pos = p + 1;
+ ctx->looked = looked;
+ ctx->copy_end = copy_end;
+
+ if (ctx->copy_start == NULL && copy_end) {
+ ctx->copy_start = ctx->buf->pos;
+ }
+
+ return NGX_HTTP_SSI_ERROR;
+
+ default:
+ state = ssi_error_state;
+ break;
+ }
+
+ break;
+ }
+ }
+
+ ctx->state = state;
+ ctx->pos = p;
+ ctx->looked = looked;
+
+ ctx->copy_end = (state == ssi_start_state) ? p : copy_end;
+
+ if (ctx->copy_start == NULL && ctx->copy_end) {
+ ctx->copy_start = ctx->buf->pos;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_str_t *
+ngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name,
+ ngx_uint_t key)
+{
+ ngx_uint_t i;
+ ngx_list_part_t *part;
+ ngx_http_ssi_var_t *var;
+ ngx_http_ssi_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
+
+#if (NGX_PCRE)
+ {
+ ngx_str_t *value;
+
+ if (key >= '0' && key <= '9') {
+ i = key - '0';
+
+ if (i < ctx->ncaptures) {
+ value = ngx_palloc(r->pool, sizeof(ngx_str_t));
+ if (value == NULL) {
+ return NULL;
+ }
+
+ i *= 2;
+
+ value->data = ctx->captures_data + ctx->captures[i];
+ value->len = ctx->captures[i + 1] - ctx->captures[i];
+
+ return value;
+ }
+ }
+ }
+#endif
+
+ if (ctx->variables == NULL) {
+ return NULL;
+ }
+
+ part = &ctx->variables->part;
+ var = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ var = part->elts;
+ i = 0;
+ }
+
+ if (name->len != var[i].name.len) {
+ continue;
+ }
+
+ if (key != var[i].key) {
+ continue;
+ }
+
+ if (ngx_strncmp(name->data, var[i].name.data, name->len) == 0) {
+ return &var[i].value;
+ }
+ }
+
+ return NULL;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
+ ngx_str_t *text, ngx_uint_t flags)
+{
+ u_char ch, *p, **value, *data, *part_data;
+ size_t *size, len, prefix, part_len;
+ ngx_str_t var, *val;
+ ngx_int_t key;
+ ngx_uint_t i, n, bracket, quoted;
+ ngx_array_t lengths, values;
+ ngx_http_variable_value_t *vv;
+
+ n = ngx_http_script_variables_count(text);
+
+ if (n == 0) {
+
+ data = text->data;
+ p = data;
+
+ if ((flags & NGX_HTTP_SSI_ADD_PREFIX) && text->data[0] != '/') {
+
+ for (prefix = r->uri.len; prefix; prefix--) {
+ if (r->uri.data[prefix - 1] == '/') {
+ break;
+ }
+ }
+
+ if (prefix) {
+ len = prefix + text->len;
+
+ data = ngx_pnalloc(r->pool, len);
+ if (data == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_copy(data, r->uri.data, prefix);
+ }
+ }
+
+ quoted = 0;
+
+ for (i = 0; i < text->len; i++) {
+ ch = text->data[i];
+
+ if (!quoted) {
+
+ if (ch == '\\') {
+ quoted = 1;
+ continue;
+ }
+
+ } else {
+ quoted = 0;
+
+ if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {
+ *p++ = '\\';
+ }
+ }
+
+ *p++ = ch;
+ }
+
+ text->len = p - data;
+ text->data = data;
+
+ return NGX_OK;
+ }
+
+ if (ngx_array_init(&lengths, r->pool, 8, sizeof(size_t *)) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&values, r->pool, 8, sizeof(u_char *)) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ len = 0;
+ i = 0;
+
+ while (i < text->len) {
+
+ if (text->data[i] == '$') {
+
+ var.len = 0;
+
+ if (++i == text->len) {
+ goto invalid_variable;
+ }
+
+ if (text->data[i] == '{') {
+ bracket = 1;
+
+ if (++i == text->len) {
+ goto invalid_variable;
+ }
+
+ var.data = &text->data[i];
+
+ } else {
+ bracket = 0;
+ var.data = &text->data[i];
+ }
+
+ for ( /* void */ ; i < text->len; i++, var.len++) {
+ ch = text->data[i];
+
+ if (ch == '}' && bracket) {
+ i++;
+ bracket = 0;
+ break;
+ }
+
+ if ((ch >= 'A' && ch <= 'Z')
+ || (ch >= 'a' && ch <= 'z')
+ || (ch >= '0' && ch <= '9')
+ || ch == '_')
+ {
+ continue;
+ }
+
+ break;
+ }
+
+ if (bracket) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "the closing bracket in \"%V\" "
+ "variable is missing", &var);
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ if (var.len == 0) {
+ goto invalid_variable;
+ }
+
+ key = ngx_hash_strlow(var.data, var.data, var.len);
+
+ val = ngx_http_ssi_get_variable(r, &var, key);
+
+ if (val == NULL) {
+ vv = ngx_http_get_variable(r, &var, key);
+ if (vv == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (vv->not_found) {
+ continue;
+ }
+
+ part_data = vv->data;
+ part_len = vv->len;
+
+ } else {
+ part_data = val->data;
+ part_len = val->len;
+ }
+
+ } else {
+ part_data = &text->data[i];
+ quoted = 0;
+
+ for (p = part_data; i < text->len; i++) {
+ ch = text->data[i];
+
+ if (!quoted) {
+
+ if (ch == '\\') {
+ quoted = 1;
+ continue;
+ }
+
+ if (ch == '$') {
+ break;
+ }
+
+ } else {
+ quoted = 0;
+
+ if (ch != '\\' && ch != '\'' && ch != '"' && ch != '$') {
+ *p++ = '\\';
+ }
+ }
+
+ *p++ = ch;
+ }
+
+ part_len = p - part_data;
+ }
+
+ len += part_len;
+
+ size = ngx_array_push(&lengths);
+ if (size == NULL) {
+ return NGX_ERROR;
+ }
+
+ *size = part_len;
+
+ value = ngx_array_push(&values);
+ if (value == NULL) {
+ return NGX_ERROR;
+ }
+
+ *value = part_data;
+ }
+
+ prefix = 0;
+
+ size = lengths.elts;
+ value = values.elts;
+
+ if (flags & NGX_HTTP_SSI_ADD_PREFIX) {
+ for (i = 0; i < values.nelts; i++) {
+ if (size[i] != 0) {
+ if (*value[i] != '/') {
+ for (prefix = r->uri.len; prefix; prefix--) {
+ if (r->uri.data[prefix - 1] == '/') {
+ len += prefix;
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+ }
+ }
+
+ p = ngx_pnalloc(r->pool, len + ((flags & NGX_HTTP_SSI_ADD_ZERO) ? 1 : 0));
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ text->len = len;
+ text->data = p;
+
+ p = ngx_copy(p, r->uri.data, prefix);
+
+ for (i = 0; i < values.nelts; i++) {
+ p = ngx_copy(p, value[i], size[i]);
+ }
+
+ return NGX_OK;
+
+invalid_variable:
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid variable name in \"%V\"", text);
+
+ return NGX_HTTP_SSI_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_regex_match(ngx_http_request_t *r, ngx_str_t *pattern,
+ ngx_str_t *str)
+{
+#if (NGX_PCRE)
+ int rc, *captures;
+ u_char *p, errstr[NGX_MAX_CONF_ERRSTR];
+ size_t size;
+ ngx_int_t key;
+ ngx_str_t *vv, name, value;
+ ngx_uint_t i, n;
+ ngx_http_ssi_ctx_t *ctx;
+ ngx_http_ssi_var_t *var;
+ ngx_regex_compile_t rgc;
+
+ ngx_memzero(&rgc, sizeof(ngx_regex_compile_t));
+
+ rgc.pattern = *pattern;
+ rgc.pool = r->pool;
+ rgc.err.len = NGX_MAX_CONF_ERRSTR;
+ rgc.err.data = errstr;
+
+ if (ngx_regex_compile(&rgc) != NGX_OK) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%V", &rgc.err);
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ n = (rgc.captures + 1) * 3;
+
+ captures = ngx_palloc(r->pool, n * sizeof(int));
+ if (captures == NULL) {
+ return NGX_ERROR;
+ }
+
+ rc = ngx_regex_exec(rgc.regex, str, captures, n);
+
+ if (rc < NGX_REGEX_NO_MATCHED) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
+ rc, str, pattern);
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ if (rc == NGX_REGEX_NO_MATCHED) {
+ return NGX_DECLINED;
+ }
+
+ ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
+
+ ctx->ncaptures = rc;
+ ctx->captures = captures;
+ ctx->captures_data = str->data;
+
+ if (rgc.named_captures > 0) {
+
+ if (ctx->variables == NULL) {
+ ctx->variables = ngx_list_create(r->pool, 4,
+ sizeof(ngx_http_ssi_var_t));
+ if (ctx->variables == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ size = rgc.name_size;
+ p = rgc.names;
+
+ for (i = 0; i < (ngx_uint_t) rgc.named_captures; i++, p += size) {
+
+ name.data = &p[2];
+ name.len = ngx_strlen(name.data);
+
+ n = 2 * ((p[0] << 8) + p[1]);
+
+ value.data = &str->data[captures[n]];
+ value.len = captures[n + 1] - captures[n];
+
+ key = ngx_hash_strlow(name.data, name.data, name.len);
+
+ vv = ngx_http_ssi_get_variable(r, &name, key);
+
+ if (vv) {
+ *vv = value;
+ continue;
+ }
+
+ var = ngx_list_push(ctx->variables);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->name = name;
+ var->key = key;
+ var->value = value;
+ }
+ }
+
+ return NGX_OK;
+
+#else
+
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "the using of the regex \"%V\" in SSI requires PCRE library",
+ pattern);
+ return NGX_HTTP_SSI_ERROR;
+
+#endif
+}
+
+
+static ngx_int_t
+ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
+ ngx_str_t **params)
+{
+ ngx_int_t rc, key;
+ ngx_str_t *uri, *file, *wait, *set, *stub, args;
+ ngx_buf_t *b;
+ ngx_uint_t flags, i;
+ ngx_chain_t *cl, *tl, **ll, *out;
+ ngx_http_request_t *sr;
+ ngx_http_ssi_var_t *var;
+ ngx_http_ssi_ctx_t *mctx;
+ ngx_http_ssi_block_t *bl;
+ ngx_http_post_subrequest_t *psr;
+
+ uri = params[NGX_HTTP_SSI_INCLUDE_VIRTUAL];
+ file = params[NGX_HTTP_SSI_INCLUDE_FILE];
+ wait = params[NGX_HTTP_SSI_INCLUDE_WAIT];
+ set = params[NGX_HTTP_SSI_INCLUDE_SET];
+ stub = params[NGX_HTTP_SSI_INCLUDE_STUB];
+
+ if (uri && file) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "inlcusion may be either virtual=\"%V\" or file=\"%V\"",
+ uri, file);
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ if (uri == NULL && file == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "no parameter in \"include\" SSI command");
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ if (set && stub) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "\"set\" and \"stub\" cannot be used together "
+ "in \"include\" SSI command");
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ if (wait) {
+ if (uri == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "\"wait\" cannot be used with file=\"%V\"", file);
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ if (wait->len == 2
+ && ngx_strncasecmp(wait->data, (u_char *) "no", 2) == 0)
+ {
+ wait = NULL;
+
+ } else if (wait->len != 3
+ || ngx_strncasecmp(wait->data, (u_char *) "yes", 3) != 0)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid value \"%V\" in the \"wait\" parameter",
+ wait);
+ return NGX_HTTP_SSI_ERROR;
+ }
+ }
+
+ if (uri == NULL) {
+ uri = file;
+ wait = (ngx_str_t *) -1;
+ }
+
+ rc = ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "ssi include: \"%V\"", uri);
+
+ ngx_str_null(&args);
+ flags = NGX_HTTP_LOG_UNSAFE;
+
+ if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ psr = NULL;
+
+ mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
+
+ if (stub) {
+ if (mctx->blocks) {
+ bl = mctx->blocks->elts;
+ for (i = 0; i < mctx->blocks->nelts; i++) {
+ if (stub->len == bl[i].name.len
+ && ngx_strncmp(stub->data, bl[i].name.data, stub->len) == 0)
+ {
+ goto found;
+ }
+ }
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "\"stub\"=\"%V\" for \"include\" not found", stub);
+ return NGX_HTTP_SSI_ERROR;
+
+ found:
+
+ psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
+ if (psr == NULL) {
+ return NGX_ERROR;
+ }
+
+ psr->handler = ngx_http_ssi_stub_output;
+
+ if (bl[i].count++) {
+
+ out = NULL;
+ ll = &out;
+
+ for (tl = bl[i].bufs; tl; tl = tl->next) {
+
+ if (ctx->free) {
+ cl = ctx->free;
+ ctx->free = ctx->free->next;
+ b = cl->buf;
+
+ } else {
+ b = ngx_alloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ }
+
+ ngx_memcpy(b, tl->buf, sizeof(ngx_buf_t));
+
+ b->pos = b->start;
+
+ *ll = cl;
+ cl->next = NULL;
+ ll = &cl->next;
+ }
+
+ psr->data = out;
+
+ } else {
+ psr->data = bl[i].bufs;
+ }
+ }
+
+ if (wait) {
+ flags |= NGX_HTTP_SUBREQUEST_WAITED;
+ }
+
+ if (set) {
+ key = ngx_hash_strlow(set->data, set->data, set->len);
+
+ psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
+ if (psr == NULL) {
+ return NGX_ERROR;
+ }
+
+ psr->handler = ngx_http_ssi_set_variable;
+ psr->data = ngx_http_ssi_get_variable(r, set, key);
+
+ if (psr->data == NULL) {
+
+ if (mctx->variables == NULL) {
+ mctx->variables = ngx_list_create(r->pool, 4,
+ sizeof(ngx_http_ssi_var_t));
+ if (mctx->variables == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ var = ngx_list_push(mctx->variables);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->name = *set;
+ var->key = key;
+ var->value = ngx_http_ssi_null_string;
+ psr->data = &var->value;
+ }
+
+ flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED;
+ }
+
+ if (ngx_http_subrequest(r, uri, &args, &sr, psr, flags) != NGX_OK) {
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ if (wait == NULL && set == NULL) {
+ return NGX_OK;
+ }
+
+ if (ctx->wait == NULL) {
+ ctx->wait = sr;
+
+ return NGX_AGAIN;
+
+ } else {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "can only wait for one subrequest at a time");
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data, ngx_int_t rc)
+{
+ ngx_chain_t *out;
+
+ if (rc == NGX_ERROR || r->connection->error || r->request_output) {
+ return rc;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "ssi stub output: \"%V?%V\"", &r->uri, &r->args);
+
+ out = data;
+
+ if (!r->header_sent) {
+ r->headers_out.content_type_len =
+ r->parent->headers_out.content_type_len;
+ r->headers_out.content_type = r->parent->headers_out.content_type;
+
+ if (ngx_http_send_header(r) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+
+ return ngx_http_output_filter(r, out);
+}
+
+
+static ngx_int_t
+ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data, ngx_int_t rc)
+{
+ ngx_str_t *value = data;
+
+ if (r->upstream) {
+ value->len = r->upstream->buffer.last - r->upstream->buffer.pos;
+ value->data = r->upstream->buffer.pos;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
+ ngx_str_t **params)
+{
+ u_char *p;
+ uintptr_t len;
+ ngx_int_t key;
+ ngx_buf_t *b;
+ ngx_str_t *var, *value, *enc, text;
+ ngx_chain_t *cl;
+ ngx_http_variable_value_t *vv;
+
+ var = params[NGX_HTTP_SSI_ECHO_VAR];
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "ssi echo \"%V\"", var);
+
+ key = ngx_hash_strlow(var->data, var->data, var->len);
+
+ value = ngx_http_ssi_get_variable(r, var, key);
+
+ if (value == NULL) {
+ vv = ngx_http_get_variable(r, var, key);
+
+ if (vv == NULL) {
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ if (!vv->not_found) {
+ text.data = vv->data;
+ text.len = vv->len;
+ value = &text;
+ }
+ }
+
+ if (value == NULL) {
+ value = params[NGX_HTTP_SSI_ECHO_DEFAULT];
+
+ if (value == NULL) {
+ value = &ngx_http_ssi_none;
+
+ } else if (value->len == 0) {
+ return NGX_OK;
+ }
+
+ } else {
+ if (value->len == 0) {
+ return NGX_OK;
+ }
+ }
+
+ enc = params[NGX_HTTP_SSI_ECHO_ENCODING];
+
+ if (enc) {
+ if (enc->len == 4 && ngx_strncmp(enc->data, "none", 4) == 0) {
+
+ ctx->encoding = NGX_HTTP_SSI_NO_ENCODING;
+
+ } else if (enc->len == 3 && ngx_strncmp(enc->data, "url", 3) == 0) {
+
+ ctx->encoding = NGX_HTTP_SSI_URL_ENCODING;
+
+ } else if (enc->len == 6 && ngx_strncmp(enc->data, "entity", 6) == 0) {
+
+ ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;
+
+ } else {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "unknown encoding \"%V\" in the \"echo\" command",
+ enc);
+ }
+ }
+
+ p = value->data;
+
+ switch (ctx->encoding) {
+
+ case NGX_HTTP_SSI_URL_ENCODING:
+ len = 2 * ngx_escape_uri(NULL, value->data, value->len,
+ NGX_ESCAPE_HTML);
+
+ if (len) {
+ p = ngx_pnalloc(r->pool, value->len + len);
+ if (p == NULL) {
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ (void) ngx_escape_uri(p, value->data, value->len, NGX_ESCAPE_HTML);
+ }
+
+ len += value->len;
+ break;
+
+ case NGX_HTTP_SSI_ENTITY_ENCODING:
+ len = ngx_escape_html(NULL, value->data, value->len);
+
+ if (len) {
+ p = ngx_pnalloc(r->pool, value->len + len);
+ if (p == NULL) {
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ (void) ngx_escape_html(p, value->data, value->len);
+ }
+
+ len += value->len;
+ break;
+
+ default: /* NGX_HTTP_SSI_NO_ENCODING */
+ len = value->len;
+ break;
+ }
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ b->memory = 1;
+ b->pos = p;
+ b->last = p + len;
+
+ cl->buf = b;
+ cl->next = NULL;
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_config(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
+ ngx_str_t **params)
+{
+ ngx_str_t *value;
+
+ value = params[NGX_HTTP_SSI_CONFIG_TIMEFMT];
+
+ if (value) {
+ ctx->timefmt.len = value->len;
+ ctx->timefmt.data = ngx_pnalloc(r->pool, value->len + 1);
+ if (ctx->timefmt.data == NULL) {
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ ngx_cpystrn(ctx->timefmt.data, value->data, value->len + 1);
+ }
+
+ value = params[NGX_HTTP_SSI_CONFIG_ERRMSG];
+
+ if (value) {
+ ctx->errmsg = *value;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
+ ngx_str_t **params)
+{
+ ngx_int_t key, rc;
+ ngx_str_t *name, *value, *vv;
+ ngx_http_ssi_var_t *var;
+ ngx_http_ssi_ctx_t *mctx;
+
+ mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
+
+ if (mctx->variables == NULL) {
+ mctx->variables = ngx_list_create(r->pool, 4,
+ sizeof(ngx_http_ssi_var_t));
+ if (mctx->variables == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ name = params[NGX_HTTP_SSI_SET_VAR];
+ value = params[NGX_HTTP_SSI_SET_VALUE];
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "ssi set \"%V\" \"%V\"", name, value);
+
+ rc = ngx_http_ssi_evaluate_string(r, ctx, value, 0);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ key = ngx_hash_strlow(name->data, name->data, name->len);
+
+ vv = ngx_http_ssi_get_variable(r, name, key);
+
+ if (vv) {
+ *vv = *value;
+ return NGX_OK;
+ }
+
+ var = ngx_list_push(mctx->variables);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->name = *name;
+ var->key = key;
+ var->value = *value;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "set: \"%V\"=\"%V\"", name, value);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_if(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
+ ngx_str_t **params)
+{
+ u_char *p, *last;
+ ngx_str_t *expr, left, right;
+ ngx_int_t rc;
+ ngx_uint_t negative, noregex, flags;
+
+ if (ctx->command.len == 2) {
+ if (ctx->conditional) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "the \"if\" command inside the \"if\" command");
+ return NGX_HTTP_SSI_ERROR;
+ }
+ }
+
+ if (ctx->output_chosen) {
+ ctx->output = 0;
+ return NGX_OK;
+ }
+
+ expr = params[NGX_HTTP_SSI_IF_EXPR];
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "ssi if expr=\"%V\"", expr);
+
+ left.data = expr->data;
+ last = expr->data + expr->len;
+
+ for (p = left.data; p < last; p++) {
+ if (*p >= 'A' && *p <= 'Z') {
+ *p |= 0x20;
+ continue;
+ }
+
+ if ((*p >= 'a' && *p <= 'z')
+ || (*p >= '0' && *p <= '9')
+ || *p == '$' || *p == '{' || *p == '}' || *p == '_'
+ || *p == '"' || *p == '\'')
+ {
+ continue;
+ }
+
+ break;
+ }
+
+ left.len = p - left.data;
+
+ while (p < last && *p == ' ') {
+ p++;
+ }
+
+ flags = 0;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "left: \"%V\"", &left);
+
+ rc = ngx_http_ssi_evaluate_string(r, ctx, &left, flags);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "evaluted left: \"%V\"", &left);
+
+ if (p == last) {
+ if (left.len) {
+ ctx->output = 1;
+ ctx->output_chosen = 1;
+
+ } else {
+ ctx->output = 0;
+ }
+
+ ctx->conditional = NGX_HTTP_SSI_COND_IF;
+
+ return NGX_OK;
+ }
+
+ if (p < last && *p == '=') {
+ negative = 0;
+ p++;
+
+ } else if (p + 1 < last && *p == '!' && *(p + 1) == '=') {
+ negative = 1;
+ p += 2;
+
+ } else {
+ goto invalid_expression;
+ }
+
+ while (p < last && *p == ' ') {
+ p++;
+ }
+
+ if (p < last - 1 && *p == '/') {
+ if (*(last - 1) != '/') {
+ goto invalid_expression;
+ }
+
+ noregex = 0;
+ flags = NGX_HTTP_SSI_ADD_ZERO;
+ last--;
+ p++;
+
+ } else {
+ noregex = 1;
+ flags = 0;
+
+ if (p < last - 1 && p[0] == '\\' && p[1] == '/') {
+ p++;
+ }
+ }
+
+ right.len = last - p;
+ right.data = p;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "right: \"%V\"", &right);
+
+ rc = ngx_http_ssi_evaluate_string(r, ctx, &right, flags);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "evaluted right: \"%V\"", &right);
+
+ if (noregex) {
+ if (left.len != right.len) {
+ rc = -1;
+
+ } else {
+ rc = ngx_strncmp(left.data, right.data, right.len);
+ }
+
+ } else {
+ right.data[right.len] = '\0';
+
+ rc = ngx_http_ssi_regex_match(r, &right, &left);
+
+ if (rc == NGX_OK) {
+ rc = 0;
+ } else if (rc == NGX_DECLINED) {
+ rc = -1;
+ } else {
+ return rc;
+ }
+ }
+
+ if ((rc == 0 && !negative) || (rc != 0 && negative)) {
+ ctx->output = 1;
+ ctx->output_chosen = 1;
+
+ } else {
+ ctx->output = 0;
+ }
+
+ ctx->conditional = NGX_HTTP_SSI_COND_IF;
+
+ return NGX_OK;
+
+invalid_expression:
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid expression in \"%V\"", expr);
+
+ return NGX_HTTP_SSI_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_else(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
+ ngx_str_t **params)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "ssi else");
+
+ if (ctx->output_chosen) {
+ ctx->output = 0;
+ } else {
+ ctx->output = 1;
+ }
+
+ ctx->conditional = NGX_HTTP_SSI_COND_ELSE;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_endif(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
+ ngx_str_t **params)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "ssi endif");
+
+ ctx->output = 1;
+ ctx->output_chosen = 0;
+ ctx->conditional = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_block(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
+ ngx_str_t **params)
+{
+ ngx_http_ssi_ctx_t *mctx;
+ ngx_http_ssi_block_t *bl;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "ssi block");
+
+ mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
+
+ if (mctx->blocks == NULL) {
+ mctx->blocks = ngx_array_create(r->pool, 4,
+ sizeof(ngx_http_ssi_block_t));
+ if (mctx->blocks == NULL) {
+ return NGX_HTTP_SSI_ERROR;
+ }
+ }
+
+ bl = ngx_array_push(mctx->blocks);
+ if (bl == NULL) {
+ return NGX_HTTP_SSI_ERROR;
+ }
+
+ bl->name = *params[NGX_HTTP_SSI_BLOCK_NAME];
+ bl->bufs = NULL;
+ bl->count = 0;
+
+ ctx->output = 0;
+ ctx->block = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_endblock(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
+ ngx_str_t **params)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "ssi endblock");
+
+ ctx->output = 1;
+ ctx->block = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t gmt)
+{
+ ngx_http_ssi_ctx_t *ctx;
+ ngx_time_t *tp;
+ ngx_str_t *timefmt;
+ struct tm tm;
+ char buf[NGX_HTTP_SSI_DATE_LEN];
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ tp = ngx_timeofday();
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
+
+ timefmt = ctx ? &ctx->timefmt : &ngx_http_ssi_timefmt;
+
+ if (timefmt->len == sizeof("%s") - 1
+ && timefmt->data[0] == '%' && timefmt->data[1] == 's')
+ {
+ v->data = ngx_pnalloc(r->pool, NGX_TIME_T_LEN);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(v->data, "%T", tp->sec) - v->data;
+
+ return NGX_OK;
+ }
+
+ if (gmt) {
+ ngx_libc_gmtime(tp->sec, &tm);
+ } else {
+ ngx_libc_localtime(tp->sec, &tm);
+ }
+
+ v->len = strftime(buf, NGX_HTTP_SSI_DATE_LEN,
+ (char *) timefmt->data, &tm);
+ if (v->len == 0) {
+ return NGX_ERROR;
+ }
+
+ v->data = ngx_pnalloc(r->pool, v->len);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(v->data, buf, v->len);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_preconfiguration(ngx_conf_t *cf)
+{
+ ngx_int_t rc;
+ ngx_http_variable_t *var, *v;
+ ngx_http_ssi_command_t *cmd;
+ ngx_http_ssi_main_conf_t *smcf;
+
+ for (v = ngx_http_ssi_vars; v->name.len; v++) {
+ var = ngx_http_add_variable(cf, &v->name, v->flags);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = v->get_handler;
+ var->data = v->data;
+ }
+
+ smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_ssi_filter_module);
+
+ for (cmd = ngx_http_ssi_commands; cmd->name.len; cmd++) {
+ rc = ngx_hash_add_key(&smcf->commands, &cmd->name, cmd,
+ NGX_HASH_READONLY_KEY);
+
+ if (rc == NGX_OK) {
+ continue;
+ }
+
+ if (rc == NGX_BUSY) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "conflicting SSI command \"%V\"", &cmd->name);
+ }
+
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_ssi_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_http_ssi_main_conf_t *smcf;
+
+ smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_main_conf_t));
+ if (smcf == NULL) {
+ return NULL;
+ }
+
+ smcf->commands.pool = cf->pool;
+ smcf->commands.temp_pool = cf->temp_pool;
+
+ if (ngx_hash_keys_array_init(&smcf->commands, NGX_HASH_SMALL) != NGX_OK) {
+ return NULL;
+ }
+
+ return smcf;
+}
+
+
+static char *
+ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf)
+{
+ ngx_http_ssi_main_conf_t *smcf = conf;
+
+ ngx_hash_init_t hash;
+
+ hash.hash = &smcf->hash;
+ hash.key = ngx_hash_key;
+ hash.max_size = 1024;
+ hash.bucket_size = ngx_cacheline_size;
+ hash.name = "ssi_command_hash";
+ hash.pool = cf->pool;
+ hash.temp_pool = NULL;
+
+ if (ngx_hash_init(&hash, smcf->commands.keys.elts,
+ smcf->commands.keys.nelts)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_ssi_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_ssi_loc_conf_t *slcf;
+
+ slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_loc_conf_t));
+ if (slcf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->types = { NULL };
+ * conf->types_keys = NULL;
+ */
+
+ slcf->enable = NGX_CONF_UNSET;
+ slcf->silent_errors = NGX_CONF_UNSET;
+ slcf->ignore_recycled_buffers = NGX_CONF_UNSET;
+ slcf->last_modified = NGX_CONF_UNSET;
+
+ slcf->min_file_chunk = NGX_CONF_UNSET_SIZE;
+ slcf->value_len = NGX_CONF_UNSET_SIZE;
+
+ return slcf;
+}
+
+
+static char *
+ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_ssi_loc_conf_t *prev = parent;
+ ngx_http_ssi_loc_conf_t *conf = child;
+
+ ngx_conf_merge_value(conf->enable, prev->enable, 0);
+ ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0);
+ ngx_conf_merge_value(conf->ignore_recycled_buffers,
+ prev->ignore_recycled_buffers, 0);
+ ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);
+
+ ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024);
+ ngx_conf_merge_size_value(conf->value_len, prev->value_len, 255);
+
+ if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
+ &prev->types_keys, &prev->types,
+ ngx_http_html_default_types)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_ssi_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_ssi_header_filter;
+
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_ssi_body_filter;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssi_filter_module.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssi_filter_module.h
new file mode 100644
index 00000000000..0bd01a06775
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssi_filter_module.h
@@ -0,0 +1,114 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_SSI_FILTER_H_INCLUDED_
+#define _NGX_HTTP_SSI_FILTER_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_SSI_MAX_PARAMS 16
+
+#define NGX_HTTP_SSI_COMMAND_LEN 32
+#define NGX_HTTP_SSI_PARAM_LEN 32
+#define NGX_HTTP_SSI_PARAMS_N 4
+
+
+#define NGX_HTTP_SSI_COND_IF 1
+#define NGX_HTTP_SSI_COND_ELSE 2
+
+
+#define NGX_HTTP_SSI_NO_ENCODING 0
+#define NGX_HTTP_SSI_URL_ENCODING 1
+#define NGX_HTTP_SSI_ENTITY_ENCODING 2
+
+
+typedef struct {
+ ngx_hash_t hash;
+ ngx_hash_keys_arrays_t commands;
+} ngx_http_ssi_main_conf_t;
+
+
+typedef struct {
+ ngx_buf_t *buf;
+
+ u_char *pos;
+ u_char *copy_start;
+ u_char *copy_end;
+
+ ngx_uint_t key;
+ ngx_str_t command;
+ ngx_array_t params;
+ ngx_table_elt_t *param;
+ ngx_table_elt_t params_array[NGX_HTTP_SSI_PARAMS_N];
+
+ ngx_chain_t *in;
+ ngx_chain_t *out;
+ ngx_chain_t **last_out;
+ ngx_chain_t *busy;
+ ngx_chain_t *free;
+
+ ngx_uint_t state;
+ ngx_uint_t saved_state;
+ size_t saved;
+ size_t looked;
+
+ size_t value_len;
+
+ ngx_list_t *variables;
+ ngx_array_t *blocks;
+
+#if (NGX_PCRE)
+ ngx_uint_t ncaptures;
+ int *captures;
+ u_char *captures_data;
+#endif
+
+ unsigned conditional:2;
+ unsigned encoding:2;
+ unsigned block:1;
+ unsigned output:1;
+ unsigned output_chosen:1;
+
+ ngx_http_request_t *wait;
+ void *value_buf;
+ ngx_str_t timefmt;
+ ngx_str_t errmsg;
+} ngx_http_ssi_ctx_t;
+
+
+typedef ngx_int_t (*ngx_http_ssi_command_pt) (ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ctx, ngx_str_t **);
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_uint_t index;
+
+ unsigned mandatory:1;
+ unsigned multiple:1;
+} ngx_http_ssi_param_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_http_ssi_command_pt handler;
+ ngx_http_ssi_param_t *params;
+
+ unsigned conditional:2;
+ unsigned block:1;
+ unsigned flush:1;
+} ngx_http_ssi_command_t;
+
+
+extern ngx_module_t ngx_http_ssi_filter_module;
+
+
+#endif /* _NGX_HTTP_SSI_FILTER_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssl_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssl_module.c
new file mode 100644
index 00000000000..4c69091d60a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssl_module.c
@@ -0,0 +1,962 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
+ ngx_pool_t *pool, ngx_str_t *s);
+
+
+#define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5"
+#define NGX_DEFAULT_ECDH_CURVE "prime256v1"
+
+#define NGX_HTTP_NPN_ADVERTISE "\x08http/1.1"
+
+
+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+static int ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn,
+ const unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen, void *arg);
+#endif
+
+#ifdef TLSEXT_TYPE_next_proto_neg
+static int ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn,
+ const unsigned char **out, unsigned int *outlen, void *arg);
+#endif
+
+static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_ssl_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_ssl_add_variables(ngx_conf_t *cf);
+static void *ngx_http_ssl_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+
+static char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+static ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);
+
+
+static ngx_conf_bitmask_t ngx_http_ssl_protocols[] = {
+ { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
+ { ngx_string("SSLv3"), NGX_SSL_SSLv3 },
+ { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
+ { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
+ { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_conf_enum_t ngx_http_ssl_verify[] = {
+ { ngx_string("off"), 0 },
+ { ngx_string("on"), 1 },
+ { ngx_string("optional"), 2 },
+ { ngx_string("optional_no_ca"), 3 },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_command_t ngx_http_ssl_commands[] = {
+
+ { ngx_string("ssl"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_http_ssl_enable,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, enable),
+ NULL },
+
+ { ngx_string("ssl_certificate"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, certificate),
+ NULL },
+
+ { ngx_string("ssl_certificate_key"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, certificate_key),
+ NULL },
+
+ { ngx_string("ssl_password_file"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_http_ssl_password_file,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("ssl_dhparam"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, dhparam),
+ NULL },
+
+ { ngx_string("ssl_ecdh_curve"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, ecdh_curve),
+ NULL },
+
+ { ngx_string("ssl_protocols"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, protocols),
+ &ngx_http_ssl_protocols },
+
+ { ngx_string("ssl_ciphers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, ciphers),
+ NULL },
+
+ { ngx_string("ssl_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, buffer_size),
+ NULL },
+
+ { ngx_string("ssl_verify_client"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, verify),
+ &ngx_http_ssl_verify },
+
+ { ngx_string("ssl_verify_depth"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, verify_depth),
+ NULL },
+
+ { ngx_string("ssl_client_certificate"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, client_certificate),
+ NULL },
+
+ { ngx_string("ssl_trusted_certificate"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, trusted_certificate),
+ NULL },
+
+ { ngx_string("ssl_prefer_server_ciphers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, prefer_server_ciphers),
+ NULL },
+
+ { ngx_string("ssl_session_cache"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12,
+ ngx_http_ssl_session_cache,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("ssl_session_tickets"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, session_tickets),
+ NULL },
+
+ { ngx_string("ssl_session_ticket_key"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, session_ticket_keys),
+ NULL },
+
+ { ngx_string("ssl_session_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_sec_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, session_timeout),
+ NULL },
+
+ { ngx_string("ssl_crl"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, crl),
+ NULL },
+
+ { ngx_string("ssl_stapling"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, stapling),
+ NULL },
+
+ { ngx_string("ssl_stapling_file"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, stapling_file),
+ NULL },
+
+ { ngx_string("ssl_stapling_responder"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, stapling_responder),
+ NULL },
+
+ { ngx_string("ssl_stapling_verify"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_ssl_srv_conf_t, stapling_verify),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_ssl_module_ctx = {
+ ngx_http_ssl_add_variables, /* preconfiguration */
+ ngx_http_ssl_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_http_ssl_create_srv_conf, /* create server configuration */
+ ngx_http_ssl_merge_srv_conf, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_ssl_module = {
+ NGX_MODULE_V1,
+ &ngx_http_ssl_module_ctx, /* module context */
+ ngx_http_ssl_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_variable_t ngx_http_ssl_vars[] = {
+
+ { ngx_string("ssl_protocol"), NULL, ngx_http_ssl_static_variable,
+ (uintptr_t) ngx_ssl_get_protocol, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+ { ngx_string("ssl_cipher"), NULL, ngx_http_ssl_static_variable,
+ (uintptr_t) ngx_ssl_get_cipher_name, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+ { ngx_string("ssl_session_id"), NULL, ngx_http_ssl_variable,
+ (uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+ { ngx_string("ssl_session_reused"), NULL, ngx_http_ssl_variable,
+ (uintptr_t) ngx_ssl_get_session_reused, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+ { ngx_string("ssl_server_name"), NULL, ngx_http_ssl_variable,
+ (uintptr_t) ngx_ssl_get_server_name, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+ { ngx_string("ssl_client_cert"), NULL, ngx_http_ssl_variable,
+ (uintptr_t) ngx_ssl_get_certificate, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+ { ngx_string("ssl_client_raw_cert"), NULL, ngx_http_ssl_variable,
+ (uintptr_t) ngx_ssl_get_raw_certificate,
+ NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+ { ngx_string("ssl_client_s_dn"), NULL, ngx_http_ssl_variable,
+ (uintptr_t) ngx_ssl_get_subject_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+ { ngx_string("ssl_client_i_dn"), NULL, ngx_http_ssl_variable,
+ (uintptr_t) ngx_ssl_get_issuer_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+ { ngx_string("ssl_client_serial"), NULL, ngx_http_ssl_variable,
+ (uintptr_t) ngx_ssl_get_serial_number, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+ { ngx_string("ssl_client_fingerprint"), NULL, ngx_http_ssl_variable,
+ (uintptr_t) ngx_ssl_get_fingerprint, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+ { ngx_string("ssl_client_verify"), NULL, ngx_http_ssl_variable,
+ (uintptr_t) ngx_ssl_get_client_verify, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
+ { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
+
+
+static ngx_str_t ngx_http_ssl_sess_id_ctx = ngx_string("HTTP");
+
+
+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+
+static int
+ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out,
+ unsigned char *outlen, const unsigned char *in, unsigned int inlen,
+ void *arg)
+{
+ unsigned int srvlen;
+ unsigned char *srv;
+#if (NGX_DEBUG)
+ unsigned int i;
+#endif
+#if (NGX_HTTP_SPDY)
+ ngx_http_connection_t *hc;
+#endif
+#if (NGX_HTTP_SPDY || NGX_DEBUG)
+ ngx_connection_t *c;
+
+ c = ngx_ssl_get_connection(ssl_conn);
+#endif
+
+#if (NGX_DEBUG)
+ for (i = 0; i < inlen; i += in[i] + 1) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "SSL ALPN supported by client: %*s", in[i], &in[i + 1]);
+ }
+#endif
+
+#if (NGX_HTTP_SPDY)
+ hc = c->data;
+
+ if (hc->addr_conf->spdy) {
+ srv = (unsigned char *) NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;
+ srvlen = sizeof(NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;
+
+ } else
+#endif
+ {
+ srv = (unsigned char *) NGX_HTTP_NPN_ADVERTISE;
+ srvlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1;
+ }
+
+ if (SSL_select_next_proto((unsigned char **) out, outlen, srv, srvlen,
+ in, inlen)
+ != OPENSSL_NPN_NEGOTIATED)
+ {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "SSL ALPN selected: %*s", *outlen, *out);
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+#endif
+
+
+#ifdef TLSEXT_TYPE_next_proto_neg
+
+static int
+ngx_http_ssl_npn_advertised(ngx_ssl_conn_t *ssl_conn,
+ const unsigned char **out, unsigned int *outlen, void *arg)
+{
+#if (NGX_HTTP_SPDY || NGX_DEBUG)
+ ngx_connection_t *c;
+
+ c = ngx_ssl_get_connection(ssl_conn);
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "SSL NPN advertised");
+#endif
+
+#if (NGX_HTTP_SPDY)
+ {
+ ngx_http_connection_t *hc;
+
+ hc = c->data;
+
+ if (hc->addr_conf->spdy) {
+ *out = (unsigned char *) NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE;
+ *outlen = sizeof(NGX_SPDY_NPN_ADVERTISE NGX_HTTP_NPN_ADVERTISE) - 1;
+
+ return SSL_TLSEXT_ERR_OK;
+ }
+ }
+#endif
+
+ *out = (unsigned char *) NGX_HTTP_NPN_ADVERTISE;
+ *outlen = sizeof(NGX_HTTP_NPN_ADVERTISE) - 1;
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_ssl_static_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_ssl_variable_handler_pt handler = (ngx_ssl_variable_handler_pt) data;
+
+ size_t len;
+ ngx_str_t s;
+
+ if (r->connection->ssl) {
+
+ (void) handler(r->connection, NULL, &s);
+
+ v->data = s.data;
+
+ for (len = 0; v->data[len]; len++) { /* void */ }
+
+ v->len = len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+ }
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_ssl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_ssl_variable_handler_pt handler = (ngx_ssl_variable_handler_pt) data;
+
+ ngx_str_t s;
+
+ if (r->connection->ssl) {
+
+ if (handler(r->connection, r->pool, &s) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ v->len = s.len;
+ v->data = s.data;
+
+ if (v->len) {
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+ }
+ }
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_ssl_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var, *v;
+
+ for (v = ngx_http_ssl_vars; v->name.len; v++) {
+ var = ngx_http_add_variable(cf, &v->name, v->flags);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = v->get_handler;
+ var->data = v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_http_ssl_srv_conf_t *sscf;
+
+ sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t));
+ if (sscf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * sscf->protocols = 0;
+ * sscf->certificate = { 0, NULL };
+ * sscf->certificate_key = { 0, NULL };
+ * sscf->dhparam = { 0, NULL };
+ * sscf->ecdh_curve = { 0, NULL };
+ * sscf->client_certificate = { 0, NULL };
+ * sscf->trusted_certificate = { 0, NULL };
+ * sscf->crl = { 0, NULL };
+ * sscf->ciphers = { 0, NULL };
+ * sscf->shm_zone = NULL;
+ * sscf->stapling_file = { 0, NULL };
+ * sscf->stapling_responder = { 0, NULL };
+ */
+
+ sscf->enable = NGX_CONF_UNSET;
+ sscf->prefer_server_ciphers = NGX_CONF_UNSET;
+ sscf->buffer_size = NGX_CONF_UNSET_SIZE;
+ sscf->verify = NGX_CONF_UNSET_UINT;
+ sscf->verify_depth = NGX_CONF_UNSET_UINT;
+ sscf->passwords = NGX_CONF_UNSET_PTR;
+ sscf->builtin_session_cache = NGX_CONF_UNSET;
+ sscf->session_timeout = NGX_CONF_UNSET;
+ sscf->session_tickets = NGX_CONF_UNSET;
+ sscf->session_ticket_keys = NGX_CONF_UNSET_PTR;
+ sscf->stapling = NGX_CONF_UNSET;
+ sscf->stapling_verify = NGX_CONF_UNSET;
+
+ return sscf;
+}
+
+
+static char *
+ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_ssl_srv_conf_t *prev = parent;
+ ngx_http_ssl_srv_conf_t *conf = child;
+
+ ngx_pool_cleanup_t *cln;
+
+ if (conf->enable == NGX_CONF_UNSET) {
+ if (prev->enable == NGX_CONF_UNSET) {
+ conf->enable = 0;
+
+ } else {
+ conf->enable = prev->enable;
+ conf->file = prev->file;
+ conf->line = prev->line;
+ }
+ }
+
+ ngx_conf_merge_value(conf->session_timeout,
+ prev->session_timeout, 300);
+
+ ngx_conf_merge_value(conf->prefer_server_ciphers,
+ prev->prefer_server_ciphers, 0);
+
+ ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
+ (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1
+ |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
+
+ ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
+ NGX_SSL_BUFSIZE);
+
+ ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);
+ ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);
+
+ ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
+ ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");
+
+ ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
+
+ ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
+
+ ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
+ "");
+ ngx_conf_merge_str_value(conf->trusted_certificate,
+ prev->trusted_certificate, "");
+ ngx_conf_merge_str_value(conf->crl, prev->crl, "");
+
+ ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
+ NGX_DEFAULT_ECDH_CURVE);
+
+ ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
+
+ ngx_conf_merge_value(conf->stapling, prev->stapling, 0);
+ ngx_conf_merge_value(conf->stapling_verify, prev->stapling_verify, 0);
+ ngx_conf_merge_str_value(conf->stapling_file, prev->stapling_file, "");
+ ngx_conf_merge_str_value(conf->stapling_responder,
+ prev->stapling_responder, "");
+
+ conf->ssl.log = cf->log;
+
+ if (conf->enable) {
+
+ if (conf->certificate.len == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"ssl_certificate\" is defined for "
+ "the \"ssl\" directive in %s:%ui",
+ conf->file, conf->line);
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->certificate_key.len == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"ssl_certificate_key\" is defined for "
+ "the \"ssl\" directive in %s:%ui",
+ conf->file, conf->line);
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+
+ if (conf->certificate.len == 0) {
+ return NGX_CONF_OK;
+ }
+
+ if (conf->certificate_key.len == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"ssl_certificate_key\" is defined "
+ "for certificate \"%V\"", &conf->certificate);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+
+ if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
+ ngx_http_ssl_servername)
+ == 0)
+ {
+ ngx_log_error(NGX_LOG_WARN, cf->log, 0,
+ "nginx was built with SNI support, however, now it is linked "
+ "dynamically to an OpenSSL library which has no tlsext support, "
+ "therefore SNI is not available");
+ }
+
+#endif
+
+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+ SSL_CTX_set_alpn_select_cb(conf->ssl.ctx, ngx_http_ssl_alpn_select, NULL);
+#endif
+
+#ifdef TLSEXT_TYPE_next_proto_neg
+ SSL_CTX_set_next_protos_advertised_cb(conf->ssl.ctx,
+ ngx_http_ssl_npn_advertised, NULL);
+#endif
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ cln->handler = ngx_ssl_cleanup_ctx;
+ cln->data = &conf->ssl;
+
+ if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
+ &conf->certificate_key, conf->passwords)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (SSL_CTX_set_cipher_list(conf->ssl.ctx,
+ (const char *) conf->ciphers.data)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
+ "SSL_CTX_set_cipher_list(\"%V\") failed",
+ &conf->ciphers);
+ return NGX_CONF_ERROR;
+ }
+
+ conf->ssl.buffer_size = conf->buffer_size;
+
+ if (conf->verify) {
+
+ if (conf->client_certificate.len == 0 && conf->verify != 3) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no ssl_client_certificate for ssl_client_verify");
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_client_certificate(cf, &conf->ssl,
+ &conf->client_certificate,
+ conf->verify_depth)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (ngx_ssl_trusted_certificate(cf, &conf->ssl,
+ &conf->trusted_certificate,
+ conf->verify_depth)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->prefer_server_ciphers) {
+ SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+ }
+
+ /* a temporary 512-bit RSA key is required for export versions of MSIE */
+ SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback);
+
+ if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_value(conf->builtin_session_cache,
+ prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);
+
+ if (conf->shm_zone == NULL) {
+ conf->shm_zone = prev->shm_zone;
+ }
+
+ if (ngx_ssl_session_cache(&conf->ssl, &ngx_http_ssl_sess_id_ctx,
+ conf->builtin_session_cache,
+ conf->shm_zone, conf->session_timeout)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_value(conf->session_tickets, prev->session_tickets, 1);
+
+#ifdef SSL_OP_NO_TICKET
+ if (!conf->session_tickets) {
+ SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET);
+ }
+#endif
+
+ ngx_conf_merge_ptr_value(conf->session_ticket_keys,
+ prev->session_ticket_keys, NULL);
+
+ if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->stapling) {
+
+ if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file,
+ &conf->stapling_responder, conf->stapling_verify)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_ssl_srv_conf_t *sscf = conf;
+
+ char *rv;
+
+ rv = ngx_conf_set_flag_slot(cf, cmd, conf);
+
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+
+ sscf->file = cf->conf_file->file.name.data;
+ sscf->line = cf->conf_file->line;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_ssl_srv_conf_t *sscf = conf;
+
+ ngx_str_t *value;
+
+ if (sscf->passwords != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ sscf->passwords = ngx_ssl_read_password_file(cf, &value[1]);
+
+ if (sscf->passwords == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_ssl_srv_conf_t *sscf = conf;
+
+ size_t len;
+ ngx_str_t *value, name, size;
+ ngx_int_t n;
+ ngx_uint_t i, j;
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "off") == 0) {
+ sscf->builtin_session_cache = NGX_SSL_NO_SCACHE;
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "none") == 0) {
+ sscf->builtin_session_cache = NGX_SSL_NONE_SCACHE;
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "builtin") == 0) {
+ sscf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;
+ continue;
+ }
+
+ if (value[i].len > sizeof("builtin:") - 1
+ && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)
+ == 0)
+ {
+ n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,
+ value[i].len - (sizeof("builtin:") - 1));
+
+ if (n == NGX_ERROR) {
+ goto invalid;
+ }
+
+ sscf->builtin_session_cache = n;
+
+ continue;
+ }
+
+ if (value[i].len > sizeof("shared:") - 1
+ && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)
+ == 0)
+ {
+ len = 0;
+
+ for (j = sizeof("shared:") - 1; j < value[i].len; j++) {
+ if (value[i].data[j] == ':') {
+ break;
+ }
+
+ len++;
+ }
+
+ if (len == 0) {
+ goto invalid;
+ }
+
+ name.len = len;
+ name.data = value[i].data + sizeof("shared:") - 1;
+
+ size.len = value[i].len - j - 1;
+ size.data = name.data + len + 1;
+
+ n = ngx_parse_size(&size);
+
+ if (n == NGX_ERROR) {
+ goto invalid;
+ }
+
+ if (n < (ngx_int_t) (8 * ngx_pagesize)) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "session cache \"%V\" is too small",
+ &value[i]);
+
+ return NGX_CONF_ERROR;
+ }
+
+ sscf->shm_zone = ngx_shared_memory_add(cf, &name, n,
+ &ngx_http_ssl_module);
+ if (sscf->shm_zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ sscf->shm_zone->init = ngx_ssl_session_cache_init;
+
+ continue;
+ }
+
+ goto invalid;
+ }
+
+ if (sscf->shm_zone && sscf->builtin_session_cache == NGX_CONF_UNSET) {
+ sscf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;
+ }
+
+ return NGX_CONF_OK;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid session cache \"%V\"", &value[i]);
+
+ return NGX_CONF_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_ssl_init(ngx_conf_t *cf)
+{
+ ngx_uint_t s;
+ ngx_http_ssl_srv_conf_t *sscf;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_srv_conf_t **cscfp;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+ cscfp = cmcf->servers.elts;
+
+ for (s = 0; s < cmcf->servers.nelts; s++) {
+
+ sscf = cscfp[s]->ctx->srv_conf[ngx_http_ssl_module.ctx_index];
+
+ if (sscf->ssl.ctx == NULL || !sscf->stapling) {
+ continue;
+ }
+
+ clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
+
+ if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver,
+ clcf->resolver_timeout)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssl_module.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssl_module.h
new file mode 100644
index 00000000000..8e69e9e4d0d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_ssl_module.h
@@ -0,0 +1,65 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_SSL_H_INCLUDED_
+#define _NGX_HTTP_SSL_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_flag_t enable;
+
+ ngx_ssl_t ssl;
+
+ ngx_flag_t prefer_server_ciphers;
+
+ ngx_uint_t protocols;
+
+ ngx_uint_t verify;
+ ngx_uint_t verify_depth;
+
+ size_t buffer_size;
+
+ ssize_t builtin_session_cache;
+
+ time_t session_timeout;
+
+ ngx_str_t certificate;
+ ngx_str_t certificate_key;
+ ngx_str_t dhparam;
+ ngx_str_t ecdh_curve;
+ ngx_str_t client_certificate;
+ ngx_str_t trusted_certificate;
+ ngx_str_t crl;
+
+ ngx_str_t ciphers;
+
+ ngx_array_t *passwords;
+
+ ngx_shm_zone_t *shm_zone;
+
+ ngx_flag_t session_tickets;
+ ngx_array_t *session_ticket_keys;
+
+ ngx_flag_t stapling;
+ ngx_flag_t stapling_verify;
+ ngx_str_t stapling_file;
+ ngx_str_t stapling_responder;
+
+ u_char *file;
+ ngx_uint_t line;
+} ngx_http_ssl_srv_conf_t;
+
+
+extern ngx_module_t ngx_http_ssl_module;
+
+
+#endif /* _NGX_HTTP_SSL_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_static_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_static_module.c
new file mode 100644
index 00000000000..631eb17b267
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_static_module.c
@@ -0,0 +1,290 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static ngx_int_t ngx_http_static_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_static_init(ngx_conf_t *cf);
+
+
+ngx_http_module_t ngx_http_static_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_static_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_static_module = {
+ NGX_MODULE_V1,
+ &ngx_http_static_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_static_handler(ngx_http_request_t *r)
+{
+ u_char *last, *location;
+ size_t root, len;
+ ngx_str_t path;
+ ngx_int_t rc;
+ ngx_uint_t level;
+ ngx_log_t *log;
+ ngx_buf_t *b;
+ ngx_chain_t out;
+ ngx_open_file_info_t of;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
+ return NGX_HTTP_NOT_ALLOWED;
+ }
+
+ if (r->uri.data[r->uri.len - 1] == '/') {
+ return NGX_DECLINED;
+ }
+
+ log = r->connection->log;
+
+ /*
+ * ngx_http_map_uri_to_path() allocates memory for terminating '\0'
+ * so we do not need to reserve memory for '/' for possible redirect
+ */
+
+ last = ngx_http_map_uri_to_path(r, &path, &root, 0);
+ if (last == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ path.len = last - path.data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
+ "http filename: \"%s\"", path.data);
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+ of.read_ahead = clcf->read_ahead;
+ of.directio = clcf->directio;
+ of.valid = clcf->open_file_cache_valid;
+ of.min_uses = clcf->open_file_cache_min_uses;
+ of.errors = clcf->open_file_cache_errors;
+ of.events = clcf->open_file_cache_events;
+
+ if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+ != NGX_OK)
+ {
+ switch (of.err) {
+
+ case 0:
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+
+ case NGX_ENOENT:
+ case NGX_ENOTDIR:
+ case NGX_ENAMETOOLONG:
+
+ level = NGX_LOG_ERR;
+ rc = NGX_HTTP_NOT_FOUND;
+ break;
+
+ case NGX_EACCES:
+#if (NGX_HAVE_OPENAT)
+ case NGX_EMLINK:
+ case NGX_ELOOP:
+#endif
+
+ level = NGX_LOG_ERR;
+ rc = NGX_HTTP_FORBIDDEN;
+ break;
+
+ default:
+
+ level = NGX_LOG_CRIT;
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ break;
+ }
+
+ if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
+ ngx_log_error(level, log, of.err,
+ "%s \"%s\" failed", of.failed, path.data);
+ }
+
+ return rc;
+ }
+
+ r->root_tested = !r->error_page;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);
+
+ if (of.is_dir) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");
+
+ ngx_http_clear_location(r);
+
+ r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));
+ if (r->headers_out.location == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ len = r->uri.len + 1;
+
+ if (!clcf->alias && clcf->root_lengths == NULL && r->args.len == 0) {
+ location = path.data + clcf->root.len;
+
+ *last = '/';
+
+ } else {
+ if (r->args.len) {
+ len += r->args.len + 1;
+ }
+
+ location = ngx_pnalloc(r->pool, len);
+ if (location == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ last = ngx_copy(location, r->uri.data, r->uri.len);
+
+ *last = '/';
+
+ if (r->args.len) {
+ *++last = '?';
+ ngx_memcpy(++last, r->args.data, r->args.len);
+ }
+ }
+
+ /*
+ * we do not need to set the r->headers_out.location->hash and
+ * r->headers_out.location->key fields
+ */
+
+ r->headers_out.location->value.len = len;
+ r->headers_out.location->value.data = location;
+
+ return NGX_HTTP_MOVED_PERMANENTLY;
+ }
+
+#if !(NGX_WIN32) /* the not regular files are probably Unix specific */
+
+ if (!of.is_file) {
+ ngx_log_error(NGX_LOG_CRIT, log, 0,
+ "\"%s\" is not a regular file", path.data);
+
+ return NGX_HTTP_NOT_FOUND;
+ }
+
+#endif
+
+ if (r->method & NGX_HTTP_POST) {
+ return NGX_HTTP_NOT_ALLOWED;
+ }
+
+ rc = ngx_http_discard_request_body(r);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ log->action = "sending response to client";
+
+ r->headers_out.status = NGX_HTTP_OK;
+ r->headers_out.content_length_n = of.size;
+ r->headers_out.last_modified_time = of.mtime;
+
+ if (ngx_http_set_etag(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (ngx_http_set_content_type(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (r != r->main && of.size == 0) {
+ return ngx_http_send_header(r);
+ }
+
+ r->allow_ranges = 1;
+
+ /* we need to allocate all before the header would be sent */
+
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
+ if (b->file == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ return rc;
+ }
+
+ b->file_pos = 0;
+ b->file_last = of.size;
+
+ b->in_file = b->file_last ? 1: 0;
+ b->last_buf = (r == r->main) ? 1: 0;
+ b->last_in_chain = 1;
+
+ b->file->fd = of.fd;
+ b->file->name = path;
+ b->file->log = log;
+ b->file->directio = of.is_directio;
+
+ out.buf = b;
+ out.next = NULL;
+
+ return ngx_http_output_filter(r, &out);
+}
+
+
+static ngx_int_t
+ngx_http_static_init(ngx_conf_t *cf)
+{
+ ngx_http_handler_pt *h;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ *h = ngx_http_static_handler;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_stub_status_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_stub_status_module.c
new file mode 100644
index 00000000000..f4f5888b6f2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_stub_status_module.c
@@ -0,0 +1,236 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static ngx_int_t ngx_http_stub_status_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_stub_status_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_stub_status_add_variables(ngx_conf_t *cf);
+static char *ngx_http_set_stub_status(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_http_status_commands[] = {
+
+ { ngx_string("stub_status"),
+ NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_http_set_stub_status,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_stub_status_module_ctx = {
+ ngx_http_stub_status_add_variables, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_stub_status_module = {
+ NGX_MODULE_V1,
+ &ngx_http_stub_status_module_ctx, /* module context */
+ ngx_http_status_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_variable_t ngx_http_stub_status_vars[] = {
+
+ { ngx_string("connections_active"), NULL, ngx_http_stub_status_variable,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("connections_reading"), NULL, ngx_http_stub_status_variable,
+ 1, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("connections_writing"), NULL, ngx_http_stub_status_variable,
+ 2, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("connections_waiting"), NULL, ngx_http_stub_status_variable,
+ 3, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
+
+
+static ngx_int_t
+ngx_http_stub_status_handler(ngx_http_request_t *r)
+{
+ size_t size;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t out;
+ ngx_atomic_int_t ap, hn, ac, rq, rd, wr, wa;
+
+ if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) {
+ return NGX_HTTP_NOT_ALLOWED;
+ }
+
+ rc = ngx_http_discard_request_body(r);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ r->headers_out.content_type_len = sizeof("text/plain") - 1;
+ ngx_str_set(&r->headers_out.content_type, "text/plain");
+ r->headers_out.content_type_lowcase = NULL;
+
+ if (r->method == NGX_HTTP_HEAD) {
+ r->headers_out.status = NGX_HTTP_OK;
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ return rc;
+ }
+ }
+
+ size = sizeof("Active connections: \n") + NGX_ATOMIC_T_LEN
+ + sizeof("server accepts handled requests\n") - 1
+ + 6 + 3 * NGX_ATOMIC_T_LEN
+ + sizeof("Reading: Writing: Waiting: \n") + 3 * NGX_ATOMIC_T_LEN;
+
+ b = ngx_create_temp_buf(r->pool, size);
+ if (b == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ out.buf = b;
+ out.next = NULL;
+
+ ap = *ngx_stat_accepted;
+ hn = *ngx_stat_handled;
+ ac = *ngx_stat_active;
+ rq = *ngx_stat_requests;
+ rd = *ngx_stat_reading;
+ wr = *ngx_stat_writing;
+ wa = *ngx_stat_waiting;
+
+ b->last = ngx_sprintf(b->last, "Active connections: %uA \n", ac);
+
+ b->last = ngx_cpymem(b->last, "server accepts handled requests\n",
+ sizeof("server accepts handled requests\n") - 1);
+
+ b->last = ngx_sprintf(b->last, " %uA %uA %uA \n", ap, hn, rq);
+
+ b->last = ngx_sprintf(b->last, "Reading: %uA Writing: %uA Waiting: %uA \n",
+ rd, wr, wa);
+
+ r->headers_out.status = NGX_HTTP_OK;
+ r->headers_out.content_length_n = b->last - b->pos;
+
+ b->last_buf = (r == r->main) ? 1 : 0;
+ b->last_in_chain = 1;
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ return rc;
+ }
+
+ return ngx_http_output_filter(r, &out);
+}
+
+
+static ngx_int_t
+ngx_http_stub_status_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ ngx_atomic_int_t value;
+
+ p = ngx_pnalloc(r->pool, NGX_ATOMIC_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ switch (data) {
+ case 0:
+ value = *ngx_stat_active;
+ break;
+
+ case 1:
+ value = *ngx_stat_reading;
+ break;
+
+ case 2:
+ value = *ngx_stat_writing;
+ break;
+
+ case 3:
+ value = *ngx_stat_waiting;
+ break;
+
+ /* suppress warning */
+ default:
+ value = 0;
+ break;
+ }
+
+ v->len = ngx_sprintf(p, "%uA", value) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_stub_status_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var, *v;
+
+ for (v = ngx_http_stub_status_vars; v->name.len; v++) {
+ var = ngx_http_add_variable(cf, &v->name, v->flags);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = v->get_handler;
+ var->data = v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_http_set_stub_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+ clcf->handler = ngx_http_stub_status_handler;
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_sub_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_sub_filter_module.c
new file mode 100644
index 00000000000..5e6e038bfe9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_sub_filter_module.c
@@ -0,0 +1,756 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_str_t match;
+ ngx_http_complex_value_t value;
+
+ ngx_hash_t types;
+
+ ngx_flag_t once;
+ ngx_flag_t last_modified;
+
+ ngx_array_t *types_keys;
+} ngx_http_sub_loc_conf_t;
+
+
+typedef enum {
+ sub_start_state = 0,
+ sub_match_state,
+} ngx_http_sub_state_e;
+
+
+typedef struct {
+ ngx_str_t match;
+ ngx_str_t saved;
+ ngx_str_t looked;
+
+ ngx_uint_t once; /* unsigned once:1 */
+
+ ngx_buf_t *buf;
+
+ u_char *pos;
+ u_char *copy_start;
+ u_char *copy_end;
+
+ ngx_chain_t *in;
+ ngx_chain_t *out;
+ ngx_chain_t **last_out;
+ ngx_chain_t *busy;
+ ngx_chain_t *free;
+
+ ngx_str_t sub;
+
+ ngx_uint_t state;
+} ngx_http_sub_ctx_t;
+
+
+static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r,
+ ngx_http_sub_ctx_t *ctx);
+static ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r,
+ ngx_http_sub_ctx_t *ctx);
+
+static char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static void *ngx_http_sub_create_conf(ngx_conf_t *cf);
+static char *ngx_http_sub_merge_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static ngx_int_t ngx_http_sub_filter_init(ngx_conf_t *cf);
+
+
+static ngx_command_t ngx_http_sub_filter_commands[] = {
+
+ { ngx_string("sub_filter"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_http_sub_filter,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("sub_filter_types"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_types_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_sub_loc_conf_t, types_keys),
+ &ngx_http_html_default_types[0] },
+
+ { ngx_string("sub_filter_once"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_sub_loc_conf_t, once),
+ NULL },
+
+ { ngx_string("sub_filter_last_modified"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_sub_loc_conf_t, last_modified),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_sub_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_sub_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_sub_create_conf, /* create location configuration */
+ ngx_http_sub_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_sub_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_sub_filter_module_ctx, /* module context */
+ ngx_http_sub_filter_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_sub_header_filter(ngx_http_request_t *r)
+{
+ ngx_http_sub_ctx_t *ctx;
+ ngx_http_sub_loc_conf_t *slcf;
+
+ slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
+
+ if (slcf->match.len == 0
+ || r->headers_out.content_length_n == 0
+ || ngx_http_test_content_type(r, &slcf->types) == NULL)
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->saved.data = ngx_pnalloc(r->pool, slcf->match.len);
+ if (ctx->saved.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx->looked.data = ngx_pnalloc(r->pool, slcf->match.len);
+ if (ctx->looked.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module);
+
+ ctx->match = slcf->match;
+ ctx->last_out = &ctx->out;
+
+ r->filter_need_in_memory = 1;
+
+ if (r == r->main) {
+ ngx_http_clear_content_length(r);
+
+ if (!slcf->last_modified) {
+ ngx_http_clear_last_modified(r);
+ ngx_http_clear_etag(r);
+
+ } else {
+ ngx_http_weak_etag(r);
+ }
+ }
+
+ return ngx_http_next_header_filter(r);
+}
+
+
+static ngx_int_t
+ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+ ngx_http_sub_ctx_t *ctx;
+ ngx_http_sub_loc_conf_t *slcf;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module);
+
+ if (ctx == NULL) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ if ((in == NULL
+ && ctx->buf == NULL
+ && ctx->in == NULL
+ && ctx->busy == NULL))
+ {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ if (ctx->once && (ctx->buf == NULL || ctx->in == NULL)) {
+
+ if (ctx->busy) {
+ if (ngx_http_sub_output(r, ctx) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ /* add the incoming chain to the chain ctx->in */
+
+ if (in) {
+ if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http sub filter \"%V\"", &r->uri);
+
+ while (ctx->in || ctx->buf) {
+
+ if (ctx->buf == NULL) {
+ ctx->buf = ctx->in->buf;
+ ctx->in = ctx->in->next;
+ ctx->pos = ctx->buf->pos;
+ }
+
+ if (ctx->state == sub_start_state) {
+ ctx->copy_start = ctx->pos;
+ ctx->copy_end = ctx->pos;
+ }
+
+ b = NULL;
+
+ while (ctx->pos < ctx->buf->last) {
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "saved: \"%V\" state: %d", &ctx->saved, ctx->state);
+
+ rc = ngx_http_sub_parse(r, ctx);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "parse: %d, looked: \"%V\" %p-%p",
+ rc, &ctx->looked, ctx->copy_start, ctx->copy_end);
+
+ if (rc == NGX_ERROR) {
+ return rc;
+ }
+
+ if (ctx->saved.len) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "saved: \"%V\"", &ctx->saved);
+
+ cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = cl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->pos = ngx_pnalloc(r->pool, ctx->saved.len);
+ if (b->pos == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(b->pos, ctx->saved.data, ctx->saved.len);
+ b->last = b->pos + ctx->saved.len;
+ b->memory = 1;
+
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ ctx->saved.len = 0;
+ }
+
+ if (ctx->copy_start != ctx->copy_end) {
+
+ cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = cl->buf;
+
+ ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));
+
+ b->pos = ctx->copy_start;
+ b->last = ctx->copy_end;
+ b->shadow = NULL;
+ b->last_buf = 0;
+ b->last_in_chain = 0;
+ b->recycled = 0;
+
+ if (b->in_file) {
+ b->file_last = b->file_pos + (b->last - ctx->buf->pos);
+ b->file_pos += b->pos - ctx->buf->pos;
+ }
+
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+ }
+
+ if (ctx->state == sub_start_state) {
+ ctx->copy_start = ctx->pos;
+ ctx->copy_end = ctx->pos;
+
+ } else {
+ ctx->copy_start = NULL;
+ ctx->copy_end = NULL;
+ }
+
+ if (ctx->looked.len > (size_t) (ctx->pos - ctx->buf->pos)) {
+ ctx->saved.len = ctx->looked.len - (ctx->pos - ctx->buf->pos);
+ ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len);
+ }
+
+ if (rc == NGX_AGAIN) {
+ continue;
+ }
+
+
+ /* rc == NGX_OK */
+
+ cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = cl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);
+
+ if (ctx->sub.data == NULL) {
+
+ if (ngx_http_complex_value(r, &slcf->value, &ctx->sub)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ if (ctx->sub.len) {
+ b->memory = 1;
+ b->pos = ctx->sub.data;
+ b->last = ctx->sub.data + ctx->sub.len;
+
+ } else {
+ b->sync = 1;
+ }
+
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ ctx->once = slcf->once;
+
+ continue;
+ }
+
+ if (ctx->looked.len
+ && (ctx->buf->last_buf || ctx->buf->last_in_chain))
+ {
+ cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = cl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->pos = ctx->looked.data;
+ b->last = b->pos + ctx->looked.len;
+ b->memory = 1;
+
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+
+ ctx->looked.len = 0;
+ }
+
+ if (ctx->buf->last_buf || ctx->buf->flush || ctx->buf->sync
+ || ngx_buf_in_memory(ctx->buf))
+ {
+ if (b == NULL) {
+ cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = cl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->sync = 1;
+
+ *ctx->last_out = cl;
+ ctx->last_out = &cl->next;
+ }
+
+ b->last_buf = ctx->buf->last_buf;
+ b->last_in_chain = ctx->buf->last_in_chain;
+ b->flush = ctx->buf->flush;
+ b->shadow = ctx->buf;
+
+ b->recycled = ctx->buf->recycled;
+ }
+
+ ctx->buf = NULL;
+
+ ctx->saved.len = ctx->looked.len;
+ ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->looked.len);
+ }
+
+ if (ctx->out == NULL && ctx->busy == NULL) {
+ return NGX_OK;
+ }
+
+ return ngx_http_sub_output(r, ctx);
+}
+
+
+static ngx_int_t
+ngx_http_sub_output(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)
+{
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl;
+
+#if 1
+ b = NULL;
+ for (cl = ctx->out; cl; cl = cl->next) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "sub out: %p %p", cl->buf, cl->buf->pos);
+ if (cl->buf == b) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "the same buf was used in sub");
+ ngx_debug_point();
+ return NGX_ERROR;
+ }
+ b = cl->buf;
+ }
+#endif
+
+ rc = ngx_http_next_body_filter(r, ctx->out);
+
+ if (ctx->busy == NULL) {
+ ctx->busy = ctx->out;
+
+ } else {
+ for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
+ cl->next = ctx->out;
+ }
+
+ ctx->out = NULL;
+ ctx->last_out = &ctx->out;
+
+ while (ctx->busy) {
+
+ cl = ctx->busy;
+ b = cl->buf;
+
+ if (ngx_buf_size(b) != 0) {
+ break;
+ }
+
+ if (b->shadow) {
+ b->shadow->pos = b->shadow->last;
+ }
+
+ ctx->busy = cl->next;
+
+ if (ngx_buf_in_memory(b) || b->in_file) {
+ /* add data bufs only to the free buf chain */
+
+ cl->next = ctx->free;
+ ctx->free = cl;
+ }
+ }
+
+ if (ctx->in || ctx->buf) {
+ r->buffered |= NGX_HTTP_SUB_BUFFERED;
+
+ } else {
+ r->buffered &= ~NGX_HTTP_SUB_BUFFERED;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)
+{
+ u_char *p, *last, *copy_end, ch, match;
+ size_t looked, i;
+ ngx_http_sub_state_e state;
+
+ if (ctx->once) {
+ ctx->copy_start = ctx->pos;
+ ctx->copy_end = ctx->buf->last;
+ ctx->pos = ctx->buf->last;
+ ctx->looked.len = 0;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "once");
+
+ return NGX_AGAIN;
+ }
+
+ state = ctx->state;
+ looked = ctx->looked.len;
+ last = ctx->buf->last;
+ copy_end = ctx->copy_end;
+
+ for (p = ctx->pos; p < last; p++) {
+
+ ch = *p;
+ ch = ngx_tolower(ch);
+
+ if (state == sub_start_state) {
+
+ /* the tight loop */
+
+ match = ctx->match.data[0];
+
+ for ( ;; ) {
+ if (ch == match) {
+ copy_end = p;
+ ctx->looked.data[0] = *p;
+ looked = 1;
+ state = sub_match_state;
+
+ goto match_started;
+ }
+
+ if (++p == last) {
+ break;
+ }
+
+ ch = *p;
+ ch = ngx_tolower(ch);
+ }
+
+ ctx->state = state;
+ ctx->pos = p;
+ ctx->looked.len = looked;
+ ctx->copy_end = p;
+
+ if (ctx->copy_start == NULL) {
+ ctx->copy_start = ctx->buf->pos;
+ }
+
+ return NGX_AGAIN;
+
+ match_started:
+
+ continue;
+ }
+
+ /* state == sub_match_state */
+
+ if (ch == ctx->match.data[looked]) {
+ ctx->looked.data[looked] = *p;
+ looked++;
+
+ if (looked == ctx->match.len) {
+
+ ctx->state = sub_start_state;
+ ctx->pos = p + 1;
+ ctx->looked.len = 0;
+ ctx->saved.len = 0;
+ ctx->copy_end = copy_end;
+
+ if (ctx->copy_start == NULL && copy_end) {
+ ctx->copy_start = ctx->buf->pos;
+ }
+
+ return NGX_OK;
+ }
+
+ } else {
+ /*
+ * check if there is another partial match in previously
+ * matched substring to catch cases like "aab" in "aaab"
+ */
+
+ ctx->looked.data[looked] = *p;
+ looked++;
+
+ for (i = 1; i < looked; i++) {
+ if (ngx_strncasecmp(ctx->looked.data + i,
+ ctx->match.data, looked - i)
+ == 0)
+ {
+ break;
+ }
+ }
+
+ if (i < looked) {
+ if (ctx->saved.len > i) {
+ ctx->saved.len = i;
+ }
+
+ if ((size_t) (p + 1 - ctx->buf->pos) >= looked - i) {
+ copy_end = p + 1 - (looked - i);
+ }
+
+ ngx_memmove(ctx->looked.data, ctx->looked.data + i, looked - i);
+ looked = looked - i;
+
+ } else {
+ copy_end = p;
+ looked = 0;
+ state = sub_start_state;
+ }
+
+ if (ctx->saved.len) {
+ p++;
+ goto out;
+ }
+ }
+ }
+
+ ctx->saved.len = 0;
+
+out:
+
+ ctx->state = state;
+ ctx->pos = p;
+ ctx->looked.len = looked;
+
+ ctx->copy_end = (state == sub_start_state) ? p : copy_end;
+
+ if (ctx->copy_start == NULL && ctx->copy_end) {
+ ctx->copy_start = ctx->buf->pos;
+ }
+
+ return NGX_AGAIN;
+}
+
+
+static char *
+ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_sub_loc_conf_t *slcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_compile_complex_value_t ccv;
+
+ if (slcf->match.data) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ ngx_strlow(value[1].data, value[1].data, value[1].len);
+
+ slcf->match = value[1];
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[2];
+ ccv.complex_value = &slcf->value;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_sub_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_sub_loc_conf_t *slcf;
+
+ slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sub_loc_conf_t));
+ if (slcf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->match = { 0, NULL };
+ * conf->types = { NULL };
+ * conf->types_keys = NULL;
+ */
+
+ slcf->once = NGX_CONF_UNSET;
+ slcf->last_modified = NGX_CONF_UNSET;
+
+ return slcf;
+}
+
+
+static char *
+ngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_sub_loc_conf_t *prev = parent;
+ ngx_http_sub_loc_conf_t *conf = child;
+
+ ngx_conf_merge_value(conf->once, prev->once, 1);
+ ngx_conf_merge_str_value(conf->match, prev->match, "");
+ ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);
+
+ if (conf->value.value.data == NULL) {
+ conf->value = prev->value;
+ }
+
+ if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
+ &prev->types_keys, &prev->types,
+ ngx_http_html_default_types)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_sub_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_sub_header_filter;
+
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_sub_body_filter;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_hash_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_hash_module.c
new file mode 100644
index 00000000000..777e180a59f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_hash_module.c
@@ -0,0 +1,631 @@
+
+/*
+ * Copyright (C) Roman Arutyunyan
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ uint32_t hash;
+ ngx_str_t *server;
+} ngx_http_upstream_chash_point_t;
+
+
+typedef struct {
+ ngx_uint_t number;
+ ngx_http_upstream_chash_point_t point[1];
+} ngx_http_upstream_chash_points_t;
+
+
+typedef struct {
+ ngx_http_complex_value_t key;
+ ngx_http_upstream_chash_points_t *points;
+} ngx_http_upstream_hash_srv_conf_t;
+
+
+typedef struct {
+ /* the round robin data must be first */
+ ngx_http_upstream_rr_peer_data_t rrp;
+ ngx_http_upstream_hash_srv_conf_t *conf;
+ ngx_str_t key;
+ ngx_uint_t tries;
+ ngx_uint_t rehash;
+ uint32_t hash;
+ ngx_event_get_peer_pt get_rr_peer;
+} ngx_http_upstream_hash_peer_data_t;
+
+
+static ngx_int_t ngx_http_upstream_init_hash(ngx_conf_t *cf,
+ ngx_http_upstream_srv_conf_t *us);
+static ngx_int_t ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us);
+static ngx_int_t ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc,
+ void *data);
+
+static ngx_int_t ngx_http_upstream_init_chash(ngx_conf_t *cf,
+ ngx_http_upstream_srv_conf_t *us);
+static void ngx_http_upstream_add_chash_point(
+ ngx_http_upstream_chash_points_t *points, uint32_t hash, ngx_str_t *server);
+static ngx_uint_t ngx_http_upstream_find_chash_point(
+ ngx_http_upstream_chash_points_t *points, uint32_t hash);
+static ngx_int_t ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us);
+static ngx_int_t ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc,
+ void *data);
+
+static void *ngx_http_upstream_hash_create_conf(ngx_conf_t *cf);
+static char *ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_http_upstream_hash_commands[] = {
+
+ { ngx_string("hash"),
+ NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12,
+ ngx_http_upstream_hash,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_upstream_hash_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_http_upstream_hash_create_conf, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_upstream_hash_module = {
+ NGX_MODULE_V1,
+ &ngx_http_upstream_hash_module_ctx, /* module context */
+ ngx_http_upstream_hash_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_upstream_init_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
+{
+ if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ us->peer.init = ngx_http_upstream_init_hash_peer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ ngx_http_upstream_hash_srv_conf_t *hcf;
+ ngx_http_upstream_hash_peer_data_t *hp;
+
+ hp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_hash_peer_data_t));
+ if (hp == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->upstream->peer.data = &hp->rrp;
+
+ if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ r->upstream->peer.get = ngx_http_upstream_get_hash_peer;
+
+ hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);
+
+ if (ngx_http_complex_value(r, &hcf->key, &hp->key) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "upstream hash key:\"%V\"", &hp->key);
+
+ hp->conf = hcf;
+ hp->tries = 0;
+ hp->rehash = 0;
+ hp->hash = 0;
+ hp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_hash_peer_data_t *hp = data;
+
+ time_t now;
+ u_char buf[NGX_INT_T_LEN];
+ size_t size;
+ uint32_t hash;
+ ngx_int_t w;
+ uintptr_t m;
+ ngx_uint_t i, n, p;
+ ngx_http_upstream_rr_peer_t *peer;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get hash peer, try: %ui", pc->tries);
+
+ if (hp->tries > 20 || hp->rrp.peers->single) {
+ return hp->get_rr_peer(pc, &hp->rrp);
+ }
+
+ now = ngx_time();
+
+ pc->cached = 0;
+ pc->connection = NULL;
+
+ for ( ;; ) {
+
+ /*
+ * Hash expression is compatible with Cache::Memcached:
+ * ((crc32([REHASH] KEY) >> 16) & 0x7fff) + PREV_HASH
+ * with REHASH omitted at the first iteration.
+ */
+
+ ngx_crc32_init(hash);
+
+ if (hp->rehash > 0) {
+ size = ngx_sprintf(buf, "%ui", hp->rehash) - buf;
+ ngx_crc32_update(&hash, buf, size);
+ }
+
+ ngx_crc32_update(&hash, hp->key.data, hp->key.len);
+ ngx_crc32_final(hash);
+
+ hash = (hash >> 16) & 0x7fff;
+
+ hp->hash += hash;
+ hp->rehash++;
+
+ if (!hp->rrp.peers->weighted) {
+ p = hp->hash % hp->rrp.peers->number;
+
+ } else {
+ w = hp->hash % hp->rrp.peers->total_weight;
+
+ for (i = 0; i < hp->rrp.peers->number; i++) {
+ w -= hp->rrp.peers->peer[i].weight;
+ if (w < 0) {
+ break;
+ }
+ }
+
+ p = i;
+ }
+
+ n = p / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
+
+ if (hp->rrp.tried[n] & m) {
+ goto next;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get hash peer, value:%uD, peer:%ui", hp->hash, p);
+
+ peer = &hp->rrp.peers->peer[p];
+
+ if (peer->down) {
+ goto next;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ goto next;
+ }
+
+ break;
+
+ next:
+
+ if (++hp->tries > 20) {
+ return hp->get_rr_peer(pc, &hp->rrp);
+ }
+ }
+
+ hp->rrp.current = p;
+
+ pc->sockaddr = peer->sockaddr;
+ pc->socklen = peer->socklen;
+ pc->name = &peer->name;
+
+ if (now - peer->checked > peer->fail_timeout) {
+ peer->checked = now;
+ }
+
+ hp->rrp.tried[n] |= m;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
+{
+ u_char *host, *port, c;
+ size_t host_len, port_len, size;
+ uint32_t hash, base_hash, prev_hash;
+ ngx_str_t *server;
+ ngx_uint_t npoints, i, j;
+ ngx_http_upstream_rr_peer_t *peer;
+ ngx_http_upstream_rr_peers_t *peers;
+ ngx_http_upstream_chash_points_t *points;
+ ngx_http_upstream_hash_srv_conf_t *hcf;
+
+ if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ us->peer.init = ngx_http_upstream_init_chash_peer;
+
+ peers = us->peer.data;
+ npoints = peers->total_weight * 160;
+
+ size = sizeof(ngx_http_upstream_chash_points_t)
+ + sizeof(ngx_http_upstream_chash_point_t) * (npoints - 1);
+
+ points = ngx_palloc(cf->pool, size);
+ if (points == NULL) {
+ return NGX_ERROR;
+ }
+
+ points->number = 0;
+
+ for (i = 0; i < peers->number; i++) {
+ peer = &peers->peer[i];
+ server = &peer->server;
+
+ /*
+ * Hash expression is compatible with Cache::Memcached::Fast:
+ * crc32(HOST \0 PORT PREV_HASH).
+ */
+
+ if (server->len >= 5
+ && ngx_strncasecmp(server->data, (u_char *) "unix:", 5) == 0)
+ {
+ host = server->data + 5;
+ host_len = server->len - 5;
+ port = NULL;
+ port_len = 0;
+ goto done;
+ }
+
+ for (j = 0; j < server->len; j++) {
+ c = server->data[server->len - j - 1];
+
+ if (c == ':') {
+ host = server->data;
+ host_len = server->len - j - 1;
+ port = server->data + server->len - j;
+ port_len = j;
+ goto done;
+ }
+
+ if (c < '0' || c > '9') {
+ break;
+ }
+ }
+
+ host = server->data;
+ host_len = server->len;
+ port = NULL;
+ port_len = 0;
+
+ done:
+
+ ngx_crc32_init(base_hash);
+ ngx_crc32_update(&base_hash, host, host_len);
+ ngx_crc32_update(&base_hash, (u_char *) "", 1);
+ ngx_crc32_update(&base_hash, port, port_len);
+
+ prev_hash = 0;
+ npoints = peer->weight * 160;
+
+ for (j = 0; j < npoints; j++) {
+ hash = base_hash;
+
+ ngx_crc32_update(&hash, (u_char *) &prev_hash, sizeof(uint32_t));
+ ngx_crc32_final(hash);
+
+ ngx_http_upstream_add_chash_point(points, hash, &peer->server);
+
+ prev_hash = hash;
+ }
+ }
+
+ hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);
+ hcf->points = points;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_add_chash_point(ngx_http_upstream_chash_points_t *points,
+ uint32_t hash, ngx_str_t *server)
+{
+ size_t size;
+ ngx_uint_t i;
+ ngx_http_upstream_chash_point_t *point;
+
+ i = ngx_http_upstream_find_chash_point(points, hash);
+ point = &points->point[i];
+
+ if (point->hash == hash) {
+ return;
+ }
+
+ size = (points->number - i) * sizeof(ngx_http_upstream_chash_point_t);
+
+ ngx_memmove(point + 1, point, size);
+
+ points->number++;
+ point->hash = hash;
+ point->server = server;
+}
+
+
+static ngx_uint_t
+ngx_http_upstream_find_chash_point(ngx_http_upstream_chash_points_t *points,
+ uint32_t hash)
+{
+ ngx_uint_t i, j, k;
+ ngx_http_upstream_chash_point_t *point;
+
+ /* find first point >= hash */
+
+ point = &points->point[0];
+
+ i = 0;
+ j = points->number;
+
+ while (i < j) {
+ k = (i + j) / 2;
+
+ if (hash > point[k].hash) {
+ i = k + 1;
+
+ } else if (hash < point[k].hash) {
+ j = k;
+
+ } else {
+ return k;
+ }
+ }
+
+ return i;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ uint32_t hash;
+ ngx_http_upstream_hash_srv_conf_t *hcf;
+ ngx_http_upstream_hash_peer_data_t *hp;
+
+ if (ngx_http_upstream_init_hash_peer(r, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ r->upstream->peer.get = ngx_http_upstream_get_chash_peer;
+
+ hp = r->upstream->peer.data;
+ hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);
+
+ hash = ngx_crc32_long(hp->key.data, hp->key.len);
+ hp->hash = ngx_http_upstream_find_chash_point(hcf->points, hash);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_hash_peer_data_t *hp = data;
+
+ time_t now;
+ intptr_t m;
+ ngx_str_t *server;
+ ngx_int_t total;
+ ngx_uint_t i, n;
+ ngx_http_upstream_rr_peer_t *peer, *best;
+ ngx_http_upstream_chash_point_t *point;
+ ngx_http_upstream_chash_points_t *points;
+ ngx_http_upstream_hash_srv_conf_t *hcf;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get consistent hash peer, try: %ui", pc->tries);
+
+ pc->cached = 0;
+ pc->connection = NULL;
+
+ now = ngx_time();
+ hcf = hp->conf;
+
+ points = hcf->points;
+ point = &points->point[0];
+
+ for ( ;; ) {
+ server = point[hp->hash % points->number].server;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "consistent hash peer:%uD, server:\"%V\"",
+ hp->hash, server);
+
+ best = NULL;
+ total = 0;
+
+ for (i = 0; i < hp->rrp.peers->number; i++) {
+
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ if (hp->rrp.tried[n] & m) {
+ continue;
+ }
+
+ peer = &hp->rrp.peers->peer[i];
+
+ if (peer->down) {
+ continue;
+ }
+
+ if (peer->server.len != server->len
+ || ngx_strncmp(peer->server.data, server->data, server->len)
+ != 0)
+ {
+ continue;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ continue;
+ }
+
+ peer->current_weight += peer->effective_weight;
+ total += peer->effective_weight;
+
+ if (peer->effective_weight < peer->weight) {
+ peer->effective_weight++;
+ }
+
+ if (best == NULL || peer->current_weight > best->current_weight) {
+ best = peer;
+ }
+ }
+
+ if (best) {
+ best->current_weight -= total;
+
+ i = best - &hp->rrp.peers->peer[0];
+
+ hp->rrp.current = i;
+
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ hp->rrp.tried[n] |= m;
+
+ if (now - best->checked > best->fail_timeout) {
+ best->checked = now;
+ }
+
+ pc->sockaddr = best->sockaddr;
+ pc->socklen = best->socklen;
+ pc->name = &best->name;
+
+ return NGX_OK;
+ }
+
+ hp->hash++;
+ hp->tries++;
+
+ if (hp->tries >= points->number) {
+ return NGX_BUSY;
+ }
+ }
+}
+
+
+static void *
+ngx_http_upstream_hash_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_upstream_hash_srv_conf_t *conf;
+
+ conf = ngx_palloc(cf->pool, sizeof(ngx_http_upstream_hash_srv_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->points = NULL;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_upstream_hash_srv_conf_t *hcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_upstream_srv_conf_t *uscf;
+ ngx_http_compile_complex_value_t ccv;
+
+ value = cf->args->elts;
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &hcf->key;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
+
+ if (uscf->peer.init_upstream) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "load balancing method redefined");
+ }
+
+ uscf->flags = NGX_HTTP_UPSTREAM_CREATE
+ |NGX_HTTP_UPSTREAM_WEIGHT
+ |NGX_HTTP_UPSTREAM_MAX_FAILS
+ |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
+ |NGX_HTTP_UPSTREAM_DOWN;
+
+ if (cf->args->nelts == 2) {
+ uscf->peer.init_upstream = ngx_http_upstream_init_hash;
+
+ } else if (ngx_strcmp(value[2].data, "consistent") == 0) {
+ uscf->peer.init_upstream = ngx_http_upstream_init_chash;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_ip_hash_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_ip_hash_module.c
new file mode 100644
index 00000000000..148d73a8472
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_ip_hash_module.c
@@ -0,0 +1,279 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ /* the round robin data must be first */
+ ngx_http_upstream_rr_peer_data_t rrp;
+
+ ngx_uint_t hash;
+
+ u_char addrlen;
+ u_char *addr;
+
+ u_char tries;
+
+ ngx_event_get_peer_pt get_rr_peer;
+} ngx_http_upstream_ip_hash_peer_data_t;
+
+
+static ngx_int_t ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us);
+static ngx_int_t ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc,
+ void *data);
+static char *ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_http_upstream_ip_hash_commands[] = {
+
+ { ngx_string("ip_hash"),
+ NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,
+ ngx_http_upstream_ip_hash,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_upstream_ip_hash_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_upstream_ip_hash_module = {
+ NGX_MODULE_V1,
+ &ngx_http_upstream_ip_hash_module_ctx, /* module context */
+ ngx_http_upstream_ip_hash_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static u_char ngx_http_upstream_ip_hash_pseudo_addr[3];
+
+
+static ngx_int_t
+ngx_http_upstream_init_ip_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
+{
+ if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ us->peer.init = ngx_http_upstream_init_ip_hash_peer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+ ngx_http_upstream_ip_hash_peer_data_t *iphp;
+
+ iphp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_ip_hash_peer_data_t));
+ if (iphp == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->upstream->peer.data = &iphp->rrp;
+
+ if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;
+
+ switch (r->connection->sockaddr->sa_family) {
+
+ case AF_INET:
+ sin = (struct sockaddr_in *) r->connection->sockaddr;
+ iphp->addr = (u_char *) &sin->sin_addr.s_addr;
+ iphp->addrlen = 3;
+ break;
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
+ iphp->addr = (u_char *) &sin6->sin6_addr.s6_addr;
+ iphp->addrlen = 16;
+ break;
+#endif
+
+ default:
+ iphp->addr = ngx_http_upstream_ip_hash_pseudo_addr;
+ iphp->addrlen = 3;
+ }
+
+ iphp->hash = 89;
+ iphp->tries = 0;
+ iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_ip_hash_peer_data_t *iphp = data;
+
+ time_t now;
+ ngx_int_t w;
+ uintptr_t m;
+ ngx_uint_t i, n, p, hash;
+ ngx_http_upstream_rr_peer_t *peer;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get ip hash peer, try: %ui", pc->tries);
+
+ /* TODO: cached */
+
+ if (iphp->tries > 20 || iphp->rrp.peers->single) {
+ return iphp->get_rr_peer(pc, &iphp->rrp);
+ }
+
+ now = ngx_time();
+
+ pc->cached = 0;
+ pc->connection = NULL;
+
+ hash = iphp->hash;
+
+ for ( ;; ) {
+
+ for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) {
+ hash = (hash * 113 + iphp->addr[i]) % 6271;
+ }
+
+ if (!iphp->rrp.peers->weighted) {
+ p = hash % iphp->rrp.peers->number;
+
+ } else {
+ w = hash % iphp->rrp.peers->total_weight;
+
+ for (i = 0; i < iphp->rrp.peers->number; i++) {
+ w -= iphp->rrp.peers->peer[i].weight;
+ if (w < 0) {
+ break;
+ }
+ }
+
+ p = i;
+ }
+
+ n = p / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
+
+ if (iphp->rrp.tried[n] & m) {
+ goto next;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get ip hash peer, hash: %ui %04XA", p, m);
+
+ peer = &iphp->rrp.peers->peer[p];
+
+ /* ngx_lock_mutex(iphp->rrp.peers->mutex); */
+
+ if (peer->down) {
+ goto next_try;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ goto next_try;
+ }
+
+ break;
+
+ next_try:
+
+ iphp->rrp.tried[n] |= m;
+
+ /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
+
+ pc->tries--;
+
+ next:
+
+ if (++iphp->tries > 20) {
+ return iphp->get_rr_peer(pc, &iphp->rrp);
+ }
+ }
+
+ iphp->rrp.current = p;
+
+ pc->sockaddr = peer->sockaddr;
+ pc->socklen = peer->socklen;
+ pc->name = &peer->name;
+
+ if (now - peer->checked > peer->fail_timeout) {
+ peer->checked = now;
+ }
+
+ /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
+
+ iphp->rrp.tried[n] |= m;
+ iphp->hash = hash;
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_upstream_srv_conf_t *uscf;
+
+ uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
+
+ if (uscf->peer.init_upstream) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "load balancing method redefined");
+ }
+
+ uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash;
+
+ uscf->flags = NGX_HTTP_UPSTREAM_CREATE
+ |NGX_HTTP_UPSTREAM_WEIGHT
+ |NGX_HTTP_UPSTREAM_MAX_FAILS
+ |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
+ |NGX_HTTP_UPSTREAM_DOWN;
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_keepalive_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_keepalive_module.c
new file mode 100644
index 00000000000..d07ed9edaf2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_keepalive_module.c
@@ -0,0 +1,536 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_uint_t max_cached;
+
+ ngx_queue_t cache;
+ ngx_queue_t free;
+
+ ngx_http_upstream_init_pt original_init_upstream;
+ ngx_http_upstream_init_peer_pt original_init_peer;
+
+} ngx_http_upstream_keepalive_srv_conf_t;
+
+
+typedef struct {
+ ngx_http_upstream_keepalive_srv_conf_t *conf;
+
+ ngx_http_upstream_t *upstream;
+
+ void *data;
+
+ ngx_event_get_peer_pt original_get_peer;
+ ngx_event_free_peer_pt original_free_peer;
+
+#if (NGX_HTTP_SSL)
+ ngx_event_set_peer_session_pt original_set_session;
+ ngx_event_save_peer_session_pt original_save_session;
+#endif
+
+} ngx_http_upstream_keepalive_peer_data_t;
+
+
+typedef struct {
+ ngx_http_upstream_keepalive_srv_conf_t *conf;
+
+ ngx_queue_t queue;
+ ngx_connection_t *connection;
+
+ socklen_t socklen;
+ u_char sockaddr[NGX_SOCKADDRLEN];
+
+} ngx_http_upstream_keepalive_cache_t;
+
+
+static ngx_int_t ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us);
+static ngx_int_t ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc,
+ void *data);
+static void ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc,
+ void *data, ngx_uint_t state);
+
+static void ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev);
+static void ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev);
+static void ngx_http_upstream_keepalive_close(ngx_connection_t *c);
+
+
+#if (NGX_HTTP_SSL)
+static ngx_int_t ngx_http_upstream_keepalive_set_session(
+ ngx_peer_connection_t *pc, void *data);
+static void ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc,
+ void *data);
+#endif
+
+static void *ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf);
+static char *ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_http_upstream_keepalive_commands[] = {
+
+ { ngx_string("keepalive"),
+ NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12,
+ ngx_http_upstream_keepalive,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_upstream_keepalive_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_http_upstream_keepalive_create_conf, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_upstream_keepalive_module = {
+ NGX_MODULE_V1,
+ &ngx_http_upstream_keepalive_module_ctx, /* module context */
+ ngx_http_upstream_keepalive_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_upstream_init_keepalive(ngx_conf_t *cf,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ ngx_uint_t i;
+ ngx_http_upstream_keepalive_srv_conf_t *kcf;
+ ngx_http_upstream_keepalive_cache_t *cached;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,
+ "init keepalive");
+
+ kcf = ngx_http_conf_upstream_srv_conf(us,
+ ngx_http_upstream_keepalive_module);
+
+ if (kcf->original_init_upstream(cf, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ kcf->original_init_peer = us->peer.init;
+
+ us->peer.init = ngx_http_upstream_init_keepalive_peer;
+
+ /* allocate cache items and add to free queue */
+
+ cached = ngx_pcalloc(cf->pool,
+ sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached);
+ if (cached == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_queue_init(&kcf->cache);
+ ngx_queue_init(&kcf->free);
+
+ for (i = 0; i < kcf->max_cached; i++) {
+ ngx_queue_insert_head(&kcf->free, &cached[i].queue);
+ cached[i].conf = kcf;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ ngx_http_upstream_keepalive_peer_data_t *kp;
+ ngx_http_upstream_keepalive_srv_conf_t *kcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "init keepalive peer");
+
+ kcf = ngx_http_conf_upstream_srv_conf(us,
+ ngx_http_upstream_keepalive_module);
+
+ kp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_keepalive_peer_data_t));
+ if (kp == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (kcf->original_init_peer(r, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ kp->conf = kcf;
+ kp->upstream = r->upstream;
+ kp->data = r->upstream->peer.data;
+ kp->original_get_peer = r->upstream->peer.get;
+ kp->original_free_peer = r->upstream->peer.free;
+
+ r->upstream->peer.data = kp;
+ r->upstream->peer.get = ngx_http_upstream_get_keepalive_peer;
+ r->upstream->peer.free = ngx_http_upstream_free_keepalive_peer;
+
+#if (NGX_HTTP_SSL)
+ kp->original_set_session = r->upstream->peer.set_session;
+ kp->original_save_session = r->upstream->peer.save_session;
+ r->upstream->peer.set_session = ngx_http_upstream_keepalive_set_session;
+ r->upstream->peer.save_session = ngx_http_upstream_keepalive_save_session;
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_keepalive_peer_data_t *kp = data;
+ ngx_http_upstream_keepalive_cache_t *item;
+
+ ngx_int_t rc;
+ ngx_queue_t *q, *cache;
+ ngx_connection_t *c;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get keepalive peer");
+
+ /* ask balancer */
+
+ rc = kp->original_get_peer(pc, kp->data);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ /* search cache for suitable connection */
+
+ cache = &kp->conf->cache;
+
+ for (q = ngx_queue_head(cache);
+ q != ngx_queue_sentinel(cache);
+ q = ngx_queue_next(q))
+ {
+ item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
+ c = item->connection;
+
+ if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,
+ item->socklen, pc->socklen)
+ == 0)
+ {
+ ngx_queue_remove(q);
+ ngx_queue_insert_head(&kp->conf->free, q);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get keepalive peer: using connection %p", c);
+
+ c->idle = 0;
+ c->log = pc->log;
+ c->read->log = pc->log;
+ c->write->log = pc->log;
+ c->pool->log = pc->log;
+
+ pc->connection = c;
+ pc->cached = 1;
+
+ return NGX_DONE;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data,
+ ngx_uint_t state)
+{
+ ngx_http_upstream_keepalive_peer_data_t *kp = data;
+ ngx_http_upstream_keepalive_cache_t *item;
+
+ ngx_queue_t *q;
+ ngx_connection_t *c;
+ ngx_http_upstream_t *u;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "free keepalive peer");
+
+ /* cache valid connections */
+
+ u = kp->upstream;
+ c = pc->connection;
+
+ if (state & NGX_PEER_FAILED
+ || c == NULL
+ || c->read->eof
+ || c->read->error
+ || c->read->timedout
+ || c->write->error
+ || c->write->timedout)
+ {
+ goto invalid;
+ }
+
+ if (!u->keepalive) {
+ goto invalid;
+ }
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ goto invalid;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "free keepalive peer: saving connection %p", c);
+
+ if (ngx_queue_empty(&kp->conf->free)) {
+
+ q = ngx_queue_last(&kp->conf->cache);
+ ngx_queue_remove(q);
+
+ item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
+
+ ngx_http_upstream_keepalive_close(item->connection);
+
+ } else {
+ q = ngx_queue_head(&kp->conf->free);
+ ngx_queue_remove(q);
+
+ item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);
+ }
+
+ item->connection = c;
+ ngx_queue_insert_head(&kp->conf->cache, q);
+
+ pc->connection = NULL;
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ c->write->handler = ngx_http_upstream_keepalive_dummy_handler;
+ c->read->handler = ngx_http_upstream_keepalive_close_handler;
+
+ c->data = item;
+ c->idle = 1;
+ c->log = ngx_cycle->log;
+ c->read->log = ngx_cycle->log;
+ c->write->log = ngx_cycle->log;
+ c->pool->log = ngx_cycle->log;
+
+ item->socklen = pc->socklen;
+ ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);
+
+ if (c->read->ready) {
+ ngx_http_upstream_keepalive_close_handler(c->read);
+ }
+
+invalid:
+
+ kp->original_free_peer(pc, kp->data, state);
+}
+
+
+static void
+ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
+ "keepalive dummy handler");
+}
+
+
+static void
+ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev)
+{
+ ngx_http_upstream_keepalive_srv_conf_t *conf;
+ ngx_http_upstream_keepalive_cache_t *item;
+
+ int n;
+ char buf[1];
+ ngx_connection_t *c;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,
+ "keepalive close handler");
+
+ c = ev->data;
+
+ if (c->close) {
+ goto close;
+ }
+
+ n = recv(c->fd, buf, 1, MSG_PEEK);
+
+ if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {
+ /* stale event */
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ goto close;
+ }
+
+ return;
+ }
+
+close:
+
+ item = c->data;
+ conf = item->conf;
+
+ ngx_http_upstream_keepalive_close(c);
+
+ ngx_queue_remove(&item->queue);
+ ngx_queue_insert_head(&conf->free, &item->queue);
+}
+
+
+static void
+ngx_http_upstream_keepalive_close(ngx_connection_t *c)
+{
+
+#if (NGX_HTTP_SSL)
+
+ if (c->ssl) {
+ c->ssl->no_wait_shutdown = 1;
+ c->ssl->no_send_shutdown = 1;
+
+ if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+ c->ssl->handler = ngx_http_upstream_keepalive_close;
+ return;
+ }
+ }
+
+#endif
+
+ ngx_destroy_pool(c->pool);
+ ngx_close_connection(c);
+}
+
+
+#if (NGX_HTTP_SSL)
+
+static ngx_int_t
+ngx_http_upstream_keepalive_set_session(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_keepalive_peer_data_t *kp = data;
+
+ return kp->original_set_session(pc, kp->data);
+}
+
+
+static void
+ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_keepalive_peer_data_t *kp = data;
+
+ kp->original_save_session(pc, kp->data);
+ return;
+}
+
+#endif
+
+
+static void *
+ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_upstream_keepalive_srv_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool,
+ sizeof(ngx_http_upstream_keepalive_srv_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->original_init_upstream = NULL;
+ * conf->original_init_peer = NULL;
+ */
+
+ conf->max_cached = 1;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_upstream_srv_conf_t *uscf;
+ ngx_http_upstream_keepalive_srv_conf_t *kcf = conf;
+
+ ngx_int_t n;
+ ngx_str_t *value;
+ ngx_uint_t i;
+
+ uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
+
+ if (kcf->original_init_upstream) {
+ return "is duplicate";
+ }
+
+ kcf->original_init_upstream = uscf->peer.init_upstream
+ ? uscf->peer.init_upstream
+ : ngx_http_upstream_init_round_robin;
+
+ uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;
+
+ /* read options */
+
+ value = cf->args->elts;
+
+ n = ngx_atoi(value[1].data, value[1].len);
+
+ if (n == NGX_ERROR || n == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%V\" in \"%V\" directive",
+ &value[1], &cmd->name);
+ return NGX_CONF_ERROR;
+ }
+
+ kcf->max_cached = n;
+
+ for (i = 2; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "single") == 0) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "the \"single\" parameter is deprecated");
+ continue;
+ }
+
+ goto invalid;
+ }
+
+ return NGX_CONF_OK;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[i]);
+
+ return NGX_CONF_ERROR;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_least_conn_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_least_conn_module.c
new file mode 100644
index 00000000000..dbef95d4164
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_upstream_least_conn_module.c
@@ -0,0 +1,408 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_uint_t *conns;
+} ngx_http_upstream_least_conn_conf_t;
+
+
+typedef struct {
+ /* the round robin data must be first */
+ ngx_http_upstream_rr_peer_data_t rrp;
+
+ ngx_uint_t *conns;
+
+ ngx_event_get_peer_pt get_rr_peer;
+ ngx_event_free_peer_pt free_rr_peer;
+} ngx_http_upstream_lc_peer_data_t;
+
+
+static ngx_int_t ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us);
+static ngx_int_t ngx_http_upstream_get_least_conn_peer(
+ ngx_peer_connection_t *pc, void *data);
+static void ngx_http_upstream_free_least_conn_peer(ngx_peer_connection_t *pc,
+ void *data, ngx_uint_t state);
+static void *ngx_http_upstream_least_conn_create_conf(ngx_conf_t *cf);
+static char *ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_http_upstream_least_conn_commands[] = {
+
+ { ngx_string("least_conn"),
+ NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,
+ ngx_http_upstream_least_conn,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_upstream_least_conn_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_http_upstream_least_conn_create_conf, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_upstream_least_conn_module = {
+ NGX_MODULE_V1,
+ &ngx_http_upstream_least_conn_module_ctx, /* module context */
+ ngx_http_upstream_least_conn_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_upstream_init_least_conn(ngx_conf_t *cf,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ ngx_uint_t n;
+ ngx_http_upstream_rr_peers_t *peers;
+ ngx_http_upstream_least_conn_conf_t *lcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,
+ "init least conn");
+
+ if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ peers = us->peer.data;
+
+ n = peers->number;
+
+ if (peers->next) {
+ n += peers->next->number;
+ }
+
+ lcf = ngx_http_conf_upstream_srv_conf(us,
+ ngx_http_upstream_least_conn_module);
+
+ lcf->conns = ngx_pcalloc(cf->pool, sizeof(ngx_uint_t) * n);
+ if (lcf->conns == NULL) {
+ return NGX_ERROR;
+ }
+
+ us->peer.init = ngx_http_upstream_init_least_conn_peer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ ngx_http_upstream_lc_peer_data_t *lcp;
+ ngx_http_upstream_least_conn_conf_t *lcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "init least conn peer");
+
+ lcf = ngx_http_conf_upstream_srv_conf(us,
+ ngx_http_upstream_least_conn_module);
+
+ lcp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_lc_peer_data_t));
+ if (lcp == NULL) {
+ return NGX_ERROR;
+ }
+
+ lcp->conns = lcf->conns;
+
+ r->upstream->peer.data = &lcp->rrp;
+
+ if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ r->upstream->peer.get = ngx_http_upstream_get_least_conn_peer;
+ r->upstream->peer.free = ngx_http_upstream_free_least_conn_peer;
+
+ lcp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
+ lcp->free_rr_peer = ngx_http_upstream_free_round_robin_peer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_lc_peer_data_t *lcp = data;
+
+ time_t now;
+ uintptr_t m;
+ ngx_int_t rc, total;
+ ngx_uint_t i, n, p, many;
+ ngx_http_upstream_rr_peer_t *peer, *best;
+ ngx_http_upstream_rr_peers_t *peers;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get least conn peer, try: %ui", pc->tries);
+
+ if (lcp->rrp.peers->single) {
+ return lcp->get_rr_peer(pc, &lcp->rrp);
+ }
+
+ pc->cached = 0;
+ pc->connection = NULL;
+
+ now = ngx_time();
+
+ peers = lcp->rrp.peers;
+
+ best = NULL;
+ total = 0;
+
+#if (NGX_SUPPRESS_WARN)
+ many = 0;
+ p = 0;
+#endif
+
+ for (i = 0; i < peers->number; i++) {
+
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ if (lcp->rrp.tried[n] & m) {
+ continue;
+ }
+
+ peer = &peers->peer[i];
+
+ if (peer->down) {
+ continue;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ continue;
+ }
+
+ /*
+ * select peer with least number of connections; if there are
+ * multiple peers with the same number of connections, select
+ * based on round-robin
+ */
+
+ if (best == NULL
+ || lcp->conns[i] * best->weight < lcp->conns[p] * peer->weight)
+ {
+ best = peer;
+ many = 0;
+ p = i;
+
+ } else if (lcp->conns[i] * best->weight
+ == lcp->conns[p] * peer->weight)
+ {
+ many = 1;
+ }
+ }
+
+ if (best == NULL) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get least conn peer, no peer found");
+
+ goto failed;
+ }
+
+ if (many) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get least conn peer, many");
+
+ for (i = p; i < peers->number; i++) {
+
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ if (lcp->rrp.tried[n] & m) {
+ continue;
+ }
+
+ peer = &peers->peer[i];
+
+ if (peer->down) {
+ continue;
+ }
+
+ if (lcp->conns[i] * best->weight != lcp->conns[p] * peer->weight) {
+ continue;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ continue;
+ }
+
+ peer->current_weight += peer->effective_weight;
+ total += peer->effective_weight;
+
+ if (peer->effective_weight < peer->weight) {
+ peer->effective_weight++;
+ }
+
+ if (peer->current_weight > best->current_weight) {
+ best = peer;
+ p = i;
+ }
+ }
+ }
+
+ best->current_weight -= total;
+
+ if (now - best->checked > best->fail_timeout) {
+ best->checked = now;
+ }
+
+ pc->sockaddr = best->sockaddr;
+ pc->socklen = best->socklen;
+ pc->name = &best->name;
+
+ lcp->rrp.current = p;
+
+ n = p / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
+
+ lcp->rrp.tried[n] |= m;
+ lcp->conns[p]++;
+
+ if (pc->tries == 1 && peers->next) {
+ pc->tries += peers->next->number;
+ }
+
+ return NGX_OK;
+
+failed:
+
+ if (peers->next) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get least conn peer, backup servers");
+
+ lcp->conns += peers->number;
+
+ lcp->rrp.peers = peers->next;
+ pc->tries = lcp->rrp.peers->number;
+
+ n = (lcp->rrp.peers->number + (8 * sizeof(uintptr_t) - 1))
+ / (8 * sizeof(uintptr_t));
+
+ for (i = 0; i < n; i++) {
+ lcp->rrp.tried[i] = 0;
+ }
+
+ rc = ngx_http_upstream_get_least_conn_peer(pc, lcp);
+
+ if (rc != NGX_BUSY) {
+ return rc;
+ }
+ }
+
+ /* all peers failed, mark them as live for quick recovery */
+
+ for (i = 0; i < peers->number; i++) {
+ peers->peer[i].fails = 0;
+ }
+
+ pc->name = peers->name;
+
+ return NGX_BUSY;
+}
+
+
+static void
+ngx_http_upstream_free_least_conn_peer(ngx_peer_connection_t *pc,
+ void *data, ngx_uint_t state)
+{
+ ngx_http_upstream_lc_peer_data_t *lcp = data;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "free least conn peer %ui %ui", pc->tries, state);
+
+ if (lcp->rrp.peers->single) {
+ lcp->free_rr_peer(pc, &lcp->rrp, state);
+ return;
+ }
+
+ lcp->conns[lcp->rrp.current]--;
+
+ lcp->free_rr_peer(pc, &lcp->rrp, state);
+}
+
+
+static void *
+ngx_http_upstream_least_conn_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_upstream_least_conn_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool,
+ sizeof(ngx_http_upstream_least_conn_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->conns = NULL;
+ */
+
+ return conf;
+}
+
+
+static char *
+ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_upstream_srv_conf_t *uscf;
+
+ uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
+
+ if (uscf->peer.init_upstream) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "load balancing method redefined");
+ }
+
+ uscf->peer.init_upstream = ngx_http_upstream_init_least_conn;
+
+ uscf->flags = NGX_HTTP_UPSTREAM_CREATE
+ |NGX_HTTP_UPSTREAM_WEIGHT
+ |NGX_HTTP_UPSTREAM_MAX_FAILS
+ |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
+ |NGX_HTTP_UPSTREAM_DOWN
+ |NGX_HTTP_UPSTREAM_BACKUP;
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_userid_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_userid_filter_module.c
new file mode 100644
index 00000000000..1487c091eee
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_userid_filter_module.c
@@ -0,0 +1,842 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_USERID_OFF 0
+#define NGX_HTTP_USERID_LOG 1
+#define NGX_HTTP_USERID_V1 2
+#define NGX_HTTP_USERID_ON 3
+
+/* 31 Dec 2037 23:55:55 GMT */
+#define NGX_HTTP_USERID_MAX_EXPIRES 2145916555
+
+
+typedef struct {
+ ngx_uint_t enable;
+
+ ngx_int_t service;
+
+ ngx_str_t name;
+ ngx_str_t domain;
+ ngx_str_t path;
+ ngx_str_t p3p;
+
+ time_t expires;
+
+ u_char mark;
+} ngx_http_userid_conf_t;
+
+
+typedef struct {
+ uint32_t uid_got[4];
+ uint32_t uid_set[4];
+ ngx_str_t cookie;
+ ngx_uint_t reset;
+} ngx_http_userid_ctx_t;
+
+
+static ngx_http_userid_ctx_t *ngx_http_userid_get_uid(ngx_http_request_t *r,
+ ngx_http_userid_conf_t *conf);
+static ngx_int_t ngx_http_userid_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, ngx_str_t *name, uint32_t *uid);
+static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r,
+ ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
+static ngx_int_t ngx_http_userid_create_uid(ngx_http_request_t *r,
+ ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
+
+static ngx_int_t ngx_http_userid_add_variables(ngx_conf_t *cf);
+static ngx_int_t ngx_http_userid_init(ngx_conf_t *cf);
+static void *ngx_http_userid_create_conf(ngx_conf_t *cf);
+static char *ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_http_userid_domain(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_userid_path(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static ngx_int_t ngx_http_userid_init_worker(ngx_cycle_t *cycle);
+
+
+
+static uint32_t start_value;
+static uint32_t sequencer_v1 = 1;
+static uint32_t sequencer_v2 = 0x03030302;
+
+
+static u_char expires[] = "; expires=Thu, 31-Dec-37 23:55:55 GMT";
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+
+
+static ngx_conf_enum_t ngx_http_userid_state[] = {
+ { ngx_string("off"), NGX_HTTP_USERID_OFF },
+ { ngx_string("log"), NGX_HTTP_USERID_LOG },
+ { ngx_string("v1"), NGX_HTTP_USERID_V1 },
+ { ngx_string("on"), NGX_HTTP_USERID_ON },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_conf_post_handler_pt ngx_http_userid_domain_p =
+ ngx_http_userid_domain;
+static ngx_conf_post_handler_pt ngx_http_userid_path_p = ngx_http_userid_path;
+static ngx_conf_post_handler_pt ngx_http_userid_p3p_p = ngx_http_userid_p3p;
+
+
+static ngx_command_t ngx_http_userid_commands[] = {
+
+ { ngx_string("userid"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_userid_conf_t, enable),
+ ngx_http_userid_state },
+
+ { ngx_string("userid_service"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_userid_conf_t, service),
+ NULL },
+
+ { ngx_string("userid_name"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_userid_conf_t, name),
+ NULL },
+
+ { ngx_string("userid_domain"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_userid_conf_t, domain),
+ &ngx_http_userid_domain_p },
+
+ { ngx_string("userid_path"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_userid_conf_t, path),
+ &ngx_http_userid_path_p },
+
+ { ngx_string("userid_expires"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_userid_expires,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("userid_p3p"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_userid_conf_t, p3p),
+ &ngx_http_userid_p3p_p },
+
+ { ngx_string("userid_mark"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_userid_mark,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_userid_filter_module_ctx = {
+ ngx_http_userid_add_variables, /* preconfiguration */
+ ngx_http_userid_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_userid_create_conf, /* create location configuration */
+ ngx_http_userid_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_userid_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_userid_filter_module_ctx, /* module context */
+ ngx_http_userid_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ ngx_http_userid_init_worker, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t ngx_http_userid_got = ngx_string("uid_got");
+static ngx_str_t ngx_http_userid_set = ngx_string("uid_set");
+static ngx_str_t ngx_http_userid_reset = ngx_string("uid_reset");
+static ngx_uint_t ngx_http_userid_reset_index;
+
+
+static ngx_int_t
+ngx_http_userid_filter(ngx_http_request_t *r)
+{
+ ngx_http_userid_ctx_t *ctx;
+ ngx_http_userid_conf_t *conf;
+
+ if (r != r->main) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
+
+ if (conf->enable < NGX_HTTP_USERID_V1) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ctx = ngx_http_userid_get_uid(r, conf);
+
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_http_userid_set_uid(r, ctx, conf) == NGX_OK) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_userid_got_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_userid_ctx_t *ctx;
+ ngx_http_userid_conf_t *conf;
+
+ conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);
+
+ if (conf->enable == NGX_HTTP_USERID_OFF) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ ctx = ngx_http_userid_get_uid(r->main, conf);
+
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ctx->uid_got[3] != 0) {
+ return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_got);
+ }
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_userid_set_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_userid_ctx_t *ctx;
+ ngx_http_userid_conf_t *conf;
+
+ conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);
+
+ if (conf->enable < NGX_HTTP_USERID_V1) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ ctx = ngx_http_userid_get_uid(r->main, conf);
+
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_http_userid_create_uid(r->main, ctx, conf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ctx->uid_set[3] == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_set);
+}
+
+
+static ngx_http_userid_ctx_t *
+ngx_http_userid_get_uid(ngx_http_request_t *r, ngx_http_userid_conf_t *conf)
+{
+ ngx_int_t n;
+ ngx_str_t src, dst;
+ ngx_table_elt_t **cookies;
+ ngx_http_userid_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
+
+ if (ctx) {
+ return ctx;
+ }
+
+ if (ctx == NULL) {
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_userid_ctx_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module);
+ }
+
+ n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &conf->name,
+ &ctx->cookie);
+ if (n == NGX_DECLINED) {
+ return ctx;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "uid cookie: \"%V\"", &ctx->cookie);
+
+ if (ctx->cookie.len < 22) {
+ cookies = r->headers_in.cookies.elts;
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client sent too short userid cookie \"%V\"",
+ &cookies[n]->value);
+ return ctx;
+ }
+
+ src = ctx->cookie;
+
+ /*
+ * we have to limit the encoded string to 22 characters because
+ * 1) cookie may be marked by "userid_mark",
+ * 2) and there are already the millions cookies with a garbage
+ * instead of the correct base64 trail "=="
+ */
+
+ src.len = 22;
+
+ dst.data = (u_char *) ctx->uid_got;
+
+ if (ngx_decode_base64(&dst, &src) == NGX_ERROR) {
+ cookies = r->headers_in.cookies.elts;
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client sent invalid userid cookie \"%V\"",
+ &cookies[n]->value);
+ return ctx;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "uid: %08XD%08XD%08XD%08XD",
+ ctx->uid_got[0], ctx->uid_got[1],
+ ctx->uid_got[2], ctx->uid_got[3]);
+
+ return ctx;
+}
+
+
+static ngx_int_t
+ngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
+ ngx_http_userid_conf_t *conf)
+{
+ u_char *cookie, *p;
+ size_t len;
+ ngx_str_t src, dst;
+ ngx_table_elt_t *set_cookie, *p3p;
+
+ if (ngx_http_userid_create_uid(r, ctx, conf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ctx->uid_set[3] == 0) {
+ return NGX_OK;
+ }
+
+ len = conf->name.len + 1 + ngx_base64_encoded_length(16) + conf->path.len;
+
+ if (conf->expires) {
+ len += sizeof(expires) - 1 + 2;
+ }
+
+ if (conf->domain.len) {
+ len += conf->domain.len;
+ }
+
+ cookie = ngx_pnalloc(r->pool, len);
+ if (cookie == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_copy(cookie, conf->name.data, conf->name.len);
+ *p++ = '=';
+
+ if (ctx->uid_got[3] == 0 || ctx->reset) {
+ src.len = 16;
+ src.data = (u_char *) ctx->uid_set;
+ dst.data = p;
+
+ ngx_encode_base64(&dst, &src);
+
+ p += dst.len;
+
+ if (conf->mark) {
+ *(p - 2) = conf->mark;
+ }
+
+ } else {
+ p = ngx_cpymem(p, ctx->cookie.data, 22);
+ *p++ = conf->mark;
+ *p++ = '=';
+ }
+
+ if (conf->expires == NGX_HTTP_USERID_MAX_EXPIRES) {
+ p = ngx_cpymem(p, expires, sizeof(expires) - 1);
+
+ } else if (conf->expires) {
+ p = ngx_cpymem(p, expires, sizeof("; expires=") - 1);
+ p = ngx_http_cookie_time(p, ngx_time() + conf->expires);
+ }
+
+ p = ngx_copy(p, conf->domain.data, conf->domain.len);
+
+ p = ngx_copy(p, conf->path.data, conf->path.len);
+
+ set_cookie = ngx_list_push(&r->headers_out.headers);
+ if (set_cookie == NULL) {
+ return NGX_ERROR;
+ }
+
+ set_cookie->hash = 1;
+ ngx_str_set(&set_cookie->key, "Set-Cookie");
+ set_cookie->value.len = p - cookie;
+ set_cookie->value.data = cookie;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "uid cookie: \"%V\"", &set_cookie->value);
+
+ if (conf->p3p.len == 0) {
+ return NGX_OK;
+ }
+
+ p3p = ngx_list_push(&r->headers_out.headers);
+ if (p3p == NULL) {
+ return NGX_ERROR;
+ }
+
+ p3p->hash = 1;
+ ngx_str_set(&p3p->key, "P3P");
+ p3p->value = conf->p3p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_userid_create_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
+ ngx_http_userid_conf_t *conf)
+{
+ ngx_connection_t *c;
+ struct sockaddr_in *sin;
+ ngx_http_variable_value_t *vv;
+#if (NGX_HAVE_INET6)
+ u_char *p;
+ struct sockaddr_in6 *sin6;
+#endif
+
+ if (ctx->uid_set[3] != 0) {
+ return NGX_OK;
+ }
+
+ if (ctx->uid_got[3] != 0) {
+
+ vv = ngx_http_get_indexed_variable(r, ngx_http_userid_reset_index);
+
+ if (vv->len == 0 || (vv->len == 1 && vv->data[0] == '0')) {
+
+ if (conf->mark == '\0'
+ || (ctx->cookie.len > 23
+ && ctx->cookie.data[22] == conf->mark
+ && ctx->cookie.data[23] == '='))
+ {
+ return NGX_OK;
+ }
+
+ ctx->uid_set[0] = ctx->uid_got[0];
+ ctx->uid_set[1] = ctx->uid_got[1];
+ ctx->uid_set[2] = ctx->uid_got[2];
+ ctx->uid_set[3] = ctx->uid_got[3];
+
+ return NGX_OK;
+
+ } else {
+ ctx->reset = 1;
+
+ if (vv->len == 3 && ngx_strncmp(vv->data, "log", 3) == 0) {
+ ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
+ "userid cookie \"%V=%08XD%08XD%08XD%08XD\" was reset",
+ &conf->name, ctx->uid_got[0], ctx->uid_got[1],
+ ctx->uid_got[2], ctx->uid_got[3]);
+ }
+ }
+ }
+
+ /*
+ * TODO: in the threaded mode the sequencers should be in TLS and their
+ * ranges should be divided between threads
+ */
+
+ if (conf->enable == NGX_HTTP_USERID_V1) {
+ if (conf->service == NGX_CONF_UNSET) {
+ ctx->uid_set[0] = 0;
+ } else {
+ ctx->uid_set[0] = conf->service;
+ }
+ ctx->uid_set[1] = (uint32_t) ngx_time();
+ ctx->uid_set[2] = start_value;
+ ctx->uid_set[3] = sequencer_v1;
+ sequencer_v1 += 0x100;
+
+ } else {
+ if (conf->service == NGX_CONF_UNSET) {
+
+ c = r->connection;
+
+ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+
+ p = (u_char *) &ctx->uid_set[0];
+
+ *p++ = sin6->sin6_addr.s6_addr[12];
+ *p++ = sin6->sin6_addr.s6_addr[13];
+ *p++ = sin6->sin6_addr.s6_addr[14];
+ *p = sin6->sin6_addr.s6_addr[15];
+
+ break;
+#endif
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) c->local_sockaddr;
+ ctx->uid_set[0] = sin->sin_addr.s_addr;
+ break;
+ }
+
+ } else {
+ ctx->uid_set[0] = htonl(conf->service);
+ }
+
+ ctx->uid_set[1] = htonl((uint32_t) ngx_time());
+ ctx->uid_set[2] = htonl(start_value);
+ ctx->uid_set[3] = htonl(sequencer_v2);
+ sequencer_v2 += 0x100;
+ if (sequencer_v2 < 0x03030302) {
+ sequencer_v2 = 0x03030302;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_userid_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ ngx_str_t *name, uint32_t *uid)
+{
+ v->len = name->len + sizeof("=00001111222233334444555566667777") - 1;
+ v->data = ngx_pnalloc(r->pool, v->len);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ ngx_sprintf(v->data, "%V=%08XD%08XD%08XD%08XD",
+ name, uid[0], uid[1], uid[2], uid[3]);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_userid_reset_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ *v = ngx_http_variable_null_value;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_userid_add_variables(ngx_conf_t *cf)
+{
+ ngx_int_t n;
+ ngx_http_variable_t *var;
+
+ var = ngx_http_add_variable(cf, &ngx_http_userid_got, 0);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = ngx_http_userid_got_variable;
+
+ var = ngx_http_add_variable(cf, &ngx_http_userid_set, 0);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = ngx_http_userid_set_variable;
+
+ var = ngx_http_add_variable(cf, &ngx_http_userid_reset,
+ NGX_HTTP_VAR_CHANGEABLE);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = ngx_http_userid_reset_variable;
+
+ n = ngx_http_get_variable_index(cf, &ngx_http_userid_reset);
+ if (n == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_userid_reset_index = n;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_userid_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_userid_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_userid_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->name = { 0, NULL };
+ * conf->domain = { 0, NULL };
+ * conf->path = { 0, NULL };
+ * conf->p3p = { 0, NULL };
+ */
+
+ conf->enable = NGX_CONF_UNSET_UINT;
+ conf->service = NGX_CONF_UNSET;
+ conf->expires = NGX_CONF_UNSET;
+ conf->mark = (u_char) '\xFF';
+
+ return conf;
+}
+
+
+static char *
+ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_userid_conf_t *prev = parent;
+ ngx_http_userid_conf_t *conf = child;
+
+ ngx_conf_merge_uint_value(conf->enable, prev->enable,
+ NGX_HTTP_USERID_OFF);
+
+ ngx_conf_merge_str_value(conf->name, prev->name, "uid");
+ ngx_conf_merge_str_value(conf->domain, prev->domain, "");
+ ngx_conf_merge_str_value(conf->path, prev->path, "; path=/");
+ ngx_conf_merge_str_value(conf->p3p, prev->p3p, "");
+
+ ngx_conf_merge_value(conf->service, prev->service, NGX_CONF_UNSET);
+ ngx_conf_merge_sec_value(conf->expires, prev->expires, 0);
+
+ if (conf->mark == (u_char) '\xFF') {
+ if (prev->mark == (u_char) '\xFF') {
+ conf->mark = '\0';
+ } else {
+ conf->mark = prev->mark;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_userid_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_userid_filter;
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_http_userid_domain(ngx_conf_t *cf, void *post, void *data)
+{
+ ngx_str_t *domain = data;
+
+ u_char *p, *new;
+
+ if (ngx_strcmp(domain->data, "none") == 0) {
+ ngx_str_set(domain, "");
+ return NGX_CONF_OK;
+ }
+
+ new = ngx_pnalloc(cf->pool, sizeof("; domain=") - 1 + domain->len);
+ if (new == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ p = ngx_cpymem(new, "; domain=", sizeof("; domain=") - 1);
+ ngx_memcpy(p, domain->data, domain->len);
+
+ domain->len += sizeof("; domain=") - 1;
+ domain->data = new;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_userid_path(ngx_conf_t *cf, void *post, void *data)
+{
+ ngx_str_t *path = data;
+
+ u_char *p, *new;
+
+ new = ngx_pnalloc(cf->pool, sizeof("; path=") - 1 + path->len);
+ if (new == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ p = ngx_cpymem(new, "; path=", sizeof("; path=") - 1);
+ ngx_memcpy(p, path->data, path->len);
+
+ path->len += sizeof("; path=") - 1;
+ path->data = new;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_userid_conf_t *ucf = conf;
+
+ ngx_str_t *value;
+
+ if (ucf->expires != NGX_CONF_UNSET) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "max") == 0) {
+ ucf->expires = NGX_HTTP_USERID_MAX_EXPIRES;
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ ucf->expires = 0;
+ return NGX_CONF_OK;
+ }
+
+ ucf->expires = ngx_parse_time(&value[1], 1);
+ if (ucf->expires == (time_t) NGX_ERROR) {
+ return "invalid value";
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data)
+{
+ ngx_str_t *p3p = data;
+
+ if (ngx_strcmp(p3p->data, "none") == 0) {
+ ngx_str_set(p3p, "");
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_userid_conf_t *ucf = conf;
+
+ ngx_str_t *value;
+
+ if (ucf->mark != (u_char) '\xFF') {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ ucf->mark = '\0';
+ return NGX_CONF_OK;
+ }
+
+ if (value[1].len != 1
+ || !((value[1].data[0] >= '0' && value[1].data[0] <= '9')
+ || (value[1].data[0] >= 'A' && value[1].data[0] <= 'Z')
+ || (value[1].data[0] >= 'a' && value[1].data[0] <= 'z')
+ || value[1].data[0] == '='))
+ {
+ return "value must be \"off\" or a single letter, digit or \"=\"";
+ }
+
+ ucf->mark = value[1].data[0];
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_userid_init_worker(ngx_cycle_t *cycle)
+{
+ struct timeval tp;
+
+ ngx_gettimeofday(&tp);
+
+ /* use the most significant usec part that fits to 16 bits */
+ start_value = ((tp.tv_usec / 20) << 16) | ngx_pid;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_uwsgi_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_uwsgi_module.c
new file mode 100644
index 00000000000..a613b684a30
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_uwsgi_module.c
@@ -0,0 +1,2121 @@
+
+/*
+ * Copyright (C) Unbit S.a.s. 2009-2010
+ * Copyright (C) 2008 Manlio Perillo (manlio.perillo@gmail.com)
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_http_upstream_conf_t upstream;
+
+ ngx_array_t *flushes;
+ ngx_array_t *params_len;
+ ngx_array_t *params;
+ ngx_array_t *params_source;
+
+ ngx_hash_t headers_hash;
+ ngx_uint_t header_params;
+
+ ngx_array_t *uwsgi_lengths;
+ ngx_array_t *uwsgi_values;
+
+#if (NGX_HTTP_CACHE)
+ ngx_http_complex_value_t cache_key;
+#endif
+
+ ngx_str_t uwsgi_string;
+
+ ngx_uint_t modifier1;
+ ngx_uint_t modifier2;
+
+#if (NGX_HTTP_SSL)
+ ngx_uint_t ssl;
+ ngx_uint_t ssl_protocols;
+ ngx_str_t ssl_ciphers;
+ ngx_uint_t ssl_verify_depth;
+ ngx_str_t ssl_trusted_certificate;
+ ngx_str_t ssl_crl;
+#endif
+} ngx_http_uwsgi_loc_conf_t;
+
+
+static ngx_int_t ngx_http_uwsgi_eval(ngx_http_request_t *r,
+ ngx_http_uwsgi_loc_conf_t *uwcf);
+static ngx_int_t ngx_http_uwsgi_create_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_uwsgi_reinit_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_uwsgi_process_status_line(ngx_http_request_t *r);
+static ngx_int_t ngx_http_uwsgi_process_header(ngx_http_request_t *r);
+static void ngx_http_uwsgi_abort_request(ngx_http_request_t *r);
+static void ngx_http_uwsgi_finalize_request(ngx_http_request_t *r,
+ ngx_int_t rc);
+
+static void *ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static ngx_int_t ngx_http_uwsgi_merge_params(ngx_conf_t *cf,
+ ngx_http_uwsgi_loc_conf_t *conf, ngx_http_uwsgi_loc_conf_t *prev);
+
+static char *ngx_http_uwsgi_pass(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_uwsgi_store(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+#if (NGX_HTTP_CACHE)
+static ngx_int_t ngx_http_uwsgi_create_key(ngx_http_request_t *r);
+static char *ngx_http_uwsgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_uwsgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+#endif
+
+#if (NGX_HTTP_SSL)
+static ngx_int_t ngx_http_uwsgi_set_ssl(ngx_conf_t *cf,
+ ngx_http_uwsgi_loc_conf_t *uwcf);
+#endif
+
+
+static ngx_conf_num_bounds_t ngx_http_uwsgi_modifier_bounds = {
+ ngx_conf_check_num_bounds, 0, 255
+};
+
+
+static ngx_conf_bitmask_t ngx_http_uwsgi_next_upstream_masks[] = {
+ { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
+ { ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
+ { ngx_string("invalid_header"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
+ { ngx_string("http_500"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },
+ { ngx_string("http_503"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },
+ { ngx_string("http_403"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },
+ { ngx_string("http_404"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
+ { ngx_string("updating"), NGX_HTTP_UPSTREAM_FT_UPDATING },
+ { ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
+ { ngx_null_string, 0 }
+};
+
+
+#if (NGX_HTTP_SSL)
+
+static ngx_conf_bitmask_t ngx_http_uwsgi_ssl_protocols[] = {
+ { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
+ { ngx_string("SSLv3"), NGX_SSL_SSLv3 },
+ { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
+ { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
+ { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
+ { ngx_null_string, 0 }
+};
+
+#endif
+
+
+ngx_module_t ngx_http_uwsgi_module;
+
+
+static ngx_command_t ngx_http_uwsgi_commands[] = {
+
+ { ngx_string("uwsgi_pass"),
+ NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
+ ngx_http_uwsgi_pass,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("uwsgi_modifier1"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, modifier1),
+ &ngx_http_uwsgi_modifier_bounds },
+
+ { ngx_string("uwsgi_modifier2"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, modifier2),
+ &ngx_http_uwsgi_modifier_bounds },
+
+ { ngx_string("uwsgi_store"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_uwsgi_store,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("uwsgi_store_access"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
+ ngx_conf_set_access_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.store_access),
+ NULL },
+
+ { ngx_string("uwsgi_buffering"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.buffering),
+ NULL },
+
+ { ngx_string("uwsgi_ignore_client_abort"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ignore_client_abort),
+ NULL },
+
+ { ngx_string("uwsgi_bind"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_upstream_bind_set_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.local),
+ NULL },
+
+ { ngx_string("uwsgi_connect_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.connect_timeout),
+ NULL },
+
+ { ngx_string("uwsgi_send_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.send_timeout),
+ NULL },
+
+ { ngx_string("uwsgi_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.buffer_size),
+ NULL },
+
+ { ngx_string("uwsgi_pass_request_headers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.pass_request_headers),
+ NULL },
+
+ { ngx_string("uwsgi_pass_request_body"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.pass_request_body),
+ NULL },
+
+ { ngx_string("uwsgi_intercept_errors"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.intercept_errors),
+ NULL },
+
+ { ngx_string("uwsgi_read_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.read_timeout),
+ NULL },
+
+ { ngx_string("uwsgi_buffers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_conf_set_bufs_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.bufs),
+ NULL },
+
+ { ngx_string("uwsgi_busy_buffers_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.busy_buffers_size_conf),
+ NULL },
+
+#if (NGX_HTTP_CACHE)
+
+ { ngx_string("uwsgi_cache"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_uwsgi_cache,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("uwsgi_cache_key"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_uwsgi_cache_key,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("uwsgi_cache_path"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
+ ngx_http_file_cache_set_slot,
+ 0,
+ 0,
+ &ngx_http_uwsgi_module },
+
+ { ngx_string("uwsgi_cache_bypass"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_set_predicate_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_bypass),
+ NULL },
+
+ { ngx_string("uwsgi_no_cache"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_set_predicate_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.no_cache),
+ NULL },
+
+ { ngx_string("uwsgi_cache_valid"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_file_cache_valid_set_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_valid),
+ NULL },
+
+ { ngx_string("uwsgi_cache_min_uses"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_min_uses),
+ NULL },
+
+ { ngx_string("uwsgi_cache_use_stale"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_use_stale),
+ &ngx_http_uwsgi_next_upstream_masks },
+
+ { ngx_string("uwsgi_cache_methods"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_methods),
+ &ngx_http_upstream_cache_method_mask },
+
+ { ngx_string("uwsgi_cache_lock"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_lock),
+ NULL },
+
+ { ngx_string("uwsgi_cache_lock_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_lock_timeout),
+ NULL },
+
+ { ngx_string("uwsgi_cache_revalidate"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_revalidate),
+ NULL },
+
+#endif
+
+ { ngx_string("uwsgi_temp_path"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
+ ngx_conf_set_path_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.temp_path),
+ NULL },
+
+ { ngx_string("uwsgi_max_temp_file_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.max_temp_file_size_conf),
+ NULL },
+
+ { ngx_string("uwsgi_temp_file_write_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.temp_file_write_size_conf),
+ NULL },
+
+ { ngx_string("uwsgi_next_upstream"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.next_upstream),
+ &ngx_http_uwsgi_next_upstream_masks },
+
+ { ngx_string("uwsgi_param"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,
+ ngx_http_upstream_param_set_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, params_source),
+ NULL },
+
+ { ngx_string("uwsgi_string"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, uwsgi_string),
+ NULL },
+
+ { ngx_string("uwsgi_pass_header"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.pass_headers),
+ NULL },
+
+ { ngx_string("uwsgi_hide_header"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.hide_headers),
+ NULL },
+
+ { ngx_string("uwsgi_ignore_headers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ignore_headers),
+ &ngx_http_upstream_ignore_headers_masks },
+
+#if (NGX_HTTP_SSL)
+
+ { ngx_string("uwsgi_ssl_session_reuse"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_session_reuse),
+ NULL },
+
+ { ngx_string("uwsgi_ssl_protocols"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, ssl_protocols),
+ &ngx_http_uwsgi_ssl_protocols },
+
+ { ngx_string("uwsgi_ssl_ciphers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, ssl_ciphers),
+ NULL },
+
+ { ngx_string("uwsgi_ssl_name"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_set_complex_value_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_name),
+ NULL },
+
+ { ngx_string("uwsgi_ssl_server_name"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_server_name),
+ NULL },
+
+ { ngx_string("uwsgi_ssl_verify"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_verify),
+ NULL },
+
+ { ngx_string("uwsgi_ssl_verify_depth"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, ssl_verify_depth),
+ NULL },
+
+ { ngx_string("uwsgi_ssl_trusted_certificate"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, ssl_trusted_certificate),
+ NULL },
+
+ { ngx_string("uwsgi_ssl_crl"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_uwsgi_loc_conf_t, ssl_crl),
+ NULL },
+
+#endif
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_uwsgi_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_uwsgi_create_loc_conf, /* create location configuration */
+ ngx_http_uwsgi_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_uwsgi_module = {
+ NGX_MODULE_V1,
+ &ngx_http_uwsgi_module_ctx, /* module context */
+ ngx_http_uwsgi_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t ngx_http_uwsgi_hide_headers[] = {
+ ngx_string("X-Accel-Expires"),
+ ngx_string("X-Accel-Redirect"),
+ ngx_string("X-Accel-Limit-Rate"),
+ ngx_string("X-Accel-Buffering"),
+ ngx_string("X-Accel-Charset"),
+ ngx_null_string
+};
+
+
+#if (NGX_HTTP_CACHE)
+
+static ngx_keyval_t ngx_http_uwsgi_cache_headers[] = {
+ { ngx_string("HTTP_IF_MODIFIED_SINCE"),
+ ngx_string("$upstream_cache_last_modified") },
+ { ngx_string("HTTP_IF_UNMODIFIED_SINCE"), ngx_string("") },
+ { ngx_string("HTTP_IF_NONE_MATCH"), ngx_string("$upstream_cache_etag") },
+ { ngx_string("HTTP_IF_MATCH"), ngx_string("") },
+ { ngx_string("HTTP_RANGE"), ngx_string("") },
+ { ngx_string("HTTP_IF_RANGE"), ngx_string("") },
+ { ngx_null_string, ngx_null_string }
+};
+
+#endif
+
+
+static ngx_path_init_t ngx_http_uwsgi_temp_path = {
+ ngx_string(NGX_HTTP_UWSGI_TEMP_PATH), { 1, 2, 0 }
+};
+
+
+static ngx_int_t
+ngx_http_uwsgi_handler(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_http_status_t *status;
+ ngx_http_upstream_t *u;
+ ngx_http_uwsgi_loc_conf_t *uwcf;
+
+ if (ngx_http_upstream_create(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ status = ngx_pcalloc(r->pool, sizeof(ngx_http_status_t));
+ if (status == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_http_set_ctx(r, status, ngx_http_uwsgi_module);
+
+ uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module);
+
+ u = r->upstream;
+
+ if (uwcf->uwsgi_lengths == NULL) {
+
+#if (NGX_HTTP_SSL)
+ u->ssl = (uwcf->upstream.ssl != NULL);
+
+ if (u->ssl) {
+ ngx_str_set(&u->schema, "suwsgi://");
+
+ } else {
+ ngx_str_set(&u->schema, "uwsgi://");
+ }
+#else
+ ngx_str_set(&u->schema, "uwsgi://");
+#endif
+
+ } else {
+ if (ngx_http_uwsgi_eval(r, uwcf) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ u->output.tag = (ngx_buf_tag_t) &ngx_http_uwsgi_module;
+
+ u->conf = &uwcf->upstream;
+
+#if (NGX_HTTP_CACHE)
+ u->create_key = ngx_http_uwsgi_create_key;
+#endif
+ u->create_request = ngx_http_uwsgi_create_request;
+ u->reinit_request = ngx_http_uwsgi_reinit_request;
+ u->process_header = ngx_http_uwsgi_process_status_line;
+ u->abort_request = ngx_http_uwsgi_abort_request;
+ u->finalize_request = ngx_http_uwsgi_finalize_request;
+ r->state = 0;
+
+ u->buffering = uwcf->upstream.buffering;
+
+ u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
+ if (u->pipe == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ u->pipe->input_filter = ngx_event_pipe_copy_input_filter;
+ u->pipe->input_ctx = r;
+
+ rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ return rc;
+ }
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_http_uwsgi_eval(ngx_http_request_t *r, ngx_http_uwsgi_loc_conf_t * uwcf)
+{
+ size_t add;
+ ngx_url_t url;
+ ngx_http_upstream_t *u;
+
+ ngx_memzero(&url, sizeof(ngx_url_t));
+
+ if (ngx_http_script_run(r, &url.url, uwcf->uwsgi_lengths->elts, 0,
+ uwcf->uwsgi_values->elts)
+ == NULL)
+ {
+ return NGX_ERROR;
+ }
+
+ if (url.url.len > 8
+ && ngx_strncasecmp(url.url.data, (u_char *) "uwsgi://", 8) == 0)
+ {
+ add = 8;
+
+ } else if (url.url.len > 9
+ && ngx_strncasecmp(url.url.data, (u_char *) "suwsgi://", 9) == 0)
+ {
+
+#if (NGX_HTTP_SSL)
+ add = 9;
+ r->upstream->ssl = 1;
+#else
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "suwsgi protocol requires SSL support");
+ return NGX_ERROR;
+#endif
+
+ } else {
+ add = 0;
+ }
+
+ u = r->upstream;
+
+ if (add) {
+ u->schema.len = add;
+ u->schema.data = url.url.data;
+
+ url.url.data += add;
+ url.url.len -= add;
+
+ } else {
+ ngx_str_set(&u->schema, "uwsgi://");
+ }
+
+ url.no_resolve = 1;
+
+ if (ngx_parse_url(r->pool, &url) != NGX_OK) {
+ if (url.err) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "%s in upstream \"%V\"", url.err, &url.url);
+ }
+
+ return NGX_ERROR;
+ }
+
+ u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
+ if (u->resolved == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (url.addrs && url.addrs[0].sockaddr) {
+ u->resolved->sockaddr = url.addrs[0].sockaddr;
+ u->resolved->socklen = url.addrs[0].socklen;
+ u->resolved->naddrs = 1;
+ u->resolved->host = url.addrs[0].name;
+
+ } else {
+ u->resolved->host = url.host;
+ u->resolved->port = url.port;
+ u->resolved->no_port = url.no_port;
+ }
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HTTP_CACHE)
+
+static ngx_int_t
+ngx_http_uwsgi_create_key(ngx_http_request_t *r)
+{
+ ngx_str_t *key;
+ ngx_http_uwsgi_loc_conf_t *uwcf;
+
+ key = ngx_array_push(&r->cache->keys);
+ if (key == NULL) {
+ return NGX_ERROR;
+ }
+
+ uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module);
+
+ if (ngx_http_complex_value(r, &uwcf->cache_key, key) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_uwsgi_create_request(ngx_http_request_t *r)
+{
+ u_char ch, *lowcase_key;
+ size_t key_len, val_len, len, allocated;
+ ngx_uint_t i, n, hash, skip_empty, header_params;
+ ngx_buf_t *b;
+ ngx_chain_t *cl, *body;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *header, **ignored;
+ ngx_http_script_code_pt code;
+ ngx_http_script_engine_t e, le;
+ ngx_http_uwsgi_loc_conf_t *uwcf;
+ ngx_http_script_len_code_pt lcode;
+
+ len = 0;
+ header_params = 0;
+ ignored = NULL;
+
+ uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module);
+
+ if (uwcf->params_len) {
+ ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
+
+ ngx_http_script_flush_no_cacheable_variables(r, uwcf->flushes);
+ le.flushed = 1;
+
+ le.ip = uwcf->params_len->elts;
+ le.request = r;
+
+ while (*(uintptr_t *) le.ip) {
+
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ key_len = lcode(&le);
+
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ skip_empty = lcode(&le);
+
+ for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode (&le)) {
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ }
+ le.ip += sizeof(uintptr_t);
+
+ if (skip_empty && val_len == 0) {
+ continue;
+ }
+
+ len += 2 + key_len + 2 + val_len;
+ }
+ }
+
+ if (uwcf->upstream.pass_request_headers) {
+
+ allocated = 0;
+ lowcase_key = NULL;
+
+ if (uwcf->header_params) {
+ n = 0;
+ part = &r->headers_in.headers.part;
+
+ while (part) {
+ n += part->nelts;
+ part = part->next;
+ }
+
+ ignored = ngx_palloc(r->pool, n * sizeof(void *));
+ if (ignored == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ part = &r->headers_in.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (uwcf->header_params) {
+ if (allocated < header[i].key.len) {
+ allocated = header[i].key.len + 16;
+ lowcase_key = ngx_pnalloc(r->pool, allocated);
+ if (lowcase_key == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ hash = 0;
+
+ for (n = 0; n < header[i].key.len; n++) {
+ ch = header[i].key.data[n];
+
+ if (ch >= 'A' && ch <= 'Z') {
+ ch |= 0x20;
+
+ } else if (ch == '-') {
+ ch = '_';
+ }
+
+ hash = ngx_hash(hash, ch);
+ lowcase_key[n] = ch;
+ }
+
+ if (ngx_hash_find(&uwcf->headers_hash, hash, lowcase_key, n)) {
+ ignored[header_params++] = &header[i];
+ continue;
+ }
+ }
+
+ len += 2 + sizeof("HTTP_") - 1 + header[i].key.len
+ + 2 + header[i].value.len;
+ }
+ }
+
+ len += uwcf->uwsgi_string.len;
+
+#if 0
+ /* allow custom uwsgi packet */
+ if (len > 0 && len < 2) {
+ ngx_log_error (NGX_LOG_ALERT, r->connection->log, 0,
+ "uwsgi request is too little: %uz", len);
+ return NGX_ERROR;
+ }
+#endif
+
+ b = ngx_create_temp_buf(r->pool, len + 4);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+
+ *b->last++ = (u_char) uwcf->modifier1;
+ *b->last++ = (u_char) (len & 0xff);
+ *b->last++ = (u_char) ((len >> 8) & 0xff);
+ *b->last++ = (u_char) uwcf->modifier2;
+
+ if (uwcf->params_len) {
+ ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
+
+ e.ip = uwcf->params->elts;
+ e.pos = b->last;
+ e.request = r;
+ e.flushed = 1;
+
+ le.ip = uwcf->params_len->elts;
+
+ while (*(uintptr_t *) le.ip) {
+
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ key_len = (u_char) lcode (&le);
+
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ skip_empty = lcode(&le);
+
+ for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ }
+ le.ip += sizeof(uintptr_t);
+
+ if (skip_empty && val_len == 0) {
+ e.skip = 1;
+
+ while (*(uintptr_t *) e.ip) {
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) &e);
+ }
+ e.ip += sizeof(uintptr_t);
+
+ e.skip = 0;
+
+ continue;
+ }
+
+ *e.pos++ = (u_char) (key_len & 0xff);
+ *e.pos++ = (u_char) ((key_len >> 8) & 0xff);
+
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) & e);
+
+ *e.pos++ = (u_char) (val_len & 0xff);
+ *e.pos++ = (u_char) ((val_len >> 8) & 0xff);
+
+ while (*(uintptr_t *) e.ip) {
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) & e);
+ }
+
+ e.ip += sizeof(uintptr_t);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "uwsgi param: \"%*s: %*s\"",
+ key_len, e.pos - (key_len + 2 + val_len),
+ val_len, e.pos - val_len);
+ }
+
+ b->last = e.pos;
+ }
+
+ if (uwcf->upstream.pass_request_headers) {
+
+ part = &r->headers_in.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ for (n = 0; n < header_params; n++) {
+ if (&header[i] == ignored[n]) {
+ goto next;
+ }
+ }
+
+ key_len = sizeof("HTTP_") - 1 + header[i].key.len;
+ *b->last++ = (u_char) (key_len & 0xff);
+ *b->last++ = (u_char) ((key_len >> 8) & 0xff);
+
+ b->last = ngx_cpymem(b->last, "HTTP_", sizeof("HTTP_") - 1);
+ for (n = 0; n < header[i].key.len; n++) {
+ ch = header[i].key.data[n];
+
+ if (ch >= 'a' && ch <= 'z') {
+ ch &= ~0x20;
+
+ } else if (ch == '-') {
+ ch = '_';
+ }
+
+ *b->last++ = ch;
+ }
+
+ val_len = header[i].value.len;
+ *b->last++ = (u_char) (val_len & 0xff);
+ *b->last++ = (u_char) ((val_len >> 8) & 0xff);
+ b->last = ngx_copy(b->last, header[i].value.data, val_len);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "uwsgi param: \"%*s: %*s\"",
+ key_len, b->last - (key_len + 2 + val_len),
+ val_len, b->last - val_len);
+ next:
+
+ continue;
+ }
+ }
+
+ b->last = ngx_copy(b->last, uwcf->uwsgi_string.data,
+ uwcf->uwsgi_string.len);
+
+ if (uwcf->upstream.pass_request_body) {
+ body = r->upstream->request_bufs;
+ r->upstream->request_bufs = cl;
+
+ while (body) {
+ b = ngx_alloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
+
+ cl->next = ngx_alloc_chain_link(r->pool);
+ if (cl->next == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = cl->next;
+ cl->buf = b;
+
+ body = body->next;
+ }
+
+ } else {
+ r->upstream->request_bufs = cl;
+ }
+
+ cl->next = NULL;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_uwsgi_reinit_request(ngx_http_request_t *r)
+{
+ ngx_http_status_t *status;
+
+ status = ngx_http_get_module_ctx(r, ngx_http_uwsgi_module);
+
+ if (status == NULL) {
+ return NGX_OK;
+ }
+
+ status->code = 0;
+ status->count = 0;
+ status->start = NULL;
+ status->end = NULL;
+
+ r->upstream->process_header = ngx_http_uwsgi_process_status_line;
+ r->state = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_uwsgi_process_status_line(ngx_http_request_t *r)
+{
+ size_t len;
+ ngx_int_t rc;
+ ngx_http_status_t *status;
+ ngx_http_upstream_t *u;
+
+ status = ngx_http_get_module_ctx(r, ngx_http_uwsgi_module);
+
+ if (status == NULL) {
+ return NGX_ERROR;
+ }
+
+ u = r->upstream;
+
+ rc = ngx_http_parse_status_line(r, &u->buffer, status);
+
+ if (rc == NGX_AGAIN) {
+ return rc;
+ }
+
+ if (rc == NGX_ERROR) {
+ u->process_header = ngx_http_uwsgi_process_header;
+ return ngx_http_uwsgi_process_header(r);
+ }
+
+ if (u->state && u->state->status == 0) {
+ u->state->status = status->code;
+ }
+
+ u->headers_in.status_n = status->code;
+
+ len = status->end - status->start;
+ u->headers_in.status_line.len = len;
+
+ u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);
+ if (u->headers_in.status_line.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(u->headers_in.status_line.data, status->start, len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http uwsgi status %ui \"%V\"",
+ u->headers_in.status_n, &u->headers_in.status_line);
+
+ u->process_header = ngx_http_uwsgi_process_header;
+
+ return ngx_http_uwsgi_process_header(r);
+}
+
+
+static ngx_int_t
+ngx_http_uwsgi_process_header(ngx_http_request_t *r)
+{
+ ngx_str_t *status_line;
+ ngx_int_t rc, status;
+ ngx_table_elt_t *h;
+ ngx_http_upstream_t *u;
+ ngx_http_upstream_header_t *hh;
+ ngx_http_upstream_main_conf_t *umcf;
+
+ umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
+
+ for ( ;; ) {
+
+ rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);
+
+ if (rc == NGX_OK) {
+
+ /* a header line has been parsed successfully */
+
+ h = ngx_list_push(&r->upstream->headers_in.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = r->header_hash;
+
+ h->key.len = r->header_name_end - r->header_name_start;
+ h->value.len = r->header_end - r->header_start;
+
+ h->key.data = ngx_pnalloc(r->pool,
+ h->key.len + 1 + h->value.len + 1
+ + h->key.len);
+ if (h->key.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->value.data = h->key.data + h->key.len + 1;
+ h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;
+
+ ngx_memcpy(h->key.data, r->header_name_start, h->key.len);
+ h->key.data[h->key.len] = '\0';
+ ngx_memcpy(h->value.data, r->header_start, h->value.len);
+ h->value.data[h->value.len] = '\0';
+
+ if (h->key.len == r->lowcase_index) {
+ ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
+
+ } else {
+ ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
+ }
+
+ hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
+ h->lowcase_key, h->key.len);
+
+ if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http uwsgi header: \"%V: %V\"", &h->key, &h->value);
+
+ continue;
+ }
+
+ if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
+
+ /* a whole header has been parsed successfully */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http uwsgi header done");
+
+ u = r->upstream;
+
+ if (u->headers_in.status_n) {
+ goto done;
+ }
+
+ if (u->headers_in.status) {
+ status_line = &u->headers_in.status->value;
+
+ status = ngx_atoi(status_line->data, 3);
+ if (status == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent invalid status \"%V\"",
+ status_line);
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+
+ u->headers_in.status_n = status;
+ u->headers_in.status_line = *status_line;
+
+ } else if (u->headers_in.location) {
+ u->headers_in.status_n = 302;
+ ngx_str_set(&u->headers_in.status_line,
+ "302 Moved Temporarily");
+
+ } else {
+ u->headers_in.status_n = 200;
+ ngx_str_set(&u->headers_in.status_line, "200 OK");
+ }
+
+ if (u->state && u->state->status == 0) {
+ u->state->status = u->headers_in.status_n;
+ }
+
+ done:
+
+ if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS
+ && r->headers_in.upgrade)
+ {
+ u->upgrade = 1;
+ }
+
+ return NGX_OK;
+ }
+
+ if (rc == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ /* there was error while a header line parsing */
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream sent invalid header");
+
+ return NGX_HTTP_UPSTREAM_INVALID_HEADER;
+ }
+}
+
+
+static void
+ngx_http_uwsgi_abort_request(ngx_http_request_t *r)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "abort http uwsgi request");
+
+ return;
+}
+
+
+static void
+ngx_http_uwsgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "finalize http uwsgi request");
+
+ return;
+}
+
+
+static void *
+ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_uwsgi_loc_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_uwsgi_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->modifier1 = NGX_CONF_UNSET_UINT;
+ conf->modifier2 = NGX_CONF_UNSET_UINT;
+
+ conf->upstream.store = NGX_CONF_UNSET;
+ conf->upstream.store_access = NGX_CONF_UNSET_UINT;
+ conf->upstream.buffering = NGX_CONF_UNSET;
+ conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
+
+ conf->upstream.local = NGX_CONF_UNSET_PTR;
+
+ conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
+
+ conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;
+ conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
+
+ conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;
+ conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
+ conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;
+
+ conf->upstream.pass_request_headers = NGX_CONF_UNSET;
+ conf->upstream.pass_request_body = NGX_CONF_UNSET;
+
+#if (NGX_HTTP_CACHE)
+ conf->upstream.cache = NGX_CONF_UNSET_PTR;
+ conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;
+ conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;
+ conf->upstream.no_cache = NGX_CONF_UNSET_PTR;
+ conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;
+ conf->upstream.cache_lock = NGX_CONF_UNSET;
+ conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;
+ conf->upstream.cache_revalidate = NGX_CONF_UNSET;
+#endif
+
+ conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
+ conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;
+
+ conf->upstream.intercept_errors = NGX_CONF_UNSET;
+
+#if (NGX_HTTP_SSL)
+ conf->upstream.ssl_session_reuse = NGX_CONF_UNSET;
+ conf->upstream.ssl_server_name = NGX_CONF_UNSET;
+ conf->upstream.ssl_verify = NGX_CONF_UNSET;
+ conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
+#endif
+
+ /* "uwsgi_cyclic_temp_file" is disabled */
+ conf->upstream.cyclic_temp_file = 0;
+
+ conf->upstream.change_buffering = 1;
+
+ ngx_str_set(&conf->upstream.module, "uwsgi");
+
+ return conf;
+}
+
+
+static char *
+ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_uwsgi_loc_conf_t *prev = parent;
+ ngx_http_uwsgi_loc_conf_t *conf = child;
+
+ size_t size;
+ ngx_hash_init_t hash;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (conf->upstream.store != 0) {
+ ngx_conf_merge_value(conf->upstream.store, prev->upstream.store, 0);
+
+ if (conf->upstream.store_lengths == NULL) {
+ conf->upstream.store_lengths = prev->upstream.store_lengths;
+ conf->upstream.store_values = prev->upstream.store_values;
+ }
+ }
+
+ ngx_conf_merge_uint_value(conf->upstream.store_access,
+ prev->upstream.store_access, 0600);
+
+ ngx_conf_merge_value(conf->upstream.buffering,
+ prev->upstream.buffering, 1);
+
+ ngx_conf_merge_value(conf->upstream.ignore_client_abort,
+ prev->upstream.ignore_client_abort, 0);
+
+ ngx_conf_merge_ptr_value(conf->upstream.local,
+ prev->upstream.local, NULL);
+
+ ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
+ prev->upstream.connect_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->upstream.send_timeout,
+ prev->upstream.send_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->upstream.read_timeout,
+ prev->upstream.read_timeout, 60000);
+
+ ngx_conf_merge_size_value(conf->upstream.send_lowat,
+ prev->upstream.send_lowat, 0);
+
+ ngx_conf_merge_size_value(conf->upstream.buffer_size,
+ prev->upstream.buffer_size,
+ (size_t) ngx_pagesize);
+
+
+ ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,
+ 8, ngx_pagesize);
+
+ if (conf->upstream.bufs.num < 2) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "there must be at least 2 \"uwsgi_buffers\"");
+ return NGX_CONF_ERROR;
+ }
+
+
+ size = conf->upstream.buffer_size;
+ if (size < conf->upstream.bufs.size) {
+ size = conf->upstream.bufs.size;
+ }
+
+
+ ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,
+ prev->upstream.busy_buffers_size_conf,
+ NGX_CONF_UNSET_SIZE);
+
+ if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {
+ conf->upstream.busy_buffers_size = 2 * size;
+ } else {
+ conf->upstream.busy_buffers_size =
+ conf->upstream.busy_buffers_size_conf;
+ }
+
+ if (conf->upstream.busy_buffers_size < size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"uwsgi_busy_buffers_size\" must be equal to or greater "
+ "than the maximum of the value of \"uwsgi_buffer_size\" and "
+ "one of the \"uwsgi_buffers\"");
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->upstream.busy_buffers_size
+ > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"uwsgi_busy_buffers_size\" must be less than "
+ "the size of all \"uwsgi_buffers\" minus one buffer");
+
+ return NGX_CONF_ERROR;
+ }
+
+
+ ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,
+ prev->upstream.temp_file_write_size_conf,
+ NGX_CONF_UNSET_SIZE);
+
+ if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {
+ conf->upstream.temp_file_write_size = 2 * size;
+ } else {
+ conf->upstream.temp_file_write_size =
+ conf->upstream.temp_file_write_size_conf;
+ }
+
+ if (conf->upstream.temp_file_write_size < size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"uwsgi_temp_file_write_size\" must be equal to or greater than "
+ "the maximum of the value of \"uwsgi_buffer_size\" and "
+ "one of the \"uwsgi_buffers\"");
+
+ return NGX_CONF_ERROR;
+ }
+
+
+ ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,
+ prev->upstream.max_temp_file_size_conf,
+ NGX_CONF_UNSET_SIZE);
+
+ if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {
+ conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;
+ } else {
+ conf->upstream.max_temp_file_size =
+ conf->upstream.max_temp_file_size_conf;
+ }
+
+ if (conf->upstream.max_temp_file_size != 0
+ && conf->upstream.max_temp_file_size < size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"uwsgi_max_temp_file_size\" must be equal to zero to disable "
+ "temporary files usage or must be equal to or greater than "
+ "the maximum of the value of \"uwsgi_buffer_size\" and "
+ "one of the \"uwsgi_buffers\"");
+
+ return NGX_CONF_ERROR;
+ }
+
+
+ ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
+ prev->upstream.ignore_headers,
+ NGX_CONF_BITMASK_SET);
+
+
+ ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
+ prev->upstream.next_upstream,
+ (NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_ERROR
+ |NGX_HTTP_UPSTREAM_FT_TIMEOUT));
+
+ if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
+ conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_OFF;
+ }
+
+ if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
+ prev->upstream.temp_path,
+ &ngx_http_uwsgi_temp_path)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ ngx_conf_merge_ptr_value(conf->upstream.cache,
+ prev->upstream.cache, NULL);
+
+ if (conf->upstream.cache && conf->upstream.cache->data == NULL) {
+ ngx_shm_zone_t *shm_zone;
+
+ shm_zone = conf->upstream.cache;
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"uwsgi_cache\" zone \"%V\" is unknown",
+ &shm_zone->shm.name);
+
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,
+ prev->upstream.cache_min_uses, 1);
+
+ ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,
+ prev->upstream.cache_use_stale,
+ (NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_OFF));
+
+ if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {
+ conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET
+ |NGX_HTTP_UPSTREAM_FT_OFF;
+ }
+
+ if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {
+ conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;
+ }
+
+ if (conf->upstream.cache_methods == 0) {
+ conf->upstream.cache_methods = prev->upstream.cache_methods;
+ }
+
+ conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;
+
+ ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,
+ prev->upstream.cache_bypass, NULL);
+
+ ngx_conf_merge_ptr_value(conf->upstream.no_cache,
+ prev->upstream.no_cache, NULL);
+
+ ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
+ prev->upstream.cache_valid, NULL);
+
+ if (conf->cache_key.value.data == NULL) {
+ conf->cache_key = prev->cache_key;
+ }
+
+ ngx_conf_merge_value(conf->upstream.cache_lock,
+ prev->upstream.cache_lock, 0);
+
+ ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,
+ prev->upstream.cache_lock_timeout, 5000);
+
+ ngx_conf_merge_value(conf->upstream.cache_revalidate,
+ prev->upstream.cache_revalidate, 0);
+
+#endif
+
+ ngx_conf_merge_value(conf->upstream.pass_request_headers,
+ prev->upstream.pass_request_headers, 1);
+ ngx_conf_merge_value(conf->upstream.pass_request_body,
+ prev->upstream.pass_request_body, 1);
+
+ ngx_conf_merge_value(conf->upstream.intercept_errors,
+ prev->upstream.intercept_errors, 0);
+
+#if (NGX_HTTP_SSL)
+
+ ngx_conf_merge_value(conf->upstream.ssl_session_reuse,
+ prev->upstream.ssl_session_reuse, 1);
+
+ ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,
+ (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3
+ |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1
+ |NGX_SSL_TLSv1_2));
+
+ ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,
+ "DEFAULT");
+
+ if (conf->upstream.ssl_name == NULL) {
+ conf->upstream.ssl_name = prev->upstream.ssl_name;
+ }
+
+ ngx_conf_merge_value(conf->upstream.ssl_server_name,
+ prev->upstream.ssl_server_name, 0);
+ ngx_conf_merge_value(conf->upstream.ssl_verify,
+ prev->upstream.ssl_verify, 0);
+ ngx_conf_merge_uint_value(conf->ssl_verify_depth,
+ prev->ssl_verify_depth, 1);
+ ngx_conf_merge_str_value(conf->ssl_trusted_certificate,
+ prev->ssl_trusted_certificate, "");
+ ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, "");
+
+ if (conf->ssl && ngx_http_uwsgi_set_ssl(cf, conf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->upstream.ssl == NULL) {
+ conf->upstream.ssl = prev->upstream.ssl;
+ }
+
+#endif
+
+ ngx_conf_merge_str_value(conf->uwsgi_string, prev->uwsgi_string, "");
+
+ hash.max_size = 512;
+ hash.bucket_size = ngx_align(64, ngx_cacheline_size);
+ hash.name = "uwsgi_hide_headers_hash";
+
+ if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
+ &prev->upstream, ngx_http_uwsgi_hide_headers, &hash)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->upstream.upstream == NULL) {
+ conf->upstream.upstream = prev->upstream.upstream;
+ }
+
+ if (conf->uwsgi_lengths == NULL) {
+ conf->uwsgi_lengths = prev->uwsgi_lengths;
+ conf->uwsgi_values = prev->uwsgi_values;
+ }
+
+ if (conf->upstream.upstream || conf->uwsgi_lengths) {
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+ if (clcf->handler == NULL && clcf->lmt_excpt) {
+ clcf->handler = ngx_http_uwsgi_handler;
+ }
+ }
+
+ ngx_conf_merge_uint_value(conf->modifier1, prev->modifier1, 0);
+ ngx_conf_merge_uint_value(conf->modifier2, prev->modifier2, 0);
+
+ if (ngx_http_uwsgi_merge_params(cf, conf, prev) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_uwsgi_merge_params(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *conf,
+ ngx_http_uwsgi_loc_conf_t *prev)
+{
+ u_char *p;
+ size_t size;
+ uintptr_t *code;
+ ngx_uint_t i, nsrc;
+ ngx_array_t headers_names;
+#if (NGX_HTTP_CACHE)
+ ngx_array_t params_merged;
+#endif
+ ngx_hash_key_t *hk;
+ ngx_hash_init_t hash;
+ ngx_http_upstream_param_t *src;
+ ngx_http_script_compile_t sc;
+ ngx_http_script_copy_code_t *copy;
+
+ if (conf->params_source == NULL) {
+ conf->params_source = prev->params_source;
+
+ if (prev->headers_hash.buckets
+#if (NGX_HTTP_CACHE)
+ && ((conf->upstream.cache == NULL)
+ == (prev->upstream.cache == NULL))
+#endif
+ )
+ {
+ conf->flushes = prev->flushes;
+ conf->params_len = prev->params_len;
+ conf->params = prev->params;
+ conf->headers_hash = prev->headers_hash;
+ conf->header_params = prev->header_params;
+
+ return NGX_OK;
+ }
+ }
+
+ if (conf->params_source == NULL
+#if (NGX_HTTP_CACHE)
+ && (conf->upstream.cache == NULL)
+#endif
+ )
+ {
+ conf->headers_hash.buckets = (void *) 1;
+ return NGX_OK;
+ }
+
+ conf->params_len = ngx_array_create(cf->pool, 64, 1);
+ if (conf->params_len == NULL) {
+ return NGX_ERROR;
+ }
+
+ conf->params = ngx_array_create(cf->pool, 512, 1);
+ if (conf->params == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (conf->params_source) {
+ src = conf->params_source->elts;
+ nsrc = conf->params_source->nelts;
+
+ } else {
+ src = NULL;
+ nsrc = 0;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (conf->upstream.cache) {
+ ngx_keyval_t *h;
+ ngx_http_upstream_param_t *s;
+
+ if (ngx_array_init(&params_merged, cf->temp_pool, 4,
+ sizeof(ngx_http_upstream_param_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ for (i = 0; i < nsrc; i++) {
+
+ s = ngx_array_push(&params_merged);
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ *s = src[i];
+ }
+
+ h = ngx_http_uwsgi_cache_headers;
+
+ while (h->key.len) {
+
+ src = params_merged.elts;
+ nsrc = params_merged.nelts;
+
+ for (i = 0; i < nsrc; i++) {
+ if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {
+ goto next;
+ }
+ }
+
+ s = ngx_array_push(&params_merged);
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ s->key = h->key;
+ s->value = h->value;
+ s->skip_empty = 1;
+
+ next:
+
+ h++;
+ }
+
+ src = params_merged.elts;
+ nsrc = params_merged.nelts;
+ }
+
+#endif
+
+ for (i = 0; i < nsrc; i++) {
+
+ if (src[i].key.len > sizeof("HTTP_") - 1
+ && ngx_strncmp(src[i].key.data, "HTTP_", sizeof("HTTP_") - 1) == 0)
+ {
+ hk = ngx_array_push(&headers_names);
+ if (hk == NULL) {
+ return NGX_ERROR;
+ }
+
+ hk->key.len = src[i].key.len - 5;
+ hk->key.data = src[i].key.data + 5;
+ hk->key_hash = ngx_hash_key_lc(hk->key.data, hk->key.len);
+ hk->value = (void *) 1;
+
+ if (src[i].value.len == 0) {
+ continue;
+ }
+ }
+
+ copy = ngx_array_push_n(conf->params_len,
+ sizeof(ngx_http_script_copy_code_t));
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+ copy->len = src[i].key.len;
+
+ copy = ngx_array_push_n(conf->params_len,
+ sizeof(ngx_http_script_copy_code_t));
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+ copy->len = src[i].skip_empty;
+
+
+ size = (sizeof(ngx_http_script_copy_code_t)
+ + src[i].key.len + sizeof(uintptr_t) - 1)
+ & ~(sizeof(uintptr_t) - 1);
+
+ copy = ngx_array_push_n(conf->params, size);
+ if (copy == NULL) {
+ return NGX_ERROR;
+ }
+
+ copy->code = ngx_http_script_copy_code;
+ copy->len = src[i].key.len;
+
+ p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
+ ngx_memcpy(p, src[i].key.data, src[i].key.len);
+
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &src[i].value;
+ sc.flushes = &conf->flushes;
+ sc.lengths = &conf->params_len;
+ sc.values = &conf->params;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+
+
+ code = ngx_array_push_n(conf->params, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+ }
+
+ code = ngx_array_push_n(conf->params_len, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+
+ conf->header_params = headers_names.nelts;
+
+ hash.hash = &conf->headers_hash;
+ hash.key = ngx_hash_key_lc;
+ hash.max_size = 512;
+ hash.bucket_size = 64;
+ hash.name = "uwsgi_params_hash";
+ hash.pool = cf->pool;
+ hash.temp_pool = NULL;
+
+ return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);
+}
+
+
+static char *
+ngx_http_uwsgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_uwsgi_loc_conf_t *uwcf = conf;
+
+ size_t add;
+ ngx_url_t u;
+ ngx_str_t *value, *url;
+ ngx_uint_t n;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_script_compile_t sc;
+
+ if (uwcf->upstream.upstream || uwcf->uwsgi_lengths) {
+ return "is duplicate";
+ }
+
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+ clcf->handler = ngx_http_uwsgi_handler;
+
+ value = cf->args->elts;
+
+ url = &value[1];
+
+ n = ngx_http_script_variables_count(url);
+
+ if (n) {
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = url;
+ sc.lengths = &uwcf->uwsgi_lengths;
+ sc.values = &uwcf->uwsgi_values;
+ sc.variables = n;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+#if (NGX_HTTP_SSL)
+ uwcf->ssl = 1;
+#endif
+
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_strncasecmp(url->data, (u_char *) "uwsgi://", 8) == 0) {
+ add = 8;
+
+ } else if (ngx_strncasecmp(url->data, (u_char *) "suwsgi://", 9) == 0) {
+
+#if (NGX_HTTP_SSL)
+ add = 9;
+ uwcf->ssl = 1;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "suwsgi protocol requires SSL support");
+ return NGX_CONF_ERROR;
+#endif
+
+ } else {
+ add = 0;
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url.len = url->len - add;
+ u.url.data = url->data + add;
+ u.no_resolve = 1;
+
+ uwcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
+ if (uwcf->upstream.upstream == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (clcf->name.data[clcf->name.len - 1] == '/') {
+ clcf->auto_redirect = 1;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_uwsgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_uwsgi_loc_conf_t *uwcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_script_compile_t sc;
+
+ if (uwcf->upstream.store != NGX_CONF_UNSET || uwcf->upstream.store_lengths)
+ {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ uwcf->upstream.store = 0;
+ return NGX_CONF_OK;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (uwcf->upstream.cache != NGX_CONF_UNSET_PTR
+ && uwcf->upstream.cache != NULL)
+ {
+ return "is incompatible with \"uwsgi_cache\"";
+ }
+
+#endif
+
+ if (ngx_strcmp(value[1].data, "on") == 0) {
+ uwcf->upstream.store = 1;
+ return NGX_CONF_OK;
+ }
+
+ /* include the terminating '\0' into script */
+ value[1].len++;
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &value[1];
+ sc.lengths = &uwcf->upstream.store_lengths;
+ sc.values = &uwcf->upstream.store_values;
+ sc.variables = ngx_http_script_variables_count(&value[1]);
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+#if (NGX_HTTP_CACHE)
+
+static char *
+ngx_http_uwsgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_uwsgi_loc_conf_t *uwcf = conf;
+
+ ngx_str_t *value;
+
+ value = cf->args->elts;
+
+ if (uwcf->upstream.cache != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ uwcf->upstream.cache = NULL;
+ return NGX_CONF_OK;
+ }
+
+ if (uwcf->upstream.store > 0 || uwcf->upstream.store_lengths) {
+ return "is incompatible with \"uwsgi_store\"";
+ }
+
+ uwcf->upstream.cache = ngx_shared_memory_add(cf, &value[1], 0,
+ &ngx_http_uwsgi_module);
+ if (uwcf->upstream.cache == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_uwsgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_uwsgi_loc_conf_t *uwcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_compile_complex_value_t ccv;
+
+ value = cf->args->elts;
+
+ if (uwcf->cache_key.value.data) {
+ return "is duplicate";
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &uwcf->cache_key;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+#endif
+
+
+#if (NGX_HTTP_SSL)
+
+static ngx_int_t
+ngx_http_uwsgi_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf)
+{
+ ngx_pool_cleanup_t *cln;
+
+ uwcf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));
+ if (uwcf->upstream.ssl == NULL) {
+ return NGX_ERROR;
+ }
+
+ uwcf->upstream.ssl->log = cf->log;
+
+ if (ngx_ssl_create(uwcf->upstream.ssl, uwcf->ssl_protocols, NULL)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_ssl_cleanup_ctx;
+ cln->data = uwcf->upstream.ssl;
+
+ if (SSL_CTX_set_cipher_list(uwcf->upstream.ssl->ctx,
+ (const char *) uwcf->ssl_ciphers.data)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
+ "SSL_CTX_set_cipher_list(\"%V\") failed",
+ &uwcf->ssl_ciphers);
+ return NGX_ERROR;
+ }
+
+ if (uwcf->upstream.ssl_verify) {
+ if (uwcf->ssl_trusted_certificate.len == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no uwsgi_ssl_trusted_certificate for uwsgi_ssl_verify");
+ return NGX_ERROR;
+ }
+
+ if (ngx_ssl_trusted_certificate(cf, uwcf->upstream.ssl,
+ &uwcf->ssl_trusted_certificate,
+ uwcf->ssl_verify_depth)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_ssl_crl(cf, uwcf->upstream.ssl, &uwcf->ssl_crl) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_xslt_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_xslt_filter_module.c
new file mode 100644
index 00000000000..315081e47bd
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/ngx_http_xslt_filter_module.c
@@ -0,0 +1,1147 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxslt/xslt.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/transform.h>
+#include <libxslt/variables.h>
+#include <libxslt/xsltutils.h>
+
+#if (NGX_HAVE_EXSLT)
+#include <libexslt/exslt.h>
+#endif
+
+
+#ifndef NGX_HTTP_XSLT_REUSE_DTD
+#define NGX_HTTP_XSLT_REUSE_DTD 1
+#endif
+
+
+typedef struct {
+ u_char *name;
+ void *data;
+} ngx_http_xslt_file_t;
+
+
+typedef struct {
+ ngx_array_t dtd_files; /* ngx_http_xslt_file_t */
+ ngx_array_t sheet_files; /* ngx_http_xslt_file_t */
+} ngx_http_xslt_filter_main_conf_t;
+
+
+typedef struct {
+ u_char *name;
+ ngx_http_complex_value_t value;
+ ngx_uint_t quote; /* unsigned quote:1; */
+} ngx_http_xslt_param_t;
+
+
+typedef struct {
+ xsltStylesheetPtr stylesheet;
+ ngx_array_t params; /* ngx_http_xslt_param_t */
+} ngx_http_xslt_sheet_t;
+
+
+typedef struct {
+ xmlDtdPtr dtd;
+ ngx_array_t sheets; /* ngx_http_xslt_sheet_t */
+ ngx_hash_t types;
+ ngx_array_t *types_keys;
+ ngx_array_t *params; /* ngx_http_xslt_param_t */
+ ngx_flag_t last_modified;
+} ngx_http_xslt_filter_loc_conf_t;
+
+
+typedef struct {
+ xmlDocPtr doc;
+ xmlParserCtxtPtr ctxt;
+ xsltTransformContextPtr transform;
+ ngx_http_request_t *request;
+ ngx_array_t params;
+
+ ngx_uint_t done; /* unsigned done:1; */
+} ngx_http_xslt_filter_ctx_t;
+
+
+static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r,
+ ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
+static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r,
+ ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
+
+
+static void ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
+ const xmlChar *externalId, const xmlChar *systemId);
+static void ngx_cdecl ngx_http_xslt_sax_error(void *data, const char *msg, ...);
+
+
+static ngx_buf_t *ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
+ ngx_http_xslt_filter_ctx_t *ctx);
+static ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r,
+ ngx_http_xslt_filter_ctx_t *ctx, ngx_array_t *params, ngx_uint_t final);
+static u_char *ngx_http_xslt_content_type(xsltStylesheetPtr s);
+static u_char *ngx_http_xslt_encoding(xsltStylesheetPtr s);
+static void ngx_http_xslt_cleanup(void *data);
+
+static char *ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static void ngx_http_xslt_cleanup_dtd(void *data);
+static void ngx_http_xslt_cleanup_stylesheet(void *data);
+static void *ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf);
+static void *ngx_http_xslt_filter_create_conf(ngx_conf_t *cf);
+static char *ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static ngx_int_t ngx_http_xslt_filter_preconfiguration(ngx_conf_t *cf);
+static ngx_int_t ngx_http_xslt_filter_init(ngx_conf_t *cf);
+static void ngx_http_xslt_filter_exit(ngx_cycle_t *cycle);
+
+
+ngx_str_t ngx_http_xslt_default_types[] = {
+ ngx_string("text/xml"),
+ ngx_null_string
+};
+
+
+static ngx_command_t ngx_http_xslt_filter_commands[] = {
+
+ { ngx_string("xml_entities"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_xslt_entities,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("xslt_stylesheet"),
+ NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_xslt_stylesheet,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("xslt_param"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_http_xslt_param,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("xslt_string_param"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_http_xslt_param,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ (void *) 1 },
+
+ { ngx_string("xslt_types"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_types_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_xslt_filter_loc_conf_t, types_keys),
+ &ngx_http_xslt_default_types[0] },
+
+ { ngx_string("xslt_last_modified"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_xslt_filter_loc_conf_t, last_modified),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_xslt_filter_module_ctx = {
+ ngx_http_xslt_filter_preconfiguration, /* preconfiguration */
+ ngx_http_xslt_filter_init, /* postconfiguration */
+
+ ngx_http_xslt_filter_create_main_conf, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_xslt_filter_create_conf, /* create location configuration */
+ ngx_http_xslt_filter_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_xslt_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_xslt_filter_module_ctx, /* module context */
+ ngx_http_xslt_filter_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ ngx_http_xslt_filter_exit, /* exit process */
+ ngx_http_xslt_filter_exit, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_xslt_header_filter(ngx_http_request_t *r)
+{
+ ngx_http_xslt_filter_ctx_t *ctx;
+ ngx_http_xslt_filter_loc_conf_t *conf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "xslt filter header");
+
+ if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
+
+ if (conf->sheets.nelts == 0
+ || ngx_http_test_content_type(r, &conf->types) == NULL)
+ {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
+
+ if (ctx) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module);
+
+ r->main_filter_need_in_memory = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ int wellFormed;
+ ngx_chain_t *cl;
+ ngx_http_xslt_filter_ctx_t *ctx;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "xslt filter body");
+
+ if (in == NULL) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);
+
+ if (ctx == NULL || ctx->done) {
+ return ngx_http_next_body_filter(r, in);
+ }
+
+ for (cl = in; cl; cl = cl->next) {
+
+ if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {
+
+ if (ctx->ctxt->myDoc) {
+
+#if (NGX_HTTP_XSLT_REUSE_DTD)
+ ctx->ctxt->myDoc->extSubset = NULL;
+#endif
+ xmlFreeDoc(ctx->ctxt->myDoc);
+ }
+
+ xmlFreeParserCtxt(ctx->ctxt);
+
+ return ngx_http_xslt_send(r, ctx, NULL);
+ }
+
+ if (cl->buf->last_buf || cl->buf->last_in_chain) {
+
+ ctx->doc = ctx->ctxt->myDoc;
+
+#if (NGX_HTTP_XSLT_REUSE_DTD)
+ ctx->doc->extSubset = NULL;
+#endif
+
+ wellFormed = ctx->ctxt->wellFormed;
+
+ xmlFreeParserCtxt(ctx->ctxt);
+
+ if (wellFormed) {
+ return ngx_http_xslt_send(r, ctx,
+ ngx_http_xslt_apply_stylesheet(r, ctx));
+ }
+
+ xmlFreeDoc(ctx->doc);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "not well formed XML document");
+
+ return ngx_http_xslt_send(r, ctx, NULL);
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
+ ngx_buf_t *b)
+{
+ ngx_int_t rc;
+ ngx_chain_t out;
+ ngx_pool_cleanup_t *cln;
+ ngx_http_xslt_filter_loc_conf_t *conf;
+
+ ctx->done = 1;
+
+ if (b == NULL) {
+ return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ }
+
+ cln = ngx_pool_cleanup_add(r->pool, 0);
+
+ if (cln == NULL) {
+ ngx_free(b->pos);
+ return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ }
+
+ if (r == r->main) {
+ r->headers_out.content_length_n = b->last - b->pos;
+
+ if (r->headers_out.content_length) {
+ r->headers_out.content_length->hash = 0;
+ r->headers_out.content_length = NULL;
+ }
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
+
+ if (!conf->last_modified) {
+ ngx_http_clear_last_modified(r);
+ ngx_http_clear_etag(r);
+
+ } else {
+ ngx_http_weak_etag(r);
+ }
+ }
+
+ rc = ngx_http_next_header_filter(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ ngx_free(b->pos);
+ return rc;
+ }
+
+ cln->handler = ngx_http_xslt_cleanup;
+ cln->data = b->pos;
+
+ out.buf = b;
+ out.next = NULL;
+
+ return ngx_http_next_body_filter(r, &out);
+}
+
+
+static ngx_int_t
+ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
+ ngx_buf_t *b)
+{
+ int err;
+ xmlParserCtxtPtr ctxt;
+
+ if (ctx->ctxt == NULL) {
+
+ ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
+ if (ctxt == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "xmlCreatePushParserCtxt() failed");
+ return NGX_ERROR;
+ }
+ xmlCtxtUseOptions(ctxt, XML_PARSE_NOENT|XML_PARSE_DTDLOAD
+ |XML_PARSE_NOWARNING);
+
+ ctxt->sax->externalSubset = ngx_http_xslt_sax_external_subset;
+ ctxt->sax->setDocumentLocator = NULL;
+ ctxt->sax->error = ngx_http_xslt_sax_error;
+ ctxt->sax->fatalError = ngx_http_xslt_sax_error;
+ ctxt->sax->_private = ctx;
+
+ ctx->ctxt = ctxt;
+ ctx->request = r;
+ }
+
+ err = xmlParseChunk(ctx->ctxt, (char *) b->pos, (int) (b->last - b->pos),
+ (b->last_buf) || (b->last_in_chain));
+
+ if (err == 0) {
+ b->pos = b->last;
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "xmlParseChunk() failed, error:%d", err);
+
+ return NGX_ERROR;
+}
+
+
+static void
+ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,
+ const xmlChar *externalId, const xmlChar *systemId)
+{
+ xmlParserCtxtPtr ctxt = data;
+
+ xmlDocPtr doc;
+ xmlDtdPtr dtd;
+ ngx_http_request_t *r;
+ ngx_http_xslt_filter_ctx_t *ctx;
+ ngx_http_xslt_filter_loc_conf_t *conf;
+
+ ctx = ctxt->sax->_private;
+ r = ctx->request;
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "xslt filter extSubset: \"%s\" \"%s\" \"%s\"",
+ name ? name : (xmlChar *) "",
+ externalId ? externalId : (xmlChar *) "",
+ systemId ? systemId : (xmlChar *) "");
+
+ doc = ctxt->myDoc;
+
+#if (NGX_HTTP_XSLT_REUSE_DTD)
+
+ dtd = conf->dtd;
+
+#else
+
+ dtd = xmlCopyDtd(conf->dtd);
+ if (dtd == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "xmlCopyDtd() failed");
+ return;
+ }
+
+ if (doc->children == NULL) {
+ xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
+
+ } else {
+ xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd);
+ }
+
+#endif
+
+ doc->extSubset = dtd;
+}
+
+
+static void ngx_cdecl
+ngx_http_xslt_sax_error(void *data, const char *msg, ...)
+{
+ xmlParserCtxtPtr ctxt = data;
+
+ size_t n;
+ va_list args;
+ ngx_http_xslt_filter_ctx_t *ctx;
+ u_char buf[NGX_MAX_ERROR_STR];
+
+ ctx = ctxt->sax->_private;
+
+ buf[0] = '\0';
+
+ va_start(args, msg);
+ n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args);
+ va_end(args);
+
+ while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }
+
+ ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
+ "libxml2 error: \"%*s\"", n + 1, buf);
+}
+
+
+static ngx_buf_t *
+ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,
+ ngx_http_xslt_filter_ctx_t *ctx)
+{
+ int len, rc, doc_type;
+ u_char *type, *encoding;
+ ngx_buf_t *b;
+ ngx_uint_t i;
+ xmlChar *buf;
+ xmlDocPtr doc, res;
+ ngx_http_xslt_sheet_t *sheet;
+ ngx_http_xslt_filter_loc_conf_t *conf;
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);
+ sheet = conf->sheets.elts;
+ doc = ctx->doc;
+
+ /* preallocate array for 4 params */
+
+ if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *))
+ != NGX_OK)
+ {
+ xmlFreeDoc(doc);
+ return NULL;
+ }
+
+ for (i = 0; i < conf->sheets.nelts; i++) {
+
+ ctx->transform = xsltNewTransformContext(sheet[i].stylesheet, doc);
+ if (ctx->transform == NULL) {
+ xmlFreeDoc(doc);
+ return NULL;
+ }
+
+ if (conf->params
+ && ngx_http_xslt_params(r, ctx, conf->params, 0) != NGX_OK)
+ {
+ xsltFreeTransformContext(ctx->transform);
+ xmlFreeDoc(doc);
+ return NULL;
+ }
+
+ if (ngx_http_xslt_params(r, ctx, &sheet[i].params, 1) != NGX_OK) {
+ xsltFreeTransformContext(ctx->transform);
+ xmlFreeDoc(doc);
+ return NULL;
+ }
+
+ res = xsltApplyStylesheetUser(sheet[i].stylesheet, doc,
+ ctx->params.elts, NULL, NULL,
+ ctx->transform);
+
+ xsltFreeTransformContext(ctx->transform);
+ xmlFreeDoc(doc);
+
+ if (res == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "xsltApplyStylesheet() failed");
+ return NULL;
+ }
+
+ doc = res;
+
+ /* reset array elements */
+ ctx->params.nelts = 0;
+ }
+
+ /* there must be at least one stylesheet */
+
+ if (r == r->main) {
+ type = ngx_http_xslt_content_type(sheet[i - 1].stylesheet);
+
+ } else {
+ type = NULL;
+ }
+
+ encoding = ngx_http_xslt_encoding(sheet[i - 1].stylesheet);
+ doc_type = doc->type;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "xslt filter type: %d t:%s e:%s",
+ doc_type, type ? type : (u_char *) "(null)",
+ encoding ? encoding : (u_char *) "(null)");
+
+ rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet);
+
+ xmlFreeDoc(doc);
+
+ if (rc != 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "xsltSaveResultToString() failed");
+ return NULL;
+ }
+
+ if (len == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "xsltSaveResultToString() returned zero-length result");
+ return NULL;
+ }
+
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ ngx_free(buf);
+ return NULL;
+ }
+
+ b->pos = buf;
+ b->last = buf + len;
+ b->memory = 1;
+
+ if (encoding) {
+ r->headers_out.charset.len = ngx_strlen(encoding);
+ r->headers_out.charset.data = encoding;
+ }
+
+ if (r != r->main) {
+ return b;
+ }
+
+ b->last_buf = 1;
+
+ if (type) {
+ len = ngx_strlen(type);
+
+ r->headers_out.content_type_len = len;
+ r->headers_out.content_type.len = len;
+ r->headers_out.content_type.data = type;
+
+ } else if (doc_type == XML_HTML_DOCUMENT_NODE) {
+
+ r->headers_out.content_type_len = sizeof("text/html") - 1;
+ ngx_str_set(&r->headers_out.content_type, "text/html");
+ }
+
+ r->headers_out.content_type_lowcase = NULL;
+
+ return b;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
+ ngx_array_t *params, ngx_uint_t final)
+{
+ u_char *p, *last, *value, *dst, *src, **s;
+ size_t len;
+ ngx_uint_t i;
+ ngx_str_t string;
+ ngx_http_xslt_param_t *param;
+
+ param = params->elts;
+
+ for (i = 0; i < params->nelts; i++) {
+
+ if (ngx_http_complex_value(r, &param[i].value, &string) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "xslt filter param: \"%s\"", string.data);
+
+ if (param[i].name) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "xslt filter param name: \"%s\"", param[i].name);
+
+ if (param[i].quote) {
+ if (xsltQuoteOneUserParam(ctx->transform, param[i].name,
+ string.data)
+ != 0)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "xsltQuoteOneUserParam(\"%s\", \"%s\") failed",
+ param[i].name, string.data);
+ return NGX_ERROR;
+ }
+
+ continue;
+ }
+
+ s = ngx_array_push(&ctx->params);
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ *s = param[i].name;
+
+ s = ngx_array_push(&ctx->params);
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ *s = string.data;
+
+ continue;
+ }
+
+ /*
+ * parse param1=value1:param2=value2 syntax as used by parameters
+ * specified in xslt_stylesheet directives
+ */
+
+ p = string.data;
+ last = string.data + string.len;
+
+ while (p && *p) {
+
+ value = p;
+ p = (u_char *) ngx_strchr(p, '=');
+ if (p == NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid libxslt parameter \"%s\"", value);
+ return NGX_ERROR;
+ }
+ *p++ = '\0';
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "xslt filter param name: \"%s\"", value);
+
+ s = ngx_array_push(&ctx->params);
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ *s = value;
+
+ value = p;
+ p = (u_char *) ngx_strchr(p, ':');
+
+ if (p) {
+ len = p - value;
+ *p++ = '\0';
+
+ } else {
+ len = last - value;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "xslt filter param value: \"%s\"", value);
+
+ dst = value;
+ src = value;
+
+ ngx_unescape_uri(&dst, &src, len, 0);
+
+ *dst = '\0';
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "xslt filter param unescaped: \"%s\"", value);
+
+ s = ngx_array_push(&ctx->params);
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ *s = value;
+ }
+ }
+
+ if (final) {
+ s = ngx_array_push(&ctx->params);
+ if (s == NULL) {
+ return NGX_ERROR;
+ }
+
+ *s = NULL;
+ }
+
+ return NGX_OK;
+}
+
+
+static u_char *
+ngx_http_xslt_content_type(xsltStylesheetPtr s)
+{
+ u_char *type;
+
+ if (s->mediaType) {
+ return s->mediaType;
+ }
+
+ for (s = s->imports; s; s = s->next) {
+
+ type = ngx_http_xslt_content_type(s);
+
+ if (type) {
+ return type;
+ }
+ }
+
+ return NULL;
+}
+
+
+static u_char *
+ngx_http_xslt_encoding(xsltStylesheetPtr s)
+{
+ u_char *encoding;
+
+ if (s->encoding) {
+ return s->encoding;
+ }
+
+ for (s = s->imports; s; s = s->next) {
+
+ encoding = ngx_http_xslt_encoding(s);
+
+ if (encoding) {
+ return encoding;
+ }
+ }
+
+ return NULL;
+}
+
+
+static void
+ngx_http_xslt_cleanup(void *data)
+{
+ ngx_free(data);
+}
+
+
+static char *
+ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
+
+ ngx_str_t *value;
+ ngx_uint_t i;
+ ngx_pool_cleanup_t *cln;
+ ngx_http_xslt_file_t *file;
+ ngx_http_xslt_filter_main_conf_t *xmcf;
+
+ if (xlcf->dtd) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
+
+ file = xmcf->dtd_files.elts;
+ for (i = 0; i < xmcf->dtd_files.nelts; i++) {
+ if (ngx_strcmp(file[i].name, value[1].data) == 0) {
+ xlcf->dtd = file[i].data;
+ return NGX_CONF_OK;
+ }
+ }
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);
+
+ if (xlcf->dtd == NULL) {
+ ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "xmlParseDTD() failed");
+ return NGX_CONF_ERROR;
+ }
+
+ cln->handler = ngx_http_xslt_cleanup_dtd;
+ cln->data = xlcf->dtd;
+
+ file = ngx_array_push(&xmcf->dtd_files);
+ if (file == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ file->name = value[1].data;
+ file->data = xlcf->dtd;
+
+ return NGX_CONF_OK;
+}
+
+
+
+static char *
+ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
+
+ ngx_str_t *value;
+ ngx_uint_t i, n;
+ ngx_pool_cleanup_t *cln;
+ ngx_http_xslt_file_t *file;
+ ngx_http_xslt_sheet_t *sheet;
+ ngx_http_xslt_param_t *param;
+ ngx_http_compile_complex_value_t ccv;
+ ngx_http_xslt_filter_main_conf_t *xmcf;
+
+ value = cf->args->elts;
+
+ if (xlcf->sheets.elts == NULL) {
+ if (ngx_array_init(&xlcf->sheets, cf->pool, 1,
+ sizeof(ngx_http_xslt_sheet_t))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ sheet = ngx_array_push(&xlcf->sheets);
+ if (sheet == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));
+
+ if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);
+
+ file = xmcf->sheet_files.elts;
+ for (i = 0; i < xmcf->sheet_files.nelts; i++) {
+ if (ngx_strcmp(file[i].name, value[1].data) == 0) {
+ sheet->stylesheet = file[i].data;
+ goto found;
+ }
+ }
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ sheet->stylesheet = xsltParseStylesheetFile(value[1].data);
+ if (sheet->stylesheet == NULL) {
+ ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
+ "xsltParseStylesheetFile(\"%s\") failed",
+ value[1].data);
+ return NGX_CONF_ERROR;
+ }
+
+ cln->handler = ngx_http_xslt_cleanup_stylesheet;
+ cln->data = sheet->stylesheet;
+
+ file = ngx_array_push(&xmcf->sheet_files);
+ if (file == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ file->name = value[1].data;
+ file->data = sheet->stylesheet;
+
+found:
+
+ n = cf->args->nelts;
+
+ if (n == 2) {
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_array_init(&sheet->params, cf->pool, n - 2,
+ sizeof(ngx_http_xslt_param_t))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ for (i = 2; i < n; i++) {
+
+ param = ngx_array_push(&sheet->params);
+ if (param == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(param, sizeof(ngx_http_xslt_param_t));
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[i];
+ ccv.complex_value = &param->value;
+ ccv.zero = 1;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_xslt_filter_loc_conf_t *xlcf = conf;
+
+ ngx_http_xslt_param_t *param;
+ ngx_http_compile_complex_value_t ccv;
+ ngx_str_t *value;
+
+ value = cf->args->elts;
+
+ if (xlcf->params == NULL) {
+ xlcf->params = ngx_array_create(cf->pool, 2,
+ sizeof(ngx_http_xslt_param_t));
+ if (xlcf->params == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ param = ngx_array_push(xlcf->params);
+ if (param == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ param->name = value[1].data;
+ param->quote = (cmd->post == NULL) ? 0 : 1;
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[2];
+ ccv.complex_value = &param->value;
+ ccv.zero = 1;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static void
+ngx_http_xslt_cleanup_dtd(void *data)
+{
+ xmlFreeDtd(data);
+}
+
+
+static void
+ngx_http_xslt_cleanup_stylesheet(void *data)
+{
+ xsltFreeStylesheet(data);
+}
+
+
+static void *
+ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_http_xslt_filter_main_conf_t *conf;
+
+ conf = ngx_palloc(cf->pool, sizeof(ngx_http_xslt_filter_main_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(&conf->dtd_files, cf->pool, 1,
+ sizeof(ngx_http_xslt_file_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ if (ngx_array_init(&conf->sheet_files, cf->pool, 1,
+ sizeof(ngx_http_xslt_file_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return conf;
+}
+
+
+static void *
+ngx_http_xslt_filter_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_xslt_filter_loc_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_loc_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->dtd = NULL;
+ * conf->sheets = { NULL };
+ * conf->types = { NULL };
+ * conf->types_keys = NULL;
+ * conf->params = NULL;
+ */
+
+ conf->last_modified = NGX_CONF_UNSET;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_xslt_filter_loc_conf_t *prev = parent;
+ ngx_http_xslt_filter_loc_conf_t *conf = child;
+
+ if (conf->dtd == NULL) {
+ conf->dtd = prev->dtd;
+ }
+
+ if (conf->sheets.nelts == 0) {
+ conf->sheets = prev->sheets;
+ }
+
+ if (conf->params == NULL) {
+ conf->params = prev->params;
+ }
+
+ if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
+ &prev->types_keys, &prev->types,
+ ngx_http_xslt_default_types)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_filter_preconfiguration(ngx_conf_t *cf)
+{
+ xmlInitParser();
+
+#if (NGX_HAVE_EXSLT)
+ exsltRegisterAll();
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_xslt_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_xslt_header_filter;
+
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_xslt_body_filter;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_xslt_filter_exit(ngx_cycle_t *cycle)
+{
+ xsltCleanupGlobals();
+ xmlCleanupParser();
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/Makefile.PL b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/Makefile.PL
new file mode 100644
index 00000000000..03348b555fc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/Makefile.PL
@@ -0,0 +1,33 @@
+
+# Copyright (C) Igor Sysoev
+# Copyright (C) Nginx, Inc.
+
+use 5.006001;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+ NAME => 'nginx',
+ VERSION_FROM => 'nginx.pm', # finds $VERSION
+ PREREQ_PM => {}, # e.g., Module::Name => 1.1
+
+ ABSTRACT_FROM => 'nginx.pm', # retrieve abstract from module
+ AUTHOR => 'Igor Sysoev',
+
+ CCFLAGS => "$ENV{NGX_PM_CFLAGS}",
+ OPTIMIZE => '-O',
+
+ INC => join(" ", map {
+ m#^/# ? "-I $_" : "-I ../../../../../$_"
+ } (split /\s+/, $ENV{NGX_INCS})),
+
+ depend => {
+ 'nginx.c' => join(" ", map {
+ m#^/# ? $_ : "../../../../../$_"
+ } (split(/\s+/, $ENV{NGX_DEPS}),
+ "src/http/modules/perl/ngx_http_perl_module.h"))
+ },
+
+ PM => {
+ 'nginx.pm' => '$(INST_LIBDIR)/nginx.pm'
+ }
+);
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/nginx.pm b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/nginx.pm
new file mode 100644
index 00000000000..e3f73611025
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/nginx.pm
@@ -0,0 +1,138 @@
+package nginx;
+
+use 5.006001;
+use strict;
+use warnings;
+
+require Exporter;
+
+our @ISA = qw(Exporter);
+
+our @EXPORT = qw(
+ OK
+ DECLINED
+
+ HTTP_OK
+ HTTP_CREATED
+ HTTP_ACCEPTED
+ HTTP_NO_CONTENT
+ HTTP_PARTIAL_CONTENT
+
+ HTTP_MOVED_PERMANENTLY
+ HTTP_MOVED_TEMPORARILY
+ HTTP_REDIRECT
+ HTTP_SEE_OTHER
+ HTTP_NOT_MODIFIED
+ HTTP_TEMPORARY_REDIRECT
+
+ HTTP_BAD_REQUEST
+ HTTP_UNAUTHORIZED
+ HTTP_PAYMENT_REQUIRED
+ HTTP_FORBIDDEN
+ HTTP_NOT_FOUND
+ HTTP_NOT_ALLOWED
+ HTTP_NOT_ACCEPTABLE
+ HTTP_REQUEST_TIME_OUT
+ HTTP_CONFLICT
+ HTTP_GONE
+ HTTP_LENGTH_REQUIRED
+ HTTP_REQUEST_ENTITY_TOO_LARGE
+ HTTP_REQUEST_URI_TOO_LARGE
+ HTTP_UNSUPPORTED_MEDIA_TYPE
+ HTTP_RANGE_NOT_SATISFIABLE
+
+ HTTP_INTERNAL_SERVER_ERROR
+ HTTP_SERVER_ERROR
+ HTTP_NOT_IMPLEMENTED
+ HTTP_BAD_GATEWAY
+ HTTP_SERVICE_UNAVAILABLE
+ HTTP_GATEWAY_TIME_OUT
+ HTTP_INSUFFICIENT_STORAGE
+);
+
+our $VERSION = '%%VERSION%%';
+
+require XSLoader;
+XSLoader::load('nginx', $VERSION);
+
+# Preloaded methods go here.
+
+use constant OK => 0;
+use constant DECLINED => -5;
+
+use constant HTTP_OK => 200;
+use constant HTTP_CREATED => 201;
+use constant HTTP_ACCEPTED => 202;
+use constant HTTP_NO_CONTENT => 204;
+use constant HTTP_PARTIAL_CONTENT => 206;
+
+use constant HTTP_MOVED_PERMANENTLY => 301;
+use constant HTTP_MOVED_TEMPORARILY => 302;
+use constant HTTP_REDIRECT => 302;
+use constant HTTP_SEE_OTHER => 303;
+use constant HTTP_NOT_MODIFIED => 304;
+use constant HTTP_TEMPORARY_REDIRECT => 307;
+
+use constant HTTP_BAD_REQUEST => 400;
+use constant HTTP_UNAUTHORIZED => 401;
+use constant HTTP_PAYMENT_REQUIRED => 402;
+use constant HTTP_FORBIDDEN => 403;
+use constant HTTP_NOT_FOUND => 404;
+use constant HTTP_NOT_ALLOWED => 405;
+use constant HTTP_NOT_ACCEPTABLE => 406;
+use constant HTTP_REQUEST_TIME_OUT => 408;
+use constant HTTP_CONFLICT => 409;
+use constant HTTP_GONE => 410;
+use constant HTTP_LENGTH_REQUIRED => 411;
+use constant HTTP_REQUEST_ENTITY_TOO_LARGE => 413;
+use constant HTTP_REQUEST_URI_TOO_LARGE => 414;
+use constant HTTP_UNSUPPORTED_MEDIA_TYPE => 415;
+use constant HTTP_RANGE_NOT_SATISFIABLE => 416;
+
+use constant HTTP_INTERNAL_SERVER_ERROR => 500;
+use constant HTTP_SERVER_ERROR => 500;
+use constant HTTP_NOT_IMPLEMENTED => 501;
+use constant HTTP_BAD_GATEWAY => 502;
+use constant HTTP_SERVICE_UNAVAILABLE => 503;
+use constant HTTP_GATEWAY_TIME_OUT => 504;
+use constant HTTP_INSUFFICIENT_STORAGE => 507;
+
+
+sub rflush {
+ my $r = shift;
+
+ $r->flush;
+}
+
+
+1;
+__END__
+
+=head1 NAME
+
+nginx - Perl interface to the nginx HTTP server API
+
+=head1 SYNOPSIS
+
+ use nginx;
+
+=head1 DESCRIPTION
+
+This module provides a Perl interface to the nginx HTTP server API.
+
+
+=head1 SEE ALSO
+
+http://nginx.org/en/docs/http/ngx_http_perl_module.html
+
+=head1 AUTHOR
+
+Igor Sysoev
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) Igor Sysoev
+Copyright (C) Nginx, Inc.
+
+
+=cut
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/nginx.xs b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/nginx.xs
new file mode 100644
index 00000000000..71f17a8bb45
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/nginx.xs
@@ -0,0 +1,1038 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#define PERL_NO_GET_CONTEXT
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_http_perl_module.h>
+
+#include "XSUB.h"
+
+
+#define ngx_http_perl_set_request(r) \
+ r = INT2PTR(ngx_http_request_t *, SvIV((SV *) SvRV(ST(0))))
+
+
+#define ngx_http_perl_set_targ(p, len) \
+ \
+ SvUPGRADE(TARG, SVt_PV); \
+ SvPOK_on(TARG); \
+ sv_setpvn(TARG, (char *) p, len)
+
+
+static ngx_int_t
+ngx_http_perl_sv2str(pTHX_ ngx_http_request_t *r, ngx_str_t *s, SV *sv)
+{
+ u_char *p;
+ STRLEN len;
+
+ if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PV) {
+ sv = SvRV(sv);
+ }
+
+ p = (u_char *) SvPV(sv, len);
+
+ s->len = len;
+
+ if (SvREADONLY(sv) && SvPOK(sv)) {
+ s->data = p;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "perl sv2str: %08XD \"%V\"", sv->sv_flags, s);
+
+ return NGX_OK;
+ }
+
+ s->data = ngx_pnalloc(r->pool, len);
+ if (s->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->data, p, len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "perl sv2str: %08XD \"%V\"", sv->sv_flags, s);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_perl_output(ngx_http_request_t *r, ngx_buf_t *b)
+{
+ ngx_chain_t out;
+#if (NGX_HTTP_SSI)
+ ngx_chain_t *cl;
+ ngx_http_perl_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
+
+ if (ctx->ssi) {
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ cl->next = NULL;
+ *ctx->ssi->last_out = cl;
+ ctx->ssi->last_out = &cl->next;
+
+ return NGX_OK;
+ }
+#endif
+
+ out.buf = b;
+ out.next = NULL;
+
+ return ngx_http_output_filter(r, &out);
+}
+
+
+MODULE = nginx PACKAGE = nginx
+
+
+void
+status(r, code)
+ CODE:
+
+ ngx_http_request_t *r;
+
+ ngx_http_perl_set_request(r);
+
+ r->headers_out.status = SvIV(ST(1));
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "perl status: %d", r->headers_out.status);
+
+ XSRETURN_UNDEF;
+
+
+void
+send_http_header(r, ...)
+ CODE:
+
+ ngx_http_request_t *r;
+ SV *sv;
+
+ ngx_http_perl_set_request(r);
+
+ if (r->headers_out.status == 0) {
+ r->headers_out.status = NGX_HTTP_OK;
+ }
+
+ if (items != 1) {
+ sv = ST(1);
+
+ if (ngx_http_perl_sv2str(aTHX_ r, &r->headers_out.content_type, sv)
+ != NGX_OK)
+ {
+ XSRETURN_EMPTY;
+ }
+
+ r->headers_out.content_type_len = r->headers_out.content_type.len;
+
+ } else {
+ if (ngx_http_set_content_type(r) != NGX_OK) {
+ XSRETURN_EMPTY;
+ }
+ }
+
+ (void) ngx_http_send_header(r);
+
+
+void
+header_only(r)
+ CODE:
+
+ dXSTARG;
+ ngx_http_request_t *r;
+
+ ngx_http_perl_set_request(r);
+
+ sv_upgrade(TARG, SVt_IV);
+ sv_setiv(TARG, r->header_only);
+
+ ST(0) = TARG;
+
+
+void
+uri(r)
+ CODE:
+
+ dXSTARG;
+ ngx_http_request_t *r;
+
+ ngx_http_perl_set_request(r);
+ ngx_http_perl_set_targ(r->uri.data, r->uri.len);
+
+ ST(0) = TARG;
+
+
+void
+args(r)
+ CODE:
+
+ dXSTARG;
+ ngx_http_request_t *r;
+
+ ngx_http_perl_set_request(r);
+ ngx_http_perl_set_targ(r->args.data, r->args.len);
+
+ ST(0) = TARG;
+
+
+void
+request_method(r)
+ CODE:
+
+ dXSTARG;
+ ngx_http_request_t *r;
+
+ ngx_http_perl_set_request(r);
+ ngx_http_perl_set_targ(r->method_name.data, r->method_name.len);
+
+ ST(0) = TARG;
+
+
+void
+remote_addr(r)
+ CODE:
+
+ dXSTARG;
+ ngx_http_request_t *r;
+
+ ngx_http_perl_set_request(r);
+ ngx_http_perl_set_targ(r->connection->addr_text.data,
+ r->connection->addr_text.len);
+
+ ST(0) = TARG;
+
+
+void
+header_in(r, key)
+ CODE:
+
+ dXSTARG;
+ ngx_http_request_t *r;
+ SV *key;
+ u_char *p, *lowcase_key, *value, sep;
+ STRLEN len;
+ ssize_t size;
+ ngx_uint_t i, n, hash;
+ ngx_array_t *a;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *h, **ph;
+ ngx_http_header_t *hh;
+ ngx_http_core_main_conf_t *cmcf;
+
+ ngx_http_perl_set_request(r);
+
+ key = ST(1);
+
+ if (SvROK(key) && SvTYPE(SvRV(key)) == SVt_PV) {
+ key = SvRV(key);
+ }
+
+ p = (u_char *) SvPV(key, len);
+
+ /* look up hashed headers */
+
+ lowcase_key = ngx_pnalloc(r->pool, len);
+ if (lowcase_key == NULL) {
+ XSRETURN_UNDEF;
+ }
+
+ hash = ngx_hash_strlow(lowcase_key, p, len);
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ hh = ngx_hash_find(&cmcf->headers_in_hash, hash, lowcase_key, len);
+
+ if (hh) {
+
+ if (hh->offset == offsetof(ngx_http_headers_in_t, cookies)) {
+ sep = ';';
+ goto multi;
+ }
+#if (NGX_HTTP_X_FORWARDED_FOR)
+ if (hh->offset == offsetof(ngx_http_headers_in_t, x_forwarded_for)) {
+ sep = ',';
+ goto multi;
+ }
+#endif
+
+ if (hh->offset) {
+
+ ph = (ngx_table_elt_t **) ((char *) &r->headers_in + hh->offset);
+
+ if (*ph) {
+ ngx_http_perl_set_targ((*ph)->value.data, (*ph)->value.len);
+
+ goto done;
+ }
+
+ XSRETURN_UNDEF;
+ }
+
+ multi:
+
+ /* Cookie, X-Forwarded-For */
+
+ a = (ngx_array_t *) ((char *) &r->headers_in + hh->offset);
+
+ n = a->nelts;
+
+ if (n == 0) {
+ XSRETURN_UNDEF;
+ }
+
+ ph = a->elts;
+
+ if (n == 1) {
+ ngx_http_perl_set_targ((*ph)->value.data, (*ph)->value.len);
+
+ goto done;
+ }
+
+ size = - (ssize_t) (sizeof("; ") - 1);
+
+ for (i = 0; i < n; i++) {
+ size += ph[i]->value.len + sizeof("; ") - 1;
+ }
+
+ value = ngx_pnalloc(r->pool, size);
+ if (value == NULL) {
+ XSRETURN_UNDEF;
+ }
+
+ p = value;
+
+ for (i = 0; /* void */ ; i++) {
+ p = ngx_copy(p, ph[i]->value.data, ph[i]->value.len);
+
+ if (i == n - 1) {
+ break;
+ }
+
+ *p++ = sep; *p++ = ' ';
+ }
+
+ ngx_http_perl_set_targ(value, size);
+
+ goto done;
+ }
+
+ /* iterate over all headers */
+
+ part = &r->headers_in.headers.part;
+ h = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ h = part->elts;
+ i = 0;
+ }
+
+ if (len != h[i].key.len
+ || ngx_strcasecmp(p, h[i].key.data) != 0)
+ {
+ continue;
+ }
+
+ ngx_http_perl_set_targ(h[i].value.data, h[i].value.len);
+
+ goto done;
+ }
+
+ XSRETURN_UNDEF;
+
+ done:
+
+ ST(0) = TARG;
+
+
+void
+has_request_body(r, next)
+ CODE:
+
+ dXSTARG;
+ ngx_http_request_t *r;
+ ngx_http_perl_ctx_t *ctx;
+
+ ngx_http_perl_set_request(r);
+
+ if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
+ XSRETURN_UNDEF;
+ }
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
+ ctx->next = SvRV(ST(1));
+
+ r->request_body_in_single_buf = 1;
+ r->request_body_in_persistent_file = 1;
+ r->request_body_in_clean_file = 1;
+
+ if (r->request_body_in_file_only) {
+ r->request_body_file_log_level = 0;
+ }
+
+ ngx_http_read_client_request_body(r, ngx_http_perl_handle_request);
+
+ sv_upgrade(TARG, SVt_IV);
+ sv_setiv(TARG, 1);
+
+ ST(0) = TARG;
+
+
+void
+request_body(r)
+ CODE:
+
+ dXSTARG;
+ ngx_http_request_t *r;
+ u_char *p, *data;
+ size_t len;
+ ngx_buf_t *buf;
+ ngx_chain_t *cl;
+
+ ngx_http_perl_set_request(r);
+
+ if (r->request_body == NULL
+ || r->request_body->temp_file
+ || r->request_body->bufs == NULL)
+ {
+ XSRETURN_UNDEF;
+ }
+
+ cl = r->request_body->bufs;
+ buf = cl->buf;
+
+ if (cl->next == NULL) {
+ len = buf->last - buf->pos;
+ data = buf->pos;
+ goto done;
+ }
+
+ len = buf->last - buf->pos;
+ cl = cl->next;
+
+ for ( /* void */ ; cl; cl = cl->next) {
+ buf = cl->buf;
+ len += buf->last - buf->pos;
+ }
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ XSRETURN_UNDEF;
+ }
+
+ data = p;
+ cl = r->request_body->bufs;
+
+ for ( /* void */ ; cl; cl = cl->next) {
+ buf = cl->buf;
+ p = ngx_cpymem(p, buf->pos, buf->last - buf->pos);
+ }
+
+ done:
+
+ if (len == 0) {
+ XSRETURN_UNDEF;
+ }
+
+ ngx_http_perl_set_targ(data, len);
+
+ ST(0) = TARG;
+
+
+void
+request_body_file(r)
+ CODE:
+
+ dXSTARG;
+ ngx_http_request_t *r;
+
+ ngx_http_perl_set_request(r);
+
+ if (r->request_body == NULL || r->request_body->temp_file == NULL) {
+ XSRETURN_UNDEF;
+ }
+
+ ngx_http_perl_set_targ(r->request_body->temp_file->file.name.data,
+ r->request_body->temp_file->file.name.len);
+
+ ST(0) = TARG;
+
+
+void
+discard_request_body(r)
+ CODE:
+
+ ngx_http_request_t *r;
+
+ ngx_http_perl_set_request(r);
+
+ ngx_http_discard_request_body(r);
+
+
+void
+header_out(r, key, value)
+ CODE:
+
+ ngx_http_request_t *r;
+ SV *key;
+ SV *value;
+ ngx_table_elt_t *header;
+
+ ngx_http_perl_set_request(r);
+
+ key = ST(1);
+ value = ST(2);
+
+ header = ngx_list_push(&r->headers_out.headers);
+ if (header == NULL) {
+ XSRETURN_EMPTY;
+ }
+
+ header->hash = 1;
+
+ if (ngx_http_perl_sv2str(aTHX_ r, &header->key, key) != NGX_OK) {
+ XSRETURN_EMPTY;
+ }
+
+ if (ngx_http_perl_sv2str(aTHX_ r, &header->value, value) != NGX_OK) {
+ XSRETURN_EMPTY;
+ }
+
+ if (header->key.len == sizeof("Content-Length") - 1
+ && ngx_strncasecmp(header->key.data, (u_char *) "Content-Length",
+ sizeof("Content-Length") - 1) == 0)
+ {
+ r->headers_out.content_length_n = (off_t) SvIV(value);
+ r->headers_out.content_length = header;
+ }
+
+ if (header->key.len == sizeof("Content-Encoding") - 1
+ && ngx_strncasecmp(header->key.data, (u_char *) "Content-Encoding",
+ sizeof("Content-Encoding") - 1) == 0)
+ {
+ r->headers_out.content_encoding = header;
+ }
+
+
+void
+filename(r)
+ CODE:
+
+ dXSTARG;
+ size_t root;
+ ngx_http_request_t *r;
+ ngx_http_perl_ctx_t *ctx;
+
+ ngx_http_perl_set_request(r);
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
+ if (ctx->filename.data) {
+ goto done;
+ }
+
+ if (ngx_http_map_uri_to_path(r, &ctx->filename, &root, 0) == NULL) {
+ XSRETURN_UNDEF;
+ }
+
+ ctx->filename.len--;
+ sv_setpv(PL_statname, (char *) ctx->filename.data);
+
+ done:
+
+ ngx_http_perl_set_targ(ctx->filename.data, ctx->filename.len);
+
+ ST(0) = TARG;
+
+
+void
+print(r, ...)
+ CODE:
+
+ ngx_http_request_t *r;
+ SV *sv;
+ int i;
+ u_char *p;
+ size_t size;
+ STRLEN len;
+ ngx_buf_t *b;
+
+ ngx_http_perl_set_request(r);
+
+ if (items == 2) {
+
+ /*
+ * do zero copy for prolate single read-only SV:
+ * $r->print("some text\n");
+ */
+
+ sv = ST(1);
+
+ if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PV) {
+ sv = SvRV(sv);
+ }
+
+ if (SvREADONLY(sv) && SvPOK(sv)) {
+
+ p = (u_char *) SvPV(sv, len);
+
+ if (len == 0) {
+ XSRETURN_EMPTY;
+ }
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ XSRETURN_EMPTY;
+ }
+
+ b->memory = 1;
+ b->pos = p;
+ b->last = p + len;
+ b->start = p;
+ b->end = b->last;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "$r->print: read-only SV: %z", len);
+
+ goto out;
+ }
+ }
+
+ size = 0;
+
+ for (i = 1; i < items; i++) {
+
+ sv = ST(i);
+
+ if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PV) {
+ sv = SvRV(sv);
+ }
+
+ (void) SvPV(sv, len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "$r->print: copy SV: %z", len);
+
+ size += len;
+ }
+
+ if (size == 0) {
+ XSRETURN_EMPTY;
+ }
+
+ b = ngx_create_temp_buf(r->pool, size);
+ if (b == NULL) {
+ XSRETURN_EMPTY;
+ }
+
+ for (i = 1; i < items; i++) {
+ sv = ST(i);
+
+ if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PV) {
+ sv = SvRV(sv);
+ }
+
+ p = (u_char *) SvPV(sv, len);
+ b->last = ngx_cpymem(b->last, p, len);
+ }
+
+ out:
+
+ (void) ngx_http_perl_output(r, b);
+
+
+void
+sendfile(r, filename, offset = -1, bytes = 0)
+ CODE:
+
+ ngx_http_request_t *r;
+ char *filename;
+ off_t offset;
+ size_t bytes;
+ ngx_str_t path;
+ ngx_buf_t *b;
+ ngx_open_file_info_t of;
+ ngx_http_core_loc_conf_t *clcf;
+
+ ngx_http_perl_set_request(r);
+
+ filename = SvPV_nolen(ST(1));
+
+ if (filename == NULL) {
+ croak("sendfile(): NULL filename");
+ }
+
+ offset = items < 3 ? -1 : SvIV(ST(2));
+ bytes = items < 4 ? 0 : SvIV(ST(3));
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ XSRETURN_EMPTY;
+ }
+
+ b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
+ if (b->file == NULL) {
+ XSRETURN_EMPTY;
+ }
+
+ path.len = ngx_strlen(filename);
+
+ path.data = ngx_pnalloc(r->pool, path.len + 1);
+ if (path.data == NULL) {
+ XSRETURN_EMPTY;
+ }
+
+ (void) ngx_cpystrn(path.data, (u_char *) filename, path.len + 1);
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+ of.read_ahead = clcf->read_ahead;
+ of.directio = clcf->directio;
+ of.valid = clcf->open_file_cache_valid;
+ of.min_uses = clcf->open_file_cache_min_uses;
+ of.errors = clcf->open_file_cache_errors;
+ of.events = clcf->open_file_cache_events;
+
+ if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+ XSRETURN_EMPTY;
+ }
+
+ if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+ != NGX_OK)
+ {
+ if (of.err == 0) {
+ XSRETURN_EMPTY;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+ "%s \"%s\" failed", of.failed, filename);
+ XSRETURN_EMPTY;
+ }
+
+ if (offset == -1) {
+ offset = 0;
+ }
+
+ if (bytes == 0) {
+ bytes = of.size - offset;
+ }
+
+ b->in_file = 1;
+
+ b->file_pos = offset;
+ b->file_last = offset + bytes;
+
+ b->file->fd = of.fd;
+ b->file->log = r->connection->log;
+ b->file->directio = of.is_directio;
+
+ (void) ngx_http_perl_output(r, b);
+
+
+void
+flush(r)
+ CODE:
+
+ ngx_http_request_t *r;
+ ngx_buf_t *b;
+
+ ngx_http_perl_set_request(r);
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ XSRETURN_EMPTY;
+ }
+
+ b->flush = 1;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "$r->flush");
+
+ (void) ngx_http_perl_output(r, b);
+
+ XSRETURN_EMPTY;
+
+
+void
+internal_redirect(r, uri)
+ CODE:
+
+ ngx_http_request_t *r;
+ SV *uri;
+ ngx_uint_t i;
+ ngx_http_perl_ctx_t *ctx;
+
+ ngx_http_perl_set_request(r);
+
+ uri = ST(1);
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
+
+ if (ngx_http_perl_sv2str(aTHX_ r, &ctx->redirect_uri, uri) != NGX_OK) {
+ XSRETURN_EMPTY;
+ }
+
+ for (i = 0; i < ctx->redirect_uri.len; i++) {
+ if (ctx->redirect_uri.data[i] == '?') {
+
+ ctx->redirect_args.len = ctx->redirect_uri.len - (i + 1);
+ ctx->redirect_args.data = &ctx->redirect_uri.data[i + 1];
+ ctx->redirect_uri.len = i;
+
+ XSRETURN_EMPTY;
+ }
+ }
+
+
+void
+allow_ranges(r)
+ CODE:
+
+ ngx_http_request_t *r;
+
+ ngx_http_perl_set_request(r);
+
+ r->allow_ranges = 1;
+
+
+void
+unescape(r, text, type = 0)
+ CODE:
+
+ dXSTARG;
+ ngx_http_request_t *r;
+ SV *text;
+ int type;
+ u_char *p, *dst, *src;
+ STRLEN len;
+
+ ngx_http_perl_set_request(r);
+
+ text = ST(1);
+
+ src = (u_char *) SvPV(text, len);
+
+ p = ngx_pnalloc(r->pool, len + 1);
+ if (p == NULL) {
+ XSRETURN_UNDEF;
+ }
+
+ dst = p;
+
+ type = items < 3 ? 0 : SvIV(ST(2));
+
+ ngx_unescape_uri(&dst, &src, len, (ngx_uint_t) type);
+ *dst = '\0';
+
+ ngx_http_perl_set_targ(p, dst - p);
+
+ ST(0) = TARG;
+
+
+void
+variable(r, name, value = NULL)
+ CODE:
+
+ dXSTARG;
+ ngx_http_request_t *r;
+ SV *name, *value;
+ u_char *p, *lowcase;
+ STRLEN len;
+ ngx_str_t var, val;
+ ngx_uint_t i, hash;
+ ngx_http_perl_var_t *v;
+ ngx_http_perl_ctx_t *ctx;
+ ngx_http_variable_value_t *vv;
+
+ ngx_http_perl_set_request(r);
+
+ name = ST(1);
+
+ if (SvROK(name) && SvTYPE(SvRV(name)) == SVt_PV) {
+ name = SvRV(name);
+ }
+
+ if (items == 2) {
+ value = NULL;
+
+ } else {
+ value = ST(2);
+
+ if (SvROK(value) && SvTYPE(SvRV(value)) == SVt_PV) {
+ value = SvRV(value);
+ }
+
+ if (ngx_http_perl_sv2str(aTHX_ r, &val, value) != NGX_OK) {
+ XSRETURN_UNDEF;
+ }
+ }
+
+ p = (u_char *) SvPV(name, len);
+
+ lowcase = ngx_pnalloc(r->pool, len);
+ if (lowcase == NULL) {
+ XSRETURN_UNDEF;
+ }
+
+ hash = ngx_hash_strlow(lowcase, p, len);
+
+ var.len = len;
+ var.data = lowcase;
+#if (NGX_DEBUG)
+
+ if (value) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "perl variable: \"%V\"=\"%V\"", &var, &val);
+ } else {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "perl variable: \"%V\"", &var);
+ }
+#endif
+
+ vv = ngx_http_get_variable(r, &var, hash);
+ if (vv == NULL) {
+ XSRETURN_UNDEF;
+ }
+
+ if (vv->not_found) {
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
+
+ if (ctx->variables) {
+
+ v = ctx->variables->elts;
+ for (i = 0; i < ctx->variables->nelts; i++) {
+
+ if (hash != v[i].hash
+ || len != v[i].name.len
+ || ngx_strncmp(lowcase, v[i].name.data, len) != 0)
+ {
+ continue;
+ }
+
+ if (value) {
+ v[i].value = val;
+ XSRETURN_UNDEF;
+ }
+
+ ngx_http_perl_set_targ(v[i].value.data, v[i].value.len);
+
+ goto done;
+ }
+ }
+
+ if (value) {
+ if (ctx->variables == NULL) {
+ ctx->variables = ngx_array_create(r->pool, 1,
+ sizeof(ngx_http_perl_var_t));
+ if (ctx->variables == NULL) {
+ XSRETURN_UNDEF;
+ }
+ }
+
+ v = ngx_array_push(ctx->variables);
+ if (v == NULL) {
+ XSRETURN_UNDEF;
+ }
+
+ v->hash = hash;
+ v->name.len = len;
+ v->name.data = lowcase;
+ v->value = val;
+
+ XSRETURN_UNDEF;
+ }
+
+ XSRETURN_UNDEF;
+ }
+
+ if (value) {
+ vv->len = val.len;
+ vv->valid = 1;
+ vv->no_cacheable = 0;
+ vv->not_found = 0;
+ vv->data = val.data;
+
+ XSRETURN_UNDEF;
+ }
+
+ ngx_http_perl_set_targ(vv->data, vv->len);
+
+ done:
+
+ ST(0) = TARG;
+
+
+void
+sleep(r, sleep, next)
+ CODE:
+
+ ngx_http_request_t *r;
+ ngx_msec_t sleep;
+ ngx_http_perl_ctx_t *ctx;
+
+ ngx_http_perl_set_request(r);
+
+ sleep = (ngx_msec_t) SvIV(ST(1));
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "perl sleep: %M", sleep);
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
+
+ ctx->next = SvRV(ST(2));
+
+ ngx_add_timer(r->connection->write, sleep);
+
+ r->write_event_handler = ngx_http_perl_sleep_handler;
+ r->main->count++;
+
+
+void
+log_error(r, err, msg)
+ CODE:
+
+ ngx_http_request_t *r;
+ SV *err, *msg;
+ u_char *p;
+ STRLEN len;
+ ngx_err_t e;
+
+ ngx_http_perl_set_request(r);
+
+ err = ST(1);
+
+ if (SvROK(err) && SvTYPE(SvRV(err)) == SVt_PV) {
+ err = SvRV(err);
+ }
+
+ e = SvIV(err);
+
+ msg = ST(2);
+
+ if (SvROK(msg) && SvTYPE(SvRV(msg)) == SVt_PV) {
+ msg = SvRV(msg);
+ }
+
+ p = (u_char *) SvPV(msg, len);
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, e, "perl: %s", p);
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/ngx_http_perl_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/ngx_http_perl_module.c
new file mode 100644
index 00000000000..bf4d1fe9ad7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/ngx_http_perl_module.c
@@ -0,0 +1,1076 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_http_perl_module.h>
+
+
+typedef struct {
+ PerlInterpreter *perl;
+ HV *nginx;
+ ngx_array_t *modules;
+ ngx_array_t *requires;
+} ngx_http_perl_main_conf_t;
+
+
+typedef struct {
+ SV *sub;
+ ngx_str_t handler;
+} ngx_http_perl_loc_conf_t;
+
+
+typedef struct {
+ SV *sub;
+ ngx_str_t handler;
+} ngx_http_perl_variable_t;
+
+
+#if (NGX_HTTP_SSI)
+static ngx_int_t ngx_http_perl_ssi(ngx_http_request_t *r,
+ ngx_http_ssi_ctx_t *ssi_ctx, ngx_str_t **params);
+#endif
+
+static char *ngx_http_perl_init_interpreter(ngx_conf_t *cf,
+ ngx_http_perl_main_conf_t *pmcf);
+static PerlInterpreter *ngx_http_perl_create_interpreter(ngx_conf_t *cf,
+ ngx_http_perl_main_conf_t *pmcf);
+static ngx_int_t ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires,
+ ngx_log_t *log);
+static ngx_int_t ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,
+ HV *nginx, SV *sub, SV **args, ngx_str_t *handler, ngx_str_t *rv);
+static void ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv);
+
+static ngx_int_t ngx_http_perl_preconfiguration(ngx_conf_t *cf);
+static void *ngx_http_perl_create_main_conf(ngx_conf_t *cf);
+static char *ngx_http_perl_init_main_conf(ngx_conf_t *cf, void *conf);
+static void *ngx_http_perl_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_perl_merge_loc_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_http_perl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_http_perl_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+
+#if (NGX_HAVE_PERL_MULTIPLICITY)
+static void ngx_http_perl_cleanup_perl(void *data);
+#endif
+
+static ngx_int_t ngx_http_perl_init_worker(ngx_cycle_t *cycle);
+static void ngx_http_perl_exit(ngx_cycle_t *cycle);
+
+
+static ngx_command_t ngx_http_perl_commands[] = {
+
+ { ngx_string("perl_modules"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ offsetof(ngx_http_perl_main_conf_t, modules),
+ NULL },
+
+ { ngx_string("perl_require"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ offsetof(ngx_http_perl_main_conf_t, requires),
+ NULL },
+
+ { ngx_string("perl"),
+ NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
+ ngx_http_perl,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("perl_set"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,
+ ngx_http_perl_set,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_perl_module_ctx = {
+ ngx_http_perl_preconfiguration, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ ngx_http_perl_create_main_conf, /* create main configuration */
+ ngx_http_perl_init_main_conf, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_perl_create_loc_conf, /* create location configuration */
+ ngx_http_perl_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_perl_module = {
+ NGX_MODULE_V1,
+ &ngx_http_perl_module_ctx, /* module context */
+ ngx_http_perl_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ ngx_http_perl_init_worker, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ ngx_http_perl_exit, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+#if (NGX_HTTP_SSI)
+
+#define NGX_HTTP_PERL_SSI_SUB 0
+#define NGX_HTTP_PERL_SSI_ARG 1
+
+
+static ngx_http_ssi_param_t ngx_http_perl_ssi_params[] = {
+ { ngx_string("sub"), NGX_HTTP_PERL_SSI_SUB, 1, 0 },
+ { ngx_string("arg"), NGX_HTTP_PERL_SSI_ARG, 0, 1 },
+ { ngx_null_string, 0, 0, 0 }
+};
+
+static ngx_http_ssi_command_t ngx_http_perl_ssi_command = {
+ ngx_string("perl"), ngx_http_perl_ssi, ngx_http_perl_ssi_params, 0, 0, 1
+};
+
+#endif
+
+
+static ngx_str_t ngx_null_name = ngx_null_string;
+static HV *nginx_stash;
+
+#if (NGX_HAVE_PERL_MULTIPLICITY)
+static ngx_uint_t ngx_perl_term;
+#else
+static PerlInterpreter *perl;
+#endif
+
+
+static void
+ngx_http_perl_xs_init(pTHX)
+{
+ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
+
+ nginx_stash = gv_stashpv("nginx", TRUE);
+}
+
+
+static ngx_int_t
+ngx_http_perl_handler(ngx_http_request_t *r)
+{
+ r->main->count++;
+
+ ngx_http_perl_handle_request(r);
+
+ return NGX_DONE;
+}
+
+
+void
+ngx_http_perl_handle_request(ngx_http_request_t *r)
+{
+ SV *sub;
+ ngx_int_t rc;
+ ngx_str_t uri, args, *handler;
+ ngx_http_perl_ctx_t *ctx;
+ ngx_http_perl_loc_conf_t *plcf;
+ ngx_http_perl_main_conf_t *pmcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "perl handler");
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
+
+ if (ctx == NULL) {
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));
+ if (ctx == NULL) {
+ ngx_http_finalize_request(r, NGX_ERROR);
+ return;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
+ }
+
+ pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);
+
+ {
+
+ dTHXa(pmcf->perl);
+ PERL_SET_CONTEXT(pmcf->perl);
+
+ if (ctx->next == NULL) {
+ plcf = ngx_http_get_module_loc_conf(r, ngx_http_perl_module);
+ sub = plcf->sub;
+ handler = &plcf->handler;
+
+ } else {
+ sub = ctx->next;
+ handler = &ngx_null_name;
+ ctx->next = NULL;
+ }
+
+ rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, sub, NULL, handler,
+ NULL);
+
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "perl handler done: %i", rc);
+
+ if (rc == NGX_DONE) {
+ ngx_http_finalize_request(r, rc);
+ return;
+ }
+
+ if (rc > 600) {
+ rc = NGX_OK;
+ }
+
+ if (ctx->redirect_uri.len) {
+ uri = ctx->redirect_uri;
+ args = ctx->redirect_args;
+
+ } else {
+ uri.len = 0;
+ }
+
+ ctx->filename.data = NULL;
+ ctx->redirect_uri.len = 0;
+
+ if (ctx->done || ctx->next) {
+ ngx_http_finalize_request(r, NGX_DONE);
+ return;
+ }
+
+ if (uri.len) {
+ ngx_http_internal_redirect(r, &uri, &args);
+ ngx_http_finalize_request(r, NGX_DONE);
+ return;
+ }
+
+ if (rc == NGX_OK || rc == NGX_HTTP_OK) {
+ ngx_http_send_special(r, NGX_HTTP_LAST);
+ ctx->done = 1;
+ }
+
+ ngx_http_finalize_request(r, rc);
+}
+
+
+void
+ngx_http_perl_sleep_handler(ngx_http_request_t *r)
+{
+ ngx_event_t *wev;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "perl sleep handler");
+
+ wev = r->connection->write;
+
+ if (wev->timedout) {
+ wev->timedout = 0;
+ ngx_http_perl_handle_request(r);
+ return;
+ }
+
+ if (ngx_handle_write_event(wev, 0) != NGX_OK) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ }
+}
+
+
+static ngx_int_t
+ngx_http_perl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_http_perl_variable_t *pv = (ngx_http_perl_variable_t *) data;
+
+ ngx_int_t rc;
+ ngx_str_t value;
+ ngx_http_perl_ctx_t *ctx;
+ ngx_http_perl_main_conf_t *pmcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "perl variable handler");
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
+
+ if (ctx == NULL) {
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
+ }
+
+ pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);
+
+ value.data = NULL;
+
+ {
+
+ dTHXa(pmcf->perl);
+ PERL_SET_CONTEXT(pmcf->perl);
+
+ rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, pv->sub, NULL,
+ &pv->handler, &value);
+
+ }
+
+ if (value.data) {
+ v->len = value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = value.data;
+
+ } else {
+ v->not_found = 1;
+ }
+
+ ctx->filename.data = NULL;
+ ctx->redirect_uri.len = 0;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "perl variable done");
+
+ return rc;
+}
+
+
+#if (NGX_HTTP_SSI)
+
+static ngx_int_t
+ngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx,
+ ngx_str_t **params)
+{
+ SV *sv, **asv;
+ ngx_int_t rc;
+ ngx_str_t *handler, **args;
+ ngx_uint_t i;
+ ngx_http_perl_ctx_t *ctx;
+ ngx_http_perl_main_conf_t *pmcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "perl ssi handler");
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
+
+ if (ctx == NULL) {
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
+ }
+
+ pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);
+
+ ctx->ssi = ssi_ctx;
+
+ handler = params[NGX_HTTP_PERL_SSI_SUB];
+ handler->data[handler->len] = '\0';
+
+ {
+
+ dTHXa(pmcf->perl);
+ PERL_SET_CONTEXT(pmcf->perl);
+
+#if 0
+
+ /* the code is disabled to force the precompiled perl code using only */
+
+ ngx_http_perl_eval_anon_sub(aTHX_ handler, &sv);
+
+ if (sv == &PL_sv_undef) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "eval_pv(\"%V\") failed", handler);
+ return NGX_ERROR;
+ }
+
+ if (sv == NULL) {
+ sv = newSVpvn((char *) handler->data, handler->len);
+ }
+
+#endif
+
+ sv = newSVpvn((char *) handler->data, handler->len);
+
+ args = &params[NGX_HTTP_PERL_SSI_ARG];
+
+ if (args) {
+
+ for (i = 0; args[i]; i++) { /* void */ }
+
+ asv = ngx_pcalloc(r->pool, (i + 1) * sizeof(SV *));
+
+ if (asv == NULL) {
+ SvREFCNT_dec(sv);
+ return NGX_ERROR;
+ }
+
+ asv[0] = (SV *) (uintptr_t) i;
+
+ for (i = 0; args[i]; i++) {
+ asv[i + 1] = newSVpvn((char *) args[i]->data, args[i]->len);
+ }
+
+ } else {
+ asv = NULL;
+ }
+
+ rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, sv, asv, handler,
+ NULL);
+
+ SvREFCNT_dec(sv);
+
+ }
+
+ ctx->filename.data = NULL;
+ ctx->redirect_uri.len = 0;
+ ctx->ssi = NULL;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "perl ssi done");
+
+ return rc;
+}
+
+#endif
+
+
+static char *
+ngx_http_perl_init_interpreter(ngx_conf_t *cf, ngx_http_perl_main_conf_t *pmcf)
+{
+ ngx_str_t *m;
+ ngx_uint_t i;
+#if (NGX_HAVE_PERL_MULTIPLICITY)
+ ngx_pool_cleanup_t *cln;
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+#endif
+
+#ifdef NGX_PERL_MODULES
+ if (pmcf->modules == NGX_CONF_UNSET_PTR) {
+
+ pmcf->modules = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t));
+ if (pmcf->modules == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ m = ngx_array_push(pmcf->modules);
+ if (m == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_str_set(m, NGX_PERL_MODULES);
+ }
+#endif
+
+ if (pmcf->modules != NGX_CONF_UNSET_PTR) {
+ m = pmcf->modules->elts;
+ for (i = 0; i < pmcf->modules->nelts; i++) {
+ if (ngx_conf_full_name(cf->cycle, &m[i], 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+ }
+
+#if !(NGX_HAVE_PERL_MULTIPLICITY)
+
+ if (perl) {
+
+ if (ngx_set_environment(cf->cycle, NULL) == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_http_perl_run_requires(aTHX_ pmcf->requires, cf->log)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ pmcf->perl = perl;
+ pmcf->nginx = nginx_stash;
+
+ return NGX_CONF_OK;
+ }
+
+#endif
+
+ if (nginx_stash == NULL) {
+ PERL_SYS_INIT(&ngx_argc, &ngx_argv);
+ }
+
+ pmcf->perl = ngx_http_perl_create_interpreter(cf, pmcf);
+
+ if (pmcf->perl == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ pmcf->nginx = nginx_stash;
+
+#if (NGX_HAVE_PERL_MULTIPLICITY)
+
+ cln->handler = ngx_http_perl_cleanup_perl;
+ cln->data = pmcf->perl;
+
+#else
+
+ perl = pmcf->perl;
+
+#endif
+
+ return NGX_CONF_OK;
+}
+
+
+static PerlInterpreter *
+ngx_http_perl_create_interpreter(ngx_conf_t *cf,
+ ngx_http_perl_main_conf_t *pmcf)
+{
+ int n;
+ STRLEN len;
+ SV *sv;
+ char *ver, **embedding;
+ ngx_str_t *m;
+ ngx_uint_t i;
+ PerlInterpreter *perl;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, "create perl interpreter");
+
+ if (ngx_set_environment(cf->cycle, NULL) == NULL) {
+ return NULL;
+ }
+
+ perl = perl_alloc();
+ if (perl == NULL) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, 0, "perl_alloc() failed");
+ return NULL;
+ }
+
+ {
+
+ dTHXa(perl);
+ PERL_SET_CONTEXT(perl);
+
+ perl_construct(perl);
+
+#ifdef PERL_EXIT_DESTRUCT_END
+ PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
+#endif
+
+ n = (pmcf->modules != NGX_CONF_UNSET_PTR) ? pmcf->modules->nelts * 2 : 0;
+
+ embedding = ngx_palloc(cf->pool, (4 + n) * sizeof(char *));
+ if (embedding == NULL) {
+ goto fail;
+ }
+
+ embedding[0] = "";
+
+ if (n++) {
+ m = pmcf->modules->elts;
+ for (i = 0; i < pmcf->modules->nelts; i++) {
+ embedding[2 * i + 1] = "-I";
+ embedding[2 * i + 2] = (char *) m[i].data;
+ }
+ }
+
+ embedding[n++] = "-Mnginx";
+ embedding[n++] = "-e";
+ embedding[n++] = "0";
+
+ n = perl_parse(perl, ngx_http_perl_xs_init, n, embedding, NULL);
+
+ if (n != 0) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, 0, "perl_parse() failed: %d", n);
+ goto fail;
+ }
+
+ sv = get_sv("nginx::VERSION", FALSE);
+ ver = SvPV(sv, len);
+
+ if (ngx_strcmp(ver, NGINX_VERSION) != 0) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, 0,
+ "version " NGINX_VERSION " of nginx.pm is required, "
+ "but %s was found", ver);
+ goto fail;
+ }
+
+ if (ngx_http_perl_run_requires(aTHX_ pmcf->requires, cf->log) != NGX_OK) {
+ goto fail;
+ }
+
+ }
+
+ return perl;
+
+fail:
+
+ (void) perl_destruct(perl);
+
+ perl_free(perl);
+
+ return NULL;
+}
+
+
+static ngx_int_t
+ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires, ngx_log_t *log)
+{
+ u_char *err;
+ STRLEN len;
+ ngx_str_t *script;
+ ngx_uint_t i;
+
+ if (requires == NGX_CONF_UNSET_PTR) {
+ return NGX_OK;
+ }
+
+ script = requires->elts;
+ for (i = 0; i < requires->nelts; i++) {
+
+ require_pv((char *) script[i].data);
+
+ if (SvTRUE(ERRSV)) {
+
+ err = (u_char *) SvPV(ERRSV, len);
+ while (--len && (err[len] == CR || err[len] == LF)) { /* void */ }
+
+ ngx_log_error(NGX_LOG_EMERG, log, 0,
+ "require_pv(\"%s\") failed: \"%*s\"",
+ script[i].data, len + 1, err);
+
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r, HV *nginx, SV *sub,
+ SV **args, ngx_str_t *handler, ngx_str_t *rv)
+{
+ SV *sv;
+ int n, status;
+ char *line;
+ u_char *err;
+ STRLEN len, n_a;
+ ngx_uint_t i;
+ ngx_connection_t *c;
+
+ dSP;
+
+ status = 0;
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK(sp);
+
+ sv = sv_2mortal(sv_bless(newRV_noinc(newSViv(PTR2IV(r))), nginx));
+ XPUSHs(sv);
+
+ if (args) {
+ EXTEND(sp, (intptr_t) args[0]);
+
+ for (i = 1; i <= (uintptr_t) args[0]; i++) {
+ PUSHs(sv_2mortal(args[i]));
+ }
+ }
+
+ PUTBACK;
+
+ c = r->connection;
+
+ n = call_sv(sub, G_EVAL);
+
+ SPAGAIN;
+
+ if (n) {
+ if (rv == NULL) {
+ status = POPi;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "call_sv: %d", status);
+
+ } else {
+ line = SvPVx(POPs, n_a);
+ rv->len = n_a;
+
+ rv->data = ngx_pnalloc(r->pool, n_a);
+ if (rv->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(rv->data, line, n_a);
+ }
+ }
+
+ PUTBACK;
+
+ FREETMPS;
+ LEAVE;
+
+ /* check $@ */
+
+ if (SvTRUE(ERRSV)) {
+
+ err = (u_char *) SvPV(ERRSV, len);
+ while (--len && (err[len] == CR || err[len] == LF)) { /* void */ }
+
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "call_sv(\"%V\") failed: \"%*s\"", handler, len + 1, err);
+
+ if (rv) {
+ return NGX_ERROR;
+ }
+
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (n != 1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "call_sv(\"%V\") returned %d results", handler, n);
+ status = NGX_OK;
+ }
+
+ if (rv) {
+ return NGX_OK;
+ }
+
+ return (ngx_int_t) status;
+}
+
+
+static void
+ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv)
+{
+ u_char *p;
+
+ for (p = handler->data; *p; p++) {
+ if (*p != ' ' && *p != '\t' && *p != CR && *p != LF) {
+ break;
+ }
+ }
+
+ if (ngx_strncmp(p, "sub ", 4) == 0
+ || ngx_strncmp(p, "sub{", 4) == 0
+ || ngx_strncmp(p, "use ", 4) == 0)
+ {
+ *sv = eval_pv((char *) p, FALSE);
+
+ /* eval_pv() does not set ERRSV on failure */
+
+ return;
+ }
+
+ *sv = NULL;
+}
+
+
+static void *
+ngx_http_perl_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_http_perl_main_conf_t *pmcf;
+
+ pmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_main_conf_t));
+ if (pmcf == NULL) {
+ return NULL;
+ }
+
+ pmcf->modules = NGX_CONF_UNSET_PTR;
+ pmcf->requires = NGX_CONF_UNSET_PTR;
+
+ return pmcf;
+}
+
+
+static char *
+ngx_http_perl_init_main_conf(ngx_conf_t *cf, void *conf)
+{
+ ngx_http_perl_main_conf_t *pmcf = conf;
+
+ if (pmcf->perl == NULL) {
+ if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+#if (NGX_HAVE_PERL_MULTIPLICITY)
+
+static void
+ngx_http_perl_cleanup_perl(void *data)
+{
+ PerlInterpreter *perl = data;
+
+ PERL_SET_CONTEXT(perl);
+
+ (void) perl_destruct(perl);
+
+ perl_free(perl);
+
+ if (ngx_perl_term) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "perl term");
+
+ PERL_SYS_TERM();
+ }
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_perl_preconfiguration(ngx_conf_t *cf)
+{
+#if (NGX_HTTP_SSI)
+ ngx_int_t rc;
+ ngx_http_ssi_main_conf_t *smcf;
+
+ smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_ssi_filter_module);
+
+ rc = ngx_hash_add_key(&smcf->commands, &ngx_http_perl_ssi_command.name,
+ &ngx_http_perl_ssi_command, NGX_HASH_READONLY_KEY);
+
+ if (rc != NGX_OK) {
+ if (rc == NGX_BUSY) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "conflicting SSI command \"%V\"",
+ &ngx_http_perl_ssi_command.name);
+ }
+
+ return NGX_ERROR;
+ }
+#endif
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_perl_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_perl_loc_conf_t *plcf;
+
+ plcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_loc_conf_t));
+ if (plcf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * plcf->handler = { 0, NULL };
+ */
+
+ return plcf;
+}
+
+
+static char *
+ngx_http_perl_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_perl_loc_conf_t *prev = parent;
+ ngx_http_perl_loc_conf_t *conf = child;
+
+ if (conf->sub == NULL) {
+ conf->sub = prev->sub;
+ conf->handler = prev->handler;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_perl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_perl_loc_conf_t *plcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_perl_main_conf_t *pmcf;
+
+ value = cf->args->elts;
+
+ if (plcf->handler.data) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate perl handler \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ pmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_perl_module);
+
+ if (pmcf->perl == NULL) {
+ if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ plcf->handler = value[1];
+
+ {
+
+ dTHXa(pmcf->perl);
+ PERL_SET_CONTEXT(pmcf->perl);
+
+ ngx_http_perl_eval_anon_sub(aTHX_ &value[1], &plcf->sub);
+
+ if (plcf->sub == &PL_sv_undef) {
+ ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
+ "eval_pv(\"%V\") failed", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (plcf->sub == NULL) {
+ plcf->sub = newSVpvn((char *) value[1].data, value[1].len);
+ }
+
+ }
+
+ clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+ clcf->handler = ngx_http_perl_handler;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_perl_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_int_t index;
+ ngx_str_t *value;
+ ngx_http_variable_t *v;
+ ngx_http_perl_variable_t *pv;
+ ngx_http_perl_main_conf_t *pmcf;
+
+ value = cf->args->elts;
+
+ if (value[1].data[0] != '$') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ value[1].len--;
+ value[1].data++;
+
+ v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
+ if (v == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ pv = ngx_palloc(cf->pool, sizeof(ngx_http_perl_variable_t));
+ if (pv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ index = ngx_http_get_variable_index(cf, &value[1]);
+ if (index == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ pmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_perl_module);
+
+ if (pmcf->perl == NULL) {
+ if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ pv->handler = value[2];
+
+ {
+
+ dTHXa(pmcf->perl);
+ PERL_SET_CONTEXT(pmcf->perl);
+
+ ngx_http_perl_eval_anon_sub(aTHX_ &value[2], &pv->sub);
+
+ if (pv->sub == &PL_sv_undef) {
+ ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
+ "eval_pv(\"%V\") failed", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (pv->sub == NULL) {
+ pv->sub = newSVpvn((char *) value[2].data, value[2].len);
+ }
+
+ }
+
+ v->get_handler = ngx_http_perl_variable;
+ v->data = (uintptr_t) pv;
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_perl_init_worker(ngx_cycle_t *cycle)
+{
+ ngx_http_perl_main_conf_t *pmcf;
+
+ pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_perl_module);
+
+ if (pmcf) {
+ dTHXa(pmcf->perl);
+ PERL_SET_CONTEXT(pmcf->perl);
+
+ /* set worker's $$ */
+
+ sv_setiv(GvSV(gv_fetchpv("$", TRUE, SVt_PV)), (I32) ngx_pid);
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_perl_exit(ngx_cycle_t *cycle)
+{
+#if (NGX_HAVE_PERL_MULTIPLICITY)
+
+ /*
+ * the master exit hook is run before global pool cleanup,
+ * therefore just set flag here
+ */
+
+ ngx_perl_term = 1;
+
+#else
+
+ if (nginx_stash) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cycle->log, 0, "perl term");
+
+ (void) perl_destruct(perl);
+
+ perl_free(perl);
+
+ PERL_SYS_TERM();
+ }
+
+#endif
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/ngx_http_perl_module.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/ngx_http_perl_module.h
new file mode 100644
index 00000000000..5e60b031ec2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/ngx_http_perl_module.h
@@ -0,0 +1,67 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_PERL_MODULE_H_INCLUDED_
+#define _NGX_HTTP_PERL_MODULE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <nginx.h>
+
+#include <EXTERN.h>
+#include <perl.h>
+
+
+typedef ngx_http_request_t *nginx;
+
+typedef struct {
+ ngx_str_t filename;
+ ngx_str_t redirect_uri;
+ ngx_str_t redirect_args;
+
+ SV *next;
+
+ ngx_uint_t done; /* unsigned done:1; */
+
+ ngx_array_t *variables; /* array of ngx_http_perl_var_t */
+
+#if (NGX_HTTP_SSI)
+ ngx_http_ssi_ctx_t *ssi;
+#endif
+} ngx_http_perl_ctx_t;
+
+
+typedef struct {
+ ngx_uint_t hash;
+ ngx_str_t name;
+ ngx_str_t value;
+} ngx_http_perl_var_t;
+
+
+extern ngx_module_t ngx_http_perl_module;
+
+
+/*
+ * workaround for "unused variable `Perl___notused'" warning
+ * when building with perl 5.6.1
+ */
+#ifndef PERL_IMPLICIT_CONTEXT
+#undef dTHXa
+#define dTHXa(a)
+#endif
+
+
+extern void boot_DynaLoader(pTHX_ CV* cv);
+
+
+void ngx_http_perl_handle_request(ngx_http_request_t *r);
+void ngx_http_perl_sleep_handler(ngx_http_request_t *r);
+
+
+#endif /* _NGX_HTTP_PERL_MODULE_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/typemap b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/typemap
new file mode 100644
index 00000000000..e2f1a4c6af6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/modules/perl/typemap
@@ -0,0 +1,3 @@
+TYPEMAP
+
+nginx T_PTROBJ
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http.c
new file mode 100644
index 00000000000..31577f9a28e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http.c
@@ -0,0 +1,2118 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static char *ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static ngx_int_t ngx_http_init_phases(ngx_conf_t *cf,
+ ngx_http_core_main_conf_t *cmcf);
+static ngx_int_t ngx_http_init_headers_in_hash(ngx_conf_t *cf,
+ ngx_http_core_main_conf_t *cmcf);
+static ngx_int_t ngx_http_init_phase_handlers(ngx_conf_t *cf,
+ ngx_http_core_main_conf_t *cmcf);
+
+static ngx_int_t ngx_http_add_addresses(ngx_conf_t *cf,
+ ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port,
+ ngx_http_listen_opt_t *lsopt);
+static ngx_int_t ngx_http_add_address(ngx_conf_t *cf,
+ ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port,
+ ngx_http_listen_opt_t *lsopt);
+static ngx_int_t ngx_http_add_server(ngx_conf_t *cf,
+ ngx_http_core_srv_conf_t *cscf, ngx_http_conf_addr_t *addr);
+
+static char *ngx_http_merge_servers(ngx_conf_t *cf,
+ ngx_http_core_main_conf_t *cmcf, ngx_http_module_t *module,
+ ngx_uint_t ctx_index);
+static char *ngx_http_merge_locations(ngx_conf_t *cf,
+ ngx_queue_t *locations, void **loc_conf, ngx_http_module_t *module,
+ ngx_uint_t ctx_index);
+static ngx_int_t ngx_http_init_locations(ngx_conf_t *cf,
+ ngx_http_core_srv_conf_t *cscf, ngx_http_core_loc_conf_t *pclcf);
+static ngx_int_t ngx_http_init_static_location_trees(ngx_conf_t *cf,
+ ngx_http_core_loc_conf_t *pclcf);
+static ngx_int_t ngx_http_cmp_locations(const ngx_queue_t *one,
+ const ngx_queue_t *two);
+static ngx_int_t ngx_http_join_exact_locations(ngx_conf_t *cf,
+ ngx_queue_t *locations);
+static void ngx_http_create_locations_list(ngx_queue_t *locations,
+ ngx_queue_t *q);
+static ngx_http_location_tree_node_t *
+ ngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations,
+ size_t prefix);
+
+static ngx_int_t ngx_http_optimize_servers(ngx_conf_t *cf,
+ ngx_http_core_main_conf_t *cmcf, ngx_array_t *ports);
+static ngx_int_t ngx_http_server_names(ngx_conf_t *cf,
+ ngx_http_core_main_conf_t *cmcf, ngx_http_conf_addr_t *addr);
+static ngx_int_t ngx_http_cmp_conf_addrs(const void *one, const void *two);
+static int ngx_libc_cdecl ngx_http_cmp_dns_wildcards(const void *one,
+ const void *two);
+
+static ngx_int_t ngx_http_init_listening(ngx_conf_t *cf,
+ ngx_http_conf_port_t *port);
+static ngx_listening_t *ngx_http_add_listening(ngx_conf_t *cf,
+ ngx_http_conf_addr_t *addr);
+static ngx_int_t ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,
+ ngx_http_conf_addr_t *addr);
+#if (NGX_HAVE_INET6)
+static ngx_int_t ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,
+ ngx_http_conf_addr_t *addr);
+#endif
+
+ngx_uint_t ngx_http_max_module;
+
+
+ngx_int_t (*ngx_http_top_header_filter) (ngx_http_request_t *r);
+ngx_int_t (*ngx_http_top_body_filter) (ngx_http_request_t *r, ngx_chain_t *ch);
+
+
+ngx_str_t ngx_http_html_default_types[] = {
+ ngx_string("text/html"),
+ ngx_null_string
+};
+
+
+static ngx_command_t ngx_http_commands[] = {
+
+ { ngx_string("http"),
+ NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_http_block,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_core_module_t ngx_http_module_ctx = {
+ ngx_string("http"),
+ NULL,
+ NULL
+};
+
+
+ngx_module_t ngx_http_module = {
+ NGX_MODULE_V1,
+ &ngx_http_module_ctx, /* module context */
+ ngx_http_commands, /* module directives */
+ NGX_CORE_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static char *
+ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ ngx_uint_t mi, m, s;
+ ngx_conf_t pcf;
+ ngx_http_module_t *module;
+ ngx_http_conf_ctx_t *ctx;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_srv_conf_t **cscfp;
+ ngx_http_core_main_conf_t *cmcf;
+
+ /* the main http context */
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *(ngx_http_conf_ctx_t **) conf = ctx;
+
+
+ /* count the number of the http modules and set up their indices */
+
+ ngx_http_max_module = 0;
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
+ continue;
+ }
+
+ ngx_modules[m]->ctx_index = ngx_http_max_module++;
+ }
+
+
+ /* the http main_conf context, it is the same in the all http contexts */
+
+ ctx->main_conf = ngx_pcalloc(cf->pool,
+ sizeof(void *) * ngx_http_max_module);
+ if (ctx->main_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /*
+ * the http null srv_conf context, it is used to merge
+ * the server{}s' srv_conf's
+ */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /*
+ * the http null loc_conf context, it is used to merge
+ * the server{}s' loc_conf's
+ */
+
+ ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
+ if (ctx->loc_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /*
+ * create the main_conf's, the null srv_conf's, and the null loc_conf's
+ * of the all http modules
+ */
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+ mi = ngx_modules[m]->ctx_index;
+
+ if (module->create_main_conf) {
+ ctx->main_conf[mi] = module->create_main_conf(cf);
+ if (ctx->main_conf[mi] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (module->create_srv_conf) {
+ ctx->srv_conf[mi] = module->create_srv_conf(cf);
+ if (ctx->srv_conf[mi] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (module->create_loc_conf) {
+ ctx->loc_conf[mi] = module->create_loc_conf(cf);
+ if (ctx->loc_conf[mi] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+ }
+
+ pcf = *cf;
+ cf->ctx = ctx;
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+
+ if (module->preconfiguration) {
+ if (module->preconfiguration(cf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+ }
+
+ /* parse inside the http{} block */
+
+ cf->module_type = NGX_HTTP_MODULE;
+ cf->cmd_type = NGX_HTTP_MAIN_CONF;
+ rv = ngx_conf_parse(cf, NULL);
+
+ if (rv != NGX_CONF_OK) {
+ goto failed;
+ }
+
+ /*
+ * init http{} main_conf's, merge the server{}s' srv_conf's
+ * and its location{}s' loc_conf's
+ */
+
+ cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
+ cscfp = cmcf->servers.elts;
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+ mi = ngx_modules[m]->ctx_index;
+
+ /* init http{} main_conf's */
+
+ if (module->init_main_conf) {
+ rv = module->init_main_conf(cf, ctx->main_conf[mi]);
+ if (rv != NGX_CONF_OK) {
+ goto failed;
+ }
+ }
+
+ rv = ngx_http_merge_servers(cf, cmcf, module, mi);
+ if (rv != NGX_CONF_OK) {
+ goto failed;
+ }
+ }
+
+
+ /* create location trees */
+
+ for (s = 0; s < cmcf->servers.nelts; s++) {
+
+ clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
+
+ if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+
+ if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+
+ if (module->postconfiguration) {
+ if (module->postconfiguration(cf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+ }
+
+ if (ngx_http_variables_init_vars(cf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ /*
+ * http{}'s cf->ctx was needed while the configuration merging
+ * and in postconfiguration process
+ */
+
+ *cf = pcf;
+
+
+ if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /* optimize the lists of ports, addresses and server names */
+
+ if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+
+failed:
+
+ *cf = pcf;
+
+ return rv;
+}
+
+
+static ngx_int_t
+ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
+{
+ if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,
+ cf->pool, 1, sizeof(ngx_http_handler_pt))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers,
+ cf->pool, 1, sizeof(ngx_http_handler_pt))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,
+ cf->pool, 1, sizeof(ngx_http_handler_pt))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers,
+ cf->pool, 1, sizeof(ngx_http_handler_pt))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,
+ cf->pool, 2, sizeof(ngx_http_handler_pt))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,
+ cf->pool, 4, sizeof(ngx_http_handler_pt))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers,
+ cf->pool, 1, sizeof(ngx_http_handler_pt))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_init_headers_in_hash(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
+{
+ ngx_array_t headers_in;
+ ngx_hash_key_t *hk;
+ ngx_hash_init_t hash;
+ ngx_http_header_t *header;
+
+ if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ for (header = ngx_http_headers_in; header->name.len; header++) {
+ hk = ngx_array_push(&headers_in);
+ if (hk == NULL) {
+ return NGX_ERROR;
+ }
+
+ hk->key = header->name;
+ hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);
+ hk->value = header;
+ }
+
+ hash.hash = &cmcf->headers_in_hash;
+ hash.key = ngx_hash_key_lc;
+ hash.max_size = 512;
+ hash.bucket_size = ngx_align(64, ngx_cacheline_size);
+ hash.name = "headers_in_hash";
+ hash.pool = cf->pool;
+ hash.temp_pool = NULL;
+
+ if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
+{
+ ngx_int_t j;
+ ngx_uint_t i, n;
+ ngx_uint_t find_config_index, use_rewrite, use_access;
+ ngx_http_handler_pt *h;
+ ngx_http_phase_handler_t *ph;
+ ngx_http_phase_handler_pt checker;
+
+ cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;
+ cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;
+ find_config_index = 0;
+ use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;
+ use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;
+
+ n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */;
+
+ for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
+ n += cmcf->phases[i].handlers.nelts;
+ }
+
+ ph = ngx_pcalloc(cf->pool,
+ n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));
+ if (ph == NULL) {
+ return NGX_ERROR;
+ }
+
+ cmcf->phase_engine.handlers = ph;
+ n = 0;
+
+ for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
+ h = cmcf->phases[i].handlers.elts;
+
+ switch (i) {
+
+ case NGX_HTTP_SERVER_REWRITE_PHASE:
+ if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {
+ cmcf->phase_engine.server_rewrite_index = n;
+ }
+ checker = ngx_http_core_rewrite_phase;
+
+ break;
+
+ case NGX_HTTP_FIND_CONFIG_PHASE:
+ find_config_index = n;
+
+ ph->checker = ngx_http_core_find_config_phase;
+ n++;
+ ph++;
+
+ continue;
+
+ case NGX_HTTP_REWRITE_PHASE:
+ if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {
+ cmcf->phase_engine.location_rewrite_index = n;
+ }
+ checker = ngx_http_core_rewrite_phase;
+
+ break;
+
+ case NGX_HTTP_POST_REWRITE_PHASE:
+ if (use_rewrite) {
+ ph->checker = ngx_http_core_post_rewrite_phase;
+ ph->next = find_config_index;
+ n++;
+ ph++;
+ }
+
+ continue;
+
+ case NGX_HTTP_ACCESS_PHASE:
+ checker = ngx_http_core_access_phase;
+ n++;
+ break;
+
+ case NGX_HTTP_POST_ACCESS_PHASE:
+ if (use_access) {
+ ph->checker = ngx_http_core_post_access_phase;
+ ph->next = n;
+ ph++;
+ }
+
+ continue;
+
+ case NGX_HTTP_TRY_FILES_PHASE:
+ if (cmcf->try_files) {
+ ph->checker = ngx_http_core_try_files_phase;
+ n++;
+ ph++;
+ }
+
+ continue;
+
+ case NGX_HTTP_CONTENT_PHASE:
+ checker = ngx_http_core_content_phase;
+ break;
+
+ default:
+ checker = ngx_http_core_generic_phase;
+ }
+
+ n += cmcf->phases[i].handlers.nelts;
+
+ for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--) {
+ ph->checker = checker;
+ ph->handler = h[j];
+ ph->next = n;
+ ph++;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_http_merge_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
+ ngx_http_module_t *module, ngx_uint_t ctx_index)
+{
+ char *rv;
+ ngx_uint_t s;
+ ngx_http_conf_ctx_t *ctx, saved;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_srv_conf_t **cscfp;
+
+ cscfp = cmcf->servers.elts;
+ ctx = (ngx_http_conf_ctx_t *) cf->ctx;
+ saved = *ctx;
+ rv = NGX_CONF_OK;
+
+ for (s = 0; s < cmcf->servers.nelts; s++) {
+
+ /* merge the server{}s' srv_conf's */
+
+ ctx->srv_conf = cscfp[s]->ctx->srv_conf;
+
+ if (module->merge_srv_conf) {
+ rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index],
+ cscfp[s]->ctx->srv_conf[ctx_index]);
+ if (rv != NGX_CONF_OK) {
+ goto failed;
+ }
+ }
+
+ if (module->merge_loc_conf) {
+
+ /* merge the server{}'s loc_conf */
+
+ ctx->loc_conf = cscfp[s]->ctx->loc_conf;
+
+ rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index],
+ cscfp[s]->ctx->loc_conf[ctx_index]);
+ if (rv != NGX_CONF_OK) {
+ goto failed;
+ }
+
+ /* merge the locations{}' loc_conf's */
+
+ clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
+
+ rv = ngx_http_merge_locations(cf, clcf->locations,
+ cscfp[s]->ctx->loc_conf,
+ module, ctx_index);
+ if (rv != NGX_CONF_OK) {
+ goto failed;
+ }
+ }
+ }
+
+failed:
+
+ *ctx = saved;
+
+ return rv;
+}
+
+
+static char *
+ngx_http_merge_locations(ngx_conf_t *cf, ngx_queue_t *locations,
+ void **loc_conf, ngx_http_module_t *module, ngx_uint_t ctx_index)
+{
+ char *rv;
+ ngx_queue_t *q;
+ ngx_http_conf_ctx_t *ctx, saved;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_location_queue_t *lq;
+
+ if (locations == NULL) {
+ return NGX_CONF_OK;
+ }
+
+ ctx = (ngx_http_conf_ctx_t *) cf->ctx;
+ saved = *ctx;
+
+ for (q = ngx_queue_head(locations);
+ q != ngx_queue_sentinel(locations);
+ q = ngx_queue_next(q))
+ {
+ lq = (ngx_http_location_queue_t *) q;
+
+ clcf = lq->exact ? lq->exact : lq->inclusive;
+ ctx->loc_conf = clcf->loc_conf;
+
+ rv = module->merge_loc_conf(cf, loc_conf[ctx_index],
+ clcf->loc_conf[ctx_index]);
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+
+ rv = ngx_http_merge_locations(cf, clcf->locations, clcf->loc_conf,
+ module, ctx_index);
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+ }
+
+ *ctx = saved;
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_init_locations(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+ ngx_http_core_loc_conf_t *pclcf)
+{
+ ngx_uint_t n;
+ ngx_queue_t *q, *locations, *named, tail;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_location_queue_t *lq;
+ ngx_http_core_loc_conf_t **clcfp;
+#if (NGX_PCRE)
+ ngx_uint_t r;
+ ngx_queue_t *regex;
+#endif
+
+ locations = pclcf->locations;
+
+ if (locations == NULL) {
+ return NGX_OK;
+ }
+
+ ngx_queue_sort(locations, ngx_http_cmp_locations);
+
+ named = NULL;
+ n = 0;
+#if (NGX_PCRE)
+ regex = NULL;
+ r = 0;
+#endif
+
+ for (q = ngx_queue_head(locations);
+ q != ngx_queue_sentinel(locations);
+ q = ngx_queue_next(q))
+ {
+ lq = (ngx_http_location_queue_t *) q;
+
+ clcf = lq->exact ? lq->exact : lq->inclusive;
+
+ if (ngx_http_init_locations(cf, NULL, clcf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+#if (NGX_PCRE)
+
+ if (clcf->regex) {
+ r++;
+
+ if (regex == NULL) {
+ regex = q;
+ }
+
+ continue;
+ }
+
+#endif
+
+ if (clcf->named) {
+ n++;
+
+ if (named == NULL) {
+ named = q;
+ }
+
+ continue;
+ }
+
+ if (clcf->noname) {
+ break;
+ }
+ }
+
+ if (q != ngx_queue_sentinel(locations)) {
+ ngx_queue_split(locations, q, &tail);
+ }
+
+ if (named) {
+ clcfp = ngx_palloc(cf->pool,
+ (n + 1) * sizeof(ngx_http_core_loc_conf_t *));
+ if (clcfp == NULL) {
+ return NGX_ERROR;
+ }
+
+ cscf->named_locations = clcfp;
+
+ for (q = named;
+ q != ngx_queue_sentinel(locations);
+ q = ngx_queue_next(q))
+ {
+ lq = (ngx_http_location_queue_t *) q;
+
+ *(clcfp++) = lq->exact;
+ }
+
+ *clcfp = NULL;
+
+ ngx_queue_split(locations, named, &tail);
+ }
+
+#if (NGX_PCRE)
+
+ if (regex) {
+
+ clcfp = ngx_palloc(cf->pool,
+ (r + 1) * sizeof(ngx_http_core_loc_conf_t *));
+ if (clcfp == NULL) {
+ return NGX_ERROR;
+ }
+
+ pclcf->regex_locations = clcfp;
+
+ for (q = regex;
+ q != ngx_queue_sentinel(locations);
+ q = ngx_queue_next(q))
+ {
+ lq = (ngx_http_location_queue_t *) q;
+
+ *(clcfp++) = lq->exact;
+ }
+
+ *clcfp = NULL;
+
+ ngx_queue_split(locations, regex, &tail);
+ }
+
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_init_static_location_trees(ngx_conf_t *cf,
+ ngx_http_core_loc_conf_t *pclcf)
+{
+ ngx_queue_t *q, *locations;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_location_queue_t *lq;
+
+ locations = pclcf->locations;
+
+ if (locations == NULL) {
+ return NGX_OK;
+ }
+
+ if (ngx_queue_empty(locations)) {
+ return NGX_OK;
+ }
+
+ for (q = ngx_queue_head(locations);
+ q != ngx_queue_sentinel(locations);
+ q = ngx_queue_next(q))
+ {
+ lq = (ngx_http_location_queue_t *) q;
+
+ clcf = lq->exact ? lq->exact : lq->inclusive;
+
+ if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (ngx_http_join_exact_locations(cf, locations) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_create_locations_list(locations, ngx_queue_head(locations));
+
+ pclcf->static_locations = ngx_http_create_locations_tree(cf, locations, 0);
+ if (pclcf->static_locations == NULL) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations,
+ ngx_http_core_loc_conf_t *clcf)
+{
+ ngx_http_location_queue_t *lq;
+
+ if (*locations == NULL) {
+ *locations = ngx_palloc(cf->temp_pool,
+ sizeof(ngx_http_location_queue_t));
+ if (*locations == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_queue_init(*locations);
+ }
+
+ lq = ngx_palloc(cf->temp_pool, sizeof(ngx_http_location_queue_t));
+ if (lq == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (clcf->exact_match
+#if (NGX_PCRE)
+ || clcf->regex
+#endif
+ || clcf->named || clcf->noname)
+ {
+ lq->exact = clcf;
+ lq->inclusive = NULL;
+
+ } else {
+ lq->exact = NULL;
+ lq->inclusive = clcf;
+ }
+
+ lq->name = &clcf->name;
+ lq->file_name = cf->conf_file->file.name.data;
+ lq->line = cf->conf_file->line;
+
+ ngx_queue_init(&lq->list);
+
+ ngx_queue_insert_tail(*locations, &lq->queue);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_cmp_locations(const ngx_queue_t *one, const ngx_queue_t *two)
+{
+ ngx_int_t rc;
+ ngx_http_core_loc_conf_t *first, *second;
+ ngx_http_location_queue_t *lq1, *lq2;
+
+ lq1 = (ngx_http_location_queue_t *) one;
+ lq2 = (ngx_http_location_queue_t *) two;
+
+ first = lq1->exact ? lq1->exact : lq1->inclusive;
+ second = lq2->exact ? lq2->exact : lq2->inclusive;
+
+ if (first->noname && !second->noname) {
+ /* shift no named locations to the end */
+ return 1;
+ }
+
+ if (!first->noname && second->noname) {
+ /* shift no named locations to the end */
+ return -1;
+ }
+
+ if (first->noname || second->noname) {
+ /* do not sort no named locations */
+ return 0;
+ }
+
+ if (first->named && !second->named) {
+ /* shift named locations to the end */
+ return 1;
+ }
+
+ if (!first->named && second->named) {
+ /* shift named locations to the end */
+ return -1;
+ }
+
+ if (first->named && second->named) {
+ return ngx_strcmp(first->name.data, second->name.data);
+ }
+
+#if (NGX_PCRE)
+
+ if (first->regex && !second->regex) {
+ /* shift the regex matches to the end */
+ return 1;
+ }
+
+ if (!first->regex && second->regex) {
+ /* shift the regex matches to the end */
+ return -1;
+ }
+
+ if (first->regex || second->regex) {
+ /* do not sort the regex matches */
+ return 0;
+ }
+
+#endif
+
+ rc = ngx_filename_cmp(first->name.data, second->name.data,
+ ngx_min(first->name.len, second->name.len) + 1);
+
+ if (rc == 0 && !first->exact_match && second->exact_match) {
+ /* an exact match must be before the same inclusive one */
+ return 1;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_join_exact_locations(ngx_conf_t *cf, ngx_queue_t *locations)
+{
+ ngx_queue_t *q, *x;
+ ngx_http_location_queue_t *lq, *lx;
+
+ q = ngx_queue_head(locations);
+
+ while (q != ngx_queue_last(locations)) {
+
+ x = ngx_queue_next(q);
+
+ lq = (ngx_http_location_queue_t *) q;
+ lx = (ngx_http_location_queue_t *) x;
+
+ if (lq->name->len == lx->name->len
+ && ngx_filename_cmp(lq->name->data, lx->name->data, lx->name->len)
+ == 0)
+ {
+ if ((lq->exact && lx->exact) || (lq->inclusive && lx->inclusive)) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "duplicate location \"%V\" in %s:%ui",
+ lx->name, lx->file_name, lx->line);
+
+ return NGX_ERROR;
+ }
+
+ lq->inclusive = lx->inclusive;
+
+ ngx_queue_remove(x);
+
+ continue;
+ }
+
+ q = ngx_queue_next(q);
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_create_locations_list(ngx_queue_t *locations, ngx_queue_t *q)
+{
+ u_char *name;
+ size_t len;
+ ngx_queue_t *x, tail;
+ ngx_http_location_queue_t *lq, *lx;
+
+ if (q == ngx_queue_last(locations)) {
+ return;
+ }
+
+ lq = (ngx_http_location_queue_t *) q;
+
+ if (lq->inclusive == NULL) {
+ ngx_http_create_locations_list(locations, ngx_queue_next(q));
+ return;
+ }
+
+ len = lq->name->len;
+ name = lq->name->data;
+
+ for (x = ngx_queue_next(q);
+ x != ngx_queue_sentinel(locations);
+ x = ngx_queue_next(x))
+ {
+ lx = (ngx_http_location_queue_t *) x;
+
+ if (len > lx->name->len
+ || ngx_filename_cmp(name, lx->name->data, len) != 0)
+ {
+ break;
+ }
+ }
+
+ q = ngx_queue_next(q);
+
+ if (q == x) {
+ ngx_http_create_locations_list(locations, x);
+ return;
+ }
+
+ ngx_queue_split(locations, q, &tail);
+ ngx_queue_add(&lq->list, &tail);
+
+ if (x == ngx_queue_sentinel(locations)) {
+ ngx_http_create_locations_list(&lq->list, ngx_queue_head(&lq->list));
+ return;
+ }
+
+ ngx_queue_split(&lq->list, x, &tail);
+ ngx_queue_add(locations, &tail);
+
+ ngx_http_create_locations_list(&lq->list, ngx_queue_head(&lq->list));
+
+ ngx_http_create_locations_list(locations, x);
+}
+
+
+/*
+ * to keep cache locality for left leaf nodes, allocate nodes in following
+ * order: node, left subtree, right subtree, inclusive subtree
+ */
+
+static ngx_http_location_tree_node_t *
+ngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations,
+ size_t prefix)
+{
+ size_t len;
+ ngx_queue_t *q, tail;
+ ngx_http_location_queue_t *lq;
+ ngx_http_location_tree_node_t *node;
+
+ q = ngx_queue_middle(locations);
+
+ lq = (ngx_http_location_queue_t *) q;
+ len = lq->name->len - prefix;
+
+ node = ngx_palloc(cf->pool,
+ offsetof(ngx_http_location_tree_node_t, name) + len);
+ if (node == NULL) {
+ return NULL;
+ }
+
+ node->left = NULL;
+ node->right = NULL;
+ node->tree = NULL;
+ node->exact = lq->exact;
+ node->inclusive = lq->inclusive;
+
+ node->auto_redirect = (u_char) ((lq->exact && lq->exact->auto_redirect)
+ || (lq->inclusive && lq->inclusive->auto_redirect));
+
+ node->len = (u_char) len;
+ ngx_memcpy(node->name, &lq->name->data[prefix], len);
+
+ ngx_queue_split(locations, q, &tail);
+
+ if (ngx_queue_empty(locations)) {
+ /*
+ * ngx_queue_split() insures that if left part is empty,
+ * then right one is empty too
+ */
+ goto inclusive;
+ }
+
+ node->left = ngx_http_create_locations_tree(cf, locations, prefix);
+ if (node->left == NULL) {
+ return NULL;
+ }
+
+ ngx_queue_remove(q);
+
+ if (ngx_queue_empty(&tail)) {
+ goto inclusive;
+ }
+
+ node->right = ngx_http_create_locations_tree(cf, &tail, prefix);
+ if (node->right == NULL) {
+ return NULL;
+ }
+
+inclusive:
+
+ if (ngx_queue_empty(&lq->list)) {
+ return node;
+ }
+
+ node->tree = ngx_http_create_locations_tree(cf, &lq->list, prefix + len);
+ if (node->tree == NULL) {
+ return NULL;
+ }
+
+ return node;
+}
+
+
+ngx_int_t
+ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+ ngx_http_listen_opt_t *lsopt)
+{
+ in_port_t p;
+ ngx_uint_t i;
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ ngx_http_conf_port_t *port;
+ ngx_http_core_main_conf_t *cmcf;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ if (cmcf->ports == NULL) {
+ cmcf->ports = ngx_array_create(cf->temp_pool, 2,
+ sizeof(ngx_http_conf_port_t));
+ if (cmcf->ports == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ sa = &lsopt->u.sockaddr;
+
+ switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = &lsopt->u.sockaddr_in6;
+ p = sin6->sin6_port;
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ p = 0;
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = &lsopt->u.sockaddr_in;
+ p = sin->sin_port;
+ break;
+ }
+
+ port = cmcf->ports->elts;
+ for (i = 0; i < cmcf->ports->nelts; i++) {
+
+ if (p != port[i].port || sa->sa_family != port[i].family) {
+ continue;
+ }
+
+ /* a port is already in the port list */
+
+ return ngx_http_add_addresses(cf, cscf, &port[i], lsopt);
+ }
+
+ /* add a port to the port list */
+
+ port = ngx_array_push(cmcf->ports);
+ if (port == NULL) {
+ return NGX_ERROR;
+ }
+
+ port->family = sa->sa_family;
+ port->port = p;
+ port->addrs.elts = NULL;
+
+ return ngx_http_add_address(cf, cscf, port, lsopt);
+}
+
+
+static ngx_int_t
+ngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+ ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt)
+{
+ u_char *p;
+ size_t len, off;
+ ngx_uint_t i, default_server;
+ struct sockaddr *sa;
+ ngx_http_conf_addr_t *addr;
+#if (NGX_HAVE_UNIX_DOMAIN)
+ struct sockaddr_un *saun;
+#endif
+#if (NGX_HTTP_SSL)
+ ngx_uint_t ssl;
+#endif
+#if (NGX_HTTP_SPDY)
+ ngx_uint_t spdy;
+#endif
+
+ /*
+ * we cannot compare whole sockaddr struct's as kernel
+ * may fill some fields in inherited sockaddr struct's
+ */
+
+ sa = &lsopt->u.sockaddr;
+
+ switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ off = offsetof(struct sockaddr_in6, sin6_addr);
+ len = 16;
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ off = offsetof(struct sockaddr_un, sun_path);
+ len = sizeof(saun->sun_path);
+ break;
+#endif
+
+ default: /* AF_INET */
+ off = offsetof(struct sockaddr_in, sin_addr);
+ len = 4;
+ break;
+ }
+
+ p = lsopt->u.sockaddr_data + off;
+
+ addr = port->addrs.elts;
+
+ for (i = 0; i < port->addrs.nelts; i++) {
+
+ if (ngx_memcmp(p, addr[i].opt.u.sockaddr_data + off, len) != 0) {
+ continue;
+ }
+
+ /* the address is already in the address list */
+
+ if (ngx_http_add_server(cf, cscf, &addr[i]) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ /* preserve default_server bit during listen options overwriting */
+ default_server = addr[i].opt.default_server;
+
+#if (NGX_HTTP_SSL)
+ ssl = lsopt->ssl || addr[i].opt.ssl;
+#endif
+#if (NGX_HTTP_SPDY)
+ spdy = lsopt->spdy || addr[i].opt.spdy;
+#endif
+
+ if (lsopt->set) {
+
+ if (addr[i].opt.set) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate listen options for %s", addr[i].opt.addr);
+ return NGX_ERROR;
+ }
+
+ addr[i].opt = *lsopt;
+ }
+
+ /* check the duplicate "default" server for this address:port */
+
+ if (lsopt->default_server) {
+
+ if (default_server) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "a duplicate default server for %s", addr[i].opt.addr);
+ return NGX_ERROR;
+ }
+
+ default_server = 1;
+ addr[i].default_server = cscf;
+ }
+
+ addr[i].opt.default_server = default_server;
+#if (NGX_HTTP_SSL)
+ addr[i].opt.ssl = ssl;
+#endif
+#if (NGX_HTTP_SPDY)
+ addr[i].opt.spdy = spdy;
+#endif
+
+ return NGX_OK;
+ }
+
+ /* add the address to the addresses list that bound to this port */
+
+ return ngx_http_add_address(cf, cscf, port, lsopt);
+}
+
+
+/*
+ * add the server address, the server names and the server core module
+ * configurations to the port list
+ */
+
+static ngx_int_t
+ngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+ ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt)
+{
+ ngx_http_conf_addr_t *addr;
+
+ if (port->addrs.elts == NULL) {
+ if (ngx_array_init(&port->addrs, cf->temp_pool, 4,
+ sizeof(ngx_http_conf_addr_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+#if (NGX_HTTP_SPDY && NGX_HTTP_SSL \
+ && !defined TLSEXT_TYPE_application_layer_protocol_negotiation \
+ && !defined TLSEXT_TYPE_next_proto_neg)
+ if (lsopt->spdy && lsopt->ssl) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "nginx was built without OpenSSL ALPN or NPN "
+ "support, SPDY is not enabled for %s", lsopt->addr);
+ }
+#endif
+
+ addr = ngx_array_push(&port->addrs);
+ if (addr == NULL) {
+ return NGX_ERROR;
+ }
+
+ addr->opt = *lsopt;
+ addr->hash.buckets = NULL;
+ addr->hash.size = 0;
+ addr->wc_head = NULL;
+ addr->wc_tail = NULL;
+#if (NGX_PCRE)
+ addr->nregex = 0;
+ addr->regex = NULL;
+#endif
+ addr->default_server = cscf;
+ addr->servers.elts = NULL;
+
+ return ngx_http_add_server(cf, cscf, addr);
+}
+
+
+/* add the server core module configuration to the address:port */
+
+static ngx_int_t
+ngx_http_add_server(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+ ngx_http_conf_addr_t *addr)
+{
+ ngx_uint_t i;
+ ngx_http_core_srv_conf_t **server;
+
+ if (addr->servers.elts == NULL) {
+ if (ngx_array_init(&addr->servers, cf->temp_pool, 4,
+ sizeof(ngx_http_core_srv_conf_t *))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ } else {
+ server = addr->servers.elts;
+ for (i = 0; i < addr->servers.nelts; i++) {
+ if (server[i] == cscf) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "a duplicate listen %s", addr->opt.addr);
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ server = ngx_array_push(&addr->servers);
+ if (server == NULL) {
+ return NGX_ERROR;
+ }
+
+ *server = cscf;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
+ ngx_array_t *ports)
+{
+ ngx_uint_t p, a;
+ ngx_http_conf_port_t *port;
+ ngx_http_conf_addr_t *addr;
+
+ if (ports == NULL) {
+ return NGX_OK;
+ }
+
+ port = ports->elts;
+ for (p = 0; p < ports->nelts; p++) {
+
+ ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
+ sizeof(ngx_http_conf_addr_t), ngx_http_cmp_conf_addrs);
+
+ /*
+ * check whether all name-based servers have the same
+ * configuration as a default server for given address:port
+ */
+
+ addr = port[p].addrs.elts;
+ for (a = 0; a < port[p].addrs.nelts; a++) {
+
+ if (addr[a].servers.nelts > 1
+#if (NGX_PCRE)
+ || addr[a].default_server->captures
+#endif
+ )
+ {
+ if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_server_names(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,
+ ngx_http_conf_addr_t *addr)
+{
+ ngx_int_t rc;
+ ngx_uint_t n, s;
+ ngx_hash_init_t hash;
+ ngx_hash_keys_arrays_t ha;
+ ngx_http_server_name_t *name;
+ ngx_http_core_srv_conf_t **cscfp;
+#if (NGX_PCRE)
+ ngx_uint_t regex, i;
+
+ regex = 0;
+#endif
+
+ ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t));
+
+ ha.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);
+ if (ha.temp_pool == NULL) {
+ return NGX_ERROR;
+ }
+
+ ha.pool = cf->pool;
+
+ if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) {
+ goto failed;
+ }
+
+ cscfp = addr->servers.elts;
+
+ for (s = 0; s < addr->servers.nelts; s++) {
+
+ name = cscfp[s]->server_names.elts;
+
+ for (n = 0; n < cscfp[s]->server_names.nelts; n++) {
+
+#if (NGX_PCRE)
+ if (name[n].regex) {
+ regex++;
+ continue;
+ }
+#endif
+
+ rc = ngx_hash_add_key(&ha, &name[n].name, name[n].server,
+ NGX_HASH_WILDCARD_KEY);
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (rc == NGX_DECLINED) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "invalid server name or wildcard \"%V\" on %s",
+ &name[n].name, addr->opt.addr);
+ return NGX_ERROR;
+ }
+
+ if (rc == NGX_BUSY) {
+ ngx_log_error(NGX_LOG_WARN, cf->log, 0,
+ "conflicting server name \"%V\" on %s, ignored",
+ &name[n].name, addr->opt.addr);
+ }
+ }
+ }
+
+ hash.key = ngx_hash_key_lc;
+ hash.max_size = cmcf->server_names_hash_max_size;
+ hash.bucket_size = cmcf->server_names_hash_bucket_size;
+ hash.name = "server_names_hash";
+ hash.pool = cf->pool;
+
+ if (ha.keys.nelts) {
+ hash.hash = &addr->hash;
+ hash.temp_pool = NULL;
+
+ if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) {
+ goto failed;
+ }
+ }
+
+ if (ha.dns_wc_head.nelts) {
+
+ ngx_qsort(ha.dns_wc_head.elts, (size_t) ha.dns_wc_head.nelts,
+ sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);
+
+ hash.hash = NULL;
+ hash.temp_pool = ha.temp_pool;
+
+ if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts,
+ ha.dns_wc_head.nelts)
+ != NGX_OK)
+ {
+ goto failed;
+ }
+
+ addr->wc_head = (ngx_hash_wildcard_t *) hash.hash;
+ }
+
+ if (ha.dns_wc_tail.nelts) {
+
+ ngx_qsort(ha.dns_wc_tail.elts, (size_t) ha.dns_wc_tail.nelts,
+ sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);
+
+ hash.hash = NULL;
+ hash.temp_pool = ha.temp_pool;
+
+ if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,
+ ha.dns_wc_tail.nelts)
+ != NGX_OK)
+ {
+ goto failed;
+ }
+
+ addr->wc_tail = (ngx_hash_wildcard_t *) hash.hash;
+ }
+
+ ngx_destroy_pool(ha.temp_pool);
+
+#if (NGX_PCRE)
+
+ if (regex == 0) {
+ return NGX_OK;
+ }
+
+ addr->nregex = regex;
+ addr->regex = ngx_palloc(cf->pool, regex * sizeof(ngx_http_server_name_t));
+ if (addr->regex == NULL) {
+ return NGX_ERROR;
+ }
+
+ i = 0;
+
+ for (s = 0; s < addr->servers.nelts; s++) {
+
+ name = cscfp[s]->server_names.elts;
+
+ for (n = 0; n < cscfp[s]->server_names.nelts; n++) {
+ if (name[n].regex) {
+ addr->regex[i++] = name[n];
+ }
+ }
+ }
+
+#endif
+
+ return NGX_OK;
+
+failed:
+
+ ngx_destroy_pool(ha.temp_pool);
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_cmp_conf_addrs(const void *one, const void *two)
+{
+ ngx_http_conf_addr_t *first, *second;
+
+ first = (ngx_http_conf_addr_t *) one;
+ second = (ngx_http_conf_addr_t *) two;
+
+ if (first->opt.wildcard) {
+ /* a wildcard address must be the last resort, shift it to the end */
+ return 1;
+ }
+
+ if (second->opt.wildcard) {
+ /* a wildcard address must be the last resort, shift it to the end */
+ return -1;
+ }
+
+ if (first->opt.bind && !second->opt.bind) {
+ /* shift explicit bind()ed addresses to the start */
+ return -1;
+ }
+
+ if (!first->opt.bind && second->opt.bind) {
+ /* shift explicit bind()ed addresses to the start */
+ return 1;
+ }
+
+ /* do not sort by default */
+
+ return 0;
+}
+
+
+static int ngx_libc_cdecl
+ngx_http_cmp_dns_wildcards(const void *one, const void *two)
+{
+ ngx_hash_key_t *first, *second;
+
+ first = (ngx_hash_key_t *) one;
+ second = (ngx_hash_key_t *) two;
+
+ return ngx_dns_strcmp(first->key.data, second->key.data);
+}
+
+
+static ngx_int_t
+ngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port)
+{
+ ngx_uint_t i, last, bind_wildcard;
+ ngx_listening_t *ls;
+ ngx_http_port_t *hport;
+ ngx_http_conf_addr_t *addr;
+
+ addr = port->addrs.elts;
+ last = port->addrs.nelts;
+
+ /*
+ * If there is a binding to an "*:port" then we need to bind() to
+ * the "*:port" only and ignore other implicit bindings. The bindings
+ * have been already sorted: explicit bindings are on the start, then
+ * implicit bindings go, and wildcard binding is in the end.
+ */
+
+ if (addr[last - 1].opt.wildcard) {
+ addr[last - 1].opt.bind = 1;
+ bind_wildcard = 1;
+
+ } else {
+ bind_wildcard = 0;
+ }
+
+ i = 0;
+
+ while (i < last) {
+
+ if (bind_wildcard && !addr[i].opt.bind) {
+ i++;
+ continue;
+ }
+
+ ls = ngx_http_add_listening(cf, &addr[i]);
+ if (ls == NULL) {
+ return NGX_ERROR;
+ }
+
+ hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t));
+ if (hport == NULL) {
+ return NGX_ERROR;
+ }
+
+ ls->servers = hport;
+
+ if (i == last - 1) {
+ hport->naddrs = last;
+
+ } else {
+ hport->naddrs = 1;
+ i = 0;
+ }
+
+ switch (ls->sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ if (ngx_http_add_addrs6(cf, hport, addr) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ break;
+#endif
+ default: /* AF_INET */
+ if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ break;
+ }
+
+ addr++;
+ last--;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_listening_t *
+ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)
+{
+ ngx_listening_t *ls;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_srv_conf_t *cscf;
+
+ ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen);
+ if (ls == NULL) {
+ return NULL;
+ }
+
+ ls->addr_ntop = 1;
+
+ ls->handler = ngx_http_init_connection;
+
+ cscf = addr->default_server;
+ ls->pool_size = cscf->connection_pool_size;
+ ls->post_accept_timeout = cscf->client_header_timeout;
+
+ clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];
+
+ ls->logp = clcf->error_log;
+ ls->log.data = &ls->addr_text;
+ ls->log.handler = ngx_accept_log_error;
+
+#if (NGX_WIN32)
+ {
+ ngx_iocp_conf_t *iocpcf = NULL;
+
+ if (ngx_get_conf(cf->cycle->conf_ctx, ngx_events_module)) {
+ iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module);
+ }
+ if (iocpcf && iocpcf->acceptex_read) {
+ ls->post_accept_buffer_size = cscf->client_header_buffer_size;
+ }
+ }
+#endif
+
+ ls->backlog = addr->opt.backlog;
+ ls->rcvbuf = addr->opt.rcvbuf;
+ ls->sndbuf = addr->opt.sndbuf;
+
+ ls->keepalive = addr->opt.so_keepalive;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ ls->keepidle = addr->opt.tcp_keepidle;
+ ls->keepintvl = addr->opt.tcp_keepintvl;
+ ls->keepcnt = addr->opt.tcp_keepcnt;
+#endif
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
+ ls->accept_filter = addr->opt.accept_filter;
+#endif
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
+ ls->deferred_accept = addr->opt.deferred_accept;
+#endif
+
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ ls->ipv6only = addr->opt.ipv6only;
+#endif
+
+#if (NGX_HAVE_SETFIB)
+ ls->setfib = addr->opt.setfib;
+#endif
+
+#if (NGX_HAVE_TCP_FASTOPEN)
+ ls->fastopen = addr->opt.fastopen;
+#endif
+
+ return ls;
+}
+
+
+static ngx_int_t
+ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,
+ ngx_http_conf_addr_t *addr)
+{
+ ngx_uint_t i;
+ ngx_http_in_addr_t *addrs;
+ struct sockaddr_in *sin;
+ ngx_http_virtual_names_t *vn;
+
+ hport->addrs = ngx_pcalloc(cf->pool,
+ hport->naddrs * sizeof(ngx_http_in_addr_t));
+ if (hport->addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ addrs = hport->addrs;
+
+ for (i = 0; i < hport->naddrs; i++) {
+
+ sin = &addr[i].opt.u.sockaddr_in;
+ addrs[i].addr = sin->sin_addr.s_addr;
+ addrs[i].conf.default_server = addr[i].default_server;
+#if (NGX_HTTP_SSL)
+ addrs[i].conf.ssl = addr[i].opt.ssl;
+#endif
+#if (NGX_HTTP_SPDY)
+ addrs[i].conf.spdy = addr[i].opt.spdy;
+#endif
+ addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
+
+ if (addr[i].hash.buckets == NULL
+ && (addr[i].wc_head == NULL
+ || addr[i].wc_head->hash.buckets == NULL)
+ && (addr[i].wc_tail == NULL
+ || addr[i].wc_tail->hash.buckets == NULL)
+#if (NGX_PCRE)
+ && addr[i].nregex == 0
+#endif
+ )
+ {
+ continue;
+ }
+
+ vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t));
+ if (vn == NULL) {
+ return NGX_ERROR;
+ }
+
+ addrs[i].conf.virtual_names = vn;
+
+ vn->names.hash = addr[i].hash;
+ vn->names.wc_head = addr[i].wc_head;
+ vn->names.wc_tail = addr[i].wc_tail;
+#if (NGX_PCRE)
+ vn->nregex = addr[i].nregex;
+ vn->regex = addr[i].regex;
+#endif
+ }
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HAVE_INET6)
+
+static ngx_int_t
+ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,
+ ngx_http_conf_addr_t *addr)
+{
+ ngx_uint_t i;
+ ngx_http_in6_addr_t *addrs6;
+ struct sockaddr_in6 *sin6;
+ ngx_http_virtual_names_t *vn;
+
+ hport->addrs = ngx_pcalloc(cf->pool,
+ hport->naddrs * sizeof(ngx_http_in6_addr_t));
+ if (hport->addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ addrs6 = hport->addrs;
+
+ for (i = 0; i < hport->naddrs; i++) {
+
+ sin6 = &addr[i].opt.u.sockaddr_in6;
+ addrs6[i].addr6 = sin6->sin6_addr;
+ addrs6[i].conf.default_server = addr[i].default_server;
+#if (NGX_HTTP_SSL)
+ addrs6[i].conf.ssl = addr[i].opt.ssl;
+#endif
+#if (NGX_HTTP_SPDY)
+ addrs6[i].conf.spdy = addr[i].opt.spdy;
+#endif
+
+ if (addr[i].hash.buckets == NULL
+ && (addr[i].wc_head == NULL
+ || addr[i].wc_head->hash.buckets == NULL)
+ && (addr[i].wc_tail == NULL
+ || addr[i].wc_tail->hash.buckets == NULL)
+#if (NGX_PCRE)
+ && addr[i].nregex == 0
+#endif
+ )
+ {
+ continue;
+ }
+
+ vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t));
+ if (vn == NULL) {
+ return NGX_ERROR;
+ }
+
+ addrs6[i].conf.virtual_names = vn;
+
+ vn->names.hash = addr[i].hash;
+ vn->names.wc_head = addr[i].wc_head;
+ vn->names.wc_tail = addr[i].wc_tail;
+#if (NGX_PCRE)
+ vn->nregex = addr[i].nregex;
+ vn->regex = addr[i].regex;
+#endif
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
+char *
+ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_array_t **types;
+ ngx_str_t *value, *default_type;
+ ngx_uint_t i, n, hash;
+ ngx_hash_key_t *type;
+
+ types = (ngx_array_t **) (p + cmd->offset);
+
+ if (*types == (void *) -1) {
+ return NGX_CONF_OK;
+ }
+
+ default_type = cmd->post;
+
+ if (*types == NULL) {
+ *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t));
+ if (*types == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (default_type) {
+ type = ngx_array_push(*types);
+ if (type == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ type->key = *default_type;
+ type->key_hash = ngx_hash_key(default_type->data,
+ default_type->len);
+ type->value = (void *) 4;
+ }
+ }
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (value[i].len == 1 && value[i].data[0] == '*') {
+ *types = (void *) -1;
+ return NGX_CONF_OK;
+ }
+
+ hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len);
+ value[i].data[value[i].len] = '\0';
+
+ type = (*types)->elts;
+ for (n = 0; n < (*types)->nelts; n++) {
+
+ if (ngx_strcmp(value[i].data, type[n].key.data) == 0) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "duplicate MIME type \"%V\"", &value[i]);
+ continue;
+ }
+ }
+
+ type = ngx_array_push(*types);
+ if (type == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ type->key = value[i];
+ type->key_hash = hash;
+ type->value = (void *) 4;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t **keys, ngx_hash_t *types_hash,
+ ngx_array_t **prev_keys, ngx_hash_t *prev_types_hash,
+ ngx_str_t *default_types)
+{
+ ngx_hash_init_t hash;
+
+ if (*keys) {
+
+ if (*keys == (void *) -1) {
+ return NGX_CONF_OK;
+ }
+
+ hash.hash = types_hash;
+ hash.key = NULL;
+ hash.max_size = 2048;
+ hash.bucket_size = 64;
+ hash.name = "test_types_hash";
+ hash.pool = cf->pool;
+ hash.temp_pool = NULL;
+
+ if (ngx_hash_init(&hash, (*keys)->elts, (*keys)->nelts) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+ }
+
+ if (prev_types_hash->buckets == NULL) {
+
+ if (*prev_keys == NULL) {
+
+ if (ngx_http_set_default_types(cf, prev_keys, default_types)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ } else if (*prev_keys == (void *) -1) {
+ *keys = *prev_keys;
+ return NGX_CONF_OK;
+ }
+
+ hash.hash = prev_types_hash;
+ hash.key = NULL;
+ hash.max_size = 2048;
+ hash.bucket_size = 64;
+ hash.name = "test_types_hash";
+ hash.pool = cf->pool;
+ hash.temp_pool = NULL;
+
+ if (ngx_hash_init(&hash, (*prev_keys)->elts, (*prev_keys)->nelts)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ *types_hash = *prev_types_hash;
+
+ return NGX_CONF_OK;
+
+}
+
+
+ngx_int_t
+ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types,
+ ngx_str_t *default_type)
+{
+ ngx_hash_key_t *type;
+
+ *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t));
+ if (*types == NULL) {
+ return NGX_ERROR;
+ }
+
+ while (default_type->len) {
+
+ type = ngx_array_push(*types);
+ if (type == NULL) {
+ return NGX_ERROR;
+ }
+
+ type->key = *default_type;
+ type->key_hash = ngx_hash_key(default_type->data,
+ default_type->len);
+ type->value = (void *) 4;
+
+ default_type++;
+ }
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http.h
new file mode 100644
index 00000000000..0acc234942c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http.h
@@ -0,0 +1,186 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_H_INCLUDED_
+#define _NGX_HTTP_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef struct ngx_http_request_s ngx_http_request_t;
+typedef struct ngx_http_upstream_s ngx_http_upstream_t;
+typedef struct ngx_http_cache_s ngx_http_cache_t;
+typedef struct ngx_http_file_cache_s ngx_http_file_cache_t;
+typedef struct ngx_http_log_ctx_s ngx_http_log_ctx_t;
+typedef struct ngx_http_chunked_s ngx_http_chunked_t;
+
+#if (NGX_HTTP_SPDY)
+typedef struct ngx_http_spdy_stream_s ngx_http_spdy_stream_t;
+#endif
+
+typedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+typedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r,
+ ngx_http_request_t *sr, u_char *buf, size_t len);
+
+
+#include <ngx_http_variables.h>
+#include <ngx_http_config.h>
+#include <ngx_http_request.h>
+#include <ngx_http_script.h>
+#include <ngx_http_upstream.h>
+#include <ngx_http_upstream_round_robin.h>
+#include <ngx_http_busy_lock.h>
+#include <ngx_http_core_module.h>
+
+#if (NGX_HTTP_SPDY)
+#include <ngx_http_spdy.h>
+#endif
+#if (NGX_HTTP_CACHE)
+#include <ngx_http_cache.h>
+#endif
+#if (NGX_HTTP_SSI)
+#include <ngx_http_ssi_filter_module.h>
+#endif
+#if (NGX_HTTP_SSL)
+#include <ngx_http_ssl_module.h>
+#endif
+
+
+struct ngx_http_log_ctx_s {
+ ngx_connection_t *connection;
+ ngx_http_request_t *request;
+ ngx_http_request_t *current_request;
+};
+
+
+struct ngx_http_chunked_s {
+ ngx_uint_t state;
+ off_t size;
+ off_t length;
+};
+
+
+typedef struct {
+ ngx_uint_t http_version;
+ ngx_uint_t code;
+ ngx_uint_t count;
+ u_char *start;
+ u_char *end;
+} ngx_http_status_t;
+
+
+#define ngx_http_get_module_ctx(r, module) (r)->ctx[module.ctx_index]
+#define ngx_http_set_ctx(r, c, module) r->ctx[module.ctx_index] = c;
+
+
+ngx_int_t ngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations,
+ ngx_http_core_loc_conf_t *clcf);
+ngx_int_t ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
+ ngx_http_listen_opt_t *lsopt);
+
+
+void ngx_http_init_connection(ngx_connection_t *c);
+void ngx_http_close_connection(ngx_connection_t *c);
+
+#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)
+int ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg);
+#endif
+
+ngx_int_t ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b);
+ngx_int_t ngx_http_parse_uri(ngx_http_request_t *r);
+ngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r,
+ ngx_uint_t merge_slashes);
+ngx_int_t ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
+ ngx_http_status_t *status);
+ngx_int_t ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
+ ngx_str_t *args, ngx_uint_t *flags);
+ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
+ ngx_uint_t allow_underscores);
+ngx_int_t ngx_http_parse_multi_header_lines(ngx_array_t *headers,
+ ngx_str_t *name, ngx_str_t *value);
+ngx_int_t ngx_http_parse_set_cookie_lines(ngx_array_t *headers,
+ ngx_str_t *name, ngx_str_t *value);
+ngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len,
+ ngx_str_t *value);
+void ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri,
+ ngx_str_t *args);
+ngx_int_t ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
+ ngx_http_chunked_t *ctx);
+
+
+ngx_http_request_t *ngx_http_create_request(ngx_connection_t *c);
+ngx_int_t ngx_http_process_request_uri(ngx_http_request_t *r);
+ngx_int_t ngx_http_process_request_header(ngx_http_request_t *r);
+void ngx_http_process_request(ngx_http_request_t *r);
+void ngx_http_update_location_config(ngx_http_request_t *r);
+void ngx_http_handler(ngx_http_request_t *r);
+void ngx_http_run_posted_requests(ngx_connection_t *c);
+ngx_int_t ngx_http_post_request(ngx_http_request_t *r,
+ ngx_http_posted_request_t *pr);
+void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc);
+void ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc);
+
+void ngx_http_empty_handler(ngx_event_t *wev);
+void ngx_http_request_empty_handler(ngx_http_request_t *r);
+
+
+#define ngx_http_ephemeral(r) (void *) (&r->uri_start)
+
+
+#define NGX_HTTP_LAST 1
+#define NGX_HTTP_FLUSH 2
+
+ngx_int_t ngx_http_send_special(ngx_http_request_t *r, ngx_uint_t flags);
+
+
+ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r,
+ ngx_http_client_body_handler_pt post_handler);
+
+ngx_int_t ngx_http_send_header(ngx_http_request_t *r);
+ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r,
+ ngx_int_t error);
+ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r,
+ ngx_module_t *m, ngx_int_t error);
+void ngx_http_clean_header(ngx_http_request_t *r);
+
+
+time_t ngx_http_parse_time(u_char *value, size_t len);
+size_t ngx_http_get_time(char *buf, time_t t);
+
+
+
+ngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r);
+void ngx_http_discarded_request_body_handler(ngx_http_request_t *r);
+void ngx_http_block_reading(ngx_http_request_t *r);
+void ngx_http_test_reading(ngx_http_request_t *r);
+
+
+char *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t **keys,
+ ngx_hash_t *types_hash, ngx_array_t **prev_keys,
+ ngx_hash_t *prev_types_hash, ngx_str_t *default_types);
+ngx_int_t ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types,
+ ngx_str_t *default_type);
+
+#if (NGX_HTTP_DEGRADATION)
+ngx_uint_t ngx_http_degraded(ngx_http_request_t *);
+#endif
+
+
+extern ngx_module_t ngx_http_module;
+
+extern ngx_str_t ngx_http_html_default_types[];
+
+
+extern ngx_http_output_header_filter_pt ngx_http_top_header_filter;
+extern ngx_http_output_body_filter_pt ngx_http_top_body_filter;
+
+
+#endif /* _NGX_HTTP_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_busy_lock.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_busy_lock.c
new file mode 100644
index 00000000000..3b4b28c8b32
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_busy_lock.c
@@ -0,0 +1,307 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+
+static int ngx_http_busy_lock_look_cacheable(ngx_http_busy_lock_t *bl,
+ ngx_http_busy_lock_ctx_t *bc,
+ int lock);
+
+
+int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc)
+{
+ if (bl->busy < bl->max_busy) {
+ bl->busy++;
+
+ if (bc->time) {
+ bc->time = 0;
+ bl->waiting--;
+ }
+
+ return NGX_OK;
+ }
+
+ if (bc->time) {
+ if (bc->time < bl->timeout) {
+ ngx_add_timer(bc->event, 1000);
+ return NGX_AGAIN;
+ }
+
+ bl->waiting--;
+ return NGX_DONE;
+
+ }
+
+ if (bl->timeout == 0) {
+ return NGX_DONE;
+ }
+
+ if (bl->waiting < bl->max_waiting) {
+ bl->waiting++;
+
+#if 0
+ ngx_add_timer(bc->event, 1000);
+ bc->event->event_handler = bc->event_handler;
+#endif
+
+ /* TODO: ngx_handle_level_read_event() */
+
+ return NGX_AGAIN;
+ }
+
+ return NGX_ERROR;
+}
+
+
+int ngx_http_busy_lock_cacheable(ngx_http_busy_lock_t *bl,
+ ngx_http_busy_lock_ctx_t *bc, int lock)
+{
+ int rc;
+
+ rc = ngx_http_busy_lock_look_cacheable(bl, bc, lock);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, bc->event->log, 0,
+ "http busylock: %d w:%d mw::%d",
+ rc, bl->waiting, bl->max_waiting);
+
+ if (rc == NGX_OK) { /* no the same request, there's free slot */
+ return NGX_OK;
+ }
+
+ if (rc == NGX_ERROR && !lock) { /* no the same request, no free slot */
+ return NGX_OK;
+ }
+
+ /* rc == NGX_AGAIN: the same request */
+
+ if (bc->time) {
+ if (bc->time < bl->timeout) {
+ ngx_add_timer(bc->event, 1000);
+ return NGX_AGAIN;
+ }
+
+ bl->waiting--;
+ return NGX_DONE;
+
+ }
+
+ if (bl->timeout == 0) {
+ return NGX_DONE;
+ }
+
+ if (bl->waiting < bl->max_waiting) {
+#if 0
+ bl->waiting++;
+ ngx_add_timer(bc->event, 1000);
+ bc->event->event_handler = bc->event_handler;
+#endif
+
+ /* TODO: ngx_handle_level_read_event() */
+
+ return NGX_AGAIN;
+ }
+
+ return NGX_ERROR;
+}
+
+
+void ngx_http_busy_unlock(ngx_http_busy_lock_t *bl,
+ ngx_http_busy_lock_ctx_t *bc)
+{
+ if (bl == NULL) {
+ return;
+ }
+
+ if (bl->md5) {
+ bl->md5_mask[bc->slot / 8] &= ~(1 << (bc->slot & 7));
+ bl->cacheable--;
+ }
+
+ bl->busy--;
+}
+
+
+static int ngx_http_busy_lock_look_cacheable(ngx_http_busy_lock_t *bl,
+ ngx_http_busy_lock_ctx_t *bc,
+ int lock)
+{
+ int i, b, cacheable, free;
+ u_int mask;
+
+ b = 0;
+ cacheable = 0;
+ free = -1;
+
+#if (NGX_SUPPRESS_WARN)
+ mask = 0;
+#endif
+
+ for (i = 0; i < bl->max_busy; i++) {
+
+ if ((b & 7) == 0) {
+ mask = bl->md5_mask[i / 8];
+ }
+
+ if (mask & 1) {
+ if (ngx_memcmp(&bl->md5[i * 16], bc->md5, 16) == 0) {
+ return NGX_AGAIN;
+ }
+ cacheable++;
+
+ } else if (free == -1) {
+ free = i;
+ }
+
+#if 1
+ if (cacheable == bl->cacheable) {
+ if (free == -1 && cacheable < bl->max_busy) {
+ free = i + 1;
+ }
+
+ break;
+ }
+#endif
+
+ mask >>= 1;
+ b++;
+ }
+
+ if (free == -1) {
+ return NGX_ERROR;
+ }
+
+ if (lock) {
+ if (bl->busy == bl->max_busy) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(&bl->md5[free * 16], bc->md5, 16);
+ bl->md5_mask[free / 8] |= 1 << (free & 7);
+ bc->slot = free;
+
+ bl->cacheable++;
+ bl->busy++;
+ }
+
+ return NGX_OK;
+}
+
+
+char *ngx_http_set_busy_lock_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *p = conf;
+
+ ngx_uint_t i, dup, invalid;
+ ngx_str_t *value, line;
+ ngx_http_busy_lock_t *bl, **blp;
+
+ blp = (ngx_http_busy_lock_t **) (p + cmd->offset);
+ if (*blp) {
+ return "is duplicate";
+ }
+
+ /* ngx_calloc_shared() */
+ bl = ngx_pcalloc(cf->pool, sizeof(ngx_http_busy_lock_t));
+ if (bl == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ *blp = bl;
+
+ /* ngx_calloc_shared() */
+ bl->mutex = ngx_pcalloc(cf->pool, sizeof(ngx_event_mutex_t));
+ if (bl->mutex == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ dup = 0;
+ invalid = 0;
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (value[i].data[1] != '=') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%s\"", value[i].data);
+ return NGX_CONF_ERROR;
+ }
+
+ switch (value[i].data[0]) {
+
+ case 'b':
+ if (bl->max_busy) {
+ dup = 1;
+ break;
+ }
+
+ bl->max_busy = ngx_atoi(value[i].data + 2, value[i].len - 2);
+ if (bl->max_busy == NGX_ERROR) {
+ invalid = 1;
+ break;
+ }
+
+ continue;
+
+ case 'w':
+ if (bl->max_waiting) {
+ dup = 1;
+ break;
+ }
+
+ bl->max_waiting = ngx_atoi(value[i].data + 2, value[i].len - 2);
+ if (bl->max_waiting == NGX_ERROR) {
+ invalid = 1;
+ break;
+ }
+
+ continue;
+
+ case 't':
+ if (bl->timeout) {
+ dup = 1;
+ break;
+ }
+
+ line.len = value[i].len - 2;
+ line.data = value[i].data + 2;
+
+ bl->timeout = ngx_parse_time(&line, 1);
+ if (bl->timeout == (time_t) NGX_ERROR) {
+ invalid = 1;
+ break;
+ }
+
+ continue;
+
+ default:
+ invalid = 1;
+ }
+
+ if (dup) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate value \"%s\"", value[i].data);
+ return NGX_CONF_ERROR;
+ }
+
+ if (invalid) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%s\"", value[i].data);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (bl->timeout == 0 && bl->max_waiting) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "busy lock waiting is useless with zero timeout, ignoring");
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_busy_lock.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_busy_lock.h
new file mode 100644
index 00000000000..c676382f29d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_busy_lock.h
@@ -0,0 +1,54 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_BUSY_LOCK_H_INCLUDED_
+#define _NGX_HTTP_BUSY_LOCK_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ u_char *md5_mask;
+ char *md5;
+ int cacheable;
+
+ int busy;
+ int max_busy;
+
+ int waiting;
+ int max_waiting;
+
+ time_t timeout;
+
+ ngx_event_mutex_t *mutex;
+} ngx_http_busy_lock_t;
+
+
+typedef struct {
+ time_t time;
+ ngx_event_t *event;
+ void (*event_handler)(ngx_event_t *ev);
+ u_char *md5;
+ int slot;
+} ngx_http_busy_lock_ctx_t;
+
+
+int ngx_http_busy_lock(ngx_http_busy_lock_t *bl, ngx_http_busy_lock_ctx_t *bc);
+int ngx_http_busy_lock_cacheable(ngx_http_busy_lock_t *bl,
+ ngx_http_busy_lock_ctx_t *bc, int lock);
+void ngx_http_busy_unlock(ngx_http_busy_lock_t *bl,
+ ngx_http_busy_lock_ctx_t *bc);
+
+char *ngx_http_set_busy_lock_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+#endif /* _NGX_HTTP_BUSY_LOCK_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_cache.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_cache.h
new file mode 100644
index 00000000000..1cfd9fe845f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_cache.h
@@ -0,0 +1,169 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_CACHE_H_INCLUDED_
+#define _NGX_HTTP_CACHE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_CACHE_MISS 1
+#define NGX_HTTP_CACHE_BYPASS 2
+#define NGX_HTTP_CACHE_EXPIRED 3
+#define NGX_HTTP_CACHE_STALE 4
+#define NGX_HTTP_CACHE_UPDATING 5
+#define NGX_HTTP_CACHE_REVALIDATED 6
+#define NGX_HTTP_CACHE_HIT 7
+#define NGX_HTTP_CACHE_SCARCE 8
+
+#define NGX_HTTP_CACHE_KEY_LEN 16
+#define NGX_HTTP_CACHE_ETAG_LEN 42
+
+#define NGX_HTTP_CACHE_VERSION 2
+
+
+typedef struct {
+ ngx_uint_t status;
+ time_t valid;
+} ngx_http_cache_valid_t;
+
+
+typedef struct {
+ ngx_rbtree_node_t node;
+ ngx_queue_t queue;
+
+ u_char key[NGX_HTTP_CACHE_KEY_LEN
+ - sizeof(ngx_rbtree_key_t)];
+
+ unsigned count:20;
+ unsigned uses:10;
+ unsigned valid_msec:10;
+ unsigned error:10;
+ unsigned exists:1;
+ unsigned updating:1;
+ unsigned deleting:1;
+ /* 11 unused bits */
+
+ ngx_file_uniq_t uniq;
+ time_t expire;
+ time_t valid_sec;
+ size_t body_start;
+ off_t fs_size;
+} ngx_http_file_cache_node_t;
+
+
+struct ngx_http_cache_s {
+ ngx_file_t file;
+ ngx_array_t keys;
+ uint32_t crc32;
+ u_char key[NGX_HTTP_CACHE_KEY_LEN];
+
+ ngx_file_uniq_t uniq;
+ time_t valid_sec;
+ time_t last_modified;
+ time_t date;
+
+ ngx_str_t etag;
+
+ size_t header_start;
+ size_t body_start;
+ off_t length;
+ off_t fs_size;
+
+ ngx_uint_t min_uses;
+ ngx_uint_t error;
+ ngx_uint_t valid_msec;
+
+ ngx_buf_t *buf;
+
+ ngx_http_file_cache_t *file_cache;
+ ngx_http_file_cache_node_t *node;
+
+ ngx_msec_t lock_timeout;
+ ngx_msec_t wait_time;
+
+ ngx_event_t wait_event;
+
+ unsigned lock:1;
+ unsigned waiting:1;
+
+ unsigned updated:1;
+ unsigned updating:1;
+ unsigned exists:1;
+ unsigned temp_file:1;
+};
+
+
+typedef struct {
+ ngx_uint_t version;
+ time_t valid_sec;
+ time_t last_modified;
+ time_t date;
+ uint32_t crc32;
+ u_short valid_msec;
+ u_short header_start;
+ u_short body_start;
+ u_char etag_len;
+ u_char etag[NGX_HTTP_CACHE_ETAG_LEN];
+} ngx_http_file_cache_header_t;
+
+
+typedef struct {
+ ngx_rbtree_t rbtree;
+ ngx_rbtree_node_t sentinel;
+ ngx_queue_t queue;
+ ngx_atomic_t cold;
+ ngx_atomic_t loading;
+ off_t size;
+} ngx_http_file_cache_sh_t;
+
+
+struct ngx_http_file_cache_s {
+ ngx_http_file_cache_sh_t *sh;
+ ngx_slab_pool_t *shpool;
+
+ ngx_path_t *path;
+
+ off_t max_size;
+ size_t bsize;
+
+ time_t inactive;
+
+ ngx_uint_t files;
+ ngx_uint_t loader_files;
+ ngx_msec_t last;
+ ngx_msec_t loader_sleep;
+ ngx_msec_t loader_threshold;
+
+ ngx_shm_zone_t *shm_zone;
+};
+
+
+ngx_int_t ngx_http_file_cache_new(ngx_http_request_t *r);
+ngx_int_t ngx_http_file_cache_create(ngx_http_request_t *r);
+void ngx_http_file_cache_create_key(ngx_http_request_t *r);
+ngx_int_t ngx_http_file_cache_open(ngx_http_request_t *r);
+void ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf);
+void ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf);
+void ngx_http_file_cache_update_header(ngx_http_request_t *r);
+ngx_int_t ngx_http_cache_send(ngx_http_request_t *);
+void ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf);
+time_t ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status);
+
+char *ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+extern ngx_str_t ngx_http_cache_status[];
+
+
+#endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_config.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_config.h
new file mode 100644
index 00000000000..2208c601b96
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_config.h
@@ -0,0 +1,75 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_CONFIG_H_INCLUDED_
+#define _NGX_HTTP_CONFIG_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ void **main_conf;
+ void **srv_conf;
+ void **loc_conf;
+} ngx_http_conf_ctx_t;
+
+
+typedef struct {
+ ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
+ ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
+
+ void *(*create_main_conf)(ngx_conf_t *cf);
+ char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
+
+ void *(*create_srv_conf)(ngx_conf_t *cf);
+ char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
+
+ void *(*create_loc_conf)(ngx_conf_t *cf);
+ char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
+} ngx_http_module_t;
+
+
+#define NGX_HTTP_MODULE 0x50545448 /* "HTTP" */
+
+#define NGX_HTTP_MAIN_CONF 0x02000000
+#define NGX_HTTP_SRV_CONF 0x04000000
+#define NGX_HTTP_LOC_CONF 0x08000000
+#define NGX_HTTP_UPS_CONF 0x10000000
+#define NGX_HTTP_SIF_CONF 0x20000000
+#define NGX_HTTP_LIF_CONF 0x40000000
+#define NGX_HTTP_LMT_CONF 0x80000000
+
+
+#define NGX_HTTP_MAIN_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, main_conf)
+#define NGX_HTTP_SRV_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, srv_conf)
+#define NGX_HTTP_LOC_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, loc_conf)
+
+
+#define ngx_http_get_module_main_conf(r, module) \
+ (r)->main_conf[module.ctx_index]
+#define ngx_http_get_module_srv_conf(r, module) (r)->srv_conf[module.ctx_index]
+#define ngx_http_get_module_loc_conf(r, module) (r)->loc_conf[module.ctx_index]
+
+
+#define ngx_http_conf_get_module_main_conf(cf, module) \
+ ((ngx_http_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
+#define ngx_http_conf_get_module_srv_conf(cf, module) \
+ ((ngx_http_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]
+#define ngx_http_conf_get_module_loc_conf(cf, module) \
+ ((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index]
+
+#define ngx_http_cycle_get_module_main_conf(cycle, module) \
+ (cycle->conf_ctx[ngx_http_module.index] ? \
+ ((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index]) \
+ ->main_conf[module.ctx_index]: \
+ NULL)
+
+
+#endif /* _NGX_HTTP_CONFIG_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_copy_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_copy_filter_module.c
new file mode 100644
index 00000000000..3ad27b0425b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_copy_filter_module.c
@@ -0,0 +1,303 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_bufs_t bufs;
+} ngx_http_copy_filter_conf_t;
+
+
+#if (NGX_HAVE_FILE_AIO)
+static void ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx,
+ ngx_file_t *file);
+static void ngx_http_copy_aio_event_handler(ngx_event_t *ev);
+#if (NGX_HAVE_AIO_SENDFILE)
+static void ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev);
+#endif
+#endif
+
+static void *ngx_http_copy_filter_create_conf(ngx_conf_t *cf);
+static char *ngx_http_copy_filter_merge_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static ngx_int_t ngx_http_copy_filter_init(ngx_conf_t *cf);
+
+
+static ngx_command_t ngx_http_copy_filter_commands[] = {
+
+ { ngx_string("output_buffers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
+ ngx_conf_set_bufs_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_copy_filter_conf_t, bufs),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_copy_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_copy_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ ngx_http_copy_filter_create_conf, /* create location configuration */
+ ngx_http_copy_filter_merge_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_copy_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_copy_filter_module_ctx, /* module context */
+ ngx_http_copy_filter_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+ ngx_output_chain_ctx_t *ctx;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_copy_filter_conf_t *conf;
+
+ c = r->connection;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http copy filter: \"%V?%V\"", &r->uri, &r->args);
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);
+
+ if (ctx == NULL) {
+ ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t));
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_http_set_ctx(r, ctx, ngx_http_copy_filter_module);
+
+ conf = ngx_http_get_module_loc_conf(r, ngx_http_copy_filter_module);
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ctx->sendfile = c->sendfile;
+ ctx->need_in_memory = r->main_filter_need_in_memory
+ || r->filter_need_in_memory;
+ ctx->need_in_temp = r->filter_need_temporary;
+
+ ctx->alignment = clcf->directio_alignment;
+
+ ctx->pool = r->pool;
+ ctx->bufs = conf->bufs;
+ ctx->tag = (ngx_buf_tag_t) &ngx_http_copy_filter_module;
+
+ ctx->output_filter = (ngx_output_chain_filter_pt)
+ ngx_http_next_body_filter;
+ ctx->filter_ctx = r;
+
+#if (NGX_HAVE_FILE_AIO)
+ if (ngx_file_aio) {
+ if (clcf->aio) {
+ ctx->aio_handler = ngx_http_copy_aio_handler;
+ }
+#if (NGX_HAVE_AIO_SENDFILE)
+ c->aio_sendfile = (clcf->aio == NGX_HTTP_AIO_SENDFILE);
+#endif
+ }
+#endif
+
+ if (in && in->buf && ngx_buf_size(in->buf)) {
+ r->request_output = 1;
+ }
+ }
+
+#if (NGX_HAVE_FILE_AIO)
+ ctx->aio = r->aio;
+#endif
+
+ for ( ;; ) {
+ rc = ngx_output_chain(ctx, in);
+
+ if (ctx->in == NULL) {
+ r->buffered &= ~NGX_HTTP_COPY_BUFFERED;
+
+ } else {
+ r->buffered |= NGX_HTTP_COPY_BUFFERED;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http copy filter: %i \"%V?%V\"", rc, &r->uri, &r->args);
+
+#if (NGX_HAVE_FILE_AIO && NGX_HAVE_AIO_SENDFILE)
+
+ if (c->busy_sendfile) {
+ ssize_t n;
+ off_t offset;
+ ngx_file_t *file;
+ ngx_http_ephemeral_t *e;
+
+ if (r->aio) {
+ c->busy_sendfile = NULL;
+ return rc;
+ }
+
+ file = c->busy_sendfile->file;
+ offset = c->busy_sendfile->file_pos;
+
+ if (file->aio) {
+ c->busy_count = (offset == file->aio->last_offset) ?
+ c->busy_count + 1 : 0;
+ file->aio->last_offset = offset;
+
+ if (c->busy_count > 2) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "sendfile(%V) returned busy again",
+ &file->name);
+ c->aio_sendfile = 0;
+ }
+ }
+
+ c->busy_sendfile = NULL;
+ e = (ngx_http_ephemeral_t *) &r->uri_start;
+
+ n = ngx_file_aio_read(file, &e->aio_preload, 1, offset, r->pool);
+
+ if (n > 0) {
+ in = NULL;
+ continue;
+ }
+
+ rc = n;
+
+ if (rc == NGX_AGAIN) {
+ file->aio->data = r;
+ file->aio->handler = ngx_http_copy_aio_sendfile_event_handler;
+
+ r->main->blocked++;
+ r->aio = 1;
+ }
+ }
+#endif
+
+ return rc;
+ }
+}
+
+
+#if (NGX_HAVE_FILE_AIO)
+
+static void
+ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx, ngx_file_t *file)
+{
+ ngx_http_request_t *r;
+
+ r = ctx->filter_ctx;
+
+ file->aio->data = r;
+ file->aio->handler = ngx_http_copy_aio_event_handler;
+
+ r->main->blocked++;
+ r->aio = 1;
+ ctx->aio = 1;
+}
+
+
+static void
+ngx_http_copy_aio_event_handler(ngx_event_t *ev)
+{
+ ngx_event_aio_t *aio;
+ ngx_http_request_t *r;
+
+ aio = ev->data;
+ r = aio->data;
+
+ r->main->blocked--;
+ r->aio = 0;
+
+ r->connection->write->handler(r->connection->write);
+}
+
+
+#if (NGX_HAVE_AIO_SENDFILE)
+
+static void
+ngx_http_copy_aio_sendfile_event_handler(ngx_event_t *ev)
+{
+ ngx_event_aio_t *aio;
+ ngx_http_request_t *r;
+
+ aio = ev->data;
+ r = aio->data;
+
+ r->main->blocked--;
+ r->aio = 0;
+ ev->complete = 0;
+
+ r->connection->write->handler(r->connection->write);
+}
+
+#endif
+#endif
+
+
+static void *
+ngx_http_copy_filter_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_copy_filter_conf_t *conf;
+
+ conf = ngx_palloc(cf->pool, sizeof(ngx_http_copy_filter_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ conf->bufs.num = 0;
+
+ return conf;
+}
+
+
+static char *
+ngx_http_copy_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_copy_filter_conf_t *prev = parent;
+ ngx_http_copy_filter_conf_t *conf = child;
+
+ ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 1, 32768);
+
+ return NULL;
+}
+
+
+static ngx_int_t
+ngx_http_copy_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_copy_filter;
+
+ return NGX_OK;
+}
+
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_core_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_core_module.c
new file mode 100644
index 00000000000..4071b612649
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_core_module.c
@@ -0,0 +1,5283 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ u_char *name;
+ uint32_t method;
+} ngx_http_method_name_t;
+
+
+#define NGX_HTTP_REQUEST_BODY_FILE_OFF 0
+#define NGX_HTTP_REQUEST_BODY_FILE_ON 1
+#define NGX_HTTP_REQUEST_BODY_FILE_CLEAN 2
+
+
+static ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r);
+static ngx_int_t ngx_http_core_find_static_location(ngx_http_request_t *r,
+ ngx_http_location_tree_node_t *node);
+
+static ngx_int_t ngx_http_core_preconfiguration(ngx_conf_t *cf);
+static void *ngx_http_core_create_main_conf(ngx_conf_t *cf);
+static char *ngx_http_core_init_main_conf(ngx_conf_t *cf, void *conf);
+static void *ngx_http_core_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_http_core_merge_srv_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+static void *ngx_http_core_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_core_merge_loc_conf(ngx_conf_t *cf,
+ void *parent, void *child);
+
+static char *ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *dummy);
+static char *ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *dummy);
+static ngx_int_t ngx_http_core_regex_location(ngx_conf_t *cf,
+ ngx_http_core_loc_conf_t *clcf, ngx_str_t *regex, ngx_uint_t caseless);
+
+static char *ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_core_type(ngx_conf_t *cf, ngx_command_t *dummy,
+ void *conf);
+
+static char *ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_http_core_limit_except(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_core_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_core_internal(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+#if (NGX_HTTP_GZIP)
+static ngx_int_t ngx_http_gzip_accept_encoding(ngx_str_t *ae);
+static ngx_uint_t ngx_http_gzip_quantity(u_char *p, u_char *last);
+static char *ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+#endif
+static ngx_int_t ngx_http_get_forwarded_addr_internal(ngx_http_request_t *r,
+ ngx_addr_t *addr, u_char *xff, size_t xfflen, ngx_array_t *proxies,
+ int recursive);
+#if (NGX_HAVE_OPENAT)
+static char *ngx_http_disable_symlinks(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+#endif
+
+static char *ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data);
+
+static ngx_conf_post_t ngx_http_core_lowat_post =
+ { ngx_http_core_lowat_check };
+
+static ngx_conf_post_handler_pt ngx_http_core_pool_size_p =
+ ngx_http_core_pool_size;
+
+static ngx_conf_deprecated_t ngx_conf_deprecated_optimize_server_names = {
+ ngx_conf_deprecated, "optimize_server_names", "server_name_in_redirect"
+};
+
+static ngx_conf_deprecated_t ngx_conf_deprecated_open_file_cache_retest = {
+ ngx_conf_deprecated, "open_file_cache_retest", "open_file_cache_valid"
+};
+
+static ngx_conf_deprecated_t ngx_conf_deprecated_satisfy_any = {
+ ngx_conf_deprecated, "satisfy_any", "satisfy"
+};
+
+
+static ngx_conf_enum_t ngx_http_core_request_body_in_file[] = {
+ { ngx_string("off"), NGX_HTTP_REQUEST_BODY_FILE_OFF },
+ { ngx_string("on"), NGX_HTTP_REQUEST_BODY_FILE_ON },
+ { ngx_string("clean"), NGX_HTTP_REQUEST_BODY_FILE_CLEAN },
+ { ngx_null_string, 0 }
+};
+
+
+#if (NGX_HAVE_FILE_AIO)
+
+static ngx_conf_enum_t ngx_http_core_aio[] = {
+ { ngx_string("off"), NGX_HTTP_AIO_OFF },
+ { ngx_string("on"), NGX_HTTP_AIO_ON },
+#if (NGX_HAVE_AIO_SENDFILE)
+ { ngx_string("sendfile"), NGX_HTTP_AIO_SENDFILE },
+#endif
+ { ngx_null_string, 0 }
+};
+
+#endif
+
+
+static ngx_conf_enum_t ngx_http_core_satisfy[] = {
+ { ngx_string("all"), NGX_HTTP_SATISFY_ALL },
+ { ngx_string("any"), NGX_HTTP_SATISFY_ANY },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_conf_enum_t ngx_http_core_lingering_close[] = {
+ { ngx_string("off"), NGX_HTTP_LINGERING_OFF },
+ { ngx_string("on"), NGX_HTTP_LINGERING_ON },
+ { ngx_string("always"), NGX_HTTP_LINGERING_ALWAYS },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_conf_enum_t ngx_http_core_if_modified_since[] = {
+ { ngx_string("off"), NGX_HTTP_IMS_OFF },
+ { ngx_string("exact"), NGX_HTTP_IMS_EXACT },
+ { ngx_string("before"), NGX_HTTP_IMS_BEFORE },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_conf_bitmask_t ngx_http_core_keepalive_disable[] = {
+ { ngx_string("none"), NGX_HTTP_KEEPALIVE_DISABLE_NONE },
+ { ngx_string("msie6"), NGX_HTTP_KEEPALIVE_DISABLE_MSIE6 },
+ { ngx_string("safari"), NGX_HTTP_KEEPALIVE_DISABLE_SAFARI },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_path_init_t ngx_http_client_temp_path = {
+ ngx_string(NGX_HTTP_CLIENT_TEMP_PATH), { 0, 0, 0 }
+};
+
+
+#if (NGX_HTTP_GZIP)
+
+static ngx_conf_enum_t ngx_http_gzip_http_version[] = {
+ { ngx_string("1.0"), NGX_HTTP_VERSION_10 },
+ { ngx_string("1.1"), NGX_HTTP_VERSION_11 },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_conf_bitmask_t ngx_http_gzip_proxied_mask[] = {
+ { ngx_string("off"), NGX_HTTP_GZIP_PROXIED_OFF },
+ { ngx_string("expired"), NGX_HTTP_GZIP_PROXIED_EXPIRED },
+ { ngx_string("no-cache"), NGX_HTTP_GZIP_PROXIED_NO_CACHE },
+ { ngx_string("no-store"), NGX_HTTP_GZIP_PROXIED_NO_STORE },
+ { ngx_string("private"), NGX_HTTP_GZIP_PROXIED_PRIVATE },
+ { ngx_string("no_last_modified"), NGX_HTTP_GZIP_PROXIED_NO_LM },
+ { ngx_string("no_etag"), NGX_HTTP_GZIP_PROXIED_NO_ETAG },
+ { ngx_string("auth"), NGX_HTTP_GZIP_PROXIED_AUTH },
+ { ngx_string("any"), NGX_HTTP_GZIP_PROXIED_ANY },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_str_t ngx_http_gzip_no_cache = ngx_string("no-cache");
+static ngx_str_t ngx_http_gzip_no_store = ngx_string("no-store");
+static ngx_str_t ngx_http_gzip_private = ngx_string("private");
+
+#endif
+
+
+static ngx_command_t ngx_http_core_commands[] = {
+
+ { ngx_string("variables_hash_max_size"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ offsetof(ngx_http_core_main_conf_t, variables_hash_max_size),
+ NULL },
+
+ { ngx_string("variables_hash_bucket_size"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ offsetof(ngx_http_core_main_conf_t, variables_hash_bucket_size),
+ NULL },
+
+ { ngx_string("server_names_hash_max_size"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ offsetof(ngx_http_core_main_conf_t, server_names_hash_max_size),
+ NULL },
+
+ { ngx_string("server_names_hash_bucket_size"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ offsetof(ngx_http_core_main_conf_t, server_names_hash_bucket_size),
+ NULL },
+
+ { ngx_string("server"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_http_core_server,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("connection_pool_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_core_srv_conf_t, connection_pool_size),
+ &ngx_http_core_pool_size_p },
+
+ { ngx_string("request_pool_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_core_srv_conf_t, request_pool_size),
+ &ngx_http_core_pool_size_p },
+
+ { ngx_string("client_header_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_core_srv_conf_t, client_header_timeout),
+ NULL },
+
+ { ngx_string("client_header_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_core_srv_conf_t, client_header_buffer_size),
+ NULL },
+
+ { ngx_string("large_client_header_buffers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE2,
+ ngx_conf_set_bufs_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_core_srv_conf_t, large_client_header_buffers),
+ NULL },
+
+ { ngx_string("optimize_server_names"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, server_name_in_redirect),
+ &ngx_conf_deprecated_optimize_server_names },
+
+ { ngx_string("ignore_invalid_headers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_core_srv_conf_t, ignore_invalid_headers),
+ NULL },
+
+ { ngx_string("merge_slashes"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_core_srv_conf_t, merge_slashes),
+ NULL },
+
+ { ngx_string("underscores_in_headers"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_core_srv_conf_t, underscores_in_headers),
+ NULL },
+
+ { ngx_string("location"),
+ NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
+ ngx_http_core_location,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("listen"),
+ NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
+ ngx_http_core_listen,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("server_name"),
+ NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
+ ngx_http_core_server_name,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("types_hash_max_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, types_hash_max_size),
+ NULL },
+
+ { ngx_string("types_hash_bucket_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, types_hash_bucket_size),
+ NULL },
+
+ { ngx_string("types"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
+ |NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_http_core_types,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("default_type"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, default_type),
+ NULL },
+
+ { ngx_string("root"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_TAKE1,
+ ngx_http_core_root,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("alias"),
+ NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_core_root,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("limit_except"),
+ NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE,
+ ngx_http_core_limit_except,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("client_max_body_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_off_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, client_max_body_size),
+ NULL },
+
+ { ngx_string("client_body_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, client_body_buffer_size),
+ NULL },
+
+ { ngx_string("client_body_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, client_body_timeout),
+ NULL },
+
+ { ngx_string("client_body_temp_path"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
+ ngx_conf_set_path_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, client_body_temp_path),
+ NULL },
+
+ { ngx_string("client_body_in_file_only"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, client_body_in_file_only),
+ &ngx_http_core_request_body_in_file },
+
+ { ngx_string("client_body_in_single_buffer"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, client_body_in_single_buffer),
+ NULL },
+
+ { ngx_string("sendfile"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, sendfile),
+ NULL },
+
+ { ngx_string("sendfile_max_chunk"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, sendfile_max_chunk),
+ NULL },
+
+#if (NGX_HAVE_FILE_AIO)
+
+ { ngx_string("aio"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, aio),
+ &ngx_http_core_aio },
+
+#endif
+
+ { ngx_string("read_ahead"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, read_ahead),
+ NULL },
+
+ { ngx_string("directio"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_http_core_directio,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("directio_alignment"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_off_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, directio_alignment),
+ NULL },
+
+ { ngx_string("tcp_nopush"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, tcp_nopush),
+ NULL },
+
+ { ngx_string("tcp_nodelay"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, tcp_nodelay),
+ NULL },
+
+ { ngx_string("send_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, send_timeout),
+ NULL },
+
+ { ngx_string("send_lowat"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, send_lowat),
+ &ngx_http_core_lowat_post },
+
+ { ngx_string("postpone_output"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, postpone_output),
+ NULL },
+
+ { ngx_string("limit_rate"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, limit_rate),
+ NULL },
+
+ { ngx_string("limit_rate_after"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, limit_rate_after),
+ NULL },
+
+ { ngx_string("keepalive_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
+ ngx_http_core_keepalive,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("keepalive_requests"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, keepalive_requests),
+ NULL },
+
+ { ngx_string("keepalive_disable"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, keepalive_disable),
+ &ngx_http_core_keepalive_disable },
+
+ { ngx_string("satisfy"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, satisfy),
+ &ngx_http_core_satisfy },
+
+ { ngx_string("satisfy_any"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, satisfy),
+ &ngx_conf_deprecated_satisfy_any },
+
+ { ngx_string("internal"),
+ NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
+ ngx_http_core_internal,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("lingering_close"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, lingering_close),
+ &ngx_http_core_lingering_close },
+
+ { ngx_string("lingering_time"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, lingering_time),
+ NULL },
+
+ { ngx_string("lingering_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, lingering_timeout),
+ NULL },
+
+ { ngx_string("reset_timedout_connection"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, reset_timedout_connection),
+ NULL },
+
+ { ngx_string("server_name_in_redirect"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, server_name_in_redirect),
+ NULL },
+
+ { ngx_string("port_in_redirect"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, port_in_redirect),
+ NULL },
+
+ { ngx_string("msie_padding"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, msie_padding),
+ NULL },
+
+ { ngx_string("msie_refresh"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, msie_refresh),
+ NULL },
+
+ { ngx_string("log_not_found"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, log_not_found),
+ NULL },
+
+ { ngx_string("log_subrequest"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, log_subrequest),
+ NULL },
+
+ { ngx_string("recursive_error_pages"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, recursive_error_pages),
+ NULL },
+
+ { ngx_string("server_tokens"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, server_tokens),
+ NULL },
+
+ { ngx_string("if_modified_since"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, if_modified_since),
+ &ngx_http_core_if_modified_since },
+
+ { ngx_string("max_ranges"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, max_ranges),
+ NULL },
+
+ { ngx_string("chunked_transfer_encoding"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, chunked_transfer_encoding),
+ NULL },
+
+ { ngx_string("etag"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, etag),
+ NULL },
+
+ { ngx_string("error_page"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_2MORE,
+ ngx_http_core_error_page,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("try_files"),
+ NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE,
+ ngx_http_core_try_files,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("post_action"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, post_action),
+ NULL },
+
+ { ngx_string("error_log"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_core_error_log,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("open_file_cache"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
+ ngx_http_core_open_file_cache,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, open_file_cache),
+ NULL },
+
+ { ngx_string("open_file_cache_valid"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_sec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, open_file_cache_valid),
+ NULL },
+
+ { ngx_string("open_file_cache_retest"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_sec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, open_file_cache_valid),
+ &ngx_conf_deprecated_open_file_cache_retest },
+
+ { ngx_string("open_file_cache_min_uses"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, open_file_cache_min_uses),
+ NULL },
+
+ { ngx_string("open_file_cache_errors"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, open_file_cache_errors),
+ NULL },
+
+ { ngx_string("open_file_cache_events"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, open_file_cache_events),
+ NULL },
+
+ { ngx_string("resolver"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_core_resolver,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("resolver_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, resolver_timeout),
+ NULL },
+
+#if (NGX_HTTP_GZIP)
+
+ { ngx_string("gzip_vary"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, gzip_vary),
+ NULL },
+
+ { ngx_string("gzip_http_version"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, gzip_http_version),
+ &ngx_http_gzip_http_version },
+
+ { ngx_string("gzip_proxied"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, gzip_proxied),
+ &ngx_http_gzip_proxied_mask },
+
+ { ngx_string("gzip_disable"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_gzip_disable,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+#endif
+
+#if (NGX_HAVE_OPENAT)
+
+ { ngx_string("disable_symlinks"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
+ ngx_http_disable_symlinks,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
+#endif
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_core_module_ctx = {
+ ngx_http_core_preconfiguration, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ ngx_http_core_create_main_conf, /* create main configuration */
+ ngx_http_core_init_main_conf, /* init main configuration */
+
+ ngx_http_core_create_srv_conf, /* create server configuration */
+ ngx_http_core_merge_srv_conf, /* merge server configuration */
+
+ ngx_http_core_create_loc_conf, /* create location configuration */
+ ngx_http_core_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_core_module = {
+ NGX_MODULE_V1,
+ &ngx_http_core_module_ctx, /* module context */
+ ngx_http_core_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+ngx_str_t ngx_http_core_get_method = { 3, (u_char *) "GET " };
+
+
+void
+ngx_http_handler(ngx_http_request_t *r)
+{
+ ngx_http_core_main_conf_t *cmcf;
+
+ r->connection->log->action = NULL;
+
+ r->connection->unexpected_eof = 0;
+
+ if (!r->internal) {
+ switch (r->headers_in.connection_type) {
+ case 0:
+ r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);
+ break;
+
+ case NGX_HTTP_CONNECTION_CLOSE:
+ r->keepalive = 0;
+ break;
+
+ case NGX_HTTP_CONNECTION_KEEP_ALIVE:
+ r->keepalive = 1;
+ break;
+ }
+
+ r->lingering_close = (r->headers_in.content_length_n > 0
+ || r->headers_in.chunked);
+ r->phase_handler = 0;
+
+ } else {
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+ r->phase_handler = cmcf->phase_engine.server_rewrite_index;
+ }
+
+ r->valid_location = 1;
+#if (NGX_HTTP_GZIP)
+ r->gzip_tested = 0;
+ r->gzip_ok = 0;
+ r->gzip_vary = 0;
+#endif
+
+ r->write_event_handler = ngx_http_core_run_phases;
+ ngx_http_core_run_phases(r);
+}
+
+
+void
+ngx_http_core_run_phases(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_http_phase_handler_t *ph;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ ph = cmcf->phase_engine.handlers;
+
+ while (ph[r->phase_handler].checker) {
+
+ rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
+
+ if (rc == NGX_OK) {
+ return;
+ }
+ }
+}
+
+
+ngx_int_t
+ngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
+{
+ ngx_int_t rc;
+
+ /*
+ * generic phase checker,
+ * used by the post read and pre-access phases
+ */
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "generic phase: %ui", r->phase_handler);
+
+ rc = ph->handler(r);
+
+ if (rc == NGX_OK) {
+ r->phase_handler = ph->next;
+ return NGX_AGAIN;
+ }
+
+ if (rc == NGX_DECLINED) {
+ r->phase_handler++;
+ return NGX_AGAIN;
+ }
+
+ if (rc == NGX_AGAIN || rc == NGX_DONE) {
+ return NGX_OK;
+ }
+
+ /* rc == NGX_ERROR || rc == NGX_HTTP_... */
+
+ ngx_http_finalize_request(r, rc);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_core_rewrite_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
+{
+ ngx_int_t rc;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "rewrite phase: %ui", r->phase_handler);
+
+ rc = ph->handler(r);
+
+ if (rc == NGX_DECLINED) {
+ r->phase_handler++;
+ return NGX_AGAIN;
+ }
+
+ if (rc == NGX_DONE) {
+ return NGX_OK;
+ }
+
+ /* NGX_OK, NGX_AGAIN, NGX_ERROR, NGX_HTTP_... */
+
+ ngx_http_finalize_request(r, rc);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_core_find_config_phase(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph)
+{
+ u_char *p;
+ size_t len;
+ ngx_int_t rc;
+ ngx_http_core_loc_conf_t *clcf;
+
+ r->content_handler = NULL;
+ r->uri_changed = 0;
+
+ rc = ngx_http_core_find_location(r);
+
+ if (rc == NGX_ERROR) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_OK;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (!r->internal && clcf->internal) {
+ ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
+ return NGX_OK;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "using configuration \"%s%V\"",
+ (clcf->noname ? "*" : (clcf->exact_match ? "=" : "")),
+ &clcf->name);
+
+ ngx_http_update_location_config(r);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http cl:%O max:%O",
+ r->headers_in.content_length_n, clcf->client_max_body_size);
+
+ if (r->headers_in.content_length_n != -1
+ && !r->discard_body
+ && clcf->client_max_body_size
+ && clcf->client_max_body_size < r->headers_in.content_length_n)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client intended to send too large body: %O bytes",
+ r->headers_in.content_length_n);
+
+ r->expect_tested = 1;
+ (void) ngx_http_discard_request_body(r);
+ ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE);
+ return NGX_OK;
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_http_clear_location(r);
+
+ r->headers_out.location = ngx_list_push(&r->headers_out.headers);
+ if (r->headers_out.location == NULL) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_OK;
+ }
+
+ /*
+ * we do not need to set the r->headers_out.location->hash and
+ * r->headers_out.location->key fields
+ */
+
+ if (r->args.len == 0) {
+ r->headers_out.location->value = clcf->name;
+
+ } else {
+ len = clcf->name.len + 1 + r->args.len;
+ p = ngx_pnalloc(r->pool, len);
+
+ if (p == NULL) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_OK;
+ }
+
+ r->headers_out.location->value.len = len;
+ r->headers_out.location->value.data = p;
+
+ p = ngx_cpymem(p, clcf->name.data, clcf->name.len);
+ *p++ = '?';
+ ngx_memcpy(p, r->args.data, r->args.len);
+ }
+
+ ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY);
+ return NGX_OK;
+ }
+
+ r->phase_handler++;
+ return NGX_AGAIN;
+}
+
+
+ngx_int_t
+ngx_http_core_post_rewrite_phase(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph)
+{
+ ngx_http_core_srv_conf_t *cscf;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "post rewrite phase: %ui", r->phase_handler);
+
+ if (!r->uri_changed) {
+ r->phase_handler++;
+ return NGX_AGAIN;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "uri changes: %d", r->uri_changes);
+
+ /*
+ * gcc before 3.3 compiles the broken code for
+ * if (r->uri_changes-- == 0)
+ * if the r->uri_changes is defined as
+ * unsigned uri_changes:4
+ */
+
+ r->uri_changes--;
+
+ if (r->uri_changes == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "rewrite or internal redirection cycle "
+ "while processing \"%V\"", &r->uri);
+
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_OK;
+ }
+
+ r->phase_handler = ph->next;
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+ r->loc_conf = cscf->ctx->loc_conf;
+
+ return NGX_AGAIN;
+}
+
+
+ngx_int_t
+ngx_http_core_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
+{
+ ngx_int_t rc;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (r != r->main) {
+ r->phase_handler = ph->next;
+ return NGX_AGAIN;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "access phase: %ui", r->phase_handler);
+
+ rc = ph->handler(r);
+
+ if (rc == NGX_DECLINED) {
+ r->phase_handler++;
+ return NGX_AGAIN;
+ }
+
+ if (rc == NGX_AGAIN || rc == NGX_DONE) {
+ return NGX_OK;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {
+
+ if (rc == NGX_OK) {
+ r->phase_handler++;
+ return NGX_AGAIN;
+ }
+
+ } else {
+ if (rc == NGX_OK) {
+ r->access_code = 0;
+
+ if (r->headers_out.www_authenticate) {
+ r->headers_out.www_authenticate->hash = 0;
+ }
+
+ r->phase_handler = ph->next;
+ return NGX_AGAIN;
+ }
+
+ if (rc == NGX_HTTP_FORBIDDEN || rc == NGX_HTTP_UNAUTHORIZED) {
+ if (r->access_code != NGX_HTTP_UNAUTHORIZED) {
+ r->access_code = rc;
+ }
+
+ r->phase_handler++;
+ return NGX_AGAIN;
+ }
+ }
+
+ /* rc == NGX_ERROR || rc == NGX_HTTP_... */
+
+ ngx_http_finalize_request(r, rc);
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_core_post_access_phase(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph)
+{
+ ngx_int_t access_code;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "post access phase: %ui", r->phase_handler);
+
+ access_code = r->access_code;
+
+ if (access_code) {
+ if (access_code == NGX_HTTP_FORBIDDEN) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "access forbidden by rule");
+ }
+
+ r->access_code = 0;
+ ngx_http_finalize_request(r, access_code);
+ return NGX_OK;
+ }
+
+ r->phase_handler++;
+ return NGX_AGAIN;
+}
+
+
+ngx_int_t
+ngx_http_core_try_files_phase(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph)
+{
+ size_t len, root, alias, reserve, allocated;
+ u_char *p, *name;
+ ngx_str_t path, args;
+ ngx_uint_t test_dir;
+ ngx_http_try_file_t *tf;
+ ngx_open_file_info_t of;
+ ngx_http_script_code_pt code;
+ ngx_http_script_engine_t e;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_script_len_code_pt lcode;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "try files phase: %ui", r->phase_handler);
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->try_files == NULL) {
+ r->phase_handler++;
+ return NGX_AGAIN;
+ }
+
+ allocated = 0;
+ root = 0;
+ name = NULL;
+ /* suppress MSVC warning */
+ path.data = NULL;
+
+ tf = clcf->try_files;
+
+ alias = clcf->alias;
+
+ for ( ;; ) {
+
+ if (tf->lengths) {
+ ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
+
+ e.ip = tf->lengths->elts;
+ e.request = r;
+
+ /* 1 is for terminating '\0' as in static names */
+ len = 1;
+
+ while (*(uintptr_t *) e.ip) {
+ lcode = *(ngx_http_script_len_code_pt *) e.ip;
+ len += lcode(&e);
+ }
+
+ } else {
+ len = tf->name.len;
+ }
+
+ if (!alias) {
+ reserve = len > r->uri.len ? len - r->uri.len : 0;
+
+ } else if (alias == NGX_MAX_SIZE_T_VALUE) {
+ reserve = len;
+
+ } else {
+ reserve = len > r->uri.len - alias ? len - (r->uri.len - alias) : 0;
+ }
+
+ if (reserve > allocated || !allocated) {
+
+ /* 16 bytes are preallocation */
+ allocated = reserve + 16;
+
+ if (ngx_http_map_uri_to_path(r, &path, &root, allocated) == NULL) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_OK;
+ }
+
+ name = path.data + root;
+ }
+
+ if (tf->values == NULL) {
+
+ /* tf->name.len includes the terminating '\0' */
+
+ ngx_memcpy(name, tf->name.data, tf->name.len);
+
+ path.len = (name + tf->name.len - 1) - path.data;
+
+ } else {
+ e.ip = tf->values->elts;
+ e.pos = name;
+ e.flushed = 1;
+
+ while (*(uintptr_t *) e.ip) {
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) &e);
+ }
+
+ path.len = e.pos - path.data;
+
+ *e.pos = '\0';
+
+ if (alias && ngx_strncmp(name, clcf->name.data, alias) == 0) {
+ ngx_memmove(name, name + alias, len - alias);
+ path.len -= alias;
+ }
+ }
+
+ test_dir = tf->test_dir;
+
+ tf++;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "trying to use %s: \"%s\" \"%s\"",
+ test_dir ? "dir" : "file", name, path.data);
+
+ if (tf->lengths == NULL && tf->name.len == 0) {
+
+ if (tf->code) {
+ ngx_http_finalize_request(r, tf->code);
+ return NGX_OK;
+ }
+
+ path.len -= root;
+ path.data += root;
+
+ if (path.data[0] == '@') {
+ (void) ngx_http_named_location(r, &path);
+
+ } else {
+ ngx_http_split_args(r, &path, &args);
+
+ (void) ngx_http_internal_redirect(r, &path, &args);
+ }
+
+ ngx_http_finalize_request(r, NGX_DONE);
+ return NGX_OK;
+ }
+
+ ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+ of.read_ahead = clcf->read_ahead;
+ of.directio = clcf->directio;
+ of.valid = clcf->open_file_cache_valid;
+ of.min_uses = clcf->open_file_cache_min_uses;
+ of.test_only = 1;
+ of.errors = clcf->open_file_cache_errors;
+ of.events = clcf->open_file_cache_events;
+
+ if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_OK;
+ }
+
+ if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+ != NGX_OK)
+ {
+ if (of.err != NGX_ENOENT
+ && of.err != NGX_ENOTDIR
+ && of.err != NGX_ENAMETOOLONG)
+ {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
+ "%s \"%s\" failed", of.failed, path.data);
+ }
+
+ continue;
+ }
+
+ if (of.is_dir && !test_dir) {
+ continue;
+ }
+
+ path.len -= root;
+ path.data += root;
+
+ if (!alias) {
+ r->uri = path;
+
+ } else if (alias == NGX_MAX_SIZE_T_VALUE) {
+ if (!test_dir) {
+ r->uri = path;
+ r->add_uri_to_alias = 1;
+ }
+
+ } else {
+ r->uri.len = alias + path.len;
+ r->uri.data = ngx_pnalloc(r->pool, r->uri.len);
+ if (r->uri.data == NULL) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_OK;
+ }
+
+ p = ngx_copy(r->uri.data, clcf->name.data, alias);
+ ngx_memcpy(p, name, path.len);
+ }
+
+ ngx_http_set_exten(r);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "try file uri: \"%V\"", &r->uri);
+
+ r->phase_handler++;
+ return NGX_AGAIN;
+ }
+
+ /* not reached */
+}
+
+
+ngx_int_t
+ngx_http_core_content_phase(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph)
+{
+ size_t root;
+ ngx_int_t rc;
+ ngx_str_t path;
+
+ if (r->content_handler) {
+ r->write_event_handler = ngx_http_request_empty_handler;
+ ngx_http_finalize_request(r, r->content_handler(r));
+ return NGX_OK;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "content phase: %ui", r->phase_handler);
+
+ rc = ph->handler(r);
+
+ if (rc != NGX_DECLINED) {
+ ngx_http_finalize_request(r, rc);
+ return NGX_OK;
+ }
+
+ /* rc == NGX_DECLINED */
+
+ ph++;
+
+ if (ph->checker) {
+ r->phase_handler++;
+ return NGX_AGAIN;
+ }
+
+ /* no content handler was found */
+
+ if (r->uri.data[r->uri.len - 1] == '/') {
+
+ if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "directory index of \"%s\" is forbidden", path.data);
+ }
+
+ ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found");
+
+ ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
+ return NGX_OK;
+}
+
+
+void
+ngx_http_update_location_config(ngx_http_request_t *r)
+{
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (r->method & clcf->limit_except) {
+ r->loc_conf = clcf->limit_except_loc_conf;
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+ }
+
+ if (r == r->main) {
+ ngx_http_set_connection_log(r->connection, clcf->error_log);
+ }
+
+ if ((ngx_io.flags & NGX_IO_SENDFILE) && clcf->sendfile) {
+ r->connection->sendfile = 1;
+
+ } else {
+ r->connection->sendfile = 0;
+ }
+
+ if (clcf->client_body_in_file_only) {
+ r->request_body_in_file_only = 1;
+ r->request_body_in_persistent_file = 1;
+ r->request_body_in_clean_file =
+ clcf->client_body_in_file_only == NGX_HTTP_REQUEST_BODY_FILE_CLEAN;
+ r->request_body_file_log_level = NGX_LOG_NOTICE;
+
+ } else {
+ r->request_body_file_log_level = NGX_LOG_WARN;
+ }
+
+ r->request_body_in_single_buf = clcf->client_body_in_single_buffer;
+
+ if (r->keepalive) {
+ if (clcf->keepalive_timeout == 0) {
+ r->keepalive = 0;
+
+ } else if (r->connection->requests >= clcf->keepalive_requests) {
+ r->keepalive = 0;
+
+ } else if (r->headers_in.msie6
+ && r->method == NGX_HTTP_POST
+ && (clcf->keepalive_disable
+ & NGX_HTTP_KEEPALIVE_DISABLE_MSIE6))
+ {
+ /*
+ * MSIE may wait for some time if an response for
+ * a POST request was sent over a keepalive connection
+ */
+ r->keepalive = 0;
+
+ } else if (r->headers_in.safari
+ && (clcf->keepalive_disable
+ & NGX_HTTP_KEEPALIVE_DISABLE_SAFARI))
+ {
+ /*
+ * Safari may send a POST request to a closed keepalive
+ * connection and may stall for some time, see
+ * https://bugs.webkit.org/show_bug.cgi?id=5760
+ */
+ r->keepalive = 0;
+ }
+ }
+
+ if (!clcf->tcp_nopush) {
+ /* disable TCP_NOPUSH/TCP_CORK use */
+ r->connection->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;
+ }
+
+ if (r->limit_rate == 0) {
+ r->limit_rate = clcf->limit_rate;
+ }
+
+ if (clcf->handler) {
+ r->content_handler = clcf->handler;
+ }
+}
+
+
+/*
+ * NGX_OK - exact or regex match
+ * NGX_DONE - auto redirect
+ * NGX_AGAIN - inclusive match
+ * NGX_ERROR - regex error
+ * NGX_DECLINED - no match
+ */
+
+static ngx_int_t
+ngx_http_core_find_location(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_http_core_loc_conf_t *pclcf;
+#if (NGX_PCRE)
+ ngx_int_t n;
+ ngx_uint_t noregex;
+ ngx_http_core_loc_conf_t *clcf, **clcfp;
+
+ noregex = 0;
+#endif
+
+ pclcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ rc = ngx_http_core_find_static_location(r, pclcf->static_locations);
+
+ if (rc == NGX_AGAIN) {
+
+#if (NGX_PCRE)
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ noregex = clcf->noregex;
+#endif
+
+ /* look up nested locations */
+
+ rc = ngx_http_core_find_location(r);
+ }
+
+ if (rc == NGX_OK || rc == NGX_DONE) {
+ return rc;
+ }
+
+ /* rc == NGX_DECLINED or rc == NGX_AGAIN in nested location */
+
+#if (NGX_PCRE)
+
+ if (noregex == 0 && pclcf->regex_locations) {
+
+ for (clcfp = pclcf->regex_locations; *clcfp; clcfp++) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "test location: ~ \"%V\"", &(*clcfp)->name);
+
+ n = ngx_http_regex_exec(r, (*clcfp)->regex, &r->uri);
+
+ if (n == NGX_OK) {
+ r->loc_conf = (*clcfp)->loc_conf;
+
+ /* look up nested locations */
+
+ rc = ngx_http_core_find_location(r);
+
+ return (rc == NGX_ERROR) ? rc : NGX_OK;
+ }
+
+ if (n == NGX_DECLINED) {
+ continue;
+ }
+
+ return NGX_ERROR;
+ }
+ }
+#endif
+
+ return rc;
+}
+
+
+/*
+ * NGX_OK - exact match
+ * NGX_DONE - auto redirect
+ * NGX_AGAIN - inclusive match
+ * NGX_DECLINED - no match
+ */
+
+static ngx_int_t
+ngx_http_core_find_static_location(ngx_http_request_t *r,
+ ngx_http_location_tree_node_t *node)
+{
+ u_char *uri;
+ size_t len, n;
+ ngx_int_t rc, rv;
+
+ len = r->uri.len;
+ uri = r->uri.data;
+
+ rv = NGX_DECLINED;
+
+ for ( ;; ) {
+
+ if (node == NULL) {
+ return rv;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "test location: \"%*s\"", node->len, node->name);
+
+ n = (len <= (size_t) node->len) ? len : node->len;
+
+ rc = ngx_filename_cmp(uri, node->name, n);
+
+ if (rc != 0) {
+ node = (rc < 0) ? node->left : node->right;
+
+ continue;
+ }
+
+ if (len > (size_t) node->len) {
+
+ if (node->inclusive) {
+
+ r->loc_conf = node->inclusive->loc_conf;
+ rv = NGX_AGAIN;
+
+ node = node->tree;
+ uri += n;
+ len -= n;
+
+ continue;
+ }
+
+ /* exact only */
+
+ node = node->right;
+
+ continue;
+ }
+
+ if (len == (size_t) node->len) {
+
+ if (node->exact) {
+ r->loc_conf = node->exact->loc_conf;
+ return NGX_OK;
+
+ } else {
+ r->loc_conf = node->inclusive->loc_conf;
+ return NGX_AGAIN;
+ }
+ }
+
+ /* len < node->len */
+
+ if (len + 1 == (size_t) node->len && node->auto_redirect) {
+
+ r->loc_conf = (node->exact) ? node->exact->loc_conf:
+ node->inclusive->loc_conf;
+ rv = NGX_DONE;
+ }
+
+ node = node->left;
+ }
+}
+
+
+void *
+ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash)
+{
+ u_char c, *lowcase;
+ size_t len;
+ ngx_uint_t i, hash;
+
+ if (types_hash->size == 0) {
+ return (void *) 4;
+ }
+
+ if (r->headers_out.content_type.len == 0) {
+ return NULL;
+ }
+
+ len = r->headers_out.content_type_len;
+
+ if (r->headers_out.content_type_lowcase == NULL) {
+
+ lowcase = ngx_pnalloc(r->pool, len);
+ if (lowcase == NULL) {
+ return NULL;
+ }
+
+ r->headers_out.content_type_lowcase = lowcase;
+
+ hash = 0;
+
+ for (i = 0; i < len; i++) {
+ c = ngx_tolower(r->headers_out.content_type.data[i]);
+ hash = ngx_hash(hash, c);
+ lowcase[i] = c;
+ }
+
+ r->headers_out.content_type_hash = hash;
+ }
+
+ return ngx_hash_find(types_hash, r->headers_out.content_type_hash,
+ r->headers_out.content_type_lowcase, len);
+}
+
+
+ngx_int_t
+ngx_http_set_content_type(ngx_http_request_t *r)
+{
+ u_char c, *exten;
+ ngx_str_t *type;
+ ngx_uint_t i, hash;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (r->headers_out.content_type.len) {
+ return NGX_OK;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (r->exten.len) {
+
+ hash = 0;
+
+ for (i = 0; i < r->exten.len; i++) {
+ c = r->exten.data[i];
+
+ if (c >= 'A' && c <= 'Z') {
+
+ exten = ngx_pnalloc(r->pool, r->exten.len);
+ if (exten == NULL) {
+ return NGX_ERROR;
+ }
+
+ hash = ngx_hash_strlow(exten, r->exten.data, r->exten.len);
+
+ r->exten.data = exten;
+
+ break;
+ }
+
+ hash = ngx_hash(hash, c);
+ }
+
+ type = ngx_hash_find(&clcf->types_hash, hash,
+ r->exten.data, r->exten.len);
+
+ if (type) {
+ r->headers_out.content_type_len = type->len;
+ r->headers_out.content_type = *type;
+
+ return NGX_OK;
+ }
+ }
+
+ r->headers_out.content_type_len = clcf->default_type.len;
+ r->headers_out.content_type = clcf->default_type;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_http_set_exten(ngx_http_request_t *r)
+{
+ ngx_int_t i;
+
+ ngx_str_null(&r->exten);
+
+ for (i = r->uri.len - 1; i > 1; i--) {
+ if (r->uri.data[i] == '.' && r->uri.data[i - 1] != '/') {
+
+ r->exten.len = r->uri.len - i - 1;
+ r->exten.data = &r->uri.data[i + 1];
+
+ return;
+
+ } else if (r->uri.data[i] == '/') {
+ return;
+ }
+ }
+
+ return;
+}
+
+
+ngx_int_t
+ngx_http_set_etag(ngx_http_request_t *r)
+{
+ ngx_table_elt_t *etag;
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (!clcf->etag) {
+ return NGX_OK;
+ }
+
+ etag = ngx_list_push(&r->headers_out.headers);
+ if (etag == NULL) {
+ return NGX_ERROR;
+ }
+
+ etag->hash = 1;
+ ngx_str_set(&etag->key, "ETag");
+
+ etag->value.data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN + NGX_TIME_T_LEN + 3);
+ if (etag->value.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ etag->value.len = ngx_sprintf(etag->value.data, "\"%xT-%xO\"",
+ r->headers_out.last_modified_time,
+ r->headers_out.content_length_n)
+ - etag->value.data;
+
+ r->headers_out.etag = etag;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_http_weak_etag(ngx_http_request_t *r)
+{
+ size_t len;
+ u_char *p;
+ ngx_table_elt_t *etag;
+
+ etag = r->headers_out.etag;
+
+ if (etag == NULL) {
+ return;
+ }
+
+ if (etag->value.len > 2
+ && etag->value.data[0] == 'W'
+ && etag->value.data[1] == '/')
+ {
+ return;
+ }
+
+ if (etag->value.len < 1 || etag->value.data[0] != '"') {
+ r->headers_out.etag->hash = 0;
+ r->headers_out.etag = NULL;
+ return;
+ }
+
+ p = ngx_pnalloc(r->pool, etag->value.len + 2);
+ if (p == NULL) {
+ r->headers_out.etag->hash = 0;
+ r->headers_out.etag = NULL;
+ return;
+ }
+
+ len = ngx_sprintf(p, "W/%V", &etag->value) - p;
+
+ etag->value.data = p;
+ etag->value.len = len;
+}
+
+
+ngx_int_t
+ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status,
+ ngx_str_t *ct, ngx_http_complex_value_t *cv)
+{
+ ngx_int_t rc;
+ ngx_str_t val;
+ ngx_buf_t *b;
+ ngx_chain_t out;
+
+ if (ngx_http_discard_request_body(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ r->headers_out.status = status;
+
+ if (ngx_http_complex_value(r, cv, &val) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (status == NGX_HTTP_MOVED_PERMANENTLY
+ || status == NGX_HTTP_MOVED_TEMPORARILY
+ || status == NGX_HTTP_SEE_OTHER
+ || status == NGX_HTTP_TEMPORARY_REDIRECT)
+ {
+ ngx_http_clear_location(r);
+
+ r->headers_out.location = ngx_list_push(&r->headers_out.headers);
+ if (r->headers_out.location == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ r->headers_out.location->hash = 1;
+ ngx_str_set(&r->headers_out.location->key, "Location");
+ r->headers_out.location->value = val;
+
+ return status;
+ }
+
+ r->headers_out.content_length_n = val.len;
+
+ if (ct) {
+ r->headers_out.content_type_len = ct->len;
+ r->headers_out.content_type = *ct;
+
+ } else {
+ if (ngx_http_set_content_type(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ if (r->method == NGX_HTTP_HEAD || (r != r->main && val.len == 0)) {
+ return ngx_http_send_header(r);
+ }
+
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b->pos = val.data;
+ b->last = val.data + val.len;
+ b->memory = val.len ? 1 : 0;
+ b->last_buf = (r == r->main) ? 1 : 0;
+ b->last_in_chain = 1;
+
+ out.buf = b;
+ out.next = NULL;
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ return rc;
+ }
+
+ return ngx_http_output_filter(r, &out);
+}
+
+
+ngx_int_t
+ngx_http_send_header(ngx_http_request_t *r)
+{
+ if (r->header_sent) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "header already sent");
+ return NGX_ERROR;
+ }
+
+ if (r->err_status) {
+ r->headers_out.status = r->err_status;
+ r->headers_out.status_line.len = 0;
+ }
+
+ return ngx_http_top_header_filter(r);
+}
+
+
+ngx_int_t
+ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+
+ c = r->connection;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http output filter \"%V?%V\"", &r->uri, &r->args);
+
+ rc = ngx_http_top_body_filter(r, in);
+
+ if (rc == NGX_ERROR) {
+ /* NGX_ERROR may be returned by any filter */
+ c->error = 1;
+ }
+
+ return rc;
+}
+
+
+u_char *
+ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *path,
+ size_t *root_length, size_t reserved)
+{
+ u_char *last;
+ size_t alias;
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ alias = clcf->alias;
+
+ if (alias && !r->valid_location) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "\"alias\" cannot be used in location \"%V\" "
+ "where URI was rewritten", &clcf->name);
+ return NULL;
+ }
+
+ if (clcf->root_lengths == NULL) {
+
+ *root_length = clcf->root.len;
+
+ path->len = clcf->root.len + reserved + r->uri.len - alias + 1;
+
+ path->data = ngx_pnalloc(r->pool, path->len);
+ if (path->data == NULL) {
+ return NULL;
+ }
+
+ last = ngx_copy(path->data, clcf->root.data, clcf->root.len);
+
+ } else {
+
+ if (alias == NGX_MAX_SIZE_T_VALUE) {
+ reserved += r->add_uri_to_alias ? r->uri.len + 1 : 1;
+
+ } else {
+ reserved += r->uri.len - alias + 1;
+ }
+
+ if (ngx_http_script_run(r, path, clcf->root_lengths->elts, reserved,
+ clcf->root_values->elts)
+ == NULL)
+ {
+ return NULL;
+ }
+
+ if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, path)
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ *root_length = path->len - reserved;
+ last = path->data + *root_length;
+
+ if (alias == NGX_MAX_SIZE_T_VALUE) {
+ if (!r->add_uri_to_alias) {
+ *last = '\0';
+ return last;
+ }
+
+ alias = 0;
+ }
+ }
+
+ last = ngx_cpystrn(last, r->uri.data + alias, r->uri.len - alias + 1);
+
+ return last;
+}
+
+
+ngx_int_t
+ngx_http_auth_basic_user(ngx_http_request_t *r)
+{
+ ngx_str_t auth, encoded;
+ ngx_uint_t len;
+
+ if (r->headers_in.user.len == 0 && r->headers_in.user.data != NULL) {
+ return NGX_DECLINED;
+ }
+
+ if (r->headers_in.authorization == NULL) {
+ r->headers_in.user.data = (u_char *) "";
+ return NGX_DECLINED;
+ }
+
+ encoded = r->headers_in.authorization->value;
+
+ if (encoded.len < sizeof("Basic ") - 1
+ || ngx_strncasecmp(encoded.data, (u_char *) "Basic ",
+ sizeof("Basic ") - 1)
+ != 0)
+ {
+ r->headers_in.user.data = (u_char *) "";
+ return NGX_DECLINED;
+ }
+
+ encoded.len -= sizeof("Basic ") - 1;
+ encoded.data += sizeof("Basic ") - 1;
+
+ while (encoded.len && encoded.data[0] == ' ') {
+ encoded.len--;
+ encoded.data++;
+ }
+
+ if (encoded.len == 0) {
+ r->headers_in.user.data = (u_char *) "";
+ return NGX_DECLINED;
+ }
+
+ auth.len = ngx_base64_decoded_length(encoded.len);
+ auth.data = ngx_pnalloc(r->pool, auth.len + 1);
+ if (auth.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_decode_base64(&auth, &encoded) != NGX_OK) {
+ r->headers_in.user.data = (u_char *) "";
+ return NGX_DECLINED;
+ }
+
+ auth.data[auth.len] = '\0';
+
+ for (len = 0; len < auth.len; len++) {
+ if (auth.data[len] == ':') {
+ break;
+ }
+ }
+
+ if (len == 0 || len == auth.len) {
+ r->headers_in.user.data = (u_char *) "";
+ return NGX_DECLINED;
+ }
+
+ r->headers_in.user.len = len;
+ r->headers_in.user.data = auth.data;
+ r->headers_in.passwd.len = auth.len - len - 1;
+ r->headers_in.passwd.data = &auth.data[len + 1];
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HTTP_GZIP)
+
+ngx_int_t
+ngx_http_gzip_ok(ngx_http_request_t *r)
+{
+ time_t date, expires;
+ ngx_uint_t p;
+ ngx_array_t *cc;
+ ngx_table_elt_t *e, *d, *ae;
+ ngx_http_core_loc_conf_t *clcf;
+
+ r->gzip_tested = 1;
+
+ if (r != r->main) {
+ return NGX_DECLINED;
+ }
+
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ r->gzip_ok = 1;
+ return NGX_OK;
+ }
+#endif
+
+ ae = r->headers_in.accept_encoding;
+ if (ae == NULL) {
+ return NGX_DECLINED;
+ }
+
+ if (ae->value.len < sizeof("gzip") - 1) {
+ return NGX_DECLINED;
+ }
+
+ /*
+ * test first for the most common case "gzip,...":
+ * MSIE: "gzip, deflate"
+ * Firefox: "gzip,deflate"
+ * Chrome: "gzip,deflate,sdch"
+ * Safari: "gzip, deflate"
+ * Opera: "gzip, deflate"
+ */
+
+ if (ngx_memcmp(ae->value.data, "gzip,", 5) != 0
+ && ngx_http_gzip_accept_encoding(&ae->value) != NGX_OK)
+ {
+ return NGX_DECLINED;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (r->headers_in.msie6 && clcf->gzip_disable_msie6) {
+ return NGX_DECLINED;
+ }
+
+ if (r->http_version < clcf->gzip_http_version) {
+ return NGX_DECLINED;
+ }
+
+ if (r->headers_in.via == NULL) {
+ goto ok;
+ }
+
+ p = clcf->gzip_proxied;
+
+ if (p & NGX_HTTP_GZIP_PROXIED_OFF) {
+ return NGX_DECLINED;
+ }
+
+ if (p & NGX_HTTP_GZIP_PROXIED_ANY) {
+ goto ok;
+ }
+
+ if (r->headers_in.authorization && (p & NGX_HTTP_GZIP_PROXIED_AUTH)) {
+ goto ok;
+ }
+
+ e = r->headers_out.expires;
+
+ if (e) {
+
+ if (!(p & NGX_HTTP_GZIP_PROXIED_EXPIRED)) {
+ return NGX_DECLINED;
+ }
+
+ expires = ngx_http_parse_time(e->value.data, e->value.len);
+ if (expires == NGX_ERROR) {
+ return NGX_DECLINED;
+ }
+
+ d = r->headers_out.date;
+
+ if (d) {
+ date = ngx_http_parse_time(d->value.data, d->value.len);
+ if (date == NGX_ERROR) {
+ return NGX_DECLINED;
+ }
+
+ } else {
+ date = ngx_time();
+ }
+
+ if (expires < date) {
+ goto ok;
+ }
+
+ return NGX_DECLINED;
+ }
+
+ cc = &r->headers_out.cache_control;
+
+ if (cc->elts) {
+
+ if ((p & NGX_HTTP_GZIP_PROXIED_NO_CACHE)
+ && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_no_cache,
+ NULL)
+ >= 0)
+ {
+ goto ok;
+ }
+
+ if ((p & NGX_HTTP_GZIP_PROXIED_NO_STORE)
+ && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_no_store,
+ NULL)
+ >= 0)
+ {
+ goto ok;
+ }
+
+ if ((p & NGX_HTTP_GZIP_PROXIED_PRIVATE)
+ && ngx_http_parse_multi_header_lines(cc, &ngx_http_gzip_private,
+ NULL)
+ >= 0)
+ {
+ goto ok;
+ }
+
+ return NGX_DECLINED;
+ }
+
+ if ((p & NGX_HTTP_GZIP_PROXIED_NO_LM) && r->headers_out.last_modified) {
+ return NGX_DECLINED;
+ }
+
+ if ((p & NGX_HTTP_GZIP_PROXIED_NO_ETAG) && r->headers_out.etag) {
+ return NGX_DECLINED;
+ }
+
+ok:
+
+#if (NGX_PCRE)
+
+ if (clcf->gzip_disable && r->headers_in.user_agent) {
+
+ if (ngx_regex_exec_array(clcf->gzip_disable,
+ &r->headers_in.user_agent->value,
+ r->connection->log)
+ != NGX_DECLINED)
+ {
+ return NGX_DECLINED;
+ }
+ }
+
+#endif
+
+ r->gzip_ok = 1;
+
+ return NGX_OK;
+}
+
+
+/*
+ * gzip is enabled for the following quantities:
+ * "gzip; q=0.001" ... "gzip; q=1.000"
+ * gzip is disabled for the following quantities:
+ * "gzip; q=0" ... "gzip; q=0.000", and for any invalid cases
+ */
+
+static ngx_int_t
+ngx_http_gzip_accept_encoding(ngx_str_t *ae)
+{
+ u_char *p, *start, *last;
+
+ start = ae->data;
+ last = start + ae->len;
+
+ for ( ;; ) {
+ p = ngx_strcasestrn(start, "gzip", 4 - 1);
+ if (p == NULL) {
+ return NGX_DECLINED;
+ }
+
+ if (p == start || (*(p - 1) == ',' || *(p - 1) == ' ')) {
+ break;
+ }
+
+ start = p + 4;
+ }
+
+ p += 4;
+
+ while (p < last) {
+ switch (*p++) {
+ case ',':
+ return NGX_OK;
+ case ';':
+ goto quantity;
+ case ' ':
+ continue;
+ default:
+ return NGX_DECLINED;
+ }
+ }
+
+ return NGX_OK;
+
+quantity:
+
+ while (p < last) {
+ switch (*p++) {
+ case 'q':
+ case 'Q':
+ goto equal;
+ case ' ':
+ continue;
+ default:
+ return NGX_DECLINED;
+ }
+ }
+
+ return NGX_OK;
+
+equal:
+
+ if (p + 2 > last || *p++ != '=') {
+ return NGX_DECLINED;
+ }
+
+ if (ngx_http_gzip_quantity(p, last) == 0) {
+ return NGX_DECLINED;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_uint_t
+ngx_http_gzip_quantity(u_char *p, u_char *last)
+{
+ u_char c;
+ ngx_uint_t n, q;
+
+ c = *p++;
+
+ if (c != '0' && c != '1') {
+ return 0;
+ }
+
+ q = (c - '0') * 100;
+
+ if (p == last) {
+ return q;
+ }
+
+ c = *p++;
+
+ if (c == ',' || c == ' ') {
+ return q;
+ }
+
+ if (c != '.') {
+ return 0;
+ }
+
+ n = 0;
+
+ while (p < last) {
+ c = *p++;
+
+ if (c == ',' || c == ' ') {
+ break;
+ }
+
+ if (c >= '0' && c <= '9') {
+ q += c - '0';
+ n++;
+ continue;
+ }
+
+ return 0;
+ }
+
+ if (q > 100 || n > 3) {
+ return 0;
+ }
+
+ return q;
+}
+
+#endif
+
+
+ngx_int_t
+ngx_http_subrequest(ngx_http_request_t *r,
+ ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,
+ ngx_http_post_subrequest_t *ps, ngx_uint_t flags)
+{
+ ngx_time_t *tp;
+ ngx_connection_t *c;
+ ngx_http_request_t *sr;
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_postponed_request_t *pr, *p;
+
+ r->main->subrequests--;
+
+ if (r->main->subrequests == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "subrequests cycle while processing \"%V\"", uri);
+ r->main->subrequests = 1;
+ return NGX_ERROR;
+ }
+
+ sr = ngx_pcalloc(r->pool, sizeof(ngx_http_request_t));
+ if (sr == NULL) {
+ return NGX_ERROR;
+ }
+
+ sr->signature = NGX_HTTP_MODULE;
+
+ c = r->connection;
+ sr->connection = c;
+
+ sr->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
+ if (sr->ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_list_init(&sr->headers_out.headers, r->pool, 20,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+ sr->main_conf = cscf->ctx->main_conf;
+ sr->srv_conf = cscf->ctx->srv_conf;
+ sr->loc_conf = cscf->ctx->loc_conf;
+
+ sr->pool = r->pool;
+
+ sr->headers_in = r->headers_in;
+
+ ngx_http_clear_content_length(sr);
+ ngx_http_clear_accept_ranges(sr);
+ ngx_http_clear_last_modified(sr);
+
+ sr->request_body = r->request_body;
+
+#if (NGX_HTTP_SPDY)
+ sr->spdy_stream = r->spdy_stream;
+#endif
+
+ sr->method = NGX_HTTP_GET;
+ sr->http_version = r->http_version;
+
+ sr->request_line = r->request_line;
+ sr->uri = *uri;
+
+ if (args) {
+ sr->args = *args;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http subrequest \"%V?%V\"", uri, &sr->args);
+
+ sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0;
+ sr->waited = (flags & NGX_HTTP_SUBREQUEST_WAITED) != 0;
+
+ sr->unparsed_uri = r->unparsed_uri;
+ sr->method_name = ngx_http_core_get_method;
+ sr->http_protocol = r->http_protocol;
+
+ ngx_http_set_exten(sr);
+
+ sr->main = r->main;
+ sr->parent = r;
+ sr->post_subrequest = ps;
+ sr->read_event_handler = ngx_http_request_empty_handler;
+ sr->write_event_handler = ngx_http_handler;
+
+ if (c->data == r && r->postponed == NULL) {
+ c->data = sr;
+ }
+
+ sr->variables = r->variables;
+
+ sr->log_handler = r->log_handler;
+
+ pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
+ if (pr == NULL) {
+ return NGX_ERROR;
+ }
+
+ pr->request = sr;
+ pr->out = NULL;
+ pr->next = NULL;
+
+ if (r->postponed) {
+ for (p = r->postponed; p->next; p = p->next) { /* void */ }
+ p->next = pr;
+
+ } else {
+ r->postponed = pr;
+ }
+
+ sr->internal = 1;
+
+ sr->discard_body = r->discard_body;
+ sr->expect_tested = 1;
+ sr->main_filter_need_in_memory = r->main_filter_need_in_memory;
+
+ sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;
+
+ tp = ngx_timeofday();
+ sr->start_sec = tp->sec;
+ sr->start_msec = tp->msec;
+
+ r->main->count++;
+
+ *psr = sr;
+
+ return ngx_http_post_request(sr, NULL);
+}
+
+
+ngx_int_t
+ngx_http_internal_redirect(ngx_http_request_t *r,
+ ngx_str_t *uri, ngx_str_t *args)
+{
+ ngx_http_core_srv_conf_t *cscf;
+
+ r->uri_changes--;
+
+ if (r->uri_changes == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "rewrite or internal redirection cycle "
+ "while internally redirecting to \"%V\"", uri);
+
+ r->main->count++;
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_DONE;
+ }
+
+ r->uri = *uri;
+
+ if (args) {
+ r->args = *args;
+
+ } else {
+ ngx_str_null(&r->args);
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "internal redirect: \"%V?%V\"", uri, &r->args);
+
+ ngx_http_set_exten(r);
+
+ /* clear the modules contexts */
+ ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+ r->loc_conf = cscf->ctx->loc_conf;
+
+ ngx_http_update_location_config(r);
+
+#if (NGX_HTTP_CACHE)
+ r->cache = NULL;
+#endif
+
+ r->internal = 1;
+ r->valid_unparsed_uri = 0;
+ r->add_uri_to_alias = 0;
+ r->main->count++;
+
+ ngx_http_handler(r);
+
+ return NGX_DONE;
+}
+
+
+ngx_int_t
+ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name)
+{
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_core_loc_conf_t **clcfp;
+ ngx_http_core_main_conf_t *cmcf;
+
+ r->main->count++;
+ r->uri_changes--;
+
+ if (r->uri_changes == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "rewrite or internal redirection cycle "
+ "while redirect to named location \"%V\"", name);
+
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_DONE;
+ }
+
+ if (r->uri.len == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "empty URI in redirect to named location \"%V\"", name);
+
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_DONE;
+ }
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ if (cscf->named_locations) {
+
+ for (clcfp = cscf->named_locations; *clcfp; clcfp++) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "test location: \"%V\"", &(*clcfp)->name);
+
+ if (name->len != (*clcfp)->name.len
+ || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0)
+ {
+ continue;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "using location: %V \"%V?%V\"",
+ name, &r->uri, &r->args);
+
+ r->internal = 1;
+ r->content_handler = NULL;
+ r->uri_changed = 0;
+ r->loc_conf = (*clcfp)->loc_conf;
+
+ /* clear the modules contexts */
+ ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
+
+ ngx_http_update_location_config(r);
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ r->phase_handler = cmcf->phase_engine.location_rewrite_index;
+
+ r->write_event_handler = ngx_http_core_run_phases;
+ ngx_http_core_run_phases(r);
+
+ return NGX_DONE;
+ }
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "could not find named location \"%V\"", name);
+
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+ return NGX_DONE;
+}
+
+
+ngx_http_cleanup_t *
+ngx_http_cleanup_add(ngx_http_request_t *r, size_t size)
+{
+ ngx_http_cleanup_t *cln;
+
+ r = r->main;
+
+ cln = ngx_palloc(r->pool, sizeof(ngx_http_cleanup_t));
+ if (cln == NULL) {
+ return NULL;
+ }
+
+ if (size) {
+ cln->data = ngx_palloc(r->pool, size);
+ if (cln->data == NULL) {
+ return NULL;
+ }
+
+ } else {
+ cln->data = NULL;
+ }
+
+ cln->handler = NULL;
+ cln->next = r->cleanup;
+
+ r->cleanup = cln;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http cleanup add: %p", cln);
+
+ return cln;
+}
+
+
+ngx_int_t
+ngx_http_set_disable_symlinks(ngx_http_request_t *r,
+ ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of)
+{
+#if (NGX_HAVE_OPENAT)
+ u_char *p;
+ ngx_str_t from;
+
+ of->disable_symlinks = clcf->disable_symlinks;
+
+ if (clcf->disable_symlinks_from == NULL) {
+ return NGX_OK;
+ }
+
+ if (ngx_http_complex_value(r, clcf->disable_symlinks_from, &from)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (from.len == 0
+ || from.len > path->len
+ || ngx_memcmp(path->data, from.data, from.len) != 0)
+ {
+ return NGX_OK;
+ }
+
+ if (from.len == path->len) {
+ of->disable_symlinks = NGX_DISABLE_SYMLINKS_OFF;
+ return NGX_OK;
+ }
+
+ p = path->data + from.len;
+
+ if (*p == '/') {
+ of->disable_symlinks_from = from.len;
+ return NGX_OK;
+ }
+
+ p--;
+
+ if (*p == '/') {
+ of->disable_symlinks_from = from.len - 1;
+ }
+#endif
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr,
+ ngx_array_t *headers, ngx_str_t *value, ngx_array_t *proxies,
+ int recursive)
+{
+ ngx_int_t rc;
+ ngx_uint_t i, found;
+ ngx_table_elt_t **h;
+
+ if (headers == NULL) {
+ return ngx_http_get_forwarded_addr_internal(r, addr, value->data,
+ value->len, proxies,
+ recursive);
+ }
+
+ i = headers->nelts;
+ h = headers->elts;
+
+ rc = NGX_DECLINED;
+
+ found = 0;
+
+ while (i-- > 0) {
+ rc = ngx_http_get_forwarded_addr_internal(r, addr, h[i]->value.data,
+ h[i]->value.len, proxies,
+ recursive);
+
+ if (!recursive) {
+ break;
+ }
+
+ if (rc == NGX_DECLINED && found) {
+ rc = NGX_DONE;
+ break;
+ }
+
+ if (rc != NGX_OK) {
+ break;
+ }
+
+ found = 1;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_get_forwarded_addr_internal(ngx_http_request_t *r, ngx_addr_t *addr,
+ u_char *xff, size_t xfflen, ngx_array_t *proxies, int recursive)
+{
+ u_char *p;
+ in_addr_t inaddr;
+ ngx_int_t rc;
+ ngx_addr_t paddr;
+ ngx_cidr_t *cidr;
+ ngx_uint_t family, i;
+#if (NGX_HAVE_INET6)
+ ngx_uint_t n;
+ struct in6_addr *inaddr6;
+#endif
+
+#if (NGX_SUPPRESS_WARN)
+ inaddr = 0;
+#if (NGX_HAVE_INET6)
+ inaddr6 = NULL;
+#endif
+#endif
+
+ family = addr->sockaddr->sa_family;
+
+ if (family == AF_INET) {
+ inaddr = ((struct sockaddr_in *) addr->sockaddr)->sin_addr.s_addr;
+ }
+
+#if (NGX_HAVE_INET6)
+ else if (family == AF_INET6) {
+ inaddr6 = &((struct sockaddr_in6 *) addr->sockaddr)->sin6_addr;
+
+ if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {
+ family = AF_INET;
+
+ p = inaddr6->s6_addr;
+
+ inaddr = p[12] << 24;
+ inaddr += p[13] << 16;
+ inaddr += p[14] << 8;
+ inaddr += p[15];
+
+ inaddr = htonl(inaddr);
+ }
+ }
+#endif
+
+ for (cidr = proxies->elts, i = 0; i < proxies->nelts; i++) {
+ if (cidr[i].family != family) {
+ goto next;
+ }
+
+ switch (family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ for (n = 0; n < 16; n++) {
+ if ((inaddr6->s6_addr[n] & cidr[i].u.in6.mask.s6_addr[n])
+ != cidr[i].u.in6.addr.s6_addr[n])
+ {
+ goto next;
+ }
+ }
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ break;
+#endif
+
+ default: /* AF_INET */
+ if ((inaddr & cidr[i].u.in.mask) != cidr[i].u.in.addr) {
+ goto next;
+ }
+ break;
+ }
+
+ for (p = xff + xfflen - 1; p > xff; p--, xfflen--) {
+ if (*p != ' ' && *p != ',') {
+ break;
+ }
+ }
+
+ for ( /* void */ ; p > xff; p--) {
+ if (*p == ' ' || *p == ',') {
+ p++;
+ break;
+ }
+ }
+
+ if (ngx_parse_addr(r->pool, &paddr, p, xfflen - (p - xff)) != NGX_OK) {
+ return NGX_DECLINED;
+ }
+
+ *addr = paddr;
+
+ if (recursive && p > xff) {
+ rc = ngx_http_get_forwarded_addr_internal(r, addr, xff, p - 1 - xff,
+ proxies, 1);
+
+ if (rc == NGX_DECLINED) {
+ return NGX_DONE;
+ }
+
+ /* rc == NGX_OK || rc == NGX_DONE */
+ return rc;
+ }
+
+ return NGX_OK;
+
+ next:
+ continue;
+ }
+
+ return NGX_DECLINED;
+}
+
+
+static char *
+ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
+{
+ char *rv;
+ void *mconf;
+ ngx_uint_t i;
+ ngx_conf_t pcf;
+ ngx_http_module_t *module;
+ struct sockaddr_in *sin;
+ ngx_http_conf_ctx_t *ctx, *http_ctx;
+ ngx_http_listen_opt_t lsopt;
+ ngx_http_core_srv_conf_t *cscf, **cscfp;
+ ngx_http_core_main_conf_t *cmcf;
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ http_ctx = cf->ctx;
+ ctx->main_conf = http_ctx->main_conf;
+
+ /* the server{}'s srv_conf */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ /* the server{}'s loc_conf */
+
+ ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
+ if (ctx->loc_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[i]->ctx;
+
+ if (module->create_srv_conf) {
+ mconf = module->create_srv_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->srv_conf[ngx_modules[i]->ctx_index] = mconf;
+ }
+
+ if (module->create_loc_conf) {
+ mconf = module->create_loc_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
+ }
+ }
+
+
+ /* the server configuration context */
+
+ cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];
+ cscf->ctx = ctx;
+
+
+ cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
+
+ cscfp = ngx_array_push(&cmcf->servers);
+ if (cscfp == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *cscfp = cscf;
+
+
+ /* parse inside server{} */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+ cf->cmd_type = NGX_HTTP_SRV_CONF;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = pcf;
+
+ if (rv == NGX_CONF_OK && !cscf->listen) {
+ ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));
+
+ sin = &lsopt.u.sockaddr_in;
+
+ sin->sin_family = AF_INET;
+#if (NGX_WIN32)
+ sin->sin_port = htons(80);
+#else
+ sin->sin_port = htons((getuid() == 0) ? 80 : 8000);
+#endif
+ sin->sin_addr.s_addr = INADDR_ANY;
+
+ lsopt.socklen = sizeof(struct sockaddr_in);
+
+ lsopt.backlog = NGX_LISTEN_BACKLOG;
+ lsopt.rcvbuf = -1;
+ lsopt.sndbuf = -1;
+#if (NGX_HAVE_SETFIB)
+ lsopt.setfib = -1;
+#endif
+#if (NGX_HAVE_TCP_FASTOPEN)
+ lsopt.fastopen = -1;
+#endif
+ lsopt.wildcard = 1;
+
+ (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.socklen, lsopt.addr,
+ NGX_SOCKADDR_STRLEN, 1);
+
+ if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return rv;
+}
+
+
+static char *
+ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
+{
+ char *rv;
+ u_char *mod;
+ size_t len;
+ ngx_str_t *value, *name;
+ ngx_uint_t i;
+ ngx_conf_t save;
+ ngx_http_module_t *module;
+ ngx_http_conf_ctx_t *ctx, *pctx;
+ ngx_http_core_loc_conf_t *clcf, *pclcf;
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ pctx = cf->ctx;
+ ctx->main_conf = pctx->main_conf;
+ ctx->srv_conf = pctx->srv_conf;
+
+ ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
+ if (ctx->loc_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[i]->ctx;
+
+ if (module->create_loc_conf) {
+ ctx->loc_conf[ngx_modules[i]->ctx_index] =
+ module->create_loc_conf(cf);
+ if (ctx->loc_conf[ngx_modules[i]->ctx_index] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+ }
+
+ clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
+ clcf->loc_conf = ctx->loc_conf;
+
+ value = cf->args->elts;
+
+ if (cf->args->nelts == 3) {
+
+ len = value[1].len;
+ mod = value[1].data;
+ name = &value[2];
+
+ if (len == 1 && mod[0] == '=') {
+
+ clcf->name = *name;
+ clcf->exact_match = 1;
+
+ } else if (len == 2 && mod[0] == '^' && mod[1] == '~') {
+
+ clcf->name = *name;
+ clcf->noregex = 1;
+
+ } else if (len == 1 && mod[0] == '~') {
+
+ if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ } else if (len == 2 && mod[0] == '~' && mod[1] == '*') {
+
+ if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid location modifier \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+
+ name = &value[1];
+
+ if (name->data[0] == '=') {
+
+ clcf->name.len = name->len - 1;
+ clcf->name.data = name->data + 1;
+ clcf->exact_match = 1;
+
+ } else if (name->data[0] == '^' && name->data[1] == '~') {
+
+ clcf->name.len = name->len - 2;
+ clcf->name.data = name->data + 2;
+ clcf->noregex = 1;
+
+ } else if (name->data[0] == '~') {
+
+ name->len--;
+ name->data++;
+
+ if (name->data[0] == '*') {
+
+ name->len--;
+ name->data++;
+
+ if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+ if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ } else {
+
+ clcf->name = *name;
+
+ if (name->data[0] == '@') {
+ clcf->named = 1;
+ }
+ }
+ }
+
+ pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
+
+ if (pclcf->name.len) {
+
+ /* nested location */
+
+#if 0
+ clcf->prev_location = pclcf;
+#endif
+
+ if (pclcf->exact_match) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "location \"%V\" cannot be inside "
+ "the exact location \"%V\"",
+ &clcf->name, &pclcf->name);
+ return NGX_CONF_ERROR;
+ }
+
+ if (pclcf->named) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "location \"%V\" cannot be inside "
+ "the named location \"%V\"",
+ &clcf->name, &pclcf->name);
+ return NGX_CONF_ERROR;
+ }
+
+ if (clcf->named) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "named location \"%V\" can be "
+ "on the server level only",
+ &clcf->name);
+ return NGX_CONF_ERROR;
+ }
+
+ len = pclcf->name.len;
+
+#if (NGX_PCRE)
+ if (clcf->regex == NULL
+ && ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)
+#else
+ if (ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)
+#endif
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "location \"%V\" is outside location \"%V\"",
+ &clcf->name, &pclcf->name);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ save = *cf;
+ cf->ctx = ctx;
+ cf->cmd_type = NGX_HTTP_LOC_CONF;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = save;
+
+ return rv;
+}
+
+
+static ngx_int_t
+ngx_http_core_regex_location(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf,
+ ngx_str_t *regex, ngx_uint_t caseless)
+{
+#if (NGX_PCRE)
+ ngx_regex_compile_t rc;
+ u_char errstr[NGX_MAX_CONF_ERRSTR];
+
+ ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+ rc.pattern = *regex;
+ rc.err.len = NGX_MAX_CONF_ERRSTR;
+ rc.err.data = errstr;
+
+#if (NGX_HAVE_CASELESS_FILESYSTEM)
+ rc.options = NGX_REGEX_CASELESS;
+#else
+ rc.options = caseless ? NGX_REGEX_CASELESS : 0;
+#endif
+
+ clcf->regex = ngx_http_regex_compile(cf, &rc);
+ if (clcf->regex == NULL) {
+ return NGX_ERROR;
+ }
+
+ clcf->name = *regex;
+
+ return NGX_OK;
+
+#else
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "using regex \"%V\" requires PCRE library",
+ regex);
+ return NGX_ERROR;
+
+#endif
+}
+
+
+static char *
+ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf = conf;
+
+ char *rv;
+ ngx_conf_t save;
+
+ if (clcf->types == NULL) {
+ clcf->types = ngx_array_create(cf->pool, 64, sizeof(ngx_hash_key_t));
+ if (clcf->types == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ save = *cf;
+ cf->handler = ngx_http_core_type;
+ cf->handler_conf = conf;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = save;
+
+ return rv;
+}
+
+
+static char *
+ngx_http_core_type(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf = conf;
+
+ ngx_str_t *value, *content_type, *old;
+ ngx_uint_t i, n, hash;
+ ngx_hash_key_t *type;
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[0].data, "include") == 0) {
+ if (cf->args->nelts != 2) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid number of arguments"
+ " in \"include\" directive");
+ return NGX_CONF_ERROR;
+ }
+
+ return ngx_conf_include(cf, dummy, conf);
+ }
+
+ content_type = ngx_palloc(cf->pool, sizeof(ngx_str_t));
+ if (content_type == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *content_type = value[0];
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len);
+
+ type = clcf->types->elts;
+ for (n = 0; n < clcf->types->nelts; n++) {
+ if (ngx_strcmp(value[i].data, type[n].key.data) == 0) {
+ old = type[n].value;
+ type[n].value = content_type;
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "duplicate extension \"%V\", "
+ "content type: \"%V\", "
+ "previous content type: \"%V\"",
+ &value[i], content_type, old);
+ goto next;
+ }
+ }
+
+
+ type = ngx_array_push(clcf->types);
+ if (type == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ type->key = value[i];
+ type->key_hash = hash;
+ type->value = content_type;
+
+ next:
+ continue;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_core_preconfiguration(ngx_conf_t *cf)
+{
+ return ngx_http_variables_add_core_vars(cf);
+}
+
+
+static void *
+ngx_http_core_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_main_conf_t));
+ if (cmcf == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(&cmcf->servers, cf->pool, 4,
+ sizeof(ngx_http_core_srv_conf_t *))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ cmcf->server_names_hash_max_size = NGX_CONF_UNSET_UINT;
+ cmcf->server_names_hash_bucket_size = NGX_CONF_UNSET_UINT;
+
+ cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT;
+ cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT;
+
+ return cmcf;
+}
+
+
+static char *
+ngx_http_core_init_main_conf(ngx_conf_t *cf, void *conf)
+{
+ ngx_http_core_main_conf_t *cmcf = conf;
+
+ ngx_conf_init_uint_value(cmcf->server_names_hash_max_size, 512);
+ ngx_conf_init_uint_value(cmcf->server_names_hash_bucket_size,
+ ngx_cacheline_size);
+
+ cmcf->server_names_hash_bucket_size =
+ ngx_align(cmcf->server_names_hash_bucket_size, ngx_cacheline_size);
+
+
+ ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024);
+ ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64);
+
+ cmcf->variables_hash_bucket_size =
+ ngx_align(cmcf->variables_hash_bucket_size, ngx_cacheline_size);
+
+ if (cmcf->ncaptures) {
+ cmcf->ncaptures = (cmcf->ncaptures + 1) * 3;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_core_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_http_core_srv_conf_t *cscf;
+
+ cscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_srv_conf_t));
+ if (cscf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->client_large_buffers.num = 0;
+ */
+
+ if (ngx_array_init(&cscf->server_names, cf->temp_pool, 4,
+ sizeof(ngx_http_server_name_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ cscf->connection_pool_size = NGX_CONF_UNSET_SIZE;
+ cscf->request_pool_size = NGX_CONF_UNSET_SIZE;
+ cscf->client_header_timeout = NGX_CONF_UNSET_MSEC;
+ cscf->client_header_buffer_size = NGX_CONF_UNSET_SIZE;
+ cscf->ignore_invalid_headers = NGX_CONF_UNSET;
+ cscf->merge_slashes = NGX_CONF_UNSET;
+ cscf->underscores_in_headers = NGX_CONF_UNSET;
+
+ return cscf;
+}
+
+
+static char *
+ngx_http_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_core_srv_conf_t *prev = parent;
+ ngx_http_core_srv_conf_t *conf = child;
+
+ ngx_str_t name;
+ ngx_http_server_name_t *sn;
+
+ /* TODO: it does not merge, it inits only */
+
+ ngx_conf_merge_size_value(conf->connection_pool_size,
+ prev->connection_pool_size, 256);
+ ngx_conf_merge_size_value(conf->request_pool_size,
+ prev->request_pool_size, 4096);
+ ngx_conf_merge_msec_value(conf->client_header_timeout,
+ prev->client_header_timeout, 60000);
+ ngx_conf_merge_size_value(conf->client_header_buffer_size,
+ prev->client_header_buffer_size, 1024);
+ ngx_conf_merge_bufs_value(conf->large_client_header_buffers,
+ prev->large_client_header_buffers,
+ 4, 8192);
+
+ if (conf->large_client_header_buffers.size < conf->connection_pool_size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"large_client_header_buffers\" size must be "
+ "equal to or greater than \"connection_pool_size\"");
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_value(conf->ignore_invalid_headers,
+ prev->ignore_invalid_headers, 1);
+
+ ngx_conf_merge_value(conf->merge_slashes, prev->merge_slashes, 1);
+
+ ngx_conf_merge_value(conf->underscores_in_headers,
+ prev->underscores_in_headers, 0);
+
+ if (conf->server_names.nelts == 0) {
+ /* the array has 4 empty preallocated elements, so push cannot fail */
+ sn = ngx_array_push(&conf->server_names);
+#if (NGX_PCRE)
+ sn->regex = NULL;
+#endif
+ sn->server = conf;
+ ngx_str_set(&sn->name, "");
+ }
+
+ sn = conf->server_names.elts;
+ name = sn[0].name;
+
+#if (NGX_PCRE)
+ if (sn->regex) {
+ name.len++;
+ name.data--;
+ } else
+#endif
+
+ if (name.data[0] == '.') {
+ name.len--;
+ name.data++;
+ }
+
+ conf->server_name.len = name.len;
+ conf->server_name.data = ngx_pstrdup(cf->pool, &name);
+ if (conf->server_name.data == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_core_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_loc_conf_t));
+ if (clcf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * clcf->root = { 0, NULL };
+ * clcf->limit_except = 0;
+ * clcf->post_action = { 0, NULL };
+ * clcf->types = NULL;
+ * clcf->default_type = { 0, NULL };
+ * clcf->error_log = NULL;
+ * clcf->error_pages = NULL;
+ * clcf->try_files = NULL;
+ * clcf->client_body_path = NULL;
+ * clcf->regex = NULL;
+ * clcf->exact_match = 0;
+ * clcf->auto_redirect = 0;
+ * clcf->alias = 0;
+ * clcf->gzip_proxied = 0;
+ * clcf->keepalive_disable = 0;
+ */
+
+ clcf->client_max_body_size = NGX_CONF_UNSET;
+ clcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE;
+ clcf->client_body_timeout = NGX_CONF_UNSET_MSEC;
+ clcf->satisfy = NGX_CONF_UNSET_UINT;
+ clcf->if_modified_since = NGX_CONF_UNSET_UINT;
+ clcf->max_ranges = NGX_CONF_UNSET_UINT;
+ clcf->client_body_in_file_only = NGX_CONF_UNSET_UINT;
+ clcf->client_body_in_single_buffer = NGX_CONF_UNSET;
+ clcf->internal = NGX_CONF_UNSET;
+ clcf->sendfile = NGX_CONF_UNSET;
+ clcf->sendfile_max_chunk = NGX_CONF_UNSET_SIZE;
+#if (NGX_HAVE_FILE_AIO)
+ clcf->aio = NGX_CONF_UNSET;
+#endif
+ clcf->read_ahead = NGX_CONF_UNSET_SIZE;
+ clcf->directio = NGX_CONF_UNSET;
+ clcf->directio_alignment = NGX_CONF_UNSET;
+ clcf->tcp_nopush = NGX_CONF_UNSET;
+ clcf->tcp_nodelay = NGX_CONF_UNSET;
+ clcf->send_timeout = NGX_CONF_UNSET_MSEC;
+ clcf->send_lowat = NGX_CONF_UNSET_SIZE;
+ clcf->postpone_output = NGX_CONF_UNSET_SIZE;
+ clcf->limit_rate = NGX_CONF_UNSET_SIZE;
+ clcf->limit_rate_after = NGX_CONF_UNSET_SIZE;
+ clcf->keepalive_timeout = NGX_CONF_UNSET_MSEC;
+ clcf->keepalive_header = NGX_CONF_UNSET;
+ clcf->keepalive_requests = NGX_CONF_UNSET_UINT;
+ clcf->lingering_close = NGX_CONF_UNSET_UINT;
+ clcf->lingering_time = NGX_CONF_UNSET_MSEC;
+ clcf->lingering_timeout = NGX_CONF_UNSET_MSEC;
+ clcf->resolver_timeout = NGX_CONF_UNSET_MSEC;
+ clcf->reset_timedout_connection = NGX_CONF_UNSET;
+ clcf->server_name_in_redirect = NGX_CONF_UNSET;
+ clcf->port_in_redirect = NGX_CONF_UNSET;
+ clcf->msie_padding = NGX_CONF_UNSET;
+ clcf->msie_refresh = NGX_CONF_UNSET;
+ clcf->log_not_found = NGX_CONF_UNSET;
+ clcf->log_subrequest = NGX_CONF_UNSET;
+ clcf->recursive_error_pages = NGX_CONF_UNSET;
+ clcf->server_tokens = NGX_CONF_UNSET;
+ clcf->chunked_transfer_encoding = NGX_CONF_UNSET;
+ clcf->etag = NGX_CONF_UNSET;
+ clcf->types_hash_max_size = NGX_CONF_UNSET_UINT;
+ clcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT;
+
+ clcf->open_file_cache = NGX_CONF_UNSET_PTR;
+ clcf->open_file_cache_valid = NGX_CONF_UNSET;
+ clcf->open_file_cache_min_uses = NGX_CONF_UNSET_UINT;
+ clcf->open_file_cache_errors = NGX_CONF_UNSET;
+ clcf->open_file_cache_events = NGX_CONF_UNSET;
+
+#if (NGX_HTTP_GZIP)
+ clcf->gzip_vary = NGX_CONF_UNSET;
+ clcf->gzip_http_version = NGX_CONF_UNSET_UINT;
+#if (NGX_PCRE)
+ clcf->gzip_disable = NGX_CONF_UNSET_PTR;
+#endif
+ clcf->gzip_disable_msie6 = 3;
+#if (NGX_HTTP_DEGRADATION)
+ clcf->gzip_disable_degradation = 3;
+#endif
+#endif
+
+#if (NGX_HAVE_OPENAT)
+ clcf->disable_symlinks = NGX_CONF_UNSET_UINT;
+ clcf->disable_symlinks_from = NGX_CONF_UNSET_PTR;
+#endif
+
+ return clcf;
+}
+
+
+static ngx_str_t ngx_http_core_text_html_type = ngx_string("text/html");
+static ngx_str_t ngx_http_core_image_gif_type = ngx_string("image/gif");
+static ngx_str_t ngx_http_core_image_jpeg_type = ngx_string("image/jpeg");
+
+static ngx_hash_key_t ngx_http_core_default_types[] = {
+ { ngx_string("html"), 0, &ngx_http_core_text_html_type },
+ { ngx_string("gif"), 0, &ngx_http_core_image_gif_type },
+ { ngx_string("jpg"), 0, &ngx_http_core_image_jpeg_type },
+ { ngx_null_string, 0, NULL }
+};
+
+
+static char *
+ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_core_loc_conf_t *prev = parent;
+ ngx_http_core_loc_conf_t *conf = child;
+
+ ngx_uint_t i;
+ ngx_hash_key_t *type;
+ ngx_hash_init_t types_hash;
+
+ if (conf->root.data == NULL) {
+
+ conf->alias = prev->alias;
+ conf->root = prev->root;
+ conf->root_lengths = prev->root_lengths;
+ conf->root_values = prev->root_values;
+
+ if (prev->root.data == NULL) {
+ ngx_str_set(&conf->root, "html");
+
+ if (ngx_conf_full_name(cf->cycle, &conf->root, 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+ }
+
+ if (conf->post_action.data == NULL) {
+ conf->post_action = prev->post_action;
+ }
+
+ ngx_conf_merge_uint_value(conf->types_hash_max_size,
+ prev->types_hash_max_size, 1024);
+
+ ngx_conf_merge_uint_value(conf->types_hash_bucket_size,
+ prev->types_hash_bucket_size, 64);
+
+ conf->types_hash_bucket_size = ngx_align(conf->types_hash_bucket_size,
+ ngx_cacheline_size);
+
+ /*
+ * the special handling of the "types" directive in the "http" section
+ * to inherit the http's conf->types_hash to all servers
+ */
+
+ if (prev->types && prev->types_hash.buckets == NULL) {
+
+ types_hash.hash = &prev->types_hash;
+ types_hash.key = ngx_hash_key_lc;
+ types_hash.max_size = conf->types_hash_max_size;
+ types_hash.bucket_size = conf->types_hash_bucket_size;
+ types_hash.name = "types_hash";
+ types_hash.pool = cf->pool;
+ types_hash.temp_pool = NULL;
+
+ if (ngx_hash_init(&types_hash, prev->types->elts, prev->types->nelts)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (conf->types == NULL) {
+ conf->types = prev->types;
+ conf->types_hash = prev->types_hash;
+ }
+
+ if (conf->types == NULL) {
+ conf->types = ngx_array_create(cf->pool, 3, sizeof(ngx_hash_key_t));
+ if (conf->types == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (i = 0; ngx_http_core_default_types[i].key.len; i++) {
+ type = ngx_array_push(conf->types);
+ if (type == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ type->key = ngx_http_core_default_types[i].key;
+ type->key_hash =
+ ngx_hash_key_lc(ngx_http_core_default_types[i].key.data,
+ ngx_http_core_default_types[i].key.len);
+ type->value = ngx_http_core_default_types[i].value;
+ }
+ }
+
+ if (conf->types_hash.buckets == NULL) {
+
+ types_hash.hash = &conf->types_hash;
+ types_hash.key = ngx_hash_key_lc;
+ types_hash.max_size = conf->types_hash_max_size;
+ types_hash.bucket_size = conf->types_hash_bucket_size;
+ types_hash.name = "types_hash";
+ types_hash.pool = cf->pool;
+ types_hash.temp_pool = NULL;
+
+ if (ngx_hash_init(&types_hash, conf->types->elts, conf->types->nelts)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (conf->error_log == NULL) {
+ if (prev->error_log) {
+ conf->error_log = prev->error_log;
+ } else {
+ conf->error_log = &cf->cycle->new_log;
+ }
+ }
+
+ if (conf->error_pages == NULL && prev->error_pages) {
+ conf->error_pages = prev->error_pages;
+ }
+
+ ngx_conf_merge_str_value(conf->default_type,
+ prev->default_type, "text/plain");
+
+ ngx_conf_merge_off_value(conf->client_max_body_size,
+ prev->client_max_body_size, 1 * 1024 * 1024);
+ ngx_conf_merge_size_value(conf->client_body_buffer_size,
+ prev->client_body_buffer_size,
+ (size_t) 2 * ngx_pagesize);
+ ngx_conf_merge_msec_value(conf->client_body_timeout,
+ prev->client_body_timeout, 60000);
+
+ ngx_conf_merge_bitmask_value(conf->keepalive_disable,
+ prev->keepalive_disable,
+ (NGX_CONF_BITMASK_SET
+ |NGX_HTTP_KEEPALIVE_DISABLE_MSIE6));
+ ngx_conf_merge_uint_value(conf->satisfy, prev->satisfy,
+ NGX_HTTP_SATISFY_ALL);
+ ngx_conf_merge_uint_value(conf->if_modified_since, prev->if_modified_since,
+ NGX_HTTP_IMS_EXACT);
+ ngx_conf_merge_uint_value(conf->max_ranges, prev->max_ranges,
+ NGX_MAX_INT32_VALUE);
+ ngx_conf_merge_uint_value(conf->client_body_in_file_only,
+ prev->client_body_in_file_only,
+ NGX_HTTP_REQUEST_BODY_FILE_OFF);
+ ngx_conf_merge_value(conf->client_body_in_single_buffer,
+ prev->client_body_in_single_buffer, 0);
+ ngx_conf_merge_value(conf->internal, prev->internal, 0);
+ ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);
+ ngx_conf_merge_size_value(conf->sendfile_max_chunk,
+ prev->sendfile_max_chunk, 0);
+#if (NGX_HAVE_FILE_AIO)
+ ngx_conf_merge_value(conf->aio, prev->aio, NGX_HTTP_AIO_OFF);
+#endif
+ ngx_conf_merge_size_value(conf->read_ahead, prev->read_ahead, 0);
+ ngx_conf_merge_off_value(conf->directio, prev->directio,
+ NGX_OPEN_FILE_DIRECTIO_OFF);
+ ngx_conf_merge_off_value(conf->directio_alignment, prev->directio_alignment,
+ 512);
+ ngx_conf_merge_value(conf->tcp_nopush, prev->tcp_nopush, 0);
+ ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1);
+
+ ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 60000);
+ ngx_conf_merge_size_value(conf->send_lowat, prev->send_lowat, 0);
+ ngx_conf_merge_size_value(conf->postpone_output, prev->postpone_output,
+ 1460);
+ ngx_conf_merge_size_value(conf->limit_rate, prev->limit_rate, 0);
+ ngx_conf_merge_size_value(conf->limit_rate_after, prev->limit_rate_after,
+ 0);
+ ngx_conf_merge_msec_value(conf->keepalive_timeout,
+ prev->keepalive_timeout, 75000);
+ ngx_conf_merge_sec_value(conf->keepalive_header,
+ prev->keepalive_header, 0);
+ ngx_conf_merge_uint_value(conf->keepalive_requests,
+ prev->keepalive_requests, 100);
+ ngx_conf_merge_uint_value(conf->lingering_close,
+ prev->lingering_close, NGX_HTTP_LINGERING_ON);
+ ngx_conf_merge_msec_value(conf->lingering_time,
+ prev->lingering_time, 30000);
+ ngx_conf_merge_msec_value(conf->lingering_timeout,
+ prev->lingering_timeout, 5000);
+ ngx_conf_merge_msec_value(conf->resolver_timeout,
+ prev->resolver_timeout, 30000);
+
+ if (conf->resolver == NULL) {
+
+ if (prev->resolver == NULL) {
+
+ /*
+ * create dummy resolver in http {} context
+ * to inherit it in all servers
+ */
+
+ prev->resolver = ngx_resolver_create(cf, NULL, 0);
+ if (prev->resolver == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ conf->resolver = prev->resolver;
+ }
+
+ if (ngx_conf_merge_path_value(cf, &conf->client_body_temp_path,
+ prev->client_body_temp_path,
+ &ngx_http_client_temp_path)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_value(conf->reset_timedout_connection,
+ prev->reset_timedout_connection, 0);
+ ngx_conf_merge_value(conf->server_name_in_redirect,
+ prev->server_name_in_redirect, 0);
+ ngx_conf_merge_value(conf->port_in_redirect, prev->port_in_redirect, 1);
+ ngx_conf_merge_value(conf->msie_padding, prev->msie_padding, 1);
+ ngx_conf_merge_value(conf->msie_refresh, prev->msie_refresh, 0);
+ ngx_conf_merge_value(conf->log_not_found, prev->log_not_found, 1);
+ ngx_conf_merge_value(conf->log_subrequest, prev->log_subrequest, 0);
+ ngx_conf_merge_value(conf->recursive_error_pages,
+ prev->recursive_error_pages, 0);
+ ngx_conf_merge_value(conf->server_tokens, prev->server_tokens, 1);
+ ngx_conf_merge_value(conf->chunked_transfer_encoding,
+ prev->chunked_transfer_encoding, 1);
+ ngx_conf_merge_value(conf->etag, prev->etag, 1);
+
+ ngx_conf_merge_ptr_value(conf->open_file_cache,
+ prev->open_file_cache, NULL);
+
+ ngx_conf_merge_sec_value(conf->open_file_cache_valid,
+ prev->open_file_cache_valid, 60);
+
+ ngx_conf_merge_uint_value(conf->open_file_cache_min_uses,
+ prev->open_file_cache_min_uses, 1);
+
+ ngx_conf_merge_sec_value(conf->open_file_cache_errors,
+ prev->open_file_cache_errors, 0);
+
+ ngx_conf_merge_sec_value(conf->open_file_cache_events,
+ prev->open_file_cache_events, 0);
+#if (NGX_HTTP_GZIP)
+
+ ngx_conf_merge_value(conf->gzip_vary, prev->gzip_vary, 0);
+ ngx_conf_merge_uint_value(conf->gzip_http_version, prev->gzip_http_version,
+ NGX_HTTP_VERSION_11);
+ ngx_conf_merge_bitmask_value(conf->gzip_proxied, prev->gzip_proxied,
+ (NGX_CONF_BITMASK_SET|NGX_HTTP_GZIP_PROXIED_OFF));
+
+#if (NGX_PCRE)
+ ngx_conf_merge_ptr_value(conf->gzip_disable, prev->gzip_disable, NULL);
+#endif
+
+ if (conf->gzip_disable_msie6 == 3) {
+ conf->gzip_disable_msie6 =
+ (prev->gzip_disable_msie6 == 3) ? 0 : prev->gzip_disable_msie6;
+ }
+
+#if (NGX_HTTP_DEGRADATION)
+
+ if (conf->gzip_disable_degradation == 3) {
+ conf->gzip_disable_degradation =
+ (prev->gzip_disable_degradation == 3) ?
+ 0 : prev->gzip_disable_degradation;
+ }
+
+#endif
+#endif
+
+#if (NGX_HAVE_OPENAT)
+ ngx_conf_merge_uint_value(conf->disable_symlinks, prev->disable_symlinks,
+ NGX_DISABLE_SYMLINKS_OFF);
+ ngx_conf_merge_ptr_value(conf->disable_symlinks_from,
+ prev->disable_symlinks_from, NULL);
+#endif
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_srv_conf_t *cscf = conf;
+
+ ngx_str_t *value, size;
+ ngx_url_t u;
+ ngx_uint_t n;
+ ngx_http_listen_opt_t lsopt;
+
+ cscf->listen = 1;
+
+ value = cf->args->elts;
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = value[1];
+ u.listen = 1;
+ u.default_port = 80;
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in \"%V\" of the \"listen\" directive",
+ u.err, &u.url);
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));
+
+ ngx_memcpy(&lsopt.u.sockaddr, u.sockaddr, u.socklen);
+
+ lsopt.socklen = u.socklen;
+ lsopt.backlog = NGX_LISTEN_BACKLOG;
+ lsopt.rcvbuf = -1;
+ lsopt.sndbuf = -1;
+#if (NGX_HAVE_SETFIB)
+ lsopt.setfib = -1;
+#endif
+#if (NGX_HAVE_TCP_FASTOPEN)
+ lsopt.fastopen = -1;
+#endif
+ lsopt.wildcard = u.wildcard;
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ lsopt.ipv6only = 1;
+#endif
+
+ (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.socklen, lsopt.addr,
+ NGX_SOCKADDR_STRLEN, 1);
+
+ for (n = 2; n < cf->args->nelts; n++) {
+
+ if (ngx_strcmp(value[n].data, "default_server") == 0
+ || ngx_strcmp(value[n].data, "default") == 0)
+ {
+ lsopt.default_server = 1;
+ continue;
+ }
+
+ if (ngx_strcmp(value[n].data, "bind") == 0) {
+ lsopt.set = 1;
+ lsopt.bind = 1;
+ continue;
+ }
+
+#if (NGX_HAVE_SETFIB)
+ if (ngx_strncmp(value[n].data, "setfib=", 7) == 0) {
+ lsopt.setfib = ngx_atoi(value[n].data + 7, value[n].len - 7);
+ lsopt.set = 1;
+ lsopt.bind = 1;
+
+ if (lsopt.setfib == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid setfib \"%V\"", &value[n]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+#endif
+
+#if (NGX_HAVE_TCP_FASTOPEN)
+ if (ngx_strncmp(value[n].data, "fastopen=", 9) == 0) {
+ lsopt.fastopen = ngx_atoi(value[n].data + 9, value[n].len - 9);
+ lsopt.set = 1;
+ lsopt.bind = 1;
+
+ if (lsopt.fastopen == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid fastopen \"%V\"", &value[n]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+#endif
+
+ if (ngx_strncmp(value[n].data, "backlog=", 8) == 0) {
+ lsopt.backlog = ngx_atoi(value[n].data + 8, value[n].len - 8);
+ lsopt.set = 1;
+ lsopt.bind = 1;
+
+ if (lsopt.backlog == NGX_ERROR || lsopt.backlog == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid backlog \"%V\"", &value[n]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[n].data, "rcvbuf=", 7) == 0) {
+ size.len = value[n].len - 7;
+ size.data = value[n].data + 7;
+
+ lsopt.rcvbuf = ngx_parse_size(&size);
+ lsopt.set = 1;
+ lsopt.bind = 1;
+
+ if (lsopt.rcvbuf == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid rcvbuf \"%V\"", &value[n]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[n].data, "sndbuf=", 7) == 0) {
+ size.len = value[n].len - 7;
+ size.data = value[n].data + 7;
+
+ lsopt.sndbuf = ngx_parse_size(&size);
+ lsopt.set = 1;
+ lsopt.bind = 1;
+
+ if (lsopt.sndbuf == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid sndbuf \"%V\"", &value[n]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[n].data, "accept_filter=", 14) == 0) {
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
+ lsopt.accept_filter = (char *) &value[n].data[14];
+ lsopt.set = 1;
+ lsopt.bind = 1;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "accept filters \"%V\" are not supported "
+ "on this platform, ignored",
+ &value[n]);
+#endif
+ continue;
+ }
+
+ if (ngx_strcmp(value[n].data, "deferred") == 0) {
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
+ lsopt.deferred_accept = 1;
+ lsopt.set = 1;
+ lsopt.bind = 1;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the deferred accept is not supported "
+ "on this platform, ignored");
+#endif
+ continue;
+ }
+
+ if (ngx_strncmp(value[n].data, "ipv6only=o", 10) == 0) {
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ struct sockaddr *sa;
+
+ sa = &lsopt.u.sockaddr;
+
+ if (sa->sa_family == AF_INET6) {
+
+ if (ngx_strcmp(&value[n].data[10], "n") == 0) {
+ lsopt.ipv6only = 1;
+
+ } else if (ngx_strcmp(&value[n].data[10], "ff") == 0) {
+ lsopt.ipv6only = 0;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid ipv6only flags \"%s\"",
+ &value[n].data[9]);
+ return NGX_CONF_ERROR;
+ }
+
+ lsopt.set = 1;
+ lsopt.bind = 1;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "ipv6only is not supported "
+ "on addr \"%s\", ignored", lsopt.addr);
+ }
+
+ continue;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "ipv6only is not supported "
+ "on this platform");
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ if (ngx_strcmp(value[n].data, "ssl") == 0) {
+#if (NGX_HTTP_SSL)
+ lsopt.ssl = 1;
+ continue;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"ssl\" parameter requires "
+ "ngx_http_ssl_module");
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ if (ngx_strcmp(value[n].data, "spdy") == 0) {
+#if (NGX_HTTP_SPDY)
+ lsopt.spdy = 1;
+ continue;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"spdy\" parameter requires "
+ "ngx_http_spdy_module");
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ if (ngx_strncmp(value[n].data, "so_keepalive=", 13) == 0) {
+
+ if (ngx_strcmp(&value[n].data[13], "on") == 0) {
+ lsopt.so_keepalive = 1;
+
+ } else if (ngx_strcmp(&value[n].data[13], "off") == 0) {
+ lsopt.so_keepalive = 2;
+
+ } else {
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ u_char *p, *end;
+ ngx_str_t s;
+
+ end = value[n].data + value[n].len;
+ s.data = value[n].data + 13;
+
+ p = ngx_strlchr(s.data, end, ':');
+ if (p == NULL) {
+ p = end;
+ }
+
+ if (p > s.data) {
+ s.len = p - s.data;
+
+ lsopt.tcp_keepidle = ngx_parse_time(&s, 1);
+ if (lsopt.tcp_keepidle == (time_t) NGX_ERROR) {
+ goto invalid_so_keepalive;
+ }
+ }
+
+ s.data = (p < end) ? (p + 1) : end;
+
+ p = ngx_strlchr(s.data, end, ':');
+ if (p == NULL) {
+ p = end;
+ }
+
+ if (p > s.data) {
+ s.len = p - s.data;
+
+ lsopt.tcp_keepintvl = ngx_parse_time(&s, 1);
+ if (lsopt.tcp_keepintvl == (time_t) NGX_ERROR) {
+ goto invalid_so_keepalive;
+ }
+ }
+
+ s.data = (p < end) ? (p + 1) : end;
+
+ if (s.data < end) {
+ s.len = end - s.data;
+
+ lsopt.tcp_keepcnt = ngx_atoi(s.data, s.len);
+ if (lsopt.tcp_keepcnt == NGX_ERROR) {
+ goto invalid_so_keepalive;
+ }
+ }
+
+ if (lsopt.tcp_keepidle == 0 && lsopt.tcp_keepintvl == 0
+ && lsopt.tcp_keepcnt == 0)
+ {
+ goto invalid_so_keepalive;
+ }
+
+ lsopt.so_keepalive = 1;
+
+#else
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"so_keepalive\" parameter accepts "
+ "only \"on\" or \"off\" on this platform");
+ return NGX_CONF_ERROR;
+
+#endif
+ }
+
+ lsopt.set = 1;
+ lsopt.bind = 1;
+
+ continue;
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ invalid_so_keepalive:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid so_keepalive value: \"%s\"",
+ &value[n].data[13]);
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ if (ngx_strcmp(value[n].data, "proxy_protocol") == 0) {
+ lsopt.proxy_protocol = 1;
+ continue;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[n]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_http_add_listen(cf, cscf, &lsopt) == NGX_OK) {
+ return NGX_CONF_OK;
+ }
+
+ return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_http_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_srv_conf_t *cscf = conf;
+
+ u_char ch;
+ ngx_str_t *value;
+ ngx_uint_t i;
+ ngx_http_server_name_t *sn;
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ ch = value[i].data[0];
+
+ if ((ch == '*' && (value[i].len < 3 || value[i].data[1] != '.'))
+ || (ch == '.' && value[i].len < 2))
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "server name \"%V\" is invalid", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_strchr(value[i].data, '/')) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "server name \"%V\" has suspicious symbols",
+ &value[i]);
+ }
+
+ sn = ngx_array_push(&cscf->server_names);
+ if (sn == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+#if (NGX_PCRE)
+ sn->regex = NULL;
+#endif
+ sn->server = cscf;
+
+ if (ngx_strcasecmp(value[i].data, (u_char *) "$hostname") == 0) {
+ sn->name = cf->cycle->hostname;
+
+ } else {
+ sn->name = value[i];
+ }
+
+ if (value[i].data[0] != '~') {
+ ngx_strlow(sn->name.data, sn->name.data, sn->name.len);
+ continue;
+ }
+
+#if (NGX_PCRE)
+ {
+ u_char *p;
+ ngx_regex_compile_t rc;
+ u_char errstr[NGX_MAX_CONF_ERRSTR];
+
+ if (value[i].len == 1) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "empty regex in server name \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ value[i].len--;
+ value[i].data++;
+
+ ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+ rc.pattern = value[i];
+ rc.err.len = NGX_MAX_CONF_ERRSTR;
+ rc.err.data = errstr;
+
+ for (p = value[i].data; p < value[i].data + value[i].len; p++) {
+ if (*p >= 'A' && *p <= 'Z') {
+ rc.options = NGX_REGEX_CASELESS;
+ break;
+ }
+ }
+
+ sn->regex = ngx_http_regex_compile(cf, &rc);
+ if (sn->regex == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ sn->name = value[i];
+ cscf->captures = (rc.captures > 0);
+ }
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "using regex \"%V\" "
+ "requires PCRE library", &value[i]);
+
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf = conf;
+
+ ngx_str_t *value;
+ ngx_int_t alias;
+ ngx_uint_t n;
+ ngx_http_script_compile_t sc;
+
+ alias = (cmd->name.len == sizeof("alias") - 1) ? 1 : 0;
+
+ if (clcf->root.data) {
+
+ if ((clcf->alias != 0) == alias) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"%V\" directive is duplicate",
+ &cmd->name);
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"%V\" directive is duplicate, "
+ "\"%s\" directive was specified earlier",
+ &cmd->name, clcf->alias ? "alias" : "root");
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (clcf->named && alias) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"alias\" directive cannot be used "
+ "inside the named location");
+
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strstr(value[1].data, "$document_root")
+ || ngx_strstr(value[1].data, "${document_root}"))
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the $document_root variable cannot be used "
+ "in the \"%V\" directive",
+ &cmd->name);
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_strstr(value[1].data, "$realpath_root")
+ || ngx_strstr(value[1].data, "${realpath_root}"))
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the $realpath_root variable cannot be used "
+ "in the \"%V\" directive",
+ &cmd->name);
+
+ return NGX_CONF_ERROR;
+ }
+
+ clcf->alias = alias ? clcf->name.len : 0;
+ clcf->root = value[1];
+
+ if (!alias && clcf->root.data[clcf->root.len - 1] == '/') {
+ clcf->root.len--;
+ }
+
+ if (clcf->root.data[0] != '$') {
+ if (ngx_conf_full_name(cf->cycle, &clcf->root, 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ n = ngx_http_script_variables_count(&clcf->root);
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+ sc.variables = n;
+
+#if (NGX_PCRE)
+ if (alias && clcf->regex) {
+ clcf->alias = NGX_MAX_SIZE_T_VALUE;
+ n = 1;
+ }
+#endif
+
+ if (n) {
+ sc.cf = cf;
+ sc.source = &clcf->root;
+ sc.lengths = &clcf->root_lengths;
+ sc.values = &clcf->root_values;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_http_method_name_t ngx_methods_names[] = {
+ { (u_char *) "GET", (uint32_t) ~NGX_HTTP_GET },
+ { (u_char *) "HEAD", (uint32_t) ~NGX_HTTP_HEAD },
+ { (u_char *) "POST", (uint32_t) ~NGX_HTTP_POST },
+ { (u_char *) "PUT", (uint32_t) ~NGX_HTTP_PUT },
+ { (u_char *) "DELETE", (uint32_t) ~NGX_HTTP_DELETE },
+ { (u_char *) "MKCOL", (uint32_t) ~NGX_HTTP_MKCOL },
+ { (u_char *) "COPY", (uint32_t) ~NGX_HTTP_COPY },
+ { (u_char *) "MOVE", (uint32_t) ~NGX_HTTP_MOVE },
+ { (u_char *) "OPTIONS", (uint32_t) ~NGX_HTTP_OPTIONS },
+ { (u_char *) "PROPFIND", (uint32_t) ~NGX_HTTP_PROPFIND },
+ { (u_char *) "PROPPATCH", (uint32_t) ~NGX_HTTP_PROPPATCH },
+ { (u_char *) "LOCK", (uint32_t) ~NGX_HTTP_LOCK },
+ { (u_char *) "UNLOCK", (uint32_t) ~NGX_HTTP_UNLOCK },
+ { (u_char *) "PATCH", (uint32_t) ~NGX_HTTP_PATCH },
+ { NULL, 0 }
+};
+
+
+static char *
+ngx_http_core_limit_except(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *pclcf = conf;
+
+ char *rv;
+ void *mconf;
+ ngx_str_t *value;
+ ngx_uint_t i;
+ ngx_conf_t save;
+ ngx_http_module_t *module;
+ ngx_http_conf_ctx_t *ctx, *pctx;
+ ngx_http_method_name_t *name;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (pclcf->limit_except) {
+ return "duplicate";
+ }
+
+ pclcf->limit_except = 0xffffffff;
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+ for (name = ngx_methods_names; name->name; name++) {
+
+ if (ngx_strcasecmp(value[i].data, name->name) == 0) {
+ pclcf->limit_except &= name->method;
+ goto next;
+ }
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid method \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+
+ next:
+ continue;
+ }
+
+ if (!(pclcf->limit_except & NGX_HTTP_GET)) {
+ pclcf->limit_except &= (uint32_t) ~NGX_HTTP_HEAD;
+ }
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ pctx = cf->ctx;
+ ctx->main_conf = pctx->main_conf;
+ ctx->srv_conf = pctx->srv_conf;
+
+ ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
+ if (ctx->loc_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[i]->ctx;
+
+ if (module->create_loc_conf) {
+
+ mconf = module->create_loc_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
+ }
+ }
+
+
+ clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];
+ pclcf->limit_except_loc_conf = ctx->loc_conf;
+ clcf->loc_conf = ctx->loc_conf;
+ clcf->name = pclcf->name;
+ clcf->noname = 1;
+ clcf->lmt_excpt = 1;
+
+ if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ save = *cf;
+ cf->ctx = ctx;
+ cf->cmd_type = NGX_HTTP_LMT_CONF;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = save;
+
+ return rv;
+}
+
+
+static char *
+ngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf = conf;
+
+ ngx_str_t *value;
+
+ if (clcf->directio != NGX_CONF_UNSET) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ clcf->directio = NGX_OPEN_FILE_DIRECTIO_OFF;
+ return NGX_CONF_OK;
+ }
+
+ clcf->directio = ngx_parse_offset(&value[1]);
+ if (clcf->directio == (off_t) NGX_ERROR) {
+ return "invalid value";
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf = conf;
+
+ u_char *p;
+ ngx_int_t overwrite;
+ ngx_str_t *value, uri, args;
+ ngx_uint_t i, n;
+ ngx_http_err_page_t *err;
+ ngx_http_complex_value_t cv;
+ ngx_http_compile_complex_value_t ccv;
+
+ if (clcf->error_pages == NULL) {
+ clcf->error_pages = ngx_array_create(cf->pool, 4,
+ sizeof(ngx_http_err_page_t));
+ if (clcf->error_pages == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ value = cf->args->elts;
+
+ i = cf->args->nelts - 2;
+
+ if (value[i].data[0] == '=') {
+ if (i == 1) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (value[i].len > 1) {
+ overwrite = ngx_atoi(&value[i].data[1], value[i].len - 1);
+
+ if (overwrite == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+ overwrite = 0;
+ }
+
+ n = 2;
+
+ } else {
+ overwrite = -1;
+ n = 1;
+ }
+
+ uri = value[cf->args->nelts - 1];
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &uri;
+ ccv.complex_value = &cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_str_null(&args);
+
+ if (cv.lengths == NULL && uri.len && uri.data[0] == '/') {
+ p = (u_char *) ngx_strchr(uri.data, '?');
+
+ if (p) {
+ cv.value.len = p - uri.data;
+ cv.value.data = uri.data;
+ p++;
+ args.len = (uri.data + uri.len) - p;
+ args.data = p;
+ }
+ }
+
+ for (i = 1; i < cf->args->nelts - n; i++) {
+ err = ngx_array_push(clcf->error_pages);
+ if (err == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ err->status = ngx_atoi(value[i].data, value[i].len);
+
+ if (err->status == NGX_ERROR || err->status == 499) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (err->status < 300 || err->status > 599) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "value \"%V\" must be between 300 and 599",
+ &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ err->overwrite = overwrite;
+
+ if (overwrite == -1) {
+ switch (err->status) {
+ case NGX_HTTP_TO_HTTPS:
+ case NGX_HTTPS_CERT_ERROR:
+ case NGX_HTTPS_NO_CERT:
+ err->overwrite = NGX_HTTP_BAD_REQUEST;
+ default:
+ break;
+ }
+ }
+
+ err->value = cv;
+ err->args = args;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf = conf;
+
+ ngx_str_t *value;
+ ngx_int_t code;
+ ngx_uint_t i, n;
+ ngx_http_try_file_t *tf;
+ ngx_http_script_compile_t sc;
+ ngx_http_core_main_conf_t *cmcf;
+
+ if (clcf->try_files) {
+ return "is duplicate";
+ }
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ cmcf->try_files = 1;
+
+ tf = ngx_pcalloc(cf->pool, cf->args->nelts * sizeof(ngx_http_try_file_t));
+ if (tf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ clcf->try_files = tf;
+
+ value = cf->args->elts;
+
+ for (i = 0; i < cf->args->nelts - 1; i++) {
+
+ tf[i].name = value[i + 1];
+
+ if (tf[i].name.len > 0
+ && tf[i].name.data[tf[i].name.len - 1] == '/'
+ && i + 2 < cf->args->nelts)
+ {
+ tf[i].test_dir = 1;
+ tf[i].name.len--;
+ tf[i].name.data[tf[i].name.len] = '\0';
+ }
+
+ n = ngx_http_script_variables_count(&tf[i].name);
+
+ if (n) {
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = cf;
+ sc.source = &tf[i].name;
+ sc.lengths = &tf[i].lengths;
+ sc.values = &tf[i].values;
+ sc.variables = n;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+ /* add trailing '\0' to length */
+ tf[i].name.len++;
+ }
+ }
+
+ if (tf[i - 1].name.data[0] == '=') {
+
+ code = ngx_atoi(tf[i - 1].name.data + 1, tf[i - 1].name.len - 2);
+
+ if (code == NGX_ERROR || code > 999) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid code \"%*s\"",
+ tf[i - 1].name.len - 1, tf[i - 1].name.data);
+ return NGX_CONF_ERROR;
+ }
+
+ tf[i].code = code;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf = conf;
+
+ time_t inactive;
+ ngx_str_t *value, s;
+ ngx_int_t max;
+ ngx_uint_t i;
+
+ if (clcf->open_file_cache != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ max = 0;
+ inactive = 60;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
+
+ max = ngx_atoi(value[i].data + 4, value[i].len - 4);
+ if (max <= 0) {
+ goto failed;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
+
+ s.len = value[i].len - 9;
+ s.data = value[i].data + 9;
+
+ inactive = ngx_parse_time(&s, 1);
+ if (inactive == (time_t) NGX_ERROR) {
+ goto failed;
+ }
+
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "off") == 0) {
+
+ clcf->open_file_cache = NULL;
+
+ continue;
+ }
+
+ failed:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid \"open_file_cache\" parameter \"%V\"",
+ &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (clcf->open_file_cache == NULL) {
+ return NGX_CONF_OK;
+ }
+
+ if (max == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"open_file_cache\" must have the \"max\" parameter");
+ return NGX_CONF_ERROR;
+ }
+
+ clcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);
+ if (clcf->open_file_cache) {
+ return NGX_CONF_OK;
+ }
+
+ return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf = conf;
+
+ return ngx_log_set_log(cf, &clcf->error_log);
+}
+
+
+static char *
+ngx_http_core_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf = conf;
+
+ ngx_str_t *value;
+
+ if (clcf->keepalive_timeout != NGX_CONF_UNSET_MSEC) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ clcf->keepalive_timeout = ngx_parse_time(&value[1], 0);
+
+ if (clcf->keepalive_timeout == (ngx_msec_t) NGX_ERROR) {
+ return "invalid value";
+ }
+
+ if (cf->args->nelts == 2) {
+ return NGX_CONF_OK;
+ }
+
+ clcf->keepalive_header = ngx_parse_time(&value[2], 1);
+
+ if (clcf->keepalive_header == (time_t) NGX_ERROR) {
+ return "invalid value";
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_core_internal(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf = conf;
+
+ if (clcf->internal != NGX_CONF_UNSET) {
+ return "is duplicate";
+ }
+
+ clcf->internal = 1;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf = conf;
+
+ ngx_str_t *value;
+
+ if (clcf->resolver) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ clcf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1);
+ if (clcf->resolver == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+#if (NGX_HTTP_GZIP)
+
+static char *
+ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf = conf;
+
+#if (NGX_PCRE)
+
+ ngx_str_t *value;
+ ngx_uint_t i;
+ ngx_regex_elt_t *re;
+ ngx_regex_compile_t rc;
+ u_char errstr[NGX_MAX_CONF_ERRSTR];
+
+ if (clcf->gzip_disable == NGX_CONF_UNSET_PTR) {
+ clcf->gzip_disable = ngx_array_create(cf->pool, 2,
+ sizeof(ngx_regex_elt_t));
+ if (clcf->gzip_disable == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ value = cf->args->elts;
+
+ ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
+
+ rc.pool = cf->pool;
+ rc.err.len = NGX_MAX_CONF_ERRSTR;
+ rc.err.data = errstr;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "msie6") == 0) {
+ clcf->gzip_disable_msie6 = 1;
+ continue;
+ }
+
+#if (NGX_HTTP_DEGRADATION)
+
+ if (ngx_strcmp(value[i].data, "degradation") == 0) {
+ clcf->gzip_disable_degradation = 1;
+ continue;
+ }
+
+#endif
+
+ re = ngx_array_push(clcf->gzip_disable);
+ if (re == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ rc.pattern = value[i];
+ rc.options = NGX_REGEX_CASELESS;
+
+ if (ngx_regex_compile(&rc) != NGX_OK) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
+ return NGX_CONF_ERROR;
+ }
+
+ re->regex = rc.regex;
+ re->name = value[i].data;
+ }
+
+ return NGX_CONF_OK;
+
+#else
+ ngx_str_t *value;
+ ngx_uint_t i;
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+ if (ngx_strcmp(value[i].data, "msie6") == 0) {
+ clcf->gzip_disable_msie6 = 1;
+ continue;
+ }
+
+#if (NGX_HTTP_DEGRADATION)
+
+ if (ngx_strcmp(value[i].data, "degradation") == 0) {
+ clcf->gzip_disable_degradation = 1;
+ continue;
+ }
+
+#endif
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "without PCRE library \"gzip_disable\" supports "
+ "builtin \"msie6\" and \"degradation\" mask only");
+
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+
+#endif
+}
+
+#endif
+
+
+#if (NGX_HAVE_OPENAT)
+
+static char *
+ngx_http_disable_symlinks(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_core_loc_conf_t *clcf = conf;
+
+ ngx_str_t *value;
+ ngx_uint_t i;
+ ngx_http_compile_complex_value_t ccv;
+
+ if (clcf->disable_symlinks != NGX_CONF_UNSET_UINT) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "off") == 0) {
+ clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_OFF;
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "if_not_owner") == 0) {
+ clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_NOTOWNER;
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "on") == 0) {
+ clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_ON;
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "from=", 5) == 0) {
+ value[i].len -= 5;
+ value[i].data += 5;
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[i];
+ ccv.complex_value = ngx_palloc(cf->pool,
+ sizeof(ngx_http_complex_value_t));
+ if (ccv.complex_value == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ clcf->disable_symlinks_from = ccv.complex_value;
+
+ continue;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (clcf->disable_symlinks == NGX_CONF_UNSET_UINT) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"%V\" must have \"off\", \"on\" "
+ "or \"if_not_owner\" parameter",
+ &cmd->name);
+ return NGX_CONF_ERROR;
+ }
+
+ if (cf->args->nelts == 2) {
+ clcf->disable_symlinks_from = NULL;
+ return NGX_CONF_OK;
+ }
+
+ if (clcf->disable_symlinks_from == NGX_CONF_UNSET_PTR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate parameters \"%V %V\"",
+ &value[1], &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (clcf->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"from=\" cannot be used with \"off\" parameter");
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+#endif
+
+
+static char *
+ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data)
+{
+#if (NGX_FREEBSD)
+ ssize_t *np = data;
+
+ if ((u_long) *np >= ngx_freebsd_net_inet_tcp_sendspace) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"send_lowat\" must be less than %d "
+ "(sysctl net.inet.tcp.sendspace)",
+ ngx_freebsd_net_inet_tcp_sendspace);
+
+ return NGX_CONF_ERROR;
+ }
+
+#elif !(NGX_HAVE_SO_SNDLOWAT)
+ ssize_t *np = data;
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "\"send_lowat\" is not supported, ignored");
+
+ *np = 0;
+
+#endif
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data)
+{
+ size_t *sp = data;
+
+ if (*sp < NGX_MIN_POOL_SIZE) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the pool size must be no less than %uz",
+ NGX_MIN_POOL_SIZE);
+ return NGX_CONF_ERROR;
+ }
+
+ if (*sp % NGX_POOL_ALIGNMENT) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the pool size must be a multiple of %uz",
+ NGX_POOL_ALIGNMENT);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_core_module.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_core_module.h
new file mode 100644
index 00000000000..285120de7ba
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_core_module.h
@@ -0,0 +1,589 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_CORE_H_INCLUDED_
+#define _NGX_HTTP_CORE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_GZIP_PROXIED_OFF 0x0002
+#define NGX_HTTP_GZIP_PROXIED_EXPIRED 0x0004
+#define NGX_HTTP_GZIP_PROXIED_NO_CACHE 0x0008
+#define NGX_HTTP_GZIP_PROXIED_NO_STORE 0x0010
+#define NGX_HTTP_GZIP_PROXIED_PRIVATE 0x0020
+#define NGX_HTTP_GZIP_PROXIED_NO_LM 0x0040
+#define NGX_HTTP_GZIP_PROXIED_NO_ETAG 0x0080
+#define NGX_HTTP_GZIP_PROXIED_AUTH 0x0100
+#define NGX_HTTP_GZIP_PROXIED_ANY 0x0200
+
+
+#define NGX_HTTP_AIO_OFF 0
+#define NGX_HTTP_AIO_ON 1
+#define NGX_HTTP_AIO_SENDFILE 2
+
+
+#define NGX_HTTP_SATISFY_ALL 0
+#define NGX_HTTP_SATISFY_ANY 1
+
+
+#define NGX_HTTP_LINGERING_OFF 0
+#define NGX_HTTP_LINGERING_ON 1
+#define NGX_HTTP_LINGERING_ALWAYS 2
+
+
+#define NGX_HTTP_IMS_OFF 0
+#define NGX_HTTP_IMS_EXACT 1
+#define NGX_HTTP_IMS_BEFORE 2
+
+
+#define NGX_HTTP_KEEPALIVE_DISABLE_NONE 0x0002
+#define NGX_HTTP_KEEPALIVE_DISABLE_MSIE6 0x0004
+#define NGX_HTTP_KEEPALIVE_DISABLE_SAFARI 0x0008
+
+
+typedef struct ngx_http_location_tree_node_s ngx_http_location_tree_node_t;
+typedef struct ngx_http_core_loc_conf_s ngx_http_core_loc_conf_t;
+
+
+typedef struct {
+ union {
+ struct sockaddr sockaddr;
+ struct sockaddr_in sockaddr_in;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 sockaddr_in6;
+#endif
+#if (NGX_HAVE_UNIX_DOMAIN)
+ struct sockaddr_un sockaddr_un;
+#endif
+ u_char sockaddr_data[NGX_SOCKADDRLEN];
+ } u;
+
+ socklen_t socklen;
+
+ unsigned set:1;
+ unsigned default_server:1;
+ unsigned bind:1;
+ unsigned wildcard:1;
+#if (NGX_HTTP_SSL)
+ unsigned ssl:1;
+#endif
+#if (NGX_HTTP_SPDY)
+ unsigned spdy:1;
+#endif
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ unsigned ipv6only:1;
+#endif
+ unsigned so_keepalive:2;
+ unsigned proxy_protocol:1;
+
+ int backlog;
+ int rcvbuf;
+ int sndbuf;
+#if (NGX_HAVE_SETFIB)
+ int setfib;
+#endif
+#if (NGX_HAVE_TCP_FASTOPEN)
+ int fastopen;
+#endif
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ int tcp_keepidle;
+ int tcp_keepintvl;
+ int tcp_keepcnt;
+#endif
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
+ char *accept_filter;
+#endif
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
+ ngx_uint_t deferred_accept;
+#endif
+
+ u_char addr[NGX_SOCKADDR_STRLEN + 1];
+} ngx_http_listen_opt_t;
+
+
+typedef enum {
+ NGX_HTTP_POST_READ_PHASE = 0,
+
+ NGX_HTTP_SERVER_REWRITE_PHASE,
+
+ NGX_HTTP_FIND_CONFIG_PHASE,
+ NGX_HTTP_REWRITE_PHASE,
+ NGX_HTTP_POST_REWRITE_PHASE,
+
+ NGX_HTTP_PREACCESS_PHASE,
+
+ NGX_HTTP_ACCESS_PHASE,
+ NGX_HTTP_POST_ACCESS_PHASE,
+
+ NGX_HTTP_TRY_FILES_PHASE,
+ NGX_HTTP_CONTENT_PHASE,
+
+ NGX_HTTP_LOG_PHASE
+} ngx_http_phases;
+
+typedef struct ngx_http_phase_handler_s ngx_http_phase_handler_t;
+
+typedef ngx_int_t (*ngx_http_phase_handler_pt)(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph);
+
+struct ngx_http_phase_handler_s {
+ ngx_http_phase_handler_pt checker;
+ ngx_http_handler_pt handler;
+ ngx_uint_t next;
+};
+
+
+typedef struct {
+ ngx_http_phase_handler_t *handlers;
+ ngx_uint_t server_rewrite_index;
+ ngx_uint_t location_rewrite_index;
+} ngx_http_phase_engine_t;
+
+
+typedef struct {
+ ngx_array_t handlers;
+} ngx_http_phase_t;
+
+
+typedef struct {
+ ngx_array_t servers; /* ngx_http_core_srv_conf_t */
+
+ ngx_http_phase_engine_t phase_engine;
+
+ ngx_hash_t headers_in_hash;
+
+ ngx_hash_t variables_hash;
+
+ ngx_array_t variables; /* ngx_http_variable_t */
+ ngx_uint_t ncaptures;
+
+ ngx_uint_t server_names_hash_max_size;
+ ngx_uint_t server_names_hash_bucket_size;
+
+ ngx_uint_t variables_hash_max_size;
+ ngx_uint_t variables_hash_bucket_size;
+
+ ngx_hash_keys_arrays_t *variables_keys;
+
+ ngx_array_t *ports;
+
+ ngx_uint_t try_files; /* unsigned try_files:1 */
+
+ ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];
+} ngx_http_core_main_conf_t;
+
+
+typedef struct {
+ /* array of the ngx_http_server_name_t, "server_name" directive */
+ ngx_array_t server_names;
+
+ /* server ctx */
+ ngx_http_conf_ctx_t *ctx;
+
+ ngx_str_t server_name;
+
+ size_t connection_pool_size;
+ size_t request_pool_size;
+ size_t client_header_buffer_size;
+
+ ngx_bufs_t large_client_header_buffers;
+
+ ngx_msec_t client_header_timeout;
+
+ ngx_flag_t ignore_invalid_headers;
+ ngx_flag_t merge_slashes;
+ ngx_flag_t underscores_in_headers;
+
+ unsigned listen:1;
+#if (NGX_PCRE)
+ unsigned captures:1;
+#endif
+
+ ngx_http_core_loc_conf_t **named_locations;
+} ngx_http_core_srv_conf_t;
+
+
+/* list of structures to find core_srv_conf quickly at run time */
+
+
+typedef struct {
+#if (NGX_PCRE)
+ ngx_http_regex_t *regex;
+#endif
+ ngx_http_core_srv_conf_t *server; /* virtual name server conf */
+ ngx_str_t name;
+} ngx_http_server_name_t;
+
+
+typedef struct {
+ ngx_hash_combined_t names;
+
+ ngx_uint_t nregex;
+ ngx_http_server_name_t *regex;
+} ngx_http_virtual_names_t;
+
+
+struct ngx_http_addr_conf_s {
+ /* the default server configuration for this address:port */
+ ngx_http_core_srv_conf_t *default_server;
+
+ ngx_http_virtual_names_t *virtual_names;
+
+#if (NGX_HTTP_SSL)
+ unsigned ssl:1;
+#endif
+#if (NGX_HTTP_SPDY)
+ unsigned spdy:1;
+#endif
+ unsigned proxy_protocol:1;
+};
+
+
+typedef struct {
+ in_addr_t addr;
+ ngx_http_addr_conf_t conf;
+} ngx_http_in_addr_t;
+
+
+#if (NGX_HAVE_INET6)
+
+typedef struct {
+ struct in6_addr addr6;
+ ngx_http_addr_conf_t conf;
+} ngx_http_in6_addr_t;
+
+#endif
+
+
+typedef struct {
+ /* ngx_http_in_addr_t or ngx_http_in6_addr_t */
+ void *addrs;
+ ngx_uint_t naddrs;
+} ngx_http_port_t;
+
+
+typedef struct {
+ ngx_int_t family;
+ in_port_t port;
+ ngx_array_t addrs; /* array of ngx_http_conf_addr_t */
+} ngx_http_conf_port_t;
+
+
+typedef struct {
+ ngx_http_listen_opt_t opt;
+
+ ngx_hash_t hash;
+ ngx_hash_wildcard_t *wc_head;
+ ngx_hash_wildcard_t *wc_tail;
+
+#if (NGX_PCRE)
+ ngx_uint_t nregex;
+ ngx_http_server_name_t *regex;
+#endif
+
+ /* the default server configuration for this address:port */
+ ngx_http_core_srv_conf_t *default_server;
+ ngx_array_t servers; /* array of ngx_http_core_srv_conf_t */
+} ngx_http_conf_addr_t;
+
+
+typedef struct {
+ ngx_int_t status;
+ ngx_int_t overwrite;
+ ngx_http_complex_value_t value;
+ ngx_str_t args;
+} ngx_http_err_page_t;
+
+
+typedef struct {
+ ngx_array_t *lengths;
+ ngx_array_t *values;
+ ngx_str_t name;
+
+ unsigned code:10;
+ unsigned test_dir:1;
+} ngx_http_try_file_t;
+
+
+struct ngx_http_core_loc_conf_s {
+ ngx_str_t name; /* location name */
+
+#if (NGX_PCRE)
+ ngx_http_regex_t *regex;
+#endif
+
+ unsigned noname:1; /* "if () {}" block or limit_except */
+ unsigned lmt_excpt:1;
+ unsigned named:1;
+
+ unsigned exact_match:1;
+ unsigned noregex:1;
+
+ unsigned auto_redirect:1;
+#if (NGX_HTTP_GZIP)
+ unsigned gzip_disable_msie6:2;
+#if (NGX_HTTP_DEGRADATION)
+ unsigned gzip_disable_degradation:2;
+#endif
+#endif
+
+ ngx_http_location_tree_node_t *static_locations;
+#if (NGX_PCRE)
+ ngx_http_core_loc_conf_t **regex_locations;
+#endif
+
+ /* pointer to the modules' loc_conf */
+ void **loc_conf;
+
+ uint32_t limit_except;
+ void **limit_except_loc_conf;
+
+ ngx_http_handler_pt handler;
+
+ /* location name length for inclusive location with inherited alias */
+ size_t alias;
+ ngx_str_t root; /* root, alias */
+ ngx_str_t post_action;
+
+ ngx_array_t *root_lengths;
+ ngx_array_t *root_values;
+
+ ngx_array_t *types;
+ ngx_hash_t types_hash;
+ ngx_str_t default_type;
+
+ off_t client_max_body_size; /* client_max_body_size */
+ off_t directio; /* directio */
+ off_t directio_alignment; /* directio_alignment */
+
+ size_t client_body_buffer_size; /* client_body_buffer_size */
+ size_t send_lowat; /* send_lowat */
+ size_t postpone_output; /* postpone_output */
+ size_t limit_rate; /* limit_rate */
+ size_t limit_rate_after; /* limit_rate_after */
+ size_t sendfile_max_chunk; /* sendfile_max_chunk */
+ size_t read_ahead; /* read_ahead */
+
+ ngx_msec_t client_body_timeout; /* client_body_timeout */
+ ngx_msec_t send_timeout; /* send_timeout */
+ ngx_msec_t keepalive_timeout; /* keepalive_timeout */
+ ngx_msec_t lingering_time; /* lingering_time */
+ ngx_msec_t lingering_timeout; /* lingering_timeout */
+ ngx_msec_t resolver_timeout; /* resolver_timeout */
+
+ ngx_resolver_t *resolver; /* resolver */
+
+ time_t keepalive_header; /* keepalive_timeout */
+
+ ngx_uint_t keepalive_requests; /* keepalive_requests */
+ ngx_uint_t keepalive_disable; /* keepalive_disable */
+ ngx_uint_t satisfy; /* satisfy */
+ ngx_uint_t lingering_close; /* lingering_close */
+ ngx_uint_t if_modified_since; /* if_modified_since */
+ ngx_uint_t max_ranges; /* max_ranges */
+ ngx_uint_t client_body_in_file_only; /* client_body_in_file_only */
+
+ ngx_flag_t client_body_in_single_buffer;
+ /* client_body_in_singe_buffer */
+ ngx_flag_t internal; /* internal */
+ ngx_flag_t sendfile; /* sendfile */
+#if (NGX_HAVE_FILE_AIO)
+ ngx_flag_t aio; /* aio */
+#endif
+ ngx_flag_t tcp_nopush; /* tcp_nopush */
+ ngx_flag_t tcp_nodelay; /* tcp_nodelay */
+ ngx_flag_t reset_timedout_connection; /* reset_timedout_connection */
+ ngx_flag_t server_name_in_redirect; /* server_name_in_redirect */
+ ngx_flag_t port_in_redirect; /* port_in_redirect */
+ ngx_flag_t msie_padding; /* msie_padding */
+ ngx_flag_t msie_refresh; /* msie_refresh */
+ ngx_flag_t log_not_found; /* log_not_found */
+ ngx_flag_t log_subrequest; /* log_subrequest */
+ ngx_flag_t recursive_error_pages; /* recursive_error_pages */
+ ngx_flag_t server_tokens; /* server_tokens */
+ ngx_flag_t chunked_transfer_encoding; /* chunked_transfer_encoding */
+ ngx_flag_t etag; /* etag */
+
+#if (NGX_HTTP_GZIP)
+ ngx_flag_t gzip_vary; /* gzip_vary */
+
+ ngx_uint_t gzip_http_version; /* gzip_http_version */
+ ngx_uint_t gzip_proxied; /* gzip_proxied */
+
+#if (NGX_PCRE)
+ ngx_array_t *gzip_disable; /* gzip_disable */
+#endif
+#endif
+
+#if (NGX_HAVE_OPENAT)
+ ngx_uint_t disable_symlinks; /* disable_symlinks */
+ ngx_http_complex_value_t *disable_symlinks_from;
+#endif
+
+ ngx_array_t *error_pages; /* error_page */
+ ngx_http_try_file_t *try_files; /* try_files */
+
+ ngx_path_t *client_body_temp_path; /* client_body_temp_path */
+
+ ngx_open_file_cache_t *open_file_cache;
+ time_t open_file_cache_valid;
+ ngx_uint_t open_file_cache_min_uses;
+ ngx_flag_t open_file_cache_errors;
+ ngx_flag_t open_file_cache_events;
+
+ ngx_log_t *error_log;
+
+ ngx_uint_t types_hash_max_size;
+ ngx_uint_t types_hash_bucket_size;
+
+ ngx_queue_t *locations;
+
+#if 0
+ ngx_http_core_loc_conf_t *prev_location;
+#endif
+};
+
+
+typedef struct {
+ ngx_queue_t queue;
+ ngx_http_core_loc_conf_t *exact;
+ ngx_http_core_loc_conf_t *inclusive;
+ ngx_str_t *name;
+ u_char *file_name;
+ ngx_uint_t line;
+ ngx_queue_t list;
+} ngx_http_location_queue_t;
+
+
+struct ngx_http_location_tree_node_s {
+ ngx_http_location_tree_node_t *left;
+ ngx_http_location_tree_node_t *right;
+ ngx_http_location_tree_node_t *tree;
+
+ ngx_http_core_loc_conf_t *exact;
+ ngx_http_core_loc_conf_t *inclusive;
+
+ u_char auto_redirect;
+ u_char len;
+ u_char name[1];
+};
+
+
+void ngx_http_core_run_phases(ngx_http_request_t *r);
+ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph);
+ngx_int_t ngx_http_core_rewrite_phase(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph);
+ngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph);
+ngx_int_t ngx_http_core_post_rewrite_phase(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph);
+ngx_int_t ngx_http_core_access_phase(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph);
+ngx_int_t ngx_http_core_post_access_phase(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph);
+ngx_int_t ngx_http_core_try_files_phase(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph);
+ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r,
+ ngx_http_phase_handler_t *ph);
+
+
+void *ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash);
+ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r);
+void ngx_http_set_exten(ngx_http_request_t *r);
+ngx_int_t ngx_http_set_etag(ngx_http_request_t *r);
+void ngx_http_weak_etag(ngx_http_request_t *r);
+ngx_int_t ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status,
+ ngx_str_t *ct, ngx_http_complex_value_t *cv);
+u_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name,
+ size_t *root_length, size_t reserved);
+ngx_int_t ngx_http_auth_basic_user(ngx_http_request_t *r);
+#if (NGX_HTTP_GZIP)
+ngx_int_t ngx_http_gzip_ok(ngx_http_request_t *r);
+#endif
+
+
+ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,
+ ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **sr,
+ ngx_http_post_subrequest_t *psr, ngx_uint_t flags);
+ngx_int_t ngx_http_internal_redirect(ngx_http_request_t *r,
+ ngx_str_t *uri, ngx_str_t *args);
+ngx_int_t ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name);
+
+
+ngx_http_cleanup_t *ngx_http_cleanup_add(ngx_http_request_t *r, size_t size);
+
+
+typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r);
+typedef ngx_int_t (*ngx_http_output_body_filter_pt)
+ (ngx_http_request_t *r, ngx_chain_t *chain);
+
+
+ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *chain);
+ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *chain);
+
+
+ngx_int_t ngx_http_set_disable_symlinks(ngx_http_request_t *r,
+ ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of);
+
+ngx_int_t ngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr,
+ ngx_array_t *headers, ngx_str_t *value, ngx_array_t *proxies,
+ int recursive);
+
+
+extern ngx_module_t ngx_http_core_module;
+
+extern ngx_uint_t ngx_http_max_module;
+
+extern ngx_str_t ngx_http_core_get_method;
+
+
+#define ngx_http_clear_content_length(r) \
+ \
+ r->headers_out.content_length_n = -1; \
+ if (r->headers_out.content_length) { \
+ r->headers_out.content_length->hash = 0; \
+ r->headers_out.content_length = NULL; \
+ }
+ \
+#define ngx_http_clear_accept_ranges(r) \
+ \
+ r->allow_ranges = 0; \
+ if (r->headers_out.accept_ranges) { \
+ r->headers_out.accept_ranges->hash = 0; \
+ r->headers_out.accept_ranges = NULL; \
+ }
+
+#define ngx_http_clear_last_modified(r) \
+ \
+ r->headers_out.last_modified_time = -1; \
+ if (r->headers_out.last_modified) { \
+ r->headers_out.last_modified->hash = 0; \
+ r->headers_out.last_modified = NULL; \
+ }
+
+#define ngx_http_clear_location(r) \
+ \
+ if (r->headers_out.location) { \
+ r->headers_out.location->hash = 0; \
+ r->headers_out.location = NULL; \
+ }
+
+#define ngx_http_clear_etag(r) \
+ \
+ if (r->headers_out.etag) { \
+ r->headers_out.etag->hash = 0; \
+ r->headers_out.etag = NULL; \
+ }
+
+
+#endif /* _NGX_HTTP_CORE_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_file_cache.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_file_cache.c
new file mode 100644
index 00000000000..71e6e36c235
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_file_cache.c
@@ -0,0 +1,2015 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_md5.h>
+
+
+static ngx_int_t ngx_http_file_cache_lock(ngx_http_request_t *r,
+ ngx_http_cache_t *c);
+static void ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev);
+static ngx_int_t ngx_http_file_cache_read(ngx_http_request_t *r,
+ ngx_http_cache_t *c);
+static ssize_t ngx_http_file_cache_aio_read(ngx_http_request_t *r,
+ ngx_http_cache_t *c);
+#if (NGX_HAVE_FILE_AIO)
+static void ngx_http_cache_aio_event_handler(ngx_event_t *ev);
+#endif
+static ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache,
+ ngx_http_cache_t *c);
+static ngx_int_t ngx_http_file_cache_name(ngx_http_request_t *r,
+ ngx_path_t *path);
+static ngx_http_file_cache_node_t *
+ ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key);
+static void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+static void ngx_http_file_cache_cleanup(void *data);
+static time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache);
+static time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache);
+static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache,
+ ngx_queue_t *q, u_char *name);
+static void ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache);
+static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx,
+ ngx_str_t *path);
+static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx,
+ ngx_str_t *path);
+static ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx,
+ ngx_str_t *path);
+static ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache,
+ ngx_http_cache_t *c);
+static ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx,
+ ngx_str_t *path);
+
+
+ngx_str_t ngx_http_cache_status[] = {
+ ngx_string("MISS"),
+ ngx_string("BYPASS"),
+ ngx_string("EXPIRED"),
+ ngx_string("STALE"),
+ ngx_string("UPDATING"),
+ ngx_string("REVALIDATED"),
+ ngx_string("HIT")
+};
+
+
+static u_char ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' };
+
+
+static ngx_int_t
+ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data)
+{
+ ngx_http_file_cache_t *ocache = data;
+
+ size_t len;
+ ngx_uint_t n;
+ ngx_http_file_cache_t *cache;
+
+ cache = shm_zone->data;
+
+ if (ocache) {
+ if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) {
+ ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
+ "cache \"%V\" uses the \"%V\" cache path "
+ "while previously it used the \"%V\" cache path",
+ &shm_zone->shm.name, &cache->path->name,
+ &ocache->path->name);
+
+ return NGX_ERROR;
+ }
+
+ for (n = 0; n < 3; n++) {
+ if (cache->path->level[n] != ocache->path->level[n]) {
+ ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
+ "cache \"%V\" had previously different levels",
+ &shm_zone->shm.name);
+ return NGX_ERROR;
+ }
+ }
+
+ cache->sh = ocache->sh;
+
+ cache->shpool = ocache->shpool;
+ cache->bsize = ocache->bsize;
+
+ cache->max_size /= cache->bsize;
+
+ if (!cache->sh->cold || cache->sh->loading) {
+ cache->path->loader = NULL;
+ }
+
+ return NGX_OK;
+ }
+
+ cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
+
+ if (shm_zone->shm.exists) {
+ cache->sh = cache->shpool->data;
+ cache->bsize = ngx_fs_bsize(cache->path->name.data);
+
+ return NGX_OK;
+ }
+
+ cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t));
+ if (cache->sh == NULL) {
+ return NGX_ERROR;
+ }
+
+ cache->shpool->data = cache->sh;
+
+ ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel,
+ ngx_http_file_cache_rbtree_insert_value);
+
+ ngx_queue_init(&cache->sh->queue);
+
+ cache->sh->cold = 1;
+ cache->sh->loading = 0;
+ cache->sh->size = 0;
+
+ cache->bsize = ngx_fs_bsize(cache->path->name.data);
+
+ cache->max_size /= cache->bsize;
+
+ len = sizeof(" in cache keys zone \"\"") + shm_zone->shm.name.len;
+
+ cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len);
+ if (cache->shpool->log_ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_sprintf(cache->shpool->log_ctx, " in cache keys zone \"%V\"%Z",
+ &shm_zone->shm.name);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_file_cache_new(ngx_http_request_t *r)
+{
+ ngx_http_cache_t *c;
+
+ c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t));
+ if (c == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&c->keys, r->pool, 4, sizeof(ngx_str_t)) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ r->cache = c;
+ c->file.log = r->connection->log;
+ c->file.fd = NGX_INVALID_FILE;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_file_cache_create(ngx_http_request_t *r)
+{
+ ngx_http_cache_t *c;
+ ngx_pool_cleanup_t *cln;
+ ngx_http_file_cache_t *cache;
+
+ c = r->cache;
+ cache = c->file_cache;
+
+ cln = ngx_pool_cleanup_add(r->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_http_file_cache_cleanup;
+ cln->data = c;
+
+ if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_http_file_cache_create_key(ngx_http_request_t *r)
+{
+ size_t len;
+ ngx_str_t *key;
+ ngx_uint_t i;
+ ngx_md5_t md5;
+ ngx_http_cache_t *c;
+
+ c = r->cache;
+
+ len = 0;
+
+ ngx_crc32_init(c->crc32);
+ ngx_md5_init(&md5);
+
+ key = c->keys.elts;
+ for (i = 0; i < c->keys.nelts; i++) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http cache key: \"%V\"", &key[i]);
+
+ len += key[i].len;
+
+ ngx_crc32_update(&c->crc32, key[i].data, key[i].len);
+ ngx_md5_update(&md5, key[i].data, key[i].len);
+ }
+
+ c->header_start = sizeof(ngx_http_file_cache_header_t)
+ + sizeof(ngx_http_file_cache_key) + len + 1;
+
+ ngx_crc32_final(c->crc32);
+ ngx_md5_final(c->key, &md5);
+}
+
+
+ngx_int_t
+ngx_http_file_cache_open(ngx_http_request_t *r)
+{
+ ngx_int_t rc, rv;
+ ngx_uint_t cold, test;
+ ngx_http_cache_t *c;
+ ngx_pool_cleanup_t *cln;
+ ngx_open_file_info_t of;
+ ngx_http_file_cache_t *cache;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->cache;
+
+ if (c->waiting) {
+ return NGX_AGAIN;
+ }
+
+ if (c->buf) {
+ return ngx_http_file_cache_read(r, c);
+ }
+
+ cache = c->file_cache;
+
+ if (c->node == NULL) {
+ cln = ngx_pool_cleanup_add(r->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_http_file_cache_cleanup;
+ cln->data = c;
+ }
+
+ rc = ngx_http_file_cache_exists(cache, c);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http file cache exists: %i e:%d", rc, c->exists);
+
+ if (rc == NGX_ERROR) {
+ return rc;
+ }
+
+ if (rc == NGX_AGAIN) {
+ return NGX_HTTP_CACHE_SCARCE;
+ }
+
+ cold = cache->sh->cold;
+
+ if (rc == NGX_OK) {
+
+ if (c->error) {
+ return c->error;
+ }
+
+ c->temp_file = 1;
+ test = c->exists ? 1 : 0;
+ rv = NGX_DECLINED;
+
+ } else { /* rc == NGX_DECLINED */
+
+ if (c->min_uses > 1) {
+
+ if (!cold) {
+ return NGX_HTTP_CACHE_SCARCE;
+ }
+
+ test = 1;
+ rv = NGX_HTTP_CACHE_SCARCE;
+
+ } else {
+ c->temp_file = 1;
+ test = cold ? 1 : 0;
+ rv = NGX_DECLINED;
+ }
+ }
+
+ if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (!test) {
+ goto done;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+ of.uniq = c->uniq;
+ of.valid = clcf->open_file_cache_valid;
+ of.min_uses = clcf->open_file_cache_min_uses;
+ of.events = clcf->open_file_cache_events;
+ of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
+ of.read_ahead = clcf->read_ahead;
+
+ if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool)
+ != NGX_OK)
+ {
+ switch (of.err) {
+
+ case 0:
+ return NGX_ERROR;
+
+ case NGX_ENOENT:
+ case NGX_ENOTDIR:
+ goto done;
+
+ default:
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
+ ngx_open_file_n " \"%s\" failed", c->file.name.data);
+ return NGX_ERROR;
+ }
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http file cache fd: %d", of.fd);
+
+ c->file.fd = of.fd;
+ c->file.log = r->connection->log;
+ c->uniq = of.uniq;
+ c->length = of.size;
+ c->fs_size = (of.fs_size + cache->bsize - 1) / cache->bsize;
+
+ c->buf = ngx_create_temp_buf(r->pool, c->body_start);
+ if (c->buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ return ngx_http_file_cache_read(r, c);
+
+done:
+
+ if (rv == NGX_DECLINED) {
+ return ngx_http_file_cache_lock(r, c);
+ }
+
+ return rv;
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_lock(ngx_http_request_t *r, ngx_http_cache_t *c)
+{
+ ngx_msec_t now, timer;
+ ngx_http_file_cache_t *cache;
+
+ if (!c->lock) {
+ return NGX_DECLINED;
+ }
+
+ cache = c->file_cache;
+
+ ngx_shmtx_lock(&cache->shpool->mutex);
+
+ if (!c->node->updating) {
+ c->node->updating = 1;
+ c->updating = 1;
+ }
+
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http file cache lock u:%d wt:%M",
+ c->updating, c->wait_time);
+
+ if (c->updating) {
+ return NGX_DECLINED;
+ }
+
+ c->waiting = 1;
+
+ now = ngx_current_msec;
+
+ if (c->wait_time == 0) {
+ c->wait_time = now + c->lock_timeout;
+
+ c->wait_event.handler = ngx_http_file_cache_lock_wait_handler;
+ c->wait_event.data = r;
+ c->wait_event.log = r->connection->log;
+ }
+
+ timer = c->wait_time - now;
+
+ ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);
+
+ r->main->blocked++;
+
+ return NGX_AGAIN;
+}
+
+
+static void
+ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev)
+{
+ ngx_uint_t wait;
+ ngx_msec_t timer;
+ ngx_http_cache_t *c;
+ ngx_http_request_t *r;
+ ngx_http_file_cache_t *cache;
+
+ r = ev->data;
+ c = r->cache;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,
+ "http file cache wait handler wt:%M cur:%M",
+ c->wait_time, ngx_current_msec);
+
+ timer = c->wait_time - ngx_current_msec;
+
+ if ((ngx_msec_int_t) timer <= 0) {
+ ngx_log_error(NGX_LOG_INFO, ev->log, 0, "cache lock timeout");
+ c->lock = 0;
+ goto wakeup;
+ }
+
+ cache = c->file_cache;
+ wait = 0;
+
+ ngx_shmtx_lock(&cache->shpool->mutex);
+
+ if (c->node->updating) {
+ wait = 1;
+ }
+
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+
+ if (wait) {
+ ngx_add_timer(ev, (timer > 500) ? 500 : timer);
+ return;
+ }
+
+wakeup:
+
+ c->waiting = 0;
+ r->main->blocked--;
+ r->connection->write->handler(r->connection->write);
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c)
+{
+ time_t now;
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_http_file_cache_t *cache;
+ ngx_http_file_cache_header_t *h;
+
+ n = ngx_http_file_cache_aio_read(r, c);
+
+ if (n < 0) {
+ return n;
+ }
+
+ if ((size_t) n < c->header_start) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+ "cache file \"%s\" is too small", c->file.name.data);
+ return NGX_DECLINED;
+ }
+
+ h = (ngx_http_file_cache_header_t *) c->buf->pos;
+
+ if (h->version != NGX_HTTP_CACHE_VERSION) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "cache file \"%s\" version mismatch", c->file.name.data);
+ return NGX_DECLINED;
+ }
+
+ if (h->crc32 != c->crc32) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+ "cache file \"%s\" has md5 collision", c->file.name.data);
+ return NGX_DECLINED;
+ }
+
+ if ((size_t) h->body_start > c->body_start) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+ "cache file \"%s\" has too long header",
+ c->file.name.data);
+ return NGX_DECLINED;
+ }
+
+ c->buf->last += n;
+
+ c->valid_sec = h->valid_sec;
+ c->last_modified = h->last_modified;
+ c->date = h->date;
+ c->valid_msec = h->valid_msec;
+ c->header_start = h->header_start;
+ c->body_start = h->body_start;
+ c->etag.len = h->etag_len;
+ c->etag.data = h->etag;
+
+ r->cached = 1;
+
+ cache = c->file_cache;
+
+ if (cache->sh->cold) {
+
+ ngx_shmtx_lock(&cache->shpool->mutex);
+
+ if (!c->node->exists) {
+ c->node->uses = 1;
+ c->node->body_start = c->body_start;
+ c->node->exists = 1;
+ c->node->uniq = c->uniq;
+ c->node->fs_size = c->fs_size;
+
+ cache->sh->size += c->fs_size;
+ }
+
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+ }
+
+ now = ngx_time();
+
+ if (c->valid_sec < now) {
+
+ ngx_shmtx_lock(&cache->shpool->mutex);
+
+ if (c->node->updating) {
+ rc = NGX_HTTP_CACHE_UPDATING;
+
+ } else {
+ c->node->updating = 1;
+ c->updating = 1;
+ rc = NGX_HTTP_CACHE_STALE;
+ }
+
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http file cache expired: %i %T %T",
+ rc, c->valid_sec, now);
+
+ return rc;
+ }
+
+ return NGX_OK;
+}
+
+
+static ssize_t
+ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
+{
+#if (NGX_HAVE_FILE_AIO)
+ ssize_t n;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (!ngx_file_aio) {
+ goto noaio;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (!clcf->aio) {
+ goto noaio;
+ }
+
+ n = ngx_file_aio_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);
+
+ if (n != NGX_AGAIN) {
+ return n;
+ }
+
+ c->file.aio->data = r;
+ c->file.aio->handler = ngx_http_cache_aio_event_handler;
+
+ r->main->blocked++;
+ r->aio = 1;
+
+ return NGX_AGAIN;
+
+noaio:
+
+#endif
+
+ return ngx_read_file(&c->file, c->buf->pos, c->body_start, 0);
+}
+
+
+#if (NGX_HAVE_FILE_AIO)
+
+static void
+ngx_http_cache_aio_event_handler(ngx_event_t *ev)
+{
+ ngx_event_aio_t *aio;
+ ngx_http_request_t *r;
+
+ aio = ev->data;
+ r = aio->data;
+
+ r->main->blocked--;
+ r->aio = 0;
+
+ r->connection->write->handler(r->connection->write);
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
+{
+ ngx_int_t rc;
+ ngx_http_file_cache_node_t *fcn;
+
+ ngx_shmtx_lock(&cache->shpool->mutex);
+
+ fcn = c->node;
+
+ if (fcn == NULL) {
+ fcn = ngx_http_file_cache_lookup(cache, c->key);
+ }
+
+ if (fcn) {
+ ngx_queue_remove(&fcn->queue);
+
+ if (c->node == NULL) {
+ fcn->uses++;
+ fcn->count++;
+ }
+
+ if (fcn->error) {
+
+ if (fcn->valid_sec < ngx_time()) {
+ goto renew;
+ }
+
+ rc = NGX_OK;
+
+ goto done;
+ }
+
+ if (fcn->exists || fcn->uses >= c->min_uses) {
+
+ c->exists = fcn->exists;
+ if (fcn->body_start) {
+ c->body_start = fcn->body_start;
+ }
+
+ rc = NGX_OK;
+
+ goto done;
+ }
+
+ rc = NGX_AGAIN;
+
+ goto done;
+ }
+
+ fcn = ngx_slab_calloc_locked(cache->shpool,
+ sizeof(ngx_http_file_cache_node_t));
+ if (fcn == NULL) {
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+
+ (void) ngx_http_file_cache_forced_expire(cache);
+
+ ngx_shmtx_lock(&cache->shpool->mutex);
+
+ fcn = ngx_slab_calloc_locked(cache->shpool,
+ sizeof(ngx_http_file_cache_node_t));
+ if (fcn == NULL) {
+ rc = NGX_ERROR;
+ goto failed;
+ }
+ }
+
+ ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
+
+ ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
+ NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
+
+ ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
+
+ fcn->uses = 1;
+ fcn->count = 1;
+
+renew:
+
+ rc = NGX_DECLINED;
+
+ fcn->valid_msec = 0;
+ fcn->error = 0;
+ fcn->exists = 0;
+ fcn->valid_sec = 0;
+ fcn->uniq = 0;
+ fcn->body_start = 0;
+ fcn->fs_size = 0;
+
+done:
+
+ fcn->expire = ngx_time() + cache->inactive;
+
+ ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
+
+ c->uniq = fcn->uniq;
+ c->error = fcn->error;
+ c->node = fcn;
+
+failed:
+
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path)
+{
+ u_char *p;
+ ngx_http_cache_t *c;
+
+ c = r->cache;
+
+ if (c->file.name.len) {
+ return NGX_OK;
+ }
+
+ c->file.name.len = path->name.len + 1 + path->len
+ + 2 * NGX_HTTP_CACHE_KEY_LEN;
+
+ c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1);
+ if (c->file.name.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(c->file.name.data, path->name.data, path->name.len);
+
+ p = c->file.name.data + path->name.len + 1 + path->len;
+ p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN);
+ *p = '\0';
+
+ ngx_create_hashed_filename(path, c->file.name.data, c->file.name.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "cache file: \"%s\"", c->file.name.data);
+
+ return NGX_OK;
+}
+
+
+static ngx_http_file_cache_node_t *
+ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key)
+{
+ ngx_int_t rc;
+ ngx_rbtree_key_t node_key;
+ ngx_rbtree_node_t *node, *sentinel;
+ ngx_http_file_cache_node_t *fcn;
+
+ ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t));
+
+ node = cache->sh->rbtree.root;
+ sentinel = cache->sh->rbtree.sentinel;
+
+ while (node != sentinel) {
+
+ if (node_key < node->key) {
+ node = node->left;
+ continue;
+ }
+
+ if (node_key > node->key) {
+ node = node->right;
+ continue;
+ }
+
+ /* node_key == node->key */
+
+ fcn = (ngx_http_file_cache_node_t *) node;
+
+ rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key,
+ NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
+
+ if (rc == 0) {
+ return fcn;
+ }
+
+ node = (rc < 0) ? node->left : node->right;
+ }
+
+ /* not found */
+
+ return NULL;
+}
+
+
+static void
+ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+ ngx_rbtree_node_t **p;
+ ngx_http_file_cache_node_t *cn, *cnt;
+
+ for ( ;; ) {
+
+ if (node->key < temp->key) {
+
+ p = &temp->left;
+
+ } else if (node->key > temp->key) {
+
+ p = &temp->right;
+
+ } else { /* node->key == temp->key */
+
+ cn = (ngx_http_file_cache_node_t *) node;
+ cnt = (ngx_http_file_cache_node_t *) temp;
+
+ p = (ngx_memcmp(cn->key, cnt->key,
+ NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t))
+ < 0)
+ ? &temp->left : &temp->right;
+ }
+
+ if (*p == sentinel) {
+ break;
+ }
+
+ temp = *p;
+ }
+
+ *p = node;
+ node->parent = temp;
+ node->left = sentinel;
+ node->right = sentinel;
+ ngx_rbt_red(node);
+}
+
+
+void
+ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf)
+{
+ ngx_http_file_cache_header_t *h = (ngx_http_file_cache_header_t *) buf;
+
+ u_char *p;
+ ngx_str_t *key;
+ ngx_uint_t i;
+ ngx_http_cache_t *c;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http file cache set header");
+
+ c = r->cache;
+
+ ngx_memzero(h, sizeof(ngx_http_file_cache_header_t));
+
+ h->version = NGX_HTTP_CACHE_VERSION;
+ h->valid_sec = c->valid_sec;
+ h->last_modified = c->last_modified;
+ h->date = c->date;
+ h->crc32 = c->crc32;
+ h->valid_msec = (u_short) c->valid_msec;
+ h->header_start = (u_short) c->header_start;
+ h->body_start = (u_short) c->body_start;
+
+ if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {
+ h->etag_len = (u_char) c->etag.len;
+ ngx_memcpy(h->etag, c->etag.data, c->etag.len);
+ }
+
+ p = buf + sizeof(ngx_http_file_cache_header_t);
+
+ p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key));
+
+ key = c->keys.elts;
+ for (i = 0; i < c->keys.nelts; i++) {
+ p = ngx_copy(p, key[i].data, key[i].len);
+ }
+
+ *p = LF;
+}
+
+
+void
+ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)
+{
+ off_t fs_size;
+ ngx_int_t rc;
+ ngx_file_uniq_t uniq;
+ ngx_file_info_t fi;
+ ngx_http_cache_t *c;
+ ngx_ext_rename_file_t ext;
+ ngx_http_file_cache_t *cache;
+
+ c = r->cache;
+
+ if (c->updated) {
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http file cache update");
+
+ c->updated = 1;
+ c->updating = 0;
+
+ cache = c->file_cache;
+
+ uniq = 0;
+ fs_size = 0;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http file cache rename: \"%s\" to \"%s\"",
+ tf->file.name.data, c->file.name.data);
+
+ ext.access = NGX_FILE_OWNER_ACCESS;
+ ext.path_access = NGX_FILE_OWNER_ACCESS;
+ ext.time = -1;
+ ext.create_path = 1;
+ ext.delete_file = 1;
+ ext.log = r->connection->log;
+
+ rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext);
+
+ if (rc == NGX_OK) {
+
+ if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+ ngx_fd_info_n " \"%s\" failed", tf->file.name.data);
+
+ rc = NGX_ERROR;
+
+ } else {
+ uniq = ngx_file_uniq(&fi);
+ fs_size = (ngx_file_fs_size(&fi) + cache->bsize - 1) / cache->bsize;
+ }
+ }
+
+ ngx_shmtx_lock(&cache->shpool->mutex);
+
+ c->node->count--;
+ c->node->uniq = uniq;
+ c->node->body_start = c->body_start;
+
+ cache->sh->size += fs_size - c->node->fs_size;
+ c->node->fs_size = fs_size;
+
+ if (rc == NGX_OK) {
+ c->node->exists = 1;
+ }
+
+ c->node->updating = 0;
+
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+}
+
+
+void
+ngx_http_file_cache_update_header(ngx_http_request_t *r)
+{
+ ssize_t n;
+ ngx_err_t err;
+ ngx_file_t file;
+ ngx_file_info_t fi;
+ ngx_http_cache_t *c;
+ ngx_http_file_cache_header_t h;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http file cache update header");
+
+ c = r->cache;
+
+ ngx_memzero(&file, sizeof(ngx_file_t));
+
+ file.name = c->file.name;
+ file.log = r->connection->log;
+ file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);
+
+ if (file.fd == NGX_INVALID_FILE) {
+ err = ngx_errno;
+
+ /* cache file may have been deleted */
+
+ if (err == NGX_ENOENT) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http file cache \"%s\" not found",
+ file.name.data);
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
+ ngx_open_file_n " \"%s\" failed", file.name.data);
+ return;
+ }
+
+ /*
+ * make sure cache file wasn't replaced;
+ * if it was, do nothing
+ */
+
+ if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+ ngx_fd_info_n " \"%s\" failed", file.name.data);
+ goto done;
+ }
+
+ if (c->uniq != ngx_file_uniq(&fi)
+ || c->length != ngx_file_size(&fi))
+ {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http file cache \"%s\" changed",
+ file.name.data);
+ goto done;
+ }
+
+ n = ngx_read_file(&file, (u_char *) &h,
+ sizeof(ngx_http_file_cache_header_t), 0);
+
+ if (n == NGX_ERROR) {
+ goto done;
+ }
+
+ if ((size_t) n != sizeof(ngx_http_file_cache_header_t)) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
+ ngx_read_file_n " read only %z of %z from \"%s\"",
+ n, sizeof(ngx_http_file_cache_header_t), file.name.data);
+ goto done;
+ }
+
+ if (h.version != NGX_HTTP_CACHE_VERSION
+ || h.last_modified != c->last_modified
+ || h.crc32 != c->crc32
+ || h.header_start != c->header_start
+ || h.body_start != c->body_start)
+ {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http file cache \"%s\" content changed",
+ file.name.data);
+ goto done;
+ }
+
+ /*
+ * update cache file header with new data,
+ * notably h.valid_sec and h.date
+ */
+
+ ngx_memzero(&h, sizeof(ngx_http_file_cache_header_t));
+
+ h.version = NGX_HTTP_CACHE_VERSION;
+ h.valid_sec = c->valid_sec;
+ h.last_modified = c->last_modified;
+ h.date = c->date;
+ h.crc32 = c->crc32;
+ h.valid_msec = (u_short) c->valid_msec;
+ h.header_start = (u_short) c->header_start;
+ h.body_start = (u_short) c->body_start;
+
+ if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {
+ h.etag_len = (u_char) c->etag.len;
+ ngx_memcpy(h.etag, c->etag.data, c->etag.len);
+ }
+
+ (void) ngx_write_file(&file, (u_char *) &h,
+ sizeof(ngx_http_file_cache_header_t), 0);
+
+done:
+
+ if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", file.name.data);
+ }
+}
+
+
+ngx_int_t
+ngx_http_cache_send(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t out;
+ ngx_http_cache_t *c;
+
+ c = r->cache;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http file cache send: %s", c->file.name.data);
+
+ if (r != r->main && c->length - c->body_start == 0) {
+ return ngx_http_send_header(r);
+ }
+
+ /* we need to allocate all before the header would be sent */
+
+ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+ if (b == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
+ if (b->file == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+ return rc;
+ }
+
+ b->file_pos = c->body_start;
+ b->file_last = c->length;
+
+ b->in_file = (c->length - c->body_start) ? 1: 0;
+ b->last_buf = (r == r->main) ? 1: 0;
+ b->last_in_chain = 1;
+
+ b->file->fd = c->file.fd;
+ b->file->name = c->file.name;
+ b->file->log = r->connection->log;
+
+ out.buf = b;
+ out.next = NULL;
+
+ return ngx_http_output_filter(r, &out);
+}
+
+
+void
+ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf)
+{
+ ngx_http_file_cache_t *cache;
+ ngx_http_file_cache_node_t *fcn;
+
+ if (c->updated || c->node == NULL) {
+ return;
+ }
+
+ cache = c->file_cache;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
+ "http file cache free, fd: %d", c->file.fd);
+
+ ngx_shmtx_lock(&cache->shpool->mutex);
+
+ fcn = c->node;
+ fcn->count--;
+
+ if (c->updating) {
+ fcn->updating = 0;
+ }
+
+ if (c->error) {
+ fcn->error = c->error;
+
+ if (c->valid_sec) {
+ fcn->valid_sec = c->valid_sec;
+ fcn->valid_msec = c->valid_msec;
+ }
+
+ } else if (!fcn->exists && fcn->count == 0 && c->min_uses == 1) {
+ ngx_queue_remove(&fcn->queue);
+ ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
+ ngx_slab_free_locked(cache->shpool, fcn);
+ c->node = NULL;
+ }
+
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+
+ c->updated = 1;
+ c->updating = 0;
+
+ if (c->temp_file) {
+ if (tf && tf->file.fd != NGX_INVALID_FILE) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
+ "http file cache incomplete: \"%s\"",
+ tf->file.name.data);
+
+ if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno,
+ ngx_delete_file_n " \"%s\" failed",
+ tf->file.name.data);
+ }
+ }
+ }
+
+ if (c->wait_event.timer_set) {
+ ngx_del_timer(&c->wait_event);
+ }
+}
+
+
+static void
+ngx_http_file_cache_cleanup(void *data)
+{
+ ngx_http_cache_t *c = data;
+
+ if (c->updated) {
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,
+ "http file cache cleanup");
+
+ if (c->updating) {
+ ngx_log_error(NGX_LOG_ALERT, c->file.log, 0,
+ "stalled cache updating, error:%ui", c->error);
+ }
+
+ ngx_http_file_cache_free(c, NULL);
+}
+
+
+static time_t
+ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)
+{
+ u_char *name;
+ size_t len;
+ time_t wait;
+ ngx_uint_t tries;
+ ngx_path_t *path;
+ ngx_queue_t *q;
+ ngx_http_file_cache_node_t *fcn;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+ "http file cache forced expire");
+
+ path = cache->path;
+ len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
+
+ name = ngx_alloc(len + 1, ngx_cycle->log);
+ if (name == NULL) {
+ return 10;
+ }
+
+ ngx_memcpy(name, path->name.data, path->name.len);
+
+ wait = 10;
+ tries = 20;
+
+ ngx_shmtx_lock(&cache->shpool->mutex);
+
+ for (q = ngx_queue_last(&cache->sh->queue);
+ q != ngx_queue_sentinel(&cache->sh->queue);
+ q = ngx_queue_prev(q))
+ {
+ fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
+
+ ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+ "http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd",
+ fcn->count, fcn->exists,
+ fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
+
+ if (fcn->count == 0) {
+ ngx_http_file_cache_delete(cache, q, name);
+ wait = 0;
+
+ } else {
+ if (--tries) {
+ continue;
+ }
+
+ wait = 1;
+ }
+
+ break;
+ }
+
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+
+ ngx_free(name);
+
+ return wait;
+}
+
+
+static time_t
+ngx_http_file_cache_expire(ngx_http_file_cache_t *cache)
+{
+ u_char *name, *p;
+ size_t len;
+ time_t now, wait;
+ ngx_path_t *path;
+ ngx_queue_t *q;
+ ngx_http_file_cache_node_t *fcn;
+ u_char key[2 * NGX_HTTP_CACHE_KEY_LEN];
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+ "http file cache expire");
+
+ path = cache->path;
+ len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
+
+ name = ngx_alloc(len + 1, ngx_cycle->log);
+ if (name == NULL) {
+ return 10;
+ }
+
+ ngx_memcpy(name, path->name.data, path->name.len);
+
+ now = ngx_time();
+
+ ngx_shmtx_lock(&cache->shpool->mutex);
+
+ for ( ;; ) {
+
+ if (ngx_quit || ngx_terminate) {
+ wait = 1;
+ break;
+ }
+
+ if (ngx_queue_empty(&cache->sh->queue)) {
+ wait = 10;
+ break;
+ }
+
+ q = ngx_queue_last(&cache->sh->queue);
+
+ fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
+
+ wait = fcn->expire - now;
+
+ if (wait > 0) {
+ wait = wait > 10 ? 10 : wait;
+ break;
+ }
+
+ ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+ "http file cache expire: #%d %d %02xd%02xd%02xd%02xd",
+ fcn->count, fcn->exists,
+ fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);
+
+ if (fcn->count == 0) {
+ ngx_http_file_cache_delete(cache, q, name);
+ continue;
+ }
+
+ if (fcn->deleting) {
+ wait = 1;
+ break;
+ }
+
+ p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
+ sizeof(ngx_rbtree_key_t));
+ len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
+ (void) ngx_hex_dump(p, fcn->key, len);
+
+ /*
+ * abnormally exited workers may leave locked cache entries,
+ * and although it may be safe to remove them completely,
+ * we prefer to just move them to the top of the inactive queue
+ */
+
+ ngx_queue_remove(q);
+ fcn->expire = ngx_time() + cache->inactive;
+ ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
+
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "ignore long locked inactive cache entry %*s, count:%d",
+ 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);
+ }
+
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+
+ ngx_free(name);
+
+ return wait;
+}
+
+
+static void
+ngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q,
+ u_char *name)
+{
+ u_char *p;
+ size_t len;
+ ngx_path_t *path;
+ ngx_http_file_cache_node_t *fcn;
+
+ fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
+
+ if (fcn->exists) {
+ cache->sh->size -= fcn->fs_size;
+
+ path = cache->path;
+ p = name + path->name.len + 1 + path->len;
+ p = ngx_hex_dump(p, (u_char *) &fcn->node.key,
+ sizeof(ngx_rbtree_key_t));
+ len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
+ p = ngx_hex_dump(p, fcn->key, len);
+ *p = '\0';
+
+ fcn->count++;
+ fcn->deleting = 1;
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+
+ len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
+ ngx_create_hashed_filename(path, name, len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+ "http file cache expire: \"%s\"", name);
+
+ if (ngx_delete_file(name) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,
+ ngx_delete_file_n " \"%s\" failed", name);
+ }
+
+ ngx_shmtx_lock(&cache->shpool->mutex);
+ fcn->count--;
+ fcn->deleting = 0;
+ }
+
+ if (fcn->count == 0) {
+ ngx_queue_remove(q);
+ ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
+ ngx_slab_free_locked(cache->shpool, fcn);
+ }
+}
+
+
+static time_t
+ngx_http_file_cache_manager(void *data)
+{
+ ngx_http_file_cache_t *cache = data;
+
+ off_t size;
+ time_t next, wait;
+
+ next = ngx_http_file_cache_expire(cache);
+
+ cache->last = ngx_current_msec;
+ cache->files = 0;
+
+ for ( ;; ) {
+ ngx_shmtx_lock(&cache->shpool->mutex);
+
+ size = cache->sh->size;
+
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+ "http file cache size: %O", size);
+
+ if (size < cache->max_size) {
+ return next;
+ }
+
+ wait = ngx_http_file_cache_forced_expire(cache);
+
+ if (wait > 0) {
+ return wait;
+ }
+
+ if (ngx_quit || ngx_terminate) {
+ return next;
+ }
+ }
+}
+
+
+static void
+ngx_http_file_cache_loader(void *data)
+{
+ ngx_http_file_cache_t *cache = data;
+
+ ngx_tree_ctx_t tree;
+
+ if (!cache->sh->cold || cache->sh->loading) {
+ return;
+ }
+
+ if (!ngx_atomic_cmp_set(&cache->sh->loading, 0, ngx_pid)) {
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+ "http file cache loader");
+
+ tree.init_handler = NULL;
+ tree.file_handler = ngx_http_file_cache_manage_file;
+ tree.pre_tree_handler = ngx_http_file_cache_noop;
+ tree.post_tree_handler = ngx_http_file_cache_noop;
+ tree.spec_handler = ngx_http_file_cache_delete_file;
+ tree.data = cache;
+ tree.alloc = 0;
+ tree.log = ngx_cycle->log;
+
+ cache->last = ngx_current_msec;
+ cache->files = 0;
+
+ if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {
+ cache->sh->loading = 0;
+ return;
+ }
+
+ cache->sh->cold = 0;
+ cache->sh->loading = 0;
+
+ ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
+ "http file cache: %V %.3fM, bsize: %uz",
+ &cache->path->name,
+ ((double) cache->sh->size * cache->bsize) / (1024 * 1024),
+ cache->bsize);
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+ ngx_msec_t elapsed;
+ ngx_http_file_cache_t *cache;
+
+ cache = ctx->data;
+
+ if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) {
+ (void) ngx_http_file_cache_delete_file(ctx, path);
+ }
+
+ if (++cache->files >= cache->loader_files) {
+ ngx_http_file_cache_loader_sleep(cache);
+
+ } else {
+ ngx_time_update();
+
+ elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
+ "http file cache loader time elapsed: %M", elapsed);
+
+ if (elapsed >= cache->loader_threshold) {
+ ngx_http_file_cache_loader_sleep(cache);
+ }
+ }
+
+ return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;
+}
+
+
+static void
+ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache)
+{
+ ngx_msleep(cache->loader_sleep);
+
+ ngx_time_update();
+
+ cache->last = ngx_current_msec;
+ cache->files = 0;
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)
+{
+ u_char *p;
+ ngx_int_t n;
+ ngx_uint_t i;
+ ngx_http_cache_t c;
+ ngx_http_file_cache_t *cache;
+
+ if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
+ return NGX_ERROR;
+ }
+
+ if (ctx->size < (off_t) sizeof(ngx_http_file_cache_header_t)) {
+ ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,
+ "cache file \"%s\" is too small", name->data);
+ return NGX_ERROR;
+ }
+
+ ngx_memzero(&c, sizeof(ngx_http_cache_t));
+ cache = ctx->data;
+
+ c.length = ctx->size;
+ c.fs_size = (ctx->fs_size + cache->bsize - 1) / cache->bsize;
+
+ p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN];
+
+ for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) {
+ n = ngx_hextoi(p, 2);
+
+ if (n == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ p += 2;
+
+ c.key[i] = (u_char) n;
+ }
+
+ return ngx_http_file_cache_add(cache, &c);
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)
+{
+ ngx_http_file_cache_node_t *fcn;
+
+ ngx_shmtx_lock(&cache->shpool->mutex);
+
+ fcn = ngx_http_file_cache_lookup(cache, c->key);
+
+ if (fcn == NULL) {
+
+ fcn = ngx_slab_calloc_locked(cache->shpool,
+ sizeof(ngx_http_file_cache_node_t));
+ if (fcn == NULL) {
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
+
+ ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],
+ NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));
+
+ ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
+
+ fcn->uses = 1;
+ fcn->exists = 1;
+ fcn->fs_size = c->fs_size;
+
+ cache->sh->size += c->fs_size;
+
+ } else {
+ ngx_queue_remove(&fcn->queue);
+ }
+
+ fcn->expire = ngx_time() + cache->inactive;
+
+ ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
+
+ ngx_shmtx_unlock(&cache->shpool->mutex);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)
+{
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,
+ "http file cache delete: \"%s\"", path->data);
+
+ if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
+ ngx_delete_file_n " \"%s\" failed", path->data);
+ }
+
+ return NGX_OK;
+}
+
+
+time_t
+ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status)
+{
+ ngx_uint_t i;
+ ngx_http_cache_valid_t *valid;
+
+ if (cache_valid == NULL) {
+ return 0;
+ }
+
+ valid = cache_valid->elts;
+ for (i = 0; i < cache_valid->nelts; i++) {
+
+ if (valid[i].status == 0) {
+ return valid[i].valid;
+ }
+
+ if (valid[i].status == status) {
+ return valid[i].valid;
+ }
+ }
+
+ return 0;
+}
+
+
+char *
+ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ off_t max_size;
+ u_char *last, *p;
+ time_t inactive;
+ ssize_t size;
+ ngx_str_t s, name, *value;
+ ngx_int_t loader_files;
+ ngx_msec_t loader_sleep, loader_threshold;
+ ngx_uint_t i, n;
+ ngx_http_file_cache_t *cache;
+
+ cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t));
+ if (cache == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
+ if (cache->path == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ inactive = 600;
+ loader_files = 100;
+ loader_sleep = 50;
+ loader_threshold = 200;
+
+ name.len = 0;
+ size = 0;
+ max_size = NGX_MAX_OFF_T_VALUE;
+
+ value = cf->args->elts;
+
+ cache->path->name = value[1];
+
+ if (cache->path->name.data[cache->path->name.len - 1] == '/') {
+ cache->path->name.len--;
+ }
+
+ if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (i = 2; i < cf->args->nelts; i++) {
+
+ if (ngx_strncmp(value[i].data, "levels=", 7) == 0) {
+
+ p = value[i].data + 7;
+ last = value[i].data + value[i].len;
+
+ for (n = 0; n < 3 && p < last; n++) {
+
+ if (*p > '0' && *p < '3') {
+
+ cache->path->level[n] = *p++ - '0';
+ cache->path->len += cache->path->level[n] + 1;
+
+ if (p == last) {
+ break;
+ }
+
+ if (*p++ == ':' && n < 2 && p != last) {
+ continue;
+ }
+
+ goto invalid_levels;
+ }
+
+ goto invalid_levels;
+ }
+
+ if (cache->path->len < 10 + 3) {
+ continue;
+ }
+
+ invalid_levels:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid \"levels\" \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_strncmp(value[i].data, "keys_zone=", 10) == 0) {
+
+ name.data = value[i].data + 10;
+
+ p = (u_char *) ngx_strchr(name.data, ':');
+
+ if (p) {
+ name.len = p - name.data;
+
+ p++;
+
+ s.len = value[i].data + value[i].len - p;
+ s.data = p;
+
+ size = ngx_parse_size(&s);
+ if (size > 8191) {
+ continue;
+ }
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid keys zone size \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
+
+ s.len = value[i].len - 9;
+ s.data = value[i].data + 9;
+
+ inactive = ngx_parse_time(&s, 1);
+ if (inactive == (time_t) NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid inactive value \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "max_size=", 9) == 0) {
+
+ s.len = value[i].len - 9;
+ s.data = value[i].data + 9;
+
+ max_size = ngx_parse_offset(&s);
+ if (max_size < 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid max_size value \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "loader_files=", 13) == 0) {
+
+ loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13);
+ if (loader_files == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid loader_files value \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "loader_sleep=", 13) == 0) {
+
+ s.len = value[i].len - 13;
+ s.data = value[i].data + 13;
+
+ loader_sleep = ngx_parse_time(&s, 0);
+ if (loader_sleep == (ngx_msec_t) NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid loader_sleep value \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "loader_threshold=", 17) == 0) {
+
+ s.len = value[i].len - 17;
+ s.data = value[i].data + 17;
+
+ loader_threshold = ngx_parse_time(&s, 0);
+ if (loader_threshold == (ngx_msec_t) NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid loader_threshold value \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ continue;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (name.len == 0 || size == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"%V\" must have \"keys_zone\" parameter",
+ &cmd->name);
+ return NGX_CONF_ERROR;
+ }
+
+ cache->path->manager = ngx_http_file_cache_manager;
+ cache->path->loader = ngx_http_file_cache_loader;
+ cache->path->data = cache;
+ cache->path->conf_file = cf->conf_file->file.name.data;
+ cache->path->line = cf->conf_file->line;
+ cache->loader_files = loader_files;
+ cache->loader_sleep = loader_sleep;
+ cache->loader_threshold = loader_threshold;
+
+ if (ngx_add_path(cf, &cache->path) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post);
+ if (cache->shm_zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (cache->shm_zone->data) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate zone \"%V\"", &name);
+ return NGX_CONF_ERROR;
+ }
+
+
+ cache->shm_zone->init = ngx_http_file_cache_init;
+ cache->shm_zone->data = cache;
+
+ cache->inactive = inactive;
+ cache->max_size = max_size;
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *p = conf;
+
+ time_t valid;
+ ngx_str_t *value;
+ ngx_uint_t i, n, status;
+ ngx_array_t **a;
+ ngx_http_cache_valid_t *v;
+ static ngx_uint_t statuses[] = { 200, 301, 302 };
+
+ a = (ngx_array_t **) (p + cmd->offset);
+
+ if (*a == NGX_CONF_UNSET_PTR) {
+ *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t));
+ if (*a == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ value = cf->args->elts;
+ n = cf->args->nelts - 1;
+
+ valid = ngx_parse_time(&value[n], 1);
+ if (valid == (time_t) NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid time value \"%V\"", &value[n]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (n == 1) {
+
+ for (i = 0; i < 3; i++) {
+ v = ngx_array_push(*a);
+ if (v == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ v->status = statuses[i];
+ v->valid = valid;
+ }
+
+ return NGX_CONF_OK;
+ }
+
+ for (i = 1; i < n; i++) {
+
+ if (ngx_strcmp(value[i].data, "any") == 0) {
+
+ status = 0;
+
+ } else {
+
+ status = ngx_atoi(value[i].data, value[i].len);
+ if (status < 100) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid status \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ v = ngx_array_push(*a);
+ if (v == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ v->status = status;
+ v->valid = valid;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_header_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_header_filter_module.c
new file mode 100644
index 00000000000..507dc939c78
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_header_filter_module.c
@@ -0,0 +1,633 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <nginx.h>
+
+
+static ngx_int_t ngx_http_header_filter_init(ngx_conf_t *cf);
+static ngx_int_t ngx_http_header_filter(ngx_http_request_t *r);
+
+
+static ngx_http_module_t ngx_http_header_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_header_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL, /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_header_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_header_filter_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static char ngx_http_server_string[] = "Server: nginx" CRLF;
+static char ngx_http_server_full_string[] = "Server: " NGINX_VER CRLF;
+
+
+static ngx_str_t ngx_http_status_lines[] = {
+
+ ngx_string("200 OK"),
+ ngx_string("201 Created"),
+ ngx_string("202 Accepted"),
+ ngx_null_string, /* "203 Non-Authoritative Information" */
+ ngx_string("204 No Content"),
+ ngx_null_string, /* "205 Reset Content" */
+ ngx_string("206 Partial Content"),
+
+ /* ngx_null_string, */ /* "207 Multi-Status" */
+
+#define NGX_HTTP_LAST_2XX 207
+#define NGX_HTTP_OFF_3XX (NGX_HTTP_LAST_2XX - 200)
+
+ /* ngx_null_string, */ /* "300 Multiple Choices" */
+
+ ngx_string("301 Moved Permanently"),
+ ngx_string("302 Moved Temporarily"),
+ ngx_string("303 See Other"),
+ ngx_string("304 Not Modified"),
+ ngx_null_string, /* "305 Use Proxy" */
+ ngx_null_string, /* "306 unused" */
+ ngx_string("307 Temporary Redirect"),
+
+#define NGX_HTTP_LAST_3XX 308
+#define NGX_HTTP_OFF_4XX (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)
+
+ ngx_string("400 Bad Request"),
+ ngx_string("401 Unauthorized"),
+ ngx_string("402 Payment Required"),
+ ngx_string("403 Forbidden"),
+ ngx_string("404 Not Found"),
+ ngx_string("405 Not Allowed"),
+ ngx_string("406 Not Acceptable"),
+ ngx_null_string, /* "407 Proxy Authentication Required" */
+ ngx_string("408 Request Time-out"),
+ ngx_string("409 Conflict"),
+ ngx_string("410 Gone"),
+ ngx_string("411 Length Required"),
+ ngx_string("412 Precondition Failed"),
+ ngx_string("413 Request Entity Too Large"),
+ ngx_string("414 Request-URI Too Large"),
+ ngx_string("415 Unsupported Media Type"),
+ ngx_string("416 Requested Range Not Satisfiable"),
+
+ /* ngx_null_string, */ /* "417 Expectation Failed" */
+ /* ngx_null_string, */ /* "418 unused" */
+ /* ngx_null_string, */ /* "419 unused" */
+ /* ngx_null_string, */ /* "420 unused" */
+ /* ngx_null_string, */ /* "421 unused" */
+ /* ngx_null_string, */ /* "422 Unprocessable Entity" */
+ /* ngx_null_string, */ /* "423 Locked" */
+ /* ngx_null_string, */ /* "424 Failed Dependency" */
+
+#define NGX_HTTP_LAST_4XX 417
+#define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)
+
+ ngx_string("500 Internal Server Error"),
+ ngx_string("501 Not Implemented"),
+ ngx_string("502 Bad Gateway"),
+ ngx_string("503 Service Temporarily Unavailable"),
+ ngx_string("504 Gateway Time-out"),
+
+ ngx_null_string, /* "505 HTTP Version Not Supported" */
+ ngx_null_string, /* "506 Variant Also Negotiates" */
+ ngx_string("507 Insufficient Storage"),
+ /* ngx_null_string, */ /* "508 unused" */
+ /* ngx_null_string, */ /* "509 unused" */
+ /* ngx_null_string, */ /* "510 Not Extended" */
+
+#define NGX_HTTP_LAST_5XX 508
+
+};
+
+
+ngx_http_header_out_t ngx_http_headers_out[] = {
+ { ngx_string("Server"), offsetof(ngx_http_headers_out_t, server) },
+ { ngx_string("Date"), offsetof(ngx_http_headers_out_t, date) },
+ { ngx_string("Content-Length"),
+ offsetof(ngx_http_headers_out_t, content_length) },
+ { ngx_string("Content-Encoding"),
+ offsetof(ngx_http_headers_out_t, content_encoding) },
+ { ngx_string("Location"), offsetof(ngx_http_headers_out_t, location) },
+ { ngx_string("Last-Modified"),
+ offsetof(ngx_http_headers_out_t, last_modified) },
+ { ngx_string("Accept-Ranges"),
+ offsetof(ngx_http_headers_out_t, accept_ranges) },
+ { ngx_string("Expires"), offsetof(ngx_http_headers_out_t, expires) },
+ { ngx_string("Cache-Control"),
+ offsetof(ngx_http_headers_out_t, cache_control) },
+ { ngx_string("ETag"), offsetof(ngx_http_headers_out_t, etag) },
+
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_int_t
+ngx_http_header_filter(ngx_http_request_t *r)
+{
+ u_char *p;
+ size_t len;
+ ngx_str_t host, *status_line;
+ ngx_buf_t *b;
+ ngx_uint_t status, i, port;
+ ngx_chain_t out;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *header;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_srv_conf_t *cscf;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+ u_char addr[NGX_SOCKADDR_STRLEN];
+
+ if (r->header_sent) {
+ return NGX_OK;
+ }
+
+ r->header_sent = 1;
+
+ if (r != r->main) {
+ return NGX_OK;
+ }
+
+ if (r->http_version < NGX_HTTP_VERSION_10) {
+ return NGX_OK;
+ }
+
+ if (r->method == NGX_HTTP_HEAD) {
+ r->header_only = 1;
+ }
+
+ if (r->headers_out.last_modified_time != -1) {
+ if (r->headers_out.status != NGX_HTTP_OK
+ && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT
+ && r->headers_out.status != NGX_HTTP_NOT_MODIFIED)
+ {
+ r->headers_out.last_modified_time = -1;
+ r->headers_out.last_modified = NULL;
+ }
+ }
+
+ len = sizeof("HTTP/1.x ") - 1 + sizeof(CRLF) - 1
+ /* the end of the header */
+ + sizeof(CRLF) - 1;
+
+ /* status line */
+
+ if (r->headers_out.status_line.len) {
+ len += r->headers_out.status_line.len;
+ status_line = &r->headers_out.status_line;
+#if (NGX_SUPPRESS_WARN)
+ status = 0;
+#endif
+
+ } else {
+
+ status = r->headers_out.status;
+
+ if (status >= NGX_HTTP_OK
+ && status < NGX_HTTP_LAST_2XX)
+ {
+ /* 2XX */
+
+ if (status == NGX_HTTP_NO_CONTENT) {
+ r->header_only = 1;
+ ngx_str_null(&r->headers_out.content_type);
+ r->headers_out.last_modified_time = -1;
+ r->headers_out.last_modified = NULL;
+ r->headers_out.content_length = NULL;
+ r->headers_out.content_length_n = -1;
+ }
+
+ status -= NGX_HTTP_OK;
+ status_line = &ngx_http_status_lines[status];
+ len += ngx_http_status_lines[status].len;
+
+ } else if (status >= NGX_HTTP_MOVED_PERMANENTLY
+ && status < NGX_HTTP_LAST_3XX)
+ {
+ /* 3XX */
+
+ if (status == NGX_HTTP_NOT_MODIFIED) {
+ r->header_only = 1;
+ }
+
+ status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;
+ status_line = &ngx_http_status_lines[status];
+ len += ngx_http_status_lines[status].len;
+
+ } else if (status >= NGX_HTTP_BAD_REQUEST
+ && status < NGX_HTTP_LAST_4XX)
+ {
+ /* 4XX */
+ status = status - NGX_HTTP_BAD_REQUEST
+ + NGX_HTTP_OFF_4XX;
+
+ status_line = &ngx_http_status_lines[status];
+ len += ngx_http_status_lines[status].len;
+
+ } else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR
+ && status < NGX_HTTP_LAST_5XX)
+ {
+ /* 5XX */
+ status = status - NGX_HTTP_INTERNAL_SERVER_ERROR
+ + NGX_HTTP_OFF_5XX;
+
+ status_line = &ngx_http_status_lines[status];
+ len += ngx_http_status_lines[status].len;
+
+ } else {
+ len += NGX_INT_T_LEN + 1 /* SP */;
+ status_line = NULL;
+ }
+
+ if (status_line && status_line->len == 0) {
+ status = r->headers_out.status;
+ len += NGX_INT_T_LEN + 1 /* SP */;
+ status_line = NULL;
+ }
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (r->headers_out.server == NULL) {
+ len += clcf->server_tokens ? sizeof(ngx_http_server_full_string) - 1:
+ sizeof(ngx_http_server_string) - 1;
+ }
+
+ if (r->headers_out.date == NULL) {
+ len += sizeof("Date: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;
+ }
+
+ if (r->headers_out.content_type.len) {
+ len += sizeof("Content-Type: ") - 1
+ + r->headers_out.content_type.len + 2;
+
+ if (r->headers_out.content_type_len == r->headers_out.content_type.len
+ && r->headers_out.charset.len)
+ {
+ len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
+ }
+ }
+
+ if (r->headers_out.content_length == NULL
+ && r->headers_out.content_length_n >= 0)
+ {
+ len += sizeof("Content-Length: ") - 1 + NGX_OFF_T_LEN + 2;
+ }
+
+ if (r->headers_out.last_modified == NULL
+ && r->headers_out.last_modified_time != -1)
+ {
+ len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;
+ }
+
+ c = r->connection;
+
+ if (r->headers_out.location
+ && r->headers_out.location->value.len
+ && r->headers_out.location->value.data[0] == '/')
+ {
+ r->headers_out.location->hash = 0;
+
+ if (clcf->server_name_in_redirect) {
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+ host = cscf->server_name;
+
+ } else if (r->headers_in.server.len) {
+ host = r->headers_in.server;
+
+ } else {
+ host.len = NGX_SOCKADDR_STRLEN;
+ host.data = addr;
+
+ if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+ port = ntohs(sin6->sin6_port);
+ break;
+#endif
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ port = 0;
+ break;
+#endif
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) c->local_sockaddr;
+ port = ntohs(sin->sin_port);
+ break;
+ }
+
+ len += sizeof("Location: https://") - 1
+ + host.len
+ + r->headers_out.location->value.len + 2;
+
+ if (clcf->port_in_redirect) {
+
+#if (NGX_HTTP_SSL)
+ if (c->ssl)
+ port = (port == 443) ? 0 : port;
+ else
+#endif
+ port = (port == 80) ? 0 : port;
+
+ } else {
+ port = 0;
+ }
+
+ if (port) {
+ len += sizeof(":65535") - 1;
+ }
+
+ } else {
+ ngx_str_null(&host);
+ port = 0;
+ }
+
+ if (r->chunked) {
+ len += sizeof("Transfer-Encoding: chunked" CRLF) - 1;
+ }
+
+ if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
+ len += sizeof("Connection: upgrade" CRLF) - 1;
+
+ } else if (r->keepalive) {
+ len += sizeof("Connection: keep-alive" CRLF) - 1;
+
+ /*
+ * MSIE and Opera ignore the "Keep-Alive: timeout=<N>" header.
+ * MSIE keeps the connection alive for about 60-65 seconds.
+ * Opera keeps the connection alive very long.
+ * Mozilla keeps the connection alive for N plus about 1-10 seconds.
+ * Konqueror keeps the connection alive for about N seconds.
+ */
+
+ if (clcf->keepalive_header) {
+ len += sizeof("Keep-Alive: timeout=") - 1 + NGX_TIME_T_LEN + 2;
+ }
+
+ } else {
+ len += sizeof("Connection: close" CRLF) - 1;
+ }
+
+#if (NGX_HTTP_GZIP)
+ if (r->gzip_vary) {
+ if (clcf->gzip_vary) {
+ len += sizeof("Vary: Accept-Encoding" CRLF) - 1;
+
+ } else {
+ r->gzip_vary = 0;
+ }
+ }
+#endif
+
+ part = &r->headers_out.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].hash == 0) {
+ continue;
+ }
+
+ len += header[i].key.len + sizeof(": ") - 1 + header[i].value.len
+ + sizeof(CRLF) - 1;
+ }
+
+ b = ngx_create_temp_buf(r->pool, len);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ /* "HTTP/1.x " */
+ b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1);
+
+ /* status line */
+ if (status_line) {
+ b->last = ngx_copy(b->last, status_line->data, status_line->len);
+
+ } else {
+ b->last = ngx_sprintf(b->last, "%03ui ", status);
+ }
+ *b->last++ = CR; *b->last++ = LF;
+
+ if (r->headers_out.server == NULL) {
+ if (clcf->server_tokens) {
+ p = (u_char *) ngx_http_server_full_string;
+ len = sizeof(ngx_http_server_full_string) - 1;
+
+ } else {
+ p = (u_char *) ngx_http_server_string;
+ len = sizeof(ngx_http_server_string) - 1;
+ }
+
+ b->last = ngx_cpymem(b->last, p, len);
+ }
+
+ if (r->headers_out.date == NULL) {
+ b->last = ngx_cpymem(b->last, "Date: ", sizeof("Date: ") - 1);
+ b->last = ngx_cpymem(b->last, ngx_cached_http_time.data,
+ ngx_cached_http_time.len);
+
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (r->headers_out.content_type.len) {
+ b->last = ngx_cpymem(b->last, "Content-Type: ",
+ sizeof("Content-Type: ") - 1);
+ p = b->last;
+ b->last = ngx_copy(b->last, r->headers_out.content_type.data,
+ r->headers_out.content_type.len);
+
+ if (r->headers_out.content_type_len == r->headers_out.content_type.len
+ && r->headers_out.charset.len)
+ {
+ b->last = ngx_cpymem(b->last, "; charset=",
+ sizeof("; charset=") - 1);
+ b->last = ngx_copy(b->last, r->headers_out.charset.data,
+ r->headers_out.charset.len);
+
+ /* update r->headers_out.content_type for possible logging */
+
+ r->headers_out.content_type.len = b->last - p;
+ r->headers_out.content_type.data = p;
+ }
+
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (r->headers_out.content_length == NULL
+ && r->headers_out.content_length_n >= 0)
+ {
+ b->last = ngx_sprintf(b->last, "Content-Length: %O" CRLF,
+ r->headers_out.content_length_n);
+ }
+
+ if (r->headers_out.last_modified == NULL
+ && r->headers_out.last_modified_time != -1)
+ {
+ b->last = ngx_cpymem(b->last, "Last-Modified: ",
+ sizeof("Last-Modified: ") - 1);
+ b->last = ngx_http_time(b->last, r->headers_out.last_modified_time);
+
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (host.data) {
+
+ p = b->last + sizeof("Location: ") - 1;
+
+ b->last = ngx_cpymem(b->last, "Location: http",
+ sizeof("Location: http") - 1);
+
+#if (NGX_HTTP_SSL)
+ if (c->ssl) {
+ *b->last++ ='s';
+ }
+#endif
+
+ *b->last++ = ':'; *b->last++ = '/'; *b->last++ = '/';
+ b->last = ngx_copy(b->last, host.data, host.len);
+
+ if (port) {
+ b->last = ngx_sprintf(b->last, ":%ui", port);
+ }
+
+ b->last = ngx_copy(b->last, r->headers_out.location->value.data,
+ r->headers_out.location->value.len);
+
+ /* update r->headers_out.location->value for possible logging */
+
+ r->headers_out.location->value.len = b->last - p;
+ r->headers_out.location->value.data = p;
+ ngx_str_set(&r->headers_out.location->key, "Location");
+
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (r->chunked) {
+ b->last = ngx_cpymem(b->last, "Transfer-Encoding: chunked" CRLF,
+ sizeof("Transfer-Encoding: chunked" CRLF) - 1);
+ }
+
+ if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
+ b->last = ngx_cpymem(b->last, "Connection: upgrade" CRLF,
+ sizeof("Connection: upgrade" CRLF) - 1);
+
+ } else if (r->keepalive) {
+ b->last = ngx_cpymem(b->last, "Connection: keep-alive" CRLF,
+ sizeof("Connection: keep-alive" CRLF) - 1);
+
+ if (clcf->keepalive_header) {
+ b->last = ngx_sprintf(b->last, "Keep-Alive: timeout=%T" CRLF,
+ clcf->keepalive_header);
+ }
+
+ } else {
+ b->last = ngx_cpymem(b->last, "Connection: close" CRLF,
+ sizeof("Connection: close" CRLF) - 1);
+ }
+
+#if (NGX_HTTP_GZIP)
+ if (r->gzip_vary) {
+ b->last = ngx_cpymem(b->last, "Vary: Accept-Encoding" CRLF,
+ sizeof("Vary: Accept-Encoding" CRLF) - 1);
+ }
+#endif
+
+ part = &r->headers_out.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].hash == 0) {
+ continue;
+ }
+
+ b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
+ *b->last++ = ':'; *b->last++ = ' ';
+
+ b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "%*s", (size_t) (b->last - b->pos), b->pos);
+
+ /* the end of HTTP header */
+ *b->last++ = CR; *b->last++ = LF;
+
+ r->header_size = b->last - b->pos;
+
+ if (r->header_only) {
+ b->last_buf = 1;
+ }
+
+ out.buf = b;
+ out.next = NULL;
+
+ return ngx_http_write_filter(r, &out);
+}
+
+
+static ngx_int_t
+ngx_http_header_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_top_header_filter = ngx_http_header_filter;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_parse.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_parse.c
new file mode 100644
index 00000000000..b60f41bb6a3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_parse.c
@@ -0,0 +1,2359 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static uint32_t usual[] = {
+ 0xffffdbfe, /* 1111 1111 1111 1111 1101 1011 1111 1110 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x7fff37d6, /* 0111 1111 1111 1111 0011 0111 1101 0110 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+#if (NGX_WIN32)
+ 0xefffffff, /* 1110 1111 1111 1111 1111 1111 1111 1111 */
+#else
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+#endif
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+};
+
+
+#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
+
+#define ngx_str3_cmp(m, c0, c1, c2, c3) \
+ *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
+
+#define ngx_str3Ocmp(m, c0, c1, c2, c3) \
+ *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
+
+#define ngx_str4cmp(m, c0, c1, c2, c3) \
+ *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)
+
+#define ngx_str5cmp(m, c0, c1, c2, c3, c4) \
+ *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
+ && m[4] == c4
+
+#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5) \
+ *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
+ && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4)
+
+#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
+ *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
+ && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
+
+#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
+ *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
+ && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)
+
+#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \
+ *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \
+ && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) \
+ && m[8] == c8
+
+#else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */
+
+#define ngx_str3_cmp(m, c0, c1, c2, c3) \
+ m[0] == c0 && m[1] == c1 && m[2] == c2
+
+#define ngx_str3Ocmp(m, c0, c1, c2, c3) \
+ m[0] == c0 && m[2] == c2 && m[3] == c3
+
+#define ngx_str4cmp(m, c0, c1, c2, c3) \
+ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3
+
+#define ngx_str5cmp(m, c0, c1, c2, c3, c4) \
+ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4
+
+#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5) \
+ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
+ && m[4] == c4 && m[5] == c5
+
+#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
+ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
+ && m[4] == c4 && m[5] == c5 && m[6] == c6
+
+#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \
+ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
+ && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7
+
+#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \
+ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \
+ && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8
+
+#endif
+
+
+/* gcc, icc, msvc and others compile these switches as an jump table */
+
+ngx_int_t
+ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
+{
+ u_char c, ch, *p, *m;
+ enum {
+ sw_start = 0,
+ sw_method,
+ sw_spaces_before_uri,
+ sw_schema,
+ sw_schema_slash,
+ sw_schema_slash_slash,
+ sw_host_start,
+ sw_host,
+ sw_host_end,
+ sw_host_ip_literal,
+ sw_port,
+ sw_host_http_09,
+ sw_after_slash_in_uri,
+ sw_check_uri,
+ sw_check_uri_http_09,
+ sw_uri,
+ sw_http_09,
+ sw_http_H,
+ sw_http_HT,
+ sw_http_HTT,
+ sw_http_HTTP,
+ sw_first_major_digit,
+ sw_major_digit,
+ sw_first_minor_digit,
+ sw_minor_digit,
+ sw_spaces_after_digit,
+ sw_almost_done
+ } state;
+
+ state = r->state;
+
+ for (p = b->pos; p < b->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* HTTP methods: GET, HEAD, POST */
+ case sw_start:
+ r->request_start = p;
+
+ if (ch == CR || ch == LF) {
+ break;
+ }
+
+ if ((ch < 'A' || ch > 'Z') && ch != '_') {
+ return NGX_HTTP_PARSE_INVALID_METHOD;
+ }
+
+ state = sw_method;
+ break;
+
+ case sw_method:
+ if (ch == ' ') {
+ r->method_end = p - 1;
+ m = r->request_start;
+
+ switch (p - m) {
+
+ case 3:
+ if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {
+ r->method = NGX_HTTP_GET;
+ break;
+ }
+
+ if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {
+ r->method = NGX_HTTP_PUT;
+ break;
+ }
+
+ break;
+
+ case 4:
+ if (m[1] == 'O') {
+
+ if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {
+ r->method = NGX_HTTP_POST;
+ break;
+ }
+
+ if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {
+ r->method = NGX_HTTP_COPY;
+ break;
+ }
+
+ if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {
+ r->method = NGX_HTTP_MOVE;
+ break;
+ }
+
+ if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {
+ r->method = NGX_HTTP_LOCK;
+ break;
+ }
+
+ } else {
+
+ if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {
+ r->method = NGX_HTTP_HEAD;
+ break;
+ }
+ }
+
+ break;
+
+ case 5:
+ if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {
+ r->method = NGX_HTTP_MKCOL;
+ break;
+ }
+
+ if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {
+ r->method = NGX_HTTP_PATCH;
+ break;
+ }
+
+ if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {
+ r->method = NGX_HTTP_TRACE;
+ break;
+ }
+
+ break;
+
+ case 6:
+ if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {
+ r->method = NGX_HTTP_DELETE;
+ break;
+ }
+
+ if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {
+ r->method = NGX_HTTP_UNLOCK;
+ break;
+ }
+
+ break;
+
+ case 7:
+ if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))
+ {
+ r->method = NGX_HTTP_OPTIONS;
+ }
+
+ break;
+
+ case 8:
+ if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))
+ {
+ r->method = NGX_HTTP_PROPFIND;
+ }
+
+ break;
+
+ case 9:
+ if (ngx_str9cmp(m,
+ 'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))
+ {
+ r->method = NGX_HTTP_PROPPATCH;
+ }
+
+ break;
+ }
+
+ state = sw_spaces_before_uri;
+ break;
+ }
+
+ if ((ch < 'A' || ch > 'Z') && ch != '_') {
+ return NGX_HTTP_PARSE_INVALID_METHOD;
+ }
+
+ break;
+
+ /* space* before URI */
+ case sw_spaces_before_uri:
+
+ if (ch == '/') {
+ r->uri_start = p;
+ state = sw_after_slash_in_uri;
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ r->schema_start = p;
+ state = sw_schema;
+ break;
+ }
+
+ switch (ch) {
+ case ' ':
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_schema:
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ switch (ch) {
+ case ':':
+ r->schema_end = p;
+ state = sw_schema_slash;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_schema_slash:
+ switch (ch) {
+ case '/':
+ state = sw_schema_slash_slash;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_schema_slash_slash:
+ switch (ch) {
+ case '/':
+ state = sw_host_start;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_host_start:
+
+ r->host_start = p;
+
+ if (ch == '[') {
+ state = sw_host_ip_literal;
+ break;
+ }
+
+ state = sw_host;
+
+ /* fall through */
+
+ case sw_host:
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
+ break;
+ }
+
+ /* fall through */
+
+ case sw_host_end:
+
+ r->host_end = p;
+
+ switch (ch) {
+ case ':':
+ state = sw_port;
+ break;
+ case '/':
+ r->uri_start = p;
+ state = sw_after_slash_in_uri;
+ break;
+ case ' ':
+ /*
+ * use single "/" from request line to preserve pointers,
+ * if request line will be copied to large client buffer
+ */
+ r->uri_start = r->schema_end + 1;
+ r->uri_end = r->schema_end + 2;
+ state = sw_host_http_09;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_host_ip_literal:
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ switch (ch) {
+ case ':':
+ break;
+ case ']':
+ state = sw_host_end;
+ break;
+ case '-':
+ case '.':
+ case '_':
+ case '~':
+ /* unreserved */
+ break;
+ case '!':
+ case '$':
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case ';':
+ case '=':
+ /* sub-delims */
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_port:
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ switch (ch) {
+ case '/':
+ r->port_end = p;
+ r->uri_start = p;
+ state = sw_after_slash_in_uri;
+ break;
+ case ' ':
+ r->port_end = p;
+ /*
+ * use single "/" from request line to preserve pointers,
+ * if request line will be copied to large client buffer
+ */
+ r->uri_start = r->schema_end + 1;
+ r->uri_end = r->schema_end + 2;
+ state = sw_host_http_09;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ /* space+ after "http://host[:port] " */
+ case sw_host_http_09:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ r->http_minor = 9;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->http_minor = 9;
+ goto done;
+ case 'H':
+ r->http_protocol.data = p;
+ state = sw_http_H;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+
+ /* check "/.", "//", "%", and "\" (Win32) in URI */
+ case sw_after_slash_in_uri:
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ state = sw_check_uri;
+ break;
+ }
+
+ switch (ch) {
+ case ' ':
+ r->uri_end = p;
+ state = sw_check_uri_http_09;
+ break;
+ case CR:
+ r->uri_end = p;
+ r->http_minor = 9;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->uri_end = p;
+ r->http_minor = 9;
+ goto done;
+ case '.':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+ case '%':
+ r->quoted_uri = 1;
+ state = sw_uri;
+ break;
+ case '/':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+#if (NGX_WIN32)
+ case '\\':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+#endif
+ case '?':
+ r->args_start = p + 1;
+ state = sw_uri;
+ break;
+ case '#':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+ case '+':
+ r->plus_in_uri = 1;
+ break;
+ case '\0':
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ default:
+ state = sw_check_uri;
+ break;
+ }
+ break;
+
+ /* check "/", "%" and "\" (Win32) in URI */
+ case sw_check_uri:
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ break;
+ }
+
+ switch (ch) {
+ case '/':
+#if (NGX_WIN32)
+ if (r->uri_ext == p) {
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+ }
+#endif
+ r->uri_ext = NULL;
+ state = sw_after_slash_in_uri;
+ break;
+ case '.':
+ r->uri_ext = p + 1;
+ break;
+ case ' ':
+ r->uri_end = p;
+ state = sw_check_uri_http_09;
+ break;
+ case CR:
+ r->uri_end = p;
+ r->http_minor = 9;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->uri_end = p;
+ r->http_minor = 9;
+ goto done;
+#if (NGX_WIN32)
+ case '\\':
+ r->complex_uri = 1;
+ state = sw_after_slash_in_uri;
+ break;
+#endif
+ case '%':
+ r->quoted_uri = 1;
+ state = sw_uri;
+ break;
+ case '?':
+ r->args_start = p + 1;
+ state = sw_uri;
+ break;
+ case '#':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+ case '+':
+ r->plus_in_uri = 1;
+ break;
+ case '\0':
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ /* space+ after URI */
+ case sw_check_uri_http_09:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ r->http_minor = 9;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->http_minor = 9;
+ goto done;
+ case 'H':
+ r->http_protocol.data = p;
+ state = sw_http_H;
+ break;
+ default:
+ r->space_in_uri = 1;
+ state = sw_check_uri;
+ p--;
+ break;
+ }
+ break;
+
+
+ /* URI */
+ case sw_uri:
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ break;
+ }
+
+ switch (ch) {
+ case ' ':
+ r->uri_end = p;
+ state = sw_http_09;
+ break;
+ case CR:
+ r->uri_end = p;
+ r->http_minor = 9;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->uri_end = p;
+ r->http_minor = 9;
+ goto done;
+ case '#':
+ r->complex_uri = 1;
+ break;
+ case '\0':
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ /* space+ after URI */
+ case sw_http_09:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ r->http_minor = 9;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->http_minor = 9;
+ goto done;
+ case 'H':
+ r->http_protocol.data = p;
+ state = sw_http_H;
+ break;
+ default:
+ r->space_in_uri = 1;
+ state = sw_uri;
+ p--;
+ break;
+ }
+ break;
+
+ case sw_http_H:
+ switch (ch) {
+ case 'T':
+ state = sw_http_HT;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_http_HT:
+ switch (ch) {
+ case 'T':
+ state = sw_http_HTT;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_http_HTT:
+ switch (ch) {
+ case 'P':
+ state = sw_http_HTTP;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ case sw_http_HTTP:
+ switch (ch) {
+ case '/':
+ state = sw_first_major_digit;
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ /* first digit of major HTTP version */
+ case sw_first_major_digit:
+ if (ch < '1' || ch > '9') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ r->http_major = ch - '0';
+ state = sw_major_digit;
+ break;
+
+ /* major HTTP version or dot */
+ case sw_major_digit:
+ if (ch == '.') {
+ state = sw_first_minor_digit;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ r->http_major = r->http_major * 10 + ch - '0';
+ break;
+
+ /* first digit of minor HTTP version */
+ case sw_first_minor_digit:
+ if (ch < '0' || ch > '9') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ r->http_minor = ch - '0';
+ state = sw_minor_digit;
+ break;
+
+ /* minor HTTP version or end of request line */
+ case sw_minor_digit:
+ if (ch == CR) {
+ state = sw_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ goto done;
+ }
+
+ if (ch == ' ') {
+ state = sw_spaces_after_digit;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ r->http_minor = r->http_minor * 10 + ch - '0';
+ break;
+
+ case sw_spaces_after_digit:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ break;
+
+ /* end of request line */
+ case sw_almost_done:
+ r->request_end = p - 1;
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ }
+ }
+
+ b->pos = p;
+ r->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ b->pos = p + 1;
+
+ if (r->request_end == NULL) {
+ r->request_end = p;
+ }
+
+ r->http_version = r->http_major * 1000 + r->http_minor;
+ r->state = sw_start;
+
+ if (r->http_version == 9 && r->method != NGX_HTTP_GET) {
+ return NGX_HTTP_PARSE_INVALID_09_METHOD;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,
+ ngx_uint_t allow_underscores)
+{
+ u_char c, ch, *p;
+ ngx_uint_t hash, i;
+ enum {
+ sw_start = 0,
+ sw_name,
+ sw_space_before_value,
+ sw_value,
+ sw_space_after_value,
+ sw_ignore_line,
+ sw_almost_done,
+ sw_header_almost_done
+ } state;
+
+ /* the last '\0' is not needed because string is zero terminated */
+
+ static u_char lowcase[] =
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
+ "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
+ "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+ state = r->state;
+ hash = r->header_hash;
+ i = r->lowcase_index;
+
+ for (p = b->pos; p < b->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* first char */
+ case sw_start:
+ r->header_name_start = p;
+ r->invalid_header = 0;
+
+ switch (ch) {
+ case CR:
+ r->header_end = p;
+ state = sw_header_almost_done;
+ break;
+ case LF:
+ r->header_end = p;
+ goto header_done;
+ default:
+ state = sw_name;
+
+ c = lowcase[ch];
+
+ if (c) {
+ hash = ngx_hash(0, c);
+ r->lowcase_header[0] = c;
+ i = 1;
+ break;
+ }
+
+ if (ch == '_') {
+ if (allow_underscores) {
+ hash = ngx_hash(0, ch);
+ r->lowcase_header[0] = ch;
+ i = 1;
+
+ } else {
+ r->invalid_header = 1;
+ }
+
+ break;
+ }
+
+ if (ch == '\0') {
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ r->invalid_header = 1;
+
+ break;
+
+ }
+ break;
+
+ /* header name */
+ case sw_name:
+ c = lowcase[ch];
+
+ if (c) {
+ hash = ngx_hash(hash, c);
+ r->lowcase_header[i++] = c;
+ i &= (NGX_HTTP_LC_HEADER_LEN - 1);
+ break;
+ }
+
+ if (ch == '_') {
+ if (allow_underscores) {
+ hash = ngx_hash(hash, ch);
+ r->lowcase_header[i++] = ch;
+ i &= (NGX_HTTP_LC_HEADER_LEN - 1);
+
+ } else {
+ r->invalid_header = 1;
+ }
+
+ break;
+ }
+
+ if (ch == ':') {
+ r->header_name_end = p;
+ state = sw_space_before_value;
+ break;
+ }
+
+ if (ch == CR) {
+ r->header_name_end = p;
+ r->header_start = p;
+ r->header_end = p;
+ state = sw_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ r->header_name_end = p;
+ r->header_start = p;
+ r->header_end = p;
+ goto done;
+ }
+
+ /* IIS may send the duplicate "HTTP/1.1 ..." lines */
+ if (ch == '/'
+ && r->upstream
+ && p - r->header_name_start == 4
+ && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0)
+ {
+ state = sw_ignore_line;
+ break;
+ }
+
+ if (ch == '\0') {
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ r->invalid_header = 1;
+
+ break;
+
+ /* space* before header value */
+ case sw_space_before_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ r->header_start = p;
+ r->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->header_start = p;
+ r->header_end = p;
+ goto done;
+ case '\0':
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ default:
+ r->header_start = p;
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* header value */
+ case sw_value:
+ switch (ch) {
+ case ' ':
+ r->header_end = p;
+ state = sw_space_after_value;
+ break;
+ case CR:
+ r->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ r->header_end = p;
+ goto done;
+ case '\0':
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+ break;
+
+ /* space* before end of header line */
+ case sw_space_after_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ case '\0':
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ default:
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* ignore header line */
+ case sw_ignore_line:
+ switch (ch) {
+ case LF:
+ state = sw_start;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ /* end of header line */
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ case CR:
+ break;
+ default:
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+ break;
+
+ /* end of header */
+ case sw_header_almost_done:
+ switch (ch) {
+ case LF:
+ goto header_done;
+ default:
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+ }
+ }
+
+ b->pos = p;
+ r->state = state;
+ r->header_hash = hash;
+ r->lowcase_index = i;
+
+ return NGX_AGAIN;
+
+done:
+
+ b->pos = p + 1;
+ r->state = sw_start;
+ r->header_hash = hash;
+ r->lowcase_index = i;
+
+ return NGX_OK;
+
+header_done:
+
+ b->pos = p + 1;
+ r->state = sw_start;
+
+ return NGX_HTTP_PARSE_HEADER_DONE;
+}
+
+
+ngx_int_t
+ngx_http_parse_uri(ngx_http_request_t *r)
+{
+ u_char *p, ch;
+ enum {
+ sw_start = 0,
+ sw_after_slash_in_uri,
+ sw_check_uri,
+ sw_uri
+ } state;
+
+ state = sw_start;
+
+ for (p = r->uri_start; p != r->uri_end; p++) {
+
+ ch = *p;
+
+ switch (state) {
+
+ case sw_start:
+
+ if (ch != '/') {
+ return NGX_ERROR;
+ }
+
+ state = sw_after_slash_in_uri;
+ break;
+
+ /* check "/.", "//", "%", and "\" (Win32) in URI */
+ case sw_after_slash_in_uri:
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ state = sw_check_uri;
+ break;
+ }
+
+ switch (ch) {
+ case ' ':
+ r->space_in_uri = 1;
+ state = sw_check_uri;
+ break;
+ case '.':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+ case '%':
+ r->quoted_uri = 1;
+ state = sw_uri;
+ break;
+ case '/':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+#if (NGX_WIN32)
+ case '\\':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+#endif
+ case '?':
+ r->args_start = p + 1;
+ state = sw_uri;
+ break;
+ case '#':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+ case '+':
+ r->plus_in_uri = 1;
+ break;
+ default:
+ state = sw_check_uri;
+ break;
+ }
+ break;
+
+ /* check "/", "%" and "\" (Win32) in URI */
+ case sw_check_uri:
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ break;
+ }
+
+ switch (ch) {
+ case '/':
+#if (NGX_WIN32)
+ if (r->uri_ext == p) {
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+ }
+#endif
+ r->uri_ext = NULL;
+ state = sw_after_slash_in_uri;
+ break;
+ case '.':
+ r->uri_ext = p + 1;
+ break;
+ case ' ':
+ r->space_in_uri = 1;
+ break;
+#if (NGX_WIN32)
+ case '\\':
+ r->complex_uri = 1;
+ state = sw_after_slash_in_uri;
+ break;
+#endif
+ case '%':
+ r->quoted_uri = 1;
+ state = sw_uri;
+ break;
+ case '?':
+ r->args_start = p + 1;
+ state = sw_uri;
+ break;
+ case '#':
+ r->complex_uri = 1;
+ state = sw_uri;
+ break;
+ case '+':
+ r->plus_in_uri = 1;
+ break;
+ }
+ break;
+
+ /* URI */
+ case sw_uri:
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ break;
+ }
+
+ switch (ch) {
+ case ' ':
+ r->space_in_uri = 1;
+ break;
+ case '#':
+ r->complex_uri = 1;
+ break;
+ }
+ break;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)
+{
+ u_char c, ch, decoded, *p, *u;
+ enum {
+ sw_usual = 0,
+ sw_slash,
+ sw_dot,
+ sw_dot_dot,
+ sw_quoted,
+ sw_quoted_second
+ } state, quoted_state;
+
+#if (NGX_SUPPRESS_WARN)
+ decoded = '\0';
+ quoted_state = sw_usual;
+#endif
+
+ state = sw_usual;
+ p = r->uri_start;
+ u = r->uri.data;
+ r->uri_ext = NULL;
+ r->args_start = NULL;
+
+ ch = *p++;
+
+ while (p <= r->uri_end) {
+
+ /*
+ * we use "ch = *p++" inside the cycle, but this operation is safe,
+ * because after the URI there is always at least one character:
+ * the line feed
+ */
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "s:%d in:'%Xd:%c'", state, ch, ch);
+
+ switch (state) {
+
+ case sw_usual:
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ *u++ = ch;
+ ch = *p++;
+ break;
+ }
+
+ switch (ch) {
+#if (NGX_WIN32)
+ case '\\':
+ if (u - 2 >= r->uri.data
+ && *(u - 1) == '.' && *(u - 2) != '.')
+ {
+ u--;
+ }
+
+ r->uri_ext = NULL;
+
+ if (p == r->uri_start + r->uri.len) {
+
+ /*
+ * we omit the last "\" to cause redirect because
+ * the browsers do not treat "\" as "/" in relative URL path
+ */
+
+ break;
+ }
+
+ state = sw_slash;
+ *u++ = '/';
+ break;
+#endif
+ case '/':
+#if (NGX_WIN32)
+ if (u - 2 >= r->uri.data
+ && *(u - 1) == '.' && *(u - 2) != '.')
+ {
+ u--;
+ }
+#endif
+ r->uri_ext = NULL;
+ state = sw_slash;
+ *u++ = ch;
+ break;
+ case '%':
+ quoted_state = state;
+ state = sw_quoted;
+ break;
+ case '?':
+ r->args_start = p;
+ goto args;
+ case '#':
+ goto done;
+ case '.':
+ r->uri_ext = u + 1;
+ *u++ = ch;
+ break;
+ case '+':
+ r->plus_in_uri = 1;
+ /* fall through */
+ default:
+ *u++ = ch;
+ break;
+ }
+
+ ch = *p++;
+ break;
+
+ case sw_slash:
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ state = sw_usual;
+ *u++ = ch;
+ ch = *p++;
+ break;
+ }
+
+ switch (ch) {
+#if (NGX_WIN32)
+ case '\\':
+ break;
+#endif
+ case '/':
+ if (!merge_slashes) {
+ *u++ = ch;
+ }
+ break;
+ case '.':
+ state = sw_dot;
+ *u++ = ch;
+ break;
+ case '%':
+ quoted_state = state;
+ state = sw_quoted;
+ break;
+ case '?':
+ r->args_start = p;
+ goto args;
+ case '#':
+ goto done;
+ case '+':
+ r->plus_in_uri = 1;
+ default:
+ state = sw_usual;
+ *u++ = ch;
+ break;
+ }
+
+ ch = *p++;
+ break;
+
+ case sw_dot:
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ state = sw_usual;
+ *u++ = ch;
+ ch = *p++;
+ break;
+ }
+
+ switch (ch) {
+#if (NGX_WIN32)
+ case '\\':
+#endif
+ case '/':
+ state = sw_slash;
+ u--;
+ break;
+ case '.':
+ state = sw_dot_dot;
+ *u++ = ch;
+ break;
+ case '%':
+ quoted_state = state;
+ state = sw_quoted;
+ break;
+ case '?':
+ r->args_start = p;
+ goto args;
+ case '#':
+ goto done;
+ case '+':
+ r->plus_in_uri = 1;
+ default:
+ state = sw_usual;
+ *u++ = ch;
+ break;
+ }
+
+ ch = *p++;
+ break;
+
+ case sw_dot_dot:
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ state = sw_usual;
+ *u++ = ch;
+ ch = *p++;
+ break;
+ }
+
+ switch (ch) {
+#if (NGX_WIN32)
+ case '\\':
+#endif
+ case '/':
+ state = sw_slash;
+ u -= 5;
+ for ( ;; ) {
+ if (u < r->uri.data) {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ if (*u == '/') {
+ u++;
+ break;
+ }
+ u--;
+ }
+ break;
+ case '%':
+ quoted_state = state;
+ state = sw_quoted;
+ break;
+ case '?':
+ r->args_start = p;
+ goto args;
+ case '#':
+ goto done;
+ case '+':
+ r->plus_in_uri = 1;
+ default:
+ state = sw_usual;
+ *u++ = ch;
+ break;
+ }
+
+ ch = *p++;
+ break;
+
+ case sw_quoted:
+ r->quoted_uri = 1;
+
+ if (ch >= '0' && ch <= '9') {
+ decoded = (u_char) (ch - '0');
+ state = sw_quoted_second;
+ ch = *p++;
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'f') {
+ decoded = (u_char) (c - 'a' + 10);
+ state = sw_quoted_second;
+ ch = *p++;
+ break;
+ }
+
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+
+ case sw_quoted_second:
+ if (ch >= '0' && ch <= '9') {
+ ch = (u_char) ((decoded << 4) + ch - '0');
+
+ if (ch == '%' || ch == '#') {
+ state = sw_usual;
+ *u++ = ch;
+ ch = *p++;
+ break;
+
+ } else if (ch == '\0') {
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+
+ state = quoted_state;
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'f') {
+ ch = (u_char) ((decoded << 4) + c - 'a' + 10);
+
+ if (ch == '?') {
+ state = sw_usual;
+ *u++ = ch;
+ ch = *p++;
+ break;
+
+ } else if (ch == '+') {
+ r->plus_in_uri = 1;
+ }
+
+ state = quoted_state;
+ break;
+ }
+
+ return NGX_HTTP_PARSE_INVALID_REQUEST;
+ }
+ }
+
+done:
+
+ r->uri.len = u - r->uri.data;
+
+ if (r->uri_ext) {
+ r->exten.len = u - r->uri_ext;
+ r->exten.data = r->uri_ext;
+ }
+
+ r->uri_ext = NULL;
+
+ return NGX_OK;
+
+args:
+
+ while (p < r->uri_end) {
+ if (*p++ != '#') {
+ continue;
+ }
+
+ r->args.len = p - 1 - r->args_start;
+ r->args.data = r->args_start;
+ r->args_start = NULL;
+
+ break;
+ }
+
+ r->uri.len = u - r->uri.data;
+
+ if (r->uri_ext) {
+ r->exten.len = u - r->uri_ext;
+ r->exten.data = r->uri_ext;
+ }
+
+ r->uri_ext = NULL;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,
+ ngx_http_status_t *status)
+{
+ u_char ch;
+ u_char *p;
+ enum {
+ sw_start = 0,
+ sw_H,
+ sw_HT,
+ sw_HTT,
+ sw_HTTP,
+ sw_first_major_digit,
+ sw_major_digit,
+ sw_first_minor_digit,
+ sw_minor_digit,
+ sw_status,
+ sw_space_after_status,
+ sw_status_text,
+ sw_almost_done
+ } state;
+
+ state = r->state;
+
+ for (p = b->pos; p < b->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* "HTTP/" */
+ case sw_start:
+ switch (ch) {
+ case 'H':
+ state = sw_H;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_H:
+ switch (ch) {
+ case 'T':
+ state = sw_HT;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HT:
+ switch (ch) {
+ case 'T':
+ state = sw_HTT;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HTT:
+ switch (ch) {
+ case 'P':
+ state = sw_HTTP;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ case sw_HTTP:
+ switch (ch) {
+ case '/':
+ state = sw_first_major_digit;
+ break;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ /* the first digit of major HTTP version */
+ case sw_first_major_digit:
+ if (ch < '1' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ r->http_major = ch - '0';
+ state = sw_major_digit;
+ break;
+
+ /* the major HTTP version or dot */
+ case sw_major_digit:
+ if (ch == '.') {
+ state = sw_first_minor_digit;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ r->http_major = r->http_major * 10 + ch - '0';
+ break;
+
+ /* the first digit of minor HTTP version */
+ case sw_first_minor_digit:
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ r->http_minor = ch - '0';
+ state = sw_minor_digit;
+ break;
+
+ /* the minor HTTP version or the end of the request line */
+ case sw_minor_digit:
+ if (ch == ' ') {
+ state = sw_status;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ r->http_minor = r->http_minor * 10 + ch - '0';
+ break;
+
+ /* HTTP status code */
+ case sw_status:
+ if (ch == ' ') {
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ return NGX_ERROR;
+ }
+
+ status->code = status->code * 10 + ch - '0';
+
+ if (++status->count == 3) {
+ state = sw_space_after_status;
+ status->start = p - 2;
+ }
+
+ break;
+
+ /* space or end of line */
+ case sw_space_after_status:
+ switch (ch) {
+ case ' ':
+ state = sw_status_text;
+ break;
+ case '.': /* IIS may send 403.1, 403.2, etc */
+ state = sw_status_text;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+ break;
+
+ /* any text until end of line */
+ case sw_status_text:
+ switch (ch) {
+ case CR:
+ state = sw_almost_done;
+
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+
+ /* end of status line */
+ case sw_almost_done:
+ status->end = p - 1;
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ b->pos = p;
+ r->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ b->pos = p + 1;
+
+ if (status->end == NULL) {
+ status->end = p;
+ }
+
+ status->http_version = r->http_major * 1000 + r->http_minor;
+ r->state = sw_start;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,
+ ngx_str_t *args, ngx_uint_t *flags)
+{
+ u_char ch, *p, *src, *dst;
+ size_t len;
+ ngx_uint_t quoted;
+
+ len = uri->len;
+ p = uri->data;
+ quoted = 0;
+
+ if (len == 0 || p[0] == '?') {
+ goto unsafe;
+ }
+
+ if (p[0] == '.' && len > 1 && p[1] == '.'
+ && (len == 2 || ngx_path_separator(p[2])))
+ {
+ goto unsafe;
+ }
+
+ for ( /* void */ ; len; len--) {
+
+ ch = *p++;
+
+ if (ch == '%') {
+ quoted = 1;
+ continue;
+ }
+
+ if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
+ continue;
+ }
+
+ if (ch == '?') {
+ args->len = len - 1;
+ args->data = p;
+ uri->len -= len;
+
+ break;
+ }
+
+ if (ch == '\0') {
+ goto unsafe;
+ }
+
+ if (ngx_path_separator(ch) && len > 2) {
+
+ /* detect "/../" and "/.." */
+
+ if (p[0] == '.' && p[1] == '.'
+ && (len == 3 || ngx_path_separator(p[2])))
+ {
+ goto unsafe;
+ }
+ }
+ }
+
+ if (quoted) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "escaped URI: \"%V\"", uri);
+
+ src = uri->data;
+
+ dst = ngx_pnalloc(r->pool, uri->len);
+ if (dst == NULL) {
+ return NGX_ERROR;
+ }
+
+ uri->data = dst;
+
+ ngx_unescape_uri(&dst, &src, uri->len, 0);
+
+ uri->len = dst - uri->data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "unescaped URI: \"%V\"", uri);
+
+ len = uri->len;
+ p = uri->data;
+
+ if (p[0] == '.' && len > 1 && p[1] == '.'
+ && (len == 2 || ngx_path_separator(p[2])))
+ {
+ goto unsafe;
+ }
+
+ for ( /* void */ ; len; len--) {
+
+ ch = *p++;
+
+ if (ch == '\0') {
+ goto unsafe;
+ }
+
+ if (ngx_path_separator(ch) && len > 2) {
+
+ /* detect "/../" and "/.." */
+
+ if (p[0] == '.' && p[1] == '.'
+ && (len == 3 || ngx_path_separator(p[2])))
+ {
+ goto unsafe;
+ }
+ }
+ }
+ }
+
+ return NGX_OK;
+
+unsafe:
+
+ if (*flags & NGX_HTTP_LOG_UNSAFE) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "unsafe URI \"%V\" was detected", uri);
+ }
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name,
+ ngx_str_t *value)
+{
+ ngx_uint_t i;
+ u_char *start, *last, *end, ch;
+ ngx_table_elt_t **h;
+
+ h = headers->elts;
+
+ for (i = 0; i < headers->nelts; i++) {
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
+ "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);
+
+ if (name->len > h[i]->value.len) {
+ continue;
+ }
+
+ start = h[i]->value.data;
+ end = h[i]->value.data + h[i]->value.len;
+
+ while (start < end) {
+
+ if (ngx_strncasecmp(start, name->data, name->len) != 0) {
+ goto skip;
+ }
+
+ for (start += name->len; start < end && *start == ' '; start++) {
+ /* void */
+ }
+
+ if (value == NULL) {
+ if (start == end || *start == ',') {
+ return i;
+ }
+
+ goto skip;
+ }
+
+ if (start == end || *start++ != '=') {
+ /* the invalid header value */
+ goto skip;
+ }
+
+ while (start < end && *start == ' ') { start++; }
+
+ for (last = start; last < end && *last != ';'; last++) {
+ /* void */
+ }
+
+ value->len = last - start;
+ value->data = start;
+
+ return i;
+
+ skip:
+
+ while (start < end) {
+ ch = *start++;
+ if (ch == ';' || ch == ',') {
+ break;
+ }
+ }
+
+ while (start < end && *start == ' ') { start++; }
+ }
+ }
+
+ return NGX_DECLINED;
+}
+
+
+ngx_int_t
+ngx_http_parse_set_cookie_lines(ngx_array_t *headers, ngx_str_t *name,
+ ngx_str_t *value)
+{
+ ngx_uint_t i;
+ u_char *start, *last, *end;
+ ngx_table_elt_t **h;
+
+ h = headers->elts;
+
+ for (i = 0; i < headers->nelts; i++) {
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, headers->pool->log, 0,
+ "parse header: \"%V: %V\"", &h[i]->key, &h[i]->value);
+
+ if (name->len >= h[i]->value.len) {
+ continue;
+ }
+
+ start = h[i]->value.data;
+ end = h[i]->value.data + h[i]->value.len;
+
+ if (ngx_strncasecmp(start, name->data, name->len) != 0) {
+ continue;
+ }
+
+ for (start += name->len; start < end && *start == ' '; start++) {
+ /* void */
+ }
+
+ if (start == end || *start++ != '=') {
+ /* the invalid header value */
+ continue;
+ }
+
+ while (start < end && *start == ' ') { start++; }
+
+ for (last = start; last < end && *last != ';'; last++) {
+ /* void */
+ }
+
+ value->len = last - start;
+ value->data = start;
+
+ return i;
+ }
+
+ return NGX_DECLINED;
+}
+
+
+ngx_int_t
+ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)
+{
+ u_char *p, *last;
+
+ if (r->args.len == 0) {
+ return NGX_DECLINED;
+ }
+
+ p = r->args.data;
+ last = p + r->args.len;
+
+ for ( /* void */ ; p < last; p++) {
+
+ /* we need '=' after name, so drop one char from last */
+
+ p = ngx_strlcasestrn(p, last - 1, name, len - 1);
+
+ if (p == NULL) {
+ return NGX_DECLINED;
+ }
+
+ if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {
+
+ value->data = p + len + 1;
+
+ p = ngx_strlchr(p, last, '&');
+
+ if (p == NULL) {
+ p = r->args.data + r->args.len;
+ }
+
+ value->len = p - value->data;
+
+ return NGX_OK;
+ }
+ }
+
+ return NGX_DECLINED;
+}
+
+
+void
+ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)
+{
+ u_char *p, *last;
+
+ last = uri->data + uri->len;
+
+ p = ngx_strlchr(uri->data, last, '?');
+
+ if (p) {
+ uri->len = p - uri->data;
+ p++;
+ args->len = last - p;
+ args->data = p;
+
+ } else {
+ args->len = 0;
+ }
+}
+
+
+ngx_int_t
+ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,
+ ngx_http_chunked_t *ctx)
+{
+ u_char *pos, ch, c;
+ ngx_int_t rc;
+ enum {
+ sw_chunk_start = 0,
+ sw_chunk_size,
+ sw_chunk_extension,
+ sw_chunk_extension_almost_done,
+ sw_chunk_data,
+ sw_after_data,
+ sw_after_data_almost_done,
+ sw_last_chunk_extension,
+ sw_last_chunk_extension_almost_done,
+ sw_trailer,
+ sw_trailer_almost_done,
+ sw_trailer_header,
+ sw_trailer_header_almost_done
+ } state;
+
+ state = ctx->state;
+
+ if (state == sw_chunk_data && ctx->size == 0) {
+ state = sw_after_data;
+ }
+
+ rc = NGX_AGAIN;
+
+ for (pos = b->pos; pos < b->last; pos++) {
+
+ ch = *pos;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http chunked byte: %02Xd s:%d", ch, state);
+
+ switch (state) {
+
+ case sw_chunk_start:
+ if (ch >= '0' && ch <= '9') {
+ state = sw_chunk_size;
+ ctx->size = ch - '0';
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+
+ if (c >= 'a' && c <= 'f') {
+ state = sw_chunk_size;
+ ctx->size = c - 'a' + 10;
+ break;
+ }
+
+ goto invalid;
+
+ case sw_chunk_size:
+ if (ch >= '0' && ch <= '9') {
+ ctx->size = ctx->size * 16 + (ch - '0');
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+
+ if (c >= 'a' && c <= 'f') {
+ ctx->size = ctx->size * 16 + (c - 'a' + 10);
+ break;
+ }
+
+ if (ctx->size == 0) {
+
+ switch (ch) {
+ case CR:
+ state = sw_last_chunk_extension_almost_done;
+ break;
+ case LF:
+ state = sw_trailer;
+ break;
+ case ';':
+ case ' ':
+ case '\t':
+ state = sw_last_chunk_extension;
+ break;
+ default:
+ goto invalid;
+ }
+
+ break;
+ }
+
+ switch (ch) {
+ case CR:
+ state = sw_chunk_extension_almost_done;
+ break;
+ case LF:
+ state = sw_chunk_data;
+ break;
+ case ';':
+ case ' ':
+ case '\t':
+ state = sw_chunk_extension;
+ break;
+ default:
+ goto invalid;
+ }
+
+ break;
+
+ case sw_chunk_extension:
+ switch (ch) {
+ case CR:
+ state = sw_chunk_extension_almost_done;
+ break;
+ case LF:
+ state = sw_chunk_data;
+ }
+ break;
+
+ case sw_chunk_extension_almost_done:
+ if (ch == LF) {
+ state = sw_chunk_data;
+ break;
+ }
+ goto invalid;
+
+ case sw_chunk_data:
+ rc = NGX_OK;
+ goto data;
+
+ case sw_after_data:
+ switch (ch) {
+ case CR:
+ state = sw_after_data_almost_done;
+ break;
+ case LF:
+ state = sw_chunk_start;
+ }
+ break;
+
+ case sw_after_data_almost_done:
+ if (ch == LF) {
+ state = sw_chunk_start;
+ break;
+ }
+ goto invalid;
+
+ case sw_last_chunk_extension:
+ switch (ch) {
+ case CR:
+ state = sw_last_chunk_extension_almost_done;
+ break;
+ case LF:
+ state = sw_trailer;
+ }
+ break;
+
+ case sw_last_chunk_extension_almost_done:
+ if (ch == LF) {
+ state = sw_trailer;
+ break;
+ }
+ goto invalid;
+
+ case sw_trailer:
+ switch (ch) {
+ case CR:
+ state = sw_trailer_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ state = sw_trailer_header;
+ }
+ break;
+
+ case sw_trailer_almost_done:
+ if (ch == LF) {
+ goto done;
+ }
+ goto invalid;
+
+ case sw_trailer_header:
+ switch (ch) {
+ case CR:
+ state = sw_trailer_header_almost_done;
+ break;
+ case LF:
+ state = sw_trailer;
+ }
+ break;
+
+ case sw_trailer_header_almost_done:
+ if (ch == LF) {
+ state = sw_trailer;
+ break;
+ }
+ goto invalid;
+
+ }
+ }
+
+data:
+
+ ctx->state = state;
+ b->pos = pos;
+
+ switch (state) {
+
+ case sw_chunk_start:
+ ctx->length = 3 /* "0" LF LF */;
+ break;
+ case sw_chunk_size:
+ ctx->length = 1 /* LF */
+ + (ctx->size ? ctx->size + 4 /* LF "0" LF LF */
+ : 1 /* LF */);
+ break;
+ case sw_chunk_extension:
+ case sw_chunk_extension_almost_done:
+ ctx->length = 1 /* LF */ + ctx->size + 4 /* LF "0" LF LF */;
+ break;
+ case sw_chunk_data:
+ ctx->length = ctx->size + 4 /* LF "0" LF LF */;
+ break;
+ case sw_after_data:
+ case sw_after_data_almost_done:
+ ctx->length = 4 /* LF "0" LF LF */;
+ break;
+ case sw_last_chunk_extension:
+ case sw_last_chunk_extension_almost_done:
+ ctx->length = 2 /* LF LF */;
+ break;
+ case sw_trailer:
+ case sw_trailer_almost_done:
+ ctx->length = 1 /* LF */;
+ break;
+ case sw_trailer_header:
+ case sw_trailer_header_almost_done:
+ ctx->length = 2 /* LF LF */;
+ break;
+
+ }
+
+ if (ctx->size < 0 || ctx->length < 0) {
+ goto invalid;
+ }
+
+ return rc;
+
+done:
+
+ ctx->state = 0;
+ b->pos = pos + 1;
+
+ return NGX_DONE;
+
+invalid:
+
+ return NGX_ERROR;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_parse_time.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_parse_time.c
new file mode 100644
index 00000000000..985af31725b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_parse_time.c
@@ -0,0 +1,277 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static ngx_uint_t mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+time_t
+ngx_http_parse_time(u_char *value, size_t len)
+{
+ u_char *p, *end;
+ ngx_int_t month;
+ ngx_uint_t day, year, hour, min, sec;
+ uint64_t time;
+ enum {
+ no = 0,
+ rfc822, /* Tue, 10 Nov 2002 23:50:13 */
+ rfc850, /* Tuesday, 10-Dec-02 23:50:13 */
+ isoc /* Tue Dec 10 23:50:13 2002 */
+ } fmt;
+
+ fmt = 0;
+ end = value + len;
+
+#if (NGX_SUPPRESS_WARN)
+ day = 32;
+ year = 2038;
+#endif
+
+ for (p = value; p < end; p++) {
+ if (*p == ',') {
+ break;
+ }
+
+ if (*p == ' ') {
+ fmt = isoc;
+ break;
+ }
+ }
+
+ for (p++; p < end; p++)
+ if (*p != ' ') {
+ break;
+ }
+
+ if (end - p < 18) {
+ return NGX_ERROR;
+ }
+
+ if (fmt != isoc) {
+ if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+ return NGX_ERROR;
+ }
+
+ day = (*p - '0') * 10 + *(p + 1) - '0';
+ p += 2;
+
+ if (*p == ' ') {
+ if (end - p < 18) {
+ return NGX_ERROR;
+ }
+ fmt = rfc822;
+
+ } else if (*p == '-') {
+ fmt = rfc850;
+
+ } else {
+ return NGX_ERROR;
+ }
+
+ p++;
+ }
+
+ switch (*p) {
+
+ case 'J':
+ month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6;
+ break;
+
+ case 'F':
+ month = 1;
+ break;
+
+ case 'M':
+ month = *(p + 2) == 'r' ? 2 : 4;
+ break;
+
+ case 'A':
+ month = *(p + 1) == 'p' ? 3 : 7;
+ break;
+
+ case 'S':
+ month = 8;
+ break;
+
+ case 'O':
+ month = 9;
+ break;
+
+ case 'N':
+ month = 10;
+ break;
+
+ case 'D':
+ month = 11;
+ break;
+
+ default:
+ return NGX_ERROR;
+ }
+
+ p += 3;
+
+ if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) {
+ return NGX_ERROR;
+ }
+
+ p++;
+
+ if (fmt == rfc822) {
+ if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
+ || *(p + 2) < '0' || *(p + 2) > '9'
+ || *(p + 3) < '0' || *(p + 3) > '9')
+ {
+ return NGX_ERROR;
+ }
+
+ year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
+ + (*(p + 2) - '0') * 10 + *(p + 3) - '0';
+ p += 4;
+
+ } else if (fmt == rfc850) {
+ if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+ return NGX_ERROR;
+ }
+
+ year = (*p - '0') * 10 + *(p + 1) - '0';
+ year += (year < 70) ? 2000 : 1900;
+ p += 2;
+ }
+
+ if (fmt == isoc) {
+ if (*p == ' ') {
+ p++;
+ }
+
+ if (*p < '0' || *p > '9') {
+ return NGX_ERROR;
+ }
+
+ day = *p++ - '0';
+
+ if (*p != ' ') {
+ if (*p < '0' || *p > '9') {
+ return NGX_ERROR;
+ }
+
+ day = day * 10 + *p++ - '0';
+ }
+
+ if (end - p < 14) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (*p++ != ' ') {
+ return NGX_ERROR;
+ }
+
+ if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+ return NGX_ERROR;
+ }
+
+ hour = (*p - '0') * 10 + *(p + 1) - '0';
+ p += 2;
+
+ if (*p++ != ':') {
+ return NGX_ERROR;
+ }
+
+ if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+ return NGX_ERROR;
+ }
+
+ min = (*p - '0') * 10 + *(p + 1) - '0';
+ p += 2;
+
+ if (*p++ != ':') {
+ return NGX_ERROR;
+ }
+
+ if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {
+ return NGX_ERROR;
+ }
+
+ sec = (*p - '0') * 10 + *(p + 1) - '0';
+
+ if (fmt == isoc) {
+ p += 2;
+
+ if (*p++ != ' ') {
+ return NGX_ERROR;
+ }
+
+ if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'
+ || *(p + 2) < '0' || *(p + 2) > '9'
+ || *(p + 3) < '0' || *(p + 3) > '9')
+ {
+ return NGX_ERROR;
+ }
+
+ year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100
+ + (*(p + 2) - '0') * 10 + *(p + 3) - '0';
+ }
+
+ if (hour > 23 || min > 59 || sec > 59) {
+ return NGX_ERROR;
+ }
+
+ if (day == 29 && month == 1) {
+ if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) {
+ return NGX_ERROR;
+ }
+
+ } else if (day > mday[month]) {
+ return NGX_ERROR;
+ }
+
+ /*
+ * shift new year to March 1 and start months from 1 (not 0),
+ * it is needed for Gauss' formula
+ */
+
+ if (--month <= 0) {
+ month += 12;
+ year -= 1;
+ }
+
+ /* Gauss' formula for Gregorian days since March 1, 1 BC */
+
+ time = (uint64_t) (
+ /* days in years including leap years since March 1, 1 BC */
+
+ 365 * year + year / 4 - year / 100 + year / 400
+
+ /* days before the month */
+
+ + 367 * month / 12 - 30
+
+ /* days before the day */
+
+ + day - 1
+
+ /*
+ * 719527 days were between March 1, 1 BC and March 1, 1970,
+ * 31 and 28 days were in January and February 1970
+ */
+
+ - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec;
+
+#if (NGX_TIME_T_SIZE <= 4)
+
+ if (time > 0x7fffffff) {
+ return NGX_ERROR;
+ }
+
+#endif
+
+ return (time_t) time;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_postpone_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_postpone_filter_module.c
new file mode 100644
index 00000000000..e893b836488
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_postpone_filter_module.c
@@ -0,0 +1,176 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static ngx_int_t ngx_http_postpone_filter_add(ngx_http_request_t *r,
+ ngx_chain_t *in);
+static ngx_int_t ngx_http_postpone_filter_init(ngx_conf_t *cf);
+
+
+static ngx_http_module_t ngx_http_postpone_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_postpone_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_postpone_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_postpone_filter_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
+
+
+static ngx_int_t
+ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ ngx_connection_t *c;
+ ngx_http_postponed_request_t *pr;
+
+ c = r->connection;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http postpone filter \"%V?%V\" %p", &r->uri, &r->args, in);
+
+ if (r != c->data) {
+
+ if (in) {
+ ngx_http_postpone_filter_add(r, in);
+ return NGX_OK;
+ }
+
+#if 0
+ /* TODO: SSI may pass NULL */
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "http postpone filter NULL inactive request");
+#endif
+
+ return NGX_OK;
+ }
+
+ if (r->postponed == NULL) {
+
+ if (in || c->buffered) {
+ return ngx_http_next_body_filter(r->main, in);
+ }
+
+ return NGX_OK;
+ }
+
+ if (in) {
+ ngx_http_postpone_filter_add(r, in);
+ }
+
+ do {
+ pr = r->postponed;
+
+ if (pr->request) {
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http postpone filter wake \"%V?%V\"",
+ &pr->request->uri, &pr->request->args);
+
+ r->postponed = pr->next;
+
+ c->data = pr->request;
+
+ return ngx_http_post_request(pr->request, NULL);
+ }
+
+ if (pr->out == NULL) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "http postpone filter NULL output");
+
+ } else {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http postpone filter output \"%V?%V\"",
+ &r->uri, &r->args);
+
+ if (ngx_http_next_body_filter(r->main, pr->out) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+
+ r->postponed = pr->next;
+
+ } while (r->postponed);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ ngx_http_postponed_request_t *pr, **ppr;
+
+ if (r->postponed) {
+ for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }
+
+ if (pr->request == NULL) {
+ goto found;
+ }
+
+ ppr = &pr->next;
+
+ } else {
+ ppr = &r->postponed;
+ }
+
+ pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));
+ if (pr == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ppr = pr;
+
+ pr->request = NULL;
+ pr->out = NULL;
+ pr->next = NULL;
+
+found:
+
+ if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) {
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_postpone_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_body_filter = ngx_http_top_body_filter;
+ ngx_http_top_body_filter = ngx_http_postpone_filter;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_request.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_request.c
new file mode 100644
index 00000000000..845ada322e3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_request.c
@@ -0,0 +1,3650 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static void ngx_http_wait_request_handler(ngx_event_t *ev);
+static void ngx_http_process_request_line(ngx_event_t *rev);
+static void ngx_http_process_request_headers(ngx_event_t *rev);
+static ssize_t ngx_http_read_request_header(ngx_http_request_t *r);
+static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
+ ngx_uint_t request_line);
+
+static ngx_int_t ngx_http_process_header_line(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_process_multi_header_lines(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_process_host(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+
+static ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool,
+ ngx_uint_t alloc);
+static ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r,
+ ngx_str_t *host);
+static ngx_int_t ngx_http_find_virtual_server(ngx_connection_t *c,
+ ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,
+ ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp);
+
+static void ngx_http_request_handler(ngx_event_t *ev);
+static void ngx_http_terminate_request(ngx_http_request_t *r, ngx_int_t rc);
+static void ngx_http_terminate_handler(ngx_http_request_t *r);
+static void ngx_http_finalize_connection(ngx_http_request_t *r);
+static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r);
+static void ngx_http_writer(ngx_http_request_t *r);
+static void ngx_http_request_finalizer(ngx_http_request_t *r);
+
+static void ngx_http_set_keepalive(ngx_http_request_t *r);
+static void ngx_http_keepalive_handler(ngx_event_t *ev);
+static void ngx_http_set_lingering_close(ngx_http_request_t *r);
+static void ngx_http_lingering_close_handler(ngx_event_t *ev);
+static ngx_int_t ngx_http_post_action(ngx_http_request_t *r);
+static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error);
+static void ngx_http_log_request(ngx_http_request_t *r);
+
+static u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len);
+static u_char *ngx_http_log_error_handler(ngx_http_request_t *r,
+ ngx_http_request_t *sr, u_char *buf, size_t len);
+
+#if (NGX_HTTP_SSL)
+static void ngx_http_ssl_handshake(ngx_event_t *rev);
+static void ngx_http_ssl_handshake_handler(ngx_connection_t *c);
+#endif
+
+
+static char *ngx_http_client_errors[] = {
+
+ /* NGX_HTTP_PARSE_INVALID_METHOD */
+ "client sent invalid method",
+
+ /* NGX_HTTP_PARSE_INVALID_REQUEST */
+ "client sent invalid request",
+
+ /* NGX_HTTP_PARSE_INVALID_09_METHOD */
+ "client sent invalid method in HTTP/0.9 request"
+};
+
+
+ngx_http_header_t ngx_http_headers_in[] = {
+ { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host),
+ ngx_http_process_host },
+
+ { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection),
+ ngx_http_process_connection },
+
+ { ngx_string("If-Modified-Since"),
+ offsetof(ngx_http_headers_in_t, if_modified_since),
+ ngx_http_process_unique_header_line },
+
+ { ngx_string("If-Unmodified-Since"),
+ offsetof(ngx_http_headers_in_t, if_unmodified_since),
+ ngx_http_process_unique_header_line },
+
+ { ngx_string("If-Match"),
+ offsetof(ngx_http_headers_in_t, if_match),
+ ngx_http_process_unique_header_line },
+
+ { ngx_string("If-None-Match"),
+ offsetof(ngx_http_headers_in_t, if_none_match),
+ ngx_http_process_unique_header_line },
+
+ { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),
+ ngx_http_process_user_agent },
+
+ { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer),
+ ngx_http_process_header_line },
+
+ { ngx_string("Content-Length"),
+ offsetof(ngx_http_headers_in_t, content_length),
+ ngx_http_process_unique_header_line },
+
+ { ngx_string("Content-Type"),
+ offsetof(ngx_http_headers_in_t, content_type),
+ ngx_http_process_header_line },
+
+ { ngx_string("Range"), offsetof(ngx_http_headers_in_t, range),
+ ngx_http_process_header_line },
+
+ { ngx_string("If-Range"),
+ offsetof(ngx_http_headers_in_t, if_range),
+ ngx_http_process_unique_header_line },
+
+ { ngx_string("Transfer-Encoding"),
+ offsetof(ngx_http_headers_in_t, transfer_encoding),
+ ngx_http_process_header_line },
+
+ { ngx_string("Expect"),
+ offsetof(ngx_http_headers_in_t, expect),
+ ngx_http_process_unique_header_line },
+
+ { ngx_string("Upgrade"),
+ offsetof(ngx_http_headers_in_t, upgrade),
+ ngx_http_process_header_line },
+
+#if (NGX_HTTP_GZIP)
+ { ngx_string("Accept-Encoding"),
+ offsetof(ngx_http_headers_in_t, accept_encoding),
+ ngx_http_process_header_line },
+
+ { ngx_string("Via"), offsetof(ngx_http_headers_in_t, via),
+ ngx_http_process_header_line },
+#endif
+
+ { ngx_string("Authorization"),
+ offsetof(ngx_http_headers_in_t, authorization),
+ ngx_http_process_unique_header_line },
+
+ { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive),
+ ngx_http_process_header_line },
+
+#if (NGX_HTTP_X_FORWARDED_FOR)
+ { ngx_string("X-Forwarded-For"),
+ offsetof(ngx_http_headers_in_t, x_forwarded_for),
+ ngx_http_process_multi_header_lines },
+#endif
+
+#if (NGX_HTTP_REALIP)
+ { ngx_string("X-Real-IP"),
+ offsetof(ngx_http_headers_in_t, x_real_ip),
+ ngx_http_process_header_line },
+#endif
+
+#if (NGX_HTTP_HEADERS)
+ { ngx_string("Accept"), offsetof(ngx_http_headers_in_t, accept),
+ ngx_http_process_header_line },
+
+ { ngx_string("Accept-Language"),
+ offsetof(ngx_http_headers_in_t, accept_language),
+ ngx_http_process_header_line },
+#endif
+
+#if (NGX_HTTP_DAV)
+ { ngx_string("Depth"), offsetof(ngx_http_headers_in_t, depth),
+ ngx_http_process_header_line },
+
+ { ngx_string("Destination"), offsetof(ngx_http_headers_in_t, destination),
+ ngx_http_process_header_line },
+
+ { ngx_string("Overwrite"), offsetof(ngx_http_headers_in_t, overwrite),
+ ngx_http_process_header_line },
+
+ { ngx_string("Date"), offsetof(ngx_http_headers_in_t, date),
+ ngx_http_process_header_line },
+#endif
+
+ { ngx_string("Cookie"), offsetof(ngx_http_headers_in_t, cookies),
+ ngx_http_process_multi_header_lines },
+
+ { ngx_null_string, 0, NULL }
+};
+
+
+void
+ngx_http_init_connection(ngx_connection_t *c)
+{
+ ngx_uint_t i;
+ ngx_event_t *rev;
+ struct sockaddr_in *sin;
+ ngx_http_port_t *port;
+ ngx_http_in_addr_t *addr;
+ ngx_http_log_ctx_t *ctx;
+ ngx_http_connection_t *hc;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+ ngx_http_in6_addr_t *addr6;
+#endif
+
+ hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
+ if (hc == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->data = hc;
+
+ /* find the server configuration for the address:port */
+
+ port = c->listening->servers;
+
+ if (port->naddrs > 1) {
+
+ /*
+ * there are several addresses on this port and one of them
+ * is an "*:port" wildcard so getsockname() in ngx_http_server_addr()
+ * is required to determine a server address
+ */
+
+ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+
+ addr6 = port->addrs;
+
+ /* the last address is "*" */
+
+ for (i = 0; i < port->naddrs - 1; i++) {
+ if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
+ break;
+ }
+ }
+
+ hc->addr_conf = &addr6[i].conf;
+
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) c->local_sockaddr;
+
+ addr = port->addrs;
+
+ /* the last address is "*" */
+
+ for (i = 0; i < port->naddrs - 1; i++) {
+ if (addr[i].addr == sin->sin_addr.s_addr) {
+ break;
+ }
+ }
+
+ hc->addr_conf = &addr[i].conf;
+
+ break;
+ }
+
+ } else {
+
+ switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ addr6 = port->addrs;
+ hc->addr_conf = &addr6[0].conf;
+ break;
+#endif
+
+ default: /* AF_INET */
+ addr = port->addrs;
+ hc->addr_conf = &addr[0].conf;
+ break;
+ }
+ }
+
+ /* the default server configuration for the address:port */
+ hc->conf_ctx = hc->addr_conf->default_server->ctx;
+
+ ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));
+ if (ctx == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ ctx->connection = c;
+ ctx->request = NULL;
+ ctx->current_request = NULL;
+
+ c->log->connection = c->number;
+ c->log->handler = ngx_http_log_error;
+ c->log->data = ctx;
+ c->log->action = "waiting for request";
+
+ c->log_error = NGX_ERROR_INFO;
+
+ rev = c->read;
+ rev->handler = ngx_http_wait_request_handler;
+ c->write->handler = ngx_http_empty_handler;
+
+#if (NGX_HTTP_SPDY)
+ if (hc->addr_conf->spdy) {
+ rev->handler = ngx_http_spdy_init;
+ }
+#endif
+
+#if (NGX_HTTP_SSL)
+ {
+ ngx_http_ssl_srv_conf_t *sscf;
+
+ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
+
+ if (sscf->enable || hc->addr_conf->ssl) {
+
+ c->log->action = "SSL handshaking";
+
+ if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "no \"ssl_certificate\" is defined "
+ "in server listening on SSL port");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ hc->ssl = 1;
+
+ rev->handler = ngx_http_ssl_handshake;
+ }
+ }
+#endif
+
+ if (hc->addr_conf->proxy_protocol) {
+ hc->proxy_protocol = 1;
+ c->log->action = "reading PROXY protocol";
+ }
+
+ if (rev->ready) {
+ /* the deferred accept(), rtsig, aio, iocp */
+
+ if (ngx_use_accept_mutex) {
+ ngx_post_event(rev, &ngx_posted_events);
+ return;
+ }
+
+ rev->handler(rev);
+ return;
+ }
+
+ ngx_add_timer(rev, c->listening->post_accept_timeout);
+ ngx_reusable_connection(c, 1);
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_close_connection(c);
+ return;
+ }
+}
+
+
+static void
+ngx_http_wait_request_handler(ngx_event_t *rev)
+{
+ u_char *p;
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_connection_t *c;
+ ngx_http_connection_t *hc;
+ ngx_http_core_srv_conf_t *cscf;
+
+ c = rev->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (c->close) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ hc = c->data;
+ cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
+
+ size = cscf->client_header_buffer_size;
+
+ b = c->buffer;
+
+ if (b == NULL) {
+ b = ngx_create_temp_buf(c->pool, size);
+ if (b == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->buffer = b;
+
+ } else if (b->start == NULL) {
+
+ b->start = ngx_palloc(c->pool, size);
+ if (b->start == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ b->pos = b->start;
+ b->last = b->start;
+ b->end = b->last + size;
+ }
+
+ n = c->recv(c, b->last, size);
+
+ if (n == NGX_AGAIN) {
+
+ if (!rev->timer_set) {
+ ngx_add_timer(rev, c->listening->post_accept_timeout);
+ ngx_reusable_connection(c, 1);
+ }
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ /*
+ * We are trying to not hold c->buffer's memory for an idle connection.
+ */
+
+ if (ngx_pfree(c->pool, b->start) == NGX_OK) {
+ b->start = NULL;
+ }
+
+ return;
+ }
+
+ if (n == NGX_ERROR) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (n == 0) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client closed connection");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ b->last += n;
+
+ if (hc->proxy_protocol) {
+ hc->proxy_protocol = 0;
+
+ p = ngx_proxy_protocol_parse(c, b->pos, b->last);
+
+ if (p == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ b->pos = p;
+
+ if (b->pos == b->last) {
+ c->log->action = "waiting for request";
+ b->pos = b->start;
+ b->last = b->start;
+ ngx_post_event(rev, &ngx_posted_events);
+ return;
+ }
+ }
+
+ c->log->action = "reading client request line";
+
+ ngx_reusable_connection(c, 0);
+
+ c->data = ngx_http_create_request(c);
+ if (c->data == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ rev->handler = ngx_http_process_request_line;
+ ngx_http_process_request_line(rev);
+}
+
+
+ngx_http_request_t *
+ngx_http_create_request(ngx_connection_t *c)
+{
+ ngx_pool_t *pool;
+ ngx_time_t *tp;
+ ngx_http_request_t *r;
+ ngx_http_log_ctx_t *ctx;
+ ngx_http_connection_t *hc;
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_main_conf_t *cmcf;
+
+ c->requests++;
+
+ hc = c->data;
+
+ cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
+
+ pool = ngx_create_pool(cscf->request_pool_size, c->log);
+ if (pool == NULL) {
+ return NULL;
+ }
+
+ r = ngx_pcalloc(pool, sizeof(ngx_http_request_t));
+ if (r == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ r->pool = pool;
+
+ r->http_connection = hc;
+ r->signature = NGX_HTTP_MODULE;
+ r->connection = c;
+
+ r->main_conf = hc->conf_ctx->main_conf;
+ r->srv_conf = hc->conf_ctx->srv_conf;
+ r->loc_conf = hc->conf_ctx->loc_conf;
+
+ r->read_event_handler = ngx_http_block_reading;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_http_set_connection_log(r->connection, clcf->error_log);
+
+ r->header_in = hc->nbusy ? hc->busy[0] : c->buffer;
+
+ if (ngx_list_init(&r->headers_out.headers, r->pool, 20,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ ngx_destroy_pool(r->pool);
+ return NULL;
+ }
+
+ r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
+ if (r->ctx == NULL) {
+ ngx_destroy_pool(r->pool);
+ return NULL;
+ }
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts
+ * sizeof(ngx_http_variable_value_t));
+ if (r->variables == NULL) {
+ ngx_destroy_pool(r->pool);
+ return NULL;
+ }
+
+#if (NGX_HTTP_SSL)
+ if (c->ssl) {
+ r->main_filter_need_in_memory = 1;
+ }
+#endif
+
+ r->main = r;
+ r->count = 1;
+
+ tp = ngx_timeofday();
+ r->start_sec = tp->sec;
+ r->start_msec = tp->msec;
+
+ r->method = NGX_HTTP_UNKNOWN;
+ r->http_version = NGX_HTTP_VERSION_10;
+
+ r->headers_in.content_length_n = -1;
+ r->headers_in.keep_alive_n = -1;
+ r->headers_out.content_length_n = -1;
+ r->headers_out.last_modified_time = -1;
+
+ r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;
+ r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1;
+
+ r->http_state = NGX_HTTP_READING_REQUEST_STATE;
+
+ ctx = c->log->data;
+ ctx->request = r;
+ ctx->current_request = r;
+ r->log_handler = ngx_http_log_error_handler;
+
+#if (NGX_STAT_STUB)
+ (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
+ r->stat_reading = 1;
+ (void) ngx_atomic_fetch_add(ngx_stat_requests, 1);
+#endif
+
+ return r;
+}
+
+
+#if (NGX_HTTP_SSL)
+
+static void
+ngx_http_ssl_handshake(ngx_event_t *rev)
+{
+ u_char *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER + 1];
+ size_t size;
+ ssize_t n;
+ ngx_err_t err;
+ ngx_int_t rc;
+ ngx_connection_t *c;
+ ngx_http_connection_t *hc;
+ ngx_http_ssl_srv_conf_t *sscf;
+
+ c = rev->data;
+ hc = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "http check ssl handshake");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (c->close) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ size = hc->proxy_protocol ? sizeof(buf) : 1;
+
+ n = recv(c->fd, (char *) buf, size, MSG_PEEK);
+
+ err = ngx_socket_errno;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http recv(): %d", n);
+
+ if (n == -1) {
+ if (err == NGX_EAGAIN) {
+
+ if (!rev->timer_set) {
+ ngx_add_timer(rev, c->listening->post_accept_timeout);
+ ngx_reusable_connection(c, 1);
+ }
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_close_connection(c);
+ }
+
+ return;
+ }
+
+ ngx_connection_error(c, err, "recv() failed");
+ ngx_http_close_connection(c);
+
+ return;
+ }
+
+ if (hc->proxy_protocol) {
+ hc->proxy_protocol = 0;
+
+ p = ngx_proxy_protocol_parse(c, buf, buf + n);
+
+ if (p == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ size = p - buf;
+
+ if (c->recv(c, buf, size) != (ssize_t) size) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->log->action = "SSL handshaking";
+
+ if (n == (ssize_t) size) {
+ ngx_post_event(rev, &ngx_posted_events);
+ return;
+ }
+
+ n = 1;
+ buf[0] = *p;
+ }
+
+ if (n == 1) {
+ if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "https ssl handshake: 0x%02Xd", buf[0]);
+
+ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,
+ ngx_http_ssl_module);
+
+ if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER)
+ != NGX_OK)
+ {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ rc = ngx_ssl_handshake(c);
+
+ if (rc == NGX_AGAIN) {
+
+ if (!rev->timer_set) {
+ ngx_add_timer(rev, c->listening->post_accept_timeout);
+ }
+
+ ngx_reusable_connection(c, 0);
+
+ c->ssl->handler = ngx_http_ssl_handshake_handler;
+ return;
+ }
+
+ ngx_http_ssl_handshake_handler(c);
+
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "plain http");
+
+ c->log->action = "waiting for request";
+
+ rev->handler = ngx_http_wait_request_handler;
+ ngx_http_wait_request_handler(rev);
+
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client closed connection");
+ ngx_http_close_connection(c);
+}
+
+
+static void
+ngx_http_ssl_handshake_handler(ngx_connection_t *c)
+{
+ if (c->ssl->handshaked) {
+
+ /*
+ * The majority of browsers do not send the "close notify" alert.
+ * Among them are MSIE, old Mozilla, Netscape 4, Konqueror,
+ * and Links. And what is more, MSIE ignores the server's alert.
+ *
+ * Opera and recent Mozilla send the alert.
+ */
+
+ c->ssl->no_wait_shutdown = 1;
+
+#if (NGX_HTTP_SPDY \
+ && (defined TLSEXT_TYPE_application_layer_protocol_negotiation \
+ || defined TLSEXT_TYPE_next_proto_neg))
+ {
+ unsigned int len;
+ const unsigned char *data;
+ static const ngx_str_t spdy = ngx_string(NGX_SPDY_NPN_NEGOTIATED);
+
+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+ SSL_get0_alpn_selected(c->ssl->connection, &data, &len);
+
+#ifdef TLSEXT_TYPE_next_proto_neg
+ if (len == 0) {
+ SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);
+ }
+#endif
+
+#else /* TLSEXT_TYPE_next_proto_neg */
+ SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);
+#endif
+
+ if (len == spdy.len && ngx_strncmp(data, spdy.data, spdy.len) == 0) {
+ ngx_http_spdy_init(c->read);
+ return;
+ }
+ }
+#endif
+
+ c->log->action = "waiting for request";
+
+ c->read->handler = ngx_http_wait_request_handler;
+ /* STUB: epoll edge */ c->write->handler = ngx_http_empty_handler;
+
+ ngx_reusable_connection(c, 1);
+
+ ngx_http_wait_request_handler(c->read);
+
+ return;
+ }
+
+ if (c->read->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ }
+
+ ngx_http_close_connection(c);
+}
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+
+int
+ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
+{
+ ngx_str_t host;
+ const char *servername;
+ ngx_connection_t *c;
+ ngx_http_connection_t *hc;
+ ngx_http_ssl_srv_conf_t *sscf;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_srv_conf_t *cscf;
+
+ servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);
+
+ if (servername == NULL) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ c = ngx_ssl_get_connection(ssl_conn);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "SSL server name: \"%s\"", servername);
+
+ host.len = ngx_strlen(servername);
+
+ if (host.len == 0) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ host.data = (u_char *) servername;
+
+ if (ngx_http_validate_host(&host, c->pool, 1) != NGX_OK) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ hc = c->data;
+
+ if (ngx_http_find_virtual_server(c, hc->addr_conf->virtual_names, &host,
+ NULL, &cscf)
+ != NGX_OK)
+ {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t));
+ if (hc->ssl_servername == NULL) {
+ return SSL_TLSEXT_ERR_NOACK;
+ }
+
+ *hc->ssl_servername = host;
+
+ hc->conf_ctx = cscf->ctx;
+
+ clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module);
+
+ ngx_http_set_connection_log(c, clcf->error_log);
+
+ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
+
+ if (sscf->ssl.ctx) {
+ SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx);
+
+ /*
+ * SSL_set_SSL_CTX() only changes certs as of 1.0.0d
+ * adjust other things we care about
+ */
+
+ SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sscf->ssl.ctx),
+ SSL_CTX_get_verify_callback(sscf->ssl.ctx));
+
+ SSL_set_verify_depth(ssl_conn, SSL_CTX_get_verify_depth(sscf->ssl.ctx));
+
+#ifdef SSL_CTRL_CLEAR_OPTIONS
+ /* only in 0.9.8m+ */
+ SSL_clear_options(ssl_conn, SSL_get_options(ssl_conn) &
+ ~SSL_CTX_get_options(sscf->ssl.ctx));
+#endif
+
+ SSL_set_options(ssl_conn, SSL_CTX_get_options(sscf->ssl.ctx));
+ }
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+#endif
+
+#endif
+
+
+static void
+ngx_http_process_request_line(ngx_event_t *rev)
+{
+ ssize_t n;
+ ngx_int_t rc, rv;
+ ngx_str_t host;
+ ngx_connection_t *c;
+ ngx_http_request_t *r;
+
+ c = rev->data;
+ r = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "http process request line");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ rc = NGX_AGAIN;
+
+ for ( ;; ) {
+
+ if (rc == NGX_AGAIN) {
+ n = ngx_http_read_request_header(r);
+
+ if (n == NGX_AGAIN || n == NGX_ERROR) {
+ return;
+ }
+ }
+
+ rc = ngx_http_parse_request_line(r, r->header_in);
+
+ if (rc == NGX_OK) {
+
+ /* the request line has been parsed successfully */
+
+ r->request_line.len = r->request_end - r->request_start;
+ r->request_line.data = r->request_start;
+ r->request_length = r->header_in->pos - r->request_start;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http request line: \"%V\"", &r->request_line);
+
+ r->method_name.len = r->method_end - r->request_start + 1;
+ r->method_name.data = r->request_line.data;
+
+ if (r->http_protocol.data) {
+ r->http_protocol.len = r->request_end - r->http_protocol.data;
+ }
+
+ if (ngx_http_process_request_uri(r) != NGX_OK) {
+ return;
+ }
+
+ if (r->host_start && r->host_end) {
+
+ host.len = r->host_end - r->host_start;
+ host.data = r->host_start;
+
+ rc = ngx_http_validate_host(&host, r->pool, 0);
+
+ if (rc == NGX_DECLINED) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid host in request line");
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {
+ return;
+ }
+
+ r->headers_in.server = host;
+ }
+
+ if (r->http_version < NGX_HTTP_VERSION_10) {
+
+ if (r->headers_in.server.len == 0
+ && ngx_http_set_virtual_server(r, &r->headers_in.server)
+ == NGX_ERROR)
+ {
+ return;
+ }
+
+ ngx_http_process_request(r);
+ return;
+ }
+
+
+ if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ c->log->action = "reading client request headers";
+
+ rev->handler = ngx_http_process_request_headers;
+ ngx_http_process_request_headers(rev);
+
+ return;
+ }
+
+ if (rc != NGX_AGAIN) {
+
+ /* there was error while a request line parsing */
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ ngx_http_client_errors[rc - NGX_HTTP_CLIENT_ERROR]);
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return;
+ }
+
+ /* NGX_AGAIN: a request line parsing is still incomplete */
+
+ if (r->header_in->pos == r->header_in->end) {
+
+ rv = ngx_http_alloc_large_header_buffer(r, 1);
+
+ if (rv == NGX_ERROR) {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (rv == NGX_DECLINED) {
+ r->request_line.len = r->header_in->end - r->request_start;
+ r->request_line.data = r->request_start;
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent too long URI");
+ ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE);
+ return;
+ }
+ }
+ }
+}
+
+
+ngx_int_t
+ngx_http_process_request_uri(ngx_http_request_t *r)
+{
+ ngx_http_core_srv_conf_t *cscf;
+
+ if (r->args_start) {
+ r->uri.len = r->args_start - 1 - r->uri_start;
+ } else {
+ r->uri.len = r->uri_end - r->uri_start;
+ }
+
+ if (r->complex_uri || r->quoted_uri) {
+
+ r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1);
+ if (r->uri.data == NULL) {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ if (ngx_http_parse_complex_uri(r, cscf->merge_slashes) != NGX_OK) {
+ r->uri.len = 0;
+
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid request");
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return NGX_ERROR;
+ }
+
+ } else {
+ r->uri.data = r->uri_start;
+ }
+
+ r->unparsed_uri.len = r->uri_end - r->uri_start;
+ r->unparsed_uri.data = r->uri_start;
+
+ r->valid_unparsed_uri = r->space_in_uri ? 0 : 1;
+
+ if (r->uri_ext) {
+ if (r->args_start) {
+ r->exten.len = r->args_start - 1 - r->uri_ext;
+ } else {
+ r->exten.len = r->uri_end - r->uri_ext;
+ }
+
+ r->exten.data = r->uri_ext;
+ }
+
+ if (r->args_start && r->uri_end > r->args_start) {
+ r->args.len = r->uri_end - r->args_start;
+ r->args.data = r->args_start;
+ }
+
+#if (NGX_WIN32)
+ {
+ u_char *p, *last;
+
+ p = r->uri.data;
+ last = r->uri.data + r->uri.len;
+
+ while (p < last) {
+
+ if (*p++ == ':') {
+
+ /*
+ * this check covers "::$data", "::$index_allocation" and
+ * ":$i30:$index_allocation"
+ */
+
+ if (p < last && *p == '$') {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent unsafe win32 URI");
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ p = r->uri.data + r->uri.len - 1;
+
+ while (p > r->uri.data) {
+
+ if (*p == ' ') {
+ p--;
+ continue;
+ }
+
+ if (*p == '.') {
+ p--;
+ continue;
+ }
+
+ break;
+ }
+
+ if (p != r->uri.data + r->uri.len - 1) {
+ r->uri.len = p + 1 - r->uri.data;
+ ngx_http_set_exten(r);
+ }
+
+ }
+#endif
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http uri: \"%V\"", &r->uri);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http args: \"%V\"", &r->args);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http exten: \"%V\"", &r->exten);
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_process_request_headers(ngx_event_t *rev)
+{
+ u_char *p;
+ size_t len;
+ ssize_t n;
+ ngx_int_t rc, rv;
+ ngx_table_elt_t *h;
+ ngx_connection_t *c;
+ ngx_http_header_t *hh;
+ ngx_http_request_t *r;
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_core_main_conf_t *cmcf;
+
+ c = rev->data;
+ r = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
+ "http process request header line");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ rc = NGX_AGAIN;
+
+ for ( ;; ) {
+
+ if (rc == NGX_AGAIN) {
+
+ if (r->header_in->pos == r->header_in->end) {
+
+ rv = ngx_http_alloc_large_header_buffer(r, 0);
+
+ if (rv == NGX_ERROR) {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (rv == NGX_DECLINED) {
+ p = r->header_name_start;
+
+ r->lingering_close = 1;
+
+ if (p == NULL) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent too large request");
+ ngx_http_finalize_request(r,
+ NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
+ return;
+ }
+
+ len = r->header_in->end - p;
+
+ if (len > NGX_MAX_ERROR_STR - 300) {
+ len = NGX_MAX_ERROR_STR - 300;
+ p[len++] = '.'; p[len++] = '.'; p[len++] = '.';
+ }
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent too long header line: \"%*s\"",
+ len, r->header_name_start);
+
+ ngx_http_finalize_request(r,
+ NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
+ return;
+ }
+ }
+
+ n = ngx_http_read_request_header(r);
+
+ if (n == NGX_AGAIN || n == NGX_ERROR) {
+ return;
+ }
+ }
+
+ /* the host header could change the server configuration context */
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ rc = ngx_http_parse_header_line(r, r->header_in,
+ cscf->underscores_in_headers);
+
+ if (rc == NGX_OK) {
+
+ r->request_length += r->header_in->pos - r->header_name_start;
+
+ if (r->invalid_header && cscf->ignore_invalid_headers) {
+
+ /* there was error while a header line parsing */
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid header line: \"%*s\"",
+ r->header_end - r->header_name_start,
+ r->header_name_start);
+ continue;
+ }
+
+ /* a header line has been parsed successfully */
+
+ h = ngx_list_push(&r->headers_in.headers);
+ if (h == NULL) {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ h->hash = r->header_hash;
+
+ h->key.len = r->header_name_end - r->header_name_start;
+ h->key.data = r->header_name_start;
+ h->key.data[h->key.len] = '\0';
+
+ h->value.len = r->header_end - r->header_start;
+ h->value.data = r->header_start;
+ h->value.data[h->value.len] = '\0';
+
+ h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
+ if (h->lowcase_key == NULL) {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (h->key.len == r->lowcase_index) {
+ ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
+
+ } else {
+ ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
+ }
+
+ hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
+ h->lowcase_key, h->key.len);
+
+ if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
+ return;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http header: \"%V: %V\"",
+ &h->key, &h->value);
+
+ continue;
+ }
+
+ if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
+
+ /* a whole header has been parsed successfully */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http header done");
+
+ r->request_length += r->header_in->pos - r->header_name_start;
+
+ r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
+
+ rc = ngx_http_process_request_header(r);
+
+ if (rc != NGX_OK) {
+ return;
+ }
+
+ ngx_http_process_request(r);
+
+ return;
+ }
+
+ if (rc == NGX_AGAIN) {
+
+ /* a header line parsing is still not complete */
+
+ continue;
+ }
+
+ /* rc == NGX_HTTP_PARSE_INVALID_HEADER: "\r" is not followed by "\n" */
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid header line: \"%*s\\r...\"",
+ r->header_end - r->header_name_start,
+ r->header_name_start);
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return;
+ }
+}
+
+
+static ssize_t
+ngx_http_read_request_header(ngx_http_request_t *r)
+{
+ ssize_t n;
+ ngx_event_t *rev;
+ ngx_connection_t *c;
+ ngx_http_core_srv_conf_t *cscf;
+
+ c = r->connection;
+ rev = c->read;
+
+ n = r->header_in->last - r->header_in->pos;
+
+ if (n > 0) {
+ return n;
+ }
+
+ if (rev->ready) {
+ n = c->recv(c, r->header_in->last,
+ r->header_in->end - r->header_in->last);
+ } else {
+ n = NGX_AGAIN;
+ }
+
+ if (n == NGX_AGAIN) {
+ if (!rev->timer_set) {
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+ ngx_add_timer(rev, cscf->client_header_timeout);
+ }
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ if (n == 0) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client prematurely closed connection");
+ }
+
+ if (n == 0 || n == NGX_ERROR) {
+ c->error = 1;
+ c->log->action = "reading client request headers";
+
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return NGX_ERROR;
+ }
+
+ r->header_in->last += n;
+
+ return n;
+}
+
+
+static ngx_int_t
+ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
+ ngx_uint_t request_line)
+{
+ u_char *old, *new;
+ ngx_buf_t *b;
+ ngx_http_connection_t *hc;
+ ngx_http_core_srv_conf_t *cscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http alloc large header buffer");
+
+ if (request_line && r->state == 0) {
+
+ /* the client fills up the buffer with "\r\n" */
+
+ r->header_in->pos = r->header_in->start;
+ r->header_in->last = r->header_in->start;
+
+ return NGX_OK;
+ }
+
+ old = request_line ? r->request_start : r->header_name_start;
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ if (r->state != 0
+ && (size_t) (r->header_in->pos - old)
+ >= cscf->large_client_header_buffers.size)
+ {
+ return NGX_DECLINED;
+ }
+
+ hc = r->http_connection;
+
+ if (hc->nfree) {
+ b = hc->free[--hc->nfree];
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http large header free: %p %uz",
+ b->pos, b->end - b->last);
+
+ } else if (hc->nbusy < cscf->large_client_header_buffers.num) {
+
+ if (hc->busy == NULL) {
+ hc->busy = ngx_palloc(r->connection->pool,
+ cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));
+ if (hc->busy == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ b = ngx_create_temp_buf(r->connection->pool,
+ cscf->large_client_header_buffers.size);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http large header alloc: %p %uz",
+ b->pos, b->end - b->last);
+
+ } else {
+ return NGX_DECLINED;
+ }
+
+ hc->busy[hc->nbusy++] = b;
+
+ if (r->state == 0) {
+ /*
+ * r->state == 0 means that a header line was parsed successfully
+ * and we do not need to copy incomplete header line and
+ * to relocate the parser header pointers
+ */
+
+ r->header_in = b;
+
+ return NGX_OK;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http large header copy: %d", r->header_in->pos - old);
+
+ new = b->start;
+
+ ngx_memcpy(new, old, r->header_in->pos - old);
+
+ b->pos = new + (r->header_in->pos - old);
+ b->last = new + (r->header_in->pos - old);
+
+ if (request_line) {
+ r->request_start = new;
+
+ if (r->request_end) {
+ r->request_end = new + (r->request_end - old);
+ }
+
+ r->method_end = new + (r->method_end - old);
+
+ r->uri_start = new + (r->uri_start - old);
+ r->uri_end = new + (r->uri_end - old);
+
+ if (r->schema_start) {
+ r->schema_start = new + (r->schema_start - old);
+ r->schema_end = new + (r->schema_end - old);
+ }
+
+ if (r->host_start) {
+ r->host_start = new + (r->host_start - old);
+ if (r->host_end) {
+ r->host_end = new + (r->host_end - old);
+ }
+ }
+
+ if (r->port_start) {
+ r->port_start = new + (r->port_start - old);
+ r->port_end = new + (r->port_end - old);
+ }
+
+ if (r->uri_ext) {
+ r->uri_ext = new + (r->uri_ext - old);
+ }
+
+ if (r->args_start) {
+ r->args_start = new + (r->args_start - old);
+ }
+
+ if (r->http_protocol.data) {
+ r->http_protocol.data = new + (r->http_protocol.data - old);
+ }
+
+ } else {
+ r->header_name_start = new;
+ r->header_name_end = new + (r->header_name_end - old);
+ r->header_start = new + (r->header_start - old);
+ r->header_end = new + (r->header_end - old);
+ }
+
+ r->header_in = b;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_table_elt_t **ph;
+
+ ph = (ngx_table_elt_t **) ((char *) &r->headers_in + offset);
+
+ if (*ph == NULL) {
+ *ph = h;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_process_unique_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_table_elt_t **ph;
+
+ ph = (ngx_table_elt_t **) ((char *) &r->headers_in + offset);
+
+ if (*ph == NULL) {
+ *ph = h;
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent duplicate header line: \"%V: %V\", "
+ "previous value: \"%V: %V\"",
+ &h->key, &h->value, &(*ph)->key, &(*ph)->value);
+
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_int_t rc;
+ ngx_str_t host;
+
+ if (r->headers_in.host == NULL) {
+ r->headers_in.host = h;
+ }
+
+ host = h->value;
+
+ rc = ngx_http_validate_host(&host, r->pool, 0);
+
+ if (rc == NGX_DECLINED) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid host header");
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return NGX_ERROR;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+ if (r->headers_in.server.len) {
+ return NGX_OK;
+ }
+
+ if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ r->headers_in.server = host;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ if (ngx_strcasestrn(h->value.data, "close", 5 - 1)) {
+ r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
+
+ } else if (ngx_strcasestrn(h->value.data, "keep-alive", 10 - 1)) {
+ r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ u_char *user_agent, *msie;
+
+ if (r->headers_in.user_agent) {
+ return NGX_OK;
+ }
+
+ r->headers_in.user_agent = h;
+
+ /* check some widespread browsers while the header is in CPU cache */
+
+ user_agent = h->value.data;
+
+ msie = ngx_strstrn(user_agent, "MSIE ", 5 - 1);
+
+ if (msie && msie + 7 < user_agent + h->value.len) {
+
+ r->headers_in.msie = 1;
+
+ if (msie[6] == '.') {
+
+ switch (msie[5]) {
+ case '4':
+ case '5':
+ r->headers_in.msie6 = 1;
+ break;
+ case '6':
+ if (ngx_strstrn(msie + 8, "SV1", 3 - 1) == NULL) {
+ r->headers_in.msie6 = 1;
+ }
+ break;
+ }
+ }
+
+#if 0
+ /* MSIE ignores the SSL "close notify" alert */
+ if (c->ssl) {
+ c->ssl->no_send_shutdown = 1;
+ }
+#endif
+ }
+
+ if (ngx_strstrn(user_agent, "Opera", 5 - 1)) {
+ r->headers_in.opera = 1;
+ r->headers_in.msie = 0;
+ r->headers_in.msie6 = 0;
+ }
+
+ if (!r->headers_in.msie && !r->headers_in.opera) {
+
+ if (ngx_strstrn(user_agent, "Gecko/", 6 - 1)) {
+ r->headers_in.gecko = 1;
+
+ } else if (ngx_strstrn(user_agent, "Chrome/", 7 - 1)) {
+ r->headers_in.chrome = 1;
+
+ } else if (ngx_strstrn(user_agent, "Safari/", 7 - 1)
+ && ngx_strstrn(user_agent, "Mac OS X", 8 - 1))
+ {
+ r->headers_in.safari = 1;
+
+ } else if (ngx_strstrn(user_agent, "Konqueror", 9 - 1)) {
+ r->headers_in.konqueror = 1;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_process_multi_header_lines(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_array_t *headers;
+ ngx_table_elt_t **ph;
+
+ headers = (ngx_array_t *) ((char *) &r->headers_in + offset);
+
+ if (headers->elts == NULL) {
+ if (ngx_array_init(headers, r->pool, 1, sizeof(ngx_table_elt_t *))
+ != NGX_OK)
+ {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+ }
+
+ ph = ngx_array_push(headers);
+ if (ph == NULL) {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+ *ph = h;
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_process_request_header(ngx_http_request_t *r)
+{
+ if (r->headers_in.server.len == 0
+ && ngx_http_set_virtual_server(r, &r->headers_in.server)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+
+ if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent HTTP/1.1 request without \"Host\" header");
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return NGX_ERROR;
+ }
+
+ if (r->headers_in.content_length) {
+ r->headers_in.content_length_n =
+ ngx_atoof(r->headers_in.content_length->value.data,
+ r->headers_in.content_length->value.len);
+
+ if (r->headers_in.content_length_n == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid \"Content-Length\" header");
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return NGX_ERROR;
+ }
+ }
+
+ if (r->method & NGX_HTTP_TRACE) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent TRACE method");
+ ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
+ return NGX_ERROR;
+ }
+
+ if (r->headers_in.transfer_encoding) {
+ if (r->headers_in.transfer_encoding->value.len == 7
+ && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
+ (u_char *) "chunked", 7) == 0)
+ {
+ r->headers_in.content_length = NULL;
+ r->headers_in.content_length_n = -1;
+ r->headers_in.chunked = 1;
+
+ } else if (r->headers_in.transfer_encoding->value.len != 8
+ || ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
+ (u_char *) "identity", 8) != 0)
+ {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent unknown \"Transfer-Encoding\": \"%V\"",
+ &r->headers_in.transfer_encoding->value);
+ ngx_http_finalize_request(r, NGX_HTTP_NOT_IMPLEMENTED);
+ return NGX_ERROR;
+ }
+ }
+
+ if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) {
+ if (r->headers_in.keep_alive) {
+ r->headers_in.keep_alive_n =
+ ngx_atotm(r->headers_in.keep_alive->value.data,
+ r->headers_in.keep_alive->value.len);
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_http_process_request(ngx_http_request_t *r)
+{
+ ngx_connection_t *c;
+
+ c = r->connection;
+
+#if (NGX_HTTP_SSL)
+
+ if (r->http_connection->ssl) {
+ long rc;
+ X509 *cert;
+ ngx_http_ssl_srv_conf_t *sscf;
+
+ if (c->ssl == NULL) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent plain HTTP request to HTTPS port");
+ ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
+ return;
+ }
+
+ sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
+
+ if (sscf->verify) {
+ rc = SSL_get_verify_result(c->ssl->connection);
+
+ if (rc != X509_V_OK
+ && (sscf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))
+ {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client SSL certificate verify error: (%l:%s)",
+ rc, X509_verify_cert_error_string(rc));
+
+ ngx_ssl_remove_cached_session(sscf->ssl.ctx,
+ (SSL_get0_session(c->ssl->connection)));
+
+ ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR);
+ return;
+ }
+
+ if (sscf->verify == 1) {
+ cert = SSL_get_peer_certificate(c->ssl->connection);
+
+ if (cert == NULL) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent no required SSL certificate");
+
+ ngx_ssl_remove_cached_session(sscf->ssl.ctx,
+ (SSL_get0_session(c->ssl->connection)));
+
+ ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT);
+ return;
+ }
+
+ X509_free(cert);
+ }
+ }
+ }
+
+#endif
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+#if (NGX_STAT_STUB)
+ (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
+ r->stat_reading = 0;
+ (void) ngx_atomic_fetch_add(ngx_stat_writing, 1);
+ r->stat_writing = 1;
+#endif
+
+ c->read->handler = ngx_http_request_handler;
+ c->write->handler = ngx_http_request_handler;
+ r->read_event_handler = ngx_http_block_reading;
+
+ ngx_http_handler(r);
+
+ ngx_http_run_posted_requests(c);
+}
+
+
+static ngx_int_t
+ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool, ngx_uint_t alloc)
+{
+ u_char *h, ch;
+ size_t i, dot_pos, host_len;
+
+ enum {
+ sw_usual = 0,
+ sw_literal,
+ sw_rest
+ } state;
+
+ dot_pos = host->len;
+ host_len = host->len;
+
+ h = host->data;
+
+ state = sw_usual;
+
+ for (i = 0; i < host->len; i++) {
+ ch = h[i];
+
+ switch (ch) {
+
+ case '.':
+ if (dot_pos == i - 1) {
+ return NGX_DECLINED;
+ }
+ dot_pos = i;
+ break;
+
+ case ':':
+ if (state == sw_usual) {
+ host_len = i;
+ state = sw_rest;
+ }
+ break;
+
+ case '[':
+ if (i == 0) {
+ state = sw_literal;
+ }
+ break;
+
+ case ']':
+ if (state == sw_literal) {
+ host_len = i + 1;
+ state = sw_rest;
+ }
+ break;
+
+ case '\0':
+ return NGX_DECLINED;
+
+ default:
+
+ if (ngx_path_separator(ch)) {
+ return NGX_DECLINED;
+ }
+
+ if (ch >= 'A' && ch <= 'Z') {
+ alloc = 1;
+ }
+
+ break;
+ }
+ }
+
+ if (dot_pos == host_len - 1) {
+ host_len--;
+ }
+
+ if (host_len == 0) {
+ return NGX_DECLINED;
+ }
+
+ if (alloc) {
+ host->data = ngx_pnalloc(pool, host_len);
+ if (host->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_strlow(host->data, h, host_len);
+ }
+
+ host->len = host_len;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_set_virtual_server(ngx_http_request_t *r, ngx_str_t *host)
+{
+ ngx_int_t rc;
+ ngx_http_connection_t *hc;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_srv_conf_t *cscf;
+
+#if (NGX_SUPPRESS_WARN)
+ cscf = NULL;
+#endif
+
+ hc = r->http_connection;
+
+#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)
+
+ if (hc->ssl_servername) {
+ if (hc->ssl_servername->len == host->len
+ && ngx_strncmp(hc->ssl_servername->data,
+ host->data, host->len) == 0)
+ {
+#if (NGX_PCRE)
+ if (hc->ssl_servername_regex
+ && ngx_http_regex_exec(r, hc->ssl_servername_regex,
+ hc->ssl_servername) != NGX_OK)
+ {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+#endif
+ return NGX_OK;
+ }
+ }
+
+#endif
+
+ rc = ngx_http_find_virtual_server(r->connection,
+ hc->addr_conf->virtual_names,
+ host, r, &cscf);
+
+ if (rc == NGX_ERROR) {
+ ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)
+
+ if (hc->ssl_servername) {
+ ngx_http_ssl_srv_conf_t *sscf;
+
+ if (rc == NGX_DECLINED) {
+ cscf = hc->addr_conf->default_server;
+ rc = NGX_OK;
+ }
+
+ sscf = ngx_http_get_module_srv_conf(cscf->ctx, ngx_http_ssl_module);
+
+ if (sscf->verify) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client attempted to request the server name "
+ "different from that one was negotiated");
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return NGX_ERROR;
+ }
+ }
+
+#endif
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+ r->srv_conf = cscf->ctx->srv_conf;
+ r->loc_conf = cscf->ctx->loc_conf;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_http_set_connection_log(r->connection, clcf->error_log);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_find_virtual_server(ngx_connection_t *c,
+ ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,
+ ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp)
+{
+ ngx_http_core_srv_conf_t *cscf;
+
+ if (virtual_names == NULL) {
+ return NGX_DECLINED;
+ }
+
+ cscf = ngx_hash_find_combined(&virtual_names->names,
+ ngx_hash_key(host->data, host->len),
+ host->data, host->len);
+
+ if (cscf) {
+ *cscfp = cscf;
+ return NGX_OK;
+ }
+
+#if (NGX_PCRE)
+
+ if (host->len && virtual_names->nregex) {
+ ngx_int_t n;
+ ngx_uint_t i;
+ ngx_http_server_name_t *sn;
+
+ sn = virtual_names->regex;
+
+#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)
+
+ if (r == NULL) {
+ ngx_http_connection_t *hc;
+
+ for (i = 0; i < virtual_names->nregex; i++) {
+
+ n = ngx_regex_exec(sn[i].regex->regex, host, NULL, 0);
+
+ if (n == NGX_REGEX_NO_MATCHED) {
+ continue;
+ }
+
+ if (n >= 0) {
+ hc = c->data;
+ hc->ssl_servername_regex = sn[i].regex;
+
+ *cscfp = sn[i].server;
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ ngx_regex_exec_n " failed: %i "
+ "on \"%V\" using \"%V\"",
+ n, host, &sn[i].regex->name);
+
+ return NGX_ERROR;
+ }
+
+ return NGX_DECLINED;
+ }
+
+#endif /* NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME */
+
+ for (i = 0; i < virtual_names->nregex; i++) {
+
+ n = ngx_http_regex_exec(r, sn[i].regex, host);
+
+ if (n == NGX_DECLINED) {
+ continue;
+ }
+
+ if (n == NGX_OK) {
+ *cscfp = sn[i].server;
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+ }
+ }
+
+#endif /* NGX_PCRE */
+
+ return NGX_DECLINED;
+}
+
+
+static void
+ngx_http_request_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *c;
+ ngx_http_request_t *r;
+ ngx_http_log_ctx_t *ctx;
+
+ c = ev->data;
+ r = c->data;
+
+ ctx = c->log->data;
+ ctx->current_request = r;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http run request: \"%V?%V\"", &r->uri, &r->args);
+
+ if (ev->write) {
+ r->write_event_handler(r);
+
+ } else {
+ r->read_event_handler(r);
+ }
+
+ ngx_http_run_posted_requests(c);
+}
+
+
+void
+ngx_http_run_posted_requests(ngx_connection_t *c)
+{
+ ngx_http_request_t *r;
+ ngx_http_log_ctx_t *ctx;
+ ngx_http_posted_request_t *pr;
+
+ for ( ;; ) {
+
+ if (c->destroyed) {
+ return;
+ }
+
+ r = c->data;
+ pr = r->main->posted_requests;
+
+ if (pr == NULL) {
+ return;
+ }
+
+ r->main->posted_requests = pr->next;
+
+ r = pr->request;
+
+ ctx = c->log->data;
+ ctx->current_request = r;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http posted request: \"%V?%V\"", &r->uri, &r->args);
+
+ r->write_event_handler(r);
+ }
+}
+
+
+ngx_int_t
+ngx_http_post_request(ngx_http_request_t *r, ngx_http_posted_request_t *pr)
+{
+ ngx_http_posted_request_t **p;
+
+ if (pr == NULL) {
+ pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));
+ if (pr == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ pr->request = r;
+ pr->next = NULL;
+
+ for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ }
+
+ *p = pr;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
+{
+ ngx_connection_t *c;
+ ngx_http_request_t *pr;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+
+ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http finalize request: %d, \"%V?%V\" a:%d, c:%d",
+ rc, &r->uri, &r->args, r == c->data, r->main->count);
+
+ if (rc == NGX_DONE) {
+ ngx_http_finalize_connection(r);
+ return;
+ }
+
+ if (rc == NGX_OK && r->filter_finalize) {
+ c->error = 1;
+ }
+
+ if (rc == NGX_DECLINED) {
+ r->content_handler = NULL;
+ r->write_event_handler = ngx_http_core_run_phases;
+ ngx_http_core_run_phases(r);
+ return;
+ }
+
+ if (r != r->main && r->post_subrequest) {
+ rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc);
+ }
+
+ if (rc == NGX_ERROR
+ || rc == NGX_HTTP_REQUEST_TIME_OUT
+ || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST
+ || c->error)
+ {
+ if (ngx_http_post_action(r) == NGX_OK) {
+ return;
+ }
+
+ if (r->main->blocked) {
+ r->write_event_handler = ngx_http_request_finalizer;
+ }
+
+ ngx_http_terminate_request(r, rc);
+ return;
+ }
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE
+ || rc == NGX_HTTP_CREATED
+ || rc == NGX_HTTP_NO_CONTENT)
+ {
+ if (rc == NGX_HTTP_CLOSE) {
+ ngx_http_terminate_request(r, rc);
+ return;
+ }
+
+ if (r == r->main) {
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+ }
+
+ c->read->handler = ngx_http_request_handler;
+ c->write->handler = ngx_http_request_handler;
+
+ ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc));
+ return;
+ }
+
+ if (r != r->main) {
+
+ if (r->buffered || r->postponed) {
+
+ if (ngx_http_set_write_handler(r) != NGX_OK) {
+ ngx_http_terminate_request(r, 0);
+ }
+
+ return;
+ }
+
+ pr = r->parent;
+
+ if (r == c->data) {
+
+ r->main->count--;
+ r->main->subrequests++;
+
+ if (!r->logged) {
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->log_subrequest) {
+ ngx_http_log_request(r);
+ }
+
+ r->logged = 1;
+
+ } else {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "subrequest: \"%V?%V\" logged again",
+ &r->uri, &r->args);
+ }
+
+ r->done = 1;
+
+ if (pr->postponed && pr->postponed->request == r) {
+ pr->postponed = pr->postponed->next;
+ }
+
+ c->data = pr;
+
+ } else {
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http finalize non-active request: \"%V?%V\"",
+ &r->uri, &r->args);
+
+ r->write_event_handler = ngx_http_request_finalizer;
+
+ if (r->waited) {
+ r->done = 1;
+ }
+ }
+
+ if (ngx_http_post_request(pr, NULL) != NGX_OK) {
+ r->main->count++;
+ ngx_http_terminate_request(r, 0);
+ return;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http wake parent request: \"%V?%V\"",
+ &pr->uri, &pr->args);
+
+ return;
+ }
+
+ if (r->buffered || c->buffered || r->postponed || r->blocked) {
+
+ if (ngx_http_set_write_handler(r) != NGX_OK) {
+ ngx_http_terminate_request(r, 0);
+ }
+
+ return;
+ }
+
+ if (r != c->data) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "http finalize non-active request: \"%V?%V\"",
+ &r->uri, &r->args);
+ return;
+ }
+
+ r->done = 1;
+ r->write_event_handler = ngx_http_request_empty_handler;
+
+ if (!r->post_action) {
+ r->request_complete = 1;
+ }
+
+ if (ngx_http_post_action(r) == NGX_OK) {
+ return;
+ }
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ if (c->write->timer_set) {
+ c->write->delayed = 0;
+ ngx_del_timer(c->write);
+ }
+
+ if (c->read->eof) {
+ ngx_http_close_request(r, 0);
+ return;
+ }
+
+ ngx_http_finalize_connection(r);
+}
+
+
+static void
+ngx_http_terminate_request(ngx_http_request_t *r, ngx_int_t rc)
+{
+ ngx_http_cleanup_t *cln;
+ ngx_http_request_t *mr;
+ ngx_http_ephemeral_t *e;
+
+ mr = r->main;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http terminate request count:%d", mr->count);
+
+ if (rc > 0 && (mr->headers_out.status == 0 || mr->connection->sent == 0)) {
+ mr->headers_out.status = rc;
+ }
+
+ cln = mr->cleanup;
+ mr->cleanup = NULL;
+
+ while (cln) {
+ if (cln->handler) {
+ cln->handler(cln->data);
+ }
+
+ cln = cln->next;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http terminate cleanup count:%d blk:%d",
+ mr->count, mr->blocked);
+
+ if (mr->write_event_handler) {
+
+ if (mr->blocked) {
+ return;
+ }
+
+ e = ngx_http_ephemeral(mr);
+ mr->posted_requests = NULL;
+ mr->write_event_handler = ngx_http_terminate_handler;
+ (void) ngx_http_post_request(mr, &e->terminal_posted_request);
+ return;
+ }
+
+ ngx_http_close_request(mr, rc);
+}
+
+
+static void
+ngx_http_terminate_handler(ngx_http_request_t *r)
+{
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http terminate handler count:%d", r->count);
+
+ r->count = 1;
+
+ ngx_http_close_request(r, 0);
+}
+
+
+static void
+ngx_http_finalize_connection(ngx_http_request_t *r)
+{
+ ngx_http_core_loc_conf_t *clcf;
+
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ ngx_http_close_request(r, 0);
+ return;
+ }
+#endif
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (r->main->count != 1) {
+
+ if (r->discard_body) {
+ r->read_event_handler = ngx_http_discarded_request_body_handler;
+ ngx_add_timer(r->connection->read, clcf->lingering_timeout);
+
+ if (r->lingering_time == 0) {
+ r->lingering_time = ngx_time()
+ + (time_t) (clcf->lingering_time / 1000);
+ }
+ }
+
+ ngx_http_close_request(r, 0);
+ return;
+ }
+
+ if (!ngx_terminate
+ && !ngx_exiting
+ && r->keepalive
+ && clcf->keepalive_timeout > 0)
+ {
+ ngx_http_set_keepalive(r);
+ return;
+ }
+
+ if (clcf->lingering_close == NGX_HTTP_LINGERING_ALWAYS
+ || (clcf->lingering_close == NGX_HTTP_LINGERING_ON
+ && (r->lingering_close
+ || r->header_in->pos < r->header_in->last
+ || r->connection->read->ready)))
+ {
+ ngx_http_set_lingering_close(r);
+ return;
+ }
+
+ ngx_http_close_request(r, 0);
+}
+
+
+static ngx_int_t
+ngx_http_set_write_handler(ngx_http_request_t *r)
+{
+ ngx_event_t *wev;
+ ngx_http_core_loc_conf_t *clcf;
+
+ r->http_state = NGX_HTTP_WRITING_REQUEST_STATE;
+
+ r->read_event_handler = r->discard_body ?
+ ngx_http_discarded_request_body_handler:
+ ngx_http_test_reading;
+ r->write_event_handler = ngx_http_writer;
+
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ return NGX_OK;
+ }
+#endif
+
+ wev = r->connection->write;
+
+ if (wev->ready && wev->delayed) {
+ return NGX_OK;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+ if (!wev->delayed) {
+ ngx_add_timer(wev, clcf->send_timeout);
+ }
+
+ if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
+ ngx_http_close_request(r, 0);
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_writer(ngx_http_request_t *r)
+{
+ int rc;
+ ngx_event_t *wev;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ wev = c->write;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,
+ "http writer handler: \"%V?%V\"", &r->uri, &r->args);
+
+ clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);
+
+ if (wev->timedout) {
+ if (!wev->delayed) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "client timed out");
+ c->timedout = 1;
+
+ ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ wev->timedout = 0;
+ wev->delayed = 0;
+
+ if (!wev->ready) {
+ ngx_add_timer(wev, clcf->send_timeout);
+
+ if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
+ ngx_http_close_request(r, 0);
+ }
+
+ return;
+ }
+
+ }
+
+ if (wev->delayed || r->aio) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0,
+ "http writer delayed");
+
+ if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
+ ngx_http_close_request(r, 0);
+ }
+
+ return;
+ }
+
+ rc = ngx_http_output_filter(r, NULL);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http writer output filter: %d, \"%V?%V\"",
+ rc, &r->uri, &r->args);
+
+ if (rc == NGX_ERROR) {
+ ngx_http_finalize_request(r, rc);
+ return;
+ }
+
+ if (r->buffered || r->postponed || (r == r->main && c->buffered)) {
+
+ if (!wev->delayed) {
+ ngx_add_timer(wev, clcf->send_timeout);
+ }
+
+ if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
+ ngx_http_close_request(r, 0);
+ }
+
+ return;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,
+ "http writer done: \"%V?%V\"", &r->uri, &r->args);
+
+ r->write_event_handler = ngx_http_request_empty_handler;
+
+ ngx_http_finalize_request(r, rc);
+}
+
+
+static void
+ngx_http_request_finalizer(ngx_http_request_t *r)
+{
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http finalizer done: \"%V?%V\"", &r->uri, &r->args);
+
+ ngx_http_finalize_request(r, 0);
+}
+
+
+void
+ngx_http_block_reading(ngx_http_request_t *r)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http reading blocked");
+
+ /* aio does not call this handler */
+
+ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT)
+ && r->connection->read->active)
+ {
+ if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) != NGX_OK) {
+ ngx_http_close_request(r, 0);
+ }
+ }
+}
+
+
+void
+ngx_http_test_reading(ngx_http_request_t *r)
+{
+ int n;
+ char buf[1];
+ ngx_err_t err;
+ ngx_event_t *rev;
+ ngx_connection_t *c;
+
+ c = r->connection;
+ rev = c->read;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http test reading");
+
+#if (NGX_HTTP_SPDY)
+
+ if (r->spdy_stream) {
+ if (c->error) {
+ err = 0;
+ goto closed;
+ }
+
+ return;
+ }
+
+#endif
+
+#if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+
+ if (!rev->pending_eof) {
+ return;
+ }
+
+ rev->eof = 1;
+ c->error = 1;
+ err = rev->kq_errno;
+
+ goto closed;
+ }
+
+#endif
+
+#if (NGX_HAVE_EPOLLRDHUP)
+
+ if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && rev->pending_eof) {
+ socklen_t len;
+
+ rev->eof = 1;
+ c->error = 1;
+
+ err = 0;
+ len = sizeof(ngx_err_t);
+
+ /*
+ * BSDs and Linux return 0 and set a pending error in err
+ * Solaris returns -1 and sets errno
+ */
+
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)
+ == -1)
+ {
+ err = ngx_socket_errno;
+ }
+
+ goto closed;
+ }
+
+#endif
+
+ n = recv(c->fd, buf, 1, MSG_PEEK);
+
+ if (n == 0) {
+ rev->eof = 1;
+ c->error = 1;
+ err = 0;
+
+ goto closed;
+
+ } else if (n == -1) {
+ err = ngx_socket_errno;
+
+ if (err != NGX_EAGAIN) {
+ rev->eof = 1;
+ c->error = 1;
+
+ goto closed;
+ }
+ }
+
+ /* aio does not call this handler */
+
+ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) {
+
+ if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
+ ngx_http_close_request(r, 0);
+ }
+ }
+
+ return;
+
+closed:
+
+ if (err) {
+ rev->error = 1;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, c->log, err,
+ "client prematurely closed connection");
+
+ ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);
+}
+
+
+static void
+ngx_http_set_keepalive(ngx_http_request_t *r)
+{
+ int tcp_nodelay;
+ ngx_int_t i;
+ ngx_buf_t *b, *f;
+ ngx_event_t *rev, *wev;
+ ngx_connection_t *c;
+ ngx_http_connection_t *hc;
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ rev = c->read;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "set http keepalive handler");
+
+ if (r->discard_body) {
+ r->write_event_handler = ngx_http_request_empty_handler;
+ r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);
+ ngx_add_timer(rev, clcf->lingering_timeout);
+ return;
+ }
+
+ c->log->action = "closing request";
+
+ hc = r->http_connection;
+ b = r->header_in;
+
+ if (b->pos < b->last) {
+
+ /* the pipelined request */
+
+ if (b != c->buffer) {
+
+ /*
+ * If the large header buffers were allocated while the previous
+ * request processing then we do not use c->buffer for
+ * the pipelined request (see ngx_http_create_request()).
+ *
+ * Now we would move the large header buffers to the free list.
+ */
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ if (hc->free == NULL) {
+ hc->free = ngx_palloc(c->pool,
+ cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));
+
+ if (hc->free == NULL) {
+ ngx_http_close_request(r, 0);
+ return;
+ }
+ }
+
+ for (i = 0; i < hc->nbusy - 1; i++) {
+ f = hc->busy[i];
+ hc->free[hc->nfree++] = f;
+ f->pos = f->start;
+ f->last = f->start;
+ }
+
+ hc->busy[0] = b;
+ hc->nbusy = 1;
+ }
+ }
+
+ /* guard against recursive call from ngx_http_finalize_connection() */
+ r->keepalive = 0;
+
+ ngx_http_free_request(r, 0);
+
+ c->data = hc;
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ wev = c->write;
+ wev->handler = ngx_http_empty_handler;
+
+ if (b->pos < b->last) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "pipelined request");
+
+ c->log->action = "reading client pipelined request line";
+
+ r = ngx_http_create_request(c);
+ if (r == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ r->pipeline = 1;
+
+ c->data = r;
+
+ c->sent = 0;
+ c->destroyed = 0;
+
+ if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+
+ rev->handler = ngx_http_process_request_line;
+ ngx_post_event(rev, &ngx_posted_events);
+ return;
+ }
+
+ /*
+ * To keep a memory footprint as small as possible for an idle keepalive
+ * connection we try to free c->buffer's memory if it was allocated outside
+ * the c->pool. The large header buffers are always allocated outside the
+ * c->pool and are freed too.
+ */
+
+ b = c->buffer;
+
+ if (ngx_pfree(c->pool, b->start) == NGX_OK) {
+
+ /*
+ * the special note for ngx_http_keepalive_handler() that
+ * c->buffer's memory was freed
+ */
+
+ b->pos = NULL;
+
+ } else {
+ b->pos = b->start;
+ b->last = b->start;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc free: %p %d",
+ hc->free, hc->nfree);
+
+ if (hc->free) {
+ for (i = 0; i < hc->nfree; i++) {
+ ngx_pfree(c->pool, hc->free[i]->start);
+ hc->free[i] = NULL;
+ }
+
+ hc->nfree = 0;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "hc busy: %p %d",
+ hc->busy, hc->nbusy);
+
+ if (hc->busy) {
+ for (i = 0; i < hc->nbusy; i++) {
+ ngx_pfree(c->pool, hc->busy[i]->start);
+ hc->busy[i] = NULL;
+ }
+
+ hc->nbusy = 0;
+ }
+
+#if (NGX_HTTP_SSL)
+ if (c->ssl) {
+ ngx_ssl_free_buffer(c);
+ }
+#endif
+
+ rev->handler = ngx_http_keepalive_handler;
+
+ if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
+ if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {
+ ngx_http_close_connection(c);
+ return;
+ }
+ }
+
+ c->log->action = "keepalive";
+
+ if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {
+ if (ngx_tcp_push(c->fd) == -1) {
+ ngx_connection_error(c, ngx_socket_errno, ngx_tcp_push_n " failed");
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;
+ tcp_nodelay = ngx_tcp_nodelay_and_tcp_nopush ? 1 : 0;
+
+ } else {
+ tcp_nodelay = 1;
+ }
+
+ if (tcp_nodelay
+ && clcf->tcp_nodelay
+ && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET)
+ {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay");
+
+ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &tcp_nodelay, sizeof(int))
+ == -1)
+ {
+#if (NGX_SOLARIS)
+ /* Solaris returns EINVAL if a socket has been shut down */
+ c->log_error = NGX_ERROR_IGNORE_EINVAL;
+#endif
+
+ ngx_connection_error(c, ngx_socket_errno,
+ "setsockopt(TCP_NODELAY) failed");
+
+ c->log_error = NGX_ERROR_INFO;
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->tcp_nodelay = NGX_TCP_NODELAY_SET;
+ }
+
+#if 0
+ /* if ngx_http_request_t was freed then we need some other place */
+ r->http_state = NGX_HTTP_KEEPALIVE_STATE;
+#endif
+
+ c->idle = 1;
+ ngx_reusable_connection(c, 1);
+
+ ngx_add_timer(rev, clcf->keepalive_timeout);
+
+ if (rev->ready) {
+ ngx_post_event(rev, &ngx_posted_events);
+ }
+}
+
+
+static void
+ngx_http_keepalive_handler(ngx_event_t *rev)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_connection_t *c;
+
+ c = rev->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http keepalive handler");
+
+ if (rev->timedout || c->close) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+#if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ if (rev->pending_eof) {
+ c->log->handler = NULL;
+ ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,
+ "kevent() reported that client %V closed "
+ "keepalive connection", &c->addr_text);
+#if (NGX_HTTP_SSL)
+ if (c->ssl) {
+ c->ssl->no_send_shutdown = 1;
+ }
+#endif
+ ngx_http_close_connection(c);
+ return;
+ }
+ }
+
+#endif
+
+ b = c->buffer;
+ size = b->end - b->start;
+
+ if (b->pos == NULL) {
+
+ /*
+ * The c->buffer's memory was freed by ngx_http_set_keepalive().
+ * However, the c->buffer->start and c->buffer->end were not changed
+ * to keep the buffer size.
+ */
+
+ b->pos = ngx_palloc(c->pool, size);
+ if (b->pos == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ b->start = b->pos;
+ b->last = b->pos;
+ b->end = b->pos + size;
+ }
+
+ /*
+ * MSIE closes a keepalive connection with RST flag
+ * so we ignore ECONNRESET here.
+ */
+
+ c->log_error = NGX_ERROR_IGNORE_ECONNRESET;
+ ngx_set_socket_errno(0);
+
+ n = c->recv(c, b->last, size);
+ c->log_error = NGX_ERROR_INFO;
+
+ if (n == NGX_AGAIN) {
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ /*
+ * Like ngx_http_set_keepalive() we are trying to not hold
+ * c->buffer's memory for a keepalive connection.
+ */
+
+ if (ngx_pfree(c->pool, b->start) == NGX_OK) {
+
+ /*
+ * the special note that c->buffer's memory was freed
+ */
+
+ b->pos = NULL;
+ }
+
+ return;
+ }
+
+ if (n == NGX_ERROR) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->log->handler = NULL;
+
+ if (n == 0) {
+ ngx_log_error(NGX_LOG_INFO, c->log, ngx_socket_errno,
+ "client %V closed keepalive connection", &c->addr_text);
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ b->last += n;
+
+ c->log->handler = ngx_http_log_error;
+ c->log->action = "reading client request line";
+
+ c->idle = 0;
+ ngx_reusable_connection(c, 0);
+
+ c->data = ngx_http_create_request(c);
+ if (c->data == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->sent = 0;
+ c->destroyed = 0;
+
+ ngx_del_timer(rev);
+
+ rev->handler = ngx_http_process_request_line;
+ ngx_http_process_request_line(rev);
+}
+
+
+static void
+ngx_http_set_lingering_close(ngx_http_request_t *r)
+{
+ ngx_event_t *rev, *wev;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ rev = c->read;
+ rev->handler = ngx_http_lingering_close_handler;
+
+ r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);
+ ngx_add_timer(rev, clcf->lingering_timeout);
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_close_request(r, 0);
+ return;
+ }
+
+ wev = c->write;
+ wev->handler = ngx_http_empty_handler;
+
+ if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {
+ if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {
+ ngx_http_close_request(r, 0);
+ return;
+ }
+ }
+
+ if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) {
+ ngx_connection_error(c, ngx_socket_errno,
+ ngx_shutdown_socket_n " failed");
+ ngx_http_close_request(r, 0);
+ return;
+ }
+
+ if (rev->ready) {
+ ngx_http_lingering_close_handler(rev);
+ }
+}
+
+
+static void
+ngx_http_lingering_close_handler(ngx_event_t *rev)
+{
+ ssize_t n;
+ ngx_msec_t timer;
+ ngx_connection_t *c;
+ ngx_http_request_t *r;
+ ngx_http_core_loc_conf_t *clcf;
+ u_char buffer[NGX_HTTP_LINGERING_BUFFER_SIZE];
+
+ c = rev->data;
+ r = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http lingering close handler");
+
+ if (rev->timedout) {
+ ngx_http_close_request(r, 0);
+ return;
+ }
+
+ timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time();
+ if ((ngx_msec_int_t) timer <= 0) {
+ ngx_http_close_request(r, 0);
+ return;
+ }
+
+ do {
+ n = c->recv(c, buffer, NGX_HTTP_LINGERING_BUFFER_SIZE);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lingering read: %d", n);
+
+ if (n == NGX_ERROR || n == 0) {
+ ngx_http_close_request(r, 0);
+ return;
+ }
+
+ } while (rev->ready);
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_close_request(r, 0);
+ return;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ timer *= 1000;
+
+ if (timer > clcf->lingering_timeout) {
+ timer = clcf->lingering_timeout;
+ }
+
+ ngx_add_timer(rev, timer);
+}
+
+
+void
+ngx_http_empty_handler(ngx_event_t *wev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, "http empty handler");
+
+ return;
+}
+
+
+void
+ngx_http_request_empty_handler(ngx_http_request_t *r)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http request empty handler");
+
+ return;
+}
+
+
+ngx_int_t
+ngx_http_send_special(ngx_http_request_t *r, ngx_uint_t flags)
+{
+ ngx_buf_t *b;
+ ngx_chain_t out;
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (flags & NGX_HTTP_LAST) {
+
+ if (r == r->main && !r->post_action) {
+ b->last_buf = 1;
+
+ } else {
+ b->sync = 1;
+ b->last_in_chain = 1;
+ }
+ }
+
+ if (flags & NGX_HTTP_FLUSH) {
+ b->flush = 1;
+ }
+
+ out.buf = b;
+ out.next = NULL;
+
+ return ngx_http_output_filter(r, &out);
+}
+
+
+static ngx_int_t
+ngx_http_post_action(ngx_http_request_t *r)
+{
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->post_action.data == NULL) {
+ return NGX_DECLINED;
+ }
+
+ if (r->post_action && r->uri_changes == 0) {
+ return NGX_DECLINED;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "post action: \"%V\"", &clcf->post_action);
+
+ r->main->count--;
+
+ r->http_version = NGX_HTTP_VERSION_9;
+ r->header_only = 1;
+ r->post_action = 1;
+
+ r->read_event_handler = ngx_http_block_reading;
+
+ if (clcf->post_action.data[0] == '/') {
+ ngx_http_internal_redirect(r, &clcf->post_action, NULL);
+
+ } else {
+ ngx_http_named_location(r, &clcf->post_action);
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc)
+{
+ ngx_connection_t *c;
+
+ r = r->main;
+ c = r->connection;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http request count:%d blk:%d", r->count, r->blocked);
+
+ if (r->count == 0) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0, "http request count is zero");
+ }
+
+ r->count--;
+
+ if (r->count || r->blocked) {
+ return;
+ }
+
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ ngx_http_spdy_close_stream(r->spdy_stream, rc);
+ return;
+ }
+#endif
+
+ ngx_http_free_request(r, rc);
+ ngx_http_close_connection(c);
+}
+
+
+void
+ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc)
+{
+ ngx_log_t *log;
+ ngx_pool_t *pool;
+ struct linger linger;
+ ngx_http_cleanup_t *cln;
+ ngx_http_log_ctx_t *ctx;
+ ngx_http_core_loc_conf_t *clcf;
+
+ log = r->connection->log;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http close request");
+
+ if (r->pool == NULL) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0, "http request already closed");
+ return;
+ }
+
+ cln = r->cleanup;
+ r->cleanup = NULL;
+
+ while (cln) {
+ if (cln->handler) {
+ cln->handler(cln->data);
+ }
+
+ cln = cln->next;
+ }
+
+#if (NGX_STAT_STUB)
+
+ if (r->stat_reading) {
+ (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
+ }
+
+ if (r->stat_writing) {
+ (void) ngx_atomic_fetch_add(ngx_stat_writing, -1);
+ }
+
+#endif
+
+ if (rc > 0 && (r->headers_out.status == 0 || r->connection->sent == 0)) {
+ r->headers_out.status = rc;
+ }
+
+ log->action = "logging request";
+
+ ngx_http_log_request(r);
+
+ log->action = "closing request";
+
+ if (r->connection->timedout) {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->reset_timedout_connection) {
+ linger.l_onoff = 1;
+ linger.l_linger = 0;
+
+ if (setsockopt(r->connection->fd, SOL_SOCKET, SO_LINGER,
+ (const void *) &linger, sizeof(struct linger)) == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
+ "setsockopt(SO_LINGER) failed");
+ }
+ }
+ }
+
+ /* the various request strings were allocated from r->pool */
+ ctx = log->data;
+ ctx->request = NULL;
+
+ r->request_line.len = 0;
+
+ r->connection->destroyed = 1;
+
+ /*
+ * Setting r->pool to NULL will increase probability to catch double close
+ * of request since the request object is allocated from its own pool.
+ */
+
+ pool = r->pool;
+ r->pool = NULL;
+
+ ngx_destroy_pool(pool);
+}
+
+
+static void
+ngx_http_log_request(ngx_http_request_t *r)
+{
+ ngx_uint_t i, n;
+ ngx_http_handler_pt *log_handler;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts;
+ n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts;
+
+ for (i = 0; i < n; i++) {
+ log_handler[i](r);
+ }
+}
+
+
+void
+ngx_http_close_connection(ngx_connection_t *c)
+{
+ ngx_pool_t *pool;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "close http connection: %d", c->fd);
+
+#if (NGX_HTTP_SSL)
+
+ if (c->ssl) {
+ if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+ c->ssl->handler = ngx_http_close_connection;
+ return;
+ }
+ }
+
+#endif
+
+#if (NGX_STAT_STUB)
+ (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
+#endif
+
+ c->destroyed = 1;
+
+ pool = c->pool;
+
+ ngx_close_connection(c);
+
+ ngx_destroy_pool(pool);
+}
+
+
+static u_char *
+ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ u_char *p;
+ ngx_http_request_t *r;
+ ngx_http_log_ctx_t *ctx;
+
+ if (log->action) {
+ p = ngx_snprintf(buf, len, " while %s", log->action);
+ len -= p - buf;
+ buf = p;
+ }
+
+ ctx = log->data;
+
+ p = ngx_snprintf(buf, len, ", client: %V", &ctx->connection->addr_text);
+ len -= p - buf;
+
+ r = ctx->request;
+
+ if (r) {
+ return r->log_handler(r, ctx->current_request, p, len);
+
+ } else {
+ p = ngx_snprintf(p, len, ", server: %V",
+ &ctx->connection->listening->addr_text);
+ }
+
+ return p;
+}
+
+
+static u_char *
+ngx_http_log_error_handler(ngx_http_request_t *r, ngx_http_request_t *sr,
+ u_char *buf, size_t len)
+{
+ char *uri_separator;
+ u_char *p;
+ ngx_http_upstream_t *u;
+ ngx_http_core_srv_conf_t *cscf;
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ p = ngx_snprintf(buf, len, ", server: %V", &cscf->server_name);
+ len -= p - buf;
+ buf = p;
+
+ if (r->request_line.data == NULL && r->request_start) {
+ for (p = r->request_start; p < r->header_in->last; p++) {
+ if (*p == CR || *p == LF) {
+ break;
+ }
+ }
+
+ r->request_line.len = p - r->request_start;
+ r->request_line.data = r->request_start;
+ }
+
+ if (r->request_line.len) {
+ p = ngx_snprintf(buf, len, ", request: \"%V\"", &r->request_line);
+ len -= p - buf;
+ buf = p;
+ }
+
+ if (r != sr) {
+ p = ngx_snprintf(buf, len, ", subrequest: \"%V\"", &sr->uri);
+ len -= p - buf;
+ buf = p;
+ }
+
+ u = sr->upstream;
+
+ if (u && u->peer.name) {
+
+ uri_separator = "";
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ if (u->peer.sockaddr && u->peer.sockaddr->sa_family == AF_UNIX) {
+ uri_separator = ":";
+ }
+#endif
+
+ p = ngx_snprintf(buf, len, ", upstream: \"%V%V%s%V\"",
+ &u->schema, u->peer.name,
+ uri_separator, &u->uri);
+ len -= p - buf;
+ buf = p;
+ }
+
+ if (r->headers_in.host) {
+ p = ngx_snprintf(buf, len, ", host: \"%V\"",
+ &r->headers_in.host->value);
+ len -= p - buf;
+ buf = p;
+ }
+
+ if (r->headers_in.referer) {
+ p = ngx_snprintf(buf, len, ", referrer: \"%V\"",
+ &r->headers_in.referer->value);
+ buf = p;
+ }
+
+ return buf;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_request.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_request.h
new file mode 100644
index 00000000000..f6ea6fb56e7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_request.h
@@ -0,0 +1,598 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_REQUEST_H_INCLUDED_
+#define _NGX_HTTP_REQUEST_H_INCLUDED_
+
+
+#define NGX_HTTP_MAX_URI_CHANGES 10
+#define NGX_HTTP_MAX_SUBREQUESTS 200
+
+/* must be 2^n */
+#define NGX_HTTP_LC_HEADER_LEN 32
+
+
+#define NGX_HTTP_DISCARD_BUFFER_SIZE 4096
+#define NGX_HTTP_LINGERING_BUFFER_SIZE 4096
+
+
+#define NGX_HTTP_VERSION_9 9
+#define NGX_HTTP_VERSION_10 1000
+#define NGX_HTTP_VERSION_11 1001
+
+#define NGX_HTTP_UNKNOWN 0x0001
+#define NGX_HTTP_GET 0x0002
+#define NGX_HTTP_HEAD 0x0004
+#define NGX_HTTP_POST 0x0008
+#define NGX_HTTP_PUT 0x0010
+#define NGX_HTTP_DELETE 0x0020
+#define NGX_HTTP_MKCOL 0x0040
+#define NGX_HTTP_COPY 0x0080
+#define NGX_HTTP_MOVE 0x0100
+#define NGX_HTTP_OPTIONS 0x0200
+#define NGX_HTTP_PROPFIND 0x0400
+#define NGX_HTTP_PROPPATCH 0x0800
+#define NGX_HTTP_LOCK 0x1000
+#define NGX_HTTP_UNLOCK 0x2000
+#define NGX_HTTP_PATCH 0x4000
+#define NGX_HTTP_TRACE 0x8000
+
+#define NGX_HTTP_CONNECTION_CLOSE 1
+#define NGX_HTTP_CONNECTION_KEEP_ALIVE 2
+
+
+#define NGX_NONE 1
+
+
+#define NGX_HTTP_PARSE_HEADER_DONE 1
+
+#define NGX_HTTP_CLIENT_ERROR 10
+#define NGX_HTTP_PARSE_INVALID_METHOD 10
+#define NGX_HTTP_PARSE_INVALID_REQUEST 11
+#define NGX_HTTP_PARSE_INVALID_09_METHOD 12
+
+#define NGX_HTTP_PARSE_INVALID_HEADER 13
+
+
+/* unused 1 */
+#define NGX_HTTP_SUBREQUEST_IN_MEMORY 2
+#define NGX_HTTP_SUBREQUEST_WAITED 4
+#define NGX_HTTP_LOG_UNSAFE 8
+
+
+#define NGX_HTTP_CONTINUE 100
+#define NGX_HTTP_SWITCHING_PROTOCOLS 101
+#define NGX_HTTP_PROCESSING 102
+
+#define NGX_HTTP_OK 200
+#define NGX_HTTP_CREATED 201
+#define NGX_HTTP_ACCEPTED 202
+#define NGX_HTTP_NO_CONTENT 204
+#define NGX_HTTP_PARTIAL_CONTENT 206
+
+#define NGX_HTTP_SPECIAL_RESPONSE 300
+#define NGX_HTTP_MOVED_PERMANENTLY 301
+#define NGX_HTTP_MOVED_TEMPORARILY 302
+#define NGX_HTTP_SEE_OTHER 303
+#define NGX_HTTP_NOT_MODIFIED 304
+#define NGX_HTTP_TEMPORARY_REDIRECT 307
+
+#define NGX_HTTP_BAD_REQUEST 400
+#define NGX_HTTP_UNAUTHORIZED 401
+#define NGX_HTTP_FORBIDDEN 403
+#define NGX_HTTP_NOT_FOUND 404
+#define NGX_HTTP_NOT_ALLOWED 405
+#define NGX_HTTP_REQUEST_TIME_OUT 408
+#define NGX_HTTP_CONFLICT 409
+#define NGX_HTTP_LENGTH_REQUIRED 411
+#define NGX_HTTP_PRECONDITION_FAILED 412
+#define NGX_HTTP_REQUEST_ENTITY_TOO_LARGE 413
+#define NGX_HTTP_REQUEST_URI_TOO_LARGE 414
+#define NGX_HTTP_UNSUPPORTED_MEDIA_TYPE 415
+#define NGX_HTTP_RANGE_NOT_SATISFIABLE 416
+
+
+/* Our own HTTP codes */
+
+/* The special code to close connection without any response */
+#define NGX_HTTP_CLOSE 444
+
+#define NGX_HTTP_NGINX_CODES 494
+
+#define NGX_HTTP_REQUEST_HEADER_TOO_LARGE 494
+
+#define NGX_HTTPS_CERT_ERROR 495
+#define NGX_HTTPS_NO_CERT 496
+
+/*
+ * We use the special code for the plain HTTP requests that are sent to
+ * HTTPS port to distinguish it from 4XX in an error page redirection
+ */
+#define NGX_HTTP_TO_HTTPS 497
+
+/* 498 is the canceled code for the requests with invalid host name */
+
+/*
+ * HTTP does not define the code for the case when a client closed
+ * the connection while we are processing its request so we introduce
+ * own code to log such situation when a client has closed the connection
+ * before we even try to send the HTTP header to it
+ */
+#define NGX_HTTP_CLIENT_CLOSED_REQUEST 499
+
+
+#define NGX_HTTP_INTERNAL_SERVER_ERROR 500
+#define NGX_HTTP_NOT_IMPLEMENTED 501
+#define NGX_HTTP_BAD_GATEWAY 502
+#define NGX_HTTP_SERVICE_UNAVAILABLE 503
+#define NGX_HTTP_GATEWAY_TIME_OUT 504
+#define NGX_HTTP_INSUFFICIENT_STORAGE 507
+
+
+#define NGX_HTTP_LOWLEVEL_BUFFERED 0xf0
+#define NGX_HTTP_WRITE_BUFFERED 0x10
+#define NGX_HTTP_GZIP_BUFFERED 0x20
+#define NGX_HTTP_SSI_BUFFERED 0x01
+#define NGX_HTTP_SUB_BUFFERED 0x02
+#define NGX_HTTP_COPY_BUFFERED 0x04
+
+
+typedef enum {
+ NGX_HTTP_INITING_REQUEST_STATE = 0,
+ NGX_HTTP_READING_REQUEST_STATE,
+ NGX_HTTP_PROCESS_REQUEST_STATE,
+
+ NGX_HTTP_CONNECT_UPSTREAM_STATE,
+ NGX_HTTP_WRITING_UPSTREAM_STATE,
+ NGX_HTTP_READING_UPSTREAM_STATE,
+
+ NGX_HTTP_WRITING_REQUEST_STATE,
+ NGX_HTTP_LINGERING_CLOSE_STATE,
+ NGX_HTTP_KEEPALIVE_STATE
+} ngx_http_state_e;
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_uint_t offset;
+ ngx_http_header_handler_pt handler;
+} ngx_http_header_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_uint_t offset;
+} ngx_http_header_out_t;
+
+
+typedef struct {
+ ngx_list_t headers;
+
+ ngx_table_elt_t *host;
+ ngx_table_elt_t *connection;
+ ngx_table_elt_t *if_modified_since;
+ ngx_table_elt_t *if_unmodified_since;
+ ngx_table_elt_t *if_match;
+ ngx_table_elt_t *if_none_match;
+ ngx_table_elt_t *user_agent;
+ ngx_table_elt_t *referer;
+ ngx_table_elt_t *content_length;
+ ngx_table_elt_t *content_type;
+
+ ngx_table_elt_t *range;
+ ngx_table_elt_t *if_range;
+
+ ngx_table_elt_t *transfer_encoding;
+ ngx_table_elt_t *expect;
+ ngx_table_elt_t *upgrade;
+
+#if (NGX_HTTP_GZIP)
+ ngx_table_elt_t *accept_encoding;
+ ngx_table_elt_t *via;
+#endif
+
+ ngx_table_elt_t *authorization;
+
+ ngx_table_elt_t *keep_alive;
+
+#if (NGX_HTTP_X_FORWARDED_FOR)
+ ngx_array_t x_forwarded_for;
+#endif
+
+#if (NGX_HTTP_REALIP)
+ ngx_table_elt_t *x_real_ip;
+#endif
+
+#if (NGX_HTTP_HEADERS)
+ ngx_table_elt_t *accept;
+ ngx_table_elt_t *accept_language;
+#endif
+
+#if (NGX_HTTP_DAV)
+ ngx_table_elt_t *depth;
+ ngx_table_elt_t *destination;
+ ngx_table_elt_t *overwrite;
+ ngx_table_elt_t *date;
+#endif
+
+ ngx_str_t user;
+ ngx_str_t passwd;
+
+ ngx_array_t cookies;
+
+ ngx_str_t server;
+ off_t content_length_n;
+ time_t keep_alive_n;
+
+ unsigned connection_type:2;
+ unsigned chunked:1;
+ unsigned msie:1;
+ unsigned msie6:1;
+ unsigned opera:1;
+ unsigned gecko:1;
+ unsigned chrome:1;
+ unsigned safari:1;
+ unsigned konqueror:1;
+} ngx_http_headers_in_t;
+
+
+typedef struct {
+ ngx_list_t headers;
+
+ ngx_uint_t status;
+ ngx_str_t status_line;
+
+ ngx_table_elt_t *server;
+ ngx_table_elt_t *date;
+ ngx_table_elt_t *content_length;
+ ngx_table_elt_t *content_encoding;
+ ngx_table_elt_t *location;
+ ngx_table_elt_t *refresh;
+ ngx_table_elt_t *last_modified;
+ ngx_table_elt_t *content_range;
+ ngx_table_elt_t *accept_ranges;
+ ngx_table_elt_t *www_authenticate;
+ ngx_table_elt_t *expires;
+ ngx_table_elt_t *etag;
+
+ ngx_str_t *override_charset;
+
+ size_t content_type_len;
+ ngx_str_t content_type;
+ ngx_str_t charset;
+ u_char *content_type_lowcase;
+ ngx_uint_t content_type_hash;
+
+ ngx_array_t cache_control;
+
+ off_t content_length_n;
+ time_t date_time;
+ time_t last_modified_time;
+} ngx_http_headers_out_t;
+
+
+typedef void (*ngx_http_client_body_handler_pt)(ngx_http_request_t *r);
+
+typedef struct {
+ ngx_temp_file_t *temp_file;
+ ngx_chain_t *bufs;
+ ngx_buf_t *buf;
+ off_t rest;
+ ngx_chain_t *free;
+ ngx_chain_t *busy;
+ ngx_http_chunked_t *chunked;
+ ngx_http_client_body_handler_pt post_handler;
+} ngx_http_request_body_t;
+
+
+typedef struct ngx_http_addr_conf_s ngx_http_addr_conf_t;
+
+typedef struct {
+ ngx_http_addr_conf_t *addr_conf;
+ ngx_http_conf_ctx_t *conf_ctx;
+
+#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)
+ ngx_str_t *ssl_servername;
+#if (NGX_PCRE)
+ ngx_http_regex_t *ssl_servername_regex;
+#endif
+#endif
+
+ ngx_buf_t **busy;
+ ngx_int_t nbusy;
+
+ ngx_buf_t **free;
+ ngx_int_t nfree;
+
+#if (NGX_HTTP_SSL)
+ unsigned ssl:1;
+#endif
+ unsigned proxy_protocol:1;
+} ngx_http_connection_t;
+
+
+typedef void (*ngx_http_cleanup_pt)(void *data);
+
+typedef struct ngx_http_cleanup_s ngx_http_cleanup_t;
+
+struct ngx_http_cleanup_s {
+ ngx_http_cleanup_pt handler;
+ void *data;
+ ngx_http_cleanup_t *next;
+};
+
+
+typedef ngx_int_t (*ngx_http_post_subrequest_pt)(ngx_http_request_t *r,
+ void *data, ngx_int_t rc);
+
+typedef struct {
+ ngx_http_post_subrequest_pt handler;
+ void *data;
+} ngx_http_post_subrequest_t;
+
+
+typedef struct ngx_http_postponed_request_s ngx_http_postponed_request_t;
+
+struct ngx_http_postponed_request_s {
+ ngx_http_request_t *request;
+ ngx_chain_t *out;
+ ngx_http_postponed_request_t *next;
+};
+
+
+typedef struct ngx_http_posted_request_s ngx_http_posted_request_t;
+
+struct ngx_http_posted_request_s {
+ ngx_http_request_t *request;
+ ngx_http_posted_request_t *next;
+};
+
+
+typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
+typedef void (*ngx_http_event_handler_pt)(ngx_http_request_t *r);
+
+
+struct ngx_http_request_s {
+ uint32_t signature; /* "HTTP" */
+
+ ngx_connection_t *connection;
+
+ void **ctx;
+ void **main_conf;
+ void **srv_conf;
+ void **loc_conf;
+
+ ngx_http_event_handler_pt read_event_handler;
+ ngx_http_event_handler_pt write_event_handler;
+
+#if (NGX_HTTP_CACHE)
+ ngx_http_cache_t *cache;
+#endif
+
+ ngx_http_upstream_t *upstream;
+ ngx_array_t *upstream_states;
+ /* of ngx_http_upstream_state_t */
+
+ ngx_pool_t *pool;
+ ngx_buf_t *header_in;
+
+ ngx_http_headers_in_t headers_in;
+ ngx_http_headers_out_t headers_out;
+
+ ngx_http_request_body_t *request_body;
+
+ time_t lingering_time;
+ time_t start_sec;
+ ngx_msec_t start_msec;
+
+ ngx_uint_t method;
+ ngx_uint_t http_version;
+
+ ngx_str_t request_line;
+ ngx_str_t uri;
+ ngx_str_t args;
+ ngx_str_t exten;
+ ngx_str_t unparsed_uri;
+
+ ngx_str_t method_name;
+ ngx_str_t http_protocol;
+
+ ngx_chain_t *out;
+ ngx_http_request_t *main;
+ ngx_http_request_t *parent;
+ ngx_http_postponed_request_t *postponed;
+ ngx_http_post_subrequest_t *post_subrequest;
+ ngx_http_posted_request_t *posted_requests;
+
+ ngx_int_t phase_handler;
+ ngx_http_handler_pt content_handler;
+ ngx_uint_t access_code;
+
+ ngx_http_variable_value_t *variables;
+
+#if (NGX_PCRE)
+ ngx_uint_t ncaptures;
+ int *captures;
+ u_char *captures_data;
+#endif
+
+ size_t limit_rate;
+ size_t limit_rate_after;
+
+ /* used to learn the Apache compatible response length without a header */
+ size_t header_size;
+
+ off_t request_length;
+
+ ngx_uint_t err_status;
+
+ ngx_http_connection_t *http_connection;
+#if (NGX_HTTP_SPDY)
+ ngx_http_spdy_stream_t *spdy_stream;
+#endif
+
+ ngx_http_log_handler_pt log_handler;
+
+ ngx_http_cleanup_t *cleanup;
+
+ unsigned subrequests:8;
+ unsigned count:8;
+ unsigned blocked:8;
+
+ unsigned aio:1;
+
+ unsigned http_state:4;
+
+ /* URI with "/." and on Win32 with "//" */
+ unsigned complex_uri:1;
+
+ /* URI with "%" */
+ unsigned quoted_uri:1;
+
+ /* URI with "+" */
+ unsigned plus_in_uri:1;
+
+ /* URI with " " */
+ unsigned space_in_uri:1;
+
+ unsigned invalid_header:1;
+
+ unsigned add_uri_to_alias:1;
+ unsigned valid_location:1;
+ unsigned valid_unparsed_uri:1;
+ unsigned uri_changed:1;
+ unsigned uri_changes:4;
+
+ unsigned request_body_in_single_buf:1;
+ unsigned request_body_in_file_only:1;
+ unsigned request_body_in_persistent_file:1;
+ unsigned request_body_in_clean_file:1;
+ unsigned request_body_file_group_access:1;
+ unsigned request_body_file_log_level:3;
+
+ unsigned subrequest_in_memory:1;
+ unsigned waited:1;
+
+#if (NGX_HTTP_CACHE)
+ unsigned cached:1;
+#endif
+
+#if (NGX_HTTP_GZIP)
+ unsigned gzip_tested:1;
+ unsigned gzip_ok:1;
+ unsigned gzip_vary:1;
+#endif
+
+ unsigned proxy:1;
+ unsigned bypass_cache:1;
+ unsigned no_cache:1;
+
+ /*
+ * instead of using the request context data in
+ * ngx_http_limit_conn_module and ngx_http_limit_req_module
+ * we use the single bits in the request structure
+ */
+ unsigned limit_conn_set:1;
+ unsigned limit_req_set:1;
+
+#if 0
+ unsigned cacheable:1;
+#endif
+
+ unsigned pipeline:1;
+ unsigned chunked:1;
+ unsigned header_only:1;
+ unsigned keepalive:1;
+ unsigned lingering_close:1;
+ unsigned discard_body:1;
+ unsigned internal:1;
+ unsigned error_page:1;
+ unsigned filter_finalize:1;
+ unsigned post_action:1;
+ unsigned request_complete:1;
+ unsigned request_output:1;
+ unsigned header_sent:1;
+ unsigned expect_tested:1;
+ unsigned root_tested:1;
+ unsigned done:1;
+ unsigned logged:1;
+
+ unsigned buffered:4;
+
+ unsigned main_filter_need_in_memory:1;
+ unsigned filter_need_in_memory:1;
+ unsigned filter_need_temporary:1;
+ unsigned allow_ranges:1;
+ unsigned single_range:1;
+ unsigned disable_not_modified:1;
+
+#if (NGX_STAT_STUB)
+ unsigned stat_reading:1;
+ unsigned stat_writing:1;
+#endif
+
+ /* used to parse HTTP headers */
+
+ ngx_uint_t state;
+
+ ngx_uint_t header_hash;
+ ngx_uint_t lowcase_index;
+ u_char lowcase_header[NGX_HTTP_LC_HEADER_LEN];
+
+ u_char *header_name_start;
+ u_char *header_name_end;
+ u_char *header_start;
+ u_char *header_end;
+
+ /*
+ * a memory that can be reused after parsing a request line
+ * via ngx_http_ephemeral_t
+ */
+
+ u_char *uri_start;
+ u_char *uri_end;
+ u_char *uri_ext;
+ u_char *args_start;
+ u_char *request_start;
+ u_char *request_end;
+ u_char *method_end;
+ u_char *schema_start;
+ u_char *schema_end;
+ u_char *host_start;
+ u_char *host_end;
+ u_char *port_start;
+ u_char *port_end;
+
+ unsigned http_minor:16;
+ unsigned http_major:16;
+};
+
+
+typedef struct {
+ ngx_http_posted_request_t terminal_posted_request;
+#if (NGX_HAVE_AIO_SENDFILE)
+ u_char aio_preload;
+#endif
+} ngx_http_ephemeral_t;
+
+
+extern ngx_http_header_t ngx_http_headers_in[];
+extern ngx_http_header_out_t ngx_http_headers_out[];
+
+
+#define ngx_http_set_connection_log(c, l) \
+ \
+ c->log->file = l->file; \
+ c->log->next = l->next; \
+ c->log->writer = l->writer; \
+ c->log->wdata = l->wdata; \
+ if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { \
+ c->log->log_level = l->log_level; \
+ }
+
+
+#endif /* _NGX_HTTP_REQUEST_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_request_body.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_request_body.c
new file mode 100644
index 00000000000..bbf16fd2576
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_request_body.c
@@ -0,0 +1,1099 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
+static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);
+static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);
+static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r,
+ ngx_buf_t *b);
+static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r);
+
+static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r,
+ ngx_chain_t *in);
+static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r,
+ ngx_chain_t *in);
+static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r,
+ ngx_chain_t *in);
+static ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r,
+ ngx_chain_t *in);
+
+
+ngx_int_t
+ngx_http_read_client_request_body(ngx_http_request_t *r,
+ ngx_http_client_body_handler_pt post_handler)
+{
+ size_t preread;
+ ssize_t size;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t out, *cl;
+ ngx_http_request_body_t *rb;
+ ngx_http_core_loc_conf_t *clcf;
+
+ r->main->count++;
+
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream && r == r->main) {
+ rc = ngx_http_spdy_read_request_body(r, post_handler);
+ goto done;
+ }
+#endif
+
+ if (r != r->main || r->request_body || r->discard_body) {
+ post_handler(r);
+ return NGX_OK;
+ }
+
+ if (ngx_http_test_expect(r) != NGX_OK) {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ goto done;
+ }
+
+ rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
+ if (rb == NULL) {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ goto done;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * rb->bufs = NULL;
+ * rb->buf = NULL;
+ * rb->free = NULL;
+ * rb->busy = NULL;
+ * rb->chunked = NULL;
+ */
+
+ rb->rest = -1;
+ rb->post_handler = post_handler;
+
+ r->request_body = rb;
+
+ if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {
+ post_handler(r);
+ return NGX_OK;
+ }
+
+ preread = r->header_in->last - r->header_in->pos;
+
+ if (preread) {
+
+ /* there is the pre-read part of the request body */
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http client request body preread %uz", preread);
+
+ out.buf = r->header_in;
+ out.next = NULL;
+
+ rc = ngx_http_request_body_filter(r, &out);
+
+ if (rc != NGX_OK) {
+ goto done;
+ }
+
+ r->request_length += preread - (r->header_in->last - r->header_in->pos);
+
+ if (!r->headers_in.chunked
+ && rb->rest > 0
+ && rb->rest <= (off_t) (r->header_in->end - r->header_in->last))
+ {
+ /* the whole request body may be placed in r->header_in */
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ goto done;
+ }
+
+ b->temporary = 1;
+ b->start = r->header_in->pos;
+ b->pos = r->header_in->pos;
+ b->last = r->header_in->last;
+ b->end = r->header_in->end;
+
+ rb->buf = b;
+
+ r->read_event_handler = ngx_http_read_client_request_body_handler;
+ r->write_event_handler = ngx_http_request_empty_handler;
+
+ rc = ngx_http_do_read_client_request_body(r);
+ goto done;
+ }
+
+ } else {
+ /* set rb->rest */
+
+ if (ngx_http_request_body_filter(r, NULL) != NGX_OK) {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ goto done;
+ }
+ }
+
+ if (rb->rest == 0) {
+ /* the whole request body was pre-read */
+
+ if (r->request_body_in_file_only) {
+ if (ngx_http_write_request_body(r) != NGX_OK) {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ goto done;
+ }
+
+ if (rb->temp_file->file.offset != 0) {
+
+ cl = ngx_chain_get_free_buf(r->pool, &rb->free);
+ if (cl == NULL) {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ goto done;
+ }
+
+ b = cl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->in_file = 1;
+ b->file_last = rb->temp_file->file.offset;
+ b->file = &rb->temp_file->file;
+
+ rb->bufs = cl;
+
+ } else {
+ rb->bufs = NULL;
+ }
+ }
+
+ post_handler(r);
+
+ return NGX_OK;
+ }
+
+ if (rb->rest < 0) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "negative request body rest");
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ goto done;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ size = clcf->client_body_buffer_size;
+ size += size >> 2;
+
+ /* TODO: honor r->request_body_in_single_buf */
+
+ if (!r->headers_in.chunked && rb->rest < size) {
+ size = (ssize_t) rb->rest;
+
+ if (r->request_body_in_single_buf) {
+ size += preread;
+ }
+
+ } else {
+ size = clcf->client_body_buffer_size;
+ }
+
+ rb->buf = ngx_create_temp_buf(r->pool, size);
+ if (rb->buf == NULL) {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ goto done;
+ }
+
+ r->read_event_handler = ngx_http_read_client_request_body_handler;
+ r->write_event_handler = ngx_http_request_empty_handler;
+
+ rc = ngx_http_do_read_client_request_body(r);
+
+done:
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ r->main->count--;
+ }
+
+ return rc;
+}
+
+
+static void
+ngx_http_read_client_request_body_handler(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+
+ if (r->connection->read->timedout) {
+ r->connection->timedout = 1;
+ ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ rc = ngx_http_do_read_client_request_body(r);
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ ngx_http_finalize_request(r, rc);
+ }
+}
+
+
+static ngx_int_t
+ngx_http_do_read_client_request_body(ngx_http_request_t *r)
+{
+ off_t rest;
+ size_t size;
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl, out;
+ ngx_connection_t *c;
+ ngx_http_request_body_t *rb;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ rb = r->request_body;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http read client request body");
+
+ for ( ;; ) {
+ for ( ;; ) {
+ if (rb->buf->last == rb->buf->end) {
+
+ /* pass buffer to request body filter chain */
+
+ out.buf = rb->buf;
+ out.next = NULL;
+
+ rc = ngx_http_request_body_filter(r, &out);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ /* write to file */
+
+ if (ngx_http_write_request_body(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* update chains */
+
+ rc = ngx_http_request_body_filter(r, NULL);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ if (rb->busy != NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ rb->buf->pos = rb->buf->start;
+ rb->buf->last = rb->buf->start;
+ }
+
+ size = rb->buf->end - rb->buf->last;
+ rest = rb->rest - (rb->buf->last - rb->buf->pos);
+
+ if ((off_t) size > rest) {
+ size = (size_t) rest;
+ }
+
+ n = c->recv(c, rb->buf->last, size);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http client request body recv %z", n);
+
+ if (n == NGX_AGAIN) {
+ break;
+ }
+
+ if (n == 0) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client prematurely closed connection");
+ }
+
+ if (n == 0 || n == NGX_ERROR) {
+ c->error = 1;
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ rb->buf->last += n;
+ r->request_length += n;
+
+ if (n == rest) {
+ /* pass buffer to request body filter chain */
+
+ out.buf = rb->buf;
+ out.next = NULL;
+
+ rc = ngx_http_request_body_filter(r, &out);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+ }
+
+ if (rb->rest == 0) {
+ break;
+ }
+
+ if (rb->buf->last < rb->buf->end) {
+ break;
+ }
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http client request body rest %O", rb->rest);
+
+ if (rb->rest == 0) {
+ break;
+ }
+
+ if (!c->read->ready) {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+ ngx_add_timer(c->read, clcf->client_body_timeout);
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ return NGX_AGAIN;
+ }
+ }
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ if (rb->temp_file || r->request_body_in_file_only) {
+
+ /* save the last part */
+
+ if (ngx_http_write_request_body(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (rb->temp_file->file.offset != 0) {
+
+ cl = ngx_chain_get_free_buf(r->pool, &rb->free);
+ if (cl == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b = cl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->in_file = 1;
+ b->file_last = rb->temp_file->file.offset;
+ b->file = &rb->temp_file->file;
+
+ rb->bufs = cl;
+
+ } else {
+ rb->bufs = NULL;
+ }
+ }
+
+ r->read_event_handler = ngx_http_block_reading;
+
+ rb->post_handler(r);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_write_request_body(ngx_http_request_t *r)
+{
+ ssize_t n;
+ ngx_chain_t *cl;
+ ngx_temp_file_t *tf;
+ ngx_http_request_body_t *rb;
+ ngx_http_core_loc_conf_t *clcf;
+
+ rb = r->request_body;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http write client request body, bufs %p", rb->bufs);
+
+ if (rb->temp_file == NULL) {
+ tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
+ if (tf == NULL) {
+ return NGX_ERROR;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ tf->file.fd = NGX_INVALID_FILE;
+ tf->file.log = r->connection->log;
+ tf->path = clcf->client_body_temp_path;
+ tf->pool = r->pool;
+ tf->warn = "a client request body is buffered to a temporary file";
+ tf->log_level = r->request_body_file_log_level;
+ tf->persistent = r->request_body_in_persistent_file;
+ tf->clean = r->request_body_in_clean_file;
+
+ if (r->request_body_file_group_access) {
+ tf->access = 0660;
+ }
+
+ rb->temp_file = tf;
+
+ if (rb->bufs == NULL) {
+ /* empty body with r->request_body_in_file_only */
+
+ if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
+ tf->persistent, tf->clean, tf->access)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+ }
+
+ if (rb->bufs == NULL) {
+ return NGX_OK;
+ }
+
+ n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);
+
+ /* TODO: n == 0 or not complete and level event */
+
+ if (n == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ rb->temp_file->offset += n;
+
+ /* mark all buffers as written */
+
+ for (cl = rb->bufs; cl; cl = cl->next) {
+ cl->buf->pos = cl->buf->last;
+ }
+
+ rb->bufs = NULL;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_discard_request_body(ngx_http_request_t *r)
+{
+ ssize_t size;
+ ngx_int_t rc;
+ ngx_event_t *rev;
+
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream && r == r->main) {
+ r->spdy_stream->skip_data = NGX_SPDY_DATA_DISCARD;
+ return NGX_OK;
+ }
+#endif
+
+ if (r != r->main || r->discard_body || r->request_body) {
+ return NGX_OK;
+ }
+
+ if (ngx_http_test_expect(r) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ rev = r->connection->read;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body");
+
+ if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+
+ if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
+ return NGX_OK;
+ }
+
+ size = r->header_in->last - r->header_in->pos;
+
+ if (size || r->headers_in.chunked) {
+ rc = ngx_http_discard_request_body_filter(r, r->header_in);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+
+ if (r->headers_in.content_length_n == 0) {
+ return NGX_OK;
+ }
+ }
+
+ rc = ngx_http_read_discarded_request_body(r);
+
+ if (rc == NGX_OK) {
+ r->lingering_close = 0;
+ return NGX_OK;
+ }
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ return rc;
+ }
+
+ /* rc == NGX_AGAIN */
+
+ r->read_event_handler = ngx_http_discarded_request_body_handler;
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ r->count++;
+ r->discard_body = 1;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+ ngx_msec_t timer;
+ ngx_event_t *rev;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ rev = c->read;
+
+ if (rev->timedout) {
+ c->timedout = 1;
+ c->error = 1;
+ ngx_http_finalize_request(r, NGX_ERROR);
+ return;
+ }
+
+ if (r->lingering_time) {
+ timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time();
+
+ if ((ngx_msec_int_t) timer <= 0) {
+ r->discard_body = 0;
+ r->lingering_close = 0;
+ ngx_http_finalize_request(r, NGX_ERROR);
+ return;
+ }
+
+ } else {
+ timer = 0;
+ }
+
+ rc = ngx_http_read_discarded_request_body(r);
+
+ if (rc == NGX_OK) {
+ r->discard_body = 0;
+ r->lingering_close = 0;
+ ngx_http_finalize_request(r, NGX_DONE);
+ return;
+ }
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ c->error = 1;
+ ngx_http_finalize_request(r, NGX_ERROR);
+ return;
+ }
+
+ /* rc == NGX_AGAIN */
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ c->error = 1;
+ ngx_http_finalize_request(r, NGX_ERROR);
+ return;
+ }
+
+ if (timer) {
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ timer *= 1000;
+
+ if (timer > clcf->lingering_timeout) {
+ timer = clcf->lingering_timeout;
+ }
+
+ ngx_add_timer(rev, timer);
+ }
+}
+
+
+static ngx_int_t
+ngx_http_read_discarded_request_body(ngx_http_request_t *r)
+{
+ size_t size;
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_buf_t b;
+ u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http read discarded body");
+
+ ngx_memzero(&b, sizeof(ngx_buf_t));
+
+ b.temporary = 1;
+
+ for ( ;; ) {
+ if (r->headers_in.content_length_n == 0) {
+ r->read_event_handler = ngx_http_block_reading;
+ return NGX_OK;
+ }
+
+ if (!r->connection->read->ready) {
+ return NGX_AGAIN;
+ }
+
+ size = (size_t) ngx_min(r->headers_in.content_length_n,
+ NGX_HTTP_DISCARD_BUFFER_SIZE);
+
+ n = r->connection->recv(r->connection, buffer, size);
+
+ if (n == NGX_ERROR) {
+ r->connection->error = 1;
+ return NGX_OK;
+ }
+
+ if (n == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ if (n == 0) {
+ return NGX_OK;
+ }
+
+ b.pos = buffer;
+ b.last = buffer + n;
+
+ rc = ngx_http_discard_request_body_filter(r, &b);
+
+ if (rc != NGX_OK) {
+ return rc;
+ }
+ }
+}
+
+
+static ngx_int_t
+ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
+{
+ size_t size;
+ ngx_int_t rc;
+ ngx_http_request_body_t *rb;
+
+ if (r->headers_in.chunked) {
+
+ rb = r->request_body;
+
+ if (rb == NULL) {
+
+ rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
+ if (rb == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
+ if (rb->chunked == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ r->request_body = rb;
+ }
+
+ for ( ;; ) {
+
+ rc = ngx_http_parse_chunked(r, b, rb->chunked);
+
+ if (rc == NGX_OK) {
+
+ /* a chunk has been parsed successfully */
+
+ size = b->last - b->pos;
+
+ if ((off_t) size > rb->chunked->size) {
+ b->pos += (size_t) rb->chunked->size;
+ rb->chunked->size = 0;
+
+ } else {
+ rb->chunked->size -= size;
+ b->pos = b->last;
+ }
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+
+ /* a whole response has been parsed successfully */
+
+ r->headers_in.content_length_n = 0;
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+
+ /* set amount of data we want to see next time */
+
+ r->headers_in.content_length_n = rb->chunked->length;
+ break;
+ }
+
+ /* invalid */
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client sent invalid chunked body");
+
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ } else {
+ size = b->last - b->pos;
+
+ if ((off_t) size > r->headers_in.content_length_n) {
+ b->pos += (size_t) r->headers_in.content_length_n;
+ r->headers_in.content_length_n = 0;
+
+ } else {
+ b->pos = b->last;
+ r->headers_in.content_length_n -= size;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_test_expect(ngx_http_request_t *r)
+{
+ ngx_int_t n;
+ ngx_str_t *expect;
+
+ if (r->expect_tested
+ || r->headers_in.expect == NULL
+ || r->http_version < NGX_HTTP_VERSION_11)
+ {
+ return NGX_OK;
+ }
+
+ r->expect_tested = 1;
+
+ expect = &r->headers_in.expect->value;
+
+ if (expect->len != sizeof("100-continue") - 1
+ || ngx_strncasecmp(expect->data, (u_char *) "100-continue",
+ sizeof("100-continue") - 1)
+ != 0)
+ {
+ return NGX_OK;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "send 100 Continue");
+
+ n = r->connection->send(r->connection,
+ (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF,
+ sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1);
+
+ if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) {
+ return NGX_OK;
+ }
+
+ /* we assume that such small packet should be send successfully */
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ if (r->headers_in.chunked) {
+ return ngx_http_request_body_chunked_filter(r, in);
+
+ } else {
+ return ngx_http_request_body_length_filter(r, in);
+ }
+}
+
+
+static ngx_int_t
+ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ size_t size;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl, *tl, *out, **ll;
+ ngx_http_request_body_t *rb;
+
+ rb = r->request_body;
+
+ if (rb->rest == -1) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http request body content length filter");
+
+ rb->rest = r->headers_in.content_length_n;
+ }
+
+ out = NULL;
+ ll = &out;
+
+ for (cl = in; cl; cl = cl->next) {
+
+ if (rb->rest == 0) {
+ break;
+ }
+
+ tl = ngx_chain_get_free_buf(r->pool, &rb->free);
+ if (tl == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b = tl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->temporary = 1;
+ b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
+ b->start = cl->buf->pos;
+ b->pos = cl->buf->pos;
+ b->last = cl->buf->last;
+ b->end = cl->buf->end;
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if ((off_t) size < rb->rest) {
+ cl->buf->pos = cl->buf->last;
+ rb->rest -= size;
+
+ } else {
+ cl->buf->pos += (size_t) rb->rest;
+ rb->rest = 0;
+ b->last = cl->buf->pos;
+ b->last_buf = 1;
+ }
+
+ *ll = tl;
+ ll = &tl->next;
+ }
+
+ rc = ngx_http_request_body_save_filter(r, out);
+
+ ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
+ (ngx_buf_tag_t) &ngx_http_read_client_request_body);
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ size_t size;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t *cl, *out, *tl, **ll;
+ ngx_http_request_body_t *rb;
+ ngx_http_core_loc_conf_t *clcf;
+
+ rb = r->request_body;
+
+ if (rb->rest == -1) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http request body chunked filter");
+
+ rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
+ if (rb->chunked == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ r->headers_in.content_length_n = 0;
+ rb->rest = 3;
+ }
+
+ out = NULL;
+ ll = &out;
+
+ for (cl = in; cl; cl = cl->next) {
+
+ for ( ;; ) {
+
+ ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
+ "http body chunked buf "
+ "t:%d f:%d %p, pos %p, size: %z file: %O, size: %z",
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+
+ rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);
+
+ if (rc == NGX_OK) {
+
+ /* a chunk has been parsed successfully */
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->client_max_body_size
+ && clcf->client_max_body_size
+ - r->headers_in.content_length_n < rb->chunked->size)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client intended to send too large chunked "
+ "body: %O+%O bytes",
+ r->headers_in.content_length_n,
+ rb->chunked->size);
+
+ r->lingering_close = 1;
+
+ return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
+ tl = ngx_chain_get_free_buf(r->pool, &rb->free);
+ if (tl == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b = tl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->temporary = 1;
+ b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
+ b->start = cl->buf->pos;
+ b->pos = cl->buf->pos;
+ b->last = cl->buf->last;
+ b->end = cl->buf->end;
+
+ *ll = tl;
+ ll = &tl->next;
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if ((off_t) size > rb->chunked->size) {
+ cl->buf->pos += (size_t) rb->chunked->size;
+ r->headers_in.content_length_n += rb->chunked->size;
+ rb->chunked->size = 0;
+
+ } else {
+ rb->chunked->size -= size;
+ r->headers_in.content_length_n += size;
+ cl->buf->pos = cl->buf->last;
+ }
+
+ b->last = cl->buf->pos;
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+
+ /* a whole response has been parsed successfully */
+
+ rb->rest = 0;
+
+ tl = ngx_chain_get_free_buf(r->pool, &rb->free);
+ if (tl == NULL) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ b = tl->buf;
+
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->last_buf = 1;
+
+ *ll = tl;
+ ll = &tl->next;
+
+ break;
+ }
+
+ if (rc == NGX_AGAIN) {
+
+ /* set rb->rest, amount of data we want to see next time */
+
+ rb->rest = rb->chunked->length;
+
+ break;
+ }
+
+ /* invalid */
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client sent invalid chunked body");
+
+ return NGX_HTTP_BAD_REQUEST;
+ }
+ }
+
+ rc = ngx_http_request_body_save_filter(r, out);
+
+ ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
+ (ngx_buf_tag_t) &ngx_http_read_client_request_body);
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+#if (NGX_DEBUG)
+ ngx_chain_t *cl;
+#endif
+ ngx_http_request_body_t *rb;
+
+ rb = r->request_body;
+
+#if (NGX_DEBUG)
+
+ for (cl = rb->bufs; cl; cl = cl->next) {
+ ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
+ "http body old buf t:%d f:%d %p, pos %p, size: %z "
+ "file: %O, size: %z",
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+ }
+
+ for (cl = in; cl; cl = cl->next) {
+ ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
+ "http body new buf t:%d f:%d %p, pos %p, size: %z "
+ "file: %O, size: %z",
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+ }
+
+#endif
+
+ /* TODO: coalesce neighbouring buffers */
+
+ if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_script.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_script.c
new file mode 100644
index 00000000000..02e2be3bb80
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_script.c
@@ -0,0 +1,1754 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static ngx_int_t ngx_http_script_init_arrays(ngx_http_script_compile_t *sc);
+static ngx_int_t ngx_http_script_done(ngx_http_script_compile_t *sc);
+static ngx_int_t ngx_http_script_add_copy_code(ngx_http_script_compile_t *sc,
+ ngx_str_t *value, ngx_uint_t last);
+static ngx_int_t ngx_http_script_add_var_code(ngx_http_script_compile_t *sc,
+ ngx_str_t *name);
+static ngx_int_t ngx_http_script_add_args_code(ngx_http_script_compile_t *sc);
+#if (NGX_PCRE)
+static ngx_int_t ngx_http_script_add_capture_code(ngx_http_script_compile_t *sc,
+ ngx_uint_t n);
+#endif
+static ngx_int_t
+ ngx_http_script_add_full_name_code(ngx_http_script_compile_t *sc);
+static size_t ngx_http_script_full_name_len_code(ngx_http_script_engine_t *e);
+static void ngx_http_script_full_name_code(ngx_http_script_engine_t *e);
+
+
+#define ngx_http_script_exit (u_char *) &ngx_http_script_exit_code
+
+static uintptr_t ngx_http_script_exit_code = (uintptr_t) NULL;
+
+
+void
+ngx_http_script_flush_complex_value(ngx_http_request_t *r,
+ ngx_http_complex_value_t *val)
+{
+ ngx_uint_t *index;
+
+ index = val->flushes;
+
+ if (index) {
+ while (*index != (ngx_uint_t) -1) {
+
+ if (r->variables[*index].no_cacheable) {
+ r->variables[*index].valid = 0;
+ r->variables[*index].not_found = 0;
+ }
+
+ index++;
+ }
+ }
+}
+
+
+ngx_int_t
+ngx_http_complex_value(ngx_http_request_t *r, ngx_http_complex_value_t *val,
+ ngx_str_t *value)
+{
+ size_t len;
+ ngx_http_script_code_pt code;
+ ngx_http_script_len_code_pt lcode;
+ ngx_http_script_engine_t e;
+
+ if (val->lengths == NULL) {
+ *value = val->value;
+ return NGX_OK;
+ }
+
+ ngx_http_script_flush_complex_value(r, val);
+
+ ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
+
+ e.ip = val->lengths;
+ e.request = r;
+ e.flushed = 1;
+
+ len = 0;
+
+ while (*(uintptr_t *) e.ip) {
+ lcode = *(ngx_http_script_len_code_pt *) e.ip;
+ len += lcode(&e);
+ }
+
+ value->len = len;
+ value->data = ngx_pnalloc(r->pool, len);
+ if (value->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ e.ip = val->values;
+ e.pos = value->data;
+ e.buf = *value;
+
+ while (*(uintptr_t *) e.ip) {
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) &e);
+ }
+
+ *value = e.buf;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv)
+{
+ ngx_str_t *v;
+ ngx_uint_t i, n, nv, nc;
+ ngx_array_t flushes, lengths, values, *pf, *pl, *pv;
+ ngx_http_script_compile_t sc;
+
+ v = ccv->value;
+
+ nv = 0;
+ nc = 0;
+
+ for (i = 0; i < v->len; i++) {
+ if (v->data[i] == '$') {
+ if (v->data[i + 1] >= '1' && v->data[i + 1] <= '9') {
+ nc++;
+
+ } else {
+ nv++;
+ }
+ }
+ }
+
+ if ((v->len == 0 || v->data[0] != '$')
+ && (ccv->conf_prefix || ccv->root_prefix))
+ {
+ if (ngx_conf_full_name(ccv->cf->cycle, v, ccv->conf_prefix) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ccv->conf_prefix = 0;
+ ccv->root_prefix = 0;
+ }
+
+ ccv->complex_value->value = *v;
+ ccv->complex_value->flushes = NULL;
+ ccv->complex_value->lengths = NULL;
+ ccv->complex_value->values = NULL;
+
+ if (nv == 0 && nc == 0) {
+ return NGX_OK;
+ }
+
+ n = nv + 1;
+
+ if (ngx_array_init(&flushes, ccv->cf->pool, n, sizeof(ngx_uint_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ n = nv * (2 * sizeof(ngx_http_script_copy_code_t)
+ + sizeof(ngx_http_script_var_code_t))
+ + sizeof(uintptr_t);
+
+ if (ngx_array_init(&lengths, ccv->cf->pool, n, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ n = (nv * (2 * sizeof(ngx_http_script_copy_code_t)
+ + sizeof(ngx_http_script_var_code_t))
+ + sizeof(uintptr_t)
+ + v->len
+ + sizeof(uintptr_t) - 1)
+ & ~(sizeof(uintptr_t) - 1);
+
+ if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ pf = &flushes;
+ pl = &lengths;
+ pv = &values;
+
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+
+ sc.cf = ccv->cf;
+ sc.source = v;
+ sc.flushes = &pf;
+ sc.lengths = &pl;
+ sc.values = &pv;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
+ sc.zero = ccv->zero;
+ sc.conf_prefix = ccv->conf_prefix;
+ sc.root_prefix = ccv->root_prefix;
+
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (flushes.nelts) {
+ ccv->complex_value->flushes = flushes.elts;
+ ccv->complex_value->flushes[flushes.nelts] = (ngx_uint_t) -1;
+ }
+
+ ccv->complex_value->lengths = lengths.elts;
+ ccv->complex_value->values = values.elts;
+
+ return NGX_OK;
+}
+
+
+char *
+ngx_http_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *value;
+ ngx_http_complex_value_t **cv;
+ ngx_http_compile_complex_value_t ccv;
+
+ cv = (ngx_http_complex_value_t **) (p + cmd->offset);
+
+ if (*cv != NULL) {
+ return "duplicate";
+ }
+
+ *cv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+ if (*cv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = *cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+ngx_int_t
+ngx_http_test_predicates(ngx_http_request_t *r, ngx_array_t *predicates)
+{
+ ngx_str_t val;
+ ngx_uint_t i;
+ ngx_http_complex_value_t *cv;
+
+ if (predicates == NULL) {
+ return NGX_OK;
+ }
+
+ cv = predicates->elts;
+
+ for (i = 0; i < predicates->nelts; i++) {
+ if (ngx_http_complex_value(r, &cv[i], &val) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (val.len && (val.len != 1 || val.data[0] != '0')) {
+ return NGX_DECLINED;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+char *
+ngx_http_set_predicate_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *value;
+ ngx_uint_t i;
+ ngx_array_t **a;
+ ngx_http_complex_value_t *cv;
+ ngx_http_compile_complex_value_t ccv;
+
+ a = (ngx_array_t **) (p + cmd->offset);
+
+ if (*a == NGX_CONF_UNSET_PTR) {
+ *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_complex_value_t));
+ if (*a == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+ cv = ngx_array_push(*a);
+ if (cv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[i];
+ ccv.complex_value = cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+ngx_uint_t
+ngx_http_script_variables_count(ngx_str_t *value)
+{
+ ngx_uint_t i, n;
+
+ for (n = 0, i = 0; i < value->len; i++) {
+ if (value->data[i] == '$') {
+ n++;
+ }
+ }
+
+ return n;
+}
+
+
+ngx_int_t
+ngx_http_script_compile(ngx_http_script_compile_t *sc)
+{
+ u_char ch;
+ ngx_str_t name;
+ ngx_uint_t i, bracket;
+
+ if (ngx_http_script_init_arrays(sc) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ for (i = 0; i < sc->source->len; /* void */ ) {
+
+ name.len = 0;
+
+ if (sc->source->data[i] == '$') {
+
+ if (++i == sc->source->len) {
+ goto invalid_variable;
+ }
+
+#if (NGX_PCRE)
+ {
+ ngx_uint_t n;
+
+ if (sc->source->data[i] >= '1' && sc->source->data[i] <= '9') {
+
+ n = sc->source->data[i] - '0';
+
+ if (sc->captures_mask & (1 << n)) {
+ sc->dup_capture = 1;
+ }
+
+ sc->captures_mask |= 1 << n;
+
+ if (ngx_http_script_add_capture_code(sc, n) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ i++;
+
+ continue;
+ }
+ }
+#endif
+
+ if (sc->source->data[i] == '{') {
+ bracket = 1;
+
+ if (++i == sc->source->len) {
+ goto invalid_variable;
+ }
+
+ name.data = &sc->source->data[i];
+
+ } else {
+ bracket = 0;
+ name.data = &sc->source->data[i];
+ }
+
+ for ( /* void */ ; i < sc->source->len; i++, name.len++) {
+ ch = sc->source->data[i];
+
+ if (ch == '}' && bracket) {
+ i++;
+ bracket = 0;
+ break;
+ }
+
+ if ((ch >= 'A' && ch <= 'Z')
+ || (ch >= 'a' && ch <= 'z')
+ || (ch >= '0' && ch <= '9')
+ || ch == '_')
+ {
+ continue;
+ }
+
+ break;
+ }
+
+ if (bracket) {
+ ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0,
+ "the closing bracket in \"%V\" "
+ "variable is missing", &name);
+ return NGX_ERROR;
+ }
+
+ if (name.len == 0) {
+ goto invalid_variable;
+ }
+
+ sc->variables++;
+
+ if (ngx_http_script_add_var_code(sc, &name) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ continue;
+ }
+
+ if (sc->source->data[i] == '?' && sc->compile_args) {
+ sc->args = 1;
+ sc->compile_args = 0;
+
+ if (ngx_http_script_add_args_code(sc) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ i++;
+
+ continue;
+ }
+
+ name.data = &sc->source->data[i];
+
+ while (i < sc->source->len) {
+
+ if (sc->source->data[i] == '$') {
+ break;
+ }
+
+ if (sc->source->data[i] == '?') {
+
+ sc->args = 1;
+
+ if (sc->compile_args) {
+ break;
+ }
+ }
+
+ i++;
+ name.len++;
+ }
+
+ sc->size += name.len;
+
+ if (ngx_http_script_add_copy_code(sc, &name, (i == sc->source->len))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ return ngx_http_script_done(sc);
+
+invalid_variable:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, "invalid variable name");
+
+ return NGX_ERROR;
+}
+
+
+u_char *
+ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value,
+ void *code_lengths, size_t len, void *code_values)
+{
+ ngx_uint_t i;
+ ngx_http_script_code_pt code;
+ ngx_http_script_len_code_pt lcode;
+ ngx_http_script_engine_t e;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ for (i = 0; i < cmcf->variables.nelts; i++) {
+ if (r->variables[i].no_cacheable) {
+ r->variables[i].valid = 0;
+ r->variables[i].not_found = 0;
+ }
+ }
+
+ ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
+
+ e.ip = code_lengths;
+ e.request = r;
+ e.flushed = 1;
+
+ while (*(uintptr_t *) e.ip) {
+ lcode = *(ngx_http_script_len_code_pt *) e.ip;
+ len += lcode(&e);
+ }
+
+
+ value->len = len;
+ value->data = ngx_pnalloc(r->pool, len);
+ if (value->data == NULL) {
+ return NULL;
+ }
+
+ e.ip = code_values;
+ e.pos = value->data;
+
+ while (*(uintptr_t *) e.ip) {
+ code = *(ngx_http_script_code_pt *) e.ip;
+ code((ngx_http_script_engine_t *) &e);
+ }
+
+ return e.pos;
+}
+
+
+void
+ngx_http_script_flush_no_cacheable_variables(ngx_http_request_t *r,
+ ngx_array_t *indices)
+{
+ ngx_uint_t n, *index;
+
+ if (indices) {
+ index = indices->elts;
+ for (n = 0; n < indices->nelts; n++) {
+ if (r->variables[index[n]].no_cacheable) {
+ r->variables[index[n]].valid = 0;
+ r->variables[index[n]].not_found = 0;
+ }
+ }
+ }
+}
+
+
+static ngx_int_t
+ngx_http_script_init_arrays(ngx_http_script_compile_t *sc)
+{
+ ngx_uint_t n;
+
+ if (sc->flushes && *sc->flushes == NULL) {
+ n = sc->variables ? sc->variables : 1;
+ *sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t));
+ if (*sc->flushes == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (*sc->lengths == NULL) {
+ n = sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
+ + sizeof(ngx_http_script_var_code_t))
+ + sizeof(uintptr_t);
+
+ *sc->lengths = ngx_array_create(sc->cf->pool, n, 1);
+ if (*sc->lengths == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (*sc->values == NULL) {
+ n = (sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)
+ + sizeof(ngx_http_script_var_code_t))
+ + sizeof(uintptr_t)
+ + sc->source->len
+ + sizeof(uintptr_t) - 1)
+ & ~(sizeof(uintptr_t) - 1);
+
+ *sc->values = ngx_array_create(sc->cf->pool, n, 1);
+ if (*sc->values == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ sc->variables = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_script_done(ngx_http_script_compile_t *sc)
+{
+ ngx_str_t zero;
+ uintptr_t *code;
+
+ if (sc->zero) {
+
+ zero.len = 1;
+ zero.data = (u_char *) "\0";
+
+ if (ngx_http_script_add_copy_code(sc, &zero, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (sc->conf_prefix || sc->root_prefix) {
+ if (ngx_http_script_add_full_name_code(sc) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (sc->complete_lengths) {
+ code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+ }
+
+ if (sc->complete_values) {
+ code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t),
+ &sc->main);
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) NULL;
+ }
+
+ return NGX_OK;
+}
+
+
+void *
+ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes, size_t size)
+{
+ if (*codes == NULL) {
+ *codes = ngx_array_create(pool, 256, 1);
+ if (*codes == NULL) {
+ return NULL;
+ }
+ }
+
+ return ngx_array_push_n(*codes, size);
+}
+
+
+void *
+ngx_http_script_add_code(ngx_array_t *codes, size_t size, void *code)
+{
+ u_char *elts, **p;
+ void *new;
+
+ elts = codes->elts;
+
+ new = ngx_array_push_n(codes, size);
+ if (new == NULL) {
+ return NULL;
+ }
+
+ if (code) {
+ if (elts != codes->elts) {
+ p = code;
+ *p += (u_char *) codes->elts - elts;
+ }
+ }
+
+ return new;
+}
+
+
+static ngx_int_t
+ngx_http_script_add_copy_code(ngx_http_script_compile_t *sc, ngx_str_t *value,
+ ngx_uint_t last)
+{
+ u_char *p;
+ size_t size, len, zero;
+ ngx_http_script_copy_code_t *code;
+
+ zero = (sc->zero && last);
+ len = value->len + zero;
+
+ code = ngx_http_script_add_code(*sc->lengths,
+ sizeof(ngx_http_script_copy_code_t), NULL);
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ code->code = (ngx_http_script_code_pt) ngx_http_script_copy_len_code;
+ code->len = len;
+
+ size = (sizeof(ngx_http_script_copy_code_t) + len + sizeof(uintptr_t) - 1)
+ & ~(sizeof(uintptr_t) - 1);
+
+ code = ngx_http_script_add_code(*sc->values, size, &sc->main);
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ code->code = ngx_http_script_copy_code;
+ code->len = len;
+
+ p = ngx_cpymem((u_char *) code + sizeof(ngx_http_script_copy_code_t),
+ value->data, value->len);
+
+ if (zero) {
+ *p = '\0';
+ sc->zero = 0;
+ }
+
+ return NGX_OK;
+}
+
+
+size_t
+ngx_http_script_copy_len_code(ngx_http_script_engine_t *e)
+{
+ ngx_http_script_copy_code_t *code;
+
+ code = (ngx_http_script_copy_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_http_script_copy_code_t);
+
+ return code->len;
+}
+
+
+void
+ngx_http_script_copy_code(ngx_http_script_engine_t *e)
+{
+ u_char *p;
+ ngx_http_script_copy_code_t *code;
+
+ code = (ngx_http_script_copy_code_t *) e->ip;
+
+ p = e->pos;
+
+ if (!e->skip) {
+ e->pos = ngx_copy(p, e->ip + sizeof(ngx_http_script_copy_code_t),
+ code->len);
+ }
+
+ e->ip += sizeof(ngx_http_script_copy_code_t)
+ + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script copy: \"%*s\"", e->pos - p, p);
+}
+
+
+static ngx_int_t
+ngx_http_script_add_var_code(ngx_http_script_compile_t *sc, ngx_str_t *name)
+{
+ ngx_int_t index, *p;
+ ngx_http_script_var_code_t *code;
+
+ index = ngx_http_get_variable_index(sc->cf, name);
+
+ if (index == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (sc->flushes) {
+ p = ngx_array_push(*sc->flushes);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ *p = index;
+ }
+
+ code = ngx_http_script_add_code(*sc->lengths,
+ sizeof(ngx_http_script_var_code_t), NULL);
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ code->code = (ngx_http_script_code_pt) ngx_http_script_copy_var_len_code;
+ code->index = (uintptr_t) index;
+
+ code = ngx_http_script_add_code(*sc->values,
+ sizeof(ngx_http_script_var_code_t),
+ &sc->main);
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ code->code = ngx_http_script_copy_var_code;
+ code->index = (uintptr_t) index;
+
+ return NGX_OK;
+}
+
+
+size_t
+ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e)
+{
+ ngx_http_variable_value_t *value;
+ ngx_http_script_var_code_t *code;
+
+ code = (ngx_http_script_var_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_http_script_var_code_t);
+
+ if (e->flushed) {
+ value = ngx_http_get_indexed_variable(e->request, code->index);
+
+ } else {
+ value = ngx_http_get_flushed_variable(e->request, code->index);
+ }
+
+ if (value && !value->not_found) {
+ return value->len;
+ }
+
+ return 0;
+}
+
+
+void
+ngx_http_script_copy_var_code(ngx_http_script_engine_t *e)
+{
+ u_char *p;
+ ngx_http_variable_value_t *value;
+ ngx_http_script_var_code_t *code;
+
+ code = (ngx_http_script_var_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_http_script_var_code_t);
+
+ if (!e->skip) {
+
+ if (e->flushed) {
+ value = ngx_http_get_indexed_variable(e->request, code->index);
+
+ } else {
+ value = ngx_http_get_flushed_variable(e->request, code->index);
+ }
+
+ if (value && !value->not_found) {
+ p = e->pos;
+ e->pos = ngx_copy(p, value->data, value->len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP,
+ e->request->connection->log, 0,
+ "http script var: \"%*s\"", e->pos - p, p);
+ }
+ }
+}
+
+
+static ngx_int_t
+ngx_http_script_add_args_code(ngx_http_script_compile_t *sc)
+{
+ uintptr_t *code;
+
+ code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) ngx_http_script_mark_args_code;
+
+ code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t), &sc->main);
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ *code = (uintptr_t) ngx_http_script_start_args_code;
+
+ return NGX_OK;
+}
+
+
+size_t
+ngx_http_script_mark_args_code(ngx_http_script_engine_t *e)
+{
+ e->is_args = 1;
+ e->ip += sizeof(uintptr_t);
+
+ return 1;
+}
+
+
+void
+ngx_http_script_start_args_code(ngx_http_script_engine_t *e)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script args");
+
+ e->is_args = 1;
+ e->args = e->pos;
+ e->ip += sizeof(uintptr_t);
+}
+
+
+#if (NGX_PCRE)
+
+void
+ngx_http_script_regex_start_code(ngx_http_script_engine_t *e)
+{
+ size_t len;
+ ngx_int_t rc;
+ ngx_uint_t n;
+ ngx_http_request_t *r;
+ ngx_http_script_engine_t le;
+ ngx_http_script_len_code_pt lcode;
+ ngx_http_script_regex_code_t *code;
+
+ code = (ngx_http_script_regex_code_t *) e->ip;
+
+ r = e->request;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http script regex: \"%V\"", &code->name);
+
+ if (code->uri) {
+ e->line = r->uri;
+ } else {
+ e->sp--;
+ e->line.len = e->sp->len;
+ e->line.data = e->sp->data;
+ }
+
+ rc = ngx_http_regex_exec(r, code->regex, &e->line);
+
+ if (rc == NGX_DECLINED) {
+ if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
+ ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
+ "\"%V\" does not match \"%V\"",
+ &code->name, &e->line);
+ }
+
+ r->ncaptures = 0;
+
+ if (code->test) {
+ if (code->negative_test) {
+ e->sp->len = 1;
+ e->sp->data = (u_char *) "1";
+
+ } else {
+ e->sp->len = 0;
+ e->sp->data = (u_char *) "";
+ }
+
+ e->sp++;
+
+ e->ip += sizeof(ngx_http_script_regex_code_t);
+ return;
+ }
+
+ e->ip += code->next;
+ return;
+ }
+
+ if (rc == NGX_ERROR) {
+ e->ip = ngx_http_script_exit;
+ e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ return;
+ }
+
+ if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
+ ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
+ "\"%V\" matches \"%V\"", &code->name, &e->line);
+ }
+
+ if (code->test) {
+ if (code->negative_test) {
+ e->sp->len = 0;
+ e->sp->data = (u_char *) "";
+
+ } else {
+ e->sp->len = 1;
+ e->sp->data = (u_char *) "1";
+ }
+
+ e->sp++;
+
+ e->ip += sizeof(ngx_http_script_regex_code_t);
+ return;
+ }
+
+ if (code->status) {
+ e->status = code->status;
+
+ if (!code->redirect) {
+ e->ip = ngx_http_script_exit;
+ return;
+ }
+ }
+
+ if (code->uri) {
+ r->internal = 1;
+ r->valid_unparsed_uri = 0;
+
+ if (code->break_cycle) {
+ r->valid_location = 0;
+ r->uri_changed = 0;
+
+ } else {
+ r->uri_changed = 1;
+ }
+ }
+
+ if (code->lengths == NULL) {
+ e->buf.len = code->size;
+
+ if (code->uri) {
+ if (r->ncaptures && (r->quoted_uri || r->plus_in_uri)) {
+ e->buf.len += 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len,
+ NGX_ESCAPE_ARGS);
+ }
+ }
+
+ for (n = 2; n < r->ncaptures; n += 2) {
+ e->buf.len += r->captures[n + 1] - r->captures[n];
+ }
+
+ } else {
+ ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
+
+ le.ip = code->lengths->elts;
+ le.line = e->line;
+ le.request = r;
+ le.quote = code->redirect;
+
+ len = 0;
+
+ while (*(uintptr_t *) le.ip) {
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ len += lcode(&le);
+ }
+
+ e->buf.len = len;
+ }
+
+ if (code->add_args && r->args.len) {
+ e->buf.len += r->args.len + 1;
+ }
+
+ e->buf.data = ngx_pnalloc(r->pool, e->buf.len);
+ if (e->buf.data == NULL) {
+ e->ip = ngx_http_script_exit;
+ e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ return;
+ }
+
+ e->quote = code->redirect;
+
+ e->pos = e->buf.data;
+
+ e->ip += sizeof(ngx_http_script_regex_code_t);
+}
+
+
+void
+ngx_http_script_regex_end_code(ngx_http_script_engine_t *e)
+{
+ u_char *dst, *src;
+ ngx_http_request_t *r;
+ ngx_http_script_regex_end_code_t *code;
+
+ code = (ngx_http_script_regex_end_code_t *) e->ip;
+
+ r = e->request;
+
+ e->quote = 0;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http script regex end");
+
+ if (code->redirect) {
+
+ dst = e->buf.data;
+ src = e->buf.data;
+
+ ngx_unescape_uri(&dst, &src, e->pos - e->buf.data,
+ NGX_UNESCAPE_REDIRECT);
+
+ if (src < e->pos) {
+ dst = ngx_movemem(dst, src, e->pos - src);
+ }
+
+ e->pos = dst;
+
+ if (code->add_args && r->args.len) {
+ *e->pos++ = (u_char) (code->args ? '&' : '?');
+ e->pos = ngx_copy(e->pos, r->args.data, r->args.len);
+ }
+
+ e->buf.len = e->pos - e->buf.data;
+
+ if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
+ ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
+ "rewritten redirect: \"%V\"", &e->buf);
+ }
+
+ ngx_http_clear_location(r);
+
+ r->headers_out.location = ngx_list_push(&r->headers_out.headers);
+ if (r->headers_out.location == NULL) {
+ e->ip = ngx_http_script_exit;
+ e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ return;
+ }
+
+ r->headers_out.location->hash = 1;
+ ngx_str_set(&r->headers_out.location->key, "Location");
+ r->headers_out.location->value = e->buf;
+
+ e->ip += sizeof(ngx_http_script_regex_end_code_t);
+ return;
+ }
+
+ if (e->args) {
+ e->buf.len = e->args - e->buf.data;
+
+ if (code->add_args && r->args.len) {
+ *e->pos++ = '&';
+ e->pos = ngx_copy(e->pos, r->args.data, r->args.len);
+ }
+
+ r->args.len = e->pos - e->args;
+ r->args.data = e->args;
+
+ e->args = NULL;
+
+ } else {
+ e->buf.len = e->pos - e->buf.data;
+
+ if (!code->add_args) {
+ r->args.len = 0;
+ }
+ }
+
+ if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {
+ ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
+ "rewritten data: \"%V\", args: \"%V\"",
+ &e->buf, &r->args);
+ }
+
+ if (code->uri) {
+ r->uri = e->buf;
+
+ if (r->uri.len == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "the rewritten URI has a zero length");
+ e->ip = ngx_http_script_exit;
+ e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ return;
+ }
+
+ ngx_http_set_exten(r);
+ }
+
+ e->ip += sizeof(ngx_http_script_regex_end_code_t);
+}
+
+
+static ngx_int_t
+ngx_http_script_add_capture_code(ngx_http_script_compile_t *sc, ngx_uint_t n)
+{
+ ngx_http_script_copy_capture_code_t *code;
+
+ code = ngx_http_script_add_code(*sc->lengths,
+ sizeof(ngx_http_script_copy_capture_code_t),
+ NULL);
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ code->code = (ngx_http_script_code_pt)
+ ngx_http_script_copy_capture_len_code;
+ code->n = 2 * n;
+
+
+ code = ngx_http_script_add_code(*sc->values,
+ sizeof(ngx_http_script_copy_capture_code_t),
+ &sc->main);
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ code->code = ngx_http_script_copy_capture_code;
+ code->n = 2 * n;
+
+ if (sc->ncaptures < n) {
+ sc->ncaptures = n;
+ }
+
+ return NGX_OK;
+}
+
+
+size_t
+ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e)
+{
+ int *cap;
+ u_char *p;
+ ngx_uint_t n;
+ ngx_http_request_t *r;
+ ngx_http_script_copy_capture_code_t *code;
+
+ r = e->request;
+
+ code = (ngx_http_script_copy_capture_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_http_script_copy_capture_code_t);
+
+ n = code->n;
+
+ if (n < r->ncaptures) {
+
+ cap = r->captures;
+
+ if ((e->is_args || e->quote)
+ && (e->request->quoted_uri || e->request->plus_in_uri))
+ {
+ p = r->captures_data;
+
+ return cap[n + 1] - cap[n]
+ + 2 * ngx_escape_uri(NULL, &p[cap[n]], cap[n + 1] - cap[n],
+ NGX_ESCAPE_ARGS);
+ } else {
+ return cap[n + 1] - cap[n];
+ }
+ }
+
+ return 0;
+}
+
+
+void
+ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e)
+{
+ int *cap;
+ u_char *p, *pos;
+ ngx_uint_t n;
+ ngx_http_request_t *r;
+ ngx_http_script_copy_capture_code_t *code;
+
+ r = e->request;
+
+ code = (ngx_http_script_copy_capture_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_http_script_copy_capture_code_t);
+
+ n = code->n;
+
+ pos = e->pos;
+
+ if (n < r->ncaptures) {
+
+ cap = r->captures;
+ p = r->captures_data;
+
+ if ((e->is_args || e->quote)
+ && (e->request->quoted_uri || e->request->plus_in_uri))
+ {
+ e->pos = (u_char *) ngx_escape_uri(pos, &p[cap[n]],
+ cap[n + 1] - cap[n],
+ NGX_ESCAPE_ARGS);
+ } else {
+ e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]);
+ }
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script capture: \"%*s\"", e->pos - pos, pos);
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_script_add_full_name_code(ngx_http_script_compile_t *sc)
+{
+ ngx_http_script_full_name_code_t *code;
+
+ code = ngx_http_script_add_code(*sc->lengths,
+ sizeof(ngx_http_script_full_name_code_t),
+ NULL);
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ code->code = (ngx_http_script_code_pt) ngx_http_script_full_name_len_code;
+ code->conf_prefix = sc->conf_prefix;
+
+ code = ngx_http_script_add_code(*sc->values,
+ sizeof(ngx_http_script_full_name_code_t),
+ &sc->main);
+ if (code == NULL) {
+ return NGX_ERROR;
+ }
+
+ code->code = ngx_http_script_full_name_code;
+ code->conf_prefix = sc->conf_prefix;
+
+ return NGX_OK;
+}
+
+
+static size_t
+ngx_http_script_full_name_len_code(ngx_http_script_engine_t *e)
+{
+ ngx_http_script_full_name_code_t *code;
+
+ code = (ngx_http_script_full_name_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_http_script_full_name_code_t);
+
+ return code->conf_prefix ? ngx_cycle->conf_prefix.len:
+ ngx_cycle->prefix.len;
+}
+
+
+static void
+ngx_http_script_full_name_code(ngx_http_script_engine_t *e)
+{
+ ngx_http_script_full_name_code_t *code;
+
+ ngx_str_t value, *prefix;
+
+ code = (ngx_http_script_full_name_code_t *) e->ip;
+
+ value.data = e->buf.data;
+ value.len = e->pos - e->buf.data;
+
+ prefix = code->conf_prefix ? (ngx_str_t *) &ngx_cycle->conf_prefix:
+ (ngx_str_t *) &ngx_cycle->prefix;
+
+ if (ngx_get_full_name(e->request->pool, prefix, &value) != NGX_OK) {
+ e->ip = ngx_http_script_exit;
+ e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ return;
+ }
+
+ e->buf = value;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script fullname: \"%V\"", &value);
+
+ e->ip += sizeof(ngx_http_script_full_name_code_t);
+}
+
+
+void
+ngx_http_script_return_code(ngx_http_script_engine_t *e)
+{
+ ngx_http_script_return_code_t *code;
+
+ code = (ngx_http_script_return_code_t *) e->ip;
+
+ if (code->status < NGX_HTTP_BAD_REQUEST
+ || code->text.value.len
+ || code->text.lengths)
+ {
+ e->status = ngx_http_send_response(e->request, code->status, NULL,
+ &code->text);
+ } else {
+ e->status = code->status;
+ }
+
+ e->ip = ngx_http_script_exit;
+}
+
+
+void
+ngx_http_script_break_code(ngx_http_script_engine_t *e)
+{
+ e->request->uri_changed = 0;
+
+ e->ip = ngx_http_script_exit;
+}
+
+
+void
+ngx_http_script_if_code(ngx_http_script_engine_t *e)
+{
+ ngx_http_script_if_code_t *code;
+
+ code = (ngx_http_script_if_code_t *) e->ip;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script if");
+
+ e->sp--;
+
+ if (e->sp->len && (e->sp->len != 1 || e->sp->data[0] != '0')) {
+ if (code->loc_conf) {
+ e->request->loc_conf = code->loc_conf;
+ ngx_http_update_location_config(e->request);
+ }
+
+ e->ip += sizeof(ngx_http_script_if_code_t);
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script if: false");
+
+ e->ip += code->next;
+}
+
+
+void
+ngx_http_script_equal_code(ngx_http_script_engine_t *e)
+{
+ ngx_http_variable_value_t *val, *res;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script equal");
+
+ e->sp--;
+ val = e->sp;
+ res = e->sp - 1;
+
+ e->ip += sizeof(uintptr_t);
+
+ if (val->len == res->len
+ && ngx_strncmp(val->data, res->data, res->len) == 0)
+ {
+ *res = ngx_http_variable_true_value;
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script equal: no");
+
+ *res = ngx_http_variable_null_value;
+}
+
+
+void
+ngx_http_script_not_equal_code(ngx_http_script_engine_t *e)
+{
+ ngx_http_variable_value_t *val, *res;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script not equal");
+
+ e->sp--;
+ val = e->sp;
+ res = e->sp - 1;
+
+ e->ip += sizeof(uintptr_t);
+
+ if (val->len == res->len
+ && ngx_strncmp(val->data, res->data, res->len) == 0)
+ {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script not equal: no");
+
+ *res = ngx_http_variable_null_value;
+ return;
+ }
+
+ *res = ngx_http_variable_true_value;
+}
+
+
+void
+ngx_http_script_file_code(ngx_http_script_engine_t *e)
+{
+ ngx_str_t path;
+ ngx_http_request_t *r;
+ ngx_open_file_info_t of;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_variable_value_t *value;
+ ngx_http_script_file_code_t *code;
+
+ value = e->sp - 1;
+
+ code = (ngx_http_script_file_code_t *) e->ip;
+ e->ip += sizeof(ngx_http_script_file_code_t);
+
+ path.len = value->len - 1;
+ path.data = value->data;
+
+ r = e->request;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http script file op %p \"%V\"", code->op, &path);
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ ngx_memzero(&of, sizeof(ngx_open_file_info_t));
+
+ of.read_ahead = clcf->read_ahead;
+ of.directio = clcf->directio;
+ of.valid = clcf->open_file_cache_valid;
+ of.min_uses = clcf->open_file_cache_min_uses;
+ of.test_only = 1;
+ of.errors = clcf->open_file_cache_errors;
+ of.events = clcf->open_file_cache_events;
+
+ if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
+ e->ip = ngx_http_script_exit;
+ e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ return;
+ }
+
+ if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
+ != NGX_OK)
+ {
+ if (of.err != NGX_ENOENT
+ && of.err != NGX_ENOTDIR
+ && of.err != NGX_ENAMETOOLONG)
+ {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,
+ "%s \"%s\" failed", of.failed, value->data);
+ }
+
+ switch (code->op) {
+
+ case ngx_http_script_file_plain:
+ case ngx_http_script_file_dir:
+ case ngx_http_script_file_exists:
+ case ngx_http_script_file_exec:
+ goto false_value;
+
+ case ngx_http_script_file_not_plain:
+ case ngx_http_script_file_not_dir:
+ case ngx_http_script_file_not_exists:
+ case ngx_http_script_file_not_exec:
+ goto true_value;
+ }
+
+ goto false_value;
+ }
+
+ switch (code->op) {
+ case ngx_http_script_file_plain:
+ if (of.is_file) {
+ goto true_value;
+ }
+ goto false_value;
+
+ case ngx_http_script_file_not_plain:
+ if (of.is_file) {
+ goto false_value;
+ }
+ goto true_value;
+
+ case ngx_http_script_file_dir:
+ if (of.is_dir) {
+ goto true_value;
+ }
+ goto false_value;
+
+ case ngx_http_script_file_not_dir:
+ if (of.is_dir) {
+ goto false_value;
+ }
+ goto true_value;
+
+ case ngx_http_script_file_exists:
+ if (of.is_file || of.is_dir || of.is_link) {
+ goto true_value;
+ }
+ goto false_value;
+
+ case ngx_http_script_file_not_exists:
+ if (of.is_file || of.is_dir || of.is_link) {
+ goto false_value;
+ }
+ goto true_value;
+
+ case ngx_http_script_file_exec:
+ if (of.is_exec) {
+ goto true_value;
+ }
+ goto false_value;
+
+ case ngx_http_script_file_not_exec:
+ if (of.is_exec) {
+ goto false_value;
+ }
+ goto true_value;
+ }
+
+false_value:
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http script file op false");
+
+ *value = ngx_http_variable_null_value;
+ return;
+
+true_value:
+
+ *value = ngx_http_variable_true_value;
+ return;
+}
+
+
+void
+ngx_http_script_complex_value_code(ngx_http_script_engine_t *e)
+{
+ size_t len;
+ ngx_http_script_engine_t le;
+ ngx_http_script_len_code_pt lcode;
+ ngx_http_script_complex_value_code_t *code;
+
+ code = (ngx_http_script_complex_value_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_http_script_complex_value_code_t);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script complex value");
+
+ ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
+
+ le.ip = code->lengths->elts;
+ le.line = e->line;
+ le.request = e->request;
+ le.quote = e->quote;
+
+ for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) {
+ lcode = *(ngx_http_script_len_code_pt *) le.ip;
+ }
+
+ e->buf.len = len;
+ e->buf.data = ngx_pnalloc(e->request->pool, len);
+ if (e->buf.data == NULL) {
+ e->ip = ngx_http_script_exit;
+ e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ return;
+ }
+
+ e->pos = e->buf.data;
+
+ e->sp->len = e->buf.len;
+ e->sp->data = e->buf.data;
+ e->sp++;
+}
+
+
+void
+ngx_http_script_value_code(ngx_http_script_engine_t *e)
+{
+ ngx_http_script_value_code_t *code;
+
+ code = (ngx_http_script_value_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_http_script_value_code_t);
+
+ e->sp->len = code->text_len;
+ e->sp->data = (u_char *) code->text_data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script value: \"%v\"", e->sp);
+
+ e->sp++;
+}
+
+
+void
+ngx_http_script_set_var_code(ngx_http_script_engine_t *e)
+{
+ ngx_http_request_t *r;
+ ngx_http_script_var_code_t *code;
+
+ code = (ngx_http_script_var_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_http_script_var_code_t);
+
+ r = e->request;
+
+ e->sp--;
+
+ r->variables[code->index].len = e->sp->len;
+ r->variables[code->index].valid = 1;
+ r->variables[code->index].no_cacheable = 0;
+ r->variables[code->index].not_found = 0;
+ r->variables[code->index].data = e->sp->data;
+
+#if (NGX_DEBUG)
+ {
+ ngx_http_variable_t *v;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ v = cmcf->variables.elts;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script set $%V", &v[code->index].name);
+ }
+#endif
+}
+
+
+void
+ngx_http_script_var_set_handler_code(ngx_http_script_engine_t *e)
+{
+ ngx_http_script_var_handler_code_t *code;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script set var handler");
+
+ code = (ngx_http_script_var_handler_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_http_script_var_handler_code_t);
+
+ e->sp--;
+
+ code->handler(e->request, e->sp, code->data);
+}
+
+
+void
+ngx_http_script_var_code(ngx_http_script_engine_t *e)
+{
+ ngx_http_variable_value_t *value;
+ ngx_http_script_var_code_t *code;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script var");
+
+ code = (ngx_http_script_var_code_t *) e->ip;
+
+ e->ip += sizeof(ngx_http_script_var_code_t);
+
+ value = ngx_http_get_flushed_variable(e->request, code->index);
+
+ if (value && !value->not_found) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
+ "http script var: \"%v\"", value);
+
+ *e->sp = *value;
+ e->sp++;
+
+ return;
+ }
+
+ *e->sp = ngx_http_variable_null_value;
+ e->sp++;
+}
+
+
+void
+ngx_http_script_nop_code(ngx_http_script_engine_t *e)
+{
+ e->ip += sizeof(uintptr_t);
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_script.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_script.h
new file mode 100644
index 00000000000..46592ab0155
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_script.h
@@ -0,0 +1,257 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_SCRIPT_H_INCLUDED_
+#define _NGX_HTTP_SCRIPT_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ u_char *ip;
+ u_char *pos;
+ ngx_http_variable_value_t *sp;
+
+ ngx_str_t buf;
+ ngx_str_t line;
+
+ /* the start of the rewritten arguments */
+ u_char *args;
+
+ unsigned flushed:1;
+ unsigned skip:1;
+ unsigned quote:1;
+ unsigned is_args:1;
+ unsigned log:1;
+
+ ngx_int_t status;
+ ngx_http_request_t *request;
+} ngx_http_script_engine_t;
+
+
+typedef struct {
+ ngx_conf_t *cf;
+ ngx_str_t *source;
+
+ ngx_array_t **flushes;
+ ngx_array_t **lengths;
+ ngx_array_t **values;
+
+ ngx_uint_t variables;
+ ngx_uint_t ncaptures;
+ ngx_uint_t captures_mask;
+ ngx_uint_t size;
+
+ void *main;
+
+ unsigned compile_args:1;
+ unsigned complete_lengths:1;
+ unsigned complete_values:1;
+ unsigned zero:1;
+ unsigned conf_prefix:1;
+ unsigned root_prefix:1;
+
+ unsigned dup_capture:1;
+ unsigned args:1;
+} ngx_http_script_compile_t;
+
+
+typedef struct {
+ ngx_str_t value;
+ ngx_uint_t *flushes;
+ void *lengths;
+ void *values;
+} ngx_http_complex_value_t;
+
+
+typedef struct {
+ ngx_conf_t *cf;
+ ngx_str_t *value;
+ ngx_http_complex_value_t *complex_value;
+
+ unsigned zero:1;
+ unsigned conf_prefix:1;
+ unsigned root_prefix:1;
+} ngx_http_compile_complex_value_t;
+
+
+typedef void (*ngx_http_script_code_pt) (ngx_http_script_engine_t *e);
+typedef size_t (*ngx_http_script_len_code_pt) (ngx_http_script_engine_t *e);
+
+
+typedef struct {
+ ngx_http_script_code_pt code;
+ uintptr_t len;
+} ngx_http_script_copy_code_t;
+
+
+typedef struct {
+ ngx_http_script_code_pt code;
+ uintptr_t index;
+} ngx_http_script_var_code_t;
+
+
+typedef struct {
+ ngx_http_script_code_pt code;
+ ngx_http_set_variable_pt handler;
+ uintptr_t data;
+} ngx_http_script_var_handler_code_t;
+
+
+typedef struct {
+ ngx_http_script_code_pt code;
+ uintptr_t n;
+} ngx_http_script_copy_capture_code_t;
+
+
+#if (NGX_PCRE)
+
+typedef struct {
+ ngx_http_script_code_pt code;
+ ngx_http_regex_t *regex;
+ ngx_array_t *lengths;
+ uintptr_t size;
+ uintptr_t status;
+ uintptr_t next;
+
+ uintptr_t test:1;
+ uintptr_t negative_test:1;
+ uintptr_t uri:1;
+ uintptr_t args:1;
+
+ /* add the r->args to the new arguments */
+ uintptr_t add_args:1;
+
+ uintptr_t redirect:1;
+ uintptr_t break_cycle:1;
+
+ ngx_str_t name;
+} ngx_http_script_regex_code_t;
+
+
+typedef struct {
+ ngx_http_script_code_pt code;
+
+ uintptr_t uri:1;
+ uintptr_t args:1;
+
+ /* add the r->args to the new arguments */
+ uintptr_t add_args:1;
+
+ uintptr_t redirect:1;
+} ngx_http_script_regex_end_code_t;
+
+#endif
+
+
+typedef struct {
+ ngx_http_script_code_pt code;
+ uintptr_t conf_prefix;
+} ngx_http_script_full_name_code_t;
+
+
+typedef struct {
+ ngx_http_script_code_pt code;
+ uintptr_t status;
+ ngx_http_complex_value_t text;
+} ngx_http_script_return_code_t;
+
+
+typedef enum {
+ ngx_http_script_file_plain = 0,
+ ngx_http_script_file_not_plain,
+ ngx_http_script_file_dir,
+ ngx_http_script_file_not_dir,
+ ngx_http_script_file_exists,
+ ngx_http_script_file_not_exists,
+ ngx_http_script_file_exec,
+ ngx_http_script_file_not_exec
+} ngx_http_script_file_op_e;
+
+
+typedef struct {
+ ngx_http_script_code_pt code;
+ uintptr_t op;
+} ngx_http_script_file_code_t;
+
+
+typedef struct {
+ ngx_http_script_code_pt code;
+ uintptr_t next;
+ void **loc_conf;
+} ngx_http_script_if_code_t;
+
+
+typedef struct {
+ ngx_http_script_code_pt code;
+ ngx_array_t *lengths;
+} ngx_http_script_complex_value_code_t;
+
+
+typedef struct {
+ ngx_http_script_code_pt code;
+ uintptr_t value;
+ uintptr_t text_len;
+ uintptr_t text_data;
+} ngx_http_script_value_code_t;
+
+
+void ngx_http_script_flush_complex_value(ngx_http_request_t *r,
+ ngx_http_complex_value_t *val);
+ngx_int_t ngx_http_complex_value(ngx_http_request_t *r,
+ ngx_http_complex_value_t *val, ngx_str_t *value);
+ngx_int_t ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv);
+char *ngx_http_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+ngx_int_t ngx_http_test_predicates(ngx_http_request_t *r,
+ ngx_array_t *predicates);
+char *ngx_http_set_predicate_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+ngx_uint_t ngx_http_script_variables_count(ngx_str_t *value);
+ngx_int_t ngx_http_script_compile(ngx_http_script_compile_t *sc);
+u_char *ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value,
+ void *code_lengths, size_t reserved, void *code_values);
+void ngx_http_script_flush_no_cacheable_variables(ngx_http_request_t *r,
+ ngx_array_t *indices);
+
+void *ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes,
+ size_t size);
+void *ngx_http_script_add_code(ngx_array_t *codes, size_t size, void *code);
+
+size_t ngx_http_script_copy_len_code(ngx_http_script_engine_t *e);
+void ngx_http_script_copy_code(ngx_http_script_engine_t *e);
+size_t ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e);
+void ngx_http_script_copy_var_code(ngx_http_script_engine_t *e);
+size_t ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e);
+void ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e);
+size_t ngx_http_script_mark_args_code(ngx_http_script_engine_t *e);
+void ngx_http_script_start_args_code(ngx_http_script_engine_t *e);
+#if (NGX_PCRE)
+void ngx_http_script_regex_start_code(ngx_http_script_engine_t *e);
+void ngx_http_script_regex_end_code(ngx_http_script_engine_t *e);
+#endif
+void ngx_http_script_return_code(ngx_http_script_engine_t *e);
+void ngx_http_script_break_code(ngx_http_script_engine_t *e);
+void ngx_http_script_if_code(ngx_http_script_engine_t *e);
+void ngx_http_script_equal_code(ngx_http_script_engine_t *e);
+void ngx_http_script_not_equal_code(ngx_http_script_engine_t *e);
+void ngx_http_script_file_code(ngx_http_script_engine_t *e);
+void ngx_http_script_complex_value_code(ngx_http_script_engine_t *e);
+void ngx_http_script_value_code(ngx_http_script_engine_t *e);
+void ngx_http_script_set_var_code(ngx_http_script_engine_t *e);
+void ngx_http_script_var_set_handler_code(ngx_http_script_engine_t *e);
+void ngx_http_script_var_code(ngx_http_script_engine_t *e);
+void ngx_http_script_nop_code(ngx_http_script_engine_t *e);
+
+
+#endif /* _NGX_HTTP_SCRIPT_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy.c
new file mode 100644
index 00000000000..478036979f3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy.c
@@ -0,0 +1,3647 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_http_spdy_module.h>
+
+#include <zlib.h>
+
+
+#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
+
+#define ngx_str5cmp(m, c0, c1, c2, c3, c4) \
+ *(uint32_t *) m == (c3 << 24 | c2 << 16 | c1 << 8 | c0) \
+ && m[4] == c4
+
+#else
+
+#define ngx_str5cmp(m, c0, c1, c2, c3, c4) \
+ m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4
+
+#endif
+
+
+#if (NGX_HAVE_NONALIGNED)
+
+#define ngx_spdy_frame_parse_uint16(p) ntohs(*(uint16_t *) (p))
+#define ngx_spdy_frame_parse_uint32(p) ntohl(*(uint32_t *) (p))
+
+#else
+
+#define ngx_spdy_frame_parse_uint16(p) ((p)[0] << 8 | (p)[1])
+#define ngx_spdy_frame_parse_uint32(p) \
+ ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])
+
+#endif
+
+#define ngx_spdy_frame_parse_sid(p) \
+ (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff)
+#define ngx_spdy_frame_parse_delta(p) \
+ (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff)
+
+
+#define ngx_spdy_ctl_frame_check(h) \
+ (((h) & 0xffff0000) == ngx_spdy_ctl_frame_head(0))
+#define ngx_spdy_data_frame_check(h) \
+ (!((h) & (uint32_t) NGX_SPDY_CTL_BIT << 31))
+
+#define ngx_spdy_ctl_frame_type(h) ((h) & 0x0000ffff)
+#define ngx_spdy_frame_flags(p) ((p) >> 24)
+#define ngx_spdy_frame_length(p) ((p) & 0x00ffffff)
+#define ngx_spdy_frame_id(p) ((p) & 0x00ffffff)
+
+
+#define NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE 4096
+#define NGX_SPDY_CTL_FRAME_BUFFER_SIZE 16
+
+#define NGX_SPDY_PROTOCOL_ERROR 1
+#define NGX_SPDY_INVALID_STREAM 2
+#define NGX_SPDY_REFUSED_STREAM 3
+#define NGX_SPDY_UNSUPPORTED_VERSION 4
+#define NGX_SPDY_CANCEL 5
+#define NGX_SPDY_INTERNAL_ERROR 6
+#define NGX_SPDY_FLOW_CONTROL_ERROR 7
+#define NGX_SPDY_STREAM_IN_USE 8
+#define NGX_SPDY_STREAM_ALREADY_CLOSED 9
+/* deprecated 10 */
+#define NGX_SPDY_FRAME_TOO_LARGE 11
+
+#define NGX_SPDY_SETTINGS_MAX_STREAMS 4
+#define NGX_SPDY_SETTINGS_INIT_WINDOW 7
+
+#define NGX_SPDY_SETTINGS_FLAG_PERSIST 0x01
+#define NGX_SPDY_SETTINGS_FLAG_PERSISTED 0x02
+
+#define NGX_SPDY_MAX_WINDOW NGX_MAX_INT32_VALUE
+#define NGX_SPDY_CONNECTION_WINDOW 65536
+#define NGX_SPDY_INIT_STREAM_WINDOW 65536
+#define NGX_SPDY_STREAM_WINDOW NGX_SPDY_MAX_WINDOW
+
+typedef struct {
+ ngx_uint_t hash;
+ u_char len;
+ u_char header[7];
+ ngx_int_t (*handler)(ngx_http_request_t *r);
+} ngx_http_spdy_request_header_t;
+
+
+static void ngx_http_spdy_read_handler(ngx_event_t *rev);
+static void ngx_http_spdy_write_handler(ngx_event_t *wev);
+static void ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc);
+
+static u_char *ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+static u_char *ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler);
+
+static u_char *ngx_http_spdy_state_inflate_error(
+ ngx_http_spdy_connection_t *sc, int rc);
+static u_char *ngx_http_spdy_state_protocol_error(
+ ngx_http_spdy_connection_t *sc);
+static u_char *ngx_http_spdy_state_internal_error(
+ ngx_http_spdy_connection_t *sc);
+
+static ngx_int_t ngx_http_spdy_send_window_update(
+ ngx_http_spdy_connection_t *sc, ngx_uint_t sid, ngx_uint_t delta);
+static ngx_int_t ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc,
+ ngx_uint_t sid, ngx_uint_t status, ngx_uint_t priority);
+static ngx_int_t ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc);
+static ngx_int_t ngx_http_spdy_settings_frame_handler(
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
+static ngx_http_spdy_out_frame_t *ngx_http_spdy_get_ctl_frame(
+ ngx_http_spdy_connection_t *sc, size_t size, ngx_uint_t priority);
+static ngx_int_t ngx_http_spdy_ctl_frame_handler(
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
+
+static ngx_http_spdy_stream_t *ngx_http_spdy_create_stream(
+ ngx_http_spdy_connection_t *sc, ngx_uint_t id, ngx_uint_t priority);
+static ngx_http_spdy_stream_t *ngx_http_spdy_get_stream_by_id(
+ ngx_http_spdy_connection_t *sc, ngx_uint_t sid);
+#define ngx_http_spdy_streams_index_size(sscf) (sscf->streams_index_mask + 1)
+#define ngx_http_spdy_stream_index(sscf, sid) \
+ ((sid >> 1) & sscf->streams_index_mask)
+
+static ngx_int_t ngx_http_spdy_parse_header(ngx_http_request_t *r);
+static ngx_int_t ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r);
+
+static ngx_int_t ngx_http_spdy_handle_request_header(ngx_http_request_t *r);
+static ngx_int_t ngx_http_spdy_parse_method(ngx_http_request_t *r);
+static ngx_int_t ngx_http_spdy_parse_scheme(ngx_http_request_t *r);
+static ngx_int_t ngx_http_spdy_parse_host(ngx_http_request_t *r);
+static ngx_int_t ngx_http_spdy_parse_path(ngx_http_request_t *r);
+static ngx_int_t ngx_http_spdy_parse_version(ngx_http_request_t *r);
+
+static ngx_int_t ngx_http_spdy_construct_request_line(ngx_http_request_t *r);
+static void ngx_http_spdy_run_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_spdy_init_request_body(ngx_http_request_t *r);
+
+static ngx_int_t ngx_http_spdy_terminate_stream(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_stream_t *stream, ngx_uint_t status);
+
+static void ngx_http_spdy_close_stream_handler(ngx_event_t *ev);
+
+static void ngx_http_spdy_handle_connection_handler(ngx_event_t *rev);
+static void ngx_http_spdy_keepalive_handler(ngx_event_t *rev);
+static void ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc,
+ ngx_int_t rc);
+
+static ngx_int_t ngx_http_spdy_adjust_windows(ngx_http_spdy_connection_t *sc,
+ ssize_t delta);
+
+static void ngx_http_spdy_pool_cleanup(void *data);
+
+static void *ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size);
+static void ngx_http_spdy_zfree(void *opaque, void *address);
+
+
+static const u_char ngx_http_spdy_dict[] = {
+ 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, /* - - - - o p t i */
+ 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, /* o n s - - - - h */
+ 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, /* e a d - - - - p */
+ 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, /* o s t - - - - p */
+ 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, /* u t - - - - d e */
+ 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, /* l e t e - - - - */
+ 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, /* t r a c e - - - */
+ 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, /* - a c c e p t - */
+ 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, /* - - - a c c e p */
+ 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, /* t - c h a r s e */
+ 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, /* t - - - - a c c */
+ 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, /* e p t - e n c o */
+ 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, /* d i n g - - - - */
+ 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, /* a c c e p t - l */
+ 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, /* a n g u a g e - */
+ 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, /* - - - a c c e p */
+ 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, /* t - r a n g e s */
+ 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, /* - - - - a g e - */
+ 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, /* - - - a l l o w */
+ 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, /* - - - - a u t h */
+ 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, /* o r i z a t i o */
+ 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, /* n - - - - c a c */
+ 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, /* h e - c o n t r */
+ 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, /* o l - - - - c o */
+ 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, /* n n e c t i o n */
+ 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, /* - - - - c o n t */
+ 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, /* e n t - b a s e */
+ 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, /* - - - - c o n t */
+ 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, /* e n t - e n c o */
+ 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, /* d i n g - - - - */
+ 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, /* c o n t e n t - */
+ 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, /* l a n g u a g e */
+ 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, /* - - - - c o n t */
+ 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, /* e n t - l e n g */
+ 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, /* t h - - - - c o */
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, /* n t e n t - l o */
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, /* c a t i o n - - */
+ 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, /* - - c o n t e n */
+ 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, /* t - m d 5 - - - */
+ 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, /* - c o n t e n t */
+ 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, /* - r a n g e - - */
+ 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, /* - - c o n t e n */
+ 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, /* t - t y p e - - */
+ 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, /* - - d a t e - - */
+ 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, /* - - e t a g - - */
+ 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, /* - - e x p e c t */
+ 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, /* - - - - e x p i */
+ 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, /* r e s - - - - f */
+ 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, /* r o m - - - - h */
+ 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, /* o s t - - - - i */
+ 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, /* f - m a t c h - */
+ 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, /* - - - i f - m o */
+ 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, /* d i f i e d - s */
+ 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, /* i n c e - - - - */
+ 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, /* i f - n o n e - */
+ 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, /* m a t c h - - - */
+ 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, /* - i f - r a n g */
+ 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, /* e - - - - i f - */
+ 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, /* u n m o d i f i */
+ 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, /* e d - s i n c e */
+ 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, /* - - - - l a s t */
+ 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, /* - m o d i f i e */
+ 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, /* d - - - - l o c */
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, /* a t i o n - - - */
+ 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, /* - m a x - f o r */
+ 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, /* w a r d s - - - */
+ 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, /* - p r a g m a - */
+ 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, /* - - - p r o x y */
+ 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, /* - a u t h e n t */
+ 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, /* i c a t e - - - */
+ 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, /* - p r o x y - a */
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, /* u t h o r i z a */
+ 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, /* t i o n - - - - */
+ 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, /* r a n g e - - - */
+ 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, /* - r e f e r e r */
+ 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, /* - - - - r e t r */
+ 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, /* y - a f t e r - */
+ 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, /* - - - s e r v e */
+ 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, /* r - - - - t e - */
+ 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, /* - - - t r a i l */
+ 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, /* e r - - - - t r */
+ 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, /* a n s f e r - e */
+ 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, /* n c o d i n g - */
+ 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, /* - - - u p g r a */
+ 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, /* d e - - - - u s */
+ 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, /* e r - a g e n t */
+ 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, /* - - - - v a r y */
+ 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, /* - - - - v i a - */
+ 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, /* - - - w a r n i */
+ 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, /* n g - - - - w w */
+ 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, /* w - a u t h e n */
+ 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, /* t i c a t e - - */
+ 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, /* - - m e t h o d */
+ 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, /* - - - - g e t - */
+ 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, /* - - - s t a t u */
+ 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, /* s - - - - 2 0 0 */
+ 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, /* - O K - - - - v */
+ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, /* e r s i o n - - */
+ 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, /* - - H T T P - 1 */
+ 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, /* - 1 - - - - u r */
+ 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, /* l - - - - p u b */
+ 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, /* l i c - - - - s */
+ 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, /* e t - c o o k i */
+ 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, /* e - - - - k e e */
+ 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, /* p - a l i v e - */
+ 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, /* - - - o r i g i */
+ 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, /* n 1 0 0 1 0 1 2 */
+ 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, /* 0 1 2 0 2 2 0 5 */
+ 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, /* 2 0 6 3 0 0 3 0 */
+ 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, /* 2 3 0 3 3 0 4 3 */
+ 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, /* 0 5 3 0 6 3 0 7 */
+ 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, /* 4 0 2 4 0 5 4 0 */
+ 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, /* 6 4 0 7 4 0 8 4 */
+ 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, /* 0 9 4 1 0 4 1 1 */
+ 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, /* 4 1 2 4 1 3 4 1 */
+ 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, /* 4 4 1 5 4 1 6 4 */
+ 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, /* 1 7 5 0 2 5 0 4 */
+ 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, /* 5 0 5 2 0 3 - N */
+ 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, /* o n - A u t h o */
+ 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, /* r i t a t i v e */
+ 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, /* - I n f o r m a */
+ 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, /* t i o n 2 0 4 - */
+ 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, /* N o - C o n t e */
+ 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, /* n t 3 0 1 - M o */
+ 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, /* v e d - P e r m */
+ 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, /* a n e n t l y 4 */
+ 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, /* 0 0 - B a d - R */
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, /* e q u e s t 4 0 */
+ 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, /* 1 - U n a u t h */
+ 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, /* o r i z e d 4 0 */
+ 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, /* 3 - F o r b i d */
+ 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, /* d e n 4 0 4 - N */
+ 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, /* o t - F o u n d */
+ 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, /* 5 0 0 - I n t e */
+ 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, /* r n a l - S e r */
+ 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, /* v e r - E r r o */
+ 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, /* r 5 0 1 - N o t */
+ 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, /* - I m p l e m e */
+ 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, /* n t e d 5 0 3 - */
+ 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, /* S e r v i c e - */
+ 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, /* U n a v a i l a */
+ 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, /* b l e J a n - F */
+ 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, /* e b - M a r - A */
+ 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, /* p r - M a y - J */
+ 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, /* u n - J u l - A */
+ 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, /* u g - S e p t - */
+ 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, /* O c t - N o v - */
+ 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, /* D e c - 0 0 - 0 */
+ 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, /* 0 - 0 0 - M o n */
+ 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, /* - - T u e - - W */
+ 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, /* e d - - T h u - */
+ 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, /* - F r i - - S a */
+ 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, /* t - - S u n - - */
+ 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, /* G M T c h u n k */
+ 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, /* e d - t e x t - */
+ 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, /* h t m l - i m a */
+ 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, /* g e - p n g - i */
+ 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, /* m a g e - j p g */
+ 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, /* - i m a g e - g */
+ 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, /* i f - a p p l i */
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, /* c a t i o n - x */
+ 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, /* m l - a p p l i */
+ 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, /* c a t i o n - x */
+ 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, /* h t m l - x m l */
+ 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, /* - t e x t - p l */
+ 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, /* a i n - t e x t */
+ 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, /* - j a v a s c r */
+ 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, /* i p t - p u b l */
+ 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, /* i c p r i v a t */
+ 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, /* e m a x - a g e */
+ 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, /* - g z i p - d e */
+ 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, /* f l a t e - s d */
+ 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, /* c h c h a r s e */
+ 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, /* t - u t f - 8 c */
+ 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, /* h a r s e t - i */
+ 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, /* s o - 8 8 5 9 - */
+ 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, /* 1 - u t f - - - */
+ 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e /* - e n q - 0 - */
+};
+
+
+static ngx_http_spdy_request_header_t ngx_http_spdy_request_headers[] = {
+ { 0, 6, "method", ngx_http_spdy_parse_method },
+ { 0, 6, "scheme", ngx_http_spdy_parse_scheme },
+ { 0, 4, "host", ngx_http_spdy_parse_host },
+ { 0, 4, "path", ngx_http_spdy_parse_path },
+ { 0, 7, "version", ngx_http_spdy_parse_version },
+};
+
+#define NGX_SPDY_REQUEST_HEADERS \
+ (sizeof(ngx_http_spdy_request_headers) \
+ / sizeof(ngx_http_spdy_request_header_t))
+
+
+void
+ngx_http_spdy_init(ngx_event_t *rev)
+{
+ int rc;
+ ngx_connection_t *c;
+ ngx_pool_cleanup_t *cln;
+ ngx_http_connection_t *hc;
+ ngx_http_spdy_srv_conf_t *sscf;
+ ngx_http_spdy_main_conf_t *smcf;
+ ngx_http_spdy_connection_t *sc;
+
+ c = rev->data;
+ hc = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "init spdy request");
+
+ c->log->action = "processing SPDY";
+
+ smcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_spdy_module);
+
+ if (smcf->recv_buffer == NULL) {
+ smcf->recv_buffer = ngx_palloc(ngx_cycle->pool, smcf->recv_buffer_size);
+ if (smcf->recv_buffer == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+ }
+
+ sc = ngx_pcalloc(c->pool, sizeof(ngx_http_spdy_connection_t));
+ if (sc == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ sc->connection = c;
+ sc->http_connection = hc;
+
+ sc->send_window = NGX_SPDY_CONNECTION_WINDOW;
+ sc->recv_window = NGX_SPDY_CONNECTION_WINDOW;
+
+ sc->init_window = NGX_SPDY_INIT_STREAM_WINDOW;
+
+ sc->handler = hc->proxy_protocol ? ngx_http_spdy_proxy_protocol
+ : ngx_http_spdy_state_head;
+
+ sc->zstream_in.zalloc = ngx_http_spdy_zalloc;
+ sc->zstream_in.zfree = ngx_http_spdy_zfree;
+ sc->zstream_in.opaque = sc;
+
+ rc = inflateInit(&sc->zstream_in);
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "inflateInit() failed: %d", rc);
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ sc->zstream_out.zalloc = ngx_http_spdy_zalloc;
+ sc->zstream_out.zfree = ngx_http_spdy_zfree;
+ sc->zstream_out.opaque = sc;
+
+ sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_spdy_module);
+
+ rc = deflateInit2(&sc->zstream_out, (int) sscf->headers_comp,
+ Z_DEFLATED, 11, 4, Z_DEFAULT_STRATEGY);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "deflateInit2() failed: %d", rc);
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ rc = deflateSetDictionary(&sc->zstream_out, ngx_http_spdy_dict,
+ sizeof(ngx_http_spdy_dict));
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "deflateSetDictionary() failed: %d", rc);
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log);
+ if (sc->pool == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_pool_cleanup_file_t));
+ if (cln == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ cln->handler = ngx_http_spdy_pool_cleanup;
+ cln->data = sc;
+
+ sc->streams_index = ngx_pcalloc(sc->pool,
+ ngx_http_spdy_streams_index_size(sscf)
+ * sizeof(ngx_http_spdy_stream_t *));
+ if (sc->streams_index == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (ngx_http_spdy_send_settings(sc) == NGX_ERROR) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (ngx_http_spdy_send_window_update(sc, 0, NGX_SPDY_MAX_WINDOW
+ - sc->recv_window)
+ == NGX_ERROR)
+ {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ sc->recv_window = NGX_SPDY_MAX_WINDOW;
+
+ ngx_queue_init(&sc->waiting);
+ ngx_queue_init(&sc->posted);
+
+ c->data = sc;
+
+ rev->handler = ngx_http_spdy_read_handler;
+ c->write->handler = ngx_http_spdy_write_handler;
+
+ ngx_http_spdy_read_handler(rev);
+}
+
+
+static void
+ngx_http_spdy_read_handler(ngx_event_t *rev)
+{
+ u_char *p, *end;
+ size_t available;
+ ssize_t n;
+ ngx_connection_t *c;
+ ngx_http_spdy_main_conf_t *smcf;
+ ngx_http_spdy_connection_t *sc;
+
+ c = rev->data;
+ sc = c->data;
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy read handler");
+
+ sc->blocked = 1;
+
+ smcf = ngx_http_get_module_main_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ available = smcf->recv_buffer_size - 2 * NGX_SPDY_STATE_BUFFER_SIZE;
+
+ do {
+ p = smcf->recv_buffer;
+
+ ngx_memcpy(p, sc->buffer, NGX_SPDY_STATE_BUFFER_SIZE);
+ end = p + sc->buffer_used;
+
+ n = c->recv(c, end, available);
+
+ if (n == NGX_AGAIN) {
+ break;
+ }
+
+ if (n == 0 && (sc->incomplete || sc->processing)) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client prematurely closed connection");
+ }
+
+ if (n == 0 || n == NGX_ERROR) {
+ ngx_http_spdy_finalize_connection(sc,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ end += n;
+
+ sc->buffer_used = 0;
+ sc->incomplete = 0;
+
+ do {
+ p = sc->handler(sc, p, end);
+
+ if (p == NULL) {
+ return;
+ }
+
+ } while (p != end);
+
+ } while (rev->ready);
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (sc->last_out && ngx_http_spdy_send_output_queue(sc) == NGX_ERROR) {
+ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ sc->blocked = 0;
+
+ if (sc->processing) {
+ if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+ return;
+ }
+
+ ngx_http_spdy_handle_connection(sc);
+}
+
+
+static void
+ngx_http_spdy_write_handler(ngx_event_t *wev)
+{
+ ngx_int_t rc;
+ ngx_queue_t *q;
+ ngx_connection_t *c;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_spdy_connection_t *sc;
+
+ c = wev->data;
+ sc = c->data;
+
+ if (wev->timedout) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "spdy write event timed out");
+ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy write handler");
+
+ sc->blocked = 1;
+
+ rc = ngx_http_spdy_send_output_queue(sc);
+
+ if (rc == NGX_ERROR) {
+ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ while (!ngx_queue_empty(&sc->posted)) {
+ q = ngx_queue_head(&sc->posted);
+
+ ngx_queue_remove(q);
+
+ stream = ngx_queue_data(q, ngx_http_spdy_stream_t, queue);
+
+ stream->handled = 0;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "run spdy stream %ui", stream->id);
+
+ wev = stream->request->connection->write;
+ wev->handler(wev);
+ }
+
+ sc->blocked = 0;
+
+ if (rc == NGX_AGAIN) {
+ return;
+ }
+
+ ngx_http_spdy_handle_connection(sc);
+}
+
+
+ngx_int_t
+ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc)
+{
+ ngx_chain_t *cl;
+ ngx_event_t *wev;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_spdy_out_frame_t *out, *frame, *fn;
+
+ c = sc->connection;
+
+ if (c->error) {
+ return NGX_ERROR;
+ }
+
+ wev = c->write;
+
+ if (!wev->ready) {
+ return NGX_OK;
+ }
+
+ cl = NULL;
+ out = NULL;
+
+ for (frame = sc->last_out; frame; frame = fn) {
+ frame->last->next = cl;
+ cl = frame->first;
+
+ fn = frame->next;
+ frame->next = out;
+ out = frame;
+
+ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "spdy frame out: %p sid:%ui prio:%ui bl:%d len:%uz",
+ out, out->stream ? out->stream->id : 0, out->priority,
+ out->blocked, out->length);
+ }
+
+ cl = c->send_chain(c, cl, 0);
+
+ if (cl == NGX_CHAIN_ERROR) {
+ c->error = 1;
+
+ if (!sc->blocked) {
+ ngx_post_event(wev, &ngx_posted_events);
+ }
+
+ return NGX_ERROR;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(sc->http_connection->conf_ctx,
+ ngx_http_core_module);
+
+ if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {
+ return NGX_ERROR; /* FIXME */
+ }
+
+ if (cl) {
+ ngx_add_timer(wev, clcf->send_timeout);
+
+ } else {
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+ }
+
+ for ( /* void */ ; out; out = fn) {
+ fn = out->next;
+
+ if (out->handler(sc, out) != NGX_OK) {
+ out->blocked = 1;
+ out->priority = NGX_SPDY_HIGHEST_PRIORITY;
+ break;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "spdy frame sent: %p sid:%ui bl:%d len:%uz",
+ out, out->stream ? out->stream->id : 0,
+ out->blocked, out->length);
+ }
+
+ frame = NULL;
+
+ for ( /* void */ ; out; out = fn) {
+ fn = out->next;
+ out->next = frame;
+ frame = out;
+ }
+
+ sc->last_out = frame;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc)
+{
+ ngx_connection_t *c;
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ if (sc->last_out || sc->processing) {
+ return;
+ }
+
+ c = sc->connection;
+
+ if (c->error) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ if (c->buffered) {
+ return;
+ }
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+ if (sc->incomplete) {
+ ngx_add_timer(c->read, sscf->recv_timeout);
+ return;
+ }
+
+ if (ngx_terminate || ngx_exiting) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ ngx_destroy_pool(sc->pool);
+
+ sc->pool = NULL;
+ sc->free_ctl_frames = NULL;
+ sc->free_fake_connections = NULL;
+
+#if (NGX_HTTP_SSL)
+ if (c->ssl) {
+ ngx_ssl_free_buffer(c);
+ }
+#endif
+
+ c->destroyed = 1;
+ c->idle = 1;
+ ngx_reusable_connection(c, 1);
+
+ c->write->handler = ngx_http_empty_handler;
+ c->read->handler = ngx_http_spdy_keepalive_handler;
+
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ ngx_add_timer(c->read, sscf->keepalive_timeout);
+}
+
+
+static u_char *
+ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ ngx_log_t *log;
+
+ log = sc->connection->log;
+ log->action = "reading PROXY protocol";
+
+ pos = ngx_proxy_protocol_parse(sc->connection, pos, end);
+
+ log->action = "processing SPDY";
+
+ if (pos == NULL) {
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ uint32_t head, flen;
+ ngx_uint_t type;
+
+ if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_head);
+ }
+
+ head = ngx_spdy_frame_parse_uint32(pos);
+
+ pos += sizeof(uint32_t);
+
+ flen = ngx_spdy_frame_parse_uint32(pos);
+
+ sc->flags = ngx_spdy_frame_flags(flen);
+ sc->length = ngx_spdy_frame_length(flen);
+
+ pos += sizeof(uint32_t);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "process spdy frame head:%08XD f:%Xd l:%uz",
+ head, sc->flags, sc->length);
+
+ if (ngx_spdy_ctl_frame_check(head)) {
+ type = ngx_spdy_ctl_frame_type(head);
+
+ switch (type) {
+
+ case NGX_SPDY_SYN_STREAM:
+ return ngx_http_spdy_state_syn_stream(sc, pos, end);
+
+ case NGX_SPDY_SYN_REPLY:
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client sent unexpected SYN_REPLY frame");
+ return ngx_http_spdy_state_protocol_error(sc);
+
+ case NGX_SPDY_RST_STREAM:
+ return ngx_http_spdy_state_rst_stream(sc, pos, end);
+
+ case NGX_SPDY_SETTINGS:
+ return ngx_http_spdy_state_settings(sc, pos, end);
+
+ case NGX_SPDY_PING:
+ return ngx_http_spdy_state_ping(sc, pos, end);
+
+ case NGX_SPDY_GOAWAY:
+ return ngx_http_spdy_state_skip(sc, pos, end); /* TODO */
+
+ case NGX_SPDY_HEADERS:
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client sent unexpected HEADERS frame");
+ return ngx_http_spdy_state_protocol_error(sc);
+
+ case NGX_SPDY_WINDOW_UPDATE:
+ return ngx_http_spdy_state_window_update(sc, pos, end);
+
+ default:
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy control frame with unknown type %ui", type);
+ return ngx_http_spdy_state_skip(sc, pos, end);
+ }
+ }
+
+ if (ngx_spdy_data_frame_check(head)) {
+ sc->stream = ngx_http_spdy_get_stream_by_id(sc, head);
+ return ngx_http_spdy_state_data(sc, pos, end);
+ }
+
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client sent invalid frame");
+
+ return ngx_http_spdy_state_protocol_error(sc);
+}
+
+
+static u_char *
+ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ ngx_uint_t sid, prio;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ if (end - pos < NGX_SPDY_SYN_STREAM_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_syn_stream);
+ }
+
+ if (sc->length <= NGX_SPDY_SYN_STREAM_SIZE) {
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client sent SYN_STREAM frame with incorrect length %uz",
+ sc->length);
+
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ sc->length -= NGX_SPDY_SYN_STREAM_SIZE;
+
+ sid = ngx_spdy_frame_parse_sid(pos);
+ prio = pos[8] >> 5;
+
+ pos += NGX_SPDY_SYN_STREAM_SIZE;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy SYN_STREAM frame sid:%ui prio:%ui", sid, prio);
+
+ if (sid % 2 == 0 || sid <= sc->last_sid) {
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client sent SYN_STREAM frame "
+ "with invalid Stream-ID %ui", sid);
+
+ stream = ngx_http_spdy_get_stream_by_id(sc, sid);
+
+ if (stream) {
+ if (ngx_http_spdy_terminate_stream(sc, stream,
+ NGX_SPDY_PROTOCOL_ERROR)
+ != NGX_OK)
+ {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+ }
+
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ sc->last_sid = sid;
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ if (sc->processing >= sscf->concurrent_streams) {
+
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "concurrent streams exceeded %ui", sc->processing);
+
+ if (ngx_http_spdy_send_rst_stream(sc, sid, NGX_SPDY_REFUSED_STREAM,
+ prio)
+ != NGX_OK)
+ {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ return ngx_http_spdy_state_headers_skip(sc, pos, end);
+ }
+
+ stream = ngx_http_spdy_create_stream(sc, sid, prio);
+ if (stream == NULL) {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ stream->in_closed = (sc->flags & NGX_SPDY_FLAG_FIN) ? 1 : 0;
+
+ stream->request->request_length = NGX_SPDY_FRAME_HEADER_SIZE
+ + NGX_SPDY_SYN_STREAM_SIZE
+ + sc->length;
+
+ sc->stream = stream;
+
+ return ngx_http_spdy_state_headers(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ int z;
+ size_t size;
+ ngx_buf_t *buf;
+ ngx_int_t rc;
+ ngx_http_request_t *r;
+
+ size = end - pos;
+
+ if (size == 0) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_headers);
+ }
+
+ if (size > sc->length) {
+ size = sc->length;
+ }
+
+ r = sc->stream->request;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "process spdy header block %uz of %uz", size, sc->length);
+
+ buf = r->header_in;
+
+ sc->zstream_in.next_in = pos;
+ sc->zstream_in.avail_in = size;
+ sc->zstream_in.next_out = buf->last;
+
+ /* one byte is reserved for null-termination of the last header value */
+ sc->zstream_in.avail_out = buf->end - buf->last - 1;
+
+ z = inflate(&sc->zstream_in, Z_NO_FLUSH);
+
+ if (z == Z_NEED_DICT) {
+ z = inflateSetDictionary(&sc->zstream_in, ngx_http_spdy_dict,
+ sizeof(ngx_http_spdy_dict));
+
+ if (z != Z_OK) {
+ if (z == Z_DATA_ERROR) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent SYN_STREAM frame with header "
+ "block encoded using wrong dictionary: %ul",
+ (u_long) sc->zstream_in.adler);
+
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "inflateSetDictionary() failed: %d", z);
+
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy inflateSetDictionary(): %d", z);
+
+ z = sc->zstream_in.avail_in ? inflate(&sc->zstream_in, Z_NO_FLUSH)
+ : Z_OK;
+ }
+
+ if (z != Z_OK) {
+ return ngx_http_spdy_state_inflate_error(sc, z);
+ }
+
+ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
+ sc->zstream_in.next_in, sc->zstream_in.next_out,
+ sc->zstream_in.avail_in, sc->zstream_in.avail_out,
+ z);
+
+ sc->length -= sc->zstream_in.next_in - pos;
+ pos = sc->zstream_in.next_in;
+
+ buf->last = sc->zstream_in.next_out;
+
+ if (r->headers_in.headers.part.elts == NULL) {
+
+ if (buf->last - buf->pos < NGX_SPDY_NV_NUM_SIZE) {
+
+ if (sc->length == 0) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "premature end of spdy header block");
+
+ return ngx_http_spdy_state_headers_error(sc, pos, end);
+ }
+
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_headers);
+ }
+
+ sc->entries = ngx_spdy_frame_parse_uint32(buf->pos);
+
+ buf->pos += NGX_SPDY_NV_NUM_SIZE;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy header block has %ui entries",
+ sc->entries);
+
+ if (ngx_list_init(&r->headers_in.headers, r->pool, 20,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ ngx_http_spdy_close_stream(sc->stream,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return ngx_http_spdy_state_headers_skip(sc, pos, end);
+ }
+
+ if (ngx_array_init(&r->headers_in.cookies, r->pool, 2,
+ sizeof(ngx_table_elt_t *))
+ != NGX_OK)
+ {
+ ngx_http_spdy_close_stream(sc->stream,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return ngx_http_spdy_state_headers_skip(sc, pos, end);
+ }
+ }
+
+ while (sc->entries) {
+
+ rc = ngx_http_spdy_parse_header(r);
+
+ switch (rc) {
+
+ case NGX_DONE:
+ sc->entries--;
+
+ case NGX_OK:
+ break;
+
+ case NGX_AGAIN:
+
+ if (sc->zstream_in.avail_in) {
+
+ rc = ngx_http_spdy_alloc_large_header_buffer(r);
+
+ if (rc == NGX_DECLINED) {
+ ngx_http_finalize_request(r,
+ NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
+ return ngx_http_spdy_state_headers_skip(sc, pos, end);
+ }
+
+ if (rc != NGX_OK) {
+ ngx_http_spdy_close_stream(sc->stream,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return ngx_http_spdy_state_headers_skip(sc, pos, end);
+ }
+
+ /* null-terminate the last processed header name or value */
+ *buf->pos = '\0';
+
+ buf = r->header_in;
+
+ sc->zstream_in.next_out = buf->last;
+
+ /* one byte is reserved for null-termination */
+ sc->zstream_in.avail_out = buf->end - buf->last - 1;
+
+ z = inflate(&sc->zstream_in, Z_NO_FLUSH);
+
+ if (z != Z_OK) {
+ return ngx_http_spdy_state_inflate_error(sc, z);
+ }
+
+ sc->length -= sc->zstream_in.next_in - pos;
+ pos = sc->zstream_in.next_in;
+
+ buf->last = sc->zstream_in.next_out;
+
+ continue;
+ }
+
+ if (sc->length == 0) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "premature end of spdy header block");
+
+ return ngx_http_spdy_state_headers_error(sc, pos, end);
+ }
+
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_headers);
+
+ case NGX_HTTP_PARSE_INVALID_HEADER:
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return ngx_http_spdy_state_headers_skip(sc, pos, end);
+
+ default: /* NGX_ERROR */
+ return ngx_http_spdy_state_headers_error(sc, pos, end);
+ }
+
+ /* a header line has been parsed successfully */
+
+ rc = ngx_http_spdy_handle_request_header(r);
+
+ if (rc != NGX_OK) {
+ if (rc == NGX_HTTP_PARSE_INVALID_HEADER) {
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return ngx_http_spdy_state_headers_skip(sc, pos, end);
+ }
+
+ if (rc != NGX_ABORT) {
+ ngx_http_spdy_close_stream(sc->stream,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ }
+
+ return ngx_http_spdy_state_headers_skip(sc, pos, end);
+ }
+ }
+
+ if (buf->pos != buf->last || sc->zstream_in.avail_in) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "incorrect number of spdy header block entries");
+
+ return ngx_http_spdy_state_headers_error(sc, pos, end);
+ }
+
+ if (sc->length) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_headers);
+ }
+
+ /* null-terminate the last header value */
+ *buf->pos = '\0';
+
+ ngx_http_spdy_run_request(r);
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ int n;
+ size_t size;
+ u_char buffer[NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE];
+
+ if (sc->length == 0) {
+ return ngx_http_spdy_state_complete(sc, pos, end);
+ }
+
+ size = end - pos;
+
+ if (size == 0) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_headers_skip);
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy header block skip %uz of %uz", size, sc->length);
+
+ sc->zstream_in.next_in = pos;
+ sc->zstream_in.avail_in = (size < sc->length) ? size : sc->length;
+
+ while (sc->zstream_in.avail_in) {
+ sc->zstream_in.next_out = buffer;
+ sc->zstream_in.avail_out = NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE;
+
+ n = inflate(&sc->zstream_in, Z_NO_FLUSH);
+
+ if (n != Z_OK) {
+ return ngx_http_spdy_state_inflate_error(sc, n);
+ }
+ }
+
+ pos = sc->zstream_in.next_in;
+
+ if (size < sc->length) {
+ sc->length -= size;
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_headers_skip);
+ }
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ ngx_http_spdy_stream_t *stream;
+
+ stream = sc->stream;
+
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client sent SYN_STREAM frame for stream %ui "
+ "with invalid header block", stream->id);
+
+ if (ngx_http_spdy_send_rst_stream(sc, stream->id, NGX_SPDY_PROTOCOL_ERROR,
+ stream->priority)
+ != NGX_OK)
+ {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ stream->out_closed = 1;
+
+ ngx_http_spdy_close_stream(stream, NGX_HTTP_BAD_REQUEST);
+
+ return ngx_http_spdy_state_headers_skip(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_window_update(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ size_t delta;
+ ngx_uint_t sid;
+ ngx_event_t *wev;
+ ngx_queue_t *q;
+ ngx_http_spdy_stream_t *stream;
+
+ if (end - pos < NGX_SPDY_WINDOW_UPDATE_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_window_update);
+ }
+
+ if (sc->length != NGX_SPDY_WINDOW_UPDATE_SIZE) {
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client sent WINDOW_UPDATE frame "
+ "with incorrect length %uz", sc->length);
+
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ sid = ngx_spdy_frame_parse_sid(pos);
+
+ pos += NGX_SPDY_SID_SIZE;
+
+ delta = ngx_spdy_frame_parse_delta(pos);
+
+ pos += NGX_SPDY_DELTA_SIZE;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy WINDOW_UPDATE sid:%ui delta:%ui", sid, delta);
+
+ if (sid) {
+ stream = ngx_http_spdy_get_stream_by_id(sc, sid);
+
+ if (stream == NULL) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "unknown spdy stream");
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+ }
+
+ if (stream->send_window > (ssize_t) (NGX_SPDY_MAX_WINDOW - delta)) {
+
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client violated flow control for stream %ui: "
+ "received WINDOW_UPDATE frame with delta %uz "
+ "not allowed for window %z",
+ sid, delta, stream->send_window);
+
+ if (ngx_http_spdy_terminate_stream(sc, stream,
+ NGX_SPDY_FLOW_CONTROL_ERROR)
+ == NGX_ERROR)
+ {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+ }
+
+ stream->send_window += delta;
+
+ if (stream->exhausted) {
+ stream->exhausted = 0;
+
+ wev = stream->request->connection->write;
+
+ if (!wev->timer_set) {
+ wev->delayed = 0;
+ wev->handler(wev);
+ }
+ }
+
+ } else {
+ sc->send_window += delta;
+
+ if (sc->send_window > NGX_SPDY_MAX_WINDOW) {
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client violated connection flow control: "
+ "received WINDOW_UPDATE frame with delta %uz "
+ "not allowed for window %uz",
+ delta, sc->send_window);
+
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ while (!ngx_queue_empty(&sc->waiting)) {
+ q = ngx_queue_head(&sc->waiting);
+
+ ngx_queue_remove(q);
+
+ stream = ngx_queue_data(q, ngx_http_spdy_stream_t, queue);
+
+ stream->handled = 0;
+
+ wev = stream->request->connection->write;
+
+ if (!wev->timer_set) {
+ wev->delayed = 0;
+ wev->handler(wev);
+
+ if (sc->send_window == 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ ngx_http_spdy_stream_t *stream;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy DATA frame");
+
+ if (sc->length > sc->recv_window) {
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client violated connection flow control: "
+ "received DATA frame length %uz, available window %uz",
+ sc->length, sc->recv_window);
+
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ sc->recv_window -= sc->length;
+
+ if (sc->recv_window < NGX_SPDY_MAX_WINDOW / 4) {
+
+ if (ngx_http_spdy_send_window_update(sc, 0,
+ NGX_SPDY_MAX_WINDOW
+ - sc->recv_window)
+ == NGX_ERROR)
+ {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ sc->recv_window = NGX_SPDY_MAX_WINDOW;
+ }
+
+ stream = sc->stream;
+
+ if (stream == NULL) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "unknown spdy stream");
+
+ return ngx_http_spdy_state_skip(sc, pos, end);
+ }
+
+ if (sc->length > stream->recv_window) {
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client violated flow control for stream %ui: "
+ "received DATA frame length %uz, available window %uz",
+ stream->id, sc->length, stream->recv_window);
+
+ if (ngx_http_spdy_terminate_stream(sc, stream,
+ NGX_SPDY_FLOW_CONTROL_ERROR)
+ == NGX_ERROR)
+ {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ return ngx_http_spdy_state_skip(sc, pos, end);
+ }
+
+ stream->recv_window -= sc->length;
+
+ if (stream->recv_window < NGX_SPDY_STREAM_WINDOW / 4) {
+
+ if (ngx_http_spdy_send_window_update(sc, stream->id,
+ NGX_SPDY_STREAM_WINDOW
+ - stream->recv_window)
+ == NGX_ERROR)
+ {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ stream->recv_window = NGX_SPDY_STREAM_WINDOW;
+ }
+
+ if (stream->in_closed) {
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client sent DATA frame for half-closed stream %ui",
+ stream->id);
+
+ if (ngx_http_spdy_terminate_stream(sc, stream,
+ NGX_SPDY_STREAM_ALREADY_CLOSED)
+ == NGX_ERROR)
+ {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ return ngx_http_spdy_state_skip(sc, pos, end);
+ }
+
+ return ngx_http_spdy_state_read_data(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_read_data(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *buf;
+ ngx_int_t rc;
+ ngx_temp_file_t *tf;
+ ngx_http_request_t *r;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_request_body_t *rb;
+ ngx_http_core_loc_conf_t *clcf;
+
+ stream = sc->stream;
+
+ if (stream == NULL) {
+ return ngx_http_spdy_state_skip(sc, pos, end);
+ }
+
+ if (stream->skip_data) {
+
+ if (sc->flags & NGX_SPDY_FLAG_FIN) {
+ stream->in_closed = 1;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "skipping spdy DATA frame, reason: %d",
+ stream->skip_data);
+
+ return ngx_http_spdy_state_skip(sc, pos, end);
+ }
+
+ size = end - pos;
+
+ if (size > sc->length) {
+ size = sc->length;
+ }
+
+ r = stream->request;
+
+ if (r->request_body == NULL
+ && ngx_http_spdy_init_request_body(r) != NGX_OK)
+ {
+ stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;
+ return ngx_http_spdy_state_skip(sc, pos, end);
+ }
+
+ rb = r->request_body;
+ tf = rb->temp_file;
+ buf = rb->buf;
+
+ if (size) {
+ rb->rest += size;
+
+ if (r->headers_in.content_length_n != -1
+ && r->headers_in.content_length_n < rb->rest)
+ {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client intended to send body data "
+ "larger than declared");
+
+ stream->skip_data = NGX_SPDY_DATA_ERROR;
+ goto error;
+
+ } else {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->client_max_body_size
+ && clcf->client_max_body_size < rb->rest)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "client intended to send "
+ "too large chunked body: %O bytes", rb->rest);
+
+ stream->skip_data = NGX_SPDY_DATA_ERROR;
+ goto error;
+ }
+ }
+
+ sc->length -= size;
+
+ if (tf) {
+ buf->start = pos;
+ buf->pos = pos;
+
+ pos += size;
+
+ buf->end = pos;
+ buf->last = pos;
+
+ n = ngx_write_chain_to_temp_file(tf, rb->bufs);
+
+ /* TODO: n == 0 or not complete and level event */
+
+ if (n == NGX_ERROR) {
+ stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;
+ goto error;
+ }
+
+ tf->offset += n;
+
+ } else {
+ buf->last = ngx_cpymem(buf->last, pos, size);
+ pos += size;
+ }
+
+ r->request_length += size;
+ }
+
+ if (sc->length) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_read_data);
+ }
+
+ if (sc->flags & NGX_SPDY_FLAG_FIN) {
+
+ stream->in_closed = 1;
+
+ if (r->headers_in.content_length_n < 0) {
+ r->headers_in.content_length_n = rb->rest;
+
+ } else if (r->headers_in.content_length_n != rb->rest) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client prematurely closed stream: "
+ "only %O out of %O bytes of request body received",
+ rb->rest, r->headers_in.content_length_n);
+
+ stream->skip_data = NGX_SPDY_DATA_ERROR;
+ goto error;
+ }
+
+ if (tf) {
+ ngx_memzero(buf, sizeof(ngx_buf_t));
+
+ buf->in_file = 1;
+ buf->file_last = tf->file.offset;
+ buf->file = &tf->file;
+
+ rb->buf = NULL;
+ }
+
+ if (rb->post_handler) {
+ r->read_event_handler = ngx_http_block_reading;
+ rb->post_handler(r);
+ }
+ }
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+
+error:
+
+ if (rb->post_handler) {
+
+ if (stream->skip_data == NGX_SPDY_DATA_ERROR) {
+ rc = (r->headers_in.content_length_n == -1)
+ ? NGX_HTTP_REQUEST_ENTITY_TOO_LARGE
+ : NGX_HTTP_BAD_REQUEST;
+
+ } else {
+ rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ngx_http_finalize_request(r, rc);
+ }
+
+ return ngx_http_spdy_state_skip(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ ngx_uint_t sid, status;
+ ngx_event_t *ev;
+ ngx_connection_t *fc;
+ ngx_http_spdy_stream_t *stream;
+
+ if (end - pos < NGX_SPDY_RST_STREAM_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_rst_stream);
+ }
+
+ if (sc->length != NGX_SPDY_RST_STREAM_SIZE) {
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client sent RST_STREAM frame with incorrect length %uz",
+ sc->length);
+
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ sid = ngx_spdy_frame_parse_sid(pos);
+
+ pos += NGX_SPDY_SID_SIZE;
+
+ status = ngx_spdy_frame_parse_uint32(pos);
+
+ pos += sizeof(uint32_t);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy RST_STREAM sid:%ui st:%ui", sid, status);
+
+ stream = ngx_http_spdy_get_stream_by_id(sc, sid);
+
+ if (stream == NULL) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "unknown spdy stream");
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+ }
+
+ stream->in_closed = 1;
+ stream->out_closed = 1;
+
+ fc = stream->request->connection;
+ fc->error = 1;
+
+ switch (status) {
+
+ case NGX_SPDY_CANCEL:
+ ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+ "client canceled stream %ui", sid);
+ break;
+
+ case NGX_SPDY_INTERNAL_ERROR:
+ ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+ "client terminated stream %ui due to internal error",
+ sid);
+ break;
+
+ default:
+ ngx_log_error(NGX_LOG_INFO, fc->log, 0,
+ "client terminated stream %ui with status %ui",
+ sid, status);
+ break;
+ }
+
+ ev = fc->read;
+ ev->handler(ev);
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ u_char *p;
+ ngx_buf_t *buf;
+ ngx_http_spdy_out_frame_t *frame;
+
+ if (end - pos < NGX_SPDY_PING_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_ping);
+ }
+
+ if (sc->length != NGX_SPDY_PING_SIZE) {
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client sent PING frame with incorrect length %uz",
+ sc->length);
+
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy PING frame");
+
+ frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_PING_SIZE,
+ NGX_SPDY_HIGHEST_PRIORITY);
+ if (frame == NULL) {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ buf = frame->first->buf;
+
+ p = buf->pos;
+
+ p = ngx_spdy_frame_write_head(p, NGX_SPDY_PING);
+ p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_PING_SIZE);
+
+ p = ngx_cpymem(p, pos, NGX_SPDY_PING_SIZE);
+
+ buf->last = p;
+
+ ngx_http_spdy_queue_frame(sc, frame);
+
+ pos += NGX_SPDY_PING_SIZE;
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ size_t size;
+
+ size = end - pos;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy frame skip %uz of %uz", size, sc->length);
+
+ if (size < sc->length) {
+ sc->length -= size;
+ return ngx_http_spdy_state_save(sc, end, end,
+ ngx_http_spdy_state_skip);
+ }
+
+ return ngx_http_spdy_state_complete(sc, pos + sc->length, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ ngx_uint_t fid, val;
+
+ if (sc->entries == 0) {
+
+ if (end - pos < NGX_SPDY_SETTINGS_NUM_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_settings);
+ }
+
+ sc->entries = ngx_spdy_frame_parse_uint32(pos);
+
+ pos += NGX_SPDY_SETTINGS_NUM_SIZE;
+ sc->length -= NGX_SPDY_SETTINGS_NUM_SIZE;
+
+ if (sc->length < sc->entries * NGX_SPDY_SETTINGS_PAIR_SIZE) {
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client sent SETTINGS frame with incorrect "
+ "length %uz or number of entries %ui",
+ sc->length, sc->entries);
+
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy SETTINGS frame has %ui entries", sc->entries);
+ }
+
+ while (sc->entries) {
+ if (end - pos < NGX_SPDY_SETTINGS_PAIR_SIZE) {
+ return ngx_http_spdy_state_save(sc, pos, end,
+ ngx_http_spdy_state_settings);
+ }
+
+ sc->entries--;
+ sc->length -= NGX_SPDY_SETTINGS_PAIR_SIZE;
+
+ fid = ngx_spdy_frame_parse_uint32(pos);
+
+ pos += NGX_SPDY_SETTINGS_FID_SIZE;
+
+ val = ngx_spdy_frame_parse_uint32(pos);
+
+ pos += NGX_SPDY_SETTINGS_VAL_SIZE;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy SETTINGS entry fl:%ui id:%ui val:%ui",
+ ngx_spdy_frame_flags(fid), ngx_spdy_frame_id(fid), val);
+
+ if (ngx_spdy_frame_flags(fid) == NGX_SPDY_SETTINGS_FLAG_PERSISTED) {
+ continue;
+ }
+
+ switch (ngx_spdy_frame_id(fid)) {
+
+ case NGX_SPDY_SETTINGS_INIT_WINDOW:
+
+ if (val > NGX_SPDY_MAX_WINDOW) {
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client sent SETTINGS frame with "
+ "incorrect INIT_WINDOW value: %ui", val);
+
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ if (ngx_http_spdy_adjust_windows(sc, val - sc->init_window)
+ != NGX_OK)
+ {
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ sc->init_window = val;
+
+ continue;
+ }
+ }
+
+ return ngx_http_spdy_state_complete(sc, pos, end);
+}
+
+
+static u_char *
+ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, u_char *pos,
+ u_char *end)
+{
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy frame complete pos:%p end:%p", pos, end);
+
+ if (pos > end) {
+ ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,
+ "receive buffer overrun");
+
+ ngx_debug_point();
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ sc->handler = ngx_http_spdy_state_head;
+ sc->stream = NULL;
+
+ return pos;
+}
+
+
+static u_char *
+ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler)
+{
+ size_t size;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy frame state save pos:%p end:%p handler:%p",
+ pos, end, handler);
+
+ size = end - pos;
+
+ if (size > NGX_SPDY_STATE_BUFFER_SIZE) {
+ ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,
+ "state buffer overflow: %uz bytes required", size);
+
+ ngx_debug_point();
+ return ngx_http_spdy_state_internal_error(sc);
+ }
+
+ ngx_memcpy(sc->buffer, pos, NGX_SPDY_STATE_BUFFER_SIZE);
+
+ sc->buffer_used = size;
+ sc->handler = handler;
+ sc->incomplete = 1;
+
+ return end;
+}
+
+
+static u_char *
+ngx_http_spdy_state_inflate_error(ngx_http_spdy_connection_t *sc, int rc)
+{
+ if (rc == Z_DATA_ERROR || rc == Z_STREAM_END) {
+ ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0,
+ "client sent SYN_STREAM frame with "
+ "corrupted header block, inflate() failed: %d", rc);
+
+ return ngx_http_spdy_state_protocol_error(sc);
+ }
+
+ ngx_log_error(NGX_LOG_ERR, sc->connection->log, 0,
+ "inflate() failed: %d", rc);
+
+ return ngx_http_spdy_state_internal_error(sc);
+}
+
+
+static u_char *
+ngx_http_spdy_state_protocol_error(ngx_http_spdy_connection_t *sc)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy state protocol error");
+
+ if (sc->stream) {
+ sc->stream->out_closed = 1;
+ ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST);
+ }
+
+ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST);
+
+ return NULL;
+}
+
+
+static u_char *
+ngx_http_spdy_state_internal_error(ngx_http_spdy_connection_t *sc)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy state internal error");
+
+ if (sc->stream) {
+ sc->stream->out_closed = 1;
+ ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ }
+
+ ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR);
+
+ return NULL;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_send_window_update(ngx_http_spdy_connection_t *sc, ngx_uint_t sid,
+ ngx_uint_t delta)
+{
+ u_char *p;
+ ngx_buf_t *buf;
+ ngx_http_spdy_out_frame_t *frame;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy send WINDOW_UPDATE sid:%ui delta:%ui", sid, delta);
+
+ frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_WINDOW_UPDATE_SIZE,
+ NGX_SPDY_HIGHEST_PRIORITY);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf = frame->first->buf;
+
+ p = buf->pos;
+
+ p = ngx_spdy_frame_write_head(p, NGX_SPDY_WINDOW_UPDATE);
+ p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_WINDOW_UPDATE_SIZE);
+
+ p = ngx_spdy_frame_write_sid(p, sid);
+ p = ngx_spdy_frame_aligned_write_uint32(p, delta);
+
+ buf->last = p;
+
+ ngx_http_spdy_queue_frame(sc, frame);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t sid,
+ ngx_uint_t status, ngx_uint_t priority)
+{
+ u_char *p;
+ ngx_buf_t *buf;
+ ngx_http_spdy_out_frame_t *frame;
+
+ if (sc->connection->error) {
+ return NGX_OK;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy send RST_STREAM sid:%ui st:%ui", sid, status);
+
+ frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_RST_STREAM_SIZE,
+ priority);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf = frame->first->buf;
+
+ p = buf->pos;
+
+ p = ngx_spdy_frame_write_head(p, NGX_SPDY_RST_STREAM);
+ p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_RST_STREAM_SIZE);
+
+ p = ngx_spdy_frame_write_sid(p, sid);
+ p = ngx_spdy_frame_aligned_write_uint32(p, status);
+
+ buf->last = p;
+
+ ngx_http_spdy_queue_frame(sc, frame);
+
+ return NGX_OK;
+}
+
+
+#if 0
+static ngx_int_t
+ngx_http_spdy_send_goaway(ngx_http_spdy_connection_t *sc)
+{
+ u_char *p;
+ ngx_buf_t *buf;
+ ngx_http_spdy_out_frame_t *frame;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy send GOAWAY sid:%ui", sc->last_sid);
+
+ frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_GOAWAY_SIZE,
+ NGX_SPDY_HIGHEST_PRIORITY);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf = frame->first->buf;
+
+ p = buf->pos;
+
+ p = ngx_spdy_frame_write_head(p, NGX_SPDY_GOAWAY);
+ p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_GOAWAY_SIZE);
+
+ p = ngx_spdy_frame_write_sid(p, sc->last_sid);
+
+ buf->last = p;
+
+ ngx_http_spdy_queue_frame(sc, frame);
+
+ return NGX_OK;
+}
+#endif
+
+
+static ngx_int_t
+ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc)
+{
+ u_char *p;
+ ngx_buf_t *buf;
+ ngx_chain_t *cl;
+ ngx_http_spdy_srv_conf_t *sscf;
+ ngx_http_spdy_out_frame_t *frame;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy send SETTINGS frame");
+
+ frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t));
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl = ngx_alloc_chain_link(sc->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf = ngx_create_temp_buf(sc->pool, NGX_SPDY_FRAME_HEADER_SIZE
+ + NGX_SPDY_SETTINGS_NUM_SIZE
+ + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE);
+ if (buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ buf->last_buf = 1;
+
+ cl->buf = buf;
+ cl->next = NULL;
+
+ frame->first = cl;
+ frame->last = cl;
+ frame->handler = ngx_http_spdy_settings_frame_handler;
+ frame->stream = NULL;
+#if (NGX_DEBUG)
+ frame->length = NGX_SPDY_SETTINGS_NUM_SIZE
+ + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE;
+#endif
+ frame->priority = NGX_SPDY_HIGHEST_PRIORITY;
+ frame->blocked = 0;
+
+ p = buf->pos;
+
+ p = ngx_spdy_frame_write_head(p, NGX_SPDY_SETTINGS);
+ p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_CLEAR_SETTINGS,
+ NGX_SPDY_SETTINGS_NUM_SIZE
+ + 2 * NGX_SPDY_SETTINGS_PAIR_SIZE);
+
+ p = ngx_spdy_frame_aligned_write_uint32(p, 2);
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ p = ngx_spdy_frame_write_flags_and_id(p, 0, NGX_SPDY_SETTINGS_MAX_STREAMS);
+ p = ngx_spdy_frame_aligned_write_uint32(p, sscf->concurrent_streams);
+
+ p = ngx_spdy_frame_write_flags_and_id(p, 0, NGX_SPDY_SETTINGS_INIT_WINDOW);
+ p = ngx_spdy_frame_aligned_write_uint32(p, NGX_SPDY_STREAM_WINDOW);
+
+ buf->last = p;
+
+ ngx_http_spdy_queue_frame(sc, frame);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_spdy_settings_frame_handler(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_buf_t *buf;
+
+ buf = frame->first->buf;
+
+ if (buf->pos != buf->last) {
+ return NGX_AGAIN;
+ }
+
+ ngx_free_chain(sc->pool, frame->first);
+
+ return NGX_OK;
+}
+
+
+static ngx_http_spdy_out_frame_t *
+ngx_http_spdy_get_ctl_frame(ngx_http_spdy_connection_t *sc, size_t length,
+ ngx_uint_t priority)
+{
+ ngx_chain_t *cl;
+ ngx_http_spdy_out_frame_t *frame;
+
+ frame = sc->free_ctl_frames;
+
+ if (frame) {
+ sc->free_ctl_frames = frame->next;
+
+ cl = frame->first;
+ cl->buf->pos = cl->buf->start;
+
+ } else {
+ frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t));
+ if (frame == NULL) {
+ return NULL;
+ }
+
+ cl = ngx_alloc_chain_link(sc->pool);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ cl->buf = ngx_create_temp_buf(sc->pool,
+ NGX_SPDY_CTL_FRAME_BUFFER_SIZE);
+ if (cl->buf == NULL) {
+ return NULL;
+ }
+
+ cl->buf->last_buf = 1;
+
+ frame->first = cl;
+ frame->last = cl;
+ frame->handler = ngx_http_spdy_ctl_frame_handler;
+ frame->stream = NULL;
+ }
+
+#if (NGX_DEBUG)
+ if (length > NGX_SPDY_CTL_FRAME_BUFFER_SIZE - NGX_SPDY_FRAME_HEADER_SIZE) {
+ ngx_log_error(NGX_LOG_ALERT, sc->pool->log, 0,
+ "requested control frame is too large: %uz", length);
+ return NULL;
+ }
+
+ frame->length = length;
+#endif
+
+ frame->priority = priority;
+ frame->blocked = 0;
+
+ return frame;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_ctl_frame_handler(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_buf_t *buf;
+
+ buf = frame->first->buf;
+
+ if (buf->pos != buf->last) {
+ return NGX_AGAIN;
+ }
+
+ frame->next = sc->free_ctl_frames;
+ sc->free_ctl_frames = frame;
+
+ return NGX_OK;
+}
+
+
+static ngx_http_spdy_stream_t *
+ngx_http_spdy_create_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t id,
+ ngx_uint_t priority)
+{
+ ngx_log_t *log;
+ ngx_uint_t index;
+ ngx_event_t *rev, *wev;
+ ngx_connection_t *fc;
+ ngx_http_log_ctx_t *ctx;
+ ngx_http_request_t *r;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ fc = sc->free_fake_connections;
+
+ if (fc) {
+ sc->free_fake_connections = fc->data;
+
+ rev = fc->read;
+ wev = fc->write;
+ log = fc->log;
+ ctx = log->data;
+
+ } else {
+ fc = ngx_palloc(sc->pool, sizeof(ngx_connection_t));
+ if (fc == NULL) {
+ return NULL;
+ }
+
+ rev = ngx_palloc(sc->pool, sizeof(ngx_event_t));
+ if (rev == NULL) {
+ return NULL;
+ }
+
+ wev = ngx_palloc(sc->pool, sizeof(ngx_event_t));
+ if (wev == NULL) {
+ return NULL;
+ }
+
+ log = ngx_palloc(sc->pool, sizeof(ngx_log_t));
+ if (log == NULL) {
+ return NULL;
+ }
+
+ ctx = ngx_palloc(sc->pool, sizeof(ngx_http_log_ctx_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->connection = fc;
+ ctx->request = NULL;
+ }
+
+ ngx_memcpy(log, sc->connection->log, sizeof(ngx_log_t));
+
+ log->data = ctx;
+
+ ngx_memzero(rev, sizeof(ngx_event_t));
+
+ rev->data = fc;
+ rev->ready = 1;
+ rev->handler = ngx_http_spdy_close_stream_handler;
+ rev->log = log;
+
+ ngx_memcpy(wev, rev, sizeof(ngx_event_t));
+
+ wev->write = 1;
+
+ ngx_memcpy(fc, sc->connection, sizeof(ngx_connection_t));
+
+ fc->data = sc->http_connection;
+ fc->read = rev;
+ fc->write = wev;
+ fc->sent = 0;
+ fc->log = log;
+ fc->buffered = 0;
+ fc->sndlowat = 1;
+ fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
+
+ r = ngx_http_create_request(fc);
+ if (r == NULL) {
+ return NULL;
+ }
+
+ r->valid_location = 1;
+
+ fc->data = r;
+ sc->connection->requests++;
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ r->header_in = ngx_create_temp_buf(r->pool,
+ cscf->client_header_buffer_size);
+ if (r->header_in == NULL) {
+ ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NULL;
+ }
+
+ r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;
+
+ stream = ngx_pcalloc(r->pool, sizeof(ngx_http_spdy_stream_t));
+ if (stream == NULL) {
+ ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NULL;
+ }
+
+ r->spdy_stream = stream;
+
+ stream->id = id;
+ stream->request = r;
+ stream->connection = sc;
+
+ stream->send_window = sc->init_window;
+ stream->recv_window = NGX_SPDY_STREAM_WINDOW;
+
+ stream->priority = priority;
+
+ sscf = ngx_http_get_module_srv_conf(r, ngx_http_spdy_module);
+
+ index = ngx_http_spdy_stream_index(sscf, id);
+
+ stream->index = sc->streams_index[index];
+ sc->streams_index[index] = stream;
+
+ sc->processing++;
+
+ return stream;
+}
+
+
+static ngx_http_spdy_stream_t *
+ngx_http_spdy_get_stream_by_id(ngx_http_spdy_connection_t *sc,
+ ngx_uint_t sid)
+{
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ stream = sc->streams_index[ngx_http_spdy_stream_index(sscf, sid)];
+
+ while (stream) {
+ if (stream->id == sid) {
+ return stream;
+ }
+
+ stream = stream->index;
+ }
+
+ return NULL;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_parse_header(ngx_http_request_t *r)
+{
+ u_char *p, *end, ch;
+ ngx_uint_t hash;
+ ngx_http_core_srv_conf_t *cscf;
+
+ enum {
+ sw_name_len = 0,
+ sw_name,
+ sw_value_len,
+ sw_value
+ } state;
+
+ state = r->state;
+
+ p = r->header_in->pos;
+ end = r->header_in->last;
+
+ switch (state) {
+
+ case sw_name_len:
+
+ if (end - p < NGX_SPDY_NV_NLEN_SIZE) {
+ return NGX_AGAIN;
+ }
+
+ r->lowcase_index = ngx_spdy_frame_parse_uint32(p);
+
+ if (r->lowcase_index == 0) {
+ return NGX_ERROR;
+ }
+
+ /* null-terminate the previous header value */
+ *p = '\0';
+
+ p += NGX_SPDY_NV_NLEN_SIZE;
+
+ r->invalid_header = 0;
+
+ state = sw_name;
+
+ /* fall through */
+
+ case sw_name:
+
+ if ((ngx_uint_t) (end - p) < r->lowcase_index) {
+ break;
+ }
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ r->header_name_start = p;
+ r->header_name_end = p + r->lowcase_index;
+
+ if (p[0] == ':') {
+ p++;
+ }
+
+ hash = 0;
+
+ for ( /* void */ ; p != r->header_name_end; p++) {
+
+ ch = *p;
+
+ hash = ngx_hash(hash, ch);
+
+ if ((ch >= 'a' && ch <= 'z')
+ || (ch == '-')
+ || (ch >= '0' && ch <= '9')
+ || (ch == '_' && cscf->underscores_in_headers))
+ {
+ continue;
+ }
+
+ switch (ch) {
+ case '\0':
+ case LF:
+ case CR:
+ case ':':
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid header name: \"%*s\"",
+ r->lowcase_index, r->header_name_start);
+
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ if (ch >= 'A' && ch <= 'Z') {
+ return NGX_ERROR;
+ }
+
+ r->invalid_header = 1;
+ }
+
+ r->header_hash = hash;
+
+ state = sw_value_len;
+
+ /* fall through */
+
+ case sw_value_len:
+
+ if (end - p < NGX_SPDY_NV_VLEN_SIZE) {
+ break;
+ }
+
+ r->lowcase_index = ngx_spdy_frame_parse_uint32(p);
+
+ /* null-terminate header name */
+ *p = '\0';
+
+ p += NGX_SPDY_NV_VLEN_SIZE;
+
+ state = sw_value;
+
+ /* fall through */
+
+ case sw_value:
+
+ if ((ngx_uint_t) (end - p) < r->lowcase_index) {
+ break;
+ }
+
+ r->header_start = p;
+
+ while (r->lowcase_index--) {
+ ch = *p;
+
+ if (ch == '\0') {
+
+ if (p == r->header_start) {
+ return NGX_ERROR;
+ }
+
+ r->header_end = p;
+ r->header_in->pos = p + 1;
+
+ r->state = sw_value;
+
+ return NGX_OK;
+ }
+
+ if (ch == CR || ch == LF) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent header \"%*s\" with "
+ "invalid value: \"%*s\\%c...\"",
+ r->header_name_end - r->header_name_start,
+ r->header_name_start,
+ p - r->header_start,
+ r->header_start,
+ ch == CR ? 'r' : 'n');
+
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ p++;
+ }
+
+ r->header_end = p;
+ r->header_in->pos = p;
+
+ r->state = 0;
+
+ return NGX_DONE;
+ }
+
+ r->header_in->pos = p;
+ r->state = state;
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r)
+{
+ u_char *old, *new, *p;
+ size_t rest;
+ ngx_buf_t *buf;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_core_srv_conf_t *cscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy alloc large header buffer");
+
+ stream = r->spdy_stream;
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ if (stream->header_buffers
+ == (ngx_uint_t) cscf->large_client_header_buffers.num)
+ {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent too large request");
+
+ return NGX_DECLINED;
+ }
+
+ rest = r->header_in->last - r->header_in->pos;
+
+ /*
+ * equality is prohibited since one more byte is needed
+ * for null-termination
+ */
+ if (rest >= cscf->large_client_header_buffers.size) {
+ p = r->header_in->pos;
+
+ if (rest > NGX_MAX_ERROR_STR - 300) {
+ rest = NGX_MAX_ERROR_STR - 300;
+ p[rest++] = '.'; p[rest++] = '.'; p[rest++] = '.';
+ }
+
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent too long header name or value: \"%*s\"",
+ rest, p);
+
+ return NGX_DECLINED;
+ }
+
+ buf = ngx_create_temp_buf(r->pool, cscf->large_client_header_buffers.size);
+ if (buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy large header alloc: %p %uz",
+ buf->pos, buf->end - buf->last);
+
+ old = r->header_in->pos;
+ new = buf->pos;
+
+ if (rest) {
+ buf->last = ngx_cpymem(new, old, rest);
+ }
+
+ r->header_in = buf;
+
+ stream->header_buffers++;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_handle_request_header(ngx_http_request_t *r)
+{
+ ngx_uint_t i;
+ ngx_table_elt_t *h;
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_spdy_request_header_t *sh;
+
+ if (r->invalid_header) {
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ if (cscf->ignore_invalid_headers) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid header: \"%*s\"",
+ r->header_end - r->header_name_start,
+ r->header_name_start);
+ return NGX_OK;
+ }
+
+ }
+
+ if (r->header_name_start[0] == ':') {
+ r->header_name_start++;
+
+ for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) {
+ sh = &ngx_http_spdy_request_headers[i];
+
+ if (sh->hash != r->header_hash
+ || sh->len != r->header_name_end - r->header_name_start
+ || ngx_strncmp(sh->header, r->header_name_start, sh->len) != 0)
+ {
+ continue;
+ }
+
+ return sh->handler(r);
+ }
+
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid header name: \":%*s\"",
+ r->header_end - r->header_name_start,
+ r->header_name_start);
+
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ h = ngx_list_push(&r->headers_in.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ h->hash = r->header_hash;
+
+ h->key.len = r->header_name_end - r->header_name_start;
+ h->key.data = r->header_name_start;
+
+ h->value.len = r->header_end - r->header_start;
+ h->value.data = r->header_start;
+
+ h->lowcase_key = h->key.data;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_http_spdy_request_headers_init(void)
+{
+ ngx_uint_t i;
+ ngx_http_spdy_request_header_t *h;
+
+ for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) {
+ h = &ngx_http_spdy_request_headers[i];
+ h->hash = ngx_hash_key(h->header, h->len);
+ }
+}
+
+
+static ngx_int_t
+ngx_http_spdy_parse_method(ngx_http_request_t *r)
+{
+ size_t k, len;
+ ngx_uint_t n;
+ const u_char *p, *m;
+
+ /*
+ * This array takes less than 256 sequential bytes,
+ * and if typical CPU cache line size is 64 bytes,
+ * it is prefetched for 4 load operations.
+ */
+ static const struct {
+ u_char len;
+ const u_char method[11];
+ uint32_t value;
+ } tests[] = {
+ { 3, "GET", NGX_HTTP_GET },
+ { 4, "POST", NGX_HTTP_POST },
+ { 4, "HEAD", NGX_HTTP_HEAD },
+ { 7, "OPTIONS", NGX_HTTP_OPTIONS },
+ { 8, "PROPFIND", NGX_HTTP_PROPFIND },
+ { 3, "PUT", NGX_HTTP_PUT },
+ { 5, "MKCOL", NGX_HTTP_MKCOL },
+ { 6, "DELETE", NGX_HTTP_DELETE },
+ { 4, "COPY", NGX_HTTP_COPY },
+ { 4, "MOVE", NGX_HTTP_MOVE },
+ { 9, "PROPPATCH", NGX_HTTP_PROPPATCH },
+ { 4, "LOCK", NGX_HTTP_LOCK },
+ { 6, "UNLOCK", NGX_HTTP_UNLOCK },
+ { 5, "PATCH", NGX_HTTP_PATCH },
+ { 5, "TRACE", NGX_HTTP_TRACE }
+ }, *test;
+
+ if (r->method_name.len) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent duplicate :method header");
+
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ len = r->header_end - r->header_start;
+
+ r->method_name.len = len;
+ r->method_name.data = r->header_start;
+
+ test = tests;
+ n = sizeof(tests) / sizeof(tests[0]);
+
+ do {
+ if (len == test->len) {
+ p = r->method_name.data;
+ m = test->method;
+ k = len;
+
+ do {
+ if (*p++ != *m++) {
+ goto next;
+ }
+ } while (--k);
+
+ r->method = test->value;
+ return NGX_OK;
+ }
+
+ next:
+ test++;
+
+ } while (--n);
+
+ p = r->method_name.data;
+
+ do {
+ if ((*p < 'A' || *p > 'Z') && *p != '_') {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid method: \"%V\"",
+ &r->method_name);
+
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ p++;
+
+ } while (--len);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_parse_scheme(ngx_http_request_t *r)
+{
+ if (r->schema_start) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent duplicate :schema header");
+
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ r->schema_start = r->header_start;
+ r->schema_end = r->header_end;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_parse_host(ngx_http_request_t *r)
+{
+ ngx_table_elt_t *h;
+
+ if (r->headers_in.host) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent duplicate :host header");
+
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ h = ngx_list_push(&r->headers_in.headers);
+ if (h == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->headers_in.host = h;
+
+ h->hash = r->header_hash;
+
+ h->key.len = r->header_name_end - r->header_name_start;
+ h->key.data = r->header_name_start;
+
+ h->value.len = r->header_end - r->header_start;
+ h->value.data = r->header_start;
+
+ h->lowcase_key = h->key.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_parse_path(ngx_http_request_t *r)
+{
+ if (r->unparsed_uri.len) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent duplicate :path header");
+
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ r->uri_start = r->header_start;
+ r->uri_end = r->header_end;
+
+ if (ngx_http_parse_uri(r) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid URI: \"%*s\"",
+ r->uri_end - r->uri_start, r->uri_start);
+
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ if (ngx_http_process_request_uri(r) != NGX_OK) {
+ /*
+ * request has been finalized already
+ * in ngx_http_process_request_uri()
+ */
+ return NGX_ABORT;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_parse_version(ngx_http_request_t *r)
+{
+ u_char *p, ch;
+
+ if (r->http_protocol.len) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent duplicate :version header");
+
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+ }
+
+ p = r->header_start;
+
+ if (r->header_end - p < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) {
+ goto invalid;
+ }
+
+ ch = *(p + 5);
+
+ if (ch < '1' || ch > '9') {
+ goto invalid;
+ }
+
+ r->http_major = ch - '0';
+
+ for (p += 6; p != r->header_end - 2; p++) {
+
+ ch = *p;
+
+ if (ch == '.') {
+ break;
+ }
+
+ if (ch < '0' || ch > '9') {
+ goto invalid;
+ }
+
+ r->http_major = r->http_major * 10 + ch - '0';
+ }
+
+ if (*p != '.') {
+ goto invalid;
+ }
+
+ ch = *(p + 1);
+
+ if (ch < '0' || ch > '9') {
+ goto invalid;
+ }
+
+ r->http_minor = ch - '0';
+
+ for (p += 2; p != r->header_end; p++) {
+
+ ch = *p;
+
+ if (ch < '0' || ch > '9') {
+ goto invalid;
+ }
+
+ r->http_minor = r->http_minor * 10 + ch - '0';
+ }
+
+ r->http_protocol.len = r->header_end - r->header_start;
+ r->http_protocol.data = r->header_start;
+ r->http_version = r->http_major * 1000 + r->http_minor;
+
+ return NGX_OK;
+
+invalid:
+
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid http version: \"%*s\"",
+ r->header_end - r->header_start, r->header_start);
+
+ return NGX_HTTP_PARSE_INVALID_HEADER;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_construct_request_line(ngx_http_request_t *r)
+{
+ u_char *p;
+
+ if (r->method_name.len == 0
+ || r->unparsed_uri.len == 0
+ || r->http_protocol.len == 0)
+ {
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return NGX_ERROR;
+ }
+
+ r->request_line.len = r->method_name.len + 1
+ + r->unparsed_uri.len + 1
+ + r->http_protocol.len;
+
+ p = ngx_pnalloc(r->pool, r->request_line.len + 1);
+ if (p == NULL) {
+ ngx_http_spdy_close_stream(r->spdy_stream,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_ERROR;
+ }
+
+ r->request_line.data = p;
+
+ p = ngx_cpymem(p, r->method_name.data, r->method_name.len);
+
+ *p++ = ' ';
+
+ p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);
+
+ *p++ = ' ';
+
+ ngx_memcpy(p, r->http_protocol.data, r->http_protocol.len + 1);
+
+ /* some modules expect the space character after method name */
+ r->method_name.data = r->request_line.data;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_spdy_run_request(ngx_http_request_t *r)
+{
+ ngx_uint_t i;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *h;
+ ngx_http_header_t *hh;
+ ngx_http_core_main_conf_t *cmcf;
+
+ if (ngx_http_spdy_construct_request_line(r) != NGX_OK) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy http request line: \"%V\"", &r->request_line);
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ part = &r->headers_in.headers.part;
+ h = part->elts;
+
+ for (i = 0 ;; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ h = part->elts;
+ i = 0;
+ }
+
+ hh = ngx_hash_find(&cmcf->headers_in_hash, h[i].hash,
+ h[i].lowcase_key, h[i].key.len);
+
+ if (hh && hh->handler(r, &h[i], hh->offset) != NGX_OK) {
+ return;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy http header: \"%V: %V\"", &h[i].key, &h[i].value);
+ }
+
+ r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
+
+ if (ngx_http_process_request_header(r) != NGX_OK) {
+ return;
+ }
+
+ if (r->headers_in.content_length_n > 0 && r->spdy_stream->in_closed) {
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client prematurely closed stream");
+
+ r->spdy_stream->skip_data = NGX_SPDY_DATA_ERROR;
+
+ ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
+ return;
+ }
+
+ ngx_http_process_request(r);
+}
+
+
+static ngx_int_t
+ngx_http_spdy_init_request_body(ngx_http_request_t *r)
+{
+ ngx_buf_t *buf;
+ ngx_temp_file_t *tf;
+ ngx_http_request_body_t *rb;
+ ngx_http_core_loc_conf_t *clcf;
+
+ rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
+ if (rb == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->request_body = rb;
+
+ if (r->spdy_stream->in_closed) {
+ return NGX_OK;
+ }
+
+ rb->rest = r->headers_in.content_length_n;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (r->request_body_in_file_only
+ || rb->rest > (off_t) clcf->client_body_buffer_size
+ || rb->rest < 0)
+ {
+ tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
+ if (tf == NULL) {
+ return NGX_ERROR;
+ }
+
+ tf->file.fd = NGX_INVALID_FILE;
+ tf->file.log = r->connection->log;
+ tf->path = clcf->client_body_temp_path;
+ tf->pool = r->pool;
+ tf->warn = "a client request body is buffered to a temporary file";
+ tf->log_level = r->request_body_file_log_level;
+ tf->persistent = r->request_body_in_persistent_file;
+ tf->clean = r->request_body_in_clean_file;
+
+ if (r->request_body_file_group_access) {
+ tf->access = 0660;
+ }
+
+ rb->temp_file = tf;
+
+ if (r->spdy_stream->in_closed
+ && ngx_create_temp_file(&tf->file, tf->path, tf->pool,
+ tf->persistent, tf->clean, tf->access)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ buf = ngx_calloc_buf(r->pool);
+ if (buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ } else {
+
+ if (rb->rest == 0) {
+ return NGX_OK;
+ }
+
+ buf = ngx_create_temp_buf(r->pool, (size_t) rb->rest);
+ if (buf == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ rb->buf = buf;
+
+ rb->bufs = ngx_alloc_chain_link(r->pool);
+ if (rb->bufs == NULL) {
+ return NGX_ERROR;
+ }
+
+ rb->bufs->buf = buf;
+ rb->bufs->next = NULL;
+
+ rb->rest = 0;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_spdy_read_request_body(ngx_http_request_t *r,
+ ngx_http_client_body_handler_pt post_handler)
+{
+ ngx_http_spdy_stream_t *stream;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy read request body");
+
+ stream = r->spdy_stream;
+
+ switch (stream->skip_data) {
+
+ case NGX_SPDY_DATA_DISCARD:
+ post_handler(r);
+ return NGX_OK;
+
+ case NGX_SPDY_DATA_ERROR:
+ if (r->headers_in.content_length_n == -1) {
+ return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
+ } else {
+ return NGX_HTTP_BAD_REQUEST;
+ }
+
+ case NGX_SPDY_DATA_INTERNAL_ERROR:
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (!r->request_body && ngx_http_spdy_init_request_body(r) != NGX_OK) {
+ stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR;
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if (stream->in_closed) {
+ post_handler(r);
+ return NGX_OK;
+ }
+
+ r->request_body->post_handler = post_handler;
+
+ r->read_event_handler = ngx_http_test_reading;
+ r->write_event_handler = ngx_http_request_empty_handler;
+
+ return NGX_AGAIN;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_terminate_stream(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_stream_t *stream, ngx_uint_t status)
+{
+ ngx_event_t *rev;
+ ngx_connection_t *fc;
+
+ if (ngx_http_spdy_send_rst_stream(sc, stream->id, status,
+ NGX_SPDY_HIGHEST_PRIORITY)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+
+ stream->out_closed = 1;
+
+ fc = stream->request->connection;
+ fc->error = 1;
+
+ rev = fc->read;
+ rev->handler(rev);
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_spdy_close_stream_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *fc;
+ ngx_http_request_t *r;
+
+ fc = ev->data;
+ r = fc->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy close stream handler");
+
+ ngx_http_spdy_close_stream(r->spdy_stream, 0);
+}
+
+
+void
+ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc)
+{
+ ngx_event_t *ev;
+ ngx_connection_t *fc;
+ ngx_http_spdy_stream_t **index, *s;
+ ngx_http_spdy_srv_conf_t *sscf;
+ ngx_http_spdy_connection_t *sc;
+
+ sc = stream->connection;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy close stream %ui, queued %ui, processing %ui",
+ stream->id, stream->queued, sc->processing);
+
+ fc = stream->request->connection;
+
+ if (stream->queued) {
+ fc->write->handler = ngx_http_spdy_close_stream_handler;
+ return;
+ }
+
+ if (!stream->out_closed) {
+ if (ngx_http_spdy_send_rst_stream(sc, stream->id,
+ NGX_SPDY_INTERNAL_ERROR,
+ stream->priority)
+ != NGX_OK)
+ {
+ sc->connection->error = 1;
+ }
+ }
+
+ if (sc->stream == stream) {
+ sc->stream = NULL;
+ }
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ index = sc->streams_index + ngx_http_spdy_stream_index(sscf, stream->id);
+
+ for ( ;; ) {
+ s = *index;
+
+ if (s == NULL) {
+ break;
+ }
+
+ if (s == stream) {
+ *index = s->index;
+ break;
+ }
+
+ index = &s->index;
+ }
+
+ ngx_http_free_request(stream->request, rc);
+
+ ev = fc->read;
+
+ if (ev->active || ev->disabled) {
+ ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,
+ "fake read event was activated");
+ }
+
+ if (ev->timer_set) {
+ ngx_del_timer(ev);
+ }
+
+ if (ev->prev) {
+ ngx_delete_posted_event(ev);
+ }
+
+ ev = fc->write;
+
+ if (ev->active || ev->disabled) {
+ ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0,
+ "fake write event was activated");
+ }
+
+ if (ev->timer_set) {
+ ngx_del_timer(ev);
+ }
+
+ if (ev->prev) {
+ ngx_delete_posted_event(ev);
+ }
+
+ fc->data = sc->free_fake_connections;
+ sc->free_fake_connections = fc;
+
+ sc->processing--;
+
+ if (sc->processing || sc->blocked) {
+ return;
+ }
+
+ ev = sc->connection->read;
+
+ ev->handler = ngx_http_spdy_handle_connection_handler;
+ ngx_post_event(ev, &ngx_posted_events);
+}
+
+
+static void
+ngx_http_spdy_handle_connection_handler(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+
+ rev->handler = ngx_http_spdy_read_handler;
+
+ if (rev->ready) {
+ ngx_http_spdy_read_handler(rev);
+ return;
+ }
+
+ c = rev->data;
+
+ ngx_http_spdy_handle_connection(c->data);
+}
+
+
+static void
+ngx_http_spdy_keepalive_handler(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_http_spdy_srv_conf_t *sscf;
+ ngx_http_spdy_connection_t *sc;
+
+ c = rev->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy keepalive handler");
+
+ if (rev->timedout || c->close) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+#if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ if (rev->pending_eof) {
+ c->log->handler = NULL;
+ ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,
+ "kevent() reported that client %V closed "
+ "keepalive connection", &c->addr_text);
+#if (NGX_HTTP_SSL)
+ if (c->ssl) {
+ c->ssl->no_send_shutdown = 1;
+ }
+#endif
+ ngx_http_close_connection(c);
+ return;
+ }
+ }
+
+#endif
+
+ c->destroyed = 0;
+ c->idle = 0;
+ ngx_reusable_connection(c, 0);
+
+ sc = c->data;
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log);
+ if (sc->pool == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ sc->streams_index = ngx_pcalloc(sc->pool,
+ ngx_http_spdy_streams_index_size(sscf)
+ * sizeof(ngx_http_spdy_stream_t *));
+ if (sc->streams_index == NULL) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->write->handler = ngx_http_spdy_write_handler;
+
+ rev->handler = ngx_http_spdy_read_handler;
+ ngx_http_spdy_read_handler(rev);
+}
+
+
+static void
+ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc,
+ ngx_int_t rc)
+{
+ ngx_uint_t i, size;
+ ngx_event_t *ev;
+ ngx_connection_t *c, *fc;
+ ngx_http_request_t *r;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ c = sc->connection;
+
+ if (!sc->processing) {
+ ngx_http_close_connection(c);
+ return;
+ }
+
+ c->error = 1;
+ c->read->handler = ngx_http_empty_handler;
+ c->write->handler = ngx_http_empty_handler;
+
+ sc->last_out = NULL;
+
+ sc->blocked = 1;
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ size = ngx_http_spdy_streams_index_size(sscf);
+
+ for (i = 0; i < size; i++) {
+ stream = sc->streams_index[i];
+
+ while (stream) {
+ stream->handled = 0;
+
+ r = stream->request;
+ fc = r->connection;
+
+ fc->error = 1;
+
+ if (stream->queued) {
+ stream->queued = 0;
+
+ ev = fc->write;
+ ev->delayed = 0;
+
+ } else {
+ ev = fc->read;
+ }
+
+ stream = stream->index;
+
+ ev->eof = 1;
+ ev->handler(ev);
+ }
+ }
+
+ sc->blocked = 0;
+
+ if (sc->processing) {
+ return;
+ }
+
+ ngx_http_close_connection(c);
+}
+
+
+static ngx_int_t
+ngx_http_spdy_adjust_windows(ngx_http_spdy_connection_t *sc, ssize_t delta)
+{
+ ngx_uint_t i, size;
+ ngx_event_t *wev;
+ ngx_http_spdy_stream_t *stream, *sn;
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx,
+ ngx_http_spdy_module);
+
+ size = ngx_http_spdy_streams_index_size(sscf);
+
+ for (i = 0; i < size; i++) {
+
+ for (stream = sc->streams_index[i]; stream; stream = sn) {
+ sn = stream->index;
+
+ if (delta > 0
+ && stream->send_window
+ > (ssize_t) (NGX_SPDY_MAX_WINDOW - delta))
+ {
+ if (ngx_http_spdy_terminate_stream(sc, stream,
+ NGX_SPDY_FLOW_CONTROL_ERROR)
+ == NGX_ERROR)
+ {
+ return NGX_ERROR;
+ }
+
+ continue;
+ }
+
+ stream->send_window += delta;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy:%ui adjust window:%z",
+ stream->id, stream->send_window);
+
+ if (stream->send_window > 0 && stream->exhausted) {
+ stream->exhausted = 0;
+
+ wev = stream->request->connection->write;
+
+ if (!wev->timer_set) {
+ wev->delayed = 0;
+ wev->handler(wev);
+ }
+ }
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_spdy_pool_cleanup(void *data)
+{
+ ngx_http_spdy_connection_t *sc = data;
+
+ if (sc->pool) {
+ ngx_destroy_pool(sc->pool);
+ }
+}
+
+
+static void *
+ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size)
+{
+ ngx_http_spdy_connection_t *sc = opaque;
+
+ return ngx_palloc(sc->connection->pool, items * size);
+}
+
+
+static void
+ngx_http_spdy_zfree(void *opaque, void *address)
+{
+#if 0
+ ngx_http_spdy_connection_t *sc = opaque;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy zfree: %p", address);
+#endif
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy.h
new file mode 100644
index 00000000000..df24495a14e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy.h
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#ifndef _NGX_HTTP_SPDY_H_INCLUDED_
+#define _NGX_HTTP_SPDY_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+#include <zlib.h>
+
+
+#define NGX_SPDY_VERSION 3
+
+#define NGX_SPDY_NPN_ADVERTISE "\x08spdy/3.1"
+#define NGX_SPDY_NPN_NEGOTIATED "spdy/3.1"
+
+#define NGX_SPDY_STATE_BUFFER_SIZE 16
+
+#define NGX_SPDY_CTL_BIT 1
+
+#define NGX_SPDY_SYN_STREAM 1
+#define NGX_SPDY_SYN_REPLY 2
+#define NGX_SPDY_RST_STREAM 3
+#define NGX_SPDY_SETTINGS 4
+#define NGX_SPDY_PING 6
+#define NGX_SPDY_GOAWAY 7
+#define NGX_SPDY_HEADERS 8
+#define NGX_SPDY_WINDOW_UPDATE 9
+
+#define NGX_SPDY_FRAME_HEADER_SIZE 8
+
+#define NGX_SPDY_SID_SIZE 4
+#define NGX_SPDY_DELTA_SIZE 4
+
+#define NGX_SPDY_SYN_STREAM_SIZE 10
+#define NGX_SPDY_SYN_REPLY_SIZE 4
+#define NGX_SPDY_RST_STREAM_SIZE 8
+#define NGX_SPDY_PING_SIZE 4
+#define NGX_SPDY_GOAWAY_SIZE 8
+#define NGX_SPDY_WINDOW_UPDATE_SIZE 8
+#define NGX_SPDY_NV_NUM_SIZE 4
+#define NGX_SPDY_NV_NLEN_SIZE 4
+#define NGX_SPDY_NV_VLEN_SIZE 4
+#define NGX_SPDY_SETTINGS_NUM_SIZE 4
+#define NGX_SPDY_SETTINGS_FID_SIZE 4
+#define NGX_SPDY_SETTINGS_VAL_SIZE 4
+
+#define NGX_SPDY_SETTINGS_PAIR_SIZE \
+ (NGX_SPDY_SETTINGS_FID_SIZE + NGX_SPDY_SETTINGS_VAL_SIZE)
+
+#define NGX_SPDY_HIGHEST_PRIORITY 0
+#define NGX_SPDY_LOWEST_PRIORITY 7
+
+#define NGX_SPDY_FLAG_FIN 0x01
+#define NGX_SPDY_FLAG_UNIDIRECTIONAL 0x02
+#define NGX_SPDY_FLAG_CLEAR_SETTINGS 0x01
+
+#define NGX_SPDY_MAX_FRAME_SIZE ((1 << 24) - 1)
+
+#define NGX_SPDY_DATA_DISCARD 1
+#define NGX_SPDY_DATA_ERROR 2
+#define NGX_SPDY_DATA_INTERNAL_ERROR 3
+
+
+typedef struct ngx_http_spdy_connection_s ngx_http_spdy_connection_t;
+typedef struct ngx_http_spdy_out_frame_s ngx_http_spdy_out_frame_t;
+
+
+typedef u_char *(*ngx_http_spdy_handler_pt) (ngx_http_spdy_connection_t *sc,
+ u_char *pos, u_char *end);
+
+struct ngx_http_spdy_connection_s {
+ ngx_connection_t *connection;
+ ngx_http_connection_t *http_connection;
+
+ ngx_uint_t processing;
+
+ size_t send_window;
+ size_t recv_window;
+ size_t init_window;
+
+ ngx_queue_t waiting;
+
+ u_char buffer[NGX_SPDY_STATE_BUFFER_SIZE];
+ size_t buffer_used;
+ ngx_http_spdy_handler_pt handler;
+
+ z_stream zstream_in;
+ z_stream zstream_out;
+
+ ngx_pool_t *pool;
+
+ ngx_http_spdy_out_frame_t *free_ctl_frames;
+ ngx_connection_t *free_fake_connections;
+
+ ngx_http_spdy_stream_t **streams_index;
+
+ ngx_http_spdy_out_frame_t *last_out;
+
+ ngx_queue_t posted;
+
+ ngx_http_spdy_stream_t *stream;
+
+ ngx_uint_t entries;
+ size_t length;
+ u_char flags;
+
+ ngx_uint_t last_sid;
+
+ unsigned blocked:1;
+ unsigned incomplete:1;
+};
+
+
+struct ngx_http_spdy_stream_s {
+ ngx_uint_t id;
+ ngx_http_request_t *request;
+ ngx_http_spdy_connection_t *connection;
+ ngx_http_spdy_stream_t *index;
+
+ ngx_uint_t header_buffers;
+ ngx_uint_t queued;
+
+ /*
+ * A change to SETTINGS_INITIAL_WINDOW_SIZE could cause the
+ * send_window to become negative, hence it's signed.
+ */
+ ssize_t send_window;
+ size_t recv_window;
+
+ ngx_http_spdy_out_frame_t *free_frames;
+ ngx_chain_t *free_data_headers;
+ ngx_chain_t *free_bufs;
+
+ ngx_queue_t queue;
+
+ unsigned priority:3;
+ unsigned handled:1;
+ unsigned blocked:1;
+ unsigned exhausted:1;
+ unsigned in_closed:1;
+ unsigned out_closed:1;
+ unsigned skip_data:2;
+};
+
+
+struct ngx_http_spdy_out_frame_s {
+ ngx_http_spdy_out_frame_t *next;
+ ngx_chain_t *first;
+ ngx_chain_t *last;
+ ngx_int_t (*handler)(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame);
+
+ ngx_http_spdy_stream_t *stream;
+ size_t length;
+
+ ngx_uint_t priority;
+ unsigned blocked:1;
+ unsigned fin:1;
+};
+
+
+static ngx_inline void
+ngx_http_spdy_queue_frame(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_http_spdy_out_frame_t **out;
+
+ for (out = &sc->last_out; *out; out = &(*out)->next)
+ {
+ /*
+ * NB: higher values represent lower priorities.
+ */
+ if (frame->priority >= (*out)->priority) {
+ break;
+ }
+ }
+
+ frame->next = *out;
+ *out = frame;
+}
+
+
+static ngx_inline void
+ngx_http_spdy_queue_blocked_frame(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_http_spdy_out_frame_t **out;
+
+ for (out = &sc->last_out; *out; out = &(*out)->next)
+ {
+ if ((*out)->blocked) {
+ break;
+ }
+ }
+
+ frame->next = *out;
+ *out = frame;
+}
+
+
+void ngx_http_spdy_init(ngx_event_t *rev);
+void ngx_http_spdy_request_headers_init(void);
+
+ngx_int_t ngx_http_spdy_read_request_body(ngx_http_request_t *r,
+ ngx_http_client_body_handler_pt post_handler);
+
+void ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc);
+
+ngx_int_t ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc);
+
+
+#define ngx_spdy_frame_aligned_write_uint16(p, s) \
+ (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t))
+
+#define ngx_spdy_frame_aligned_write_uint32(p, s) \
+ (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t))
+
+#if (NGX_HAVE_NONALIGNED)
+
+#define ngx_spdy_frame_write_uint16 ngx_spdy_frame_aligned_write_uint16
+#define ngx_spdy_frame_write_uint32 ngx_spdy_frame_aligned_write_uint32
+
+#else
+
+#define ngx_spdy_frame_write_uint16(p, s) \
+ ((p)[0] = (u_char) ((s) >> 8), \
+ (p)[1] = (u_char) (s), \
+ (p) + sizeof(uint16_t))
+
+#define ngx_spdy_frame_write_uint32(p, s) \
+ ((p)[0] = (u_char) ((s) >> 24), \
+ (p)[1] = (u_char) ((s) >> 16), \
+ (p)[2] = (u_char) ((s) >> 8), \
+ (p)[3] = (u_char) (s), \
+ (p) + sizeof(uint32_t))
+
+#endif
+
+
+#define ngx_spdy_ctl_frame_head(t) \
+ ((uint32_t) NGX_SPDY_CTL_BIT << 31 | NGX_SPDY_VERSION << 16 | (t))
+
+#define ngx_spdy_frame_write_head(p, t) \
+ ngx_spdy_frame_aligned_write_uint32(p, ngx_spdy_ctl_frame_head(t))
+
+#define ngx_spdy_frame_write_flags_and_len(p, f, l) \
+ ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (l))
+#define ngx_spdy_frame_write_flags_and_id(p, f, i) \
+ ngx_spdy_frame_aligned_write_uint32(p, (f) << 24 | (i))
+
+#define ngx_spdy_frame_write_sid ngx_spdy_frame_aligned_write_uint32
+#define ngx_spdy_frame_write_window ngx_spdy_frame_aligned_write_uint32
+
+#endif /* _NGX_HTTP_SPDY_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy_filter_module.c
new file mode 100644
index 00000000000..82405d9a1ee
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy_filter_module.c
@@ -0,0 +1,1213 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <nginx.h>
+#include <ngx_http_spdy_module.h>
+
+#include <zlib.h>
+
+
+#define ngx_http_spdy_nv_nsize(h) (NGX_SPDY_NV_NLEN_SIZE + sizeof(h) - 1)
+#define ngx_http_spdy_nv_vsize(h) (NGX_SPDY_NV_VLEN_SIZE + sizeof(h) - 1)
+
+#define ngx_http_spdy_nv_write_num ngx_spdy_frame_write_uint32
+#define ngx_http_spdy_nv_write_nlen ngx_spdy_frame_write_uint32
+#define ngx_http_spdy_nv_write_vlen ngx_spdy_frame_write_uint32
+
+#define ngx_http_spdy_nv_write_name(p, h) \
+ ngx_cpymem(ngx_http_spdy_nv_write_nlen(p, sizeof(h) - 1), h, sizeof(h) - 1)
+
+#define ngx_http_spdy_nv_write_val(p, h) \
+ ngx_cpymem(ngx_http_spdy_nv_write_vlen(p, sizeof(h) - 1), h, sizeof(h) - 1)
+
+
+static ngx_chain_t *ngx_http_spdy_send_chain(ngx_connection_t *fc,
+ ngx_chain_t *in, off_t limit);
+
+static ngx_inline ngx_int_t ngx_http_spdy_filter_send(
+ ngx_connection_t *fc, ngx_http_spdy_stream_t *stream);
+static ngx_inline ngx_int_t ngx_http_spdy_flow_control(
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream);
+static void ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_stream_t *stream);
+
+static ngx_chain_t *ngx_http_spdy_filter_get_shadow(
+ ngx_http_spdy_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size);
+static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame(
+ ngx_http_spdy_stream_t *stream, size_t len, ngx_chain_t *first,
+ ngx_chain_t *last);
+
+static ngx_int_t ngx_http_spdy_syn_frame_handler(
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
+static ngx_int_t ngx_http_spdy_data_frame_handler(
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
+static ngx_inline void ngx_http_spdy_handle_frame(
+ ngx_http_spdy_stream_t *stream, ngx_http_spdy_out_frame_t *frame);
+static ngx_inline void ngx_http_spdy_handle_stream(
+ ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream);
+
+static void ngx_http_spdy_filter_cleanup(void *data);
+
+static ngx_int_t ngx_http_spdy_filter_init(ngx_conf_t *cf);
+
+
+static ngx_http_module_t ngx_http_spdy_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_spdy_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_spdy_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_spdy_filter_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
+
+
+static ngx_int_t
+ngx_http_spdy_header_filter(ngx_http_request_t *r)
+{
+ int rc;
+ size_t len;
+ u_char *p, *buf, *last;
+ ngx_buf_t *b;
+ ngx_str_t host;
+ ngx_uint_t i, j, count, port;
+ ngx_chain_t *cl;
+ ngx_list_part_t *part, *pt;
+ ngx_table_elt_t *header, *h;
+ ngx_connection_t *c;
+ ngx_http_cleanup_t *cln;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_core_srv_conf_t *cscf;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_spdy_out_frame_t *frame;
+ ngx_http_spdy_connection_t *sc;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+ u_char addr[NGX_SOCKADDR_STRLEN];
+
+ if (!r->spdy_stream) {
+ return ngx_http_next_header_filter(r);
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "spdy header filter");
+
+ if (r->header_sent) {
+ return NGX_OK;
+ }
+
+ r->header_sent = 1;
+
+ if (r != r->main) {
+ return NGX_OK;
+ }
+
+ c = r->connection;
+
+ if (r->method == NGX_HTTP_HEAD) {
+ r->header_only = 1;
+ }
+
+ switch (r->headers_out.status) {
+
+ case NGX_HTTP_OK:
+ case NGX_HTTP_PARTIAL_CONTENT:
+ break;
+
+ case NGX_HTTP_NOT_MODIFIED:
+ r->header_only = 1;
+ break;
+
+ case NGX_HTTP_NO_CONTENT:
+ r->header_only = 1;
+
+ ngx_str_null(&r->headers_out.content_type);
+
+ r->headers_out.content_length = NULL;
+ r->headers_out.content_length_n = -1;
+
+ /* fall through */
+
+ default:
+ r->headers_out.last_modified_time = -1;
+ r->headers_out.last_modified = NULL;
+ }
+
+ len = NGX_SPDY_NV_NUM_SIZE
+ + ngx_http_spdy_nv_nsize(":version")
+ + ngx_http_spdy_nv_vsize("HTTP/1.1")
+ + ngx_http_spdy_nv_nsize(":status")
+ + (r->headers_out.status_line.len
+ ? NGX_SPDY_NV_VLEN_SIZE + r->headers_out.status_line.len
+ : ngx_http_spdy_nv_vsize("418"));
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (r->headers_out.server == NULL) {
+ len += ngx_http_spdy_nv_nsize("server");
+ len += clcf->server_tokens ? ngx_http_spdy_nv_vsize(NGINX_VER)
+ : ngx_http_spdy_nv_vsize("nginx");
+ }
+
+ if (r->headers_out.date == NULL) {
+ len += ngx_http_spdy_nv_nsize("date")
+ + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT");
+ }
+
+ if (r->headers_out.content_type.len) {
+ len += ngx_http_spdy_nv_nsize("content-type")
+ + NGX_SPDY_NV_VLEN_SIZE + r->headers_out.content_type.len;
+
+ if (r->headers_out.content_type_len == r->headers_out.content_type.len
+ && r->headers_out.charset.len)
+ {
+ len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
+ }
+ }
+
+ if (r->headers_out.content_length == NULL
+ && r->headers_out.content_length_n >= 0)
+ {
+ len += ngx_http_spdy_nv_nsize("content-length")
+ + NGX_SPDY_NV_VLEN_SIZE + NGX_OFF_T_LEN;
+ }
+
+ if (r->headers_out.last_modified == NULL
+ && r->headers_out.last_modified_time != -1)
+ {
+ len += ngx_http_spdy_nv_nsize("last-modified")
+ + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT");
+ }
+
+ if (r->headers_out.location
+ && r->headers_out.location->value.len
+ && r->headers_out.location->value.data[0] == '/')
+ {
+ r->headers_out.location->hash = 0;
+
+ if (clcf->server_name_in_redirect) {
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+ host = cscf->server_name;
+
+ } else if (r->headers_in.server.len) {
+ host = r->headers_in.server;
+
+ } else {
+ host.len = NGX_SOCKADDR_STRLEN;
+ host.data = addr;
+
+ if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+ port = ntohs(sin6->sin6_port);
+ break;
+#endif
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ port = 0;
+ break;
+#endif
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) c->local_sockaddr;
+ port = ntohs(sin->sin_port);
+ break;
+ }
+
+ len += ngx_http_spdy_nv_nsize("location")
+ + ngx_http_spdy_nv_vsize("https://")
+ + host.len
+ + r->headers_out.location->value.len;
+
+ if (clcf->port_in_redirect) {
+
+#if (NGX_HTTP_SSL)
+ if (c->ssl)
+ port = (port == 443) ? 0 : port;
+ else
+#endif
+ port = (port == 80) ? 0 : port;
+
+ } else {
+ port = 0;
+ }
+
+ if (port) {
+ len += sizeof(":65535") - 1;
+ }
+
+ } else {
+ ngx_str_null(&host);
+ port = 0;
+ }
+
+ part = &r->headers_out.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].hash == 0) {
+ continue;
+ }
+
+ len += NGX_SPDY_NV_NLEN_SIZE + header[i].key.len
+ + NGX_SPDY_NV_VLEN_SIZE + header[i].value.len;
+ }
+
+ buf = ngx_alloc(len, r->pool->log);
+ if (buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ last = buf + NGX_SPDY_NV_NUM_SIZE;
+
+ last = ngx_http_spdy_nv_write_name(last, ":version");
+ last = ngx_http_spdy_nv_write_val(last, "HTTP/1.1");
+
+ last = ngx_http_spdy_nv_write_name(last, ":status");
+
+ if (r->headers_out.status_line.len) {
+ last = ngx_http_spdy_nv_write_vlen(last,
+ r->headers_out.status_line.len);
+ last = ngx_cpymem(last, r->headers_out.status_line.data,
+ r->headers_out.status_line.len);
+ } else {
+ last = ngx_http_spdy_nv_write_vlen(last, 3);
+ last = ngx_sprintf(last, "%03ui", r->headers_out.status);
+ }
+
+ count = 2;
+
+ if (r->headers_out.server == NULL) {
+ last = ngx_http_spdy_nv_write_name(last, "server");
+ last = clcf->server_tokens
+ ? ngx_http_spdy_nv_write_val(last, NGINX_VER)
+ : ngx_http_spdy_nv_write_val(last, "nginx");
+
+ count++;
+ }
+
+ if (r->headers_out.date == NULL) {
+ last = ngx_http_spdy_nv_write_name(last, "date");
+
+ last = ngx_http_spdy_nv_write_vlen(last, ngx_cached_http_time.len);
+
+ last = ngx_cpymem(last, ngx_cached_http_time.data,
+ ngx_cached_http_time.len);
+
+ count++;
+ }
+
+ if (r->headers_out.content_type.len) {
+
+ last = ngx_http_spdy_nv_write_name(last, "content-type");
+
+ p = last + NGX_SPDY_NV_VLEN_SIZE;
+
+ last = ngx_cpymem(p, r->headers_out.content_type.data,
+ r->headers_out.content_type.len);
+
+ if (r->headers_out.content_type_len == r->headers_out.content_type.len
+ && r->headers_out.charset.len)
+ {
+ last = ngx_cpymem(last, "; charset=", sizeof("; charset=") - 1);
+
+ last = ngx_cpymem(last, r->headers_out.charset.data,
+ r->headers_out.charset.len);
+
+ /* update r->headers_out.content_type for possible logging */
+
+ r->headers_out.content_type.len = last - p;
+ r->headers_out.content_type.data = p;
+ }
+
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
+ r->headers_out.content_type.len);
+
+ count++;
+ }
+
+ if (r->headers_out.content_length == NULL
+ && r->headers_out.content_length_n >= 0)
+ {
+ last = ngx_http_spdy_nv_write_name(last, "content-length");
+
+ p = last + NGX_SPDY_NV_VLEN_SIZE;
+
+ last = ngx_sprintf(p, "%O", r->headers_out.content_length_n);
+
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
+ last - p);
+
+ count++;
+ }
+
+ if (r->headers_out.last_modified == NULL
+ && r->headers_out.last_modified_time != -1)
+ {
+ last = ngx_http_spdy_nv_write_name(last, "last-modified");
+
+ p = last + NGX_SPDY_NV_VLEN_SIZE;
+
+ last = ngx_http_time(p, r->headers_out.last_modified_time);
+
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
+ last - p);
+
+ count++;
+ }
+
+ if (host.data) {
+
+ last = ngx_http_spdy_nv_write_name(last, "location");
+
+ p = last + NGX_SPDY_NV_VLEN_SIZE;
+
+ last = ngx_cpymem(p, "http", sizeof("http") - 1);
+
+#if (NGX_HTTP_SSL)
+ if (c->ssl) {
+ *last++ ='s';
+ }
+#endif
+
+ *last++ = ':'; *last++ = '/'; *last++ = '/';
+
+ last = ngx_cpymem(last, host.data, host.len);
+
+ if (port) {
+ last = ngx_sprintf(last, ":%ui", port);
+ }
+
+ last = ngx_cpymem(last, r->headers_out.location->value.data,
+ r->headers_out.location->value.len);
+
+ /* update r->headers_out.location->value for possible logging */
+
+ r->headers_out.location->value.len = last - p;
+ r->headers_out.location->value.data = p;
+ ngx_str_set(&r->headers_out.location->key, "location");
+
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
+ r->headers_out.location->value.len);
+
+ count++;
+ }
+
+ part = &r->headers_out.headers.part;
+ header = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].hash == 0 || header[i].hash == 2) {
+ continue;
+ }
+
+ last = ngx_http_spdy_nv_write_nlen(last, header[i].key.len);
+
+ ngx_strlow(last, header[i].key.data, header[i].key.len);
+ last += header[i].key.len;
+
+ p = last + NGX_SPDY_NV_VLEN_SIZE;
+
+ last = ngx_cpymem(p, header[i].value.data, header[i].value.len);
+
+ pt = part;
+ h = header;
+
+ for (j = i + 1; /* void */; j++) {
+
+ if (j >= pt->nelts) {
+ if (pt->next == NULL) {
+ break;
+ }
+
+ pt = pt->next;
+ h = pt->elts;
+ j = 0;
+ }
+
+ if (h[j].hash == 0 || h[j].hash == 2
+ || h[j].key.len != header[i].key.len
+ || ngx_strncasecmp(header[i].key.data, h[j].key.data,
+ header[i].key.len))
+ {
+ continue;
+ }
+
+ *last++ = '\0';
+
+ last = ngx_cpymem(last, h[j].value.data, h[j].value.len);
+
+ h[j].hash = 2;
+ }
+
+ (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
+ last - p);
+
+ count++;
+ }
+
+ (void) ngx_http_spdy_nv_write_num(buf, count);
+
+ stream = r->spdy_stream;
+ sc = stream->connection;
+
+ len = last - buf;
+
+ b = ngx_create_temp_buf(r->pool, NGX_SPDY_FRAME_HEADER_SIZE
+ + NGX_SPDY_SYN_REPLY_SIZE
+ + deflateBound(&sc->zstream_out, len));
+ if (b == NULL) {
+ ngx_free(buf);
+ return NGX_ERROR;
+ }
+
+ b->last += NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_REPLY_SIZE;
+
+ sc->zstream_out.next_in = buf;
+ sc->zstream_out.avail_in = len;
+ sc->zstream_out.next_out = b->last;
+ sc->zstream_out.avail_out = b->end - b->last;
+
+ rc = deflate(&sc->zstream_out, Z_SYNC_FLUSH);
+
+ ngx_free(buf);
+
+ if (rc != Z_OK) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflate() failed: %d", rc);
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "spdy deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
+ sc->zstream_out.next_in, sc->zstream_out.next_out,
+ sc->zstream_out.avail_in, sc->zstream_out.avail_out,
+ rc);
+
+ b->last = sc->zstream_out.next_out;
+
+ p = b->pos;
+ p = ngx_spdy_frame_write_head(p, NGX_SPDY_SYN_REPLY);
+
+ len = b->last - b->pos;
+
+ r->header_size = len;
+
+ len -= NGX_SPDY_FRAME_HEADER_SIZE;
+
+ if (r->header_only) {
+ b->last_buf = 1;
+ p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN, len);
+
+ } else {
+ p = ngx_spdy_frame_write_flags_and_len(p, 0, len);
+ }
+
+ (void) ngx_spdy_frame_write_sid(p, stream->id);
+
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = b;
+ cl->next = NULL;
+
+ frame = ngx_palloc(r->pool, sizeof(ngx_http_spdy_out_frame_t));
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ frame->first = cl;
+ frame->last = cl;
+ frame->handler = ngx_http_spdy_syn_frame_handler;
+ frame->stream = stream;
+ frame->length = len;
+ frame->priority = stream->priority;
+ frame->blocked = 1;
+ frame->fin = r->header_only;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
+ "spdy:%ui create SYN_REPLY frame %p: len:%uz",
+ stream->id, frame, frame->length);
+
+ ngx_http_spdy_queue_blocked_frame(sc, frame);
+
+ cln = ngx_http_cleanup_add(r, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->handler = ngx_http_spdy_filter_cleanup;
+ cln->data = stream;
+
+ stream->queued = 1;
+
+ c->send_chain = ngx_http_spdy_send_chain;
+ c->need_last_buf = 1;
+
+ return ngx_http_spdy_filter_send(c, stream);
+}
+
+
+static ngx_chain_t *
+ngx_http_spdy_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
+{
+ off_t size, offset;
+ size_t rest, frame_size;
+ ngx_chain_t *cl, *out, **ln;
+ ngx_http_request_t *r;
+ ngx_http_spdy_stream_t *stream;
+ ngx_http_spdy_loc_conf_t *slcf;
+ ngx_http_spdy_out_frame_t *frame;
+ ngx_http_spdy_connection_t *sc;
+
+ r = fc->data;
+ stream = r->spdy_stream;
+
+#if (NGX_SUPPRESS_WARN)
+ size = 0;
+#endif
+
+ while (in) {
+ size = ngx_buf_size(in->buf);
+
+ if (size || in->buf->last_buf) {
+ break;
+ }
+
+ in = in->next;
+ }
+
+ if (in == NULL) {
+
+ if (stream->queued) {
+ fc->write->delayed = 1;
+ } else {
+ fc->buffered &= ~NGX_SPDY_BUFFERED;
+ }
+
+ return NULL;
+ }
+
+ sc = stream->connection;
+
+ if (size && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) {
+ fc->write->delayed = 1;
+ return in;
+ }
+
+ if (limit == 0 || limit > (off_t) sc->send_window) {
+ limit = sc->send_window;
+ }
+
+ if (limit > stream->send_window) {
+ limit = (stream->send_window > 0) ? stream->send_window : 0;
+ }
+
+ if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ cl->buf = in->buf;
+ in->buf = cl->buf->shadow;
+
+ offset = ngx_buf_in_memory(in->buf)
+ ? (cl->buf->pos - in->buf->pos)
+ : (cl->buf->file_pos - in->buf->file_pos);
+
+ cl->next = stream->free_bufs;
+ stream->free_bufs = cl;
+
+ } else {
+ offset = 0;
+ }
+
+#if (NGX_SUPPRESS_WARN)
+ cl = NULL;
+#endif
+
+ slcf = ngx_http_get_module_loc_conf(r, ngx_http_spdy_module);
+
+ frame_size = (limit <= (off_t) slcf->chunk_size) ? (size_t) limit
+ : slcf->chunk_size;
+
+ for ( ;; ) {
+ ln = &out;
+ rest = frame_size;
+
+ while ((off_t) rest >= size) {
+
+ if (offset) {
+ cl = ngx_http_spdy_filter_get_shadow(stream, in->buf,
+ offset, size);
+ if (cl == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ offset = 0;
+
+ } else {
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ cl->buf = in->buf;
+ }
+
+ *ln = cl;
+ ln = &cl->next;
+
+ rest -= (size_t) size;
+ in = in->next;
+
+ if (in == NULL) {
+ frame_size -= rest;
+ rest = 0;
+ break;
+ }
+
+ size = ngx_buf_size(in->buf);
+ }
+
+ if (rest) {
+ cl = ngx_http_spdy_filter_get_shadow(stream, in->buf,
+ offset, rest);
+ if (cl == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ cl->buf->flush = 0;
+ cl->buf->last_buf = 0;
+
+ *ln = cl;
+
+ offset += rest;
+ size -= rest;
+ }
+
+ frame = ngx_http_spdy_filter_get_data_frame(stream, frame_size,
+ out, cl);
+ if (frame == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_http_spdy_queue_frame(sc, frame);
+
+ sc->send_window -= frame_size;
+
+ stream->send_window -= frame_size;
+ stream->queued++;
+
+ if (in == NULL) {
+ break;
+ }
+
+ limit -= frame_size;
+
+ if (limit == 0) {
+ break;
+ }
+
+ if (limit < (off_t) slcf->chunk_size) {
+ frame_size = (size_t) limit;
+ }
+ }
+
+ if (offset) {
+ cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, offset, size);
+ if (cl == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ in->buf = cl->buf;
+ ngx_free_chain(r->pool, cl);
+ }
+
+ if (ngx_http_spdy_filter_send(fc, stream) == NGX_ERROR) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ if (in && ngx_http_spdy_flow_control(sc, stream) == NGX_DECLINED) {
+ fc->write->delayed = 1;
+ }
+
+ return in;
+}
+
+
+static ngx_chain_t *
+ngx_http_spdy_filter_get_shadow(ngx_http_spdy_stream_t *stream, ngx_buf_t *buf,
+ off_t offset, off_t size)
+{
+ ngx_buf_t *chunk;
+ ngx_chain_t *cl;
+
+ cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ chunk = cl->buf;
+
+ ngx_memcpy(chunk, buf, sizeof(ngx_buf_t));
+
+ chunk->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow;
+ chunk->shadow = buf;
+
+ if (ngx_buf_in_memory(chunk)) {
+ chunk->pos += offset;
+ chunk->last = chunk->pos + size;
+ }
+
+ if (chunk->in_file) {
+ chunk->file_pos += offset;
+ chunk->file_last = chunk->file_pos + size;
+ }
+
+ return cl;
+}
+
+
+static ngx_http_spdy_out_frame_t *
+ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream,
+ size_t len, ngx_chain_t *first, ngx_chain_t *last)
+{
+ u_char *p;
+ ngx_buf_t *buf;
+ ngx_uint_t flags;
+ ngx_chain_t *cl;
+ ngx_http_spdy_out_frame_t *frame;
+
+
+ frame = stream->free_frames;
+
+ if (frame) {
+ stream->free_frames = frame->next;
+
+ } else {
+ frame = ngx_palloc(stream->request->pool,
+ sizeof(ngx_http_spdy_out_frame_t));
+ if (frame == NULL) {
+ return NULL;
+ }
+ }
+
+ flags = last->buf->last_buf ? NGX_SPDY_FLAG_FIN : 0;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
+ "spdy:%ui create DATA frame %p: len:%uz flags:%ui",
+ stream->id, frame, len, flags);
+
+ cl = ngx_chain_get_free_buf(stream->request->pool,
+ &stream->free_data_headers);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ buf = cl->buf;
+
+ if (buf->start) {
+ p = buf->start;
+ buf->pos = p;
+
+ p += NGX_SPDY_SID_SIZE;
+
+ (void) ngx_spdy_frame_write_flags_and_len(p, flags, len);
+
+ } else {
+ p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ buf->pos = p;
+ buf->start = p;
+
+ p = ngx_spdy_frame_write_sid(p, stream->id);
+ p = ngx_spdy_frame_write_flags_and_len(p, flags, len);
+
+ buf->last = p;
+ buf->end = p;
+
+ buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame;
+ buf->memory = 1;
+ }
+
+ cl->next = first;
+ first = cl;
+
+ last->buf->flush = 1;
+
+ frame->first = first;
+ frame->last = last;
+ frame->handler = ngx_http_spdy_data_frame_handler;
+ frame->stream = stream;
+ frame->length = len;
+ frame->priority = stream->priority;
+ frame->blocked = 0;
+ frame->fin = last->buf->last_buf;
+
+ return frame;
+}
+
+
+static ngx_inline ngx_int_t
+ngx_http_spdy_filter_send(ngx_connection_t *fc, ngx_http_spdy_stream_t *stream)
+{
+ stream->blocked = 1;
+
+ if (ngx_http_spdy_send_output_queue(stream->connection) == NGX_ERROR) {
+ fc->error = 1;
+ return NGX_ERROR;
+ }
+
+ stream->blocked = 0;
+
+ if (stream->queued) {
+ fc->buffered |= NGX_SPDY_BUFFERED;
+ fc->write->delayed = 1;
+ return NGX_AGAIN;
+ }
+
+ fc->buffered &= ~NGX_SPDY_BUFFERED;
+
+ return NGX_OK;
+}
+
+
+static ngx_inline ngx_int_t
+ngx_http_spdy_flow_control(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_stream_t *stream)
+{
+ if (stream->send_window <= 0) {
+ stream->exhausted = 1;
+ return NGX_DECLINED;
+ }
+
+ if (sc->send_window == 0) {
+ ngx_http_spdy_waiting_queue(sc, stream);
+ return NGX_DECLINED;
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_spdy_waiting_queue(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_stream_t *stream)
+{
+ ngx_queue_t *q;
+ ngx_http_spdy_stream_t *s;
+
+ if (stream->handled) {
+ return;
+ }
+
+ stream->handled = 1;
+
+ for (q = ngx_queue_last(&sc->waiting);
+ q != ngx_queue_sentinel(&sc->waiting);
+ q = ngx_queue_prev(q))
+ {
+ s = ngx_queue_data(q, ngx_http_spdy_stream_t, queue);
+
+ /*
+ * NB: higher values represent lower priorities.
+ */
+ if (stream->priority >= s->priority) {
+ break;
+ }
+ }
+
+ ngx_queue_insert_after(q, &stream->queue);
+}
+
+
+static ngx_int_t
+ngx_http_spdy_syn_frame_handler(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_buf_t *buf;
+ ngx_http_spdy_stream_t *stream;
+
+ buf = frame->first->buf;
+
+ if (buf->pos != buf->last) {
+ return NGX_AGAIN;
+ }
+
+ stream = frame->stream;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy:%ui SYN_REPLY frame %p was sent", stream->id, frame);
+
+ ngx_free_chain(stream->request->pool, frame->first);
+
+ ngx_http_spdy_handle_frame(stream, frame);
+
+ ngx_http_spdy_handle_stream(sc, stream);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_buf_t *buf;
+ ngx_chain_t *cl, *ln;
+ ngx_http_spdy_stream_t *stream;
+
+ stream = frame->stream;
+
+ cl = frame->first;
+
+ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame) {
+
+ if (cl->buf->pos != cl->buf->last) {
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy:%ui DATA frame %p was sent partially",
+ stream->id, frame);
+
+ return NGX_AGAIN;
+ }
+
+ ln = cl->next;
+
+ cl->next = stream->free_data_headers;
+ stream->free_data_headers = cl;
+
+ if (cl == frame->last) {
+ goto done;
+ }
+
+ cl = ln;
+ }
+
+ for ( ;; ) {
+ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {
+ buf = cl->buf->shadow;
+
+ if (ngx_buf_in_memory(buf)) {
+ buf->pos = cl->buf->pos;
+ }
+
+ if (buf->in_file) {
+ buf->file_pos = cl->buf->file_pos;
+ }
+ }
+
+ if (ngx_buf_size(cl->buf) != 0) {
+
+ if (cl != frame->first) {
+ frame->first = cl;
+ ngx_http_spdy_handle_stream(sc, stream);
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy:%ui DATA frame %p was sent partially",
+ stream->id, frame);
+
+ return NGX_AGAIN;
+ }
+
+ ln = cl->next;
+
+ if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) {
+ cl->next = stream->free_bufs;
+ stream->free_bufs = cl;
+
+ } else {
+ ngx_free_chain(stream->request->pool, cl);
+ }
+
+ if (cl == frame->last) {
+ goto done;
+ }
+
+ cl = ln;
+ }
+
+done:
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
+ "spdy:%ui DATA frame %p was sent", stream->id, frame);
+
+ stream->request->header_size += NGX_SPDY_FRAME_HEADER_SIZE;
+
+ ngx_http_spdy_handle_frame(stream, frame);
+
+ ngx_http_spdy_handle_stream(sc, stream);
+
+ return NGX_OK;
+}
+
+
+static ngx_inline void
+ngx_http_spdy_handle_frame(ngx_http_spdy_stream_t *stream,
+ ngx_http_spdy_out_frame_t *frame)
+{
+ ngx_http_request_t *r;
+
+ r = stream->request;
+
+ r->connection->sent += NGX_SPDY_FRAME_HEADER_SIZE + frame->length;
+
+ if (frame->fin) {
+ stream->out_closed = 1;
+ }
+
+ frame->next = stream->free_frames;
+ stream->free_frames = frame;
+
+ stream->queued--;
+}
+
+
+static ngx_inline void
+ngx_http_spdy_handle_stream(ngx_http_spdy_connection_t *sc,
+ ngx_http_spdy_stream_t *stream)
+{
+ ngx_event_t *wev;
+
+ if (stream->handled || stream->blocked || stream->exhausted) {
+ return;
+ }
+
+ wev = stream->request->connection->write;
+
+ if (!wev->timer_set) {
+ wev->delayed = 0;
+
+ stream->handled = 1;
+ ngx_queue_insert_tail(&sc->posted, &stream->queue);
+ }
+}
+
+
+static void
+ngx_http_spdy_filter_cleanup(void *data)
+{
+ ngx_http_spdy_stream_t *stream = data;
+
+ size_t delta;
+ ngx_http_spdy_out_frame_t *frame, **fn;
+ ngx_http_spdy_connection_t *sc;
+
+ if (stream->handled) {
+ stream->handled = 0;
+ ngx_queue_remove(&stream->queue);
+ }
+
+ if (stream->queued == 0) {
+ return;
+ }
+
+ delta = 0;
+ sc = stream->connection;
+ fn = &sc->last_out;
+
+ for ( ;; ) {
+ frame = *fn;
+
+ if (frame == NULL) {
+ break;
+ }
+
+ if (frame->stream == stream && !frame->blocked) {
+ *fn = frame->next;
+
+ delta += frame->length;
+
+ if (--stream->queued == 0) {
+ break;
+ }
+
+ continue;
+ }
+
+ fn = &frame->next;
+ }
+
+ if (sc->send_window == 0 && delta && !ngx_queue_empty(&sc->waiting)) {
+ ngx_queue_add(&sc->posted, &sc->waiting);
+ ngx_queue_init(&sc->waiting);
+ }
+
+ sc->send_window += delta;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_next_header_filter = ngx_http_top_header_filter;
+ ngx_http_top_header_filter = ngx_http_spdy_header_filter;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy_module.c
new file mode 100644
index 00000000000..5178a36f239
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy_module.c
@@ -0,0 +1,408 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <ngx_http_spdy_module.h>
+
+
+static ngx_int_t ngx_http_spdy_add_variables(ngx_conf_t *cf);
+
+static ngx_int_t ngx_http_spdy_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_spdy_request_priority_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_spdy_module_init(ngx_cycle_t *cycle);
+
+static void *ngx_http_spdy_create_main_conf(ngx_conf_t *cf);
+static char *ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf);
+static void *ngx_http_spdy_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static void *ngx_http_spdy_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+static char *ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post,
+ void *data);
+static char *ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post,
+ void *data);
+static char *ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data);
+
+
+static ngx_conf_num_bounds_t ngx_http_spdy_headers_comp_bounds = {
+ ngx_conf_check_num_bounds, 0, 9
+};
+
+static ngx_conf_post_t ngx_http_spdy_recv_buffer_size_post =
+ { ngx_http_spdy_recv_buffer_size };
+static ngx_conf_post_t ngx_http_spdy_pool_size_post =
+ { ngx_http_spdy_pool_size };
+static ngx_conf_post_t ngx_http_spdy_streams_index_mask_post =
+ { ngx_http_spdy_streams_index_mask };
+static ngx_conf_post_t ngx_http_spdy_chunk_size_post =
+ { ngx_http_spdy_chunk_size };
+
+
+static ngx_command_t ngx_http_spdy_commands[] = {
+
+ { ngx_string("spdy_recv_buffer_size"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_MAIN_CONF_OFFSET,
+ offsetof(ngx_http_spdy_main_conf_t, recv_buffer_size),
+ &ngx_http_spdy_recv_buffer_size_post },
+
+ { ngx_string("spdy_pool_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_spdy_srv_conf_t, pool_size),
+ &ngx_http_spdy_pool_size_post },
+
+ { ngx_string("spdy_max_concurrent_streams"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_spdy_srv_conf_t, concurrent_streams),
+ NULL },
+
+ { ngx_string("spdy_streams_index_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_spdy_srv_conf_t, streams_index_mask),
+ &ngx_http_spdy_streams_index_mask_post },
+
+ { ngx_string("spdy_recv_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_spdy_srv_conf_t, recv_timeout),
+ NULL },
+
+ { ngx_string("spdy_keepalive_timeout"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_spdy_srv_conf_t, keepalive_timeout),
+ NULL },
+
+ { ngx_string("spdy_headers_comp"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ offsetof(ngx_http_spdy_srv_conf_t, headers_comp),
+ &ngx_http_spdy_headers_comp_bounds },
+
+ { ngx_string("spdy_chunk_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_spdy_loc_conf_t, chunk_size),
+ &ngx_http_spdy_chunk_size_post },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_spdy_module_ctx = {
+ ngx_http_spdy_add_variables, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ ngx_http_spdy_create_main_conf, /* create main configuration */
+ ngx_http_spdy_init_main_conf, /* init main configuration */
+
+ ngx_http_spdy_create_srv_conf, /* create server configuration */
+ ngx_http_spdy_merge_srv_conf, /* merge server configuration */
+
+ ngx_http_spdy_create_loc_conf, /* create location configuration */
+ ngx_http_spdy_merge_loc_conf /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_spdy_module = {
+ NGX_MODULE_V1,
+ &ngx_http_spdy_module_ctx, /* module context */
+ ngx_http_spdy_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ ngx_http_spdy_module_init, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_variable_t ngx_http_spdy_vars[] = {
+
+ { ngx_string("spdy"), NULL,
+ ngx_http_spdy_variable, 0, 0, 0 },
+
+ { ngx_string("spdy_request_priority"), NULL,
+ ngx_http_spdy_request_priority_variable, 0, 0, 0 },
+
+ { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
+
+
+static ngx_int_t
+ngx_http_spdy_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var, *v;
+
+ for (v = ngx_http_spdy_vars; v->name.len; v++) {
+ var = ngx_http_add_variable(cf, &v->name, v->flags);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = v->get_handler;
+ var->data = v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->spdy_stream) {
+ v->len = sizeof("3.1") - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "3.1";
+
+ return NGX_OK;
+ }
+
+ *v = ngx_http_variable_null_value;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_request_priority_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->spdy_stream) {
+ v->len = 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ v->data = ngx_pnalloc(r->pool, 1);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->data[0] = '0' + (u_char) r->spdy_stream->priority;
+
+ return NGX_OK;
+ }
+
+ *v = ngx_http_variable_null_value;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_spdy_module_init(ngx_cycle_t *cycle)
+{
+ ngx_http_spdy_request_headers_init();
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_http_spdy_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_http_spdy_main_conf_t *smcf;
+
+ smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_main_conf_t));
+ if (smcf == NULL) {
+ return NULL;
+ }
+
+ smcf->recv_buffer_size = NGX_CONF_UNSET_SIZE;
+
+ return smcf;
+}
+
+
+static char *
+ngx_http_spdy_init_main_conf(ngx_conf_t *cf, void *conf)
+{
+ ngx_http_spdy_main_conf_t *smcf = conf;
+
+ ngx_conf_init_size_value(smcf->recv_buffer_size, 256 * 1024);
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_spdy_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_http_spdy_srv_conf_t *sscf;
+
+ sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_srv_conf_t));
+ if (sscf == NULL) {
+ return NULL;
+ }
+
+ sscf->pool_size = NGX_CONF_UNSET_SIZE;
+
+ sscf->concurrent_streams = NGX_CONF_UNSET_UINT;
+ sscf->streams_index_mask = NGX_CONF_UNSET_UINT;
+
+ sscf->recv_timeout = NGX_CONF_UNSET_MSEC;
+ sscf->keepalive_timeout = NGX_CONF_UNSET_MSEC;
+
+ sscf->headers_comp = NGX_CONF_UNSET;
+
+ return sscf;
+}
+
+
+static char *
+ngx_http_spdy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_spdy_srv_conf_t *prev = parent;
+ ngx_http_spdy_srv_conf_t *conf = child;
+
+ ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096);
+
+ ngx_conf_merge_uint_value(conf->concurrent_streams,
+ prev->concurrent_streams, 100);
+
+ ngx_conf_merge_uint_value(conf->streams_index_mask,
+ prev->streams_index_mask, 32 - 1);
+
+ ngx_conf_merge_msec_value(conf->recv_timeout,
+ prev->recv_timeout, 30000);
+ ngx_conf_merge_msec_value(conf->keepalive_timeout,
+ prev->keepalive_timeout, 180000);
+
+ ngx_conf_merge_value(conf->headers_comp, prev->headers_comp, 0);
+
+ return NGX_CONF_OK;
+}
+
+
+static void *
+ngx_http_spdy_create_loc_conf(ngx_conf_t *cf)
+{
+ ngx_http_spdy_loc_conf_t *slcf;
+
+ slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_spdy_loc_conf_t));
+ if (slcf == NULL) {
+ return NULL;
+ }
+
+ slcf->chunk_size = NGX_CONF_UNSET_SIZE;
+
+ return slcf;
+}
+
+
+static char *
+ngx_http_spdy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_http_spdy_loc_conf_t *prev = parent;
+ ngx_http_spdy_loc_conf_t *conf = child;
+
+ ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024);
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_spdy_recv_buffer_size(ngx_conf_t *cf, void *post, void *data)
+{
+ size_t *sp = data;
+
+ if (*sp <= 2 * NGX_SPDY_STATE_BUFFER_SIZE) {
+ return "value is too small";
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_spdy_pool_size(ngx_conf_t *cf, void *post, void *data)
+{
+ size_t *sp = data;
+
+ if (*sp < NGX_MIN_POOL_SIZE) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the pool size must be no less than %uz",
+ NGX_MIN_POOL_SIZE);
+ return NGX_CONF_ERROR;
+ }
+
+ if (*sp % NGX_POOL_ALIGNMENT) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the pool size must be a multiple of %uz",
+ NGX_POOL_ALIGNMENT);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_spdy_streams_index_mask(ngx_conf_t *cf, void *post, void *data)
+{
+ ngx_uint_t *np = data;
+
+ ngx_uint_t mask;
+
+ mask = *np - 1;
+
+ if (*np == 0 || (*np & mask)) {
+ return "must be a power of two";
+ }
+
+ *np = mask;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_spdy_chunk_size(ngx_conf_t *cf, void *post, void *data)
+{
+ size_t *sp = data;
+
+ if (*sp == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the spdy chunk size cannot be zero");
+ return NGX_CONF_ERROR;
+ }
+
+ if (*sp > NGX_SPDY_MAX_FRAME_SIZE) {
+ *sp = NGX_SPDY_MAX_FRAME_SIZE;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy_module.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy_module.h
new file mode 100644
index 00000000000..5242322b3d6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_spdy_module.h
@@ -0,0 +1,41 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ * Copyright (C) Valentin V. Bartenev
+ */
+
+
+#ifndef _NGX_HTTP_SPDY_MODULE_H_INCLUDED_
+#define _NGX_HTTP_SPDY_MODULE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ size_t recv_buffer_size;
+ u_char *recv_buffer;
+} ngx_http_spdy_main_conf_t;
+
+
+typedef struct {
+ size_t pool_size;
+ ngx_uint_t concurrent_streams;
+ ngx_uint_t streams_index_mask;
+ ngx_msec_t recv_timeout;
+ ngx_msec_t keepalive_timeout;
+ ngx_int_t headers_comp;
+} ngx_http_spdy_srv_conf_t;
+
+
+typedef struct {
+ size_t chunk_size;
+} ngx_http_spdy_loc_conf_t;
+
+
+extern ngx_module_t ngx_http_spdy_module;
+
+
+#endif /* _NGX_HTTP_SPDY_MODULE_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_special_response.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_special_response.c
new file mode 100644
index 00000000000..546400539ba
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_special_response.c
@@ -0,0 +1,792 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <nginx.h>
+
+
+static ngx_int_t ngx_http_send_error_page(ngx_http_request_t *r,
+ ngx_http_err_page_t *err_page);
+static ngx_int_t ngx_http_send_special_response(ngx_http_request_t *r,
+ ngx_http_core_loc_conf_t *clcf, ngx_uint_t err);
+static ngx_int_t ngx_http_send_refresh(ngx_http_request_t *r);
+
+
+static u_char ngx_http_error_full_tail[] =
+"<hr><center>" NGINX_VER "</center>" CRLF
+"</body>" CRLF
+"</html>" CRLF
+;
+
+
+static u_char ngx_http_error_tail[] =
+"<hr><center>nginx</center>" CRLF
+"</body>" CRLF
+"</html>" CRLF
+;
+
+
+static u_char ngx_http_msie_padding[] =
+"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
+"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
+"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
+"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
+"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
+"<!-- a padding to disable MSIE and Chrome friendly error page -->" CRLF
+;
+
+
+static u_char ngx_http_msie_refresh_head[] =
+"<html><head><meta http-equiv=\"Refresh\" content=\"0; URL=";
+
+
+static u_char ngx_http_msie_refresh_tail[] =
+"\"></head><body></body></html>" CRLF;
+
+
+static char ngx_http_error_301_page[] =
+"<html>" CRLF
+"<head><title>301 Moved Permanently</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>301 Moved Permanently</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_302_page[] =
+"<html>" CRLF
+"<head><title>302 Found</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>302 Found</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_303_page[] =
+"<html>" CRLF
+"<head><title>303 See Other</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>303 See Other</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_307_page[] =
+"<html>" CRLF
+"<head><title>307 Temporary Redirect</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>307 Temporary Redirect</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_400_page[] =
+"<html>" CRLF
+"<head><title>400 Bad Request</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>400 Bad Request</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_401_page[] =
+"<html>" CRLF
+"<head><title>401 Authorization Required</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>401 Authorization Required</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_402_page[] =
+"<html>" CRLF
+"<head><title>402 Payment Required</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>402 Payment Required</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_403_page[] =
+"<html>" CRLF
+"<head><title>403 Forbidden</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>403 Forbidden</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_404_page[] =
+"<html>" CRLF
+"<head><title>404 Not Found</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>404 Not Found</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_405_page[] =
+"<html>" CRLF
+"<head><title>405 Not Allowed</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>405 Not Allowed</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_406_page[] =
+"<html>" CRLF
+"<head><title>406 Not Acceptable</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>406 Not Acceptable</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_408_page[] =
+"<html>" CRLF
+"<head><title>408 Request Time-out</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>408 Request Time-out</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_409_page[] =
+"<html>" CRLF
+"<head><title>409 Conflict</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>409 Conflict</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_410_page[] =
+"<html>" CRLF
+"<head><title>410 Gone</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>410 Gone</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_411_page[] =
+"<html>" CRLF
+"<head><title>411 Length Required</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>411 Length Required</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_412_page[] =
+"<html>" CRLF
+"<head><title>412 Precondition Failed</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>412 Precondition Failed</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_413_page[] =
+"<html>" CRLF
+"<head><title>413 Request Entity Too Large</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>413 Request Entity Too Large</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_414_page[] =
+"<html>" CRLF
+"<head><title>414 Request-URI Too Large</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>414 Request-URI Too Large</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_415_page[] =
+"<html>" CRLF
+"<head><title>415 Unsupported Media Type</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>415 Unsupported Media Type</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_416_page[] =
+"<html>" CRLF
+"<head><title>416 Requested Range Not Satisfiable</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>416 Requested Range Not Satisfiable</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_494_page[] =
+"<html>" CRLF
+"<head><title>400 Request Header Or Cookie Too Large</title></head>"
+CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>400 Bad Request</h1></center>" CRLF
+"<center>Request Header Or Cookie Too Large</center>" CRLF
+;
+
+
+static char ngx_http_error_495_page[] =
+"<html>" CRLF
+"<head><title>400 The SSL certificate error</title></head>"
+CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>400 Bad Request</h1></center>" CRLF
+"<center>The SSL certificate error</center>" CRLF
+;
+
+
+static char ngx_http_error_496_page[] =
+"<html>" CRLF
+"<head><title>400 No required SSL certificate was sent</title></head>"
+CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>400 Bad Request</h1></center>" CRLF
+"<center>No required SSL certificate was sent</center>" CRLF
+;
+
+
+static char ngx_http_error_497_page[] =
+"<html>" CRLF
+"<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>"
+CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>400 Bad Request</h1></center>" CRLF
+"<center>The plain HTTP request was sent to HTTPS port</center>" CRLF
+;
+
+
+static char ngx_http_error_500_page[] =
+"<html>" CRLF
+"<head><title>500 Internal Server Error</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>500 Internal Server Error</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_501_page[] =
+"<html>" CRLF
+"<head><title>501 Not Implemented</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>501 Not Implemented</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_502_page[] =
+"<html>" CRLF
+"<head><title>502 Bad Gateway</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>502 Bad Gateway</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_503_page[] =
+"<html>" CRLF
+"<head><title>503 Service Temporarily Unavailable</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>503 Service Temporarily Unavailable</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_504_page[] =
+"<html>" CRLF
+"<head><title>504 Gateway Time-out</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>504 Gateway Time-out</h1></center>" CRLF
+;
+
+
+static char ngx_http_error_507_page[] =
+"<html>" CRLF
+"<head><title>507 Insufficient Storage</title></head>" CRLF
+"<body bgcolor=\"white\">" CRLF
+"<center><h1>507 Insufficient Storage</h1></center>" CRLF
+;
+
+
+static ngx_str_t ngx_http_error_pages[] = {
+
+ ngx_null_string, /* 201, 204 */
+
+#define NGX_HTTP_LAST_2XX 202
+#define NGX_HTTP_OFF_3XX (NGX_HTTP_LAST_2XX - 201)
+
+ /* ngx_null_string, */ /* 300 */
+ ngx_string(ngx_http_error_301_page),
+ ngx_string(ngx_http_error_302_page),
+ ngx_string(ngx_http_error_303_page),
+ ngx_null_string, /* 304 */
+ ngx_null_string, /* 305 */
+ ngx_null_string, /* 306 */
+ ngx_string(ngx_http_error_307_page),
+
+#define NGX_HTTP_LAST_3XX 308
+#define NGX_HTTP_OFF_4XX (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)
+
+ ngx_string(ngx_http_error_400_page),
+ ngx_string(ngx_http_error_401_page),
+ ngx_string(ngx_http_error_402_page),
+ ngx_string(ngx_http_error_403_page),
+ ngx_string(ngx_http_error_404_page),
+ ngx_string(ngx_http_error_405_page),
+ ngx_string(ngx_http_error_406_page),
+ ngx_null_string, /* 407 */
+ ngx_string(ngx_http_error_408_page),
+ ngx_string(ngx_http_error_409_page),
+ ngx_string(ngx_http_error_410_page),
+ ngx_string(ngx_http_error_411_page),
+ ngx_string(ngx_http_error_412_page),
+ ngx_string(ngx_http_error_413_page),
+ ngx_string(ngx_http_error_414_page),
+ ngx_string(ngx_http_error_415_page),
+ ngx_string(ngx_http_error_416_page),
+
+#define NGX_HTTP_LAST_4XX 417
+#define NGX_HTTP_OFF_5XX (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)
+
+ ngx_string(ngx_http_error_494_page), /* 494, request header too large */
+ ngx_string(ngx_http_error_495_page), /* 495, https certificate error */
+ ngx_string(ngx_http_error_496_page), /* 496, https no certificate */
+ ngx_string(ngx_http_error_497_page), /* 497, http to https */
+ ngx_string(ngx_http_error_404_page), /* 498, canceled */
+ ngx_null_string, /* 499, client has closed connection */
+
+ ngx_string(ngx_http_error_500_page),
+ ngx_string(ngx_http_error_501_page),
+ ngx_string(ngx_http_error_502_page),
+ ngx_string(ngx_http_error_503_page),
+ ngx_string(ngx_http_error_504_page),
+ ngx_null_string, /* 505 */
+ ngx_null_string, /* 506 */
+ ngx_string(ngx_http_error_507_page)
+
+#define NGX_HTTP_LAST_5XX 508
+
+};
+
+
+static ngx_str_t ngx_http_get_name = { 3, (u_char *) "GET " };
+
+
+ngx_int_t
+ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error)
+{
+ ngx_uint_t i, err;
+ ngx_http_err_page_t *err_page;
+ ngx_http_core_loc_conf_t *clcf;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http special response: %i, \"%V?%V\"",
+ error, &r->uri, &r->args);
+
+ r->err_status = error;
+
+ if (r->keepalive) {
+ switch (error) {
+ case NGX_HTTP_BAD_REQUEST:
+ case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE:
+ case NGX_HTTP_REQUEST_URI_TOO_LARGE:
+ case NGX_HTTP_TO_HTTPS:
+ case NGX_HTTPS_CERT_ERROR:
+ case NGX_HTTPS_NO_CERT:
+ case NGX_HTTP_INTERNAL_SERVER_ERROR:
+ case NGX_HTTP_NOT_IMPLEMENTED:
+ r->keepalive = 0;
+ }
+ }
+
+ if (r->lingering_close) {
+ switch (error) {
+ case NGX_HTTP_BAD_REQUEST:
+ case NGX_HTTP_TO_HTTPS:
+ case NGX_HTTPS_CERT_ERROR:
+ case NGX_HTTPS_NO_CERT:
+ r->lingering_close = 0;
+ }
+ }
+
+ r->headers_out.content_type.len = 0;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (!r->error_page && clcf->error_pages && r->uri_changes != 0) {
+
+ if (clcf->recursive_error_pages == 0) {
+ r->error_page = 1;
+ }
+
+ err_page = clcf->error_pages->elts;
+
+ for (i = 0; i < clcf->error_pages->nelts; i++) {
+ if (err_page[i].status == error) {
+ return ngx_http_send_error_page(r, &err_page[i]);
+ }
+ }
+ }
+
+ r->expect_tested = 1;
+
+ if (ngx_http_discard_request_body(r) != NGX_OK) {
+ r->keepalive = 0;
+ }
+
+ if (clcf->msie_refresh
+ && r->headers_in.msie
+ && (error == NGX_HTTP_MOVED_PERMANENTLY
+ || error == NGX_HTTP_MOVED_TEMPORARILY))
+ {
+ return ngx_http_send_refresh(r);
+ }
+
+ if (error == NGX_HTTP_CREATED) {
+ /* 201 */
+ err = 0;
+
+ } else if (error == NGX_HTTP_NO_CONTENT) {
+ /* 204 */
+ err = 0;
+
+ } else if (error >= NGX_HTTP_MOVED_PERMANENTLY
+ && error < NGX_HTTP_LAST_3XX)
+ {
+ /* 3XX */
+ err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;
+
+ } else if (error >= NGX_HTTP_BAD_REQUEST
+ && error < NGX_HTTP_LAST_4XX)
+ {
+ /* 4XX */
+ err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_OFF_4XX;
+
+ } else if (error >= NGX_HTTP_NGINX_CODES
+ && error < NGX_HTTP_LAST_5XX)
+ {
+ /* 49X, 5XX */
+ err = error - NGX_HTTP_NGINX_CODES + NGX_HTTP_OFF_5XX;
+ switch (error) {
+ case NGX_HTTP_TO_HTTPS:
+ case NGX_HTTPS_CERT_ERROR:
+ case NGX_HTTPS_NO_CERT:
+ case NGX_HTTP_REQUEST_HEADER_TOO_LARGE:
+ r->err_status = NGX_HTTP_BAD_REQUEST;
+ break;
+ }
+
+ } else {
+ /* unknown code, zero body */
+ err = 0;
+ }
+
+ return ngx_http_send_special_response(r, clcf, err);
+}
+
+
+ngx_int_t
+ngx_http_filter_finalize_request(ngx_http_request_t *r, ngx_module_t *m,
+ ngx_int_t error)
+{
+ void *ctx;
+ ngx_int_t rc;
+
+ ngx_http_clean_header(r);
+
+ ctx = NULL;
+
+ if (m) {
+ ctx = r->ctx[m->ctx_index];
+ }
+
+ /* clear the modules contexts */
+ ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
+
+ if (m) {
+ r->ctx[m->ctx_index] = ctx;
+ }
+
+ r->filter_finalize = 1;
+
+ rc = ngx_http_special_response_handler(r, error);
+
+ /* NGX_ERROR resets any pending data */
+
+ switch (rc) {
+
+ case NGX_OK:
+ case NGX_DONE:
+ return NGX_ERROR;
+
+ default:
+ return rc;
+ }
+}
+
+
+void
+ngx_http_clean_header(ngx_http_request_t *r)
+{
+ ngx_memzero(&r->headers_out.status,
+ sizeof(ngx_http_headers_out_t)
+ - offsetof(ngx_http_headers_out_t, status));
+
+ r->headers_out.headers.part.nelts = 0;
+ r->headers_out.headers.part.next = NULL;
+ r->headers_out.headers.last = &r->headers_out.headers.part;
+
+ r->headers_out.content_length_n = -1;
+ r->headers_out.last_modified_time = -1;
+}
+
+
+static ngx_int_t
+ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page)
+{
+ ngx_int_t overwrite;
+ ngx_str_t uri, args;
+ ngx_table_elt_t *location;
+ ngx_http_core_loc_conf_t *clcf;
+
+ overwrite = err_page->overwrite;
+
+ if (overwrite && overwrite != NGX_HTTP_OK) {
+ r->expect_tested = 1;
+ }
+
+ if (overwrite >= 0) {
+ r->err_status = overwrite;
+ }
+
+ if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (uri.data[0] == '/') {
+
+ if (err_page->value.lengths) {
+ ngx_http_split_args(r, &uri, &args);
+
+ } else {
+ args = err_page->args;
+ }
+
+ if (r->method != NGX_HTTP_HEAD) {
+ r->method = NGX_HTTP_GET;
+ r->method_name = ngx_http_get_name;
+ }
+
+ return ngx_http_internal_redirect(r, &uri, &args);
+ }
+
+ if (uri.data[0] == '@') {
+ return ngx_http_named_location(r, &uri);
+ }
+
+ location = ngx_list_push(&r->headers_out.headers);
+
+ if (location == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (overwrite != NGX_HTTP_MOVED_PERMANENTLY
+ && overwrite != NGX_HTTP_MOVED_TEMPORARILY
+ && overwrite != NGX_HTTP_SEE_OTHER
+ && overwrite != NGX_HTTP_TEMPORARY_REDIRECT)
+ {
+ r->err_status = NGX_HTTP_MOVED_TEMPORARILY;
+ }
+
+ location->hash = 1;
+ ngx_str_set(&location->key, "Location");
+ location->value = uri;
+
+ ngx_http_clear_location(r);
+
+ r->headers_out.location = location;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->msie_refresh && r->headers_in.msie) {
+ return ngx_http_send_refresh(r);
+ }
+
+ return ngx_http_send_special_response(r, clcf, r->err_status
+ - NGX_HTTP_MOVED_PERMANENTLY
+ + NGX_HTTP_OFF_3XX);
+}
+
+
+static ngx_int_t
+ngx_http_send_special_response(ngx_http_request_t *r,
+ ngx_http_core_loc_conf_t *clcf, ngx_uint_t err)
+{
+ u_char *tail;
+ size_t len;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_uint_t msie_padding;
+ ngx_chain_t out[3];
+
+ if (clcf->server_tokens) {
+ len = sizeof(ngx_http_error_full_tail) - 1;
+ tail = ngx_http_error_full_tail;
+
+ } else {
+ len = sizeof(ngx_http_error_tail) - 1;
+ tail = ngx_http_error_tail;
+ }
+
+ msie_padding = 0;
+
+ if (ngx_http_error_pages[err].len) {
+ r->headers_out.content_length_n = ngx_http_error_pages[err].len + len;
+ if (clcf->msie_padding
+ && (r->headers_in.msie || r->headers_in.chrome)
+ && r->http_version >= NGX_HTTP_VERSION_10
+ && err >= NGX_HTTP_OFF_4XX)
+ {
+ r->headers_out.content_length_n +=
+ sizeof(ngx_http_msie_padding) - 1;
+ msie_padding = 1;
+ }
+
+ r->headers_out.content_type_len = sizeof("text/html") - 1;
+ ngx_str_set(&r->headers_out.content_type, "text/html");
+ r->headers_out.content_type_lowcase = NULL;
+
+ } else {
+ r->headers_out.content_length_n = 0;
+ }
+
+ if (r->headers_out.content_length) {
+ r->headers_out.content_length->hash = 0;
+ r->headers_out.content_length = NULL;
+ }
+
+ ngx_http_clear_accept_ranges(r);
+ ngx_http_clear_last_modified(r);
+ ngx_http_clear_etag(r);
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || r->header_only) {
+ return rc;
+ }
+
+ if (ngx_http_error_pages[err].len == 0) {
+ return ngx_http_send_special(r, NGX_HTTP_LAST);
+ }
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->memory = 1;
+ b->pos = ngx_http_error_pages[err].data;
+ b->last = ngx_http_error_pages[err].data + ngx_http_error_pages[err].len;
+
+ out[0].buf = b;
+ out[0].next = &out[1];
+
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->memory = 1;
+
+ b->pos = tail;
+ b->last = tail + len;
+
+ out[1].buf = b;
+ out[1].next = NULL;
+
+ if (msie_padding) {
+ b = ngx_calloc_buf(r->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->memory = 1;
+ b->pos = ngx_http_msie_padding;
+ b->last = ngx_http_msie_padding + sizeof(ngx_http_msie_padding) - 1;
+
+ out[1].next = &out[2];
+ out[2].buf = b;
+ out[2].next = NULL;
+ }
+
+ if (r == r->main) {
+ b->last_buf = 1;
+ }
+
+ b->last_in_chain = 1;
+
+ return ngx_http_output_filter(r, &out[0]);
+}
+
+
+static ngx_int_t
+ngx_http_send_refresh(ngx_http_request_t *r)
+{
+ u_char *p, *location;
+ size_t len, size;
+ uintptr_t escape;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t out;
+
+ len = r->headers_out.location->value.len;
+ location = r->headers_out.location->value.data;
+
+ escape = 2 * ngx_escape_uri(NULL, location, len, NGX_ESCAPE_REFRESH);
+
+ size = sizeof(ngx_http_msie_refresh_head) - 1
+ + escape + len
+ + sizeof(ngx_http_msie_refresh_tail) - 1;
+
+ r->err_status = NGX_HTTP_OK;
+
+ r->headers_out.content_type_len = sizeof("text/html") - 1;
+ ngx_str_set(&r->headers_out.content_type, "text/html");
+ r->headers_out.content_type_lowcase = NULL;
+
+ r->headers_out.location->hash = 0;
+ r->headers_out.location = NULL;
+
+ r->headers_out.content_length_n = size;
+
+ if (r->headers_out.content_length) {
+ r->headers_out.content_length->hash = 0;
+ r->headers_out.content_length = NULL;
+ }
+
+ ngx_http_clear_accept_ranges(r);
+ ngx_http_clear_last_modified(r);
+ ngx_http_clear_etag(r);
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || r->header_only) {
+ return rc;
+ }
+
+ b = ngx_create_temp_buf(r->pool, size);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head,
+ sizeof(ngx_http_msie_refresh_head) - 1);
+
+ if (escape == 0) {
+ p = ngx_cpymem(p, location, len);
+
+ } else {
+ p = (u_char *) ngx_escape_uri(p, location, len, NGX_ESCAPE_REFRESH);
+ }
+
+ b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail,
+ sizeof(ngx_http_msie_refresh_tail) - 1);
+
+ b->last_buf = 1;
+ b->last_in_chain = 1;
+
+ out.buf = b;
+ out.next = NULL;
+
+ return ngx_http_output_filter(r, &out);
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream.c
new file mode 100644
index 00000000000..18b04f775f0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream.c
@@ -0,0 +1,5553 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#if (NGX_HTTP_CACHE)
+static ngx_int_t ngx_http_upstream_cache(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_cache_status(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_cache_last_modified(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_cache_etag(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+#endif
+
+static void ngx_http_upstream_init_request(ngx_http_request_t *r);
+static void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r);
+static void ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r);
+static void ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
+ ngx_event_t *ev);
+static void ngx_http_upstream_connect(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_reinit(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_send_request(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_header(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c);
+static ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_send_response(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgrade(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r);
+static void ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r);
+static void ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
+ ngx_uint_t from_upstream, ngx_uint_t do_write);
+static void
+ ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r);
+static void
+ ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void
+ ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
+ ngx_uint_t do_write);
+static ngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data);
+static ngx_int_t ngx_http_upstream_non_buffered_filter(void *data,
+ ssize_t bytes);
+static void ngx_http_upstream_process_downstream(ngx_http_request_t *r);
+static void ngx_http_upstream_process_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_request(ngx_http_request_t *r);
+static void ngx_http_upstream_store(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_dummy_handler(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_next(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_uint_t ft_type);
+static void ngx_http_upstream_cleanup(void *data);
+static void ngx_http_upstream_finalize_request(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_int_t rc);
+
+static ngx_int_t ngx_http_upstream_process_header_line(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_content_length(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_last_modified(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t
+ ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_accel_expires(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_limit_rate(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_buffering(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_charset(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_connection(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t
+ ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t
+ ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_copy_content_type(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_rewrite_location(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+
+#if (NGX_HTTP_GZIP)
+static ngx_int_t ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
+#endif
+
+static ngx_int_t ngx_http_upstream_add_variables(ngx_conf_t *cf);
+static ngx_int_t ngx_http_upstream_addr_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_status_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_response_time_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upstream_response_length_variable(
+ ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);
+
+static char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy);
+static char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+static ngx_addr_t *ngx_http_upstream_get_local(ngx_http_request_t *r,
+ ngx_http_upstream_local_t *local);
+
+static void *ngx_http_upstream_create_main_conf(ngx_conf_t *cf);
+static char *ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf);
+
+#if (NGX_HTTP_SSL)
+static void ngx_http_upstream_ssl_init_connection(ngx_http_request_t *,
+ ngx_http_upstream_t *u, ngx_connection_t *c);
+static void ngx_http_upstream_ssl_handshake(ngx_connection_t *c);
+static ngx_int_t ngx_http_upstream_ssl_name(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_connection_t *c);
+#endif
+
+
+ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = {
+
+ { ngx_string("Status"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, status),
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("Content-Type"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, content_type),
+ ngx_http_upstream_copy_content_type, 0, 1 },
+
+ { ngx_string("Content-Length"),
+ ngx_http_upstream_process_content_length, 0,
+ ngx_http_upstream_ignore_header_line, 0, 0 },
+
+ { ngx_string("Date"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, date),
+ ngx_http_upstream_copy_header_line,
+ offsetof(ngx_http_headers_out_t, date), 0 },
+
+ { ngx_string("Last-Modified"),
+ ngx_http_upstream_process_last_modified, 0,
+ ngx_http_upstream_copy_last_modified, 0, 0 },
+
+ { ngx_string("ETag"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, etag),
+ ngx_http_upstream_copy_header_line,
+ offsetof(ngx_http_headers_out_t, etag), 0 },
+
+ { ngx_string("Server"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, server),
+ ngx_http_upstream_copy_header_line,
+ offsetof(ngx_http_headers_out_t, server), 0 },
+
+ { ngx_string("WWW-Authenticate"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, www_authenticate),
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("Location"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, location),
+ ngx_http_upstream_rewrite_location, 0, 0 },
+
+ { ngx_string("Refresh"),
+ ngx_http_upstream_ignore_header_line, 0,
+ ngx_http_upstream_rewrite_refresh, 0, 0 },
+
+ { ngx_string("Set-Cookie"),
+ ngx_http_upstream_process_set_cookie,
+ offsetof(ngx_http_upstream_headers_in_t, cookies),
+ ngx_http_upstream_rewrite_set_cookie, 0, 1 },
+
+ { ngx_string("Content-Disposition"),
+ ngx_http_upstream_ignore_header_line, 0,
+ ngx_http_upstream_copy_header_line, 0, 1 },
+
+ { ngx_string("Cache-Control"),
+ ngx_http_upstream_process_cache_control, 0,
+ ngx_http_upstream_copy_multi_header_lines,
+ offsetof(ngx_http_headers_out_t, cache_control), 1 },
+
+ { ngx_string("Expires"),
+ ngx_http_upstream_process_expires, 0,
+ ngx_http_upstream_copy_header_line,
+ offsetof(ngx_http_headers_out_t, expires), 1 },
+
+ { ngx_string("Accept-Ranges"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, accept_ranges),
+ ngx_http_upstream_copy_allow_ranges,
+ offsetof(ngx_http_headers_out_t, accept_ranges), 1 },
+
+ { ngx_string("Connection"),
+ ngx_http_upstream_process_connection, 0,
+ ngx_http_upstream_ignore_header_line, 0, 0 },
+
+ { ngx_string("Keep-Alive"),
+ ngx_http_upstream_ignore_header_line, 0,
+ ngx_http_upstream_ignore_header_line, 0, 0 },
+
+ { ngx_string("X-Powered-By"),
+ ngx_http_upstream_ignore_header_line, 0,
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("X-Accel-Expires"),
+ ngx_http_upstream_process_accel_expires, 0,
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("X-Accel-Redirect"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, x_accel_redirect),
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("X-Accel-Limit-Rate"),
+ ngx_http_upstream_process_limit_rate, 0,
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("X-Accel-Buffering"),
+ ngx_http_upstream_process_buffering, 0,
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("X-Accel-Charset"),
+ ngx_http_upstream_process_charset, 0,
+ ngx_http_upstream_copy_header_line, 0, 0 },
+
+ { ngx_string("Transfer-Encoding"),
+ ngx_http_upstream_process_transfer_encoding, 0,
+ ngx_http_upstream_ignore_header_line, 0, 0 },
+
+#if (NGX_HTTP_GZIP)
+ { ngx_string("Content-Encoding"),
+ ngx_http_upstream_process_header_line,
+ offsetof(ngx_http_upstream_headers_in_t, content_encoding),
+ ngx_http_upstream_copy_content_encoding, 0, 0 },
+#endif
+
+ { ngx_null_string, NULL, 0, NULL, 0, 0 }
+};
+
+
+static ngx_command_t ngx_http_upstream_commands[] = {
+
+ { ngx_string("upstream"),
+ NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
+ ngx_http_upstream,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("server"),
+ NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,
+ ngx_http_upstream_server,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_upstream_module_ctx = {
+ ngx_http_upstream_add_variables, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ ngx_http_upstream_create_main_conf, /* create main configuration */
+ ngx_http_upstream_init_main_conf, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_upstream_module = {
+ NGX_MODULE_V1,
+ &ngx_http_upstream_module_ctx, /* module context */
+ ngx_http_upstream_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_variable_t ngx_http_upstream_vars[] = {
+
+ { ngx_string("upstream_addr"), NULL,
+ ngx_http_upstream_addr_variable, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("upstream_status"), NULL,
+ ngx_http_upstream_status_variable, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("upstream_response_time"), NULL,
+ ngx_http_upstream_response_time_variable, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("upstream_response_length"), NULL,
+ ngx_http_upstream_response_length_variable, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+#if (NGX_HTTP_CACHE)
+
+ { ngx_string("upstream_cache_status"), NULL,
+ ngx_http_upstream_cache_status, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("upstream_cache_last_modified"), NULL,
+ ngx_http_upstream_cache_last_modified, 0,
+ NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+ { ngx_string("upstream_cache_etag"), NULL,
+ ngx_http_upstream_cache_etag, 0,
+ NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+#endif
+
+ { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
+
+
+static ngx_http_upstream_next_t ngx_http_upstream_next_errors[] = {
+ { 500, NGX_HTTP_UPSTREAM_FT_HTTP_500 },
+ { 502, NGX_HTTP_UPSTREAM_FT_HTTP_502 },
+ { 503, NGX_HTTP_UPSTREAM_FT_HTTP_503 },
+ { 504, NGX_HTTP_UPSTREAM_FT_HTTP_504 },
+ { 403, NGX_HTTP_UPSTREAM_FT_HTTP_403 },
+ { 404, NGX_HTTP_UPSTREAM_FT_HTTP_404 },
+ { 0, 0 }
+};
+
+
+ngx_conf_bitmask_t ngx_http_upstream_cache_method_mask[] = {
+ { ngx_string("GET"), NGX_HTTP_GET},
+ { ngx_string("HEAD"), NGX_HTTP_HEAD },
+ { ngx_string("POST"), NGX_HTTP_POST },
+ { ngx_null_string, 0 }
+};
+
+
+ngx_conf_bitmask_t ngx_http_upstream_ignore_headers_masks[] = {
+ { ngx_string("X-Accel-Redirect"), NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT },
+ { ngx_string("X-Accel-Expires"), NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES },
+ { ngx_string("X-Accel-Limit-Rate"), NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE },
+ { ngx_string("X-Accel-Buffering"), NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING },
+ { ngx_string("X-Accel-Charset"), NGX_HTTP_UPSTREAM_IGN_XA_CHARSET },
+ { ngx_string("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES },
+ { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL },
+ { ngx_string("Set-Cookie"), NGX_HTTP_UPSTREAM_IGN_SET_COOKIE },
+ { ngx_null_string, 0 }
+};
+
+
+ngx_int_t
+ngx_http_upstream_create(ngx_http_request_t *r)
+{
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+
+ if (u && u->cleanup) {
+ r->main->count++;
+ ngx_http_upstream_cleanup(r);
+ }
+
+ u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
+ if (u == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->upstream = u;
+
+ u->peer.log = r->connection->log;
+ u->peer.log_error = NGX_ERROR_ERR;
+#if (NGX_THREADS)
+ u->peer.lock = &r->connection->lock;
+#endif
+
+#if (NGX_HTTP_CACHE)
+ r->cache = NULL;
+#endif
+
+ u->headers_in.content_length_n = -1;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_http_upstream_init(ngx_http_request_t *r)
+{
+ ngx_connection_t *c;
+
+ c = r->connection;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http init upstream, client timer: %d", c->read->timer_set);
+
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ ngx_http_upstream_init_request(r);
+ return;
+ }
+#endif
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {
+
+ if (!c->write->active) {
+ if (ngx_add_event(c->write, NGX_WRITE_EVENT, NGX_CLEAR_EVENT)
+ == NGX_ERROR)
+ {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+ }
+
+ ngx_http_upstream_init_request(r);
+}
+
+
+static void
+ngx_http_upstream_init_request(ngx_http_request_t *r)
+{
+ ngx_str_t *host;
+ ngx_uint_t i;
+ ngx_resolver_ctx_t *ctx, temp;
+ ngx_http_cleanup_t *cln;
+ ngx_http_upstream_t *u;
+ ngx_http_core_loc_conf_t *clcf;
+ ngx_http_upstream_srv_conf_t *uscf, **uscfp;
+ ngx_http_upstream_main_conf_t *umcf;
+
+ if (r->aio) {
+ return;
+ }
+
+ u = r->upstream;
+
+#if (NGX_HTTP_CACHE)
+
+ if (u->conf->cache) {
+ ngx_int_t rc;
+
+ rc = ngx_http_upstream_cache(r, u);
+
+ if (rc == NGX_BUSY) {
+ r->write_event_handler = ngx_http_upstream_init_request;
+ return;
+ }
+
+ r->write_event_handler = ngx_http_request_empty_handler;
+
+ if (rc == NGX_DONE) {
+ return;
+ }
+
+ if (rc != NGX_DECLINED) {
+ ngx_http_finalize_request(r, rc);
+ return;
+ }
+ }
+
+#endif
+
+ u->store = (u->conf->store || u->conf->store_lengths);
+
+ if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {
+ r->read_event_handler = ngx_http_upstream_rd_check_broken_connection;
+ r->write_event_handler = ngx_http_upstream_wr_check_broken_connection;
+ }
+
+ if (r->request_body) {
+ u->request_bufs = r->request_body->bufs;
+ }
+
+ if (u->create_request(r) != NGX_OK) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ u->peer.local = ngx_http_upstream_get_local(r, u->conf->local);
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ u->output.alignment = clcf->directio_alignment;
+ u->output.pool = r->pool;
+ u->output.bufs.num = 1;
+ u->output.bufs.size = clcf->client_body_buffer_size;
+ u->output.output_filter = ngx_chain_writer;
+ u->output.filter_ctx = &u->writer;
+
+ u->writer.pool = r->pool;
+
+ if (r->upstream_states == NULL) {
+
+ r->upstream_states = ngx_array_create(r->pool, 1,
+ sizeof(ngx_http_upstream_state_t));
+ if (r->upstream_states == NULL) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ } else {
+
+ u->state = ngx_array_push(r->upstream_states);
+ if (u->state == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));
+ }
+
+ cln = ngx_http_cleanup_add(r, 0);
+ if (cln == NULL) {
+ ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ cln->handler = ngx_http_upstream_cleanup;
+ cln->data = r;
+ u->cleanup = &cln->handler;
+
+ if (u->resolved == NULL) {
+
+ uscf = u->conf->upstream;
+
+ } else {
+
+#if (NGX_HTTP_SSL)
+ u->ssl_name = u->resolved->host;
+#endif
+
+ if (u->resolved->sockaddr) {
+
+ if (ngx_http_upstream_create_round_robin_peer(r, u->resolved)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ ngx_http_upstream_connect(r, u);
+
+ return;
+ }
+
+ host = &u->resolved->host;
+
+ umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
+
+ uscfp = umcf->upstreams.elts;
+
+ for (i = 0; i < umcf->upstreams.nelts; i++) {
+
+ uscf = uscfp[i];
+
+ if (uscf->host.len == host->len
+ && ((uscf->port == 0 && u->resolved->no_port)
+ || uscf->port == u->resolved->port)
+ && ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0)
+ {
+ goto found;
+ }
+ }
+
+ if (u->resolved->port == 0) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "no port in upstream \"%V\"", host);
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ temp.name = *host;
+
+ ctx = ngx_resolve_start(clcf->resolver, &temp);
+ if (ctx == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (ctx == NGX_NO_RESOLVER) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "no resolver defined to resolve %V", host);
+
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
+ return;
+ }
+
+ ctx->name = *host;
+ ctx->handler = ngx_http_upstream_resolve_handler;
+ ctx->data = r;
+ ctx->timeout = clcf->resolver_timeout;
+
+ u->resolved->ctx = ctx;
+
+ if (ngx_resolve_name(ctx) != NGX_OK) {
+ u->resolved->ctx = NULL;
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ return;
+ }
+
+found:
+
+ if (uscf == NULL) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "no upstream configuration");
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+#if (NGX_HTTP_SSL)
+ u->ssl_name = uscf->host;
+#endif
+
+ if (uscf->peer.init(r, uscf) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ ngx_http_upstream_connect(r, u);
+}
+
+
+#if (NGX_HTTP_CACHE)
+
+static ngx_int_t
+ngx_http_upstream_cache(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_int_t rc;
+ ngx_http_cache_t *c;
+
+ c = r->cache;
+
+ if (c == NULL) {
+
+ if (!(r->method & u->conf->cache_methods)) {
+ return NGX_DECLINED;
+ }
+
+ if (r->method & NGX_HTTP_HEAD) {
+ u->method = ngx_http_core_get_method;
+ }
+
+ if (ngx_http_file_cache_new(r) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (u->create_key(r) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ /* TODO: add keys */
+
+ ngx_http_file_cache_create_key(r);
+
+ if (r->cache->header_start + 256 >= u->conf->buffer_size) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "%V_buffer_size %uz is not enough for cache key, "
+ "it should be increased to at least %uz",
+ &u->conf->module, u->conf->buffer_size,
+ ngx_align(r->cache->header_start + 256, 1024));
+
+ r->cache = NULL;
+ return NGX_DECLINED;
+ }
+
+ u->cacheable = 1;
+
+ switch (ngx_http_test_predicates(r, u->conf->cache_bypass)) {
+
+ case NGX_ERROR:
+ return NGX_ERROR;
+
+ case NGX_DECLINED:
+ u->cache_status = NGX_HTTP_CACHE_BYPASS;
+ return NGX_DECLINED;
+
+ default: /* NGX_OK */
+ break;
+ }
+
+ c = r->cache;
+
+ c->min_uses = u->conf->cache_min_uses;
+ c->body_start = u->conf->buffer_size;
+ c->file_cache = u->conf->cache->data;
+
+ c->lock = u->conf->cache_lock;
+ c->lock_timeout = u->conf->cache_lock_timeout;
+
+ u->cache_status = NGX_HTTP_CACHE_MISS;
+ }
+
+ rc = ngx_http_file_cache_open(r);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream cache: %i", rc);
+
+ switch (rc) {
+
+ case NGX_HTTP_CACHE_UPDATING:
+
+ if (u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING) {
+ u->cache_status = rc;
+ rc = NGX_OK;
+
+ } else {
+ rc = NGX_HTTP_CACHE_STALE;
+ }
+
+ break;
+
+ case NGX_OK:
+ u->cache_status = NGX_HTTP_CACHE_HIT;
+ }
+
+ switch (rc) {
+
+ case NGX_OK:
+
+ rc = ngx_http_upstream_cache_send(r, u);
+
+ if (rc != NGX_HTTP_UPSTREAM_INVALID_HEADER) {
+ return rc;
+ }
+
+ break;
+
+ case NGX_HTTP_CACHE_STALE:
+
+ c->valid_sec = 0;
+ u->buffer.start = NULL;
+ u->cache_status = NGX_HTTP_CACHE_EXPIRED;
+
+ break;
+
+ case NGX_DECLINED:
+
+ if ((size_t) (u->buffer.end - u->buffer.start) < u->conf->buffer_size) {
+ u->buffer.start = NULL;
+
+ } else {
+ u->buffer.pos = u->buffer.start + c->header_start;
+ u->buffer.last = u->buffer.pos;
+ }
+
+ break;
+
+ case NGX_HTTP_CACHE_SCARCE:
+
+ u->cacheable = 0;
+
+ break;
+
+ case NGX_AGAIN:
+
+ return NGX_BUSY;
+
+ case NGX_ERROR:
+
+ return NGX_ERROR;
+
+ default:
+
+ /* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */
+
+ u->cache_status = NGX_HTTP_CACHE_HIT;
+
+ return rc;
+ }
+
+ r->cached = 0;
+
+ return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_int_t rc;
+ ngx_http_cache_t *c;
+
+ r->cached = 1;
+ c = r->cache;
+
+ if (c->header_start == c->body_start) {
+ r->http_version = NGX_HTTP_VERSION_9;
+ return ngx_http_cache_send(r);
+ }
+
+ /* TODO: cache stack */
+
+ u->buffer = *c->buf;
+ u->buffer.pos += c->header_start;
+
+ ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
+ u->headers_in.content_length_n = -1;
+
+ if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ rc = u->process_header(r);
+
+ if (rc == NGX_OK) {
+
+ if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {
+ return NGX_DONE;
+ }
+
+ return ngx_http_cache_send(r);
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ /* rc == NGX_HTTP_UPSTREAM_INVALID_HEADER */
+
+ /* TODO: delete file */
+
+ return rc;
+}
+
+#endif
+
+
+static void
+ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx)
+{
+ ngx_connection_t *c;
+ ngx_http_request_t *r;
+ ngx_http_upstream_t *u;
+ ngx_http_upstream_resolved_t *ur;
+
+ r = ctx->data;
+ c = r->connection;
+
+ u = r->upstream;
+ ur = u->resolved;
+
+ if (ctx->state) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "%V could not be resolved (%i: %s)",
+ &ctx->name, ctx->state,
+ ngx_resolver_strerror(ctx->state));
+
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
+ goto failed;
+ }
+
+ ur->naddrs = ctx->naddrs;
+ ur->addrs = ctx->addrs;
+
+#if (NGX_DEBUG)
+ {
+ u_char text[NGX_SOCKADDR_STRLEN];
+ ngx_str_t addr;
+ ngx_uint_t i;
+
+ addr.data = text;
+
+ for (i = 0; i < ctx->naddrs; i++) {
+ addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen,
+ text, NGX_SOCKADDR_STRLEN, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "name was resolved to %V", &addr);
+ }
+ }
+#endif
+
+ if (ngx_http_upstream_create_round_robin_peer(r, ur) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ goto failed;
+ }
+
+ ngx_resolve_name_done(ctx);
+ ur->ctx = NULL;
+
+ ngx_http_upstream_connect(r, u);
+
+failed:
+
+ ngx_http_run_posted_requests(c);
+}
+
+
+static void
+ngx_http_upstream_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *c;
+ ngx_http_request_t *r;
+ ngx_http_log_ctx_t *ctx;
+ ngx_http_upstream_t *u;
+
+ c = ev->data;
+ r = c->data;
+
+ u = r->upstream;
+ c = r->connection;
+
+ ctx = c->log->data;
+ ctx->current_request = r;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream request: \"%V?%V\"", &r->uri, &r->args);
+
+ if (ev->write) {
+ u->write_event_handler(r, u);
+
+ } else {
+ u->read_event_handler(r, u);
+ }
+
+ ngx_http_run_posted_requests(c);
+}
+
+
+static void
+ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r)
+{
+ ngx_http_upstream_check_broken_connection(r, r->connection->read);
+}
+
+
+static void
+ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r)
+{
+ ngx_http_upstream_check_broken_connection(r, r->connection->write);
+}
+
+
+static void
+ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
+ ngx_event_t *ev)
+{
+ int n;
+ char buf[1];
+ ngx_err_t err;
+ ngx_int_t event;
+ ngx_connection_t *c;
+ ngx_http_upstream_t *u;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,
+ "http upstream check client, write event:%d, \"%V\"",
+ ev->write, &r->uri);
+
+ c = r->connection;
+ u = r->upstream;
+
+ if (c->error) {
+ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {
+
+ event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;
+
+ if (ngx_del_event(ev, event, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ if (!u->cacheable) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ }
+
+ return;
+ }
+
+#if (NGX_HTTP_SPDY)
+ if (r->spdy_stream) {
+ return;
+ }
+#endif
+
+#if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+
+ if (!ev->pending_eof) {
+ return;
+ }
+
+ ev->eof = 1;
+ c->error = 1;
+
+ if (ev->kq_errno) {
+ ev->error = 1;
+ }
+
+ if (!u->cacheable && u->peer.connection) {
+ ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,
+ "kevent() reported that client prematurely closed "
+ "connection, so upstream connection is closed too");
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,
+ "kevent() reported that client prematurely closed "
+ "connection");
+
+ if (u->peer.connection == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ }
+
+ return;
+ }
+
+#endif
+
+#if (NGX_HAVE_EPOLLRDHUP)
+
+ if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ev->pending_eof) {
+ socklen_t len;
+
+ ev->eof = 1;
+ c->error = 1;
+
+ err = 0;
+ len = sizeof(ngx_err_t);
+
+ /*
+ * BSDs and Linux return 0 and set a pending error in err
+ * Solaris returns -1 and sets errno
+ */
+
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)
+ == -1)
+ {
+ err = ngx_socket_errno;
+ }
+
+ if (err) {
+ ev->error = 1;
+ }
+
+ if (!u->cacheable && u->peer.connection) {
+ ngx_log_error(NGX_LOG_INFO, ev->log, err,
+ "epoll_wait() reported that client prematurely closed "
+ "connection, so upstream connection is closed too");
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, ev->log, err,
+ "epoll_wait() reported that client prematurely closed "
+ "connection");
+
+ if (u->peer.connection == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ }
+
+ return;
+ }
+
+#endif
+
+ n = recv(c->fd, buf, 1, MSG_PEEK);
+
+ err = ngx_socket_errno;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, err,
+ "http upstream recv(): %d", n);
+
+ if (ev->write && (n >= 0 || err == NGX_EAGAIN)) {
+ return;
+ }
+
+ if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {
+
+ event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;
+
+ if (ngx_del_event(ev, event, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ if (n > 0) {
+ return;
+ }
+
+ if (n == -1) {
+ if (err == NGX_EAGAIN) {
+ return;
+ }
+
+ ev->error = 1;
+
+ } else { /* n == 0 */
+ err = 0;
+ }
+
+ ev->eof = 1;
+ c->error = 1;
+
+ if (!u->cacheable && u->peer.connection) {
+ ngx_log_error(NGX_LOG_INFO, ev->log, err,
+ "client prematurely closed connection, "
+ "so upstream connection is closed too");
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, ev->log, err,
+ "client prematurely closed connection");
+
+ if (u->peer.connection == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ }
+}
+
+
+static void
+ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_int_t rc;
+ ngx_time_t *tp;
+ ngx_connection_t *c;
+
+ r->connection->log->action = "connecting to upstream";
+
+ if (u->state && u->state->response_sec) {
+ tp = ngx_timeofday();
+ u->state->response_sec = tp->sec - u->state->response_sec;
+ u->state->response_msec = tp->msec - u->state->response_msec;
+ }
+
+ u->state = ngx_array_push(r->upstream_states);
+ if (u->state == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));
+
+ tp = ngx_timeofday();
+ u->state->response_sec = tp->sec;
+ u->state->response_msec = tp->msec;
+
+ rc = ngx_event_connect_peer(&u->peer);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream connect: %i", rc);
+
+ if (rc == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ u->state->peer = u->peer.name;
+
+ if (rc == NGX_BUSY) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams");
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE);
+ return;
+ }
+
+ if (rc == NGX_DECLINED) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+ return;
+ }
+
+ /* rc == NGX_OK || rc == NGX_AGAIN || rc == NGX_DONE */
+
+ c = u->peer.connection;
+
+ c->data = r;
+
+ c->write->handler = ngx_http_upstream_handler;
+ c->read->handler = ngx_http_upstream_handler;
+
+ u->write_event_handler = ngx_http_upstream_send_request_handler;
+ u->read_event_handler = ngx_http_upstream_process_header;
+
+ c->sendfile &= r->connection->sendfile;
+ u->output.sendfile = c->sendfile;
+
+ if (c->pool == NULL) {
+
+ /* we need separate pool here to be able to cache SSL connections */
+
+ c->pool = ngx_create_pool(128, r->connection->log);
+ if (c->pool == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ c->log = r->connection->log;
+ c->pool->log = c->log;
+ c->read->log = c->log;
+ c->write->log = c->log;
+
+ /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */
+
+ u->writer.out = NULL;
+ u->writer.last = &u->writer.out;
+ u->writer.connection = c;
+ u->writer.limit = 0;
+
+ if (u->request_sent) {
+ if (ngx_http_upstream_reinit(r, u) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ if (r->request_body
+ && r->request_body->buf
+ && r->request_body->temp_file
+ && r == r->main)
+ {
+ /*
+ * the r->request_body->buf can be reused for one request only,
+ * the subrequests should allocate their own temporary bufs
+ */
+
+ u->output.free = ngx_alloc_chain_link(r->pool);
+ if (u->output.free == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ u->output.free->buf = r->request_body->buf;
+ u->output.free->next = NULL;
+ u->output.allocated = 1;
+
+ r->request_body->buf->pos = r->request_body->buf->start;
+ r->request_body->buf->last = r->request_body->buf->start;
+ r->request_body->buf->tag = u->output.tag;
+ }
+
+ u->request_sent = 0;
+
+ if (rc == NGX_AGAIN) {
+ ngx_add_timer(c->write, u->conf->connect_timeout);
+ return;
+ }
+
+#if (NGX_HTTP_SSL)
+
+ if (u->ssl && c->ssl == NULL) {
+ ngx_http_upstream_ssl_init_connection(r, u, c);
+ return;
+ }
+
+#endif
+
+ ngx_http_upstream_send_request(r, u);
+}
+
+
+#if (NGX_HTTP_SSL)
+
+static void
+ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_connection_t *c)
+{
+ ngx_int_t rc;
+
+ if (ngx_http_upstream_test_connect(c) != NGX_OK) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+ return;
+ }
+
+ if (ngx_ssl_create_connection(u->conf->ssl, c,
+ NGX_SSL_BUFFER|NGX_SSL_CLIENT)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ c->sendfile = 0;
+ u->output.sendfile = 0;
+
+ if (u->conf->ssl_server_name || u->conf->ssl_verify) {
+ if (ngx_http_upstream_ssl_name(r, u, c) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ if (u->conf->ssl_session_reuse) {
+ if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+ }
+
+ r->connection->log->action = "SSL handshaking to upstream";
+
+ rc = ngx_ssl_handshake(c);
+
+ if (rc == NGX_AGAIN) {
+
+ if (!c->write->timer_set) {
+ ngx_add_timer(c->write, u->conf->connect_timeout);
+ }
+
+ c->ssl->handler = ngx_http_upstream_ssl_handshake;
+ return;
+ }
+
+ ngx_http_upstream_ssl_handshake(c);
+}
+
+
+static void
+ngx_http_upstream_ssl_handshake(ngx_connection_t *c)
+{
+ long rc;
+ ngx_http_request_t *r;
+ ngx_http_upstream_t *u;
+
+ r = c->data;
+ u = r->upstream;
+
+ if (c->ssl->handshaked) {
+
+ if (u->conf->ssl_verify) {
+ rc = SSL_get_verify_result(c->ssl->connection);
+
+ if (rc != X509_V_OK) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "upstream SSL certificate verify error: (%l:%s)",
+ rc, X509_verify_cert_error_string(rc));
+ goto failed;
+ }
+
+ if (ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "upstream SSL certificate does not match \"%V\"",
+ &u->ssl_name);
+ goto failed;
+ }
+ }
+
+ if (u->conf->ssl_session_reuse) {
+ u->peer.save_session(&u->peer, u->peer.data);
+ }
+
+ c->write->handler = ngx_http_upstream_handler;
+ c->read->handler = ngx_http_upstream_handler;
+
+ c = r->connection;
+
+ ngx_http_upstream_send_request(r, u);
+
+ ngx_http_run_posted_requests(c);
+ return;
+ }
+
+failed:
+
+ c = r->connection;
+
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+
+ ngx_http_run_posted_requests(c);
+}
+
+
+static ngx_int_t
+ngx_http_upstream_ssl_name(ngx_http_request_t *r, ngx_http_upstream_t *u,
+ ngx_connection_t *c)
+{
+ u_char *p, *last;
+ ngx_str_t name;
+
+ if (u->conf->ssl_name) {
+ if (ngx_http_complex_value(r, u->conf->ssl_name, &name) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ name = u->ssl_name;
+ }
+
+ if (name.len == 0) {
+ goto done;
+ }
+
+ /*
+ * ssl name here may contain port, notably if derived from $proxy_host
+ * or $http_host; we have to strip it
+ */
+
+ p = name.data;
+ last = name.data + name.len;
+
+ if (*p == '[') {
+ p = ngx_strlchr(p, last, ']');
+
+ if (p == NULL) {
+ p = name.data;
+ }
+ }
+
+ p = ngx_strlchr(p, last, ':');
+
+ if (p != NULL) {
+ name.len = p - name.data;
+ }
+
+ if (!u->conf->ssl_server_name) {
+ goto done;
+ }
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+
+ /* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */
+
+ if (name.len == 0 || *name.data == '[') {
+ goto done;
+ }
+
+ if (ngx_inet_addr(name.data, name.len) != INADDR_NONE) {
+ goto done;
+ }
+
+ /*
+ * SSL_set_tlsext_host_name() needs a null-terminated string,
+ * hence we explicitly null-terminate name here
+ */
+
+ p = ngx_pnalloc(r->pool, name.len + 1);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_cpystrn(p, name.data, name.len + 1);
+
+ name.data = p;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "upstream SSL server name: \"%s\"", name.data);
+
+ if (SSL_set_tlsext_host_name(c->ssl->connection, name.data) == 0) {
+ ngx_ssl_error(NGX_LOG_ERR, r->connection->log, 0,
+ "SSL_set_tlsext_host_name(\"%s\") failed", name.data);
+ return NGX_ERROR;
+ }
+
+#endif
+
+done:
+
+ u->ssl_name = name;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_chain_t *cl;
+
+ if (u->reinit_request(r) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ u->keepalive = 0;
+ u->upgrade = 0;
+
+ ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
+ u->headers_in.content_length_n = -1;
+
+ if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ /* reinit the request chain */
+
+ for (cl = u->request_bufs; cl; cl = cl->next) {
+ cl->buf->pos = cl->buf->start;
+ cl->buf->file_pos = 0;
+ }
+
+ /* reinit the subrequest's ngx_output_chain() context */
+
+ if (r->request_body && r->request_body->temp_file
+ && r != r->main && u->output.buf)
+ {
+ u->output.free = ngx_alloc_chain_link(r->pool);
+ if (u->output.free == NULL) {
+ return NGX_ERROR;
+ }
+
+ u->output.free->buf = u->output.buf;
+ u->output.free->next = NULL;
+
+ u->output.buf->pos = u->output.buf->start;
+ u->output.buf->last = u->output.buf->start;
+ }
+
+ u->output.buf = NULL;
+ u->output.in = NULL;
+ u->output.busy = NULL;
+
+ /* reinit u->buffer */
+
+ u->buffer.pos = u->buffer.start;
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache) {
+ u->buffer.pos += r->cache->header_start;
+ }
+
+#endif
+
+ u->buffer.last = u->buffer.pos;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+
+ c = u->peer.connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream send request");
+
+ if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+ return;
+ }
+
+ c->log->action = "sending request to upstream";
+
+ rc = ngx_output_chain(&u->output, u->request_sent ? NULL : u->request_bufs);
+
+ u->request_sent = 1;
+
+ if (rc == NGX_ERROR) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+ return;
+ }
+
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ if (rc == NGX_AGAIN) {
+ ngx_add_timer(c->write, u->conf->send_timeout);
+
+ if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ return;
+ }
+
+ /* rc == NGX_OK */
+
+ if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {
+ if (ngx_tcp_push(c->fd) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,
+ ngx_tcp_push_n " failed");
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;
+ }
+
+ u->write_event_handler = ngx_http_upstream_dummy_handler;
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ ngx_add_timer(c->read, u->conf->read_timeout);
+
+ if (c->read->ready) {
+ ngx_http_upstream_process_header(r, u);
+ return;
+ }
+}
+
+
+static void
+ngx_http_upstream_send_request_handler(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_connection_t *c;
+
+ c = u->peer.connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream send request handler");
+
+ if (c->write->timedout) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
+ return;
+ }
+
+#if (NGX_HTTP_SSL)
+
+ if (u->ssl && c->ssl == NULL) {
+ ngx_http_upstream_ssl_init_connection(r, u, c);
+ return;
+ }
+
+#endif
+
+ if (u->header_sent) {
+ u->write_event_handler = ngx_http_upstream_dummy_handler;
+
+ (void) ngx_handle_write_event(c->write, 0);
+
+ return;
+ }
+
+ ngx_http_upstream_send_request(r, u);
+}
+
+
+static void
+ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_connection_t *c;
+
+ c = u->peer.connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process header");
+
+ c->log->action = "reading response header from upstream";
+
+ if (c->read->timedout) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);
+ return;
+ }
+
+ if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+ return;
+ }
+
+ if (u->buffer.start == NULL) {
+ u->buffer.start = ngx_palloc(r->pool, u->conf->buffer_size);
+ if (u->buffer.start == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ u->buffer.pos = u->buffer.start;
+ u->buffer.last = u->buffer.start;
+ u->buffer.end = u->buffer.start + u->conf->buffer_size;
+ u->buffer.temporary = 1;
+
+ u->buffer.tag = u->output.tag;
+
+ if (ngx_list_init(&u->headers_in.headers, r->pool, 8,
+ sizeof(ngx_table_elt_t))
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache) {
+ u->buffer.pos += r->cache->header_start;
+ u->buffer.last = u->buffer.pos;
+ }
+#endif
+ }
+
+ for ( ;; ) {
+
+ n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last);
+
+ if (n == NGX_AGAIN) {
+#if 0
+ ngx_add_timer(rev, u->read_timeout);
+#endif
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ return;
+ }
+
+ if (n == 0) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "upstream prematurely closed connection");
+ }
+
+ if (n == NGX_ERROR || n == 0) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);
+ return;
+ }
+
+ u->buffer.last += n;
+
+#if 0
+ u->valid_header_in = 0;
+
+ u->peer.cached = 0;
+#endif
+
+ rc = u->process_header(r);
+
+ if (rc == NGX_AGAIN) {
+
+ if (u->buffer.last == u->buffer.end) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "upstream sent too big header");
+
+ ngx_http_upstream_next(r, u,
+ NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);
+ return;
+ }
+
+ continue;
+ }
+
+ break;
+ }
+
+ if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {
+ ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);
+ return;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ /* rc == NGX_OK */
+
+ if (u->headers_in.status_n >= NGX_HTTP_SPECIAL_RESPONSE) {
+
+ if (ngx_http_upstream_test_next(r, u) == NGX_OK) {
+ return;
+ }
+
+ if (ngx_http_upstream_intercept_errors(r, u) == NGX_OK) {
+ return;
+ }
+ }
+
+ if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {
+ return;
+ }
+
+ if (!r->subrequest_in_memory) {
+ ngx_http_upstream_send_response(r, u);
+ return;
+ }
+
+ /* subrequest content in memory */
+
+ if (u->input_filter == NULL) {
+ u->input_filter_init = ngx_http_upstream_non_buffered_filter_init;
+ u->input_filter = ngx_http_upstream_non_buffered_filter;
+ u->input_filter_ctx = r;
+ }
+
+ if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ n = u->buffer.last - u->buffer.pos;
+
+ if (n) {
+ u->buffer.last = u->buffer.pos;
+
+ u->state->response_length += n;
+
+ if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+ }
+
+ if (u->length == 0) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ u->read_event_handler = ngx_http_upstream_process_body_in_memory;
+
+ ngx_http_upstream_process_body_in_memory(r, u);
+}
+
+
+static ngx_int_t
+ngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_uint_t status;
+ ngx_http_upstream_next_t *un;
+
+ status = u->headers_in.status_n;
+
+ for (un = ngx_http_upstream_next_errors; un->status; un++) {
+
+ if (status != un->status) {
+ continue;
+ }
+
+ if (u->peer.tries > 1 && (u->conf->next_upstream & un->mask)) {
+ ngx_http_upstream_next(r, u, un->mask);
+ return NGX_OK;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (u->cache_status == NGX_HTTP_CACHE_EXPIRED
+ && (u->conf->cache_use_stale & un->mask))
+ {
+ ngx_int_t rc;
+
+ rc = u->reinit_request(r);
+
+ if (rc == NGX_OK) {
+ u->cache_status = NGX_HTTP_CACHE_STALE;
+ rc = ngx_http_upstream_cache_send(r, u);
+ }
+
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return NGX_OK;
+ }
+
+#endif
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (status == NGX_HTTP_NOT_MODIFIED
+ && u->cache_status == NGX_HTTP_CACHE_EXPIRED
+ && u->conf->cache_revalidate)
+ {
+ time_t now, valid;
+ ngx_int_t rc;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream not modified");
+
+ now = ngx_time();
+ valid = r->cache->valid_sec;
+
+ rc = u->reinit_request(r);
+
+ if (rc != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return NGX_OK;
+ }
+
+ u->cache_status = NGX_HTTP_CACHE_REVALIDATED;
+ rc = ngx_http_upstream_cache_send(r, u);
+
+ if (valid == 0) {
+ valid = r->cache->valid_sec;
+ }
+
+ if (valid == 0) {
+ valid = ngx_http_file_cache_valid(u->conf->cache_valid,
+ u->headers_in.status_n);
+ if (valid) {
+ valid = now + valid;
+ }
+ }
+
+ if (valid) {
+ r->cache->valid_sec = valid;
+ r->cache->date = now;
+
+ ngx_http_file_cache_update_header(r);
+ }
+
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return NGX_OK;
+ }
+
+#endif
+
+ return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_intercept_errors(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_int_t status;
+ ngx_uint_t i;
+ ngx_table_elt_t *h;
+ ngx_http_err_page_t *err_page;
+ ngx_http_core_loc_conf_t *clcf;
+
+ status = u->headers_in.status_n;
+
+ if (status == NGX_HTTP_NOT_FOUND && u->conf->intercept_404) {
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_NOT_FOUND);
+ return NGX_OK;
+ }
+
+ if (!u->conf->intercept_errors) {
+ return NGX_DECLINED;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->error_pages == NULL) {
+ return NGX_DECLINED;
+ }
+
+ err_page = clcf->error_pages->elts;
+ for (i = 0; i < clcf->error_pages->nelts; i++) {
+
+ if (err_page[i].status == status) {
+
+ if (status == NGX_HTTP_UNAUTHORIZED
+ && u->headers_in.www_authenticate)
+ {
+ h = ngx_list_push(&r->headers_out.headers);
+
+ if (h == NULL) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_OK;
+ }
+
+ *h = *u->headers_in.www_authenticate;
+
+ r->headers_out.www_authenticate = h;
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache) {
+ time_t valid;
+
+ valid = ngx_http_file_cache_valid(u->conf->cache_valid, status);
+
+ if (valid) {
+ r->cache->valid_sec = ngx_time() + valid;
+ r->cache->error = status;
+ }
+
+ ngx_http_file_cache_free(r->cache, u->pipe->temp_file);
+ }
+#endif
+ ngx_http_upstream_finalize_request(r, u, status);
+
+ return NGX_OK;
+ }
+ }
+
+ return NGX_DECLINED;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_test_connect(ngx_connection_t *c)
+{
+ int err;
+ socklen_t len;
+
+#if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ if (c->write->pending_eof || c->read->pending_eof) {
+ if (c->write->pending_eof) {
+ err = c->write->kq_errno;
+
+ } else {
+ err = c->read->kq_errno;
+ }
+
+ c->log->action = "connecting to upstream";
+ (void) ngx_connection_error(c, err,
+ "kevent() reported that connect() failed");
+ return NGX_ERROR;
+ }
+
+ } else
+#endif
+ {
+ err = 0;
+ len = sizeof(int);
+
+ /*
+ * BSDs and Linux return 0 and set a pending error in err
+ * Solaris returns -1 and sets errno
+ */
+
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)
+ == -1)
+ {
+ err = ngx_socket_errno;
+ }
+
+ if (err) {
+ c->log->action = "connecting to upstream";
+ (void) ngx_connection_error(c, err, "connect() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_str_t uri, args;
+ ngx_uint_t i, flags;
+ ngx_list_part_t *part;
+ ngx_table_elt_t *h;
+ ngx_http_upstream_header_t *hh;
+ ngx_http_upstream_main_conf_t *umcf;
+
+ umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
+
+ if (u->headers_in.x_accel_redirect
+ && !(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT))
+ {
+ ngx_http_upstream_finalize_request(r, u, NGX_DECLINED);
+
+ part = &u->headers_in.headers.part;
+ h = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ h = part->elts;
+ i = 0;
+ }
+
+ hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
+ h[i].lowcase_key, h[i].key.len);
+
+ if (hh && hh->redirect) {
+ if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
+ ngx_http_finalize_request(r,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_DONE;
+ }
+ }
+ }
+
+ uri = u->headers_in.x_accel_redirect->value;
+ ngx_str_null(&args);
+ flags = NGX_HTTP_LOG_UNSAFE;
+
+ if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {
+ ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
+ return NGX_DONE;
+ }
+
+ if (r->method != NGX_HTTP_HEAD) {
+ r->method = NGX_HTTP_GET;
+ }
+
+ ngx_http_internal_redirect(r, &uri, &args);
+ ngx_http_finalize_request(r, NGX_DONE);
+ return NGX_DONE;
+ }
+
+ part = &u->headers_in.headers.part;
+ h = part->elts;
+
+ for (i = 0; /* void */; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ h = part->elts;
+ i = 0;
+ }
+
+ if (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,
+ h[i].lowcase_key, h[i].key.len))
+ {
+ continue;
+ }
+
+ hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,
+ h[i].lowcase_key, h[i].key.len);
+
+ if (hh) {
+ if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_DONE;
+ }
+
+ continue;
+ }
+
+ if (ngx_http_upstream_copy_header_line(r, &h[i], 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return NGX_DONE;
+ }
+ }
+
+ if (r->headers_out.server && r->headers_out.server->value.data == NULL) {
+ r->headers_out.server->hash = 0;
+ }
+
+ if (r->headers_out.date && r->headers_out.date->value.data == NULL) {
+ r->headers_out.date->hash = 0;
+ }
+
+ r->headers_out.status = u->headers_in.status_n;
+ r->headers_out.status_line = u->headers_in.status_line;
+
+ r->headers_out.content_length_n = u->headers_in.content_length_n;
+
+ r->disable_not_modified = !u->cacheable;
+
+ u->length = -1;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_event_t *rev;
+ ngx_connection_t *c;
+
+ c = u->peer.connection;
+ rev = c->read;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process body on memory");
+
+ if (rev->timedout) {
+ ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT);
+ return;
+ }
+
+ b = &u->buffer;
+
+ for ( ;; ) {
+
+ size = b->end - b->last;
+
+ if (size == 0) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "upstream buffer is too small to read response");
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ n = c->recv(c, b->last, size);
+
+ if (n == NGX_AGAIN) {
+ break;
+ }
+
+ if (n == 0 || n == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, n);
+ return;
+ }
+
+ u->state->response_length += n;
+
+ if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (!rev->ready) {
+ break;
+ }
+ }
+
+ if (u->length == 0) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (rev->active) {
+ ngx_add_timer(rev, u->conf->read_timeout);
+
+ } else if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+}
+
+
+static void
+ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ int tcp_nodelay;
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_event_pipe_t *p;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ rc = ngx_http_send_header(r);
+
+ if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) {
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return;
+ }
+
+ u->header_sent = 1;
+
+ if (u->upgrade) {
+ ngx_http_upstream_upgrade(r, u);
+ return;
+ }
+
+ c = r->connection;
+
+ if (r->header_only) {
+
+ if (!u->buffering) {
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return;
+ }
+
+ if (!u->cacheable && !u->store) {
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return;
+ }
+
+ u->pipe->downstream_error = 1;
+ }
+
+ if (r->request_body && r->request_body->temp_file) {
+ ngx_pool_run_cleanup_file(r->pool, r->request_body->temp_file->file.fd);
+ r->request_body->temp_file->file.fd = NGX_INVALID_FILE;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (!u->buffering) {
+
+ if (u->input_filter == NULL) {
+ u->input_filter_init = ngx_http_upstream_non_buffered_filter_init;
+ u->input_filter = ngx_http_upstream_non_buffered_filter;
+ u->input_filter_ctx = r;
+ }
+
+ u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream;
+ r->write_event_handler =
+ ngx_http_upstream_process_non_buffered_downstream;
+
+ r->limit_rate = 0;
+
+ if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay");
+
+ tcp_nodelay = 1;
+
+ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &tcp_nodelay, sizeof(int)) == -1)
+ {
+ ngx_connection_error(c, ngx_socket_errno,
+ "setsockopt(TCP_NODELAY) failed");
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ c->tcp_nodelay = NGX_TCP_NODELAY_SET;
+ }
+
+ n = u->buffer.last - u->buffer.pos;
+
+ if (n) {
+ u->buffer.last = u->buffer.pos;
+
+ u->state->response_length += n;
+
+ if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ ngx_http_upstream_process_non_buffered_downstream(r);
+
+ } else {
+ u->buffer.pos = u->buffer.start;
+ u->buffer.last = u->buffer.start;
+
+ if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (u->peer.connection->read->ready || u->length == 0) {
+ ngx_http_upstream_process_non_buffered_upstream(r, u);
+ }
+ }
+
+ return;
+ }
+
+ /* TODO: preallocate event_pipe bufs, look "Content-Length" */
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache && r->cache->file.fd != NGX_INVALID_FILE) {
+ ngx_pool_run_cleanup_file(r->pool, r->cache->file.fd);
+ r->cache->file.fd = NGX_INVALID_FILE;
+ }
+
+ switch (ngx_http_test_predicates(r, u->conf->no_cache)) {
+
+ case NGX_ERROR:
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+
+ case NGX_DECLINED:
+ u->cacheable = 0;
+ break;
+
+ default: /* NGX_OK */
+
+ if (u->cache_status == NGX_HTTP_CACHE_BYPASS) {
+
+ r->cache->min_uses = u->conf->cache_min_uses;
+ r->cache->body_start = u->conf->buffer_size;
+ r->cache->file_cache = u->conf->cache->data;
+
+ if (ngx_http_file_cache_create(r) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+ }
+
+ break;
+ }
+
+ if (u->cacheable) {
+ time_t now, valid;
+
+ now = ngx_time();
+
+ valid = r->cache->valid_sec;
+
+ if (valid == 0) {
+ valid = ngx_http_file_cache_valid(u->conf->cache_valid,
+ u->headers_in.status_n);
+ if (valid) {
+ r->cache->valid_sec = now + valid;
+ }
+ }
+
+ if (valid) {
+ r->cache->last_modified = u->headers_in.last_modified_time;
+ r->cache->date = now;
+ r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start);
+
+ if (u->headers_in.etag) {
+ r->cache->etag = u->headers_in.etag->value;
+ }
+
+ ngx_http_file_cache_set_header(r, u->buffer.start);
+
+ } else {
+ u->cacheable = 0;
+ }
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http cacheable: %d", u->cacheable);
+
+ if (u->cacheable == 0 && r->cache) {
+ ngx_http_file_cache_free(r->cache, u->pipe->temp_file);
+ }
+
+#endif
+
+ p = u->pipe;
+
+ p->output_filter = (ngx_event_pipe_output_filter_pt) ngx_http_output_filter;
+ p->output_ctx = r;
+ p->tag = u->output.tag;
+ p->bufs = u->conf->bufs;
+ p->busy_size = u->conf->busy_buffers_size;
+ p->upstream = u->peer.connection;
+ p->downstream = c;
+ p->pool = r->pool;
+ p->log = c->log;
+
+ p->cacheable = u->cacheable || u->store;
+
+ p->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
+ if (p->temp_file == NULL) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ p->temp_file->file.fd = NGX_INVALID_FILE;
+ p->temp_file->file.log = c->log;
+ p->temp_file->path = u->conf->temp_path;
+ p->temp_file->pool = r->pool;
+
+ if (p->cacheable) {
+ p->temp_file->persistent = 1;
+
+ } else {
+ p->temp_file->log_level = NGX_LOG_WARN;
+ p->temp_file->warn = "an upstream response is buffered "
+ "to a temporary file";
+ }
+
+ p->max_temp_file_size = u->conf->max_temp_file_size;
+ p->temp_file_write_size = u->conf->temp_file_write_size;
+
+ p->preread_bufs = ngx_alloc_chain_link(r->pool);
+ if (p->preread_bufs == NULL) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ p->preread_bufs->buf = &u->buffer;
+ p->preread_bufs->next = NULL;
+ u->buffer.recycled = 1;
+
+ p->preread_size = u->buffer.last - u->buffer.pos;
+
+ if (u->cacheable) {
+
+ p->buf_to_file = ngx_calloc_buf(r->pool);
+ if (p->buf_to_file == NULL) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ p->buf_to_file->start = u->buffer.start;
+ p->buf_to_file->pos = u->buffer.start;
+ p->buf_to_file->last = u->buffer.pos;
+ p->buf_to_file->temporary = 1;
+ }
+
+ if (ngx_event_flags & NGX_USE_AIO_EVENT) {
+ /* the posted aio operation may corrupt a shadow buffer */
+ p->single_buf = 1;
+ }
+
+ /* TODO: p->free_bufs = 0 if use ngx_create_chain_of_bufs() */
+ p->free_bufs = 1;
+
+ /*
+ * event_pipe would do u->buffer.last += p->preread_size
+ * as though these bytes were read
+ */
+ u->buffer.last = u->buffer.pos;
+
+ if (u->conf->cyclic_temp_file) {
+
+ /*
+ * we need to disable the use of sendfile() if we use cyclic temp file
+ * because the writing a new data may interfere with sendfile()
+ * that uses the same kernel file pages (at least on FreeBSD)
+ */
+
+ p->cyclic_temp_file = 1;
+ c->sendfile = 0;
+
+ } else {
+ p->cyclic_temp_file = 0;
+ }
+
+ p->read_timeout = u->conf->read_timeout;
+ p->send_timeout = clcf->send_timeout;
+ p->send_lowat = clcf->send_lowat;
+
+ p->length = -1;
+
+ if (u->input_filter_init
+ && u->input_filter_init(p->input_ctx) != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ u->read_event_handler = ngx_http_upstream_process_upstream;
+ r->write_event_handler = ngx_http_upstream_process_downstream;
+
+ ngx_http_upstream_process_upstream(r, u);
+}
+
+
+static void
+ngx_http_upstream_upgrade(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ int tcp_nodelay;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ /* TODO: prevent upgrade if not requested or not possible */
+
+ r->keepalive = 0;
+ c->log->action = "proxying upgraded connection";
+
+ u->read_event_handler = ngx_http_upstream_upgraded_read_upstream;
+ u->write_event_handler = ngx_http_upstream_upgraded_write_upstream;
+ r->read_event_handler = ngx_http_upstream_upgraded_read_downstream;
+ r->write_event_handler = ngx_http_upstream_upgraded_write_downstream;
+
+ if (clcf->tcp_nodelay) {
+ tcp_nodelay = 1;
+
+ if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay");
+
+ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &tcp_nodelay, sizeof(int)) == -1)
+ {
+ ngx_connection_error(c, ngx_socket_errno,
+ "setsockopt(TCP_NODELAY) failed");
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ c->tcp_nodelay = NGX_TCP_NODELAY_SET;
+ }
+
+ if (u->peer.connection->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, u->peer.connection->log, 0,
+ "tcp_nodelay");
+
+ if (setsockopt(u->peer.connection->fd, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &tcp_nodelay, sizeof(int)) == -1)
+ {
+ ngx_connection_error(u->peer.connection, ngx_socket_errno,
+ "setsockopt(TCP_NODELAY) failed");
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ u->peer.connection->tcp_nodelay = NGX_TCP_NODELAY_SET;
+ }
+ }
+
+ if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (u->peer.connection->read->ready
+ || u->buffer.pos != u->buffer.last)
+ {
+ ngx_post_event(c->read, &ngx_posted_events);
+ ngx_http_upstream_process_upgraded(r, 1, 1);
+ return;
+ }
+
+ ngx_http_upstream_process_upgraded(r, 0, 1);
+}
+
+
+static void
+ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r)
+{
+ ngx_http_upstream_process_upgraded(r, 0, 0);
+}
+
+
+static void
+ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r)
+{
+ ngx_http_upstream_process_upgraded(r, 1, 1);
+}
+
+
+static void
+ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_http_upstream_process_upgraded(r, 1, 0);
+}
+
+
+static void
+ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_http_upstream_process_upgraded(r, 0, 1);
+}
+
+
+static void
+ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
+ ngx_uint_t from_upstream, ngx_uint_t do_write)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_connection_t *c, *downstream, *upstream, *dst, *src;
+ ngx_http_upstream_t *u;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ u = r->upstream;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process upgraded, fu:%ui", from_upstream);
+
+ downstream = c;
+ upstream = u->peer.connection;
+
+ if (downstream->write->timedout) {
+ c->timedout = 1;
+ ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ if (upstream->read->timedout || upstream->write->timedout) {
+ ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT);
+ return;
+ }
+
+ if (from_upstream) {
+ src = upstream;
+ dst = downstream;
+ b = &u->buffer;
+
+ } else {
+ src = downstream;
+ dst = upstream;
+ b = &u->from_client;
+
+ if (r->header_in->last > r->header_in->pos) {
+ b = r->header_in;
+ b->end = b->last;
+ do_write = 1;
+ }
+
+ if (b->start == NULL) {
+ b->start = ngx_palloc(r->pool, u->conf->buffer_size);
+ if (b->start == NULL) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ b->pos = b->start;
+ b->last = b->start;
+ b->end = b->start + u->conf->buffer_size;
+ b->temporary = 1;
+ b->tag = u->output.tag;
+ }
+ }
+
+ for ( ;; ) {
+
+ if (do_write) {
+
+ size = b->last - b->pos;
+
+ if (size && dst->write->ready) {
+
+ n = dst->send(dst, b->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (n > 0) {
+ b->pos += n;
+
+ if (b->pos == b->last) {
+ b->pos = b->start;
+ b->last = b->start;
+ }
+ }
+ }
+ }
+
+ size = b->end - b->last;
+
+ if (size && src->read->ready) {
+
+ n = src->recv(src, b->last, size);
+
+ if (n == NGX_AGAIN || n == 0) {
+ break;
+ }
+
+ if (n > 0) {
+ do_write = 1;
+ b->last += n;
+
+ continue;
+ }
+
+ if (n == NGX_ERROR) {
+ src->read->eof = 1;
+ }
+ }
+
+ break;
+ }
+
+ if ((upstream->read->eof && u->buffer.pos == u->buffer.last)
+ || (downstream->read->eof && u->from_client.pos == u->from_client.last)
+ || (downstream->read->eof && upstream->read->eof))
+ {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream upgraded done");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (ngx_handle_write_event(upstream->write, u->conf->send_lowat)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (upstream->write->active && !upstream->write->ready) {
+ ngx_add_timer(upstream->write, u->conf->send_timeout);
+
+ } else if (upstream->write->timer_set) {
+ ngx_del_timer(upstream->write);
+ }
+
+ if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (upstream->read->active && !upstream->read->ready) {
+ ngx_add_timer(upstream->read, u->conf->read_timeout);
+
+ } else if (upstream->read->timer_set) {
+ ngx_del_timer(upstream->read);
+ }
+
+ if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (ngx_handle_read_event(downstream->read, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (downstream->write->active && !downstream->write->ready) {
+ ngx_add_timer(downstream->write, clcf->send_timeout);
+
+ } else if (downstream->write->timer_set) {
+ ngx_del_timer(downstream->write);
+ }
+}
+
+
+static void
+ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r)
+{
+ ngx_event_t *wev;
+ ngx_connection_t *c;
+ ngx_http_upstream_t *u;
+
+ c = r->connection;
+ u = r->upstream;
+ wev = c->write;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process non buffered downstream");
+
+ c->log->action = "sending to client";
+
+ if (wev->timedout) {
+ c->timedout = 1;
+ ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ ngx_http_upstream_process_non_buffered_request(r, 1);
+}
+
+
+static void
+ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_connection_t *c;
+
+ c = u->peer.connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process non buffered upstream");
+
+ c->log->action = "reading upstream";
+
+ if (c->read->timedout) {
+ ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT);
+ return;
+ }
+
+ ngx_http_upstream_process_non_buffered_request(r, 0);
+}
+
+
+static void
+ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
+ ngx_uint_t do_write)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_int_t rc;
+ ngx_connection_t *downstream, *upstream;
+ ngx_http_upstream_t *u;
+ ngx_http_core_loc_conf_t *clcf;
+
+ u = r->upstream;
+ downstream = r->connection;
+ upstream = u->peer.connection;
+
+ b = &u->buffer;
+
+ do_write = do_write || u->length == 0;
+
+ for ( ;; ) {
+
+ if (do_write) {
+
+ if (u->out_bufs || u->busy_bufs) {
+ rc = ngx_http_output_filter(r, u->out_bufs);
+
+ if (rc == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ ngx_chain_update_chains(r->pool, &u->free_bufs, &u->busy_bufs,
+ &u->out_bufs, u->output.tag);
+ }
+
+ if (u->busy_bufs == NULL) {
+
+ if (u->length == 0
+ || (upstream->read->eof && u->length == -1))
+ {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (upstream->read->eof) {
+ ngx_log_error(NGX_LOG_ERR, upstream->log, 0,
+ "upstream prematurely closed connection");
+
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_BAD_GATEWAY);
+ return;
+ }
+
+ if (upstream->read->error) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_BAD_GATEWAY);
+ return;
+ }
+
+ b->pos = b->start;
+ b->last = b->start;
+ }
+ }
+
+ size = b->end - b->last;
+
+ if (size && upstream->read->ready) {
+
+ n = upstream->recv(upstream, b->last, size);
+
+ if (n == NGX_AGAIN) {
+ break;
+ }
+
+ if (n > 0) {
+ u->state->response_length += n;
+
+ if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+ }
+
+ do_write = 1;
+
+ continue;
+ }
+
+ break;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (downstream->data == r) {
+ if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+ }
+
+ if (downstream->write->active && !downstream->write->ready) {
+ ngx_add_timer(downstream->write, clcf->send_timeout);
+
+ } else if (downstream->write->timer_set) {
+ ngx_del_timer(downstream->write);
+ }
+
+ if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ if (upstream->read->active && !upstream->read->ready) {
+ ngx_add_timer(upstream->read, u->conf->read_timeout);
+
+ } else if (upstream->read->timer_set) {
+ ngx_del_timer(upstream->read);
+ }
+}
+
+
+static ngx_int_t
+ngx_http_upstream_non_buffered_filter_init(void *data)
+{
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes)
+{
+ ngx_http_request_t *r = data;
+
+ ngx_buf_t *b;
+ ngx_chain_t *cl, **ll;
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+
+ for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
+ ll = &cl->next;
+ }
+
+ cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ll = cl;
+
+ cl->buf->flush = 1;
+ cl->buf->memory = 1;
+
+ b = &u->buffer;
+
+ cl->buf->pos = b->last;
+ b->last += bytes;
+ cl->buf->last = b->last;
+ cl->buf->tag = u->output.tag;
+
+ if (u->length == -1) {
+ return NGX_OK;
+ }
+
+ u->length -= bytes;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_process_downstream(ngx_http_request_t *r)
+{
+ ngx_event_t *wev;
+ ngx_connection_t *c;
+ ngx_event_pipe_t *p;
+ ngx_http_upstream_t *u;
+
+ c = r->connection;
+ u = r->upstream;
+ p = u->pipe;
+ wev = c->write;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process downstream");
+
+ c->log->action = "sending to client";
+
+ if (wev->timedout) {
+
+ if (wev->delayed) {
+
+ wev->timedout = 0;
+ wev->delayed = 0;
+
+ if (!wev->ready) {
+ ngx_add_timer(wev, p->send_timeout);
+
+ if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ }
+
+ return;
+ }
+
+ if (ngx_event_pipe(p, wev->write) == NGX_ABORT) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+
+ } else {
+ p->downstream_error = 1;
+ c->timedout = 1;
+ ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
+ }
+
+ } else {
+
+ if (wev->delayed) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http downstream delayed");
+
+ if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ }
+
+ return;
+ }
+
+ if (ngx_event_pipe(p, 1) == NGX_ABORT) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+ }
+
+ ngx_http_upstream_process_request(r);
+}
+
+
+static void
+ngx_http_upstream_process_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_connection_t *c;
+
+ c = u->peer.connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process upstream");
+
+ c->log->action = "reading upstream";
+
+ if (c->read->timedout) {
+ u->pipe->upstream_error = 1;
+ ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+
+ } else {
+ if (ngx_event_pipe(u->pipe, 0) == NGX_ABORT) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ return;
+ }
+ }
+
+ ngx_http_upstream_process_request(r);
+}
+
+
+static void
+ngx_http_upstream_process_request(ngx_http_request_t *r)
+{
+ ngx_temp_file_t *tf;
+ ngx_event_pipe_t *p;
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+ p = u->pipe;
+
+ if (u->peer.connection) {
+
+ if (u->store) {
+
+ if (p->upstream_eof || p->upstream_done) {
+
+ tf = p->temp_file;
+
+ if (u->headers_in.status_n == NGX_HTTP_OK
+ && (p->upstream_done || p->length == -1)
+ && (u->headers_in.content_length_n == -1
+ || u->headers_in.content_length_n == tf->offset))
+ {
+ ngx_http_upstream_store(r, u);
+ }
+ }
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (u->cacheable) {
+
+ if (p->upstream_done) {
+ ngx_http_file_cache_update(r, p->temp_file);
+
+ } else if (p->upstream_eof) {
+
+ tf = p->temp_file;
+
+ if (p->length == -1
+ && (u->headers_in.content_length_n == -1
+ || u->headers_in.content_length_n
+ == tf->offset - (off_t) r->cache->body_start))
+ {
+ ngx_http_file_cache_update(r, tf);
+
+ } else {
+ ngx_http_file_cache_free(r->cache, tf);
+ }
+
+ } else if (p->upstream_error) {
+ ngx_http_file_cache_free(r->cache, p->temp_file);
+ }
+ }
+
+#endif
+
+ if (p->upstream_done || p->upstream_eof || p->upstream_error) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream exit: %p", p->out);
+
+ if (p->upstream_done
+ || (p->upstream_eof && p->length == -1))
+ {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (p->upstream_eof) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "upstream prematurely closed connection");
+ }
+
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);
+ return;
+ }
+ }
+
+ if (p->downstream_error) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream downstream error");
+
+ if (!u->cacheable && !u->store && u->peer.connection) {
+ ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+ }
+ }
+}
+
+
+static void
+ngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ size_t root;
+ time_t lm;
+ ngx_str_t path;
+ ngx_temp_file_t *tf;
+ ngx_ext_rename_file_t ext;
+
+ tf = u->pipe->temp_file;
+
+ if (tf->file.fd == NGX_INVALID_FILE) {
+
+ /* create file for empty 200 response */
+
+ tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
+ if (tf == NULL) {
+ return;
+ }
+
+ tf->file.fd = NGX_INVALID_FILE;
+ tf->file.log = r->connection->log;
+ tf->path = u->conf->temp_path;
+ tf->pool = r->pool;
+ tf->persistent = 1;
+
+ if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
+ tf->persistent, tf->clean, tf->access)
+ != NGX_OK)
+ {
+ return;
+ }
+
+ u->pipe->temp_file = tf;
+ }
+
+ ext.access = u->conf->store_access;
+ ext.path_access = u->conf->store_access;
+ ext.time = -1;
+ ext.create_path = 1;
+ ext.delete_file = 1;
+ ext.log = r->connection->log;
+
+ if (u->headers_in.last_modified) {
+
+ lm = ngx_http_parse_time(u->headers_in.last_modified->value.data,
+ u->headers_in.last_modified->value.len);
+
+ if (lm != NGX_ERROR) {
+ ext.time = lm;
+ ext.fd = tf->file.fd;
+ }
+ }
+
+ if (u->conf->store_lengths == NULL) {
+
+ if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
+ return;
+ }
+
+ } else {
+ if (ngx_http_script_run(r, &path, u->conf->store_lengths->elts, 0,
+ u->conf->store_values->elts)
+ == NULL)
+ {
+ return;
+ }
+ }
+
+ path.len--;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "upstream stores \"%s\" to \"%s\"",
+ tf->file.name.data, path.data);
+
+ (void) ngx_ext_rename_file(&tf->file.name, &path, &ext);
+
+ u->store = 0;
+}
+
+
+static void
+ngx_http_upstream_dummy_handler(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream dummy handler");
+}
+
+
+static void
+ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u,
+ ngx_uint_t ft_type)
+{
+ ngx_uint_t status, state;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http next upstream, %xi", ft_type);
+
+ if (u->peer.sockaddr) {
+
+ if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_403
+ || ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404)
+ {
+ state = NGX_PEER_NEXT;
+
+ } else {
+ state = NGX_PEER_FAILED;
+ }
+
+ u->peer.free(&u->peer, u->peer.data, state);
+ u->peer.sockaddr = NULL;
+ }
+
+ if (ft_type == NGX_HTTP_UPSTREAM_FT_TIMEOUT) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ }
+
+ if (u->peer.cached && ft_type == NGX_HTTP_UPSTREAM_FT_ERROR) {
+ status = 0;
+
+ /* TODO: inform balancer instead */
+
+ u->peer.tries++;
+
+ } else {
+ switch (ft_type) {
+
+ case NGX_HTTP_UPSTREAM_FT_TIMEOUT:
+ status = NGX_HTTP_GATEWAY_TIME_OUT;
+ break;
+
+ case NGX_HTTP_UPSTREAM_FT_HTTP_500:
+ status = NGX_HTTP_INTERNAL_SERVER_ERROR;
+ break;
+
+ case NGX_HTTP_UPSTREAM_FT_HTTP_403:
+ status = NGX_HTTP_FORBIDDEN;
+ break;
+
+ case NGX_HTTP_UPSTREAM_FT_HTTP_404:
+ status = NGX_HTTP_NOT_FOUND;
+ break;
+
+ /*
+ * NGX_HTTP_UPSTREAM_FT_BUSY_LOCK and NGX_HTTP_UPSTREAM_FT_MAX_WAITING
+ * never reach here
+ */
+
+ default:
+ status = NGX_HTTP_BAD_GATEWAY;
+ }
+ }
+
+ if (r->connection->error) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
+ if (status) {
+ u->state->status = status;
+
+ if (u->peer.tries == 0 || !(u->conf->next_upstream & ft_type)) {
+
+#if (NGX_HTTP_CACHE)
+
+ if (u->cache_status == NGX_HTTP_CACHE_EXPIRED
+ && (u->conf->cache_use_stale & ft_type))
+ {
+ ngx_int_t rc;
+
+ rc = u->reinit_request(r);
+
+ if (rc == NGX_OK) {
+ u->cache_status = NGX_HTTP_CACHE_STALE;
+ rc = ngx_http_upstream_cache_send(r, u);
+ }
+
+ ngx_http_upstream_finalize_request(r, u, rc);
+ return;
+ }
+#endif
+
+ ngx_http_upstream_finalize_request(r, u, status);
+ return;
+ }
+ }
+
+ if (u->peer.connection) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "close http upstream connection: %d",
+ u->peer.connection->fd);
+#if (NGX_HTTP_SSL)
+
+ if (u->peer.connection->ssl) {
+ u->peer.connection->ssl->no_wait_shutdown = 1;
+ u->peer.connection->ssl->no_send_shutdown = 1;
+
+ (void) ngx_ssl_shutdown(u->peer.connection);
+ }
+#endif
+
+ if (u->peer.connection->pool) {
+ ngx_destroy_pool(u->peer.connection->pool);
+ }
+
+ ngx_close_connection(u->peer.connection);
+ u->peer.connection = NULL;
+ }
+
+ ngx_http_upstream_connect(r, u);
+}
+
+
+static void
+ngx_http_upstream_cleanup(void *data)
+{
+ ngx_http_request_t *r = data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "cleanup http upstream request: \"%V\"", &r->uri);
+
+ ngx_http_upstream_finalize_request(r, r->upstream, NGX_DONE);
+}
+
+
+static void
+ngx_http_upstream_finalize_request(ngx_http_request_t *r,
+ ngx_http_upstream_t *u, ngx_int_t rc)
+{
+ ngx_uint_t flush;
+ ngx_time_t *tp;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "finalize http upstream request: %i", rc);
+
+ if (u->cleanup) {
+ *u->cleanup = NULL;
+ u->cleanup = NULL;
+ }
+
+ if (u->resolved && u->resolved->ctx) {
+ ngx_resolve_name_done(u->resolved->ctx);
+ u->resolved->ctx = NULL;
+ }
+
+ if (u->state && u->state->response_sec) {
+ tp = ngx_timeofday();
+ u->state->response_sec = tp->sec - u->state->response_sec;
+ u->state->response_msec = tp->msec - u->state->response_msec;
+
+ if (u->pipe && u->pipe->read_length) {
+ u->state->response_length = u->pipe->read_length;
+ }
+ }
+
+ u->finalize_request(r, rc);
+
+ if (u->peer.free && u->peer.sockaddr) {
+ u->peer.free(&u->peer, u->peer.data, 0);
+ u->peer.sockaddr = NULL;
+ }
+
+ if (u->peer.connection) {
+
+#if (NGX_HTTP_SSL)
+
+ /* TODO: do not shutdown persistent connection */
+
+ if (u->peer.connection->ssl) {
+
+ /*
+ * We send the "close notify" shutdown alert to the upstream only
+ * and do not wait its "close notify" shutdown alert.
+ * It is acceptable according to the TLS standard.
+ */
+
+ u->peer.connection->ssl->no_wait_shutdown = 1;
+
+ (void) ngx_ssl_shutdown(u->peer.connection);
+ }
+#endif
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "close http upstream connection: %d",
+ u->peer.connection->fd);
+
+ if (u->peer.connection->pool) {
+ ngx_destroy_pool(u->peer.connection->pool);
+ }
+
+ ngx_close_connection(u->peer.connection);
+ }
+
+ u->peer.connection = NULL;
+
+ if (u->pipe && u->pipe->temp_file) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http upstream temp fd: %d",
+ u->pipe->temp_file->file.fd);
+ }
+
+ if (u->store && u->pipe && u->pipe->temp_file
+ && u->pipe->temp_file->file.fd != NGX_INVALID_FILE)
+ {
+ if (ngx_delete_file(u->pipe->temp_file->file.name.data)
+ == NGX_FILE_ERROR)
+ {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+ ngx_delete_file_n " \"%s\" failed",
+ u->pipe->temp_file->file.name.data);
+ }
+ }
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cache) {
+
+ if (u->cacheable) {
+
+ if (rc == NGX_HTTP_BAD_GATEWAY || rc == NGX_HTTP_GATEWAY_TIME_OUT) {
+ time_t valid;
+
+ valid = ngx_http_file_cache_valid(u->conf->cache_valid, rc);
+
+ if (valid) {
+ r->cache->valid_sec = ngx_time() + valid;
+ r->cache->error = rc;
+ }
+ }
+ }
+
+ ngx_http_file_cache_free(r->cache, u->pipe->temp_file);
+ }
+
+#endif
+
+ if (r->subrequest_in_memory
+ && u->headers_in.status_n >= NGX_HTTP_SPECIAL_RESPONSE)
+ {
+ u->buffer.last = u->buffer.pos;
+ }
+
+ if (rc == NGX_DECLINED) {
+ return;
+ }
+
+ r->connection->log->action = "sending to client";
+
+ if (!u->header_sent
+ || rc == NGX_HTTP_REQUEST_TIME_OUT
+ || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST)
+ {
+ ngx_http_finalize_request(r, rc);
+ return;
+ }
+
+ flush = 0;
+
+ if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+ rc = NGX_ERROR;
+ flush = 1;
+ }
+
+ if (r->header_only) {
+ ngx_http_finalize_request(r, rc);
+ return;
+ }
+
+ if (rc == 0) {
+ rc = ngx_http_send_special(r, NGX_HTTP_LAST);
+
+ } else if (flush) {
+ r->keepalive = 0;
+ rc = ngx_http_send_special(r, NGX_HTTP_FLUSH);
+ }
+
+ ngx_http_finalize_request(r, rc);
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_table_elt_t **ph;
+
+ ph = (ngx_table_elt_t **) ((char *) &r->upstream->headers_in + offset);
+
+ if (*ph == NULL) {
+ *ph = h;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_content_length(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+
+ u->headers_in.content_length = h;
+ u->headers_in.content_length_n = ngx_atoof(h->value.data, h->value.len);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_last_modified(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+
+ u->headers_in.last_modified = h;
+
+#if (NGX_HTTP_CACHE)
+
+ if (u->cacheable) {
+ u->headers_in.last_modified_time = ngx_http_parse_time(h->value.data,
+ h->value.len);
+ }
+
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_array_t *pa;
+ ngx_table_elt_t **ph;
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+ pa = &u->headers_in.cookies;
+
+ if (pa->elts == NULL) {
+ if (ngx_array_init(pa, r->pool, 1, sizeof(ngx_table_elt_t *)) != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ ph = ngx_array_push(pa);
+ if (ph == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ph = h;
+
+#if (NGX_HTTP_CACHE)
+ if (!(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_SET_COOKIE)) {
+ u->cacheable = 0;
+ }
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_array_t *pa;
+ ngx_table_elt_t **ph;
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+ pa = &u->headers_in.cache_control;
+
+ if (pa->elts == NULL) {
+ if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ ph = ngx_array_push(pa);
+ if (ph == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ph = h;
+
+#if (NGX_HTTP_CACHE)
+ {
+ u_char *p, *last;
+ ngx_int_t n;
+
+ if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL) {
+ return NGX_OK;
+ }
+
+ if (r->cache == NULL) {
+ return NGX_OK;
+ }
+
+ if (r->cache->valid_sec != 0 && u->headers_in.x_accel_expires != NULL) {
+ return NGX_OK;
+ }
+
+ p = h->value.data;
+ last = p + h->value.len;
+
+ if (ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL
+ || ngx_strlcasestrn(p, last, (u_char *) "no-store", 8 - 1) != NULL
+ || ngx_strlcasestrn(p, last, (u_char *) "private", 7 - 1) != NULL)
+ {
+ u->cacheable = 0;
+ return NGX_OK;
+ }
+
+ p = ngx_strlcasestrn(p, last, (u_char *) "max-age=", 8 - 1);
+
+ if (p == NULL) {
+ return NGX_OK;
+ }
+
+ n = 0;
+
+ for (p += 8; p < last; p++) {
+ if (*p == ',' || *p == ';' || *p == ' ') {
+ break;
+ }
+
+ if (*p >= '0' && *p <= '9') {
+ n = n * 10 + *p - '0';
+ continue;
+ }
+
+ u->cacheable = 0;
+ return NGX_OK;
+ }
+
+ if (n == 0) {
+ u->cacheable = 0;
+ return NGX_OK;
+ }
+
+ r->cache->valid_sec = ngx_time() + n;
+ }
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+ u->headers_in.expires = h;
+
+#if (NGX_HTTP_CACHE)
+ {
+ time_t expires;
+
+ if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_EXPIRES) {
+ return NGX_OK;
+ }
+
+ if (r->cache == NULL) {
+ return NGX_OK;
+ }
+
+ if (r->cache->valid_sec != 0) {
+ return NGX_OK;
+ }
+
+ expires = ngx_http_parse_time(h->value.data, h->value.len);
+
+ if (expires == NGX_ERROR || expires < ngx_time()) {
+ u->cacheable = 0;
+ return NGX_OK;
+ }
+
+ r->cache->valid_sec = expires;
+ }
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_accel_expires(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+ u->headers_in.x_accel_expires = h;
+
+#if (NGX_HTTP_CACHE)
+ {
+ u_char *p;
+ size_t len;
+ ngx_int_t n;
+
+ if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES) {
+ return NGX_OK;
+ }
+
+ if (r->cache == NULL) {
+ return NGX_OK;
+ }
+
+ len = h->value.len;
+ p = h->value.data;
+
+ if (p[0] != '@') {
+ n = ngx_atoi(p, len);
+
+ switch (n) {
+ case 0:
+ u->cacheable = 0;
+ /* fall through */
+
+ case NGX_ERROR:
+ return NGX_OK;
+
+ default:
+ r->cache->valid_sec = ngx_time() + n;
+ return NGX_OK;
+ }
+ }
+
+ p++;
+ len--;
+
+ n = ngx_atoi(p, len);
+
+ if (n != NGX_ERROR) {
+ r->cache->valid_sec = n;
+ }
+ }
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_limit_rate(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_int_t n;
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+ u->headers_in.x_accel_limit_rate = h;
+
+ if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE) {
+ return NGX_OK;
+ }
+
+ n = ngx_atoi(h->value.data, h->value.len);
+
+ if (n != NGX_ERROR) {
+ r->limit_rate = (size_t) n;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_buffering(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ u_char c0, c1, c2;
+ ngx_http_upstream_t *u;
+
+ u = r->upstream;
+
+ if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING) {
+ return NGX_OK;
+ }
+
+ if (u->conf->change_buffering) {
+
+ if (h->value.len == 2) {
+ c0 = ngx_tolower(h->value.data[0]);
+ c1 = ngx_tolower(h->value.data[1]);
+
+ if (c0 == 'n' && c1 == 'o') {
+ u->buffering = 0;
+ }
+
+ } else if (h->value.len == 3) {
+ c0 = ngx_tolower(h->value.data[0]);
+ c1 = ngx_tolower(h->value.data[1]);
+ c2 = ngx_tolower(h->value.data[2]);
+
+ if (c0 == 'y' && c1 == 'e' && c2 == 's') {
+ u->buffering = 1;
+ }
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_charset(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ if (r->upstream->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_CHARSET) {
+ return NGX_OK;
+ }
+
+ r->headers_out.override_charset = &h->value;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ r->upstream->headers_in.connection = h;
+
+ if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,
+ (u_char *) "close", 5 - 1)
+ != NULL)
+ {
+ r->upstream->headers_in.connection_close = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ r->upstream->headers_in.transfer_encoding = h;
+
+ if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,
+ (u_char *) "chunked", 7 - 1)
+ != NULL)
+ {
+ r->upstream->headers_in.chunked = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_table_elt_t *ho, **ph;
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ if (offset) {
+ ph = (ngx_table_elt_t **) ((char *) &r->headers_out + offset);
+ *ph = ho;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_array_t *pa;
+ ngx_table_elt_t *ho, **ph;
+
+ pa = (ngx_array_t *) ((char *) &r->headers_out + offset);
+
+ if (pa->elts == NULL) {
+ if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ ph = ngx_array_push(pa);
+ if (ph == NULL) {
+ return NGX_ERROR;
+ }
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+ *ph = ho;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_copy_content_type(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ u_char *p, *last;
+
+ r->headers_out.content_type_len = h->value.len;
+ r->headers_out.content_type = h->value;
+ r->headers_out.content_type_lowcase = NULL;
+
+ for (p = h->value.data; *p; p++) {
+
+ if (*p != ';') {
+ continue;
+ }
+
+ last = p;
+
+ while (*++p == ' ') { /* void */ }
+
+ if (*p == '\0') {
+ return NGX_OK;
+ }
+
+ if (ngx_strncasecmp(p, (u_char *) "charset=", 8) != 0) {
+ continue;
+ }
+
+ p += 8;
+
+ r->headers_out.content_type_len = last - h->value.data;
+
+ if (*p == '"') {
+ p++;
+ }
+
+ last = h->value.data + h->value.len;
+
+ if (*(last - 1) == '"') {
+ last--;
+ }
+
+ r->headers_out.charset.len = last - p;
+ r->headers_out.charset.data = p;
+
+ return NGX_OK;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_table_elt_t *ho;
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ r->headers_out.last_modified = ho;
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->upstream->cacheable) {
+ r->headers_out.last_modified_time =
+ r->upstream->headers_in.last_modified_time;
+ }
+
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_rewrite_location(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_int_t rc;
+ ngx_table_elt_t *ho;
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ if (r->upstream->rewrite_redirect) {
+ rc = r->upstream->rewrite_redirect(r, ho, 0);
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+ if (rc == NGX_OK) {
+ r->headers_out.location = ho;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "rewritten location: \"%V\"", &ho->value);
+ }
+
+ return rc;
+ }
+
+ if (ho->value.data[0] != '/') {
+ r->headers_out.location = ho;
+ }
+
+ /*
+ * we do not set r->headers_out.location here to avoid the handling
+ * the local redirects without a host name by ngx_http_header_filter()
+ */
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ u_char *p;
+ ngx_int_t rc;
+ ngx_table_elt_t *ho;
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ if (r->upstream->rewrite_redirect) {
+
+ p = ngx_strcasestrn(ho->value.data, "url=", 4 - 1);
+
+ if (p) {
+ rc = r->upstream->rewrite_redirect(r, ho, p + 4 - ho->value.data);
+
+ } else {
+ return NGX_OK;
+ }
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+ if (rc == NGX_OK) {
+ r->headers_out.refresh = ho;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "rewritten refresh: \"%V\"", &ho->value);
+ }
+
+ return rc;
+ }
+
+ r->headers_out.refresh = ho;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_int_t rc;
+ ngx_table_elt_t *ho;
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ if (r->upstream->rewrite_cookie) {
+ rc = r->upstream->rewrite_cookie(r, ho);
+
+ if (rc == NGX_DECLINED) {
+ return NGX_OK;
+ }
+
+#if (NGX_DEBUG)
+ if (rc == NGX_OK) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "rewritten cookie: \"%V\"", &ho->value);
+ }
+#endif
+
+ return rc;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_table_elt_t *ho;
+
+#if (NGX_HTTP_CACHE)
+
+ if (r->cached) {
+ r->allow_ranges = 1;
+ return NGX_OK;
+ }
+
+ if (r->upstream->cacheable) {
+ r->allow_ranges = 1;
+ r->single_range = 1;
+ return NGX_OK;
+ }
+
+#endif
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ r->headers_out.accept_ranges = ho;
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HTTP_GZIP)
+
+static ngx_int_t
+ngx_http_upstream_copy_content_encoding(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset)
+{
+ ngx_table_elt_t *ho;
+
+ ho = ngx_list_push(&r->headers_out.headers);
+ if (ho == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ho = *h;
+
+ r->headers_out.content_encoding = ho;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_upstream_add_variables(ngx_conf_t *cf)
+{
+ ngx_http_variable_t *var, *v;
+
+ for (v = ngx_http_upstream_vars; v->name.len; v++) {
+ var = ngx_http_add_variable(cf, &v->name, v->flags);
+ if (var == NULL) {
+ return NGX_ERROR;
+ }
+
+ var->get_handler = v->get_handler;
+ var->data = v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_addr_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_http_upstream_state_t *state;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ len = 0;
+ state = r->upstream_states->elts;
+
+ for (i = 0; i < r->upstream_states->nelts; i++) {
+ if (state[i].peer) {
+ len += state[i].peer->len + 2;
+
+ } else {
+ len += 3;
+ }
+ }
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->data = p;
+
+ i = 0;
+
+ for ( ;; ) {
+ if (state[i].peer) {
+ p = ngx_cpymem(p, state[i].peer->data, state[i].peer->len);
+ }
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ if (state[i].peer) {
+ *p++ = ',';
+ *p++ = ' ';
+
+ } else {
+ *p++ = ' ';
+ *p++ = ':';
+ *p++ = ' ';
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ continue;
+ }
+ }
+
+ v->len = p - v->data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_status_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_http_upstream_state_t *state;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ len = r->upstream_states->nelts * (3 + 2);
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->data = p;
+
+ i = 0;
+ state = r->upstream_states->elts;
+
+ for ( ;; ) {
+ if (state[i].status) {
+ p = ngx_sprintf(p, "%ui", state[i].status);
+
+ } else {
+ *p++ = '-';
+ }
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ if (state[i].peer) {
+ *p++ = ',';
+ *p++ = ' ';
+
+ } else {
+ *p++ = ' ';
+ *p++ = ':';
+ *p++ = ' ';
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ continue;
+ }
+ }
+
+ v->len = p - v->data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_response_time_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_msec_int_t ms;
+ ngx_http_upstream_state_t *state;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ len = r->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2);
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->data = p;
+
+ i = 0;
+ state = r->upstream_states->elts;
+
+ for ( ;; ) {
+ if (state[i].status) {
+ ms = (ngx_msec_int_t)
+ (state[i].response_sec * 1000 + state[i].response_msec);
+ ms = ngx_max(ms, 0);
+ p = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000);
+
+ } else {
+ *p++ = '-';
+ }
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ if (state[i].peer) {
+ *p++ = ',';
+ *p++ = ' ';
+
+ } else {
+ *p++ = ' ';
+ *p++ = ':';
+ *p++ = ' ';
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ continue;
+ }
+ }
+
+ v->len = p - v->data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_response_length_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_http_upstream_state_t *state;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ len = r->upstream_states->nelts * (NGX_OFF_T_LEN + 2);
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->data = p;
+
+ i = 0;
+ state = r->upstream_states->elts;
+
+ for ( ;; ) {
+ p = ngx_sprintf(p, "%O", state[i].response_length);
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ if (state[i].peer) {
+ *p++ = ',';
+ *p++ = ' ';
+
+ } else {
+ *p++ = ' ';
+ *p++ = ':';
+ *p++ = ' ';
+
+ if (++i == r->upstream_states->nelts) {
+ break;
+ }
+
+ continue;
+ }
+ }
+
+ v->len = p - v->data;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_upstream_header_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->upstream == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
+ &r->upstream->headers_in.headers.part,
+ sizeof("upstream_http_") - 1);
+}
+
+
+ngx_int_t
+ngx_http_upstream_cookie_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_str_t *name = (ngx_str_t *) data;
+
+ ngx_str_t cookie, s;
+
+ if (r->upstream == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ s.len = name->len - (sizeof("upstream_cookie_") - 1);
+ s.data = name->data + sizeof("upstream_cookie_") - 1;
+
+ if (ngx_http_parse_set_cookie_lines(&r->upstream->headers_in.cookies,
+ &s, &cookie)
+ == NGX_DECLINED)
+ {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->len = cookie.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = cookie.data;
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HTTP_CACHE)
+
+ngx_int_t
+ngx_http_upstream_cache_status(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_uint_t n;
+
+ if (r->upstream == NULL || r->upstream->cache_status == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ n = r->upstream->cache_status - 1;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->len = ngx_http_cache_status[n].len;
+ v->data = ngx_http_cache_status[n].data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_cache_last_modified(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ if (r->upstream == NULL
+ || !r->upstream->conf->cache_revalidate
+ || r->upstream->cache_status != NGX_HTTP_CACHE_EXPIRED
+ || r->cache->last_modified == -1)
+ {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ p = ngx_pnalloc(r->pool, sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_http_time(p, r->cache->last_modified) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_cache_etag(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->upstream == NULL
+ || !r->upstream->conf->cache_revalidate
+ || r->upstream->cache_status != NGX_HTTP_CACHE_EXPIRED
+ || r->cache->etag.len == 0)
+ {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->len = r->cache->etag.len;
+ v->data = r->cache->etag.data;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static char *
+ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
+{
+ char *rv;
+ void *mconf;
+ ngx_str_t *value;
+ ngx_url_t u;
+ ngx_uint_t m;
+ ngx_conf_t pcf;
+ ngx_http_module_t *module;
+ ngx_http_conf_ctx_t *ctx, *http_ctx;
+ ngx_http_upstream_srv_conf_t *uscf;
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ value = cf->args->elts;
+ u.host = value[1];
+ u.no_resolve = 1;
+ u.no_port = 1;
+
+ uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE
+ |NGX_HTTP_UPSTREAM_WEIGHT
+ |NGX_HTTP_UPSTREAM_MAX_FAILS
+ |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
+ |NGX_HTTP_UPSTREAM_DOWN
+ |NGX_HTTP_UPSTREAM_BACKUP);
+ if (uscf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ http_ctx = cf->ctx;
+ ctx->main_conf = http_ctx->main_conf;
+
+ /* the upstream{}'s srv_conf */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf;
+
+ uscf->srv_conf = ctx->srv_conf;
+
+
+ /* the upstream{}'s loc_conf */
+
+ ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
+ if (ctx->loc_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+
+ if (module->create_srv_conf) {
+ mconf = module->create_srv_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf;
+ }
+
+ if (module->create_loc_conf) {
+ mconf = module->create_loc_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->loc_conf[ngx_modules[m]->ctx_index] = mconf;
+ }
+ }
+
+ uscf->servers = ngx_array_create(cf->pool, 4,
+ sizeof(ngx_http_upstream_server_t));
+ if (uscf->servers == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /* parse inside upstream{} */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+ cf->cmd_type = NGX_HTTP_UPS_CONF;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = pcf;
+
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+
+ if (uscf->servers->nelts == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "no servers are inside upstream");
+ return NGX_CONF_ERROR;
+ }
+
+ return rv;
+}
+
+
+static char *
+ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_upstream_srv_conf_t *uscf = conf;
+
+ time_t fail_timeout;
+ ngx_str_t *value, s;
+ ngx_url_t u;
+ ngx_int_t weight, max_fails;
+ ngx_uint_t i;
+ ngx_http_upstream_server_t *us;
+
+ us = ngx_array_push(uscf->servers);
+ if (us == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(us, sizeof(ngx_http_upstream_server_t));
+
+ value = cf->args->elts;
+
+ weight = 1;
+ max_fails = 1;
+ fail_timeout = 10;
+
+ for (i = 2; i < cf->args->nelts; i++) {
+
+ if (ngx_strncmp(value[i].data, "weight=", 7) == 0) {
+
+ if (!(uscf->flags & NGX_HTTP_UPSTREAM_WEIGHT)) {
+ goto invalid;
+ }
+
+ weight = ngx_atoi(&value[i].data[7], value[i].len - 7);
+
+ if (weight == NGX_ERROR || weight == 0) {
+ goto invalid;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) {
+
+ if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_FAILS)) {
+ goto invalid;
+ }
+
+ max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10);
+
+ if (max_fails == NGX_ERROR) {
+ goto invalid;
+ }
+
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) {
+
+ if (!(uscf->flags & NGX_HTTP_UPSTREAM_FAIL_TIMEOUT)) {
+ goto invalid;
+ }
+
+ s.len = value[i].len - 13;
+ s.data = &value[i].data[13];
+
+ fail_timeout = ngx_parse_time(&s, 1);
+
+ if (fail_timeout == (time_t) NGX_ERROR) {
+ goto invalid;
+ }
+
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "backup") == 0) {
+
+ if (!(uscf->flags & NGX_HTTP_UPSTREAM_BACKUP)) {
+ goto invalid;
+ }
+
+ us->backup = 1;
+
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "down") == 0) {
+
+ if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) {
+ goto invalid;
+ }
+
+ us->down = 1;
+
+ continue;
+ }
+
+ goto invalid;
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = value[1];
+ u.default_port = 80;
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in upstream \"%V\"", u.err, &u.url);
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ us->name = u.url;
+ us->addrs = u.addrs;
+ us->naddrs = u.naddrs;
+ us->weight = weight;
+ us->max_fails = max_fails;
+ us->fail_timeout = fail_timeout;
+
+ return NGX_CONF_OK;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[i]);
+
+ return NGX_CONF_ERROR;
+}
+
+
+ngx_http_upstream_srv_conf_t *
+ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)
+{
+ ngx_uint_t i;
+ ngx_http_upstream_server_t *us;
+ ngx_http_upstream_srv_conf_t *uscf, **uscfp;
+ ngx_http_upstream_main_conf_t *umcf;
+
+ if (!(flags & NGX_HTTP_UPSTREAM_CREATE)) {
+
+ if (ngx_parse_url(cf->pool, u) != NGX_OK) {
+ if (u->err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in upstream \"%V\"", u->err, &u->url);
+ }
+
+ return NULL;
+ }
+ }
+
+ umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);
+
+ uscfp = umcf->upstreams.elts;
+
+ for (i = 0; i < umcf->upstreams.nelts; i++) {
+
+ if (uscfp[i]->host.len != u->host.len
+ || ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len)
+ != 0)
+ {
+ continue;
+ }
+
+ if ((flags & NGX_HTTP_UPSTREAM_CREATE)
+ && (uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE))
+ {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate upstream \"%V\"", &u->host);
+ return NULL;
+ }
+
+ if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && !u->no_port) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "upstream \"%V\" may not have port %d",
+ &u->host, u->port);
+ return NULL;
+ }
+
+ if ((flags & NGX_HTTP_UPSTREAM_CREATE) && !uscfp[i]->no_port) {
+ ngx_log_error(NGX_LOG_WARN, cf->log, 0,
+ "upstream \"%V\" may not have port %d in %s:%ui",
+ &u->host, uscfp[i]->port,
+ uscfp[i]->file_name, uscfp[i]->line);
+ return NULL;
+ }
+
+ if (uscfp[i]->port && u->port
+ && uscfp[i]->port != u->port)
+ {
+ continue;
+ }
+
+ if (uscfp[i]->default_port && u->default_port
+ && uscfp[i]->default_port != u->default_port)
+ {
+ continue;
+ }
+
+ if (flags & NGX_HTTP_UPSTREAM_CREATE) {
+ uscfp[i]->flags = flags;
+ }
+
+ return uscfp[i];
+ }
+
+ uscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_srv_conf_t));
+ if (uscf == NULL) {
+ return NULL;
+ }
+
+ uscf->flags = flags;
+ uscf->host = u->host;
+ uscf->file_name = cf->conf_file->file.name.data;
+ uscf->line = cf->conf_file->line;
+ uscf->port = u->port;
+ uscf->default_port = u->default_port;
+ uscf->no_port = u->no_port;
+
+ if (u->naddrs == 1) {
+ uscf->servers = ngx_array_create(cf->pool, 1,
+ sizeof(ngx_http_upstream_server_t));
+ if (uscf->servers == NULL) {
+ return NULL;
+ }
+
+ us = ngx_array_push(uscf->servers);
+ if (us == NULL) {
+ return NULL;
+ }
+
+ ngx_memzero(us, sizeof(ngx_http_upstream_server_t));
+
+ us->addrs = u->addrs;
+ us->naddrs = 1;
+ }
+
+ uscfp = ngx_array_push(&umcf->upstreams);
+ if (uscfp == NULL) {
+ return NULL;
+ }
+
+ *uscfp = uscf;
+
+ return uscf;
+}
+
+
+char *
+ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *p = conf;
+
+ ngx_int_t rc;
+ ngx_str_t *value;
+ ngx_http_complex_value_t cv;
+ ngx_http_upstream_local_t **plocal, *local;
+ ngx_http_compile_complex_value_t ccv;
+
+ plocal = (ngx_http_upstream_local_t **) (p + cmd->offset);
+
+ if (*plocal != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ *plocal = NULL;
+ return NGX_CONF_OK;
+ }
+
+ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+ ccv.cf = cf;
+ ccv.value = &value[1];
+ ccv.complex_value = &cv;
+
+ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ local = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_local_t));
+ if (local == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *plocal = local;
+
+ if (cv.lengths) {
+ local->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+ if (local->value == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *local->value = cv;
+
+ return NGX_CONF_OK;
+ }
+
+ local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t));
+ if (local->addr == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ rc = ngx_parse_addr(cf->pool, local->addr, value[1].data, value[1].len);
+
+ switch (rc) {
+ case NGX_OK:
+ local->addr->name = value[1];
+ return NGX_CONF_OK;
+
+ case NGX_DECLINED:
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid address \"%V\"", &value[1]);
+ /* fall through */
+
+ default:
+ return NGX_CONF_ERROR;
+ }
+}
+
+
+static ngx_addr_t *
+ngx_http_upstream_get_local(ngx_http_request_t *r,
+ ngx_http_upstream_local_t *local)
+{
+ ngx_int_t rc;
+ ngx_str_t val;
+ ngx_addr_t *addr;
+
+ if (local == NULL) {
+ return NULL;
+ }
+
+ if (local->value == NULL) {
+ return local->addr;
+ }
+
+ if (ngx_http_complex_value(r, local->value, &val) != NGX_OK) {
+ return NULL;
+ }
+
+ if (val.len == 0) {
+ return NULL;
+ }
+
+ addr = ngx_palloc(r->pool, sizeof(ngx_addr_t));
+ if (addr == NULL) {
+ return NULL;
+ }
+
+ rc = ngx_parse_addr(r->pool, addr, val.data, val.len);
+
+ switch (rc) {
+ case NGX_OK:
+ addr->name = val;
+ return addr;
+
+ case NGX_DECLINED:
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid local address \"%V\"", &val);
+ /* fall through */
+
+ default:
+ return NULL;
+ }
+}
+
+
+char *
+ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *value;
+ ngx_array_t **a;
+ ngx_http_upstream_param_t *param;
+
+ a = (ngx_array_t **) (p + cmd->offset);
+
+ if (*a == NULL) {
+ *a = ngx_array_create(cf->pool, 4, sizeof(ngx_http_upstream_param_t));
+ if (*a == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ param = ngx_array_push(*a);
+ if (param == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ param->key = value[1];
+ param->value = value[2];
+ param->skip_empty = 0;
+
+ if (cf->args->nelts == 4) {
+ if (ngx_strcmp(value[3].data, "if_not_empty") != 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[3]);
+ return NGX_CONF_ERROR;
+ }
+
+ param->skip_empty = 1;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+ngx_int_t
+ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,
+ ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,
+ ngx_str_t *default_hide_headers, ngx_hash_init_t *hash)
+{
+ ngx_str_t *h;
+ ngx_uint_t i, j;
+ ngx_array_t hide_headers;
+ ngx_hash_key_t *hk;
+
+ if (conf->hide_headers == NGX_CONF_UNSET_PTR
+ && conf->pass_headers == NGX_CONF_UNSET_PTR)
+ {
+ conf->hide_headers = prev->hide_headers;
+ conf->pass_headers = prev->pass_headers;
+
+ conf->hide_headers_hash = prev->hide_headers_hash;
+
+ if (conf->hide_headers_hash.buckets
+#if (NGX_HTTP_CACHE)
+ && ((conf->cache == NULL) == (prev->cache == NULL))
+#endif
+ )
+ {
+ return NGX_OK;
+ }
+
+ } else {
+ if (conf->hide_headers == NGX_CONF_UNSET_PTR) {
+ conf->hide_headers = prev->hide_headers;
+ }
+
+ if (conf->pass_headers == NGX_CONF_UNSET_PTR) {
+ conf->pass_headers = prev->pass_headers;
+ }
+ }
+
+ if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ for (h = default_hide_headers; h->len; h++) {
+ hk = ngx_array_push(&hide_headers);
+ if (hk == NULL) {
+ return NGX_ERROR;
+ }
+
+ hk->key = *h;
+ hk->key_hash = ngx_hash_key_lc(h->data, h->len);
+ hk->value = (void *) 1;
+ }
+
+ if (conf->hide_headers != NGX_CONF_UNSET_PTR) {
+
+ h = conf->hide_headers->elts;
+
+ for (i = 0; i < conf->hide_headers->nelts; i++) {
+
+ hk = hide_headers.elts;
+
+ for (j = 0; j < hide_headers.nelts; j++) {
+ if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {
+ goto exist;
+ }
+ }
+
+ hk = ngx_array_push(&hide_headers);
+ if (hk == NULL) {
+ return NGX_ERROR;
+ }
+
+ hk->key = h[i];
+ hk->key_hash = ngx_hash_key_lc(h[i].data, h[i].len);
+ hk->value = (void *) 1;
+
+ exist:
+
+ continue;
+ }
+ }
+
+ if (conf->pass_headers != NGX_CONF_UNSET_PTR) {
+
+ h = conf->pass_headers->elts;
+ hk = hide_headers.elts;
+
+ for (i = 0; i < conf->pass_headers->nelts; i++) {
+ for (j = 0; j < hide_headers.nelts; j++) {
+
+ if (hk[j].key.data == NULL) {
+ continue;
+ }
+
+ if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {
+ hk[j].key.data = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ hash->hash = &conf->hide_headers_hash;
+ hash->key = ngx_hash_key_lc;
+ hash->pool = cf->pool;
+ hash->temp_pool = NULL;
+
+ return ngx_hash_init(hash, hide_headers.elts, hide_headers.nelts);
+}
+
+
+static void *
+ngx_http_upstream_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_http_upstream_main_conf_t *umcf;
+
+ umcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_main_conf_t));
+ if (umcf == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(&umcf->upstreams, cf->pool, 4,
+ sizeof(ngx_http_upstream_srv_conf_t *))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return umcf;
+}
+
+
+static char *
+ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf)
+{
+ ngx_http_upstream_main_conf_t *umcf = conf;
+
+ ngx_uint_t i;
+ ngx_array_t headers_in;
+ ngx_hash_key_t *hk;
+ ngx_hash_init_t hash;
+ ngx_http_upstream_init_pt init;
+ ngx_http_upstream_header_t *header;
+ ngx_http_upstream_srv_conf_t **uscfp;
+
+ uscfp = umcf->upstreams.elts;
+
+ for (i = 0; i < umcf->upstreams.nelts; i++) {
+
+ init = uscfp[i]->peer.init_upstream ? uscfp[i]->peer.init_upstream:
+ ngx_http_upstream_init_round_robin;
+
+ if (init(cf, uscfp[i]) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+
+ /* upstream_headers_in_hash */
+
+ if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ for (header = ngx_http_upstream_headers_in; header->name.len; header++) {
+ hk = ngx_array_push(&headers_in);
+ if (hk == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ hk->key = header->name;
+ hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);
+ hk->value = header;
+ }
+
+ hash.hash = &umcf->headers_in_hash;
+ hash.key = ngx_hash_key_lc;
+ hash.max_size = 512;
+ hash.bucket_size = ngx_align(64, ngx_cacheline_size);
+ hash.name = "upstream_headers_in_hash";
+ hash.pool = cf->pool;
+ hash.temp_pool = NULL;
+
+ if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream.h
new file mode 100644
index 00000000000..dafb5a319b8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream.h
@@ -0,0 +1,396 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_UPSTREAM_H_INCLUDED_
+#define _NGX_HTTP_UPSTREAM_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include <ngx_event_pipe.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_UPSTREAM_FT_ERROR 0x00000002
+#define NGX_HTTP_UPSTREAM_FT_TIMEOUT 0x00000004
+#define NGX_HTTP_UPSTREAM_FT_INVALID_HEADER 0x00000008
+#define NGX_HTTP_UPSTREAM_FT_HTTP_500 0x00000010
+#define NGX_HTTP_UPSTREAM_FT_HTTP_502 0x00000020
+#define NGX_HTTP_UPSTREAM_FT_HTTP_503 0x00000040
+#define NGX_HTTP_UPSTREAM_FT_HTTP_504 0x00000080
+#define NGX_HTTP_UPSTREAM_FT_HTTP_403 0x00000100
+#define NGX_HTTP_UPSTREAM_FT_HTTP_404 0x00000200
+#define NGX_HTTP_UPSTREAM_FT_UPDATING 0x00000400
+#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK 0x00000800
+#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING 0x00001000
+#define NGX_HTTP_UPSTREAM_FT_NOLIVE 0x40000000
+#define NGX_HTTP_UPSTREAM_FT_OFF 0x80000000
+
+#define NGX_HTTP_UPSTREAM_FT_STATUS (NGX_HTTP_UPSTREAM_FT_HTTP_500 \
+ |NGX_HTTP_UPSTREAM_FT_HTTP_502 \
+ |NGX_HTTP_UPSTREAM_FT_HTTP_503 \
+ |NGX_HTTP_UPSTREAM_FT_HTTP_504 \
+ |NGX_HTTP_UPSTREAM_FT_HTTP_403 \
+ |NGX_HTTP_UPSTREAM_FT_HTTP_404)
+
+#define NGX_HTTP_UPSTREAM_INVALID_HEADER 40
+
+
+#define NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT 0x00000002
+#define NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES 0x00000004
+#define NGX_HTTP_UPSTREAM_IGN_EXPIRES 0x00000008
+#define NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL 0x00000010
+#define NGX_HTTP_UPSTREAM_IGN_SET_COOKIE 0x00000020
+#define NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE 0x00000040
+#define NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING 0x00000080
+#define NGX_HTTP_UPSTREAM_IGN_XA_CHARSET 0x00000100
+
+
+typedef struct {
+ ngx_msec_t bl_time;
+ ngx_uint_t bl_state;
+
+ ngx_uint_t status;
+ time_t response_sec;
+ ngx_uint_t response_msec;
+ off_t response_length;
+
+ ngx_str_t *peer;
+} ngx_http_upstream_state_t;
+
+
+typedef struct {
+ ngx_hash_t headers_in_hash;
+ ngx_array_t upstreams;
+ /* ngx_http_upstream_srv_conf_t */
+} ngx_http_upstream_main_conf_t;
+
+typedef struct ngx_http_upstream_srv_conf_s ngx_http_upstream_srv_conf_t;
+
+typedef ngx_int_t (*ngx_http_upstream_init_pt)(ngx_conf_t *cf,
+ ngx_http_upstream_srv_conf_t *us);
+typedef ngx_int_t (*ngx_http_upstream_init_peer_pt)(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us);
+
+
+typedef struct {
+ ngx_http_upstream_init_pt init_upstream;
+ ngx_http_upstream_init_peer_pt init;
+ void *data;
+} ngx_http_upstream_peer_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_addr_t *addrs;
+ ngx_uint_t naddrs;
+ ngx_uint_t weight;
+ ngx_uint_t max_fails;
+ time_t fail_timeout;
+
+ unsigned down:1;
+ unsigned backup:1;
+} ngx_http_upstream_server_t;
+
+
+#define NGX_HTTP_UPSTREAM_CREATE 0x0001
+#define NGX_HTTP_UPSTREAM_WEIGHT 0x0002
+#define NGX_HTTP_UPSTREAM_MAX_FAILS 0x0004
+#define NGX_HTTP_UPSTREAM_FAIL_TIMEOUT 0x0008
+#define NGX_HTTP_UPSTREAM_DOWN 0x0010
+#define NGX_HTTP_UPSTREAM_BACKUP 0x0020
+
+
+struct ngx_http_upstream_srv_conf_s {
+ ngx_http_upstream_peer_t peer;
+ void **srv_conf;
+
+ ngx_array_t *servers; /* ngx_http_upstream_server_t */
+
+ ngx_uint_t flags;
+ ngx_str_t host;
+ u_char *file_name;
+ ngx_uint_t line;
+ in_port_t port;
+ in_port_t default_port;
+ ngx_uint_t no_port; /* unsigned no_port:1 */
+};
+
+
+typedef struct {
+ ngx_addr_t *addr;
+ ngx_http_complex_value_t *value;
+} ngx_http_upstream_local_t;
+
+
+typedef struct {
+ ngx_http_upstream_srv_conf_t *upstream;
+
+ ngx_msec_t connect_timeout;
+ ngx_msec_t send_timeout;
+ ngx_msec_t read_timeout;
+ ngx_msec_t timeout;
+
+ size_t send_lowat;
+ size_t buffer_size;
+
+ size_t busy_buffers_size;
+ size_t max_temp_file_size;
+ size_t temp_file_write_size;
+
+ size_t busy_buffers_size_conf;
+ size_t max_temp_file_size_conf;
+ size_t temp_file_write_size_conf;
+
+ ngx_bufs_t bufs;
+
+ ngx_uint_t ignore_headers;
+ ngx_uint_t next_upstream;
+ ngx_uint_t store_access;
+ ngx_flag_t buffering;
+ ngx_flag_t pass_request_headers;
+ ngx_flag_t pass_request_body;
+
+ ngx_flag_t ignore_client_abort;
+ ngx_flag_t intercept_errors;
+ ngx_flag_t cyclic_temp_file;
+
+ ngx_path_t *temp_path;
+
+ ngx_hash_t hide_headers_hash;
+ ngx_array_t *hide_headers;
+ ngx_array_t *pass_headers;
+
+ ngx_http_upstream_local_t *local;
+
+#if (NGX_HTTP_CACHE)
+ ngx_shm_zone_t *cache;
+
+ ngx_uint_t cache_min_uses;
+ ngx_uint_t cache_use_stale;
+ ngx_uint_t cache_methods;
+
+ ngx_flag_t cache_lock;
+ ngx_msec_t cache_lock_timeout;
+
+ ngx_flag_t cache_revalidate;
+
+ ngx_array_t *cache_valid;
+ ngx_array_t *cache_bypass;
+ ngx_array_t *no_cache;
+#endif
+
+ ngx_array_t *store_lengths;
+ ngx_array_t *store_values;
+
+ signed store:2;
+ unsigned intercept_404:1;
+ unsigned change_buffering:1;
+
+#if (NGX_HTTP_SSL)
+ ngx_ssl_t *ssl;
+ ngx_flag_t ssl_session_reuse;
+
+ ngx_http_complex_value_t *ssl_name;
+ ngx_flag_t ssl_server_name;
+ ngx_flag_t ssl_verify;
+#endif
+
+ ngx_str_t module;
+} ngx_http_upstream_conf_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_http_header_handler_pt handler;
+ ngx_uint_t offset;
+ ngx_http_header_handler_pt copy_handler;
+ ngx_uint_t conf;
+ ngx_uint_t redirect; /* unsigned redirect:1; */
+} ngx_http_upstream_header_t;
+
+
+typedef struct {
+ ngx_list_t headers;
+
+ ngx_uint_t status_n;
+ ngx_str_t status_line;
+
+ ngx_table_elt_t *status;
+ ngx_table_elt_t *date;
+ ngx_table_elt_t *server;
+ ngx_table_elt_t *connection;
+
+ ngx_table_elt_t *expires;
+ ngx_table_elt_t *etag;
+ ngx_table_elt_t *x_accel_expires;
+ ngx_table_elt_t *x_accel_redirect;
+ ngx_table_elt_t *x_accel_limit_rate;
+
+ ngx_table_elt_t *content_type;
+ ngx_table_elt_t *content_length;
+
+ ngx_table_elt_t *last_modified;
+ ngx_table_elt_t *location;
+ ngx_table_elt_t *accept_ranges;
+ ngx_table_elt_t *www_authenticate;
+ ngx_table_elt_t *transfer_encoding;
+
+#if (NGX_HTTP_GZIP)
+ ngx_table_elt_t *content_encoding;
+#endif
+
+ ngx_array_t cache_control;
+ ngx_array_t cookies;
+
+ off_t content_length_n;
+ time_t last_modified_time;
+
+ unsigned connection_close:1;
+ unsigned chunked:1;
+} ngx_http_upstream_headers_in_t;
+
+
+typedef struct {
+ ngx_str_t host;
+ in_port_t port;
+ ngx_uint_t no_port; /* unsigned no_port:1 */
+
+ ngx_uint_t naddrs;
+ ngx_addr_t *addrs;
+
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+
+ ngx_resolver_ctx_t *ctx;
+} ngx_http_upstream_resolved_t;
+
+
+typedef void (*ngx_http_upstream_handler_pt)(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+
+
+struct ngx_http_upstream_s {
+ ngx_http_upstream_handler_pt read_event_handler;
+ ngx_http_upstream_handler_pt write_event_handler;
+
+ ngx_peer_connection_t peer;
+
+ ngx_event_pipe_t *pipe;
+
+ ngx_chain_t *request_bufs;
+
+ ngx_output_chain_ctx_t output;
+ ngx_chain_writer_ctx_t writer;
+
+ ngx_http_upstream_conf_t *conf;
+
+ ngx_http_upstream_headers_in_t headers_in;
+
+ ngx_http_upstream_resolved_t *resolved;
+
+ ngx_buf_t from_client;
+
+ ngx_buf_t buffer;
+ off_t length;
+
+ ngx_chain_t *out_bufs;
+ ngx_chain_t *busy_bufs;
+ ngx_chain_t *free_bufs;
+
+ ngx_int_t (*input_filter_init)(void *data);
+ ngx_int_t (*input_filter)(void *data, ssize_t bytes);
+ void *input_filter_ctx;
+
+#if (NGX_HTTP_CACHE)
+ ngx_int_t (*create_key)(ngx_http_request_t *r);
+#endif
+ ngx_int_t (*create_request)(ngx_http_request_t *r);
+ ngx_int_t (*reinit_request)(ngx_http_request_t *r);
+ ngx_int_t (*process_header)(ngx_http_request_t *r);
+ void (*abort_request)(ngx_http_request_t *r);
+ void (*finalize_request)(ngx_http_request_t *r,
+ ngx_int_t rc);
+ ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r,
+ ngx_table_elt_t *h, size_t prefix);
+ ngx_int_t (*rewrite_cookie)(ngx_http_request_t *r,
+ ngx_table_elt_t *h);
+
+ ngx_msec_t timeout;
+
+ ngx_http_upstream_state_t *state;
+
+ ngx_str_t method;
+ ngx_str_t schema;
+ ngx_str_t uri;
+
+#if (NGX_HTTP_SSL)
+ ngx_str_t ssl_name;
+#endif
+
+ ngx_http_cleanup_pt *cleanup;
+
+ unsigned store:1;
+ unsigned cacheable:1;
+ unsigned accel:1;
+ unsigned ssl:1;
+#if (NGX_HTTP_CACHE)
+ unsigned cache_status:3;
+#endif
+
+ unsigned buffering:1;
+ unsigned keepalive:1;
+ unsigned upgrade:1;
+
+ unsigned request_sent:1;
+ unsigned header_sent:1;
+};
+
+
+typedef struct {
+ ngx_uint_t status;
+ ngx_uint_t mask;
+} ngx_http_upstream_next_t;
+
+
+typedef struct {
+ ngx_str_t key;
+ ngx_str_t value;
+ ngx_uint_t skip_empty;
+} ngx_http_upstream_param_t;
+
+
+ngx_int_t ngx_http_upstream_cookie_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+ngx_int_t ngx_http_upstream_create(ngx_http_request_t *r);
+void ngx_http_upstream_init(ngx_http_request_t *r);
+ngx_http_upstream_srv_conf_t *ngx_http_upstream_add(ngx_conf_t *cf,
+ ngx_url_t *u, ngx_uint_t flags);
+char *ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+ngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,
+ ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,
+ ngx_str_t *default_hide_headers, ngx_hash_init_t *hash);
+
+
+#define ngx_http_conf_upstream_srv_conf(uscf, module) \
+ uscf->srv_conf[module.ctx_index]
+
+
+extern ngx_module_t ngx_http_upstream_module;
+extern ngx_conf_bitmask_t ngx_http_upstream_cache_method_mask[];
+extern ngx_conf_bitmask_t ngx_http_upstream_ignore_headers_masks[];
+
+
+#endif /* _NGX_HTTP_UPSTREAM_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream_round_robin.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream_round_robin.c
new file mode 100644
index 00000000000..b39eeae5046
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream_round_robin.c
@@ -0,0 +1,697 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_peer(
+ ngx_http_upstream_rr_peer_data_t *rrp);
+
+#if (NGX_HTTP_SSL)
+
+static ngx_int_t ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc,
+ void *data);
+static void ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc,
+ void *data);
+
+#endif
+
+
+ngx_int_t
+ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ ngx_url_t u;
+ ngx_uint_t i, j, n, w;
+ ngx_http_upstream_server_t *server;
+ ngx_http_upstream_rr_peer_t *peer;
+ ngx_http_upstream_rr_peers_t *peers, *backup;
+
+ us->peer.init = ngx_http_upstream_init_round_robin_peer;
+
+ if (us->servers) {
+ server = us->servers->elts;
+
+ n = 0;
+ w = 0;
+
+ for (i = 0; i < us->servers->nelts; i++) {
+ if (server[i].backup) {
+ continue;
+ }
+
+ n += server[i].naddrs;
+ w += server[i].naddrs * server[i].weight;
+ }
+
+ if (n == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no servers in upstream \"%V\" in %s:%ui",
+ &us->host, us->file_name, us->line);
+ return NGX_ERROR;
+ }
+
+ peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)
+ + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));
+ if (peers == NULL) {
+ return NGX_ERROR;
+ }
+
+ peers->single = (n == 1);
+ peers->number = n;
+ peers->weighted = (w != n);
+ peers->total_weight = w;
+ peers->name = &us->host;
+
+ n = 0;
+ peer = peers->peer;
+
+ for (i = 0; i < us->servers->nelts; i++) {
+ if (server[i].backup) {
+ continue;
+ }
+
+ for (j = 0; j < server[i].naddrs; j++) {
+ peer[n].sockaddr = server[i].addrs[j].sockaddr;
+ peer[n].socklen = server[i].addrs[j].socklen;
+ peer[n].name = server[i].addrs[j].name;
+ peer[n].weight = server[i].weight;
+ peer[n].effective_weight = server[i].weight;
+ peer[n].current_weight = 0;
+ peer[n].max_fails = server[i].max_fails;
+ peer[n].fail_timeout = server[i].fail_timeout;
+ peer[n].down = server[i].down;
+ peer[n].server = server[i].name;
+ n++;
+ }
+ }
+
+ us->peer.data = peers;
+
+ /* backup servers */
+
+ n = 0;
+ w = 0;
+
+ for (i = 0; i < us->servers->nelts; i++) {
+ if (!server[i].backup) {
+ continue;
+ }
+
+ n += server[i].naddrs;
+ w += server[i].naddrs * server[i].weight;
+ }
+
+ if (n == 0) {
+ return NGX_OK;
+ }
+
+ backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)
+ + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));
+ if (backup == NULL) {
+ return NGX_ERROR;
+ }
+
+ peers->single = 0;
+ backup->single = 0;
+ backup->number = n;
+ backup->weighted = (w != n);
+ backup->total_weight = w;
+ backup->name = &us->host;
+
+ n = 0;
+ peer = backup->peer;
+
+ for (i = 0; i < us->servers->nelts; i++) {
+ if (!server[i].backup) {
+ continue;
+ }
+
+ for (j = 0; j < server[i].naddrs; j++) {
+ peer[n].sockaddr = server[i].addrs[j].sockaddr;
+ peer[n].socklen = server[i].addrs[j].socklen;
+ peer[n].name = server[i].addrs[j].name;
+ peer[n].weight = server[i].weight;
+ peer[n].effective_weight = server[i].weight;
+ peer[n].current_weight = 0;
+ peer[n].max_fails = server[i].max_fails;
+ peer[n].fail_timeout = server[i].fail_timeout;
+ peer[n].down = server[i].down;
+ peer[n].server = server[i].name;
+ n++;
+ }
+ }
+
+ peers->next = backup;
+
+ return NGX_OK;
+ }
+
+
+ /* an upstream implicitly defined by proxy_pass, etc. */
+
+ if (us->port == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no port in upstream \"%V\" in %s:%ui",
+ &us->host, us->file_name, us->line);
+ return NGX_ERROR;
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.host = us->host;
+ u.port = us->port;
+
+ if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "%s in upstream \"%V\" in %s:%ui",
+ u.err, &us->host, us->file_name, us->line);
+ }
+
+ return NGX_ERROR;
+ }
+
+ n = u.naddrs;
+
+ peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)
+ + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));
+ if (peers == NULL) {
+ return NGX_ERROR;
+ }
+
+ peers->single = (n == 1);
+ peers->number = n;
+ peers->weighted = 0;
+ peers->total_weight = n;
+ peers->name = &us->host;
+
+ peer = peers->peer;
+
+ for (i = 0; i < u.naddrs; i++) {
+ peer[i].sockaddr = u.addrs[i].sockaddr;
+ peer[i].socklen = u.addrs[i].socklen;
+ peer[i].name = u.addrs[i].name;
+ peer[i].weight = 1;
+ peer[i].effective_weight = 1;
+ peer[i].current_weight = 0;
+ peer[i].max_fails = 1;
+ peer[i].fail_timeout = 10;
+ }
+
+ us->peer.data = peers;
+
+ /* implicitly defined upstream has no backup servers */
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ ngx_uint_t n;
+ ngx_http_upstream_rr_peer_data_t *rrp;
+
+ rrp = r->upstream->peer.data;
+
+ if (rrp == NULL) {
+ rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));
+ if (rrp == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->upstream->peer.data = rrp;
+ }
+
+ rrp->peers = us->peer.data;
+ rrp->current = 0;
+
+ n = rrp->peers->number;
+
+ if (rrp->peers->next && rrp->peers->next->number > n) {
+ n = rrp->peers->next->number;
+ }
+
+ if (n <= 8 * sizeof(uintptr_t)) {
+ rrp->tried = &rrp->data;
+ rrp->data = 0;
+
+ } else {
+ n = (n + (8 * sizeof(uintptr_t) - 1)) / (8 * sizeof(uintptr_t));
+
+ rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));
+ if (rrp->tried == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;
+ r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;
+ r->upstream->peer.tries = rrp->peers->number;
+#if (NGX_HTTP_SSL)
+ r->upstream->peer.set_session =
+ ngx_http_upstream_set_round_robin_peer_session;
+ r->upstream->peer.save_session =
+ ngx_http_upstream_save_round_robin_peer_session;
+#endif
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
+ ngx_http_upstream_resolved_t *ur)
+{
+ u_char *p;
+ size_t len;
+ socklen_t socklen;
+ ngx_uint_t i, n;
+ struct sockaddr *sockaddr;
+ ngx_http_upstream_rr_peer_t *peer;
+ ngx_http_upstream_rr_peers_t *peers;
+ ngx_http_upstream_rr_peer_data_t *rrp;
+
+ rrp = r->upstream->peer.data;
+
+ if (rrp == NULL) {
+ rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));
+ if (rrp == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->upstream->peer.data = rrp;
+ }
+
+ peers = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peers_t)
+ + sizeof(ngx_http_upstream_rr_peer_t) * (ur->naddrs - 1));
+ if (peers == NULL) {
+ return NGX_ERROR;
+ }
+
+ peers->single = (ur->naddrs == 1);
+ peers->number = ur->naddrs;
+ peers->name = &ur->host;
+
+ peer = peers->peer;
+
+ if (ur->sockaddr) {
+ peer[0].sockaddr = ur->sockaddr;
+ peer[0].socklen = ur->socklen;
+ peer[0].name = ur->host;
+ peer[0].weight = 1;
+ peer[0].effective_weight = 1;
+ peer[0].current_weight = 0;
+ peer[0].max_fails = 1;
+ peer[0].fail_timeout = 10;
+
+ } else {
+
+ for (i = 0; i < ur->naddrs; i++) {
+
+ socklen = ur->addrs[i].socklen;
+
+ sockaddr = ngx_palloc(r->pool, socklen);
+ if (sockaddr == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen);
+
+ switch (sockaddr->sa_family) {
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(ur->port);
+ break;
+#endif
+ default: /* AF_INET */
+ ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port);
+ }
+
+ p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);
+
+ peer[i].sockaddr = sockaddr;
+ peer[i].socklen = socklen;
+ peer[i].name.len = len;
+ peer[i].name.data = p;
+ peer[i].weight = 1;
+ peer[i].effective_weight = 1;
+ peer[i].current_weight = 0;
+ peer[i].max_fails = 1;
+ peer[i].fail_timeout = 10;
+ }
+ }
+
+ rrp->peers = peers;
+ rrp->current = 0;
+
+ if (rrp->peers->number <= 8 * sizeof(uintptr_t)) {
+ rrp->tried = &rrp->data;
+ rrp->data = 0;
+
+ } else {
+ n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
+ / (8 * sizeof(uintptr_t));
+
+ rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));
+ if (rrp->tried == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;
+ r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;
+ r->upstream->peer.tries = rrp->peers->number;
+#if (NGX_HTTP_SSL)
+ r->upstream->peer.set_session = ngx_http_upstream_empty_set_session;
+ r->upstream->peer.save_session = ngx_http_upstream_empty_save_session;
+#endif
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_rr_peer_data_t *rrp = data;
+
+ ngx_int_t rc;
+ ngx_uint_t i, n;
+ ngx_http_upstream_rr_peer_t *peer;
+ ngx_http_upstream_rr_peers_t *peers;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get rr peer, try: %ui", pc->tries);
+
+ pc->cached = 0;
+ pc->connection = NULL;
+
+ peers = rrp->peers;
+
+ /* ngx_lock_mutex(peers->mutex); */
+
+ if (peers->single) {
+ peer = &peers->peer[0];
+
+ if (peer->down) {
+ goto failed;
+ }
+
+ } else {
+
+ /* there are several peers */
+
+ peer = ngx_http_upstream_get_peer(rrp);
+
+ if (peer == NULL) {
+ goto failed;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get rr peer, current: %ui %i",
+ rrp->current, peer->current_weight);
+ }
+
+ pc->sockaddr = peer->sockaddr;
+ pc->socklen = peer->socklen;
+ pc->name = &peer->name;
+
+ /* ngx_unlock_mutex(peers->mutex); */
+
+ if (pc->tries == 1 && peers->next) {
+ pc->tries += peers->next->number;
+ }
+
+ return NGX_OK;
+
+failed:
+
+ if (peers->next) {
+
+ /* ngx_unlock_mutex(peers->mutex); */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "backup servers");
+
+ rrp->peers = peers->next;
+ pc->tries = rrp->peers->number;
+
+ n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
+ / (8 * sizeof(uintptr_t));
+
+ for (i = 0; i < n; i++) {
+ rrp->tried[i] = 0;
+ }
+
+ rc = ngx_http_upstream_get_round_robin_peer(pc, rrp);
+
+ if (rc != NGX_BUSY) {
+ return rc;
+ }
+
+ /* ngx_lock_mutex(peers->mutex); */
+ }
+
+ /* all peers failed, mark them as live for quick recovery */
+
+ for (i = 0; i < peers->number; i++) {
+ peers->peer[i].fails = 0;
+ }
+
+ /* ngx_unlock_mutex(peers->mutex); */
+
+ pc->name = peers->name;
+
+ return NGX_BUSY;
+}
+
+
+static ngx_http_upstream_rr_peer_t *
+ngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)
+{
+ time_t now;
+ uintptr_t m;
+ ngx_int_t total;
+ ngx_uint_t i, n;
+ ngx_http_upstream_rr_peer_t *peer, *best;
+
+ now = ngx_time();
+
+ best = NULL;
+ total = 0;
+
+ for (i = 0; i < rrp->peers->number; i++) {
+
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ if (rrp->tried[n] & m) {
+ continue;
+ }
+
+ peer = &rrp->peers->peer[i];
+
+ if (peer->down) {
+ continue;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ continue;
+ }
+
+ peer->current_weight += peer->effective_weight;
+ total += peer->effective_weight;
+
+ if (peer->effective_weight < peer->weight) {
+ peer->effective_weight++;
+ }
+
+ if (best == NULL || peer->current_weight > best->current_weight) {
+ best = peer;
+ }
+ }
+
+ if (best == NULL) {
+ return NULL;
+ }
+
+ i = best - &rrp->peers->peer[0];
+
+ rrp->current = i;
+
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ rrp->tried[n] |= m;
+
+ best->current_weight -= total;
+
+ if (now - best->checked > best->fail_timeout) {
+ best->checked = now;
+ }
+
+ return best;
+}
+
+
+void
+ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,
+ ngx_uint_t state)
+{
+ ngx_http_upstream_rr_peer_data_t *rrp = data;
+
+ time_t now;
+ ngx_http_upstream_rr_peer_t *peer;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "free rr peer %ui %ui", pc->tries, state);
+
+ /* TODO: NGX_PEER_KEEPALIVE */
+
+ if (rrp->peers->single) {
+ pc->tries = 0;
+ return;
+ }
+
+ peer = &rrp->peers->peer[rrp->current];
+
+ if (state & NGX_PEER_FAILED) {
+ now = ngx_time();
+
+ /* ngx_lock_mutex(rrp->peers->mutex); */
+
+ peer->fails++;
+ peer->accessed = now;
+ peer->checked = now;
+
+ if (peer->max_fails) {
+ peer->effective_weight -= peer->weight / peer->max_fails;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "free rr peer failed: %ui %i",
+ rrp->current, peer->effective_weight);
+
+ if (peer->effective_weight < 0) {
+ peer->effective_weight = 0;
+ }
+
+ /* ngx_unlock_mutex(rrp->peers->mutex); */
+
+ } else {
+
+ /* mark peer live if check passed */
+
+ if (peer->accessed < peer->checked) {
+ peer->fails = 0;
+ }
+ }
+
+ if (pc->tries) {
+ pc->tries--;
+ }
+
+ /* ngx_unlock_mutex(rrp->peers->mutex); */
+}
+
+
+#if (NGX_HTTP_SSL)
+
+ngx_int_t
+ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
+ void *data)
+{
+ ngx_http_upstream_rr_peer_data_t *rrp = data;
+
+ ngx_int_t rc;
+ ngx_ssl_session_t *ssl_session;
+ ngx_http_upstream_rr_peer_t *peer;
+
+ peer = &rrp->peers->peer[rrp->current];
+
+ /* TODO: threads only mutex */
+ /* ngx_lock_mutex(rrp->peers->mutex); */
+
+ ssl_session = peer->ssl_session;
+
+ rc = ngx_ssl_set_session(pc->connection, ssl_session);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "set session: %p", ssl_session);
+
+ /* ngx_unlock_mutex(rrp->peers->mutex); */
+
+ return rc;
+}
+
+
+void
+ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
+ void *data)
+{
+ ngx_http_upstream_rr_peer_data_t *rrp = data;
+
+ ngx_ssl_session_t *old_ssl_session, *ssl_session;
+ ngx_http_upstream_rr_peer_t *peer;
+
+ ssl_session = ngx_ssl_get_session(pc->connection);
+
+ if (ssl_session == NULL) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "save session: %p", ssl_session);
+
+ peer = &rrp->peers->peer[rrp->current];
+
+ /* TODO: threads only mutex */
+ /* ngx_lock_mutex(rrp->peers->mutex); */
+
+ old_ssl_session = peer->ssl_session;
+ peer->ssl_session = ssl_session;
+
+ /* ngx_unlock_mutex(rrp->peers->mutex); */
+
+ if (old_ssl_session) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "old session: %p", old_ssl_session);
+
+ /* TODO: may block */
+
+ ngx_ssl_free_session(old_ssl_session);
+ }
+}
+
+
+static ngx_int_t
+ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc, void *data)
+{
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc, void *data)
+{
+ return;
+}
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream_round_robin.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream_round_robin.h
new file mode 100644
index 00000000000..9db82a63c0d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_upstream_round_robin.h
@@ -0,0 +1,90 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
+#define _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+ ngx_str_t name;
+ ngx_str_t server;
+
+ ngx_int_t current_weight;
+ ngx_int_t effective_weight;
+ ngx_int_t weight;
+
+ ngx_uint_t fails;
+ time_t accessed;
+ time_t checked;
+
+ ngx_uint_t max_fails;
+ time_t fail_timeout;
+
+ ngx_uint_t down; /* unsigned down:1; */
+
+#if (NGX_HTTP_SSL)
+ ngx_ssl_session_t *ssl_session; /* local to a process */
+#endif
+} ngx_http_upstream_rr_peer_t;
+
+
+typedef struct ngx_http_upstream_rr_peers_s ngx_http_upstream_rr_peers_t;
+
+struct ngx_http_upstream_rr_peers_s {
+ ngx_uint_t number;
+
+ /* ngx_mutex_t *mutex; */
+
+ ngx_uint_t total_weight;
+
+ unsigned single:1;
+ unsigned weighted:1;
+
+ ngx_str_t *name;
+
+ ngx_http_upstream_rr_peers_t *next;
+
+ ngx_http_upstream_rr_peer_t peer[1];
+};
+
+
+typedef struct {
+ ngx_http_upstream_rr_peers_t *peers;
+ ngx_uint_t current;
+ uintptr_t *tried;
+ uintptr_t data;
+} ngx_http_upstream_rr_peer_data_t;
+
+
+ngx_int_t ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
+ ngx_http_upstream_srv_conf_t *us);
+ngx_int_t ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us);
+ngx_int_t ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,
+ ngx_http_upstream_resolved_t *ur);
+ngx_int_t ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc,
+ void *data);
+void ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc,
+ void *data, ngx_uint_t state);
+
+#if (NGX_HTTP_SSL)
+ngx_int_t
+ ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,
+ void *data);
+void ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,
+ void *data);
+#endif
+
+
+#endif /* _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_variables.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_variables.c
new file mode 100644
index 00000000000..1b61c39d46f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_variables.c
@@ -0,0 +1,2613 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include <nginx.h>
+
+
+static ngx_int_t ngx_http_variable_request(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+#if 0
+static void ngx_http_variable_request_set(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+#endif
+static ngx_int_t ngx_http_variable_request_get_size(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static void ngx_http_variable_request_set_size(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_header(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_variable_cookies(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_headers(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_headers_internal(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data, u_char sep);
+
+static ngx_int_t ngx_http_variable_unknown_header_in(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_line(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+#if (NGX_HAVE_TCP_INFO)
+static ngx_int_t ngx_http_variable_tcpinfo(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+#endif
+
+static ngx_int_t ngx_http_variable_content_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_remote_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_scheme(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_https(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static void ngx_http_variable_set_args(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_document_root(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_realpath_root(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_filename(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_server_name(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_method(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_remote_user(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_bytes_sent(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_body_bytes_sent(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_pipe(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_completion(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_body(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_body_file(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_request_time(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_status(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_variable_sent_content_type(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_content_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_location(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_last_modified(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_connection(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_keep_alive(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_variable_connection(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_connection_requests(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+static ngx_int_t ngx_http_variable_nginx_version(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_hostname(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_pid(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_msec(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_time_iso8601(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_variable_time_local(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+/*
+ * TODO:
+ * Apache CGI: AUTH_TYPE, PATH_INFO (null), PATH_TRANSLATED
+ * REMOTE_HOST (null), REMOTE_IDENT (null),
+ * SERVER_SOFTWARE
+ *
+ * Apache SSI: DOCUMENT_NAME, LAST_MODIFIED, USER_NAME (file owner)
+ */
+
+/*
+ * the $http_host, $http_user_agent, $http_referer, and $http_via
+ * variables may be handled by generic
+ * ngx_http_variable_unknown_header_in(), but for performance reasons
+ * they are handled using dedicated entries
+ */
+
+static ngx_http_variable_t ngx_http_core_variables[] = {
+
+ { ngx_string("http_host"), NULL, ngx_http_variable_header,
+ offsetof(ngx_http_request_t, headers_in.host), 0, 0 },
+
+ { ngx_string("http_user_agent"), NULL, ngx_http_variable_header,
+ offsetof(ngx_http_request_t, headers_in.user_agent), 0, 0 },
+
+ { ngx_string("http_referer"), NULL, ngx_http_variable_header,
+ offsetof(ngx_http_request_t, headers_in.referer), 0, 0 },
+
+#if (NGX_HTTP_GZIP)
+ { ngx_string("http_via"), NULL, ngx_http_variable_header,
+ offsetof(ngx_http_request_t, headers_in.via), 0, 0 },
+#endif
+
+#if (NGX_HTTP_X_FORWARDED_FOR)
+ { ngx_string("http_x_forwarded_for"), NULL, ngx_http_variable_headers,
+ offsetof(ngx_http_request_t, headers_in.x_forwarded_for), 0, 0 },
+#endif
+
+ { ngx_string("http_cookie"), NULL, ngx_http_variable_cookies,
+ offsetof(ngx_http_request_t, headers_in.cookies), 0, 0 },
+
+ { ngx_string("content_length"), NULL, ngx_http_variable_content_length,
+ 0, 0, 0 },
+
+ { ngx_string("content_type"), NULL, ngx_http_variable_header,
+ offsetof(ngx_http_request_t, headers_in.content_type), 0, 0 },
+
+ { ngx_string("host"), NULL, ngx_http_variable_host, 0, 0, 0 },
+
+ { ngx_string("binary_remote_addr"), NULL,
+ ngx_http_variable_binary_remote_addr, 0, 0, 0 },
+
+ { ngx_string("remote_addr"), NULL, ngx_http_variable_remote_addr, 0, 0, 0 },
+
+ { ngx_string("remote_port"), NULL, ngx_http_variable_remote_port, 0, 0, 0 },
+
+ { ngx_string("proxy_protocol_addr"), NULL,
+ ngx_http_variable_proxy_protocol_addr, 0, 0, 0 },
+
+ { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 },
+
+ { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 },
+
+ { ngx_string("server_protocol"), NULL, ngx_http_variable_request,
+ offsetof(ngx_http_request_t, http_protocol), 0, 0 },
+
+ { ngx_string("scheme"), NULL, ngx_http_variable_scheme, 0, 0, 0 },
+
+ { ngx_string("https"), NULL, ngx_http_variable_https, 0, 0, 0 },
+
+ { ngx_string("request_uri"), NULL, ngx_http_variable_request,
+ offsetof(ngx_http_request_t, unparsed_uri), 0, 0 },
+
+ { ngx_string("uri"), NULL, ngx_http_variable_request,
+ offsetof(ngx_http_request_t, uri),
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("document_uri"), NULL, ngx_http_variable_request,
+ offsetof(ngx_http_request_t, uri),
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("request"), NULL, ngx_http_variable_request_line, 0, 0, 0 },
+
+ { ngx_string("document_root"), NULL,
+ ngx_http_variable_document_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("realpath_root"), NULL,
+ ngx_http_variable_realpath_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("query_string"), NULL, ngx_http_variable_request,
+ offsetof(ngx_http_request_t, args),
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("args"),
+ ngx_http_variable_set_args,
+ ngx_http_variable_request,
+ offsetof(ngx_http_request_t, args),
+ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("is_args"), NULL, ngx_http_variable_is_args,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("request_filename"), NULL,
+ ngx_http_variable_request_filename, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("server_name"), NULL, ngx_http_variable_server_name, 0, 0, 0 },
+
+ { ngx_string("request_method"), NULL,
+ ngx_http_variable_request_method, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("remote_user"), NULL, ngx_http_variable_remote_user, 0, 0, 0 },
+
+ { ngx_string("bytes_sent"), NULL, ngx_http_variable_bytes_sent,
+ 0, 0, 0 },
+
+ { ngx_string("body_bytes_sent"), NULL, ngx_http_variable_body_bytes_sent,
+ 0, 0, 0 },
+
+ { ngx_string("pipe"), NULL, ngx_http_variable_pipe,
+ 0, 0, 0 },
+
+ { ngx_string("request_completion"), NULL,
+ ngx_http_variable_request_completion,
+ 0, 0, 0 },
+
+ { ngx_string("request_body"), NULL,
+ ngx_http_variable_request_body,
+ 0, 0, 0 },
+
+ { ngx_string("request_body_file"), NULL,
+ ngx_http_variable_request_body_file,
+ 0, 0, 0 },
+
+ { ngx_string("request_length"), NULL, ngx_http_variable_request_length,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("request_time"), NULL, ngx_http_variable_request_time,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("status"), NULL,
+ ngx_http_variable_status, 0,
+ NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("sent_http_content_type"), NULL,
+ ngx_http_variable_sent_content_type, 0, 0, 0 },
+
+ { ngx_string("sent_http_content_length"), NULL,
+ ngx_http_variable_sent_content_length, 0, 0, 0 },
+
+ { ngx_string("sent_http_location"), NULL,
+ ngx_http_variable_sent_location, 0, 0, 0 },
+
+ { ngx_string("sent_http_last_modified"), NULL,
+ ngx_http_variable_sent_last_modified, 0, 0, 0 },
+
+ { ngx_string("sent_http_connection"), NULL,
+ ngx_http_variable_sent_connection, 0, 0, 0 },
+
+ { ngx_string("sent_http_keep_alive"), NULL,
+ ngx_http_variable_sent_keep_alive, 0, 0, 0 },
+
+ { ngx_string("sent_http_transfer_encoding"), NULL,
+ ngx_http_variable_sent_transfer_encoding, 0, 0, 0 },
+
+ { ngx_string("sent_http_cache_control"), NULL, ngx_http_variable_headers,
+ offsetof(ngx_http_request_t, headers_out.cache_control), 0, 0 },
+
+ { ngx_string("limit_rate"), ngx_http_variable_request_set_size,
+ ngx_http_variable_request_get_size,
+ offsetof(ngx_http_request_t, limit_rate),
+ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("connection"), NULL,
+ ngx_http_variable_connection, 0, 0, 0 },
+
+ { ngx_string("connection_requests"), NULL,
+ ngx_http_variable_connection_requests, 0, 0, 0 },
+
+ { ngx_string("nginx_version"), NULL, ngx_http_variable_nginx_version,
+ 0, 0, 0 },
+
+ { ngx_string("hostname"), NULL, ngx_http_variable_hostname,
+ 0, 0, 0 },
+
+ { ngx_string("pid"), NULL, ngx_http_variable_pid,
+ 0, 0, 0 },
+
+ { ngx_string("msec"), NULL, ngx_http_variable_msec,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("time_iso8601"), NULL, ngx_http_variable_time_iso8601,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("time_local"), NULL, ngx_http_variable_time_local,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+#if (NGX_HAVE_TCP_INFO)
+ { ngx_string("tcpinfo_rtt"), NULL, ngx_http_variable_tcpinfo,
+ 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("tcpinfo_rttvar"), NULL, ngx_http_variable_tcpinfo,
+ 1, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("tcpinfo_snd_cwnd"), NULL, ngx_http_variable_tcpinfo,
+ 2, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+
+ { ngx_string("tcpinfo_rcv_space"), NULL, ngx_http_variable_tcpinfo,
+ 3, NGX_HTTP_VAR_NOCACHEABLE, 0 },
+#endif
+
+ { ngx_null_string, NULL, NULL, 0, 0, 0 }
+};
+
+
+ngx_http_variable_value_t ngx_http_variable_null_value =
+ ngx_http_variable("");
+ngx_http_variable_value_t ngx_http_variable_true_value =
+ ngx_http_variable("1");
+
+
+ngx_http_variable_t *
+ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)
+{
+ ngx_int_t rc;
+ ngx_uint_t i;
+ ngx_hash_key_t *key;
+ ngx_http_variable_t *v;
+ ngx_http_core_main_conf_t *cmcf;
+
+ if (name->len == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"$\"");
+ return NULL;
+ }
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ key = cmcf->variables_keys->keys.elts;
+ for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) {
+ if (name->len != key[i].key.len
+ || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0)
+ {
+ continue;
+ }
+
+ v = key[i].value;
+
+ if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the duplicate \"%V\" variable", name);
+ return NULL;
+ }
+
+ return v;
+ }
+
+ v = ngx_palloc(cf->pool, sizeof(ngx_http_variable_t));
+ if (v == NULL) {
+ return NULL;
+ }
+
+ v->name.len = name->len;
+ v->name.data = ngx_pnalloc(cf->pool, name->len);
+ if (v->name.data == NULL) {
+ return NULL;
+ }
+
+ ngx_strlow(v->name.data, name->data, name->len);
+
+ v->set_handler = NULL;
+ v->get_handler = NULL;
+ v->data = 0;
+ v->flags = flags;
+ v->index = 0;
+
+ rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0);
+
+ if (rc == NGX_ERROR) {
+ return NULL;
+ }
+
+ if (rc == NGX_BUSY) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "conflicting variable name \"%V\"", name);
+ return NULL;
+ }
+
+ return v;
+}
+
+
+ngx_int_t
+ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name)
+{
+ ngx_uint_t i;
+ ngx_http_variable_t *v;
+ ngx_http_core_main_conf_t *cmcf;
+
+ if (name->len == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid variable name \"$\"");
+ return NGX_ERROR;
+ }
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ v = cmcf->variables.elts;
+
+ if (v == NULL) {
+ if (ngx_array_init(&cmcf->variables, cf->pool, 4,
+ sizeof(ngx_http_variable_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ } else {
+ for (i = 0; i < cmcf->variables.nelts; i++) {
+ if (name->len != v[i].name.len
+ || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)
+ {
+ continue;
+ }
+
+ return i;
+ }
+ }
+
+ v = ngx_array_push(&cmcf->variables);
+ if (v == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->name.len = name->len;
+ v->name.data = ngx_pnalloc(cf->pool, name->len);
+ if (v->name.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_strlow(v->name.data, name->data, name->len);
+
+ v->set_handler = NULL;
+ v->get_handler = NULL;
+ v->data = 0;
+ v->flags = 0;
+ v->index = cmcf->variables.nelts - 1;
+
+ return v->index;
+}
+
+
+ngx_http_variable_value_t *
+ngx_http_get_indexed_variable(ngx_http_request_t *r, ngx_uint_t index)
+{
+ ngx_http_variable_t *v;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ if (cmcf->variables.nelts <= index) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "unknown variable index: %ui", index);
+ return NULL;
+ }
+
+ if (r->variables[index].not_found || r->variables[index].valid) {
+ return &r->variables[index];
+ }
+
+ v = cmcf->variables.elts;
+
+ if (v[index].get_handler(r, &r->variables[index], v[index].data)
+ == NGX_OK)
+ {
+ if (v[index].flags & NGX_HTTP_VAR_NOCACHEABLE) {
+ r->variables[index].no_cacheable = 1;
+ }
+
+ return &r->variables[index];
+ }
+
+ r->variables[index].valid = 0;
+ r->variables[index].not_found = 1;
+
+ return NULL;
+}
+
+
+ngx_http_variable_value_t *
+ngx_http_get_flushed_variable(ngx_http_request_t *r, ngx_uint_t index)
+{
+ ngx_http_variable_value_t *v;
+
+ v = &r->variables[index];
+
+ if (v->valid || v->not_found) {
+ if (!v->no_cacheable) {
+ return v;
+ }
+
+ v->valid = 0;
+ v->not_found = 0;
+ }
+
+ return ngx_http_get_indexed_variable(r, index);
+}
+
+
+ngx_http_variable_value_t *
+ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key)
+{
+ ngx_http_variable_t *v;
+ ngx_http_variable_value_t *vv;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len);
+
+ if (v) {
+ if (v->flags & NGX_HTTP_VAR_INDEXED) {
+ return ngx_http_get_flushed_variable(r, v->index);
+
+ } else {
+
+ vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
+
+ if (vv && v->get_handler(r, vv, v->data) == NGX_OK) {
+ return vv;
+ }
+
+ return NULL;
+ }
+ }
+
+ vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
+ if (vv == NULL) {
+ return NULL;
+ }
+
+ if (ngx_strncmp(name->data, "http_", 5) == 0) {
+
+ if (ngx_http_variable_unknown_header_in(r, vv, (uintptr_t) name)
+ == NGX_OK)
+ {
+ return vv;
+ }
+
+ return NULL;
+ }
+
+ if (ngx_strncmp(name->data, "sent_http_", 10) == 0) {
+
+ if (ngx_http_variable_unknown_header_out(r, vv, (uintptr_t) name)
+ == NGX_OK)
+ {
+ return vv;
+ }
+
+ return NULL;
+ }
+
+ if (ngx_strncmp(name->data, "upstream_http_", 14) == 0) {
+
+ if (ngx_http_upstream_header_variable(r, vv, (uintptr_t) name)
+ == NGX_OK)
+ {
+ return vv;
+ }
+
+ return NULL;
+ }
+
+ if (ngx_strncmp(name->data, "cookie_", 7) == 0) {
+
+ if (ngx_http_variable_cookie(r, vv, (uintptr_t) name) == NGX_OK) {
+ return vv;
+ }
+
+ return NULL;
+ }
+
+ if (ngx_strncmp(name->data, "upstream_cookie_", 16) == 0) {
+
+ if (ngx_http_upstream_cookie_variable(r, vv, (uintptr_t) name)
+ == NGX_OK)
+ {
+ return vv;
+ }
+
+ return NULL;
+ }
+
+ if (ngx_strncmp(name->data, "arg_", 4) == 0) {
+
+ if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) {
+ return vv;
+ }
+
+ return NULL;
+ }
+
+ vv->not_found = 1;
+
+ return vv;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_str_t *s;
+
+ s = (ngx_str_t *) ((char *) r + data);
+
+ if (s->data) {
+ v->len = s->len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = s->data;
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+#if 0
+
+static void
+ngx_http_variable_request_set(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_str_t *s;
+
+ s = (ngx_str_t *) ((char *) r + data);
+
+ s->len = v->len;
+ s->data = v->data;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_variable_request_get_size(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ size_t *sp;
+
+ sp = (size_t *) ((char *) r + data);
+
+ v->data = ngx_pnalloc(r->pool, NGX_SIZE_T_LEN);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(v->data, "%uz", *sp) - v->data;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_variable_request_set_size(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ssize_t s, *sp;
+ ngx_str_t val;
+
+ val.len = v->len;
+ val.data = v->data;
+
+ s = ngx_parse_size(&val);
+
+ if (s == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "invalid size \"%V\"", &val);
+ return;
+ }
+
+ sp = (ssize_t *) ((char *) r + data);
+
+ *sp = s;
+
+ return;
+}
+
+
+static ngx_int_t
+ngx_http_variable_header(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_table_elt_t *h;
+
+ h = *(ngx_table_elt_t **) ((char *) r + data);
+
+ if (h) {
+ v->len = h->value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = h->value.data;
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_cookies(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ return ngx_http_variable_headers_internal(r, v, data, ';');
+}
+
+
+static ngx_int_t
+ngx_http_variable_headers(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ return ngx_http_variable_headers_internal(r, v, data, ',');
+}
+
+
+static ngx_int_t
+ngx_http_variable_headers_internal(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data, u_char sep)
+{
+ size_t len;
+ u_char *p, *end;
+ ngx_uint_t i, n;
+ ngx_array_t *a;
+ ngx_table_elt_t **h;
+
+ a = (ngx_array_t *) ((char *) r + data);
+
+ n = a->nelts;
+ h = a->elts;
+
+ len = 0;
+
+ for (i = 0; i < n; i++) {
+
+ if (h[i]->hash == 0) {
+ continue;
+ }
+
+ len += h[i]->value.len + 2;
+ }
+
+ if (len == 0) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ len -= 2;
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ if (n == 1) {
+ v->len = (*h)->value.len;
+ v->data = (*h)->value.data;
+
+ return NGX_OK;
+ }
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = len;
+ v->data = p;
+
+ end = p + len;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (h[i]->hash == 0) {
+ continue;
+ }
+
+ p = ngx_copy(p, h[i]->value.data, h[i]->value.len);
+
+ if (p == end) {
+ break;
+ }
+
+ *p++ = sep; *p++ = ' ';
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_unknown_header_in(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
+ &r->headers_in.headers.part,
+ sizeof("http_") - 1);
+}
+
+
+static ngx_int_t
+ngx_http_variable_unknown_header_out(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ return ngx_http_variable_unknown_header(v, (ngx_str_t *) data,
+ &r->headers_out.headers.part,
+ sizeof("sent_http_") - 1);
+}
+
+
+ngx_int_t
+ngx_http_variable_unknown_header(ngx_http_variable_value_t *v, ngx_str_t *var,
+ ngx_list_part_t *part, size_t prefix)
+{
+ u_char ch;
+ ngx_uint_t i, n;
+ ngx_table_elt_t *header;
+
+ header = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+
+ part = part->next;
+ header = part->elts;
+ i = 0;
+ }
+
+ if (header[i].hash == 0) {
+ continue;
+ }
+
+ for (n = 0; n + prefix < var->len && n < header[i].key.len; n++) {
+ ch = header[i].key.data[n];
+
+ if (ch >= 'A' && ch <= 'Z') {
+ ch |= 0x20;
+
+ } else if (ch == '-') {
+ ch = '_';
+ }
+
+ if (var->data[n + prefix] != ch) {
+ break;
+ }
+ }
+
+ if (n + prefix == var->len && n == header[i].key.len) {
+ v->len = header[i].value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = header[i].value.data;
+
+ return NGX_OK;
+ }
+ }
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_line(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p, *s;
+
+ s = r->request_line.data;
+
+ if (s == NULL) {
+ s = r->request_start;
+
+ if (s == NULL) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ for (p = s; p < r->header_in->last; p++) {
+ if (*p == CR || *p == LF) {
+ break;
+ }
+ }
+
+ r->request_line.len = p - s;
+ r->request_line.data = s;
+ }
+
+ v->len = r->request_line.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = s;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_str_t *name = (ngx_str_t *) data;
+
+ ngx_str_t cookie, s;
+
+ s.len = name->len - (sizeof("cookie_") - 1);
+ s.data = name->data + sizeof("cookie_") - 1;
+
+ if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &s, &cookie)
+ == NGX_DECLINED)
+ {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->len = cookie.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = cookie.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_str_t *name = (ngx_str_t *) data;
+
+ u_char *arg;
+ size_t len;
+ ngx_str_t value;
+
+ len = name->len - (sizeof("arg_") - 1);
+ arg = name->data + sizeof("arg_") - 1;
+
+ if (ngx_http_arg(r, arg, len, &value) != NGX_OK) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->data = value.data;
+ v->len = value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HAVE_TCP_INFO)
+
+static ngx_int_t
+ngx_http_variable_tcpinfo(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ struct tcp_info ti;
+ socklen_t len;
+ uint32_t value;
+
+ len = sizeof(struct tcp_info);
+ if (getsockopt(r->connection->fd, IPPROTO_TCP, TCP_INFO, &ti, &len) == -1) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ switch (data) {
+ case 0:
+ value = ti.tcpi_rtt;
+ break;
+
+ case 1:
+ value = ti.tcpi_rttvar;
+ break;
+
+ case 2:
+ value = ti.tcpi_snd_cwnd;
+ break;
+
+ case 3:
+ value = ti.tcpi_rcv_space;
+ break;
+
+ /* suppress warning */
+ default:
+ value = 0;
+ break;
+ }
+
+ v->len = ngx_sprintf(v->data, "%uD", value) - v->data;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_http_variable_content_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ if (r->headers_in.content_length) {
+ v->len = r->headers_in.content_length->value.len;
+ v->data = r->headers_in.content_length->value.data;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ } else if (r->headers_in.content_length_n >= 0) {
+ p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%O", r->headers_in.content_length_n) - p;
+ v->data = p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ ngx_http_core_srv_conf_t *cscf;
+
+ if (r->headers_in.server.len) {
+ v->len = r->headers_in.server.len;
+ v->data = r->headers_in.server.data;
+
+ } else {
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ v->len = cscf->server_name.len;
+ v->data = cscf->server_name.data;
+ }
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ switch (r->connection->sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
+
+ v->len = sizeof(struct in6_addr);
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = sin6->sin6_addr.s6_addr;
+
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) r->connection->sockaddr;
+
+ v->len = sizeof(in_addr_t);
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) &sin->sin_addr;
+
+ break;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_remote_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ v->len = r->connection->addr_text.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->connection->addr_text.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_remote_port(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_uint_t port;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ v->len = 0;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ switch (r->connection->sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
+ port = ntohs(sin6->sin6_port);
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ port = 0;
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) r->connection->sockaddr;
+ port = ntohs(sin->sin_port);
+ break;
+ }
+
+ if (port > 0 && port < 65536) {
+ v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ v->len = r->connection->proxy_protocol_addr.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->connection->proxy_protocol_addr.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_server_addr(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_str_t s;
+ u_char addr[NGX_SOCKADDR_STRLEN];
+
+ s.len = NGX_SOCKADDR_STRLEN;
+ s.data = addr;
+
+ if (ngx_connection_local_sockaddr(r->connection, &s, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ s.data = ngx_pnalloc(r->pool, s.len);
+ if (s.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s.data, addr, s.len);
+
+ v->len = s.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = s.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_server_port(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_uint_t port;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ v->len = 0;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ if (ngx_connection_local_sockaddr(r->connection, NULL, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ v->data = ngx_pnalloc(r->pool, sizeof("65535") - 1);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ switch (r->connection->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) r->connection->local_sockaddr;
+ port = ntohs(sin6->sin6_port);
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ port = 0;
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) r->connection->local_sockaddr;
+ port = ntohs(sin->sin_port);
+ break;
+ }
+
+ if (port > 0 && port < 65536) {
+ v->len = ngx_sprintf(v->data, "%ui", port) - v->data;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_scheme(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+#if (NGX_HTTP_SSL)
+
+ if (r->connection->ssl) {
+ v->len = sizeof("https") - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "https";
+
+ return NGX_OK;
+ }
+
+#endif
+
+ v->len = sizeof("http") - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "http";
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_https(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+#if (NGX_HTTP_SSL)
+
+ if (r->connection->ssl) {
+ v->len = sizeof("on") - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "on";
+
+ return NGX_OK;
+ }
+
+#endif
+
+ *v = ngx_http_variable_null_value;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_http_variable_set_args(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ r->args.len = v->len;
+ r->args.data = v->data;
+ r->valid_unparsed_uri = 0;
+}
+
+
+static ngx_int_t
+ngx_http_variable_is_args(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ if (r->args.len == 0) {
+ v->len = 0;
+ v->data = NULL;
+ return NGX_OK;
+ }
+
+ v->len = 1;
+ v->data = (u_char *) "?";
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_document_root(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_str_t path;
+ ngx_http_core_loc_conf_t *clcf;
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->root_lengths == NULL) {
+ v->len = clcf->root.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = clcf->root.data;
+
+ } else {
+ if (ngx_http_script_run(r, &path, clcf->root_lengths->elts, 0,
+ clcf->root_values->elts)
+ == NULL)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, &path)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ v->len = path.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = path.data;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_realpath_root(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *real;
+ size_t len;
+ ngx_str_t path;
+ ngx_http_core_loc_conf_t *clcf;
+#if (NGX_HAVE_MAX_PATH)
+ u_char buffer[NGX_MAX_PATH];
+#endif
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->root_lengths == NULL) {
+ path = clcf->root;
+
+ } else {
+ if (ngx_http_script_run(r, &path, clcf->root_lengths->elts, 1,
+ clcf->root_values->elts)
+ == NULL)
+ {
+ return NGX_ERROR;
+ }
+
+ path.data[path.len - 1] = '\0';
+
+ if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, &path)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+#if (NGX_HAVE_MAX_PATH)
+ real = buffer;
+#else
+ real = NULL;
+#endif
+
+ real = ngx_realpath(path.data, real);
+
+ if (real == NULL) {
+ ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+ ngx_realpath_n " \"%s\" failed", path.data);
+ return NGX_ERROR;
+ }
+
+ len = ngx_strlen(real);
+
+ v->data = ngx_pnalloc(r->pool, len);
+ if (v->data == NULL) {
+#if !(NGX_HAVE_MAX_PATH)
+ ngx_free(real);
+#endif
+ return NGX_ERROR;
+ }
+
+ v->len = len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ ngx_memcpy(v->data, real, len);
+
+#if !(NGX_HAVE_MAX_PATH)
+ ngx_free(real);
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_filename(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ size_t root;
+ ngx_str_t path;
+
+ if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
+ return NGX_ERROR;
+ }
+
+ /* ngx_http_map_uri_to_path() allocates memory for terminating '\0' */
+
+ v->len = path.len - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = path.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_server_name(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_core_srv_conf_t *cscf;
+
+ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
+
+ v->len = cscf->server_name.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = cscf->server_name.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_method(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->main->method_name.data) {
+ v->len = r->main->method_name.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->main->method_name.data;
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_remote_user(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_int_t rc;
+
+ rc = ngx_http_auth_basic_user(r);
+
+ if (rc == NGX_DECLINED) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ v->len = r->headers_in.user.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->headers_in.user.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_bytes_sent(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%O", r->connection->sent) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_body_bytes_sent(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ off_t sent;
+ u_char *p;
+
+ sent = r->connection->sent - r->header_size;
+
+ if (sent < 0) {
+ sent = 0;
+ }
+
+ p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%O", sent) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_pipe(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ v->data = (u_char *) (r->pipeline ? "p" : ".");
+ v->len = 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_status(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_uint_t status;
+
+ v->data = ngx_pnalloc(r->pool, NGX_INT_T_LEN);
+ if (v->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (r->err_status) {
+ status = r->err_status;
+
+ } else if (r->headers_out.status) {
+ status = r->headers_out.status;
+
+ } else if (r->http_version == NGX_HTTP_VERSION_9) {
+ status = 9;
+
+ } else {
+ status = 0;
+ }
+
+ v->len = ngx_sprintf(v->data, "%03ui", status) - v->data;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_content_type(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->headers_out.content_type.len) {
+ v->len = r->headers_out.content_type.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->headers_out.content_type.data;
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_content_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ if (r->headers_out.content_length) {
+ v->len = r->headers_out.content_length->value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->headers_out.content_length->value.data;
+
+ return NGX_OK;
+ }
+
+ if (r->headers_out.content_length_n >= 0) {
+ p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%O", r->headers_out.content_length_n) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+ }
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_location(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_str_t name;
+
+ if (r->headers_out.location) {
+ v->len = r->headers_out.location->value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->headers_out.location->value.data;
+
+ return NGX_OK;
+ }
+
+ ngx_str_set(&name, "sent_http_location");
+
+ return ngx_http_variable_unknown_header(v, &name,
+ &r->headers_out.headers.part,
+ sizeof("sent_http_") - 1);
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_last_modified(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ if (r->headers_out.last_modified) {
+ v->len = r->headers_out.last_modified->value.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->headers_out.last_modified->value.data;
+
+ return NGX_OK;
+ }
+
+ if (r->headers_out.last_modified_time >= 0) {
+ p = ngx_pnalloc(r->pool, sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_http_time(p, r->headers_out.last_modified_time) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+ }
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_connection(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ size_t len;
+ char *p;
+
+ if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
+ len = sizeof("upgrade") - 1;
+ p = "upgrade";
+
+ } else if (r->keepalive) {
+ len = sizeof("keep-alive") - 1;
+ p = "keep-alive";
+
+ } else {
+ len = sizeof("close") - 1;
+ p = "close";
+ }
+
+ v->len = len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_keep_alive(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ ngx_http_core_loc_conf_t *clcf;
+
+ if (r->keepalive) {
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (clcf->keepalive_header) {
+
+ p = ngx_pnalloc(r->pool, sizeof("timeout=") - 1 + NGX_TIME_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "timeout=%T", clcf->keepalive_header) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+ }
+ }
+
+ v->not_found = 1;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->chunked) {
+ v->len = sizeof("chunked") - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "chunked";
+
+ } else {
+ v->not_found = 1;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_completion(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->request_complete) {
+ v->len = 2;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "OK";
+
+ return NGX_OK;
+ }
+
+ v->len = 0;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) "";
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_body(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ size_t len;
+ ngx_buf_t *buf;
+ ngx_chain_t *cl;
+
+ if (r->request_body == NULL
+ || r->request_body->bufs == NULL
+ || r->request_body->temp_file)
+ {
+ v->not_found = 1;
+
+ return NGX_OK;
+ }
+
+ cl = r->request_body->bufs;
+ buf = cl->buf;
+
+ if (cl->next == NULL) {
+ v->len = buf->last - buf->pos;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = buf->pos;
+
+ return NGX_OK;
+ }
+
+ len = buf->last - buf->pos;
+ cl = cl->next;
+
+ for ( /* void */ ; cl; cl = cl->next) {
+ buf = cl->buf;
+ len += buf->last - buf->pos;
+ }
+
+ p = ngx_pnalloc(r->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->data = p;
+ cl = r->request_body->bufs;
+
+ for ( /* void */ ; cl; cl = cl->next) {
+ buf = cl->buf;
+ p = ngx_cpymem(p, buf->pos, buf->last - buf->pos);
+ }
+
+ v->len = len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_body_file(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ if (r->request_body == NULL || r->request_body->temp_file == NULL) {
+ v->not_found = 1;
+
+ return NGX_OK;
+ }
+
+ v->len = r->request_body->temp_file->file.name.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = r->request_body->temp_file->file.name.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_length(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%O", r->request_length) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_request_time(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ ngx_time_t *tp;
+ ngx_msec_int_t ms;
+
+ p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ tp = ngx_timeofday();
+
+ ms = (ngx_msec_int_t)
+ ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));
+ ms = ngx_max(ms, 0);
+
+ v->len = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_connection(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, NGX_ATOMIC_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%uA", r->connection->number) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_connection_requests(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, NGX_INT_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%ui", r->connection->requests) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_nginx_version(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ v->len = sizeof(NGINX_VERSION) - 1;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = (u_char *) NGINX_VERSION;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_hostname(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ v->len = ngx_cycle->hostname.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = ngx_cycle->hostname.data;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_pid(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, NGX_INT64_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%P", ngx_pid) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_msec(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+ ngx_time_t *tp;
+
+ p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ tp = ngx_timeofday();
+
+ v->len = ngx_sprintf(p, "%T.%03M", tp->sec, tp->msec) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_time_iso8601(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, ngx_cached_http_log_iso8601.len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(p, ngx_cached_http_log_iso8601.data,
+ ngx_cached_http_log_iso8601.len);
+
+ v->len = ngx_cached_http_log_iso8601.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_variable_time_local(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ u_char *p;
+
+ p = ngx_pnalloc(r->pool, ngx_cached_http_log_time.len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(p, ngx_cached_http_log_time.data, ngx_cached_http_log_time.len);
+
+ v->len = ngx_cached_http_log_time.len;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+}
+
+
+void *
+ngx_http_map_find(ngx_http_request_t *r, ngx_http_map_t *map, ngx_str_t *match)
+{
+ void *value;
+ u_char *low;
+ size_t len;
+ ngx_uint_t key;
+
+ len = match->len;
+
+ if (len) {
+ low = ngx_pnalloc(r->pool, len);
+ if (low == NULL) {
+ return NULL;
+ }
+
+ } else {
+ low = NULL;
+ }
+
+ key = ngx_hash_strlow(low, match->data, len);
+
+ value = ngx_hash_find_combined(&map->hash, key, low, len);
+ if (value) {
+ return value;
+ }
+
+#if (NGX_PCRE)
+
+ if (len && map->nregex) {
+ ngx_int_t n;
+ ngx_uint_t i;
+ ngx_http_map_regex_t *reg;
+
+ reg = map->regex;
+
+ for (i = 0; i < map->nregex; i++) {
+
+ n = ngx_http_regex_exec(r, reg[i].regex, match);
+
+ if (n == NGX_OK) {
+ return reg[i].value;
+ }
+
+ if (n == NGX_DECLINED) {
+ continue;
+ }
+
+ /* NGX_ERROR */
+
+ return NULL;
+ }
+ }
+
+#endif
+
+ return NULL;
+}
+
+
+#if (NGX_PCRE)
+
+static ngx_int_t
+ngx_http_variable_not_found(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+ uintptr_t data)
+{
+ v->not_found = 1;
+ return NGX_OK;
+}
+
+
+ngx_http_regex_t *
+ngx_http_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc)
+{
+ u_char *p;
+ size_t size;
+ ngx_str_t name;
+ ngx_uint_t i, n;
+ ngx_http_variable_t *v;
+ ngx_http_regex_t *re;
+ ngx_http_regex_variable_t *rv;
+ ngx_http_core_main_conf_t *cmcf;
+
+ rc->pool = cf->pool;
+
+ if (ngx_regex_compile(rc) != NGX_OK) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc->err);
+ return NULL;
+ }
+
+ re = ngx_pcalloc(cf->pool, sizeof(ngx_http_regex_t));
+ if (re == NULL) {
+ return NULL;
+ }
+
+ re->regex = rc->regex;
+ re->ncaptures = rc->captures;
+ re->name = rc->pattern;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+ cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures);
+
+ n = (ngx_uint_t) rc->named_captures;
+
+ if (n == 0) {
+ return re;
+ }
+
+ rv = ngx_palloc(rc->pool, n * sizeof(ngx_http_regex_variable_t));
+ if (rv == NULL) {
+ return NULL;
+ }
+
+ re->variables = rv;
+ re->nvariables = n;
+
+ size = rc->name_size;
+ p = rc->names;
+
+ for (i = 0; i < n; i++) {
+ rv[i].capture = 2 * ((p[0] << 8) + p[1]);
+
+ name.data = &p[2];
+ name.len = ngx_strlen(name.data);
+
+ v = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);
+ if (v == NULL) {
+ return NULL;
+ }
+
+ rv[i].index = ngx_http_get_variable_index(cf, &name);
+ if (rv[i].index == NGX_ERROR) {
+ return NULL;
+ }
+
+ v->get_handler = ngx_http_variable_not_found;
+
+ p += size;
+ }
+
+ return re;
+}
+
+
+ngx_int_t
+ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re, ngx_str_t *s)
+{
+ ngx_int_t rc, index;
+ ngx_uint_t i, n, len;
+ ngx_http_variable_value_t *vv;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
+
+ if (re->ncaptures) {
+ len = cmcf->ncaptures;
+
+ if (r->captures == NULL) {
+ r->captures = ngx_palloc(r->pool, len * sizeof(int));
+ if (r->captures == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ } else {
+ len = 0;
+ }
+
+ rc = ngx_regex_exec(re->regex, s, r->captures, len);
+
+ if (rc == NGX_REGEX_NO_MATCHED) {
+ return NGX_DECLINED;
+ }
+
+ if (rc < 0) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ ngx_regex_exec_n " failed: %i on \"%V\" using \"%V\"",
+ rc, s, &re->name);
+ return NGX_ERROR;
+ }
+
+ for (i = 0; i < re->nvariables; i++) {
+
+ n = re->variables[i].capture;
+ index = re->variables[i].index;
+ vv = &r->variables[index];
+
+ vv->len = r->captures[n + 1] - r->captures[n];
+ vv->valid = 1;
+ vv->no_cacheable = 0;
+ vv->not_found = 0;
+ vv->data = &s->data[r->captures[n]];
+
+#if (NGX_DEBUG)
+ {
+ ngx_http_variable_t *v;
+
+ v = cmcf->variables.elts;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http regex set $%V to \"%*s\"",
+ &v[index].name, vv->len, vv->data);
+ }
+#endif
+ }
+
+ r->ncaptures = rc * 2;
+ r->captures_data = s->data;
+
+ return NGX_OK;
+}
+
+#endif
+
+
+ngx_int_t
+ngx_http_variables_add_core_vars(ngx_conf_t *cf)
+{
+ ngx_int_t rc;
+ ngx_http_variable_t *cv, *v;
+ ngx_http_core_main_conf_t *cmcf;
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,
+ sizeof(ngx_hash_keys_arrays_t));
+ if (cmcf->variables_keys == NULL) {
+ return NGX_ERROR;
+ }
+
+ cmcf->variables_keys->pool = cf->pool;
+ cmcf->variables_keys->temp_pool = cf->pool;
+
+ if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ for (cv = ngx_http_core_variables; cv->name.len; cv++) {
+ v = ngx_palloc(cf->pool, sizeof(ngx_http_variable_t));
+ if (v == NULL) {
+ return NGX_ERROR;
+ }
+
+ *v = *cv;
+
+ rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v,
+ NGX_HASH_READONLY_KEY);
+
+ if (rc == NGX_OK) {
+ continue;
+ }
+
+ if (rc == NGX_BUSY) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "conflicting variable name \"%V\"", &v->name);
+ }
+
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_variables_init_vars(ngx_conf_t *cf)
+{
+ ngx_uint_t i, n;
+ ngx_hash_key_t *key;
+ ngx_hash_init_t hash;
+ ngx_http_variable_t *v, *av;
+ ngx_http_core_main_conf_t *cmcf;
+
+ /* set the handlers for the indexed http variables */
+
+ cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
+
+ v = cmcf->variables.elts;
+ key = cmcf->variables_keys->keys.elts;
+
+ for (i = 0; i < cmcf->variables.nelts; i++) {
+
+ for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
+
+ av = key[n].value;
+
+ if (av->get_handler
+ && v[i].name.len == key[n].key.len
+ && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len)
+ == 0)
+ {
+ v[i].get_handler = av->get_handler;
+ v[i].data = av->data;
+
+ av->flags |= NGX_HTTP_VAR_INDEXED;
+ v[i].flags = av->flags;
+
+ av->index = i;
+
+ goto next;
+ }
+ }
+
+ if (ngx_strncmp(v[i].name.data, "http_", 5) == 0) {
+ v[i].get_handler = ngx_http_variable_unknown_header_in;
+ v[i].data = (uintptr_t) &v[i].name;
+
+ continue;
+ }
+
+ if (ngx_strncmp(v[i].name.data, "sent_http_", 10) == 0) {
+ v[i].get_handler = ngx_http_variable_unknown_header_out;
+ v[i].data = (uintptr_t) &v[i].name;
+
+ continue;
+ }
+
+ if (ngx_strncmp(v[i].name.data, "upstream_http_", 14) == 0) {
+ v[i].get_handler = ngx_http_upstream_header_variable;
+ v[i].data = (uintptr_t) &v[i].name;
+ v[i].flags = NGX_HTTP_VAR_NOCACHEABLE;
+
+ continue;
+ }
+
+ if (ngx_strncmp(v[i].name.data, "cookie_", 7) == 0) {
+ v[i].get_handler = ngx_http_variable_cookie;
+ v[i].data = (uintptr_t) &v[i].name;
+
+ continue;
+ }
+
+ if (ngx_strncmp(v[i].name.data, "upstream_cookie_", 16) == 0) {
+ v[i].get_handler = ngx_http_upstream_cookie_variable;
+ v[i].data = (uintptr_t) &v[i].name;
+ v[i].flags = NGX_HTTP_VAR_NOCACHEABLE;
+
+ continue;
+ }
+
+ if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) {
+ v[i].get_handler = ngx_http_variable_argument;
+ v[i].data = (uintptr_t) &v[i].name;
+ v[i].flags = NGX_HTTP_VAR_NOCACHEABLE;
+
+ continue;
+ }
+
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "unknown \"%V\" variable", &v[i].name);
+
+ return NGX_ERROR;
+
+ next:
+ continue;
+ }
+
+
+ for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {
+ av = key[n].value;
+
+ if (av->flags & NGX_HTTP_VAR_NOHASH) {
+ key[n].key.data = NULL;
+ }
+ }
+
+
+ hash.hash = &cmcf->variables_hash;
+ hash.key = ngx_hash_key;
+ hash.max_size = cmcf->variables_hash_max_size;
+ hash.bucket_size = cmcf->variables_hash_bucket_size;
+ hash.name = "variables_hash";
+ hash.pool = cf->pool;
+ hash.temp_pool = NULL;
+
+ if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts,
+ cmcf->variables_keys->keys.nelts)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ cmcf->variables_keys = NULL;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_variables.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_variables.h
new file mode 100644
index 00000000000..829fab31e64
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_variables.h
@@ -0,0 +1,112 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_HTTP_VARIABLES_H_INCLUDED_
+#define _NGX_HTTP_VARIABLES_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef ngx_variable_value_t ngx_http_variable_value_t;
+
+#define ngx_http_variable(v) { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v }
+
+typedef struct ngx_http_variable_s ngx_http_variable_t;
+
+typedef void (*ngx_http_set_variable_pt) (ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+typedef ngx_int_t (*ngx_http_get_variable_pt) (ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+
+
+#define NGX_HTTP_VAR_CHANGEABLE 1
+#define NGX_HTTP_VAR_NOCACHEABLE 2
+#define NGX_HTTP_VAR_INDEXED 4
+#define NGX_HTTP_VAR_NOHASH 8
+
+
+struct ngx_http_variable_s {
+ ngx_str_t name; /* must be first to build the hash */
+ ngx_http_set_variable_pt set_handler;
+ ngx_http_get_variable_pt get_handler;
+ uintptr_t data;
+ ngx_uint_t flags;
+ ngx_uint_t index;
+};
+
+
+ngx_http_variable_t *ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name,
+ ngx_uint_t flags);
+ngx_int_t ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name);
+ngx_http_variable_value_t *ngx_http_get_indexed_variable(ngx_http_request_t *r,
+ ngx_uint_t index);
+ngx_http_variable_value_t *ngx_http_get_flushed_variable(ngx_http_request_t *r,
+ ngx_uint_t index);
+
+ngx_http_variable_value_t *ngx_http_get_variable(ngx_http_request_t *r,
+ ngx_str_t *name, ngx_uint_t key);
+
+ngx_int_t ngx_http_variable_unknown_header(ngx_http_variable_value_t *v,
+ ngx_str_t *var, ngx_list_part_t *part, size_t prefix);
+
+
+#if (NGX_PCRE)
+
+typedef struct {
+ ngx_uint_t capture;
+ ngx_int_t index;
+} ngx_http_regex_variable_t;
+
+
+typedef struct {
+ ngx_regex_t *regex;
+ ngx_uint_t ncaptures;
+ ngx_http_regex_variable_t *variables;
+ ngx_uint_t nvariables;
+ ngx_str_t name;
+} ngx_http_regex_t;
+
+
+typedef struct {
+ ngx_http_regex_t *regex;
+ void *value;
+} ngx_http_map_regex_t;
+
+
+ngx_http_regex_t *ngx_http_regex_compile(ngx_conf_t *cf,
+ ngx_regex_compile_t *rc);
+ngx_int_t ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re,
+ ngx_str_t *s);
+
+#endif
+
+
+typedef struct {
+ ngx_hash_combined_t hash;
+#if (NGX_PCRE)
+ ngx_http_map_regex_t *regex;
+ ngx_uint_t nregex;
+#endif
+} ngx_http_map_t;
+
+
+void *ngx_http_map_find(ngx_http_request_t *r, ngx_http_map_t *map,
+ ngx_str_t *match);
+
+
+ngx_int_t ngx_http_variables_add_core_vars(ngx_conf_t *cf);
+ngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf);
+
+
+extern ngx_http_variable_value_t ngx_http_variable_null_value;
+extern ngx_http_variable_value_t ngx_http_variable_true_value;
+
+
+#endif /* _NGX_HTTP_VARIABLES_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_write_filter_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_write_filter_module.c
new file mode 100644
index 00000000000..83cb1fa1e98
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/http/ngx_http_write_filter_module.c
@@ -0,0 +1,318 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+static ngx_int_t ngx_http_write_filter_init(ngx_conf_t *cf);
+
+
+static ngx_http_module_t ngx_http_write_filter_module_ctx = {
+ NULL, /* preconfiguration */
+ ngx_http_write_filter_init, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL, /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_write_filter_module = {
+ NGX_MODULE_V1,
+ &ngx_http_write_filter_module_ctx, /* module context */
+ NULL, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+ngx_int_t
+ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+ off_t size, sent, nsent, limit;
+ ngx_uint_t last, flush;
+ ngx_msec_t delay;
+ ngx_chain_t *cl, *ln, **ll, *chain;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+
+ if (c->error) {
+ return NGX_ERROR;
+ }
+
+ size = 0;
+ flush = 0;
+ last = 0;
+ ll = &r->out;
+
+ /* find the size, the flush point and the last link of the saved chain */
+
+ for (cl = r->out; cl; cl = cl->next) {
+ ll = &cl->next;
+
+ ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "write old buf t:%d f:%d %p, pos %p, size: %z "
+ "file: %O, size: %z",
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+
+#if 1
+ if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "zero size buf in writer "
+ "t:%d r:%d f:%d %p %p-%p %p %O-%O",
+ cl->buf->temporary,
+ cl->buf->recycled,
+ cl->buf->in_file,
+ cl->buf->start,
+ cl->buf->pos,
+ cl->buf->last,
+ cl->buf->file,
+ cl->buf->file_pos,
+ cl->buf->file_last);
+
+ ngx_debug_point();
+ return NGX_ERROR;
+ }
+#endif
+
+ size += ngx_buf_size(cl->buf);
+
+ if (cl->buf->flush || cl->buf->recycled) {
+ flush = 1;
+ }
+
+ if (cl->buf->last_buf) {
+ last = 1;
+ }
+ }
+
+ /* add the new chain to the existent one */
+
+ for (ln = in; ln; ln = ln->next) {
+ cl = ngx_alloc_chain_link(r->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = ln->buf;
+ *ll = cl;
+ ll = &cl->next;
+
+ ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "write new buf t:%d f:%d %p, pos %p, size: %z "
+ "file: %O, size: %z",
+ cl->buf->temporary, cl->buf->in_file,
+ cl->buf->start, cl->buf->pos,
+ cl->buf->last - cl->buf->pos,
+ cl->buf->file_pos,
+ cl->buf->file_last - cl->buf->file_pos);
+
+#if 1
+ if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "zero size buf in writer "
+ "t:%d r:%d f:%d %p %p-%p %p %O-%O",
+ cl->buf->temporary,
+ cl->buf->recycled,
+ cl->buf->in_file,
+ cl->buf->start,
+ cl->buf->pos,
+ cl->buf->last,
+ cl->buf->file,
+ cl->buf->file_pos,
+ cl->buf->file_last);
+
+ ngx_debug_point();
+ return NGX_ERROR;
+ }
+#endif
+
+ size += ngx_buf_size(cl->buf);
+
+ if (cl->buf->flush || cl->buf->recycled) {
+ flush = 1;
+ }
+
+ if (cl->buf->last_buf) {
+ last = 1;
+ }
+ }
+
+ *ll = NULL;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http write filter: l:%d f:%d s:%O", last, flush, size);
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ /*
+ * avoid the output if there are no last buf, no flush point,
+ * there are the incoming bufs and the size of all bufs
+ * is smaller than "postpone_output" directive
+ */
+
+ if (!last && !flush && in && size < (off_t) clcf->postpone_output) {
+ return NGX_OK;
+ }
+
+ if (c->write->delayed) {
+ c->buffered |= NGX_HTTP_WRITE_BUFFERED;
+ return NGX_AGAIN;
+ }
+
+ if (size == 0
+ && !(c->buffered & NGX_LOWLEVEL_BUFFERED)
+ && !(last && c->need_last_buf))
+ {
+ if (last || flush) {
+ for (cl = r->out; cl; /* void */) {
+ ln = cl;
+ cl = cl->next;
+ ngx_free_chain(r->pool, ln);
+ }
+
+ r->out = NULL;
+ c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
+
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "the http output chain is empty");
+
+ ngx_debug_point();
+
+ return NGX_ERROR;
+ }
+
+ if (r->limit_rate) {
+ if (r->limit_rate_after == 0) {
+ r->limit_rate_after = clcf->limit_rate_after;
+ }
+
+ limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1)
+ - (c->sent - r->limit_rate_after);
+
+ if (limit <= 0) {
+ c->write->delayed = 1;
+ ngx_add_timer(c->write,
+ (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1));
+
+ c->buffered |= NGX_HTTP_WRITE_BUFFERED;
+
+ return NGX_AGAIN;
+ }
+
+ if (clcf->sendfile_max_chunk
+ && (off_t) clcf->sendfile_max_chunk < limit)
+ {
+ limit = clcf->sendfile_max_chunk;
+ }
+
+ } else {
+ limit = clcf->sendfile_max_chunk;
+ }
+
+ sent = c->sent;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http write filter limit %O", limit);
+
+ chain = c->send_chain(c, r->out, limit);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http write filter %p", chain);
+
+ if (chain == NGX_CHAIN_ERROR) {
+ c->error = 1;
+ return NGX_ERROR;
+ }
+
+ if (r->limit_rate) {
+
+ nsent = c->sent;
+
+ if (r->limit_rate_after) {
+
+ sent -= r->limit_rate_after;
+ if (sent < 0) {
+ sent = 0;
+ }
+
+ nsent -= r->limit_rate_after;
+ if (nsent < 0) {
+ nsent = 0;
+ }
+ }
+
+ delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate);
+
+ if (delay > 0) {
+ limit = 0;
+ c->write->delayed = 1;
+ ngx_add_timer(c->write, delay);
+ }
+ }
+
+ if (limit
+ && c->write->ready
+ && c->sent - sent >= limit - (off_t) (2 * ngx_pagesize))
+ {
+ c->write->delayed = 1;
+ ngx_add_timer(c->write, 1);
+ }
+
+ for (cl = r->out; cl && cl != chain; /* void */) {
+ ln = cl;
+ cl = cl->next;
+ ngx_free_chain(r->pool, ln);
+ }
+
+ r->out = chain;
+
+ if (chain) {
+ c->buffered |= NGX_HTTP_WRITE_BUFFERED;
+ return NGX_AGAIN;
+ }
+
+ c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
+
+ if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {
+ return NGX_AGAIN;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_write_filter_init(ngx_conf_t *cf)
+{
+ ngx_http_top_body_filter = ngx_http_write_filter;
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail.c
new file mode 100644
index 00000000000..350d2cdf90b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail.c
@@ -0,0 +1,568 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_mail.h>
+
+
+static char *ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static ngx_int_t ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
+ ngx_mail_listen_t *listen);
+static char *ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports);
+static ngx_int_t ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport,
+ ngx_mail_conf_addr_t *addr);
+#if (NGX_HAVE_INET6)
+static ngx_int_t ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport,
+ ngx_mail_conf_addr_t *addr);
+#endif
+static ngx_int_t ngx_mail_cmp_conf_addrs(const void *one, const void *two);
+
+
+ngx_uint_t ngx_mail_max_module;
+
+
+static ngx_command_t ngx_mail_commands[] = {
+
+ { ngx_string("mail"),
+ NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_mail_block,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("imap"),
+ NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_mail_block,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_core_module_t ngx_mail_module_ctx = {
+ ngx_string("mail"),
+ NULL,
+ NULL
+};
+
+
+ngx_module_t ngx_mail_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_module_ctx, /* module context */
+ ngx_mail_commands, /* module directives */
+ NGX_CORE_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static char *
+ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ ngx_uint_t i, m, mi, s;
+ ngx_conf_t pcf;
+ ngx_array_t ports;
+ ngx_mail_listen_t *listen;
+ ngx_mail_module_t *module;
+ ngx_mail_conf_ctx_t *ctx;
+ ngx_mail_core_srv_conf_t **cscfp;
+ ngx_mail_core_main_conf_t *cmcf;
+
+ if (cmd->name.data[0] == 'i') {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "the \"imap\" directive is deprecated, "
+ "use the \"mail\" directive instead");
+ }
+
+ /* the main mail context */
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *(ngx_mail_conf_ctx_t **) conf = ctx;
+
+ /* count the number of the http modules and set up their indices */
+
+ ngx_mail_max_module = 0;
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_MAIL_MODULE) {
+ continue;
+ }
+
+ ngx_modules[m]->ctx_index = ngx_mail_max_module++;
+ }
+
+
+ /* the mail main_conf context, it is the same in the all mail contexts */
+
+ ctx->main_conf = ngx_pcalloc(cf->pool,
+ sizeof(void *) * ngx_mail_max_module);
+ if (ctx->main_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /*
+ * the mail null srv_conf context, it is used to merge
+ * the server{}s' srv_conf's
+ */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_mail_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /*
+ * create the main_conf's, the null srv_conf's, and the null loc_conf's
+ * of the all mail modules
+ */
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_MAIL_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+ mi = ngx_modules[m]->ctx_index;
+
+ if (module->create_main_conf) {
+ ctx->main_conf[mi] = module->create_main_conf(cf);
+ if (ctx->main_conf[mi] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (module->create_srv_conf) {
+ ctx->srv_conf[mi] = module->create_srv_conf(cf);
+ if (ctx->srv_conf[mi] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+ }
+
+
+ /* parse inside the mail{} block */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+
+ cf->module_type = NGX_MAIL_MODULE;
+ cf->cmd_type = NGX_MAIL_MAIN_CONF;
+ rv = ngx_conf_parse(cf, NULL);
+
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+
+
+ /* init mail{} main_conf's, merge the server{}s' srv_conf's */
+
+ cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index];
+ cscfp = cmcf->servers.elts;
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_MAIL_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+ mi = ngx_modules[m]->ctx_index;
+
+ /* init mail{} main_conf's */
+
+ cf->ctx = ctx;
+
+ if (module->init_main_conf) {
+ rv = module->init_main_conf(cf, ctx->main_conf[mi]);
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+ }
+
+ for (s = 0; s < cmcf->servers.nelts; s++) {
+
+ /* merge the server{}s' srv_conf's */
+
+ cf->ctx = cscfp[s]->ctx;
+
+ if (module->merge_srv_conf) {
+ rv = module->merge_srv_conf(cf,
+ ctx->srv_conf[mi],
+ cscfp[s]->ctx->srv_conf[mi]);
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+ }
+ }
+ }
+
+ *cf = pcf;
+
+
+ if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_mail_conf_port_t))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ listen = cmcf->listen.elts;
+
+ for (i = 0; i < cmcf->listen.nelts; i++) {
+ if (ngx_mail_add_ports(cf, &ports, &listen[i]) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return ngx_mail_optimize_servers(cf, &ports);
+}
+
+
+static ngx_int_t
+ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
+ ngx_mail_listen_t *listen)
+{
+ in_port_t p;
+ ngx_uint_t i;
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ ngx_mail_conf_port_t *port;
+ ngx_mail_conf_addr_t *addr;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ sa = (struct sockaddr *) &listen->sockaddr;
+
+ switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) sa;
+ p = sin6->sin6_port;
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ p = 0;
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) sa;
+ p = sin->sin_port;
+ break;
+ }
+
+ port = ports->elts;
+ for (i = 0; i < ports->nelts; i++) {
+ if (p == port[i].port && sa->sa_family == port[i].family) {
+
+ /* a port is already in the port list */
+
+ port = &port[i];
+ goto found;
+ }
+ }
+
+ /* add a port to the port list */
+
+ port = ngx_array_push(ports);
+ if (port == NULL) {
+ return NGX_ERROR;
+ }
+
+ port->family = sa->sa_family;
+ port->port = p;
+
+ if (ngx_array_init(&port->addrs, cf->temp_pool, 2,
+ sizeof(ngx_mail_conf_addr_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+found:
+
+ addr = ngx_array_push(&port->addrs);
+ if (addr == NULL) {
+ return NGX_ERROR;
+ }
+
+ addr->sockaddr = (struct sockaddr *) &listen->sockaddr;
+ addr->socklen = listen->socklen;
+ addr->ctx = listen->ctx;
+ addr->bind = listen->bind;
+ addr->wildcard = listen->wildcard;
+ addr->so_keepalive = listen->so_keepalive;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ addr->tcp_keepidle = listen->tcp_keepidle;
+ addr->tcp_keepintvl = listen->tcp_keepintvl;
+ addr->tcp_keepcnt = listen->tcp_keepcnt;
+#endif
+#if (NGX_MAIL_SSL)
+ addr->ssl = listen->ssl;
+#endif
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ addr->ipv6only = listen->ipv6only;
+#endif
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
+{
+ ngx_uint_t i, p, last, bind_wildcard;
+ ngx_listening_t *ls;
+ ngx_mail_port_t *mport;
+ ngx_mail_conf_port_t *port;
+ ngx_mail_conf_addr_t *addr;
+
+ port = ports->elts;
+ for (p = 0; p < ports->nelts; p++) {
+
+ ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
+ sizeof(ngx_mail_conf_addr_t), ngx_mail_cmp_conf_addrs);
+
+ addr = port[p].addrs.elts;
+ last = port[p].addrs.nelts;
+
+ /*
+ * if there is the binding to the "*:port" then we need to bind()
+ * to the "*:port" only and ignore the other bindings
+ */
+
+ if (addr[last - 1].wildcard) {
+ addr[last - 1].bind = 1;
+ bind_wildcard = 1;
+
+ } else {
+ bind_wildcard = 0;
+ }
+
+ i = 0;
+
+ while (i < last) {
+
+ if (bind_wildcard && !addr[i].bind) {
+ i++;
+ continue;
+ }
+
+ ls = ngx_create_listening(cf, addr[i].sockaddr, addr[i].socklen);
+ if (ls == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ls->addr_ntop = 1;
+ ls->handler = ngx_mail_init_connection;
+ ls->pool_size = 256;
+
+ /* TODO: error_log directive */
+ ls->logp = &cf->cycle->new_log;
+ ls->log.data = &ls->addr_text;
+ ls->log.handler = ngx_accept_log_error;
+
+ ls->keepalive = addr[i].so_keepalive;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ ls->keepidle = addr[i].tcp_keepidle;
+ ls->keepintvl = addr[i].tcp_keepintvl;
+ ls->keepcnt = addr[i].tcp_keepcnt;
+#endif
+
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ ls->ipv6only = addr[i].ipv6only;
+#endif
+
+ mport = ngx_palloc(cf->pool, sizeof(ngx_mail_port_t));
+ if (mport == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ls->servers = mport;
+
+ if (i == last - 1) {
+ mport->naddrs = last;
+
+ } else {
+ mport->naddrs = 1;
+ i = 0;
+ }
+
+ switch (ls->sockaddr->sa_family) {
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ if (ngx_mail_add_addrs6(cf, mport, addr) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ break;
+#endif
+ default: /* AF_INET */
+ if (ngx_mail_add_addrs(cf, mport, addr) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ break;
+ }
+
+ addr++;
+ last--;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport,
+ ngx_mail_conf_addr_t *addr)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_mail_in_addr_t *addrs;
+ struct sockaddr_in *sin;
+ u_char buf[NGX_SOCKADDR_STRLEN];
+
+ mport->addrs = ngx_pcalloc(cf->pool,
+ mport->naddrs * sizeof(ngx_mail_in_addr_t));
+ if (mport->addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ addrs = mport->addrs;
+
+ for (i = 0; i < mport->naddrs; i++) {
+
+ sin = (struct sockaddr_in *) addr[i].sockaddr;
+ addrs[i].addr = sin->sin_addr.s_addr;
+
+ addrs[i].conf.ctx = addr[i].ctx;
+#if (NGX_MAIL_SSL)
+ addrs[i].conf.ssl = addr[i].ssl;
+#endif
+
+ len = ngx_sock_ntop(addr[i].sockaddr, addr[i].socklen, buf,
+ NGX_SOCKADDR_STRLEN, 1);
+
+ p = ngx_pnalloc(cf->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(p, buf, len);
+
+ addrs[i].conf.addr_text.len = len;
+ addrs[i].conf.addr_text.data = p;
+ }
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HAVE_INET6)
+
+static ngx_int_t
+ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport,
+ ngx_mail_conf_addr_t *addr)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_mail_in6_addr_t *addrs6;
+ struct sockaddr_in6 *sin6;
+ u_char buf[NGX_SOCKADDR_STRLEN];
+
+ mport->addrs = ngx_pcalloc(cf->pool,
+ mport->naddrs * sizeof(ngx_mail_in6_addr_t));
+ if (mport->addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ addrs6 = mport->addrs;
+
+ for (i = 0; i < mport->naddrs; i++) {
+
+ sin6 = (struct sockaddr_in6 *) addr[i].sockaddr;
+ addrs6[i].addr6 = sin6->sin6_addr;
+
+ addrs6[i].conf.ctx = addr[i].ctx;
+#if (NGX_MAIL_SSL)
+ addrs6[i].conf.ssl = addr[i].ssl;
+#endif
+
+ len = ngx_sock_ntop(addr[i].sockaddr, addr[i].socklen, buf,
+ NGX_SOCKADDR_STRLEN, 1);
+
+ p = ngx_pnalloc(cf->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(p, buf, len);
+
+ addrs6[i].conf.addr_text.len = len;
+ addrs6[i].conf.addr_text.data = p;
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_mail_cmp_conf_addrs(const void *one, const void *two)
+{
+ ngx_mail_conf_addr_t *first, *second;
+
+ first = (ngx_mail_conf_addr_t *) one;
+ second = (ngx_mail_conf_addr_t *) two;
+
+ if (first->wildcard) {
+ /* a wildcard must be the last resort, shift it to the end */
+ return 1;
+ }
+
+ if (second->wildcard) {
+ /* a wildcard must be the last resort, shift it to the end */
+ return -1;
+ }
+
+ if (first->bind && !second->bind) {
+ /* shift explicit bind()ed addresses to the start */
+ return -1;
+ }
+
+ if (!first->bind && second->bind) {
+ /* shift explicit bind()ed addresses to the start */
+ return 1;
+ }
+
+ /* do not sort by default */
+
+ return 0;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail.h
new file mode 100644
index 00000000000..dc39f1e1375
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail.h
@@ -0,0 +1,421 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_MAIL_H_INCLUDED_
+#define _NGX_MAIL_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+
+#if (NGX_MAIL_SSL)
+#include <ngx_mail_ssl_module.h>
+#endif
+
+
+
+typedef struct {
+ void **main_conf;
+ void **srv_conf;
+} ngx_mail_conf_ctx_t;
+
+
+typedef struct {
+ u_char sockaddr[NGX_SOCKADDRLEN];
+ socklen_t socklen;
+
+ /* server ctx */
+ ngx_mail_conf_ctx_t *ctx;
+
+ unsigned bind:1;
+ unsigned wildcard:1;
+#if (NGX_MAIL_SSL)
+ unsigned ssl:1;
+#endif
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ unsigned ipv6only:1;
+#endif
+ unsigned so_keepalive:2;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ int tcp_keepidle;
+ int tcp_keepintvl;
+ int tcp_keepcnt;
+#endif
+} ngx_mail_listen_t;
+
+
+typedef struct {
+ ngx_mail_conf_ctx_t *ctx;
+ ngx_str_t addr_text;
+#if (NGX_MAIL_SSL)
+ ngx_uint_t ssl; /* unsigned ssl:1; */
+#endif
+} ngx_mail_addr_conf_t;
+
+typedef struct {
+ in_addr_t addr;
+ ngx_mail_addr_conf_t conf;
+} ngx_mail_in_addr_t;
+
+
+#if (NGX_HAVE_INET6)
+
+typedef struct {
+ struct in6_addr addr6;
+ ngx_mail_addr_conf_t conf;
+} ngx_mail_in6_addr_t;
+
+#endif
+
+
+typedef struct {
+ /* ngx_mail_in_addr_t or ngx_mail_in6_addr_t */
+ void *addrs;
+ ngx_uint_t naddrs;
+} ngx_mail_port_t;
+
+
+typedef struct {
+ int family;
+ in_port_t port;
+ ngx_array_t addrs; /* array of ngx_mail_conf_addr_t */
+} ngx_mail_conf_port_t;
+
+
+typedef struct {
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+
+ ngx_mail_conf_ctx_t *ctx;
+
+ unsigned bind:1;
+ unsigned wildcard:1;
+#if (NGX_MAIL_SSL)
+ unsigned ssl:1;
+#endif
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ unsigned ipv6only:1;
+#endif
+ unsigned so_keepalive:2;
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ int tcp_keepidle;
+ int tcp_keepintvl;
+ int tcp_keepcnt;
+#endif
+} ngx_mail_conf_addr_t;
+
+
+typedef struct {
+ ngx_array_t servers; /* ngx_mail_core_srv_conf_t */
+ ngx_array_t listen; /* ngx_mail_listen_t */
+} ngx_mail_core_main_conf_t;
+
+
+#define NGX_MAIL_POP3_PROTOCOL 0
+#define NGX_MAIL_IMAP_PROTOCOL 1
+#define NGX_MAIL_SMTP_PROTOCOL 2
+
+
+typedef struct ngx_mail_protocol_s ngx_mail_protocol_t;
+
+
+typedef struct {
+ ngx_mail_protocol_t *protocol;
+
+ ngx_msec_t timeout;
+ ngx_msec_t resolver_timeout;
+
+ ngx_flag_t so_keepalive;
+
+ ngx_str_t server_name;
+
+ u_char *file_name;
+ ngx_int_t line;
+
+ ngx_resolver_t *resolver;
+
+ /* server ctx */
+ ngx_mail_conf_ctx_t *ctx;
+} ngx_mail_core_srv_conf_t;
+
+
+typedef enum {
+ ngx_pop3_start = 0,
+ ngx_pop3_user,
+ ngx_pop3_passwd,
+ ngx_pop3_auth_login_username,
+ ngx_pop3_auth_login_password,
+ ngx_pop3_auth_plain,
+ ngx_pop3_auth_cram_md5
+} ngx_pop3_state_e;
+
+
+typedef enum {
+ ngx_imap_start = 0,
+ ngx_imap_auth_login_username,
+ ngx_imap_auth_login_password,
+ ngx_imap_auth_plain,
+ ngx_imap_auth_cram_md5,
+ ngx_imap_login,
+ ngx_imap_user,
+ ngx_imap_passwd
+} ngx_imap_state_e;
+
+
+typedef enum {
+ ngx_smtp_start = 0,
+ ngx_smtp_auth_login_username,
+ ngx_smtp_auth_login_password,
+ ngx_smtp_auth_plain,
+ ngx_smtp_auth_cram_md5,
+ ngx_smtp_helo,
+ ngx_smtp_helo_xclient,
+ ngx_smtp_helo_from,
+ ngx_smtp_xclient,
+ ngx_smtp_xclient_from,
+ ngx_smtp_xclient_helo,
+ ngx_smtp_from,
+ ngx_smtp_to
+} ngx_smtp_state_e;
+
+
+typedef struct {
+ ngx_peer_connection_t upstream;
+ ngx_buf_t *buffer;
+} ngx_mail_proxy_ctx_t;
+
+
+typedef struct {
+ uint32_t signature; /* "MAIL" */
+
+ ngx_connection_t *connection;
+
+ ngx_str_t out;
+ ngx_buf_t *buffer;
+
+ void **ctx;
+ void **main_conf;
+ void **srv_conf;
+
+ ngx_resolver_ctx_t *resolver_ctx;
+
+ ngx_mail_proxy_ctx_t *proxy;
+
+ ngx_uint_t mail_state;
+
+ unsigned protocol:3;
+ unsigned blocked:1;
+ unsigned quit:1;
+ unsigned quoted:1;
+ unsigned backslash:1;
+ unsigned no_sync_literal:1;
+ unsigned starttls:1;
+ unsigned esmtp:1;
+ unsigned auth_method:3;
+ unsigned auth_wait:1;
+
+ ngx_str_t login;
+ ngx_str_t passwd;
+
+ ngx_str_t salt;
+ ngx_str_t tag;
+ ngx_str_t tagged_line;
+ ngx_str_t text;
+
+ ngx_str_t *addr_text;
+ ngx_str_t host;
+ ngx_str_t smtp_helo;
+ ngx_str_t smtp_from;
+ ngx_str_t smtp_to;
+
+ ngx_str_t cmd;
+
+ ngx_uint_t command;
+ ngx_array_t args;
+
+ ngx_uint_t login_attempt;
+
+ /* used to parse POP3/IMAP/SMTP command */
+
+ ngx_uint_t state;
+ u_char *cmd_start;
+ u_char *arg_start;
+ u_char *arg_end;
+ ngx_uint_t literal_len;
+} ngx_mail_session_t;
+
+
+typedef struct {
+ ngx_str_t *client;
+ ngx_mail_session_t *session;
+} ngx_mail_log_ctx_t;
+
+
+#define NGX_POP3_USER 1
+#define NGX_POP3_PASS 2
+#define NGX_POP3_CAPA 3
+#define NGX_POP3_QUIT 4
+#define NGX_POP3_NOOP 5
+#define NGX_POP3_STLS 6
+#define NGX_POP3_APOP 7
+#define NGX_POP3_AUTH 8
+#define NGX_POP3_STAT 9
+#define NGX_POP3_LIST 10
+#define NGX_POP3_RETR 11
+#define NGX_POP3_DELE 12
+#define NGX_POP3_RSET 13
+#define NGX_POP3_TOP 14
+#define NGX_POP3_UIDL 15
+
+
+#define NGX_IMAP_LOGIN 1
+#define NGX_IMAP_LOGOUT 2
+#define NGX_IMAP_CAPABILITY 3
+#define NGX_IMAP_NOOP 4
+#define NGX_IMAP_STARTTLS 5
+
+#define NGX_IMAP_NEXT 6
+
+#define NGX_IMAP_AUTHENTICATE 7
+
+
+#define NGX_SMTP_HELO 1
+#define NGX_SMTP_EHLO 2
+#define NGX_SMTP_AUTH 3
+#define NGX_SMTP_QUIT 4
+#define NGX_SMTP_NOOP 5
+#define NGX_SMTP_MAIL 6
+#define NGX_SMTP_RSET 7
+#define NGX_SMTP_RCPT 8
+#define NGX_SMTP_DATA 9
+#define NGX_SMTP_VRFY 10
+#define NGX_SMTP_EXPN 11
+#define NGX_SMTP_HELP 12
+#define NGX_SMTP_STARTTLS 13
+
+
+#define NGX_MAIL_AUTH_PLAIN 0
+#define NGX_MAIL_AUTH_LOGIN 1
+#define NGX_MAIL_AUTH_LOGIN_USERNAME 2
+#define NGX_MAIL_AUTH_APOP 3
+#define NGX_MAIL_AUTH_CRAM_MD5 4
+#define NGX_MAIL_AUTH_NONE 5
+
+
+#define NGX_MAIL_AUTH_PLAIN_ENABLED 0x0002
+#define NGX_MAIL_AUTH_LOGIN_ENABLED 0x0004
+#define NGX_MAIL_AUTH_APOP_ENABLED 0x0008
+#define NGX_MAIL_AUTH_CRAM_MD5_ENABLED 0x0010
+#define NGX_MAIL_AUTH_NONE_ENABLED 0x0020
+
+
+#define NGX_MAIL_PARSE_INVALID_COMMAND 20
+
+
+typedef void (*ngx_mail_init_session_pt)(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+typedef void (*ngx_mail_init_protocol_pt)(ngx_event_t *rev);
+typedef void (*ngx_mail_auth_state_pt)(ngx_event_t *rev);
+typedef ngx_int_t (*ngx_mail_parse_command_pt)(ngx_mail_session_t *s);
+
+
+struct ngx_mail_protocol_s {
+ ngx_str_t name;
+ in_port_t port[4];
+ ngx_uint_t type;
+
+ ngx_mail_init_session_pt init_session;
+ ngx_mail_init_protocol_pt init_protocol;
+ ngx_mail_parse_command_pt parse_command;
+ ngx_mail_auth_state_pt auth_state;
+
+ ngx_str_t internal_server_error;
+};
+
+
+typedef struct {
+ ngx_mail_protocol_t *protocol;
+
+ void *(*create_main_conf)(ngx_conf_t *cf);
+ char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
+
+ void *(*create_srv_conf)(ngx_conf_t *cf);
+ char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev,
+ void *conf);
+} ngx_mail_module_t;
+
+
+#define NGX_MAIL_MODULE 0x4C49414D /* "MAIL" */
+
+#define NGX_MAIL_MAIN_CONF 0x02000000
+#define NGX_MAIL_SRV_CONF 0x04000000
+
+
+#define NGX_MAIL_MAIN_CONF_OFFSET offsetof(ngx_mail_conf_ctx_t, main_conf)
+#define NGX_MAIL_SRV_CONF_OFFSET offsetof(ngx_mail_conf_ctx_t, srv_conf)
+
+
+#define ngx_mail_get_module_ctx(s, module) (s)->ctx[module.ctx_index]
+#define ngx_mail_set_ctx(s, c, module) s->ctx[module.ctx_index] = c;
+#define ngx_mail_delete_ctx(s, module) s->ctx[module.ctx_index] = NULL;
+
+
+#define ngx_mail_get_module_main_conf(s, module) \
+ (s)->main_conf[module.ctx_index]
+#define ngx_mail_get_module_srv_conf(s, module) (s)->srv_conf[module.ctx_index]
+
+#define ngx_mail_conf_get_module_main_conf(cf, module) \
+ ((ngx_mail_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
+#define ngx_mail_conf_get_module_srv_conf(cf, module) \
+ ((ngx_mail_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]
+
+
+#if (NGX_MAIL_SSL)
+void ngx_mail_starttls_handler(ngx_event_t *rev);
+ngx_int_t ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c);
+#endif
+
+
+void ngx_mail_init_connection(ngx_connection_t *c);
+
+ngx_int_t ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_mail_core_srv_conf_t *cscf);
+ngx_int_t ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_uint_t n);
+ngx_int_t ngx_mail_auth_login_username(ngx_mail_session_t *s,
+ ngx_connection_t *c, ngx_uint_t n);
+ngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+ngx_int_t ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s,
+ ngx_connection_t *c, char *prefix, size_t len);
+ngx_int_t ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c);
+ngx_int_t ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c);
+
+void ngx_mail_send(ngx_event_t *wev);
+ngx_int_t ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c);
+void ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c);
+void ngx_mail_close_connection(ngx_connection_t *c);
+void ngx_mail_session_internal_server_error(ngx_mail_session_t *s);
+u_char *ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len);
+
+
+char *ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+
+
+/* STUB */
+void ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer);
+void ngx_mail_auth_http_init(ngx_mail_session_t *s);
+/**/
+
+
+extern ngx_uint_t ngx_mail_max_module;
+extern ngx_module_t ngx_mail_core_module;
+
+
+#endif /* _NGX_MAIL_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_auth_http_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_auth_http_module.c
new file mode 100644
index 00000000000..eb7531c80a3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_auth_http_module.c
@@ -0,0 +1,1461 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include <ngx_mail.h>
+
+
+typedef struct {
+ ngx_addr_t *peer;
+
+ ngx_msec_t timeout;
+
+ ngx_str_t host_header;
+ ngx_str_t uri;
+ ngx_str_t header;
+
+ ngx_array_t *headers;
+
+ u_char *file;
+ ngx_uint_t line;
+} ngx_mail_auth_http_conf_t;
+
+
+typedef struct ngx_mail_auth_http_ctx_s ngx_mail_auth_http_ctx_t;
+
+typedef void (*ngx_mail_auth_http_handler_pt)(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx);
+
+struct ngx_mail_auth_http_ctx_s {
+ ngx_buf_t *request;
+ ngx_buf_t *response;
+ ngx_peer_connection_t peer;
+
+ ngx_mail_auth_http_handler_pt handler;
+
+ ngx_uint_t state;
+
+ u_char *header_name_start;
+ u_char *header_name_end;
+ u_char *header_start;
+ u_char *header_end;
+
+ ngx_str_t addr;
+ ngx_str_t port;
+ ngx_str_t err;
+ ngx_str_t errmsg;
+ ngx_str_t errcode;
+
+ time_t sleep;
+
+ ngx_pool_t *pool;
+};
+
+
+static void ngx_mail_auth_http_write_handler(ngx_event_t *wev);
+static void ngx_mail_auth_http_read_handler(ngx_event_t *rev);
+static void ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx);
+static void ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx);
+static void ngx_mail_auth_sleep_handler(ngx_event_t *rev);
+static ngx_int_t ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx);
+static void ngx_mail_auth_http_block_read(ngx_event_t *rev);
+static void ngx_mail_auth_http_dummy_handler(ngx_event_t *ev);
+static ngx_buf_t *ngx_mail_auth_http_create_request(ngx_mail_session_t *s,
+ ngx_pool_t *pool, ngx_mail_auth_http_conf_t *ahcf);
+static ngx_int_t ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text,
+ ngx_str_t *escaped);
+
+static void *ngx_mail_auth_http_create_conf(ngx_conf_t *cf);
+static char *ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_mail_auth_http_commands[] = {
+
+ { ngx_string("auth_http"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_mail_auth_http,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("auth_http_timeout"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_auth_http_conf_t, timeout),
+ NULL },
+
+ { ngx_string("auth_http_header"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE2,
+ ngx_mail_auth_http_header,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_auth_http_module_ctx = {
+ NULL, /* protocol */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_auth_http_create_conf, /* create server configuration */
+ ngx_mail_auth_http_merge_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_auth_http_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_auth_http_module_ctx, /* module context */
+ ngx_mail_auth_http_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t ngx_mail_auth_http_method[] = {
+ ngx_string("plain"),
+ ngx_string("plain"),
+ ngx_string("plain"),
+ ngx_string("apop"),
+ ngx_string("cram-md5"),
+ ngx_string("none")
+};
+
+static ngx_str_t ngx_mail_smtp_errcode = ngx_string("535 5.7.0");
+
+
+void
+ngx_mail_auth_http_init(ngx_mail_session_t *s)
+{
+ ngx_int_t rc;
+ ngx_pool_t *pool;
+ ngx_mail_auth_http_ctx_t *ctx;
+ ngx_mail_auth_http_conf_t *ahcf;
+
+ s->connection->log->action = "in http auth state";
+
+ pool = ngx_create_pool(2048, s->connection->log);
+ if (pool == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ctx = ngx_pcalloc(pool, sizeof(ngx_mail_auth_http_ctx_t));
+ if (ctx == NULL) {
+ ngx_destroy_pool(pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->pool = pool;
+
+ ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);
+
+ ctx->request = ngx_mail_auth_http_create_request(s, pool, ahcf);
+ if (ctx->request == NULL) {
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_mail_set_ctx(s, ctx, ngx_mail_auth_http_module);
+
+ ctx->peer.sockaddr = ahcf->peer->sockaddr;
+ ctx->peer.socklen = ahcf->peer->socklen;
+ ctx->peer.name = &ahcf->peer->name;
+ ctx->peer.get = ngx_event_get_peer;
+ ctx->peer.log = s->connection->log;
+ ctx->peer.log_error = NGX_ERROR_ERR;
+
+ rc = ngx_event_connect_peer(&ctx->peer);
+
+ if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
+ if (ctx->peer.connection) {
+ ngx_close_connection(ctx->peer.connection);
+ }
+
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->peer.connection->data = s;
+ ctx->peer.connection->pool = s->connection->pool;
+
+ s->connection->read->handler = ngx_mail_auth_http_block_read;
+ ctx->peer.connection->read->handler = ngx_mail_auth_http_read_handler;
+ ctx->peer.connection->write->handler = ngx_mail_auth_http_write_handler;
+
+ ctx->handler = ngx_mail_auth_http_ignore_status_line;
+
+ ngx_add_timer(ctx->peer.connection->read, ahcf->timeout);
+ ngx_add_timer(ctx->peer.connection->write, ahcf->timeout);
+
+ if (rc == NGX_OK) {
+ ngx_mail_auth_http_write_handler(ctx->peer.connection->write);
+ return;
+ }
+}
+
+
+static void
+ngx_mail_auth_http_write_handler(ngx_event_t *wev)
+{
+ ssize_t n, size;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_auth_http_ctx_t *ctx;
+ ngx_mail_auth_http_conf_t *ahcf;
+
+ c = wev->data;
+ s = c->data;
+
+ ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0,
+ "mail auth http write handler");
+
+ if (wev->timedout) {
+ ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
+ "auth http server %V timed out", ctx->peer.name);
+ ngx_close_connection(c);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ size = ctx->request->last - ctx->request->pos;
+
+ n = ngx_send(c, ctx->request->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_close_connection(c);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ if (n > 0) {
+ ctx->request->pos += n;
+
+ if (n == size) {
+ wev->handler = ngx_mail_auth_http_dummy_handler;
+
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+
+ if (ngx_handle_write_event(wev, 0) != NGX_OK) {
+ ngx_close_connection(c);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ }
+
+ return;
+ }
+ }
+
+ if (!wev->timer_set) {
+ ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);
+ ngx_add_timer(wev, ahcf->timeout);
+ }
+}
+
+
+static void
+ngx_mail_auth_http_read_handler(ngx_event_t *rev)
+{
+ ssize_t n, size;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_auth_http_ctx_t *ctx;
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail auth http read handler");
+
+ ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
+ "auth http server %V timed out", ctx->peer.name);
+ ngx_close_connection(c);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ if (ctx->response == NULL) {
+ ctx->response = ngx_create_temp_buf(ctx->pool, 1024);
+ if (ctx->response == NULL) {
+ ngx_close_connection(c);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+ }
+
+ size = ctx->response->end - ctx->response->last;
+
+ n = ngx_recv(c, ctx->response->pos, size);
+
+ if (n > 0) {
+ ctx->response->last += n;
+
+ ctx->handler(s, ctx);
+ return;
+ }
+
+ if (n == NGX_AGAIN) {
+ return;
+ }
+
+ ngx_close_connection(c);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+}
+
+
+static void
+ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx)
+{
+ u_char *p, ch;
+ enum {
+ sw_start = 0,
+ sw_H,
+ sw_HT,
+ sw_HTT,
+ sw_HTTP,
+ sw_skip,
+ sw_almost_done
+ } state;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "mail auth http process status line");
+
+ state = ctx->state;
+
+ for (p = ctx->response->pos; p < ctx->response->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* "HTTP/" */
+ case sw_start:
+ if (ch == 'H') {
+ state = sw_H;
+ break;
+ }
+ goto next;
+
+ case sw_H:
+ if (ch == 'T') {
+ state = sw_HT;
+ break;
+ }
+ goto next;
+
+ case sw_HT:
+ if (ch == 'T') {
+ state = sw_HTT;
+ break;
+ }
+ goto next;
+
+ case sw_HTT:
+ if (ch == 'P') {
+ state = sw_HTTP;
+ break;
+ }
+ goto next;
+
+ case sw_HTTP:
+ if (ch == '/') {
+ state = sw_skip;
+ break;
+ }
+ goto next;
+
+ /* any text until end of line */
+ case sw_skip:
+ switch (ch) {
+ case CR:
+ state = sw_almost_done;
+
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+
+ /* end of status line */
+ case sw_almost_done:
+ if (ch == LF) {
+ goto done;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server &V sent invalid response",
+ ctx->peer.name);
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+ }
+
+ ctx->response->pos = p;
+ ctx->state = state;
+
+ return;
+
+next:
+
+ p = ctx->response->start - 1;
+
+done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = 0;
+ ctx->handler = ngx_mail_auth_http_process_headers;
+ ctx->handler(s, ctx);
+}
+
+
+static void
+ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx)
+{
+ u_char *p;
+ time_t timer;
+ size_t len, size;
+ ngx_int_t rc, port, n;
+ ngx_addr_t *peer;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "mail auth http process headers");
+
+ for ( ;; ) {
+ rc = ngx_mail_auth_http_parse_header_line(s, ctx);
+
+ if (rc == NGX_OK) {
+
+#if (NGX_DEBUG)
+ {
+ ngx_str_t key, value;
+
+ key.len = ctx->header_name_end - ctx->header_name_start;
+ key.data = ctx->header_name_start;
+ value.len = ctx->header_end - ctx->header_start;
+ value.data = ctx->header_start;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "mail auth http header: \"%V: %V\"",
+ &key, &value);
+ }
+#endif
+
+ len = ctx->header_name_end - ctx->header_name_start;
+
+ if (len == sizeof("Auth-Status") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-Status",
+ sizeof("Auth-Status") - 1)
+ == 0)
+ {
+ len = ctx->header_end - ctx->header_start;
+
+ if (len == 2
+ && ctx->header_start[0] == 'O'
+ && ctx->header_start[1] == 'K')
+ {
+ continue;
+ }
+
+ if (len == 4
+ && ctx->header_start[0] == 'W'
+ && ctx->header_start[1] == 'A'
+ && ctx->header_start[2] == 'I'
+ && ctx->header_start[3] == 'T')
+ {
+ s->auth_wait = 1;
+ continue;
+ }
+
+ ctx->errmsg.len = len;
+ ctx->errmsg.data = ctx->header_start;
+
+ switch (s->protocol) {
+
+ case NGX_MAIL_POP3_PROTOCOL:
+ size = sizeof("-ERR ") - 1 + len + sizeof(CRLF) - 1;
+ break;
+
+ case NGX_MAIL_IMAP_PROTOCOL:
+ size = s->tag.len + sizeof("NO ") - 1 + len
+ + sizeof(CRLF) - 1;
+ break;
+
+ default: /* NGX_MAIL_SMTP_PROTOCOL */
+ ctx->err = ctx->errmsg;
+ continue;
+ }
+
+ p = ngx_pnalloc(s->connection->pool, size);
+ if (p == NULL) {
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->err.data = p;
+
+ switch (s->protocol) {
+
+ case NGX_MAIL_POP3_PROTOCOL:
+ *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R'; *p++ = ' ';
+ break;
+
+ case NGX_MAIL_IMAP_PROTOCOL:
+ p = ngx_cpymem(p, s->tag.data, s->tag.len);
+ *p++ = 'N'; *p++ = 'O'; *p++ = ' ';
+ break;
+
+ default: /* NGX_MAIL_SMTP_PROTOCOL */
+ break;
+ }
+
+ p = ngx_cpymem(p, ctx->header_start, len);
+ *p++ = CR; *p++ = LF;
+
+ ctx->err.len = p - ctx->err.data;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Server") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-Server",
+ sizeof("Auth-Server") - 1)
+ == 0)
+ {
+ ctx->addr.len = ctx->header_end - ctx->header_start;
+ ctx->addr.data = ctx->header_start;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Port") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-Port",
+ sizeof("Auth-Port") - 1)
+ == 0)
+ {
+ ctx->port.len = ctx->header_end - ctx->header_start;
+ ctx->port.data = ctx->header_start;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-User") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-User",
+ sizeof("Auth-User") - 1)
+ == 0)
+ {
+ s->login.len = ctx->header_end - ctx->header_start;
+
+ s->login.data = ngx_pnalloc(s->connection->pool, s->login.len);
+ if (s->login.data == NULL) {
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_memcpy(s->login.data, ctx->header_start, s->login.len);
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Pass") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-Pass",
+ sizeof("Auth-Pass") - 1)
+ == 0)
+ {
+ s->passwd.len = ctx->header_end - ctx->header_start;
+
+ s->passwd.data = ngx_pnalloc(s->connection->pool,
+ s->passwd.len);
+ if (s->passwd.data == NULL) {
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_memcpy(s->passwd.data, ctx->header_start, s->passwd.len);
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Wait") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-Wait",
+ sizeof("Auth-Wait") - 1)
+ == 0)
+ {
+ n = ngx_atoi(ctx->header_start,
+ ctx->header_end - ctx->header_start);
+
+ if (n != NGX_ERROR) {
+ ctx->sleep = n;
+ }
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Error-Code") - 1
+ && ngx_strncasecmp(ctx->header_name_start,
+ (u_char *) "Auth-Error-Code",
+ sizeof("Auth-Error-Code") - 1)
+ == 0)
+ {
+ ctx->errcode.len = ctx->header_end - ctx->header_start;
+
+ ctx->errcode.data = ngx_pnalloc(s->connection->pool,
+ ctx->errcode.len);
+ if (ctx->errcode.data == NULL) {
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_memcpy(ctx->errcode.data, ctx->header_start,
+ ctx->errcode.len);
+
+ continue;
+ }
+
+ /* ignore other headers */
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "mail auth http header done");
+
+ ngx_close_connection(ctx->peer.connection);
+
+ if (ctx->err.len) {
+
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "client login failed: \"%V\"", &ctx->errmsg);
+
+ if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {
+
+ if (ctx->errcode.len == 0) {
+ ctx->errcode = ngx_mail_smtp_errcode;
+ }
+
+ ctx->err.len = ctx->errcode.len + ctx->errmsg.len
+ + sizeof(" " CRLF) - 1;
+
+ p = ngx_pnalloc(s->connection->pool, ctx->err.len);
+ if (p == NULL) {
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->err.data = p;
+
+ p = ngx_cpymem(p, ctx->errcode.data, ctx->errcode.len);
+ *p++ = ' ';
+ p = ngx_cpymem(p, ctx->errmsg.data, ctx->errmsg.len);
+ *p++ = CR; *p = LF;
+ }
+
+ s->out = ctx->err;
+ timer = ctx->sleep;
+
+ ngx_destroy_pool(ctx->pool);
+
+ if (timer == 0) {
+ s->quit = 1;
+ ngx_mail_send(s->connection->write);
+ return;
+ }
+
+ ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));
+
+ s->connection->read->handler = ngx_mail_auth_sleep_handler;
+
+ return;
+ }
+
+ if (s->auth_wait) {
+ timer = ctx->sleep;
+
+ ngx_destroy_pool(ctx->pool);
+
+ if (timer == 0) {
+ ngx_mail_auth_http_init(s);
+ return;
+ }
+
+ ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));
+
+ s->connection->read->handler = ngx_mail_auth_sleep_handler;
+
+ return;
+ }
+
+ if (ctx->addr.len == 0 || ctx->port.len == 0) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server %V did not send server or port",
+ ctx->peer.name);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ if (s->passwd.data == NULL
+ && s->protocol != NGX_MAIL_SMTP_PROTOCOL)
+ {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server %V did not send password",
+ ctx->peer.name);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ peer = ngx_pcalloc(s->connection->pool, sizeof(ngx_addr_t));
+ if (peer == NULL) {
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ rc = ngx_parse_addr(s->connection->pool, peer,
+ ctx->addr.data, ctx->addr.len);
+
+ switch (rc) {
+ case NGX_OK:
+ break;
+
+ case NGX_DECLINED:
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server %V sent invalid server "
+ "address:\"%V\"",
+ ctx->peer.name, &ctx->addr);
+ /* fall through */
+
+ default:
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ port = ngx_atoi(ctx->port.data, ctx->port.len);
+ if (port == NGX_ERROR || port < 1 || port > 65535) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server %V sent invalid server "
+ "port:\"%V\"",
+ ctx->peer.name, &ctx->port);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ switch (peer->sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) peer->sockaddr;
+ sin6->sin6_port = htons((in_port_t) port);
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) peer->sockaddr;
+ sin->sin_port = htons((in_port_t) port);
+ break;
+ }
+
+ len = ctx->addr.len + 1 + ctx->port.len;
+
+ peer->name.len = len;
+
+ peer->name.data = ngx_pnalloc(s->connection->pool, len);
+ if (peer->name.data == NULL) {
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ len = ctx->addr.len;
+
+ ngx_memcpy(peer->name.data, ctx->addr.data, len);
+
+ peer->name.data[len++] = ':';
+
+ ngx_memcpy(peer->name.data + len, ctx->port.data, ctx->port.len);
+
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_proxy_init(s, peer);
+
+ return;
+ }
+
+ if (rc == NGX_AGAIN ) {
+ return;
+ }
+
+ /* rc == NGX_ERROR */
+
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server %V sent invalid header in response",
+ ctx->peer.name);
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+
+ return;
+ }
+}
+
+
+static void
+ngx_mail_auth_sleep_handler(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail auth sleep handler");
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+
+ rev->timedout = 0;
+
+ if (s->auth_wait) {
+ s->auth_wait = 0;
+ ngx_mail_auth_http_init(s);
+ return;
+ }
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ rev->handler = cscf->protocol->auth_state;
+
+ s->mail_state = 0;
+ s->auth_method = NGX_MAIL_AUTH_PLAIN;
+
+ c->log->action = "in auth state";
+
+ ngx_mail_send(c->write);
+
+ if (c->destroyed) {
+ return;
+ }
+
+ ngx_add_timer(rev, cscf->timeout);
+
+ if (rev->ready) {
+ rev->handler(rev);
+ return;
+ }
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+
+ return;
+ }
+
+ if (rev->active) {
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+ }
+}
+
+
+static ngx_int_t
+ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,
+ ngx_mail_auth_http_ctx_t *ctx)
+{
+ u_char c, ch, *p;
+ enum {
+ sw_start = 0,
+ sw_name,
+ sw_space_before_value,
+ sw_value,
+ sw_space_after_value,
+ sw_almost_done,
+ sw_header_almost_done
+ } state;
+
+ state = ctx->state;
+
+ for (p = ctx->response->pos; p < ctx->response->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* first char */
+ case sw_start:
+
+ switch (ch) {
+ case CR:
+ ctx->header_end = p;
+ state = sw_header_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto header_done;
+ default:
+ state = sw_name;
+ ctx->header_name_start = p;
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ return NGX_ERROR;
+ }
+ break;
+
+ /* header name */
+ case sw_name:
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ break;
+ }
+
+ if (ch == ':') {
+ ctx->header_name_end = p;
+ state = sw_space_before_value;
+ break;
+ }
+
+ if (ch == '-') {
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ break;
+ }
+
+ if (ch == CR) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ }
+
+ return NGX_ERROR;
+
+ /* space* before header value */
+ case sw_space_before_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ default:
+ ctx->header_start = p;
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* header value */
+ case sw_value:
+ switch (ch) {
+ case ' ':
+ ctx->header_end = p;
+ state = sw_space_after_value;
+ break;
+ case CR:
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto done;
+ }
+ break;
+
+ /* space* before end of header line */
+ case sw_space_after_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* end of header line */
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+
+ /* end of header */
+ case sw_header_almost_done:
+ switch (ch) {
+ case LF:
+ goto header_done;
+ default:
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ ctx->response->pos = p;
+ ctx->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_OK;
+
+header_done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_DONE;
+}
+
+
+static void
+ngx_mail_auth_http_block_read(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_auth_http_ctx_t *ctx;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail auth http block read");
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ c = rev->data;
+ s = c->data;
+
+ ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);
+
+ ngx_close_connection(ctx->peer.connection);
+ ngx_destroy_pool(ctx->pool);
+ ngx_mail_session_internal_server_error(s);
+ }
+}
+
+
+static void
+ngx_mail_auth_http_dummy_handler(ngx_event_t *ev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, ev->log, 0,
+ "mail auth http dummy handler");
+}
+
+
+static ngx_buf_t *
+ngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool,
+ ngx_mail_auth_http_conf_t *ahcf)
+{
+ size_t len;
+ ngx_buf_t *b;
+ ngx_str_t login, passwd;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) {
+ return NULL;
+ }
+
+ if (ngx_mail_auth_http_escape(pool, &s->passwd, &passwd) != NGX_OK) {
+ return NULL;
+ }
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1
+ + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1
+ + sizeof("Auth-Method: ") - 1
+ + ngx_mail_auth_http_method[s->auth_method].len
+ + sizeof(CRLF) - 1
+ + sizeof("Auth-User: ") - 1 + login.len + sizeof(CRLF) - 1
+ + sizeof("Auth-Pass: ") - 1 + passwd.len + sizeof(CRLF) - 1
+ + sizeof("Auth-Salt: ") - 1 + s->salt.len
+ + sizeof("Auth-Protocol: ") - 1 + cscf->protocol->name.len
+ + sizeof(CRLF) - 1
+ + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN
+ + sizeof(CRLF) - 1
+ + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len
+ + sizeof(CRLF) - 1
+ + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1
+ + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len
+ + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len
+ + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len
+ + ahcf->header.len
+ + sizeof(CRLF) - 1;
+
+ b = ngx_create_temp_buf(pool, len);
+ if (b == NULL) {
+ return NULL;
+ }
+
+ b->last = ngx_cpymem(b->last, "GET ", sizeof("GET ") - 1);
+ b->last = ngx_copy(b->last, ahcf->uri.data, ahcf->uri.len);
+ b->last = ngx_cpymem(b->last, " HTTP/1.0" CRLF,
+ sizeof(" HTTP/1.0" CRLF) - 1);
+
+ b->last = ngx_cpymem(b->last, "Host: ", sizeof("Host: ") - 1);
+ b->last = ngx_copy(b->last, ahcf->host_header.data,
+ ahcf->host_header.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "Auth-Method: ",
+ sizeof("Auth-Method: ") - 1);
+ b->last = ngx_cpymem(b->last,
+ ngx_mail_auth_http_method[s->auth_method].data,
+ ngx_mail_auth_http_method[s->auth_method].len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "Auth-User: ", sizeof("Auth-User: ") - 1);
+ b->last = ngx_copy(b->last, login.data, login.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "Auth-Pass: ", sizeof("Auth-Pass: ") - 1);
+ b->last = ngx_copy(b->last, passwd.data, passwd.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ if (s->auth_method != NGX_MAIL_AUTH_PLAIN && s->salt.len) {
+ b->last = ngx_cpymem(b->last, "Auth-Salt: ", sizeof("Auth-Salt: ") - 1);
+ b->last = ngx_copy(b->last, s->salt.data, s->salt.len);
+
+ s->passwd.data = NULL;
+ }
+
+ b->last = ngx_cpymem(b->last, "Auth-Protocol: ",
+ sizeof("Auth-Protocol: ") - 1);
+ b->last = ngx_cpymem(b->last, cscf->protocol->name.data,
+ cscf->protocol->name.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %ui" CRLF,
+ s->login_attempt);
+
+ b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1);
+ b->last = ngx_copy(b->last, s->connection->addr_text.data,
+ s->connection->addr_text.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ if (s->host.len) {
+ b->last = ngx_cpymem(b->last, "Client-Host: ",
+ sizeof("Client-Host: ") - 1);
+ b->last = ngx_copy(b->last, s->host.data, s->host.len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+
+ /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */
+
+ b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ",
+ sizeof("Auth-SMTP-Helo: ") - 1);
+ b->last = ngx_copy(b->last, s->smtp_helo.data, s->smtp_helo.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "Auth-SMTP-From: ",
+ sizeof("Auth-SMTP-From: ") - 1);
+ b->last = ngx_copy(b->last, s->smtp_from.data, s->smtp_from.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "Auth-SMTP-To: ",
+ sizeof("Auth-SMTP-To: ") - 1);
+ b->last = ngx_copy(b->last, s->smtp_to.data, s->smtp_to.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ }
+
+ if (ahcf->header.len) {
+ b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len);
+ }
+
+ /* add "\r\n" at the header end */
+ *b->last++ = CR; *b->last++ = LF;
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ {
+ ngx_str_t l;
+
+ l.len = b->last - b->pos;
+ l.data = b->pos;
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "mail auth http header:%N\"%V\"", &l);
+ }
+#endif
+
+ return b;
+}
+
+
+static ngx_int_t
+ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, ngx_str_t *escaped)
+{
+ u_char *p;
+ uintptr_t n;
+
+ n = ngx_escape_uri(NULL, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
+
+ if (n == 0) {
+ *escaped = *text;
+ return NGX_OK;
+ }
+
+ escaped->len = text->len + n * 2;
+
+ p = ngx_pnalloc(pool, escaped->len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_escape_uri(p, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);
+
+ escaped->data = p;
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_mail_auth_http_create_conf(ngx_conf_t *cf)
+{
+ ngx_mail_auth_http_conf_t *ahcf;
+
+ ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_auth_http_conf_t));
+ if (ahcf == NULL) {
+ return NULL;
+ }
+
+ ahcf->timeout = NGX_CONF_UNSET_MSEC;
+
+ ahcf->file = cf->conf_file->file.name.data;
+ ahcf->line = cf->conf_file->line;
+
+ return ahcf;
+}
+
+
+static char *
+ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_auth_http_conf_t *prev = parent;
+ ngx_mail_auth_http_conf_t *conf = child;
+
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_table_elt_t *header;
+
+ if (conf->peer == NULL) {
+ conf->peer = prev->peer;
+ conf->host_header = prev->host_header;
+ conf->uri = prev->uri;
+
+ if (conf->peer == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"auth_http\" is defined for server in %s:%ui",
+ conf->file, conf->line);
+
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
+
+ if (conf->headers == NULL) {
+ conf->headers = prev->headers;
+ conf->header = prev->header;
+ }
+
+ if (conf->headers && conf->header.len == 0) {
+ len = 0;
+ header = conf->headers->elts;
+ for (i = 0; i < conf->headers->nelts; i++) {
+ len += header[i].key.len + 2 + header[i].value.len + 2;
+ }
+
+ p = ngx_pnalloc(cf->pool, len);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->header.len = len;
+ conf->header.data = p;
+
+ for (i = 0; i < conf->headers->nelts; i++) {
+ p = ngx_cpymem(p, header[i].key.data, header[i].key.len);
+ *p++ = ':'; *p++ = ' ';
+ p = ngx_cpymem(p, header[i].value.data, header[i].value.len);
+ *p++ = CR; *p++ = LF;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_auth_http_conf_t *ahcf = conf;
+
+ ngx_str_t *value;
+ ngx_url_t u;
+
+ value = cf->args->elts;
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = value[1];
+ u.default_port = 80;
+ u.uri_part = 1;
+
+ if (ngx_strncmp(u.url.data, "http://", 7) == 0) {
+ u.url.len -= 7;
+ u.url.data += 7;
+ }
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in auth_http \"%V\"", u.err, &u.url);
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ ahcf->peer = u.addrs;
+
+ if (u.family != AF_UNIX) {
+ ahcf->host_header = u.host;
+
+ } else {
+ ngx_str_set(&ahcf->host_header, "localhost");
+ }
+
+ ahcf->uri = u.uri;
+
+ if (ahcf->uri.len == 0) {
+ ngx_str_set(&ahcf->uri, "/");
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_auth_http_conf_t *ahcf = conf;
+
+ ngx_str_t *value;
+ ngx_table_elt_t *header;
+
+ if (ahcf->headers == NULL) {
+ ahcf->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_table_elt_t));
+ if (ahcf->headers == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ header = ngx_array_push(ahcf->headers);
+ if (header == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ header->key = value[1];
+ header->value = value[2];
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_core_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_core_module.c
new file mode 100644
index 00000000000..4ee7c8dc3d7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_core_module.c
@@ -0,0 +1,653 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_mail.h>
+
+
+static void *ngx_mail_core_create_main_conf(ngx_conf_t *cf);
+static void *ngx_mail_core_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_mail_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_conf_deprecated_t ngx_conf_deprecated_so_keepalive = {
+ ngx_conf_deprecated, "so_keepalive",
+ "so_keepalive\" parameter of the \"listen"
+};
+
+
+static ngx_command_t ngx_mail_core_commands[] = {
+
+ { ngx_string("server"),
+ NGX_MAIL_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_mail_core_server,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("listen"),
+ NGX_MAIL_SRV_CONF|NGX_CONF_TAKE12,
+ ngx_mail_core_listen,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("protocol"),
+ NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_mail_core_protocol,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("so_keepalive"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_core_srv_conf_t, so_keepalive),
+ &ngx_conf_deprecated_so_keepalive },
+
+ { ngx_string("timeout"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_core_srv_conf_t, timeout),
+ NULL },
+
+ { ngx_string("server_name"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_core_srv_conf_t, server_name),
+ NULL },
+
+ { ngx_string("resolver"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_mail_core_resolver,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("resolver_timeout"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_core_srv_conf_t, resolver_timeout),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_core_module_ctx = {
+ NULL, /* protocol */
+
+ ngx_mail_core_create_main_conf, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_core_create_srv_conf, /* create server configuration */
+ ngx_mail_core_merge_srv_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_core_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_core_module_ctx, /* module context */
+ ngx_mail_core_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static void *
+ngx_mail_core_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_mail_core_main_conf_t *cmcf;
+
+ cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_main_conf_t));
+ if (cmcf == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(&cmcf->servers, cf->pool, 4,
+ sizeof(ngx_mail_core_srv_conf_t *))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_mail_listen_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return cmcf;
+}
+
+
+static void *
+ngx_mail_core_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_mail_core_srv_conf_t *cscf;
+
+ cscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_srv_conf_t));
+ if (cscf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * cscf->protocol = NULL;
+ */
+
+ cscf->timeout = NGX_CONF_UNSET_MSEC;
+ cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;
+ cscf->so_keepalive = NGX_CONF_UNSET;
+
+ cscf->resolver = NGX_CONF_UNSET_PTR;
+
+ cscf->file_name = cf->conf_file->file.name.data;
+ cscf->line = cf->conf_file->line;
+
+ return cscf;
+}
+
+
+static char *
+ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_core_srv_conf_t *prev = parent;
+ ngx_mail_core_srv_conf_t *conf = child;
+
+ ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
+ ngx_conf_merge_msec_value(conf->resolver_timeout, prev->resolver_timeout,
+ 30000);
+
+ ngx_conf_merge_value(conf->so_keepalive, prev->so_keepalive, 0);
+
+
+ ngx_conf_merge_str_value(conf->server_name, prev->server_name, "");
+
+ if (conf->server_name.len == 0) {
+ conf->server_name = cf->cycle->hostname;
+ }
+
+ if (conf->protocol == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "unknown mail protocol for server in %s:%ui",
+ conf->file_name, conf->line);
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_ptr_value(conf->resolver, prev->resolver, NULL);
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ void *mconf;
+ ngx_uint_t m;
+ ngx_conf_t pcf;
+ ngx_mail_module_t *module;
+ ngx_mail_conf_ctx_t *ctx, *mail_ctx;
+ ngx_mail_core_srv_conf_t *cscf, **cscfp;
+ ngx_mail_core_main_conf_t *cmcf;
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ mail_ctx = cf->ctx;
+ ctx->main_conf = mail_ctx->main_conf;
+
+ /* the server{}'s srv_conf */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_mail_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_MAIL_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+
+ if (module->create_srv_conf) {
+ mconf = module->create_srv_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf;
+ }
+ }
+
+ /* the server configuration context */
+
+ cscf = ctx->srv_conf[ngx_mail_core_module.ctx_index];
+ cscf->ctx = ctx;
+
+ cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index];
+
+ cscfp = ngx_array_push(&cmcf->servers);
+ if (cscfp == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *cscfp = cscf;
+
+
+ /* parse inside server{} */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+ cf->cmd_type = NGX_MAIL_SRV_CONF;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = pcf;
+
+ return rv;
+}
+
+
+static char *
+ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_core_srv_conf_t *cscf = conf;
+
+ size_t len, off;
+ in_port_t port;
+ ngx_str_t *value;
+ ngx_url_t u;
+ ngx_uint_t i, m;
+ struct sockaddr *sa;
+ ngx_mail_listen_t *ls;
+ ngx_mail_module_t *module;
+ struct sockaddr_in *sin;
+ ngx_mail_core_main_conf_t *cmcf;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ value = cf->args->elts;
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = value[1];
+ u.listen = 1;
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in \"%V\" of the \"listen\" directive",
+ u.err, &u.url);
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ cmcf = ngx_mail_conf_get_module_main_conf(cf, ngx_mail_core_module);
+
+ ls = cmcf->listen.elts;
+
+ for (i = 0; i < cmcf->listen.nelts; i++) {
+
+ sa = (struct sockaddr *) ls[i].sockaddr;
+
+ if (sa->sa_family != u.family) {
+ continue;
+ }
+
+ switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ off = offsetof(struct sockaddr_in6, sin6_addr);
+ len = 16;
+ sin6 = (struct sockaddr_in6 *) sa;
+ port = sin6->sin6_port;
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ off = offsetof(struct sockaddr_un, sun_path);
+ len = sizeof(((struct sockaddr_un *) sa)->sun_path);
+ port = 0;
+ break;
+#endif
+
+ default: /* AF_INET */
+ off = offsetof(struct sockaddr_in, sin_addr);
+ len = 4;
+ sin = (struct sockaddr_in *) sa;
+ port = sin->sin_port;
+ break;
+ }
+
+ if (ngx_memcmp(ls[i].sockaddr + off, u.sockaddr + off, len) != 0) {
+ continue;
+ }
+
+ if (port != u.port) {
+ continue;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate \"%V\" address and port pair", &u.url);
+ return NGX_CONF_ERROR;
+ }
+
+ ls = ngx_array_push(&cmcf->listen);
+ if (ls == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(ls, sizeof(ngx_mail_listen_t));
+
+ ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen);
+
+ ls->socklen = u.socklen;
+ ls->wildcard = u.wildcard;
+ ls->ctx = cf->ctx;
+
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ ls->ipv6only = 1;
+#endif
+
+ if (cscf->protocol == NULL) {
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_MAIL_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+
+ if (module->protocol == NULL) {
+ continue;
+ }
+
+ for (i = 0; module->protocol->port[i]; i++) {
+ if (module->protocol->port[i] == u.port) {
+ cscf->protocol = module->protocol;
+ break;
+ }
+ }
+ }
+ }
+
+ for (i = 2; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "bind") == 0) {
+ ls->bind = 1;
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) {
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ struct sockaddr *sa;
+ u_char buf[NGX_SOCKADDR_STRLEN];
+
+ sa = (struct sockaddr *) ls->sockaddr;
+
+ if (sa->sa_family == AF_INET6) {
+
+ if (ngx_strcmp(&value[i].data[10], "n") == 0) {
+ ls->ipv6only = 1;
+
+ } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) {
+ ls->ipv6only = 0;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid ipv6only flags \"%s\"",
+ &value[i].data[9]);
+ return NGX_CONF_ERROR;
+ }
+
+ ls->bind = 1;
+
+ } else {
+ len = ngx_sock_ntop(sa, ls->socklen, buf,
+ NGX_SOCKADDR_STRLEN, 1);
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "ipv6only is not supported "
+ "on addr \"%*s\", ignored", len, buf);
+ }
+
+ continue;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "bind ipv6only is not supported "
+ "on this platform");
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ if (ngx_strcmp(value[i].data, "ssl") == 0) {
+#if (NGX_MAIL_SSL)
+ ls->ssl = 1;
+ continue;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"ssl\" parameter requires "
+ "ngx_mail_ssl_module");
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ if (ngx_strncmp(value[i].data, "so_keepalive=", 13) == 0) {
+
+ if (ngx_strcmp(&value[i].data[13], "on") == 0) {
+ ls->so_keepalive = 1;
+
+ } else if (ngx_strcmp(&value[i].data[13], "off") == 0) {
+ ls->so_keepalive = 2;
+
+ } else {
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ u_char *p, *end;
+ ngx_str_t s;
+
+ end = value[i].data + value[i].len;
+ s.data = value[i].data + 13;
+
+ p = ngx_strlchr(s.data, end, ':');
+ if (p == NULL) {
+ p = end;
+ }
+
+ if (p > s.data) {
+ s.len = p - s.data;
+
+ ls->tcp_keepidle = ngx_parse_time(&s, 1);
+ if (ls->tcp_keepidle == (time_t) NGX_ERROR) {
+ goto invalid_so_keepalive;
+ }
+ }
+
+ s.data = (p < end) ? (p + 1) : end;
+
+ p = ngx_strlchr(s.data, end, ':');
+ if (p == NULL) {
+ p = end;
+ }
+
+ if (p > s.data) {
+ s.len = p - s.data;
+
+ ls->tcp_keepintvl = ngx_parse_time(&s, 1);
+ if (ls->tcp_keepintvl == (time_t) NGX_ERROR) {
+ goto invalid_so_keepalive;
+ }
+ }
+
+ s.data = (p < end) ? (p + 1) : end;
+
+ if (s.data < end) {
+ s.len = end - s.data;
+
+ ls->tcp_keepcnt = ngx_atoi(s.data, s.len);
+ if (ls->tcp_keepcnt == NGX_ERROR) {
+ goto invalid_so_keepalive;
+ }
+ }
+
+ if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0
+ && ls->tcp_keepcnt == 0)
+ {
+ goto invalid_so_keepalive;
+ }
+
+ ls->so_keepalive = 1;
+
+#else
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the \"so_keepalive\" parameter accepts "
+ "only \"on\" or \"off\" on this platform");
+ return NGX_CONF_ERROR;
+
+#endif
+ }
+
+ ls->bind = 1;
+
+ continue;
+
+#if (NGX_HAVE_KEEPALIVE_TUNABLE)
+ invalid_so_keepalive:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid so_keepalive value: \"%s\"",
+ &value[i].data[13]);
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the invalid \"%V\" parameter", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_core_srv_conf_t *cscf = conf;
+
+ ngx_str_t *value;
+ ngx_uint_t m;
+ ngx_mail_module_t *module;
+
+ value = cf->args->elts;
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_MAIL_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+
+ if (module->protocol
+ && ngx_strcmp(module->protocol->name.data, value[1].data) == 0)
+ {
+ cscf->protocol = module->protocol;
+
+ return NGX_CONF_OK;
+ }
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unknown protocol \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_core_srv_conf_t *cscf = conf;
+
+ ngx_str_t *value;
+
+ value = cf->args->elts;
+
+ if (cscf->resolver != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ cscf->resolver = NULL;
+ return NGX_CONF_OK;
+ }
+
+ cscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1);
+ if (cscf->resolver == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *c, *value;
+ ngx_uint_t i;
+ ngx_array_t *a;
+
+ a = (ngx_array_t *) (p + cmd->offset);
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+ c = ngx_array_push(a);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = value[i];
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_handler.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_handler.c
new file mode 100644
index 00000000000..784111f6e80
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_handler.c
@@ -0,0 +1,788 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_mail.h>
+
+
+static void ngx_mail_init_session(ngx_connection_t *c);
+
+#if (NGX_MAIL_SSL)
+static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c);
+static void ngx_mail_ssl_handshake_handler(ngx_connection_t *c);
+#endif
+
+
+void
+ngx_mail_init_connection(ngx_connection_t *c)
+{
+ size_t len;
+ ngx_uint_t i;
+ ngx_mail_port_t *port;
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ ngx_mail_log_ctx_t *ctx;
+ ngx_mail_in_addr_t *addr;
+ ngx_mail_session_t *s;
+ ngx_mail_addr_conf_t *addr_conf;
+ u_char text[NGX_SOCKADDR_STRLEN];
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+ ngx_mail_in6_addr_t *addr6;
+#endif
+
+
+ /* find the server configuration for the address:port */
+
+ port = c->listening->servers;
+
+ if (port->naddrs > 1) {
+
+ /*
+ * There are several addresses on this port and one of them
+ * is the "*:port" wildcard so getsockname() is needed to determine
+ * the server address.
+ *
+ * AcceptEx() already gave this address.
+ */
+
+ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ sa = c->local_sockaddr;
+
+ switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) sa;
+
+ addr6 = port->addrs;
+
+ /* the last address is "*" */
+
+ for (i = 0; i < port->naddrs - 1; i++) {
+ if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
+ break;
+ }
+ }
+
+ addr_conf = &addr6[i].conf;
+
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) sa;
+
+ addr = port->addrs;
+
+ /* the last address is "*" */
+
+ for (i = 0; i < port->naddrs - 1; i++) {
+ if (addr[i].addr == sin->sin_addr.s_addr) {
+ break;
+ }
+ }
+
+ addr_conf = &addr[i].conf;
+
+ break;
+ }
+
+ } else {
+ switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ addr6 = port->addrs;
+ addr_conf = &addr6[0].conf;
+ break;
+#endif
+
+ default: /* AF_INET */
+ addr = port->addrs;
+ addr_conf = &addr[0].conf;
+ break;
+ }
+ }
+
+ s = ngx_pcalloc(c->pool, sizeof(ngx_mail_session_t));
+ if (s == NULL) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ s->main_conf = addr_conf->ctx->main_conf;
+ s->srv_conf = addr_conf->ctx->srv_conf;
+
+ s->addr_text = &addr_conf->addr_text;
+
+ c->data = s;
+ s->connection = c;
+
+ len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1);
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA client %*s connected to %V",
+ c->number, len, text, s->addr_text);
+
+ ctx = ngx_palloc(c->pool, sizeof(ngx_mail_log_ctx_t));
+ if (ctx == NULL) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ ctx->client = &c->addr_text;
+ ctx->session = s;
+
+ c->log->connection = c->number;
+ c->log->handler = ngx_mail_log_error;
+ c->log->data = ctx;
+ c->log->action = "sending client greeting line";
+
+ c->log_error = NGX_ERROR_INFO;
+
+#if (NGX_MAIL_SSL)
+ {
+ ngx_mail_ssl_conf_t *sslcf;
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ if (sslcf->enable) {
+ c->log->action = "SSL handshaking";
+
+ ngx_mail_ssl_init_connection(&sslcf->ssl, c);
+ return;
+ }
+
+ if (addr_conf->ssl) {
+
+ c->log->action = "SSL handshaking";
+
+ if (sslcf->ssl.ctx == NULL) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "no \"ssl_certificate\" is defined "
+ "in server listening on SSL port");
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ ngx_mail_ssl_init_connection(&sslcf->ssl, c);
+ return;
+ }
+
+ }
+#endif
+
+ ngx_mail_init_session(c);
+}
+
+
+#if (NGX_MAIL_SSL)
+
+void
+ngx_mail_starttls_handler(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_ssl_conf_t *sslcf;
+
+ c = rev->data;
+ s = c->data;
+ s->starttls = 1;
+
+ c->log->action = "in starttls state";
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ ngx_mail_ssl_init_connection(&sslcf->ssl, c);
+}
+
+
+static void
+ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c)
+{
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ if (ngx_ssl_create_connection(ssl, c, 0) == NGX_ERROR) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (ngx_ssl_handshake(c) == NGX_AGAIN) {
+
+ s = c->data;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ ngx_add_timer(c->read, cscf->timeout);
+
+ c->ssl->handler = ngx_mail_ssl_handshake_handler;
+
+ return;
+ }
+
+ ngx_mail_ssl_handshake_handler(c);
+}
+
+
+static void
+ngx_mail_ssl_handshake_handler(ngx_connection_t *c)
+{
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ if (c->ssl->handshaked) {
+
+ s = c->data;
+
+ if (s->starttls) {
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ c->read->handler = cscf->protocol->init_protocol;
+ c->write->handler = ngx_mail_send;
+
+ cscf->protocol->init_protocol(c->read);
+
+ return;
+ }
+
+ c->read->ready = 0;
+
+ ngx_mail_init_session(c);
+ return;
+ }
+
+ ngx_mail_close_connection(c);
+}
+
+#endif
+
+
+static void
+ngx_mail_init_session(ngx_connection_t *c)
+{
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ s = c->data;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ s->protocol = cscf->protocol->type;
+
+ s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_mail_max_module);
+ if (s->ctx == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ c->write->handler = ngx_mail_send;
+
+ cscf->protocol->init_session(s, c);
+}
+
+
+ngx_int_t
+ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_mail_core_srv_conf_t *cscf)
+{
+ s->salt.data = ngx_pnalloc(c->pool,
+ sizeof(" <18446744073709551616.@>" CRLF) - 1
+ + NGX_TIME_T_LEN
+ + cscf->server_name.len);
+ if (s->salt.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ s->salt.len = ngx_sprintf(s->salt.data, "<%ul.%T@%V>" CRLF,
+ ngx_random(), ngx_time(), &cscf->server_name)
+ - s->salt.data;
+
+ return NGX_OK;
+}
+
+
+#if (NGX_MAIL_SSL)
+
+ngx_int_t
+ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_mail_ssl_conf_t *sslcf;
+
+ if (c->ssl) {
+ return 0;
+ }
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
+
+
+ngx_int_t
+ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n)
+{
+ u_char *p, *last;
+ ngx_str_t *arg, plain;
+
+ arg = s->args.elts;
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth plain: \"%V\"", &arg[n]);
+#endif
+
+ plain.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
+ if (plain.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_decode_base64(&plain, &arg[n]) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid base64 encoding in AUTH PLAIN command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ p = plain.data;
+ last = p + plain.len;
+
+ while (p < last && *p++) { /* void */ }
+
+ if (p == last) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid login in AUTH PLAIN command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ s->login.data = p;
+
+ while (p < last && *p) { p++; }
+
+ if (p == last) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid password in AUTH PLAIN command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ s->login.len = p++ - s->login.data;
+
+ s->passwd.len = last - p;
+ s->passwd.data = p;
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth plain: \"%V\" \"%V\"", &s->login, &s->passwd);
+#endif
+
+ return NGX_DONE;
+}
+
+
+ngx_int_t
+ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_uint_t n)
+{
+ ngx_str_t *arg;
+
+ arg = s->args.elts;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth login username: \"%V\"", &arg[n]);
+
+ s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));
+ if (s->login.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_decode_base64(&s->login, &arg[n]) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid base64 encoding in AUTH LOGIN command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth login username: \"%V\"", &s->login);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+
+ arg = s->args.elts;
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth login password: \"%V\"", &arg[0]);
+#endif
+
+ s->passwd.data = ngx_pnalloc(c->pool,
+ ngx_base64_decoded_length(arg[0].len));
+ if (s->passwd.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid base64 encoding in AUTH LOGIN command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth login password: \"%V\"", &s->passwd);
+#endif
+
+ return NGX_DONE;
+}
+
+
+ngx_int_t
+ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c,
+ char *prefix, size_t len)
+{
+ u_char *p;
+ ngx_str_t salt;
+ ngx_uint_t n;
+
+ p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ salt.data = ngx_cpymem(p, prefix, len);
+ s->salt.len -= 2;
+
+ ngx_encode_base64(&salt, &s->salt);
+
+ s->salt.len += 2;
+ n = len + salt.len;
+ p[n++] = CR; p[n++] = LF;
+
+ s->out.len = n;
+ s->out.data = p;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ u_char *p, *last;
+ ngx_str_t *arg;
+
+ arg = s->args.elts;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth cram-md5: \"%V\"", &arg[0]);
+
+ s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));
+ if (s->login.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid base64 encoding in AUTH CRAM-MD5 command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ p = s->login.data;
+ last = p + s->login.len;
+
+ while (p < last) {
+ if (*p++ == ' ') {
+ s->login.len = p - s->login.data - 1;
+ s->passwd.len = last - p;
+ s->passwd.data = p;
+ break;
+ }
+ }
+
+ if (s->passwd.len != 32) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent invalid CRAM-MD5 hash in AUTH CRAM-MD5 command");
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "mail auth cram-md5: \"%V\" \"%V\"", &s->login, &s->passwd);
+
+ s->auth_method = NGX_MAIL_AUTH_CRAM_MD5;
+
+ return NGX_DONE;
+}
+
+
+void
+ngx_mail_send(ngx_event_t *wev)
+{
+ ngx_int_t n;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ c = wev->data;
+ s = c->data;
+
+ if (wev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (s->out.len == 0) {
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+
+ return;
+ }
+
+ n = c->send(c, s->out.data, s->out.len);
+
+ if (n > 0) {
+ s->out.data += n;
+ s->out.len -= n;
+
+ if (s->out.len != 0) {
+ goto again;
+ }
+
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+
+ if (s->quit) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (s->blocked) {
+ c->read->handler(c->read);
+ }
+
+ return;
+ }
+
+ if (n == NGX_ERROR) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ /* n == NGX_AGAIN */
+
+again:
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ ngx_add_timer(c->write, cscf->timeout);
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+}
+
+
+ngx_int_t
+ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ssize_t n;
+ ngx_int_t rc;
+ ngx_str_t l;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);
+
+ if (n == NGX_ERROR || n == 0) {
+ ngx_mail_close_connection(c);
+ return NGX_ERROR;
+ }
+
+ if (n > 0) {
+ s->buffer->last += n;
+ }
+
+ if (n == NGX_AGAIN) {
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_mail_session_internal_server_error(s);
+ return NGX_ERROR;
+ }
+
+ if (s->buffer->pos == s->buffer->last) {
+ return NGX_AGAIN;
+ }
+ }
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ rc = cscf->protocol->parse_command(s);
+
+ if (rc == NGX_AGAIN) {
+
+ if (s->buffer->last < s->buffer->end) {
+ return rc;
+ }
+
+ l.len = s->buffer->last - s->buffer->start;
+ l.data = s->buffer->start;
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "client sent too long command \"%V\"", &l);
+
+ s->quit = 1;
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (rc == NGX_IMAP_NEXT || rc == NGX_MAIL_PARSE_INVALID_COMMAND) {
+ return rc;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_mail_close_connection(c);
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ s->args.nelts = 0;
+
+ if (s->buffer->pos == s->buffer->last) {
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+ }
+
+ s->state = 0;
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ s->login_attempt++;
+
+ ngx_mail_auth_http_init(s);
+}
+
+
+void
+ngx_mail_session_internal_server_error(ngx_mail_session_t *s)
+{
+ ngx_mail_core_srv_conf_t *cscf;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ s->out = cscf->protocol->internal_server_error;
+ s->quit = 1;
+
+ ngx_mail_send(s->connection->write);
+}
+
+
+void
+ngx_mail_close_connection(ngx_connection_t *c)
+{
+ ngx_pool_t *pool;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "close mail connection: %d", c->fd);
+
+#if (NGX_MAIL_SSL)
+
+ if (c->ssl) {
+ if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+ c->ssl->handler = ngx_mail_close_connection;
+ return;
+ }
+ }
+
+#endif
+
+#if (NGX_STAT_STUB)
+ (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
+#endif
+
+ c->destroyed = 1;
+
+ pool = c->pool;
+
+ ngx_close_connection(c);
+
+ ngx_destroy_pool(pool);
+}
+
+
+u_char *
+ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ u_char *p;
+ ngx_mail_session_t *s;
+ ngx_mail_log_ctx_t *ctx;
+
+ if (log->action) {
+ p = ngx_snprintf(buf, len, " while %s", log->action);
+ len -= p - buf;
+ buf = p;
+ }
+
+ ctx = log->data;
+
+ p = ngx_snprintf(buf, len, ", client: %V", ctx->client);
+ len -= p - buf;
+ buf = p;
+
+ s = ctx->session;
+
+ if (s == NULL) {
+ return p;
+ }
+
+ p = ngx_snprintf(buf, len, "%s, server: %V",
+ s->starttls ? " using starttls" : "",
+ s->addr_text);
+ len -= p - buf;
+ buf = p;
+
+ if (s->login.len == 0) {
+ return p;
+ }
+
+ p = ngx_snprintf(buf, len, ", login: \"%V\"", &s->login);
+ len -= p - buf;
+ buf = p;
+
+ if (s->proxy == NULL) {
+ return p;
+ }
+
+ p = ngx_snprintf(buf, len, ", upstream: %V", s->proxy->upstream.name);
+
+ return p;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_imap_handler.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_imap_handler.c
new file mode 100644
index 00000000000..57e2fb77daa
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_imap_handler.c
@@ -0,0 +1,457 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_mail.h>
+#include <ngx_mail_imap_module.h>
+
+
+static ngx_int_t ngx_mail_imap_login(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+static ngx_int_t ngx_mail_imap_authenticate(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+static ngx_int_t ngx_mail_imap_capability(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+static ngx_int_t ngx_mail_imap_starttls(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+
+
+static u_char imap_greeting[] = "* OK IMAP4 ready" CRLF;
+static u_char imap_star[] = "* ";
+static u_char imap_ok[] = "OK completed" CRLF;
+static u_char imap_next[] = "+ OK" CRLF;
+static u_char imap_plain_next[] = "+ " CRLF;
+static u_char imap_username[] = "+ VXNlcm5hbWU6" CRLF;
+static u_char imap_password[] = "+ UGFzc3dvcmQ6" CRLF;
+static u_char imap_bye[] = "* BYE" CRLF;
+static u_char imap_invalid_command[] = "BAD invalid command" CRLF;
+
+
+void
+ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_mail_core_srv_conf_t *cscf;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ ngx_str_set(&s->out, imap_greeting);
+
+ c->read->handler = ngx_mail_imap_init_protocol;
+
+ ngx_add_timer(c->read, cscf->timeout);
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+
+ ngx_mail_send(c->write);
+}
+
+
+void
+ngx_mail_imap_init_protocol(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_imap_srv_conf_t *iscf;
+
+ c = rev->data;
+
+ c->log->action = "in auth state";
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ s = c->data;
+
+ if (s->buffer == NULL) {
+ if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t))
+ == NGX_ERROR)
+ {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);
+
+ s->buffer = ngx_create_temp_buf(c->pool, iscf->client_buffer_size);
+ if (s->buffer == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+ }
+
+ s->mail_state = ngx_imap_start;
+ c->read->handler = ngx_mail_imap_auth_state;
+
+ ngx_mail_imap_auth_state(rev);
+}
+
+
+void
+ngx_mail_imap_auth_state(ngx_event_t *rev)
+{
+ u_char *p, *dst, *src, *end;
+ ngx_str_t *arg;
+ ngx_int_t rc;
+ ngx_uint_t tag, i;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth state");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (s->out.len) {
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap send handler busy");
+ s->blocked = 1;
+ return;
+ }
+
+ s->blocked = 0;
+
+ rc = ngx_mail_read_command(s, c);
+
+ if (rc == NGX_AGAIN || rc == NGX_ERROR) {
+ return;
+ }
+
+ tag = 1;
+ s->text.len = 0;
+ ngx_str_set(&s->out, imap_ok);
+
+ if (rc == NGX_OK) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "imap auth command: %i",
+ s->command);
+
+ if (s->backslash) {
+
+ arg = s->args.elts;
+
+ for (i = 0; i < s->args.nelts; i++) {
+ dst = arg[i].data;
+ end = dst + arg[i].len;
+
+ for (src = dst; src < end; dst++) {
+ *dst = *src;
+ if (*src++ == '\\') {
+ *dst = *src++;
+ }
+ }
+
+ arg[i].len = dst - arg[i].data;
+ }
+
+ s->backslash = 0;
+ }
+
+ switch (s->mail_state) {
+
+ case ngx_imap_start:
+
+ switch (s->command) {
+
+ case NGX_IMAP_LOGIN:
+ rc = ngx_mail_imap_login(s, c);
+ break;
+
+ case NGX_IMAP_AUTHENTICATE:
+ rc = ngx_mail_imap_authenticate(s, c);
+ tag = (rc != NGX_OK);
+ break;
+
+ case NGX_IMAP_CAPABILITY:
+ rc = ngx_mail_imap_capability(s, c);
+ break;
+
+ case NGX_IMAP_LOGOUT:
+ s->quit = 1;
+ ngx_str_set(&s->text, imap_bye);
+ break;
+
+ case NGX_IMAP_NOOP:
+ break;
+
+ case NGX_IMAP_STARTTLS:
+ rc = ngx_mail_imap_starttls(s, c);
+ break;
+
+ default:
+ rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+ break;
+ }
+
+ break;
+
+ case ngx_imap_auth_login_username:
+ rc = ngx_mail_auth_login_username(s, c, 0);
+
+ tag = 0;
+ ngx_str_set(&s->out, imap_password);
+ s->mail_state = ngx_imap_auth_login_password;
+
+ break;
+
+ case ngx_imap_auth_login_password:
+ rc = ngx_mail_auth_login_password(s, c);
+ break;
+
+ case ngx_imap_auth_plain:
+ rc = ngx_mail_auth_plain(s, c, 0);
+ break;
+
+ case ngx_imap_auth_cram_md5:
+ rc = ngx_mail_auth_cram_md5(s, c);
+ break;
+ }
+
+ } else if (rc == NGX_IMAP_NEXT) {
+ tag = 0;
+ ngx_str_set(&s->out, imap_next);
+ }
+
+ switch (rc) {
+
+ case NGX_DONE:
+ ngx_mail_auth(s, c);
+ return;
+
+ case NGX_ERROR:
+ ngx_mail_session_internal_server_error(s);
+ return;
+
+ case NGX_MAIL_PARSE_INVALID_COMMAND:
+ s->state = 0;
+ ngx_str_set(&s->out, imap_invalid_command);
+ s->mail_state = ngx_imap_start;
+ break;
+ }
+
+ if (tag) {
+ if (s->tag.len == 0) {
+ ngx_str_set(&s->tag, imap_star);
+ }
+
+ if (s->tagged_line.len < s->tag.len + s->text.len + s->out.len) {
+ s->tagged_line.len = s->tag.len + s->text.len + s->out.len;
+ s->tagged_line.data = ngx_pnalloc(c->pool, s->tagged_line.len);
+ if (s->tagged_line.data == NULL) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+ }
+
+ p = s->tagged_line.data;
+
+ if (s->text.len) {
+ p = ngx_cpymem(p, s->text.data, s->text.len);
+ }
+
+ p = ngx_cpymem(p, s->tag.data, s->tag.len);
+ ngx_memcpy(p, s->out.data, s->out.len);
+
+ s->out.len = s->text.len + s->tag.len + s->out.len;
+ s->out.data = s->tagged_line.data;
+ }
+
+ if (rc != NGX_IMAP_NEXT) {
+ s->args.nelts = 0;
+
+ if (s->state) {
+ /* preserve tag */
+ s->arg_start = s->buffer->start + s->tag.len;
+ s->buffer->pos = s->arg_start;
+ s->buffer->last = s->arg_start;
+
+ } else {
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+ s->tag.len = 0;
+ }
+ }
+
+ ngx_mail_send(c->write);
+}
+
+
+static ngx_int_t
+ngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ arg = s->args.elts;
+
+ if (s->args.nelts != 2 || arg[0].len == 0) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ s->login.len = arg[0].len;
+ s->login.data = ngx_pnalloc(c->pool, s->login.len);
+ if (s->login.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->login.data, arg[0].data, s->login.len);
+
+ s->passwd.len = arg[1].len;
+ s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
+ if (s->passwd.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "imap login:\"%V\" passwd:\"%V\"",
+ &s->login, &s->passwd);
+#else
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "imap login:\"%V\"", &s->login);
+#endif
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_int_t rc;
+ ngx_mail_core_srv_conf_t *cscf;
+ ngx_mail_imap_srv_conf_t *iscf;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ rc = ngx_mail_auth_parse(s, c);
+
+ switch (rc) {
+
+ case NGX_MAIL_AUTH_LOGIN:
+
+ ngx_str_set(&s->out, imap_username);
+ s->mail_state = ngx_imap_auth_login_username;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_LOGIN_USERNAME:
+
+ ngx_str_set(&s->out, imap_password);
+ s->mail_state = ngx_imap_auth_login_password;
+
+ return ngx_mail_auth_login_username(s, c, 1);
+
+ case NGX_MAIL_AUTH_PLAIN:
+
+ ngx_str_set(&s->out, imap_plain_next);
+ s->mail_state = ngx_imap_auth_plain;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_CRAM_MD5:
+
+ iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);
+
+ if (!(iscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (s->salt.data == NULL) {
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {
+ s->mail_state = ngx_imap_auth_cram_md5;
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_mail_imap_capability(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_mail_imap_srv_conf_t *iscf;
+
+ iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);
+
+#if (NGX_MAIL_SSL)
+
+ if (c->ssl == NULL) {
+ ngx_mail_ssl_conf_t *sslcf;
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
+ s->text = iscf->starttls_capability;
+ return NGX_OK;
+ }
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+ s->text = iscf->starttls_only_capability;
+ return NGX_OK;
+ }
+ }
+#endif
+
+ s->text = iscf->capability;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_imap_starttls(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+#if (NGX_MAIL_SSL)
+ ngx_mail_ssl_conf_t *sslcf;
+
+ if (c->ssl == NULL) {
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+ if (sslcf->starttls) {
+ c->read->handler = ngx_mail_starttls_handler;
+ return NGX_OK;
+ }
+ }
+
+#endif
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_imap_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_imap_module.c
new file mode 100644
index 00000000000..dc80b4fb4c1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_imap_module.c
@@ -0,0 +1,253 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_mail.h>
+#include <ngx_mail_imap_module.h>
+
+
+static void *ngx_mail_imap_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+
+static ngx_str_t ngx_mail_imap_default_capabilities[] = {
+ ngx_string("IMAP4"),
+ ngx_string("IMAP4rev1"),
+ ngx_string("UIDPLUS"),
+ ngx_null_string
+};
+
+
+static ngx_conf_bitmask_t ngx_mail_imap_auth_methods[] = {
+ { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
+ { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },
+ { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_str_t ngx_mail_imap_auth_methods_names[] = {
+ ngx_string("AUTH=PLAIN"),
+ ngx_string("AUTH=LOGIN"),
+ ngx_null_string, /* APOP */
+ ngx_string("AUTH=CRAM-MD5"),
+ ngx_null_string /* NONE */
+};
+
+
+static ngx_mail_protocol_t ngx_mail_imap_protocol = {
+ ngx_string("imap"),
+ { 143, 993, 0, 0 },
+ NGX_MAIL_IMAP_PROTOCOL,
+
+ ngx_mail_imap_init_session,
+ ngx_mail_imap_init_protocol,
+ ngx_mail_imap_parse_command,
+ ngx_mail_imap_auth_state,
+
+ ngx_string("* BAD internal server error" CRLF)
+};
+
+
+static ngx_command_t ngx_mail_imap_commands[] = {
+
+ { ngx_string("imap_client_buffer"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_imap_srv_conf_t, client_buffer_size),
+ NULL },
+
+ { ngx_string("imap_capabilities"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_mail_capabilities,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_imap_srv_conf_t, capabilities),
+ NULL },
+
+ { ngx_string("imap_auth"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_imap_srv_conf_t, auth_methods),
+ &ngx_mail_imap_auth_methods },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_imap_module_ctx = {
+ &ngx_mail_imap_protocol, /* protocol */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_imap_create_srv_conf, /* create server configuration */
+ ngx_mail_imap_merge_srv_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_imap_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_imap_module_ctx, /* module context */
+ ngx_mail_imap_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static void *
+ngx_mail_imap_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_mail_imap_srv_conf_t *iscf;
+
+ iscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_imap_srv_conf_t));
+ if (iscf == NULL) {
+ return NULL;
+ }
+
+ iscf->client_buffer_size = NGX_CONF_UNSET_SIZE;
+
+ if (ngx_array_init(&iscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return iscf;
+}
+
+
+static char *
+ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_imap_srv_conf_t *prev = parent;
+ ngx_mail_imap_srv_conf_t *conf = child;
+
+ u_char *p, *auth;
+ size_t size;
+ ngx_str_t *c, *d;
+ ngx_uint_t i, m;
+
+ ngx_conf_merge_size_value(conf->client_buffer_size,
+ prev->client_buffer_size,
+ (size_t) ngx_pagesize);
+
+ ngx_conf_merge_bitmask_value(conf->auth_methods,
+ prev->auth_methods,
+ (NGX_CONF_BITMASK_SET
+ |NGX_MAIL_AUTH_PLAIN_ENABLED));
+
+
+ if (conf->capabilities.nelts == 0) {
+ conf->capabilities = prev->capabilities;
+ }
+
+ if (conf->capabilities.nelts == 0) {
+
+ for (d = ngx_mail_imap_default_capabilities; d->len; d++) {
+ c = ngx_array_push(&conf->capabilities);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = *d;
+ }
+ }
+
+ size = sizeof("* CAPABILITY" CRLF) - 1;
+
+ c = conf->capabilities.elts;
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ size += 1 + c[i].len;
+ }
+
+ for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+ m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+ m <<= 1, i++)
+ {
+ if (m & conf->auth_methods) {
+ size += 1 + ngx_mail_imap_auth_methods_names[i].len;
+ }
+ }
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->capability.len = size;
+ conf->capability.data = p;
+
+ p = ngx_cpymem(p, "* CAPABILITY", sizeof("* CAPABILITY") - 1);
+
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ *p++ = ' ';
+ p = ngx_cpymem(p, c[i].data, c[i].len);
+ }
+
+ auth = p;
+
+ for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+ m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+ m <<= 1, i++)
+ {
+ if (m & conf->auth_methods) {
+ *p++ = ' ';
+ p = ngx_cpymem(p, ngx_mail_imap_auth_methods_names[i].data,
+ ngx_mail_imap_auth_methods_names[i].len);
+ }
+ }
+
+ *p++ = CR; *p = LF;
+
+
+ size += sizeof(" STARTTLS") - 1;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->starttls_capability.len = size;
+ conf->starttls_capability.data = p;
+
+ p = ngx_cpymem(p, conf->capability.data,
+ conf->capability.len - (sizeof(CRLF) - 1));
+ p = ngx_cpymem(p, " STARTTLS", sizeof(" STARTTLS") - 1);
+ *p++ = CR; *p = LF;
+
+
+ size = (auth - conf->capability.data) + sizeof(CRLF) - 1
+ + sizeof(" STARTTLS LOGINDISABLED") - 1;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->starttls_only_capability.len = size;
+ conf->starttls_only_capability.data = p;
+
+ p = ngx_cpymem(p, conf->capability.data,
+ auth - conf->capability.data);
+ p = ngx_cpymem(p, " STARTTLS LOGINDISABLED",
+ sizeof(" STARTTLS LOGINDISABLED") - 1);
+ *p++ = CR; *p = LF;
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_imap_module.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_imap_module.h
new file mode 100644
index 00000000000..131b44597f8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_imap_module.h
@@ -0,0 +1,39 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_MAIL_IMAP_MODULE_H_INCLUDED_
+#define _NGX_MAIL_IMAP_MODULE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_mail.h>
+
+
+typedef struct {
+ size_t client_buffer_size;
+
+ ngx_str_t capability;
+ ngx_str_t starttls_capability;
+ ngx_str_t starttls_only_capability;
+
+ ngx_uint_t auth_methods;
+
+ ngx_array_t capabilities;
+} ngx_mail_imap_srv_conf_t;
+
+
+void ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c);
+void ngx_mail_imap_init_protocol(ngx_event_t *rev);
+void ngx_mail_imap_auth_state(ngx_event_t *rev);
+ngx_int_t ngx_mail_imap_parse_command(ngx_mail_session_t *s);
+
+
+extern ngx_module_t ngx_mail_imap_module;
+
+
+#endif /* _NGX_MAIL_IMAP_MODULE_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_parse.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_parse.c
new file mode 100644
index 00000000000..b158f5a0fba
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_parse.c
@@ -0,0 +1,918 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_mail.h>
+#include <ngx_mail_pop3_module.h>
+#include <ngx_mail_imap_module.h>
+#include <ngx_mail_smtp_module.h>
+
+
+ngx_int_t
+ngx_mail_pop3_parse_command(ngx_mail_session_t *s)
+{
+ u_char ch, *p, *c, c0, c1, c2, c3;
+ ngx_str_t *arg;
+ enum {
+ sw_start = 0,
+ sw_spaces_before_argument,
+ sw_argument,
+ sw_almost_done
+ } state;
+
+ state = s->state;
+
+ for (p = s->buffer->pos; p < s->buffer->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* POP3 command */
+ case sw_start:
+ if (ch == ' ' || ch == CR || ch == LF) {
+ c = s->buffer->start;
+
+ if (p - c == 4) {
+
+ c0 = ngx_toupper(c[0]);
+ c1 = ngx_toupper(c[1]);
+ c2 = ngx_toupper(c[2]);
+ c3 = ngx_toupper(c[3]);
+
+ if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R')
+ {
+ s->command = NGX_POP3_USER;
+
+ } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S')
+ {
+ s->command = NGX_POP3_PASS;
+
+ } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P')
+ {
+ s->command = NGX_POP3_APOP;
+
+ } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
+ {
+ s->command = NGX_POP3_QUIT;
+
+ } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A')
+ {
+ s->command = NGX_POP3_CAPA;
+
+ } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
+ {
+ s->command = NGX_POP3_AUTH;
+
+ } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
+ {
+ s->command = NGX_POP3_NOOP;
+#if (NGX_MAIL_SSL)
+ } else if (c0 == 'S' && c1 == 'T' && c2 == 'L' && c3 == 'S')
+ {
+ s->command = NGX_POP3_STLS;
+#endif
+ } else {
+ goto invalid;
+ }
+
+ } else {
+ goto invalid;
+ }
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+ }
+
+ if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
+ goto invalid;
+ }
+
+ break;
+
+ case sw_spaces_before_argument:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ s->arg_end = p;
+ break;
+ case LF:
+ s->arg_end = p;
+ goto done;
+ default:
+ if (s->args.nelts <= 2) {
+ state = sw_argument;
+ s->arg_start = p;
+ break;
+ }
+ goto invalid;
+ }
+ break;
+
+ case sw_argument:
+ switch (ch) {
+
+ case ' ':
+
+ /*
+ * the space should be considered as part of the at username
+ * or password, but not of argument in other commands
+ */
+
+ if (s->command == NGX_POP3_USER
+ || s->command == NGX_POP3_PASS)
+ {
+ break;
+ }
+
+ /* fall through */
+
+ case CR:
+ case LF:
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = p - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ goto invalid;
+ }
+ }
+ }
+
+ s->buffer->pos = p;
+ s->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ s->buffer->pos = p + 1;
+
+ if (s->arg_start) {
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = s->arg_end - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+ }
+
+ s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;
+
+ return NGX_OK;
+
+invalid:
+
+ s->state = sw_start;
+ s->arg_start = NULL;
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
+
+
+ngx_int_t
+ngx_mail_imap_parse_command(ngx_mail_session_t *s)
+{
+ u_char ch, *p, *c;
+ ngx_str_t *arg;
+ enum {
+ sw_start = 0,
+ sw_spaces_before_command,
+ sw_command,
+ sw_spaces_before_argument,
+ sw_argument,
+ sw_backslash,
+ sw_literal,
+ sw_no_sync_literal_argument,
+ sw_start_literal_argument,
+ sw_literal_argument,
+ sw_end_literal_argument,
+ sw_almost_done
+ } state;
+
+ state = s->state;
+
+ for (p = s->buffer->pos; p < s->buffer->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* IMAP tag */
+ case sw_start:
+ switch (ch) {
+ case ' ':
+ s->tag.len = p - s->buffer->start + 1;
+ s->tag.data = s->buffer->start;
+ state = sw_spaces_before_command;
+ break;
+ case CR:
+ s->state = sw_start;
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ case LF:
+ s->state = sw_start;
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+ break;
+
+ case sw_spaces_before_command:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ s->state = sw_start;
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ case LF:
+ s->state = sw_start;
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ default:
+ s->cmd_start = p;
+ state = sw_command;
+ break;
+ }
+ break;
+
+ case sw_command:
+ if (ch == ' ' || ch == CR || ch == LF) {
+
+ c = s->cmd_start;
+
+ switch (p - c) {
+
+ case 4:
+ if ((c[0] == 'N' || c[0] == 'n')
+ && (c[1] == 'O'|| c[1] == 'o')
+ && (c[2] == 'O'|| c[2] == 'o')
+ && (c[3] == 'P'|| c[3] == 'p'))
+ {
+ s->command = NGX_IMAP_NOOP;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 5:
+ if ((c[0] == 'L'|| c[0] == 'l')
+ && (c[1] == 'O'|| c[1] == 'o')
+ && (c[2] == 'G'|| c[2] == 'g')
+ && (c[3] == 'I'|| c[3] == 'i')
+ && (c[4] == 'N'|| c[4] == 'n'))
+ {
+ s->command = NGX_IMAP_LOGIN;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 6:
+ if ((c[0] == 'L'|| c[0] == 'l')
+ && (c[1] == 'O'|| c[1] == 'o')
+ && (c[2] == 'G'|| c[2] == 'g')
+ && (c[3] == 'O'|| c[3] == 'o')
+ && (c[4] == 'U'|| c[4] == 'u')
+ && (c[5] == 'T'|| c[5] == 't'))
+ {
+ s->command = NGX_IMAP_LOGOUT;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+#if (NGX_MAIL_SSL)
+ case 8:
+ if ((c[0] == 'S'|| c[0] == 's')
+ && (c[1] == 'T'|| c[1] == 't')
+ && (c[2] == 'A'|| c[2] == 'a')
+ && (c[3] == 'R'|| c[3] == 'r')
+ && (c[4] == 'T'|| c[4] == 't')
+ && (c[5] == 'T'|| c[5] == 't')
+ && (c[6] == 'L'|| c[6] == 'l')
+ && (c[7] == 'S'|| c[7] == 's'))
+ {
+ s->command = NGX_IMAP_STARTTLS;
+
+ } else {
+ goto invalid;
+ }
+ break;
+#endif
+
+ case 10:
+ if ((c[0] == 'C'|| c[0] == 'c')
+ && (c[1] == 'A'|| c[1] == 'a')
+ && (c[2] == 'P'|| c[2] == 'p')
+ && (c[3] == 'A'|| c[3] == 'a')
+ && (c[4] == 'B'|| c[4] == 'b')
+ && (c[5] == 'I'|| c[5] == 'i')
+ && (c[6] == 'L'|| c[6] == 'l')
+ && (c[7] == 'I'|| c[7] == 'i')
+ && (c[8] == 'T'|| c[8] == 't')
+ && (c[9] == 'Y'|| c[9] == 'y'))
+ {
+ s->command = NGX_IMAP_CAPABILITY;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 12:
+ if ((c[0] == 'A'|| c[0] == 'a')
+ && (c[1] == 'U'|| c[1] == 'u')
+ && (c[2] == 'T'|| c[2] == 't')
+ && (c[3] == 'H'|| c[3] == 'h')
+ && (c[4] == 'E'|| c[4] == 'e')
+ && (c[5] == 'N'|| c[5] == 'n')
+ && (c[6] == 'T'|| c[6] == 't')
+ && (c[7] == 'I'|| c[7] == 'i')
+ && (c[8] == 'C'|| c[8] == 'c')
+ && (c[9] == 'A'|| c[9] == 'a')
+ && (c[10] == 'T'|| c[10] == 't')
+ && (c[11] == 'E'|| c[11] == 'e'))
+ {
+ s->command = NGX_IMAP_AUTHENTICATE;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ default:
+ goto invalid;
+ }
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+ }
+
+ if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
+ goto invalid;
+ }
+
+ break;
+
+ case sw_spaces_before_argument:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ s->arg_end = p;
+ break;
+ case LF:
+ s->arg_end = p;
+ goto done;
+ case '"':
+ if (s->args.nelts <= 2) {
+ s->quoted = 1;
+ s->arg_start = p + 1;
+ state = sw_argument;
+ break;
+ }
+ goto invalid;
+ case '{':
+ if (s->args.nelts <= 2) {
+ state = sw_literal;
+ break;
+ }
+ goto invalid;
+ default:
+ if (s->args.nelts <= 2) {
+ s->arg_start = p;
+ state = sw_argument;
+ break;
+ }
+ goto invalid;
+ }
+ break;
+
+ case sw_argument:
+ if (ch == ' ' && s->quoted) {
+ break;
+ }
+
+ switch (ch) {
+ case '"':
+ if (!s->quoted) {
+ break;
+ }
+ s->quoted = 0;
+ /* fall through */
+ case ' ':
+ case CR:
+ case LF:
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = p - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+
+ switch (ch) {
+ case '"':
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+ case '\\':
+ if (s->quoted) {
+ s->backslash = 1;
+ state = sw_backslash;
+ }
+ break;
+ }
+ break;
+
+ case sw_backslash:
+ switch (ch) {
+ case CR:
+ case LF:
+ goto invalid;
+ default:
+ state = sw_argument;
+ }
+ break;
+
+ case sw_literal:
+ if (ch >= '0' && ch <= '9') {
+ s->literal_len = s->literal_len * 10 + (ch - '0');
+ break;
+ }
+ if (ch == '}') {
+ state = sw_start_literal_argument;
+ break;
+ }
+ if (ch == '+') {
+ state = sw_no_sync_literal_argument;
+ break;
+ }
+ goto invalid;
+
+ case sw_no_sync_literal_argument:
+ if (ch == '}') {
+ s->no_sync_literal = 1;
+ state = sw_start_literal_argument;
+ break;
+ }
+ goto invalid;
+
+ case sw_start_literal_argument:
+ switch (ch) {
+ case CR:
+ break;
+ case LF:
+ s->buffer->pos = p + 1;
+ s->arg_start = p + 1;
+ if (s->no_sync_literal == 0) {
+ s->state = sw_literal_argument;
+ return NGX_IMAP_NEXT;
+ }
+ state = sw_literal_argument;
+ s->no_sync_literal = 0;
+ break;
+ default:
+ goto invalid;
+ }
+ break;
+
+ case sw_literal_argument:
+ if (s->literal_len && --s->literal_len) {
+ break;
+ }
+
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = p + 1 - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+ state = sw_end_literal_argument;
+
+ break;
+
+ case sw_end_literal_argument:
+ switch (ch) {
+ case '{':
+ if (s->args.nelts <= 2) {
+ state = sw_literal;
+ break;
+ }
+ goto invalid;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ state = sw_spaces_before_argument;
+ break;
+ }
+ break;
+
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ goto invalid;
+ }
+ }
+ }
+
+ s->buffer->pos = p;
+ s->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ s->buffer->pos = p + 1;
+
+ if (s->arg_start) {
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = s->arg_end - s->arg_start;
+ arg->data = s->arg_start;
+
+ s->arg_start = NULL;
+ s->cmd_start = NULL;
+ s->quoted = 0;
+ s->no_sync_literal = 0;
+ s->literal_len = 0;
+ }
+
+ s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;
+
+ return NGX_OK;
+
+invalid:
+
+ s->state = sw_start;
+ s->quoted = 0;
+ s->no_sync_literal = 0;
+ s->literal_len = 0;
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
+
+
+ngx_int_t
+ngx_mail_smtp_parse_command(ngx_mail_session_t *s)
+{
+ u_char ch, *p, *c, c0, c1, c2, c3;
+ ngx_str_t *arg;
+ enum {
+ sw_start = 0,
+ sw_command,
+ sw_invalid,
+ sw_spaces_before_argument,
+ sw_argument,
+ sw_almost_done
+ } state;
+
+ state = s->state;
+
+ for (p = s->buffer->pos; p < s->buffer->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* SMTP command */
+ case sw_start:
+ s->cmd_start = p;
+ state = sw_command;
+
+ /* fall through */
+
+ case sw_command:
+ if (ch == ' ' || ch == CR || ch == LF) {
+ c = s->cmd_start;
+
+ if (p - c == 4) {
+
+ c0 = ngx_toupper(c[0]);
+ c1 = ngx_toupper(c[1]);
+ c2 = ngx_toupper(c[2]);
+ c3 = ngx_toupper(c[3]);
+
+ if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'O')
+ {
+ s->command = NGX_SMTP_HELO;
+
+ } else if (c0 == 'E' && c1 == 'H' && c2 == 'L' && c3 == 'O')
+ {
+ s->command = NGX_SMTP_EHLO;
+
+ } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
+ {
+ s->command = NGX_SMTP_QUIT;
+
+ } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
+ {
+ s->command = NGX_SMTP_AUTH;
+
+ } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
+ {
+ s->command = NGX_SMTP_NOOP;
+
+ } else if (c0 == 'M' && c1 == 'A' && c2 == 'I' && c3 == 'L')
+ {
+ s->command = NGX_SMTP_MAIL;
+
+ } else if (c0 == 'R' && c1 == 'S' && c2 == 'E' && c3 == 'T')
+ {
+ s->command = NGX_SMTP_RSET;
+
+ } else if (c0 == 'R' && c1 == 'C' && c2 == 'P' && c3 == 'T')
+ {
+ s->command = NGX_SMTP_RCPT;
+
+ } else if (c0 == 'V' && c1 == 'R' && c2 == 'F' && c3 == 'Y')
+ {
+ s->command = NGX_SMTP_VRFY;
+
+ } else if (c0 == 'E' && c1 == 'X' && c2 == 'P' && c3 == 'N')
+ {
+ s->command = NGX_SMTP_EXPN;
+
+ } else if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'P')
+ {
+ s->command = NGX_SMTP_HELP;
+
+ } else {
+ goto invalid;
+ }
+#if (NGX_MAIL_SSL)
+ } else if (p - c == 8) {
+
+ if ((c[0] == 'S'|| c[0] == 's')
+ && (c[1] == 'T'|| c[1] == 't')
+ && (c[2] == 'A'|| c[2] == 'a')
+ && (c[3] == 'R'|| c[3] == 'r')
+ && (c[4] == 'T'|| c[4] == 't')
+ && (c[5] == 'T'|| c[5] == 't')
+ && (c[6] == 'L'|| c[6] == 'l')
+ && (c[7] == 'S'|| c[7] == 's'))
+ {
+ s->command = NGX_SMTP_STARTTLS;
+
+ } else {
+ goto invalid;
+ }
+#endif
+ } else {
+ goto invalid;
+ }
+
+ s->cmd.data = s->cmd_start;
+ s->cmd.len = p - s->cmd_start;
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+ }
+
+ if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
+ goto invalid;
+ }
+
+ break;
+
+ case sw_invalid:
+ goto invalid;
+
+ case sw_spaces_before_argument:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ s->arg_end = p;
+ break;
+ case LF:
+ s->arg_end = p;
+ goto done;
+ default:
+ if (s->args.nelts <= 10) {
+ state = sw_argument;
+ s->arg_start = p;
+ break;
+ }
+ goto invalid;
+ }
+ break;
+
+ case sw_argument:
+ switch (ch) {
+ case ' ':
+ case CR:
+ case LF:
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = p - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ goto invalid;
+ }
+ }
+ }
+
+ s->buffer->pos = p;
+ s->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ s->buffer->pos = p + 1;
+
+ if (s->arg_start) {
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = s->arg_end - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+ }
+
+ s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;
+
+ return NGX_OK;
+
+invalid:
+
+ s->state = sw_invalid;
+ s->arg_start = NULL;
+
+ /* skip invalid command till LF */
+
+ for (p = s->buffer->pos; p < s->buffer->last; p++) {
+ if (*p == LF) {
+ s->state = sw_start;
+ p++;
+ break;
+ }
+ }
+
+ s->buffer->pos = p;
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
+
+
+ngx_int_t
+ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ if (s->args.nelts == 0) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ arg = s->args.elts;
+
+ if (arg[0].len == 5) {
+
+ if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) {
+
+ if (s->args.nelts == 1) {
+ return NGX_MAIL_AUTH_LOGIN;
+ }
+
+ if (s->args.nelts == 2) {
+ return NGX_MAIL_AUTH_LOGIN_USERNAME;
+ }
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) {
+
+ if (s->args.nelts == 1) {
+ return NGX_MAIL_AUTH_PLAIN;
+ }
+
+ if (s->args.nelts == 2) {
+ return ngx_mail_auth_plain(s, c, 1);
+ }
+ }
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (arg[0].len == 8) {
+
+ if (s->args.nelts != 1) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) {
+ return NGX_MAIL_AUTH_CRAM_MD5;
+ }
+ }
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_pop3_handler.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_pop3_handler.c
new file mode 100644
index 00000000000..51bc257a595
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_pop3_handler.c
@@ -0,0 +1,500 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_mail.h>
+#include <ngx_mail_pop3_module.h>
+
+
+static ngx_int_t ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c,
+ ngx_int_t stls);
+static ngx_int_t ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c);
+
+
+static u_char pop3_greeting[] = "+OK POP3 ready" CRLF;
+static u_char pop3_ok[] = "+OK" CRLF;
+static u_char pop3_next[] = "+ " CRLF;
+static u_char pop3_username[] = "+ VXNlcm5hbWU6" CRLF;
+static u_char pop3_password[] = "+ UGFzc3dvcmQ6" CRLF;
+static u_char pop3_invalid_command[] = "-ERR invalid command" CRLF;
+
+
+void
+ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ u_char *p;
+ ngx_mail_core_srv_conf_t *cscf;
+ ngx_mail_pop3_srv_conf_t *pscf;
+
+ pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ if (pscf->auth_methods
+ & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED))
+ {
+ if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ s->out.data = ngx_pnalloc(c->pool, sizeof(pop3_greeting) + s->salt.len);
+ if (s->out.data == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(s->out.data, pop3_greeting, sizeof(pop3_greeting) - 3);
+ *p++ = ' ';
+ p = ngx_cpymem(p, s->salt.data, s->salt.len);
+
+ s->out.len = p - s->out.data;
+
+ } else {
+ ngx_str_set(&s->out, pop3_greeting);
+ }
+
+ c->read->handler = ngx_mail_pop3_init_protocol;
+
+ ngx_add_timer(c->read, cscf->timeout);
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+
+ ngx_mail_send(c->write);
+}
+
+
+void
+ngx_mail_pop3_init_protocol(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ c = rev->data;
+
+ c->log->action = "in auth state";
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ s = c->data;
+
+ if (s->buffer == NULL) {
+ if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t))
+ == NGX_ERROR)
+ {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ s->buffer = ngx_create_temp_buf(c->pool, 128);
+ if (s->buffer == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+ }
+
+ s->mail_state = ngx_pop3_start;
+ c->read->handler = ngx_mail_pop3_auth_state;
+
+ ngx_mail_pop3_auth_state(rev);
+}
+
+
+void
+ngx_mail_pop3_auth_state(ngx_event_t *rev)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 auth state");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (s->out.len) {
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "pop3 send handler busy");
+ s->blocked = 1;
+ return;
+ }
+
+ s->blocked = 0;
+
+ rc = ngx_mail_read_command(s, c);
+
+ if (rc == NGX_AGAIN || rc == NGX_ERROR) {
+ return;
+ }
+
+ ngx_str_set(&s->out, pop3_ok);
+
+ if (rc == NGX_OK) {
+ switch (s->mail_state) {
+
+ case ngx_pop3_start:
+
+ switch (s->command) {
+
+ case NGX_POP3_USER:
+ rc = ngx_mail_pop3_user(s, c);
+ break;
+
+ case NGX_POP3_CAPA:
+ rc = ngx_mail_pop3_capa(s, c, 1);
+ break;
+
+ case NGX_POP3_APOP:
+ rc = ngx_mail_pop3_apop(s, c);
+ break;
+
+ case NGX_POP3_AUTH:
+ rc = ngx_mail_pop3_auth(s, c);
+ break;
+
+ case NGX_POP3_QUIT:
+ s->quit = 1;
+ break;
+
+ case NGX_POP3_NOOP:
+ break;
+
+ case NGX_POP3_STLS:
+ rc = ngx_mail_pop3_stls(s, c);
+ break;
+
+ default:
+ rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+ break;
+ }
+
+ break;
+
+ case ngx_pop3_user:
+
+ switch (s->command) {
+
+ case NGX_POP3_PASS:
+ rc = ngx_mail_pop3_pass(s, c);
+ break;
+
+ case NGX_POP3_CAPA:
+ rc = ngx_mail_pop3_capa(s, c, 0);
+ break;
+
+ case NGX_POP3_QUIT:
+ s->quit = 1;
+ break;
+
+ case NGX_POP3_NOOP:
+ break;
+
+ default:
+ rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+ break;
+ }
+
+ break;
+
+ /* suppress warnings */
+ case ngx_pop3_passwd:
+ break;
+
+ case ngx_pop3_auth_login_username:
+ rc = ngx_mail_auth_login_username(s, c, 0);
+
+ ngx_str_set(&s->out, pop3_password);
+ s->mail_state = ngx_pop3_auth_login_password;
+ break;
+
+ case ngx_pop3_auth_login_password:
+ rc = ngx_mail_auth_login_password(s, c);
+ break;
+
+ case ngx_pop3_auth_plain:
+ rc = ngx_mail_auth_plain(s, c, 0);
+ break;
+
+ case ngx_pop3_auth_cram_md5:
+ rc = ngx_mail_auth_cram_md5(s, c);
+ break;
+ }
+ }
+
+ switch (rc) {
+
+ case NGX_DONE:
+ ngx_mail_auth(s, c);
+ return;
+
+ case NGX_ERROR:
+ ngx_mail_session_internal_server_error(s);
+ return;
+
+ case NGX_MAIL_PARSE_INVALID_COMMAND:
+ s->mail_state = ngx_pop3_start;
+ s->state = 0;
+
+ ngx_str_set(&s->out, pop3_invalid_command);
+
+ /* fall through */
+
+ case NGX_OK:
+
+ s->args.nelts = 0;
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+
+ if (s->state) {
+ s->arg_start = s->buffer->start;
+ }
+
+ ngx_mail_send(c->write);
+ }
+}
+
+static ngx_int_t
+ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ if (s->args.nelts != 1) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ arg = s->args.elts;
+ s->login.len = arg[0].len;
+ s->login.data = ngx_pnalloc(c->pool, s->login.len);
+ if (s->login.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->login.data, arg[0].data, s->login.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "pop3 login: \"%V\"", &s->login);
+
+ s->mail_state = ngx_pop3_user;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+
+ if (s->args.nelts != 1) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ arg = s->args.elts;
+ s->passwd.len = arg[0].len;
+ s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
+ if (s->passwd.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len);
+
+#if (NGX_DEBUG_MAIL_PASSWD)
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "pop3 passwd: \"%V\"", &s->passwd);
+#endif
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t stls)
+{
+ ngx_mail_pop3_srv_conf_t *pscf;
+
+ pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
+
+#if (NGX_MAIL_SSL)
+
+ if (stls && c->ssl == NULL) {
+ ngx_mail_ssl_conf_t *sslcf;
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
+ s->out = pscf->starttls_capability;
+ return NGX_OK;
+ }
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+ s->out = pscf->starttls_only_capability;
+ return NGX_OK;
+ }
+ }
+
+#endif
+
+ s->out = pscf->capability;
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+#if (NGX_MAIL_SSL)
+ ngx_mail_ssl_conf_t *sslcf;
+
+ if (c->ssl == NULL) {
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+ if (sslcf->starttls) {
+ c->read->handler = ngx_mail_starttls_handler;
+ return NGX_OK;
+ }
+ }
+
+#endif
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
+
+
+static ngx_int_t
+ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+ ngx_mail_pop3_srv_conf_t *pscf;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ if (s->args.nelts != 2) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
+
+ if (!(pscf->auth_methods & NGX_MAIL_AUTH_APOP_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ arg = s->args.elts;
+
+ s->login.len = arg[0].len;
+ s->login.data = ngx_pnalloc(c->pool, s->login.len);
+ if (s->login.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->login.data, arg[0].data, s->login.len);
+
+ s->passwd.len = arg[1].len;
+ s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
+ if (s->passwd.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "pop3 apop: \"%V\" \"%V\"", &s->login, &s->passwd);
+
+ s->auth_method = NGX_MAIL_AUTH_APOP;
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_int_t rc;
+ ngx_mail_pop3_srv_conf_t *pscf;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);
+
+ if (s->args.nelts == 0) {
+ s->out = pscf->auth_capability;
+ s->state = 0;
+
+ return NGX_OK;
+ }
+
+ rc = ngx_mail_auth_parse(s, c);
+
+ switch (rc) {
+
+ case NGX_MAIL_AUTH_LOGIN:
+
+ ngx_str_set(&s->out, pop3_username);
+ s->mail_state = ngx_pop3_auth_login_username;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_LOGIN_USERNAME:
+
+ ngx_str_set(&s->out, pop3_password);
+ s->mail_state = ngx_pop3_auth_login_password;
+
+ return ngx_mail_auth_login_username(s, c, 1);
+
+ case NGX_MAIL_AUTH_PLAIN:
+
+ ngx_str_set(&s->out, pop3_next);
+ s->mail_state = ngx_pop3_auth_plain;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_CRAM_MD5:
+
+ if (!(pscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {
+ s->mail_state = ngx_pop3_auth_cram_md5;
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+ }
+
+ return rc;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_pop3_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_pop3_module.c
new file mode 100644
index 00000000000..b59747290e2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_pop3_module.c
@@ -0,0 +1,264 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_mail.h>
+#include <ngx_mail_pop3_module.h>
+
+
+static void *ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+
+static ngx_str_t ngx_mail_pop3_default_capabilities[] = {
+ ngx_string("TOP"),
+ ngx_string("USER"),
+ ngx_string("UIDL"),
+ ngx_null_string
+};
+
+
+static ngx_conf_bitmask_t ngx_mail_pop3_auth_methods[] = {
+ { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
+ { ngx_string("apop"), NGX_MAIL_AUTH_APOP_ENABLED },
+ { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_str_t ngx_mail_pop3_auth_plain_capability =
+ ngx_string("+OK methods supported:" CRLF
+ "LOGIN" CRLF
+ "PLAIN" CRLF
+ "." CRLF);
+
+
+static ngx_str_t ngx_mail_pop3_auth_cram_md5_capability =
+ ngx_string("+OK methods supported:" CRLF
+ "LOGIN" CRLF
+ "PLAIN" CRLF
+ "CRAM-MD5" CRLF
+ "." CRLF);
+
+
+static ngx_mail_protocol_t ngx_mail_pop3_protocol = {
+ ngx_string("pop3"),
+ { 110, 995, 0, 0 },
+ NGX_MAIL_POP3_PROTOCOL,
+
+ ngx_mail_pop3_init_session,
+ ngx_mail_pop3_init_protocol,
+ ngx_mail_pop3_parse_command,
+ ngx_mail_pop3_auth_state,
+
+ ngx_string("-ERR internal server error" CRLF)
+};
+
+
+static ngx_command_t ngx_mail_pop3_commands[] = {
+
+ { ngx_string("pop3_capabilities"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_mail_capabilities,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_pop3_srv_conf_t, capabilities),
+ NULL },
+
+ { ngx_string("pop3_auth"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_pop3_srv_conf_t, auth_methods),
+ &ngx_mail_pop3_auth_methods },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_pop3_module_ctx = {
+ &ngx_mail_pop3_protocol, /* protocol */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_pop3_create_srv_conf, /* create server configuration */
+ ngx_mail_pop3_merge_srv_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_pop3_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_pop3_module_ctx, /* module context */
+ ngx_mail_pop3_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static void *
+ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_mail_pop3_srv_conf_t *pscf;
+
+ pscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_pop3_srv_conf_t));
+ if (pscf == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(&pscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return pscf;
+}
+
+
+static char *
+ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_pop3_srv_conf_t *prev = parent;
+ ngx_mail_pop3_srv_conf_t *conf = child;
+
+ u_char *p;
+ size_t size, stls_only_size;
+ ngx_str_t *c, *d;
+ ngx_uint_t i;
+
+ ngx_conf_merge_bitmask_value(conf->auth_methods,
+ prev->auth_methods,
+ (NGX_CONF_BITMASK_SET
+ |NGX_MAIL_AUTH_PLAIN_ENABLED));
+
+ if (conf->capabilities.nelts == 0) {
+ conf->capabilities = prev->capabilities;
+ }
+
+ if (conf->capabilities.nelts == 0) {
+
+ for (d = ngx_mail_pop3_default_capabilities; d->len; d++) {
+ c = ngx_array_push(&conf->capabilities);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = *d;
+ }
+ }
+
+ size = sizeof("+OK Capability list follows" CRLF) - 1
+ + sizeof("." CRLF) - 1;
+
+ stls_only_size = size + sizeof("STLS" CRLF) - 1;
+
+ c = conf->capabilities.elts;
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ size += c[i].len + sizeof(CRLF) - 1;
+
+ if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) {
+ continue;
+ }
+
+ stls_only_size += c[i].len + sizeof(CRLF) - 1;
+ }
+
+ if (conf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) {
+ size += sizeof("SASL LOGIN PLAIN CRAM-MD5" CRLF) - 1;
+
+ } else {
+ size += sizeof("SASL LOGIN PLAIN" CRLF) - 1;
+ }
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->capability.len = size;
+ conf->capability.data = p;
+
+ p = ngx_cpymem(p, "+OK Capability list follows" CRLF,
+ sizeof("+OK Capability list follows" CRLF) - 1);
+
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ p = ngx_cpymem(p, c[i].data, c[i].len);
+ *p++ = CR; *p++ = LF;
+ }
+
+ if (conf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) {
+ p = ngx_cpymem(p, "SASL LOGIN PLAIN CRAM-MD5" CRLF,
+ sizeof("SASL LOGIN PLAIN CRAM-MD5" CRLF) - 1);
+
+ } else {
+ p = ngx_cpymem(p, "SASL LOGIN PLAIN" CRLF,
+ sizeof("SASL LOGIN PLAIN" CRLF) - 1);
+ }
+
+ *p++ = '.'; *p++ = CR; *p = LF;
+
+
+ size += sizeof("STLS" CRLF) - 1;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->starttls_capability.len = size;
+ conf->starttls_capability.data = p;
+
+ p = ngx_cpymem(p, conf->capability.data,
+ conf->capability.len - (sizeof("." CRLF) - 1));
+
+ p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1);
+ *p++ = '.'; *p++ = CR; *p = LF;
+
+
+ if (conf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED) {
+ conf->auth_capability = ngx_mail_pop3_auth_cram_md5_capability;
+
+ } else {
+ conf->auth_capability = ngx_mail_pop3_auth_plain_capability;
+ }
+
+
+ p = ngx_pnalloc(cf->pool, stls_only_size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->starttls_only_capability.len = stls_only_size;
+ conf->starttls_only_capability.data = p;
+
+ p = ngx_cpymem(p, "+OK Capability list follows" CRLF,
+ sizeof("+OK Capability list follows" CRLF) - 1);
+
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ if (ngx_strcasecmp(c[i].data, (u_char *) "USER") == 0) {
+ continue;
+ }
+
+ p = ngx_cpymem(p, c[i].data, c[i].len);
+ *p++ = CR; *p++ = LF;
+ }
+
+ p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1);
+ *p++ = '.'; *p++ = CR; *p = LF;
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_pop3_module.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_pop3_module.h
new file mode 100644
index 00000000000..86947a77251
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_pop3_module.h
@@ -0,0 +1,38 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_MAIL_POP3_MODULE_H_INCLUDED_
+#define _NGX_MAIL_POP3_MODULE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_mail.h>
+
+
+typedef struct {
+ ngx_str_t capability;
+ ngx_str_t starttls_capability;
+ ngx_str_t starttls_only_capability;
+ ngx_str_t auth_capability;
+
+ ngx_uint_t auth_methods;
+
+ ngx_array_t capabilities;
+} ngx_mail_pop3_srv_conf_t;
+
+
+void ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c);
+void ngx_mail_pop3_init_protocol(ngx_event_t *rev);
+void ngx_mail_pop3_auth_state(ngx_event_t *rev);
+ngx_int_t ngx_mail_pop3_parse_command(ngx_mail_session_t *s);
+
+
+extern ngx_module_t ngx_mail_pop3_module;
+
+
+#endif /* _NGX_MAIL_POP3_MODULE_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_proxy_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_proxy_module.c
new file mode 100644
index 00000000000..41cbcf6e312
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_proxy_module.c
@@ -0,0 +1,1136 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include <ngx_mail.h>
+
+
+typedef struct {
+ ngx_flag_t enable;
+ ngx_flag_t pass_error_message;
+ ngx_flag_t xclient;
+ size_t buffer_size;
+ ngx_msec_t timeout;
+} ngx_mail_proxy_conf_t;
+
+
+static void ngx_mail_proxy_block_read(ngx_event_t *rev);
+static void ngx_mail_proxy_pop3_handler(ngx_event_t *rev);
+static void ngx_mail_proxy_imap_handler(ngx_event_t *rev);
+static void ngx_mail_proxy_smtp_handler(ngx_event_t *rev);
+static void ngx_mail_proxy_dummy_handler(ngx_event_t *ev);
+static ngx_int_t ngx_mail_proxy_read_response(ngx_mail_session_t *s,
+ ngx_uint_t state);
+static void ngx_mail_proxy_handler(ngx_event_t *ev);
+static void ngx_mail_proxy_upstream_error(ngx_mail_session_t *s);
+static void ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s);
+static void ngx_mail_proxy_close_session(ngx_mail_session_t *s);
+static void *ngx_mail_proxy_create_conf(ngx_conf_t *cf);
+static char *ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+
+static ngx_command_t ngx_mail_proxy_commands[] = {
+
+ { ngx_string("proxy"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, enable),
+ NULL },
+
+ { ngx_string("proxy_buffer"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, buffer_size),
+ NULL },
+
+ { ngx_string("proxy_timeout"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, timeout),
+ NULL },
+
+ { ngx_string("proxy_pass_error_message"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, pass_error_message),
+ NULL },
+
+ { ngx_string("xclient"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_proxy_conf_t, xclient),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_proxy_module_ctx = {
+ NULL, /* protocol */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_proxy_create_conf, /* create server configuration */
+ ngx_mail_proxy_merge_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_proxy_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_proxy_module_ctx, /* module context */
+ ngx_mail_proxy_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static u_char smtp_auth_ok[] = "235 2.0.0 OK" CRLF;
+
+
+void
+ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer)
+{
+ int keepalive;
+ ngx_int_t rc;
+ ngx_mail_proxy_ctx_t *p;
+ ngx_mail_proxy_conf_t *pcf;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ s->connection->log->action = "connecting to upstream";
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ if (cscf->so_keepalive) {
+ keepalive = 1;
+
+ if (setsockopt(s->connection->fd, SOL_SOCKET, SO_KEEPALIVE,
+ (const void *) &keepalive, sizeof(int))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno,
+ "setsockopt(SO_KEEPALIVE) failed");
+ }
+ }
+
+ p = ngx_pcalloc(s->connection->pool, sizeof(ngx_mail_proxy_ctx_t));
+ if (p == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ s->proxy = p;
+
+ p->upstream.sockaddr = peer->sockaddr;
+ p->upstream.socklen = peer->socklen;
+ p->upstream.name = &peer->name;
+ p->upstream.get = ngx_event_get_peer;
+ p->upstream.log = s->connection->log;
+ p->upstream.log_error = NGX_ERROR_ERR;
+
+ rc = ngx_event_connect_peer(&p->upstream);
+
+ if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ ngx_add_timer(p->upstream.connection->read, cscf->timeout);
+
+ p->upstream.connection->data = s;
+ p->upstream.connection->pool = s->connection->pool;
+
+ s->connection->read->handler = ngx_mail_proxy_block_read;
+ p->upstream.connection->write->handler = ngx_mail_proxy_dummy_handler;
+
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+
+ s->proxy->buffer = ngx_create_temp_buf(s->connection->pool,
+ pcf->buffer_size);
+ if (s->proxy->buffer == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ s->out.len = 0;
+
+ switch (s->protocol) {
+
+ case NGX_MAIL_POP3_PROTOCOL:
+ p->upstream.connection->read->handler = ngx_mail_proxy_pop3_handler;
+ s->mail_state = ngx_pop3_start;
+ break;
+
+ case NGX_MAIL_IMAP_PROTOCOL:
+ p->upstream.connection->read->handler = ngx_mail_proxy_imap_handler;
+ s->mail_state = ngx_imap_start;
+ break;
+
+ default: /* NGX_MAIL_SMTP_PROTOCOL */
+ p->upstream.connection->read->handler = ngx_mail_proxy_smtp_handler;
+ s->mail_state = ngx_smtp_start;
+ break;
+ }
+}
+
+
+static void
+ngx_mail_proxy_block_read(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy block read");
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ c = rev->data;
+ s = c->data;
+
+ ngx_mail_proxy_close_session(s);
+ }
+}
+
+
+static void
+ngx_mail_proxy_pop3_handler(ngx_event_t *rev)
+{
+ u_char *p;
+ ngx_int_t rc;
+ ngx_str_t line;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_proxy_conf_t *pcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy pop3 auth handler");
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ c->timedout = 1;
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ rc = ngx_mail_proxy_read_response(s, 0);
+
+ if (rc == NGX_AGAIN) {
+ return;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_mail_proxy_upstream_error(s);
+ return;
+ }
+
+ switch (s->mail_state) {
+
+ case ngx_pop3_start:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user");
+
+ s->connection->log->action = "sending user name to upstream";
+
+ line.len = sizeof("USER ") - 1 + s->login.len + 2;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1);
+ p = ngx_cpymem(p, s->login.data, s->login.len);
+ *p++ = CR; *p = LF;
+
+ s->mail_state = ngx_pop3_user;
+ break;
+
+ case ngx_pop3_user:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send pass");
+
+ s->connection->log->action = "sending password to upstream";
+
+ line.len = sizeof("PASS ") - 1 + s->passwd.len + 2;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1);
+ p = ngx_cpymem(p, s->passwd.data, s->passwd.len);
+ *p++ = CR; *p = LF;
+
+ s->mail_state = ngx_pop3_passwd;
+ break;
+
+ case ngx_pop3_passwd:
+ s->connection->read->handler = ngx_mail_proxy_handler;
+ s->connection->write->handler = ngx_mail_proxy_handler;
+ rev->handler = ngx_mail_proxy_handler;
+ c->write->handler = ngx_mail_proxy_handler;
+
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+ ngx_add_timer(s->connection->read, pcf->timeout);
+ ngx_del_timer(c->read);
+
+ c->log->action = NULL;
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
+
+ ngx_mail_proxy_handler(s->connection->write);
+
+ return;
+
+ default:
+#if (NGX_SUPPRESS_WARN)
+ ngx_str_null(&line);
+#endif
+ break;
+ }
+
+ if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
+ /*
+ * we treat the incomplete sending as NGX_ERROR
+ * because it is very strange here
+ */
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ s->proxy->buffer->pos = s->proxy->buffer->start;
+ s->proxy->buffer->last = s->proxy->buffer->start;
+}
+
+
+static void
+ngx_mail_proxy_imap_handler(ngx_event_t *rev)
+{
+ u_char *p;
+ ngx_int_t rc;
+ ngx_str_t line;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_proxy_conf_t *pcf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy imap auth handler");
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ c->timedout = 1;
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ rc = ngx_mail_proxy_read_response(s, s->mail_state);
+
+ if (rc == NGX_AGAIN) {
+ return;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_mail_proxy_upstream_error(s);
+ return;
+ }
+
+ switch (s->mail_state) {
+
+ case ngx_imap_start:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy send login");
+
+ s->connection->log->action = "sending LOGIN command to upstream";
+
+ line.len = s->tag.len + sizeof("LOGIN ") - 1
+ + 1 + NGX_SIZE_T_LEN + 1 + 2;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ line.len = ngx_sprintf(line.data, "%VLOGIN {%uz}" CRLF,
+ &s->tag, s->login.len)
+ - line.data;
+
+ s->mail_state = ngx_imap_login;
+ break;
+
+ case ngx_imap_login:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send user");
+
+ s->connection->log->action = "sending user name to upstream";
+
+ line.len = s->login.len + 1 + 1 + NGX_SIZE_T_LEN + 1 + 2;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ line.len = ngx_sprintf(line.data, "%V {%uz}" CRLF,
+ &s->login, s->passwd.len)
+ - line.data;
+
+ s->mail_state = ngx_imap_user;
+ break;
+
+ case ngx_imap_user:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy send passwd");
+
+ s->connection->log->action = "sending password to upstream";
+
+ line.len = s->passwd.len + 2;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len);
+ *p++ = CR; *p = LF;
+
+ s->mail_state = ngx_imap_passwd;
+ break;
+
+ case ngx_imap_passwd:
+ s->connection->read->handler = ngx_mail_proxy_handler;
+ s->connection->write->handler = ngx_mail_proxy_handler;
+ rev->handler = ngx_mail_proxy_handler;
+ c->write->handler = ngx_mail_proxy_handler;
+
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+ ngx_add_timer(s->connection->read, pcf->timeout);
+ ngx_del_timer(c->read);
+
+ c->log->action = NULL;
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
+
+ ngx_mail_proxy_handler(s->connection->write);
+
+ return;
+
+ default:
+#if (NGX_SUPPRESS_WARN)
+ ngx_str_null(&line);
+#endif
+ break;
+ }
+
+ if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
+ /*
+ * we treat the incomplete sending as NGX_ERROR
+ * because it is very strange here
+ */
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ s->proxy->buffer->pos = s->proxy->buffer->start;
+ s->proxy->buffer->last = s->proxy->buffer->start;
+}
+
+
+static void
+ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
+{
+ u_char *p;
+ ngx_int_t rc;
+ ngx_str_t line;
+ ngx_buf_t *b;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_proxy_conf_t *pcf;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy smtp auth handler");
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ c->timedout = 1;
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ rc = ngx_mail_proxy_read_response(s, s->mail_state);
+
+ if (rc == NGX_AGAIN) {
+ return;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_mail_proxy_upstream_error(s);
+ return;
+ }
+
+ switch (s->mail_state) {
+
+ case ngx_smtp_start:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send ehlo");
+
+ s->connection->log->action = "sending HELO/EHLO to upstream";
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ line.len = sizeof("HELO ") - 1 + cscf->server_name.len + 2;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+
+ p = ngx_cpymem(line.data,
+ ((s->esmtp || pcf->xclient) ? "EHLO " : "HELO "),
+ sizeof("HELO ") - 1);
+
+ p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
+ *p++ = CR; *p = LF;
+
+ if (pcf->xclient) {
+ s->mail_state = ngx_smtp_helo_xclient;
+
+ } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+ s->mail_state = ngx_smtp_helo_from;
+
+ } else {
+ s->mail_state = ngx_smtp_helo;
+ }
+
+ break;
+
+ case ngx_smtp_helo_xclient:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy send xclient");
+
+ s->connection->log->action = "sending XCLIENT to upstream";
+
+ line.len = sizeof("XCLIENT ADDR= LOGIN= NAME="
+ CRLF) - 1
+ + s->connection->addr_text.len + s->login.len + s->host.len;
+
+#if (NGX_HAVE_INET6)
+ if (s->connection->sockaddr->sa_family == AF_INET6) {
+ line.len += sizeof("IPV6:") - 1;
+ }
+#endif
+
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, "XCLIENT ADDR=", sizeof("XCLIENT ADDR=") - 1);
+
+#if (NGX_HAVE_INET6)
+ if (s->connection->sockaddr->sa_family == AF_INET6) {
+ p = ngx_cpymem(p, "IPV6:", sizeof("IPV6:") - 1);
+ }
+#endif
+
+ p = ngx_copy(p, s->connection->addr_text.data,
+ s->connection->addr_text.len);
+
+ if (s->login.len) {
+ p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1);
+ p = ngx_copy(p, s->login.data, s->login.len);
+ }
+
+ p = ngx_cpymem(p, " NAME=", sizeof(" NAME=") - 1);
+ p = ngx_copy(p, s->host.data, s->host.len);
+
+ *p++ = CR; *p++ = LF;
+
+ line.len = p - line.data;
+
+ if (s->smtp_helo.len) {
+ s->mail_state = ngx_smtp_xclient_helo;
+
+ } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+ s->mail_state = ngx_smtp_xclient_from;
+
+ } else {
+ s->mail_state = ngx_smtp_xclient;
+ }
+
+ break;
+
+ case ngx_smtp_xclient_helo:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy send client ehlo");
+
+ s->connection->log->action = "sending client HELO/EHLO to upstream";
+
+ line.len = sizeof("HELO " CRLF) - 1 + s->smtp_helo.len;
+
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ line.len = ngx_sprintf(line.data,
+ ((s->esmtp) ? "EHLO %V" CRLF : "HELO %V" CRLF),
+ &s->smtp_helo)
+ - line.data;
+
+ s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ?
+ ngx_smtp_helo_from : ngx_smtp_helo;
+
+ break;
+
+ case ngx_smtp_helo_from:
+ case ngx_smtp_xclient_from:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy send mail from");
+
+ s->connection->log->action = "sending MAIL FROM to upstream";
+
+ line.len = s->smtp_from.len + sizeof(CRLF) - 1;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, s->smtp_from.data, s->smtp_from.len);
+ *p++ = CR; *p = LF;
+
+ s->mail_state = ngx_smtp_from;
+
+ break;
+
+ case ngx_smtp_from:
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+ "mail proxy send rcpt to");
+
+ s->connection->log->action = "sending RCPT TO to upstream";
+
+ line.len = s->smtp_to.len + sizeof(CRLF) - 1;
+ line.data = ngx_pnalloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, s->smtp_to.data, s->smtp_to.len);
+ *p++ = CR; *p = LF;
+
+ s->mail_state = ngx_smtp_to;
+
+ break;
+
+ case ngx_smtp_helo:
+ case ngx_smtp_xclient:
+ case ngx_smtp_to:
+
+ b = s->proxy->buffer;
+
+ if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+ b->pos = b->start;
+
+ } else {
+ ngx_memcpy(b->start, smtp_auth_ok, sizeof(smtp_auth_ok) - 1);
+ b->last = b->start + sizeof(smtp_auth_ok) - 1;
+ }
+
+ s->connection->read->handler = ngx_mail_proxy_handler;
+ s->connection->write->handler = ngx_mail_proxy_handler;
+ rev->handler = ngx_mail_proxy_handler;
+ c->write->handler = ngx_mail_proxy_handler;
+
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+ ngx_add_timer(s->connection->read, pcf->timeout);
+ ngx_del_timer(c->read);
+
+ c->log->action = NULL;
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
+
+ if (s->buffer->pos == s->buffer->last) {
+ ngx_mail_proxy_handler(s->connection->write);
+
+ } else {
+ ngx_mail_proxy_handler(c->write);
+ }
+
+ return;
+
+ default:
+#if (NGX_SUPPRESS_WARN)
+ ngx_str_null(&line);
+#endif
+ break;
+ }
+
+ if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
+ /*
+ * we treat the incomplete sending as NGX_ERROR
+ * because it is very strange here
+ */
+ ngx_mail_proxy_internal_server_error(s);
+ return;
+ }
+
+ s->proxy->buffer->pos = s->proxy->buffer->start;
+ s->proxy->buffer->last = s->proxy->buffer->start;
+}
+
+
+static void
+ngx_mail_proxy_dummy_handler(ngx_event_t *wev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler");
+
+ if (ngx_handle_write_event(wev, 0) != NGX_OK) {
+ c = wev->data;
+ s = c->data;
+
+ ngx_mail_proxy_close_session(s);
+ }
+}
+
+
+static ngx_int_t
+ngx_mail_proxy_read_response(ngx_mail_session_t *s, ngx_uint_t state)
+{
+ u_char *p, *m;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_mail_proxy_conf_t *pcf;
+
+ s->connection->log->action = "reading response from upstream";
+
+ b = s->proxy->buffer;
+
+ n = s->proxy->upstream.connection->recv(s->proxy->upstream.connection,
+ b->last, b->end - b->last);
+
+ if (n == NGX_ERROR || n == 0) {
+ return NGX_ERROR;
+ }
+
+ if (n == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ b->last += n;
+
+ if (b->last - b->pos < 4) {
+ return NGX_AGAIN;
+ }
+
+ if (*(b->last - 2) != CR || *(b->last - 1) != LF) {
+ if (b->last == b->end) {
+ *(b->last - 1) = '\0';
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "upstream sent too long response line: \"%s\"",
+ b->pos);
+ return NGX_ERROR;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ p = b->pos;
+
+ switch (s->protocol) {
+
+ case NGX_MAIL_POP3_PROTOCOL:
+ if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') {
+ return NGX_OK;
+ }
+ break;
+
+ case NGX_MAIL_IMAP_PROTOCOL:
+ switch (state) {
+
+ case ngx_imap_start:
+ if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {
+ return NGX_OK;
+ }
+ break;
+
+ case ngx_imap_login:
+ case ngx_imap_user:
+ if (p[0] == '+') {
+ return NGX_OK;
+ }
+ break;
+
+ case ngx_imap_passwd:
+ if (ngx_strncmp(p, s->tag.data, s->tag.len) == 0) {
+ p += s->tag.len;
+ if (p[0] == 'O' && p[1] == 'K') {
+ return NGX_OK;
+ }
+ }
+ break;
+ }
+
+ break;
+
+ default: /* NGX_MAIL_SMTP_PROTOCOL */
+
+ if (p[3] == '-') {
+ /* multiline reply, check if we got last line */
+
+ m = b->last - (sizeof(CRLF "200" CRLF) - 1);
+
+ while (m > p) {
+ if (m[0] == CR && m[1] == LF) {
+ break;
+ }
+
+ m--;
+ }
+
+ if (m <= p || m[5] == '-') {
+ return NGX_AGAIN;
+ }
+ }
+
+ switch (state) {
+
+ case ngx_smtp_start:
+ if (p[0] == '2' && p[1] == '2' && p[2] == '0') {
+ return NGX_OK;
+ }
+ break;
+
+ case ngx_smtp_helo:
+ case ngx_smtp_helo_xclient:
+ case ngx_smtp_helo_from:
+ case ngx_smtp_from:
+ if (p[0] == '2' && p[1] == '5' && p[2] == '0') {
+ return NGX_OK;
+ }
+ break;
+
+ case ngx_smtp_xclient:
+ case ngx_smtp_xclient_from:
+ case ngx_smtp_xclient_helo:
+ if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') {
+ return NGX_OK;
+ }
+ break;
+
+ case ngx_smtp_to:
+ return NGX_OK;
+ }
+
+ break;
+ }
+
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+
+ if (pcf->pass_error_message == 0) {
+ *(b->last - 2) = '\0';
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "upstream sent invalid response: \"%s\"", p);
+ return NGX_ERROR;
+ }
+
+ s->out.len = b->last - p - 2;
+ s->out.data = p;
+
+ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
+ "upstream sent invalid response: \"%V\"", &s->out);
+
+ s->out.len = b->last - b->pos;
+ s->out.data = b->pos;
+
+ return NGX_ERROR;
+}
+
+
+static void
+ngx_mail_proxy_handler(ngx_event_t *ev)
+{
+ char *action, *recv_action, *send_action;
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_uint_t do_write;
+ ngx_connection_t *c, *src, *dst;
+ ngx_mail_session_t *s;
+ ngx_mail_proxy_conf_t *pcf;
+
+ c = ev->data;
+ s = c->data;
+
+ if (ev->timedout) {
+ c->log->action = "proxying";
+
+ if (c == s->connection) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "client timed out");
+ c->timedout = 1;
+
+ } else {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ }
+
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (c == s->connection) {
+ if (ev->write) {
+ recv_action = "proxying and reading from upstream";
+ send_action = "proxying and sending to client";
+ src = s->proxy->upstream.connection;
+ dst = c;
+ b = s->proxy->buffer;
+
+ } else {
+ recv_action = "proxying and reading from client";
+ send_action = "proxying and sending to upstream";
+ src = c;
+ dst = s->proxy->upstream.connection;
+ b = s->buffer;
+ }
+
+ } else {
+ if (ev->write) {
+ recv_action = "proxying and reading from client";
+ send_action = "proxying and sending to upstream";
+ src = s->connection;
+ dst = c;
+ b = s->buffer;
+
+ } else {
+ recv_action = "proxying and reading from upstream";
+ send_action = "proxying and sending to client";
+ src = c;
+ dst = s->connection;
+ b = s->proxy->buffer;
+ }
+ }
+
+ do_write = ev->write ? 1 : 0;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_MAIL, ev->log, 0,
+ "mail proxy handler: %d, #%d > #%d",
+ do_write, src->fd, dst->fd);
+
+ for ( ;; ) {
+
+ if (do_write) {
+
+ size = b->last - b->pos;
+
+ if (size && dst->write->ready) {
+ c->log->action = send_action;
+
+ n = dst->send(dst, b->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (n > 0) {
+ b->pos += n;
+
+ if (b->pos == b->last) {
+ b->pos = b->start;
+ b->last = b->start;
+ }
+ }
+ }
+ }
+
+ size = b->end - b->last;
+
+ if (size && src->read->ready) {
+ c->log->action = recv_action;
+
+ n = src->recv(src, b->last, size);
+
+ if (n == NGX_AGAIN || n == 0) {
+ break;
+ }
+
+ if (n > 0) {
+ do_write = 1;
+ b->last += n;
+
+ continue;
+ }
+
+ if (n == NGX_ERROR) {
+ src->read->eof = 1;
+ }
+ }
+
+ break;
+ }
+
+ c->log->action = "proxying";
+
+ if ((s->connection->read->eof && s->buffer->pos == s->buffer->last)
+ || (s->proxy->upstream.connection->read->eof
+ && s->proxy->buffer->pos == s->proxy->buffer->last)
+ || (s->connection->read->eof
+ && s->proxy->upstream.connection->read->eof))
+ {
+ action = c->log->action;
+ c->log->action = NULL;
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxied session done");
+ c->log->action = action;
+
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (ngx_handle_read_event(dst->read, 0) != NGX_OK) {
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (ngx_handle_write_event(src->write, 0) != NGX_OK) {
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (ngx_handle_read_event(src->read, 0) != NGX_OK) {
+ ngx_mail_proxy_close_session(s);
+ return;
+ }
+
+ if (c == s->connection) {
+ pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+ ngx_add_timer(c->read, pcf->timeout);
+ }
+}
+
+
+static void
+ngx_mail_proxy_upstream_error(ngx_mail_session_t *s)
+{
+ if (s->proxy->upstream.connection) {
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "close mail proxy connection: %d",
+ s->proxy->upstream.connection->fd);
+
+ ngx_close_connection(s->proxy->upstream.connection);
+ }
+
+ if (s->out.len == 0) {
+ ngx_mail_session_internal_server_error(s);
+ return;
+ }
+
+ s->quit = 1;
+ ngx_mail_send(s->connection->write);
+}
+
+
+static void
+ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s)
+{
+ if (s->proxy->upstream.connection) {
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "close mail proxy connection: %d",
+ s->proxy->upstream.connection->fd);
+
+ ngx_close_connection(s->proxy->upstream.connection);
+ }
+
+ ngx_mail_session_internal_server_error(s);
+}
+
+
+static void
+ngx_mail_proxy_close_session(ngx_mail_session_t *s)
+{
+ if (s->proxy->upstream.connection) {
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+ "close mail proxy connection: %d",
+ s->proxy->upstream.connection->fd);
+
+ ngx_close_connection(s->proxy->upstream.connection);
+ }
+
+ ngx_mail_close_connection(s->connection);
+}
+
+
+static void *
+ngx_mail_proxy_create_conf(ngx_conf_t *cf)
+{
+ ngx_mail_proxy_conf_t *pcf;
+
+ pcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_proxy_conf_t));
+ if (pcf == NULL) {
+ return NULL;
+ }
+
+ pcf->enable = NGX_CONF_UNSET;
+ pcf->pass_error_message = NGX_CONF_UNSET;
+ pcf->xclient = NGX_CONF_UNSET;
+ pcf->buffer_size = NGX_CONF_UNSET_SIZE;
+ pcf->timeout = NGX_CONF_UNSET_MSEC;
+
+ return pcf;
+}
+
+
+static char *
+ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_proxy_conf_t *prev = parent;
+ ngx_mail_proxy_conf_t *conf = child;
+
+ ngx_conf_merge_value(conf->enable, prev->enable, 0);
+ ngx_conf_merge_value(conf->pass_error_message, prev->pass_error_message, 0);
+ ngx_conf_merge_value(conf->xclient, prev->xclient, 1);
+ ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
+ (size_t) ngx_pagesize);
+ ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000);
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_smtp_handler.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_smtp_handler.c
new file mode 100644
index 00000000000..46d703e2ce9
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_smtp_handler.c
@@ -0,0 +1,857 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_mail.h>
+#include <ngx_mail_smtp_module.h>
+
+
+static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_mail_smtp_resolve_name(ngx_event_t *rev);
+static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c);
+static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev);
+static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+
+static ngx_int_t ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s,
+ ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c);
+
+static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s,
+ ngx_connection_t *c, char *err);
+static void ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s,
+ ngx_connection_t *c, char *err);
+
+
+static u_char smtp_ok[] = "250 2.0.0 OK" CRLF;
+static u_char smtp_bye[] = "221 2.0.0 Bye" CRLF;
+static u_char smtp_starttls[] = "220 2.0.0 Start TLS" CRLF;
+static u_char smtp_next[] = "334 " CRLF;
+static u_char smtp_username[] = "334 VXNlcm5hbWU6" CRLF;
+static u_char smtp_password[] = "334 UGFzc3dvcmQ6" CRLF;
+static u_char smtp_invalid_command[] = "500 5.5.1 Invalid command" CRLF;
+static u_char smtp_invalid_pipelining[] =
+ "503 5.5.0 Improper use of SMTP command pipelining" CRLF;
+static u_char smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF;
+static u_char smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF;
+static u_char smtp_bad_sequence[] = "503 5.5.1 Bad sequence of commands" CRLF;
+
+
+static ngx_str_t smtp_unavailable = ngx_string("[UNAVAILABLE]");
+static ngx_str_t smtp_tempunavail = ngx_string("[TEMPUNAVAIL]");
+
+
+void
+ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_resolver_ctx_t *ctx;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ if (cscf->resolver == NULL) {
+ s->host = smtp_unavailable;
+ ngx_mail_smtp_greeting(s, c);
+ return;
+ }
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ if (c->sockaddr->sa_family == AF_UNIX) {
+ s->host = smtp_tempunavail;
+ ngx_mail_smtp_greeting(s, c);
+ return;
+ }
+#endif
+
+ c->log->action = "in resolving client address";
+
+ ctx = ngx_resolve_start(cscf->resolver, NULL);
+ if (ctx == NULL) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ ctx->addr.sockaddr = c->sockaddr;
+ ctx->addr.socklen = c->socklen;
+ ctx->handler = ngx_mail_smtp_resolve_addr_handler;
+ ctx->data = s;
+ ctx->timeout = cscf->resolver_timeout;
+
+ if (ngx_resolve_addr(ctx) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+}
+
+
+static void
+ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ s = ctx->data;
+ c = s->connection;
+
+ if (ctx->state) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "%V could not be resolved (%i: %s)",
+ &c->addr_text, ctx->state,
+ ngx_resolver_strerror(ctx->state));
+
+ if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
+ s->host = smtp_unavailable;
+
+ } else {
+ s->host = smtp_tempunavail;
+ }
+
+ ngx_resolve_addr_done(ctx);
+
+ ngx_mail_smtp_greeting(s, s->connection);
+
+ return;
+ }
+
+ c->log->action = "in resolving client hostname";
+
+ s->host.data = ngx_pstrdup(c->pool, &ctx->name);
+ if (s->host.data == NULL) {
+ ngx_resolve_addr_done(ctx);
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ s->host.len = ctx->name.len;
+
+ ngx_resolve_addr_done(ctx);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "address resolved: %V", &s->host);
+
+ c->read->handler = ngx_mail_smtp_resolve_name;
+
+ ngx_post_event(c->read, &ngx_posted_events);
+}
+
+
+static void
+ngx_mail_smtp_resolve_name(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_resolver_ctx_t *ctx;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ c = rev->data;
+ s = c->data;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ ctx = ngx_resolve_start(cscf->resolver, NULL);
+ if (ctx == NULL) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ ctx->name = s->host;
+ ctx->handler = ngx_mail_smtp_resolve_name_handler;
+ ctx->data = s;
+ ctx->timeout = cscf->resolver_timeout;
+
+ if (ngx_resolve_name(ctx) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+}
+
+
+static void
+ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx)
+{
+ ngx_uint_t i;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ s = ctx->data;
+ c = s->connection;
+
+ if (ctx->state) {
+ ngx_log_error(NGX_LOG_ERR, c->log, 0,
+ "\"%V\" could not be resolved (%i: %s)",
+ &ctx->name, ctx->state,
+ ngx_resolver_strerror(ctx->state));
+
+ if (ctx->state == NGX_RESOLVE_NXDOMAIN) {
+ s->host = smtp_unavailable;
+
+ } else {
+ s->host = smtp_tempunavail;
+ }
+
+ } else {
+
+#if (NGX_DEBUG)
+ {
+ u_char text[NGX_SOCKADDR_STRLEN];
+ ngx_str_t addr;
+
+ addr.data = text;
+
+ for (i = 0; i < ctx->naddrs; i++) {
+ addr.len = ngx_sock_ntop(ctx->addrs[i].sockaddr,
+ ctx->addrs[i].socklen,
+ text, NGX_SOCKADDR_STRLEN, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "name was resolved to %V", &addr);
+ }
+ }
+#endif
+
+ for (i = 0; i < ctx->naddrs; i++) {
+ if (ngx_cmp_sockaddr(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen,
+ c->sockaddr, c->socklen, 0)
+ == NGX_OK)
+ {
+ goto found;
+ }
+ }
+
+ s->host = smtp_unavailable;
+ }
+
+found:
+
+ ngx_resolve_name_done(ctx);
+
+ ngx_mail_smtp_greeting(s, c);
+}
+
+
+static void
+ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_msec_t timeout;
+ ngx_mail_core_srv_conf_t *cscf;
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "smtp greeting for \"%V\"", &s->host);
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+ sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+ timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout;
+ ngx_add_timer(c->read, timeout);
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ }
+
+ if (sscf->greeting_delay) {
+ c->read->handler = ngx_mail_smtp_invalid_pipelining;
+ return;
+ }
+
+ c->read->handler = ngx_mail_smtp_init_protocol;
+
+ s->out = sscf->greeting;
+
+ ngx_mail_send(c->write);
+}
+
+
+static void
+ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+ ngx_mail_core_srv_conf_t *cscf;
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+ c = rev->data;
+ s = c->data;
+
+ c->log->action = "in delay pipelining state";
+
+ if (rev->timedout) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "delay greeting");
+
+ rev->timedout = 0;
+
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ c->read->handler = ngx_mail_smtp_init_protocol;
+
+ ngx_add_timer(c->read, cscf->timeout);
+
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+ s->out = sscf->greeting;
+
+ } else {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "invalid pipelining");
+
+ if (s->buffer == NULL) {
+ if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {
+ return;
+ }
+ }
+
+ if (ngx_mail_smtp_discard_command(s, c,
+ "client was rejected before greeting: \"%V\"")
+ != NGX_OK)
+ {
+ return;
+ }
+
+ ngx_str_set(&s->out, smtp_invalid_pipelining);
+ s->quit = 1;
+ }
+
+ ngx_mail_send(c->write);
+}
+
+
+void
+ngx_mail_smtp_init_protocol(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ c = rev->data;
+
+ c->log->action = "in auth state";
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ s = c->data;
+
+ if (s->buffer == NULL) {
+ if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {
+ return;
+ }
+ }
+
+ s->mail_state = ngx_smtp_start;
+ c->read->handler = ngx_mail_smtp_auth_state;
+
+ ngx_mail_smtp_auth_state(rev);
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_create_buffer(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+ if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) {
+ ngx_mail_session_internal_server_error(s);
+ return NGX_ERROR;
+ }
+
+ sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+ s->buffer = ngx_create_temp_buf(c->pool, sscf->client_buffer_size);
+ if (s->buffer == NULL) {
+ ngx_mail_session_internal_server_error(s);
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_mail_smtp_auth_state(ngx_event_t *rev)
+{
+ ngx_int_t rc;
+ ngx_connection_t *c;
+ ngx_mail_session_t *s;
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp auth state");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_mail_close_connection(c);
+ return;
+ }
+
+ if (s->out.len) {
+ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp send handler busy");
+ s->blocked = 1;
+ return;
+ }
+
+ s->blocked = 0;
+
+ rc = ngx_mail_read_command(s, c);
+
+ if (rc == NGX_AGAIN || rc == NGX_ERROR) {
+ return;
+ }
+
+ ngx_str_set(&s->out, smtp_ok);
+
+ if (rc == NGX_OK) {
+ switch (s->mail_state) {
+
+ case ngx_smtp_start:
+
+ switch (s->command) {
+
+ case NGX_SMTP_HELO:
+ case NGX_SMTP_EHLO:
+ rc = ngx_mail_smtp_helo(s, c);
+ break;
+
+ case NGX_SMTP_AUTH:
+ rc = ngx_mail_smtp_auth(s, c);
+ break;
+
+ case NGX_SMTP_QUIT:
+ s->quit = 1;
+ ngx_str_set(&s->out, smtp_bye);
+ break;
+
+ case NGX_SMTP_MAIL:
+ rc = ngx_mail_smtp_mail(s, c);
+ break;
+
+ case NGX_SMTP_RCPT:
+ rc = ngx_mail_smtp_rcpt(s, c);
+ break;
+
+ case NGX_SMTP_RSET:
+ rc = ngx_mail_smtp_rset(s, c);
+ break;
+
+ case NGX_SMTP_NOOP:
+ break;
+
+ case NGX_SMTP_STARTTLS:
+ rc = ngx_mail_smtp_starttls(s, c);
+ ngx_str_set(&s->out, smtp_starttls);
+ break;
+
+ default:
+ rc = NGX_MAIL_PARSE_INVALID_COMMAND;
+ break;
+ }
+
+ break;
+
+ case ngx_smtp_auth_login_username:
+ rc = ngx_mail_auth_login_username(s, c, 0);
+
+ ngx_str_set(&s->out, smtp_password);
+ s->mail_state = ngx_smtp_auth_login_password;
+ break;
+
+ case ngx_smtp_auth_login_password:
+ rc = ngx_mail_auth_login_password(s, c);
+ break;
+
+ case ngx_smtp_auth_plain:
+ rc = ngx_mail_auth_plain(s, c, 0);
+ break;
+
+ case ngx_smtp_auth_cram_md5:
+ rc = ngx_mail_auth_cram_md5(s, c);
+ break;
+ }
+ }
+
+ if (s->buffer->pos < s->buffer->last) {
+ s->blocked = 1;
+ }
+
+ switch (rc) {
+
+ case NGX_DONE:
+ ngx_mail_auth(s, c);
+ return;
+
+ case NGX_ERROR:
+ ngx_mail_session_internal_server_error(s);
+ return;
+
+ case NGX_MAIL_PARSE_INVALID_COMMAND:
+ s->mail_state = ngx_smtp_start;
+ s->state = 0;
+ ngx_str_set(&s->out, smtp_invalid_command);
+
+ /* fall through */
+
+ case NGX_OK:
+ s->args.nelts = 0;
+
+ if (s->buffer->pos == s->buffer->last) {
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+ }
+
+ if (s->state) {
+ s->arg_start = s->buffer->pos;
+ }
+
+ ngx_mail_send(c->write);
+ }
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg;
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+ if (s->args.nelts != 1) {
+ ngx_str_set(&s->out, smtp_invalid_argument);
+ s->state = 0;
+ return NGX_OK;
+ }
+
+ arg = s->args.elts;
+
+ s->smtp_helo.len = arg[0].len;
+
+ s->smtp_helo.data = ngx_pnalloc(c->pool, arg[0].len);
+ if (s->smtp_helo.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len);
+
+ ngx_str_null(&s->smtp_from);
+ ngx_str_null(&s->smtp_to);
+
+ sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+ if (s->command == NGX_SMTP_HELO) {
+ s->out = sscf->server_name;
+
+ } else {
+ s->esmtp = 1;
+
+#if (NGX_MAIL_SSL)
+
+ if (c->ssl == NULL) {
+ ngx_mail_ssl_conf_t *sslcf;
+
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {
+ s->out = sscf->starttls_capability;
+ return NGX_OK;
+ }
+
+ if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {
+ s->out = sscf->starttls_only_capability;
+ return NGX_OK;
+ }
+ }
+#endif
+
+ s->out = sscf->capability;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_int_t rc;
+ ngx_mail_core_srv_conf_t *cscf;
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+#if (NGX_MAIL_SSL)
+ if (ngx_mail_starttls_only(s, c)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+#endif
+
+ if (s->args.nelts == 0) {
+ ngx_str_set(&s->out, smtp_invalid_argument);
+ s->state = 0;
+ return NGX_OK;
+ }
+
+ rc = ngx_mail_auth_parse(s, c);
+
+ switch (rc) {
+
+ case NGX_MAIL_AUTH_LOGIN:
+
+ ngx_str_set(&s->out, smtp_username);
+ s->mail_state = ngx_smtp_auth_login_username;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_LOGIN_USERNAME:
+
+ ngx_str_set(&s->out, smtp_password);
+ s->mail_state = ngx_smtp_auth_login_password;
+
+ return ngx_mail_auth_login_username(s, c, 1);
+
+ case NGX_MAIL_AUTH_PLAIN:
+
+ ngx_str_set(&s->out, smtp_next);
+ s->mail_state = ngx_smtp_auth_plain;
+
+ return NGX_OK;
+
+ case NGX_MAIL_AUTH_CRAM_MD5:
+
+ sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+ if (!(sscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+ }
+
+ if (s->salt.data == NULL) {
+ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+ if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4) == NGX_OK) {
+ s->mail_state = ngx_smtp_auth_cram_md5;
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+ }
+
+ return rc;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg, cmd;
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+ sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+ if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) {
+ ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\"");
+ ngx_str_set(&s->out, smtp_auth_required);
+ return NGX_OK;
+ }
+
+ /* auth none */
+
+ if (s->smtp_from.len) {
+ ngx_str_set(&s->out, smtp_bad_sequence);
+ return NGX_OK;
+ }
+
+ if (s->args.nelts == 0) {
+ ngx_str_set(&s->out, smtp_invalid_argument);
+ return NGX_OK;
+ }
+
+ arg = s->args.elts;
+ arg += s->args.nelts - 1;
+
+ cmd.len = arg->data + arg->len - s->cmd.data;
+ cmd.data = s->cmd.data;
+
+ s->smtp_from.len = cmd.len;
+
+ s->smtp_from.data = ngx_pnalloc(c->pool, cmd.len);
+ if (s->smtp_from.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->smtp_from.data, cmd.data, cmd.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "smtp mail from:\"%V\"", &s->smtp_from);
+
+ ngx_str_set(&s->out, smtp_ok);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_t *arg, cmd;
+
+ if (s->smtp_from.len == 0) {
+ ngx_str_set(&s->out, smtp_bad_sequence);
+ return NGX_OK;
+ }
+
+ if (s->args.nelts == 0) {
+ ngx_str_set(&s->out, smtp_invalid_argument);
+ return NGX_OK;
+ }
+
+ arg = s->args.elts;
+ arg += s->args.nelts - 1;
+
+ cmd.len = arg->data + arg->len - s->cmd.data;
+ cmd.data = s->cmd.data;
+
+ s->smtp_to.len = cmd.len;
+
+ s->smtp_to.data = ngx_pnalloc(c->pool, cmd.len);
+ if (s->smtp_to.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(s->smtp_to.data, cmd.data, cmd.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+ "smtp rcpt to:\"%V\"", &s->smtp_to);
+
+ s->auth_method = NGX_MAIL_AUTH_NONE;
+
+ return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+ ngx_str_null(&s->smtp_from);
+ ngx_str_null(&s->smtp_to);
+ ngx_str_set(&s->out, smtp_ok);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+#if (NGX_MAIL_SSL)
+ ngx_mail_ssl_conf_t *sslcf;
+
+ if (c->ssl == NULL) {
+ sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+ if (sslcf->starttls) {
+
+ /*
+ * RFC3207 requires us to discard any knowledge
+ * obtained from client before STARTTLS.
+ */
+
+ ngx_str_null(&s->smtp_helo);
+ ngx_str_null(&s->smtp_from);
+ ngx_str_null(&s->smtp_to);
+
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+
+ c->read->handler = ngx_mail_starttls_handler;
+ return NGX_OK;
+ }
+ }
+
+#endif
+
+ return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c,
+ char *err)
+{
+ ssize_t n;
+
+ n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);
+
+ if (n == NGX_ERROR || n == 0) {
+ ngx_mail_close_connection(c);
+ return NGX_ERROR;
+ }
+
+ if (n > 0) {
+ s->buffer->last += n;
+ }
+
+ if (n == NGX_AGAIN) {
+ if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+ ngx_mail_session_internal_server_error(s);
+ return NGX_ERROR;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ ngx_mail_smtp_log_rejected_command(s, c, err);
+
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, ngx_connection_t *c,
+ char *err)
+{
+ u_char ch;
+ ngx_str_t cmd;
+ ngx_uint_t i;
+
+ if (c->log->log_level < NGX_LOG_INFO) {
+ return;
+ }
+
+ cmd.len = s->buffer->last - s->buffer->start;
+ cmd.data = s->buffer->start;
+
+ for (i = 0; i < cmd.len; i++) {
+ ch = cmd.data[i];
+
+ if (ch != CR && ch != LF) {
+ continue;
+ }
+
+ cmd.data[i] = '_';
+ }
+
+ cmd.len = i;
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, err, &cmd);
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_smtp_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_smtp_module.c
new file mode 100644
index 00000000000..02bbf1fb9d8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_smtp_module.c
@@ -0,0 +1,307 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_mail.h>
+#include <ngx_mail_smtp_module.h>
+
+
+static void *ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+
+static ngx_conf_bitmask_t ngx_mail_smtp_auth_methods[] = {
+ { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
+ { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },
+ { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
+ { ngx_string("none"), NGX_MAIL_AUTH_NONE_ENABLED },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_str_t ngx_mail_smtp_auth_methods_names[] = {
+ ngx_string("PLAIN"),
+ ngx_string("LOGIN"),
+ ngx_null_string, /* APOP */
+ ngx_string("CRAM-MD5"),
+ ngx_null_string /* NONE */
+};
+
+
+static ngx_mail_protocol_t ngx_mail_smtp_protocol = {
+ ngx_string("smtp"),
+ { 25, 465, 587, 0 },
+ NGX_MAIL_SMTP_PROTOCOL,
+
+ ngx_mail_smtp_init_session,
+ ngx_mail_smtp_init_protocol,
+ ngx_mail_smtp_parse_command,
+ ngx_mail_smtp_auth_state,
+
+ ngx_string("451 4.3.2 Internal server error" CRLF)
+};
+
+
+static ngx_command_t ngx_mail_smtp_commands[] = {
+
+ { ngx_string("smtp_client_buffer"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_smtp_srv_conf_t, client_buffer_size),
+ NULL },
+
+ { ngx_string("smtp_greeting_delay"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_smtp_srv_conf_t, greeting_delay),
+ NULL },
+
+ { ngx_string("smtp_capabilities"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_mail_capabilities,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_smtp_srv_conf_t, capabilities),
+ NULL },
+
+ { ngx_string("smtp_auth"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_smtp_srv_conf_t, auth_methods),
+ &ngx_mail_smtp_auth_methods },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_smtp_module_ctx = {
+ &ngx_mail_smtp_protocol, /* protocol */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_smtp_create_srv_conf, /* create server configuration */
+ ngx_mail_smtp_merge_srv_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_smtp_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_smtp_module_ctx, /* module context */
+ ngx_mail_smtp_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static void *
+ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_mail_smtp_srv_conf_t *sscf;
+
+ sscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_smtp_srv_conf_t));
+ if (sscf == NULL) {
+ return NULL;
+ }
+
+ sscf->client_buffer_size = NGX_CONF_UNSET_SIZE;
+ sscf->greeting_delay = NGX_CONF_UNSET_MSEC;
+
+ if (ngx_array_init(&sscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return sscf;
+}
+
+
+static char *
+ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_smtp_srv_conf_t *prev = parent;
+ ngx_mail_smtp_srv_conf_t *conf = child;
+
+ u_char *p, *auth, *last;
+ size_t size;
+ ngx_str_t *c;
+ ngx_uint_t i, m, auth_enabled;
+ ngx_mail_core_srv_conf_t *cscf;
+
+ ngx_conf_merge_size_value(conf->client_buffer_size,
+ prev->client_buffer_size,
+ (size_t) ngx_pagesize);
+
+ ngx_conf_merge_msec_value(conf->greeting_delay,
+ prev->greeting_delay, 0);
+
+ ngx_conf_merge_bitmask_value(conf->auth_methods,
+ prev->auth_methods,
+ (NGX_CONF_BITMASK_SET
+ |NGX_MAIL_AUTH_PLAIN_ENABLED
+ |NGX_MAIL_AUTH_LOGIN_ENABLED));
+
+
+ cscf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_core_module);
+
+ size = sizeof("220 ESMTP ready" CRLF) - 1 + cscf->server_name.len;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->greeting.len = size;
+ conf->greeting.data = p;
+
+ *p++ = '2'; *p++ = '2'; *p++ = '0'; *p++ = ' ';
+ p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
+ ngx_memcpy(p, " ESMTP ready" CRLF, sizeof(" ESMTP ready" CRLF) - 1);
+
+
+ size = sizeof("250 " CRLF) - 1 + cscf->server_name.len;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->server_name.len = size;
+ conf->server_name.data = p;
+
+ *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';
+ p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
+ *p++ = CR; *p = LF;
+
+
+ if (conf->capabilities.nelts == 0) {
+ conf->capabilities = prev->capabilities;
+ }
+
+ size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1;
+
+ c = conf->capabilities.elts;
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1;
+ }
+
+ auth_enabled = 0;
+
+ for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+ m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+ m <<= 1, i++)
+ {
+ if (m & conf->auth_methods) {
+ size += 1 + ngx_mail_smtp_auth_methods_names[i].len;
+ auth_enabled = 1;
+ }
+ }
+
+ if (auth_enabled) {
+ size += sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1;
+ }
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->capability.len = size;
+ conf->capability.data = p;
+
+ last = p;
+
+ *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';
+ p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
+ *p++ = CR; *p++ = LF;
+
+ for (i = 0; i < conf->capabilities.nelts; i++) {
+ last = p;
+ *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';
+ p = ngx_cpymem(p, c[i].data, c[i].len);
+ *p++ = CR; *p++ = LF;
+ }
+
+ auth = p;
+
+ if (auth_enabled) {
+ last = p;
+
+ *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';
+ *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H';
+
+ for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+ m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+ m <<= 1, i++)
+ {
+ if (m & conf->auth_methods) {
+ *p++ = ' ';
+ p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data,
+ ngx_mail_smtp_auth_methods_names[i].len);
+ }
+ }
+
+ *p++ = CR; *p = LF;
+
+ } else {
+ last[3] = ' ';
+ }
+
+ size += sizeof("250 STARTTLS" CRLF) - 1;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->starttls_capability.len = size;
+ conf->starttls_capability.data = p;
+
+ p = ngx_cpymem(p, conf->capability.data, conf->capability.len);
+
+ p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
+
+ p = conf->starttls_capability.data
+ + (last - conf->capability.data) + 3;
+ *p = '-';
+
+ size = (auth - conf->capability.data)
+ + sizeof("250 STARTTLS" CRLF) - 1;
+
+ p = ngx_pnalloc(cf->pool, size);
+ if (p == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ conf->starttls_only_capability.len = size;
+ conf->starttls_only_capability.data = p;
+
+ p = ngx_cpymem(p, conf->capability.data, auth - conf->capability.data);
+
+ ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
+
+ if (last < auth) {
+ p = conf->starttls_only_capability.data
+ + (last - conf->capability.data) + 3;
+ *p = '-';
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_smtp_module.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_smtp_module.h
new file mode 100644
index 00000000000..04ffab60ade
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_smtp_module.h
@@ -0,0 +1,45 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_MAIL_SMTP_MODULE_H_INCLUDED_
+#define _NGX_MAIL_SMTP_MODULE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_mail.h>
+#include <ngx_mail_smtp_module.h>
+
+
+typedef struct {
+ ngx_msec_t greeting_delay;
+
+ size_t client_buffer_size;
+
+ ngx_str_t capability;
+ ngx_str_t starttls_capability;
+ ngx_str_t starttls_only_capability;
+
+ ngx_str_t server_name;
+ ngx_str_t greeting;
+
+ ngx_uint_t auth_methods;
+
+ ngx_array_t capabilities;
+} ngx_mail_smtp_srv_conf_t;
+
+
+void ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c);
+void ngx_mail_smtp_init_protocol(ngx_event_t *rev);
+void ngx_mail_smtp_auth_state(ngx_event_t *rev);
+ngx_int_t ngx_mail_smtp_parse_command(ngx_mail_session_t *s);
+
+
+extern ngx_module_t ngx_mail_smtp_module;
+
+
+#endif /* _NGX_MAIL_SMTP_MODULE_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_ssl_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_ssl_module.c
new file mode 100644
index 00000000000..f864d991060
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_ssl_module.c
@@ -0,0 +1,568 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_mail.h>
+
+
+#define NGX_DEFAULT_CIPHERS "HIGH:!aNULL:!MD5"
+#define NGX_DEFAULT_ECDH_CURVE "prime256v1"
+
+
+static void *ngx_mail_ssl_create_conf(ngx_conf_t *cf);
+static char *ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child);
+
+static char *ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_conf_enum_t ngx_mail_starttls_state[] = {
+ { ngx_string("off"), NGX_MAIL_STARTTLS_OFF },
+ { ngx_string("on"), NGX_MAIL_STARTTLS_ON },
+ { ngx_string("only"), NGX_MAIL_STARTTLS_ONLY },
+ { ngx_null_string, 0 }
+};
+
+
+
+static ngx_conf_bitmask_t ngx_mail_ssl_protocols[] = {
+ { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
+ { ngx_string("SSLv3"), NGX_SSL_SSLv3 },
+ { ngx_string("TLSv1"), NGX_SSL_TLSv1 },
+ { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 },
+ { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_command_t ngx_mail_ssl_commands[] = {
+
+ { ngx_string("ssl"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_mail_ssl_enable,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, enable),
+ NULL },
+
+ { ngx_string("starttls"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_mail_ssl_starttls,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, starttls),
+ ngx_mail_starttls_state },
+
+ { ngx_string("ssl_certificate"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, certificate),
+ NULL },
+
+ { ngx_string("ssl_certificate_key"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, certificate_key),
+ NULL },
+
+ { ngx_string("ssl_password_file"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_mail_ssl_password_file,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("ssl_dhparam"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, dhparam),
+ NULL },
+
+ { ngx_string("ssl_ecdh_curve"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, ecdh_curve),
+ NULL },
+
+ { ngx_string("ssl_protocols"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
+ ngx_conf_set_bitmask_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, protocols),
+ &ngx_mail_ssl_protocols },
+
+ { ngx_string("ssl_ciphers"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, ciphers),
+ NULL },
+
+ { ngx_string("ssl_prefer_server_ciphers"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, prefer_server_ciphers),
+ NULL },
+
+ { ngx_string("ssl_session_cache"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE12,
+ ngx_mail_ssl_session_cache,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("ssl_session_tickets"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, session_tickets),
+ NULL },
+
+ { ngx_string("ssl_session_ticket_key"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_array_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, session_ticket_keys),
+ NULL },
+
+ { ngx_string("ssl_session_timeout"),
+ NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_sec_slot,
+ NGX_MAIL_SRV_CONF_OFFSET,
+ offsetof(ngx_mail_ssl_conf_t, session_timeout),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_mail_module_t ngx_mail_ssl_module_ctx = {
+ NULL, /* protocol */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_mail_ssl_create_conf, /* create server configuration */
+ ngx_mail_ssl_merge_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_mail_ssl_module = {
+ NGX_MODULE_V1,
+ &ngx_mail_ssl_module_ctx, /* module context */
+ ngx_mail_ssl_commands, /* module directives */
+ NGX_MAIL_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_str_t ngx_mail_ssl_sess_id_ctx = ngx_string("MAIL");
+
+
+static void *
+ngx_mail_ssl_create_conf(ngx_conf_t *cf)
+{
+ ngx_mail_ssl_conf_t *scf;
+
+ scf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_ssl_conf_t));
+ if (scf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * scf->protocols = 0;
+ * scf->certificate = { 0, NULL };
+ * scf->certificate_key = { 0, NULL };
+ * scf->dhparam = { 0, NULL };
+ * scf->ecdh_curve = { 0, NULL };
+ * scf->ciphers = { 0, NULL };
+ * scf->shm_zone = NULL;
+ */
+
+ scf->enable = NGX_CONF_UNSET;
+ scf->starttls = NGX_CONF_UNSET_UINT;
+ scf->passwords = NGX_CONF_UNSET_PTR;
+ scf->prefer_server_ciphers = NGX_CONF_UNSET;
+ scf->builtin_session_cache = NGX_CONF_UNSET;
+ scf->session_timeout = NGX_CONF_UNSET;
+ scf->session_tickets = NGX_CONF_UNSET;
+ scf->session_ticket_keys = NGX_CONF_UNSET_PTR;
+
+ return scf;
+}
+
+
+static char *
+ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_mail_ssl_conf_t *prev = parent;
+ ngx_mail_ssl_conf_t *conf = child;
+
+ char *mode;
+ ngx_pool_cleanup_t *cln;
+
+ ngx_conf_merge_value(conf->enable, prev->enable, 0);
+ ngx_conf_merge_uint_value(conf->starttls, prev->starttls,
+ NGX_MAIL_STARTTLS_OFF);
+
+ ngx_conf_merge_value(conf->session_timeout,
+ prev->session_timeout, 300);
+
+ ngx_conf_merge_value(conf->prefer_server_ciphers,
+ prev->prefer_server_ciphers, 0);
+
+ ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
+ (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3|NGX_SSL_TLSv1
+ |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2));
+
+ ngx_conf_merge_str_value(conf->certificate, prev->certificate, "");
+ ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key, "");
+
+ ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
+
+ ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
+
+ ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
+ NGX_DEFAULT_ECDH_CURVE);
+
+ ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
+
+
+ conf->ssl.log = cf->log;
+
+ if (conf->enable) {
+ mode = "ssl";
+
+ } else if (conf->starttls != NGX_MAIL_STARTTLS_OFF) {
+ mode = "starttls";
+
+ } else {
+ mode = "";
+ }
+
+ if (conf->file == NULL) {
+ conf->file = prev->file;
+ conf->line = prev->line;
+ }
+
+ if (*mode) {
+
+ if (conf->certificate.len == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"ssl_certificate\" is defined for "
+ "the \"%s\" directive in %s:%ui",
+ mode, conf->file, conf->line);
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->certificate_key.len == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"ssl_certificate_key\" is defined for "
+ "the \"%s\" directive in %s:%ui",
+ mode, conf->file, conf->line);
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+
+ if (conf->certificate.len == 0) {
+ return NGX_CONF_OK;
+ }
+
+ if (conf->certificate_key.len == 0) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "no \"ssl_certificate_key\" is defined "
+ "for certificate \"%V\"",
+ &conf->certificate);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ cln->handler = ngx_ssl_cleanup_ctx;
+ cln->data = &conf->ssl;
+
+ if (ngx_ssl_certificate(cf, &conf->ssl, &conf->certificate,
+ &conf->certificate_key, conf->passwords)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ if (SSL_CTX_set_cipher_list(conf->ssl.ctx,
+ (const char *) conf->ciphers.data)
+ == 0)
+ {
+ ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
+ "SSL_CTX_set_cipher_list(\"%V\") failed",
+ &conf->ciphers);
+ return NGX_CONF_ERROR;
+ }
+
+ if (conf->prefer_server_ciphers) {
+ SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+ }
+
+ SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback);
+
+ if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_value(conf->builtin_session_cache,
+ prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);
+
+ if (conf->shm_zone == NULL) {
+ conf->shm_zone = prev->shm_zone;
+ }
+
+ if (ngx_ssl_session_cache(&conf->ssl, &ngx_mail_ssl_sess_id_ctx,
+ conf->builtin_session_cache,
+ conf->shm_zone, conf->session_timeout)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_conf_merge_value(conf->session_tickets,
+ prev->session_tickets, 1);
+
+#ifdef SSL_OP_NO_TICKET
+ if (!conf->session_tickets) {
+ SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET);
+ }
+#endif
+
+ ngx_conf_merge_ptr_value(conf->session_ticket_keys,
+ prev->session_ticket_keys, NULL);
+
+ if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys)
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_ssl_conf_t *scf = conf;
+
+ char *rv;
+
+ rv = ngx_conf_set_flag_slot(cf, cmd, conf);
+
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+
+ if (scf->enable && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "\"starttls\" directive conflicts with \"ssl on\"");
+ return NGX_CONF_ERROR;
+ }
+
+ scf->file = cf->conf_file->file.name.data;
+ scf->line = cf->conf_file->line;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_ssl_conf_t *scf = conf;
+
+ char *rv;
+
+ rv = ngx_conf_set_enum_slot(cf, cmd, conf);
+
+ if (rv != NGX_CONF_OK) {
+ return rv;
+ }
+
+ if (scf->enable == 1 && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "\"ssl\" directive conflicts with \"starttls\"");
+ return NGX_CONF_ERROR;
+ }
+
+ scf->file = cf->conf_file->file.name.data;
+ scf->line = cf->conf_file->line;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_ssl_conf_t *scf = conf;
+
+ ngx_str_t *value;
+
+ if (scf->passwords != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ scf->passwords = ngx_ssl_read_password_file(cf, &value[1]);
+
+ if (scf->passwords == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_mail_ssl_conf_t *scf = conf;
+
+ size_t len;
+ ngx_str_t *value, name, size;
+ ngx_int_t n;
+ ngx_uint_t i, j;
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "off") == 0) {
+ scf->builtin_session_cache = NGX_SSL_NO_SCACHE;
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "none") == 0) {
+ scf->builtin_session_cache = NGX_SSL_NONE_SCACHE;
+ continue;
+ }
+
+ if (ngx_strcmp(value[i].data, "builtin") == 0) {
+ scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;
+ continue;
+ }
+
+ if (value[i].len > sizeof("builtin:") - 1
+ && ngx_strncmp(value[i].data, "builtin:", sizeof("builtin:") - 1)
+ == 0)
+ {
+ n = ngx_atoi(value[i].data + sizeof("builtin:") - 1,
+ value[i].len - (sizeof("builtin:") - 1));
+
+ if (n == NGX_ERROR) {
+ goto invalid;
+ }
+
+ scf->builtin_session_cache = n;
+
+ continue;
+ }
+
+ if (value[i].len > sizeof("shared:") - 1
+ && ngx_strncmp(value[i].data, "shared:", sizeof("shared:") - 1)
+ == 0)
+ {
+ len = 0;
+
+ for (j = sizeof("shared:") - 1; j < value[i].len; j++) {
+ if (value[i].data[j] == ':') {
+ break;
+ }
+
+ len++;
+ }
+
+ if (len == 0) {
+ goto invalid;
+ }
+
+ name.len = len;
+ name.data = value[i].data + sizeof("shared:") - 1;
+
+ size.len = value[i].len - j - 1;
+ size.data = name.data + len + 1;
+
+ n = ngx_parse_size(&size);
+
+ if (n == NGX_ERROR) {
+ goto invalid;
+ }
+
+ if (n < (ngx_int_t) (8 * ngx_pagesize)) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "session cache \"%V\" is too small",
+ &value[i]);
+
+ return NGX_CONF_ERROR;
+ }
+
+ scf->shm_zone = ngx_shared_memory_add(cf, &name, n,
+ &ngx_mail_ssl_module);
+ if (scf->shm_zone == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ scf->shm_zone->init = ngx_ssl_session_cache_init;
+
+ continue;
+ }
+
+ goto invalid;
+ }
+
+ if (scf->shm_zone && scf->builtin_session_cache == NGX_CONF_UNSET) {
+ scf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;
+ }
+
+ return NGX_CONF_OK;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid session cache \"%V\"", &value[i]);
+
+ return NGX_CONF_ERROR;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_ssl_module.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_ssl_module.h
new file mode 100644
index 00000000000..987d029ef73
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/mail/ngx_mail_ssl_module.h
@@ -0,0 +1,57 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_MAIL_SSL_H_INCLUDED_
+#define _NGX_MAIL_SSL_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_mail.h>
+
+
+#define NGX_MAIL_STARTTLS_OFF 0
+#define NGX_MAIL_STARTTLS_ON 1
+#define NGX_MAIL_STARTTLS_ONLY 2
+
+
+typedef struct {
+ ngx_flag_t enable;
+ ngx_flag_t prefer_server_ciphers;
+
+ ngx_ssl_t ssl;
+
+ ngx_uint_t starttls;
+ ngx_uint_t protocols;
+
+ ssize_t builtin_session_cache;
+
+ time_t session_timeout;
+
+ ngx_str_t certificate;
+ ngx_str_t certificate_key;
+ ngx_str_t dhparam;
+ ngx_str_t ecdh_curve;
+
+ ngx_str_t ciphers;
+
+ ngx_array_t *passwords;
+
+ ngx_shm_zone_t *shm_zone;
+
+ ngx_flag_t session_tickets;
+ ngx_array_t *session_ticket_keys;
+
+ u_char *file;
+ ngx_uint_t line;
+} ngx_mail_ssl_conf_t;
+
+
+extern ngx_module_t ngx_mail_ssl_module;
+
+
+#endif /* _NGX_MAIL_SSL_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/misc/ngx_cpp_test_module.cpp b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/misc/ngx_cpp_test_module.cpp
new file mode 100644
index 00000000000..5d2f08d3967
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/misc/ngx_cpp_test_module.cpp
@@ -0,0 +1,29 @@
+
+// stub module to test header files' C++ compatibility
+
+extern "C" {
+ #include <ngx_config.h>
+ #include <ngx_core.h>
+ #include <ngx_event.h>
+ #include <ngx_event_connect.h>
+ #include <ngx_event_pipe.h>
+
+ #include <ngx_http.h>
+
+ #include <ngx_mail.h>
+ #include <ngx_mail_pop3_module.h>
+ #include <ngx_mail_imap_module.h>
+ #include <ngx_mail_smtp_module.h>
+}
+
+// nginx header files should go before other, because they define 64-bit off_t
+// #include <string>
+
+
+void ngx_cpp_test_handler(void *data);
+
+void
+ngx_cpp_test_handler(void *data)
+{
+ return;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/misc/ngx_google_perftools_module.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/misc/ngx_google_perftools_module.c
new file mode 100644
index 00000000000..f2f8221b560
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/misc/ngx_google_perftools_module.c
@@ -0,0 +1,126 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+/*
+ * declare Profiler interface here because
+ * <google/profiler.h> is C++ header file
+ */
+
+int ProfilerStart(u_char* fname);
+void ProfilerStop(void);
+void ProfilerRegisterThread(void);
+
+
+static void *ngx_google_perftools_create_conf(ngx_cycle_t *cycle);
+static ngx_int_t ngx_google_perftools_worker(ngx_cycle_t *cycle);
+
+
+typedef struct {
+ ngx_str_t profiles;
+} ngx_google_perftools_conf_t;
+
+
+static ngx_command_t ngx_google_perftools_commands[] = {
+
+ { ngx_string("google_perftools_profiles"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ 0,
+ offsetof(ngx_google_perftools_conf_t, profiles),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_core_module_t ngx_google_perftools_module_ctx = {
+ ngx_string("google_perftools"),
+ ngx_google_perftools_create_conf,
+ NULL
+};
+
+
+ngx_module_t ngx_google_perftools_module = {
+ NGX_MODULE_V1,
+ &ngx_google_perftools_module_ctx, /* module context */
+ ngx_google_perftools_commands, /* module directives */
+ NGX_CORE_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ ngx_google_perftools_worker, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static void *
+ngx_google_perftools_create_conf(ngx_cycle_t *cycle)
+{
+ ngx_google_perftools_conf_t *gptcf;
+
+ gptcf = ngx_pcalloc(cycle->pool, sizeof(ngx_google_perftools_conf_t));
+ if (gptcf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc()
+ *
+ * gptcf->profiles = { 0, NULL };
+ */
+
+ return gptcf;
+}
+
+
+static ngx_int_t
+ngx_google_perftools_worker(ngx_cycle_t *cycle)
+{
+ u_char *profile;
+ ngx_google_perftools_conf_t *gptcf;
+
+ gptcf = (ngx_google_perftools_conf_t *)
+ ngx_get_conf(cycle->conf_ctx, ngx_google_perftools_module);
+
+ if (gptcf->profiles.len == 0) {
+ return NGX_OK;
+ }
+
+ profile = ngx_alloc(gptcf->profiles.len + NGX_INT_T_LEN + 2, cycle->log);
+ if (profile == NULL) {
+ return NGX_OK;
+ }
+
+ if (getenv("CPUPROFILE")) {
+ /* disable inherited Profiler enabled in master process */
+ ProfilerStop();
+ }
+
+ ngx_sprintf(profile, "%V.%d%Z", &gptcf->profiles, ngx_pid);
+
+ if (ProfilerStart(profile)) {
+ /* start ITIMER_PROF timer */
+ ProfilerRegisterThread();
+
+ } else {
+ ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_errno,
+ "ProfilerStart(%s) failed", profile);
+ }
+
+ ngx_free(profile);
+
+ return NGX_OK;
+}
+
+
+/* ProfilerStop() is called on Profiler destruction */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_read.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_read.c
new file mode 100644
index 00000000000..7849881730f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_read.c
@@ -0,0 +1,109 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+extern int ngx_kqueue;
+
+
+ssize_t
+ngx_aio_read(ngx_connection_t *c, u_char *buf, size_t size)
+{
+ int n;
+ ngx_event_t *rev;
+
+ rev = c->read;
+
+ if (!rev->ready) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0, "second aio post");
+ return NGX_AGAIN;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "rev->complete: %d", rev->complete);
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "aio size: %d", size);
+
+ if (!rev->complete) {
+ ngx_memzero(&rev->aiocb, sizeof(struct aiocb));
+
+ rev->aiocb.aio_fildes = c->fd;
+ rev->aiocb.aio_buf = buf;
+ rev->aiocb.aio_nbytes = size;
+
+#if (NGX_HAVE_KQUEUE)
+ rev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;
+ rev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
+ rev->aiocb.aio_sigevent.sigev_value.sigval_ptr = rev;
+#endif
+
+ if (aio_read(&rev->aiocb) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, rev->log, ngx_errno,
+ "aio_read() failed");
+ rev->error = 1;
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "aio_read: #%d OK", c->fd);
+
+ rev->active = 1;
+ rev->ready = 0;
+ }
+
+ rev->complete = 0;
+
+ n = aio_error(&rev->aiocb);
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, "aio_error() failed");
+ rev->error = 1;
+ return NGX_ERROR;
+ }
+
+ if (n != 0) {
+ if (n == NGX_EINPROGRESS) {
+ if (rev->ready) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, n,
+ "aio_read() still in progress");
+ rev->ready = 0;
+ }
+ return NGX_AGAIN;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, c->log, n, "aio_read() failed");
+ rev->error = 1;
+ rev->ready = 0;
+ return NGX_ERROR;
+ }
+
+ n = aio_return(&rev->aiocb);
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
+ "aio_return() failed");
+
+ rev->error = 1;
+ rev->ready = 0;
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, rev->log, 0,
+ "aio_read: #%d %d", c->fd, n);
+
+ if (n == 0) {
+ rev->eof = 1;
+ rev->ready = 0;
+ } else {
+ rev->ready = 1;
+ }
+
+ rev->active = 0;
+
+ return n;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_read_chain.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_read_chain.c
new file mode 100644
index 00000000000..8c831b95129
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_read_chain.c
@@ -0,0 +1,78 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+ssize_t
+ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl)
+{
+ int n;
+ u_char *buf, *prev;
+ size_t size;
+ ssize_t total;
+
+ if (c->read->pending_eof) {
+ c->read->ready = 0;
+ return 0;
+ }
+
+ total = 0;
+
+ while (cl) {
+
+ /* we can post the single aio operation only */
+
+ if (!c->read->ready) {
+ return total ? total : NGX_AGAIN;
+ }
+
+ buf = cl->buf->last;
+ prev = cl->buf->last;
+ size = 0;
+
+ /* coalesce the neighbouring bufs */
+
+ while (cl && prev == cl->buf->last) {
+ size += cl->buf->end - cl->buf->last;
+ prev = cl->buf->end;
+ cl = cl->next;
+ }
+
+ n = ngx_aio_read(c, buf, size);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_read: %d", n);
+
+ if (n == NGX_AGAIN) {
+ return total ? total : NGX_AGAIN;
+ }
+
+ if (n == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (n == 0) {
+ c->read->pending_eof = 1;
+ if (total) {
+ c->read->eof = 0;
+ c->read->ready = 1;
+ }
+ return total;
+ }
+
+ if (n > 0) {
+ total += n;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "aio_read total: %d", total);
+ }
+
+ return total ? total : NGX_AGAIN;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_write.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_write.c
new file mode 100644
index 00000000000..f0d93918e3b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_write.c
@@ -0,0 +1,109 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+extern int ngx_kqueue;
+
+
+ssize_t
+ngx_aio_write(ngx_connection_t *c, u_char *buf, size_t size)
+{
+ int n;
+ ngx_event_t *wev;
+
+ wev = c->write;
+
+ if (!wev->ready) {
+ return NGX_AGAIN;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, wev->log, 0,
+ "aio: wev->complete: %d", wev->complete);
+
+ if (!wev->complete) {
+ ngx_memzero(&wev->aiocb, sizeof(struct aiocb));
+
+ wev->aiocb.aio_fildes = c->fd;
+ wev->aiocb.aio_buf = buf;
+ wev->aiocb.aio_nbytes = size;
+
+#if (NGX_HAVE_KQUEUE)
+ wev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;
+ wev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
+ wev->aiocb.aio_sigevent.sigev_value.sigval_ptr = wev;
+#endif
+
+ if (aio_write(&wev->aiocb) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, wev->log, ngx_errno,
+ "aio_write() failed");
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0, "aio_write: OK");
+
+ wev->active = 1;
+ wev->ready = 0;
+ }
+
+ wev->complete = 0;
+
+ n = aio_error(&wev->aiocb);
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_CRIT, wev->log, ngx_errno, "aio_error() failed");
+ wev->error = 1;
+ return NGX_ERROR;
+ }
+
+ if (n != 0) {
+ if (n == NGX_EINPROGRESS) {
+ if (wev->ready) {
+ ngx_log_error(NGX_LOG_ALERT, wev->log, n,
+ "aio_write() still in progress");
+ wev->ready = 0;
+ }
+ return NGX_AGAIN;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, wev->log, n, "aio_write() failed");
+ wev->error = 1;
+ wev->ready = 0;
+
+#if 1
+ n = aio_return(&wev->aiocb);
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_ALERT, wev->log, ngx_errno,
+ "aio_return() failed");
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, wev->log, n, "aio_return() %d", n);
+#endif
+
+ return NGX_ERROR;
+ }
+
+ n = aio_return(&wev->aiocb);
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_ALERT, wev->log, ngx_errno,
+ "aio_return() failed");
+
+ wev->error = 1;
+ wev->ready = 0;
+ return NGX_ERROR;
+ }
+
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, wev->log, 0, "aio_write: %d", n);
+
+ wev->active = 0;
+ wev->ready = 1;
+
+ return n;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_write_chain.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_write_chain.c
new file mode 100644
index 00000000000..b0c25085d1f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_aio_write_chain.c
@@ -0,0 +1,100 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+ngx_chain_t *
+ngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
+{
+ u_char *buf, *prev;
+ off_t send, sent;
+ size_t len;
+ ssize_t n, size;
+ ngx_chain_t *cl;
+
+ /* the maximum limit size is the maximum size_t value - the page size */
+
+ if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
+ limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
+ }
+
+ send = 0;
+ sent = 0;
+ cl = in;
+
+ while (cl) {
+
+ if (cl->buf->pos == cl->buf->last) {
+ cl = cl->next;
+ continue;
+ }
+
+ /* we can post the single aio operation only */
+
+ if (!c->write->ready) {
+ return cl;
+ }
+
+ buf = cl->buf->pos;
+ prev = buf;
+ len = 0;
+
+ /* coalesce the neighbouring bufs */
+
+ while (cl && prev == cl->buf->pos && send < limit) {
+ if (ngx_buf_special(cl->buf)) {
+ continue;
+ }
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if (send + size > limit) {
+ size = limit - send;
+ }
+
+ len += size;
+ prev = cl->buf->pos + size;
+ send += size;
+ cl = cl->next;
+ }
+
+ n = ngx_aio_write(c, buf, len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "aio_write: %z", n);
+
+ if (n == NGX_ERROR) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ if (n > 0) {
+ sent += n;
+ c->sent += n;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "aio_write sent: %O", c->sent);
+
+ for (cl = in; cl; cl = cl->next) {
+
+ if (sent >= cl->buf->last - cl->buf->pos) {
+ sent -= cl->buf->last - cl->buf->pos;
+ cl->buf->pos = cl->buf->last;
+
+ continue;
+ }
+
+ cl->buf->pos += sent;
+
+ break;
+ }
+ }
+
+ return cl;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_alloc.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_alloc.c
new file mode 100644
index 00000000000..5c2f7870256
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_alloc.c
@@ -0,0 +1,90 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+ngx_uint_t ngx_pagesize;
+ngx_uint_t ngx_pagesize_shift;
+ngx_uint_t ngx_cacheline_size;
+
+
+void *
+ngx_alloc(size_t size, ngx_log_t *log)
+{
+ void *p;
+
+ p = malloc(size);
+ if (p == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ "malloc(%uz) failed", size);
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, "malloc: %p:%uz", p, size);
+
+ return p;
+}
+
+
+void *
+ngx_calloc(size_t size, ngx_log_t *log)
+{
+ void *p;
+
+ p = ngx_alloc(size, log);
+
+ if (p) {
+ ngx_memzero(p, size);
+ }
+
+ return p;
+}
+
+
+#if (NGX_HAVE_POSIX_MEMALIGN)
+
+void *
+ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)
+{
+ void *p;
+ int err;
+
+ err = posix_memalign(&p, alignment, size);
+
+ if (err) {
+ ngx_log_error(NGX_LOG_EMERG, log, err,
+ "posix_memalign(%uz, %uz) failed", alignment, size);
+ p = NULL;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,
+ "posix_memalign: %p:%uz @%uz", p, size, alignment);
+
+ return p;
+}
+
+#elif (NGX_HAVE_MEMALIGN)
+
+void *
+ngx_memalign(size_t alignment, size_t size, ngx_log_t *log)
+{
+ void *p;
+
+ p = memalign(alignment, size);
+ if (p == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ "memalign(%uz, %uz) failed", alignment, size);
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,
+ "memalign: %p:%uz @%uz", p, size, alignment);
+
+ return p;
+}
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_alloc.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_alloc.h
new file mode 100644
index 00000000000..655db257f41
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_alloc.h
@@ -0,0 +1,45 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_ALLOC_H_INCLUDED_
+#define _NGX_ALLOC_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+void *ngx_alloc(size_t size, ngx_log_t *log);
+void *ngx_calloc(size_t size, ngx_log_t *log);
+
+#define ngx_free free
+
+
+/*
+ * Linux has memalign() or posix_memalign()
+ * Solaris has memalign()
+ * FreeBSD 7.0 has posix_memalign(), besides, early version's malloc()
+ * aligns allocations bigger than page size at the page boundary
+ */
+
+#if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN)
+
+void *ngx_memalign(size_t alignment, size_t size, ngx_log_t *log);
+
+#else
+
+#define ngx_memalign(alignment, size, log) ngx_alloc(size, log)
+
+#endif
+
+
+extern ngx_uint_t ngx_pagesize;
+extern ngx_uint_t ngx_pagesize_shift;
+extern ngx_uint_t ngx_cacheline_size;
+
+
+#endif /* _NGX_ALLOC_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_atomic.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_atomic.h
new file mode 100644
index 00000000000..417cd86ff29
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_atomic.h
@@ -0,0 +1,313 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_ATOMIC_H_INCLUDED_
+#define _NGX_ATOMIC_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#if (NGX_HAVE_LIBATOMIC)
+
+#define AO_REQUIRE_CAS
+#include <atomic_ops.h>
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+typedef long ngx_atomic_int_t;
+typedef AO_t ngx_atomic_uint_t;
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+
+#if (NGX_PTR_SIZE == 8)
+#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1)
+#else
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+#endif
+
+#define ngx_atomic_cmp_set(lock, old, new) \
+ AO_compare_and_swap(lock, old, new)
+#define ngx_atomic_fetch_add(value, add) \
+ AO_fetch_and_add(value, add)
+#define ngx_memory_barrier() AO_nop()
+#define ngx_cpu_pause()
+
+
+#elif (NGX_DARWIN_ATOMIC)
+
+/*
+ * use Darwin 8 atomic(3) and barrier(3) operations
+ * optimized at run-time for UP and SMP
+ */
+
+#include <libkern/OSAtomic.h>
+
+/* "bool" conflicts with perl's CORE/handy.h */
+#if 0
+#undef bool
+#endif
+
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+#if (NGX_PTR_SIZE == 8)
+
+typedef int64_t ngx_atomic_int_t;
+typedef uint64_t ngx_atomic_uint_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1)
+
+#define ngx_atomic_cmp_set(lock, old, new) \
+ OSAtomicCompareAndSwap64Barrier(old, new, (int64_t *) lock)
+
+#define ngx_atomic_fetch_add(value, add) \
+ (OSAtomicAdd64(add, (int64_t *) value) - add)
+
+#else
+
+typedef int32_t ngx_atomic_int_t;
+typedef uint32_t ngx_atomic_uint_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+
+#define ngx_atomic_cmp_set(lock, old, new) \
+ OSAtomicCompareAndSwap32Barrier(old, new, (int32_t *) lock)
+
+#define ngx_atomic_fetch_add(value, add) \
+ (OSAtomicAdd32(add, (int32_t *) value) - add)
+
+#endif
+
+#define ngx_memory_barrier() OSMemoryBarrier()
+
+#define ngx_cpu_pause()
+
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+
+
+#elif (NGX_HAVE_GCC_ATOMIC)
+
+/* GCC 4.1 builtin atomic operations */
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+typedef long ngx_atomic_int_t;
+typedef unsigned long ngx_atomic_uint_t;
+
+#if (NGX_PTR_SIZE == 8)
+#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1)
+#else
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+#endif
+
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+
+
+#define ngx_atomic_cmp_set(lock, old, set) \
+ __sync_bool_compare_and_swap(lock, old, set)
+
+#define ngx_atomic_fetch_add(value, add) \
+ __sync_fetch_and_add(value, add)
+
+#define ngx_memory_barrier() __sync_synchronize()
+
+#if ( __i386__ || __i386 || __amd64__ || __amd64 )
+#define ngx_cpu_pause() __asm__ ("pause")
+#else
+#define ngx_cpu_pause()
+#endif
+
+
+#elif ( __i386__ || __i386 )
+
+typedef int32_t ngx_atomic_int_t;
+typedef uint32_t ngx_atomic_uint_t;
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+
+
+#if ( __SUNPRO_C )
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+ngx_atomic_uint_t
+ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
+ ngx_atomic_uint_t set);
+
+ngx_atomic_int_t
+ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add);
+
+/*
+ * Sun Studio 12 exits with segmentation fault on '__asm ("pause")',
+ * so ngx_cpu_pause is declared in src/os/unix/ngx_sunpro_x86.il
+ */
+
+void
+ngx_cpu_pause(void);
+
+/* the code in src/os/unix/ngx_sunpro_x86.il */
+
+#define ngx_memory_barrier() __asm (".volatile"); __asm (".nonvolatile")
+
+
+#else /* ( __GNUC__ || __INTEL_COMPILER ) */
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+#include "ngx_gcc_atomic_x86.h"
+
+#endif
+
+
+#elif ( __amd64__ || __amd64 )
+
+typedef int64_t ngx_atomic_int_t;
+typedef uint64_t ngx_atomic_uint_t;
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1)
+
+
+#if ( __SUNPRO_C )
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+ngx_atomic_uint_t
+ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
+ ngx_atomic_uint_t set);
+
+ngx_atomic_int_t
+ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add);
+
+/*
+ * Sun Studio 12 exits with segmentation fault on '__asm ("pause")',
+ * so ngx_cpu_pause is declared in src/os/unix/ngx_sunpro_amd64.il
+ */
+
+void
+ngx_cpu_pause(void);
+
+/* the code in src/os/unix/ngx_sunpro_amd64.il */
+
+#define ngx_memory_barrier() __asm (".volatile"); __asm (".nonvolatile")
+
+
+#else /* ( __GNUC__ || __INTEL_COMPILER ) */
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+#include "ngx_gcc_atomic_amd64.h"
+
+#endif
+
+
+#elif ( __sparc__ || __sparc || __sparcv9 )
+
+#if (NGX_PTR_SIZE == 8)
+
+typedef int64_t ngx_atomic_int_t;
+typedef uint64_t ngx_atomic_uint_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1)
+
+#else
+
+typedef int32_t ngx_atomic_int_t;
+typedef uint32_t ngx_atomic_uint_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+
+#endif
+
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+
+
+#if ( __SUNPRO_C )
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+#include "ngx_sunpro_atomic_sparc64.h"
+
+
+#else /* ( __GNUC__ || __INTEL_COMPILER ) */
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+#include "ngx_gcc_atomic_sparc64.h"
+
+#endif
+
+
+#elif ( __powerpc__ || __POWERPC__ )
+
+#define NGX_HAVE_ATOMIC_OPS 1
+
+#if (NGX_PTR_SIZE == 8)
+
+typedef int64_t ngx_atomic_int_t;
+typedef uint64_t ngx_atomic_uint_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-9223372036854775808") - 1)
+
+#else
+
+typedef int32_t ngx_atomic_int_t;
+typedef uint32_t ngx_atomic_uint_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+
+#endif
+
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+
+
+#include "ngx_gcc_atomic_ppc.h"
+
+#endif
+
+
+#if !(NGX_HAVE_ATOMIC_OPS)
+
+#define NGX_HAVE_ATOMIC_OPS 0
+
+typedef int32_t ngx_atomic_int_t;
+typedef uint32_t ngx_atomic_uint_t;
+typedef volatile ngx_atomic_uint_t ngx_atomic_t;
+#define NGX_ATOMIC_T_LEN (sizeof("-2147483648") - 1)
+
+
+static ngx_inline ngx_atomic_uint_t
+ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
+ ngx_atomic_uint_t set)
+{
+ if (*lock == old) {
+ *lock = set;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static ngx_inline ngx_atomic_int_t
+ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)
+{
+ ngx_atomic_int_t old;
+
+ old = *value;
+ *value += add;
+
+ return old;
+}
+
+#define ngx_memory_barrier()
+#define ngx_cpu_pause()
+
+#endif
+
+
+void ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin);
+
+#define ngx_trylock(lock) (*(lock) == 0 && ngx_atomic_cmp_set(lock, 0, 1))
+#define ngx_unlock(lock) *(lock) = 0
+
+
+#endif /* _NGX_ATOMIC_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_channel.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_channel.c
new file mode 100644
index 00000000000..8e9069660f4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_channel.c
@@ -0,0 +1,260 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_channel.h>
+
+
+ngx_int_t
+ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,
+ ngx_log_t *log)
+{
+ ssize_t n;
+ ngx_err_t err;
+ struct iovec iov[1];
+ struct msghdr msg;
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+
+ union {
+ struct cmsghdr cm;
+ char space[CMSG_SPACE(sizeof(int))];
+ } cmsg;
+
+ if (ch->fd == -1) {
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+
+ } else {
+ msg.msg_control = (caddr_t) &cmsg;
+ msg.msg_controllen = sizeof(cmsg);
+
+ ngx_memzero(&cmsg, sizeof(cmsg));
+
+ cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg.cm.cmsg_level = SOL_SOCKET;
+ cmsg.cm.cmsg_type = SCM_RIGHTS;
+
+ /*
+ * We have to use ngx_memcpy() instead of simple
+ * *(int *) CMSG_DATA(&cmsg.cm) = ch->fd;
+ * because some gcc 4.4 with -O2/3/s optimization issues the warning:
+ * dereferencing type-punned pointer will break strict-aliasing rules
+ *
+ * Fortunately, gcc with -O1 compiles this ngx_memcpy()
+ * in the same simple assignment as in the code above
+ */
+
+ ngx_memcpy(CMSG_DATA(&cmsg.cm), &ch->fd, sizeof(int));
+ }
+
+ msg.msg_flags = 0;
+
+#else
+
+ if (ch->fd == -1) {
+ msg.msg_accrights = NULL;
+ msg.msg_accrightslen = 0;
+
+ } else {
+ msg.msg_accrights = (caddr_t) &ch->fd;
+ msg.msg_accrightslen = sizeof(int);
+ }
+
+#endif
+
+ iov[0].iov_base = (char *) ch;
+ iov[0].iov_len = size;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ n = sendmsg(s, &msg, 0);
+
+ if (n == -1) {
+ err = ngx_errno;
+ if (err == NGX_EAGAIN) {
+ return NGX_AGAIN;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, log, err, "sendmsg() failed");
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_read_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size, ngx_log_t *log)
+{
+ ssize_t n;
+ ngx_err_t err;
+ struct iovec iov[1];
+ struct msghdr msg;
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+ union {
+ struct cmsghdr cm;
+ char space[CMSG_SPACE(sizeof(int))];
+ } cmsg;
+#else
+ int fd;
+#endif
+
+ iov[0].iov_base = (char *) ch;
+ iov[0].iov_len = size;
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+ msg.msg_control = (caddr_t) &cmsg;
+ msg.msg_controllen = sizeof(cmsg);
+#else
+ msg.msg_accrights = (caddr_t) &fd;
+ msg.msg_accrightslen = sizeof(int);
+#endif
+
+ n = recvmsg(s, &msg, 0);
+
+ if (n == -1) {
+ err = ngx_errno;
+ if (err == NGX_EAGAIN) {
+ return NGX_AGAIN;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, log, err, "recvmsg() failed");
+ return NGX_ERROR;
+ }
+
+ if (n == 0) {
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "recvmsg() returned zero");
+ return NGX_ERROR;
+ }
+
+ if ((size_t) n < sizeof(ngx_channel_t)) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "recvmsg() returned not enough data: %z", n);
+ return NGX_ERROR;
+ }
+
+#if (NGX_HAVE_MSGHDR_MSG_CONTROL)
+
+ if (ch->command == NGX_CMD_OPEN_CHANNEL) {
+
+ if (cmsg.cm.cmsg_len < (socklen_t) CMSG_LEN(sizeof(int))) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "recvmsg() returned too small ancillary data");
+ return NGX_ERROR;
+ }
+
+ if (cmsg.cm.cmsg_level != SOL_SOCKET || cmsg.cm.cmsg_type != SCM_RIGHTS)
+ {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "recvmsg() returned invalid ancillary data "
+ "level %d or type %d",
+ cmsg.cm.cmsg_level, cmsg.cm.cmsg_type);
+ return NGX_ERROR;
+ }
+
+ /* ch->fd = *(int *) CMSG_DATA(&cmsg.cm); */
+
+ ngx_memcpy(&ch->fd, CMSG_DATA(&cmsg.cm), sizeof(int));
+ }
+
+ if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "recvmsg() truncated data");
+ }
+
+#else
+
+ if (ch->command == NGX_CMD_OPEN_CHANNEL) {
+ if (msg.msg_accrightslen != sizeof(int)) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "recvmsg() returned no ancillary data");
+ return NGX_ERROR;
+ }
+
+ ch->fd = fd;
+ }
+
+#endif
+
+ return n;
+}
+
+
+ngx_int_t
+ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd, ngx_int_t event,
+ ngx_event_handler_pt handler)
+{
+ ngx_event_t *ev, *rev, *wev;
+ ngx_connection_t *c;
+
+ c = ngx_get_connection(fd, cycle->log);
+
+ if (c == NULL) {
+ return NGX_ERROR;
+ }
+
+ c->pool = cycle->pool;
+
+ rev = c->read;
+ wev = c->write;
+
+ rev->log = cycle->log;
+ wev->log = cycle->log;
+
+#if (NGX_THREADS)
+ rev->lock = &c->lock;
+ wev->lock = &c->lock;
+ rev->own_lock = &c->lock;
+ wev->own_lock = &c->lock;
+#endif
+
+ rev->channel = 1;
+ wev->channel = 1;
+
+ ev = (event == NGX_READ_EVENT) ? rev : wev;
+
+ ev->handler = handler;
+
+ if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {
+ if (ngx_add_conn(c) == NGX_ERROR) {
+ ngx_free_connection(c);
+ return NGX_ERROR;
+ }
+
+ } else {
+ if (ngx_add_event(ev, event, 0) == NGX_ERROR) {
+ ngx_free_connection(c);
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_close_channel(ngx_fd_t *fd, ngx_log_t *log)
+{
+ if (close(fd[0]) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "close() channel failed");
+ }
+
+ if (close(fd[1]) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "close() channel failed");
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_channel.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_channel.h
new file mode 100644
index 00000000000..d7a9f6b54c5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_channel.h
@@ -0,0 +1,34 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_CHANNEL_H_INCLUDED_
+#define _NGX_CHANNEL_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+typedef struct {
+ ngx_uint_t command;
+ ngx_pid_t pid;
+ ngx_int_t slot;
+ ngx_fd_t fd;
+} ngx_channel_t;
+
+
+ngx_int_t ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,
+ ngx_log_t *log);
+ngx_int_t ngx_read_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,
+ ngx_log_t *log);
+ngx_int_t ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd,
+ ngx_int_t event, ngx_event_handler_pt handler);
+void ngx_close_channel(ngx_fd_t *fd, ngx_log_t *log);
+
+
+#endif /* _NGX_CHANNEL_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_daemon.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_daemon.c
new file mode 100644
index 00000000000..ab672110838
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_daemon.c
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+ngx_int_t
+ngx_daemon(ngx_log_t *log)
+{
+ int fd;
+
+ switch (fork()) {
+ case -1:
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed");
+ return NGX_ERROR;
+
+ case 0:
+ break;
+
+ default:
+ exit(0);
+ }
+
+ ngx_pid = ngx_getpid();
+
+ if (setsid() == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "setsid() failed");
+ return NGX_ERROR;
+ }
+
+ umask(0);
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ "open(\"/dev/null\") failed");
+ return NGX_ERROR;
+ }
+
+ if (dup2(fd, STDIN_FILENO) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed");
+ return NGX_ERROR;
+ }
+
+ if (dup2(fd, STDOUT_FILENO) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed");
+ return NGX_ERROR;
+ }
+
+#if 0
+ if (dup2(fd, STDERR_FILENO) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDERR) failed");
+ return NGX_ERROR;
+ }
+#endif
+
+ if (fd > STDERR_FILENO) {
+ if (close(fd) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin.h
new file mode 100644
index 00000000000..4d01b26ea54
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin.h
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_DARWIN_H_INCLUDED_
+#define _NGX_DARWIN_H_INCLUDED_
+
+
+void ngx_debug_init(void);
+ngx_chain_t *ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,
+ off_t limit);
+
+extern int ngx_darwin_kern_osreldate;
+extern int ngx_darwin_hw_ncpu;
+extern u_long ngx_darwin_net_inet_tcp_sendspace;
+
+extern ngx_uint_t ngx_debug_malloc;
+
+
+#endif /* _NGX_DARWIN_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin_config.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin_config.h
new file mode 100644
index 00000000000..bbad977cb6c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin_config.h
@@ -0,0 +1,95 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_DARWIN_CONFIG_H_INCLUDED_
+#define _NGX_DARWIN_CONFIG_H_INCLUDED_
+
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stddef.h> /* offsetof() */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+#include <glob.h>
+#include <sys/mount.h> /* statfs() */
+
+#include <sys/filio.h> /* FIONBIO */
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h> /* TCP_NODELAY */
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/un.h>
+
+#include <sys/sysctl.h>
+#include <xlocale.h>
+
+
+#ifndef IOV_MAX
+#define IOV_MAX 64
+#endif
+
+
+#include <ngx_auto_config.h>
+
+
+#if (NGX_HAVE_POSIX_SEM)
+#include <semaphore.h>
+#endif
+
+
+#if (NGX_HAVE_POLL)
+#include <poll.h>
+#endif
+
+
+#if (NGX_HAVE_KQUEUE)
+#include <sys/event.h>
+#endif
+
+
+#define NGX_LISTEN_BACKLOG -1
+
+
+#ifndef NGX_HAVE_INHERITED_NONBLOCK
+#define NGX_HAVE_INHERITED_NONBLOCK 1
+#endif
+
+
+#ifndef NGX_HAVE_CASELESS_FILESYSTEM
+#define NGX_HAVE_CASELESS_FILESYSTEM 1
+#endif
+
+
+#define NGX_HAVE_OS_SPECIFIC_INIT 1
+#define NGX_HAVE_DEBUG_MALLOC 1
+
+
+extern char **environ;
+
+
+#endif /* _NGX_DARWIN_CONFIG_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin_init.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin_init.c
new file mode 100644
index 00000000000..1bc7520cade
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin_init.c
@@ -0,0 +1,196 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+char ngx_darwin_kern_ostype[16];
+char ngx_darwin_kern_osrelease[128];
+int ngx_darwin_hw_ncpu;
+int ngx_darwin_kern_ipc_somaxconn;
+u_long ngx_darwin_net_inet_tcp_sendspace;
+
+ngx_uint_t ngx_debug_malloc;
+
+
+static ngx_os_io_t ngx_darwin_io = {
+ ngx_unix_recv,
+ ngx_readv_chain,
+ ngx_udp_unix_recv,
+ ngx_unix_send,
+#if (NGX_HAVE_SENDFILE)
+ ngx_darwin_sendfile_chain,
+ NGX_IO_SENDFILE
+#else
+ ngx_writev_chain,
+ 0
+#endif
+};
+
+
+typedef struct {
+ char *name;
+ void *value;
+ size_t size;
+ ngx_uint_t exists;
+} sysctl_t;
+
+
+sysctl_t sysctls[] = {
+ { "hw.ncpu",
+ &ngx_darwin_hw_ncpu,
+ sizeof(ngx_darwin_hw_ncpu), 0 },
+
+ { "net.inet.tcp.sendspace",
+ &ngx_darwin_net_inet_tcp_sendspace,
+ sizeof(ngx_darwin_net_inet_tcp_sendspace), 0 },
+
+ { "kern.ipc.somaxconn",
+ &ngx_darwin_kern_ipc_somaxconn,
+ sizeof(ngx_darwin_kern_ipc_somaxconn), 0 },
+
+ { NULL, NULL, 0, 0 }
+};
+
+
+void
+ngx_debug_init(void)
+{
+#if (NGX_DEBUG_MALLOC)
+
+ /*
+ * MacOSX 10.6, 10.7: MallocScribble fills freed memory with 0x55
+ * and fills allocated memory with 0xAA.
+ * MacOSX 10.4, 10.5: MallocScribble fills freed memory with 0x55,
+ * MallocPreScribble fills allocated memory with 0xAA.
+ * MacOSX 10.3: MallocScribble fills freed memory with 0x55,
+ * and no way to fill allocated memory.
+ */
+
+ setenv("MallocScribble", "1", 0);
+
+ ngx_debug_malloc = 1;
+
+#else
+
+ if (getenv("MallocScribble")) {
+ ngx_debug_malloc = 1;
+ }
+
+#endif
+}
+
+
+ngx_int_t
+ngx_os_specific_init(ngx_log_t *log)
+{
+ size_t size;
+ ngx_err_t err;
+ ngx_uint_t i;
+
+ size = sizeof(ngx_darwin_kern_ostype);
+ if (sysctlbyname("kern.ostype", ngx_darwin_kern_ostype, &size, NULL, 0)
+ == -1)
+ {
+ err = ngx_errno;
+
+ if (err != NGX_ENOENT) {
+
+ ngx_log_error(NGX_LOG_ALERT, log, err,
+ "sysctlbyname(kern.ostype) failed");
+
+ if (err != NGX_ENOMEM) {
+ return NGX_ERROR;
+ }
+
+ ngx_darwin_kern_ostype[size - 1] = '\0';
+ }
+ }
+
+ size = sizeof(ngx_darwin_kern_osrelease);
+ if (sysctlbyname("kern.osrelease", ngx_darwin_kern_osrelease, &size,
+ NULL, 0)
+ == -1)
+ {
+ err = ngx_errno;
+
+ if (err != NGX_ENOENT) {
+
+ ngx_log_error(NGX_LOG_ALERT, log, err,
+ "sysctlbyname(kern.osrelease) failed");
+
+ if (err != NGX_ENOMEM) {
+ return NGX_ERROR;
+ }
+
+ ngx_darwin_kern_osrelease[size - 1] = '\0';
+ }
+ }
+
+ for (i = 0; sysctls[i].name; i++) {
+ size = sysctls[i].size;
+
+ if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0)
+ == 0)
+ {
+ sysctls[i].exists = 1;
+ continue;
+ }
+
+ err = ngx_errno;
+
+ if (err == NGX_ENOENT) {
+ continue;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, log, err,
+ "sysctlbyname(%s) failed", sysctls[i].name);
+ return NGX_ERROR;
+ }
+
+ ngx_ncpu = ngx_darwin_hw_ncpu;
+
+ if (ngx_darwin_kern_ipc_somaxconn > 32767) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "sysctl kern.ipc.somaxconn must be less than 32768");
+ return NGX_ERROR;
+ }
+
+ ngx_tcp_nodelay_and_tcp_nopush = 1;
+
+ ngx_os_io = ngx_darwin_io;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_os_specific_status(ngx_log_t *log)
+{
+ u_long value;
+ ngx_uint_t i;
+
+ if (ngx_darwin_kern_ostype[0]) {
+ ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
+ ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease);
+ }
+
+ for (i = 0; sysctls[i].name; i++) {
+ if (sysctls[i].exists) {
+ if (sysctls[i].size == sizeof(long)) {
+ value = *(long *) sysctls[i].value;
+
+ } else {
+ value = *(int *) sysctls[i].value;
+ }
+
+ ngx_log_error(NGX_LOG_NOTICE, log, 0, "%s: %l",
+ sysctls[i].name, value);
+ }
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin_sendfile_chain.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin_sendfile_chain.c
new file mode 100644
index 00000000000..ee44e1d58e1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_darwin_sendfile_chain.c
@@ -0,0 +1,370 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+/*
+ * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same
+ * old bug as early FreeBSD sendfile() syscall:
+ * http://bugs.freebsd.org/33771
+ *
+ * Besides sendfile() has another bug: if one calls sendfile()
+ * with both a header and a trailer, then sendfile() ignores a file part
+ * at all and sends only the header and the trailer together.
+ * For this reason we send a trailer only if there is no a header.
+ *
+ * Although sendfile() allows to pass a header or a trailer,
+ * it may send the header or the trailer and a part of the file
+ * in different packets. And FreeBSD workaround (TCP_NOPUSH option)
+ * does not help.
+ */
+
+
+#if (IOV_MAX > 64)
+#define NGX_HEADERS 64
+#define NGX_TRAILERS 64
+#else
+#define NGX_HEADERS IOV_MAX
+#define NGX_TRAILERS IOV_MAX
+#endif
+
+
+ngx_chain_t *
+ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
+{
+ int rc;
+ u_char *prev;
+ off_t size, send, prev_send, aligned, sent, fprev;
+ off_t header_size, file_size;
+ ngx_uint_t eintr, complete;
+ ngx_err_t err;
+ ngx_buf_t *file;
+ ngx_array_t header, trailer;
+ ngx_event_t *wev;
+ ngx_chain_t *cl;
+ struct sf_hdtr hdtr;
+ struct iovec *iov, headers[NGX_HEADERS], trailers[NGX_TRAILERS];
+
+ wev = c->write;
+
+ if (!wev->ready) {
+ return in;
+ }
+
+#if (NGX_HAVE_KQUEUE)
+
+ if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
+ (void) ngx_connection_error(c, wev->kq_errno,
+ "kevent() reported about an closed connection");
+ wev->error = 1;
+ return NGX_CHAIN_ERROR;
+ }
+
+#endif
+
+ /* the maximum limit size is the maximum size_t value - the page size */
+
+ if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
+ limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
+ }
+
+ send = 0;
+
+ header.elts = headers;
+ header.size = sizeof(struct iovec);
+ header.nalloc = NGX_HEADERS;
+ header.pool = c->pool;
+
+ trailer.elts = trailers;
+ trailer.size = sizeof(struct iovec);
+ trailer.nalloc = NGX_TRAILERS;
+ trailer.pool = c->pool;
+
+ for ( ;; ) {
+ file = NULL;
+ file_size = 0;
+ header_size = 0;
+ eintr = 0;
+ complete = 0;
+ prev_send = send;
+
+ header.nelts = 0;
+ trailer.nelts = 0;
+
+ /* create the header iovec and coalesce the neighbouring bufs */
+
+ prev = NULL;
+ iov = NULL;
+
+ for (cl = in; cl && send < limit; cl = cl->next) {
+
+ if (ngx_buf_special(cl->buf)) {
+ continue;
+ }
+
+ if (!ngx_buf_in_memory_only(cl->buf)) {
+ break;
+ }
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if (send + size > limit) {
+ size = limit - send;
+ }
+
+ if (prev == cl->buf->pos) {
+ iov->iov_len += (size_t) size;
+
+ } else {
+ if (header.nelts >= IOV_MAX) {
+ break;
+ }
+
+ iov = ngx_array_push(&header);
+ if (iov == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ iov->iov_base = (void *) cl->buf->pos;
+ iov->iov_len = (size_t) size;
+ }
+
+ prev = cl->buf->pos + (size_t) size;
+ header_size += size;
+ send += size;
+ }
+
+
+ if (cl && cl->buf->in_file && send < limit) {
+ file = cl->buf;
+
+ /* coalesce the neighbouring file bufs */
+
+ do {
+ size = cl->buf->file_last - cl->buf->file_pos;
+
+ if (send + size > limit) {
+ size = limit - send;
+
+ aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
+ & ~((off_t) ngx_pagesize - 1);
+
+ if (aligned <= cl->buf->file_last) {
+ size = aligned - cl->buf->file_pos;
+ }
+ }
+
+ file_size += size;
+ send += size;
+ fprev = cl->buf->file_pos + size;
+ cl = cl->next;
+
+ } while (cl
+ && cl->buf->in_file
+ && send < limit
+ && file->file->fd == cl->buf->file->fd
+ && fprev == cl->buf->file_pos);
+ }
+
+ if (file && header.nelts == 0) {
+
+ /* create the trailer iovec and coalesce the neighbouring bufs */
+
+ prev = NULL;
+ iov = NULL;
+
+ while (cl && send < limit) {
+
+ if (ngx_buf_special(cl->buf)) {
+ cl = cl->next;
+ continue;
+ }
+
+ if (!ngx_buf_in_memory_only(cl->buf)) {
+ break;
+ }
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if (send + size > limit) {
+ size = limit - send;
+ }
+
+ if (prev == cl->buf->pos) {
+ iov->iov_len += (size_t) size;
+
+ } else {
+ if (trailer.nelts >= IOV_MAX) {
+ break;
+ }
+
+ iov = ngx_array_push(&trailer);
+ if (iov == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ iov->iov_base = (void *) cl->buf->pos;
+ iov->iov_len = (size_t) size;
+ }
+
+ prev = cl->buf->pos + (size_t) size;
+ send += size;
+ cl = cl->next;
+ }
+ }
+
+ if (file) {
+
+ /*
+ * sendfile() returns EINVAL if sf_hdtr's count is 0,
+ * but corresponding pointer is not NULL
+ */
+
+ hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL;
+ hdtr.hdr_cnt = header.nelts;
+ hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL;
+ hdtr.trl_cnt = trailer.nelts;
+
+ sent = header_size + file_size;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "sendfile: @%O %O h:%O",
+ file->file_pos, sent, header_size);
+
+ rc = sendfile(file->file->fd, c->fd, file->file_pos,
+ &sent, &hdtr, 0);
+
+ if (rc == -1) {
+ err = ngx_errno;
+
+ switch (err) {
+ case NGX_EAGAIN:
+ break;
+
+ case NGX_EINTR:
+ eintr = 1;
+ break;
+
+ default:
+ wev->error = 1;
+ (void) ngx_connection_error(c, err, "sendfile() failed");
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "sendfile() sent only %O bytes", sent);
+ }
+
+ if (rc == 0 && sent == 0) {
+
+ /*
+ * if rc and sent equal to zero, then someone
+ * has truncated the file, so the offset became beyond
+ * the end of the file
+ */
+
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "sendfile() reported that \"%s\" was truncated",
+ file->file->name.data);
+
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "sendfile: %d, @%O %O:%O",
+ rc, file->file_pos, sent, file_size + header_size);
+
+ } else {
+ rc = writev(c->fd, header.elts, header.nelts);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "writev: %d of %uz", rc, send);
+
+ if (rc == -1) {
+ err = ngx_errno;
+
+ switch (err) {
+ case NGX_EAGAIN:
+ break;
+
+ case NGX_EINTR:
+ eintr = 1;
+ break;
+
+ default:
+ wev->error = 1;
+ ngx_connection_error(c, err, "writev() failed");
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "writev() not ready");
+ }
+
+ sent = rc > 0 ? rc : 0;
+ }
+
+ if (send - prev_send == sent) {
+ complete = 1;
+ }
+
+ c->sent += sent;
+
+ for ( /* void */ ; in; in = in->next) {
+
+ if (ngx_buf_special(in->buf)) {
+ continue;
+ }
+
+ if (sent == 0) {
+ break;
+ }
+
+ size = ngx_buf_size(in->buf);
+
+ if (sent >= size) {
+ sent -= size;
+
+ if (ngx_buf_in_memory(in->buf)) {
+ in->buf->pos = in->buf->last;
+ }
+
+ if (in->buf->in_file) {
+ in->buf->file_pos = in->buf->file_last;
+ }
+
+ continue;
+ }
+
+ if (ngx_buf_in_memory(in->buf)) {
+ in->buf->pos += (size_t) sent;
+ }
+
+ if (in->buf->in_file) {
+ in->buf->file_pos += sent;
+ }
+
+ break;
+ }
+
+ if (eintr) {
+ continue;
+ }
+
+ if (!complete) {
+ wev->ready = 0;
+ return in;
+ }
+
+ if (send >= limit || in == NULL) {
+ return in;
+ }
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_errno.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_errno.c
new file mode 100644
index 00000000000..e787b2377ed
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_errno.c
@@ -0,0 +1,87 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * The strerror() messages are copied because:
+ *
+ * 1) strerror() and strerror_r() functions are not Async-Signal-Safe,
+ * therefore, they cannot be used in signal handlers;
+ *
+ * 2) a direct sys_errlist[] array may be used instead of these functions,
+ * but Linux linker warns about its usage:
+ *
+ * warning: `sys_errlist' is deprecated; use `strerror' or `strerror_r' instead
+ * warning: `sys_nerr' is deprecated; use `strerror' or `strerror_r' instead
+ *
+ * causing false bug reports.
+ */
+
+
+static ngx_str_t *ngx_sys_errlist;
+static ngx_str_t ngx_unknown_error = ngx_string("Unknown error");
+
+
+u_char *
+ngx_strerror(ngx_err_t err, u_char *errstr, size_t size)
+{
+ ngx_str_t *msg;
+
+ msg = ((ngx_uint_t) err < NGX_SYS_NERR) ? &ngx_sys_errlist[err]:
+ &ngx_unknown_error;
+ size = ngx_min(size, msg->len);
+
+ return ngx_cpymem(errstr, msg->data, size);
+}
+
+
+ngx_int_t
+ngx_strerror_init(void)
+{
+ char *msg;
+ u_char *p;
+ size_t len;
+ ngx_err_t err;
+
+ /*
+ * ngx_strerror() is not ready to work at this stage, therefore,
+ * malloc() is used and possible errors are logged using strerror().
+ */
+
+ len = NGX_SYS_NERR * sizeof(ngx_str_t);
+
+ ngx_sys_errlist = malloc(len);
+ if (ngx_sys_errlist == NULL) {
+ goto failed;
+ }
+
+ for (err = 0; err < NGX_SYS_NERR; err++) {
+ msg = strerror(err);
+ len = ngx_strlen(msg);
+
+ p = malloc(len);
+ if (p == NULL) {
+ goto failed;
+ }
+
+ ngx_memcpy(p, msg, len);
+ ngx_sys_errlist[err].len = len;
+ ngx_sys_errlist[err].data = p;
+ }
+
+ return NGX_OK;
+
+failed:
+
+ err = errno;
+ ngx_log_stderr(0, "malloc(%uz) failed (%d: %s)", len, err, strerror(err));
+
+ return NGX_ERROR;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_errno.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_errno.h
new file mode 100644
index 00000000000..16cafda3107
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_errno.h
@@ -0,0 +1,78 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_ERRNO_H_INCLUDED_
+#define _NGX_ERRNO_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef int ngx_err_t;
+
+#define NGX_EPERM EPERM
+#define NGX_ENOENT ENOENT
+#define NGX_ENOPATH ENOENT
+#define NGX_ESRCH ESRCH
+#define NGX_EINTR EINTR
+#define NGX_ECHILD ECHILD
+#define NGX_ENOMEM ENOMEM
+#define NGX_EACCES EACCES
+#define NGX_EBUSY EBUSY
+#define NGX_EEXIST EEXIST
+#define NGX_EXDEV EXDEV
+#define NGX_ENOTDIR ENOTDIR
+#define NGX_EISDIR EISDIR
+#define NGX_EINVAL EINVAL
+#define NGX_ENFILE ENFILE
+#define NGX_EMFILE EMFILE
+#define NGX_ENOSPC ENOSPC
+#define NGX_EPIPE EPIPE
+#define NGX_EINPROGRESS EINPROGRESS
+#define NGX_ENOPROTOOPT ENOPROTOOPT
+#define NGX_EOPNOTSUPP EOPNOTSUPP
+#define NGX_EADDRINUSE EADDRINUSE
+#define NGX_ECONNABORTED ECONNABORTED
+#define NGX_ECONNRESET ECONNRESET
+#define NGX_ENOTCONN ENOTCONN
+#define NGX_ETIMEDOUT ETIMEDOUT
+#define NGX_ECONNREFUSED ECONNREFUSED
+#define NGX_ENAMETOOLONG ENAMETOOLONG
+#define NGX_ENETDOWN ENETDOWN
+#define NGX_ENETUNREACH ENETUNREACH
+#define NGX_EHOSTDOWN EHOSTDOWN
+#define NGX_EHOSTUNREACH EHOSTUNREACH
+#define NGX_ENOSYS ENOSYS
+#define NGX_ECANCELED ECANCELED
+#define NGX_EILSEQ EILSEQ
+#define NGX_ENOMOREFILES 0
+#define NGX_ELOOP ELOOP
+#define NGX_EBADF EBADF
+
+#if (NGX_HAVE_OPENAT)
+#define NGX_EMLINK EMLINK
+#endif
+
+#if (__hpux__)
+#define NGX_EAGAIN EWOULDBLOCK
+#else
+#define NGX_EAGAIN EAGAIN
+#endif
+
+
+#define ngx_errno errno
+#define ngx_socket_errno errno
+#define ngx_set_errno(err) errno = err
+#define ngx_set_socket_errno(err) errno = err
+
+
+u_char *ngx_strerror(ngx_err_t err, u_char *errstr, size_t size);
+ngx_int_t ngx_strerror_init(void);
+
+
+#endif /* _NGX_ERRNO_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_file_aio_read.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_file_aio_read.c
new file mode 100644
index 00000000000..0bb383de551
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_file_aio_read.c
@@ -0,0 +1,208 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+/*
+ * FreeBSD file AIO features and quirks:
+ *
+ * if an asked data are already in VM cache, then aio_error() returns 0,
+ * and the data are already copied in buffer;
+ *
+ * aio_read() preread in VM cache as minimum 16K (probably BKVASIZE);
+ * the first AIO preload may be up to 128K;
+ *
+ * aio_read/aio_error() may return EINPROGRESS for just written data;
+ *
+ * kqueue EVFILT_AIO filter is level triggered only: an event repeats
+ * until aio_return() will be called;
+ *
+ * aio_cancel() cannot cancel file AIO: it returns AIO_NOTCANCELED always.
+ */
+
+
+extern int ngx_kqueue;
+
+
+static ssize_t ngx_file_aio_result(ngx_file_t *file, ngx_event_aio_t *aio,
+ ngx_event_t *ev);
+static void ngx_file_aio_event_handler(ngx_event_t *ev);
+
+
+ssize_t
+ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,
+ ngx_pool_t *pool)
+{
+ int n;
+ ngx_event_t *ev;
+ ngx_event_aio_t *aio;
+
+ if (!ngx_file_aio) {
+ return ngx_read_file(file, buf, size, offset);
+ }
+
+ aio = file->aio;
+
+ if (aio == NULL) {
+ aio = ngx_pcalloc(pool, sizeof(ngx_event_aio_t));
+ if (aio == NULL) {
+ return NGX_ERROR;
+ }
+
+ aio->file = file;
+ aio->fd = file->fd;
+ aio->event.data = aio;
+ aio->event.ready = 1;
+ aio->event.log = file->log;
+#if (NGX_HAVE_AIO_SENDFILE)
+ aio->last_offset = -1;
+#endif
+ file->aio = aio;
+ }
+
+ ev = &aio->event;
+
+ if (!ev->ready) {
+ ngx_log_error(NGX_LOG_ALERT, file->log, 0,
+ "second aio post for \"%V\"", &file->name);
+ return NGX_AGAIN;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "aio complete:%d @%O:%z %V",
+ ev->complete, offset, size, &file->name);
+
+ if (ev->complete) {
+ ev->complete = 0;
+ ngx_set_errno(aio->err);
+
+ if (aio->err == 0) {
+ return aio->nbytes;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "aio read \"%s\" failed", file->name.data);
+
+ return NGX_ERROR;
+ }
+
+ ngx_memzero(&aio->aiocb, sizeof(struct aiocb));
+
+ aio->aiocb.aio_fildes = file->fd;
+ aio->aiocb.aio_offset = offset;
+ aio->aiocb.aio_buf = buf;
+ aio->aiocb.aio_nbytes = size;
+#if (NGX_HAVE_KQUEUE)
+ aio->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;
+ aio->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;
+ aio->aiocb.aio_sigevent.sigev_value.sigval_ptr = ev;
+#endif
+ ev->handler = ngx_file_aio_event_handler;
+
+ n = aio_read(&aio->aiocb);
+
+ if (n == -1) {
+ n = ngx_errno;
+
+ if (n == NGX_EAGAIN) {
+ return ngx_read_file(file, buf, size, offset);
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, file->log, n,
+ "aio_read(\"%V\") failed", &file->name);
+
+ if (n == NGX_ENOSYS) {
+ ngx_file_aio = 0;
+ return ngx_read_file(file, buf, size, offset);
+ }
+
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "aio_read: fd:%d %d", file->fd, n);
+
+ ev->active = 1;
+ ev->ready = 0;
+ ev->complete = 0;
+
+ return ngx_file_aio_result(aio->file, aio, ev);
+}
+
+
+static ssize_t
+ngx_file_aio_result(ngx_file_t *file, ngx_event_aio_t *aio, ngx_event_t *ev)
+{
+ int n;
+ ngx_err_t err;
+
+ n = aio_error(&aio->aiocb);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "aio_error: fd:%d %d", file->fd, n);
+
+ if (n == -1) {
+ err = ngx_errno;
+ aio->err = err;
+
+ ngx_log_error(NGX_LOG_ALERT, file->log, err,
+ "aio_error(\"%V\") failed", &file->name);
+ return NGX_ERROR;
+ }
+
+ if (n == NGX_EINPROGRESS) {
+ if (ev->ready) {
+ ev->ready = 0;
+ ngx_log_error(NGX_LOG_ALERT, file->log, n,
+ "aio_read(\"%V\") still in progress",
+ &file->name);
+ }
+
+ return NGX_AGAIN;
+ }
+
+ n = aio_return(&aio->aiocb);
+
+ if (n == -1) {
+ err = ngx_errno;
+ aio->err = err;
+ ev->ready = 1;
+
+ ngx_log_error(NGX_LOG_CRIT, file->log, err,
+ "aio_return(\"%V\") failed", &file->name);
+ return NGX_ERROR;
+ }
+
+ aio->err = 0;
+ aio->nbytes = n;
+ ev->ready = 1;
+ ev->active = 0;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "aio_return: fd:%d %d", file->fd, n);
+
+ return n;
+}
+
+
+static void
+ngx_file_aio_event_handler(ngx_event_t *ev)
+{
+ ngx_event_aio_t *aio;
+
+ aio = ev->data;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, ev->log, 0,
+ "aio event handler fd:%d %V", aio->fd, &aio->file->name);
+
+ if (ngx_file_aio_result(aio->file, aio, ev) != NGX_AGAIN) {
+ aio->handler(ev);
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_files.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_files.c
new file mode 100644
index 00000000000..c3ae47fdbf6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_files.c
@@ -0,0 +1,564 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#if (NGX_HAVE_FILE_AIO)
+
+ngx_uint_t ngx_file_aio = 1;
+
+#endif
+
+
+ssize_t
+ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset)
+{
+ ssize_t n;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "read: %d, %p, %uz, %O", file->fd, buf, size, offset);
+
+#if (NGX_HAVE_PREAD)
+
+ n = pread(file->fd, buf, size, offset);
+
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "pread() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+#else
+
+ if (file->sys_offset != offset) {
+ if (lseek(file->fd, offset, SEEK_SET) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "lseek() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ file->sys_offset = offset;
+ }
+
+ n = read(file->fd, buf, size);
+
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "read() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ file->sys_offset += n;
+
+#endif
+
+ file->offset += n;
+
+ return n;
+}
+
+
+ssize_t
+ngx_write_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset)
+{
+ ssize_t n, written;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "write: %d, %p, %uz, %O", file->fd, buf, size, offset);
+
+ written = 0;
+
+#if (NGX_HAVE_PWRITE)
+
+ for ( ;; ) {
+ n = pwrite(file->fd, buf + written, size, offset);
+
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "pwrite() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ file->offset += n;
+ written += n;
+
+ if ((size_t) n == size) {
+ return written;
+ }
+
+ offset += n;
+ size -= n;
+ }
+
+#else
+
+ if (file->sys_offset != offset) {
+ if (lseek(file->fd, offset, SEEK_SET) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "lseek() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ file->sys_offset = offset;
+ }
+
+ for ( ;; ) {
+ n = write(file->fd, buf + written, size);
+
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "write() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ file->offset += n;
+ written += n;
+
+ if ((size_t) n == size) {
+ return written;
+ }
+
+ size -= n;
+ }
+#endif
+}
+
+
+ngx_fd_t
+ngx_open_tempfile(u_char *name, ngx_uint_t persistent, ngx_uint_t access)
+{
+ ngx_fd_t fd;
+
+ fd = open((const char *) name, O_CREAT|O_EXCL|O_RDWR,
+ access ? access : 0600);
+
+ if (fd != -1 && !persistent) {
+ (void) unlink((const char *) name);
+ }
+
+ return fd;
+}
+
+
+#define NGX_IOVS 8
+
+ssize_t
+ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset,
+ ngx_pool_t *pool)
+{
+ u_char *prev;
+ size_t size;
+ ssize_t total, n;
+ ngx_array_t vec;
+ struct iovec *iov, iovs[NGX_IOVS];
+
+ /* use pwrite() if there is the only buf in a chain */
+
+ if (cl->next == NULL) {
+ return ngx_write_file(file, cl->buf->pos,
+ (size_t) (cl->buf->last - cl->buf->pos),
+ offset);
+ }
+
+ total = 0;
+
+ vec.elts = iovs;
+ vec.size = sizeof(struct iovec);
+ vec.nalloc = NGX_IOVS;
+ vec.pool = pool;
+
+ do {
+ prev = NULL;
+ iov = NULL;
+ size = 0;
+
+ vec.nelts = 0;
+
+ /* create the iovec and coalesce the neighbouring bufs */
+
+ while (cl && vec.nelts < IOV_MAX) {
+ if (prev == cl->buf->pos) {
+ iov->iov_len += cl->buf->last - cl->buf->pos;
+
+ } else {
+ iov = ngx_array_push(&vec);
+ if (iov == NULL) {
+ return NGX_ERROR;
+ }
+
+ iov->iov_base = (void *) cl->buf->pos;
+ iov->iov_len = cl->buf->last - cl->buf->pos;
+ }
+
+ size += cl->buf->last - cl->buf->pos;
+ prev = cl->buf->last;
+ cl = cl->next;
+ }
+
+ /* use pwrite() if there is the only iovec buffer */
+
+ if (vec.nelts == 1) {
+ iov = vec.elts;
+
+ n = ngx_write_file(file, (u_char *) iov[0].iov_base,
+ iov[0].iov_len, offset);
+
+ if (n == NGX_ERROR) {
+ return n;
+ }
+
+ return total + n;
+ }
+
+ if (file->sys_offset != offset) {
+ if (lseek(file->fd, offset, SEEK_SET) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "lseek() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ file->sys_offset = offset;
+ }
+
+ n = writev(file->fd, vec.elts, vec.nelts);
+
+ if (n == -1) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "writev() \"%s\" failed", file->name.data);
+ return NGX_ERROR;
+ }
+
+ if ((size_t) n != size) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, 0,
+ "writev() \"%s\" has written only %z of %uz",
+ file->name.data, n, size);
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "writev: %d, %z", file->fd, n);
+
+ file->sys_offset += n;
+ file->offset += n;
+ offset += n;
+ total += n;
+
+ } while (cl);
+
+ return total;
+}
+
+
+ngx_int_t
+ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s)
+{
+ struct timeval tv[2];
+
+ tv[0].tv_sec = ngx_time();
+ tv[0].tv_usec = 0;
+ tv[1].tv_sec = s;
+ tv[1].tv_usec = 0;
+
+ if (utimes((char *) name, tv) != -1) {
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_create_file_mapping(ngx_file_mapping_t *fm)
+{
+ fm->fd = ngx_open_file(fm->name, NGX_FILE_RDWR, NGX_FILE_TRUNCATE,
+ NGX_FILE_DEFAULT_ACCESS);
+ if (fm->fd == NGX_INVALID_FILE) {
+ ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,
+ ngx_open_file_n " \"%s\" failed", fm->name);
+ return NGX_ERROR;
+ }
+
+ if (ftruncate(fm->fd, fm->size) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,
+ "ftruncate() \"%s\" failed", fm->name);
+ goto failed;
+ }
+
+ fm->addr = mmap(NULL, fm->size, PROT_READ|PROT_WRITE, MAP_SHARED,
+ fm->fd, 0);
+ if (fm->addr != MAP_FAILED) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,
+ "mmap(%uz) \"%s\" failed", fm->size, fm->name);
+
+failed:
+
+ if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", fm->name);
+ }
+
+ return NGX_ERROR;
+}
+
+
+void
+ngx_close_file_mapping(ngx_file_mapping_t *fm)
+{
+ if (munmap(fm->addr, fm->size) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,
+ "munmap(%uz) \"%s\" failed", fm->size, fm->name);
+ }
+
+ if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", fm->name);
+ }
+}
+
+
+ngx_int_t
+ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir)
+{
+ dir->dir = opendir((const char *) name->data);
+
+ if (dir->dir == NULL) {
+ return NGX_ERROR;
+ }
+
+ dir->valid_info = 0;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_read_dir(ngx_dir_t *dir)
+{
+ dir->de = readdir(dir->dir);
+
+ if (dir->de) {
+#if (NGX_HAVE_D_TYPE)
+ dir->type = dir->de->d_type;
+#else
+ dir->type = 0;
+#endif
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_open_glob(ngx_glob_t *gl)
+{
+ int n;
+
+ n = glob((char *) gl->pattern, 0, NULL, &gl->pglob);
+
+ if (n == 0) {
+ return NGX_OK;
+ }
+
+#ifdef GLOB_NOMATCH
+
+ if (n == GLOB_NOMATCH && gl->test) {
+ return NGX_OK;
+ }
+
+#endif
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name)
+{
+ size_t count;
+
+#ifdef GLOB_NOMATCH
+ count = (size_t) gl->pglob.gl_pathc;
+#else
+ count = (size_t) gl->pglob.gl_matchc;
+#endif
+
+ if (gl->n < count) {
+
+ name->len = (size_t) ngx_strlen(gl->pglob.gl_pathv[gl->n]);
+ name->data = (u_char *) gl->pglob.gl_pathv[gl->n];
+ gl->n++;
+
+ return NGX_OK;
+ }
+
+ return NGX_DONE;
+}
+
+
+void
+ngx_close_glob(ngx_glob_t *gl)
+{
+ globfree(&gl->pglob);
+}
+
+
+ngx_err_t
+ngx_trylock_fd(ngx_fd_t fd)
+{
+ struct flock fl;
+
+ ngx_memzero(&fl, sizeof(struct flock));
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+
+ if (fcntl(fd, F_SETLK, &fl) == -1) {
+ return ngx_errno;
+ }
+
+ return 0;
+}
+
+
+ngx_err_t
+ngx_lock_fd(ngx_fd_t fd)
+{
+ struct flock fl;
+
+ ngx_memzero(&fl, sizeof(struct flock));
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+
+ if (fcntl(fd, F_SETLKW, &fl) == -1) {
+ return ngx_errno;
+ }
+
+ return 0;
+}
+
+
+ngx_err_t
+ngx_unlock_fd(ngx_fd_t fd)
+{
+ struct flock fl;
+
+ ngx_memzero(&fl, sizeof(struct flock));
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+
+ if (fcntl(fd, F_SETLK, &fl) == -1) {
+ return ngx_errno;
+ }
+
+ return 0;
+}
+
+
+#if (NGX_HAVE_POSIX_FADVISE) && !(NGX_HAVE_F_READAHEAD)
+
+ngx_int_t
+ngx_read_ahead(ngx_fd_t fd, size_t n)
+{
+ int err;
+
+ err = posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
+
+ if (err == 0) {
+ return 0;
+ }
+
+ ngx_set_errno(err);
+ return NGX_FILE_ERROR;
+}
+
+#endif
+
+
+#if (NGX_HAVE_O_DIRECT)
+
+ngx_int_t
+ngx_directio_on(ngx_fd_t fd)
+{
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+
+ if (flags == -1) {
+ return NGX_FILE_ERROR;
+ }
+
+ return fcntl(fd, F_SETFL, flags | O_DIRECT);
+}
+
+
+ngx_int_t
+ngx_directio_off(ngx_fd_t fd)
+{
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+
+ if (flags == -1) {
+ return NGX_FILE_ERROR;
+ }
+
+ return fcntl(fd, F_SETFL, flags & ~O_DIRECT);
+}
+
+#endif
+
+
+#if (NGX_HAVE_STATFS)
+
+size_t
+ngx_fs_bsize(u_char *name)
+{
+ struct statfs fs;
+
+ if (statfs((char *) name, &fs) == -1) {
+ return 512;
+ }
+
+ if ((fs.f_bsize % 512) != 0) {
+ return 512;
+ }
+
+ return (size_t) fs.f_bsize;
+}
+
+#elif (NGX_HAVE_STATVFS)
+
+size_t
+ngx_fs_bsize(u_char *name)
+{
+ struct statvfs fs;
+
+ if (statvfs((char *) name, &fs) == -1) {
+ return 512;
+ }
+
+ if ((fs.f_frsize % 512) != 0) {
+ return 512;
+ }
+
+ return (size_t) fs.f_frsize;
+}
+
+#else
+
+size_t
+ngx_fs_bsize(u_char *name)
+{
+ return 512;
+}
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_files.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_files.h
new file mode 100644
index 00000000000..a78ec961365
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_files.h
@@ -0,0 +1,386 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_FILES_H_INCLUDED_
+#define _NGX_FILES_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef int ngx_fd_t;
+typedef struct stat ngx_file_info_t;
+typedef ino_t ngx_file_uniq_t;
+
+
+typedef struct {
+ u_char *name;
+ size_t size;
+ void *addr;
+ ngx_fd_t fd;
+ ngx_log_t *log;
+} ngx_file_mapping_t;
+
+
+typedef struct {
+ DIR *dir;
+ struct dirent *de;
+ struct stat info;
+
+ unsigned type:8;
+ unsigned valid_info:1;
+} ngx_dir_t;
+
+
+typedef struct {
+ size_t n;
+ glob_t pglob;
+ u_char *pattern;
+ ngx_log_t *log;
+ ngx_uint_t test;
+} ngx_glob_t;
+
+
+#define NGX_INVALID_FILE -1
+#define NGX_FILE_ERROR -1
+
+
+
+#ifdef __CYGWIN__
+
+#ifndef NGX_HAVE_CASELESS_FILESYSTEM
+#define NGX_HAVE_CASELESS_FILESYSTEM 1
+#endif
+
+#define ngx_open_file(name, mode, create, access) \
+ open((const char *) name, mode|create|O_BINARY, access)
+
+#else
+
+#define ngx_open_file(name, mode, create, access) \
+ open((const char *) name, mode|create, access)
+
+#endif
+
+#define ngx_open_file_n "open()"
+
+#define NGX_FILE_RDONLY O_RDONLY
+#define NGX_FILE_WRONLY O_WRONLY
+#define NGX_FILE_RDWR O_RDWR
+#define NGX_FILE_CREATE_OR_OPEN O_CREAT
+#define NGX_FILE_OPEN 0
+#define NGX_FILE_TRUNCATE (O_CREAT|O_TRUNC)
+#define NGX_FILE_APPEND (O_WRONLY|O_APPEND)
+#define NGX_FILE_NONBLOCK O_NONBLOCK
+
+#if (NGX_HAVE_OPENAT)
+#define NGX_FILE_NOFOLLOW O_NOFOLLOW
+
+#if defined(O_DIRECTORY)
+#define NGX_FILE_DIRECTORY O_DIRECTORY
+#else
+#define NGX_FILE_DIRECTORY 0
+#endif
+
+#if defined(O_SEARCH)
+#define NGX_FILE_SEARCH (O_SEARCH|NGX_FILE_DIRECTORY)
+
+#elif defined(O_EXEC)
+#define NGX_FILE_SEARCH (O_EXEC|NGX_FILE_DIRECTORY)
+
+#elif (NGX_HAVE_O_PATH)
+#define NGX_FILE_SEARCH (O_PATH|O_RDONLY|NGX_FILE_DIRECTORY)
+
+#else
+#define NGX_FILE_SEARCH (O_RDONLY|NGX_FILE_DIRECTORY)
+#endif
+
+#endif /* NGX_HAVE_OPENAT */
+
+#define NGX_FILE_DEFAULT_ACCESS 0644
+#define NGX_FILE_OWNER_ACCESS 0600
+
+
+#define ngx_close_file close
+#define ngx_close_file_n "close()"
+
+
+#define ngx_delete_file(name) unlink((const char *) name)
+#define ngx_delete_file_n "unlink()"
+
+
+ngx_fd_t ngx_open_tempfile(u_char *name, ngx_uint_t persistent,
+ ngx_uint_t access);
+#define ngx_open_tempfile_n "open()"
+
+
+ssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset);
+#if (NGX_HAVE_PREAD)
+#define ngx_read_file_n "pread()"
+#else
+#define ngx_read_file_n "read()"
+#endif
+
+ssize_t ngx_write_file(ngx_file_t *file, u_char *buf, size_t size,
+ off_t offset);
+
+ssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce,
+ off_t offset, ngx_pool_t *pool);
+
+
+#define ngx_read_fd read
+#define ngx_read_fd_n "read()"
+
+/*
+ * we use inlined function instead of simple #define
+ * because glibc 2.3 sets warn_unused_result attribute for write()
+ * and in this case gcc 4.3 ignores (void) cast
+ */
+static ngx_inline ssize_t
+ngx_write_fd(ngx_fd_t fd, void *buf, size_t n)
+{
+ return write(fd, buf, n);
+}
+
+#define ngx_write_fd_n "write()"
+
+
+#define ngx_write_console ngx_write_fd
+
+
+#define ngx_linefeed(p) *p++ = LF;
+#define NGX_LINEFEED_SIZE 1
+#define NGX_LINEFEED "\x0a"
+
+
+#define ngx_rename_file(o, n) rename((const char *) o, (const char *) n)
+#define ngx_rename_file_n "rename()"
+
+
+#define ngx_change_file_access(n, a) chmod((const char *) n, a)
+#define ngx_change_file_access_n "chmod()"
+
+
+ngx_int_t ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s);
+#define ngx_set_file_time_n "utimes()"
+
+
+#define ngx_file_info(file, sb) stat((const char *) file, sb)
+#define ngx_file_info_n "stat()"
+
+#define ngx_fd_info(fd, sb) fstat(fd, sb)
+#define ngx_fd_info_n "fstat()"
+
+#define ngx_link_info(file, sb) lstat((const char *) file, sb)
+#define ngx_link_info_n "lstat()"
+
+#define ngx_is_dir(sb) (S_ISDIR((sb)->st_mode))
+#define ngx_is_file(sb) (S_ISREG((sb)->st_mode))
+#define ngx_is_link(sb) (S_ISLNK((sb)->st_mode))
+#define ngx_is_exec(sb) (((sb)->st_mode & S_IXUSR) == S_IXUSR)
+#define ngx_file_access(sb) ((sb)->st_mode & 0777)
+#define ngx_file_size(sb) (sb)->st_size
+#define ngx_file_fs_size(sb) ngx_max((sb)->st_size, (sb)->st_blocks * 512)
+#define ngx_file_mtime(sb) (sb)->st_mtime
+#define ngx_file_uniq(sb) (sb)->st_ino
+
+
+ngx_int_t ngx_create_file_mapping(ngx_file_mapping_t *fm);
+void ngx_close_file_mapping(ngx_file_mapping_t *fm);
+
+
+#define ngx_realpath(p, r) (u_char *) realpath((char *) p, (char *) r)
+#define ngx_realpath_n "realpath()"
+#define ngx_getcwd(buf, size) (getcwd((char *) buf, size) != NULL)
+#define ngx_getcwd_n "getcwd()"
+#define ngx_path_separator(c) ((c) == '/')
+
+
+#if defined(PATH_MAX)
+
+#define NGX_HAVE_MAX_PATH 1
+#define NGX_MAX_PATH PATH_MAX
+
+#else
+
+#define NGX_MAX_PATH 4096
+
+#endif
+
+
+#define NGX_DIR_MASK_LEN 0
+
+
+ngx_int_t ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir);
+#define ngx_open_dir_n "opendir()"
+
+
+#define ngx_close_dir(d) closedir((d)->dir)
+#define ngx_close_dir_n "closedir()"
+
+
+ngx_int_t ngx_read_dir(ngx_dir_t *dir);
+#define ngx_read_dir_n "readdir()"
+
+
+#define ngx_create_dir(name, access) mkdir((const char *) name, access)
+#define ngx_create_dir_n "mkdir()"
+
+
+#define ngx_delete_dir(name) rmdir((const char *) name)
+#define ngx_delete_dir_n "rmdir()"
+
+
+#define ngx_dir_access(a) (a | (a & 0444) >> 2)
+
+
+#define ngx_de_name(dir) ((u_char *) (dir)->de->d_name)
+#if (NGX_HAVE_D_NAMLEN)
+#define ngx_de_namelen(dir) (dir)->de->d_namlen
+#else
+#define ngx_de_namelen(dir) ngx_strlen((dir)->de->d_name)
+#endif
+
+static ngx_inline ngx_int_t
+ngx_de_info(u_char *name, ngx_dir_t *dir)
+{
+ dir->type = 0;
+ return stat((const char *) name, &dir->info);
+}
+
+#define ngx_de_info_n "stat()"
+#define ngx_de_link_info(name, dir) lstat((const char *) name, &(dir)->info)
+#define ngx_de_link_info_n "lstat()"
+
+#if (NGX_HAVE_D_TYPE)
+
+/*
+ * some file systems (e.g. XFS on Linux and CD9660 on FreeBSD)
+ * do not set dirent.d_type
+ */
+
+#define ngx_de_is_dir(dir) \
+ (((dir)->type) ? ((dir)->type == DT_DIR) : (S_ISDIR((dir)->info.st_mode)))
+#define ngx_de_is_file(dir) \
+ (((dir)->type) ? ((dir)->type == DT_REG) : (S_ISREG((dir)->info.st_mode)))
+#define ngx_de_is_link(dir) \
+ (((dir)->type) ? ((dir)->type == DT_LNK) : (S_ISLNK((dir)->info.st_mode)))
+
+#else
+
+#define ngx_de_is_dir(dir) (S_ISDIR((dir)->info.st_mode))
+#define ngx_de_is_file(dir) (S_ISREG((dir)->info.st_mode))
+#define ngx_de_is_link(dir) (S_ISLNK((dir)->info.st_mode))
+
+#endif
+
+#define ngx_de_access(dir) (((dir)->info.st_mode) & 0777)
+#define ngx_de_size(dir) (dir)->info.st_size
+#define ngx_de_fs_size(dir) \
+ ngx_max((dir)->info.st_size, (dir)->info.st_blocks * 512)
+#define ngx_de_mtime(dir) (dir)->info.st_mtime
+
+
+ngx_int_t ngx_open_glob(ngx_glob_t *gl);
+#define ngx_open_glob_n "glob()"
+ngx_int_t ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name);
+void ngx_close_glob(ngx_glob_t *gl);
+
+
+ngx_err_t ngx_trylock_fd(ngx_fd_t fd);
+ngx_err_t ngx_lock_fd(ngx_fd_t fd);
+ngx_err_t ngx_unlock_fd(ngx_fd_t fd);
+
+#define ngx_trylock_fd_n "fcntl(F_SETLK, F_WRLCK)"
+#define ngx_lock_fd_n "fcntl(F_SETLKW, F_WRLCK)"
+#define ngx_unlock_fd_n "fcntl(F_SETLK, F_UNLCK)"
+
+
+#if (NGX_HAVE_F_READAHEAD)
+
+#define NGX_HAVE_READ_AHEAD 1
+
+#define ngx_read_ahead(fd, n) fcntl(fd, F_READAHEAD, (int) n)
+#define ngx_read_ahead_n "fcntl(fd, F_READAHEAD)"
+
+#elif (NGX_HAVE_POSIX_FADVISE)
+
+#define NGX_HAVE_READ_AHEAD 1
+
+ngx_int_t ngx_read_ahead(ngx_fd_t fd, size_t n);
+#define ngx_read_ahead_n "posix_fadvise(POSIX_FADV_SEQUENTIAL)"
+
+#else
+
+#define ngx_read_ahead(fd, n) 0
+#define ngx_read_ahead_n "ngx_read_ahead_n"
+
+#endif
+
+
+#if (NGX_HAVE_O_DIRECT)
+
+ngx_int_t ngx_directio_on(ngx_fd_t fd);
+#define ngx_directio_on_n "fcntl(O_DIRECT)"
+
+ngx_int_t ngx_directio_off(ngx_fd_t fd);
+#define ngx_directio_off_n "fcntl(!O_DIRECT)"
+
+#elif (NGX_HAVE_F_NOCACHE)
+
+#define ngx_directio_on(fd) fcntl(fd, F_NOCACHE, 1)
+#define ngx_directio_on_n "fcntl(F_NOCACHE, 1)"
+
+#elif (NGX_HAVE_DIRECTIO)
+
+#define ngx_directio_on(fd) directio(fd, DIRECTIO_ON)
+#define ngx_directio_on_n "directio(DIRECTIO_ON)"
+
+#else
+
+#define ngx_directio_on(fd) 0
+#define ngx_directio_on_n "ngx_directio_on_n"
+
+#endif
+
+size_t ngx_fs_bsize(u_char *name);
+
+
+#if (NGX_HAVE_OPENAT)
+
+#define ngx_openat_file(fd, name, mode, create, access) \
+ openat(fd, (const char *) name, mode|create, access)
+
+#define ngx_openat_file_n "openat()"
+
+#define ngx_file_at_info(fd, name, sb, flag) \
+ fstatat(fd, (const char *) name, sb, flag)
+
+#define ngx_file_at_info_n "fstatat()"
+
+#define NGX_AT_FDCWD (ngx_fd_t) AT_FDCWD
+
+#endif
+
+
+#define ngx_stderr STDERR_FILENO
+#define ngx_set_stderr(fd) dup2(fd, STDERR_FILENO)
+#define ngx_set_stderr_n "dup2(STDERR_FILENO)"
+
+
+#if (NGX_HAVE_FILE_AIO)
+
+ssize_t ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size,
+ off_t offset, ngx_pool_t *pool);
+
+extern ngx_uint_t ngx_file_aio;
+
+#endif
+
+
+#endif /* _NGX_FILES_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd.h
new file mode 100644
index 00000000000..4f93da55cd2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd.h
@@ -0,0 +1,25 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_FREEBSD_H_INCLUDED_
+#define _NGX_FREEBSD_H_INCLUDED_
+
+
+void ngx_debug_init(void);
+ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,
+ off_t limit);
+
+extern int ngx_freebsd_kern_osreldate;
+extern int ngx_freebsd_hw_ncpu;
+extern u_long ngx_freebsd_net_inet_tcp_sendspace;
+
+extern ngx_uint_t ngx_freebsd_sendfile_nbytes_bug;
+extern ngx_uint_t ngx_freebsd_use_tcp_nopush;
+extern ngx_uint_t ngx_debug_malloc;
+
+
+#endif /* _NGX_FREEBSD_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_config.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_config.h
new file mode 100644
index 00000000000..92b2928c8b5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_config.h
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_FREEBSD_CONFIG_H_INCLUDED_
+#define _NGX_FREEBSD_CONFIG_H_INCLUDED_
+
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stddef.h> /* offsetof() */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+#include <glob.h>
+#include <time.h>
+#include <sys/param.h> /* ALIGN() */
+#include <sys/mount.h> /* statfs() */
+
+#include <sys/filio.h> /* FIONBIO */
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h> /* TCP_NODELAY, TCP_NOPUSH */
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/un.h>
+
+#include <libutil.h> /* setproctitle() before 4.1 */
+#include <osreldate.h>
+#include <sys/sysctl.h>
+
+
+#if __FreeBSD_version < 400017
+
+/*
+ * FreeBSD 3.x has no CMSG_SPACE() and CMSG_LEN() and has the broken CMSG_DATA()
+ */
+
+#undef CMSG_SPACE
+#define CMSG_SPACE(l) (ALIGN(sizeof(struct cmsghdr)) + ALIGN(l))
+
+#undef CMSG_LEN
+#define CMSG_LEN(l) (ALIGN(sizeof(struct cmsghdr)) + (l))
+
+#undef CMSG_DATA
+#define CMSG_DATA(cmsg) ((u_char *)(cmsg) + ALIGN(sizeof(struct cmsghdr)))
+
+#endif
+
+
+#include <ngx_auto_config.h>
+
+
+#if (NGX_HAVE_POSIX_SEM)
+#include <semaphore.h>
+#endif
+
+
+#if (NGX_HAVE_POLL)
+#include <poll.h>
+#endif
+
+
+#if (NGX_HAVE_KQUEUE)
+#include <sys/event.h>
+#endif
+
+
+#if (NGX_HAVE_FILE_AIO || NGX_HAVE_AIO)
+#include <aio.h>
+typedef struct aiocb ngx_aiocb_t;
+#endif
+
+
+#define NGX_LISTEN_BACKLOG -1
+
+
+#ifdef __DragonFly__
+#define NGX_KEEPALIVE_FACTOR 1000
+#endif
+
+
+#if (__FreeBSD_version < 430000 || __FreeBSD_version < 500012)
+
+pid_t rfork_thread(int flags, void *stack, int (*func)(void *arg), void *arg);
+
+#endif
+
+#ifndef IOV_MAX
+#define IOV_MAX 1024
+#endif
+
+
+#ifndef NGX_HAVE_INHERITED_NONBLOCK
+#define NGX_HAVE_INHERITED_NONBLOCK 1
+#endif
+
+
+#define NGX_HAVE_OS_SPECIFIC_INIT 1
+#define NGX_HAVE_DEBUG_MALLOC 1
+
+
+extern char **environ;
+extern char *malloc_options;
+
+
+#endif /* _NGX_FREEBSD_CONFIG_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_init.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_init.c
new file mode 100644
index 00000000000..c4c12dd741c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_init.c
@@ -0,0 +1,260 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/* FreeBSD 3.0 at least */
+char ngx_freebsd_kern_ostype[16];
+char ngx_freebsd_kern_osrelease[128];
+int ngx_freebsd_kern_osreldate;
+int ngx_freebsd_hw_ncpu;
+int ngx_freebsd_kern_ipc_somaxconn;
+u_long ngx_freebsd_net_inet_tcp_sendspace;
+
+/* FreeBSD 4.9 */
+int ngx_freebsd_machdep_hlt_logical_cpus;
+
+
+ngx_uint_t ngx_freebsd_sendfile_nbytes_bug;
+ngx_uint_t ngx_freebsd_use_tcp_nopush;
+
+ngx_uint_t ngx_debug_malloc;
+
+
+static ngx_os_io_t ngx_freebsd_io = {
+ ngx_unix_recv,
+ ngx_readv_chain,
+ ngx_udp_unix_recv,
+ ngx_unix_send,
+#if (NGX_HAVE_SENDFILE)
+ ngx_freebsd_sendfile_chain,
+ NGX_IO_SENDFILE
+#else
+ ngx_writev_chain,
+ 0
+#endif
+};
+
+
+typedef struct {
+ char *name;
+ void *value;
+ size_t size;
+ ngx_uint_t exists;
+} sysctl_t;
+
+
+sysctl_t sysctls[] = {
+ { "hw.ncpu",
+ &ngx_freebsd_hw_ncpu,
+ sizeof(ngx_freebsd_hw_ncpu), 0 },
+
+ { "machdep.hlt_logical_cpus",
+ &ngx_freebsd_machdep_hlt_logical_cpus,
+ sizeof(ngx_freebsd_machdep_hlt_logical_cpus), 0 },
+
+ { "net.inet.tcp.sendspace",
+ &ngx_freebsd_net_inet_tcp_sendspace,
+ sizeof(ngx_freebsd_net_inet_tcp_sendspace), 0 },
+
+ { "kern.ipc.somaxconn",
+ &ngx_freebsd_kern_ipc_somaxconn,
+ sizeof(ngx_freebsd_kern_ipc_somaxconn), 0 },
+
+ { NULL, NULL, 0, 0 }
+};
+
+
+void
+ngx_debug_init(void)
+{
+#if (NGX_DEBUG_MALLOC)
+
+#if __FreeBSD_version >= 500014 && __FreeBSD_version < 1000011
+ _malloc_options = "J";
+#elif __FreeBSD_version < 500014
+ malloc_options = "J";
+#endif
+
+ ngx_debug_malloc = 1;
+
+#else
+ char *mo;
+
+ mo = getenv("MALLOC_OPTIONS");
+
+ if (mo && ngx_strchr(mo, 'J')) {
+ ngx_debug_malloc = 1;
+ }
+#endif
+}
+
+
+ngx_int_t
+ngx_os_specific_init(ngx_log_t *log)
+{
+ int version;
+ size_t size;
+ ngx_err_t err;
+ ngx_uint_t i;
+
+ size = sizeof(ngx_freebsd_kern_ostype);
+ if (sysctlbyname("kern.ostype",
+ ngx_freebsd_kern_ostype, &size, NULL, 0) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ "sysctlbyname(kern.ostype) failed");
+
+ if (ngx_errno != NGX_ENOMEM) {
+ return NGX_ERROR;
+ }
+
+ ngx_freebsd_kern_ostype[size - 1] = '\0';
+ }
+
+ size = sizeof(ngx_freebsd_kern_osrelease);
+ if (sysctlbyname("kern.osrelease",
+ ngx_freebsd_kern_osrelease, &size, NULL, 0) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ "sysctlbyname(kern.osrelease) failed");
+
+ if (ngx_errno != NGX_ENOMEM) {
+ return NGX_ERROR;
+ }
+
+ ngx_freebsd_kern_osrelease[size - 1] = '\0';
+ }
+
+
+ size = sizeof(int);
+ if (sysctlbyname("kern.osreldate",
+ &ngx_freebsd_kern_osreldate, &size, NULL, 0) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ "sysctlbyname(kern.osreldate) failed");
+ return NGX_ERROR;
+ }
+
+ version = ngx_freebsd_kern_osreldate;
+
+
+#if (NGX_HAVE_SENDFILE)
+
+ /*
+ * The determination of the sendfile() "nbytes bug" is complex enough.
+ * There are two sendfile() syscalls: a new #393 has no bug while
+ * an old #336 has the bug in some versions and has not in others.
+ * Besides libc_r wrapper also emulates the bug in some versions.
+ * There is no way to say exactly if syscall #336 in FreeBSD circa 4.6
+ * has the bug. We use the algorithm that is correct at least for
+ * RELEASEs and for syscalls only (not libc_r wrapper).
+ *
+ * 4.6.1-RELEASE and below have the bug
+ * 4.6.2-RELEASE and above have the new syscall
+ *
+ * We detect the new sendfile() syscall available at the compile time
+ * to allow an old binary to run correctly on an updated FreeBSD system.
+ */
+
+#if (__FreeBSD__ == 4 && __FreeBSD_version >= 460102) \
+ || __FreeBSD_version == 460002 || __FreeBSD_version >= 500039
+
+ /* a new syscall without the bug */
+
+ ngx_freebsd_sendfile_nbytes_bug = 0;
+
+#else
+
+ /* an old syscall that may have the bug */
+
+ ngx_freebsd_sendfile_nbytes_bug = 1;
+
+#endif
+
+#endif /* NGX_HAVE_SENDFILE */
+
+
+ if ((version < 500000 && version >= 440003) || version >= 500017) {
+ ngx_freebsd_use_tcp_nopush = 1;
+ }
+
+
+ for (i = 0; sysctls[i].name; i++) {
+ size = sysctls[i].size;
+
+ if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0)
+ == 0)
+ {
+ sysctls[i].exists = 1;
+ continue;
+ }
+
+ err = ngx_errno;
+
+ if (err == NGX_ENOENT) {
+ continue;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, log, err,
+ "sysctlbyname(%s) failed", sysctls[i].name);
+ return NGX_ERROR;
+ }
+
+ if (ngx_freebsd_machdep_hlt_logical_cpus) {
+ ngx_ncpu = ngx_freebsd_hw_ncpu / 2;
+
+ } else {
+ ngx_ncpu = ngx_freebsd_hw_ncpu;
+ }
+
+ if (version < 600008 && ngx_freebsd_kern_ipc_somaxconn > 32767) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "sysctl kern.ipc.somaxconn must be less than 32768");
+ return NGX_ERROR;
+ }
+
+ ngx_tcp_nodelay_and_tcp_nopush = 1;
+
+ ngx_os_io = ngx_freebsd_io;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_os_specific_status(ngx_log_t *log)
+{
+ u_long value;
+ ngx_uint_t i;
+
+ ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
+ ngx_freebsd_kern_ostype, ngx_freebsd_kern_osrelease);
+
+#ifdef __DragonFly_version
+ ngx_log_error(NGX_LOG_NOTICE, log, 0,
+ "kern.osreldate: %d, built on %d",
+ ngx_freebsd_kern_osreldate, __DragonFly_version);
+#else
+ ngx_log_error(NGX_LOG_NOTICE, log, 0,
+ "kern.osreldate: %d, built on %d",
+ ngx_freebsd_kern_osreldate, __FreeBSD_version);
+#endif
+
+ for (i = 0; sysctls[i].name; i++) {
+ if (sysctls[i].exists) {
+ if (sysctls[i].size == sizeof(long)) {
+ value = *(long *) sysctls[i].value;
+
+ } else {
+ value = *(int *) sysctls[i].value;
+ }
+
+ ngx_log_error(NGX_LOG_NOTICE, log, 0, "%s: %l",
+ sysctls[i].name, value);
+ }
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_rfork_thread.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_rfork_thread.c
new file mode 100644
index 00000000000..e92f9a9fdd7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_rfork_thread.c
@@ -0,0 +1,756 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+/*
+ * The threads implementation uses the rfork(RFPROC|RFTHREAD|RFMEM) syscall
+ * to create threads. All threads use the stacks of the same size mmap()ed
+ * below the main stack. Thus the current thread id is determined via
+ * the stack pointer value.
+ *
+ * The mutex implementation uses the ngx_atomic_cmp_set() operation
+ * to acquire a mutex and the SysV semaphore to wait on a mutex and to wake up
+ * the waiting threads. The light mutex does not use semaphore, so after
+ * spinning in the lock the thread calls sched_yield(). However the light
+ * mutexes are intended to be used with the "trylock" operation only.
+ * The SysV semop() is a cheap syscall, particularly if it has little sembuf's
+ * and does not use SEM_UNDO.
+ *
+ * The condition variable implementation uses the signal #64.
+ * The signal handler is SIG_IGN so the kill() is a cheap syscall.
+ * The thread waits a signal in kevent(). The use of the EVFILT_SIGNAL
+ * is safe since FreeBSD 4.10-STABLE.
+ *
+ * This threads implementation currently works on i386 (486+) and amd64
+ * platforms only.
+ */
+
+
+char *ngx_freebsd_kern_usrstack;
+size_t ngx_thread_stack_size;
+
+
+static size_t rz_size;
+static size_t usable_stack_size;
+static char *last_stack;
+
+static ngx_uint_t nthreads;
+static ngx_uint_t max_threads;
+
+static ngx_uint_t nkeys;
+static ngx_tid_t *tids; /* the threads tids array */
+void **ngx_tls; /* the threads tls's array */
+
+/* the thread-safe libc errno */
+
+static int errno0; /* the main thread's errno */
+static int *errnos; /* the threads errno's array */
+
+int *
+__error()
+{
+ int tid;
+
+ tid = ngx_gettid();
+
+ return tid ? &errnos[tid - 1] : &errno0;
+}
+
+
+/*
+ * __isthreaded enables the spinlocks in some libc functions, i.e. in malloc()
+ * and some other places. Nevertheless we protect our malloc()/free() calls
+ * by own mutex that is more efficient than the spinlock.
+ *
+ * _spinlock() is a weak referenced stub in src/lib/libc/gen/_spinlock_stub.c
+ * that does nothing.
+ */
+
+extern int __isthreaded;
+
+void
+_spinlock(ngx_atomic_t *lock)
+{
+ ngx_int_t tries;
+
+ tries = 0;
+
+ for ( ;; ) {
+
+ if (*lock) {
+ if (ngx_ncpu > 1 && tries++ < 1000) {
+ continue;
+ }
+
+ sched_yield();
+ tries = 0;
+
+ } else {
+ if (ngx_atomic_cmp_set(lock, 0, 1)) {
+ return;
+ }
+ }
+ }
+}
+
+
+/*
+ * Before FreeBSD 5.1 _spinunlock() is a simple #define in
+ * src/lib/libc/include/spinlock.h that zeroes lock.
+ *
+ * Since FreeBSD 5.1 _spinunlock() is a weak referenced stub in
+ * src/lib/libc/gen/_spinlock_stub.c that does nothing.
+ */
+
+#ifndef _spinunlock
+
+void
+_spinunlock(ngx_atomic_t *lock)
+{
+ *lock = 0;
+}
+
+#endif
+
+
+ngx_err_t
+ngx_create_thread(ngx_tid_t *tid, ngx_thread_value_t (*func)(void *arg),
+ void *arg, ngx_log_t *log)
+{
+ ngx_pid_t id;
+ ngx_err_t err;
+ char *stack, *stack_top;
+
+ if (nthreads >= max_threads) {
+ ngx_log_error(NGX_LOG_CRIT, log, 0,
+ "no more than %ui threads can be created", max_threads);
+ return NGX_ERROR;
+ }
+
+ last_stack -= ngx_thread_stack_size;
+
+ stack = mmap(last_stack, usable_stack_size, PROT_READ|PROT_WRITE,
+ MAP_STACK, -1, 0);
+
+ if (stack == MAP_FAILED) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ "mmap(%p:%uz, MAP_STACK) thread stack failed",
+ last_stack, usable_stack_size);
+ return NGX_ERROR;
+ }
+
+ if (stack != last_stack) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "stack %p address was changed to %p", last_stack, stack);
+ return NGX_ERROR;
+ }
+
+ stack_top = stack + usable_stack_size;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, log, 0,
+ "thread stack: %p-%p", stack, stack_top);
+
+ ngx_set_errno(0);
+
+ id = rfork_thread(RFPROC|RFTHREAD|RFMEM, stack_top,
+ (ngx_rfork_thread_func_pt) func, arg);
+
+ err = ngx_errno;
+
+ if (id == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, err, "rfork() failed");
+
+ } else {
+ *tid = id;
+ nthreads = (ngx_freebsd_kern_usrstack - stack_top)
+ / ngx_thread_stack_size;
+ tids[nthreads] = id;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "rfork()ed thread: %P", id);
+ }
+
+ return err;
+}
+
+
+ngx_int_t
+ngx_init_threads(int n, size_t size, ngx_cycle_t *cycle)
+{
+ char *red_zone, *zone;
+ size_t len;
+ ngx_int_t i;
+ struct sigaction sa;
+
+ max_threads = n + 1;
+
+ for (i = 0; i < n; i++) {
+ ngx_memzero(&sa, sizeof(struct sigaction));
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(NGX_CV_SIGNAL, &sa, NULL) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "sigaction(%d, SIG_IGN) failed", NGX_CV_SIGNAL);
+ return NGX_ERROR;
+ }
+ }
+
+ len = sizeof(ngx_freebsd_kern_usrstack);
+ if (sysctlbyname("kern.usrstack", &ngx_freebsd_kern_usrstack, &len,
+ NULL, 0) == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "sysctlbyname(kern.usrstack) failed");
+ return NGX_ERROR;
+ }
+
+ /* the main thread stack red zone */
+ rz_size = ngx_pagesize;
+ red_zone = ngx_freebsd_kern_usrstack - (size + rz_size);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+ "usrstack: %p red zone: %p",
+ ngx_freebsd_kern_usrstack, red_zone);
+
+ zone = mmap(red_zone, rz_size, PROT_NONE, MAP_ANON, -1, 0);
+ if (zone == MAP_FAILED) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "mmap(%p:%uz, PROT_NONE, MAP_ANON) red zone failed",
+ red_zone, rz_size);
+ return NGX_ERROR;
+ }
+
+ if (zone != red_zone) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "red zone %p address was changed to %p", red_zone, zone);
+ return NGX_ERROR;
+ }
+
+ /* create the thread errno' array */
+
+ errnos = ngx_calloc(n * sizeof(int), cycle->log);
+ if (errnos == NULL) {
+ return NGX_ERROR;
+ }
+
+ /* create the thread tids array */
+
+ tids = ngx_calloc((n + 1) * sizeof(ngx_tid_t), cycle->log);
+ if (tids == NULL) {
+ return NGX_ERROR;
+ }
+
+ tids[0] = ngx_pid;
+
+ /* create the thread tls' array */
+
+ ngx_tls = ngx_calloc(NGX_THREAD_KEYS_MAX * (n + 1) * sizeof(void *),
+ cycle->log);
+ if (ngx_tls == NULL) {
+ return NGX_ERROR;
+ }
+
+ nthreads = 1;
+
+ last_stack = zone + rz_size;
+ usable_stack_size = size;
+ ngx_thread_stack_size = size + rz_size;
+
+ /* allow the spinlock in libc malloc() */
+ __isthreaded = 1;
+
+ ngx_threaded = 1;
+
+ return NGX_OK;
+}
+
+
+ngx_tid_t
+ngx_thread_self(void)
+{
+ ngx_int_t tid;
+
+ tid = ngx_gettid();
+
+ if (tids == NULL) {
+ return ngx_pid;
+ }
+
+ return tids[tid];
+}
+
+
+ngx_err_t
+ngx_thread_key_create(ngx_tls_key_t *key)
+{
+ if (nkeys >= NGX_THREAD_KEYS_MAX) {
+ return NGX_ENOMEM;
+ }
+
+ *key = nkeys++;
+
+ return 0;
+}
+
+
+ngx_err_t
+ngx_thread_set_tls(ngx_tls_key_t key, void *value)
+{
+ if (key >= NGX_THREAD_KEYS_MAX) {
+ return NGX_EINVAL;
+ }
+
+ ngx_tls[key * NGX_THREAD_KEYS_MAX + ngx_gettid()] = value;
+ return 0;
+}
+
+
+ngx_mutex_t *
+ngx_mutex_init(ngx_log_t *log, ngx_uint_t flags)
+{
+ ngx_mutex_t *m;
+ union semun op;
+
+ m = ngx_alloc(sizeof(ngx_mutex_t), log);
+ if (m == NULL) {
+ return NULL;
+ }
+
+ m->lock = 0;
+ m->log = log;
+
+ if (flags & NGX_MUTEX_LIGHT) {
+ m->semid = -1;
+ return m;
+ }
+
+ m->semid = semget(IPC_PRIVATE, 1, SEM_R|SEM_A);
+ if (m->semid == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "semget() failed");
+ return NULL;
+ }
+
+ op.val = 0;
+
+ if (semctl(m->semid, 0, SETVAL, op) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "semctl(SETVAL) failed");
+
+ if (semctl(m->semid, 0, IPC_RMID) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ "semctl(IPC_RMID) failed");
+ }
+
+ return NULL;
+ }
+
+ return m;
+}
+
+
+void
+ngx_mutex_destroy(ngx_mutex_t *m)
+{
+ if (semctl(m->semid, 0, IPC_RMID) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,
+ "semctl(IPC_RMID) failed");
+ }
+
+ ngx_free((void *) m);
+}
+
+
+ngx_int_t
+ngx_mutex_dolock(ngx_mutex_t *m, ngx_int_t try)
+{
+ uint32_t lock, old;
+ ngx_uint_t tries;
+ struct sembuf op;
+
+ if (!ngx_threaded) {
+ return NGX_OK;
+ }
+
+#if (NGX_DEBUG)
+ if (try) {
+ ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,
+ "try lock mutex %p lock:%XD", m, m->lock);
+ } else {
+ ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,
+ "lock mutex %p lock:%XD", m, m->lock);
+ }
+#endif
+
+ old = m->lock;
+ tries = 0;
+
+ for ( ;; ) {
+ if (old & NGX_MUTEX_LOCK_BUSY) {
+
+ if (try) {
+ return NGX_AGAIN;
+ }
+
+ if (ngx_ncpu > 1 && tries++ < 1000) {
+
+ /* the spinlock is used only on the SMP system */
+
+ old = m->lock;
+ continue;
+ }
+
+ if (m->semid == -1) {
+ sched_yield();
+
+ tries = 0;
+ old = m->lock;
+ continue;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,
+ "mutex %p lock:%XD", m, m->lock);
+
+ /*
+ * The mutex is locked so we increase a number
+ * of the threads that are waiting on the mutex
+ */
+
+ lock = old + 1;
+
+ if ((lock & ~NGX_MUTEX_LOCK_BUSY) > nthreads) {
+ ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,
+ "%D threads wait for mutex %p, "
+ "while only %ui threads are available",
+ lock & ~NGX_MUTEX_LOCK_BUSY, m, nthreads);
+ ngx_abort();
+ }
+
+ if (ngx_atomic_cmp_set(&m->lock, old, lock)) {
+
+ ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,
+ "wait mutex %p lock:%XD", m, m->lock);
+
+ /*
+ * The number of the waiting threads has been increased
+ * and we would wait on the SysV semaphore.
+ * A semaphore should wake up us more efficiently than
+ * a simple sched_yield() or usleep().
+ */
+
+ op.sem_num = 0;
+ op.sem_op = -1;
+ op.sem_flg = 0;
+
+ if (semop(m->semid, &op, 1) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,
+ "semop() failed while waiting on mutex %p", m);
+ ngx_abort();
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,
+ "mutex waked up %p lock:%XD", m, m->lock);
+
+ tries = 0;
+ old = m->lock;
+ continue;
+ }
+
+ old = m->lock;
+
+ } else {
+ lock = old | NGX_MUTEX_LOCK_BUSY;
+
+ if (ngx_atomic_cmp_set(&m->lock, old, lock)) {
+
+ /* we locked the mutex */
+
+ break;
+ }
+
+ old = m->lock;
+ }
+
+ if (tries++ > 1000) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0,
+ "mutex %p is contested", m);
+
+ /* the mutex is probably contested so we are giving up now */
+
+ sched_yield();
+
+ tries = 0;
+ old = m->lock;
+ }
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,
+ "mutex %p is locked, lock:%XD", m, m->lock);
+
+ return NGX_OK;
+}
+
+
+void
+ngx_mutex_unlock(ngx_mutex_t *m)
+{
+ uint32_t lock, old;
+ struct sembuf op;
+
+ if (!ngx_threaded) {
+ return;
+ }
+
+ old = m->lock;
+
+ if (!(old & NGX_MUTEX_LOCK_BUSY)) {
+ ngx_log_error(NGX_LOG_ALERT, m->log, 0,
+ "trying to unlock the free mutex %p", m);
+ ngx_abort();
+ }
+
+ /* free the mutex */
+
+#if 0
+ ngx_log_debug2(NGX_LOG_DEBUG_MUTEX, m->log, 0,
+ "unlock mutex %p lock:%XD", m, old);
+#endif
+
+ for ( ;; ) {
+ lock = old & ~NGX_MUTEX_LOCK_BUSY;
+
+ if (ngx_atomic_cmp_set(&m->lock, old, lock)) {
+ break;
+ }
+
+ old = m->lock;
+ }
+
+ if (m->semid == -1) {
+ ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0,
+ "mutex %p is unlocked", m);
+
+ return;
+ }
+
+ /* check whether we need to wake up a waiting thread */
+
+ old = m->lock;
+
+ for ( ;; ) {
+ if (old & NGX_MUTEX_LOCK_BUSY) {
+
+ /* the mutex is just locked by another thread */
+
+ break;
+ }
+
+ if (old == 0) {
+ break;
+ }
+
+ /* there are the waiting threads */
+
+ lock = old - 1;
+
+ if (ngx_atomic_cmp_set(&m->lock, old, lock)) {
+
+ /* wake up the thread that waits on semaphore */
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0,
+ "wake up mutex %p", m);
+
+ op.sem_num = 0;
+ op.sem_op = 1;
+ op.sem_flg = 0;
+
+ if (semop(m->semid, &op, 1) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, m->log, ngx_errno,
+ "semop() failed while waking up on mutex %p", m);
+ ngx_abort();
+ }
+
+ break;
+ }
+
+ old = m->lock;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0,
+ "mutex %p is unlocked", m);
+
+ return;
+}
+
+
+ngx_cond_t *
+ngx_cond_init(ngx_log_t *log)
+{
+ ngx_cond_t *cv;
+
+ cv = ngx_alloc(sizeof(ngx_cond_t), log);
+ if (cv == NULL) {
+ return NULL;
+ }
+
+ cv->signo = NGX_CV_SIGNAL;
+ cv->tid = -1;
+ cv->log = log;
+ cv->kq = -1;
+
+ return cv;
+}
+
+
+void
+ngx_cond_destroy(ngx_cond_t *cv)
+{
+ if (close(cv->kq) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cv->log, ngx_errno,
+ "kqueue close() failed");
+ }
+
+ ngx_free(cv);
+}
+
+
+ngx_int_t
+ngx_cond_wait(ngx_cond_t *cv, ngx_mutex_t *m)
+{
+ int n;
+ ngx_err_t err;
+ struct kevent kev;
+ struct timespec ts;
+
+ if (cv->kq == -1) {
+
+ /*
+ * We have to add the EVFILT_SIGNAL filter in the rfork()ed thread.
+ * Otherwise the thread would not get a signal event.
+ *
+ * However, we have not to open the kqueue in the thread,
+ * it is simply handy do it together.
+ */
+
+ cv->kq = kqueue();
+ if (cv->kq == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cv->log, ngx_errno, "kqueue() failed");
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, cv->log, 0,
+ "cv kq:%d signo:%d", cv->kq, cv->signo);
+
+ kev.ident = cv->signo;
+ kev.filter = EVFILT_SIGNAL;
+ kev.flags = EV_ADD;
+ kev.fflags = 0;
+ kev.data = 0;
+ kev.udata = NULL;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+
+ if (kevent(cv->kq, &kev, 1, NULL, 0, &ts) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cv->log, ngx_errno, "kevent() failed");
+ return NGX_ERROR;
+ }
+
+ cv->tid = ngx_thread_self();
+ }
+
+ ngx_mutex_unlock(m);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_CORE, cv->log, 0,
+ "cv %p wait, kq:%d, signo:%d", cv, cv->kq, cv->signo);
+
+ for ( ;; ) {
+ n = kevent(cv->kq, NULL, 0, &kev, 1, NULL);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, cv->log, 0,
+ "cv %p kevent: %d", cv, n);
+
+ if (n == -1) {
+ err = ngx_errno;
+ ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,
+ cv->log, ngx_errno,
+ "kevent() failed while waiting condition variable %p",
+ cv);
+
+ if (err == NGX_EINTR) {
+ break;
+ }
+
+ return NGX_ERROR;
+ }
+
+ if (n == 0) {
+ ngx_log_error(NGX_LOG_ALERT, cv->log, 0,
+ "kevent() returned no events "
+ "while waiting condition variable %p",
+ cv);
+ continue;
+ }
+
+ if (kev.filter != EVFILT_SIGNAL) {
+ ngx_log_error(NGX_LOG_ALERT, cv->log, 0,
+ "kevent() returned unexpected events: %d "
+ "while waiting condition variable %p",
+ kev.filter, cv);
+ continue;
+ }
+
+ if (kev.ident != (uintptr_t) cv->signo) {
+ ngx_log_error(NGX_LOG_ALERT, cv->log, 0,
+ "kevent() returned unexpected signal: %d ",
+ "while waiting condition variable %p",
+ kev.ident, cv);
+ continue;
+ }
+
+ break;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, "cv %p is waked up", cv);
+
+ ngx_mutex_lock(m);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_cond_signal(ngx_cond_t *cv)
+{
+ ngx_err_t err;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_CORE, cv->log, 0,
+ "cv %p to signal %P %d",
+ cv, cv->tid, cv->signo);
+
+ if (cv->tid == -1) {
+ return NGX_OK;
+ }
+
+ if (kill(cv->tid, cv->signo) == -1) {
+
+ err = ngx_errno;
+
+ ngx_log_error(NGX_LOG_ALERT, cv->log, err,
+ "kill() failed while signaling condition variable %p", cv);
+
+ if (err == NGX_ESRCH) {
+ cv->tid = -1;
+ }
+
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, "cv %p is signaled", cv);
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_rfork_thread.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_rfork_thread.h
new file mode 100644
index 00000000000..ff160449dad
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_rfork_thread.h
@@ -0,0 +1,122 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_FREEBSD_RFORK_THREAD_H_INCLUDED_
+#define _NGX_FREEBSD_RFORK_THREAD_H_INCLUDED_
+
+
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sched.h>
+
+typedef pid_t ngx_tid_t;
+
+#define ngx_log_pid ngx_thread_self()
+#define ngx_log_tid 0
+
+#define NGX_TID_T_FMT "%P"
+
+
+#define NGX_MUTEX_LIGHT 1
+
+#define NGX_MUTEX_LOCK_BUSY 0x80000000
+
+typedef volatile struct {
+ ngx_atomic_t lock;
+ ngx_log_t *log;
+ int semid;
+} ngx_mutex_t;
+
+
+#define NGX_CV_SIGNAL 64
+
+typedef struct {
+ int signo;
+ int kq;
+ ngx_tid_t tid;
+ ngx_log_t *log;
+} ngx_cond_t;
+
+
+#define ngx_thread_sigmask(how, set, oset) \
+ (sigprocmask(how, set, oset) == -1) ? ngx_errno : 0
+
+#define ngx_thread_sigmask_n "sigprocmask()"
+
+#define ngx_thread_join(t, p)
+
+#define ngx_setthrtitle(n) setproctitle(n)
+
+
+extern char *ngx_freebsd_kern_usrstack;
+extern size_t ngx_thread_stack_size;
+
+
+static ngx_inline ngx_int_t
+ngx_gettid(void)
+{
+ char *sp;
+
+ if (ngx_thread_stack_size == 0) {
+ return 0;
+ }
+
+#if ( __i386__ )
+
+ __asm__ volatile ("mov %%esp, %0" : "=q" (sp));
+
+#elif ( __amd64__ )
+
+ __asm__ volatile ("mov %%rsp, %0" : "=q" (sp));
+
+#else
+
+#error "rfork()ed threads are not supported on this platform"
+
+#endif
+
+ return (ngx_freebsd_kern_usrstack - sp) / ngx_thread_stack_size;
+}
+
+
+ngx_tid_t ngx_thread_self(void);
+
+
+typedef ngx_uint_t ngx_tls_key_t;
+
+#define NGX_THREAD_KEYS_MAX 16
+
+extern void **ngx_tls;
+
+ngx_err_t ngx_thread_key_create(ngx_tls_key_t *key);
+#define ngx_thread_key_create_n "the tls key creation"
+
+ngx_err_t ngx_thread_set_tls(ngx_tls_key_t key, void *value);
+#define ngx_thread_set_tls_n "the tls key setting"
+
+
+static void *
+ngx_thread_get_tls(ngx_tls_key_t key)
+{
+ if (key >= NGX_THREAD_KEYS_MAX) {
+ return NULL;
+ }
+
+ return ngx_tls[key * NGX_THREAD_KEYS_MAX + ngx_gettid()];
+}
+
+
+#define ngx_mutex_trylock(m) ngx_mutex_dolock(m, 1)
+#define ngx_mutex_lock(m) (void) ngx_mutex_dolock(m, 0)
+ngx_int_t ngx_mutex_dolock(ngx_mutex_t *m, ngx_int_t try);
+void ngx_mutex_unlock(ngx_mutex_t *m);
+
+
+typedef int (*ngx_rfork_thread_func_pt)(void *arg);
+
+
+#endif /* _NGX_FREEBSD_RFORK_THREAD_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_sendfile_chain.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_sendfile_chain.c
new file mode 100644
index 00000000000..6491e928fe5
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_freebsd_sendfile_chain.c
@@ -0,0 +1,440 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+/*
+ * Although FreeBSD sendfile() allows to pass a header and a trailer,
+ * it cannot send a header with a part of the file in one packet until
+ * FreeBSD 5.3. Besides, over the fast ethernet connection sendfile()
+ * may send the partially filled packets, i.e. the 8 file pages may be sent
+ * as the 11 full 1460-bytes packets, then one incomplete 324-bytes packet,
+ * and then again the 11 full 1460-bytes packets.
+ *
+ * Therefore we use the TCP_NOPUSH option (similar to Linux's TCP_CORK)
+ * to postpone the sending - it not only sends a header and the first part of
+ * the file in one packet, but also sends the file pages in the full packets.
+ *
+ * But until FreeBSD 4.5 turning TCP_NOPUSH off does not flush a pending
+ * data that less than MSS, so that data may be sent with 5 second delay.
+ * So we do not use TCP_NOPUSH on FreeBSD prior to 4.5, although it can be used
+ * for non-keepalive HTTP connections.
+ */
+
+
+#if (IOV_MAX > 64)
+#define NGX_HEADERS 64
+#define NGX_TRAILERS 64
+#else
+#define NGX_HEADERS IOV_MAX
+#define NGX_TRAILERS IOV_MAX
+#endif
+
+
+ngx_chain_t *
+ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
+{
+ int rc, flags;
+ u_char *prev;
+ off_t size, send, prev_send, aligned, sent, fprev;
+ size_t header_size, file_size;
+ ngx_uint_t eintr, eagain, complete;
+ ngx_err_t err;
+ ngx_buf_t *file;
+ ngx_array_t header, trailer;
+ ngx_event_t *wev;
+ ngx_chain_t *cl;
+ struct sf_hdtr hdtr;
+ struct iovec *iov, headers[NGX_HEADERS], trailers[NGX_TRAILERS];
+
+ wev = c->write;
+
+ if (!wev->ready) {
+ return in;
+ }
+
+#if (NGX_HAVE_KQUEUE)
+
+ if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
+ (void) ngx_connection_error(c, wev->kq_errno,
+ "kevent() reported about an closed connection");
+ wev->error = 1;
+ return NGX_CHAIN_ERROR;
+ }
+
+#endif
+
+ /* the maximum limit size is the maximum size_t value - the page size */
+
+ if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
+ limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
+ }
+
+ send = 0;
+ eagain = 0;
+ flags = 0;
+
+ header.elts = headers;
+ header.size = sizeof(struct iovec);
+ header.nalloc = NGX_HEADERS;
+ header.pool = c->pool;
+
+ trailer.elts = trailers;
+ trailer.size = sizeof(struct iovec);
+ trailer.nalloc = NGX_TRAILERS;
+ trailer.pool = c->pool;
+
+ for ( ;; ) {
+ file = NULL;
+ file_size = 0;
+ header_size = 0;
+ eintr = 0;
+ complete = 0;
+ prev_send = send;
+
+ header.nelts = 0;
+ trailer.nelts = 0;
+
+ /* create the header iovec and coalesce the neighbouring bufs */
+
+ prev = NULL;
+ iov = NULL;
+
+ for (cl = in; cl && send < limit; cl = cl->next) {
+
+ if (ngx_buf_special(cl->buf)) {
+ continue;
+ }
+
+ if (!ngx_buf_in_memory_only(cl->buf)) {
+ break;
+ }
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if (send + size > limit) {
+ size = limit - send;
+ }
+
+ if (prev == cl->buf->pos) {
+ iov->iov_len += (size_t) size;
+
+ } else {
+ if (header.nelts >= IOV_MAX){
+ break;
+ }
+
+ iov = ngx_array_push(&header);
+ if (iov == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ iov->iov_base = (void *) cl->buf->pos;
+ iov->iov_len = (size_t) size;
+ }
+
+ prev = cl->buf->pos + (size_t) size;
+ header_size += (size_t) size;
+ send += size;
+ }
+
+
+ if (cl && cl->buf->in_file && send < limit) {
+ file = cl->buf;
+
+ /* coalesce the neighbouring file bufs */
+
+ do {
+ size = cl->buf->file_last - cl->buf->file_pos;
+
+ if (send + size > limit) {
+ size = limit - send;
+
+ aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
+ & ~((off_t) ngx_pagesize - 1);
+
+ if (aligned <= cl->buf->file_last) {
+ size = aligned - cl->buf->file_pos;
+ }
+ }
+
+ file_size += (size_t) size;
+ send += size;
+ fprev = cl->buf->file_pos + size;
+ cl = cl->next;
+
+ } while (cl
+ && cl->buf->in_file
+ && send < limit
+ && file->file->fd == cl->buf->file->fd
+ && fprev == cl->buf->file_pos);
+ }
+
+
+ if (file) {
+
+ /* create the trailer iovec and coalesce the neighbouring bufs */
+
+ prev = NULL;
+ iov = NULL;
+
+ while (cl && send < limit) {
+
+ if (ngx_buf_special(cl->buf)) {
+ cl = cl->next;
+ continue;
+ }
+
+ if (!ngx_buf_in_memory_only(cl->buf)) {
+ break;
+ }
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if (send + size > limit) {
+ size = limit - send;
+ }
+
+ if (prev == cl->buf->pos) {
+ iov->iov_len += (size_t) size;
+
+ } else {
+ if (trailer.nelts >= IOV_MAX){
+ break;
+ }
+
+ iov = ngx_array_push(&trailer);
+ if (iov == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ iov->iov_base = (void *) cl->buf->pos;
+ iov->iov_len = (size_t) size;
+ }
+
+ prev = cl->buf->pos + (size_t) size;
+ send += size;
+ cl = cl->next;
+ }
+ }
+
+ if (file) {
+
+ if (ngx_freebsd_use_tcp_nopush
+ && c->tcp_nopush == NGX_TCP_NOPUSH_UNSET)
+ {
+ if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
+ err = ngx_socket_errno;
+
+ /*
+ * there is a tiny chance to be interrupted, however,
+ * we continue a processing without the TCP_NOPUSH
+ */
+
+ if (err != NGX_EINTR) {
+ wev->error = 1;
+ (void) ngx_connection_error(c, err,
+ ngx_tcp_nopush_n " failed");
+ return NGX_CHAIN_ERROR;
+ }
+
+ } else {
+ c->tcp_nopush = NGX_TCP_NOPUSH_SET;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "tcp_nopush");
+ }
+ }
+
+ /*
+ * sendfile() does unneeded work if sf_hdtr's count is 0,
+ * but corresponding pointer is not NULL
+ */
+
+ hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL;
+ hdtr.hdr_cnt = header.nelts;
+ hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL;
+ hdtr.trl_cnt = trailer.nelts;
+
+ /*
+ * the "nbytes bug" of the old sendfile() syscall:
+ * http://bugs.freebsd.org/33771
+ */
+
+ if (!ngx_freebsd_sendfile_nbytes_bug) {
+ header_size = 0;
+ }
+
+ sent = 0;
+
+#if (NGX_HAVE_AIO_SENDFILE)
+ flags = c->aio_sendfile ? SF_NODISKIO : 0;
+#endif
+
+ rc = sendfile(file->file->fd, c->fd, file->file_pos,
+ file_size + header_size, &hdtr, &sent, flags);
+
+ if (rc == -1) {
+ err = ngx_errno;
+
+ switch (err) {
+ case NGX_EAGAIN:
+ eagain = 1;
+ break;
+
+ case NGX_EINTR:
+ eintr = 1;
+ break;
+
+#if (NGX_HAVE_AIO_SENDFILE)
+ case NGX_EBUSY:
+ c->busy_sendfile = file;
+ break;
+#endif
+
+ default:
+ wev->error = 1;
+ (void) ngx_connection_error(c, err, "sendfile() failed");
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "sendfile() sent only %O bytes", sent);
+
+ /*
+ * sendfile() in FreeBSD 3.x-4.x may return value >= 0
+ * on success, although only 0 is documented
+ */
+
+ } else if (rc >= 0 && sent == 0) {
+
+ /*
+ * if rc is OK and sent equal to zero, then someone
+ * has truncated the file, so the offset became beyond
+ * the end of the file
+ */
+
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "sendfile() reported that \"%s\" was truncated at %O",
+ file->file->name.data, file->file_pos);
+
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "sendfile: %d, @%O %O:%uz",
+ rc, file->file_pos, sent, file_size + header_size);
+
+ } else {
+ rc = writev(c->fd, header.elts, header.nelts);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "writev: %d of %uz", rc, header_size);
+
+ if (rc == -1) {
+ err = ngx_errno;
+
+ switch (err) {
+ case NGX_EAGAIN:
+ break;
+
+ case NGX_EINTR:
+ eintr = 1;
+ break;
+
+ default:
+ wev->error = 1;
+ ngx_connection_error(c, err, "writev() failed");
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "writev() not ready");
+ }
+
+ sent = rc > 0 ? rc : 0;
+ }
+
+ if (send - prev_send == sent) {
+ complete = 1;
+ }
+
+ c->sent += sent;
+
+ for ( /* void */ ; in; in = in->next) {
+
+ if (ngx_buf_special(in->buf)) {
+ continue;
+ }
+
+ if (sent == 0) {
+ break;
+ }
+
+ size = ngx_buf_size(in->buf);
+
+ if (sent >= size) {
+ sent -= size;
+
+ if (ngx_buf_in_memory(in->buf)) {
+ in->buf->pos = in->buf->last;
+ }
+
+ if (in->buf->in_file) {
+ in->buf->file_pos = in->buf->file_last;
+ }
+
+ continue;
+ }
+
+ if (ngx_buf_in_memory(in->buf)) {
+ in->buf->pos += (size_t) sent;
+ }
+
+ if (in->buf->in_file) {
+ in->buf->file_pos += sent;
+ }
+
+ break;
+ }
+
+#if (NGX_HAVE_AIO_SENDFILE)
+ if (c->busy_sendfile) {
+ return in;
+ }
+#endif
+
+ if (eagain) {
+
+ /*
+ * sendfile() may return EAGAIN, even if it has sent a whole file
+ * part, it indicates that the successive sendfile() call would
+ * return EAGAIN right away and would not send anything.
+ * We use it as a hint.
+ */
+
+ wev->ready = 0;
+ return in;
+ }
+
+ if (eintr) {
+ continue;
+ }
+
+ if (!complete) {
+ wev->ready = 0;
+ return in;
+ }
+
+ if (send >= limit || in == NULL) {
+ return in;
+ }
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_amd64.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_amd64.h
new file mode 100644
index 00000000000..159a2974270
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_amd64.h
@@ -0,0 +1,82 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#if (NGX_SMP)
+#define NGX_SMP_LOCK "lock;"
+#else
+#define NGX_SMP_LOCK
+#endif
+
+
+/*
+ * "cmpxchgq r, [m]":
+ *
+ * if (rax == [m]) {
+ * zf = 1;
+ * [m] = r;
+ * } else {
+ * zf = 0;
+ * rax = [m];
+ * }
+ *
+ *
+ * The "r" is any register, %rax (%r0) - %r16.
+ * The "=a" and "a" are the %rax register.
+ * Although we can return result in any register, we use "a" because it is
+ * used in cmpxchgq anyway. The result is actually in %al but not in $rax,
+ * however as the code is inlined gcc can test %al as well as %rax.
+ *
+ * The "cc" means that flags were changed.
+ */
+
+static ngx_inline ngx_atomic_uint_t
+ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
+ ngx_atomic_uint_t set)
+{
+ u_char res;
+
+ __asm__ volatile (
+
+ NGX_SMP_LOCK
+ " cmpxchgq %3, %1; "
+ " sete %0; "
+
+ : "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory");
+
+ return res;
+}
+
+
+/*
+ * "xaddq r, [m]":
+ *
+ * temp = [m];
+ * [m] += r;
+ * r = temp;
+ *
+ *
+ * The "+r" is any register, %rax (%r0) - %r16.
+ * The "cc" means that flags were changed.
+ */
+
+static ngx_inline ngx_atomic_int_t
+ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)
+{
+ __asm__ volatile (
+
+ NGX_SMP_LOCK
+ " xaddq %0, %1; "
+
+ : "+r" (add) : "m" (*value) : "cc", "memory");
+
+ return add;
+}
+
+
+#define ngx_memory_barrier() __asm__ volatile ("" ::: "memory")
+
+#define ngx_cpu_pause() __asm__ ("pause")
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_ppc.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_ppc.h
new file mode 100644
index 00000000000..45afc4b9eac
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_ppc.h
@@ -0,0 +1,155 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+/*
+ * The ppc assembler treats ";" as comment, so we have to use "\n".
+ * The minus in "bne-" is a hint for the branch prediction unit that
+ * this branch is unlikely to be taken.
+ * The "1b" means the nearest backward label "1" and the "1f" means
+ * the nearest forward label "1".
+ *
+ * The "b" means that the base registers can be used only, i.e.
+ * any register except r0. The r0 register always has a zero value and
+ * could not be used in "addi r0, r0, 1".
+ * The "=&b" means that no input registers can be used.
+ *
+ * "sync" read and write barriers
+ * "isync" read barrier, is faster than "sync"
+ * "eieio" write barrier, is faster than "sync"
+ * "lwsync" write barrier, is faster than "eieio" on ppc64
+ */
+
+#if (NGX_PTR_SIZE == 8)
+
+static ngx_inline ngx_atomic_uint_t
+ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
+ ngx_atomic_uint_t set)
+{
+ ngx_atomic_uint_t res, temp;
+
+ __asm__ volatile (
+
+ " li %0, 0 \n" /* preset "0" to "res" */
+ " lwsync \n" /* write barrier */
+ "1: \n"
+ " ldarx %1, 0, %2 \n" /* load from [lock] into "temp" */
+ /* and store reservation */
+ " cmpd %1, %3 \n" /* compare "temp" and "old" */
+ " bne- 2f \n" /* not equal */
+ " stdcx. %4, 0, %2 \n" /* store "set" into [lock] if reservation */
+ /* is not cleared */
+ " bne- 1b \n" /* the reservation was cleared */
+ " isync \n" /* read barrier */
+ " li %0, 1 \n" /* set "1" to "res" */
+ "2: \n"
+
+ : "=&b" (res), "=&b" (temp)
+ : "b" (lock), "b" (old), "b" (set)
+ : "cc", "memory");
+
+ return res;
+}
+
+
+static ngx_inline ngx_atomic_int_t
+ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)
+{
+ ngx_atomic_uint_t res, temp;
+
+ __asm__ volatile (
+
+ " lwsync \n" /* write barrier */
+ "1: ldarx %0, 0, %2 \n" /* load from [value] into "res" */
+ /* and store reservation */
+ " add %1, %0, %3 \n" /* "res" + "add" store in "temp" */
+ " stdcx. %1, 0, %2 \n" /* store "temp" into [value] if reservation */
+ /* is not cleared */
+ " bne- 1b \n" /* try again if reservation was cleared */
+ " isync \n" /* read barrier */
+
+ : "=&b" (res), "=&b" (temp)
+ : "b" (value), "b" (add)
+ : "cc", "memory");
+
+ return res;
+}
+
+
+#if (NGX_SMP)
+#define ngx_memory_barrier() \
+ __asm__ volatile ("isync \n lwsync \n" ::: "memory")
+#else
+#define ngx_memory_barrier() __asm__ volatile ("" ::: "memory")
+#endif
+
+#else
+
+static ngx_inline ngx_atomic_uint_t
+ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
+ ngx_atomic_uint_t set)
+{
+ ngx_atomic_uint_t res, temp;
+
+ __asm__ volatile (
+
+ " li %0, 0 \n" /* preset "0" to "res" */
+ " eieio \n" /* write barrier */
+ "1: \n"
+ " lwarx %1, 0, %2 \n" /* load from [lock] into "temp" */
+ /* and store reservation */
+ " cmpw %1, %3 \n" /* compare "temp" and "old" */
+ " bne- 2f \n" /* not equal */
+ " stwcx. %4, 0, %2 \n" /* store "set" into [lock] if reservation */
+ /* is not cleared */
+ " bne- 1b \n" /* the reservation was cleared */
+ " isync \n" /* read barrier */
+ " li %0, 1 \n" /* set "1" to "res" */
+ "2: \n"
+
+ : "=&b" (res), "=&b" (temp)
+ : "b" (lock), "b" (old), "b" (set)
+ : "cc", "memory");
+
+ return res;
+}
+
+
+static ngx_inline ngx_atomic_int_t
+ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)
+{
+ ngx_atomic_uint_t res, temp;
+
+ __asm__ volatile (
+
+ " eieio \n" /* write barrier */
+ "1: lwarx %0, 0, %2 \n" /* load from [value] into "res" */
+ /* and store reservation */
+ " add %1, %0, %3 \n" /* "res" + "add" store in "temp" */
+ " stwcx. %1, 0, %2 \n" /* store "temp" into [value] if reservation */
+ /* is not cleared */
+ " bne- 1b \n" /* try again if reservation was cleared */
+ " isync \n" /* read barrier */
+
+ : "=&b" (res), "=&b" (temp)
+ : "b" (value), "b" (add)
+ : "cc", "memory");
+
+ return res;
+}
+
+
+#if (NGX_SMP)
+#define ngx_memory_barrier() \
+ __asm__ volatile ("isync \n eieio \n" ::: "memory")
+#else
+#define ngx_memory_barrier() __asm__ volatile ("" ::: "memory")
+#endif
+
+#endif
+
+
+#define ngx_cpu_pause()
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_sparc64.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_sparc64.h
new file mode 100644
index 00000000000..a84db354482
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_sparc64.h
@@ -0,0 +1,82 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+/*
+ * "casa [r1] 0x80, r2, r0" and
+ * "casxa [r1] 0x80, r2, r0" do the following:
+ *
+ * if ([r1] == r2) {
+ * swap(r0, [r1]);
+ * } else {
+ * r0 = [r1];
+ * }
+ *
+ * so "r0 == r2" means that the operation was successful.
+ *
+ *
+ * The "r" means the general register.
+ * The "+r" means the general register used for both input and output.
+ */
+
+
+#if (NGX_PTR_SIZE == 4)
+#define NGX_CASA "casa"
+#else
+#define NGX_CASA "casxa"
+#endif
+
+
+static ngx_inline ngx_atomic_uint_t
+ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
+ ngx_atomic_uint_t set)
+{
+ __asm__ volatile (
+
+ NGX_CASA " [%1] 0x80, %2, %0"
+
+ : "+r" (set) : "r" (lock), "r" (old) : "memory");
+
+ return (set == old);
+}
+
+
+static ngx_inline ngx_atomic_int_t
+ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)
+{
+ ngx_atomic_uint_t old, res;
+
+ old = *value;
+
+ for ( ;; ) {
+
+ res = old + add;
+
+ __asm__ volatile (
+
+ NGX_CASA " [%1] 0x80, %2, %0"
+
+ : "+r" (res) : "r" (value), "r" (old) : "memory");
+
+ if (res == old) {
+ return res;
+ }
+
+ old = res;
+ }
+}
+
+
+#if (NGX_SMP)
+#define ngx_memory_barrier() \
+ __asm__ volatile ( \
+ "membar #LoadLoad | #LoadStore | #StoreStore | #StoreLoad" \
+ ::: "memory")
+#else
+#define ngx_memory_barrier() __asm__ volatile ("" ::: "memory")
+#endif
+
+#define ngx_cpu_pause()
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_x86.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_x86.h
new file mode 100644
index 00000000000..54e01aebf0b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_gcc_atomic_x86.h
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#if (NGX_SMP)
+#define NGX_SMP_LOCK "lock;"
+#else
+#define NGX_SMP_LOCK
+#endif
+
+
+/*
+ * "cmpxchgl r, [m]":
+ *
+ * if (eax == [m]) {
+ * zf = 1;
+ * [m] = r;
+ * } else {
+ * zf = 0;
+ * eax = [m];
+ * }
+ *
+ *
+ * The "r" means the general register.
+ * The "=a" and "a" are the %eax register.
+ * Although we can return result in any register, we use "a" because it is
+ * used in cmpxchgl anyway. The result is actually in %al but not in %eax,
+ * however, as the code is inlined gcc can test %al as well as %eax,
+ * and icc adds "movzbl %al, %eax" by itself.
+ *
+ * The "cc" means that flags were changed.
+ */
+
+static ngx_inline ngx_atomic_uint_t
+ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
+ ngx_atomic_uint_t set)
+{
+ u_char res;
+
+ __asm__ volatile (
+
+ NGX_SMP_LOCK
+ " cmpxchgl %3, %1; "
+ " sete %0; "
+
+ : "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory");
+
+ return res;
+}
+
+
+/*
+ * "xaddl r, [m]":
+ *
+ * temp = [m];
+ * [m] += r;
+ * r = temp;
+ *
+ *
+ * The "+r" means the general register.
+ * The "cc" means that flags were changed.
+ */
+
+
+#if !(( __GNUC__ == 2 && __GNUC_MINOR__ <= 7 ) || ( __INTEL_COMPILER >= 800 ))
+
+/*
+ * icc 8.1 and 9.0 compile broken code with -march=pentium4 option:
+ * ngx_atomic_fetch_add() always return the input "add" value,
+ * so we use the gcc 2.7 version.
+ *
+ * icc 8.1 and 9.0 with -march=pentiumpro option or icc 7.1 compile
+ * correct code.
+ */
+
+static ngx_inline ngx_atomic_int_t
+ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)
+{
+ __asm__ volatile (
+
+ NGX_SMP_LOCK
+ " xaddl %0, %1; "
+
+ : "+r" (add) : "m" (*value) : "cc", "memory");
+
+ return add;
+}
+
+
+#else
+
+/*
+ * gcc 2.7 does not support "+r", so we have to use the fixed
+ * %eax ("=a" and "a") and this adds two superfluous instructions in the end
+ * of code, something like this: "mov %eax, %edx / mov %edx, %eax".
+ */
+
+static ngx_inline ngx_atomic_int_t
+ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)
+{
+ ngx_atomic_uint_t old;
+
+ __asm__ volatile (
+
+ NGX_SMP_LOCK
+ " xaddl %2, %1; "
+
+ : "=a" (old) : "m" (*value), "a" (add) : "cc", "memory");
+
+ return old;
+}
+
+#endif
+
+
+/*
+ * on x86 the write operations go in a program order, so we need only
+ * to disable the gcc reorder optimizations
+ */
+
+#define ngx_memory_barrier() __asm__ volatile ("" ::: "memory")
+
+/* old "as" does not support "pause" opcode */
+#define ngx_cpu_pause() __asm__ (".byte 0xf3, 0x90")
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux.h
new file mode 100644
index 00000000000..1b8bdac51a4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux.h
@@ -0,0 +1,18 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_LINUX_H_INCLUDED_
+#define _NGX_LINUX_H_INCLUDED_
+
+
+ngx_chain_t *ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,
+ off_t limit);
+
+extern int ngx_linux_rtsig_max;
+
+
+#endif /* _NGX_LINUX_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_aio_read.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_aio_read.c
new file mode 100644
index 00000000000..8273c13f960
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_aio_read.c
@@ -0,0 +1,137 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+extern int ngx_eventfd;
+extern aio_context_t ngx_aio_ctx;
+
+
+static void ngx_file_aio_event_handler(ngx_event_t *ev);
+
+
+static int
+io_submit(aio_context_t ctx, long n, struct iocb **paiocb)
+{
+ return syscall(SYS_io_submit, ctx, n, paiocb);
+}
+
+
+ssize_t
+ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,
+ ngx_pool_t *pool)
+{
+ ngx_err_t err;
+ struct iocb *piocb[1];
+ ngx_event_t *ev;
+ ngx_event_aio_t *aio;
+
+ if (!ngx_file_aio) {
+ return ngx_read_file(file, buf, size, offset);
+ }
+
+ aio = file->aio;
+
+ if (aio == NULL) {
+ aio = ngx_pcalloc(pool, sizeof(ngx_event_aio_t));
+ if (aio == NULL) {
+ return NGX_ERROR;
+ }
+
+ aio->file = file;
+ aio->fd = file->fd;
+ aio->event.data = aio;
+ aio->event.ready = 1;
+ aio->event.log = file->log;
+ file->aio = aio;
+ }
+
+ ev = &aio->event;
+
+ if (!ev->ready) {
+ ngx_log_error(NGX_LOG_ALERT, file->log, 0,
+ "second aio post for \"%V\"", &file->name);
+ return NGX_AGAIN;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "aio complete:%d @%O:%z %V",
+ ev->complete, offset, size, &file->name);
+
+ if (ev->complete) {
+ ev->active = 0;
+ ev->complete = 0;
+
+ if (aio->res >= 0) {
+ ngx_set_errno(0);
+ return aio->res;
+ }
+
+ ngx_set_errno(-aio->res);
+
+ ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,
+ "aio read \"%s\" failed", file->name.data);
+
+ return NGX_ERROR;
+ }
+
+ ngx_memzero(&aio->aiocb, sizeof(struct iocb));
+
+ aio->aiocb.aio_data = (uint64_t) (uintptr_t) ev;
+ aio->aiocb.aio_lio_opcode = IOCB_CMD_PREAD;
+ aio->aiocb.aio_fildes = file->fd;
+ aio->aiocb.aio_buf = (uint64_t) (uintptr_t) buf;
+ aio->aiocb.aio_nbytes = size;
+ aio->aiocb.aio_offset = offset;
+ aio->aiocb.aio_flags = IOCB_FLAG_RESFD;
+ aio->aiocb.aio_resfd = ngx_eventfd;
+
+ ev->handler = ngx_file_aio_event_handler;
+
+ piocb[0] = &aio->aiocb;
+
+ if (io_submit(ngx_aio_ctx, 1, piocb) == 1) {
+ ev->active = 1;
+ ev->ready = 0;
+ ev->complete = 0;
+
+ return NGX_AGAIN;
+ }
+
+ err = ngx_errno;
+
+ if (err == NGX_EAGAIN) {
+ return ngx_read_file(file, buf, size, offset);
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, file->log, err,
+ "io_submit(\"%V\") failed", &file->name);
+
+ if (err == NGX_ENOSYS) {
+ ngx_file_aio = 0;
+ return ngx_read_file(file, buf, size, offset);
+ }
+
+ return NGX_ERROR;
+}
+
+
+static void
+ngx_file_aio_event_handler(ngx_event_t *ev)
+{
+ ngx_event_aio_t *aio;
+
+ aio = ev->data;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, ev->log, 0,
+ "aio event handler fd:%d %V", aio->fd, &aio->file->name);
+
+ aio->handler(ev);
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_config.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_config.h
new file mode 100644
index 00000000000..c6c02c93e5f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_config.h
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_LINUX_CONFIG_H_INCLUDED_
+#define _NGX_LINUX_CONFIG_H_INCLUDED_
+
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* pread(), pwrite(), gethostname() */
+#endif
+
+#define _FILE_OFFSET_BITS 64
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stddef.h> /* offsetof() */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+#include <glob.h>
+#include <sys/vfs.h> /* statfs() */
+
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h> /* TCP_NODELAY, TCP_CORK */
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/un.h>
+
+#include <time.h> /* tzset() */
+#include <malloc.h> /* memalign() */
+#include <limits.h> /* IOV_MAX */
+#include <sys/ioctl.h>
+#include <crypt.h>
+#include <sys/utsname.h> /* uname() */
+
+
+#include <ngx_auto_config.h>
+
+
+#if (NGX_HAVE_POSIX_SEM)
+#include <semaphore.h>
+#endif
+
+
+#if (NGX_HAVE_SYS_PRCTL_H)
+#include <sys/prctl.h>
+#endif
+
+
+#if (NGX_HAVE_SENDFILE64)
+#include <sys/sendfile.h>
+#else
+extern ssize_t sendfile(int s, int fd, int32_t *offset, size_t size);
+#define NGX_SENDFILE_LIMIT 0x80000000
+#endif
+
+
+#if (NGX_HAVE_POLL)
+#include <poll.h>
+#endif
+
+
+#if (NGX_HAVE_RTSIG)
+#include <poll.h>
+#include <sys/sysctl.h>
+#endif
+
+
+#if (NGX_HAVE_EPOLL)
+#include <sys/epoll.h>
+#endif
+
+
+#if (NGX_HAVE_FILE_AIO)
+#if (NGX_HAVE_SYS_EVENTFD_H)
+#include <sys/eventfd.h>
+#endif
+#include <sys/syscall.h>
+#include <linux/aio_abi.h>
+typedef struct iocb ngx_aiocb_t;
+#endif
+
+
+#define NGX_LISTEN_BACKLOG 511
+
+
+#ifndef NGX_HAVE_SO_SNDLOWAT
+/* setsockopt(SO_SNDLOWAT) returns ENOPROTOOPT */
+#define NGX_HAVE_SO_SNDLOWAT 0
+#endif
+
+
+#ifndef NGX_HAVE_INHERITED_NONBLOCK
+#define NGX_HAVE_INHERITED_NONBLOCK 0
+#endif
+
+
+#define NGX_HAVE_OS_SPECIFIC_INIT 1
+#define ngx_debug_init()
+
+
+extern char **environ;
+
+
+#endif /* _NGX_LINUX_CONFIG_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_init.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_init.c
new file mode 100644
index 00000000000..b910380d7ce
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_init.c
@@ -0,0 +1,91 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+u_char ngx_linux_kern_ostype[50];
+u_char ngx_linux_kern_osrelease[50];
+
+int ngx_linux_rtsig_max;
+
+
+static ngx_os_io_t ngx_linux_io = {
+ ngx_unix_recv,
+ ngx_readv_chain,
+ ngx_udp_unix_recv,
+ ngx_unix_send,
+#if (NGX_HAVE_SENDFILE)
+ ngx_linux_sendfile_chain,
+ NGX_IO_SENDFILE
+#else
+ ngx_writev_chain,
+ 0
+#endif
+};
+
+
+ngx_int_t
+ngx_os_specific_init(ngx_log_t *log)
+{
+ struct utsname u;
+
+ if (uname(&u) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "uname() failed");
+ return NGX_ERROR;
+ }
+
+ (void) ngx_cpystrn(ngx_linux_kern_ostype, (u_char *) u.sysname,
+ sizeof(ngx_linux_kern_ostype));
+
+ (void) ngx_cpystrn(ngx_linux_kern_osrelease, (u_char *) u.release,
+ sizeof(ngx_linux_kern_osrelease));
+
+#if (NGX_HAVE_RTSIG)
+ {
+ int name[2];
+ size_t len;
+ ngx_err_t err;
+
+ name[0] = CTL_KERN;
+ name[1] = KERN_RTSIGMAX;
+ len = sizeof(ngx_linux_rtsig_max);
+
+ if (sysctl(name, 2, &ngx_linux_rtsig_max, &len, NULL, 0) == -1) {
+ err = ngx_errno;
+
+ if (err != NGX_ENOTDIR && err != NGX_ENOSYS) {
+ ngx_log_error(NGX_LOG_ALERT, log, err,
+ "sysctl(KERN_RTSIGMAX) failed");
+
+ return NGX_ERROR;
+ }
+
+ ngx_linux_rtsig_max = 0;
+ }
+
+ }
+#endif
+
+ ngx_os_io = ngx_linux_io;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_os_specific_status(ngx_log_t *log)
+{
+ ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
+ ngx_linux_kern_ostype, ngx_linux_kern_osrelease);
+
+#if (NGX_HAVE_RTSIG)
+ ngx_log_error(NGX_LOG_NOTICE, log, 0, "sysctl(KERN_RTSIGMAX): %d",
+ ngx_linux_rtsig_max);
+#endif
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_sendfile_chain.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_sendfile_chain.c
new file mode 100644
index 00000000000..16395f94337
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_linux_sendfile_chain.c
@@ -0,0 +1,378 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+/*
+ * On Linux up to 2.4.21 sendfile() (syscall #187) works with 32-bit
+ * offsets only, and the including <sys/sendfile.h> breaks the compiling,
+ * if off_t is 64 bit wide. So we use own sendfile() definition, where offset
+ * parameter is int32_t, and use sendfile() for the file parts below 2G only,
+ * see src/os/unix/ngx_linux_config.h
+ *
+ * Linux 2.4.21 has the new sendfile64() syscall #239.
+ *
+ * On Linux up to 2.6.16 sendfile() does not allow to pass the count parameter
+ * more than 2G-1 bytes even on 64-bit platforms: it returns EINVAL,
+ * so we limit it to 2G-1 bytes.
+ */
+
+#define NGX_SENDFILE_MAXSIZE 2147483647L
+
+
+#if (IOV_MAX > 64)
+#define NGX_HEADERS 64
+#else
+#define NGX_HEADERS IOV_MAX
+#endif
+
+
+ngx_chain_t *
+ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
+{
+ int rc, tcp_nodelay;
+ off_t size, send, prev_send, aligned, sent, fprev;
+ u_char *prev;
+ size_t file_size;
+ ngx_err_t err;
+ ngx_buf_t *file;
+ ngx_uint_t eintr, complete;
+ ngx_array_t header;
+ ngx_event_t *wev;
+ ngx_chain_t *cl;
+ struct iovec *iov, headers[NGX_HEADERS];
+#if (NGX_HAVE_SENDFILE64)
+ off_t offset;
+#else
+ int32_t offset;
+#endif
+
+ wev = c->write;
+
+ if (!wev->ready) {
+ return in;
+ }
+
+
+ /* the maximum limit size is 2G-1 - the page size */
+
+ if (limit == 0 || limit > (off_t) (NGX_SENDFILE_MAXSIZE - ngx_pagesize)) {
+ limit = NGX_SENDFILE_MAXSIZE - ngx_pagesize;
+ }
+
+
+ send = 0;
+
+ header.elts = headers;
+ header.size = sizeof(struct iovec);
+ header.nalloc = NGX_HEADERS;
+ header.pool = c->pool;
+
+ for ( ;; ) {
+ file = NULL;
+ file_size = 0;
+ eintr = 0;
+ complete = 0;
+ prev_send = send;
+
+ header.nelts = 0;
+
+ prev = NULL;
+ iov = NULL;
+
+ /* create the iovec and coalesce the neighbouring bufs */
+
+ for (cl = in; cl && send < limit; cl = cl->next) {
+
+ if (ngx_buf_special(cl->buf)) {
+ continue;
+ }
+
+#if 1
+ if (!ngx_buf_in_memory(cl->buf) && !cl->buf->in_file) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "zero size buf in sendfile "
+ "t:%d r:%d f:%d %p %p-%p %p %O-%O",
+ cl->buf->temporary,
+ cl->buf->recycled,
+ cl->buf->in_file,
+ cl->buf->start,
+ cl->buf->pos,
+ cl->buf->last,
+ cl->buf->file,
+ cl->buf->file_pos,
+ cl->buf->file_last);
+
+ ngx_debug_point();
+
+ return NGX_CHAIN_ERROR;
+ }
+#endif
+
+ if (!ngx_buf_in_memory_only(cl->buf)) {
+ break;
+ }
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if (send + size > limit) {
+ size = limit - send;
+ }
+
+ if (prev == cl->buf->pos) {
+ iov->iov_len += (size_t) size;
+
+ } else {
+ if (header.nelts >= IOV_MAX) {
+ break;
+ }
+
+ iov = ngx_array_push(&header);
+ if (iov == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ iov->iov_base = (void *) cl->buf->pos;
+ iov->iov_len = (size_t) size;
+ }
+
+ prev = cl->buf->pos + (size_t) size;
+ send += size;
+ }
+
+ /* set TCP_CORK if there is a header before a file */
+
+ if (c->tcp_nopush == NGX_TCP_NOPUSH_UNSET
+ && header.nelts != 0
+ && cl
+ && cl->buf->in_file)
+ {
+ /* the TCP_CORK and TCP_NODELAY are mutually exclusive */
+
+ if (c->tcp_nodelay == NGX_TCP_NODELAY_SET) {
+
+ tcp_nodelay = 0;
+
+ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &tcp_nodelay, sizeof(int)) == -1)
+ {
+ err = ngx_socket_errno;
+
+ /*
+ * there is a tiny chance to be interrupted, however,
+ * we continue a processing with the TCP_NODELAY
+ * and without the TCP_CORK
+ */
+
+ if (err != NGX_EINTR) {
+ wev->error = 1;
+ ngx_connection_error(c, err,
+ "setsockopt(TCP_NODELAY) failed");
+ return NGX_CHAIN_ERROR;
+ }
+
+ } else {
+ c->tcp_nodelay = NGX_TCP_NODELAY_UNSET;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "no tcp_nodelay");
+ }
+ }
+
+ if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
+
+ if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
+ err = ngx_socket_errno;
+
+ /*
+ * there is a tiny chance to be interrupted, however,
+ * we continue a processing without the TCP_CORK
+ */
+
+ if (err != NGX_EINTR) {
+ wev->error = 1;
+ ngx_connection_error(c, err,
+ ngx_tcp_nopush_n " failed");
+ return NGX_CHAIN_ERROR;
+ }
+
+ } else {
+ c->tcp_nopush = NGX_TCP_NOPUSH_SET;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "tcp_nopush");
+ }
+ }
+ }
+
+ /* get the file buf */
+
+ if (header.nelts == 0 && cl && cl->buf->in_file && send < limit) {
+ file = cl->buf;
+
+ /* coalesce the neighbouring file bufs */
+
+ do {
+ size = cl->buf->file_last - cl->buf->file_pos;
+
+ if (send + size > limit) {
+ size = limit - send;
+
+ aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
+ & ~((off_t) ngx_pagesize - 1);
+
+ if (aligned <= cl->buf->file_last) {
+ size = aligned - cl->buf->file_pos;
+ }
+ }
+
+ file_size += (size_t) size;
+ send += size;
+ fprev = cl->buf->file_pos + size;
+ cl = cl->next;
+
+ } while (cl
+ && cl->buf->in_file
+ && send < limit
+ && file->file->fd == cl->buf->file->fd
+ && fprev == cl->buf->file_pos);
+ }
+
+ if (file) {
+#if 1
+ if (file_size == 0) {
+ ngx_debug_point();
+ return NGX_CHAIN_ERROR;
+ }
+#endif
+#if (NGX_HAVE_SENDFILE64)
+ offset = file->file_pos;
+#else
+ offset = (int32_t) file->file_pos;
+#endif
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "sendfile: @%O %uz", file->file_pos, file_size);
+
+ rc = sendfile(c->fd, file->file->fd, &offset, file_size);
+
+ if (rc == -1) {
+ err = ngx_errno;
+
+ switch (err) {
+ case NGX_EAGAIN:
+ break;
+
+ case NGX_EINTR:
+ eintr = 1;
+ break;
+
+ default:
+ wev->error = 1;
+ ngx_connection_error(c, err, "sendfile() failed");
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "sendfile() is not ready");
+ }
+
+ sent = rc > 0 ? rc : 0;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "sendfile: %d, @%O %O:%uz",
+ rc, file->file_pos, sent, file_size);
+
+ } else {
+ rc = writev(c->fd, header.elts, header.nelts);
+
+ if (rc == -1) {
+ err = ngx_errno;
+
+ switch (err) {
+ case NGX_EAGAIN:
+ break;
+
+ case NGX_EINTR:
+ eintr = 1;
+ break;
+
+ default:
+ wev->error = 1;
+ ngx_connection_error(c, err, "writev() failed");
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "writev() not ready");
+ }
+
+ sent = rc > 0 ? rc : 0;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "writev: %O", sent);
+ }
+
+ if (send - prev_send == sent) {
+ complete = 1;
+ }
+
+ c->sent += sent;
+
+ for ( /* void */ ; in; in = in->next) {
+
+ if (ngx_buf_special(in->buf)) {
+ continue;
+ }
+
+ if (sent == 0) {
+ break;
+ }
+
+ size = ngx_buf_size(in->buf);
+
+ if (sent >= size) {
+ sent -= size;
+
+ if (ngx_buf_in_memory(in->buf)) {
+ in->buf->pos = in->buf->last;
+ }
+
+ if (in->buf->in_file) {
+ in->buf->file_pos = in->buf->file_last;
+ }
+
+ continue;
+ }
+
+ if (ngx_buf_in_memory(in->buf)) {
+ in->buf->pos += (size_t) sent;
+ }
+
+ if (in->buf->in_file) {
+ in->buf->file_pos += sent;
+ }
+
+ break;
+ }
+
+ if (eintr) {
+ continue;
+ }
+
+ if (!complete) {
+ wev->ready = 0;
+ return in;
+ }
+
+ if (send >= limit || in == NULL) {
+ return in;
+ }
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_os.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_os.h
new file mode 100644
index 00000000000..c646e2aa5a8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_os.h
@@ -0,0 +1,83 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_OS_H_INCLUDED_
+#define _NGX_OS_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#define NGX_IO_SENDFILE 1
+
+
+typedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size);
+typedef ssize_t (*ngx_recv_chain_pt)(ngx_connection_t *c, ngx_chain_t *in);
+typedef ssize_t (*ngx_send_pt)(ngx_connection_t *c, u_char *buf, size_t size);
+typedef ngx_chain_t *(*ngx_send_chain_pt)(ngx_connection_t *c, ngx_chain_t *in,
+ off_t limit);
+
+typedef struct {
+ ngx_recv_pt recv;
+ ngx_recv_chain_pt recv_chain;
+ ngx_recv_pt udp_recv;
+ ngx_send_pt send;
+ ngx_send_chain_pt send_chain;
+ ngx_uint_t flags;
+} ngx_os_io_t;
+
+
+ngx_int_t ngx_os_init(ngx_log_t *log);
+void ngx_os_status(ngx_log_t *log);
+ngx_int_t ngx_os_specific_init(ngx_log_t *log);
+void ngx_os_specific_status(ngx_log_t *log);
+ngx_int_t ngx_daemon(ngx_log_t *log);
+ngx_int_t ngx_os_signal_process(ngx_cycle_t *cycle, char *sig, ngx_int_t pid);
+
+
+ssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size);
+ssize_t ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *entry);
+ssize_t ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size);
+ssize_t ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size);
+ngx_chain_t *ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in,
+ off_t limit);
+
+#if (NGX_HAVE_AIO)
+ssize_t ngx_aio_read(ngx_connection_t *c, u_char *buf, size_t size);
+ssize_t ngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl);
+ssize_t ngx_aio_write(ngx_connection_t *c, u_char *buf, size_t size);
+ngx_chain_t *ngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in,
+ off_t limit);
+#endif
+
+
+extern ngx_os_io_t ngx_os_io;
+extern ngx_int_t ngx_ncpu;
+extern ngx_int_t ngx_max_sockets;
+extern ngx_uint_t ngx_inherited_nonblocking;
+extern ngx_uint_t ngx_tcp_nodelay_and_tcp_nopush;
+
+
+#if (NGX_FREEBSD)
+#include <ngx_freebsd.h>
+
+
+#elif (NGX_LINUX)
+#include <ngx_linux.h>
+
+
+#elif (NGX_SOLARIS)
+#include <ngx_solaris.h>
+
+
+#elif (NGX_DARWIN)
+#include <ngx_darwin.h>
+#endif
+
+
+#endif /* _NGX_OS_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_posix_config.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_posix_config.h
new file mode 100644
index 00000000000..d725659dfac
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_posix_config.h
@@ -0,0 +1,158 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_POSIX_CONFIG_H_INCLUDED_
+#define _NGX_POSIX_CONFIG_H_INCLUDED_
+
+
+#if (NGX_HPUX)
+#define _XOPEN_SOURCE
+#define _XOPEN_SOURCE_EXTENDED 1
+#define _HPUX_ALT_XOPEN_SOCKET_API
+#endif
+
+
+#if (NGX_TRU64)
+#define _REENTRANT
+#endif
+
+
+#ifdef __CYGWIN__
+#define timezonevar /* timezone is variable */
+#define NGX_BROKEN_SCM_RIGHTS 1
+#endif
+
+
+#include <sys/types.h>
+#include <sys/time.h>
+#if (NGX_HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+#if (NGX_HAVE_INTTYPES_H)
+#include <inttypes.h>
+#endif
+#include <stdarg.h>
+#include <stddef.h> /* offsetof() */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+#include <glob.h>
+#include <time.h>
+#if (NGX_HAVE_SYS_PARAM_H)
+#include <sys/param.h> /* statfs() */
+#endif
+#if (NGX_HAVE_SYS_MOUNT_H)
+#include <sys/mount.h> /* statfs() */
+#endif
+#if (NGX_HAVE_SYS_STATVFS_H)
+#include <sys/statvfs.h> /* statvfs() */
+#endif
+
+#if (NGX_HAVE_SYS_FILIO_H)
+#include <sys/filio.h> /* FIONBIO */
+#endif
+#include <sys/ioctl.h> /* FIONBIO */
+
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h> /* TCP_NODELAY */
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/un.h>
+
+#if (NGX_HAVE_LIMITS_H)
+#include <limits.h> /* IOV_MAX */
+#endif
+
+#ifdef __CYGWIN__
+#include <malloc.h> /* memalign() */
+#endif
+
+#if (NGX_HAVE_CRYPT_H)
+#include <crypt.h>
+#endif
+
+
+#ifndef IOV_MAX
+#define IOV_MAX 16
+#endif
+
+
+#include <ngx_auto_config.h>
+
+
+#if (NGX_HAVE_POSIX_SEM)
+#include <semaphore.h>
+#endif
+
+
+#if (NGX_HAVE_POLL)
+#include <poll.h>
+#endif
+
+
+#if (NGX_HAVE_KQUEUE)
+#include <sys/event.h>
+#endif
+
+
+#if (NGX_HAVE_DEVPOLL)
+#include <sys/ioctl.h>
+#include <sys/devpoll.h>
+#endif
+
+
+#if (NGX_HAVE_FILE_AIO)
+#include <aio.h>
+typedef struct aiocb ngx_aiocb_t;
+#endif
+
+
+#define NGX_LISTEN_BACKLOG 511
+
+#define ngx_debug_init()
+
+
+#if (__FreeBSD__) && (__FreeBSD_version < 400017)
+
+#include <sys/param.h> /* ALIGN() */
+
+/*
+ * FreeBSD 3.x has no CMSG_SPACE() and CMSG_LEN() and has the broken CMSG_DATA()
+ */
+
+#undef CMSG_SPACE
+#define CMSG_SPACE(l) (ALIGN(sizeof(struct cmsghdr)) + ALIGN(l))
+
+#undef CMSG_LEN
+#define CMSG_LEN(l) (ALIGN(sizeof(struct cmsghdr)) + (l))
+
+#undef CMSG_DATA
+#define CMSG_DATA(cmsg) ((u_char *)(cmsg) + ALIGN(sizeof(struct cmsghdr)))
+
+#endif
+
+
+extern char **environ;
+
+
+#endif /* _NGX_POSIX_CONFIG_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_posix_init.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_posix_init.c
new file mode 100644
index 00000000000..9a4de022d6d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_posix_init.c
@@ -0,0 +1,128 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <nginx.h>
+
+
+ngx_int_t ngx_ncpu;
+ngx_int_t ngx_max_sockets;
+ngx_uint_t ngx_inherited_nonblocking;
+ngx_uint_t ngx_tcp_nodelay_and_tcp_nopush;
+
+
+struct rlimit rlmt;
+
+
+ngx_os_io_t ngx_os_io = {
+ ngx_unix_recv,
+ ngx_readv_chain,
+ ngx_udp_unix_recv,
+ ngx_unix_send,
+ ngx_writev_chain,
+ 0
+};
+
+
+ngx_int_t
+ngx_os_init(ngx_log_t *log)
+{
+ ngx_uint_t n;
+
+#if (NGX_HAVE_OS_SPECIFIC_INIT)
+ if (ngx_os_specific_init(log) != NGX_OK) {
+ return NGX_ERROR;
+ }
+#endif
+
+ ngx_init_setproctitle(log);
+
+ ngx_pagesize = getpagesize();
+ ngx_cacheline_size = NGX_CPU_CACHE_LINE;
+
+ for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ }
+
+#if (NGX_HAVE_SC_NPROCESSORS_ONLN)
+ if (ngx_ncpu == 0) {
+ ngx_ncpu = sysconf(_SC_NPROCESSORS_ONLN);
+ }
+#endif
+
+ if (ngx_ncpu < 1) {
+ ngx_ncpu = 1;
+ }
+
+ ngx_cpuinfo();
+
+ if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, errno,
+ "getrlimit(RLIMIT_NOFILE) failed)");
+ return NGX_ERROR;
+ }
+
+ ngx_max_sockets = (ngx_int_t) rlmt.rlim_cur;
+
+#if (NGX_HAVE_INHERITED_NONBLOCK || NGX_HAVE_ACCEPT4)
+ ngx_inherited_nonblocking = 1;
+#else
+ ngx_inherited_nonblocking = 0;
+#endif
+
+ srandom(ngx_time());
+
+ return NGX_OK;
+}
+
+
+void
+ngx_os_status(ngx_log_t *log)
+{
+ ngx_log_error(NGX_LOG_NOTICE, log, 0, NGINX_VER_BUILD);
+
+#ifdef NGX_COMPILER
+ ngx_log_error(NGX_LOG_NOTICE, log, 0, "built by " NGX_COMPILER);
+#endif
+
+#if (NGX_HAVE_OS_SPECIFIC_INIT)
+ ngx_os_specific_status(log);
+#endif
+
+ ngx_log_error(NGX_LOG_NOTICE, log, 0,
+ "getrlimit(RLIMIT_NOFILE): %r:%r",
+ rlmt.rlim_cur, rlmt.rlim_max);
+}
+
+
+#if 0
+
+ngx_int_t
+ngx_posix_post_conf_init(ngx_log_t *log)
+{
+ ngx_fd_t pp[2];
+
+ if (pipe(pp) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "pipe() failed");
+ return NGX_ERROR;
+ }
+
+ if (dup2(pp[1], STDERR_FILENO) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, errno, "dup2(STDERR) failed");
+ return NGX_ERROR;
+ }
+
+ if (pp[1] > STDERR_FILENO) {
+ if (close(pp[1]) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, errno, "close() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process.c
new file mode 100644
index 00000000000..6f3f38556ab
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process.c
@@ -0,0 +1,630 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_channel.h>
+
+
+typedef struct {
+ int signo;
+ char *signame;
+ char *name;
+ void (*handler)(int signo);
+} ngx_signal_t;
+
+
+
+static void ngx_execute_proc(ngx_cycle_t *cycle, void *data);
+static void ngx_signal_handler(int signo);
+static void ngx_process_get_status(void);
+static void ngx_unlock_mutexes(ngx_pid_t pid);
+
+
+int ngx_argc;
+char **ngx_argv;
+char **ngx_os_argv;
+
+ngx_int_t ngx_process_slot;
+ngx_socket_t ngx_channel;
+ngx_int_t ngx_last_process;
+ngx_process_t ngx_processes[NGX_MAX_PROCESSES];
+
+
+ngx_signal_t signals[] = {
+ { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
+ "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
+ "reload",
+ ngx_signal_handler },
+
+ { ngx_signal_value(NGX_REOPEN_SIGNAL),
+ "SIG" ngx_value(NGX_REOPEN_SIGNAL),
+ "reopen",
+ ngx_signal_handler },
+
+ { ngx_signal_value(NGX_NOACCEPT_SIGNAL),
+ "SIG" ngx_value(NGX_NOACCEPT_SIGNAL),
+ "",
+ ngx_signal_handler },
+
+ { ngx_signal_value(NGX_TERMINATE_SIGNAL),
+ "SIG" ngx_value(NGX_TERMINATE_SIGNAL),
+ "stop",
+ ngx_signal_handler },
+
+ { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),
+ "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),
+ "quit",
+ ngx_signal_handler },
+
+ { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
+ "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
+ "",
+ ngx_signal_handler },
+
+ { SIGALRM, "SIGALRM", "", ngx_signal_handler },
+
+ { SIGINT, "SIGINT", "", ngx_signal_handler },
+
+ { SIGIO, "SIGIO", "", ngx_signal_handler },
+
+ { SIGCHLD, "SIGCHLD", "", ngx_signal_handler },
+
+ { SIGSYS, "SIGSYS, SIG_IGN", "", SIG_IGN },
+
+ { SIGPIPE, "SIGPIPE, SIG_IGN", "", SIG_IGN },
+
+ { 0, NULL, "", NULL }
+};
+
+
+ngx_pid_t
+ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
+ char *name, ngx_int_t respawn)
+{
+ u_long on;
+ ngx_pid_t pid;
+ ngx_int_t s;
+
+ if (respawn >= 0) {
+ s = respawn;
+
+ } else {
+ for (s = 0; s < ngx_last_process; s++) {
+ if (ngx_processes[s].pid == -1) {
+ break;
+ }
+ }
+
+ if (s == NGX_MAX_PROCESSES) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "no more than %d processes can be spawned",
+ NGX_MAX_PROCESSES);
+ return NGX_INVALID_PID;
+ }
+ }
+
+
+ if (respawn != NGX_PROCESS_DETACHED) {
+
+ /* Solaris 9 still has no AF_LOCAL */
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "socketpair() failed while spawning \"%s\"", name);
+ return NGX_INVALID_PID;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+ "channel %d:%d",
+ ngx_processes[s].channel[0],
+ ngx_processes[s].channel[1]);
+
+ if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ ngx_nonblocking_n " failed while spawning \"%s\"",
+ name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+ }
+
+ if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ ngx_nonblocking_n " failed while spawning \"%s\"",
+ name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+ }
+
+ on = 1;
+ if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "ioctl(FIOASYNC) failed while spawning \"%s\"", name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+ }
+
+ if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "fcntl(F_SETOWN) failed while spawning \"%s\"", name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+ }
+
+ if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
+ name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+ }
+
+ if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
+ name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+ }
+
+ ngx_channel = ngx_processes[s].channel[1];
+
+ } else {
+ ngx_processes[s].channel[0] = -1;
+ ngx_processes[s].channel[1] = -1;
+ }
+
+ ngx_process_slot = s;
+
+
+ pid = fork();
+
+ switch (pid) {
+
+ case -1:
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "fork() failed while spawning \"%s\"", name);
+ ngx_close_channel(ngx_processes[s].channel, cycle->log);
+ return NGX_INVALID_PID;
+
+ case 0:
+ ngx_pid = ngx_getpid();
+ proc(cycle, data);
+ break;
+
+ default:
+ break;
+ }
+
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start %s %P", name, pid);
+
+ ngx_processes[s].pid = pid;
+ ngx_processes[s].exited = 0;
+
+ if (respawn >= 0) {
+ return pid;
+ }
+
+ ngx_processes[s].proc = proc;
+ ngx_processes[s].data = data;
+ ngx_processes[s].name = name;
+ ngx_processes[s].exiting = 0;
+
+ switch (respawn) {
+
+ case NGX_PROCESS_NORESPAWN:
+ ngx_processes[s].respawn = 0;
+ ngx_processes[s].just_spawn = 0;
+ ngx_processes[s].detached = 0;
+ break;
+
+ case NGX_PROCESS_JUST_SPAWN:
+ ngx_processes[s].respawn = 0;
+ ngx_processes[s].just_spawn = 1;
+ ngx_processes[s].detached = 0;
+ break;
+
+ case NGX_PROCESS_RESPAWN:
+ ngx_processes[s].respawn = 1;
+ ngx_processes[s].just_spawn = 0;
+ ngx_processes[s].detached = 0;
+ break;
+
+ case NGX_PROCESS_JUST_RESPAWN:
+ ngx_processes[s].respawn = 1;
+ ngx_processes[s].just_spawn = 1;
+ ngx_processes[s].detached = 0;
+ break;
+
+ case NGX_PROCESS_DETACHED:
+ ngx_processes[s].respawn = 0;
+ ngx_processes[s].just_spawn = 0;
+ ngx_processes[s].detached = 1;
+ break;
+ }
+
+ if (s == ngx_last_process) {
+ ngx_last_process++;
+ }
+
+ return pid;
+}
+
+
+ngx_pid_t
+ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx)
+{
+ return ngx_spawn_process(cycle, ngx_execute_proc, ctx, ctx->name,
+ NGX_PROCESS_DETACHED);
+}
+
+
+static void
+ngx_execute_proc(ngx_cycle_t *cycle, void *data)
+{
+ ngx_exec_ctx_t *ctx = data;
+
+ if (execve(ctx->path, ctx->argv, ctx->envp) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "execve() failed while executing %s \"%s\"",
+ ctx->name, ctx->path);
+ }
+
+ exit(1);
+}
+
+
+ngx_int_t
+ngx_init_signals(ngx_log_t *log)
+{
+ ngx_signal_t *sig;
+ struct sigaction sa;
+
+ for (sig = signals; sig->signo != 0; sig++) {
+ ngx_memzero(&sa, sizeof(struct sigaction));
+ sa.sa_handler = sig->handler;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(sig->signo, &sa, NULL) == -1) {
+#if (NGX_VALGRIND)
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ "sigaction(%s) failed, ignored", sig->signame);
+#else
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ "sigaction(%s) failed", sig->signame);
+ return NGX_ERROR;
+#endif
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_signal_handler(int signo)
+{
+ char *action;
+ ngx_int_t ignore;
+ ngx_err_t err;
+ ngx_signal_t *sig;
+
+ ignore = 0;
+
+ err = ngx_errno;
+
+ for (sig = signals; sig->signo != 0; sig++) {
+ if (sig->signo == signo) {
+ break;
+ }
+ }
+
+ ngx_time_sigsafe_update();
+
+ action = "";
+
+ switch (ngx_process) {
+
+ case NGX_PROCESS_MASTER:
+ case NGX_PROCESS_SINGLE:
+ switch (signo) {
+
+ case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
+ ngx_quit = 1;
+ action = ", shutting down";
+ break;
+
+ case ngx_signal_value(NGX_TERMINATE_SIGNAL):
+ case SIGINT:
+ ngx_terminate = 1;
+ action = ", exiting";
+ break;
+
+ case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
+ if (ngx_daemonized) {
+ ngx_noaccept = 1;
+ action = ", stop accepting connections";
+ }
+ break;
+
+ case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
+ ngx_reconfigure = 1;
+ action = ", reconfiguring";
+ break;
+
+ case ngx_signal_value(NGX_REOPEN_SIGNAL):
+ ngx_reopen = 1;
+ action = ", reopening logs";
+ break;
+
+ case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
+ if (getppid() > 1 || ngx_new_binary > 0) {
+
+ /*
+ * Ignore the signal in the new binary if its parent is
+ * not the init process, i.e. the old binary's process
+ * is still running. Or ignore the signal in the old binary's
+ * process if the new binary's process is already running.
+ */
+
+ action = ", ignoring";
+ ignore = 1;
+ break;
+ }
+
+ ngx_change_binary = 1;
+ action = ", changing binary";
+ break;
+
+ case SIGALRM:
+ ngx_sigalrm = 1;
+ break;
+
+ case SIGIO:
+ ngx_sigio = 1;
+ break;
+
+ case SIGCHLD:
+ ngx_reap = 1;
+ break;
+ }
+
+ break;
+
+ case NGX_PROCESS_WORKER:
+ case NGX_PROCESS_HELPER:
+ switch (signo) {
+
+ case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
+ if (!ngx_daemonized) {
+ break;
+ }
+ ngx_debug_quit = 1;
+ case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
+ ngx_quit = 1;
+ action = ", shutting down";
+ break;
+
+ case ngx_signal_value(NGX_TERMINATE_SIGNAL):
+ case SIGINT:
+ ngx_terminate = 1;
+ action = ", exiting";
+ break;
+
+ case ngx_signal_value(NGX_REOPEN_SIGNAL):
+ ngx_reopen = 1;
+ action = ", reopening logs";
+ break;
+
+ case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
+ case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
+ case SIGIO:
+ action = ", ignoring";
+ break;
+ }
+
+ break;
+ }
+
+ ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
+ "signal %d (%s) received%s", signo, sig->signame, action);
+
+ if (ignore) {
+ ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0,
+ "the changing binary signal is ignored: "
+ "you should shutdown or terminate "
+ "before either old or new binary's process");
+ }
+
+ if (signo == SIGCHLD) {
+ ngx_process_get_status();
+ }
+
+ ngx_set_errno(err);
+}
+
+
+static void
+ngx_process_get_status(void)
+{
+ int status;
+ char *process;
+ ngx_pid_t pid;
+ ngx_err_t err;
+ ngx_int_t i;
+ ngx_uint_t one;
+
+ one = 0;
+
+ for ( ;; ) {
+ pid = waitpid(-1, &status, WNOHANG);
+
+ if (pid == 0) {
+ return;
+ }
+
+ if (pid == -1) {
+ err = ngx_errno;
+
+ if (err == NGX_EINTR) {
+ continue;
+ }
+
+ if (err == NGX_ECHILD && one) {
+ return;
+ }
+
+ /*
+ * Solaris always calls the signal handler for each exited process
+ * despite waitpid() may be already called for this process.
+ *
+ * When several processes exit at the same time FreeBSD may
+ * erroneously call the signal handler for exited process
+ * despite waitpid() may be already called for this process.
+ */
+
+ if (err == NGX_ECHILD) {
+ ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, err,
+ "waitpid() failed");
+ return;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,
+ "waitpid() failed");
+ return;
+ }
+
+
+ one = 1;
+ process = "unknown process";
+
+ for (i = 0; i < ngx_last_process; i++) {
+ if (ngx_processes[i].pid == pid) {
+ ngx_processes[i].status = status;
+ ngx_processes[i].exited = 1;
+ process = ngx_processes[i].name;
+ break;
+ }
+ }
+
+ if (WTERMSIG(status)) {
+#ifdef WCOREDUMP
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "%s %P exited on signal %d%s",
+ process, pid, WTERMSIG(status),
+ WCOREDUMP(status) ? " (core dumped)" : "");
+#else
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "%s %P exited on signal %d",
+ process, pid, WTERMSIG(status));
+#endif
+
+ } else {
+ ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
+ "%s %P exited with code %d",
+ process, pid, WEXITSTATUS(status));
+ }
+
+ if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "%s %P exited with fatal code %d "
+ "and cannot be respawned",
+ process, pid, WEXITSTATUS(status));
+ ngx_processes[i].respawn = 0;
+ }
+
+ ngx_unlock_mutexes(pid);
+ }
+}
+
+
+static void
+ngx_unlock_mutexes(ngx_pid_t pid)
+{
+ ngx_uint_t i;
+ ngx_shm_zone_t *shm_zone;
+ ngx_list_part_t *part;
+ ngx_slab_pool_t *sp;
+
+ /*
+ * unlock the accept mutex if the abnormally exited process
+ * held it
+ */
+
+ if (ngx_accept_mutex_ptr) {
+ (void) ngx_shmtx_force_unlock(&ngx_accept_mutex, pid);
+ }
+
+ /*
+ * unlock shared memory mutexes if held by the abnormally exited
+ * process
+ */
+
+ part = (ngx_list_part_t *) &ngx_cycle->shared_memory.part;
+ shm_zone = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+ part = part->next;
+ shm_zone = part->elts;
+ i = 0;
+ }
+
+ sp = (ngx_slab_pool_t *) shm_zone[i].shm.addr;
+
+ if (ngx_shmtx_force_unlock(&sp->mutex, pid)) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "shared memory zone \"%V\" was locked by %P",
+ &shm_zone[i].shm.name, pid);
+ }
+ }
+}
+
+
+void
+ngx_debug_point(void)
+{
+ ngx_core_conf_t *ccf;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
+ ngx_core_module);
+
+ switch (ccf->debug_points) {
+
+ case NGX_DEBUG_POINTS_STOP:
+ raise(SIGSTOP);
+ break;
+
+ case NGX_DEBUG_POINTS_ABORT:
+ ngx_abort();
+ }
+}
+
+
+ngx_int_t
+ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_int_t pid)
+{
+ ngx_signal_t *sig;
+
+ for (sig = signals; sig->signo != 0; sig++) {
+ if (ngx_strcmp(name, sig->name) == 0) {
+ if (kill(pid, sig->signo) != -1) {
+ return 0;
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "kill(%P, %d) failed", pid, sig->signo);
+ }
+ }
+
+ return 1;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process.h
new file mode 100644
index 00000000000..7b5e8c0c25e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process.h
@@ -0,0 +1,88 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_PROCESS_H_INCLUDED_
+#define _NGX_PROCESS_H_INCLUDED_
+
+
+#include <ngx_setaffinity.h>
+#include <ngx_setproctitle.h>
+
+
+typedef pid_t ngx_pid_t;
+
+#define NGX_INVALID_PID -1
+
+typedef void (*ngx_spawn_proc_pt) (ngx_cycle_t *cycle, void *data);
+
+typedef struct {
+ ngx_pid_t pid;
+ int status;
+ ngx_socket_t channel[2];
+
+ ngx_spawn_proc_pt proc;
+ void *data;
+ char *name;
+
+ unsigned respawn:1;
+ unsigned just_spawn:1;
+ unsigned detached:1;
+ unsigned exiting:1;
+ unsigned exited:1;
+} ngx_process_t;
+
+
+typedef struct {
+ char *path;
+ char *name;
+ char *const *argv;
+ char *const *envp;
+} ngx_exec_ctx_t;
+
+
+#define NGX_MAX_PROCESSES 1024
+
+#define NGX_PROCESS_NORESPAWN -1
+#define NGX_PROCESS_JUST_SPAWN -2
+#define NGX_PROCESS_RESPAWN -3
+#define NGX_PROCESS_JUST_RESPAWN -4
+#define NGX_PROCESS_DETACHED -5
+
+
+#define ngx_getpid getpid
+
+#ifndef ngx_log_pid
+#define ngx_log_pid ngx_pid
+#endif
+
+
+ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle,
+ ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn);
+ngx_pid_t ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx);
+ngx_int_t ngx_init_signals(ngx_log_t *log);
+void ngx_debug_point(void);
+
+
+#if (NGX_HAVE_SCHED_YIELD)
+#define ngx_sched_yield() sched_yield()
+#else
+#define ngx_sched_yield() usleep(1)
+#endif
+
+
+extern int ngx_argc;
+extern char **ngx_argv;
+extern char **ngx_os_argv;
+
+extern ngx_pid_t ngx_pid;
+extern ngx_socket_t ngx_channel;
+extern ngx_int_t ngx_process_slot;
+extern ngx_int_t ngx_last_process;
+extern ngx_process_t ngx_processes[NGX_MAX_PROCESSES];
+
+
+#endif /* _NGX_PROCESS_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process_cycle.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process_cycle.c
new file mode 100644
index 00000000000..3205aa5554c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process_cycle.c
@@ -0,0 +1,1413 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_channel.h>
+
+
+static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n,
+ ngx_int_t type);
+static void ngx_start_cache_manager_processes(ngx_cycle_t *cycle,
+ ngx_uint_t respawn);
+static void ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch);
+static void ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo);
+static ngx_uint_t ngx_reap_children(ngx_cycle_t *cycle);
+static void ngx_master_process_exit(ngx_cycle_t *cycle);
+static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data);
+static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker);
+static void ngx_worker_process_exit(ngx_cycle_t *cycle);
+static void ngx_channel_handler(ngx_event_t *ev);
+#if (NGX_THREADS)
+static void ngx_wakeup_worker_threads(ngx_cycle_t *cycle);
+static ngx_thread_value_t ngx_worker_thread_cycle(void *data);
+#endif
+static void ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data);
+static void ngx_cache_manager_process_handler(ngx_event_t *ev);
+static void ngx_cache_loader_process_handler(ngx_event_t *ev);
+
+
+ngx_uint_t ngx_process;
+ngx_pid_t ngx_pid;
+ngx_uint_t ngx_threaded;
+
+sig_atomic_t ngx_reap;
+sig_atomic_t ngx_sigio;
+sig_atomic_t ngx_sigalrm;
+sig_atomic_t ngx_terminate;
+sig_atomic_t ngx_quit;
+sig_atomic_t ngx_debug_quit;
+ngx_uint_t ngx_exiting;
+sig_atomic_t ngx_reconfigure;
+sig_atomic_t ngx_reopen;
+
+sig_atomic_t ngx_change_binary;
+ngx_pid_t ngx_new_binary;
+ngx_uint_t ngx_inherited;
+ngx_uint_t ngx_daemonized;
+
+sig_atomic_t ngx_noaccept;
+ngx_uint_t ngx_noaccepting;
+ngx_uint_t ngx_restart;
+
+
+#if (NGX_THREADS)
+volatile ngx_thread_t ngx_threads[NGX_MAX_THREADS];
+ngx_int_t ngx_threads_n;
+#endif
+
+
+static u_char master_process[] = "master process";
+
+
+static ngx_cache_manager_ctx_t ngx_cache_manager_ctx = {
+ ngx_cache_manager_process_handler, "cache manager process", 0
+};
+
+static ngx_cache_manager_ctx_t ngx_cache_loader_ctx = {
+ ngx_cache_loader_process_handler, "cache loader process", 60000
+};
+
+
+static ngx_cycle_t ngx_exit_cycle;
+static ngx_log_t ngx_exit_log;
+static ngx_open_file_t ngx_exit_log_file;
+
+
+void
+ngx_master_process_cycle(ngx_cycle_t *cycle)
+{
+ char *title;
+ u_char *p;
+ size_t size;
+ ngx_int_t i;
+ ngx_uint_t n, sigio;
+ sigset_t set;
+ struct itimerval itv;
+ ngx_uint_t live;
+ ngx_msec_t delay;
+ ngx_listening_t *ls;
+ ngx_core_conf_t *ccf;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGCHLD);
+ sigaddset(&set, SIGALRM);
+ sigaddset(&set, SIGIO);
+ sigaddset(&set, SIGINT);
+ sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));
+ sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));
+ sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));
+ sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));
+ sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
+ sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));
+
+ if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "sigprocmask() failed");
+ }
+
+ sigemptyset(&set);
+
+
+ size = sizeof(master_process);
+
+ for (i = 0; i < ngx_argc; i++) {
+ size += ngx_strlen(ngx_argv[i]) + 1;
+ }
+
+ title = ngx_pnalloc(cycle->pool, size);
+ if (title == NULL) {
+ /* fatal */
+ exit(2);
+ }
+
+ p = ngx_cpymem(title, master_process, sizeof(master_process) - 1);
+ for (i = 0; i < ngx_argc; i++) {
+ *p++ = ' ';
+ p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size);
+ }
+
+ ngx_setproctitle(title);
+
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+ ngx_start_worker_processes(cycle, ccf->worker_processes,
+ NGX_PROCESS_RESPAWN);
+ ngx_start_cache_manager_processes(cycle, 0);
+
+ ngx_new_binary = 0;
+ delay = 0;
+ sigio = 0;
+ live = 1;
+
+ for ( ;; ) {
+ if (delay) {
+ if (ngx_sigalrm) {
+ sigio = 0;
+ delay *= 2;
+ ngx_sigalrm = 0;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "termination cycle: %d", delay);
+
+ itv.it_interval.tv_sec = 0;
+ itv.it_interval.tv_usec = 0;
+ itv.it_value.tv_sec = delay / 1000;
+ itv.it_value.tv_usec = (delay % 1000 ) * 1000;
+
+ if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "setitimer() failed");
+ }
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend");
+
+ sigsuspend(&set);
+
+ ngx_time_update();
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "wake up, sigio %i", sigio);
+
+ if (ngx_reap) {
+ ngx_reap = 0;
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
+
+ live = ngx_reap_children(cycle);
+ }
+
+ if (!live && (ngx_terminate || ngx_quit)) {
+ ngx_master_process_exit(cycle);
+ }
+
+ if (ngx_terminate) {
+ if (delay == 0) {
+ delay = 50;
+ }
+
+ if (sigio) {
+ sigio--;
+ continue;
+ }
+
+ sigio = ccf->worker_processes + 2 /* cache processes */;
+
+ if (delay > 1000) {
+ ngx_signal_worker_processes(cycle, SIGKILL);
+ } else {
+ ngx_signal_worker_processes(cycle,
+ ngx_signal_value(NGX_TERMINATE_SIGNAL));
+ }
+
+ continue;
+ }
+
+ if (ngx_quit) {
+ ngx_signal_worker_processes(cycle,
+ ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
+
+ ls = cycle->listening.elts;
+ for (n = 0; n < cycle->listening.nelts; n++) {
+ if (ngx_close_socket(ls[n].fd) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
+ ngx_close_socket_n " %V failed",
+ &ls[n].addr_text);
+ }
+ }
+ cycle->listening.nelts = 0;
+
+ continue;
+ }
+
+ if (ngx_reconfigure) {
+ ngx_reconfigure = 0;
+
+ if (ngx_new_binary) {
+ ngx_start_worker_processes(cycle, ccf->worker_processes,
+ NGX_PROCESS_RESPAWN);
+ ngx_start_cache_manager_processes(cycle, 0);
+ ngx_noaccepting = 0;
+
+ continue;
+ }
+
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");
+
+ cycle = ngx_init_cycle(cycle);
+ if (cycle == NULL) {
+ cycle = (ngx_cycle_t *) ngx_cycle;
+ continue;
+ }
+
+ ngx_cycle = cycle;
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
+ ngx_core_module);
+ ngx_start_worker_processes(cycle, ccf->worker_processes,
+ NGX_PROCESS_JUST_RESPAWN);
+ ngx_start_cache_manager_processes(cycle, 1);
+
+ /* allow new processes to start */
+ ngx_msleep(100);
+
+ live = 1;
+ ngx_signal_worker_processes(cycle,
+ ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
+ }
+
+ if (ngx_restart) {
+ ngx_restart = 0;
+ ngx_start_worker_processes(cycle, ccf->worker_processes,
+ NGX_PROCESS_RESPAWN);
+ ngx_start_cache_manager_processes(cycle, 0);
+ live = 1;
+ }
+
+ if (ngx_reopen) {
+ ngx_reopen = 0;
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
+ ngx_reopen_files(cycle, ccf->user);
+ ngx_signal_worker_processes(cycle,
+ ngx_signal_value(NGX_REOPEN_SIGNAL));
+ }
+
+ if (ngx_change_binary) {
+ ngx_change_binary = 0;
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary");
+ ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);
+ }
+
+ if (ngx_noaccept) {
+ ngx_noaccept = 0;
+ ngx_noaccepting = 1;
+ ngx_signal_worker_processes(cycle,
+ ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
+ }
+ }
+}
+
+
+void
+ngx_single_process_cycle(ngx_cycle_t *cycle)
+{
+ ngx_uint_t i;
+
+ if (ngx_set_environment(cycle, NULL) == NULL) {
+ /* fatal */
+ exit(2);
+ }
+
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->init_process) {
+ if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
+ /* fatal */
+ exit(2);
+ }
+ }
+ }
+
+ for ( ;; ) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");
+
+ ngx_process_events_and_timers(cycle);
+
+ if (ngx_terminate || ngx_quit) {
+
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->exit_process) {
+ ngx_modules[i]->exit_process(cycle);
+ }
+ }
+
+ ngx_master_process_exit(cycle);
+ }
+
+ if (ngx_reconfigure) {
+ ngx_reconfigure = 0;
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");
+
+ cycle = ngx_init_cycle(cycle);
+ if (cycle == NULL) {
+ cycle = (ngx_cycle_t *) ngx_cycle;
+ continue;
+ }
+
+ ngx_cycle = cycle;
+ }
+
+ if (ngx_reopen) {
+ ngx_reopen = 0;
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
+ ngx_reopen_files(cycle, (ngx_uid_t) -1);
+ }
+ }
+}
+
+
+static void
+ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
+{
+ ngx_int_t i;
+ ngx_channel_t ch;
+
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes");
+
+ ngx_memzero(&ch, sizeof(ngx_channel_t));
+
+ ch.command = NGX_CMD_OPEN_CHANNEL;
+
+ for (i = 0; i < n; i++) {
+
+ ngx_spawn_process(cycle, ngx_worker_process_cycle,
+ (void *) (intptr_t) i, "worker process", type);
+
+ ch.pid = ngx_processes[ngx_process_slot].pid;
+ ch.slot = ngx_process_slot;
+ ch.fd = ngx_processes[ngx_process_slot].channel[0];
+
+ ngx_pass_open_channel(cycle, &ch);
+ }
+}
+
+
+static void
+ngx_start_cache_manager_processes(ngx_cycle_t *cycle, ngx_uint_t respawn)
+{
+ ngx_uint_t i, manager, loader;
+ ngx_path_t **path;
+ ngx_channel_t ch;
+
+ manager = 0;
+ loader = 0;
+
+ path = ngx_cycle->paths.elts;
+ for (i = 0; i < ngx_cycle->paths.nelts; i++) {
+
+ if (path[i]->manager) {
+ manager = 1;
+ }
+
+ if (path[i]->loader) {
+ loader = 1;
+ }
+ }
+
+ if (manager == 0) {
+ return;
+ }
+
+ ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,
+ &ngx_cache_manager_ctx, "cache manager process",
+ respawn ? NGX_PROCESS_JUST_RESPAWN : NGX_PROCESS_RESPAWN);
+
+ ngx_memzero(&ch, sizeof(ngx_channel_t));
+
+ ch.command = NGX_CMD_OPEN_CHANNEL;
+ ch.pid = ngx_processes[ngx_process_slot].pid;
+ ch.slot = ngx_process_slot;
+ ch.fd = ngx_processes[ngx_process_slot].channel[0];
+
+ ngx_pass_open_channel(cycle, &ch);
+
+ if (loader == 0) {
+ return;
+ }
+
+ ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,
+ &ngx_cache_loader_ctx, "cache loader process",
+ respawn ? NGX_PROCESS_JUST_SPAWN : NGX_PROCESS_NORESPAWN);
+
+ ch.command = NGX_CMD_OPEN_CHANNEL;
+ ch.pid = ngx_processes[ngx_process_slot].pid;
+ ch.slot = ngx_process_slot;
+ ch.fd = ngx_processes[ngx_process_slot].channel[0];
+
+ ngx_pass_open_channel(cycle, &ch);
+}
+
+
+static void
+ngx_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch)
+{
+ ngx_int_t i;
+
+ for (i = 0; i < ngx_last_process; i++) {
+
+ if (i == ngx_process_slot
+ || ngx_processes[i].pid == -1
+ || ngx_processes[i].channel[0] == -1)
+ {
+ continue;
+ }
+
+ ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+ "pass channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d",
+ ch->slot, ch->pid, ch->fd,
+ i, ngx_processes[i].pid,
+ ngx_processes[i].channel[0]);
+
+ /* TODO: NGX_AGAIN */
+
+ ngx_write_channel(ngx_processes[i].channel[0],
+ ch, sizeof(ngx_channel_t), cycle->log);
+ }
+}
+
+
+static void
+ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo)
+{
+ ngx_int_t i;
+ ngx_err_t err;
+ ngx_channel_t ch;
+
+ ngx_memzero(&ch, sizeof(ngx_channel_t));
+
+#if (NGX_BROKEN_SCM_RIGHTS)
+
+ ch.command = 0;
+
+#else
+
+ switch (signo) {
+
+ case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
+ ch.command = NGX_CMD_QUIT;
+ break;
+
+ case ngx_signal_value(NGX_TERMINATE_SIGNAL):
+ ch.command = NGX_CMD_TERMINATE;
+ break;
+
+ case ngx_signal_value(NGX_REOPEN_SIGNAL):
+ ch.command = NGX_CMD_REOPEN;
+ break;
+
+ default:
+ ch.command = 0;
+ }
+
+#endif
+
+ ch.fd = -1;
+
+
+ for (i = 0; i < ngx_last_process; i++) {
+
+ ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "child: %d %P e:%d t:%d d:%d r:%d j:%d",
+ i,
+ ngx_processes[i].pid,
+ ngx_processes[i].exiting,
+ ngx_processes[i].exited,
+ ngx_processes[i].detached,
+ ngx_processes[i].respawn,
+ ngx_processes[i].just_spawn);
+
+ if (ngx_processes[i].detached || ngx_processes[i].pid == -1) {
+ continue;
+ }
+
+ if (ngx_processes[i].just_spawn) {
+ ngx_processes[i].just_spawn = 0;
+ continue;
+ }
+
+ if (ngx_processes[i].exiting
+ && signo == ngx_signal_value(NGX_SHUTDOWN_SIGNAL))
+ {
+ continue;
+ }
+
+ if (ch.command) {
+ if (ngx_write_channel(ngx_processes[i].channel[0],
+ &ch, sizeof(ngx_channel_t), cycle->log)
+ == NGX_OK)
+ {
+ if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {
+ ngx_processes[i].exiting = 1;
+ }
+
+ continue;
+ }
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+ "kill (%P, %d)", ngx_processes[i].pid, signo);
+
+ if (kill(ngx_processes[i].pid, signo) == -1) {
+ err = ngx_errno;
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+ "kill(%P, %d) failed", ngx_processes[i].pid, signo);
+
+ if (err == NGX_ESRCH) {
+ ngx_processes[i].exited = 1;
+ ngx_processes[i].exiting = 0;
+ ngx_reap = 1;
+ }
+
+ continue;
+ }
+
+ if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {
+ ngx_processes[i].exiting = 1;
+ }
+ }
+}
+
+
+static ngx_uint_t
+ngx_reap_children(ngx_cycle_t *cycle)
+{
+ ngx_int_t i, n;
+ ngx_uint_t live;
+ ngx_channel_t ch;
+ ngx_core_conf_t *ccf;
+
+ ngx_memzero(&ch, sizeof(ngx_channel_t));
+
+ ch.command = NGX_CMD_CLOSE_CHANNEL;
+ ch.fd = -1;
+
+ live = 0;
+ for (i = 0; i < ngx_last_process; i++) {
+
+ ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+ "child: %d %P e:%d t:%d d:%d r:%d j:%d",
+ i,
+ ngx_processes[i].pid,
+ ngx_processes[i].exiting,
+ ngx_processes[i].exited,
+ ngx_processes[i].detached,
+ ngx_processes[i].respawn,
+ ngx_processes[i].just_spawn);
+
+ if (ngx_processes[i].pid == -1) {
+ continue;
+ }
+
+ if (ngx_processes[i].exited) {
+
+ if (!ngx_processes[i].detached) {
+ ngx_close_channel(ngx_processes[i].channel, cycle->log);
+
+ ngx_processes[i].channel[0] = -1;
+ ngx_processes[i].channel[1] = -1;
+
+ ch.pid = ngx_processes[i].pid;
+ ch.slot = i;
+
+ for (n = 0; n < ngx_last_process; n++) {
+ if (ngx_processes[n].exited
+ || ngx_processes[n].pid == -1
+ || ngx_processes[n].channel[0] == -1)
+ {
+ continue;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+ "pass close channel s:%i pid:%P to:%P",
+ ch.slot, ch.pid, ngx_processes[n].pid);
+
+ /* TODO: NGX_AGAIN */
+
+ ngx_write_channel(ngx_processes[n].channel[0],
+ &ch, sizeof(ngx_channel_t), cycle->log);
+ }
+ }
+
+ if (ngx_processes[i].respawn
+ && !ngx_processes[i].exiting
+ && !ngx_terminate
+ && !ngx_quit)
+ {
+ if (ngx_spawn_process(cycle, ngx_processes[i].proc,
+ ngx_processes[i].data,
+ ngx_processes[i].name, i)
+ == NGX_INVALID_PID)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "could not respawn %s",
+ ngx_processes[i].name);
+ continue;
+ }
+
+
+ ch.command = NGX_CMD_OPEN_CHANNEL;
+ ch.pid = ngx_processes[ngx_process_slot].pid;
+ ch.slot = ngx_process_slot;
+ ch.fd = ngx_processes[ngx_process_slot].channel[0];
+
+ ngx_pass_open_channel(cycle, &ch);
+
+ live = 1;
+
+ continue;
+ }
+
+ if (ngx_processes[i].pid == ngx_new_binary) {
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
+ ngx_core_module);
+
+ if (ngx_rename_file((char *) ccf->oldpid.data,
+ (char *) ccf->pid.data)
+ == NGX_FILE_ERROR)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ ngx_rename_file_n " %s back to %s failed "
+ "after the new binary process \"%s\" exited",
+ ccf->oldpid.data, ccf->pid.data, ngx_argv[0]);
+ }
+
+ ngx_new_binary = 0;
+ if (ngx_noaccepting) {
+ ngx_restart = 1;
+ ngx_noaccepting = 0;
+ }
+ }
+
+ if (i == ngx_last_process - 1) {
+ ngx_last_process--;
+
+ } else {
+ ngx_processes[i].pid = -1;
+ }
+
+ } else if (ngx_processes[i].exiting || !ngx_processes[i].detached) {
+ live = 1;
+ }
+ }
+
+ return live;
+}
+
+
+static void
+ngx_master_process_exit(ngx_cycle_t *cycle)
+{
+ ngx_uint_t i;
+
+ ngx_delete_pidfile(cycle);
+
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exit");
+
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->exit_master) {
+ ngx_modules[i]->exit_master(cycle);
+ }
+ }
+
+ ngx_close_listening_sockets(cycle);
+
+ /*
+ * Copy ngx_cycle->log related data to the special static exit cycle,
+ * log, and log file structures enough to allow a signal handler to log.
+ * The handler may be called when standard ngx_cycle->log allocated from
+ * ngx_cycle->pool is already destroyed.
+ */
+
+
+ ngx_exit_log = *ngx_log_get_file_log(ngx_cycle->log);
+
+ ngx_exit_log_file.fd = ngx_exit_log.file->fd;
+ ngx_exit_log.file = &ngx_exit_log_file;
+ ngx_exit_log.next = NULL;
+ ngx_exit_log.writer = NULL;
+
+ ngx_exit_cycle.log = &ngx_exit_log;
+ ngx_exit_cycle.files = ngx_cycle->files;
+ ngx_exit_cycle.files_n = ngx_cycle->files_n;
+ ngx_cycle = &ngx_exit_cycle;
+
+ ngx_destroy_pool(cycle->pool);
+
+ exit(0);
+}
+
+
+static void
+ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
+{
+ ngx_int_t worker = (intptr_t) data;
+
+ ngx_uint_t i;
+ ngx_connection_t *c;
+
+ ngx_process = NGX_PROCESS_WORKER;
+
+ ngx_worker_process_init(cycle, worker);
+
+ ngx_setproctitle("worker process");
+
+#if (NGX_THREADS)
+ {
+ ngx_int_t n;
+ ngx_err_t err;
+ ngx_core_conf_t *ccf;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+ if (ngx_threads_n) {
+ if (ngx_init_threads(ngx_threads_n, ccf->thread_stack_size, cycle)
+ == NGX_ERROR)
+ {
+ /* fatal */
+ exit(2);
+ }
+
+ err = ngx_thread_key_create(&ngx_core_tls_key);
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+ ngx_thread_key_create_n " failed");
+ /* fatal */
+ exit(2);
+ }
+
+ for (n = 0; n < ngx_threads_n; n++) {
+
+ ngx_threads[n].cv = ngx_cond_init(cycle->log);
+
+ if (ngx_threads[n].cv == NULL) {
+ /* fatal */
+ exit(2);
+ }
+
+ if (ngx_create_thread((ngx_tid_t *) &ngx_threads[n].tid,
+ ngx_worker_thread_cycle,
+ (void *) &ngx_threads[n], cycle->log)
+ != 0)
+ {
+ /* fatal */
+ exit(2);
+ }
+ }
+ }
+ }
+#endif
+
+ for ( ;; ) {
+
+ if (ngx_exiting) {
+
+ c = cycle->connections;
+
+ for (i = 0; i < cycle->connection_n; i++) {
+
+ /* THREAD: lock */
+
+ if (c[i].fd != -1 && c[i].idle) {
+ c[i].close = 1;
+ c[i].read->handler(c[i].read);
+ }
+ }
+
+ if (ngx_event_timer_rbtree.root == ngx_event_timer_rbtree.sentinel)
+ {
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
+
+ ngx_worker_process_exit(cycle);
+ }
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");
+
+ ngx_process_events_and_timers(cycle);
+
+ if (ngx_terminate) {
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
+
+ ngx_worker_process_exit(cycle);
+ }
+
+ if (ngx_quit) {
+ ngx_quit = 0;
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
+ "gracefully shutting down");
+ ngx_setproctitle("worker process is shutting down");
+
+ if (!ngx_exiting) {
+ ngx_close_listening_sockets(cycle);
+ ngx_exiting = 1;
+ }
+ }
+
+ if (ngx_reopen) {
+ ngx_reopen = 0;
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
+ ngx_reopen_files(cycle, -1);
+ }
+ }
+}
+
+
+static void
+ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)
+{
+ sigset_t set;
+ uint64_t cpu_affinity;
+ ngx_int_t n;
+ ngx_uint_t i;
+ struct rlimit rlmt;
+ ngx_core_conf_t *ccf;
+ ngx_listening_t *ls;
+
+ if (ngx_set_environment(cycle, NULL) == NULL) {
+ /* fatal */
+ exit(2);
+ }
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+ if (worker >= 0 && ccf->priority != 0) {
+ if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "setpriority(%d) failed", ccf->priority);
+ }
+ }
+
+ if (ccf->rlimit_nofile != NGX_CONF_UNSET) {
+ rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile;
+ rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile;
+
+ if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "setrlimit(RLIMIT_NOFILE, %i) failed",
+ ccf->rlimit_nofile);
+ }
+ }
+
+ if (ccf->rlimit_core != NGX_CONF_UNSET) {
+ rlmt.rlim_cur = (rlim_t) ccf->rlimit_core;
+ rlmt.rlim_max = (rlim_t) ccf->rlimit_core;
+
+ if (setrlimit(RLIMIT_CORE, &rlmt) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "setrlimit(RLIMIT_CORE, %O) failed",
+ ccf->rlimit_core);
+ }
+ }
+
+#ifdef RLIMIT_SIGPENDING
+ if (ccf->rlimit_sigpending != NGX_CONF_UNSET) {
+ rlmt.rlim_cur = (rlim_t) ccf->rlimit_sigpending;
+ rlmt.rlim_max = (rlim_t) ccf->rlimit_sigpending;
+
+ if (setrlimit(RLIMIT_SIGPENDING, &rlmt) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "setrlimit(RLIMIT_SIGPENDING, %i) failed",
+ ccf->rlimit_sigpending);
+ }
+ }
+#endif
+
+ if (geteuid() == 0) {
+ if (setgid(ccf->group) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "setgid(%d) failed", ccf->group);
+ /* fatal */
+ exit(2);
+ }
+
+ if (initgroups(ccf->username, ccf->group) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "initgroups(%s, %d) failed",
+ ccf->username, ccf->group);
+ }
+
+ if (setuid(ccf->user) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "setuid(%d) failed", ccf->user);
+ /* fatal */
+ exit(2);
+ }
+ }
+
+ if (worker >= 0) {
+ cpu_affinity = ngx_get_cpu_affinity(worker);
+
+ if (cpu_affinity) {
+ ngx_setaffinity(cpu_affinity, cycle->log);
+ }
+ }
+
+#if (NGX_HAVE_PR_SET_DUMPABLE)
+
+ /* allow coredump after setuid() in Linux 2.4.x */
+
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "prctl(PR_SET_DUMPABLE) failed");
+ }
+
+#endif
+
+ if (ccf->working_directory.len) {
+ if (chdir((char *) ccf->working_directory.data) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "chdir(\"%s\") failed", ccf->working_directory.data);
+ /* fatal */
+ exit(2);
+ }
+ }
+
+ sigemptyset(&set);
+
+ if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "sigprocmask() failed");
+ }
+
+ srandom((ngx_pid << 16) ^ ngx_time());
+
+ /*
+ * disable deleting previous events for the listening sockets because
+ * in the worker processes there are no events at all at this point
+ */
+ ls = cycle->listening.elts;
+ for (i = 0; i < cycle->listening.nelts; i++) {
+ ls[i].previous = NULL;
+ }
+
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->init_process) {
+ if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
+ /* fatal */
+ exit(2);
+ }
+ }
+ }
+
+ for (n = 0; n < ngx_last_process; n++) {
+
+ if (ngx_processes[n].pid == -1) {
+ continue;
+ }
+
+ if (n == ngx_process_slot) {
+ continue;
+ }
+
+ if (ngx_processes[n].channel[1] == -1) {
+ continue;
+ }
+
+ if (close(ngx_processes[n].channel[1]) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "close() channel failed");
+ }
+ }
+
+ if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "close() channel failed");
+ }
+
+#if 0
+ ngx_last_process = 0;
+#endif
+
+ if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
+ ngx_channel_handler)
+ == NGX_ERROR)
+ {
+ /* fatal */
+ exit(2);
+ }
+}
+
+
+static void
+ngx_worker_process_exit(ngx_cycle_t *cycle)
+{
+ ngx_uint_t i;
+ ngx_connection_t *c;
+
+#if (NGX_THREADS)
+ ngx_terminate = 1;
+
+ ngx_wakeup_worker_threads(cycle);
+#endif
+
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->exit_process) {
+ ngx_modules[i]->exit_process(cycle);
+ }
+ }
+
+ if (ngx_exiting) {
+ c = cycle->connections;
+ for (i = 0; i < cycle->connection_n; i++) {
+ if (c[i].fd != -1
+ && c[i].read
+ && !c[i].read->accept
+ && !c[i].read->channel
+ && !c[i].read->resolver)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "*%uA open socket #%d left in connection %ui",
+ c[i].number, c[i].fd, i);
+ ngx_debug_quit = 1;
+ }
+ }
+
+ if (ngx_debug_quit) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "aborting");
+ ngx_debug_point();
+ }
+ }
+
+ /*
+ * Copy ngx_cycle->log related data to the special static exit cycle,
+ * log, and log file structures enough to allow a signal handler to log.
+ * The handler may be called when standard ngx_cycle->log allocated from
+ * ngx_cycle->pool is already destroyed.
+ */
+
+ ngx_exit_log = *ngx_log_get_file_log(ngx_cycle->log);
+
+ ngx_exit_log_file.fd = ngx_exit_log.file->fd;
+ ngx_exit_log.file = &ngx_exit_log_file;
+ ngx_exit_log.next = NULL;
+ ngx_exit_log.writer = NULL;
+
+ ngx_exit_cycle.log = &ngx_exit_log;
+ ngx_exit_cycle.files = ngx_cycle->files;
+ ngx_exit_cycle.files_n = ngx_cycle->files_n;
+ ngx_cycle = &ngx_exit_cycle;
+
+ ngx_destroy_pool(cycle->pool);
+
+ ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, "exit");
+
+ exit(0);
+}
+
+
+static void
+ngx_channel_handler(ngx_event_t *ev)
+{
+ ngx_int_t n;
+ ngx_channel_t ch;
+ ngx_connection_t *c;
+
+ if (ev->timedout) {
+ ev->timedout = 0;
+ return;
+ }
+
+ c = ev->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel handler");
+
+ for ( ;; ) {
+
+ n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, "channel: %i", n);
+
+ if (n == NGX_ERROR) {
+
+ if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {
+ ngx_del_conn(c, 0);
+ }
+
+ ngx_close_connection(c);
+ return;
+ }
+
+ if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {
+ if (ngx_add_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) {
+ return;
+ }
+ }
+
+ if (n == NGX_AGAIN) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,
+ "channel command: %d", ch.command);
+
+ switch (ch.command) {
+
+ case NGX_CMD_QUIT:
+ ngx_quit = 1;
+ break;
+
+ case NGX_CMD_TERMINATE:
+ ngx_terminate = 1;
+ break;
+
+ case NGX_CMD_REOPEN:
+ ngx_reopen = 1;
+ break;
+
+ case NGX_CMD_OPEN_CHANNEL:
+
+ ngx_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0,
+ "get channel s:%i pid:%P fd:%d",
+ ch.slot, ch.pid, ch.fd);
+
+ ngx_processes[ch.slot].pid = ch.pid;
+ ngx_processes[ch.slot].channel[0] = ch.fd;
+ break;
+
+ case NGX_CMD_CLOSE_CHANNEL:
+
+ ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0,
+ "close channel s:%i pid:%P our:%P fd:%d",
+ ch.slot, ch.pid, ngx_processes[ch.slot].pid,
+ ngx_processes[ch.slot].channel[0]);
+
+ if (close(ngx_processes[ch.slot].channel[0]) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
+ "close() channel failed");
+ }
+
+ ngx_processes[ch.slot].channel[0] = -1;
+ break;
+ }
+ }
+}
+
+
+#if (NGX_THREADS)
+
+static void
+ngx_wakeup_worker_threads(ngx_cycle_t *cycle)
+{
+ ngx_int_t i;
+ ngx_uint_t live;
+
+ for ( ;; ) {
+
+ live = 0;
+
+ for (i = 0; i < ngx_threads_n; i++) {
+ if (ngx_threads[i].state < NGX_THREAD_EXIT) {
+ if (ngx_cond_signal(ngx_threads[i].cv) == NGX_ERROR) {
+ ngx_threads[i].state = NGX_THREAD_DONE;
+
+ } else {
+ live = 1;
+ }
+ }
+
+ if (ngx_threads[i].state == NGX_THREAD_EXIT) {
+ ngx_thread_join(ngx_threads[i].tid, NULL);
+ ngx_threads[i].state = NGX_THREAD_DONE;
+ }
+ }
+
+ if (live == 0) {
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+ "all worker threads are joined");
+
+ /* STUB */
+ ngx_done_events(cycle);
+ ngx_mutex_destroy(ngx_event_timer_mutex);
+ ngx_mutex_destroy(ngx_posted_events_mutex);
+
+ return;
+ }
+
+ ngx_sched_yield();
+ }
+}
+
+
+static ngx_thread_value_t
+ngx_worker_thread_cycle(void *data)
+{
+ ngx_thread_t *thr = data;
+
+ sigset_t set;
+ ngx_err_t err;
+ ngx_core_tls_t *tls;
+ ngx_cycle_t *cycle;
+
+ cycle = (ngx_cycle_t *) ngx_cycle;
+
+ sigemptyset(&set);
+ sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));
+ sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));
+ sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));
+
+ err = ngx_thread_sigmask(SIG_BLOCK, &set, NULL);
+ if (err) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+ ngx_thread_sigmask_n " failed");
+ return (ngx_thread_value_t) 1;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+ "thread " NGX_TID_T_FMT " started", ngx_thread_self());
+
+ ngx_setthrtitle("worker thread");
+
+ tls = ngx_calloc(sizeof(ngx_core_tls_t), cycle->log);
+ if (tls == NULL) {
+ return (ngx_thread_value_t) 1;
+ }
+
+ err = ngx_thread_set_tls(ngx_core_tls_key, tls);
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+ ngx_thread_set_tls_n " failed");
+ return (ngx_thread_value_t) 1;
+ }
+
+ ngx_mutex_lock(ngx_posted_events_mutex);
+
+ for ( ;; ) {
+ thr->state = NGX_THREAD_FREE;
+
+ if (ngx_cond_wait(thr->cv, ngx_posted_events_mutex) == NGX_ERROR) {
+ return (ngx_thread_value_t) 1;
+ }
+
+ if (ngx_terminate) {
+ thr->state = NGX_THREAD_EXIT;
+
+ ngx_mutex_unlock(ngx_posted_events_mutex);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+ "thread " NGX_TID_T_FMT " is done",
+ ngx_thread_self());
+
+ return (ngx_thread_value_t) 0;
+ }
+
+ thr->state = NGX_THREAD_BUSY;
+
+ if (ngx_event_thread_process_posted(cycle) == NGX_ERROR) {
+ return (ngx_thread_value_t) 1;
+ }
+
+ if (ngx_event_thread_process_posted(cycle) == NGX_ERROR) {
+ return (ngx_thread_value_t) 1;
+ }
+
+ if (ngx_process_changes) {
+ if (ngx_process_changes(cycle, 1) == NGX_ERROR) {
+ return (ngx_thread_value_t) 1;
+ }
+ }
+ }
+}
+
+#endif
+
+
+static void
+ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data)
+{
+ ngx_cache_manager_ctx_t *ctx = data;
+
+ void *ident[4];
+ ngx_event_t ev;
+
+ /*
+ * Set correct process type since closing listening Unix domain socket
+ * in a master process also removes the Unix domain socket file.
+ */
+ ngx_process = NGX_PROCESS_HELPER;
+
+ ngx_close_listening_sockets(cycle);
+
+ /* Set a moderate number of connections for a helper process. */
+ cycle->connection_n = 512;
+
+ ngx_worker_process_init(cycle, -1);
+
+ ngx_memzero(&ev, sizeof(ngx_event_t));
+ ev.handler = ctx->handler;
+ ev.data = ident;
+ ev.log = cycle->log;
+ ident[3] = (void *) -1;
+
+ ngx_use_accept_mutex = 0;
+
+ ngx_setproctitle(ctx->name);
+
+ ngx_add_timer(&ev, ctx->delay);
+
+ for ( ;; ) {
+
+ if (ngx_terminate || ngx_quit) {
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
+ exit(0);
+ }
+
+ if (ngx_reopen) {
+ ngx_reopen = 0;
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
+ ngx_reopen_files(cycle, -1);
+ }
+
+ ngx_process_events_and_timers(cycle);
+ }
+}
+
+
+static void
+ngx_cache_manager_process_handler(ngx_event_t *ev)
+{
+ time_t next, n;
+ ngx_uint_t i;
+ ngx_path_t **path;
+
+ next = 60 * 60;
+
+ path = ngx_cycle->paths.elts;
+ for (i = 0; i < ngx_cycle->paths.nelts; i++) {
+
+ if (path[i]->manager) {
+ n = path[i]->manager(path[i]->data);
+
+ next = (n <= next) ? n : next;
+
+ ngx_time_update();
+ }
+ }
+
+ if (next == 0) {
+ next = 1;
+ }
+
+ ngx_add_timer(ev, next * 1000);
+}
+
+
+static void
+ngx_cache_loader_process_handler(ngx_event_t *ev)
+{
+ ngx_uint_t i;
+ ngx_path_t **path;
+ ngx_cycle_t *cycle;
+
+ cycle = (ngx_cycle_t *) ngx_cycle;
+
+ path = cycle->paths.elts;
+ for (i = 0; i < cycle->paths.nelts; i++) {
+
+ if (ngx_terminate || ngx_quit) {
+ break;
+ }
+
+ if (path[i]->loader) {
+ path[i]->loader(path[i]->data);
+ ngx_time_update();
+ }
+ }
+
+ exit(0);
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process_cycle.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process_cycle.h
new file mode 100644
index 00000000000..94747b85d55
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_process_cycle.h
@@ -0,0 +1,61 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_PROCESS_CYCLE_H_INCLUDED_
+#define _NGX_PROCESS_CYCLE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#define NGX_CMD_OPEN_CHANNEL 1
+#define NGX_CMD_CLOSE_CHANNEL 2
+#define NGX_CMD_QUIT 3
+#define NGX_CMD_TERMINATE 4
+#define NGX_CMD_REOPEN 5
+
+
+#define NGX_PROCESS_SINGLE 0
+#define NGX_PROCESS_MASTER 1
+#define NGX_PROCESS_SIGNALLER 2
+#define NGX_PROCESS_WORKER 3
+#define NGX_PROCESS_HELPER 4
+
+
+typedef struct {
+ ngx_event_handler_pt handler;
+ char *name;
+ ngx_msec_t delay;
+} ngx_cache_manager_ctx_t;
+
+
+void ngx_master_process_cycle(ngx_cycle_t *cycle);
+void ngx_single_process_cycle(ngx_cycle_t *cycle);
+
+
+extern ngx_uint_t ngx_process;
+extern ngx_pid_t ngx_pid;
+extern ngx_pid_t ngx_new_binary;
+extern ngx_uint_t ngx_inherited;
+extern ngx_uint_t ngx_daemonized;
+extern ngx_uint_t ngx_threaded;
+extern ngx_uint_t ngx_exiting;
+
+extern sig_atomic_t ngx_reap;
+extern sig_atomic_t ngx_sigio;
+extern sig_atomic_t ngx_sigalrm;
+extern sig_atomic_t ngx_quit;
+extern sig_atomic_t ngx_debug_quit;
+extern sig_atomic_t ngx_terminate;
+extern sig_atomic_t ngx_noaccept;
+extern sig_atomic_t ngx_reconfigure;
+extern sig_atomic_t ngx_reopen;
+extern sig_atomic_t ngx_change_binary;
+
+
+#endif /* _NGX_PROCESS_CYCLE_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_pthread_thread.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_pthread_thread.c
new file mode 100644
index 00000000000..1cf31c3bc2b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_pthread_thread.c
@@ -0,0 +1,278 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+static ngx_uint_t nthreads;
+static ngx_uint_t max_threads;
+
+
+static pthread_attr_t thr_attr;
+
+
+ngx_err_t
+ngx_create_thread(ngx_tid_t *tid, ngx_thread_value_t (*func)(void *arg),
+ void *arg, ngx_log_t *log)
+{
+ int err;
+
+ if (nthreads >= max_threads) {
+ ngx_log_error(NGX_LOG_CRIT, log, 0,
+ "no more than %ui threads can be created", max_threads);
+ return NGX_ERROR;
+ }
+
+ err = pthread_create(tid, &thr_attr, func, arg);
+
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, log, err, "pthread_create() failed");
+ return err;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
+ "thread is created: " NGX_TID_T_FMT, *tid);
+
+ nthreads++;
+
+ return err;
+}
+
+
+ngx_int_t
+ngx_init_threads(int n, size_t size, ngx_cycle_t *cycle)
+{
+ int err;
+
+ max_threads = n;
+
+ err = pthread_attr_init(&thr_attr);
+
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+ "pthread_attr_init() failed");
+ return NGX_ERROR;
+ }
+
+ err = pthread_attr_setstacksize(&thr_attr, size);
+
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+ "pthread_attr_setstacksize() failed");
+ return NGX_ERROR;
+ }
+
+ ngx_threaded = 1;
+
+ return NGX_OK;
+}
+
+
+ngx_mutex_t *
+ngx_mutex_init(ngx_log_t *log, ngx_uint_t flags)
+{
+ int err;
+ ngx_mutex_t *m;
+
+ m = ngx_alloc(sizeof(ngx_mutex_t), log);
+ if (m == NULL) {
+ return NULL;
+ }
+
+ m->log = log;
+
+ err = pthread_mutex_init(&m->mutex, NULL);
+
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, m->log, err,
+ "pthread_mutex_init() failed");
+ return NULL;
+ }
+
+ return m;
+}
+
+
+void
+ngx_mutex_destroy(ngx_mutex_t *m)
+{
+ int err;
+
+ err = pthread_mutex_destroy(&m->mutex);
+
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, m->log, err,
+ "pthread_mutex_destroy(%p) failed", m);
+ }
+
+ ngx_free(m);
+}
+
+
+void
+ngx_mutex_lock(ngx_mutex_t *m)
+{
+ int err;
+
+ if (!ngx_threaded) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "lock mutex %p", m);
+
+ err = pthread_mutex_lock(&m->mutex);
+
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, m->log, err,
+ "pthread_mutex_lock(%p) failed", m);
+ ngx_abort();
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "mutex %p is locked", m);
+
+ return;
+}
+
+
+ngx_int_t
+ngx_mutex_trylock(ngx_mutex_t *m)
+{
+ int err;
+
+ if (!ngx_threaded) {
+ return NGX_OK;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "try lock mutex %p", m);
+
+ err = pthread_mutex_trylock(&m->mutex);
+
+ if (err == NGX_EBUSY) {
+ return NGX_AGAIN;
+ }
+
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, m->log, err,
+ "pthread_mutex_trylock(%p) failed", m);
+ ngx_abort();
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "mutex %p is locked", m);
+
+ return NGX_OK;
+}
+
+
+void
+ngx_mutex_unlock(ngx_mutex_t *m)
+{
+ int err;
+
+ if (!ngx_threaded) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "unlock mutex %p", m);
+
+ err = pthread_mutex_unlock(&m->mutex);
+
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, m->log, err,
+ "pthread_mutex_unlock(%p) failed", m);
+ ngx_abort();
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "mutex %p is unlocked", m);
+
+ return;
+}
+
+
+ngx_cond_t *
+ngx_cond_init(ngx_log_t *log)
+{
+ int err;
+ ngx_cond_t *cv;
+
+ cv = ngx_alloc(sizeof(ngx_cond_t), log);
+ if (cv == NULL) {
+ return NULL;
+ }
+
+ cv->log = log;
+
+ err = pthread_cond_init(&cv->cond, NULL);
+
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, cv->log, err,
+ "pthread_cond_init() failed");
+ return NULL;
+ }
+
+ return cv;
+}
+
+
+void
+ngx_cond_destroy(ngx_cond_t *cv)
+{
+ int err;
+
+ err = pthread_cond_destroy(&cv->cond);
+
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, cv->log, err,
+ "pthread_cond_destroy(%p) failed", cv);
+ }
+
+ ngx_free(cv);
+}
+
+
+ngx_int_t
+ngx_cond_wait(ngx_cond_t *cv, ngx_mutex_t *m)
+{
+ int err;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, "cv %p wait", cv);
+
+ err = pthread_cond_wait(&cv->cond, &m->mutex);
+
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, cv->log, err,
+ "pthread_cond_wait(%p) failed", cv);
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, "cv %p is waked up", cv);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_MUTEX, m->log, 0, "mutex %p is locked", m);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_cond_signal(ngx_cond_t *cv)
+{
+ int err;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, "cv %p to signal", cv);
+
+ err = pthread_cond_signal(&cv->cond);
+
+ if (err != 0) {
+ ngx_log_error(NGX_LOG_ALERT, cv->log, err,
+ "pthread_cond_signal(%p) failed", cv);
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cv->log, 0, "cv %p is signaled", cv);
+
+ return NGX_OK;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_readv_chain.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_readv_chain.c
new file mode 100644
index 00000000000..e4eb5ff3281
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_readv_chain.c
@@ -0,0 +1,271 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if (IOV_MAX > 64)
+#define NGX_IOVS 64
+#else
+#define NGX_IOVS IOV_MAX
+#endif
+
+
+#if (NGX_HAVE_KQUEUE)
+
+ssize_t
+ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain)
+{
+ u_char *prev;
+ ssize_t n, size;
+ ngx_err_t err;
+ ngx_array_t vec;
+ ngx_event_t *rev;
+ struct iovec *iov, iovs[NGX_IOVS];
+
+ rev = c->read;
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "readv: eof:%d, avail:%d, err:%d",
+ rev->pending_eof, rev->available, rev->kq_errno);
+
+ if (rev->available == 0) {
+ if (rev->pending_eof) {
+ rev->ready = 0;
+ rev->eof = 1;
+
+ ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,
+ "kevent() reported about an closed connection");
+
+ if (rev->kq_errno) {
+ rev->error = 1;
+ ngx_set_socket_errno(rev->kq_errno);
+ return NGX_ERROR;
+ }
+
+ return 0;
+
+ } else {
+ return NGX_AGAIN;
+ }
+ }
+ }
+
+ prev = NULL;
+ iov = NULL;
+ size = 0;
+
+ vec.elts = iovs;
+ vec.nelts = 0;
+ vec.size = sizeof(struct iovec);
+ vec.nalloc = NGX_IOVS;
+ vec.pool = c->pool;
+
+ /* coalesce the neighbouring bufs */
+
+ while (chain) {
+ if (prev == chain->buf->last) {
+ iov->iov_len += chain->buf->end - chain->buf->last;
+
+ } else {
+ if (vec.nelts >= IOV_MAX) {
+ break;
+ }
+
+ iov = ngx_array_push(&vec);
+ if (iov == NULL) {
+ return NGX_ERROR;
+ }
+
+ iov->iov_base = (void *) chain->buf->last;
+ iov->iov_len = chain->buf->end - chain->buf->last;
+ }
+
+ size += chain->buf->end - chain->buf->last;
+ prev = chain->buf->end;
+ chain = chain->next;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "readv: %d, last:%d", vec.nelts, iov->iov_len);
+
+ rev = c->read;
+
+ do {
+ n = readv(c->fd, (struct iovec *) vec.elts, vec.nelts);
+
+ if (n >= 0) {
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ rev->available -= n;
+
+ /*
+ * rev->available may be negative here because some additional
+ * bytes may be received between kevent() and recv()
+ */
+
+ if (rev->available <= 0) {
+ if (!rev->pending_eof) {
+ rev->ready = 0;
+ }
+
+ if (rev->available < 0) {
+ rev->available = 0;
+ }
+ }
+
+ if (n == 0) {
+
+ /*
+ * on FreeBSD recv() may return 0 on closed socket
+ * even if kqueue reported about available data
+ */
+
+#if 0
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+ "readv() returned 0 while kevent() reported "
+ "%d available bytes", rev->available);
+#endif
+
+ rev->ready = 0;
+ rev->eof = 1;
+ rev->available = 0;
+ }
+
+ return n;
+ }
+
+ if (n < size && !(ngx_event_flags & NGX_USE_GREEDY_EVENT)) {
+ rev->ready = 0;
+ }
+
+ if (n == 0) {
+ rev->eof = 1;
+ }
+
+ return n;
+ }
+
+ err = ngx_socket_errno;
+
+ if (err == NGX_EAGAIN || err == NGX_EINTR) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "readv() not ready");
+ n = NGX_AGAIN;
+
+ } else {
+ n = ngx_connection_error(c, err, "readv() failed");
+ break;
+ }
+
+ } while (err == NGX_EINTR);
+
+ rev->ready = 0;
+
+ if (n == NGX_ERROR) {
+ c->read->error = 1;
+ }
+
+ return n;
+}
+
+#else /* ! NGX_HAVE_KQUEUE */
+
+ssize_t
+ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain)
+{
+ u_char *prev;
+ ssize_t n, size;
+ ngx_err_t err;
+ ngx_array_t vec;
+ ngx_event_t *rev;
+ struct iovec *iov, iovs[NGX_IOVS];
+
+ prev = NULL;
+ iov = NULL;
+ size = 0;
+
+ vec.elts = iovs;
+ vec.nelts = 0;
+ vec.size = sizeof(struct iovec);
+ vec.nalloc = NGX_IOVS;
+ vec.pool = c->pool;
+
+ /* coalesce the neighbouring bufs */
+
+ while (chain) {
+ if (prev == chain->buf->last) {
+ iov->iov_len += chain->buf->end - chain->buf->last;
+
+ } else {
+ if (vec.nelts >= IOV_MAX) {
+ break;
+ }
+
+ iov = ngx_array_push(&vec);
+ if (iov == NULL) {
+ return NGX_ERROR;
+ }
+
+ iov->iov_base = (void *) chain->buf->last;
+ iov->iov_len = chain->buf->end - chain->buf->last;
+ }
+
+ size += chain->buf->end - chain->buf->last;
+ prev = chain->buf->end;
+ chain = chain->next;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "readv: %d:%d", vec.nelts, iov->iov_len);
+
+ rev = c->read;
+
+ do {
+ n = readv(c->fd, (struct iovec *) vec.elts, vec.nelts);
+
+ if (n == 0) {
+ rev->ready = 0;
+ rev->eof = 1;
+
+ return n;
+
+ } else if (n > 0) {
+
+ if (n < size && !(ngx_event_flags & NGX_USE_GREEDY_EVENT)) {
+ rev->ready = 0;
+ }
+
+ return n;
+ }
+
+ err = ngx_socket_errno;
+
+ if (err == NGX_EAGAIN || err == NGX_EINTR) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "readv() not ready");
+ n = NGX_AGAIN;
+
+ } else {
+ n = ngx_connection_error(c, err, "readv() failed");
+ break;
+ }
+
+ } while (err == NGX_EINTR);
+
+ rev->ready = 0;
+
+ if (n == NGX_ERROR) {
+ c->read->error = 1;
+ }
+
+ return n;
+}
+
+#endif /* NGX_HAVE_KQUEUE */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_recv.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_recv.c
new file mode 100644
index 00000000000..86675dfdd8f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_recv.c
@@ -0,0 +1,183 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if (NGX_HAVE_KQUEUE)
+
+ssize_t
+ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
+{
+ ssize_t n;
+ ngx_err_t err;
+ ngx_event_t *rev;
+
+ rev = c->read;
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "recv: eof:%d, avail:%d, err:%d",
+ rev->pending_eof, rev->available, rev->kq_errno);
+
+ if (rev->available == 0) {
+ if (rev->pending_eof) {
+ rev->ready = 0;
+ rev->eof = 1;
+
+ if (rev->kq_errno) {
+ rev->error = 1;
+ ngx_set_socket_errno(rev->kq_errno);
+
+ return ngx_connection_error(c, rev->kq_errno,
+ "kevent() reported about an closed connection");
+ }
+
+ return 0;
+
+ } else {
+ rev->ready = 0;
+ return NGX_AGAIN;
+ }
+ }
+ }
+
+ do {
+ n = recv(c->fd, buf, size, 0);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "recv: fd:%d %d of %d", c->fd, n, size);
+
+ if (n >= 0) {
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ rev->available -= n;
+
+ /*
+ * rev->available may be negative here because some additional
+ * bytes may be received between kevent() and recv()
+ */
+
+ if (rev->available <= 0) {
+ if (!rev->pending_eof) {
+ rev->ready = 0;
+ }
+
+ if (rev->available < 0) {
+ rev->available = 0;
+ }
+ }
+
+ if (n == 0) {
+
+ /*
+ * on FreeBSD recv() may return 0 on closed socket
+ * even if kqueue reported about available data
+ */
+
+ rev->ready = 0;
+ rev->eof = 1;
+ rev->available = 0;
+ }
+
+ return n;
+ }
+
+ if ((size_t) n < size
+ && !(ngx_event_flags & NGX_USE_GREEDY_EVENT))
+ {
+ rev->ready = 0;
+ }
+
+ if (n == 0) {
+ rev->eof = 1;
+ }
+
+ return n;
+ }
+
+ err = ngx_socket_errno;
+
+ if (err == NGX_EAGAIN || err == NGX_EINTR) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "recv() not ready");
+ n = NGX_AGAIN;
+
+ } else {
+ n = ngx_connection_error(c, err, "recv() failed");
+ break;
+ }
+
+ } while (err == NGX_EINTR);
+
+ rev->ready = 0;
+
+ if (n == NGX_ERROR) {
+ rev->error = 1;
+ }
+
+ return n;
+}
+
+#else /* ! NGX_HAVE_KQUEUE */
+
+ssize_t
+ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
+{
+ ssize_t n;
+ ngx_err_t err;
+ ngx_event_t *rev;
+
+ rev = c->read;
+
+ do {
+ n = recv(c->fd, buf, size, 0);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "recv: fd:%d %d of %d", c->fd, n, size);
+
+ if (n == 0) {
+ rev->ready = 0;
+ rev->eof = 1;
+ return n;
+
+ } else if (n > 0) {
+
+ if ((size_t) n < size
+ && !(ngx_event_flags & NGX_USE_GREEDY_EVENT))
+ {
+ rev->ready = 0;
+ }
+
+ return n;
+ }
+
+ err = ngx_socket_errno;
+
+ if (err == NGX_EAGAIN || err == NGX_EINTR) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "recv() not ready");
+ n = NGX_AGAIN;
+
+ } else {
+ n = ngx_connection_error(c, err, "recv() failed");
+ break;
+ }
+
+ } while (err == NGX_EINTR);
+
+ rev->ready = 0;
+
+ if (n == NGX_ERROR) {
+ rev->error = 1;
+ }
+
+ return n;
+}
+
+#endif /* NGX_HAVE_KQUEUE */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_send.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_send.c
new file mode 100644
index 00000000000..80995ab3a50
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_send.c
@@ -0,0 +1,73 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+ssize_t
+ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size)
+{
+ ssize_t n;
+ ngx_err_t err;
+ ngx_event_t *wev;
+
+ wev = c->write;
+
+#if (NGX_HAVE_KQUEUE)
+
+ if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
+ (void) ngx_connection_error(c, wev->kq_errno,
+ "kevent() reported about an closed connection");
+ wev->error = 1;
+ return NGX_ERROR;
+ }
+
+#endif
+
+ for ( ;; ) {
+ n = send(c->fd, buf, size, 0);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "send: fd:%d %d of %d", c->fd, n, size);
+
+ if (n > 0) {
+ if (n < (ssize_t) size) {
+ wev->ready = 0;
+ }
+
+ c->sent += n;
+
+ return n;
+ }
+
+ err = ngx_socket_errno;
+
+ if (n == 0) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, err, "send() returned zero");
+ wev->ready = 0;
+ return n;
+ }
+
+ if (err == NGX_EAGAIN || err == NGX_EINTR) {
+ wev->ready = 0;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "send() not ready");
+
+ if (err == NGX_EAGAIN) {
+ return NGX_AGAIN;
+ }
+
+ } else {
+ wev->error = 1;
+ (void) ngx_connection_error(c, err, "send() failed");
+ return NGX_ERROR;
+ }
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setaffinity.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setaffinity.c
new file mode 100644
index 00000000000..8f6cf35948a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setaffinity.c
@@ -0,0 +1,69 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#if (NGX_HAVE_CPUSET_SETAFFINITY)
+
+#include <sys/cpuset.h>
+
+void
+ngx_setaffinity(uint64_t cpu_affinity, ngx_log_t *log)
+{
+ cpuset_t mask;
+ ngx_uint_t i;
+
+ ngx_log_error(NGX_LOG_NOTICE, log, 0,
+ "cpuset_setaffinity(0x%08Xl)", cpu_affinity);
+
+ CPU_ZERO(&mask);
+ i = 0;
+ do {
+ if (cpu_affinity & 1) {
+ CPU_SET(i, &mask);
+ }
+ i++;
+ cpu_affinity >>= 1;
+ } while (cpu_affinity);
+
+ if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1,
+ sizeof(cpuset_t), &mask) == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ "cpuset_setaffinity() failed");
+ }
+}
+
+#elif (NGX_HAVE_SCHED_SETAFFINITY)
+
+void
+ngx_setaffinity(uint64_t cpu_affinity, ngx_log_t *log)
+{
+ cpu_set_t mask;
+ ngx_uint_t i;
+
+ ngx_log_error(NGX_LOG_NOTICE, log, 0,
+ "sched_setaffinity(0x%08Xl)", cpu_affinity);
+
+ CPU_ZERO(&mask);
+ i = 0;
+ do {
+ if (cpu_affinity & 1) {
+ CPU_SET(i, &mask);
+ }
+ i++;
+ cpu_affinity >>= 1;
+ } while (cpu_affinity);
+
+ if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ "sched_setaffinity() failed");
+ }
+}
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setaffinity.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setaffinity.h
new file mode 100644
index 00000000000..33f5835dee6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setaffinity.h
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+#ifndef _NGX_SETAFFINITY_H_INCLUDED_
+#define _NGX_SETAFFINITY_H_INCLUDED_
+
+
+#if (NGX_HAVE_SCHED_SETAFFINITY || NGX_HAVE_CPUSET_SETAFFINITY)
+
+#define NGX_HAVE_CPU_AFFINITY 1
+
+void ngx_setaffinity(uint64_t cpu_affinity, ngx_log_t *log);
+
+#else
+
+#define ngx_setaffinity(cpu_affinity, log)
+
+#endif
+
+
+#endif /* _NGX_SETAFFINITY_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setproctitle.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setproctitle.c
new file mode 100644
index 00000000000..91afa519142
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setproctitle.c
@@ -0,0 +1,135 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#if (NGX_SETPROCTITLE_USES_ENV)
+
+/*
+ * To change the process title in Linux and Solaris we have to set argv[1]
+ * to NULL and to copy the title to the same place where the argv[0] points to.
+ * However, argv[0] may be too small to hold a new title. Fortunately, Linux
+ * and Solaris store argv[] and environ[] one after another. So we should
+ * ensure that is the continuous memory and then we allocate the new memory
+ * for environ[] and copy it. After this we could use the memory starting
+ * from argv[0] for our process title.
+ *
+ * The Solaris's standard /bin/ps does not show the changed process title.
+ * You have to use "/usr/ucb/ps -w" instead. Besides, the UCB ps does not
+ * show a new title if its length less than the origin command line length.
+ * To avoid it we append to a new title the origin command line in the
+ * parenthesis.
+ */
+
+extern char **environ;
+
+static char *ngx_os_argv_last;
+
+ngx_int_t
+ngx_init_setproctitle(ngx_log_t *log)
+{
+ u_char *p;
+ size_t size;
+ ngx_uint_t i;
+
+ size = 0;
+
+ for (i = 0; environ[i]; i++) {
+ size += ngx_strlen(environ[i]) + 1;
+ }
+
+ p = ngx_alloc(size, log);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_os_argv_last = ngx_os_argv[0];
+
+ for (i = 0; ngx_os_argv[i]; i++) {
+ if (ngx_os_argv_last == ngx_os_argv[i]) {
+ ngx_os_argv_last = ngx_os_argv[i] + ngx_strlen(ngx_os_argv[i]) + 1;
+ }
+ }
+
+ for (i = 0; environ[i]; i++) {
+ if (ngx_os_argv_last == environ[i]) {
+
+ size = ngx_strlen(environ[i]) + 1;
+ ngx_os_argv_last = environ[i] + size;
+
+ ngx_cpystrn(p, (u_char *) environ[i], size);
+ environ[i] = (char *) p;
+ p += size;
+ }
+ }
+
+ ngx_os_argv_last--;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_setproctitle(char *title)
+{
+ u_char *p;
+
+#if (NGX_SOLARIS)
+
+ ngx_int_t i;
+ size_t size;
+
+#endif
+
+ ngx_os_argv[1] = NULL;
+
+ p = ngx_cpystrn((u_char *) ngx_os_argv[0], (u_char *) "nginx: ",
+ ngx_os_argv_last - ngx_os_argv[0]);
+
+ p = ngx_cpystrn(p, (u_char *) title, ngx_os_argv_last - (char *) p);
+
+#if (NGX_SOLARIS)
+
+ size = 0;
+
+ for (i = 0; i < ngx_argc; i++) {
+ size += ngx_strlen(ngx_argv[i]) + 1;
+ }
+
+ if (size > (size_t) ((char *) p - ngx_os_argv[0])) {
+
+ /*
+ * ngx_setproctitle() is too rare operation so we use
+ * the non-optimized copies
+ */
+
+ p = ngx_cpystrn(p, (u_char *) " (", ngx_os_argv_last - (char *) p);
+
+ for (i = 0; i < ngx_argc; i++) {
+ p = ngx_cpystrn(p, (u_char *) ngx_argv[i],
+ ngx_os_argv_last - (char *) p);
+ p = ngx_cpystrn(p, (u_char *) " ", ngx_os_argv_last - (char *) p);
+ }
+
+ if (*(p - 1) == ' ') {
+ *(p - 1) = ')';
+ }
+ }
+
+#endif
+
+ if (ngx_os_argv_last - (char *) p) {
+ ngx_memset(p, NGX_SETPROCTITLE_PAD, ngx_os_argv_last - (char *) p);
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+ "setproctitle: \"%s\"", ngx_os_argv[0]);
+}
+
+#endif /* NGX_SETPROCTITLE_USES_ENV */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setproctitle.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setproctitle.h
new file mode 100644
index 00000000000..2323408c46e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_setproctitle.h
@@ -0,0 +1,52 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_SETPROCTITLE_H_INCLUDED_
+#define _NGX_SETPROCTITLE_H_INCLUDED_
+
+
+#if (NGX_HAVE_SETPROCTITLE)
+
+/* FreeBSD, NetBSD, OpenBSD */
+
+#define ngx_init_setproctitle(log)
+#define ngx_setproctitle(title) setproctitle("%s", title)
+
+
+#else /* !NGX_HAVE_SETPROCTITLE */
+
+#if !defined NGX_SETPROCTITLE_USES_ENV
+
+#if (NGX_SOLARIS)
+
+#define NGX_SETPROCTITLE_USES_ENV 1
+#define NGX_SETPROCTITLE_PAD ' '
+
+ngx_int_t ngx_init_setproctitle(ngx_log_t *log);
+void ngx_setproctitle(char *title);
+
+#elif (NGX_LINUX) || (NGX_DARWIN)
+
+#define NGX_SETPROCTITLE_USES_ENV 1
+#define NGX_SETPROCTITLE_PAD '\0'
+
+ngx_int_t ngx_init_setproctitle(ngx_log_t *log);
+void ngx_setproctitle(char *title);
+
+#else
+
+#define ngx_init_setproctitle(log)
+#define ngx_setproctitle(title)
+
+#endif /* OSes */
+
+#endif /* NGX_SETPROCTITLE_USES_ENV */
+
+#endif /* NGX_HAVE_SETPROCTITLE */
+
+
+#endif /* _NGX_SETPROCTITLE_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_shmem.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_shmem.c
new file mode 100644
index 00000000000..3ec7cbf1fd3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_shmem.c
@@ -0,0 +1,126 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#if (NGX_HAVE_MAP_ANON)
+
+ngx_int_t
+ngx_shm_alloc(ngx_shm_t *shm)
+{
+ shm->addr = (u_char *) mmap(NULL, shm->size,
+ PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_SHARED, -1, 0);
+
+ if (shm->addr == MAP_FAILED) {
+ ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
+ "mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size);
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_shm_free(ngx_shm_t *shm)
+{
+ if (munmap((void *) shm->addr, shm->size) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
+ "munmap(%p, %uz) failed", shm->addr, shm->size);
+ }
+}
+
+#elif (NGX_HAVE_MAP_DEVZERO)
+
+ngx_int_t
+ngx_shm_alloc(ngx_shm_t *shm)
+{
+ ngx_fd_t fd;
+
+ fd = open("/dev/zero", O_RDWR);
+
+ if (fd == -1) {
+ ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
+ "open(\"/dev/zero\") failed");
+ return NGX_ERROR;
+ }
+
+ shm->addr = (u_char *) mmap(NULL, shm->size, PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, 0);
+
+ if (shm->addr == MAP_FAILED) {
+ ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
+ "mmap(/dev/zero, MAP_SHARED, %uz) failed", shm->size);
+ }
+
+ if (close(fd) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
+ "close(\"/dev/zero\") failed");
+ }
+
+ return (shm->addr == MAP_FAILED) ? NGX_ERROR : NGX_OK;
+}
+
+
+void
+ngx_shm_free(ngx_shm_t *shm)
+{
+ if (munmap((void *) shm->addr, shm->size) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
+ "munmap(%p, %uz) failed", shm->addr, shm->size);
+ }
+}
+
+#elif (NGX_HAVE_SYSVSHM)
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+
+ngx_int_t
+ngx_shm_alloc(ngx_shm_t *shm)
+{
+ int id;
+
+ id = shmget(IPC_PRIVATE, shm->size, (SHM_R|SHM_W|IPC_CREAT));
+
+ if (id == -1) {
+ ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
+ "shmget(%uz) failed", shm->size);
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, shm->log, 0, "shmget id: %d", id);
+
+ shm->addr = shmat(id, NULL, 0);
+
+ if (shm->addr == (void *) -1) {
+ ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, "shmat() failed");
+ }
+
+ if (shmctl(id, IPC_RMID, NULL) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
+ "shmctl(IPC_RMID) failed");
+ }
+
+ return (shm->addr == (void *) -1) ? NGX_ERROR : NGX_OK;
+}
+
+
+void
+ngx_shm_free(ngx_shm_t *shm)
+{
+ if (shmdt(shm->addr) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
+ "shmdt(%p) failed", shm->addr);
+ }
+}
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_shmem.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_shmem.h
new file mode 100644
index 00000000000..566a7d3300b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_shmem.h
@@ -0,0 +1,29 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_SHMEM_H_INCLUDED_
+#define _NGX_SHMEM_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef struct {
+ u_char *addr;
+ size_t size;
+ ngx_str_t name;
+ ngx_log_t *log;
+ ngx_uint_t exists; /* unsigned exists:1; */
+} ngx_shm_t;
+
+
+ngx_int_t ngx_shm_alloc(ngx_shm_t *shm);
+void ngx_shm_free(ngx_shm_t *shm);
+
+
+#endif /* _NGX_SHMEM_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_socket.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_socket.c
new file mode 100644
index 00000000000..3978f655c04
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_socket.c
@@ -0,0 +1,116 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * ioctl(FIONBIO) sets a non-blocking mode with the single syscall
+ * while fcntl(F_SETFL, O_NONBLOCK) needs to learn the current state
+ * using fcntl(F_GETFL).
+ *
+ * ioctl() and fcntl() are syscalls at least in FreeBSD 2.x, Linux 2.2
+ * and Solaris 7.
+ *
+ * ioctl() in Linux 2.4 and 2.6 uses BKL, however, fcntl(F_SETFL) uses it too.
+ */
+
+
+#if (NGX_HAVE_FIONBIO)
+
+int
+ngx_nonblocking(ngx_socket_t s)
+{
+ int nb;
+
+ nb = 1;
+
+ return ioctl(s, FIONBIO, &nb);
+}
+
+
+int
+ngx_blocking(ngx_socket_t s)
+{
+ int nb;
+
+ nb = 0;
+
+ return ioctl(s, FIONBIO, &nb);
+}
+
+#endif
+
+
+#if (NGX_FREEBSD)
+
+int
+ngx_tcp_nopush(ngx_socket_t s)
+{
+ int tcp_nopush;
+
+ tcp_nopush = 1;
+
+ return setsockopt(s, IPPROTO_TCP, TCP_NOPUSH,
+ (const void *) &tcp_nopush, sizeof(int));
+}
+
+
+int
+ngx_tcp_push(ngx_socket_t s)
+{
+ int tcp_nopush;
+
+ tcp_nopush = 0;
+
+ return setsockopt(s, IPPROTO_TCP, TCP_NOPUSH,
+ (const void *) &tcp_nopush, sizeof(int));
+}
+
+#elif (NGX_LINUX)
+
+
+int
+ngx_tcp_nopush(ngx_socket_t s)
+{
+ int cork;
+
+ cork = 1;
+
+ return setsockopt(s, IPPROTO_TCP, TCP_CORK,
+ (const void *) &cork, sizeof(int));
+}
+
+
+int
+ngx_tcp_push(ngx_socket_t s)
+{
+ int cork;
+
+ cork = 0;
+
+ return setsockopt(s, IPPROTO_TCP, TCP_CORK,
+ (const void *) &cork, sizeof(int));
+}
+
+#else
+
+int
+ngx_tcp_nopush(ngx_socket_t s)
+{
+ return 0;
+}
+
+
+int
+ngx_tcp_push(ngx_socket_t s)
+{
+ return 0;
+}
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_socket.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_socket.h
new file mode 100644
index 00000000000..fcc51533568
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_socket.h
@@ -0,0 +1,64 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_SOCKET_H_INCLUDED_
+#define _NGX_SOCKET_H_INCLUDED_
+
+
+#include <ngx_config.h>
+
+
+#define NGX_WRITE_SHUTDOWN SHUT_WR
+
+typedef int ngx_socket_t;
+
+#define ngx_socket socket
+#define ngx_socket_n "socket()"
+
+
+#if (NGX_HAVE_FIONBIO)
+
+int ngx_nonblocking(ngx_socket_t s);
+int ngx_blocking(ngx_socket_t s);
+
+#define ngx_nonblocking_n "ioctl(FIONBIO)"
+#define ngx_blocking_n "ioctl(!FIONBIO)"
+
+#else
+
+#define ngx_nonblocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK)
+#define ngx_nonblocking_n "fcntl(O_NONBLOCK)"
+
+#define ngx_blocking(s) fcntl(s, F_SETFL, fcntl(s, F_GETFL) & ~O_NONBLOCK)
+#define ngx_blocking_n "fcntl(!O_NONBLOCK)"
+
+#endif
+
+int ngx_tcp_nopush(ngx_socket_t s);
+int ngx_tcp_push(ngx_socket_t s);
+
+#if (NGX_LINUX)
+
+#define ngx_tcp_nopush_n "setsockopt(TCP_CORK)"
+#define ngx_tcp_push_n "setsockopt(!TCP_CORK)"
+
+#else
+
+#define ngx_tcp_nopush_n "setsockopt(TCP_NOPUSH)"
+#define ngx_tcp_push_n "setsockopt(!TCP_NOPUSH)"
+
+#endif
+
+
+#define ngx_shutdown_socket shutdown
+#define ngx_shutdown_socket_n "shutdown()"
+
+#define ngx_close_socket close
+#define ngx_close_socket_n "close() socket"
+
+
+#endif /* _NGX_SOCKET_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris.h
new file mode 100644
index 00000000000..7b167d830ab
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris.h
@@ -0,0 +1,16 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_SOLARIS_H_INCLUDED_
+#define _NGX_SOLARIS_H_INCLUDED_
+
+
+ngx_chain_t *ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in,
+ off_t limit);
+
+
+#endif /* _NGX_SOLARIS_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris_config.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris_config.h
new file mode 100644
index 00000000000..ddfd984577e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris_config.h
@@ -0,0 +1,110 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_SOLARIS_CONFIG_H_INCLUDED_
+#define _NGX_SOLARIS_CONFIG_H_INCLUDED_
+
+
+#ifndef _REENTRANT
+#define _REENTRANT
+#endif
+
+#define _FILE_OFFSET_BITS 64 /* must be before <sys/types.h> */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stddef.h> /* offsetof() */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <dirent.h>
+#include <glob.h>
+#include <time.h>
+#include <sys/statvfs.h> /* statvfs() */
+
+#include <sys/filio.h> /* FIONBIO */
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sched.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h> /* TCP_NODELAY */
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/un.h>
+
+#include <sys/systeminfo.h>
+#include <limits.h> /* IOV_MAX */
+#include <inttypes.h>
+#include <crypt.h>
+
+#define NGX_ALIGNMENT _MAX_ALIGNMENT
+
+#include <ngx_auto_config.h>
+
+
+#if (NGX_HAVE_POSIX_SEM)
+#include <semaphore.h>
+#endif
+
+
+#if (NGX_HAVE_POLL)
+#include <poll.h>
+#endif
+
+
+#if (NGX_HAVE_DEVPOLL)
+#include <sys/ioctl.h>
+#include <sys/devpoll.h>
+#endif
+
+
+#if (NGX_HAVE_EVENTPORT)
+#include <port.h>
+#endif
+
+
+#if (NGX_HAVE_SENDFILE)
+#include <sys/sendfile.h>
+#endif
+
+
+#define NGX_LISTEN_BACKLOG 511
+
+
+#ifndef NGX_HAVE_INHERITED_NONBLOCK
+#define NGX_HAVE_INHERITED_NONBLOCK 1
+#endif
+
+
+#ifndef NGX_HAVE_SO_SNDLOWAT
+/* setsockopt(SO_SNDLOWAT) returns ENOPROTOOPT */
+#define NGX_HAVE_SO_SNDLOWAT 0
+#endif
+
+
+#define NGX_HAVE_OS_SPECIFIC_INIT 1
+#define ngx_debug_init()
+
+
+extern char **environ;
+
+
+#endif /* _NGX_SOLARIS_CONFIG_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris_init.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris_init.c
new file mode 100644
index 00000000000..f2f3600dab1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris_init.c
@@ -0,0 +1,75 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+char ngx_solaris_sysname[20];
+char ngx_solaris_release[10];
+char ngx_solaris_version[50];
+
+
+static ngx_os_io_t ngx_solaris_io = {
+ ngx_unix_recv,
+ ngx_readv_chain,
+ ngx_udp_unix_recv,
+ ngx_unix_send,
+#if (NGX_HAVE_SENDFILE)
+ ngx_solaris_sendfilev_chain,
+ NGX_IO_SENDFILE
+#else
+ ngx_writev_chain,
+ 0
+#endif
+};
+
+
+ngx_int_t
+ngx_os_specific_init(ngx_log_t *log)
+{
+ if (sysinfo(SI_SYSNAME, ngx_solaris_sysname, sizeof(ngx_solaris_sysname))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ "sysinfo(SI_SYSNAME) failed");
+ return NGX_ERROR;
+ }
+
+ if (sysinfo(SI_RELEASE, ngx_solaris_release, sizeof(ngx_solaris_release))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ "sysinfo(SI_RELEASE) failed");
+ return NGX_ERROR;
+ }
+
+ if (sysinfo(SI_VERSION, ngx_solaris_version, sizeof(ngx_solaris_version))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ "sysinfo(SI_SYSNAME) failed");
+ return NGX_ERROR;
+ }
+
+
+ ngx_os_io = ngx_solaris_io;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_os_specific_status(ngx_log_t *log)
+{
+
+ ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
+ ngx_solaris_sysname, ngx_solaris_release);
+
+ ngx_log_error(NGX_LOG_NOTICE, log, 0, "version: %s",
+ ngx_solaris_version);
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris_sendfilev_chain.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris_sendfilev_chain.c
new file mode 100644
index 00000000000..37bb09d961a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_solaris_sendfilev_chain.c
@@ -0,0 +1,260 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if (NGX_TEST_BUILD_SOLARIS_SENDFILEV)
+
+/* Solaris declarations */
+
+typedef struct sendfilevec {
+ int sfv_fd;
+ u_int sfv_flag;
+ off_t sfv_off;
+ size_t sfv_len;
+} sendfilevec_t;
+
+#define SFV_FD_SELF -2
+
+static ssize_t sendfilev(int fd, const struct sendfilevec *vec,
+ int sfvcnt, size_t *xferred)
+{
+ return -1;
+}
+
+ngx_chain_t *ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in,
+ off_t limit);
+
+#endif
+
+
+#if (IOV_MAX > 64)
+#define NGX_SENDFILEVECS 64
+#else
+#define NGX_SENDFILEVECS IOV_MAX
+#endif
+
+
+
+ngx_chain_t *
+ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
+{
+ int fd;
+ u_char *prev;
+ off_t size, send, prev_send, aligned, fprev;
+ size_t sent;
+ ssize_t n;
+ ngx_int_t eintr, complete;
+ ngx_err_t err;
+ sendfilevec_t *sfv, sfvs[NGX_SENDFILEVECS];
+ ngx_array_t vec;
+ ngx_event_t *wev;
+ ngx_chain_t *cl;
+
+ wev = c->write;
+
+ if (!wev->ready) {
+ return in;
+ }
+
+ if (!c->sendfile) {
+ return ngx_writev_chain(c, in, limit);
+ }
+
+
+ /* the maximum limit size is the maximum size_t value - the page size */
+
+ if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
+ limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
+ }
+
+
+ send = 0;
+
+ vec.elts = sfvs;
+ vec.size = sizeof(sendfilevec_t);
+ vec.nalloc = NGX_SENDFILEVECS;
+ vec.pool = c->pool;
+
+ for ( ;; ) {
+ fd = SFV_FD_SELF;
+ prev = NULL;
+ fprev = 0;
+ sfv = NULL;
+ eintr = 0;
+ complete = 0;
+ sent = 0;
+ prev_send = send;
+
+ vec.nelts = 0;
+
+ /* create the sendfilevec and coalesce the neighbouring bufs */
+
+ for (cl = in; cl && send < limit; cl = cl->next) {
+
+ if (ngx_buf_special(cl->buf)) {
+ continue;
+ }
+
+ if (ngx_buf_in_memory_only(cl->buf)) {
+ fd = SFV_FD_SELF;
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if (send + size > limit) {
+ size = limit - send;
+ }
+
+ if (prev == cl->buf->pos) {
+ sfv->sfv_len += (size_t) size;
+
+ } else {
+ if (vec.nelts >= IOV_MAX) {
+ break;
+ }
+
+ sfv = ngx_array_push(&vec);
+ if (sfv == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ sfv->sfv_fd = SFV_FD_SELF;
+ sfv->sfv_flag = 0;
+ sfv->sfv_off = (off_t) (uintptr_t) cl->buf->pos;
+ sfv->sfv_len = (size_t) size;
+ }
+
+ prev = cl->buf->pos + (size_t) size;
+ send += size;
+
+ } else {
+ prev = NULL;
+
+ size = cl->buf->file_last - cl->buf->file_pos;
+
+ if (send + size > limit) {
+ size = limit - send;
+
+ aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
+ & ~((off_t) ngx_pagesize - 1);
+
+ if (aligned <= cl->buf->file_last) {
+ size = aligned - cl->buf->file_pos;
+ }
+ }
+
+ if (fd == cl->buf->file->fd && fprev == cl->buf->file_pos) {
+ sfv->sfv_len += (size_t) size;
+
+ } else {
+ if (vec.nelts >= IOV_MAX) {
+ break;
+ }
+
+ sfv = ngx_array_push(&vec);
+ if (sfv == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ fd = cl->buf->file->fd;
+ sfv->sfv_fd = fd;
+ sfv->sfv_flag = 0;
+ sfv->sfv_off = cl->buf->file_pos;
+ sfv->sfv_len = (size_t) size;
+ }
+
+ fprev = cl->buf->file_pos + size;
+ send += size;
+ }
+ }
+
+ n = sendfilev(c->fd, vec.elts, vec.nelts, &sent);
+
+ if (n == -1) {
+ err = ngx_errno;
+
+ switch (err) {
+ case NGX_EAGAIN:
+ break;
+
+ case NGX_EINTR:
+ eintr = 1;
+ break;
+
+ default:
+ wev->error = 1;
+ ngx_connection_error(c, err, "sendfilev() failed");
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "sendfilev() sent only %uz bytes", sent);
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "sendfilev: %z %z", n, sent);
+
+ if (send - prev_send == (off_t) sent) {
+ complete = 1;
+ }
+
+ c->sent += sent;
+
+ for ( /* void */ ; in; in = in->next) {
+
+ if (ngx_buf_special(in->buf)) {
+ continue;
+ }
+
+ if (sent == 0) {
+ break;
+ }
+
+ size = ngx_buf_size(in->buf);
+
+ if ((off_t) sent >= size) {
+ sent = (size_t) ((off_t) sent - size);
+
+ if (ngx_buf_in_memory(in->buf)) {
+ in->buf->pos = in->buf->last;
+ }
+
+ if (in->buf->in_file) {
+ in->buf->file_pos = in->buf->file_last;
+ }
+
+ continue;
+ }
+
+ if (ngx_buf_in_memory(in->buf)) {
+ in->buf->pos += sent;
+ }
+
+ if (in->buf->in_file) {
+ in->buf->file_pos += sent;
+ }
+
+ break;
+ }
+
+ if (eintr) {
+ continue;
+ }
+
+ if (!complete) {
+ wev->ready = 0;
+ return in;
+ }
+
+ if (send >= limit || in == NULL) {
+ return in;
+ }
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_amd64.il b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_amd64.il
new file mode 100644
index 00000000000..dc454b20b2f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_amd64.il
@@ -0,0 +1,43 @@
+/
+/ Copyright (C) Igor Sysoev
+/ Copyright (C) Nginx, Inc.
+/
+
+/ ngx_atomic_uint_t ngx_atomic_cmp_set(ngx_atomic_t *lock,
+/ ngx_atomic_uint_t old, ngx_atomic_uint_t set);
+/
+/ the arguments are passed in %rdi, %rsi, %rdx
+/ the result is returned in the %rax
+
+ .inline ngx_atomic_cmp_set,0
+ movq %rsi, %rax
+ lock
+ cmpxchgq %rdx, (%rdi)
+ setz %al
+ movzbq %al, %rax
+ .end
+
+
+/ ngx_atomic_int_t ngx_atomic_fetch_add(ngx_atomic_t *value,
+/ ngx_atomic_int_t add);
+/
+/ the arguments are passed in %rdi, %rsi
+/ the result is returned in the %rax
+
+ .inline ngx_atomic_fetch_add,0
+ movq %rsi, %rax
+ lock
+ xaddq %rax, (%rdi)
+ .end
+
+
+/ ngx_cpu_pause()
+/
+/ the "rep; nop" is used instead of "pause" to avoid the "[ PAUSE ]" hardware
+/ capability added by linker because Solaris/amd64 does not know about it:
+/
+/ ld.so.1: nginx: fatal: hardware capability unsupported: 0x2000 [ PAUSE ]
+
+ .inline ngx_cpu_pause,0
+ rep; nop
+ .end
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_atomic_sparc64.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_atomic_sparc64.h
new file mode 100644
index 00000000000..5f280553c9e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_atomic_sparc64.h
@@ -0,0 +1,61 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#if (NGX_PTR_SIZE == 4)
+#define NGX_CASA ngx_casa
+#else
+#define NGX_CASA ngx_casxa
+#endif
+
+
+ngx_atomic_uint_t
+ngx_casa(ngx_atomic_uint_t set, ngx_atomic_uint_t old, ngx_atomic_t *lock);
+
+ngx_atomic_uint_t
+ngx_casxa(ngx_atomic_uint_t set, ngx_atomic_uint_t old, ngx_atomic_t *lock);
+
+/* the code in src/os/unix/ngx_sunpro_sparc64.il */
+
+
+static ngx_inline ngx_atomic_uint_t
+ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,
+ ngx_atomic_uint_t set)
+{
+ set = NGX_CASA(set, old, lock);
+
+ return (set == old);
+}
+
+
+static ngx_inline ngx_atomic_int_t
+ngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)
+{
+ ngx_atomic_uint_t old, res;
+
+ old = *value;
+
+ for ( ;; ) {
+
+ res = old + add;
+
+ res = NGX_CASA(res, old, value);
+
+ if (res == old) {
+ return res;
+ }
+
+ old = res;
+ }
+}
+
+
+#define ngx_memory_barrier() \
+ __asm (".volatile"); \
+ __asm ("membar #LoadLoad | #LoadStore | #StoreStore | #StoreLoad"); \
+ __asm (".nonvolatile")
+
+#define ngx_cpu_pause()
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_sparc64.il b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_sparc64.il
new file mode 100644
index 00000000000..bdeef612507
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_sparc64.il
@@ -0,0 +1,36 @@
+/
+/ Copyright (C) Igor Sysoev
+/ Copyright (C) Nginx, Inc.
+/
+
+
+/ "casa [%o2] 0x80, %o1, %o0" and
+/ "casxa [%o2] 0x80, %o1, %o0" do the following:
+/
+/ if ([%o2] == %o1) {
+/ swap(%o0, [%o2]);
+/ } else {
+/ %o0 = [%o2];
+/ }
+
+
+/ ngx_atomic_uint_t ngx_casa(ngx_atomic_uint_t set, ngx_atomic_uint_t old,
+/ ngx_atomic_t *lock);
+/
+/ the arguments are passed in the %o0, %o1, %o2
+/ the result is returned in the %o0
+
+ .inline ngx_casa,0
+ casa [%o2] 0x80, %o1, %o0
+ .end
+
+
+/ ngx_atomic_uint_t ngx_casxa(ngx_atomic_uint_t set, ngx_atomic_uint_t old,
+/ ngx_atomic_t *lock);
+/
+/ the arguments are passed in the %o0, %o1, %o2
+/ the result is returned in the %o0
+
+ .inline ngx_casxa,0
+ casxa [%o2] 0x80, %o1, %o0
+ .end
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_x86.il b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_x86.il
new file mode 100644
index 00000000000..fd1cc00492e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_sunpro_x86.il
@@ -0,0 +1,44 @@
+/
+/ Copyright (C) Igor Sysoev
+/ Copyright (C) Nginx, Inc.
+/
+
+/ ngx_atomic_uint_t ngx_atomic_cmp_set(ngx_atomic_t *lock,
+/ ngx_atomic_uint_t old, ngx_atomic_uint_t set);
+/
+/ the arguments are passed on stack (%esp), 4(%esp), 8(%esp)
+
+ .inline ngx_atomic_cmp_set,0
+ movl (%esp), %ecx
+ movl 4(%esp), %eax
+ movl 8(%esp), %edx
+ lock
+ cmpxchgl %edx, (%ecx)
+ setz %al
+ movzbl %al, %eax
+ .end
+
+
+/ ngx_atomic_int_t ngx_atomic_fetch_add(ngx_atomic_t *value,
+/ ngx_atomic_int_t add);
+/
+/ the arguments are passed on stack (%esp), 4(%esp)
+
+ .inline ngx_atomic_fetch_add,0
+ movl (%esp), %ecx
+ movl 4(%esp), %eax
+ lock
+ xaddl %eax, (%ecx)
+ .end
+
+
+/ ngx_cpu_pause()
+/
+/ the "rep; nop" is used instead of "pause" to avoid the "[ PAUSE ]" hardware
+/ capability added by linker because Solaris/i386 does not know about it:
+/
+/ ld.so.1: nginx: fatal: hardware capability unsupported: 0x2000 [ PAUSE ]
+
+ .inline ngx_cpu_pause,0
+ rep; nop
+ .end
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_thread.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_thread.h
new file mode 100644
index 00000000000..49c5d5656a7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_thread.h
@@ -0,0 +1,128 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_THREAD_H_INCLUDED_
+#define _NGX_THREAD_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+#if (NGX_THREADS)
+
+#define NGX_MAX_THREADS 128
+
+#if (NGX_USE_RFORK)
+#include <ngx_freebsd_rfork_thread.h>
+
+
+#else /* use pthreads */
+
+#include <pthread.h>
+
+typedef pthread_t ngx_tid_t;
+
+#define ngx_thread_self() pthread_self()
+#define ngx_log_tid (int) ngx_thread_self()
+
+#if (NGX_FREEBSD) && !(NGX_LINUXTHREADS)
+#define NGX_TID_T_FMT "%p"
+#else
+#define NGX_TID_T_FMT "%d"
+#endif
+
+
+typedef pthread_key_t ngx_tls_key_t;
+
+#define ngx_thread_key_create(key) pthread_key_create(key, NULL)
+#define ngx_thread_key_create_n "pthread_key_create()"
+#define ngx_thread_set_tls pthread_setspecific
+#define ngx_thread_set_tls_n "pthread_setspecific()"
+#define ngx_thread_get_tls pthread_getspecific
+
+
+#define NGX_MUTEX_LIGHT 0
+
+typedef struct {
+ pthread_mutex_t mutex;
+ ngx_log_t *log;
+} ngx_mutex_t;
+
+typedef struct {
+ pthread_cond_t cond;
+ ngx_log_t *log;
+} ngx_cond_t;
+
+#define ngx_thread_sigmask pthread_sigmask
+#define ngx_thread_sigmask_n "pthread_sigmask()"
+
+#define ngx_thread_join(t, p) pthread_join(t, p)
+
+#define ngx_setthrtitle(n)
+
+
+
+ngx_int_t ngx_mutex_trylock(ngx_mutex_t *m);
+void ngx_mutex_lock(ngx_mutex_t *m);
+void ngx_mutex_unlock(ngx_mutex_t *m);
+
+#endif
+
+
+#define ngx_thread_volatile volatile
+
+
+typedef struct {
+ ngx_tid_t tid;
+ ngx_cond_t *cv;
+ ngx_uint_t state;
+} ngx_thread_t;
+
+#define NGX_THREAD_FREE 1
+#define NGX_THREAD_BUSY 2
+#define NGX_THREAD_EXIT 3
+#define NGX_THREAD_DONE 4
+
+extern ngx_int_t ngx_threads_n;
+extern volatile ngx_thread_t ngx_threads[NGX_MAX_THREADS];
+
+
+typedef void * ngx_thread_value_t;
+
+ngx_int_t ngx_init_threads(int n, size_t size, ngx_cycle_t *cycle);
+ngx_err_t ngx_create_thread(ngx_tid_t *tid,
+ ngx_thread_value_t (*func)(void *arg), void *arg, ngx_log_t *log);
+
+ngx_mutex_t *ngx_mutex_init(ngx_log_t *log, ngx_uint_t flags);
+void ngx_mutex_destroy(ngx_mutex_t *m);
+
+
+ngx_cond_t *ngx_cond_init(ngx_log_t *log);
+void ngx_cond_destroy(ngx_cond_t *cv);
+ngx_int_t ngx_cond_wait(ngx_cond_t *cv, ngx_mutex_t *m);
+ngx_int_t ngx_cond_signal(ngx_cond_t *cv);
+
+
+#else /* !NGX_THREADS */
+
+#define ngx_thread_volatile
+
+#define ngx_log_tid 0
+#define NGX_TID_T_FMT "%d"
+
+#define ngx_mutex_trylock(m) NGX_OK
+#define ngx_mutex_lock(m)
+#define ngx_mutex_unlock(m)
+
+#define ngx_cond_signal(cv)
+
+#define ngx_thread_main() 1
+
+#endif
+
+
+#endif /* _NGX_THREAD_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_time.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_time.c
new file mode 100644
index 00000000000..cc760b2eb01
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_time.c
@@ -0,0 +1,104 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * FreeBSD does not test /etc/localtime change, however, we can workaround it
+ * by calling tzset() with TZ and then without TZ to update timezone.
+ * The trick should work since FreeBSD 2.1.0.
+ *
+ * Linux does not test /etc/localtime change in localtime(),
+ * but may stat("/etc/localtime") several times in every strftime(),
+ * therefore we use it to update timezone.
+ *
+ * Solaris does not test /etc/TIMEZONE change too and no workaround available.
+ */
+
+void
+ngx_timezone_update(void)
+{
+#if (NGX_FREEBSD)
+
+ if (getenv("TZ")) {
+ return;
+ }
+
+ putenv("TZ=UTC");
+
+ tzset();
+
+ unsetenv("TZ");
+
+ tzset();
+
+#elif (NGX_LINUX)
+ time_t s;
+ struct tm *t;
+ char buf[4];
+
+ s = time(0);
+
+ t = localtime(&s);
+
+ strftime(buf, 4, "%H", t);
+
+#endif
+}
+
+
+void
+ngx_localtime(time_t s, ngx_tm_t *tm)
+{
+#if (NGX_HAVE_LOCALTIME_R)
+ (void) localtime_r(&s, tm);
+
+#else
+ ngx_tm_t *t;
+
+ t = localtime(&s);
+ *tm = *t;
+
+#endif
+
+ tm->ngx_tm_mon++;
+ tm->ngx_tm_year += 1900;
+}
+
+
+void
+ngx_libc_localtime(time_t s, struct tm *tm)
+{
+#if (NGX_HAVE_LOCALTIME_R)
+ (void) localtime_r(&s, tm);
+
+#else
+ struct tm *t;
+
+ t = localtime(&s);
+ *tm = *t;
+
+#endif
+}
+
+
+void
+ngx_libc_gmtime(time_t s, struct tm *tm)
+{
+#if (NGX_HAVE_LOCALTIME_R)
+ (void) gmtime_r(&s, tm);
+
+#else
+ struct tm *t;
+
+ t = gmtime(&s);
+ *tm = *t;
+
+#endif
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_time.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_time.h
new file mode 100644
index 00000000000..c128c9aa043
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_time.h
@@ -0,0 +1,66 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_TIME_H_INCLUDED_
+#define _NGX_TIME_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef ngx_rbtree_key_t ngx_msec_t;
+typedef ngx_rbtree_key_int_t ngx_msec_int_t;
+
+typedef struct tm ngx_tm_t;
+
+#define ngx_tm_sec tm_sec
+#define ngx_tm_min tm_min
+#define ngx_tm_hour tm_hour
+#define ngx_tm_mday tm_mday
+#define ngx_tm_mon tm_mon
+#define ngx_tm_year tm_year
+#define ngx_tm_wday tm_wday
+#define ngx_tm_isdst tm_isdst
+
+#define ngx_tm_sec_t int
+#define ngx_tm_min_t int
+#define ngx_tm_hour_t int
+#define ngx_tm_mday_t int
+#define ngx_tm_mon_t int
+#define ngx_tm_year_t int
+#define ngx_tm_wday_t int
+
+
+#if (NGX_HAVE_GMTOFF)
+#define ngx_tm_gmtoff tm_gmtoff
+#define ngx_tm_zone tm_zone
+#endif
+
+
+#if (NGX_SOLARIS)
+
+#define ngx_timezone(isdst) (- (isdst ? altzone : timezone) / 60)
+
+#else
+
+#define ngx_timezone(isdst) (- (isdst ? timezone + 3600 : timezone) / 60)
+
+#endif
+
+
+void ngx_timezone_update(void);
+void ngx_localtime(time_t s, ngx_tm_t *tm);
+void ngx_libc_localtime(time_t s, struct tm *tm);
+void ngx_libc_gmtime(time_t s, struct tm *tm);
+
+#define ngx_gettimeofday(tp) (void) gettimeofday(tp, NULL);
+#define ngx_msleep(ms) (void) usleep(ms * 1000)
+#define ngx_sleep(s) (void) sleep(s)
+
+
+#endif /* _NGX_TIME_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_udp_recv.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_udp_recv.c
new file mode 100644
index 00000000000..1c807a08bb2
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_udp_recv.c
@@ -0,0 +1,115 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if (NGX_HAVE_KQUEUE)
+
+ssize_t
+ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
+{
+ ssize_t n;
+ ngx_err_t err;
+ ngx_event_t *rev;
+
+ rev = c->read;
+
+ do {
+ n = recv(c->fd, buf, size, 0);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "recv: fd:%d %d of %d", c->fd, n, size);
+
+ if (n >= 0) {
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ rev->available -= n;
+
+ /*
+ * rev->available may be negative here because some additional
+ * bytes may be received between kevent() and recv()
+ */
+
+ if (rev->available <= 0) {
+ rev->ready = 0;
+ rev->available = 0;
+ }
+ }
+
+ return n;
+ }
+
+ err = ngx_socket_errno;
+
+ if (err == NGX_EAGAIN || err == NGX_EINTR) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "recv() not ready");
+ n = NGX_AGAIN;
+
+ } else {
+ n = ngx_connection_error(c, err, "recv() failed");
+ break;
+ }
+
+ } while (err == NGX_EINTR);
+
+ rev->ready = 0;
+
+ if (n == NGX_ERROR) {
+ rev->error = 1;
+ }
+
+ return n;
+}
+
+#else /* ! NGX_HAVE_KQUEUE */
+
+ssize_t
+ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size)
+{
+ ssize_t n;
+ ngx_err_t err;
+ ngx_event_t *rev;
+
+ rev = c->read;
+
+ do {
+ n = recv(c->fd, buf, size, 0);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "recv: fd:%d %d of %d", c->fd, n, size);
+
+ if (n >= 0) {
+ return n;
+ }
+
+ err = ngx_socket_errno;
+
+ if (err == NGX_EAGAIN || err == NGX_EINTR) {
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "recv() not ready");
+ n = NGX_AGAIN;
+
+ } else {
+ n = ngx_connection_error(c, err, "recv() failed");
+ break;
+ }
+
+ } while (err == NGX_EINTR);
+
+ rev->ready = 0;
+
+ if (n == NGX_ERROR) {
+ rev->error = 1;
+ }
+
+ return n;
+}
+
+#endif /* NGX_HAVE_KQUEUE */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_user.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_user.c
new file mode 100644
index 00000000000..7a71203cb8f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_user.c
@@ -0,0 +1,108 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * Solaris has thread-safe crypt()
+ * Linux has crypt_r(); "struct crypt_data" is more than 128K
+ * FreeBSD needs the mutex to protect crypt()
+ *
+ * TODO:
+ * ngx_crypt_init() to init mutex
+ */
+
+
+#if (NGX_CRYPT)
+
+#if (NGX_HAVE_GNU_CRYPT_R)
+
+ngx_int_t
+ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+{
+ char *value;
+ size_t len;
+ struct crypt_data cd;
+
+ cd.initialized = 0;
+ /* work around the glibc bug */
+ cd.current_salt[0] = ~salt[0];
+
+ value = crypt_r((char *) key, (char *) salt, &cd);
+
+ if (value) {
+ len = ngx_strlen(value) + 1;
+
+ *encrypted = ngx_pnalloc(pool, len);
+ if (*encrypted == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(*encrypted, value, len);
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, pool->log, ngx_errno, "crypt_r() failed");
+
+ return NGX_ERROR;
+}
+
+#else
+
+ngx_int_t
+ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+{
+ char *value;
+ size_t len;
+ ngx_err_t err;
+
+#if (NGX_THREADS && NGX_NONREENTRANT_CRYPT)
+
+ /* crypt() is a time consuming function, so we only try to lock */
+
+ if (ngx_mutex_trylock(ngx_crypt_mutex) != NGX_OK) {
+ return NGX_AGAIN;
+ }
+
+#endif
+
+ value = crypt((char *) key, (char *) salt);
+
+ if (value) {
+ len = ngx_strlen(value) + 1;
+
+ *encrypted = ngx_pnalloc(pool, len);
+ if (*encrypted == NULL) {
+#if (NGX_THREADS && NGX_NONREENTRANT_CRYPT)
+ ngx_mutex_unlock(ngx_crypt_mutex);
+#endif
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(*encrypted, value, len);
+#if (NGX_THREADS && NGX_NONREENTRANT_CRYPT)
+ ngx_mutex_unlock(ngx_crypt_mutex);
+#endif
+ return NGX_OK;
+ }
+
+ err = ngx_errno;
+
+#if (NGX_THREADS && NGX_NONREENTRANT_CRYPT)
+ ngx_mutex_unlock(ngx_crypt_mutex);
+#endif
+
+ ngx_log_error(NGX_LOG_CRIT, pool->log, err, "crypt() failed");
+
+ return NGX_ERROR;
+}
+
+#endif
+
+#endif /* NGX_CRYPT */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_user.h b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_user.h
new file mode 100644
index 00000000000..6e82204f4d1
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_user.h
@@ -0,0 +1,24 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_USER_H_INCLUDED_
+#define _NGX_USER_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef uid_t ngx_uid_t;
+typedef gid_t ngx_gid_t;
+
+
+ngx_int_t ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,
+ u_char **encrypted);
+
+
+#endif /* _NGX_USER_H_INCLUDED_ */
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_writev_chain.c b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_writev_chain.c
new file mode 100644
index 00000000000..805982d6558
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/ngx_writev_chain.c
@@ -0,0 +1,185 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if (IOV_MAX > 64)
+#define NGX_IOVS 64
+#else
+#define NGX_IOVS IOV_MAX
+#endif
+
+
+ngx_chain_t *
+ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
+{
+ u_char *prev;
+ ssize_t n, size, sent;
+ off_t send, prev_send;
+ ngx_uint_t eintr, complete;
+ ngx_err_t err;
+ ngx_array_t vec;
+ ngx_chain_t *cl;
+ ngx_event_t *wev;
+ struct iovec *iov, iovs[NGX_IOVS];
+
+ wev = c->write;
+
+ if (!wev->ready) {
+ return in;
+ }
+
+#if (NGX_HAVE_KQUEUE)
+
+ if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
+ (void) ngx_connection_error(c, wev->kq_errno,
+ "kevent() reported about an closed connection");
+ wev->error = 1;
+ return NGX_CHAIN_ERROR;
+ }
+
+#endif
+
+ /* the maximum limit size is the maximum size_t value - the page size */
+
+ if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
+ limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
+ }
+
+ send = 0;
+
+ vec.elts = iovs;
+ vec.size = sizeof(struct iovec);
+ vec.nalloc = NGX_IOVS;
+ vec.pool = c->pool;
+
+ for ( ;; ) {
+ prev = NULL;
+ iov = NULL;
+ eintr = 0;
+ complete = 0;
+ prev_send = send;
+
+ vec.nelts = 0;
+
+ /* create the iovec and coalesce the neighbouring bufs */
+
+ for (cl = in; cl && send < limit; cl = cl->next) {
+
+ if (ngx_buf_special(cl->buf)) {
+ continue;
+ }
+
+#if 1
+ if (!ngx_buf_in_memory(cl->buf)) {
+ ngx_debug_point();
+ }
+#endif
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if (send + size > limit) {
+ size = (ssize_t) (limit - send);
+ }
+
+ if (prev == cl->buf->pos) {
+ iov->iov_len += size;
+
+ } else {
+ if (vec.nelts >= IOV_MAX) {
+ break;
+ }
+
+ iov = ngx_array_push(&vec);
+ if (iov == NULL) {
+ return NGX_CHAIN_ERROR;
+ }
+
+ iov->iov_base = (void *) cl->buf->pos;
+ iov->iov_len = size;
+ }
+
+ prev = cl->buf->pos + size;
+ send += size;
+ }
+
+ n = writev(c->fd, vec.elts, vec.nelts);
+
+ if (n == -1) {
+ err = ngx_errno;
+
+ switch (err) {
+ case NGX_EAGAIN:
+ break;
+
+ case NGX_EINTR:
+ eintr = 1;
+ break;
+
+ default:
+ wev->error = 1;
+ (void) ngx_connection_error(c, err, "writev() failed");
+ return NGX_CHAIN_ERROR;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
+ "writev() not ready");
+ }
+
+ sent = n > 0 ? n : 0;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "writev: %z", sent);
+
+ if (send - prev_send == sent) {
+ complete = 1;
+ }
+
+ c->sent += sent;
+
+ for (cl = in; cl; cl = cl->next) {
+
+ if (ngx_buf_special(cl->buf)) {
+ continue;
+ }
+
+ if (sent == 0) {
+ break;
+ }
+
+ size = cl->buf->last - cl->buf->pos;
+
+ if (sent >= size) {
+ sent -= size;
+ cl->buf->pos = cl->buf->last;
+
+ continue;
+ }
+
+ cl->buf->pos += sent;
+
+ break;
+ }
+
+ if (eintr) {
+ continue;
+ }
+
+ if (!complete) {
+ wev->ready = 0;
+ return cl;
+ }
+
+ if (send >= limit || cl == NULL) {
+ return cl;
+ }
+
+ in = cl;
+ }
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/rfork_thread.S b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/rfork_thread.S
new file mode 100644
index 00000000000..e570349f93b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/nginx-1.7.4/src/os/unix/rfork_thread.S
@@ -0,0 +1,73 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <sys/syscall.h>
+#include <machine/asm.h>
+
+/*
+ * rfork_thread(3) - rfork_thread(flags, stack, func, arg);
+ */
+
+#define KERNCALL int $0x80
+
+ENTRY(rfork_thread)
+ push %ebp
+ mov %esp, %ebp
+ push %esi
+
+ mov 12(%ebp), %esi # the thread stack address
+
+ sub $4, %esi
+ mov 20(%ebp), %eax # the thread argument
+ mov %eax, (%esi)
+
+ sub $4, %esi
+ mov 16(%ebp), %eax # the thread start address
+ mov %eax, (%esi)
+
+ push 8(%ebp) # rfork(2) flags
+ push $0
+ mov $SYS_rfork, %eax
+ KERNCALL
+ jc error
+
+ cmp $0, %edx
+ jne child
+
+parent:
+ add $8, %esp
+ pop %esi
+ leave
+ ret
+
+child:
+ mov %esi, %esp
+ pop %eax
+ call *%eax # call a thread start address ...
+ add $4, %esp
+
+ push %eax
+ push $0
+ mov $SYS_exit, %eax # ... and exit(2) after a thread would return
+ KERNCALL
+
+error:
+ add $8, %esp
+ pop %esi
+ leave
+ PIC_PROLOGUE
+
+ /* libc's cerror: jmp PIC_PLT(HIDENAME(cerror)) */
+
+ push %eax
+ call PIC_PLT(CNAME(__error))
+ pop %ecx
+ PIC_EPILOGUE
+ mov %ecx, (%eax)
+ mov $-1, %eax
+ mov $-1, %edx
+ ret
diff --git a/storage/mroonga/vendor/groonga/vendor/onigmo/Makefile.am b/storage/mroonga/vendor/groonga/vendor/onigmo/Makefile.am
new file mode 100644
index 00000000000..40ccc1d73db
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/onigmo/Makefile.am
@@ -0,0 +1,22 @@
+EXTRA_DIST = \
+ configure
+
+CONFIGURE_DEPENDENCIES = \
+ configure
+
+ALL_DEPEND_TARGETS =
+CLEAN_DEPEND_TARGETS =
+if WITH_MRUBY
+ALL_DEPEND_TARGETS += onigmo-all
+CLEAN_DEPEND_TARGETS += onigmo-clean
+endif
+
+onigmo-all:
+ cd ../onigmo-source && $(MAKE) all
+
+all: $(ALL_DEPEND_TARGETS)
+
+onigmo-clean:
+ cd ../onigmo-source && $(MAKE) clean
+
+clean: $(CLEAN_DEPEND_TARGETS)
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/CMakeLists.txt b/storage/mroonga/vendor/groonga/vendor/plugins/CMakeLists.txt
new file mode 100644
index 00000000000..845c57f5716
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Copyright(C) 2013 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+file(GLOB
+ PLUGIN_CMAKE_LISTS_LIST
+ "${CMAKE_CURRENT_SOURCE_DIR}/*/CMakeLists.txt")
+if(PLUGIN_CMAKE_LISTS_LIST)
+ foreach(PLUGIN_CMAKE_LISTS "${PLUGIN_CMAKE_LISTS_LIST}")
+ string(REGEX REPLACE
+ "(^${CMAKE_CURRENT_SOURCE_DIR}/+|/+CMakeLists\\.txt$)" ""
+ PLUGIN_DIR
+ "${PLUGIN_CMAKE_LISTS}")
+ add_subdirectory("${PLUGIN_DIR}")
+ endforeach(PLUGIN_CMAKE_LISTS)
+endif()
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/AUTHORS b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/AUTHORS
new file mode 100644
index 00000000000..95053dbcb47
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/AUTHORS
@@ -0,0 +1 @@
+See README.md.
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/CMakeLists.txt b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/CMakeLists.txt
new file mode 100644
index 00000000000..484a7d63bfd
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/CMakeLists.txt
@@ -0,0 +1,65 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; version 2
+# of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA
+
+cmake_minimum_required(VERSION 2.6)
+project(groonga-normalizer-mysql)
+
+file(READ "${CMAKE_CURRENT_SOURCE_DIR}/version" VERSION)
+
+if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
+ set(GROONGA_NORMALIZER_MYSQL_BUNDLED FALSE)
+else()
+ set(GROONGA_NORMALIZER_MYSQL_BUNDLED TRUE)
+endif()
+
+if(GROONGA_NORMALIZER_MYSQL_BUNDLED)
+ set(GROONGA_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include")
+ set(GROONGA_LIBRARY_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/lib")
+ set(GROONGA_LIBRARIES "libgroonga")
+ set(GROONGA_PLUGINS_DIR "${GRN_RELATIVE_PLUGINS_DIR}")
+else()
+ include(FindPkgConfig)
+ include(${CMAKE_CURRENT_SOURCE_DIR}/build/cmake_modules/ReadFileList.cmake)
+
+ file(READ
+ ${CMAKE_CURRENT_SOURCE_DIR}/required_groonga_version
+ GROONGA_REQUIRED_VERSION)
+ string(STRIP "${GROONGA_REQUIRED_VERSION}" GROONGA_REQUIRED_VERSION)
+
+ pkg_check_modules(GROONGA REQUIRED "groonga >= ${GROONGA_REQUIRED_VERSION}")
+ _pkgconfig_invoke(groonga GROONGA PLUGINS_DIR "" --variable=pluginsdir)
+endif()
+
+include_directories(
+ ${CMAKE_BINARY_DIR}
+ ${GROONGA_INCLUDE_DIRS})
+
+link_directories(
+ ${GROONGA_LIBRARY_DIRS})
+
+add_subdirectory(normalizers)
+
+configure_file(
+ groonga-normalizer-mysql.pc.in
+ "${CMAKE_CURRENT_BINARY_DIR}/groonga-normalizer-mysql.pc"
+ @ONLY)
+
+if(NOT GROONGA_NORMALIZER_MYSQL_FOUND)
+ install(
+ FILES "${CMAKE_CURRENT_BINARY_DIR}/groonga-normalizer-mysql.pc"
+ DESTINATION "lib/pkgconfig/")
+endif()
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/ChangeLog b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/ChangeLog
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/ChangeLog
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/INSTALL b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/INSTALL
new file mode 100644
index 00000000000..95053dbcb47
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/INSTALL
@@ -0,0 +1 @@
+See README.md.
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/Makefile.am
new file mode 100644
index 00000000000..cdc7ec525f4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/Makefile.am
@@ -0,0 +1,84 @@
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = \
+ build \
+ normalizers \
+ packages \
+ test \
+ doc
+
+docdir = $(datadir)/doc/$(PACKAGE)
+dist_doc_DATA = \
+ README.md
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = groonga-normalizer-mysql.pc
+
+EXTRA_DIST = \
+ CMakeLists.txt
+
+update-tables:
+ cd normalizers && $(MAKE) update-tables
+
+echo-groonga:
+ @echo $(GROONGA)
+
+echo-groonga-httpd:
+ @echo $(GROONGA_HTTPD)
+
+echo-ruby:
+ @echo $(RUBY)
+
+tag:
+ cd $(top_srcdir) && git tag v$(VERSION) -a -m 'groonga-normalizer-mysql $(VERSION)!!!'
+
+update-version:
+ @if test -z "$(NEW_VERSION)"; then \
+ echo "\$$(NEW_VERSION) is missing"; \
+ exit 1; \
+ fi
+ @echo -n $(NEW_VERSION) > $(srcdir)/version
+
+update-latest-release: misc
+ @if test -z "$(OLD_RELEASE)"; then \
+ echo "\$$(OLD_RELEASE) is missing"; \
+ exit 1; \
+ fi
+ @if test -z "$(OLD_RELEASE_DATE)"; then \
+ echo "\$$(OLD_RELEASE_DATE) is missing"; \
+ exit 1; \
+ fi
+ @if test -z "$(NEW_RELEASE_DATE)"; then \
+ echo "\$$(NEW_RELEASE_DATE) is missing"; \
+ exit 1; \
+ fi
+ cd $(top_srcdir) && \
+ misc/update-latest-release.rb \
+ $(PACKAGE) $(OLD_RELEASE) $(OLD_RELEASE_DATE) \
+ $(VERSION) $(NEW_RELEASE_DATE) \
+ packages/rpm/fedora/groonga-normalizer-mysql.spec.in \
+ packages/rpm/centos/groonga-normalizer-mysql.spec.in \
+ packages/debian/changelog
+
+misc:
+ @if test -z "$(CUTTER_SOURCE_PATH)"; then \
+ echo "\$$(CUTTER_SOURCE_PATH) is missing"; \
+ exit 1; \
+ fi
+ ln -s "$(CUTTER_SOURCE_PATH)/misc" misc
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/NEWS b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/NEWS
new file mode 100644
index 00000000000..a4685347e88
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/NEWS
@@ -0,0 +1 @@
+See doc/text/news.md.
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/README b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/README
new file mode 100644
index 00000000000..95053dbcb47
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/README
@@ -0,0 +1 @@
+See README.md.
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/README.md b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/README.md
new file mode 100644
index 00000000000..866aba84d40
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/README.md
@@ -0,0 +1,201 @@
+# README
+
+## Name
+
+groonga-normalizer-mysql
+
+## Description
+
+Groonga-normalizer-mysql is a groonga plugin. It provides MySQL
+compatible normalizers and a custom normalizer to groonga.
+
+MySQL compatible normalizers are `NormalizerMySQLGeneralCI` and
+`NormalizerMySQLUnicodeCI`. `NormalizerMySQLGeneralCI` corresponds to
+`utf8mb4_general_ci`. `NormalizerMySQLUnicodeCI` corresponds to
+`utf8mb4_unicode_ci`.
+
+A custom normalizer is
+`NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark`. It is
+self-descriptive name but long. It is a variant normalizer of
+`NormalizerMySQLUnicode`. It has different behaviors. The followings
+are the different behaviors.
+
+* `NormalizerMySQLUnicode` normalizes all small Hiragana such as `ぁ`,
+ `っ` to Hiragana such as `あ`, `つ`.
+ `NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark`
+ doesn't normalize `ぁ` to `あ` nor `っ` to `つ`. `ぁ` and `あ` are
+ different characters. `っ` and `つ` are also different characters.
+ This behavior is described by `ExceptKanaCI` in the long name. This
+ following behaviors ared described by
+ `ExceptKanaWithVoicedSoundMark` in the long name.
+* `NormalizerMySQLUnicode` normalizes all Hiragana with voiced sound
+ mark such as `が` to Hiragana without voiced sound mark such as `か`.
+ `NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark` doesn't
+ normalize `が` to `か`. `が` and `か` are different characters.
+* `NormalizerMySQLUnicode` normalizes all Hiragana with semi-voiced sound
+ mark such as `ぱ` to Hiragana without semi-voiced sound mark such as `は`.
+ `NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark` doesn't
+ normalize `ぱ` to `は`. `ぱ` and `は` are different characters.
+* `NormalizerMySQLUnicode` normalizes all Katakana with voiced sound
+ mark such as `ガ` to Katakana without voiced sound mark such as `カ`.
+ `NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark` doesn't
+ normalize `ガ` to `カ`. `ガ` and `カ` are different characters.
+* `NormalizerMySQLUnicode` normalizes all Katakana with semi-voiced sound
+ mark such as `パ` to Hiragana without semi-voiced sound mark such as `ハ`.
+ `NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark` doesn't
+ normalize `パ` to `ハ`. `パ` and `ハ` are different characters.
+* `NormalizerMySQLUnicode` normalizes all halfwidth Katakana with
+ voiced sound mark such as `ガ` to halfwidth Katakana without voiced
+ sound mark such as `カ`.
+ `NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark`
+ normalizes all halfwidth Katakana with voided sound mark such as `ガ`
+ to fullwidth Katakana with voiced sound mark such as `ガ`.
+
+`NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark` is MySQL
+incompatible normalizer but it is useful for Japanese text. For
+example, `ふらつく` and `ブラック` has different
+means. `NormalizerMySQLUnicodeCI` identifies `ふらつく` with `ブラック
+` but `NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark
+doesn't identify them.
+
+## Install
+
+### Debian GNU/Linux
+
+[Add apt-line for the groonga deb package repository](http://groonga.org/docs/install/debian.html)
+and install `groonga-normalizer-mysql` package:
+
+ % sudo aptitude -V -D -y install groonga-normalizer-mysql
+
+### Ubuntu
+
+[Add apt-line for the groonga deb package repository](http://groonga.org/docs/install/ubuntu.html)
+and install `groonga-normalizer-mysql` package:
+
+ % sudo aptitude -V -D -y install groonga-normalizer-mysql
+
+
+### CentOS
+
+Install `groonga-repository` package:
+
+ % sudo rpm -ivh http://packages.groonga.org/centos/groonga-release-1.1.0-1.noarch.rpm
+ % sudo yum makecache
+
+Then install `groonga-normalizer-mysql` package:
+
+ % sudo yum install -y groonga-normalizer-mysql
+
+### Fedora
+
+Install `groonga-repository` package:
+
+ % sudo rpm -ivh http://packages.groonga.org/fedora/groonga-release-1.1.0-1.noarch.rpm
+ % sudo yum makecache
+
+Then install `groonga-normalizer-mysql` package:
+
+ % sudo yum install -y groonga-normalizer-mysql
+
+### OS X - Homebrew
+
+Install `groonga-normalizer-mysql` package:
+
+ % brew install groonga-normalizer-mysql
+
+### Windows
+
+You need to build from source. Here are build instructions.
+
+#### Build system
+
+Install the following build tools:
+
+* [Microsoft Visual Studio 2010 Express](http://www.microsoft.com/japan/msdn/vstudio/express/): 2012 isn't tested yet.
+* [CMake](http://www.cmake.org/)
+
+#### Build groonga
+
+Download the latest groonga source from [packages.groonga.org](http://packages.groonga.org/source/groonga/). Source file name is formatted as `groonga-X.Y.Z.zip`.
+
+Extract the source and move to the source folder:
+
+ > cd ...\groonga-X.Y.Z
+ groonga-X.Y.Z>
+
+Run CMake. Here is a command line to install groonga to `C:\groonga` folder:
+
+ groonga-X.Y.Z> cmake . -G "Visual Studio 10 Win64" -DCMAKE_INSTALL_PREFIX=C:\groonga
+
+Build:
+
+ groonga-X.Y.Z> cmake --build . --config Release
+
+Install:
+
+ groonga-X.Y.Z> cmake --build . --config Release --target Install
+
+#### Build groonga-normalizer-mysql
+
+Download the latest groonga-normalizer-mysql source from [packages.groonga.org](http://packages.groonga.org/source/groonga-normalizer-mysql/). Source file name is formatted as `groonga-normalizer-X.Y.Z.zip`.
+
+Extract the source and move to the source folder:
+
+ > cd ...\groonga-normalizer-mysql-X.Y.Z
+ groonga-normalizer-mysql-X.Y.Z>
+
+IMPORTANT!!!: Set `PKG_CONFIG_PATH` environment variable:
+
+ groonga-normalizer-mysql-X.Y.Z> set PKG_CONFIG_PATH=C:\groongalocal\lib\pkgconfig
+
+Run CMake. Here is a command line to install groonga to `C:\groonga` folder:
+
+ groonga-normalizer-mysql-X.Y.Z> cmake . -G "Visual Studio 10 Win64" -DCMAKE_INSTALL_PREFIX=C:\groonga
+
+Build:
+
+ groonga-normalizer-mysql-X.Y.Z> cmake --build . --config Release
+
+Install:
+
+ groonga-normalizer-mysql-X.Y.Z> cmake --build . --config Release --target Install
+
+## Usage
+
+First, you need to register `normalizers/mysql` plugin:
+
+ groonga> register normalizers/mysql
+
+Then, you can use `NormalizerMySQLGeneralCI` and
+`NormalizerMySQLUnicodeCI` as normalizers:
+
+ groonga> table_create Lexicon TABLE_PAT_KEY --default_tokenizer TokenBigram --normalizer NormalizerMySQLGeneralCI
+
+## Dependencies
+
+* groonga >= 3.0.3
+
+## Mailing list
+
+* English: [groonga-talk@lists.sourceforge.net](https://lists.sourceforge.net/lists/listinfo/groonga-talk)
+* Japanese: [groonga-dev@lists.sourceforge.jp](http://lists.sourceforge.jp/mailman/listinfo/groonga-dev)
+
+## Thanks
+
+* Alexander Barkov \<bar@udm.net\>: The author of
+ `MYSQL_SOURCE/strings/ctype-utf8.c`.
+* ...
+
+## Authors
+
+* Kouhei Sutou \<kou@clear-code.com\>
+
+## License
+
+LGPLv2 only. See doc/text/lgpl-2.0.txt for details.
+
+This program uses normalization table defined in MySQL source code. So
+this program is derived work of
+`MYSQL_SOURCE/strings/ctype-utf8.c`. This program is the same license
+as `MYSQL_SOURCE/strings/ctype-utf8.c` and it is licensed under LGPLv2
+only.
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/autogen.sh b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/autogen.sh
new file mode 100755
index 00000000000..62385ed00d6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/autogen.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+mkdir -p m4
+autoreconf -i
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/build/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/build/Makefile.am
new file mode 100644
index 00000000000..4a73087dc26
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/build/Makefile.am
@@ -0,0 +1,18 @@
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+SUBDIRS = \
+ cmake_modules
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/build/cmake_modules/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/build/cmake_modules/Makefile.am
new file mode 100644
index 00000000000..44ac5150c6d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/build/cmake_modules/Makefile.am
@@ -0,0 +1,18 @@
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+EXTRA_DIST = \
+ ReadFileList.cmake
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/build/cmake_modules/ReadFileList.cmake b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/build/cmake_modules/ReadFileList.cmake
new file mode 100644
index 00000000000..018587991d8
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/build/cmake_modules/ReadFileList.cmake
@@ -0,0 +1,27 @@
+# Copyright(C) 2012 Brazil
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License version 2.1 as published by the Free Software Foundation.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+macro(read_file_list file_name output_variable)
+ file(READ ${file_name} ${output_variable})
+ # Remove variable declaration at the first line:
+ # "libgroonga_la_SOURCES = \" -> ""
+ string(REGEX REPLACE "^.*=[ \t]*\\\\" ""
+ ${output_variable} "${${output_variable}}")
+ # Remove white spaces: " com.c \\\n com.h \\\n" -> "com.c\\com.h"
+ string(REGEX REPLACE "[ \t\n]" "" ${output_variable} "${${output_variable}}")
+ # Convert string to list: "com.c\\com.h" -> "com.c;com.h"
+ # NOTE: List in CMake is ";" separated string.
+ string(REGEX REPLACE "\\\\" ";" ${output_variable} "${${output_variable}}")
+endmacro()
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/configure.ac b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/configure.ac
new file mode 100644
index 00000000000..a2e71e465db
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/configure.ac
@@ -0,0 +1,179 @@
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+AC_PREREQ(2.59)
+m4_define([groonga_normalizer_mysql_version], m4_include(version))
+AC_INIT([groonga-normalizer-mysql],
+ groonga_normalizer_mysql_version,
+ [groonga-talk@lists.sourceforge.net])
+
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_SRCDIR([normalizers/mysql.c])
+AM_CONFIG_HEADER([config.h])
+
+AM_INIT_AUTOMAKE([1.9 tar-pax])
+
+AC_PROG_LIBTOOL
+
+if test "$GCC" = "yes"; then
+ CFLAGS="$CFLAGS -Wall -Wextra"
+ CFLAGS="$CFLAGS -Wmissing-declarations -Wmissing-prototypes"
+fi
+
+m4_define([groonga_required_version], m4_include(required_groonga_version))
+GROONGA_REQUIRED_VERSION=groonga_required_version
+PKG_CHECK_MODULES([GROONGA], [groonga >= ${GROONGA_REQUIRED_VERSION}])
+
+_PKG_CONFIG(GROONGA_PLUGINS_DIR, [variable=pluginsdir], [groonga])
+_PKG_CONFIG(GROONGA, [variable=groonga], [groonga])
+
+GROONGA_PLUGINS_DIR="${pkg_cv_GROONGA_PLUGINS_DIR}"
+GROONGA="${pkg_cv_GROONGA}"
+
+AC_SUBST(GROONGA_PLUGINS_DIR)
+AC_SUBST(GROONGA)
+
+normalizers_pluginsdir="\${GROONGA_PLUGINS_DIR}/normalizers"
+AC_SUBST(normalizers_pluginsdir)
+
+# check Ruby for test
+ac_cv_ruby_available="no"
+AC_ARG_WITH([ruby19],
+ AS_HELP_STRING([--with-ruby19=PATH],
+ [Ruby 1.9 interpreter path (default: auto)]),
+ [RUBY="$withval"],
+ [RUBY="auto"])
+
+if test "x$RUBY" = "xno"; then
+ RUBY=
+else
+ if test "x$RUBY" = "xauto"; then
+ AC_PATH_PROGS(RUBY, [ruby1.9 ruby19 ruby1.9.1 ruby], ruby19-not-found)
+ if test "$RUBY" != "ruby19-not-found"; then
+ ruby_version="`$RUBY --version`"
+ if echo "$ruby_version" | grep -q -- 'ruby 1\.9'; then
+ ac_cv_ruby_available="yes"
+ else
+ AC_MSG_WARN([$RUBY isn't Ruby 1.9 ($ruby_version)])
+ fi
+ fi
+ else
+ ruby_not_found_warning_message="$RUBY is not found. Disable HTTP test."
+ case "$RUBY" in
+ /*)
+ AC_CHECK_FILE([$RUBY],
+ [ac_cv_ruby_available="yes"],
+ [AC_MSG_WARN([$ruby_not_found_warning_message])
+ RUBY="$RUBY-not-found"])
+ ;;
+ *)
+ ruby_not_found="$RUBY-not-found"
+ AC_PATH_PROGS(RUBY, "$RUBY", "$ruby_not_found")
+ if test "$RUBY" = "$ruby_not_found"; then
+ AC_MSG_WARN([$ruby_not_found_warning_message])
+ else
+ ac_cv_ruby_available="yes"
+ fi
+ ;;
+ esac
+ fi
+fi
+AC_SUBST(RUBY)
+AM_CONDITIONAL([WITH_TEST], [test "$ac_cv_ruby_available" = "yes"])
+
+# For package
+AC_ARG_WITH(rsync-path,
+ [AS_HELP_STRING([--with-rsync-path=PATH],
+ [specify rsync path to upload groonga packages.])],
+ [RSYNC_PATH="$withval"],
+ [RSYNC_PATH="packages@packages.groonga.org:public"])
+AC_SUBST(RSYNC_PATH)
+
+AC_ARG_WITH(launchpad-uploader-pgp-key,
+ [AS_HELP_STRING([--with-launchpad-uploader-pgp-key=KEY],
+ [specify PGP key UID to upload Groonga packages to Launchpad.])],
+ [LAUNCHPAD_UPLOADER_PGP_KEY="$withval"],
+ [LAUNCHPAD_UPLOADER_PGP_KEY=""])
+AC_SUBST(LAUNCHPAD_UPLOADER_PGP_KEY)
+
+AC_ARG_WITH([groonga-source-path],
+ AS_HELP_STRING([--with-groonga-source-path=PATH],
+ [Specify Groonga source path for
+ groonga-normalizer-mysql's release manager.]),
+ [GROONGA_SOURCE_PATH="$withval"])
+case "$GROONGA_SOURCE_PATH" in
+ ""|/*)
+ : # do nothing
+ ;;
+ *)
+ GROONGA_SOURCE_PATH="\$(top_builddir)/${GROONGA_SOURCE_PATH}"
+ ;;
+esac
+AC_SUBST(GROONGA_SOURCE_PATH)
+
+AC_ARG_WITH([cutter-source-path],
+ AS_HELP_STRING([--with-cutter-source-path=PATH],
+ [Specify Cutter source path for
+ groonga-normalizer-mysql's release manager.]),
+ [CUTTER_SOURCE_PATH="$withval"])
+case "$CUTTER_SOURCE_PATH" in
+ ""|/*)
+ : # do nothing
+ ;;
+ *)
+ CUTTER_SOURCE_PATH="\$(top_builddir)/${CUTTER_SOURCE_PATH}"
+ ;;
+esac
+AC_SUBST(CUTTER_SOURCE_PATH)
+
+GPG_UID=m4_include(gpg_uid)
+AC_SUBST(GPG_UID)
+
+AC_CONFIG_FILES([
+ Makefile
+ groonga-normalizer-mysql.pc
+ build/Makefile
+ build/cmake_modules/Makefile
+ normalizers/Makefile
+ packages/Makefile
+ packages/apt/Makefile
+ packages/ubuntu/Makefile
+ packages/rpm/Makefile
+ packages/rpm/centos/Makefile
+ packages/rpm/centos/groonga-normalizer-mysql.spec
+ packages/rpm/fedora/Makefile
+ packages/rpm/fedora/groonga-normalizer-mysql.spec
+ packages/yum/Makefile
+ packages/yum/env.sh
+ packages/source/Makefile
+ test/Makefile
+ doc/Makefile
+ doc/text/Makefile
+])
+
+AC_OUTPUT
+
+echo "$PACKAGE_NAME $PACKAGE_VERSION configuration:"
+echo "-----------------------"
+echo " Compiler: ${CC}"
+echo " CFLAGS: ${CFLAGS}"
+echo " CXXFLAGS: ${CXXFLAGS}"
+echo " Libraries: ${LIBS}"
+echo
+echo "groonga"
+echo " CFLAGS: ${GROONGA_CFLAGS}"
+echo " Libraries: ${GROONGA_LIBS}"
+echo " install directory: ${normalizers_pluginsdir}"
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/data/travis/setup.sh b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/data/travis/setup.sh
new file mode 100755
index 00000000000..a4752f82131
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/data/travis/setup.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+set -e
+
+if [ "$GROONGA_NORMALIZER_MYSQL_MASTER" = "yes" ]; then
+ if ! pkg-config --exists groonga; then
+ sudo apt-get install -qq -y -V libgroonga-dev
+ fi
+ git clone --depth 1 https://github.com/groonga/groonga-normalizer-mysql.git
+ cd groonga-normalizer-mysql
+ ./autogen.sh
+ ./configure CFLAGS="-O0 -g3" CXXFLAGS="-O0 -g3" --prefix=/usr
+ make -j$(grep '^processor' /proc/cpuinfo | wc -l) > /dev/null
+ sudo make install > /dev/null
+ cd ..
+else
+ sudo apt-get install -qq -y -V groonga-normalizer-mysql
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/doc/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/doc/Makefile.am
new file mode 100644
index 00000000000..771336d84a0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/doc/Makefile.am
@@ -0,0 +1,18 @@
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+SUBDIRS = \
+ text
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/doc/text/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/doc/text/Makefile.am
new file mode 100644
index 00000000000..52dbe4a8f0d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/doc/text/Makefile.am
@@ -0,0 +1,19 @@
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+docdir = $(datadir)/doc/$(PACKAGE)
+dist_doc_DATA = \
+ lgpl-2.0.txt
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/doc/text/lgpl-2.0.txt b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/doc/text/lgpl-2.0.txt
new file mode 100644
index 00000000000..5bc8fb2c8f7
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/doc/text/lgpl-2.0.txt
@@ -0,0 +1,481 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/gpg_uid b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/gpg_uid
new file mode 100644
index 00000000000..7c1a800ba92
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/gpg_uid
@@ -0,0 +1 @@
+45499429
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/groonga-normalizer-mysql.pc.in b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/groonga-normalizer-mysql.pc.in
new file mode 100644
index 00000000000..c1093445916
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/groonga-normalizer-mysql.pc.in
@@ -0,0 +1,5 @@
+plugin_name=normalizers/mysql
+
+Name: groonga-normalizer-mysql
+Description: A MySQL compatible normalizer plugin for groonga
+Version: @VERSION@
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/CMakeLists.txt b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/CMakeLists.txt
new file mode 100644
index 00000000000..ae290b66347
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; version 2
+# of the License.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA
+
+set(NORMALIZERS_DIR "${GROONGA_PLUGINS_DIR}/normalizers")
+read_file_list(${CMAKE_CURRENT_SOURCE_DIR}/mysql_sources.am MYSQL_SOURCES)
+add_library(mysql_normalizer MODULE ${MYSQL_SOURCES})
+set_target_properties(mysql_normalizer PROPERTIES
+ PREFIX ""
+ OUTPUT_NAME "mysql")
+target_link_libraries(mysql_normalizer ${GROONGA_LIBRARIES})
+if(NOT MRN_GROONGA_BUNDLED)
+ install(TARGETS mysql_normalizer DESTINATION "${NORMALIZERS_DIR}")
+endif()
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/Makefile.am
new file mode 100644
index 00000000000..c5c131ce2af
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/Makefile.am
@@ -0,0 +1,69 @@
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+EXTRA_DIST = \
+ CMakeLists.txt
+
+AM_CFLAGS = \
+ $(GROONGA_CFLAGS)
+
+AM_LDFLAGS = \
+ -avoid-version \
+ -module \
+ -no-undefined
+
+LIBS = \
+ $(GROONGA_LIBS)
+
+normalizers_plugins_LTLIBRARIES =
+normalizers_plugins_LTLIBRARIES += mysql.la
+
+include mysql_sources.am
+
+ensure-mysql-source-dir:
+ @if test -z "$(MYSQL_SOURCE_DIR)"; then \
+ echo "\$$(MYSQL_SOURCE_DIR) is missing"; \
+ exit 1; \
+ fi
+
+UPDATE_TABLES_TARGETS = \
+ update-general-ci-table \
+ update-unicode-ci-table \
+ update-unicode-ci-except-kana-ci-kana-with-voiced-sound-mark-table
+
+update-tables: $(UPDATE_TABLES_TARGETS)
+
+update-general-ci-table: ensure-mysql-source-dir
+ $(RUBY) \
+ $(top_srcdir)/tool/generate_utf8_table.rb \
+ $(MYSQL_SOURCE_DIR)/strings/ctype-utf8.c > \
+ $(srcdir)/mysql_general_ci_table.h
+
+update-unicode-ci-table: ensure-mysql-source-dir
+ $(RUBY) \
+ $(top_srcdir)/tool/generate_uca_table.rb \
+ $(MYSQL_SOURCE_DIR)/strings/ctype-uca.c > \
+ $(srcdir)/mysql_unicode_ci_table.h
+
+update-unicode-ci-except-kana-ci-kana-with-voiced-sound-mark-table: ensure-mysql-source-dir
+ $(RUBY) \
+ $(top_srcdir)/tool/generate_uca_table.rb \
+ --split-small-kana \
+ --split-kana-with-voiced-sound-mark \
+ --split-kana-with-semi-voiced-sound-mark \
+ --suffix _except_kana_ci_kana_with_voiced_sound_mark \
+ $(MYSQL_SOURCE_DIR)/strings/ctype-uca.c > \
+ $(srcdir)/mysql_unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_table.h
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql.c b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql.c
new file mode 100644
index 00000000000..94acfb16389
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql.c
@@ -0,0 +1,666 @@
+/* -*- c-basic-offset: 2 -*- */
+/*
+ Copyright(C) 2013-2014 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; version 2
+ of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301, USA
+*/
+
+#include <groonga/normalizer.h>
+#include <groonga/nfkc.h>
+
+#include <string.h>
+#include <stdio.h>
+
+#include "mysql_general_ci_table.h"
+#include "mysql_unicode_ci_table.h"
+#include "mysql_unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_table.h"
+
+#ifdef __GNUC__
+# define GNUC_UNUSED __attribute__((__unused__))
+#else
+# define GNUC_UNUSED
+#endif
+
+#ifdef _MSC_VER
+# define inline _inline
+# define snprintf _snprintf
+#endif
+
+#define SNIPPET_BUFFER_SIZE 256
+
+typedef grn_bool (*normalizer_func)(grn_ctx *ctx,
+ const char *utf8,
+ int *character_length,
+ int rest_length,
+ uint32_t **normalize_table,
+ char *normalized,
+ unsigned int *normalized_characer_length,
+ unsigned int *normalized_length_in_bytes,
+ unsigned int *normalized_n_characters);
+
+static inline unsigned int
+unichar_to_utf8(uint32_t unichar, char *output)
+{
+ unsigned int n_bytes;
+
+ if (unichar < 0x80) {
+ output[0] = unichar;
+ n_bytes = 1;
+ } else if (unichar < 0x0800) {
+ output[0] = ((unichar >> 6) & 0x1f) | 0xc0;
+ output[1] = (unichar & 0x3f) | 0x80;
+ n_bytes = 2;
+ } else if (unichar < 0x10000) {
+ output[0] = (unichar >> 12) | 0xe0;
+ output[1] = ((unichar >> 6) & 0x3f) | 0x80;
+ output[2] = (unichar & 0x3f) | 0x80;
+ n_bytes = 3;
+ } else if (unichar < 0x200000) {
+ output[0] = (unichar >> 18) | 0xf0;
+ output[1] = ((unichar >> 12) & 0x3f) | 0x80;
+ output[2] = ((unichar >> 6) & 0x3f) | 0x80;
+ output[3] = (unichar & 0x3f) | 0x80;
+ n_bytes = 4;
+ } else if (unichar < 0x4000000) {
+ output[0] = (unichar >> 24) | 0xf8;
+ output[1] = ((unichar >> 18) & 0x3f) | 0x80;
+ output[2] = ((unichar >> 12) & 0x3f) | 0x80;
+ output[3] = ((unichar >> 6) & 0x3f) | 0x80;
+ output[4] = (unichar & 0x3f) | 0x80;
+ n_bytes = 5;
+ } else {
+ output[0] = (unichar >> 30) | 0xfc;
+ output[1] = ((unichar >> 24) & 0x3f) | 0x80;
+ output[2] = ((unichar >> 18) & 0x3f) | 0x80;
+ output[3] = ((unichar >> 12) & 0x3f) | 0x80;
+ output[4] = ((unichar >> 6) & 0x3f) | 0x80;
+ output[5] = (unichar & 0x3f) | 0x80;
+ n_bytes = 6;
+ }
+
+ return n_bytes;
+}
+
+static inline uint32_t
+utf8_to_unichar(const char *utf8, int byte_size)
+{
+ uint32_t unichar;
+ const unsigned char *bytes = (const unsigned char *)utf8;
+
+ switch (byte_size) {
+ case 1 :
+ unichar = bytes[0] & 0x7f;
+ break;
+ case 2 :
+ unichar = ((bytes[0] & 0x1f) << 6) + (bytes[1] & 0x3f);
+ break;
+ case 3 :
+ unichar =
+ ((bytes[0] & 0x0f) << 12) +
+ ((bytes[1] & 0x3f) << 6) +
+ ((bytes[2] & 0x3f));
+ break;
+ case 4 :
+ unichar =
+ ((bytes[0] & 0x07) << 18) +
+ ((bytes[1] & 0x3f) << 12) +
+ ((bytes[2] & 0x3f) << 6) +
+ ((bytes[3] & 0x3f));
+ break;
+ case 5 :
+ unichar =
+ ((bytes[0] & 0x03) << 24) +
+ ((bytes[1] & 0x3f) << 18) +
+ ((bytes[2] & 0x3f) << 12) +
+ ((bytes[3] & 0x3f) << 6) +
+ ((bytes[4] & 0x3f));
+ break;
+ case 6 :
+ unichar =
+ ((bytes[0] & 0x01) << 30) +
+ ((bytes[1] & 0x3f) << 24) +
+ ((bytes[2] & 0x3f) << 18) +
+ ((bytes[3] & 0x3f) << 12) +
+ ((bytes[4] & 0x3f) << 6) +
+ ((bytes[5] & 0x3f));
+ break;
+ default :
+ unichar = 0;
+ break;
+ }
+
+ return unichar;
+}
+
+static inline void
+decompose_character(const char *rest, int character_length,
+ int *page, uint32_t *low_code)
+{
+ switch (character_length) {
+ case 1 :
+ *page = 0x00;
+ *low_code = rest[0] & 0x7f;
+ break;
+ case 2 :
+ *page = (rest[0] & 0x1c) >> 2;
+ *low_code = ((rest[0] & 0x03) << 6) + (rest[1] & 0x3f);
+ break;
+ case 3 :
+ *page = ((rest[0] & 0x0f) << 4) + ((rest[1] & 0x3c) >> 2);
+ *low_code = ((rest[1] & 0x03) << 6) + (rest[2] & 0x3f);
+ break;
+ case 4 :
+ *page =
+ ((rest[0] & 0x07) << 10) +
+ ((rest[1] & 0x3f) << 4) +
+ ((rest[2] & 0x3c) >> 2);
+ *low_code = ((rest[1] & 0x03) << 6) + (rest[2] & 0x3f);
+ break;
+ default :
+ *page = -1;
+ *low_code = 0x00;
+ break;
+ }
+}
+
+static inline void
+normalize_character(const char *utf8, int character_length,
+ uint32_t **normalize_table,
+ char *normalized,
+ unsigned int *normalized_character_length,
+ unsigned int *normalized_length_in_bytes,
+ unsigned int *normalized_n_characters)
+{
+ int page;
+ uint32_t low_code;
+ decompose_character(utf8, character_length, &page, &low_code);
+ if ((0x00 <= page && page <= 0xff) && normalize_table[page]) {
+ uint32_t normalized_code;
+ unsigned int n_bytes;
+ normalized_code = normalize_table[page][low_code];
+ if (normalized_code == 0x00000) {
+ *normalized_character_length = 0;
+ } else {
+ n_bytes = unichar_to_utf8(normalized_code,
+ normalized + *normalized_length_in_bytes);
+ *normalized_character_length = n_bytes;
+ *normalized_length_in_bytes += n_bytes;
+ (*normalized_n_characters)++;
+ }
+ } else {
+ int i;
+ for (i = 0; i < character_length; i++) {
+ normalized[*normalized_length_in_bytes + i] = utf8[i];
+ }
+ *normalized_character_length = character_length;
+ *normalized_length_in_bytes += character_length;
+ (*normalized_n_characters)++;
+ }
+}
+
+static void
+sized_buffer_append(char *buffer,
+ unsigned int buffer_length,
+ unsigned int *buffer_rest_length,
+ const char *string)
+{
+ size_t string_length;
+
+ string_length = strlen(string);
+ if (string_length >= *buffer_rest_length) {
+ return;
+ }
+
+ strncat(buffer, string, buffer_length);
+ *buffer_rest_length -= string_length;
+}
+
+static void
+sized_buffer_dump_string(char *buffer,
+ unsigned int buffer_length,
+ unsigned int *buffer_rest_length,
+ const char *string, unsigned int string_length)
+{
+ const unsigned char *bytes;
+ unsigned int i;
+
+ bytes = (const unsigned char *)string;
+ for (i = 0; i < string_length; i++) {
+ unsigned char byte = bytes[i];
+#define FORMATTED_BYTE_BUFFER_SIZE 5 /* "0xFF\0" */
+ char formatted_byte[FORMATTED_BYTE_BUFFER_SIZE];
+ if (i > 0) {
+ sized_buffer_append(buffer, buffer_length, buffer_rest_length,
+ " ");
+ }
+ if (byte == 0) {
+ strncpy(formatted_byte, "0x00", FORMATTED_BYTE_BUFFER_SIZE);
+ } else {
+ snprintf(formatted_byte, FORMATTED_BYTE_BUFFER_SIZE, "%#04x", byte);
+ }
+ sized_buffer_append(buffer, buffer_length, buffer_rest_length,
+ formatted_byte);
+#undef FORMATTED_BYTE_BUFFER_SIZE
+ }
+}
+
+static const char *
+snippet(const char *string, unsigned int length, unsigned int target_byte,
+ char *buffer, unsigned int buffer_length)
+{
+ const char *elision_mark = "...";
+ unsigned int max_window_length = 12;
+ unsigned int window_length;
+ unsigned int buffer_rest_length = buffer_length - 1;
+
+ buffer[0] = '\0';
+
+ if (target_byte > 0) {
+ sized_buffer_append(buffer, buffer_length, &buffer_rest_length,
+ elision_mark);
+ }
+
+ sized_buffer_append(buffer, buffer_length, &buffer_rest_length, "<");
+ if (target_byte + max_window_length > length) {
+ window_length = length - target_byte;
+ } else {
+ window_length = max_window_length;
+ }
+ sized_buffer_dump_string(buffer, buffer_length, &buffer_rest_length,
+ string + target_byte, window_length);
+ sized_buffer_append(buffer, buffer_length, &buffer_rest_length,
+ ">");
+
+ if (target_byte + window_length < length) {
+ sized_buffer_append(buffer, buffer_length, &buffer_rest_length,
+ elision_mark);
+ }
+
+ return buffer;
+}
+
+static void
+normalize(grn_ctx *ctx, grn_obj *string,
+ const char *normalizer_type_label,
+ uint32_t **normalize_table,
+ normalizer_func custom_normalizer)
+{
+ const char *original, *rest;
+ unsigned int original_length_in_bytes, rest_length;
+ char *normalized;
+ unsigned int normalized_length_in_bytes = 0;
+ unsigned int normalized_n_characters = 0;
+ unsigned char *types = NULL;
+ unsigned char *current_type = NULL;
+ short *checks = NULL;
+ short *current_check = NULL;
+ grn_encoding encoding;
+ int flags;
+ grn_bool remove_blank_p;
+
+ encoding = grn_string_get_encoding(ctx, string);
+ flags = grn_string_get_flags(ctx, string);
+ remove_blank_p = flags & GRN_STRING_REMOVE_BLANK;
+ grn_string_get_original(ctx, string, &original, &original_length_in_bytes);
+ {
+ unsigned int max_normalized_length_in_bytes =
+ original_length_in_bytes + 1;
+ normalized = GRN_PLUGIN_MALLOC(ctx, max_normalized_length_in_bytes);
+ }
+ if (flags & GRN_STRING_WITH_TYPES) {
+ unsigned int max_normalized_n_characters = original_length_in_bytes + 1;
+ types = GRN_PLUGIN_MALLOC(ctx, max_normalized_n_characters);
+ current_type = types;
+ }
+ if (flags & GRN_STRING_WITH_CHECKS) {
+ unsigned int max_checks_size = sizeof(short) * original_length_in_bytes + 1;
+ checks = GRN_PLUGIN_MALLOC(ctx, max_checks_size);
+ current_check = checks;
+ current_check[0] = 0;
+ }
+ rest = original;
+ rest_length = original_length_in_bytes;
+ while (rest_length > 0) {
+ int character_length;
+
+ character_length = grn_plugin_charlen(ctx, rest, rest_length, encoding);
+ if (character_length == 0) {
+ break;
+ }
+
+ if (remove_blank_p && character_length == 1 && rest[0] == ' ') {
+ if (current_type > types) {
+ current_type[-1] |= GRN_CHAR_BLANK;
+ }
+ if (current_check) {
+ current_check[0]++;
+ }
+ } else {
+ grn_bool custom_normalized = GRN_FALSE;
+ unsigned int normalized_character_length;
+ if (custom_normalizer) {
+ custom_normalized = custom_normalizer(ctx,
+ rest,
+ &character_length,
+ rest_length - character_length,
+ normalize_table,
+ normalized,
+ &normalized_character_length,
+ &normalized_length_in_bytes,
+ &normalized_n_characters);
+ }
+ if (!custom_normalized) {
+ normalize_character(rest, character_length, normalize_table,
+ normalized,
+ &normalized_character_length,
+ &normalized_length_in_bytes,
+ &normalized_n_characters);
+ }
+ if (current_type && normalized_character_length > 0) {
+ char *current_normalized;
+ current_normalized =
+ normalized + normalized_length_in_bytes - normalized_character_length;
+ current_type[0] =
+ grn_nfkc_char_type((unsigned char *)current_normalized);
+ current_type++;
+ }
+ if (current_check) {
+ current_check[0] += character_length;
+ if (normalized_character_length > 0) {
+ unsigned int i;
+ current_check++;
+ for (i = 1; i < normalized_character_length; i++) {
+ current_check[0] = 0;
+ current_check++;
+ }
+ current_check[0] = 0;
+ }
+ }
+ }
+
+ rest += character_length;
+ rest_length -= character_length;
+ }
+ if (current_type) {
+ current_type[0] = GRN_CHAR_NULL;
+ }
+ normalized[normalized_length_in_bytes] = '\0';
+
+ if (rest_length > 0) {
+ char buffer[SNIPPET_BUFFER_SIZE];
+ GRN_PLUGIN_LOG(ctx, GRN_LOG_DEBUG,
+ "[normalizer][%s] failed to normalize at %u byte: %s",
+ normalizer_type_label,
+ original_length_in_bytes - rest_length,
+ snippet(original,
+ original_length_in_bytes,
+ original_length_in_bytes - rest_length,
+ buffer,
+ SNIPPET_BUFFER_SIZE));
+ }
+ grn_string_set_normalized(ctx,
+ string,
+ normalized,
+ normalized_length_in_bytes,
+ normalized_n_characters);
+ grn_string_set_types(ctx, string, types);
+ grn_string_set_checks(ctx, string, checks);
+}
+
+static grn_obj *
+mysql_general_ci_next(GNUC_UNUSED grn_ctx *ctx,
+ GNUC_UNUSED int nargs,
+ grn_obj **args,
+ GNUC_UNUSED grn_user_data *user_data)
+{
+ grn_obj *string = args[0];
+ grn_encoding encoding;
+ const char *normalizer_type_label = "mysql-general-ci";
+
+ encoding = grn_string_get_encoding(ctx, string);
+ if (encoding != GRN_ENC_UTF8) {
+ GRN_PLUGIN_ERROR(ctx,
+ GRN_FUNCTION_NOT_IMPLEMENTED,
+ "[normalizer][%s] "
+ "UTF-8 encoding is only supported: %s",
+ normalizer_type_label,
+ grn_encoding_to_string(encoding));
+ return NULL;
+ }
+ normalize(ctx, string, normalizer_type_label, general_ci_table, NULL);
+ return NULL;
+}
+
+static grn_obj *
+mysql_unicode_ci_next(GNUC_UNUSED grn_ctx *ctx,
+ GNUC_UNUSED int nargs,
+ grn_obj **args,
+ GNUC_UNUSED grn_user_data *user_data)
+{
+ grn_obj *string = args[0];
+ grn_encoding encoding;
+ const char *normalizer_type_label = "mysql-unicode-ci";
+
+ encoding = grn_string_get_encoding(ctx, string);
+ if (encoding != GRN_ENC_UTF8) {
+ GRN_PLUGIN_ERROR(ctx,
+ GRN_FUNCTION_NOT_IMPLEMENTED,
+ "[normalizer][%s] "
+ "UTF-8 encoding is only supported: %s",
+ normalizer_type_label,
+ grn_encoding_to_string(encoding));
+ return NULL;
+ }
+ normalize(ctx, string, normalizer_type_label, unicode_ci_table, NULL);
+ return NULL;
+}
+
+#define HALFWIDTH_KATAKANA_LETTER_KA 0xff76
+#define HALFWIDTH_KATAKANA_LETTER_KI 0xff77
+#define HALFWIDTH_KATAKANA_LETTER_KU 0xff78
+#define HALFWIDTH_KATAKANA_LETTER_KE 0xff79
+#define HALFWIDTH_KATAKANA_LETTER_KO 0xff7a
+
+#define HALFWIDTH_KATAKANA_LETTER_SA 0xff7b
+#define HALFWIDTH_KATAKANA_LETTER_SI 0xff7c
+#define HALFWIDTH_KATAKANA_LETTER_SU 0xff7d
+#define HALFWIDTH_KATAKANA_LETTER_SE 0xff7e
+#define HALFWIDTH_KATAKANA_LETTER_SO 0xff7f
+
+#define HALFWIDTH_KATAKANA_LETTER_TA 0xff80
+#define HALFWIDTH_KATAKANA_LETTER_TI 0xff81
+#define HALFWIDTH_KATAKANA_LETTER_TU 0xff82
+#define HALFWIDTH_KATAKANA_LETTER_TE 0xff83
+#define HALFWIDTH_KATAKANA_LETTER_TO 0xff84
+
+#define HALFWIDTH_KATAKANA_LETTER_HA 0xff8a
+#define HALFWIDTH_KATAKANA_LETTER_HI 0xff8b
+#define HALFWIDTH_KATAKANA_LETTER_HU 0xff8c
+#define HALFWIDTH_KATAKANA_LETTER_HE 0xff8d
+#define HALFWIDTH_KATAKANA_LETTER_HO 0xff8e
+
+#define HALFWIDTH_KATAKANA_VOICED_SOUND_MARK 0xff9e
+#define HALFWIDTH_KATAKANA_SEMI_VOICED_SOUND_MARK 0xff9f
+
+#define HIRAGANA_LETTER_KA 0x304b
+#define HIRAGANA_VOICED_SOUND_MARK_OFFSET 1
+#define HIRAGANA_VOICED_SOUND_MARK_GAP 2
+
+#define HIRAGANA_LETTER_HA 0x306f
+#define HIRAGANA_HA_LINE_BA_OFFSET 1
+#define HIRAGANA_HA_LINE_PA_OFFSET 2
+#define HIRAGANA_HA_LINE_GAP 3
+
+static grn_bool
+normalize_halfwidth_katakana_with_voiced_sound_mark(
+ grn_ctx *ctx,
+ const char *utf8,
+ int *character_length,
+ int rest_length,
+ GNUC_UNUSED uint32_t **normalize_table,
+ char *normalized,
+ unsigned int *normalized_character_length,
+ unsigned int *normalized_length_in_bytes,
+ unsigned int *normalized_n_characters)
+{
+ grn_bool custom_normalized = GRN_FALSE;
+ grn_bool is_voiced_sound_markable_halfwidth_katakana = GRN_FALSE;
+ grn_bool is_semi_voiced_sound_markable_halfwidth_katakana = GRN_FALSE;
+ grn_bool is_ha_line = GRN_FALSE;
+ uint32_t unichar;
+
+ if (*character_length != 3) {
+ return GRN_FALSE;
+ }
+ if (rest_length < 3) {
+ return GRN_FALSE;
+ }
+
+ unichar = utf8_to_unichar(utf8, *character_length);
+ if (HALFWIDTH_KATAKANA_LETTER_KA <= unichar &&
+ unichar <= HALFWIDTH_KATAKANA_LETTER_TO) {
+ is_voiced_sound_markable_halfwidth_katakana = GRN_TRUE;
+ } else if (HALFWIDTH_KATAKANA_LETTER_HA <= unichar &&
+ unichar <= HALFWIDTH_KATAKANA_LETTER_HO) {
+ is_voiced_sound_markable_halfwidth_katakana = GRN_TRUE;
+ is_semi_voiced_sound_markable_halfwidth_katakana = GRN_TRUE;
+ is_ha_line = GRN_TRUE;
+ }
+
+ if (!is_voiced_sound_markable_halfwidth_katakana &&
+ !is_semi_voiced_sound_markable_halfwidth_katakana) {
+ return GRN_FALSE;
+ }
+
+ {
+ int next_character_length;
+ uint32_t next_unichar;
+ next_character_length = grn_plugin_charlen(ctx,
+ utf8 + *character_length,
+ rest_length,
+ GRN_ENC_UTF8);
+ if (next_character_length != 3) {
+ return GRN_FALSE;
+ }
+ next_unichar = utf8_to_unichar(utf8 + *character_length,
+ next_character_length);
+ if (next_unichar == HALFWIDTH_KATAKANA_VOICED_SOUND_MARK) {
+ if (is_voiced_sound_markable_halfwidth_katakana) {
+ unsigned int n_bytes;
+ if (is_ha_line) {
+ n_bytes = unichar_to_utf8(HIRAGANA_LETTER_HA +
+ HIRAGANA_HA_LINE_BA_OFFSET +
+ ((unichar - HALFWIDTH_KATAKANA_LETTER_HA) *
+ HIRAGANA_HA_LINE_GAP),
+ normalized + *normalized_length_in_bytes);
+ } else {
+ int small_tu_offset = 0;
+ if (HALFWIDTH_KATAKANA_LETTER_TU <= unichar &&
+ unichar <= HALFWIDTH_KATAKANA_LETTER_TO) {
+ small_tu_offset = 1;
+ }
+ n_bytes = unichar_to_utf8(HIRAGANA_LETTER_KA +
+ HIRAGANA_VOICED_SOUND_MARK_OFFSET +
+ small_tu_offset +
+ ((unichar - HALFWIDTH_KATAKANA_LETTER_KA) *
+ HIRAGANA_VOICED_SOUND_MARK_GAP),
+ normalized + *normalized_length_in_bytes);
+ }
+ *character_length += next_character_length;
+ *normalized_character_length = n_bytes;
+ *normalized_length_in_bytes += n_bytes;
+ (*normalized_n_characters)++;
+ custom_normalized = GRN_TRUE;
+ }
+ } else if (next_unichar == HALFWIDTH_KATAKANA_SEMI_VOICED_SOUND_MARK) {
+ if (is_semi_voiced_sound_markable_halfwidth_katakana) {
+ unsigned int n_bytes;
+ n_bytes = unichar_to_utf8(HIRAGANA_LETTER_HA +
+ HIRAGANA_HA_LINE_PA_OFFSET +
+ ((unichar - HALFWIDTH_KATAKANA_LETTER_HA) *
+ HIRAGANA_HA_LINE_GAP),
+ normalized + *normalized_length_in_bytes);
+ *character_length += next_character_length;
+ *normalized_character_length = n_bytes;
+ *normalized_length_in_bytes += n_bytes;
+ (*normalized_n_characters)++;
+ custom_normalized = GRN_TRUE;
+ }
+ }
+ }
+
+ return custom_normalized;
+}
+
+static grn_obj *
+mysql_unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_next(
+ GNUC_UNUSED grn_ctx *ctx,
+ GNUC_UNUSED int nargs,
+ grn_obj **args,
+ GNUC_UNUSED grn_user_data *user_data)
+{
+ grn_obj *string = args[0];
+ grn_encoding encoding;
+ const char *normalizer_type_label =
+ "mysql-unicode-ci-except-kana-ci-kana-with-voiced-sound-mark";
+
+ encoding = grn_string_get_encoding(ctx, string);
+ if (encoding != GRN_ENC_UTF8) {
+ GRN_PLUGIN_ERROR(ctx,
+ GRN_FUNCTION_NOT_IMPLEMENTED,
+ "[normalizer][%s] "
+ "UTF-8 encoding is only supported: %s",
+ normalizer_type_label,
+ grn_encoding_to_string(encoding));
+ return NULL;
+ }
+ normalize(ctx, string,
+ normalizer_type_label,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_table,
+ normalize_halfwidth_katakana_with_voiced_sound_mark);
+ return NULL;
+}
+
+grn_rc
+GRN_PLUGIN_INIT(grn_ctx *ctx)
+{
+ return ctx->rc;
+}
+
+grn_rc
+GRN_PLUGIN_REGISTER(grn_ctx *ctx)
+{
+ grn_normalizer_register(ctx, "NormalizerMySQLGeneralCI", -1,
+ NULL, mysql_general_ci_next, NULL);
+ grn_normalizer_register(ctx, "NormalizerMySQLUnicodeCI", -1,
+ NULL, mysql_unicode_ci_next, NULL);
+ grn_normalizer_register(ctx,
+ "NormalizerMySQLUnicodeCI"
+ "Except"
+ "KanaCI"
+ "KanaWithVoicedSoundMark",
+ -1,
+ NULL,
+ mysql_unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_next,
+ NULL);
+ return GRN_SUCCESS;
+}
+
+grn_rc
+GRN_PLUGIN_FIN(GNUC_UNUSED grn_ctx *ctx)
+{
+ return GRN_SUCCESS;
+}
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_general_ci_table.h b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_general_ci_table.h
new file mode 100644
index 00000000000..15ed558e2dc
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_general_ci_table.h
@@ -0,0 +1,565 @@
+/*
+ Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; version 2
+ of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301, USA
+
+ This file uses normalization table defined in
+ mysql-5.5.29/strings/ctype-utf8.c.
+ The following is the header of the file:
+
+ Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; version 2
+ of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301, USA
+
+ UTF8 according RFC 2279
+ Written by Alexander Barkov <bar@udm.net>
+*/
+
+#ifndef MYSQL_UTF8_H
+#define MYSQL_UTF8_H
+
+#include <stdint.h>
+
+static uint32_t general_ci_page_00[] = {
+ 0x00000, 0x00001, 0x00002, 0x00003, 0x00004, 0x00005, 0x00006, 0x00007,
+ 0x00008, 0x00009, 0x0000a, 0x0000b, 0x0000c, 0x0000d, 0x0000e, 0x0000f,
+ 0x00010, 0x00011, 0x00012, 0x00013, 0x00014, 0x00015, 0x00016, 0x00017,
+ 0x00018, 0x00019, 0x0001a, 0x0001b, 0x0001c, 0x0001d, 0x0001e, 0x0001f,
+ 0x00020, 0x00021, 0x00022, 0x00023, 0x00024, 0x00025, 0x00026, 0x00027,
+ 0x00028, 0x00029, 0x0002a, 0x0002b, 0x0002c, 0x0002d, 0x0002e, 0x0002f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0003a, 0x0003b, 0x0003c, 0x0003d, 0x0003e, 0x0003f,
+ 0x00040, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047,
+ 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f,
+ 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057,
+ 0x00058, 0x00059, 0x0005a, 0x0005b, 0x0005c, 0x0005d, 0x0005e, 0x0005f,
+ 0x00060, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047,
+ 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f,
+ 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057,
+ 0x00058, 0x00059, 0x0005a, 0x0007b, 0x0007c, 0x0007d, 0x0007e, 0x0007f,
+ 0x00080, 0x00081, 0x00082, 0x00083, 0x00084, 0x00085, 0x00086, 0x00087,
+ 0x00088, 0x00089, 0x0008a, 0x0008b, 0x0008c, 0x0008d, 0x0008e, 0x0008f,
+ 0x00090, 0x00091, 0x00092, 0x00093, 0x00094, 0x00095, 0x00096, 0x00097,
+ 0x00098, 0x00099, 0x0009a, 0x0009b, 0x0009c, 0x0009d, 0x0009e, 0x0009f,
+ 0x000a0, 0x000a1, 0x000a2, 0x000a3, 0x000a4, 0x000a5, 0x000a6, 0x000a7,
+ 0x000a8, 0x000a9, 0x000aa, 0x000ab, 0x000ac, 0x000ad, 0x000ae, 0x000af,
+ 0x000b0, 0x000b1, 0x000b2, 0x000b3, 0x000b4, 0x0039c, 0x000b6, 0x000b7,
+ 0x000b8, 0x000b9, 0x000ba, 0x000bb, 0x000bc, 0x000bd, 0x000be, 0x000bf,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x000c6, 0x00043,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00049, 0x00049, 0x00049, 0x00049,
+ 0x000d0, 0x0004e, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x000d7,
+ 0x000d8, 0x00055, 0x00055, 0x00055, 0x00055, 0x00059, 0x000de, 0x00053,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x000c6, 0x00043,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00049, 0x00049, 0x00049, 0x00049,
+ 0x000d0, 0x0004e, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x000f7,
+ 0x000d8, 0x00055, 0x00055, 0x00055, 0x00055, 0x00059, 0x000de, 0x00059
+};
+
+static uint32_t general_ci_page_01[] = {
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00043, 0x00043,
+ 0x00043, 0x00043, 0x00043, 0x00043, 0x00043, 0x00043, 0x00044, 0x00044,
+ 0x00110, 0x00110, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00047, 0x00047, 0x00047, 0x00047,
+ 0x00047, 0x00047, 0x00047, 0x00047, 0x00048, 0x00048, 0x00126, 0x00126,
+ 0x00049, 0x00049, 0x00049, 0x00049, 0x00049, 0x00049, 0x00049, 0x00049,
+ 0x00049, 0x00049, 0x00132, 0x00132, 0x0004a, 0x0004a, 0x0004b, 0x0004b,
+ 0x00138, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0013f,
+ 0x0013f, 0x00141, 0x00141, 0x0004e, 0x0004e, 0x0004e, 0x0004e, 0x0004e,
+ 0x0004e, 0x00149, 0x0014a, 0x0014a, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x00152, 0x00152, 0x00052, 0x00052, 0x00052, 0x00052,
+ 0x00052, 0x00052, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053,
+ 0x00053, 0x00053, 0x00054, 0x00054, 0x00054, 0x00054, 0x00166, 0x00166,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00057, 0x00057, 0x00059, 0x00059,
+ 0x00059, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x00053,
+ 0x00180, 0x00181, 0x00182, 0x00182, 0x00184, 0x00184, 0x00186, 0x00187,
+ 0x00187, 0x00189, 0x0018a, 0x0018b, 0x0018b, 0x0018d, 0x0018e, 0x0018f,
+ 0x00190, 0x00191, 0x00191, 0x00193, 0x00194, 0x001f6, 0x00196, 0x00197,
+ 0x00198, 0x00198, 0x0019a, 0x0019b, 0x0019c, 0x0019d, 0x0019e, 0x0019f,
+ 0x0004f, 0x0004f, 0x001a2, 0x001a2, 0x001a4, 0x001a4, 0x001a6, 0x001a7,
+ 0x001a7, 0x001a9, 0x001aa, 0x001ab, 0x001ac, 0x001ac, 0x001ae, 0x00055,
+ 0x00055, 0x001b1, 0x001b2, 0x001b3, 0x001b3, 0x001b5, 0x001b5, 0x001b7,
+ 0x001b8, 0x001b8, 0x001ba, 0x001bb, 0x001bc, 0x001bc, 0x001be, 0x001f7,
+ 0x001c0, 0x001c1, 0x001c2, 0x001c3, 0x001c4, 0x001c4, 0x001c4, 0x001c7,
+ 0x001c7, 0x001c7, 0x001ca, 0x001ca, 0x001ca, 0x00041, 0x00041, 0x00049,
+ 0x00049, 0x0004f, 0x0004f, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x0018e, 0x00041, 0x00041,
+ 0x00041, 0x00041, 0x000c6, 0x000c6, 0x001e4, 0x001e4, 0x00047, 0x00047,
+ 0x0004b, 0x0004b, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x001b7, 0x001b7,
+ 0x0004a, 0x001f1, 0x001f1, 0x001f1, 0x00047, 0x00047, 0x001f6, 0x001f7,
+ 0x0004e, 0x0004e, 0x00041, 0x00041, 0x000c6, 0x000c6, 0x000d8, 0x000d8
+};
+
+static uint32_t general_ci_page_02[] = {
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00049, 0x00049, 0x00049, 0x00049, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x00052, 0x00052, 0x00052, 0x00052, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00053, 0x00053, 0x00054, 0x00054, 0x0021c, 0x0021c, 0x00048, 0x00048,
+ 0x00220, 0x00221, 0x00222, 0x00222, 0x00224, 0x00224, 0x00041, 0x00041,
+ 0x00045, 0x00045, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x00059, 0x00059, 0x00234, 0x00235, 0x00236, 0x00237,
+ 0x00238, 0x00239, 0x0023a, 0x0023b, 0x0023c, 0x0023d, 0x0023e, 0x0023f,
+ 0x00240, 0x00241, 0x00242, 0x00243, 0x00244, 0x00245, 0x00246, 0x00247,
+ 0x00248, 0x00249, 0x0024a, 0x0024b, 0x0024c, 0x0024d, 0x0024e, 0x0024f,
+ 0x00250, 0x00251, 0x00252, 0x00181, 0x00186, 0x00255, 0x00189, 0x0018a,
+ 0x00258, 0x0018f, 0x0025a, 0x00190, 0x0025c, 0x0025d, 0x0025e, 0x0025f,
+ 0x00193, 0x00261, 0x00262, 0x00194, 0x00264, 0x00265, 0x00266, 0x00267,
+ 0x00197, 0x00196, 0x0026a, 0x0026b, 0x0026c, 0x0026d, 0x0026e, 0x0019c,
+ 0x00270, 0x00271, 0x0019d, 0x00273, 0x00274, 0x0019f, 0x00276, 0x00277,
+ 0x00278, 0x00279, 0x0027a, 0x0027b, 0x0027c, 0x0027d, 0x0027e, 0x0027f,
+ 0x001a6, 0x00281, 0x00282, 0x001a9, 0x00284, 0x00285, 0x00286, 0x00287,
+ 0x001ae, 0x00289, 0x001b1, 0x001b2, 0x0028c, 0x0028d, 0x0028e, 0x0028f,
+ 0x00290, 0x00291, 0x001b7, 0x00293, 0x00294, 0x00295, 0x00296, 0x00297,
+ 0x00298, 0x00299, 0x0029a, 0x0029b, 0x0029c, 0x0029d, 0x0029e, 0x0029f,
+ 0x002a0, 0x002a1, 0x002a2, 0x002a3, 0x002a4, 0x002a5, 0x002a6, 0x002a7,
+ 0x002a8, 0x002a9, 0x002aa, 0x002ab, 0x002ac, 0x002ad, 0x002ae, 0x002af,
+ 0x002b0, 0x002b1, 0x002b2, 0x002b3, 0x002b4, 0x002b5, 0x002b6, 0x002b7,
+ 0x002b8, 0x002b9, 0x002ba, 0x002bb, 0x002bc, 0x002bd, 0x002be, 0x002bf,
+ 0x002c0, 0x002c1, 0x002c2, 0x002c3, 0x002c4, 0x002c5, 0x002c6, 0x002c7,
+ 0x002c8, 0x002c9, 0x002ca, 0x002cb, 0x002cc, 0x002cd, 0x002ce, 0x002cf,
+ 0x002d0, 0x002d1, 0x002d2, 0x002d3, 0x002d4, 0x002d5, 0x002d6, 0x002d7,
+ 0x002d8, 0x002d9, 0x002da, 0x002db, 0x002dc, 0x002dd, 0x002de, 0x002df,
+ 0x002e0, 0x002e1, 0x002e2, 0x002e3, 0x002e4, 0x002e5, 0x002e6, 0x002e7,
+ 0x002e8, 0x002e9, 0x002ea, 0x002eb, 0x002ec, 0x002ed, 0x002ee, 0x002ef,
+ 0x002f0, 0x002f1, 0x002f2, 0x002f3, 0x002f4, 0x002f5, 0x002f6, 0x002f7,
+ 0x002f8, 0x002f9, 0x002fa, 0x002fb, 0x002fc, 0x002fd, 0x002fe, 0x002ff
+};
+
+static uint32_t general_ci_page_03[] = {
+ 0x00300, 0x00301, 0x00302, 0x00303, 0x00304, 0x00305, 0x00306, 0x00307,
+ 0x00308, 0x00309, 0x0030a, 0x0030b, 0x0030c, 0x0030d, 0x0030e, 0x0030f,
+ 0x00310, 0x00311, 0x00312, 0x00313, 0x00314, 0x00315, 0x00316, 0x00317,
+ 0x00318, 0x00319, 0x0031a, 0x0031b, 0x0031c, 0x0031d, 0x0031e, 0x0031f,
+ 0x00320, 0x00321, 0x00322, 0x00323, 0x00324, 0x00325, 0x00326, 0x00327,
+ 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d, 0x0032e, 0x0032f,
+ 0x00330, 0x00331, 0x00332, 0x00333, 0x00334, 0x00335, 0x00336, 0x00337,
+ 0x00338, 0x00339, 0x0033a, 0x0033b, 0x0033c, 0x0033d, 0x0033e, 0x0033f,
+ 0x00340, 0x00341, 0x00342, 0x00343, 0x00344, 0x00399, 0x00346, 0x00347,
+ 0x00348, 0x00349, 0x0034a, 0x0034b, 0x0034c, 0x0034d, 0x0034e, 0x0034f,
+ 0x00350, 0x00351, 0x00352, 0x00353, 0x00354, 0x00355, 0x00356, 0x00357,
+ 0x00358, 0x00359, 0x0035a, 0x0035b, 0x0035c, 0x0035d, 0x0035e, 0x0035f,
+ 0x00360, 0x00361, 0x00362, 0x00363, 0x00364, 0x00365, 0x00366, 0x00367,
+ 0x00368, 0x00369, 0x0036a, 0x0036b, 0x0036c, 0x0036d, 0x0036e, 0x0036f,
+ 0x00370, 0x00371, 0x00372, 0x00373, 0x00374, 0x00375, 0x00376, 0x00377,
+ 0x00378, 0x00379, 0x0037a, 0x0037b, 0x0037c, 0x0037d, 0x0037e, 0x0037f,
+ 0x00380, 0x00381, 0x00382, 0x00383, 0x00384, 0x00385, 0x00391, 0x00387,
+ 0x00395, 0x00397, 0x00399, 0x0038b, 0x0039f, 0x0038d, 0x003a5, 0x003a9,
+ 0x00399, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397,
+ 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f,
+ 0x003a0, 0x003a1, 0x003a2, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7,
+ 0x003a8, 0x003a9, 0x00399, 0x003a5, 0x00391, 0x00395, 0x00397, 0x00399,
+ 0x003a5, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397,
+ 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f,
+ 0x003a0, 0x003a1, 0x003a3, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7,
+ 0x003a8, 0x003a9, 0x00399, 0x003a5, 0x0039f, 0x003a5, 0x003a9, 0x003cf,
+ 0x00392, 0x00398, 0x003d2, 0x003d2, 0x003d2, 0x003a6, 0x003a0, 0x003d7,
+ 0x003d8, 0x003d9, 0x003da, 0x003da, 0x003dc, 0x003dc, 0x003de, 0x003de,
+ 0x003e0, 0x003e0, 0x003e2, 0x003e2, 0x003e4, 0x003e4, 0x003e6, 0x003e6,
+ 0x003e8, 0x003e8, 0x003ea, 0x003ea, 0x003ec, 0x003ec, 0x003ee, 0x003ee,
+ 0x0039a, 0x003a1, 0x003a3, 0x003f3, 0x003f4, 0x003f5, 0x003f6, 0x003f7,
+ 0x003f8, 0x003f9, 0x003fa, 0x003fb, 0x003fc, 0x003fd, 0x003fe, 0x003ff
+};
+
+static uint32_t general_ci_page_04[] = {
+ 0x00415, 0x00415, 0x00402, 0x00413, 0x00404, 0x00405, 0x00406, 0x00406,
+ 0x00408, 0x00409, 0x0040a, 0x0040b, 0x0041a, 0x00418, 0x00423, 0x0040f,
+ 0x00410, 0x00411, 0x00412, 0x00413, 0x00414, 0x00415, 0x00416, 0x00417,
+ 0x00418, 0x00419, 0x0041a, 0x0041b, 0x0041c, 0x0041d, 0x0041e, 0x0041f,
+ 0x00420, 0x00421, 0x00422, 0x00423, 0x00424, 0x00425, 0x00426, 0x00427,
+ 0x00428, 0x00429, 0x0042a, 0x0042b, 0x0042c, 0x0042d, 0x0042e, 0x0042f,
+ 0x00410, 0x00411, 0x00412, 0x00413, 0x00414, 0x00415, 0x00416, 0x00417,
+ 0x00418, 0x00419, 0x0041a, 0x0041b, 0x0041c, 0x0041d, 0x0041e, 0x0041f,
+ 0x00420, 0x00421, 0x00422, 0x00423, 0x00424, 0x00425, 0x00426, 0x00427,
+ 0x00428, 0x00429, 0x0042a, 0x0042b, 0x0042c, 0x0042d, 0x0042e, 0x0042f,
+ 0x00415, 0x00415, 0x00402, 0x00413, 0x00404, 0x00405, 0x00406, 0x00406,
+ 0x00408, 0x00409, 0x0040a, 0x0040b, 0x0041a, 0x00418, 0x00423, 0x0040f,
+ 0x00460, 0x00460, 0x00462, 0x00462, 0x00464, 0x00464, 0x00466, 0x00466,
+ 0x00468, 0x00468, 0x0046a, 0x0046a, 0x0046c, 0x0046c, 0x0046e, 0x0046e,
+ 0x00470, 0x00470, 0x00472, 0x00472, 0x00474, 0x00474, 0x00474, 0x00474,
+ 0x00478, 0x00478, 0x0047a, 0x0047a, 0x0047c, 0x0047c, 0x0047e, 0x0047e,
+ 0x00480, 0x00480, 0x00482, 0x00483, 0x00484, 0x00485, 0x00486, 0x00487,
+ 0x00488, 0x00489, 0x0048a, 0x0048b, 0x0048c, 0x0048c, 0x0048e, 0x0048e,
+ 0x00490, 0x00490, 0x00492, 0x00492, 0x00494, 0x00494, 0x00496, 0x00496,
+ 0x00498, 0x00498, 0x0049a, 0x0049a, 0x0049c, 0x0049c, 0x0049e, 0x0049e,
+ 0x004a0, 0x004a0, 0x004a2, 0x004a2, 0x004a4, 0x004a4, 0x004a6, 0x004a6,
+ 0x004a8, 0x004a8, 0x004aa, 0x004aa, 0x004ac, 0x004ac, 0x004ae, 0x004ae,
+ 0x004b0, 0x004b0, 0x004b2, 0x004b2, 0x004b4, 0x004b4, 0x004b6, 0x004b6,
+ 0x004b8, 0x004b8, 0x004ba, 0x004ba, 0x004bc, 0x004bc, 0x004be, 0x004be,
+ 0x004c0, 0x00416, 0x00416, 0x004c3, 0x004c3, 0x004c5, 0x004c6, 0x004c7,
+ 0x004c7, 0x004c9, 0x004ca, 0x004cb, 0x004cb, 0x004cd, 0x004ce, 0x004cf,
+ 0x00410, 0x00410, 0x00410, 0x00410, 0x004d4, 0x004d4, 0x00415, 0x00415,
+ 0x004d8, 0x004d8, 0x004d8, 0x004d8, 0x00416, 0x00416, 0x00417, 0x00417,
+ 0x004e0, 0x004e0, 0x00418, 0x00418, 0x00418, 0x00418, 0x0041e, 0x0041e,
+ 0x004e8, 0x004e8, 0x004e8, 0x004e8, 0x0042d, 0x0042d, 0x00423, 0x00423,
+ 0x00423, 0x00423, 0x00423, 0x00423, 0x00427, 0x00427, 0x004f6, 0x004f7,
+ 0x0042b, 0x0042b, 0x004fa, 0x004fb, 0x004fc, 0x004fd, 0x004fe, 0x004ff
+};
+
+static uint32_t general_ci_page_05[] = {
+ 0x00500, 0x00501, 0x00502, 0x00503, 0x00504, 0x00505, 0x00506, 0x00507,
+ 0x00508, 0x00509, 0x0050a, 0x0050b, 0x0050c, 0x0050d, 0x0050e, 0x0050f,
+ 0x00510, 0x00511, 0x00512, 0x00513, 0x00514, 0x00515, 0x00516, 0x00517,
+ 0x00518, 0x00519, 0x0051a, 0x0051b, 0x0051c, 0x0051d, 0x0051e, 0x0051f,
+ 0x00520, 0x00521, 0x00522, 0x00523, 0x00524, 0x00525, 0x00526, 0x00527,
+ 0x00528, 0x00529, 0x0052a, 0x0052b, 0x0052c, 0x0052d, 0x0052e, 0x0052f,
+ 0x00530, 0x00531, 0x00532, 0x00533, 0x00534, 0x00535, 0x00536, 0x00537,
+ 0x00538, 0x00539, 0x0053a, 0x0053b, 0x0053c, 0x0053d, 0x0053e, 0x0053f,
+ 0x00540, 0x00541, 0x00542, 0x00543, 0x00544, 0x00545, 0x00546, 0x00547,
+ 0x00548, 0x00549, 0x0054a, 0x0054b, 0x0054c, 0x0054d, 0x0054e, 0x0054f,
+ 0x00550, 0x00551, 0x00552, 0x00553, 0x00554, 0x00555, 0x00556, 0x00557,
+ 0x00558, 0x00559, 0x0055a, 0x0055b, 0x0055c, 0x0055d, 0x0055e, 0x0055f,
+ 0x00560, 0x00531, 0x00532, 0x00533, 0x00534, 0x00535, 0x00536, 0x00537,
+ 0x00538, 0x00539, 0x0053a, 0x0053b, 0x0053c, 0x0053d, 0x0053e, 0x0053f,
+ 0x00540, 0x00541, 0x00542, 0x00543, 0x00544, 0x00545, 0x00546, 0x00547,
+ 0x00548, 0x00549, 0x0054a, 0x0054b, 0x0054c, 0x0054d, 0x0054e, 0x0054f,
+ 0x00550, 0x00551, 0x00552, 0x00553, 0x00554, 0x00555, 0x00556, 0x00587,
+ 0x00588, 0x00589, 0x0058a, 0x0058b, 0x0058c, 0x0058d, 0x0058e, 0x0058f,
+ 0x00590, 0x00591, 0x00592, 0x00593, 0x00594, 0x00595, 0x00596, 0x00597,
+ 0x00598, 0x00599, 0x0059a, 0x0059b, 0x0059c, 0x0059d, 0x0059e, 0x0059f,
+ 0x005a0, 0x005a1, 0x005a2, 0x005a3, 0x005a4, 0x005a5, 0x005a6, 0x005a7,
+ 0x005a8, 0x005a9, 0x005aa, 0x005ab, 0x005ac, 0x005ad, 0x005ae, 0x005af,
+ 0x005b0, 0x005b1, 0x005b2, 0x005b3, 0x005b4, 0x005b5, 0x005b6, 0x005b7,
+ 0x005b8, 0x005b9, 0x005ba, 0x005bb, 0x005bc, 0x005bd, 0x005be, 0x005bf,
+ 0x005c0, 0x005c1, 0x005c2, 0x005c3, 0x005c4, 0x005c5, 0x005c6, 0x005c7,
+ 0x005c8, 0x005c9, 0x005ca, 0x005cb, 0x005cc, 0x005cd, 0x005ce, 0x005cf,
+ 0x005d0, 0x005d1, 0x005d2, 0x005d3, 0x005d4, 0x005d5, 0x005d6, 0x005d7,
+ 0x005d8, 0x005d9, 0x005da, 0x005db, 0x005dc, 0x005dd, 0x005de, 0x005df,
+ 0x005e0, 0x005e1, 0x005e2, 0x005e3, 0x005e4, 0x005e5, 0x005e6, 0x005e7,
+ 0x005e8, 0x005e9, 0x005ea, 0x005eb, 0x005ec, 0x005ed, 0x005ee, 0x005ef,
+ 0x005f0, 0x005f1, 0x005f2, 0x005f3, 0x005f4, 0x005f5, 0x005f6, 0x005f7,
+ 0x005f8, 0x005f9, 0x005fa, 0x005fb, 0x005fc, 0x005fd, 0x005fe, 0x005ff
+};
+
+static uint32_t general_ci_page_1e[] = {
+ 0x00041, 0x00041, 0x00042, 0x00042, 0x00042, 0x00042, 0x00042, 0x00042,
+ 0x00043, 0x00043, 0x00044, 0x00044, 0x00044, 0x00044, 0x00044, 0x00044,
+ 0x00044, 0x00044, 0x00044, 0x00044, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00046, 0x00046,
+ 0x00047, 0x00047, 0x00048, 0x00048, 0x00048, 0x00048, 0x00048, 0x00048,
+ 0x00048, 0x00048, 0x00048, 0x00048, 0x00049, 0x00049, 0x00049, 0x00049,
+ 0x0004b, 0x0004b, 0x0004b, 0x0004b, 0x0004b, 0x0004b, 0x0004c, 0x0004c,
+ 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004d, 0x0004d,
+ 0x0004d, 0x0004d, 0x0004d, 0x0004d, 0x0004e, 0x0004e, 0x0004e, 0x0004e,
+ 0x0004e, 0x0004e, 0x0004e, 0x0004e, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x00050, 0x00050, 0x00050, 0x00050,
+ 0x00052, 0x00052, 0x00052, 0x00052, 0x00052, 0x00052, 0x00052, 0x00052,
+ 0x00053, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053,
+ 0x00053, 0x00053, 0x00054, 0x00054, 0x00054, 0x00054, 0x00054, 0x00054,
+ 0x00054, 0x00054, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00056, 0x00056, 0x00056, 0x00056,
+ 0x00057, 0x00057, 0x00057, 0x00057, 0x00057, 0x00057, 0x00057, 0x00057,
+ 0x00057, 0x00057, 0x00058, 0x00058, 0x00058, 0x00058, 0x00059, 0x00059,
+ 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x00048, 0x00054,
+ 0x00057, 0x00059, 0x01e9a, 0x00053, 0x01e9c, 0x01e9d, 0x01e9e, 0x01e9f,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00049, 0x00049, 0x00049, 0x00049, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00059, 0x00059, 0x00059, 0x00059, 0x00059, 0x00059,
+ 0x00059, 0x00059, 0x01efa, 0x01efb, 0x01efc, 0x01efd, 0x01efe, 0x01eff
+};
+
+static uint32_t general_ci_page_1f[] = {
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391,
+ 0x00395, 0x00395, 0x00395, 0x00395, 0x00395, 0x00395, 0x01f16, 0x01f17,
+ 0x00395, 0x00395, 0x00395, 0x00395, 0x00395, 0x00395, 0x01f1e, 0x01f1f,
+ 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397,
+ 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397,
+ 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399,
+ 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399,
+ 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x01f46, 0x01f47,
+ 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x01f4e, 0x01f4f,
+ 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a5,
+ 0x01f58, 0x003a5, 0x01f5a, 0x003a5, 0x01f5c, 0x003a5, 0x01f5e, 0x003a5,
+ 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9,
+ 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9,
+ 0x00391, 0x01fbb, 0x00395, 0x01fc9, 0x00397, 0x01fcb, 0x00399, 0x01fdb,
+ 0x0039f, 0x01ff9, 0x003a5, 0x01feb, 0x003a9, 0x01ffb, 0x01f7e, 0x01f7f,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391,
+ 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397,
+ 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397,
+ 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9,
+ 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x01fb5, 0x00391, 0x00391,
+ 0x00391, 0x00391, 0x00391, 0x01fbb, 0x00391, 0x01fbd, 0x00399, 0x01fbf,
+ 0x01fc0, 0x01fc1, 0x00397, 0x00397, 0x00397, 0x01fc5, 0x00397, 0x00397,
+ 0x00395, 0x01fc9, 0x00397, 0x01fcb, 0x00397, 0x01fcd, 0x01fce, 0x01fcf,
+ 0x00399, 0x00399, 0x00399, 0x01fd3, 0x01fd4, 0x01fd5, 0x00399, 0x00399,
+ 0x00399, 0x00399, 0x00399, 0x01fdb, 0x01fdc, 0x01fdd, 0x01fde, 0x01fdf,
+ 0x003a5, 0x003a5, 0x003a5, 0x01fe3, 0x003a1, 0x003a1, 0x003a5, 0x003a5,
+ 0x003a5, 0x003a5, 0x003a5, 0x01feb, 0x003a1, 0x01fed, 0x01fee, 0x01fef,
+ 0x01ff0, 0x01ff1, 0x003a9, 0x003a9, 0x003a9, 0x01ff5, 0x003a9, 0x003a9,
+ 0x0039f, 0x01ff9, 0x003a9, 0x01ffb, 0x003a9, 0x01ffd, 0x01ffe, 0x01fff
+};
+
+static uint32_t general_ci_page_21[] = {
+ 0x02100, 0x02101, 0x02102, 0x02103, 0x02104, 0x02105, 0x02106, 0x02107,
+ 0x02108, 0x02109, 0x0210a, 0x0210b, 0x0210c, 0x0210d, 0x0210e, 0x0210f,
+ 0x02110, 0x02111, 0x02112, 0x02113, 0x02114, 0x02115, 0x02116, 0x02117,
+ 0x02118, 0x02119, 0x0211a, 0x0211b, 0x0211c, 0x0211d, 0x0211e, 0x0211f,
+ 0x02120, 0x02121, 0x02122, 0x02123, 0x02124, 0x02125, 0x02126, 0x02127,
+ 0x02128, 0x02129, 0x0212a, 0x0212b, 0x0212c, 0x0212d, 0x0212e, 0x0212f,
+ 0x02130, 0x02131, 0x02132, 0x02133, 0x02134, 0x02135, 0x02136, 0x02137,
+ 0x02138, 0x02139, 0x0213a, 0x0213b, 0x0213c, 0x0213d, 0x0213e, 0x0213f,
+ 0x02140, 0x02141, 0x02142, 0x02143, 0x02144, 0x02145, 0x02146, 0x02147,
+ 0x02148, 0x02149, 0x0214a, 0x0214b, 0x0214c, 0x0214d, 0x0214e, 0x0214f,
+ 0x02150, 0x02151, 0x02152, 0x02153, 0x02154, 0x02155, 0x02156, 0x02157,
+ 0x02158, 0x02159, 0x0215a, 0x0215b, 0x0215c, 0x0215d, 0x0215e, 0x0215f,
+ 0x02160, 0x02161, 0x02162, 0x02163, 0x02164, 0x02165, 0x02166, 0x02167,
+ 0x02168, 0x02169, 0x0216a, 0x0216b, 0x0216c, 0x0216d, 0x0216e, 0x0216f,
+ 0x02160, 0x02161, 0x02162, 0x02163, 0x02164, 0x02165, 0x02166, 0x02167,
+ 0x02168, 0x02169, 0x0216a, 0x0216b, 0x0216c, 0x0216d, 0x0216e, 0x0216f,
+ 0x02180, 0x02181, 0x02182, 0x02183, 0x02184, 0x02185, 0x02186, 0x02187,
+ 0x02188, 0x02189, 0x0218a, 0x0218b, 0x0218c, 0x0218d, 0x0218e, 0x0218f,
+ 0x02190, 0x02191, 0x02192, 0x02193, 0x02194, 0x02195, 0x02196, 0x02197,
+ 0x02198, 0x02199, 0x0219a, 0x0219b, 0x0219c, 0x0219d, 0x0219e, 0x0219f,
+ 0x021a0, 0x021a1, 0x021a2, 0x021a3, 0x021a4, 0x021a5, 0x021a6, 0x021a7,
+ 0x021a8, 0x021a9, 0x021aa, 0x021ab, 0x021ac, 0x021ad, 0x021ae, 0x021af,
+ 0x021b0, 0x021b1, 0x021b2, 0x021b3, 0x021b4, 0x021b5, 0x021b6, 0x021b7,
+ 0x021b8, 0x021b9, 0x021ba, 0x021bb, 0x021bc, 0x021bd, 0x021be, 0x021bf,
+ 0x021c0, 0x021c1, 0x021c2, 0x021c3, 0x021c4, 0x021c5, 0x021c6, 0x021c7,
+ 0x021c8, 0x021c9, 0x021ca, 0x021cb, 0x021cc, 0x021cd, 0x021ce, 0x021cf,
+ 0x021d0, 0x021d1, 0x021d2, 0x021d3, 0x021d4, 0x021d5, 0x021d6, 0x021d7,
+ 0x021d8, 0x021d9, 0x021da, 0x021db, 0x021dc, 0x021dd, 0x021de, 0x021df,
+ 0x021e0, 0x021e1, 0x021e2, 0x021e3, 0x021e4, 0x021e5, 0x021e6, 0x021e7,
+ 0x021e8, 0x021e9, 0x021ea, 0x021eb, 0x021ec, 0x021ed, 0x021ee, 0x021ef,
+ 0x021f0, 0x021f1, 0x021f2, 0x021f3, 0x021f4, 0x021f5, 0x021f6, 0x021f7,
+ 0x021f8, 0x021f9, 0x021fa, 0x021fb, 0x021fc, 0x021fd, 0x021fe, 0x021ff
+};
+
+static uint32_t general_ci_page_24[] = {
+ 0x02400, 0x02401, 0x02402, 0x02403, 0x02404, 0x02405, 0x02406, 0x02407,
+ 0x02408, 0x02409, 0x0240a, 0x0240b, 0x0240c, 0x0240d, 0x0240e, 0x0240f,
+ 0x02410, 0x02411, 0x02412, 0x02413, 0x02414, 0x02415, 0x02416, 0x02417,
+ 0x02418, 0x02419, 0x0241a, 0x0241b, 0x0241c, 0x0241d, 0x0241e, 0x0241f,
+ 0x02420, 0x02421, 0x02422, 0x02423, 0x02424, 0x02425, 0x02426, 0x02427,
+ 0x02428, 0x02429, 0x0242a, 0x0242b, 0x0242c, 0x0242d, 0x0242e, 0x0242f,
+ 0x02430, 0x02431, 0x02432, 0x02433, 0x02434, 0x02435, 0x02436, 0x02437,
+ 0x02438, 0x02439, 0x0243a, 0x0243b, 0x0243c, 0x0243d, 0x0243e, 0x0243f,
+ 0x02440, 0x02441, 0x02442, 0x02443, 0x02444, 0x02445, 0x02446, 0x02447,
+ 0x02448, 0x02449, 0x0244a, 0x0244b, 0x0244c, 0x0244d, 0x0244e, 0x0244f,
+ 0x02450, 0x02451, 0x02452, 0x02453, 0x02454, 0x02455, 0x02456, 0x02457,
+ 0x02458, 0x02459, 0x0245a, 0x0245b, 0x0245c, 0x0245d, 0x0245e, 0x0245f,
+ 0x02460, 0x02461, 0x02462, 0x02463, 0x02464, 0x02465, 0x02466, 0x02467,
+ 0x02468, 0x02469, 0x0246a, 0x0246b, 0x0246c, 0x0246d, 0x0246e, 0x0246f,
+ 0x02470, 0x02471, 0x02472, 0x02473, 0x02474, 0x02475, 0x02476, 0x02477,
+ 0x02478, 0x02479, 0x0247a, 0x0247b, 0x0247c, 0x0247d, 0x0247e, 0x0247f,
+ 0x02480, 0x02481, 0x02482, 0x02483, 0x02484, 0x02485, 0x02486, 0x02487,
+ 0x02488, 0x02489, 0x0248a, 0x0248b, 0x0248c, 0x0248d, 0x0248e, 0x0248f,
+ 0x02490, 0x02491, 0x02492, 0x02493, 0x02494, 0x02495, 0x02496, 0x02497,
+ 0x02498, 0x02499, 0x0249a, 0x0249b, 0x0249c, 0x0249d, 0x0249e, 0x0249f,
+ 0x024a0, 0x024a1, 0x024a2, 0x024a3, 0x024a4, 0x024a5, 0x024a6, 0x024a7,
+ 0x024a8, 0x024a9, 0x024aa, 0x024ab, 0x024ac, 0x024ad, 0x024ae, 0x024af,
+ 0x024b0, 0x024b1, 0x024b2, 0x024b3, 0x024b4, 0x024b5, 0x024b6, 0x024b7,
+ 0x024b8, 0x024b9, 0x024ba, 0x024bb, 0x024bc, 0x024bd, 0x024be, 0x024bf,
+ 0x024c0, 0x024c1, 0x024c2, 0x024c3, 0x024c4, 0x024c5, 0x024c6, 0x024c7,
+ 0x024c8, 0x024c9, 0x024ca, 0x024cb, 0x024cc, 0x024cd, 0x024ce, 0x024cf,
+ 0x024b6, 0x024b7, 0x024b8, 0x024b9, 0x024ba, 0x024bb, 0x024bc, 0x024bd,
+ 0x024be, 0x024bf, 0x024c0, 0x024c1, 0x024c2, 0x024c3, 0x024c4, 0x024c5,
+ 0x024c6, 0x024c7, 0x024c8, 0x024c9, 0x024ca, 0x024cb, 0x024cc, 0x024cd,
+ 0x024ce, 0x024cf, 0x024ea, 0x024eb, 0x024ec, 0x024ed, 0x024ee, 0x024ef,
+ 0x024f0, 0x024f1, 0x024f2, 0x024f3, 0x024f4, 0x024f5, 0x024f6, 0x024f7,
+ 0x024f8, 0x024f9, 0x024fa, 0x024fb, 0x024fc, 0x024fd, 0x024fe, 0x024ff
+};
+
+static uint32_t general_ci_page_ff[] = {
+ 0x0ff00, 0x0ff01, 0x0ff02, 0x0ff03, 0x0ff04, 0x0ff05, 0x0ff06, 0x0ff07,
+ 0x0ff08, 0x0ff09, 0x0ff0a, 0x0ff0b, 0x0ff0c, 0x0ff0d, 0x0ff0e, 0x0ff0f,
+ 0x0ff10, 0x0ff11, 0x0ff12, 0x0ff13, 0x0ff14, 0x0ff15, 0x0ff16, 0x0ff17,
+ 0x0ff18, 0x0ff19, 0x0ff1a, 0x0ff1b, 0x0ff1c, 0x0ff1d, 0x0ff1e, 0x0ff1f,
+ 0x0ff20, 0x0ff21, 0x0ff22, 0x0ff23, 0x0ff24, 0x0ff25, 0x0ff26, 0x0ff27,
+ 0x0ff28, 0x0ff29, 0x0ff2a, 0x0ff2b, 0x0ff2c, 0x0ff2d, 0x0ff2e, 0x0ff2f,
+ 0x0ff30, 0x0ff31, 0x0ff32, 0x0ff33, 0x0ff34, 0x0ff35, 0x0ff36, 0x0ff37,
+ 0x0ff38, 0x0ff39, 0x0ff3a, 0x0ff3b, 0x0ff3c, 0x0ff3d, 0x0ff3e, 0x0ff3f,
+ 0x0ff40, 0x0ff21, 0x0ff22, 0x0ff23, 0x0ff24, 0x0ff25, 0x0ff26, 0x0ff27,
+ 0x0ff28, 0x0ff29, 0x0ff2a, 0x0ff2b, 0x0ff2c, 0x0ff2d, 0x0ff2e, 0x0ff2f,
+ 0x0ff30, 0x0ff31, 0x0ff32, 0x0ff33, 0x0ff34, 0x0ff35, 0x0ff36, 0x0ff37,
+ 0x0ff38, 0x0ff39, 0x0ff3a, 0x0ff5b, 0x0ff5c, 0x0ff5d, 0x0ff5e, 0x0ff5f,
+ 0x0ff60, 0x0ff61, 0x0ff62, 0x0ff63, 0x0ff64, 0x0ff65, 0x0ff66, 0x0ff67,
+ 0x0ff68, 0x0ff69, 0x0ff6a, 0x0ff6b, 0x0ff6c, 0x0ff6d, 0x0ff6e, 0x0ff6f,
+ 0x0ff70, 0x0ff71, 0x0ff72, 0x0ff73, 0x0ff74, 0x0ff75, 0x0ff76, 0x0ff77,
+ 0x0ff78, 0x0ff79, 0x0ff7a, 0x0ff7b, 0x0ff7c, 0x0ff7d, 0x0ff7e, 0x0ff7f,
+ 0x0ff80, 0x0ff81, 0x0ff82, 0x0ff83, 0x0ff84, 0x0ff85, 0x0ff86, 0x0ff87,
+ 0x0ff88, 0x0ff89, 0x0ff8a, 0x0ff8b, 0x0ff8c, 0x0ff8d, 0x0ff8e, 0x0ff8f,
+ 0x0ff90, 0x0ff91, 0x0ff92, 0x0ff93, 0x0ff94, 0x0ff95, 0x0ff96, 0x0ff97,
+ 0x0ff98, 0x0ff99, 0x0ff9a, 0x0ff9b, 0x0ff9c, 0x0ff9d, 0x0ff9e, 0x0ff9f,
+ 0x0ffa0, 0x0ffa1, 0x0ffa2, 0x0ffa3, 0x0ffa4, 0x0ffa5, 0x0ffa6, 0x0ffa7,
+ 0x0ffa8, 0x0ffa9, 0x0ffaa, 0x0ffab, 0x0ffac, 0x0ffad, 0x0ffae, 0x0ffaf,
+ 0x0ffb0, 0x0ffb1, 0x0ffb2, 0x0ffb3, 0x0ffb4, 0x0ffb5, 0x0ffb6, 0x0ffb7,
+ 0x0ffb8, 0x0ffb9, 0x0ffba, 0x0ffbb, 0x0ffbc, 0x0ffbd, 0x0ffbe, 0x0ffbf,
+ 0x0ffc0, 0x0ffc1, 0x0ffc2, 0x0ffc3, 0x0ffc4, 0x0ffc5, 0x0ffc6, 0x0ffc7,
+ 0x0ffc8, 0x0ffc9, 0x0ffca, 0x0ffcb, 0x0ffcc, 0x0ffcd, 0x0ffce, 0x0ffcf,
+ 0x0ffd0, 0x0ffd1, 0x0ffd2, 0x0ffd3, 0x0ffd4, 0x0ffd5, 0x0ffd6, 0x0ffd7,
+ 0x0ffd8, 0x0ffd9, 0x0ffda, 0x0ffdb, 0x0ffdc, 0x0ffdd, 0x0ffde, 0x0ffdf,
+ 0x0ffe0, 0x0ffe1, 0x0ffe2, 0x0ffe3, 0x0ffe4, 0x0ffe5, 0x0ffe6, 0x0ffe7,
+ 0x0ffe8, 0x0ffe9, 0x0ffea, 0x0ffeb, 0x0ffec, 0x0ffed, 0x0ffee, 0x0ffef,
+ 0x0fff0, 0x0fff1, 0x0fff2, 0x0fff3, 0x0fff4, 0x0fff5, 0x0fff6, 0x0fff7,
+ 0x0fff8, 0x0fff9, 0x0fffa, 0x0fffb, 0x0fffc, 0x0fffd, 0x0fffe, 0x0ffff
+};
+
+static uint32_t *general_ci_table[256] = {
+ general_ci_page_00, general_ci_page_01,
+ general_ci_page_02, general_ci_page_03,
+ general_ci_page_04, general_ci_page_05,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ general_ci_page_1e, general_ci_page_1f,
+ NULL, general_ci_page_21,
+ NULL, NULL,
+ general_ci_page_24, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, general_ci_page_ff
+};
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_sources.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_sources.am
new file mode 100644
index 00000000000..6f1d9b02018
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_sources.am
@@ -0,0 +1,5 @@
+mysql_la_SOURCES = \
+ mysql.c \
+ mysql_general_ci_table.h \
+ mysql_unicode_ci_table.h \
+ mysql_unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_table.h
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_table.h b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_table.h
new file mode 100644
index 00000000000..4e6b6c539f4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_table.h
@@ -0,0 +1,1685 @@
+/*
+ Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; version 2
+ of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301, USA
+
+ This file uses normalization table defined in
+ mysql-5.5.29/strings/ctype-uca.c.
+ The following is the header of the file:
+
+ Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; version 2
+ of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301, USA
+
+ UCA (Unicode Collation Algorithm) support.
+ Written by Alexander Barkov <bar@mysql.com>
+*/
+
+#ifndef MYSQL_UCA_EXCEPT_KANA_CI_KANA_WITH_VOICED_SOUND_MARK_H
+#define MYSQL_UCA_EXCEPT_KANA_CI_KANA_WITH_VOICED_SOUND_MARK_H
+
+#include <stdint.h>
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_00[] = {
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00009, 0x0000a, 0x0000b, 0x0000c, 0x0000d, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00020, 0x00021, 0x00022, 0x00023, 0x00024, 0x00025, 0x00026, 0x00027,
+ 0x00028, 0x00029, 0x0002a, 0x0002b, 0x0002c, 0x0002d, 0x0002e, 0x0002f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0003a, 0x0003b, 0x0003c, 0x0003d, 0x0003e, 0x0003f,
+ 0x00040, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047,
+ 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f,
+ 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057,
+ 0x00058, 0x00059, 0x0005a, 0x0005b, 0x0005c, 0x0005d, 0x0005e, 0x0005f,
+ 0x00060, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047,
+ 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f,
+ 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057,
+ 0x00058, 0x00059, 0x0005a, 0x0007b, 0x0007c, 0x0007d, 0x0007e, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00085, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00020, 0x000a1, 0x000a2, 0x000a3, 0x000a4, 0x000a5, 0x000a6, 0x000a7,
+ 0x000a8, 0x000a9, 0x00041, 0x000ab, 0x000ac, 0x000ad, 0x000ae, 0x000af,
+ 0x000b0, 0x000b1, 0x00032, 0x00033, 0x000b4, 0x0039c, 0x000b6, 0x000b7,
+ 0x000b8, 0x00031, 0x0004f, 0x000bb, 0x000bc, 0x000bd, 0x000be, 0x000bf,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x000c6, 0x00043,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00049, 0x00049, 0x00049, 0x00049,
+ 0x000d0, 0x0004e, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x000d7,
+ 0x000d8, 0x00055, 0x00055, 0x00055, 0x00055, 0x00059, 0x000de, 0x000df,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x000c6, 0x00043,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00049, 0x00049, 0x00049, 0x00049,
+ 0x000d0, 0x0004e, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x000f7,
+ 0x000d8, 0x00055, 0x00055, 0x00055, 0x00055, 0x00059, 0x000de, 0x00059
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_01[] = {
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00043, 0x00043,
+ 0x00043, 0x00043, 0x00043, 0x00043, 0x00043, 0x00043, 0x00044, 0x00044,
+ 0x00110, 0x00110, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00047, 0x00047, 0x00047, 0x00047,
+ 0x00047, 0x00047, 0x00047, 0x00047, 0x00048, 0x00048, 0x00126, 0x00126,
+ 0x00049, 0x00049, 0x00049, 0x00049, 0x00049, 0x00049, 0x00049, 0x00049,
+ 0x00049, 0x00131, 0x00132, 0x00132, 0x0004a, 0x0004a, 0x0004b, 0x0004b,
+ 0x00138, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0013f,
+ 0x0013f, 0x00141, 0x00141, 0x0004e, 0x0004e, 0x0004e, 0x0004e, 0x0004e,
+ 0x0004e, 0x00149, 0x0014a, 0x0014a, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x00152, 0x00152, 0x00052, 0x00052, 0x00052, 0x00052,
+ 0x00052, 0x00052, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053,
+ 0x00053, 0x00053, 0x00054, 0x00054, 0x00054, 0x00054, 0x00166, 0x00166,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00057, 0x00057, 0x00059, 0x00059,
+ 0x00059, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x00053,
+ 0x00180, 0x00181, 0x00182, 0x00182, 0x00184, 0x00184, 0x00186, 0x00187,
+ 0x00187, 0x00189, 0x0018a, 0x0018b, 0x0018b, 0x0018d, 0x0018e, 0x0018f,
+ 0x00190, 0x00191, 0x00191, 0x00193, 0x00194, 0x00195, 0x00196, 0x00197,
+ 0x00198, 0x00198, 0x0019a, 0x0019b, 0x0019c, 0x0019d, 0x0019e, 0x0019f,
+ 0x0004f, 0x0004f, 0x001a2, 0x001a2, 0x001a4, 0x001a4, 0x001a6, 0x001a7,
+ 0x001a7, 0x001a9, 0x001aa, 0x001ab, 0x001ac, 0x001ac, 0x001ae, 0x00055,
+ 0x00055, 0x001b1, 0x001b2, 0x001b3, 0x001b3, 0x001b5, 0x001b5, 0x001b7,
+ 0x001b8, 0x001b8, 0x001ba, 0x001bb, 0x001bc, 0x001bc, 0x001be, 0x001bf,
+ 0x001c0, 0x001c1, 0x001c2, 0x001c3, 0x001c4, 0x001c4, 0x001c4, 0x001c7,
+ 0x001c7, 0x001c7, 0x001ca, 0x001ca, 0x001ca, 0x00041, 0x00041, 0x00049,
+ 0x00049, 0x0004f, 0x0004f, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x0018e, 0x00041, 0x00041,
+ 0x00041, 0x00041, 0x000c6, 0x000c6, 0x001e4, 0x001e4, 0x00047, 0x00047,
+ 0x0004b, 0x0004b, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x001b7, 0x001b7,
+ 0x0004a, 0x001c4, 0x001c4, 0x001c4, 0x00047, 0x00047, 0x00195, 0x001bf,
+ 0x0004e, 0x0004e, 0x00041, 0x00041, 0x000c6, 0x000c6, 0x000d8, 0x001ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_02[] = {
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00049, 0x00049, 0x00049, 0x00049, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x00052, 0x00052, 0x00052, 0x00052, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00053, 0x00053, 0x00054, 0x00054, 0x0021c, 0x0021c, 0x00048, 0x00048,
+ 0x0019e, 0x00221, 0x00222, 0x00222, 0x00224, 0x00224, 0x00041, 0x00041,
+ 0x00045, 0x00045, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x00059, 0x00059, 0x00234, 0x00235, 0x00236, 0x00237,
+ 0x00238, 0x00239, 0x0023a, 0x0023b, 0x0023c, 0x0023d, 0x0023e, 0x0023f,
+ 0x00240, 0x00241, 0x00242, 0x00243, 0x00244, 0x00245, 0x00246, 0x00247,
+ 0x00248, 0x00249, 0x0024a, 0x0024b, 0x0024c, 0x0024d, 0x0024e, 0x0024f,
+ 0x00250, 0x00251, 0x00252, 0x00181, 0x00186, 0x00255, 0x00189, 0x0018a,
+ 0x00258, 0x0018f, 0x0025a, 0x00190, 0x0025c, 0x0025d, 0x0025e, 0x0025f,
+ 0x00193, 0x00261, 0x00262, 0x00194, 0x00264, 0x00265, 0x00266, 0x00267,
+ 0x00197, 0x00196, 0x0026a, 0x0026b, 0x0026c, 0x0026d, 0x0026e, 0x0019c,
+ 0x00270, 0x00271, 0x0019d, 0x00273, 0x00274, 0x0019f, 0x00276, 0x00277,
+ 0x00278, 0x00279, 0x0027a, 0x0027b, 0x0027c, 0x0027d, 0x0027e, 0x0027f,
+ 0x001a6, 0x00281, 0x00282, 0x001a9, 0x00284, 0x00285, 0x00286, 0x00287,
+ 0x001ae, 0x00289, 0x001b1, 0x001b2, 0x0028c, 0x0028d, 0x0028e, 0x0028f,
+ 0x00290, 0x00291, 0x001b7, 0x00293, 0x00294, 0x00295, 0x00296, 0x00297,
+ 0x00298, 0x00299, 0x0029a, 0x0029b, 0x0029c, 0x0029d, 0x0029e, 0x0029f,
+ 0x002a0, 0x002a1, 0x002a2, 0x001c4, 0x002a4, 0x002a5, 0x001be, 0x002a7,
+ 0x002a8, 0x002a9, 0x002aa, 0x002ab, 0x002ac, 0x002ad, 0x002ae, 0x002af,
+ 0x00048, 0x00266, 0x0004a, 0x00052, 0x00279, 0x0027b, 0x00281, 0x00057,
+ 0x00059, 0x002b9, 0x002ba, 0x002bb, 0x002bc, 0x002bd, 0x002be, 0x002bf,
+ 0x002c0, 0x002c1, 0x002c2, 0x002c3, 0x002c4, 0x002c5, 0x002c6, 0x002c7,
+ 0x002c8, 0x002c9, 0x002ca, 0x002cb, 0x002cc, 0x002cd, 0x002ce, 0x002cf,
+ 0x002d0, 0x002d1, 0x002d2, 0x002d3, 0x002d4, 0x002d5, 0x002d6, 0x002d7,
+ 0x002d8, 0x002d9, 0x002da, 0x002db, 0x002dc, 0x002dd, 0x002de, 0x002df,
+ 0x00194, 0x0004c, 0x00053, 0x00058, 0x00295, 0x002e5, 0x002e6, 0x002e7,
+ 0x002e8, 0x002e9, 0x002ea, 0x002eb, 0x002ec, 0x002ed, 0x002ee, 0x002ef,
+ 0x002f0, 0x002f1, 0x002f2, 0x002f3, 0x002f4, 0x002f5, 0x002f6, 0x002f7,
+ 0x002f8, 0x002f9, 0x002fa, 0x002fb, 0x002fc, 0x002fd, 0x002fe, 0x002ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_03[] = {
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00358, 0x00359, 0x0035a, 0x0035b, 0x0035c, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00041, 0x00045, 0x00049, 0x0004f, 0x00055,
+ 0x00043, 0x00044, 0x00048, 0x0004d, 0x00052, 0x00054, 0x00056, 0x00058,
+ 0x00370, 0x00371, 0x00372, 0x00373, 0x002b9, 0x00375, 0x00376, 0x00377,
+ 0x00378, 0x00379, 0x00399, 0x0037b, 0x0037c, 0x0037d, 0x0003b, 0x0037f,
+ 0x00380, 0x00381, 0x00382, 0x00383, 0x000b4, 0x000a8, 0x00391, 0x000b7,
+ 0x00395, 0x00397, 0x00399, 0x0038b, 0x0039f, 0x0038d, 0x003a5, 0x003a9,
+ 0x00399, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397,
+ 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f,
+ 0x003a0, 0x003a1, 0x003a2, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7,
+ 0x003a8, 0x003a9, 0x00399, 0x003a5, 0x00391, 0x00395, 0x00397, 0x00399,
+ 0x003a5, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397,
+ 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f,
+ 0x003a0, 0x003a1, 0x003a3, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7,
+ 0x003a8, 0x003a9, 0x00399, 0x003a5, 0x0039f, 0x003a5, 0x003a9, 0x003cf,
+ 0x00392, 0x00398, 0x003a5, 0x003a5, 0x003a5, 0x003a6, 0x003a0, 0x003d7,
+ 0x003d8, 0x003d8, 0x003da, 0x003da, 0x003dc, 0x003dc, 0x003de, 0x003de,
+ 0x003e0, 0x003e0, 0x003e2, 0x003e2, 0x003e4, 0x003e4, 0x003e6, 0x003e6,
+ 0x003e8, 0x003e8, 0x003ea, 0x003ea, 0x003ec, 0x003ec, 0x003ee, 0x003ee,
+ 0x0039a, 0x003a1, 0x003a3, 0x003f3, 0x00398, 0x00395, 0x003f6, 0x003f7,
+ 0x003f7, 0x003a3, 0x003fa, 0x003fa, 0x003fc, 0x003fd, 0x003fe, 0x003ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_04[] = {
+ 0x00400, 0x00400, 0x00402, 0x00403, 0x00404, 0x00405, 0x00406, 0x00407,
+ 0x00408, 0x00409, 0x0040a, 0x0040b, 0x0040c, 0x0040d, 0x0040e, 0x0040f,
+ 0x00410, 0x00411, 0x00412, 0x00413, 0x00414, 0x00400, 0x00416, 0x00417,
+ 0x0040d, 0x00419, 0x0041a, 0x0041b, 0x0041c, 0x0041d, 0x0041e, 0x0041f,
+ 0x00420, 0x00421, 0x00422, 0x00423, 0x00424, 0x00425, 0x00426, 0x00427,
+ 0x00428, 0x00429, 0x0042a, 0x0042b, 0x0042c, 0x0042d, 0x0042e, 0x0042f,
+ 0x00410, 0x00411, 0x00412, 0x00413, 0x00414, 0x00400, 0x00416, 0x00417,
+ 0x0040d, 0x00419, 0x0041a, 0x0041b, 0x0041c, 0x0041d, 0x0041e, 0x0041f,
+ 0x00420, 0x00421, 0x00422, 0x00423, 0x00424, 0x00425, 0x00426, 0x00427,
+ 0x00428, 0x00429, 0x0042a, 0x0042b, 0x0042c, 0x0042d, 0x0042e, 0x0042f,
+ 0x00400, 0x00400, 0x00402, 0x00403, 0x00404, 0x00405, 0x00406, 0x00407,
+ 0x00408, 0x00409, 0x0040a, 0x0040b, 0x0040c, 0x0040d, 0x0040e, 0x0040f,
+ 0x00460, 0x00460, 0x00462, 0x00462, 0x00464, 0x00464, 0x00466, 0x00466,
+ 0x00468, 0x00468, 0x0046a, 0x0046a, 0x0046c, 0x0046c, 0x0046e, 0x0046e,
+ 0x00470, 0x00470, 0x00472, 0x00472, 0x00474, 0x00474, 0x00476, 0x00476,
+ 0x00478, 0x00478, 0x0047a, 0x0047a, 0x0047c, 0x0047c, 0x0047e, 0x0047e,
+ 0x00480, 0x00480, 0x00482, 0x00000, 0x00000, 0x00000, 0x00000, 0x00487,
+ 0x00000, 0x00000, 0x0048a, 0x0048a, 0x0048c, 0x0048c, 0x0048e, 0x0048e,
+ 0x00413, 0x00413, 0x00492, 0x00492, 0x00494, 0x00494, 0x00496, 0x00496,
+ 0x00498, 0x00498, 0x0049a, 0x0049a, 0x0049c, 0x0049c, 0x0049e, 0x0049e,
+ 0x004a0, 0x004a0, 0x004a2, 0x004a2, 0x004a4, 0x004a4, 0x004a6, 0x004a6,
+ 0x004a8, 0x004a8, 0x004aa, 0x004aa, 0x004ac, 0x004ac, 0x004ae, 0x004ae,
+ 0x004b0, 0x004b0, 0x004b2, 0x004b2, 0x004b4, 0x004b4, 0x004b6, 0x004b6,
+ 0x004b8, 0x004b8, 0x004ba, 0x004ba, 0x004bc, 0x004bc, 0x004be, 0x004be,
+ 0x004c0, 0x00416, 0x00416, 0x004c3, 0x004c3, 0x004c5, 0x004c5, 0x004c7,
+ 0x004c7, 0x004c9, 0x004c9, 0x004cb, 0x004cb, 0x004cd, 0x004cd, 0x004cf,
+ 0x004d0, 0x004d0, 0x004d2, 0x004d2, 0x004d4, 0x004d4, 0x004d6, 0x004d6,
+ 0x004d8, 0x004d8, 0x004da, 0x004da, 0x004dc, 0x004dc, 0x004de, 0x004de,
+ 0x004e0, 0x004e0, 0x0040d, 0x0040d, 0x004e4, 0x004e4, 0x004e6, 0x004e6,
+ 0x004e8, 0x004e8, 0x004ea, 0x004ea, 0x004ec, 0x004ec, 0x00423, 0x00423,
+ 0x004f0, 0x004f0, 0x004f2, 0x004f2, 0x004f4, 0x004f4, 0x004f6, 0x004f7,
+ 0x004f8, 0x004f8, 0x004fa, 0x004fb, 0x004fc, 0x004fd, 0x004fe, 0x004ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_05[] = {
+ 0x00500, 0x00500, 0x00502, 0x00502, 0x00504, 0x00504, 0x00506, 0x00506,
+ 0x00508, 0x00508, 0x0050a, 0x0050a, 0x0050c, 0x0050c, 0x0050e, 0x0050e,
+ 0x00510, 0x00511, 0x00512, 0x00513, 0x00514, 0x00515, 0x00516, 0x00517,
+ 0x00518, 0x00519, 0x0051a, 0x0051b, 0x0051c, 0x0051d, 0x0051e, 0x0051f,
+ 0x00520, 0x00521, 0x00522, 0x00523, 0x00524, 0x00525, 0x00526, 0x00527,
+ 0x00528, 0x00529, 0x0052a, 0x0052b, 0x0052c, 0x0052d, 0x0052e, 0x0052f,
+ 0x00530, 0x00531, 0x00532, 0x00533, 0x00534, 0x00535, 0x00536, 0x00537,
+ 0x00538, 0x00539, 0x0053a, 0x0053b, 0x0053c, 0x0053d, 0x0053e, 0x0053f,
+ 0x00540, 0x00541, 0x00542, 0x00543, 0x00544, 0x00545, 0x00546, 0x00547,
+ 0x00548, 0x00549, 0x0054a, 0x0054b, 0x0054c, 0x0054d, 0x0054e, 0x0054f,
+ 0x00550, 0x00551, 0x00552, 0x00553, 0x00554, 0x00555, 0x00556, 0x00557,
+ 0x00558, 0x00559, 0x0055a, 0x0055b, 0x0055c, 0x0055d, 0x0055e, 0x0055f,
+ 0x00560, 0x00531, 0x00532, 0x00533, 0x00534, 0x00535, 0x00536, 0x00537,
+ 0x00538, 0x00539, 0x0053a, 0x0053b, 0x0053c, 0x0053d, 0x0053e, 0x0053f,
+ 0x00540, 0x00541, 0x00542, 0x00543, 0x00544, 0x00545, 0x00546, 0x00547,
+ 0x00548, 0x00549, 0x0054a, 0x0054b, 0x0054c, 0x0054d, 0x0054e, 0x0054f,
+ 0x00550, 0x00551, 0x00552, 0x00553, 0x00554, 0x00555, 0x00556, 0x00587,
+ 0x00588, 0x00589, 0x0058a, 0x0058b, 0x0058c, 0x0058d, 0x0058e, 0x0058f,
+ 0x00590, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x005a2, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x005ba, 0x00000, 0x00000, 0x00000, 0x005be, 0x00000,
+ 0x005c0, 0x00000, 0x00000, 0x005c3, 0x00000, 0x005c5, 0x005c6, 0x005c7,
+ 0x005c8, 0x005c9, 0x005ca, 0x005cb, 0x005cc, 0x005cd, 0x005ce, 0x005cf,
+ 0x005d0, 0x005d1, 0x005d2, 0x005d3, 0x005d4, 0x005d5, 0x005d6, 0x005d7,
+ 0x005d8, 0x005d9, 0x005da, 0x005da, 0x005dc, 0x005dd, 0x005dd, 0x005df,
+ 0x005df, 0x005e1, 0x005e2, 0x005e3, 0x005e3, 0x005e5, 0x005e5, 0x005e7,
+ 0x005e8, 0x005e9, 0x005ea, 0x005eb, 0x005ec, 0x005ed, 0x005ee, 0x005ef,
+ 0x005f0, 0x005f1, 0x005f2, 0x005f3, 0x005f4, 0x005f5, 0x005f6, 0x005f7,
+ 0x005f8, 0x005f9, 0x005fa, 0x005fb, 0x005fc, 0x005fd, 0x005fe, 0x005ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_06[] = {
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00604, 0x00605, 0x00606, 0x00607,
+ 0x00608, 0x00609, 0x0060a, 0x0060b, 0x0060c, 0x0060d, 0x0060e, 0x0060f,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00616, 0x00617,
+ 0x00618, 0x00619, 0x0061a, 0x0061b, 0x0061c, 0x0061d, 0x0061e, 0x0061f,
+ 0x00620, 0x00621, 0x00622, 0x00623, 0x00624, 0x00625, 0x00626, 0x00627,
+ 0x00628, 0x00629, 0x0062a, 0x0062b, 0x0062c, 0x0062d, 0x0062e, 0x0062f,
+ 0x00630, 0x00631, 0x00632, 0x00633, 0x00634, 0x00635, 0x00636, 0x00637,
+ 0x00638, 0x00639, 0x0063a, 0x0063b, 0x0063c, 0x0063d, 0x0063e, 0x0063f,
+ 0x00640, 0x00641, 0x00642, 0x00643, 0x00644, 0x00645, 0x00646, 0x00647,
+ 0x00648, 0x00649, 0x0064a, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00659, 0x0065a, 0x0065b, 0x0065c, 0x0065d, 0x0065e, 0x0065f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0066a, 0x0066b, 0x0066c, 0x0066d, 0x0066e, 0x0066f,
+ 0x00000, 0x00671, 0x00672, 0x00673, 0x00621, 0x00675, 0x00676, 0x00677,
+ 0x00678, 0x00679, 0x0067a, 0x0067b, 0x0067c, 0x0067d, 0x0067e, 0x0067f,
+ 0x00680, 0x00681, 0x00682, 0x00683, 0x00684, 0x00685, 0x00686, 0x00687,
+ 0x00688, 0x00689, 0x0068a, 0x0068b, 0x0068c, 0x0068d, 0x0068e, 0x0068f,
+ 0x00690, 0x00691, 0x00692, 0x00693, 0x00694, 0x00695, 0x00696, 0x00697,
+ 0x00698, 0x00699, 0x0069a, 0x0069b, 0x0069c, 0x0069d, 0x0069e, 0x0069f,
+ 0x006a0, 0x006a1, 0x006a2, 0x006a3, 0x006a4, 0x006a5, 0x006a6, 0x006a7,
+ 0x006a8, 0x006a9, 0x006aa, 0x006ab, 0x006ac, 0x006ad, 0x006ae, 0x006af,
+ 0x006b0, 0x006b1, 0x006b2, 0x006b3, 0x006b4, 0x006b5, 0x006b6, 0x006b7,
+ 0x006b8, 0x006b9, 0x006ba, 0x006bb, 0x006bc, 0x006bd, 0x006be, 0x006bf,
+ 0x006c0, 0x006c1, 0x006c1, 0x006c3, 0x006c4, 0x006c5, 0x006c6, 0x006c7,
+ 0x006c8, 0x006c9, 0x006ca, 0x006cb, 0x006cc, 0x006cd, 0x006ce, 0x006cf,
+ 0x006d0, 0x006d1, 0x006d2, 0x006d2, 0x006d4, 0x006c0, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00648, 0x0064a, 0x00000,
+ 0x00000, 0x006e9, 0x00000, 0x00000, 0x00000, 0x00000, 0x006ee, 0x006ef,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x006fa, 0x006fb, 0x006fc, 0x00621, 0x00645, 0x006ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_07[] = {
+ 0x00700, 0x00701, 0x00702, 0x00703, 0x00704, 0x00705, 0x00706, 0x00707,
+ 0x00708, 0x00709, 0x0070a, 0x0070b, 0x0070c, 0x0070d, 0x0070e, 0x00000,
+ 0x00710, 0x00000, 0x00712, 0x00713, 0x00713, 0x00715, 0x00716, 0x00717,
+ 0x00718, 0x00719, 0x0071a, 0x0071b, 0x0071b, 0x0071d, 0x0071e, 0x0071f,
+ 0x00720, 0x00721, 0x00722, 0x00723, 0x00723, 0x00725, 0x00726, 0x00726,
+ 0x00728, 0x00729, 0x0072a, 0x0072b, 0x0072c, 0x00712, 0x00713, 0x00715,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x0074b, 0x0074c, 0x0074d, 0x0074e, 0x0074f,
+ 0x00750, 0x00751, 0x00752, 0x00753, 0x00754, 0x00755, 0x00756, 0x00757,
+ 0x00758, 0x00759, 0x0075a, 0x0075b, 0x0075c, 0x0075d, 0x0075e, 0x0075f,
+ 0x00760, 0x00761, 0x00762, 0x00763, 0x00764, 0x00765, 0x00766, 0x00767,
+ 0x00768, 0x00769, 0x0076a, 0x0076b, 0x0076c, 0x0076d, 0x0076e, 0x0076f,
+ 0x00770, 0x00771, 0x00772, 0x00773, 0x00774, 0x00775, 0x00776, 0x00777,
+ 0x00778, 0x00779, 0x0077a, 0x0077b, 0x0077c, 0x0077d, 0x0077e, 0x0077f,
+ 0x00780, 0x00781, 0x00782, 0x00783, 0x00784, 0x00785, 0x00786, 0x00787,
+ 0x00788, 0x00789, 0x0078a, 0x0078b, 0x0078c, 0x0078d, 0x0078e, 0x0078f,
+ 0x00790, 0x00791, 0x00792, 0x00793, 0x00794, 0x00795, 0x00796, 0x00797,
+ 0x00798, 0x00799, 0x0079a, 0x0079b, 0x0079c, 0x0079d, 0x0079e, 0x0079f,
+ 0x007a0, 0x007a1, 0x007a2, 0x007a3, 0x007a4, 0x007a5, 0x007a6, 0x007a7,
+ 0x007a8, 0x007a9, 0x007aa, 0x007ab, 0x007ac, 0x007ad, 0x007ae, 0x007af,
+ 0x007b0, 0x007b1, 0x007b2, 0x007b3, 0x007b4, 0x007b5, 0x007b6, 0x007b7,
+ 0x007b8, 0x007b9, 0x007ba, 0x007bb, 0x007bc, 0x007bd, 0x007be, 0x007bf,
+ 0x007c0, 0x007c1, 0x007c2, 0x007c3, 0x007c4, 0x007c5, 0x007c6, 0x007c7,
+ 0x007c8, 0x007c9, 0x007ca, 0x007cb, 0x007cc, 0x007cd, 0x007ce, 0x007cf,
+ 0x007d0, 0x007d1, 0x007d2, 0x007d3, 0x007d4, 0x007d5, 0x007d6, 0x007d7,
+ 0x007d8, 0x007d9, 0x007da, 0x007db, 0x007dc, 0x007dd, 0x007de, 0x007df,
+ 0x007e0, 0x007e1, 0x007e2, 0x007e3, 0x007e4, 0x007e5, 0x007e6, 0x007e7,
+ 0x007e8, 0x007e9, 0x007ea, 0x007eb, 0x007ec, 0x007ed, 0x007ee, 0x007ef,
+ 0x007f0, 0x007f1, 0x007f2, 0x007f3, 0x007f4, 0x007f5, 0x007f6, 0x007f7,
+ 0x007f8, 0x007f9, 0x007fa, 0x007fb, 0x007fc, 0x007fd, 0x007fe, 0x007ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_09[] = {
+ 0x00900, 0x00000, 0x00000, 0x00000, 0x00904, 0x00905, 0x00906, 0x00907,
+ 0x00908, 0x00909, 0x0090a, 0x0090b, 0x0090c, 0x0090d, 0x0090e, 0x0090f,
+ 0x00910, 0x00911, 0x00912, 0x00913, 0x00914, 0x00915, 0x00916, 0x00917,
+ 0x00918, 0x00919, 0x0091a, 0x0091b, 0x0091c, 0x0091d, 0x0091e, 0x0091f,
+ 0x00920, 0x00921, 0x00922, 0x00923, 0x00924, 0x00925, 0x00926, 0x00927,
+ 0x00928, 0x00928, 0x0092a, 0x0092b, 0x0092c, 0x0092d, 0x0092e, 0x0092f,
+ 0x00930, 0x00930, 0x00932, 0x00933, 0x00933, 0x00935, 0x00936, 0x00937,
+ 0x00938, 0x00939, 0x0093a, 0x0093b, 0x00000, 0x0093d, 0x0093e, 0x0093f,
+ 0x00940, 0x00941, 0x00942, 0x00943, 0x00944, 0x00945, 0x00946, 0x00947,
+ 0x00948, 0x00949, 0x0094a, 0x0094b, 0x0094c, 0x0094d, 0x0094e, 0x0094f,
+ 0x00950, 0x00000, 0x00000, 0x00000, 0x00000, 0x00955, 0x00956, 0x00957,
+ 0x00915, 0x00916, 0x00917, 0x0091c, 0x00921, 0x00922, 0x0092b, 0x0092f,
+ 0x00960, 0x00961, 0x00962, 0x00963, 0x00964, 0x00965, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00970, 0x00971, 0x00972, 0x00973, 0x00974, 0x00975, 0x00976, 0x00977,
+ 0x00978, 0x00979, 0x0097a, 0x0097b, 0x0097c, 0x0097d, 0x0097e, 0x0097f,
+ 0x00980, 0x00000, 0x00000, 0x00000, 0x00984, 0x00985, 0x00986, 0x00987,
+ 0x00988, 0x00989, 0x0098a, 0x0098b, 0x0098c, 0x0098d, 0x0098e, 0x0098f,
+ 0x00990, 0x00991, 0x00992, 0x00993, 0x00994, 0x00995, 0x00996, 0x00997,
+ 0x00998, 0x00999, 0x0099a, 0x0099b, 0x0099c, 0x0099d, 0x0099e, 0x0099f,
+ 0x009a0, 0x009a1, 0x009a2, 0x009a3, 0x009a4, 0x009a5, 0x009a6, 0x009a7,
+ 0x009a8, 0x009a9, 0x009aa, 0x009ab, 0x009ac, 0x009ad, 0x009ae, 0x009af,
+ 0x009b0, 0x009b1, 0x009b2, 0x009b3, 0x009b4, 0x009b5, 0x009b6, 0x009b7,
+ 0x009b8, 0x009b9, 0x009ba, 0x009bb, 0x00000, 0x009bd, 0x009be, 0x009bf,
+ 0x009c0, 0x009c1, 0x009c2, 0x009c3, 0x009c4, 0x009c5, 0x009c6, 0x009c7,
+ 0x009c8, 0x009c9, 0x009ca, 0x009cb, 0x009cc, 0x009cd, 0x009ce, 0x009cf,
+ 0x009d0, 0x009d1, 0x009d2, 0x009d3, 0x009d4, 0x009d5, 0x009d6, 0x009d7,
+ 0x009d8, 0x009d9, 0x009da, 0x009db, 0x009a1, 0x009a2, 0x009de, 0x009af,
+ 0x009e0, 0x009e1, 0x009e2, 0x009e3, 0x009e4, 0x009e5, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x009f0, 0x009f1, 0x009f2, 0x009f3, 0x00031, 0x00032, 0x00033, 0x00034,
+ 0x009f8, 0x009f9, 0x009fa, 0x009fb, 0x009fc, 0x009fd, 0x009fe, 0x009ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_0a[] = {
+ 0x00a00, 0x00000, 0x00000, 0x00000, 0x00a04, 0x00a05, 0x00a06, 0x00a07,
+ 0x00a08, 0x00a09, 0x00a0a, 0x00a0b, 0x00a0c, 0x00a0d, 0x00a0e, 0x00a0f,
+ 0x00a10, 0x00a11, 0x00a12, 0x00a13, 0x00a14, 0x00a15, 0x00a16, 0x00a17,
+ 0x00a18, 0x00a19, 0x00a1a, 0x00a1b, 0x00a1c, 0x00a1d, 0x00a1e, 0x00a1f,
+ 0x00a20, 0x00a21, 0x00a22, 0x00a23, 0x00a24, 0x00a25, 0x00a26, 0x00a27,
+ 0x00a28, 0x00a29, 0x00a2a, 0x00a2b, 0x00a2c, 0x00a2d, 0x00a2e, 0x00a2f,
+ 0x00a30, 0x00a31, 0x00a32, 0x00a32, 0x00a34, 0x00a35, 0x00a36, 0x00a37,
+ 0x00a36, 0x00a39, 0x00a3a, 0x00a3b, 0x00000, 0x00a3d, 0x00a3e, 0x00a3f,
+ 0x00a40, 0x00a41, 0x00a42, 0x00a43, 0x00a44, 0x00a45, 0x00a46, 0x00a47,
+ 0x00a48, 0x00a49, 0x00a4a, 0x00a4b, 0x00a4c, 0x00a4d, 0x00a4e, 0x00a4f,
+ 0x00a50, 0x00a51, 0x00a52, 0x00a53, 0x00a54, 0x00a55, 0x00a56, 0x00a57,
+ 0x00a58, 0x00a16, 0x00a17, 0x00a1c, 0x00a5c, 0x00a5d, 0x00a2b, 0x00a5f,
+ 0x00a60, 0x00a61, 0x00a62, 0x00a63, 0x00a64, 0x00a65, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00000, 0x00000, 0x00a72, 0x00a73, 0x00a74, 0x00a75, 0x00a76, 0x00a77,
+ 0x00a78, 0x00a79, 0x00a7a, 0x00a7b, 0x00a7c, 0x00a7d, 0x00a7e, 0x00a7f,
+ 0x00a80, 0x00000, 0x00000, 0x00000, 0x00a84, 0x00a85, 0x00a86, 0x00a87,
+ 0x00a88, 0x00a89, 0x00a8a, 0x00a8b, 0x00a8c, 0x00a8d, 0x00a8e, 0x00a8f,
+ 0x00a90, 0x00a91, 0x00a92, 0x00a93, 0x00a94, 0x00a95, 0x00a96, 0x00a97,
+ 0x00a98, 0x00a99, 0x00a9a, 0x00a9b, 0x00a9c, 0x00a9d, 0x00a9e, 0x00a9f,
+ 0x00aa0, 0x00aa1, 0x00aa2, 0x00aa3, 0x00aa4, 0x00aa5, 0x00aa6, 0x00aa7,
+ 0x00aa8, 0x00aa9, 0x00aaa, 0x00aab, 0x00aac, 0x00aad, 0x00aae, 0x00aaf,
+ 0x00ab0, 0x00ab1, 0x00ab2, 0x00ab3, 0x00ab4, 0x00ab5, 0x00ab6, 0x00ab7,
+ 0x00ab8, 0x00ab9, 0x00aba, 0x00abb, 0x00000, 0x00abd, 0x00abe, 0x00abf,
+ 0x00ac0, 0x00ac1, 0x00ac2, 0x00ac3, 0x00ac4, 0x00ac5, 0x00ac6, 0x00ac7,
+ 0x00ac8, 0x00ac9, 0x00aca, 0x00acb, 0x00acc, 0x00acd, 0x00ace, 0x00acf,
+ 0x00ad0, 0x00ad1, 0x00ad2, 0x00ad3, 0x00ad4, 0x00ad5, 0x00ad6, 0x00ad7,
+ 0x00ad8, 0x00ad9, 0x00ada, 0x00adb, 0x00adc, 0x00add, 0x00ade, 0x00adf,
+ 0x00ae0, 0x00ae1, 0x00ae2, 0x00ae3, 0x00ae4, 0x00ae5, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00af0, 0x00af1, 0x00af2, 0x00af3, 0x00af4, 0x00af5, 0x00af6, 0x00af7,
+ 0x00af8, 0x00af9, 0x00afa, 0x00afb, 0x00afc, 0x00afd, 0x00afe, 0x00aff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_0b[] = {
+ 0x00b00, 0x00000, 0x00000, 0x00000, 0x00b04, 0x00b05, 0x00b06, 0x00b07,
+ 0x00b08, 0x00b09, 0x00b0a, 0x00b0b, 0x00b0c, 0x00b0d, 0x00b0e, 0x00b0f,
+ 0x00b10, 0x00b11, 0x00b12, 0x00b13, 0x00b14, 0x00b15, 0x00b16, 0x00b17,
+ 0x00b18, 0x00b19, 0x00b1a, 0x00b1b, 0x00b1c, 0x00b1d, 0x00b1e, 0x00b1f,
+ 0x00b20, 0x00b21, 0x00b22, 0x00b23, 0x00b24, 0x00b25, 0x00b26, 0x00b27,
+ 0x00b28, 0x00b29, 0x00b2a, 0x00b2b, 0x00b2c, 0x00b2d, 0x00b2e, 0x00b2f,
+ 0x00b30, 0x00b31, 0x00b32, 0x00b33, 0x00b34, 0x00b35, 0x00b36, 0x00b37,
+ 0x00b38, 0x00b39, 0x00b3a, 0x00b3b, 0x00000, 0x00b3d, 0x00b3e, 0x00b3f,
+ 0x00b40, 0x00b41, 0x00b42, 0x00b43, 0x00b44, 0x00b45, 0x00b46, 0x00b47,
+ 0x00b48, 0x00b49, 0x00b4a, 0x00b4b, 0x00b4c, 0x00b4d, 0x00b4e, 0x00b4f,
+ 0x00b50, 0x00b51, 0x00b52, 0x00b53, 0x00b54, 0x00b55, 0x00b56, 0x00b57,
+ 0x00b58, 0x00b59, 0x00b5a, 0x00b5b, 0x00b21, 0x00b22, 0x00b5e, 0x00b5f,
+ 0x00b60, 0x00b61, 0x00b62, 0x00b63, 0x00b64, 0x00b65, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00b70, 0x00b71, 0x00b72, 0x00b73, 0x00b74, 0x00b75, 0x00b76, 0x00b77,
+ 0x00b78, 0x00b79, 0x00b7a, 0x00b7b, 0x00b7c, 0x00b7d, 0x00b7e, 0x00b7f,
+ 0x00b80, 0x00b81, 0x00000, 0x00b83, 0x00b84, 0x00b85, 0x00b86, 0x00b87,
+ 0x00b88, 0x00b89, 0x00b8a, 0x00b8b, 0x00b8c, 0x00b8d, 0x00b8e, 0x00b8f,
+ 0x00b90, 0x00b91, 0x00b92, 0x00b93, 0x00b94, 0x00b95, 0x00b96, 0x00b97,
+ 0x00b98, 0x00b99, 0x00b9a, 0x00b9b, 0x00b9c, 0x00b9d, 0x00b9e, 0x00b9f,
+ 0x00ba0, 0x00ba1, 0x00ba2, 0x00ba3, 0x00ba4, 0x00ba5, 0x00ba6, 0x00ba7,
+ 0x00ba8, 0x00ba9, 0x00baa, 0x00bab, 0x00bac, 0x00bad, 0x00bae, 0x00baf,
+ 0x00bb0, 0x00bb1, 0x00bb2, 0x00bb3, 0x00bb4, 0x00bb5, 0x00bb6, 0x00bb7,
+ 0x00bb8, 0x00bb9, 0x00bba, 0x00bbb, 0x00bbc, 0x00bbd, 0x00bbe, 0x00bbf,
+ 0x00bc0, 0x00bc1, 0x00bc2, 0x00bc3, 0x00bc4, 0x00bc5, 0x00bc6, 0x00bc7,
+ 0x00bc8, 0x00bc9, 0x00bca, 0x00bcb, 0x00bcc, 0x00bcd, 0x00bce, 0x00bcf,
+ 0x00bd0, 0x00bd1, 0x00bd2, 0x00bd3, 0x00bd4, 0x00bd5, 0x00bd6, 0x00bd7,
+ 0x00bd8, 0x00bd9, 0x00bda, 0x00bdb, 0x00bdc, 0x00bdd, 0x00bde, 0x00bdf,
+ 0x00be0, 0x00be1, 0x00be2, 0x00be3, 0x00be4, 0x00be5, 0x00be6, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00bf0, 0x00bf1, 0x00bf2, 0x00bf3, 0x00bf4, 0x00bf5, 0x00bf6, 0x00bf7,
+ 0x00bf8, 0x00bf9, 0x00bfa, 0x00bfb, 0x00bfc, 0x00bfd, 0x00bfe, 0x00bff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_0c[] = {
+ 0x00c00, 0x00000, 0x00000, 0x00000, 0x00c04, 0x00c05, 0x00c06, 0x00c07,
+ 0x00c08, 0x00c09, 0x00c0a, 0x00c0b, 0x00c0c, 0x00c0d, 0x00c0e, 0x00c0f,
+ 0x00c10, 0x00c11, 0x00c12, 0x00c13, 0x00c14, 0x00c15, 0x00c16, 0x00c17,
+ 0x00c18, 0x00c19, 0x00c1a, 0x00c1b, 0x00c1c, 0x00c1d, 0x00c1e, 0x00c1f,
+ 0x00c20, 0x00c21, 0x00c22, 0x00c23, 0x00c24, 0x00c25, 0x00c26, 0x00c27,
+ 0x00c28, 0x00c29, 0x00c2a, 0x00c2b, 0x00c2c, 0x00c2d, 0x00c2e, 0x00c2f,
+ 0x00c30, 0x00c31, 0x00c32, 0x00c33, 0x00c34, 0x00c35, 0x00c36, 0x00c37,
+ 0x00c38, 0x00c39, 0x00c3a, 0x00c3b, 0x00c3c, 0x00c3d, 0x00c3e, 0x00c3f,
+ 0x00c40, 0x00c41, 0x00c42, 0x00c43, 0x00c44, 0x00c45, 0x00c46, 0x00c47,
+ 0x00c48, 0x00c49, 0x00c4a, 0x00c4b, 0x00c4c, 0x00c4d, 0x00c4e, 0x00c4f,
+ 0x00c50, 0x00c51, 0x00c52, 0x00c53, 0x00c54, 0x00c55, 0x00c56, 0x00c57,
+ 0x00c58, 0x00c59, 0x00c5a, 0x00c5b, 0x00c5c, 0x00c5d, 0x00c5e, 0x00c5f,
+ 0x00c60, 0x00c61, 0x00c62, 0x00c63, 0x00c64, 0x00c65, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00c70, 0x00c71, 0x00c72, 0x00c73, 0x00c74, 0x00c75, 0x00c76, 0x00c77,
+ 0x00c78, 0x00c79, 0x00c7a, 0x00c7b, 0x00c7c, 0x00c7d, 0x00c7e, 0x00c7f,
+ 0x00c80, 0x00c81, 0x00000, 0x00000, 0x00c84, 0x00c85, 0x00c86, 0x00c87,
+ 0x00c88, 0x00c89, 0x00c8a, 0x00c8b, 0x00c8c, 0x00c8d, 0x00c8e, 0x00c8f,
+ 0x00c90, 0x00c91, 0x00c92, 0x00c93, 0x00c94, 0x00c95, 0x00c96, 0x00c97,
+ 0x00c98, 0x00c99, 0x00c9a, 0x00c9b, 0x00c9c, 0x00c9d, 0x00c9e, 0x00c9f,
+ 0x00ca0, 0x00ca1, 0x00ca2, 0x00ca3, 0x00ca4, 0x00ca5, 0x00ca6, 0x00ca7,
+ 0x00ca8, 0x00ca9, 0x00caa, 0x00cab, 0x00cac, 0x00cad, 0x00cae, 0x00caf,
+ 0x00cb0, 0x00cb1, 0x00cb2, 0x00cb3, 0x00cb4, 0x00cb5, 0x00cb6, 0x00cb7,
+ 0x00cb8, 0x00cb9, 0x00cba, 0x00cbb, 0x00000, 0x00cbd, 0x00cbe, 0x00cbf,
+ 0x00cc0, 0x00cc1, 0x00cc2, 0x00cc3, 0x00cc4, 0x00cc5, 0x00cc6, 0x00cc7,
+ 0x00cc8, 0x00cc9, 0x00cca, 0x00ccb, 0x00ccc, 0x00ccd, 0x00cce, 0x00ccf,
+ 0x00cd0, 0x00cd1, 0x00cd2, 0x00cd3, 0x00cd4, 0x00cd5, 0x00cd6, 0x00cd7,
+ 0x00cd8, 0x00cd9, 0x00cda, 0x00cdb, 0x00cdc, 0x00cdd, 0x00cde, 0x00cdf,
+ 0x00ce0, 0x00ce1, 0x00ce2, 0x00ce3, 0x00ce4, 0x00ce5, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00cf0, 0x00cf1, 0x00cf2, 0x00cf3, 0x00cf4, 0x00cf5, 0x00cf6, 0x00cf7,
+ 0x00cf8, 0x00cf9, 0x00cfa, 0x00cfb, 0x00cfc, 0x00cfd, 0x00cfe, 0x00cff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_0d[] = {
+ 0x00d00, 0x00d01, 0x00000, 0x00000, 0x00d04, 0x00d05, 0x00d06, 0x00d07,
+ 0x00d08, 0x00d09, 0x00d0a, 0x00d0b, 0x00d0c, 0x00d0d, 0x00d0e, 0x00d0f,
+ 0x00d10, 0x00d11, 0x00d12, 0x00d13, 0x00d14, 0x00d15, 0x00d16, 0x00d17,
+ 0x00d18, 0x00d19, 0x00d1a, 0x00d1b, 0x00d1c, 0x00d1d, 0x00d1e, 0x00d1f,
+ 0x00d20, 0x00d21, 0x00d22, 0x00d23, 0x00d24, 0x00d25, 0x00d26, 0x00d27,
+ 0x00d28, 0x00d29, 0x00d2a, 0x00d2b, 0x00d2c, 0x00d2d, 0x00d2e, 0x00d2f,
+ 0x00d30, 0x00d31, 0x00d32, 0x00d33, 0x00d34, 0x00d35, 0x00d36, 0x00d37,
+ 0x00d38, 0x00d39, 0x00d3a, 0x00d3b, 0x00d3c, 0x00d3d, 0x00d3e, 0x00d3f,
+ 0x00d40, 0x00d41, 0x00d42, 0x00d43, 0x00d44, 0x00d45, 0x00d46, 0x00d47,
+ 0x00d48, 0x00d49, 0x00d4a, 0x00d4b, 0x00d4c, 0x00d4d, 0x00d4e, 0x00d4f,
+ 0x00d50, 0x00d51, 0x00d52, 0x00d53, 0x00d54, 0x00d55, 0x00d56, 0x00d57,
+ 0x00d58, 0x00d59, 0x00d5a, 0x00d5b, 0x00d5c, 0x00d5d, 0x00d5e, 0x00d5f,
+ 0x00d60, 0x00d61, 0x00d62, 0x00d63, 0x00d64, 0x00d65, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00d70, 0x00d71, 0x00d72, 0x00d73, 0x00d74, 0x00d75, 0x00d76, 0x00d77,
+ 0x00d78, 0x00d79, 0x00d7a, 0x00d7b, 0x00d7c, 0x00d7d, 0x00d7e, 0x00d7f,
+ 0x00d80, 0x00d81, 0x00000, 0x00000, 0x00d84, 0x00d85, 0x00d86, 0x00d87,
+ 0x00d88, 0x00d89, 0x00d8a, 0x00d8b, 0x00d8c, 0x00d8d, 0x00d8e, 0x00d8f,
+ 0x00d90, 0x00d91, 0x00d92, 0x00d93, 0x00d94, 0x00d95, 0x00d96, 0x00d97,
+ 0x00d98, 0x00d99, 0x00d9a, 0x00d9b, 0x00d9c, 0x00d9d, 0x00d9e, 0x00d9f,
+ 0x00da0, 0x00da1, 0x00da2, 0x00da3, 0x00da4, 0x00da5, 0x00da6, 0x00da7,
+ 0x00da8, 0x00da9, 0x00daa, 0x00dab, 0x00dac, 0x00dad, 0x00dae, 0x00daf,
+ 0x00db0, 0x00db1, 0x00db2, 0x00db3, 0x00db4, 0x00db5, 0x00db6, 0x00db7,
+ 0x00db8, 0x00db9, 0x00dba, 0x00dbb, 0x00dbc, 0x00dbd, 0x00dbe, 0x00dbf,
+ 0x00dc0, 0x00dc1, 0x00dc2, 0x00dc3, 0x00dc4, 0x00dc5, 0x00dc6, 0x00dc7,
+ 0x00dc8, 0x00dc9, 0x00dca, 0x00dcb, 0x00dcc, 0x00dcd, 0x00dce, 0x00dcf,
+ 0x00dd0, 0x00dd1, 0x00dd2, 0x00dd3, 0x00dd4, 0x00dd5, 0x00dd6, 0x00dd7,
+ 0x00dd8, 0x00dd9, 0x00dda, 0x00ddb, 0x00ddc, 0x00ddd, 0x00dde, 0x00ddf,
+ 0x00de0, 0x00de1, 0x00de2, 0x00de3, 0x00de4, 0x00de5, 0x00de6, 0x00de7,
+ 0x00de8, 0x00de9, 0x00dea, 0x00deb, 0x00dec, 0x00ded, 0x00dee, 0x00def,
+ 0x00df0, 0x00df1, 0x00df2, 0x00df3, 0x00df4, 0x00df5, 0x00df6, 0x00df7,
+ 0x00df8, 0x00df9, 0x00dfa, 0x00dfb, 0x00dfc, 0x00dfd, 0x00dfe, 0x00dff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_0e[] = {
+ 0x00e00, 0x00e01, 0x00e02, 0x00e03, 0x00e04, 0x00e05, 0x00e06, 0x00e07,
+ 0x00e08, 0x00e09, 0x00e0a, 0x00e0b, 0x00e0c, 0x00e0d, 0x00e0e, 0x00e0f,
+ 0x00e10, 0x00e11, 0x00e12, 0x00e13, 0x00e14, 0x00e15, 0x00e16, 0x00e17,
+ 0x00e18, 0x00e19, 0x00e1a, 0x00e1b, 0x00e1c, 0x00e1d, 0x00e1e, 0x00e1f,
+ 0x00e20, 0x00e21, 0x00e22, 0x00e23, 0x00e24, 0x00e25, 0x00e26, 0x00e27,
+ 0x00e28, 0x00e29, 0x00e2a, 0x00e2b, 0x00e2c, 0x00e2d, 0x00e2e, 0x00e2f,
+ 0x00e30, 0x00e31, 0x00e32, 0x00e33, 0x00e34, 0x00e35, 0x00e36, 0x00e37,
+ 0x00e38, 0x00e39, 0x00e3a, 0x00e3b, 0x00e3c, 0x00e3d, 0x00e3e, 0x00e3f,
+ 0x00e40, 0x00e41, 0x00e42, 0x00e43, 0x00e44, 0x00e45, 0x00e46, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00e4c, 0x00e4d, 0x00000, 0x00e4f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x00e5a, 0x00e5b, 0x00e5c, 0x00e5d, 0x00e5e, 0x00e5f,
+ 0x00e60, 0x00e61, 0x00e62, 0x00e63, 0x00e64, 0x00e65, 0x00e66, 0x00e67,
+ 0x00e68, 0x00e69, 0x00e6a, 0x00e6b, 0x00e6c, 0x00e6d, 0x00e6e, 0x00e6f,
+ 0x00e70, 0x00e71, 0x00e72, 0x00e73, 0x00e74, 0x00e75, 0x00e76, 0x00e77,
+ 0x00e78, 0x00e79, 0x00e7a, 0x00e7b, 0x00e7c, 0x00e7d, 0x00e7e, 0x00e7f,
+ 0x00e80, 0x00e81, 0x00e82, 0x00e83, 0x00e84, 0x00e85, 0x00e86, 0x00e87,
+ 0x00e88, 0x00e89, 0x00e8a, 0x00e8b, 0x00e8c, 0x00e8d, 0x00e8e, 0x00e8f,
+ 0x00e90, 0x00e91, 0x00e92, 0x00e93, 0x00e94, 0x00e95, 0x00e96, 0x00e97,
+ 0x00e98, 0x00e99, 0x00e9a, 0x00e9b, 0x00e9c, 0x00e9d, 0x00e9e, 0x00e9f,
+ 0x00ea0, 0x00ea1, 0x00ea2, 0x00ea3, 0x00ea4, 0x00ea5, 0x00ea6, 0x00ea7,
+ 0x00ea8, 0x00ea9, 0x00eaa, 0x00eab, 0x00eac, 0x00ead, 0x00eae, 0x00eaf,
+ 0x00eb0, 0x00eb1, 0x00eb2, 0x00eb3, 0x00eb4, 0x00eb5, 0x00eb6, 0x00eb7,
+ 0x00eb8, 0x00eb9, 0x00eba, 0x00ebb, 0x00ebc, 0x00ebd, 0x00ebe, 0x00ebf,
+ 0x00ec0, 0x00ec1, 0x00ec2, 0x00ec3, 0x00ec4, 0x00ec5, 0x00ec6, 0x00ec7,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00ecc, 0x00ecd, 0x00ece, 0x00ecf,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x00eda, 0x00edb, 0x00edc, 0x00edd, 0x00ede, 0x00edf,
+ 0x00ee0, 0x00ee1, 0x00ee2, 0x00ee3, 0x00ee4, 0x00ee5, 0x00ee6, 0x00ee7,
+ 0x00ee8, 0x00ee9, 0x00eea, 0x00eeb, 0x00eec, 0x00eed, 0x00eee, 0x00eef,
+ 0x00ef0, 0x00ef1, 0x00ef2, 0x00ef3, 0x00ef4, 0x00ef5, 0x00ef6, 0x00ef7,
+ 0x00ef8, 0x00ef9, 0x00efa, 0x00efb, 0x00efc, 0x00efd, 0x00efe, 0x00eff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_0f[] = {
+ 0x00f00, 0x00f01, 0x00f02, 0x00f03, 0x00f04, 0x00f05, 0x00f06, 0x00f07,
+ 0x00f08, 0x00f09, 0x00f0a, 0x00f0b, 0x00f0b, 0x00f0d, 0x00f0e, 0x00f0f,
+ 0x00f10, 0x00f11, 0x00f12, 0x00f13, 0x00f14, 0x00f15, 0x00f16, 0x00f17,
+ 0x00000, 0x00000, 0x00f1a, 0x00f1b, 0x00f1c, 0x00f1d, 0x00f1e, 0x00f1f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036,
+ 0x00037, 0x00038, 0x00039, 0x00030, 0x00f34, 0x00000, 0x00f36, 0x00000,
+ 0x00f38, 0x00000, 0x00f3a, 0x00f3b, 0x00f3c, 0x00f3d, 0x00f3e, 0x00f3f,
+ 0x00f40, 0x00f41, 0x00f42, 0x00f43, 0x00f44, 0x00f45, 0x00f46, 0x00f47,
+ 0x00f48, 0x00f49, 0x00f4a, 0x00f4b, 0x00f4c, 0x00f4d, 0x00f4e, 0x00f4f,
+ 0x00f50, 0x00f51, 0x00f52, 0x00f53, 0x00f54, 0x00f55, 0x00f56, 0x00f57,
+ 0x00f58, 0x00f59, 0x00f5a, 0x00f5b, 0x00f5c, 0x00f5d, 0x00f5e, 0x00f5f,
+ 0x00f60, 0x00f61, 0x00f62, 0x00f63, 0x00f64, 0x00f65, 0x00f66, 0x00f67,
+ 0x00f68, 0x00f69, 0x00f62, 0x00f6b, 0x00f6c, 0x00f6d, 0x00f6e, 0x00f6f,
+ 0x00f70, 0x00f71, 0x00f72, 0x00f73, 0x00f74, 0x00f75, 0x00f76, 0x00f77,
+ 0x00f78, 0x00f79, 0x00f7a, 0x00f7b, 0x00f7c, 0x00f7d, 0x00000, 0x00000,
+ 0x00f80, 0x00f81, 0x00000, 0x00000, 0x00f84, 0x00f85, 0x00000, 0x00000,
+ 0x00f88, 0x00f89, 0x00f8a, 0x00f8b, 0x00f8c, 0x00f8d, 0x00f8e, 0x00f8f,
+ 0x00f90, 0x00f91, 0x00f92, 0x00f93, 0x00f94, 0x00f95, 0x00f96, 0x00f97,
+ 0x00f98, 0x00f99, 0x00f9a, 0x00f9b, 0x00f9c, 0x00f9d, 0x00f9e, 0x00f9f,
+ 0x00fa0, 0x00fa1, 0x00fa2, 0x00fa3, 0x00fa4, 0x00fa5, 0x00fa6, 0x00fa7,
+ 0x00fa8, 0x00fa9, 0x00faa, 0x00fab, 0x00fac, 0x00fad, 0x00fae, 0x00faf,
+ 0x00fb0, 0x00fb1, 0x00fb2, 0x00fb3, 0x00fb4, 0x00fb5, 0x00fb6, 0x00fb7,
+ 0x00fb8, 0x00fb9, 0x00fad, 0x00fb1, 0x00fb2, 0x00fbd, 0x00fbe, 0x00fbf,
+ 0x00fc0, 0x00fc1, 0x00fc2, 0x00fc3, 0x00fc4, 0x00fc5, 0x00000, 0x00fc7,
+ 0x00fc8, 0x00fc9, 0x00fca, 0x00fcb, 0x00fcc, 0x00fcd, 0x00fce, 0x00fcf,
+ 0x00fd0, 0x00fd1, 0x00fd2, 0x00fd3, 0x00fd4, 0x00fd5, 0x00fd6, 0x00fd7,
+ 0x00fd8, 0x00fd9, 0x00fda, 0x00fdb, 0x00fdc, 0x00fdd, 0x00fde, 0x00fdf,
+ 0x00fe0, 0x00fe1, 0x00fe2, 0x00fe3, 0x00fe4, 0x00fe5, 0x00fe6, 0x00fe7,
+ 0x00fe8, 0x00fe9, 0x00fea, 0x00feb, 0x00fec, 0x00fed, 0x00fee, 0x00fef,
+ 0x00ff0, 0x00ff1, 0x00ff2, 0x00ff3, 0x00ff4, 0x00ff5, 0x00ff6, 0x00ff7,
+ 0x00ff8, 0x00ff9, 0x00ffa, 0x00ffb, 0x00ffc, 0x00ffd, 0x00ffe, 0x00fff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_10[] = {
+ 0x01000, 0x01001, 0x01002, 0x01003, 0x01004, 0x01005, 0x01006, 0x01007,
+ 0x01008, 0x01009, 0x0100a, 0x0100b, 0x0100c, 0x0100d, 0x0100e, 0x0100f,
+ 0x01010, 0x01011, 0x01012, 0x01013, 0x01014, 0x01015, 0x01016, 0x01017,
+ 0x01018, 0x01019, 0x0101a, 0x0101b, 0x0101c, 0x0101d, 0x0101e, 0x0101f,
+ 0x01020, 0x01021, 0x01022, 0x01023, 0x01024, 0x01025, 0x01026, 0x01027,
+ 0x01028, 0x01029, 0x0102a, 0x0102b, 0x0102c, 0x0102d, 0x0102e, 0x0102f,
+ 0x01030, 0x01031, 0x01032, 0x01033, 0x01034, 0x01035, 0x00000, 0x00000,
+ 0x00000, 0x01039, 0x0103a, 0x0103b, 0x0103c, 0x0103d, 0x0103e, 0x0103f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0104a, 0x0104b, 0x0104c, 0x0104d, 0x0104e, 0x0104f,
+ 0x01050, 0x01051, 0x01052, 0x01053, 0x01054, 0x01055, 0x01056, 0x01057,
+ 0x01058, 0x01059, 0x0105a, 0x0105b, 0x0105c, 0x0105d, 0x0105e, 0x0105f,
+ 0x01060, 0x01061, 0x01062, 0x01063, 0x01064, 0x01065, 0x01066, 0x01067,
+ 0x01068, 0x01069, 0x0106a, 0x0106b, 0x0106c, 0x0106d, 0x0106e, 0x0106f,
+ 0x01070, 0x01071, 0x01072, 0x01073, 0x01074, 0x01075, 0x01076, 0x01077,
+ 0x01078, 0x01079, 0x0107a, 0x0107b, 0x0107c, 0x0107d, 0x0107e, 0x0107f,
+ 0x01080, 0x01081, 0x01082, 0x01083, 0x01084, 0x01085, 0x01086, 0x01087,
+ 0x01088, 0x01089, 0x0108a, 0x0108b, 0x0108c, 0x0108d, 0x0108e, 0x0108f,
+ 0x01090, 0x01091, 0x01092, 0x01093, 0x01094, 0x01095, 0x01096, 0x01097,
+ 0x01098, 0x01099, 0x0109a, 0x0109b, 0x0109c, 0x0109d, 0x0109e, 0x0109f,
+ 0x010a0, 0x010a1, 0x010a2, 0x010a3, 0x010a4, 0x010a5, 0x010a6, 0x010a7,
+ 0x010a8, 0x010a9, 0x010aa, 0x010ab, 0x010ac, 0x010ad, 0x010ae, 0x010af,
+ 0x010b0, 0x010b1, 0x010b2, 0x010b3, 0x010b4, 0x010b5, 0x010b6, 0x010b7,
+ 0x010b8, 0x010b9, 0x010ba, 0x010bb, 0x010bc, 0x010bd, 0x010be, 0x010bf,
+ 0x010c0, 0x010c1, 0x010c2, 0x010c3, 0x010c4, 0x010c5, 0x010c6, 0x010c7,
+ 0x010c8, 0x010c9, 0x010ca, 0x010cb, 0x010cc, 0x010cd, 0x010ce, 0x010cf,
+ 0x010a0, 0x010a1, 0x010a2, 0x010a3, 0x010a4, 0x010a5, 0x010a6, 0x010a7,
+ 0x010a8, 0x010a9, 0x010aa, 0x010ab, 0x010ac, 0x010ad, 0x010ae, 0x010af,
+ 0x010b0, 0x010b1, 0x010b2, 0x010b3, 0x010b4, 0x010b5, 0x010b6, 0x010b7,
+ 0x010b8, 0x010b9, 0x010ba, 0x010bb, 0x010bc, 0x010bd, 0x010be, 0x010bf,
+ 0x010c0, 0x010c1, 0x010c2, 0x010c3, 0x010c4, 0x010c5, 0x010f6, 0x010f7,
+ 0x010f8, 0x010f9, 0x010fa, 0x010fb, 0x010fc, 0x010fd, 0x010fe, 0x010ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_13[] = {
+ 0x01300, 0x01301, 0x01302, 0x01303, 0x01304, 0x01305, 0x01306, 0x01307,
+ 0x01308, 0x01309, 0x0130a, 0x0130b, 0x0130c, 0x0130d, 0x0130e, 0x0130f,
+ 0x01310, 0x01311, 0x01312, 0x01313, 0x01314, 0x01315, 0x01316, 0x01317,
+ 0x01318, 0x01319, 0x0131a, 0x0131b, 0x0131c, 0x0131d, 0x0131e, 0x0131f,
+ 0x01320, 0x01321, 0x01322, 0x01323, 0x01324, 0x01325, 0x01326, 0x01327,
+ 0x01328, 0x01329, 0x0132a, 0x0132b, 0x0132c, 0x0132d, 0x0132e, 0x0132f,
+ 0x01330, 0x01331, 0x01332, 0x01333, 0x01334, 0x01335, 0x01336, 0x01337,
+ 0x01338, 0x01339, 0x0133a, 0x0133b, 0x0133c, 0x0133d, 0x0133e, 0x0133f,
+ 0x01340, 0x01341, 0x01342, 0x01343, 0x01344, 0x01345, 0x01346, 0x01347,
+ 0x01348, 0x01349, 0x0134a, 0x0134b, 0x0134c, 0x0134d, 0x0134e, 0x0134f,
+ 0x01350, 0x01351, 0x01352, 0x01353, 0x01354, 0x01355, 0x01356, 0x01357,
+ 0x01358, 0x01359, 0x0135a, 0x0135b, 0x0135c, 0x0135d, 0x0135e, 0x0135f,
+ 0x01360, 0x01361, 0x01362, 0x01363, 0x01364, 0x01365, 0x01366, 0x01367,
+ 0x01368, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x01372, 0x01373, 0x01374, 0x01375, 0x01376, 0x01377,
+ 0x01378, 0x01379, 0x0137a, 0x0137b, 0x0137c, 0x0137d, 0x0137e, 0x0137f,
+ 0x01380, 0x01381, 0x01382, 0x01383, 0x01384, 0x01385, 0x01386, 0x01387,
+ 0x01388, 0x01389, 0x0138a, 0x0138b, 0x0138c, 0x0138d, 0x0138e, 0x0138f,
+ 0x01390, 0x01391, 0x01392, 0x01393, 0x01394, 0x01395, 0x01396, 0x01397,
+ 0x01398, 0x01399, 0x0139a, 0x0139b, 0x0139c, 0x0139d, 0x0139e, 0x0139f,
+ 0x013a0, 0x013a1, 0x013a2, 0x013a3, 0x013a4, 0x013a5, 0x013a6, 0x013a7,
+ 0x013a8, 0x013a9, 0x013aa, 0x013ab, 0x013ac, 0x013ad, 0x013ae, 0x013af,
+ 0x013b0, 0x013b1, 0x013b2, 0x013b3, 0x013b4, 0x013b5, 0x013b6, 0x013b7,
+ 0x013b8, 0x013b9, 0x013ba, 0x013bb, 0x013bc, 0x013bd, 0x013be, 0x013bf,
+ 0x013c0, 0x013c1, 0x013c2, 0x013c3, 0x013c4, 0x013c5, 0x013c6, 0x013c7,
+ 0x013c8, 0x013c9, 0x013ca, 0x013cb, 0x013cc, 0x013cd, 0x013ce, 0x013cf,
+ 0x013d0, 0x013d1, 0x013d2, 0x013d3, 0x013d4, 0x013d5, 0x013d6, 0x013d7,
+ 0x013d8, 0x013d9, 0x013da, 0x013db, 0x013dc, 0x013dd, 0x013de, 0x013df,
+ 0x013e0, 0x013e1, 0x013e2, 0x013e3, 0x013e4, 0x013e5, 0x013e6, 0x013e7,
+ 0x013e8, 0x013e9, 0x013ea, 0x013eb, 0x013ec, 0x013ed, 0x013ee, 0x013ef,
+ 0x013f0, 0x013f1, 0x013f2, 0x013f3, 0x013f4, 0x013f5, 0x013f6, 0x013f7,
+ 0x013f8, 0x013f9, 0x013fa, 0x013fb, 0x013fc, 0x013fd, 0x013fe, 0x013ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_16[] = {
+ 0x01600, 0x01601, 0x01602, 0x01603, 0x01604, 0x01605, 0x01606, 0x01607,
+ 0x01608, 0x01609, 0x0160a, 0x0160b, 0x0160c, 0x0160d, 0x0160e, 0x0160f,
+ 0x01610, 0x01611, 0x01612, 0x01613, 0x01614, 0x01615, 0x01616, 0x01617,
+ 0x01618, 0x01619, 0x0161a, 0x0161b, 0x0161c, 0x0161d, 0x0161e, 0x0161f,
+ 0x01620, 0x01621, 0x01622, 0x01623, 0x01624, 0x01625, 0x01626, 0x01627,
+ 0x01628, 0x01629, 0x0162a, 0x0162b, 0x0162c, 0x0162d, 0x0162e, 0x0162f,
+ 0x01630, 0x01631, 0x01632, 0x01633, 0x01634, 0x01635, 0x01636, 0x01637,
+ 0x01638, 0x01639, 0x0163a, 0x0163b, 0x0163c, 0x0163d, 0x0163e, 0x0163f,
+ 0x01640, 0x01641, 0x01642, 0x01643, 0x01644, 0x01645, 0x01646, 0x01647,
+ 0x01648, 0x01649, 0x0164a, 0x0164b, 0x0164c, 0x0164d, 0x0164e, 0x0164f,
+ 0x01650, 0x01651, 0x01652, 0x01653, 0x01654, 0x01655, 0x01656, 0x01657,
+ 0x01658, 0x01659, 0x0165a, 0x0165b, 0x0165c, 0x0165d, 0x0165e, 0x0165f,
+ 0x01660, 0x01661, 0x01662, 0x01663, 0x01664, 0x01665, 0x01666, 0x01667,
+ 0x01668, 0x01669, 0x0166a, 0x0166b, 0x0166c, 0x0166d, 0x0166e, 0x0166f,
+ 0x01670, 0x01671, 0x01672, 0x01673, 0x01674, 0x01675, 0x01676, 0x01677,
+ 0x01678, 0x01679, 0x0167a, 0x0167b, 0x0167c, 0x0167d, 0x0167e, 0x0167f,
+ 0x01680, 0x01681, 0x01682, 0x01683, 0x01684, 0x01685, 0x01686, 0x01687,
+ 0x01688, 0x01689, 0x0168a, 0x0168b, 0x0168c, 0x0168d, 0x0168e, 0x0168f,
+ 0x01690, 0x01691, 0x01692, 0x01693, 0x01694, 0x01695, 0x01696, 0x01697,
+ 0x01698, 0x01699, 0x0169a, 0x0169b, 0x0169c, 0x0169d, 0x0169e, 0x0169f,
+ 0x016a0, 0x016a0, 0x016a2, 0x016a3, 0x016a2, 0x016a2, 0x016a6, 0x016a6,
+ 0x016a8, 0x016a8, 0x016aa, 0x016ab, 0x016a8, 0x016a8, 0x016a8, 0x016af,
+ 0x016b0, 0x016b1, 0x016b2, 0x016b2, 0x016b2, 0x016b2, 0x016b2, 0x016b7,
+ 0x016b8, 0x016b9, 0x016ba, 0x016ba, 0x016ba, 0x016ba, 0x016be, 0x016be,
+ 0x016be, 0x016c1, 0x016c1, 0x016c3, 0x016c3, 0x016c5, 0x016c5, 0x016c7,
+ 0x016c8, 0x016c9, 0x016ca, 0x016ca, 0x016ca, 0x016ca, 0x016ca, 0x016cf,
+ 0x016cf, 0x016cf, 0x016d2, 0x016d2, 0x016d2, 0x016c8, 0x016d6, 0x016d7,
+ 0x016d7, 0x016d7, 0x016da, 0x016da, 0x016dc, 0x016dc, 0x016de, 0x016df,
+ 0x016e0, 0x016e1, 0x016e2, 0x016e3, 0x016e4, 0x016e5, 0x016e6, 0x016e6,
+ 0x016e6, 0x016b9, 0x016ca, 0x016eb, 0x016ec, 0x016ed, 0x016ee, 0x016ef,
+ 0x016f0, 0x016f1, 0x016f2, 0x016f3, 0x016f4, 0x016f5, 0x016f6, 0x016f7,
+ 0x016f8, 0x016f9, 0x016fa, 0x016fb, 0x016fc, 0x016fd, 0x016fe, 0x016ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_17[] = {
+ 0x01700, 0x01701, 0x01702, 0x01703, 0x01704, 0x01705, 0x01706, 0x01707,
+ 0x01708, 0x01709, 0x0170a, 0x0170b, 0x0170c, 0x0170d, 0x0170e, 0x0170f,
+ 0x01710, 0x01711, 0x01712, 0x01713, 0x01714, 0x01715, 0x01716, 0x01717,
+ 0x01718, 0x01719, 0x0171a, 0x0171b, 0x0171c, 0x0171d, 0x0171e, 0x0171f,
+ 0x01720, 0x01721, 0x01722, 0x01723, 0x01724, 0x01725, 0x01726, 0x01727,
+ 0x01728, 0x01729, 0x0172a, 0x0172b, 0x0172c, 0x0172d, 0x0172e, 0x0172f,
+ 0x01730, 0x01731, 0x01732, 0x01733, 0x01734, 0x01735, 0x01736, 0x01737,
+ 0x01738, 0x01739, 0x0173a, 0x0173b, 0x0173c, 0x0173d, 0x0173e, 0x0173f,
+ 0x01740, 0x01741, 0x01742, 0x01743, 0x01744, 0x01745, 0x01746, 0x01747,
+ 0x01748, 0x01749, 0x0174a, 0x0174b, 0x0174c, 0x0174d, 0x0174e, 0x0174f,
+ 0x01750, 0x01751, 0x01752, 0x01753, 0x01754, 0x01755, 0x01756, 0x01757,
+ 0x01758, 0x01759, 0x0175a, 0x0175b, 0x0175c, 0x0175d, 0x0175e, 0x0175f,
+ 0x01760, 0x01761, 0x01762, 0x01763, 0x01764, 0x01765, 0x01766, 0x01767,
+ 0x01768, 0x01769, 0x0176a, 0x0176b, 0x0176c, 0x0176d, 0x0176e, 0x0176f,
+ 0x01770, 0x01771, 0x01772, 0x01773, 0x01774, 0x01775, 0x01776, 0x01777,
+ 0x01778, 0x01779, 0x0177a, 0x0177b, 0x0177c, 0x0177d, 0x0177e, 0x0177f,
+ 0x01780, 0x01781, 0x01782, 0x01783, 0x01784, 0x01785, 0x01786, 0x01787,
+ 0x01788, 0x01789, 0x0178a, 0x0178b, 0x0178c, 0x0178d, 0x0178e, 0x0178f,
+ 0x01790, 0x01791, 0x01792, 0x01793, 0x01794, 0x01795, 0x01796, 0x01797,
+ 0x01798, 0x01799, 0x0179a, 0x0179b, 0x0179c, 0x0179d, 0x0179e, 0x0179f,
+ 0x017a0, 0x017a1, 0x017a2, 0x017a3, 0x017a4, 0x017a5, 0x017a6, 0x017a7,
+ 0x017a8, 0x017a9, 0x017aa, 0x017ab, 0x017ac, 0x017ad, 0x017ae, 0x017af,
+ 0x017b0, 0x017b1, 0x017b2, 0x017b3, 0x017b4, 0x017b5, 0x017b6, 0x017b7,
+ 0x017b8, 0x017b9, 0x017ba, 0x017bb, 0x017bc, 0x017bd, 0x017be, 0x017bf,
+ 0x017c0, 0x017c1, 0x017c2, 0x017c3, 0x017c4, 0x017c5, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x017d2, 0x00000, 0x017d4, 0x017d5, 0x017d6, 0x017d7,
+ 0x017d8, 0x017d9, 0x017da, 0x017db, 0x017dc, 0x00000, 0x017de, 0x017df,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x017ea, 0x017eb, 0x017ec, 0x017ed, 0x017ee, 0x017ef,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x017fa, 0x017fb, 0x017fc, 0x017fd, 0x017fe, 0x017ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_18[] = {
+ 0x01800, 0x01801, 0x01802, 0x01803, 0x01804, 0x01805, 0x01806, 0x01807,
+ 0x01808, 0x01809, 0x0180a, 0x00000, 0x00000, 0x00000, 0x00000, 0x0180f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0181a, 0x0181b, 0x0181c, 0x0181d, 0x0181e, 0x0181f,
+ 0x01820, 0x01821, 0x01822, 0x01823, 0x01824, 0x01825, 0x01826, 0x01827,
+ 0x01828, 0x01829, 0x0182a, 0x0182b, 0x0182c, 0x0182d, 0x0182e, 0x0182f,
+ 0x01830, 0x01831, 0x01832, 0x01833, 0x01834, 0x01835, 0x01836, 0x01837,
+ 0x01838, 0x01839, 0x0183a, 0x0183b, 0x0183c, 0x0183d, 0x0183e, 0x0183f,
+ 0x01840, 0x01841, 0x01842, 0x01843, 0x01844, 0x01845, 0x01846, 0x01847,
+ 0x01848, 0x01849, 0x0184a, 0x0184b, 0x0184c, 0x0184d, 0x0184e, 0x0184f,
+ 0x01850, 0x01851, 0x01852, 0x01853, 0x01854, 0x01855, 0x01856, 0x01857,
+ 0x01858, 0x01859, 0x0185a, 0x0185b, 0x0185c, 0x0185d, 0x0185e, 0x0185f,
+ 0x01860, 0x01861, 0x01862, 0x01863, 0x01864, 0x01865, 0x01866, 0x01867,
+ 0x01868, 0x01869, 0x0186a, 0x0186b, 0x0186c, 0x0186d, 0x0186e, 0x0186f,
+ 0x01870, 0x01871, 0x01872, 0x01873, 0x01874, 0x01875, 0x01876, 0x01877,
+ 0x01878, 0x01879, 0x0187a, 0x0187b, 0x0187c, 0x0187d, 0x0187e, 0x0187f,
+ 0x01880, 0x01881, 0x01882, 0x01883, 0x01884, 0x01885, 0x01886, 0x01887,
+ 0x01888, 0x01889, 0x0188a, 0x0188b, 0x0188c, 0x0188d, 0x0188e, 0x0188f,
+ 0x01890, 0x01891, 0x01892, 0x01893, 0x01894, 0x01895, 0x01896, 0x01897,
+ 0x01898, 0x01899, 0x0189a, 0x0189b, 0x0189c, 0x0189d, 0x0189e, 0x0189f,
+ 0x018a0, 0x018a1, 0x018a2, 0x018a3, 0x018a4, 0x018a5, 0x018a6, 0x018a7,
+ 0x018a8, 0x018a9, 0x018aa, 0x018ab, 0x018ac, 0x018ad, 0x018ae, 0x018af,
+ 0x018b0, 0x018b1, 0x018b2, 0x018b3, 0x018b4, 0x018b5, 0x018b6, 0x018b7,
+ 0x018b8, 0x018b9, 0x018ba, 0x018bb, 0x018bc, 0x018bd, 0x018be, 0x018bf,
+ 0x018c0, 0x018c1, 0x018c2, 0x018c3, 0x018c4, 0x018c5, 0x018c6, 0x018c7,
+ 0x018c8, 0x018c9, 0x018ca, 0x018cb, 0x018cc, 0x018cd, 0x018ce, 0x018cf,
+ 0x018d0, 0x018d1, 0x018d2, 0x018d3, 0x018d4, 0x018d5, 0x018d6, 0x018d7,
+ 0x018d8, 0x018d9, 0x018da, 0x018db, 0x018dc, 0x018dd, 0x018de, 0x018df,
+ 0x018e0, 0x018e1, 0x018e2, 0x018e3, 0x018e4, 0x018e5, 0x018e6, 0x018e7,
+ 0x018e8, 0x018e9, 0x018ea, 0x018eb, 0x018ec, 0x018ed, 0x018ee, 0x018ef,
+ 0x018f0, 0x018f1, 0x018f2, 0x018f3, 0x018f4, 0x018f5, 0x018f6, 0x018f7,
+ 0x018f8, 0x018f9, 0x018fa, 0x018fb, 0x018fc, 0x018fd, 0x018fe, 0x018ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_19[] = {
+ 0x01900, 0x01901, 0x01902, 0x01903, 0x01904, 0x01905, 0x01906, 0x01907,
+ 0x01908, 0x01909, 0x0190a, 0x0190b, 0x0190c, 0x0190d, 0x0190e, 0x0190f,
+ 0x01910, 0x01911, 0x01912, 0x01913, 0x01914, 0x01915, 0x01916, 0x01917,
+ 0x01918, 0x01919, 0x0191a, 0x0191b, 0x0191c, 0x0191d, 0x0191e, 0x0191f,
+ 0x01920, 0x01921, 0x01922, 0x01923, 0x01924, 0x01925, 0x01926, 0x01927,
+ 0x01928, 0x01929, 0x0192a, 0x0192b, 0x0192c, 0x0192d, 0x0192e, 0x0192f,
+ 0x01930, 0x01931, 0x01932, 0x01933, 0x01934, 0x01935, 0x01936, 0x01937,
+ 0x01938, 0x00000, 0x00000, 0x00000, 0x0193c, 0x0193d, 0x0193e, 0x0193f,
+ 0x01940, 0x01941, 0x01942, 0x01943, 0x01944, 0x01945, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x01950, 0x01951, 0x01952, 0x01953, 0x01954, 0x01955, 0x01956, 0x01957,
+ 0x01958, 0x01959, 0x0195a, 0x0195b, 0x0195c, 0x0195d, 0x0195e, 0x0195f,
+ 0x01960, 0x01961, 0x01962, 0x01963, 0x01964, 0x01965, 0x01966, 0x01967,
+ 0x01968, 0x01969, 0x0196a, 0x0196b, 0x0196c, 0x0196d, 0x0196e, 0x0196f,
+ 0x01970, 0x01971, 0x01972, 0x01973, 0x01974, 0x01975, 0x01976, 0x01977,
+ 0x01978, 0x01979, 0x0197a, 0x0197b, 0x0197c, 0x0197d, 0x0197e, 0x0197f,
+ 0x01980, 0x01981, 0x01982, 0x01983, 0x01984, 0x01985, 0x01986, 0x01987,
+ 0x01988, 0x01989, 0x0198a, 0x0198b, 0x0198c, 0x0198d, 0x0198e, 0x0198f,
+ 0x01990, 0x01991, 0x01992, 0x01993, 0x01994, 0x01995, 0x01996, 0x01997,
+ 0x01998, 0x01999, 0x0199a, 0x0199b, 0x0199c, 0x0199d, 0x0199e, 0x0199f,
+ 0x019a0, 0x019a1, 0x019a2, 0x019a3, 0x019a4, 0x019a5, 0x019a6, 0x019a7,
+ 0x019a8, 0x019a9, 0x019aa, 0x019ab, 0x019ac, 0x019ad, 0x019ae, 0x019af,
+ 0x019b0, 0x019b1, 0x019b2, 0x019b3, 0x019b4, 0x019b5, 0x019b6, 0x019b7,
+ 0x019b8, 0x019b9, 0x019ba, 0x019bb, 0x019bc, 0x019bd, 0x019be, 0x019bf,
+ 0x019c0, 0x019c1, 0x019c2, 0x019c3, 0x019c4, 0x019c5, 0x019c6, 0x019c7,
+ 0x019c8, 0x019c9, 0x019ca, 0x019cb, 0x019cc, 0x019cd, 0x019ce, 0x019cf,
+ 0x019d0, 0x019d1, 0x019d2, 0x019d3, 0x019d4, 0x019d5, 0x019d6, 0x019d7,
+ 0x019d8, 0x019d9, 0x019da, 0x019db, 0x019dc, 0x019dd, 0x019de, 0x019df,
+ 0x019e0, 0x019e1, 0x019e2, 0x019e3, 0x019e4, 0x019e5, 0x019e6, 0x019e7,
+ 0x019e8, 0x019e9, 0x019ea, 0x019eb, 0x019ec, 0x019ed, 0x019ee, 0x019ef,
+ 0x019f0, 0x019f1, 0x019f2, 0x019f3, 0x019f4, 0x019f5, 0x019f6, 0x019f7,
+ 0x019f8, 0x019f9, 0x019fa, 0x019fb, 0x019fc, 0x019fd, 0x019fe, 0x019ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_1d[] = {
+ 0x01d00, 0x01d01, 0x01d02, 0x01d03, 0x01d04, 0x01d05, 0x01d06, 0x01d07,
+ 0x01d08, 0x01d09, 0x01d0a, 0x01d0b, 0x01d0c, 0x01d0d, 0x01d0e, 0x01d0f,
+ 0x01d10, 0x01d11, 0x01d12, 0x01d13, 0x01d14, 0x01d15, 0x01d16, 0x01d17,
+ 0x01d18, 0x01d19, 0x01d1a, 0x01d1b, 0x01d1c, 0x01d1d, 0x01d1e, 0x01d1f,
+ 0x01d20, 0x01d21, 0x01d22, 0x01d23, 0x01d24, 0x01d25, 0x01d26, 0x01d27,
+ 0x01d28, 0x01d29, 0x01d2a, 0x01d2b, 0x00041, 0x000c6, 0x00042, 0x01d2f,
+ 0x00044, 0x00045, 0x0018e, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b,
+ 0x0004c, 0x0004d, 0x0004e, 0x01d3b, 0x0004f, 0x00222, 0x00050, 0x00052,
+ 0x00054, 0x00055, 0x00057, 0x00041, 0x00250, 0x00251, 0x01d02, 0x00042,
+ 0x00044, 0x00045, 0x0018f, 0x00190, 0x01d08, 0x00047, 0x01d09, 0x0004b,
+ 0x0004d, 0x0014a, 0x0004f, 0x00186, 0x01d16, 0x01d17, 0x00050, 0x00054,
+ 0x00055, 0x01d1d, 0x0019c, 0x00056, 0x01d25, 0x00392, 0x00393, 0x00394,
+ 0x003a6, 0x003a7, 0x00049, 0x00052, 0x00055, 0x00056, 0x00392, 0x00393,
+ 0x003a1, 0x003a6, 0x003a7, 0x01d6b, 0x01d6c, 0x01d6d, 0x01d6e, 0x01d6f,
+ 0x01d70, 0x01d71, 0x01d72, 0x01d73, 0x01d74, 0x01d75, 0x01d76, 0x01d77,
+ 0x01d78, 0x01d79, 0x01d7a, 0x01d7b, 0x01d7c, 0x01d7d, 0x01d7e, 0x01d7f,
+ 0x01d80, 0x01d81, 0x01d82, 0x01d83, 0x01d84, 0x01d85, 0x01d86, 0x01d87,
+ 0x01d88, 0x01d89, 0x01d8a, 0x01d8b, 0x01d8c, 0x01d8d, 0x01d8e, 0x01d8f,
+ 0x01d90, 0x01d91, 0x01d92, 0x01d93, 0x01d94, 0x01d95, 0x01d96, 0x01d97,
+ 0x01d98, 0x01d99, 0x01d9a, 0x01d9b, 0x01d9c, 0x01d9d, 0x01d9e, 0x01d9f,
+ 0x01da0, 0x01da1, 0x01da2, 0x01da3, 0x01da4, 0x01da5, 0x01da6, 0x01da7,
+ 0x01da8, 0x01da9, 0x01daa, 0x01dab, 0x01dac, 0x01dad, 0x01dae, 0x01daf,
+ 0x01db0, 0x01db1, 0x01db2, 0x01db3, 0x01db4, 0x01db5, 0x01db6, 0x01db7,
+ 0x01db8, 0x01db9, 0x01dba, 0x01dbb, 0x01dbc, 0x01dbd, 0x01dbe, 0x01dbf,
+ 0x01dc0, 0x01dc1, 0x01dc2, 0x01dc3, 0x01dc4, 0x01dc5, 0x01dc6, 0x01dc7,
+ 0x01dc8, 0x01dc9, 0x01dca, 0x01dcb, 0x01dcc, 0x01dcd, 0x01dce, 0x01dcf,
+ 0x01dd0, 0x01dd1, 0x01dd2, 0x01dd3, 0x01dd4, 0x01dd5, 0x01dd6, 0x01dd7,
+ 0x01dd8, 0x01dd9, 0x01dda, 0x01ddb, 0x01ddc, 0x01ddd, 0x01dde, 0x01ddf,
+ 0x01de0, 0x01de1, 0x01de2, 0x01de3, 0x01de4, 0x01de5, 0x01de6, 0x01de7,
+ 0x01de8, 0x01de9, 0x01dea, 0x01deb, 0x01dec, 0x01ded, 0x01dee, 0x01def,
+ 0x01df0, 0x01df1, 0x01df2, 0x01df3, 0x01df4, 0x01df5, 0x01df6, 0x01df7,
+ 0x01df8, 0x01df9, 0x01dfa, 0x01dfb, 0x01dfc, 0x01dfd, 0x01dfe, 0x01dff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_1e[] = {
+ 0x00041, 0x00041, 0x00042, 0x00042, 0x00042, 0x00042, 0x00042, 0x00042,
+ 0x00043, 0x00043, 0x00044, 0x00044, 0x00044, 0x00044, 0x00044, 0x00044,
+ 0x00044, 0x00044, 0x00044, 0x00044, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00046, 0x00046,
+ 0x00047, 0x00047, 0x00048, 0x00048, 0x00048, 0x00048, 0x00048, 0x00048,
+ 0x00048, 0x00048, 0x00048, 0x00048, 0x00049, 0x00049, 0x00049, 0x00049,
+ 0x0004b, 0x0004b, 0x0004b, 0x0004b, 0x0004b, 0x0004b, 0x0004c, 0x0004c,
+ 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004d, 0x0004d,
+ 0x0004d, 0x0004d, 0x0004d, 0x0004d, 0x0004e, 0x0004e, 0x0004e, 0x0004e,
+ 0x0004e, 0x0004e, 0x0004e, 0x0004e, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x00050, 0x00050, 0x00050, 0x00050,
+ 0x00052, 0x00052, 0x00052, 0x00052, 0x00052, 0x00052, 0x00052, 0x00052,
+ 0x00053, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053,
+ 0x00053, 0x00053, 0x00054, 0x00054, 0x00054, 0x00054, 0x00054, 0x00054,
+ 0x00054, 0x00054, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00056, 0x00056, 0x00056, 0x00056,
+ 0x00057, 0x00057, 0x00057, 0x00057, 0x00057, 0x00057, 0x00057, 0x00057,
+ 0x00057, 0x00057, 0x00058, 0x00058, 0x00058, 0x00058, 0x00059, 0x00059,
+ 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x00048, 0x00054,
+ 0x00057, 0x00059, 0x01e9a, 0x00053, 0x01e9c, 0x01e9d, 0x01e9e, 0x01e9f,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00049, 0x00049, 0x00049, 0x00049, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00059, 0x00059, 0x00059, 0x00059, 0x00059, 0x00059,
+ 0x00059, 0x00059, 0x01efa, 0x01efb, 0x01efc, 0x01efd, 0x01efe, 0x01eff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_1f[] = {
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391,
+ 0x00395, 0x00395, 0x00395, 0x00395, 0x00395, 0x00395, 0x01f16, 0x01f17,
+ 0x00395, 0x00395, 0x00395, 0x00395, 0x00395, 0x00395, 0x01f1e, 0x01f1f,
+ 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397,
+ 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397,
+ 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399,
+ 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399,
+ 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x01f46, 0x01f47,
+ 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x01f4e, 0x01f4f,
+ 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a5,
+ 0x01f58, 0x003a5, 0x01f5a, 0x003a5, 0x01f5c, 0x003a5, 0x01f5e, 0x003a5,
+ 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9,
+ 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9,
+ 0x00391, 0x00391, 0x00395, 0x00395, 0x00397, 0x00397, 0x00399, 0x00399,
+ 0x0039f, 0x0039f, 0x003a5, 0x003a5, 0x003a9, 0x003a9, 0x01f7e, 0x01f7f,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391,
+ 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397,
+ 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397,
+ 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9,
+ 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x01fb5, 0x00391, 0x00391,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x01fbd, 0x00399, 0x01fbd,
+ 0x01fc0, 0x000a8, 0x00397, 0x00397, 0x00397, 0x01fc5, 0x00397, 0x00397,
+ 0x00395, 0x00395, 0x00397, 0x00397, 0x00397, 0x01fbd, 0x01fbd, 0x01fbd,
+ 0x00399, 0x00399, 0x00399, 0x00399, 0x01fd4, 0x01fd5, 0x00399, 0x00399,
+ 0x00399, 0x00399, 0x00399, 0x00399, 0x01fdc, 0x01fdd, 0x01fdd, 0x01fdd,
+ 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a1, 0x003a1, 0x003a5, 0x003a5,
+ 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a1, 0x000a8, 0x000a8, 0x00060,
+ 0x01ff0, 0x01ff1, 0x003a9, 0x003a9, 0x003a9, 0x01ff5, 0x003a9, 0x003a9,
+ 0x0039f, 0x0039f, 0x003a9, 0x003a9, 0x003a9, 0x000b4, 0x01fdd, 0x01fff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_20[] = {
+ 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020,
+ 0x00020, 0x00020, 0x00020, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x02010, 0x02010, 0x02012, 0x02013, 0x02014, 0x02015, 0x02016, 0x02017,
+ 0x02018, 0x02019, 0x0201a, 0x0201b, 0x0201c, 0x0201d, 0x0201e, 0x0201f,
+ 0x02020, 0x02021, 0x02022, 0x02023, 0x0002e, 0x02025, 0x02026, 0x02027,
+ 0x02028, 0x02029, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00020,
+ 0x02030, 0x02031, 0x02032, 0x02033, 0x02034, 0x02035, 0x02036, 0x02037,
+ 0x02038, 0x02039, 0x0203a, 0x0203b, 0x0203c, 0x0203d, 0x0203e, 0x0203f,
+ 0x02040, 0x02041, 0x02042, 0x02043, 0x02044, 0x02045, 0x02046, 0x02047,
+ 0x02048, 0x02049, 0x0204a, 0x0204b, 0x0204c, 0x0204d, 0x0204e, 0x0204f,
+ 0x02050, 0x02051, 0x02052, 0x02053, 0x02054, 0x02055, 0x02056, 0x02057,
+ 0x02058, 0x02059, 0x0205a, 0x0205b, 0x0205c, 0x0205d, 0x0205e, 0x00020,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x02064, 0x02065, 0x02066, 0x02067,
+ 0x02068, 0x02069, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00030, 0x00049, 0x02072, 0x02073, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0002b, 0x0207b, 0x0003d, 0x00028, 0x00029, 0x0004e,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0002b, 0x0207b, 0x0003d, 0x00028, 0x00029, 0x0208f,
+ 0x02090, 0x02091, 0x02092, 0x02093, 0x02094, 0x02095, 0x02096, 0x02097,
+ 0x02098, 0x02099, 0x0209a, 0x0209b, 0x0209c, 0x0209d, 0x0209e, 0x0209f,
+ 0x020a0, 0x020a1, 0x020a2, 0x020a3, 0x020a4, 0x020a5, 0x020a6, 0x020a7,
+ 0x020a8, 0x020a9, 0x020aa, 0x020ab, 0x020ac, 0x020ad, 0x020ae, 0x020af,
+ 0x020b0, 0x020b1, 0x020b2, 0x020b3, 0x020b4, 0x020b5, 0x020b6, 0x020b7,
+ 0x020b8, 0x020b9, 0x020ba, 0x020bb, 0x020bc, 0x020bd, 0x020be, 0x020bf,
+ 0x020c0, 0x020c1, 0x020c2, 0x020c3, 0x020c4, 0x020c5, 0x020c6, 0x020c7,
+ 0x020c8, 0x020c9, 0x020ca, 0x020cb, 0x020cc, 0x020cd, 0x020ce, 0x020cf,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x020eb, 0x020ec, 0x020ed, 0x020ee, 0x020ef,
+ 0x020f0, 0x020f1, 0x020f2, 0x020f3, 0x020f4, 0x020f5, 0x020f6, 0x020f7,
+ 0x020f8, 0x020f9, 0x020fa, 0x020fb, 0x020fc, 0x020fd, 0x020fe, 0x020ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_21[] = {
+ 0x02100, 0x02101, 0x00043, 0x02103, 0x02104, 0x02105, 0x02106, 0x00190,
+ 0x02108, 0x02109, 0x00047, 0x00048, 0x00048, 0x00048, 0x00048, 0x00126,
+ 0x00049, 0x00049, 0x0004c, 0x0004c, 0x02114, 0x0004e, 0x02116, 0x02117,
+ 0x02118, 0x00050, 0x00051, 0x00052, 0x00052, 0x00052, 0x0211e, 0x0211f,
+ 0x02120, 0x02121, 0x02122, 0x02123, 0x0005a, 0x02125, 0x003a9, 0x02127,
+ 0x0005a, 0x02129, 0x0004b, 0x00041, 0x00042, 0x00043, 0x0212e, 0x00045,
+ 0x00045, 0x00046, 0x02132, 0x0004d, 0x0004f, 0x005d0, 0x005d1, 0x005d2,
+ 0x005d3, 0x00049, 0x0213a, 0x0213b, 0x0213c, 0x00393, 0x00393, 0x003a0,
+ 0x02140, 0x02141, 0x02142, 0x02143, 0x02144, 0x00044, 0x00044, 0x00045,
+ 0x00049, 0x0004a, 0x0214a, 0x0214b, 0x0214c, 0x0214d, 0x0214e, 0x0214f,
+ 0x02150, 0x02151, 0x02152, 0x02153, 0x02154, 0x02155, 0x02156, 0x02157,
+ 0x02158, 0x02159, 0x0215a, 0x0215b, 0x0215c, 0x0215d, 0x0215e, 0x0215f,
+ 0x00049, 0x02161, 0x02162, 0x02163, 0x00056, 0x02165, 0x02166, 0x02167,
+ 0x02168, 0x00058, 0x0216a, 0x0216b, 0x0004c, 0x00043, 0x00044, 0x0004d,
+ 0x00049, 0x02161, 0x02162, 0x02163, 0x00056, 0x02165, 0x02166, 0x02167,
+ 0x02168, 0x00058, 0x0216a, 0x0216b, 0x0004c, 0x00043, 0x00044, 0x0004d,
+ 0x02180, 0x02181, 0x02182, 0x02183, 0x02184, 0x02185, 0x02186, 0x02187,
+ 0x02188, 0x02189, 0x0218a, 0x0218b, 0x0218c, 0x0218d, 0x0218e, 0x0218f,
+ 0x02190, 0x02191, 0x02192, 0x02193, 0x02194, 0x02195, 0x02196, 0x02197,
+ 0x02198, 0x02199, 0x02190, 0x02192, 0x0219c, 0x0219d, 0x0219e, 0x0219f,
+ 0x021a0, 0x021a1, 0x021a2, 0x021a3, 0x021a4, 0x021a5, 0x021a6, 0x021a7,
+ 0x021a8, 0x021a9, 0x021aa, 0x021ab, 0x021ac, 0x021ad, 0x02194, 0x021af,
+ 0x021b0, 0x021b1, 0x021b2, 0x021b3, 0x021b4, 0x021b5, 0x021b6, 0x021b7,
+ 0x021b8, 0x021b9, 0x021ba, 0x021bb, 0x021bc, 0x021bd, 0x021be, 0x021bf,
+ 0x021c0, 0x021c1, 0x021c2, 0x021c3, 0x021c4, 0x021c5, 0x021c6, 0x021c7,
+ 0x021c8, 0x021c9, 0x021ca, 0x021cb, 0x021cc, 0x021cd, 0x021ce, 0x021cf,
+ 0x021cd, 0x021d1, 0x021cf, 0x021d3, 0x021ce, 0x021d5, 0x021d6, 0x021d7,
+ 0x021d8, 0x021d9, 0x021da, 0x021db, 0x021dc, 0x021dd, 0x021de, 0x021df,
+ 0x021e0, 0x021e1, 0x021e2, 0x021e3, 0x021e4, 0x021e5, 0x021e6, 0x021e7,
+ 0x021e8, 0x021e9, 0x021ea, 0x021eb, 0x021ec, 0x021ed, 0x021ee, 0x021ef,
+ 0x021f0, 0x021f1, 0x021f2, 0x021f3, 0x021f4, 0x021f5, 0x021f6, 0x021f7,
+ 0x021f8, 0x021f9, 0x021fa, 0x021fb, 0x021fc, 0x021fd, 0x021fe, 0x021ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_22[] = {
+ 0x02200, 0x02201, 0x02202, 0x02203, 0x02203, 0x02205, 0x02206, 0x02207,
+ 0x02208, 0x02208, 0x0220a, 0x0220b, 0x0220b, 0x0220d, 0x0220e, 0x0220f,
+ 0x02210, 0x02140, 0x0207b, 0x02213, 0x02214, 0x02215, 0x02216, 0x02217,
+ 0x02218, 0x02219, 0x0221a, 0x0221b, 0x0221c, 0x0221d, 0x0221e, 0x0221f,
+ 0x02220, 0x02221, 0x02222, 0x02223, 0x02223, 0x02225, 0x02225, 0x02227,
+ 0x02228, 0x02229, 0x0222a, 0x0222b, 0x0222c, 0x0222d, 0x0222e, 0x0222f,
+ 0x02230, 0x02231, 0x02232, 0x02233, 0x02234, 0x02235, 0x02236, 0x02237,
+ 0x02238, 0x02239, 0x0223a, 0x0223b, 0x0223c, 0x0223d, 0x0223e, 0x0223f,
+ 0x02240, 0x0223c, 0x02242, 0x02243, 0x02243, 0x02245, 0x02246, 0x02245,
+ 0x02248, 0x02248, 0x0224a, 0x0224b, 0x0224c, 0x0224d, 0x0224e, 0x0224f,
+ 0x02250, 0x02251, 0x02252, 0x02253, 0x02254, 0x02255, 0x02256, 0x02257,
+ 0x02258, 0x02259, 0x0225a, 0x0225b, 0x0225c, 0x0225d, 0x0225e, 0x0225f,
+ 0x0003d, 0x02261, 0x02261, 0x02263, 0x02264, 0x02265, 0x02266, 0x02267,
+ 0x02268, 0x02269, 0x0226a, 0x0226b, 0x0226c, 0x0224d, 0x0003c, 0x0003e,
+ 0x02264, 0x02265, 0x02272, 0x02273, 0x02272, 0x02273, 0x02276, 0x02277,
+ 0x02276, 0x02277, 0x0227a, 0x0227b, 0x0227c, 0x0227d, 0x0227e, 0x0227f,
+ 0x0227a, 0x0227b, 0x02282, 0x02283, 0x02282, 0x02283, 0x02286, 0x02287,
+ 0x02286, 0x02287, 0x0228a, 0x0228b, 0x0228c, 0x0228d, 0x0228e, 0x0228f,
+ 0x02290, 0x02291, 0x02292, 0x02293, 0x02294, 0x02295, 0x02296, 0x02297,
+ 0x02298, 0x02299, 0x0229a, 0x0229b, 0x0229c, 0x0229d, 0x0229e, 0x0229f,
+ 0x022a0, 0x022a1, 0x022a2, 0x022a3, 0x022a4, 0x022a5, 0x022a6, 0x022a7,
+ 0x022a8, 0x022a9, 0x022aa, 0x022ab, 0x022a2, 0x022a8, 0x022a9, 0x022ab,
+ 0x022b0, 0x022b1, 0x022b2, 0x022b3, 0x022b4, 0x022b5, 0x022b6, 0x022b7,
+ 0x022b8, 0x022b9, 0x022ba, 0x022bb, 0x022bc, 0x022bd, 0x022be, 0x022bf,
+ 0x022c0, 0x022c1, 0x022c2, 0x022c3, 0x022c4, 0x022c5, 0x022c6, 0x022c7,
+ 0x022c8, 0x022c9, 0x022ca, 0x022cb, 0x022cc, 0x022cd, 0x022ce, 0x022cf,
+ 0x022d0, 0x022d1, 0x022d2, 0x022d3, 0x022d4, 0x022d5, 0x022d6, 0x022d7,
+ 0x022d8, 0x022d9, 0x022da, 0x022db, 0x022dc, 0x022dd, 0x022de, 0x022df,
+ 0x0227c, 0x0227d, 0x02291, 0x02292, 0x022e4, 0x022e5, 0x022e6, 0x022e7,
+ 0x022e8, 0x022e9, 0x022b2, 0x022b3, 0x022b4, 0x022b5, 0x022ee, 0x022ef,
+ 0x022f0, 0x022f1, 0x022f2, 0x022f3, 0x022f4, 0x022f5, 0x022f6, 0x022f7,
+ 0x022f8, 0x022f9, 0x022fa, 0x022fb, 0x022fc, 0x022fd, 0x022fe, 0x022ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_24[] = {
+ 0x02400, 0x02401, 0x02402, 0x02403, 0x02404, 0x02405, 0x02406, 0x02407,
+ 0x02408, 0x02409, 0x0240a, 0x0240b, 0x0240c, 0x0240d, 0x0240e, 0x0240f,
+ 0x02410, 0x02411, 0x02412, 0x02413, 0x02414, 0x02415, 0x02416, 0x02417,
+ 0x02418, 0x02419, 0x0241a, 0x0241b, 0x0241c, 0x0241d, 0x0241e, 0x0241f,
+ 0x02420, 0x02421, 0x02422, 0x02423, 0x02424, 0x02425, 0x02426, 0x02427,
+ 0x02428, 0x02429, 0x0242a, 0x0242b, 0x0242c, 0x0242d, 0x0242e, 0x0242f,
+ 0x02430, 0x02431, 0x02432, 0x02433, 0x02434, 0x02435, 0x02436, 0x02437,
+ 0x02438, 0x02439, 0x0243a, 0x0243b, 0x0243c, 0x0243d, 0x0243e, 0x0243f,
+ 0x02440, 0x02441, 0x02442, 0x02443, 0x02444, 0x02445, 0x02446, 0x02447,
+ 0x02448, 0x02449, 0x0244a, 0x0244b, 0x0244c, 0x0244d, 0x0244e, 0x0244f,
+ 0x02450, 0x02451, 0x02452, 0x02453, 0x02454, 0x02455, 0x02456, 0x02457,
+ 0x02458, 0x02459, 0x0245a, 0x0245b, 0x0245c, 0x0245d, 0x0245e, 0x0245f,
+ 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038,
+ 0x00039, 0x02469, 0x0246a, 0x0246b, 0x0246c, 0x0246d, 0x0246e, 0x0246f,
+ 0x02470, 0x02471, 0x02472, 0x02473, 0x02474, 0x02475, 0x02476, 0x02477,
+ 0x02478, 0x02479, 0x0247a, 0x0247b, 0x0247c, 0x0247d, 0x0247e, 0x0247f,
+ 0x02480, 0x02481, 0x02482, 0x02483, 0x02484, 0x02485, 0x02486, 0x02487,
+ 0x02488, 0x02489, 0x0248a, 0x0248b, 0x0248c, 0x0248d, 0x0248e, 0x0248f,
+ 0x02490, 0x02491, 0x02492, 0x02493, 0x02494, 0x02495, 0x02496, 0x02497,
+ 0x02498, 0x02499, 0x0249a, 0x0249b, 0x0249c, 0x0249d, 0x0249e, 0x0249f,
+ 0x024a0, 0x024a1, 0x024a2, 0x024a3, 0x024a4, 0x024a5, 0x024a6, 0x024a7,
+ 0x024a8, 0x024a9, 0x024aa, 0x024ab, 0x024ac, 0x024ad, 0x024ae, 0x024af,
+ 0x024b0, 0x024b1, 0x024b2, 0x024b3, 0x024b4, 0x024b5, 0x00041, 0x00042,
+ 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a,
+ 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052,
+ 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a,
+ 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048,
+ 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050,
+ 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058,
+ 0x00059, 0x0005a, 0x00030, 0x0246a, 0x0246b, 0x0246c, 0x0246d, 0x0246e,
+ 0x0246f, 0x02470, 0x02471, 0x02472, 0x02473, 0x00031, 0x00032, 0x00033,
+ 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x02469, 0x00030
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_27[] = {
+ 0x02700, 0x02701, 0x02702, 0x02703, 0x02704, 0x02705, 0x02706, 0x02707,
+ 0x02708, 0x02709, 0x0270a, 0x0270b, 0x0270c, 0x0270d, 0x0270e, 0x0270f,
+ 0x02710, 0x02711, 0x02712, 0x02713, 0x02714, 0x02715, 0x02716, 0x02717,
+ 0x02718, 0x02719, 0x0271a, 0x0271b, 0x0271c, 0x0271d, 0x0271e, 0x0271f,
+ 0x02720, 0x02721, 0x02722, 0x02723, 0x02724, 0x02725, 0x02726, 0x02727,
+ 0x02728, 0x02729, 0x0272a, 0x0272b, 0x0272c, 0x0272d, 0x0272e, 0x0272f,
+ 0x02730, 0x02731, 0x02732, 0x02733, 0x02734, 0x02735, 0x02736, 0x02737,
+ 0x02738, 0x02739, 0x0273a, 0x0273b, 0x0273c, 0x0273d, 0x0273e, 0x0273f,
+ 0x02740, 0x02741, 0x02742, 0x02743, 0x02744, 0x02745, 0x02746, 0x02747,
+ 0x02748, 0x02749, 0x0274a, 0x0274b, 0x0274c, 0x0274d, 0x0274e, 0x0274f,
+ 0x02750, 0x02751, 0x02752, 0x02753, 0x02754, 0x02755, 0x02756, 0x02757,
+ 0x02758, 0x02759, 0x0275a, 0x0275b, 0x0275c, 0x0275d, 0x0275e, 0x0275f,
+ 0x02760, 0x02761, 0x02762, 0x02763, 0x02764, 0x02765, 0x02766, 0x02767,
+ 0x02768, 0x02769, 0x0276a, 0x0276b, 0x0276c, 0x0276d, 0x0276e, 0x0276f,
+ 0x02770, 0x02771, 0x02772, 0x02773, 0x02774, 0x02775, 0x00031, 0x00032,
+ 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x02469,
+ 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038,
+ 0x00039, 0x02469, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036,
+ 0x00037, 0x00038, 0x00039, 0x02469, 0x02794, 0x02795, 0x02796, 0x02797,
+ 0x02798, 0x02799, 0x0279a, 0x0279b, 0x0279c, 0x0279d, 0x0279e, 0x0279f,
+ 0x027a0, 0x027a1, 0x027a2, 0x027a3, 0x027a4, 0x027a5, 0x027a6, 0x027a7,
+ 0x027a8, 0x027a9, 0x027aa, 0x027ab, 0x027ac, 0x027ad, 0x027ae, 0x027af,
+ 0x027b0, 0x027b1, 0x027b2, 0x027b3, 0x027b4, 0x027b5, 0x027b6, 0x027b7,
+ 0x027b8, 0x027b9, 0x027ba, 0x027bb, 0x027bc, 0x027bd, 0x027be, 0x027bf,
+ 0x027c0, 0x027c1, 0x027c2, 0x027c3, 0x027c4, 0x027c5, 0x027c6, 0x027c7,
+ 0x027c8, 0x027c9, 0x027ca, 0x027cb, 0x027cc, 0x027cd, 0x027ce, 0x027cf,
+ 0x027d0, 0x027d1, 0x027d2, 0x027d3, 0x027d4, 0x027d5, 0x027d6, 0x027d7,
+ 0x027d8, 0x027d9, 0x027da, 0x027db, 0x027dc, 0x027dd, 0x027de, 0x027df,
+ 0x027e0, 0x027e1, 0x027e2, 0x027e3, 0x027e4, 0x027e5, 0x027e6, 0x027e7,
+ 0x027e8, 0x027e9, 0x027ea, 0x027eb, 0x027ec, 0x027ed, 0x027ee, 0x027ef,
+ 0x027f0, 0x027f1, 0x027f2, 0x027f3, 0x027f4, 0x027f5, 0x027f6, 0x027f7,
+ 0x027f8, 0x027f9, 0x027fa, 0x027fb, 0x027fc, 0x027fd, 0x027fe, 0x027ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_2a[] = {
+ 0x02a00, 0x02a01, 0x02a02, 0x02a03, 0x02a04, 0x02a05, 0x02a06, 0x02a07,
+ 0x02a08, 0x02a09, 0x02a0a, 0x02a0b, 0x02a0c, 0x02a0d, 0x02a0e, 0x02a0f,
+ 0x02a10, 0x02a11, 0x02a12, 0x02a13, 0x02a14, 0x02a15, 0x02a16, 0x02a17,
+ 0x02a18, 0x02a19, 0x02a1a, 0x02a1b, 0x02a1c, 0x02a1d, 0x02a1e, 0x02a1f,
+ 0x02a20, 0x02a21, 0x02a22, 0x02a23, 0x02a24, 0x02a25, 0x02a26, 0x02a27,
+ 0x02a28, 0x02a29, 0x02a2a, 0x02a2b, 0x02a2c, 0x02a2d, 0x02a2e, 0x02a2f,
+ 0x02a30, 0x02a31, 0x02a32, 0x02a33, 0x02a34, 0x02a35, 0x02a36, 0x02a37,
+ 0x02a38, 0x02a39, 0x02a3a, 0x02a3b, 0x02a3c, 0x02a3d, 0x02a3e, 0x02a3f,
+ 0x02a40, 0x02a41, 0x02a42, 0x02a43, 0x02a44, 0x02a45, 0x02a46, 0x02a47,
+ 0x02a48, 0x02a49, 0x02a4a, 0x02a4b, 0x02a4c, 0x02a4d, 0x02a4e, 0x02a4f,
+ 0x02a50, 0x02a51, 0x02a52, 0x02a53, 0x02a54, 0x02a55, 0x02a56, 0x02a57,
+ 0x02a58, 0x02a59, 0x02a5a, 0x02a5b, 0x02a5c, 0x02a5d, 0x02a5e, 0x02a5f,
+ 0x02a60, 0x02a61, 0x02a62, 0x02a63, 0x02a64, 0x02a65, 0x02a66, 0x02a67,
+ 0x02a68, 0x02a69, 0x02a6a, 0x02a6b, 0x02a6c, 0x02a6d, 0x02a6e, 0x02a6f,
+ 0x02a70, 0x02a71, 0x02a72, 0x02a73, 0x02a74, 0x02a75, 0x02a76, 0x02a77,
+ 0x02a78, 0x02a79, 0x02a7a, 0x02a7b, 0x02a7c, 0x02a7d, 0x02a7e, 0x02a7f,
+ 0x02a80, 0x02a81, 0x02a82, 0x02a83, 0x02a84, 0x02a85, 0x02a86, 0x02a87,
+ 0x02a88, 0x02a89, 0x02a8a, 0x02a8b, 0x02a8c, 0x02a8d, 0x02a8e, 0x02a8f,
+ 0x02a90, 0x02a91, 0x02a92, 0x02a93, 0x02a94, 0x02a95, 0x02a96, 0x02a97,
+ 0x02a98, 0x02a99, 0x02a9a, 0x02a9b, 0x02a9c, 0x02a9d, 0x02a9e, 0x02a9f,
+ 0x02aa0, 0x02aa1, 0x02aa2, 0x02aa3, 0x02aa4, 0x02aa5, 0x02aa6, 0x02aa7,
+ 0x02aa8, 0x02aa9, 0x02aaa, 0x02aab, 0x02aac, 0x02aad, 0x02aae, 0x02aaf,
+ 0x02ab0, 0x02ab1, 0x02ab2, 0x02ab3, 0x02ab4, 0x02ab5, 0x02ab6, 0x02ab7,
+ 0x02ab8, 0x02ab9, 0x02aba, 0x02abb, 0x02abc, 0x02abd, 0x02abe, 0x02abf,
+ 0x02ac0, 0x02ac1, 0x02ac2, 0x02ac3, 0x02ac4, 0x02ac5, 0x02ac6, 0x02ac7,
+ 0x02ac8, 0x02ac9, 0x02aca, 0x02acb, 0x02acc, 0x02acd, 0x02ace, 0x02acf,
+ 0x02ad0, 0x02ad1, 0x02ad2, 0x02ad3, 0x02ad4, 0x02ad5, 0x02ad6, 0x02ad7,
+ 0x02ad8, 0x02ad9, 0x02ada, 0x02adb, 0x02adc, 0x02adc, 0x02ade, 0x02adf,
+ 0x02ae0, 0x02ae1, 0x02ae2, 0x02ae3, 0x02ae4, 0x02ae5, 0x02ae6, 0x02ae7,
+ 0x02ae8, 0x02ae9, 0x02aea, 0x02aeb, 0x02aec, 0x02aed, 0x02aee, 0x02aef,
+ 0x02af0, 0x02af1, 0x02af2, 0x02af3, 0x02af4, 0x02af5, 0x02af6, 0x02af7,
+ 0x02af8, 0x02af9, 0x02afa, 0x02afb, 0x02afc, 0x02afd, 0x02afe, 0x02aff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_2e[] = {
+ 0x02e00, 0x02e01, 0x02e02, 0x02e03, 0x02e04, 0x02e05, 0x02e06, 0x02e07,
+ 0x02e08, 0x02e09, 0x02e0a, 0x02e0b, 0x02e0c, 0x02e0d, 0x02e0e, 0x02e0f,
+ 0x02e10, 0x02e11, 0x02e12, 0x02e13, 0x02e14, 0x02e15, 0x02e16, 0x02e17,
+ 0x02e18, 0x02e19, 0x02e1a, 0x02e1b, 0x02e1c, 0x02e1d, 0x02e1e, 0x02e1f,
+ 0x02e20, 0x02e21, 0x02e22, 0x02e23, 0x02e24, 0x02e25, 0x02e26, 0x02e27,
+ 0x02e28, 0x02e29, 0x02e2a, 0x02e2b, 0x02e2c, 0x02e2d, 0x02e2e, 0x02e2f,
+ 0x02e30, 0x02e31, 0x02e32, 0x02e33, 0x02e34, 0x02e35, 0x02e36, 0x02e37,
+ 0x02e38, 0x02e39, 0x02e3a, 0x02e3b, 0x02e3c, 0x02e3d, 0x02e3e, 0x02e3f,
+ 0x02e40, 0x02e41, 0x02e42, 0x02e43, 0x02e44, 0x02e45, 0x02e46, 0x02e47,
+ 0x02e48, 0x02e49, 0x02e4a, 0x02e4b, 0x02e4c, 0x02e4d, 0x02e4e, 0x02e4f,
+ 0x02e50, 0x02e51, 0x02e52, 0x02e53, 0x02e54, 0x02e55, 0x02e56, 0x02e57,
+ 0x02e58, 0x02e59, 0x02e5a, 0x02e5b, 0x02e5c, 0x02e5d, 0x02e5e, 0x02e5f,
+ 0x02e60, 0x02e61, 0x02e62, 0x02e63, 0x02e64, 0x02e65, 0x02e66, 0x02e67,
+ 0x02e68, 0x02e69, 0x02e6a, 0x02e6b, 0x02e6c, 0x02e6d, 0x02e6e, 0x02e6f,
+ 0x02e70, 0x02e71, 0x02e72, 0x02e73, 0x02e74, 0x02e75, 0x02e76, 0x02e77,
+ 0x02e78, 0x02e79, 0x02e7a, 0x02e7b, 0x02e7c, 0x02e7d, 0x02e7e, 0x02e7f,
+ 0x02e80, 0x02e81, 0x02e82, 0x02e83, 0x0319a, 0x02e85, 0x02e86, 0x02f0f,
+ 0x02f11, 0x02e89, 0x02f18, 0x02e8b, 0x02f29, 0x02f29, 0x02e8e, 0x02e8f,
+ 0x02e8e, 0x02e8f, 0x02e92, 0x02e93, 0x02e94, 0x02e95, 0x02e96, 0x02f3c,
+ 0x02e98, 0x02e99, 0x02e9a, 0x02e9b, 0x02f47, 0x02f49, 0x02e9e, 0x02e9f,
+ 0x02ea0, 0x02ea1, 0x02ea2, 0x02ea3, 0x02ea4, 0x02ea4, 0x02ea6, 0x02f5c,
+ 0x02ea8, 0x02ea9, 0x02eaa, 0x02f6c, 0x02f70, 0x02ead, 0x02f75, 0x02eaf,
+ 0x02eb0, 0x02eb1, 0x02eb2, 0x02eb1, 0x02eb1, 0x02eb2, 0x02eb7, 0x02eb7,
+ 0x02eb8, 0x02eb9, 0x02eba, 0x02f80, 0x02f81, 0x02f85, 0x02ebe, 0x02ebe,
+ 0x02ebe, 0x02ec1, 0x02ec2, 0x02ec3, 0x02ec4, 0x02ec5, 0x02f93, 0x02f93,
+ 0x02ec8, 0x02ec9, 0x02f9c, 0x02ecb, 0x02ecc, 0x02ecc, 0x02ecc, 0x02fa2,
+ 0x02ed0, 0x02fa7, 0x02ed2, 0x02ed3, 0x02ed4, 0x02fa9, 0x02ed6, 0x02fac,
+ 0x02ed8, 0x02ed9, 0x02eda, 0x02edb, 0x02edc, 0x02fb7, 0x02ede, 0x02ede,
+ 0x02ee0, 0x02fb8, 0x02ee2, 0x02fbb, 0x02fc1, 0x02ee5, 0x02ee6, 0x02ee7,
+ 0x02ee8, 0x02ee9, 0x02eea, 0x02eeb, 0x02eec, 0x02eed, 0x02eee, 0x02eef,
+ 0x02ef0, 0x02ef2, 0x02ef2, 0x02ef3, 0x02ef4, 0x02ef5, 0x02ef6, 0x02ef7,
+ 0x02ef8, 0x02ef9, 0x02efa, 0x02efb, 0x02efc, 0x02efd, 0x02efe, 0x02eff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_2f[] = {
+ 0x03192, 0x02f01, 0x02e80, 0x02f03, 0x0319a, 0x02f05, 0x03193, 0x02f07,
+ 0x0319f, 0x02f09, 0x02f0a, 0x02f0b, 0x02e86, 0x02f0d, 0x02f0e, 0x02f0f,
+ 0x02f10, 0x02f11, 0x02f12, 0x02f13, 0x02f14, 0x02f15, 0x02f16, 0x02f17,
+ 0x02f18, 0x02e8b, 0x02e81, 0x02f1b, 0x02f1c, 0x02f1d, 0x02f1e, 0x02f1f,
+ 0x02f20, 0x02f21, 0x02f22, 0x02f23, 0x02f24, 0x02f25, 0x02f26, 0x02f27,
+ 0x02f28, 0x02f29, 0x02e8e, 0x02f2b, 0x02f2c, 0x02f2d, 0x02f2e, 0x02f2f,
+ 0x02f30, 0x02f31, 0x02f32, 0x02e93, 0x02f34, 0x02f35, 0x02f36, 0x02f37,
+ 0x02f38, 0x02e95, 0x02f3a, 0x02f3b, 0x02f3c, 0x02f3d, 0x02f3e, 0x02f3f,
+ 0x02f40, 0x02f41, 0x02f42, 0x02f43, 0x02f44, 0x02f45, 0x02f46, 0x02f47,
+ 0x02f48, 0x02f49, 0x02f4a, 0x02f4b, 0x02f4c, 0x02f4d, 0x02f4e, 0x02f4f,
+ 0x02f50, 0x02f51, 0x02f52, 0x02f53, 0x02f54, 0x02f55, 0x02f56, 0x02f57,
+ 0x02f58, 0x02f59, 0x02f5a, 0x02f5b, 0x02f5c, 0x02f5d, 0x02f5e, 0x02f5f,
+ 0x02f60, 0x02f61, 0x02f62, 0x02f63, 0x02f64, 0x02f65, 0x02eaa, 0x02f67,
+ 0x02f68, 0x02f69, 0x02f6a, 0x02f6b, 0x02f6c, 0x02f6d, 0x02f6e, 0x02f6f,
+ 0x02f70, 0x02f71, 0x02f72, 0x02f73, 0x02f74, 0x02f75, 0x02f76, 0x02f77,
+ 0x02f78, 0x02f79, 0x02eb7, 0x02f7b, 0x02f7c, 0x02f7d, 0x02f7e, 0x02f7f,
+ 0x02f80, 0x02f81, 0x02f82, 0x02f83, 0x02f84, 0x02f85, 0x02f86, 0x02f87,
+ 0x02f88, 0x02f89, 0x02f8a, 0x02f8b, 0x02f8c, 0x02f8d, 0x02f8e, 0x02f8f,
+ 0x02f90, 0x02f91, 0x02f92, 0x02f93, 0x02f94, 0x02f95, 0x02f96, 0x02f97,
+ 0x02f98, 0x02f99, 0x02f9a, 0x02f9b, 0x02f9c, 0x02f9d, 0x02f9e, 0x02f9f,
+ 0x02fa0, 0x02fa1, 0x02fa2, 0x02fa3, 0x02fa4, 0x02fa5, 0x02fa6, 0x02fa7,
+ 0x02fa8, 0x02fa9, 0x02faa, 0x02fab, 0x02fac, 0x02fad, 0x02fae, 0x02faf,
+ 0x02fb0, 0x02fb1, 0x02fb2, 0x02fb3, 0x02fb4, 0x02fb5, 0x02fb6, 0x02fb7,
+ 0x02fb8, 0x02fb9, 0x02fba, 0x02fbb, 0x02fbc, 0x02fbd, 0x02fbe, 0x02fbf,
+ 0x02fc0, 0x02fc1, 0x02fc2, 0x02fc3, 0x02ee7, 0x02fc5, 0x02fc6, 0x02fc7,
+ 0x02fc8, 0x02fc9, 0x02fca, 0x02fcb, 0x02fcc, 0x02fcd, 0x02fce, 0x02fcf,
+ 0x02fd0, 0x02eeb, 0x02eed, 0x02eef, 0x02ef2, 0x02fd5, 0x02fd6, 0x02fd7,
+ 0x02fd8, 0x02fd9, 0x02fda, 0x02fdb, 0x02fdc, 0x02fdd, 0x02fde, 0x02fdf,
+ 0x02fe0, 0x02fe1, 0x02fe2, 0x02fe3, 0x02fe4, 0x02fe5, 0x02fe6, 0x02fe7,
+ 0x02fe8, 0x02fe9, 0x02fea, 0x02feb, 0x02fec, 0x02fed, 0x02fee, 0x02fef,
+ 0x02ff0, 0x02ff1, 0x02ff2, 0x02ff3, 0x02ff4, 0x02ff5, 0x02ff6, 0x02ff7,
+ 0x02ff8, 0x02ff9, 0x02ffa, 0x02ffb, 0x02ffc, 0x02ffd, 0x02ffe, 0x02fff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_30[] = {
+ 0x00020, 0x03001, 0x03002, 0x03003, 0x03004, 0x03005, 0x03006, 0x00030,
+ 0x02329, 0x0232a, 0x0300a, 0x0300b, 0x0300c, 0x0300d, 0x0300e, 0x0300f,
+ 0x03010, 0x03011, 0x03012, 0x03013, 0x03014, 0x03015, 0x03016, 0x03017,
+ 0x03018, 0x03019, 0x0301a, 0x0301b, 0x0301c, 0x0301d, 0x0301e, 0x0301f,
+ 0x03020, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x03030, 0x03031, 0x03031, 0x03033, 0x03033, 0x03035, 0x03012, 0x03037,
+ 0x02f17, 0x03039, 0x0303a, 0x0303b, 0x0303c, 0x0303d, 0x0303e, 0x0303f,
+ 0x03040, 0x03041, 0x03042, 0x03043, 0x03044, 0x03045, 0x03046, 0x03047,
+ 0x03048, 0x03049, 0x0304a, 0x0304b, 0x0304c, 0x0304d, 0x0304e, 0x0304f,
+ 0x03050, 0x03051, 0x03052, 0x03053, 0x03054, 0x03055, 0x03056, 0x03057,
+ 0x03058, 0x03059, 0x0305a, 0x0305b, 0x0305c, 0x0305d, 0x0305e, 0x0305f,
+ 0x03060, 0x03061, 0x03062, 0x03063, 0x03064, 0x03065, 0x03066, 0x03067,
+ 0x03068, 0x03069, 0x0306a, 0x0306b, 0x0306c, 0x0306d, 0x0306e, 0x0306f,
+ 0x03070, 0x03071, 0x03072, 0x03073, 0x03074, 0x03075, 0x03076, 0x03077,
+ 0x03078, 0x03079, 0x0307a, 0x0307b, 0x0307c, 0x0307d, 0x0307e, 0x0307f,
+ 0x03080, 0x03081, 0x03082, 0x03083, 0x03084, 0x03085, 0x03086, 0x03087,
+ 0x03088, 0x03089, 0x0308a, 0x0308b, 0x0308c, 0x0308d, 0x0308e, 0x0308f,
+ 0x03090, 0x03091, 0x03092, 0x03093, 0x03046, 0x0304b, 0x03051, 0x03097,
+ 0x03098, 0x00000, 0x00000, 0x0309b, 0x0309c, 0x0309d, 0x0309d, 0x0309f,
+ 0x030a0, 0x03041, 0x03042, 0x03043, 0x03044, 0x03045, 0x03046, 0x03047,
+ 0x03048, 0x03049, 0x0304a, 0x0304b, 0x0304c, 0x0304d, 0x0304e, 0x0304f,
+ 0x03050, 0x03051, 0x03052, 0x03053, 0x03054, 0x03055, 0x03056, 0x03057,
+ 0x03058, 0x03059, 0x0305a, 0x0305b, 0x0305c, 0x0305d, 0x0305e, 0x0305f,
+ 0x03060, 0x03061, 0x03062, 0x03063, 0x03064, 0x03065, 0x03066, 0x03067,
+ 0x03068, 0x03069, 0x0306a, 0x0306b, 0x0306c, 0x0306d, 0x0306e, 0x0306f,
+ 0x03070, 0x03071, 0x03072, 0x03073, 0x03074, 0x03075, 0x03076, 0x03077,
+ 0x03078, 0x03079, 0x0307a, 0x0307b, 0x0307c, 0x0307d, 0x0307e, 0x0307f,
+ 0x03080, 0x03081, 0x03082, 0x03083, 0x03084, 0x03085, 0x03086, 0x03087,
+ 0x03088, 0x03089, 0x0308a, 0x0308b, 0x0308c, 0x0308d, 0x0308e, 0x0308f,
+ 0x03090, 0x03091, 0x03092, 0x03093, 0x03046, 0x0304b, 0x03051, 0x0308f,
+ 0x03090, 0x03091, 0x03092, 0x030fb, 0x030fc, 0x030fd, 0x030fd, 0x030ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_31[] = {
+ 0x03100, 0x03101, 0x03102, 0x03103, 0x03104, 0x03105, 0x03106, 0x03107,
+ 0x03108, 0x03109, 0x0310a, 0x0310b, 0x0310c, 0x0310d, 0x0310e, 0x0310f,
+ 0x03110, 0x03111, 0x03112, 0x03113, 0x03114, 0x03115, 0x03116, 0x03117,
+ 0x03118, 0x03119, 0x0311a, 0x0311b, 0x0311c, 0x0311d, 0x0311e, 0x0311f,
+ 0x03120, 0x03121, 0x03122, 0x03123, 0x03124, 0x03125, 0x03126, 0x03127,
+ 0x03128, 0x03129, 0x0312a, 0x0312b, 0x0312c, 0x0312d, 0x0312e, 0x0312f,
+ 0x03130, 0x01100, 0x01101, 0x011aa, 0x01102, 0x011ac, 0x011ad, 0x01103,
+ 0x01104, 0x01105, 0x011b0, 0x011b1, 0x011b2, 0x011b3, 0x011b4, 0x011b5,
+ 0x0111a, 0x01106, 0x01107, 0x01108, 0x01121, 0x01109, 0x0110a, 0x0110b,
+ 0x0110c, 0x0110d, 0x0110e, 0x0110f, 0x01110, 0x01111, 0x01112, 0x01161,
+ 0x01162, 0x01163, 0x01164, 0x01165, 0x01166, 0x01167, 0x01168, 0x01169,
+ 0x0116a, 0x0116b, 0x0116c, 0x0116d, 0x0116e, 0x0116f, 0x01170, 0x01171,
+ 0x01172, 0x01173, 0x01174, 0x01175, 0x01160, 0x01114, 0x01115, 0x011c7,
+ 0x011c8, 0x011cc, 0x011ce, 0x011d3, 0x011d7, 0x011d9, 0x0111c, 0x011dd,
+ 0x011df, 0x0111d, 0x0111e, 0x01120, 0x01122, 0x01123, 0x01127, 0x01129,
+ 0x0112b, 0x0112c, 0x0112d, 0x0112e, 0x0112f, 0x01132, 0x01136, 0x01140,
+ 0x01147, 0x0114c, 0x011f1, 0x011f2, 0x01157, 0x01158, 0x01159, 0x01184,
+ 0x01185, 0x01188, 0x01191, 0x01192, 0x01194, 0x0119e, 0x011a1, 0x0318f,
+ 0x03190, 0x03191, 0x03192, 0x03193, 0x03194, 0x03195, 0x03196, 0x03197,
+ 0x03198, 0x03199, 0x0319a, 0x0319b, 0x0319c, 0x0319d, 0x0319e, 0x0319f,
+ 0x03105, 0x03117, 0x03110, 0x0310d, 0x031a4, 0x031a4, 0x031a6, 0x0311b,
+ 0x03128, 0x0311a, 0x03127, 0x03128, 0x031ac, 0x031ad, 0x0311e, 0x03120,
+ 0x031b0, 0x031b1, 0x031b2, 0x03127, 0x03106, 0x0310a, 0x0310e, 0x0310f,
+ 0x031b8, 0x031b9, 0x031ba, 0x031bb, 0x031bc, 0x031bd, 0x031be, 0x031bf,
+ 0x031c0, 0x031c1, 0x031c2, 0x031c3, 0x031c4, 0x031c5, 0x031c6, 0x031c7,
+ 0x031c8, 0x031c9, 0x031ca, 0x031cb, 0x031cc, 0x031cd, 0x031ce, 0x031cf,
+ 0x031d0, 0x031d1, 0x031d2, 0x031d3, 0x031d4, 0x031d5, 0x031d6, 0x031d7,
+ 0x031d8, 0x031d9, 0x031da, 0x031db, 0x031dc, 0x031dd, 0x031de, 0x031df,
+ 0x031e0, 0x031e1, 0x031e2, 0x031e3, 0x031e4, 0x031e5, 0x031e6, 0x031e7,
+ 0x031e8, 0x031e9, 0x031ea, 0x031eb, 0x031ec, 0x031ed, 0x031ee, 0x031ef,
+ 0x0304f, 0x03057, 0x03059, 0x03068, 0x0306c, 0x0306f, 0x03072, 0x03075,
+ 0x03078, 0x0307b, 0x03080, 0x03089, 0x0308a, 0x0308b, 0x0308c, 0x031ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_32[] = {
+ 0x03200, 0x03201, 0x03202, 0x03203, 0x03204, 0x03205, 0x03206, 0x03207,
+ 0x03208, 0x03209, 0x0320a, 0x0320b, 0x0320c, 0x0320d, 0x0320e, 0x0320f,
+ 0x03210, 0x03211, 0x03212, 0x03213, 0x03214, 0x03215, 0x03216, 0x03217,
+ 0x03218, 0x03219, 0x0321a, 0x0321b, 0x0321c, 0x0321d, 0x0321e, 0x0321f,
+ 0x03220, 0x03221, 0x03222, 0x03223, 0x03224, 0x03225, 0x03226, 0x03227,
+ 0x03228, 0x03229, 0x0322a, 0x0322b, 0x0322c, 0x0322d, 0x0322e, 0x0322f,
+ 0x03230, 0x03231, 0x03232, 0x03233, 0x03234, 0x03235, 0x03236, 0x03237,
+ 0x03238, 0x03239, 0x0323a, 0x0323b, 0x0323c, 0x0323d, 0x0323e, 0x0323f,
+ 0x03240, 0x03241, 0x03242, 0x03243, 0x03244, 0x03245, 0x03246, 0x03247,
+ 0x03248, 0x03249, 0x0324a, 0x0324b, 0x0324c, 0x0324d, 0x0324e, 0x0324f,
+ 0x03250, 0x03251, 0x03252, 0x03253, 0x03254, 0x03255, 0x03256, 0x03257,
+ 0x03258, 0x03259, 0x0325a, 0x0325b, 0x0325c, 0x0325d, 0x0325e, 0x0325f,
+ 0x01100, 0x01102, 0x01103, 0x01105, 0x01106, 0x01107, 0x01109, 0x0110b,
+ 0x0110c, 0x0110e, 0x0110f, 0x01110, 0x01111, 0x01112, 0x0326e, 0x0326f,
+ 0x03270, 0x03271, 0x03272, 0x03273, 0x03274, 0x03275, 0x03276, 0x03277,
+ 0x03278, 0x03279, 0x0327a, 0x0327b, 0x0327c, 0x0327d, 0x0327e, 0x0327f,
+ 0x03192, 0x03193, 0x03194, 0x03195, 0x03284, 0x03285, 0x03286, 0x02f0b,
+ 0x03288, 0x02f17, 0x02f49, 0x02f55, 0x02f54, 0x02f4a, 0x02fa6, 0x02f1f,
+ 0x02f47, 0x03291, 0x03292, 0x03293, 0x03294, 0x03295, 0x03296, 0x03297,
+ 0x03298, 0x03299, 0x0329a, 0x02f25, 0x0329c, 0x0329d, 0x0329e, 0x0329f,
+ 0x032a0, 0x032a1, 0x032a2, 0x032a3, 0x03196, 0x03197, 0x03198, 0x032a7,
+ 0x032a8, 0x032a9, 0x032aa, 0x032ab, 0x032ac, 0x032ad, 0x032ae, 0x032af,
+ 0x032b0, 0x032b1, 0x032b2, 0x032b3, 0x032b4, 0x032b5, 0x032b6, 0x032b7,
+ 0x032b8, 0x032b9, 0x032ba, 0x032bb, 0x032bc, 0x032bd, 0x032be, 0x032bf,
+ 0x032c0, 0x032c1, 0x032c2, 0x032c3, 0x032c4, 0x032c5, 0x032c6, 0x032c7,
+ 0x032c8, 0x032c9, 0x032ca, 0x032cb, 0x032cc, 0x032cd, 0x032ce, 0x032cf,
+ 0x03042, 0x03044, 0x03046, 0x03048, 0x0304a, 0x0304b, 0x0304d, 0x0304f,
+ 0x03051, 0x03053, 0x03055, 0x03057, 0x03059, 0x0305b, 0x0305d, 0x0305f,
+ 0x03061, 0x03064, 0x03066, 0x03068, 0x0306a, 0x0306b, 0x0306c, 0x0306d,
+ 0x0306e, 0x0306f, 0x03072, 0x03075, 0x03078, 0x0307b, 0x0307e, 0x0307f,
+ 0x03080, 0x03081, 0x03082, 0x03084, 0x03086, 0x03088, 0x03089, 0x0308a,
+ 0x0308b, 0x0308c, 0x0308d, 0x0308f, 0x03090, 0x03091, 0x03092, 0x032ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_33[] = {
+ 0x03300, 0x03301, 0x03302, 0x03303, 0x03304, 0x03305, 0x03306, 0x03307,
+ 0x03308, 0x03309, 0x0330a, 0x0330b, 0x0330c, 0x0330d, 0x0330e, 0x0330f,
+ 0x03310, 0x03311, 0x03312, 0x03313, 0x03314, 0x03315, 0x03316, 0x03317,
+ 0x03318, 0x03319, 0x0331a, 0x0331b, 0x0331c, 0x0331d, 0x0331e, 0x0331f,
+ 0x03320, 0x03321, 0x03322, 0x03323, 0x03324, 0x03325, 0x03326, 0x03327,
+ 0x03328, 0x03329, 0x0332a, 0x0332b, 0x0332c, 0x0332d, 0x0332e, 0x0332f,
+ 0x03330, 0x03331, 0x03332, 0x03333, 0x03334, 0x03335, 0x03336, 0x03337,
+ 0x03338, 0x03339, 0x0333a, 0x0333b, 0x0333c, 0x0333d, 0x0333e, 0x0333f,
+ 0x03340, 0x03341, 0x03342, 0x03343, 0x03344, 0x03345, 0x03346, 0x03347,
+ 0x03348, 0x03349, 0x0334a, 0x0334b, 0x0334c, 0x0334d, 0x0334e, 0x0334f,
+ 0x03350, 0x03351, 0x03352, 0x03353, 0x03354, 0x03355, 0x03356, 0x03357,
+ 0x03358, 0x03359, 0x0335a, 0x0335b, 0x0335c, 0x0335d, 0x0335e, 0x0335f,
+ 0x03360, 0x03361, 0x03362, 0x03363, 0x03364, 0x03365, 0x03366, 0x03367,
+ 0x03368, 0x03369, 0x0336a, 0x0336b, 0x0336c, 0x0336d, 0x0336e, 0x0336f,
+ 0x03370, 0x03371, 0x03372, 0x03373, 0x03374, 0x03375, 0x03376, 0x03377,
+ 0x03378, 0x03379, 0x0337a, 0x0337b, 0x0337c, 0x0337d, 0x0337e, 0x0337f,
+ 0x03380, 0x03381, 0x03382, 0x03383, 0x03384, 0x03385, 0x03386, 0x03387,
+ 0x03388, 0x03389, 0x0338a, 0x0338b, 0x0338c, 0x0338d, 0x0338e, 0x0338f,
+ 0x03390, 0x03391, 0x03392, 0x03393, 0x03394, 0x03395, 0x03396, 0x03397,
+ 0x03398, 0x03399, 0x0339a, 0x0339b, 0x0339c, 0x0339d, 0x0339e, 0x0339f,
+ 0x033a0, 0x033a1, 0x033a2, 0x033a3, 0x033a4, 0x033a5, 0x033a6, 0x033a7,
+ 0x033a8, 0x03380, 0x033aa, 0x033ab, 0x033ac, 0x033ad, 0x033ae, 0x033af,
+ 0x033b0, 0x033b1, 0x033b2, 0x033b3, 0x033b4, 0x033b5, 0x033b6, 0x033b7,
+ 0x033b8, 0x033b7, 0x033ba, 0x033bb, 0x033bc, 0x033bd, 0x033be, 0x033bd,
+ 0x033c0, 0x033c1, 0x033c2, 0x033c3, 0x033c4, 0x033c5, 0x033c6, 0x033c7,
+ 0x033c8, 0x033c9, 0x033ca, 0x033cb, 0x033cc, 0x033cd, 0x0339e, 0x033cf,
+ 0x033d0, 0x033d1, 0x033d2, 0x033d3, 0x03386, 0x033d5, 0x033d6, 0x033d7,
+ 0x033d8, 0x033d9, 0x033da, 0x033db, 0x033dc, 0x033dd, 0x033de, 0x033df,
+ 0x033e0, 0x033e1, 0x033e2, 0x033e3, 0x033e4, 0x033e5, 0x033e6, 0x033e7,
+ 0x033e8, 0x033e9, 0x033ea, 0x033eb, 0x033ec, 0x033ed, 0x033ee, 0x033ef,
+ 0x033f0, 0x033f1, 0x033f2, 0x033f3, 0x033f4, 0x033f5, 0x033f6, 0x033f7,
+ 0x033f8, 0x033f9, 0x033fa, 0x033fb, 0x033fc, 0x033fd, 0x033fe, 0x033ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_f9[] = {
+ 0x0f900, 0x0f901, 0x02f9e, 0x0f903, 0x0f904, 0x0f905, 0x0f906, 0x02ef2,
+ 0x02ef2, 0x0f909, 0x02fa6, 0x0f90b, 0x0f90c, 0x0f90d, 0x0f90e, 0x0f90f,
+ 0x0f910, 0x0f911, 0x0f912, 0x0f913, 0x0f914, 0x0f915, 0x0f916, 0x0f917,
+ 0x0f918, 0x0f919, 0x0f91a, 0x0f91b, 0x0f91c, 0x0f91d, 0x0f91e, 0x0f91f,
+ 0x0f920, 0x0f921, 0x0f922, 0x0f923, 0x0f924, 0x0f925, 0x0f926, 0x0f927,
+ 0x0f928, 0x0f929, 0x0f92a, 0x0f92b, 0x0f92c, 0x0f92d, 0x0f92e, 0x0f92f,
+ 0x0f930, 0x0f931, 0x0f932, 0x0f933, 0x02f7c, 0x0f935, 0x0f936, 0x0f937,
+ 0x0f938, 0x0f939, 0x0f93a, 0x0f93b, 0x0f93c, 0x0f93d, 0x0f93e, 0x0f93f,
+ 0x02fc5, 0x0f941, 0x0f942, 0x0f943, 0x0f944, 0x0f945, 0x0f946, 0x0f947,
+ 0x0f948, 0x0f949, 0x0f94a, 0x0f94b, 0x0f94c, 0x0f94d, 0x0f94e, 0x0f94f,
+ 0x0f950, 0x0f951, 0x0f952, 0x0f953, 0x0f954, 0x0f955, 0x0f956, 0x0f957,
+ 0x0f958, 0x0f959, 0x0f95a, 0x0f95b, 0x0f914, 0x0f95d, 0x0f95e, 0x0f95f,
+ 0x0f960, 0x0f961, 0x0f962, 0x0f963, 0x0f964, 0x0f965, 0x0f966, 0x0f967,
+ 0x0f968, 0x0f969, 0x0f96a, 0x0f96b, 0x0f96c, 0x0f96d, 0x0f96e, 0x0f96f,
+ 0x0f970, 0x02fa0, 0x0f972, 0x0f973, 0x0f974, 0x0f975, 0x0f976, 0x0f977,
+ 0x0f978, 0x0f979, 0x0f97a, 0x0f97b, 0x0f97c, 0x0f97d, 0x0f97e, 0x0f97f,
+ 0x0f980, 0x02f25, 0x0f982, 0x0f983, 0x0f984, 0x0f985, 0x0f986, 0x0f987,
+ 0x0f988, 0x0f989, 0x02f12, 0x0f98b, 0x0f98c, 0x0f98d, 0x0f98e, 0x0f98f,
+ 0x0f990, 0x0f991, 0x0f992, 0x0f993, 0x0f994, 0x0f995, 0x0f996, 0x0f997,
+ 0x0f998, 0x0f999, 0x0f99a, 0x0f99b, 0x0f99c, 0x0f99d, 0x0f99e, 0x0f99f,
+ 0x0f9a0, 0x0f96f, 0x0f9a2, 0x0f9a3, 0x0f9a4, 0x0f9a5, 0x0f9a6, 0x0f9a7,
+ 0x0f9a8, 0x0f9a9, 0x0f95f, 0x0f9ab, 0x0f9ac, 0x0f9ad, 0x0f9ae, 0x0f9af,
+ 0x0f9b0, 0x0f9b1, 0x0f9b2, 0x0f9b3, 0x0f9b4, 0x0f9b5, 0x0f9b6, 0x0f9b7,
+ 0x0f9b8, 0x0f9b9, 0x0f9ba, 0x0f9bb, 0x0f9bc, 0x0f9bd, 0x0f9be, 0x0f914,
+ 0x0f9c0, 0x0f9c1, 0x0f9c2, 0x0f9c3, 0x02eef, 0x0f9c5, 0x0f9c6, 0x0f9c7,
+ 0x0f9c8, 0x0f9c9, 0x0f9ca, 0x0f9cb, 0x0f9cc, 0x0f9cd, 0x0f9ce, 0x0f9cf,
+ 0x0f9d0, 0x03285, 0x0f9d2, 0x0f9d3, 0x0f9d4, 0x0f9d5, 0x0f9d6, 0x0f9d7,
+ 0x0f9d8, 0x0f9d9, 0x0f9da, 0x0f961, 0x0f9dc, 0x0f9dd, 0x0f9de, 0x0f9df,
+ 0x0f9e0, 0x0f9e1, 0x0f9e2, 0x0f9e3, 0x0f9e4, 0x0f9e5, 0x0f9e6, 0x0f9e7,
+ 0x0f9e8, 0x02fa5, 0x0f9ea, 0x0f9eb, 0x0f9ec, 0x0f9ed, 0x0f9ee, 0x0f9ef,
+ 0x0f9f0, 0x0f9f1, 0x0f9f2, 0x0f9f3, 0x0f9f4, 0x0f9f5, 0x0f9f6, 0x02f74,
+ 0x0f9f8, 0x0f9f9, 0x0f9fa, 0x0f9fb, 0x0f9fc, 0x0f9fd, 0x0f9fe, 0x0f9ff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_fa[] = {
+ 0x0fa00, 0x0fa01, 0x0fa02, 0x0fa03, 0x0fa04, 0x0fa05, 0x0fa06, 0x0fa07,
+ 0x02f8f, 0x0fa09, 0x02f92, 0x0fa0b, 0x0fa0c, 0x0fa0d, 0x0fa0e, 0x0fa0f,
+ 0x0fa10, 0x0fa11, 0x0fa12, 0x0fa13, 0x0fa14, 0x0fa15, 0x0fa16, 0x0fa17,
+ 0x0fa18, 0x0fa19, 0x0fa1a, 0x0fa1b, 0x0fa1c, 0x0fa1d, 0x02f7b, 0x0fa1f,
+ 0x0fa20, 0x0fa21, 0x0fa22, 0x0fa23, 0x0fa24, 0x0fa25, 0x0fa26, 0x0fa27,
+ 0x0fa28, 0x0fa29, 0x0fa2a, 0x0fa2b, 0x0fa2c, 0x0fa2d, 0x0fa2e, 0x0fa2f,
+ 0x0fa30, 0x0fa31, 0x0fa32, 0x0fa33, 0x0fa34, 0x0fa35, 0x0fa36, 0x0fa37,
+ 0x0fa38, 0x0fa39, 0x0fa3a, 0x0fa3b, 0x02f2c, 0x0fa3d, 0x0fa3e, 0x0fa3f,
+ 0x0fa40, 0x0fa41, 0x0fa42, 0x0fa43, 0x0fa44, 0x0fa45, 0x0fa46, 0x0fa47,
+ 0x0fa48, 0x02ea4, 0x0fa4a, 0x0fa4b, 0x03293, 0x0fa4d, 0x0fa4e, 0x0fa4f,
+ 0x0fa50, 0x03297, 0x0fa52, 0x0fa53, 0x0fa54, 0x0fa55, 0x0fa56, 0x0f996,
+ 0x0fa58, 0x0fa59, 0x0fa5a, 0x0fa5b, 0x0fa5c, 0x02ebe, 0x02ebe, 0x0fa5f,
+ 0x0fa60, 0x0fa61, 0x0fa62, 0x0fa63, 0x0fa64, 0x0fa65, 0x02ecc, 0x0fa25,
+ 0x0fa68, 0x0fa69, 0x0fa6a, 0x0fa6b, 0x0fa6c, 0x0fa6d, 0x0fa6e, 0x0fa6f,
+ 0x0fa70, 0x0fa71, 0x0fa72, 0x0fa73, 0x0fa74, 0x0fa75, 0x0fa76, 0x0fa77,
+ 0x0fa78, 0x0fa79, 0x0fa7a, 0x0fa7b, 0x0fa7c, 0x0fa7d, 0x0fa7e, 0x0fa7f,
+ 0x0fa80, 0x0fa81, 0x0fa82, 0x0fa83, 0x0fa84, 0x0fa85, 0x0fa86, 0x0fa87,
+ 0x0fa88, 0x0fa89, 0x0fa8a, 0x0fa8b, 0x0fa8c, 0x0fa8d, 0x0fa8e, 0x0fa8f,
+ 0x0fa90, 0x0fa91, 0x0fa92, 0x0fa93, 0x0fa94, 0x0fa95, 0x0fa96, 0x0fa97,
+ 0x0fa98, 0x0fa99, 0x0fa9a, 0x0fa9b, 0x0fa9c, 0x0fa9d, 0x0fa9e, 0x0fa9f,
+ 0x0faa0, 0x0faa1, 0x0faa2, 0x0faa3, 0x0faa4, 0x0faa5, 0x0faa6, 0x0faa7,
+ 0x0faa8, 0x0faa9, 0x0faaa, 0x0faab, 0x0faac, 0x0faad, 0x0faae, 0x0faaf,
+ 0x0fab0, 0x0fab1, 0x0fab2, 0x0fab3, 0x0fab4, 0x0fab5, 0x0fab6, 0x0fab7,
+ 0x0fab8, 0x0fab9, 0x0faba, 0x0fabb, 0x0fabc, 0x0fabd, 0x0fabe, 0x0fabf,
+ 0x0fac0, 0x0fac1, 0x0fac2, 0x0fac3, 0x0fac4, 0x0fac5, 0x0fac6, 0x0fac7,
+ 0x0fac8, 0x0fac9, 0x0faca, 0x0facb, 0x0facc, 0x0facd, 0x0face, 0x0facf,
+ 0x0fad0, 0x0fad1, 0x0fad2, 0x0fad3, 0x0fad4, 0x0fad5, 0x0fad6, 0x0fad7,
+ 0x0fad8, 0x0fad9, 0x0fada, 0x0fadb, 0x0fadc, 0x0fadd, 0x0fade, 0x0fadf,
+ 0x0fae0, 0x0fae1, 0x0fae2, 0x0fae3, 0x0fae4, 0x0fae5, 0x0fae6, 0x0fae7,
+ 0x0fae8, 0x0fae9, 0x0faea, 0x0faeb, 0x0faec, 0x0faed, 0x0faee, 0x0faef,
+ 0x0faf0, 0x0faf1, 0x0faf2, 0x0faf3, 0x0faf4, 0x0faf5, 0x0faf6, 0x0faf7,
+ 0x0faf8, 0x0faf9, 0x0fafa, 0x0fafb, 0x0fafc, 0x0fafd, 0x0fafe, 0x0faff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_fb[] = {
+ 0x0fb00, 0x0fb01, 0x0fb02, 0x0fb03, 0x0fb04, 0x0fb05, 0x0fb05, 0x0fb07,
+ 0x0fb08, 0x0fb09, 0x0fb0a, 0x0fb0b, 0x0fb0c, 0x0fb0d, 0x0fb0e, 0x0fb0f,
+ 0x0fb10, 0x0fb11, 0x0fb12, 0x0fb13, 0x0fb14, 0x0fb15, 0x0fb16, 0x0fb17,
+ 0x0fb18, 0x0fb19, 0x0fb1a, 0x0fb1b, 0x0fb1c, 0x005d9, 0x00000, 0x005f2,
+ 0x005e2, 0x005d0, 0x005d3, 0x005d4, 0x005da, 0x005dc, 0x005dd, 0x005e8,
+ 0x005ea, 0x0002b, 0x005e9, 0x005e9, 0x005e9, 0x005e9, 0x005d0, 0x005d0,
+ 0x005d0, 0x005d1, 0x005d2, 0x005d3, 0x005d4, 0x005d5, 0x005d6, 0x0fb37,
+ 0x005d8, 0x005d9, 0x005da, 0x005da, 0x005dc, 0x0fb3d, 0x005dd, 0x0fb3f,
+ 0x005df, 0x005e1, 0x0fb42, 0x005e3, 0x005e3, 0x0fb45, 0x005e5, 0x005e7,
+ 0x005e8, 0x005e9, 0x005ea, 0x005d5, 0x005d1, 0x005da, 0x005e3, 0x0fb4f,
+ 0x00671, 0x00671, 0x0067b, 0x0067b, 0x0067b, 0x0067b, 0x0067e, 0x0067e,
+ 0x0067e, 0x0067e, 0x00680, 0x00680, 0x00680, 0x00680, 0x0067a, 0x0067a,
+ 0x0067a, 0x0067a, 0x0067f, 0x0067f, 0x0067f, 0x0067f, 0x00679, 0x00679,
+ 0x00679, 0x00679, 0x006a4, 0x006a4, 0x006a4, 0x006a4, 0x006a6, 0x006a6,
+ 0x006a6, 0x006a6, 0x00684, 0x00684, 0x00684, 0x00684, 0x00683, 0x00683,
+ 0x00683, 0x00683, 0x00686, 0x00686, 0x00686, 0x00686, 0x00687, 0x00687,
+ 0x00687, 0x00687, 0x0068d, 0x0068d, 0x0068c, 0x0068c, 0x0068e, 0x0068e,
+ 0x00688, 0x00688, 0x00698, 0x00698, 0x00691, 0x00691, 0x006a9, 0x006a9,
+ 0x006a9, 0x006a9, 0x006af, 0x006af, 0x006af, 0x006af, 0x006b3, 0x006b3,
+ 0x006b3, 0x006b3, 0x006b1, 0x006b1, 0x006b1, 0x006b1, 0x006ba, 0x006ba,
+ 0x006bb, 0x006bb, 0x006bb, 0x006bb, 0x006c0, 0x006c0, 0x006c1, 0x006c1,
+ 0x006c1, 0x006c1, 0x006be, 0x006be, 0x006be, 0x006be, 0x006d2, 0x006d2,
+ 0x006d2, 0x006d2, 0x0fbb2, 0x0fbb3, 0x0fbb4, 0x0fbb5, 0x0fbb6, 0x0fbb7,
+ 0x0fbb8, 0x0fbb9, 0x0fbba, 0x0fbbb, 0x0fbbc, 0x0fbbd, 0x0fbbe, 0x0fbbf,
+ 0x0fbc0, 0x0fbc1, 0x0fbc2, 0x0fbc3, 0x0fbc4, 0x0fbc5, 0x0fbc6, 0x0fbc7,
+ 0x0fbc8, 0x0fbc9, 0x0fbca, 0x0fbcb, 0x0fbcc, 0x0fbcd, 0x0fbce, 0x0fbcf,
+ 0x0fbd0, 0x0fbd1, 0x0fbd2, 0x006ad, 0x006ad, 0x006ad, 0x006ad, 0x006c7,
+ 0x006c7, 0x006c6, 0x006c6, 0x006c8, 0x006c8, 0x00677, 0x006cb, 0x006cb,
+ 0x006c5, 0x006c5, 0x006c9, 0x006c9, 0x006d0, 0x006d0, 0x006d0, 0x006d0,
+ 0x00649, 0x00649, 0x0fbea, 0x0fbea, 0x0fbec, 0x0fbec, 0x0fbee, 0x0fbee,
+ 0x0fbf0, 0x0fbf0, 0x0fbf2, 0x0fbf2, 0x0fbf4, 0x0fbf4, 0x0fbf6, 0x0fbf6,
+ 0x0fbf6, 0x0fbf9, 0x0fbf9, 0x0fbf9, 0x006cc, 0x006cc, 0x006cc, 0x006cc
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_fc[] = {
+ 0x0fc00, 0x0fc01, 0x0fc02, 0x0fbf9, 0x0fc04, 0x0fc05, 0x0fc06, 0x0fc07,
+ 0x0fc08, 0x0fc09, 0x0fc0a, 0x0fc0b, 0x0fc0c, 0x0fc0d, 0x0fc0e, 0x0fc0f,
+ 0x0fc10, 0x0fc11, 0x0fc12, 0x0fc13, 0x0fc14, 0x0fc15, 0x0fc16, 0x0fc17,
+ 0x0fc18, 0x0fc19, 0x0fc1a, 0x0fc1b, 0x0fc1c, 0x0fc1d, 0x0fc1e, 0x0fc1f,
+ 0x0fc20, 0x0fc21, 0x0fc22, 0x0fc23, 0x0fc24, 0x0fc25, 0x0fc26, 0x0fc27,
+ 0x0fc28, 0x0fc29, 0x0fc2a, 0x0fc2b, 0x0fc2c, 0x0fc2d, 0x0fc2e, 0x0fc2f,
+ 0x0fc30, 0x0fc31, 0x0fc32, 0x0fc33, 0x0fc34, 0x0fc35, 0x0fc36, 0x0fc37,
+ 0x0fc38, 0x0fc39, 0x0fc3a, 0x0fc3b, 0x0fc3c, 0x0fc3d, 0x0fc3e, 0x0fc3f,
+ 0x0fc40, 0x0fc41, 0x0fc42, 0x0fc43, 0x0fc44, 0x0fc45, 0x0fc46, 0x0fc47,
+ 0x0fc48, 0x0fc49, 0x0fc4a, 0x0fc4b, 0x0fc4c, 0x0fc4d, 0x0fc4e, 0x0fc4f,
+ 0x0fc50, 0x0fc51, 0x0fc52, 0x0fc53, 0x0fc54, 0x0fc55, 0x0fc56, 0x0fc57,
+ 0x0fc58, 0x0fc59, 0x0fc5a, 0x00630, 0x00631, 0x00649, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x0fc64, 0x0fc65, 0x0fc02, 0x0fc67,
+ 0x0fbf9, 0x0fc04, 0x0fc6a, 0x0fc6b, 0x0fc08, 0x0fc6d, 0x0fc09, 0x0fc0a,
+ 0x0fc70, 0x0fc71, 0x0fc0e, 0x0fc73, 0x0fc0f, 0x0fc10, 0x0fc76, 0x0fc77,
+ 0x0fc12, 0x0fc79, 0x0fc13, 0x0fc14, 0x0fc31, 0x0fc32, 0x0fc35, 0x0fc36,
+ 0x0fc37, 0x0fc3b, 0x0fc3c, 0x0fc3d, 0x0fc3e, 0x0fc42, 0x0fc43, 0x0fc44,
+ 0x0fc88, 0x0fc48, 0x0fc8a, 0x0fc8b, 0x0fc4e, 0x0fc8d, 0x0fc4f, 0x0fc50,
+ 0x00649, 0x0fc91, 0x0fc92, 0x0fc58, 0x0fc94, 0x0fc59, 0x0fc5a, 0x0fc00,
+ 0x0fc01, 0x0fc99, 0x0fc02, 0x0fc9b, 0x0fc05, 0x0fc06, 0x0fc07, 0x0fc08,
+ 0x0fca0, 0x0fc0b, 0x0fc0c, 0x0fc0d, 0x0fc0e, 0x0fca5, 0x0fc12, 0x0fc15,
+ 0x0fc16, 0x0fc17, 0x0fc18, 0x0fc19, 0x0fc1b, 0x0fc1c, 0x0fc1d, 0x0fc1e,
+ 0x0fc1f, 0x0fc20, 0x0fcb2, 0x0fc21, 0x0fc22, 0x0fc23, 0x0fc24, 0x0fc25,
+ 0x0fc26, 0x0fc28, 0x0fc29, 0x0fc2a, 0x0fc2b, 0x0fc2c, 0x0fc2d, 0x0fc2e,
+ 0x0fc2f, 0x0fc30, 0x0fc33, 0x0fc34, 0x0fc38, 0x0fc39, 0x0fc3a, 0x0fc3b,
+ 0x0fc3c, 0x0fc3f, 0x0fc40, 0x0fc41, 0x0fc42, 0x0fccd, 0x0fc45, 0x0fc46,
+ 0x0fc47, 0x0fc48, 0x0fc4b, 0x0fc4c, 0x0fc4d, 0x0fc4e, 0x0fcd6, 0x0fc51,
+ 0x0fc52, 0x00647, 0x0fc55, 0x0fc56, 0x0fc57, 0x0fc58, 0x0fcde, 0x0fc02,
+ 0x0fc9b, 0x0fc08, 0x0fca0, 0x0fc0e, 0x0fca5, 0x0fc12, 0x0fce6, 0x0fc1f,
+ 0x0fce8, 0x0fce9, 0x0fcea, 0x0fc3b, 0x0fc3c, 0x0fc42, 0x0fc4e, 0x0fcd6,
+ 0x0fc58, 0x0fcde, 0x00000, 0x00000, 0x00000, 0x0fcf5, 0x0fcf6, 0x0fcf7,
+ 0x0fcf8, 0x0fcf9, 0x0fcfa, 0x0fcfb, 0x0fcfc, 0x0fcfd, 0x0fcfe, 0x0fcff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_fd[] = {
+ 0x0fd00, 0x0fd01, 0x0fd02, 0x0fd03, 0x0fd04, 0x0fd05, 0x0fd06, 0x0fd07,
+ 0x0fd08, 0x0fd09, 0x0fd0a, 0x0fd0b, 0x0fce9, 0x0fd0d, 0x0fd0e, 0x0fd0f,
+ 0x0fd10, 0x0fcf5, 0x0fcf6, 0x0fcf7, 0x0fcf8, 0x0fcf9, 0x0fcfa, 0x0fcfb,
+ 0x0fcfc, 0x0fcfd, 0x0fcfe, 0x0fd1b, 0x0fd00, 0x0fd01, 0x0fd02, 0x0fd03,
+ 0x0fd04, 0x0fd05, 0x0fd06, 0x0fd07, 0x0fd08, 0x0fd09, 0x0fd0a, 0x0fd0b,
+ 0x0fce9, 0x0fd0d, 0x0fd0e, 0x0fd0f, 0x0fd10, 0x0fd09, 0x0fd0a, 0x0fd0b,
+ 0x0fce9, 0x0fce8, 0x0fcea, 0x0fc27, 0x0fc1c, 0x0fc1d, 0x0fc1e, 0x0fd09,
+ 0x0fd0a, 0x0fd0b, 0x0fc27, 0x0fc28, 0x00627, 0x00627, 0x0fd3e, 0x0fd3f,
+ 0x0fd40, 0x0fd41, 0x0fd42, 0x0fd43, 0x0fd44, 0x0fd45, 0x0fd46, 0x0fd47,
+ 0x0fd48, 0x0fd49, 0x0fd4a, 0x0fd4b, 0x0fd4c, 0x0fd4d, 0x0fd4e, 0x0fd4f,
+ 0x0fd50, 0x0fd51, 0x0fd51, 0x0fd53, 0x0fd54, 0x0fd55, 0x0fd56, 0x0fd57,
+ 0x0fd58, 0x0fd58, 0x0fd5a, 0x0fd5b, 0x0fd5c, 0x0fd5d, 0x0fd5e, 0x0fd5f,
+ 0x0fd5f, 0x0fd61, 0x0fd62, 0x0fd62, 0x0fd64, 0x0fd64, 0x0fd66, 0x0fd67,
+ 0x0fd67, 0x0fd69, 0x0fd6a, 0x0fd6a, 0x0fd6c, 0x0fd6c, 0x0fd6e, 0x0fd6f,
+ 0x0fd6f, 0x0fd71, 0x0fd71, 0x0fd73, 0x0fd74, 0x0fd75, 0x0fd76, 0x0fd76,
+ 0x0fd78, 0x0fd79, 0x0fd7a, 0x0fd7b, 0x0fd7c, 0x0fd7c, 0x0fd7e, 0x0fd7f,
+ 0x0fd80, 0x0fd81, 0x0fd82, 0x0fd83, 0x0fd83, 0x0fd85, 0x0fd85, 0x0fd87,
+ 0x0fd87, 0x0fd89, 0x0fd8a, 0x0fd8b, 0x0fd8c, 0x0fd8d, 0x0fd8e, 0x0fd8f,
+ 0x0fd90, 0x0fd91, 0x0fd92, 0x0fd93, 0x0fd94, 0x0fd95, 0x0fd96, 0x0fd97,
+ 0x0fd97, 0x0fd99, 0x0fd9a, 0x0fd9b, 0x0fd9c, 0x0fd9c, 0x0fd9e, 0x0fd9f,
+ 0x0fda0, 0x0fda1, 0x0fda2, 0x0fda3, 0x0fda4, 0x0fda5, 0x0fda6, 0x0fda7,
+ 0x0fda8, 0x0fda9, 0x0fdaa, 0x0fdab, 0x0fdac, 0x0fdad, 0x0fdae, 0x0fdaf,
+ 0x0fdb0, 0x0fdb1, 0x0fdb2, 0x0fdb3, 0x0fd7e, 0x0fd80, 0x0fdb6, 0x0fdb7,
+ 0x0fdb8, 0x0fdb9, 0x0fdba, 0x0fdbb, 0x0fdba, 0x0fdb8, 0x0fdbe, 0x0fdbf,
+ 0x0fdc0, 0x0fdc1, 0x0fdc2, 0x0fdbb, 0x0fd75, 0x0fd66, 0x0fdc6, 0x0fdc7,
+ 0x0fdc8, 0x0fdc9, 0x0fdca, 0x0fdcb, 0x0fdcc, 0x0fdcd, 0x0fdce, 0x0fdcf,
+ 0x0fdd0, 0x0fdd1, 0x0fdd2, 0x0fdd3, 0x0fdd4, 0x0fdd5, 0x0fdd6, 0x0fdd7,
+ 0x0fdd8, 0x0fdd9, 0x0fdda, 0x0fddb, 0x0fddc, 0x0fddd, 0x0fdde, 0x0fddf,
+ 0x0fde0, 0x0fde1, 0x0fde2, 0x0fde3, 0x0fde4, 0x0fde5, 0x0fde6, 0x0fde7,
+ 0x0fde8, 0x0fde9, 0x0fdea, 0x0fdeb, 0x0fdec, 0x0fded, 0x0fdee, 0x0fdef,
+ 0x0fdf0, 0x0fdf1, 0x0fdf2, 0x0fdf3, 0x0fdf4, 0x0fdf5, 0x0fdf6, 0x0fdf7,
+ 0x0fdf8, 0x0fdf9, 0x0fdfa, 0x0fdfb, 0x0fdfc, 0x0fdfd, 0x0fdfe, 0x0fdff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_fe[] = {
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x0fe10, 0x0fe11, 0x0fe12, 0x0fe13, 0x0fe14, 0x0fe15, 0x0fe16, 0x0fe17,
+ 0x0fe18, 0x0fe19, 0x0fe1a, 0x0fe1b, 0x0fe1c, 0x0fe1d, 0x0fe1e, 0x0fe1f,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x0fe24, 0x0fe25, 0x0fe26, 0x0fe27,
+ 0x0fe28, 0x0fe29, 0x0fe2a, 0x0fe2b, 0x0fe2c, 0x0fe2d, 0x0fe2e, 0x0fe2f,
+ 0x02025, 0x02014, 0x02013, 0x0005f, 0x0005f, 0x00028, 0x00029, 0x0007b,
+ 0x0007d, 0x03014, 0x03015, 0x03010, 0x03011, 0x0300a, 0x0300b, 0x02329,
+ 0x0232a, 0x0300c, 0x0300d, 0x0300e, 0x0300f, 0x0fe45, 0x0fe46, 0x0005b,
+ 0x0005d, 0x0203e, 0x0203e, 0x0203e, 0x0203e, 0x0005f, 0x0005f, 0x0005f,
+ 0x0002c, 0x03001, 0x0002e, 0x0fe53, 0x0003b, 0x0003a, 0x0003f, 0x00021,
+ 0x02014, 0x00028, 0x00029, 0x0007b, 0x0007d, 0x03014, 0x03015, 0x00023,
+ 0x00026, 0x0002a, 0x0002b, 0x0002d, 0x0003c, 0x0003e, 0x0003d, 0x0fe67,
+ 0x0005c, 0x00024, 0x00025, 0x00040, 0x0fe6c, 0x0fe6d, 0x0fe6e, 0x0fe6f,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x0fe75, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00621, 0x00622, 0x00622, 0x00623, 0x00623, 0x00624, 0x00624, 0x00625,
+ 0x00625, 0x00626, 0x00626, 0x00626, 0x00626, 0x00627, 0x00627, 0x00628,
+ 0x00628, 0x00628, 0x00628, 0x00629, 0x00629, 0x0062a, 0x0062a, 0x0062a,
+ 0x0062a, 0x0062b, 0x0062b, 0x0062b, 0x0062b, 0x0062c, 0x0062c, 0x0062c,
+ 0x0062c, 0x0062d, 0x0062d, 0x0062d, 0x0062d, 0x0062e, 0x0062e, 0x0062e,
+ 0x0062e, 0x0062f, 0x0062f, 0x00630, 0x00630, 0x00631, 0x00631, 0x00632,
+ 0x00632, 0x00633, 0x00633, 0x00633, 0x00633, 0x00634, 0x00634, 0x00634,
+ 0x00634, 0x00635, 0x00635, 0x00635, 0x00635, 0x00636, 0x00636, 0x00636,
+ 0x00636, 0x00637, 0x00637, 0x00637, 0x00637, 0x00638, 0x00638, 0x00638,
+ 0x00638, 0x00639, 0x00639, 0x00639, 0x00639, 0x0063a, 0x0063a, 0x0063a,
+ 0x0063a, 0x00641, 0x00641, 0x00641, 0x00641, 0x00642, 0x00642, 0x00642,
+ 0x00642, 0x00643, 0x00643, 0x00643, 0x00643, 0x00644, 0x00644, 0x00644,
+ 0x00644, 0x00645, 0x00645, 0x00645, 0x00645, 0x00646, 0x00646, 0x00646,
+ 0x00646, 0x00647, 0x00647, 0x00647, 0x00647, 0x00648, 0x00648, 0x00649,
+ 0x00649, 0x0064a, 0x0064a, 0x0064a, 0x0064a, 0x0fef5, 0x0fef5, 0x0fef7,
+ 0x0fef7, 0x0fef9, 0x0fef9, 0x0fefb, 0x0fefb, 0x0fefd, 0x0fefe, 0x0feff
+};
+
+static uint32_t unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_ff[] = {
+ 0x0ff00, 0x00021, 0x00022, 0x00023, 0x00024, 0x00025, 0x00026, 0x00027,
+ 0x00028, 0x00029, 0x0002a, 0x0002b, 0x0002c, 0x0002d, 0x0002e, 0x0002f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0003a, 0x0003b, 0x0003c, 0x0003d, 0x0003e, 0x0003f,
+ 0x00040, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047,
+ 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f,
+ 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057,
+ 0x00058, 0x00059, 0x0005a, 0x0005b, 0x0005c, 0x0005d, 0x0005e, 0x0005f,
+ 0x00060, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047,
+ 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f,
+ 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057,
+ 0x00058, 0x00059, 0x0005a, 0x0007b, 0x0007c, 0x0007d, 0x0007e, 0x02985,
+ 0x02986, 0x03002, 0x0300c, 0x0300d, 0x03001, 0x030fb, 0x03092, 0x03041,
+ 0x03043, 0x03045, 0x03047, 0x03049, 0x03083, 0x03085, 0x03087, 0x03063,
+ 0x030fc, 0x03042, 0x03044, 0x03046, 0x03048, 0x0304a, 0x0304b, 0x0304d,
+ 0x0304f, 0x03051, 0x03053, 0x03055, 0x03057, 0x03059, 0x0305b, 0x0305d,
+ 0x0305f, 0x03061, 0x03064, 0x03066, 0x03068, 0x0306a, 0x0306b, 0x0306c,
+ 0x0306d, 0x0306e, 0x0306f, 0x03072, 0x03075, 0x03078, 0x0307b, 0x0307e,
+ 0x0307f, 0x03080, 0x03081, 0x03082, 0x03084, 0x03086, 0x03088, 0x03089,
+ 0x0308a, 0x0308b, 0x0308c, 0x0308d, 0x0308f, 0x03093, 0x00000, 0x00000,
+ 0x01160, 0x01100, 0x01101, 0x011aa, 0x01102, 0x011ac, 0x011ad, 0x01103,
+ 0x01104, 0x01105, 0x011b0, 0x011b1, 0x011b2, 0x011b3, 0x011b4, 0x011b5,
+ 0x0111a, 0x01106, 0x01107, 0x01108, 0x01121, 0x01109, 0x0110a, 0x0110b,
+ 0x0110c, 0x0110d, 0x0110e, 0x0110f, 0x01110, 0x01111, 0x01112, 0x0ffbf,
+ 0x0ffc0, 0x0ffc1, 0x01161, 0x01162, 0x01163, 0x01164, 0x01165, 0x01166,
+ 0x0ffc8, 0x0ffc9, 0x01167, 0x01168, 0x01169, 0x0116a, 0x0116b, 0x0116c,
+ 0x0ffd0, 0x0ffd1, 0x0116d, 0x0116e, 0x0116f, 0x01170, 0x01171, 0x01172,
+ 0x0ffd8, 0x0ffd9, 0x01173, 0x01174, 0x01175, 0x0ffdd, 0x0ffde, 0x0ffdf,
+ 0x000a2, 0x000a3, 0x000ac, 0x000af, 0x000a6, 0x000a5, 0x020a9, 0x0ffe7,
+ 0x02502, 0x02190, 0x02191, 0x02192, 0x02193, 0x025a0, 0x025cb, 0x0ffef,
+ 0x0fff0, 0x0fff1, 0x0fff2, 0x0fff3, 0x0fff4, 0x0fff5, 0x0fff6, 0x0fff7,
+ 0x0fff8, 0x00000, 0x00000, 0x00000, 0x0fffc, 0x0fffd, 0x0fffe, 0x0ffff
+};
+
+static uint32_t *unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_table[256] = {
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_00, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_01,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_02, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_03,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_04, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_05,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_06, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_07,
+ NULL, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_09,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_0a, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_0b,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_0c, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_0d,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_0e, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_0f,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_10, NULL,
+ NULL, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_13,
+ NULL, NULL,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_16, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_17,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_18, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_19,
+ NULL, NULL,
+ NULL, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_1d,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_1e, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_1f,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_20, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_21,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_22, NULL,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_24, NULL,
+ NULL, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_27,
+ NULL, NULL,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_2a, NULL,
+ NULL, NULL,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_2e, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_2f,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_30, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_31,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_32, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_33,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_f9,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_fa, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_fb,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_fc, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_fd,
+ unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_fe, unicode_ci_except_kana_ci_kana_with_voiced_sound_mark_page_ff
+};
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_unicode_ci_table.h b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_unicode_ci_table.h
new file mode 100644
index 00000000000..861c2029e68
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/normalizers/mysql_unicode_ci_table.h
@@ -0,0 +1,1685 @@
+/*
+ Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; version 2
+ of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301, USA
+
+ This file uses normalization table defined in
+ mysql-5.5.29/strings/ctype-uca.c.
+ The following is the header of the file:
+
+ Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; version 2
+ of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301, USA
+
+ UCA (Unicode Collation Algorithm) support.
+ Written by Alexander Barkov <bar@mysql.com>
+*/
+
+#ifndef MYSQL_UCA_H
+#define MYSQL_UCA_H
+
+#include <stdint.h>
+
+static uint32_t unicode_ci_page_00[] = {
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00009, 0x0000a, 0x0000b, 0x0000c, 0x0000d, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00020, 0x00021, 0x00022, 0x00023, 0x00024, 0x00025, 0x00026, 0x00027,
+ 0x00028, 0x00029, 0x0002a, 0x0002b, 0x0002c, 0x0002d, 0x0002e, 0x0002f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0003a, 0x0003b, 0x0003c, 0x0003d, 0x0003e, 0x0003f,
+ 0x00040, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047,
+ 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f,
+ 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057,
+ 0x00058, 0x00059, 0x0005a, 0x0005b, 0x0005c, 0x0005d, 0x0005e, 0x0005f,
+ 0x00060, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047,
+ 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f,
+ 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057,
+ 0x00058, 0x00059, 0x0005a, 0x0007b, 0x0007c, 0x0007d, 0x0007e, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00085, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00020, 0x000a1, 0x000a2, 0x000a3, 0x000a4, 0x000a5, 0x000a6, 0x000a7,
+ 0x000a8, 0x000a9, 0x00041, 0x000ab, 0x000ac, 0x000ad, 0x000ae, 0x000af,
+ 0x000b0, 0x000b1, 0x00032, 0x00033, 0x000b4, 0x0039c, 0x000b6, 0x000b7,
+ 0x000b8, 0x00031, 0x0004f, 0x000bb, 0x000bc, 0x000bd, 0x000be, 0x000bf,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x000c6, 0x00043,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00049, 0x00049, 0x00049, 0x00049,
+ 0x000d0, 0x0004e, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x000d7,
+ 0x000d8, 0x00055, 0x00055, 0x00055, 0x00055, 0x00059, 0x000de, 0x000df,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x000c6, 0x00043,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00049, 0x00049, 0x00049, 0x00049,
+ 0x000d0, 0x0004e, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x000f7,
+ 0x000d8, 0x00055, 0x00055, 0x00055, 0x00055, 0x00059, 0x000de, 0x00059
+};
+
+static uint32_t unicode_ci_page_01[] = {
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00043, 0x00043,
+ 0x00043, 0x00043, 0x00043, 0x00043, 0x00043, 0x00043, 0x00044, 0x00044,
+ 0x00110, 0x00110, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00047, 0x00047, 0x00047, 0x00047,
+ 0x00047, 0x00047, 0x00047, 0x00047, 0x00048, 0x00048, 0x00126, 0x00126,
+ 0x00049, 0x00049, 0x00049, 0x00049, 0x00049, 0x00049, 0x00049, 0x00049,
+ 0x00049, 0x00131, 0x00132, 0x00132, 0x0004a, 0x0004a, 0x0004b, 0x0004b,
+ 0x00138, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0013f,
+ 0x0013f, 0x00141, 0x00141, 0x0004e, 0x0004e, 0x0004e, 0x0004e, 0x0004e,
+ 0x0004e, 0x00149, 0x0014a, 0x0014a, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x00152, 0x00152, 0x00052, 0x00052, 0x00052, 0x00052,
+ 0x00052, 0x00052, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053,
+ 0x00053, 0x00053, 0x00054, 0x00054, 0x00054, 0x00054, 0x00166, 0x00166,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00057, 0x00057, 0x00059, 0x00059,
+ 0x00059, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x00053,
+ 0x00180, 0x00181, 0x00182, 0x00182, 0x00184, 0x00184, 0x00186, 0x00187,
+ 0x00187, 0x00189, 0x0018a, 0x0018b, 0x0018b, 0x0018d, 0x0018e, 0x0018f,
+ 0x00190, 0x00191, 0x00191, 0x00193, 0x00194, 0x00195, 0x00196, 0x00197,
+ 0x00198, 0x00198, 0x0019a, 0x0019b, 0x0019c, 0x0019d, 0x0019e, 0x0019f,
+ 0x0004f, 0x0004f, 0x001a2, 0x001a2, 0x001a4, 0x001a4, 0x001a6, 0x001a7,
+ 0x001a7, 0x001a9, 0x001aa, 0x001ab, 0x001ac, 0x001ac, 0x001ae, 0x00055,
+ 0x00055, 0x001b1, 0x001b2, 0x001b3, 0x001b3, 0x001b5, 0x001b5, 0x001b7,
+ 0x001b8, 0x001b8, 0x001ba, 0x001bb, 0x001bc, 0x001bc, 0x001be, 0x001bf,
+ 0x001c0, 0x001c1, 0x001c2, 0x001c3, 0x001c4, 0x001c4, 0x001c4, 0x001c7,
+ 0x001c7, 0x001c7, 0x001ca, 0x001ca, 0x001ca, 0x00041, 0x00041, 0x00049,
+ 0x00049, 0x0004f, 0x0004f, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x0018e, 0x00041, 0x00041,
+ 0x00041, 0x00041, 0x000c6, 0x000c6, 0x001e4, 0x001e4, 0x00047, 0x00047,
+ 0x0004b, 0x0004b, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x001b7, 0x001b7,
+ 0x0004a, 0x001c4, 0x001c4, 0x001c4, 0x00047, 0x00047, 0x00195, 0x001bf,
+ 0x0004e, 0x0004e, 0x00041, 0x00041, 0x000c6, 0x000c6, 0x000d8, 0x001ff
+};
+
+static uint32_t unicode_ci_page_02[] = {
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00049, 0x00049, 0x00049, 0x00049, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x00052, 0x00052, 0x00052, 0x00052, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00053, 0x00053, 0x00054, 0x00054, 0x0021c, 0x0021c, 0x00048, 0x00048,
+ 0x0019e, 0x00221, 0x00222, 0x00222, 0x00224, 0x00224, 0x00041, 0x00041,
+ 0x00045, 0x00045, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x00059, 0x00059, 0x00234, 0x00235, 0x00236, 0x00237,
+ 0x00238, 0x00239, 0x0023a, 0x0023b, 0x0023c, 0x0023d, 0x0023e, 0x0023f,
+ 0x00240, 0x00241, 0x00242, 0x00243, 0x00244, 0x00245, 0x00246, 0x00247,
+ 0x00248, 0x00249, 0x0024a, 0x0024b, 0x0024c, 0x0024d, 0x0024e, 0x0024f,
+ 0x00250, 0x00251, 0x00252, 0x00181, 0x00186, 0x00255, 0x00189, 0x0018a,
+ 0x00258, 0x0018f, 0x0025a, 0x00190, 0x0025c, 0x0025d, 0x0025e, 0x0025f,
+ 0x00193, 0x00261, 0x00262, 0x00194, 0x00264, 0x00265, 0x00266, 0x00267,
+ 0x00197, 0x00196, 0x0026a, 0x0026b, 0x0026c, 0x0026d, 0x0026e, 0x0019c,
+ 0x00270, 0x00271, 0x0019d, 0x00273, 0x00274, 0x0019f, 0x00276, 0x00277,
+ 0x00278, 0x00279, 0x0027a, 0x0027b, 0x0027c, 0x0027d, 0x0027e, 0x0027f,
+ 0x001a6, 0x00281, 0x00282, 0x001a9, 0x00284, 0x00285, 0x00286, 0x00287,
+ 0x001ae, 0x00289, 0x001b1, 0x001b2, 0x0028c, 0x0028d, 0x0028e, 0x0028f,
+ 0x00290, 0x00291, 0x001b7, 0x00293, 0x00294, 0x00295, 0x00296, 0x00297,
+ 0x00298, 0x00299, 0x0029a, 0x0029b, 0x0029c, 0x0029d, 0x0029e, 0x0029f,
+ 0x002a0, 0x002a1, 0x002a2, 0x001c4, 0x002a4, 0x002a5, 0x001be, 0x002a7,
+ 0x002a8, 0x002a9, 0x002aa, 0x002ab, 0x002ac, 0x002ad, 0x002ae, 0x002af,
+ 0x00048, 0x00266, 0x0004a, 0x00052, 0x00279, 0x0027b, 0x00281, 0x00057,
+ 0x00059, 0x002b9, 0x002ba, 0x002bb, 0x002bc, 0x002bd, 0x002be, 0x002bf,
+ 0x002c0, 0x002c1, 0x002c2, 0x002c3, 0x002c4, 0x002c5, 0x002c6, 0x002c7,
+ 0x002c8, 0x002c9, 0x002ca, 0x002cb, 0x002cc, 0x002cd, 0x002ce, 0x002cf,
+ 0x002d0, 0x002d1, 0x002d2, 0x002d3, 0x002d4, 0x002d5, 0x002d6, 0x002d7,
+ 0x002d8, 0x002d9, 0x002da, 0x002db, 0x002dc, 0x002dd, 0x002de, 0x002df,
+ 0x00194, 0x0004c, 0x00053, 0x00058, 0x00295, 0x002e5, 0x002e6, 0x002e7,
+ 0x002e8, 0x002e9, 0x002ea, 0x002eb, 0x002ec, 0x002ed, 0x002ee, 0x002ef,
+ 0x002f0, 0x002f1, 0x002f2, 0x002f3, 0x002f4, 0x002f5, 0x002f6, 0x002f7,
+ 0x002f8, 0x002f9, 0x002fa, 0x002fb, 0x002fc, 0x002fd, 0x002fe, 0x002ff
+};
+
+static uint32_t unicode_ci_page_03[] = {
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00358, 0x00359, 0x0035a, 0x0035b, 0x0035c, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00041, 0x00045, 0x00049, 0x0004f, 0x00055,
+ 0x00043, 0x00044, 0x00048, 0x0004d, 0x00052, 0x00054, 0x00056, 0x00058,
+ 0x00370, 0x00371, 0x00372, 0x00373, 0x002b9, 0x00375, 0x00376, 0x00377,
+ 0x00378, 0x00379, 0x00399, 0x0037b, 0x0037c, 0x0037d, 0x0003b, 0x0037f,
+ 0x00380, 0x00381, 0x00382, 0x00383, 0x000b4, 0x000a8, 0x00391, 0x000b7,
+ 0x00395, 0x00397, 0x00399, 0x0038b, 0x0039f, 0x0038d, 0x003a5, 0x003a9,
+ 0x00399, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397,
+ 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f,
+ 0x003a0, 0x003a1, 0x003a2, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7,
+ 0x003a8, 0x003a9, 0x00399, 0x003a5, 0x00391, 0x00395, 0x00397, 0x00399,
+ 0x003a5, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397,
+ 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f,
+ 0x003a0, 0x003a1, 0x003a3, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7,
+ 0x003a8, 0x003a9, 0x00399, 0x003a5, 0x0039f, 0x003a5, 0x003a9, 0x003cf,
+ 0x00392, 0x00398, 0x003a5, 0x003a5, 0x003a5, 0x003a6, 0x003a0, 0x003d7,
+ 0x003d8, 0x003d8, 0x003da, 0x003da, 0x003dc, 0x003dc, 0x003de, 0x003de,
+ 0x003e0, 0x003e0, 0x003e2, 0x003e2, 0x003e4, 0x003e4, 0x003e6, 0x003e6,
+ 0x003e8, 0x003e8, 0x003ea, 0x003ea, 0x003ec, 0x003ec, 0x003ee, 0x003ee,
+ 0x0039a, 0x003a1, 0x003a3, 0x003f3, 0x00398, 0x00395, 0x003f6, 0x003f7,
+ 0x003f7, 0x003a3, 0x003fa, 0x003fa, 0x003fc, 0x003fd, 0x003fe, 0x003ff
+};
+
+static uint32_t unicode_ci_page_04[] = {
+ 0x00400, 0x00400, 0x00402, 0x00403, 0x00404, 0x00405, 0x00406, 0x00407,
+ 0x00408, 0x00409, 0x0040a, 0x0040b, 0x0040c, 0x0040d, 0x0040e, 0x0040f,
+ 0x00410, 0x00411, 0x00412, 0x00413, 0x00414, 0x00400, 0x00416, 0x00417,
+ 0x0040d, 0x00419, 0x0041a, 0x0041b, 0x0041c, 0x0041d, 0x0041e, 0x0041f,
+ 0x00420, 0x00421, 0x00422, 0x00423, 0x00424, 0x00425, 0x00426, 0x00427,
+ 0x00428, 0x00429, 0x0042a, 0x0042b, 0x0042c, 0x0042d, 0x0042e, 0x0042f,
+ 0x00410, 0x00411, 0x00412, 0x00413, 0x00414, 0x00400, 0x00416, 0x00417,
+ 0x0040d, 0x00419, 0x0041a, 0x0041b, 0x0041c, 0x0041d, 0x0041e, 0x0041f,
+ 0x00420, 0x00421, 0x00422, 0x00423, 0x00424, 0x00425, 0x00426, 0x00427,
+ 0x00428, 0x00429, 0x0042a, 0x0042b, 0x0042c, 0x0042d, 0x0042e, 0x0042f,
+ 0x00400, 0x00400, 0x00402, 0x00403, 0x00404, 0x00405, 0x00406, 0x00407,
+ 0x00408, 0x00409, 0x0040a, 0x0040b, 0x0040c, 0x0040d, 0x0040e, 0x0040f,
+ 0x00460, 0x00460, 0x00462, 0x00462, 0x00464, 0x00464, 0x00466, 0x00466,
+ 0x00468, 0x00468, 0x0046a, 0x0046a, 0x0046c, 0x0046c, 0x0046e, 0x0046e,
+ 0x00470, 0x00470, 0x00472, 0x00472, 0x00474, 0x00474, 0x00476, 0x00476,
+ 0x00478, 0x00478, 0x0047a, 0x0047a, 0x0047c, 0x0047c, 0x0047e, 0x0047e,
+ 0x00480, 0x00480, 0x00482, 0x00000, 0x00000, 0x00000, 0x00000, 0x00487,
+ 0x00000, 0x00000, 0x0048a, 0x0048a, 0x0048c, 0x0048c, 0x0048e, 0x0048e,
+ 0x00413, 0x00413, 0x00492, 0x00492, 0x00494, 0x00494, 0x00496, 0x00496,
+ 0x00498, 0x00498, 0x0049a, 0x0049a, 0x0049c, 0x0049c, 0x0049e, 0x0049e,
+ 0x004a0, 0x004a0, 0x004a2, 0x004a2, 0x004a4, 0x004a4, 0x004a6, 0x004a6,
+ 0x004a8, 0x004a8, 0x004aa, 0x004aa, 0x004ac, 0x004ac, 0x004ae, 0x004ae,
+ 0x004b0, 0x004b0, 0x004b2, 0x004b2, 0x004b4, 0x004b4, 0x004b6, 0x004b6,
+ 0x004b8, 0x004b8, 0x004ba, 0x004ba, 0x004bc, 0x004bc, 0x004be, 0x004be,
+ 0x004c0, 0x00416, 0x00416, 0x004c3, 0x004c3, 0x004c5, 0x004c5, 0x004c7,
+ 0x004c7, 0x004c9, 0x004c9, 0x004cb, 0x004cb, 0x004cd, 0x004cd, 0x004cf,
+ 0x004d0, 0x004d0, 0x004d2, 0x004d2, 0x004d4, 0x004d4, 0x004d6, 0x004d6,
+ 0x004d8, 0x004d8, 0x004da, 0x004da, 0x004dc, 0x004dc, 0x004de, 0x004de,
+ 0x004e0, 0x004e0, 0x0040d, 0x0040d, 0x004e4, 0x004e4, 0x004e6, 0x004e6,
+ 0x004e8, 0x004e8, 0x004ea, 0x004ea, 0x004ec, 0x004ec, 0x00423, 0x00423,
+ 0x004f0, 0x004f0, 0x004f2, 0x004f2, 0x004f4, 0x004f4, 0x004f6, 0x004f7,
+ 0x004f8, 0x004f8, 0x004fa, 0x004fb, 0x004fc, 0x004fd, 0x004fe, 0x004ff
+};
+
+static uint32_t unicode_ci_page_05[] = {
+ 0x00500, 0x00500, 0x00502, 0x00502, 0x00504, 0x00504, 0x00506, 0x00506,
+ 0x00508, 0x00508, 0x0050a, 0x0050a, 0x0050c, 0x0050c, 0x0050e, 0x0050e,
+ 0x00510, 0x00511, 0x00512, 0x00513, 0x00514, 0x00515, 0x00516, 0x00517,
+ 0x00518, 0x00519, 0x0051a, 0x0051b, 0x0051c, 0x0051d, 0x0051e, 0x0051f,
+ 0x00520, 0x00521, 0x00522, 0x00523, 0x00524, 0x00525, 0x00526, 0x00527,
+ 0x00528, 0x00529, 0x0052a, 0x0052b, 0x0052c, 0x0052d, 0x0052e, 0x0052f,
+ 0x00530, 0x00531, 0x00532, 0x00533, 0x00534, 0x00535, 0x00536, 0x00537,
+ 0x00538, 0x00539, 0x0053a, 0x0053b, 0x0053c, 0x0053d, 0x0053e, 0x0053f,
+ 0x00540, 0x00541, 0x00542, 0x00543, 0x00544, 0x00545, 0x00546, 0x00547,
+ 0x00548, 0x00549, 0x0054a, 0x0054b, 0x0054c, 0x0054d, 0x0054e, 0x0054f,
+ 0x00550, 0x00551, 0x00552, 0x00553, 0x00554, 0x00555, 0x00556, 0x00557,
+ 0x00558, 0x00559, 0x0055a, 0x0055b, 0x0055c, 0x0055d, 0x0055e, 0x0055f,
+ 0x00560, 0x00531, 0x00532, 0x00533, 0x00534, 0x00535, 0x00536, 0x00537,
+ 0x00538, 0x00539, 0x0053a, 0x0053b, 0x0053c, 0x0053d, 0x0053e, 0x0053f,
+ 0x00540, 0x00541, 0x00542, 0x00543, 0x00544, 0x00545, 0x00546, 0x00547,
+ 0x00548, 0x00549, 0x0054a, 0x0054b, 0x0054c, 0x0054d, 0x0054e, 0x0054f,
+ 0x00550, 0x00551, 0x00552, 0x00553, 0x00554, 0x00555, 0x00556, 0x00587,
+ 0x00588, 0x00589, 0x0058a, 0x0058b, 0x0058c, 0x0058d, 0x0058e, 0x0058f,
+ 0x00590, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x005a2, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x005ba, 0x00000, 0x00000, 0x00000, 0x005be, 0x00000,
+ 0x005c0, 0x00000, 0x00000, 0x005c3, 0x00000, 0x005c5, 0x005c6, 0x005c7,
+ 0x005c8, 0x005c9, 0x005ca, 0x005cb, 0x005cc, 0x005cd, 0x005ce, 0x005cf,
+ 0x005d0, 0x005d1, 0x005d2, 0x005d3, 0x005d4, 0x005d5, 0x005d6, 0x005d7,
+ 0x005d8, 0x005d9, 0x005da, 0x005da, 0x005dc, 0x005dd, 0x005dd, 0x005df,
+ 0x005df, 0x005e1, 0x005e2, 0x005e3, 0x005e3, 0x005e5, 0x005e5, 0x005e7,
+ 0x005e8, 0x005e9, 0x005ea, 0x005eb, 0x005ec, 0x005ed, 0x005ee, 0x005ef,
+ 0x005f0, 0x005f1, 0x005f2, 0x005f3, 0x005f4, 0x005f5, 0x005f6, 0x005f7,
+ 0x005f8, 0x005f9, 0x005fa, 0x005fb, 0x005fc, 0x005fd, 0x005fe, 0x005ff
+};
+
+static uint32_t unicode_ci_page_06[] = {
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00604, 0x00605, 0x00606, 0x00607,
+ 0x00608, 0x00609, 0x0060a, 0x0060b, 0x0060c, 0x0060d, 0x0060e, 0x0060f,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00616, 0x00617,
+ 0x00618, 0x00619, 0x0061a, 0x0061b, 0x0061c, 0x0061d, 0x0061e, 0x0061f,
+ 0x00620, 0x00621, 0x00622, 0x00623, 0x00624, 0x00625, 0x00626, 0x00627,
+ 0x00628, 0x00629, 0x0062a, 0x0062b, 0x0062c, 0x0062d, 0x0062e, 0x0062f,
+ 0x00630, 0x00631, 0x00632, 0x00633, 0x00634, 0x00635, 0x00636, 0x00637,
+ 0x00638, 0x00639, 0x0063a, 0x0063b, 0x0063c, 0x0063d, 0x0063e, 0x0063f,
+ 0x00640, 0x00641, 0x00642, 0x00643, 0x00644, 0x00645, 0x00646, 0x00647,
+ 0x00648, 0x00649, 0x0064a, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00659, 0x0065a, 0x0065b, 0x0065c, 0x0065d, 0x0065e, 0x0065f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0066a, 0x0066b, 0x0066c, 0x0066d, 0x0066e, 0x0066f,
+ 0x00000, 0x00671, 0x00672, 0x00673, 0x00621, 0x00675, 0x00676, 0x00677,
+ 0x00678, 0x00679, 0x0067a, 0x0067b, 0x0067c, 0x0067d, 0x0067e, 0x0067f,
+ 0x00680, 0x00681, 0x00682, 0x00683, 0x00684, 0x00685, 0x00686, 0x00687,
+ 0x00688, 0x00689, 0x0068a, 0x0068b, 0x0068c, 0x0068d, 0x0068e, 0x0068f,
+ 0x00690, 0x00691, 0x00692, 0x00693, 0x00694, 0x00695, 0x00696, 0x00697,
+ 0x00698, 0x00699, 0x0069a, 0x0069b, 0x0069c, 0x0069d, 0x0069e, 0x0069f,
+ 0x006a0, 0x006a1, 0x006a2, 0x006a3, 0x006a4, 0x006a5, 0x006a6, 0x006a7,
+ 0x006a8, 0x006a9, 0x006aa, 0x006ab, 0x006ac, 0x006ad, 0x006ae, 0x006af,
+ 0x006b0, 0x006b1, 0x006b2, 0x006b3, 0x006b4, 0x006b5, 0x006b6, 0x006b7,
+ 0x006b8, 0x006b9, 0x006ba, 0x006bb, 0x006bc, 0x006bd, 0x006be, 0x006bf,
+ 0x006c0, 0x006c1, 0x006c1, 0x006c3, 0x006c4, 0x006c5, 0x006c6, 0x006c7,
+ 0x006c8, 0x006c9, 0x006ca, 0x006cb, 0x006cc, 0x006cd, 0x006ce, 0x006cf,
+ 0x006d0, 0x006d1, 0x006d2, 0x006d2, 0x006d4, 0x006c0, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00648, 0x0064a, 0x00000,
+ 0x00000, 0x006e9, 0x00000, 0x00000, 0x00000, 0x00000, 0x006ee, 0x006ef,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x006fa, 0x006fb, 0x006fc, 0x00621, 0x00645, 0x006ff
+};
+
+static uint32_t unicode_ci_page_07[] = {
+ 0x00700, 0x00701, 0x00702, 0x00703, 0x00704, 0x00705, 0x00706, 0x00707,
+ 0x00708, 0x00709, 0x0070a, 0x0070b, 0x0070c, 0x0070d, 0x0070e, 0x00000,
+ 0x00710, 0x00000, 0x00712, 0x00713, 0x00713, 0x00715, 0x00716, 0x00717,
+ 0x00718, 0x00719, 0x0071a, 0x0071b, 0x0071b, 0x0071d, 0x0071e, 0x0071f,
+ 0x00720, 0x00721, 0x00722, 0x00723, 0x00723, 0x00725, 0x00726, 0x00726,
+ 0x00728, 0x00729, 0x0072a, 0x0072b, 0x0072c, 0x00712, 0x00713, 0x00715,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x0074b, 0x0074c, 0x0074d, 0x0074e, 0x0074f,
+ 0x00750, 0x00751, 0x00752, 0x00753, 0x00754, 0x00755, 0x00756, 0x00757,
+ 0x00758, 0x00759, 0x0075a, 0x0075b, 0x0075c, 0x0075d, 0x0075e, 0x0075f,
+ 0x00760, 0x00761, 0x00762, 0x00763, 0x00764, 0x00765, 0x00766, 0x00767,
+ 0x00768, 0x00769, 0x0076a, 0x0076b, 0x0076c, 0x0076d, 0x0076e, 0x0076f,
+ 0x00770, 0x00771, 0x00772, 0x00773, 0x00774, 0x00775, 0x00776, 0x00777,
+ 0x00778, 0x00779, 0x0077a, 0x0077b, 0x0077c, 0x0077d, 0x0077e, 0x0077f,
+ 0x00780, 0x00781, 0x00782, 0x00783, 0x00784, 0x00785, 0x00786, 0x00787,
+ 0x00788, 0x00789, 0x0078a, 0x0078b, 0x0078c, 0x0078d, 0x0078e, 0x0078f,
+ 0x00790, 0x00791, 0x00792, 0x00793, 0x00794, 0x00795, 0x00796, 0x00797,
+ 0x00798, 0x00799, 0x0079a, 0x0079b, 0x0079c, 0x0079d, 0x0079e, 0x0079f,
+ 0x007a0, 0x007a1, 0x007a2, 0x007a3, 0x007a4, 0x007a5, 0x007a6, 0x007a7,
+ 0x007a8, 0x007a9, 0x007aa, 0x007ab, 0x007ac, 0x007ad, 0x007ae, 0x007af,
+ 0x007b0, 0x007b1, 0x007b2, 0x007b3, 0x007b4, 0x007b5, 0x007b6, 0x007b7,
+ 0x007b8, 0x007b9, 0x007ba, 0x007bb, 0x007bc, 0x007bd, 0x007be, 0x007bf,
+ 0x007c0, 0x007c1, 0x007c2, 0x007c3, 0x007c4, 0x007c5, 0x007c6, 0x007c7,
+ 0x007c8, 0x007c9, 0x007ca, 0x007cb, 0x007cc, 0x007cd, 0x007ce, 0x007cf,
+ 0x007d0, 0x007d1, 0x007d2, 0x007d3, 0x007d4, 0x007d5, 0x007d6, 0x007d7,
+ 0x007d8, 0x007d9, 0x007da, 0x007db, 0x007dc, 0x007dd, 0x007de, 0x007df,
+ 0x007e0, 0x007e1, 0x007e2, 0x007e3, 0x007e4, 0x007e5, 0x007e6, 0x007e7,
+ 0x007e8, 0x007e9, 0x007ea, 0x007eb, 0x007ec, 0x007ed, 0x007ee, 0x007ef,
+ 0x007f0, 0x007f1, 0x007f2, 0x007f3, 0x007f4, 0x007f5, 0x007f6, 0x007f7,
+ 0x007f8, 0x007f9, 0x007fa, 0x007fb, 0x007fc, 0x007fd, 0x007fe, 0x007ff
+};
+
+static uint32_t unicode_ci_page_09[] = {
+ 0x00900, 0x00000, 0x00000, 0x00000, 0x00904, 0x00905, 0x00906, 0x00907,
+ 0x00908, 0x00909, 0x0090a, 0x0090b, 0x0090c, 0x0090d, 0x0090e, 0x0090f,
+ 0x00910, 0x00911, 0x00912, 0x00913, 0x00914, 0x00915, 0x00916, 0x00917,
+ 0x00918, 0x00919, 0x0091a, 0x0091b, 0x0091c, 0x0091d, 0x0091e, 0x0091f,
+ 0x00920, 0x00921, 0x00922, 0x00923, 0x00924, 0x00925, 0x00926, 0x00927,
+ 0x00928, 0x00928, 0x0092a, 0x0092b, 0x0092c, 0x0092d, 0x0092e, 0x0092f,
+ 0x00930, 0x00930, 0x00932, 0x00933, 0x00933, 0x00935, 0x00936, 0x00937,
+ 0x00938, 0x00939, 0x0093a, 0x0093b, 0x00000, 0x0093d, 0x0093e, 0x0093f,
+ 0x00940, 0x00941, 0x00942, 0x00943, 0x00944, 0x00945, 0x00946, 0x00947,
+ 0x00948, 0x00949, 0x0094a, 0x0094b, 0x0094c, 0x0094d, 0x0094e, 0x0094f,
+ 0x00950, 0x00000, 0x00000, 0x00000, 0x00000, 0x00955, 0x00956, 0x00957,
+ 0x00915, 0x00916, 0x00917, 0x0091c, 0x00921, 0x00922, 0x0092b, 0x0092f,
+ 0x00960, 0x00961, 0x00962, 0x00963, 0x00964, 0x00965, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00970, 0x00971, 0x00972, 0x00973, 0x00974, 0x00975, 0x00976, 0x00977,
+ 0x00978, 0x00979, 0x0097a, 0x0097b, 0x0097c, 0x0097d, 0x0097e, 0x0097f,
+ 0x00980, 0x00000, 0x00000, 0x00000, 0x00984, 0x00985, 0x00986, 0x00987,
+ 0x00988, 0x00989, 0x0098a, 0x0098b, 0x0098c, 0x0098d, 0x0098e, 0x0098f,
+ 0x00990, 0x00991, 0x00992, 0x00993, 0x00994, 0x00995, 0x00996, 0x00997,
+ 0x00998, 0x00999, 0x0099a, 0x0099b, 0x0099c, 0x0099d, 0x0099e, 0x0099f,
+ 0x009a0, 0x009a1, 0x009a2, 0x009a3, 0x009a4, 0x009a5, 0x009a6, 0x009a7,
+ 0x009a8, 0x009a9, 0x009aa, 0x009ab, 0x009ac, 0x009ad, 0x009ae, 0x009af,
+ 0x009b0, 0x009b1, 0x009b2, 0x009b3, 0x009b4, 0x009b5, 0x009b6, 0x009b7,
+ 0x009b8, 0x009b9, 0x009ba, 0x009bb, 0x00000, 0x009bd, 0x009be, 0x009bf,
+ 0x009c0, 0x009c1, 0x009c2, 0x009c3, 0x009c4, 0x009c5, 0x009c6, 0x009c7,
+ 0x009c8, 0x009c9, 0x009ca, 0x009cb, 0x009cc, 0x009cd, 0x009ce, 0x009cf,
+ 0x009d0, 0x009d1, 0x009d2, 0x009d3, 0x009d4, 0x009d5, 0x009d6, 0x009d7,
+ 0x009d8, 0x009d9, 0x009da, 0x009db, 0x009a1, 0x009a2, 0x009de, 0x009af,
+ 0x009e0, 0x009e1, 0x009e2, 0x009e3, 0x009e4, 0x009e5, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x009f0, 0x009f1, 0x009f2, 0x009f3, 0x00031, 0x00032, 0x00033, 0x00034,
+ 0x009f8, 0x009f9, 0x009fa, 0x009fb, 0x009fc, 0x009fd, 0x009fe, 0x009ff
+};
+
+static uint32_t unicode_ci_page_0a[] = {
+ 0x00a00, 0x00000, 0x00000, 0x00000, 0x00a04, 0x00a05, 0x00a06, 0x00a07,
+ 0x00a08, 0x00a09, 0x00a0a, 0x00a0b, 0x00a0c, 0x00a0d, 0x00a0e, 0x00a0f,
+ 0x00a10, 0x00a11, 0x00a12, 0x00a13, 0x00a14, 0x00a15, 0x00a16, 0x00a17,
+ 0x00a18, 0x00a19, 0x00a1a, 0x00a1b, 0x00a1c, 0x00a1d, 0x00a1e, 0x00a1f,
+ 0x00a20, 0x00a21, 0x00a22, 0x00a23, 0x00a24, 0x00a25, 0x00a26, 0x00a27,
+ 0x00a28, 0x00a29, 0x00a2a, 0x00a2b, 0x00a2c, 0x00a2d, 0x00a2e, 0x00a2f,
+ 0x00a30, 0x00a31, 0x00a32, 0x00a32, 0x00a34, 0x00a35, 0x00a36, 0x00a37,
+ 0x00a36, 0x00a39, 0x00a3a, 0x00a3b, 0x00000, 0x00a3d, 0x00a3e, 0x00a3f,
+ 0x00a40, 0x00a41, 0x00a42, 0x00a43, 0x00a44, 0x00a45, 0x00a46, 0x00a47,
+ 0x00a48, 0x00a49, 0x00a4a, 0x00a4b, 0x00a4c, 0x00a4d, 0x00a4e, 0x00a4f,
+ 0x00a50, 0x00a51, 0x00a52, 0x00a53, 0x00a54, 0x00a55, 0x00a56, 0x00a57,
+ 0x00a58, 0x00a16, 0x00a17, 0x00a1c, 0x00a5c, 0x00a5d, 0x00a2b, 0x00a5f,
+ 0x00a60, 0x00a61, 0x00a62, 0x00a63, 0x00a64, 0x00a65, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00000, 0x00000, 0x00a72, 0x00a73, 0x00a74, 0x00a75, 0x00a76, 0x00a77,
+ 0x00a78, 0x00a79, 0x00a7a, 0x00a7b, 0x00a7c, 0x00a7d, 0x00a7e, 0x00a7f,
+ 0x00a80, 0x00000, 0x00000, 0x00000, 0x00a84, 0x00a85, 0x00a86, 0x00a87,
+ 0x00a88, 0x00a89, 0x00a8a, 0x00a8b, 0x00a8c, 0x00a8d, 0x00a8e, 0x00a8f,
+ 0x00a90, 0x00a91, 0x00a92, 0x00a93, 0x00a94, 0x00a95, 0x00a96, 0x00a97,
+ 0x00a98, 0x00a99, 0x00a9a, 0x00a9b, 0x00a9c, 0x00a9d, 0x00a9e, 0x00a9f,
+ 0x00aa0, 0x00aa1, 0x00aa2, 0x00aa3, 0x00aa4, 0x00aa5, 0x00aa6, 0x00aa7,
+ 0x00aa8, 0x00aa9, 0x00aaa, 0x00aab, 0x00aac, 0x00aad, 0x00aae, 0x00aaf,
+ 0x00ab0, 0x00ab1, 0x00ab2, 0x00ab3, 0x00ab4, 0x00ab5, 0x00ab6, 0x00ab7,
+ 0x00ab8, 0x00ab9, 0x00aba, 0x00abb, 0x00000, 0x00abd, 0x00abe, 0x00abf,
+ 0x00ac0, 0x00ac1, 0x00ac2, 0x00ac3, 0x00ac4, 0x00ac5, 0x00ac6, 0x00ac7,
+ 0x00ac8, 0x00ac9, 0x00aca, 0x00acb, 0x00acc, 0x00acd, 0x00ace, 0x00acf,
+ 0x00ad0, 0x00ad1, 0x00ad2, 0x00ad3, 0x00ad4, 0x00ad5, 0x00ad6, 0x00ad7,
+ 0x00ad8, 0x00ad9, 0x00ada, 0x00adb, 0x00adc, 0x00add, 0x00ade, 0x00adf,
+ 0x00ae0, 0x00ae1, 0x00ae2, 0x00ae3, 0x00ae4, 0x00ae5, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00af0, 0x00af1, 0x00af2, 0x00af3, 0x00af4, 0x00af5, 0x00af6, 0x00af7,
+ 0x00af8, 0x00af9, 0x00afa, 0x00afb, 0x00afc, 0x00afd, 0x00afe, 0x00aff
+};
+
+static uint32_t unicode_ci_page_0b[] = {
+ 0x00b00, 0x00000, 0x00000, 0x00000, 0x00b04, 0x00b05, 0x00b06, 0x00b07,
+ 0x00b08, 0x00b09, 0x00b0a, 0x00b0b, 0x00b0c, 0x00b0d, 0x00b0e, 0x00b0f,
+ 0x00b10, 0x00b11, 0x00b12, 0x00b13, 0x00b14, 0x00b15, 0x00b16, 0x00b17,
+ 0x00b18, 0x00b19, 0x00b1a, 0x00b1b, 0x00b1c, 0x00b1d, 0x00b1e, 0x00b1f,
+ 0x00b20, 0x00b21, 0x00b22, 0x00b23, 0x00b24, 0x00b25, 0x00b26, 0x00b27,
+ 0x00b28, 0x00b29, 0x00b2a, 0x00b2b, 0x00b2c, 0x00b2d, 0x00b2e, 0x00b2f,
+ 0x00b30, 0x00b31, 0x00b32, 0x00b33, 0x00b34, 0x00b35, 0x00b36, 0x00b37,
+ 0x00b38, 0x00b39, 0x00b3a, 0x00b3b, 0x00000, 0x00b3d, 0x00b3e, 0x00b3f,
+ 0x00b40, 0x00b41, 0x00b42, 0x00b43, 0x00b44, 0x00b45, 0x00b46, 0x00b47,
+ 0x00b48, 0x00b49, 0x00b4a, 0x00b4b, 0x00b4c, 0x00b4d, 0x00b4e, 0x00b4f,
+ 0x00b50, 0x00b51, 0x00b52, 0x00b53, 0x00b54, 0x00b55, 0x00b56, 0x00b57,
+ 0x00b58, 0x00b59, 0x00b5a, 0x00b5b, 0x00b21, 0x00b22, 0x00b5e, 0x00b5f,
+ 0x00b60, 0x00b61, 0x00b62, 0x00b63, 0x00b64, 0x00b65, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00b70, 0x00b71, 0x00b72, 0x00b73, 0x00b74, 0x00b75, 0x00b76, 0x00b77,
+ 0x00b78, 0x00b79, 0x00b7a, 0x00b7b, 0x00b7c, 0x00b7d, 0x00b7e, 0x00b7f,
+ 0x00b80, 0x00b81, 0x00000, 0x00b83, 0x00b84, 0x00b85, 0x00b86, 0x00b87,
+ 0x00b88, 0x00b89, 0x00b8a, 0x00b8b, 0x00b8c, 0x00b8d, 0x00b8e, 0x00b8f,
+ 0x00b90, 0x00b91, 0x00b92, 0x00b93, 0x00b94, 0x00b95, 0x00b96, 0x00b97,
+ 0x00b98, 0x00b99, 0x00b9a, 0x00b9b, 0x00b9c, 0x00b9d, 0x00b9e, 0x00b9f,
+ 0x00ba0, 0x00ba1, 0x00ba2, 0x00ba3, 0x00ba4, 0x00ba5, 0x00ba6, 0x00ba7,
+ 0x00ba8, 0x00ba9, 0x00baa, 0x00bab, 0x00bac, 0x00bad, 0x00bae, 0x00baf,
+ 0x00bb0, 0x00bb1, 0x00bb2, 0x00bb3, 0x00bb4, 0x00bb5, 0x00bb6, 0x00bb7,
+ 0x00bb8, 0x00bb9, 0x00bba, 0x00bbb, 0x00bbc, 0x00bbd, 0x00bbe, 0x00bbf,
+ 0x00bc0, 0x00bc1, 0x00bc2, 0x00bc3, 0x00bc4, 0x00bc5, 0x00bc6, 0x00bc7,
+ 0x00bc8, 0x00bc9, 0x00bca, 0x00bcb, 0x00bcc, 0x00bcd, 0x00bce, 0x00bcf,
+ 0x00bd0, 0x00bd1, 0x00bd2, 0x00bd3, 0x00bd4, 0x00bd5, 0x00bd6, 0x00bd7,
+ 0x00bd8, 0x00bd9, 0x00bda, 0x00bdb, 0x00bdc, 0x00bdd, 0x00bde, 0x00bdf,
+ 0x00be0, 0x00be1, 0x00be2, 0x00be3, 0x00be4, 0x00be5, 0x00be6, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00bf0, 0x00bf1, 0x00bf2, 0x00bf3, 0x00bf4, 0x00bf5, 0x00bf6, 0x00bf7,
+ 0x00bf8, 0x00bf9, 0x00bfa, 0x00bfb, 0x00bfc, 0x00bfd, 0x00bfe, 0x00bff
+};
+
+static uint32_t unicode_ci_page_0c[] = {
+ 0x00c00, 0x00000, 0x00000, 0x00000, 0x00c04, 0x00c05, 0x00c06, 0x00c07,
+ 0x00c08, 0x00c09, 0x00c0a, 0x00c0b, 0x00c0c, 0x00c0d, 0x00c0e, 0x00c0f,
+ 0x00c10, 0x00c11, 0x00c12, 0x00c13, 0x00c14, 0x00c15, 0x00c16, 0x00c17,
+ 0x00c18, 0x00c19, 0x00c1a, 0x00c1b, 0x00c1c, 0x00c1d, 0x00c1e, 0x00c1f,
+ 0x00c20, 0x00c21, 0x00c22, 0x00c23, 0x00c24, 0x00c25, 0x00c26, 0x00c27,
+ 0x00c28, 0x00c29, 0x00c2a, 0x00c2b, 0x00c2c, 0x00c2d, 0x00c2e, 0x00c2f,
+ 0x00c30, 0x00c31, 0x00c32, 0x00c33, 0x00c34, 0x00c35, 0x00c36, 0x00c37,
+ 0x00c38, 0x00c39, 0x00c3a, 0x00c3b, 0x00c3c, 0x00c3d, 0x00c3e, 0x00c3f,
+ 0x00c40, 0x00c41, 0x00c42, 0x00c43, 0x00c44, 0x00c45, 0x00c46, 0x00c47,
+ 0x00c48, 0x00c49, 0x00c4a, 0x00c4b, 0x00c4c, 0x00c4d, 0x00c4e, 0x00c4f,
+ 0x00c50, 0x00c51, 0x00c52, 0x00c53, 0x00c54, 0x00c55, 0x00c56, 0x00c57,
+ 0x00c58, 0x00c59, 0x00c5a, 0x00c5b, 0x00c5c, 0x00c5d, 0x00c5e, 0x00c5f,
+ 0x00c60, 0x00c61, 0x00c62, 0x00c63, 0x00c64, 0x00c65, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00c70, 0x00c71, 0x00c72, 0x00c73, 0x00c74, 0x00c75, 0x00c76, 0x00c77,
+ 0x00c78, 0x00c79, 0x00c7a, 0x00c7b, 0x00c7c, 0x00c7d, 0x00c7e, 0x00c7f,
+ 0x00c80, 0x00c81, 0x00000, 0x00000, 0x00c84, 0x00c85, 0x00c86, 0x00c87,
+ 0x00c88, 0x00c89, 0x00c8a, 0x00c8b, 0x00c8c, 0x00c8d, 0x00c8e, 0x00c8f,
+ 0x00c90, 0x00c91, 0x00c92, 0x00c93, 0x00c94, 0x00c95, 0x00c96, 0x00c97,
+ 0x00c98, 0x00c99, 0x00c9a, 0x00c9b, 0x00c9c, 0x00c9d, 0x00c9e, 0x00c9f,
+ 0x00ca0, 0x00ca1, 0x00ca2, 0x00ca3, 0x00ca4, 0x00ca5, 0x00ca6, 0x00ca7,
+ 0x00ca8, 0x00ca9, 0x00caa, 0x00cab, 0x00cac, 0x00cad, 0x00cae, 0x00caf,
+ 0x00cb0, 0x00cb1, 0x00cb2, 0x00cb3, 0x00cb4, 0x00cb5, 0x00cb6, 0x00cb7,
+ 0x00cb8, 0x00cb9, 0x00cba, 0x00cbb, 0x00000, 0x00cbd, 0x00cbe, 0x00cbf,
+ 0x00cc0, 0x00cc1, 0x00cc2, 0x00cc3, 0x00cc4, 0x00cc5, 0x00cc6, 0x00cc7,
+ 0x00cc8, 0x00cc9, 0x00cca, 0x00ccb, 0x00ccc, 0x00ccd, 0x00cce, 0x00ccf,
+ 0x00cd0, 0x00cd1, 0x00cd2, 0x00cd3, 0x00cd4, 0x00cd5, 0x00cd6, 0x00cd7,
+ 0x00cd8, 0x00cd9, 0x00cda, 0x00cdb, 0x00cdc, 0x00cdd, 0x00cde, 0x00cdf,
+ 0x00ce0, 0x00ce1, 0x00ce2, 0x00ce3, 0x00ce4, 0x00ce5, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00cf0, 0x00cf1, 0x00cf2, 0x00cf3, 0x00cf4, 0x00cf5, 0x00cf6, 0x00cf7,
+ 0x00cf8, 0x00cf9, 0x00cfa, 0x00cfb, 0x00cfc, 0x00cfd, 0x00cfe, 0x00cff
+};
+
+static uint32_t unicode_ci_page_0d[] = {
+ 0x00d00, 0x00d01, 0x00000, 0x00000, 0x00d04, 0x00d05, 0x00d06, 0x00d07,
+ 0x00d08, 0x00d09, 0x00d0a, 0x00d0b, 0x00d0c, 0x00d0d, 0x00d0e, 0x00d0f,
+ 0x00d10, 0x00d11, 0x00d12, 0x00d13, 0x00d14, 0x00d15, 0x00d16, 0x00d17,
+ 0x00d18, 0x00d19, 0x00d1a, 0x00d1b, 0x00d1c, 0x00d1d, 0x00d1e, 0x00d1f,
+ 0x00d20, 0x00d21, 0x00d22, 0x00d23, 0x00d24, 0x00d25, 0x00d26, 0x00d27,
+ 0x00d28, 0x00d29, 0x00d2a, 0x00d2b, 0x00d2c, 0x00d2d, 0x00d2e, 0x00d2f,
+ 0x00d30, 0x00d31, 0x00d32, 0x00d33, 0x00d34, 0x00d35, 0x00d36, 0x00d37,
+ 0x00d38, 0x00d39, 0x00d3a, 0x00d3b, 0x00d3c, 0x00d3d, 0x00d3e, 0x00d3f,
+ 0x00d40, 0x00d41, 0x00d42, 0x00d43, 0x00d44, 0x00d45, 0x00d46, 0x00d47,
+ 0x00d48, 0x00d49, 0x00d4a, 0x00d4b, 0x00d4c, 0x00d4d, 0x00d4e, 0x00d4f,
+ 0x00d50, 0x00d51, 0x00d52, 0x00d53, 0x00d54, 0x00d55, 0x00d56, 0x00d57,
+ 0x00d58, 0x00d59, 0x00d5a, 0x00d5b, 0x00d5c, 0x00d5d, 0x00d5e, 0x00d5f,
+ 0x00d60, 0x00d61, 0x00d62, 0x00d63, 0x00d64, 0x00d65, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x00d70, 0x00d71, 0x00d72, 0x00d73, 0x00d74, 0x00d75, 0x00d76, 0x00d77,
+ 0x00d78, 0x00d79, 0x00d7a, 0x00d7b, 0x00d7c, 0x00d7d, 0x00d7e, 0x00d7f,
+ 0x00d80, 0x00d81, 0x00000, 0x00000, 0x00d84, 0x00d85, 0x00d86, 0x00d87,
+ 0x00d88, 0x00d89, 0x00d8a, 0x00d8b, 0x00d8c, 0x00d8d, 0x00d8e, 0x00d8f,
+ 0x00d90, 0x00d91, 0x00d92, 0x00d93, 0x00d94, 0x00d95, 0x00d96, 0x00d97,
+ 0x00d98, 0x00d99, 0x00d9a, 0x00d9b, 0x00d9c, 0x00d9d, 0x00d9e, 0x00d9f,
+ 0x00da0, 0x00da1, 0x00da2, 0x00da3, 0x00da4, 0x00da5, 0x00da6, 0x00da7,
+ 0x00da8, 0x00da9, 0x00daa, 0x00dab, 0x00dac, 0x00dad, 0x00dae, 0x00daf,
+ 0x00db0, 0x00db1, 0x00db2, 0x00db3, 0x00db4, 0x00db5, 0x00db6, 0x00db7,
+ 0x00db8, 0x00db9, 0x00dba, 0x00dbb, 0x00dbc, 0x00dbd, 0x00dbe, 0x00dbf,
+ 0x00dc0, 0x00dc1, 0x00dc2, 0x00dc3, 0x00dc4, 0x00dc5, 0x00dc6, 0x00dc7,
+ 0x00dc8, 0x00dc9, 0x00dca, 0x00dcb, 0x00dcc, 0x00dcd, 0x00dce, 0x00dcf,
+ 0x00dd0, 0x00dd1, 0x00dd2, 0x00dd3, 0x00dd4, 0x00dd5, 0x00dd6, 0x00dd7,
+ 0x00dd8, 0x00dd9, 0x00dda, 0x00ddb, 0x00ddc, 0x00ddd, 0x00dde, 0x00ddf,
+ 0x00de0, 0x00de1, 0x00de2, 0x00de3, 0x00de4, 0x00de5, 0x00de6, 0x00de7,
+ 0x00de8, 0x00de9, 0x00dea, 0x00deb, 0x00dec, 0x00ded, 0x00dee, 0x00def,
+ 0x00df0, 0x00df1, 0x00df2, 0x00df3, 0x00df4, 0x00df5, 0x00df6, 0x00df7,
+ 0x00df8, 0x00df9, 0x00dfa, 0x00dfb, 0x00dfc, 0x00dfd, 0x00dfe, 0x00dff
+};
+
+static uint32_t unicode_ci_page_0e[] = {
+ 0x00e00, 0x00e01, 0x00e02, 0x00e03, 0x00e04, 0x00e05, 0x00e06, 0x00e07,
+ 0x00e08, 0x00e09, 0x00e0a, 0x00e0b, 0x00e0c, 0x00e0d, 0x00e0e, 0x00e0f,
+ 0x00e10, 0x00e11, 0x00e12, 0x00e13, 0x00e14, 0x00e15, 0x00e16, 0x00e17,
+ 0x00e18, 0x00e19, 0x00e1a, 0x00e1b, 0x00e1c, 0x00e1d, 0x00e1e, 0x00e1f,
+ 0x00e20, 0x00e21, 0x00e22, 0x00e23, 0x00e24, 0x00e25, 0x00e26, 0x00e27,
+ 0x00e28, 0x00e29, 0x00e2a, 0x00e2b, 0x00e2c, 0x00e2d, 0x00e2e, 0x00e2f,
+ 0x00e30, 0x00e31, 0x00e32, 0x00e33, 0x00e34, 0x00e35, 0x00e36, 0x00e37,
+ 0x00e38, 0x00e39, 0x00e3a, 0x00e3b, 0x00e3c, 0x00e3d, 0x00e3e, 0x00e3f,
+ 0x00e40, 0x00e41, 0x00e42, 0x00e43, 0x00e44, 0x00e45, 0x00e46, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00e4c, 0x00e4d, 0x00000, 0x00e4f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x00e5a, 0x00e5b, 0x00e5c, 0x00e5d, 0x00e5e, 0x00e5f,
+ 0x00e60, 0x00e61, 0x00e62, 0x00e63, 0x00e64, 0x00e65, 0x00e66, 0x00e67,
+ 0x00e68, 0x00e69, 0x00e6a, 0x00e6b, 0x00e6c, 0x00e6d, 0x00e6e, 0x00e6f,
+ 0x00e70, 0x00e71, 0x00e72, 0x00e73, 0x00e74, 0x00e75, 0x00e76, 0x00e77,
+ 0x00e78, 0x00e79, 0x00e7a, 0x00e7b, 0x00e7c, 0x00e7d, 0x00e7e, 0x00e7f,
+ 0x00e80, 0x00e81, 0x00e82, 0x00e83, 0x00e84, 0x00e85, 0x00e86, 0x00e87,
+ 0x00e88, 0x00e89, 0x00e8a, 0x00e8b, 0x00e8c, 0x00e8d, 0x00e8e, 0x00e8f,
+ 0x00e90, 0x00e91, 0x00e92, 0x00e93, 0x00e94, 0x00e95, 0x00e96, 0x00e97,
+ 0x00e98, 0x00e99, 0x00e9a, 0x00e9b, 0x00e9c, 0x00e9d, 0x00e9e, 0x00e9f,
+ 0x00ea0, 0x00ea1, 0x00ea2, 0x00ea3, 0x00ea4, 0x00ea5, 0x00ea6, 0x00ea7,
+ 0x00ea8, 0x00ea9, 0x00eaa, 0x00eab, 0x00eac, 0x00ead, 0x00eae, 0x00eaf,
+ 0x00eb0, 0x00eb1, 0x00eb2, 0x00eb3, 0x00eb4, 0x00eb5, 0x00eb6, 0x00eb7,
+ 0x00eb8, 0x00eb9, 0x00eba, 0x00ebb, 0x00ebc, 0x00ebd, 0x00ebe, 0x00ebf,
+ 0x00ec0, 0x00ec1, 0x00ec2, 0x00ec3, 0x00ec4, 0x00ec5, 0x00ec6, 0x00ec7,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00ecc, 0x00ecd, 0x00ece, 0x00ecf,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x00eda, 0x00edb, 0x00edc, 0x00edd, 0x00ede, 0x00edf,
+ 0x00ee0, 0x00ee1, 0x00ee2, 0x00ee3, 0x00ee4, 0x00ee5, 0x00ee6, 0x00ee7,
+ 0x00ee8, 0x00ee9, 0x00eea, 0x00eeb, 0x00eec, 0x00eed, 0x00eee, 0x00eef,
+ 0x00ef0, 0x00ef1, 0x00ef2, 0x00ef3, 0x00ef4, 0x00ef5, 0x00ef6, 0x00ef7,
+ 0x00ef8, 0x00ef9, 0x00efa, 0x00efb, 0x00efc, 0x00efd, 0x00efe, 0x00eff
+};
+
+static uint32_t unicode_ci_page_0f[] = {
+ 0x00f00, 0x00f01, 0x00f02, 0x00f03, 0x00f04, 0x00f05, 0x00f06, 0x00f07,
+ 0x00f08, 0x00f09, 0x00f0a, 0x00f0b, 0x00f0b, 0x00f0d, 0x00f0e, 0x00f0f,
+ 0x00f10, 0x00f11, 0x00f12, 0x00f13, 0x00f14, 0x00f15, 0x00f16, 0x00f17,
+ 0x00000, 0x00000, 0x00f1a, 0x00f1b, 0x00f1c, 0x00f1d, 0x00f1e, 0x00f1f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036,
+ 0x00037, 0x00038, 0x00039, 0x00030, 0x00f34, 0x00000, 0x00f36, 0x00000,
+ 0x00f38, 0x00000, 0x00f3a, 0x00f3b, 0x00f3c, 0x00f3d, 0x00f3e, 0x00f3f,
+ 0x00f40, 0x00f41, 0x00f42, 0x00f43, 0x00f44, 0x00f45, 0x00f46, 0x00f47,
+ 0x00f48, 0x00f49, 0x00f4a, 0x00f4b, 0x00f4c, 0x00f4d, 0x00f4e, 0x00f4f,
+ 0x00f50, 0x00f51, 0x00f52, 0x00f53, 0x00f54, 0x00f55, 0x00f56, 0x00f57,
+ 0x00f58, 0x00f59, 0x00f5a, 0x00f5b, 0x00f5c, 0x00f5d, 0x00f5e, 0x00f5f,
+ 0x00f60, 0x00f61, 0x00f62, 0x00f63, 0x00f64, 0x00f65, 0x00f66, 0x00f67,
+ 0x00f68, 0x00f69, 0x00f62, 0x00f6b, 0x00f6c, 0x00f6d, 0x00f6e, 0x00f6f,
+ 0x00f70, 0x00f71, 0x00f72, 0x00f73, 0x00f74, 0x00f75, 0x00f76, 0x00f77,
+ 0x00f78, 0x00f79, 0x00f7a, 0x00f7b, 0x00f7c, 0x00f7d, 0x00000, 0x00000,
+ 0x00f80, 0x00f81, 0x00000, 0x00000, 0x00f84, 0x00f85, 0x00000, 0x00000,
+ 0x00f88, 0x00f89, 0x00f8a, 0x00f8b, 0x00f8c, 0x00f8d, 0x00f8e, 0x00f8f,
+ 0x00f90, 0x00f91, 0x00f92, 0x00f93, 0x00f94, 0x00f95, 0x00f96, 0x00f97,
+ 0x00f98, 0x00f99, 0x00f9a, 0x00f9b, 0x00f9c, 0x00f9d, 0x00f9e, 0x00f9f,
+ 0x00fa0, 0x00fa1, 0x00fa2, 0x00fa3, 0x00fa4, 0x00fa5, 0x00fa6, 0x00fa7,
+ 0x00fa8, 0x00fa9, 0x00faa, 0x00fab, 0x00fac, 0x00fad, 0x00fae, 0x00faf,
+ 0x00fb0, 0x00fb1, 0x00fb2, 0x00fb3, 0x00fb4, 0x00fb5, 0x00fb6, 0x00fb7,
+ 0x00fb8, 0x00fb9, 0x00fad, 0x00fb1, 0x00fb2, 0x00fbd, 0x00fbe, 0x00fbf,
+ 0x00fc0, 0x00fc1, 0x00fc2, 0x00fc3, 0x00fc4, 0x00fc5, 0x00000, 0x00fc7,
+ 0x00fc8, 0x00fc9, 0x00fca, 0x00fcb, 0x00fcc, 0x00fcd, 0x00fce, 0x00fcf,
+ 0x00fd0, 0x00fd1, 0x00fd2, 0x00fd3, 0x00fd4, 0x00fd5, 0x00fd6, 0x00fd7,
+ 0x00fd8, 0x00fd9, 0x00fda, 0x00fdb, 0x00fdc, 0x00fdd, 0x00fde, 0x00fdf,
+ 0x00fe0, 0x00fe1, 0x00fe2, 0x00fe3, 0x00fe4, 0x00fe5, 0x00fe6, 0x00fe7,
+ 0x00fe8, 0x00fe9, 0x00fea, 0x00feb, 0x00fec, 0x00fed, 0x00fee, 0x00fef,
+ 0x00ff0, 0x00ff1, 0x00ff2, 0x00ff3, 0x00ff4, 0x00ff5, 0x00ff6, 0x00ff7,
+ 0x00ff8, 0x00ff9, 0x00ffa, 0x00ffb, 0x00ffc, 0x00ffd, 0x00ffe, 0x00fff
+};
+
+static uint32_t unicode_ci_page_10[] = {
+ 0x01000, 0x01001, 0x01002, 0x01003, 0x01004, 0x01005, 0x01006, 0x01007,
+ 0x01008, 0x01009, 0x0100a, 0x0100b, 0x0100c, 0x0100d, 0x0100e, 0x0100f,
+ 0x01010, 0x01011, 0x01012, 0x01013, 0x01014, 0x01015, 0x01016, 0x01017,
+ 0x01018, 0x01019, 0x0101a, 0x0101b, 0x0101c, 0x0101d, 0x0101e, 0x0101f,
+ 0x01020, 0x01021, 0x01022, 0x01023, 0x01024, 0x01025, 0x01026, 0x01027,
+ 0x01028, 0x01029, 0x0102a, 0x0102b, 0x0102c, 0x0102d, 0x0102e, 0x0102f,
+ 0x01030, 0x01031, 0x01032, 0x01033, 0x01034, 0x01035, 0x00000, 0x00000,
+ 0x00000, 0x01039, 0x0103a, 0x0103b, 0x0103c, 0x0103d, 0x0103e, 0x0103f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0104a, 0x0104b, 0x0104c, 0x0104d, 0x0104e, 0x0104f,
+ 0x01050, 0x01051, 0x01052, 0x01053, 0x01054, 0x01055, 0x01056, 0x01057,
+ 0x01058, 0x01059, 0x0105a, 0x0105b, 0x0105c, 0x0105d, 0x0105e, 0x0105f,
+ 0x01060, 0x01061, 0x01062, 0x01063, 0x01064, 0x01065, 0x01066, 0x01067,
+ 0x01068, 0x01069, 0x0106a, 0x0106b, 0x0106c, 0x0106d, 0x0106e, 0x0106f,
+ 0x01070, 0x01071, 0x01072, 0x01073, 0x01074, 0x01075, 0x01076, 0x01077,
+ 0x01078, 0x01079, 0x0107a, 0x0107b, 0x0107c, 0x0107d, 0x0107e, 0x0107f,
+ 0x01080, 0x01081, 0x01082, 0x01083, 0x01084, 0x01085, 0x01086, 0x01087,
+ 0x01088, 0x01089, 0x0108a, 0x0108b, 0x0108c, 0x0108d, 0x0108e, 0x0108f,
+ 0x01090, 0x01091, 0x01092, 0x01093, 0x01094, 0x01095, 0x01096, 0x01097,
+ 0x01098, 0x01099, 0x0109a, 0x0109b, 0x0109c, 0x0109d, 0x0109e, 0x0109f,
+ 0x010a0, 0x010a1, 0x010a2, 0x010a3, 0x010a4, 0x010a5, 0x010a6, 0x010a7,
+ 0x010a8, 0x010a9, 0x010aa, 0x010ab, 0x010ac, 0x010ad, 0x010ae, 0x010af,
+ 0x010b0, 0x010b1, 0x010b2, 0x010b3, 0x010b4, 0x010b5, 0x010b6, 0x010b7,
+ 0x010b8, 0x010b9, 0x010ba, 0x010bb, 0x010bc, 0x010bd, 0x010be, 0x010bf,
+ 0x010c0, 0x010c1, 0x010c2, 0x010c3, 0x010c4, 0x010c5, 0x010c6, 0x010c7,
+ 0x010c8, 0x010c9, 0x010ca, 0x010cb, 0x010cc, 0x010cd, 0x010ce, 0x010cf,
+ 0x010a0, 0x010a1, 0x010a2, 0x010a3, 0x010a4, 0x010a5, 0x010a6, 0x010a7,
+ 0x010a8, 0x010a9, 0x010aa, 0x010ab, 0x010ac, 0x010ad, 0x010ae, 0x010af,
+ 0x010b0, 0x010b1, 0x010b2, 0x010b3, 0x010b4, 0x010b5, 0x010b6, 0x010b7,
+ 0x010b8, 0x010b9, 0x010ba, 0x010bb, 0x010bc, 0x010bd, 0x010be, 0x010bf,
+ 0x010c0, 0x010c1, 0x010c2, 0x010c3, 0x010c4, 0x010c5, 0x010f6, 0x010f7,
+ 0x010f8, 0x010f9, 0x010fa, 0x010fb, 0x010fc, 0x010fd, 0x010fe, 0x010ff
+};
+
+static uint32_t unicode_ci_page_13[] = {
+ 0x01300, 0x01301, 0x01302, 0x01303, 0x01304, 0x01305, 0x01306, 0x01307,
+ 0x01308, 0x01309, 0x0130a, 0x0130b, 0x0130c, 0x0130d, 0x0130e, 0x0130f,
+ 0x01310, 0x01311, 0x01312, 0x01313, 0x01314, 0x01315, 0x01316, 0x01317,
+ 0x01318, 0x01319, 0x0131a, 0x0131b, 0x0131c, 0x0131d, 0x0131e, 0x0131f,
+ 0x01320, 0x01321, 0x01322, 0x01323, 0x01324, 0x01325, 0x01326, 0x01327,
+ 0x01328, 0x01329, 0x0132a, 0x0132b, 0x0132c, 0x0132d, 0x0132e, 0x0132f,
+ 0x01330, 0x01331, 0x01332, 0x01333, 0x01334, 0x01335, 0x01336, 0x01337,
+ 0x01338, 0x01339, 0x0133a, 0x0133b, 0x0133c, 0x0133d, 0x0133e, 0x0133f,
+ 0x01340, 0x01341, 0x01342, 0x01343, 0x01344, 0x01345, 0x01346, 0x01347,
+ 0x01348, 0x01349, 0x0134a, 0x0134b, 0x0134c, 0x0134d, 0x0134e, 0x0134f,
+ 0x01350, 0x01351, 0x01352, 0x01353, 0x01354, 0x01355, 0x01356, 0x01357,
+ 0x01358, 0x01359, 0x0135a, 0x0135b, 0x0135c, 0x0135d, 0x0135e, 0x0135f,
+ 0x01360, 0x01361, 0x01362, 0x01363, 0x01364, 0x01365, 0x01366, 0x01367,
+ 0x01368, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x01372, 0x01373, 0x01374, 0x01375, 0x01376, 0x01377,
+ 0x01378, 0x01379, 0x0137a, 0x0137b, 0x0137c, 0x0137d, 0x0137e, 0x0137f,
+ 0x01380, 0x01381, 0x01382, 0x01383, 0x01384, 0x01385, 0x01386, 0x01387,
+ 0x01388, 0x01389, 0x0138a, 0x0138b, 0x0138c, 0x0138d, 0x0138e, 0x0138f,
+ 0x01390, 0x01391, 0x01392, 0x01393, 0x01394, 0x01395, 0x01396, 0x01397,
+ 0x01398, 0x01399, 0x0139a, 0x0139b, 0x0139c, 0x0139d, 0x0139e, 0x0139f,
+ 0x013a0, 0x013a1, 0x013a2, 0x013a3, 0x013a4, 0x013a5, 0x013a6, 0x013a7,
+ 0x013a8, 0x013a9, 0x013aa, 0x013ab, 0x013ac, 0x013ad, 0x013ae, 0x013af,
+ 0x013b0, 0x013b1, 0x013b2, 0x013b3, 0x013b4, 0x013b5, 0x013b6, 0x013b7,
+ 0x013b8, 0x013b9, 0x013ba, 0x013bb, 0x013bc, 0x013bd, 0x013be, 0x013bf,
+ 0x013c0, 0x013c1, 0x013c2, 0x013c3, 0x013c4, 0x013c5, 0x013c6, 0x013c7,
+ 0x013c8, 0x013c9, 0x013ca, 0x013cb, 0x013cc, 0x013cd, 0x013ce, 0x013cf,
+ 0x013d0, 0x013d1, 0x013d2, 0x013d3, 0x013d4, 0x013d5, 0x013d6, 0x013d7,
+ 0x013d8, 0x013d9, 0x013da, 0x013db, 0x013dc, 0x013dd, 0x013de, 0x013df,
+ 0x013e0, 0x013e1, 0x013e2, 0x013e3, 0x013e4, 0x013e5, 0x013e6, 0x013e7,
+ 0x013e8, 0x013e9, 0x013ea, 0x013eb, 0x013ec, 0x013ed, 0x013ee, 0x013ef,
+ 0x013f0, 0x013f1, 0x013f2, 0x013f3, 0x013f4, 0x013f5, 0x013f6, 0x013f7,
+ 0x013f8, 0x013f9, 0x013fa, 0x013fb, 0x013fc, 0x013fd, 0x013fe, 0x013ff
+};
+
+static uint32_t unicode_ci_page_16[] = {
+ 0x01600, 0x01601, 0x01602, 0x01603, 0x01604, 0x01605, 0x01606, 0x01607,
+ 0x01608, 0x01609, 0x0160a, 0x0160b, 0x0160c, 0x0160d, 0x0160e, 0x0160f,
+ 0x01610, 0x01611, 0x01612, 0x01613, 0x01614, 0x01615, 0x01616, 0x01617,
+ 0x01618, 0x01619, 0x0161a, 0x0161b, 0x0161c, 0x0161d, 0x0161e, 0x0161f,
+ 0x01620, 0x01621, 0x01622, 0x01623, 0x01624, 0x01625, 0x01626, 0x01627,
+ 0x01628, 0x01629, 0x0162a, 0x0162b, 0x0162c, 0x0162d, 0x0162e, 0x0162f,
+ 0x01630, 0x01631, 0x01632, 0x01633, 0x01634, 0x01635, 0x01636, 0x01637,
+ 0x01638, 0x01639, 0x0163a, 0x0163b, 0x0163c, 0x0163d, 0x0163e, 0x0163f,
+ 0x01640, 0x01641, 0x01642, 0x01643, 0x01644, 0x01645, 0x01646, 0x01647,
+ 0x01648, 0x01649, 0x0164a, 0x0164b, 0x0164c, 0x0164d, 0x0164e, 0x0164f,
+ 0x01650, 0x01651, 0x01652, 0x01653, 0x01654, 0x01655, 0x01656, 0x01657,
+ 0x01658, 0x01659, 0x0165a, 0x0165b, 0x0165c, 0x0165d, 0x0165e, 0x0165f,
+ 0x01660, 0x01661, 0x01662, 0x01663, 0x01664, 0x01665, 0x01666, 0x01667,
+ 0x01668, 0x01669, 0x0166a, 0x0166b, 0x0166c, 0x0166d, 0x0166e, 0x0166f,
+ 0x01670, 0x01671, 0x01672, 0x01673, 0x01674, 0x01675, 0x01676, 0x01677,
+ 0x01678, 0x01679, 0x0167a, 0x0167b, 0x0167c, 0x0167d, 0x0167e, 0x0167f,
+ 0x01680, 0x01681, 0x01682, 0x01683, 0x01684, 0x01685, 0x01686, 0x01687,
+ 0x01688, 0x01689, 0x0168a, 0x0168b, 0x0168c, 0x0168d, 0x0168e, 0x0168f,
+ 0x01690, 0x01691, 0x01692, 0x01693, 0x01694, 0x01695, 0x01696, 0x01697,
+ 0x01698, 0x01699, 0x0169a, 0x0169b, 0x0169c, 0x0169d, 0x0169e, 0x0169f,
+ 0x016a0, 0x016a0, 0x016a2, 0x016a3, 0x016a2, 0x016a2, 0x016a6, 0x016a6,
+ 0x016a8, 0x016a8, 0x016aa, 0x016ab, 0x016a8, 0x016a8, 0x016a8, 0x016af,
+ 0x016b0, 0x016b1, 0x016b2, 0x016b2, 0x016b2, 0x016b2, 0x016b2, 0x016b7,
+ 0x016b8, 0x016b9, 0x016ba, 0x016ba, 0x016ba, 0x016ba, 0x016be, 0x016be,
+ 0x016be, 0x016c1, 0x016c1, 0x016c3, 0x016c3, 0x016c5, 0x016c5, 0x016c7,
+ 0x016c8, 0x016c9, 0x016ca, 0x016ca, 0x016ca, 0x016ca, 0x016ca, 0x016cf,
+ 0x016cf, 0x016cf, 0x016d2, 0x016d2, 0x016d2, 0x016c8, 0x016d6, 0x016d7,
+ 0x016d7, 0x016d7, 0x016da, 0x016da, 0x016dc, 0x016dc, 0x016de, 0x016df,
+ 0x016e0, 0x016e1, 0x016e2, 0x016e3, 0x016e4, 0x016e5, 0x016e6, 0x016e6,
+ 0x016e6, 0x016b9, 0x016ca, 0x016eb, 0x016ec, 0x016ed, 0x016ee, 0x016ef,
+ 0x016f0, 0x016f1, 0x016f2, 0x016f3, 0x016f4, 0x016f5, 0x016f6, 0x016f7,
+ 0x016f8, 0x016f9, 0x016fa, 0x016fb, 0x016fc, 0x016fd, 0x016fe, 0x016ff
+};
+
+static uint32_t unicode_ci_page_17[] = {
+ 0x01700, 0x01701, 0x01702, 0x01703, 0x01704, 0x01705, 0x01706, 0x01707,
+ 0x01708, 0x01709, 0x0170a, 0x0170b, 0x0170c, 0x0170d, 0x0170e, 0x0170f,
+ 0x01710, 0x01711, 0x01712, 0x01713, 0x01714, 0x01715, 0x01716, 0x01717,
+ 0x01718, 0x01719, 0x0171a, 0x0171b, 0x0171c, 0x0171d, 0x0171e, 0x0171f,
+ 0x01720, 0x01721, 0x01722, 0x01723, 0x01724, 0x01725, 0x01726, 0x01727,
+ 0x01728, 0x01729, 0x0172a, 0x0172b, 0x0172c, 0x0172d, 0x0172e, 0x0172f,
+ 0x01730, 0x01731, 0x01732, 0x01733, 0x01734, 0x01735, 0x01736, 0x01737,
+ 0x01738, 0x01739, 0x0173a, 0x0173b, 0x0173c, 0x0173d, 0x0173e, 0x0173f,
+ 0x01740, 0x01741, 0x01742, 0x01743, 0x01744, 0x01745, 0x01746, 0x01747,
+ 0x01748, 0x01749, 0x0174a, 0x0174b, 0x0174c, 0x0174d, 0x0174e, 0x0174f,
+ 0x01750, 0x01751, 0x01752, 0x01753, 0x01754, 0x01755, 0x01756, 0x01757,
+ 0x01758, 0x01759, 0x0175a, 0x0175b, 0x0175c, 0x0175d, 0x0175e, 0x0175f,
+ 0x01760, 0x01761, 0x01762, 0x01763, 0x01764, 0x01765, 0x01766, 0x01767,
+ 0x01768, 0x01769, 0x0176a, 0x0176b, 0x0176c, 0x0176d, 0x0176e, 0x0176f,
+ 0x01770, 0x01771, 0x01772, 0x01773, 0x01774, 0x01775, 0x01776, 0x01777,
+ 0x01778, 0x01779, 0x0177a, 0x0177b, 0x0177c, 0x0177d, 0x0177e, 0x0177f,
+ 0x01780, 0x01781, 0x01782, 0x01783, 0x01784, 0x01785, 0x01786, 0x01787,
+ 0x01788, 0x01789, 0x0178a, 0x0178b, 0x0178c, 0x0178d, 0x0178e, 0x0178f,
+ 0x01790, 0x01791, 0x01792, 0x01793, 0x01794, 0x01795, 0x01796, 0x01797,
+ 0x01798, 0x01799, 0x0179a, 0x0179b, 0x0179c, 0x0179d, 0x0179e, 0x0179f,
+ 0x017a0, 0x017a1, 0x017a2, 0x017a3, 0x017a4, 0x017a5, 0x017a6, 0x017a7,
+ 0x017a8, 0x017a9, 0x017aa, 0x017ab, 0x017ac, 0x017ad, 0x017ae, 0x017af,
+ 0x017b0, 0x017b1, 0x017b2, 0x017b3, 0x017b4, 0x017b5, 0x017b6, 0x017b7,
+ 0x017b8, 0x017b9, 0x017ba, 0x017bb, 0x017bc, 0x017bd, 0x017be, 0x017bf,
+ 0x017c0, 0x017c1, 0x017c2, 0x017c3, 0x017c4, 0x017c5, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x017d2, 0x00000, 0x017d4, 0x017d5, 0x017d6, 0x017d7,
+ 0x017d8, 0x017d9, 0x017da, 0x017db, 0x017dc, 0x00000, 0x017de, 0x017df,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x017ea, 0x017eb, 0x017ec, 0x017ed, 0x017ee, 0x017ef,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x017fa, 0x017fb, 0x017fc, 0x017fd, 0x017fe, 0x017ff
+};
+
+static uint32_t unicode_ci_page_18[] = {
+ 0x01800, 0x01801, 0x01802, 0x01803, 0x01804, 0x01805, 0x01806, 0x01807,
+ 0x01808, 0x01809, 0x0180a, 0x00000, 0x00000, 0x00000, 0x00000, 0x0180f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0181a, 0x0181b, 0x0181c, 0x0181d, 0x0181e, 0x0181f,
+ 0x01820, 0x01821, 0x01822, 0x01823, 0x01824, 0x01825, 0x01826, 0x01827,
+ 0x01828, 0x01829, 0x0182a, 0x0182b, 0x0182c, 0x0182d, 0x0182e, 0x0182f,
+ 0x01830, 0x01831, 0x01832, 0x01833, 0x01834, 0x01835, 0x01836, 0x01837,
+ 0x01838, 0x01839, 0x0183a, 0x0183b, 0x0183c, 0x0183d, 0x0183e, 0x0183f,
+ 0x01840, 0x01841, 0x01842, 0x01843, 0x01844, 0x01845, 0x01846, 0x01847,
+ 0x01848, 0x01849, 0x0184a, 0x0184b, 0x0184c, 0x0184d, 0x0184e, 0x0184f,
+ 0x01850, 0x01851, 0x01852, 0x01853, 0x01854, 0x01855, 0x01856, 0x01857,
+ 0x01858, 0x01859, 0x0185a, 0x0185b, 0x0185c, 0x0185d, 0x0185e, 0x0185f,
+ 0x01860, 0x01861, 0x01862, 0x01863, 0x01864, 0x01865, 0x01866, 0x01867,
+ 0x01868, 0x01869, 0x0186a, 0x0186b, 0x0186c, 0x0186d, 0x0186e, 0x0186f,
+ 0x01870, 0x01871, 0x01872, 0x01873, 0x01874, 0x01875, 0x01876, 0x01877,
+ 0x01878, 0x01879, 0x0187a, 0x0187b, 0x0187c, 0x0187d, 0x0187e, 0x0187f,
+ 0x01880, 0x01881, 0x01882, 0x01883, 0x01884, 0x01885, 0x01886, 0x01887,
+ 0x01888, 0x01889, 0x0188a, 0x0188b, 0x0188c, 0x0188d, 0x0188e, 0x0188f,
+ 0x01890, 0x01891, 0x01892, 0x01893, 0x01894, 0x01895, 0x01896, 0x01897,
+ 0x01898, 0x01899, 0x0189a, 0x0189b, 0x0189c, 0x0189d, 0x0189e, 0x0189f,
+ 0x018a0, 0x018a1, 0x018a2, 0x018a3, 0x018a4, 0x018a5, 0x018a6, 0x018a7,
+ 0x018a8, 0x018a9, 0x018aa, 0x018ab, 0x018ac, 0x018ad, 0x018ae, 0x018af,
+ 0x018b0, 0x018b1, 0x018b2, 0x018b3, 0x018b4, 0x018b5, 0x018b6, 0x018b7,
+ 0x018b8, 0x018b9, 0x018ba, 0x018bb, 0x018bc, 0x018bd, 0x018be, 0x018bf,
+ 0x018c0, 0x018c1, 0x018c2, 0x018c3, 0x018c4, 0x018c5, 0x018c6, 0x018c7,
+ 0x018c8, 0x018c9, 0x018ca, 0x018cb, 0x018cc, 0x018cd, 0x018ce, 0x018cf,
+ 0x018d0, 0x018d1, 0x018d2, 0x018d3, 0x018d4, 0x018d5, 0x018d6, 0x018d7,
+ 0x018d8, 0x018d9, 0x018da, 0x018db, 0x018dc, 0x018dd, 0x018de, 0x018df,
+ 0x018e0, 0x018e1, 0x018e2, 0x018e3, 0x018e4, 0x018e5, 0x018e6, 0x018e7,
+ 0x018e8, 0x018e9, 0x018ea, 0x018eb, 0x018ec, 0x018ed, 0x018ee, 0x018ef,
+ 0x018f0, 0x018f1, 0x018f2, 0x018f3, 0x018f4, 0x018f5, 0x018f6, 0x018f7,
+ 0x018f8, 0x018f9, 0x018fa, 0x018fb, 0x018fc, 0x018fd, 0x018fe, 0x018ff
+};
+
+static uint32_t unicode_ci_page_19[] = {
+ 0x01900, 0x01901, 0x01902, 0x01903, 0x01904, 0x01905, 0x01906, 0x01907,
+ 0x01908, 0x01909, 0x0190a, 0x0190b, 0x0190c, 0x0190d, 0x0190e, 0x0190f,
+ 0x01910, 0x01911, 0x01912, 0x01913, 0x01914, 0x01915, 0x01916, 0x01917,
+ 0x01918, 0x01919, 0x0191a, 0x0191b, 0x0191c, 0x0191d, 0x0191e, 0x0191f,
+ 0x01920, 0x01921, 0x01922, 0x01923, 0x01924, 0x01925, 0x01926, 0x01927,
+ 0x01928, 0x01929, 0x0192a, 0x0192b, 0x0192c, 0x0192d, 0x0192e, 0x0192f,
+ 0x01930, 0x01931, 0x01932, 0x01933, 0x01934, 0x01935, 0x01936, 0x01937,
+ 0x01938, 0x00000, 0x00000, 0x00000, 0x0193c, 0x0193d, 0x0193e, 0x0193f,
+ 0x01940, 0x01941, 0x01942, 0x01943, 0x01944, 0x01945, 0x00030, 0x00031,
+ 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039,
+ 0x01950, 0x01951, 0x01952, 0x01953, 0x01954, 0x01955, 0x01956, 0x01957,
+ 0x01958, 0x01959, 0x0195a, 0x0195b, 0x0195c, 0x0195d, 0x0195e, 0x0195f,
+ 0x01960, 0x01961, 0x01962, 0x01963, 0x01964, 0x01965, 0x01966, 0x01967,
+ 0x01968, 0x01969, 0x0196a, 0x0196b, 0x0196c, 0x0196d, 0x0196e, 0x0196f,
+ 0x01970, 0x01971, 0x01972, 0x01973, 0x01974, 0x01975, 0x01976, 0x01977,
+ 0x01978, 0x01979, 0x0197a, 0x0197b, 0x0197c, 0x0197d, 0x0197e, 0x0197f,
+ 0x01980, 0x01981, 0x01982, 0x01983, 0x01984, 0x01985, 0x01986, 0x01987,
+ 0x01988, 0x01989, 0x0198a, 0x0198b, 0x0198c, 0x0198d, 0x0198e, 0x0198f,
+ 0x01990, 0x01991, 0x01992, 0x01993, 0x01994, 0x01995, 0x01996, 0x01997,
+ 0x01998, 0x01999, 0x0199a, 0x0199b, 0x0199c, 0x0199d, 0x0199e, 0x0199f,
+ 0x019a0, 0x019a1, 0x019a2, 0x019a3, 0x019a4, 0x019a5, 0x019a6, 0x019a7,
+ 0x019a8, 0x019a9, 0x019aa, 0x019ab, 0x019ac, 0x019ad, 0x019ae, 0x019af,
+ 0x019b0, 0x019b1, 0x019b2, 0x019b3, 0x019b4, 0x019b5, 0x019b6, 0x019b7,
+ 0x019b8, 0x019b9, 0x019ba, 0x019bb, 0x019bc, 0x019bd, 0x019be, 0x019bf,
+ 0x019c0, 0x019c1, 0x019c2, 0x019c3, 0x019c4, 0x019c5, 0x019c6, 0x019c7,
+ 0x019c8, 0x019c9, 0x019ca, 0x019cb, 0x019cc, 0x019cd, 0x019ce, 0x019cf,
+ 0x019d0, 0x019d1, 0x019d2, 0x019d3, 0x019d4, 0x019d5, 0x019d6, 0x019d7,
+ 0x019d8, 0x019d9, 0x019da, 0x019db, 0x019dc, 0x019dd, 0x019de, 0x019df,
+ 0x019e0, 0x019e1, 0x019e2, 0x019e3, 0x019e4, 0x019e5, 0x019e6, 0x019e7,
+ 0x019e8, 0x019e9, 0x019ea, 0x019eb, 0x019ec, 0x019ed, 0x019ee, 0x019ef,
+ 0x019f0, 0x019f1, 0x019f2, 0x019f3, 0x019f4, 0x019f5, 0x019f6, 0x019f7,
+ 0x019f8, 0x019f9, 0x019fa, 0x019fb, 0x019fc, 0x019fd, 0x019fe, 0x019ff
+};
+
+static uint32_t unicode_ci_page_1d[] = {
+ 0x01d00, 0x01d01, 0x01d02, 0x01d03, 0x01d04, 0x01d05, 0x01d06, 0x01d07,
+ 0x01d08, 0x01d09, 0x01d0a, 0x01d0b, 0x01d0c, 0x01d0d, 0x01d0e, 0x01d0f,
+ 0x01d10, 0x01d11, 0x01d12, 0x01d13, 0x01d14, 0x01d15, 0x01d16, 0x01d17,
+ 0x01d18, 0x01d19, 0x01d1a, 0x01d1b, 0x01d1c, 0x01d1d, 0x01d1e, 0x01d1f,
+ 0x01d20, 0x01d21, 0x01d22, 0x01d23, 0x01d24, 0x01d25, 0x01d26, 0x01d27,
+ 0x01d28, 0x01d29, 0x01d2a, 0x01d2b, 0x00041, 0x000c6, 0x00042, 0x01d2f,
+ 0x00044, 0x00045, 0x0018e, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b,
+ 0x0004c, 0x0004d, 0x0004e, 0x01d3b, 0x0004f, 0x00222, 0x00050, 0x00052,
+ 0x00054, 0x00055, 0x00057, 0x00041, 0x00250, 0x00251, 0x01d02, 0x00042,
+ 0x00044, 0x00045, 0x0018f, 0x00190, 0x01d08, 0x00047, 0x01d09, 0x0004b,
+ 0x0004d, 0x0014a, 0x0004f, 0x00186, 0x01d16, 0x01d17, 0x00050, 0x00054,
+ 0x00055, 0x01d1d, 0x0019c, 0x00056, 0x01d25, 0x00392, 0x00393, 0x00394,
+ 0x003a6, 0x003a7, 0x00049, 0x00052, 0x00055, 0x00056, 0x00392, 0x00393,
+ 0x003a1, 0x003a6, 0x003a7, 0x01d6b, 0x01d6c, 0x01d6d, 0x01d6e, 0x01d6f,
+ 0x01d70, 0x01d71, 0x01d72, 0x01d73, 0x01d74, 0x01d75, 0x01d76, 0x01d77,
+ 0x01d78, 0x01d79, 0x01d7a, 0x01d7b, 0x01d7c, 0x01d7d, 0x01d7e, 0x01d7f,
+ 0x01d80, 0x01d81, 0x01d82, 0x01d83, 0x01d84, 0x01d85, 0x01d86, 0x01d87,
+ 0x01d88, 0x01d89, 0x01d8a, 0x01d8b, 0x01d8c, 0x01d8d, 0x01d8e, 0x01d8f,
+ 0x01d90, 0x01d91, 0x01d92, 0x01d93, 0x01d94, 0x01d95, 0x01d96, 0x01d97,
+ 0x01d98, 0x01d99, 0x01d9a, 0x01d9b, 0x01d9c, 0x01d9d, 0x01d9e, 0x01d9f,
+ 0x01da0, 0x01da1, 0x01da2, 0x01da3, 0x01da4, 0x01da5, 0x01da6, 0x01da7,
+ 0x01da8, 0x01da9, 0x01daa, 0x01dab, 0x01dac, 0x01dad, 0x01dae, 0x01daf,
+ 0x01db0, 0x01db1, 0x01db2, 0x01db3, 0x01db4, 0x01db5, 0x01db6, 0x01db7,
+ 0x01db8, 0x01db9, 0x01dba, 0x01dbb, 0x01dbc, 0x01dbd, 0x01dbe, 0x01dbf,
+ 0x01dc0, 0x01dc1, 0x01dc2, 0x01dc3, 0x01dc4, 0x01dc5, 0x01dc6, 0x01dc7,
+ 0x01dc8, 0x01dc9, 0x01dca, 0x01dcb, 0x01dcc, 0x01dcd, 0x01dce, 0x01dcf,
+ 0x01dd0, 0x01dd1, 0x01dd2, 0x01dd3, 0x01dd4, 0x01dd5, 0x01dd6, 0x01dd7,
+ 0x01dd8, 0x01dd9, 0x01dda, 0x01ddb, 0x01ddc, 0x01ddd, 0x01dde, 0x01ddf,
+ 0x01de0, 0x01de1, 0x01de2, 0x01de3, 0x01de4, 0x01de5, 0x01de6, 0x01de7,
+ 0x01de8, 0x01de9, 0x01dea, 0x01deb, 0x01dec, 0x01ded, 0x01dee, 0x01def,
+ 0x01df0, 0x01df1, 0x01df2, 0x01df3, 0x01df4, 0x01df5, 0x01df6, 0x01df7,
+ 0x01df8, 0x01df9, 0x01dfa, 0x01dfb, 0x01dfc, 0x01dfd, 0x01dfe, 0x01dff
+};
+
+static uint32_t unicode_ci_page_1e[] = {
+ 0x00041, 0x00041, 0x00042, 0x00042, 0x00042, 0x00042, 0x00042, 0x00042,
+ 0x00043, 0x00043, 0x00044, 0x00044, 0x00044, 0x00044, 0x00044, 0x00044,
+ 0x00044, 0x00044, 0x00044, 0x00044, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00046, 0x00046,
+ 0x00047, 0x00047, 0x00048, 0x00048, 0x00048, 0x00048, 0x00048, 0x00048,
+ 0x00048, 0x00048, 0x00048, 0x00048, 0x00049, 0x00049, 0x00049, 0x00049,
+ 0x0004b, 0x0004b, 0x0004b, 0x0004b, 0x0004b, 0x0004b, 0x0004c, 0x0004c,
+ 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004c, 0x0004d, 0x0004d,
+ 0x0004d, 0x0004d, 0x0004d, 0x0004d, 0x0004e, 0x0004e, 0x0004e, 0x0004e,
+ 0x0004e, 0x0004e, 0x0004e, 0x0004e, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x00050, 0x00050, 0x00050, 0x00050,
+ 0x00052, 0x00052, 0x00052, 0x00052, 0x00052, 0x00052, 0x00052, 0x00052,
+ 0x00053, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053, 0x00053,
+ 0x00053, 0x00053, 0x00054, 0x00054, 0x00054, 0x00054, 0x00054, 0x00054,
+ 0x00054, 0x00054, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00056, 0x00056, 0x00056, 0x00056,
+ 0x00057, 0x00057, 0x00057, 0x00057, 0x00057, 0x00057, 0x00057, 0x00057,
+ 0x00057, 0x00057, 0x00058, 0x00058, 0x00058, 0x00058, 0x00059, 0x00059,
+ 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x0005a, 0x00048, 0x00054,
+ 0x00057, 0x00059, 0x01e9a, 0x00053, 0x01e9c, 0x01e9d, 0x01e9e, 0x01e9f,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041,
+ 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041, 0x00041,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045, 0x00045,
+ 0x00049, 0x00049, 0x00049, 0x00049, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x0004f,
+ 0x0004f, 0x0004f, 0x0004f, 0x0004f, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055, 0x00055,
+ 0x00055, 0x00055, 0x00059, 0x00059, 0x00059, 0x00059, 0x00059, 0x00059,
+ 0x00059, 0x00059, 0x01efa, 0x01efb, 0x01efc, 0x01efd, 0x01efe, 0x01eff
+};
+
+static uint32_t unicode_ci_page_1f[] = {
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391,
+ 0x00395, 0x00395, 0x00395, 0x00395, 0x00395, 0x00395, 0x01f16, 0x01f17,
+ 0x00395, 0x00395, 0x00395, 0x00395, 0x00395, 0x00395, 0x01f1e, 0x01f1f,
+ 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397,
+ 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397,
+ 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399,
+ 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399, 0x00399,
+ 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x01f46, 0x01f47,
+ 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x0039f, 0x01f4e, 0x01f4f,
+ 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a5,
+ 0x01f58, 0x003a5, 0x01f5a, 0x003a5, 0x01f5c, 0x003a5, 0x01f5e, 0x003a5,
+ 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9,
+ 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9,
+ 0x00391, 0x00391, 0x00395, 0x00395, 0x00397, 0x00397, 0x00399, 0x00399,
+ 0x0039f, 0x0039f, 0x003a5, 0x003a5, 0x003a9, 0x003a9, 0x01f7e, 0x01f7f,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x00391,
+ 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397,
+ 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397, 0x00397,
+ 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9,
+ 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9, 0x003a9,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x01fb5, 0x00391, 0x00391,
+ 0x00391, 0x00391, 0x00391, 0x00391, 0x00391, 0x01fbd, 0x00399, 0x01fbd,
+ 0x01fc0, 0x000a8, 0x00397, 0x00397, 0x00397, 0x01fc5, 0x00397, 0x00397,
+ 0x00395, 0x00395, 0x00397, 0x00397, 0x00397, 0x01fbd, 0x01fbd, 0x01fbd,
+ 0x00399, 0x00399, 0x00399, 0x00399, 0x01fd4, 0x01fd5, 0x00399, 0x00399,
+ 0x00399, 0x00399, 0x00399, 0x00399, 0x01fdc, 0x01fdd, 0x01fdd, 0x01fdd,
+ 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a1, 0x003a1, 0x003a5, 0x003a5,
+ 0x003a5, 0x003a5, 0x003a5, 0x003a5, 0x003a1, 0x000a8, 0x000a8, 0x00060,
+ 0x01ff0, 0x01ff1, 0x003a9, 0x003a9, 0x003a9, 0x01ff5, 0x003a9, 0x003a9,
+ 0x0039f, 0x0039f, 0x003a9, 0x003a9, 0x003a9, 0x000b4, 0x01fdd, 0x01fff
+};
+
+static uint32_t unicode_ci_page_20[] = {
+ 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020, 0x00020,
+ 0x00020, 0x00020, 0x00020, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x02010, 0x02010, 0x02012, 0x02013, 0x02014, 0x02015, 0x02016, 0x02017,
+ 0x02018, 0x02019, 0x0201a, 0x0201b, 0x0201c, 0x0201d, 0x0201e, 0x0201f,
+ 0x02020, 0x02021, 0x02022, 0x02023, 0x0002e, 0x02025, 0x02026, 0x02027,
+ 0x02028, 0x02029, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00020,
+ 0x02030, 0x02031, 0x02032, 0x02033, 0x02034, 0x02035, 0x02036, 0x02037,
+ 0x02038, 0x02039, 0x0203a, 0x0203b, 0x0203c, 0x0203d, 0x0203e, 0x0203f,
+ 0x02040, 0x02041, 0x02042, 0x02043, 0x02044, 0x02045, 0x02046, 0x02047,
+ 0x02048, 0x02049, 0x0204a, 0x0204b, 0x0204c, 0x0204d, 0x0204e, 0x0204f,
+ 0x02050, 0x02051, 0x02052, 0x02053, 0x02054, 0x02055, 0x02056, 0x02057,
+ 0x02058, 0x02059, 0x0205a, 0x0205b, 0x0205c, 0x0205d, 0x0205e, 0x00020,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x02064, 0x02065, 0x02066, 0x02067,
+ 0x02068, 0x02069, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00030, 0x00049, 0x02072, 0x02073, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0002b, 0x0207b, 0x0003d, 0x00028, 0x00029, 0x0004e,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0002b, 0x0207b, 0x0003d, 0x00028, 0x00029, 0x0208f,
+ 0x02090, 0x02091, 0x02092, 0x02093, 0x02094, 0x02095, 0x02096, 0x02097,
+ 0x02098, 0x02099, 0x0209a, 0x0209b, 0x0209c, 0x0209d, 0x0209e, 0x0209f,
+ 0x020a0, 0x020a1, 0x020a2, 0x020a3, 0x020a4, 0x020a5, 0x020a6, 0x020a7,
+ 0x020a8, 0x020a9, 0x020aa, 0x020ab, 0x020ac, 0x020ad, 0x020ae, 0x020af,
+ 0x020b0, 0x020b1, 0x020b2, 0x020b3, 0x020b4, 0x020b5, 0x020b6, 0x020b7,
+ 0x020b8, 0x020b9, 0x020ba, 0x020bb, 0x020bc, 0x020bd, 0x020be, 0x020bf,
+ 0x020c0, 0x020c1, 0x020c2, 0x020c3, 0x020c4, 0x020c5, 0x020c6, 0x020c7,
+ 0x020c8, 0x020c9, 0x020ca, 0x020cb, 0x020cc, 0x020cd, 0x020ce, 0x020cf,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x020eb, 0x020ec, 0x020ed, 0x020ee, 0x020ef,
+ 0x020f0, 0x020f1, 0x020f2, 0x020f3, 0x020f4, 0x020f5, 0x020f6, 0x020f7,
+ 0x020f8, 0x020f9, 0x020fa, 0x020fb, 0x020fc, 0x020fd, 0x020fe, 0x020ff
+};
+
+static uint32_t unicode_ci_page_21[] = {
+ 0x02100, 0x02101, 0x00043, 0x02103, 0x02104, 0x02105, 0x02106, 0x00190,
+ 0x02108, 0x02109, 0x00047, 0x00048, 0x00048, 0x00048, 0x00048, 0x00126,
+ 0x00049, 0x00049, 0x0004c, 0x0004c, 0x02114, 0x0004e, 0x02116, 0x02117,
+ 0x02118, 0x00050, 0x00051, 0x00052, 0x00052, 0x00052, 0x0211e, 0x0211f,
+ 0x02120, 0x02121, 0x02122, 0x02123, 0x0005a, 0x02125, 0x003a9, 0x02127,
+ 0x0005a, 0x02129, 0x0004b, 0x00041, 0x00042, 0x00043, 0x0212e, 0x00045,
+ 0x00045, 0x00046, 0x02132, 0x0004d, 0x0004f, 0x005d0, 0x005d1, 0x005d2,
+ 0x005d3, 0x00049, 0x0213a, 0x0213b, 0x0213c, 0x00393, 0x00393, 0x003a0,
+ 0x02140, 0x02141, 0x02142, 0x02143, 0x02144, 0x00044, 0x00044, 0x00045,
+ 0x00049, 0x0004a, 0x0214a, 0x0214b, 0x0214c, 0x0214d, 0x0214e, 0x0214f,
+ 0x02150, 0x02151, 0x02152, 0x02153, 0x02154, 0x02155, 0x02156, 0x02157,
+ 0x02158, 0x02159, 0x0215a, 0x0215b, 0x0215c, 0x0215d, 0x0215e, 0x0215f,
+ 0x00049, 0x02161, 0x02162, 0x02163, 0x00056, 0x02165, 0x02166, 0x02167,
+ 0x02168, 0x00058, 0x0216a, 0x0216b, 0x0004c, 0x00043, 0x00044, 0x0004d,
+ 0x00049, 0x02161, 0x02162, 0x02163, 0x00056, 0x02165, 0x02166, 0x02167,
+ 0x02168, 0x00058, 0x0216a, 0x0216b, 0x0004c, 0x00043, 0x00044, 0x0004d,
+ 0x02180, 0x02181, 0x02182, 0x02183, 0x02184, 0x02185, 0x02186, 0x02187,
+ 0x02188, 0x02189, 0x0218a, 0x0218b, 0x0218c, 0x0218d, 0x0218e, 0x0218f,
+ 0x02190, 0x02191, 0x02192, 0x02193, 0x02194, 0x02195, 0x02196, 0x02197,
+ 0x02198, 0x02199, 0x02190, 0x02192, 0x0219c, 0x0219d, 0x0219e, 0x0219f,
+ 0x021a0, 0x021a1, 0x021a2, 0x021a3, 0x021a4, 0x021a5, 0x021a6, 0x021a7,
+ 0x021a8, 0x021a9, 0x021aa, 0x021ab, 0x021ac, 0x021ad, 0x02194, 0x021af,
+ 0x021b0, 0x021b1, 0x021b2, 0x021b3, 0x021b4, 0x021b5, 0x021b6, 0x021b7,
+ 0x021b8, 0x021b9, 0x021ba, 0x021bb, 0x021bc, 0x021bd, 0x021be, 0x021bf,
+ 0x021c0, 0x021c1, 0x021c2, 0x021c3, 0x021c4, 0x021c5, 0x021c6, 0x021c7,
+ 0x021c8, 0x021c9, 0x021ca, 0x021cb, 0x021cc, 0x021cd, 0x021ce, 0x021cf,
+ 0x021cd, 0x021d1, 0x021cf, 0x021d3, 0x021ce, 0x021d5, 0x021d6, 0x021d7,
+ 0x021d8, 0x021d9, 0x021da, 0x021db, 0x021dc, 0x021dd, 0x021de, 0x021df,
+ 0x021e0, 0x021e1, 0x021e2, 0x021e3, 0x021e4, 0x021e5, 0x021e6, 0x021e7,
+ 0x021e8, 0x021e9, 0x021ea, 0x021eb, 0x021ec, 0x021ed, 0x021ee, 0x021ef,
+ 0x021f0, 0x021f1, 0x021f2, 0x021f3, 0x021f4, 0x021f5, 0x021f6, 0x021f7,
+ 0x021f8, 0x021f9, 0x021fa, 0x021fb, 0x021fc, 0x021fd, 0x021fe, 0x021ff
+};
+
+static uint32_t unicode_ci_page_22[] = {
+ 0x02200, 0x02201, 0x02202, 0x02203, 0x02203, 0x02205, 0x02206, 0x02207,
+ 0x02208, 0x02208, 0x0220a, 0x0220b, 0x0220b, 0x0220d, 0x0220e, 0x0220f,
+ 0x02210, 0x02140, 0x0207b, 0x02213, 0x02214, 0x02215, 0x02216, 0x02217,
+ 0x02218, 0x02219, 0x0221a, 0x0221b, 0x0221c, 0x0221d, 0x0221e, 0x0221f,
+ 0x02220, 0x02221, 0x02222, 0x02223, 0x02223, 0x02225, 0x02225, 0x02227,
+ 0x02228, 0x02229, 0x0222a, 0x0222b, 0x0222c, 0x0222d, 0x0222e, 0x0222f,
+ 0x02230, 0x02231, 0x02232, 0x02233, 0x02234, 0x02235, 0x02236, 0x02237,
+ 0x02238, 0x02239, 0x0223a, 0x0223b, 0x0223c, 0x0223d, 0x0223e, 0x0223f,
+ 0x02240, 0x0223c, 0x02242, 0x02243, 0x02243, 0x02245, 0x02246, 0x02245,
+ 0x02248, 0x02248, 0x0224a, 0x0224b, 0x0224c, 0x0224d, 0x0224e, 0x0224f,
+ 0x02250, 0x02251, 0x02252, 0x02253, 0x02254, 0x02255, 0x02256, 0x02257,
+ 0x02258, 0x02259, 0x0225a, 0x0225b, 0x0225c, 0x0225d, 0x0225e, 0x0225f,
+ 0x0003d, 0x02261, 0x02261, 0x02263, 0x02264, 0x02265, 0x02266, 0x02267,
+ 0x02268, 0x02269, 0x0226a, 0x0226b, 0x0226c, 0x0224d, 0x0003c, 0x0003e,
+ 0x02264, 0x02265, 0x02272, 0x02273, 0x02272, 0x02273, 0x02276, 0x02277,
+ 0x02276, 0x02277, 0x0227a, 0x0227b, 0x0227c, 0x0227d, 0x0227e, 0x0227f,
+ 0x0227a, 0x0227b, 0x02282, 0x02283, 0x02282, 0x02283, 0x02286, 0x02287,
+ 0x02286, 0x02287, 0x0228a, 0x0228b, 0x0228c, 0x0228d, 0x0228e, 0x0228f,
+ 0x02290, 0x02291, 0x02292, 0x02293, 0x02294, 0x02295, 0x02296, 0x02297,
+ 0x02298, 0x02299, 0x0229a, 0x0229b, 0x0229c, 0x0229d, 0x0229e, 0x0229f,
+ 0x022a0, 0x022a1, 0x022a2, 0x022a3, 0x022a4, 0x022a5, 0x022a6, 0x022a7,
+ 0x022a8, 0x022a9, 0x022aa, 0x022ab, 0x022a2, 0x022a8, 0x022a9, 0x022ab,
+ 0x022b0, 0x022b1, 0x022b2, 0x022b3, 0x022b4, 0x022b5, 0x022b6, 0x022b7,
+ 0x022b8, 0x022b9, 0x022ba, 0x022bb, 0x022bc, 0x022bd, 0x022be, 0x022bf,
+ 0x022c0, 0x022c1, 0x022c2, 0x022c3, 0x022c4, 0x022c5, 0x022c6, 0x022c7,
+ 0x022c8, 0x022c9, 0x022ca, 0x022cb, 0x022cc, 0x022cd, 0x022ce, 0x022cf,
+ 0x022d0, 0x022d1, 0x022d2, 0x022d3, 0x022d4, 0x022d5, 0x022d6, 0x022d7,
+ 0x022d8, 0x022d9, 0x022da, 0x022db, 0x022dc, 0x022dd, 0x022de, 0x022df,
+ 0x0227c, 0x0227d, 0x02291, 0x02292, 0x022e4, 0x022e5, 0x022e6, 0x022e7,
+ 0x022e8, 0x022e9, 0x022b2, 0x022b3, 0x022b4, 0x022b5, 0x022ee, 0x022ef,
+ 0x022f0, 0x022f1, 0x022f2, 0x022f3, 0x022f4, 0x022f5, 0x022f6, 0x022f7,
+ 0x022f8, 0x022f9, 0x022fa, 0x022fb, 0x022fc, 0x022fd, 0x022fe, 0x022ff
+};
+
+static uint32_t unicode_ci_page_24[] = {
+ 0x02400, 0x02401, 0x02402, 0x02403, 0x02404, 0x02405, 0x02406, 0x02407,
+ 0x02408, 0x02409, 0x0240a, 0x0240b, 0x0240c, 0x0240d, 0x0240e, 0x0240f,
+ 0x02410, 0x02411, 0x02412, 0x02413, 0x02414, 0x02415, 0x02416, 0x02417,
+ 0x02418, 0x02419, 0x0241a, 0x0241b, 0x0241c, 0x0241d, 0x0241e, 0x0241f,
+ 0x02420, 0x02421, 0x02422, 0x02423, 0x02424, 0x02425, 0x02426, 0x02427,
+ 0x02428, 0x02429, 0x0242a, 0x0242b, 0x0242c, 0x0242d, 0x0242e, 0x0242f,
+ 0x02430, 0x02431, 0x02432, 0x02433, 0x02434, 0x02435, 0x02436, 0x02437,
+ 0x02438, 0x02439, 0x0243a, 0x0243b, 0x0243c, 0x0243d, 0x0243e, 0x0243f,
+ 0x02440, 0x02441, 0x02442, 0x02443, 0x02444, 0x02445, 0x02446, 0x02447,
+ 0x02448, 0x02449, 0x0244a, 0x0244b, 0x0244c, 0x0244d, 0x0244e, 0x0244f,
+ 0x02450, 0x02451, 0x02452, 0x02453, 0x02454, 0x02455, 0x02456, 0x02457,
+ 0x02458, 0x02459, 0x0245a, 0x0245b, 0x0245c, 0x0245d, 0x0245e, 0x0245f,
+ 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038,
+ 0x00039, 0x02469, 0x0246a, 0x0246b, 0x0246c, 0x0246d, 0x0246e, 0x0246f,
+ 0x02470, 0x02471, 0x02472, 0x02473, 0x02474, 0x02475, 0x02476, 0x02477,
+ 0x02478, 0x02479, 0x0247a, 0x0247b, 0x0247c, 0x0247d, 0x0247e, 0x0247f,
+ 0x02480, 0x02481, 0x02482, 0x02483, 0x02484, 0x02485, 0x02486, 0x02487,
+ 0x02488, 0x02489, 0x0248a, 0x0248b, 0x0248c, 0x0248d, 0x0248e, 0x0248f,
+ 0x02490, 0x02491, 0x02492, 0x02493, 0x02494, 0x02495, 0x02496, 0x02497,
+ 0x02498, 0x02499, 0x0249a, 0x0249b, 0x0249c, 0x0249d, 0x0249e, 0x0249f,
+ 0x024a0, 0x024a1, 0x024a2, 0x024a3, 0x024a4, 0x024a5, 0x024a6, 0x024a7,
+ 0x024a8, 0x024a9, 0x024aa, 0x024ab, 0x024ac, 0x024ad, 0x024ae, 0x024af,
+ 0x024b0, 0x024b1, 0x024b2, 0x024b3, 0x024b4, 0x024b5, 0x00041, 0x00042,
+ 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a,
+ 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052,
+ 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a,
+ 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048,
+ 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050,
+ 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058,
+ 0x00059, 0x0005a, 0x00030, 0x0246a, 0x0246b, 0x0246c, 0x0246d, 0x0246e,
+ 0x0246f, 0x02470, 0x02471, 0x02472, 0x02473, 0x00031, 0x00032, 0x00033,
+ 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x02469, 0x00030
+};
+
+static uint32_t unicode_ci_page_27[] = {
+ 0x02700, 0x02701, 0x02702, 0x02703, 0x02704, 0x02705, 0x02706, 0x02707,
+ 0x02708, 0x02709, 0x0270a, 0x0270b, 0x0270c, 0x0270d, 0x0270e, 0x0270f,
+ 0x02710, 0x02711, 0x02712, 0x02713, 0x02714, 0x02715, 0x02716, 0x02717,
+ 0x02718, 0x02719, 0x0271a, 0x0271b, 0x0271c, 0x0271d, 0x0271e, 0x0271f,
+ 0x02720, 0x02721, 0x02722, 0x02723, 0x02724, 0x02725, 0x02726, 0x02727,
+ 0x02728, 0x02729, 0x0272a, 0x0272b, 0x0272c, 0x0272d, 0x0272e, 0x0272f,
+ 0x02730, 0x02731, 0x02732, 0x02733, 0x02734, 0x02735, 0x02736, 0x02737,
+ 0x02738, 0x02739, 0x0273a, 0x0273b, 0x0273c, 0x0273d, 0x0273e, 0x0273f,
+ 0x02740, 0x02741, 0x02742, 0x02743, 0x02744, 0x02745, 0x02746, 0x02747,
+ 0x02748, 0x02749, 0x0274a, 0x0274b, 0x0274c, 0x0274d, 0x0274e, 0x0274f,
+ 0x02750, 0x02751, 0x02752, 0x02753, 0x02754, 0x02755, 0x02756, 0x02757,
+ 0x02758, 0x02759, 0x0275a, 0x0275b, 0x0275c, 0x0275d, 0x0275e, 0x0275f,
+ 0x02760, 0x02761, 0x02762, 0x02763, 0x02764, 0x02765, 0x02766, 0x02767,
+ 0x02768, 0x02769, 0x0276a, 0x0276b, 0x0276c, 0x0276d, 0x0276e, 0x0276f,
+ 0x02770, 0x02771, 0x02772, 0x02773, 0x02774, 0x02775, 0x00031, 0x00032,
+ 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038, 0x00039, 0x02469,
+ 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037, 0x00038,
+ 0x00039, 0x02469, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036,
+ 0x00037, 0x00038, 0x00039, 0x02469, 0x02794, 0x02795, 0x02796, 0x02797,
+ 0x02798, 0x02799, 0x0279a, 0x0279b, 0x0279c, 0x0279d, 0x0279e, 0x0279f,
+ 0x027a0, 0x027a1, 0x027a2, 0x027a3, 0x027a4, 0x027a5, 0x027a6, 0x027a7,
+ 0x027a8, 0x027a9, 0x027aa, 0x027ab, 0x027ac, 0x027ad, 0x027ae, 0x027af,
+ 0x027b0, 0x027b1, 0x027b2, 0x027b3, 0x027b4, 0x027b5, 0x027b6, 0x027b7,
+ 0x027b8, 0x027b9, 0x027ba, 0x027bb, 0x027bc, 0x027bd, 0x027be, 0x027bf,
+ 0x027c0, 0x027c1, 0x027c2, 0x027c3, 0x027c4, 0x027c5, 0x027c6, 0x027c7,
+ 0x027c8, 0x027c9, 0x027ca, 0x027cb, 0x027cc, 0x027cd, 0x027ce, 0x027cf,
+ 0x027d0, 0x027d1, 0x027d2, 0x027d3, 0x027d4, 0x027d5, 0x027d6, 0x027d7,
+ 0x027d8, 0x027d9, 0x027da, 0x027db, 0x027dc, 0x027dd, 0x027de, 0x027df,
+ 0x027e0, 0x027e1, 0x027e2, 0x027e3, 0x027e4, 0x027e5, 0x027e6, 0x027e7,
+ 0x027e8, 0x027e9, 0x027ea, 0x027eb, 0x027ec, 0x027ed, 0x027ee, 0x027ef,
+ 0x027f0, 0x027f1, 0x027f2, 0x027f3, 0x027f4, 0x027f5, 0x027f6, 0x027f7,
+ 0x027f8, 0x027f9, 0x027fa, 0x027fb, 0x027fc, 0x027fd, 0x027fe, 0x027ff
+};
+
+static uint32_t unicode_ci_page_2a[] = {
+ 0x02a00, 0x02a01, 0x02a02, 0x02a03, 0x02a04, 0x02a05, 0x02a06, 0x02a07,
+ 0x02a08, 0x02a09, 0x02a0a, 0x02a0b, 0x02a0c, 0x02a0d, 0x02a0e, 0x02a0f,
+ 0x02a10, 0x02a11, 0x02a12, 0x02a13, 0x02a14, 0x02a15, 0x02a16, 0x02a17,
+ 0x02a18, 0x02a19, 0x02a1a, 0x02a1b, 0x02a1c, 0x02a1d, 0x02a1e, 0x02a1f,
+ 0x02a20, 0x02a21, 0x02a22, 0x02a23, 0x02a24, 0x02a25, 0x02a26, 0x02a27,
+ 0x02a28, 0x02a29, 0x02a2a, 0x02a2b, 0x02a2c, 0x02a2d, 0x02a2e, 0x02a2f,
+ 0x02a30, 0x02a31, 0x02a32, 0x02a33, 0x02a34, 0x02a35, 0x02a36, 0x02a37,
+ 0x02a38, 0x02a39, 0x02a3a, 0x02a3b, 0x02a3c, 0x02a3d, 0x02a3e, 0x02a3f,
+ 0x02a40, 0x02a41, 0x02a42, 0x02a43, 0x02a44, 0x02a45, 0x02a46, 0x02a47,
+ 0x02a48, 0x02a49, 0x02a4a, 0x02a4b, 0x02a4c, 0x02a4d, 0x02a4e, 0x02a4f,
+ 0x02a50, 0x02a51, 0x02a52, 0x02a53, 0x02a54, 0x02a55, 0x02a56, 0x02a57,
+ 0x02a58, 0x02a59, 0x02a5a, 0x02a5b, 0x02a5c, 0x02a5d, 0x02a5e, 0x02a5f,
+ 0x02a60, 0x02a61, 0x02a62, 0x02a63, 0x02a64, 0x02a65, 0x02a66, 0x02a67,
+ 0x02a68, 0x02a69, 0x02a6a, 0x02a6b, 0x02a6c, 0x02a6d, 0x02a6e, 0x02a6f,
+ 0x02a70, 0x02a71, 0x02a72, 0x02a73, 0x02a74, 0x02a75, 0x02a76, 0x02a77,
+ 0x02a78, 0x02a79, 0x02a7a, 0x02a7b, 0x02a7c, 0x02a7d, 0x02a7e, 0x02a7f,
+ 0x02a80, 0x02a81, 0x02a82, 0x02a83, 0x02a84, 0x02a85, 0x02a86, 0x02a87,
+ 0x02a88, 0x02a89, 0x02a8a, 0x02a8b, 0x02a8c, 0x02a8d, 0x02a8e, 0x02a8f,
+ 0x02a90, 0x02a91, 0x02a92, 0x02a93, 0x02a94, 0x02a95, 0x02a96, 0x02a97,
+ 0x02a98, 0x02a99, 0x02a9a, 0x02a9b, 0x02a9c, 0x02a9d, 0x02a9e, 0x02a9f,
+ 0x02aa0, 0x02aa1, 0x02aa2, 0x02aa3, 0x02aa4, 0x02aa5, 0x02aa6, 0x02aa7,
+ 0x02aa8, 0x02aa9, 0x02aaa, 0x02aab, 0x02aac, 0x02aad, 0x02aae, 0x02aaf,
+ 0x02ab0, 0x02ab1, 0x02ab2, 0x02ab3, 0x02ab4, 0x02ab5, 0x02ab6, 0x02ab7,
+ 0x02ab8, 0x02ab9, 0x02aba, 0x02abb, 0x02abc, 0x02abd, 0x02abe, 0x02abf,
+ 0x02ac0, 0x02ac1, 0x02ac2, 0x02ac3, 0x02ac4, 0x02ac5, 0x02ac6, 0x02ac7,
+ 0x02ac8, 0x02ac9, 0x02aca, 0x02acb, 0x02acc, 0x02acd, 0x02ace, 0x02acf,
+ 0x02ad0, 0x02ad1, 0x02ad2, 0x02ad3, 0x02ad4, 0x02ad5, 0x02ad6, 0x02ad7,
+ 0x02ad8, 0x02ad9, 0x02ada, 0x02adb, 0x02adc, 0x02adc, 0x02ade, 0x02adf,
+ 0x02ae0, 0x02ae1, 0x02ae2, 0x02ae3, 0x02ae4, 0x02ae5, 0x02ae6, 0x02ae7,
+ 0x02ae8, 0x02ae9, 0x02aea, 0x02aeb, 0x02aec, 0x02aed, 0x02aee, 0x02aef,
+ 0x02af0, 0x02af1, 0x02af2, 0x02af3, 0x02af4, 0x02af5, 0x02af6, 0x02af7,
+ 0x02af8, 0x02af9, 0x02afa, 0x02afb, 0x02afc, 0x02afd, 0x02afe, 0x02aff
+};
+
+static uint32_t unicode_ci_page_2e[] = {
+ 0x02e00, 0x02e01, 0x02e02, 0x02e03, 0x02e04, 0x02e05, 0x02e06, 0x02e07,
+ 0x02e08, 0x02e09, 0x02e0a, 0x02e0b, 0x02e0c, 0x02e0d, 0x02e0e, 0x02e0f,
+ 0x02e10, 0x02e11, 0x02e12, 0x02e13, 0x02e14, 0x02e15, 0x02e16, 0x02e17,
+ 0x02e18, 0x02e19, 0x02e1a, 0x02e1b, 0x02e1c, 0x02e1d, 0x02e1e, 0x02e1f,
+ 0x02e20, 0x02e21, 0x02e22, 0x02e23, 0x02e24, 0x02e25, 0x02e26, 0x02e27,
+ 0x02e28, 0x02e29, 0x02e2a, 0x02e2b, 0x02e2c, 0x02e2d, 0x02e2e, 0x02e2f,
+ 0x02e30, 0x02e31, 0x02e32, 0x02e33, 0x02e34, 0x02e35, 0x02e36, 0x02e37,
+ 0x02e38, 0x02e39, 0x02e3a, 0x02e3b, 0x02e3c, 0x02e3d, 0x02e3e, 0x02e3f,
+ 0x02e40, 0x02e41, 0x02e42, 0x02e43, 0x02e44, 0x02e45, 0x02e46, 0x02e47,
+ 0x02e48, 0x02e49, 0x02e4a, 0x02e4b, 0x02e4c, 0x02e4d, 0x02e4e, 0x02e4f,
+ 0x02e50, 0x02e51, 0x02e52, 0x02e53, 0x02e54, 0x02e55, 0x02e56, 0x02e57,
+ 0x02e58, 0x02e59, 0x02e5a, 0x02e5b, 0x02e5c, 0x02e5d, 0x02e5e, 0x02e5f,
+ 0x02e60, 0x02e61, 0x02e62, 0x02e63, 0x02e64, 0x02e65, 0x02e66, 0x02e67,
+ 0x02e68, 0x02e69, 0x02e6a, 0x02e6b, 0x02e6c, 0x02e6d, 0x02e6e, 0x02e6f,
+ 0x02e70, 0x02e71, 0x02e72, 0x02e73, 0x02e74, 0x02e75, 0x02e76, 0x02e77,
+ 0x02e78, 0x02e79, 0x02e7a, 0x02e7b, 0x02e7c, 0x02e7d, 0x02e7e, 0x02e7f,
+ 0x02e80, 0x02e81, 0x02e82, 0x02e83, 0x0319a, 0x02e85, 0x02e86, 0x02f0f,
+ 0x02f11, 0x02e89, 0x02f18, 0x02e8b, 0x02f29, 0x02f29, 0x02e8e, 0x02e8f,
+ 0x02e8e, 0x02e8f, 0x02e92, 0x02e93, 0x02e94, 0x02e95, 0x02e96, 0x02f3c,
+ 0x02e98, 0x02e99, 0x02e9a, 0x02e9b, 0x02f47, 0x02f49, 0x02e9e, 0x02e9f,
+ 0x02ea0, 0x02ea1, 0x02ea2, 0x02ea3, 0x02ea4, 0x02ea4, 0x02ea6, 0x02f5c,
+ 0x02ea8, 0x02ea9, 0x02eaa, 0x02f6c, 0x02f70, 0x02ead, 0x02f75, 0x02eaf,
+ 0x02eb0, 0x02eb1, 0x02eb2, 0x02eb1, 0x02eb1, 0x02eb2, 0x02eb7, 0x02eb7,
+ 0x02eb8, 0x02eb9, 0x02eba, 0x02f80, 0x02f81, 0x02f85, 0x02ebe, 0x02ebe,
+ 0x02ebe, 0x02ec1, 0x02ec2, 0x02ec3, 0x02ec4, 0x02ec5, 0x02f93, 0x02f93,
+ 0x02ec8, 0x02ec9, 0x02f9c, 0x02ecb, 0x02ecc, 0x02ecc, 0x02ecc, 0x02fa2,
+ 0x02ed0, 0x02fa7, 0x02ed2, 0x02ed3, 0x02ed4, 0x02fa9, 0x02ed6, 0x02fac,
+ 0x02ed8, 0x02ed9, 0x02eda, 0x02edb, 0x02edc, 0x02fb7, 0x02ede, 0x02ede,
+ 0x02ee0, 0x02fb8, 0x02ee2, 0x02fbb, 0x02fc1, 0x02ee5, 0x02ee6, 0x02ee7,
+ 0x02ee8, 0x02ee9, 0x02eea, 0x02eeb, 0x02eec, 0x02eed, 0x02eee, 0x02eef,
+ 0x02ef0, 0x02ef2, 0x02ef2, 0x02ef3, 0x02ef4, 0x02ef5, 0x02ef6, 0x02ef7,
+ 0x02ef8, 0x02ef9, 0x02efa, 0x02efb, 0x02efc, 0x02efd, 0x02efe, 0x02eff
+};
+
+static uint32_t unicode_ci_page_2f[] = {
+ 0x03192, 0x02f01, 0x02e80, 0x02f03, 0x0319a, 0x02f05, 0x03193, 0x02f07,
+ 0x0319f, 0x02f09, 0x02f0a, 0x02f0b, 0x02e86, 0x02f0d, 0x02f0e, 0x02f0f,
+ 0x02f10, 0x02f11, 0x02f12, 0x02f13, 0x02f14, 0x02f15, 0x02f16, 0x02f17,
+ 0x02f18, 0x02e8b, 0x02e81, 0x02f1b, 0x02f1c, 0x02f1d, 0x02f1e, 0x02f1f,
+ 0x02f20, 0x02f21, 0x02f22, 0x02f23, 0x02f24, 0x02f25, 0x02f26, 0x02f27,
+ 0x02f28, 0x02f29, 0x02e8e, 0x02f2b, 0x02f2c, 0x02f2d, 0x02f2e, 0x02f2f,
+ 0x02f30, 0x02f31, 0x02f32, 0x02e93, 0x02f34, 0x02f35, 0x02f36, 0x02f37,
+ 0x02f38, 0x02e95, 0x02f3a, 0x02f3b, 0x02f3c, 0x02f3d, 0x02f3e, 0x02f3f,
+ 0x02f40, 0x02f41, 0x02f42, 0x02f43, 0x02f44, 0x02f45, 0x02f46, 0x02f47,
+ 0x02f48, 0x02f49, 0x02f4a, 0x02f4b, 0x02f4c, 0x02f4d, 0x02f4e, 0x02f4f,
+ 0x02f50, 0x02f51, 0x02f52, 0x02f53, 0x02f54, 0x02f55, 0x02f56, 0x02f57,
+ 0x02f58, 0x02f59, 0x02f5a, 0x02f5b, 0x02f5c, 0x02f5d, 0x02f5e, 0x02f5f,
+ 0x02f60, 0x02f61, 0x02f62, 0x02f63, 0x02f64, 0x02f65, 0x02eaa, 0x02f67,
+ 0x02f68, 0x02f69, 0x02f6a, 0x02f6b, 0x02f6c, 0x02f6d, 0x02f6e, 0x02f6f,
+ 0x02f70, 0x02f71, 0x02f72, 0x02f73, 0x02f74, 0x02f75, 0x02f76, 0x02f77,
+ 0x02f78, 0x02f79, 0x02eb7, 0x02f7b, 0x02f7c, 0x02f7d, 0x02f7e, 0x02f7f,
+ 0x02f80, 0x02f81, 0x02f82, 0x02f83, 0x02f84, 0x02f85, 0x02f86, 0x02f87,
+ 0x02f88, 0x02f89, 0x02f8a, 0x02f8b, 0x02f8c, 0x02f8d, 0x02f8e, 0x02f8f,
+ 0x02f90, 0x02f91, 0x02f92, 0x02f93, 0x02f94, 0x02f95, 0x02f96, 0x02f97,
+ 0x02f98, 0x02f99, 0x02f9a, 0x02f9b, 0x02f9c, 0x02f9d, 0x02f9e, 0x02f9f,
+ 0x02fa0, 0x02fa1, 0x02fa2, 0x02fa3, 0x02fa4, 0x02fa5, 0x02fa6, 0x02fa7,
+ 0x02fa8, 0x02fa9, 0x02faa, 0x02fab, 0x02fac, 0x02fad, 0x02fae, 0x02faf,
+ 0x02fb0, 0x02fb1, 0x02fb2, 0x02fb3, 0x02fb4, 0x02fb5, 0x02fb6, 0x02fb7,
+ 0x02fb8, 0x02fb9, 0x02fba, 0x02fbb, 0x02fbc, 0x02fbd, 0x02fbe, 0x02fbf,
+ 0x02fc0, 0x02fc1, 0x02fc2, 0x02fc3, 0x02ee7, 0x02fc5, 0x02fc6, 0x02fc7,
+ 0x02fc8, 0x02fc9, 0x02fca, 0x02fcb, 0x02fcc, 0x02fcd, 0x02fce, 0x02fcf,
+ 0x02fd0, 0x02eeb, 0x02eed, 0x02eef, 0x02ef2, 0x02fd5, 0x02fd6, 0x02fd7,
+ 0x02fd8, 0x02fd9, 0x02fda, 0x02fdb, 0x02fdc, 0x02fdd, 0x02fde, 0x02fdf,
+ 0x02fe0, 0x02fe1, 0x02fe2, 0x02fe3, 0x02fe4, 0x02fe5, 0x02fe6, 0x02fe7,
+ 0x02fe8, 0x02fe9, 0x02fea, 0x02feb, 0x02fec, 0x02fed, 0x02fee, 0x02fef,
+ 0x02ff0, 0x02ff1, 0x02ff2, 0x02ff3, 0x02ff4, 0x02ff5, 0x02ff6, 0x02ff7,
+ 0x02ff8, 0x02ff9, 0x02ffa, 0x02ffb, 0x02ffc, 0x02ffd, 0x02ffe, 0x02fff
+};
+
+static uint32_t unicode_ci_page_30[] = {
+ 0x00020, 0x03001, 0x03002, 0x03003, 0x03004, 0x03005, 0x03006, 0x00030,
+ 0x02329, 0x0232a, 0x0300a, 0x0300b, 0x0300c, 0x0300d, 0x0300e, 0x0300f,
+ 0x03010, 0x03011, 0x03012, 0x03013, 0x03014, 0x03015, 0x03016, 0x03017,
+ 0x03018, 0x03019, 0x0301a, 0x0301b, 0x0301c, 0x0301d, 0x0301e, 0x0301f,
+ 0x03020, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x03030, 0x03031, 0x03031, 0x03033, 0x03033, 0x03035, 0x03012, 0x03037,
+ 0x02f17, 0x03039, 0x0303a, 0x0303b, 0x0303c, 0x0303d, 0x0303e, 0x0303f,
+ 0x03040, 0x03042, 0x03042, 0x03044, 0x03044, 0x03046, 0x03046, 0x03048,
+ 0x03048, 0x0304a, 0x0304a, 0x0304b, 0x0304b, 0x0304d, 0x0304d, 0x0304f,
+ 0x0304f, 0x03051, 0x03051, 0x03053, 0x03053, 0x03055, 0x03055, 0x03057,
+ 0x03057, 0x03059, 0x03059, 0x0305b, 0x0305b, 0x0305d, 0x0305d, 0x0305f,
+ 0x0305f, 0x03061, 0x03061, 0x03064, 0x03064, 0x03064, 0x03066, 0x03066,
+ 0x03068, 0x03068, 0x0306a, 0x0306b, 0x0306c, 0x0306d, 0x0306e, 0x0306f,
+ 0x0306f, 0x0306f, 0x03072, 0x03072, 0x03072, 0x03075, 0x03075, 0x03075,
+ 0x03078, 0x03078, 0x03078, 0x0307b, 0x0307b, 0x0307b, 0x0307e, 0x0307f,
+ 0x03080, 0x03081, 0x03082, 0x03084, 0x03084, 0x03086, 0x03086, 0x03088,
+ 0x03088, 0x03089, 0x0308a, 0x0308b, 0x0308c, 0x0308d, 0x0308f, 0x0308f,
+ 0x03090, 0x03091, 0x03092, 0x03093, 0x03046, 0x0304b, 0x03051, 0x03097,
+ 0x03098, 0x00000, 0x00000, 0x0309b, 0x0309c, 0x0309d, 0x0309d, 0x0309f,
+ 0x030a0, 0x03042, 0x03042, 0x03044, 0x03044, 0x03046, 0x03046, 0x03048,
+ 0x03048, 0x0304a, 0x0304a, 0x0304b, 0x0304b, 0x0304d, 0x0304d, 0x0304f,
+ 0x0304f, 0x03051, 0x03051, 0x03053, 0x03053, 0x03055, 0x03055, 0x03057,
+ 0x03057, 0x03059, 0x03059, 0x0305b, 0x0305b, 0x0305d, 0x0305d, 0x0305f,
+ 0x0305f, 0x03061, 0x03061, 0x03064, 0x03064, 0x03064, 0x03066, 0x03066,
+ 0x03068, 0x03068, 0x0306a, 0x0306b, 0x0306c, 0x0306d, 0x0306e, 0x0306f,
+ 0x0306f, 0x0306f, 0x03072, 0x03072, 0x03072, 0x03075, 0x03075, 0x03075,
+ 0x03078, 0x03078, 0x03078, 0x0307b, 0x0307b, 0x0307b, 0x0307e, 0x0307f,
+ 0x03080, 0x03081, 0x03082, 0x03084, 0x03084, 0x03086, 0x03086, 0x03088,
+ 0x03088, 0x03089, 0x0308a, 0x0308b, 0x0308c, 0x0308d, 0x0308f, 0x0308f,
+ 0x03090, 0x03091, 0x03092, 0x03093, 0x03046, 0x0304b, 0x03051, 0x0308f,
+ 0x03090, 0x03091, 0x03092, 0x030fb, 0x030fc, 0x030fd, 0x030fd, 0x030ff
+};
+
+static uint32_t unicode_ci_page_31[] = {
+ 0x03100, 0x03101, 0x03102, 0x03103, 0x03104, 0x03105, 0x03106, 0x03107,
+ 0x03108, 0x03109, 0x0310a, 0x0310b, 0x0310c, 0x0310d, 0x0310e, 0x0310f,
+ 0x03110, 0x03111, 0x03112, 0x03113, 0x03114, 0x03115, 0x03116, 0x03117,
+ 0x03118, 0x03119, 0x0311a, 0x0311b, 0x0311c, 0x0311d, 0x0311e, 0x0311f,
+ 0x03120, 0x03121, 0x03122, 0x03123, 0x03124, 0x03125, 0x03126, 0x03127,
+ 0x03128, 0x03129, 0x0312a, 0x0312b, 0x0312c, 0x0312d, 0x0312e, 0x0312f,
+ 0x03130, 0x01100, 0x01101, 0x011aa, 0x01102, 0x011ac, 0x011ad, 0x01103,
+ 0x01104, 0x01105, 0x011b0, 0x011b1, 0x011b2, 0x011b3, 0x011b4, 0x011b5,
+ 0x0111a, 0x01106, 0x01107, 0x01108, 0x01121, 0x01109, 0x0110a, 0x0110b,
+ 0x0110c, 0x0110d, 0x0110e, 0x0110f, 0x01110, 0x01111, 0x01112, 0x01161,
+ 0x01162, 0x01163, 0x01164, 0x01165, 0x01166, 0x01167, 0x01168, 0x01169,
+ 0x0116a, 0x0116b, 0x0116c, 0x0116d, 0x0116e, 0x0116f, 0x01170, 0x01171,
+ 0x01172, 0x01173, 0x01174, 0x01175, 0x01160, 0x01114, 0x01115, 0x011c7,
+ 0x011c8, 0x011cc, 0x011ce, 0x011d3, 0x011d7, 0x011d9, 0x0111c, 0x011dd,
+ 0x011df, 0x0111d, 0x0111e, 0x01120, 0x01122, 0x01123, 0x01127, 0x01129,
+ 0x0112b, 0x0112c, 0x0112d, 0x0112e, 0x0112f, 0x01132, 0x01136, 0x01140,
+ 0x01147, 0x0114c, 0x011f1, 0x011f2, 0x01157, 0x01158, 0x01159, 0x01184,
+ 0x01185, 0x01188, 0x01191, 0x01192, 0x01194, 0x0119e, 0x011a1, 0x0318f,
+ 0x03190, 0x03191, 0x03192, 0x03193, 0x03194, 0x03195, 0x03196, 0x03197,
+ 0x03198, 0x03199, 0x0319a, 0x0319b, 0x0319c, 0x0319d, 0x0319e, 0x0319f,
+ 0x03105, 0x03117, 0x03110, 0x0310d, 0x031a4, 0x031a4, 0x031a6, 0x0311b,
+ 0x03128, 0x0311a, 0x03127, 0x03128, 0x031ac, 0x031ad, 0x0311e, 0x03120,
+ 0x031b0, 0x031b1, 0x031b2, 0x03127, 0x03106, 0x0310a, 0x0310e, 0x0310f,
+ 0x031b8, 0x031b9, 0x031ba, 0x031bb, 0x031bc, 0x031bd, 0x031be, 0x031bf,
+ 0x031c0, 0x031c1, 0x031c2, 0x031c3, 0x031c4, 0x031c5, 0x031c6, 0x031c7,
+ 0x031c8, 0x031c9, 0x031ca, 0x031cb, 0x031cc, 0x031cd, 0x031ce, 0x031cf,
+ 0x031d0, 0x031d1, 0x031d2, 0x031d3, 0x031d4, 0x031d5, 0x031d6, 0x031d7,
+ 0x031d8, 0x031d9, 0x031da, 0x031db, 0x031dc, 0x031dd, 0x031de, 0x031df,
+ 0x031e0, 0x031e1, 0x031e2, 0x031e3, 0x031e4, 0x031e5, 0x031e6, 0x031e7,
+ 0x031e8, 0x031e9, 0x031ea, 0x031eb, 0x031ec, 0x031ed, 0x031ee, 0x031ef,
+ 0x0304f, 0x03057, 0x03059, 0x03068, 0x0306c, 0x0306f, 0x03072, 0x03075,
+ 0x03078, 0x0307b, 0x03080, 0x03089, 0x0308a, 0x0308b, 0x0308c, 0x031ff
+};
+
+static uint32_t unicode_ci_page_32[] = {
+ 0x03200, 0x03201, 0x03202, 0x03203, 0x03204, 0x03205, 0x03206, 0x03207,
+ 0x03208, 0x03209, 0x0320a, 0x0320b, 0x0320c, 0x0320d, 0x0320e, 0x0320f,
+ 0x03210, 0x03211, 0x03212, 0x03213, 0x03214, 0x03215, 0x03216, 0x03217,
+ 0x03218, 0x03219, 0x0321a, 0x0321b, 0x0321c, 0x0321d, 0x0321e, 0x0321f,
+ 0x03220, 0x03221, 0x03222, 0x03223, 0x03224, 0x03225, 0x03226, 0x03227,
+ 0x03228, 0x03229, 0x0322a, 0x0322b, 0x0322c, 0x0322d, 0x0322e, 0x0322f,
+ 0x03230, 0x03231, 0x03232, 0x03233, 0x03234, 0x03235, 0x03236, 0x03237,
+ 0x03238, 0x03239, 0x0323a, 0x0323b, 0x0323c, 0x0323d, 0x0323e, 0x0323f,
+ 0x03240, 0x03241, 0x03242, 0x03243, 0x03244, 0x03245, 0x03246, 0x03247,
+ 0x03248, 0x03249, 0x0324a, 0x0324b, 0x0324c, 0x0324d, 0x0324e, 0x0324f,
+ 0x03250, 0x03251, 0x03252, 0x03253, 0x03254, 0x03255, 0x03256, 0x03257,
+ 0x03258, 0x03259, 0x0325a, 0x0325b, 0x0325c, 0x0325d, 0x0325e, 0x0325f,
+ 0x01100, 0x01102, 0x01103, 0x01105, 0x01106, 0x01107, 0x01109, 0x0110b,
+ 0x0110c, 0x0110e, 0x0110f, 0x01110, 0x01111, 0x01112, 0x0326e, 0x0326f,
+ 0x03270, 0x03271, 0x03272, 0x03273, 0x03274, 0x03275, 0x03276, 0x03277,
+ 0x03278, 0x03279, 0x0327a, 0x0327b, 0x0327c, 0x0327d, 0x0327e, 0x0327f,
+ 0x03192, 0x03193, 0x03194, 0x03195, 0x03284, 0x03285, 0x03286, 0x02f0b,
+ 0x03288, 0x02f17, 0x02f49, 0x02f55, 0x02f54, 0x02f4a, 0x02fa6, 0x02f1f,
+ 0x02f47, 0x03291, 0x03292, 0x03293, 0x03294, 0x03295, 0x03296, 0x03297,
+ 0x03298, 0x03299, 0x0329a, 0x02f25, 0x0329c, 0x0329d, 0x0329e, 0x0329f,
+ 0x032a0, 0x032a1, 0x032a2, 0x032a3, 0x03196, 0x03197, 0x03198, 0x032a7,
+ 0x032a8, 0x032a9, 0x032aa, 0x032ab, 0x032ac, 0x032ad, 0x032ae, 0x032af,
+ 0x032b0, 0x032b1, 0x032b2, 0x032b3, 0x032b4, 0x032b5, 0x032b6, 0x032b7,
+ 0x032b8, 0x032b9, 0x032ba, 0x032bb, 0x032bc, 0x032bd, 0x032be, 0x032bf,
+ 0x032c0, 0x032c1, 0x032c2, 0x032c3, 0x032c4, 0x032c5, 0x032c6, 0x032c7,
+ 0x032c8, 0x032c9, 0x032ca, 0x032cb, 0x032cc, 0x032cd, 0x032ce, 0x032cf,
+ 0x03042, 0x03044, 0x03046, 0x03048, 0x0304a, 0x0304b, 0x0304d, 0x0304f,
+ 0x03051, 0x03053, 0x03055, 0x03057, 0x03059, 0x0305b, 0x0305d, 0x0305f,
+ 0x03061, 0x03064, 0x03066, 0x03068, 0x0306a, 0x0306b, 0x0306c, 0x0306d,
+ 0x0306e, 0x0306f, 0x03072, 0x03075, 0x03078, 0x0307b, 0x0307e, 0x0307f,
+ 0x03080, 0x03081, 0x03082, 0x03084, 0x03086, 0x03088, 0x03089, 0x0308a,
+ 0x0308b, 0x0308c, 0x0308d, 0x0308f, 0x03090, 0x03091, 0x03092, 0x032ff
+};
+
+static uint32_t unicode_ci_page_33[] = {
+ 0x03300, 0x03301, 0x03302, 0x03303, 0x03304, 0x03305, 0x03306, 0x03307,
+ 0x03308, 0x03309, 0x0330a, 0x0330b, 0x0330c, 0x0330d, 0x0330e, 0x0330f,
+ 0x03310, 0x03311, 0x03312, 0x03313, 0x03314, 0x03315, 0x03316, 0x03317,
+ 0x03318, 0x03319, 0x0331a, 0x0331b, 0x0331c, 0x0331d, 0x0331e, 0x0331f,
+ 0x03320, 0x03321, 0x03322, 0x03323, 0x03324, 0x03325, 0x03326, 0x03327,
+ 0x03328, 0x03329, 0x0332a, 0x0332b, 0x0332c, 0x0332d, 0x0332e, 0x0332f,
+ 0x03330, 0x03331, 0x03332, 0x03333, 0x03334, 0x03335, 0x03336, 0x03337,
+ 0x03338, 0x03339, 0x0333a, 0x0333b, 0x0333c, 0x0333d, 0x0333e, 0x0333f,
+ 0x03340, 0x03341, 0x03342, 0x03343, 0x03344, 0x03345, 0x03346, 0x03347,
+ 0x03348, 0x03349, 0x0334a, 0x0334b, 0x0334c, 0x0334d, 0x0334e, 0x0334f,
+ 0x03350, 0x03351, 0x03352, 0x03353, 0x03354, 0x03355, 0x03356, 0x03357,
+ 0x03358, 0x03359, 0x0335a, 0x0335b, 0x0335c, 0x0335d, 0x0335e, 0x0335f,
+ 0x03360, 0x03361, 0x03362, 0x03363, 0x03364, 0x03365, 0x03366, 0x03367,
+ 0x03368, 0x03369, 0x0336a, 0x0336b, 0x0336c, 0x0336d, 0x0336e, 0x0336f,
+ 0x03370, 0x03371, 0x03372, 0x03373, 0x03374, 0x03375, 0x03376, 0x03377,
+ 0x03378, 0x03379, 0x0337a, 0x0337b, 0x0337c, 0x0337d, 0x0337e, 0x0337f,
+ 0x03380, 0x03381, 0x03382, 0x03383, 0x03384, 0x03385, 0x03386, 0x03387,
+ 0x03388, 0x03389, 0x0338a, 0x0338b, 0x0338c, 0x0338d, 0x0338e, 0x0338f,
+ 0x03390, 0x03391, 0x03392, 0x03393, 0x03394, 0x03395, 0x03396, 0x03397,
+ 0x03398, 0x03399, 0x0339a, 0x0339b, 0x0339c, 0x0339d, 0x0339e, 0x0339f,
+ 0x033a0, 0x033a1, 0x033a2, 0x033a3, 0x033a4, 0x033a5, 0x033a6, 0x033a7,
+ 0x033a8, 0x03380, 0x033aa, 0x033ab, 0x033ac, 0x033ad, 0x033ae, 0x033af,
+ 0x033b0, 0x033b1, 0x033b2, 0x033b3, 0x033b4, 0x033b5, 0x033b6, 0x033b7,
+ 0x033b8, 0x033b7, 0x033ba, 0x033bb, 0x033bc, 0x033bd, 0x033be, 0x033bd,
+ 0x033c0, 0x033c1, 0x033c2, 0x033c3, 0x033c4, 0x033c5, 0x033c6, 0x033c7,
+ 0x033c8, 0x033c9, 0x033ca, 0x033cb, 0x033cc, 0x033cd, 0x0339e, 0x033cf,
+ 0x033d0, 0x033d1, 0x033d2, 0x033d3, 0x03386, 0x033d5, 0x033d6, 0x033d7,
+ 0x033d8, 0x033d9, 0x033da, 0x033db, 0x033dc, 0x033dd, 0x033de, 0x033df,
+ 0x033e0, 0x033e1, 0x033e2, 0x033e3, 0x033e4, 0x033e5, 0x033e6, 0x033e7,
+ 0x033e8, 0x033e9, 0x033ea, 0x033eb, 0x033ec, 0x033ed, 0x033ee, 0x033ef,
+ 0x033f0, 0x033f1, 0x033f2, 0x033f3, 0x033f4, 0x033f5, 0x033f6, 0x033f7,
+ 0x033f8, 0x033f9, 0x033fa, 0x033fb, 0x033fc, 0x033fd, 0x033fe, 0x033ff
+};
+
+static uint32_t unicode_ci_page_f9[] = {
+ 0x0f900, 0x0f901, 0x02f9e, 0x0f903, 0x0f904, 0x0f905, 0x0f906, 0x02ef2,
+ 0x02ef2, 0x0f909, 0x02fa6, 0x0f90b, 0x0f90c, 0x0f90d, 0x0f90e, 0x0f90f,
+ 0x0f910, 0x0f911, 0x0f912, 0x0f913, 0x0f914, 0x0f915, 0x0f916, 0x0f917,
+ 0x0f918, 0x0f919, 0x0f91a, 0x0f91b, 0x0f91c, 0x0f91d, 0x0f91e, 0x0f91f,
+ 0x0f920, 0x0f921, 0x0f922, 0x0f923, 0x0f924, 0x0f925, 0x0f926, 0x0f927,
+ 0x0f928, 0x0f929, 0x0f92a, 0x0f92b, 0x0f92c, 0x0f92d, 0x0f92e, 0x0f92f,
+ 0x0f930, 0x0f931, 0x0f932, 0x0f933, 0x02f7c, 0x0f935, 0x0f936, 0x0f937,
+ 0x0f938, 0x0f939, 0x0f93a, 0x0f93b, 0x0f93c, 0x0f93d, 0x0f93e, 0x0f93f,
+ 0x02fc5, 0x0f941, 0x0f942, 0x0f943, 0x0f944, 0x0f945, 0x0f946, 0x0f947,
+ 0x0f948, 0x0f949, 0x0f94a, 0x0f94b, 0x0f94c, 0x0f94d, 0x0f94e, 0x0f94f,
+ 0x0f950, 0x0f951, 0x0f952, 0x0f953, 0x0f954, 0x0f955, 0x0f956, 0x0f957,
+ 0x0f958, 0x0f959, 0x0f95a, 0x0f95b, 0x0f914, 0x0f95d, 0x0f95e, 0x0f95f,
+ 0x0f960, 0x0f961, 0x0f962, 0x0f963, 0x0f964, 0x0f965, 0x0f966, 0x0f967,
+ 0x0f968, 0x0f969, 0x0f96a, 0x0f96b, 0x0f96c, 0x0f96d, 0x0f96e, 0x0f96f,
+ 0x0f970, 0x02fa0, 0x0f972, 0x0f973, 0x0f974, 0x0f975, 0x0f976, 0x0f977,
+ 0x0f978, 0x0f979, 0x0f97a, 0x0f97b, 0x0f97c, 0x0f97d, 0x0f97e, 0x0f97f,
+ 0x0f980, 0x02f25, 0x0f982, 0x0f983, 0x0f984, 0x0f985, 0x0f986, 0x0f987,
+ 0x0f988, 0x0f989, 0x02f12, 0x0f98b, 0x0f98c, 0x0f98d, 0x0f98e, 0x0f98f,
+ 0x0f990, 0x0f991, 0x0f992, 0x0f993, 0x0f994, 0x0f995, 0x0f996, 0x0f997,
+ 0x0f998, 0x0f999, 0x0f99a, 0x0f99b, 0x0f99c, 0x0f99d, 0x0f99e, 0x0f99f,
+ 0x0f9a0, 0x0f96f, 0x0f9a2, 0x0f9a3, 0x0f9a4, 0x0f9a5, 0x0f9a6, 0x0f9a7,
+ 0x0f9a8, 0x0f9a9, 0x0f95f, 0x0f9ab, 0x0f9ac, 0x0f9ad, 0x0f9ae, 0x0f9af,
+ 0x0f9b0, 0x0f9b1, 0x0f9b2, 0x0f9b3, 0x0f9b4, 0x0f9b5, 0x0f9b6, 0x0f9b7,
+ 0x0f9b8, 0x0f9b9, 0x0f9ba, 0x0f9bb, 0x0f9bc, 0x0f9bd, 0x0f9be, 0x0f914,
+ 0x0f9c0, 0x0f9c1, 0x0f9c2, 0x0f9c3, 0x02eef, 0x0f9c5, 0x0f9c6, 0x0f9c7,
+ 0x0f9c8, 0x0f9c9, 0x0f9ca, 0x0f9cb, 0x0f9cc, 0x0f9cd, 0x0f9ce, 0x0f9cf,
+ 0x0f9d0, 0x03285, 0x0f9d2, 0x0f9d3, 0x0f9d4, 0x0f9d5, 0x0f9d6, 0x0f9d7,
+ 0x0f9d8, 0x0f9d9, 0x0f9da, 0x0f961, 0x0f9dc, 0x0f9dd, 0x0f9de, 0x0f9df,
+ 0x0f9e0, 0x0f9e1, 0x0f9e2, 0x0f9e3, 0x0f9e4, 0x0f9e5, 0x0f9e6, 0x0f9e7,
+ 0x0f9e8, 0x02fa5, 0x0f9ea, 0x0f9eb, 0x0f9ec, 0x0f9ed, 0x0f9ee, 0x0f9ef,
+ 0x0f9f0, 0x0f9f1, 0x0f9f2, 0x0f9f3, 0x0f9f4, 0x0f9f5, 0x0f9f6, 0x02f74,
+ 0x0f9f8, 0x0f9f9, 0x0f9fa, 0x0f9fb, 0x0f9fc, 0x0f9fd, 0x0f9fe, 0x0f9ff
+};
+
+static uint32_t unicode_ci_page_fa[] = {
+ 0x0fa00, 0x0fa01, 0x0fa02, 0x0fa03, 0x0fa04, 0x0fa05, 0x0fa06, 0x0fa07,
+ 0x02f8f, 0x0fa09, 0x02f92, 0x0fa0b, 0x0fa0c, 0x0fa0d, 0x0fa0e, 0x0fa0f,
+ 0x0fa10, 0x0fa11, 0x0fa12, 0x0fa13, 0x0fa14, 0x0fa15, 0x0fa16, 0x0fa17,
+ 0x0fa18, 0x0fa19, 0x0fa1a, 0x0fa1b, 0x0fa1c, 0x0fa1d, 0x02f7b, 0x0fa1f,
+ 0x0fa20, 0x0fa21, 0x0fa22, 0x0fa23, 0x0fa24, 0x0fa25, 0x0fa26, 0x0fa27,
+ 0x0fa28, 0x0fa29, 0x0fa2a, 0x0fa2b, 0x0fa2c, 0x0fa2d, 0x0fa2e, 0x0fa2f,
+ 0x0fa30, 0x0fa31, 0x0fa32, 0x0fa33, 0x0fa34, 0x0fa35, 0x0fa36, 0x0fa37,
+ 0x0fa38, 0x0fa39, 0x0fa3a, 0x0fa3b, 0x02f2c, 0x0fa3d, 0x0fa3e, 0x0fa3f,
+ 0x0fa40, 0x0fa41, 0x0fa42, 0x0fa43, 0x0fa44, 0x0fa45, 0x0fa46, 0x0fa47,
+ 0x0fa48, 0x02ea4, 0x0fa4a, 0x0fa4b, 0x03293, 0x0fa4d, 0x0fa4e, 0x0fa4f,
+ 0x0fa50, 0x03297, 0x0fa52, 0x0fa53, 0x0fa54, 0x0fa55, 0x0fa56, 0x0f996,
+ 0x0fa58, 0x0fa59, 0x0fa5a, 0x0fa5b, 0x0fa5c, 0x02ebe, 0x02ebe, 0x0fa5f,
+ 0x0fa60, 0x0fa61, 0x0fa62, 0x0fa63, 0x0fa64, 0x0fa65, 0x02ecc, 0x0fa25,
+ 0x0fa68, 0x0fa69, 0x0fa6a, 0x0fa6b, 0x0fa6c, 0x0fa6d, 0x0fa6e, 0x0fa6f,
+ 0x0fa70, 0x0fa71, 0x0fa72, 0x0fa73, 0x0fa74, 0x0fa75, 0x0fa76, 0x0fa77,
+ 0x0fa78, 0x0fa79, 0x0fa7a, 0x0fa7b, 0x0fa7c, 0x0fa7d, 0x0fa7e, 0x0fa7f,
+ 0x0fa80, 0x0fa81, 0x0fa82, 0x0fa83, 0x0fa84, 0x0fa85, 0x0fa86, 0x0fa87,
+ 0x0fa88, 0x0fa89, 0x0fa8a, 0x0fa8b, 0x0fa8c, 0x0fa8d, 0x0fa8e, 0x0fa8f,
+ 0x0fa90, 0x0fa91, 0x0fa92, 0x0fa93, 0x0fa94, 0x0fa95, 0x0fa96, 0x0fa97,
+ 0x0fa98, 0x0fa99, 0x0fa9a, 0x0fa9b, 0x0fa9c, 0x0fa9d, 0x0fa9e, 0x0fa9f,
+ 0x0faa0, 0x0faa1, 0x0faa2, 0x0faa3, 0x0faa4, 0x0faa5, 0x0faa6, 0x0faa7,
+ 0x0faa8, 0x0faa9, 0x0faaa, 0x0faab, 0x0faac, 0x0faad, 0x0faae, 0x0faaf,
+ 0x0fab0, 0x0fab1, 0x0fab2, 0x0fab3, 0x0fab4, 0x0fab5, 0x0fab6, 0x0fab7,
+ 0x0fab8, 0x0fab9, 0x0faba, 0x0fabb, 0x0fabc, 0x0fabd, 0x0fabe, 0x0fabf,
+ 0x0fac0, 0x0fac1, 0x0fac2, 0x0fac3, 0x0fac4, 0x0fac5, 0x0fac6, 0x0fac7,
+ 0x0fac8, 0x0fac9, 0x0faca, 0x0facb, 0x0facc, 0x0facd, 0x0face, 0x0facf,
+ 0x0fad0, 0x0fad1, 0x0fad2, 0x0fad3, 0x0fad4, 0x0fad5, 0x0fad6, 0x0fad7,
+ 0x0fad8, 0x0fad9, 0x0fada, 0x0fadb, 0x0fadc, 0x0fadd, 0x0fade, 0x0fadf,
+ 0x0fae0, 0x0fae1, 0x0fae2, 0x0fae3, 0x0fae4, 0x0fae5, 0x0fae6, 0x0fae7,
+ 0x0fae8, 0x0fae9, 0x0faea, 0x0faeb, 0x0faec, 0x0faed, 0x0faee, 0x0faef,
+ 0x0faf0, 0x0faf1, 0x0faf2, 0x0faf3, 0x0faf4, 0x0faf5, 0x0faf6, 0x0faf7,
+ 0x0faf8, 0x0faf9, 0x0fafa, 0x0fafb, 0x0fafc, 0x0fafd, 0x0fafe, 0x0faff
+};
+
+static uint32_t unicode_ci_page_fb[] = {
+ 0x0fb00, 0x0fb01, 0x0fb02, 0x0fb03, 0x0fb04, 0x0fb05, 0x0fb05, 0x0fb07,
+ 0x0fb08, 0x0fb09, 0x0fb0a, 0x0fb0b, 0x0fb0c, 0x0fb0d, 0x0fb0e, 0x0fb0f,
+ 0x0fb10, 0x0fb11, 0x0fb12, 0x0fb13, 0x0fb14, 0x0fb15, 0x0fb16, 0x0fb17,
+ 0x0fb18, 0x0fb19, 0x0fb1a, 0x0fb1b, 0x0fb1c, 0x005d9, 0x00000, 0x005f2,
+ 0x005e2, 0x005d0, 0x005d3, 0x005d4, 0x005da, 0x005dc, 0x005dd, 0x005e8,
+ 0x005ea, 0x0002b, 0x005e9, 0x005e9, 0x005e9, 0x005e9, 0x005d0, 0x005d0,
+ 0x005d0, 0x005d1, 0x005d2, 0x005d3, 0x005d4, 0x005d5, 0x005d6, 0x0fb37,
+ 0x005d8, 0x005d9, 0x005da, 0x005da, 0x005dc, 0x0fb3d, 0x005dd, 0x0fb3f,
+ 0x005df, 0x005e1, 0x0fb42, 0x005e3, 0x005e3, 0x0fb45, 0x005e5, 0x005e7,
+ 0x005e8, 0x005e9, 0x005ea, 0x005d5, 0x005d1, 0x005da, 0x005e3, 0x0fb4f,
+ 0x00671, 0x00671, 0x0067b, 0x0067b, 0x0067b, 0x0067b, 0x0067e, 0x0067e,
+ 0x0067e, 0x0067e, 0x00680, 0x00680, 0x00680, 0x00680, 0x0067a, 0x0067a,
+ 0x0067a, 0x0067a, 0x0067f, 0x0067f, 0x0067f, 0x0067f, 0x00679, 0x00679,
+ 0x00679, 0x00679, 0x006a4, 0x006a4, 0x006a4, 0x006a4, 0x006a6, 0x006a6,
+ 0x006a6, 0x006a6, 0x00684, 0x00684, 0x00684, 0x00684, 0x00683, 0x00683,
+ 0x00683, 0x00683, 0x00686, 0x00686, 0x00686, 0x00686, 0x00687, 0x00687,
+ 0x00687, 0x00687, 0x0068d, 0x0068d, 0x0068c, 0x0068c, 0x0068e, 0x0068e,
+ 0x00688, 0x00688, 0x00698, 0x00698, 0x00691, 0x00691, 0x006a9, 0x006a9,
+ 0x006a9, 0x006a9, 0x006af, 0x006af, 0x006af, 0x006af, 0x006b3, 0x006b3,
+ 0x006b3, 0x006b3, 0x006b1, 0x006b1, 0x006b1, 0x006b1, 0x006ba, 0x006ba,
+ 0x006bb, 0x006bb, 0x006bb, 0x006bb, 0x006c0, 0x006c0, 0x006c1, 0x006c1,
+ 0x006c1, 0x006c1, 0x006be, 0x006be, 0x006be, 0x006be, 0x006d2, 0x006d2,
+ 0x006d2, 0x006d2, 0x0fbb2, 0x0fbb3, 0x0fbb4, 0x0fbb5, 0x0fbb6, 0x0fbb7,
+ 0x0fbb8, 0x0fbb9, 0x0fbba, 0x0fbbb, 0x0fbbc, 0x0fbbd, 0x0fbbe, 0x0fbbf,
+ 0x0fbc0, 0x0fbc1, 0x0fbc2, 0x0fbc3, 0x0fbc4, 0x0fbc5, 0x0fbc6, 0x0fbc7,
+ 0x0fbc8, 0x0fbc9, 0x0fbca, 0x0fbcb, 0x0fbcc, 0x0fbcd, 0x0fbce, 0x0fbcf,
+ 0x0fbd0, 0x0fbd1, 0x0fbd2, 0x006ad, 0x006ad, 0x006ad, 0x006ad, 0x006c7,
+ 0x006c7, 0x006c6, 0x006c6, 0x006c8, 0x006c8, 0x00677, 0x006cb, 0x006cb,
+ 0x006c5, 0x006c5, 0x006c9, 0x006c9, 0x006d0, 0x006d0, 0x006d0, 0x006d0,
+ 0x00649, 0x00649, 0x0fbea, 0x0fbea, 0x0fbec, 0x0fbec, 0x0fbee, 0x0fbee,
+ 0x0fbf0, 0x0fbf0, 0x0fbf2, 0x0fbf2, 0x0fbf4, 0x0fbf4, 0x0fbf6, 0x0fbf6,
+ 0x0fbf6, 0x0fbf9, 0x0fbf9, 0x0fbf9, 0x006cc, 0x006cc, 0x006cc, 0x006cc
+};
+
+static uint32_t unicode_ci_page_fc[] = {
+ 0x0fc00, 0x0fc01, 0x0fc02, 0x0fbf9, 0x0fc04, 0x0fc05, 0x0fc06, 0x0fc07,
+ 0x0fc08, 0x0fc09, 0x0fc0a, 0x0fc0b, 0x0fc0c, 0x0fc0d, 0x0fc0e, 0x0fc0f,
+ 0x0fc10, 0x0fc11, 0x0fc12, 0x0fc13, 0x0fc14, 0x0fc15, 0x0fc16, 0x0fc17,
+ 0x0fc18, 0x0fc19, 0x0fc1a, 0x0fc1b, 0x0fc1c, 0x0fc1d, 0x0fc1e, 0x0fc1f,
+ 0x0fc20, 0x0fc21, 0x0fc22, 0x0fc23, 0x0fc24, 0x0fc25, 0x0fc26, 0x0fc27,
+ 0x0fc28, 0x0fc29, 0x0fc2a, 0x0fc2b, 0x0fc2c, 0x0fc2d, 0x0fc2e, 0x0fc2f,
+ 0x0fc30, 0x0fc31, 0x0fc32, 0x0fc33, 0x0fc34, 0x0fc35, 0x0fc36, 0x0fc37,
+ 0x0fc38, 0x0fc39, 0x0fc3a, 0x0fc3b, 0x0fc3c, 0x0fc3d, 0x0fc3e, 0x0fc3f,
+ 0x0fc40, 0x0fc41, 0x0fc42, 0x0fc43, 0x0fc44, 0x0fc45, 0x0fc46, 0x0fc47,
+ 0x0fc48, 0x0fc49, 0x0fc4a, 0x0fc4b, 0x0fc4c, 0x0fc4d, 0x0fc4e, 0x0fc4f,
+ 0x0fc50, 0x0fc51, 0x0fc52, 0x0fc53, 0x0fc54, 0x0fc55, 0x0fc56, 0x0fc57,
+ 0x0fc58, 0x0fc59, 0x0fc5a, 0x00630, 0x00631, 0x00649, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x0fc64, 0x0fc65, 0x0fc02, 0x0fc67,
+ 0x0fbf9, 0x0fc04, 0x0fc6a, 0x0fc6b, 0x0fc08, 0x0fc6d, 0x0fc09, 0x0fc0a,
+ 0x0fc70, 0x0fc71, 0x0fc0e, 0x0fc73, 0x0fc0f, 0x0fc10, 0x0fc76, 0x0fc77,
+ 0x0fc12, 0x0fc79, 0x0fc13, 0x0fc14, 0x0fc31, 0x0fc32, 0x0fc35, 0x0fc36,
+ 0x0fc37, 0x0fc3b, 0x0fc3c, 0x0fc3d, 0x0fc3e, 0x0fc42, 0x0fc43, 0x0fc44,
+ 0x0fc88, 0x0fc48, 0x0fc8a, 0x0fc8b, 0x0fc4e, 0x0fc8d, 0x0fc4f, 0x0fc50,
+ 0x00649, 0x0fc91, 0x0fc92, 0x0fc58, 0x0fc94, 0x0fc59, 0x0fc5a, 0x0fc00,
+ 0x0fc01, 0x0fc99, 0x0fc02, 0x0fc9b, 0x0fc05, 0x0fc06, 0x0fc07, 0x0fc08,
+ 0x0fca0, 0x0fc0b, 0x0fc0c, 0x0fc0d, 0x0fc0e, 0x0fca5, 0x0fc12, 0x0fc15,
+ 0x0fc16, 0x0fc17, 0x0fc18, 0x0fc19, 0x0fc1b, 0x0fc1c, 0x0fc1d, 0x0fc1e,
+ 0x0fc1f, 0x0fc20, 0x0fcb2, 0x0fc21, 0x0fc22, 0x0fc23, 0x0fc24, 0x0fc25,
+ 0x0fc26, 0x0fc28, 0x0fc29, 0x0fc2a, 0x0fc2b, 0x0fc2c, 0x0fc2d, 0x0fc2e,
+ 0x0fc2f, 0x0fc30, 0x0fc33, 0x0fc34, 0x0fc38, 0x0fc39, 0x0fc3a, 0x0fc3b,
+ 0x0fc3c, 0x0fc3f, 0x0fc40, 0x0fc41, 0x0fc42, 0x0fccd, 0x0fc45, 0x0fc46,
+ 0x0fc47, 0x0fc48, 0x0fc4b, 0x0fc4c, 0x0fc4d, 0x0fc4e, 0x0fcd6, 0x0fc51,
+ 0x0fc52, 0x00647, 0x0fc55, 0x0fc56, 0x0fc57, 0x0fc58, 0x0fcde, 0x0fc02,
+ 0x0fc9b, 0x0fc08, 0x0fca0, 0x0fc0e, 0x0fca5, 0x0fc12, 0x0fce6, 0x0fc1f,
+ 0x0fce8, 0x0fce9, 0x0fcea, 0x0fc3b, 0x0fc3c, 0x0fc42, 0x0fc4e, 0x0fcd6,
+ 0x0fc58, 0x0fcde, 0x00000, 0x00000, 0x00000, 0x0fcf5, 0x0fcf6, 0x0fcf7,
+ 0x0fcf8, 0x0fcf9, 0x0fcfa, 0x0fcfb, 0x0fcfc, 0x0fcfd, 0x0fcfe, 0x0fcff
+};
+
+static uint32_t unicode_ci_page_fd[] = {
+ 0x0fd00, 0x0fd01, 0x0fd02, 0x0fd03, 0x0fd04, 0x0fd05, 0x0fd06, 0x0fd07,
+ 0x0fd08, 0x0fd09, 0x0fd0a, 0x0fd0b, 0x0fce9, 0x0fd0d, 0x0fd0e, 0x0fd0f,
+ 0x0fd10, 0x0fcf5, 0x0fcf6, 0x0fcf7, 0x0fcf8, 0x0fcf9, 0x0fcfa, 0x0fcfb,
+ 0x0fcfc, 0x0fcfd, 0x0fcfe, 0x0fd1b, 0x0fd00, 0x0fd01, 0x0fd02, 0x0fd03,
+ 0x0fd04, 0x0fd05, 0x0fd06, 0x0fd07, 0x0fd08, 0x0fd09, 0x0fd0a, 0x0fd0b,
+ 0x0fce9, 0x0fd0d, 0x0fd0e, 0x0fd0f, 0x0fd10, 0x0fd09, 0x0fd0a, 0x0fd0b,
+ 0x0fce9, 0x0fce8, 0x0fcea, 0x0fc27, 0x0fc1c, 0x0fc1d, 0x0fc1e, 0x0fd09,
+ 0x0fd0a, 0x0fd0b, 0x0fc27, 0x0fc28, 0x00627, 0x00627, 0x0fd3e, 0x0fd3f,
+ 0x0fd40, 0x0fd41, 0x0fd42, 0x0fd43, 0x0fd44, 0x0fd45, 0x0fd46, 0x0fd47,
+ 0x0fd48, 0x0fd49, 0x0fd4a, 0x0fd4b, 0x0fd4c, 0x0fd4d, 0x0fd4e, 0x0fd4f,
+ 0x0fd50, 0x0fd51, 0x0fd51, 0x0fd53, 0x0fd54, 0x0fd55, 0x0fd56, 0x0fd57,
+ 0x0fd58, 0x0fd58, 0x0fd5a, 0x0fd5b, 0x0fd5c, 0x0fd5d, 0x0fd5e, 0x0fd5f,
+ 0x0fd5f, 0x0fd61, 0x0fd62, 0x0fd62, 0x0fd64, 0x0fd64, 0x0fd66, 0x0fd67,
+ 0x0fd67, 0x0fd69, 0x0fd6a, 0x0fd6a, 0x0fd6c, 0x0fd6c, 0x0fd6e, 0x0fd6f,
+ 0x0fd6f, 0x0fd71, 0x0fd71, 0x0fd73, 0x0fd74, 0x0fd75, 0x0fd76, 0x0fd76,
+ 0x0fd78, 0x0fd79, 0x0fd7a, 0x0fd7b, 0x0fd7c, 0x0fd7c, 0x0fd7e, 0x0fd7f,
+ 0x0fd80, 0x0fd81, 0x0fd82, 0x0fd83, 0x0fd83, 0x0fd85, 0x0fd85, 0x0fd87,
+ 0x0fd87, 0x0fd89, 0x0fd8a, 0x0fd8b, 0x0fd8c, 0x0fd8d, 0x0fd8e, 0x0fd8f,
+ 0x0fd90, 0x0fd91, 0x0fd92, 0x0fd93, 0x0fd94, 0x0fd95, 0x0fd96, 0x0fd97,
+ 0x0fd97, 0x0fd99, 0x0fd9a, 0x0fd9b, 0x0fd9c, 0x0fd9c, 0x0fd9e, 0x0fd9f,
+ 0x0fda0, 0x0fda1, 0x0fda2, 0x0fda3, 0x0fda4, 0x0fda5, 0x0fda6, 0x0fda7,
+ 0x0fda8, 0x0fda9, 0x0fdaa, 0x0fdab, 0x0fdac, 0x0fdad, 0x0fdae, 0x0fdaf,
+ 0x0fdb0, 0x0fdb1, 0x0fdb2, 0x0fdb3, 0x0fd7e, 0x0fd80, 0x0fdb6, 0x0fdb7,
+ 0x0fdb8, 0x0fdb9, 0x0fdba, 0x0fdbb, 0x0fdba, 0x0fdb8, 0x0fdbe, 0x0fdbf,
+ 0x0fdc0, 0x0fdc1, 0x0fdc2, 0x0fdbb, 0x0fd75, 0x0fd66, 0x0fdc6, 0x0fdc7,
+ 0x0fdc8, 0x0fdc9, 0x0fdca, 0x0fdcb, 0x0fdcc, 0x0fdcd, 0x0fdce, 0x0fdcf,
+ 0x0fdd0, 0x0fdd1, 0x0fdd2, 0x0fdd3, 0x0fdd4, 0x0fdd5, 0x0fdd6, 0x0fdd7,
+ 0x0fdd8, 0x0fdd9, 0x0fdda, 0x0fddb, 0x0fddc, 0x0fddd, 0x0fdde, 0x0fddf,
+ 0x0fde0, 0x0fde1, 0x0fde2, 0x0fde3, 0x0fde4, 0x0fde5, 0x0fde6, 0x0fde7,
+ 0x0fde8, 0x0fde9, 0x0fdea, 0x0fdeb, 0x0fdec, 0x0fded, 0x0fdee, 0x0fdef,
+ 0x0fdf0, 0x0fdf1, 0x0fdf2, 0x0fdf3, 0x0fdf4, 0x0fdf5, 0x0fdf6, 0x0fdf7,
+ 0x0fdf8, 0x0fdf9, 0x0fdfa, 0x0fdfb, 0x0fdfc, 0x0fdfd, 0x0fdfe, 0x0fdff
+};
+
+static uint32_t unicode_ci_page_fe[] = {
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x0fe10, 0x0fe11, 0x0fe12, 0x0fe13, 0x0fe14, 0x0fe15, 0x0fe16, 0x0fe17,
+ 0x0fe18, 0x0fe19, 0x0fe1a, 0x0fe1b, 0x0fe1c, 0x0fe1d, 0x0fe1e, 0x0fe1f,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x0fe24, 0x0fe25, 0x0fe26, 0x0fe27,
+ 0x0fe28, 0x0fe29, 0x0fe2a, 0x0fe2b, 0x0fe2c, 0x0fe2d, 0x0fe2e, 0x0fe2f,
+ 0x02025, 0x02014, 0x02013, 0x0005f, 0x0005f, 0x00028, 0x00029, 0x0007b,
+ 0x0007d, 0x03014, 0x03015, 0x03010, 0x03011, 0x0300a, 0x0300b, 0x02329,
+ 0x0232a, 0x0300c, 0x0300d, 0x0300e, 0x0300f, 0x0fe45, 0x0fe46, 0x0005b,
+ 0x0005d, 0x0203e, 0x0203e, 0x0203e, 0x0203e, 0x0005f, 0x0005f, 0x0005f,
+ 0x0002c, 0x03001, 0x0002e, 0x0fe53, 0x0003b, 0x0003a, 0x0003f, 0x00021,
+ 0x02014, 0x00028, 0x00029, 0x0007b, 0x0007d, 0x03014, 0x03015, 0x00023,
+ 0x00026, 0x0002a, 0x0002b, 0x0002d, 0x0003c, 0x0003e, 0x0003d, 0x0fe67,
+ 0x0005c, 0x00024, 0x00025, 0x00040, 0x0fe6c, 0x0fe6d, 0x0fe6e, 0x0fe6f,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x0fe75, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00621, 0x00622, 0x00622, 0x00623, 0x00623, 0x00624, 0x00624, 0x00625,
+ 0x00625, 0x00626, 0x00626, 0x00626, 0x00626, 0x00627, 0x00627, 0x00628,
+ 0x00628, 0x00628, 0x00628, 0x00629, 0x00629, 0x0062a, 0x0062a, 0x0062a,
+ 0x0062a, 0x0062b, 0x0062b, 0x0062b, 0x0062b, 0x0062c, 0x0062c, 0x0062c,
+ 0x0062c, 0x0062d, 0x0062d, 0x0062d, 0x0062d, 0x0062e, 0x0062e, 0x0062e,
+ 0x0062e, 0x0062f, 0x0062f, 0x00630, 0x00630, 0x00631, 0x00631, 0x00632,
+ 0x00632, 0x00633, 0x00633, 0x00633, 0x00633, 0x00634, 0x00634, 0x00634,
+ 0x00634, 0x00635, 0x00635, 0x00635, 0x00635, 0x00636, 0x00636, 0x00636,
+ 0x00636, 0x00637, 0x00637, 0x00637, 0x00637, 0x00638, 0x00638, 0x00638,
+ 0x00638, 0x00639, 0x00639, 0x00639, 0x00639, 0x0063a, 0x0063a, 0x0063a,
+ 0x0063a, 0x00641, 0x00641, 0x00641, 0x00641, 0x00642, 0x00642, 0x00642,
+ 0x00642, 0x00643, 0x00643, 0x00643, 0x00643, 0x00644, 0x00644, 0x00644,
+ 0x00644, 0x00645, 0x00645, 0x00645, 0x00645, 0x00646, 0x00646, 0x00646,
+ 0x00646, 0x00647, 0x00647, 0x00647, 0x00647, 0x00648, 0x00648, 0x00649,
+ 0x00649, 0x0064a, 0x0064a, 0x0064a, 0x0064a, 0x0fef5, 0x0fef5, 0x0fef7,
+ 0x0fef7, 0x0fef9, 0x0fef9, 0x0fefb, 0x0fefb, 0x0fefd, 0x0fefe, 0x0feff
+};
+
+static uint32_t unicode_ci_page_ff[] = {
+ 0x0ff00, 0x00021, 0x00022, 0x00023, 0x00024, 0x00025, 0x00026, 0x00027,
+ 0x00028, 0x00029, 0x0002a, 0x0002b, 0x0002c, 0x0002d, 0x0002e, 0x0002f,
+ 0x00030, 0x00031, 0x00032, 0x00033, 0x00034, 0x00035, 0x00036, 0x00037,
+ 0x00038, 0x00039, 0x0003a, 0x0003b, 0x0003c, 0x0003d, 0x0003e, 0x0003f,
+ 0x00040, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047,
+ 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f,
+ 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057,
+ 0x00058, 0x00059, 0x0005a, 0x0005b, 0x0005c, 0x0005d, 0x0005e, 0x0005f,
+ 0x00060, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047,
+ 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f,
+ 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057,
+ 0x00058, 0x00059, 0x0005a, 0x0007b, 0x0007c, 0x0007d, 0x0007e, 0x02985,
+ 0x02986, 0x03002, 0x0300c, 0x0300d, 0x03001, 0x030fb, 0x03092, 0x03042,
+ 0x03044, 0x03046, 0x03048, 0x0304a, 0x03084, 0x03086, 0x03088, 0x03064,
+ 0x030fc, 0x03042, 0x03044, 0x03046, 0x03048, 0x0304a, 0x0304b, 0x0304d,
+ 0x0304f, 0x03051, 0x03053, 0x03055, 0x03057, 0x03059, 0x0305b, 0x0305d,
+ 0x0305f, 0x03061, 0x03064, 0x03066, 0x03068, 0x0306a, 0x0306b, 0x0306c,
+ 0x0306d, 0x0306e, 0x0306f, 0x03072, 0x03075, 0x03078, 0x0307b, 0x0307e,
+ 0x0307f, 0x03080, 0x03081, 0x03082, 0x03084, 0x03086, 0x03088, 0x03089,
+ 0x0308a, 0x0308b, 0x0308c, 0x0308d, 0x0308f, 0x03093, 0x00000, 0x00000,
+ 0x01160, 0x01100, 0x01101, 0x011aa, 0x01102, 0x011ac, 0x011ad, 0x01103,
+ 0x01104, 0x01105, 0x011b0, 0x011b1, 0x011b2, 0x011b3, 0x011b4, 0x011b5,
+ 0x0111a, 0x01106, 0x01107, 0x01108, 0x01121, 0x01109, 0x0110a, 0x0110b,
+ 0x0110c, 0x0110d, 0x0110e, 0x0110f, 0x01110, 0x01111, 0x01112, 0x0ffbf,
+ 0x0ffc0, 0x0ffc1, 0x01161, 0x01162, 0x01163, 0x01164, 0x01165, 0x01166,
+ 0x0ffc8, 0x0ffc9, 0x01167, 0x01168, 0x01169, 0x0116a, 0x0116b, 0x0116c,
+ 0x0ffd0, 0x0ffd1, 0x0116d, 0x0116e, 0x0116f, 0x01170, 0x01171, 0x01172,
+ 0x0ffd8, 0x0ffd9, 0x01173, 0x01174, 0x01175, 0x0ffdd, 0x0ffde, 0x0ffdf,
+ 0x000a2, 0x000a3, 0x000ac, 0x000af, 0x000a6, 0x000a5, 0x020a9, 0x0ffe7,
+ 0x02502, 0x02190, 0x02191, 0x02192, 0x02193, 0x025a0, 0x025cb, 0x0ffef,
+ 0x0fff0, 0x0fff1, 0x0fff2, 0x0fff3, 0x0fff4, 0x0fff5, 0x0fff6, 0x0fff7,
+ 0x0fff8, 0x00000, 0x00000, 0x00000, 0x0fffc, 0x0fffd, 0x0fffe, 0x0ffff
+};
+
+static uint32_t *unicode_ci_table[256] = {
+ unicode_ci_page_00, unicode_ci_page_01,
+ unicode_ci_page_02, unicode_ci_page_03,
+ unicode_ci_page_04, unicode_ci_page_05,
+ unicode_ci_page_06, unicode_ci_page_07,
+ NULL, unicode_ci_page_09,
+ unicode_ci_page_0a, unicode_ci_page_0b,
+ unicode_ci_page_0c, unicode_ci_page_0d,
+ unicode_ci_page_0e, unicode_ci_page_0f,
+ unicode_ci_page_10, NULL,
+ NULL, unicode_ci_page_13,
+ NULL, NULL,
+ unicode_ci_page_16, unicode_ci_page_17,
+ unicode_ci_page_18, unicode_ci_page_19,
+ NULL, NULL,
+ NULL, unicode_ci_page_1d,
+ unicode_ci_page_1e, unicode_ci_page_1f,
+ unicode_ci_page_20, unicode_ci_page_21,
+ unicode_ci_page_22, NULL,
+ unicode_ci_page_24, NULL,
+ NULL, unicode_ci_page_27,
+ NULL, NULL,
+ unicode_ci_page_2a, NULL,
+ NULL, NULL,
+ unicode_ci_page_2e, unicode_ci_page_2f,
+ unicode_ci_page_30, unicode_ci_page_31,
+ unicode_ci_page_32, unicode_ci_page_33,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, unicode_ci_page_f9,
+ unicode_ci_page_fa, unicode_ci_page_fb,
+ unicode_ci_page_fc, unicode_ci_page_fd,
+ unicode_ci_page_fe, unicode_ci_page_ff
+};
+
+#endif
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/Makefile.am
new file mode 100644
index 00000000000..f6ab8e5af9f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/Makefile.am
@@ -0,0 +1,6 @@
+SUBDIRS = \
+ apt \
+ ubuntu \
+ rpm \
+ yum \
+ source
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/Makefile.am
new file mode 100644
index 00000000000..22fd7a1bc5d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/Makefile.am
@@ -0,0 +1,60 @@
+REPOSITORIES_PATH = repositories
+DISTRIBUTIONS = debian ubuntu
+CHROOT_BASE = /var/lib/chroot
+ARCHITECTURES = i386 amd64
+CODES = wheezy jessie unstable precise quantal saucy trusty
+KEYRING_PACKAGE = groonga-keyring
+KEYRING_VERSION = 2012.05.29
+KEYRING_BASE_NAME = $(KEYRING_PACKAGE)-$(KEYRING_VERSION)
+
+all:
+
+release: build sign-packages update-repository sign-repository upload
+
+remove-existing-packages:
+ for distribution in $(DISTRIBUTIONS); do \
+ find $(REPOSITORIES_PATH)/$${distribution}/pool \
+ -type f -delete; \
+ done
+
+ensure-rsync-path:
+ @if test -z "$(RSYNC_PATH)"; then \
+ echo "--with-rsync-path configure option must be specified."; \
+ false; \
+ fi
+
+download: ensure-rsync-path
+ for distribution in $(DISTRIBUTIONS); do \
+ rsync -avz --progress --delete \
+ $(RSYNC_PATH)/$${distribution} \
+ ${REPOSITORIES_PATH}/; \
+ done
+
+sign-packages:
+ ./sign-packages.sh '$(GPG_UID)' '$(REPOSITORIES_PATH)/' '$(CODES)'
+
+update-repository:
+ ./update-repository.sh '$(PACKAGE_NAME)' '$(REPOSITORIES_PATH)/' \
+ '$(ARCHITECTURES)' '$(CODES)'
+
+sign-repository:
+ ./sign-repository.sh '$(GPG_UID)' '$(REPOSITORIES_PATH)/' '$(CODES)'
+
+upload: ensure-rsync-path
+ for distribution in $(DISTRIBUTIONS); do \
+ (cd $(REPOSITORIES_PATH)/$${distribution}; \
+ rsync -avz --progress --delete \
+ dists pool $(RSYNC_PATH)/$${distribution}); \
+ done
+
+build: build-package-deb
+
+build-package-deb: source
+ ./build-in-chroot.sh \
+ $(PACKAGE) $(VERSION) $(srcdir)/.. $(REPOSITORIES_PATH)/ \
+ $(CHROOT_BASE) '$(ARCHITECTURES)' '$(CODES)'
+
+source: ../$(PACKAGE)-$(VERSION).tar.gz
+
+../$(PACKAGE)-$(VERSION).tar.gz:
+ ln -s $(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz ../
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/build-deb.sh b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/build-deb.sh
new file mode 100755
index 00000000000..28ee6284fae
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/build-deb.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+LANG=C
+
+PACKAGE=$(cat /tmp/build-package)
+USER_NAME=$(cat /tmp/build-user)
+VERSION=$(cat /tmp/build-version)
+DEPENDED_PACKAGES=$(cat /tmp/depended-packages)
+BUILD_SCRIPT=/tmp/build-deb-in-chroot.sh
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+if [ ! -x /usr/bin/lsb_release ]; then
+ run apt-get update
+ run apt-get install -y lsb-release
+fi
+
+distribution=$(lsb_release --id --short)
+code_name=$(lsb_release --codename --short)
+
+security_list=/etc/apt/sources.list.d/security.list
+if [ ! -f "${security_list}" ]; then
+ case ${distribution} in
+ Debian)
+ if [ "${code_name}" = "sid" ]; then
+ touch "${security_list}"
+ else
+ cat <<EOF > "${security_list}"
+deb http://security.debian.org/ ${code_name}/updates main
+deb-src http://security.debian.org/ ${code_name}/updates main
+EOF
+ fi
+ ;;
+ Ubuntu)
+ cat <<EOF > "${security_list}"
+deb http://security.ubuntu.com/ubuntu ${code_name}-security main restricted
+deb-src http://security.ubuntu.com/ubuntu ${code_name}-security main restricted
+EOF
+ ;;
+ esac
+fi
+
+sources_list=/etc/apt/sources.list
+if [ "$distribution" = "Ubuntu" ] && \
+ ! (grep '^deb' $sources_list | grep -q universe); then
+ run sed -i'' -e 's/main$/main universe/g' $sources_list
+fi
+
+if [ ! -x /usr/bin/aptitude ]; then
+ run apt-get update
+ run apt-get install -y aptitude
+fi
+run aptitude update -V -D
+run aptitude safe-upgrade -V -D -y
+
+run aptitude install -V -D -y devscripts ${DEPENDED_PACKAGES}
+run aptitude clean
+
+if ! id $USER_NAME >/dev/null 2>&1; then
+ run useradd -m $USER_NAME
+fi
+
+cat <<EOF > $BUILD_SCRIPT
+#!/bin/sh
+
+rm -rf build
+mkdir -p build
+
+cp /tmp/${PACKAGE}-${VERSION}.tar.gz build/${PACKAGE}_${VERSION}.orig.tar.gz
+cd build
+tar xfz ${PACKAGE}_${VERSION}.orig.tar.gz
+cd ${PACKAGE}-${VERSION}/
+cp -rp /tmp/${PACKAGE}-debian debian
+# export DEB_BUILD_OPTIONS=noopt
+debuild -us -uc
+EOF
+
+run chmod +x $BUILD_SCRIPT
+run su - $USER_NAME $BUILD_SCRIPT
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/build-in-chroot.sh b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/build-in-chroot.sh
new file mode 100755
index 00000000000..6697b1e9d9d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/build-in-chroot.sh
@@ -0,0 +1,134 @@
+#!/bin/sh
+
+if [ $# != 7 ]; then
+ echo "Usage: $0 PACKAGE VERSION SOURCE_DIR DESTINATION CHROOT_BASE ARCHITECTURES CODES"
+ echo " e.g.: $0 groonga 0.1.9 SOURCE_DIR repositories/ /var/lib/chroot 'i386 amd64' 'lenny unstable hardy karmic'"
+ exit 1
+fi
+
+PACKAGE=$1
+VERSION=$2
+SOURCE_DIR=$3
+DESTINATION=$4
+CHROOT_BASE=$5
+ARCHITECTURES=$6
+CODES=$7
+
+PATH=/usr/local/sbin:/usr/sbin:$PATH
+
+script_base_dir=`dirname $0`
+
+if test "$PARALLEL" = "yes"; then
+ parallel="yes"
+else
+ parallel="no"
+fi
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+run_sudo()
+{
+ run sudo "$@"
+}
+
+build_chroot()
+{
+ architecture=$1
+ code_name=$2
+
+ run_sudo debootstrap --arch $architecture $code_name $base_dir
+
+ case $code_name in
+ lenny|squeeze|wheezy|unstable)
+ run_sudo sed -i'' -e 's/us/jp/' $base_dir/etc/apt/sources.list
+ ;;
+ *)
+ run_sudo sed -i'' \
+ -e 's,http://archive,http://jp.archive,' \
+ -e 's/main$/main universe/' \
+ $base_dir/etc/apt/sources.list
+ ;;
+ esac
+
+ run_sudo sh -c "echo >> /etc/fstab"
+ run_sudo sh -c "echo /sys ${base_dir}/sys none bind 0 0 >> /etc/fstab"
+ run_sudo sh -c "echo /dev ${base_dir}/dev none bind 0 0 >> /etc/fstab"
+ run_sudo sh -c "echo devpts-chroot ${base_dir}/dev/pts devpts defaults 0 0 >> /etc/fstab"
+ run_sudo sh -c "echo proc-chroot ${base_dir}/proc proc defaults 0 0 >> /etc/fstab"
+ run_sudo mount ${base_dir}/sys
+ run_sudo mount ${base_dir}/dev
+ run_sudo mount ${base_dir}/dev/pts
+ run_sudo mount ${base_dir}/proc
+}
+
+build()
+{
+ architecture=$1
+ code_name=$2
+
+ target=${code_name}-${architecture}
+ base_dir=${CHROOT_BASE}/${target}
+ if [ ! -d $base_dir ]; then
+ run build_chroot $architecture $code_name
+ fi
+
+ case ${code_name} in
+ squeeze|wheezy|jessie|unstable)
+ distribution=debian
+ component=main
+ ;;
+ *)
+ distribution=ubuntu
+ component=universe
+ ;;
+ esac
+
+ source_dir=${SOURCE_DIR}
+ build_user=${PACKAGE}-build
+ build_user_dir=${base_dir}/home/$build_user
+ build_dir=${build_user_dir}/build
+ pool_base_dir=${DESTINATION}${distribution}/pool/${code_name}/${component}
+ package_initial=$(echo ${PACKAGE} | sed -e 's/\(.\).*/\1/')
+ pool_dir=${pool_base_dir}/${package_initial}/${PACKAGE}
+ run cp $source_dir/${PACKAGE}-${VERSION}.tar.gz \
+ ${CHROOT_BASE}/$target/tmp/
+ run rm -rf ${CHROOT_BASE}/$target/tmp/${PACKAGE}-debian
+ run cp -rp $source_dir/debian/ \
+ ${CHROOT_BASE}/$target/tmp/${PACKAGE}-debian
+ run echo $PACKAGE > ${CHROOT_BASE}/$target/tmp/build-package
+ run echo $VERSION > ${CHROOT_BASE}/$target/tmp/build-version
+ run echo $build_user > ${CHROOT_BASE}/$target/tmp/build-user
+ run cp ${script_base_dir}/${PACKAGE}-depended-packages \
+ ${CHROOT_BASE}/$target/tmp/depended-packages
+ run cp ${script_base_dir}/build-deb.sh \
+ ${CHROOT_BASE}/$target/tmp/
+ run_sudo rm -rf $build_dir
+ run_sudo su -c "/usr/sbin/chroot ${CHROOT_BASE}/$target /tmp/build-deb.sh"
+ run mkdir -p $pool_dir
+ for path in $build_dir/*; do
+ [ -f $path ] && run cp -p $path $pool_dir/
+ done
+}
+
+for architecture in $ARCHITECTURES; do
+ for code_name in $CODES; do
+ if test "$parallel" = "yes"; then
+ build $architecture $code_name &
+ else
+ mkdir -p tmp
+ build_log=tmp/build-$code_name-$architecture.log
+ build $architecture $code_name 2>&1 | tee $build_log
+ fi;
+ done;
+done
+
+if test "$parallel" = "yes"; then
+ wait
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/groonga-normalizer-mysql-depended-packages b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/groonga-normalizer-mysql-depended-packages
new file mode 100644
index 00000000000..5bc35c391c3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/groonga-normalizer-mysql-depended-packages
@@ -0,0 +1,4 @@
+debhelper
+autotools-dev
+pkg-config
+libgroonga-dev
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/sign-packages.sh b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/sign-packages.sh
new file mode 100755
index 00000000000..11a4aea26db
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/sign-packages.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 3 ]; then
+ echo "Usage: $0 GPG_UID DESITINATION CODES"
+ echo " e.g.: $0 'F10399C0' repositories/ 'lenny unstable hardy karmic'"
+ exit 1
+fi
+
+GPG_UID=$1
+DESTINATION=$2
+CODES=$3
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+for code_name in ${CODES}; do
+ case ${code_name} in
+ squeeze|wheezy|jessie|unstable)
+ distribution=debian
+ ;;
+ *)
+ distribution=ubuntu
+ ;;
+ esac
+
+ base_directory=${DESTINATION}${distribution}
+ debsign -pgpg2 --re-sign -k${GPG_UID} \
+ $(find ${base_directory} -name '*.dsc' -or -name '*.changes') &
+ if [ "${PARALLEL}" != "yes" ]; then
+ wait
+ fi
+done
+
+wait
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/sign-repository.sh b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/sign-repository.sh
new file mode 100755
index 00000000000..fb0de850d6f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/sign-repository.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 3 ]; then
+ echo "Usage: $0 GPG_UID DESTINATION CODES"
+ echo " e.g.: $0 'F10399C0' repositories/ 'lenny unstable hardy karmic'"
+ exit 1
+fi
+
+GPG_UID=$1
+DESTINATION=$2
+CODES=$3
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+for code_name in ${CODES}; do
+ case ${code_name} in
+ squeeze|wheezy|jessie|unstable)
+ distribution=debian
+ ;;
+ *)
+ distribution=ubuntu
+ ;;
+ esac
+
+ release=${DESTINATION}${distribution}/dists/${code_name}/Release
+ rm -f ${release}.gpg
+ gpg2 --sign --detach-sign --armor \
+ --local-user ${GPG_UID} \
+ --output ${release}.gpg \
+ ${release} &
+
+ if [ "${PARALLEL}" != "yes" ]; then
+ wait
+ fi
+done
+
+wait
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/update-repository.sh b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/update-repository.sh
new file mode 100755
index 00000000000..da1f8cd121c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/apt/update-repository.sh
@@ -0,0 +1,130 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 4 ]; then
+ echo "Usage: $0 PROJECT_NAME DESTINATION ARCHITECTURES CODES"
+ echo " e.g.: $0 mroonga repositories/ 'i386 amd64' 'lenny unstable hardy karmic'"
+ exit 1
+fi
+
+PROJECT_NAME=$1
+DESTINATION=$2
+ARCHITECTURES=$3
+CODES=$4
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+update_repository()
+{
+ distribution=$1
+ code_name=$2
+ component=$3
+
+ rm -rf dists/${code_name}
+ mkdir -p dists/${code_name}/${component}/binary-i386/
+ mkdir -p dists/${code_name}/${component}/binary-amd64/
+ mkdir -p dists/${code_name}/${component}/source/
+
+ cat <<EOF > dists/.htaccess
+Options +Indexes
+EOF
+
+ cat <<EOF > dists/${code_name}/${component}/binary-i386/Release
+Archive: ${code_name}
+Component: ${component}
+Origin: The ${PROJECT_NAME} project
+Label: The ${PROJECT_NAME} project
+Architecture: i386
+EOF
+
+ cat <<EOF > dists/${code_name}/${component}/binary-amd64/Release
+Archive: ${code_name}
+Component: ${component}
+Origin: The ${PROJECT_NAME} project
+Label: The ${PROJECT_NAME} project
+Architecture: amd64
+EOF
+
+ cat <<EOF > dists/${code_name}/${component}/source/Release
+Archive: ${code_name}
+Component: ${component}
+Origin: The ${PROJECT_NAME} project
+Label: The ${PROJECT_NAME} project
+Architecture: source
+EOF
+
+ cat <<EOF > generate-${code_name}.conf
+Dir::ArchiveDir ".";
+Dir::CacheDir ".";
+TreeDefault::Directory "pool/${code_name}/${component}";
+TreeDefault::SrcDirectory "pool/${code_name}/${component}";
+Default::Packages::Extensions ".deb";
+Default::Packages::Compress ". gzip bzip2";
+Default::Sources::Compress ". gzip bzip2";
+Default::Contents::Compress "gzip bzip2";
+
+BinDirectory "dists/${code_name}/${component}/binary-i386" {
+ Packages "dists/${code_name}/${component}/binary-i386/Packages";
+ Contents "dists/${code_name}/Contents-i386";
+ SrcPackages "dists/${code_name}/${component}/source/Sources";
+};
+
+BinDirectory "dists/${code_name}/${component}/binary-amd64" {
+ Packages "dists/${code_name}/${component}/binary-amd64/Packages";
+ Contents "dists/${code_name}/Contents-amd64";
+ SrcPackages "dists/${code_name}/${component}/source/Sources";
+};
+
+Tree "dists/${code_name}" {
+ Sections "${component}";
+ Architectures "i386 amd64 source";
+};
+EOF
+ apt-ftparchive generate generate-${code_name}.conf
+ chmod 644 dists/${code_name}/Contents-*
+
+ rm -f dists/${code_name}/Release*
+ rm -f *.db
+ cat <<EOF > release-${code_name}.conf
+APT::FTPArchive::Release::Origin "The ${PROJECT_NAME} project";
+APT::FTPArchive::Release::Label "The ${PROJECT_NAME} project";
+APT::FTPArchive::Release::Architectures "i386 amd64";
+APT::FTPArchive::Release::Codename "${code_name}";
+APT::FTPArchive::Release::Suite "${code_name}";
+APT::FTPArchive::Release::Components "${component}";
+APT::FTPArchive::Release::Description "${PACKAGE_NAME} packages";
+EOF
+ apt-ftparchive -c release-${code_name}.conf \
+ release dists/${code_name} > /tmp/Release
+ mv /tmp/Release dists/${code_name}
+}
+
+for code_name in ${CODES}; do
+ case ${code_name} in
+ squeeze|wheezy|jessie|unstable)
+ distribution=debian
+ component=main
+ ;;
+ *)
+ distribution=ubuntu
+ component=universe
+ ;;
+ esac
+
+ mkdir -p ${DESTINATION}${distribution}
+ (cd ${DESTINATION}${distribution}
+ update_repository $distribution $code_name $component) &
+ if [ "${PARALLEL}" != "yes" ]; then
+ wait
+ fi
+done
+
+wait
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/changelog b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/changelog
new file mode 100644
index 00000000000..245c138a35a
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/changelog
@@ -0,0 +1,41 @@
+groonga-normalizer-mysql (1.0.6-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sun, 09 Feb 2014 00:00:00 +0900
+
+groonga-normalizer-mysql (1.0.5-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Sat, 29 Jun 2013 00:00:00 +0900
+
+groonga-normalizer-mysql (1.0.4-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Wed, 29 May 2013 00:00:00 +0900
+
+groonga-normalizer-mysql (1.0.3-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Mon, 29 Apr 2013 00:00:00 +0900
+
+groonga-normalizer-mysql (1.0.2-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- HAYASHI Kentaro <hayashi@clear-code.com> Fri, 29 Mar 2013 00:00:00 +0900
+
+groonga-normalizer-mysql (1.0.1) unstable; urgency=low
+
+ * New upstream release
+
+ -- khayashi <hayashi@clear-code.com> Thu, 28 Feb 2013 00:00:00 +0900
+
+groonga-normalizer-mysql (1.0.0-1) unstable; urgency=low
+
+ * Initial release
+
+ -- Kouhei Sutou <kou@clear-code.com> Sat, 9 Feb 2013 00:00:00 +0900
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/compat b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/compat
new file mode 100644
index 00000000000..ec635144f60
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/control b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/control
new file mode 100644
index 00000000000..20c456b0a76
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/control
@@ -0,0 +1,25 @@
+Source: groonga-normalizer-mysql
+Section: libs
+Priority: optional
+Maintainer: Kouhei Sutou <kou@clear-code.com>
+Build-Depends:
+ debhelper (>= 9),
+ autotools-dev,
+ pkg-config,
+ libgroonga-dev (>= 3.0.0)
+Standards-Version: 3.9.3
+Homepage: http://groonga.org/
+
+Package: groonga-normalizer-mysql
+Section: libs
+Architecture: any
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ libgroonga0
+Description: MySQL derived normalizer for groonga.
+ Groonga is an open-source fulltext search engine and column store.
+ It lets you write high-performance applications that requires fulltext search.
+ .
+ This package provides a normalizer which normalizes text as same as MySQL does.
+
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/copyright b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/copyright
new file mode 100644
index 00000000000..cd3295a134c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/copyright
@@ -0,0 +1,29 @@
+This package was debianized by Kouhei Sutou <kou@clear-code.com> on
+Tue, 29 Jan 2013 14:52:04 +0000.
+
+It was downloaded from <http://groonga.org/>
+
+Upstream Author(s):
+
+ Kouhei Sutou <kou at clear-code.com>
+
+Copyright:
+
+ Copyright(C) 2013 Brazil
+
+License:
+
+ This library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 2 of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+The Debian packaging is (C) 2013, Kouhei Sutou <kou@clear-code.com> and
+is licensed under the LGPL-2, see `/usr/share/common-licenses/LGPL-2'.
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/groonga-normalizer-mysql.install b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/groonga-normalizer-mysql.install
new file mode 100644
index 00000000000..1f58446dfb6
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/groonga-normalizer-mysql.install
@@ -0,0 +1 @@
+usr/lib/groonga/plugins/normalizers/mysql*
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/patches/series b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/patches/series
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/patches/series
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/rules b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/rules
new file mode 100755
index 00000000000..811e09274c0
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/rules
@@ -0,0 +1,15 @@
+#!/usr/bin/make -f
+# -*- makefile-gmake -*-
+#
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+# This has to be exported to make some magic below work.
+export DH_OPTIONS
+
+%:
+ dh $@
+
+# disable 'make check'.
+override_dh_auto_test:
+
+override_dh_install:
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/source/format b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/source/format
new file mode 100644
index 00000000000..163aaf8d82b
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/Makefile.am
new file mode 100644
index 00000000000..06031c93f6f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = centos fedora
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/centos/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/centos/Makefile.am
new file mode 100644
index 00000000000..eb5a6b42a61
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/centos/Makefile.am
@@ -0,0 +1 @@
+noinst_DATA = groonga-normalizer-mysql.spec
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/centos/groonga-normalizer-mysql.spec.in b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/centos/groonga-normalizer-mysql.spec.in
new file mode 100644
index 00000000000..e51bae6990d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/centos/groonga-normalizer-mysql.spec.in
@@ -0,0 +1,78 @@
+%global _initddir %{_sysconfdir}/init.d/
+
+Name: groonga-normalizer-mysql
+Version: @VERSION@
+Release: 1%{?dist}
+Summary: MySQL compatible normalizer plugin for groonga
+
+Group: Applications/Text
+License: LGPLv2
+URL: http://groonga.org/
+Source0: http://packages.groonga.org/source/%{name}/%{name}-%{version}.tar.gz
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n)
+BuildRequires: groonga-devel >= 3.0.3
+Requires: groonga-libs >= 3.0.3
+
+%description
+This package provides MySQL compatible normalizer plugin.
+You can use NormalizerMySQLGeneralCI and NormalizerMySQLUnicodeCI as normalizer.
+
+%package devel
+Summary: Development files for groonga-normalizer-mysql
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+This package provides development files for groonga-normalizer-mysql.
+
+%prep
+%setup -q
+
+
+%build
+%configure \
+ --disable-static
+sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool
+sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool
+make %{?_smp_mflags}
+
+%install
+make install DESTDIR=$RPM_BUILD_ROOT INSTALL="install -p"
+rm $RPM_BUILD_ROOT%{_libdir}/groonga/plugins/*/*.la
+
+%files
+%doc README AUTHORS COPYING
+%dir %{_libdir}/groonga
+%dir %{_libdir}/groonga/plugins
+%dir %{_libdir}/groonga/plugins/normalizers
+%{_libdir}/groonga/plugins/normalizers/mysql.so
+%{_datadir}/doc/groonga-normalizer-mysql/
+
+%files devel
+%{_libdir}/pkgconfig/groonga-normalizer-mysql.pc
+
+%changelog
+* Sun Feb 09 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.6-1
+- new upstream release.
+
+* Sat Jun 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.5-1
+- new upstream release.
+
+* Wed May 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.4-1
+- new upstream release.
+
+* Mon Apr 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.3-0
+- new upstream release.
+- Reduce required packages. groonga-libs is only required.
+- Require groonga 3.0.3 or later.
+- Split development files into -devel package.
+
+* Fri Mar 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.2-0
+- new upstream release.
+
+* Thu Feb 28 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.1-0
+- new upstream release
+
+* Tue Jan 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.0-0
+- initial packaging for CentOS
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/fedora/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/fedora/Makefile.am
new file mode 100644
index 00000000000..1273c1d4009
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/fedora/Makefile.am
@@ -0,0 +1,2 @@
+noinst_DATA = groonga-normalizer-mysql.spec
+
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/fedora/groonga-normalizer-mysql.spec.in b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/fedora/groonga-normalizer-mysql.spec.in
new file mode 100644
index 00000000000..3be5e0797db
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/rpm/fedora/groonga-normalizer-mysql.spec.in
@@ -0,0 +1,77 @@
+%global __provides_exclude_from ^%{_libdir}/groonga/plugins/normalizers/mysql\\.so$
+
+Name: groonga-normalizer-mysql
+Version: @VERSION@
+Release: 1%{?dist}
+Summary: MySQL compatible normalizer plugin for groonga
+
+Group: Applications/Text
+License: LGPLv2
+URL: http://groonga.org/
+Source0: http://packages.groonga.org/source/%{name}/%{name}-%{version}.tar.gz
+
+BuildRequires: groonga-devel >= 3.0.3
+Requires: groonga-libs >= 3.0.3
+ExclusiveArch: %{ix86} x86_64
+
+%description
+This package provides MySQL compatible normalizer plugin.
+You can use NormalizerMySQLGeneralCI and NormalizerMySQLUnicodeCI as normalizer.
+
+%package devel
+Summary: Development files for groonga-normalizer-mysql
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+This package provides development files for groonga-normalizer-mysql.
+
+%prep
+%setup -q
+
+%build
+%configure \
+ --disable-static
+sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool
+sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool
+make %{?_smp_mflags}
+
+%install
+make install DESTDIR=$RPM_BUILD_ROOT INSTALL="install -p"
+rm $RPM_BUILD_ROOT%{_libdir}/groonga/plugins/*/*.la
+
+%files
+%doc README AUTHORS COPYING
+%dir %{_libdir}/groonga
+%dir %{_libdir}/groonga/plugins
+%dir %{_libdir}/groonga/plugins/normalizers
+%{_libdir}/groonga/plugins/normalizers/mysql.so
+%{_datadir}/doc/groonga-normalizer-mysql/
+
+%files devel
+%{_libdir}/pkgconfig/groonga-normalizer-mysql.pc
+
+%changelog
+* Sun Feb 09 2014 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.6-1
+- new upstream release.
+
+* Sat Jun 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.5-1
+- new upstream release.
+
+* Wed May 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.4-1
+- new upstream release.
+
+* Mon Apr 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.3-1
+- new upstream release.
+- Reduce required packages. groonga-libs is only required.
+- Require groonga 3.0.3 or later.
+- Split development files into -devel package.
+
+* Fri Mar 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.2-0
+- new upstream release.
+
+* Thu Feb 28 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.1-1
+- new upstream release
+
+* Tue Jan 29 2013 HAYASHI Kentaro <hayashi@clear-code.com> - 1.0.0-1
+- initial packaging for Fedora
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/source/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/source/Makefile.am
new file mode 100644
index 00000000000..29051e80db4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/source/Makefile.am
@@ -0,0 +1,25 @@
+all:
+
+release: upload
+
+ensure-rsync-path:
+ @if test -z "$(RSYNC_PATH)"; then \
+ echo "--with-rsync-path configure option must be specified."; \
+ false; \
+ fi
+
+download: ensure-rsync-path
+ rsync -avz --progress $(RSYNC_PATH)/source/groonga-normalizer-mysql/ files
+
+upload: ensure-rsync-path files/$(PACKAGE)-$(VERSION).tar.gz files/$(PACKAGE)-$(VERSION).zip
+ rsync -avz --progress --delete files/ $(RSYNC_PATH)/source/groonga-normalizer-mysql
+
+files/$(PACKAGE)-$(VERSION).tar.gz: $(top_builddir)/$(PACKAGE)-$(VERSION).tar.gz
+ mkdir -p files
+ cp -p $< $@
+
+files/$(PACKAGE)-$(VERSION).zip: files/$(PACKAGE)-$(VERSION).tar.gz
+ rm -rf $(PACKAGE)-$(VERSION)
+ tar xvzf files/$(PACKAGE)-$(VERSION).tar.gz
+ zip -r $@ $(PACKAGE)-$(VERSION)
+ rm -rf $(PACKAGE)-$(VERSION)
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/ubuntu/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/ubuntu/Makefile.am
new file mode 100644
index 00000000000..3b616910900
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/ubuntu/Makefile.am
@@ -0,0 +1,28 @@
+CODE_NAMES = precise,trusty
+SOURCE = ../$(PACKAGE)-$(VERSION).tar.gz
+
+all:
+
+ensure-configuration:
+ @if test -z "$(LAUNCHPAD_UPLOADER_PGP_KEY)"; then \
+ echo "--with-launchpad-uploader-pgp-key configure option must be specified."; \
+ false; \
+ fi
+ @if test -z "$(GROONGA_SOURCE_PATH)"; then \
+ echo "--with-groonga-source-path configure option must be specified."; \
+ false; \
+ fi
+
+upload: source ensure-configuration
+ $(GROONGA_SOURCE_PATH)/packages/ubuntu/upload.rb \
+ --package '$(PACKAGE)' \
+ --version '$(VERSION)' \
+ --source-archive '$(SOURCE)' \
+ --code-names '$(CODE_NAMES)' \
+ --debian-directory '$(srcdir)/../debian/' \
+ --pgp-sign-key '$(LAUNCHPAD_UPLOADER_PGP_KEY)'
+
+source: $(SOURCE)
+
+$(SOURCE):
+ ln -s $(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz $(SOURCE)
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/Makefile.am b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/Makefile.am
new file mode 100644
index 00000000000..ea62f6e427e
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/Makefile.am
@@ -0,0 +1,72 @@
+REPOSITORIES_PATH = repositories
+DISTRIBUTIONS = centos
+ARCHITECTURES = i386 x86_64
+HAVE_DEVELOPMENT_BRANCH = no
+
+release: download build sign-packages update-repository upload
+
+ensure-rsync-path:
+ @if test -z "$(RSYNC_PATH)"; then \
+ echo "--with-rsync-path configure option must be specified."; \
+ false; \
+ fi
+
+sign-packages:
+ ./sign-rpm.sh '$(GPG_UID)' '$(REPOSITORIES_PATH)/' \
+ '$(DISTRIBUTIONS)'
+
+update-repository: RPM-GPG-KEY-$(PACKAGE)
+ ./update-repository.sh '$(PACKAGE)' '$(REPOSITORIES_PATH)/' \
+ '$(DISTRIBUTIONS)'
+
+upload: ensure-rsync-path
+ for distribution in $(DISTRIBUTIONS); do \
+ rsync -avz --progress --delete --exclude .gitignore \
+ $(REPOSITORIES_PATH)/$${distribution}/ \
+ $(RSYNC_PATH)/$${distribution}; \
+ done
+
+download: ensure-rsync-path
+ mkdir -p $(REPOSITORIES_PATH)
+ for distribution in $(DISTRIBUTIONS); do \
+ rsync -avz --progress --delete \
+ $(RSYNC_PATH)/$${distribution}/ \
+ $(REPOSITORIES_PATH)/$${distribution}; \
+ done
+
+build: build-in-vm
+
+build-in-vm: source specs env.sh
+ vagrant destroy --force
+ for architecture in $(ARCHITECTURES); do \
+ for version in 5 6 7; do \
+ if [ $$version = 7 -a $$architecture = i386 ]; then \
+ continue; \
+ fi; \
+ id=centos-$$version-$$architecture; \
+ vagrant up $$id; \
+ vagrant destroy --force $$id; \
+ done; \
+ done
+
+ensure-public-key:
+ gpg --list-keys '$(GPG_UID)' > /dev/null || \
+ gpg --keyserver keyserver.ubuntu.com --recv-key '$(GPG_UID)'
+
+RPM-GPG-KEY-$(PACKAGE): ensure-public-key
+ gpg --armor --export '$(GPG_UID)' > $@
+
+source: tmp/$(PACKAGE)-$(VERSION).tar.gz
+
+tmp/$(PACKAGE)-$(VERSION).tar.gz: $(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz
+ mkdir -p tmp/
+ cp $(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz tmp/
+
+$(abs_top_builddir)/$(PACKAGE)-$(VERSION).tar.gz:
+ cd $(abs_top_builddir) && $(MAKE) dist
+
+specs: tmp/centos/$(PACKAGE).spec
+
+tmp/centos/$(PACKAGE).spec: $(builddir)/../rpm/centos/$(PACKAGE).spec
+ mkdir -p tmp/centos
+ cp $(builddir)/../rpm/centos/$(PACKAGE).spec tmp/centos/
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/Vagrantfile b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/Vagrantfile
new file mode 100644
index 00000000000..4238d678721
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/Vagrantfile
@@ -0,0 +1,38 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
+VAGRANTFILE_API_VERSION = "2"
+
+Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+ vms = [
+ {
+ :id => "centos-5-i386",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-5.10-i386_chef-provisionerless.box",
+ },
+ {
+ :id => "centos-5-x86_64",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-5.10_chef-provisionerless.box",
+ },
+ {
+ :id => "centos-6-i386",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5-i386_chef-provisionerless.box",
+ },
+ {
+ :id => "centos-6-x86_64",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-6.5_chef-provisionerless.box",
+ },
+ {
+ :id => "centos-7-x86_64",
+ :box_url => "http://opscode-vm-bento.s3.amazonaws.com/vagrant/virtualbox/opscode_centos-7.0_chef-provisionerless.box",
+ },
+ ]
+
+ vms.each do |vm|
+ config.vm.define(vm[:id]) do |node|
+ node.vm.box = vm[:id]
+ node.vm.box_url = vm[:box_url]
+ node.vm.provision(:shell, :path => "build-rpm.sh")
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/build-rpm.sh b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/build-rpm.sh
new file mode 100755
index 00000000000..14a13ad0e00
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/build-rpm.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+rpmbuild_options=
+
+. /vagrant/env.sh
+
+distribution=$(cut -d " " -f 1 /etc/redhat-release | tr "A-Z" "a-z")
+if grep -q Linux /etc/redhat-release; then
+ distribution_version=$(cut -d " " -f 4 /etc/redhat-release)
+else
+ distribution_version=$(cut -d " " -f 3 /etc/redhat-release)
+fi
+distribution_version=$(echo ${distribution_version} | sed -e 's/\..*$//g')
+
+architecture="$(arch)"
+case "${architecture}" in
+ i*86)
+ architecture=i386
+ ;;
+esac
+
+run rpm -ivh http://packages.groonga.org/centos/groonga-release-1.1.0-1.noarch.rpm
+run yum makecache
+
+run yum groupinstall -y "Development Tools"
+run yum install -y rpm-build rpmdevtools tar ${DEPENDED_PACKAGES}
+
+if [ -x /usr/bin/rpmdev-setuptree ]; then
+ rm -rf .rpmmacros
+ run rpmdev-setuptree
+else
+ run cat <<EOM > ~/.rpmmacros
+%_topdir ${HOME}/rpmbuild
+EOM
+ run mkdir -p ~/rpmbuild/SOURCES
+ run mkdir -p ~/rpmbuild/SPECS
+ run mkdir -p ~/rpmbuild/BUILD
+ run mkdir -p ~/rpmbuild/RPMS
+ run mkdir -p ~/rpmbuild/SRPMS
+fi
+
+repository="/vagrant/repositories/${distribution}/${distribution_version}"
+rpm_dir="${repository}/${architecture}/Packages"
+srpm_dir="${repository}/source/SRPMS"
+run mkdir -p "${rpm_dir}" "${srpm_dir}"
+
+# for debug
+# rpmbuild_options="$rpmbuild_options --define 'optflags -O0 -g3'"
+
+cd
+
+run cp /vagrant/tmp/${PACKAGE}-${VERSION}.* rpmbuild/SOURCES/
+run cp /vagrant/tmp/${distribution}/${PACKAGE}.spec rpmbuild/SPECS/
+
+run rpmbuild -ba ${rpmbuild_options} rpmbuild/SPECS/${PACKAGE}.spec
+
+run mv rpmbuild/RPMS/*/* "${rpm_dir}/"
+run mv rpmbuild/SRPMS/* "${srpm_dir}/"
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/env.sh.in b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/env.sh.in
new file mode 100644
index 00000000000..ea55d0d88da
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/env.sh.in
@@ -0,0 +1,11 @@
+PACKAGE=@PACKAGE@
+VERSION=@VERSION@
+DEPENDED_PACKAGES="
+intltool
+libtool
+gcc
+gcc-c++
+make
+tar
+groonga-devel
+"
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/sign-rpm.sh b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/sign-rpm.sh
new file mode 100755
index 00000000000..2022ca66956
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/sign-rpm.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 3 ]; then
+ echo "Usage: $0 GPG_UID DESTINATION DISTRIBUTIONS"
+ echo " e.g.: $0 'F10399C0' repositories/ 'fedora centos'"
+ exit 1
+fi
+
+GPG_UID=$1
+DESTINATION=$2
+DISTRIBUTIONS=$3
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+rpms=""
+for distribution in ${DISTRIBUTIONS}; do
+ rpms="${rpms} $(echo ${DESTINATION}${distribution}/*/*/*/*.rpm)"
+done
+
+echo "NOTE: YOU JUST ENTER! YOU DON'T NEED TO INPUT PASSWORD!"
+echo " IT'S JUST FOR rpm COMMAND RESTRICTION!"
+run echo $rpms | xargs rpm \
+ -D "_gpg_name ${GPG_UID}" \
+ -D "_gpg_digest_algo sha1" \
+ -D "__gpg /usr/bin/gpg2" \
+ -D "__gpg_check_password_cmd /bin/true true" \
+ -D "__gpg_sign_cmd %{__gpg} gpg --batch --no-verbose --no-armor %{?_gpg_digest_algo:--digest-algo %{_gpg_digest_algo}} --no-secmem-warning -u \"%{_gpg_name}\" -sbo %{__signature_filename} %{__plaintext_filename}" \
+ --resign
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/update-repository.sh b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/update-repository.sh
new file mode 100755
index 00000000000..04058598dce
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/packages/yum/update-repository.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+script_base_dir=`dirname $0`
+
+if [ $# != 3 ]; then
+ echo "Usage: $0 GPG_KEY_NAME DESTINATION DISTRIBUTIONS"
+ echo " e.g.: $0 mitler-manager repositories/ 'fedora centos'"
+ exit 1
+fi
+
+GPG_KEY_NAME=$1
+DESTINATION=$2
+DISTRIBUTIONS=$3
+
+run()
+{
+ "$@"
+ if test $? -ne 0; then
+ echo "Failed $@"
+ exit 1
+ fi
+}
+
+for distribution in ${DISTRIBUTIONS}; do
+ for dir in ${DESTINATION}${distribution}/*/*; do
+ # "--checksum sha" is for CentOS 5. If we drop CentOS 5 support,
+ # we can remove the option.
+ test -d $dir && run createrepo --checksum sha $dir
+ done;
+
+ run cp $script_base_dir/RPM-GPG-KEY-${GPG_KEY_NAME} \
+ ${DESTINATION}${distribution}/RPM-GPG-KEY-${GPG_KEY_NAME};
+done
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/required_groonga_version b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/required_groonga_version
new file mode 100644
index 00000000000..75a22a26ac4
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/required_groonga_version
@@ -0,0 +1 @@
+3.0.3
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/dump_difference_uca.rb b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/dump_difference_uca.rb
new file mode 100644
index 00000000000..45e60c2973f
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/dump_difference_uca.rb
@@ -0,0 +1,50 @@
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+require "parser"
+
+if ARGV.size != 1
+ puts("Usage: #{$0} MYSQL_SOURCE/strings/ctype-uca.c")
+ exit(false)
+end
+
+parser = CTypeUCAParser.new
+parser.parse(ARGF)
+
+n_idencials = 0
+n_expanded_characters = 0
+parser.weight_based_characters.each do |weight, characters|
+ next if characters.size == 1
+ n_idencials += 1
+ representative_character = characters.first
+ rest_characters = characters[1..-1]
+ rest_characters.each do |character|
+ if representative_character[:utf8].bytesize > character[:utf8].bytesize
+ n_expanded_characters += 1
+ end
+ end
+ formatted_weight = weight.collect {|component| '%#07x' % component}.join(', ')
+ puts "weight: #{formatted_weight}"
+ characters.each do |character|
+ utf8 = character[:utf8]
+ code_point = character[:code_point]
+ p ["U+%04x" % code_point, utf8]
+ end
+end
+
+puts "Number of idencial weights #{n_idencials}"
+puts "Number of expanded characters: #{n_expanded_characters}"
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/dump_difference_utf8.rb b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/dump_difference_utf8.rb
new file mode 100644
index 00000000000..8cbfe969c13
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/dump_difference_utf8.rb
@@ -0,0 +1,50 @@
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+require "parser"
+
+if ARGV.size != 1
+ puts("Usage: #{$0} MYSQL_SOURCE/strings/ctype-utf8.c")
+ exit(false)
+end
+
+parser = CTypeUTF8Parser.new
+parser.parse(ARGF)
+
+n_differences = 0
+n_expanded_sort_characters = 0
+parser.sorted_pages.each do |page, characters|
+ characters.each do |character|
+ base = character[:base]
+ upper = character[:upper]
+ lower = character[:lower]
+ sort = character[:sort]
+ next if base == sort
+ n_differences += 1
+ utf8s = [base, upper, lower, sort]
+ formatted_code_points = utf8s.collect do |utf8|
+ "%#07x" % Unicode.from_utf8(utf8)
+ end
+ if sort.bytesize > base.bytesize
+ n_expanded_sort_characters += 1
+ end
+ p [utf8s, formatted_code_points]
+ end
+end
+
+puts "Number of differences: #{n_differences}"
+puts "Number of expanded sort characters: #{n_expanded_sort_characters}"
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/generate_uca_table.rb b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/generate_uca_table.rb
new file mode 100755
index 00000000000..57dba4e0477
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/generate_uca_table.rb
@@ -0,0 +1,286 @@
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+require "optparse"
+
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+require "parser"
+
+@suffix = ""
+@split_small_kana_p = false
+@split_kana_with_voiced_sound_mark_p = false
+@split_kana_with_semi_voiced_sound_mark_p = false
+
+option_parser = OptionParser.new
+option_parser.banner += " MYSQL_SOURCE/strings/ctype-uca.c"
+
+option_parser.on("--suffix=SUFFIX", "Add SUFFIX to names") do |suffix|
+ @suffix = suffix
+end
+
+option_parser.on("--[no-]split-small-kana",
+ "Split small hiragana (katakana) and " +
+ "large hiragana (katakana)",
+ "(#{@split_small_kana_p})") do |boolean|
+ @split_small_kana_p = boolean
+end
+
+option_parser.on("--[no-]split-kana-with-voiced-sound-mark",
+ "Split hiragana (katakana) with voiced sound mark",
+ "(#{@split_kana_with_voiced_sound_mark})") do |boolean|
+ @split_kana_with_voiced_sound_mark_p = boolean
+end
+
+option_parser.on("--[no-]split-kana-with-semi-voiced-sound-mark",
+ "Split hiragana (katakana) with semi-voiced sound mark",
+ "(#{@split_kana_with_semi_voiced_sound_mark})") do |boolean|
+ @split_kana_with_semi_voiced_sound_mark_p = boolean
+end
+
+begin
+ option_parser.parse!(ARGV)
+rescue OptionParser::Error
+ puts($!)
+ exit(false)
+end
+
+if ARGV.size != 1
+ puts(option_parser)
+ exit(false)
+end
+
+ctype_uca_c_path = ARGV[0]
+
+parser = CTypeUCAParser.new
+File.open(ctype_uca_c_path) do |ctype_uca_c|
+ parser.parse(ctype_uca_c)
+end
+
+SMALL_KANAS = [
+ "ぁ", "ぃ", "ぅ", "ぇ", "ぉ",
+ "っ",
+ "ゃ", "ゅ", "ょ",
+ "ゎ",
+ "ァ", "ィ", "ゥ", "ェ", "ォ",
+ "ッ",
+ "ャ", "ュ", "ョ",
+ "ヮ",
+ "ァ", "ィ", "ゥ", "ェ", "ォ",
+ "ッ",
+ "ャ", "ュ", "ョ",
+]
+def small_kana?(character)
+ SMALL_KANAS.include?(character[:utf8])
+end
+
+KANA_WITH_VOICED_SOUND_MARKS = [
+ "が", "ぎ", "ぐ", "げ", "ご",
+ "ざ", "じ", "ず", "ぜ", "ぞ",
+ "だ", "ぢ", "づ", "で", "ど",
+ "ば", "び", "ぶ", "べ", "ぼ",
+ "ガ", "ギ", "グ", "ゲ", "ゴ",
+ "ザ", "ジ", "ズ", "ゼ", "ゾ",
+ "ダ", "ヂ", "ヅ", "デ", "ド",
+ "バ", "ビ", "ブ", "ベ", "ボ",
+]
+def kana_with_voiced_sound_mark?(character)
+ KANA_WITH_VOICED_SOUND_MARKS.include?(character[:utf8])
+end
+
+KANA_WITH_SEMI_VOICED_SOUND_MARKS = [
+ "ぱ", "ぴ", "ぷ", "ぺ", "ぽ",
+ "パ", "ピ", "プ", "ペ", "ポ",
+]
+def kana_with_semi_voiced_sound_mark?(character)
+ KANA_WITH_SEMI_VOICED_SOUND_MARKS.include?(character[:utf8])
+end
+
+def split_characters(characters)
+ grouped_characters = characters.group_by do |character|
+ if @split_small_kana_p and small_kana?(character)
+ :small_kana
+ elsif @split_kana_with_voiced_sound_mark_p and
+ kana_with_voiced_sound_mark?(character)
+ :kana_with_voiced_sound_mark
+ elsif @split_kana_with_semi_voiced_sound_mark_p and
+ kana_with_semi_voiced_sound_mark?(character)
+ :kana_with_semi_voiced_sound_mark
+ else
+ :other
+ end
+ end
+ grouped_characters.values
+end
+
+grouped_characters = []
+parser.weight_based_characters.each do |weight, characters|
+ grouped_characters.concat(split_characters(characters))
+end
+
+GREEK_CAPITAL_UNICODE_RANGE = Unicode.from_utf8("Α")..Unicode.from_utf8("Ω")
+def find_greek_capital_character(characters)
+ characters.find do |character|
+ GREEK_CAPITAL_UNICODE_RANGE.cover?(character[:code_point])
+ end
+end
+
+def find_representative_character(characters)
+ representative_character = nil
+ case characters.first[:utf8]
+ when "⺄", "⺇", "⺈", "⺊", "⺌", "⺗"
+ representative_character = characters.last
+ when "⺜", "⺝", "⺧", "⺫", "⺬", "⺮", "⺶", "⺻", "⺼", "⺽"
+ representative_character = characters[1]
+ when "⻆", "⻊", "⻏", "⻑", "⻕", "⻗", "⻝", "⻡", "⻣", "⻤"
+ representative_character = characters.last
+ when "⻱", "⼀", "⼆", "⼈"
+ representative_character = characters[1]
+ when "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "っ", "ゃ", "ゅ", "ょ", "ゎ"
+ representative_character = characters[1] unless @split_small_kana_p
+ else
+ representative_character ||= find_greek_capital_character(characters)
+ end
+ representative_character ||= characters.first
+ representative_character
+end
+
+target_pages = {}
+grouped_characters.each do |characters|
+ next if characters.size == 1
+ representative_character = find_representative_character(characters)
+ representative_code_point = representative_character[:code_point]
+ rest_characters = characters.reject do |character|
+ character == representative_character
+ end
+ rest_characters.each do |character|
+ code_point = character[:code_point]
+ page = code_point >> 8
+ low_code = code_point & 0xff
+ target_pages[page] ||= [nil] * 256
+ target_pages[page][low_code] = representative_code_point
+ end
+end
+
+sorted_target_pages = target_pages.sort_by do |page, code_points|
+ page
+end
+
+
+normalized_ctype_uca_c_path =
+ ctype_uca_c_path.sub(/\A.*\/([^\/]+\/strings\/ctype-uca\.c)\z/, "\\1")
+
+@suffix_upper_case = @suffix.upcase
+
+puts(<<-HEADER)
+/*
+ Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; version 2
+ of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301, USA
+
+ This file uses normalization table defined in
+ #{normalized_ctype_uca_c_path}.
+ The following is the header of the file:
+
+ Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; version 2
+ of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301, USA
+
+ UCA (Unicode Collation Algorithm) support.
+ Written by Alexander Barkov <bar@mysql.com>
+*/
+
+#ifndef MYSQL_UCA#{@suffix_upper_case}_H
+#define MYSQL_UCA#{@suffix_upper_case}_H
+
+#include <stdint.h>
+HEADER
+
+def page_name(page)
+ "unicode_ci#{@suffix}_page_%02x" % page
+end
+
+sorted_target_pages.each do |page, characters|
+ puts(<<-PAGE_HEADER)
+
+static uint32_t #{page_name(page)}[] = {
+PAGE_HEADER
+ lines = characters.each_with_index.each_slice(8).collect do |characters_group|
+ formatted_code_points = characters_group.collect do |normalized, low_code|
+ normalized ||= (page << 8) + low_code
+ "0x%05x" % normalized
+ end
+ " " + formatted_code_points.join(", ")
+ end
+ puts(lines.join(",\n"))
+ puts(<<-PAGE_FOOTER)
+};
+PAGE_FOOTER
+end
+
+puts(<<-PAGES_HEADER)
+
+static uint32_t *unicode_ci#{@suffix}_table[256] = {
+PAGES_HEADER
+
+pages = ["NULL"] * 256
+sorted_target_pages.each do |page, characters|
+ pages[page] = page_name(page)
+end
+lines = pages.each_slice(2).collect do |pages_group|
+ formatted_pages = pages_group.collect do |page|
+ "%19s" % page
+ end
+ " " + formatted_pages.join(", ")
+end
+puts(lines.join(",\n"))
+
+puts(<<-PAGES_FOOTER)
+};
+PAGES_FOOTER
+
+puts(<<-FOOTER)
+
+#endif
+FOOTER
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/generate_utf8_table.rb b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/generate_utf8_table.rb
new file mode 100755
index 00000000000..dfedd94174c
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/generate_utf8_table.rb
@@ -0,0 +1,146 @@
+#!/usr/bin/env ruby
+#
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+require "parser"
+
+if ARGV.size != 1
+ puts("Usage: #{$0} MYSQL_SOURCE/strings/ctype-utf8.c")
+ exit(false)
+end
+
+ctype_utf8_c_path = ARGV[0]
+
+parser = CTypeUTF8Parser.new
+File.open(ctype_utf8_c_path) do |ctype_utf8_c|
+ parser.parse(ctype_utf8_c)
+end
+
+target_pages = {}
+parser.sorted_pages.each do |page, characters|
+ characters.each do |character|
+ base = character[:base]
+ upper = character[:upper]
+ lower = character[:lower]
+ sort = character[:sort]
+ next if base == sort
+ target_pages[page] ||= [nil] * 256
+ low_code = Unicode.from_utf8(base) & 0xff
+ target_pages[page][low_code] = Unicode.from_utf8(sort)
+ end
+end
+
+normalized_ctype_utf8_c_path =
+ ctype_utf8_c_path.sub(/\A.*\/([^\/]+\/strings\/ctype-utf8\.c)\z/, "\\1")
+puts(<<-HEADER)
+/*
+ Copyright(C) 2013 Kouhei Sutou <kou@clear-code.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; version 2
+ of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301, USA
+
+ This file uses normalization table defined in
+ #{normalized_ctype_utf8_c_path}.
+ The following is the header of the file:
+
+ Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; version 2
+ of the License.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ MA 02110-1301, USA
+
+ UTF8 according RFC 2279
+ Written by Alexander Barkov <bar@udm.net>
+*/
+
+#ifndef MYSQL_UTF8_H
+#define MYSQL_UTF8_H
+
+#include <stdint.h>
+HEADER
+
+def page_name(page)
+ "general_ci_page_%02x" % page
+end
+
+target_pages.each do |page, characters|
+ puts(<<-PAGE_HEADER)
+
+static uint32_t #{page_name(page)}[] = {
+PAGE_HEADER
+ lines = characters.each_with_index.each_slice(8).collect do |characters_group|
+ formatted_code_points = characters_group.collect do |normalized, low_code|
+ normalized ||= (page << 8) + low_code
+ "0x%05x" % normalized
+ end
+ " " + formatted_code_points.join(", ")
+ end
+ puts(lines.join(",\n"))
+ puts(<<-PAGE_FOOTER)
+};
+PAGE_FOOTER
+end
+
+puts(<<-PAGES_HEADER)
+
+static uint32_t *general_ci_table[256] = {
+PAGES_HEADER
+
+pages = ["NULL"] * 256
+target_pages.each do |page, characters|
+ pages[page] = page_name(page)
+end
+lines = pages.each_slice(2).collect do |pages_group|
+ formatted_pages = pages_group.collect do |page|
+ "%18s" % page
+ end
+ " " + formatted_pages.join(", ")
+end
+puts(lines.join(",\n"))
+
+puts(<<-PAGES_FOOTER)
+};
+PAGES_FOOTER
+
+puts(<<-FOOTER)
+
+#endif
+FOOTER
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/parser.rb b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/parser.rb
new file mode 100644
index 00000000000..b8652825f80
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/parser.rb
@@ -0,0 +1,161 @@
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+require "English"
+
+module Unicode
+ module_function
+ def to_utf8(code_point)
+ [code_point].pack("U")
+ end
+
+ def from_utf8(utf8)
+ utf8.unpack("U")[0]
+ end
+end
+
+class CTypeUTF8Parser
+ def initialize
+ @pages = {}
+ end
+
+ def parse(input)
+ parse_ctype_utf8(input)
+ normalize_pages
+ end
+
+ def sorted_pages
+ @pages.sort_by do |page, characters|
+ page
+ end
+ end
+
+ private
+ def parse_ctype_utf8(input)
+ current_page = nil
+ input.each_line do |line|
+ case line
+ when / plane([\da-fA-F]{2})\[\]=/
+ current_page = $1.to_i(16)
+ @pages[current_page] = []
+ when /^\s*
+ \{0x([\da-z]+),0x([\da-z]+),0x([\da-z]+)\},
+ \s*
+ \{0x([\da-z]+),0x([\da-z]+),0x([\da-z]+)\},?$/ix
+ next if current_page.nil?
+ parsed_characters = $LAST_MATCH_INFO.captures.collect do |value|
+ Unicode.to_utf8(value.to_i(16))
+ end
+ upper1, lower1, sort1, upper2, lower2, sort2 = parsed_characters
+ characters = @pages[current_page]
+ characters << {:upper => upper1, :lower => lower1, :sort => sort1}
+ characters << {:upper => upper2, :lower => lower2, :sort => sort2}
+ when /^\};$/
+ current_page = nil
+ end
+ end
+ end
+
+ def normalize_pages
+ @pages.each do |page, characters|
+ characters.each_with_index do |character, i|
+ character[:base] = Unicode.to_utf8((page << 8) + i)
+ end
+ end
+ end
+end
+
+class CTypeUCAParser
+ attr_reader :pages
+ def initialize
+ @pages = {}
+ @lengths = []
+ end
+
+ def parse(input)
+ parse_ctype_uca(input)
+ normalize_pages
+ end
+
+ def weight_based_characters
+ weight_based_characters = {}
+ sorted_pages.each do |page, characters|
+ characters.each do |character|
+ weight = character[:weight]
+ weight_based_characters[weight] ||= []
+ weight_based_characters[weight] << character
+ end
+ end
+ weight_based_characters
+ end
+
+ def sorted_pages
+ @pages.sort_by do |page, characters|
+ page
+ end
+ end
+
+ private
+ def parse_ctype_uca(input)
+ current_page = nil
+ in_length = false
+ input.each_line do |line|
+ case line
+ when / page([\da-fA-F]{3})data\[\]=/
+ current_page = $1.to_i(16)
+ @pages[current_page] = []
+ when /^\s*0x(?:[\da-z]+)(?:,\s*0x(?:[\da-z]+))*,?$/i
+ next if current_page.nil?
+ weights = line.chomp.split(/,\s*/).collect do |component|
+ Integer(component)
+ end
+ @pages[current_page].concat(weights)
+ when / uca_length\[256\]=/
+ in_length = true
+ when /^\d+(?:,\d+)*,?$/
+ next unless in_length
+ current_lengths = line.chomp.split(/,/).collect do |length|
+ Integer(length)
+ end
+ @lengths.concat(current_lengths)
+ when /^\};$/
+ current_page = nil
+ in_length = false
+ end
+ end
+ end
+
+ def normalize_pages
+ @pages.each do |page, flatten_weights|
+ weights = flatten_weights.each_slice(@lengths[page])
+ @pages[page] = weights.with_index.collect do |weight, i|
+ if weight.all?(&:zero?)
+ weight = [0]
+ else
+ while weight.last.zero?
+ weight.pop
+ end
+ end
+ code_point = (page << 8) + i
+ {
+ :weight => weight,
+ :code_point => code_point,
+ :utf8 => Unicode.to_utf8(code_point),
+ }
+ end
+ end
+ end
+end
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/travis/before_script.sh b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/travis/before_script.sh
new file mode 100755
index 00000000000..353fb53d3b3
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/travis/before_script.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# Copyright (C) 2013 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+set -e
+
+./autogen.sh
+
+./configure
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/travis/install.sh b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/travis/install.sh
new file mode 100755
index 00000000000..ed61a3540ed
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/tool/travis/install.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# Copyright (C) 2013-2014 Kouhei Sutou <kou@clear-code.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+set -e
+
+# export GROONGA_MASTER=yes
+curl --silent --location \
+ https://raw.github.com/groonga/groonga/master/data/travis/setup.sh | sh
+
+if ! pkg-config --exists groonga; then
+ sudo apt-get install -qq -y libgroonga-dev
+fi
diff --git a/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/version b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/version
new file mode 100644
index 00000000000..ece61c60124
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/plugins/groonga-normalizer-mysql/version
@@ -0,0 +1 @@
+1.0.6 \ No newline at end of file
diff --git a/storage/mroonga/vendor/groonga/vendor/update_nginx.sh b/storage/mroonga/vendor/groonga/vendor/update_nginx.sh
new file mode 100755
index 00000000000..4dbbec3602d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/vendor/update_nginx.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+set -u
+set -e
+set -x
+
+if [ $# != 1 ]; then
+ echo "Usage: $0 VERSION"
+ echo " e.g.: $0 1.2.6"
+ exit 1
+fi
+
+new_nginx_version="$1"
+
+base_dir="$(dirname "$0")"
+top_src_dir="${base_dir}/.."
+
+nginx_version_file="${top_src_dir}/nginx_version"
+current_nginx_version=$(cat "${nginx_version_file}")
+
+current_nginx_dir="${base_dir}/nginx-${current_nginx_version}"
+
+new_nginx_base_name="nginx-${new_nginx_version}"
+new_nginx_tar_gz="${new_nginx_base_name}.tar.gz"
+wget "http://nginx.org/download/${new_nginx_tar_gz}"
+
+tar xzf "${new_nginx_tar_gz}"
+rm "${new_nginx_tar_gz}"
+
+echo "${new_nginx_version}" > "${nginx_version_file}"
+
+git add "${new_nginx_base_name}"
+git rm -rf "${current_nginx_dir}" || :
diff --git a/storage/mroonga/vendor/groonga/version-gen.sh b/storage/mroonga/vendor/groonga/version-gen.sh
new file mode 100755
index 00000000000..c56cb9e0d0d
--- /dev/null
+++ b/storage/mroonga/vendor/groonga/version-gen.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+case "$0" in
+ */*)
+ cd `dirname $0`
+ ;;
+esac
+
+GRN_VERSION_SH=version.sh
+
+if test -f version
+then
+ GRN_VN=`cat version`
+elif test -d .git -o -f .git
+then
+ GRN_VN=`git describe --abbrev=7 HEAD 2>/dev/null`
+ if [ $? -ne 0 ]
+ then
+ GRN_VN=`cat base_version`
+ fi
+fi
+
+GRN_VN=`expr "$GRN_VN" : v*'\(.*\)'`
+
+if test -r $GRN_VERSION_SH
+then
+ GRN_VN_OLD=`sed -e 's/^GRN_VERSION=//' <$GRN_VERSION_SH`
+else
+ GRN_VN_OLD=unset
+fi
+
+if test "$GRN_VN_OLD" != "$GRN_VN"
+then
+ echo "GRN_VERSION=$GRN_VN" >$GRN_VERSION_SH
+fi
diff --git a/storage/mroonga/version b/storage/mroonga/version
new file mode 100644
index 00000000000..e69cf02a547
--- /dev/null
+++ b/storage/mroonga/version
@@ -0,0 +1 @@
+4.05 \ No newline at end of file
diff --git a/storage/mroonga/version_in_hex b/storage/mroonga/version_in_hex
new file mode 100644
index 00000000000..c1f0bc81d7b
--- /dev/null
+++ b/storage/mroonga/version_in_hex
@@ -0,0 +1 @@
+0x0405 \ No newline at end of file
diff --git a/storage/mroonga/version_major b/storage/mroonga/version_major
new file mode 100644
index 00000000000..bf0d87ab1b2
--- /dev/null
+++ b/storage/mroonga/version_major
@@ -0,0 +1 @@
+4 \ No newline at end of file
diff --git a/storage/mroonga/version_micro b/storage/mroonga/version_micro
new file mode 100644
index 00000000000..7813681f5b4
--- /dev/null
+++ b/storage/mroonga/version_micro
@@ -0,0 +1 @@
+5 \ No newline at end of file
diff --git a/storage/mroonga/version_minor b/storage/mroonga/version_minor
new file mode 100644
index 00000000000..c227083464f
--- /dev/null
+++ b/storage/mroonga/version_minor
@@ -0,0 +1 @@
+0 \ No newline at end of file